use std::f32::consts::PI;
use rand_core::{RngCore, SeedableRng};
use rand_pcg::Lcg64Xsh32;
use vek::{Aabr, Clamp, Vec2};
use wgpu::RenderPass;
use crate::TwRenderPass;
#[derive(Debug, Copy, Clone, Default)]
pub struct Clock {
start: f64,
end: f64,
frac: f32,
f64_tick: f64,
}
impl Clock {
pub fn new(tick: i32) -> Self {
Self {
start: tick as f64 - 1.,
end: tick as f64,
frac: 1.,
f64_tick: tick as f64,
}
}
pub fn need_next_tick(&self) -> bool {
self.current_tick() > self.end
}
pub fn next_tick(&mut self, tick: i32) -> bool {
self.start = self.end;
self.end = tick as f64;
self.update(self.current_tick())
}
pub fn update(&mut self, f_tick: f64) -> bool {
let range = self.end - self.start;
let offset_tick = f_tick - self.start;
self.frac = (offset_tick / range) as f32;
self.f64_tick = f_tick;
f_tick > self.end
}
pub fn current_tick(&self) -> f64 {
self.f64_tick
}
pub fn tick_rate(&self) -> f64 {
50.
}
pub fn current_time(&self) -> f64 {
self.f64_tick / self.tick_rate()
}
pub fn frac(&self) -> f32 {
self.frac
}
pub fn start_tick(&self) -> i32 {
self.start as i32
}
pub fn end_tick(&self) -> i32 {
self.end as i32
}
}
pub struct Rng {
rng: Lcg64Xsh32,
}
impl Rng {
pub fn new(seed: u32) -> Self {
let mut seed_bytes = [0; 16];
seed_bytes[0..4].copy_from_slice(&seed.to_le_bytes());
Self {
rng: Lcg64Xsh32::from_seed(seed_bytes),
}
}
pub fn u32(&mut self) -> u32 {
self.rng.next_u32()
}
pub fn f32(&mut self) -> f32 {
self.u32() as f32 / u32::MAX as f32
}
pub fn f32_max(&mut self, max: f32) -> f32 {
self.f32() * max
}
pub fn f32_range(&mut self, min: f32, max: f32) -> f32 {
let delta = max - min;
min + delta * self.f32()
}
pub fn angle(&mut self) -> f32 {
self.f32_max(2. * PI)
}
pub fn direction(&mut self) -> Vec2<f32> {
let angle = self.angle();
Vec2::new(angle.cos(), angle.sin())
}
pub fn chance(&mut self, odds: f32) -> bool {
self.f32() < odds
}
}
#[derive(Debug, Copy, Clone)]
pub struct Line {
pub position: u32,
pub length: u32,
}
impl Line {
pub fn intersect(self, other: Line) -> Option<Self> {
let (lower, upper) = if self.position < other.position {
(self, other)
} else {
(other, self)
};
if upper.position >= lower.position + lower.length {
None
} else {
Some(Line {
position: upper.position,
length: upper
.length
.min(lower.position + lower.length - upper.position),
})
}
}
}
#[derive(Debug, Copy, Clone)]
pub struct ScissorRect {
pub x: Line,
pub y: Line,
}
impl ScissorRect {
pub fn viewport(size: Vec2<u32>) -> Self {
Self {
x: Line {
position: 0,
length: size.x,
},
y: Line {
position: 0,
length: size.y,
},
}
}
pub fn from_corners(top_left: Vec2<u32>, bottom_right: Vec2<u32>) -> Option<Self> {
if top_left.x == bottom_right.x || top_left.y == bottom_right.y {
None
} else {
Some(Self {
x: Line {
position: top_left.x,
length: bottom_right.x - top_left.x + 1,
},
y: Line {
position: top_left.y,
length: bottom_right.y - top_left.y + 1,
},
})
}
}
pub fn intersect(&self, other: &Self) -> Option<Self> {
Some(Self {
x: self.x.intersect(other.x)?,
y: self.y.intersect(other.y)?,
})
}
pub fn apply(&self, render_pass: &mut RenderPass) {
render_pass.set_scissor_rect(
self.x.position,
self.y.position,
self.x.length,
self.y.length,
);
}
}
#[derive(Debug, Copy, Clone)]
pub struct Clip {
pub top_left: Vec2<f32>,
pub bottom_right: Vec2<f32>,
}
impl Clip {
pub fn project(&self, render_pass: &TwRenderPass, parallax: Vec2<f32>) -> Option<ScissorRect> {
let mut tile_counts = render_pass.camera.base_dimensions;
if parallax != Vec2::zero() {
tile_counts *= render_pass.camera.zoom;
}
let render_size = render_pass.size.az::<f32>();
let camera_position = render_pass.camera.position * parallax;
let camera_top_left = camera_position - tile_counts / 2.;
let top_left: Vec2<f32> = (self.top_left - camera_top_left) / tile_counts * render_size; let clip_top_left = top_left
.clamped(Vec2::zero(), render_size - 1.) .az::<u32>();
let bottom_right = (self.bottom_right - camera_top_left) / tile_counts * render_size;
let clip_bottom_right = bottom_right
.clamped(Vec2::zero(), render_size - 1.)
.az::<u32>();
ScissorRect::from_corners(clip_top_left, clip_bottom_right)
}
pub fn offset(mut self, offset: Vec2<f32>) -> Self {
self.top_left += offset;
self.bottom_right += offset;
self
}
}
#[derive(Debug, Copy, Clone)]
pub enum Corner {
TopLeft,
TopRight,
BottomLeft,
BottomRight,
}
pub const QUAD: [Corner; 4] = [
Corner::TopLeft,
Corner::TopRight,
Corner::BottomLeft,
Corner::BottomRight,
];
impl Corner {
#[rustfmt::skip]
pub const SPLIT_QUAD: [Self; 6] = [
Corner::TopLeft, Corner::TopRight, Corner::BottomLeft,
Corner::TopRight, Corner::BottomRight, Corner::BottomLeft,
];
pub const fn is_top(self) -> bool {
match self {
Corner::TopLeft | Corner::TopRight => true,
Corner::BottomLeft | Corner::BottomRight => false,
}
}
pub const fn is_bottom(self) -> bool {
!self.is_top()
}
pub const fn is_left(self) -> bool {
match self {
Corner::TopLeft | Corner::BottomLeft => true,
Corner::TopRight | Corner::BottomRight => false,
}
}
pub const fn is_right(self) -> bool {
!self.is_left()
}
#[rustfmt::skip]
pub const fn viewport(self) -> Vec2<f32> {
match self {
Corner::TopLeft => Vec2::new(-1., 1.),
Corner::TopRight => Vec2::new( 1., 1.),
Corner::BottomLeft => Vec2::new(-1., -1.),
Corner::BottomRight => Vec2::new( 1., -1.),
}
}
#[rustfmt::skip]
pub const fn direction(self) -> Vec2<f32> {
match self {
Corner::TopLeft => Vec2::new(-1., -1.),
Corner::TopRight => Vec2::new( 1., -1.),
Corner::BottomLeft => Vec2::new(-1., 1.),
Corner::BottomRight => Vec2::new( 1., 1.),
}
}
#[rustfmt::skip]
pub const fn uv(self) -> Vec2<f32> {
match self {
Corner::TopLeft => Vec2::new(0., 0.),
Corner::TopRight => Vec2::new(1., 0.),
Corner::BottomLeft => Vec2::new(0., 1.),
Corner::BottomRight => Vec2::new(1., 1.),
}
}
#[rustfmt::skip]
pub const fn select_corner<T: Copy>(self, aabr: &Aabr<T>) -> Vec2<T> {
match self {
Corner::TopLeft => Vec2::new(aabr.min.x, aabr.min.y),
Corner::TopRight => Vec2::new(aabr.max.x, aabr.min.y),
Corner::BottomLeft => Vec2::new(aabr.min.x, aabr.max.y),
Corner::BottomRight => Vec2::new(aabr.max.x, aabr.max.y),
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, bytemuck::Zeroable, bytemuck::Pod)]
#[repr(C)]
pub struct Vertex {
pub position: Vec2<f32>,
pub uv: Vec2<f32>,
}