Fix requiring viewport to be visible to render preview image

This commit is contained in:
Connor Slade
2025-02-15 18:26:57 -05:00
parent f5862d1efc
commit de034afefa
7 changed files with 86 additions and 57 deletions

View File

@@ -91,6 +91,7 @@
- [x] Cleanup self intersection resolution - [x] Cleanup self intersection resolution
- [ ] Dont fail to load an stl without normals - [ ] Dont fail to load an stl without normals
- [ ] GPU accalration for post processing effect? - [ ] GPU accalration for post processing effect?
- [ ] Fix requiring viewport to be visible to render preview image - [x] Fix requiring viewport to be visible to render preview image
- [x] Visulize mesh outside of bounging box - [x] Visulize mesh outside of bounging box
- [ ] Save panel layout - [ ] Save panel layout
- [ ] Random triangle color render mode

View File

@@ -13,6 +13,7 @@ use egui::{Vec2, Visuals};
use egui_dock::{DockState, NodeIndex}; use egui_dock::{DockState, NodeIndex};
use egui_phosphor::regular::CARET_RIGHT; use egui_phosphor::regular::CARET_RIGHT;
use egui_tracing::EventCollector; use egui_tracing::EventCollector;
use egui_wgpu::RenderState;
use nalgebra::Vector2; use nalgebra::Vector2;
use parking_lot::RwLock; use parking_lot::RwLock;
use tracing::{info, warn}; use tracing::{info, warn};
@@ -23,7 +24,7 @@ use crate::{
elephant_foot_fixer::{self}, elephant_foot_fixer::{self},
PluginManager, PluginManager,
}, },
render::{camera::Camera, rendered_mesh::RenderedMesh}, render::{camera::Camera, preview, rendered_mesh::RenderedMesh},
ui::{ ui::{
drag_and_drop, drag_and_drop,
popup::{Popup, PopupIcon, PopupManager}, popup::{Popup, PopupIcon, PopupManager},
@@ -43,6 +44,7 @@ use remote_print::RemotePrint;
use slice_operation::{SliceOperation, SliceResult}; use slice_operation::{SliceOperation, SliceResult};
pub struct App { pub struct App {
pub render_state: RenderState,
// todo: dock state in ui_state? // todo: dock state in ui_state?
pub dock_state: DockState<Tab>, pub dock_state: DockState<Tab>,
pub fps: FpsTracker, pub fps: FpsTracker,
@@ -66,7 +68,12 @@ pub struct FpsTracker {
} }
impl App { impl App {
pub fn new(config_dir: PathBuf, config: Config, event_collector: EventCollector) -> Self { pub fn new(
render_state: RenderState,
config_dir: PathBuf,
config: Config,
event_collector: EventCollector,
) -> Self {
let mut dock_state = DockState::new(vec![Tab::Viewport]); let mut dock_state = DockState::new(vec![Tab::Viewport]);
let surface = dock_state.main_surface_mut(); let surface = dock_state.main_surface_mut();
let [_old_node, new_node] = surface.split_left(NodeIndex::root(), 0.20, vec![Tab::Models]); let [_old_node, new_node] = surface.split_left(NodeIndex::root(), 0.20, vec![Tab::Models]);
@@ -75,6 +82,7 @@ impl App {
surface.split_below(new_node, 0.5, vec![Tab::Workspace, Tab::RemotePrint]); surface.split_below(new_node, 0.5, vec![Tab::Workspace, Tab::RemotePrint]);
Self { Self {
render_state,
dock_state, dock_state,
popup: PopupManager::new(), popup: PopupManager::new(),
state: UiState { state: UiState {
@@ -218,8 +226,8 @@ impl eframe::App for App {
// todo: probably dont do this // todo: probably dont do this
let app = unsafe { &mut *(self as *mut _) }; let app = unsafe { &mut *(self as *mut _) };
self.popup.render(app, ctx); self.popup.render(app, ctx);
// only update the visuals if the theme has changed
// only update the visuals if the theme has changed
match self.config.theme { match self.config.theme {
Theme::Dark => ctx.set_visuals(Visuals::dark()), Theme::Dark => ctx.set_visuals(Visuals::dark()),
Theme::Light => ctx.set_visuals(Visuals::light()), Theme::Light => ctx.set_visuals(Visuals::light()),
@@ -230,6 +238,7 @@ impl eframe::App for App {
} }
self.remote_print.tick(app); self.remote_print.tick(app);
preview::process_previews(app);
drag_and_drop::update(self, ctx); drag_and_drop::update(self, ctx);
windows::ui(self, ctx); windows::ui(self, ctx);
} }

View File

@@ -68,13 +68,13 @@ fn main() -> Result<()> {
..Default::default() ..Default::default()
}, },
Box::new(|cc| { Box::new(|cc| {
render::init_wgpu(cc); let render_state = render::init_wgpu(cc);
let mut fonts = FontDefinitions::default(); let mut fonts = FontDefinitions::default();
egui_phosphor::add_to_fonts(&mut fonts, egui_phosphor::Variant::Regular); egui_phosphor::add_to_fonts(&mut fonts, egui_phosphor::Variant::Regular);
cc.egui_ctx.set_fonts(fonts); cc.egui_ctx.set_fonts(fonts);
Box::new(App::new(config_dir, config, collector)) Box::new(App::new(render_state, config_dir, config, collector))
}), }),
) )
.unwrap(); .unwrap();

View File

@@ -2,6 +2,7 @@ use std::mem;
use dispatch::solid_line::SolidLineDispatch; use dispatch::solid_line::SolidLineDispatch;
use eframe::CreationContext; use eframe::CreationContext;
use egui_wgpu::RenderState;
use nalgebra::Vector4; use nalgebra::Vector4;
use pipelines::{ use pipelines::{
model::ModelPipeline, slice_preview::SlicePreviewPipeline, target_point::TargetPointPipeline, model::ModelPipeline, slice_preview::SlicePreviewPipeline, target_point::TargetPointPipeline,
@@ -34,7 +35,7 @@ pub struct ModelVertex {
pub position: [f32; 4], pub position: [f32; 4],
} }
pub fn init_wgpu(cc: &CreationContext) { pub fn init_wgpu(cc: &CreationContext) -> RenderState {
let render_state = cc.wgpu_render_state.as_ref().unwrap(); let render_state = cc.wgpu_render_state.as_ref().unwrap();
let device = &render_state.device; let device = &render_state.device;
@@ -47,6 +48,8 @@ pub fn init_wgpu(cc: &CreationContext) {
resources.insert(SlicePreviewRenderResources { resources.insert(SlicePreviewRenderResources {
slice_preview_pipeline: SlicePreviewPipeline::new(device), slice_preview_pipeline: SlicePreviewPipeline::new(device),
}); });
render_state.clone()
} }
impl ModelVertex { impl ModelVertex {

View File

@@ -1,7 +1,7 @@
use std::f32::consts::PI; use std::f32::consts::PI;
use image::{Rgba, RgbaImage}; use image::{Rgba, RgbaImage};
use nalgebra::{Vector2, Vector3}; use nalgebra::{Matrix4, Vector2, Vector3};
use tracing::info; use tracing::info;
use wgpu::{ use wgpu::{
BufferAddress, BufferDescriptor, BufferUsages, Color, CommandEncoderDescriptor, Device, BufferAddress, BufferDescriptor, BufferUsages, Color, CommandEncoderDescriptor, Device,
@@ -11,20 +11,33 @@ use wgpu::{
TextureFormat, TextureUsages, TextureView, TextureViewDescriptor, TextureFormat, TextureUsages, TextureView, TextureViewDescriptor,
}; };
use crate::TEXTURE_FORMAT; use crate::{app::App, TEXTURE_FORMAT};
use super::{camera::Camera, pipelines::model::ModelPipeline, workspace::WorkspaceRenderCallback}; use super::{
camera::Camera,
pipelines::model::ModelPipeline,
workspace::{WorkspaceRenderCallback, WorkspaceRenderResources},
};
pub fn process_previews(app: &App) {
match &app.slice_operation {
Some(slice_operation) if slice_operation.needs_preview_image() => {
let image = render_preview_image(app, (512, 512));
slice_operation.add_preview_image(image);
}
_ => {}
}
}
// TODO: Allow rendering multiple preview images at once // TODO: Allow rendering multiple preview images at once
pub fn render_preview_image( fn render_preview_image(app: &App, size: (u32, u32)) -> RgbaImage {
device: &Device,
queue: &Queue,
size: (u32, u32),
model_pipeline: &mut ModelPipeline,
workspace: &WorkspaceRenderCallback,
) -> RgbaImage {
info!("Generating {}x{} preview image", size.0, size.1); info!("Generating {}x{} preview image", size.0, size.1);
let mut resources = app.get_callback_resource_mut::<WorkspaceRenderResources>();
let (device, queue) = (&app.render_state.device, &app.render_state.queue);
let mut workspace = app.get_workspace_render_callback(Matrix4::zeros(), false);
let (texture, resolved_texture, depth_texture) = init_textures(device, size); let (texture, resolved_texture, depth_texture) = init_textures(device, size);
let texture_view = texture.create_view(&TextureViewDescriptor::default()); let texture_view = texture.create_view(&TextureViewDescriptor::default());
let resolved_texture_view = resolved_texture.create_view(&TextureViewDescriptor::default()); let resolved_texture_view = resolved_texture.create_view(&TextureViewDescriptor::default());
@@ -40,26 +53,22 @@ pub fn render_preview_image(
let target = (min + max) / 2.0; let target = (min + max) / 2.0;
let distance = (min - max).magnitude() / 2.0; let distance = (min - max).magnitude() / 2.0;
let mut old_workspace = workspace.clone(); workspace.camera = Camera {
old_workspace.camera = Camera {
target, target,
distance, distance,
angle: Vector2::new(0.0, PI / 10.0), angle: Vector2::new(0.0, PI / 10.0),
..old_workspace.camera ..workspace.camera
}; };
let workspace = WorkspaceRenderCallback { let aspect = size.0 as f32 / size.1 as f32;
transform: old_workspace workspace.transform = workspace.camera.view_projection_matrix(aspect);
.camera
.view_projection_matrix(size.0 as f32 / size.1 as f32), resources.model_pipeline.prepare(device, &workspace);
..old_workspace
};
model_pipeline.prepare(device, &workspace);
render_preview( render_preview(
device, device,
queue, queue,
model_pipeline, &resources.model_pipeline,
&workspace, &workspace,
&texture_view, &texture_view,
&resolved_texture_view, &resolved_texture_view,

View File

@@ -6,13 +6,12 @@ use nalgebra::{Matrix4, Vector3};
use parking_lot::RwLock; use parking_lot::RwLock;
use wgpu::{CommandBuffer, CommandEncoder, Device, Queue, RenderPass}; use wgpu::{CommandBuffer, CommandEncoder, Device, Queue, RenderPass};
use crate::app::{config::Config, slice_operation::SliceOperation}; use crate::app::config::Config;
use super::{ use super::{
camera::Camera, camera::Camera,
dispatch::solid_line::SolidLineDispatch, dispatch::solid_line::SolidLineDispatch,
pipelines::{model::ModelPipeline, target_point::TargetPointPipeline}, pipelines::{model::ModelPipeline, target_point::TargetPointPipeline},
preview::render_preview_image,
rendered_mesh::RenderedMesh, rendered_mesh::RenderedMesh,
}; };
@@ -27,6 +26,7 @@ pub struct WorkspaceRenderResources {
pub struct WorkspaceRenderCallback { pub struct WorkspaceRenderCallback {
pub camera: Camera, pub camera: Camera,
pub transform: Matrix4<f32>, pub transform: Matrix4<f32>,
pub is_moving: bool,
pub bed_size: Vector3<f32>, pub bed_size: Vector3<f32>,
pub grid_size: f32, pub grid_size: f32,
@@ -34,8 +34,6 @@ pub struct WorkspaceRenderCallback {
pub models: Arc<RwLock<Vec<RenderedMesh>>>, pub models: Arc<RwLock<Vec<RenderedMesh>>>,
pub config: Config, pub config: Config,
pub is_moving: bool,
pub slice_operation: Option<SliceOperation>,
pub line_support_debug: Vec<[Vector3<f32>; 2]>, pub line_support_debug: Vec<[Vector3<f32>; 2]>,
} }
@@ -50,15 +48,6 @@ impl CallbackTrait for WorkspaceRenderCallback {
) -> Vec<CommandBuffer> { ) -> Vec<CommandBuffer> {
let resources = resources.get_mut::<WorkspaceRenderResources>().unwrap(); let resources = resources.get_mut::<WorkspaceRenderResources>().unwrap();
match &self.slice_operation {
Some(slice_operation) if slice_operation.needs_preview_image() => {
let pipeline = &mut resources.model_pipeline;
let image = render_preview_image(device, queue, (512, 512), pipeline, self);
slice_operation.add_preview_image(image);
}
_ => {}
}
resources.solid_line.prepare(device, queue, self); resources.solid_line.prepare(device, queue, self);
resources.model_pipeline.prepare(device, self); resources.model_pipeline.prepare(device, self);
resources.target_point_pipeline.prepare(queue, self); resources.target_point_pipeline.prepare(queue, self);

View File

@@ -2,6 +2,8 @@ use eframe::Theme;
use egui::{CentralPanel, Color32, Context, Frame, Id, Sense, Ui, WidgetText}; use egui::{CentralPanel, Color32, Context, Frame, Id, Sense, Ui, WidgetText};
use egui_dock::{DockArea, NodeIndex, SurfaceIndex, TabViewer}; use egui_dock::{DockArea, NodeIndex, SurfaceIndex, TabViewer};
use egui_wgpu::Callback; use egui_wgpu::Callback;
use nalgebra::Matrix4;
use parking_lot::MappedRwLockWriteGuard;
use crate::{app::App, render::workspace::WorkspaceRenderCallback}; use crate::{app::App, render::workspace::WorkspaceRenderCallback};
@@ -135,24 +137,40 @@ fn viewport(app: &mut App, ui: &mut Ui, _ctx: &Context) {
}; };
ui.painter().rect_filled(rect, 0.0, color); ui.painter().rect_filled(rect, 0.0, color);
let aspect = rect.width() / rect.height();
let view_projection = app.camera.view_projection_matrix(aspect);
let callback = Callback::new_paint_callback( let callback = Callback::new_paint_callback(
rect, rect,
WorkspaceRenderCallback { app.get_workspace_render_callback(view_projection, response.dragged()),
camera: app.camera.clone(),
transform: app
.camera
.view_projection_matrix(rect.width() / rect.height()),
bed_size: app.slice_config.platform_size,
grid_size: app.config.grid_size,
is_moving: response.dragged(),
slice_operation: app.slice_operation.clone(),
models: app.meshes.clone(),
config: app.config.clone(),
line_support_debug: app.state.line_support_debug.clone(),
},
); );
ui.painter().add(callback); ui.painter().add(callback);
} }
impl App {
pub fn get_workspace_render_callback(
&self,
view_projection: Matrix4<f32>,
is_moving: bool,
) -> WorkspaceRenderCallback {
WorkspaceRenderCallback {
camera: self.camera.clone(),
transform: view_projection,
bed_size: self.slice_config.platform_size,
grid_size: self.config.grid_size,
is_moving,
models: self.meshes.clone(),
config: self.config.clone(),
line_support_debug: self.state.line_support_debug.clone(),
}
}
pub fn get_callback_resource_mut<T: 'static>(&self) -> MappedRwLockWriteGuard<T> {
MappedRwLockWriteGuard::map(self.render_state.renderer.write(), |x| {
x.callback_resources.get_mut::<T>().unwrap()
})
}
}