mirror of
https://codeberg.org/icewind/vbsp.git
synced 2026-06-03 18:54:05 +02:00
split data structs
This commit is contained in:
parent
771ae67730
commit
90146abcce
2 changed files with 401 additions and 473 deletions
395
src/data.rs
Normal file
395
src/data.rs
Normal file
|
|
@ -0,0 +1,395 @@
|
||||||
|
use crate::bspfile::LumpType;
|
||||||
|
use arrayvec::ArrayString;
|
||||||
|
use binread::io::SeekFrom;
|
||||||
|
use binread::{BinRead, BinResult, ReadOptions};
|
||||||
|
use bitflags::bitflags;
|
||||||
|
use bv::BitVec;
|
||||||
|
use parse_display::Display;
|
||||||
|
use std::fmt;
|
||||||
|
use std::io::{Error, ErrorKind};
|
||||||
|
use std::mem::size_of;
|
||||||
|
use std::ops::Index;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct Directories {
|
||||||
|
entries: [LumpEntry; 64],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BinRead for Directories {
|
||||||
|
type Args = <LumpEntry as BinRead>::Args;
|
||||||
|
|
||||||
|
fn read_options<R: binread::io::Read + binread::io::Seek>(
|
||||||
|
reader: &mut R,
|
||||||
|
options: &ReadOptions,
|
||||||
|
args: Self::Args,
|
||||||
|
) -> BinResult<Self> {
|
||||||
|
let mut entries = [LumpEntry::default(); 64];
|
||||||
|
for i in 0..64 {
|
||||||
|
entries[i] = LumpEntry::read_options(reader, options, args)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Directories { entries })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Index<LumpType> for Directories {
|
||||||
|
type Output = LumpEntry;
|
||||||
|
|
||||||
|
fn index(&self, index: LumpType) -> &Self::Output {
|
||||||
|
&self.entries[index as usize]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, BinRead)]
|
||||||
|
#[br(little)]
|
||||||
|
pub struct Header {
|
||||||
|
pub v: u8,
|
||||||
|
pub b: u8,
|
||||||
|
pub s: u8,
|
||||||
|
pub p: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, Default, BinRead)]
|
||||||
|
#[br(little)]
|
||||||
|
pub struct LumpEntry {
|
||||||
|
pub offset: u32,
|
||||||
|
pub length: u32,
|
||||||
|
pub version: u32,
|
||||||
|
pub ident: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, BinRead)]
|
||||||
|
pub struct LeafFace {
|
||||||
|
pub face: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct Entities {
|
||||||
|
pub entities: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for Entities {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct Entities<'a> {
|
||||||
|
entities: Vec<Entity<'a>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
Entities {
|
||||||
|
entities: self.iter().collect(),
|
||||||
|
}
|
||||||
|
.fmt(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Entities {
|
||||||
|
pub fn iter(&self) -> impl Iterator<Item = Entity<'_>> {
|
||||||
|
struct Iter<'a> {
|
||||||
|
buf: &'a str,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Iterator for Iter<'a> {
|
||||||
|
type Item = Entity<'a>;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
let start = self.buf.find('{')? + 1;
|
||||||
|
let end = start + self.buf[start..].find('}')?;
|
||||||
|
|
||||||
|
let out = &self.buf[start..end];
|
||||||
|
|
||||||
|
self.buf = &self.buf[end + 1..];
|
||||||
|
|
||||||
|
Some(Entity { buf: out })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Iter {
|
||||||
|
buf: &self.entities,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct Entity<'a> {
|
||||||
|
buf: &'a str,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for Entity<'_> {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
self.properties().collect::<HashMap<_, _>>().fmt(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Entity<'a> {
|
||||||
|
pub fn properties(&self) -> impl Iterator<Item = (&'a str, &'a str)> {
|
||||||
|
struct Iter<'a> {
|
||||||
|
buf: &'a str,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Iterator for Iter<'a> {
|
||||||
|
type Item = (&'a str, &'a str);
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
let start = self.buf.find('"')? + 1;
|
||||||
|
let end = start + self.buf[start..].find('"')?;
|
||||||
|
|
||||||
|
let key = &self.buf[start..end];
|
||||||
|
|
||||||
|
let rest = &self.buf[end + 1..];
|
||||||
|
|
||||||
|
let start = rest.find('"')? + 1;
|
||||||
|
let end = start + rest[start..].find('"')?;
|
||||||
|
|
||||||
|
let value = &rest[start..end];
|
||||||
|
|
||||||
|
self.buf = &rest[end + 1..];
|
||||||
|
|
||||||
|
Some((key, value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Iter { buf: &self.buf }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bitflags! {
|
||||||
|
#[derive(BinRead)]
|
||||||
|
pub struct TextureFlags: u32 {
|
||||||
|
const LIGHT = 0b0000_0000_0000_0000_0001; // value will hold the light strength
|
||||||
|
const SKY2D = 0b0000_0000_0000_0000_0010; // don't draw, indicate we should skylight + draw 2d sky but don't draw the 3d skybox
|
||||||
|
const SKY = 0b0000_0000_0000_0000_0100; // don't draw, but add the skybox
|
||||||
|
const WARP = 0b0000_0000_0000_0000_1000; // turbulent water warp
|
||||||
|
const TRANS = 0b0000_0000_0000_0001_0000; // texture is translucent
|
||||||
|
const NOPORTAL = 0b0000_0000_0000_0010_0000; // the surface can't have a portal placed on it
|
||||||
|
const TRIGGER = 0b0000_0000_0000_0100_0000; // xbox hack to work around elimination of trigger surfaces
|
||||||
|
const NODRAW = 0b0000_0000_0000_1000_0000; // don't bother referencing the texture
|
||||||
|
const HINT = 0b0000_0000_0001_0000_0000; // make a primary bsp splitter
|
||||||
|
const SKIP = 0b0000_0000_0010_0000_0000; // completely ignore, allowing non-closed brushes
|
||||||
|
const NOLIGHT = 0b0000_0000_0100_0000_0000; // dont calculate light
|
||||||
|
const BUMPLIGHT = 0b0000_0000_1000_0000_0000; // calculate thee light maps for the surface for bump mapping
|
||||||
|
const NOSHADOWS = 0b0000_0001_0000_0000_0000; // don't receive shadows
|
||||||
|
const NODECALS = 0b0000_0010_0000_0000_0000; // don't receive decals
|
||||||
|
const NOCHOP = 0b0000_0100_0000_0000_0000; // don't subdivide patches on this surface
|
||||||
|
const HITBOX = 0b0000_1000_0000_0000_0000; // surface is part of a hitbox
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Display, Clone)]
|
||||||
|
pub struct Name(ArrayString<[u8; 64]>);
|
||||||
|
|
||||||
|
impl BinRead for Name {
|
||||||
|
type Args = ();
|
||||||
|
|
||||||
|
fn read_options<R: binread::io::Read + binread::io::Seek>(
|
||||||
|
reader: &mut R,
|
||||||
|
options: &ReadOptions,
|
||||||
|
args: Self::Args,
|
||||||
|
) -> BinResult<Self> {
|
||||||
|
use std::str;
|
||||||
|
|
||||||
|
let mut name_buf: [u8; 64] = [0; 64];
|
||||||
|
|
||||||
|
for i in 0..64 {
|
||||||
|
name_buf[i] = u8::read_options(reader, options, args)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let zero_pos =
|
||||||
|
name_buf
|
||||||
|
.iter()
|
||||||
|
.position(|c| *c == 0)
|
||||||
|
.ok_or_else(|| binread::Error::AssertFail {
|
||||||
|
pos: reader.seek(SeekFrom::Current(0)).unwrap() as usize,
|
||||||
|
message: "Name not null terminated".to_string(),
|
||||||
|
})?;
|
||||||
|
let name = &name_buf[..zero_pos];
|
||||||
|
Ok(Name(
|
||||||
|
ArrayString::from(
|
||||||
|
str::from_utf8(name).map_err(|err| Error::new(ErrorKind::InvalidData, err))?,
|
||||||
|
)
|
||||||
|
.expect(
|
||||||
|
"Programmer error: it should be impossible for the string to exceed the capacity",
|
||||||
|
),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, BinRead)]
|
||||||
|
pub struct Vector {
|
||||||
|
x: f32,
|
||||||
|
y: f32,
|
||||||
|
z: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, BinRead)]
|
||||||
|
pub struct TextureInfo {
|
||||||
|
pub texture_scale: [f32; 4],
|
||||||
|
pub texture_transform: [f32; 4],
|
||||||
|
pub light_map_scale: [f32; 4],
|
||||||
|
pub light_map_transform: [f32; 4],
|
||||||
|
pub flags: TextureFlags,
|
||||||
|
pub texture_data_index: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
static_assertions::const_assert_eq!(size_of::<TextureInfo>(), 72);
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, BinRead)]
|
||||||
|
pub struct TextureData {
|
||||||
|
pub reflectivity: Vector,
|
||||||
|
pub name_string_table_id: i32,
|
||||||
|
pub width: i32,
|
||||||
|
pub height: i32,
|
||||||
|
pub view_width: i32,
|
||||||
|
pub view_height: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, BinRead)]
|
||||||
|
pub struct Plane {
|
||||||
|
pub normal: Vector,
|
||||||
|
pub dist: f32,
|
||||||
|
pub ty: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, BinRead)]
|
||||||
|
pub struct Node {
|
||||||
|
pub plane: u32,
|
||||||
|
pub children: [i32; 2],
|
||||||
|
pub mins: [i32; 3],
|
||||||
|
pub maxs: [i32; 3],
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, BinRead)]
|
||||||
|
pub struct Leaf {
|
||||||
|
pub cluster: i32,
|
||||||
|
pub area: u32,
|
||||||
|
pub mins: [i32; 3],
|
||||||
|
pub maxs: [i32; 3],
|
||||||
|
pub leaf_face: u32,
|
||||||
|
pub num_leaf_faces: u32,
|
||||||
|
pub leaf_brush: u32,
|
||||||
|
pub num_leaf_brushes: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, BinRead)]
|
||||||
|
pub struct LeafBrush {
|
||||||
|
pub brush: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, BinRead)]
|
||||||
|
pub struct Model {
|
||||||
|
pub mins: [f32; 3],
|
||||||
|
pub maxs: [f32; 3],
|
||||||
|
pub face: u32,
|
||||||
|
pub num_faces: u32,
|
||||||
|
pub brush: u32,
|
||||||
|
pub num_brushes: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, BinRead)]
|
||||||
|
pub struct Brush {
|
||||||
|
pub brush_side: u32,
|
||||||
|
pub num_brush_sides: u32,
|
||||||
|
pub texture: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, BinRead)]
|
||||||
|
pub struct BrushSide {
|
||||||
|
pub plane: u32,
|
||||||
|
pub texture: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, BinRead)]
|
||||||
|
pub struct Vertex {
|
||||||
|
pub position: Vector,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, BinRead)]
|
||||||
|
pub struct Edge {
|
||||||
|
pub start_index: u16,
|
||||||
|
pub end_index: u16,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum EdgeDirection {
|
||||||
|
FirstToLast,
|
||||||
|
LastToFirst,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, BinRead)]
|
||||||
|
pub struct SurfaceEdge {
|
||||||
|
edge: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SurfaceEdge {
|
||||||
|
pub fn edge_index(&self) -> usize {
|
||||||
|
self.edge.abs() as usize
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn direction(&self) -> EdgeDirection {
|
||||||
|
if self.edge >= 0 {
|
||||||
|
EdgeDirection::FirstToLast
|
||||||
|
} else {
|
||||||
|
EdgeDirection::LastToFirst
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, BinRead)]
|
||||||
|
pub struct MeshVert {
|
||||||
|
pub offset: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, BinRead)]
|
||||||
|
pub struct Effect {
|
||||||
|
pub name: Name,
|
||||||
|
pub brush: u32,
|
||||||
|
pub unknown: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, BinRead)]
|
||||||
|
pub struct Face {
|
||||||
|
pub plane_num: u16,
|
||||||
|
pub side: u8,
|
||||||
|
pub on_node: u8,
|
||||||
|
pub first_edge: i32,
|
||||||
|
pub num_edges: i16,
|
||||||
|
pub texture_info: i16,
|
||||||
|
pub displacement_info: i16,
|
||||||
|
pub surface_fog_volume_id: i16,
|
||||||
|
pub styles: [u8; 4],
|
||||||
|
pub light_offset: i32,
|
||||||
|
pub area: f32,
|
||||||
|
pub light_map_texture_min: [i32; 2],
|
||||||
|
pub light_map_texture_size: [i32; 2],
|
||||||
|
pub original_face: i32,
|
||||||
|
pub primitive_count: u16,
|
||||||
|
pub first_primitive_index: u16,
|
||||||
|
pub smoothing_groups: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
static_assertions::const_assert_eq!(size_of::<Face>(), 56);
|
||||||
|
|
||||||
|
const LIGHTMAP_SIZE: usize = 128;
|
||||||
|
|
||||||
|
#[derive(Default, Clone, Copy, BinRead, Debug)]
|
||||||
|
pub struct LightColor {
|
||||||
|
r: u8,
|
||||||
|
g: u8,
|
||||||
|
b: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, BinRead)]
|
||||||
|
pub struct Lightvol {
|
||||||
|
ambient: [u8; 3],
|
||||||
|
directional: [u8; 3],
|
||||||
|
dir: [u8; 2],
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Debug, Clone)]
|
||||||
|
pub struct VisData {
|
||||||
|
pub n_vecs: u32,
|
||||||
|
// Number of vectors.
|
||||||
|
pub sz_vecs: u32,
|
||||||
|
// Size of each vector, in bytes.
|
||||||
|
pub vecs: BitVec<u8>, // Visibility data. One bit per cluster per vector.
|
||||||
|
}
|
||||||
479
src/lib.rs
479
src/lib.rs
|
|
@ -2,25 +2,20 @@
|
||||||
#![allow(unreachable_code)]
|
#![allow(unreachable_code)]
|
||||||
|
|
||||||
mod bspfile;
|
mod bspfile;
|
||||||
|
mod data;
|
||||||
mod reader;
|
mod reader;
|
||||||
|
|
||||||
use crate::bspfile::LumpType;
|
use crate::bspfile::LumpType;
|
||||||
use arrayvec::ArrayString;
|
use crate::data::*;
|
||||||
use binread::io::{Cursor, SeekFrom};
|
use binread::io::Cursor;
|
||||||
use binread::{BinRead, BinResult, ReadOptions};
|
use binread::BinRead;
|
||||||
use bitflags::bitflags;
|
|
||||||
use bspfile::BspFile;
|
use bspfile::BspFile;
|
||||||
use bv::BitVec;
|
|
||||||
use itertools::{GroupBy, Itertools};
|
use itertools::{GroupBy, Itertools};
|
||||||
use parse_display::Display;
|
|
||||||
use reader::LumpReader;
|
use reader::LumpReader;
|
||||||
use std::ops::Index;
|
|
||||||
use std::{
|
use std::{
|
||||||
convert::{TryFrom, TryInto},
|
convert::TryInto,
|
||||||
fmt,
|
io::{self, Read},
|
||||||
io::{self, Error, ErrorKind, Read},
|
|
||||||
iter::once,
|
iter::once,
|
||||||
mem::size_of,
|
|
||||||
ops::Deref,
|
ops::Deref,
|
||||||
};
|
};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
@ -57,468 +52,6 @@ pub enum BspError {
|
||||||
|
|
||||||
pub type BspResult<T> = Result<T, BspError>;
|
pub type BspResult<T> = Result<T, BspError>;
|
||||||
|
|
||||||
#[derive(Debug, Clone, BinRead)]
|
|
||||||
#[repr(u32)]
|
|
||||||
pub enum FaceType {
|
|
||||||
Polygon = 1,
|
|
||||||
Patch = 2,
|
|
||||||
Mesh = 3,
|
|
||||||
Billboard = 4,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct FaceTypeError;
|
|
||||||
|
|
||||||
impl fmt::Display for FaceTypeError {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
write!(f, "Invalid face type ID")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::error::Error for FaceTypeError {}
|
|
||||||
|
|
||||||
impl TryFrom<u32> for FaceType {
|
|
||||||
type Error = FaceTypeError;
|
|
||||||
|
|
||||||
fn try_from(other: u32) -> Result<Self, FaceTypeError> {
|
|
||||||
const POLYGON: u32 = FaceType::Polygon as u32;
|
|
||||||
const PATCH: u32 = FaceType::Patch as u32;
|
|
||||||
const MESH: u32 = FaceType::Mesh as u32;
|
|
||||||
const BILLBOARD: u32 = FaceType::Billboard as u32;
|
|
||||||
|
|
||||||
match other {
|
|
||||||
POLYGON => Ok(FaceType::Polygon),
|
|
||||||
PATCH => Ok(FaceType::Patch),
|
|
||||||
MESH => Ok(FaceType::Mesh),
|
|
||||||
BILLBOARD => Ok(FaceType::Billboard),
|
|
||||||
_ => Err(FaceTypeError),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct Directories {
|
|
||||||
entries: [LumpEntry; 64],
|
|
||||||
}
|
|
||||||
|
|
||||||
impl BinRead for Directories {
|
|
||||||
type Args = <LumpEntry as BinRead>::Args;
|
|
||||||
|
|
||||||
fn read_options<R: binread::io::Read + binread::io::Seek>(
|
|
||||||
reader: &mut R,
|
|
||||||
options: &ReadOptions,
|
|
||||||
args: Self::Args,
|
|
||||||
) -> BinResult<Self> {
|
|
||||||
let mut entries = [LumpEntry::default(); 64];
|
|
||||||
for i in 0..64 {
|
|
||||||
entries[i] = LumpEntry::read_options(reader, options, args)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(Directories { entries })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Index<LumpType> for Directories {
|
|
||||||
type Output = LumpEntry;
|
|
||||||
|
|
||||||
fn index(&self, index: LumpType) -> &Self::Output {
|
|
||||||
&self.entries[index as usize]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, BinRead)]
|
|
||||||
#[br(little)]
|
|
||||||
pub struct Header {
|
|
||||||
pub v: u8,
|
|
||||||
pub b: u8,
|
|
||||||
pub s: u8,
|
|
||||||
pub p: u8,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Default, BinRead)]
|
|
||||||
#[br(little)]
|
|
||||||
pub struct LumpEntry {
|
|
||||||
offset: u32,
|
|
||||||
length: u32,
|
|
||||||
version: u32,
|
|
||||||
ident: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, BinRead)]
|
|
||||||
pub struct LeafFace {
|
|
||||||
pub face: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct Entities {
|
|
||||||
entities: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Debug for Entities {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
#[derive(Debug)]
|
|
||||||
struct Entities<'a> {
|
|
||||||
entities: Vec<Entity<'a>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
Entities {
|
|
||||||
entities: self.iter().collect(),
|
|
||||||
}
|
|
||||||
.fmt(f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Entities {
|
|
||||||
pub fn iter(&self) -> impl Iterator<Item = Entity<'_>> {
|
|
||||||
struct Iter<'a> {
|
|
||||||
buf: &'a str,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> Iterator for Iter<'a> {
|
|
||||||
type Item = Entity<'a>;
|
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
|
||||||
let start = self.buf.find('{')? + 1;
|
|
||||||
let end = start + self.buf[start..].find('}')?;
|
|
||||||
|
|
||||||
let out = &self.buf[start..end];
|
|
||||||
|
|
||||||
self.buf = &self.buf[end + 1..];
|
|
||||||
|
|
||||||
Some(Entity { buf: out })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Iter {
|
|
||||||
buf: &self.entities,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct Entity<'a> {
|
|
||||||
buf: &'a str,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Debug for Entity<'_> {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
use std::collections::HashMap;
|
|
||||||
|
|
||||||
self.properties().collect::<HashMap<_, _>>().fmt(f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> Entity<'a> {
|
|
||||||
pub fn properties(&self) -> impl Iterator<Item = (&'a str, &'a str)> {
|
|
||||||
struct Iter<'a> {
|
|
||||||
buf: &'a str,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> Iterator for Iter<'a> {
|
|
||||||
type Item = (&'a str, &'a str);
|
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
|
||||||
let start = self.buf.find('"')? + 1;
|
|
||||||
let end = start + self.buf[start..].find('"')?;
|
|
||||||
|
|
||||||
let key = &self.buf[start..end];
|
|
||||||
|
|
||||||
let rest = &self.buf[end + 1..];
|
|
||||||
|
|
||||||
let start = rest.find('"')? + 1;
|
|
||||||
let end = start + rest[start..].find('"')?;
|
|
||||||
|
|
||||||
let value = &rest[start..end];
|
|
||||||
|
|
||||||
self.buf = &rest[end + 1..];
|
|
||||||
|
|
||||||
Some((key, value))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Iter { buf: &self.buf }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bitflags! {
|
|
||||||
#[derive(BinRead)]
|
|
||||||
pub struct TextureFlags: u32 {
|
|
||||||
const LIGHT = 0b0000_0000_0000_0000_0001; // value will hold the light strength
|
|
||||||
const SKY2D = 0b0000_0000_0000_0000_0010; // don't draw, indicate we should skylight + draw 2d sky but don't draw the 3d skybox
|
|
||||||
const SKY = 0b0000_0000_0000_0000_0100; // don't draw, but add the skybox
|
|
||||||
const WARP = 0b0000_0000_0000_0000_1000; // turbulent water warp
|
|
||||||
const TRANS = 0b0000_0000_0000_0001_0000; // texture is translucent
|
|
||||||
const NOPORTAL = 0b0000_0000_0000_0010_0000; // the surface can't have a portal placed on it
|
|
||||||
const TRIGGER = 0b0000_0000_0000_0100_0000; // xbox hack to work around elimination of trigger surfaces
|
|
||||||
const NODRAW = 0b0000_0000_0000_1000_0000; // don't bother referencing the texture
|
|
||||||
const HINT = 0b0000_0000_0001_0000_0000; // make a primary bsp splitter
|
|
||||||
const SKIP = 0b0000_0000_0010_0000_0000; // completely ignore, allowing non-closed brushes
|
|
||||||
const NOLIGHT = 0b0000_0000_0100_0000_0000; // dont calculate light
|
|
||||||
const BUMPLIGHT = 0b0000_0000_1000_0000_0000; // calculate thee light maps for the surface for bump mapping
|
|
||||||
const NOSHADOWS = 0b0000_0001_0000_0000_0000; // don't receive shadows
|
|
||||||
const NODECALS = 0b0000_0010_0000_0000_0000; // don't receive decals
|
|
||||||
const NOCHOP = 0b0000_0100_0000_0000_0000; // don't subdivide patches on this surface
|
|
||||||
const HITBOX = 0b0000_1000_0000_0000_0000; // surface is part of a hitbox
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Display, Clone)]
|
|
||||||
pub struct Name(ArrayString<[u8; 64]>);
|
|
||||||
|
|
||||||
impl BinRead for Name {
|
|
||||||
type Args = ();
|
|
||||||
|
|
||||||
fn read_options<R: binread::io::Read + binread::io::Seek>(
|
|
||||||
reader: &mut R,
|
|
||||||
options: &ReadOptions,
|
|
||||||
args: Self::Args,
|
|
||||||
) -> BinResult<Self> {
|
|
||||||
use std::str;
|
|
||||||
|
|
||||||
let mut name_buf: [u8; 64] = [0; 64];
|
|
||||||
|
|
||||||
for i in 0..64 {
|
|
||||||
name_buf[i] = u8::read_options(reader, options, args)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
let zero_pos =
|
|
||||||
name_buf
|
|
||||||
.iter()
|
|
||||||
.position(|c| *c == 0)
|
|
||||||
.ok_or_else(|| binread::Error::AssertFail {
|
|
||||||
pos: reader.seek(SeekFrom::Current(0)).unwrap() as usize,
|
|
||||||
message: "Name not null terminated".to_string(),
|
|
||||||
})?;
|
|
||||||
let name = &name_buf[..zero_pos];
|
|
||||||
Ok(Name(
|
|
||||||
ArrayString::from(
|
|
||||||
str::from_utf8(name).map_err(|err| Error::new(ErrorKind::InvalidData, err))?,
|
|
||||||
)
|
|
||||||
.expect(
|
|
||||||
"Programmer error: it should be impossible for the string to exceed the capacity",
|
|
||||||
),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, BinRead)]
|
|
||||||
pub struct Vector {
|
|
||||||
x: f32,
|
|
||||||
y: f32,
|
|
||||||
z: f32,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, BinRead)]
|
|
||||||
pub struct TextureInfo {
|
|
||||||
pub texture_scale: [f32; 4],
|
|
||||||
pub texture_transform: [f32; 4],
|
|
||||||
pub light_map_scale: [f32; 4],
|
|
||||||
pub light_map_transform: [f32; 4],
|
|
||||||
pub flags: TextureFlags,
|
|
||||||
pub texture_data_index: i32,
|
|
||||||
}
|
|
||||||
|
|
||||||
static_assertions::const_assert_eq!(size_of::<TextureInfo>(), 72);
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, BinRead)]
|
|
||||||
pub struct TextureData {
|
|
||||||
pub reflectivity: Vector,
|
|
||||||
pub name_string_table_id: i32,
|
|
||||||
pub width: i32,
|
|
||||||
pub height: i32,
|
|
||||||
pub view_width: i32,
|
|
||||||
pub view_height: i32,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, BinRead)]
|
|
||||||
pub struct Plane {
|
|
||||||
pub normal: Vector,
|
|
||||||
pub dist: f32,
|
|
||||||
pub ty: i32,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, BinRead)]
|
|
||||||
pub struct Node {
|
|
||||||
pub plane: u32,
|
|
||||||
pub children: [i32; 2],
|
|
||||||
pub mins: [i32; 3],
|
|
||||||
pub maxs: [i32; 3],
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, BinRead)]
|
|
||||||
pub struct Leaf {
|
|
||||||
pub cluster: i32,
|
|
||||||
pub area: u32,
|
|
||||||
pub mins: [i32; 3],
|
|
||||||
pub maxs: [i32; 3],
|
|
||||||
pub leaf_face: u32,
|
|
||||||
pub num_leaf_faces: u32,
|
|
||||||
pub leaf_brush: u32,
|
|
||||||
pub num_leaf_brushes: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, BinRead)]
|
|
||||||
pub struct LeafBrush {
|
|
||||||
pub brush: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, BinRead)]
|
|
||||||
pub struct Model {
|
|
||||||
pub mins: [f32; 3],
|
|
||||||
pub maxs: [f32; 3],
|
|
||||||
pub face: u32,
|
|
||||||
pub num_faces: u32,
|
|
||||||
pub brush: u32,
|
|
||||||
pub num_brushes: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, BinRead)]
|
|
||||||
pub struct Brush {
|
|
||||||
pub brush_side: u32,
|
|
||||||
pub num_brush_sides: u32,
|
|
||||||
pub texture: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, BinRead)]
|
|
||||||
pub struct BrushSide {
|
|
||||||
pub plane: u32,
|
|
||||||
pub texture: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, BinRead)]
|
|
||||||
pub struct Vertex {
|
|
||||||
pub position: Vector,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, BinRead)]
|
|
||||||
pub struct Edge {
|
|
||||||
pub start_index: u16,
|
|
||||||
pub end_index: u16,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum EdgeDirection {
|
|
||||||
FirstToLast,
|
|
||||||
LastToFirst,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, BinRead)]
|
|
||||||
pub struct SurfaceEdge {
|
|
||||||
edge: i32,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SurfaceEdge {
|
|
||||||
pub fn edge_index(&self) -> usize {
|
|
||||||
self.edge.abs() as usize
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn direction(&self) -> EdgeDirection {
|
|
||||||
if self.edge >= 0 {
|
|
||||||
EdgeDirection::FirstToLast
|
|
||||||
} else {
|
|
||||||
EdgeDirection::LastToFirst
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, BinRead)]
|
|
||||||
pub struct MeshVert {
|
|
||||||
pub offset: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, BinRead)]
|
|
||||||
pub struct Effect {
|
|
||||||
pub name: Name,
|
|
||||||
pub brush: u32,
|
|
||||||
pub unknown: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, BinRead)]
|
|
||||||
pub struct Face {
|
|
||||||
pub plane_num: u16,
|
|
||||||
pub side: u8,
|
|
||||||
pub on_node: u8,
|
|
||||||
pub first_edge: i32,
|
|
||||||
pub num_edges: i16,
|
|
||||||
pub texture_info: i16,
|
|
||||||
pub displacement_info: i16,
|
|
||||||
pub surface_fog_volume_id: i16,
|
|
||||||
pub styles: [u8; 4],
|
|
||||||
pub light_offset: i32,
|
|
||||||
pub area: f32,
|
|
||||||
pub light_map_texture_min: [i32; 2],
|
|
||||||
pub light_map_texture_size: [i32; 2],
|
|
||||||
pub original_face: i32,
|
|
||||||
pub primitive_count: u16,
|
|
||||||
pub first_primitive_index: u16,
|
|
||||||
pub smoothing_groups: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
static_assertions::const_assert_eq!(size_of::<Face>(), 56);
|
|
||||||
|
|
||||||
const LIGHTMAP_SIZE: usize = 128;
|
|
||||||
|
|
||||||
#[derive(Default, Clone, Copy, BinRead, Debug)]
|
|
||||||
pub struct LightColor {
|
|
||||||
r: u8,
|
|
||||||
g: u8,
|
|
||||||
b: u8,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct Lightmap {
|
|
||||||
map: [[LightColor; LIGHTMAP_SIZE]; LIGHTMAP_SIZE],
|
|
||||||
}
|
|
||||||
|
|
||||||
impl BinRead for Lightmap {
|
|
||||||
type Args = <LightColor as BinRead>::Args;
|
|
||||||
|
|
||||||
fn read_options<R: binread::io::Read + binread::io::Seek>(
|
|
||||||
reader: &mut R,
|
|
||||||
options: &ReadOptions,
|
|
||||||
args: Self::Args,
|
|
||||||
) -> BinResult<Self> {
|
|
||||||
let mut map = [[LightColor::default(); LIGHTMAP_SIZE]; LIGHTMAP_SIZE];
|
|
||||||
|
|
||||||
for x in 0..LIGHTMAP_SIZE {
|
|
||||||
for y in 0..LIGHTMAP_SIZE {
|
|
||||||
map[x][y] = LightColor::read_options(reader, options, args)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(Lightmap { map })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Debug for Lightmap {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
#[derive(Debug)]
|
|
||||||
struct Lightmap {
|
|
||||||
map: Vec<Vec<LightColor>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
Lightmap {
|
|
||||||
map: self.map.iter().map(|a| a.to_vec()).collect::<Vec<_>>(),
|
|
||||||
}
|
|
||||||
.fmt(f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, BinRead)]
|
|
||||||
pub struct Lightvol {
|
|
||||||
ambient: [u8; 3],
|
|
||||||
directional: [u8; 3],
|
|
||||||
dir: [u8; 2],
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default, Debug, Clone)]
|
|
||||||
pub struct VisData {
|
|
||||||
pub n_vecs: u32,
|
|
||||||
// Number of vectors.
|
|
||||||
pub sz_vecs: u32,
|
|
||||||
// Size of each vector, in bytes.
|
|
||||||
pub vecs: BitVec<u8>, // Visibility data. One bit per cluster per vector.
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Handle<'a, T> {
|
pub struct Handle<'a, T> {
|
||||||
bsp: &'a Bsp,
|
bsp: &'a Bsp,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue