extract explicit mesh with topology information from implicit surfaces with boolean operations, and do surface/volume integrating on them.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

389 lines
19 KiB

#include <algorithm/glue_algorithm.hpp>
#include <extract_patch.hpp>
#include <implicit_arrangement.hpp>
#include <patch_connectivity.hpp>
#include <pair_faces.hpp>
#include <topology_ray_shooting.hpp>
#include <globals.hpp>
#include <internal_api.hpp>
#include <primitive_process.hpp>
#include <implicit_surface_network_processor.hpp>
#include "Eigen/src/Core/Matrix.h"
ImplicitSurfaceNetworkProcessor g_processor{};
void ImplicitSurfaceNetworkProcessor::preinit(const virtual_node_t& tree_node) noexcept
{
auto leaf_indices = blobtree_get_leaf_nodes(tree_node.main_index);
virtual_node_t pointer = tree_node;
// 1. merge aabbs
// 2. build mapping: primitive index -> leaf node index
aabb_t scene_aabb{};
leaf_index_of_primitive.resize(get_primitive_count());
for (const auto& leaf_index : leaf_indices) {
pointer.inner_index = leaf_index;
const uint32_t primitive_index = node_fetch_primitive_index(blobtree_get_node(pointer));
leaf_index_of_primitive[primitive_index] = leaf_index;
const auto& type = get_primitive_node(primitive_index).type;
if (type != PRIMITIVE_TYPE_CONSTANT && type != PRIMITIVE_TYPE_PLANE) scene_aabb.extend(get_aabb(primitive_index));
}
// update background mesh using scene aabb
// EDIT: scene aabb with a little margin
this->background_mesh_manager.generate(scene_aabb.min - g_settings.scene_aabb_margin * Eigen::Vector3d::Ones(),
scene_aabb.max + g_settings.scene_aabb_margin * Eigen::Vector3d::Ones());
}
void ImplicitSurfaceNetworkProcessor::clear() noexcept
{
iso_vertices.clear();
polygon_faces.clear();
vertex_counts_of_face.clear();
}
solve_result_t ImplicitSurfaceNetworkProcessor::run(const virtual_node_t& tree_node) noexcept
{
const auto& background_vertices = background_mesh_manager.get_vertices();
const auto& background_indices = background_mesh_manager.get_indices();
if (background_vertices.empty() || background_indices.empty()) {
std::cout << "Current network processor is runned before initialized!" << std::endl;
return {};
}
const auto num_vert = background_vertices.size();
const auto num_tets = background_indices.size();
const auto num_funcs = get_primitive_count();
// temporary geometry results
stl_vector_mp<polygon_face_t> iso_faces{}; ///< Polygonal faces at the surface network mesh
stl_vector_mp<stl_vector_mp<uint32_t>> patches{}; ///< A connected component of faces bounded by non-manifold edges
stl_vector_mp<iso_edge_t> iso_edges{}; ///< Edges at the surface network mesh
stl_vector_mp<stl_vector_mp<uint32_t>> chains{}; ///< Chains of non-manifold edges
stl_vector_mp<stl_vector_mp<uint32_t>> non_manifold_edges_of_vert{}; ///< Indices of non-manifold vertices
stl_vector_mp<stl_vector_mp<uint32_t>>
shells{}; ///< An array of shells. Each shell is a connected component consist of patches. Even patch index, 2*i,
///< indicates patch i is consistently oriented with the shell. Odd patch index, 2*i+1, indicates patch i has
///< opposite orientation with respect to the shell.
stl_vector_mp<stl_vector_mp<uint32_t>>
arrangement_cells{}; ///< A 3D region partitioned by the surface network; encoded by a vector of shell indices
// compute function signs at vertices
// EDIT: we only need to identify the sdf value is inside or on surface/outside
// Eigen::Matrix<int8_t, Eigen::Dynamic, Eigen::Dynamic> scalar_field_signs(num_funcs, num_vert);
auto scalar_field_sign = [](double x) -> int8_t { return (x < 0) ? 1 : ((x > 0) ? -1 : 0); };
stl_vector_mp<stl_vector_mp<double>> vertex_scalar_values(num_vert, stl_vector_mp<double>(num_funcs));
stl_vector_mp<bool> is_positive_scalar_field_sign(num_funcs * num_vert, false);
stl_vector_mp<bool> is_negative_scalar_field_sign(num_funcs * num_vert, false);
stl_vector_mp<bool> is_degenerate_vertex(num_vert, false);
bool has_degenerate_vertex{};
{
g_timers_manager.push_timer("identify sdf signs");
for (uint32_t i = 0; i < num_vert; ++i) {
const auto& point = background_vertices[i];
for (uint32_t j = 0; j < num_funcs; ++j) {
vertex_scalar_values[i][j] = evaluate(j, point);
const auto sign = scalar_field_sign(vertex_scalar_values[i][j]);
switch (sign) {
case -1: is_negative_scalar_field_sign[i * num_funcs + j] = true; break;
case 0:
is_degenerate_vertex[i] = true;
has_degenerate_vertex = true;
break;
case 1: is_positive_scalar_field_sign[i * num_funcs + j] = true; break;
default: break;
}
}
}
g_timers_manager.pop_timer("identify sdf signs");
}
// filter active functions in each tetrahedron
// TODO: optimize this part by using SIMD and ranges::filter
uint32_t num_intersecting_tet = 0;
stl_vector_mp<uint32_t> active_functions_in_tet{}; // active function indices in CRS vector format
stl_vector_mp<uint32_t> start_index_of_tet{};
{
g_timers_manager.push_timer("filter active functions");
active_functions_in_tet.reserve(num_tets);
start_index_of_tet.reserve(num_tets + 1);
start_index_of_tet.emplace_back(0);
for (Eigen::Index i = 0; i < num_tets; ++i) {
for (Eigen::Index j = 0; j < num_funcs; ++j) {
uint32_t pos_count{}, neg_count{};
for (uint32_t k = 0; k < 4; ++k) {
if (is_positive_scalar_field_sign[background_indices[i][k] * num_funcs + j]) pos_count++;
if (is_negative_scalar_field_sign[background_indices[i][k] * num_funcs + j]) neg_count++;
}
// if (scalar_field_signs(j, tet_ptr[k]) == 1) pos_count++;
// tets[i].size() == 4, this means that the function is active in this tet
if (pos_count < 4 && neg_count < 4) active_functions_in_tet.emplace_back(j);
}
if (active_functions_in_tet.size() > start_index_of_tet.back()) { ++num_intersecting_tet; }
start_index_of_tet.emplace_back(static_cast<uint32_t>(active_functions_in_tet.size()));
}
g_timers_manager.pop_timer("filter active functions");
}
// compute arrangement in each tet
// HINT: we skip robust test for this part for now
stl_vector_mp<std::shared_ptr<arrangement_t>> cut_results{};
uint32_t num_1_func = 0;
uint32_t num_2_func = 0;
uint32_t num_more_func = 0;
{
// g_timers_manager.push_timer("implicit arrangements calculation in total");
cut_results.reserve(num_intersecting_tet);
stl_vector_mp<plane_t> planes{};
planes.reserve(3);
for (uint32_t i = 0; i < num_tets; ++i) {
const auto start_index = start_index_of_tet[i];
const auto active_funcs_in_curr_tet = start_index_of_tet[i + 1] - start_index;
if (active_funcs_in_curr_tet == 0) {
cut_results.emplace_back(nullptr);
continue;
}
g_timers_manager.push_timer(active_funcs_in_curr_tet == 1
? "implicit arrangements calculation (1 func)"
: (active_funcs_in_curr_tet == 2
? "implicit arrangments calculation (2 funcs)"
: "implicit arrangements calculation (>= 3 funcs)"));
const auto& tet = background_indices[i];
planes.clear();
for (uint32_t j = 0; j < active_funcs_in_curr_tet; ++j) {
const auto fid = active_functions_in_tet[start_index + j];
planes.emplace_back(plane_t{-vertex_scalar_values[tet[0]][fid],
-vertex_scalar_values[tet[1]][fid],
-vertex_scalar_values[tet[2]][fid],
-vertex_scalar_values[tet[3]][fid]});
}
cut_results.emplace_back(std::make_shared<arrangement_t>(std::move(compute_arrangement(planes))));
switch (active_funcs_in_curr_tet) {
case 1:
g_timers_manager.pop_timer("implicit arrangements calculation (1 func)");
++num_1_func;
break;
case 2:
g_timers_manager.pop_timer("implicit arrangments calculation (2 funcs)");
++num_2_func;
break;
default:
g_timers_manager.pop_timer("implicit arrangements calculation (>= 3 funcs)");
++num_more_func;
break;
}
}
// g_timers_manager.pop_timer("implicit arrangements calculation in total");
}
// extract arrangement mesh: combining results from all tets to produce a mesh
// compute xyz coordinates of iso-vertices on the fly
// HINT: vertices of faces are always oriented counterclockwise from the view of the positive side of the supporting plane
// but since the sign is reversed, so that every face is always oriented clockwise when viewing outside
stl_vector_mp<iso_vertex_t> iso_verts{};
{
g_timers_manager.push_timer("extract arrangement & iso mesh");
extract_iso_mesh(num_1_func,
num_2_func,
num_more_func,
cut_results,
active_functions_in_tet,
start_index_of_tet,
background_mesh_manager.identity(),
vertex_scalar_values,
iso_vertices,
iso_verts,
iso_faces);
g_timers_manager.pop_timer("extract arrangement & iso mesh");
}
// compute iso-edges and edge-face connectivity
stl_vector_mp<stl_vector_mp<uint32_t>> edges_of_iso_face{};
{
g_timers_manager.push_timer("compute iso-edge and edge-face connectivity");
compute_patch_edges(iso_faces, edges_of_iso_face, iso_edges);
g_timers_manager.pop_timer("compute iso-edge and edge-face connectivity");
}
// group iso-faces into patches
// compute map: iso-face Id --> patch Id
stl_vector_mp<uint32_t> patch_of_face(iso_faces.size());
{
g_timers_manager.push_timer("group iso-faces into patches");
compute_patches(edges_of_iso_face, iso_edges, iso_faces, patches, patch_of_face);
g_timers_manager.pop_timer("group iso-faces into patches");
}
// compute surface and volume integrals of patches
stl_vector_mp<double> surf_int_of_patch{};
stl_vector_mp<double> vol_int_of_patch{};
{
g_timers_manager.push_timer("compute surface and volume integrals of patches");
surf_int_of_patch.reserve(patches.size());
vol_int_of_patch.reserve(patches.size());
for (const auto& face_of_patch_mapping : patches) {
const auto& [surf_int, vol_int] = patch_integrator.integrate(iso_vertices, iso_faces, face_of_patch_mapping);
surf_int_of_patch.emplace_back(std::move(surf_int));
vol_int_of_patch.emplace_back(std::move(vol_int));
}
g_timers_manager.pop_timer("compute surface and volume integrals of patches");
}
// group non-manifold iso-edges into chains
{
g_timers_manager.push_timer("group non-manifold iso-edges into chains");
non_manifold_edges_of_vert.resize(iso_verts.size());
// get incident non-manifold edges for iso-vertices
for (uint32_t i = 0; i < iso_edges.size(); i++) {
if (iso_edges[i].headers.size() > 2) { // non-manifold edge (not a boundary edge)
// there is only one patch incident to a boundary edge,
// so there is no need to figure out the "order" of patches around a boundary
// edge
non_manifold_edges_of_vert[iso_edges[i].v1].emplace_back(i);
non_manifold_edges_of_vert[iso_edges[i].v2].emplace_back(i);
}
}
// group non-manifold iso-edges into chains
compute_chains(iso_edges, non_manifold_edges_of_vert, chains);
g_timers_manager.pop_timer("group non-manifold iso-edges into chains");
}
// compute incident tets for degenerate vertices
flat_hash_map_mp<uint32_t, stl_vector_mp<uint32_t>> incident_tets{};
{
g_timers_manager.push_timer("compute incident tets for degenerate vertices");
if (has_degenerate_vertex) {
for (uint32_t i = 0; i < num_tets; ++i) {
for (uint32_t j = 0; j < 4; ++j) {
if (is_degenerate_vertex[background_indices[i][j]]) {
incident_tets[background_indices[i][j]].emplace_back(i);
}
}
}
}
g_timers_manager.pop_timer("compute incident tets for degenerate vertices");
}
// compute order of patches around chains
// (patch i, 1) <--> 2i, (patch i, -1) <--> 2i+1
// compute half-patch adjacency list
// stl_vector_mp<stl_vector_mp<half_patch_pair_t>> half_patch_pair_list{};
stl_vector_mp<stl_vector_mp<uint32_t>> half_patch_adj_list(2 * patches.size());
{
g_timers_manager.push_timer("compute order of patches around chains");
// half_patch_pair_list.resize(chains.size());
// order iso-faces incident to each representative iso-edge
for (uint32_t i = 0; i < chains.size(); i++) {
// pick first iso-edge from each chain as representative
const auto& iso_edge = iso_edges[chains[i][0]];
// with degeneracy handling
compute_patch_order(iso_edge,
background_indices,
iso_verts,
iso_faces,
cut_results,
active_functions_in_tet,
start_index_of_tet,
incident_tets,
patch_of_face,
half_patch_adj_list);
// half_patch_pair_list[i]);
}
g_timers_manager.pop_timer("compute order of patches around chains");
}
// group patches into shells and components
// each shell is represented as a list of half-patch indices
// each component is represented as a list of patch indices
stl_vector_mp<uint32_t> shell_of_half_patch{};
stl_vector_mp<stl_vector_mp<uint32_t>> components{};
stl_vector_mp<uint32_t> component_of_patch{};
{
g_timers_manager.push_timer("group patches into shells and components");
compute_shells_and_components(half_patch_adj_list, shells, shell_of_half_patch, components, component_of_patch);
g_timers_manager.pop_timer("group patches into shells and components");
}
// resolve nesting order, compute arrangement cells
// an arrangement cell is represented by a list of bounding shells
// get solve result by propagation
solve_result_t result{};
{
g_timers_manager.push_timer("compute arrangement cells");
if (components.size() == 1) { // no nesting problem, each shell is an arrangement cell
{
// only the -1 side shell is a valid arrangement cell
auto& shells = arrangement_cells.emplace_back(1);
shells.emplace_back(1);
// arrangement_cells.reserve(shells.size());
// for (uint32_t i = 0; i < shells.size(); ++i) {
// arrangement_cells.emplace_back(1);
// arrangement_cells.back()[0] = i;
// }
}
result.mesh.vertices = reinterpret_cast<const raw_vector3d_t*>(iso_vertices.data());
result.mesh.num_vertices = static_cast<uint32_t>(iso_vertices.size());
for (const auto& half_patch : shells[1]) {
result.surf_int_result += surf_int_of_patch[half_patch / 2];
result.vol_int_result += -vol_int_of_patch[half_patch / 2];
for (const auto& face_id : patches[half_patch / 2]) {
const auto& face = iso_faces[face_id];
polygon_faces.insert(polygon_faces.end(),
std::make_move_iterator(polygon_faces.begin()),
std::make_move_iterator(polygon_faces.end()));
vertex_counts_of_face.emplace_back(face.vertex_indices.size());
}
}
result.mesh.faces = polygon_faces.data();
result.mesh.num_faces = static_cast<uint32_t>(polygon_faces.size());
result.mesh.vertex_counts = vertex_counts_of_face.data();
} else { // resolve nesting order
g_timers_manager.push_timer("arrangement cells: topo ray shooting");
stl_vector_mp<std::pair<uint32_t, uint32_t>> shell_links{};
topo_ray_shooting(background_mesh_manager.identity(),
cut_results,
iso_verts,
iso_faces,
patches,
patch_of_face,
shells,
shell_of_half_patch,
components,
component_of_patch,
shell_links);
g_timers_manager.pop_timer("arrangement cells: topo ray shooting");
// group shells into arrangement cells
g_timers_manager.push_timer("arrangement cells: group shells");
compute_arrangement_cells(static_cast<uint32_t>(shells.size()), shell_links, arrangement_cells);
g_timers_manager.pop_timer("arrangement cells: group shells");
// propagate solve result
g_timers_manager.push_timer("arrangement cells: propagate solve result");
result = std::move(patch_propagator.execute(tree_node,
leaf_index_of_primitive,
iso_vertices,
iso_faces,
patches,
surf_int_of_patch,
vol_int_of_patch,
arrangement_cells,
shell_of_half_patch,
shells,
polygon_faces,
vertex_counts_of_face));
g_timers_manager.pop_timer("arrangement cells: propagate solve result");
}
g_timers_manager.pop_timer("compute arrangement cells");
}
result.success = true;
return result;
}