Imports the current Win32 source for the pod-racing game 'Red Planet', built on the MUNGA engine and its L4 (Win32/DirectX) platform layer: - MUNGA / MUNGA_L4: cross-platform engine core and Win32 backend - RP / RP_L4: Red Planet game logic and Win32 application - DivLoader, Setup1: asset loader and installer project - lib, MUNGA_L4/openal, MUNGA_L4/sos: third-party audio dependencies Removed stale Subversion metadata and added .gitignore/.gitattributes. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
465 lines
13 KiB
C++
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;
|
|
}
|