use std::path::PathBuf;
use benthic_protocol::{
messages::ui::land_update::LandData,
session::{CacheDir, Session, write_json},
};
use bitreader::{BitReader, BitReaderError};
use log::warn;
use metaverse_messages::udp::environment::layer_data::{LayerData, LayerType};
use crate::{
cloud::Cloud,
constants::{build_copy_matrix16, build_dequantize_table16, idct_column16, idct_line16},
error::{LayerError, PatchError},
land::Land,
water::Water,
wind::Wind,
};
use glam::{U16Vec2, Vec3, u16, u32, usize};
static COPY_MATRIX_16: [usize; 256] = build_copy_matrix16();
static DEQUANTIZE_TABLE_16: [f32; 256] = build_dequantize_table16();
pub const END_OF_PATCHES: u8 = 97;
pub enum PatchLayer {
Land(Vec<Land>),
Wind(Vec<Wind>),
Water(Vec<Water>),
Cloud(Vec<Cloud>),
}
pub fn handle_layer<C, A, U>(
layer_data: LayerData,
session: &mut Session<C, A, Land, U>,
) -> Result<Vec<PathBuf>, LayerError> {
let patch_data = parse_layer_data(&layer_data)?;
match patch_data {
PatchLayer::Land(patches) => {
let mut paths = Vec::new();
for land in patches {
session
.environment_cache
.patch_cache
.insert(land.terrain_header.location, land.clone());
let mut layer_meshes = Vec::new();
if let Some(mesh) = land.clone().generate_mesh(
&mut session.environment_cache.patch_queue,
&session.environment_cache.patch_cache,
) {
layer_meshes.push(mesh);
}
let queue_save = session.environment_cache.patch_queue.clone();
for (_location, land) in queue_save {
if let Some(mesh) = land.generate_mesh(
&mut session.environment_cache.patch_queue,
&session.environment_cache.patch_cache,
) {
layer_meshes.push(mesh);
}
}
for (mesh, coordinate) in layer_meshes {
let scale = land.terrain_header.patch_size as f32;
let json_path = write_json(
&LandData {
vertices: mesh.vertices,
indices: mesh.indices,
position: Vec3 {
x: (coordinate.x as f32) * scale,
y: 0.0,
z: (coordinate.y as f32) * scale,
},
},
&land.terrain_header.filename,
CacheDir::Land,
)?;
paths.push(json_path);
}
}
Ok(paths)
}
PatchLayer::Wind(_patches) => {
warn!("Wind patch received. Currently unimplemented.");
Ok(Vec::new())
}
PatchLayer::Water(_patches) => {
warn!("Water patch received. Currently unimplemented.");
Ok(Vec::new())
}
PatchLayer::Cloud(_patches) => {
warn!("Cloud patch received. Currently unimplemented.");
Ok(Vec::new())
}
}
}
pub fn parse_layer_data(data: &LayerData) -> Result<PatchLayer, PatchError> {
match data.layer_type {
LayerType::Land | LayerType::LandExtended => {
let extended = matches!(data.layer_type, LayerType::LandExtended);
let result = Land::from_packet(data, extended)?;
Ok(PatchLayer::Land(result))
}
LayerType::Wind | LayerType::WindExtended => {
let extended = matches!(data.layer_type, LayerType::WindExtended);
let result = Wind::from_packet(data, extended)?;
Ok(PatchLayer::Wind(result))
}
LayerType::Water | LayerType::WaterExtended => {
let extended = matches!(data.layer_type, LayerType::WaterExtended);
let result = Water::from_packet(data, extended)?;
Ok(PatchLayer::Water(result))
}
LayerType::Cloud | LayerType::CloudExtended | LayerType::Unknown => {
let extended = !matches!(data.layer_type, LayerType::Cloud);
let result = Cloud::from_packet(data, extended)?;
Ok(PatchLayer::Cloud(result))
}
}
}
pub trait PatchData: Sized {
fn from_packet(packet: &LayerData, extended: bool) -> Result<Vec<Self>, PatchError>;
}
#[derive(Debug, Clone)]
pub struct TerrainHeader {
pub quantized_world_bits: u8,
pub dc_offset: f32,
pub range: u16,
pub extended_region: bool,
pub world_bits: u32,
pub location: U16Vec2,
pub stride: u16,
pub patch_size: usize,
pub filename: String,
}
impl TerrainHeader {
pub fn from_bytes(
reader: &mut BitReader,
extended_region: bool,
) -> Result<Self, BitReaderError> {
let quantized_world_bits = reader.read_u8(8)?;
if quantized_world_bits == END_OF_PATCHES {
return Ok(TerrainHeader {
quantized_world_bits,
dc_offset: 0.0,
range: 0,
extended_region: false,
world_bits: 0,
location: U16Vec2 { x: 0, y: 0 },
patch_size: 0,
stride: 0,
filename: "".to_string(),
});
}
let dc_offset_bits = reader.read_u32(32)?.swap_bytes();
let dc_offset = f32::from_bits(dc_offset_bits);
let range = reader.read_u16(16)?.swap_bytes();
let patch_ids = if extended_region {
read_bits(reader, 32)?
} else {
read_bits(reader, 10)?
};
let patch_decoded = bits_to_big_endian(&patch_ids, 8);
let x = if extended_region {
patch_decoded.checked_shr(16).unwrap_or(0)
} else {
patch_decoded.checked_shr(5).unwrap_or(0)
};
let y = if extended_region {
patch_decoded & 0xFFFF
} else {
patch_decoded & 0x1F
};
let world_bits = ((quantized_world_bits & 0x0f) + 2) as u32;
Ok(TerrainHeader {
quantized_world_bits,
dc_offset,
range,
extended_region,
world_bits,
location: U16Vec2 {
x: x as u16,
y: y as u16,
},
patch_size: 0,
stride: 0,
filename: "".to_string(),
})
}
}
pub fn decompress_patch(terrain_header: &TerrainHeader, patch: &[f32]) -> Vec<f32> {
let patch_size = terrain_header.patch_size;
let mut block: Vec<f32> = vec![0.0; patch_size * patch_size];
let mut output: Vec<f32> = vec![0.0; patch_size * patch_size];
let prequant = (terrain_header.quantized_world_bits >> 4) + 2;
let prequant = if prequant > 0 { prequant } else { 1 };
let quantize = 1 << prequant;
let ooq = 1.0f32 / quantize as f32;
let mult = ooq * terrain_header.range as f32;
let addval = mult * (1 << (prequant - 1)) as f32 + terrain_header.dc_offset;
if terrain_header.patch_size == 16 {
for i in 0..block.len() {
block[i] = patch[COPY_MATRIX_16[i]] * DEQUANTIZE_TABLE_16[i];
}
let mut temp: Vec<f32> = vec![0.0; 16 * 16];
for i in 0..16 {
idct_column16(&block, &mut temp, i);
}
for i in 0..16 {
idct_line16(&temp, &mut block, i);
}
} else {
println!("patch size unsupported")
}
for i in 0..block.len() {
output[i] = block[i] * mult + addval;
}
output
}
pub fn bits_to_big_endian(bits: &[u32], chunk_size: usize) -> u32 {
let mut value: u32 = 0;
for chunk in bits.chunks(chunk_size).rev() {
for &bit in chunk {
value = (value << 1) | bit
}
}
value
}
pub fn read_bits(reader: &mut BitReader, bit_count: u32) -> Result<Vec<u32>, BitReaderError> {
let mut bits = Vec::with_capacity(bit_count as usize);
for _ in 0..bit_count {
let bit = reader.read_bool()?; bits.push(if bit { 1 } else { 0 });
}
Ok(bits)
}