use crate::{BBox2D, GeoId, LineStrip2D, OrganicSurfaceDetail, Poly2D, Poly3D};
use rustc_hash::FxHashMap;
use uuid::Uuid;
use vek::{Mat3, Vec2, Vec3};
#[derive(Debug, Default, Clone)]
pub struct Chunk {
pub origin: Vec2<i32>,
pub size: i32,
pub bbox: BBox2D,
pub polys_map: FxHashMap<GeoId, Poly2D>,
pub lines2d_px: FxHashMap<GeoId, LineStrip2D>,
pub polys3d_map: rustc_hash::FxHashMap<GeoId, Vec<Poly3D>>,
pub priority: i32,
}
impl Chunk {
fn insert_poly2d_allow_terrain_dupes(&mut self, poly: Poly2D) {
if self.polys_map.contains_key(&poly.id)
&& let GeoId::Terrain(tx, tz) = poly.id
&& let Some(mut existing) = self.polys_map.remove(&poly.id)
{
let salt = self.polys_map.len() as u32;
let h = ((tx as u32).wrapping_mul(73856093))
^ ((tz as u32).wrapping_mul(19349663))
^ salt
^ (existing.tile_id.as_u128() as u32);
existing.id = GeoId::Unknown(0x7000_0000u32 ^ h);
self.polys_map.insert(existing.id, existing);
}
self.polys_map.insert(poly.id, poly);
}
pub fn new(origin: Vec2<i32>, size: i32) -> Self {
let bbox = BBox2D::from_pos_size(origin.map(|v| v as f32), Vec2::broadcast(size as f32));
Self {
origin,
size,
bbox,
..Default::default()
}
}
pub fn add(&mut self, poly: Poly2D) {
self.insert_poly2d_allow_terrain_dupes(poly);
}
pub fn add_3d(&mut self, poly: Poly3D) {
self.polys3d_map.entry(poly.id).or_default().push(poly);
}
pub fn add_poly_2d(
&mut self,
id: GeoId,
tile_id: Uuid,
vertices: Vec<[f32; 2]>,
uvs: Vec<[f32; 2]>,
indices: Vec<(usize, usize, usize)>,
layer: i32,
visible: bool,
) {
let poly = Poly2D {
id,
tile_id,
tile_id2: None,
blend_weights: Vec::new(),
vertices,
uvs,
indices,
transform: Mat3::identity(),
layer,
visible,
};
self.insert_poly2d_allow_terrain_dupes(poly);
}
pub fn add_line_strip_2d(
&mut self,
id: GeoId,
tile_id: Uuid,
points: Vec<[f32; 2]>,
width: f32,
layer: i32,
) {
if points.len() < 2 {
return;
}
let half = 0.5 * width;
let mut vertices: Vec<[f32; 2]> = Vec::with_capacity(points.len() * 4);
let mut uvs: Vec<[f32; 2]> = Vec::with_capacity(points.len() * 4);
let mut indices: Vec<(usize, usize, usize)> = Vec::with_capacity((points.len() - 1) * 2);
for seg in 0..(points.len() - 1) {
let p0 = points[seg];
let p1 = points[seg + 1];
let dx = p1[0] - p0[0];
let dy = p1[1] - p0[1];
let len = (dx * dx + dy * dy).sqrt();
if len == 0.0 {
continue;
}
let nx = -dy / len; let ny = dx / len;
let ox = nx * half;
let oy = ny * half;
let v0 = [p0[0] - ox, p0[1] - oy]; let v1 = [p0[0] + ox, p0[1] + oy]; let v2 = [p1[0] + ox, p1[1] + oy]; let v3 = [p1[0] - ox, p1[1] - oy];
let base = vertices.len();
vertices.extend_from_slice(&[v0, v1, v2, v3]);
uvs.extend_from_slice(&[[0.0, 0.0], [0.0, 1.0], [1.0, 1.0], [1.0, 0.0]]);
indices.push((base + 0, base + 1, base + 2));
indices.push((base + 0, base + 2, base + 3));
}
if vertices.is_empty() {
return;
}
let poly = Poly2D {
id,
tile_id,
tile_id2: None,
blend_weights: Vec::new(),
vertices,
uvs,
indices,
transform: Mat3::identity(),
layer,
visible: true,
};
self.insert_poly2d_allow_terrain_dupes(poly);
}
pub fn add_line_strip_2d_px(
&mut self,
id: GeoId,
tile_id: Uuid,
points: Vec<[f32; 2]>,
width_px: f32,
layer: i32,
) {
if points.len() < 2 {
return;
}
let line = LineStrip2D {
id,
tile_id,
points,
width_px,
layer,
visible: true,
};
self.lines2d_px.insert(id, line);
}
pub fn add_square_2d(
&mut self,
id: GeoId,
tile_id: Uuid,
center: [f32; 2],
size: f32,
layer: i32,
visible: bool,
) {
if size <= 0.0 {
return;
}
let half = 0.5 * size;
let (cx, cy) = (center[0], center[1]);
let x0 = cx - half; let x1 = cx + half; let y0 = cy - half; let y1 = cy + half;
let vertices = vec![
[x0, y0], [x0, y1], [x1, y1], [x1, y0], ];
let uvs = vec![[0.0, 0.0], [0.0, 1.0], [1.0, 1.0], [1.0, 0.0]];
let indices = vec![(0, 1, 2), (0, 2, 3)];
let poly = Poly2D {
id,
tile_id,
tile_id2: None,
blend_weights: Vec::new(),
vertices,
uvs,
indices,
transform: Mat3::identity(),
layer,
visible,
};
self.insert_poly2d_allow_terrain_dupes(poly);
}
pub fn add_poly_3d(
&mut self,
id: GeoId,
tile_id: uuid::Uuid,
vertices: Vec<[f32; 4]>,
uvs: Vec<[f32; 2]>,
indices: Vec<(usize, usize, usize)>,
layer: i32,
visible: bool,
) {
self.polys3d_map.entry(id).or_default().push(Poly3D {
id,
tile_id,
vertices,
uvs,
organic_uvs: Vec::new(),
indices,
layer,
visible,
opacity: 1.0,
tile_id2: None,
blend_weights: Vec::new(),
organic_detail: None,
});
}
pub fn add_surface_poly_3d(
&mut self,
id: GeoId,
tile_id: uuid::Uuid,
vertices: Vec<[f32; 4]>,
uvs: Vec<[f32; 2]>,
organic_uvs: Vec<[f32; 2]>,
indices: Vec<(usize, usize, usize)>,
layer: i32,
visible: bool,
organic_detail: OrganicSurfaceDetail,
) {
self.polys3d_map.entry(id).or_default().push(Poly3D {
id,
tile_id,
vertices,
uvs,
organic_uvs,
indices,
layer,
visible,
opacity: 1.0,
tile_id2: None,
blend_weights: Vec::new(),
organic_detail: Some(organic_detail),
});
}
pub fn add_poly_2d_blended(
&mut self,
id: GeoId,
tile_id: Uuid,
tile_id2: Uuid,
vertices: Vec<[f32; 2]>,
uvs: Vec<[f32; 2]>,
blend_weights: Vec<f32>,
indices: Vec<(usize, usize, usize)>,
layer: i32,
visible: bool,
) {
let poly = Poly2D {
id,
tile_id,
tile_id2: Some(tile_id2),
blend_weights,
vertices,
uvs,
indices,
transform: Mat3::identity(),
layer,
visible,
};
self.insert_poly2d_allow_terrain_dupes(poly);
}
pub fn add_poly_3d_blended(
&mut self,
id: GeoId,
tile_id: uuid::Uuid,
tile_id2: uuid::Uuid,
vertices: Vec<[f32; 4]>,
uvs: Vec<[f32; 2]>,
blend_weights: Vec<f32>,
indices: Vec<(usize, usize, usize)>,
layer: i32,
visible: bool,
) {
self.polys3d_map.entry(id).or_default().push(Poly3D {
id,
tile_id,
vertices,
uvs,
organic_uvs: Vec::new(),
indices,
layer,
visible,
opacity: 1.0,
tile_id2: Some(tile_id2),
blend_weights,
organic_detail: None,
});
}
pub fn add_surface_poly_3d_blended(
&mut self,
id: GeoId,
tile_id: uuid::Uuid,
tile_id2: uuid::Uuid,
vertices: Vec<[f32; 4]>,
uvs: Vec<[f32; 2]>,
organic_uvs: Vec<[f32; 2]>,
blend_weights: Vec<f32>,
indices: Vec<(usize, usize, usize)>,
layer: i32,
visible: bool,
organic_detail: OrganicSurfaceDetail,
) {
self.polys3d_map.entry(id).or_default().push(Poly3D {
id,
tile_id,
vertices,
uvs,
organic_uvs,
indices,
layer,
visible,
opacity: 1.0,
tile_id2: Some(tile_id2),
blend_weights,
organic_detail: Some(organic_detail),
});
}
pub fn add_billboard_3d(
&mut self,
id: GeoId,
tile_id: Uuid,
center: Vec3<f32>,
view_right: Vec3<f32>,
view_up: Vec3<f32>,
size: f32,
visible: bool,
) {
if !size.is_finite() || size <= 0.0 {
return;
}
let right_len = view_right.magnitude();
let right = if !right_len.is_finite() || right_len < 1e-6 {
Vec3::unit_x()
} else {
view_right / right_len
};
let up_len = view_up.magnitude();
let mut up = if !up_len.is_finite() || up_len < 1e-6 {
Vec3::unit_y()
} else {
view_up / up_len
};
if right.cross(up).magnitude() < 1e-6 {
let mut fallback = if right.y.abs() < 0.9 {
Vec3::unit_y()
} else {
Vec3::unit_z()
};
fallback = fallback - right * fallback.dot(right);
let fb_len = fallback.magnitude();
up = if !fb_len.is_finite() || fb_len < 1e-6 {
Vec3::unit_z()
} else {
fallback / fb_len
};
}
let h = 0.5 * size;
let p0 = center - right * h - up * h;
let p1 = center + right * h - up * h;
let p2 = center + right * h + up * h;
let p3 = center - right * h + up * h;
let vertices = vec![
[p0.x, p0.y, p0.z, 1.0],
[p1.x, p1.y, p1.z, 1.0],
[p2.x, p2.y, p2.z, 1.0],
[p3.x, p3.y, p3.z, 1.0],
];
let uvs = vec![[0.0, 1.0], [1.0, 1.0], [1.0, 0.0], [0.0, 0.0]];
let indices = vec![(0usize, 1usize, 2usize), (0usize, 2usize, 3usize)];
let poly = Poly3D {
id,
tile_id,
vertices,
uvs,
organic_uvs: Vec::new(),
indices,
layer: 0,
visible,
opacity: 1.0,
tile_id2: None,
blend_weights: Vec::new(),
organic_detail: None,
};
self.polys3d_map.entry(id).or_default().push(poly);
}
pub fn add_line_3d(
&mut self,
id: GeoId,
tile_id: Uuid,
a: Vec3<f32>,
b: Vec3<f32>,
thickness: f32,
normal: Vec3<f32>,
layer: i32,
) {
let dir = b - a;
let dir_len = dir.magnitude();
if dir_len < 1e-6 || !dir_len.is_finite() {
return;
}
let dir_n = dir / dir_len;
let mut n = if normal.magnitude() < 1e-6 || !normal.magnitude().is_finite() {
Vec3::unit_y()
} else {
normal.normalized()
};
if dir_n.dot(n).abs() > 0.999 {
let ax = dir_n.x.abs();
let ay = dir_n.y.abs();
let az = dir_n.z.abs();
n = if ax <= ay && ax <= az {
Vec3::unit_x()
} else if ay <= az {
Vec3::unit_y()
} else {
Vec3::unit_z()
};
}
let mut side = n.cross(dir_n);
if !side.x.is_finite()
|| !side.y.is_finite()
|| !side.z.is_finite()
|| side.magnitude() < 1e-6
{
side = dir_n.cross(Vec3::unit_y());
if side.magnitude() < 1e-6 {
side = dir_n.cross(Vec3::unit_x());
}
}
let side_n = side.normalized();
let half = side_n * (thickness * 0.5);
let cap = dir_n * (thickness * 0.5);
let a_ext = a - cap;
let b_ext = b + cap;
let v0 = a_ext - half; let v1 = a_ext + half; let v2 = b_ext + half; let v3 = b_ext - half;
let vertices = vec![
[v0.x, v0.y, v0.z, 1.0],
[v1.x, v1.y, v1.z, 1.0],
[v2.x, v2.y, v2.z, 1.0],
[v3.x, v3.y, v3.z, 1.0],
];
let uvs = vec![[0.0, 1.0], [1.0, 1.0], [1.0, 0.0], [0.0, 0.0]];
let indices = vec![(0usize, 1usize, 2usize), (0usize, 2usize, 3usize)];
let poly = Poly3D {
id,
tile_id,
vertices,
uvs,
organic_uvs: Vec::new(),
indices,
layer,
visible: true,
opacity: 1.0,
tile_id2: None,
blend_weights: Vec::new(),
organic_detail: None,
};
self.polys3d_map.entry(id).or_default().push(poly);
}
}