use crate::generation::{LIGHTMAP_SCALE, MAT_SCALE};
use crate::prelude::*;
use std::ops::{Deref, DerefMut};
use std::{borrow::Cow, fmt::Display};
pub const NO_DRAW: Material<'static> = Material::new(Cow::Borrowed("tools/toolsnodraw"));
#[repr(transparent)]
#[derive(Clone, Debug, PartialEq)]
pub struct Material<'a>(pub Texture<'a>);
impl<'a> Material<'a> {
pub const fn new(mat: StrType<'a>) -> Self {
Texture::new_mat(mat).into_material()
}
pub const fn into_texture(self) -> Texture<'a> {
unsafe { std::mem::transmute(self) }
}
#[allow(clippy::missing_const_for_fn)] pub fn mat(mut self, material: StrType<'a>) -> Self {
self.0.material = material;
self
}
pub const fn light_scale(mut self, scale: u8) -> Self {
debug_assert!(scale != 0);
self.0.light_scale = scale;
self
}
pub const fn uv(mut self, uaxis: UVAxis<f32>, vaxis: UVAxis<f32>) -> Self {
self.0.uaxis = uaxis;
self.0.vaxis = vaxis;
self
}
}
impl<'a> Default for Material<'a> {
fn default() -> Self {
Self::DEV_PERSON
}
}
impl<'a> AsRef<Texture<'a>> for Material<'a> {
fn as_ref(&self) -> &Texture<'a> {
&self.0
}
}
impl<'a> AsMut<Texture<'a>> for Material<'a> {
fn as_mut(&mut self) -> &mut Texture<'a> {
&mut self.0
}
}
impl<'a> From<Texture<'a>> for Material<'a> {
fn from(value: Texture<'a>) -> Self {
Self(value)
}
}
impl<'a> Material<'a> {
pub const DEV_FLOOR: Material<'static> = Material::DEV_GRAY;
pub const DEV_WALL: Material<'static> = Material::DEV_PERSON;
pub const DEV_128: Material<'static> = Material::DEV_WALL_ORANGE;
pub const DEV_ORANGE: Material<'static> =
Material::new(Cow::Borrowed("dev/dev_measuregeneric01"));
pub const DEV_GRAY: Material<'static> =
Material::new(Cow::Borrowed("dev/dev_measuregeneric01b"));
pub const DEV_WALL_ORANGE: Material<'static> =
Material::new(Cow::Borrowed("dev/dev_measurewall01a"));
pub const DEV_PERSON: Material<'static> =
Material::new(Cow::Borrowed("dev/dev_measurewall01c"));
pub const DEV_WALL_GRAY: Material<'static> =
Material::new(Cow::Borrowed("dev/dev_measurewall01d"));
pub const DEV_64: Material<'static> = Material::new(Cow::Borrowed("dev/dev_measurecrate01"));
pub const DEV_32: Material<'static> = Material::new(Cow::Borrowed("dev/dev_measurecrate02"));
pub const REFLECT_10: Material<'static> = Material::new(Cow::Borrowed("dev/reflectivity_10"));
pub const REFLECT_20: Material<'static> = Material::new(Cow::Borrowed("dev/reflectivity_20"));
pub const REFLECT_30: Material<'static> = Material::new(Cow::Borrowed("dev/reflectivity_30"));
pub const REFLECT_40: Material<'static> = Material::new(Cow::Borrowed("dev/reflectivity_40"));
pub const REFLECT_50: Material<'static> = Material::new(Cow::Borrowed("dev/reflectivity_50"));
pub const REFLECT_60: Material<'static> = Material::new(Cow::Borrowed("dev/reflectivity_60"));
pub const REFLECT_70: Material<'static> = Material::new(Cow::Borrowed("dev/reflectivity_70"));
pub const REFLECT_80: Material<'static> = Material::new(Cow::Borrowed("dev/reflectivity_80"));
pub const REFLECT_90: Material<'static> = Material::new(Cow::Borrowed("dev/reflectivity_90"));
pub const AREAPORTAL: Material<'static> = Material::new(Cow::Borrowed("tools/toolsareaportal"));
pub const BLOCKBULLETS: Material<'static> =
Material::new(Cow::Borrowed("tools/toolsblockbullets"));
pub const BLOCKLIGHT: Material<'static> = Material::new(Cow::Borrowed("tools/toolsblocklight"));
pub const BLOCK_LOS: Material<'static> = Material::new(Cow::Borrowed("tools/toolsblock_los"));
pub const CLIP: Material<'static> = Material::new(Cow::Borrowed("tools/toolsclip"));
pub const DOTTED: Material<'static> = Material::new(Cow::Borrowed("tools/toolsdotted"));
pub const HINT: Material<'static> = Material::new(Cow::Borrowed("tools/toolshint"));
pub const INVISIBLELADDER: Material<'static> =
Material::new(Cow::Borrowed("tools/toolsinvisibleladder"));
pub const INVISIBLE: Material<'static> = Material::new(Cow::Borrowed("tools/toolsinvisible"));
pub const NODRAW: Material<'static> = Material::new(Cow::Borrowed("tools/toolsnodraw"));
pub const NPCCLIP: Material<'static> = Material::new(Cow::Borrowed("tools/toolsnpcclip"));
pub const OCCLUDER: Material<'static> = Material::new(Cow::Borrowed("tools/toolsoccluder"));
pub const PLAYERCLIP: Material<'static> = Material::new(Cow::Borrowed("tools/toolsplayerclip"));
pub const SKIP: Material<'static> = Material::new(Cow::Borrowed("tools/toolsskip"));
pub const SKYBOX: Material<'static> = Material::new(Cow::Borrowed("tools/toolsskybox"));
pub const SKYBOX2D: Material<'static> = Material::new(Cow::Borrowed("tools/toolsskybox2d"));
pub const TRIGGER: Material<'static> = Material::new(Cow::Borrowed("tools/toolstrigger"));
}
#[derive(Clone, Debug, PartialEq)]
pub struct Texture<'a> {
pub material: StrType<'a>,
pub uaxis: UVAxis<f32>,
pub vaxis: UVAxis<f32>,
pub light_scale: u8,
}
impl<'a> Texture<'a> {
pub const fn new(
material: StrType<'a>, uaxis: UVAxis<f32>, vaxis: UVAxis<f32>, light_scale: u8,
) -> Self {
Self { material, uaxis, vaxis, light_scale }
}
pub const fn new_mat(mat: StrType<'a>) -> Self {
Texture {
material: mat,
uaxis: UVAxis::default_top().0,
vaxis: UVAxis::default_top().1,
light_scale: LIGHTMAP_SCALE,
}
}
pub const fn into_material(self) -> Material<'a> {
Material(self)
}
pub const fn cube_textures(mats: [&str; 6]) -> [Texture; 6] {
let [east, west, north, south, top, bottom] = mats;
let east = Texture::new_mat(Cow::Borrowed(east)).east();
let west = Texture::new_mat(Cow::Borrowed(west)).west();
let north = Texture::new_mat(Cow::Borrowed(north)).north();
let south = Texture::new_mat(Cow::Borrowed(south)).south();
let top = Texture::new_mat(Cow::Borrowed(top)).top();
let bottom = Texture::new_mat(Cow::Borrowed(bottom)).bottom();
[east, west, north, south, top, bottom]
}
pub const fn east(mut self) -> Self {
self.uaxis = UVAxis::default_east().0;
self.vaxis = UVAxis::default_east().1;
self
}
pub const fn west(self) -> Self {
self.east()
}
pub const fn north(mut self) -> Self {
self.uaxis = UVAxis::default_north().0;
self.vaxis = UVAxis::default_north().1;
self
}
pub const fn south(self) -> Self {
self.north()
}
pub const fn top(mut self) -> Self {
self.uaxis = UVAxis::default_top().0;
self.vaxis = UVAxis::default_top().1;
self
}
pub const fn bottom(self) -> Self {
self.top()
}
}
impl<'a> Default for Texture<'a> {
fn default() -> Self {
Material::DEV_PERSON.into()
}
}
impl<'a> AsRef<Material<'a>> for Texture<'a> {
fn as_ref(&self) -> &Material<'a> {
unsafe { std::mem::transmute(self) }
}
}
impl<'a> AsMut<Material<'a>> for Texture<'a> {
fn as_mut(&mut self) -> &mut Material<'a> {
unsafe { std::mem::transmute(self) }
}
}
impl<'a> Deref for Texture<'a> {
type Target = Material<'a>;
fn deref(&self) -> &Self::Target {
self.as_ref()
}
}
impl<'a> DerefMut for Texture<'a> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.as_mut()
}
}
impl<'a> From<Material<'a>> for Texture<'a> {
fn from(value: Material<'a>) -> Self {
value.0
}
}
#[derive(Clone, Default, Debug, PartialEq)]
pub struct UVAxis<T> {
pub x: T,
pub y: T,
pub z: T,
pub trans: T,
pub scale: T,
}
impl<T> UVAxis<T> {
pub const fn new(x: T, y: T, z: T, trans: T, scale: T) -> Self {
Self { x, y, z, trans, scale }
}
}
impl UVAxis<f32> {
pub const fn default_east() -> (Self, Self) {
(
Self { x: 0.0, y: 1.0, z: 0.0, trans: 0.0, scale: 0.25 },
Self { x: 0.0, y: 0.0, z: -1.0, trans: 0.0, scale: 0.25 },
)
}
pub const fn default_west() -> (Self, Self) {
Self::default_east()
}
pub const fn default_north() -> (Self, Self) {
(
Self { x: 1.0, y: 0.0, z: 0.0, trans: 0.0, scale: 0.25 },
Self { x: 0.0, y: 0.0, z: -1.0, trans: 0.0, scale: 0.25 },
)
}
pub const fn default_south() -> (Self, Self) {
Self::default_north()
}
pub const fn default_top() -> (Self, Self) {
(
Self { x: 1.0, y: 0.0, z: 0.0, trans: 0.0, scale: 0.25 },
Self { x: 0.0, y: -1.0, z: 0.0, trans: 0.0, scale: 0.25 },
)
}
pub const fn default_bottom() -> (Self, Self) {
Self::default_top()
}
pub fn from_norm_align_world(normal: &Vector3<f32>) -> (Self, Self) {
let normal_abs = normal.clone().abs();
let axis = normal_abs.greatest_axis();
match axis {
Axis3::X => Self::default_east(),
Axis3::Y => Self::default_north(),
Axis3::Z => Self::default_top(),
}
}
pub fn from_norm(normal: &Vector3<f32>) -> (Self, Self) {
if normal.is_axis_aligned() {
return Self::from_norm_align_world(normal);
}
let mut flip_z = Vector3::new(normal.x, normal.y, -normal.z);
if normal.z.abs() < 1.0 {
flip_z.z = (flip_z.z + 16.0).copysign(flip_z.z); }
let mut uaxis = normal.cross(&flip_z).normalize();
let mut vaxis = -normal.cross(&uaxis).normalize();
if normal.z.is_sign_negative() {
uaxis.x = -uaxis.x;
uaxis.y = -uaxis.y;
uaxis.z = -uaxis.z;
vaxis.x = -vaxis.x;
vaxis.y = -vaxis.y;
vaxis.z = -vaxis.z;
}
let uaxis = UVAxis::new(uaxis.x, uaxis.y, uaxis.z, 0.0, MAT_SCALE);
let vaxis = UVAxis::new(vaxis.x, vaxis.y, vaxis.z, 0.0, MAT_SCALE);
(uaxis, vaxis)
}
}
impl<T: Display> Display for UVAxis<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "[{} {} {} {}] {}", self.x, self.y, self.z, self.trans, self.scale)
}
}