1
0
Fork 0
mirror of https://codeberg.org/icewind/vbsp.git synced 2026-06-03 18:54:05 +02:00

some more data implemented

This commit is contained in:
Robin Appelman 2020-06-26 00:05:48 +02:00
commit 771ae67730
3 changed files with 175 additions and 175 deletions

View file

@ -18,6 +18,7 @@ thiserror = "1.0"
lzma-rs = "0.1.3" lzma-rs = "0.1.3"
binread = "1.0" binread = "1.0"
parse-display = "0.1.2" parse-display = "0.1.2"
static_assertions = "1.1.0"
[features] [features]
bench = [] bench = []

View file

@ -120,15 +120,15 @@ pub enum LumpType {
Unused1, Unused1,
Unused2, Unused2,
Unused3, Unused3,
DisplayInfo, DisplacementInfo,
OriginalFaces, OriginalFaces,
PhysDisplay, PhysDisplacement,
PhysCollide, PhysCollide,
VertNormals, VertNormals,
VertNormalIndices, VertNormalIndices,
DisplayLightMapAlphas, DisplacementLightMapAlphas,
DisplayVertices, DisplacementVertices,
DisplayLightMapSamplePositions, DisplacementLightMapSamplePositions,
GameLump, GameLump,
LeafWaterData, LeafWaterData,
Primitives, Primitives,
@ -142,7 +142,7 @@ pub enum LumpType {
Overlays, Overlays,
LeafMinimumDistanceToWater, LeafMinimumDistanceToWater,
FaceMacroTextureInfo, FaceMacroTextureInfo,
DisplayTris, DisplacementTris,
PhysicsCollideSurface, PhysicsCollideSurface,
WaterOverlays, WaterOverlays,
LeafAmbientIndexHdr, LeafAmbientIndexHdr,
@ -157,5 +157,5 @@ pub enum LumpType {
OverlayFades, OverlayFades,
OverlaySystemLevels, OverlaySystemLevels,
PhysLevel, PhysLevel,
DisplayMultiBlend, DisplacementMultiBlend,
} }

View file

@ -16,10 +16,11 @@ use parse_display::Display;
use reader::LumpReader; use reader::LumpReader;
use std::ops::Index; use std::ops::Index;
use std::{ use std::{
borrow::Cow,
convert::{TryFrom, TryInto}, convert::{TryFrom, TryInto},
fmt, fmt,
io::{self, Error, ErrorKind, Read}, io::{self, Error, ErrorKind, Read},
iter::once,
mem::size_of,
ops::Deref, ops::Deref,
}; };
use thiserror::Error; use thiserror::Error;
@ -240,74 +241,24 @@ impl<'a> Entity<'a> {
} }
bitflags! { bitflags! {
pub struct SurfaceFlags: u32 { #[derive(BinRead)]
const NODAMAGE = 0b0000_0000_0000_0000_0001; // Never give falling damage pub struct TextureFlags: u32 {
const SLICK = 0b0000_0000_0000_0000_0010; // Affects game physics const LIGHT = 0b0000_0000_0000_0000_0001; // value will hold the light strength
const SKY = 0b0000_0000_0000_0000_0100; // Lighting from environment map 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 LADDER = 0b0000_0000_0000_0000_1000; // Climbable ladder const SKY = 0b0000_0000_0000_0000_0100; // don't draw, but add the skybox
const NOIMPACT = 0b0000_0000_0000_0001_0000; // Don't make missile explosions const WARP = 0b0000_0000_0000_0000_1000; // turbulent water warp
const NOMARKS = 0b0000_0000_0000_0010_0000; // Don't leave missile marks const TRANS = 0b0000_0000_0000_0001_0000; // texture is translucent
const FLESH = 0b0000_0000_0000_0100_0000; // Make flesh sounds and effects const NOPORTAL = 0b0000_0000_0000_0010_0000; // the surface can't have a portal placed on it
const NODRAW = 0b0000_0000_0000_1000_0000; // Don't generate a drawsurface at all const TRIGGER = 0b0000_0000_0000_0100_0000; // xbox hack to work around elimination of trigger surfaces
const HINT = 0b0000_0000_0001_0000_0000; // Make a primary bsp splitter const NODRAW = 0b0000_0000_0000_1000_0000; // don't bother referencing the texture
const SKIP = 0b0000_0000_0010_0000_0000; // Completely ignore, allowing non-closed brushes const HINT = 0b0000_0000_0001_0000_0000; // make a primary bsp splitter
const NOLIGHTMAP = 0b0000_0000_0100_0000_0000; // Surface doesn't need a lightmap const SKIP = 0b0000_0000_0010_0000_0000; // completely ignore, allowing non-closed brushes
const POINTLIGHT = 0b0000_0000_1000_0000_0000; // Generate lighting info at vertices const NOLIGHT = 0b0000_0000_0100_0000_0000; // dont calculate light
const METALSTEPS = 0b0000_0001_0000_0000_0000; // Clanking footsteps const BUMPLIGHT = 0b0000_0000_1000_0000_0000; // calculate thee light maps for the surface for bump mapping
const NOSTEPS = 0b0000_0010_0000_0000_0000; // No footstep sounds const NOSHADOWS = 0b0000_0001_0000_0000_0000; // don't receive shadows
const NONSOLID = 0b0000_0100_0000_0000_0000; // Don't collide against curves with this set const NODECALS = 0b0000_0010_0000_0000_0000; // don't receive decals
const LIGHTFILTER = 0b0000_1000_0000_0000_0000; // Act as a light filter during q3map -light const NOCHOP = 0b0000_0100_0000_0000_0000; // don't subdivide patches on this surface
const ALPHASHADOW = 0b0001_0000_0000_0000_0000; // Do per-pixel light shadow casting in q3map const HITBOX = 0b0000_1000_0000_0000_0000; // surface is part of a hitbox
const NODLIGHT = 0b0010_0000_0000_0000_0000; // Never add dynamic lights
}
}
impl SurfaceFlags {
pub fn should_draw(&self) -> bool {
!self.intersects(Self::HINT | Self::SKIP | Self::NODRAW | Self::LIGHTFILTER)
}
}
bitflags! {
pub struct ContentFlags: u32 {
// An eye is never valid in a solid
const SOLID = 0b0000_0000_0000_0000_0000_0000_0000_0001;
const LAVA = 0b0000_0000_0000_0000_0000_0000_0000_1000;
const SLIME = 0b0000_0000_0000_0000_0000_0000_0001_0000;
const WATER = 0b0000_0000_0000_0000_0000_0000_0010_0000;
const FOG = 0b0000_0000_0000_0000_0000_0000_0100_0000;
const NOTTEAM1 = 0b0000_0000_0000_0000_0000_0000_1000_0000;
const NOTTEAM2 = 0b0000_0000_0000_0000_0000_0001_0000_0000;
const NOBOTCLIP = 0b0000_0000_0000_0000_0000_0010_0000_0000;
const AREAPORTAL = 0b0000_0000_0000_0000_1000_0000_0000_0000;
const PLAYERCLIP = 0b0000_0000_0000_0001_0000_0000_0000_0000;
const MONSTERCLIP = 0b0000_0000_0000_0010_0000_0000_0000_0000;
// Bot-specific contents types
const TELEPORTER = 0b0000_0000_0000_0100_0000_0000_0000_0000;
const JUMPPAD = 0b0000_0000_0000_1000_0000_0000_0000_0000;
const CLUSTERPORTAL = 0b0000_0000_0001_0000_0000_0000_0000_0000;
const DONOTENTER = 0b0000_0000_0010_0000_0000_0000_0000_0000;
const BOTCLIP = 0b0000_0000_0100_0000_0000_0000_0000_0000;
const MOVER = 0b0000_0000_1000_0000_0000_0000_0000_0000;
// Removed before bsping an entity
const ORIGIN = 0b0000_0001_0000_0000_0000_0000_0000_0000;
// Should never be on a brush, only in game
const BODY = 0b0000_0010_0000_0000_0000_0000_0000_0000;
const CORPSE = 0b0000_0100_0000_0000_0000_0000_0000_0000;
// Brushes not used for the bsp
const DETAIL = 0b0000_1000_0000_0000_0000_0000_0000_0000;
// Brushes used for the bsp
const STRUCTURAL = 0b0001_0000_0000_0000_0000_0000_0000_0000;
// Don't consume surface fragments inside
const TRANSLUCENT = 0b0010_0000_0000_0000_0000_0000_0000_0000;
const TRIGGER = 0b0100_0000_0000_0000_0000_0000_0000_0000;
// Don't leave bodies or items (death fog, lava)
const NODROP = 0b1000_0000_0000_0000_0000_0000_0000_0000;
} }
} }
@ -350,44 +301,40 @@ impl BinRead for Name {
} }
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone, BinRead)]
pub struct Texture { pub struct Vector {
pub name: Name, x: f32,
pub flags: SurfaceFlags, y: f32,
pub contents: ContentFlags, z: f32,
} }
impl BinRead for Texture { #[derive(Debug, Clone, BinRead)]
type Args = (); 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,
}
fn read_options<R: binread::io::Read + binread::io::Seek>( static_assertions::const_assert_eq!(size_of::<TextureInfo>(), 72);
reader: &mut R,
options: &ReadOptions,
args: Self::Args,
) -> BinResult<Self> {
let name = Name::read_options(reader, options, args)?;
let flags = SurfaceFlags::from_bits(u32::read_options(reader, options, args)?).ok_or_else(
|| binread::Error::NoVariantMatch {
pos: reader.seek(SeekFrom::Current(0)).unwrap() as usize,
},
)?;
let contents = ContentFlags::from_bits(u32::read_options(reader, options, args)?)
.ok_or_else(|| binread::Error::NoVariantMatch {
pos: reader.seek(SeekFrom::Current(0)).unwrap() as usize,
})?;
Ok(Texture { #[derive(Debug, Clone, BinRead)]
name, pub struct TextureData {
flags, pub reflectivity: Vector,
contents, pub name_string_table_id: i32,
}) pub width: i32,
} pub height: i32,
pub view_width: i32,
pub view_height: i32,
} }
#[derive(Debug, Clone, BinRead)] #[derive(Debug, Clone, BinRead)]
pub struct Plane { pub struct Plane {
pub normal: [f32; 3], pub normal: Vector,
pub dist: f32, pub dist: f32,
pub ty: i32,
} }
#[derive(Debug, Clone, BinRead)] #[derive(Debug, Clone, BinRead)]
@ -440,11 +387,37 @@ pub struct BrushSide {
#[derive(Debug, Clone, BinRead)] #[derive(Debug, Clone, BinRead)]
pub struct Vertex { pub struct Vertex {
pub position: [f32; 3], pub position: Vector,
pub surface_texcoord: [f32; 2], }
pub lightmap_texcoord: [f32; 2],
pub normal: [f32; 3], #[derive(Debug, Clone, BinRead)]
pub color: [u8; 4], 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)] #[derive(Debug, Clone, BinRead)]
@ -461,22 +434,27 @@ pub struct Effect {
#[derive(Debug, Clone, BinRead)] #[derive(Debug, Clone, BinRead)]
pub struct Face { pub struct Face {
pub texture: u32, pub plane_num: u16,
pub effect: u32, pub side: u8,
pub face_type: FaceType, pub on_node: u8,
pub vertex: u32, pub first_edge: i32,
pub num_vertices: u32, pub num_edges: i16,
pub mesh_vert: u32, pub texture_info: i16,
pub num_mesh_verts: u32, pub displacement_info: i16,
pub lm_index: u32, pub surface_fog_volume_id: i16,
pub lm_start: [u32; 2], pub styles: [u8; 4],
pub lm_size: [u32; 2], pub light_offset: i32,
pub lm_origin: [f32; 3], pub area: f32,
pub lm_vecs: [[f32; 3]; 2], pub light_map_texture_min: [i32; 2],
pub normal: [f32; 3], pub light_map_texture_size: [i32; 2],
pub size: [u32; 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; const LIGHTMAP_SIZE: usize = 128;
#[derive(Default, Clone, Copy, BinRead, Debug)] #[derive(Default, Clone, Copy, BinRead, Debug)]
@ -647,7 +625,8 @@ impl<'a> IntoIterator for &'a mut Leaves {
pub struct Bsp { pub struct Bsp {
pub header: Header, pub header: Header,
pub entities: Entities, pub entities: Entities,
pub textures: Vec<Texture>, pub textures_data: Vec<TextureData>,
pub textures_info: Vec<TextureInfo>,
pub planes: Vec<Plane>, pub planes: Vec<Plane>,
pub nodes: Vec<Node>, pub nodes: Vec<Node>,
pub leaves: Leaves, pub leaves: Leaves,
@ -657,6 +636,8 @@ pub struct Bsp {
pub brushes: Vec<Brush>, pub brushes: Vec<Brush>,
pub brush_sides: Vec<BrushSide>, pub brush_sides: Vec<BrushSide>,
pub vertices: Vec<Vertex>, pub vertices: Vec<Vertex>,
pub edges: Vec<Edge>,
pub surface_edges: Vec<SurfaceEdge>,
pub faces: Vec<Face>, pub faces: Vec<Face>,
pub vis_data: VisData, pub vis_data: VisData,
} }
@ -666,9 +647,12 @@ impl Bsp {
let bsp_file = BspFile::new(data)?; let bsp_file = BspFile::new(data)?;
let entities = bsp_file.lump_reader(LumpType::Entities)?.read_entities()?; let entities = bsp_file.lump_reader(LumpType::Entities)?.read_entities()?;
let textures = bsp_file let textures_data = bsp_file
.lump_reader(LumpType::TextureData)? .lump_reader(LumpType::TextureData)?
.read_vec(|r| r.read())?; .read_vec(|r| r.read())?;
let textures_info = bsp_file
.lump_reader(LumpType::TextureInfo)?
.read_vec(|r| r.read())?;
let planes = bsp_file let planes = bsp_file
.lump_reader(LumpType::Planes)? .lump_reader(LumpType::Planes)?
.read_vec(|r| r.read())?; .read_vec(|r| r.read())?;
@ -697,6 +681,12 @@ impl Bsp {
let vertices = bsp_file let vertices = bsp_file
.lump_reader(LumpType::Vertices)? .lump_reader(LumpType::Vertices)?
.read_vec(|r| r.read())?; .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 let faces = bsp_file
.lump_reader(LumpType::Faces)? .lump_reader(LumpType::Faces)?
.read_vec(|r| r.read())?; .read_vec(|r| r.read())?;
@ -706,7 +696,8 @@ impl Bsp {
Bsp { Bsp {
header: bsp_file.header().clone(), header: bsp_file.header().clone(),
entities, entities,
textures, textures_data,
textures_info,
planes, planes,
nodes, nodes,
leaves, leaves,
@ -716,6 +707,8 @@ impl Bsp {
brushes, brushes,
brush_sides, brush_sides,
vertices, vertices,
edges,
surface_edges,
faces, faces,
vis_data, vis_data,
} }
@ -743,10 +736,6 @@ impl Bsp {
}) })
} }
pub fn texture(&self, n: usize) -> Option<&Texture> {
self.textures.get(n)
}
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 {
bsp: self, bsp: self,
@ -764,25 +753,26 @@ impl Bsp {
pub fn leaf_at(&self, point: [f32; 3]) -> Option<Handle<'_, Leaf>> { pub fn leaf_at(&self, point: [f32; 3]) -> Option<Handle<'_, Leaf>> {
let mut current = self.root_node()?; let mut current = self.root_node()?;
None
loop { // loop {
let plane = current.plane()?; // let plane = current.plane()?;
let dot: f32 = point // let dot: f32 = point
.iter() // .iter()
.zip(plane.normal.iter()) // .zip(plane.normal.iter())
.map(|(a, b)| a * b) // .map(|(a, b)| a * b)
.sum(); // .sum();
//
let [front, back] = current.children; // let [front, back] = current.children;
//
let next = if dot < plane.dist { back } else { front }; // let next = if dot < plane.dist { back } else { front };
//
if next < 0 { // if next < 0 {
return self.leaf((!next) as usize); // return self.leaf((!next) as usize);
} else { // } else {
current = self.node(next as usize)?; // current = self.node(next as usize)?;
} // }
} // }
} }
} }
@ -804,31 +794,39 @@ impl<'a> Handle<'a, Model> {
} }
} }
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> { impl<'a> Handle<'a, Face> {
pub fn texture(&self) -> Option<&Texture> { pub fn texture(&self) -> Option<Handle<TextureInfo>> {
self.bsp.texture(self.texture as _) 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 = Cow<'a, Vertex>> { pub fn vertices(&'a self) -> impl Iterator<Item = &'a Vertex> + 'a {
todo!(); (self.data.first_edge..(self.data.first_edge + self.data.num_edges as i32))
vec![].into_iter() .flat_map(move |surface_edge| self.bsp.surface_edges.get(surface_edge as usize))
.flat_map(move |surface_edge| {
// match self.face_type { self.bsp
// FaceType::Polygon | FaceType::Mesh => { .edges
// let start = self.mesh_vert as usize; .get(surface_edge.edge_index())
// let end = start + self.num_mesh_verts as usize; .map(|edge| (edge, surface_edge.direction()))
// let bsp = self.bsp; })
// let vertex = self.vertex; .flat_map(|(edge, direction)| match direction {
// EdgeDirection::FirstToLast => once(edge.start_index).chain(once(edge.end_index)),
// Either::Left( EdgeDirection::LastToFirst => once(edge.end_index).chain(once(edge.start_index)),
// bsp.mesh_verts[start..end] })
// .iter() .flat_map(move |vert_index| self.bsp.vertices.get(vert_index as usize))
// .map(move |mv| Cow::Borrowed(&bsp.vertices[(mv.offset + vertex) as usize])),
// )
// }
// // TODO
// _ => Either::Right(std::iter::empty()),
// }
} }
} }
@ -879,6 +877,7 @@ impl<'a> Handle<'a, Leaf> {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::Bsp; use super::Bsp;
#[test] #[test]
fn tf2_file() { fn tf2_file() {
use std::fs::read; use std::fs::read;