mod camera_parameters;
pub use camera_parameters::*;
use glam::{Mat4, Vec3, Vec4};
use polyscope_core::pick::PickResult;
use polyscope_core::quantity::Quantity;
use polyscope_core::structure::{HasQuantities, RenderContext, Structure};
use polyscope_render::CurveNetworkRenderData;
pub struct CameraView {
name: String,
params: CameraParameters,
enabled: bool,
transform: Mat4,
quantities: Vec<Box<dyn Quantity>>,
color: Vec4,
widget_focal_length: f32,
widget_focal_length_is_relative: bool,
widget_thickness: f32,
render_data: Option<CurveNetworkRenderData>,
prepared_length_scale: f32,
fly_to_requested: bool,
}
impl CameraView {
pub fn new(name: impl Into<String>, params: CameraParameters) -> Self {
Self {
name: name.into(),
params,
enabled: true,
transform: Mat4::IDENTITY,
quantities: Vec::new(),
color: Vec4::new(0.0, 0.0, 0.0, 1.0),
widget_focal_length: 0.20,
widget_focal_length_is_relative: true,
widget_thickness: 0.02,
render_data: None,
prepared_length_scale: 0.0,
fly_to_requested: false,
}
}
pub fn from_look_at(
name: impl Into<String>,
position: Vec3,
look_at: Vec3,
up: Vec3,
fov_vertical_degrees: f32,
aspect_ratio: f32,
) -> Self {
let look_dir = (look_at - position).normalize();
let params = CameraParameters::from_vectors(
position,
look_dir,
up,
fov_vertical_degrees,
aspect_ratio,
);
Self::new(name, params)
}
#[must_use]
pub fn params(&self) -> &CameraParameters {
&self.params
}
pub fn set_params(&mut self, params: CameraParameters) -> &mut Self {
self.params = params;
self.render_data = None; self
}
#[must_use]
pub fn color(&self) -> Vec4 {
self.color
}
pub fn set_color(&mut self, color: Vec3) -> &mut Self {
self.color = color.extend(1.0);
self.render_data = None;
self
}
#[must_use]
pub fn widget_focal_length(&self) -> f32 {
self.widget_focal_length
}
pub fn set_widget_focal_length(&mut self, length: f32, is_relative: bool) -> &mut Self {
self.widget_focal_length = length;
self.widget_focal_length_is_relative = is_relative;
self.render_data = None; self
}
#[must_use]
pub fn widget_thickness(&self) -> f32 {
self.widget_thickness
}
pub fn set_widget_thickness(&mut self, thickness: f32) -> &mut Self {
self.widget_thickness = thickness;
self.render_data = None; self
}
fn compute_focal_length(&self, length_scale: f32) -> f32 {
if self.widget_focal_length_is_relative {
self.widget_focal_length * length_scale
} else {
self.widget_focal_length
}
}
fn compute_radius(&self, length_scale: f32) -> f32 {
let focal = self.compute_focal_length(length_scale);
focal * self.widget_thickness
}
fn generate_wireframe(&self, length_scale: f32) -> (Vec<Vec3>, Vec<[u32; 2]>) {
let focal = self.compute_focal_length(length_scale);
let root = self.params.position();
let (look_dir, up_dir, right_dir) = self.params.camera_frame();
let frame_center = root + look_dir * focal;
let half_height = focal * (self.params.fov_vertical_degrees().to_radians() / 2.0).tan();
let half_width = self.params.aspect_ratio() * half_height;
let frame_up = up_dir * half_height;
let frame_right = right_dir * half_width;
let upper_left = frame_center + frame_up - frame_right;
let upper_right = frame_center + frame_up + frame_right;
let lower_left = frame_center - frame_up - frame_right;
let lower_right = frame_center - frame_up + frame_right;
let tri_left = frame_center + frame_up * 1.2 - frame_right * 0.7;
let tri_right = frame_center + frame_up * 1.2 + frame_right * 0.7;
let tri_top = frame_center + frame_up * 2.0;
let nodes = vec![
root, upper_left, upper_right, lower_left, lower_right, tri_left, tri_right, tri_top, ];
let edges = vec![
[0, 1],
[0, 2],
[0, 3],
[0, 4],
[1, 2],
[2, 4],
[4, 3],
[3, 1],
[5, 6],
[6, 7],
[7, 5],
];
(nodes, edges)
}
#[must_use]
pub fn needs_reinit(&self, length_scale: f32) -> bool {
self.render_data.is_none()
|| (self.widget_focal_length_is_relative
&& (self.prepared_length_scale - length_scale).abs() > 1e-6)
}
pub fn init_render_data(
&mut self,
device: &wgpu::Device,
bind_group_layout: &wgpu::BindGroupLayout,
camera_buffer: &wgpu::Buffer,
queue: &wgpu::Queue,
length_scale: f32,
) {
let (nodes, edges) = self.generate_wireframe(length_scale);
let edge_tail_inds: Vec<u32> = edges.iter().map(|e| e[0]).collect();
let edge_tip_inds: Vec<u32> = edges.iter().map(|e| e[1]).collect();
let render_data = CurveNetworkRenderData::new(
device,
bind_group_layout,
camera_buffer,
&nodes,
&edge_tail_inds,
&edge_tip_inds,
);
let uniforms = polyscope_render::CurveNetworkUniforms {
color: self.color.to_array(),
radius: self.compute_radius(length_scale),
radius_is_relative: 0, render_mode: 0, _padding: 0.0,
};
render_data.update_uniforms(queue, &uniforms);
self.render_data = Some(render_data);
self.prepared_length_scale = length_scale;
}
#[must_use]
pub fn render_data(&self) -> Option<&CurveNetworkRenderData> {
self.render_data.as_ref()
}
#[must_use]
pub fn take_fly_to_request(&mut self) -> bool {
let requested = self.fly_to_requested;
self.fly_to_requested = false;
requested
}
pub fn build_egui_ui(&mut self, ui: &mut egui::Ui) {
ui.horizontal(|ui| {
if ui.button("fly to").clicked() {
self.fly_to_requested = true;
}
ui.label(format!(
"FoV: {:.1}° aspect: {:.2}",
self.params.fov_vertical_degrees(),
self.params.aspect_ratio()
));
});
ui.horizontal(|ui| {
ui.label("Color:");
let mut color = [self.color.x, self.color.y, self.color.z];
if ui.color_edit_button_rgb(&mut color).changed() {
self.set_color(Vec3::new(color[0], color[1], color[2]));
}
});
ui.horizontal(|ui| {
ui.label("Thickness:");
let mut thickness = self.widget_thickness;
if ui
.add(
egui::DragValue::new(&mut thickness)
.speed(0.001)
.range(0.001..=0.5),
)
.changed()
{
self.set_widget_thickness(thickness);
}
});
ui.separator();
ui.label(format!(
"Position: ({:.2}, {:.2}, {:.2})",
self.params.position().x,
self.params.position().y,
self.params.position().z
));
}
}
impl Structure for CameraView {
fn as_any(&self) -> &dyn std::any::Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
self
}
fn name(&self) -> &str {
&self.name
}
fn type_name(&self) -> &'static str {
"CameraView"
}
fn bounding_box(&self) -> Option<(Vec3, Vec3)> {
Some((self.params.position(), self.params.position()))
}
fn length_scale(&self) -> f32 {
0.0
}
fn transform(&self) -> Mat4 {
self.transform
}
fn set_transform(&mut self, transform: Mat4) {
self.transform = transform;
}
fn is_enabled(&self) -> bool {
self.enabled
}
fn set_enabled(&mut self, enabled: bool) {
self.enabled = enabled;
}
fn draw(&self, _ctx: &mut dyn RenderContext) {
}
fn draw_pick(&self, _ctx: &mut dyn RenderContext) {
}
fn build_ui(&mut self, _ui: &dyn std::any::Any) {
}
fn build_pick_ui(&self, _ui: &dyn std::any::Any, _pick: &PickResult) {
}
fn clear_gpu_resources(&mut self) {
self.render_data = None;
for quantity in &mut self.quantities {
quantity.clear_gpu_resources();
}
}
fn refresh(&mut self) {
self.render_data = None;
for quantity in &mut self.quantities {
quantity.refresh();
}
}
}
impl HasQuantities for CameraView {
fn add_quantity(&mut self, quantity: Box<dyn Quantity>) {
self.quantities.push(quantity);
}
fn get_quantity(&self, name: &str) -> Option<&dyn Quantity> {
self.quantities
.iter()
.find(|q| q.name() == name)
.map(std::convert::AsRef::as_ref)
}
fn get_quantity_mut(&mut self, name: &str) -> Option<&mut Box<dyn Quantity>> {
self.quantities.iter_mut().find(|q| q.name() == name)
}
fn remove_quantity(&mut self, name: &str) -> Option<Box<dyn Quantity>> {
let idx = self.quantities.iter().position(|q| q.name() == name)?;
Some(self.quantities.remove(idx))
}
fn quantities(&self) -> &[Box<dyn Quantity>] {
&self.quantities
}
}