mod bumper_base_mesh;
mod bumper_cap_mesh;
mod bumper_ring_mesh;
mod bumper_socket_mesh;
use crate::vpx::gameitem::bumper::Bumper;
use crate::vpx::gameitem::primitive::VertexWrapper;
use crate::vpx::model::Vertex3dNoTex2;
use crate::vpx::obj::VpxFace;
use crate::vpx::math::{Mat3, Vec3};
pub use bumper_base_mesh::*;
pub use bumper_cap_mesh::*;
pub use bumper_ring_mesh::*;
pub use bumper_socket_mesh::*;
pub struct BumperMeshes {
pub base: Option<(Vec<VertexWrapper>, Vec<VpxFace>)>,
pub socket: Option<(Vec<VertexWrapper>, Vec<VpxFace>)>,
pub ring: Option<(Vec<VertexWrapper>, Vec<VpxFace>)>,
pub cap: Option<(Vec<VertexWrapper>, Vec<VpxFace>)>,
}
pub fn build_bumper_meshes(bumper: &Bumper) -> BumperMeshes {
let full_matrix = Mat3::rotate_z(bumper.orientation.to_radians());
BumperMeshes {
base: if bumper.is_base_visible {
Some(generate_base_mesh(bumper, &full_matrix))
} else {
None
},
socket: if bumper.is_socket_visible.unwrap_or(true) {
Some(generate_socket_mesh(bumper, &full_matrix))
} else {
None
},
ring: if bumper.is_ring_visible.unwrap_or(true) {
Some(generate_ring_mesh(bumper, &full_matrix))
} else {
None
},
cap: if bumper.is_cap_visible {
Some(generate_cap_mesh(bumper, &full_matrix))
} else {
None
},
}
}
fn generate_base_mesh(bumper: &Bumper, full_matrix: &Mat3) -> (Vec<VertexWrapper>, Vec<VpxFace>) {
let scale_xy = bumper.radius;
let vertices: Vec<VertexWrapper> = BUMPER_BASE_MESH
.iter()
.map(|v| {
let vert = full_matrix.multiply_vector(Vec3 {
x: v.x,
y: v.y,
z: v.z,
});
let norm = full_matrix.multiply_vector_no_translate(Vec3 {
x: v.nx,
y: v.ny,
z: v.nz,
});
VertexWrapper::new(
[0u8; 32],
Vertex3dNoTex2 {
x: vert.x * scale_xy,
y: vert.y * scale_xy,
z: vert.z * bumper.height_scale,
nx: norm.x,
ny: norm.y,
nz: norm.z,
tu: v.tu,
tv: v.tv,
},
)
})
.collect();
let indices: Vec<VpxFace> = BUMPER_BASE_INDICES
.chunks(3)
.map(|chunk| VpxFace {
i0: chunk[0] as i64,
i1: chunk[1] as i64,
i2: chunk[2] as i64,
})
.collect();
(vertices, indices)
}
fn generate_socket_mesh(bumper: &Bumper, full_matrix: &Mat3) -> (Vec<VertexWrapper>, Vec<VpxFace>) {
let scale_xy = bumper.radius;
let vertices: Vec<VertexWrapper> = BUMPER_SOCKET_MESH
.iter()
.map(|v| {
let vert = full_matrix.multiply_vector(Vec3 {
x: v.x,
y: v.y,
z: v.z,
});
let norm = full_matrix.multiply_vector_no_translate(Vec3 {
x: v.nx,
y: v.ny,
z: v.nz,
});
VertexWrapper::new(
[0u8; 32],
Vertex3dNoTex2 {
x: vert.x * scale_xy,
y: vert.y * scale_xy,
z: vert.z * bumper.height_scale + 5.0,
nx: norm.x,
ny: norm.y,
nz: norm.z,
tu: v.tu,
tv: v.tv,
},
)
})
.collect();
let indices: Vec<VpxFace> = BUMPER_SOCKET_INDICES
.chunks(3)
.map(|chunk| VpxFace {
i0: chunk[0] as i64,
i1: chunk[1] as i64,
i2: chunk[2] as i64,
})
.collect();
(vertices, indices)
}
fn generate_ring_mesh(bumper: &Bumper, full_matrix: &Mat3) -> (Vec<VertexWrapper>, Vec<VpxFace>) {
let scale_xy = bumper.radius;
let vertices: Vec<VertexWrapper> = BUMPER_RING_MESH
.iter()
.map(|v| {
let vert = full_matrix.multiply_vector(Vec3 {
x: v.x,
y: v.y,
z: v.z,
});
let norm = full_matrix.multiply_vector_no_translate(Vec3 {
x: v.nx,
y: v.ny,
z: v.nz,
});
VertexWrapper::new(
[0u8; 32],
Vertex3dNoTex2 {
x: vert.x * scale_xy,
y: vert.y * scale_xy,
z: vert.z * bumper.height_scale,
nx: norm.x,
ny: norm.y,
nz: norm.z,
tu: v.tu,
tv: v.tv,
},
)
})
.collect();
let indices: Vec<VpxFace> = BUMPER_RING_INDICES
.chunks(3)
.map(|chunk| VpxFace {
i0: chunk[0] as i64,
i1: chunk[1] as i64,
i2: chunk[2] as i64,
})
.collect();
(vertices, indices)
}
fn generate_cap_mesh(bumper: &Bumper, full_matrix: &Mat3) -> (Vec<VertexWrapper>, Vec<VpxFace>) {
let scale_xy = bumper.radius * 2.0;
let vertices: Vec<VertexWrapper> = BUMPER_CAP_MESH
.iter()
.map(|v| {
let vert = full_matrix.multiply_vector(Vec3 {
x: v.x,
y: v.y,
z: v.z,
});
let norm = full_matrix.multiply_vector_no_translate(Vec3 {
x: v.nx,
y: v.ny,
z: v.nz,
});
VertexWrapper::new(
[0u8; 32],
Vertex3dNoTex2 {
x: vert.x * scale_xy,
y: vert.y * scale_xy,
z: vert.z * bumper.height_scale + bumper.height_scale,
nx: norm.x,
ny: norm.y,
nz: norm.z,
tu: v.tu,
tv: v.tv,
},
)
})
.collect();
let indices: Vec<VpxFace> = BUMPER_CAP_INDICES
.chunks(3)
.map(|chunk| VpxFace {
i0: chunk[0] as i64,
i1: chunk[1] as i64,
i2: chunk[2] as i64,
})
.collect();
(vertices, indices)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::vpx::gameitem::vertex2d::Vertex2D;
#[test]
fn test_build_bumper_meshes() {
let bumper = Bumper {
center: Vertex2D::new(500.0, 500.0),
radius: 45.0,
height_scale: 90.0,
orientation: 0.0,
is_base_visible: true,
is_cap_visible: true,
is_ring_visible: Some(true),
is_socket_visible: Some(true),
..Default::default()
};
let meshes = build_bumper_meshes(&bumper);
assert!(meshes.base.is_some());
let (base_verts, base_indices) = meshes.base.unwrap();
assert_eq!(base_verts.len(), BUMPER_BASE_NUM_VERTICES);
assert_eq!(base_indices.len(), BUMPER_BASE_NUM_INDICES / 3);
assert!(meshes.socket.is_some());
let (socket_verts, socket_indices) = meshes.socket.unwrap();
assert_eq!(socket_verts.len(), BUMPER_SOCKET_NUM_VERTICES);
assert_eq!(socket_indices.len(), BUMPER_SOCKET_NUM_INDICES / 3);
assert!(meshes.ring.is_some());
let (ring_verts, ring_indices) = meshes.ring.unwrap();
assert_eq!(ring_verts.len(), BUMPER_RING_NUM_VERTICES);
assert_eq!(ring_indices.len(), BUMPER_RING_NUM_INDICES / 3);
assert!(meshes.cap.is_some());
let (cap_verts, cap_indices) = meshes.cap.unwrap();
assert_eq!(cap_verts.len(), BUMPER_CAP_NUM_VERTICES);
assert_eq!(cap_indices.len(), BUMPER_CAP_NUM_INDICES / 3);
}
#[test]
fn test_bumper_visibility_flags() {
let bumper = Bumper {
center: Vertex2D::new(500.0, 500.0),
radius: 45.0,
height_scale: 90.0,
orientation: 0.0,
is_base_visible: false,
is_cap_visible: false,
is_ring_visible: Some(false),
is_socket_visible: Some(false),
..Default::default()
};
let meshes = build_bumper_meshes(&bumper);
assert!(meshes.base.is_none());
assert!(meshes.socket.is_none());
assert!(meshes.ring.is_none());
assert!(meshes.cap.is_none());
}
}