diff --git a/engine/MUNGA_L4/L4VIDEO.cpp b/engine/MUNGA_L4/L4VIDEO.cpp index f549a48..0e5f200 100644 --- a/engine/MUNGA_L4/L4VIDEO.cpp +++ b/engine/MUNGA_L4/L4VIDEO.cpp @@ -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 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 s_tubeVB; // x,y,z,u,v per vertex - static std::vector s_tubeIB; - static int s_tubeVerts = 0, s_tubeTris = 0; + struct BTTubeModel + { + std::vector vb; // x,y,z,u,v per vertex + std::vector 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 { diff --git a/game/reconstructed/mech4.cpp b/game/reconstructed/mech4.cpp index 33521d1..48affcd 100644 --- a/game/reconstructed/mech4.cpp +++ b/game/reconstructed/mech4.cpp @@ -2272,18 +2272,21 @@ void if (r < 0.0f || g < 0.0f || b < 0.0f) { r = 0.78f; g = 0.08f; b = 0.02f; } // PPC (classID 0xBD4 = 3028): a thicker, brighter bolt than a laser tube. const int isPPC = ((int)em->GetClassID() == 3028); - unsigned glow = - (((unsigned)(r * 200.0f) & 0xFF) << 16) | - (((unsigned)(g * 200.0f) & 0xFF) << 8) | - ((unsigned)(b * 200.0f) & 0xFF); - unsigned core = - (((unsigned)(96.0f + r * 96.0f) & 0xFF) << 16) | - (((unsigned)(96.0f + g * 96.0f) & 0xFF) << 8) | - ((unsigned)(96.0f + b * 96.0f) & 0xFF); - BTPushBeam(mz.x, mz.y, mz.z, bend.x, bend.y, bend.z, - glow, ttl1, isPPC ? 4.4f : 3.0f); // wide glow (grit shows) - BTPushBeam(mz.x, mz.y, mz.z, bend.x, bend.y, bend.z, - core, ttl1, isPPC ? 1.5f : 0.9f); // thin bright core + // ONE draw per beam, at the model's NATURAL width: the weapon's + // own tube (ERMLASER radius 0.22u, PPC bolt 0.62u) IS the beam + // -- the old inflated two-layer glow/core (3.0x + 0.9x widths) + // drew fat cartoon cylinders 13x the authored size. The tint + // modulates the scrolling grit; thin natural tubes stay under + // saturation without a hand-dimmed core. + extern void BTPushBeamKind(float,float,float, float,float,float, + unsigned, float, float, int); + unsigned tint = + (((unsigned)(40.0f + r * 215.0f) & 0xFF) << 16) | + (((unsigned)(40.0f + g * 215.0f) & 0xFF) << 8) | + ((unsigned)(40.0f + b * 215.0f) & 0xFF); + BTPushBeamKind(mz.x, mz.y, mz.z, bend.x, bend.y, bend.z, + tint, ttl1, 1.0f /* natural model width */, + isPPC ? 1 : 0 /* ppc.bgf : ermlaser.bgf */); if (getenv("BT_BEAM_LOG") && (s_beamStateLog++ % 30) == 0) DEBUG_STREAM << "[beam] " << (isPPC ? "PPC" : "laser") << " #" << wi << " mz=(" << mz.x << "," << mz.y << "," << mz.z