render: exact scrollincandescent port - the LED chyron scrolls and glows
This commit is contained in:
parent
146a158f90
commit
cbc492a407
6 changed files with 12091 additions and 11941 deletions
|
|
@ -28,6 +28,8 @@ cbuffer C : register(b0) {
|
||||||
// items never use otherwise. Fog is currently applied to transparent
|
// items never use otherwise. Fog is currently applied to transparent
|
||||||
// items only (the km-distant mist sheets; everything else we render is
|
// items only (the km-distant mist sheets; everything else we render is
|
||||||
// near enough for fog to be negligible).
|
// near enough for fog to be negligible).
|
||||||
|
// Fam 14 (scrollincandescent, opaque, so every fog slot is free):
|
||||||
|
// xy = the period-wrapped UV scroll offset, z = the material multiplier.
|
||||||
float4 misc;
|
float4 misc;
|
||||||
};
|
};
|
||||||
// Per-frame dynamic-shadow (CSM) receiver constants, captured from the
|
// Per-frame dynamic-shadow (CSM) receiver constants, captured from the
|
||||||
|
|
@ -648,8 +650,10 @@ float4 ps_main(VSOut i) : SV_Target {
|
||||||
// dynamicobject items (cam_pos.w <= -21) are excluded here and clip in
|
// dynamicobject items (cam_pos.w <= -21) are excluded here and clip in
|
||||||
// their own branch (only the .alphatest variant tests, at ALPHAREF 30).
|
// their own branch (only the .alphatest variant tests, at ALPHAREF 30).
|
||||||
// Fam 13 (reflective_trans glass, cam_pos.w = -13) never alpha-tests;
|
// Fam 13 (reflective_trans glass, cam_pos.w = -13) never alpha-tests;
|
||||||
// it alpha-BLENDS in the sorted sub-pass.
|
// it alpha-BLENDS in the sorted sub-pass. Fam 14 (scrollincandescent)
|
||||||
if (cam_pos.w > -12.5 || cam_pos.w < -13.5) {
|
// never alpha-tests either (its technique sets no test state, and its
|
||||||
|
// PS ignores the diffuse alpha entirely).
|
||||||
|
if (cam_pos.w > -12.5 || cam_pos.w < -14.5) {
|
||||||
float aref = cam_pos.w < -0.5 ? 0.1176 : 0.35;
|
float aref = cam_pos.w < -0.5 ? 0.1176 : 0.35;
|
||||||
clip(misc.x > 0.0 ? albedo.a * albedo.a - 0.0627 : albedo.a - aref);
|
clip(misc.x > 0.0 ? albedo.a * albedo.a - 0.0627 : albedo.a - aref);
|
||||||
}
|
}
|
||||||
|
|
@ -1128,6 +1132,16 @@ float4 ps_main(VSOut i) : SV_Target {
|
||||||
} else {
|
} else {
|
||||||
lin = dlin * (fam < 11.5 ? sh_fogp.w : 1.0);
|
lin = dlin * (fam < 11.5 ? sh_fogp.w : 1.0);
|
||||||
}
|
}
|
||||||
|
} else if (fam > 13.5 && fam < 14.5) {
|
||||||
|
// scrollincandescent (the stadium LED chyron band): emissive scrolled
|
||||||
|
// diffuse. The game's VS adds g_fAnimationTime * (uSpeed, vSpeed) to
|
||||||
|
// the texcoord (misc.xy carries that offset, period-wrapped CPU-side);
|
||||||
|
// its PS is one diffuse fetch -> D^2 * m_params[0].y (misc.z) ->
|
||||||
|
// the shared fog/exposure/tone tail. No lightmap term, no kd, no
|
||||||
|
// shadow receive, no material multiplier on the fog term
|
||||||
|
// (ucode-exact: scrollincandescent_defaultPS).
|
||||||
|
float3 sd = diffuse.Sample(smp, i.uv + misc.xy).rgb;
|
||||||
|
lin = sd * sd * misc.z;
|
||||||
} else {
|
} else {
|
||||||
// Environment families: macro overlay (0.5-neutral, fades under decal
|
// Environment families: macro overlay (0.5-neutral, fades under decal
|
||||||
// art), linear decal composite, lightmap squared and min-clamped
|
// art), linear decal composite, lightmap squared and min-clamped
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -1910,6 +1910,8 @@ bool BuildItemFromMesh(uint8_t* base, uint32_t mesh, DrawItem& item) {
|
||||||
item.macro_tex = 0;
|
item.macro_tex = 0;
|
||||||
item.macro_scale = 1.0f;
|
item.macro_scale = 1.0f;
|
||||||
item.macro_opacity = 1.0f;
|
item.macro_opacity = 1.0f;
|
||||||
|
item.scroll_u = 0.0f;
|
||||||
|
item.scroll_v = 0.0f;
|
||||||
item.decal_art = 0;
|
item.decal_art = 0;
|
||||||
item.hair = false;
|
item.hair = false;
|
||||||
item.char_family = 0;
|
item.char_family = 0;
|
||||||
|
|
@ -1997,6 +1999,16 @@ bool BuildItemFromMesh(uint8_t* base, uint32_t mesh, DrawItem& item) {
|
||||||
item.detail_scale = f;
|
item.detail_scale = f;
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
|
} else if (std::memcmp(text, "uAnimationSpeed", 16) == 0 ||
|
||||||
|
std::memcmp(text, "vAnimationSpeed", 16) == 0) {
|
||||||
|
// Shader-constant channels (the game's VS c7.x / c8.x): UV scroll
|
||||||
|
// speed in texcoords per second, consumed by the fam-14
|
||||||
|
// scrollincandescent branch. Signed; the direction is authored.
|
||||||
|
const float f = std::bit_cast<float>(chan_u32(i, 0x10));
|
||||||
|
if (std::fabs(f) < 1e6f) { // false for NaN too
|
||||||
|
(text[0] == 'u' ? item.scroll_u : item.scroll_v) = f;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
} else if (std::memcmp(text, "macroOverlayUVScale", 20) == 0 ||
|
} else if (std::memcmp(text, "macroOverlayUVScale", 20) == 0 ||
|
||||||
std::memcmp(text, "macroOverlayOpacity", 20) == 0) {
|
std::memcmp(text, "macroOverlayOpacity", 20) == 0) {
|
||||||
// Shader-constant channel: the float lives in the first guid word.
|
// Shader-constant channel: the float lives in the first guid word.
|
||||||
|
|
@ -2142,6 +2154,11 @@ bool BuildItemFromMesh(uint8_t* base, uint32_t mesh, DrawItem& item) {
|
||||||
item.env_family = 11;
|
item.env_family = 11;
|
||||||
} else if (is("incandescent.default", 21)) {
|
} else if (is("incandescent.default", 21)) {
|
||||||
item.env_family = 12;
|
item.env_family = 12;
|
||||||
|
} else if (is("incandescent.backlituvscroll", 29)) {
|
||||||
|
// scrollincandescent.fx: emissive diffuse scrolled by
|
||||||
|
// g_fAnimationTime x the u/vAnimationSpeed channels (the
|
||||||
|
// stadium LED chyron band).
|
||||||
|
item.env_family = 14;
|
||||||
}
|
}
|
||||||
// dynamicobject.fx props (dispensers, dumpsters, benches, cans):
|
// dynamicobject.fx props (dispensers, dumpsters, benches, cans):
|
||||||
// rigid movable objects with their own dual-shadow lit PS
|
// rigid movable objects with their own dual-shadow lit PS
|
||||||
|
|
@ -4161,7 +4178,7 @@ void OnDrawDone(uint8_t* base, uint32_t func, uint32_t r4, uint32_t r5, uint32_t
|
||||||
if ((!g_fog_frame_done || !g_shadow_frame_done || !g_sky_frame_done ||
|
if ((!g_fog_frame_done || !g_shadow_frame_done || !g_sky_frame_done ||
|
||||||
!g_tree_frame_done || !g_proxy_frame_done || !g_dynobj_frame_done ||
|
!g_tree_frame_done || !g_proxy_frame_done || !g_dynobj_frame_done ||
|
||||||
!g_water_frame_done || !g_ocean_frame_done ||
|
!g_water_frame_done || !g_ocean_frame_done ||
|
||||||
!g_oceanrefl_frame_done) &&
|
!g_oceanrefl_frame_done || !g_scroll_frame_done) &&
|
||||||
func == 0 && flags2d == 0 && SceneEnabled() &&
|
func == 0 && flags2d == 0 && SceneEnabled() &&
|
||||||
(g_fog_cam[0] != 0.0f || g_fog_cam[1] != 0.0f || g_fog_cam[2] != 0.0f)) {
|
(g_fog_cam[0] != 0.0f || g_fog_cam[1] != 0.0f || g_fog_cam[2] != 0.0f)) {
|
||||||
const float dx = LoadGuestF32(base, bank + 16 * 4) - g_fog_cam[0];
|
const float dx = LoadGuestF32(base, bank + 16 * 4) - g_fog_cam[0];
|
||||||
|
|
@ -4382,6 +4399,46 @@ void OnDrawDone(uint8_t* base, uint32_t func, uint32_t r4, uint32_t r5, uint32_t
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// scrollincandescent.fx (the emissive time-scrolled LED chyron):
|
||||||
|
// g_fAnimationTime from the VERTEX bank (c9.x) and the material
|
||||||
|
// multiplier m_params[0].y from the PIXEL bank (c3.y). Same POSITIVE
|
||||||
|
// debug-path gate as the water rows; the bank values alone cannot
|
||||||
|
// discriminate this family.
|
||||||
|
if (!g_scroll_frame_done && ps_bank != 0) {
|
||||||
|
const auto scroll_ps = [&]() -> bool {
|
||||||
|
const auto check = [&](uint32_t obj) -> bool {
|
||||||
|
if (obj < 0x10000 || !GuestReadableApprox(base, obj)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
char text[120] = {};
|
||||||
|
for (int k = 0; k < 119; ++k) {
|
||||||
|
text[k] = char(REX_LOAD_U8(obj + 0x54 + k));
|
||||||
|
if (text[k] == '\0') break;
|
||||||
|
}
|
||||||
|
return std::strstr(text, "\\scrollincandescent") != nullptr;
|
||||||
|
};
|
||||||
|
return check(g_cur_ps_obj.load(std::memory_order_relaxed)) ||
|
||||||
|
check(g_cur_vs_obj.load(std::memory_order_relaxed));
|
||||||
|
};
|
||||||
|
if (scroll_ps()) {
|
||||||
|
const float t = LoadGuestF32(base, bank + 36 * 4); // VS c9.x
|
||||||
|
const float mult = LoadGuestF32(base, ps_bank + 13 * 4); // PS c3.y
|
||||||
|
// Range gate: the animation clock is a non-negative seconds
|
||||||
|
// counter, the multiplier a small positive scale.
|
||||||
|
if (t >= 0.0f && t < 1e7f && mult > 0.0f && mult <= 8.0f) {
|
||||||
|
const bool first = !g_scroll_have;
|
||||||
|
g_scroll_rows[0] = t;
|
||||||
|
g_scroll_rows[1] = mult;
|
||||||
|
g_scroll_have = true;
|
||||||
|
g_scroll_frame_done = true;
|
||||||
|
if (first) {
|
||||||
|
REXLOG_INFO(
|
||||||
|
"native-scene: scroll rows captured: t={:.2f} mult={:.3g}",
|
||||||
|
t, mult);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
// ocean_defaultPS material rows (PCA mean/weights at c2..c8,
|
// ocean_defaultPS material rows (PCA mean/weights at c2..c8,
|
||||||
// m_params[0..2] at c12..c14) and the oceanreflection sheet row (c3).
|
// m_params[0..2] at c12..c14) and the oceanreflection sheet row (c3).
|
||||||
// Same POSITIVE debug-path gating as the water rows.
|
// Same POSITIVE debug-path gating as the water rows.
|
||||||
|
|
@ -9080,6 +9137,10 @@ void BuildFrameScene(uint8_t* base, const SubmitRecord* records, size_t count) {
|
||||||
sizeof(scene.oceanrefl_rows));
|
sizeof(scene.oceanrefl_rows));
|
||||||
scene.oceanrefl_valid = true;
|
scene.oceanrefl_valid = true;
|
||||||
}
|
}
|
||||||
|
if (g_scroll_have) {
|
||||||
|
std::memcpy(scene.scroll_rows, g_scroll_rows, sizeof(scene.scroll_rows));
|
||||||
|
scene.scroll_valid = true;
|
||||||
|
}
|
||||||
if (g_shadow_have) {
|
if (g_shadow_have) {
|
||||||
std::memcpy(scene.shadow_rows, g_shadow_rows, sizeof(g_shadow_rows));
|
std::memcpy(scene.shadow_rows, g_shadow_rows, sizeof(g_shadow_rows));
|
||||||
scene.shadow_valid = true;
|
scene.shadow_valid = true;
|
||||||
|
|
@ -9226,6 +9287,7 @@ void BuildFrameScene(uint8_t* base, const SubmitRecord* records, size_t count) {
|
||||||
g_water_frame_done = false;
|
g_water_frame_done = false;
|
||||||
g_ocean_frame_done = false;
|
g_ocean_frame_done = false;
|
||||||
g_oceanrefl_frame_done = false;
|
g_oceanrefl_frame_done = false;
|
||||||
|
g_scroll_frame_done = false;
|
||||||
|
|
||||||
|
|
||||||
// Draw-time STRETCH VETO: the last line of defense, judging what the GPU
|
// Draw-time STRETCH VETO: the last line of defense, judging what the GPU
|
||||||
|
|
|
||||||
|
|
@ -64,6 +64,11 @@ struct DrawItem {
|
||||||
uint32_t macro_tex; // guest renderengine::Texture address (0 = none)
|
uint32_t macro_tex; // guest renderengine::Texture address (0 = none)
|
||||||
float macro_scale; // macroOverlayUVScale channel constant
|
float macro_scale; // macroOverlayUVScale channel constant
|
||||||
float macro_opacity; // macroOverlayOpacity channel constant
|
float macro_opacity; // macroOverlayOpacity channel constant
|
||||||
|
// uAnimationSpeed / vAnimationSpeed channel constants (texcoords per
|
||||||
|
// second of the frame's animation time): the scrollincandescent UV
|
||||||
|
// scroll (fam 14: the stadium LED chyron band). 0 = no scroll.
|
||||||
|
float scroll_u;
|
||||||
|
float scroll_v;
|
||||||
uint16_t pos_offset;
|
uint16_t pos_offset;
|
||||||
uint16_t uv_offset;
|
uint16_t uv_offset;
|
||||||
uint16_t uv2_offset;
|
uint16_t uv2_offset;
|
||||||
|
|
@ -210,7 +215,9 @@ struct DrawItem {
|
||||||
// 7 = environmentsimple.alphatest, 8 = environmentsimple.diffuse,
|
// 7 = environmentsimple.alphatest, 8 = environmentsimple.diffuse,
|
||||||
// 9 = tree.default, 10 = animated.tree, 11 = proxyworld.default,
|
// 9 = tree.default, 10 = animated.tree, 11 = proxyworld.default,
|
||||||
// 12 = incandescent.default, 13 = environment.reflective_trans
|
// 12 = incandescent.default, 13 = environment.reflective_trans
|
||||||
// (transparentenvironmentreflective: blended glass, alpha sub-pass).
|
// (transparentenvironmentreflective: blended glass, alpha sub-pass),
|
||||||
|
// 14 = incandescent.backlituvscroll (scrollincandescent: the emissive
|
||||||
|
// time-scrolled LED chyron band).
|
||||||
uint8_t env_family = 0;
|
uint8_t env_family = 0;
|
||||||
// dynamicobject.fx family (movable props: dispensers, dumpsters, benches,
|
// dynamicobject.fx family (movable props: dispensers, dumpsters, benches,
|
||||||
// cans, ...). 0 = not a dynamic object, 1 = dynamicobject.default,
|
// cans, ...). 0 = not a dynamic object, 1 = dynamicobject.default,
|
||||||
|
|
@ -340,6 +347,12 @@ struct FrameScene {
|
||||||
// height-ramp alpha fade bounds.
|
// height-ramp alpha fade bounds.
|
||||||
bool oceanrefl_valid = false;
|
bool oceanrefl_valid = false;
|
||||||
float oceanrefl_rows[4] = {};
|
float oceanrefl_rows[4] = {};
|
||||||
|
// scrollincandescent.fx rows, captured once per frame from a
|
||||||
|
// scrollincandescent draw (debug-path classified): [0] = g_fAnimationTime
|
||||||
|
// (VS c9.x), [1] = the material multiplier m_params[0].y (PS c3.y).
|
||||||
|
// Consumed by the fam-14 branch (the scrolling LED chyron).
|
||||||
|
bool scroll_valid = false;
|
||||||
|
float scroll_rows[2] = {};
|
||||||
// dynamicobject.fx frame-global lighting rows, captured from a
|
// dynamicobject.fx frame-global lighting rows, captured from a
|
||||||
// dynamicobject/alphatestdynamicobject PS bank (debug-path classified):
|
// dynamicobject/alphatestdynamicobject PS bank (debug-path classified):
|
||||||
// [0..2] sun direction (PS c9), [3] scene exposure (c13.x),
|
// [0..2] sun direction (PS c9), [3] scene exposure (c13.x),
|
||||||
|
|
|
||||||
|
|
@ -7801,7 +7801,11 @@ bool RenderScene(const NativeGuestOutputRenderContext& context, void* /*user_dat
|
||||||
// exposure / material multiplier at b1); without them the tone chain
|
// exposure / material multiplier at b1); without them the tone chain
|
||||||
// would multiply by zero and render black.
|
// would multiply by zero and render black.
|
||||||
if (debug_mode == 0 && item.env_family != 0 && scene.shadow_valid &&
|
if (debug_mode == 0 && item.env_family != 0 && scene.shadow_valid &&
|
||||||
!item.water && !item.transparent) {
|
!item.water && !item.transparent &&
|
||||||
|
// Fam 14 needs the frame's scroll rows (time + multiplier); the
|
||||||
|
// tone chain would multiply by zero without them; the legacy
|
||||||
|
// shading covers the capture-less frames.
|
||||||
|
(item.env_family != 14 || scene.scroll_valid)) {
|
||||||
constants[39] = -float(item.env_family);
|
constants[39] = -float(item.env_family);
|
||||||
}
|
}
|
||||||
// cam_pos.w = -(20 + variant) selects the exact dynamicobject branch
|
// cam_pos.w = -(20 + variant) selects the exact dynamicobject branch
|
||||||
|
|
@ -8190,6 +8194,18 @@ bool RenderScene(const NativeGuestOutputRenderContext& context, void* /*user_dat
|
||||||
constants[50] = scene.fog_ramp[1];
|
constants[50] = scene.fog_ramp[1];
|
||||||
constants[51] = scene.fog_ramp[2];
|
constants[51] = scene.fog_ramp[2];
|
||||||
std::memcpy(constants + 40, scene.fog_color, 4 * sizeof(float));
|
std::memcpy(constants + 40, scene.fog_color, 4 * sizeof(float));
|
||||||
|
} else if (item.env_family == 14 && constants[39] < -13.5f) {
|
||||||
|
// Fam 14 (scrollincandescent, the LED chyron): misc.xy = the frame's
|
||||||
|
// UV scroll offset (g_fAnimationTime x the material's channel speeds,
|
||||||
|
// wrapped to one texture period here; the shader adds a small
|
||||||
|
// bounded offset instead of losing uv precision to a large raw
|
||||||
|
// time x speed product), misc.z = the material multiplier
|
||||||
|
// m_params[0].y.
|
||||||
|
const float ou = scene.scroll_rows[0] * item.scroll_u;
|
||||||
|
const float ov = scene.scroll_rows[0] * item.scroll_v;
|
||||||
|
constants[48] = ou - std::floor(ou);
|
||||||
|
constants[49] = ov - std::floor(ov);
|
||||||
|
constants[50] = scene.scroll_rows[1];
|
||||||
} else if (v2_flags != 0) {
|
} else if (v2_flags != 0) {
|
||||||
// misc.z = the v2 material bind flags, misc.w = detailNormalUVScale
|
// misc.z = the v2 material bind flags, misc.w = detailNormalUVScale
|
||||||
// (opaque fams 1-4 and the dynobj families; see the v2 blocks above).
|
// (opaque fams 1-4 and the dynobj families; see the v2 blocks above).
|
||||||
|
|
|
||||||
|
|
@ -310,6 +310,13 @@ inline bool g_ocean_frame_done = false;
|
||||||
inline float g_oceanrefl_rows[4] = {};
|
inline float g_oceanrefl_rows[4] = {};
|
||||||
inline bool g_oceanrefl_have = false;
|
inline bool g_oceanrefl_have = false;
|
||||||
inline bool g_oceanrefl_frame_done = false;
|
inline bool g_oceanrefl_frame_done = false;
|
||||||
|
// scrollincandescent.fx rows (see FrameScene::scroll_rows): [0] =
|
||||||
|
// g_fAnimationTime (VS c9.x), [1] = the material multiplier m_params[0].y
|
||||||
|
// (PS c3.y). Captured from a scrollincandescent draw (debug-path
|
||||||
|
// classified). Guest render thread only.
|
||||||
|
inline float g_scroll_rows[2] = {};
|
||||||
|
inline bool g_scroll_have = false;
|
||||||
|
inline bool g_scroll_frame_done = false;
|
||||||
// dynamicobject.fx frame-global lighting rows (see FrameScene::dynobj_rows),
|
// dynamicobject.fx frame-global lighting rows (see FrameScene::dynobj_rows),
|
||||||
// captured from a dynamicobject/alphatestdynamicobject PS bank (debug-path
|
// captured from a dynamicobject/alphatestdynamicobject PS bank (debug-path
|
||||||
// classified). Guest render thread only.
|
// classified). Guest render thread only.
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue