//=======================================================================// // 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 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; }