/*
This file is part of Mitsuba, a physically based rendering system.
Copyright (c) 2007-2012 by Wenzel Jakob and others.
Mitsuba is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License Version 3
as published by the Free Software Foundation.
Mitsuba is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
#include
#include
#if defined(MTS_SSE)
#include
#include
#include
#endif
MTS_NAMESPACE_BEGIN
ShapeKDTree::ShapeKDTree() {
#if !defined(MTS_KD_CONSERVE_MEMORY)
m_triAccel = NULL;
#endif
m_shapeMap.push_back(0);
}
ShapeKDTree::~ShapeKDTree() {
#if !defined(MTS_KD_CONSERVE_MEMORY)
if (m_triAccel)
freeAligned(m_triAccel);
#endif
for (size_t i=0; idecRef();
}
static StatsCounter raysTraced("General", "Normal rays traced");
static StatsCounter shadowRaysTraced("General", "Shadow rays traced");
void ShapeKDTree::addShape(const Shape *shape) {
Assert(!isBuilt());
if (shape->isCompound())
Log(EError, "Cannot add compound shapes to a kd-tree - expand them first!");
if (shape->getClass()->derivesFrom(MTS_CLASS(TriMesh))) {
// Triangle meshes are expanded into individual primitives,
// which are visible to the tree construction code. Generic
// primitives are only handled by their AABBs
m_shapeMap.push_back((SizeType)
static_cast(shape)->getTriangleCount());
m_triangleFlag.push_back(true);
} else {
m_shapeMap.push_back(1);
m_triangleFlag.push_back(false);
}
shape->incRef();
m_shapes.push_back(shape);
}
void ShapeKDTree::build() {
for (size_t i=1; i::buildInternal();
#if !defined(MTS_KD_CONSERVE_MEMORY)
ref timer = new Timer();
SizeType primCount = getPrimitiveCount();
Log(EDebug, "Precomputing triangle intersection information (%s)",
memString(sizeof(TriAccel)*primCount).c_str());
m_triAccel = static_cast(allocAligned(primCount * sizeof(TriAccel)));
IndexType idx = 0;
for (IndexType i=0; i(shape);
const Triangle *triangles = mesh->getTriangles();
const Point *positions = mesh->getVertexPositions();
for (IndexType j=0; jgetTriangleCount(); ++j) {
const Triangle &tri = triangles[j];
const Point &v0 = positions[tri.idx[0]];
const Point &v1 = positions[tri.idx[1]];
const Point &v2 = positions[tri.idx[2]];
m_triAccel[idx].load(v0, v1, v2);
m_triAccel[idx].shapeIndex = i;
m_triAccel[idx].primIndex = j;
++idx;
}
} else {
/* Create a 'fake' triangle, which redirects to a Shape */
memset(&m_triAccel[idx], 0, sizeof(TriAccel));
m_triAccel[idx].shapeIndex = i;
m_triAccel[idx].k = KNoTriangleFlag;
++idx;
}
}
Log(EDebug, "Finished -- took %i ms.", timer->getMilliseconds());
Log(EDebug, "");
KDAssert(idx == primCount);
#endif
}
bool ShapeKDTree::rayIntersect(const Ray &ray, Intersection &its) const {
uint8_t temp[MTS_KD_INTERSECTION_TEMP];
its.t = std::numeric_limits::infinity();
Float mint, maxt;
++raysTraced;
if (m_aabb.rayIntersect(ray, mint, maxt)) {
/* Use an adaptive ray epsilon */
Float rayMinT = ray.mint;
if (rayMinT == Epsilon)
rayMinT *= std::max(std::max(std::max(std::abs(ray.o.x),
std::abs(ray.o.y)), std::abs(ray.o.z)), Epsilon);
if (rayMinT > mint) mint = rayMinT;
if (ray.maxt < maxt) maxt = ray.maxt;
if (EXPECT_TAKEN(maxt > mint)) {
if (rayIntersectHavran(ray, mint, maxt, its.t, temp)) {
fillIntersectionRecord(ray, temp, its);
return true;
}
}
}
return false;
}
bool ShapeKDTree::rayIntersect(const Ray &ray, Float &t, ConstShapePtr &shape,
Normal &n, Point2 &uv) const {
uint8_t temp[MTS_KD_INTERSECTION_TEMP];
Float mint, maxt;
t = std::numeric_limits::infinity();
++shadowRaysTraced;
if (m_aabb.rayIntersect(ray, mint, maxt)) {
/* Use an adaptive ray epsilon */
Float rayMinT = ray.mint;
if (rayMinT == Epsilon)
rayMinT *= std::max(std::max(std::abs(ray.o.x),
std::abs(ray.o.y)), std::abs(ray.o.z));
if (rayMinT > mint) mint = rayMinT;
if (ray.maxt < maxt) maxt = ray.maxt;
if (EXPECT_TAKEN(maxt > mint)) {
if (rayIntersectHavran(ray, mint, maxt, t, temp)) {
const IntersectionCache *cache = reinterpret_cast(temp);
shape = m_shapes[cache->shapeIndex];
if (m_triangleFlag[cache->shapeIndex]) {
const TriMesh *trimesh = static_cast(shape);
const Triangle &tri = trimesh->getTriangles()[cache->primIndex];
const Point *vertexPositions = trimesh->getVertexPositions();
const Point2 *vertexTexcoords = trimesh->getVertexTexcoords();
const uint32_t idx0 = tri.idx[0], idx1 = tri.idx[1], idx2 = tri.idx[2];
const Point &p0 = vertexPositions[idx0];
const Point &p1 = vertexPositions[idx1];
const Point &p2 = vertexPositions[idx2];
n = normalize(cross(p1-p0, p2-p0));
if (EXPECT_TAKEN(vertexTexcoords)) {
const Vector b(1 - cache->u - cache->v, cache->u, cache->v);
const Point2 &t0 = vertexTexcoords[idx0];
const Point2 &t1 = vertexTexcoords[idx1];
const Point2 &t2 = vertexTexcoords[idx2];
uv = t0 * b.x + t1 * b.y + t2 * b.z;
} else {
uv = Point2(0.0f);
}
} else {
/// Uh oh... -- much unnecessary work is done here
Intersection its;
its.t = t;
shape->fillIntersectionRecord(ray,
reinterpret_cast(temp) + 8, its);
n = its.geoFrame.n;
uv = its.uv;
if (its.shape)
shape = its.shape;
}
return true;
}
}
}
return false;
}
bool ShapeKDTree::rayIntersect(const Ray &ray) const {
Float mint, maxt, t = std::numeric_limits::infinity();
++shadowRaysTraced;
if (m_aabb.rayIntersect(ray, mint, maxt)) {
/* Use an adaptive ray epsilon */
Float rayMinT = ray.mint;
if (rayMinT == Epsilon)
rayMinT *= std::max(std::max(std::abs(ray.o.x),
std::abs(ray.o.y)), std::abs(ray.o.z));
if (rayMinT > mint) mint = rayMinT;
if (ray.maxt < maxt) maxt = ray.maxt;
if (EXPECT_TAKEN(maxt > mint))
if (rayIntersectHavran(ray, mint, maxt, t, NULL))
return true;
}
return false;
}
#if defined(MTS_HAS_COHERENT_RT)
/// Ray traversal stack entry for uncoherent ray tracing
struct CoherentKDStackEntry {
/* Current ray interval */
RayInterval4 MM_ALIGN16 interval;
/* Pointer to the far child */
const ShapeKDTree::KDNode * __restrict node;
};
static StatsCounter coherentPackets("General", "Coherent ray packets");
static StatsCounter incoherentPackets("General", "Incoherent ray packets");
void ShapeKDTree::rayIntersectPacket(const RayPacket4 &packet,
const RayInterval4 &rayInterval, Intersection4 &its, void *temp) const {
CoherentKDStackEntry MM_ALIGN16 stack[MTS_KD_MAXDEPTH];
RayInterval4 MM_ALIGN16 interval;
const KDNode * __restrict currNode = m_nodes;
int stackIndex = 0;
++coherentPackets;
/* First, intersect with the kd-tree AABB to determine
the intersection search intervals */
if (!m_aabb.rayIntersectPacket(packet, interval))
return;
interval.mint.ps = _mm_max_ps(interval.mint.ps, rayInterval.mint.ps);
interval.maxt.ps = _mm_min_ps(interval.maxt.ps, rayInterval.maxt.ps);
SSEVector itsFound( _mm_cmpgt_ps(interval.mint.ps, interval.maxt.ps));
SSEVector masked(itsFound);
if (_mm_movemask_ps(itsFound.ps) == 0xF)
return;
while (currNode != NULL) {
while (EXPECT_TAKEN(!currNode->isLeaf())) {
const uint8_t axis = currNode->getAxis();
/* Calculate the plane intersection */
const __m128
splitVal = _mm_set1_ps(currNode->getSplit()),
t = _mm_mul_ps(_mm_sub_ps(splitVal, packet.o[axis].ps),
packet.dRcp[axis].ps);
const __m128
startsAfterSplit = _mm_or_ps(masked.ps,
_mm_cmplt_ps(t, interval.mint.ps)),
endsBeforeSplit = _mm_or_ps(masked.ps,
_mm_cmpgt_ps(t, interval.maxt.ps));
currNode = currNode->getLeft() + packet.signs[axis][0];
/* The interval completely completely lies on one side
of the split plane */
if (EXPECT_TAKEN(_mm_movemask_ps(startsAfterSplit) == 15)) {
currNode = currNode->getSibling();
continue;
}
if (EXPECT_TAKEN(_mm_movemask_ps(endsBeforeSplit) == 15))
continue;
stack[stackIndex].node = currNode->getSibling();
stack[stackIndex].interval.maxt = interval.maxt;
stack[stackIndex].interval.mint.ps = _mm_max_ps(t, interval.mint.ps);
interval.maxt.ps = _mm_min_ps(t, interval.maxt.ps);
masked.ps = _mm_or_ps(masked.ps,
_mm_cmpgt_ps(interval.mint.ps, interval.maxt.ps));
stackIndex++;
}
/* Arrived at a leaf node - intersect against primitives */
const IndexType primStart = currNode->getPrimStart();
const IndexType primEnd = currNode->getPrimEnd();
if (EXPECT_NOT_TAKEN(primStart != primEnd)) {
SSEVector
searchStart(_mm_max_ps(rayInterval.mint.ps,
_mm_mul_ps(interval.mint.ps, SSEConstants::om_eps.ps))),
searchEnd(_mm_min_ps(rayInterval.maxt.ps,
_mm_mul_ps(interval.maxt.ps, SSEConstants::op_eps.ps)));
for (IndexType entry=primStart; entry != primEnd; entry++) {
const TriAccel &kdTri = m_triAccel[m_indices[entry]];
if (EXPECT_TAKEN(kdTri.k != KNoTriangleFlag)) {
itsFound.ps = _mm_or_ps(itsFound.ps,
mitsuba::rayIntersectPacket(kdTri, packet, searchStart.ps, searchEnd.ps, masked.ps, its));
} else {
const Shape *shape = m_shapes[kdTri.shapeIndex];
for (int i=0; i<4; ++i) {
if (masked.i[i])
continue;
Ray ray;
for (int axis=0; axis<3; axis++) {
ray.o[axis] = packet.o[axis].f[i];
ray.d[axis] = packet.d[axis].f[i];
ray.dRcp[axis] = packet.dRcp[axis].f[i];
}
Float t;
if (shape->rayIntersect(ray, searchStart.f[i], searchEnd.f[i], t,
reinterpret_cast(temp)
+ i * MTS_KD_INTERSECTION_TEMP + 8)) {
its.t.f[i] = t;
its.shapeIndex.i[i] = kdTri.shapeIndex;
its.primIndex.i[i] = KNoTriangleFlag;
itsFound.i[i] = 0xFFFFFFFF;
}
}
}
searchEnd.ps = _mm_min_ps(searchEnd.ps, its.t.ps);
}
}
/* Abort if the tree has been traversed or if
intersections have been found for all four rays */
if (_mm_movemask_ps(itsFound.ps) == 0xF || --stackIndex < 0)
break;
/* Pop from the stack */
currNode = stack[stackIndex].node;
interval = stack[stackIndex].interval;
masked.ps = _mm_or_ps(itsFound.ps,
_mm_cmpgt_ps(interval.mint.ps, interval.maxt.ps));
}
}
void ShapeKDTree::rayIntersectPacketIncoherent(const RayPacket4 &packet,
const RayInterval4 &rayInterval, Intersection4 &its4, void *temp) const {
++incoherentPackets;
for (int i=0; i<4; i++) {
Ray ray;
Float t;
for (int axis=0; axis<3; axis++) {
ray.o[axis] = packet.o[axis].f[i];
ray.d[axis] = packet.d[axis].f[i];
ray.dRcp[axis] = packet.dRcp[axis].f[i];
}
ray.mint = rayInterval.mint.f[i];
ray.maxt = rayInterval.maxt.f[i];
uint8_t *rayTemp = reinterpret_cast(temp) + i * MTS_KD_INTERSECTION_TEMP;
if (ray.mint < ray.maxt && rayIntersectHavran(ray, ray.mint, ray.maxt, t, rayTemp)) {
const IntersectionCache *cache = reinterpret_cast(rayTemp);
its4.t.f[i] = t;
its4.shapeIndex.i[i] = cache->shapeIndex;
its4.primIndex.i[i] = cache->primIndex;
its4.u.f[i] = cache->u;
its4.v.f[i] = cache->v;
}
}
}
#endif
MTS_IMPLEMENT_CLASS(ShapeKDTree, false, KDTreeBase)
MTS_NAMESPACE_END