162 lines
4.3 KiB
Rust
162 lines
4.3 KiB
Rust
use std::{collections::HashMap, str::FromStr};
|
|
|
|
use cgmath::{vec2, Deg, Rad, Vector2, Vector3};
|
|
use entity_manager::*;
|
|
|
|
use crate::*;
|
|
|
|
pub struct LightningMarker;
|
|
|
|
impl EntityComponent for LightningMarker {
|
|
fn name(&self) -> &str {
|
|
Self::debug_name()
|
|
}
|
|
}
|
|
|
|
impl ComponentDebug for LightningMarker {
|
|
fn debug_name() -> &'static str {
|
|
"LightningMarker"
|
|
}
|
|
}
|
|
|
|
pub struct Lightning {
|
|
pub thickness: f32,
|
|
pub lines: Vec<Vector2<f32>>,
|
|
}
|
|
|
|
impl Lightning {
|
|
const WIDTH: f32 = 0.25;
|
|
const LENGTH: f32 = 1.0;
|
|
const DIRECTION: Vector2<f32> = Vector2::new(1.0, 0.0);
|
|
|
|
pub fn new(thickness: f32) -> Self {
|
|
Self {
|
|
thickness,
|
|
lines: Self::create_lines(),
|
|
}
|
|
}
|
|
|
|
pub fn create_vertices(&self) -> Vec<Vector2<f32>> {
|
|
self.lines
|
|
.iter()
|
|
.skip(1)
|
|
.enumerate()
|
|
.map(|(index, p)| {
|
|
let prev_p = self.lines[index];
|
|
|
|
let tangent = prev_p - p;
|
|
let normal = vec2(-tangent.y, tangent.x).normalize();
|
|
|
|
let p1 = prev_p - normal * 0.5 * self.thickness;
|
|
let p2 = p - normal * 0.5 * self.thickness;
|
|
let p3 = p + normal * 0.5 * self.thickness;
|
|
let p4 = prev_p + normal * 0.5 * self.thickness;
|
|
|
|
vec![p1, p2, p3, p3, p4, p1]
|
|
})
|
|
.flatten()
|
|
.collect()
|
|
}
|
|
|
|
pub fn create_entity(
|
|
&self,
|
|
game_handle: &GameHandle,
|
|
scene: &Scene,
|
|
position: Vector3<f32>,
|
|
target: Vector3<f32>,
|
|
) -> Result<EntityObject> {
|
|
let game = game_handle.upgrade();
|
|
let mut entity = game.engine().assets().empty_entity();
|
|
|
|
#[cfg(debug_assertions)]
|
|
{
|
|
entity.debug_name = Some("Lightning".to_string());
|
|
}
|
|
|
|
let tangent = (target - position).truncate();
|
|
let scale = tangent.magnitude();
|
|
let angle = tangent.angle(Self::DIRECTION);
|
|
let angle_offset: Rad<f32> = Deg(90.0).into();
|
|
let height = position.z;
|
|
|
|
let mut mesh = AssetMesh::new(&scene.device(), scene.render_type())?;
|
|
mesh.set_name("Lightning");
|
|
|
|
let data: Vec<PositionOnly> = self
|
|
.create_vertices()
|
|
.into_iter()
|
|
.rev()
|
|
.map(|p| PositionOnly::new(p.extend(0.0)))
|
|
.collect();
|
|
|
|
let vertex_buffer = Buffer::builder()
|
|
.set_memory_usage(MemoryUsage::CpuOnly)
|
|
.set_usage(VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT)
|
|
.set_data(&data)
|
|
.build(scene.device().clone())?;
|
|
|
|
mesh.add_primitive(
|
|
vertex_buffer,
|
|
None,
|
|
None,
|
|
PrimitiveMaterial {
|
|
color: Color::from_str("#2660ff")?.into(),
|
|
|
|
..Default::default()
|
|
},
|
|
true,
|
|
)?;
|
|
|
|
entity.insert_component(Draw::new(vec![mesh]));
|
|
|
|
let audio = Audio::new(
|
|
&game.engine().context(),
|
|
[(
|
|
SOUND_CREATE_KEY.to_string(),
|
|
game_handle.build_data_path("sounds/shot2.ogg"),
|
|
)]
|
|
.into_iter()
|
|
.collect::<HashMap<_, _>>(),
|
|
)?;
|
|
|
|
entity.insert_component(audio);
|
|
|
|
let location = Location::new_and_setup(&mut entity)?;
|
|
location.set_position(position);
|
|
location.set_scale_uniform(scale);
|
|
location.set_rotation(angle - angle_offset);
|
|
|
|
entity.insert_component(LightningMarker);
|
|
entity.insert_component(BoundingBox {
|
|
min: [0.0, -0.25, 0.0],
|
|
max: [1.0, 0.25, 1.0],
|
|
});
|
|
|
|
println!(
|
|
"Lightning created with components: {:#?}",
|
|
entity.component_names()
|
|
);
|
|
|
|
Ok(entity)
|
|
}
|
|
|
|
fn create_lines() -> Vec<Vector2<f32>> {
|
|
let mut lines = Vec::new();
|
|
|
|
for _ in 0..50 {
|
|
lines.push(vec2(Random::range_f32(0.0, Self::LENGTH), 0.0));
|
|
}
|
|
|
|
lines.sort_by(|lhs, rhs| lhs.x.partial_cmp(&rhs.x).unwrap());
|
|
|
|
[vec2(0.0, 0.0)]
|
|
.into_iter()
|
|
.chain(
|
|
lines
|
|
.into_iter()
|
|
.map(|p| vec2(p.x, Random::range_f32(-Self::WIDTH, Self::WIDTH))),
|
|
)
|
|
.chain([vec2(1.0, 0.0)].into_iter())
|
|
.collect()
|
|
}
|
|
}
|