1
0
Fork 0
mirror of https://codeberg.org/icewind/vbsp.git synced 2026-06-04 02:54:08 +02:00
vbsp/src/lib.rs

450 lines
13 KiB
Rust

mod bspfile;
pub mod data;
mod reader;
use crate::bspfile::LumpType;
pub use crate::data::TextureFlags;
pub use crate::data::Vector;
use crate::data::*;
use binread::io::Cursor;
use binread::BinRead;
use bspfile::BspFile;
use itertools::{GroupBy, Itertools};
use reader::LumpReader;
use std::{io::Read, ops::Deref};
use thiserror::Error;
#[derive(Debug, Error)]
pub enum BspError {
#[error("unexpected magic numbers or version, is this a valve bsp?")]
UnexpectedHeader(Header),
#[error("bsp lump is out of bounds of the bsp file")]
LumpOutOfBounds(LumpEntry),
#[error("unexpected length of uncompressed lump, got {got} but expected {expected}")]
UnexpectedUncompressedLumpSize { got: u32, expected: u32 },
#[error("error while decompressing lump")]
LumpDecompressError(lzma_rs::error::Error),
#[error("malformed utf8 data")]
Utf8Error(#[from] std::string::FromUtf8Error),
#[error("Directory entry length isn't a multiple of element size")]
MalformedLump,
#[error("invalid surface flag in {0}")]
InvalidSurfaceFlag(Name),
#[error("invalid content flag in {0}")]
InvalidContentFlag(Name),
#[error("non null-terminated name")]
InvalidName,
#[error("unexpected eof while reading data")]
UnexpectedEOF,
#[error("extra data at the end of the lump")]
UnexpectedExtraData,
#[error("error while reading data: {0}")]
ReadError(#[from] std::io::Error),
#[error("error while reading data: {0}")]
BinReadError(#[from] binread::Error),
}
pub type BspResult<T> = Result<T, BspError>;
#[derive(Debug)]
pub struct Handle<'a, T> {
bsp: &'a Bsp,
data: &'a T,
}
impl<T> Clone for Handle<'_, T> {
fn clone(&self) -> Self {
Handle { ..*self }
}
}
impl<'a, T> Handle<'a, T> {
pub fn as_ref(&self) -> &'a T {
self.data
}
}
impl<T> Deref for Handle<'_, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
self.data
}
}
#[derive(Debug, Clone)]
pub struct Leaves {
leaves: Vec<Leaf>,
}
impl Leaves {
pub fn new(mut leaves: Vec<Leaf>) -> Self {
leaves.sort_unstable_by_key(|leaf| leaf.cluster);
Leaves { leaves }
}
pub fn iter(&self) -> impl Iterator<Item = &Leaf> {
self.into_iter()
}
pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut Leaf> {
self.into_iter()
}
pub fn into_inner(self) -> Vec<Leaf> {
self.leaves
}
// TODO: There's no syntax for `-> T where &T: IntoIterator<...>` and `GroupBy`
// doesn't implement `IntoIterator` directly, only `&GroupBy`, so we have
// to explicitly specify the type.
pub fn clusters<'this>(
&'this self,
) -> GroupBy<i16, impl Iterator<Item = &'this Leaf>, impl FnMut(&&'this Leaf) -> i16> {
self.leaves.iter().group_by(|leaf: &&Leaf| leaf.cluster)
}
}
impl From<Vec<Leaf>> for Leaves {
fn from(other: Vec<Leaf>) -> Self {
Self::new(other)
}
}
impl Deref for Leaves {
type Target = [Leaf];
fn deref(&self) -> &Self::Target {
&self.leaves
}
}
impl IntoIterator for Leaves {
type IntoIter = <Vec<Leaf> as IntoIterator>::IntoIter;
type Item = Leaf;
fn into_iter(self) -> Self::IntoIter {
self.leaves.into_iter()
}
}
impl<'a> IntoIterator for &'a Leaves {
type IntoIter = <&'a [Leaf] as IntoIterator>::IntoIter;
type Item = &'a Leaf;
fn into_iter(self) -> Self::IntoIter {
(&self.leaves[..]).into_iter()
}
}
impl<'a> IntoIterator for &'a mut Leaves {
type IntoIter = <&'a mut [Leaf] as IntoIterator>::IntoIter;
type Item = &'a mut Leaf;
fn into_iter(self) -> Self::IntoIter {
(&mut self.leaves[..]).into_iter()
}
}
// TODO: Store all the allocated objects inline to improve cache usage
#[derive(Debug)]
pub struct Bsp {
pub header: Header,
pub entities: Entities,
pub textures_data: Vec<TextureData>,
pub textures_info: Vec<TextureInfo>,
pub planes: Vec<Plane>,
pub nodes: Vec<Node>,
pub leaves: Leaves,
pub leaf_faces: Vec<LeafFace>,
pub leaf_brushes: Vec<LeafBrush>,
pub models: Vec<Model>,
pub brushes: Vec<Brush>,
pub brush_sides: Vec<BrushSide>,
pub vertices: Vec<Vertex>,
pub edges: Vec<Edge>,
pub surface_edges: Vec<SurfaceEdge>,
pub faces: Vec<Face>,
pub original_faces: Vec<Face>,
pub vis_data: VisData,
}
impl Bsp {
pub fn read(data: &[u8]) -> BspResult<Self> {
let bsp_file = BspFile::new(data)?;
let entities = bsp_file.lump_reader(LumpType::Entities)?.read_entities()?;
let textures_data = bsp_file
.lump_reader(LumpType::TextureData)?
.read_vec(|r| r.read())?;
let textures_info = bsp_file
.lump_reader(LumpType::TextureInfo)?
.read_vec(|r| r.read())?;
let planes = bsp_file
.lump_reader(LumpType::Planes)?
.read_vec(|r| r.read())?;
let nodes = bsp_file
.lump_reader(LumpType::Nodes)?
.read_vec(|r| r.read())?;
let leaves = bsp_file
.lump_reader(LumpType::Leaves)?
.read_vec(|r| r.read())?
.into();
let leaf_faces = bsp_file
.lump_reader(LumpType::LeafFaces)?
.read_vec(|r| r.read())?;
let leaf_brushes = bsp_file
.lump_reader(LumpType::LeafBrushes)?
.read_vec(|r| r.read())?;
let models = bsp_file
.lump_reader(LumpType::Models)?
.read_vec(|r| r.read())?;
let brushes = bsp_file
.lump_reader(LumpType::Brushes)?
.read_vec(|r| r.read())?;
let brush_sides = bsp_file
.lump_reader(LumpType::BrushSides)?
.read_vec(|r| r.read())?;
let vertices = bsp_file
.lump_reader(LumpType::Vertices)?
.read_vec(|r| r.read())?;
let edges = bsp_file
.lump_reader(LumpType::Edges)?
.read_vec(|r| r.read())?;
let surface_edges = bsp_file
.lump_reader(LumpType::SurfaceEdges)?
.read_vec(|r| r.read())?;
let faces = bsp_file
.lump_reader(LumpType::Faces)?
.read_vec(|r| r.read())?;
let original_faces = bsp_file
.lump_reader(LumpType::OriginalFaces)?
.read_vec(|r| r.read())?;
let vis_data = bsp_file.lump_reader(LumpType::Visibility)?.read_visdata()?;
Ok({
Bsp {
header: bsp_file.header().clone(),
entities,
textures_data,
textures_info,
planes,
nodes,
leaves,
leaf_faces,
leaf_brushes,
models,
brushes,
brush_sides,
vertices,
edges,
surface_edges,
faces,
original_faces,
vis_data,
}
})
}
pub fn leaf(&self, n: usize) -> Option<Handle<'_, Leaf>> {
self.leaves.get(n).map(|leaf| Handle {
bsp: self,
data: leaf,
})
}
pub fn plane(&self, n: usize) -> Option<Handle<'_, Plane>> {
self.planes.get(n).map(|plane| Handle {
bsp: self,
data: plane,
})
}
pub fn face(&self, n: usize) -> Option<Handle<'_, Face>> {
self.faces.get(n).map(|face| Handle {
bsp: self,
data: face,
})
}
pub fn node(&self, n: usize) -> Option<Handle<'_, Node>> {
self.nodes.get(n).map(|node| Handle {
bsp: self,
data: node,
})
}
pub fn root_node(&self) -> Option<Handle<'_, Node>> {
self.node(0)
}
pub fn models(&self) -> impl Iterator<Item = Handle<'_, Model>> {
self.models.iter().map(move |m| Handle::new(self, m))
}
pub fn leaf_at(&self, point: Vector) -> Option<Handle<'_, Leaf>> {
let mut current = self.root_node()?;
loop {
let plane = current.plane()?;
let dot: f32 = point
.iter()
.zip(plane.normal.iter())
.map(|(a, b)| a * b)
.sum();
let [front, back] = current.children;
let next = if dot < plane.dist { back } else { front };
if next < 0 {
return self.leaf((!next) as usize);
} else {
current = self.node(next as usize)?;
}
}
}
pub fn original_faces(&self) -> impl Iterator<Item = Handle<Face>> {
self.faces.iter().map(move |face| Handle {
bsp: self,
data: face,
})
}
}
impl<'a, T> Handle<'a, T> {
pub fn new(bsp: &'a Bsp, data: &'a T) -> Self {
Handle { bsp, data }
}
}
impl<'a> Handle<'a, Model> {
pub fn faces(&self) -> impl Iterator<Item = Handle<'a, Face>> {
let start = self.first_face as usize;
let end = start + self.face_count as usize;
let bsp = self.bsp;
bsp.faces[start..end]
.iter()
.map(move |face| Handle::new(bsp, face))
}
}
impl<'a> Handle<'a, TextureInfo> {
pub fn texture(&self) -> Option<&TextureData> {
self.bsp
.textures_data
.get(self.data.texture_data_index as usize)
}
}
impl<'a> Handle<'a, Face> {
pub fn texture(&self) -> Option<Handle<TextureInfo>> {
self.bsp
.textures_info
.get(self.texture_info as usize)
.map(|texture_info| Handle {
bsp: self.bsp,
data: texture_info,
})
}
pub fn vertices(&self) -> impl Iterator<Item = &'a Vertex> + 'a {
let bsp = self.bsp;
self.vertex_indexes()
.flat_map(move |vert_index| bsp.vertices.get(vert_index as usize))
}
pub fn vertex_indexes(&self) -> impl Iterator<Item = u16> + 'a {
let bsp = self.bsp;
(self.data.first_edge..(self.data.first_edge + self.data.num_edges as i32))
.flat_map(move |surface_edge| bsp.surface_edges.get(surface_edge as usize))
.flat_map(move |surface_edge| {
bsp.edges
.get(surface_edge.edge_index())
.map(|edge| (edge, surface_edge.direction()))
})
.map(|(edge, direction)| match direction {
EdgeDirection::FirstToLast => edge.start_index,
EdgeDirection::LastToFirst => edge.end_index,
})
}
pub fn is_visible(&self) -> bool {
self.texture()
.map(|texture| {
!texture.flags.intersects(
TextureFlags::LIGHT
| TextureFlags::SKY2D
| TextureFlags::SKY
| TextureFlags::WARP
| TextureFlags::TRANS
| TextureFlags::TRIGGER
| TextureFlags::HINT
| TextureFlags::SKIP
| TextureFlags::NODRAW
| TextureFlags::HITBOX,
)
})
.unwrap_or_default()
}
}
impl Handle<'_, Node> {
pub fn plane(&self) -> Option<Handle<'_, Plane>> {
self.bsp.plane(self.plane_index as _)
}
}
impl<'a> Handle<'a, Leaf> {
pub fn visible_set(&self) -> Option<impl Iterator<Item = Handle<'a, Leaf>>> {
// TODO: Use `itertools::Either`?
let cluster = self.cluster;
let bsp = self.bsp;
if cluster < 0 {
None
} else {
let visible_clusters = bsp.vis_data.visible_clusters(cluster);
Some(
bsp.leaves
.iter()
.filter(move |leaf| {
if leaf.cluster == cluster {
true
} else if leaf.cluster > 0 {
visible_clusters[leaf.cluster as u64]
} else {
false
}
})
.map(move |leaf| Handle { bsp, data: leaf }),
)
}
}
pub fn faces(&self) -> impl Iterator<Item = Handle<'a, Face>> {
let start = self.first_leaf_face as usize;
let end = start + self.leaf_face_count as usize;
let bsp = self.bsp;
bsp.leaf_faces[start..end]
.iter()
.filter_map(move |leaf_face| bsp.face(leaf_face.face as usize))
}
}
#[cfg(test)]
mod tests {
use super::Bsp;
#[test]
fn tf2_file() {
use std::fs::read;
let data = read("koth_bagel_rc2a.bsp").unwrap();
Bsp::read(&data).unwrap();
}
}