the things are changing

This commit is contained in:
TriMill 2023-06-08 20:25:06 -04:00
parent b732e1ff3d
commit 866f979c0c
12 changed files with 328 additions and 131 deletions

63
Cargo.lock generated
View file

@ -86,15 +86,6 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "approx"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f2a05fd1bd10b2527e20a2cd32d8873d115b8b39fe219ee25f42a8aca6ba278"
dependencies = [
"num-traits",
]
[[package]] [[package]]
name = "arrayref" name = "arrayref"
version = "0.3.7" version = "0.3.7"
@ -245,16 +236,6 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" 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]] [[package]]
name = "codespan-reporting" name = "codespan-reporting"
version = "0.11.1" version = "0.11.1"
@ -291,12 +272,6 @@ dependencies = [
"web-sys", "web-sys",
] ]
[[package]]
name = "const_panic"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6051f239ecec86fde3410901ab7860d458d160371533842974fc61f96d15879b"
[[package]] [[package]]
name = "core-foundation" name = "core-foundation"
version = "0.9.3" version = "0.9.3"
@ -452,38 +427,6 @@ dependencies = [
"log", "log",
] ]
[[package]]
name = "encase"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8fce2eeef77fd4a293a54b62aa00ac9daebfbcda4bf8998c5a815635b004aa1c"
dependencies = [
"cgmath",
"const_panic",
"encase_derive",
"thiserror",
]
[[package]]
name = "encase_derive"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0e520cde08cbf4f7cc097f61573ec06ce467019803de8ae82fb2823fa1554a0e"
dependencies = [
"encase_derive_impl",
]
[[package]]
name = "encase_derive_impl"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fe2568f851fd6144a45fa91cfed8fe5ca8fc0b56ba6797bfc1ed2771b90e37c"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.18",
]
[[package]] [[package]]
name = "env_logger" name = "env_logger"
version = "0.10.0" version = "0.10.0"
@ -818,8 +761,6 @@ checksum = "f92be4933c13fd498862a9e02a3055f8a8d9c039ce33db97306fd5a6caa7f29b"
name = "libcxgraph" name = "libcxgraph"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"cgmath",
"encase",
"lalrpop", "lalrpop",
"lalrpop-util", "lalrpop-util",
"log", "log",
@ -1258,9 +1199,9 @@ dependencies = [
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.59" version = "1.0.60"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6aeca18b86b413c660b781aa319e4e2648a3e6f9eadc9b47e9038e6fe9f3451b" checksum = "dec2b086b7a862cf4de201096214fa870344cf922b2b30c167badb3af3195406"
dependencies = [ dependencies = [
"unicode-ident", "unicode-ident",
] ]

View file

@ -1,4 +1,6 @@
use std::collections::HashMap;
use libcxgraph::{renderer::WgpuState, language::compile}; use libcxgraph::{renderer::WgpuState, language::compile};
use winit::{event_loop::EventLoop, window::Window, event::{Event, WindowEvent}}; use winit::{event_loop::EventLoop, window::Window, event::{Event, WindowEvent}};
@ -8,7 +10,7 @@ fn main() {
.init(); .init();
let src = "plot(z) = 27^z - 9^z - 3^z"; let src = "plot(z) = 27^z - 9^z - 3^z";
let wgsl = compile(src).unwrap(); let wgsl = compile(src, &HashMap::new()).unwrap();
println!("{wgsl}"); println!("{wgsl}");
let event_loop = EventLoop::new(); let event_loop = EventLoop::new();
@ -24,8 +26,9 @@ async fn run(event_loop: EventLoop<()>, window: Window, code: &str) {
state.load_shaders(code); state.load_shaders(code);
state.set_bounds((-5.0, -5.0), (5.0, 5.0)); state.uniforms.bounds_min = (-5.0, -5.0).into();
state.set_shading_intensity(0.05); state.uniforms.bounds_max = ( 5.0, 5.0).into();
state.uniforms.shading_intensity = 0.3;
event_loop.run(move |event, _, control_flow| { event_loop.run(move |event, _, control_flow| {
control_flow.set_wait(); control_flow.set_wait();

View file

@ -10,14 +10,65 @@
<canvas id="grid_canvas"></canvas> <canvas id="grid_canvas"></canvas>
</div> </div>
<div class="menu"> <div class="menu"><details open>
<input id="menu_checkbox" type="checkbox" checked> <summary>Menu</summary>
<div class="menu-inner" id="menu_inner">
<div><input type="button" id="button_graph" value="Graph"></div> <div><input type="button" id="button_graph" value="Graph"></div>
<textarea id="source_text">f(z) = z^2 + 3i&#10;plot(z) = 5z^2 + f(1/z) - 1</textarea>
<details>
<summary>Options</summary>
<div>
<div><label for="range_resolution">Resolution</label></div>
<input type="range" id="range_resolution" name="resolution" min="-2" max="2" step="1" value="0">
</div>
<div>
<div><label for="range_shading">Shading intensity</label></div>
<input type="range" id="range_shading" min="0" max="1" step="0.01" value="0.3">
</div>
<div>
<div><label for="range_contour">Contour intensity</label></div>
<input type="range" id="range_contour" min="0" max="1" step="0.01" value="0.0">
</div>
<div>
<div>
<input type="checkbox" class="decor" id="checkbox_decor_1" data-value="1">
<label for="checkbox_decor_1">Real contours</label>
</div>
<div>
<input type="checkbox" class="decor" id="checkbox_decor_2" data-value="2">
<label for="checkbox_decor_2">Imaginary contours</label>
</div>
<div>
<input type="checkbox" class="decor" id="checkbox_decor_4" data-value="4">
<label for="checkbox_decor_4">Argument contours</label>
</div>
<div>
<input type="checkbox" class="decor" id="checkbox_decor_8" data-value="8">
<label for="checkbox_decor_8">Magnitude contours</label>
</div> </div>
</div> </div>
<div>
<div>Coloring</div>
<input type="radio" name="color_mode" id="radio_color_0" data-value="0" checked>
<label for="radio_color_0">Standard</label><br>
<input type="radio" name="color_mode" id="radio_color_1" data-value="1">
<label for="radio_color_1">Uniform</label><br>
<input type="radio" name="color_mode" id="radio_color_2" data-value="2">
<label for="radio_color_2">None</label>
</div>
</details>
<div id="div_error_msg" hidden></div>
<div><textarea id="source_text">f(z) = z^2 + 3i&#10;plot(z) = 5z^2 + f(1/z) - 1</textarea></div>
</div></div>
<script> <script>
import('./index.js').then(m => window.module = m) import('./index.js').then(m => window.module = m)
</script> </script>

View file

@ -1,7 +1,3 @@
menu_checkbox.addEventListener("change", () => {
menu_inner.hidden = !menu_checkbox.checked;
});
import init, * as cxgraph from "./pkg/cxgraph_web.js"; import init, * as cxgraph from "./pkg/cxgraph_web.js";
await init(); await init();
@ -9,6 +5,7 @@ let graphView = {
xoff: 0, xoff: 0,
yoff: 0, yoff: 0,
scale: 3, scale: 3,
res_mult: 1,
}; };
function redraw() { function redraw() {
@ -31,7 +28,7 @@ function onViewChange() {
function onResize() { function onResize() {
let width = window.innerWidth; let width = window.innerWidth;
let height = window.innerHeight; let height = window.innerHeight;
cxgraph.resize(width, height); cxgraph.resize(width*graphView.res_mult, height*graphView.res_mult);
grid_canvas.width = width; grid_canvas.width = width;
grid_canvas.height = height; grid_canvas.height = height;
canvas.style.width = "100vw"; canvas.style.width = "100vw";
@ -72,8 +69,15 @@ function onMouseMove(e) {
function onGraph() { function onGraph() {
let src = document.getElementById("source_text").value; let src = document.getElementById("source_text").value;
try {
cxgraph.load_shader(src); cxgraph.load_shader(src);
div_error_msg.hidden = true;
redraw(); redraw();
} catch(e) {
console.log(e);
div_error_msg.textContent = e.toString();
div_error_msg.hidden = false;
}
} }
window.addEventListener("resize", onResize); window.addEventListener("resize", onResize);
@ -83,6 +87,50 @@ canvas.addEventListener("mouseup", onMouseUp);
canvas.addEventListener("mousemove", onMouseMove); canvas.addEventListener("mousemove", onMouseMove);
button_graph.addEventListener("click", onGraph); button_graph.addEventListener("click", onGraph);
let class_decor = document.getElementsByClassName("decor")
for(let e of class_decor) {
e.addEventListener("change", () => {
let decor = 0;
for(let elem of class_decor) {
if(elem.checked) {
decor += parseInt(elem.getAttribute("data-value"));
}
}
cxgraph.set_decorations(decor);
redraw();
});
e.checked = false;
}
let name_color_mode = document.getElementsByName("color_mode");
for(let e of name_color_mode) {
e.addEventListener("change", () => {
let selected = document.querySelector("input[name=color_mode]:checked");
cxgraph.set_coloring(parseInt(selected.getAttribute("data-value")));
redraw();
});
e.checked = false;
}
name_color_mode[0].checked = true;
range_shading.addEventListener("change", () => {
let value = parseFloat(range_shading.value);
cxgraph.set_shading_intensity(value);
redraw();
});
range_contour.addEventListener("change", () => {
let value = parseFloat(range_contour.value);
cxgraph.set_contour_intensity(value);
redraw();
});
range_resolution.addEventListener("change", () => {
graphView.res_mult = Math.pow(2, parseFloat(range_resolution.value));
onResize();
});
onResize(); onResize();
onGraph(); onGraph();

View file

@ -1,3 +1,5 @@
use std::collections::HashMap;
use libcxgraph::{renderer::WgpuState, language::compile}; use libcxgraph::{renderer::WgpuState, language::compile};
use winit::{window::WindowBuilder, event_loop::EventLoop, platform::web::WindowBuilderExtWebSys}; use winit::{window::WindowBuilder, event_loop::EventLoop, platform::web::WindowBuilderExtWebSys};
use wasm_bindgen::{prelude::*, JsValue}; use wasm_bindgen::{prelude::*, JsValue};
@ -41,14 +43,16 @@ pub async fn start() {
let size = window.inner_size(); let size = window.inner_size();
let mut state = WgpuState::new(&window, size.into()).await; let mut state = WgpuState::new(&window, size.into()).await;
state.set_bounds((-5.0, -5.0), (5.0, 5.0)); state.uniforms.bounds_min = (-5.0, -5.0).into();
state.set_shading_intensity(0.01); state.uniforms.bounds_max = ( 5.0, 5.0).into();
state.uniforms.shading_intensity = 0.3;
state.uniforms.contour_intensity = 0.0;
unsafe { WGPU_STATE = Some(state) }; unsafe { WGPU_STATE = Some(state) };
} }
#[wasm_bindgen] #[wasm_bindgen]
pub fn load_shader(src: &str) -> Result<(), JsValue> { pub fn load_shader(src: &str) -> Result<(), JsValue> {
let wgsl = compile(src).map_err(|e| e.to_string())?; let wgsl = compile(src, &HashMap::new()).map_err(|e| e.to_string())?;
with_state(|state| state.load_shaders(&wgsl)); with_state(|state| state.load_shaders(&wgsl));
Ok(()) Ok(())
} }
@ -65,10 +69,29 @@ pub fn resize(width: u32, height: u32) {
#[wasm_bindgen] #[wasm_bindgen]
pub fn set_bounds(min_x: f32, min_y: f32, max_x: f32, max_y: f32) { pub fn set_bounds(min_x: f32, min_y: f32, max_x: f32, max_y: f32) {
with_state(|state| state.set_bounds((min_x, min_y), (max_x, max_y))); with_state(|state| {
state.uniforms.bounds_min = (min_x, min_y).into();
state.uniforms.bounds_max = (max_x, max_y).into();
});
} }
#[wasm_bindgen] #[wasm_bindgen]
pub fn set_shading_intensity(value: f32) { pub fn set_shading_intensity(value: f32) {
with_state(|state| state.set_shading_intensity(value)); with_state(|state| state.uniforms.shading_intensity = value);
}
#[wasm_bindgen]
pub fn set_contour_intensity(value: f32) {
with_state(|state| state.uniforms.contour_intensity = value);
}
#[wasm_bindgen]
pub fn set_coloring(value: u32) {
with_state(|state| state.uniforms.coloring = value);
}
#[wasm_bindgen]
pub fn set_decorations(value: u32) {
with_state(|state| state.uniforms.decorations = value);
} }

View file

@ -1,3 +1,7 @@
* {
font-family: monospace;
}
body, html { body, html {
height: 100%; height: 100%;
margin: 0; margin: 0;
@ -39,8 +43,23 @@ canvas {
z-index: 10; z-index: 10;
} }
#source_text { #source_text {
min-width: max-content;
width: 300px; width: 300px;
height: 500px; height: 500px;
} }
#div_error_msg {
color: #f9a;
}
div.menu > details {
max-width: min-content;
}
details > *:not(summary) {
padding-top: 2px;
padding-bottom: 2px;
padding-left: 8px;
border-left: 1px solid #fff;
}

View file

@ -12,8 +12,6 @@ log = "0.4"
lalrpop-util = { version = "0.20.0", features = ["lexer", "unicode"] } lalrpop-util = { version = "0.20.0", features = ["lexer", "unicode"] }
num-complex = "0.4" num-complex = "0.4"
wgpu = "0.16" wgpu = "0.16"
cgmath = "0.18"
encase = { version = "0.6", features = ["cgmath"] }
raw-window-handle = "0.5" raw-window-handle = "0.5"
[build-dependencies] [build-dependencies]

View file

@ -1,6 +1,6 @@
use std::{collections::{HashSet, HashMap}, fmt}; use std::{collections::{HashSet, HashMap}, fmt};
use super::{ast::{Definition, Expression, ExpressionType, BinaryOp, UnaryOp}, builtins::{BUILTIN_CONSTS, BUILTIN_FUNCS}}; use super::{ast::{Definition, Expression, ExpressionType, BinaryOp, UnaryOp}, builtins::{BUILTIN_CONSTS, BUILTIN_FUNCS}, Variable};
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct CompileError(String); pub struct CompileError(String);
@ -83,6 +83,7 @@ fn format_tmp(idx: usize) -> String { format!("tmp_{}", idx) }
pub struct Compiler<'w, 'i, W: fmt::Write> { pub struct Compiler<'w, 'i, W: fmt::Write> {
buf: &'w mut W, buf: &'w mut W,
vars: &'w HashMap<String, Variable>,
global_funcs: HashMap<&'i str, usize>, global_funcs: HashMap<&'i str, usize>,
global_consts: HashSet<&'i str>, global_consts: HashSet<&'i str>,
} }
@ -109,9 +110,10 @@ impl<'i> LocalState<'i> {
} }
impl<'w, 'i, W: fmt::Write> Compiler<'w, 'i, W> { impl<'w, 'i, W: fmt::Write> Compiler<'w, 'i, W> {
pub fn new(buf: &'w mut W) -> Self { pub fn new(buf: &'w mut W, vars: &'w HashMap<String, Variable>) -> Self {
Self { Self {
buf, buf,
vars,
global_consts: HashSet::new(), global_consts: HashSet::new(),
global_funcs: HashMap::new(), global_funcs: HashMap::new(),
} }

View file

@ -1,3 +1,5 @@
use std::collections::HashMap;
use lalrpop_util::lalrpop_mod; use lalrpop_util::lalrpop_mod;
use crate::language::token::Lexer; use crate::language::token::Lexer;
@ -11,13 +13,18 @@ mod builtins;
lalrpop_mod!(pub syntax, "/language/syntax.rs"); lalrpop_mod!(pub syntax, "/language/syntax.rs");
pub fn compile(src: &str) -> Result<String, Box<dyn std::error::Error>> { pub enum Variable {
Slider(usize),
Point(usize, usize),
}
pub fn compile(src: &str, vars: &HashMap<String, Variable>) -> Result<String, Box<dyn std::error::Error>> {
let lexer = Lexer::new(src); let lexer = Lexer::new(src);
let result = syntax::ProgramParser::new() let result = syntax::ProgramParser::new()
.parse(src, lexer) .parse(src, lexer)
.map_err(|e| e.to_string())?; .map_err(|e| e.to_string())?;
let mut wgsl = String::new(); let mut wgsl = String::new();
let mut cmp = Compiler::new(&mut wgsl); let mut cmp = Compiler::new(&mut wgsl, vars);
for defn in result { for defn in result {
cmp.compile_defn(&defn)?; cmp.compile_defn(&defn)?;
} }

View file

@ -54,7 +54,7 @@ Definition: Definition<'input> = {
// Expressions // Expressions
Exprs: Vec<Expression<'input>> = { Exprs: Vec<Expression<'input>> = {
<args:(<Expr> ",")*> <last:Expr?> => args.into_iter().chain(last).collect(), <args:(<Expr> ",")*> <last:Expr> ","? => args.into_iter().chain(std::iter::once(last)).collect(),
} }
Expr: Expression<'input> = Store; Expr: Expression<'input> = Store;

View file

@ -3,10 +3,14 @@
//////////////// ////////////////
struct Uniforms { struct Uniforms {
variables: array<vec4f, 4>,
resolution: vec2u, resolution: vec2u,
bounds_min: vec2f, bounds_min: vec2f,
bounds_max: vec2f, bounds_max: vec2f,
shading_intensity: f32, shading_intensity: f32,
contour_intensity: f32,
decoration: u32,
coloring: u32,
} }
@group(0) @binding(1) var<uniform> uniforms: Uniforms; @group(0) @binding(1) var<uniform> uniforms: Uniforms;
@ -19,12 +23,21 @@ fn remap(val: vec2f, a1: vec2f, b1: vec2f, a2: vec2f, b2: vec2f) -> vec2f {
return a2 + (b2 - a2) * ((val - a1) / (b1 - a1)); return a2 + (b2 - a2) * ((val - a1) / (b1 - a1));
} }
fn correct_mod(x: f32, y: f32) -> f32 {
return ((x % y) + y) % y;
}
fn correct_mod2(x: vec2f, y: vec2f) -> vec2f {
return ((x % y) + y) % y;
}
///////////////// /////////////////
// constants // // constants //
///////////////// /////////////////
const TAU = 6.283185307179586; const TAU = 6.283185307179586;
const E = 2.718281828459045; const E = 2.718281828459045;
const RECIP_SQRT2 = 0.7071067811865475;
const C_TAU = vec2f(TAU, 0.0); const C_TAU = vec2f(TAU, 0.0);
const C_E = vec2f(E, 0.0); const C_E = vec2f(E, 0.0);
@ -51,7 +64,7 @@ fn c_abs_sq(z: vec2f) -> vec2f {
} }
fn c_abs(z: vec2f) -> vec2f { fn c_abs(z: vec2f) -> vec2f {
return vec2(sqrt(dot(z, z)), 0.0); return vec2(length(z), 0.0);
} }
fn c_arg(z: vec2f) -> vec2f { fn c_arg(z: vec2f) -> vec2f {
@ -202,28 +215,99 @@ fn hsv2rgb(c: vec3f) -> vec3f {
} }
fn shademap(r: f32) -> f32 { fn shademap(r: f32) -> f32 {
return r*inverseSqrt(r * r + 0.0625 * uniforms.shading_intensity); let i = uniforms.shading_intensity * uniforms.shading_intensity * uniforms.shading_intensity;
return r*inverseSqrt(r * r + 0.0625 * i);
} }
fn colorfor(w: vec2f) -> vec3f { fn coloring_standard(z: vec2f) -> vec3f {
let z = func_plot(w);
if z.x != z.x || z.y != z.y { if z.x != z.x || z.y != z.y {
return vec3f(0.5, 0.5, 0.5); return vec3f(0.5, 0.5, 0.5);
} }
let r = sqrt(z.x*z.x + z.y*z.y); let r = length(z);
let arg = atan2(z.y, z.x); let arg = atan2(z.y, z.x);
let hsv = vec3f(arg / TAU + 1.0, shademap(1.0/r), shademap(r)); let hsv = vec3f(arg / TAU + 1.0, shademap(1.0/r), shademap(r));
return pow(hsv2rgb(hsv), vec3(2.0)); return hsv2rgb(hsv);
}
fn coloring_uniform(z: vec2f) -> vec3f {
if z.x != z.x || z.y != z.y {
return vec3f(0.5, 0.5, 0.5);
}
let arg = atan2(z.y, z.x);
let mag = length(z);
let r = cos(arg - 0.0*TAU/3.0)*0.5 + 0.5;
let g = cos(arg - 1.0*TAU/3.0)*0.5 + 0.5;
let b = cos(arg - 2.0*TAU/3.0)*0.5 + 0.5;
let hue = vec3(r, g, b);
let s = 1.0 - shademap(1.0/mag);
let v = 1.0 - shademap(mag);
return hue * (1.0 - s - v) + s;
}
fn coloring_none(z: vec2f) -> vec3f {
return vec3f(0.5, 0.5, 0.5);
}
fn decoration_contour_re(z: vec2f) -> f32 {
return correct_mod(floor(z.x), 2.0) * 2.0 - 1.0;
}
fn decoration_contour_im(z: vec2f) -> f32 {
return correct_mod(floor(z.y), 2.0) * 2.0 - 1.0;
}
fn decoration_contour_arg(z: vec2f) -> f32 {
let arg = atan2(z.y, z.x);
return round(correct_mod(arg + TAU, TAU/8.0) * 8.0/TAU) * 2.0 - 1.0;
}
fn decoration_contour_mag(z: vec2f) -> f32 {
let logmag = 0.5 * log2(z.x*z.x + z.y*z.y);
return round(correct_mod(0.5 * logmag, 1.0)) * 2.0 - 1.0;
} }
@fragment @fragment
fn main(@builtin(position) in: vec4f) -> @location(0) vec4f { fn main(@builtin(position) in: vec4f) -> @location(0) vec4f {
let pos = vec2(in.x, f32(uniforms.resolution.y) - in.y); let pos = vec2(in.x, f32(uniforms.resolution.y) - in.y);
let z = remap(pos, vec2(0.0), vec2f(uniforms.resolution), uniforms.bounds_min, uniforms.bounds_max); let w = remap(pos, vec2(0.0), vec2f(uniforms.resolution), uniforms.bounds_min, uniforms.bounds_max);
let col = colorfor(z); let z = func_plot(w);
return vec4f(col, 1.0); var col = vec3f();
switch uniforms.coloring {
case 0u, default: {
col = coloring_standard(z);
}
case 1u: {
col = coloring_uniform(z);
}
case 2u: {
col = coloring_none(z);
}
}
var contours = 1.0;
if (uniforms.decoration & 0x01u) != 0u {
contours *= decoration_contour_re(z);
}
if (uniforms.decoration & 0x02u) != 0u {
contours *= decoration_contour_im(z);
}
if (uniforms.decoration & 0x04u) != 0u {
contours *= decoration_contour_arg(z);
}
if (uniforms.decoration & 0x08u) != 0u {
contours *= decoration_contour_mag(z);
}
let final_col = mix(col, vec3f(contours * 0.5 + 0.5), uniforms.contour_intensity);
return vec4f(pow(final_col, vec3(1.68)), 1.0);
} }

View file

@ -1,22 +1,45 @@
use std::num::NonZeroU64; use std::{num::NonZeroU64, io::Cursor};
use encase::{ShaderType, ShaderSize, UniformBuffer}; use log::info;
use log::warn;
use wgpu::util::DeviceExt; use wgpu::util::DeviceExt;
type Vec2u = cgmath::Vector2<u32>; #[derive(Debug)]
type Vec2f = cgmath::Vector2<f32>; #[repr(C)]
pub struct Uniforms {
#[derive(ShaderType)] pub variables: [f32; 16],
struct Uniforms { pub resolution: (u32, u32),
pub resolution: Vec2u, pub bounds_min: (f32, f32),
pub bounds_min: Vec2f, pub bounds_max: (f32, f32),
pub bounds_max: Vec2f,
pub shading_intensity: f32, pub shading_intensity: f32,
pub contour_intensity: f32,
pub decorations: u32,
pub coloring: u32,
_padding: [u8; 8],
}
const UNIFORM_SIZE: usize = std::mem::size_of::<Uniforms>();
impl Uniforms {
pub fn encode(&self, buf: &mut impl std::io::Write) -> Result<(), std::io::Error> {
for var in self.variables {
buf.write_all(&var.to_le_bytes())?;
}
buf.write_all(&self.resolution.0.to_le_bytes())?;
buf.write_all(&self.resolution.1.to_le_bytes())?;
buf.write_all(&self.bounds_min.0.to_le_bytes())?;
buf.write_all(&self.bounds_min.1.to_le_bytes())?;
buf.write_all(&self.bounds_max.0.to_le_bytes())?;
buf.write_all(&self.bounds_max.1.to_le_bytes())?;
buf.write_all(&self.shading_intensity.to_le_bytes())?;
buf.write_all(&self.contour_intensity.to_le_bytes())?;
buf.write_all(&self.decorations.to_le_bytes())?;
buf.write_all(&self.coloring.to_le_bytes())?;
Ok(())
}
} }
pub struct WgpuState { pub struct WgpuState {
uniforms: Uniforms, pub uniforms: Uniforms,
surface: wgpu::Surface, surface: wgpu::Surface,
device: wgpu::Device, device: wgpu::Device,
config: wgpu::SurfaceConfiguration, config: wgpu::SurfaceConfiguration,
@ -67,21 +90,21 @@ impl WgpuState {
let uniform_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { let uniform_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: None, label: None,
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST, usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
contents: &[0; Uniforms::SHADER_SIZE.get() as usize], contents: &[0; UNIFORM_SIZE],
}); });
let uniform_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { let uniform_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
label: None, label: None,
entries: &[ entries: &[
wgpu::BindGroupLayoutEntry { wgpu::BindGroupLayoutEntry {
visibility: wgpu::ShaderStages::VERTEX_FRAGMENT, binding: 1,
visibility: wgpu::ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Buffer { ty: wgpu::BindingType::Buffer {
ty: wgpu::BufferBindingType::Uniform, ty: wgpu::BufferBindingType::Uniform,
has_dynamic_offset: false, has_dynamic_offset: false,
min_binding_size: None, min_binding_size: None,
}, },
count: None, count: None,
binding: 1,
}, },
] ]
}); });
@ -95,7 +118,7 @@ impl WgpuState {
resource: wgpu::BindingResource::Buffer(wgpu::BufferBinding { resource: wgpu::BindingResource::Buffer(wgpu::BufferBinding {
buffer: &uniform_buffer, buffer: &uniform_buffer,
offset: 0, offset: 0,
size: Some(NonZeroU64::new(Uniforms::SHADER_SIZE.get()).unwrap()), size: Some(NonZeroU64::new(UNIFORM_SIZE as u64).unwrap()),
}), }),
}, },
], ],
@ -104,10 +127,15 @@ impl WgpuState {
// Done // // Done //
let uniforms = Uniforms { let uniforms = Uniforms {
variables: [0.0; 16],
resolution: size.into(), resolution: size.into(),
bounds_min: [-0.0, -0.0].into(), bounds_min: (-0.0, -0.0),
bounds_max: [ 0.0, 0.0].into(), bounds_max: ( 0.0, 0.0),
shading_intensity: 0.0, shading_intensity: 0.0,
contour_intensity: 0.0,
decorations: 0,
coloring: 0,
_padding: [0; 8],
}; };
Self { Self {
@ -205,9 +233,11 @@ impl WgpuState {
rpass.draw(1..4, 0..1); rpass.draw(1..4, 0..1);
} }
} }
let mut uniform_buffer = UniformBuffer::new([0; Uniforms::SHADER_SIZE.get() as usize]); info!("Redrawing");
uniform_buffer.write(&self.uniforms).unwrap(); info!("Uniforms: {:?}", self.uniforms);
self.queue.write_buffer(&self.uniform_buffer, 0, &uniform_buffer.into_inner()); let mut cursor = Cursor::new([0; UNIFORM_SIZE]);
self.uniforms.encode(&mut cursor).unwrap();
self.queue.write_buffer(&self.uniform_buffer, 0, &cursor.into_inner());
self.queue.submit(Some(encoder.finish())); self.queue.submit(Some(encoder.finish()));
frame.present(); frame.present();
} }
@ -219,13 +249,4 @@ impl WgpuState {
self.surface.configure(&self.device, &self.config); self.surface.configure(&self.device, &self.config);
self.uniforms.resolution = size.into(); self.uniforms.resolution = size.into();
} }
pub fn set_bounds(&mut self, min: (f32, f32), max: (f32, f32)) {
self.uniforms.bounds_min = min.into();
self.uniforms.bounds_max = max.into();
}
pub fn set_shading_intensity(&mut self, value: f32) {
self.uniforms.shading_intensity = value;
}
} }