/*
* Copyright (C) Oscar Shrimpton 2020
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, either version 3 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see .
*/
use super::Q3BspFile;
use crate::coords::CoordSystem;
use crate::helpers::{slice_to_i32, slice_to_u32, slice_to_vec2ui, slice_to_vec3};
use crate::traits::faces::*;
use crate::types::{ParseError, Result};
use na::Vector3;
const FACE_SIZE: usize = (4 * 8) + (4 * 2) + (4 * 2) + (4 * 3) + ((4 * 2) * 3) + (4 * 3) + (4 * 2);
pub fn from_data(
data: &[u8],
n_textures: u32,
n_effects: u32,
n_vertices: u32,
n_meshverts: u32,
n_lightmaps: u32,
) -> Result> {
if data.len() % FACE_SIZE != 0 {
return Err(ParseError::Invalid);
}
let length = data.len() / FACE_SIZE;
let mut faces = Vec::with_capacity(length);
for n in 0..length {
faces.push(face_from_slice(
&data[n * FACE_SIZE..(n + 1) * FACE_SIZE],
n_textures,
n_effects,
n_vertices,
n_meshverts,
n_lightmaps,
)?);
}
Ok(faces.into_boxed_slice())
}
fn face_from_slice(
data: &[u8],
n_textures: u32,
n_effects: u32,
n_vertices: u32,
n_meshverts: u32,
n_lightmaps: u32,
) -> Result {
if data.len() != FACE_SIZE {
panic!("tried to call face.from_slice with invalid slice size");
}
// texture
let texture_idx = slice_to_u32(&data[0..4]);
if texture_idx >= n_textures {
return Err(ParseError::Invalid);
}
// effects
let effect_idx = slice_to_u32(&data[4..8]);
let effect_idx = if effect_idx < 0xffffffff {
if effect_idx >= n_effects {
return Err(ParseError::Invalid);
}
Some(effect_idx)
} else {
None
};
// face type
// TODO
let face_type: FaceType = unsafe { ::std::mem::transmute(slice_to_u32(&data[8..12])) };
// vertices
let vertex_offset = slice_to_u32(&data[12..16]);
let vertex_n = slice_to_u32(&data[16..20]);
if (vertex_offset + vertex_n) > n_vertices {
return Err(ParseError::Invalid);
}
let vertices_idx = vertex_offset..vertex_offset + vertex_n;
// meshverts
let meshverts_offset = slice_to_u32(&data[20..24]);
let meshverts_n = slice_to_u32(&data[24..28]);
if (meshverts_offset + meshverts_n) > n_meshverts {
return Err(ParseError::Invalid);
}
let meshverts_idx = meshverts_offset..meshverts_offset + meshverts_n;
// lightmap
let lightmap_idx = slice_to_i32(&data[28..32]);
let lightmap_idx = if lightmap_idx > 0 {
if lightmap_idx as u32 >= n_lightmaps {
return Err(ParseError::Invalid);
}
Some(lightmap_idx as u32)
} else {
None
};
// map properties
let map_start = slice_to_vec2ui(&data[32..40]);
let map_size = slice_to_vec2ui(&data[40..48]);
let map_origin = slice_to_vec3(&data[48..60]);
// map_vecs
let mut map_vecs = [Vector3::new(0.0, 0.0, 0.0); 2];
for (n, map_vec) in map_vecs.iter_mut().enumerate() {
let offset = 60 + (n * 3 * 4);
*map_vec = slice_to_vec3(&data[offset..offset + 12]);
}
// normal & size
let normal = slice_to_vec3(&data[84..96]);
let size = slice_to_vec2ui(&data[96..104]);
Ok(Face {
face_type,
texture_idx,
effect_idx,
vertices_idx,
meshverts_idx,
lightmap_idx,
map_start,
map_size,
map_origin,
map_vecs,
normal,
size,
})
}
impl HasFaces for Q3BspFile {
type FacesIter<'a> = std::slice::Iter<'a, Face>;
fn faces_iter(&self) -> Self::FacesIter<'_> {
self.faces.iter()
}
fn faces_len(&self) -> u32 {
self.faces.len() as u32
}
fn get_face(&self, index: u32) -> &Face {
&self.faces[index as usize]
}
}