From 08935ec07a1da7c7808d48844a4d06dbc4358bd2 Mon Sep 17 00:00:00 2001
From: TheLostInPlace <42595640+TheLostInPlace@users.noreply.github.com>
Date: Sun, 19 Jul 2026 00:50:50 -0500
Subject: [PATCH 1/4] staging: fresh commit for PR
---
.gitignore | 6 +-
gamedata/configs/mod_system_pip_tonemap.ltx | 16 +
gamedata/configs/pip_objective_mm.ltx | 78 +
.../configs/text/eng/ui_mm_modded_exes.xml | 50 +
gamedata/scripts/_imgui_groups.script | 2 +-
.../scripts/options_modded_exes_pip.script | 64 +
.../scripts/options_modded_exes_visual.script | 2 +
.../scripts/zzz_extra_scope_features.script | 167 +++
gamedata/shaders/r3/scope_color_write.s | 20 +
gamedata/shaders/r3/scope_custom_depth.h | 26 +
gamedata/shaders/r3/scope_debug.ps | Bin 0 -> 1091 bytes
gamedata/shaders/r3/scope_debug.s | 15 +
gamedata/shaders/r3/scope_defines.h | 19 +
gamedata/shaders/r3/scope_depth_write.ps | Bin 0 -> 1026 bytes
gamedata/shaders/r3/scope_depth_write.s | 5 +
gamedata/shaders/r3/scope_vertex.vs | 55 +
gamedata/shaders/r3/svp_distort_stamp.ps | Bin 0 -> 339 bytes
gamedata/shaders/r3/svp_distort_stamp.s | 6 +
gamedata/shaders/r3/svp_hooks_common.h | 41 +
gamedata/shaders/r3/svp_hooks_image.h | 208 +++
gamedata/shaders/r3/svp_hooks_lens.h | 15 +
gamedata/shaders/r3/svp_hooks_reticle.h | 55 +
gamedata/shaders/r3/svp_hooks_shadow.h | 30 +
gamedata/shaders/r3/svp_nearblur.ps | Bin 0 -> 2649 bytes
gamedata/shaders/r3/svp_nearblur.s | 8 +
gamedata/shaders/r3/svp_taa_stamp.ps | Bin 0 -> 307 bytes
gamedata/shaders/r3/svp_taa_stamp.s | 6 +
gamedata/textures/fx/svp_rain.dds | Bin 0 -> 349652 bytes
src/Include/xrRender/Kinematics.h | 19 +
src/Include/xrRender/ParticleCustom.h | 3 +
.../Blender_Recorder_StandartBinding.cpp | 97 +-
src/Layers/xrRender/Debug/dxPixEventWrapper.h | 5 +
src/Layers/xrRender/Debug/renderdoc_app.h | 875 +++++++++++
src/Layers/xrRender/DetailManager_VS.cpp | 17 +-
src/Layers/xrRender/FTreeVisual.cpp | 11 +-
src/Layers/xrRender/HW.h | 4 +
src/Layers/xrRender/Light_DB.cpp | 8 +-
src/Layers/xrRender/ParticleEffect.cpp | 20 +-
src/Layers/xrRender/ParticleEffect.h | 4 +
src/Layers/xrRender/ParticleGroup.cpp | 22 +
src/Layers/xrRender/ParticleGroup.h | 3 +
src/Layers/xrRender/R_Backend.h | 2 +-
src/Layers/xrRender/R_Backend_Runtime.h | 48 +-
src/Layers/xrRender/R_Backend_xform.cpp | 73 +-
src/Layers/xrRender/R_Backend_xform.h | 24 +-
src/Layers/xrRender/R_calculate.cpp | 9 +
src/Layers/xrRender/R_sun.cpp | 21 +
src/Layers/xrRender/Shader.h | 2 +-
src/Layers/xrRender/SkeletonCustom.h | 5 +-
src/Layers/xrRender/SkeletonX.cpp | 26 +-
src/Layers/xrRender/SkeletonX.h | 5 +
src/Layers/xrRender/dxEnvironmentRender.cpp | 9 +-
src/Layers/xrRender/dxRenderDeviceRender.cpp | 5 +-
src/Layers/xrRender/lights_render.cpp | 140 +-
src/Layers/xrRender/r__dsgraph_build.cpp | 106 +-
src/Layers/xrRender/r__dsgraph_manager.h | 36 +-
src/Layers/xrRender/r__dsgraph_render.cpp | 105 +-
src/Layers/xrRender/r__dsgraph_types.h | 6 +
.../rendertarget_phase_nightvision.cpp | 17 +-
.../xrRender/rendertarget_phase_smaa.cpp | 25 +
src/Layers/xrRender/svp_console.cpp | 256 ++++
src/Layers/xrRender/svp_console.h | 131 ++
src/Layers/xrRender/svp_constants.cpp | 185 +++
src/Layers/xrRender/svp_constants.h | 14 +
src/Layers/xrRender/svp_dsgraph_cull.cpp | 67 +
src/Layers/xrRender/svp_hud_latch.cpp | 273 ++++
src/Layers/xrRender/svp_lens_detect.cpp | 251 ++++
src/Layers/xrRender/svp_lens_detect.h | 8 +
src/Layers/xrRender/svp_lens_detect_math.cpp | 359 +++++
src/Layers/xrRender/svp_lens_detect_math.h | 67 +
src/Layers/xrRender/xrRender_console.cpp | 65 +
src/Layers/xrRender/xrRender_console.h | 4 +
.../xrRenderDX10/dx10DetailManager_VS.cpp | 15 +-
src/Layers/xrRenderDX10/dx10EventWrapper.cpp | 42 +-
src/Layers/xrRenderDX10/dx10EventWrapper.h | 25 +-
src/Layers/xrRenderDX10/dx10HW.cpp | 21 +-
.../dx10ResourceManager_Scripting.cpp | 6 +
src/Layers/xrRenderDX10/dx10SH_RT.cpp | 8 +
src/Layers/xrRenderPC_R1/xrRender_R1.vcxproj | 12 +
.../xrRenderPC_R1/xrRender_R1.vcxproj.filters | 30 +
src/Layers/xrRenderPC_R2/xrRender_R2.vcxproj | 12 +
.../xrRenderPC_R2/xrRender_R2.vcxproj.filters | 30 +
src/Layers/xrRenderPC_R3/xrRender_R3.vcxproj | 12 +
.../xrRenderPC_R3/xrRender_R3.vcxproj.filters | 30 +
src/Layers/xrRenderPC_R4/SvpDlss.h | 65 +
src/Layers/xrRenderPC_R4/r4.cpp | 48 +-
src/Layers/xrRenderPC_R4/r4.h | 10 +-
src/Layers/xrRenderPC_R4/r4_R_render.cpp | 467 +++++-
src/Layers/xrRenderPC_R4/r4_rendertarget.cpp | 297 ++--
src/Layers/xrRenderPC_R4/r4_rendertarget.h | 42 +
.../r4_rendertarget_phase_combine.cpp | 236 ++-
.../r4_rendertarget_phase_luminance.cpp | 9 +-
src/Layers/xrRenderPC_R4/svp_camera.cpp | 1308 +++++++++++++++++
src/Layers/xrRenderPC_R4/svp_camera.h | 13 +
src/Layers/xrRenderPC_R4/svp_optics.cpp | 991 +++++++++++++
src/Layers/xrRenderPC_R4/svp_stats.cpp | 524 +++++++
src/Layers/xrRenderPC_R4/svp_stats.h | 43 +
src/Layers/xrRenderPC_R4/svp_target.cpp | 121 ++
src/Layers/xrRenderPC_R4/svp_target.h | 11 +
src/Layers/xrRenderPC_R4/xrRender_R4.vcxproj | 19 +
.../xrRenderPC_R4/xrRender_R4.vcxproj.filters | 51 +
src/xrCore/Xr_ini.cpp | 13 +
src/xrCore/_matrix.h | 12 +
src/xrCore/log.h | 1 +
src/xrCore/svp_log.cpp | 45 +
src/xrCore/svp_log.h | 8 +
src/xrCore/xrCore.vcxproj | 2 +
src/xrCore/xrCore.vcxproj.filters | 6 +
src/xrCore/xr_ini.h | 3 +
src/xrEngine/CameraManager.cpp | 59 +-
src/xrEngine/CameraManager.h | 1 +
src/xrEngine/XR_IOConsole.cpp | 2 +
src/xrEngine/bone.h | 4 +-
src/xrEngine/device.cpp | 33 +-
src/xrEngine/device.h | 84 +-
src/xrEngine/imgui_base.cpp | 5 +
src/xrEngine/svp_crash_context.cpp | 31 +
src/xrEngine/svp_crash_context.h | 23 +
src/xrEngine/svp_gameplay_cvars.cpp | 35 +
src/xrEngine/svp_gameplay_cvars.h | 27 +
src/xrEngine/svp_state.h | 122 ++
src/xrEngine/x_ray.cpp | 3 +
src/xrEngine/xrEngine.vcxproj | 5 +
src/xrEngine/xrEngine.vcxproj.filters | 15 +
src/xrEngine/xr_ioc_cmd.cpp | 2 +
src/xrGame/Actor.cpp | 14 +-
src/xrGame/Actor.h | 2 +-
src/xrGame/ActorCameras.cpp | 9 +
src/xrGame/ActorEffector.cpp | 9 +-
src/xrGame/ActorInput.cpp | 12 +-
src/xrGame/Actor_Weapon.cpp | 4 +
src/xrGame/HudItem.cpp | 21 +-
src/xrGame/Level.cpp | 3 +-
src/xrGame/ParticlesObject.cpp | 10 +
src/xrGame/ParticlesObject.h | 1 +
src/xrGame/ShootingObject.cpp | 5 +
src/xrGame/UIGameCustom.cpp | 43 +-
src/xrGame/UIGameCustom.h | 3 +
src/xrGame/Weapon.cpp | 256 +++-
src/xrGame/Weapon.h | 34 +-
src/xrGame/WeaponAK74.cpp | 11 +-
src/xrGame/WeaponBinoculars.cpp | 49 +-
src/xrGame/WeaponBinoculars.h | 4 +
src/xrGame/WeaponBinocularsVision.cpp | 50 +-
src/xrGame/console_registrator_script.cpp | 50 +
src/xrGame/level_script.cpp | 16 +
src/xrGame/svp_mags.cpp | 102 ++
src/xrGame/svp_mags.h | 19 +
src/xrGame/svp_weapon.cpp | 243 +++
src/xrGame/xrGame.vcxproj | 3 +
src/xrGame/xrGame.vcxproj.filters | 9 +
151 files changed, 9846 insertions(+), 501 deletions(-)
create mode 100644 gamedata/configs/mod_system_pip_tonemap.ltx
create mode 100644 gamedata/configs/pip_objective_mm.ltx
create mode 100644 gamedata/scripts/options_modded_exes_pip.script
create mode 100644 gamedata/scripts/zzz_extra_scope_features.script
create mode 100644 gamedata/shaders/r3/scope_color_write.s
create mode 100644 gamedata/shaders/r3/scope_custom_depth.h
create mode 100644 gamedata/shaders/r3/scope_debug.ps
create mode 100644 gamedata/shaders/r3/scope_debug.s
create mode 100644 gamedata/shaders/r3/scope_defines.h
create mode 100644 gamedata/shaders/r3/scope_depth_write.ps
create mode 100644 gamedata/shaders/r3/scope_depth_write.s
create mode 100644 gamedata/shaders/r3/scope_vertex.vs
create mode 100644 gamedata/shaders/r3/svp_distort_stamp.ps
create mode 100644 gamedata/shaders/r3/svp_distort_stamp.s
create mode 100644 gamedata/shaders/r3/svp_hooks_common.h
create mode 100644 gamedata/shaders/r3/svp_hooks_image.h
create mode 100644 gamedata/shaders/r3/svp_hooks_lens.h
create mode 100644 gamedata/shaders/r3/svp_hooks_reticle.h
create mode 100644 gamedata/shaders/r3/svp_hooks_shadow.h
create mode 100644 gamedata/shaders/r3/svp_nearblur.ps
create mode 100644 gamedata/shaders/r3/svp_nearblur.s
create mode 100644 gamedata/shaders/r3/svp_taa_stamp.ps
create mode 100644 gamedata/shaders/r3/svp_taa_stamp.s
create mode 100644 gamedata/textures/fx/svp_rain.dds
create mode 100644 src/Layers/xrRender/Debug/renderdoc_app.h
create mode 100644 src/Layers/xrRender/svp_console.cpp
create mode 100644 src/Layers/xrRender/svp_console.h
create mode 100644 src/Layers/xrRender/svp_constants.cpp
create mode 100644 src/Layers/xrRender/svp_constants.h
create mode 100644 src/Layers/xrRender/svp_dsgraph_cull.cpp
create mode 100644 src/Layers/xrRender/svp_hud_latch.cpp
create mode 100644 src/Layers/xrRender/svp_lens_detect.cpp
create mode 100644 src/Layers/xrRender/svp_lens_detect.h
create mode 100644 src/Layers/xrRender/svp_lens_detect_math.cpp
create mode 100644 src/Layers/xrRender/svp_lens_detect_math.h
create mode 100644 src/Layers/xrRenderPC_R4/SvpDlss.h
create mode 100644 src/Layers/xrRenderPC_R4/svp_camera.cpp
create mode 100644 src/Layers/xrRenderPC_R4/svp_camera.h
create mode 100644 src/Layers/xrRenderPC_R4/svp_optics.cpp
create mode 100644 src/Layers/xrRenderPC_R4/svp_stats.cpp
create mode 100644 src/Layers/xrRenderPC_R4/svp_stats.h
create mode 100644 src/Layers/xrRenderPC_R4/svp_target.cpp
create mode 100644 src/Layers/xrRenderPC_R4/svp_target.h
create mode 100644 src/xrCore/svp_log.cpp
create mode 100644 src/xrCore/svp_log.h
create mode 100644 src/xrEngine/svp_crash_context.cpp
create mode 100644 src/xrEngine/svp_crash_context.h
create mode 100644 src/xrEngine/svp_gameplay_cvars.cpp
create mode 100644 src/xrEngine/svp_gameplay_cvars.h
create mode 100644 src/xrEngine/svp_state.h
create mode 100644 src/xrGame/svp_mags.cpp
create mode 100644 src/xrGame/svp_mags.h
create mode 100644 src/xrGame/svp_weapon.cpp
diff --git a/.gitignore b/.gitignore
index 79ddee2722..2a7492b2c4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -33,8 +33,12 @@ src/3rd party/luajit-2/src/host/*.exe
# compressor runtime artifacts
compressor/engine.log
+
+# local build logs
+/build_*.log
compressor/mod/
+compressor/mod.db0
# emacs temp file
\#*\#
-*~
\ No newline at end of file
+*~
diff --git a/gamedata/configs/mod_system_pip_tonemap.ltx b/gamedata/configs/mod_system_pip_tonemap.ltx
new file mode 100644
index 0000000000..7003731bae
--- /dev/null
+++ b/gamedata/configs/mod_system_pip_tonemap.ltx
@@ -0,0 +1,16 @@
+; PiP per-scope SVP tonemapper overrides. Optional per-optic exposure control for the
+; second-viewport render, pushed by zzz_extra_scope_features on aim change. Both keys default
+; to 0 = inherit the main tonemap, so an optic with no entry here behaves byte-identical to stock.
+;
+; s3ds_middle_grey target middle-grey for the scope's own adaptation (lower = darker scope image,
+; the anti-blowout lever for bright-sky / NVG / thermal optics). ~0.05-0.5.
+; s3ds_adapt_speed how fast the scope re-exposes (lower = slower, holds exposure through a muzzle
+; flash or a sky pan instead of snapping bright). ~1-8.
+;
+; Authored game-feel, not spec-sheet data. Seed conservatively per optic and tune in-game with the
+; live console cvars s3ds_middle_grey / s3ds_adapt_speed before committing values here.
+; Example (commented out until tuned):
+;
+; ![some_nvg_scope_section]
+; s3ds_middle_grey = 0.18
+; s3ds_adapt_speed = 3
diff --git a/gamedata/configs/pip_objective_mm.ltx b/gamedata/configs/pip_objective_mm.ltx
new file mode 100644
index 0000000000..684e280cb4
--- /dev/null
+++ b/gamedata/configs/pip_objective_mm.ltx
@@ -0,0 +1,78 @@
+; PiP per-scope objective clear aperture (mm), from manufacturer spec sheets.
+; Feeds the true exit pupil (objective / magnification) for twilight dimming and the
+; eyebox, absolute mm so it is independent of the mesh-relative scope_objective_lens_offset.w.
+; Read directly by zzz_extra_scope_features (plain sections, not a dltx merge target) and
+; pushed on aim change, a section-authored s3ds_objective_mm still wins
+
+[pn23]
+s3ds_objective_mm = 50
+
+[wpn_addon_scope_pu]
+s3ds_objective_mm = 22
+
+[marchf]
+s3ds_objective_mm = 42
+
+; leupold is the Mark 8 CQBSS 1.1-8x24, the earlier 40mm came from a mismatched Mark 4 LR/T
+[leupold]
+s3ds_objective_mm = 24
+
+[mark8_rmr]
+s3ds_objective_mm = 24
+
+[razorhd]
+s3ds_objective_mm = 24
+
+[march_f_shorty]
+s3ds_objective_mm = 24
+
+[march_f_shorty_alt]
+s3ds_objective_mm = 24
+
+[wpn_l96a1]
+s3ds_objective_mm = 50
+
+[wpn_l96a1m]
+s3ds_objective_mm = 50
+
+[c79]
+s3ds_objective_mm = 28
+
+[acog]
+s3ds_objective_mm = 32
+
+[1p29]
+s3ds_objective_mm = 26
+
+[usp1]
+s3ds_objective_mm = 26
+
+[ps01]
+s3ds_objective_mm = 24
+
+[pso2]
+s3ds_objective_mm = 24
+
+[pso1m21]
+s3ds_objective_mm = 24
+
+[pritseldob]
+s3ds_objective_mm = 24
+
+[vulcan]
+s3ds_objective_mm = 48
+
+[wpn_fn2000]
+s3ds_objective_mm = 15
+
+[wpn_addon_scope_po4x34]
+s3ds_objective_mm = 34
+
+[chs]
+s3ds_objective_mm = 20
+
+[wpn_remington700]
+s3ds_objective_mm = 40
+
+[wpn_m98b]
+s3ds_objective_mm = 40
diff --git a/gamedata/configs/text/eng/ui_mm_modded_exes.xml b/gamedata/configs/text/eng/ui_mm_modded_exes.xml
index 8903b49976..54b8cc170c 100644
--- a/gamedata/configs/text/eng/ui_mm_modded_exes.xml
+++ b/gamedata/configs/text/eng/ui_mm_modded_exes.xml
@@ -295,6 +295,56 @@
Fog Max Travel Distance (scope_fog_travel)
+
+
+
+ Picture in Picture
+
+
+ Picture in Picture Mode (r__svpscope)
+
+
+ True scope rendering, a real second render of the scene through the lens. Eyepiece places the scope camera at the ocular lens, Objective at the front lens
+
+
+ Zoom Smoothing Rate (g_zoom_smooth)
+
+
+ Lerp speed of the dynamic zoom transition, higher is faster, 0 is instant stepped
+
+
+ Analog Zoom Steps (g_zoom_analog)
+
+
+ 0 uses the scope's configured zoom steps, higher gives finer continuous zoom across the range
+
+
+ Scope Render Scale [DLSS only] (r__svp_render_scale)
+
+
+ Resolution scale of the scope render, inert until DLSS upscaling is enabled, applies on video restart
+
+
+ DLSS Scope Upscaling [experimental] (r__svp_dlss)
+
+
+ Experimental DLSS-SR path for the scope render, currently a passthrough stub
+
+
+ Scope Debug Overlay (r__scope_debug)
+
+
+ Developer overlay, draws the derived eyepiece, objective and camera lenses in the world
+
+
+ Eyepiece
+
+
+ Objective
+
+
Particles
diff --git a/gamedata/scripts/_imgui_groups.script b/gamedata/scripts/_imgui_groups.script
index ef6dac91a3..5fb808b995 100644
--- a/gamedata/scripts/_imgui_groups.script
+++ b/gamedata/scripts/_imgui_groups.script
@@ -136,7 +136,7 @@ ImGui.Groups.Add("MenuBar")
--- Unique, renders unconditionally
ImGui.Groups.Add("Unique")
-AddUniqueCall(ImGui.Groups.Unique.Render)
+-- Rendered by the engine every frame (ide::OnFrame), frame safe with mt_level_call
--- Debug, provides a shared menu for tooling (inspectors, editors, etc.)
ImGui.Groups.Add("Debug")
diff --git a/gamedata/scripts/options_modded_exes_pip.script b/gamedata/scripts/options_modded_exes_pip.script
new file mode 100644
index 0000000000..b56ff6deda
--- /dev/null
+++ b/gamedata/scripts/options_modded_exes_pip.script
@@ -0,0 +1,64 @@
+--- Picture in Picture scope options tree
+
+-- Import smart constructors
+options_builder.import_into(this)
+
+-- a matched PiP exe registers the PiP cvars; on a non-PiP engine the controls below would read cvars that
+-- do not exist and crash the menu on open. probe one PiP cvar safely (get_variable_bounds returns an empty
+-- table for a missing command) and only expose the controls when the engine actually supports them
+local function pip_engine_present()
+ local ok, b = pcall(function() return get_console():get_variable_bounds("r__svpscope") end)
+ return ok and b ~= nil and b.max ~= nil
+end
+
+local _full = page {
+ { id = "pip" },
+
+ list_enum {
+ id = "r__svpscope",
+ content = {
+ "OFF",
+ "Eyepiece",
+ "Objective"
+ },
+ },
+ track {
+ id = "g_zoom_smooth",
+ def = 12,
+ min = 0,
+ max = 60,
+ step = 1,
+ },
+ track {
+ id = "g_zoom_analog",
+ def = 0,
+ min = 0,
+ max = 200,
+ step = 5,
+ },
+
+ line {},
+
+ track {
+ id = "r__svp_render_scale",
+ def = 1.0,
+ min = 0.4,
+ max = 1.0,
+ step = 0.05,
+ },
+ list_bool { id = "r__svp_dlss" },
+
+ line {},
+
+ track {
+ id = "r__scope_debug",
+ def = 0,
+ min = 0,
+ max = 4,
+ step = 1,
+ },
+
+}
+
+-- expose the controls only on a matched PiP engine; otherwise an empty page so the menu never reads the missing cvars
+PAGE = pip_engine_present() and _full or page { { id = "pip" }, line {} }
diff --git a/gamedata/scripts/options_modded_exes_visual.script b/gamedata/scripts/options_modded_exes_visual.script
index 56c0c96078..9d2ba1283a 100644
--- a/gamedata/scripts/options_modded_exes_visual.script
+++ b/gamedata/scripts/options_modded_exes_visual.script
@@ -4,6 +4,7 @@ local page_ui_hud = options_modded_exes_ui_hud.PAGE
local page_crosshair = options_modded_exes_crosshair.PAGE
local page_hdr10 = options_modded_exes_hdr10.PAGE
local page_3d_scopes = options_modded_exes_3d_scopes.PAGE
+local page_pip = options_modded_exes_pip.PAGE
local page_particles = options_modded_exes_particles.PAGE
local page_graphics = options_modded_exes_graphics.PAGE
@@ -17,6 +18,7 @@ GROUP = group {
page_ui_hud,
page_crosshair,
page_3d_scopes,
+ page_pip,
page_particles,
page_hdr10,
}
\ No newline at end of file
diff --git a/gamedata/scripts/zzz_extra_scope_features.script b/gamedata/scripts/zzz_extra_scope_features.script
new file mode 100644
index 0000000000..2d56e0773d
--- /dev/null
+++ b/gamedata/scripts/zzz_extra_scope_features.script
@@ -0,0 +1,167 @@
+--[[
+ zzz_extra_scope_features (true-PiP)
+
+ Pushes the per-scope objective lens geometry to the engine so the SVP can place the front plane
+ and size the defocus aperture per optic. The scope_objective_lens_offset key (3DSS objective-lens
+ data, x/y lateral, z forward, w radius, all in eyepiece-radius units) is resolved from the active
+ optic's section and written to the scope_objective_lens_offset console command on every aim change.
+
+ Zoom is owned by the engine (curMag drive) and the 3DSS/Godis controllers. This script NEVER
+ touches zoom. On a non-PiP exe the command is unregistered, get_string returns nil, and the whole
+ script bails - stock behaviour, no side effects.
+]]--
+
+-- Taken from weapon_cover_tilt.script
+function IsBinoc(sec)
+ return SYS_GetParam(0, sec, "ammo_class", "") == "ammo_binoc"
+ or SYS_GetParam(0, sec, "class", "") == "WP_BINOC"
+end
+
+local RETRY_EV = "pip_scope_push"
+
+-- read a key from the attached-scope section first, then the weapon section, absent = nil
+local function read_str(scope_sec, weap_sec, key)
+ return (scope_sec and ini_sys:r_string_ex(scope_sec, key)) or (weap_sec and ini_sys:r_string_ex(weap_sec, key))
+end
+local function read_num(scope_sec, weap_sec, key)
+ return (scope_sec and ini_sys:r_float_ex(scope_sec, key)) or (weap_sec and ini_sys:r_float_ex(weap_sec, key))
+end
+
+-- per-scope SVP tonemapper and ballistic zero, absent keys push the safe default
+local function push_aux(scope_sec, weap_sec)
+ exec_console_cmd("s3ds_middle_grey " .. (read_num(scope_sec, weap_sec, "s3ds_middle_grey") or 0))
+ exec_console_cmd("s3ds_adapt_speed " .. (read_num(scope_sec, weap_sec, "s3ds_adapt_speed") or 0))
+ local zero = read_num(scope_sec, weap_sec, "scope_zero_m")
+ exec_console_cmd("g_svp_zero " .. ((zero and zero > 0) and zero or 100))
+end
+
+-- spec-sheet mm rides its own plain-section ltx read directly, a section-authored key still wins
+local mm_ini
+local function read_mm(scope_sec, weap_sec)
+ local mm = read_num(scope_sec, weap_sec, "s3ds_objective_mm")
+ if mm then
+ return mm
+ end
+ mm_ini = mm_ini or ini_file("pip_objective_mm.ltx")
+ return (scope_sec and mm_ini:r_float_ex(scope_sec, "s3ds_objective_mm"))
+ or (weap_sec and mm_ini:r_float_ex(weap_sec, "s3ds_objective_mm"))
+end
+
+-- authored offset (scope then weapon) beats the engine measured detection, returns true when a value landed
+local function push_lens(scope_sec, weap_sec)
+ local off = read_str(scope_sec, weap_sec, "scope_objective_lens_offset")
+ local src = off and "authored"
+ if not off then
+ local det = svp_detected_offset and svp_detected_offset()
+ if det and det ~= "" then off, src = det, "detected" end
+ end
+ if not off then
+ return false
+ end
+ exec_console_cmd("scope_objective_lens_offset " .. off)
+ local obj = read_mm(scope_sec, weap_sec)
+ if not obj then
+ local dmm = svp_detected_obj_mm and svp_detected_obj_mm()
+ if dmm and dmm > 0 then obj = dmm end
+ end
+ exec_console_cmd("s3ds_objective_mm " .. (obj or 0))
+ printf("[SVP-PUSH] sec=%s src=%s off=%s mm=%s", scope_sec or weap_sec or "?", src, off, tostring(obj or 0))
+ return true
+end
+
+-- resolve and push for the live aim, returns true when settled (pushed, alt-cleared or unscoped)
+local function try_push()
+ local item = db.actor and db.actor:active_item()
+ local weapon = item and item:cast_Weapon()
+ if not (item and weapon and weapon:IsZoomed()) then
+ return true
+ end
+ if IsBinoc(item:section()) then
+ return true
+ end
+ local scope_sec = utils_item.get_attached_scope(item)
+ local weap_sec = item:section()
+ push_aux(scope_sec, weap_sec)
+ -- the resolved value wins even on an alt zoom type, gamma analog zoom raises the type on the
+ -- same primary optic right after zoom in and must not stomp the just pushed value
+ if push_lens(scope_sec, weap_sec) then
+ return true
+ end
+ -- genuinely no objective, an alt sight clears the stale primary value, the primary optic keeps
+ -- its last value and retries until the detection cache warms
+ if weapon:GetZoomType() ~= 0 then
+ exec_console_cmd("scope_objective_lens_offset 0,0,0,0")
+ exec_console_cmd("s3ds_objective_mm 0")
+ return true
+ end
+ return false
+end
+
+-- half second cadence so the retry is no busy loop, the cap rides out a slow first load
+-- (a fresh session held the weapon unsettled past 9s) yet ends a sight with nothing to resolve
+local RETRY_STEP_S = 0.5
+local RETRY_MAX = 60
+
+local function push_offset()
+ if try_push() then
+ return
+ end
+ -- detection cache is cold on the zoom-in frame, leave the previous value and re-resolve until it warms
+ printf("[SVP-PUSH] skip kept, retrying the live aim")
+ RemoveTimeEvent(RETRY_EV, "retry")
+ local tries = 0
+ CreateTimeEvent(RETRY_EV, "retry", RETRY_STEP_S, function()
+ tries = tries + 1
+ if try_push() then
+ return true
+ end
+ if tries >= RETRY_MAX then
+ printf("[SVP-PUSH] unresolved after retry, kept")
+ return true
+ end
+ ResetTimeEvent(RETRY_EV, "retry", RETRY_STEP_S)
+ return false
+ end)
+end
+
+local function clear_offset()
+ RemoveTimeEvent(RETRY_EV, "retry")
+ -- the objective/mm are read only while scoped, leave them for the next aim to re-resolve so a
+ -- zoom out never stomps a just pushed value, only the scope tonemapper returns to the default view
+ exec_console_cmd("s3ds_middle_grey 0")
+ exec_console_cmd("s3ds_adapt_speed 0")
+end
+
+-- one-shot per install, resets only the listed cvars and leaves every other setting alone
+local PIP_MIGRATION_VERSION = 20260712
+local function run_migrations()
+ local saved = axr_main.config:r_value("pip", "migration_version", 2, 0)
+ if saved >= PIP_MIGRATION_VERSION then
+ return
+ end
+ if saved < 20260710 then
+ -- 20260710 experimental glass optics ship default-off, clear values saved by older builds
+ exec_console_cmd("r__svp_veiling_glare 0")
+ exec_console_cmd("r__svp_coating 0")
+ exec_console_cmd("r__svp_mirage 0")
+ exec_console_cmd("r__svp_reticle_washout 0")
+ end
+ if saved < 20260712 then
+ -- 20260712 objective fallback re-sourced to the authored-set median
+ exec_console_cmd("r__svp_obj_size 1.0")
+ end
+ axr_main.config:w_value("pip", "migration_version", PIP_MIGRATION_VERSION)
+ axr_main.config:save()
+ printf("pip migration %s applied", PIP_MIGRATION_VERSION)
+end
+
+function on_game_start()
+ if not get_console():get_string("scope_objective_lens_offset") then
+ printf("dev/svp exes not installed - extra scope features inert")
+ return
+ end
+ run_migrations()
+ RegisterScriptCallback("actor_on_weapon_zoom_in", push_offset)
+ RegisterScriptCallback("actor_on_weapon_zoom_type_changed", push_offset)
+ RegisterScriptCallback("actor_on_weapon_zoom_out", clear_offset)
+end
diff --git a/gamedata/shaders/r3/scope_color_write.s b/gamedata/shaders/r3/scope_color_write.s
new file mode 100644
index 0000000000..4d0c751800
--- /dev/null
+++ b/gamedata/shaders/r3/scope_color_write.s
@@ -0,0 +1,20 @@
+function normal(shader, t_base, t_second, t_detail)
+ shader:begin("scope_vertex", "scope_color_write")
+ : zb(true, false)
+ : blend(true, blend.srcalpha, blend.invsrcalpha)
+ : scopelense(3)
+ shader:dx10texture("s_reticle", "$user$reticle")
+ shader:dx10texture("s_image", "$user$viewport2")
+ shader:dx10texture("s_tonemap", "$user$tonemap")
+ shader:dx10texture("s_tonemap_svp", "$user$svp_tonemap")
+ shader:dx10texture("s_heat", "$user$heat")
+ shader:dx10texture("s_distort", "$user$generic1")
+ shader:dx10texture("s_position", "$user$generic2")
+ shader:dx10texture("s_inside", "wpn\\scope_utility\\inside")
+ shader:dx10texture("s_dirt", "wpn\\scope_utility\\dirt")
+ shader:dx10texture("s_reflection", "wpn\\scope_utility\\reflection")
+ shader:dx10texture("s_heat_map", "wpn\\scope_utility\\heat_map")
+ shader:dx10texture("s_svp_rain", "fx\\svp_rain")
+ shader:dx10sampler("smp_base")
+ shader:dx10sampler("smp_rtlinear");
+end
\ No newline at end of file
diff --git a/gamedata/shaders/r3/scope_custom_depth.h b/gamedata/shaders/r3/scope_custom_depth.h
new file mode 100644
index 0000000000..d0e77e2b4e
--- /dev/null
+++ b/gamedata/shaders/r3/scope_custom_depth.h
@@ -0,0 +1,26 @@
+/*
+ =====================================================================
+ Addon : Shader 3D Scopes
+ Link : https://www.moddb.com/mods/stalker-anomaly/addons/shader-3d-scopes
+ Authors : LVutner, party_50
+
+ All credit to original authors.
+ =====================================================================
+*/
+
+#include "scope_3dss_common.h"
+
+float scope_custom_depth(float4 hpos) {
+ bool nvg_blur = SETTING(SETTINGS, ST_NVG_BLUR) && floor(shader_param_8.x) != 0 || m_hud_params.x == 0;
+
+ float NO_BLUR = 100.0;
+
+ // true pip under nvg stamps the disc as unwritten sky, the nightvision depth terms
+ // otherwise read the 100m marker as mid range scenery and boost it into washout
+ if (shader_scope_params.w < -1.5 && floor(shader_param_8.x) != 0)
+ return 0.0;
+
+ return nvg_blur
+ ? hpos.z
+ : NO_BLUR;
+}
\ No newline at end of file
diff --git a/gamedata/shaders/r3/scope_debug.ps b/gamedata/shaders/r3/scope_debug.ps
new file mode 100644
index 0000000000000000000000000000000000000000..5384c3d0fefc1d13b37b62e12546daf1c8b0c2e3
GIT binary patch
literal 1091
zcmaJGsifs~#)
z<2P^Kyu4FYi}j|Ia8j$Mm1kS!R8!N{2|3-rh(_zKN0xHuH&&*S6dlR;4fH-XVqg9^ck)p$>AUAMv<0lvzA3*MLXh_Fan}l^%-r8ynr7!kkIrZRH}m53%Y|_n9~H_-cTQX!RnkQL1(F);I_>1
z?|y~F4c)dFQ8UO88n}8pdOGX^8lgw8qt;pyLHJ#TjbVS<4g@t}e?l)WaXx(eF@~v~
zbdw!6%K{c1I*yG)&W=4#;XFz3jBxUYBD@Z1zbYEN>4>6lutCw$=TC}0?zJ^dUI?XKTOcJnpiF5$mIM7LriS+C1RO|83n+_y7O^
literal 0
HcmV?d00001
diff --git a/gamedata/shaders/r3/scope_debug.s b/gamedata/shaders/r3/scope_debug.s
new file mode 100644
index 0000000000..33b6983102
--- /dev/null
+++ b/gamedata/shaders/r3/scope_debug.s
@@ -0,0 +1,15 @@
+function normal(shader, t_base, t_second, t_detail)
+ shader:begin("stub_notransform_t", "scope_debug")
+ : zb(false, false)
+ : blend(true, blend.srcalpha, blend.invsrcalpha)
+ shader:dx10texture("s_3dss_tex", "$user$viewport2$main")
+ shader:dx10texture("s_pip_tex", "$user$viewport2$svp")
+ shader:dx10sampler("smp_base")
+ shader:dx10texture("pp_m", "$user$ssfx_prev_p$main")
+ shader:dx10texture("pp_s", "$user$ssfx_prev_p$svp")
+ shader:dx10texture("mv_m", "$user$ssfx_motion_vectors$main")
+ shader:dx10texture("mv_s", "$user$ssfx_motion_vectors$svp")
+ shader:dx10texture("pf_m", "$user$ssfx_prev_frame$main")
+ shader:dx10texture("pf_s", "$user$ssfx_prev_frame$svp")
+ shader:dx10texture("sm" , "$user$smap_depth")
+end
\ No newline at end of file
diff --git a/gamedata/shaders/r3/scope_defines.h b/gamedata/shaders/r3/scope_defines.h
new file mode 100644
index 0000000000..1babad5d51
--- /dev/null
+++ b/gamedata/shaders/r3/scope_defines.h
@@ -0,0 +1,19 @@
+// DO NOT EDIT, OR INCLUDE IN MODS
+
+#ifndef SCOPE_DEFINES_H
+#define SCOPE_DEFINES_H
+
+
+#define SCOPE_PHASE_GBUFFER 1
+#define SCOPE_PHASE_DEPTHWRITE 2
+#define SCOPE_PHASE_IMAGE 8
+#define SCOPE_PHASE_RETICLE 16
+#define SCOPE_PHASE_CUSTOM_DEPTH 32
+#define SCOPE_PHASE_SHADOW 64
+#define SCOPE_PHASE_LENS 128
+
+// Hacky solution to fix jitter/ring around scope
+#define SCOPE_PHASE_JITTERFIX 256
+
+
+#endif
\ No newline at end of file
diff --git a/gamedata/shaders/r3/scope_depth_write.ps b/gamedata/shaders/r3/scope_depth_write.ps
new file mode 100644
index 0000000000000000000000000000000000000000..1d4cea4421ef6922180f20d2aa9747379b74b82e
GIT binary patch
literal 1026
zcmaJ=U2EGg6n(CK#ifOjKu8?crr%f?j*m!?!L7Ny$nm7MM_
z8Jo@Ei_*_rS5RTuaE(wqSz4i_fm&&JLTgb2YE3_TG=^`{u)0y)!ja7xtM)~Ot=)~f
z!Zqw#Wyay(!UO2L{7qGXRbZU1d5P2o(fExWmsNw%YAl7Jc0d^8NcyV6T1bR=lO=cQ
z8iHvM1oOJHlGN~N3Bd%OwXg^rOiQP0Q9><#qPIxo-ZOwR^zRKCGAQI0BytU;2{o-6
zWrf;rSHf5>OJq(8g70Byz2hqmym1&ZW~_Flg-w<^+d9jBG1BSr*RU-p65(@&1!Uif
zoa-&xAUwxZqOPek7-HssU=G-*QrMRv=3~e%`o{YFI*G`;4T0?DX_PKtMUigNXG{8B
z@vBBo(~W;P-mo{a@ZLM@xsannp`3+eJalp#&=R`Fy}|w#v^!3He}HV2ZsKBddy~b*
z>M_gH`yz@r`7M0@*L6vz-Ay@iE&jvRk$ith^7zCwNdH986HZ^d%-F@r4;i76^hx&L
z(>|$XZ-~5mFUQhgSj*!8N5LeRbVnw^IP_S`RmtWBO8;p1laH#apA(&?bw)Iw%;|ZY
YDV|1u1`JL>5AIHAv`))ot08;)4LWQ`i2wiq
literal 0
HcmV?d00001
diff --git a/gamedata/shaders/r3/scope_depth_write.s b/gamedata/shaders/r3/scope_depth_write.s
new file mode 100644
index 0000000000..4f8ecc9db3
--- /dev/null
+++ b/gamedata/shaders/r3/scope_depth_write.s
@@ -0,0 +1,5 @@
+function normal(shader, t_base, t_second, t_detail)
+ shader:begin("scope_vertex","scope_depth_write")
+ : zb(false, true)
+ : scopelense(2)
+end
diff --git a/gamedata/shaders/r3/scope_vertex.vs b/gamedata/shaders/r3/scope_vertex.vs
new file mode 100644
index 0000000000..98972517b6
--- /dev/null
+++ b/gamedata/shaders/r3/scope_vertex.vs
@@ -0,0 +1,55 @@
+// PIP_COMPAT_PATCH scope_vertex 20260715
+// DO NOT EDIT, OR INCLUDE IN MODS
+
+#include "scope_common.h"
+
+// self-contained TAA jitter so the scope compiles without SSS's screenspace_mvectors.h
+// ssfx_jitter is engine-bound and zero when TAA is off, the formula matches SSS exactly
+#ifndef SSFX_MV_LOADED
+#define SSFX_MV_LOADED
+uniform float4 ssfx_jitter;
+float2 ssfx_taa_jitter(float4 hpos) { return hpos.xy + ssfx_jitter.xy * hpos.w; }
+#endif
+
+struct v_in
+{
+ float4 P : POSITION; // (float,float,float,1)
+ float4 Nh : NORMAL; // (nx,ny,nz,hemi occlusion)
+ float3 T : TANGENT; // (nx,ny,nz)
+ float3 B : BINORMAL; // (nx,ny,nz)
+ float2 tc : TEXCOORD0; // (u,v)
+};
+
+v_out main (v_in v)
+{
+ v.Nh = unpack_D3DCOLOR(v.Nh);
+
+ v_out o;
+
+ o.hpos = mul(m_WVP, v.P);
+
+
+
+ o.tc0 = v.tc.xy;
+
+ o.w_P = mul(m_W, v.P).xyz;
+ o.w_T = mul(m_W, v.T).xyz;
+ o.w_B = mul(m_W, v.B).xyz;
+ o.w_N = mul((float3x3)m_W, unpack_bx2(v.Nh));
+
+ o.v_P = float4(mul(m_WV, v.P).xyz, v.Nh.w );
+ o.v_N = mul((float3x3)m_WV, unpack_bx2(v.Nh));
+
+ o.ssp_jitter = float2(0,0);
+
+ // true pip draws every phase jittered so the main taa resolve treats the lens like any surface
+ if ((scope_phase & SCOPE_PHASE_JITTERFIX) || shader_scope_params.w < -1.5) {
+ // pixel space jitter of this draw, consumers subtract it to read unjittered screen positions
+ o.ssp_jitter = ssfx_jitter.xy * float2(0.5, -0.5) * screen_res.xy;
+
+ // Jitter the output position to ensure no ring around lens
+ o.hpos.xy = ssfx_taa_jitter(o.hpos);
+ }
+
+ return o;
+}
\ No newline at end of file
diff --git a/gamedata/shaders/r3/svp_distort_stamp.ps b/gamedata/shaders/r3/svp_distort_stamp.ps
new file mode 100644
index 0000000000000000000000000000000000000000..ce0ea7feb4aae6f1db5b321010a9fcb63e0ac08e
GIT binary patch
literal 339
zcmZXOK}*9x5QWd~uXxl;3X;TPDd=7BtauIE?6g_T?u41y77_ouA@(5l_~w1{-fJ41
zB^oJgAcUyKVn|*;kg1sX5z33#bK1vDID4586){k@XrJH{$wG{j)w$c4`>_PVN_(ED
zoBtB$dRRHvG 0.0001) ? svp_glass2.w : 1.0;
+ scope_tc = float2(s.tc0.x, 0.5 + (s.tc0.y - 0.5) * vcrop);
+ }
+}
+
+void svp_img_fix_flipped_lens(inout float2 scope_tc, Scope s)
+{
+ // auto-correct a mesh-inverted lens: some scope models author the lens V flipped so the world renders
+ // upside down. test the raw mesh UV gradient so the image and reticle always agree
+ if (shader_scope_params.w < -1.5 && svp_env.w > 0.5 && ddy(s.tc0.y) < 0.0)
+ scope_tc.y = 1.0 - scope_tc.y;
+}
+
+void svp_img_thermal_fill(inout float2 scope_tc, Scope s)
+{
+ // true PiP panel fill, tc0 spans the panel exactly, svp_glass2.w = the engine V-crop
+ // (1 = no crop when the SVP is sized to the panel aspect)
+ if (shader_scope_params.w < -1.5)
+ {
+ float hw = (svp_glass2.w > 0.0001) ? svp_glass2.w : 1.0;
+ scope_tc = float2(s.tc0.x, 0.5 + (s.tc0.y - 0.5) * hw);
+ // authored pixelation on the filled frame at the optical mag (engine curMag is
+ // ratio*optical), the fixed sensor grid is the fallback when the MCM toggle is off
+ if (SETTING(SETTINGS, ST_THERMAL_PIXELATION)) {
+ float pixelate = max(curMag() / max(svp_optics.w, 1.0), 1.0);
+ scope_tc = (floor(scope_tc * LCD_RES / pixelate) * pixelate + 0.5) / LCD_RES;
+ }
+ else if (svp_control.w > 0.5)
+ scope_tc = (floor(scope_tc * LCD_RES) + 0.5) / LCD_RES;
+ }
+}
+
+void svp_img_sensor_noise(inout float3 back, float2 scope_tc)
+{
+ // per-cell sensor noise, reseeded ~12Hz
+ if (shader_scope_params.w < -1.5 && svp_control.w > 0.5)
+ {
+ float2 cell = floor(scope_tc * LCD_RES);
+ float n = frac(sin(dot(cell, float2(12.9898, 78.233)) + floor(timers.x * 12.0) * 3.7) * 43758.5453);
+ back *= 0.92 + 0.16 * n;
+ }
+}
+
+void svp_img_thermal_veil(inout float3 back, gbuffer_data gbd)
+{
+ // front-lens veil retired, svp_optics.z is a dead lane, no-op kept for the compat patch call
+}
+
+void svp_img_lcd_mask(inout float3 back)
+{
+ // LCD subpixel mask, the stock lcd_effect look re-enabled deliberately for the panel
+ if (shader_scope_params.w < -1.5 && svp_control.w > 0.5)
+ {
+ int2 px = int2(scope.hpos.xy);
+ float3 m = float3(0.4, 0.4, 0.4);
+ int c = px.x % 3;
+ if (c == 1) m.r = 1; else if (c == 2) m.g = 1; else m.b = 1;
+ if (px.y % 3 == 0) m = float3(0.25, 0.25, 0.25);
+ back *= lerp(float3(1, 1, 1), m * 2.0, 0.6);
+ }
+}
+
+void svp_image_glass_fx(inout float3 back, float2 scope_tc, Scope s)
+{
+ // true PiP contrast-adaptive sharpen, recovers upscale/TAA softness without ringing
+ // svp_glass3.z tapers the sharpen from the disc center to the rim, .w holds an inner crisp zone
+ if (shader_scope_params.w < -1.5 && svp_glass3.x > 0.001)
+ {
+ float sharp_r = length((s.tc0.xy - 0.5) * 2.0);
+ float sharp_knee = saturate((sharp_r - svp_glass3.w) / max(1.0 - svp_glass3.w, 1e-3));
+ float sharp_taper = 1.0 - svp_glass3.z * smoothstep(0.0, 1.0, sharp_knee);
+ back += cas_delta(scope_tc, svp_glass3.x * sharp_taper) * LENS_COLOR;
+ }
+ // true PiP heat mirage, hot distant ground shimmers, engine gates strength by sun heat + magnification
+ if (shader_scope_params.w < -1.5 && svp_glass2.y > 0.001)
+ {
+ gbuffer_data mgb = gbuffer_load_data(scope_tc, scope_tc * screen_res.xy, 0);
+ float farg = smoothstep(35.0, 120.0, mgb.P.z);
+ float ground = saturate((s.tc0.y - 0.45) * 2.2);
+ float amp = svp_glass2.y * farg * ground * 0.006;
+ if (amp > 0.0001)
+ {
+ float wob = sin(s.tc0.x * 34.0 + timers.x * 7.0) * sin(s.tc0.y * 21.0 + timers.x * 5.0);
+ back = sampleScopeTex(scope_tc + float2(0.0, wob * amp)).rgb * LENS_COLOR;
+ }
+ }
+ // true PiP field curvature, the outer field softens like a real non-flat-field scope
+ // flat screens have no eyepiece field, the UV radius would draw an ellipse on the wide panel
+ if (shader_scope_params.w < -1.5 && svp_glass.y > 0.001 && RETICLE_TYPE != RT_FLAT_SCREEN)
+ {
+ float soft = smoothstep(0.65, 1.0, length((s.tc0.xy - 0.5) * 2.0)) * svp_glass.y;
+ // skip the blur sample when the field curvature has no visible contribution
+ if (soft > 0.001)
+ back = lerp(back, blur_sample(scope_tc).rgb * LENS_COLOR, saturate(soft));
+ }
+ // true PiP rain on the objective glass, animated droplets refract the world (SSS hud-raindrop path)
+ if (shader_scope_params.w < -1.5 && svp_env.y > 0.001)
+ {
+ float rain = saturate(svp_env.y * 4.0);
+ float2 luv = s.tc0.xy;
+ // low tiling = big coalesced droplets, not a fine mist; two layers merge into blobs
+ float4 l0 = s_svp_rain.Sample(smp_base, luv * 1.8 + float2(0.0, -timers.x * 0.005));
+ float4 l1 = s_svp_rain.Sample(smp_base, luv * 2.6 + 0.5);
+ float4 d = max(l0, l1);
+ // light rain lands sparse but real drops, the cutoff never fully starves below heavy
+ float mask = saturate((1.0 - frac(d.w + timers.x * 0.35)) * d.x - (1.0 - rain) * 0.85);
+ if (mask > 0.001)
+ {
+ float2 nrm = (float2(d.z, d.y) - 0.5) * 2.0;
+ float3 refr = sampleScopeTex(scope_tc + nrm * 0.07 * mask).rgb * LENS_COLOR;
+ back = lerp(back, refr, saturate(mask * 3.5)) + mask * 0.06;
+ }
+ }
+ // true PiP scope-local eye adaptation, the capture is pre-tonemap and the main pass grades
+ // it with the MAIN exposure, rescale by the scope's own measured exposure
+ if (shader_scope_params.w < -1.5 && svp_exposure.x > 0)
+ {
+ float lm = s_tonemap.Load(int3(0, 0, 0)).x;
+ float ls = s_tonemap_svp.Load(int3(0, 0, 0)).x;
+ // s_tonemap_svp is bound by our packed scope_color_write.s, guard the 0 read so a
+ // foreign loose scope_color_write.s (if one ever wins the VFS) can't floor the image
+ if (lm > 0.0001 && ls > 0.0001)
+ back *= clamp(svp_exposure.x * ls / lm, 0.25, 4.0);
+ }
+ // true PiP exit-pupil twilight dimming, engine-bound (zoom shrinks the exit pupil below the dark-adapted eye)
+ if (shader_scope_params.w < -1.5 && svp_exposure.y > 0)
+ back *= svp_exposure.y;
+ // true PiP veiling glare, off-axis sun scatters off the coatings and washes the image near the sun
+ if (shader_scope_params.w < -1.5 && svp_env.x > 0.001)
+ {
+ float g = saturate(svp_env.x);
+ // the scatter carries the live sun chroma at the calibrated energy, neutral warm when the sun is dark
+ float sun_l = dot(L_sun_color.rgb, float3(0.299, 0.587, 0.114));
+ float3 scat = sun_l > 0.001 ? L_sun_color.rgb * (0.55 / sun_l) : float3(0.6, 0.55, 0.45);
+ back = back * (1.0 - 0.5 * g) + scat * g * 0.85;
+ }
+ // true PiP lens coating, typical multi-coated glass transmission with a faint warm tint (AR reflects blue)
+ if (shader_scope_params.w < -1.5 && svp_glass2.x > 0.001)
+ {
+ float ct = saturate(svp_glass2.x);
+ back *= lerp(float3(1.0, 1.0, 1.0), float3(0.93, 0.915, 0.888), ct);
+ }
+ // lens UV debug: green top / red bottom of the SVP sample coord, a flipped gradient = flipped lens V
+ if (shader_scope_params.w < -1.5 && svp_env.z > 0.5)
+ back = float3(scope_tc.y, 1.0 - scope_tc.y, 0.2);
+}
+
+#endif
diff --git a/gamedata/shaders/r3/svp_hooks_lens.h b/gamedata/shaders/r3/svp_hooks_lens.h
new file mode 100644
index 0000000000..b1687f0dd9
--- /dev/null
+++ b/gamedata/shaders/r3/svp_hooks_lens.h
@@ -0,0 +1,15 @@
+// svp_hooks_lens 20260715 thinhook
+// relocated true-PiP flat-screen lens cull, included by scope_custom_lens.h after scope_3dss_common.h
+#ifndef SVP_HOOKS_LENS_INCLUDED
+#define SVP_HOOKS_LENS_INCLUDED
+
+#include "svp_hooks_common.h"
+
+// a flat screen is a see-through window, not coated glass, drop the whole lens layer under
+// true PiP so the world fills the panel (reflections/specular/dirt/vignette would ellipse it)
+bool svp_lens_flat_window_cull()
+{
+ return RETICLE_TYPE == RT_FLAT_SCREEN && shader_scope_params.w < -1.5;
+}
+
+#endif
diff --git a/gamedata/shaders/r3/svp_hooks_reticle.h b/gamedata/shaders/r3/svp_hooks_reticle.h
new file mode 100644
index 0000000000..f0ef43815f
--- /dev/null
+++ b/gamedata/shaders/r3/svp_hooks_reticle.h
@@ -0,0 +1,55 @@
+// svp_hooks_reticle 20260715 thinhook
+// relocated true-PiP sight-reticle terms, included by scope_custom_reticle.h after scope_3dss_common.h
+#ifndef SVP_HOOKS_RETICLE_INCLUDED
+#define SVP_HOOKS_RETICLE_INCLUDED
+
+#include "svp_hooks_common.h"
+
+bool svp_reticle_kill_chroma()
+{
+ return shader_scope_params.w < -1.5 && svp_control.y > 0.5 && IMAGE_TYPE != IT_THERMAL && IMAGE_TYPE != IT_THERMAL_COLOR;
+}
+
+// ACOG fiber brightness source, sun visibility (fiber gathers sunlight) or scene luminance
+float svp_reticle_acog_fiber(float lum)
+{
+ return (shader_scope_params.w < -1.5 && svp_glass.z > 0.5)
+ ? saturate(dot(L_hemi_color.rgb, float3(0.299, 0.587, 0.114)) * 3.0)
+ : lum;
+}
+
+// Sight reticle. true PiP (w < -1.5) swaps the eye-coupled field for a centered one of the
+// same slope plus a true-scale parallax term (svp_optics.y, ~0.15 mrad at full eye deflection)
+float2 svp_reticle_t_field(Scope S, float2 V_tangent)
+{
+ float2 t_field = V_tangent.xy * mas_scale();
+ if (shader_scope_params.w < -1.5)
+ t_field = -(S.tc0 - 0.5) * (mas_scale() * svp_optics.x) + V_tangent.xy * (mas_scale() * svp_optics.y);
+ return t_field;
+}
+
+float svp_reticle_pip_pin()
+{
+ return (shader_scope_params.w < -1.5) ? 0.0 : 1.0;
+}
+
+// mirror the sample V around the reticle center under true PiP so it matches the auto-flipped world
+// same raw mesh UV test as the image chunk so the two can never disagree
+void svp_reticle_flip(inout float2 reticle_tc, inout float2 reticle_lens_tc, Scope S)
+{
+ if (RETICLE_TYPE != RT_SCREEN && RETICLE_TYPE != RT_FLAT_SCREEN
+ && shader_scope_params.w < -1.5 && svp_glass.w > 0.5 && ddy(S.tc0.y) < 0.0)
+ {
+ reticle_tc.y = 1.0 - reticle_tc.y;
+ reticle_lens_tc.y = 1.0 - reticle_lens_tc.y;
+ }
+}
+
+// true PiP illuminated reticle wash-out, the glow loses contrast against a bright background (black lines are rgb 0, untouched)
+void svp_reticle_washout(inout float4 result, float lum)
+{
+ if (shader_scope_params.w < -1.5 && svp_glass.x > 0.001)
+ result.rgb *= max(0.65, 1.0 - svp_glass.x * (1.0 - lum) * 0.3);
+}
+
+#endif
diff --git a/gamedata/shaders/r3/svp_hooks_shadow.h b/gamedata/shaders/r3/svp_hooks_shadow.h
new file mode 100644
index 0000000000..9d3c474a2d
--- /dev/null
+++ b/gamedata/shaders/r3/svp_hooks_shadow.h
@@ -0,0 +1,30 @@
+// PIP_HOOK svp_hooks_shadow 20260715
+// true pip logic for the 3DSS shadow chunk, rides mod.db0, the compat patch includes it
+
+#ifndef SVP_HOOKS_SHADOW_H
+#define SVP_HOOKS_SHADOW_H
+
+#include "svp_hooks_common.h"
+
+// the swing side slides the pupil center so the shadow enters from the side of the motion
+// and a symmetric ring cannot form at center
+float2 svp_shadow_swing(float2 off)
+{
+ if (shader_scope_params.w < -1.5)
+ off += svp_exposure.zw;
+ return off;
+}
+
+// the parallax crescent rides the swing envelope, calm aim shows none and a hard weapon
+// swing sweeps in a dark crescent that fades back out quickly
+float4 svp_shadow_soften(float4 shadow)
+{
+ if (shader_scope_params.w < -1.5)
+ {
+ shadow.rgb = 0;
+ shadow.a *= saturate((svp_glass4.w - 0.3) / 0.35);
+ }
+ return shadow;
+}
+
+#endif
diff --git a/gamedata/shaders/r3/svp_nearblur.ps b/gamedata/shaders/r3/svp_nearblur.ps
new file mode 100644
index 0000000000000000000000000000000000000000..5dd3d999d9929ff5270d92cd751166eb1ab6798f
GIT binary patch
literal 2649
zcmai0e@`Pf5dAy*DW-Q)yMZJmy9Wp5Xhi{CktiS%om5qAc1bPQFL%X
zIWJT;Go&QhM^3{}_hd3va}pJs@(xW^nRD4?a$0gSwa~1jiF)FXG-WuTi4_)qKhBJ0
zg)vIHr17Lw)81Sy*;L7%;q4C1q$rfG$grwLCge22t~$6>NGh?SOy~?>R6&`N#mopm
zViPs99$3wAbIF$E;YM$T%*t8L=}o4p3YR>-iJ}o-+L`9bC7H3DjC}>CFL!HYPNQf8
zrzjP(@4n(^?5?WJDWQ<&I;uLf@=&`T>J!z=J?yQi0wT?F0S4=3hZa=L)@xUv03R~r
zRDE+*nwxONIQ4g=m%|~|Tz3QSIkU{&c190Zqw9;CtMTaf`>PHeW2Y#YJ(vgy#Y(2B
zW|Egt^cETl$?5i&@yENH%d6Y*`R&J>D@uCl$;ru4nxsV(`9cY-}!L;ZGdeHyhi2j7z7HGQCdub&ic`k4lO
z{Pg+-{YYzH;7l&Gu$D`jPG&^`x5VaY)rQP7X$>($1=G=&9gIe-d4Ndkw$s6Rw_6Ba
z=5(PhHjKkLH5AgS@)l8(zjR?o#|Ds$!T^hKk4Z$*1f
z)_CA*IPm{mqvBrw5ao5L*?#RL4B!fHgBf6U?c{J%z|CcjT7#JvoYhMH0ALH*IL1R2
za!P6rf)wObnPsS@X2z1RQOhCT1LR66`)Dk${L
zikhtQ;X^Mb5x`pG-69kn(h1|i~5hjS;&-;fC(
zKe+N=vAOGC)cM3YP{OLsY-gd2!C{{)s|_t4nZ*#$J|;}M4(!M^vl!5Z{&fxV@K=IP
ziIY79z#^>Vz!&7)?f{1CDpyR$p9yXDfP<-@7alecxlve)B`*mCab0qBLEfFZyJ3y4
zR05J}Wjz2qB$?8hV=#j9ojOb)K0H;LGuM2E!r{^RIU%XW17|ik#Z&@ep;;CmCcTs6
zmQj5Aqg1N?6Ez@3GcfT|BgoC6)6Jax5cr0#
z+{X{}&hvfhSNmr#-wjCj8Se7(C1l;0gbnvY!zY**CU$A|od$T=4d%l0+WGXWzQ#Hr
z2inFG;lBXibhynNYhT5@Xz`tcKGb#5&K_E0cL*R?D|~IzWF_9T=78G@)4J0f@#fQ{m#T~LsXTNG6IH`670t|V=CFB+NucM@5QV?3{Z616b&ojV{>$#o6x
z-3m{OvYXp{UNn@y(b)#Ejc-~FTI_5+gTq@nzPLBVCp>c*QFQYxG!w;t?1XXSo`U}X
DOGR1T
literal 0
HcmV?d00001
diff --git a/gamedata/shaders/r3/svp_nearblur.s b/gamedata/shaders/r3/svp_nearblur.s
new file mode 100644
index 0000000000..b4ff65ebcb
--- /dev/null
+++ b/gamedata/shaders/r3/svp_nearblur.s
@@ -0,0 +1,8 @@
+function normal(shader, t_base, t_second, t_detail)
+ shader:begin("stub_notransform_t", "svp_nearblur")
+ : zb(false, false)
+ shader:dx10texture("s_nb_image", "$user$svp_nearblur_src")
+ shader:dx10texture("s_nb_pos", "$user$svp_nearblur_pos")
+ shader:dx10sampler("smp_base")
+ shader:dx10sampler("smp_nofilter")
+end
diff --git a/gamedata/shaders/r3/svp_taa_stamp.ps b/gamedata/shaders/r3/svp_taa_stamp.ps
new file mode 100644
index 0000000000000000000000000000000000000000..0705602162f4be7816e8c0b4bbc97d04e5e5fbcc
GIT binary patch
literal 307
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