use crate::camera3d::Camera3D;
use crate::mesh3d::{Mesh3D, Vec3};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Billboard {
pub position: Vec3,
pub size: Vec3,
pub texture_path: Option<String>,
pub color: [u8; 4],
pub billboard_type: BillboardType,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum BillboardType {
Spherical,
Cylindrical,
Fixed,
}
impl Billboard {
pub fn new(position: Vec3, size: Vec3) -> Self {
Self {
position,
size,
texture_path: None,
color: [255, 255, 255, 255],
billboard_type: BillboardType::Spherical,
}
}
pub fn spherical(position: Vec3, width: f32, height: f32) -> Self {
Self {
position,
size: Vec3::new(width, height, 1.0),
texture_path: None,
color: [255, 255, 255, 255],
billboard_type: BillboardType::Spherical,
}
}
pub fn cylindrical(position: Vec3, width: f32, height: f32) -> Self {
Self {
position,
size: Vec3::new(width, height, 1.0),
texture_path: None,
color: [255, 255, 255, 255],
billboard_type: BillboardType::Cylindrical,
}
}
pub fn with_texture(mut self, path: String) -> Self {
self.texture_path = Some(path);
self
}
pub fn with_color(mut self, color: [u8; 4]) -> Self {
self.color = color;
self
}
pub fn to_mesh(&self, camera: &Camera3D) -> Mesh3D {
let mut mesh = Mesh3D::quad(self.size.x, self.size.y);
mesh.position = self.position;
mesh.set_color(self.color);
match self.billboard_type {
BillboardType::Spherical => {
let to_camera = (camera.position - self.position).normalize();
mesh.rotation.y = to_camera.x.atan2(to_camera.z);
mesh.rotation.x = -to_camera.y.asin();
}
BillboardType::Cylindrical => {
let to_camera = camera.position - self.position;
mesh.rotation.y = to_camera.x.atan2(to_camera.z);
}
BillboardType::Fixed => {
}
}
mesh
}
}
pub struct BillboardBatch {
pub billboards: Vec<Billboard>,
}
impl BillboardBatch {
pub fn new() -> Self {
Self {
billboards: Vec::new(),
}
}
pub fn add(&mut self, billboard: Billboard) {
self.billboards.push(billboard);
}
pub fn clear(&mut self) {
self.billboards.clear();
}
pub fn sort_by_distance(&mut self, camera_position: Vec3) {
self.billboards.sort_by(|a, b| {
let dist_a = camera_position.distance(&a.position);
let dist_b = camera_position.distance(&b.position);
dist_b.partial_cmp(&dist_a).unwrap()
});
}
pub fn to_meshes(&self, camera: &Camera3D) -> Vec<Mesh3D> {
self.billboards
.iter()
.map(|billboard| billboard.to_mesh(camera))
.collect()
}
}
impl Default for BillboardBatch {
fn default() -> Self {
Self::new()
}
}