redguard-preservation 0.6.2

A CLI tool to preserve Redguard files
Documentation
use crate::model3d::Model3DFile;

#[derive(Debug, Clone, Copy)]
pub(crate) struct GeometryConvention {
    pub(crate) x_sign: f32,
    pub(crate) y_sign: f32,
    pub(crate) z_sign: f32,
}

pub(crate) const SCENE_CONVENTION: GeometryConvention = GeometryConvention {
    x_sign: -1.0,
    y_sign: -1.0,
    z_sign: 1.0,
};

#[must_use]
pub(crate) const fn sanitize_f32(value: f32) -> f32 {
    if value.is_nan() { 0.0 } else { value }
}

#[must_use]
pub(crate) fn transform_position(
    x: f32,
    y: f32,
    z: f32,
    scale: f32,
    convention: GeometryConvention,
) -> [f32; 3] {
    [
        convention.x_sign * sanitize_f32(x) / scale,
        convention.y_sign * sanitize_f32(y) / scale,
        convention.z_sign * sanitize_f32(z) / scale,
    ]
}

#[must_use]
pub(crate) fn transform_normal(x: f32, y: f32, z: f32, convention: GeometryConvention) -> [f32; 3] {
    [
        convention.x_sign * sanitize_f32(x),
        convention.y_sign * sanitize_f32(y),
        convention.z_sign * sanitize_f32(z),
    ]
}

#[must_use]
pub(crate) fn resolve_vertex_normal(
    model: &Model3DFile,
    vertex_index: usize,
    cumulative_fv_index: usize,
    face_normal: [f32; 3],
    convention: GeometryConvention,
) -> [f32; 3] {
    let vn_index = if !model.normal_indices.is_empty() {
        model
            .normal_indices
            .get(cumulative_fv_index)
            .and_then(|&index| usize::try_from(index).ok())
    } else if !model.vertex_normals.is_empty() {
        Some(vertex_index)
    } else {
        None
    };

    if let Some(idx) = vn_index
        && let Some(vn) = model.vertex_normals.get(idx)
        && !vn.x.is_nan()
        && !vn.y.is_nan()
        && !vn.z.is_nan()
    {
        return transform_normal(vn.x, vn.y, vn.z, convention);
    }

    face_normal
}

#[must_use]
pub(crate) const fn triangle_vertex_offsets(i: usize) -> [usize; 3] {
    [0, i, i + 1]
}