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