7 changed files with 650 additions and 0 deletions
@ -0,0 +1,8 @@ |
|||
cmake_minimum_required(VERSION 3.27) |
|||
project(STL2Voxel) |
|||
|
|||
set(CMAKE_CXX_STANDARD 20) |
|||
|
|||
include_directories(.) |
|||
|
|||
add_executable(STL2Voxel main.cpp) |
@ -0,0 +1,177 @@ |
|||
#pragma once |
|||
#include <vector> |
|||
#include <cassert> |
|||
|
|||
template<typename T> |
|||
class Vec3 { |
|||
public: |
|||
Vec3(T a_, T b_, T c_) : a(a_), b(b_), c(c_) {} |
|||
Vec3() : a(0), b(0), c(0) {} |
|||
|
|||
union { T x, a; }; |
|||
union { T y, b; }; |
|||
union { T z, c; }; |
|||
|
|||
T operator[](int i) const { |
|||
if (i == 0) { |
|||
return a; |
|||
} else if (i == 1) { |
|||
return b; |
|||
} else if (i == 2) { |
|||
return c; |
|||
} |
|||
return a; |
|||
} |
|||
|
|||
T &operator[](int i) { |
|||
if (i == 0) { |
|||
return a; |
|||
} else if (i == 1) { |
|||
return b; |
|||
} else if (i == 2) { |
|||
return c; |
|||
} |
|||
return a; |
|||
} |
|||
|
|||
Vec3 operator+(const Vec3 &other) const { |
|||
return {a + other.a, b + other.b, c + other.c}; |
|||
} |
|||
|
|||
Vec3 operator/(const float w) const { |
|||
return {a / w, b / w, c / w}; |
|||
} |
|||
|
|||
Vec3 operator-(const Vec3 &other) const { |
|||
return {a - other.a, b - other.b, c - other.c}; |
|||
} |
|||
|
|||
Vec3 operator*(const float &w) const { |
|||
return {a * w, b * w, c * w}; |
|||
} |
|||
|
|||
float length() const { |
|||
return sqrt(a * a + b * b + c * c); |
|||
} |
|||
|
|||
Vec3 norm() const { |
|||
return *this / length(); |
|||
} |
|||
|
|||
Vec3 cross(const Vec3 &other) const { |
|||
return {b * other.c - c * other.b, c * other.a - a * other.c, a * other.b - b * other.a}; |
|||
} |
|||
|
|||
float dot(const Vec3 &other) const { |
|||
return a * other.a + b * other.b + c * other.c; |
|||
} |
|||
|
|||
bool operator==(const Vec3& other) const { |
|||
return x == other.x && y == other.y && z == other.z; |
|||
} |
|||
}; |
|||
|
|||
// 为 Vec3 创建哈希函数
|
|||
namespace std { |
|||
template<typename T> |
|||
struct hash<Vec3<T>> { |
|||
size_t operator()(const Vec3<T>& vec) const { |
|||
return hash<T>()(vec.x) ^ hash<T>()(vec.y) ^ hash<T>()(vec.z); |
|||
} |
|||
}; |
|||
} |
|||
|
|||
typedef Vec3<float> Vec3f; |
|||
typedef Vec3<unsigned int> Vec3u; |
|||
|
|||
class AABB { |
|||
public: |
|||
Vec3f min = {std::numeric_limits<float>::max(), std::numeric_limits<float>::max(), |
|||
std::numeric_limits<float>::max()}; |
|||
Vec3f max = {std::numeric_limits<float>::lowest(), std::numeric_limits<float>::lowest(), |
|||
std::numeric_limits<float>::lowest()}; |
|||
Vec3f center() const { |
|||
return (min + max) / 2; |
|||
} |
|||
Vec3f size() const { |
|||
return max - min; |
|||
} |
|||
AABB operator+(const Vec3f& v) const { |
|||
return {min + v, max + v}; |
|||
} |
|||
}; |
|||
|
|||
class Mesh { |
|||
public: |
|||
std::vector<Vec3f> vertices; |
|||
std::vector<Vec3u> faces; |
|||
// 获得指定面的 AABB
|
|||
AABB getAABB(const std::vector<size_t>& faceIndices) const { |
|||
AABB aabb; |
|||
for (const size_t &idx: faceIndices) { |
|||
const Vec3u &face = faces[idx]; |
|||
for (int i = 0; i < 3; ++i) { |
|||
const Vec3f &v = vertices[face[i]]; |
|||
for (int j = 0; j < 3; ++j) { |
|||
aabb.min[j] = std::min(aabb.min[j], v[j]); |
|||
aabb.max[j] = std::max(aabb.max[j], v[j]); |
|||
} |
|||
} |
|||
} |
|||
return aabb; |
|||
} |
|||
AABB getAABB() const { |
|||
AABB aabb; |
|||
for (const Vec3f &v: vertices) { |
|||
for (int j = 0; j < 3; ++j) { |
|||
aabb.min[j] = std::min(aabb.min[j], v[j]); |
|||
aabb.max[j] = std::max(aabb.max[j], v[j]); |
|||
} |
|||
} |
|||
return aabb; |
|||
} |
|||
}; |
|||
|
|||
|
|||
//class LineSegment {
|
|||
//public:
|
|||
// Vec3f start;
|
|||
// Vec3f end;
|
|||
//
|
|||
// /**
|
|||
// * @brief Construct a new Line Segment object
|
|||
// * @param s start point
|
|||
// * @param e end point
|
|||
// * @param r radius
|
|||
// */
|
|||
// LineSegment(const Vec3f &s, const Vec3f &e, const float r = 0) : start(s), end(e) {
|
|||
// length = (end - start).length();
|
|||
// dir = (end - start).norm();
|
|||
// }
|
|||
//
|
|||
// float getLength() const {
|
|||
// return length;
|
|||
// }
|
|||
//
|
|||
// Vec3f getDir() const {
|
|||
// return dir;
|
|||
// }
|
|||
//
|
|||
//private:
|
|||
// float length;
|
|||
// Vec3f dir;
|
|||
//};
|
|||
|
|||
class Ray { |
|||
public: |
|||
Vec3f start; |
|||
Vec3f dir; |
|||
|
|||
Ray(const Vec3f &s, const Vec3f &d) : start(s), dir(d) { |
|||
dir = dir.norm(); |
|||
} |
|||
}; |
|||
|
|||
|
|||
|
|||
|
@ -0,0 +1,281 @@ |
|||
#pragma once |
|||
|
|||
#include <numeric> |
|||
#include "cmath" |
|||
#include "algorithm" |
|||
#include "Geometry.hpp" |
|||
|
|||
struct IntersectRes { |
|||
bool hit; |
|||
float t; |
|||
}; |
|||
|
|||
IntersectRes |
|||
triangleRayIntersection(const Ray &ray, const Vec3f &a, const Vec3f &b, const Vec3f &c) { |
|||
Vec3f e1 = b - a; |
|||
Vec3f e2 = c - a; |
|||
Vec3f s = ray.start - a; |
|||
Vec3f s1 = ray.dir.cross(e2); |
|||
Vec3f s2 = s.cross(e1); |
|||
float s1_dot_e1 = s1.dot(e1); |
|||
float b1 = s1.dot(s) / s1_dot_e1; |
|||
float b2 = s2.dot(ray.dir) / s1_dot_e1; |
|||
if (b1 >= 0. && b2 >= 0. && b1 + b2 <= 1.) { |
|||
// hit
|
|||
return {true, s2.dot(e2) / s1_dot_e1}; |
|||
} |
|||
return {false, 0.}; |
|||
} |
|||
|
|||
bool isClose(double a, double b, double rel_tol = 1e-9, double abs_tol = 0.0) { |
|||
return std::fabs(a - b) <= std::max(rel_tol * std::max(std::fabs(a), std::fabs(b)), abs_tol); |
|||
} |
|||
|
|||
int sign(double x) { |
|||
return (x > 0) - (x < 0); |
|||
} |
|||
|
|||
float dot2(Vec3f a) { |
|||
return a.dot(a); |
|||
} |
|||
|
|||
float pointTriangleDistance(const Vec3f &p, const Vec3f &a, const Vec3f &b, const Vec3f &c) { |
|||
Vec3f ab = b - a, ap = p - a; |
|||
Vec3f bc = c - b, bp = p - b; |
|||
Vec3f ca = a - c, cp = p - c; |
|||
Vec3f nor = ab.cross(ca); |
|||
|
|||
return sqrt( |
|||
(sign(ab.cross(nor).dot(ap)) + |
|||
sign(bc.cross(nor).dot(bp)) + |
|||
sign(ca.cross(nor).dot(cp)) < 2.0) |
|||
? |
|||
fmin(fmin( |
|||
dot2(ab * std::clamp(ab.dot(ap) / dot2(ab), 0.0f, 1.0f) - ap), |
|||
dot2(bc * std::clamp(bc.dot(bp) / dot2(bc), 0.0f, 1.0f) - bp)), |
|||
dot2(ca * std::clamp(ca.dot(cp) / dot2(ca), 0.0f, 1.0f) - cp)) |
|||
: |
|||
nor.dot(ap) * nor.dot(ap) / dot2(nor)); |
|||
} |
|||
|
|||
class BVHNode { |
|||
public: |
|||
size_t left; |
|||
size_t right; |
|||
size_t parent; |
|||
AABB boundingBox; |
|||
|
|||
BVHNode(size_t l, size_t r, size_t p, const AABB &aabb) : left(l), right(r), parent(p), boundingBox(aabb) {} |
|||
|
|||
BVHNode() : left(0), right(0), parent(0), boundingBox() {} |
|||
}; |
|||
|
|||
|
|||
class BVH { |
|||
public: |
|||
const Mesh &mesh; |
|||
std::vector<Vec3f> faceCenters; |
|||
std::vector<BVHNode> nodes; |
|||
|
|||
BVH(const Mesh &mesh_) : mesh(mesh_) { |
|||
faceCenters.reserve(mesh.faces.size()); |
|||
for (const Vec3u &face: mesh.faces) { |
|||
Vec3f center = (mesh.vertices[face[0]] + mesh.vertices[face[1]] + mesh.vertices[face[2]]) / 3.0f; |
|||
faceCenters.push_back(center); |
|||
} |
|||
std::vector<size_t> indicesList(mesh.faces.size()); |
|||
std::iota(indicesList.begin(), indicesList.end(), 0); |
|||
nodes.resize(2 * mesh.faces.size() - 1); |
|||
size_t nowIdx = 0; |
|||
dfsBuild(indicesList, computeAABB(indicesList), nowIdx); |
|||
} |
|||
|
|||
size_t |
|||
dfsBuild(std::vector<size_t> &indicesList, AABB aabb, size_t &nowIdx) { |
|||
const size_t nodeIdx = nowIdx; |
|||
nowIdx++; |
|||
if (indicesList.size() == 1) { |
|||
// leaf
|
|||
nodes[nodeIdx] = {0, indicesList[0], 0, aabb}; |
|||
return nodeIdx; |
|||
} |
|||
// longest axis
|
|||
int longAxis = -1; |
|||
float longAxisLen = -1; |
|||
for (int i = 0; i < 3; ++i) { |
|||
const float axisLen = aabb.max[i] - aabb.min[i]; |
|||
if (axisLen > longAxisLen) { |
|||
longAxisLen = axisLen; |
|||
longAxis = i; |
|||
} |
|||
} |
|||
|
|||
// split indices list
|
|||
const size_t k = indicesList.size() / 2; |
|||
// std::nth_element(indicesList.begin(), indicesList.begin() + k - 1, indicesList.end(),
|
|||
// [&](const size_t &a, const size_t &b) {
|
|||
// return faceCenters[a][longAxis] < faceCenters[b][longAxis];
|
|||
// });
|
|||
nth(indicesList, k - 1, longAxis); |
|||
std::vector<size_t> leftIndices(indicesList.begin(), indicesList.begin() + k); |
|||
std::vector<size_t> rightIndices(indicesList.begin() + k, indicesList.end()); |
|||
|
|||
const AABB leftAABB = computeAABB(leftIndices); |
|||
const AABB rightAABB = computeAABB(rightIndices); |
|||
const size_t leftIdx = dfsBuild(leftIndices, leftAABB, nowIdx); |
|||
const size_t rightIdx = dfsBuild(rightIndices, rightAABB, nowIdx); |
|||
nodes[nodeIdx] = {leftIdx, rightIdx, 0, aabb}; |
|||
nodes[leftIdx].parent = nodeIdx; |
|||
nodes[rightIdx].parent = nodeIdx; |
|||
return nodeIdx; |
|||
} |
|||
|
|||
unsigned int intersectWithRay(const Ray &ray) const { |
|||
return recursiveRayIntersection(ray, 0); |
|||
} |
|||
|
|||
void writeBVHAsObj(const std::string &path) { |
|||
std::ofstream file(path); |
|||
for (const BVHNode &node: nodes) { |
|||
Vec3f vertex; |
|||
for (int i = 0; i < 2; ++i) { |
|||
vertex[0] = i == 0 ? node.boundingBox.min[0] : node.boundingBox.max[0]; |
|||
for (int j = 0; j < 2; ++j) { |
|||
vertex[1] = j == 0 ? node.boundingBox.min[1] : node.boundingBox.max[1]; |
|||
for (int k = 0; k < 2; ++k) { |
|||
vertex[2] = k == 0 ? node.boundingBox.min[2] : node.boundingBox.max[2]; |
|||
file << "v " << vertex[0] << " " << vertex[1] << " " << vertex[2] << std::endl; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
for (size_t i = 0; i < nodes.size(); ++i) { |
|||
const BVHNode &node = nodes[i]; |
|||
|
|||
file << "l " << i * 8 + 1 << " " << i * 8 + 2 << std::endl; |
|||
file << "l " << i * 8 + 3 << " " << i * 8 + 4 << std::endl; |
|||
file << "l " << i * 8 + 5 << " " << i * 8 + 6 << std::endl; |
|||
file << "l " << i * 8 + 7 << " " << i * 8 + 8 << std::endl; |
|||
file << "l " << i * 8 + 1 << " " << i * 8 + 3 << std::endl; |
|||
file << "l " << i * 8 + 2 << " " << i * 8 + 4 << std::endl; |
|||
file << "l " << i * 8 + 5 << " " << i * 8 + 7 << std::endl; |
|||
file << "l " << i * 8 + 6 << " " << i * 8 + 8 << std::endl; |
|||
file << "l " << i * 8 + 1 << " " << i * 8 + 5 << std::endl; |
|||
file << "l " << i * 8 + 2 << " " << i * 8 + 6 << std::endl; |
|||
file << "l " << i * 8 + 3 << " " << i * 8 + 7 << std::endl; |
|||
file << "l " << i * 8 + 4 << " " << i * 8 + 8 << std::endl; |
|||
|
|||
} |
|||
file.close(); |
|||
} |
|||
|
|||
void writeLeavesAsObj(const std::string &path) { |
|||
std::ofstream file(path); |
|||
for (const BVHNode &node: nodes) { |
|||
if (node.left == 0) { |
|||
Vec3f vertex; |
|||
for (int i = 0; i < 2; ++i) { |
|||
vertex[0] = i == 0 ? node.boundingBox.min[0] : node.boundingBox.max[0]; |
|||
for (int j = 0; j < 2; ++j) { |
|||
vertex[1] = j == 0 ? node.boundingBox.min[1] : node.boundingBox.max[1]; |
|||
for (int k = 0; k < 2; ++k) { |
|||
vertex[2] = k == 0 ? node.boundingBox.min[2] : node.boundingBox.max[2]; |
|||
file << "v " << vertex[0] << " " << vertex[1] << " " << vertex[2] << std::endl; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
for (size_t i = 0; i < nodes.size(); ++i) { |
|||
const BVHNode &node = nodes[i]; |
|||
if (node.left == 0) { |
|||
file << "l " << i * 8 + 1 << " " << i * 8 + 2 << std::endl; |
|||
file << "l " << i * 8 + 3 << " " << i * 8 + 4 << std::endl; |
|||
file << "l " << i * 8 + 5 << " " << i * 8 + 6 << std::endl; |
|||
file << "l " << i * 8 + 7 << " " << i * 8 + 8 << std::endl; |
|||
file << "l " << i * 8 + 1 << " " << i * 8 + 3 << std::endl; |
|||
file << "l " << i * 8 + 2 << " " << i * 8 + 4 << std::endl; |
|||
file << "l " << i * 8 + 5 << " " << i * 8 + 7 << std::endl; |
|||
file << "l " << i * 8 + 6 << " " << i * 8 + 8 << std::endl; |
|||
file << "l " << i * 8 + 1 << " " << i * 8 + 5 << std::endl; |
|||
file << "l " << i * 8 + 2 << " " << i * 8 + 6 << std::endl; |
|||
file << "l " << i * 8 + 3 << " " << i * 8 + 7 << std::endl; |
|||
file << "l " << i * 8 + 4 << " " << i * 8 + 8 << std::endl; |
|||
} |
|||
} |
|||
file.close(); |
|||
} |
|||
|
|||
private: |
|||
AABB computeAABB(const std::vector<size_t> &indices) { |
|||
AABB aabb; |
|||
for (const size_t &idx: indices) { |
|||
const Vec3u &face = mesh.faces[idx]; |
|||
for (int i = 0; i < 3; ++i) { |
|||
const Vec3f &vertex = mesh.vertices[face[i]]; |
|||
for (int j = 0; j < 3; ++j) { |
|||
aabb.min[j] = std::min(aabb.min[j], vertex[j]); |
|||
aabb.max[j] = std::max(aabb.max[j], vertex[j]); |
|||
} |
|||
} |
|||
} |
|||
return {aabb.min, aabb.max}; |
|||
} |
|||
|
|||
unsigned int recursiveRayIntersection(const Ray &ray, size_t nodeIdx) const { |
|||
// segment-box intersection test
|
|||
const AABB &aabb = nodes[nodeIdx].boundingBox; |
|||
bool hit = false; |
|||
for (int i = 0; !hit && i < 3; ++i) { |
|||
float t_min = (aabb.min[i] - ray.start[i]) / ray.dir[i]; |
|||
float t_max = (aabb.max[i] - ray.start[i]) / ray.dir[i]; |
|||
if (t_min > t_max) { |
|||
std::swap(t_min, t_max); |
|||
} |
|||
if (t_max < 0) return 0; |
|||
Vec3f hitPt = ray.start + ray.dir * t_min; |
|||
int otherPlane1 = (i + 1) % 3, otherPlane2 = (i + 2) % 3; |
|||
if (hitPt[otherPlane1] >= aabb.min[otherPlane1] && |
|||
hitPt[otherPlane1] <= aabb.max[otherPlane1] && |
|||
hitPt[otherPlane2] >= aabb.min[otherPlane2] && |
|||
hitPt[otherPlane2] <= aabb.max[otherPlane2]) { |
|||
hit = true; |
|||
} |
|||
} |
|||
if (hit) { |
|||
if (nodes[nodeIdx].left == 0) { |
|||
// leaf
|
|||
Vec3u face = mesh.faces[nodes[nodeIdx].right]; |
|||
IntersectRes res = triangleRayIntersection(ray, mesh.vertices[face[0]], mesh.vertices[face[1]], |
|||
mesh.vertices[face[2]]); |
|||
if (!res.hit || res.t < 0) return 0; |
|||
return 1; |
|||
} else { |
|||
// check children
|
|||
return recursiveRayIntersection(ray, nodes[nodeIdx].left) + |
|||
recursiveRayIntersection(ray, nodes[nodeIdx].right); |
|||
} |
|||
} |
|||
return 0; |
|||
} |
|||
|
|||
// implement nth, without std::nth_element
|
|||
// kth is 0-based
|
|||
void nth(std::vector<size_t> &indicesList, size_t kth, int longAxis) { |
|||
recursiveChoose(indicesList, 0, indicesList.size() - 1, kth, longAxis); |
|||
} |
|||
|
|||
void recursiveChoose(std::vector<size_t> &indicesList, size_t begin, size_t end, size_t kth, int longAxis) { |
|||
if (begin >= end) return; |
|||
int i = begin, j = end; |
|||
while (i < j) { |
|||
while (i < j && faceCenters[indicesList[j]][longAxis] >= faceCenters[indicesList[begin]][longAxis]) j--; |
|||
while (i < j && faceCenters[indicesList[i]][longAxis] <= faceCenters[indicesList[begin]][longAxis]) i++; |
|||
std::swap(indicesList[i], indicesList[j]); |
|||
} |
|||
std::swap(indicesList[begin], indicesList[i]); |
|||
if (i == kth) return; |
|||
if (i < kth) recursiveChoose(indicesList, i + 1, end, kth, longAxis); |
|||
else recursiveChoose(indicesList, begin, i - 1, kth, longAxis); |
|||
} |
|||
}; |
@ -0,0 +1,66 @@ |
|||
#pragma once |
|||
#include "Geometry.hpp" |
|||
#include <iostream> |
|||
#include <fstream> |
|||
#include <sstream> |
|||
#include <vector> |
|||
#include <string> |
|||
#include <unordered_map> |
|||
|
|||
bool readSTL(const std::string& filename, Mesh& mesh) { |
|||
std::ifstream file(filename); |
|||
if (!file.is_open()) { |
|||
std::cerr << "Failed to open file: " << filename << std::endl; |
|||
return false; |
|||
} |
|||
|
|||
std::string line; |
|||
std::unordered_map<Vec3<float>, unsigned int> vertexMap; // 用于去重
|
|||
unsigned int vertexIndex = 0; |
|||
|
|||
while (std::getline(file, line)) { |
|||
std::istringstream stream(line); |
|||
std::string keyword; |
|||
|
|||
if (line.find("facet normal") != std::string::npos) { |
|||
std::getline(file, line); // Read the outer loop
|
|||
|
|||
Vec3<float> vertices[3]; |
|||
for (int i = 0; i < 3; ++i) { |
|||
std::getline(file, line); |
|||
stream.clear(); |
|||
stream.str(line); |
|||
stream >> keyword >> vertices[i].x >> vertices[i].y >> vertices[i].z; |
|||
|
|||
// 检查是否已存在该顶点
|
|||
if (vertexMap.find(vertices[i]) == vertexMap.end()) { |
|||
vertexMap[vertices[i]] = vertexIndex; |
|||
mesh.vertices.push_back(vertices[i]); |
|||
vertexIndex++; |
|||
} |
|||
} |
|||
|
|||
// 添加面索引
|
|||
mesh.faces.emplace_back(vertexMap[vertices[0]], vertexMap[vertices[1]], vertexMap[vertices[2]]); |
|||
|
|||
std::getline(file, line); // Read endloop
|
|||
std::getline(file, line); // Read endfacet
|
|||
} |
|||
} |
|||
|
|||
file.close(); |
|||
return true; |
|||
} |
|||
|
|||
//int main() {
|
|||
// Mesh mesh;
|
|||
// if (readSTL("../plate.stl", mesh)) {
|
|||
// std::cout << "Successfully read STL file." << std::endl;
|
|||
// std::cout << "Vertices count: " << mesh.vertices.size() << std::endl;
|
|||
// std::cout << "Faces count: " << mesh.faces.size() << std::endl;
|
|||
// } else {
|
|||
// std::cerr << "Failed to read STL file." << std::endl;
|
|||
// }
|
|||
// return 0;
|
|||
//}
|
|||
|
@ -0,0 +1,85 @@ |
|||
#pragma once |
|||
|
|||
#include <vector> |
|||
#include "Intersection.hpp" |
|||
|
|||
class VoxelGrid { |
|||
private: |
|||
int width, height, depth, xySize; |
|||
Vec3<float> cellSize; |
|||
AABB aabb; |
|||
std::vector<uint8_t> data; // 每个体素只占用1 bit
|
|||
|
|||
public: |
|||
VoxelGrid(const AABB &aabb, const Vec3<float> &cellSize) |
|||
: cellSize(cellSize) { |
|||
// 根据 AABB 和 cellSize 计算宽、高、深
|
|||
width = static_cast<int>(std::lround((aabb.max.x - aabb.min.x) / cellSize.x)); |
|||
height = static_cast<int>(std::lround((aabb.max.y - aabb.min.y) / cellSize.y)); |
|||
depth = static_cast<int>(std::lround((aabb.max.z - aabb.min.z) / cellSize.z)); |
|||
|
|||
xySize = width * height; |
|||
int totalSize = (xySize * depth + 7) / 8; // 每8个体素占用一个字节
|
|||
data.resize(totalSize, 0); |
|||
this->aabb = aabb; // 使用传入的 AABB
|
|||
} |
|||
|
|||
VoxelGrid() = default; |
|||
|
|||
// 根据索引获取单元格的 AABB
|
|||
AABB getCellAABB(int x, int y, int z) const { |
|||
Vec3<float> cellMin(x * cellSize.x, y * cellSize.y, z * cellSize.z); |
|||
Vec3<float> cellMax((x + 1) * cellSize.x, (y + 1) * cellSize.y, (z + 1) * cellSize.z); |
|||
return AABB(cellMin, cellMax) + aabb.min; |
|||
} |
|||
|
|||
// 根据索引写入
|
|||
void setVoxel(int x, int y, int z, bool value) { |
|||
assert(x >= 0 && x < width && y >= 0 && y < height && z >= 0 && z < depth); |
|||
int index = x + y * width + z * xySize; |
|||
int byteIndex = index / 8; |
|||
int bitIndex = index % 8; |
|||
|
|||
if (value) { |
|||
data[byteIndex] |= (1 << bitIndex); // 设置为1
|
|||
} else { |
|||
data[byteIndex] &= ~(1 << bitIndex); // 设置为0
|
|||
} |
|||
} |
|||
|
|||
// 根据索引读取
|
|||
bool getVoxel(int x, int y, int z) const { |
|||
assert(x >= 0 && x < width && y >= 0 && y < height && z >= 0 && z < depth); |
|||
int index = x + y * width + z * xySize; |
|||
int byteIndex = index / 8; |
|||
int bitIndex = index % 8; |
|||
|
|||
return (data[byteIndex] >> bitIndex) & 1; // 返回位值
|
|||
} |
|||
|
|||
static VoxelGrid generateFromMesh(const Mesh &mesh, const Vec3<float> &cellSize) { |
|||
auto sceneAABB = mesh.getAABB(); |
|||
auto voxelGrid = VoxelGrid(sceneAABB, cellSize); |
|||
BVH bvh(mesh); |
|||
int solvedVoxel = 0; |
|||
for (int z = 0; z < voxelGrid.depth; ++z) { |
|||
for (int y = 0; y < voxelGrid.height; ++y) { |
|||
for (int x = 0; x < voxelGrid.width; ++x) { |
|||
auto start = voxelGrid.getCellAABB(x, y, z).center(); |
|||
auto dir = Vec3f(1, 2, 3).norm(); |
|||
if (bvh.intersectWithRay({start, dir}) % 2 == 1) { |
|||
voxelGrid.setVoxel(x, y, z, true); |
|||
} else { |
|||
voxelGrid.setVoxel(x, y, z, false); |
|||
} |
|||
|
|||
solvedVoxel++; |
|||
if (solvedVoxel % int(1e5) == 0) { |
|||
std::cout << "Solved " << solvedVoxel << " voxels." << std::endl; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
return voxelGrid; |
|||
} |
|||
}; |
@ -0,0 +1,24 @@ |
|||
#include <iostream> |
|||
#include "STLReader.hpp" |
|||
#include "Voxel.hpp" |
|||
#include <chrono> |
|||
|
|||
int main() { |
|||
Mesh mesh; |
|||
if (readSTL("../plate.stl", mesh)) { |
|||
std::cout << "Successfully read STL file." << std::endl; |
|||
std::cout << "Vertices count: " << mesh.vertices.size() << std::endl; |
|||
std::cout << "Faces count: " << mesh.faces.size() << std::endl; |
|||
} else { |
|||
std::cerr << "Failed to read STL file." << std::endl; |
|||
} |
|||
Vec3f cellSize = {0.1, 0.1, 0.1}; |
|||
// Vec3 cellSize = {0.01, 0.01, 0.01};
|
|||
// timer
|
|||
auto begin = std::chrono::steady_clock::now(); |
|||
auto voxelGrid = VoxelGrid::generateFromMesh(mesh, cellSize); |
|||
auto end = std::chrono::steady_clock::now(); |
|||
std::cout << "Time elapsed: " << std::chrono::duration_cast<std::chrono::milliseconds>(end - begin).count() << "ms" |
|||
<< std::endl; |
|||
return 0; |
|||
} |
Loading…
Reference in new issue