Searchlight beam: the btfx brighten material class + BT_SPOT_SELF rig
SPOT.BGF decoded: a 7-vert cone from the mount, ~50u forward and ~35deg DOWN
(a ground-pool lamp, not an air beam), verts tinted cyan-white, material
class 'brighten' smuggling its additive factor in DIFFUSE.r (0.25) with a
warm emissive on the night page. The loader had never met the class -- it
drew as an opaque dark-red blob. Now: brightenFactor parsed (name-gated to
brighten*), batch -> L4DRAWOP.brightenAlpha, drawn in the blend pass as an
additive veil (dest += vertexRGB x factor), unlit. [T3] tint compose
(vertex cyan vs night emissive warm) noted in the draw branch -- field
eyeball accepted the current look.
BT_SPOT_SELF=1 (bench-only): builds the cone on the own-cockpit tree so the
BT_CAM=face view can inspect it solo. KB: view toggle is BACKTICK ('V' is
the rear-view hold since #68 -- the toggle skips bound keys); stale V-toggle
claims swept.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
7d8f109241
commit
b75bb4a04c
@@ -443,8 +443,13 @@ d3d_OBJECT* d3d_OBJECT::LoadObjectBGF(LPDIRECT3DDEVICE9 device, char *fileName)
|
||||
// drawAsSky) and vanishes -- DECLOUDS carries vertex alpha but must
|
||||
// stay in the sky pass.
|
||||
const bool vtxAlphaOp = data.batches[i].vertexAlpha && !isSkyObj;
|
||||
object->mDrawOps[i].alphaTest = (object->mIsShadow != 0) || vtxAlphaOp;
|
||||
// BTFX brighten veil (spot cone): route to the blend pass like the
|
||||
// vertex-alpha cards; the draw-state branch in DrawMesh adds
|
||||
// dest += emissive x factor instead of SRCALPHA/INVSRCALPHA.
|
||||
const bool brightenOp = data.batches[i].brightenFactor > 0.0f && !isSkyObj;
|
||||
object->mDrawOps[i].alphaTest = (object->mIsShadow != 0) || vtxAlphaOp || brightenOp;
|
||||
object->mDrawOps[i].vertexAlphaBlend = vtxAlphaOp;
|
||||
object->mDrawOps[i].brightenAlpha = brightenOp ? data.batches[i].brightenFactor : 0.0f;
|
||||
object->mDrawOps[i].drawAsDecal = false;
|
||||
object->mDrawOps[i].drawAsSky = isSkyObj; // sky dome -> PASS_SKY (fullbright)
|
||||
// PUNCH (dpl_Punchize; BT_PUNCH=0 disables): cutout batch -- black texels
|
||||
@@ -1279,7 +1284,9 @@ void d3d_OBJECT::DrawMesh(int pass, const D3DXMATRIX *viewTransform, Time target
|
||||
// (SRCALPHA/INVSRCALPHA). Runs only in the blend pass (the op filter
|
||||
// above routed it there; ALPHABLEND is on and z-write off pass-wide).
|
||||
DWORD sVLight = 0, sVSrc = 0, sVDst = 0, sVCop = 0, sVCa1 = 0, sVCa2 = 0, sVAop = 0, sVAa2 = 0;
|
||||
const bool vtxAlphaCard = (drawOp->vertexAlphaBlend && pass == PASS_ALPHABLEND);
|
||||
const bool brightenCard = (drawOp->brightenAlpha > 0.0f && pass == PASS_ALPHABLEND);
|
||||
const bool vtxAlphaCard =
|
||||
((drawOp->vertexAlphaBlend && pass == PASS_ALPHABLEND) || brightenCard);
|
||||
if (vtxAlphaCard)
|
||||
{
|
||||
mDevice->GetRenderState(D3DRS_LIGHTING, &sVLight);
|
||||
@@ -1292,13 +1299,35 @@ void d3d_OBJECT::DrawMesh(int pass, const D3DXMATRIX *viewTransform, Time target
|
||||
mDevice->GetTextureStageState(0, D3DTSS_ALPHAARG2, &sVAa2);
|
||||
mDevice->SetRenderState(D3DRS_LIGHTING, FALSE);
|
||||
mDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
|
||||
mDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
|
||||
mDevice->SetRenderState(D3DRS_DESTBLEND,
|
||||
brightenCard ? D3DBLEND_ONE : D3DBLEND_INVSRCALPHA);
|
||||
if (brightenCard)
|
||||
{
|
||||
// BTFX brighten veil: dest += vertexRGB x factor. SPOT.BGF's
|
||||
// verts author the beam tint (cyan-white 0.12/0.98/1.0, alpha
|
||||
// 1.0 -- no fade gradient); the factor rides TFACTOR alpha.
|
||||
// [T3 tint compose]: the night material ALSO authors emissive
|
||||
// (0.9,0.4,0.2 warm) -- whether the DIV board tinted the veil
|
||||
// by vertex, emissive, or their product is unconfirmed; vertex
|
||||
// (a white-blue searchlight) matches the authored geometry
|
||||
// gradient model. Flip here if era-look evidence says warm.
|
||||
mDevice->SetRenderState(D3DRS_TEXTUREFACTOR,
|
||||
D3DCOLOR_COLORVALUE(1.0f, 1.0f, 1.0f, drawOp->brightenAlpha));
|
||||
mDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1);
|
||||
mDevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_DIFFUSE);
|
||||
mDevice->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_TFACTOR);
|
||||
mDevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG2);
|
||||
mDevice->SetTextureStageState(0, D3DTSS_ALPHAARG2, D3DTA_TFACTOR);
|
||||
}
|
||||
else
|
||||
{
|
||||
mDevice->SetTextureStageState(0, D3DTSS_COLOROP,
|
||||
drawOp->texture.texture != NULL ? D3DTOP_MODULATE : D3DTOP_SELECTARG2);
|
||||
mDevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
|
||||
mDevice->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_DIFFUSE);
|
||||
mDevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG2);
|
||||
mDevice->SetTextureStageState(0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE);
|
||||
}
|
||||
}
|
||||
|
||||
const bool copCut = (drawOp->copRole == 2 && iOp > 0
|
||||
|
||||
@@ -350,6 +350,12 @@ struct MatInfo {
|
||||
// EMISSIVE (tag 0x26): pure-emissive materials (diffuse black) render as an
|
||||
// unlit glow -- tex x emissive (btpolar:pintBIceEmit_mtl, the polar ice).
|
||||
bool hasEmissive = false;
|
||||
// BTFX "brighten" class (SPOT.BGF searchlight beam cone): the material
|
||||
// smuggles its ADDITIVE factor in DIFFUSE.r (brighten.25 authors diffuse
|
||||
// {0.25,0,0}) and its glow colour in EMISSIVE ({0.9,0.4,0.2} on the night
|
||||
// page). 0 = not a brighten material; >0 = draw as an additive veil,
|
||||
// dest += emissive x factor (the 1995 DIV brighten semantics).
|
||||
float brightenFactor = 0.0f;
|
||||
float emissive[3] = {0,0,0};
|
||||
};
|
||||
|
||||
@@ -495,12 +501,14 @@ struct MaterialResolver {
|
||||
std::string name, texName, rampName;
|
||||
MatInfo info;
|
||||
bool haveColor = false;
|
||||
float rawDiffuseR = -1.0f; // brighten-class factor carrier
|
||||
for (const Chunk& ch : c.children) {
|
||||
if (ch.id == TAG_NAME) name = chunkStr(ch);
|
||||
else if (ch.id == TAG_MATERIAL_TEXTURE && ch.len > 1) texName = chunkStr(ch, 1);
|
||||
else if (ch.id == TAG_RAMP_REF) rampName = chunkStr(ch);
|
||||
else if (ch.id == TAG_DIFFUSE && ch.len >= 12) {
|
||||
info.color = packColor(rdF32(ch.data), rdF32(ch.data + 4), rdF32(ch.data + 8));
|
||||
rawDiffuseR = rdF32(ch.data);
|
||||
haveColor = true;
|
||||
info.hasDiffuse = true;
|
||||
} else if (ch.id == TAG_AMBIENT && ch.len >= 12 && !haveColor
|
||||
@@ -520,6 +528,12 @@ struct MaterialResolver {
|
||||
info.hasEmissive = (info.emissive[0] + info.emissive[1] + info.emissive[2]) > 0.001f;
|
||||
}
|
||||
}
|
||||
// BTFX brighten class: name-gated so ordinary materials keep
|
||||
// their diffuse semantics untouched (the factor rides only in
|
||||
// "brighten*" records -- brighten.25/.5/.75 in btfx.bmf).
|
||||
if (name.compare(0, 8, "brighten") == 0 && rawDiffuseR > 0.0f)
|
||||
info.brightenFactor = rawDiffuseR;
|
||||
|
||||
// Resolve the ramp reference to its low/high colours: this
|
||||
// file's own definitions first, then the cross-library
|
||||
// registry (mech skins reference 'softer' defined elsewhere).
|
||||
@@ -645,6 +659,7 @@ struct Builder {
|
||||
int currentCopRole = 0; // role of the pmesh being built (consumed by buildPmesh)
|
||||
bool currentHasEmissive = false;
|
||||
float currentEmissive[3] = {0,0,0};
|
||||
float currentBrighten = 0.0f; // btfx brighten-class additive factor
|
||||
|
||||
// per-vertex scratch (normals accumulated from forward triangles only)
|
||||
std::vector<float> px, py, pz, nx, ny, nz, uu, vv;
|
||||
@@ -926,6 +941,7 @@ struct Builder {
|
||||
batch.shadowMat = currentShadowMat;
|
||||
batch.hasEmissive = currentHasEmissive;
|
||||
for (int i = 0; i < 3; ++i) batch.emissive[i] = currentEmissive[i];
|
||||
batch.brightenFactor = currentBrighten; // btfx additive veil (spot cone)
|
||||
// SUBMISSION-ORDER DEPTH BIAS (additive objects): the content layers
|
||||
// EXACTLY-COPLANAR shells (AR02 LOD2: solid concrete + a coplanar
|
||||
// PUNCH-cutout overlay + an inner shell, plane separations
|
||||
@@ -1251,8 +1267,9 @@ struct Builder {
|
||||
for (int i = 0; i < 3; ++i) { currentRampLo[i] = info.rampLo[i]; currentRampHi[i] = info.rampHi[i]; }
|
||||
currentHasEmissive = info.hasEmissive;
|
||||
for (int i = 0; i < 3; ++i) currentEmissive[i] = info.emissive[i];
|
||||
currentBrighten = info.brightenFactor;
|
||||
}
|
||||
else { currentColor = colorForMaterial(full); currentTex.clear(); currentHasDiffuse = false; currentTexChannel = 0; currentTexScroll = false; currentTexScrollU = currentTexScrollV = 0.0f; }
|
||||
else { currentColor = colorForMaterial(full); currentTex.clear(); currentHasDiffuse = false; currentTexChannel = 0; currentTexScroll = false; currentTexScrollU = currentTexScrollV = 0.0f; currentBrighten = 0.0f; }
|
||||
// TRANSLOCATION WARP: override the material's coarse "sky" ramp with the
|
||||
// NARROW LAVENDER ramp that matched the original (capture.png). The swirl
|
||||
// is the bintA cloud coloured lo->hi by luminance; a wide sky ramp
|
||||
|
||||
@@ -44,6 +44,12 @@ struct BgfDrawBatch {
|
||||
// alpha). The verts keep their authored RGBA gradient and the draw routes
|
||||
// to the alpha-blend pass, unlit, colour = texture x vertex gradient.
|
||||
bool vertexAlpha = false;
|
||||
// BTFX "brighten" ADDITIVE VEIL (the searchlight SPOT.BGF beam cone): the
|
||||
// material class authors its factor in DIFFUSE.r and its glow colour in
|
||||
// EMISSIVE; the batch draws in the blend pass as dest += emissive x factor
|
||||
// (1995 DIV brighten semantics; the record also authors fog IMMUNE +
|
||||
// DRAWLAST, both satisfied by the blend pass). 0 = not a brighten batch.
|
||||
float brightenFactor = 0.0f;
|
||||
// TEXTURE ALPHA CUTOUT (issue #3, the wreck scorch splat): an RGBA4444 BSL
|
||||
// slice (texChannel >= 8) carries an AUTHORED alpha channel -- in the shipped
|
||||
// content it is a BINARY 0/240 cutout mask (bexp9 = the scorch splat
|
||||
|
||||
@@ -109,6 +109,11 @@ struct L4DRAWOP
|
||||
// UNLIT, colour = texture x the authored per-vertex gradient, alpha = the
|
||||
// per-vertex fade (SRCALPHA/INVSRCALPHA).
|
||||
bool vertexAlphaBlend;
|
||||
// BTFX BRIGHTEN VEIL (the searchlight SPOT.BGF cone): additive brighten of
|
||||
// the framebuffer, dest += material.Emissive x this factor (the class
|
||||
// authors the factor in DIFFUSE.r, the glow colour in EMISSIVE). Drawn in
|
||||
// the alpha-blend pass, unlit, fog-immune by authoring. 0 = off.
|
||||
float brightenAlpha;
|
||||
// COCKPIT PUNCH STENCIL-CUT (task #55, i860-firmware-decoded): 0=normal,
|
||||
// 1=aperture MASK (drawn stencil-only, paired with the following hull op),
|
||||
// 2=HULL (drawn stencil-rejected under the mask = window cutouts).
|
||||
|
||||
Reference in New Issue
Block a user