Beams at the models' NATURAL width, per-weapon tube models (de-cartooned)
The "cartoonish" beams: the port drew the ermlaser tube INFLATED -- a 3.0x glow + 0.9x core two-layer hack, 13x the authored radius. Parsed the beam models: ERMLASER/MLASER 0.22u radius, SLASER 0.11, LLASER 0.32, PPC 0.62 -- thin pencil beams with an authored per-class size progression; the PPC bolt is genuinely ~3x fatter with its own 62-vert shape. Now: ONE draw per beam at natural model scale (width multiplier 1.0), using the weapon's OWN tube model (ermlaser for lasers, ppc.bgf for PPCs; the s/m/llaser tubes load for other mechs' loadouts). BTPushBeamKind carries the model selector; BTPushBeam keeps its signature (kind 0). Tint = 40+215x the authored PipColor; the thin natural tubes stay under additive saturation so the scrolling grit reads without a hand-dimmed core layer. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
2036e7da51
commit
31626a3b97
+53
-27
@@ -56,7 +56,8 @@ struct BTBeamFx
|
||||
D3DXVECTOR3 from, to;
|
||||
DWORD color; // 0x00RRGGBB glow colour (additive; alpha ignored)
|
||||
float ttl, maxTtl; // seconds
|
||||
float width; // world units
|
||||
float width; // natural-model-scale multiplier (1.0 = authored radius)
|
||||
int kind; // tube model: 0=ermlaser 1=ppc 2=slaser 3=mlaser 4=llaser
|
||||
};
|
||||
static std::vector<BTBeamFx> gBTBeams;
|
||||
|
||||
@@ -85,8 +86,8 @@ void BTSetLodEye(float x, float y, float z)
|
||||
}
|
||||
}
|
||||
|
||||
void BTPushBeam(float fx, float fy, float fz, float tx, float ty, float tz,
|
||||
unsigned color, float ttl, float width)
|
||||
void BTPushBeamKind(float fx, float fy, float fz, float tx, float ty, float tz,
|
||||
unsigned color, float ttl, float width, int kind)
|
||||
{
|
||||
if (gBTBeams.size() > 256) return; // runaway guard
|
||||
BTBeamFx b;
|
||||
@@ -95,9 +96,16 @@ void BTPushBeam(float fx, float fy, float fz, float tx, float ty, float tz,
|
||||
b.color = color;
|
||||
b.ttl = ttl; b.maxTtl = (ttl > 1e-4f) ? ttl : 1e-4f;
|
||||
b.width = width;
|
||||
b.kind = (kind >= 0 && kind < 5) ? kind : 0;
|
||||
gBTBeams.push_back(b);
|
||||
}
|
||||
|
||||
void BTPushBeam(float fx, float fy, float fz, float tx, float ty, float tz,
|
||||
unsigned color, float ttl, float width)
|
||||
{
|
||||
BTPushBeamKind(fx, fy, fz, tx, ty, tz, color, ttl, width, 0);
|
||||
}
|
||||
|
||||
void BTDrawBeams(LPDIRECT3DDEVICE9 dev, const D3DXMATRIX *view, float dt)
|
||||
{
|
||||
if (gBTBeams.empty()) return;
|
||||
@@ -190,37 +198,51 @@ void BTDrawBeams(LPDIRECT3DDEVICE9 dev, const D3DXMATRIX *view, float dt)
|
||||
}
|
||||
}
|
||||
}
|
||||
// AUTHENTIC GEOMETRY: render the real ermlaser.bgf TUBE (a thin 2000-long tube
|
||||
// with UVs tiled ~8x down its length), transformed per beam (scale the length to
|
||||
// the shot distance, orient local -Z -> muzzle->target), instead of a camera-
|
||||
// facing billboard quad. BT_BEAM_TUBE=0 falls back to the billboard.
|
||||
// AUTHENTIC GEOMETRY: render each weapon's REAL beam model (thin 2000-long
|
||||
// tubes, -Z aligned, UVs tiled down the length), transformed per beam (scale
|
||||
// the length to the shot distance, orient local -Z -> muzzle->target). The
|
||||
// authored radii ARE the beam widths: ERMLASER 0.22, PPC 0.62 (a genuinely
|
||||
// fatter bolt), SLASER 0.11 / MLASER 0.22 / LLASER 0.32 -- draw at natural
|
||||
// scale (width multiplier 1.0), NOT inflated. BT_BEAM_TUBE=0 -> billboard.
|
||||
static int s_tubeTried = 0;
|
||||
static std::vector<float> s_tubeVB; // x,y,z,u,v per vertex
|
||||
static std::vector<uint32_t> s_tubeIB;
|
||||
static int s_tubeVerts = 0, s_tubeTris = 0;
|
||||
struct BTTubeModel
|
||||
{
|
||||
std::vector<float> vb; // x,y,z,u,v per vertex
|
||||
std::vector<uint32_t> ib;
|
||||
int verts, tris;
|
||||
};
|
||||
static BTTubeModel s_tubes[5];
|
||||
static const char *const kTubeNames[5] =
|
||||
{ "ermlaser.bgf", "ppc.bgf", "slaser.bgf", "mlaser.bgf", "llaser.bgf" };
|
||||
if (!s_tubeTried)
|
||||
{
|
||||
s_tubeTried = 1;
|
||||
const char *tvv = getenv("BT_BEAM_TUBE");
|
||||
if (tvv == 0 || tvv[0] != '0')
|
||||
{
|
||||
BgfData bd;
|
||||
if (LoadBgfFile("ermlaser.bgf", bd) && bd.ok && !bd.indices.empty())
|
||||
for (int t = 0; t < 5; ++t)
|
||||
{
|
||||
s_tubeVerts = (int)bd.verts.size();
|
||||
s_tubeVB.reserve(bd.verts.size() * 5);
|
||||
for (size_t k = 0; k < bd.verts.size(); ++k)
|
||||
BgfData bd;
|
||||
s_tubes[t].verts = s_tubes[t].tris = 0;
|
||||
if (LoadBgfFile(kTubeNames[t], bd) && bd.ok && !bd.indices.empty())
|
||||
{
|
||||
s_tubeVB.push_back(bd.verts[k].x); s_tubeVB.push_back(bd.verts[k].y);
|
||||
s_tubeVB.push_back(bd.verts[k].z);
|
||||
s_tubeVB.push_back(bd.verts[k].u); s_tubeVB.push_back(bd.verts[k].v);
|
||||
s_tubes[t].verts = (int)bd.verts.size();
|
||||
s_tubes[t].vb.reserve(bd.verts.size() * 5);
|
||||
for (size_t k = 0; k < bd.verts.size(); ++k)
|
||||
{
|
||||
s_tubes[t].vb.push_back(bd.verts[k].x);
|
||||
s_tubes[t].vb.push_back(bd.verts[k].y);
|
||||
s_tubes[t].vb.push_back(bd.verts[k].z);
|
||||
s_tubes[t].vb.push_back(bd.verts[k].u);
|
||||
s_tubes[t].vb.push_back(bd.verts[k].v);
|
||||
}
|
||||
s_tubes[t].ib = bd.indices;
|
||||
s_tubes[t].tris = (int)(bd.indices.size() / 3);
|
||||
}
|
||||
s_tubeIB = bd.indices;
|
||||
s_tubeTris = (int)(bd.indices.size() / 3);
|
||||
}
|
||||
}
|
||||
}
|
||||
const bool useTube = (s_beamTex != 0 && s_tubeTris > 0);
|
||||
const bool useTube = (s_beamTex != 0 && s_tubes[0].tris > 0);
|
||||
static float s_tubeWidth = -1.0f;
|
||||
if (s_tubeWidth < 0.0f)
|
||||
{ const char *wv = getenv("BT_BEAM_WIDTH"); s_tubeWidth = wv ? (float)atof(wv) : 1.0f; } // global width multiplier
|
||||
@@ -299,9 +321,9 @@ void BTDrawBeams(LPDIRECT3DDEVICE9 dev, const D3DXMATRIX *view, float dt)
|
||||
D3DXVECTOR3 xc; D3DXVec3Cross(&xc, &up, &zc); D3DXVec3Normalize(&xc, &xc);
|
||||
D3DXVECTOR3 yc; D3DXVec3Cross(&yc, &zc, &xc);
|
||||
D3DXMATRIX S, R, T, W;
|
||||
// PER-BEAM width (b.width) x global BT_BEAM_WIDTH multiplier: a wide dim glow
|
||||
// and a THIN bright core render at different radii (real ermlaser = thin core
|
||||
// in a wider glow) instead of stacking to a solid white cylinder.
|
||||
// NATURAL width: the model's authored radius IS the beam width
|
||||
// (ERMLASER 0.22u, PPC 0.62u...). b.width is a multiplier on that
|
||||
// (1.0 for weapon beams); BT_BEAM_WIDTH is the global dev override.
|
||||
const float ws = b.width * s_tubeWidth;
|
||||
D3DXMatrixScaling(&S, ws, ws, beamLen / 2000.0f);
|
||||
D3DXMatrixIdentity(&R);
|
||||
@@ -313,16 +335,20 @@ void BTDrawBeams(LPDIRECT3DDEVICE9 dev, const D3DXMATRIX *view, float dt)
|
||||
D3DXMatrixMultiply(&W, &W, &T);
|
||||
dev->SetTransform(D3DTS_WORLD, &W);
|
||||
dev->SetRenderState(D3DRS_TEXTUREFACTOR, col);
|
||||
// the beam's own weapon-model tube (ermlaser/ppc/slaser/...); fall
|
||||
// back to the ermlaser tube when that model didn't load
|
||||
const BTTubeModel &tube =
|
||||
(s_tubes[b.kind].tris > 0) ? s_tubes[b.kind] : s_tubes[0];
|
||||
// per-beam UVs: world-fixed grit density + scroll (see banner above)
|
||||
const float uScale = beamLen / 2000.0f;
|
||||
s_tubeScrolled = s_tubeVB;
|
||||
s_tubeScrolled = tube.vb;
|
||||
for (size_t k = 0; k + 4 < s_tubeScrolled.size(); k += 5)
|
||||
{
|
||||
s_tubeScrolled[k + 3] = s_tubeScrolled[k + 3] * uScale + uScroll;
|
||||
s_tubeScrolled[k + 4] += vScroll;
|
||||
}
|
||||
dev->DrawIndexedPrimitiveUP(D3DPT_TRIANGLELIST, 0, s_tubeVerts, s_tubeTris,
|
||||
&s_tubeIB[0], D3DFMT_INDEX32, &s_tubeScrolled[0], 5 * sizeof(float));
|
||||
dev->DrawIndexedPrimitiveUP(D3DPT_TRIANGLELIST, 0, tube.verts, tube.tris,
|
||||
&tube.ib[0], D3DFMT_INDEX32, &s_tubeScrolled[0], 5 * sizeof(float));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user