engine/gavania-core/src/game/content/lightning.rs

163 lines
4.3 KiB
Rust
Raw Normal View History

2024-08-23 11:22:09 +00:00
use std::{collections::HashMap, str::FromStr};
use cgmath::{vec2, Deg, Rad, Vector2, Vector3};
2024-08-25 07:11:52 +00:00
use entity_manager::*;
2024-08-23 11:22:09 +00:00
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()
}
}