Files
arcattackandClaude Opus 4.8 7b7d465e5e Initial commit: bt411 -- standalone Windows BattleTech (Tesla 4.10 port)
Clean, self-contained extraction of the BattleTech-specific work from the
reverse-engineering workspace -- engine + game + content + build, with nothing
from Red Planet or the raw archive dumps. Builds green (Win32) and runs the
single-player drive->animate->target->fire->damage->destroy loop out of the box.

Layout:
  engine/   MUNGA + MUNGA_L4 shared 2007 engine, carrying our BT render/loader
            work (bgfload/L4D3D/L4VIDEO: BSL bit-slice decode, LOD/ground/shadow
            models) + image codec; the minimal rp/ headers the audio HAL needs
  game/     reconstructed BT logic + surviving-original BT source + fwd shims
            + WinMain launcher
  content/  full runtime tree (BTL4.RES, VIDEO/, GAUGE/, AUDIO/, eggs, BTDPL.INI)
  docs/     format specs + reconstruction ledgers
  reference/ raw Ghidra pseudocode (recon source-of-truth) + decomp exporter
  tools/    MP console emulator + map/resource scanners

One top-level CMake builds munga_engine lib + bt410_l4 game lib + btl4.exe.
All paths relativized (186 fwd shims + ~437 CMake abs paths -> repo-relative);
DXSDK is the one external, overridable via -DDXSDK. Verified: builds to a
byte-identical 2.27MB exe and runs combat (TARGET DESTROYED, 0 crashes) against
the bundled content.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-05 21:03:40 -05:00

465 lines
13 KiB
C++

#include "munga.h"
#pragma hdrstop
#include "boxsolid.h"
#include "line.h"
//#############################################################################
//############################### BoxedCone #############################
//#############################################################################
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
BoxedCone::BoxedCone(
const ExtentBox &extents,
BoxedSolid::Material material,
Simulation *owner,
BoxedSolid *next_solid
):
BoxedSolid(extents, ConeType, material, owner, next_solid)
{
Check_Pointer(this);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
BoxedCone::~BoxedCone()
{
Check_Pointer(this);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Logical
BoxedCone::IntersectsBounded(const ExtentBox &extents)
{
Check(this);
Check(&extents);
Verify(minX <= extents.minX);
Verify(maxX >= extents.maxX);
Verify(minY <= extents.minY);
Verify(maxY >= extents.maxY);
Verify(minZ <= extents.minZ);
Verify(maxZ >= extents.maxZ);
//
//------------------------------------------------------------------------
// Find the center point in the XZ plane of the cone, and find the biggest
// radius of the cone by measuring in the X axis, along with its height
//------------------------------------------------------------------------
//
Vector3D center;
center.x = (minX + maxX) * 0.5f;
center.y = minY;
center.z = (minZ + maxZ) * 0.5f;
Scalar radius = maxX - center.x;
Verify(!Small_Enough(radius));
//
//--------------------------------------------------------------------------
// Convert the closest point in the slice to the coordinates of the cone,
// putting the apex of the cone at the origin, then make sure that the point
// isn't in one of the corners of the box
//--------------------------------------------------------------------------
//
Vector3D nearest(center);
extents.Constrain(&nearest);
center.y = maxY;
nearest.Subtract(center,nearest);
Scalar r = nearest.x*nearest.x + nearest.z*nearest.z;
if (r > radius*radius)
{
return False;
}
//
//--------------------------------------------------------------------------
// Compute the distance from the axis, and then see if the slope of the line
// from the cone apex to the test point is less than the slope of the cone
//--------------------------------------------------------------------------
//
r = Sqrt(r);
Scalar height = maxY - minY;
return r*height <= nearest.y*radius;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Logical
BoxedCone::ContainsBounded(const Point3D &point)
{
Check(this);
Check(&point);
Verify(minX <= point.x);
Verify(maxX >= point.x);
Verify(minY <= point.y);
Verify(maxY >= point.y);
Verify(minZ <= point.z);
Verify(maxZ >= point.z);
//
//------------------------------------------------------------------------
// Find the center point in the XZ plane of the cone, and find the biggest
// radius of the cone by measuring in the X axis, along with its height
//------------------------------------------------------------------------
//
Vector3D center;
center.x = (minX + maxX) * 0.5f;
center.y = maxY;
center.z = (minZ + maxZ) * 0.5f;
Scalar radius = maxX - center.x;
Verify(!Small_Enough(radius));
//
//--------------------------------------------------------------------------
// Convert the closest point in the slice to the coordinates of the cone,
// putting the apex of the cone at the origin, then make sure that the point
// isn't in one of the corners of the box
//--------------------------------------------------------------------------
//
Vector3D nearest;
nearest.Subtract(center, point);
Scalar r = nearest.x*nearest.x + nearest.z*nearest.z;
if (r > radius*radius)
{
return False;
}
//
//--------------------------------------------------------------------------
// Compute the distance from the axis, and then see if the slope of the line
// from the cone apex to the test point is less than the slope of the cone
//--------------------------------------------------------------------------
//
r = Sqrt(r);
Scalar height = maxY - minY;
return r*height <= nearest.y*radius;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Scalar
BoxedCone::FindDistanceBelowBounded(const Point3D &point)
{
Check(this);
Check(&point);
Verify(minX <= point.x);
Verify(maxX >= point.x);
Verify(minY <= point.y);
Verify(minZ <= point.z);
Verify(maxZ >= point.z);
//
//------------------------------------------------------------------------
// Find the center point in the XZ plane of the cone, and find the biggest
// radius of the cone by measuring in the X axis, along with its height
//------------------------------------------------------------------------
//
Vector3D center;
center.x = (minX + maxX) * 0.5f;
center.y = maxY;
center.z = (minZ + maxZ) * 0.5f;
Scalar radius = maxX - center.x;
Verify(!Small_Enough(radius));
//
//--------------------------------------------------------------------------
// Convert the closest point in the slice to the coordinates of the cone,
// putting the apex of the cone at the origin, then make sure that the point
// isn't in one of the corners of the box
//--------------------------------------------------------------------------
//
Vector3D nearest;
nearest.Subtract(point, center);
Scalar r = nearest.x*nearest.x + nearest.z*nearest.z;
if (r > radius*radius)
{
return -1.0f;
}
//
//--------------------------------------------------------------------------
// Compute the distance from the axis, and then see if the slope of the line
// from the cone apex to the test point is less than the slope of the cone
//--------------------------------------------------------------------------
//
r = Sqrt(r);
Scalar height = maxY - minY;
nearest.y += r*(height/radius);
return Max(nearest.y,0.0f);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// This function is a helper function used in the derivation for the terms of
// the quadratic equation to find t when colliding a line and a cone. I'm not
// real clear on the physical representation of this kind of dot product
//
static Scalar
Special_Cone_Dot_Product(
const Vector3D &v1,
const Vector3D &v2,
Scalar squared_tan
)
{
return v1.x*v2.x - squared_tan*v1.y*v2.y + v1.z*v2.z;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Logical
BoxedCone::HitByBounded(
Line *line,
Scalar enters,
Scalar leaves
)
{
Check(this);
Check(line);
Verify(enters <= leaves);
Verify(leaves >= 0.0f);
//
//--------------------------------------------------------------------------
// Find out the size of the box, and set up the height and maximum radius of
// the cone, along with the squared tangent of the spread angle
//--------------------------------------------------------------------------
//
Vector3D center;
center.x = (minX + maxX) * 0.5f;
center.y = maxY;
center.z = (minZ + maxZ) * 0.5f;
Scalar radius = maxX - center.x;
Scalar height = maxY - minY;
Verify(!Small_Enough(height));
Scalar squared_tan = radius*radius/(height*height);
//
//-------------------------------------------------------------------------
// Find the line between the point of the cone and the origin of our line,
// then set up the conditions for the quadratic equation. The following
// equation sets up a*t^2 + b*t + c = 0. The terms a, b, and c are derived
// by solving the following equations for t:
//
// p == line->origin + line->direction * t
// r == p - cone->apex
// r.x^2 + r.z^2 == tan^2(cone_angle)*r.y^2
//
// The special cone dot product function is a handy way to reduce the
// complexity of the problem
//-------------------------------------------------------------------------
//
Vector3D v;
v.Subtract(line->origin,center);
//
//--------------------------------------------------------------------------
// If the line diverges from the axis at the same angle as the spread angle,
// we need to find the closest point on the line to the axis. We then drop
// a vertical plane containing the line through the cone at this point, thus
// generating a hyperbola. We then solve the hyperbola vs. line equation to
// get our intersection point
//--------------------------------------------------------------------------
//
Scalar a =
Special_Cone_Dot_Product(line->direction, line->direction, squared_tan);
if (Small_Enough(a))
{
}
//
//-----------------------------------------------
// Otherwise, continue solving the first equation
//-----------------------------------------------
//
else
{
Scalar b =
2.0f * Special_Cone_Dot_Product(v, line->direction, squared_tan);
Scalar c = Special_Cone_Dot_Product(v, v, squared_tan);
//
//-----------------------------------------------------------------------
// Now, use the quadratic equation to determine where the two points
// intersect the cone. If there is no solution, than the line missed the
// cone
//-----------------------------------------------------------------------
//
Verify(!Small_Enough(a));
Scalar t = -b / (2.0f * a);
Scalar i = b*b - 4.0f*a*c;
if (i < 0.0f)
{
return False;
}
//
//----------------------------------------------------------------------
// If the interval is zero, then the line hits the cone in one spot only
// (a tangential hit). Check to see if hits the lower half of the conic
// equation (our cone)
//----------------------------------------------------------------------
//
if (Small_Enough(i))
{
Scalar y = v.y + line->direction.y * t;
if (y > 0.0f)
{
return False;
}
//
//----------------------------------------------------------------
// It hit the lower cone, so see if it was entering or leaving the
// cone
//----------------------------------------------------------------
//
if (line->direction.y > 0.0f)
{
//
//-------------------------------------------------------------
// The line is leaving the cone, so see if we need to reset the
// leaving distance
//-------------------------------------------------------------
//
if (t < leaves)
{
leaves = t;
}
}
//
//------------------------------------------------------------
// The line is entering the cone, so see if we need to set the
// entering distance
//------------------------------------------------------------
//
else if (t > enters)
{
enters = t;
}
//
//-------------------------------------------------------------------
// See if we still have a collision with the cone, and if so, set the
// new line length
//-------------------------------------------------------------------
//
if (enters > leaves || enters > line->length || leaves < 0.0f)
{
return False;
}
line->length = Max(enters, 0.0f);
return True;
}
//
//----------------------------------------------------------------------
// If the interval is non-zero, then the conic section was struck in two
// places. Find out the lengths to those places and the y values for
// those points
//----------------------------------------------------------------------
//
i = Sqrt(i) / (2.0f * a);
Scalar t1,t2;
if (i > 0.0f)
{
t1 = t - i;
t2 = t + i;
}
else
{
t1 = t + i;
t2 = t - i;
}
Scalar y1 = v.y + line->direction.y * t1;
Scalar y2 = v.y + line->direction.y * t2;
//
//----------------------------------------------------------------------
// There are four combinations of the signs of the y values. Each has a
// different effect on missing/hitting, entering or leaving distances.
// First check to see if only the upper conic was hit
//----------------------------------------------------------------------
//
if (y1 > 0.0f)
{
if (y2 > 0.0f)
{
return False;
}
//
//-----------------------------------------------------------------
// The ray is entering our conic, so set the distance appropriately
//-----------------------------------------------------------------
//
if (t2 > enters)
{
enters = t2;
}
}
//
//----------------------------------------------------------------------
// See if the ray is leaving the lower conic, and if so, set the leaving
// distance accordingly
//----------------------------------------------------------------------
//
else
{
if (y2 > 0.0f)
{
if (t1 < leaves)
{
leaves = t1;
}
}
//
//--------------------------------------------------------------------
// Both spots are in the lower conic, so set both leaving and entering
// distances
//--------------------------------------------------------------------
//
else
{
if (t1 > enters)
{
enters = t1;
}
if (t2 < leaves)
{
leaves = t2;
}
}
}
//
//-----------------------------------------------------------------------
// See if we still have a collision with the cone, and if so, set the new
// line length
//-----------------------------------------------------------------------
//
if (enters > leaves || enters > line->length || leaves < 0.0f)
{
return False;
}
line->length = Max(enters, 0.0f);
}
return True;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Logical
BoxedCone::TestInstance() const
{
return solidType == ConeType;
}