-
Notifications
You must be signed in to change notification settings - Fork 36
Integration with egui #115
-
How easy is it to get ranim working in an egui project? This could add some really great visuals to egui.
Beta Was this translation helpful? Give feedback.
All reactions
Replies: 1 comment 1 reply
-
If you want to use ranim to create some UI elements, it might not suit to you, because ranim is designed to produce scientific or mathematical visualization videos (similar to 3b1b/manim).
If you want to display ranim scenes in egui, there are mainly two solutions:
One is to use wgpu + winit + egui, rendering both ranim and egui to the window's surface (this is how ranim-app display ranim scene).
Another is to use eframe + egui_wgpu, rendering to egui's canvas through egui_wgpu::Callback.
Beta Was this translation helpful? Give feedback.
All reactions
-
Now, ranim-app is refactored with eframe in #114, and drawing a ranim scene in eframe is quite easy.
First, you constructs wgpu and ranim-render related things:
fn prepare_renderer(&mut self, frame: &eframe::Frame) { if self.renderer.is_some() { return; } tracing::info!("preparing renderer..."); let Some(render_state) = frame.wgpu_render_state() else { tracing::info!("frame.wgpu_render_state() is none"); tracing::info!("{:?}", frame.info()); return; }; tracing::info!("constructing renderer..."); // Construct WgpuContext using eframe's resources. // NOTE: We assume ranim-render doesn't strictly depend on the instance for the operations we do here. let ctx = WgpuContext { instance: wgpu::Instance::default(), // Dummy instance adapter: wgpu::Adapter::clone(&render_state.adapter), device: wgpu::Device::clone(&render_state.device), queue: wgpu::Queue::clone(&render_state.queue), }; let renderer = Renderer::new(&ctx, 2560, 1440, 8); // TODO: dynamic size // Register texture with egui let texture_view = &renderer.render_textures.linear_render_view; let id = render_state.renderer.write().register_native_texture( &render_state.device, texture_view, wgpu::FilterMode::Linear, ); self.texture_id = Some(id); self.renderer = Some(renderer); self.wgpu_ctx = Some(ctx); }
Then in each cycle, you simply call ranim's API to render a scene, then in ui you use Painter to draw the texture as an image:
fn render_animation(&mut self) { if let (Some(ctx), Some(renderer)) = (self.wgpu_ctx.as_ref(), self.renderer.as_mut()) { // ... self.store .update(self.timeline.eval_at_sec(self.timeline_state.current_sec)); renderer.render_store_with_pool(ctx, self.clear_color, &self.store, &mut self.pool); self.pool.clean(); } }
// In ui, something like this if let Some(tid) = texture_id { // Maintain aspect ratio // TODO: We could update renderer size here if we want dynamic resolution let available_size = ui.available_size(); let aspect_ratio = self .renderer .as_ref() .map(|r| r.ratio()) .unwrap_or(1280.0 / 7.0); let mut size = available_size; if size.x / size.y > aspect_ratio { size.x = size.y * aspect_ratio; } else { size.y = size.x / aspect_ratio; } ui.centered_and_justified(|ui| { ui.image(egui::load::SizedTexture::new(tid, size)); }); } else { ui.centered_and_justified(|ui| { ui.spinner(); }); }
Beta Was this translation helpful? Give feedback.