Warp polish: animate scroll, recenter, no depth-clip, expand-only respawn
Four fixes for the user's "static / above the viewport / cavern clips it / doesn't
shoot off" report, each root-caused:
1. STATIC swirl -> now animates. The material's SCROLL (0.0 0.0 0.1 0.5) only
reaches the draw via a per-texture .met file (L4D3D.cpp:640) that tsphere lacks,
and the ramp-bake path leaves doScroll=false -- so the swirl was frozen. Set the
authored scroll rates (u -0.1/s, v +0.5/s) + REPEAT wrap on the sphere's draw-ops
at load, so SetTextureScrolling churns the swirl (the "spins around").
2. "Above the viewport" -> recenter. tsphere.bgf is modelled with its centre at
y=+8.25 (logged: center=(0,8.25,0.47) r=21.5); scaled x100-150 that threw the
sphere ~1000 units overhead. Translate the mesh's mCullCenter to the origin
before scaling so it sits ON the eye/anchor. (Added public-ish mCullCenter use;
l4d3d.h already exposes it.)
3. Cavern clip -> Z-test off. The arena is an enclosed cavern; a depth-tested 150x
sphere gets occluded by the ceiling/walls. Draw the POV overlay with ZENABLE off
(ZWRITE already off, so no depth pollution). BT_WARP_ZTEST=1 restores it.
4. "Fades in then disappears" -> expand-only. A respawn now starts in the EXPAND
phase (1->150 over 1s) so the swirl immediately bursts and blasts outward ("shoots
off into the distance"). The collapse (100->1) is the DEATH warp, not the
respawn. BT_WARP_COLLAPSE=1 plays the full collapse->expand.
Build + render + no-crash verified 2-node; expand warp fires POV/world. Still
deferred: the SetIsDead world-mask (black-out that makes the expand "reveal" the
world) -- carries the camera-coupling risk, add scoped if wanted.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
a9d1534292
commit
062c66999e
@@ -2263,7 +2263,17 @@ static void
|
||||
float v = (float)atof(s);
|
||||
if (v > 0.0f) { gWarpCollapseScale = v; gWarpExpandScale = v * 1.5f; }
|
||||
}
|
||||
gWarpPhase = 1; // collapse
|
||||
// RESPAWN = birth = the EXPAND phase (scale 1 -> 150 over 1s): the swirl bursts
|
||||
// from the reinsertion point and blasts outward past you ("shoots off into the
|
||||
// distance"). The COLLAPSE (100 -> 1) is the DEATH warp -- a separate trigger in
|
||||
// the authentic engine (POVTranslocateRenderable), not part of the respawn -- so
|
||||
// starting a respawn with a collapse read as "fades in then disappears".
|
||||
// BT_WARP_COLLAPSE=1 plays the full collapse->expand cycle.
|
||||
{
|
||||
static int s_collapse = -1;
|
||||
if (s_collapse < 0) { const char *cv = getenv("BT_WARP_COLLAPSE"); s_collapse = (cv && cv[0] == '1') ? 1 : 0; }
|
||||
gWarpPhase = s_collapse ? 1 : 2; // 1 collapse->expand, 2 expand only
|
||||
}
|
||||
gWarpT = 0.0f;
|
||||
gWarpPOV = pov;
|
||||
gWarpX = x; gWarpY = y; gWarpZ = z;
|
||||
@@ -2297,17 +2307,35 @@ void
|
||||
gTLocSphere = d3d_OBJECT::LoadObject(device, "tsphere.bgf");
|
||||
if (gTLocSphere != 0)
|
||||
{
|
||||
// Route the sphere's (opaque) draw-ops INTO the alpha-blend pass.
|
||||
// DrawMesh skips any op whose alphaTest != (pass==PASS_ALPHABLEND)
|
||||
// (L4D3D.cpp:1045), so an un-flagged tsphere renders NOTHING in the
|
||||
// alpha pass -- the warp was invisible for exactly this reason.
|
||||
for (int op = 0; op < gTLocSphere->GetDrawOpCount(); ++op)
|
||||
gTLocSphere->GetDrawOp(op)->alphaTest = true;
|
||||
{
|
||||
L4DRAWOP *dop = gTLocSphere->GetDrawOp(op);
|
||||
// Route the sphere's ops INTO the alpha-blend pass: DrawMesh skips any
|
||||
// op whose alphaTest != (pass==PASS_ALPHABLEND) (L4D3D.cpp:1045).
|
||||
dop->alphaTest = true;
|
||||
// THE SWIRL MOTION. The authentic material scrolls its texture
|
||||
// (tsphere_scr_tex SPECIAL "SCROLL 0.0 0.0 0.1 0.5"), but the port only
|
||||
// picks scroll up from a per-texture .met file (L4D3D.cpp:640), which
|
||||
// tsphere has none of, and the ramp-bake path leaves doScroll=false --
|
||||
// so our swirl was FROZEN. Set the authored scroll rates here so
|
||||
// SetTextureScrolling animates it (u -0.1/s, v +0.5/s -> the churning
|
||||
// swirl that "spins around"), REPEAT wrap so the scroll tiles.
|
||||
dop->texture.doScroll = true;
|
||||
dop->texture.scrollUDelta = 0.1f;
|
||||
dop->texture.scrollVDelta = 0.5f;
|
||||
dop->texture.wrap_u = L4TEXOP::REPEAT;
|
||||
dop->texture.wrap_v = L4TEXOP::REPEAT;
|
||||
}
|
||||
}
|
||||
if (getenv("BT_TLOC_LOG"))
|
||||
DEBUG_STREAM << "[tloc] tsphere.bgf load "
|
||||
<< (gTLocSphere ? "OK" : "FAILED")
|
||||
<< " ops=" << (gTLocSphere ? gTLocSphere->GetDrawOpCount() : 0)
|
||||
<< (gTLocSphere ? " center=(" : "")
|
||||
<< (gTLocSphere ? gTLocSphere->mCullCenter.x : 0.0f) << ","
|
||||
<< (gTLocSphere ? gTLocSphere->mCullCenter.y : 0.0f) << ","
|
||||
<< (gTLocSphere ? gTLocSphere->mCullCenter.z : 0.0f) << ") r="
|
||||
<< (gTLocSphere ? gTLocSphere->GetRadius() : 0.0f)
|
||||
<< "\n" << std::flush;
|
||||
}
|
||||
if (gTLocSphere == 0)
|
||||
@@ -2351,18 +2379,26 @@ void
|
||||
// - Non-POV (observing a PEER respawn): anchor at the peer's world point so the
|
||||
// swirl plays over there -- pure scale, then translate.
|
||||
const float s = scale;
|
||||
// Recenter on the mesh's own bounding-sphere centre: tsphere.bgf is modelled
|
||||
// with its centre OFF the origin, so a raw scale pushed it "significantly above
|
||||
// the viewport" once multiplied by 100-150. Translate the centre to the origin
|
||||
// FIRST, then scale, so the swirl sits ON the eye / anchor.
|
||||
const D3DXVECTOR3 &cc = gTLocSphere->mCullCenter;
|
||||
D3DXMATRIX recenter, scaleM;
|
||||
D3DXMatrixTranslation(&recenter, -cc.x, -cc.y, -cc.z);
|
||||
D3DXMatrixScaling(&scaleM, s, s, s);
|
||||
D3DXMATRIX m;
|
||||
if (gWarpPOV && view != 0)
|
||||
{
|
||||
D3DXMATRIX invView, scaleM;
|
||||
D3DXMATRIX invView;
|
||||
D3DXMatrixInverse(&invView, 0, view);
|
||||
D3DXMatrixScaling(&scaleM, s, s, s);
|
||||
m = scaleM * invView; // Scale in the eye's local frame, then eye->world
|
||||
m = recenter * scaleM * invView; // centre mesh, scale, then eye->world
|
||||
}
|
||||
else
|
||||
{
|
||||
D3DXMatrixScaling(&m, s, s, s);
|
||||
m._41 = gWarpX; m._42 = gWarpY; m._43 = gWarpZ;
|
||||
D3DXMATRIX anchorM;
|
||||
D3DXMatrixTranslation(&anchorM, gWarpX, gWarpY, gWarpZ);
|
||||
m = recenter * scaleM * anchorM; // centre mesh, scale, move to anchor
|
||||
}
|
||||
|
||||
gTLocSphere->SetLocalToWorld(m);
|
||||
@@ -2386,7 +2422,8 @@ void
|
||||
// (ALPHABLENDENABLE + ZWRITE off, L4VIDEO.cpp:7762) but the blend/colour are
|
||||
// indeterminate here (beams/pfx drew first): set them explicitly + RESTORE
|
||||
// every state (a leaked LIGHTING/TFACTOR/stage-op corrupts the scene).
|
||||
DWORD sSrc, sDst, sLight, sTFactor, sCOp, sCA1, sCA2, sAOp, sAA1;
|
||||
DWORD sSrc, sDst, sLight, sTFactor, sCOp, sCA1, sCA2, sAOp, sAA1, sZ;
|
||||
device->GetRenderState(D3DRS_ZENABLE, &sZ);
|
||||
device->GetRenderState(D3DRS_SRCBLEND, &sSrc);
|
||||
device->GetRenderState(D3DRS_DESTBLEND, &sDst);
|
||||
device->GetRenderState(D3DRS_LIGHTING, &sLight);
|
||||
@@ -2417,6 +2454,17 @@ void
|
||||
s_additive ? D3DBLEND_ONE : D3DBLEND_INVSRCALPHA);
|
||||
}
|
||||
device->SetRenderState(D3DRS_LIGHTING, FALSE);
|
||||
// Z-TEST OFF: the sphere is huge (up to 150x) and this arena is an enclosed
|
||||
// CAVERN, so a depth-tested sphere gets CLIPPED by the ceiling/walls ("the cavern
|
||||
// is obscuring it"). The warp is a POV overlay that engulfs you -- draw it over
|
||||
// the world regardless of depth (ZWRITE is already off in the alpha pass, so it
|
||||
// does not pollute the depth buffer for later draws). BT_WARP_ZTEST=1 keeps the
|
||||
// depth test if the overlay-over-everything look is unwanted.
|
||||
{
|
||||
static int s_ztest = -1;
|
||||
if (s_ztest < 0) { const char *z = getenv("BT_WARP_ZTEST"); s_ztest = (z && z[0] == '1') ? 1 : 0; }
|
||||
if (!s_ztest) device->SetRenderState(D3DRS_ZENABLE, FALSE);
|
||||
}
|
||||
{
|
||||
// TFACTOR: the RAMP now supplies the blue-white, so the tint is WHITE by
|
||||
// default (MODULATE by white = the baked swirl unchanged); only the ALPHA
|
||||
@@ -2439,6 +2487,7 @@ void
|
||||
|
||||
gTLocSphere->Draw(PASS_ALPHABLEND, view, frame_time);
|
||||
|
||||
device->SetRenderState(D3DRS_ZENABLE, sZ);
|
||||
device->SetRenderState(D3DRS_SRCBLEND, sSrc);
|
||||
device->SetRenderState(D3DRS_DESTBLEND, sDst);
|
||||
device->SetRenderState(D3DRS_LIGHTING, sLight);
|
||||
|
||||
Reference in New Issue
Block a user