mirror of
https://codeberg.org/icewind/vbsp.git
synced 2026-06-03 10:44:07 +02:00
displacement map reading
This commit is contained in:
parent
21b78f9efe
commit
d3b614c76d
4 changed files with 387 additions and 198 deletions
|
|
@ -19,6 +19,7 @@ lzma-rs = "0.2.0"
|
||||||
binrw = "0.8.0"
|
binrw = "0.8.0"
|
||||||
static_assertions = "1.1.0"
|
static_assertions = "1.1.0"
|
||||||
miette = "3.2.0"
|
miette = "3.2.0"
|
||||||
|
num_enum = "0.5.6"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
obj = "0.10"
|
obj = "0.10"
|
||||||
|
|
@ -27,3 +28,6 @@ miette = { version = "3.2.0", features = ["fancy"] }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
bench = []
|
bench = []
|
||||||
|
|
||||||
|
[profile.dev]
|
||||||
|
opt-level = 2
|
||||||
|
|
|
||||||
162
src/data.rs
162
src/data.rs
|
|
@ -5,11 +5,35 @@ use binrw::io::SeekFrom;
|
||||||
use binrw::{BinRead, BinResult, ReadOptions};
|
use binrw::{BinRead, BinResult, ReadOptions};
|
||||||
use bitflags::bitflags;
|
use bitflags::bitflags;
|
||||||
use bv::BitVec;
|
use bv::BitVec;
|
||||||
|
use num_enum::TryFromPrimitive;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::fmt::{Debug, Display, Formatter};
|
use std::fmt::{Debug, Display, Formatter};
|
||||||
use std::mem::size_of;
|
use std::io::{Read, Seek};
|
||||||
|
use std::mem::{align_of, size_of};
|
||||||
use std::ops::{Add, Index};
|
use std::ops::{Add, Index};
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
fn test_read_bytes<T: BinRead>()
|
||||||
|
where
|
||||||
|
T::Args: Default,
|
||||||
|
{
|
||||||
|
use binrw::BinReaderExt;
|
||||||
|
use std::any::type_name;
|
||||||
|
use std::io::Cursor;
|
||||||
|
|
||||||
|
let bytes = [0; 512];
|
||||||
|
let mut reader = Cursor::new(bytes);
|
||||||
|
|
||||||
|
let _ = reader.read_le::<T>().unwrap();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
reader.position() as usize,
|
||||||
|
size_of::<T>(),
|
||||||
|
"Invalid number of bytes used to read {}",
|
||||||
|
type_name::<T>()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, BinRead)]
|
#[derive(Clone, BinRead)]
|
||||||
pub struct Directories {
|
pub struct Directories {
|
||||||
entries: [LumpEntry; 64],
|
entries: [LumpEntry; 64],
|
||||||
|
|
@ -299,12 +323,17 @@ pub struct Leaf {
|
||||||
pub leaf_face_count: u16,
|
pub leaf_face_count: u16,
|
||||||
pub first_leaf_brush: u16,
|
pub first_leaf_brush: u16,
|
||||||
pub leaf_brush_count: u16,
|
pub leaf_brush_count: u16,
|
||||||
#[br(pad_size_to = 4)]
|
#[br(align_after = align_of::<Leaf>())]
|
||||||
pub leaf_watter_data_id: i16,
|
pub leaf_watter_data_id: i16,
|
||||||
}
|
}
|
||||||
|
|
||||||
static_assertions::const_assert_eq!(size_of::<Leaf>(), 32);
|
static_assertions::const_assert_eq!(size_of::<Leaf>(), 32);
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_leaf_bytes() {
|
||||||
|
test_read_bytes::<Leaf>();
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, BinRead)]
|
#[derive(Debug, Clone, BinRead)]
|
||||||
pub struct LeafBrush {
|
pub struct LeafBrush {
|
||||||
pub brush: u16,
|
pub brush: u16,
|
||||||
|
|
@ -484,3 +513,132 @@ impl VisData {
|
||||||
visible_clusters
|
visible_clusters
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, BinRead)]
|
||||||
|
pub struct DisplacementInfo {
|
||||||
|
pub start_position: Vector,
|
||||||
|
pub displacement_vertex_start: i32,
|
||||||
|
pub displacement_triangle_tag_start: i32,
|
||||||
|
|
||||||
|
pub power: i32,
|
||||||
|
pub minimum_tesselation: i32,
|
||||||
|
pub smoothing_angle: f32,
|
||||||
|
pub contents: i32,
|
||||||
|
|
||||||
|
pub map_face: u16,
|
||||||
|
|
||||||
|
#[br(align_before = 4)]
|
||||||
|
pub lightmap_alpha_start: i32,
|
||||||
|
pub lightmap_sample_position_start: i32,
|
||||||
|
|
||||||
|
pub edge_neighbours: [DisplacementNeighbour; 4],
|
||||||
|
pub corner_neighbours: [DisplacementCornerNeighbour; 4],
|
||||||
|
|
||||||
|
pub allowed_vertices: [u32; 10],
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_displacement_bytes() {
|
||||||
|
test_read_bytes::<DisplacementInfo>();
|
||||||
|
}
|
||||||
|
|
||||||
|
static_assertions::const_assert_eq!(size_of::<DisplacementInfo>(), 176);
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct DisplacementNeighbour {
|
||||||
|
pub sub_neighbours: [Option<DisplacementSubNeighbour>; 2],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BinRead for DisplacementNeighbour {
|
||||||
|
type Args = ();
|
||||||
|
|
||||||
|
fn read_options<R: Read + Seek>(
|
||||||
|
reader: &mut R,
|
||||||
|
options: &ReadOptions,
|
||||||
|
args: Self::Args,
|
||||||
|
) -> BinResult<Self> {
|
||||||
|
let raws = <[RawDisplacementSubNeighbour; 2]>::read_options(reader, options, args)?;
|
||||||
|
Ok(DisplacementNeighbour {
|
||||||
|
sub_neighbours: raws.map(|raw| raw.try_into().ok()),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static_assertions::const_assert_eq!(size_of::<DisplacementNeighbour>(), 12);
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, BinRead)]
|
||||||
|
#[br(assert(neighbour_index == u16::MAX || (neighbour_orientation < 4 && span < 4 && neighbour_span < 4), "valid neighbour index with invalid enum fields"))]
|
||||||
|
struct RawDisplacementSubNeighbour {
|
||||||
|
neighbour_index: u16,
|
||||||
|
neighbour_orientation: u8,
|
||||||
|
span: u8,
|
||||||
|
#[br(align_after = align_of::<DisplacementSubNeighbour>())]
|
||||||
|
neighbour_span: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_sub_neighbour_bytes() {
|
||||||
|
test_read_bytes::<RawDisplacementSubNeighbour>();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct DisplacementSubNeighbour {
|
||||||
|
pub neighbour_index: u16,
|
||||||
|
/// Orientation of the neighbour relative to us
|
||||||
|
pub neighbour_orientation: NeighbourOrientation,
|
||||||
|
/// How the neighbour fits into us
|
||||||
|
pub span: NeighbourSpan,
|
||||||
|
/// How we fit into our neighbour
|
||||||
|
pub neighbour_span: NeighbourSpan,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<RawDisplacementSubNeighbour> for DisplacementSubNeighbour {
|
||||||
|
type Error = ();
|
||||||
|
|
||||||
|
fn try_from(value: RawDisplacementSubNeighbour) -> Result<Self, Self::Error> {
|
||||||
|
match value.neighbour_index {
|
||||||
|
u16::MAX => Err(()),
|
||||||
|
neighbour_index => Ok(DisplacementSubNeighbour {
|
||||||
|
neighbour_index,
|
||||||
|
// note that we already checked if these enums are valid in the assert of the RawDisplacementSubNeighbour reader
|
||||||
|
neighbour_orientation: NeighbourOrientation::try_from(value.neighbour_orientation)
|
||||||
|
.unwrap(),
|
||||||
|
span: NeighbourSpan::try_from(value.span).unwrap(),
|
||||||
|
neighbour_span: NeighbourSpan::try_from(value.neighbour_span).unwrap(),
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static_assertions::const_assert_eq!(size_of::<DisplacementSubNeighbour>(), 6);
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, TryFromPrimitive)]
|
||||||
|
#[repr(u8)]
|
||||||
|
pub enum NeighbourSpan {
|
||||||
|
CornerToCorner,
|
||||||
|
CornerToMidPoint,
|
||||||
|
MidPointToCorner,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, TryFromPrimitive)]
|
||||||
|
#[repr(u8)]
|
||||||
|
pub enum NeighbourOrientation {
|
||||||
|
Ccw0,
|
||||||
|
Ccw90,
|
||||||
|
Ccw180,
|
||||||
|
Ccw270,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, BinRead)]
|
||||||
|
pub struct DisplacementCornerNeighbour {
|
||||||
|
pub neighbours: [u16; 4],
|
||||||
|
#[br(align_after = align_of::<DisplacementCornerNeighbour>())]
|
||||||
|
pub neighbour_count: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
static_assertions::const_assert_eq!(size_of::<DisplacementCornerNeighbour>(), 10);
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_corner_neighbour_bytes() {
|
||||||
|
test_read_bytes::<DisplacementCornerNeighbour>();
|
||||||
|
}
|
||||||
|
|
|
||||||
205
src/handle.rs
Normal file
205
src/handle.rs
Normal file
|
|
@ -0,0 +1,205 @@
|
||||||
|
use crate::data::*;
|
||||||
|
use crate::Bsp;
|
||||||
|
use std::ops::Deref;
|
||||||
|
|
||||||
|
/// A handle represents a data structure in the bsp file and the bsp file containing it.
|
||||||
|
///
|
||||||
|
/// Keeping a reference of the bsp file with the data is required since a lot of data types
|
||||||
|
/// reference parts from other structures in the bsp file
|
||||||
|
#[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> AsRef<T> for Handle<'a, T> {
|
||||||
|
fn as_ref(&self) -> &'a T {
|
||||||
|
self.data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Deref for Handle<'_, T> {
|
||||||
|
type Target = T;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
self.data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T> Handle<'a, T> {
|
||||||
|
pub fn new(bsp: &'a Bsp, data: &'a T) -> Self {
|
||||||
|
Handle { bsp, data }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Handle<'a, Model> {
|
||||||
|
/// Get all faces that make up the 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> {
|
||||||
|
/// Get the texture data references by the texture
|
||||||
|
pub fn texture(&self) -> Option<&TextureData> {
|
||||||
|
self.bsp
|
||||||
|
.textures_data
|
||||||
|
.get(self.data.texture_data_index as usize)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Handle<'a, Face> {
|
||||||
|
/// Get the texture of the 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,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get all vertices making up the face
|
||||||
|
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))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the vertex indexes of all vertices making up the face
|
||||||
|
///
|
||||||
|
/// The indexes index into the `vertices` field of the bsp file
|
||||||
|
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,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check if the face is flagged as visible
|
||||||
|
pub fn is_visible(&self) -> bool {
|
||||||
|
self.texture()
|
||||||
|
.map(|texture| {
|
||||||
|
!texture.flags.intersects(
|
||||||
|
TextureFlags::LIGHT
|
||||||
|
| TextureFlags::SKY2D
|
||||||
|
| TextureFlags::SKY
|
||||||
|
| TextureFlags::WARP
|
||||||
|
| TextureFlags::TRIGGER
|
||||||
|
| TextureFlags::HINT
|
||||||
|
| TextureFlags::SKIP
|
||||||
|
| TextureFlags::NODRAW
|
||||||
|
| TextureFlags::HITBOX,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.unwrap_or_default()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Triangulate the face
|
||||||
|
///
|
||||||
|
/// Triangulation only works for faces that can be turned into a triangle fan trivially
|
||||||
|
pub fn triangulate(&self) -> impl Iterator<Item = [Vector; 3]> + 'a {
|
||||||
|
let mut vertices = self.vertices();
|
||||||
|
|
||||||
|
let a = vertices.next().expect("face with <3 points");
|
||||||
|
let mut b = vertices.next().expect("face with <3 points");
|
||||||
|
|
||||||
|
vertices.map(move |c| {
|
||||||
|
let points = [a.position, b.position, c.position];
|
||||||
|
b = c;
|
||||||
|
points
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Handle<'_, Node> {
|
||||||
|
/// Get the plane splitting this node
|
||||||
|
pub fn plane(&self) -> Option<Handle<'_, Plane>> {
|
||||||
|
self.bsp.plane(self.plane_index as _)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Handle<'a, Leaf> {
|
||||||
|
/// Get all other leaves visible from this one
|
||||||
|
pub fn visible_set(&self) -> Option<impl Iterator<Item = Handle<'a, Leaf>>> {
|
||||||
|
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 }),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get all faces in this 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))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Handle<'a, DisplacementInfo> {
|
||||||
|
pub fn edge_neighbours(&self) -> impl Iterator<Item = Handle<'a, DisplacementSubNeighbour>> {
|
||||||
|
self.data
|
||||||
|
.edge_neighbours
|
||||||
|
.iter()
|
||||||
|
.flat_map(|edge| &edge.sub_neighbours)
|
||||||
|
.filter_map(|sub| sub.as_ref())
|
||||||
|
.map(|sub| Handle::new(self.bsp, sub))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn corner_neighbours(&self) -> impl Iterator<Item = Handle<'a, DisplacementInfo>> {
|
||||||
|
self.data
|
||||||
|
.corner_neighbours
|
||||||
|
.iter()
|
||||||
|
.flat_map(|corner| &corner.neighbours[0..corner.neighbour_count.min(4) as usize])
|
||||||
|
.copied()
|
||||||
|
.filter_map(|id| self.bsp.displacement(id as usize))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Handle<'a, DisplacementSubNeighbour> {
|
||||||
|
pub fn displacement(&self) -> Option<Handle<'a, DisplacementInfo>> {
|
||||||
|
self.bsp.displacement(self.data.neighbour_index as usize)
|
||||||
|
}
|
||||||
|
}
|
||||||
214
src/lib.rs
214
src/lib.rs
|
|
@ -1,12 +1,14 @@
|
||||||
mod bspfile;
|
mod bspfile;
|
||||||
pub mod data;
|
pub mod data;
|
||||||
mod error;
|
mod error;
|
||||||
|
mod handle;
|
||||||
mod reader;
|
mod reader;
|
||||||
|
|
||||||
use crate::bspfile::LumpType;
|
use crate::bspfile::LumpType;
|
||||||
pub use crate::data::TextureFlags;
|
pub use crate::data::TextureFlags;
|
||||||
pub use crate::data::Vector;
|
pub use crate::data::Vector;
|
||||||
use crate::data::*;
|
use crate::data::*;
|
||||||
|
use crate::handle::Handle;
|
||||||
use binrw::io::Cursor;
|
use binrw::io::Cursor;
|
||||||
use binrw::BinRead;
|
use binrw::BinRead;
|
||||||
use bspfile::BspFile;
|
use bspfile::BspFile;
|
||||||
|
|
@ -16,36 +18,6 @@ use std::{io::Read, ops::Deref};
|
||||||
|
|
||||||
pub type BspResult<T> = Result<T, BspError>;
|
pub type BspResult<T> = Result<T, BspError>;
|
||||||
|
|
||||||
/// A handle represents a data structure in the bsp file and the bsp file containing it.
|
|
||||||
///
|
|
||||||
/// Keeping a reference of the bsp file with the data is required since a lot of data types
|
|
||||||
/// reference parts from other structures in the bsp file
|
|
||||||
#[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> AsRef<T> for Handle<'a, T> {
|
|
||||||
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)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Leaves {
|
pub struct Leaves {
|
||||||
leaves: Vec<Leaf>,
|
leaves: Vec<Leaf>,
|
||||||
|
|
@ -199,6 +171,7 @@ pub struct Bsp {
|
||||||
pub faces: Vec<Face>,
|
pub faces: Vec<Face>,
|
||||||
pub original_faces: Vec<Face>,
|
pub original_faces: Vec<Face>,
|
||||||
pub vis_data: VisData,
|
pub vis_data: VisData,
|
||||||
|
pub displacements: Vec<DisplacementInfo>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Bsp {
|
impl Bsp {
|
||||||
|
|
@ -253,6 +226,9 @@ impl Bsp {
|
||||||
.lump_reader(LumpType::OriginalFaces)?
|
.lump_reader(LumpType::OriginalFaces)?
|
||||||
.read_vec(|r| r.read())?;
|
.read_vec(|r| r.read())?;
|
||||||
let vis_data = bsp_file.lump_reader(LumpType::Visibility)?.read_visdata()?;
|
let vis_data = bsp_file.lump_reader(LumpType::Visibility)?.read_visdata()?;
|
||||||
|
let displacements = bsp_file
|
||||||
|
.lump_reader(LumpType::DisplacementInfo)?
|
||||||
|
.read_vec(|r| r.read())?;
|
||||||
|
|
||||||
Ok({
|
Ok({
|
||||||
Bsp {
|
Bsp {
|
||||||
|
|
@ -274,36 +250,31 @@ impl Bsp {
|
||||||
faces,
|
faces,
|
||||||
original_faces,
|
original_faces,
|
||||||
vis_data,
|
vis_data,
|
||||||
|
displacements,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn leaf(&self, n: usize) -> Option<Handle<'_, Leaf>> {
|
pub fn leaf(&self, n: usize) -> Option<Handle<'_, Leaf>> {
|
||||||
self.leaves.get(n).map(|leaf| Handle {
|
self.leaves.get(n).map(|leaf| Handle::new(self, leaf))
|
||||||
bsp: self,
|
|
||||||
data: leaf,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn plane(&self, n: usize) -> Option<Handle<'_, Plane>> {
|
pub fn plane(&self, n: usize) -> Option<Handle<'_, Plane>> {
|
||||||
self.planes.get(n).map(|plane| Handle {
|
self.planes.get(n).map(|plane| Handle::new(self, plane))
|
||||||
bsp: self,
|
|
||||||
data: plane,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn face(&self, n: usize) -> Option<Handle<'_, Face>> {
|
pub fn face(&self, n: usize) -> Option<Handle<'_, Face>> {
|
||||||
self.faces.get(n).map(|face| Handle {
|
self.faces.get(n).map(|face| Handle::new(self, face))
|
||||||
bsp: self,
|
|
||||||
data: face,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn node(&self, n: usize) -> Option<Handle<'_, Node>> {
|
pub fn node(&self, n: usize) -> Option<Handle<'_, Node>> {
|
||||||
self.nodes.get(n).map(|node| Handle {
|
self.nodes.get(n).map(|node| Handle::new(self, node))
|
||||||
bsp: self,
|
}
|
||||||
data: node,
|
|
||||||
})
|
pub fn displacement(&self, n: usize) -> Option<Handle<'_, DisplacementInfo>> {
|
||||||
|
self.displacements
|
||||||
|
.get(n)
|
||||||
|
.map(|displacement| Handle::new(self, displacement))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the root node of the bsp
|
/// Get the root node of the bsp
|
||||||
|
|
@ -342,156 +313,7 @@ impl Bsp {
|
||||||
|
|
||||||
/// Get all faces stored in the bsp
|
/// Get all faces stored in the bsp
|
||||||
pub fn original_faces(&self) -> impl Iterator<Item = Handle<Face>> {
|
pub fn original_faces(&self) -> impl Iterator<Item = Handle<Face>> {
|
||||||
self.faces.iter().map(move |face| Handle {
|
self.faces.iter().map(move |face| Handle::new(self, face))
|
||||||
bsp: self,
|
|
||||||
data: face,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T> Handle<'a, T> {
|
|
||||||
fn new(bsp: &'a Bsp, data: &'a T) -> Self {
|
|
||||||
Handle { bsp, data }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> Handle<'a, Model> {
|
|
||||||
/// Get all faces that make up the 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> {
|
|
||||||
/// Get the texture data references by the texture
|
|
||||||
pub fn texture(&self) -> Option<&TextureData> {
|
|
||||||
self.bsp
|
|
||||||
.textures_data
|
|
||||||
.get(self.data.texture_data_index as usize)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> Handle<'a, Face> {
|
|
||||||
/// Get the texture of the 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,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get all vertices making up the face
|
|
||||||
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))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get the vertex indexes of all vertices making up the face
|
|
||||||
///
|
|
||||||
/// The indexes index into the `vertices` field of the bsp file
|
|
||||||
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,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Check if the face is flagged as visible
|
|
||||||
pub fn is_visible(&self) -> bool {
|
|
||||||
self.texture()
|
|
||||||
.map(|texture| {
|
|
||||||
!texture.flags.intersects(
|
|
||||||
TextureFlags::LIGHT
|
|
||||||
| TextureFlags::SKY2D
|
|
||||||
| TextureFlags::SKY
|
|
||||||
| TextureFlags::WARP
|
|
||||||
| TextureFlags::TRIGGER
|
|
||||||
| TextureFlags::HINT
|
|
||||||
| TextureFlags::SKIP
|
|
||||||
| TextureFlags::NODRAW
|
|
||||||
| TextureFlags::HITBOX,
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.unwrap_or_default()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Triangulate the face
|
|
||||||
///
|
|
||||||
/// Triangulation only works for faces that can be turned into a triangle fan trivially
|
|
||||||
pub fn triangulate(&self) -> impl Iterator<Item = [Vector; 3]> + 'a {
|
|
||||||
let mut vertices = self.vertices();
|
|
||||||
|
|
||||||
let a = vertices.next().expect("face with <3 points");
|
|
||||||
let mut b = vertices.next().expect("face with <3 points");
|
|
||||||
|
|
||||||
vertices.map(move |c| {
|
|
||||||
let points = [a.position, b.position, c.position];
|
|
||||||
b = c;
|
|
||||||
points
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Handle<'_, Node> {
|
|
||||||
/// Get the plane splitting this node
|
|
||||||
pub fn plane(&self) -> Option<Handle<'_, Plane>> {
|
|
||||||
self.bsp.plane(self.plane_index as _)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> Handle<'a, Leaf> {
|
|
||||||
/// Get all other leaves visible from this one
|
|
||||||
pub fn visible_set(&self) -> Option<impl Iterator<Item = Handle<'a, Leaf>>> {
|
|
||||||
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 }),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get all faces in this 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))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue