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.
400 lines
11 KiB
C++
400 lines
11 KiB
C++
//===========================================================================//
|
|
// File: plane.cc //
|
|
// Project: MUNGA Brick: Math Library //
|
|
// Contents: Implementation details for the plane class //
|
|
//---------------------------------------------------------------------------//
|
|
// Date Who Modification //
|
|
// -------- --- ---------------------------------------------------------- //
|
|
// 12/01/94 JMA Initial coding. //
|
|
//---------------------------------------------------------------------------//
|
|
// Copyright (C) 1994-1995, Virtual World Entertainment, Inc. //
|
|
// All Rights reserved worldwide //
|
|
// This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL //
|
|
//===========================================================================//
|
|
|
|
#include "StuffHeaders.hpp"
|
|
|
|
//
|
|
//#############################################################################
|
|
//#############################################################################
|
|
//
|
|
void
|
|
Plane::BuildPlane(
|
|
const Point3D& p0,
|
|
const Point3D& p1,
|
|
const Point3D& p2
|
|
)
|
|
{
|
|
Vector3D v1,v2;
|
|
v1.Subtract(p1, p0);
|
|
v2.Subtract(p2, p1);
|
|
|
|
Vector3D axis;
|
|
axis.Cross(v1, v2);
|
|
Verify(!Small_Enough(axis.GetLength()));
|
|
normal = axis;
|
|
offset = normal * p0;
|
|
#if defined(_ARMOR)
|
|
Scalar offset2 = normal * p1;
|
|
Scalar offset3 = normal * p2;
|
|
|
|
//
|
|
//----------------------------------------------------------------
|
|
// occasionaly the offset becomes large and the triangle is small
|
|
// this way we still maintain proof up to 1e-4 of the offset value
|
|
//----------------------------------------------------------------
|
|
//
|
|
Verify(Close_Enough(
|
|
offset,
|
|
offset2,
|
|
Fabs(offset+offset2) > 200.0f
|
|
? SMALL*Fabs(offset+offset2)/200.0f
|
|
: SMALL
|
|
));
|
|
Verify(Close_Enough(
|
|
offset,
|
|
offset3,
|
|
Fabs(offset+offset3) > 200.0f
|
|
? SMALL*Fabs(offset+offset3)/200.0f
|
|
: SMALL
|
|
));
|
|
#endif
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
//#############################################################################
|
|
//
|
|
Plane&
|
|
Plane::Multiply(
|
|
const Plane &p,
|
|
const LinearMatrix4D &m
|
|
)
|
|
{
|
|
Check_Pointer(this);
|
|
Check_Object(&p);
|
|
Check_Object(&m);
|
|
Verify(this != &p);
|
|
|
|
normal.x = p.normal.x*m(0,0) + p.normal.y*m(1,0) + p.normal.z*m(2,0);
|
|
normal.y = p.normal.x*m(0,1) + p.normal.y*m(1,1) + p.normal.z*m(2,1);
|
|
normal.z = p.normal.x*m(0,2) + p.normal.y*m(1,2) + p.normal.z*m(2,2);
|
|
offset =
|
|
normal.x*m(3,0) + normal.y*m(3,1) + normal.z*m(3,2) + p.offset;
|
|
|
|
return *this;
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
//#############################################################################
|
|
//
|
|
Scalar
|
|
Plane::GetDistanceTo(const Sphere& sphere) const
|
|
{
|
|
Check_Object(this);
|
|
Check_Object(&sphere);
|
|
|
|
//
|
|
//-----------------------------------------------------------------------
|
|
// Find out how far the centerpoint of the sphere is from us, then adjust
|
|
// so that 0.0 means we are intersecting the sphere
|
|
//-----------------------------------------------------------------------
|
|
//
|
|
Scalar distance = GetDistanceTo(sphere.center);
|
|
if (distance > sphere.radius)
|
|
return distance - sphere.radius;
|
|
else if (distance < -sphere.radius)
|
|
return distance + sphere.radius;
|
|
return 0.0f;
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
//#############################################################################
|
|
//
|
|
Scalar
|
|
Plane::GetDistanceTo(const OBB& box) const
|
|
{
|
|
Check_Object(this);
|
|
Check_Object(&box);
|
|
|
|
//
|
|
//-------------------------------------------------
|
|
// Rotate the plane into the local space of the box
|
|
//-------------------------------------------------
|
|
//
|
|
Plane local_plane;
|
|
LinearMatrix4D parent_to_local;
|
|
parent_to_local.Invert(box.localToParent);
|
|
local_plane.Multiply(*this, parent_to_local);
|
|
Point3D inside_extents(box.axisExtents);
|
|
Point3D outside_extents(box.axisExtents);
|
|
|
|
//
|
|
//---------------------------------------------------------------------
|
|
// Look at the resulting vector to find out which corners of the box we
|
|
// should be testing
|
|
//---------------------------------------------------------------------
|
|
//
|
|
if (local_plane.normal.x > 0.0f)
|
|
inside_extents.x = -inside_extents.x;
|
|
else
|
|
outside_extents.x = -outside_extents.x;
|
|
if (local_plane.normal.y > 0.0f)
|
|
inside_extents.y = -inside_extents.y;
|
|
else
|
|
outside_extents.y = -outside_extents.y;
|
|
if (local_plane.normal.z > 0.0f)
|
|
inside_extents.z = -inside_extents.z;
|
|
else
|
|
outside_extents.z = -outside_extents.z;
|
|
|
|
//
|
|
//---------------------------
|
|
// Test against the plane now
|
|
//---------------------------
|
|
//
|
|
Scalar distance = local_plane.GetDistanceTo(outside_extents);
|
|
if (distance < 0.0f)
|
|
return distance;
|
|
distance = local_plane.GetDistanceTo(inside_extents);
|
|
if (distance > 0.0f)
|
|
return distance;
|
|
return 0.0f;
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
//#############################################################################
|
|
//
|
|
bool
|
|
Plane::ContainsSomeOf(
|
|
const Sphere &sphere,
|
|
Scalar thickness
|
|
) const
|
|
{
|
|
return normal*sphere.center - offset <= sphere.radius - thickness;
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
//#############################################################################
|
|
//
|
|
bool
|
|
Plane::ContainsAllOf(
|
|
const Sphere &sphere,
|
|
Scalar thickness
|
|
) const
|
|
{
|
|
return offset - normal*sphere.center >= sphere.radius + thickness;
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
//#############################################################################
|
|
//
|
|
bool
|
|
Plane::ContainsSomeOf(
|
|
const ExtentBox &box,
|
|
Scalar thickness
|
|
) const
|
|
{
|
|
Check_Object(this);
|
|
Check_Object(&box);
|
|
|
|
Point3D test;
|
|
test.x = (normal.x > 0.0f) ? box.minX : box.maxX;
|
|
test.y = (normal.y > 0.0f) ? box.minY : box.maxY;
|
|
test.z = (normal.z > 0.0f) ? box.minZ : box.maxZ;
|
|
return normal * test < offset - thickness;
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
//#############################################################################
|
|
//
|
|
bool
|
|
Plane::ContainsAllOf(
|
|
const ExtentBox &box,
|
|
Scalar thickness
|
|
) const
|
|
{
|
|
Check_Object(this);
|
|
Check_Object(&box);
|
|
|
|
Point3D test;
|
|
test.x = (normal.x < 0.0f) ? box.minX : box.maxX;
|
|
test.y = (normal.y < 0.0f) ? box.minY : box.maxY;
|
|
test.z = (normal.z < 0.0f) ? box.minZ : box.maxZ;
|
|
return normal * test <= offset + thickness;
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
//#############################################################################
|
|
//
|
|
bool
|
|
Plane::Intersects(
|
|
const Sphere &sphere,
|
|
Scalar thickness
|
|
) const
|
|
{
|
|
Scalar dist = normal*sphere.center - offset;
|
|
return Abs(dist) <= sphere.radius + thickness;
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
//#############################################################################
|
|
//
|
|
bool
|
|
Plane::Intersects(
|
|
const ExtentBox &box,
|
|
Scalar thickness
|
|
) const
|
|
{
|
|
if (!ContainsSomeOf(box, thickness))
|
|
{
|
|
return false;
|
|
}
|
|
STOP(("What is this????"));
|
|
Plane inverse(-normal.x, -normal.y, -normal.z, -offset);
|
|
return false;
|
|
// return inverse.ContainsSomeOf(box);
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
//#############################################################################
|
|
//
|
|
#if !defined(Spew)
|
|
void
|
|
Spew(
|
|
const char* group,
|
|
const Plane& plane
|
|
)
|
|
{
|
|
Check_Object(&plane);
|
|
SPEW((group, "\n\tPlane Normal: +"));
|
|
Spew(group, plane.normal);
|
|
SPEW((group, "\tOffset: %f+", plane.offset));
|
|
}
|
|
#endif
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
bool
|
|
Plane::ComputeBestDividingPlane(ReadOnlyArrayOf<Point3D> &points)
|
|
{
|
|
Check_Object(&points);
|
|
|
|
//
|
|
//-----------------
|
|
// Compute the sums
|
|
//-----------------
|
|
//
|
|
unsigned count = points.GetLength();
|
|
Verify(count > 1);
|
|
Point3D sum = Point3D::Identity;
|
|
Scalar xx = 0.0f;
|
|
Scalar xy = 0.0f;
|
|
Scalar xz = 0.0f;
|
|
Scalar yy = 0.0f;
|
|
Scalar yz = 0.0f;
|
|
Scalar zz = 0.0f;
|
|
unsigned i;
|
|
for (i=0; i<count; i++)
|
|
{
|
|
sum += points[i];
|
|
xx += points[i].x*points[i].x;
|
|
xy += points[i].x*points[i].y;
|
|
xz += points[i].x*points[i].z;
|
|
yy += points[i].y*points[i].y;
|
|
yz += points[i].y*points[i].z;
|
|
zz += points[i].z*points[i].z;
|
|
}
|
|
|
|
//
|
|
//--------------------------------------
|
|
// Compute the least squares differences
|
|
//--------------------------------------
|
|
//
|
|
Scalar t = 1.0f / static_cast<Scalar>(count);
|
|
xx -= t*sum.x*sum.x;
|
|
xy -= t*sum.x*sum.y;
|
|
xz -= t*sum.x*sum.z;
|
|
yy -= t*sum.y*sum.y;
|
|
yz -= t*sum.y*sum.z;
|
|
zz -= t*sum.z*sum.z;
|
|
|
|
//
|
|
//------------------------------------------------------------------
|
|
// Find the largest axis component, and use that to orient the plane
|
|
//------------------------------------------------------------------
|
|
//
|
|
int axis;
|
|
if (xx > yy)
|
|
axis = (xx > zz) ? X_Axis : Z_Axis;
|
|
else
|
|
axis = (yy > zz) ? Y_Axis : Z_Axis;
|
|
Vector3D direction;
|
|
switch (axis)
|
|
{
|
|
case X_Axis:
|
|
if (xx < SMALL)
|
|
return false;
|
|
direction.x = 1.0f;
|
|
direction.y = xy / xx;
|
|
direction.z = xz / xx;
|
|
break;
|
|
|
|
case Y_Axis:
|
|
if (yy < SMALL)
|
|
return false;
|
|
Verify(yy > SMALL);
|
|
direction.x = xy / yy;
|
|
direction.y = 1.0f;
|
|
direction.z = yz / yy;
|
|
break;
|
|
|
|
case Z_Axis:
|
|
if (zz < SMALL)
|
|
return false;
|
|
direction.x = xz / zz;
|
|
direction.y = yz / zz;
|
|
direction.z = 1.0f;
|
|
break;
|
|
|
|
default:
|
|
direction = Vector3D::Identity;
|
|
STOP(("Bad switch"));
|
|
}
|
|
|
|
|
|
//
|
|
//-----------------------------------------------------------------------
|
|
// The dividing plane will coincide with the mean point and have a normal
|
|
// parallel to the variance vector
|
|
//-----------------------------------------------------------------------
|
|
//
|
|
normal = direction;
|
|
sum *= t;
|
|
offset = normal * sum;
|
|
Check_Object(this);
|
|
|
|
//
|
|
//-----------------------------------------------------------------------
|
|
// Now make sure that a coincident set didn't get through due to rounding
|
|
// errors
|
|
//-----------------------------------------------------------------------
|
|
//
|
|
for (i=0; i<count; i++)
|
|
{
|
|
if (points[i] != sum)
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|