Files
CydandClaude Fable 5 fdd9ac9d97 Initial import: Tesla Release 4.10 (Tesla:BattleTech & Tesla:Red Planet)
Archival snapshot of the Virtual World Entertainment Tesla cockpit
software, 1994-1996: MUNGA engine and L4 pod layer source (Borland
C++ 5.0), BT/RP game code, and game content (models, audio, maps,
gauges, Division renderer data). Includes third-party libraries:
Division dVS/DPL graphics, HMI SOS audio, WATTCP networking.

Files are preserved byte-for-byte (.gitattributes disables all
line-ending conversion). README.md documents the layout, target
hardware, and toolchain.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-02 13:21:58 -05:00

485 lines
15 KiB
C++

//===========================================================================//
// File: boxsolid.cc //
// Project: MUNGA Brick: Spatializer Library //
// Contents: Implementation details of bounding-box collision subtypes //
//---------------------------------------------------------------------------//
// Date Who Modification //
// -------- --- ---------------------------------------------------------- //
// 01/11/95 JMA Initial port back to C++ //
//---------------------------------------------------------------------------//
// Copyright (C) 1993-1995, Virtual World Entertainment, Inc. //
// All Rights reserved worldwide //
// This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL //
//===========================================================================//
#include <munga.hpp>
#pragma hdrstop
#if !defined(BOXSOLID_HPP)
# include <boxsolid.hpp>
#endif
#if !defined(LINE_HPP)
# include <line.hpp>
#endif
//#############################################################################
//############################### 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;
}