bezier curves, i guess
This commit is contained in:
parent
06b52aa5d4
commit
ec477975f0
8 changed files with 614 additions and 44 deletions
64
.vscode/launch.json
vendored
Normal file
64
.vscode/launch.json
vendored
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
{
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"type": "lldb",
|
||||
"request": "launch",
|
||||
"name": "Debug unit tests in library 'bezier'",
|
||||
"cargo": {
|
||||
"args": [
|
||||
"test",
|
||||
"--no-run",
|
||||
"--lib",
|
||||
"--package=bezier"
|
||||
],
|
||||
"filter": {
|
||||
"name": "bezier",
|
||||
"kind": "lib"
|
||||
}
|
||||
},
|
||||
"args": [],
|
||||
"cwd": "${workspaceFolder}"
|
||||
},
|
||||
{
|
||||
"type": "lldb",
|
||||
"request": "launch",
|
||||
"name": "Debug executable 'bezier'",
|
||||
"cargo": {
|
||||
"args": [
|
||||
"build",
|
||||
"--bin=bezier",
|
||||
"--package=bezier"
|
||||
],
|
||||
"filter": {
|
||||
"name": "bezier",
|
||||
"kind": "bin"
|
||||
}
|
||||
},
|
||||
"args": [],
|
||||
"cwd": "${workspaceFolder}"
|
||||
},
|
||||
{
|
||||
"type": "lldb",
|
||||
"request": "launch",
|
||||
"name": "Debug unit tests in executable 'bezier'",
|
||||
"cargo": {
|
||||
"args": [
|
||||
"test",
|
||||
"--no-run",
|
||||
"--bin=bezier",
|
||||
"--package=bezier"
|
||||
],
|
||||
"filter": {
|
||||
"name": "bezier",
|
||||
"kind": "bin"
|
||||
}
|
||||
},
|
||||
"args": [],
|
||||
"cwd": "${workspaceFolder}"
|
||||
}
|
||||
]
|
||||
}
|
||||
157
Cargo.lock
generated
157
Cargo.lock
generated
|
|
@ -131,6 +131,15 @@ dependencies = [
|
|||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "approx"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f2a05fd1bd10b2527e20a2cd32d8873d115b8b39fe219ee25f42a8aca6ba278"
|
||||
dependencies = [
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "arrayref"
|
||||
version = "0.3.7"
|
||||
|
|
@ -175,7 +184,9 @@ name = "bezier"
|
|||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bytemuck",
|
||||
"cgmath",
|
||||
"env_logger",
|
||||
"geo",
|
||||
"log",
|
||||
"pollster",
|
||||
"wgpu",
|
||||
|
|
@ -260,6 +271,12 @@ dependencies = [
|
|||
"syn 2.0.66",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
||||
|
||||
[[package]]
|
||||
name = "bytes"
|
||||
version = "1.6.0"
|
||||
|
|
@ -321,6 +338,16 @@ version = "0.1.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e"
|
||||
|
||||
[[package]]
|
||||
name = "cgmath"
|
||||
version = "0.18.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1a98d30140e3296250832bbaaff83b27dcd6fa3cc70fb6f1f3e5c9c0023b5317"
|
||||
dependencies = [
|
||||
"approx",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "codespan-reporting"
|
||||
version = "0.11.1"
|
||||
|
|
@ -471,6 +498,22 @@ version = "1.2.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2"
|
||||
|
||||
[[package]]
|
||||
name = "earcutr"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "79127ed59a85d7687c409e9978547cffb7dc79675355ed22da6b66fd5f6ead01"
|
||||
dependencies = [
|
||||
"itertools",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b"
|
||||
|
||||
[[package]]
|
||||
name = "env_filter"
|
||||
version = "0.1.0"
|
||||
|
|
@ -510,6 +553,12 @@ dependencies = [
|
|||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "float_next_after"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8bf7cc16383c4b8d58b9905a8509f02926ce3058053c056376248d958c9df1e8"
|
||||
|
||||
[[package]]
|
||||
name = "foreign-types"
|
||||
version = "0.5.0"
|
||||
|
|
@ -537,6 +586,44 @@ version = "0.3.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b"
|
||||
|
||||
[[package]]
|
||||
name = "geo"
|
||||
version = "0.28.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f811f663912a69249fa620dcd2a005db7254529da2d8a0b23942e81f47084501"
|
||||
dependencies = [
|
||||
"earcutr",
|
||||
"float_next_after",
|
||||
"geo-types",
|
||||
"geographiclib-rs",
|
||||
"log",
|
||||
"num-traits",
|
||||
"robust",
|
||||
"rstar",
|
||||
"spade",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "geo-types"
|
||||
version = "0.7.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9ff16065e5720f376fbced200a5ae0f47ace85fd70b7e54269790281353b6d61"
|
||||
dependencies = [
|
||||
"approx",
|
||||
"num-traits",
|
||||
"rstar",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "geographiclib-rs"
|
||||
version = "0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6e5ed84f8089c70234b0a8e0aedb6dc733671612ddc0d37c6066052f9781960"
|
||||
dependencies = [
|
||||
"libm",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gethostname"
|
||||
version = "0.4.3"
|
||||
|
|
@ -642,6 +729,15 @@ dependencies = [
|
|||
"bitflags 2.5.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hash32"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.14.5"
|
||||
|
|
@ -667,6 +763,16 @@ dependencies = [
|
|||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "heapless"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad"
|
||||
dependencies = [
|
||||
"hash32",
|
||||
"stable_deref_trait",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.3.9"
|
||||
|
|
@ -712,6 +818,15 @@ version = "1.70.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800"
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57"
|
||||
dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jni"
|
||||
version = "0.21.1"
|
||||
|
|
@ -795,6 +910,12 @@ dependencies = [
|
|||
"windows-targets 0.52.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libm"
|
||||
version = "0.2.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058"
|
||||
|
||||
[[package]]
|
||||
name = "libredox"
|
||||
version = "0.0.2"
|
||||
|
|
@ -925,6 +1046,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"libm",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -1209,6 +1331,23 @@ version = "1.1.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "19b30a45b0cd0bcca8037f3d0dc3421eaf95327a17cad11964fb8179b4fc4832"
|
||||
|
||||
[[package]]
|
||||
name = "robust"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cbf4a6aa5f6d6888f39e980649f3ad6b666acdce1d78e95b8a2cb076e687ae30"
|
||||
|
||||
[[package]]
|
||||
name = "rstar"
|
||||
version = "0.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "133315eb94c7b1e8d0cb097e5a710d850263372fd028fff18969de708afc7008"
|
||||
dependencies = [
|
||||
"heapless",
|
||||
"num-traits",
|
||||
"smallvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc-hash"
|
||||
version = "1.1.0"
|
||||
|
|
@ -1340,6 +1479,18 @@ dependencies = [
|
|||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "spade"
|
||||
version = "2.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b20a809169ae442497e41a997fc5f14e2eea04e6ac590816a910d5d8068c8c0"
|
||||
dependencies = [
|
||||
"hashbrown",
|
||||
"num-traits",
|
||||
"robust",
|
||||
"smallvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "spirv"
|
||||
version = "0.3.0+sdk-1.3.268.0"
|
||||
|
|
@ -1349,6 +1500,12 @@ dependencies = [
|
|||
"bitflags 2.5.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "stable_deref_trait"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
|
||||
|
||||
[[package]]
|
||||
name = "static_assertions"
|
||||
version = "1.1.0"
|
||||
|
|
|
|||
|
|
@ -10,3 +10,5 @@ env_logger = "*"
|
|||
pollster = "*"
|
||||
log = "0.4.21"
|
||||
bytemuck = { version = "1.12", features = [ "derive" ] }
|
||||
cgmath = "0.18"
|
||||
geo = "0.28.0"
|
||||
|
|
|
|||
199
src/curve.rs
Normal file
199
src/curve.rs
Normal file
|
|
@ -0,0 +1,199 @@
|
|||
use cgmath::InnerSpace;
|
||||
|
||||
use crate::{vertex::RenderData, Vertex};
|
||||
|
||||
type Vector2 = cgmath::Vector2<f64>;
|
||||
|
||||
fn vec2(x: f64, y: f64) -> Vector2 {
|
||||
cgmath::vec2(x, y)
|
||||
}
|
||||
|
||||
pub struct Bezier {
|
||||
pub start: Vector2,
|
||||
pub middle: Vector2,
|
||||
pub end: Vector2,
|
||||
}
|
||||
|
||||
impl Bezier {
|
||||
pub fn subdivide(&self, count: usize) -> PolyLine {
|
||||
PolyLine {
|
||||
points: (0..count)
|
||||
.map(|i| self.eval((i as f64) / (count - 1) as f64))
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new(start: Vector2, middle: Vector2, end: Vector2) -> Self {
|
||||
Self { start, middle, end }
|
||||
}
|
||||
|
||||
fn eval(&self, t: f64) -> Vector2 {
|
||||
let a = Self::lerp(self.start, self.middle, t);
|
||||
let b = Self::lerp(self.middle, self.end, t);
|
||||
Self::lerp(a, b, t)
|
||||
}
|
||||
|
||||
fn lerp(start: Vector2, end: Vector2, t: f64) -> Vector2 {
|
||||
end * t + start * (1.0 - t)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PolyLine {
|
||||
pub points: Vec<Vector2>,
|
||||
}
|
||||
|
||||
impl PolyLine {
|
||||
pub fn new() -> Self {
|
||||
Self { points: Vec::new() }
|
||||
}
|
||||
|
||||
pub fn get_render_data(&self, width: f64) -> RenderData {
|
||||
let mut result = RenderData::new();
|
||||
|
||||
for i in 1..self.points.len() {
|
||||
result = result.merge(self.get_segment_render_data(i, width));
|
||||
}
|
||||
|
||||
for i in 1..self.points.len() - 1 {
|
||||
result = result.merge(self.get_connection_render_data(i, width));
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
fn get_segment_render_data(&self, i: usize, width: f64) -> RenderData {
|
||||
let start_points = self.get_adjusted_start_points(i - 1, width);
|
||||
let end_points = self.get_adjusted_end_points(i, width);
|
||||
let vertices: Vec<Vertex> = [start_points.0, start_points.1, end_points.0, end_points.1]
|
||||
.map(Vector2::into)
|
||||
.map(Vertex::new_f64)
|
||||
.into_iter()
|
||||
.collect();
|
||||
let indices: Vec<_> = vec![0, 2, 3, 0, 3, 1];
|
||||
RenderData { vertices, indices }
|
||||
}
|
||||
|
||||
fn get_connection_render_data(&self, i: usize, width: f64) -> RenderData {
|
||||
let vertices: Vec<_> = match self.get_connection(i, width) {
|
||||
Some((intersection, false)) => {
|
||||
vec![
|
||||
self.get_end_points(i, width).1,
|
||||
intersection,
|
||||
self.get_start_points(i, width).1,
|
||||
]
|
||||
}
|
||||
Some((intersection, true)) => {
|
||||
vec![
|
||||
self.get_end_points(i, width).0,
|
||||
intersection,
|
||||
self.get_start_points(i, width).0,
|
||||
]
|
||||
}
|
||||
None => vec![],
|
||||
}
|
||||
.into_iter()
|
||||
.map(Vector2::into)
|
||||
.map(Vertex::new_f64)
|
||||
.into_iter()
|
||||
.collect();
|
||||
let indices = if vertices.is_empty() {
|
||||
vec![]
|
||||
} else {
|
||||
vec![0, 1, 2]
|
||||
};
|
||||
RenderData { vertices, indices }
|
||||
}
|
||||
|
||||
fn get_adjusted_start_points(&self, i: usize, width: f64) -> (Vector2, Vector2) {
|
||||
let start_points = self.get_start_points(i, width);
|
||||
if i == 0 {
|
||||
return start_points;
|
||||
}
|
||||
match self.get_connection(i, width) {
|
||||
Some((intersection, false)) => (intersection, start_points.1),
|
||||
Some((intersection, true)) => (start_points.0, intersection),
|
||||
None => start_points,
|
||||
}
|
||||
}
|
||||
|
||||
fn get_adjusted_end_points(&self, i: usize, width: f64) -> (Vector2, Vector2) {
|
||||
let end_points = self.get_end_points(i, width);
|
||||
if i + 1 == self.points.len() {
|
||||
return end_points;
|
||||
}
|
||||
match self.get_connection(i, width) {
|
||||
Some((intersection, false)) => (intersection, end_points.1),
|
||||
Some((intersection, true)) => (end_points.0, intersection),
|
||||
None => end_points,
|
||||
}
|
||||
}
|
||||
|
||||
fn get_connection(&self, i: usize, width: f64) -> Option<(Vector2, bool)> {
|
||||
let start_points = self.get_start_points(i - 1, width);
|
||||
let end_points = self.get_end_points(i, width);
|
||||
let next_start_points = self.get_start_points(i, width);
|
||||
let next_end_points = self.get_end_points(i + 1, width);
|
||||
let lines = (
|
||||
Self::line(start_points.0, end_points.0),
|
||||
Self::line(start_points.1, end_points.1),
|
||||
);
|
||||
let next_lines = (
|
||||
Self::line(next_start_points.0, next_end_points.0),
|
||||
Self::line(next_start_points.1, next_end_points.1),
|
||||
);
|
||||
|
||||
use geo::algorithm::line_intersection::{line_intersection, LineIntersection};
|
||||
|
||||
let intersections = (
|
||||
line_intersection(lines.0, next_lines.0),
|
||||
line_intersection(lines.1, next_lines.1),
|
||||
);
|
||||
|
||||
match intersections {
|
||||
(Some(LineIntersection::SinglePoint { intersection, .. }), None) => {
|
||||
Some((Vector2::new(intersection.x, intersection.y), false))
|
||||
}
|
||||
(None, Some(LineIntersection::SinglePoint { intersection, .. })) => {
|
||||
Some((Vector2::new(intersection.x, intersection.y), true))
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn get_start_points(&self, i: usize, width: f64) -> (Vector2, Vector2) {
|
||||
if i + 1 == self.points.len() {
|
||||
panic!();
|
||||
}
|
||||
Self::offset_by_direction(
|
||||
self.points[i],
|
||||
(self.points[i + 1] - self.points[i]).normalize() * width,
|
||||
)
|
||||
}
|
||||
|
||||
fn get_end_points(&self, i: usize, width: f64) -> (Vector2, Vector2) {
|
||||
if i == 0 {
|
||||
panic!();
|
||||
}
|
||||
Self::offset_by_direction(
|
||||
self.points[i],
|
||||
(self.points[i] - self.points[i - 1]).normalize() * width,
|
||||
)
|
||||
}
|
||||
|
||||
fn line(start: Vector2, end: Vector2) -> geo::Line<f64> {
|
||||
geo::Line {
|
||||
start: geo::Coord {
|
||||
x: start.x,
|
||||
y: start.y,
|
||||
},
|
||||
end: geo::Coord { x: end.x, y: end.y },
|
||||
}
|
||||
}
|
||||
|
||||
fn offset_by_direction(point: Vector2, direction: Vector2) -> (Vector2, Vector2) {
|
||||
(
|
||||
point + vec2(direction.y, -direction.x),
|
||||
point + vec2(-direction.y, direction.x),
|
||||
)
|
||||
}
|
||||
}
|
||||
11
src/lib.rs
11
src/lib.rs
|
|
@ -1,8 +1,11 @@
|
|||
pub mod vertex;
|
||||
pub mod curve;
|
||||
mod state;
|
||||
pub mod vertex;
|
||||
|
||||
use std::time::SystemTime;
|
||||
|
||||
pub use vertex::Vertex;
|
||||
use state::State;
|
||||
pub use vertex::Vertex;
|
||||
|
||||
use winit::{
|
||||
event::{Event, KeyEvent, WindowEvent},
|
||||
|
|
@ -22,6 +25,8 @@ pub async fn run() {
|
|||
|
||||
let mut state = State::new(&window).await;
|
||||
|
||||
let start_time = SystemTime::now();
|
||||
|
||||
let _ = event_loop.run(move |mut event, control_flow| match event {
|
||||
Event::WindowEvent {
|
||||
ref mut event,
|
||||
|
|
@ -42,7 +47,7 @@ pub async fn run() {
|
|||
state.resize(*physical_size);
|
||||
}
|
||||
WindowEvent::RedrawRequested => {
|
||||
state.update();
|
||||
state.update(start_time.elapsed().unwrap());
|
||||
match state.render() {
|
||||
Ok(_) => {}
|
||||
Err(wgpu::SurfaceError::Lost) => {} /*state.resize(state.size)*/,
|
||||
|
|
|
|||
|
|
@ -4,20 +4,20 @@ struct VertexInput {
|
|||
|
||||
struct VertexOutput {
|
||||
@builtin(position) clip_position: vec4<f32>,
|
||||
@location(0) color: vec3<f32>,
|
||||
}
|
||||
|
||||
@vertex
|
||||
fn vs_main(
|
||||
@builtin(vertex_index) in_vertex_index: u32,
|
||||
model: VertexInput
|
||||
) -> VertexOutput {
|
||||
var out: VertexOutput;
|
||||
let x = f32(1 - i32(in_vertex_index)) * 0.5;
|
||||
let y = f32(i32(in_vertex_index & 1u) * 2 - 1) * 0.5;
|
||||
out.clip_position = vec4<f32>(x, y, 0.0, 1.0);
|
||||
out.color = vec3<f32>(1.0, 1.0, 1.0);
|
||||
out.clip_position = vec4<f32>(model.position, 0.0, 1.0);
|
||||
return out;
|
||||
}
|
||||
|
||||
@fragment
|
||||
fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
|
||||
return vec4<f32>(0.3, 0.2, 0.1, 1.0);
|
||||
return vec4<f32>(in.color, 1.0);
|
||||
}
|
||||
|
|
|
|||
174
src/state.rs
174
src/state.rs
|
|
@ -1,5 +1,7 @@
|
|||
use wgpu::{util::DeviceExt, ColorTargetState};
|
||||
use crate::Vertex;
|
||||
use std::time::Duration;
|
||||
|
||||
use crate::{curve::Bezier, Vertex};
|
||||
use wgpu::ColorTargetState;
|
||||
|
||||
pub struct State<'window> {
|
||||
window: &'window winit::window::Window,
|
||||
|
|
@ -8,9 +10,13 @@ pub struct State<'window> {
|
|||
device: wgpu::Device,
|
||||
queue: wgpu::Queue,
|
||||
|
||||
render_pipeline: wgpu::RenderPipeline,
|
||||
pipelines: [wgpu::RenderPipeline; 2],
|
||||
current_pipeline: usize,
|
||||
|
||||
vertex_buffer: wgpu::Buffer,
|
||||
index_buffer: wgpu::Buffer,
|
||||
|
||||
num_indices: u32,
|
||||
}
|
||||
|
||||
impl<'window> State<'window> {
|
||||
|
|
@ -37,7 +43,7 @@ impl<'window> State<'window> {
|
|||
.request_device(
|
||||
&wgpu::DeviceDescriptor {
|
||||
label: Some("Wgpu device"),
|
||||
required_features: wgpu::Features::empty(),
|
||||
required_features: wgpu::Features::POLYGON_MODE_LINE,
|
||||
required_limits: wgpu::Limits::default(),
|
||||
},
|
||||
None,
|
||||
|
|
@ -50,19 +56,26 @@ impl<'window> State<'window> {
|
|||
|
||||
let shader_module = device.create_shader_module(wgpu::include_wgsl!("shader.wgsl"));
|
||||
|
||||
let vertices = [
|
||||
Vertex::new_white([0.0, 0.0]),
|
||||
Vertex::new_white([1.0, 0.0]),
|
||||
Vertex::new_white([1.0, 1.0]),
|
||||
];
|
||||
|
||||
let vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
||||
let vertex_buffer = device.create_buffer(&wgpu::BufferDescriptor {
|
||||
label: Some("Vertex Buffer"),
|
||||
contents: bytemuck::cast_slice(&vertices),
|
||||
usage: wgpu::BufferUsages::VERTEX,
|
||||
usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
|
||||
size: 10 * 2048,
|
||||
mapped_at_creation: false,
|
||||
});
|
||||
|
||||
let render_pipeline = Self::create_render_pipeline(&device, &shader_module, &surface_config);
|
||||
let index_buffer = device.create_buffer(&wgpu::BufferDescriptor {
|
||||
label: Some("Index Buffer"),
|
||||
usage: wgpu::BufferUsages::INDEX | wgpu::BufferUsages::COPY_DST,
|
||||
size: 10 * 1024,
|
||||
mapped_at_creation: false,
|
||||
});
|
||||
|
||||
// println!("{:#?} {:#?}", vertices[0], vertices[1]);
|
||||
|
||||
let pipelines = [
|
||||
Self::create_fill_render_pipeline(&device, &shader_module, &surface_config),
|
||||
Self::create_line_render_pipeline(&device, &shader_module, &surface_config),
|
||||
];
|
||||
|
||||
Self {
|
||||
window,
|
||||
|
|
@ -70,8 +83,11 @@ impl<'window> State<'window> {
|
|||
surface,
|
||||
device,
|
||||
queue,
|
||||
render_pipeline,
|
||||
vertex_buffer
|
||||
pipelines,
|
||||
current_pipeline: 0,
|
||||
vertex_buffer,
|
||||
index_buffer,
|
||||
num_indices: 0,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -116,9 +132,10 @@ impl<'window> State<'window> {
|
|||
occlusion_query_set: None,
|
||||
});
|
||||
|
||||
render_pass.set_pipeline(&self.render_pipeline);
|
||||
render_pass.set_pipeline(&self.pipelines[self.current_pipeline]);
|
||||
render_pass.set_vertex_buffer(0, self.vertex_buffer.slice(..));
|
||||
render_pass.draw(0..3, 0..1);
|
||||
render_pass.set_index_buffer(self.index_buffer.slice(..), wgpu::IndexFormat::Uint32);
|
||||
render_pass.draw_indexed(0..self.num_indices, 0, 0..1);
|
||||
}
|
||||
|
||||
pub fn window(&self) -> &winit::window::Window {
|
||||
|
|
@ -126,7 +143,23 @@ impl<'window> State<'window> {
|
|||
}
|
||||
|
||||
pub fn input(&mut self, _event: &mut winit::event::WindowEvent) -> bool {
|
||||
false
|
||||
use winit::event::{ElementState, KeyEvent, WindowEvent};
|
||||
use winit::keyboard::{KeyCode, PhysicalKey};
|
||||
match _event {
|
||||
WindowEvent::KeyboardInput {
|
||||
event:
|
||||
KeyEvent {
|
||||
physical_key: PhysicalKey::Code(KeyCode::Space),
|
||||
state: ElementState::Pressed,
|
||||
..
|
||||
},
|
||||
..
|
||||
} => {
|
||||
self.current_pipeline = self.current_pipeline ^ 1;
|
||||
false
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn resize(&mut self, new_size: winit::dpi::PhysicalSize<u32>) {
|
||||
|
|
@ -138,13 +171,71 @@ impl<'window> State<'window> {
|
|||
self.surface.configure(&self.device, &self.surface_config);
|
||||
}
|
||||
|
||||
pub fn update(&mut self) {}
|
||||
pub fn update(&mut self, since_start: Duration) {
|
||||
let width = 0.01;
|
||||
let count = 30;
|
||||
|
||||
fn create_render_pipeline(device: &wgpu::Device, shader_module: &wgpu::ShaderModule, surface_config: &wgpu::SurfaceConfiguration) -> wgpu::RenderPipeline {
|
||||
let speed = 1000.0;
|
||||
|
||||
let start_y = ((since_start.as_millis() as f64) / speed).sin() * 0.5;
|
||||
let middle_y = ((since_start.as_millis() as f64) / speed * 2.0).sin();
|
||||
let end_y = ((since_start.as_millis() as f64) / speed * 1.5).sin() * 0.5;
|
||||
|
||||
let poly_line = Bezier::new(
|
||||
cgmath::Vector2 {
|
||||
x: -0.5,
|
||||
y: start_y,
|
||||
},
|
||||
cgmath::Vector2 {
|
||||
x: 0.0,
|
||||
y: middle_y,
|
||||
},
|
||||
cgmath::Vector2 { x: 0.5, y: end_y },
|
||||
)
|
||||
.subdivide(count);
|
||||
|
||||
let data = poly_line.get_render_data(width);
|
||||
|
||||
self.queue
|
||||
.write_buffer(&self.vertex_buffer, 0, bytemuck::cast_slice(&data.vertices));
|
||||
self.queue
|
||||
.write_buffer(&self.index_buffer, 0, bytemuck::cast_slice(&data.indices));
|
||||
self.num_indices = data.indices.len() as u32;
|
||||
}
|
||||
|
||||
fn create_fill_render_pipeline(
|
||||
device: &wgpu::Device,
|
||||
shader_module: &wgpu::ShaderModule,
|
||||
surface_config: &wgpu::SurfaceConfiguration,
|
||||
) -> wgpu::RenderPipeline {
|
||||
let vertex = Self::create_vertex_state(shader_module);
|
||||
let color_targets = Self::create_color_targets(surface_config);
|
||||
let fragment = Self::create_fragment_state(shader_module, &color_targets);
|
||||
let primitive = Self::create_primitive_state();
|
||||
let primitive = Self::create_fill_primitive_state();
|
||||
let multisample = Self::create_multisample_state();
|
||||
|
||||
let render_pipeline_layout = Self::create_pipeline_layout(device);
|
||||
device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
|
||||
label: Some("Render Pipeline"),
|
||||
layout: Some(&render_pipeline_layout),
|
||||
vertex,
|
||||
fragment: Some(fragment),
|
||||
primitive,
|
||||
depth_stencil: None,
|
||||
multisample,
|
||||
multiview: None,
|
||||
})
|
||||
}
|
||||
|
||||
fn create_line_render_pipeline(
|
||||
device: &wgpu::Device,
|
||||
shader_module: &wgpu::ShaderModule,
|
||||
surface_config: &wgpu::SurfaceConfiguration,
|
||||
) -> wgpu::RenderPipeline {
|
||||
let vertex = Self::create_vertex_state(shader_module);
|
||||
let color_targets = Self::create_color_targets(surface_config);
|
||||
let fragment = Self::create_fragment_state(shader_module, &color_targets);
|
||||
let primitive = Self::create_line_primitive_state();
|
||||
let multisample = Self::create_multisample_state();
|
||||
|
||||
let render_pipeline_layout = Self::create_pipeline_layout(device);
|
||||
|
|
@ -168,7 +259,11 @@ impl<'window> State<'window> {
|
|||
})
|
||||
}
|
||||
|
||||
fn create_surface_config(surface: &wgpu::Surface, adapter: &wgpu::Adapter, size: &winit::dpi::PhysicalSize<u32>) -> wgpu::SurfaceConfiguration {
|
||||
fn create_surface_config(
|
||||
surface: &wgpu::Surface,
|
||||
adapter: &wgpu::Adapter,
|
||||
size: &winit::dpi::PhysicalSize<u32>,
|
||||
) -> wgpu::SurfaceConfiguration {
|
||||
let surface_capabilities = surface.get_capabilities(adapter);
|
||||
let surface_format = surface_capabilities
|
||||
.formats
|
||||
|
|
@ -181,7 +276,7 @@ impl<'window> State<'window> {
|
|||
format: surface_format,
|
||||
width: size.width,
|
||||
height: size.height,
|
||||
present_mode: wgpu::PresentMode::Fifo,
|
||||
present_mode: wgpu::PresentMode::AutoNoVsync,
|
||||
desired_maximum_frame_latency: 2,
|
||||
alpha_mode: surface_capabilities.alpha_modes[0],
|
||||
view_formats: vec![],
|
||||
|
|
@ -198,7 +293,10 @@ impl<'window> State<'window> {
|
|||
}
|
||||
}
|
||||
|
||||
fn create_fragment_state<'a>(shader_module: &'a wgpu::ShaderModule, targets: &'a [Option<ColorTargetState>]) -> wgpu::FragmentState<'a> {
|
||||
fn create_fragment_state<'a>(
|
||||
shader_module: &'a wgpu::ShaderModule,
|
||||
targets: &'a [Option<ColorTargetState>],
|
||||
) -> wgpu::FragmentState<'a> {
|
||||
wgpu::FragmentState {
|
||||
module: shader_module,
|
||||
entry_point: "fs_main",
|
||||
|
|
@ -206,7 +304,9 @@ impl<'window> State<'window> {
|
|||
}
|
||||
}
|
||||
|
||||
fn create_color_targets(surface_config: &wgpu::SurfaceConfiguration) -> Vec<Option<ColorTargetState>> {
|
||||
fn create_color_targets(
|
||||
surface_config: &wgpu::SurfaceConfiguration,
|
||||
) -> Vec<Option<ColorTargetState>> {
|
||||
vec![Some(wgpu::ColorTargetState {
|
||||
format: surface_config.format,
|
||||
blend: Some(wgpu::BlendState::REPLACE),
|
||||
|
|
@ -214,19 +314,35 @@ impl<'window> State<'window> {
|
|||
})]
|
||||
}
|
||||
|
||||
fn create_primitive_state() -> wgpu::PrimitiveState {
|
||||
fn create_fill_primitive_state() -> wgpu::PrimitiveState {
|
||||
wgpu::PrimitiveState {
|
||||
topology: wgpu::PrimitiveTopology::TriangleList,
|
||||
strip_index_format: None,
|
||||
front_face: wgpu::FrontFace::Ccw,
|
||||
cull_mode: Some(wgpu::Face::Back),
|
||||
cull_mode: None,
|
||||
polygon_mode: wgpu::PolygonMode::Fill,
|
||||
unclipped_depth: false,
|
||||
conservative: false,
|
||||
}
|
||||
}
|
||||
|
||||
fn create_line_primitive_state() -> wgpu::PrimitiveState {
|
||||
wgpu::PrimitiveState {
|
||||
topology: wgpu::PrimitiveTopology::TriangleList,
|
||||
strip_index_format: None,
|
||||
front_face: wgpu::FrontFace::Ccw,
|
||||
cull_mode: None,
|
||||
polygon_mode: wgpu::PolygonMode::Line,
|
||||
unclipped_depth: false,
|
||||
conservative: false,
|
||||
}
|
||||
}
|
||||
|
||||
fn create_multisample_state() -> wgpu::MultisampleState {
|
||||
wgpu::MultisampleState { count: 1, mask: !0, alpha_to_coverage_enabled: false }
|
||||
wgpu::MultisampleState {
|
||||
count: 1,
|
||||
mask: !0,
|
||||
alpha_to_coverage_enabled: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,19 +2,21 @@
|
|||
#[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)]
|
||||
pub struct Vertex {
|
||||
position: [f32; 2],
|
||||
color: [f32; 3],
|
||||
}
|
||||
|
||||
impl Vertex {
|
||||
pub fn new(position: [f32; 2], color: [f32; 3]) -> Vertex {
|
||||
Vertex {position, color}
|
||||
pub fn new(position: [f32; 2]) -> Vertex {
|
||||
Vertex { position }
|
||||
}
|
||||
|
||||
pub fn new_white(position: [f32; 2]) -> Vertex {
|
||||
Vertex {position, color: [1.0, 1.0, 1.0]}
|
||||
pub fn new_f64(position: [f64; 2]) -> Vertex {
|
||||
Vertex {
|
||||
position: position.map(|x| x as f32),
|
||||
}
|
||||
}
|
||||
|
||||
const ATTRIBS: [wgpu::VertexAttribute; 2] = wgpu::vertex_attr_array![0 => Float32x2, 1 => Float32x3];
|
||||
const ATTRIBS: [wgpu::VertexAttribute; 2] =
|
||||
wgpu::vertex_attr_array![0 => Float32x2, 1 => Float32x3];
|
||||
|
||||
pub const fn desc() -> wgpu::VertexBufferLayout<'static> {
|
||||
use std::mem;
|
||||
|
|
@ -26,3 +28,28 @@ impl Vertex {
|
|||
}
|
||||
}
|
||||
|
||||
pub struct RenderData {
|
||||
pub vertices: Vec<Vertex>,
|
||||
pub indices: Vec<u32>,
|
||||
}
|
||||
|
||||
impl RenderData {
|
||||
pub fn new() -> RenderData {
|
||||
RenderData {
|
||||
vertices: vec![],
|
||||
indices: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn merge(self: RenderData, other: RenderData) -> RenderData {
|
||||
let vertices_len = self.vertices.len() as u32;
|
||||
RenderData {
|
||||
vertices: self.vertices.into_iter().chain(other.vertices).collect(),
|
||||
indices: self
|
||||
.indices
|
||||
.into_iter()
|
||||
.chain(other.indices.into_iter().map(|i| i + vertices_len))
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue