161 lines
4.4 KiB
C++
161 lines
4.4 KiB
C++
/* -----------------------------------------------------------------------------
|
|
GSFramework
|
|
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
|
----------------------------------------------------------------------------- */
|
|
|
|
|
|
#include <float.h>
|
|
#include "bih/bih.h"
|
|
#include "timing/benchmark.h"
|
|
#include "log/log.h"
|
|
|
|
using namespace GS;
|
|
using namespace GS::BIH;
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
static void HalveMinMax(MinMax &minmax, int n, bool trim_max)
|
|
{
|
|
if (trim_max)
|
|
minmax.mx[n] = (minmax.mn[n] + minmax.mx[n]) * 0.5f;
|
|
else minmax.mn[n] = (minmax.mn[n] + minmax.mx[n]) * 0.5f;
|
|
}
|
|
//-----------------------------------------------------------------------------
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void Tree::MakeNodeLeaf(Node *node, uint count, uint *p_sarray, MinMax * /*varray*/)
|
|
{
|
|
leaf_count++;
|
|
node->axis = Math::AxisNone;
|
|
node->p = (void *)p_sarray;
|
|
node->count = count;
|
|
}
|
|
void Tree::DoNodeSplit(MinMax &minmax, uint count, uint *sarray, MinMax *varray, uint &pivot, Node *node, uint &split_axis)
|
|
{
|
|
// Determine split axis.
|
|
Vector4 dt = minmax.mx - minmax.mn;
|
|
|
|
if ((dt.x > dt.y) && (dt.x > dt.z))
|
|
split_axis = 0;
|
|
else if ((dt.y > dt.x) && (dt.y > dt.z))
|
|
split_axis = 1;
|
|
else
|
|
split_axis = 2;
|
|
|
|
float split_coord = (minmax.mn[split_axis] + minmax.mx[split_axis]) * 0.5f;
|
|
|
|
// Fill split arrays.
|
|
float extends[2];
|
|
uint high = count;
|
|
|
|
//--------------------------------------------------------------------------
|
|
#define __INDICE_SWAP__(LO, HI) { uint swp = sarray[LO]; sarray[LO] = sarray[HI]; sarray[HI] = swp; }
|
|
//--------------------------------------------------------------------------
|
|
#define __GET_EXTENDS__(I, S) { extends[0] = varray[sarray[I]].mn[S]; extends[1] = varray[sarray[I]].mx[S]; }
|
|
|
|
pivot = 0;
|
|
while (pivot < high)
|
|
{
|
|
__GET_EXTENDS__(pivot, split_axis)
|
|
if ((extends[1] - split_coord) > (split_coord - extends[0]))
|
|
{ // max
|
|
__INDICE_SWAP__(pivot, high - 1)
|
|
high--;
|
|
}
|
|
else
|
|
{ // min
|
|
__INDICE_SWAP__(0, pivot)
|
|
pivot++;
|
|
}
|
|
}
|
|
|
|
// Node extends.
|
|
node->split[0] = -FLT_MAX;
|
|
|
|
uint n;
|
|
for (n = 0; n < pivot; ++n)
|
|
{
|
|
__GET_EXTENDS__(n, split_axis)
|
|
if (extends[1] > node->split[0])
|
|
node->split[0] = extends[1] + 0.0001f;
|
|
}
|
|
node->split[1] = FLT_MAX;
|
|
for (; n < count; ++n)
|
|
{
|
|
__GET_EXTENDS__(n, split_axis)
|
|
if (extends[0] < node->split[1])
|
|
node->split[1] = extends[0] - 0.0001f;
|
|
}
|
|
}
|
|
bool Tree::Split(MinMax &l_minmax, uint count, uint *p_sarray, MinMax *varray, Node *node, uint dpth)
|
|
{
|
|
if ((count <= min_leaf_vcount) || (dpth == 64))
|
|
{
|
|
if (dpth > depth)
|
|
depth = dpth;
|
|
MakeNodeLeaf(node, count, p_sarray, varray);
|
|
}
|
|
else
|
|
{
|
|
// Split node.
|
|
uint pivot, split_axis;
|
|
DoNodeSplit(l_minmax, count, p_sarray, varray, pivot, node, split_axis);
|
|
|
|
// Distribute to children.
|
|
node_count += 2;
|
|
Node *children = new Node[2];
|
|
if (!children)
|
|
__ERR__(__LOG_E__ << "Failed to allocate BIH node children.\n", false)
|
|
node->axis = (char)split_axis;
|
|
node->p = (void *)children;
|
|
|
|
MinMax minmax_child = l_minmax;
|
|
HalveMinMax(minmax_child, split_axis, true);
|
|
Split(minmax_child, pivot, p_sarray, varray, &children[0], dpth + 1);
|
|
minmax_child = l_minmax;
|
|
HalveMinMax(minmax_child, split_axis, false);
|
|
Split(minmax_child, count - pivot, &p_sarray[pivot], varray, &children[1], dpth + 1);
|
|
}
|
|
return true;
|
|
}
|
|
//-----------------------------------------------------------------------------
|
|
|
|
//-----------------------------------------------------------------------------
|
|
bool Tree::Build(uint count, MinMax *varray)
|
|
{
|
|
Benchmark build_bench(true);
|
|
|
|
if (!count)
|
|
return false;
|
|
|
|
// Initialize split array.
|
|
if (!sarray.Allocate(count))
|
|
__ERR__(__LOG_E__ << "Failed to allocate BIH indice array.\n", false)
|
|
|
|
uint n;
|
|
for (n = 0; n < count; ++n)
|
|
sarray[n] = n;
|
|
|
|
// Get volume set bounding coordinates.
|
|
minmax = varray[0];
|
|
for (n = 1; n < count; ++n)
|
|
minmax.Grow(varray[n]);
|
|
minmax.mn -= 0.0001f;
|
|
minmax.mx += 0.0001f;
|
|
|
|
// Split.
|
|
leaf_count = 0;
|
|
node_count = 1;
|
|
depth = 0;
|
|
|
|
if (!(root = new Node))
|
|
__ERR__(__LOG_E__ << "Failed to allocate BIH root node.\n", false)
|
|
|
|
bool success = Split(minmax, count, sarray, varray, root, 0);
|
|
|
|
build_bench.Stop();
|
|
// __LOG__ << "Done in " << build_bench.GetLastStepMs() << "ms. " << node_count << " nodes, " << leaf_count << " leaves, depth = " << depth << ".\n";
|
|
return success;
|
|
}
|
|
//-----------------------------------------------------------------------------
|