app: move the showcase hotkey to Ctrl+Shift+B

This commit is contained in:
mchughalex 2026-07-20 00:08:18 +01:00
parent 5a46eac8fa
commit 035d3e22a0
3 changed files with 55 additions and 7 deletions

View file

@ -770,7 +770,7 @@ void Skate3BaseApp::OnCreateDialogs(rex::ui::ImGuiDrawer* drawer) {
"Native render debug menu", [this] { "Native render debug menu", [this] {
ToggleNativeDebug(); ToggleNativeDebug();
}); });
rex::ui::RegisterBind("bind_skate3_showcase", "Home", rex::ui::RegisterBind("bind_skate3_showcase", "Ctrl+Shift+B",
"Graphics build-up showcase", [] { "Graphics build-up showcase", [] {
REXCVAR_SET( REXCVAR_SET(
skate3_native_render_scene_showcase, skate3_native_render_scene_showcase,

View file

@ -426,7 +426,7 @@ void DrawShowcaseSetupWindow(bool* p_open) {
return; return;
} }
const bool running = REXCVAR_GET(skate3_native_render_scene_showcase); const bool running = REXCVAR_GET(skate3_native_render_scene_showcase);
if (ImGui::Button(running ? "Cancel showcase" : "Start showcase (Home)", if (ImGui::Button(running ? "Cancel showcase" : "Start showcase (Ctrl+Shift+B)",
ImVec2(-1.0f, 0.0f))) { ImVec2(-1.0f, 0.0f))) {
REXCVAR_SET(skate3_native_render_scene_showcase, !running); REXCVAR_SET(skate3_native_render_scene_showcase, !running);
} }
@ -551,7 +551,7 @@ void DrawShowcaseCaptureSection() {
DrawMaxQualityToggle(); DrawMaxQualityToggle();
{ {
const bool running = REXCVAR_GET(skate3_native_render_scene_showcase); const bool running = REXCVAR_GET(skate3_native_render_scene_showcase);
if (ImGui::Button(running ? "Cancel showcase" : "Start showcase (Home)")) { if (ImGui::Button(running ? "Cancel showcase" : "Start showcase (Ctrl+Shift+B)")) {
REXCVAR_SET(skate3_native_render_scene_showcase, !running); REXCVAR_SET(skate3_native_render_scene_showcase, !running);
} }
if (ImGui::IsItemHovered()) { if (ImGui::IsItemHovered()) {

View file

@ -300,8 +300,8 @@ REXCVAR_DEFINE_BOOL(skate3_native_render_scene_showcase, false, "Skate 3",
"both sides rendered live. Layer order and grouping come from " "both sides rendered live. Layer order and grouping come from "
"skate3_native_render_scene_showcase_order (edited in the F12 " "skate3_native_render_scene_showcase_order (edited in the F12 "
"showcase setup window). Clears itself when the sequence " "showcase setup window). Clears itself when the sequence "
"finishes; set to false mid-run to cancel. Also on the Home " "finishes; set to false mid-run to cancel. Also on "
"key by default (bind_skate3_showcase). Layers whose feature " "Ctrl+Shift+B by default (bind_skate3_showcase). Layers whose feature "
"is disabled (or unavailable without the HDR intermediate) " "is disabled (or unavailable without the HDR intermediate) "
"are skipped.") "are skipped.")
.lifecycle(rex::cvar::Lifecycle::kHotReload); .lifecycle(rex::cvar::Lifecycle::kHotReload);
@ -7883,6 +7883,43 @@ static void ApplySunOverride(FrameScene& scene) {
} }
} }
// Hor+ ultrawide: clip-space X scale that widens the game's screen-shaped
// projection to the wide guest-output aspect (see the SDK's
// ApplyNativeGuestOutputWideAspect). 1.0 while the output is not wide. The
// projection aspect is read off the matrix itself (|m11/m00|) so the FOV
// override and freecam zoom compose naturally.
float WideOutputHorScale(const float proj[16]) {
const double wide_aspect = rex::graphics::GetNativeGuestOutputWideAspect();
if (wide_aspect <= 0.0 || !rex::graphics::IsNativeGuestOutputActive()) {
return 1.0f;
}
const float m00 = std::fabs(proj[0]);
const float m11 = std::fabs(proj[5]);
if (!(m00 > 1e-6f) || !(m11 > 1e-6f)) {
return 1.0f;
}
const double base_aspect = double(m11) / double(m00);
if (wide_aspect <= base_aspect + 0.01) {
return 1.0f;
}
return float(base_aspect / wide_aspect);
}
// Applies the Hor+ scale to the published camera: column 0 of the row-vector
// projection and view*proj (clip.x = dot(v, column 0)). Every consumer (draw
// MVPs, post-pass unprojection, splines, frustum tests) reads these published
// matrices, so the frame stays self-consistent, exactly as if the game camera
// itself had the wide aspect.
void WidenPublishedCamera(FrameScene& scene, float scale) {
if (scale == 1.0f) {
return;
}
for (int r = 0; r < 4; ++r) {
scene.proj[r * 4 + 0] *= scale;
scene.view_proj[r * 4 + 0] *= scale;
}
}
void BuildFrameScene(uint8_t* base, const SubmitRecord* records, size_t count) { void BuildFrameScene(uint8_t* base, const SubmitRecord* records, size_t count) {
if (!SceneEnabled()) { if (!SceneEnabled()) {
return; return;
@ -9218,6 +9255,12 @@ void BuildFrameScene(uint8_t* base, const SubmitRecord* records, size_t count) {
} }
} }
// Hor+ ultrawide scale for this frame's camera (1.0 when the output is not
// wide). The matrices are widened after the camera overrides below; the
// retention frustum guards divide by it here so their tests already match
// the frame's real (wider) view.
const float wide_hor_scale = WideOutputHorScale(scene.proj);
// Off-screen retention (see g_retained_items): re-append statics the game // Off-screen retention (see g_retained_items): re-append statics the game
// view-culled this frame while the trailing rendered pose can still see // view-culled this frame while the trailing rendered pose can still see
// them. Runs AFTER the smoothing block so retained copies never enter the // them. Runs AFTER the smoothing block so retained copies never enter the
@ -9275,14 +9318,14 @@ void BuildFrameScene(uint8_t* base, const SubmitRecord* records, size_t count) {
// it. The 0.97 margin shrinks the tested frustum so bounds poking // it. The 0.97 margin shrinks the tested frustum so bounds poking
// just inside an edge still count as view-culled. // just inside an edge still count as view-culled.
if (now - r.last_seen > kRetainTtlFrames || if (now - r.last_seen > kRetainTtlFrames ||
!ItemOutsideFrustum(r.item, guest_vp, 0.97f)) { !ItemOutsideFrustum(r.item, guest_vp, 0.97f / wide_hor_scale)) {
rit = g_retained_items.erase(rit); rit = g_retained_items.erase(rit);
continue; continue;
} }
// Draw it only if the RENDERED pose can actually see it (widened // Draw it only if the RENDERED pose can actually see it (widened
// frustum: only clearly-outside skips); after a fast 180 the trail // frustum: only clearly-outside skips); after a fast 180 the trail
// behind the camera stays retained but costs nothing. // behind the camera stays retained but costs nothing.
if (!ItemOutsideFrustum(r.item, scene.view_proj, 1.05f)) { if (!ItemOutsideFrustum(r.item, scene.view_proj, 1.05f / wide_hor_scale)) {
scene.items.push_back(r.item); scene.items.push_back(r.item);
} }
++rit; ++rit;
@ -9478,6 +9521,11 @@ void BuildFrameScene(uint8_t* base, const SubmitRecord* records, size_t count) {
UpdateFreecam(scene, cam_view, UpdateFreecam(scene, cam_view,
std::chrono::duration<double>(build_t0.time_since_epoch()).count()); std::chrono::duration<double>(build_t0.time_since_epoch()).count());
// Hor+ ultrawide: widen the published camera to the wide output aspect.
// Applied after every camera override (smoothing, synthetic pan, freecam)
// so it survives their view_proj/proj rewrites.
WidenPublishedCamera(scene, wide_hor_scale);
// Publish the frame's captured fog rows and re-arm the OnDrawDone capture // Publish the frame's captured fog rows and re-arm the OnDrawDone capture
// (keyed to this frame's camera) for the next frame. // (keyed to this frame's camera) for the next frame.
if (g_fog_have) { if (g_fog_have) {