From 937dc12fd9d70501d3296851b5183a958b2467c6 Mon Sep 17 00:00:00 2001 From: David Reeves Date: Sun, 1 Sep 2024 12:49:44 +0100 Subject: [PATCH] Simplify camera event handlers Event handlers now infer camera distance from zoom controls which are no longer an optional input. Other changes - Implements pan support in the camera touch event handler - Add touch event handler to hello-tetra example - Fixes world axes transform in hello-tetra example --- example/hello-tetra/scene.cpp | 27 +++++++++++---- include/dr/app/event_handlers.hpp | 12 +++---- src/event_handlers.cpp | 57 +++++++++++++++++-------------- 3 files changed, 57 insertions(+), 39 deletions(-) diff --git a/example/hello-tetra/scene.cpp b/example/hello-tetra/scene.cpp index 2b11073..3d77d36 100644 --- a/example/hello-tetra/scene.cpp +++ b/example/hello-tetra/scene.cpp @@ -30,6 +30,8 @@ struct { struct { bool mouse_down[3]; + Vec2 last_touch_points[2]; + i8 last_num_touches; } input; struct { @@ -178,14 +180,17 @@ void draw_mesh(Mat4 const& local_to_view, Mat4 const& view_to_clip) sg_draw(0, 12, 1); } -void debug_draw(Mat4 const& local_to_view, Mat4 const& view_to_clip) +void debug_draw( + Mat4 const& local_to_view, + Mat4 const& world_to_view, + Mat4 const& view_to_clip) { sgl_defaults(); sgl_matrix_mode_projection(); sgl_load_matrix(view_to_clip.data()); - debug_draw_axes(local_to_view, 0.1f); + debug_draw_axes(world_to_view, 0.25f); debug_draw_unit_cube_edges(local_to_view); sgl_draw(); @@ -229,21 +234,31 @@ void draw(void* /*context*/) state.view.clip_far); draw_mesh(local_to_view, view_to_clip); - debug_draw(local_to_view, view_to_clip); + debug_draw(local_to_view, world_to_view, view_to_clip); draw_ui(); } void handle_event(void* /*context*/, App::Event const& event) { + f32 const screen_to_view = dr::screen_to_view(state.view.fov_y, sapp_heightf()); + camera_handle_mouse_event( event, - state.camera.offset.z(), - screen_to_view(state.view.fov_y, sapp_heightf()), + state.zoom.target, &state.orbit.target, - &state.zoom.target, &state.pan.target, + screen_to_view, state.input.mouse_down); + camera_handle_touch_event( + event, + state.zoom.target, + &state.orbit.target, + &state.pan.target, + screen_to_view, + state.input.last_touch_points, + state.input.last_num_touches); + constexpr auto center_camera = []() { state.zoom.target.distance = 4.0f; state.pan.target.offset = {}; diff --git a/include/dr/app/event_handlers.hpp b/include/dr/app/event_handlers.hpp index 7ca2a68..1112b58 100644 --- a/include/dr/app/event_handlers.hpp +++ b/include/dr/app/event_handlers.hpp @@ -14,27 +14,25 @@ struct Pan; /// True if the mouse is over the app window bool is_mouse_over(App::Event const& event); -/// Scale factor for taking screen space distances to view space (at z = -1). +/// Scale factor for taking screen space displacements to view space (at z = -1). f32 screen_to_view(f32 fov, f32 size); /// Handles mouse events for camera control void camera_handle_mouse_event( App::Event const& event, - f32 camera_distance, - f32 screen_to_view, + Zoom& zoom, Orbit* orbit, - Zoom* zoom, Pan* pan, + f32 screen_to_view, bool mouse_down[3]); /// Handles touch events for camera control void camera_handle_touch_event( App::Event const& event, - f32 camera_distance, - f32 screen_to_view, + Zoom& zoom, Orbit* orbit, - Zoom* zoom, Pan* pan, + f32 screen_to_view, Vec2 last_touch_points[2], i8& last_num_touches); diff --git a/src/event_handlers.cpp b/src/event_handlers.cpp index d8ecae2..498a576 100644 --- a/src/event_handlers.cpp +++ b/src/event_handlers.cpp @@ -2,6 +2,7 @@ #include +#include #include #include @@ -14,20 +15,18 @@ bool is_mouse_over(App::Event const& event) && event.mouse_y < event.window_height; } -f32 screen_to_view(f32 const fov, f32 const size) -{ - return std::tan(fov * 0.5f) / (size * 0.5f); -} +f32 screen_to_view(f32 const fov, f32 const size) { return std::tan(fov * 0.5f) / (size * 0.5f); } void camera_handle_mouse_event( App::Event const& event, - f32 const camera_distance, - f32 const screen_to_view, + Zoom& zoom, Orbit* const orbit, - Zoom* const zoom, Pan* const pan, + f32 const screen_to_view, bool mouse_down[3]) { + f32 const cam_offset = zoom.distance; + switch (event.type) { case SAPP_EVENTTYPE_MOUSE_DOWN: @@ -65,30 +64,25 @@ void camera_handle_mouse_event( } case SAPP_EVENTTYPE_MOUSE_MOVE: { - if (mouse_down[0] && orbit != nullptr) + if (mouse_down[0] && orbit) { Vec2 const d{event.mouse_dx, event.mouse_dy}; orbit->handle_drag(d * screen_to_view); } - if (mouse_down[1] && pan != nullptr) + if (mouse_down[1] && pan) { Vec2 const d{event.mouse_dx, event.mouse_dy}; - pan->handle_drag(d * (camera_distance * screen_to_view)); + pan->handle_drag(d * (cam_offset * screen_to_view)); } break; } case SAPP_EVENTTYPE_MOUSE_SCROLL: { - constexpr f32 scroll_scale = 0.1f; - - if (zoom != nullptr) - { - f32 const d = scroll_scale * sign(event.scroll_y); - zoom->handle_scroll(d * camera_distance); - } - + constexpr f32 scroll_scale = 0.1f; // TODO(dr): Expose as parameter + f32 const d = scroll_scale * sign(event.scroll_y); + zoom.handle_scroll(d * cam_offset); break; } default: @@ -100,14 +94,15 @@ void camera_handle_mouse_event( void camera_handle_touch_event( App::Event const& event, - f32 const camera_distance, - f32 const screen_to_view, + Zoom& zoom, Orbit* const orbit, - Zoom* const zoom, - Pan* const /*pan*/, + Pan* const pan, + f32 const screen_to_view, Vec2 last_touch_points[2], i8& last_num_touches) { + f32 const cam_offset = zoom.distance; + switch (event.type) { case SAPP_EVENTTYPE_TOUCHES_BEGAN: @@ -133,7 +128,7 @@ void camera_handle_touch_event( { case 1: { - if (orbit != nullptr) + if (orbit) { Vec2 const d = p0 - last_touch_points[0]; orbit->handle_drag(d * screen_to_view); @@ -142,12 +137,22 @@ void camera_handle_touch_event( } case 2: { - // TODO: Handle as pan if deltas for each point are nearly parallel - if (zoom != nullptr) + // Handle drag pan + if(pan) + { + Vec2 const d0 = screen_to_view * (p0 - last_touch_points[0]); + Vec2 const d1 = screen_to_view * (p1 - last_touch_points[1]); + + constexpr f32 abs_tol = 1.0e-3; // TODO(dr): Test this + if(near_equal(d0, d1, abs_tol)) + pan->handle_drag((d0 + d1) * (0.5f * cam_offset)); + } + + // Handle pinch zoom { f32 const d0 = (last_touch_points[0] - last_touch_points[1]).norm(); f32 const d1 = (p0 - p1).norm(); - zoom->handle_scroll((d1 - d0) * (camera_distance * screen_to_view)); + zoom.handle_scroll((d1 - d0) * (cam_offset * screen_to_view)); } break; }