Complete disaster-recovery snapshot: engine/game source, game data assets, VC6 toolchain + DX SDKs, build outputs, deployed game, and _UNUSED archive. Large binaries in Git LFS; text preserved byte-for-byte (core.autocrlf=false, no eol attributes). See RECOVERY.md for the one-clone rebuild procedure.
343 lines
11 KiB
C++
343 lines
11 KiB
C++
//=======================================================================//
|
|
// File: line.cpp //
|
|
// Project: Architecture //
|
|
// Author: J.M. Albertson //
|
|
//-----------------------------------------------------------------------//
|
|
// Copyright (C) 1994, Virtual World Entertainments, All Rights reserved //
|
|
// PROPRIETARY AND CONFIDENTIAL //
|
|
//=======================================================================//
|
|
|
|
#include "StuffHeaders.hpp"
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
Line3D&
|
|
Line3D::SetDirection(const Vector3D &vector)
|
|
{
|
|
Check_Pointer(this);
|
|
Check_Object(&vector);
|
|
|
|
//
|
|
//---------------------------------------
|
|
// Make sure m_length of vector is non-zero
|
|
//---------------------------------------
|
|
//
|
|
m_length = vector.GetLength();
|
|
Verify(!Small_Enough(m_length));
|
|
m_length = 1.0f / m_length;
|
|
|
|
//
|
|
//----------------------------------------------
|
|
// Normalize the vector and put it into the line
|
|
//----------------------------------------------
|
|
//
|
|
m_direction.x = vector.x*m_length;
|
|
m_direction.y = vector.y*m_length;
|
|
m_direction.z = vector.z*m_length;
|
|
return *this;
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
Scalar
|
|
Line3D::GetDistanceTo(
|
|
const Plane &plane,
|
|
Scalar *product
|
|
) const
|
|
{
|
|
Check_Object(this);
|
|
Check_Object(&plane);
|
|
Check_Pointer(product);
|
|
|
|
*product = m_direction * plane.normal;
|
|
if (Small_Enough(*product))
|
|
return -1.0f;
|
|
Scalar result = -plane.GetDistanceTo(m_origin) / *product;
|
|
return result;
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
Scalar
|
|
Line3D::GetDistanceTo(
|
|
const Sphere &sphere,
|
|
Scalar *penetration
|
|
) const
|
|
{
|
|
Check_Object(this);
|
|
Check_Object(&sphere);
|
|
Check_Pointer(penetration);
|
|
|
|
//
|
|
//-------------------------------------------------------------------
|
|
// Determine if ray intersects bounding sphere of object. If sphere
|
|
// is (X-C)*(X-C) = R^2 and ray is X = t*D+L for t >= 0, then
|
|
// intersection is obtained by plugging X into sphere equation to
|
|
// get quadratic: (D*D)t^2 + 2*(D*(L-C))t + (L-C)*(L-C) = 0
|
|
// Define a = D*D = 1.0f, b = 2*(D*(L-C)), and c = (L-C)*(L-C).
|
|
//-------------------------------------------------------------------
|
|
//
|
|
Vector3D diff;
|
|
diff.Subtract(m_origin, sphere.center);
|
|
Scalar b = (m_direction*diff) * 2.0f;
|
|
Scalar c = (diff*diff) - sphere.radius*sphere.radius;
|
|
|
|
//
|
|
//-------------------------------------------------------------------------
|
|
// If penetration is negative, we couldn't hit the sphere at all. If it is
|
|
// really small, it touches at only one place
|
|
//-------------------------------------------------------------------------
|
|
//
|
|
*penetration = b*b - 4.0f*c;
|
|
if (*penetration < -SMALL)
|
|
return -1.0f;
|
|
b *= -0.5f;
|
|
if (*penetration<SMALL)
|
|
{
|
|
*penetration = 0.0f;
|
|
Min_Clamp(b, 0.0f);
|
|
return (b > m_length) ? -1.0f : b;
|
|
}
|
|
|
|
//
|
|
//-------------------------------------------------------------
|
|
// We know we hit the sphere, so figure out where it first hits
|
|
//-------------------------------------------------------------
|
|
//
|
|
*penetration = 0.5f * Sqrt(*penetration);
|
|
if (b + *penetration < -SMALL)
|
|
return -1.0f;
|
|
b -= *penetration;
|
|
if (b > m_length)
|
|
return -1.0f;
|
|
Min_Clamp(b, 0.0f);
|
|
return b;
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
Scalar
|
|
Line3D::GetDistanceTo(const OBB& box)
|
|
{
|
|
Check_Object(this);
|
|
Check_Object(&box);
|
|
|
|
//
|
|
//------------------------------------------------------------------------
|
|
// Get the vector from the line to the centerpoint of the OBB. All planes
|
|
// will be generated relative to this
|
|
//------------------------------------------------------------------------
|
|
//
|
|
Point3D center;
|
|
center = box.localToParent;
|
|
Vector3D delta;
|
|
delta.Subtract(center, m_origin);
|
|
|
|
//
|
|
//--------------------------------------------------
|
|
// Set up the loop to examine each of the three axes
|
|
//--------------------------------------------------
|
|
//
|
|
Scalar enters = -100.0f - m_length;
|
|
Scalar leaves = m_length + 100.0f;
|
|
for (int axis=X_Axis; axis <= Z_Axis; ++axis)
|
|
{
|
|
UnitVector3D
|
|
normal(
|
|
box.localToParent(axis, X_Axis),
|
|
box.localToParent(axis, Y_Axis),
|
|
box.localToParent(axis, Z_Axis)
|
|
);
|
|
|
|
//
|
|
//----------------------------------------------------------------------
|
|
// Now, we have to calculate how far the line moves along the normal per
|
|
// unit traveled down the line. If it is perpendicular to the normal,
|
|
// then it will hit or miss based solely upon the m_origin location
|
|
//----------------------------------------------------------------------
|
|
//
|
|
Scalar drift = m_direction * normal;
|
|
Scalar distance;
|
|
if (Small_Enough(drift))
|
|
{
|
|
distance = delta * normal;
|
|
if (Fabs(distance) > box.axisExtents[axis])
|
|
return -1.0f;
|
|
else
|
|
continue;
|
|
}
|
|
|
|
//
|
|
//--------------------------------------------------------------------
|
|
// We know the line is not parallel, so we will now calculate how long
|
|
// the line will stay inside the box. We also will calculate how far
|
|
// from the m_origin to the centerplane of the OBB
|
|
//--------------------------------------------------------------------
|
|
//
|
|
drift = 1.0f / drift;
|
|
Scalar span = box.axisExtents[axis] * Fabs(drift);
|
|
distance = (delta * normal) * drift;
|
|
|
|
//
|
|
//--------------------------------------------------------------------
|
|
// Now adjust where the line can enter and leave the OBB, and if it is
|
|
// no longer possible to hit, stop checking
|
|
//--------------------------------------------------------------------
|
|
//
|
|
Scalar enter = distance - span;
|
|
Scalar leave = distance + span;
|
|
if (enter > enters)
|
|
enters = enter;
|
|
if (leave < leaves)
|
|
leaves = leave;
|
|
if (enters > leaves)
|
|
return -1.0f;
|
|
}
|
|
|
|
//
|
|
//-------------------------------------------------------------------------
|
|
// If we got here, then the line in theory can hit the OBB, so now we check
|
|
// to make sure it hits it within the allowed span of the line
|
|
//-------------------------------------------------------------------------
|
|
//
|
|
if (leaves < 0.0f || enters > m_length)
|
|
return -1.0f;
|
|
Min_Clamp(enters, 0.0f);
|
|
return enters;
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
Scalar
|
|
Line3D::GetDistanceTo(
|
|
const OBB& box,
|
|
int *first_axis
|
|
)
|
|
{
|
|
Check_Object(this);
|
|
Check_Object(&box);
|
|
Check_Pointer(first_axis);
|
|
|
|
//
|
|
//------------------------------------------------------------------------
|
|
// Get the vector from the line to the centerpoint of the OBB. All planes
|
|
// will be generated relative to this
|
|
//------------------------------------------------------------------------
|
|
//
|
|
Point3D center;
|
|
center = box.localToParent;
|
|
Vector3D delta;
|
|
delta.Subtract(center, m_origin);
|
|
|
|
//
|
|
//--------------------------------------------------
|
|
// Set up the loop to examine each of the three axes
|
|
//--------------------------------------------------
|
|
//
|
|
Scalar enters = -100.0f - m_length;
|
|
Scalar leaves = m_length + 100.0f;
|
|
for (int axis=X_Axis; axis <= Z_Axis; ++axis)
|
|
{
|
|
UnitVector3D
|
|
normal(
|
|
box.localToParent(axis, X_Axis),
|
|
box.localToParent(axis, Y_Axis),
|
|
box.localToParent(axis, Z_Axis)
|
|
);
|
|
|
|
//
|
|
//----------------------------------------------------------------------
|
|
// Now, we have to calculate how far the line moves along the normal per
|
|
// unit traveled down the line. If it is perpendicular to the normal,
|
|
// then it will hit or miss based solely upon the m_origin location
|
|
//----------------------------------------------------------------------
|
|
//
|
|
Scalar drift = m_direction * normal;
|
|
Scalar distance;
|
|
if (Small_Enough(drift))
|
|
{
|
|
distance = delta * normal;
|
|
if (Fabs(distance) > box.axisExtents[axis])
|
|
return -1.0f;
|
|
else
|
|
continue;
|
|
}
|
|
|
|
//
|
|
//--------------------------------------------------------------------
|
|
// We know the line is not parallel, so we will now calculate how long
|
|
// the line will stay inside the box. We also will calculate how far
|
|
// from the m_origin to the centerplane of the OBB
|
|
//--------------------------------------------------------------------
|
|
//
|
|
drift = 1.0f / drift;
|
|
Scalar span = box.axisExtents[axis] * Fabs(drift);
|
|
distance = (delta * normal) * drift;
|
|
|
|
//
|
|
//--------------------------------------------------------------------
|
|
// Now adjust where the line can enter and leave the OBB, and if it is
|
|
// no longer possible to hit, stop checking
|
|
//--------------------------------------------------------------------
|
|
//
|
|
Scalar enter = distance - span;
|
|
Scalar leave = distance + span;
|
|
if (enter > enters)
|
|
{
|
|
*first_axis = axis;
|
|
enters = enter;
|
|
}
|
|
if (leave < leaves)
|
|
leaves = leave;
|
|
if (enters > leaves)
|
|
return -1.0f;
|
|
}
|
|
|
|
//
|
|
//-------------------------------------------------------------------------
|
|
// If we got here, then the line in theory can hit the OBB, so now we check
|
|
// to make sure it hits it within the allowed span of the line
|
|
//-------------------------------------------------------------------------
|
|
//
|
|
if (leaves < 0.0f || enters > m_length)
|
|
return -1.0f;
|
|
Min_Clamp(enters, 0.0f);
|
|
return enters;
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
Scalar
|
|
Stuff::Find_Time_Till_Closest_Approach(
|
|
const Point3D& origin1,
|
|
const Vector3D& velocity1,
|
|
const Point3D& origin2,
|
|
const Vector3D& velocity2
|
|
)
|
|
{
|
|
Vector3D a,b;
|
|
a.Subtract(origin1, origin2);
|
|
b.Subtract(velocity1, velocity2);
|
|
|
|
//
|
|
//--------------------------------------------------------------------
|
|
// If the velocities are identical, any point will do for the test, so
|
|
// return time zero
|
|
//--------------------------------------------------------------------
|
|
//
|
|
Scalar d = b.GetLengthSquared();
|
|
if (Small_Enough(d))
|
|
return 0.0f;
|
|
|
|
//
|
|
//-------------------------------------------------------------------------
|
|
// The equation representing the difference in the lines is a+bt. If we dot
|
|
// this equation with itself, we get a function representing the squared
|
|
// distances between the lines = aa + 2tab + ttbb. The derivative of this
|
|
// function with respect to t is 2ab + 2tbb. The closest approach is when
|
|
// the derivative is zero, or when t = -a*b / b*b
|
|
//-------------------------------------------------------------------------
|
|
//
|
|
return (a * b) / -d;
|
|
}
|