124 lines
3.6 KiB
C++
124 lines
3.6 KiB
C++
#pragma once
|
||
#include "vde/core/point.h"
|
||
#include <vector>
|
||
#include <array>
|
||
#include <functional>
|
||
#include <cmath>
|
||
#include <algorithm>
|
||
|
||
namespace vde::mesh {
|
||
using core::Point2D;
|
||
using core::Point3D;
|
||
using core::Vector3D;
|
||
|
||
/**
|
||
* @brief Marching Cubes 输出网格
|
||
* @ingroup mesh
|
||
*/
|
||
struct MCMesh {
|
||
std::vector<Point3D> vertices; ///< 顶点坐标
|
||
std::vector<std::array<int, 3>> triangles; ///< 三角形索引
|
||
};
|
||
|
||
/**
|
||
* @brief Marching Cubes 等值面提取
|
||
*
|
||
* 从标量场 f(x,y,z) 中提取等值面 S = { (x,y,z) | f(x,y,z) = iso_level }。
|
||
* 将包围盒划分为 resolution×resolution×resolution 体素网格,
|
||
* 在每个体素的 8 个角求值 f,查表确定等值面穿越该体素的三角形配置。
|
||
*
|
||
* @param f 标量场函数 f(x,y,z) → double
|
||
* @param iso_level 等值面值(默认 0)
|
||
* @param bmin 包围盒最小角
|
||
* @param bmax 包围盒最大角
|
||
* @param resolution 每轴采样分辨率(≥ 1),总网格 = resolution³ 个立方体
|
||
* @return MCMesh 三角形网格
|
||
*
|
||
* @note 使用经典的 256 查表法(Lorensen & Cline 1987)
|
||
* @code{.cpp}
|
||
* // 提取球面等值面
|
||
* auto f = [](double x,double y,double z) {
|
||
* return sdf_sphere(x,y,z, 0,0,0, 1.0);
|
||
* };
|
||
* auto mesh = marching_cubes(f, 0.0,
|
||
* {-2,-2,-2}, {2,2,2}, 64);
|
||
* @endcode
|
||
* @see sdf_sphere sdf_box
|
||
* @ingroup mesh
|
||
*/
|
||
MCMesh marching_cubes(const std::function<double(double,double,double)>& f,
|
||
double iso_level,
|
||
const Point3D& bmin, const Point3D& bmax,
|
||
int resolution);
|
||
|
||
/**
|
||
* @brief SDF 球体函数
|
||
*
|
||
* @param x,y,z 采样点坐标
|
||
* @param cx,cy,cz 球心坐标
|
||
* @param r 半径
|
||
* @return 有符号距离:内部 < 0,表面 = 0,外部 > 0
|
||
*
|
||
* @code{.cpp}
|
||
* auto sphere_sdf = [](double x,double y,double z) {
|
||
* return sdf_sphere(x,y,z, 0,0,0, 1.0);
|
||
* };
|
||
* @endcode
|
||
* @ingroup mesh
|
||
*/
|
||
inline double sdf_sphere(double x, double y, double z, double cx, double cy, double cz, double r) {
|
||
return std::sqrt((x-cx)*(x-cx) + (y-cy)*(y-cy) + (z-cz)*(z-cz)) - r;
|
||
}
|
||
|
||
/**
|
||
* @brief SDF 立方体函数(轴对齐,中心在原点)
|
||
*
|
||
* @param x,y,z 采样点坐标
|
||
* @param hx,hy,hz 半边长
|
||
* @return 有符号距离
|
||
*
|
||
* @ingroup mesh
|
||
*/
|
||
inline double sdf_box(double x, double y, double z, double hx, double hy, double hz) {
|
||
double dx = std::abs(x) - hx, dy = std::abs(y) - hy, dz = std::abs(z) - hz;
|
||
return std::sqrt(std::max(dx,0.0)*std::max(dx,0.0)
|
||
+ std::max(dy,0.0)*std::max(dy,0.0)
|
||
+ std::max(dz,0.0)*std::max(dz,0.0))
|
||
+ std::min(std::max({dx,dy,dz}), 0.0);
|
||
}
|
||
|
||
/**
|
||
* @brief SDF 平滑并集(Smooth Union)
|
||
*
|
||
* 对两个 SDF 做平滑混合,k 控制过渡区宽度。
|
||
*
|
||
* @param d1,d2 两个 SDF 值
|
||
* @param k 平滑宽度(> 0),值越大过渡越平滑
|
||
* @return 混合后的 SDF 值
|
||
*
|
||
* @ingroup mesh
|
||
*/
|
||
namespace { inline double lerp_impl(double a, double b, double t) { return a + t * (b - a); } }
|
||
inline double sdf_smooth_union(double d1, double d2, double k) {
|
||
double h = std::clamp(0.5 + 0.5*(d2-d1)/k, 0.0, 1.0);
|
||
return lerp_impl(d2, d1, h) - k*h*(1.0-h);
|
||
}
|
||
|
||
/**
|
||
* @brief SDF 平滑差集(Smooth Subtraction)
|
||
*
|
||
* d1 减去 d2,过渡区由 k 控制。
|
||
*
|
||
* @param d1,d2 两个 SDF 值
|
||
* @param k 平滑宽度(> 0)
|
||
* @return 混合后的 SDF 值
|
||
*
|
||
* @ingroup mesh
|
||
*/
|
||
inline double sdf_smooth_subtraction(double d1, double d2, double k) {
|
||
double h = std::clamp(0.5 - 0.5*(d2+d1)/k, 0.0, 1.0);
|
||
return lerp_impl(d2, -d1, h) + k*h*(1.0-h);
|
||
}
|
||
|
||
} // namespace vde::mesh
|