aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authors-ol <s+removethis@s-ol.nu>2026-01-17 21:52:40 +0000
committers-ol <s+removethis@s-ol.nu>2026-01-17 22:18:55 +0000
commitc9cee3cce7066389657909d4a10938ec16a28645 (patch)
treef0b850be02f7c584fa9d89c5b223d1ececddf2a1 /src
parentadd examples (diff)
downloadnodetoy-c9cee3cce7066389657909d4a10938ec16a28645.tar.gz
nodetoy-c9cee3cce7066389657909d4a10938ec16a28645.zip
fix init on load, resizable preview
Diffstat (limited to 'src')
-rw-r--r--src/main.rs55
-rw-r--r--src/preview.rs109
2 files changed, 72 insertions, 92 deletions
diff --git a/src/main.rs b/src/main.rs
index e9eb1e1..0e95c19 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -159,7 +159,7 @@ impl SnarlViewer<AnyNode> for Viewer {
pub struct App {
snarl: Snarl<AnyNode>,
viewer: Viewer,
- preview: preview::Custom3d,
+ preview: preview::Preview,
}
lazy_static! {
@@ -221,7 +221,7 @@ impl App {
App {
snarl,
viewer: Viewer { dirty: true },
- preview: preview::Custom3d::new(cx).expect("Failed to init preview"),
+ preview: preview::Preview::new(cx).expect("Failed to init preview"),
}
}
}
@@ -297,28 +297,37 @@ impl eframe::App for App {
});
});
- let mut shader: Option<String> = None;
+ let shader = if self.viewer.dirty {
+ let mut buf = String::new();
+ match self.snarl.compile(&mut buf) {
+ Ok(()) => {
+ info!("{}", buf);
+ Some(buf)
+ }
+ Err(x) => {
+ error!("Error compiling: {}", x);
+ None
+ }
+ }
+ } else {
+ None
+ };
+
+ egui::SidePanel::right("preview").show(ctx, |ui| {
+ egui::Frame::canvas(ui.style()).show(ui, |ui| {
+ if self.preview.update(ui, shader) {
+ self.viewer.dirty = false;
+ }
+ });
+ });
+
egui::CentralPanel::default().show(ctx, |ui| {
- let response = SnarlWidget::new()
+ SnarlWidget::new()
.id(Id::new("snarl-demo"))
.style(default_style())
.show(&mut self.snarl, &mut self.viewer, ui);
- let _ = response.changed();
- if self.viewer.dirty {
- self.viewer.dirty = false;
- let mut buf = String::new();
- match self.snarl.compile(&mut buf) {
- Ok(()) => {
- info!("{}", buf);
- shader = Some(buf);
- }
- Err(x) => {
- error!("Error compiling: {}", x);
- }
- }
- }
-
+ // let _ = response.changed();
ui.input(|i| {
if let Some(data) = i.raw.dropped_files.first().and_then(|f| f.bytes.as_ref()) {
self.snarl = serde_json::from_slice(data).unwrap();
@@ -326,14 +335,6 @@ impl eframe::App for App {
}
});
});
-
- egui::SidePanel::right("preview").show(ctx, |ui| {
- egui::ScrollArea::vertical().show(ui, |ui| {
- egui::Frame::canvas(ui.style()).show(ui, |ui| {
- self.preview.custom_painting(ui, shader);
- });
- });
- });
}
fn save(&mut self, storage: &mut dyn eframe::Storage) {
diff --git a/src/preview.rs b/src/preview.rs
index f1d4820..5b27144 100644
--- a/src/preview.rs
+++ b/src/preview.rs
@@ -1,30 +1,20 @@
-#![expect(clippy::unwrap_used)] // TODO(emilk): avoid unwraps
-
-use std::num::NonZeroU64;
-
use eframe::{
egui_wgpu::wgpu::util::DeviceExt as _,
egui_wgpu::{self, wgpu},
};
+use std::num::NonZeroU64;
-pub struct Custom3d;
+pub struct Preview {
+ ready: bool,
+}
-impl Custom3d {
+impl Preview {
pub fn new<'a>(cc: &'a eframe::CreationContext<'a>) -> Option<Self> {
- // Get the WGPU render state from the eframe creation context. This can also be retrieved
- // from `eframe::Frame` when you don't have a `CreationContext` available.
- let wgpu_render_state = cc.wgpu_render_state.as_ref()?;
-
- // Because the graphics pipeline must have the same lifetime as the egui render pass,
- // instead of storing the pipeline in our `Custom3d` struct, we insert it into the
- // `paint_callback_resources` type map, which is stored alongside the render pass.
- let resources = &mut wgpu_render_state.renderer.write().callback_resources;
-
- let device = &wgpu_render_state.device;
- let format: wgpu::TextureFormat = wgpu_render_state.target_format;
+ let state = cc.wgpu_render_state.as_ref()?;
+ let resources = &mut state.renderer.write().callback_resources;
resources.insert(TriangleRenderResources::init_with_shader(
- device,
- format,
+ &state.device,
+ state.target_format,
"#version 450
in vec2 in_uv;
out vec4 out_color;
@@ -33,32 +23,36 @@ void main() {
out_color = vec4(1, 0, 1, 1.0);
}",
));
- resources.insert(format);
- Some(Self)
+ Some(Self { ready: false })
+ }
+
+ pub fn update(&mut self, ui: &mut egui::Ui, frag_shader: Option<String>) -> bool {
+ ui.set_min_width(150.0);
+ let size = ui.available_size().min_elem();
+ let (rect, _) = ui.allocate_exact_size(egui::Vec2::splat(size), egui::Sense::empty());
+
+ let have_shader = frag_shader.is_some();
+
+ ui.painter().add(egui_wgpu::Callback::new_paint_callback(
+ rect,
+ CustomTriangleCallback {
+ frag_shader,
+ time: ui.ctx().input(|i| i.time) as f32,
+ },
+ ));
+
+ ui.ctx().request_repaint();
+
+ if !self.ready {
+ self.ready = true;
+ false
+ } else {
+ have_shader
+ }
}
}
-// Callbacks in egui_wgpu have 3 stages:
-// * prepare (per callback impl)
-// * finish_prepare (once)
-// * paint (per callback impl)
-//
-// The prepare callback is called every frame before paint and is given access to the wgpu
-// Device and Queue, which can be used, for instance, to update buffers and uniforms before
-// rendering.
-// If [`egui_wgpu::Renderer`] has [`egui_wgpu::FinishPrepareCallback`] registered,
-// it will be called after all `prepare` callbacks have been called.
-// You can use this to update any shared resources that need to be updated once per frame
-// after all callbacks have been processed.
-//
-// On both prepare methods you can use the main `CommandEncoder` that is passed-in,
-// return an arbitrary number of user-defined `CommandBuffer`s, or both.
-// The main command buffer, as well as all user-defined ones, will be submitted together
-// to the GPU in a single call.
-//
-// The paint callback is called after finish prepare and is given access to egui's main render pass,
-// which can be used to issue draw commands.
struct CustomTriangleCallback {
frag_shader: Option<String>,
time: f32,
@@ -73,16 +67,15 @@ impl egui_wgpu::CallbackTrait for CustomTriangleCallback {
_egui_encoder: &mut wgpu::CommandEncoder,
resources: &mut egui_wgpu::CallbackResources,
) -> Vec<wgpu::CommandBuffer> {
+ let triangle_resources: &mut TriangleRenderResources = resources.get_mut().unwrap();
if let Some(code) = &self.frag_shader {
- let format: wgpu::TextureFormat = *resources.get().unwrap();
- let triangle_resources: &mut TriangleRenderResources = resources.get_mut().unwrap();
- *triangle_resources = TriangleRenderResources::init_with_shader(device, format, code);
- triangle_resources
- } else {
- let triangle_resources: &mut TriangleRenderResources = resources.get_mut().unwrap();
- triangle_resources
+ *triangle_resources = TriangleRenderResources::init_with_shader(
+ device,
+ triangle_resources.target_format,
+ code,
+ );
}
- .prepare(device, queue, screen, self.time);
+ triangle_resources.prepare(device, queue, screen, self.time);
Vec::new()
}
@@ -98,25 +91,10 @@ impl egui_wgpu::CallbackTrait for CustomTriangleCallback {
}
}
-impl Custom3d {
- pub fn custom_painting(&mut self, ui: &mut egui::Ui, frag_shader: Option<String>) {
- let (rect, _) = ui.allocate_exact_size(egui::Vec2::splat(300.0), egui::Sense::drag());
-
- ui.painter().add(egui_wgpu::Callback::new_paint_callback(
- rect,
- CustomTriangleCallback {
- frag_shader,
- time: ui.ctx().input(|i| i.time) as f32,
- },
- ));
-
- ui.ctx().request_repaint();
- }
-}
-
struct TriangleRenderResources {
pipeline: wgpu::RenderPipeline,
bind_group: wgpu::BindGroup,
+ target_format: wgpu::TextureFormat,
uniform_buffer: wgpu::Buffer,
}
@@ -225,6 +203,7 @@ void main() {
Self {
pipeline,
bind_group,
+ target_format,
uniform_buffer,
}
}