mod gate_bracket_mesh;
mod gate_long_plate_mesh;
mod gate_plate_mesh;
mod gate_wire_mesh;
mod gate_wire_rectangle_mesh;
use crate::vpx::gameitem::gate::{Gate, GateType};
use crate::vpx::gameitem::primitive::VertexWrapper;
use crate::vpx::math::{Matrix3D, Vertex3D};
use crate::vpx::model::Vertex3dNoTex2;
use crate::vpx::obj::VpxFace;
use gate_bracket_mesh::{GATE_BRACKET_INDICES, GATE_BRACKET_MESH};
use gate_long_plate_mesh::{GATE_LONG_PLATE_INDICES, GATE_LONG_PLATE_MESH};
use gate_plate_mesh::{GATE_PLATE_INDICES, GATE_PLATE_MESH};
use gate_wire_mesh::{GATE_WIRE_INDICES, GATE_WIRE_MESH};
use gate_wire_rectangle_mesh::{GATE_WIRE_RECTANGLE_INDICES, GATE_WIRE_RECTANGLE_MESH};
pub struct GateMeshes {
pub bracket: Option<(Vec<VertexWrapper>, Vec<VpxFace>)>,
pub wire: (Vec<VertexWrapper>, Vec<VpxFace>),
}
fn get_mesh_for_type(gate_type: &GateType) -> (&'static [Vertex3dNoTex2], &'static [u16]) {
match gate_type {
GateType::WireW => (&GATE_WIRE_MESH, &GATE_WIRE_INDICES),
GateType::WireRectangle => (&GATE_WIRE_RECTANGLE_MESH, &GATE_WIRE_RECTANGLE_INDICES),
GateType::Plate => (&GATE_PLATE_MESH, &GATE_PLATE_INDICES),
GateType::LongPlate => (&GATE_LONG_PLATE_MESH, &GATE_LONG_PLATE_INDICES),
}
}
fn generate_bracket_mesh(gate: &Gate) -> (Vec<VertexWrapper>, Vec<VpxFace>) {
let rot_matrix = Matrix3D::rotate_z(gate.rotation.to_radians());
let vert_matrix = rot_matrix * Matrix3D::scale_uniform(gate.length);
let vertices: Vec<VertexWrapper> = GATE_BRACKET_MESH
.iter()
.map(|v| {
let pos = Vertex3D::new(v.x, v.y, v.z);
let transformed_pos = vert_matrix.transform_vertex(pos);
let normal = rot_matrix.transform_normal(v.nx, v.ny, v.nz);
let normal = normal.normalized();
VertexWrapper {
vpx_encoded_vertex: [0u8; 32],
vertex: Vertex3dNoTex2 {
x: transformed_pos.x,
y: transformed_pos.y,
z: transformed_pos.z,
nx: normal.x,
ny: normal.y,
nz: normal.z,
tu: v.tu,
tv: v.tv,
},
}
})
.collect();
let faces: Vec<VpxFace> = GATE_BRACKET_INDICES
.chunks(3)
.map(|chunk| VpxFace {
i0: chunk[0] as i64,
i1: chunk[1] as i64,
i2: chunk[2] as i64,
})
.collect();
(vertices, faces)
}
fn generate_wire_mesh(
gate: &Gate,
mesh: &[Vertex3dNoTex2],
indices: &[u16],
) -> (Vec<VertexWrapper>, Vec<VpxFace>) {
let world_matrix =
Matrix3D::rotate_z(gate.rotation.to_radians()) * Matrix3D::scale_uniform(gate.length);
let vertices: Vec<VertexWrapper> = mesh
.iter()
.map(|v| {
let pos = Vertex3D::new(v.x, v.y, v.z);
let transformed_pos = world_matrix.transform_vertex(pos);
let rot_matrix = Matrix3D::rotate_z(gate.rotation.to_radians());
let normal = rot_matrix.transform_normal(v.nx, v.ny, v.nz);
let normal = normal.normalized();
VertexWrapper {
vpx_encoded_vertex: [0u8; 32],
vertex: Vertex3dNoTex2 {
x: transformed_pos.x,
y: transformed_pos.y,
z: transformed_pos.z,
nx: normal.x,
ny: normal.y,
nz: normal.z,
tu: v.tu,
tv: v.tv,
},
}
})
.collect();
let faces: Vec<VpxFace> = indices
.chunks(3)
.map(|chunk| VpxFace {
i0: chunk[0] as i64,
i1: chunk[1] as i64,
i2: chunk[2] as i64,
})
.collect();
(vertices, faces)
}
pub fn build_gate_meshes(gate: &Gate) -> Option<GateMeshes> {
if !gate.is_visible {
return None;
}
let gate_type = gate.gate_type.as_ref().unwrap_or(&GateType::WireW);
let (mesh, indices) = get_mesh_for_type(gate_type);
Some(GateMeshes {
bracket: if gate.show_bracket {
Some(generate_bracket_mesh(gate))
} else {
None
},
wire: generate_wire_mesh(gate, mesh, indices),
})
}
#[cfg(test)]
mod tests {
use super::*;
use crate::vpx::gameitem::vertex2d::Vertex2D;
fn make_test_gate(gate_type: GateType, show_bracket: bool, is_visible: bool) -> Gate {
Gate {
center: Vertex2D { x: 100.0, y: 200.0 },
length: 50.0,
height: 30.0,
rotation: 45.0,
gate_type: Some(gate_type),
show_bracket,
is_visible,
..Default::default()
}
}
#[test]
fn test_build_gate_meshes_wire_w() {
let gate = make_test_gate(GateType::WireW, true, true);
let result = build_gate_meshes(&gate);
assert!(result.is_some());
let meshes = result.unwrap();
assert!(meshes.bracket.is_some());
let (bracket_verts, bracket_faces) = meshes.bracket.unwrap();
assert_eq!(bracket_verts.len(), 184);
assert_eq!(bracket_faces.len(), 516 / 3);
let (wire_verts, wire_faces) = meshes.wire;
assert_eq!(wire_verts.len(), 186);
assert_eq!(wire_faces.len(), 1008 / 3);
}
#[test]
fn test_build_gate_meshes_no_bracket() {
let gate = make_test_gate(GateType::WireW, false, true);
let result = build_gate_meshes(&gate);
assert!(result.is_some());
let meshes = result.unwrap();
assert!(meshes.bracket.is_none());
}
#[test]
fn test_build_gate_meshes_invisible() {
let gate = make_test_gate(GateType::WireW, true, false);
let result = build_gate_meshes(&gate);
assert!(result.is_none());
}
#[test]
fn test_build_gate_meshes_all_types() {
let types = [
GateType::WireW,
GateType::WireRectangle,
GateType::Plate,
GateType::LongPlate,
];
for gate_type in types {
let gate = make_test_gate(gate_type.clone(), true, true);
let result = build_gate_meshes(&gate);
assert!(
result.is_some(),
"Failed to generate mesh for {:?}",
gate_type
);
}
}
}