mirror of
https://github.com/amwatson/CitraVR.git
synced 2024-09-20 03:11:40 +02:00
Super Immersive Heuristics
Implementation of a heuristic scan of registers to find the registers responsible for; * View Matrix * Which eye is being drawn This makes profile #2 of the Super Immersive mode very user friendly (no manual discovery of registers is required)
This commit is contained in:
parent
54b624dfbb
commit
6157f87047
15 changed files with 338 additions and 81 deletions
|
@ -49,9 +49,8 @@ enum class IntSetting(
|
|||
if (hMDType == VRUtils.HMDType.QUEST3.value) 1 else 2),
|
||||
VR_CPU_LEVEL("vr_cpu_level", Settings.SECTION_VR, 3),
|
||||
VR_IMMERSIVE_MODE("vr_immersive_mode", Settings.SECTION_VR, 0),
|
||||
VR_IMMERSIVE_POSITIONAL_FACTOR("vr_immersive_positional_factor", Settings.SECTION_VR, 0),
|
||||
VR_IMMERSIVE_POSITIONAL_GAME_SCALER("vr_immersive_positional_game_scaler", Settings.SECTION_VR, 0),
|
||||
VR_SI_MODE_REGISTER_OFFSET("vr_si_mode_register_offset", Settings.SECTION_VR, 0);
|
||||
VR_SI_MODE_REGISTER_OFFSET("vr_si_mode_register_offset", Settings.SECTION_VR, -1);
|
||||
|
||||
override var int: Int = defaultValue
|
||||
|
||||
|
|
|
@ -15,7 +15,8 @@ enum class StringSetting(
|
|||
CAMERA_OUTER_LEFT_NAME("camera_outer_left_name", Settings.SECTION_CAMERA, "ndk"),
|
||||
CAMERA_OUTER_LEFT_CONFIG("camera_outer_left_config", Settings.SECTION_CAMERA, "_back"),
|
||||
CAMERA_OUTER_RIGHT_NAME("camera_outer_right_name", Settings.SECTION_CAMERA, "ndk"),
|
||||
CAMERA_OUTER_RIGHT_CONFIG("camera_outer_right_config", Settings.SECTION_CAMERA, "_back");
|
||||
CAMERA_OUTER_RIGHT_CONFIG("camera_outer_right_config", Settings.SECTION_CAMERA, "_back"),
|
||||
VR_IMMMERSIVE_EYE_INDICATOR("vr_immersive_eye_indicator", Settings.SECTION_VR, "");
|
||||
|
||||
override var string: String = defaultValue
|
||||
|
||||
|
|
|
@ -1125,18 +1125,6 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
|
|||
IntSetting.VR_IMMERSIVE_MODE.defaultValue
|
||||
)
|
||||
)
|
||||
add(
|
||||
SliderSetting(
|
||||
IntSetting.VR_IMMERSIVE_POSITIONAL_FACTOR,
|
||||
R.string.vr_immersive_pos_factor_title,
|
||||
R.string.vr_immersive_pos_factor_description,
|
||||
0,
|
||||
40,
|
||||
"x",
|
||||
IntSetting.VR_IMMERSIVE_POSITIONAL_FACTOR.key,
|
||||
IntSetting.VR_IMMERSIVE_POSITIONAL_FACTOR.defaultValue.toFloat()
|
||||
)
|
||||
)
|
||||
add(
|
||||
SingleChoiceSetting(
|
||||
IntSetting.VR_IMMERSIVE_POSITIONAL_GAME_SCALER,
|
||||
|
@ -1153,13 +1141,21 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
|
|||
IntSetting.VR_SI_MODE_REGISTER_OFFSET,
|
||||
R.string.vr_si_mode_register_offset_title,
|
||||
R.string.vr_si_mode_register_offset_description,
|
||||
0,
|
||||
-1,
|
||||
92,
|
||||
"register",
|
||||
IntSetting.VR_SI_MODE_REGISTER_OFFSET.key,
|
||||
IntSetting.VR_SI_MODE_REGISTER_OFFSET.defaultValue.toFloat()
|
||||
)
|
||||
)
|
||||
add(
|
||||
StringInputSetting(
|
||||
StringSetting.VR_IMMMERSIVE_EYE_INDICATOR,
|
||||
R.string.vr_immersive_eye_indicator_title,
|
||||
R.string.vr_immersive_eye_indicator_description,
|
||||
""
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -290,15 +290,22 @@ void Config::ReadValues() {
|
|||
"VR", "vr_immersive_mode", 0);
|
||||
Settings::values.vr_immersive_mode = VRSettings::values.vr_immersive_mode;
|
||||
VRSettings::values.vr_si_mode_register_offset = sdl2_config->GetInteger(
|
||||
"VR", "vr_si_mode_register_offset", 0);
|
||||
"VR", "vr_si_mode_register_offset", -1);
|
||||
Settings::values.vr_si_mode_register_offset = VRSettings::values.vr_si_mode_register_offset;
|
||||
VRSettings::values.vr_immersive_positional_factor = sdl2_config->GetInteger(
|
||||
"VR", "vr_immersive_positional_factor", 0);
|
||||
Settings::values.vr_immersive_positional_factor = VRSettings::values.vr_immersive_positional_factor;
|
||||
|
||||
// For immersive modes we use the factor_3d value as a camera movement factor
|
||||
// which means it affects stereo separation and positional movement
|
||||
// We have to divide this by 10 or the numbers are too big
|
||||
VRSettings::values.vr_factor_3d = sdl2_config->GetInteger(
|
||||
"Renderer", "factor_3d", 100) / 10;
|
||||
VRSettings::values.vr_immersive_positional_game_scaler = sdl2_config->GetInteger(
|
||||
"VR", "vr_immersive_positional_game_scaler", 0);
|
||||
Settings::values.vr_immersive_positional_game_scaler = VRSettings::values.vr_immersive_positional_game_scaler;
|
||||
|
||||
VRSettings::values.vr_immersive_eye_indicator = sdl2_config->GetString(
|
||||
"VR", "vr_immersive_eye_indicator", "");
|
||||
Settings::values.vr_immersive_eye_indicator = VRSettings::values.vr_immersive_eye_indicator;
|
||||
|
||||
if (Settings::values.vr_immersive_mode.GetValue() > 0) {
|
||||
LOG_INFO(Config, "VR immersive mode enabled");
|
||||
|
||||
|
|
|
@ -442,13 +442,20 @@ XrPosef GameSurfaceLayer::GetTopPanelFromHeadPose(uint32_t eye, const XrPosef& h
|
|||
XrVector3f forward, up, right;
|
||||
XrMath::Quatf::ToVectors(headPose.orientation, forward, right, up);
|
||||
|
||||
panelPosition.z += kSuperImmersiveRadius * (forward.x * 0.4f);
|
||||
panelPosition.y -= kSuperImmersiveRadius * (forward.z * 0.4f);
|
||||
panelPosition.x += kSuperImmersiveRadius * (forward.y * 0.4f);
|
||||
panelPosition.z += kSuperImmersiveRadius * (forward.x * 0.58f);
|
||||
panelPosition.y -= kSuperImmersiveRadius * (forward.z * 0.58f);
|
||||
panelPosition.x += kSuperImmersiveRadius * (forward.y * 0.58f);
|
||||
|
||||
panelPosition.z += up.x / 12.f;
|
||||
panelPosition.y -= up.z / 12.f;
|
||||
panelPosition.x += up.y / 12.f;
|
||||
panelPosition.z += up.x / 25.f;
|
||||
panelPosition.y -= up.z / 25.f;
|
||||
panelPosition.x += up.y / 25.f;
|
||||
|
||||
if (mImmersiveMode == 3)
|
||||
{
|
||||
panelPosition.z += right.x * (0.065f / 2.f) * (1 - 2.f * eye);
|
||||
panelPosition.y -= right.z * (0.065f / 2.f) * (1 - 2.f * eye);
|
||||
panelPosition.x += right.y * (0.065f / 2.f) * (1 - 2.f * eye);
|
||||
}
|
||||
|
||||
return XrPosef{headPose.orientation, panelPosition};
|
||||
}
|
||||
|
|
|
@ -75,7 +75,7 @@ std::chrono::time_point<std::chrono::steady_clock> gOnCreateStartTime;
|
|||
std::unique_ptr<OpenXr> gOpenXr;
|
||||
MessageQueue gMessageQueue;
|
||||
|
||||
const std::vector<float> immersiveScaleFactor = {1.0f, 3.0f, 1.8f};
|
||||
const std::vector<float> immersiveScaleFactor = {1.0f, 3.0f, 1.4f};
|
||||
|
||||
void ForwardButtonStateChangeToCitra(JNIEnv* jni, jobject activityObject,
|
||||
jmethodID forwardVRInputMethodID, const int androidButtonCode,
|
||||
|
@ -433,12 +433,12 @@ private:
|
|||
-MATH_FLOAT_PI / 8.0f) ||
|
||||
// If in "super immersive" mode then put controller next to head in order to
|
||||
// disable the mode temporarily
|
||||
(VRSettings::values.vr_immersive_mode > 2 && length < 0.2)) {
|
||||
(VRSettings::values.vr_immersive_mode >= 2 && length < 0.2)) {
|
||||
XrVector4f identity[4] = {};
|
||||
XrMath::Matrixf::Identity(identity);
|
||||
immersiveModeFactor = 1.0f;
|
||||
Core::System::GetInstance().GPU().Renderer().Rasterizer()->SetVRData(
|
||||
1, immersiveModeFactor, -1, (float*)identity);
|
||||
1, immersiveModeFactor, -1, 0.f, (float*)identity);
|
||||
} else {
|
||||
XrVector4f transform[4] = {};
|
||||
XrMath::Quatf::ToRotationMatrix(gOpenXr->headLocation.pose.orientation,
|
||||
|
@ -454,20 +454,16 @@ private:
|
|||
invertedOrientation, gOpenXr->headLocation.pose.position);
|
||||
|
||||
const float gamePosScaler =
|
||||
powf(10.f, VRSettings::values.vr_immersive_positional_game_scaler);
|
||||
inv_transform[3].x = -position.x *
|
||||
VRSettings::values.vr_immersive_positional_factor *
|
||||
gamePosScaler;
|
||||
inv_transform[3].y = -position.y *
|
||||
VRSettings::values.vr_immersive_positional_factor *
|
||||
gamePosScaler;
|
||||
inv_transform[3].z = -position.z *
|
||||
VRSettings::values.vr_immersive_positional_factor *
|
||||
gamePosScaler;
|
||||
powf(10.f, VRSettings::values.vr_immersive_positional_game_scaler) *
|
||||
VRSettings::values.vr_factor_3d;
|
||||
|
||||
inv_transform[3].x = -position.x * gamePosScaler;
|
||||
inv_transform[3].y = -position.y * gamePosScaler;
|
||||
inv_transform[3].z = -position.z * gamePosScaler;
|
||||
|
||||
Core::System::GetInstance().GPU().Renderer().Rasterizer()->SetVRData(
|
||||
VRSettings::values.vr_immersive_mode, immersiveModeFactor, uoffset,
|
||||
(float*)inv_transform);
|
||||
-gamePosScaler, (float*)inv_transform);
|
||||
showLowerPanel = false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -45,8 +45,9 @@ struct Values {
|
|||
int32_t vr_immersive_mode = 0;
|
||||
bool extra_performance_mode_enabled = false;
|
||||
int32_t vr_si_mode_register_offset = -1;
|
||||
int32_t vr_immersive_positional_factor = 0;
|
||||
int32_t vr_factor_3d = 100;
|
||||
int32_t vr_immersive_positional_game_scaler = 0;
|
||||
std::string vr_immersive_eye_indicator;
|
||||
} extern values;
|
||||
|
||||
} // namespace VRSettings
|
||||
|
|
|
@ -453,9 +453,9 @@
|
|||
|
||||
<string-array name="vrImmersiveModeNames">
|
||||
<item>Off</item>
|
||||
<item>180 Degree Immersive (3DoF)</item>
|
||||
<item>Super Immersive (6DoF) - Profile 1</item>
|
||||
<item>Super Immersive (6DoF) - Profile 2</item>
|
||||
<item>180 Degree Wrap-Around Immersive (3DoF)</item>
|
||||
<item>"Super Immersive (6DoF) - Profile 1 (Ocarina of Time & Majora's Mask)"</item>
|
||||
<item>Super Immersive (6DoF) - Profile 2 (All other games)</item>
|
||||
</string-array>
|
||||
|
||||
<integer-array name="vrImmersiveModeValues">
|
||||
|
|
|
@ -683,10 +683,10 @@
|
|||
<string name="vr_notice_description2">Some games just won\'t run well. Be sure to check the CitraVR Game Compatibility List to see which games perform best.</string>
|
||||
<string name="vr_input_binding_subtitle">\nHaving trouble? Press Select + Start to disable Gamepad Gaze Mode, then try again</string>
|
||||
<string name="immersive_mode">Immersive Mode</string>
|
||||
<string name="vr_si_mode_register_offset_title">Register Offset</string>
|
||||
<string name="vr_si_mode_register_offset_description">Sets the register offset used for the view when using Super Immersive Mode Profile 2 - varies by game</string>
|
||||
<string name="vr_immersive_pos_factor_title">Positional Movement Factor</string>
|
||||
<string name="vr_immersive_pos_factor_description">Adjusts how much movement left/right/up/down affects the game camera (0 = disabled)</string>
|
||||
<string name="vr_immersive_pos_game_scaler_title">Positional Movement Factor Multiplier</string>
|
||||
<string name="vr_immersive_pos_game_scaler_description">Adjusts how much movement left/right/up/down affects the game camera, some games required larger values, so this multiplies the Positional Movement Factor</string>
|
||||
<string name="vr_si_mode_register_offset_title">Register Offset [Optional]</string>
|
||||
<string name="vr_si_mode_register_offset_description">[default: -1 = use auto-detect] CAUTION: Sets the register offset used for the view when using Super Immersive Mode Profile 2</string>
|
||||
<string name="vr_immersive_eye_indicator_title">Eye Indicator Register [Optional]</string>
|
||||
<string name="vr_immersive_eye_indicator_description">[Leave blank for auto-detect] CAUTION: Sets the register and index offset used for identifying which eye is being drawn e.g 87,2</string>
|
||||
<string name="vr_immersive_pos_game_scaler_title">Depth Factor Multiplier</string>
|
||||
<string name="vr_immersive_pos_game_scaler_description">Adjusts how much the depth slider affects the game camera (movement/separation), some games require smaller/larger values, so this multiplier adjusts how much movement the depth setting causes</string>
|
||||
</resources>
|
||||
|
|
|
@ -557,9 +557,9 @@ struct Values {
|
|||
|
||||
// VR
|
||||
Setting<u32> vr_immersive_mode{0, "vr_immersive_mode"};
|
||||
Setting<u32> vr_si_mode_register_offset{-1, "vr_si_mode_register_offset"};
|
||||
Setting<u32> vr_immersive_positional_factor{0, "vr_immersive_positional_factor"};
|
||||
Setting<int32_t> vr_si_mode_register_offset{-1, "vr_si_mode_register_offset"};
|
||||
Setting<u32> vr_immersive_positional_game_scaler{0, "vr_immersive_positional_game_scaler"};
|
||||
Setting<std::string> vr_immersive_eye_indicator{"", "vr_immersive_eye_indicator"};
|
||||
};
|
||||
|
||||
extern Values values;
|
||||
|
|
|
@ -85,7 +85,9 @@ Handler::Handler(Core::Timing& timing, u64 override_init_time) : timing(timing)
|
|||
std::bind(&Handler::UpdateTimeCallback, this, _1, _2));
|
||||
timing.ScheduleEvent(0, update_time_event, 0, 0);
|
||||
|
||||
float slidestate = Settings::values.factor_3d.GetValue() / 100.0f;
|
||||
float slidestate = Settings::values.vr_immersive_mode.GetValue() < 3 ?
|
||||
Settings::values.factor_3d.GetValue() / 100.0f :
|
||||
0.01f;
|
||||
shared_page.sliderstate_3d = static_cast<float_le>(slidestate);
|
||||
|
||||
// TODO(PabloMK7)
|
||||
|
|
|
@ -217,9 +217,10 @@ void Module::UpdatePadCallback(std::uintptr_t user_data, s64 cycles_late) {
|
|||
|
||||
// TODO(xperia64): How the 3D Slider is updated by the HID module needs to be RE'd
|
||||
// and possibly moved to its own Core::Timing event.
|
||||
mem->pad.sliderstate_3d = (Settings::values.factor_3d.GetValue() / 100.0f);
|
||||
system.Kernel().GetSharedPageHandler().Set3DSlider(Settings::values.factor_3d.GetValue() /
|
||||
100.0f);
|
||||
mem->pad.sliderstate_3d = Settings::values.vr_immersive_mode.GetValue() < 3 ?
|
||||
(Settings::values.factor_3d.GetValue() / 100.0f) :
|
||||
0.01f;
|
||||
system.Kernel().GetSharedPageHandler().Set3DSlider(mem->pad.sliderstate_3d);
|
||||
|
||||
// Reschedule recurrent event
|
||||
system.CoreTiming().ScheduleEvent(pad_update_ticks - cycles_late, pad_update_event);
|
||||
|
|
|
@ -22,6 +22,8 @@ static Common::Vec3f LightColor(const Pica::LightingRegs::LightColor& color) {
|
|||
return Common::Vec3u{color.r, color.g, color.b} / 255.0f;
|
||||
}
|
||||
|
||||
constexpr float VR_IPD = 0.065f;
|
||||
|
||||
RasterizerAccelerated::HardwareVertex::HardwareVertex(const Pica::OutputVertex& v,
|
||||
bool flip_quaternion) {
|
||||
position[0] = v.pos.x.ToFloat32();
|
||||
|
@ -859,7 +861,7 @@ void RasterizerAccelerated::SyncClipPlane() {
|
|||
}
|
||||
}
|
||||
|
||||
void RasterizerAccelerated::SetVRData(const int32_t &vrImmersiveMode, const float& immersiveModeFactor, int uoffset, const float view[16])
|
||||
void RasterizerAccelerated::SetVRData(const int32_t &vrImmersiveMode, const float& immersiveModeFactor, int uoffset, const float& gamePosScaler, const float inv_view[16])
|
||||
{
|
||||
if (vs_uniform_block_data.data.vr_immersive_mode_factor != immersiveModeFactor)
|
||||
{
|
||||
|
@ -869,7 +871,8 @@ void RasterizerAccelerated::SetVRData(const int32_t &vrImmersiveMode, const floa
|
|||
|
||||
vr_uoffset = uoffset;
|
||||
vr_immersive_mode = vrImmersiveMode;
|
||||
std::memcpy(vr_view, view, sizeof(float) * 16);
|
||||
vr_game_pos_scaler = gamePosScaler;
|
||||
std::memcpy(vr_inv_view, inv_view, sizeof(float) * 16);
|
||||
}
|
||||
|
||||
static void MatrixTranspose(float m[16], const float src[16]) {
|
||||
|
@ -924,37 +927,275 @@ void RasterizerAccelerated::ApplyVRDataToPicaVSUniforms(Pica::Shader::Generator:
|
|||
viewMatrixIndex = vr_uoffset;
|
||||
break;
|
||||
default:
|
||||
viewMatrixIndex = -1;
|
||||
break;
|
||||
return;
|
||||
}
|
||||
|
||||
const bool findLeftRightEyeIndicator = Settings::values.vr_immersive_eye_indicator.GetValue().empty();
|
||||
|
||||
/*
|
||||
* The following section is a "heuristic" algorithm that guesses (pretty well actually!) both the number of the
|
||||
* register for the view transformation matrix and also the left/right eye indicator register based on a bunch of
|
||||
* characteristics I gathered by looking at the register values for a few games. If it gets it wrong, then it is
|
||||
* still possible to supply your own values using the config options, but the default now should be using this
|
||||
* "auto-detect" routine.
|
||||
*/
|
||||
if (viewMatrixIndex == -1 && mode >= 3)
|
||||
{
|
||||
struct regscore
|
||||
{
|
||||
regscore() {
|
||||
neg = pos = 0;
|
||||
score = bonus = 0.f;
|
||||
}
|
||||
int neg;
|
||||
int pos;
|
||||
std::set<float> values;
|
||||
float score;
|
||||
float bonus;
|
||||
};
|
||||
|
||||
//static array to keep track of the way the data changes in each register location
|
||||
static regscore regscores[96 * 4];
|
||||
static regscore regscores2[96];
|
||||
|
||||
// use a counter to ignore the first number of
|
||||
// times through here, as a lot of these will
|
||||
// just be things like intro videos etc
|
||||
static int counter = 0;
|
||||
constexpr int start = 5000;
|
||||
constexpr int stop = 250000;
|
||||
constexpr float EPSILON = 0.00001f;
|
||||
auto between = [=](float l, float v, float r) { return v <= r && v >= l; };
|
||||
static int identityMatrix[16] = {
|
||||
1, 0, 0, 0,
|
||||
0, 1, 0, 0,
|
||||
0, 0, 1, 0,
|
||||
0, 0, 0, 1
|
||||
};
|
||||
|
||||
if (++counter > start)
|
||||
{
|
||||
//Stop collecting after a period of time as we should have a good hit by now
|
||||
//we don't want to waste precious CPU
|
||||
if (counter < stop)
|
||||
{
|
||||
for (int r = 0; r < 96; ++r)
|
||||
{
|
||||
/*
|
||||
* This block does the scoring for the possible view matrix register
|
||||
*/
|
||||
if (r <= 91)
|
||||
{
|
||||
//Check that all the values are between -1 and 1
|
||||
//don't include values that might be positional and values in the final 4x4 row
|
||||
bool valid = true;
|
||||
for (int i = 0; i < 3 && valid; ++i) {
|
||||
for (int j = 0; j < 3 && valid; ++j) {
|
||||
valid = between(-1.f, f[r + i][j], 1.f);
|
||||
}
|
||||
}
|
||||
|
||||
//only proceed to score if register could legitimately be a view matrix
|
||||
if (valid) {
|
||||
regscores2[r].score += 1;
|
||||
|
||||
//bonus point if register appear to be an identity matrix
|
||||
bool isIdentity = true;
|
||||
for (int i = 0; i < 3 && isIdentity; ++i) {
|
||||
for (int j = 0; j < 3 && isIdentity; ++j) {
|
||||
isIdentity = (int)std::roundf(f[r + i][j]) == identityMatrix[i * 4 + j];
|
||||
}
|
||||
}
|
||||
if (isIdentity) {
|
||||
regscores2[r].score += 1;
|
||||
}
|
||||
|
||||
//another bonus point if the absolute position values in the matrix are bigger than 1
|
||||
if (fabsf(f[r].w) > 1.f && fabsf(f[r+1].w) > 1.f && fabsf(f[r+2].w) > 1.f) {
|
||||
regscores2[r].score += 1;
|
||||
}
|
||||
|
||||
//another bonus point if the last position contains 0, 1, 2, 3 or 0, 0, 0, 1
|
||||
if ((f[r + 3].x == 0.f && f[r + 3].y == 0.f && f[r + 3].z == 0.f &&
|
||||
f[r + 3].w == 1.f) ||
|
||||
(f[r + 3].x == 0.f && f[r + 3].y == 1.f && f[r + 3].z == 2.f &&
|
||||
f[r + 3].w == 3.f))
|
||||
{
|
||||
regscores2[r].score += 1;
|
||||
}
|
||||
|
||||
//Certain values in the register should never be exactly 0
|
||||
if (f[r].x == 0.f || f[r + 1].y == 0.f || f[r + 2].z == 0.f) {
|
||||
regscores2[r].score -= 2;
|
||||
}
|
||||
|
||||
// final bonus point if register is a commonly occuring one
|
||||
if (r == 90 || r == 4 || r == 8) {
|
||||
regscores2[r].score += 1;
|
||||
}
|
||||
} else {
|
||||
//Not possibly a view matrix, subtract a point as punishment, but don't go below 0
|
||||
if (regscores2[r].score > 0)
|
||||
regscores2[r].score -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* This block does the scoring for the possible left/right eye indicator register, typical characteristics
|
||||
* of that register are:
|
||||
* * Its value has a more or less equal number of negative and positive values over time
|
||||
* * It is a small number greater than almost 0 and less than 0.1
|
||||
* * It only has a small amount of variance in its values (rounded to 5dp)
|
||||
* * Frequently selected registers get a small bonus, as they then become more likely to be the correct ones
|
||||
*/
|
||||
for (int i = 0; i <= 3; ++i)
|
||||
{
|
||||
float value = fabsf(f[r][i]);
|
||||
|
||||
auto ®score = regscores[r * 4 + i];
|
||||
|
||||
if (value < 0.1f && value > EPSILON)
|
||||
{
|
||||
if (f[r][i] < 0.f)
|
||||
{
|
||||
regscore.neg++;
|
||||
}
|
||||
else
|
||||
{
|
||||
regscore.pos++;
|
||||
}
|
||||
|
||||
//Store the value rounded, we are looking for the register with the least variance
|
||||
regscore.values.insert(
|
||||
floorf(value * 10000) / 10000);
|
||||
}
|
||||
|
||||
// recalc score
|
||||
if (regscore.neg > 0 &&
|
||||
regscore.pos > 0 &&
|
||||
regscore.values.size() > 0)
|
||||
{
|
||||
int max = std::max<int>(regscore.neg,
|
||||
regscore.pos);
|
||||
int min = std::min<int>(regscore.neg,
|
||||
regscore.pos);
|
||||
regscore.score =
|
||||
((min * 1000.0f) /
|
||||
(float) (max *
|
||||
regscore.values.size())) *
|
||||
((float) (min + max) / (counter - start));
|
||||
|
||||
//If this register has only seen 1 or two legit values, then punish it
|
||||
if (regscore.values.size() < 2)
|
||||
{
|
||||
regscore.score /= 10.0f;
|
||||
}
|
||||
|
||||
//Add on any bonuses this register has received
|
||||
regscore.score += regscore.bonus;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Now find the highest scoring registers/index
|
||||
float topscore = 0.f;
|
||||
float mat_topscore = 0.f;
|
||||
for (int r = 0; r < 96; ++r)
|
||||
{
|
||||
if (regscores2[r].score > mat_topscore) {
|
||||
vr_heuristic.view_matrixregister = r;
|
||||
mat_topscore = regscores2[r].score;
|
||||
}
|
||||
|
||||
for (int i = 0; i <= 3; ++i)
|
||||
{
|
||||
if (regscores[r * 4 + i].score > topscore)
|
||||
{
|
||||
vr_heuristic.eye_indicator_register = r;
|
||||
vr_heuristic.eye_indicator_reg_index = i;
|
||||
topscore = regscores[r * 4 + i].score;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (mat_topscore != 0.f) {
|
||||
regscores2[vr_heuristic.view_matrixregister].score += 0.05f; // small perk for being selected
|
||||
}
|
||||
|
||||
if (topscore != 0.0f)
|
||||
{
|
||||
regscores[vr_heuristic.eye_indicator_register * 4 +
|
||||
vr_heuristic.eye_indicator_reg_index].bonus += 0.001f; // small perk for being selected
|
||||
}
|
||||
}
|
||||
|
||||
viewMatrixIndex = vr_heuristic.view_matrixregister;
|
||||
}
|
||||
}
|
||||
|
||||
if (viewMatrixIndex != -1 && vs_uniforms.uniforms.f.size() > viewMatrixIndex)
|
||||
{
|
||||
if (matrixMode == 2)
|
||||
{
|
||||
f[viewMatrixIndex][0] = vr_view[0];
|
||||
f[viewMatrixIndex][1] = vr_view[4];
|
||||
f[viewMatrixIndex][2] = vr_view[8];
|
||||
f[viewMatrixIndex + 1][0] = vr_view[1];
|
||||
f[viewMatrixIndex + 1][1] = vr_view[5];
|
||||
f[viewMatrixIndex + 1][2] = vr_view[9];
|
||||
f[viewMatrixIndex + 2][0] = vr_view[2];
|
||||
f[viewMatrixIndex + 2][1] = vr_view[6];
|
||||
f[viewMatrixIndex + 2][2] = vr_view[10];
|
||||
f[viewMatrixIndex][0] = vr_inv_view[0];
|
||||
f[viewMatrixIndex][1] = vr_inv_view[4];
|
||||
f[viewMatrixIndex][2] = vr_inv_view[8];
|
||||
f[viewMatrixIndex + 1][0] = vr_inv_view[1];
|
||||
f[viewMatrixIndex + 1][1] = vr_inv_view[5];
|
||||
f[viewMatrixIndex + 1][2] = vr_inv_view[9];
|
||||
f[viewMatrixIndex + 2][0] = vr_inv_view[2];
|
||||
f[viewMatrixIndex + 2][1] = vr_inv_view[6];
|
||||
f[viewMatrixIndex + 2][2] = vr_inv_view[10];
|
||||
}
|
||||
else if (matrixMode >= 3)
|
||||
else if (matrixMode == 3)
|
||||
{
|
||||
// First apply the view transformation matrix to the right location
|
||||
float v[16], v2[16], v3[16];
|
||||
MatrixTranspose(v, &f[viewMatrixIndex].x);
|
||||
std::memcpy(v2, vr_view, sizeof(float) * 16);
|
||||
std::memcpy(v2, vr_inv_view, sizeof(float) * 16);
|
||||
v2[12] = v2[13] = v2[14] = 0.f;
|
||||
MatrixMultiply(v3, v2, v);
|
||||
MatrixTranspose(&f[viewMatrixIndex].x, v3);
|
||||
|
||||
// This following part is an improvement on just using the default stereo separation provided by the 3DS
|
||||
// The problem with the default is it doesn't work correctly when rotating the headset, whereas the following approach
|
||||
// applies a left/right separation from an (almost) centered camera POV.
|
||||
// This profile mode will set a depth scale of only 0.1%, and then use a defined vr uniform register
|
||||
// to identify if the left or right eye is being drawn and apply appropriate stereo separation
|
||||
//
|
||||
// The pair of values (e.g "87,2") represents the offset of the Vec4f in the vs pica uniforms and the
|
||||
// index into that Vec4 of the value that is check for: -ve for left eye and +ve for right eye
|
||||
float eye_indicator_register = vr_heuristic.eye_indicator_register;
|
||||
float eye_indicator_reg_index = vr_heuristic.eye_indicator_reg_index;
|
||||
//If the user _has_ defined this (unlikely!) then use the config setting
|
||||
if (!findLeftRightEyeIndicator)
|
||||
{
|
||||
// Finding these offset/index values manually is easier to do using a desktop build of Citra in a debugger
|
||||
// however the heuristic search appears to do a very good job of this so it is unlikely people
|
||||
// will need to find and specify this config, but is still available to set if it fails to work for
|
||||
// a game and someone identifies the appropriate values
|
||||
const std::string vr_immersive_eye_indicator = Settings::values.vr_immersive_eye_indicator.GetValue();
|
||||
eye_indicator_register = atoi(vr_immersive_eye_indicator.substr(0, vr_immersive_eye_indicator.find_first_of(',')).c_str());
|
||||
eye_indicator_reg_index = atoi(vr_immersive_eye_indicator.substr(vr_immersive_eye_indicator.find_first_of(',')+1).c_str());
|
||||
}
|
||||
|
||||
//If we found/know a viable register/index, then use it for left/right eye logic
|
||||
if (eye_indicator_register != -1 &&
|
||||
eye_indicator_reg_index != -1 &&
|
||||
eye_indicator_register < vs_uniforms.uniforms.f.size() &&
|
||||
(eye_indicator_register < viewMatrixIndex ||
|
||||
eye_indicator_register > viewMatrixIndex + 3) &&
|
||||
f[eye_indicator_register][eye_indicator_reg_index] != 0.0f)
|
||||
{
|
||||
const bool isLeftEye = (f[eye_indicator_register][eye_indicator_reg_index] < 0.f);
|
||||
f[viewMatrixIndex][3] +=
|
||||
vr_game_pos_scaler * (isLeftEye ? -1 : 1) * (VR_IPD / 2.f);
|
||||
}
|
||||
}
|
||||
|
||||
f[viewMatrixIndex][3] += vr_view[12];
|
||||
f[viewMatrixIndex + 1][3] += vr_view[13];
|
||||
f[viewMatrixIndex + 2][3] += vr_view[14];
|
||||
f[viewMatrixIndex][3] += vr_inv_view[12];
|
||||
f[viewMatrixIndex + 1][3] += vr_inv_view[13];
|
||||
f[viewMatrixIndex + 2][3] += vr_inv_view[14];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "common/vector_math.h"
|
||||
#include "video_core/rasterizer_interface.h"
|
||||
#include "video_core/shader/generator/pica_fs_config.h"
|
||||
#include "video_core/shader/generator/shader_uniforms.h"
|
||||
|
@ -31,7 +30,7 @@ public:
|
|||
|
||||
void SyncEntireState() override;
|
||||
|
||||
void SetVRData(const int32_t &vrImmersiveMode, const float& immersiveModeFactor, int uoffset, const float view[16]) override;
|
||||
void SetVRData(const int32_t &vrImmersiveMode, const float& immersiveModeFactor, int uoffset, const float& gamePosScaler, const float inv_view[16]) override;
|
||||
|
||||
protected:
|
||||
/// Sync fixed-function pipeline state
|
||||
|
@ -173,12 +172,19 @@ protected:
|
|||
std::array<Common::Vec4f, 256> proctex_diff_lut_data{};
|
||||
|
||||
//VR Stuff
|
||||
u32 vr_uoffset = -1;
|
||||
u32 vr_immersive_mode;
|
||||
float vr_view[16] = {};
|
||||
u32 vr_uoffset = 0;
|
||||
u32 vr_immersive_mode;
|
||||
float vr_game_pos_scaler = 0.f;
|
||||
float vr_inv_view[16] = {};
|
||||
|
||||
struct HeuristicResult
|
||||
{
|
||||
int32_t view_matrixregister = -1;
|
||||
int32_t eye_indicator_register = -1;
|
||||
int32_t eye_indicator_reg_index = -1;
|
||||
} vr_heuristic;
|
||||
|
||||
public:
|
||||
|
||||
void ApplyVRDataToPicaVSUniforms(Pica::Shader::Generator::VSPicaUniformData &vs_uniforms);
|
||||
};
|
||||
|
||||
|
|
|
@ -84,6 +84,6 @@ public:
|
|||
virtual void SyncEntireState() {}
|
||||
|
||||
/// Set VR position data on the rasterizer
|
||||
virtual void SetVRData(const int32_t &vrImmersiveMode, const float& immersiveModeFactor, int uoffset, const float view[16]) {}
|
||||
virtual void SetVRData(const int32_t &vrImmersiveMode, const float& immersiveModeFactor, int uoffset, const float& gamePosScaler, const float inv_view[16]) {}
|
||||
};
|
||||
} // namespace VideoCore
|
||||
|
|
Loading…
Reference in a new issue