#include #include #include #include #include #include #include #include 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& primitive = get_primitive_node(primitive_index); const auto& primitive_aabb = primitive.aabb; if (!primitive_aabb.isNull()) scene_aabb.extend(primitive_aabb); } // 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 iso_faces{}; ///< Polygonal faces at the surface network mesh stl_vector_mp> patches{}; ///< A connected component of faces bounded by non-manifold edges stl_vector_mp iso_edges{}; ///< Edges at the surface network mesh stl_vector_mp> chains{}; ///< Chains of non-manifold edges stl_vector_mp> non_manifold_edges_of_vert{}; ///< Indices of non-manifold vertices stl_vector_mp> 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> 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 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> vertex_scalar_values(num_vert, stl_vector_mp(num_funcs)); stl_vector_mp is_positive_scalar_field_sign(num_funcs * num_vert, false); stl_vector_mp is_negative_scalar_field_sign(num_funcs * num_vert, false); stl_vector_mp 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_sdf(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 active_functions_in_tet{}; // active function indices in CRS vector format stl_vector_mp 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(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> 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 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(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_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> 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 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 surf_int_of_patch{}; stl_vector_mp 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> 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> half_patch_pair_list{}; stl_vector_mp> 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 shell_of_half_patch{}; stl_vector_mp> components{}; stl_vector_mp 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(iso_vertices.data()); result.mesh.num_vertices = static_cast(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(face.vertex_indices.begin()), std::make_move_iterator(face.vertex_indices.end())); vertex_counts_of_face.emplace_back(face.vertex_indices.size()); } } result.mesh.faces = polygon_faces.data(); result.mesh.num_faces = static_cast(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> 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(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; }