GTE: Add 'Free Camera' feature
This commit is contained in:
parent
22202f1607
commit
dcd439e7d8
314
src/core/gte.cpp
314
src/core/gte.cpp
|
@ -5,17 +5,26 @@
|
|||
#include "cpu_core.h"
|
||||
#include "cpu_core_private.h"
|
||||
#include "cpu_pgxp.h"
|
||||
#include "host.h"
|
||||
#include "settings.h"
|
||||
|
||||
#include "util/state_wrapper.h"
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/bitutils.h"
|
||||
#include "common/gsvector.h"
|
||||
#include "common/timer.h"
|
||||
|
||||
#include "imgui.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <numbers>
|
||||
#include <numeric>
|
||||
|
||||
LOG_CHANNEL(Host);
|
||||
|
||||
namespace GTE {
|
||||
|
||||
static constexpr s64 MAC0_MIN_VALUE = -(INT64_C(1) << 31);
|
||||
|
@ -27,14 +36,42 @@ static constexpr s32 IR0_MAX_VALUE = 0x1000;
|
|||
static constexpr s32 IR123_MIN_VALUE = -(INT64_C(1) << 15);
|
||||
static constexpr s32 IR123_MAX_VALUE = (INT64_C(1) << 15) - 1;
|
||||
|
||||
static constexpr float FREECAM_MIN_TRANSLATION = -40000.0f;
|
||||
static constexpr float FREECAM_MAX_TRANSLATION = 40000.0f;
|
||||
static constexpr float FREECAM_MIN_ROTATION = -360.0f;
|
||||
static constexpr float FREECAM_MAX_ROTATION = 360.0f;
|
||||
static constexpr float FREECAM_DEFAULT_MOVE_SPEED = 4096.0f;
|
||||
static constexpr float FREECAM_MAX_MOVE_SPEED = 65536.0f;
|
||||
static constexpr float FREECAM_DEFAULT_TURN_SPEED = 30.0f;
|
||||
static constexpr float FREECAM_MAX_TURN_SPEED = 360.0f;
|
||||
|
||||
namespace {
|
||||
|
||||
struct Config
|
||||
{
|
||||
DisplayAspectRatio aspect_ratio = DisplayAspectRatio::R4_3;
|
||||
u32 custom_aspect_ratio_numerator;
|
||||
u32 custom_aspect_ratio_denominator;
|
||||
float custom_aspect_ratio_f;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
Timer::Value freecam_update_time = 0;
|
||||
std::atomic_bool freecam_transform_changed{false};
|
||||
bool freecam_enabled = false;
|
||||
bool freecam_active = false;
|
||||
|
||||
float freecam_move_speed = FREECAM_DEFAULT_MOVE_SPEED;
|
||||
float freecam_turn_speed = FREECAM_DEFAULT_TURN_SPEED;
|
||||
GSVector4 freecam_move = GSVector4::cxpr(0.0f);
|
||||
GSVector4 freecam_turn = GSVector4::cxpr(0.0f);
|
||||
|
||||
GSVector4 freecam_rotation = GSVector4::cxpr(0.0f);
|
||||
GSVector4 freecam_translation = GSVector4::cxpr(0.0f);
|
||||
|
||||
ALIGN_TO_CACHE_LINE GSMatrix4x4 freecam_matrix = GSMatrix4x4::Identity();
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
ALIGN_TO_CACHE_LINE static Config s_config;
|
||||
|
@ -172,6 +209,7 @@ static void PushSZ(s32 value);
|
|||
static void PushRGBFromMAC();
|
||||
static u32 UNRDivide(u32 lhs, u32 rhs);
|
||||
|
||||
static void ApplyFreecam(s64& x, s64& y, s64& z);
|
||||
static void MulMatVec(const s16* M_, const s16 Vx, const s16 Vy, const s16 Vz, u8 shift, bool lm);
|
||||
static void MulMatVec(const s16* M_, const s32 T[3], const s16 Vx, const s16 Vy, const s16 Vz, u8 shift, bool lm);
|
||||
static void MulMatVecBuggy(const s16* M_, const s32 T[3], const s16 Vx, const s16 Vy, const s16 Vz, u8 shift, bool lm);
|
||||
|
@ -218,6 +256,8 @@ void GTE::Initialize()
|
|||
void GTE::Reset()
|
||||
{
|
||||
std::memset(®S, 0, sizeof(REGS));
|
||||
SetFreecamEnabled(false);
|
||||
ResetFreecam();
|
||||
}
|
||||
|
||||
bool GTE::DoState(StateWrapper& sw)
|
||||
|
@ -644,9 +684,11 @@ void GTE::RTPS(const s16 V[3], u8 shift, bool lm, bool last)
|
|||
// IR1 = MAC1 = (TRX*1000h + RT11*VX0 + RT12*VY0 + RT13*VZ0) SAR (sf*12)
|
||||
// IR2 = MAC2 = (TRY*1000h + RT21*VX0 + RT22*VY0 + RT23*VZ0) SAR (sf*12)
|
||||
// IR3 = MAC3 = (TRZ*1000h + RT31*VX0 + RT32*VY0 + RT33*VZ0) SAR (sf*12)
|
||||
const s64 x = dot3(0);
|
||||
const s64 y = dot3(1);
|
||||
const s64 z = dot3(2);
|
||||
s64 x = dot3(0);
|
||||
s64 y = dot3(1);
|
||||
s64 z = dot3(2);
|
||||
if (s_config.freecam_active)
|
||||
ApplyFreecam(x, y, z);
|
||||
TruncateAndSetMAC<1>(x, shift);
|
||||
TruncateAndSetMAC<2>(y, shift);
|
||||
TruncateAndSetMAC<3>(z, shift);
|
||||
|
@ -1373,3 +1415,269 @@ GTE::InstructionImpl GTE::GetInstructionImpl(u32 inst_bits, TickCount* ticks)
|
|||
Panic("Missing handler");
|
||||
}
|
||||
}
|
||||
|
||||
bool GTE::IsFreecamEnabled()
|
||||
{
|
||||
return s_config.freecam_enabled;
|
||||
}
|
||||
|
||||
void GTE::SetFreecamEnabled(bool enabled)
|
||||
{
|
||||
if (s_config.freecam_enabled == enabled)
|
||||
return;
|
||||
|
||||
s_config.freecam_enabled = enabled;
|
||||
if (enabled)
|
||||
{
|
||||
s_config.freecam_transform_changed.store(true, std::memory_order_release);
|
||||
s_config.freecam_update_time = Timer::GetCurrentValue();
|
||||
}
|
||||
}
|
||||
|
||||
void GTE::SetFreecamMoveAxis(u32 axis, float x)
|
||||
{
|
||||
DebugAssert(axis < 3);
|
||||
s_config.freecam_move.F32[axis] = x;
|
||||
SetFreecamEnabled(true);
|
||||
}
|
||||
|
||||
void GTE::SetFreecamRotateAxis(u32 axis, float x)
|
||||
{
|
||||
DebugAssert(axis < 3);
|
||||
s_config.freecam_turn.F32[axis] = x;
|
||||
SetFreecamEnabled(true);
|
||||
}
|
||||
|
||||
void GTE::UpdateFreecam(u64 current_time)
|
||||
{
|
||||
if (!s_config.freecam_enabled)
|
||||
{
|
||||
s_config.freecam_active = false;
|
||||
return;
|
||||
}
|
||||
|
||||
const float dt = std::clamp(
|
||||
static_cast<float>(Timer::ConvertValueToSeconds(current_time - s_config.freecam_update_time)), 0.0f, 1.0f);
|
||||
s_config.freecam_update_time = current_time;
|
||||
|
||||
bool changed = true;
|
||||
s_config.freecam_transform_changed.compare_exchange_strong(changed, false, std::memory_order_acq_rel);
|
||||
|
||||
if (!(s_config.freecam_move == GSVector4::zero()).alltrue())
|
||||
{
|
||||
s_config.freecam_translation += s_config.freecam_move * GSVector4(s_config.freecam_move_speed * dt);
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (!(s_config.freecam_turn == GSVector4::zero()).alltrue())
|
||||
{
|
||||
s_config.freecam_rotation += s_config.freecam_turn * GSVector4(s_config.freecam_turn_speed *
|
||||
static_cast<float>(std::numbers::pi / 180.0) * dt);
|
||||
|
||||
// wrap around -360 degrees/360 degrees
|
||||
constexpr GSVector4 min_rot = GSVector4::cxpr(static_cast<float>(std::numbers::pi * -2.0));
|
||||
constexpr GSVector4 max_rot = GSVector4::cxpr(static_cast<float>(std::numbers::pi * 2.0));
|
||||
s_config.freecam_rotation =
|
||||
s_config.freecam_rotation.blend32(s_config.freecam_rotation + max_rot, (s_config.freecam_rotation < min_rot));
|
||||
s_config.freecam_rotation =
|
||||
s_config.freecam_rotation.blend32(s_config.freecam_rotation + min_rot, (s_config.freecam_rotation > max_rot));
|
||||
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (!changed)
|
||||
return;
|
||||
|
||||
bool any_xform = false;
|
||||
s_config.freecam_matrix = GSMatrix4x4::Identity();
|
||||
|
||||
// translate than rotate, since the camera is rotating around a point
|
||||
// remember, matrix transformation happens in the opposite of the multiplication order
|
||||
|
||||
if (s_config.freecam_translation.x != 0.0f || s_config.freecam_translation.y != 0.0f ||
|
||||
s_config.freecam_translation.z != 0.0f)
|
||||
{
|
||||
s_config.freecam_matrix = GSMatrix4x4::Translation(s_config.freecam_translation.x, s_config.freecam_translation.y,
|
||||
s_config.freecam_translation.z);
|
||||
any_xform = true;
|
||||
}
|
||||
|
||||
if (s_config.freecam_rotation.z != 0.0f)
|
||||
{
|
||||
s_config.freecam_matrix *= GSMatrix4x4::RotationZ(s_config.freecam_rotation.z);
|
||||
any_xform = true;
|
||||
}
|
||||
|
||||
if (s_config.freecam_rotation.y != 0.0f)
|
||||
{
|
||||
s_config.freecam_matrix *= GSMatrix4x4::RotationY(s_config.freecam_rotation.y);
|
||||
any_xform = true;
|
||||
}
|
||||
|
||||
if (s_config.freecam_rotation.x != 0.0f)
|
||||
{
|
||||
s_config.freecam_matrix *= GSMatrix4x4::RotationX(s_config.freecam_rotation.x);
|
||||
any_xform = true;
|
||||
}
|
||||
|
||||
s_config.freecam_active = any_xform;
|
||||
}
|
||||
|
||||
void GTE::ResetFreecam()
|
||||
{
|
||||
s_config.freecam_active = false;
|
||||
s_config.freecam_rotation = GSVector4::zero();
|
||||
s_config.freecam_translation = GSVector4::zero();
|
||||
s_config.freecam_transform_changed.store(false, std::memory_order_release);
|
||||
}
|
||||
|
||||
void GTE::ApplyFreecam(s64& x, s64& y, s64& z)
|
||||
{
|
||||
constexpr double scale = 1 << 12;
|
||||
|
||||
GSVector4 xyz(static_cast<float>(static_cast<double>(x) / scale), static_cast<float>(static_cast<double>(y) / scale),
|
||||
static_cast<float>(static_cast<double>(z) / scale), 1.0f);
|
||||
|
||||
xyz = s_config.freecam_matrix * xyz;
|
||||
|
||||
x = static_cast<s64>(static_cast<double>(xyz.x) * scale);
|
||||
y = static_cast<s64>(static_cast<double>(xyz.y) * scale);
|
||||
z = static_cast<s64>(static_cast<double>(xyz.z) * scale);
|
||||
}
|
||||
|
||||
void GTE::DrawFreecamWindow(float scale)
|
||||
{
|
||||
const ImGuiStyle& style = ImGui::GetStyle();
|
||||
|
||||
bool freecam_enabled = s_config.freecam_enabled;
|
||||
bool enabled_changed = false;
|
||||
|
||||
const float label_width = 140.0f * scale;
|
||||
const float item_width = 350.0f * scale;
|
||||
const float padding_height = 5.0f * scale;
|
||||
|
||||
if (ImGui::CollapsingHeader("Settings", ImGuiTreeNodeFlags_DefaultOpen))
|
||||
{
|
||||
const float third_width = 50.0f * scale;
|
||||
const float second_width = item_width - third_width;
|
||||
|
||||
enabled_changed = ImGui::Checkbox("Enable Freecam", &freecam_enabled);
|
||||
|
||||
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + padding_height);
|
||||
|
||||
ImGui::Columns(3, "Settings", false);
|
||||
ImGui::SetColumnWidth(0, label_width);
|
||||
ImGui::SetColumnWidth(1, second_width);
|
||||
ImGui::SetColumnWidth(2, third_width);
|
||||
|
||||
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + style.ItemInnerSpacing.y);
|
||||
ImGui::TextUnformatted("Movement Speed:");
|
||||
ImGui::NextColumn();
|
||||
ImGui::SetNextItemWidth(second_width);
|
||||
ImGui::DragFloat("##MovementSpeed", &s_config.freecam_move_speed, 1.0f, 0.0f, FREECAM_MAX_MOVE_SPEED);
|
||||
ImGui::NextColumn();
|
||||
if (ImGui::Button("Reset##ResetMovementSpeed"))
|
||||
s_config.freecam_move_speed = FREECAM_DEFAULT_MOVE_SPEED;
|
||||
ImGui::NextColumn();
|
||||
|
||||
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + style.ItemInnerSpacing.y);
|
||||
ImGui::TextUnformatted("Turning Speed:");
|
||||
ImGui::NextColumn();
|
||||
ImGui::SetNextItemWidth(second_width);
|
||||
ImGui::DragFloat("##TurnSpeed", &s_config.freecam_turn_speed, 1.0f, 0.0f, FREECAM_MAX_TURN_SPEED);
|
||||
ImGui::NextColumn();
|
||||
if (ImGui::Button("Reset##ResetTurnSpeed"))
|
||||
s_config.freecam_turn_speed = FREECAM_DEFAULT_TURN_SPEED;
|
||||
ImGui::NextColumn();
|
||||
|
||||
ImGui::Columns(1);
|
||||
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + padding_height);
|
||||
}
|
||||
|
||||
bool changed = false;
|
||||
|
||||
if (ImGui::CollapsingHeader("Rotation", ImGuiTreeNodeFlags_DefaultOpen))
|
||||
{
|
||||
ImGui::Columns(2, "Rotation", false);
|
||||
ImGui::SetColumnWidth(0, label_width);
|
||||
ImGui::SetColumnWidth(1, item_width);
|
||||
|
||||
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + style.ItemInnerSpacing.y);
|
||||
ImGui::TextUnformatted("X Rotation (Pitch):");
|
||||
ImGui::NextColumn();
|
||||
ImGui::SetNextItemWidth(item_width);
|
||||
changed |= ImGui::SliderAngle("##XRot", &s_config.freecam_rotation.x, FREECAM_MIN_ROTATION, FREECAM_MAX_ROTATION);
|
||||
ImGui::NextColumn();
|
||||
|
||||
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + style.ItemInnerSpacing.y);
|
||||
ImGui::TextUnformatted("Y Rotation (Yaw):");
|
||||
ImGui::NextColumn();
|
||||
ImGui::SetNextItemWidth(item_width);
|
||||
changed |= ImGui::SliderAngle("##YRot", &s_config.freecam_rotation.y, FREECAM_MIN_ROTATION, FREECAM_MAX_ROTATION);
|
||||
ImGui::NextColumn();
|
||||
|
||||
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + style.ItemInnerSpacing.y);
|
||||
ImGui::TextUnformatted("Z Rotation (Roll):");
|
||||
ImGui::NextColumn();
|
||||
ImGui::SetNextItemWidth(item_width);
|
||||
changed |= ImGui::SliderAngle("##ZRot", &s_config.freecam_rotation.z, FREECAM_MIN_ROTATION, FREECAM_MAX_ROTATION);
|
||||
ImGui::NextColumn();
|
||||
|
||||
ImGui::Columns(1);
|
||||
|
||||
if (ImGui::Button("Reset##ResetRotation"))
|
||||
{
|
||||
s_config.freecam_rotation = GSVector4::zero();
|
||||
changed = true;
|
||||
}
|
||||
|
||||
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + padding_height);
|
||||
}
|
||||
|
||||
if (ImGui::CollapsingHeader("Translation", ImGuiTreeNodeFlags_DefaultOpen))
|
||||
{
|
||||
ImGui::Columns(2, "Translation", false);
|
||||
ImGui::SetColumnWidth(0, label_width);
|
||||
ImGui::SetColumnWidth(1, item_width);
|
||||
|
||||
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + style.ItemInnerSpacing.y);
|
||||
ImGui::TextUnformatted("X Offset:");
|
||||
ImGui::NextColumn();
|
||||
ImGui::SetNextItemWidth(item_width);
|
||||
changed |= ImGui::DragFloat("##XOffset", &s_config.freecam_translation.x, 1.0f, FREECAM_MIN_TRANSLATION,
|
||||
FREECAM_MAX_TRANSLATION, "%.1f", ImGuiSliderFlags_None);
|
||||
ImGui::NextColumn();
|
||||
|
||||
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + style.ItemInnerSpacing.y);
|
||||
ImGui::TextUnformatted("Y Offset:");
|
||||
ImGui::NextColumn();
|
||||
ImGui::SetNextItemWidth(item_width);
|
||||
changed |= ImGui::DragFloat("##YOffset", &s_config.freecam_translation.y, 1.0f, FREECAM_MIN_TRANSLATION,
|
||||
FREECAM_MAX_TRANSLATION, "%.1f", ImGuiSliderFlags_None);
|
||||
ImGui::NextColumn();
|
||||
|
||||
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + style.ItemInnerSpacing.y);
|
||||
ImGui::TextUnformatted("Z Offset:");
|
||||
ImGui::NextColumn();
|
||||
ImGui::SetNextItemWidth(item_width);
|
||||
changed |= ImGui::DragFloat("##ZOffset", &s_config.freecam_translation.z, 1.0f, FREECAM_MIN_TRANSLATION,
|
||||
FREECAM_MAX_TRANSLATION, "%.1f", ImGuiSliderFlags_None);
|
||||
ImGui::NextColumn();
|
||||
|
||||
ImGui::Columns(1);
|
||||
|
||||
if (ImGui::Button("Reset##ResetTranslation"))
|
||||
{
|
||||
s_config.freecam_translation = GSVector4::zero();
|
||||
changed = true;
|
||||
}
|
||||
|
||||
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + padding_height);
|
||||
}
|
||||
|
||||
if (enabled_changed || (!freecam_enabled && changed))
|
||||
Host::RunOnCPUThread([enabled = freecam_enabled || changed]() { SetFreecamEnabled(enabled); });
|
||||
|
||||
if (changed)
|
||||
s_config.freecam_transform_changed.store(true, std::memory_order_release);
|
||||
}
|
||||
|
|
|
@ -27,4 +27,13 @@ void ExecuteInstruction(u32 inst_bits);
|
|||
using InstructionImpl = void (*)(Instruction);
|
||||
InstructionImpl GetInstructionImpl(u32 inst_bits, TickCount* ticks);
|
||||
|
||||
void DrawFreecamWindow(float scale);
|
||||
|
||||
bool IsFreecamEnabled();
|
||||
void SetFreecamEnabled(bool enabled);
|
||||
void SetFreecamMoveAxis(u32 axis, float x);
|
||||
void SetFreecamRotateAxis(u32 axis, float x);
|
||||
void UpdateFreecam(u64 current_time);
|
||||
void ResetFreecam();
|
||||
|
||||
} // namespace GTE
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include "gpu.h"
|
||||
#include "gpu_hw_texture_cache.h"
|
||||
#include "gpu_thread.h"
|
||||
#include "gte.h"
|
||||
#include "host.h"
|
||||
#include "imgui_overlays.h"
|
||||
#include "settings.h"
|
||||
|
@ -520,6 +521,101 @@ DEFINE_HOTKEY("RotateCounterclockwise", TRANSLATE_NOOP("Hotkeys", "Graphics"),
|
|||
}
|
||||
})
|
||||
|
||||
DEFINE_HOTKEY("FreecamToggle", TRANSLATE_NOOP("Hotkeys", "Graphics"), TRANSLATE_NOOP("Hotkeys", "Freecam Toggle"),
|
||||
[](s32 pressed) {
|
||||
if (!pressed && !Achievements::IsHardcoreModeActive())
|
||||
GTE::SetFreecamEnabled(!GTE::IsFreecamEnabled());
|
||||
})
|
||||
DEFINE_HOTKEY("FreecamReset", TRANSLATE_NOOP("Hotkeys", "Graphics"), TRANSLATE_NOOP("Hotkeys", "Freecam Reset"),
|
||||
[](s32 pressed) {
|
||||
if (!pressed && !Achievements::IsHardcoreModeActive())
|
||||
GTE::ResetFreecam();
|
||||
})
|
||||
DEFINE_HOTKEY("FreecamMoveLeft", TRANSLATE_NOOP("Hotkeys", "Graphics"), TRANSLATE_NOOP("Hotkeys", "Freecam Move Left"),
|
||||
[](s32 pressed) {
|
||||
if (Achievements::IsHardcoreModeActive())
|
||||
return;
|
||||
|
||||
GTE::SetFreecamMoveAxis(0, std::max(static_cast<float>(pressed), 0.0f));
|
||||
})
|
||||
DEFINE_HOTKEY("FreecamMoveRight", TRANSLATE_NOOP("Hotkeys", "Graphics"),
|
||||
TRANSLATE_NOOP("Hotkeys", "Freecam Move Right"), [](s32 pressed) {
|
||||
if (Achievements::IsHardcoreModeActive())
|
||||
return;
|
||||
|
||||
GTE::SetFreecamMoveAxis(0, std::min(static_cast<float>(-pressed), 0.0f));
|
||||
})
|
||||
DEFINE_HOTKEY("FreecamMoveUp", TRANSLATE_NOOP("Hotkeys", "Graphics"), TRANSLATE_NOOP("Hotkeys", "Freecam Move Up"),
|
||||
[](s32 pressed) {
|
||||
if (Achievements::IsHardcoreModeActive())
|
||||
return;
|
||||
|
||||
GTE::SetFreecamMoveAxis(1, std::max(static_cast<float>(pressed), 0.0f));
|
||||
})
|
||||
DEFINE_HOTKEY("FreecamMoveDown", TRANSLATE_NOOP("Hotkeys", "Graphics"), TRANSLATE_NOOP("Hotkeys", "Freecam Move Down"),
|
||||
[](s32 pressed) {
|
||||
if (Achievements::IsHardcoreModeActive())
|
||||
return;
|
||||
|
||||
GTE::SetFreecamMoveAxis(1, std::min(static_cast<float>(-pressed), 0.0f));
|
||||
})
|
||||
DEFINE_HOTKEY("FreecamMoveForward", TRANSLATE_NOOP("Hotkeys", "Graphics"),
|
||||
TRANSLATE_NOOP("Hotkeys", "Freecam Move Forward"), [](s32 pressed) {
|
||||
if (Achievements::IsHardcoreModeActive())
|
||||
return;
|
||||
|
||||
GTE::SetFreecamMoveAxis(2, std::min(static_cast<float>(-pressed), 0.0f));
|
||||
})
|
||||
DEFINE_HOTKEY("FreecamMoveBackward", TRANSLATE_NOOP("Hotkeys", "Graphics"),
|
||||
TRANSLATE_NOOP("Hotkeys", "Freecam Move Backward"), [](s32 pressed) {
|
||||
if (Achievements::IsHardcoreModeActive())
|
||||
return;
|
||||
|
||||
GTE::SetFreecamMoveAxis(2, std::max(static_cast<float>(pressed), 0.0f));
|
||||
})
|
||||
DEFINE_HOTKEY("FreecamRotateLeft", TRANSLATE_NOOP("Hotkeys", "Graphics"),
|
||||
TRANSLATE_NOOP("Hotkeys", "Freecam Rotate Left"), [](s32 pressed) {
|
||||
if (Achievements::IsHardcoreModeActive())
|
||||
return;
|
||||
|
||||
GTE::SetFreecamRotateAxis(1, std::max(static_cast<float>(pressed), 0.0f));
|
||||
})
|
||||
DEFINE_HOTKEY("FreecamRotateRight", TRANSLATE_NOOP("Hotkeys", "Graphics"),
|
||||
TRANSLATE_NOOP("Hotkeys", "Freecam Rotate Right"), [](s32 pressed) {
|
||||
if (Achievements::IsHardcoreModeActive())
|
||||
return;
|
||||
|
||||
GTE::SetFreecamRotateAxis(1, std::min(static_cast<float>(-pressed), 0.0f));
|
||||
})
|
||||
DEFINE_HOTKEY("FreecamRotateForward", TRANSLATE_NOOP("Hotkeys", "Graphics"),
|
||||
TRANSLATE_NOOP("Hotkeys", "Freecam Rotate Forward"), [](s32 pressed) {
|
||||
if (Achievements::IsHardcoreModeActive())
|
||||
return;
|
||||
|
||||
GTE::SetFreecamRotateAxis(0, std::min(static_cast<float>(-pressed), 0.0f));
|
||||
})
|
||||
DEFINE_HOTKEY("FreecamRotateBackward", TRANSLATE_NOOP("Hotkeys", "Graphics"),
|
||||
TRANSLATE_NOOP("Hotkeys", "Freecam Rotate Backward"), [](s32 pressed) {
|
||||
if (Achievements::IsHardcoreModeActive())
|
||||
return;
|
||||
|
||||
GTE::SetFreecamRotateAxis(0, std::max(static_cast<float>(pressed), 0.0f));
|
||||
})
|
||||
DEFINE_HOTKEY("FreecamRollLeft", TRANSLATE_NOOP("Hotkeys", "Graphics"), TRANSLATE_NOOP("Hotkeys", "Freecam Roll Left"),
|
||||
[](s32 pressed) {
|
||||
if (Achievements::IsHardcoreModeActive())
|
||||
return;
|
||||
|
||||
GTE::SetFreecamRotateAxis(2, std::min(static_cast<float>(-pressed), 0.0f));
|
||||
})
|
||||
DEFINE_HOTKEY("FreecamRollRight", TRANSLATE_NOOP("Hotkeys", "Graphics"),
|
||||
TRANSLATE_NOOP("Hotkeys", "Freecam Roll Right"), [](s32 pressed) {
|
||||
if (Achievements::IsHardcoreModeActive())
|
||||
return;
|
||||
|
||||
GTE::SetFreecamRotateAxis(2, std::max(static_cast<float>(pressed), 0.0f));
|
||||
})
|
||||
|
||||
DEFINE_HOTKEY("AudioMute", TRANSLATE_NOOP("Hotkeys", "Audio"), TRANSLATE_NOOP("Hotkeys", "Toggle Mute"),
|
||||
[](s32 pressed) {
|
||||
if (!pressed && System::IsValid())
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include "gpu.h"
|
||||
#include "gpu_backend.h"
|
||||
#include "gpu_thread.h"
|
||||
#include "gte.h"
|
||||
#include "host.h"
|
||||
#include "mdec.h"
|
||||
#include "performance_counters.h"
|
||||
|
@ -72,6 +73,7 @@ struct DebugWindowInfo
|
|||
} // namespace
|
||||
|
||||
static void FormatProcessorStat(SmallStringBase& text, double usage, double time);
|
||||
static void SetStatusIndicatorIcons(SmallStringBase& text, bool paused);
|
||||
static void DrawPerformanceOverlay(const GPUBackend* gpu, float& position_y, float scale, float margin, float spacing);
|
||||
static void DrawMediaCaptureOverlay(float& position_y, float scale, float margin, float spacing);
|
||||
static void DrawFrameTimeOverlay(float& position_y, float scale, float margin, float spacing);
|
||||
|
@ -80,9 +82,10 @@ static void DrawInputsOverlay();
|
|||
|
||||
#ifndef __ANDROID__
|
||||
|
||||
static constexpr size_t NUM_DEBUG_WINDOWS = 6;
|
||||
static constexpr size_t NUM_DEBUG_WINDOWS = 7;
|
||||
static constexpr const char* DEBUG_WINDOW_CONFIG_SECTION = "DebugWindows";
|
||||
static constexpr const std::array<DebugWindowInfo, NUM_DEBUG_WINDOWS> s_debug_window_info = {{
|
||||
{"Freecam", "Free Camera", ":icons/applications-system.png", >E::DrawFreecamWindow, 500, 400},
|
||||
{"SPU", "SPU State", ":icons/applications-system.png", &SPU::DrawDebugStateWindow, 800, 915},
|
||||
{"CDROM", "CD-ROM State", ":icons/applications-system.png", &CDROM::DrawDebugWindow, 800, 540},
|
||||
{"GPU", "GPU State", ":icons/applications-system.png", [](float sc) { g_gpu.DrawDebugStateWindow(sc); }, 450, 550},
|
||||
|
@ -250,6 +253,27 @@ void ImGuiManager::FormatProcessorStat(SmallStringBase& text, double usage, doub
|
|||
text.append_format("{:.1f}% ({:.2f}ms)", usage, time);
|
||||
}
|
||||
|
||||
void ImGuiManager::SetStatusIndicatorIcons(SmallStringBase& text, bool paused)
|
||||
{
|
||||
text.clear();
|
||||
if (GTE::IsFreecamEnabled())
|
||||
text.append(ICON_EMOJI_MAGNIFIYING_GLASS_TILTED_LEFT " ");
|
||||
|
||||
if (paused)
|
||||
{
|
||||
text.append(ICON_EMOJI_PAUSE);
|
||||
}
|
||||
else
|
||||
{
|
||||
const bool rewinding = System::IsRewinding();
|
||||
if (rewinding || System::IsFastForwardEnabled() || System::IsTurboEnabled())
|
||||
text.append(rewinding ? ICON_EMOJI_FAST_REVERSE : ICON_EMOJI_FAST_FORWARD);
|
||||
}
|
||||
|
||||
if (!text.empty() && text.back() == ' ')
|
||||
text.pop_back();
|
||||
}
|
||||
|
||||
void ImGuiManager::DrawPerformanceOverlay(const GPUBackend* gpu, float& position_y, float scale, float margin,
|
||||
float spacing)
|
||||
{
|
||||
|
@ -282,8 +306,7 @@ void ImGuiManager::DrawPerformanceOverlay(const GPUBackend* gpu, float& position
|
|||
position_y += text_size.y + spacing; \
|
||||
} while (0)
|
||||
|
||||
const System::State state = System::GetState();
|
||||
if (state == System::State::Running)
|
||||
if (!GPUThread::IsSystemPaused())
|
||||
{
|
||||
const float speed = PerformanceCounters::GetEmulationSpeed();
|
||||
if (g_gpu_settings.display_show_fps)
|
||||
|
@ -415,18 +438,14 @@ void ImGuiManager::DrawPerformanceOverlay(const GPUBackend* gpu, float& position
|
|||
|
||||
if (g_gpu_settings.display_show_status_indicators)
|
||||
{
|
||||
const bool rewinding = System::IsRewinding();
|
||||
if (rewinding || System::IsFastForwardEnabled() || System::IsTurboEnabled())
|
||||
{
|
||||
text.assign(rewinding ? ICON_EMOJI_FAST_REVERSE : ICON_EMOJI_FAST_FORWARD);
|
||||
SetStatusIndicatorIcons(text, false);
|
||||
if (!text.empty())
|
||||
DRAW_LINE(standard_font, text, IM_COL32(255, 255, 255, 255));
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (g_gpu_settings.display_show_status_indicators && state == System::State::Paused &&
|
||||
!FullscreenUI::HasActiveWindow())
|
||||
else if (g_gpu_settings.display_show_status_indicators && !FullscreenUI::HasActiveWindow())
|
||||
{
|
||||
text.assign(ICON_EMOJI_PAUSE);
|
||||
SetStatusIndicatorIcons(text, true);
|
||||
DRAW_LINE(standard_font, text, IM_COL32(255, 255, 255, 255));
|
||||
}
|
||||
|
||||
|
@ -457,7 +476,8 @@ void ImGuiManager::DrawEnhancementsOverlay(const GPUBackend* gpu)
|
|||
text.append_format(" IR={}x", g_gpu_settings.gpu_resolution_scale);
|
||||
if (g_gpu_settings.gpu_multisamples != 1)
|
||||
{
|
||||
text.append_format(" {}x{}", g_gpu_settings.gpu_multisamples, g_gpu_settings.gpu_per_sample_shading ? "SSAA" : "MSAA");
|
||||
text.append_format(" {}x{}", g_gpu_settings.gpu_multisamples,
|
||||
g_gpu_settings.gpu_per_sample_shading ? "SSAA" : "MSAA");
|
||||
}
|
||||
if (g_gpu_settings.gpu_true_color)
|
||||
text.append(" TrueCol");
|
||||
|
|
|
@ -2104,6 +2104,9 @@ void System::FrameDone()
|
|||
SaveMemoryState(AllocateMemoryState());
|
||||
}
|
||||
|
||||
Timer::Value current_time = Timer::GetCurrentValue();
|
||||
GTE::UpdateFreecam(current_time);
|
||||
|
||||
// Frame step after runahead, otherwise the pause takes precedence and the replay never happens.
|
||||
if (s_state.frame_step_request)
|
||||
{
|
||||
|
@ -2111,8 +2114,6 @@ void System::FrameDone()
|
|||
PauseSystem(true);
|
||||
}
|
||||
|
||||
Timer::Value current_time = Timer::GetCurrentValue();
|
||||
|
||||
// pre-frame sleep accounting (input lag reduction)
|
||||
const Timer::Value pre_frame_sleep_until = s_state.next_frame_time + s_state.pre_frame_sleep_time;
|
||||
s_state.last_active_frame_time = current_time - s_state.frame_start_time;
|
||||
|
|
|
@ -2134,6 +2134,7 @@ void MainWindow::connectSignals()
|
|||
g_emu_thread->dumpSPURAM(filename);
|
||||
});
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(nullptr, m_ui.actionDebugShowVRAM, "Debug", "ShowVRAM", false);
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(nullptr, m_ui.actionFreeCamera, "DebugWindows", "Freecam", false);
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(nullptr, m_ui.actionDebugShowGPUState, "DebugWindows", "GPU", false);
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(nullptr, m_ui.actionDebugShowCDROMState, "DebugWindows", "CDROM", false);
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(nullptr, m_ui.actionDebugShowSPUState, "DebugWindows", "SPU", false);
|
||||
|
|
|
@ -231,6 +231,7 @@
|
|||
<addaction name="actionMemoryScanner"/>
|
||||
<addaction name="actionISOBrowser"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionFreeCamera"/>
|
||||
<addaction name="actionMediaCapture"/>
|
||||
<addaction name="actionCaptureGPUFrame"/>
|
||||
<addaction name="separator"/>
|
||||
|
@ -970,6 +971,14 @@
|
|||
<string>ISO Browser</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionFreeCamera">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Free Camera</string>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
<resources>
|
||||
<include location="resources/duckstation-qt.qrc"/>
|
||||
|
|
Loading…
Reference in New Issue