charles/src/objectBox.cc
2014-08-09 10:54:00 -07:00

227 lines
4.6 KiB
C++

/* objectBox.cc
* vim: set tw=80:
* Eryn Wells <eryn@erynwells.me>
*/
#include <limits>
#include "objectBox.hh"
using charles::basics::Ray;
using charles::basics::Vector4;
namespace charles {
/*
* charles::Box::Box --
*/
Box::Box()
/* A unit box centered on the origin. */
: Box(Vector4(-0.5, -0.5, -0.5), Vector4(0.5, 0.5, 0.5))
{ }
/*
* charles::Box::Box --
*/
Box::Box(const Vector4& near,
const Vector4& far)
: Object(),
mNear(near),
mFar(far)
{ }
/*
* charles::Box::GetNear --
*/
Vector4&
Box::GetNear()
{
return mNear;
}
/*
* charles::Box::SetNear --
*/
void
Box::SetNear(const Vector4& near)
{
mNear = near;
}
/*
* charles::Box::GetFar --
*/
Vector4&
Box::GetFar()
{
return mFar;
}
/*
* charles::Box::SetFar --
*/
void
Box::SetFar(const Vector4& far)
{
mFar = far;
}
/*
* charles::Box::DoNormal --
*/
Vector4
Box::DoNormal(const Vector4& p)
const
{
if (NearlyEqual(p.X(), mNear.X())) {
return Vector4(-1, 0, 0);
} else if (NearlyEqual(p.X(), mFar.X())) {
return Vector4(1, 0, 0);
} else if (NearlyEqual(p.Y(), mNear.Y())) {
return Vector4(0, -1, 0);
} else if (NearlyEqual(p.Y(), mFar.Y())) {
return Vector4(0, 1, 0);
} else if (NearlyEqual(p.Z(), mNear.Z())) {
return Vector4(0, 0, -1);
} else if (NearlyEqual(p.Z(), mFar.Z())) {
return Vector4(0, 0, 1);
}
/* TODO: Eventually, I might want to raise an error here. */
return Vector4();
}
/*
* charles::Box::DoIntersect --
*/
bool
Box::DoIntersect(const Ray& ray,
TVector& t,
Stats& stats)
const
{
stats.boxIntersectionTests++;
/* This is the Kay-Kajiya box intersection algorithm. */
Double tNear = -std::numeric_limits<Double>::infinity();
Double tFar = std::numeric_limits<Double>::infinity();
if (!IntersectSlab(mNear.X(), mFar.X(),
ray.origin.X(), ray.direction.X(),
tNear, tFar)) {
return false;
}
if (!IntersectSlab(mNear.Y(), mFar.Y(),
ray.origin.Y(), ray.direction.Y(),
tNear, tFar)) {
return false;
}
if (!IntersectSlab(mNear.Z(), mFar.Z(),
ray.origin.Z(), ray.direction.Z(),
tNear, tFar)) {
return false;
}
/* We have an intersection! */
stats.boxIntersections++;
t.push_back(tNear);
t.push_back(tFar);
return true;
}
/*
* charles::Box::IntersectSlab --
*/
inline bool
Box::IntersectSlab(const Double& slabLow,
const Double& slabHigh,
const Double& rayOriginComponent,
const Double& rayDirectionComponent,
Double& tNear,
Double& tFar)
const
{
Double t0, t1;
/*
* From the Ray Tracing book:
*
* For a more efficient algorithm, unwrap the loop, expand the swap of t0
* and t1 into two branches, and change the calculations to multiply by
* the inverse of the ray's direction to avoid divisions. Unwrapping the
* loop allows elimination of comparing t0 adn t1 to tNear and tFar [for
* the X planes], as tNear will always be set to the smaller and tFar the
* larger of t0 and t1.
*
* Initially there was a for loop iterating over each parallel pair of
* planes (X, Y, and Z planes).
*/
if (NearZero(rayDirectionComponent)) {
/* The ray is parallel to the X axis. */
if (rayOriginComponent < slabLow || rayOriginComponent > slabHigh) {
/* The ray's origin is not between the slabs, so no intersection. */
return false;
}
} else {
t0 = (slabLow - rayOriginComponent) / rayDirectionComponent;
t1 = (slabHigh - rayOriginComponent) / rayDirectionComponent;
#if 0
if (t0 <= t1) {
tNear = t0;
tFar = t1;
} else {
tNear = t1;
tFar = t0;
}
#else
if (t0 > t1) {
Double tmp = t0;
t0 = t1;
t1 = tmp;
}
#endif
if (t0 > tNear) {
tNear = t0;
}
if (t1 < tFar) {
tFar = t1;
}
if (tNear > tFar) {
/* Box is missed. */
return false;
}
if (tFar < 0.0) {
/* Box is behind the ray. */
return false;
}
}
return true;
}
/*
* charles::Box::Write --
*/
void
Box::Write(std::ostream& ost)
const
{
ost << "[Box near=" << mNear << " far=" << mFar << "]";
}
} /* namespace charles */