diff --git a/src/render/camera/ArcCamera.cpp b/src/render/camera/ArcCamera.cpp index 166b810..ab7e286 100644 --- a/src/render/camera/ArcCamera.cpp +++ b/src/render/camera/ArcCamera.cpp @@ -92,7 +92,7 @@ void ArcCamera::handleKeyRelease(const int key) { } void ArcCamera::move(const float deltaTime) { - const auto velocity = moveSpeed * deltaTime; + const auto velocity = moveSpeed * moveSpeedSizeScale * deltaTime; const auto cameraDirection = glm::normalize(position - target); const auto right = glm::normalize(glm::cross(cameraDirection, world_up)); const auto up = glm::normalize(glm::cross(right, cameraDirection)); diff --git a/src/render/camera/ArcCamera.h b/src/render/camera/ArcCamera.h index e9470b0..4ca33ac 100644 --- a/src/render/camera/ArcCamera.h +++ b/src/render/camera/ArcCamera.h @@ -64,6 +64,7 @@ class ArcCamera { const glm::vec3 world_up{0.0f, 1.0f, 0.0f}; float mouseTurnSpeed; float moveSpeed; + float moveSpeedSizeScale{1.0f}; float keyboardTurnSpeed; float fieldOfView; float zoomSpeed{0.5f}; // TODO: Make configurable diff --git a/src/render/camera/Camera.cpp b/src/render/camera/Camera.cpp index 1330048..22d37ee 100644 --- a/src/render/camera/Camera.cpp +++ b/src/render/camera/Camera.cpp @@ -138,8 +138,8 @@ glm::mat4 Camera::view_matrix() const { return glm::lookAt(position, position + front, up); } -void Camera::move(float delta_time) { - auto velocity = move_speed * delta_time; +void Camera::move(const float delta_time) { + const auto velocity = move_speed * moveSpeedSizeScale * delta_time; if (active.front_back == active_directions::direction::forward) position += front * velocity; @@ -164,7 +164,7 @@ void Camera::move(float delta_time) { if (active.turn == active_directions::side::none) return; - auto turnVelocity = turnSpeed * delta_time; + const auto turnVelocity = turnSpeed * delta_time; if (active.turn == active_directions::side::left) yaw -= turnVelocity; else if (active.turn == active_directions::side::right) @@ -191,6 +191,14 @@ void Camera::mouse_move(float delta_x, float delta_y) { update(); } +float Camera::getMoveSpeedSizeScale() const { + return moveSpeedSizeScale; +} + +void Camera::setMoveSpeedSizeScale(const float value) { + moveSpeedSizeScale = value; +} + Camera::move_state Camera::getMobility() const { return mobility; } diff --git a/src/render/camera/Camera.h b/src/render/camera/Camera.h index e0cb9b4..98d4e35 100644 --- a/src/render/camera/Camera.h +++ b/src/render/camera/Camera.h @@ -75,6 +75,7 @@ class Camera { float yaw{-90.0f}; float pitch{0.0f}; + float moveSpeedSizeScale{1.0f}; float move_speed{0.05f}; float turnSpeed{0.1}; float mouseTurnSpeed{0.5f}; @@ -133,6 +134,9 @@ class Camera { void move(float delta_time); void mouse_move(float delta_x, float delta_y); + [[nodiscard]] float getMoveSpeedSizeScale() const; + void setMoveSpeedSizeScale(float value); + [[nodiscard]] glm::vec3 get_position() const; void setPosition(const glm::vec3 &value); diff --git a/src/render/helper/Floor.cpp b/src/render/helper/Floor.cpp index b2f8763..913ba4c 100644 --- a/src/render/helper/Floor.cpp +++ b/src/render/helper/Floor.cpp @@ -72,4 +72,12 @@ texture_id Floor::getTextureId() const { return *mesh.getMaterial().textureId; } +float Floor::getSize() const { + return size; +} + +void Floor::setSize(const float value) { + size = value; +} + } // namespace netsimulyzer diff --git a/src/render/helper/Floor.h b/src/render/helper/Floor.h index 36aa6f8..c05ab76 100644 --- a/src/render/helper/Floor.h +++ b/src/render/helper/Floor.h @@ -46,6 +46,7 @@ class Floor { Mesh mesh; glm::vec3 position{0.0f}; glm::mat4 model{1.0f}; + float size{100.0f}; public: explicit Floor(Mesh mesh); @@ -64,6 +65,9 @@ class Floor { [[nodiscard]] const Mesh &getMesh() const; [[nodiscard]] texture_id getTextureId() const; + [[nodiscard]] float getSize() const; + void setSize(float value); + void render(); }; diff --git a/src/render/renderer/Renderer.cpp b/src/render/renderer/Renderer.cpp index 7053e63..cd22640 100644 --- a/src/render/renderer/Renderer.cpp +++ b/src/render/renderer/Renderer.cpp @@ -443,6 +443,8 @@ void Renderer::resize(Floor &f, float size) { glBindBuffer(GL_ARRAY_BUFFER, renderInfo.vbo); glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(floorVertices), reinterpret_cast(floorVertices)); + + f.setSize(size); } CoordinateGrid::RenderInfo Renderer::allocateCoordinateGrid(float size, int stepSize) { diff --git a/src/settings/SettingsManager.h b/src/settings/SettingsManager.h index 75832e9..05b44a8 100644 --- a/src/settings/SettingsManager.h +++ b/src/settings/SettingsManager.h @@ -27,6 +27,7 @@ class SettingsManager { LastLoadPath, ResourcePath, MoveSpeed, + AutoScaleMoveSpeed, KeyboardTurnSpeed, MouseTurnSpeed, FieldOfView, @@ -192,6 +193,7 @@ class SettingsManager { {Key::LastLoadPath, {"application/lastLoadPath", {}}}, {Key::ResourcePath, {"resources/resourcePath", {}}}, {Key::MoveSpeed, {"camera/moveSpeed", 0.02f}}, + {Key::AutoScaleMoveSpeed, {"camera/autoScaleMoveSpeed", false}}, {Key::KeyboardTurnSpeed, {"camera/keyboardTurnSpeed", 0.1f}}, {Key::MouseTurnSpeed, {"camera/mouseTurnSpeed", 0.5f}}, {Key::FieldOfView, {"camera/fieldOfView", 45.0f}}, diff --git a/src/window/MainWindow.cpp b/src/window/MainWindow.cpp index 8654ac4..6dac572 100644 --- a/src/window/MainWindow.cpp +++ b/src/window/MainWindow.cpp @@ -180,10 +180,7 @@ MainWindow::MainWindow() : QMainWindow() { settingsDialog.show(); }); - QObject::connect(&settingsDialog, &SettingsDialog::moveSpeedChanged, [this](float value) { - scene.getCamera().setMoveSpeed(value); - scene.getArcCamera().moveSpeed = value; - }); + QObject::connect(&settingsDialog, &SettingsDialog::moveSpeedChanged, &scene, &SceneWidget::setCameraMoveSpeed); QObject::connect(&settingsDialog, &SettingsDialog::keyboardTurnSpeedChanged, [this](float value) { scene.getCamera().setTurnSpeed(value); @@ -253,6 +250,9 @@ MainWindow::MainWindow() : QMainWindow() { scene.setCameraType(SettingsManager::CameraTypeFromInt(value)); }); + QObject::connect(&settingsDialog, &SettingsDialog::autoscaleMoveSpeedChanged, &scene, + &SceneWidget::setAutoscaleCameraMoveSpeed); + QObject::connect(&settingsDialog, &SettingsDialog::renderFloorChanged, &scene, &SceneWidget::setFloorRenderState); QObject::connect(&settingsDialog, &SettingsDialog::backgroundColorChanged, &scene, &SceneWidget::setClearColor); diff --git a/src/window/scene/SceneWidget.cpp b/src/window/scene/SceneWidget.cpp index db2c3b5..190e31e 100644 --- a/src/window/scene/SceneWidget.cpp +++ b/src/window/scene/SceneWidget.cpp @@ -224,6 +224,51 @@ void SceneWidget::handleUndoEvents() { emit selectedItemUpdated(); } +float SceneWidget::getCameraAutoscale() const { + // Scale camera movement so we may cross the whole simulation + // In about 20 real life seconds + const auto cameraSpeed = settings.get(SettingsManager::Key::MoveSpeed).value(); + // 20 seconds in ms + constexpr auto timeToCrossInMs = 20.0f * 1000.0f; + + // 'Crossing the simulation' means from corner to corner + // + // (pretend this is square) + // + // `newSize` + // origin --> *_____ + // |\ | + // | \ | + // | \ | `newSize` + // | \| + // ------ + // since `newSize` is distance from the origin + // + // `newSize` + // *_____ + // \ | + // `newSize`*sq(2) \ | + // \ | `newSize` + // \| + // + // so, distance to the origin from one corner is `newSize` * sq(2) + // we have to double that to get corner to corner + // so, the total diagonal distance is `newSize * std::sqrt(2.0f) * 2.0f` + return floor->getSize() * std::sqrt(2.0f) * 2.0f / (cameraSpeed * timeToCrossInMs); +} + +void SceneWidget::applyAutoscaleCameraSpeed() { + if (autoscaleCameraMoveSpeed) { + const auto speedScale = getCameraAutoscale(); + camera.setMoveSpeedSizeScale(speedScale); + arcCamera.moveSpeedSizeScale = speedScale; + return; + } + + camera.setMoveSpeedSizeScale(1.0f); + arcCamera.moveSpeedSizeScale = 1.0f; +} + void SceneWidget::initializeGL() { if (!initializeOpenGLFunctions()) { std::cerr << "Failed OpenGL functions\n"; @@ -665,6 +710,8 @@ SceneWidget::SceneWidget(QWidget *parent, const Qt::WindowFlags &f) : QOpenGLWid } setResourcePath(resourceDirSetting.value()); + + applyAutoscaleCameraSpeed(); } SceneWidget::~SceneWidget() { @@ -686,11 +733,17 @@ void SceneWidget::setConfiguration(parser::GlobalConfiguration configuration) { // Don't resize beneath the default if (newSize > 100.0f) { + makeCurrent(); renderer.resize(*floor, newSize + 50.0f); // Give the new size a bit of extra overrun renderer.resize(*coordinateGrid, newSize + 50.0f, settings.get(SettingsManager::Key::RenderGridStep).value()); + doneCurrent(); } // time step handled by the MainWindow + + const auto speedScale = getCameraAutoscale(); + camera.setMoveSpeedSizeScale(speedScale); + arcCamera.moveSpeedSizeScale = speedScale; } void SceneWidget::reset() { @@ -705,6 +758,9 @@ void SceneWidget::reset() { selectedNode.reset(); fontManager.reset(); simulationTime = 0.0; + + camera.setMoveSpeedSizeScale(1.0f); + arcCamera.moveSpeedSizeScale = 1.0f; } void SceneWidget::add(const std::vector &areaModels, const std::vector &buildingModels, @@ -981,6 +1037,17 @@ void SceneWidget::setLabelScale(float value) { labelScale = value; } +void SceneWidget::setCameraMoveSpeed(const float value) { + camera.setMoveSpeed(value); + arcCamera.moveSpeed = value; + applyAutoscaleCameraSpeed(); +} + +void SceneWidget::setAutoscaleCameraMoveSpeed(const bool value) { + autoscaleCameraMoveSpeed = value; + applyAutoscaleCameraSpeed(); +} + void SceneWidget::setSelectedNode(unsigned int nodeId) { if (nodes.find(nodeId) == nodes.end()) { std::cerr << "Node with ID: " << nodeId << " selected, but not found in `nodes`, ignoring!\n"; diff --git a/src/window/scene/SceneWidget.h b/src/window/scene/SceneWidget.h index 8c366b0..30e6c46 100644 --- a/src/window/scene/SceneWidget.h +++ b/src/window/scene/SceneWidget.h @@ -111,6 +111,7 @@ class SceneWidget : public QOpenGLWidget, protected QOpenGLFunctions_3_3_Core { // that OpenGL can accept std::array clearColorGl{static_cast(clearColor.redF()), static_cast(clearColor.greenF()), static_cast(clearColor.blueF())}; + bool autoscaleCameraMoveSpeed = settings.get(SettingsManager::Key::AutoScaleMoveSpeed).value(); std::unique_ptr pickingFbo; @@ -154,6 +155,17 @@ class SceneWidget : public QOpenGLWidget, protected QOpenGLFunctions_3_3_Core { void handleEvents(); void handleUndoEvents(); + /** + * Calculate the autoscale multiplier for + * the camera to cross the scenario in a + * fixed about of time + * + * @return + * The value to use for the camera move speed multiplier + */ + float getCameraAutoscale() const; + void applyAutoscaleCameraSpeed(); + protected: void initializeGL() override; void paintGL() override; @@ -337,6 +349,9 @@ class SceneWidget : public QOpenGLWidget, protected QOpenGLFunctions_3_3_Core { */ void setLabelScale(float value); + void setCameraMoveSpeed(float value); + void setAutoscaleCameraMoveSpeed(bool value); + void setSelectedNode(unsigned int nodeId); void clearSelectedNode(); diff --git a/src/window/settings/SettingsDialog.cpp b/src/window/settings/SettingsDialog.cpp index c593d64..eac06d3 100644 --- a/src/window/settings/SettingsDialog.cpp +++ b/src/window/settings/SettingsDialog.cpp @@ -41,6 +41,8 @@ void SettingsDialog::loadSettings() { const auto cameraType = settings.get(Key::RenderCameraType).value(); ui.comboCameraType->setCurrentIndex(ui.comboCameraType->findData(static_cast(cameraType))); + ui.checkBoxScaleMoveSpeed->setChecked(settings.get(Key::AutoScaleMoveSpeed).value()); + ui.checkBoxSkybox->setChecked(settings.get(Key::RenderSkybox).value()); ui.checkBoxFloor->setChecked(settings.get(Key::RenderFloor).value()); @@ -171,6 +173,10 @@ SettingsDialog::SettingsDialog(QWidget *parent) : QDialog(parent) { loadSettings(); + ui.sliderMoveSpeed->setEnabled(!ui.checkBoxScaleMoveSpeed->isChecked()); + QObject::connect(ui.checkBoxScaleMoveSpeed, &QCheckBox::stateChanged, this, + &SettingsDialog::autoscaleMoveSpeedToggled); + QObject::connect(ui.buttonResetTheme, &QPushButton::clicked, this, &SettingsDialog::defaultWindowTheme); QObject::connect(ui.buttonResetMoveSpeed, &QPushButton::clicked, this, &SettingsDialog::defaultMoveSpeed); QObject::connect(ui.buttonResetKeyboardTurnSpeed, &QPushButton::clicked, this, @@ -192,6 +198,8 @@ SettingsDialog::SettingsDialog(QWidget *parent) : QDialog(parent) { QObject::connect(ui.buttonResetSkybox, &QPushButton::clicked, this, &SettingsDialog::defaultEnableSkybox); QObject::connect(ui.buttonResetFloor, &QPushButton::clicked, this, &SettingsDialog::defaultEnableFloor); QObject::connect(ui.buttonResetCameraType, &QPushButton::clicked, this, &SettingsDialog::defaultCameraType); + QObject::connect(ui.buttonResetMoveSpeedScale, &QPushButton::clicked, this, + &SettingsDialog::defaultAutoscaleMoveSpeed); QObject::connect(ui.buttonResetBackgroundColor, &QPushButton::clicked, this, &SettingsDialog::defaultBackgroundColor); QObject::connect(ui.buttonResetSamples, &QPushButton::clicked, this, &SettingsDialog::defaultSamples); QObject::connect(ui.buttonResetBuildingRender, &QPushButton::clicked, this, &SettingsDialog::defaultBuildingEffect); @@ -256,6 +264,7 @@ void SettingsDialog::dialogueButtonClicked(QAbstractButton *button) { ui.buttonResetSkybox->click(); ui.buttonResetFloor->click(); ui.buttonResetCameraType->click(); + ui.buttonResetMoveSpeedScale->click(); ui.buttonResetBackgroundColor->click(); ui.buttonResetSamples->click(); ui.buttonResetBuildingRender->click(); @@ -286,6 +295,12 @@ void SettingsDialog::dialogueButtonClicked(QAbstractButton *button) { emit cameraTypeChanged(static_cast(cameraType)); } + const auto autoscaleMoveSpeed = settings.get(Key::AutoScaleMoveSpeed).value(); + if (autoscaleMoveSpeed != ui.checkBoxScaleMoveSpeed->isChecked()) { + settings.set(Key::AutoScaleMoveSpeed, autoscaleMoveSpeed); + emit autoscaleMoveSpeedChanged(ui.checkBoxScaleMoveSpeed->isChecked()); + } + const auto moveSpeed = static_cast(ui.sliderMoveSpeed->value()) / moveSpeedScale; settings.set(Key::MoveSpeed, moveSpeed); emit moveSpeedChanged(moveSpeed); @@ -504,6 +519,14 @@ void SettingsDialog::dialogueButtonClicked(QAbstractButton *button) { } } +void SettingsDialog::autoscaleMoveSpeedToggled(const int state) { + ui.sliderMoveSpeed->setEnabled(state != Qt::CheckState::Checked); + if (state == Qt::CheckState::Checked) { + ui.sliderMoveSpeed->setValue( + static_cast(*settings.get(SettingsManager::Key::MoveSpeed) * moveSpeedScale)); + } +} + void SettingsDialog::defaultWindowTheme() { const auto defaultTheme = settings.getDefault(SettingsManager::Key::WindowTheme); ui.comboTheme->setCurrentIndex(ui.comboTheme->findData(static_cast(defaultTheme))); @@ -553,6 +576,9 @@ void SettingsDialog::defaultCameraType() { settings.getDefault(SettingsManager::Key::RenderCameraType); ui.comboCameraType->setCurrentIndex(ui.comboCameraType->findData(static_cast(defaultCameraType))); } +void SettingsDialog::defaultAutoscaleMoveSpeed() { + ui.checkBoxScaleMoveSpeed->setChecked(settings.getDefault(SettingsManager::Key::AutoScaleMoveSpeed)); +} void SettingsDialog::defaultEnableFloor() { ui.checkBoxFloor->setChecked(settings.getDefault(SettingsManager::Key::RenderFloor)); diff --git a/src/window/settings/SettingsDialog.h b/src/window/settings/SettingsDialog.h index 21e991f..b635874 100644 --- a/src/window/settings/SettingsDialog.h +++ b/src/window/settings/SettingsDialog.h @@ -99,6 +99,8 @@ class SettingsDialog : public QDialog { */ void dialogueButtonClicked(QAbstractButton *button); + void autoscaleMoveSpeedToggled(int state); + /** * Set the Window Theme combobox to the default value */ @@ -149,6 +151,8 @@ class SettingsDialog : public QDialog { */ void defaultCameraType(); + void defaultAutoscaleMoveSpeed(); + /** * Set the Floor checkbox to the default value */ @@ -365,6 +369,15 @@ class SettingsDialog : public QDialog { */ void cameraTypeChanged(int value); + /** + * Signal emitted when the user changes the Autoscale Move Speed toggle. + * + * @param value + * True if the user wants to use Autoscaling for movement speed + * false otherwise + */ + void autoscaleMoveSpeedChanged(bool value); + /** * Signal emitted when the user changes the * Floor render state diff --git a/src/window/settings/SettingsDialog.ui b/src/window/settings/SettingsDialog.ui index ac72ee6..3546a58 100644 --- a/src/window/settings/SettingsDialog.ui +++ b/src/window/settings/SettingsDialog.ui @@ -46,135 +46,32 @@ 0 0 531 - 1168 + 1200 - - - - - 16 - - - - Charts - - - Qt::AlignmentFlag::AlignCenter - - - - - - - Q - - - - - - - D - - - - - - - Show Building Outlines - - - - - - - Default - - - - - + + Default - - - - 20 - - - 5 - - - Qt::Orientation::Horizontal - - - - - - - - - - true - - - - - + + - Grid Step Size + Building Effect - - - - - - Qt::Orientation::Horizontal - - - - 40 - 20 - - - - - - - - - - - - - - - Qt::Orientation::Horizontal - - - - 40 - 20 - - - - - - - - + + - Down + Theme - + @@ -214,146 +111,72 @@ - - - - - 0 - 0 - - - - - 16 - - - - Window - - - Qt::AlignmentFlag::AlignCenter - - - - - - - Right - - - - - + + - Sort Order + Background Color - - + + - Building Effect + Label Size - - + + Default - - - - - + + - Left + Default - - - - - - - - - Set Custom - - - - - - - + + - 10 + 30 - 8192 + 90 - 100 + 45 Qt::Orientation::Horizontal - - - - X - - - - - - - Theme - - - - - + + Default - - - - - 16 - - - - Graphics - - - Qt::AlignmentFlag::AlignCenter - - - - - + + Default - - - - S + + + + Up - + @@ -380,206 +203,121 @@ - - - - 1 + + + + + 0 + 0 + - - 100 + + + 16 + - - 50 + + Camera - - Qt::Orientation::Horizontal + + Qt::AlignmentFlag::AlignCenter - - - - Default - - + + + + + + + + + Set Custom + + + + - - + + - Z - - - - - - - Default + X - - + + - Resource Directory + Turn Left - - - - Default + + + + 1 - - - - - - Turn Right + + 100 - - - - - - Field of View + + 50 - - - - - - Move Speed + + Qt::Orientation::Horizontal - + Default - - - - Default - - - - - + + Default - - - - Up - - - - - - - Play/Pause - - - - - + + - Default + Move Speed - - - - - + + - Label Size - - - - - - - - - Qt::Orientation::Horizontal - - - - 40 - 20 - - - - - - - - - - - - - - - Qt::Orientation::Horizontal - - - - 40 - 20 - - - - - - - - - - - - - E + Samples (MSAA) - - - - Default + + + + + 0 + 0 + - - - - - - Time Step Preference + + + 16 + - - - - - Default + Window - - - - - - Default + + Qt::AlignmentFlag::AlignCenter - + @@ -602,27 +340,10 @@ - - - - Forward - - - - - - - Turn Left - - - - - - - 0 - + + - + Qt::Orientation::Horizontal @@ -635,29 +356,14 @@ - - - - 0 - 0 - - - - - 0 - 0 - - - - - + - + Qt::Orientation::Horizontal @@ -671,163 +377,201 @@ - - + + - Default + SkyBox - - - - A + + + + Motion Trail Length - - + + + + + Default - - + + - Show Labels + Down - - + + - Samples (MSAA) + Field of View - - - - SkyBox + + + + 10 - - - - - - - 0 - 0 - - - - - 0 - 0 - - - - - 16 - - - - Resources + + 8192 - - Qt::AlignmentFlag::AlignCenter + + 100 - - - - - - Default + + Qt::Orientation::Horizontal - - + + Default - - - - Background Color - - + + - - - - Show Motion Trails + + + + Q - - + + Default - - + + + + + 16 + + - Show Grid + Charts + + + Qt::AlignmentFlag::AlignCenter - - + + + + + Default - - + + - W + X - - - - Default + + + + 20 - - - - - - Floor + + 5 + + + Qt::Orientation::Horizontal - - - - + - Browse + Default - - + + + + + 0 + 0 + + + + + 0 + 0 + + + + + 16 + + - Default + Resources + + + Qt::AlignmentFlag::AlignCenter - + + + + + + Qt::Orientation::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + + + Qt::Orientation::Horizontal + + + + 40 + 20 + + + + + + + 1 @@ -852,106 +596,304 @@ - - + + - Keyboard Turn Speed + Default - - - - 30 - - - 90 + + + + Autoscale Move Speed - - 45 + + + + + + Z - - Qt::Orientation::Horizontal + + + + + + A - - + + - Motion Trail Length + Forward - - + + - Default + Turn Right - - - - - + + - Mouse Turn Speed + Play/Pause - - - - X + + + + Default - - + + Default - - + + - Default + Floor - - - - - 0 - 0 - + + + + + + + W - - - 16 - + + + + + + Sort Order + + + + - Camera + Default - - Qt::AlignmentFlag::AlignCenter + + + + + + D - - + + Default - - - - 20 + + + + E + + + + + + + Default + + + + + + + Default + + + + + + + Default + + + + + + + Browse + + + + + + + Keyboard Turn Speed + + + + + + + + + + + + + Time Step Preference + + + + + + + Default + + + + + + + Default + + + + + + + Default + + + + + + + Show Grid + + + + + + + 0 + + + + + Qt::Orientation::Horizontal + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + + + + + + + + + + + Qt::Orientation::Horizontal + + + + 40 + 20 + + + + + + + + + + Default + + + + + + + + + + Default + + + + + + + + + + true + + + + + + + Default + + + + + + + Grid Step Size + + + + + + + Default + + + + + + + 20 5 @@ -964,23 +906,76 @@ - - + + - Backward + Show Labels - - + + + + Default + + - - + + Default + + + + Show Motion Trails + + + + + + + Mouse Turn Speed + + + + + + + Resource Directory + + + + + + + Show Building Outlines + + + + + + + S + + + + + + + Left + + + + + + + Backward + + + @@ -988,11 +983,67 @@ - - + + + + Right + + - - + + + + + 16 + + + + Graphics + + + Qt::AlignmentFlag::AlignCenter + + + + + + + + + Qt::Orientation::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + + + Qt::Orientation::Horizontal + + + + 40 + 20 + + + + + + + + Default