mod kicker_cup_mesh;
mod kicker_gottlieb_mesh;
mod kicker_hole_mesh;
mod kicker_plate_mesh;
mod kicker_simple_hole_mesh;
mod kicker_t1_mesh;
mod kicker_williams_mesh;
use crate::vpx::gameitem::kicker::{Kicker, KickerType};
use crate::vpx::gameitem::primitive::VertexWrapper;
use crate::vpx::math::Matrix3D;
use crate::vpx::model::Vertex3dNoTex2;
use crate::vpx::obj::VpxFace;
pub use kicker_cup_mesh::*;
pub use kicker_gottlieb_mesh::*;
pub use kicker_hole_mesh::*;
pub use kicker_plate_mesh::*;
pub use kicker_simple_hole_mesh::*;
pub use kicker_t1_mesh::*;
pub use kicker_williams_mesh::*;
pub struct KickerMeshes {
pub plate: Option<(Vec<VertexWrapper>, Vec<VpxFace>)>,
pub kicker: Option<(Vec<VertexWrapper>, Vec<VpxFace>)>,
}
pub fn build_kicker_meshes(kicker: &Kicker) -> KickerMeshes {
if matches!(kicker.kicker_type, KickerType::Invisible) {
return KickerMeshes {
plate: None,
kicker: None,
};
}
KickerMeshes {
plate: Some(generate_plate_mesh(kicker)),
kicker: Some(generate_kicker_mesh(kicker)),
}
}
fn generate_plate_mesh(kicker: &Kicker) -> (Vec<VertexWrapper>, Vec<VpxFace>) {
let rad = match kicker.kicker_type {
KickerType::Williams | KickerType::Gottlieb => kicker.radius * 0.88,
KickerType::Cup2 => kicker.radius * 0.87,
KickerType::Cup => kicker.radius, _ => kicker.radius * 0.82, };
let num_vertices = KICKER_PLATE_NUM_VERTICES;
let num_indices = KICKER_PLATE_NUM_INDICES;
let mut vertices = Vec::with_capacity(num_vertices);
for src in &KICKER_PLATE_VERTICES {
let x = src.x * rad;
let y = src.y * rad;
let z = src.z * rad;
vertices.push(VertexWrapper::new(
[0u8; 32],
Vertex3dNoTex2 {
x,
y,
z,
nx: src.nx,
ny: src.ny,
nz: src.nz,
tu: 0.0, tv: 0.0,
},
));
}
let mut faces = Vec::with_capacity(num_indices / 3);
for i in (0..num_indices).step_by(3) {
faces.push(VpxFace {
i0: KICKER_PLATE_INDICES[i] as i64,
i1: KICKER_PLATE_INDICES[i + 1] as i64,
i2: KICKER_PLATE_INDICES[i + 2] as i64,
});
}
(vertices, faces)
}
fn generate_kicker_mesh(kicker: &Kicker) -> (Vec<VertexWrapper>, Vec<VpxFace>) {
let (mesh_vertices, mesh_indices, z_offset, z_rot) = match kicker.kicker_type {
KickerType::Cup => (
&KICKER_CUP_VERTICES[..],
&KICKER_CUP_INDICES[..],
-0.18_f32,
kicker.orientation,
),
KickerType::Williams => (
&KICKER_WILLIAMS_VERTICES[..],
&KICKER_WILLIAMS_INDICES[..],
0.0_f32,
kicker.orientation + 90.0,
),
KickerType::Gottlieb => (
&KICKER_GOTTLIEB_VERTICES[..],
&KICKER_GOTTLIEB_INDICES[..],
0.0_f32,
kicker.orientation,
),
KickerType::Cup2 => (
&KICKER_T1_VERTICES[..],
&KICKER_T1_INDICES[..],
0.0_f32,
kicker.orientation,
),
KickerType::Hole => (
&KICKER_HOLE_VERTICES[..],
&KICKER_HOLE_INDICES[..],
0.0_f32,
0.0_f32, ),
KickerType::HoleSimple | KickerType::Invisible => (
&KICKER_SIMPLE_HOLE_VERTICES[..],
&KICKER_SIMPLE_HOLE_INDICES[..],
0.0_f32,
0.0_f32, ),
};
let num_vertices = mesh_vertices.len();
let num_indices = mesh_indices.len();
let full_matrix = Matrix3D::rotate_z(z_rot.to_radians());
let mut vertices = Vec::with_capacity(num_vertices);
for src in mesh_vertices {
let vert = full_matrix.transform_vertex(crate::vpx::math::Vertex3D::new(
src.x,
src.y,
src.z + z_offset,
));
let x = vert.x * kicker.radius;
let y = vert.y * kicker.radius;
let z = vert.z * kicker.radius;
let normal = full_matrix.transform_normal(src.nx, src.ny, src.nz);
vertices.push(VertexWrapper::new(
[0u8; 32],
Vertex3dNoTex2 {
x,
y,
z,
nx: normal.x,
ny: normal.y,
nz: normal.z,
tu: src.tu,
tv: src.tv,
},
));
}
let mut faces = Vec::with_capacity(num_indices / 3);
for i in (0..num_indices).step_by(3) {
faces.push(VpxFace {
i0: mesh_indices[i] as i64,
i1: mesh_indices[i + 1] as i64,
i2: mesh_indices[i + 2] as i64,
});
}
(vertices, faces)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::vpx::gameitem::vertex2d::Vertex2D;
fn create_test_kicker(kicker_type: KickerType) -> Kicker {
Kicker {
center: Vertex2D { x: 100.0, y: 200.0 },
radius: 25.0,
orientation: 0.0,
kicker_type,
name: "TestKicker".to_string(),
..Default::default()
}
}
#[test]
fn test_invisible_kicker_has_no_mesh() {
let kicker = create_test_kicker(KickerType::Invisible);
let meshes = build_kicker_meshes(&kicker);
assert!(meshes.plate.is_none());
assert!(meshes.kicker.is_none());
}
#[test]
fn test_cup_kicker_has_meshes() {
let kicker = create_test_kicker(KickerType::Cup);
let meshes = build_kicker_meshes(&kicker);
assert!(meshes.plate.is_some());
assert!(meshes.kicker.is_some());
let (plate_verts, plate_faces) = meshes.plate.unwrap();
assert_eq!(plate_verts.len(), KICKER_PLATE_NUM_VERTICES);
assert_eq!(plate_faces.len(), KICKER_PLATE_NUM_INDICES / 3);
let (kicker_verts, kicker_faces) = meshes.kicker.unwrap();
assert_eq!(kicker_verts.len(), KICKER_CUP_NUM_VERTICES);
assert_eq!(kicker_faces.len(), KICKER_CUP_NUM_INDICES / 3);
}
#[test]
fn test_williams_kicker_rotation() {
let kicker = create_test_kicker(KickerType::Williams);
let meshes = build_kicker_meshes(&kicker);
assert!(meshes.kicker.is_some());
let (kicker_verts, _) = meshes.kicker.unwrap();
assert_eq!(kicker_verts.len(), KICKER_WILLIAMS_NUM_VERTICES);
}
#[test]
fn test_all_kicker_types_generate_meshes() {
for kicker_type in [
KickerType::Cup,
KickerType::Cup2,
KickerType::Hole,
KickerType::HoleSimple,
KickerType::Williams,
KickerType::Gottlieb,
] {
let kicker = create_test_kicker(kicker_type.clone());
let meshes = build_kicker_meshes(&kicker);
assert!(
meshes.plate.is_some(),
"Plate should exist for {:?}",
kicker_type
);
assert!(
meshes.kicker.is_some(),
"Kicker should exist for {:?}",
kicker_type
);
}
}
}