1use std::sync::Arc;
6use winit::{
7 application::ApplicationHandler,
8 event::{WindowEvent, ElementState, MouseButton},
9 event_loop::{EventLoop, ActiveEventLoop},
10 window::{Window, WindowId},
11 keyboard::Key,
12 dpi::PhysicalPosition,
13};
14
15use threecrate_core::{PointCloud, TriangleMesh, Result, Point3f, ColoredPoint3f, Error};
16use threecrate_gpu::{
17 PointCloudRenderer, RenderConfig, PointVertex,
18 MeshRenderer, MeshRenderConfig, ShadingMode, mesh_to_gpu_mesh,
19};
20use threecrate_algorithms::{ICPResult, PlaneSegmentationResult};
21use crate::camera::Camera;
22
23use nalgebra::{Vector3, Point3};
24
25#[derive(Debug, Clone)]
27pub enum ViewData {
28 Empty,
29 PointCloud(PointCloud<Point3f>),
30 ColoredPointCloud(PointCloud<ColoredPoint3f>),
31 Mesh(TriangleMesh),
32}
33
34#[derive(Debug, Clone, Copy, PartialEq)]
36pub enum CameraMode {
37 Orbit,
38 Pan,
39 Zoom,
40}
41
42#[derive(Debug, Clone, Copy, PartialEq)]
44pub enum PipelineType {
45 Cpu,
46 Gpu,
47}
48
49#[derive(Debug, Clone)]
51pub struct ICPParams {
52 pub max_iterations: usize,
53 pub convergence_threshold: f32,
54 pub max_correspondence_distance: f32,
55}
56
57impl Default for ICPParams {
58 fn default() -> Self {
59 Self {
60 max_iterations: 50,
61 convergence_threshold: 0.001,
62 max_correspondence_distance: 1.0,
63 }
64 }
65}
66
67#[derive(Debug, Clone)]
69pub struct RANSACParams {
70 pub max_iterations: usize,
71 pub distance_threshold: f32,
72}
73
74impl Default for RANSACParams {
75 fn default() -> Self {
76 Self {
77 max_iterations: 1000,
78 distance_threshold: 0.1,
79 }
80 }
81}
82
83#[derive(Debug)]
85pub struct UIState {
86 pub render_panel_open: bool,
87 pub algorithm_panel_open: bool,
88 pub camera_panel_open: bool,
89 pub stats_panel_open: bool,
90 pub icp_params: ICPParams,
91 pub ransac_params: RANSACParams,
92 pub source_cloud: Option<PointCloud<Point3f>>,
93 pub target_cloud: Option<PointCloud<Point3f>>,
94 pub icp_result: Option<ICPResult>,
95 pub ransac_result: Option<PlaneSegmentationResult>,
96}
97
98impl Default for UIState {
99 fn default() -> Self {
100 Self {
101 render_panel_open: false,
102 algorithm_panel_open: false,
103 camera_panel_open: false,
104 stats_panel_open: false,
105 icp_params: ICPParams::default(),
106 ransac_params: RANSACParams::default(),
107 source_cloud: None,
108 target_cloud: None,
109 icp_result: None,
110 ransac_result: None,
111 }
112 }
113}
114
115pub struct InteractiveViewer {
117 current_data: ViewData,
118 camera: Camera,
119 camera_mode: CameraMode,
120 last_mouse_pos: Option<PhysicalPosition<f64>>,
121 mouse_pressed: bool,
122 right_mouse_pressed: bool,
123 debug_frame_count: usize,
124 vertices_dirty: bool,
125}
126
127impl InteractiveViewer {
128 pub fn new() -> Result<Self> {
130 let camera = Camera::new(
131 Point3::new(5.0, 5.0, 5.0),
132 Point3::new(0.0, 0.0, 0.0),
133 Vector3::new(0.0, 1.0, 0.0),
134 45.0,
135 1.0,
136 0.1,
137 100.0,
138 );
139
140 Ok(Self {
141 current_data: ViewData::Empty,
142 camera,
143 camera_mode: CameraMode::Orbit,
144 last_mouse_pos: None,
145 mouse_pressed: false,
146 right_mouse_pressed: false,
147 debug_frame_count: 0,
148 vertices_dirty: true,
149 })
150 }
151
152 pub fn set_point_cloud(&mut self, cloud: &PointCloud<Point3f>) {
154 self.current_data = ViewData::PointCloud(cloud.clone());
155 self.vertices_dirty = true;
156 println!("Set point cloud with {} points", cloud.len());
157 }
158
159 pub fn set_colored_point_cloud(&mut self, cloud: &PointCloud<ColoredPoint3f>) {
161 self.current_data = ViewData::ColoredPointCloud(cloud.clone());
162 self.vertices_dirty = true;
163 println!("Set colored point cloud with {} points", cloud.len());
164 }
165
166 pub fn set_mesh(&mut self, mesh: &TriangleMesh) {
168 self.current_data = ViewData::Mesh(mesh.clone());
169 self.vertices_dirty = true;
170 println!("Set mesh with {} vertices and {} faces", mesh.vertices.len(), mesh.faces.len());
171 }
172
173 pub fn run(self) -> Result<()> {
175 println!("Starting threecrate Interactive Viewer...");
176
177 let event_loop = EventLoop::new().map_err(|e| Error::Io(std::io::Error::new(std::io::ErrorKind::Other, format!("Failed to create event loop: {}", e))))?;
179
180 let mut app = ViewerApp {
182 viewer: self,
183 window: None,
184 point_renderer: None,
185 mesh_renderer: None,
186 cached_vertices: Vec::new(),
187 };
188
189 event_loop.run_app(&mut app).map_err(|e| Error::Io(std::io::Error::new(std::io::ErrorKind::Other, format!("Event loop error: {}", e))))?;
191
192 Ok(())
193 }
194}
195
196impl Default for InteractiveViewer {
197 fn default() -> Self {
198 Self::new().expect("Failed to create InteractiveViewer")
199 }
200}
201
202struct ViewerApp {
204 viewer: InteractiveViewer,
205 window: Option<Arc<Window>>,
206 point_renderer: Option<PointCloudRenderer<'static>>,
207 mesh_renderer: Option<MeshRenderer<'static>>,
208 cached_vertices: Vec<PointVertex>,
209}
210
211impl ApplicationHandler for ViewerApp {
212 fn resumed(&mut self, event_loop: &ActiveEventLoop) {
213 if self.window.is_none() {
214 println!("Creating window and initializing renderers...");
215
216 let window_attrs = Window::default_attributes()
218 .with_title("threecrate Interactive Viewer")
219 .with_inner_size(winit::dpi::LogicalSize::new(1200.0, 800.0));
220
221 let window = match event_loop.create_window(window_attrs) {
222 Ok(w) => Arc::new(w),
223 Err(e) => {
224 eprintln!("Failed to create window: {}", e);
225 event_loop.exit();
226 return;
227 }
228 };
229
230 let size = window.inner_size();
232 self.viewer.camera.aspect_ratio = size.width as f32 / size.height as f32;
233
234 let window_ref: &'static Window = unsafe {
237 std::mem::transmute::<&Window, &'static Window>(window.as_ref())
238 };
239
240 let pc_config = RenderConfig::default();
242 let point_renderer = match pollster::block_on(PointCloudRenderer::new(window_ref, pc_config)) {
243 Ok(r) => r,
244 Err(e) => {
245 eprintln!("Failed to create point cloud renderer: {}", e);
246 event_loop.exit();
247 return;
248 }
249 };
250
251 let mesh_config = MeshRenderConfig::default();
252 let mesh_renderer = match pollster::block_on(MeshRenderer::new(window_ref, mesh_config)) {
253 Ok(r) => r,
254 Err(e) => {
255 eprintln!("Failed to create mesh renderer: {}", e);
256 event_loop.exit();
257 return;
258 }
259 };
260
261 self.window = Some(window);
262 self.point_renderer = Some(point_renderer);
263 self.mesh_renderer = Some(mesh_renderer);
264
265 println!("Viewer initialized successfully. Window should now be visible.");
266 }
267 }
268
269 fn window_event(&mut self, event_loop: &ActiveEventLoop, _id: WindowId, event: WindowEvent) {
270 let Some(window) = &self.window else { return; };
271 let Some(point_renderer) = &mut self.point_renderer else { return; };
272 let Some(mesh_renderer) = &mut self.mesh_renderer else { return; };
273
274 match event {
275 WindowEvent::CloseRequested => {
276 event_loop.exit();
277 }
278 WindowEvent::Resized(new_size) => {
279 point_renderer.resize(new_size);
280 mesh_renderer.resize(new_size);
281 self.viewer.camera.aspect_ratio = new_size.width as f32 / new_size.height as f32;
282 }
283 WindowEvent::MouseInput { state, button, .. } => {
284 match button {
285 MouseButton::Left => {
286 self.viewer.mouse_pressed = state == ElementState::Pressed;
287 }
288 MouseButton::Right => {
289 self.viewer.right_mouse_pressed = state == ElementState::Pressed;
290 }
291 _ => {}
292 }
293 }
294 WindowEvent::CursorMoved { position, .. } => {
295 if let Some(last_pos) = self.viewer.last_mouse_pos {
296 let delta_x = position.x - last_pos.x;
297 let delta_y = position.y - last_pos.y;
298
299 if self.viewer.mouse_pressed {
300 match self.viewer.camera_mode {
301 CameraMode::Orbit => {
302 self.viewer.camera.orbit(delta_x as f32 * 0.01, delta_y as f32 * 0.01);
303 }
304 CameraMode::Pan => {
305 self.viewer.camera.pan(delta_x as f32 * 0.01, delta_y as f32 * 0.01);
306 }
307 _ => {}
308 }
309 }
310 }
311 self.viewer.last_mouse_pos = Some(position);
312 }
313 WindowEvent::MouseWheel { delta, .. } => {
314 let scroll_delta = match delta {
315 winit::event::MouseScrollDelta::LineDelta(_, y) => y,
316 winit::event::MouseScrollDelta::PixelDelta(pos) => pos.y as f32 / 100.0,
317 };
318 self.viewer.camera.zoom(scroll_delta * 0.1);
319 }
320 WindowEvent::KeyboardInput { event, .. } => {
321 if event.state == ElementState::Pressed {
322 match &event.logical_key {
323 Key::Character(c) => {
324 match c.as_str() {
325 "o" | "O" => {
326 self.viewer.camera_mode = CameraMode::Orbit;
327 println!("Switched to Orbit mode");
328 }
329 "p" | "P" => {
330 self.viewer.camera_mode = CameraMode::Pan;
331 println!("Switched to Pan mode");
332 }
333 "z" | "Z" => {
334 self.viewer.camera_mode = CameraMode::Zoom;
335 println!("Switched to Zoom mode");
336 }
337 "r" | "R" => {
338 self.viewer.camera.reset();
339 println!("Reset camera");
340 }
341 _ => {}
342 }
343 }
344 _ => {}
345 }
346 }
347 }
348 WindowEvent::RedrawRequested => {
349 let view_matrix = self.viewer.camera.view_matrix();
351 let proj_matrix = self.viewer.camera.projection_matrix();
352 let camera_pos = self.viewer.camera.position.coords;
353 point_renderer.update_camera(view_matrix, proj_matrix, camera_pos);
354 mesh_renderer.update_camera(view_matrix, proj_matrix, camera_pos);
355
356 if self.viewer.vertices_dirty {
358 self.cached_vertices = match &self.viewer.current_data {
359 ViewData::PointCloud(cloud) => {
360 let mut vertices = Vec::with_capacity(cloud.len() * 6);
361 for point in cloud.iter() {
362 let size = 0.02;
363 let pos = [point.x, point.y, point.z];
364 let color = [1.0, 1.0, 1.0];
365 let normal = [0.0, 0.0, 1.0];
366
367 let v1 = PointVertex::from_point(&Point3f::new(pos[0] - size, pos[1] - size, pos[2]), color, 16.0, normal);
368 let v2 = PointVertex::from_point(&Point3f::new(pos[0] + size, pos[1] - size, pos[2]), color, 16.0, normal);
369 let v3 = PointVertex::from_point(&Point3f::new(pos[0] + size, pos[1] + size, pos[2]), color, 16.0, normal);
370 let v4 = PointVertex::from_point(&Point3f::new(pos[0] - size, pos[1] + size, pos[2]), color, 16.0, normal);
371
372 vertices.push(v1);
373 vertices.push(v2);
374 vertices.push(v3);
375 vertices.push(v1);
376 vertices.push(v3);
377 vertices.push(v4);
378 }
379 vertices
380 }
381 ViewData::ColoredPointCloud(cloud) => {
382 let mut vertices = Vec::with_capacity(cloud.len() * 6);
383 for point in cloud.iter() {
384 let size = 0.02;
385 let pos = [point.position.x, point.position.y, point.position.z];
386 let color = [
387 point.color[0] as f32 / 255.0,
388 point.color[1] as f32 / 255.0,
389 point.color[2] as f32 / 255.0,
390 ];
391 let normal = [0.0, 0.0, 1.0];
392
393 let v1 = PointVertex::from_point(&Point3f::new(pos[0] - size, pos[1] - size, pos[2]), color, 16.0, normal);
394 let v2 = PointVertex::from_point(&Point3f::new(pos[0] + size, pos[1] - size, pos[2]), color, 16.0, normal);
395 let v3 = PointVertex::from_point(&Point3f::new(pos[0] + size, pos[1] + size, pos[2]), color, 16.0, normal);
396 let v4 = PointVertex::from_point(&Point3f::new(pos[0] - size, pos[1] + size, pos[2]), color, 16.0, normal);
397
398 vertices.push(v1);
399 vertices.push(v2);
400 vertices.push(v3);
401 vertices.push(v1);
402 vertices.push(v3);
403 vertices.push(v4);
404 }
405 vertices
406 }
407 ViewData::Mesh(_) | ViewData::Empty => vec![],
408 };
409 self.viewer.vertices_dirty = false;
410 }
411
412 if !self.cached_vertices.is_empty() {
414 if self.viewer.debug_frame_count % 60 == 0 {
415 println!("Rendering {} vertices", self.cached_vertices.len());
416 }
417 }
418 self.viewer.debug_frame_count += 1;
419
420 match &self.viewer.current_data {
421 ViewData::PointCloud(_) | ViewData::ColoredPointCloud(_) => {
422 if !self.cached_vertices.is_empty() {
423 if let Err(e) = point_renderer.render(&self.cached_vertices) {
424 eprintln!("Render error: {}", e);
425 }
426 }
427 }
428 ViewData::Mesh(mesh) => {
429 if !mesh.vertices.is_empty() && !mesh.faces.is_empty() {
430 let indices: Vec<u32> = mesh
432 .faces
433 .iter()
434 .flat_map(|f| [f[0] as u32, f[1] as u32, f[2] as u32])
435 .collect();
436
437 let normals_opt = mesh.normals.as_ref().map(|n| n.as_slice());
439
440 let colors_f32: Option<Vec<[f32; 3]>> = mesh.colors.as_ref().map(|cols| {
442 cols.iter()
443 .map(|c| [c[0] as f32 / 255.0, c[1] as f32 / 255.0, c[2] as f32 / 255.0])
444 .collect()
445 });
446 let colors_opt = colors_f32.as_ref().map(|c| c.as_slice());
447
448 let gpu_mesh = mesh_to_gpu_mesh(
450 &mesh.vertices,
451 &indices,
452 normals_opt,
453 colors_opt,
454 None,
455 );
456
457 if let Err(e) = mesh_renderer.render(&gpu_mesh, ShadingMode::Flat) {
458 eprintln!("Mesh render error: {}", e);
459 }
460 }
461 }
462 ViewData::Empty => {}
463 }
464
465 window.request_redraw();
467 }
468 _ => {}
469 }
470 }
471
472 fn about_to_wait(&mut self, _event_loop: &ActiveEventLoop) {
473 if let Some(window) = &self.window {
474 window.request_redraw();
475 }
476 }
477}
478