343 lines
6.9 KiB
C++
343 lines
6.9 KiB
C++
/* -----------------------------------------------------------------------------
|
|
GSFramework
|
|
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
|
----------------------------------------------------------------------------- */
|
|
|
|
|
|
#include <float.h>
|
|
#include "geometry/bounding_box.h"
|
|
#include "metafile/nml.h"
|
|
#include "math/matrix4.h"
|
|
|
|
using namespace GS;
|
|
using namespace GS::NML;
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
void OBB::Transform(const Matrix4 &mtx)
|
|
{
|
|
Matrix3 rmtx = Matrix3::FromMatrix4(mtx);
|
|
bb_rotation = rmtx * bb_rotation;
|
|
bb_position = bb_position * rmtx + mtx.GetRow(3);
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
|
|
//------------------------------------------------------------------------------
|
|
void OBB::ComputeMinMax(MinMax &minmax)
|
|
{
|
|
Vector4 xtd(bb_scale * 0.5f);
|
|
Vector4 smt[4];
|
|
|
|
smt[0].Set(xtd.x, xtd.y, xtd.z);
|
|
smt[1].Set(-xtd.x, xtd.y, xtd.z);
|
|
smt[2].Set(xtd.x, -xtd.y, xtd.z);
|
|
smt[3].Set(xtd.x, xtd.y, -xtd.z);
|
|
|
|
int n;
|
|
for (n = 0; n < 4; n++)
|
|
smt[n] = (smt[n] * bb_rotation).Abs();
|
|
|
|
minmax.mx = smt[0];
|
|
for (n = 1; n < 4; n++)
|
|
{
|
|
if (smt[n].x > minmax.mx.x) minmax.mx.x = smt[n].x;
|
|
if (smt[n].y > minmax.mx.y) minmax.mx.y = smt[n].y;
|
|
if (smt[n].z > minmax.mx.z) minmax.mx.z = smt[n].z;
|
|
}
|
|
minmax.mn.x = -minmax.mx.x;
|
|
minmax.mn.y = -minmax.mx.y;
|
|
minmax.mn.z = -minmax.mx.z;
|
|
|
|
minmax.mn += bb_position;
|
|
minmax.mx += bb_position;
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
|
|
//------------------------------------------------------------------------------
|
|
bool OBB::FromMetaTag(Tag &tag)
|
|
{
|
|
Tag *t;
|
|
List <Tag *> ::Iterator i(tag.GetTags().GetRoot());
|
|
|
|
t = i.ObjectPtr();
|
|
if (!t) return false;
|
|
bb_position.FromMetaTag(*t);
|
|
++i;
|
|
|
|
t = i.ObjectPtr();
|
|
if (!t) return false;
|
|
bb_scale.FromMetaTag(*t);
|
|
++i;
|
|
|
|
t = i.ObjectPtr();
|
|
if (!t) return false;
|
|
bb_rotation.FromMetaTag(*t);
|
|
return true;
|
|
}
|
|
Tag *OBB::AsMetaTag()
|
|
{
|
|
Tag *root = new Tag("OBB");
|
|
if (root)
|
|
{
|
|
root->AddChild(bb_position.AsMetaTag("Position"));
|
|
root->AddChild(bb_scale.AsMetaTag("Scale"));
|
|
root->AddChild(bb_rotation.AsMetaTag("Matrix"));
|
|
}
|
|
return root;
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
|
|
//------------------------------------------------------------------------------
|
|
bool MinMax::IntersectRay(const Vector4 &o, const Vector4 &d, float &tmin, float &tmax)
|
|
{
|
|
tmin = 0;
|
|
tmax = FLT_MAX;
|
|
|
|
for (uint n = 0; n < 3; ++n)
|
|
if (Math::EqualZero(d[n]))
|
|
{
|
|
if ((o[n] < mn[n]) || (o[n] > mx[n]))
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
float ood = 1.f / d[n];
|
|
float t0 = (mn[n] - o[n]) * ood;
|
|
float t1 = (mx[n] - o[n]) * ood;
|
|
|
|
if (t0 > t1)
|
|
{ float swp = t1; t1 = t0; t0 = swp; }
|
|
|
|
tmin = tmin < t0 ? t0 : tmin;
|
|
tmax = tmax < t1 ? tmax : t1;
|
|
|
|
if (tmin > tmax)
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
bool MinMax::ClassifyLine(const Vector4 &p1, const Vector4 &direction, Vector4 &itr, Vector4 *n) const
|
|
{
|
|
uint oc1, oc2;
|
|
|
|
oc1 = cc_oc(mn, mx, p1);
|
|
if (oc1 == ClipNone)
|
|
{
|
|
// Point inside bounding box.
|
|
if (n)
|
|
n->Set(0, 0, 0);
|
|
itr = p1;
|
|
return true;
|
|
}
|
|
|
|
oc2 = ss_oc(direction);
|
|
|
|
// Same side.
|
|
if ((oc1 & oc2) > ClipNone)
|
|
return false;
|
|
|
|
// Check intersections.
|
|
if (oc1 & (ClipRight | ClipLeft))
|
|
{
|
|
if (oc1 & ClipRight)
|
|
{
|
|
if (n)
|
|
n->Set(1, 0, 0);
|
|
itr.x = mx.x;
|
|
}
|
|
else
|
|
{
|
|
if (n)
|
|
n->Set(-1, 0, 0);
|
|
itr.x = mn.x;
|
|
}
|
|
float x1 = direction.x;
|
|
float x2 = itr.x - p1.x;
|
|
itr.y = p1.y + x2 * direction.y / x1;
|
|
itr.z = p1.z + x2 * direction.z / x1;
|
|
|
|
if ((itr.y <= mx.y) && (itr.y >= mn.y) && (itr.z <= mx.z) && (itr.z >= mn.z))
|
|
return true;
|
|
}
|
|
if (oc1 & (ClipTop | ClipBottom))
|
|
{
|
|
if (oc1 & ClipTop)
|
|
{
|
|
if (n)
|
|
n->Set(0, 1, 0);
|
|
itr.y = mx.y;
|
|
}
|
|
else
|
|
{
|
|
if (n)
|
|
n->Set(0, -1, 0);
|
|
itr.y = mn.y;
|
|
}
|
|
float y1 = direction.y;
|
|
float y2 = itr.y - p1.y;
|
|
itr.x = p1.x + y2 * direction.x / y1;
|
|
itr.z = p1.z + y2 * direction.z / y1;
|
|
|
|
if ((itr.x <= mx.x) && (itr.x >= mn.x) && (itr.z <= mx.z) && (itr.z >= mn.z))
|
|
return true;
|
|
}
|
|
if (oc1 & (ClipFront | ClipBack))
|
|
{
|
|
if (oc1 & ClipBack)
|
|
{
|
|
if (n)
|
|
n->Set(0, 0, 1);
|
|
itr.z = mx.z;
|
|
}
|
|
else
|
|
{
|
|
if (n)
|
|
n->Set(0, 0, -1);
|
|
itr.z = mn.z;
|
|
}
|
|
float z1 = direction.z;
|
|
float z2 = itr.z - p1.z;
|
|
itr.x = p1.x + z2 * direction.x / z1;
|
|
itr.y = p1.y + z2 * direction.y / z1;
|
|
|
|
if ((itr.x <= mx.x) && (itr.x >= mn.x) && (itr.y <= mx.y) && (itr.y >= mn.y))
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
bool MinMax::ClassifySegment(const Vector4 &p1, const Vector4 &p2, Vector4 &itr, Vector4 *n) const
|
|
{
|
|
uint oc1, oc2;
|
|
|
|
oc1 = cc_oc(mn, mx, p1);
|
|
if (oc1 == ClipNone)
|
|
{
|
|
// Point inside bounding box.
|
|
if (n)
|
|
n->Set(0, 0, 0);
|
|
itr = p1;
|
|
return true;
|
|
}
|
|
|
|
oc2 = cc_oc(mn, mx, p2);
|
|
if (oc2 == ClipNone)
|
|
{
|
|
// point inside bounding box
|
|
itr = p2;
|
|
return true;
|
|
}
|
|
|
|
// Same side.
|
|
if ((oc1 & oc2) > ClipNone)
|
|
return false;
|
|
|
|
// Check intersections.
|
|
if (oc1 & (ClipRight | ClipLeft))
|
|
{
|
|
if (oc1 & ClipRight)
|
|
{
|
|
if (n)
|
|
n->Set(1, 0, 0);
|
|
itr.x = mx.x;
|
|
}
|
|
else
|
|
{
|
|
if (n)
|
|
n->Set(-1, 0, 0);
|
|
itr.x = mn.x;
|
|
}
|
|
|
|
float x1 = p2.x - p1.x;
|
|
float x2 = itr.x - p1.x;
|
|
itr.y = p1.y + x2 * (p2.y - p1.y) / x1;
|
|
itr.z = p1.z + x2 * (p2.z - p1.z) / x1;
|
|
|
|
if ( (itr.y <= mx.y) &&
|
|
(itr.y >= mn.y) &&
|
|
(itr.z <= mx.z) &&
|
|
(itr.z >= mn.z) )
|
|
return true;
|
|
}
|
|
if (oc1 & (ClipTop | ClipBottom))
|
|
{
|
|
if (oc1 & ClipTop)
|
|
{
|
|
if (n)
|
|
n->Set(0, 1, 0);
|
|
itr.y = mx.y;
|
|
}
|
|
else
|
|
{
|
|
if (n)
|
|
n->Set(0, -1, 0);
|
|
itr.y = mn.y;
|
|
}
|
|
float y1 = p2.y - p1.y;
|
|
float y2 = itr.y - p1.y;
|
|
itr.x = p1.x + y2 * (p2.x - p1.x) / y1;
|
|
itr.z = p1.z + y2 * (p2.z - p1.z) / y1;
|
|
|
|
if ( (itr.x <= mx.x) &&
|
|
(itr.x >= mn.x) &&
|
|
(itr.z <= mx.z) &&
|
|
(itr.z >= mn.z) )
|
|
return true;
|
|
}
|
|
if (oc1 & (ClipFront | ClipBack))
|
|
{
|
|
if (oc1 & ClipBack)
|
|
{
|
|
if (n)
|
|
n->Set(0, 0, 1);
|
|
itr.z = mx.z;
|
|
}
|
|
else
|
|
{
|
|
if (n)
|
|
n->Set(0, 0, -1);
|
|
itr.z = mn.z;
|
|
}
|
|
float z1 = p2.z - p1.z;
|
|
float z2 = itr.z - p1.z;
|
|
itr.x = p1.x + z2 * (p2.x - p1.x) / z1;
|
|
itr.y = p1.y + z2 * (p2.y - p1.y) / z1;
|
|
|
|
if ( (itr.x <= mx.x) &&
|
|
(itr.x >= mn.x) &&
|
|
(itr.y <= mx.y) &&
|
|
(itr.y >= mn.y) )
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
|
|
//------------------------------------------------------------------------------
|
|
bool MinMax::FromMetaTag(Tag &tag)
|
|
{
|
|
Tag *t;
|
|
List <Tag *> ::Iterator i(tag.GetTags().GetRoot());
|
|
|
|
t = i.ObjectPtr();
|
|
if (!t) return false;
|
|
mn.FromMetaTag(*t);
|
|
++i;
|
|
|
|
t = i.ObjectPtr();
|
|
if (!t) return false;
|
|
mx.FromMetaTag(*t);
|
|
|
|
return true;
|
|
}
|
|
Tag *MinMax::AsMetaTag()
|
|
{
|
|
Tag *root = new Tag("MinMax");
|
|
if (root)
|
|
{
|
|
root->AddChild(mn.AsMetaTag("Min"));
|
|
root->AddChild(mx.AsMetaTag("Max"));
|
|
}
|
|
return root;
|
|
}
|
|
//------------------------------------------------------------------------------
|