/* Start Header -------------------------------------------------------
Purpose: Creating octrees in top-down fashion.
This creates subtrees reclusively.
Language: ISO C++17 Standard (/std:c++17)
Platform: 16.0, x64, Window10
Project: Spatial Algorithms - Octree
Author: Minsu Kang
Creation date: 4/15/2020
End Header --------------------------------------------------------*/
Octree::Octree(std::list<Object*>& _pObjectList)
{
m_MaxDepth = 0;
targetTreeHeight = 0;
vector<Vertex*> pointCloud;
// Find max and min positions to init the first entire volume
glm::vec3 maxPosition = glm::vec3(-std::numeric_limits<float>::max());
glm::vec3 minPosition = glm::vec3(std::numeric_limits<float>::max());
for (Object* pObj : _pObjectList)
{
maxPosition = glm::max(maxPosition, pObj->GetMaxPos());
minPosition = glm::min(minPosition, pObj->GetMinPos());
for (auto& mesh : pObj->pModel->meshes)
{
for(auto& vertex : mesh.vertices)
pointCloud.push_back(&vertex);
}
}
// to get the center pos and the half width
glm::vec3 octreeCenterPos = (maxPosition + minPosition) / 2.f;
float octreeHalfWidth = abs(maxPosition.x - octreeCenterPos.x);
octreeHalfWidth = glm::max(octreeHalfWidth, abs(maxPosition.y - octreeCenterPos.y));
octreeHalfWidth = glm::max(octreeHalfWidth, abs(maxPosition.z - octreeCenterPos.z));
// Construct and fill in root of this subtree
m_pOctree = new Octree();
m_pOctree->center = octreeCenterPos;
m_pOctree->halfWidth = octreeHalfWidth;
m_pOctree->pBox = new BoxAABB(
glm::vec3(octreeCenterPos + octreeHalfWidth),
glm::vec3(octreeCenterPos - octreeHalfWidth));
m_pOctree->pBox->centerPosition = octreeCenterPos;
m_pOctree->currDepth = 0;
// It's time to fill octree!!!
InsertVertices(m_pOctree, pointCloud);
pointCloud.clear();
}
void Octree::BuildChildNodes(Octree* pTree, const glm::vec3 center, const float halfWidth, const int currDepth)
{
// construct the eight children of the subtree
glm::vec3 offset;
float step = halfWidth * 0.5f;
pTree->pChild = new Octree[8]; // allocate
for (int i = 0; i < 8; i++) {
offset.x = ((i & 1) ? step : -step);
offset.y = ((i & 2) ? step : -step);
offset.z = ((i & 4) ? step : -step);
const glm::vec3 childCenter = center + offset;
const float childHalfWidth = step;
// init data
pTree->pChild[i].center = childCenter;
pTree->pChild[i].halfWidth = childHalfWidth;
pTree->pChild[i].pBox = new BoxAABB(childCenter + childHalfWidth, childCenter - childHalfWidth);
pTree->pChild[i].pBox->centerPosition = center;
pTree->pChild[i].pBox->color = glm::vec3(1.f);
pTree->pChild[i].currDepth = currDepth + 1;
}
}
.
.
.
void Octree::InsertVertices(Octree* pTree, vector<Vertex*> _pointCloud)
{
// Terminating criteria should be 300 (or less) triangles in the current cell.
if (_pointCloud.size() < 300)
return;
// When it cannot split its volume anymore
if (pTree->halfWidth < glm::epsilon<float>())
{
printf("ERROR: Cannot split its volume anymore!!");
return;
}
if(pTree->pChild == nullptr)
BuildChildNodes(pTree, pTree->center, pTree->halfWidth, pTree->currDepth);
vector<Vertex*> pointCloud[8];
glm::vec3 colors[8];
// Display octree cells with each �level� in different color
for (int i = 0; i < 8; ++i)
{
// random color between 0.4 and 1.0
colors[i].x = ((rand() % 100) / 200.0f) + 0.4f;
colors[i].y = ((rand() % 100) / 200.0f) + 0.4f;
colors[i].z = ((rand() % 100) / 200.0f) + 0.4f;
}
// Find the given vertex's child index among 0 - 7 sub nodes
for(auto& pVertex : _pointCloud)
{
int index = -1;
glm::vec3 delta = pVertex->Position - pTree->center;
if (delta.x < 0)
{
if (delta.y < 0)
{
if (delta.z < 0)
index = 0;
else
index = 4;
}
else
{
if (delta.z < 0)
index = 2;
else
index = 6;
}
}
else
{
if (delta.y < 0)
{
if (delta.z < 0)
index = 1;
else
index = 5;
}
else
{
if (delta.z < 0)
index = 3;
else
index = 7;
}
}
pVertex->NodeColor = colors[index];
pointCloud[index].push_back(pVertex);
}
for (int i = 0; i < 8; ++i)
{
pTree->pChild[i].pBox->color = colors[0];
InsertVertices(&(pTree->pChild[i]), pointCloud[i]);
}
m_MaxDepth = glm::max(m_MaxDepth, pTree->currDepth + 1);
}
void Octree::Draw(Shader* pShader)
{
DrawAux(pShader, m_pOctree, 0);
}
void Octree::DrawAux(Shader* pShader, Octree* pNode, int currentLevel)
{
// show all nodes
if (targetTreeHeight == -1)
{
// termination
if (pNode->pChild == NULL)
return;
// children
for (int i = 0; i < 8; ++i)
{
DrawAux(pShader, &(pNode->pChild[i]), currentLevel + 1);
pNode->pChild[i].pBox->Draw(pShader);
}
}
// show only the target node
else
{
if (currentLevel == targetTreeHeight)
{
pNode->pBox->Draw(pShader);
return;
}
if (pNode->pChild == NULL)
return;
// children
for (int i = 0; i < 8; ++i)
DrawAux(pShader, &(pNode->pChild[i]), currentLevel + 1);
}
}