#[cfg(feature = "bevy_reflect")]
use bevy_reflect::Reflect;
use qbsp_macros::BspValue;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use crate::{
BspFormat, BspParseError, BspResult,
reader::{BspByteReader, BspParseContext, BspValue, BspVariableValue},
};
pub type GrayscaleLighting = Vec<u8>;
pub type RgbLighting = Vec<[u8; 3]>;
#[derive(Clone)]
#[cfg_attr(feature = "bevy_reflect", derive(Reflect))]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum BspLighting {
Grayscale(GrayscaleLighting),
Colored(RgbLighting),
}
impl BspValue for BspLighting {
fn bsp_parse(reader: &mut BspByteReader) -> BspResult<Self> {
match reader.ctx.format {
BspFormat::BSP2 | BspFormat::BSP29 => Ok(Self::Grayscale(reader.read_rest().to_vec())),
BspFormat::BSP30 | BspFormat::BSP38 | BspFormat::BSP38Qbism => {
let total_len = reader.len();
let (rgb_pixels, rest) = reader.read_bytes(total_len)?.as_chunks::<3>();
if !rest.is_empty() {
return Err(BspParseError::ColorDataSizeNotDevisableBy3(total_len));
}
Ok(BspLighting::Colored(rgb_pixels.to_vec()))
}
}
}
fn bsp_struct_size(_ctx: &BspParseContext) -> usize {
0
}
}
impl BspLighting {
#[inline]
pub fn get(&self, i: usize) -> Option<[u8; 3]> {
match self {
Self::Grayscale(v) => {
let v = *v.get(i)?;
Some([v, v, v])
}
Self::Colored(v) => v.get(i).copied(),
}
}
#[inline]
pub fn len(&self) -> usize {
match self {
Self::Grayscale(vec) => vec.len(),
Self::Colored(vec) => vec.len(),
}
}
#[inline]
pub fn bytes(&self) -> usize {
match self {
Self::Grayscale(vec) => vec.len(),
Self::Colored(vec) => vec.len() * 3,
}
}
#[inline]
pub fn is_empty(&self) -> bool {
self.len() == 0
}
}
impl std::fmt::Debug for BspLighting {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Grayscale(vec) => write!(f, "White(...) (len: {})", vec.len()),
Self::Colored(vec) => write!(f, "Colored(...) (len: {})", vec.len()),
}
}
}
pub fn read_lit(data: &[u8], ctx: &BspParseContext, ignore_header: bool) -> BspResult<RgbLighting> {
let mut reader = BspByteReader::new(data, ctx);
if !ignore_header {
let magic: [u8; 4] = reader.read()?;
if &magic != b"QLIT" {
return Err(BspParseError::WrongMagicNumber {
found: magic,
expected: "QLIT",
});
}
let _version: i32 = reader.read()?;
}
if !reader.len().is_multiple_of(3) {
return Err(BspParseError::ColorDataSizeNotDevisableBy3(reader.len()));
}
Ok(reader.read_rest().chunks_exact(3).map(|v| [v[0], v[1], v[2]]).collect())
}
#[derive(Debug, Clone, Copy)]
#[cfg_attr(feature = "bevy_reflect", derive(Reflect))]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct LightmapOffset {
pub pixels: i32,
}
impl BspVariableValue for LightmapOffset {
type Bsp29 = PixelLightmapOffset;
type Bsp2 = PixelLightmapOffset;
type Bsp30 = ByteLightmapOffset;
type Bsp38 = ByteLightmapOffset;
type Qbism = ByteLightmapOffset;
}
#[derive(BspValue, Debug, Clone, Copy)]
#[cfg_attr(feature = "bevy_reflect", derive(Reflect))]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct PixelLightmapOffset {
pub pixel_offset: i32,
}
#[derive(BspValue, Debug, Clone, Copy)]
#[cfg_attr(feature = "bevy_reflect", derive(Reflect))]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct ByteLightmapOffset {
pub byte_offset: i32,
}
impl From<PixelLightmapOffset> for LightmapOffset {
fn from(value: PixelLightmapOffset) -> Self {
Self { pixels: value.pixel_offset }
}
}
impl From<ByteLightmapOffset> for LightmapOffset {
fn from(value: ByteLightmapOffset) -> Self {
Self {
pixels: value.byte_offset / 3,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "bevy_reflect", derive(Reflect))]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct LightmapStyle(pub u8);
impl LightmapStyle {
pub const NORMAL: Self = Self(0);
pub const NONE: Self = Self(u8::MAX);
}
impl BspValue for LightmapStyle {
#[inline]
fn bsp_parse(reader: &mut BspByteReader) -> BspResult<Self> {
reader.read().map(Self)
}
#[inline]
fn bsp_struct_size(_ctx: &BspParseContext) -> usize {
1
}
}
impl std::fmt::Display for LightmapStyle {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self.0 {
0 => write!(f, "0 (normal)"),
255 => write!(f, "255 (no lightmap)"),
n => n.fmt(f),
}
}
}