use std::{collections::HashMap, str::FromStr};
#[cfg(feature = "bevy_reflect")]
use bevy_reflect::Reflect;
use glam::{U16Vec2, UVec3, Vec3};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use smallvec::SmallVec;
use qbsp_macros::BspValue;
use crate::{
BspData, BspParseError, BspParseResultDoingJobExt, BspResult, LumpEntry,
data::{
BspFace, LightmapOffset,
lighting::{LightmapStyle, RgbLighting, read_lit},
nodes::{FloatBoundingBox, ShortBsp29LeafContents},
texture::PlanarTextureProjection,
util::{BspVariableArray, FixedStr},
},
reader::{BspByteReader, BspParseContext, BspValue},
};
pub const BSPX_ENTRY_NAME_LEN: usize = 24;
#[derive(BspValue, Debug, Clone, Copy)]
#[cfg_attr(feature = "bevy_reflect", derive(Reflect))]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct BspxLumpEntry {
pub name: FixedStr<BSPX_ENTRY_NAME_LEN>,
pub lump: LumpEntry,
}
#[derive(Debug, Clone, Default)]
#[cfg_attr(feature = "bevy_reflect", derive(Reflect))]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct BspxDirectory {
pub entries: Vec<BspxLumpEntry>,
}
impl BspValue for BspxDirectory {
fn bsp_parse(reader: &mut BspByteReader) -> BspResult<Self> {
match reader.read().and_then(|magic| {
if &magic != b"BSPX" {
Err(BspParseError::WrongMagicNumber {
found: magic,
expected: "BSPX",
})
} else {
Ok(())
}
}) {
Ok(()) => {}
Err(BspParseError::BufferOutOfBounds { .. }) => return Err(BspParseError::NoBspxDirectory),
Err(err) => return Err(err),
}
let num_lumps: u32 = reader.read().job("lump count")?;
let mut entries = Vec::with_capacity(num_lumps as usize);
for i in 0..num_lumps {
let entry: BspxLumpEntry = reader.read().job(|| format!("lump entry {i}/{num_lumps}"))?;
entries.push(entry);
}
Ok(Self { entries })
}
fn bsp_struct_size(_ctx: &BspParseContext) -> usize {
unimplemented!("BspxDirectory is of variable size")
}
}
#[derive(Debug, Clone, Default)]
#[cfg_attr(feature = "bevy_reflect", derive(Reflect))]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct BspxData {
pub rgb_lighting: Option<RgbLighting>,
pub light_grid_octree: Option<LightGridOctree>,
pub brush_list: Option<BrushList>,
pub decoupled_lm: Option<DecoupledLightmaps>,
pub face_normals: Option<FaceNormals>,
pub unparsed: HashMap<FixedStr<BSPX_ENTRY_NAME_LEN>, Vec<u8>>,
}
impl BspxData {
pub fn parse(bsp: &[u8], dir: &BspxDirectory, bsp_data: &BspData) -> BspResult<Self> {
let mut data = Self::default();
for entry in &dir.entries {
macro_rules! check_duplicate {
($lump:ident) => {
if data.$lump.is_some() {
return Err(BspParseError::DuplicateBspxLump(entry.name.to_string()));
}
};
}
let lump = entry.lump.get(bsp)?;
match entry.name.as_str() {
"RGBLIGHTING" => {
check_duplicate!(rgb_lighting);
data.rgb_lighting = Some(read_lit(lump, &bsp_data.parse_ctx, true).job("Parsing RGBLIGHTING BSPX lump")?);
}
"LIGHTGRID_OCTREE" => {
check_duplicate!(light_grid_octree);
data.light_grid_octree = Some(BspByteReader::new(lump, &bsp_data.parse_ctx).read()?);
}
"BRUSHLIST" => {
check_duplicate!(brush_list);
data.brush_list = Some(parse_brush_list(BspByteReader::new(lump, &bsp_data.parse_ctx))?);
}
"DECOUPLED_LM" => {
check_duplicate!(decoupled_lm);
data.decoupled_lm = Some(parse_decoupled_lm(BspByteReader::new(lump, &bsp_data.parse_ctx))?);
}
"FACENORMALS" => {
check_duplicate!(face_normals);
data.face_normals = Some(FaceNormals::parse(BspByteReader::new(lump, &bsp_data.parse_ctx), &bsp_data.faces)?);
}
_ => {
data.unparsed.insert(entry.name, lump.to_vec());
}
}
}
Ok(data)
}
#[inline]
pub fn get_unparsed(&self, s: &str) -> Option<&[u8]> {
self.unparsed.get(&FixedStr::from_str(s).ok()?).map(|v| &**v)
}
pub fn is_empty(&self) -> bool {
self.rgb_lighting.is_none()
&& self.light_grid_octree.is_none()
&& self.brush_list.is_none()
&& self.decoupled_lm.is_none()
&& self.face_normals.is_none()
&& self.unparsed.is_empty()
}
#[inline]
pub fn align_offset(offset: usize) -> usize {
(offset + 3) & !3
}
}
#[derive(BspValue, Debug, Clone)]
#[cfg_attr(feature = "bevy_reflect", derive(Reflect))]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct LightGridOctree {
pub step: Vec3,
pub size: UVec3,
pub mins: Vec3,
pub num_styles: u8,
pub root_idx: u32,
pub nodes: BspVariableArray<LightGridNode, u32>,
pub leafs: BspVariableArray<LightGridLeaf, u32>,
}
#[derive(BspValue, Debug, Clone)]
#[cfg_attr(feature = "bevy_reflect", derive(Reflect))]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct LightGridNode {
pub division_point: UVec3,
pub children: [u32; 8],
}
impl LightGridNode {
pub const LEAF: u32 = 1 << 31;
pub const MISSING: u32 = 1 << 30;
#[rustfmt::skip]
#[allow(clippy::identity_op)]
pub fn get_child_index_towards(&self, point: Vec3) -> u32 {
self.children[
(((point.z >= self.division_point.z as f32) as usize) << 0) |
(((point.y >= self.division_point.y as f32) as usize) << 1) |
(((point.x >= self.division_point.x as f32) as usize) << 2)
]
}
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "bevy_reflect", derive(Reflect))]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct LightGridLeaf {
pub mins: UVec3,
size: UVec3,
data: Vec<LightGridCell>,
}
impl BspValue for LightGridLeaf {
fn bsp_parse(reader: &mut BspByteReader) -> BspResult<Self> {
let mins: UVec3 = reader.read().job("position")?;
let size: UVec3 = reader.read().job("size")?;
let mut data = Vec::with_capacity(size.element_product() as usize);
for _ in 0..size.element_product() {
data.push(reader.read().job("Reading cell")?);
}
Ok(Self { mins, size, data })
}
fn bsp_struct_size(_ctx: &BspParseContext) -> usize {
unimplemented!("LightGridLeaf is of variable size")
}
}
impl LightGridLeaf {
#[inline]
pub fn cells(&self) -> &[LightGridCell] {
&self.data
}
#[inline]
pub const fn cell_idx(&self, x: u32, y: u32, z: u32) -> usize {
((z * self.size.x * self.size.y) + (y * self.size.x) + x) as usize
}
pub fn get_cell(&self, x: u32, y: u32, z: u32) -> &LightGridCell {
&self.data[self.cell_idx(x, y, z)]
}
pub fn get_cell_mut(&mut self, x: u32, y: u32, z: u32) -> &mut LightGridCell {
let idx = self.cell_idx(x, y, z);
&mut self.data[idx]
}
#[inline]
pub const fn size(&self) -> UVec3 {
self.size
}
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "bevy_reflect", derive(Reflect))]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum LightGridCell {
Occluded,
Filled(SmallVec<[LightmapCellSample; 4]>),
}
impl BspValue for LightGridCell {
fn bsp_parse(reader: &mut BspByteReader) -> BspResult<Self> {
let style_count: u8 = reader.read().job("style count")?;
if style_count == 255 {
return Ok(Self::Occluded);
}
let mut samples = SmallVec::with_capacity(style_count as usize);
for _ in 0..style_count {
samples.push(reader.read().job("cell sample")?);
}
Ok(Self::Filled(samples))
}
fn bsp_struct_size(_ctx: &BspParseContext) -> usize {
unimplemented!("LightGridCell is of variable size")
}
}
#[derive(BspValue, Debug, Clone, Copy)]
#[cfg_attr(feature = "bevy_reflect", derive(Reflect))]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct LightmapCellSample {
pub style: LightmapStyle,
pub color: [u8; 3],
}
#[derive(BspValue, Debug, Clone, Copy)]
#[cfg_attr(feature = "bevy_reflect", derive(Reflect))]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct ModelBrushesIdx {
pub brush_count: u32,
pub plane_count: u32,
}
impl TryFrom<ModelBrushesIdx> for usize {
type Error = ();
fn try_from(value: ModelBrushesIdx) -> Result<Self, Self::Error> {
Ok(value.brush_count as usize)
}
}
pub type BrushList = Vec<ModelBrushes>;
pub fn parse_brush_list(mut reader: BspByteReader) -> BspResult<BrushList> {
let mut brush_list = BrushList::new();
let mut i: usize = 0;
while reader.in_bounds() {
let brushes = reader.read().job(|| format!("Parsing BRUSHLIST BSPX lump element {i}"))?;
brush_list.push(brushes);
i += 1;
}
Ok(brush_list)
}
#[derive(BspValue, Debug, Clone)]
#[cfg_attr(feature = "bevy_reflect", derive(Reflect))]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct ModelBrushes {
pub version: u32,
pub model_idx: u32,
pub brushes: BspVariableArray<ModelBrush, ModelBrushesIdx>,
}
#[derive(BspValue, Debug, Clone)]
#[cfg_attr(feature = "bevy_reflect", derive(Reflect))]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct ModelBrush {
pub bound: FloatBoundingBox,
pub contents: ShortBsp29LeafContents,
pub planes: BspVariableArray<ModelBrushPlane, u16>,
}
#[derive(BspValue, Debug, Clone, Copy)]
#[cfg_attr(feature = "bevy_reflect", derive(Reflect))]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct ModelBrushPlane {
pub normal: Vec3,
pub dist: f32,
}
pub type DecoupledLightmaps = Vec<DecoupledLightmap>;
pub fn parse_decoupled_lm(mut reader: BspByteReader) -> BspResult<DecoupledLightmaps> {
let entries_count = reader.len() / DecoupledLightmap::bsp_struct_size(reader.ctx);
let mut lm_infos = DecoupledLightmaps::with_capacity(entries_count);
for i in 0..entries_count {
let mut lm_info: DecoupledLightmap = reader.read().job(|| format!("Parsing DECOUPLED_LM BSPX lump element {i}"))?;
lm_info.projection.u_offset += 0.5;
lm_info.projection.v_offset += 0.5;
lm_infos.push(lm_info);
}
Ok(lm_infos)
}
#[derive(BspValue, Debug, Clone, Copy)]
#[cfg_attr(feature = "bevy_reflect", derive(Reflect))]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct DecoupledLightmap {
pub size: U16Vec2,
pub offset: LightmapOffset,
pub projection: PlanarTextureProjection,
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "bevy_reflect", derive(Reflect))]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct FaceNormals {
pub unique_vecs: Vec<Vec3>,
pub face_vertices: Vec<FaceNormalVertex>,
pub faces: Vec<FaceNormalFace>,
}
impl FaceNormals {
pub fn parse(mut reader: BspByteReader, faces: &[BspFace]) -> BspResult<Self> {
let unique_vecs = reader.read::<BspVariableArray<Vec3, u32>>()?.inner;
let mut face_vertices: Vec<FaceNormalVertex> = Vec::with_capacity(faces.iter().map(|face| face.num_edges.0 as usize).sum::<usize>());
let mut face_mappings: Vec<FaceNormalFace> = Vec::with_capacity(faces.len());
for face in faces {
face_mappings.push(FaceNormalFace {
vertex_start: face_vertices.len() as u32,
vertex_count: face.num_edges.0,
});
for _ in 0..face.num_edges.0 {
face_vertices.push(reader.read()?);
}
}
Ok(Self {
unique_vecs,
face_vertices,
faces: face_mappings,
})
}
}
#[derive(Debug, Clone, Copy)]
#[cfg_attr(feature = "bevy_reflect", derive(Reflect))]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct FaceNormalFace {
pub vertex_start: u32,
pub vertex_count: u32,
}
#[derive(BspValue, Debug, Clone, Copy)]
#[cfg_attr(feature = "bevy_reflect", derive(Reflect))]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct FaceNormalVertex {
pub normal_idx: u32,
pub tangent_idx: u32,
pub bi_tangent_idx: u32,
}