mirror of
https://codeberg.org/icewind/vbsp.git
synced 2026-06-03 18:54:05 +02:00
displacement triangulation
This commit is contained in:
parent
d3b614c76d
commit
a0e505d907
3 changed files with 219 additions and 42 deletions
164
src/data.rs
164
src/data.rs
|
|
@ -6,11 +6,90 @@ use binrw::{BinRead, BinResult, ReadOptions};
|
||||||
use bitflags::bitflags;
|
use bitflags::bitflags;
|
||||||
use bv::BitVec;
|
use bv::BitVec;
|
||||||
use num_enum::TryFromPrimitive;
|
use num_enum::TryFromPrimitive;
|
||||||
|
use std::cmp::Ordering;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::fmt::{Debug, Display, Formatter};
|
use std::fmt::{Debug, Display, Formatter};
|
||||||
use std::io::{Read, Seek};
|
use std::io::{Read, Seek};
|
||||||
use std::mem::{align_of, size_of};
|
use std::mem::{align_of, size_of};
|
||||||
use std::ops::{Add, Index};
|
use std::ops::Index;
|
||||||
|
use std::ops::{Add, Mul, Sub};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, BinRead)]
|
||||||
|
pub struct Vector {
|
||||||
|
pub x: f32,
|
||||||
|
pub y: f32,
|
||||||
|
pub z: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Vector {
|
||||||
|
pub fn iter(&self) -> impl Iterator<Item = f32> {
|
||||||
|
[self.x, self.y, self.z].into_iter()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn length_squared(&self) -> f32 {
|
||||||
|
self.x.powf(2.0) + self.y.powf(2.0) + self.z.powf(2.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Add<Vector> for Vector {
|
||||||
|
type Output = Vector;
|
||||||
|
|
||||||
|
fn add(self, rhs: Vector) -> Self::Output {
|
||||||
|
Vector {
|
||||||
|
x: self.x + rhs.x,
|
||||||
|
y: self.y + rhs.y,
|
||||||
|
z: self.z + rhs.z,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Sub<Vector> for Vector {
|
||||||
|
type Output = Vector;
|
||||||
|
|
||||||
|
fn sub(self, rhs: Vector) -> Self::Output {
|
||||||
|
Vector {
|
||||||
|
x: self.x - rhs.x,
|
||||||
|
y: self.y - rhs.y,
|
||||||
|
z: self.z - rhs.z,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Mul<f32> for Vector {
|
||||||
|
type Output = Vector;
|
||||||
|
|
||||||
|
fn mul(self, rhs: f32) -> Self::Output {
|
||||||
|
Vector {
|
||||||
|
x: self.x * rhs,
|
||||||
|
y: self.y * rhs,
|
||||||
|
z: self.z * rhs,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq for Vector {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
self.x == other.x && self.y == other.y && self.z == other.z
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialOrd for Vector {
|
||||||
|
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||||
|
self.length_squared().partial_cmp(&other.length_squared())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Vector> for [f32; 3] {
|
||||||
|
fn from(vector: Vector) -> Self {
|
||||||
|
[vector.x, vector.y, vector.z]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&Vector> for [f32; 3] {
|
||||||
|
fn from(vector: &Vector) -> Self {
|
||||||
|
[vector.x, vector.y, vector.z]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
fn test_read_bytes<T: BinRead>()
|
fn test_read_bytes<T: BinRead>()
|
||||||
|
|
@ -231,43 +310,6 @@ impl<const LEN: usize> BinRead for FixedString<LEN> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, BinRead)]
|
|
||||||
pub struct Vector {
|
|
||||||
pub x: f32,
|
|
||||||
pub y: f32,
|
|
||||||
pub z: f32,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Vector {
|
|
||||||
pub fn iter(&self) -> impl Iterator<Item = f32> {
|
|
||||||
[self.x, self.y, self.z].into_iter()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Add<Vector> for Vector {
|
|
||||||
type Output = Vector;
|
|
||||||
|
|
||||||
fn add(self, rhs: Vector) -> Self::Output {
|
|
||||||
Vector {
|
|
||||||
x: self.x + rhs.x,
|
|
||||||
y: self.y + rhs.y,
|
|
||||||
z: self.z + rhs.z,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Vector> for [f32; 3] {
|
|
||||||
fn from(vector: Vector) -> Self {
|
|
||||||
[vector.x, vector.y, vector.z]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<&Vector> for [f32; 3] {
|
|
||||||
fn from(vector: &Vector) -> Self {
|
|
||||||
[vector.x, vector.y, vector.z]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, BinRead)]
|
#[derive(Debug, Clone, BinRead)]
|
||||||
pub struct TextureInfo {
|
pub struct TextureInfo {
|
||||||
pub texture_scale: [f32; 4],
|
pub texture_scale: [f32; 4],
|
||||||
|
|
@ -323,7 +365,7 @@ 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(align_after = align_of::<Leaf>())]
|
#[br(align_after = align_of::< Leaf > ())]
|
||||||
pub leaf_watter_data_id: i16,
|
pub leaf_watter_data_id: i16,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -427,6 +469,7 @@ pub struct Edge {
|
||||||
pub end_index: u16,
|
pub end_index: u16,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||||
pub enum EdgeDirection {
|
pub enum EdgeDirection {
|
||||||
FirstToLast,
|
FirstToLast,
|
||||||
LastToFirst,
|
LastToFirst,
|
||||||
|
|
@ -537,6 +580,16 @@ pub struct DisplacementInfo {
|
||||||
pub allowed_vertices: [u32; 10],
|
pub allowed_vertices: [u32; 10],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl DisplacementInfo {
|
||||||
|
pub fn vertex_count(&self) -> i32 {
|
||||||
|
(2i32.pow(self.power as u32) + 1).pow(2)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn triangle_count(&self) -> i32 {
|
||||||
|
2 * 2i32.pow(self.power as u32).pow(2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_displacement_bytes() {
|
fn test_displacement_bytes() {
|
||||||
test_read_bytes::<DisplacementInfo>();
|
test_read_bytes::<DisplacementInfo>();
|
||||||
|
|
@ -572,7 +625,7 @@ struct RawDisplacementSubNeighbour {
|
||||||
neighbour_index: u16,
|
neighbour_index: u16,
|
||||||
neighbour_orientation: u8,
|
neighbour_orientation: u8,
|
||||||
span: u8,
|
span: u8,
|
||||||
#[br(align_after = align_of::<DisplacementSubNeighbour>())]
|
#[br(align_after = align_of::< DisplacementSubNeighbour > ())]
|
||||||
neighbour_span: u8,
|
neighbour_span: u8,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -632,7 +685,7 @@ pub enum NeighbourOrientation {
|
||||||
#[derive(Debug, Clone, BinRead)]
|
#[derive(Debug, Clone, BinRead)]
|
||||||
pub struct DisplacementCornerNeighbour {
|
pub struct DisplacementCornerNeighbour {
|
||||||
pub neighbours: [u16; 4],
|
pub neighbours: [u16; 4],
|
||||||
#[br(align_after = align_of::<DisplacementCornerNeighbour>())]
|
#[br(align_after = align_of::< DisplacementCornerNeighbour > ())]
|
||||||
pub neighbour_count: u8,
|
pub neighbour_count: u8,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -642,3 +695,32 @@ static_assertions::const_assert_eq!(size_of::<DisplacementCornerNeighbour>(), 10
|
||||||
fn test_corner_neighbour_bytes() {
|
fn test_corner_neighbour_bytes() {
|
||||||
test_read_bytes::<DisplacementCornerNeighbour>();
|
test_read_bytes::<DisplacementCornerNeighbour>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, BinRead)]
|
||||||
|
pub struct DisplacementVertex {
|
||||||
|
pub vector: Vector,
|
||||||
|
pub distance: f32,
|
||||||
|
pub alpha: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DisplacementVertex {
|
||||||
|
pub fn displacement(&self) -> Vector {
|
||||||
|
self.vector * self.distance
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, BinRead)]
|
||||||
|
pub struct DisplacementTriangle {
|
||||||
|
pub tags: DisplacementTriangleFlags,
|
||||||
|
}
|
||||||
|
|
||||||
|
bitflags! {
|
||||||
|
#[derive(BinRead)]
|
||||||
|
pub struct DisplacementTriangleFlags: u8 {
|
||||||
|
const SURFACE = 0x01;
|
||||||
|
const WALKABLE = 0x02;
|
||||||
|
const BULDABLE = 0x04;
|
||||||
|
const SURFACE_PROP1 = 0x08;
|
||||||
|
const SURFACE_PROP2 = 0x10;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
use crate::data::*;
|
use crate::data::*;
|
||||||
use crate::Bsp;
|
use crate::Bsp;
|
||||||
|
use arrayvec::ArrayVec;
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
|
|
||||||
/// A handle represents a data structure in the bsp file and the bsp file containing it.
|
/// A handle represents a data structure in the bsp file and the bsp file containing it.
|
||||||
|
|
@ -97,6 +98,10 @@ impl<'a> Handle<'a, Face> {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn edge_direction(&self) -> EdgeDirection {
|
||||||
|
self.bsp.surface_edges[self.first_edge as usize].direction()
|
||||||
|
}
|
||||||
|
|
||||||
/// Check if the face is flagged as visible
|
/// Check if the face is flagged as visible
|
||||||
pub fn is_visible(&self) -> bool {
|
pub fn is_visible(&self) -> bool {
|
||||||
self.texture()
|
self.texture()
|
||||||
|
|
@ -131,6 +136,10 @@ impl<'a> Handle<'a, Face> {
|
||||||
points
|
points
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn displacement(&self) -> Option<Handle<'a, DisplacementInfo>> {
|
||||||
|
self.bsp.displacement(self.displacement_info as usize)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Handle<'_, Node> {
|
impl Handle<'_, Node> {
|
||||||
|
|
@ -196,6 +205,70 @@ impl<'a> Handle<'a, DisplacementInfo> {
|
||||||
.copied()
|
.copied()
|
||||||
.filter_map(|id| self.bsp.displacement(id as usize))
|
.filter_map(|id| self.bsp.displacement(id as usize))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn displacement_vertices(&self) -> impl Iterator<Item = Handle<'a, DisplacementVertex>> {
|
||||||
|
(self.displacement_vertex_start..(self.displacement_vertex_start + self.vertex_count()))
|
||||||
|
.flat_map(|i| self.bsp.displacement_vertex(i as usize))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn face(&self) -> Option<Handle<'a, Face>> {
|
||||||
|
self.bsp.face(self.map_face as usize)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn displaced_vertices(&self) -> Option<impl Iterator<Item = Vector> + 'a> {
|
||||||
|
let face = self.face()?;
|
||||||
|
let steps = 2usize.pow(self.power as u32) + 1;
|
||||||
|
|
||||||
|
let mut corner_positions: ArrayVec<_, 4> = face.vertices().map(|v| v.position).collect();
|
||||||
|
|
||||||
|
let start_index = corner_positions
|
||||||
|
.iter()
|
||||||
|
.copied()
|
||||||
|
.map(|point| point - self.start_position)
|
||||||
|
.enumerate()
|
||||||
|
.min_by(|(_a, a_pos), (_b, b_pos)| (a_pos).partial_cmp(b_pos).unwrap())
|
||||||
|
.map(|(i, _pos)| i)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
corner_positions.rotate_left(start_index);
|
||||||
|
|
||||||
|
let start_corner = corner_positions[0];
|
||||||
|
let x_dir = corner_positions[3] - corner_positions[0];
|
||||||
|
let y_dir = corner_positions[1] - corner_positions[0];
|
||||||
|
|
||||||
|
Some(
|
||||||
|
self.displacement_vertices()
|
||||||
|
.enumerate()
|
||||||
|
.map(move |(i, displacement)| {
|
||||||
|
let x = (i % steps) as f32 / (steps - 1) as f32;
|
||||||
|
let y = (i / steps) as f32 / (steps - 1) as f32;
|
||||||
|
let base_pos = start_corner + (x_dir * x) + (y_dir * y);
|
||||||
|
base_pos + displacement.displacement()
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn triangulated_displaced_vertices(&self) -> Option<impl Iterator<Item = Vector> + 'a> {
|
||||||
|
let vertices: Vec<_> = self.displaced_vertices()?.collect();
|
||||||
|
let steps = 2usize.pow(self.power as u32);
|
||||||
|
|
||||||
|
let index = move |x: usize, y: usize| y * (steps + 1) + x;
|
||||||
|
|
||||||
|
Some(
|
||||||
|
(0..steps)
|
||||||
|
.flat_map(move |x| (0..steps).map(move |y| (x, y)))
|
||||||
|
.flat_map(move |(x, y)| {
|
||||||
|
[
|
||||||
|
vertices[index(x, y)],
|
||||||
|
vertices[index(x + 1, y)],
|
||||||
|
vertices[index(x, y + 1)],
|
||||||
|
vertices[index(x, y + 1)],
|
||||||
|
vertices[index(x + 1, y + 1)],
|
||||||
|
vertices[index(x + 1, y)],
|
||||||
|
]
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Handle<'a, DisplacementSubNeighbour> {
|
impl<'a> Handle<'a, DisplacementSubNeighbour> {
|
||||||
|
|
|
||||||
24
src/lib.rs
24
src/lib.rs
|
|
@ -8,7 +8,7 @@ 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;
|
pub use crate::handle::Handle;
|
||||||
use binrw::io::Cursor;
|
use binrw::io::Cursor;
|
||||||
use binrw::BinRead;
|
use binrw::BinRead;
|
||||||
use bspfile::BspFile;
|
use bspfile::BspFile;
|
||||||
|
|
@ -172,6 +172,8 @@ pub struct Bsp {
|
||||||
pub original_faces: Vec<Face>,
|
pub original_faces: Vec<Face>,
|
||||||
pub vis_data: VisData,
|
pub vis_data: VisData,
|
||||||
pub displacements: Vec<DisplacementInfo>,
|
pub displacements: Vec<DisplacementInfo>,
|
||||||
|
pub displacement_vertices: Vec<DisplacementVertex>,
|
||||||
|
pub displacement_triangles: Vec<DisplacementTriangle>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Bsp {
|
impl Bsp {
|
||||||
|
|
@ -229,6 +231,12 @@ impl Bsp {
|
||||||
let displacements = bsp_file
|
let displacements = bsp_file
|
||||||
.lump_reader(LumpType::DisplacementInfo)?
|
.lump_reader(LumpType::DisplacementInfo)?
|
||||||
.read_vec(|r| r.read())?;
|
.read_vec(|r| r.read())?;
|
||||||
|
let displacement_vertices = bsp_file
|
||||||
|
.lump_reader(LumpType::DisplacementVertices)?
|
||||||
|
.read_vec(|r| r.read())?;
|
||||||
|
let displacement_triangles = bsp_file
|
||||||
|
.lump_reader(LumpType::DisplacementTris)?
|
||||||
|
.read_vec(|r| r.read())?;
|
||||||
|
|
||||||
Ok({
|
Ok({
|
||||||
Bsp {
|
Bsp {
|
||||||
|
|
@ -251,6 +259,8 @@ impl Bsp {
|
||||||
original_faces,
|
original_faces,
|
||||||
vis_data,
|
vis_data,
|
||||||
displacements,
|
displacements,
|
||||||
|
displacement_vertices,
|
||||||
|
displacement_triangles,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -277,6 +287,18 @@ impl Bsp {
|
||||||
.map(|displacement| Handle::new(self, displacement))
|
.map(|displacement| Handle::new(self, displacement))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn displacement_vertex(&self, n: usize) -> Option<Handle<'_, DisplacementVertex>> {
|
||||||
|
self.displacement_vertices
|
||||||
|
.get(n)
|
||||||
|
.map(|vert| Handle::new(self, vert))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn displacement_triangle(&self, n: usize) -> Option<Handle<'_, DisplacementTriangle>> {
|
||||||
|
self.displacement_triangles
|
||||||
|
.get(n)
|
||||||
|
.map(|tri| Handle::new(self, tri))
|
||||||
|
}
|
||||||
|
|
||||||
/// Get the root node of the bsp
|
/// Get the root node of the bsp
|
||||||
pub fn root_node(&self) -> Option<Handle<'_, Node>> {
|
pub fn root_node(&self) -> Option<Handle<'_, Node>> {
|
||||||
self.node(0)
|
self.node(0)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue