shambler 0.2.0

A rusty, hulking, lighting-tossing geometry processor.
Documentation
use crate::{
    texture::{TextureId, TextureSizes},
    Plane3d, Vector2, Vector3,
};
use rayon::iter::{IntoParallelRefIterator, ParallelIterator};
use shalrath::repr::{TextureOffset, TexturePlane};
use std::collections::BTreeMap;
use usage::Usage;

use super::{FaceId, FacePlanes, FaceVertices};

pub enum FaceUvsTag {}

pub type FaceUvs = Usage<FaceUvsTag, BTreeMap<FaceId, Vec<Vector2>>>;

pub fn new(
    faces: &Vec<FaceId>,
    textures: &BTreeMap<TextureId, String>,
    face_textures: &BTreeMap<FaceId, TextureId>,
    face_vertices: &FaceVertices,
    face_planes: &FacePlanes,
    face_texture_offsets: &BTreeMap<FaceId, TextureOffset>,
    face_texture_rotations: &BTreeMap<FaceId, f32>,
    face_texture_scales: &BTreeMap<FaceId, Vector2>,
    texture_sizes: &TextureSizes,
) -> FaceUvs {
    faces
        .par_iter()
        .map(|face_id| {
            let face_texture = &face_textures[face_id];
            let texture_size = texture_sizes.get(face_texture).copied().unwrap_or_else(|| {
                println!(
                    "Warning: Texture {} not found, generating UV with default size of 256x256",
                    &textures[face_texture],
                );
                (256, 256)
            });
            let face_vertices = &face_vertices[face_id];
            let face_plane = face_planes[face_id];
            let face_texture_offset = face_texture_offsets[face_id];
            let face_texture_rotation = face_texture_rotations[face_id];
            let face_texture_scale = face_texture_scales[face_id];

            (
                *face_id,
                face_vertices
                    .par_iter()
                    .map(|vertex| {
                        vertex_uv(
                            *vertex,
                            face_plane,
                            face_texture_offset,
                            face_texture_rotation,
                            face_texture_scale,
                            nalgebra::vector![texture_size.0 as f32, texture_size.1 as f32],
                        )
                    })
                    .collect(),
            )
        })
        .collect()
}

pub fn vertex_uv(
    vertex: Vector3,
    plane: Plane3d,
    texture_offset: TextureOffset,
    texture_rotation: f32,
    texture_scale: Vector2,
    texture_size: Vector2,
) -> Vector2 {
    match texture_offset {
        TextureOffset::Standard { u, v } => standard_uv(
            vertex,
            plane,
            u,
            v,
            texture_rotation,
            texture_scale,
            texture_size,
        ),
        TextureOffset::Valve { u, v } => valve_uv(vertex, u, v, texture_scale, texture_size),
    }
}

pub fn standard_uv(
    vertex: Vector3,
    brush_plane: Plane3d,
    u_offset: f32,
    v_offset: f32,
    texture_rotation: f32,
    texture_scale: Vector2,
    texture_size: Vector2,
) -> Vector2 {
    let up_vector = Vector3::z_axis();
    let right_vector = Vector3::y_axis();
    let forward_vector = Vector3::x_axis();

    let du = brush_plane.normal().dot(&up_vector).abs();
    let dr = brush_plane.normal().dot(&right_vector).abs();
    let df = brush_plane.normal().dot(&forward_vector).abs();

    let (x, y);
    if du >= dr && du >= df {
        x = vertex.x;
        y = -vertex.y;
    } else if dr >= du && dr >= df {
        x = vertex.x;
        y = -vertex.z;
    } else if df >= du && df >= dr {
        x = vertex.y;
        y = -vertex.z;
    } else {
        panic!("Zero-length normal");
    }

    let rot = nalgebra::Rotation2::new(texture_rotation.to_radians());

    let mut uv = rot * nalgebra::vector![x, y];
    uv.x /= texture_size.x;
    uv.y /= texture_size.y;
    uv.x /= texture_scale.x;
    uv.y /= texture_scale.y;

    let uv = uv + nalgebra::vector![u_offset / texture_size.x, v_offset / texture_size.y];

    uv
}

pub fn valve_uv(
    vertex: Vector3,
    u_plane: TexturePlane,
    v_plane: TexturePlane,
    texture_scale: Vector2,
    texture_size: Vector2,
) -> Vector2 {
    let un = nalgebra::vector![u_plane.x, u_plane.y, u_plane.z];
    let vn = nalgebra::vector![v_plane.x, v_plane.y, v_plane.z];

    let mut uv = nalgebra::vector![un.dot(&vertex), vn.dot(&vertex)];
    uv.x /= texture_size.x;
    uv.y /= texture_size.y;
    uv.x /= texture_scale.x;
    uv.y /= texture_scale.y;

    let uv = uv + nalgebra::vector![u_plane.d / texture_size.x, v_plane.d / texture_size.y];

    uv
}