use std::cell::RefCell;
use std::sync::LazyLock;
use cgmath::{One, Quaternion, Vector3, Zero};
use wgpu::BufferUsages;
use wgpu::util::BufferInitDescriptor;
use crate::asset::AssetManagerRc;
use crate::output::OutputDeviceRc;
use crate::render::model::{Color, InstObstacleBuf, InstShaderImplType, InstShaderType, Mesh, Model, ModelFactory, ModelHandle, Submesh, VertexPosNormal, VertexShaderType, get_default_primitive_state};
use crate::ui::UIManagerRc;
const POS: f32 = 0.5;
static VERTEXES: LazyLock<Box<[VertexPosNormal]>> = LazyLock::new(|| {
let side_datas = [
(-POS, POS),
(-POS, -POS),
(POS, -POS),
(POS, POS),
];
let tr_x_neg = |(pos_x, pos_y): (f32, f32)| VertexPosNormal { pos: [-POS, -pos_x, pos_y], normal: [-1.0, 0.0, 0.0] };
let tr_x_pos = |(pos_x, pos_y): (f32, f32)| VertexPosNormal { pos: [POS, pos_x, pos_y], normal: [1.0, 0.0, 0.0] };
let tr_y_neg = |(pos_x, pos_y): (f32, f32)| VertexPosNormal { pos: [pos_x, -POS, pos_y], normal: [0.0, -1.0, 0.0] };
let tr_y_pos = |(pos_x, pos_y): (f32, f32)| VertexPosNormal { pos: [-pos_x, POS, pos_y], normal: [0.0, 1.0, 0.0] };
let tr_z_neg = |(pos_x, pos_y): (f32, f32)| VertexPosNormal { pos: [pos_x, -pos_y, -POS], normal: [0.0, 0.0, -1.0] };
let tr_z_pos = |(pos_x, pos_y): (f32, f32)| VertexPosNormal { pos: [pos_x, pos_y, POS], normal: [0.0, 0.0, 1.0] };
let mut vertexes = Vec::new();
for tr in [tr_x_neg, tr_x_pos, tr_y_neg, tr_y_pos, tr_z_neg, tr_z_pos] {
for side_data in side_datas {
let vertex = tr(side_data);
vertexes.push(vertex);
}
}
vertexes.into_boxed_slice()
});
pub struct ObstacleParam {
color: Color,
outline_width: f32,
}
impl ObstacleParam {
pub fn new(color: &Color, outline_width: f32) -> Self {
Self {
color: *color,
outline_width,
}
}
}
impl ModelFactory for ObstacleParam {
type Model = Obstacle;
fn get_mesh(_asset_mgr: AssetManagerRc, output_device: OutputDeviceRc) -> Mesh {
let mut indexes: Vec<u16> = Vec::new();
let mut vertex_index = 0;
for _i in 0..6 {
indexes.push(vertex_index);
indexes.push(vertex_index + 1);
indexes.push(vertex_index + 2);
indexes.push(vertex_index);
indexes.push(vertex_index + 2);
indexes.push(vertex_index + 3);
vertex_index += 4;
}
let mut primitive_state = get_default_primitive_state();
primitive_state.cull_mode = None;
let submesh = Submesh::new(0, indexes.len() as u32, 0, primitive_state, InstShaderType::Obstacle);
let vertex_buf = output_device.create_buffer_init(&BufferInitDescriptor {
label: None,
contents: bytemuck::cast_slice(&VERTEXES),
usage: BufferUsages::VERTEX,
});
let index_buf = output_device.create_buffer_init(&BufferInitDescriptor {
label: None,
contents: bytemuck::cast_slice(&indexes),
usage: BufferUsages::INDEX,
});
let submeshes = Box::from([submesh]);
Mesh::new(vertex_buf, index_buf, VertexShaderType::PosNormal, submeshes)
}
fn create(self, handle: ModelHandle, _output_device: OutputDeviceRc, _inst_sh_impls: &mut [InstShaderImplType], _ui_manager: UIManagerRc) -> Self::Model {
Obstacle::new(self, handle)
}
}
pub struct Obstacle {
param: ObstacleParam,
handle: ModelHandle,
inner: RefCell<Inner>,
}
struct Inner {
scale: (f32, f32, f32),
pos: Vector3<f32>,
rot: Quaternion<f32>,
}
impl Obstacle {
fn new(param: ObstacleParam, handle: ModelHandle) -> Self {
Self {
param,
handle,
inner: RefCell::new(Inner {
scale: (1.0, 1.0, 1.0),
pos: Vector3::zero(),
rot: Quaternion::one(),
}),
}
}
pub fn set_visible(&self, visible: bool) {
self.handle.set_visible(0, visible);
}
pub fn set_scale(&self, scale_x: f32, scale_y: f32, scale_z: f32) {
self.inner.borrow_mut().scale = (scale_x, scale_y, scale_z);
}
pub fn set_pos(&self, pos: &Vector3<f32>) {
self.inner.borrow_mut().pos = *pos;
}
pub fn set_rot(&self, rot: &Quaternion<f32>) {
self.inner.borrow_mut().rot = *rot;
}
pub fn calc_box(&self) -> (Vector3<f32>, Vector3<f32>) {
let inner = self.inner.borrow();
let (min_x, min_y, min_z, max_x, max_y, max_z) = VERTEXES.iter().fold(
(f32::INFINITY, f32::INFINITY, f32::INFINITY, f32::NEG_INFINITY, f32::NEG_INFINITY, f32::NEG_INFINITY),
|(min_x, min_y, min_z, max_x, max_y, max_z), vertex| {
let v = inner.pos + inner.rot * Vector3::new(vertex.pos[0] * inner.scale.0, vertex.pos[1] * inner.scale.1, vertex.pos[2] * inner.scale.2);
(min_x.min(v.x), min_y.min(v.y), min_z.min(v.z), max_x.max(v.x), max_y.max(v.y), max_z.max(v.z))
}
);
(Vector3::new(min_x, min_y, min_z), Vector3::new(max_x, max_y, max_z))
}
}
impl Model for Obstacle {
fn get_pos(&self, inst_index: u16) -> Vector3<f32> {
assert!(inst_index == 0);
let inner = self.inner.borrow();
inner.pos }
fn fill_obstacle(&self, inst_index: u16) -> InstObstacleBuf {
assert!(inst_index == 0);
let inner = self.inner.borrow();
InstObstacleBuf::fill(&self.param.color, self.param.outline_width, &Vector3::new(inner.scale.0, inner.scale.1, inner.scale.2), &inner.rot, &inner.pos)
}
}