// sdf_visualizer.cpp // ────────────────────────────────────────────────────────────────────────────── // 依赖: // prim_gen.hpp → baked_blobtree_t、aabb_t、primitive_t … // base/subface.hpp → internal::get_eval_sdf_ptr、surface_type // // 文件格式(output_path): // 行 0 : grid_res y_slice // 行 1 : x0 x1 z0 z1 (世界坐标范围) // 行 2 : num_subfaces // 然后对每个 subface 重复: // 行 : surface_type_int // N 行 : N 个空格分隔的 float(iz 行,ix 列,N = grid_res+1) // ────────────────────────────────────────────────────────────────────────────── #include "sdf_visualizer.hpp" #include // internal::get_eval_sdf_ptr, surface_type #include #include #include #include #include #include void dump_sdf_slice(const baked_blobtree_t& tree, const std::string& output_path, double y_slice, int grid_res, double extra_margin) { // ── 0. 基本校验 ──────────────────────────────────────────────────────── if (tree.subfaces.empty()) { std::cerr << "[SDF_VIZ] No subfaces — nothing to dump.\n"; return; } // ── 1. 计算场景 AABB(含边距)────────────────────────────────────────── aabb_t scene_aabb{}; for (const auto& prim : tree.primitives) { scene_aabb.extend(prim->fetch_aabb()); } if (scene_aabb.isEmpty()) { std::cerr << "[SDF_VIZ] Scene AABB is invalid.\n"; return; } const double base_margin = scene_aabb.sizes().maxCoeff() * extra_margin + 0.05; scene_aabb.min().array() -= base_margin; scene_aabb.max().array() += base_margin; const double x0 = scene_aabb.min().x(), x1 = scene_aabb.max().x(); const double z0 = scene_aabb.min().z(), z1 = scene_aabb.max().z(); const int N = grid_res + 1; // 每轴采样点数 const uint32_t ns = static_cast(tree.subfaces.size()); std::cout << "[SDF_VIZ] Grid " << N << "×" << N << " | " << ns << " subface(s)" << " | Y=" << y_slice << " | X[" << x0 << ", " << x1 << "]" << " | Z[" << z0 << ", " << z1 << "]\n"; // ── 2. 对每个 subface 在网格上采样 SDF ──────────────────────────────── // grids[s][iz * N + ix] = SDF 值(float 节省内存) std::vector> grids(ns, std::vector(N * N, 0.0f)); for (uint32_t s = 0; s < ns; ++s) { // 不再调用 .raw() 获取原始指针,而是直接使用包装器对象 const auto& subface_wrapper = tree.subfaces[s]; // 使用引用,避免拷贝 surface_type subface_type = tree.subface_types[s]; auto sdf_fn = internal::get_eval_sdf_ptr(subface_type); if (!sdf_fn) { std::cout << "[SDF_VIZ] subface[" << s << "] type=" << static_cast(subface_type) << " → null eval_sdf, skipped.\n"; continue; } for (int iz = 0; iz < N; ++iz) { const double z = z0 + (z1 - z0) * static_cast(iz) / grid_res; for (int ix = 0; ix < N; ++ix) { const double x = x0 + (x1 - x0) * static_cast(ix) / grid_res; const Eigen::Vector3d pos(x, y_slice, z); // 传递包装器对象,而不是原始指针 grids[s][iz * N + ix] = static_cast(sdf_fn(subface_wrapper, pos)); } } std::cout << "[SDF_VIZ] subface[" << s << "] type=" << static_cast(subface_type) << " ✓\n"; } // ── 3. 写文件 ────────────────────────────────────────────────────────── std::ofstream ofs(output_path); if (!ofs) { std::cerr << "[SDF_VIZ] Cannot open: " << output_path << "\n"; return; } // 文件头 ofs << grid_res << ' ' << y_slice << '\n'; ofs << x0 << ' ' << x1 << ' ' << z0 << ' ' << z1 << '\n'; ofs << ns << '\n'; // 每个 subface 的数据块 for (uint32_t s = 0; s < ns; ++s) { ofs << static_cast(tree.subface_types[s]) << '\n'; for (int iz = 0; iz < N; ++iz) { for (int ix = 0; ix < N; ++ix) { if (ix) ofs << ' '; ofs << grids[s][iz * N + ix]; } ofs << '\n'; } } std::cout << "[SDF_VIZ] Written → " << output_path << '\n'; }