first commit
This commit is contained in:
commit
ffb009b737
11 changed files with 863 additions and 0 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
/target
|
19
Cargo.toml
Normal file
19
Cargo.toml
Normal file
|
@ -0,0 +1,19 @@
|
|||
[package]
|
||||
name = "library-maths"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
winit = { version = "0.29", features = ["rwh_05"] }
|
||||
env_logger = "0.10"
|
||||
log = "0.4"
|
||||
wgpu = "22.1.0"
|
||||
tokio = { version = "1.40.0", features = ["macros", "rt-multi-thread"] }
|
||||
bytemuck = "*"
|
||||
rand = "*"
|
||||
|
||||
[profile.release]
|
||||
opt-level = 3
|
||||
|
||||
[profile.dev]
|
||||
opt-level = 2
|
95
src/canvas.rs
Normal file
95
src/canvas.rs
Normal file
|
@ -0,0 +1,95 @@
|
|||
use std::vec;
|
||||
|
||||
use winit::{event::{self, ElementState, Event, KeyEvent, WindowEvent}, event_loop::{EventLoop, EventLoopWindowTarget}, keyboard::{KeyCode, PhysicalKey}, window::{self, WindowBuilder}};
|
||||
|
||||
use crate::{state::state::State, structure::{enums::{micellaneous::Shape, Resolution}, structs::Vertex, traits::Drawable}};
|
||||
|
||||
pub struct Canvas {
|
||||
pub(crate) added: Vec<Box<dyn Drawable>>,
|
||||
pub(crate) resolution: Resolution
|
||||
}
|
||||
|
||||
impl Canvas {
|
||||
pub fn new(resolution: Resolution) -> Self {
|
||||
Canvas {
|
||||
added: vec![],
|
||||
resolution
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add(&mut self, added: Shape) {
|
||||
match added {
|
||||
Shape::Regular(polygon) => self.added.push(Box::new(polygon)),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn run(&self) {
|
||||
let event_loop = EventLoop::new().unwrap();
|
||||
let window = WindowBuilder::new().with_title("ciao").build(&event_loop).unwrap();
|
||||
|
||||
let mut verts = vec![];
|
||||
self.uniform(&mut verts);
|
||||
|
||||
let mut state = State::new(&window, Some(bytemuck::cast_slice(&verts)), self.resolution).await;
|
||||
|
||||
event_loop
|
||||
.run(move |event, control_flow| match event {
|
||||
Event::WindowEvent {
|
||||
ref event,
|
||||
window_id,
|
||||
} if window_id == state.window().id() => {
|
||||
if !state.input(event) {
|
||||
match event {
|
||||
WindowEvent::CloseRequested
|
||||
| WindowEvent::KeyboardInput {
|
||||
event:
|
||||
KeyEvent {
|
||||
state: ElementState::Pressed,
|
||||
physical_key: PhysicalKey::Code(KeyCode::Escape),
|
||||
..
|
||||
},
|
||||
..
|
||||
} => control_flow.exit(),
|
||||
|
||||
WindowEvent::Resized(size) => {
|
||||
state.resize(*size);
|
||||
}
|
||||
|
||||
WindowEvent::RedrawRequested => {
|
||||
state.window().request_redraw();
|
||||
/*
|
||||
if !surface_configured {
|
||||
return;
|
||||
} ??????
|
||||
*/
|
||||
|
||||
state.update();
|
||||
|
||||
match state.render() {
|
||||
Ok(_) => (),
|
||||
Err(wgpu::SurfaceError::Lost | wgpu::SurfaceError::Outdated) => {
|
||||
state.resize(state.size);
|
||||
}
|
||||
Err(wgpu::SurfaceError::OutOfMemory) => {
|
||||
log::error!("OutOfMemory");
|
||||
control_flow.exit();
|
||||
}
|
||||
Err(wgpu::SurfaceError::Timeout) => {
|
||||
log::warn!("Surface Timeout")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
fn uniform(&self, vector: &mut Vec<Vertex>) {
|
||||
vector.extend(self.added.iter().flat_map(|d| d.uniform()));
|
||||
}
|
||||
}
|
84
src/lib.rs
Normal file
84
src/lib.rs
Normal file
|
@ -0,0 +1,84 @@
|
|||
use state::state::State;
|
||||
use winit::{event::{ElementState, Event, KeyEvent, WindowEvent}, event_loop::EventLoop, keyboard::{KeyCode, PhysicalKey}, window::WindowBuilder};
|
||||
|
||||
mod state;
|
||||
mod structure;
|
||||
mod canvas;
|
||||
|
||||
// * re-exports
|
||||
pub use canvas::Canvas;
|
||||
pub use structure::enums::Resolution;
|
||||
pub use structure::enums::micellaneous::Shape;
|
||||
pub use structure::structs::baseline::polygon::Polygon;
|
||||
pub use structure::structs::Vertex;
|
||||
pub use structure::structs::{Col, Pos};
|
||||
// * re-exports
|
||||
|
||||
impl State<'_> {
|
||||
pub fn run(&mut self) {
|
||||
env_logger::init();
|
||||
let event_loop = EventLoop::new().unwrap();
|
||||
let window = WindowBuilder::new()
|
||||
.with_title("uhhhhh")
|
||||
.build(&event_loop)
|
||||
.unwrap();
|
||||
|
||||
event_loop
|
||||
.run(move |event, control_flow| match event {
|
||||
Event::WindowEvent {
|
||||
ref event,
|
||||
window_id,
|
||||
} if window_id == self.window().id() => {
|
||||
if !self.input(event) {
|
||||
match event {
|
||||
WindowEvent::CloseRequested
|
||||
| WindowEvent::KeyboardInput {
|
||||
event:
|
||||
KeyEvent {
|
||||
state: ElementState::Pressed,
|
||||
physical_key: PhysicalKey::Code(KeyCode::Escape),
|
||||
..
|
||||
},
|
||||
..
|
||||
} => control_flow.exit(),
|
||||
|
||||
WindowEvent::Resized(size) => {
|
||||
self.resize(*size);
|
||||
}
|
||||
|
||||
WindowEvent::RedrawRequested => {
|
||||
self.window().request_redraw();
|
||||
/*
|
||||
if !surface_configured {
|
||||
return;
|
||||
} ??????
|
||||
*/
|
||||
|
||||
self.update();
|
||||
|
||||
match self.render() {
|
||||
Ok(_) => (),
|
||||
Err(
|
||||
wgpu::SurfaceError::Lost | wgpu::SurfaceError::Outdated,
|
||||
) => {
|
||||
self.resize(self.size);
|
||||
}
|
||||
Err(wgpu::SurfaceError::OutOfMemory) => {
|
||||
log::error!("OutOfMemory");
|
||||
control_flow.exit();
|
||||
}
|
||||
Err(wgpu::SurfaceError::Timeout) => {
|
||||
log::warn!("Surface Timeout")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
}
|
29
src/shaders/shader.wgsl
Normal file
29
src/shaders/shader.wgsl
Normal file
|
@ -0,0 +1,29 @@
|
|||
// Vertex shader
|
||||
|
||||
struct VertexInput {
|
||||
@location(0) position: vec3<f32>,
|
||||
@location(1) color: vec3<f32>,
|
||||
}
|
||||
|
||||
|
||||
struct VertexOutput {
|
||||
@builtin(position) clip_position: vec4<f32>,
|
||||
@location(0) color: vec3<f32>,
|
||||
};
|
||||
|
||||
@vertex
|
||||
fn vs_main(
|
||||
model: VertexInput
|
||||
) -> VertexOutput {
|
||||
var out: VertexOutput;
|
||||
out.clip_position = vec4<f32>(model.position, 1.0);
|
||||
out.color = model.color;
|
||||
return out;
|
||||
}
|
||||
|
||||
// Fragment shader
|
||||
|
||||
@fragment
|
||||
fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
|
||||
return vec4<f32>(in.color, 1.0);
|
||||
}
|
1
src/state/mod.rs
Normal file
1
src/state/mod.rs
Normal file
|
@ -0,0 +1 @@
|
|||
pub mod state;
|
315
src/state/state.rs
Normal file
315
src/state/state.rs
Normal file
|
@ -0,0 +1,315 @@
|
|||
use super::super::structure::{enums::Resolution, structs::Vertex};
|
||||
use wgpu::{
|
||||
util::{BufferInitDescriptor, DeviceExt},
|
||||
BufferUsages, Color, PipelineLayout, PipelineLayoutDescriptor, RenderPassColorAttachment,
|
||||
RenderPipelineDescriptor, ShaderModuleDescriptor, VertexBufferLayout, VertexState,
|
||||
};
|
||||
use winit::{
|
||||
event::WindowEvent,
|
||||
event_loop::{self, EventLoop},
|
||||
window::{Window, WindowBuilder},
|
||||
};
|
||||
|
||||
pub struct State<'a> {
|
||||
pub clear_color: wgpu::Color,
|
||||
pub(crate) config: wgpu::SurfaceConfiguration,
|
||||
pub(crate) device: wgpu::Device,
|
||||
//pub index_buffer: wgpu::Buffer,
|
||||
pub num_index: u32,
|
||||
pub(crate) queue: wgpu::Queue,
|
||||
pub(crate) render_pipeline: wgpu::RenderPipeline,
|
||||
pub resolution: Resolution,
|
||||
pub(crate) size: winit::dpi::PhysicalSize<u32>,
|
||||
pub(crate) surface: wgpu::Surface<'a>,
|
||||
pub(crate) vertex_buffer: wgpu::Buffer,
|
||||
// The window must be declared after the surface so
|
||||
// it gets dropped after it as the surface contains
|
||||
// unsafe references to the window's resources.
|
||||
pub(crate) window: &'a Window
|
||||
}
|
||||
|
||||
impl<'a> State<'a> {
|
||||
// Creating some of the wgpu types requires async code
|
||||
pub async fn new(
|
||||
window: &'a Window,
|
||||
verts: Option<&[u8]>,
|
||||
resolution: Resolution,
|
||||
) -> State<'a> {
|
||||
|
||||
// * ---------- SIZE ----------
|
||||
let size = window.inner_size();
|
||||
// * ---------- SIZE ----------
|
||||
|
||||
// * ---------- INSTANCE ----------
|
||||
let instance: wgpu::Instance = wgpu::Instance::new(wgpu::InstanceDescriptor {
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
backends: wgpu::Backends::PRIMARY,
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
backends: wgpu::Backends::GL,
|
||||
..Default::default()
|
||||
});
|
||||
// * ---------- INSTANCE ----------
|
||||
|
||||
// * ---------- SURFACE ----------
|
||||
let surface = instance.create_surface(window).unwrap();
|
||||
// * ---------- SURFACE ----------
|
||||
|
||||
// * ---------- ADAPTER ----------
|
||||
let adapter: wgpu::Adapter = instance
|
||||
.request_adapter(&wgpu::RequestAdapterOptionsBase {
|
||||
power_preference: wgpu::PowerPreference::default(),
|
||||
force_fallback_adapter: false,
|
||||
compatible_surface: Some(&surface),
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
// * ---------- ADAPTER ----------
|
||||
|
||||
// * ---------- DEVICE & QUEUE ----------
|
||||
let (device, queue) = adapter
|
||||
.request_device(
|
||||
&wgpu::DeviceDescriptor {
|
||||
required_features: wgpu::Features::POLYGON_MODE_LINE,
|
||||
required_limits: {
|
||||
if cfg!(target_arch = "wasm32") {
|
||||
wgpu::Limits::downlevel_webgl2_defaults()
|
||||
} else {
|
||||
wgpu::Limits::default()
|
||||
}
|
||||
},
|
||||
label: None,
|
||||
memory_hints: Default::default(),
|
||||
},
|
||||
None,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
// * ---------- DEVICE & QUEUE ----------
|
||||
|
||||
// * ---------- SURFACE CAPABILITIES ----------
|
||||
let surface_caps = surface.get_capabilities(&adapter);
|
||||
// * ---------- SURFACE CAPABILITIES ----------
|
||||
|
||||
// * ---------- SURFACE FORMAT ----------
|
||||
// Shader code in this tutorial assumes an sRGB surface texture. Using a different
|
||||
// one will result in all the colors coming out darker. If you want to support non
|
||||
// sRGB surfaces, you'll need to account for that when drawing to the frame.
|
||||
let surface_format = surface_caps
|
||||
.formats
|
||||
.iter()
|
||||
.find(|f| f.is_srgb())
|
||||
.copied()
|
||||
.unwrap_or(surface_caps.formats[0]);
|
||||
// * ---------- SURFACE FORMAT ----------
|
||||
|
||||
// * ---------- SURFACE CONFIG ----------
|
||||
let config = wgpu::SurfaceConfiguration {
|
||||
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
|
||||
format: surface_format,
|
||||
width: size.width,
|
||||
height: size.height,
|
||||
present_mode: surface_caps.present_modes[0],
|
||||
alpha_mode: surface_caps.alpha_modes[0],
|
||||
view_formats: vec![],
|
||||
desired_maximum_frame_latency: 2,
|
||||
};
|
||||
// * ---------- SURFACE CONFIG ----------
|
||||
|
||||
// * ---------- PIPELINE ----------
|
||||
let shader: wgpu::ShaderModule = device.create_shader_module(ShaderModuleDescriptor {
|
||||
label: Some("shader"),
|
||||
// source: wgpu::ShaderSource::Wgsl(include_str!("learn_shading.wgsl").into()),
|
||||
source: wgpu::ShaderSource::Wgsl(include_str!("../shaders/shader.wgsl").into()),
|
||||
});
|
||||
|
||||
let render_pipeline_layout: PipelineLayout =
|
||||
device.create_pipeline_layout(&PipelineLayoutDescriptor {
|
||||
label: Some("Render Pipeline Layout"),
|
||||
bind_group_layouts: &[],
|
||||
push_constant_ranges: &[],
|
||||
});
|
||||
|
||||
let render_pipeline: wgpu::RenderPipeline =
|
||||
device.create_render_pipeline(&RenderPipelineDescriptor {
|
||||
label: Some("Pipeline"),
|
||||
layout: Some(&render_pipeline_layout),
|
||||
vertex: VertexState {
|
||||
module: &shader,
|
||||
entry_point: "vs_main",
|
||||
compilation_options: Default::default(),
|
||||
buffers: &[Vertex::desc()],
|
||||
},
|
||||
fragment: Some(wgpu::FragmentState {
|
||||
module: &shader,
|
||||
entry_point: "fs_main",
|
||||
compilation_options: Default::default(),
|
||||
targets: &[Some(wgpu::ColorTargetState {
|
||||
format: config.format,
|
||||
blend: Some(wgpu::BlendState::REPLACE),
|
||||
write_mask: wgpu::ColorWrites::ALL,
|
||||
})],
|
||||
}),
|
||||
primitive: wgpu::PrimitiveState {
|
||||
topology: wgpu::PrimitiveTopology::TriangleStrip,
|
||||
strip_index_format: None,
|
||||
front_face: wgpu::FrontFace::Ccw,
|
||||
cull_mode: Some(wgpu::Face::Back),
|
||||
unclipped_depth: false,
|
||||
polygon_mode: wgpu::PolygonMode::Fill,
|
||||
conservative: false,
|
||||
},
|
||||
depth_stencil: None,
|
||||
multisample: wgpu::MultisampleState {
|
||||
count: 1,
|
||||
mask: !0,
|
||||
alpha_to_coverage_enabled: false,
|
||||
},
|
||||
multiview: None,
|
||||
cache: None,
|
||||
});
|
||||
// * ---------- PIPELINE ----------
|
||||
|
||||
let verts_final = match verts {
|
||||
Some(v) => v,
|
||||
None => &[],
|
||||
};
|
||||
|
||||
// ! ---------- VERT BUFFER ----------
|
||||
let vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
||||
label: Some("Vertex Buffer"),
|
||||
contents: verts_final,
|
||||
usage: BufferUsages::VERTEX,
|
||||
});
|
||||
let num_index = (verts_final.len() / std::mem::size_of::<Vertex>()) as u32;
|
||||
// ! ---------- VERT BUFFER ----------
|
||||
|
||||
// // ! ---------- INDEX BUFFER ----------
|
||||
// let index_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
||||
// label: Some("Index Buffer"),
|
||||
// contents: bytemuck::cast_slice(INDICES),
|
||||
// usage: BufferUsages::INDEX,
|
||||
// });
|
||||
// // ! ---------- INDEX BUFFER ----------
|
||||
|
||||
let clear_color = Color::WHITE;
|
||||
|
||||
Self {
|
||||
resolution,
|
||||
window,
|
||||
surface,
|
||||
device,
|
||||
queue,
|
||||
config,
|
||||
size,
|
||||
clear_color,
|
||||
render_pipeline,
|
||||
vertex_buffer,
|
||||
num_index,
|
||||
//index_buffer,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn window(&self) -> &Window {
|
||||
&self.window
|
||||
}
|
||||
|
||||
pub fn resize(&mut self, new_size: winit::dpi::PhysicalSize<u32>) {
|
||||
if new_size.height > 0 && new_size.width > 0 {
|
||||
self.size = new_size;
|
||||
|
||||
self.config.height = new_size.height;
|
||||
self.config.width = new_size.width;
|
||||
|
||||
self.surface.configure(&self.device, &self.config);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn input(&mut self, event: &WindowEvent) -> bool {
|
||||
match event {
|
||||
WindowEvent::CursorMoved {
|
||||
device_id,
|
||||
position,
|
||||
} => {
|
||||
self.clear_color = Color {
|
||||
r: position.x / self.size.width as f64,
|
||||
g: position.y / self.size.height as f64,
|
||||
b: self.clear_color.b,
|
||||
a: 1.0,
|
||||
};
|
||||
true
|
||||
}
|
||||
|
||||
WindowEvent::MouseWheel {
|
||||
device_id,
|
||||
delta,
|
||||
phase,
|
||||
} => {
|
||||
match *delta {
|
||||
winit::event::MouseScrollDelta::LineDelta(x, y) => {
|
||||
self.clear_color.b += (x as f64 + y as f64) / 50.0
|
||||
}
|
||||
winit::event::MouseScrollDelta::PixelDelta(physical_position) => {
|
||||
self.clear_color.b += physical_position.x
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update(&mut self) {}
|
||||
|
||||
pub fn render(&mut self) -> Result<(), wgpu::SurfaceError> {
|
||||
let output = self.surface.get_current_texture()?;
|
||||
|
||||
let view = output
|
||||
.texture
|
||||
.create_view(&wgpu::TextureViewDescriptor::default());
|
||||
|
||||
let mut encoder = self
|
||||
.device
|
||||
.create_command_encoder(&wgpu::CommandEncoderDescriptor {
|
||||
label: Some("encoder"),
|
||||
});
|
||||
|
||||
let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
||||
label: Some("Render Pass"),
|
||||
color_attachments: &[Some(RenderPassColorAttachment {
|
||||
view: &view,
|
||||
resolve_target: None,
|
||||
ops: wgpu::Operations {
|
||||
load: wgpu::LoadOp::Clear(self.clear_color),
|
||||
store: wgpu::StoreOp::Store,
|
||||
},
|
||||
})],
|
||||
depth_stencil_attachment: None,
|
||||
timestamp_writes: None,
|
||||
occlusion_query_set: None,
|
||||
});
|
||||
|
||||
render_pass.set_pipeline(&self.render_pipeline);
|
||||
render_pass.set_vertex_buffer(0, self.vertex_buffer.slice(..));
|
||||
//render_pass.set_index_buffer(self.index_buffer.slice(..), wgpu::IndexFormat::Uint16);
|
||||
render_pass.draw(0..self.num_index, 0..1);
|
||||
//render_pass.draw_indexed(0..self.num_index, 0, 0..1);
|
||||
|
||||
// begin_render_pass borrows wncoder mutably, need to drop the borrow since .finish() takes ownership of it.
|
||||
drop(render_pass);
|
||||
|
||||
self.queue.submit(std::iter::once(encoder.finish()));
|
||||
output.present();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn modify_vert_buffer(&mut self, verts: Vec<Vertex>) {
|
||||
self.vertex_buffer = self.device.create_buffer_init(&BufferInitDescriptor {
|
||||
label: Some("Vertex Buffer"),
|
||||
// contents: bytemuck::cast_slice(&VERTS),
|
||||
contents: bytemuck::cast_slice(&verts),
|
||||
usage: BufferUsages::VERTEX,
|
||||
});
|
||||
self.num_index = verts.len() as u32;
|
||||
}
|
||||
}
|
39
src/structure/enums.rs
Normal file
39
src/structure/enums.rs
Normal file
|
@ -0,0 +1,39 @@
|
|||
pub use micellaneous::Resolution;
|
||||
|
||||
|
||||
pub mod micellaneous {
|
||||
use crate::structure::structs::baseline::polygon::Polygon;
|
||||
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum Resolution {
|
||||
Res_16_10,
|
||||
Res_16_9,
|
||||
Res_4_3,
|
||||
Res_21_9,
|
||||
Res_32_9,
|
||||
Res_5_4,
|
||||
Custom((u16, u16)),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum Rotation {
|
||||
HorizontalAlign,
|
||||
VerticalAlign,
|
||||
Custom(f32),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Shape {
|
||||
Regular(Polygon),
|
||||
}
|
||||
}
|
||||
|
||||
pub mod errors {
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum PolyError {
|
||||
NotEnoughSides,
|
||||
SidesAndTriangleNumberArentConsistant,
|
||||
}
|
||||
}
|
3
src/structure/mod.rs
Normal file
3
src/structure/mod.rs
Normal file
|
@ -0,0 +1,3 @@
|
|||
pub mod structs;
|
||||
pub mod enums;
|
||||
pub mod traits;
|
270
src/structure/structs.rs
Normal file
270
src/structure/structs.rs
Normal file
|
@ -0,0 +1,270 @@
|
|||
pub use baseline::color::Col;
|
||||
pub use baseline::position::Pos;
|
||||
pub use baseline::vertex::Vertex;
|
||||
|
||||
pub mod baseline {
|
||||
pub mod position {
|
||||
use bytemuck::{bytes_of, Pod, Zeroable};
|
||||
|
||||
use crate::structure::traits::Drawable;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct Pos(pub(crate) [f32; 3]);
|
||||
|
||||
impl Pos {
|
||||
pub fn new(x: f32, y: f32, z: f32) -> Self {
|
||||
Self([x, y, z])
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Zeroable for Pos {}
|
||||
unsafe impl Pod for Pos {}
|
||||
}
|
||||
|
||||
pub mod color {
|
||||
use bytemuck::{Pod, Zeroable};
|
||||
use rand::Rng;
|
||||
|
||||
use crate::structure::traits::Drawable;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct Col(pub(crate) [f32; 3]);
|
||||
|
||||
impl Col {
|
||||
pub fn new(r: f32, g: f32, b: f32) -> Self {
|
||||
Self([r, g, b])
|
||||
}
|
||||
pub fn random() -> Self {
|
||||
Self::new(
|
||||
rand::thread_rng().gen_range(0.0..=1.0),
|
||||
rand::thread_rng().gen_range(0.0..=1.0),
|
||||
rand::thread_rng().gen_range(0.0..=1.0),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Zeroable for Col {}
|
||||
unsafe impl Pod for Col {}
|
||||
}
|
||||
|
||||
pub mod vertex {
|
||||
use crate::structure::traits::Drawable;
|
||||
use bytemuck::{NoUninit, Pod, Zeroable};
|
||||
use wgpu::VertexAttribute;
|
||||
|
||||
use super::super::*;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct Vertex {
|
||||
pub(crate) position: Pos,
|
||||
pub(crate) color: Col,
|
||||
}
|
||||
|
||||
unsafe impl Zeroable for Vertex {}
|
||||
unsafe impl Pod for Vertex {}
|
||||
|
||||
impl Vertex {
|
||||
pub fn new(position: Pos, color: Col) -> Self {
|
||||
Vertex {
|
||||
position: position,
|
||||
color: color,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn desc() -> wgpu::VertexBufferLayout<'static> {
|
||||
wgpu::VertexBufferLayout {
|
||||
array_stride: std::mem::size_of::<Self>() as wgpu::BufferAddress,
|
||||
step_mode: wgpu::VertexStepMode::Vertex,
|
||||
attributes: &[
|
||||
VertexAttribute {
|
||||
format: wgpu::VertexFormat::Float32x3,
|
||||
offset: 0,
|
||||
shader_location: 0,
|
||||
},
|
||||
VertexAttribute {
|
||||
format: wgpu::VertexFormat::Float32x3,
|
||||
offset: std::mem::size_of::<[f32; 3]>() as u64,
|
||||
shader_location: 1,
|
||||
},
|
||||
],
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub mod triangle {
|
||||
use super::vertex::Vertex;
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct Triangle([Vertex; 3]);
|
||||
|
||||
impl Triangle {
|
||||
pub fn new(vertices: [Vertex; 3]) -> Self {
|
||||
Triangle(vertices)
|
||||
}
|
||||
|
||||
pub fn verts(&self) -> [Vertex; 3] {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub mod polygon {
|
||||
use crate::structure::{enums::{errors::PolyError, micellaneous::Rotation}, traits::Drawable};
|
||||
|
||||
use super::{color::Col, position::Pos, triangle::Triangle, vertex::Vertex};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Polygon {
|
||||
triangles: Vec<Triangle>,
|
||||
center: Vertex,
|
||||
radius: f32,
|
||||
n_sides: usize,
|
||||
rotation: Rotation,
|
||||
}
|
||||
|
||||
impl Polygon {
|
||||
/// # USAGE
|
||||
/// quality of life method, alias [Polygon::new_from_radius_with_rotation()] with `rotation == 0.0`
|
||||
pub fn new_from_radius(center: Vertex, radius: f32, n_sides: usize) -> Result<Self, PolyError> {
|
||||
Self::new_from_radius_with_rotation(center, radius, n_sides, Rotation::HorizontalAlign)
|
||||
}
|
||||
|
||||
/// # USAGE
|
||||
/// creates a [Polygon] from a center and radius with a rotation.
|
||||
///
|
||||
/// # WARNING
|
||||
/// - the rotation must be given in radiants.
|
||||
pub fn new_from_radius_with_rotation(
|
||||
center: Vertex,
|
||||
radius: f32,
|
||||
n_sides: usize,
|
||||
rotation: Rotation,
|
||||
) -> Result<Self, PolyError> {
|
||||
if n_sides < 3 {
|
||||
return Err(PolyError::NotEnoughSides);
|
||||
}
|
||||
|
||||
let rotation_rad = match rotation {
|
||||
Rotation::HorizontalAlign => {
|
||||
if n_sides % 6 == 0 {
|
||||
0.0
|
||||
} else if n_sides % 4 == 0 {
|
||||
(std::f32::consts::PI * 2.0 / n_sides as f32) / 2.0
|
||||
} else if n_sides % 3 == 0 {
|
||||
match ((n_sides-3)/6)%2 {
|
||||
1 => (std::f32::consts::PI * 2.0 / n_sides as f32) / 4.0,
|
||||
0 => (std::f32::consts::PI * 2.0)- ((std::f32::consts::PI * 2.0 / n_sides as f32) / 4.0),
|
||||
_ => panic!(), // should never happen, it's a module. ( % 2)
|
||||
}
|
||||
|
||||
} else {
|
||||
0.0
|
||||
} //TODO: add other n_sides
|
||||
},
|
||||
Rotation::VerticalAlign => (std::f32::consts::PI * 2.0 / n_sides as f32) / 2.0, // TODO
|
||||
Rotation::Custom(rad) => rad,
|
||||
};
|
||||
|
||||
let mut vec_tris: Vec<Triangle> = Vec::with_capacity(n_sides); // TODO: add space 4 morph
|
||||
|
||||
let center_x = center.position.0[0];
|
||||
let center_y = center.position.0[1];
|
||||
|
||||
for v in 0..n_sides {
|
||||
let theta_current =
|
||||
((v as f32 / n_sides as f32) * 2.0 * std::f32::consts::PI) + rotation_rad;
|
||||
let theta_next =
|
||||
(((v + 1) as f32 / n_sides as f32) * 2.0 * std::f32::consts::PI) + rotation_rad;
|
||||
let triangle = Triangle::new([
|
||||
Vertex::new(
|
||||
Pos::new(
|
||||
(theta_current.cos() * radius) + center_x,
|
||||
(theta_current.sin() * radius) + center_y,
|
||||
0.0,
|
||||
),
|
||||
Col::random(),
|
||||
),
|
||||
Vertex::new(
|
||||
Pos::new(
|
||||
(theta_next.cos() * radius) + center_x,
|
||||
(theta_next.sin() * radius) + center_y,
|
||||
0.0,
|
||||
),
|
||||
Col::random(),
|
||||
),
|
||||
center,
|
||||
]);
|
||||
vec_tris.push(triangle);
|
||||
}
|
||||
Self::new_from_everything_with_rotation(vec_tris, center, radius, n_sides, rotation)
|
||||
}
|
||||
|
||||
/// # USAGE
|
||||
/// quality of life method, alias [Polygon::new_from_everything_with_rotation()] with `rotation == Rotation::HorizontalAlign`
|
||||
fn new_from_everything(
|
||||
triangles: Vec<Triangle>,
|
||||
center: Vertex,
|
||||
radius: f32,
|
||||
n_sides: usize,
|
||||
) -> Result<Self, PolyError> {
|
||||
Self::new_from_everything_with_rotation(
|
||||
triangles,
|
||||
center,
|
||||
radius,
|
||||
n_sides,
|
||||
Rotation::HorizontalAlign,
|
||||
)
|
||||
}
|
||||
|
||||
/// # USAGE
|
||||
/// creates a [Polygon] from a center and radius with a rotation.
|
||||
///
|
||||
/// # WARNING
|
||||
/// - the rotation must be given in radiants.
|
||||
fn new_from_everything_with_rotation(
|
||||
triangles: Vec<Triangle>,
|
||||
center: Vertex,
|
||||
radius: f32,
|
||||
n_sides: usize,
|
||||
rotation: Rotation,
|
||||
) -> Result<Self, PolyError> {
|
||||
if n_sides < 3 {
|
||||
return Err(PolyError::NotEnoughSides);
|
||||
}
|
||||
|
||||
if triangles.len() != n_sides {
|
||||
return Err(PolyError::SidesAndTriangleNumberArentConsistant);
|
||||
}
|
||||
|
||||
Ok(Polygon {
|
||||
triangles,
|
||||
center,
|
||||
radius,
|
||||
n_sides,
|
||||
rotation,
|
||||
})
|
||||
}
|
||||
|
||||
fn get_triangles(&self) -> Vec<Triangle> {
|
||||
self.triangles.clone()
|
||||
}
|
||||
|
||||
pub fn uniform(&self) -> Vec<Vertex> {
|
||||
self.get_triangles()
|
||||
.iter()
|
||||
.flat_map(|t| t.verts().to_vec())
|
||||
.collect::<Vec<Vertex>>()
|
||||
}
|
||||
}
|
||||
|
||||
impl Drawable for Polygon {
|
||||
fn uniform(&self) -> Vec<Vertex> {
|
||||
self.uniform()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
7
src/structure/traits.rs
Normal file
7
src/structure/traits.rs
Normal file
|
@ -0,0 +1,7 @@
|
|||
use bytemuck::NoUninit;
|
||||
|
||||
use super::structs::Vertex;
|
||||
|
||||
pub trait Drawable {
|
||||
fn uniform(&self) -> Vec<Vertex>;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue