how to use morton code to build octree on a obj format mesh C++
To use Morton code to build an octree on a .obj format mesh in C++, you can follow these steps:
- Load the .obj file into your program and extract the vertex positions and face indices.
- Calculate the bounding box of the mesh using the vertex positions.
- Divide the bounding box into eight equal sub-boxes to create the root nodes of the octree.
- For each vertex in the mesh, calculate its Morton code based on its position relative to the bounding box.
- Traverse the octree from the root nodes and insert each vertex into the appropriate leaf node based on its Morton code.
- For each face in the mesh, calculate its bounding box and traverse the octree to find the nodes that intersect with the box.
- For each intersecting node, add the face to the list of faces stored in that node.
- If a leaf node contains more than a certain number of vertices or faces, subdivide it into eight child nodes and redistribute the vertices and faces among them.
- Repeat steps 4-8 until all vertices and faces have been inserted into the octree.
Here is some sample code that demonstrates how to use Morton code to build an octree:
#include <vector>
#include <Eigen/Dense>
using namespace Eigen;
// Node structure for octree
struct OctreeNode {
Vector3d minCorner;
Vector3d maxCorner;
std::vector<Vector3d> vertices;
std::vector<int> faces;
OctreeNode* children[8];
};
// Calculate Morton code for a point
uint64_t mortonCode(Vector3d point, Vector3d minCorner, Vector3d maxCorner) {
uint64_t code = 0;
for (int i = 0; i < 3; i++) {
double normalizedCoord = (point[i] - minCorner[i]) / (maxCorner[i] - minCorner[i]);
normalizedCoord = std::min(std::max(normalizedCoord, 0.0), 1.0);
uint64_t mortonCoord = normalizedCoord * 1023;
code |= (mortonCoord << (i * 10));
}
return code;
}
// Insert a vertex into the octree
void insertVertex(OctreeNode* node, Vector3d vertex, Vector3d minCorner, Vector3d maxCorner) {
uint64_t code = mortonCode(vertex, minCorner, maxCorner);
for (int i = 0; i < 8; i++) {
Vector3d childMin = node->minCorner + 0.5 * (node->maxCorner - node->minCorner).cwiseProduct(Vector3d(i & 1, (i >> 1) & 1, (i >> 2) & 1));
Vector3d childMax = node->minCorner + 0.5 * (node->maxCorner - node->minCorner).cwiseProduct(Vector3d((i & 1) + 1, ((i >> 1) & 1) + 1, ((i >> 2) & 1) + 1));
if (code >= mortonCode(childMin, minCorner, maxCorner) && code <= mortonCode(childMax, minCorner, maxCorner)) {
if (node->children[i] == nullptr) {
node->children[i] = new OctreeNode;
node->children[i]->minCorner = childMin;
node->children[i]->maxCorner = childMax;
}
insertVertex(node->children[i], vertex, minCorner, maxCorner);
break;
}
}
node->vertices.push_back(vertex);
}
// Insert a face into the octree
void insertFace(OctreeNode* node, MatrixXd vertices, Vector3i face, Vector3d minCorner, Vector3d maxCorner) {
Vector3d faceMin = vertices.row(face.minCoeff()).transpose().cwiseMin(vertices.row(face.maxCoeff()).transpose()).cwiseMin(vertices.row(face(1)).transpose());
Vector3d faceMax = vertices.row(face.maxCoeff()).transpose().cwiseMax(vertices.row(face.minCoeff()).transpose()).cwiseMax(vertices.row(face(1)).transpose());
for (int i = 0; i < 8; i++) {
Vector3d childMin = node->minCorner + 0.5 * (node->maxCorner - node->minCorner).cwiseProduct(Vector3d(i & 1, (i >> 1) & 1, (i >> 2) & 1));
Vector3d childMax = node->minCorner + 0.5 * (node->maxCorner - node->minCorner).cwiseProduct(Vector3d((i & 1) + 1, ((i >> 1) & 1) + 1, ((i >> 2) & 1) + 1));
if (faceMin[0] <= childMax[0] && faceMax[0] >= childMin[0] &&
faceMin[1] <= childMax[1] && faceMax[1] >= childMin[1] &&
faceMin[2] <= childMax[2] && faceMax[2] >= childMin[2]) {
if (node->children[i] != nullptr) {
insertFace(node->children[i], vertices, face, minCorner, maxCorner);
} else {
node->faces.push_back(face);
}
}
}
}
// Subdivide a node into eight child nodes
void subdivideNode(OctreeNode* node, Vector3d minCorner, Vector3d maxCorner) {
Vector3d center = 0.5 * (node->minCorner + node->maxCorner);
for (int i = 0; i < 8; i++) {
Vector3d childMin = node->minCorner + 0.5 * (node->maxCorner - node->minCorner).cwiseProduct(Vector3d(i & 1, (i >> 1) & 1, (i >> 2) & 1));
Vector3d childMax = node->minCorner + 0.5 * (node->maxCorner - node->minCorner).cwiseProduct(Vector3d((i & 1) + 1, ((i >> 1) & 1) + 1, ((i >> 2) & 1) + 1));
node->children[i] = new OctreeNode;
node->children[i]->minCorner = childMin;
node->children[i]->maxCorner = childMax;
for (Vector3d vertex : node->vertices) {
insertVertex(node->children[i], vertex, minCorner, maxCorner);
}
for (int faceIdx : node->faces) {
insertFace(node->children[i], vertices, faces.row(faceIdx), minCorner, maxCorner);
}
}
node->vertices.clear();
node->faces.clear();
}
// Build an octree from a mesh
OctreeNode* buildOctree(MatrixXd vertices, MatrixXi faces, int maxVerticesPerNode, int maxFacesPerNode) {
Vector3d minCorner = vertices.colwise().minCoeff();
Vector3d maxCorner = vertices.colwise().maxCoeff();
OctreeNode* root = new OctreeNode;
root->minCorner = minCorner;
root->maxCorner = maxCorner;
for (int i = 0; i < vertices.rows(); i++) {
insertVertex(root, vertices.row(i).transpose(), minCorner, maxCorner);
}
for (int i = 0; i < faces.rows(); i++) {
insertFace(root, vertices, faces.row(i), minCorner, maxCorner);
}
std::vector<OctreeNode*> nodesToSubdivide = {root};
while (!nodesToSubdivide.empty()) {
OctreeNode* node = nodesToSubdivide.back();
nodesToSubdivide.pop_back();
if (node->vertices.size() > maxVerticesPerNode || node->faces.size() > maxFacesPerNode) {
subdivideNode(node, minCorner, maxCorner);
for (int i = 0; i < 8; i++) {
nodesToSubdivide.push_back(node->children[i]);
}
}
}
return root;
}
原文地址: http://www.cveoy.top/t/topic/bAYp 著作权归作者所有。请勿转载和采集!