diff --git a/code/io/spacemouse.cpp b/code/io/spacemouse.cpp index 09def91cb90..bc4f9435841 100644 --- a/code/io/spacemouse.cpp +++ b/code/io/spacemouse.cpp @@ -167,6 +167,12 @@ std::unique_ptr SpaceMouse::searchSpaceMice(int pollingFrequency) { return mouse; } +SpaceMouse* SpaceMouse::getSharedSpaceMouse(int pollingFrequency) +{ + static std::unique_ptr sharedMouse = SpaceMouse::searchSpaceMice(pollingFrequency); + return sharedMouse.get(); +} + #define HANDLE_NONLINEARITY(field, idx) field = copysignf(powf(field, std::get<0>(spacemouse_nonlinearity[idx])) * std::get<1>(spacemouse_nonlinearity[idx]), field) void SpaceMouseMovement::handleNonlinearities(std::array, 6>& spacemouse_nonlinearity) { diff --git a/code/io/spacemouse.h b/code/io/spacemouse.h index 03688299644..4f4ef563f9a 100644 --- a/code/io/spacemouse.h +++ b/code/io/spacemouse.h @@ -57,6 +57,15 @@ namespace io @returns An optional SpaceMouse object, if found */ static std::unique_ptr searchSpaceMice(int pollingFrequency = 10); + + /* + @brief Returns a shared SpaceMouse instance for the process. + + This avoids opening the same HID device from multiple call sites. + @param pollingFrequency Polling frequency to use when creating the shared instance. + @returns A pointer to the shared SpaceMouse instance, or nullptr if no supported device is present. + */ + static SpaceMouse* getSharedSpaceMouse(int pollingFrequency = 10); }; } } \ No newline at end of file diff --git a/qtfred/source_groups.cmake b/qtfred/source_groups.cmake index 43b06165d9b..9b36b37c71e 100644 --- a/qtfred/source_groups.cmake +++ b/qtfred/source_groups.cmake @@ -19,6 +19,8 @@ if (WIN32) endif() add_file_folder("Source/Mission" + src/mission/CameraController.cpp + src/mission/CameraController.h src/mission/Editor.cpp src/mission/EditorWing.cpp src/mission/Editor.h diff --git a/qtfred/src/mission/CameraController.cpp b/qtfred/src/mission/CameraController.cpp new file mode 100644 index 00000000000..e2307099a4f --- /dev/null +++ b/qtfred/src/mission/CameraController.cpp @@ -0,0 +1,108 @@ +#include "CameraController.h" + +#include "ui/ControlBindings.h" + +#include +#include + +namespace fso::fred { + +void CameraController::resetView() { + vec3d f, u, r; + + physics_init(&view_physics); + view_physics.max_vel.xyz.z = 5.0f; + view_physics.max_rotvel.xyz.x = 1.5f; + memset(&view_controls, 0, sizeof(control_info)); + + vm_vec_make(&view_pos, 0.0f, 150.0f, -200.0f); + vm_vec_make(&f, 0.0f, -0.5f, 0.866025404f); + vm_vec_make(&u, 0.0f, 0.866025404f, 0.5f); + vm_vec_make(&r, 1.0f, 0.0f, 0.0f); + vm_vector_2_matrix(&view_orient, &f, &u, &r); +} + +void CameraController::resetViewPhysics() { + physics_init(&view_physics); + view_physics.max_vel.xyz.x *= _physicsSpeed / 3.0f; + view_physics.max_vel.xyz.y *= _physicsSpeed / 3.0f; + view_physics.max_vel.xyz.z *= _physicsSpeed / 3.0f; + view_physics.max_rear_vel *= _physicsSpeed / 3.0f; + view_physics.max_rotvel.xyz.x *= _physicsRot / 30.0f; + view_physics.max_rotvel.xyz.y *= _physicsRot / 30.0f; + view_physics.max_rotvel.xyz.z *= _physicsRot / 30.0f; + view_physics.flags |= PF_ACCELERATES | PF_SLIDE_ENABLED; +} + +void CameraController::savePosition() { + saved_cam_pos = view_pos; + saved_cam_orient = view_orient; +} + +void CameraController::restorePosition() { + view_pos = saved_cam_pos; + view_orient = saved_cam_orient; +} + +bool CameraController::hasSavedPosition() const { + return !IS_VEC_NULL(&saved_cam_orient.vec.fvec); +} + +bool CameraController::hasEyeMoved() { + if (vm_vec_cmp(&eye_pos, &Last_eye_pos) || vm_matrix_cmp(&eye_orient, &Last_eye_orient)) { + Last_eye_pos = eye_pos; + Last_eye_orient = eye_orient; + return true; + } + return false; +} + +const matrix& CameraController::getLastRotMat() const { + return view_physics.last_rotmat; +} + +bool CameraController::processControls(vec3d* pos, matrix* orient, float frametime, bool use_editor_physics) { + io::spacemouse::SpaceMouse* const spacemouse = io::spacemouse::SpaceMouse::getSharedSpaceMouse(0); + + memset(&view_controls, 0, sizeof(control_info)); + + if (spacemouse != nullptr) { + auto spacemouse_movement = spacemouse->getMovement(); + spacemouse_movement.handleNonlinearities(Fred_spacemouse_nonlinearity); + view_controls.pitch += spacemouse_movement.rotation.p; + view_controls.vertical += spacemouse_movement.translation.xyz.z; + view_controls.heading += spacemouse_movement.rotation.h; + view_controls.sideways += spacemouse_movement.translation.xyz.x; + view_controls.bank += spacemouse_movement.rotation.b; + view_controls.forward += spacemouse_movement.translation.xyz.y; + } + + auto& bindings = ControlBindings::instance(); + view_controls.pitch += bindings.isPressed(ControlAction::PitchUp) ? -1.0f : 0.0f; + view_controls.pitch += bindings.isPressed(ControlAction::PitchDown) ? 1.0f : 0.0f; + view_controls.heading += bindings.isPressed(ControlAction::YawLeft) ? -1.0f : 0.0f; + view_controls.heading += bindings.isPressed(ControlAction::YawRight) ? 1.0f : 0.0f; + view_controls.sideways += bindings.isPressed(ControlAction::MoveLeft) ? -1.0f : 0.0f; + view_controls.sideways += bindings.isPressed(ControlAction::MoveRight) ? 1.0f : 0.0f; + view_controls.forward += bindings.isPressed(ControlAction::MoveForward) ? 1.0f : 0.0f; + view_controls.forward += bindings.isPressed(ControlAction::MoveBackward) ? -1.0f : 0.0f; + view_controls.vertical += bindings.isPressed(ControlAction::MoveUp) ? 1.0f : 0.0f; + view_controls.vertical += bindings.isPressed(ControlAction::MoveDown) ? -1.0f : 0.0f; + + bool wantsUpdate = (fabs(view_controls.pitch) > (frametime / 100)) + || (fabs(view_controls.vertical) > (frametime / 100)) + || (fabs(view_controls.heading) > (frametime / 100)) + || (fabs(view_controls.sideways) > (frametime / 100)) + || (fabs(view_controls.bank) > (frametime / 100)) + || (fabs(view_controls.forward) > (frametime / 100)); + + physics_read_flying_controls(orient, &view_physics, &view_controls, frametime); + if (use_editor_physics) { + physics_sim_editor(pos, orient, &view_physics, frametime); + } else { + physics_sim(pos, orient, &view_physics, &vmd_zero_vector, frametime); + } + return wantsUpdate; +} + +} // namespace fso::fred diff --git a/qtfred/src/mission/CameraController.h b/qtfred/src/mission/CameraController.h new file mode 100644 index 00000000000..661d111ef71 --- /dev/null +++ b/qtfred/src/mission/CameraController.h @@ -0,0 +1,66 @@ +#pragma once + +#include +#include + +namespace fso::fred { + +class CameraController { + physics_info view_physics; + control_info view_controls; + + vec3d saved_cam_pos = vmd_zero_vector; + matrix saved_cam_orient = {}; + + vec3d Last_eye_pos = vmd_zero_vector; + matrix Last_eye_orient = vmd_identity_matrix; + + int _physicsSpeed = 1; + int _physicsRot = 25; + int _viewpoint = 0; + int _viewObj = -1; + int _controlMode = 0; + bool _lookatMode = false; + +public: + vec3d eye_pos; + matrix eye_orient; + + vec3d view_pos; + matrix view_orient = vmd_identity_matrix; + + void resetView(); + void resetViewPhysics(); + + void savePosition(); + void restorePosition(); + bool hasSavedPosition() const; + + bool hasEyeMoved(); + + const matrix& getLastRotMat() const; + + // Returns true if the caller should schedule a view update + bool processControls(vec3d* pos, matrix* orient, float frametime, bool use_editor_physics); + + int getPhysicsSpeed() const { return _physicsSpeed; } + void setPhysicsSpeed(int speed) { _physicsSpeed = speed; resetViewPhysics(); } + + int getPhysicsRot() const { return _physicsRot; } + void setPhysicsRot(int rot) { _physicsRot = rot; resetViewPhysics(); } + + int getViewpoint() const { return _viewpoint; } + void setViewpoint(int vp) { _viewpoint = vp; } + + int getViewObj() const { return _viewObj; } + void setViewObj(int obj) { _viewObj = obj; } + + int getControlMode() const { return _controlMode; } + void setControlMode(int mode) { _controlMode = mode; } + void toggleControlMode() { _controlMode = (_controlMode + 1) % 2; } + + bool getLookatMode() const { return _lookatMode; } + void setLookatMode(bool mode) { _lookatMode = mode; } +}; + +} // namespace fso::fred diff --git a/qtfred/src/mission/Editor.cpp b/qtfred/src/mission/Editor.cpp index 2f148873679..64e1d8d755c 100644 --- a/qtfred/src/mission/Editor.cpp +++ b/qtfred/src/mission/Editor.cpp @@ -140,8 +140,8 @@ int Editor::autosave(const char* /*desc*/) { Fred_mission_save save; save.set_always_save_display_names(_lastActiveViewport->Always_save_display_names); - save.set_view_pos(_lastActiveViewport->view_pos); - save.set_view_orient(_lastActiveViewport->view_orient); + save.set_view_pos(_lastActiveViewport->camera.view_pos); + save.set_view_orient(_lastActiveViewport->camera.view_orient); save.set_fred_alt_names(Fred_alt_names); save.set_fred_callsigns(Fred_callsigns); @@ -460,8 +460,8 @@ bool Editor::loadMission(const std::string& mission_name, int flags) { } for (auto& viewport : _viewports) { - viewport->view_orient = Parse_viewer_orient; - viewport->view_pos = Parse_viewer_pos; + viewport->camera.view_orient = Parse_viewer_orient; + viewport->camera.view_pos = Parse_viewer_pos; } if (flags & MPF_IS_TEMPLATE) { @@ -479,8 +479,7 @@ bool Editor::loadMission(const std::string& mission_name, int flags) { strcpy_s(The_mission.mission_desc, "Put mission description here"); for (auto& viewport : _viewports) { - viewport->resetView(); - viewport->resetViewPhysics(); + viewport->reset(); } } @@ -654,8 +653,7 @@ void Editor::clearMission(bool fast_reload) { unmark_all(); for (auto& viewport : _viewports) { - viewport->resetView(); - viewport->resetViewPhysics(); + viewport->reset(); } Event_annotations.clear(); diff --git a/qtfred/src/mission/EditorViewport.cpp b/qtfred/src/mission/EditorViewport.cpp index 05969c9f5f0..cc382babeca 100644 --- a/qtfred/src/mission/EditorViewport.cpp +++ b/qtfred/src/mission/EditorViewport.cpp @@ -1,15 +1,15 @@ #include +#include +#include #include #include #include #include "ui/ControlBindings.h" -#include #include "object.h" #include "EditorViewport.h" #include -#include "ui/dialogs/BriefingEditorDialog.h" #include #include #include @@ -20,50 +20,8 @@ namespace { constexpr auto SETTINGS_GROUP = "Preferences"; -const fix MAX_FRAMETIME = (F1_0 / 4); // Frametime gets saturated at this. -const fix MIN_FRAMETIME = (F1_0 / 120); - const float REDUCER = 100.0f; -void process_movement_keys(const fso::fred::ControlBindings& bindings, vec3d* mvec, angles* angs) { - mvec->xyz.x = 0.0f; - mvec->xyz.y = 0.0f; - mvec->xyz.z = 0.0f; - angs->p = 0.0f; - angs->b = 0.0f; - angs->h = 0.0f; - - if (bindings.isPressed(fso::fred::ControlAction::MoveLeft)) { - mvec->xyz.x += -1.0f; - } - if (bindings.isPressed(fso::fred::ControlAction::MoveRight)) { - mvec->xyz.x += 1.0f; - } - if (bindings.isPressed(fso::fred::ControlAction::MoveForward)) { - mvec->xyz.y += 1.0f; - } - if (bindings.isPressed(fso::fred::ControlAction::MoveBackward)) { - mvec->xyz.y += -1.0f; - } - if (bindings.isPressed(fso::fred::ControlAction::MoveUp)) { - mvec->xyz.z += 1.0f; - } - if (bindings.isPressed(fso::fred::ControlAction::MoveDown)) { - mvec->xyz.z += -1.0f; - } - if (bindings.isPressed(fso::fred::ControlAction::YawLeft)) { - angs->h += -0.1f; - } - if (bindings.isPressed(fso::fred::ControlAction::YawRight)) { - angs->h += 0.1f; - } - if (bindings.isPressed(fso::fred::ControlAction::PitchUp)) { - angs->p += -0.1f; - } - if (bindings.isPressed(fso::fred::ControlAction::PitchDown)) { - angs->p += 0.1f; - } -} void align_vector_to_axis(vec3d* v) { float x, y, z; @@ -112,6 +70,42 @@ namespace fso::fred { const char* EditorViewport::DefaultLayerName = "Default"; +EditorViewport::ViewportControlLock::ViewportControlLock(EditorViewport* viewport) : _viewport(viewport) +{ + if (_viewport != nullptr) { + _viewport->lockControls(); + } +} + +EditorViewport::ViewportControlLock::~ViewportControlLock() +{ + if (_viewport != nullptr) { + _viewport->unlockControls(); + } +} + +EditorViewport::ViewportControlLock::ViewportControlLock(ViewportControlLock&& other) noexcept + : _viewport(other._viewport) +{ + other._viewport = nullptr; +} + +EditorViewport::ViewportControlLock& EditorViewport::ViewportControlLock::operator=( + ViewportControlLock&& other) noexcept +{ + if (this == &other) { + return *this; + } + + if (_viewport != nullptr) { + _viewport->unlockControls(); + } + + _viewport = other._viewport; + other._viewport = nullptr; + return *this; +} + EditorViewport::EditorViewport(Editor* in_editor, std::unique_ptr&& in_renderer) : _renderer(std::move(in_renderer)), editor(in_editor) { renderer = _renderer.get(); @@ -120,9 +114,8 @@ EditorViewport::EditorViewport(Editor* in_editor, std::unique_ptr& vm_vec_make(&Constraint, 1.0f, 0.0f, 1.0f); vm_vec_make(&Anticonstraint, 0.0f, 1.0f, 0.0f); - resetView(); + reset(); - memset(&saved_cam_orient, 0, sizeof(saved_cam_orient)); _layerNames.emplace_back(DefaultLayerName); _layerVisibility.push_back(true); syncMissionLayerNames(); @@ -214,16 +207,51 @@ void EditorViewport::saveSettings() const { void EditorViewport::needsUpdate() { _renderer->scheduleUpdate(); } -void EditorViewport::resetViewPhysics() { - physics_init(&view_physics); - view_physics.max_vel.xyz.x *= physics_speed / 3.0f; - view_physics.max_vel.xyz.y *= physics_speed / 3.0f; - view_physics.max_vel.xyz.z *= physics_speed / 3.0f; - view_physics.max_rear_vel *= physics_speed / 3.0f; - view_physics.max_rotvel.xyz.x *= physics_rot / 30.0f; - view_physics.max_rotvel.xyz.y *= physics_rot / 30.0f; - view_physics.max_rotvel.xyz.z *= physics_rot / 30.0f; - view_physics.flags |= PF_ACCELERATES | PF_SLIDE_ENABLED; + +bool EditorViewport::areControlsLocked() const +{ + return _controlLockCount > 0; +} + +EditorViewport::ViewportControlLock EditorViewport::acquireControlLock() +{ + return ViewportControlLock(this); +} + +void EditorViewport::lockControls() +{ + ++_controlLockCount; +} + +void EditorViewport::unlockControls() +{ + Assertion(_controlLockCount > 0, "Mismatched unlock on EditorViewport controls"); + --_controlLockCount; +} + +bool EditorViewport::incMissionTime() { + const fix MAX_FRAMETIME = (F1_0 / 4); + const fix MIN_FRAMETIME = (F1_0 / 120); + + fix thistime = timer_get_fixed_seconds(); + fix time_diff; + if (!_lasttime) { + time_diff = F1_0 / 30; + } else { + time_diff = thistime - _lasttime; + } + + if (time_diff > MAX_FRAMETIME) { + time_diff = MAX_FRAMETIME; + } else if (time_diff < MIN_FRAMETIME) { + return false; + } + + Frametime = time_diff; + Missiontime += Frametime; + _lasttime = thistime; + + return true; } void EditorViewport::select_objects(const Marking_box& box) { int x, y, valid, icon_mode = 0; @@ -330,56 +358,17 @@ void EditorViewport::select_objects(const Marking_box& box) { needsUpdate(); } -void EditorViewport::resetView() { - my_pos = vmd_zero_vector; - my_pos.xyz.z = -5.0f; - vec3d f, u, r; - - physics_init(&view_physics); - view_physics.max_vel.xyz.z = 5.0f; //forward/backward - view_physics.max_rotvel.xyz.x = 1.5f; //pitch - memset(&view_controls, 0, sizeof(control_info)); - - vm_vec_make(&view_pos, 0.0f, 150.0f, -200.0f); - vm_vec_make(&f, 0.0f, -0.5f, 0.866025404f); // 30 degree angle - vm_vec_make(&u, 0.0f, 0.866025404f, 0.5f); - vm_vec_make(&r, 1.0f, 0.0f, 0.0f); - vm_vector_2_matrix(&view_orient, &f, &u, &r); - +void EditorViewport::reset() { + camera.resetView(); + camera.resetViewPhysics(); The_grid = create_default_grid(); - maybe_create_new_grid(The_grid, &view_pos, &view_orient, 1); - // vm_set_identity(&view_orient); -} - - -void EditorViewport::move_mouse(int btn, int mdx, int mdy) { - int dx, dy; - - dx = mdx - last_x; - dy = mdy - last_y; - last_x = mdx; - last_y = mdy; - - if (btn & 1) { - matrix tempm, mousem; - - if (dx || dy) { - vm_trackball(dx, dy, &mousem); - vm_matrix_x_matrix(&tempm, &trackball_orient, &mousem); - trackball_orient = tempm; - view_orient = trackball_orient; - } - } - - if (btn & 2) { - my_pos.xyz.z += (float) dy; - } + maybe_create_new_grid(The_grid, &camera.view_pos, &camera.view_orient, 1); } /////////////////////////////////////////////////// void EditorViewport::process_system_keys() { auto& bindings = ControlBindings::instance(); - if (dialogs::BriefingEditorDialog::isAnyDialogOpen()) { + if (areControlsLocked()) { return; } if (bindings.takeTriggered(ControlAction::ToggleSelectionLock)) { @@ -388,169 +377,65 @@ void EditorViewport::process_system_keys() { } -void EditorViewport::process_controls(vec3d* pos, matrix* orient, float frametime, int mode) { - static std::unique_ptr spacemouse = io::spacemouse::SpaceMouse::searchSpaceMice(0); - if (dialogs::BriefingEditorDialog::isAnyDialogOpen()) { - return; - } - - if (Flying_controls_mode) { - memset(&view_controls, 0, sizeof(control_info)); - - if (spacemouse != nullptr) { - auto spacemouse_movement = spacemouse->getMovement(); - spacemouse_movement.handleNonlinearities(Fred_spacemouse_nonlinearity); - view_controls.pitch += spacemouse_movement.rotation.p; - view_controls.vertical += spacemouse_movement.translation.xyz.z; - view_controls.heading += spacemouse_movement.rotation.h; - view_controls.sideways += spacemouse_movement.translation.xyz.x; - view_controls.bank += spacemouse_movement.rotation.b; - view_controls.forward += spacemouse_movement.translation.xyz.y; - } - - auto& bindings = ControlBindings::instance(); - view_controls.pitch += bindings.isPressed(ControlAction::PitchUp) ? -1.0f : 0.0f; - view_controls.pitch += bindings.isPressed(ControlAction::PitchDown) ? 1.0f : 0.0f; - view_controls.heading += bindings.isPressed(ControlAction::YawLeft) ? -1.0f : 0.0f; - view_controls.heading += bindings.isPressed(ControlAction::YawRight) ? 1.0f : 0.0f; - view_controls.sideways += bindings.isPressed(ControlAction::MoveLeft) ? -1.0f : 0.0f; - view_controls.sideways += bindings.isPressed(ControlAction::MoveRight) ? 1.0f : 0.0f; - view_controls.forward += bindings.isPressed(ControlAction::MoveForward) ? 1.0f : 0.0f; - view_controls.forward += bindings.isPressed(ControlAction::MoveBackward) ? -1.0f : 0.0f; - view_controls.vertical += bindings.isPressed(ControlAction::MoveUp) ? 1.0f : 0.0f; - view_controls.vertical += bindings.isPressed(ControlAction::MoveDown) ? -1.0f : 0.0f; - - if ((fabs(view_controls.pitch) > (frametime / 100)) || (fabs(view_controls.vertical) > (frametime / 100)) - || (fabs(view_controls.heading) > (frametime / 100)) || (fabs(view_controls.sideways) > (frametime / 100)) - || (fabs(view_controls.bank) > (frametime / 100)) || (fabs(view_controls.forward) > (frametime / 100))) { - needsUpdate(); - } - - //view_physics.flags |= (PF_ACCELERATES | PF_SLIDE_ENABLED); - physics_read_flying_controls(orient, &view_physics, &view_controls, frametime); - if (mode) { - physics_sim_editor(pos, orient, &view_physics, frametime); - } else { - physics_sim(pos, orient, &view_physics, &vmd_zero_vector, frametime); - } - } else { - vec3d movement_vec, rel_movement_vec; - angles rotangs; - matrix newmat, rotmat; - - process_movement_keys(ControlBindings::instance(), &movement_vec, &rotangs); - if (spacemouse != nullptr) { - auto spacemouse_movement = spacemouse->getMovement(); - spacemouse_movement.handleNonlinearities(Fred_spacemouse_nonlinearity); - movement_vec += spacemouse_movement.translation; - rotangs += spacemouse_movement.rotation; - } - - vm_vec_rotate(&rel_movement_vec, &movement_vec, &The_grid->gmatrix); - vm_vec_add2(pos, &rel_movement_vec); - - vm_angles_2_matrix(&rotmat, &rotangs); - if (rotangs.h && view.Universal_heading) { - vm_transpose(orient); - } - vm_matrix_x_matrix(&newmat, orient, &rotmat); - *orient = newmat; - if (rotangs.h && view.Universal_heading) { - vm_transpose(orient); - } - } -} - -/** -* @brief Increments mission time -* -* @details This only increments the mission time if the time difference is greater than the minimum frametime to avoid -* excessive computation -* -* @return @c true if the mission time was incremented, @c false otherwise. -*/ -bool EditorViewport::inc_mission_time() { - fix thistime = timer_get_fixed_seconds(); - fix time_diff; // This holds the computed time difference since the last time this function was called - if (!lasttime) { - time_diff = F1_0 / 30; - } else { - time_diff = thistime - lasttime; - } - - if (time_diff > MAX_FRAMETIME) { - time_diff = MAX_FRAMETIME; - } else if (time_diff < MIN_FRAMETIME) { - return false; - } - - Frametime = time_diff; - Missiontime += Frametime; - lasttime = thistime; - - return true; -} - void EditorViewport::game_do_frame(const int cur_object_index) { int cmode; - vec3d viewer_position, control_pos; + vec3d control_pos; object* objp; matrix control_orient; - if (!inc_mission_time()) { - // Don't do anything if the mission time wasn't incremented + if (!incMissionTime()) { return; } // sync all timestamps across the entire frame timer_start_frame(); - viewer_position = my_orient.vec.fvec; - vm_vec_scale(&viewer_position, my_pos.xyz.z); - - if ((viewpoint == 1) && !query_valid_object(view_obj)) { - viewpoint = 0; + if ((camera.getViewpoint() == 1) && !query_valid_object(camera.getViewObj())) { + camera.setViewpoint(0); } process_system_keys(); - cmode = Control_mode; - if ((viewpoint == 1) && !cmode) { + const auto controlsLocked = areControlsLocked(); + cmode = camera.getControlMode(); + if ((camera.getViewpoint() == 1) && !cmode) { cmode = 2; } control_pos = Last_control_pos; control_orient = Last_control_orient; - // if ((key & KEY_MASK) == key) // unmodified switch (cmode) { case 0: // Control the viewer's location and orientation - process_controls(&view_pos, &view_orient, f2fl(Frametime), 1); - control_pos = view_pos; - control_orient = view_orient; + if (!controlsLocked && camera.processControls(&camera.view_pos, &camera.view_orient, f2fl(Frametime), true)) { + needsUpdate(); + } + control_pos = camera.view_pos; + control_orient = camera.view_orient; break; case 2: // Control viewpoint object - if (!Objects[view_obj].flags[Object::Object_Flags::Locked_from_editing]) { - process_controls(&Objects[view_obj].pos, &Objects[view_obj].orient, f2fl(Frametime)); - object_moved(&Objects[view_obj]); - control_pos = Objects[view_obj].pos; - control_orient = Objects[view_obj].orient; + if (!controlsLocked && !Objects[camera.getViewObj()].flags[Object::Object_Flags::Locked_from_editing]) { + camera.processControls(&Objects[camera.getViewObj()].pos, &Objects[camera.getViewObj()].orient, + f2fl(Frametime), false); + object_moved(&Objects[camera.getViewObj()]); + control_pos = Objects[camera.getViewObj()].pos; + control_orient = Objects[camera.getViewObj()].orient; } break; case 1: // Control the current object's location and orientation - if (query_valid_object(cur_object_index) && !Objects[cur_object_index].flags[Object::Object_Flags::Locked_from_editing]) { + if (!controlsLocked && query_valid_object(cur_object_index) && !Objects[cur_object_index].flags[Object::Object_Flags::Locked_from_editing]) { vec3d delta_pos, leader_old_pos; matrix leader_orient, leader_transpose, tmp; object* leader; leader = &Objects[cur_object_index]; - leader_old_pos = leader->pos; // save original position - leader_orient = leader->orient; // save original orientation + leader_old_pos = leader->pos; + leader_orient = leader->orient; vm_copy_transpose(&leader_transpose, &leader_orient); - process_controls(&leader->pos, &leader->orient, f2fl(Frametime)); - vm_vec_sub(&delta_pos, &leader->pos, &leader_old_pos); // get position change + camera.processControls(&leader->pos, &leader->orient, f2fl(Frametime), false); + vm_vec_sub(&delta_pos, &leader->pos, &leader_old_pos); control_pos = leader->pos; control_orient = leader->orient; @@ -562,36 +447,19 @@ void EditorViewport::game_do_frame(const int cur_object_index) { matrix rot_trans; vec3d tmpv1, tmpv2; - // change rotation matrix to rotate in opposite direction. This rotation - // matrix is what the leader ship has rotated by. - vm_copy_transpose(&rot_trans, &view_physics.last_rotmat); - - // get point relative to our point of rotation (make POR the origin). Since - // only the leader has been moved yet, and not the objects, we have to use - // the old leader's position. + vm_copy_transpose(&rot_trans, &camera.getLastRotMat()); vm_vec_sub(&tmpv1, &objp->pos, &leader_old_pos); - - // convert point from real-world coordinates to leader's relative coordinate - // system (z=forward vec, y=up vec, x=right vec vm_vec_rotate(&tmpv2, &tmpv1, &leader_orient); - - // now rotate the point by the transpose from above. vm_vec_rotate(&tmpv1, &tmpv2, &rot_trans); - - // convert point back into real-world coordinates vm_vec_rotate(&tmpv2, &tmpv1, &leader_transpose); - - // and move origin back to real-world origin. Object is now at its correct - // position. Note we used the leader's new position, instead of old position. vm_vec_add(&objp->pos, &leader->pos, &tmpv2); - // Now fix the object's orientation to what it should be. - vm_matrix_x_matrix(&tmp, &objp->orient, &view_physics.last_rotmat); - vm_orthogonalize_matrix(&tmp); // safety check + vm_matrix_x_matrix(&tmp, &objp->orient, &camera.getLastRotMat()); + vm_orthogonalize_matrix(&tmp); objp->orient = tmp; } else { vm_vec_add2(&objp->pos, &delta_pos); - vm_matrix_x_matrix(&tmp, &objp->orient, &view_physics.last_rotmat); + vm_matrix_x_matrix(&tmp, &objp->orient, &camera.getLastRotMat()); objp->orient = tmp; } } @@ -608,7 +476,6 @@ void EditorViewport::game_do_frame(const int cur_object_index) { objp = GET_NEXT(objp); } - // Notify the editor that the mission has changed editor->missionChanged(); } @@ -618,29 +485,29 @@ void EditorViewport::game_do_frame(const int cur_object_index) { Assert(0); } - if (Lookat_mode && query_valid_object(cur_object_index)) { + if (camera.getLookatMode() && query_valid_object(cur_object_index)) { float dist; - dist = vm_vec_dist(&view_pos, &Objects[cur_object_index].pos); - vm_vec_scale_add(&view_pos, &Objects[cur_object_index].pos, &view_orient.vec.fvec, -dist); + dist = vm_vec_dist(&camera.view_pos, &Objects[cur_object_index].pos); + vm_vec_scale_add(&camera.view_pos, &Objects[cur_object_index].pos, &camera.view_orient.vec.fvec, -dist); } - switch (viewpoint) { + switch (camera.getViewpoint()) { case 0: - eye_pos = view_pos; - eye_orient = view_orient; + camera.eye_pos = camera.view_pos; + camera.eye_orient = camera.view_orient; break; case 1: - eye_pos = Objects[view_obj].pos; - eye_orient = Objects[view_obj].orient; + camera.eye_pos = Objects[camera.getViewObj()].pos; + camera.eye_orient = Objects[camera.getViewObj()].orient; break; default: Assert(0); } - maybe_create_new_grid(The_grid, &eye_pos, &eye_orient); + maybe_create_new_grid(The_grid, &camera.eye_pos, &camera.eye_orient); if (Cursor_over != Last_cursor_over) { Last_cursor_over = Cursor_over; @@ -655,10 +522,8 @@ void EditorViewport::game_do_frame(const int cur_object_index) { } // redraw screen if current viewpoint moved or rotated - if (vm_vec_cmp(&eye_pos, &Last_eye_pos) || vm_matrix_cmp(&eye_orient, &Last_eye_orient)) { + if (camera.hasEyeMoved()) { needsUpdate(); - Last_eye_pos = eye_pos; - Last_eye_orient = eye_orient; } } @@ -666,20 +531,20 @@ void EditorViewport::level_controlled() { int cmode, count = 0; object* objp; - cmode = Control_mode; - if ((viewpoint == 1) && !cmode) { + cmode = camera.getControlMode(); + if ((camera.getViewpoint() == 1) && !cmode) { cmode = 2; } switch (cmode) { case 0: // Control the viewer's location and orientation - level_object(&view_orient); + level_object(&camera.view_orient); break; case 2: // Control viewpoint object - if (!Objects[view_obj].flags[Object::Object_Flags::Locked_from_editing]) { - level_object(&Objects[view_obj].orient); - object_moved(&Objects[view_obj]); + if (!Objects[camera.getViewObj()].flags[Object::Object_Flags::Locked_from_editing]) { + level_object(&Objects[camera.getViewObj()].orient); + object_moved(&Objects[camera.getViewObj()]); ///! \todo Notify. editor->autosave("level object"); editor->missionChanged(); @@ -722,20 +587,20 @@ void EditorViewport::verticalize_controlled() { int cmode, count = 0; object* objp; - cmode = Control_mode; - if ((viewpoint == 1) && !cmode) { + cmode = camera.getControlMode(); + if ((camera.getViewpoint() == 1) && !cmode) { cmode = 2; } switch (cmode) { case 0: // Control the viewer's location and orientation - verticalize_object(&view_orient); + verticalize_object(&camera.view_orient); break; case 2: // Control viewpoint object - if (!Objects[view_obj].flags[Object::Object_Flags::Locked_from_editing]) { - verticalize_object(&Objects[view_obj].orient); - object_moved(&Objects[view_obj]); + if (!Objects[camera.getViewObj()].flags[Object::Object_Flags::Locked_from_editing]) { + verticalize_object(&Objects[camera.getViewObj()].orient); + object_moved(&Objects[camera.getViewObj()]); ///! \todo notify. editor->autosave("align object"); editor->missionChanged(); @@ -885,14 +750,14 @@ int EditorViewport::select_object(int cx, int cy) { return -1; } - p0 = view_pos; + p0 = camera.view_pos; vm_vec_scale_add(&p1, &p0, &v, 100.0f); for (auto objp = GET_FIRST(&obj_used_list); objp != END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp)) { if (object_check_collision(objp, &p0, &p1, &hitpos)) { - hitpos.xyz.x = objp->pos.xyz.x - view_pos.xyz.x; - hitpos.xyz.y = objp->pos.xyz.y - view_pos.xyz.y; - hitpos.xyz.z = objp->pos.xyz.z - view_pos.xyz.z; + hitpos.xyz.x = objp->pos.xyz.x - camera.view_pos.xyz.x; + hitpos.xyz.y = objp->pos.xyz.y - camera.view_pos.xyz.y; + hitpos.xyz.z = objp->pos.xyz.z - camera.view_pos.xyz.z; dist = hitpos.xyz.x * hitpos.xyz.x + hitpos.xyz.y * hitpos.xyz.y + hitpos.xyz.z * hitpos.xyz.z; if (dist < best_dist) { best = OBJ_INDEX(objp); @@ -1312,10 +1177,10 @@ int EditorViewport::create_object(vec3d* pos, int waypoint_instance, bool create vec3d EditorViewport::getCreatePosition(int x, int y, float fallbackDist) { vec3d dir, pos; g3_point_to_vec_delayed(&dir, x, y); - if (fvi_ray_plane(&pos, &The_grid->center, &The_grid->gmatrix.vec.uvec, &view_pos, &dir, 0.0f) >= 0.0f) { + if (fvi_ray_plane(&pos, &The_grid->center, &The_grid->gmatrix.vec.uvec, &camera.view_pos, &dir, 0.0f) >= 0.0f) { return pos; } - vm_vec_scale_add(&pos, &view_pos, &view_orient.vec.fvec, fallbackDist); + vm_vec_scale_add(&pos, &camera.view_pos, &camera.view_orient.vec.fvec, fallbackDist); return pos; } @@ -1487,7 +1352,7 @@ int EditorViewport::drag_objects(int x, int y) */ // Do not move ships that we are currently centered around (Lookat_mode). The vector math will start going haywire and return NAN - if (!query_valid_object(editor->currentObject) || Lookat_mode) + if (!query_valid_object(editor->currentObject) || camera.getLookatMode()) return -1; if (Dup_drag == 1) { @@ -1526,11 +1391,11 @@ int EditorViewport::drag_objects(int x, int y) vec3d tmpObject = obj; tmpAnticonstraint.xyz.x = 0.0f; - r = fvi_ray_plane(&int_pnt, &tmpObject, &tmpAnticonstraint, &view_pos, &cursor_dir, 0.0f); + r = fvi_ray_plane(&int_pnt, &tmpObject, &tmpAnticonstraint, &camera.view_pos, &cursor_dir, 0.0f); // If intersected behind viewer, don't move. Too confusing, not what user wants. - vm_vec_sub(&vec1, &int_pnt, &view_pos); - vm_vec_sub(&vec2, &obj, &view_pos); + vm_vec_sub(&vec1, &int_pnt, &camera.view_pos); + vm_vec_sub(&vec2, &obj, &camera.view_pos); if ((r>=0.0f) && (vm_vec_dot(&vec1, &vec2) >= 0.0f)) { vec3d tmp1; vm_vec_sub( &tmp1, &int_pnt, &obj ); @@ -1544,11 +1409,11 @@ int EditorViewport::drag_objects(int x, int y) } else { // Move in x-z plane, defined by grid. Preserve height. - r = fvi_ray_plane(&int_pnt, &obj, &Anticonstraint, &view_pos, &cursor_dir, 0.0f); + r = fvi_ray_plane(&int_pnt, &obj, &Anticonstraint, &camera.view_pos, &cursor_dir, 0.0f); // If intersected behind viewer, don't move. Too confusing, not what user wants. - vm_vec_sub(&vec1, &int_pnt, &view_pos); - vm_vec_sub(&vec2, &obj, &view_pos); + vm_vec_sub(&vec1, &int_pnt, &camera.view_pos); + vm_vec_sub(&vec2, &obj, &camera.view_pos); if ((r>=0.0f) && (vm_vec_dot(&vec1, &vec2) >= 0.0f)) distance_moved = vm_vec_dist(&obj, &int_pnt); } @@ -1728,7 +1593,7 @@ void EditorViewport::cancel_drag() { void EditorViewport::view_universe(bool just_marked) { int max = 0; float dist, largest = 20.0f; - vec3d center, p1, p2; // center of all the objects collectively + vec3d center, p1, p2; vertex v; object *ptr; @@ -1778,8 +1643,8 @@ void EditorViewport::view_universe(bool just_marked) { } dist = fl_sqrt(largest) + 1.0f; - vm_vec_scale_add(&view_pos, ¢er, &view_orient.vec.fvec, -dist); - g3_set_view_matrix(&view_pos, &view_orient, 0.5f); + vm_vec_scale_add(&camera.view_pos, ¢er, &camera.view_orient.vec.fvec, -dist); + g3_set_view_matrix(&camera.view_pos, &camera.view_orient, 0.5f); ptr = GET_FIRST(&obj_used_list); while (ptr != END_OF_LIST(&obj_used_list)) { @@ -1789,10 +1654,10 @@ void EditorViewport::view_universe(bool just_marked) { if (g3_project_vertex(&v) & PF_OVERFLOW) Int3(); - while (v.codes & CC_OFF) { // is point off screen? - dist += 5.0f; // zoom out a little and check again. - vm_vec_scale_add(&view_pos, ¢er, &view_orient.vec.fvec, -dist); - g3_set_view_matrix(&view_pos, &view_orient, 0.5f); + while (v.codes & CC_OFF) { + dist += 5.0f; + vm_vec_scale_add(&camera.view_pos, ¢er, &camera.view_orient.vec.fvec, -dist); + g3_set_view_matrix(&camera.view_pos, &camera.view_orient, 0.5f); g3_rotate_vertex(&v, &ptr->pos); if (g3_project_vertex(&v) & PF_OVERFLOW) Int3(); @@ -1803,13 +1668,14 @@ void EditorViewport::view_universe(bool just_marked) { } dist *= 1.1f; - vm_vec_scale_add(&view_pos, ¢er, &view_orient.vec.fvec, -dist); - g3_set_view_matrix(&view_pos, &view_orient, 0.5f); + vm_vec_scale_add(&camera.view_pos, ¢er, &camera.view_orient.vec.fvec, -dist); + g3_set_view_matrix(&camera.view_pos, &camera.view_orient, 0.5f); needsUpdate(); } void EditorViewport::view_object(int obj_num) { - vm_vec_scale_add(&view_pos, &Objects[obj_num].pos, &view_orient.vec.fvec, Objects[obj_num].radius * -3.0f); + vm_vec_scale_add(&camera.view_pos, &Objects[obj_num].pos, &camera.view_orient.vec.fvec, + Objects[obj_num].radius * -3.0f); needsUpdate(); } diff --git a/qtfred/src/mission/EditorViewport.h b/qtfred/src/mission/EditorViewport.h index 4dceef9754f..ce19e1a49b4 100644 --- a/qtfred/src/mission/EditorViewport.h +++ b/qtfred/src/mission/EditorViewport.h @@ -1,6 +1,7 @@ #pragma once +#include "CameraController.h" #include "FredRenderer.h" #include "Editor.h" #include "IDialogProvider.h" @@ -49,26 +50,9 @@ struct ViewSettings { class EditorViewport { std::unique_ptr _renderer; //!< Internal, owned pointer - /** - * A lot of this stuff doesn't belong here - * @todo: Move camera stuff into own class - */ - int last_x = 0; - int last_y = 0; - - matrix my_orient = vmd_identity_matrix; - matrix trackball_orient = vmd_identity_matrix; - matrix Last_eye_orient = vmd_identity_matrix; - matrix Last_control_orient = vmd_identity_matrix; int Last_cursor_over = -1; - int Flying_controls_mode = 1; - - fix lasttime = 0; - - bool inc_mission_time(); void process_system_keys(); - void process_controls(vec3d* pos, matrix* orient, float frametime, int mode = 0); void level_object(matrix* orient); void initialSetup(); @@ -82,7 +66,23 @@ class EditorViewport { bool isLayerVisible(size_t layerIndex) const; void syncMissionLayerNames() const; void setObjectLayerByIndex(int objectIndex, size_t layerIndex); + public: + class ViewportControlLock { + public: + explicit ViewportControlLock(EditorViewport* viewport); + ~ViewportControlLock(); + + ViewportControlLock(const ViewportControlLock&) = delete; + ViewportControlLock& operator=(const ViewportControlLock&) = delete; + + ViewportControlLock(ViewportControlLock&& other) noexcept; + ViewportControlLock& operator=(ViewportControlLock&& other) noexcept; + + private: + EditorViewport* _viewport = nullptr; + }; + static const char* DefaultLayerName; enum { @@ -92,17 +92,15 @@ class EditorViewport { EditorViewport(Editor* in_editor, std::unique_ptr&& in_renderer); void needsUpdate(); + bool areControlsLocked() const; + [[nodiscard]] ViewportControlLock acquireControlLock(); - void resetView(); + void reset(); void select_objects(const Marking_box& box); - void resetViewPhysics(); - void game_do_frame(const int cur_object_index); - void move_mouse(int btn, int mdx, int mdy); - int object_check_collision(object* objp, vec3d* p0, vec3d* p1, vec3d* hitpos); int select_object(int cx, int cy); @@ -151,36 +149,19 @@ class EditorViewport { void view_object(int obj_num); - vec3d Last_eye_pos; - - vec3d eye_pos; - vec3d Last_control_pos = vmd_zero_vector; - vec3d my_pos; - matrix eye_orient; - control_info view_controls; + CameraController camera; ViewSettings view; int Cursor_over = -1; CursorMode Editing_mode = CursorMode::Moving; - matrix view_orient = vmd_identity_matrix; - vec3d view_pos; - physics_info view_physics; grid* The_grid; - int physics_speed = 1; - int physics_rot = 25; - vec3d Constraint; vec3d Anticonstraint; bool Single_axis_constraint = false; - int viewpoint = 0; - int view_obj = -1; - - int Control_mode = 0; - bool Selection_lock = false; bool button_down = false; @@ -192,9 +173,6 @@ class EditorViewport { object_orient_pos rotation_backup[MAX_OBJECTS]; - vec3d saved_cam_pos = vmd_zero_vector; - matrix saved_cam_orient; - vec3d original_pos = vmd_zero_vector; bool moved = false; @@ -202,7 +180,6 @@ class EditorViewport { int Duped_wing; bool Group_rotate = true; - bool Lookat_mode = false; int toolbar_icon_size = 24; ///< Toolbar icon size in pixels (16, 24, or 32) bool Offer_autosave_recovery = true; bool Move_ships_when_undocking = true; @@ -225,7 +202,16 @@ class EditorViewport { IDialogProvider* dialogProvider = nullptr; private: + fix _lasttime = 0; + vec3d Last_control_pos = vmd_zero_vector; + matrix Last_control_orient = vmd_identity_matrix; + int _controlLockCount = 0; + + bool incMissionTime(); void loadSettings(); + + void lockControls(); + void unlockControls(); }; } // namespace fso::fred diff --git a/qtfred/src/mission/FredRenderer.cpp b/qtfred/src/mission/FredRenderer.cpp index 8ff7505f4b9..ead4409f1ba 100644 --- a/qtfred/src/mission/FredRenderer.cpp +++ b/qtfred/src/mission/FredRenderer.cpp @@ -569,8 +569,8 @@ void FredRenderer::render_compass() { gr_set_clip(gr_screen.max_w - 100, 0, 100, 100); g3_start_frame(0); // ** Accounted for // required !!! - vm_vec_scale_add2(&eye, &_viewport->eye_orient.vec.fvec, -1.5f); - g3_set_view_matrix(&eye, &_viewport->eye_orient, 1.0f); + vm_vec_scale_add2(&eye, &_viewport->camera.eye_orient.vec.fvec, -1.5f); + g3_set_view_matrix(&eye, &_viewport->camera.eye_orient, 1.0f); v.xyz.x = 1.0f; v.xyz.y = v.xyz.z = 0.0f; @@ -866,7 +866,7 @@ void FredRenderer::render_one_model_htl(object* objp, } else Assert(0); - float size = fl_sqrt(vm_vec_dist(&_viewport->eye_pos, &objp->pos) / 20.0f); + float size = fl_sqrt(vm_vec_dist(&_viewport->camera.eye_pos, &objp->pos) / 20.0f); if (size < LOLLIPOP_SIZE) { size = LOLLIPOP_SIZE; @@ -952,7 +952,7 @@ void FredRenderer::render_frame(int cur_object_index, font::set_font(font::FONT1); light_reset(); - g3_set_view_matrix(&_viewport->eye_pos, &_viewport->eye_orient, 0.5f); + g3_set_view_matrix(&_viewport->camera.eye_pos, &_viewport->camera.eye_orient, 0.5f); // Force max star detail so the editor always shows the full Num_stars count // regardless of the player's graphics quality setting (Detail.num_stars can be 0). @@ -1060,7 +1060,7 @@ void FredRenderer::render_frame(int cur_object_index, } disable_htl(); - sprintf(buf, "(%.1f,%.1f,%.1f)", _viewport->eye_pos.xyz.x, _viewport->eye_pos.xyz.y, _viewport->eye_pos.xyz.z); + sprintf(buf, "(%.1f,%.1f,%.1f)", _viewport->camera.eye_pos.xyz.x, _viewport->camera.eye_pos.xyz.y, _viewport->camera.eye_pos.xyz.z); gr_get_string_size(&w, &h, buf); gr_set_color_fast(&colour_white); gr_string(gr_screen.max_w - w - 2, 2, buf); @@ -1082,7 +1082,7 @@ void FredRenderer::render_frame(int cur_object_index, gr_reset_clip(); g3_start_frame(0); // ** Accounted for - g3_set_view_matrix(&_viewport->eye_pos, &_viewport->eye_orient, 0.5f); + g3_set_view_matrix(&_viewport->camera.eye_pos, &_viewport->camera.eye_orient, 0.5f); } void FredRenderer::resize(int width, int height) { // Make sure the following call targets the right view port diff --git a/qtfred/src/ui/FredView.cpp b/qtfred/src/ui/FredView.cpp index c186c79737d..52b2291cf90 100644 --- a/qtfred/src/ui/FredView.cpp +++ b/qtfred/src/ui/FredView.cpp @@ -206,9 +206,8 @@ void FredView::setEditor(Editor* editor, EditorViewport* viewport) { QSettings settings; _tbLocalMove = settings.value("FredView/transformLocalMove", false).toBool(); _tbLocalRotate = settings.value("FredView/transformLocalRotate", false).toBool(); - _viewport->physics_speed = settings.value("FredView/cameraSpeedMove", 1).toInt(); - _viewport->physics_rot = settings.value("FredView/cameraSpeedRot", 25).toInt(); - _viewport->resetViewPhysics(); + _viewport->camera.setPhysicsSpeed(settings.value("FredView/cameraSpeedMove", 1).toInt()); + _viewport->camera.setPhysicsRot(settings.value("FredView/cameraSpeedRot", 25).toInt()); } connect(fred, &Editor::missionLoaded, this, &FredView::on_mission_loaded); @@ -235,11 +234,11 @@ void FredView::setEditor(Editor* editor, EditorViewport* viewport) { &FredView::viewIdle, this, [this]() { ui->actionZoomSelected->setEnabled(query_valid_object(fred->currentObject)); }); - connect(this, &FredView::viewIdle, this, [this]() { ui->actionOrbitSelected->setChecked(_viewport->Lookat_mode); }); + connect(this, &FredView::viewIdle, this, [this]() { ui->actionOrbitSelected->setChecked(_viewport->camera.getLookatMode()); }); connect(this, &FredView::viewIdle, this, - [this]() { ui->actionRestore_Camera_Pos->setEnabled(!IS_VEC_NULL(&_viewport->saved_cam_orient.vec.fvec)); }); + [this]() { ui->actionRestore_Camera_Pos->setEnabled(_viewport->camera.hasSavedPosition()); }); connect(this, &FredView::viewIdle, this, [this]() { ui->actionRevert->setEnabled(!saveName.isEmpty()); }); connect(this, &FredView::viewIdle, this, [this]() { ui->actionUndo->setEnabled(fred->undoAvailable != 0); }); connect(this, &FredView::viewIdle, this, [this]() { ui->actionDisable_Undo->setChecked(fred->autosaveDisabled != 0); }); @@ -342,8 +341,8 @@ bool FredView::saveMissionToCurrentPath() { Fred_mission_save save; save.set_save_format(_missionSaveFormat); save.set_always_save_display_names(_viewport->Always_save_display_names); - save.set_view_pos(_viewport->view_pos); - save.set_view_orient(_viewport->view_orient); + save.set_view_pos(_viewport->camera.view_pos); + save.set_view_orient(_viewport->camera.view_orient); save.set_fred_alt_names(Fred_alt_names); save.set_fred_callsigns(Fred_callsigns); @@ -359,8 +358,8 @@ bool FredView::saveMissionAs() { Fred_mission_save save; save.set_save_format(_missionSaveFormat); save.set_always_save_display_names(_viewport->Always_save_display_names); - save.set_view_pos(_viewport->view_pos); - save.set_view_orient(_viewport->view_orient); + save.set_view_pos(_viewport->camera.view_pos); + save.set_view_orient(_viewport->camera.view_orient); save.set_fred_alt_names(Fred_alt_names); save.set_fred_callsigns(Fred_callsigns); @@ -464,14 +463,14 @@ void FredView::on_actionRevert_triggered(bool) { void FredView::on_actionUndo_triggered(bool) { // Preserve camera state and saveName because autoload() triggers missionLoaded which would overwrite them - auto savedViewPos = _viewport->view_pos; - auto savedViewOrient = _viewport->view_orient; + auto savedViewPos = _viewport->camera.view_pos; + auto savedViewOrient = _viewport->camera.view_orient; auto savedSaveName = saveName; fred->autoload(); - _viewport->view_pos = savedViewPos; - _viewport->view_orient = savedViewOrient; + _viewport->camera.view_pos = savedViewPos; + _viewport->camera.view_orient = savedViewOrient; saveName = savedSaveName; } @@ -544,8 +543,8 @@ void FredView::on_actionFS1_Mission_triggered(bool) { Fred_mission_save fileSave; fileSave.set_save_format(_missionSaveFormat); fileSave.set_always_save_display_names(_viewport->Always_save_display_names); - fileSave.set_view_pos(_viewport->view_pos); - fileSave.set_view_orient(_viewport->view_orient); + fileSave.set_view_pos(_viewport->camera.view_pos); + fileSave.set_view_orient(_viewport->camera.view_orient); fileSave.set_fred_alt_names(Fred_alt_names); fileSave.set_fred_callsigns(Fred_callsigns); @@ -1024,8 +1023,7 @@ void FredView::initializeTransformBar() { _transformToolBar->addWidget(_transformMoveSpeedCombo); connect(_transformMoveSpeedCombo, QOverload::of(&QComboBox::activated), this, [this](int idx) { if (!_viewport) return; - _viewport->physics_speed = _transformMoveSpeedCombo->itemData(idx).toInt(); - _viewport->resetViewPhysics(); + _viewport->camera.setPhysicsSpeed(_transformMoveSpeedCombo->itemData(idx).toInt()); }); addFixedSpacer(8); @@ -1044,8 +1042,7 @@ void FredView::initializeTransformBar() { _transformToolBar->addWidget(_transformRotSpeedCombo); connect(_transformRotSpeedCombo, QOverload::of(&QComboBox::activated), this, [this](int idx) { if (!_viewport) return; - _viewport->physics_rot = _transformRotSpeedCombo->itemData(idx).toInt(); - _viewport->resetViewPhysics(); + _viewport->camera.setPhysicsRot(_transformRotSpeedCombo->itemData(idx).toInt()); }); addFixedSpacer(8); @@ -1420,8 +1417,8 @@ void FredView::updateUI() { _statusBarUnitsLabel->setText(tr("Units = %1 Meters").arg(_viewport->The_grid->square_size)); setWindowModified(isMissionModified()); - if (_viewport->viewpoint == 1) { - _statusBarViewmode->setText(tr("Viewpoint: %1").arg(object_name(_viewport->view_obj))); + if (_viewport->camera.getViewpoint() == 1) { + _statusBarViewmode->setText(tr("Viewpoint: %1").arg(object_name(_viewport->camera.getViewObj()))); } else { _statusBarViewmode->setText(tr("Viewpoint: Camera")); } @@ -2040,8 +2037,8 @@ void FredView::closeEvent(QCloseEvent* event) { settings.setValue("FredView/transformLocalMove", _tbLocalMove); settings.setValue("FredView/transformLocalRotate", _tbLocalRotate); if (_viewport) { - settings.setValue("FredView/cameraSpeedMove", _viewport->physics_speed); - settings.setValue("FredView/cameraSpeedRot", _viewport->physics_rot); + settings.setValue("FredView/cameraSpeedMove", _viewport->camera.getPhysicsSpeed()); + settings.setValue("FredView/cameraSpeedRot", _viewport->camera.getPhysicsRot()); } if (!maybePromptToSaveMissionChanges(tr("closing QtFRED"))) { @@ -2070,26 +2067,26 @@ void FredView::on_actionUnlock_All_Objects_triggered(bool /*enabled*/) { fred->unlockAllObjects(); } void FredView::onUpdateViewSpeeds() { - ui->actionx1->setChecked(_viewport->physics_speed == 1); - ui->actionx2->setChecked(_viewport->physics_speed == 2); - ui->actionx3->setChecked(_viewport->physics_speed == 3); - ui->actionx5->setChecked(_viewport->physics_speed == 5); - ui->actionx8->setChecked(_viewport->physics_speed == 8); - ui->actionx10->setChecked(_viewport->physics_speed == 10); - ui->actionx50->setChecked(_viewport->physics_speed == 50); - ui->actionx100->setChecked(_viewport->physics_speed == 100); - - ui->actionRotx1->setChecked(_viewport->physics_rot == 2); - ui->actionRotx5->setChecked(_viewport->physics_rot == 10); - ui->actionRotx12->setChecked(_viewport->physics_rot == 25); - ui->actionRotx25->setChecked(_viewport->physics_rot == 50); - ui->actionRotx50->setChecked(_viewport->physics_rot == 100); + ui->actionx1->setChecked(_viewport->camera.getPhysicsSpeed() == 1); + ui->actionx2->setChecked(_viewport->camera.getPhysicsSpeed() == 2); + ui->actionx3->setChecked(_viewport->camera.getPhysicsSpeed() == 3); + ui->actionx5->setChecked(_viewport->camera.getPhysicsSpeed() == 5); + ui->actionx8->setChecked(_viewport->camera.getPhysicsSpeed() == 8); + ui->actionx10->setChecked(_viewport->camera.getPhysicsSpeed() == 10); + ui->actionx50->setChecked(_viewport->camera.getPhysicsSpeed() == 50); + ui->actionx100->setChecked(_viewport->camera.getPhysicsSpeed() == 100); + + ui->actionRotx1->setChecked(_viewport->camera.getPhysicsRot() == 2); + ui->actionRotx5->setChecked(_viewport->camera.getPhysicsRot() == 10); + ui->actionRotx12->setChecked(_viewport->camera.getPhysicsRot() == 25); + ui->actionRotx25->setChecked(_viewport->camera.getPhysicsRot() == 50); + ui->actionRotx50->setChecked(_viewport->camera.getPhysicsRot() == 100); // Keep the bottom-bar combos in sync (covers changes made via keyboard shortcuts or menu). if (_transformMoveSpeedCombo) { QSignalBlocker bl(_transformMoveSpeedCombo); for (int i = 0; i < _transformMoveSpeedCombo->count(); ++i) { - if (_transformMoveSpeedCombo->itemData(i).toInt() == _viewport->physics_speed) { + if (_transformMoveSpeedCombo->itemData(i).toInt() == _viewport->camera.getPhysicsSpeed()) { _transformMoveSpeedCombo->setCurrentIndex(i); break; } @@ -2098,7 +2095,7 @@ void FredView::onUpdateViewSpeeds() { if (_transformRotSpeedCombo) { QSignalBlocker bl(_transformRotSpeedCombo); for (int i = 0; i < _transformRotSpeedCombo->count(); ++i) { - if (_transformRotSpeedCombo->itemData(i).toInt() == _viewport->physics_rot) { + if (_transformRotSpeedCombo->itemData(i).toInt() == _viewport->camera.getPhysicsRot()) { _transformRotSpeedCombo->setCurrentIndex(i); break; } @@ -2107,112 +2104,99 @@ void FredView::onUpdateViewSpeeds() { } void FredView::on_actionx1_triggered(bool enabled) { if (enabled) { - _viewport->physics_speed = 1; - _viewport->resetViewPhysics(); + _viewport->camera.setPhysicsSpeed(1); } } void FredView::on_actionx2_triggered(bool enabled) { if (enabled) { - _viewport->physics_speed = 2; - _viewport->resetViewPhysics(); + _viewport->camera.setPhysicsSpeed(2); } } void FredView::on_actionx3_triggered(bool enabled) { if (enabled) { - _viewport->physics_speed = 3; - _viewport->resetViewPhysics(); + _viewport->camera.setPhysicsSpeed(3); } } void FredView::on_actionx5_triggered(bool enabled) { if (enabled) { - _viewport->physics_speed = 5; - _viewport->resetViewPhysics(); + _viewport->camera.setPhysicsSpeed(5); } } void FredView::on_actionx8_triggered(bool enabled) { if (enabled) { - _viewport->physics_speed = 8; - _viewport->resetViewPhysics(); + _viewport->camera.setPhysicsSpeed(8); } } void FredView::on_actionx10_triggered(bool enabled) { if (enabled) { - _viewport->physics_speed = 10; - _viewport->resetViewPhysics(); + _viewport->camera.setPhysicsSpeed(10); } } void FredView::on_actionx50_triggered(bool enabled) { if (enabled) { - _viewport->physics_speed = 50; - _viewport->resetViewPhysics(); + _viewport->camera.setPhysicsSpeed(50); } } void FredView::on_actionx100_triggered(bool enabled) { if (enabled) { - _viewport->physics_speed = 100; - _viewport->resetViewPhysics(); + _viewport->camera.setPhysicsSpeed(100); } } void FredView::on_actionRotx1_triggered(bool enabled) { if (enabled) { - _viewport->physics_rot = 2; - _viewport->resetViewPhysics(); + _viewport->camera.setPhysicsRot(2); } } void FredView::on_actionRotx5_triggered(bool enabled) { if (enabled) { - _viewport->physics_rot = 10; - _viewport->resetViewPhysics(); + _viewport->camera.setPhysicsRot(10); } } void FredView::on_actionRotx12_triggered(bool enabled) { if (enabled) { - _viewport->physics_rot = 25; - _viewport->resetViewPhysics(); + _viewport->camera.setPhysicsRot(25); } } void FredView::on_actionRotx25_triggered(bool enabled) { if (enabled) { - _viewport->physics_rot = 50; - _viewport->resetViewPhysics(); + _viewport->camera.setPhysicsRot(50); } } void FredView::on_actionRotx50_triggered(bool enabled) { if (enabled) { - _viewport->physics_rot = 100; - _viewport->resetViewPhysics(); + _viewport->camera.setPhysicsRot(100); } } void FredView::onUpdateCameraControlActions() { - ui->actionCamera->setChecked(_viewport->viewpoint == 0); - ui->actionCurrent_Ship->setChecked(_viewport->viewpoint == 1); + ui->actionCamera->setChecked(_viewport->camera.getViewpoint() == 0); + ui->actionCurrent_Ship->setChecked(_viewport->camera.getViewpoint() == 1); - _controlModeCamera->setChecked(_viewport->Control_mode == 0); - _controlModeCurrentShip->setChecked(_viewport->Control_mode == 1); + _controlModeCamera->setChecked(_viewport->camera.getControlMode() == 0); + _controlModeCurrentShip->setChecked(_viewport->camera.getControlMode() == 1); } void FredView::on_actionCamera_triggered(bool enabled) { if (enabled) { - _viewport->viewpoint = 0; + _viewport->camera.setViewpoint(0); _viewport->needsUpdate(); } } void FredView::on_actionCurrent_Ship_triggered(bool enabled) { if (enabled) { - _viewport->viewpoint = 1; - _viewport->view_obj = fred->currentObject; + _viewport->camera.setViewpoint(1); + _viewport->camera.setViewObj(fred->currentObject); _viewport->needsUpdate(); } } void FredView::on_actionControlModeCamera_triggered(bool enabled) { if (enabled) { - _viewport->Control_mode = 0; + _viewport->camera.setControlMode(0); } } void FredView::on_actionControlModeCurrentShip_triggered(bool enabled) { if (enabled) { - _viewport->Control_mode = 1; + _viewport->camera.setControlMode(1); } } @@ -2638,28 +2622,25 @@ void FredView::on_actionSelectionList_triggered(bool checked) { } } void FredView::on_actionOrbitSelected_triggered(bool enabled) { - _viewport->Lookat_mode = enabled; - if (_viewport->Lookat_mode && query_valid_object(fred->currentObject)) { + _viewport->camera.setLookatMode(enabled); + if (_viewport->camera.getLookatMode() && query_valid_object(fred->currentObject)) { vec3d v, loc; matrix m; loc = Objects[fred->currentObject].pos; - vm_vec_sub(&v, &loc, &_viewport->view_pos); + vm_vec_sub(&v, &loc, &_viewport->camera.view_pos); if (v.xyz.x || v.xyz.y || v.xyz.z) { vm_vector_2_matrix(&m, &v, NULL, NULL); - _viewport->view_orient = m; + _viewport->camera.view_orient = m; } } } void FredView::on_actionSave_Camera_Pos_triggered(bool) { - _viewport->saved_cam_pos = _viewport->view_pos; - _viewport->saved_cam_orient = _viewport->view_orient; + _viewport->camera.savePosition(); } void FredView::on_actionRestore_Camera_Pos_triggered(bool) { - _viewport->view_pos = _viewport->saved_cam_pos; - _viewport->view_orient = _viewport->saved_cam_orient; - + _viewport->camera.restorePosition(); _viewport->needsUpdate(); } void FredView::on_actionClone_Marked_Objects_triggered(bool) { @@ -2745,7 +2726,7 @@ void FredView::onSetGroup(int group) { fred->updateAllViewports(); } void FredView::on_actionControl_Object_triggered(bool) { - _viewport->Control_mode = (_viewport->Control_mode + 1) % 2; + _viewport->camera.toggleControlMode(); } void FredView::on_actionLevel_Object_triggered(bool) { _viewport->level_controlled(); diff --git a/qtfred/src/ui/dialogs/BriefingEditorDialog.cpp b/qtfred/src/ui/dialogs/BriefingEditorDialog.cpp index 695c53d5e87..6b56b2294ad 100644 --- a/qtfred/src/ui/dialogs/BriefingEditorDialog.cpp +++ b/qtfred/src/ui/dialogs/BriefingEditorDialog.cpp @@ -15,7 +15,6 @@ #include #include -#include #include #include @@ -27,8 +26,6 @@ namespace fso::fred::dialogs { -int BriefingEditorDialog::_openDialogCount = 0; - namespace { float movementSpeedScaleForIndex(int index) { switch (index) { @@ -78,7 +75,7 @@ BriefingEditorDialog::BriefingEditorDialog(FredView* parent, EditorViewport* vie : QDialog(parent), SexpTreeEditorInterface(flagset()), ui(new Ui::BriefingEditorDialog()), _model(new BriefingEditorDialogModel(this, viewport)), _viewport(viewport) { - ++_openDialogCount; + _viewportLock.emplace(_viewport->acquireControlLock()); this->setFocus(); ui->setupUi(this); @@ -100,11 +97,7 @@ BriefingEditorDialog::BriefingEditorDialog(FredView* parent, EditorViewport* vie } BriefingEditorDialog::~BriefingEditorDialog() { - _openDialogCount = std::max(0, _openDialogCount - 1); -} - -bool BriefingEditorDialog::isAnyDialogOpen() { - return _openDialogCount > 0; + _viewportLock.reset(); } void BriefingEditorDialog::accept() @@ -115,6 +108,7 @@ void BriefingEditorDialog::accept() ui->defaultMusicWidget->stopPlayback(); ui->musicPackWidget->stopPlayback(); QDialog::accept(); + _viewportLock.reset(); // unlock before restoring the grid so the viewport can process controls again create_default_grid(); // restore the grid back to the normal version } // else: validation failed, don't close @@ -129,6 +123,7 @@ void BriefingEditorDialog::reject() ui->defaultMusicWidget->stopPlayback(); ui->musicPackWidget->stopPlayback(); QDialog::reject(); // actually close + _viewportLock.reset(); // unlock before restoring the grid so the viewport can process controls again create_default_grid(); // restore the grid back to the normal version } // else: do nothing, don't close diff --git a/qtfred/src/ui/dialogs/BriefingEditorDialog.h b/qtfred/src/ui/dialogs/BriefingEditorDialog.h index 1bed0b4cc63..95d24d42ea3 100644 --- a/qtfred/src/ui/dialogs/BriefingEditorDialog.h +++ b/qtfred/src/ui/dialogs/BriefingEditorDialog.h @@ -24,7 +24,6 @@ class BriefingEditorDialog : public QDialog, public SexpTreeEditorInterface { public: explicit BriefingEditorDialog(FredView* parent, EditorViewport* viewport); ~BriefingEditorDialog() override; - static bool isAnyDialogOpen(); void accept() override; void reject() override; @@ -95,7 +94,7 @@ class BriefingEditorDialog : public QDialog, public SexpTreeEditorInterface { std::unique_ptr _model; EditorViewport* _viewport; fso::fred::BriefingMapWidget* _mapWidget = nullptr; - static int _openDialogCount; + std::optional _viewportLock; void initializeUi(); void setupMapWidget(); diff --git a/qtfred/src/ui/widgets/BriefingMapWidget.cpp b/qtfred/src/ui/widgets/BriefingMapWidget.cpp index 58154393ac3..92f5d3fedb0 100644 --- a/qtfred/src/ui/widgets/BriefingMapWidget.cpp +++ b/qtfred/src/ui/widgets/BriefingMapWidget.cpp @@ -191,6 +191,11 @@ void BriefingMapWidget::initBriefingMap() { Briefing = savedBriefing; } + // Initialize the camera controller physics so it is ready before the first frame. + // The dialog's combo-box handlers will call setMovementSpeedScale/setRotationSpeedScale + // after the widget is shown, which will re-apply the correct speed values. + _cameraController.resetViewPhysics(); + _initialized = true; _renderTimer->start(); mprintf(("BriefingMapWidget: init complete, timer started for render diagnostics.\n")); @@ -277,6 +282,7 @@ void BriefingMapWidget::applyStageTransition(int stageNum, int transitionTime) { Brief_text_wipe_time_elapsed = BRIEF_TEXT_WIPE_TIME + 1.0f; brief_reset_icons(stageNum); _currentStage = stageNum; + _cameraController.resetViewPhysics(); // clear residual velocity from previous stage Briefing = savedBriefing; } @@ -427,11 +433,13 @@ void BriefingMapWidget::applyCameraToCurrentStage(const vec3d& pos, const matrix } void BriefingMapWidget::setMovementSpeedScale(float scale) { - _movementSpeedScale = std::max(0.01f, scale); + // Dialog sends 4.0, 8.0, 16.0; map to physicsSpeed 1, 2, 4 preserving the 1:2:4 ratio. + _cameraController.setPhysicsSpeed(std::max(1, fl2ir(scale / 4.0f))); } void BriefingMapWidget::setRotationSpeedScale(float scale) { - _rotationSpeedScale = std::max(0.01f, scale); + // Dialog sends 0.0625, 0.125, 0.25; map to physicsRot 8, 15, 30 (max_rotvel *= physicsRot/30). + _cameraController.setPhysicsRot(std::max(1, fl2ir(scale * 120.0f))); } void BriefingMapWidget::applyCameraPoseLikeKeyboardControls(const vec3d& camPos, const matrix& camOrient, bool updateModel) { @@ -521,7 +529,7 @@ void BriefingMapWidget::renderFrame() { gr_use_viewport(mainView); gr_screen_resize(static_cast(mainSize.first), static_cast(mainSize.second)); g3_start_frame(0); - g3_set_view_matrix(&_viewport->eye_pos, &_viewport->eye_orient, 0.5f); + g3_set_view_matrix(&_viewport->camera.eye_pos, &_viewport->camera.eye_orient, 0.5f); } _rendering = false; return; @@ -611,7 +619,7 @@ void BriefingMapWidget::renderFrame() { gr_use_viewport(mainView); gr_screen_resize(static_cast(mainSize.first), static_cast(mainSize.second)); g3_start_frame(0); - g3_set_view_matrix(&_viewport->eye_pos, &_viewport->eye_orient, 0.5f); + g3_set_view_matrix(&_viewport->camera.eye_pos, &_viewport->camera.eye_orient, 0.5f); } _rendering = false; @@ -656,47 +664,21 @@ void BriefingMapWidget::keyReleaseEvent(QKeyEvent* event) { } void BriefingMapWidget::applyBoundCameraControls(float frametime) { - auto& bindings = ControlBindings::instance(); - vec3d camPos = brief_get_current_cam_pos(); - matrix camOrient = brief_get_current_cam_orient(); - const auto oldPos = camPos; - const auto oldOrient = camOrient; - - vec3d movementVec = ZERO_VECTOR; - angles rotangs{}; - - movementVec.xyz.x += bindings.isPressed(ControlAction::MoveLeft) ? -1.0f : 0.0f; - movementVec.xyz.x += bindings.isPressed(ControlAction::MoveRight) ? 1.0f : 0.0f; - movementVec.xyz.y += bindings.isPressed(ControlAction::MoveForward) ? 1.0f : 0.0f; - movementVec.xyz.y += bindings.isPressed(ControlAction::MoveBackward) ? -1.0f : 0.0f; - movementVec.xyz.z += bindings.isPressed(ControlAction::MoveUp) ? 1.0f : 0.0f; - movementVec.xyz.z += bindings.isPressed(ControlAction::MoveDown) ? -1.0f : 0.0f; - rotangs.h += bindings.isPressed(ControlAction::YawLeft) ? -0.1f * _rotationSpeedScale : 0.0f; - rotangs.h += bindings.isPressed(ControlAction::YawRight) ? 0.1f * _rotationSpeedScale : 0.0f; - rotangs.p += bindings.isPressed(ControlAction::PitchUp) ? -0.1f * _rotationSpeedScale : 0.0f; - rotangs.p += bindings.isPressed(ControlAction::PitchDown) ? 0.1f * _rotationSpeedScale : 0.0f; - - const auto frameScale = std::max(frametime * 30.0f * _movementSpeedScale, 0.0f); - if (movementVec.xyz.x != 0.0f) { - vm_vec_scale_add2(&camPos, &camOrient.vec.rvec, movementVec.xyz.x * frameScale); - } - if (movementVec.xyz.y != 0.0f) { - vm_vec_scale_add2(&camPos, &camOrient.vec.fvec, movementVec.xyz.y * frameScale); - } - if (movementVec.xyz.z != 0.0f) { - vm_vec_scale_add2(&camPos, &camOrient.vec.uvec, movementVec.xyz.z * frameScale); - } - - if (rotangs.p != 0.0f || rotangs.h != 0.0f || rotangs.b != 0.0f) { - matrix rotmat; - matrix newmat; - vm_angles_2_matrix(&rotmat, &rotangs); - vm_matrix_x_matrix(&newmat, &camOrient, &rotmat); - camOrient = newmat; - } - - if (vm_vec_cmp(&oldPos, &camPos) || vm_matrix_cmp(&oldOrient, &camOrient)) { - applyCameraPoseLikeKeyboardControls(camPos, camOrient, true); + // Sync from briefing globals each frame so externally-driven moves + // (stage transitions, paste, coordinates dialog) are picked up before + // applying user input via the shared CameraController. + _cameraController.view_pos = brief_get_current_cam_pos(); + _cameraController.view_orient = brief_get_current_cam_orient(); + + if (_cameraController.processControls( + &_cameraController.view_pos, + &_cameraController.view_orient, + frametime, + false)) { + applyCameraPoseLikeKeyboardControls( + _cameraController.view_pos, + _cameraController.view_orient, + true); } } diff --git a/qtfred/src/ui/widgets/BriefingMapWidget.h b/qtfred/src/ui/widgets/BriefingMapWidget.h index bdd0835ed44..3bdcc3d4d99 100644 --- a/qtfred/src/ui/widgets/BriefingMapWidget.h +++ b/qtfred/src/ui/widgets/BriefingMapWidget.h @@ -5,6 +5,7 @@ #include #include "globalincs/pstypes.h" +#include "mission/CameraController.h" #include "osapi/osapi.h" #include "ui/QtGraphicsOperations.h" @@ -92,6 +93,8 @@ class BriefingMapWidget : public QWidget { void applyCameraPoseLikeKeyboardControls(const vec3d& camPos, const matrix& camOrient, bool updateModel); void applyBoundCameraControls(float frametime); + CameraController _cameraController; + BriefingMapWindow* _window = nullptr; QTimer* _renderTimer = nullptr; std::unique_ptr _briefingViewport; // our os::Viewport for gr_use_viewport @@ -126,8 +129,6 @@ class BriefingMapWidget : public QWidget { int _cutFadeFrame = 0; int _pendingCutStage = -1; - float _movementSpeedScale = 1.0f; - float _rotationSpeedScale = 1.0f; }; } // namespace fso::fred