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 only (the km-distant mist sheets; everything else we render is
|
||||
// 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;
|
||||
};
|
||||
// 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
|
||||
// their own branch (only the .alphatest variant tests, at ALPHAREF 30).
|
||||
// Fam 13 (reflective_trans glass, cam_pos.w = -13) never alpha-tests;
|
||||
// it alpha-BLENDS in the sorted sub-pass.
|
||||
if (cam_pos.w > -12.5 || cam_pos.w < -13.5) {
|
||||
// it alpha-BLENDS in the sorted sub-pass. Fam 14 (scrollincandescent)
|
||||
// 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;
|
||||
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 {
|
||||
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 {
|
||||
// Environment families: macro overlay (0.5-neutral, fades under decal
|
||||
// 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_scale = 1.0f;
|
||||
item.macro_opacity = 1.0f;
|
||||
item.scroll_u = 0.0f;
|
||||
item.scroll_v = 0.0f;
|
||||
item.decal_art = 0;
|
||||
item.hair = false;
|
||||
item.char_family = 0;
|
||||
|
|
@ -1997,6 +1999,16 @@ bool BuildItemFromMesh(uint8_t* base, uint32_t mesh, DrawItem& item) {
|
|||
item.detail_scale = f;
|
||||
}
|
||||
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 ||
|
||||
std::memcmp(text, "macroOverlayOpacity", 20) == 0) {
|
||||
// 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;
|
||||
} else if (is("incandescent.default", 21)) {
|
||||
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):
|
||||
// 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 ||
|
||||
!g_tree_frame_done || !g_proxy_frame_done || !g_dynobj_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() &&
|
||||
(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];
|
||||
|
|
@ -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,
|
||||
// m_params[0..2] at c12..c14) and the oceanreflection sheet row (c3).
|
||||
// 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));
|
||||
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) {
|
||||
std::memcpy(scene.shadow_rows, g_shadow_rows, sizeof(g_shadow_rows));
|
||||
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_ocean_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
|
||||
|
|
|
|||
|
|
@ -64,6 +64,11 @@ struct DrawItem {
|
|||
uint32_t macro_tex; // guest renderengine::Texture address (0 = none)
|
||||
float macro_scale; // macroOverlayUVScale 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 uv_offset;
|
||||
uint16_t uv2_offset;
|
||||
|
|
@ -210,7 +215,9 @@ struct DrawItem {
|
|||
// 7 = environmentsimple.alphatest, 8 = environmentsimple.diffuse,
|
||||
// 9 = tree.default, 10 = animated.tree, 11 = proxyworld.default,
|
||||
// 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;
|
||||
// dynamicobject.fx family (movable props: dispensers, dumpsters, benches,
|
||||
// cans, ...). 0 = not a dynamic object, 1 = dynamicobject.default,
|
||||
|
|
@ -340,6 +347,12 @@ struct FrameScene {
|
|||
// height-ramp alpha fade bounds.
|
||||
bool oceanrefl_valid = false;
|
||||
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/alphatestdynamicobject PS bank (debug-path classified):
|
||||
// [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
|
||||
// would multiply by zero and render black.
|
||||
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);
|
||||
}
|
||||
// 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[51] = scene.fog_ramp[2];
|
||||
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) {
|
||||
// misc.z = the v2 material bind flags, misc.w = detailNormalUVScale
|
||||
// (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 bool g_oceanrefl_have = 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),
|
||||
// captured from a dynamicobject/alphatestdynamicobject PS bank (debug-path
|
||||
// classified). Guest render thread only.
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue