356 lines
10 KiB
C++
356 lines
10 KiB
C++
// Copyright © 2008-2019 Pioneer Developers. See AUTHORS.txt for details
|
|
// Licensed under the terms of the GPL v3. See licenses/GPL-3.txt
|
|
|
|
#include "GeomTree.h"
|
|
#include "../libs.h"
|
|
#include "BVHTree.h"
|
|
#include "Weld.h"
|
|
#include "scenegraph/Serializer.h"
|
|
|
|
GeomTree::~GeomTree()
|
|
{
|
|
}
|
|
|
|
GeomTree::GeomTree(const int numVerts, const int numTris, const std::vector<vector3f> &vertices, const Uint32 *indices, const Uint32 *triflags) :
|
|
m_numVertices(numVerts),
|
|
m_numTris(numTris),
|
|
m_vertices(vertices)
|
|
{
|
|
PROFILE_SCOPED()
|
|
assert(static_cast<int>(vertices.size()) == m_numVertices);
|
|
Profiler::Timer timer;
|
|
timer.Start();
|
|
|
|
const int numIndices = numTris * 3;
|
|
m_indices.reserve(numIndices);
|
|
for (int i = 0; i < numIndices; ++i) {
|
|
m_indices.push_back(indices[i]);
|
|
}
|
|
|
|
m_triFlags.reserve(numTris);
|
|
for (int i = 0; i < numTris; ++i) {
|
|
m_triFlags.push_back(triflags[i]);
|
|
}
|
|
|
|
m_aabb.min = vector3d(FLT_MAX, FLT_MAX, FLT_MAX);
|
|
m_aabb.max = vector3d(-FLT_MAX, -FLT_MAX, -FLT_MAX);
|
|
|
|
// activeTris = tris we are still trying to put into leaves
|
|
std::vector<int> activeTris;
|
|
activeTris.reserve(numTris);
|
|
for (int i = 0; i < numTris; i++) {
|
|
activeTris.push_back(i * 3);
|
|
}
|
|
|
|
typedef std::map<std::pair<int, int>, int> EdgeType;
|
|
EdgeType edges;
|
|
#define ADD_EDGE(_i1, _i2, _triflag) \
|
|
if ((_i1) < (_i2)) \
|
|
edges[std::pair<int, int>(_i1, _i2)] = _triflag; \
|
|
else if ((_i1) > (_i2)) \
|
|
edges[std::pair<int, int>(_i2, _i1)] = _triflag;
|
|
|
|
// eliminate duplicate vertices
|
|
{
|
|
std::vector<Uint32> xrefs;
|
|
nv::Weld<vector3f> weld;
|
|
weld(m_vertices, xrefs);
|
|
m_numVertices = m_vertices.size();
|
|
|
|
//Output("--- %d vertices welded\n", count - newCount);
|
|
|
|
// Remap faces.
|
|
const Uint32 faceCount = numTris;
|
|
for (Uint32 f = 0; f < faceCount; f++) {
|
|
const Uint32 idx = (f * 3);
|
|
m_indices[idx + 0] = xrefs.at(m_indices[idx + 0]);
|
|
m_indices[idx + 1] = xrefs.at(m_indices[idx + 1]);
|
|
m_indices[idx + 2] = xrefs.at(m_indices[idx + 2]);
|
|
}
|
|
}
|
|
|
|
// Get radius, m_aabb, and merge duplicate edges
|
|
m_radius = 0;
|
|
for (int i = 0; i < numTris; i++) {
|
|
const Uint32 triflag = m_triFlags[i];
|
|
const int vi1 = m_indices[3 * i + 0];
|
|
const int vi2 = m_indices[3 * i + 1];
|
|
const int vi3 = m_indices[3 * i + 2];
|
|
|
|
ADD_EDGE(vi1, vi2, triflag);
|
|
ADD_EDGE(vi1, vi3, triflag);
|
|
ADD_EDGE(vi2, vi3, triflag);
|
|
|
|
vector3d v[3];
|
|
v[0] = vector3d(m_vertices[vi1]);
|
|
v[1] = vector3d(m_vertices[vi2]);
|
|
v[2] = vector3d(m_vertices[vi3]);
|
|
m_aabb.Update(v[0]);
|
|
m_aabb.Update(v[1]);
|
|
m_aabb.Update(v[2]);
|
|
for (int j = 0; j < 3; j++) {
|
|
const double rad = v[j].x * v[j].x + v[j].y * v[j].y + v[j].z * v[j].z;
|
|
if (rad > m_radius) m_radius = rad;
|
|
}
|
|
}
|
|
m_radius = sqrt(m_radius);
|
|
|
|
{
|
|
Aabb *aabbs = new Aabb[activeTris.size()];
|
|
for (Uint32 i = 0; i < activeTris.size(); i++) {
|
|
const vector3d v1 = vector3d(m_vertices[m_indices[activeTris[i] + 0]]);
|
|
const vector3d v2 = vector3d(m_vertices[m_indices[activeTris[i] + 1]]);
|
|
const vector3d v3 = vector3d(m_vertices[m_indices[activeTris[i] + 2]]);
|
|
aabbs[i].min = aabbs[i].max = v1;
|
|
aabbs[i].Update(v2);
|
|
aabbs[i].Update(v3);
|
|
}
|
|
|
|
//int t = SDL_GetTicks();
|
|
m_triTree.reset(new BVHTree(activeTris.size(), &activeTris[0], aabbs));
|
|
delete[] aabbs;
|
|
}
|
|
//Output("Tri tree of %d tris build in %dms\n", activeTris.size(), SDL_GetTicks() - t);
|
|
|
|
m_numEdges = edges.size();
|
|
m_edges.resize(m_numEdges);
|
|
// to build Edge bvh tree with.
|
|
m_aabbs.resize(m_numEdges);
|
|
int *edgeIdxs = new int[m_numEdges];
|
|
|
|
int pos = 0;
|
|
typedef EdgeType::iterator MapPairIter;
|
|
for (MapPairIter i = edges.begin(), iEnd = edges.end(); i != iEnd; ++i, pos++) {
|
|
// precalc some jizz
|
|
const std::pair<int, int> &vtx = (*i).first;
|
|
const int triflag = (*i).second;
|
|
const vector3f &v1 = m_vertices[vtx.first];
|
|
const vector3f &v2 = m_vertices[vtx.second];
|
|
vector3f dir = (v2 - v1);
|
|
const float len = dir.Length();
|
|
dir *= 1.0f / len;
|
|
|
|
m_edges[pos].v1i = vtx.first;
|
|
m_edges[pos].v2i = vtx.second;
|
|
m_edges[pos].triFlag = triflag;
|
|
m_edges[pos].len = len;
|
|
m_edges[pos].dir = dir;
|
|
|
|
edgeIdxs[pos] = pos;
|
|
m_aabbs[pos].min = m_aabbs[pos].max = vector3d(v1);
|
|
m_aabbs[pos].Update(vector3d(v2));
|
|
}
|
|
|
|
//t = SDL_GetTicks();
|
|
m_edgeTree.reset(new BVHTree(m_numEdges, edgeIdxs, &m_aabbs[0]));
|
|
delete[] edgeIdxs;
|
|
//Output("Edge tree of %d edges build in %dms\n", m_numEdges, SDL_GetTicks() - t);
|
|
|
|
timer.Stop();
|
|
//Output(" - - GeomTree::GeomTree took: %lf milliseconds\n", timer.millicycles());
|
|
}
|
|
|
|
GeomTree::GeomTree(Serializer::Reader &rd)
|
|
{
|
|
PROFILE_SCOPED()
|
|
m_numVertices = rd.Int32();
|
|
m_numEdges = rd.Int32();
|
|
m_numTris = rd.Int32();
|
|
m_radius = rd.Double();
|
|
|
|
m_aabb.max = rd.Vector3d();
|
|
m_aabb.min = rd.Vector3d();
|
|
m_aabb.radius = rd.Double();
|
|
|
|
const Uint32 numAabbs = rd.Int32();
|
|
m_aabbs.resize(numAabbs);
|
|
for (Uint32 iAabb = 0; iAabb < numAabbs; ++iAabb) {
|
|
rd >> m_aabbs[iAabb];
|
|
}
|
|
|
|
{
|
|
PROFILE_SCOPED_DESC("GeomTree::LoadEdges")
|
|
m_edges.resize(m_numEdges);
|
|
for (Sint32 iEdge = 0; iEdge < m_numEdges; ++iEdge) {
|
|
auto &ed = m_edges[iEdge];
|
|
rd >> ed.v1i >> ed.v2i >> ed.len >> ed.dir >> ed.triFlag;
|
|
}
|
|
}
|
|
|
|
m_vertices.resize(m_numVertices);
|
|
for (Sint32 iVert = 0; iVert < m_numVertices; ++iVert) {
|
|
m_vertices[iVert] = rd.Vector3f();
|
|
}
|
|
|
|
const int numIndicies(m_numTris * 3);
|
|
m_indices.resize(numIndicies);
|
|
for (Sint32 iIndi = 0; iIndi < numIndicies; ++iIndi) {
|
|
m_indices[iIndi] = rd.Int32();
|
|
}
|
|
|
|
m_triFlags.resize(m_numTris);
|
|
for (Sint32 iTri = 0; iTri < m_numTris; ++iTri) {
|
|
m_triFlags[iTri] = rd.Int32();
|
|
}
|
|
|
|
// activeTris = tris we are still trying to put into leaves
|
|
std::vector<int> activeTris;
|
|
activeTris.reserve(m_numTris);
|
|
for (int i = 0; i < m_numTris; i++) {
|
|
activeTris.push_back(i * 3);
|
|
}
|
|
|
|
// regenerate the aabb data
|
|
Aabb *aabbs = new Aabb[activeTris.size()];
|
|
for (Uint32 i = 0; i < activeTris.size(); i++) {
|
|
const vector3d v1 = vector3d(m_vertices[m_indices[activeTris[i] + 0]]);
|
|
const vector3d v2 = vector3d(m_vertices[m_indices[activeTris[i] + 1]]);
|
|
const vector3d v3 = vector3d(m_vertices[m_indices[activeTris[i] + 2]]);
|
|
aabbs[i].min = aabbs[i].max = v1;
|
|
aabbs[i].Update(v2);
|
|
aabbs[i].Update(v3);
|
|
}
|
|
m_triTree.reset(new BVHTree(activeTris.size(), &activeTris[0], aabbs));
|
|
delete[] aabbs;
|
|
|
|
//
|
|
int *edgeIdxs = new int[m_numEdges];
|
|
memset(edgeIdxs, 0, sizeof(int) * m_numEdges);
|
|
for (int i = 0; i < m_numEdges; i++) {
|
|
edgeIdxs[i] = i;
|
|
}
|
|
m_edgeTree.reset(new BVHTree(m_numEdges, edgeIdxs, &m_aabbs[0]));
|
|
}
|
|
|
|
static bool SlabsRayAabbTest(const BVHNode *n, const vector3f &start, const vector3f &invDir, isect_t *isect)
|
|
{
|
|
PROFILE_SCOPED()
|
|
float
|
|
l1 = (n->aabb.min.x - start.x) * invDir.x,
|
|
l2 = (n->aabb.max.x - start.x) * invDir.x,
|
|
lmin = std::min(l1, l2),
|
|
lmax = std::max(l1, l2);
|
|
|
|
l1 = (n->aabb.min.y - start.y) * invDir.y;
|
|
l2 = (n->aabb.max.y - start.y) * invDir.y;
|
|
lmin = std::max(std::min(l1, l2), lmin);
|
|
lmax = std::min(std::max(l1, l2), lmax);
|
|
|
|
l1 = (n->aabb.min.z - start.z) * invDir.z;
|
|
l2 = (n->aabb.max.z - start.z) * invDir.z;
|
|
lmin = std::max(std::min(l1, l2), lmin);
|
|
lmax = std::min(std::max(l1, l2), lmax);
|
|
|
|
return ((lmax >= 0.f) & (lmax >= lmin) & (lmin < isect->dist));
|
|
}
|
|
|
|
void GeomTree::TraceRay(const vector3f &start, const vector3f &dir, isect_t *isect) const
|
|
{
|
|
PROFILE_SCOPED()
|
|
TraceRay(m_triTree->GetRoot(), start, dir, isect);
|
|
}
|
|
|
|
void GeomTree::TraceRay(const BVHNode *currnode, const vector3f &a_origin, const vector3f &a_dir, isect_t *isect) const
|
|
{
|
|
PROFILE_SCOPED()
|
|
BVHNode *stack[32];
|
|
int stackpos = -1;
|
|
const vector3f invDir( // avoid division by zero please
|
|
is_zero_exact(a_dir.x) ? 0.0f : (1.0f / a_dir.x),
|
|
is_zero_exact(a_dir.y) ? 0.0f : (1.0f / a_dir.y),
|
|
is_zero_exact(a_dir.z) ? 0.0f : (1.0f / a_dir.z));
|
|
|
|
for (;;) {
|
|
while (!currnode->IsLeaf()) {
|
|
if (!SlabsRayAabbTest(currnode, a_origin, invDir, isect)) goto pop_bstack;
|
|
|
|
stackpos++;
|
|
stack[stackpos] = currnode->kids[1];
|
|
currnode = currnode->kids[0];
|
|
}
|
|
// triangle intersection jizz
|
|
for (int i = 0; i < currnode->numTris; i++) {
|
|
RayTriIntersect(1, a_origin, &a_dir, currnode->triIndicesStart[i], isect);
|
|
}
|
|
pop_bstack:
|
|
if (stackpos < 0) break;
|
|
currnode = stack[stackpos];
|
|
stackpos--;
|
|
}
|
|
}
|
|
|
|
void GeomTree::RayTriIntersect(int numRays, const vector3f &origin, const vector3f *dirs, int triIdx, isect_t *isects) const
|
|
{
|
|
PROFILE_SCOPED()
|
|
const vector3f a(m_vertices[m_indices[triIdx + 0]]);
|
|
const vector3f b(m_vertices[m_indices[triIdx + 1]]);
|
|
const vector3f c(m_vertices[m_indices[triIdx + 2]]);
|
|
|
|
const vector3f n = (c - a).Cross(b - a);
|
|
const float nominator = n.Dot(a - origin);
|
|
|
|
const vector3f v0_cross((c - origin).Cross(b - origin));
|
|
const vector3f v1_cross((b - origin).Cross(a - origin));
|
|
const vector3f v2_cross((a - origin).Cross(c - origin));
|
|
|
|
for (int i = 0; i < numRays; i++) {
|
|
const float v0d = v0_cross.Dot(dirs[i]);
|
|
const float v1d = v1_cross.Dot(dirs[i]);
|
|
const float v2d = v2_cross.Dot(dirs[i]);
|
|
|
|
if (((v0d > 0) && (v1d > 0) && (v2d > 0)) ||
|
|
((v0d < 0) && (v1d < 0) && (v2d < 0))) {
|
|
const float dist = nominator / dirs[i].Dot(n);
|
|
if ((dist > 0) && (dist < isects[i].dist)) {
|
|
isects[i].dist = dist;
|
|
isects[i].triIdx = triIdx / 3;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
vector3f GeomTree::GetTriNormal(int triIdx) const
|
|
{
|
|
PROFILE_SCOPED()
|
|
const vector3f a(m_vertices[m_indices[3 * triIdx + 0]]);
|
|
const vector3f b(m_vertices[m_indices[3 * triIdx + 1]]);
|
|
const vector3f c(m_vertices[m_indices[3 * triIdx + 2]]);
|
|
|
|
return (b - a).Cross(c - a).Normalized();
|
|
}
|
|
|
|
void GeomTree::Save(Serializer::Writer &wr) const
|
|
{
|
|
PROFILE_SCOPED()
|
|
wr.Int32(m_numVertices);
|
|
wr.Int32(m_numEdges);
|
|
wr.Int32(m_numTris);
|
|
wr.Double(m_radius);
|
|
|
|
wr.Vector3d(m_aabb.max);
|
|
wr.Vector3d(m_aabb.min);
|
|
wr.Double(m_aabb.radius);
|
|
|
|
wr.Int32(m_numEdges);
|
|
for (Sint32 iAabb = 0; iAabb < m_numEdges; ++iAabb) {
|
|
wr << m_aabbs[iAabb];
|
|
}
|
|
|
|
for (Sint32 iEdge = 0; iEdge < m_numEdges; ++iEdge) {
|
|
auto &ed = m_edges[iEdge];
|
|
wr << ed.v1i << ed.v2i << ed.len << ed.dir << ed.triFlag;
|
|
}
|
|
|
|
for (Sint32 iVert = 0; iVert < m_numVertices; ++iVert) {
|
|
wr.Vector3f(m_vertices[iVert]);
|
|
}
|
|
|
|
for (Sint32 iIndi = 0; iIndi < (m_numTris * 3); ++iIndi) {
|
|
wr.Int32(m_indices[iIndi]);
|
|
}
|
|
|
|
for (Sint32 iTri = 0; iTri < m_numTris; ++iTri) {
|
|
wr.Int32(m_triFlags[iTri]);
|
|
}
|
|
}
|