Files
TeslaRel410/CODE/RP/MUNGA/BOXDISKS.CPP
T
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

1797 lines
51 KiB
C++

//===========================================================================//
// File: boxdisks.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
#if !defined(PLANE_HPP)
# include <plane.hpp>
#endif
//#############################################################################
//########################## BoxedXAxisCylinder #########################
//#############################################################################
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
BoxedXAxisCylinder::BoxedXAxisCylinder(
const ExtentBox &extents,
BoxedSolid::Material material,
Simulation *owner,
BoxedSolid *next_solid
):
BoxedSolid(extents, XAxisCylinderType, material, owner, next_solid)
{
Check_Pointer(this);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
BoxedXAxisCylinder::~BoxedXAxisCylinder()
{
Check_Pointer(this);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Logical
BoxedXAxisCylinder::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 YZ plane of the cylinder, and find the
// radius of the cylinder by measuring in the Y axis. The X value will
// automatically be within the cylinder if Y & Z are
//---------------------------------------------------------------------
//
Scalar y = (minY + maxY) * 0.5f;
Scalar z = (minZ + maxZ) * 0.5f;
Scalar radius = maxY - y;
//
//-----------------------------------------------------------------------
// Convert the point to the coordinates of the cylinder, putting the
// center point of the cylinder at the origin. Note that we are
// subtracting the point from the center point as opposed to the normal
// way. This will result in both X and Y being negated, but since we are
// squaring them, this will not matter
//-----------------------------------------------------------------------
//
Scalar y2 = y;
Scalar z2 = z;
Clamp(y2, extents.minY, extents.maxY);
Clamp(z2, extents.minZ, extents.maxZ);
y2 -= y;
z2 -= z;
return y2*y2 + z2*z2 <= radius*radius;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Logical
BoxedXAxisCylinder::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 YZ plane of the cylinder, and find the
// radius of the cylinder by measuring in the Y axis. The X value will
// automatically be within the cylinder if Y & Z are
//---------------------------------------------------------------------
//
Scalar y = (minY + maxY) * 0.5f;
Scalar z = (minZ + maxZ) * 0.5f;
Scalar radius = maxY - y;
//
//-----------------------------------------------------------------------
// Convert the point to the coordinates of the cylinder, putting the
// center point of the cylinder at the origin. Note that we are
// subtracting the point from the center point as opposed to the normal
// way. This will result in both X and Y being negated, but since we are
// squaring them, this will not matter
//-----------------------------------------------------------------------
//
y -= point.y;
z -= point.z;
return y*y + z*z <= radius*radius;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Scalar
BoxedXAxisCylinder::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 cylinder, and find the
// radius of the cylinder by measuring in the X axis. The Y value will
// automatically be within the cylinder if X & Z are
//---------------------------------------------------------------------
//
Scalar z = (minZ + maxZ) * 0.5f;
Scalar radius = maxZ - z;
//
//--------------------------------------------------------------------------
// figure out the thickness of cylinder slice where a plane perpendicular to
// X drops through the cylinder and the test point
//--------------------------------------------------------------------------
//
z = point.z - z;
Scalar y = radius - Sqrt(radius*radius - z*z);
if (point.y > maxY - y)
{
return point.y - maxY + y;
}
else if (point.y < minY + y)
{
return -1.0f;
}
else
{
return 0.0f;
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Logical
BoxedXAxisCylinder::HitByBounded(
Line *line,
Scalar enters,
Scalar leaves
)
{
Check(this);
Check(line);
Verify(enters <= leaves);
Verify(leaves >= 0.0f);
Scalar y = (minY + maxY) * 0.5f;
Scalar z = (minZ + maxZ) * 0.5f;
Scalar radius = maxY - y;
Scalar a =
line->direction.y*line->direction.y
+ line->direction.z*line->direction.z;
//
//--------------------------------------------------------------------------
// If the line is parallel to the cylinder, see if it hits the bottom/top of
// the cylinder in the X-Z plane. If it does, it hits the cylinder when it
// hits the box, otherwise it misses altogether
//--------------------------------------------------------------------------
//
if (Small_Enough(a))
{
y -= line->origin.y;
z -= line->origin.z;
if (y*y + z*z > radius*radius)
{
return False;
}
line->length = Max(enters, 0.0f);
return True;
}
//
//-----------------------------------------------------------------------
// The line is not parallel to the cylinder, so solve the equation giving
// the intersection of the line with the cylinder using the quadratic
// equation
//-----------------------------------------------------------------------
//
y = line->origin.y - y;
z = line->origin.z - z;
Scalar b = 2.0f * (line->direction.y*y + line->direction.z*z);
Scalar c = y*y + z*z - radius*radius;
Scalar i = b*b - 4.0f*a*c;
if (i < SMALL)
{
return False;
}
//
//-------------------------------------------------------------------------
// If the line does hit the cylinder, update the enter/leaving distances to
// take the cylinder surface into account
//-------------------------------------------------------------------------
//
Verify(a > SMALL);
Scalar ratio = Sqrt(a)/(a*a);
i = Sqrt(i);
b *= -ratio;
i *= ratio;
Scalar enter = (b - i) * 0.5f;
if (enter > enters)
{
enters = enter;
}
Scalar leave = enter + i;
if (leave < leaves)
{
leaves = leave;
}
//
//------------------------------------------------------------------------
// If we have pushed the entering distance after the leaving distance, the
// cylinder was missed, otherwise it is hit at the new entering distance
//------------------------------------------------------------------------
//
if (enters > leaves || enters > line->length || leaves < 0.0f)
{
return False;
}
line->length = Max(enters, 0.0f);
return True;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Logical
BoxedXAxisCylinder::TestInstance() const
{
return solidType == XAxisCylinderType;
}
//#############################################################################
//########################## BoxedYAxisCylinder #########################
//#############################################################################
enum {
Min_X_Bit = 0x01,
Max_X_Bit = 0x02,
X_Bits = Min_X_Bit|Max_X_Bit,
Min_Y_Bit = 0x04,
Max_Y_Bit = 0x08,
Y_Bits = Min_Y_Bit|Max_Y_Bit,
Min_Z_Bit = 0x10,
Max_Z_Bit = 0x20,
Z_Bits = Min_Z_Bit|Max_Z_Bit,
X_Axis_Bit = 0x04,
Y_Axis_Bit = 0x02,
Z_Axis_Bit = 0x01
};
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
BoxedYAxisCylinder::BoxedYAxisCylinder(
const ExtentBox &extents,
BoxedSolid::Material material,
Simulation *owner,
BoxedSolid *next_solid
):
BoxedSolid(extents, YAxisCylinderType, material, owner, next_solid)
{
Check_Pointer(this);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
BoxedYAxisCylinder::~BoxedYAxisCylinder()
{
Check_Pointer(this);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Logical
BoxedYAxisCylinder::VerifyCollision(BoxedSolidCollision &collision)
{
Check(this);
Check(&collision);
Verify(minX <= collision.collisionSlice.minX);
Verify(maxX >= collision.collisionSlice.maxX);
Verify(minY <= collision.collisionSlice.minY);
Verify(maxY >= collision.collisionSlice.maxY);
Verify(minZ <= collision.collisionSlice.minZ);
Verify(maxZ >= collision.collisionSlice.maxZ);
Scalar
x,
z,
their_x,
their_z,
radius,
their_radius;
BoxedSolid* solid = collision.GetTreeVolume();
//
//--------------------------------------------------------------------------
// See which type of collision we are going to have to verify, and branch to
// it
//--------------------------------------------------------------------------
//
switch (solid->solidType)
{
//
//-------------------------------------------------------------------------
// When colliding with a ramp or horizontal cylinder, go ahead and have the
// collision slice assume it is from a block. The error thus generated is
// actually very small and not really noticed by anyone
//-------------------------------------------------------------------------
//
case BlockType:
case SphereType:
case ReducibleBlockType:
case RampFacingNegativeZType:
case RampFacingNegativeXType:
case RampFacingPositiveZType:
case RampFacingPositiveXType:
case InvertedRampFacingNegativeZType:
case InvertedRampFacingNegativeXType:
case InvertedRampFacingPositiveZType:
case InvertedRampFacingPositiveXType:
case WedgeFacingNegativeZAndPositiveXType:
case WedgeFacingNegativeZAndNegativeXType:
case WedgeFacingPositiveZAndNegativeXType:
case WedgeFacingPositiveZAndPositiveXType:
case XAxisCylinderType:
case ZAxisCylinderType:
case RightHandedTileType:
case LeftHandedTileType:
return IntersectsBounded(collision.collisionSlice);
//
//--------------------------------------------------------------------------
// When colliding with another upright cylinder, simply calculate the
// distance between the two center points and see if it is less than the sum
// of the two radii
//--------------------------------------------------------------------------
//
case YAxisCylinderType:
x = (maxX + minX) * 0.5f;
z = (maxZ + minZ) * 0.5f;
radius = maxX - x;
their_x = (solid->maxX + solid->minX) * 0.5f;
their_z = (solid->maxZ + solid->minZ) * 0.5f;
their_radius = solid->maxX - their_x;
radius += their_radius;
x -= their_x;
z -= their_z;
return radius*radius >= x*x + z*z;
//
//------------------------------------------------------------------------
// When colliding with a cone, we will base all the calculations on the
// assumption that the collision can be detected identically by a cylinder
// created by the the intersection of the bottom plane of this volume with
// the cone
//------------------------------------------------------------------------
//
case ConeType:
x = (maxX + minX) * 0.5f;
z = (maxZ + minZ) * 0.5f;
radius = maxX - x;
their_x = (solid->maxX + solid->minX) * 0.5f;
their_z = (solid->maxZ + solid->minZ) * 0.5f;
their_radius = solid->maxX - their_x;
their_radius *= (solid->maxY - minY) / (solid->maxY - solid->minY);
radius += their_radius;
x -= their_x;
z -= their_z;
return radius*radius >= x*x + z*z;
//
//------------------------------
// Fail on the unsupported types
//------------------------------
//
default:
#if defined(LAB_ONLY)
DEBUG_STREAM << collision.GetTreeVolume()->solidType << endl;
Fail("Unsupported collision primative\n");
#endif
return False;
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Logical
BoxedYAxisCylinder::ProcessCollision(
BoxedSolidCollision &collision,
const Vector3D &velocity,
BoxedSolidCollisionList *last_collisions,
Normal *normal,
Scalar *penetration
)
{
Check(this);
Check(&collision);
Check(&velocity);
Check_Pointer(normal);
Check_Pointer(penetration);
Scalar
result,
len;
int
i,
ok=0,
axes,
axis,
face,
mask = 0,
opp_face,
temp;
BoxedSolid *solid = collision.GetTreeVolume();
Vector3D
position;
//
//-----------------------------------------------------
// First verify that the collision really should happen
//-----------------------------------------------------
//
if (Small_Enough(velocity.LengthSquared()))
{
return False;
}
if (!VerifyCollision(collision))
{
return False;
}
//
//------------------------------------------------------------------------
// Look at each of the three coordinates of motion, and make sure that the
// collision slice is valid for at least one of them
//------------------------------------------------------------------------
//
for (axis=X_Axis; axis<=Z_Axis; ++axis)
{
//
//-------------------------------------------------------------------
// Make sure we have actual velocity along this axis, then figure out
// which face to test
//-------------------------------------------------------------------
//
(*normal)[axis] = 0.0f;
mask <<= 1;
if (Small_Enough(velocity[axis]))
{
continue;
}
face = (axis<<1) + (velocity[axis]>0.0f);
opp_face = face ^ 1;
//
//----------------------------------------------------------------------
// Find out which faces of the cylinder's bounding box should be
// considered for purposes of the collision. Sides are only considered
// valid if the leading face of the collision slice matches the leading
// face of the disk.
//----------------------------------------------------------------------
//
if (
collision.collisionSlice[face] == (*this)[face] &&
(
(face&1) && (*this)[opp_face] < collision.collisionSlice[opp_face]
||
!(face&1)
&& (*this)[opp_face] > collision.collisionSlice[opp_face]
)
)
mask |= 1;
}
//
//-----------------------------------------------------------------
// Handle the actual collision based upon the type of model and the
// components involved in the collision
//-----------------------------------------------------------------
//
switch (solid->solidType)
{
//
//--------------------------
// Handle the Z facing Ramps
//--------------------------
//
case RampFacingNegativeZType:
normal->y = solid->maxZ - solid->minZ;
normal->z = solid->maxY - solid->minY;
position.y = collision.collisionSlice.minY - solid->maxY;
position.z = collision.collisionSlice.minZ - solid->minZ;
//
//--------------------------------------------------------------------
// The normal y and z values are assumed at this point to point in the
// right direction although their lengths are screwed up
//--------------------------------------------------------------------
//
Compute_X:
len = Sqrt(normal->y*normal->y + normal->z*normal->z);
Verify(!Small_Enough(len));
normal->y /= len;
normal->z /= len;
Check_Fpu();
result = velocity.y*normal->y + velocity.z*normal->z;
if (result > -SMALL)
{
if (mask&X_Axis_Bit)
{
goto X_Only;
}
else
{
break;
}
}
*penetration =
fabs((position.y*normal->y + position.z*normal->z) / result);
Check_Fpu();
//
//-----------------------------------------------------------------------
// If the side of the ramp was involved in the collision, pick either the
// ramp or side to reflect off of based upon which opposes motion more
//-----------------------------------------------------------------------
//
if (mask&X_Axis_Bit)
{
//
//----------------------------------------------------------------
// Compute plane equation and find the distance from the most
// penetrating slice point to the plane. If the ray comes out the
// ramp, we hit that, otherwise we hit the side
//----------------------------------------------------------------
//
result = fabs((solid->maxX - solid->minX) / velocity.x);
Check_Fpu();
if (result < *penetration)
{
X_Only:
normal->x = (velocity.x > 0.0f) ? -1.0f : 1.0f;
normal->y = 0.0f;
normal->z = 0.0f;
*penetration = result;
}
}
goto Reflect;
case RampFacingPositiveZType:
normal->y = solid->maxZ - solid->minZ;
normal->z = solid->minY - solid->maxY;
position.y = collision.collisionSlice.minY - solid->maxY;
position.z = collision.collisionSlice.maxZ - solid->maxZ;
goto Compute_X;
case InvertedRampFacingNegativeZType:
normal->y = solid->minZ - solid->maxZ;
normal->z = solid->maxY - solid->minY;
position.y = collision.collisionSlice.maxY - solid->maxY;
position.z = collision.collisionSlice.minZ - solid->maxZ;
goto Compute_X;
case InvertedRampFacingPositiveZType:
normal->y = solid->minZ - solid->maxZ;
normal->z = solid->minY - solid->maxY;
position.y = collision.collisionSlice.maxY - solid->maxY;
position.z = collision.collisionSlice.maxZ - solid->minZ;
goto Compute_X;
//
//--------------------------
// Handle the X facing ramps
//--------------------------
//
case RampFacingNegativeXType:
normal->x = solid->maxY - solid->minY;
normal->y = solid->maxX - solid->minX;
position.y = collision.collisionSlice.minY - solid->maxY;
position.x = collision.collisionSlice.minX - solid->minX;
//
//--------------------------------------------------------------------
// The normal x and y values are assumed at this point to point in the
// right direction although their lengths are screwed up
//--------------------------------------------------------------------
//
Compute_Z:
len = Sqrt(normal->x*normal->x + normal->y*normal->y);
Verify(!Small_Enough(len));
normal->x /= len;
normal->y /= len;
Check_Fpu();
result = velocity.y*normal->y + velocity.x*normal->x;
if (result > -SMALL)
{
if (mask&Z_Axis_Bit)
{
goto Z_Only;
}
else
{
break;
}
}
*penetration =
fabs((position.y*normal->y + position.x*normal->x) / result);
Check_Fpu();
//
//-----------------------------------------------------------------------
// If the side of the ramp was involved in the collision, pick either the
// ramp or side to reflect off of based upon which opposes motion more
//-----------------------------------------------------------------------
//
if (mask&Z_Axis_Bit)
{
//
//----------------------------------------------------------------
// Compute plane equation and find the distance from the most
// penetrating slice point to the plane. If the ray comes out the
// ramp, we hit that, otherwise we hit the side
//----------------------------------------------------------------
//
result = fabs((solid->maxZ - solid->minZ) / velocity.z);
Check_Fpu();
if (result < *penetration)
{
Z_Only:
normal->x = 0.0f;
normal->y = 0.0f;
normal->z = (velocity.z > 0.0f) ? -1.0f : 1.0f;
*penetration = result;
}
}
goto Reflect;
case RampFacingPositiveXType:
normal->x = solid->minY - solid->maxY;
normal->y = solid->maxX - solid->minX;
position.y = collision.collisionSlice.minY - solid->maxY;
position.x = collision.collisionSlice.maxX - solid->maxX;
goto Compute_Z;
case InvertedRampFacingNegativeXType:
normal->x = solid->maxY - solid->minY;
normal->y = solid->minX - solid->maxX;
position.y = collision.collisionSlice.maxY - solid->maxY;
position.x = collision.collisionSlice.minX - solid->maxX;
goto Compute_Z;
case InvertedRampFacingPositiveXType:
normal->x = solid->minY - solid->maxY;
normal->y = solid->minX - solid->maxX;
position.y = collision.collisionSlice.maxY - solid->maxY;
position.x = collision.collisionSlice.maxX - solid->minX;
goto Compute_Z;
//
//------------------
// Handle the wedges
//------------------
//
case WedgeFacingNegativeZAndPositiveXType:
normal->x = solid->minZ - solid->maxZ;
normal->z = solid->maxX - solid->minX;
position.x = collision.collisionSlice.maxX - solid->minX;
position.z = collision.collisionSlice.minZ - solid->minZ;
//
//--------------------------------------------------------------------
// The normal x and z values are assumed at this point to point in the
// right direction although their lengths are screwed up
//--------------------------------------------------------------------
//
Compute_Y:
len = Sqrt(normal->x*normal->x + normal->z*normal->z);
Verify(!Small_Enough(len));
normal->x /= len;
normal->z /= len;
Check_Fpu();
result = velocity.x*normal->x + velocity.z*normal->z;
if (result > -SMALL)
{
if (mask&Y_Axis_Bit)
{
goto Y_Only;
}
else
{
break;
}
}
*penetration =
fabs((position.x*normal->x + position.z*normal->z) / result);
Check_Fpu();
//
//-----------------------------------------------------------------------
// If the top of the wedge was involved in the collision, pick either the
// side or top to reflect off of based upon which opposes motion more
//-----------------------------------------------------------------------
//
if (mask&Y_Axis_Bit)
{
//
//----------------------------------------------------------------
// Compute plane equation and find the distance from the most
// penetrating slice point to the plane. If the ray comes out the
// ramp, we hit that, otherwise we hit the side
//----------------------------------------------------------------
//
result = fabs((solid->maxY - solid->minY) / velocity.y);
Check_Fpu();
if (result < *penetration)
{
Y_Only:
normal->x = 0.0f;
normal->y = (velocity.y > 0.0f) ? -1.0f : 1.0f;
normal->z = 0.0f;
*penetration = result;
}
}
goto Reflect;
case WedgeFacingNegativeZAndNegativeXType:
normal->x = solid->maxZ - solid->minZ;
normal->z = solid->maxX - solid->minX;
position.x = collision.collisionSlice.minX - solid->maxX;
position.z = collision.collisionSlice.minZ - solid->minZ;
goto Compute_Y;
case WedgeFacingPositiveZAndNegativeXType:
normal->x = solid->maxZ - solid->minZ;
normal->z = solid->minX - solid->maxX;
position.x = collision.collisionSlice.minX - solid->minX;
position.z = collision.collisionSlice.maxZ - solid->minZ;
goto Compute_Y;
case WedgeFacingPositiveZAndPositiveXType:
normal->x = solid->minZ - solid->maxZ;
normal->z = solid->minX - solid->maxX;
position.x = collision.collisionSlice.maxX - solid->maxX;
position.z = collision.collisionSlice.maxZ - solid->minZ;
goto Compute_Y;
//
//----------------------------------------------------------------------
//----------------------------------------------------------------------
//
case XAxisCylinderType:
normal->y = minY + maxY - solid->minY - solid->maxY;
normal->z = minZ + maxZ - solid->minZ - solid->maxZ;
len = normal->y*normal->y + normal->z*normal->z;
if (Small_Enough(len))
{
goto Reverse;
}
len = Sqrt(len);
normal->y /= len;
normal->z /= len;
Check_Fpu();
*penetration = 1.0f;
goto Reflect;
case ZAxisCylinderType:
normal->x = minX + maxX - solid->minX - solid->maxX;
normal->y = minY + maxY - solid->minY - solid->maxY;
len = normal->x*normal->x + normal->y*normal->y;
if (Small_Enough(len))
{
goto Reverse;
}
len = Sqrt(len);
normal->x /= len;
normal->y /= len;
Check_Fpu();
*penetration = 1.0f;
goto Reflect;
case YAxisCylinderType:
normal->x = minX + maxX - solid->minX - solid->maxX;
normal->z = minZ + maxZ - solid->minZ - solid->maxZ;
len = normal->x*normal->x + normal->z*normal->z;
if (Small_Enough(len))
{
goto Reverse;
}
len = Sqrt(len);
normal->x /= len;
normal->z /= len;
Check_Fpu();
*penetration = 1.0f;
if (mask & Y_Axis_Bit)
{
len = (velocity.z > 0.0f) ? -1.0f : 1.0f;
if (len*velocity.y < velocity.x*normal->x + velocity.z*normal->z)
{
normal->x = 0.0f;
normal->y = len;
normal->z = 0.0f;
}
}
goto Reflect;
//
//-------------
// Handle cones
//-------------
//
case ConeType:
normal->x = minX + maxX - solid->minX - solid->maxX;
normal->z = minZ + maxZ - solid->minZ - solid->maxZ;
len = normal->x*normal->x + normal->z*normal->z;
*penetration = 1.0f;
if (Small_Enough(len))
{
goto Reverse;
}
normal->y =
0.5f * (solid->maxX - solid->minX) * Sqrt(len)
/ (solid->maxY - solid->minY);
Check_Fpu();
normal->Normalize(*normal);
goto Reflect;
//
//---------------
// Handle spheres
//---------------
//
case SphereType:
normal->x = minX + maxX - solid->minX - solid->maxX;
normal->y = minY + maxY - solid->minY - solid->maxY;
normal->z = minZ + maxZ - solid->minZ - solid->maxZ;
len = normal->x*normal->x + normal->y*normal->y + normal->z*normal->z;
if (Small_Enough(len))
{
goto Reverse;
}
len = Sqrt(len);
normal->x /= len;
normal->y /= len;
normal->z /= len;
Check_Fpu();
*penetration = 1.0f;
goto Reflect;
//
//---------------------------------
// Handle reflecting off of a block
//---------------------------------
//
case BlockType:
case ReducibleBlockType:
//
//---------------------------------------------------
// Count the number of axes involved in the collision
//---------------------------------------------------
//
temp = mask;
for (axes=0; temp; temp>>=1)
{
if (temp&1)
{
++axes;
}
}
//
//------------------------------------------------------
// Handle as appropriate to the number of sides involved
//------------------------------------------------------
//
switch (axes)
{
//
//---------------------------------------------------------------------
// If no sides were found to be involved in the collision, then we have
// a weird case where the disk has completely penetrated through the
// block
//---------------------------------------------------------------------
//
case 0:
//
//-------------------------------------------------------------------
// This is where we will check the current volume against our list of
// volumes hit last time. If we have not already hit this solid,
// reverse the motion as we have penetrated the object
//-------------------------------------------------------------------
//
i = 0;
if (last_collisions)
{
for (i=0; i<last_collisions->GetCollisionCount(); ++i)
{
if ((*last_collisions)[i].GetTreeVolume() == solid)
{
break;
}
}
}
if (!last_collisions || i == last_collisions->GetCollisionCount())
{
Reverse:
*penetration = 1.0f;
result = -1.0f / velocity.Length();
Check_Fpu();
normal->x = velocity.x*result;
normal->y = velocity.y*result;
normal->z = velocity.z*result;
ok = True;
break;
}
//
//--------------------------------------------------------------------
// Since this is not a fresh hit, check to see if any part of the
// colliding disk sticks out of the slice. If so, then we are heading
// out of the solid in that direction, so let the motion continue.
// WHAT IS THE VELOCITY TRIGGER FOR????
//--------------------------------------------------------------------
//
position.x = (maxX + minX) * 0.5f;
position.y = (maxY + minY) * 0.5f;
position.z = (maxZ + minZ) * 0.5f;
for (face=0; face<6; ++face)
{
i = face >> 1;
if (face&1)
{
if
(
(*this)[face] > collision.collisionSlice[face]
&& position[i] > collision.collisionSlice[face]
&& velocity[i] < 5.0f
)
break;
}
else if
(
(*this)[face] < collision.collisionSlice[face]
&& position[i] < collision.collisionSlice[face]
&& velocity[i] > -5.0f
)
break;
}
if (face != 6)
{
break;
}
//
//-------------------------------------------------------------------
// Otherwise, generate a normal opposing motion towards the center of
// the solid. For each component, if it does not oppose motion, zero
// it out. This will give a better approximation of the brick
//-------------------------------------------------------------------
//
normal->x = position.x - (solid->minX + solid->maxX) * 0.5f;
normal->y = position.y - (solid->minY + solid->maxY) * 0.5f;
normal->z = position.z - (solid->minZ + solid->maxZ) * 0.5f;
for (i=0; i<3; ++i)
{
if ((*normal)[i]*velocity[i] >= 0.0)
{
(*normal)[i] = 0.0;
}
}
//
//----------------------------------------------------------------
// Normalize the vector, making sure we don't get killed by a zero
// case
//----------------------------------------------------------------
//
result = normal->Length();
if (!Small_Enough(result))
{
normal->x /= result;
normal->y /= result;
normal->z /= result;
Check_Fpu();
}
else
{
break;
}
*penetration = 1.0f;
goto Reflect;
//
//-----------------------------------------------------------------
// If only one side is involved in the collision, reflect off of it
//-----------------------------------------------------------------
//
case 1:
Test_Corner:
position.x = 0.5f * (minX + maxX);
position.z = 0.5f * (minZ + maxZ);
switch (mask)
{
case X_Axis_Bit:
face = X_Axis << 1;
//
//----------------------------------
// Figure out the z bounce direction
//----------------------------------
//
if (collision.collisionSlice.minZ > position.z)
{
normal->z = position.z - collision.collisionSlice.minZ;
}
else if (collision.collisionSlice.maxZ < position.z)
{
normal->z = position.z - collision.collisionSlice.maxZ;
}
//
//----------------------------------
// Figure out the x bounce direction
//----------------------------------
//
len = maxX - position.x;
normal->x = len*len - normal->z*normal->z;
Verify(normal->x >= 0.0f);
normal->x = Sqrt(normal->x);
normal->x /= len;
normal->z /= len;
Check_Fpu();
if (velocity.x > 0.0f)
{
normal->x = -normal->x;
}
break;
case Y_Axis_Bit:
face = Y_Axis << 1;
*penetration =
fabs(
(
collision.collisionSlice[face]
- collision.collisionSlice[face^1]
) / velocity[face>>1]
);
Check_Fpu();
goto Pick_Side;
case Z_Axis_Bit:
face = Z_Axis << 1;
//
//----------------------------------
// Figure out the x bounce direction
//----------------------------------
//
if (collision.collisionSlice.minX > position.x)
{
normal->x = position.x - collision.collisionSlice.minX;
}
else if (collision.collisionSlice.maxX < position.x)
{
normal->x = position.x - collision.collisionSlice.maxX;
}
//
//----------------------------------
// Figure out the z bounce direction
//----------------------------------
//
len = maxX - position.x;
normal->z = len*len - normal->x*normal->x;
Verify(normal->z >= 0.0f);
normal->z = Sqrt(normal->z);
normal->x /= len;
normal->z /= len;
Check_Fpu();
if (velocity.z > 0.0f)
{
normal->z = -normal->z;
}
break;
}
*penetration =
fabs(
(
collision.collisionSlice[face]
- collision.collisionSlice[face^1]
) / velocity[face>>1]
);
Check_Fpu();
//
//-------------------------------------------------------------------
// Check to make sure that the normal opposes velocity. If it
// doesn't look to see if this is the first hit on the object, and if
// so, reverse the velocity
//-------------------------------------------------------------------
//
Reflect:
Check(normal);
ok = *normal * velocity < 0.0f;
if (!ok)
{
i = 0;
if (last_collisions)
{
for (i=0; i<last_collisions->GetCollisionCount(); ++i)
{
if ((*last_collisions)[i].GetTreeVolume() == solid)
{
break;
}
}
}
if (!last_collisions || i == last_collisions->GetCollisionCount())
{
goto Reverse;
}
}
break;
//
//---------------------------------------------------------------------
// If two faces were involved, we hit an edge, so figure out which face
// we really hit and mask off the other face
//---------------------------------------------------------------------
//
case 2:
switch (mask)
{
case X_Axis_Bit|Z_Axis_Bit:
position.x =
collision.collisionSlice.maxX - collision.collisionSlice.minX;
position.z =
collision.collisionSlice.maxZ - collision.collisionSlice.minZ;
mask ^=
(fabs(position.z*velocity.x) > fabs(position.x*velocity.z))
? Z_Axis_Bit : X_Axis_Bit;
break;
case X_Axis_Bit|Y_Axis_Bit:
position.x =
collision.collisionSlice.maxX - collision.collisionSlice.minX;
position.y =
collision.collisionSlice.maxY - collision.collisionSlice.minY;
mask ^=
(fabs(position.y*velocity.x) > fabs(position.x*velocity.y))
? Y_Axis_Bit : X_Axis_Bit;
break;
case Y_Axis_Bit|Z_Axis_Bit:
position.y =
collision.collisionSlice.maxY - collision.collisionSlice.minY;
position.z =
collision.collisionSlice.maxZ - collision.collisionSlice.minZ;
mask ^=
(fabs(position.y*velocity.z) > fabs(position.z*velocity.y))
? Y_Axis_Bit : Z_Axis_Bit;
break;
}
goto Test_Corner;
//
//-----------------------------------------------------------------------
// Otherwise, project the intruding vertex back along the velocity vector
// to try and find out which face to bounce off of
//-----------------------------------------------------------------------
//
default:
position.x =
collision.collisionSlice.maxX - collision.collisionSlice.minX;
position.y =
collision.collisionSlice.maxY - collision.collisionSlice.minY;
position.z =
collision.collisionSlice.maxZ - collision.collisionSlice.minZ;
position.x = fabs(position.x/velocity.x);
position.y = fabs(position.y/velocity.y);
position.z = fabs(position.z/velocity.z);
if (position.x < position.y)
{
face = (position.x < position.z) ? (X_Axis<<1) : (Z_Axis<<1);
}
else
{
face = (position.y < position.z) ? (Y_Axis<<1) : (Z_Axis<<1);
}
*penetration = position[face>>1];
Pick_Side:
if (velocity[face>>1] > 0.0f)
{
(*normal)[face>>1] = -1.0f;
}
else
{
(*normal)[face>>1] = 1.0f;
}
goto Reflect;
}
break;
#if defined(LAB_ONLY)
default:
Fail("Unsupported collision type!...\n");
break;
#endif
}
if (ok)
{
Check(normal);
if (*penetration < 0.0f)
{
return False;
}
}
return ok;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Logical
BoxedYAxisCylinder::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 cylinder, and find the
// radius of the cylinder by measuring in the X axis. The Y value will
// automatically be within the cylinder if X & Z are
//---------------------------------------------------------------------
//
Scalar x = (minX + maxX) * 0.5f;
Scalar z = (minZ + maxZ) * 0.5f;
Scalar radius = maxX - x;
//
//-----------------------------------------------------------------------
// Convert the point to the coordinates of the cylinder, putting the
// center point of the cylinder at the origin. Note that we are
// subtracting the point from the center point as opposed to the normal
// way. This will result in both X and Y being negated, but since we are
// squaring them, this will not matter
//-----------------------------------------------------------------------
//
Scalar x2 = x;
Scalar z2 = z;
Clamp(x2, extents.minX, extents.maxX);
Clamp(z2, extents.minZ, extents.maxZ);
x2 -= x;
z2 -= z;
return x2*x2 + z2*z2 <= radius*radius;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Logical
BoxedYAxisCylinder::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 cylinder, and find the
// radius of the cylinder by measuring in the X axis. The Y value will
// automatically be within the cylinder if X & Z are
//---------------------------------------------------------------------
//
Scalar x = (minX + maxX) * 0.5f;
Scalar z = (minZ + maxZ) * 0.5f;
Scalar radius = maxX - x;
//
//-----------------------------------------------------------------------
// Convert the point to the coordinates of the cylinder, putting the
// center point of the cylinder at the origin. Note that we are
// subtracting the point from the center point as opposed to the normal
// way. This will result in both X and Y being negated, but since we are
// squaring them, this will not matter
//-----------------------------------------------------------------------
//
x -= point.x;
z -= point.z;
return x*x + z*z <= radius*radius;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Scalar
BoxedYAxisCylinder::FindDistanceBelowBounded(const Point3D &point)
{
Check(this);
Check(&point);
Verify(minX <= point.x); //z); //GY
Verify(maxX >= point.x); //z); //GY
Verify(minY <= point.y);
Verify(minZ <= point.z);
Verify(maxZ >= point.z);
//
//---------------------------------------------------------------------
// Find the center point in the XZ plane of the cylinder, and find the
// radius of the cylinder by measuring in the X axis. The Y value will
// automatically be within the cylinder if X & Z are
//---------------------------------------------------------------------
//
Scalar x = (minX + maxX) * 0.5f;
Scalar z = (minZ + maxZ) * 0.5f;
Scalar radius = maxX - x;
//
//-----------------------------------------------------------------------
// Convert the point to the coordinates of the cylinder, putting the
// center point of the cylinder at the origin. Note that we are
// subtracting the point from the center point as opposed to the normal
// way. This will result in both X and Y being negated, but since we are
// squaring them, this will not matter
//-----------------------------------------------------------------------
//
x -= point.x;
z -= point.z;
if (x*x + z*z > radius*radius)
{
return -1.0f;
}
Scalar height = point.y - maxY;
return Abs(height);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Logical
BoxedYAxisCylinder::HitByBounded(
Line *line,
Scalar enters,
Scalar leaves
)
{
Check(this);
Check(line);
Verify(enters <= leaves);
Verify(leaves >= 0.0f);
Scalar x = (minX + maxX) * 0.5f;
Scalar z = (minZ + maxZ) * 0.5f;
Scalar radius = maxX - x;
Scalar a =
line->direction.x*line->direction.x
+ line->direction.z*line->direction.z;
//
//--------------------------------------------------------------------------
// If the line is parallel to the cylinder, see if it hits the bottom/top of
// the cylinder in the X-Z plane. If it does, it hits the cylinder when it
// hits the box, otherwise it misses altogether
//--------------------------------------------------------------------------
//
if (Small_Enough(a))
{
x -= line->origin.x;
z -= line->origin.z;
if (x*x + z*z > radius*radius)
{
return False;
}
line->length = Max(enters, 0.0f);
return True;
}
//
//-----------------------------------------------------------------------
// The line is not parallel to the cylinder, so solve the equation giving
// the intersection of the line with the cylinder using the quadratic
// equation
//-----------------------------------------------------------------------
//
x = line->origin.x - x;
z = line->origin.z - z;
Scalar b = 2.0f * (line->direction.x*x + line->direction.z*z);
Scalar c = x*x + z*z - radius*radius;
Scalar i = b*b - 4.0f*a*c;
if (i < SMALL)
{
return False;
}
//
//-------------------------------------------------------------------------
// If the line does hit the cylinder, update the enter/leaving distances to
// take the cylinder surface into account
//-------------------------------------------------------------------------
//
Verify(a > SMALL);
Scalar ratio = Sqrt(a)/(a*a);
i = Sqrt(i);
b *= -ratio;
i *= ratio;
Scalar enter = (b - i) * 0.5f;
if (enter > enters)
{
enters = enter;
}
Scalar leave = enter + i;
if (leave < leaves)
{
leaves = leave;
}
//
//------------------------------------------------------------------------
// If we have pushed the entering distance after the leaving distance, the
// cylinder was missed, otherwise it is hit at the new entering distance
//------------------------------------------------------------------------
//
if (enters > leaves || enters > line->length || leaves < 0.0f)
{
return False;
}
line->length = Max(enters, 0.0f);
return True;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Logical
BoxedYAxisCylinder::TestInstance() const
{
return solidType == YAxisCylinderType;
}
//#############################################################################
//########################## BoxedZAxisCylinder #########################
//#############################################################################
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
BoxedZAxisCylinder::BoxedZAxisCylinder(
const ExtentBox &extents,
BoxedSolid::Material material,
Simulation *owner,
BoxedSolid *next_solid
):
BoxedSolid(extents, ZAxisCylinderType, material, owner, next_solid)
{
Check_Pointer(this);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
BoxedZAxisCylinder::~BoxedZAxisCylinder()
{
Check_Pointer(this);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Logical
BoxedZAxisCylinder::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 cylinder, and find the
// radius of the cylinder by measuring in the X axis. The Y value will
// automatically be within the cylinder if X & Z are
//---------------------------------------------------------------------
//
Scalar x = (minX + maxX) * 0.5f;
Scalar y = (minY + maxY) * 0.5f;
Scalar radius = maxX - x;
//
//-----------------------------------------------------------------------
// Convert the point to the coordinates of the cylinder, putting the
// center point of the cylinder at the origin. Note that we are
// subtracting the point from the center point as opposed to the normal
// way. This will result in both X and Y being negated, but since we are
// squaring them, this will not matter
//-----------------------------------------------------------------------
//
Scalar x2 = x;
Scalar y2 = y;
Clamp(x2, extents.minX, extents.maxX);
Clamp(y2, extents.minY, extents.maxY);
x2 -= x;
y2 -= y;
return x2*x2 + y2*y2 <= radius*radius;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Logical
BoxedZAxisCylinder::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 cylinder, and find the
// radius of the cylinder by measuring in the X axis. The Y value will
// automatically be within the cylinder if X & Z are
//---------------------------------------------------------------------
//
Scalar x = (minX + maxX) * 0.5f;
Scalar y = (minY + maxY) * 0.5f;
Scalar radius = maxX - x;
//
//-----------------------------------------------------------------------
// Convert the point to the coordinates of the cylinder, putting the
// center point of the cylinder at the origin. Note that we are
// subtracting the point from the center point as opposed to the normal
// way. This will result in both X and Y being negated, but since we are
// squaring them, this will not matter
//-----------------------------------------------------------------------
//
x -= point.x;
y -= point.y;
return x*x + y*y <= radius*radius;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Scalar
BoxedZAxisCylinder::FindDistanceBelowBounded(const Point3D &point)
{
Check(this);
Check(&point);
Verify(minX <= point.x);//z); //GY
Verify(maxX >= point.x);//z); //GY
Verify(minY <= point.y);
Verify(minZ <= point.z);
Verify(maxZ >= point.z);
//
//---------------------------------------------------------------------
// Find the center point in the XZ plane of the cylinder, and find the
// radius of the cylinder by measuring in the X axis. The Y value will
// automatically be within the cylinder if X & Z are
//---------------------------------------------------------------------
//
Scalar x = (minX + maxX) * 0.5f;
Scalar radius = maxX - x;
//
//--------------------------------------------------------------------------
// figure out the thickness of cylinder slice where a plane perpendicular to
// X drops through the cylinder and the test point
//--------------------------------------------------------------------------
//
x = point.x - x;
Scalar y = radius - Sqrt(radius*radius - x*x);
if (point.y > maxY - y)
{
return point.y - maxY + y;
}
else if (point.y < minY + y)
{
return -1.0f;
}
else
{
return 0.0f;
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Logical
BoxedZAxisCylinder::HitByBounded(
Line *line,
Scalar enters,
Scalar leaves
)
{
Check(this);
Check(line);
Verify(enters <= leaves);
Verify(leaves >= 0.0f);
Scalar x = (minX + maxX) * 0.5f;
Scalar y = (minY + maxY) * 0.5f;
Scalar radius = maxX - x;
Scalar a =
line->direction.y*line->direction.y
+ line->direction.x*line->direction.x;
//
//--------------------------------------------------------------------------
// If the line is parallel to the cylinder, see if it hits the bottom/top of
// the cylinder in the X-Z plane. If it does, it hits the cylinder when it
// hits the box, otherwise it misses altogether
//--------------------------------------------------------------------------
//
if (Small_Enough(a))
{
y -= line->origin.y;
x -= line->origin.x;
if (y*y + x*x > radius*radius)
{
return False;
}
line->length = Max(enters, 0.0f);
return True;
}
//
//-----------------------------------------------------------------------
// The line is not parallel to the cylinder, so solve the equation giving
// the intersection of the line with the cylinder using the quadratic
// equation
//-----------------------------------------------------------------------
//
y = line->origin.y - y;
x = line->origin.x - x;
Scalar b = 2.0f * (line->direction.y*y + line->direction.x*x);
Scalar c = y*y + x*x - radius*radius;
Scalar i = b*b - 4.0f*a*c;
if (i < SMALL)
{
return False;
}
//
//-------------------------------------------------------------------------
// If the line does hit the cylinder, update the enter/leaving distances to
// take the cylinder surface into account
//-------------------------------------------------------------------------
//
Verify(a > SMALL);
Scalar ratio = Sqrt(a)/(a*a);
i = Sqrt(i);
b *= -ratio;
i *= ratio;
Scalar enter = (b - i) * 0.5f;
if (enter > enters)
{
enters = enter;
}
Scalar leave = enter + i;
if (leave < leaves)
{
leaves = leave;
}
//
//------------------------------------------------------------------------
// If we have pushed the entering distance after the leaving distance, the
// cylinder was missed, otherwise it is hit at the new entering distance
//------------------------------------------------------------------------
//
if (enters > leaves || enters > line->length || leaves < 0.0f)
{
return False;
}
line->length = Max(enters, 0.0f);
return True;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Logical
BoxedZAxisCylinder::TestInstance() const
{
return solidType == ZAxisCylinderType;
}