pub mod region;
use region::*;
pub const MAT_DEV_WALL: &str = "DEV/DEV_MEASUREWALL01C";
pub const MAT_DEV_FLOOR: &str = "DEV/DEV_MEASUREGENERIC01B";
pub const MAT_3D_SKY: &str = "TOOLS/TOOLSSKYBOX";
pub const LIGHTMAP_SCALE: u8 = 16;
pub const MAT_SCALE: f32 = 0.25;
pub static DEV_EAST: Texture<'static> =
Texture::new_mat(Cow::Borrowed(MAT_DEV_WALL)).east();
pub static DEV_WEST: Texture<'static> =
Texture::new_mat(Cow::Borrowed(MAT_DEV_WALL)).west();
pub static DEV_NORTH: Texture<'static> =
Texture::new_mat(Cow::Borrowed(MAT_DEV_WALL)).north();
pub static DEV_SOUTH: Texture<'static> =
Texture::new_mat(Cow::Borrowed(MAT_DEV_WALL)).south();
pub static DEV_TOP: Texture<'static> =
Texture::new_mat(Cow::Borrowed(MAT_DEV_FLOOR)).top();
pub static DEV_BOTTOM: Texture<'static> =
Texture::new_mat(Cow::Borrowed(MAT_DEV_FLOOR)).bottom();
pub static SKY_EAST: Texture<'static> =
Texture::new_mat(Cow::Borrowed(MAT_3D_SKY)).east();
pub static SKY_WEST: Texture<'static> =
Texture::new_mat(Cow::Borrowed(MAT_3D_SKY)).west();
pub static SKY_NORTH: Texture<'static> =
Texture::new_mat(Cow::Borrowed(MAT_3D_SKY)).north();
pub static SKY_SOUTH: Texture<'static> =
Texture::new_mat(Cow::Borrowed(MAT_3D_SKY)).south();
pub static SKY_TOP: Texture<'static> =
Texture::new_mat(Cow::Borrowed(MAT_3D_SKY)).top();
pub static SKY_BOTTOM: Texture<'static> =
Texture::new_mat(Cow::Borrowed(MAT_3D_SKY)).bottom();
use crate::map::{Map, Plane, Side, Solid, Texture, Vector3};
use std::borrow::Cow;
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct Bounds<T> {
pub min: Vector3<T>,
pub max: Vector3<T>,
}
impl Bounds<f32> {
pub fn new(point1: Vector3<f32>, point2: Vector3<f32>) -> Self {
Self {
min: Vector3 {
x: point1.x.min(point2.x),
y: point1.y.min(point2.y),
z: point1.z.min(point2.z),
},
max: Vector3 {
x: point1.x.max(point2.x),
y: point1.y.max(point2.y),
z: point1.z.max(point2.z),
},
}
}
pub fn collides(&self, other: &Self) -> bool {
if lines_collides(self.min.x, self.max.x, other.min.x, other.max.x) {
return true;
};
if lines_collides(self.min.y, self.max.y, other.min.y, other.max.y) {
return true;
};
if lines_collides(self.min.z, self.max.z, other.min.z, other.max.z) {
return true;
};
false
}
pub const fn verts(&self) -> [Vector3<f32>; 8] {
[
Vector3 { ..self.min }, Vector3 { y: self.max.y, ..self.min }, Vector3 { x: self.max.x, y: self.max.y, ..self.min }, Vector3 { x: self.max.x, ..self.min }, Vector3 { x: self.min.x, y: self.min.y, ..self.max }, Vector3 { x: self.min.x, ..self.max }, Vector3 { ..self.max }, Vector3 { y: self.min.y, ..self.max }, ]
}
#[rustfmt::skip]
pub const fn planes(&self) -> [Plane; 6] {
let verts = self.verts();
let top = Plane::new(verts[4].const_clone(), verts[5].const_clone(), verts[6].const_clone());
let bottom = Plane::new(verts[2].const_clone(), verts[1].const_clone(), verts[0].const_clone());
let left = Plane::new(verts[1].const_clone(), verts[5].const_clone(), verts[4].const_clone());
let right = Plane::new(verts[3].const_clone(), verts[7].const_clone(), verts[6].const_clone());
let back = Plane::new(verts[0].const_clone(), verts[4].const_clone(), verts[7].const_clone());
let front = Plane::new(verts[2].const_clone(), verts[6].const_clone(), verts[5].const_clone());
[top, bottom, left, right, back, front]
}
}
impl<'a> Map<'a> {
pub fn cube_dev(bounds: Bounds<f32>) -> Solid<'a> {
Self::cube_dev2(bounds)
}
pub fn cube_sky(bounds: Bounds<f32>) -> Solid<'a> {
let textures = [
SKY_TOP.clone(),
SKY_BOTTOM.clone(),
SKY_EAST.clone(),
SKY_WEST.clone(),
SKY_NORTH.clone(),
SKY_SOUTH.clone(),
];
Self::cube_owned(bounds, textures)
}
pub fn cube_dev1(bounds: Bounds<f32>) -> Solid<'a> {
let textures = [&DEV_TOP, &DEV_BOTTOM, &DEV_EAST, &DEV_WEST, &DEV_NORTH, &DEV_SOUTH];
Self::cube(bounds, textures)
}
pub fn cube_dev3(bounds: Bounds<f32>) -> Solid<'a> {
let textures = Texture::cube_textures([
MAT_DEV_WALL,
MAT_DEV_WALL,
MAT_DEV_WALL,
MAT_DEV_WALL,
MAT_DEV_FLOOR,
MAT_DEV_FLOOR,
]);
Self::cube_owned(bounds, textures)
}
pub fn cube_dev4(bounds: Bounds<f32>) -> Solid<'a> {
let textures = [
DEV_TOP.clone(),
DEV_BOTTOM.clone(),
DEV_EAST.clone(),
DEV_WEST.clone(),
DEV_NORTH.clone(),
DEV_SOUTH.clone(),
];
Self::cube_owned(bounds, textures)
}
pub fn cube_dev2(bounds: Bounds<f32>) -> Solid<'a> {
Self::cube_str(
bounds,
[MAT_DEV_FLOOR, MAT_DEV_FLOOR, MAT_DEV_WALL, MAT_DEV_WALL, MAT_DEV_WALL, MAT_DEV_WALL],
)
}
#[inline]
pub fn cube(bounds: Bounds<f32>, textures: [&Texture<'a>; 6]) -> Solid<'a> {
Solid::new(
bounds
.planes()
.into_iter()
.zip(textures.into_iter())
.map(|(p, t)| Side::new(p, t.clone()))
.collect(),
)
}
#[inline]
pub fn cube_owned(bounds: Bounds<f32>, textures: [Texture<'a>; 6]) -> Solid<'a> {
Solid::new(
bounds
.planes()
.into_iter()
.zip(textures.into_iter())
.map(|(p, t)| Side::new(p, t))
.collect(),
)
}
#[inline]
pub fn cube_str(bounds: Bounds<f32>, textures: [&str; 6]) -> Solid {
let [top, bottom, left, right, back, front] = textures;
let top = Texture::new_mat(Cow::Borrowed(top)).top();
let bottom = Texture::new_mat(Cow::Borrowed(bottom)).bottom();
let left = Texture::new_mat(Cow::Borrowed(left)).east();
let right = Texture::new_mat(Cow::Borrowed(right)).west();
let back = Texture::new_mat(Cow::Borrowed(back)).north();
let front = Texture::new_mat(Cow::Borrowed(front)).south();
let textures = [top, bottom, left, right, back, front];
Solid::new(
bounds
.planes()
.into_iter()
.zip(textures.into_iter())
.map(|(p, t)| Side::new(p, t))
.collect(),
)
}
pub fn cube_ref(bounds: Bounds<f32>, textures: [&Texture<'a>; 6]) -> Solid<'a> {
Solid::new(
bounds
.planes()
.into_iter()
.zip(textures.into_iter())
.map(|(p, t)| Side::new(p, t.clone()))
.collect(),
)
}
}
#[inline]
fn lines_collides(x1: f32, x2: f32, y1: f32, y2: f32) -> bool {
debug_assert!(x2 >= x1, "end must be greater or equal");
debug_assert!(y2 >= y1, "end must be greater or equal");
x2 > y1 && x1 < y2
}
#[inline]
fn lines_overlap(x1: f32, x2: f32, y1: f32, y2: f32) -> f32 {
debug_assert!(x2 >= x1, "end must be greater or equal");
debug_assert!(y2 >= y1, "end must be greater or equal");
x2.min(y2) - x1.max(y1)
}
#[cfg(test)]
mod tests {
use crate::vmf::ToLower;
use super::*;
#[test]
fn cube() {
let bounds = Bounds::new(Vector3::new(0.0, 0.0, 0.0), Vector3::new(1.0, 1.0, 1.0));
let cube = Map::cube_dev1(bounds);
let _vmf = cube.to_lower();
}
#[test]
fn collide() {
assert_1d(-1, 2, 5, 0, 1); assert_1d(0, 2, 5, 0, 2); assert_1d(1, 2, 5, 1, 3); assert_1d(2, 2, 5, 2, 4); assert_1d(3, 2, 5, 2, 5); assert_1d(2, 2, 5, 3, 5); assert_1d(1, 2, 5, 4, 6); assert_1d(0, 2, 5, 5, 7); assert_1d(-1, 2, 5, 6, 7); assert_1d(3, 2, 5, 0, 7);
assert_collide(true, 2.0, 5.0, 3.0, 3.0);
assert_length(0.0, 2.0, 5.0, 3.0, 3.0);
fn assert_1d(truth: i32, x1: i32, x2: i32, y1: i32, y2: i32) {
let truth_bool = truth > 0;
let truth = truth as f32;
let x1 = x1 as f32;
let x2 = x2 as f32;
let y1 = y1 as f32;
let y2 = y2 as f32;
eprintln!("truth: {truth: <5} input: {x1}, {x2}, {y1}, {y2}");
assert_collide(truth_bool, x1, x2, y1, y2);
assert_length(truth, x1, x2, y1, y2);
}
fn assert_collide(truth: bool, x1: f32, x2: f32, y1: f32, y2: f32) {
assert_eq!(truth, lines_collides(x1, x2, y1, y2));
assert_eq!(truth, lines_collides(y1, y2, x1, x2));
assert_eq!(truth, lines_collides(-x2, -x1, -y2, -y1));
assert_eq!(truth, lines_collides(-y2, -y1, -x2, -x1));
}
fn assert_length(truth: f32, x1: f32, x2: f32, y1: f32, y2: f32) {
assert_eq!(truth, lines_overlap(x1, x2, y1, y2));
assert_eq!(truth, lines_overlap(y1, y2, x1, x2));
assert_eq!(truth, lines_overlap(-x2, -x1, -y2, -y1));
assert_eq!(truth, lines_overlap(-y2, -y1, -x2, -x1));
}
fn eprint_return<T: std::fmt::Display>(value: T) -> T {
eprintln!("value {value}");
value
}
}
}