Skip to content

Commit 325c917

Browse files
committed
gui/entity: Camera abstraction
1 parent 8f9832f commit 325c917

File tree

5 files changed

+131
-44
lines changed

5 files changed

+131
-44
lines changed

eurochef/gui/src/entities.rs

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ use glow::HasContext;
2222

2323
use crate::{
2424
entity_frame::{EntityFrame, RenderableTexture},
25-
render::{self, entity::EntityRenderer, gl_helper, RenderUniforms},
25+
render::{self, camera::ArcBallCamera, entity::EntityRenderer, gl_helper, RenderUniforms},
2626
};
2727

2828
pub struct EntityListPanel {
@@ -207,20 +207,20 @@ impl EntityListPanel {
207207
ui.horizontal_wrapped(|ui| {
208208
ui.spacing_mut().item_spacing = [16., 16.].into();
209209
for i in ids {
210-
ui.allocate_ui(egui::Vec2::splat(256.), |ui| {
210+
ui.allocate_ui(egui::Vec2::new(256., 256. + 20.), |ui| {
211211
ui.spacing_mut().item_spacing = [4., 4.].into();
212212
ui.vertical(|ui| {
213213
let response = if let Some(Some(tex)) = self.entity_previews.get(&i) {
214-
egui::Image::new(tex.id(), [256., 230.])
214+
egui::Image::new(tex.id(), [256., 256.])
215215
.uv(egui::Rect::from_min_size(
216216
egui::Pos2::ZERO,
217-
[1.0, 0.9].into(),
217+
[1.0, 1.0].into(),
218218
))
219219
.sense(egui::Sense::click())
220220
.ui(ui)
221221
} else {
222222
let (rect, response) =
223-
ui.allocate_exact_size([256., 230.].into(), egui::Sense::click());
223+
ui.allocate_exact_size([256., 256.].into(), egui::Sense::click());
224224

225225
ui.painter().rect_filled(
226226
rect,
@@ -365,8 +365,12 @@ impl EntityListPanel {
365365

366366
let mut out = vec![0u8; (self.preview_size * self.preview_size * 4) as usize];
367367

368-
let uniforms =
369-
RenderUniforms::new(true, Vec2::new(-2., -1.), 0.39 * maximum_extent, 1.0);
368+
let zoom = 0.3 * maximum_extent;
369+
let uniforms = RenderUniforms::new(
370+
true,
371+
&ArcBallCamera::new(Vec2::new(-2., -1.), zoom, false),
372+
1.0,
373+
);
370374

371375
unsafe {
372376
#[cfg(not(target_family = "wasm"))]

eurochef/gui/src/entity_frame.rs

Lines changed: 27 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,36 @@
1-
use std::sync::{Arc, Mutex};
1+
use std::{
2+
ops::Deref,
3+
sync::{Arc, Mutex},
4+
};
25

36
use glam::{Vec2, Vec3};
47
use glow::HasContext;
8+
use instant::Instant;
59

610
use crate::{
711
entities::ProcessedEntityMesh,
8-
render::{self, entity::EntityRenderer, grid::GridRenderer, RenderUniforms},
12+
render::{
13+
self,
14+
camera::{ArcBallCamera, Camera3D},
15+
entity::EntityRenderer,
16+
grid::GridRenderer,
17+
RenderUniforms,
18+
},
919
};
1020

1121
pub struct EntityFrame {
1222
pub hashcode: u32,
1323

1424
pub textures: Vec<RenderableTexture>,
1525
pub renderers: Vec<Arc<Mutex<EntityRenderer>>>,
16-
orientation: Vec2,
17-
zoom: f32,
26+
camera: Arc<Mutex<dyn Camera3D>>,
1827

1928
grid: GridRenderer,
2029
mesh_center: Vec3,
2130
pub show_grid: bool,
2231
pub orthographic: bool,
32+
33+
last_frame: Instant,
2334
}
2435

2536
#[derive(Clone)]
@@ -44,12 +55,12 @@ impl EntityFrame {
4455
hashcode,
4556
textures,
4657
renderers: vec![],
47-
orientation: Vec2::new(-2., -1.),
48-
zoom: 5.0,
58+
camera: Arc::new(Mutex::new(ArcBallCamera::default())),
4959
mesh_center: Vec3::ZERO,
5060
show_grid: true,
5161
orthographic: false,
5262
grid: GridRenderer::new(gl, 30),
63+
last_frame: Instant::now(),
5364
};
5465

5566
unsafe {
@@ -69,26 +80,19 @@ impl EntityFrame {
6980
s
7081
}
7182

72-
fn zoom_factor(zoom_level: f32) -> f32 {
73-
2.0f32.powf(zoom_level * std::f32::consts::LN_2) - 0.9
74-
}
75-
7683
pub fn show(&mut self, ui: &mut egui::Ui) {
7784
let (rect, response) =
7885
ui.allocate_exact_size(ui.available_size(), egui::Sense::click_and_drag());
7986

80-
if let Some(multi_touch) = ui.ctx().multi_touch() {
81-
self.zoom += -(multi_touch.zoom_delta - 1.0);
82-
} else {
83-
self.orientation += Vec2::new(response.drag_delta().x, response.drag_delta().y) * 0.005;
84-
85-
self.zoom += -ui.input(|i| i.scroll_delta).y * 0.005;
86-
}
87-
88-
self.zoom = self.zoom.clamp(0.00, 250.0);
87+
self.camera.lock().unwrap().update(
88+
ui,
89+
Some(response),
90+
(Instant::now() - self.last_frame).as_secs_f32(),
91+
);
92+
self.last_frame = Instant::now();
8993

90-
let orientation = self.orientation;
91-
let zoom = Self::zoom_factor(self.zoom);
94+
// let orientation = self.orientation;
95+
// let zoom = Self::zoom_factor(self.zoom);
9296
let mesh_center = self.mesh_center;
9397
let time = ui.input(|t| t.time);
9498

@@ -98,15 +102,15 @@ impl EntityFrame {
98102
// TODO(cohae): How do we get out of this situation
99103
let grid = self.grid.clone(); // FIXME: Ugh.
100104
let textures = self.textures.clone(); // FIXME: UUUUGH.
105+
let camera = self.camera.clone();
101106

102107
let renderers = self.renderers.clone();
103108
let cb = egui_glow::CallbackFn::new(move |info, painter| unsafe {
104109
render::start_render(painter.gl());
105110

106111
let uniforms = RenderUniforms::new(
107112
orthographic,
108-
orientation,
109-
zoom,
113+
camera.lock().unwrap().deref(),
110114
info.viewport.aspect_ratio(),
111115
);
112116

eurochef/gui/src/render/camera.rs

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
use glam::{Mat4, Vec2};
2+
3+
pub trait Camera3D: Sync + Send {
4+
fn update(&mut self, ui: &egui::Ui, response: Option<egui::Response>, delta: f32);
5+
6+
/// Calculate the view matrix
7+
fn calculate_matrix(&self) -> Mat4;
8+
9+
fn zoom(&self) -> f32;
10+
}
11+
12+
fn zoom_factor(zoom_level: f32) -> f32 {
13+
2.0f32.powf(zoom_level * std::f32::consts::LN_2) - 0.9
14+
}
15+
16+
#[derive(Clone)]
17+
pub struct ArcBallCamera {
18+
orientation: Vec2,
19+
zoom: f32,
20+
log_zoom: bool,
21+
}
22+
23+
impl ArcBallCamera {
24+
pub fn new(orientation: Vec2, zoom: f32, log_zoom: bool) -> Self {
25+
ArcBallCamera {
26+
orientation,
27+
zoom,
28+
log_zoom,
29+
}
30+
}
31+
}
32+
33+
impl Default for ArcBallCamera {
34+
fn default() -> Self {
35+
ArcBallCamera {
36+
orientation: Vec2::new(-2., -1.),
37+
zoom: 5.0,
38+
log_zoom: true,
39+
}
40+
}
41+
}
42+
43+
impl Camera3D for ArcBallCamera {
44+
fn update(&mut self, ui: &egui::Ui, response: Option<egui::Response>, _delta: f32) {
45+
if let Some(multi_touch) = ui.ctx().multi_touch() {
46+
self.zoom += -(multi_touch.zoom_delta - 1.0);
47+
} else {
48+
if let Some(response) = response {
49+
self.orientation +=
50+
Vec2::new(response.drag_delta().x, response.drag_delta().y) * 0.005;
51+
}
52+
self.zoom += -ui.input(|i| i.scroll_delta).y * 0.005;
53+
}
54+
55+
self.zoom = self.zoom.clamp(0.00, 250.0);
56+
}
57+
58+
fn calculate_matrix(&self) -> Mat4 {
59+
glam::Mat4::from_rotation_translation(
60+
glam::Quat::from_rotation_x(self.orientation.y)
61+
* glam::Quat::from_rotation_z(self.orientation.x),
62+
glam::vec3(
63+
0.0,
64+
0.0,
65+
if self.log_zoom {
66+
-zoom_factor(self.zoom)
67+
} else {
68+
-self.zoom
69+
},
70+
),
71+
)
72+
}
73+
74+
fn zoom(&self) -> f32 {
75+
if self.log_zoom {
76+
zoom_factor(self.zoom)
77+
} else {
78+
self.zoom
79+
}
80+
}
81+
}

eurochef/gui/src/render/mod.rs

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
1-
use glam::{Mat4, Vec2};
1+
use glam::Mat4;
22
use glow::HasContext;
33

4+
use self::camera::Camera3D;
5+
6+
pub mod camera;
47
pub mod entity;
58
pub mod gl_helper;
69
pub mod grid;
@@ -10,27 +13,22 @@ pub struct RenderUniforms {
1013
}
1114

1215
impl RenderUniforms {
13-
pub fn new(orthographic: bool, orientation: Vec2, zoom: f32, aspect_ratio: f32) -> Self {
16+
pub fn new<C: Camera3D + ?Sized>(orthographic: bool, camera: &C, aspect_ratio: f32) -> Self {
1417
let projection = if orthographic {
1518
glam::Mat4::orthographic_rh_gl(
16-
(aspect_ratio * -zoom) * 2.0,
17-
(-aspect_ratio * -zoom) * 2.0,
18-
(1.0 * -zoom) * 2.0,
19-
(-1.0 * -zoom) * 2.0,
19+
(aspect_ratio * -camera.zoom()) * 2.0,
20+
(-aspect_ratio * -camera.zoom()) * 2.0,
21+
(1.0 * -camera.zoom()) * 2.0,
22+
(-1.0 * -camera.zoom()) * 2.0,
2023
-50.0,
2124
2500.0,
2225
)
2326
} else {
24-
glam::Mat4::perspective_rh_gl(90.0_f32.to_radians(), aspect_ratio, 0.1, 1000.0)
27+
glam::Mat4::perspective_rh_gl(90.0_f32.to_radians(), aspect_ratio, 0.1, 2000.0)
2528
};
2629

27-
let view = glam::Mat4::from_rotation_translation(
28-
glam::Quat::from_rotation_x(orientation.y) * glam::Quat::from_rotation_z(orientation.x),
29-
glam::vec3(0.0, 0.0, -zoom),
30-
);
31-
3230
Self {
33-
view: projection * view,
31+
view: projection * camera.calculate_matrix(),
3432
}
3533
}
3634
}

eurochef/gui/src/textures.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ impl TextureList {
7878
.auto_shrink([false, false])
7979
.show(ui, |ui| {
8080
ui.horizontal_wrapped(|ui| {
81-
ui.spacing_mut().item_spacing = [4.; 2].into();
81+
ui.spacing_mut().item_spacing = [4. * self.zoom; 2].into();
8282
for (i, t) in self.textures.iter().enumerate() {
8383
if self.filter_animated && self.textures[i].frame_count <= 1 {
8484
continue;

0 commit comments

Comments
 (0)