use crate::compound::{convert_bytes_to_compound, CompoundData};
use crate::height_field::HeightFieldData;
use crate::hull::HullData;
use crate::mesh::MeshData;
use std::collections::HashMap;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u32)]
pub enum GeometryKind {
Hull = 0,
Mesh = 1,
HeightField = 2,
Compound = 3,
}
#[derive(Debug, Clone)]
pub struct GeometryEntry {
pub content_hash: u64,
pub id: u32,
pub kind: GeometryKind,
pub bytes: Vec<u8>,
pub hash_next: i32,
}
#[derive(Debug, Default)]
pub struct GeometryRegistry {
pub entries: Vec<GeometryEntry>,
dedup: HashMap<u64, i32>,
}
impl GeometryRegistry {
pub fn new() -> Self {
Self::default()
}
pub fn append(&mut self, kind: GeometryKind, content_hash: u64, bytes: Vec<u8>) -> u32 {
let id = self.entries.len() as u32;
let hash_next = self.dedup.get(&content_hash).copied().unwrap_or(-1);
self.entries.push(GeometryEntry {
content_hash,
id,
kind,
bytes,
hash_next,
});
self.dedup.insert(content_hash, id as i32);
id
}
pub fn intern(&mut self, kind: GeometryKind, content_hash: u64, bytes: Vec<u8>) -> u32 {
if let Some(&head) = self.dedup.get(&content_hash) {
let mut idx = head;
while idx >= 0 {
let e = &self.entries[idx as usize];
if e.kind == kind && e.bytes == bytes {
return e.id;
}
idx = e.hash_next;
}
}
let id = self.entries.len() as u32;
let hash_next = self.dedup.get(&content_hash).copied().unwrap_or(-1);
self.entries.push(GeometryEntry {
content_hash,
id,
kind,
bytes,
hash_next,
});
self.dedup.insert(content_hash, id as i32);
id
}
pub fn intern_hull(&mut self, hull: &HullData) -> u32 {
let bytes = hull.to_bytes();
let h = hash64_blob(&bytes);
self.intern(GeometryKind::Hull, h, bytes)
}
pub fn intern_mesh(&mut self, mesh: &MeshData) -> u32 {
let bytes = mesh.to_bytes();
let h = hash64_blob(&bytes);
self.intern(GeometryKind::Mesh, h, bytes)
}
pub fn intern_height_field(&mut self, hf: &HeightFieldData) -> u32 {
let bytes = hf.to_bytes();
let h = hash64_blob(&bytes);
self.intern(GeometryKind::HeightField, h, bytes)
}
pub fn intern_compound(&mut self, compound: &CompoundData) -> u32 {
let bytes = compound.to_bytes();
let h = hash64_blob(&bytes);
self.intern(GeometryKind::Compound, h, bytes)
}
pub fn to_slots(&self) -> Vec<RegistrySlot> {
self.entries
.iter()
.map(|e| RegistrySlot {
kind: e.kind,
bytes: e.bytes.clone(),
live_compound: None,
})
.collect()
}
}
#[derive(Debug, Clone)]
pub struct RegistrySlot {
pub kind: GeometryKind,
pub bytes: Vec<u8>,
pub live_compound: Option<CompoundData>,
}
impl RegistrySlot {
pub fn ensure_compound(&mut self) -> Option<&CompoundData> {
if self.live_compound.is_none() {
self.live_compound = convert_bytes_to_compound(&self.bytes);
}
self.live_compound.as_ref()
}
}
pub fn hash64_blob(bytes: &[u8]) -> u64 {
let n = bytes.len();
let mut h = 0xcbf2_9ce4_8422_2325u64 ^ (n as u32 as u64);
const PRIME: u64 = 0x1000_0000_01b3;
let mut i = 0;
while i + 8 <= n {
let mut word_bytes = [0u8; 8];
word_bytes.copy_from_slice(&bytes[i..i + 8]);
let word = u64::from_le_bytes(word_bytes);
h = (h ^ word).wrapping_mul(PRIME);
i += 8;
}
while i < n {
h = (h ^ bytes[i] as u64).wrapping_mul(PRIME);
i += 1;
}
h ^= h >> 30;
h = h.wrapping_mul(0xbf58_476d_1ce4_e5b9);
h ^= h >> 27;
h = h.wrapping_mul(0x94d0_49bb_1331_11eb);
h ^= h >> 31;
h
}