diff options
52 files changed, 4719 insertions, 2334 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 5a555876d21..15831b6bc61 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -269,10 +269,11 @@ if(UNIX AND NOT APPLE) option(WITH_SYSTEM_EIGEN3 "Use the systems Eigen3 library" OFF) endif() +# Geometry +option(WITH_REMESH_DUALCON "Enable Remesh Algorithm using Dualcon" ON) # Modifiers option(WITH_MOD_FLUID "Enable Mantaflow Fluid Simulation Framework" ON) -option(WITH_MOD_REMESH "Enable Remesh Modifier" ON) option(WITH_MOD_OCEANSIM "Enable Ocean Modifier" ON) # Image format support @@ -1995,7 +1996,7 @@ if(FIRST_RUN) info_cfg_text("Modifiers:") info_cfg_option(WITH_MOD_FLUID) info_cfg_option(WITH_MOD_OCEANSIM) - info_cfg_option(WITH_MOD_REMESH) + info_cfg_option(WITH_REMESH_DUALCON) info_cfg_text("OpenGL:") if(WIN32) diff --git a/build_files/cmake/config/blender_full.cmake b/build_files/cmake/config/blender_full.cmake index ccd5b47c776..9b01305bde8 100644 --- a/build_files/cmake/config/blender_full.cmake +++ b/build_files/cmake/config/blender_full.cmake @@ -37,7 +37,7 @@ set(WITH_LZMA ON CACHE BOOL "" FORCE) set(WITH_LZO ON CACHE BOOL "" FORCE) set(WITH_MOD_FLUID ON CACHE BOOL "" FORCE) set(WITH_MOD_OCEANSIM ON CACHE BOOL "" FORCE) -set(WITH_MOD_REMESH ON CACHE BOOL "" FORCE) +set(WITH_REMESH_DUALCON ON CACHE BOOL "" FORCE) set(WITH_NANOVDB ON CACHE BOOL "" FORCE) set(WITH_OPENAL ON CACHE BOOL "" FORCE) set(WITH_OPENCOLLADA ON CACHE BOOL "" FORCE) diff --git a/build_files/cmake/config/blender_lite.cmake b/build_files/cmake/config/blender_lite.cmake index 0cd886e67d7..cd4e14f8e0c 100644 --- a/build_files/cmake/config/blender_lite.cmake +++ b/build_files/cmake/config/blender_lite.cmake @@ -43,7 +43,7 @@ set(WITH_LZMA OFF CACHE BOOL "" FORCE) set(WITH_LZO OFF CACHE BOOL "" FORCE) set(WITH_MOD_FLUID OFF CACHE BOOL "" FORCE) set(WITH_MOD_OCEANSIM OFF CACHE BOOL "" FORCE) -set(WITH_MOD_REMESH OFF CACHE BOOL "" FORCE) +set(WITH_REMESH_DUALCON OFF CACHE BOOL "" FORCE) set(WITH_NANOVDB OFF CACHE BOOL "" FORCE) set(WITH_OPENAL OFF CACHE BOOL "" FORCE) set(WITH_OPENCOLLADA OFF CACHE BOOL "" FORCE) diff --git a/build_files/cmake/config/blender_release.cmake b/build_files/cmake/config/blender_release.cmake index b8180d733de..c053c168f22 100644 --- a/build_files/cmake/config/blender_release.cmake +++ b/build_files/cmake/config/blender_release.cmake @@ -38,7 +38,7 @@ set(WITH_LZMA ON CACHE BOOL "" FORCE) set(WITH_LZO ON CACHE BOOL "" FORCE) set(WITH_MOD_FLUID ON CACHE BOOL "" FORCE) set(WITH_MOD_OCEANSIM ON CACHE BOOL "" FORCE) -set(WITH_MOD_REMESH ON CACHE BOOL "" FORCE) +set(WITH_REMESH_DUALCON ON CACHE BOOL "" FORCE) set(WITH_NANOVDB ON CACHE BOOL "" FORCE) set(WITH_OPENAL ON CACHE BOOL "" FORCE) set(WITH_OPENCOLLADA ON CACHE BOOL "" FORCE) diff --git a/intern/CMakeLists.txt b/intern/CMakeLists.txt index dbd939e64b7..def99b98d34 100644 --- a/intern/CMakeLists.txt +++ b/intern/CMakeLists.txt @@ -37,7 +37,7 @@ if(WITH_AUDASPACE) add_subdirectory(audaspace) endif() -if(WITH_MOD_REMESH) +if(WITH_REMESH_DUALCON) add_subdirectory(dualcon) endif() diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py index 6f9b222571c..01e0ef1979b 100644 --- a/release/scripts/startup/nodeitems_builtins.py +++ b/release/scripts/startup/nodeitems_builtins.py @@ -532,6 +532,7 @@ geometry_node_categories = [ NodeItem("GeometryNodeJoinGeometry"), NodeItem("GeometryNodeSeparateComponents"), NodeItem("GeometryNodeRaycast"), + NodeItem("GeometryNodeMergeByDistance"), ]), GeometryNodeCategory("GEO_INPUT", "Input", items=[ NodeItem("GeometryNodeObjectInfo"), @@ -554,6 +555,14 @@ geometry_node_categories = [ NodeItem("GeometryNodeEdgeSplit"), NodeItem("GeometryNodeSubdivisionSurface"), NodeItem("GeometryNodeMeshSubdivide"), + NodeItem("GeometryNodeSolidify"), + NodeItem("GeometryNodeRemeshVoxel"), + NodeItem("GeometryNodeRemeshBlocks"), + NodeItem("GeometryNodeMeshExtrude"), + NodeItem("GeometryNodeMeshInset"), + NodeItem("GeometryNodeCollapse"), + NodeItem("GeometryNodeUnsubdivide"), + NodeItem("GeometryNodeDissolve"), ]), GeometryNodeCategory("GEO_PRIMITIVES_MESH", "Mesh Primitives", items=[ NodeItem("GeometryNodeMeshCircle"), diff --git a/source/blender/CMakeLists.txt b/source/blender/CMakeLists.txt index 8d18cf0ae9a..c1adb29812d 100644 --- a/source/blender/CMakeLists.txt +++ b/source/blender/CMakeLists.txt @@ -118,6 +118,7 @@ add_subdirectory(blenloader) add_subdirectory(depsgraph) add_subdirectory(ikplugin) add_subdirectory(simulation) +add_subdirectory(geometry) add_subdirectory(gpu) add_subdirectory(imbuf) add_subdirectory(nodes) diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index c4393246926..e55a4e20e58 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -1472,7 +1472,15 @@ int ntreeTexExecTree(struct bNodeTree *ntree, #define GEO_NODE_CURVE_SET_HANDLES 1072 #define GEO_NODE_CURVE_SPLINE_TYPE 1073 #define GEO_NODE_CURVE_SELECT_HANDLES 1074 - +#define GEO_NODE_SOLIDIFY 1075 +#define GEO_NODE_REMESH_VOXEL 1076 +#define GEO_NODE_REMESH_BLOCKS 1077 +#define GEO_NODE_MERGE_BY_DISTANCE 1078 +#define GEO_NODE_MESH_EXTRUDE 1079 +#define GEO_NODE_MESH_INSET 1080 +#define GEO_NODE_COLLAPSE 1081 +#define GEO_NODE_UNSUBDIVIDE 1082 +#define GEO_NODE_DISSOLVE 1083 /** \} */ /* -------------------------------------------------------------------- */ diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index 2a0e05a2616..510e628cc08 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -5135,6 +5135,7 @@ static void registerGeometryNodes() register_node_type_geo_attribute_vector_rotate(); register_node_type_geo_boolean(); register_node_type_geo_bounding_box(); + register_node_type_geo_collapse(); register_node_type_geo_collection_info(); register_node_type_geo_convex_hull(); register_node_type_geo_curve_endpoints(); @@ -5155,12 +5156,15 @@ static void registerGeometryNodes() register_node_type_geo_curve_to_points(); register_node_type_geo_curve_trim(); register_node_type_geo_delete_geometry(); + register_node_type_geo_dissolve(); register_node_type_geo_edge_split(); register_node_type_geo_input_material(); register_node_type_geo_is_viewport(); register_node_type_geo_join_geometry(); register_node_type_geo_material_assign(); register_node_type_geo_material_replace(); + register_node_type_geo_mesh_extrude(); + register_node_type_geo_mesh_inset(); register_node_type_geo_mesh_primitive_circle(); register_node_type_geo_mesh_primitive_cone(); register_node_type_geo_mesh_primitive_cube(); @@ -5180,14 +5184,19 @@ static void registerGeometryNodes() register_node_type_geo_point_translate(); register_node_type_geo_points_to_volume(); register_node_type_geo_raycast(); + register_node_type_geo_merge_by_distance(); register_node_type_geo_sample_texture(); register_node_type_geo_select_by_handle_type(); register_node_type_geo_select_by_material(); register_node_type_geo_separate_components(); + register_node_type_geo_solidify(); register_node_type_geo_subdivision_surface(); register_node_type_geo_switch(); + register_node_type_geo_remesh_voxel(); + register_node_type_geo_remesh_blocks(); register_node_type_geo_transform(); register_node_type_geo_triangulate(); + register_node_type_geo_unsubdivide(); register_node_type_geo_viewer(); register_node_type_geo_volume_to_mesh(); } diff --git a/source/blender/bmesh/intern/bmesh_mesh.c b/source/blender/bmesh/intern/bmesh_mesh.c index b2958a9e744..5efb389b0c2 100644 --- a/source/blender/bmesh/intern/bmesh_mesh.c +++ b/source/blender/bmesh/intern/bmesh_mesh.c @@ -1446,4 +1446,242 @@ void BM_mesh_vert_coords_apply_with_mat4(BMesh *bm, } } -/** \} */ +/** + * Use to select bmesh vertex data based on an array of bool. + */ +void BM_select_vertices(BMesh *bm, const bool *mask) +{ + BMIter iter; + BMVert *v; + int i; + BM_ITER_MESH_INDEX (v, &iter, bm, BM_VERTS_OF_MESH, i) { + BM_elem_flag_set(v, BM_ELEM_SELECT, mask[i]); + } +} + +/** + * Use to select bmesh edge data based on an array of bool. + */ +void BM_select_edges(BMesh *bm, const bool *mask) +{ + BMIter iter; + BMEdge *e; + int i; + BM_ITER_MESH_INDEX (e, &iter, bm, BM_EDGES_OF_MESH, i) { + BM_elem_flag_set(e, BM_ELEM_SELECT, mask[i]); + } +} + +/** + * Use to select bmesh face data based on an array of bool. + */ +void BM_select_faces(BMesh *bm, const bool *mask) +{ + BMIter iter; + BMFace *f; + int i = 0; + BM_ITER_MESH_INDEX (f, &iter, bm, BM_FACES_OF_MESH, i) { + BM_elem_flag_set(f, BM_ELEM_SELECT, mask[i]); + } +} + +/** + * Use to temporary tag bmesh edge data based on an array of bool. + */ +void BM_tag_vertices(BMesh *bm, const bool *mask) +{ + BMIter iter; + BMVert *v; + int i; + BM_ITER_MESH_INDEX (v, &iter, bm, BM_VERTS_OF_MESH, i) { + BM_elem_flag_set(v, BM_ELEM_TAG, mask[i]); + } +} + +/** + * Use to temporary tag bmesh edge data based on an array of bool. + */ +void BM_tag_edges(BMesh *bm, const bool *mask) +{ + BMIter iter; + BMEdge *e; + int i; + BM_ITER_MESH_INDEX (e, &iter, bm, BM_EDGES_OF_MESH, i) { + BM_elem_flag_set(e, BM_ELEM_TAG, mask[i]); + } +} + +/** + * Use to temporary tag bmesh face data based on an array of bool. + */ +void BM_tag_faces(BMesh *bm, const bool *mask) +{ + BMIter iter; + BMFace *f; + int i; + BM_ITER_MESH_INDEX (f, &iter, bm, BM_FACES_OF_MESH, i) { + BM_elem_flag_set(f, BM_ELEM_TAG, mask[i]); + } +} + +/** + * Write selected bmesh vertex to array of bool with length of totvert. + */ +void BM_get_selected_vertices(BMesh *bm, bool *selection) +{ + BMIter iter; + BMVert *v; + int i; + BM_ITER_MESH_INDEX (v, &iter, bm, BM_VERTS_OF_MESH, i) { + selection[i] = BM_elem_flag_test(v, BM_ELEM_SELECT); + } +} + +/** + * Write selected bmesh edge to array of bool with length of totedge. + */ +void BM_get_selected_edges(BMesh *bm, bool *selection) +{ + BMIter iter; + BMEdge *e; + int i; + BM_ITER_MESH_INDEX (e, &iter, bm, BM_EDGES_OF_MESH, i) { + selection[i] = BM_elem_flag_test(e, BM_ELEM_SELECT); + } +} + +/** + * Write selected bmesh face to array of bool with length of totpoly. + */ +void BM_get_selected_faces(BMesh *bm, bool *selection) +{ + BMIter iter; + BMFace *f; + int i; + BM_ITER_MESH_INDEX (f, &iter, bm, BM_FACES_OF_MESH, i) { + selection[i] = BM_elem_flag_test(f, BM_ELEM_SELECT); + } +} + +/** + * Write tagged bmesh vertex to array of bool with length of totvert. + */ +void BM_get_tagged_vertices(BMesh *bm, bool *selection) +{ + BMIter iter; + BMVert *v; + int i; + BM_ITER_MESH_INDEX (v, &iter, bm, BM_VERTS_OF_MESH, i) { + selection[i] = BM_elem_flag_test(v, BM_ELEM_TAG); + } +} + +/** + * Write tagged bmesh edge to array of bool with length of totedge. + */ +void BM_get_tagged_edges(BMesh *bm, bool *selection) +{ + BMIter iter; + BMEdge *e; + int i; + BM_ITER_MESH_INDEX (e, &iter, bm, BM_EDGES_OF_MESH, i) { + selection[i] = BM_elem_flag_test(e, BM_ELEM_TAG); + } +} + +/** + * Write tagged bmesh faces to array of bool with length of totpoly. + */ +void BM_get_tagged_faces(BMesh *bm, bool *selection) +{ + BMIter iter; + BMFace *f; + int i; + BM_ITER_MESH_INDEX (f, &iter, bm, BM_FACES_OF_MESH, i) { + selection[i] = BM_elem_flag_test(f, BM_ELEM_TAG); + } +} + +/** + * Use to remove tag from all bmesh verts that are tagged with another tag. + */ +void BM_untag_vertices_by_tag(BMesh *bm, int tag) +{ + BMIter iter; + BMVert *v; + int i; + BM_ITER_MESH_INDEX (v, &iter, bm, BM_VERTS_OF_MESH, i) { + if (BM_elem_flag_test(v, tag)) { + BM_elem_flag_disable(v, BM_ELEM_TAG); + } + } +} + +/** + * Use to remove tag from all bmesh edges that are tagged with another tag. + */ +void BM_untag_edges_by_tag(BMesh *bm, int tag) +{ + BMIter iter; + BMEdge *e; + int i; + BM_ITER_MESH_INDEX (e, &iter, bm, BM_EDGES_OF_MESH, i) { + if (BM_elem_flag_test(e, tag)) { + BM_elem_flag_disable(e, BM_ELEM_TAG); + } + } +} + +/** + * Use to remove tag from all bmesh faces that are tagged with another tag. + */ +void BM_untag_faces_by_tag(BMesh *bm, int tag) +{ + BMIter iter; + BMFace *f; + int i; + BM_ITER_MESH_INDEX (f, &iter, bm, BM_FACES_OF_MESH, i) { + if (BM_elem_flag_test(f, tag)) { + BM_elem_flag_disable(f, BM_ELEM_TAG); + } + } +} + +void BM_tag_vertices_from_operator_slot(BMesh *bm, + BMOperator *b_mesh_operator, + const char *slot, + const int tag) +{ + BMOIter iter; + BMVert *v; + BM_mesh_elem_hflag_disable_all(bm, BM_VERT, BM_ELEM_TAG, false); + BMO_ITER (v, &iter, b_mesh_operator->slots_out, slot, tag) { + BM_elem_flag_enable(v, BM_ELEM_TAG); + } +} + +void BM_tag_edges_from_operator_slot(BMesh *bm, + BMOperator *b_mesh_operator, + const char *slot, + const int tag) +{ + BMOIter iter; + BMEdge *e; + BM_mesh_elem_hflag_disable_all(bm, BM_EDGE, BM_ELEM_TAG, false); + BMO_ITER (e, &iter, b_mesh_operator->slots_out, slot, tag) { + BM_elem_flag_enable(e, BM_ELEM_TAG); + } +} + +void BM_tag_faces_from_operator_slot(BMesh *bm, + BMOperator *b_mesh_operator, + const char *slot, + const int tag) +{ + BMOIter iter; + BMFace *f; + BM_mesh_elem_hflag_disable_all(bm, BM_FACE, BM_ELEM_TAG, false); + BMO_ITER (f, &iter, b_mesh_operator->slots_out, slot, tag) { + BM_elem_flag_enable(f, BM_ELEM_TAG); + } +}
\ No newline at end of file diff --git a/source/blender/bmesh/intern/bmesh_mesh.h b/source/blender/bmesh/intern/bmesh_mesh.h index bd0504b038a..dd987e96582 100644 --- a/source/blender/bmesh/intern/bmesh_mesh.h +++ b/source/blender/bmesh/intern/bmesh_mesh.h @@ -134,3 +134,36 @@ void BM_mesh_vert_coords_apply(BMesh *bm, const float (*vert_coords)[3]); void BM_mesh_vert_coords_apply_with_mat4(BMesh *bm, const float (*vert_coords)[3], const float mat[4][4]); + +void BM_select_vertices(BMesh *bm, const bool *mask); +void BM_select_edges(BMesh *bm, const bool *mask); +void BM_select_faces(BMesh *bm, const bool *mask); + +void BM_tag_vertices(BMesh *bm, const bool *mask); +void BM_tag_edges(BMesh *bm, const bool *mask); +void BM_tag_faces(BMesh *bm, const bool *mask); + +void BM_get_selected_vertices(BMesh *bm, bool *selection); +void BM_get_selected_edges(BMesh *bm, bool *selection); +void BM_get_selected_faces(BMesh *bm, bool *selection); + +void BM_get_tagged_vertices(BMesh *bm, bool *selection); +void BM_get_tagged_edges(BMesh *bm, bool *selection); +void BM_get_tagged_faces(BMesh *bm, bool *selection); + +void BM_untag_vertices_by_tag(BMesh *bm, int tag); +void BM_untag_edges_by_tag(BMesh *bm, int tag); +void BM_untag_faces_by_tag(BMesh *bm, int tag); + +void BM_tag_vertices_from_operator_slot(BMesh *bm, + BMOperator *b_mesh_operator, + const char *slot, + const int tag); +void BM_tag_edges_from_operator_slot(BMesh *bm, + BMOperator *b_mesh_operator, + const char *slot, + const int tag); +void BM_tag_faces_from_operator_slot(BMesh *bm, + BMOperator *b_mesh_operator, + const char *slot, + const int tag); diff --git a/source/blender/bmesh/intern/bmesh_opdefines.c b/source/blender/bmesh/intern/bmesh_opdefines.c index b63a09a97a6..651f9184e19 100644 --- a/source/blender/bmesh/intern/bmesh_opdefines.c +++ b/source/blender/bmesh/intern/bmesh_opdefines.c @@ -1898,6 +1898,9 @@ static BMOpDefine bmo_inset_individual_def = { {{"faces", BMO_OP_SLOT_ELEMENT_BUF, {BM_FACE}}, /* input faces */ {"thickness", BMO_OP_SLOT_FLT}, /* thickness */ {"depth", BMO_OP_SLOT_FLT}, /* depth */ + {"thickness_array", BMO_OP_SLOT_PTR}, /* thickness */ + {"depth_array", BMO_OP_SLOT_PTR}, /* depth */ + {"use_attributes", BMO_OP_SLOT_BOOL}, /* Use spans for thickness and depth */ {"use_even_offset", BMO_OP_SLOT_BOOL}, /* scale the offset to give more even thickness */ {"use_interpolate", BMO_OP_SLOT_BOOL}, /* blend face data across the inset */ {"use_relative_offset", BMO_OP_SLOT_BOOL}, /* scale the offset by surrounding geometry */ @@ -1929,6 +1932,9 @@ static BMOpDefine bmo_inset_region_def = { {"use_edge_rail", BMO_OP_SLOT_BOOL}, /* inset the region along existing edges */ {"thickness", BMO_OP_SLOT_FLT}, /* thickness */ {"depth", BMO_OP_SLOT_FLT}, /* depth */ + {"thickness_array", BMO_OP_SLOT_PTR}, /* thickness */ + {"depth_array", BMO_OP_SLOT_PTR}, /* depth */ + {"use_attributes", BMO_OP_SLOT_BOOL}, /* Use spans for thickness and depth */ {"use_outset", BMO_OP_SLOT_BOOL}, /* outset rather than inset */ {{'\0'}}, }, diff --git a/source/blender/bmesh/intern/bmesh_operator_api.h b/source/blender/bmesh/intern/bmesh_operator_api.h index 0f9488bd091..06f82e21c9c 100644 --- a/source/blender/bmesh/intern/bmesh_operator_api.h +++ b/source/blender/bmesh/intern/bmesh_operator_api.h @@ -428,6 +428,9 @@ typedef enum { BMO_DELIM_SEAM = 1 << 2, BMO_DELIM_SHARP = 1 << 3, BMO_DELIM_UV = 1 << 4, + BMO_DELIM_EDGE_SELECTION_INVSE = 1 << 5, + BMO_DELIM_EDGE_SELECTION = 1 << 6, + BMO_DELIM_FACE_SELECTION = 1 << 7, } BMO_Delimit; void BMO_op_flag_enable(BMesh *bm, BMOperator *op, const int op_flag); diff --git a/source/blender/bmesh/operators/bmo_inset.c b/source/blender/bmesh/operators/bmo_inset.c index 4833290aa0b..1e30f1b72b3 100644 --- a/source/blender/bmesh/operators/bmo_inset.c +++ b/source/blender/bmesh/operators/bmo_inset.c @@ -419,6 +419,9 @@ void bmo_inset_individual_exec(BMesh *bm, BMOperator *op) BMOIter oiter; MemArena *interp_arena = NULL; + const bool use_attributes = BMO_slot_bool_get(op->slots_in, "use_attributes"); + const float *thickness_array = BMO_slot_ptr_get(op->slots_in, "thickness_array"); + const float *depth_array = BMO_slot_ptr_get(op->slots_in, "depth_array"); const float thickness = BMO_slot_float_get(op->slots_in, "thickness"); const float depth = BMO_slot_float_get(op->slots_in, "depth"); const bool use_even_offset = BMO_slot_bool_get(op->slots_in, "use_even_offset"); @@ -433,19 +436,37 @@ void bmo_inset_individual_exec(BMesh *bm, BMOperator *op) if (use_interpolate) { interp_arena = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__); } + int i = 0; + if (use_attributes) { + BMO_ITER_INDEX (f, &oiter, op->slots_in, "faces", BM_FACE, i) { + bmo_face_inset_individual(bm, + f, + interp_arena, + thickness_array[i], + depth_array[i], + use_even_offset, + use_relative_offset, + use_interpolate); - BMO_ITER (f, &oiter, op->slots_in, "faces", BM_FACE) { - bmo_face_inset_individual(bm, - f, - interp_arena, - thickness, - depth, - use_even_offset, - use_relative_offset, - use_interpolate); + if (use_interpolate) { + BLI_memarena_clear(interp_arena); + } + } + } + else { + BMO_ITER (f, &oiter, op->slots_in, "faces", BM_FACE) { + bmo_face_inset_individual(bm, + f, + interp_arena, + thickness, + depth, + use_even_offset, + use_relative_offset, + use_interpolate); - if (use_interpolate) { - BLI_memarena_clear(interp_arena); + if (use_interpolate) { + BLI_memarena_clear(interp_arena); + } } } @@ -683,6 +704,9 @@ void bmo_inset_region_exec(BMesh *bm, BMOperator *op) const bool use_edge_rail = BMO_slot_bool_get(op->slots_in, "use_edge_rail"); const bool use_interpolate = BMO_slot_bool_get(op->slots_in, "use_interpolate"); const float thickness = BMO_slot_float_get(op->slots_in, "thickness"); + const bool use_attributes = BMO_slot_bool_get(op->slots_in, "use_attributes"); + const float *thickness_array = BMO_slot_ptr_get(op->slots_in, "thickness_array"); + const float *depth_array = BMO_slot_ptr_get(op->slots_in, "depth_array"); const float depth = BMO_slot_float_get(op->slots_in, "depth"); #ifdef USE_LOOP_CUSTOMDATA_MERGE const bool has_math_ldata = (use_interpolate && CustomData_has_math(&bm->ldata)); @@ -1096,7 +1120,12 @@ void bmo_inset_region_exec(BMesh *bm, BMOperator *op) } /* apply the offset */ - madd_v3_v3fl(v_split->co, tvec, thickness); + if (use_attributes) { + madd_v3_v3fl(v_split->co, tvec, thickness_array[v_split->head.index]); + } + else { + madd_v3_v3fl(v_split->co, tvec, thickness); + } } /* this saves expensive/slow glue check for common cases */ diff --git a/source/blender/bmesh/tools/bmesh_decimate_dissolve.c b/source/blender/bmesh/tools/bmesh_decimate_dissolve.c index d8a586acee5..349b89d4340 100644 --- a/source/blender/bmesh/tools/bmesh_decimate_dissolve.c +++ b/source/blender/bmesh/tools/bmesh_decimate_dissolve.c @@ -104,6 +104,22 @@ static bool bm_edge_is_delimiter(const BMEdge *e, BLI_assert(BM_edge_is_manifold(e)); if (delimit != 0) { + if (delimit & BMO_DELIM_EDGE_SELECTION_INVSE) { + if (!BM_elem_flag_test(e, BM_ELEM_SELECT)) { + return true; + } + } + if (delimit & BMO_DELIM_EDGE_SELECTION) { + if (BM_elem_flag_test(e, BM_ELEM_SELECT)) { + return true; + } + } + if (delimit & BMO_DELIM_FACE_SELECTION) { + if (BM_elem_flag_test(e->l->f, BM_ELEM_TAG) != + BM_elem_flag_test(e->l->radial_next->f, BM_ELEM_TAG)) { + return true; + } + } if (delimit & BMO_DELIM_SEAM) { if (BM_elem_flag_test(e, BM_ELEM_SEAM)) { return true; diff --git a/source/blender/geometry/CMakeLists.txt b/source/blender/geometry/CMakeLists.txt new file mode 100644 index 00000000000..f3c6b80aef6 --- /dev/null +++ b/source/blender/geometry/CMakeLists.txt @@ -0,0 +1,61 @@ + # ***** BEGIN GPL LICENSE BLOCK ***** + # + # This program is free software; you can redistribute it and/or + # modify it under the terms of the GNU General Public License + # as published by the Free Software Foundation; either version 2 + # of the License, or (at your option) any later version. + # + # This program is distributed in the hope that it will be useful, + # but WITHOUT ANY WARRANTY; without even the implied warranty of + # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + # GNU General Public License for more details. + # + # You should have received a copy of the GNU General Public License + # along with this program; if not, write to the Free Software Foundation, + # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + # + # The Original Code is Copyright (C) 2006, Blender Foundation + # All rights reserved. + # ***** END GPL LICENSE BLOCK ***** + + set(INC + . + ../blenfont + ../blenkernel + ../blenlib + ../blentranslation + ../functions + ../makesdna + ../makesrna + ../../../intern/guardedalloc + ../makesdna/intern + ${CMAKE_BINARY_DIR}/source/blender/makesdna/intern + ) + + set(SRC + intern/solidify_nonmanifold.c + GEO_solidifiy.h + intern/remesh_blocks.c + GEO_mesh_remesh_blocks.h + intern/mesh_merge_by_distance.c + intern/pointcloud_merge_by_distance.cc + GEO_mesh_merge_by_distance.h + GEO_pointcloud_merge_by_distance.h + ) + + set(LIB + bf_blenkernel + bf_blenlib + ) + + if(WITH_REMESH_DUALCON) + list(APPEND INC + ../../../intern/dualcon + ) + list(APPEND LIB + bf_intern_dualcon + ) + add_definitions(-DWITH_REMESH_DUALCON) + endif() + + blender_add_lib(bf_geometry "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
\ No newline at end of file diff --git a/source/blender/geometry/GEO_mesh_merge_by_distance.h b/source/blender/geometry/GEO_mesh_merge_by_distance.h new file mode 100644 index 00000000000..b1f5680a928 --- /dev/null +++ b/source/blender/geometry/GEO_mesh_merge_by_distance.h @@ -0,0 +1,39 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#pragma once + +/** \file + * \ingroup geo + */ + +#ifdef __cplusplus +extern "C" { +#endif + +enum { + WELD_MODE_ALL = 0, + WELD_MODE_CONNECTED = 1, +}; + +struct Mesh *GEO_mesh_merge_by_distance(const struct Mesh *mesh, + const bool *mask, + const float merge_distance, + const int weld_mode); + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/geometry/GEO_mesh_remesh_blocks.h b/source/blender/geometry/GEO_mesh_remesh_blocks.h new file mode 100644 index 00000000000..6e727871b67 --- /dev/null +++ b/source/blender/geometry/GEO_mesh_remesh_blocks.h @@ -0,0 +1,48 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#pragma once + +/** \file + * \ingroup geo + */ + +#ifdef __cplusplus +extern "C" { +#endif + +struct Mesh; + +typedef enum eRemeshBlocksMode { + /* Blocks. */ + REMESH_BLOCKS_CENTROID = 0, + /* Smooth. */ + REMESH_BLOCKS_MASS_POINT = 1, + /* Smooth with sharp edges. */ + REMESH_BLOCKS_SHARP_FEATURES = 2, +} eRemeshBlocksMode; + +struct Mesh *GEO_mesh_remesh_blocks(const struct Mesh *mesh, + const char remesh_flag, + const char remesh_mode, + const float threshold, + const int hermite_num, + const float scale, + const int depth); + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/geometry/GEO_pointcloud_merge_by_distance.h b/source/blender/geometry/GEO_pointcloud_merge_by_distance.h new file mode 100644 index 00000000000..c03cc08d814 --- /dev/null +++ b/source/blender/geometry/GEO_pointcloud_merge_by_distance.h @@ -0,0 +1,33 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#pragma once + +/** \file + * \ingroup geo + */ + +#ifdef __cplusplus +extern "C" { +#endif + +PointCloud *merge_by_distance_pointcloud(const PointCloud &point_cloud, + const float merge_threshold, + blender::Span<bool> selection); + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/geometry/GEO_solidifiy.h b/source/blender/geometry/GEO_solidifiy.h new file mode 100644 index 00000000000..1929f6c45d2 --- /dev/null +++ b/source/blender/geometry/GEO_solidifiy.h @@ -0,0 +1,43 @@ +#include "DNA_object_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct Mesh; + +typedef struct SolidifyData { + const Object *object; + /** New surface offset level. (Thickness) */ + float offset; + /** Midpoint of the offset. */ + float offset_fac; + /** + * Factor for the minimum weight to use when vertex-groups are used, + * avoids 0.0 weights giving duplicate geometry. + */ + float offset_fac_vg; + /** Clamp offset based on surrounding geometry. */ + float offset_clamp; + + /** Variables for #MOD_SOLIDIFY_MODE_NONMANIFOLD. */ + char nonmanifold_offset_mode; + char nonmanifold_boundary_mode; + + int flag; + + float merge_tolerance; + float bevel_convex; + const float *distance; +} SolidifyData; + +struct Mesh *solidify_nonmanifold(const SolidifyData *solidify_data, + struct Mesh *mesh, + bool **r_shell_verts, + bool **r_rim_verts, + bool **r_shell_faces, + bool **r_rim_faces); + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/geometry/intern/mesh_merge_by_distance.c b/source/blender/geometry/intern/mesh_merge_by_distance.c new file mode 100644 index 00000000000..a21c15c2c61 --- /dev/null +++ b/source/blender/geometry/intern/mesh_merge_by_distance.c @@ -0,0 +1,1927 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +//#define USE_WELD_DEBUG +//#define USE_WELD_NORMALS +//#define USE_BVHTREEKDOP + +#include "MEM_guardedalloc.h" + +#include "BLI_utildefines.h" + +#include "BLI_alloca.h" +#include "BLI_kdtree.h" +#include "BLI_math.h" + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" + +#ifdef USE_BVHTREEKDOP +# include "BKE_bvhutils.h" +#endif + +#include "BKE_context.h" +#include "BKE_mesh.h" +#include "BKE_modifier.h" + +#include "GEO_mesh_merge_by_distance.h" // Own include. + +/* Indicates when the element was not computed. */ +#define OUT_OF_CONTEXT (uint)(-1) +/* Indicates if the edge or face will be collapsed. */ +#define ELEM_COLLAPSED (uint)(-2) +/* indicates whether an edge or vertex in groups_map will be merged. */ +#define ELEM_MERGED (uint)(-2) + +/* Used to indicate a range in an array specifying a group. */ +struct WeldGroup { + uint len; + uint ofs; +}; + +/* Edge groups that will be merged. Final vertices are also indicated. */ +struct WeldGroupEdge { + struct WeldGroup group; + uint v1; + uint v2; +}; + +typedef struct WeldVert { + /* Indexes relative to the original Mesh. */ + uint vert_dest; + uint vert_orig; +} WeldVert; + +typedef struct WeldEdge { + union { + uint flag; + struct { + /* Indexes relative to the original Mesh. */ + uint edge_dest; + uint edge_orig; + uint vert_a; + uint vert_b; + }; + }; +} WeldEdge; + +typedef struct WeldLoop { + union { + uint flag; + struct { + /* Indexes relative to the original Mesh. */ + uint vert; + uint edge; + uint loop_orig; + uint loop_skip_to; + }; + }; +} WeldLoop; + +typedef struct WeldPoly { + union { + uint flag; + struct { + /* Indexes relative to the original Mesh. */ + uint poly_dst; + uint poly_orig; + uint loop_start; + uint loop_end; + /* Final Polygon Size. */ + uint len; + /* Group of loops that will be affected. */ + struct WeldGroup loops; + }; + }; +} WeldPoly; + +typedef struct WeldMesh { + /* Group of vertices to be merged. */ + struct WeldGroup *vert_groups; + uint *vert_groups_buffer; + + /* Group of edges to be merged. */ + struct WeldGroupEdge *edge_groups; + uint *edge_groups_buffer; + /* From the original index of the vertex, this indicates which group it is or is going to be + * merged. */ + uint *edge_groups_map; + + /* References all polygons and loops that will be affected. */ + WeldLoop *wloop; + WeldPoly *wpoly; + WeldPoly *wpoly_new; + uint wloop_len; + uint wpoly_len; + uint wpoly_new_len; + + /* From the actual index of the element in the mesh, it indicates what is the index of the Weld + * element above. */ + uint *loop_map; + uint *poly_map; + + uint vert_kill_len; + uint edge_kill_len; + uint loop_kill_len; + uint poly_kill_len; /* Including the new polygons. */ + + /* Size of the affected polygon with more sides. */ + uint max_poly_len; +} WeldMesh; + +typedef struct WeldLoopOfPolyIter { + uint loop_start; + uint loop_end; + const WeldLoop *wloop; + const MLoop *mloop; + const uint *loop_map; + /* Weld group. */ + uint *group; + + uint l_curr; + uint l_next; + + /* Return */ + uint group_len; + uint v; + uint e; + char type; +} WeldLoopOfPolyIter; + +/* -------------------------------------------------------------------- */ +/** \name Debug Utils + * \{ */ + +#ifdef USE_WELD_DEBUG +static bool weld_iter_loop_of_poly_begin(WeldLoopOfPolyIter *iter, + const WeldPoly *wp, + const WeldLoop *wloop, + const MLoop *mloop, + const uint *loop_map, + uint *group_buffer); + +static bool weld_iter_loop_of_poly_next(WeldLoopOfPolyIter *iter); + +static void weld_assert_edge_kill_len(const WeldEdge *wedge, + const uint wedge_len, + const uint supposed_kill_len) +{ + uint kills = 0; + const WeldEdge *we = &wedge[0]; + for (uint i = wedge_len; i--; we++) { + uint edge_dest = we->edge_dest; + /* Magically includes collapsed edges. */ + if (edge_dest != OUT_OF_CONTEXT) { + kills++; + } + } + BLI_assert(kills == supposed_kill_len); +} + +static void weld_assert_poly_and_loop_kill_len(const WeldPoly *wpoly, + const WeldPoly *wpoly_new, + const uint wpoly_new_len, + const WeldLoop *wloop, + const MLoop *mloop, + const uint *loop_map, + const uint *poly_map, + const MPoly *mpoly, + const uint mpoly_len, + const uint mloop_len, + const uint supposed_poly_kill_len, + const uint supposed_loop_kill_len) +{ + uint poly_kills = 0; + uint loop_kills = mloop_len; + const MPoly *mp = &mpoly[0]; + for (uint i = 0; i < mpoly_len; i++, mp++) { + uint poly_ctx = poly_map[i]; + if (poly_ctx != OUT_OF_CONTEXT) { + const WeldPoly *wp = &wpoly[poly_ctx]; + WeldLoopOfPolyIter iter; + if (!weld_iter_loop_of_poly_begin(&iter, wp, wloop, mloop, loop_map, NULL)) { + poly_kills++; + continue; + } + else { + if (wp->poly_dst != OUT_OF_CONTEXT) { + poly_kills++; + continue; + } + uint remain = wp->len; + uint l = wp->loop_start; + while (remain) { + uint l_next = l + 1; + uint loop_ctx = loop_map[l]; + if (loop_ctx != OUT_OF_CONTEXT) { + const WeldLoop *wl = &wloop[loop_ctx]; + if (wl->loop_skip_to != OUT_OF_CONTEXT) { + l_next = wl->loop_skip_to; + } + if (wl->flag != ELEM_COLLAPSED) { + loop_kills--; + remain--; + } + } + else { + loop_kills--; + remain--; + } + l = l_next; + } + } + } + else { + loop_kills -= mp->totloop; + } + } + + const WeldPoly *wp = &wpoly_new[0]; + for (uint i = wpoly_new_len; i--; wp++) { + if (wp->poly_dst != OUT_OF_CONTEXT) { + poly_kills++; + continue; + } + uint remain = wp->len; + uint l = wp->loop_start; + while (remain) { + uint l_next = l + 1; + uint loop_ctx = loop_map[l]; + if (loop_ctx != OUT_OF_CONTEXT) { + const WeldLoop *wl = &wloop[loop_ctx]; + if (wl->loop_skip_to != OUT_OF_CONTEXT) { + l_next = wl->loop_skip_to; + } + if (wl->flag != ELEM_COLLAPSED) { + loop_kills--; + remain--; + } + } + else { + loop_kills--; + remain--; + } + l = l_next; + } + } + + BLI_assert(poly_kills == supposed_poly_kill_len); + BLI_assert(loop_kills == supposed_loop_kill_len); +} + +static void weld_assert_poly_no_vert_repetition(const WeldPoly *wp, + const WeldLoop *wloop, + const MLoop *mloop, + const uint *loop_map) +{ + const uint len = wp->len; + uint *verts = BLI_array_alloca(verts, len); + WeldLoopOfPolyIter iter; + if (!weld_iter_loop_of_poly_begin(&iter, wp, wloop, mloop, loop_map, NULL)) { + return; + } + else { + uint i = 0; + while (weld_iter_loop_of_poly_next(&iter)) { + verts[i++] = iter.v; + } + } + for (uint i = 0; i < len; i++) { + uint va = verts[i]; + for (uint j = i + 1; j < len; j++) { + uint vb = verts[j]; + BLI_assert(va != vb); + } + } +} + +static void weld_assert_poly_len(const WeldPoly *wp, const WeldLoop *wloop) +{ + if (wp->flag == ELEM_COLLAPSED) { + return; + } + + uint len = wp->len; + const WeldLoop *wl = &wloop[wp->loops.ofs]; + BLI_assert(wp->loop_start <= wl->loop_orig); + + uint end_wloop = wp->loops.ofs + wp->loops.len; + const WeldLoop *wl_end = &wloop[end_wloop - 1]; + + uint min_len = 0; + for (; wl <= wl_end; wl++) { + BLI_assert(wl->loop_skip_to == OUT_OF_CONTEXT); /* Not for this case. */ + if (wl->flag != ELEM_COLLAPSED) { + min_len++; + } + } + BLI_assert(len >= min_len); + + uint max_len = wp->loop_end - wp->loop_start + 1; + BLI_assert(len <= max_len); +} +#endif +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Weld Vert API + * \{ */ + +static void weld_vert_ctx_alloc_and_setup(const uint mvert_len, + uint *r_vert_dest_map, + WeldVert **r_wvert, + uint *r_wvert_len) +{ + /* Vert Context. */ + uint wvert_len = 0; + + WeldVert *wvert, *wv; + wvert = MEM_mallocN(sizeof(*wvert) * mvert_len, __func__); + wv = &wvert[0]; + + uint *v_dest_iter = &r_vert_dest_map[0]; + for (uint i = 0; i < mvert_len; i++, v_dest_iter++) { + if (*v_dest_iter != OUT_OF_CONTEXT) { + wv->vert_dest = *v_dest_iter; + wv->vert_orig = i; + wv++; + wvert_len++; + } + } + + *r_wvert = MEM_reallocN(wvert, sizeof(*wvert) * wvert_len); + *r_wvert_len = wvert_len; +} + +static void weld_vert_groups_setup(const uint mvert_len, + const uint wvert_len, + const WeldVert *wvert, + const uint *vert_dest_map, + uint *r_vert_groups_map, + uint **r_vert_groups_buffer, + struct WeldGroup **r_vert_groups) +{ + /* Get weld vert groups. */ + + uint wgroups_len = 0; + const uint *vert_dest_iter = &vert_dest_map[0]; + uint *group_map_iter = &r_vert_groups_map[0]; + for (uint i = 0; i < mvert_len; i++, group_map_iter++, vert_dest_iter++) { + uint vert_dest = *vert_dest_iter; + if (vert_dest != OUT_OF_CONTEXT) { + if (vert_dest != i) { + *group_map_iter = ELEM_MERGED; + } + else { + *group_map_iter = wgroups_len; + wgroups_len++; + } + } + else { + *group_map_iter = OUT_OF_CONTEXT; + } + } + + struct WeldGroup *wgroups = MEM_callocN(sizeof(*wgroups) * wgroups_len, __func__); + + const WeldVert *wv = &wvert[0]; + for (uint i = wvert_len; i--; wv++) { + uint group_index = r_vert_groups_map[wv->vert_dest]; + wgroups[group_index].len++; + } + + uint ofs = 0; + struct WeldGroup *wg_iter = &wgroups[0]; + for (uint i = wgroups_len; i--; wg_iter++) { + wg_iter->ofs = ofs; + ofs += wg_iter->len; + } + + BLI_assert(ofs == wvert_len); + + uint *groups_buffer = MEM_mallocN(sizeof(*groups_buffer) * ofs, __func__); + wv = &wvert[0]; + for (uint i = wvert_len; i--; wv++) { + uint group_index = r_vert_groups_map[wv->vert_dest]; + groups_buffer[wgroups[group_index].ofs++] = wv->vert_orig; + } + + wg_iter = &wgroups[0]; + for (uint i = wgroups_len; i--; wg_iter++) { + wg_iter->ofs -= wg_iter->len; + } + + *r_vert_groups = wgroups; + *r_vert_groups_buffer = groups_buffer; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Weld Edge API + * \{ */ + +static void weld_edge_ctx_setup(const uint mvert_len, + const uint wedge_len, + struct WeldGroup *r_vlinks, + uint *r_edge_dest_map, + WeldEdge *r_wedge, + uint *r_edge_kiil_len) +{ + WeldEdge *we; + + /* Setup Edge Overlap. */ + uint edge_kill_len = 0; + + struct WeldGroup *vl_iter, *v_links; + v_links = r_vlinks; + vl_iter = &v_links[0]; + + we = &r_wedge[0]; + for (uint i = wedge_len; i--; we++) { + uint dst_vert_a = we->vert_a; + uint dst_vert_b = we->vert_b; + + if (dst_vert_a == dst_vert_b) { + BLI_assert(we->edge_dest == OUT_OF_CONTEXT); + r_edge_dest_map[we->edge_orig] = ELEM_COLLAPSED; + we->flag = ELEM_COLLAPSED; + edge_kill_len++; + continue; + } + + v_links[dst_vert_a].len++; + v_links[dst_vert_b].len++; + } + + uint link_len = 0; + vl_iter = &v_links[0]; + for (uint i = mvert_len; i--; vl_iter++) { + vl_iter->ofs = link_len; + link_len += vl_iter->len; + } + + if (link_len) { + uint *link_edge_buffer = MEM_mallocN(sizeof(*link_edge_buffer) * link_len, __func__); + + we = &r_wedge[0]; + for (uint i = 0; i < wedge_len; i++, we++) { + if (we->flag == ELEM_COLLAPSED) { + continue; + } + + uint dst_vert_a = we->vert_a; + uint dst_vert_b = we->vert_b; + + link_edge_buffer[v_links[dst_vert_a].ofs++] = i; + link_edge_buffer[v_links[dst_vert_b].ofs++] = i; + } + + vl_iter = &v_links[0]; + for (uint i = mvert_len; i--; vl_iter++) { + /* Fix offset */ + vl_iter->ofs -= vl_iter->len; + } + + we = &r_wedge[0]; + for (uint i = 0; i < wedge_len; i++, we++) { + if (we->edge_dest != OUT_OF_CONTEXT) { + /* No need to retest edges. + * (Already includes collapsed edges). */ + continue; + } + + uint dst_vert_a = we->vert_a; + uint dst_vert_b = we->vert_b; + + struct WeldGroup *link_a = &v_links[dst_vert_a]; + struct WeldGroup *link_b = &v_links[dst_vert_b]; + + uint edges_len_a = link_a->len; + uint edges_len_b = link_b->len; + + if (edges_len_a <= 1 || edges_len_b <= 1) { + continue; + } + + uint *edges_ctx_a = &link_edge_buffer[link_a->ofs]; + uint *edges_ctx_b = &link_edge_buffer[link_b->ofs]; + uint edge_orig = we->edge_orig; + + for (; edges_len_a--; edges_ctx_a++) { + uint e_ctx_a = *edges_ctx_a; + if (e_ctx_a == i) { + continue; + } + while (edges_len_b && *edges_ctx_b < e_ctx_a) { + edges_ctx_b++; + edges_len_b--; + } + if (edges_len_b == 0) { + break; + } + uint e_ctx_b = *edges_ctx_b; + if (e_ctx_a == e_ctx_b) { + WeldEdge *we_b = &r_wedge[e_ctx_b]; + BLI_assert(ELEM(we_b->vert_a, dst_vert_a, dst_vert_b)); + BLI_assert(ELEM(we_b->vert_b, dst_vert_a, dst_vert_b)); + BLI_assert(we_b->edge_dest == OUT_OF_CONTEXT); + BLI_assert(we_b->edge_orig != edge_orig); + r_edge_dest_map[we_b->edge_orig] = edge_orig; + we_b->edge_dest = edge_orig; + edge_kill_len++; + } + } + } + +#ifdef USE_WELD_DEBUG + weld_assert_edge_kill_len(r_wedge, wedge_len, edge_kill_len); +#endif + + MEM_freeN(link_edge_buffer); + } + + *r_edge_kiil_len = edge_kill_len; +} + +static void weld_edge_ctx_alloc(const MEdge *medge, + const uint medge_len, + const uint *vert_dest_map, + uint *r_edge_dest_map, + uint **r_edge_ctx_map, + WeldEdge **r_wedge, + uint *r_wedge_len) +{ + /* Edge Context. */ + uint *edge_map = MEM_mallocN(sizeof(*edge_map) * medge_len, __func__); + uint wedge_len = 0; + + WeldEdge *wedge, *we; + wedge = MEM_mallocN(sizeof(*wedge) * medge_len, __func__); + we = &wedge[0]; + + const MEdge *me = &medge[0]; + uint *e_dest_iter = &r_edge_dest_map[0]; + uint *iter = &edge_map[0]; + for (uint i = 0; i < medge_len; i++, me++, iter++, e_dest_iter++) { + uint v1 = me->v1; + uint v2 = me->v2; + uint v_dest_1 = vert_dest_map[v1]; + uint v_dest_2 = vert_dest_map[v2]; + if ((v_dest_1 != OUT_OF_CONTEXT) || (v_dest_2 != OUT_OF_CONTEXT)) { + we->vert_a = (v_dest_1 != OUT_OF_CONTEXT) ? v_dest_1 : v1; + we->vert_b = (v_dest_2 != OUT_OF_CONTEXT) ? v_dest_2 : v2; + we->edge_dest = OUT_OF_CONTEXT; + we->edge_orig = i; + we++; + *e_dest_iter = i; + *iter = wedge_len++; + } + else { + *e_dest_iter = OUT_OF_CONTEXT; + *iter = OUT_OF_CONTEXT; + } + } + + *r_wedge = MEM_reallocN(wedge, sizeof(*wedge) * wedge_len); + *r_wedge_len = wedge_len; + *r_edge_ctx_map = edge_map; +} + +static void weld_edge_groups_setup(const uint medge_len, + const uint edge_kill_len, + const uint wedge_len, + WeldEdge *wedge, + const uint *wedge_map, + uint *r_edge_groups_map, + uint **r_edge_groups_buffer, + struct WeldGroupEdge **r_edge_groups) +{ + + /* Get weld edge groups. */ + + struct WeldGroupEdge *wegroups, *wegrp_iter; + + uint wgroups_len = wedge_len - edge_kill_len; + wegroups = MEM_callocN(sizeof(*wegroups) * wgroups_len, __func__); + wegrp_iter = &wegroups[0]; + + wgroups_len = 0; + const uint *edge_ctx_iter = &wedge_map[0]; + uint *group_map_iter = &r_edge_groups_map[0]; + for (uint i = medge_len; i--; edge_ctx_iter++, group_map_iter++) { + uint edge_ctx = *edge_ctx_iter; + if (edge_ctx != OUT_OF_CONTEXT) { + WeldEdge *we = &wedge[edge_ctx]; + uint edge_dest = we->edge_dest; + if (edge_dest != OUT_OF_CONTEXT) { + BLI_assert(edge_dest != we->edge_orig); + *group_map_iter = ELEM_MERGED; + } + else { + we->edge_dest = we->edge_orig; + wegrp_iter->v1 = we->vert_a; + wegrp_iter->v2 = we->vert_b; + *group_map_iter = wgroups_len; + wgroups_len++; + wegrp_iter++; + } + } + else { + *group_map_iter = OUT_OF_CONTEXT; + } + } + + BLI_assert(wgroups_len == wedge_len - edge_kill_len); + + WeldEdge *we = &wedge[0]; + for (uint i = wedge_len; i--; we++) { + if (we->flag == ELEM_COLLAPSED) { + continue; + } + uint group_index = r_edge_groups_map[we->edge_dest]; + wegroups[group_index].group.len++; + } + + uint ofs = 0; + wegrp_iter = &wegroups[0]; + for (uint i = wgroups_len; i--; wegrp_iter++) { + wegrp_iter->group.ofs = ofs; + ofs += wegrp_iter->group.len; + } + + uint *groups_buffer = MEM_mallocN(sizeof(*groups_buffer) * ofs, __func__); + we = &wedge[0]; + for (uint i = wedge_len; i--; we++) { + if (we->flag == ELEM_COLLAPSED) { + continue; + } + uint group_index = r_edge_groups_map[we->edge_dest]; + groups_buffer[wegroups[group_index].group.ofs++] = we->edge_orig; + } + + wegrp_iter = &wegroups[0]; + for (uint i = wgroups_len; i--; wegrp_iter++) { + wegrp_iter->group.ofs -= wegrp_iter->group.len; + } + + *r_edge_groups_buffer = groups_buffer; + *r_edge_groups = wegroups; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Weld Poly and Loop API + * \{ */ + +static bool weld_iter_loop_of_poly_begin(WeldLoopOfPolyIter *iter, + const WeldPoly *wp, + const WeldLoop *wloop, + const MLoop *mloop, + const uint *loop_map, + uint *group_buffer) +{ + if (wp->flag == ELEM_COLLAPSED) { + return false; + } + + iter->loop_start = wp->loop_start; + iter->loop_end = wp->loop_end; + iter->wloop = wloop; + iter->mloop = mloop; + iter->loop_map = loop_map; + iter->group = group_buffer; + + uint group_len = 0; + if (group_buffer) { + /* First loop group needs more attention. */ + uint loop_start, loop_end, l; + loop_start = iter->loop_start; + loop_end = l = iter->loop_end; + while (l >= loop_start) { + const uint loop_ctx = loop_map[l]; + if (loop_ctx != OUT_OF_CONTEXT) { + const WeldLoop *wl = &wloop[loop_ctx]; + if (wl->flag == ELEM_COLLAPSED) { + l--; + continue; + } + } + break; + } + if (l != loop_end) { + group_len = loop_end - l; + int i = 0; + while (l < loop_end) { + iter->group[i++] = ++l; + } + } + } + iter->group_len = group_len; + + iter->l_next = iter->loop_start; +#ifdef USE_WELD_DEBUG + iter->v = OUT_OF_CONTEXT; +#endif + return true; +} + +static bool weld_iter_loop_of_poly_next(WeldLoopOfPolyIter *iter) +{ + uint loop_end = iter->loop_end; + const WeldLoop *wloop = iter->wloop; + const uint *loop_map = iter->loop_map; + uint l = iter->l_curr = iter->l_next; + if (l == iter->loop_start) { + /* `grupo_len` is already calculated in the first loop */ + } + else { + iter->group_len = 0; + } + while (l <= loop_end) { + uint l_next = l + 1; + const uint loop_ctx = loop_map[l]; + if (loop_ctx != OUT_OF_CONTEXT) { + const WeldLoop *wl = &wloop[loop_ctx]; + if (wl->loop_skip_to != OUT_OF_CONTEXT) { + l_next = wl->loop_skip_to; + } + if (wl->flag == ELEM_COLLAPSED) { + if (iter->group) { + iter->group[iter->group_len++] = l; + } + l = l_next; + continue; + } +#ifdef USE_WELD_DEBUG + BLI_assert(iter->v != wl->vert); +#endif + iter->v = wl->vert; + iter->e = wl->edge; + iter->type = 1; + } + else { + const MLoop *ml = &iter->mloop[l]; +#ifdef USE_WELD_DEBUG + BLI_assert(iter->v != ml->v); +#endif + iter->v = ml->v; + iter->e = ml->e; + iter->type = 0; + } + if (iter->group) { + iter->group[iter->group_len++] = l; + } + iter->l_next = l_next; + return true; + } + + return false; +} + +static void weld_poly_loop_ctx_alloc(const MPoly *mpoly, + const uint mpoly_len, + const MLoop *mloop, + const uint mloop_len, + const uint *vert_dest_map, + const uint *edge_dest_map, + WeldMesh *r_weld_mesh) +{ + /* Loop/Poly Context. */ + uint *loop_map = MEM_mallocN(sizeof(*loop_map) * mloop_len, __func__); + uint *poly_map = MEM_mallocN(sizeof(*poly_map) * mpoly_len, __func__); + uint wloop_len = 0; + uint wpoly_len = 0; + uint max_ctx_poly_len = 4; + + WeldLoop *wloop, *wl; + wloop = MEM_mallocN(sizeof(*wloop) * mloop_len, __func__); + wl = &wloop[0]; + + WeldPoly *wpoly, *wp; + wpoly = MEM_mallocN(sizeof(*wpoly) * mpoly_len, __func__); + wp = &wpoly[0]; + + uint maybe_new_poly = 0; + + const MPoly *mp = &mpoly[0]; + uint *iter = &poly_map[0]; + uint *loop_map_iter = &loop_map[0]; + for (uint i = 0; i < mpoly_len; i++, mp++, iter++) { + const uint loopstart = mp->loopstart; + const uint totloop = mp->totloop; + + uint vert_ctx_len = 0; + + uint l = loopstart; + uint prev_wloop_len = wloop_len; + const MLoop *ml = &mloop[l]; + for (uint j = totloop; j--; l++, ml++, loop_map_iter++) { + uint v = ml->v; + uint e = ml->e; + uint v_dest = vert_dest_map[v]; + uint e_dest = edge_dest_map[e]; + bool is_vert_ctx = v_dest != OUT_OF_CONTEXT; + bool is_edge_ctx = e_dest != OUT_OF_CONTEXT; + if (is_vert_ctx) { + vert_ctx_len++; + } + if (is_vert_ctx || is_edge_ctx) { + wl->vert = is_vert_ctx ? v_dest : v; + wl->edge = is_edge_ctx ? e_dest : e; + wl->loop_orig = l; + wl->loop_skip_to = OUT_OF_CONTEXT; + wl++; + + *loop_map_iter = wloop_len++; + } + else { + *loop_map_iter = OUT_OF_CONTEXT; + } + } + if (wloop_len != prev_wloop_len) { + uint loops_len = wloop_len - prev_wloop_len; + + wp->poly_dst = OUT_OF_CONTEXT; + wp->poly_orig = i; + wp->loops.len = loops_len; + wp->loops.ofs = prev_wloop_len; + wp->loop_start = loopstart; + wp->loop_end = loopstart + totloop - 1; + wp->len = totloop; + wp++; + + *iter = wpoly_len++; + if (totloop > 5 && vert_ctx_len > 1) { + uint max_new = (totloop / 3) - 1; + vert_ctx_len /= 2; + maybe_new_poly += MIN2(max_new, vert_ctx_len); + CLAMP_MIN(max_ctx_poly_len, totloop); + } + } + else { + *iter = OUT_OF_CONTEXT; + } + } + + if (mpoly_len < (wpoly_len + maybe_new_poly)) { + WeldPoly *wpoly_tmp = wpoly; + wpoly = MEM_mallocN(sizeof(*wpoly) * ((size_t)wpoly_len + maybe_new_poly), __func__); + memcpy(wpoly, wpoly_tmp, sizeof(*wpoly) * wpoly_len); + MEM_freeN(wpoly_tmp); + } + + WeldPoly *poly_new = &wpoly[wpoly_len]; + + r_weld_mesh->wloop = MEM_reallocN(wloop, sizeof(*wloop) * wloop_len); + r_weld_mesh->wpoly = wpoly; + r_weld_mesh->wpoly_new = poly_new; + r_weld_mesh->wloop_len = wloop_len; + r_weld_mesh->wpoly_len = wpoly_len; + r_weld_mesh->wpoly_new_len = 0; + r_weld_mesh->loop_map = loop_map; + r_weld_mesh->poly_map = poly_map; + r_weld_mesh->max_poly_len = max_ctx_poly_len; +} + +static void weld_poly_split_recursive(const uint *vert_dest_map, +#ifdef USE_WELD_DEBUG + const MLoop *mloop, +#endif + uint ctx_verts_len, + WeldPoly *r_wp, + WeldMesh *r_weld_mesh, + uint *r_poly_kill, + uint *r_loop_kill) +{ + uint poly_len = r_wp->len; + if (poly_len > 3 && ctx_verts_len > 1) { + const uint ctx_loops_len = r_wp->loops.len; + const uint ctx_loops_ofs = r_wp->loops.ofs; + WeldLoop *wloop = r_weld_mesh->wloop; + WeldPoly *wpoly_new = r_weld_mesh->wpoly_new; + + uint loop_kill = 0; + + WeldLoop *poly_loops = &wloop[ctx_loops_ofs]; + WeldLoop *wla = &poly_loops[0]; + WeldLoop *wla_prev = &poly_loops[ctx_loops_len - 1]; + while (wla_prev->flag == ELEM_COLLAPSED) { + wla_prev--; + } + const uint la_len = ctx_loops_len - 1; + for (uint la = 0; la < la_len; la++, wla++) { + wa_continue: + if (wla->flag == ELEM_COLLAPSED) { + continue; + } + uint vert_a = wla->vert; + /* Only test vertices that will be merged. */ + if (vert_dest_map[vert_a] != OUT_OF_CONTEXT) { + uint lb = la + 1; + WeldLoop *wlb = wla + 1; + WeldLoop *wlb_prev = wla; + uint killed_ab = 0; + ctx_verts_len = 1; + for (; lb < ctx_loops_len; lb++, wlb++) { + BLI_assert(wlb->loop_skip_to == OUT_OF_CONTEXT); + if (wlb->flag == ELEM_COLLAPSED) { + killed_ab++; + continue; + } + uint vert_b = wlb->vert; + if (vert_dest_map[vert_b] != OUT_OF_CONTEXT) { + ctx_verts_len++; + } + if (vert_a == vert_b) { + const uint dist_a = wlb->loop_orig - wla->loop_orig - killed_ab; + const uint dist_b = poly_len - dist_a; + + BLI_assert(dist_a != 0 && dist_b != 0); + if (dist_a == 1 || dist_b == 1) { + BLI_assert(dist_a != dist_b); + BLI_assert((wla->flag == ELEM_COLLAPSED) || (wlb->flag == ELEM_COLLAPSED)); + } + else { + WeldLoop *wl_tmp = NULL; + if (dist_a == 2) { + wl_tmp = wlb_prev; + BLI_assert(wla->flag != ELEM_COLLAPSED); + BLI_assert(wl_tmp->flag != ELEM_COLLAPSED); + wla->flag = ELEM_COLLAPSED; + wl_tmp->flag = ELEM_COLLAPSED; + loop_kill += 2; + poly_len -= 2; + } + if (dist_b == 2) { + if (wl_tmp != NULL) { + r_wp->flag = ELEM_COLLAPSED; + *r_poly_kill += 1; + } + else { + wl_tmp = wla_prev; + BLI_assert(wlb->flag != ELEM_COLLAPSED); + BLI_assert(wl_tmp->flag != ELEM_COLLAPSED); + wlb->flag = ELEM_COLLAPSED; + wl_tmp->flag = ELEM_COLLAPSED; + } + loop_kill += 2; + poly_len -= 2; + } + if (wl_tmp == NULL) { + const uint new_loops_len = lb - la; + const uint new_loops_ofs = ctx_loops_ofs + la; + + WeldPoly *new_wp = &wpoly_new[r_weld_mesh->wpoly_new_len++]; + new_wp->poly_dst = OUT_OF_CONTEXT; + new_wp->poly_orig = r_wp->poly_orig; + new_wp->loops.len = new_loops_len; + new_wp->loops.ofs = new_loops_ofs; + new_wp->loop_start = wla->loop_orig; + new_wp->loop_end = wlb_prev->loop_orig; + new_wp->len = dist_a; + weld_poly_split_recursive(vert_dest_map, +#ifdef USE_WELD_DEBUG + mloop, +#endif + ctx_verts_len, + new_wp, + r_weld_mesh, + r_poly_kill, + r_loop_kill); + BLI_assert(dist_b == poly_len - dist_a); + poly_len = dist_b; + if (wla_prev->loop_orig > wla->loop_orig) { + /* New start. */ + r_wp->loop_start = wlb->loop_orig; + } + else { + /* The `loop_start` doesn't change but some loops must be skipped. */ + wla_prev->loop_skip_to = wlb->loop_orig; + } + wla = wlb; + la = lb; + goto wa_continue; + } + break; + } + } + if (wlb->flag != ELEM_COLLAPSED) { + wlb_prev = wlb; + } + } + } + if (wla->flag != ELEM_COLLAPSED) { + wla_prev = wla; + } + } + r_wp->len = poly_len; + *r_loop_kill += loop_kill; + +#ifdef USE_WELD_DEBUG + weld_assert_poly_no_vert_repetition(r_wp, wloop, mloop, r_weld_mesh->loop_map); +#endif + } +} + +static void weld_poly_loop_ctx_setup(const MLoop *mloop, +#ifdef USE_WELD_DEBUG + const MPoly *mpoly, + const uint mpoly_len, + const uint mloop_len, +#endif + const uint mvert_len, + const uint *vert_dest_map, + const uint remain_edge_ctx_len, + struct WeldGroup *r_vlinks, + WeldMesh *r_weld_mesh) +{ + uint poly_kill_len, loop_kill_len, wpoly_len, wpoly_new_len; + + WeldPoly *wpoly_new, *wpoly, *wp; + WeldLoop *wloop, *wl; + + wpoly = r_weld_mesh->wpoly; + wloop = r_weld_mesh->wloop; + wpoly_new = r_weld_mesh->wpoly_new; + wpoly_len = r_weld_mesh->wpoly_len; + wpoly_new_len = 0; + poly_kill_len = 0; + loop_kill_len = 0; + + const uint *loop_map = r_weld_mesh->loop_map; + + if (remain_edge_ctx_len) { + + /* Setup Poly/Loop. */ + + wp = &wpoly[0]; + for (uint i = wpoly_len; i--; wp++) { + const uint ctx_loops_len = wp->loops.len; + const uint ctx_loops_ofs = wp->loops.ofs; + + uint poly_len = wp->len; + uint ctx_verts_len = 0; + wl = &wloop[ctx_loops_ofs]; + for (uint l = ctx_loops_len; l--; wl++) { + const uint edge_dest = wl->edge; + if (edge_dest == ELEM_COLLAPSED) { + wl->flag = ELEM_COLLAPSED; + if (poly_len == 3) { + wp->flag = ELEM_COLLAPSED; + poly_kill_len++; + loop_kill_len += 3; + poly_len = 0; + break; + } + loop_kill_len++; + poly_len--; + } + else { + const uint vert_dst = wl->vert; + if (vert_dest_map[vert_dst] != OUT_OF_CONTEXT) { + ctx_verts_len++; + } + } + } + + if (poly_len) { + wp->len = poly_len; +#ifdef USE_WELD_DEBUG + weld_assert_poly_len(wp, wloop); +#endif + + weld_poly_split_recursive(vert_dest_map, +#ifdef USE_WELD_DEBUG + mloop, +#endif + ctx_verts_len, + wp, + r_weld_mesh, + &poly_kill_len, + &loop_kill_len); + + wpoly_new_len = r_weld_mesh->wpoly_new_len; + } + } + +#ifdef USE_WELD_DEBUG + weld_assert_poly_and_loop_kill_len(wpoly, + wpoly_new, + wpoly_new_len, + wloop, + mloop, + loop_map, + r_weld_mesh->poly_map, + mpoly, + mpoly_len, + mloop_len, + poly_kill_len, + loop_kill_len); +#endif + + /* Setup Polygon Overlap. */ + + uint wpoly_and_new_len = wpoly_len + wpoly_new_len; + + struct WeldGroup *vl_iter, *v_links = r_vlinks; + memset(v_links, 0, sizeof(*v_links) * mvert_len); + + wp = &wpoly[0]; + for (uint i = wpoly_and_new_len; i--; wp++) { + WeldLoopOfPolyIter iter; + if (weld_iter_loop_of_poly_begin(&iter, wp, wloop, mloop, loop_map, NULL)) { + while (weld_iter_loop_of_poly_next(&iter)) { + v_links[iter.v].len++; + } + } + } + + uint link_len = 0; + vl_iter = &v_links[0]; + for (uint i = mvert_len; i--; vl_iter++) { + vl_iter->ofs = link_len; + link_len += vl_iter->len; + } + + if (link_len) { + uint *link_poly_buffer = MEM_mallocN(sizeof(*link_poly_buffer) * link_len, __func__); + + wp = &wpoly[0]; + for (uint i = 0; i < wpoly_and_new_len; i++, wp++) { + WeldLoopOfPolyIter iter; + if (weld_iter_loop_of_poly_begin(&iter, wp, wloop, mloop, loop_map, NULL)) { + while (weld_iter_loop_of_poly_next(&iter)) { + link_poly_buffer[v_links[iter.v].ofs++] = i; + } + } + } + + vl_iter = &v_links[0]; + for (uint i = mvert_len; i--; vl_iter++) { + /* Fix offset */ + vl_iter->ofs -= vl_iter->len; + } + + uint polys_len_a, polys_len_b, *polys_ctx_a, *polys_ctx_b, p_ctx_a, p_ctx_b; + polys_len_b = p_ctx_b = 0; /* silence warnings */ + + wp = &wpoly[0]; + for (uint i = 0; i < wpoly_and_new_len; i++, wp++) { + if (wp->poly_dst != OUT_OF_CONTEXT) { + /* No need to retest poly. + * (Already includes collapsed polygons). */ + continue; + } + + WeldLoopOfPolyIter iter; + weld_iter_loop_of_poly_begin(&iter, wp, wloop, mloop, loop_map, NULL); + weld_iter_loop_of_poly_next(&iter); + struct WeldGroup *link_a = &v_links[iter.v]; + polys_len_a = link_a->len; + if (polys_len_a == 1) { + BLI_assert(link_poly_buffer[link_a->ofs] == i); + continue; + } + uint wp_len = wp->len; + polys_ctx_a = &link_poly_buffer[link_a->ofs]; + for (; polys_len_a--; polys_ctx_a++) { + p_ctx_a = *polys_ctx_a; + if (p_ctx_a == i) { + continue; + } + + WeldPoly *wp_tmp = &wpoly[p_ctx_a]; + if (wp_tmp->len != wp_len) { + continue; + } + + WeldLoopOfPolyIter iter_b = iter; + while (weld_iter_loop_of_poly_next(&iter_b)) { + struct WeldGroup *link_b = &v_links[iter_b.v]; + polys_len_b = link_b->len; + if (polys_len_b == 1) { + BLI_assert(link_poly_buffer[link_b->ofs] == i); + polys_len_b = 0; + break; + } + + polys_ctx_b = &link_poly_buffer[link_b->ofs]; + for (; polys_len_b; polys_len_b--, polys_ctx_b++) { + p_ctx_b = *polys_ctx_b; + if (p_ctx_b < p_ctx_a) { + continue; + } + if (p_ctx_b >= p_ctx_a) { + if (p_ctx_b > p_ctx_a) { + polys_len_b = 0; + } + break; + } + } + if (polys_len_b == 0) { + break; + } + } + if (polys_len_b == 0) { + continue; + } + BLI_assert(p_ctx_a > i); + BLI_assert(p_ctx_a == p_ctx_b); + BLI_assert(wp_tmp->poly_dst == OUT_OF_CONTEXT); + BLI_assert(wp_tmp != wp); + wp_tmp->poly_dst = wp->poly_orig; + loop_kill_len += wp_tmp->len; + poly_kill_len++; + } + } + MEM_freeN(link_poly_buffer); + } + } + else { + poly_kill_len = r_weld_mesh->wpoly_len; + loop_kill_len = r_weld_mesh->wloop_len; + + wp = &wpoly[0]; + for (uint i = wpoly_len; i--; wp++) { + wp->flag = ELEM_COLLAPSED; + } + } + +#ifdef USE_WELD_DEBUG + weld_assert_poly_and_loop_kill_len(wpoly, + wpoly_new, + wpoly_new_len, + wloop, + mloop, + loop_map, + r_weld_mesh->poly_map, + mpoly, + mpoly_len, + mloop_len, + poly_kill_len, + loop_kill_len); +#endif + + r_weld_mesh->wpoly_new = wpoly_new; + r_weld_mesh->poly_kill_len = poly_kill_len; + r_weld_mesh->loop_kill_len = loop_kill_len; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Weld Mesh API + * \{ */ + +static void weld_mesh_context_create(const Mesh *mesh, + uint *vert_dest_map, + const uint vert_kill_len, + WeldMesh *r_weld_mesh) +{ + const MEdge *medge = mesh->medge; + const MLoop *mloop = mesh->mloop; + const MPoly *mpoly = mesh->mpoly; + const uint mvert_len = mesh->totvert; + const uint medge_len = mesh->totedge; + const uint mloop_len = mesh->totloop; + const uint mpoly_len = mesh->totpoly; + + uint *edge_dest_map = MEM_mallocN(sizeof(*edge_dest_map) * medge_len, __func__); + struct WeldGroup *v_links = MEM_callocN(sizeof(*v_links) * mvert_len, __func__); + + WeldVert *wvert; + uint wvert_len; + r_weld_mesh->vert_kill_len = vert_kill_len; + weld_vert_ctx_alloc_and_setup(mvert_len, vert_dest_map, &wvert, &wvert_len); + + uint *edge_ctx_map; + WeldEdge *wedge; + uint wedge_len; + weld_edge_ctx_alloc( + medge, medge_len, vert_dest_map, edge_dest_map, &edge_ctx_map, &wedge, &wedge_len); + + weld_edge_ctx_setup( + mvert_len, wedge_len, v_links, edge_dest_map, wedge, &r_weld_mesh->edge_kill_len); + + weld_poly_loop_ctx_alloc( + mpoly, mpoly_len, mloop, mloop_len, vert_dest_map, edge_dest_map, r_weld_mesh); + + weld_poly_loop_ctx_setup(mloop, +#ifdef USE_WELD_DEBUG + mpoly, + mpoly_len, + mloop_len, +#endif + mvert_len, + vert_dest_map, + wedge_len - r_weld_mesh->edge_kill_len, + v_links, + r_weld_mesh); + + weld_vert_groups_setup(mvert_len, + wvert_len, + wvert, + vert_dest_map, + vert_dest_map, + &r_weld_mesh->vert_groups_buffer, + &r_weld_mesh->vert_groups); + + weld_edge_groups_setup(medge_len, + r_weld_mesh->edge_kill_len, + wedge_len, + wedge, + edge_ctx_map, + edge_dest_map, + &r_weld_mesh->edge_groups_buffer, + &r_weld_mesh->edge_groups); + + r_weld_mesh->edge_groups_map = edge_dest_map; + MEM_freeN(v_links); + MEM_freeN(wvert); + MEM_freeN(edge_ctx_map); + MEM_freeN(wedge); +} + +static void weld_mesh_context_free(WeldMesh *weld_mesh) +{ + MEM_freeN(weld_mesh->vert_groups); + MEM_freeN(weld_mesh->vert_groups_buffer); + + MEM_freeN(weld_mesh->edge_groups); + MEM_freeN(weld_mesh->edge_groups_buffer); + MEM_freeN(weld_mesh->edge_groups_map); + + MEM_freeN(weld_mesh->wloop); + MEM_freeN(weld_mesh->wpoly); + MEM_freeN(weld_mesh->loop_map); + MEM_freeN(weld_mesh->poly_map); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Weld CustomData + * \{ */ + +static void customdata_weld( + const CustomData *source, CustomData *dest, const uint *src_indices, int count, int dest_index) +{ + if (count == 1) { + CustomData_copy_data(source, dest, src_indices[0], dest_index, 1); + return; + } + + CustomData_interp(source, dest, (const int *)src_indices, NULL, NULL, count, dest_index); + + int src_i, dest_i; + int j; + + float co[3] = {0.0f, 0.0f, 0.0f}; +#ifdef USE_WELD_NORMALS + float no[3] = {0.0f, 0.0f, 0.0f}; +#endif + uint crease = 0; + uint bweight = 0; + short flag = 0; + + /* interpolates a layer at a time */ + dest_i = 0; + for (src_i = 0; src_i < source->totlayer; src_i++) { + const int type = source->layers[src_i].type; + + /* find the first dest layer with type >= the source type + * (this should work because layers are ordered by type) + */ + while (dest_i < dest->totlayer && dest->layers[dest_i].type < type) { + dest_i++; + } + + /* if there are no more dest layers, we're done */ + if (dest_i == dest->totlayer) { + break; + } + + /* if we found a matching layer, add the data */ + if (dest->layers[dest_i].type == type) { + void *src_data = source->layers[src_i].data; + + if (type == CD_MVERT) { + for (j = 0; j < count; j++) { + MVert *mv_src = &((MVert *)src_data)[src_indices[j]]; + add_v3_v3(co, mv_src->co); +#ifdef USE_WELD_NORMALS + short *mv_src_no = mv_src->no; + no[0] += mv_src_no[0]; + no[1] += mv_src_no[1]; + no[2] += mv_src_no[2]; +#endif + bweight += mv_src->bweight; + flag |= mv_src->flag; + } + } + else if (type == CD_MEDGE) { + for (j = 0; j < count; j++) { + MEdge *me_src = &((MEdge *)src_data)[src_indices[j]]; + crease += me_src->crease; + bweight += me_src->bweight; + flag |= me_src->flag; + } + } + else if (CustomData_layer_has_interp(dest, dest_i)) { + /* Already calculated. + * TODO: Optimize by exposing `typeInfo->interp`. */ + } + else if (CustomData_layer_has_math(dest, dest_i)) { + const int size = CustomData_sizeof(type); + void *dst_data = dest->layers[dest_i].data; + void *v_dst = POINTER_OFFSET(dst_data, (size_t)dest_index * size); + for (j = 0; j < count; j++) { + CustomData_data_add( + type, v_dst, POINTER_OFFSET(src_data, (size_t)src_indices[j] * size)); + } + } + else { + CustomData_copy_layer_type_data(source, dest, type, src_indices[0], dest_index, 1); + } + + /* if there are multiple source & dest layers of the same type, + * we don't want to copy all source layers to the same dest, so + * increment dest_i + */ + dest_i++; + } + } + + float fac = 1.0f / count; + + for (dest_i = 0; dest_i < dest->totlayer; dest_i++) { + CustomDataLayer *layer_dst = &dest->layers[dest_i]; + const int type = layer_dst->type; + if (type == CD_MVERT) { + MVert *mv = &((MVert *)layer_dst->data)[dest_index]; + mul_v3_fl(co, fac); + bweight *= fac; + CLAMP_MAX(bweight, 255); + + copy_v3_v3(mv->co, co); +#ifdef USE_WELD_NORMALS + mul_v3_fl(no, fac); + short *mv_no = mv->no; + mv_no[0] = (short)no[0]; + mv_no[1] = (short)no[1]; + mv_no[2] = (short)no[2]; +#endif + + mv->flag = (char)flag; + mv->bweight = (char)bweight; + } + else if (type == CD_MEDGE) { + MEdge *me = &((MEdge *)layer_dst->data)[dest_index]; + crease *= fac; + bweight *= fac; + CLAMP_MAX(crease, 255); + CLAMP_MAX(bweight, 255); + + me->crease = (char)crease; + me->bweight = (char)bweight; + me->flag = flag; + } + else if (CustomData_layer_has_interp(dest, dest_i)) { + /* Already calculated. */ + } + else if (CustomData_layer_has_math(dest, dest_i)) { + const int size = CustomData_sizeof(type); + void *dst_data = layer_dst->data; + void *v_dst = POINTER_OFFSET(dst_data, (size_t)dest_index * size); + CustomData_data_multiply(type, v_dst, fac); + } + } +} + +/** \} */ + +#ifdef USE_BVHTREEKDOP +struct WeldOverlapData { + const MVert *mvert; + float merge_dist_sq; +}; +static bool bvhtree_weld_overlap_cb(void *userdata, int index_a, int index_b, int UNUSED(thread)) +{ + if (index_a < index_b) { + struct WeldOverlapData *data = userdata; + const MVert *mvert = data->mvert; + const float dist_sq = len_squared_v3v3(mvert[index_a].co, mvert[index_b].co); + BLI_assert(dist_sq <= ((data->merge_dist_sq + FLT_EPSILON) * 3)); + return dist_sq <= data->merge_dist_sq; + } + return false; +} +#endif + +/** Use for #MOD_WELD_MODE_CONNECTED calculation. */ +struct WeldVertexCluster { + float co[3]; + uint merged_verts; +}; + +Mesh *GEO_mesh_merge_by_distance(const Mesh *mesh, const bool *mask, const float merge_distance, const int weld_mode) +{ + Mesh *result = mesh; + + const MVert *mvert; + const MLoop *mloop; + const MPoly *mpoly, *mp; + uint totvert, totedge, totloop, totpoly; + + mvert = mesh->mvert; + totvert = mesh->totvert; + + /* Vertex Group. */ + + /* From the original index of the vertex. + * This indicates which vert it is or is going to be merged. */ + uint *vert_dest_map = MEM_malloc_arrayN(totvert, sizeof(*vert_dest_map), __func__); + uint vert_kill_len = 0; + if (weld_mode == WELD_MODE_ALL) +#ifdef USE_BVHTREEKDOP + { + /* Get overlap map. */ + struct BVHTreeFromMesh treedata; + BVHTree *bvhtree = bvhtree_from_mesh_verts_ex(&treedata, + mvert, + totvert, + false, + v_mask, + v_mask_act, + merge_distance / 2, + 2, + 6, + 0, + NULL, + NULL); + + if (bvhtree) { + struct WeldOverlapData data; + data.mvert = mvert; + data.merge_dist_sq = square_f(merge_distance); + + uint overlap_len; + BVHTreeOverlap *overlap = BLI_bvhtree_overlap_ex(bvhtree, + bvhtree, + &overlap_len, + bvhtree_weld_overlap_cb, + &data, + 1, + BVH_OVERLAP_RETURN_PAIRS); + + free_bvhtree_from_mesh(&treedata); + if (overlap) { + range_vn_u(vert_dest_map, totvert, 0); + + const BVHTreeOverlap *overlap_iter = &overlap[0]; + for (uint i = 0; i < overlap_len; i++, overlap_iter++) { + uint indexA = overlap_iter->indexA; + uint indexB = overlap_iter->indexB; + + BLI_assert(indexA < indexB); + + uint va_dst = vert_dest_map[indexA]; + while (va_dst != vert_dest_map[va_dst]) { + va_dst = vert_dest_map[va_dst]; + } + uint vb_dst = vert_dest_map[indexB]; + while (vb_dst != vert_dest_map[vb_dst]) { + vb_dst = vert_dest_map[vb_dst]; + } + if (va_dst == vb_dst) { + continue; + } + if (va_dst > vb_dst) { + SWAP(uint, va_dst, vb_dst); + } + vert_kill_len++; + vert_dest_map[vb_dst] = va_dst; + } + + /* Fix #r_vert_dest_map for next step. */ + for (uint i = 0; i < totvert; i++) { + if (i == vert_dest_map[i]) { + vert_dest_map[i] = OUT_OF_CONTEXT; + } + else { + uint v = i; + while (v != vert_dest_map[v] && vert_dest_map[v] != OUT_OF_CONTEXT) { + v = vert_dest_map[v]; + } + vert_dest_map[v] = v; + vert_dest_map[i] = v; + } + } + + MEM_freeN(overlap); + } + } + } +#else + { + KDTree_3d *tree = BLI_kdtree_3d_new(totvert); + for (uint i = 0; i < totvert; i++) { + if (!mask || mask[i]) { + BLI_kdtree_3d_insert(tree, i, mvert[i].co); + } + vert_dest_map[i] = OUT_OF_CONTEXT; + } + + BLI_kdtree_3d_balance(tree); + vert_kill_len = BLI_kdtree_3d_calc_duplicates_fast( + tree, merge_distance, false, (int *)vert_dest_map); + BLI_kdtree_3d_free(tree); + } +#endif + else { + BLI_assert(weld_mode == WELD_MODE_CONNECTED); + + MEdge *medge, *me; + + medge = mesh->medge; + totvert = mesh->totvert; + totedge = mesh->totedge; + + struct WeldVertexCluster *vert_clusters = MEM_malloc_arrayN( + totvert, sizeof(*vert_clusters), __func__); + struct WeldVertexCluster *vc = &vert_clusters[0]; + for (uint i = 0; i < totvert; i++, vc++) { + copy_v3_v3(vc->co, mvert[i].co); + vc->merged_verts = 0; + } + const float merge_dist_sq = square_f(merge_distance); + + range_vn_u(vert_dest_map, totvert, 0); + + /* Collapse Edges that are shorter than the threshold. */ + me = &medge[0]; + for (uint i = 0; i < totedge; i++, me++) { + uint v1 = me->v1; + uint v2 = me->v2; + + while (v1 != vert_dest_map[v1]) { + v1 = vert_dest_map[v1]; + } + while (v2 != vert_dest_map[v2]) { + v2 = vert_dest_map[v2]; + } + if (v1 == v2) { + continue; + } + if (mask && (!mask[v1] || !mask[v2])) { + continue; + } + if (v1 > v2) { + SWAP(uint, v1, v2); + } + struct WeldVertexCluster *v1_cluster = &vert_clusters[v1]; + struct WeldVertexCluster *v2_cluster = &vert_clusters[v2]; + + float edgedir[3]; + sub_v3_v3v3(edgedir, v2_cluster->co, v1_cluster->co); + const float dist_sq = len_squared_v3(edgedir); + if (dist_sq <= merge_dist_sq) { + float influence = (v2_cluster->merged_verts + 1) / + (float)(v1_cluster->merged_verts + v2_cluster->merged_verts + 2); + madd_v3_v3fl(v1_cluster->co, edgedir, influence); + + v1_cluster->merged_verts += v2_cluster->merged_verts + 1; + vert_dest_map[v2] = v1; + vert_kill_len++; + } + } + + MEM_freeN(vert_clusters); + + for (uint i = 0; i < totvert; i++) { + if (i == vert_dest_map[i]) { + vert_dest_map[i] = OUT_OF_CONTEXT; + } + else { + uint v = i; + while ((v != vert_dest_map[v]) && (vert_dest_map[v] != OUT_OF_CONTEXT)) { + v = vert_dest_map[v]; + } + vert_dest_map[v] = v; + vert_dest_map[i] = v; + } + } + } + + if (vert_kill_len) { + WeldMesh weld_mesh; + weld_mesh_context_create(mesh, vert_dest_map, vert_kill_len, &weld_mesh); + + mloop = mesh->mloop; + mpoly = mesh->mpoly; + + totedge = mesh->totedge; + totloop = mesh->totloop; + totpoly = mesh->totpoly; + + const int result_nverts = totvert - weld_mesh.vert_kill_len; + const int result_nedges = totedge - weld_mesh.edge_kill_len; + const int result_nloops = totloop - weld_mesh.loop_kill_len; + const int result_npolys = totpoly - weld_mesh.poly_kill_len + weld_mesh.wpoly_new_len; + + result = BKE_mesh_new_nomain_from_template( + mesh, result_nverts, result_nedges, 0, result_nloops, result_npolys); + + /* Vertices */ + + uint *vert_final = vert_dest_map; + uint *index_iter = &vert_final[0]; + int dest_index = 0; + for (uint i = 0; i < totvert; i++, index_iter++) { + int source_index = i; + int count = 0; + while (i < totvert && *index_iter == OUT_OF_CONTEXT) { + *index_iter = dest_index + count; + index_iter++; + count++; + i++; + } + if (count) { + CustomData_copy_data(&mesh->vdata, &result->vdata, source_index, dest_index, count); + dest_index += count; + } + if (i == totvert) { + break; + } + if (*index_iter != ELEM_MERGED) { + struct WeldGroup *wgroup = &weld_mesh.vert_groups[*index_iter]; + customdata_weld(&mesh->vdata, + &result->vdata, + &weld_mesh.vert_groups_buffer[wgroup->ofs], + wgroup->len, + dest_index); + *index_iter = dest_index; + dest_index++; + } + } + + BLI_assert(dest_index == result_nverts); + + /* Edges */ + + uint *edge_final = weld_mesh.edge_groups_map; + index_iter = &edge_final[0]; + dest_index = 0; + for (uint i = 0; i < totedge; i++, index_iter++) { + int source_index = i; + int count = 0; + while (i < totedge && *index_iter == OUT_OF_CONTEXT) { + *index_iter = dest_index + count; + index_iter++; + count++; + i++; + } + if (count) { + CustomData_copy_data(&mesh->edata, &result->edata, source_index, dest_index, count); + MEdge *me = &result->medge[dest_index]; + dest_index += count; + for (; count--; me++) { + me->v1 = vert_final[me->v1]; + me->v2 = vert_final[me->v2]; + } + } + if (i == totedge) { + break; + } + if (*index_iter != ELEM_MERGED) { + struct WeldGroupEdge *wegrp = &weld_mesh.edge_groups[*index_iter]; + customdata_weld(&mesh->edata, + &result->edata, + &weld_mesh.edge_groups_buffer[wegrp->group.ofs], + wegrp->group.len, + dest_index); + MEdge *me = &result->medge[dest_index]; + me->v1 = vert_final[wegrp->v1]; + me->v2 = vert_final[wegrp->v2]; + me->flag |= ME_LOOSEEDGE; + + *index_iter = dest_index; + dest_index++; + } + } + + BLI_assert(dest_index == result_nedges); + + /* Polys/Loops */ + + mp = &mpoly[0]; + MPoly *r_mp = &result->mpoly[0]; + MLoop *r_ml = &result->mloop[0]; + uint r_i = 0; + int loop_cur = 0; + uint *group_buffer = BLI_array_alloca(group_buffer, weld_mesh.max_poly_len); + for (uint i = 0; i < totpoly; i++, mp++) { + int loop_start = loop_cur; + uint poly_ctx = weld_mesh.poly_map[i]; + if (poly_ctx == OUT_OF_CONTEXT) { + uint mp_loop_len = mp->totloop; + CustomData_copy_data(&mesh->ldata, &result->ldata, mp->loopstart, loop_cur, mp_loop_len); + loop_cur += mp_loop_len; + for (; mp_loop_len--; r_ml++) { + r_ml->v = vert_final[r_ml->v]; + r_ml->e = edge_final[r_ml->e]; + } + } + else { + WeldPoly *wp = &weld_mesh.wpoly[poly_ctx]; + WeldLoopOfPolyIter iter; + if (!weld_iter_loop_of_poly_begin( + &iter, wp, weld_mesh.wloop, mloop, weld_mesh.loop_map, group_buffer)) { + continue; + } + + if (wp->poly_dst != OUT_OF_CONTEXT) { + continue; + } + while (weld_iter_loop_of_poly_next(&iter)) { + customdata_weld(&mesh->ldata, &result->ldata, group_buffer, iter.group_len, loop_cur); + uint v = vert_final[iter.v]; + uint e = edge_final[iter.e]; + r_ml->v = v; + r_ml->e = e; + r_ml++; + loop_cur++; + if (iter.type) { + result->medge[e].flag &= ~ME_LOOSEEDGE; + } + BLI_assert((result->medge[e].flag & ME_LOOSEEDGE) == 0); + } + } + + CustomData_copy_data(&mesh->pdata, &result->pdata, i, r_i, 1); + r_mp->loopstart = loop_start; + r_mp->totloop = loop_cur - loop_start; + r_mp++; + r_i++; + } + + WeldPoly *wp = &weld_mesh.wpoly_new[0]; + for (uint i = 0; i < weld_mesh.wpoly_new_len; i++, wp++) { + int loop_start = loop_cur; + WeldLoopOfPolyIter iter; + if (!weld_iter_loop_of_poly_begin( + &iter, wp, weld_mesh.wloop, mloop, weld_mesh.loop_map, group_buffer)) { + continue; + } + + if (wp->poly_dst != OUT_OF_CONTEXT) { + continue; + } + while (weld_iter_loop_of_poly_next(&iter)) { + customdata_weld(&mesh->ldata, &result->ldata, group_buffer, iter.group_len, loop_cur); + uint v = vert_final[iter.v]; + uint e = edge_final[iter.e]; + r_ml->v = v; + r_ml->e = e; + r_ml++; + loop_cur++; + if (iter.type) { + result->medge[e].flag &= ~ME_LOOSEEDGE; + } + BLI_assert((result->medge[e].flag & ME_LOOSEEDGE) == 0); + } + + r_mp->loopstart = loop_start; + r_mp->totloop = loop_cur - loop_start; + r_mp++; + r_i++; + } + + BLI_assert((int)r_i == result_npolys); + BLI_assert(loop_cur == result_nloops); + + /* is this needed? */ + /* recalculate normals */ + result->runtime.cd_dirty_vert |= CD_MASK_NORMAL; + + weld_mesh_context_free(&weld_mesh); + } + + MEM_freeN(vert_dest_map); + return result; +} diff --git a/source/blender/geometry/intern/pointcloud_merge_by_distance.cc b/source/blender/geometry/intern/pointcloud_merge_by_distance.cc new file mode 100644 index 00000000000..77794bb28b0 --- /dev/null +++ b/source/blender/geometry/intern/pointcloud_merge_by_distance.cc @@ -0,0 +1,120 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BKE_pointcloud.h" + +#include "BLI_array.hh" +#include "BLI_float3.hh" +#include "BLI_kdtree.h" +#include "BLI_span.hh" +#include "BLI_vector.hh" + +#include "DNA_pointcloud_types.h" + +#include "GEO_pointcloud_merge_by_distance.h" /* Own include. */ + +#include "FN_generic_span.hh" + +using blender::Array; +using blender::float3; +using blender::Span; +using blender::Vector; + +static KDTree_3d *build_kdtree(Span<float3> positions, Span<bool> selection) +{ + BLI_assert(positions.size() == selection.size()); + + KDTree_3d *kdtree = BLI_kdtree_3d_new(selection.size()); + + for (const int i : positions.index_range()) { + if (selection[i]) { + BLI_kdtree_3d_insert(kdtree, i, positions[i]); + } + } + + BLI_kdtree_3d_balance(kdtree); + return kdtree; +} + +static void build_merge_map(Span<float3> &positions, + Array<bool> &merge_map, + int &total_merge_operations, + const float merge_threshold, + Span<bool> selection) +{ + KDTree_3d *kdtree = build_kdtree(positions, selection); + + for (int i : positions.index_range()) { + struct CallbackData { + int index; + int &total_merge_operations; + Array<bool> &merge_map; + Span<bool> selection; + } callback_data = {i, total_merge_operations, merge_map, selection}; + + BLI_kdtree_3d_range_search_cb( + kdtree, + positions[i], + merge_threshold, + [](void *user_data, + int source_vertex_index, + const float *UNUSED(co), + float UNUSED(distance_squared)) { + CallbackData &callback_data = *static_cast<CallbackData *>(user_data); + int target_vertex_index = callback_data.index; + if (source_vertex_index != target_vertex_index && + !callback_data.merge_map[source_vertex_index] && + !callback_data.merge_map[target_vertex_index] && + callback_data.selection[source_vertex_index] && + callback_data.selection[target_vertex_index]) { + callback_data.merge_map[source_vertex_index] = true; + callback_data.total_merge_operations++; + } + return true; + }, + &callback_data); + } + + BLI_kdtree_3d_free(kdtree); +} + +PointCloud *merge_by_distance_pointcloud(const PointCloud &point_cloud, + const float merge_threshold, + Span<bool> selection) +{ + + Array<bool> merge_map(point_cloud.totpoint, false); + Span<float3> positions((const float3 *)point_cloud.co, point_cloud.totpoint); + + int total_merge_operations = 0; + + BLI_assert(positions.size() == merge_map.size()); + + build_merge_map(positions, merge_map, total_merge_operations, merge_threshold, selection); + + PointCloud *result = BKE_pointcloud_new_nomain(positions.size() - total_merge_operations); + int offset = 0; + for (const int i : positions.index_range()) { + /* Only copy the unmerged points to new pointcloud. */ + if (!merge_map[i]) { + copy_v3_v3(result->co[offset], positions[i]); + result->radius[offset] = point_cloud.radius[i]; + offset++; + } + } + + return result; +} diff --git a/source/blender/geometry/intern/remesh_blocks.c b/source/blender/geometry/intern/remesh_blocks.c new file mode 100644 index 00000000000..248def94779 --- /dev/null +++ b/source/blender/geometry/intern/remesh_blocks.c @@ -0,0 +1,171 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup geo + */ + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_modifier_types.h" +#include "DNA_customdata_types.h" + +#include "BKE_mesh.h" +#include "BKE_mesh_runtime.h" + +#include "BLI_math_vector.h" +#include "BLI_threads.h" + +#include "GEO_mesh_remesh_blocks.h" /* own include */ + +#include "MEM_guardedalloc.h" + +#ifdef WITH_REMESH_DUALCON +# include "dualcon.h" +#endif + +static void init_dualcon_mesh(DualConInput *input, const Mesh *mesh) +{ + memset(input, 0, sizeof(DualConInput)); + + input->co = (void *)mesh->mvert; + input->co_stride = sizeof(MVert); + input->totco = mesh->totvert; + + input->mloop = (void *)mesh->mloop; + input->loop_stride = sizeof(MLoop); + + BKE_mesh_runtime_looptri_ensure(mesh); + input->looptri = (void *)mesh->runtime.looptris.array; + input->tri_stride = sizeof(MLoopTri); + input->tottri = mesh->runtime.looptris.len; + + INIT_MINMAX(input->min, input->max); + BKE_mesh_minmax(mesh, input->min, input->max); +} + +/* simple structure to hold the output: a CDDM and two counters to + * keep track of the current elements */ +typedef struct { + Mesh *mesh; + int curvert, curface; +} DualConOutput; + +/* allocate and initialize a DualConOutput */ +static void *dualcon_alloc_output(int totvert, int totquad) +{ + DualConOutput *output; + + if (!(output = MEM_callocN(sizeof(DualConOutput), "DualConOutput"))) { + return NULL; + } + + output->mesh = BKE_mesh_new_nomain(totvert, 0, 0, 4 * totquad, totquad); + return output; +} + +static void dualcon_add_vert(void *output_v, const float co[3]) +{ + DualConOutput *output = output_v; + Mesh *mesh = output->mesh; + + BLI_assert(output->curvert < mesh->totvert); + + copy_v3_v3(mesh->mvert[output->curvert].co, co); + output->curvert++; +} + +static void dualcon_add_quad(void *output_v, const int vert_indices[4]) +{ + DualConOutput *output = output_v; + Mesh *mesh = output->mesh; + MLoop *mloop; + MPoly *cur_poly; + int i; + + BLI_assert(output->curface < mesh->totpoly); + + mloop = mesh->mloop; + cur_poly = &mesh->mpoly[output->curface]; + + cur_poly->loopstart = output->curface * 4; + cur_poly->totloop = 4; + for (i = 0; i < 4; i++) { + mloop[output->curface * 4 + i].v = vert_indices[i]; + } + + output->curface++; +} + +Mesh *GEO_mesh_remesh_blocks(const Mesh *mesh, + const char remesh_flag, + const char remesh_mode, + const float threshold, + const int hermite_num, + const float scale, + const int depth) +{ +#ifdef WITH_REMESH_DUALCON + + DualConOutput *output; + DualConInput input; + Mesh *result; + DualConFlags flags = 0; + DualConMode mode = 0; + + /* Dualcon modes. */ + init_dualcon_mesh(&input, mesh); + + if (remesh_flag & MOD_REMESH_FLOOD_FILL) { + flags |= DUALCON_FLOOD_FILL; + } + + switch (remesh_mode) { + case REMESH_BLOCKS_CENTROID: + mode = DUALCON_CENTROID; + break; + case REMESH_BLOCKS_MASS_POINT: + mode = DUALCON_MASS_POINT; + break; + case REMESH_BLOCKS_SHARP_FEATURES: + mode = DUALCON_SHARP_FEATURES; + break; + } + /* TODO(jbakker): Dualcon crashes when run in parallel. Could be related to incorrect + * input data or that the library isn't thread safe. + * This was identified when changing the task isolation's during T76553. */ + static ThreadMutex dualcon_mutex = BLI_MUTEX_INITIALIZER; + BLI_mutex_lock(&dualcon_mutex); + output = dualcon(&input, + dualcon_alloc_output, + dualcon_add_vert, + dualcon_add_quad, + flags, + mode, + threshold, + hermite_num, + scale, + depth); + BLI_mutex_unlock(&dualcon_mutex); + + result = output->mesh; + MEM_freeN(output); + + return result; +#else + return BKE_mesh_new_nomain(0,0,0,0,0); +#endif +} diff --git a/source/blender/modifiers/intern/MOD_solidify_nonmanifold.c b/source/blender/geometry/intern/solidify_nonmanifold.c index 5b4716a1a43..ac01e0cabc9 100644 --- a/source/blender/modifiers/intern/MOD_solidify_nonmanifold.c +++ b/source/blender/geometry/intern/solidify_nonmanifold.c @@ -18,23 +18,19 @@ * \ingroup modifiers */ -#include "BLI_utildefines.h" +#include "BKE_customdata.h" +#include "BKE_mesh.h" #include "BLI_math.h" +#include "BLI_utildefines.h" #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" -#include "DNA_object_types.h" - -#include "MEM_guardedalloc.h" +#include "DNA_modifier_types.h" -#include "BKE_deform.h" -#include "BKE_mesh.h" -#include "BKE_particle.h" +#include "GEO_solidifiy.h" -#include "MOD_modifiertypes.h" -#include "MOD_solidify_util.h" /* Own include. */ -#include "MOD_util.h" +#include "MEM_guardedalloc.h" #ifdef __GNUC__ # pragma GCC diagnostic error "-Wsign-conversion" @@ -143,12 +139,14 @@ static int comp_float_int_pair(const void *a, const void *b) } /* NOLINTNEXTLINE: readability-function-size */ -Mesh *MOD_solidify_nonmanifold_modifyMesh(ModifierData *md, - const ModifierEvalContext *ctx, - Mesh *mesh) +Mesh *solidify_nonmanifold(const SolidifyData *solidify_data, + Mesh *mesh, + bool **r_shell_verts, + bool **r_rim_verts, + bool **r_shell_faces, + bool **r_rim_faces) { Mesh *result; - const SolidifyModifierData *smd = (SolidifyModifierData *)md; MVert *mv, *mvert, *orig_mvert; MEdge *ed, *medge, *orig_medge; @@ -163,39 +161,26 @@ Mesh *MOD_solidify_nonmanifold_modifyMesh(ModifierData *md, return mesh; } - /* Only use material offsets if we have 2 or more materials. */ - const short mat_nrs = ctx->object->totcol > 1 ? ctx->object->totcol : 1; - const short mat_nr_max = mat_nrs - 1; - const short mat_ofs = mat_nrs > 1 ? smd->mat_ofs : 0; - const short mat_ofs_rim = mat_nrs > 1 ? smd->mat_ofs_rim : 0; - float(*poly_nors)[3] = NULL; - const float ofs_front = (smd->offset_fac + 1.0f) * 0.5f * smd->offset; - const float ofs_back = ofs_front - smd->offset * smd->offset_fac; - const float ofs_front_clamped = clamp_nonzero(smd->offset > 0 ? ofs_front : ofs_back, 1e-5f); - const float ofs_back_clamped = clamp_nonzero(smd->offset > 0 ? ofs_back : ofs_front, 1e-5f); - const float offset_fac_vg = smd->offset_fac_vg; - const float offset_fac_vg_inv = 1.0f - smd->offset_fac_vg; - const float offset = fabsf(smd->offset) * smd->offset_clamp; - const bool do_angle_clamp = smd->flag & MOD_SOLIDIFY_OFFSET_ANGLE_CLAMP; - const bool do_flip = (smd->flag & MOD_SOLIDIFY_FLIP) != 0; - const bool do_rim = smd->flag & MOD_SOLIDIFY_RIM; - const bool do_shell = ((smd->flag & MOD_SOLIDIFY_RIM) && (smd->flag & MOD_SOLIDIFY_NOSHELL)) == - 0; - const bool do_clamp = (smd->offset_clamp != 0.0f); - - const float bevel_convex = smd->bevel_convex; - - MDeformVert *dvert; - const bool defgrp_invert = (smd->flag & MOD_SOLIDIFY_VGROUP_INV) != 0; - int defgrp_index; - const int shell_defgrp_index = BKE_id_defgroup_name_index(&mesh->id, smd->shell_defgrp_name); - const int rim_defgrp_index = BKE_id_defgroup_name_index(&mesh->id, smd->rim_defgrp_name); - - MOD_get_vgroup(ctx->object, mesh, smd->defgrp_name, &dvert, &defgrp_index); - - const bool do_flat_faces = dvert && (smd->flag & MOD_SOLIDIFY_NONMANIFOLD_FLAT_FACES); + const float ofs_front = (solidify_data->offset_fac + 1.0f) * 0.5f * solidify_data->offset; + const float ofs_back = ofs_front - solidify_data->offset * solidify_data->offset_fac; + const float ofs_front_clamped = max_ff(1e-5f, + fabsf(solidify_data->offset > 0 ? ofs_front : ofs_back)); + const float ofs_back_clamped = max_ff(1e-5f, + fabsf(solidify_data->offset > 0 ? ofs_back : ofs_front)); + const float offset_fac_vg = solidify_data->offset_fac_vg; + const float offset_fac_vg_inv = 1.0f - solidify_data->offset_fac_vg; + const float offset = fabsf(solidify_data->offset) * solidify_data->offset_clamp; + const bool do_angle_clamp = solidify_data->flag & MOD_SOLIDIFY_OFFSET_ANGLE_CLAMP; + const bool do_flip = (solidify_data->flag & MOD_SOLIDIFY_FLIP) != 0; + const bool do_rim = solidify_data->flag & MOD_SOLIDIFY_RIM; + const bool do_shell = solidify_data->flag & MOD_SOLIDIFY_SHELL; + const bool do_clamp = (solidify_data->offset_clamp != 0.0f); + + const float bevel_convex = solidify_data->bevel_convex; + + const bool do_flat_faces = solidify_data->flag & MOD_SOLIDIFY_NONMANIFOLD_FLAT_FACES; orig_mvert = mesh->mvert; orig_medge = mesh->medge; @@ -216,10 +201,11 @@ Mesh *MOD_solidify_nonmanifold_modifyMesh(ModifierData *md, NewFaceRef *face_sides_arr = MEM_malloc_arrayN( numPolys * 2, sizeof(*face_sides_arr), "face_sides_arr in solidify"); - bool *null_faces = - (smd->nonmanifold_offset_mode == MOD_SOLIDIFY_NONMANIFOLD_OFFSET_MODE_CONSTRAINTS) ? - MEM_calloc_arrayN(numPolys, sizeof(*null_faces), "null_faces in solidify") : - NULL; + bool *null_faces = (solidify_data->nonmanifold_offset_mode == + MOD_SOLIDIFY_NONMANIFOLD_OFFSET_MODE_CONSTRAINTS) ? + MEM_calloc_arrayN( + numPolys, sizeof(*null_faces), "null_faces in solidify") : + NULL; uint largest_ngon = 3; /* Calculate face to #NewFaceRef map. */ { @@ -359,7 +345,8 @@ Mesh *MOD_solidify_nonmanifold_modifyMesh(ModifierData *md, bool *face_singularity = MEM_calloc_arrayN( numPolys, sizeof(*face_singularity), "face_sides_arr in solidify"); - const float merge_tolerance_sqr = smd->merge_tolerance * smd->merge_tolerance; + const float merge_tolerance_sqr = solidify_data->merge_tolerance * + solidify_data->merge_tolerance; uint *combined_verts = MEM_calloc_arrayN( numVerts, sizeof(*combined_verts), "combined_verts in solidify"); @@ -1393,14 +1380,7 @@ Mesh *MOD_solidify_nonmanifold_modifyMesh(ModifierData *md, int loopend = mp->loopstart + mp->totloop; ml = orig_mloop + mp->loopstart; for (int j = mp->loopstart; j < loopend; j++, ml++) { - MDeformVert *dv = &dvert[ml->v]; - if (defgrp_invert) { - scalar_vgroup = min_ff(1.0f - BKE_defvert_find_weight(dv, defgrp_index), - scalar_vgroup); - } - else { - scalar_vgroup = min_ff(BKE_defvert_find_weight(dv, defgrp_index), scalar_vgroup); - } + scalar_vgroup = min_ff(solidify_data->distance[i], scalar_vgroup); } scalar_vgroup = offset_fac_vg + (scalar_vgroup * offset_fac_vg_inv); face_weight[i] = scalar_vgroup; @@ -1416,11 +1396,12 @@ Mesh *MOD_solidify_nonmanifold_modifyMesh(ModifierData *md, if (!g->is_singularity) { float *nor = g->no; float move_nor[3] = {0, 0, 0}; - bool disable_boundary_fix = (smd->nonmanifold_boundary_mode == + bool disable_boundary_fix = (solidify_data->nonmanifold_boundary_mode == MOD_SOLIDIFY_NONMANIFOLD_BOUNDARY_MODE_NONE || (g->is_orig_closed || g->split)); /* Constraints Method. */ - if (smd->nonmanifold_offset_mode == MOD_SOLIDIFY_NONMANIFOLD_OFFSET_MODE_CONSTRAINTS) { + if (solidify_data->nonmanifold_offset_mode == + MOD_SOLIDIFY_NONMANIFOLD_OFFSET_MODE_CONSTRAINTS) { NewEdgeRef *first_edge = NULL; NewEdgeRef **edge_ptr = g->edges; /* Contains normal and offset [nx, ny, nz, ofs]. */ @@ -1466,7 +1447,7 @@ Mesh *MOD_solidify_nonmanifold_modifyMesh(ModifierData *md, } } uint face_nors_len = 0; - const float stop_explosion = 0.999f - fabsf(smd->offset_fac) * 0.05f; + const float stop_explosion = 0.999f - fabsf(solidify_data->offset_fac) * 0.05f; while (queue_index > 0) { if (face_nors_len == 0) { if (queue_index <= 2) { @@ -1616,7 +1597,7 @@ Mesh *MOD_solidify_nonmanifold_modifyMesh(ModifierData *md, ofs *= face_weight[face->index]; } - if (smd->nonmanifold_offset_mode == + if (solidify_data->nonmanifold_offset_mode == MOD_SOLIDIFY_NONMANIFOLD_OFFSET_MODE_EVEN) { MLoop *ml_next = orig_mloop + face->face->loopstart; ml = ml_next + (face->face->totloop - 1); @@ -1662,7 +1643,8 @@ Mesh *MOD_solidify_nonmanifold_modifyMesh(ModifierData *md, } /* Set normal length with selected method. */ - if (smd->nonmanifold_offset_mode == MOD_SOLIDIFY_NONMANIFOLD_OFFSET_MODE_EVEN) { + if (solidify_data->nonmanifold_offset_mode == + MOD_SOLIDIFY_NONMANIFOLD_OFFSET_MODE_EVEN) { if (has_front) { float length_sq = len_squared_v3(nor); if (LIKELY(length_sq > FLT_EPSILON)) { @@ -1751,7 +1733,8 @@ Mesh *MOD_solidify_nonmanifold_modifyMesh(ModifierData *md, sub_v3_v3v3(e1, orig_mvert_co[vm[e1_edge->v1] == i ? e1_edge->v2 : e1_edge->v1], orig_mvert_co[i]); - if (smd->nonmanifold_boundary_mode == MOD_SOLIDIFY_NONMANIFOLD_BOUNDARY_MODE_FLAT) { + if (solidify_data->nonmanifold_boundary_mode == + MOD_SOLIDIFY_NONMANIFOLD_BOUNDARY_MODE_FLAT) { cross_v3_v3v3(constr_nor, e0, e1); } else { @@ -1785,14 +1768,8 @@ Mesh *MOD_solidify_nonmanifold_modifyMesh(ModifierData *md, } float scalar_vgroup = 1; /* Use vertex group. */ - if (dvert && !do_flat_faces) { - MDeformVert *dv = &dvert[i]; - if (defgrp_invert) { - scalar_vgroup = 1.0f - BKE_defvert_find_weight(dv, defgrp_index); - } - else { - scalar_vgroup = BKE_defvert_find_weight(dv, defgrp_index); - } + if (!do_flat_faces) { + scalar_vgroup = solidify_data->distance[i]; scalar_vgroup = offset_fac_vg + (scalar_vgroup * offset_fac_vg_inv); } /* Do clamping. */ @@ -1925,16 +1902,32 @@ Mesh *MOD_solidify_nonmanifold_modifyMesh(ModifierData *md, result->cd_flag |= ME_CDFLAG_EDGE_BWEIGHT; } - /* Checks that result has dvert data. */ - if (shell_defgrp_index != -1 || rim_defgrp_index != -1) { - dvert = CustomData_duplicate_referenced_layer(&result->vdata, CD_MDEFORMVERT, result->totvert); - /* If no vertices were ever added to an object's vgroup, dvert might be NULL. */ - if (dvert == NULL) { - /* Add a valid data layer! */ - dvert = CustomData_add_layer( - &result->vdata, CD_MDEFORMVERT, CD_CALLOC, NULL, result->totvert); - } - result->dvert = dvert; + *r_shell_verts = MEM_malloc_arrayN( + (size_t)result->totvert, sizeof(bool), "shell verts selection in solidify"); + + for (int i = 0; i < result->totvert; i++) { + (*r_shell_verts)[i] = false; + } + + *r_rim_verts = MEM_malloc_arrayN( + (size_t)result->totvert, sizeof(bool), "rim verts selection in solidify"); + + for (int i = 0; i < result->totvert; i++) { + (*r_rim_verts)[i] = false; + } + + *r_shell_faces = MEM_malloc_arrayN( + (size_t)result->totpoly, sizeof(bool), "shell faces selection in solidify"); + + for (int i = 0; i < result->totpoly; i++) { + (*r_shell_faces)[i] = false; + } + + *r_rim_faces = MEM_malloc_arrayN( + (size_t)result->totpoly, sizeof(bool), "rim faces selection in solidify"); + + for (int i = 0; i < result->totpoly; i++) { + (*r_rim_faces)[i] = false; } /* Make_new_verts. */ @@ -2163,50 +2156,17 @@ Mesh *MOD_solidify_nonmanifold_modifyMesh(ModifierData *md, /* Loop data. */ int *loops = MEM_malloc_arrayN(j, sizeof(*loops), "loops in solidify"); - /* The #mat_nr is from consensus. */ - short most_mat_nr = 0; - uint most_mat_nr_face = 0; - uint most_mat_nr_count = 0; - for (short l = 0; l < mat_nrs; l++) { - uint count = 0; - uint face = 0; - uint k = 0; - for (EdgeGroup *g3 = g2; g3->valid && k < j; g3++) { - if ((do_rim && !g3->is_orig_closed) || (do_shell && g3->split)) { - /* Check both far ends in terms of faces of an edge group. */ - if (g3->edges[0]->faces[0]->face->mat_nr == l) { - face = g3->edges[0]->faces[0]->index; - count++; - } - NewEdgeRef *le = g3->edges[g3->edges_len - 1]; - if (le->faces[1] && le->faces[1]->face->mat_nr == l) { - face = le->faces[1]->index; - count++; - } - else if (!le->faces[1] && le->faces[0]->face->mat_nr == l) { - face = le->faces[0]->index; - count++; - } - k++; - } - } - if (count > most_mat_nr_count) { - most_mat_nr = l; - most_mat_nr_face = face; - most_mat_nr_count = count; - } - } - CustomData_copy_data( - &mesh->pdata, &result->pdata, (int)most_mat_nr_face, (int)poly_index, 1); + if (origindex_poly) { origindex_poly[poly_index] = ORIGINDEX_NONE; } mpoly[poly_index].loopstart = (int)loop_index; mpoly[poly_index].totloop = (int)j; - mpoly[poly_index].mat_nr = most_mat_nr + - (g->is_orig_closed || !do_rim ? 0 : mat_ofs_rim); - CLAMP(mpoly[poly_index].mat_nr, 0, mat_nr_max); - mpoly[poly_index].flag = orig_mpoly[most_mat_nr_face].flag; + + if (g->is_orig_closed || !do_rim) { + (*r_rim_faces)[poly_index] = true; + } + poly_index++; for (uint k = 0; g2->valid && k < j; g2++) { @@ -2278,8 +2238,8 @@ Mesh *MOD_solidify_nonmanifold_modifyMesh(ModifierData *md, &mesh->pdata, &result->pdata, (int)(*new_edges)->faces[0]->index, (int)poly_index, 1); mpoly[poly_index].loopstart = (int)loop_index; mpoly[poly_index].totloop = 4 - (int)(v1_singularity || v2_singularity); - mpoly[poly_index].mat_nr = face->mat_nr + mat_ofs_rim; - CLAMP(mpoly[poly_index].mat_nr, 0, mat_nr_max); + + (*r_rim_faces)[poly_index] = true; mpoly[poly_index].flag = face->flag; poly_index++; @@ -2300,20 +2260,17 @@ Mesh *MOD_solidify_nonmanifold_modifyMesh(ModifierData *md, MEdge *open_face_edge; uint open_face_edge_index; if (!do_flip) { - if (rim_defgrp_index != -1) { - BKE_defvert_ensure_index(&result->dvert[medge[edge1->new_edge].v1], rim_defgrp_index) - ->weight = 1.0f; - } + (*r_rim_verts)[medge[edge1->new_edge].v1] = true; + CustomData_copy_data(&mesh->ldata, &result->ldata, loop1, (int)loop_index, 1); mloop[loop_index].v = medge[edge1->new_edge].v1; mloop[loop_index++].e = edge1->new_edge; if (!v2_singularity) { open_face_edge_index = edge1->link_edge_groups[1]->open_face_edge; - if (rim_defgrp_index != -1) { - BKE_defvert_ensure_index(&result->dvert[medge[edge1->new_edge].v2], rim_defgrp_index) - ->weight = 1.0f; - } + + (*r_rim_verts)[medge[edge1->new_edge].v2] = true; + CustomData_copy_data(&mesh->ldata, &result->ldata, loop2, (int)loop_index, 1); mloop[loop_index].v = medge[edge1->new_edge].v2; open_face_edge = medge + open_face_edge_index; @@ -2324,21 +2281,17 @@ Mesh *MOD_solidify_nonmanifold_modifyMesh(ModifierData *md, mloop[loop_index++].e = edge2->link_edge_groups[1]->open_face_edge; } } + (*r_rim_verts)[medge[edge2->new_edge].v2] = true; - if (rim_defgrp_index != -1) { - BKE_defvert_ensure_index(&result->dvert[medge[edge2->new_edge].v2], rim_defgrp_index) - ->weight = 1.0f; - } CustomData_copy_data(&mesh->ldata, &result->ldata, loop2, (int)loop_index, 1); mloop[loop_index].v = medge[edge2->new_edge].v2; mloop[loop_index++].e = edge2->new_edge; if (!v1_singularity) { open_face_edge_index = edge2->link_edge_groups[0]->open_face_edge; - if (rim_defgrp_index != -1) { - BKE_defvert_ensure_index(&result->dvert[medge[edge2->new_edge].v1], rim_defgrp_index) - ->weight = 1.0f; - } + + (*r_rim_verts)[medge[edge2->new_edge].v1] = true; + CustomData_copy_data(&mesh->ldata, &result->ldata, loop1, (int)loop_index, 1); mloop[loop_index].v = medge[edge2->new_edge].v1; open_face_edge = medge + open_face_edge_index; @@ -2353,10 +2306,9 @@ Mesh *MOD_solidify_nonmanifold_modifyMesh(ModifierData *md, else { if (!v1_singularity) { open_face_edge_index = edge1->link_edge_groups[0]->open_face_edge; - if (rim_defgrp_index != -1) { - BKE_defvert_ensure_index(&result->dvert[medge[edge1->new_edge].v1], rim_defgrp_index) - ->weight = 1.0f; - } + + (*r_rim_verts)[medge[edge1->new_edge].v1] = true; + CustomData_copy_data(&mesh->ldata, &result->ldata, loop1, (int)loop_index, 1); mloop[loop_index].v = medge[edge1->new_edge].v1; open_face_edge = medge + open_face_edge_index; @@ -2368,20 +2320,17 @@ Mesh *MOD_solidify_nonmanifold_modifyMesh(ModifierData *md, } } - if (rim_defgrp_index != -1) { - BKE_defvert_ensure_index(&result->dvert[medge[edge2->new_edge].v1], rim_defgrp_index) - ->weight = 1.0f; - } + (*r_rim_verts)[medge[edge2->new_edge].v1] = true; + CustomData_copy_data(&mesh->ldata, &result->ldata, loop1, (int)loop_index, 1); mloop[loop_index].v = medge[edge2->new_edge].v1; mloop[loop_index++].e = edge2->new_edge; if (!v2_singularity) { open_face_edge_index = edge2->link_edge_groups[1]->open_face_edge; - if (rim_defgrp_index != -1) { - BKE_defvert_ensure_index(&result->dvert[medge[edge2->new_edge].v2], rim_defgrp_index) - ->weight = 1.0f; - } + + (*r_rim_verts)[medge[edge2->new_edge].v2] = true; + CustomData_copy_data(&mesh->ldata, &result->ldata, loop2, (int)loop_index, 1); mloop[loop_index].v = medge[edge2->new_edge].v2; open_face_edge = medge + open_face_edge_index; @@ -2393,10 +2342,8 @@ Mesh *MOD_solidify_nonmanifold_modifyMesh(ModifierData *md, } } - if (rim_defgrp_index != -1) { - BKE_defvert_ensure_index(&result->dvert[medge[edge1->new_edge].v2], rim_defgrp_index) - ->weight = 1.0f; - } + (*r_rim_verts)[medge[edge1->new_edge].v2] = true; + CustomData_copy_data(&mesh->ldata, &result->ldata, loop2, (int)loop_index, 1); mloop[loop_index].v = medge[edge1->new_edge].v2; mloop[loop_index++].e = edge1->new_edge; @@ -2410,10 +2357,13 @@ Mesh *MOD_solidify_nonmanifold_modifyMesh(ModifierData *md, NewFaceRef *fr = face_sides_arr; uint *face_loops = MEM_malloc_arrayN( largest_ngon * 2, sizeof(*face_loops), "face_loops in solidify"); + uint *face_verts = MEM_malloc_arrayN( largest_ngon * 2, sizeof(*face_verts), "face_verts in solidify"); + uint *face_edges = MEM_malloc_arrayN( largest_ngon * 2, sizeof(*face_edges), "face_edges in solidify"); + for (uint i = 0; i < numPolys * 2; i++, fr++) { const uint loopstart = (uint)fr->face->loopstart; uint totloop = (uint)fr->face->totloop; @@ -2469,15 +2419,15 @@ Mesh *MOD_solidify_nonmanifold_modifyMesh(ModifierData *md, CustomData_copy_data(&mesh->pdata, &result->pdata, (int)(i / 2), (int)poly_index, 1); mpoly[poly_index].loopstart = (int)loop_index; mpoly[poly_index].totloop = (int)k; - mpoly[poly_index].mat_nr = fr->face->mat_nr + (fr->reversed != do_flip ? mat_ofs : 0); - CLAMP(mpoly[poly_index].mat_nr, 0, mat_nr_max); + + if (fr->reversed != do_flip) { + (*r_shell_faces)[poly_index] = true; + } mpoly[poly_index].flag = fr->face->flag; if (fr->reversed != do_flip) { for (int l = (int)k - 1; l >= 0; l--) { - if (shell_defgrp_index != -1) { - BKE_defvert_ensure_index(&result->dvert[face_verts[l]], shell_defgrp_index) - ->weight = 1.0f; - } + (*r_shell_verts)[face_verts[l]] = true; + CustomData_copy_data( &mesh->ldata, &result->ldata, (int)face_loops[l], (int)loop_index, 1); mloop[loop_index].v = face_verts[l]; @@ -2502,27 +2452,28 @@ Mesh *MOD_solidify_nonmanifold_modifyMesh(ModifierData *md, MEM_freeN(face_verts); MEM_freeN(face_edges); } - if (edge_index != numNewEdges) { - BKE_modifier_set_error(ctx->object, - md, - "Internal Error: edges array wrong size: %u instead of %u", - numNewEdges, - edge_index); - } - if (poly_index != numNewPolys) { - BKE_modifier_set_error(ctx->object, - md, - "Internal Error: polys array wrong size: %u instead of %u", - numNewPolys, - poly_index); - } - if (loop_index != numNewLoops) { - BKE_modifier_set_error(ctx->object, - md, - "Internal Error: loops array wrong size: %u instead of %u", - numNewLoops, - loop_index); - } + /* Haven't found a good way to generalize this. */ + // if (edge_index != numNewEdges) { + /*BKE_modifier_set_error(ctx->object, + md,A + "Internal Error: edges array wrong size: %u instead of %u", + numNewEdges, + edge_index);*/ + //} + // if (poly_index != numNewPolys) { + /*BKE_modifier_set_error(ctx->object, + md, + "Internal Error: polys array wrong size: %u instead of %u", + numNewPolys,f + poly_index);*/ + //} + // if (loop_index != numNewLoops) { + /*BKE_modifier_set_error(ctx->object, + md, + "Internal Error: loops array wrong size: %u instead of %u", + numNewLoops, + loop_index);*/ + //} BLI_assert(edge_index == numNewEdges); BLI_assert(poly_index == numNewPolys); BLI_assert(loop_index == numNewLoops); diff --git a/source/blender/makesdna/DNA_defaults.h b/source/blender/makesdna/DNA_defaults.h index 1549e33b267..fa466718eab 100644 --- a/source/blender/makesdna/DNA_defaults.h +++ b/source/blender/makesdna/DNA_defaults.h @@ -27,7 +27,6 @@ #pragma once #include "BLI_utildefines.h" - #include "dna_type_offsets.h" #ifdef __cplusplus diff --git a/source/blender/makesdna/DNA_mesh_types.h b/source/blender/makesdna/DNA_mesh_types.h index 97f14b2195d..30af08db035 100644 --- a/source/blender/makesdna/DNA_mesh_types.h +++ b/source/blender/makesdna/DNA_mesh_types.h @@ -282,9 +282,9 @@ enum { /* We can't have both flags enabled at once, * flags defined in DNA_scene_types.h */ #define ME_EDIT_PAINT_SEL_MODE(_me) \ - (((_me)->editflag & ME_EDIT_PAINT_FACE_SEL) ? SCE_SELECT_FACE : \ - ((_me)->editflag & ME_EDIT_PAINT_VERT_SEL) ? SCE_SELECT_VERTEX : \ - 0) + (((_me)->editflag & ME_EDIT_PAINT_FACE_SEL) ? \ + SCE_SELECT_FACE : \ + ((_me)->editflag & ME_EDIT_PAINT_VERT_SEL) ? SCE_SELECT_VERTEX : 0) /* me->flag */ enum { diff --git a/source/blender/makesdna/DNA_modifier_defaults.h b/source/blender/makesdna/DNA_modifier_defaults.h index 1b3dbd148df..eb3066c11ce 100644 --- a/source/blender/makesdna/DNA_modifier_defaults.h +++ b/source/blender/makesdna/DNA_modifier_defaults.h @@ -805,7 +805,7 @@ #define _DNA_DEFAULT_WeldModifierData \ { \ .merge_dist = 0.001f, \ - .mode = MOD_WELD_MODE_ALL, \ + .mode = 0, \ .defgrp_name = "", \ } diff --git a/source/blender/makesdna/DNA_modifier_types.h b/source/blender/makesdna/DNA_modifier_types.h index 1bebbc35747..ce4280c5e49 100644 --- a/source/blender/makesdna/DNA_modifier_types.h +++ b/source/blender/makesdna/DNA_modifier_types.h @@ -1287,9 +1287,11 @@ enum { MOD_SOLIDIFY_RIM_MATERIAL = (1 << 4), /* deprecated, used in do_versions */ #endif MOD_SOLIDIFY_FLIP = (1 << 5), - MOD_SOLIDIFY_NOSHELL = (1 << 6), - MOD_SOLIDIFY_OFFSET_ANGLE_CLAMP = (1 << 7), - MOD_SOLIDIFY_NONMANIFOLD_FLAT_FACES = (1 << 8), + MOD_SOLIDIFY_SHELL = (1 << 6), + MOD_SOLIDIFY_NOSHELL = (1 << 7), /* MOD_SOLIDIFY_SHELL should be used. instead This is kept as + long as the Modifier uses it. */ + MOD_SOLIDIFY_OFFSET_ANGLE_CLAMP = (1 << 8), + MOD_SOLIDIFY_NONMANIFOLD_FLAT_FACES = (1 << 9), }; /** #SolidifyModifierData.mode */ @@ -2044,12 +2046,6 @@ enum { MOD_WELD_LOOSE_EDGES = (1 << 1), }; -/* #WeldModifierData.mode */ -enum { - MOD_WELD_MODE_ALL = 0, - MOD_WELD_MODE_CONNECTED = 1, -}; - typedef struct DataTransferModifierData { ModifierData modifier; diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index fd794ed1b21..0a2e211c210 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -1235,6 +1235,14 @@ typedef struct NodeInputVector { float vector[3]; } NodeInputVector; +typedef struct NodeGeometrySolidify { + /* GeometryNodeAttributeInputMode */ + uint8_t thickness_mode; + uint8_t nonmanifold_offset_mode; + uint8_t nonmanifold_boundary_mode; + char _pad[5]; +} NodeGeometrySolidify; + typedef struct NodeInputString { char *string; } NodeInputString; @@ -1437,6 +1445,16 @@ typedef struct NodeGeometryRaycast { char _pad[1]; } NodeGeometryRaycast; +typedef struct NodeGeometryCollapse { + /* GeometryNodeCollapseSymmetryAxis. */ + int8_t symmetry_axis; +} NodeGeometryCollapse; + +typedef struct NodeGeometryDissolve { + /* GeometryNodeDissolveDelimiter */ + int8_t selection_type; +} NodeGeometryDissolve; + /* script node mode */ #define NODE_SCRIPT_INTERNAL 0 #define NODE_SCRIPT_EXTERNAL 1 @@ -2008,6 +2026,19 @@ typedef enum GeometryNodeRaycastMapMode { GEO_NODE_RAYCAST_NEAREST = 1, } GeometryNodeRaycastMapMode; +typedef enum GeometryNodeCollapseSymmetryAxis { + GEO_NODE_COLLAPSE_SYMMETRY_AXIS_NONE = -1, + GEO_NODE_COLLAPSE_SYMMETRY_AXIS_X = 0, + GEO_NODE_COLLAPSE_SYMMETRY_AXIS_Y = 1, + GEO_NODE_COLLAPSE_SYMMETRY_AXIS_Z = 2, +} GeometryNodeCollapseSymmetryAxis; + +typedef enum GeometryNodeDissolveDelimiter { + GEO_NODE_DISSOLVE_DELIMITTER_UNSELECTED = 0, + GEO_NODE_DISSOLVE_DELIMITTER_LIMIT = 1, + GEO_NODE_DISSOLVE_DELIMITTER_SELECTION_BORDER = 2, +} GeometryNodeDissolveDelimiter; + #ifdef __cplusplus } #endif diff --git a/source/blender/makesrna/RNA_enum_types.h b/source/blender/makesrna/RNA_enum_types.h index d7520834287..c7e7dc84775 100644 --- a/source/blender/makesrna/RNA_enum_types.h +++ b/source/blender/makesrna/RNA_enum_types.h @@ -35,6 +35,71 @@ struct bNodeType; #define DEF_ENUM(id) extern const EnumPropertyItem id[]; #include "RNA_enum_items.h" +extern const EnumPropertyItem rna_enum_operator_context_items[]; + +extern const EnumPropertyItem rna_enum_wm_report_items[]; + +extern const EnumPropertyItem rna_enum_transform_pivot_items_full[]; +extern const EnumPropertyItem rna_enum_transform_orientation_items[]; +extern const EnumPropertyItem rna_enum_transform_mode_types[]; + +extern const EnumPropertyItem rna_enum_property_type_items[]; +extern const EnumPropertyItem rna_enum_property_subtype_items[]; +extern const EnumPropertyItem rna_enum_property_unit_items[]; + +extern const EnumPropertyItem rna_enum_shading_type_items[]; + +extern const EnumPropertyItem rna_enum_navigation_mode_items[]; + +extern const EnumPropertyItem rna_enum_node_socket_in_out_items[]; + +extern const EnumPropertyItem rna_enum_node_math_items[]; +extern const EnumPropertyItem rna_enum_mapping_type_items[]; +extern const EnumPropertyItem rna_enum_node_vec_math_items[]; +extern const EnumPropertyItem rna_enum_node_boolean_math_items[]; +extern const EnumPropertyItem rna_enum_node_float_compare_items[]; +extern const EnumPropertyItem rna_enum_node_filter_items[]; +extern const EnumPropertyItem rna_enum_node_float_to_int_items[]; +extern const EnumPropertyItem rna_enum_node_map_range_items[]; +extern const EnumPropertyItem rna_enum_node_clamp_items[]; +extern const EnumPropertyItem rna_enum_weld_mode_items[]; + +extern const EnumPropertyItem rna_enum_ramp_blend_items[]; + +extern const EnumPropertyItem rna_enum_prop_dynamicpaint_type_items[]; + +extern const EnumPropertyItem rna_enum_clip_editor_mode_items[]; + +extern const EnumPropertyItem rna_enum_icon_items[]; +extern const EnumPropertyItem rna_enum_uilist_layout_type_items[]; + +extern const EnumPropertyItem rna_enum_linestyle_color_modifier_type_items[]; +extern const EnumPropertyItem rna_enum_linestyle_alpha_modifier_type_items[]; +extern const EnumPropertyItem rna_enum_linestyle_thickness_modifier_type_items[]; +extern const EnumPropertyItem rna_enum_linestyle_geometry_modifier_type_items[]; + +extern const EnumPropertyItem nonmanifold_thickness_mode_items[]; +extern const EnumPropertyItem nonmanifold_boundary_mode_items[]; + +extern const EnumPropertyItem rna_enum_window_cursor_items[]; + +extern const EnumPropertyItem rna_enum_dt_method_vertex_items[]; +extern const EnumPropertyItem rna_enum_dt_method_edge_items[]; +extern const EnumPropertyItem rna_enum_dt_method_loop_items[]; +extern const EnumPropertyItem rna_enum_dt_method_poly_items[]; +extern const EnumPropertyItem rna_enum_dt_mix_mode_items[]; +extern const EnumPropertyItem rna_enum_dt_layers_select_src_items[]; +extern const EnumPropertyItem rna_enum_dt_layers_select_dst_items[]; + +extern const EnumPropertyItem rna_enum_context_mode_items[]; + +extern const EnumPropertyItem rna_enum_curveprofile_preset_items[]; +extern const EnumPropertyItem rna_enum_preference_section_items[]; + +extern const EnumPropertyItem rna_enum_attribute_type_items[]; +extern const EnumPropertyItem rna_enum_attribute_type_with_auto_items[]; +extern const EnumPropertyItem rna_enum_attribute_domain_items[]; +extern const EnumPropertyItem rna_enum_attribute_domain_with_auto_items[]; extern const EnumPropertyItem *rna_enum_attribute_domain_itemf(struct ID *id, bool *r_free); /** diff --git a/source/blender/makesrna/intern/CMakeLists.txt b/source/blender/makesrna/intern/CMakeLists.txt index 7e6d0aea2ee..4464262ceae 100644 --- a/source/blender/makesrna/intern/CMakeLists.txt +++ b/source/blender/makesrna/intern/CMakeLists.txt @@ -382,6 +382,7 @@ blender_include_dirs( ../../bmesh ../../depsgraph ../../draw + ../../geometry ../../gpu ../../ikplugin ../../imbuf diff --git a/source/blender/makesrna/intern/rna_modifier.c b/source/blender/makesrna/intern/rna_modifier.c index 486d8d13564..cf2044e7145 100644 --- a/source/blender/makesrna/intern/rna_modifier.c +++ b/source/blender/makesrna/intern/rna_modifier.c @@ -4559,6 +4559,36 @@ static void rna_def_modifier_surface(BlenderRNA *brna) RNA_def_struct_ui_icon(srna, ICON_MOD_PHYSICS); } +const EnumPropertyItem nonmanifold_thickness_mode_items[] = { + {MOD_SOLIDIFY_NONMANIFOLD_OFFSET_MODE_FIXED, + "FIXED", + 0, + "Fixed", + "Most basic thickness calculation"}, + {MOD_SOLIDIFY_NONMANIFOLD_OFFSET_MODE_EVEN, + "EVEN", + 0, + "Even", + "Even thickness calculation which takes the angle between faces into account"}, + {MOD_SOLIDIFY_NONMANIFOLD_OFFSET_MODE_CONSTRAINTS, + "CONSTRAINTS", + 0, + "Constraints", + "Thickness calculation using constraints, most advanced"}, + {0, NULL, 0, NULL, NULL}, +}; + +const EnumPropertyItem nonmanifold_boundary_mode_items[] = { + {MOD_SOLIDIFY_NONMANIFOLD_BOUNDARY_MODE_NONE, "NONE", 0, "None", "No shape correction"}, + {MOD_SOLIDIFY_NONMANIFOLD_BOUNDARY_MODE_ROUND, + "ROUND", + 0, + "Round", + "Round open perimeter shape"}, + {MOD_SOLIDIFY_NONMANIFOLD_BOUNDARY_MODE_FLAT, "FLAT", 0, "Flat", "Flat open perimeter shape"}, + {0, NULL, 0, NULL, NULL}, +}; + static void rna_def_modifier_solidify(BlenderRNA *brna) { static const EnumPropertyItem mode_items[] = { @@ -4577,40 +4607,6 @@ static void rna_def_modifier_solidify(BlenderRNA *brna) {0, NULL, 0, NULL, NULL}, }; - static const EnumPropertyItem nonmanifold_thickness_mode_items[] = { - {MOD_SOLIDIFY_NONMANIFOLD_OFFSET_MODE_FIXED, - "FIXED", - 0, - "Fixed", - "Most basic thickness calculation"}, - {MOD_SOLIDIFY_NONMANIFOLD_OFFSET_MODE_EVEN, - "EVEN", - 0, - "Even", - "Even thickness calculation which takes the angle between faces into account"}, - {MOD_SOLIDIFY_NONMANIFOLD_OFFSET_MODE_CONSTRAINTS, - "CONSTRAINTS", - 0, - "Constraints", - "Thickness calculation using constraints, most advanced"}, - {0, NULL, 0, NULL, NULL}, - }; - - static const EnumPropertyItem nonmanifold_boundary_mode_items[] = { - {MOD_SOLIDIFY_NONMANIFOLD_BOUNDARY_MODE_NONE, "NONE", 0, "None", "No shape correction"}, - {MOD_SOLIDIFY_NONMANIFOLD_BOUNDARY_MODE_ROUND, - "ROUND", - 0, - "Round", - "Round open perimeter shape"}, - {MOD_SOLIDIFY_NONMANIFOLD_BOUNDARY_MODE_FLAT, - "FLAT", - 0, - "Flat", - "Flat open perimeter shape"}, - {0, NULL, 0, NULL, NULL}, - }; - StructRNA *srna; PropertyRNA *prop; @@ -6232,17 +6228,17 @@ static void rna_def_modifier_laplaciandeform(BlenderRNA *brna) RNA_def_property_update(prop, 0, "rna_Modifier_update"); } +const EnumPropertyItem rna_enum_weld_mode_items[] = { + {0, "ALL", 0, "All", "Full merge by distance"}, + {1, "CONNECTED", 0, "Connected", "Only merge along the edges"}, + {0, NULL, 0, NULL, NULL}, +}; + static void rna_def_modifier_weld(BlenderRNA *brna) { StructRNA *srna; PropertyRNA *prop; - static const EnumPropertyItem mode_items[] = { - {MOD_WELD_MODE_ALL, "ALL", 0, "All", "Full merge by distance"}, - {MOD_WELD_MODE_CONNECTED, "CONNECTED", 0, "Connected", "Only merge along the edges"}, - {0, NULL, 0, NULL, NULL}, - }; - srna = RNA_def_struct(brna, "WeldModifier", "Modifier"); RNA_def_struct_ui_text(srna, "Weld Modifier", "Weld modifier"); RNA_def_struct_sdna(srna, "WeldModifierData"); @@ -6251,7 +6247,7 @@ static void rna_def_modifier_weld(BlenderRNA *brna) RNA_define_lib_overridable(true); prop = RNA_def_property(srna, "mode", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_items(prop, mode_items); + RNA_def_property_enum_items(prop, rna_enum_weld_mode_items); RNA_def_property_ui_text(prop, "Mode", "Mode defines the merge rule"); RNA_def_property_update(prop, 0, "rna_Modifier_update"); diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index d8ab7c7a61b..9eaa1ce1d14 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -9109,6 +9109,26 @@ static void def_geo_triangulate(StructRNA *srna) RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); } +static void def_geo_remesh_blocks(StructRNA *srna) +{ + static const EnumPropertyItem remesh_mode_items[] = { + {0, "BLOCKS", 0, "Blocks", "Output a blocky surface with no smoothing"}, + {1, "SMOOTH", 0, "Smooth", "Output a smooth surface with no sharp-features detection"}, + {2, + "SHARP", + 0, + "Sharp", + "Output a surface that reproduces sharp edges and corners from the input mesh"}, + {0, NULL, 0, NULL, NULL}, + }; + + PropertyRNA *prop = RNA_def_property(srna, "remesh_blocks_mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "custom1"); + RNA_def_property_enum_items(prop, remesh_mode_items); + RNA_def_property_ui_text(prop, "Mode", "Mesh smoothing mode for remesh operation"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); +} + static void def_geo_subdivision_surface(StructRNA *srna) { PropertyRNA *prop; @@ -9750,6 +9770,29 @@ static void def_geo_point_scale(StructRNA *srna) RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); } +static void def_geo_solidify(StructRNA *srna) +{ + PropertyRNA *prop; + + RNA_def_struct_sdna_from(srna, "NodeGeometrySolidify", "storage"); + + prop = RNA_def_property(srna, "thickness_mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_type_items_float); + RNA_def_property_ui_text( + prop, "Thickness", "Changes the Thickness input between Float and Attribute"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); + + prop = RNA_def_property(srna, "nonmanifold_offset_mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, nonmanifold_thickness_mode_items); + RNA_def_property_ui_text(prop, "Mode", "Selects the used thickness algorithm"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); + + prop = RNA_def_property(srna, "nonmanifold_boundary_mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, nonmanifold_boundary_mode_items); + RNA_def_property_ui_text(prop, "Boundary", "Selects the boundary adjustment algorithm"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); +} + static void def_geo_point_translate(StructRNA *srna) { PropertyRNA *prop; @@ -10108,6 +10151,58 @@ static void def_geo_curve_resample(StructRNA *srna) RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); } +static void def_geo_collapse(StructRNA *srna) +{ + PropertyRNA *prop; + + static EnumPropertyItem symmetry_axis_items[] = { + {GEO_NODE_COLLAPSE_SYMMETRY_AXIS_NONE, "NONE", 0, "None", "No Symmetry is applied"}, + {GEO_NODE_COLLAPSE_SYMMETRY_AXIS_X, "X", 0, "X", "Symmetry is applied on X axis"}, + {GEO_NODE_COLLAPSE_SYMMETRY_AXIS_Y, "Y", 0, "Y", "Symmetry is applied on Y axis"}, + {GEO_NODE_COLLAPSE_SYMMETRY_AXIS_Z, "Z", 0, "Z", "Symmetry is applied on Z axis"}, + {0, NULL, 0, NULL, NULL}, + }; + + RNA_def_struct_sdna_from(srna, "NodeGeometryCollapse", "storage"); + + prop = RNA_def_property(srna, "symmetry_axis", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, symmetry_axis_items); + RNA_def_property_ui_text( + prop, "Symmetry", "Set if and on what axis symmetry is applied by the operation"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); +} + +static void def_geo_dissolve(StructRNA *srna) +{ + PropertyRNA *prop; + + static EnumPropertyItem delimiter_items[] = { + {GEO_NODE_DISSOLVE_DELIMITTER_UNSELECTED, + "SELECTION", + 0, + "Selection", + "Only dissolve selected"}, + {GEO_NODE_DISSOLVE_DELIMITTER_LIMIT, + "LIMIT", + 0, + "Limit", + "Only dissolve unselected. Use especially for attributes on edge domain e.g. crease"}, + {GEO_NODE_DISSOLVE_DELIMITTER_SELECTION_BORDER, + "BORDER", + 0, + "Border as Limit", + "Use border of selection as delimiter"}, + {0, NULL, 0, NULL, NULL}, + }; + + RNA_def_struct_sdna_from(srna, "NodeGeometryDissolve", "storage"); + + prop = RNA_def_property(srna, "selection_type", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, delimiter_items); + RNA_def_property_ui_text(prop, "Selection", "Define how selection is applied"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); +} + static void def_geo_curve_subdivide(StructRNA *srna) { PropertyRNA *prop; @@ -10212,6 +10307,43 @@ static void def_geo_attribute_transfer(StructRNA *srna) RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); } +static void def_geo_mesh_extrude(StructRNA *srna) +{ + PropertyRNA *prop; + + static const EnumPropertyItem rna_node_geometry_extrude_domain_items[] = { + {0, "VERTEX", 0, "Vertex", "Extrude Vertices"}, + {1, "EDGE", 0, "Edge", "Extrude Edges"}, + {2, "FACE", 0, "Face", "Extrude Faces"}, + {0, NULL, 0, NULL, NULL}, + }; + + prop = RNA_def_property(srna, "extrude_mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "custom1"); + RNA_def_property_enum_items(prop, rna_node_geometry_extrude_domain_items); + RNA_def_property_enum_default(prop, GEO_NODE_POINT_DISTRIBUTE_RANDOM); + RNA_def_property_ui_text(prop, "Extrude Mode", "Select mesh domain to extrude"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); +} + +static void def_geo_mesh_inset(StructRNA *srna) +{ + PropertyRNA *prop; + + prop = RNA_def_property(srna, "distance_mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "custom1"); + RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_type_items_float); + RNA_def_property_ui_text( + prop, "Distance", "Changes the Distance input between Float and Attribute"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); + + prop = RNA_def_property(srna, "inset_mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "custom2"); + RNA_def_property_enum_items(prop, rna_node_geometry_attribute_input_type_items_float); + RNA_def_property_ui_text(prop, "Inset", "Changes the Inset input between Float and Attribute"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); +} + static void def_geo_input_material(StructRNA *srna) { PropertyRNA *prop; @@ -10261,6 +10393,16 @@ static void def_geo_raycast(StructRNA *srna) RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); } +static void def_geo_merge_by_distance(StructRNA *srna) +{ + PropertyRNA *prop; + + prop = RNA_def_property(srna, "merge_mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "custom1"); + RNA_def_property_enum_items(prop, rna_enum_weld_mode_items); + RNA_def_property_ui_text(prop, "Mode", "Mode defines the merge rule"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); +} /* -------------------------------------------------------------------------- */ static void rna_def_shader_node(BlenderRNA *brna) diff --git a/source/blender/modifiers/CMakeLists.txt b/source/blender/modifiers/CMakeLists.txt index d9b9fa96d04..e38152e9395 100644 --- a/source/blender/modifiers/CMakeLists.txt +++ b/source/blender/modifiers/CMakeLists.txt @@ -30,6 +30,7 @@ set(INC ../depsgraph ../editors/include ../functions + ../geometry ../makesdna ../makesrna ../nodes @@ -95,8 +96,7 @@ set(SRC intern/MOD_softbody.c intern/MOD_solidify.c intern/MOD_solidify_extrude.c - intern/MOD_solidify_nonmanifold.c - intern/MOD_subsurf.c + intern/MOD_subsurf.c intern/MOD_surface.c intern/MOD_surfacedeform.c intern/MOD_triangulate.c @@ -129,6 +129,7 @@ set(SRC set(LIB bf_blenkernel bf_blenlib + bf_geometry ) if(WITH_ALEMBIC) diff --git a/source/blender/modifiers/intern/MOD_remesh.c b/source/blender/modifiers/intern/MOD_remesh.c index df3db894f4e..cfd589f6914 100644 --- a/source/blender/modifiers/intern/MOD_remesh.c +++ b/source/blender/modifiers/intern/MOD_remesh.c @@ -20,15 +20,8 @@ * \ingroup modifiers */ -#include "MEM_guardedalloc.h" - #include "BLI_utildefines.h" -#include "BLI_math_base.h" -#include "BLI_threads.h" - -#include "BLT_translation.h" - #include "DNA_defaults.h" #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" @@ -39,9 +32,10 @@ #include "BKE_context.h" #include "BKE_mesh.h" #include "BKE_mesh_remesh_voxel.h" -#include "BKE_mesh_runtime.h" #include "BKE_screen.h" +#include "GEO_mesh_remesh_blocks.h" + #include "UI_interface.h" #include "UI_resources.h" @@ -50,15 +44,8 @@ #include "MOD_modifiertypes.h" #include "MOD_ui_common.h" -#include <stdlib.h> #include <string.h> -#ifdef WITH_MOD_REMESH -# include "BLI_math_vector.h" - -# include "dualcon.h" -#endif - static void initData(ModifierData *md) { RemeshModifierData *rmd = (RemeshModifierData *)md; @@ -68,89 +55,10 @@ static void initData(ModifierData *md) MEMCPY_STRUCT_AFTER(rmd, DNA_struct_default_get(RemeshModifierData), modifier); } -#ifdef WITH_MOD_REMESH - -static void init_dualcon_mesh(DualConInput *input, Mesh *mesh) -{ - memset(input, 0, sizeof(DualConInput)); - - input->co = (void *)mesh->mvert; - input->co_stride = sizeof(MVert); - input->totco = mesh->totvert; - - input->mloop = (void *)mesh->mloop; - input->loop_stride = sizeof(MLoop); - - BKE_mesh_runtime_looptri_ensure(mesh); - input->looptri = (void *)mesh->runtime.looptris.array; - input->tri_stride = sizeof(MLoopTri); - input->tottri = mesh->runtime.looptris.len; - - INIT_MINMAX(input->min, input->max); - BKE_mesh_minmax(mesh, input->min, input->max); -} - -/* simple structure to hold the output: a CDDM and two counters to - * keep track of the current elements */ -typedef struct { - Mesh *mesh; - int curvert, curface; -} DualConOutput; - -/* allocate and initialize a DualConOutput */ -static void *dualcon_alloc_output(int totvert, int totquad) -{ - DualConOutput *output; - - if (!(output = MEM_callocN(sizeof(DualConOutput), "DualConOutput"))) { - return NULL; - } - - output->mesh = BKE_mesh_new_nomain(totvert, 0, 0, 4 * totquad, totquad); - return output; -} - -static void dualcon_add_vert(void *output_v, const float co[3]) -{ - DualConOutput *output = output_v; - Mesh *mesh = output->mesh; - - BLI_assert(output->curvert < mesh->totvert); - - copy_v3_v3(mesh->mvert[output->curvert].co, co); - output->curvert++; -} - -static void dualcon_add_quad(void *output_v, const int vert_indices[4]) -{ - DualConOutput *output = output_v; - Mesh *mesh = output->mesh; - MLoop *mloop; - MPoly *cur_poly; - int i; - - BLI_assert(output->curface < mesh->totpoly); - - mloop = mesh->mloop; - cur_poly = &mesh->mpoly[output->curface]; - - cur_poly->loopstart = output->curface * 4; - cur_poly->totloop = 4; - for (i = 0; i < 4; i++) { - mloop[output->curface * 4 + i].v = vert_indices[i]; - } - - output->curface++; -} - static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *UNUSED(ctx), Mesh *mesh) { RemeshModifierData *rmd; - DualConOutput *output; - DualConInput input; Mesh *result; - DualConFlags flags = 0; - DualConMode mode = 0; rmd = (RemeshModifierData *)md; @@ -165,47 +73,8 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *UNUSED(ctx) } } else { - /* Dualcon modes. */ - init_dualcon_mesh(&input, mesh); - - if (rmd->flag & MOD_REMESH_FLOOD_FILL) { - flags |= DUALCON_FLOOD_FILL; - } - - switch (rmd->mode) { - case MOD_REMESH_CENTROID: - mode = DUALCON_CENTROID; - break; - case MOD_REMESH_MASS_POINT: - mode = DUALCON_MASS_POINT; - break; - case MOD_REMESH_SHARP_FEATURES: - mode = DUALCON_SHARP_FEATURES; - break; - case MOD_REMESH_VOXEL: - /* Should have been processed before as an OpenVDB operation. */ - BLI_assert(false); - break; - } - /* TODO(jbakker): Dualcon crashes when run in parallel. Could be related to incorrect - * input data or that the library isn't thread safe. - * This was identified when changing the task isolation's during T76553. */ - static ThreadMutex dualcon_mutex = BLI_MUTEX_INITIALIZER; - BLI_mutex_lock(&dualcon_mutex); - output = dualcon(&input, - dualcon_alloc_output, - dualcon_add_vert, - dualcon_add_quad, - flags, - mode, - rmd->threshold, - rmd->hermite_num, - rmd->scale, - rmd->depth); - BLI_mutex_unlock(&dualcon_mutex); - - result = output->mesh; - MEM_freeN(output); + result = GEO_mesh_remesh_blocks( + mesh, rmd->flag, rmd->mode, rmd->threshold, rmd->hermite_num, rmd->scale, rmd->depth); } if (rmd->flag & MOD_REMESH_SMOOTH_SHADING) { @@ -221,24 +90,13 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *UNUSED(ctx) BKE_mesh_copy_parameters_for_eval(result, mesh); BKE_mesh_calc_edges(result, true, false); result->runtime.cd_dirty_vert |= CD_MASK_NORMAL; - return result; -} -#else /* !WITH_MOD_REMESH */ - -static Mesh *modifyMesh(ModifierData *UNUSED(md), - const ModifierEvalContext *UNUSED(ctx), - Mesh *mesh) -{ - return mesh; + return result; } -#endif /* !WITH_MOD_REMESH */ - static void panel_draw(const bContext *UNUSED(C), Panel *panel) { uiLayout *layout = panel->layout; -#ifdef WITH_MOD_REMESH uiLayout *row, *col; PointerRNA ob_ptr; @@ -271,10 +129,6 @@ static void panel_draw(const bContext *UNUSED(C), Panel *panel) uiItemR(layout, ptr, "use_smooth_shade", 0, NULL, ICON_NONE); modifier_panel_end(layout, ptr); - -#else /* WITH_MOD_REMESH */ - uiItemL(layout, IFACE_("Built without Remesh modifier"), ICON_NONE); -#endif /* WITH_MOD_REMESH */ } static void panelRegister(ARegionType *region_type) diff --git a/source/blender/modifiers/intern/MOD_solidify.c b/source/blender/modifiers/intern/MOD_solidify.c index 736dd08a713..dc23b1f33d8 100644 --- a/source/blender/modifiers/intern/MOD_solidify.c +++ b/source/blender/modifiers/intern/MOD_solidify.c @@ -29,22 +29,26 @@ #include "DNA_defaults.h" #include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" #include "DNA_object_types.h" #include "DNA_screen_types.h" #include "BKE_context.h" -#include "BKE_particle.h" +#include "BKE_deform.h" #include "BKE_screen.h" +#include "GEO_solidifiy.h" + #include "UI_interface.h" #include "UI_resources.h" #include "RNA_access.h" -#include "MOD_modifiertypes.h" -#include "MOD_ui_common.h" +#include "MEM_guardedalloc.h" +#include "MOD_modifiertypes.h" #include "MOD_solidify_util.h" +#include "MOD_ui_common.h" static bool dependsOnNormals(ModifierData *md) { @@ -81,14 +85,154 @@ static void requiredDataMask(Object *UNUSED(ob), } } +static void get_distance_factor( + Mesh *mesh, Object *ob, const char *name, bool invert, float *r_selection) +{ + int defgrp_index = BKE_object_defgroup_name_index(ob, name); + + if (mesh && defgrp_index != -1) { + MDeformVert *dvert = mesh->dvert; + for (int i = 0; i < mesh->totvert; i++) { + MDeformVert *dv = &dvert[i]; + r_selection[i] = BKE_defvert_find_weight(dv, defgrp_index); + } + } + else { + for (int i = 0; i < mesh->totvert; i++) { + r_selection[i] = 1.0f; + } + } + + if (invert) { + for (int i = 0; i < mesh->totvert; i++) { + r_selection[i] = 1.0f - r_selection[i]; + } + } +} + +static SolidifyData solidify_data_from_modifier_data(ModifierData *md, + const ModifierEvalContext *ctx) +{ + const SolidifyModifierData *smd = (SolidifyModifierData *)md; + SolidifyData solidify_data = { + ctx->object, + smd->offset, + smd->offset_fac, + smd->offset_fac_vg, + smd->offset_clamp, + smd->nonmanifold_offset_mode, + smd->nonmanifold_boundary_mode, + smd->flag, + smd->merge_tolerance, + smd->bevel_convex, + NULL, + }; + + if (!(smd->flag & MOD_SOLIDIFY_NOSHELL)) { + solidify_data.flag |= MOD_SOLIDIFY_SHELL; + } + + return solidify_data; +} + +static Mesh *solidify_nonmanifold_modify_mesh(ModifierData *md, + const ModifierEvalContext *ctx, + Mesh *mesh, + const SolidifyModifierData *smd) +{ + SolidifyData solidify_data = solidify_data_from_modifier_data(md, ctx); + + const bool defgrp_invert = (solidify_data.flag & MOD_SOLIDIFY_VGROUP_INV) != 0; + float *selection = MEM_callocN(sizeof(float) * (uint64_t)mesh->totvert, __func__); + get_distance_factor(mesh, ctx->object, smd->defgrp_name, defgrp_invert, selection); + solidify_data.distance = selection; + + bool *shell_verts = NULL; + bool *rim_verts = NULL; + bool *shell_faces = NULL; + bool *rim_faces = NULL; + + Mesh *output_mesh = solidify_nonmanifold( + &solidify_data, mesh, &shell_verts, &rim_verts, &shell_faces, &rim_faces); + + const int shell_defgrp_index = BKE_object_defgroup_name_index(ctx->object, + smd->shell_defgrp_name); + const int rim_defgrp_index = BKE_object_defgroup_name_index(ctx->object, smd->rim_defgrp_name); + + MDeformVert *dvert; + if (shell_defgrp_index != -1 || rim_defgrp_index != -1) { + dvert = CustomData_duplicate_referenced_layer( + &output_mesh->vdata, CD_MDEFORMVERT, output_mesh->totvert); + /* If no vertices were ever added to an object's vgroup, dvert might be NULL. */ + if (dvert == NULL) { + /* Add a valid data layer! */ + dvert = CustomData_add_layer( + &output_mesh->vdata, CD_MDEFORMVERT, CD_CALLOC, NULL, output_mesh->totvert); + } + output_mesh->dvert = dvert; + if ((solidify_data.flag & MOD_SOLIDIFY_SHELL) && shell_defgrp_index != -1) { + for (int i = 0; i < output_mesh->totvert; i++) { + BKE_defvert_ensure_index(&output_mesh->dvert[i], shell_defgrp_index)->weight = + shell_verts[i]; + } + } + if ((solidify_data.flag & MOD_SOLIDIFY_RIM) && rim_defgrp_index != -1) { + for (int i = 0; i < output_mesh->totvert; i++) { + BKE_defvert_ensure_index(&output_mesh->dvert[i], rim_defgrp_index)->weight = rim_verts[i]; + } + } + } + + /* Only use material offsets if we have 2 or more materials. */ + const short mat_nrs = ctx->object->totcol > 1 ? ctx->object->totcol : 1; + const short mat_nr_max = mat_nrs - 1; + const short mat_ofs = mat_nrs > 1 ? smd->mat_ofs : 0; + const short mat_ofs_rim = mat_nrs > 1 ? smd->mat_ofs_rim : 0; + + short most_mat_nr = 0; + uint most_mat_nr_count = 0; + for (int mat_nr = 0; mat_nr < mat_nrs; mat_nr++) { + uint count = 0; + for (int i = 0; i < mesh->totpoly; i++) { + if (mesh->mpoly[i].mat_nr == mat_nr) { + count++; + } + } + if (count > most_mat_nr_count) { + most_mat_nr = mat_nr; + } + } + + for (int i = 0; i < output_mesh->totpoly; i++) { + output_mesh->mpoly[i].mat_nr = most_mat_nr; + if (mat_ofs > 0 && shell_faces && shell_faces[i]) { + output_mesh->mpoly[i].mat_nr += mat_ofs; + CLAMP(output_mesh->mpoly[i].mat_nr, 0, mat_nr_max); + } + else if (mat_ofs_rim > 0 && rim_faces && rim_faces[i]) { + output_mesh->mpoly[i].mat_nr += mat_ofs_rim; + CLAMP(output_mesh->mpoly[i].mat_nr, 0, mat_nr_max); + } + } + + MEM_freeN(selection); + MEM_freeN(shell_verts); + MEM_freeN(rim_verts); + MEM_freeN(shell_faces); + MEM_freeN(rim_faces); + return output_mesh; +} + static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *mesh) { const SolidifyModifierData *smd = (SolidifyModifierData *)md; + switch (smd->mode) { case MOD_SOLIDIFY_MODE_EXTRUDE: return MOD_solidify_extrude_modifyMesh(md, ctx, mesh); - case MOD_SOLIDIFY_MODE_NONMANIFOLD: - return MOD_solidify_nonmanifold_modifyMesh(md, ctx, mesh); + case MOD_SOLIDIFY_MODE_NONMANIFOLD: { + return solidify_nonmanifold_modify_mesh(md, ctx, mesh, smd); + } default: BLI_assert(0); } diff --git a/source/blender/modifiers/intern/MOD_solidify_util.h b/source/blender/modifiers/intern/MOD_solidify_util.h index e9f0709f7ea..770ab3a8141 100644 --- a/source/blender/modifiers/intern/MOD_solidify_util.h +++ b/source/blender/modifiers/intern/MOD_solidify_util.h @@ -24,8 +24,3 @@ Mesh *MOD_solidify_extrude_modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *mesh); - -/* MOD_solidify_nonmanifold.c */ -Mesh *MOD_solidify_nonmanifold_modifyMesh(ModifierData *md, - const ModifierEvalContext *ctx, - Mesh *mesh); diff --git a/source/blender/modifiers/intern/MOD_weld.c b/source/blender/modifiers/intern/MOD_weld.c index b1fa2a7d912..4d50d9ea243 100644 --- a/source/blender/modifiers/intern/MOD_weld.c +++ b/source/blender/modifiers/intern/MOD_weld.c @@ -33,12 +33,8 @@ #include "MEM_guardedalloc.h" -#include "BLI_utildefines.h" - -#include "BLI_alloca.h" -#include "BLI_bitmap.h" -#include "BLI_kdtree.h" #include "BLI_math.h" +#include "BLI_utildefines.h" #include "BLT_translation.h" @@ -55,1542 +51,24 @@ #include "BKE_context.h" #include "BKE_deform.h" -#include "BKE_mesh.h" #include "BKE_modifier.h" #include "BKE_screen.h" +#include "GEO_mesh_merge_by_distance.h" + #include "UI_interface.h" #include "UI_resources.h" #include "RNA_access.h" -#include "DEG_depsgraph.h" - -#include "MOD_modifiertypes.h" #include "MOD_ui_common.h" -/* Indicates when the element was not computed. */ -#define OUT_OF_CONTEXT (uint)(-1) -/* Indicates if the edge or face will be collapsed. */ -#define ELEM_COLLAPSED (uint)(-2) -/* indicates whether an edge or vertex in groups_map will be merged. */ -#define ELEM_MERGED (uint)(-2) - -/* Used to indicate a range in an array specifying a group. */ -struct WeldGroup { - uint len; - uint ofs; -}; - -/* Edge groups that will be merged. Final vertices are also indicated. */ -struct WeldGroupEdge { - struct WeldGroup group; - uint v1; - uint v2; -}; - -typedef struct WeldVert { - /* Indexes relative to the original Mesh. */ - uint vert_dest; - uint vert_orig; -} WeldVert; - -typedef struct WeldEdge { - union { - uint flag; - struct { - /* Indexes relative to the original Mesh. */ - uint edge_dest; - uint edge_orig; - uint vert_a; - uint vert_b; - }; - }; -} WeldEdge; - -typedef struct WeldLoop { - union { - uint flag; - struct { - /* Indexes relative to the original Mesh. */ - uint vert; - uint edge; - uint loop_orig; - uint loop_skip_to; - }; - }; -} WeldLoop; - -typedef struct WeldPoly { - union { - uint flag; - struct { - /* Indexes relative to the original Mesh. */ - uint poly_dst; - uint poly_orig; - uint loop_start; - uint loop_end; - /* Final Polygon Size. */ - uint len; - /* Group of loops that will be affected. */ - struct WeldGroup loops; - }; - }; -} WeldPoly; - -typedef struct WeldMesh { - /* Group of vertices to be merged. */ - struct WeldGroup *vert_groups; - uint *vert_groups_buffer; - - /* Group of edges to be merged. */ - struct WeldGroupEdge *edge_groups; - uint *edge_groups_buffer; - /* From the original index of the vertex, this indicates which group it is or is going to be - * merged. */ - uint *edge_groups_map; - - /* References all polygons and loops that will be affected. */ - WeldLoop *wloop; - WeldPoly *wpoly; - WeldPoly *wpoly_new; - uint wloop_len; - uint wpoly_len; - uint wpoly_new_len; - - /* From the actual index of the element in the mesh, it indicates what is the index of the Weld - * element above. */ - uint *loop_map; - uint *poly_map; - - uint vert_kill_len; - uint edge_kill_len; - uint loop_kill_len; - uint poly_kill_len; /* Including the new polygons. */ - - /* Size of the affected polygon with more sides. */ - uint max_poly_len; -} WeldMesh; - -typedef struct WeldLoopOfPolyIter { - uint loop_start; - uint loop_end; - const WeldLoop *wloop; - const MLoop *mloop; - const uint *loop_map; - /* Weld group. */ - uint *group; - - uint l_curr; - uint l_next; - - /* Return */ - uint group_len; - uint v; - uint e; - char type; -} WeldLoopOfPolyIter; - -/* -------------------------------------------------------------------- */ -/** \name Debug Utils - * \{ */ - -#ifdef USE_WELD_DEBUG -static bool weld_iter_loop_of_poly_begin(WeldLoopOfPolyIter *iter, - const WeldPoly *wp, - const WeldLoop *wloop, - const MLoop *mloop, - const uint *loop_map, - uint *group_buffer); - -static bool weld_iter_loop_of_poly_next(WeldLoopOfPolyIter *iter); - -static void weld_assert_edge_kill_len(const WeldEdge *wedge, - const uint wedge_len, - const uint supposed_kill_len) -{ - uint kills = 0; - const WeldEdge *we = &wedge[0]; - for (uint i = wedge_len; i--; we++) { - uint edge_dest = we->edge_dest; - /* Magically includes collapsed edges. */ - if (edge_dest != OUT_OF_CONTEXT) { - kills++; - } - } - BLI_assert(kills == supposed_kill_len); -} - -static void weld_assert_poly_and_loop_kill_len(const WeldPoly *wpoly, - const WeldPoly *wpoly_new, - const uint wpoly_new_len, - const WeldLoop *wloop, - const MLoop *mloop, - const uint *loop_map, - const uint *poly_map, - const MPoly *mpoly, - const uint mpoly_len, - const uint mloop_len, - const uint supposed_poly_kill_len, - const uint supposed_loop_kill_len) -{ - uint poly_kills = 0; - uint loop_kills = mloop_len; - const MPoly *mp = &mpoly[0]; - for (uint i = 0; i < mpoly_len; i++, mp++) { - uint poly_ctx = poly_map[i]; - if (poly_ctx != OUT_OF_CONTEXT) { - const WeldPoly *wp = &wpoly[poly_ctx]; - WeldLoopOfPolyIter iter; - if (!weld_iter_loop_of_poly_begin(&iter, wp, wloop, mloop, loop_map, NULL)) { - poly_kills++; - continue; - } - else { - if (wp->poly_dst != OUT_OF_CONTEXT) { - poly_kills++; - continue; - } - uint remain = wp->len; - uint l = wp->loop_start; - while (remain) { - uint l_next = l + 1; - uint loop_ctx = loop_map[l]; - if (loop_ctx != OUT_OF_CONTEXT) { - const WeldLoop *wl = &wloop[loop_ctx]; - if (wl->loop_skip_to != OUT_OF_CONTEXT) { - l_next = wl->loop_skip_to; - } - if (wl->flag != ELEM_COLLAPSED) { - loop_kills--; - remain--; - } - } - else { - loop_kills--; - remain--; - } - l = l_next; - } - } - } - else { - loop_kills -= mp->totloop; - } - } - - const WeldPoly *wp = &wpoly_new[0]; - for (uint i = wpoly_new_len; i--; wp++) { - if (wp->poly_dst != OUT_OF_CONTEXT) { - poly_kills++; - continue; - } - uint remain = wp->len; - uint l = wp->loop_start; - while (remain) { - uint l_next = l + 1; - uint loop_ctx = loop_map[l]; - if (loop_ctx != OUT_OF_CONTEXT) { - const WeldLoop *wl = &wloop[loop_ctx]; - if (wl->loop_skip_to != OUT_OF_CONTEXT) { - l_next = wl->loop_skip_to; - } - if (wl->flag != ELEM_COLLAPSED) { - loop_kills--; - remain--; - } - } - else { - loop_kills--; - remain--; - } - l = l_next; - } - } - - BLI_assert(poly_kills == supposed_poly_kill_len); - BLI_assert(loop_kills == supposed_loop_kill_len); -} - -static void weld_assert_poly_no_vert_repetition(const WeldPoly *wp, - const WeldLoop *wloop, - const MLoop *mloop, - const uint *loop_map) -{ - const uint len = wp->len; - uint *verts = BLI_array_alloca(verts, len); - WeldLoopOfPolyIter iter; - if (!weld_iter_loop_of_poly_begin(&iter, wp, wloop, mloop, loop_map, NULL)) { - return; - } - else { - uint i = 0; - while (weld_iter_loop_of_poly_next(&iter)) { - verts[i++] = iter.v; - } - } - for (uint i = 0; i < len; i++) { - uint va = verts[i]; - for (uint j = i + 1; j < len; j++) { - uint vb = verts[j]; - BLI_assert(va != vb); - } - } -} - -static void weld_assert_poly_len(const WeldPoly *wp, const WeldLoop *wloop) -{ - if (wp->flag == ELEM_COLLAPSED) { - return; - } - - uint len = wp->len; - const WeldLoop *wl = &wloop[wp->loops.ofs]; - BLI_assert(wp->loop_start <= wl->loop_orig); - - uint end_wloop = wp->loops.ofs + wp->loops.len; - const WeldLoop *wl_end = &wloop[end_wloop - 1]; - - uint min_len = 0; - for (; wl <= wl_end; wl++) { - BLI_assert(wl->loop_skip_to == OUT_OF_CONTEXT); /* Not for this case. */ - if (wl->flag != ELEM_COLLAPSED) { - min_len++; - } - } - BLI_assert(len >= min_len); - - uint max_len = wp->loop_end - wp->loop_start + 1; - BLI_assert(len <= max_len); -} -#endif -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Weld Vert API - * \{ */ - -static void weld_vert_ctx_alloc_and_setup(const uint mvert_len, - uint *r_vert_dest_map, - WeldVert **r_wvert, - uint *r_wvert_len) -{ - /* Vert Context. */ - uint wvert_len = 0; - - WeldVert *wvert, *wv; - wvert = MEM_mallocN(sizeof(*wvert) * mvert_len, __func__); - wv = &wvert[0]; - - uint *v_dest_iter = &r_vert_dest_map[0]; - for (uint i = 0; i < mvert_len; i++, v_dest_iter++) { - if (*v_dest_iter != OUT_OF_CONTEXT) { - wv->vert_dest = *v_dest_iter; - wv->vert_orig = i; - wv++; - wvert_len++; - } - } - - *r_wvert = MEM_reallocN(wvert, sizeof(*wvert) * wvert_len); - *r_wvert_len = wvert_len; -} - -static void weld_vert_groups_setup(const uint mvert_len, - const uint wvert_len, - const WeldVert *wvert, - const uint *vert_dest_map, - uint *r_vert_groups_map, - uint **r_vert_groups_buffer, - struct WeldGroup **r_vert_groups) -{ - /* Get weld vert groups. */ - - uint wgroups_len = 0; - const uint *vert_dest_iter = &vert_dest_map[0]; - uint *group_map_iter = &r_vert_groups_map[0]; - for (uint i = 0; i < mvert_len; i++, group_map_iter++, vert_dest_iter++) { - uint vert_dest = *vert_dest_iter; - if (vert_dest != OUT_OF_CONTEXT) { - if (vert_dest != i) { - *group_map_iter = ELEM_MERGED; - } - else { - *group_map_iter = wgroups_len; - wgroups_len++; - } - } - else { - *group_map_iter = OUT_OF_CONTEXT; - } - } - - struct WeldGroup *wgroups = MEM_callocN(sizeof(*wgroups) * wgroups_len, __func__); - - const WeldVert *wv = &wvert[0]; - for (uint i = wvert_len; i--; wv++) { - uint group_index = r_vert_groups_map[wv->vert_dest]; - wgroups[group_index].len++; - } - - uint ofs = 0; - struct WeldGroup *wg_iter = &wgroups[0]; - for (uint i = wgroups_len; i--; wg_iter++) { - wg_iter->ofs = ofs; - ofs += wg_iter->len; - } - - BLI_assert(ofs == wvert_len); - - uint *groups_buffer = MEM_mallocN(sizeof(*groups_buffer) * ofs, __func__); - wv = &wvert[0]; - for (uint i = wvert_len; i--; wv++) { - uint group_index = r_vert_groups_map[wv->vert_dest]; - groups_buffer[wgroups[group_index].ofs++] = wv->vert_orig; - } - - wg_iter = &wgroups[0]; - for (uint i = wgroups_len; i--; wg_iter++) { - wg_iter->ofs -= wg_iter->len; - } - - *r_vert_groups = wgroups; - *r_vert_groups_buffer = groups_buffer; -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Weld Edge API - * \{ */ - -static void weld_edge_ctx_setup(const uint mvert_len, - const uint wedge_len, - struct WeldGroup *r_vlinks, - uint *r_edge_dest_map, - WeldEdge *r_wedge, - uint *r_edge_kiil_len) -{ - WeldEdge *we; - - /* Setup Edge Overlap. */ - uint edge_kill_len = 0; - - struct WeldGroup *vl_iter, *v_links; - v_links = r_vlinks; - vl_iter = &v_links[0]; - - we = &r_wedge[0]; - for (uint i = wedge_len; i--; we++) { - uint dst_vert_a = we->vert_a; - uint dst_vert_b = we->vert_b; - - if (dst_vert_a == dst_vert_b) { - BLI_assert(we->edge_dest == OUT_OF_CONTEXT); - r_edge_dest_map[we->edge_orig] = ELEM_COLLAPSED; - we->flag = ELEM_COLLAPSED; - edge_kill_len++; - continue; - } - - v_links[dst_vert_a].len++; - v_links[dst_vert_b].len++; - } - - uint link_len = 0; - vl_iter = &v_links[0]; - for (uint i = mvert_len; i--; vl_iter++) { - vl_iter->ofs = link_len; - link_len += vl_iter->len; - } - - if (link_len) { - uint *link_edge_buffer = MEM_mallocN(sizeof(*link_edge_buffer) * link_len, __func__); - - we = &r_wedge[0]; - for (uint i = 0; i < wedge_len; i++, we++) { - if (we->flag == ELEM_COLLAPSED) { - continue; - } - - uint dst_vert_a = we->vert_a; - uint dst_vert_b = we->vert_b; - - link_edge_buffer[v_links[dst_vert_a].ofs++] = i; - link_edge_buffer[v_links[dst_vert_b].ofs++] = i; - } - - vl_iter = &v_links[0]; - for (uint i = mvert_len; i--; vl_iter++) { - /* Fix offset */ - vl_iter->ofs -= vl_iter->len; - } - - we = &r_wedge[0]; - for (uint i = 0; i < wedge_len; i++, we++) { - if (we->edge_dest != OUT_OF_CONTEXT) { - /* No need to retest edges. - * (Already includes collapsed edges). */ - continue; - } - - uint dst_vert_a = we->vert_a; - uint dst_vert_b = we->vert_b; - - struct WeldGroup *link_a = &v_links[dst_vert_a]; - struct WeldGroup *link_b = &v_links[dst_vert_b]; - - uint edges_len_a = link_a->len; - uint edges_len_b = link_b->len; - - if (edges_len_a <= 1 || edges_len_b <= 1) { - continue; - } - - uint *edges_ctx_a = &link_edge_buffer[link_a->ofs]; - uint *edges_ctx_b = &link_edge_buffer[link_b->ofs]; - uint edge_orig = we->edge_orig; - - for (; edges_len_a--; edges_ctx_a++) { - uint e_ctx_a = *edges_ctx_a; - if (e_ctx_a == i) { - continue; - } - while (edges_len_b && *edges_ctx_b < e_ctx_a) { - edges_ctx_b++; - edges_len_b--; - } - if (edges_len_b == 0) { - break; - } - uint e_ctx_b = *edges_ctx_b; - if (e_ctx_a == e_ctx_b) { - WeldEdge *we_b = &r_wedge[e_ctx_b]; - BLI_assert(ELEM(we_b->vert_a, dst_vert_a, dst_vert_b)); - BLI_assert(ELEM(we_b->vert_b, dst_vert_a, dst_vert_b)); - BLI_assert(we_b->edge_dest == OUT_OF_CONTEXT); - BLI_assert(we_b->edge_orig != edge_orig); - r_edge_dest_map[we_b->edge_orig] = edge_orig; - we_b->edge_dest = edge_orig; - edge_kill_len++; - } - } - } - -#ifdef USE_WELD_DEBUG - weld_assert_edge_kill_len(r_wedge, wedge_len, edge_kill_len); -#endif - - MEM_freeN(link_edge_buffer); - } - - *r_edge_kiil_len = edge_kill_len; -} - -static void weld_edge_ctx_alloc(const MEdge *medge, - const uint medge_len, - const uint *vert_dest_map, - uint *r_edge_dest_map, - uint **r_edge_ctx_map, - WeldEdge **r_wedge, - uint *r_wedge_len) -{ - /* Edge Context. */ - uint *edge_map = MEM_mallocN(sizeof(*edge_map) * medge_len, __func__); - uint wedge_len = 0; - - WeldEdge *wedge, *we; - wedge = MEM_mallocN(sizeof(*wedge) * medge_len, __func__); - we = &wedge[0]; - - const MEdge *me = &medge[0]; - uint *e_dest_iter = &r_edge_dest_map[0]; - uint *iter = &edge_map[0]; - for (uint i = 0; i < medge_len; i++, me++, iter++, e_dest_iter++) { - uint v1 = me->v1; - uint v2 = me->v2; - uint v_dest_1 = vert_dest_map[v1]; - uint v_dest_2 = vert_dest_map[v2]; - if ((v_dest_1 != OUT_OF_CONTEXT) || (v_dest_2 != OUT_OF_CONTEXT)) { - we->vert_a = (v_dest_1 != OUT_OF_CONTEXT) ? v_dest_1 : v1; - we->vert_b = (v_dest_2 != OUT_OF_CONTEXT) ? v_dest_2 : v2; - we->edge_dest = OUT_OF_CONTEXT; - we->edge_orig = i; - we++; - *e_dest_iter = i; - *iter = wedge_len++; - } - else { - *e_dest_iter = OUT_OF_CONTEXT; - *iter = OUT_OF_CONTEXT; - } - } - - *r_wedge = MEM_reallocN(wedge, sizeof(*wedge) * wedge_len); - *r_wedge_len = wedge_len; - *r_edge_ctx_map = edge_map; -} - -static void weld_edge_groups_setup(const uint medge_len, - const uint edge_kill_len, - const uint wedge_len, - WeldEdge *wedge, - const uint *wedge_map, - uint *r_edge_groups_map, - uint **r_edge_groups_buffer, - struct WeldGroupEdge **r_edge_groups) -{ - - /* Get weld edge groups. */ - - struct WeldGroupEdge *wegroups, *wegrp_iter; - - uint wgroups_len = wedge_len - edge_kill_len; - wegroups = MEM_callocN(sizeof(*wegroups) * wgroups_len, __func__); - wegrp_iter = &wegroups[0]; - - wgroups_len = 0; - const uint *edge_ctx_iter = &wedge_map[0]; - uint *group_map_iter = &r_edge_groups_map[0]; - for (uint i = medge_len; i--; edge_ctx_iter++, group_map_iter++) { - uint edge_ctx = *edge_ctx_iter; - if (edge_ctx != OUT_OF_CONTEXT) { - WeldEdge *we = &wedge[edge_ctx]; - uint edge_dest = we->edge_dest; - if (edge_dest != OUT_OF_CONTEXT) { - BLI_assert(edge_dest != we->edge_orig); - *group_map_iter = ELEM_MERGED; - } - else { - we->edge_dest = we->edge_orig; - wegrp_iter->v1 = we->vert_a; - wegrp_iter->v2 = we->vert_b; - *group_map_iter = wgroups_len; - wgroups_len++; - wegrp_iter++; - } - } - else { - *group_map_iter = OUT_OF_CONTEXT; - } - } - - BLI_assert(wgroups_len == wedge_len - edge_kill_len); - - WeldEdge *we = &wedge[0]; - for (uint i = wedge_len; i--; we++) { - if (we->flag == ELEM_COLLAPSED) { - continue; - } - uint group_index = r_edge_groups_map[we->edge_dest]; - wegroups[group_index].group.len++; - } - - uint ofs = 0; - wegrp_iter = &wegroups[0]; - for (uint i = wgroups_len; i--; wegrp_iter++) { - wegrp_iter->group.ofs = ofs; - ofs += wegrp_iter->group.len; - } - - uint *groups_buffer = MEM_mallocN(sizeof(*groups_buffer) * ofs, __func__); - we = &wedge[0]; - for (uint i = wedge_len; i--; we++) { - if (we->flag == ELEM_COLLAPSED) { - continue; - } - uint group_index = r_edge_groups_map[we->edge_dest]; - groups_buffer[wegroups[group_index].group.ofs++] = we->edge_orig; - } - - wegrp_iter = &wegroups[0]; - for (uint i = wgroups_len; i--; wegrp_iter++) { - wegrp_iter->group.ofs -= wegrp_iter->group.len; - } - - *r_edge_groups_buffer = groups_buffer; - *r_edge_groups = wegroups; -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Weld Poly and Loop API - * \{ */ - -static bool weld_iter_loop_of_poly_begin(WeldLoopOfPolyIter *iter, - const WeldPoly *wp, - const WeldLoop *wloop, - const MLoop *mloop, - const uint *loop_map, - uint *group_buffer) -{ - if (wp->flag == ELEM_COLLAPSED) { - return false; - } - - iter->loop_start = wp->loop_start; - iter->loop_end = wp->loop_end; - iter->wloop = wloop; - iter->mloop = mloop; - iter->loop_map = loop_map; - iter->group = group_buffer; - - uint group_len = 0; - if (group_buffer) { - /* First loop group needs more attention. */ - uint loop_start, loop_end, l; - loop_start = iter->loop_start; - loop_end = l = iter->loop_end; - while (l >= loop_start) { - const uint loop_ctx = loop_map[l]; - if (loop_ctx != OUT_OF_CONTEXT) { - const WeldLoop *wl = &wloop[loop_ctx]; - if (wl->flag == ELEM_COLLAPSED) { - l--; - continue; - } - } - break; - } - if (l != loop_end) { - group_len = loop_end - l; - int i = 0; - while (l < loop_end) { - iter->group[i++] = ++l; - } - } - } - iter->group_len = group_len; - - iter->l_next = iter->loop_start; -#ifdef USE_WELD_DEBUG - iter->v = OUT_OF_CONTEXT; -#endif - return true; -} - -static bool weld_iter_loop_of_poly_next(WeldLoopOfPolyIter *iter) -{ - uint loop_end = iter->loop_end; - const WeldLoop *wloop = iter->wloop; - const uint *loop_map = iter->loop_map; - uint l = iter->l_curr = iter->l_next; - if (l == iter->loop_start) { - /* `grupo_len` is already calculated in the first loop */ - } - else { - iter->group_len = 0; - } - while (l <= loop_end) { - uint l_next = l + 1; - const uint loop_ctx = loop_map[l]; - if (loop_ctx != OUT_OF_CONTEXT) { - const WeldLoop *wl = &wloop[loop_ctx]; - if (wl->loop_skip_to != OUT_OF_CONTEXT) { - l_next = wl->loop_skip_to; - } - if (wl->flag == ELEM_COLLAPSED) { - if (iter->group) { - iter->group[iter->group_len++] = l; - } - l = l_next; - continue; - } -#ifdef USE_WELD_DEBUG - BLI_assert(iter->v != wl->vert); -#endif - iter->v = wl->vert; - iter->e = wl->edge; - iter->type = 1; - } - else { - const MLoop *ml = &iter->mloop[l]; -#ifdef USE_WELD_DEBUG - BLI_assert(iter->v != ml->v); -#endif - iter->v = ml->v; - iter->e = ml->e; - iter->type = 0; - } - if (iter->group) { - iter->group[iter->group_len++] = l; - } - iter->l_next = l_next; - return true; - } - - return false; -} - -static void weld_poly_loop_ctx_alloc(const MPoly *mpoly, - const uint mpoly_len, - const MLoop *mloop, - const uint mloop_len, - const uint *vert_dest_map, - const uint *edge_dest_map, - WeldMesh *r_weld_mesh) -{ - /* Loop/Poly Context. */ - uint *loop_map = MEM_mallocN(sizeof(*loop_map) * mloop_len, __func__); - uint *poly_map = MEM_mallocN(sizeof(*poly_map) * mpoly_len, __func__); - uint wloop_len = 0; - uint wpoly_len = 0; - uint max_ctx_poly_len = 4; - - WeldLoop *wloop, *wl; - wloop = MEM_mallocN(sizeof(*wloop) * mloop_len, __func__); - wl = &wloop[0]; - - WeldPoly *wpoly, *wp; - wpoly = MEM_mallocN(sizeof(*wpoly) * mpoly_len, __func__); - wp = &wpoly[0]; - - uint maybe_new_poly = 0; - - const MPoly *mp = &mpoly[0]; - uint *iter = &poly_map[0]; - uint *loop_map_iter = &loop_map[0]; - for (uint i = 0; i < mpoly_len; i++, mp++, iter++) { - const uint loopstart = mp->loopstart; - const uint totloop = mp->totloop; - - uint vert_ctx_len = 0; - - uint l = loopstart; - uint prev_wloop_len = wloop_len; - const MLoop *ml = &mloop[l]; - for (uint j = totloop; j--; l++, ml++, loop_map_iter++) { - uint v = ml->v; - uint e = ml->e; - uint v_dest = vert_dest_map[v]; - uint e_dest = edge_dest_map[e]; - bool is_vert_ctx = v_dest != OUT_OF_CONTEXT; - bool is_edge_ctx = e_dest != OUT_OF_CONTEXT; - if (is_vert_ctx) { - vert_ctx_len++; - } - if (is_vert_ctx || is_edge_ctx) { - wl->vert = is_vert_ctx ? v_dest : v; - wl->edge = is_edge_ctx ? e_dest : e; - wl->loop_orig = l; - wl->loop_skip_to = OUT_OF_CONTEXT; - wl++; - - *loop_map_iter = wloop_len++; - } - else { - *loop_map_iter = OUT_OF_CONTEXT; - } - } - if (wloop_len != prev_wloop_len) { - uint loops_len = wloop_len - prev_wloop_len; - - wp->poly_dst = OUT_OF_CONTEXT; - wp->poly_orig = i; - wp->loops.len = loops_len; - wp->loops.ofs = prev_wloop_len; - wp->loop_start = loopstart; - wp->loop_end = loopstart + totloop - 1; - wp->len = totloop; - wp++; - - *iter = wpoly_len++; - if (totloop > 5 && vert_ctx_len > 1) { - uint max_new = (totloop / 3) - 1; - vert_ctx_len /= 2; - maybe_new_poly += MIN2(max_new, vert_ctx_len); - CLAMP_MIN(max_ctx_poly_len, totloop); - } - } - else { - *iter = OUT_OF_CONTEXT; - } - } - - if (mpoly_len < (wpoly_len + maybe_new_poly)) { - WeldPoly *wpoly_tmp = wpoly; - wpoly = MEM_mallocN(sizeof(*wpoly) * ((size_t)wpoly_len + maybe_new_poly), __func__); - memcpy(wpoly, wpoly_tmp, sizeof(*wpoly) * wpoly_len); - MEM_freeN(wpoly_tmp); - } - - WeldPoly *poly_new = &wpoly[wpoly_len]; - - r_weld_mesh->wloop = MEM_reallocN(wloop, sizeof(*wloop) * wloop_len); - r_weld_mesh->wpoly = wpoly; - r_weld_mesh->wpoly_new = poly_new; - r_weld_mesh->wloop_len = wloop_len; - r_weld_mesh->wpoly_len = wpoly_len; - r_weld_mesh->wpoly_new_len = 0; - r_weld_mesh->loop_map = loop_map; - r_weld_mesh->poly_map = poly_map; - r_weld_mesh->max_poly_len = max_ctx_poly_len; -} - -static void weld_poly_split_recursive(const uint *vert_dest_map, -#ifdef USE_WELD_DEBUG - const MLoop *mloop, -#endif - uint ctx_verts_len, - WeldPoly *r_wp, - WeldMesh *r_weld_mesh, - uint *r_poly_kill, - uint *r_loop_kill) -{ - uint poly_len = r_wp->len; - if (poly_len > 3 && ctx_verts_len > 1) { - const uint ctx_loops_len = r_wp->loops.len; - const uint ctx_loops_ofs = r_wp->loops.ofs; - WeldLoop *wloop = r_weld_mesh->wloop; - WeldPoly *wpoly_new = r_weld_mesh->wpoly_new; - - uint loop_kill = 0; - - WeldLoop *poly_loops = &wloop[ctx_loops_ofs]; - WeldLoop *wla = &poly_loops[0]; - WeldLoop *wla_prev = &poly_loops[ctx_loops_len - 1]; - while (wla_prev->flag == ELEM_COLLAPSED) { - wla_prev--; - } - const uint la_len = ctx_loops_len - 1; - for (uint la = 0; la < la_len; la++, wla++) { - wa_continue: - if (wla->flag == ELEM_COLLAPSED) { - continue; - } - uint vert_a = wla->vert; - /* Only test vertices that will be merged. */ - if (vert_dest_map[vert_a] != OUT_OF_CONTEXT) { - uint lb = la + 1; - WeldLoop *wlb = wla + 1; - WeldLoop *wlb_prev = wla; - uint killed_ab = 0; - ctx_verts_len = 1; - for (; lb < ctx_loops_len; lb++, wlb++) { - BLI_assert(wlb->loop_skip_to == OUT_OF_CONTEXT); - if (wlb->flag == ELEM_COLLAPSED) { - killed_ab++; - continue; - } - uint vert_b = wlb->vert; - if (vert_dest_map[vert_b] != OUT_OF_CONTEXT) { - ctx_verts_len++; - } - if (vert_a == vert_b) { - const uint dist_a = wlb->loop_orig - wla->loop_orig - killed_ab; - const uint dist_b = poly_len - dist_a; - - BLI_assert(dist_a != 0 && dist_b != 0); - if (dist_a == 1 || dist_b == 1) { - BLI_assert(dist_a != dist_b); - BLI_assert((wla->flag == ELEM_COLLAPSED) || (wlb->flag == ELEM_COLLAPSED)); - } - else { - WeldLoop *wl_tmp = NULL; - if (dist_a == 2) { - wl_tmp = wlb_prev; - BLI_assert(wla->flag != ELEM_COLLAPSED); - BLI_assert(wl_tmp->flag != ELEM_COLLAPSED); - wla->flag = ELEM_COLLAPSED; - wl_tmp->flag = ELEM_COLLAPSED; - loop_kill += 2; - poly_len -= 2; - } - if (dist_b == 2) { - if (wl_tmp != NULL) { - r_wp->flag = ELEM_COLLAPSED; - *r_poly_kill += 1; - } - else { - wl_tmp = wla_prev; - BLI_assert(wlb->flag != ELEM_COLLAPSED); - BLI_assert(wl_tmp->flag != ELEM_COLLAPSED); - wlb->flag = ELEM_COLLAPSED; - wl_tmp->flag = ELEM_COLLAPSED; - } - loop_kill += 2; - poly_len -= 2; - } - if (wl_tmp == NULL) { - const uint new_loops_len = lb - la; - const uint new_loops_ofs = ctx_loops_ofs + la; - - WeldPoly *new_wp = &wpoly_new[r_weld_mesh->wpoly_new_len++]; - new_wp->poly_dst = OUT_OF_CONTEXT; - new_wp->poly_orig = r_wp->poly_orig; - new_wp->loops.len = new_loops_len; - new_wp->loops.ofs = new_loops_ofs; - new_wp->loop_start = wla->loop_orig; - new_wp->loop_end = wlb_prev->loop_orig; - new_wp->len = dist_a; - weld_poly_split_recursive(vert_dest_map, -#ifdef USE_WELD_DEBUG - mloop, -#endif - ctx_verts_len, - new_wp, - r_weld_mesh, - r_poly_kill, - r_loop_kill); - BLI_assert(dist_b == poly_len - dist_a); - poly_len = dist_b; - if (wla_prev->loop_orig > wla->loop_orig) { - /* New start. */ - r_wp->loop_start = wlb->loop_orig; - } - else { - /* The `loop_start` doesn't change but some loops must be skipped. */ - wla_prev->loop_skip_to = wlb->loop_orig; - } - wla = wlb; - la = lb; - goto wa_continue; - } - break; - } - } - if (wlb->flag != ELEM_COLLAPSED) { - wlb_prev = wlb; - } - } - } - if (wla->flag != ELEM_COLLAPSED) { - wla_prev = wla; - } - } - r_wp->len = poly_len; - *r_loop_kill += loop_kill; - -#ifdef USE_WELD_DEBUG - weld_assert_poly_no_vert_repetition(r_wp, wloop, mloop, r_weld_mesh->loop_map); -#endif - } -} - -static void weld_poly_loop_ctx_setup(const MLoop *mloop, -#ifdef USE_WELD_DEBUG - const MPoly *mpoly, - const uint mpoly_len, - const uint mloop_len, -#endif - const uint mvert_len, - const uint *vert_dest_map, - const uint remain_edge_ctx_len, - struct WeldGroup *r_vlinks, - WeldMesh *r_weld_mesh) -{ - uint poly_kill_len, loop_kill_len, wpoly_len, wpoly_new_len; - - WeldPoly *wpoly_new, *wpoly, *wp; - WeldLoop *wloop, *wl; - - wpoly = r_weld_mesh->wpoly; - wloop = r_weld_mesh->wloop; - wpoly_new = r_weld_mesh->wpoly_new; - wpoly_len = r_weld_mesh->wpoly_len; - wpoly_new_len = 0; - poly_kill_len = 0; - loop_kill_len = 0; - - const uint *loop_map = r_weld_mesh->loop_map; - - if (remain_edge_ctx_len) { - - /* Setup Poly/Loop. */ - - wp = &wpoly[0]; - for (uint i = wpoly_len; i--; wp++) { - const uint ctx_loops_len = wp->loops.len; - const uint ctx_loops_ofs = wp->loops.ofs; - - uint poly_len = wp->len; - uint ctx_verts_len = 0; - wl = &wloop[ctx_loops_ofs]; - for (uint l = ctx_loops_len; l--; wl++) { - const uint edge_dest = wl->edge; - if (edge_dest == ELEM_COLLAPSED) { - wl->flag = ELEM_COLLAPSED; - if (poly_len == 3) { - wp->flag = ELEM_COLLAPSED; - poly_kill_len++; - loop_kill_len += 3; - poly_len = 0; - break; - } - loop_kill_len++; - poly_len--; - } - else { - const uint vert_dst = wl->vert; - if (vert_dest_map[vert_dst] != OUT_OF_CONTEXT) { - ctx_verts_len++; - } - } - } - - if (poly_len) { - wp->len = poly_len; -#ifdef USE_WELD_DEBUG - weld_assert_poly_len(wp, wloop); -#endif - - weld_poly_split_recursive(vert_dest_map, -#ifdef USE_WELD_DEBUG - mloop, -#endif - ctx_verts_len, - wp, - r_weld_mesh, - &poly_kill_len, - &loop_kill_len); - - wpoly_new_len = r_weld_mesh->wpoly_new_len; - } - } - -#ifdef USE_WELD_DEBUG - weld_assert_poly_and_loop_kill_len(wpoly, - wpoly_new, - wpoly_new_len, - wloop, - mloop, - loop_map, - r_weld_mesh->poly_map, - mpoly, - mpoly_len, - mloop_len, - poly_kill_len, - loop_kill_len); -#endif - - /* Setup Polygon Overlap. */ - - uint wpoly_and_new_len = wpoly_len + wpoly_new_len; - - struct WeldGroup *vl_iter, *v_links = r_vlinks; - memset(v_links, 0, sizeof(*v_links) * mvert_len); - - wp = &wpoly[0]; - for (uint i = wpoly_and_new_len; i--; wp++) { - WeldLoopOfPolyIter iter; - if (weld_iter_loop_of_poly_begin(&iter, wp, wloop, mloop, loop_map, NULL)) { - while (weld_iter_loop_of_poly_next(&iter)) { - v_links[iter.v].len++; - } - } - } - - uint link_len = 0; - vl_iter = &v_links[0]; - for (uint i = mvert_len; i--; vl_iter++) { - vl_iter->ofs = link_len; - link_len += vl_iter->len; - } - - if (link_len) { - uint *link_poly_buffer = MEM_mallocN(sizeof(*link_poly_buffer) * link_len, __func__); - - wp = &wpoly[0]; - for (uint i = 0; i < wpoly_and_new_len; i++, wp++) { - WeldLoopOfPolyIter iter; - if (weld_iter_loop_of_poly_begin(&iter, wp, wloop, mloop, loop_map, NULL)) { - while (weld_iter_loop_of_poly_next(&iter)) { - link_poly_buffer[v_links[iter.v].ofs++] = i; - } - } - } - - vl_iter = &v_links[0]; - for (uint i = mvert_len; i--; vl_iter++) { - /* Fix offset */ - vl_iter->ofs -= vl_iter->len; - } - - uint polys_len_a, polys_len_b, *polys_ctx_a, *polys_ctx_b, p_ctx_a, p_ctx_b; - polys_len_b = p_ctx_b = 0; /* silence warnings */ - - wp = &wpoly[0]; - for (uint i = 0; i < wpoly_and_new_len; i++, wp++) { - if (wp->poly_dst != OUT_OF_CONTEXT) { - /* No need to retest poly. - * (Already includes collapsed polygons). */ - continue; - } - - WeldLoopOfPolyIter iter; - weld_iter_loop_of_poly_begin(&iter, wp, wloop, mloop, loop_map, NULL); - weld_iter_loop_of_poly_next(&iter); - struct WeldGroup *link_a = &v_links[iter.v]; - polys_len_a = link_a->len; - if (polys_len_a == 1) { - BLI_assert(link_poly_buffer[link_a->ofs] == i); - continue; - } - uint wp_len = wp->len; - polys_ctx_a = &link_poly_buffer[link_a->ofs]; - for (; polys_len_a--; polys_ctx_a++) { - p_ctx_a = *polys_ctx_a; - if (p_ctx_a == i) { - continue; - } - - WeldPoly *wp_tmp = &wpoly[p_ctx_a]; - if (wp_tmp->len != wp_len) { - continue; - } - - WeldLoopOfPolyIter iter_b = iter; - while (weld_iter_loop_of_poly_next(&iter_b)) { - struct WeldGroup *link_b = &v_links[iter_b.v]; - polys_len_b = link_b->len; - if (polys_len_b == 1) { - BLI_assert(link_poly_buffer[link_b->ofs] == i); - polys_len_b = 0; - break; - } - - polys_ctx_b = &link_poly_buffer[link_b->ofs]; - for (; polys_len_b; polys_len_b--, polys_ctx_b++) { - p_ctx_b = *polys_ctx_b; - if (p_ctx_b < p_ctx_a) { - continue; - } - if (p_ctx_b >= p_ctx_a) { - if (p_ctx_b > p_ctx_a) { - polys_len_b = 0; - } - break; - } - } - if (polys_len_b == 0) { - break; - } - } - if (polys_len_b == 0) { - continue; - } - BLI_assert(p_ctx_a > i); - BLI_assert(p_ctx_a == p_ctx_b); - BLI_assert(wp_tmp->poly_dst == OUT_OF_CONTEXT); - BLI_assert(wp_tmp != wp); - wp_tmp->poly_dst = wp->poly_orig; - loop_kill_len += wp_tmp->len; - poly_kill_len++; - } - } - MEM_freeN(link_poly_buffer); - } - } - else { - poly_kill_len = r_weld_mesh->wpoly_len; - loop_kill_len = r_weld_mesh->wloop_len; - - wp = &wpoly[0]; - for (uint i = wpoly_len; i--; wp++) { - wp->flag = ELEM_COLLAPSED; - } - } - -#ifdef USE_WELD_DEBUG - weld_assert_poly_and_loop_kill_len(wpoly, - wpoly_new, - wpoly_new_len, - wloop, - mloop, - loop_map, - r_weld_mesh->poly_map, - mpoly, - mpoly_len, - mloop_len, - poly_kill_len, - loop_kill_len); -#endif - - r_weld_mesh->wpoly_new = wpoly_new; - r_weld_mesh->poly_kill_len = poly_kill_len; - r_weld_mesh->loop_kill_len = loop_kill_len; -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Weld Mesh API - * \{ */ - -static void weld_mesh_context_create(const Mesh *mesh, - uint *vert_dest_map, - const uint vert_kill_len, - WeldMesh *r_weld_mesh) -{ - const MEdge *medge = mesh->medge; - const MLoop *mloop = mesh->mloop; - const MPoly *mpoly = mesh->mpoly; - const uint mvert_len = mesh->totvert; - const uint medge_len = mesh->totedge; - const uint mloop_len = mesh->totloop; - const uint mpoly_len = mesh->totpoly; - - uint *edge_dest_map = MEM_mallocN(sizeof(*edge_dest_map) * medge_len, __func__); - struct WeldGroup *v_links = MEM_callocN(sizeof(*v_links) * mvert_len, __func__); - - WeldVert *wvert; - uint wvert_len; - r_weld_mesh->vert_kill_len = vert_kill_len; - weld_vert_ctx_alloc_and_setup(mvert_len, vert_dest_map, &wvert, &wvert_len); - - uint *edge_ctx_map; - WeldEdge *wedge; - uint wedge_len; - weld_edge_ctx_alloc( - medge, medge_len, vert_dest_map, edge_dest_map, &edge_ctx_map, &wedge, &wedge_len); - - weld_edge_ctx_setup( - mvert_len, wedge_len, v_links, edge_dest_map, wedge, &r_weld_mesh->edge_kill_len); - - weld_poly_loop_ctx_alloc( - mpoly, mpoly_len, mloop, mloop_len, vert_dest_map, edge_dest_map, r_weld_mesh); - - weld_poly_loop_ctx_setup(mloop, -#ifdef USE_WELD_DEBUG - mpoly, - mpoly_len, - mloop_len, -#endif - mvert_len, - vert_dest_map, - wedge_len - r_weld_mesh->edge_kill_len, - v_links, - r_weld_mesh); - - weld_vert_groups_setup(mvert_len, - wvert_len, - wvert, - vert_dest_map, - vert_dest_map, - &r_weld_mesh->vert_groups_buffer, - &r_weld_mesh->vert_groups); - - weld_edge_groups_setup(medge_len, - r_weld_mesh->edge_kill_len, - wedge_len, - wedge, - edge_ctx_map, - edge_dest_map, - &r_weld_mesh->edge_groups_buffer, - &r_weld_mesh->edge_groups); - - r_weld_mesh->edge_groups_map = edge_dest_map; - MEM_freeN(v_links); - MEM_freeN(wvert); - MEM_freeN(edge_ctx_map); - MEM_freeN(wedge); -} - -static void weld_mesh_context_free(WeldMesh *weld_mesh) -{ - MEM_freeN(weld_mesh->vert_groups); - MEM_freeN(weld_mesh->vert_groups_buffer); - - MEM_freeN(weld_mesh->edge_groups); - MEM_freeN(weld_mesh->edge_groups_buffer); - MEM_freeN(weld_mesh->edge_groups_map); - - MEM_freeN(weld_mesh->wloop); - MEM_freeN(weld_mesh->wpoly); - MEM_freeN(weld_mesh->loop_map); - MEM_freeN(weld_mesh->poly_map); -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Weld CustomData - * \{ */ - -static void customdata_weld( - const CustomData *source, CustomData *dest, const uint *src_indices, int count, int dest_index) -{ - if (count == 1) { - CustomData_copy_data(source, dest, src_indices[0], dest_index, 1); - return; - } - - CustomData_interp(source, dest, (const int *)src_indices, NULL, NULL, count, dest_index); - - int src_i, dest_i; - int j; - - float co[3] = {0.0f, 0.0f, 0.0f}; -#ifdef USE_WELD_NORMALS - float no[3] = {0.0f, 0.0f, 0.0f}; -#endif - uint crease = 0; - uint bweight = 0; - short flag = 0; - - /* interpolates a layer at a time */ - dest_i = 0; - for (src_i = 0; src_i < source->totlayer; src_i++) { - const int type = source->layers[src_i].type; - - /* find the first dest layer with type >= the source type - * (this should work because layers are ordered by type) - */ - while (dest_i < dest->totlayer && dest->layers[dest_i].type < type) { - dest_i++; - } - - /* if there are no more dest layers, we're done */ - if (dest_i == dest->totlayer) { - break; - } - - /* if we found a matching layer, add the data */ - if (dest->layers[dest_i].type == type) { - void *src_data = source->layers[src_i].data; - - if (type == CD_MVERT) { - for (j = 0; j < count; j++) { - MVert *mv_src = &((MVert *)src_data)[src_indices[j]]; - add_v3_v3(co, mv_src->co); -#ifdef USE_WELD_NORMALS - short *mv_src_no = mv_src->no; - no[0] += mv_src_no[0]; - no[1] += mv_src_no[1]; - no[2] += mv_src_no[2]; -#endif - bweight += mv_src->bweight; - flag |= mv_src->flag; - } - } - else if (type == CD_MEDGE) { - for (j = 0; j < count; j++) { - MEdge *me_src = &((MEdge *)src_data)[src_indices[j]]; - crease += me_src->crease; - bweight += me_src->bweight; - flag |= me_src->flag; - } - } - else if (CustomData_layer_has_interp(dest, dest_i)) { - /* Already calculated. - * TODO: Optimize by exposing `typeInfo->interp`. */ - } - else if (CustomData_layer_has_math(dest, dest_i)) { - const int size = CustomData_sizeof(type); - void *dst_data = dest->layers[dest_i].data; - void *v_dst = POINTER_OFFSET(dst_data, (size_t)dest_index * size); - for (j = 0; j < count; j++) { - CustomData_data_add( - type, v_dst, POINTER_OFFSET(src_data, (size_t)src_indices[j] * size)); - } - } - else { - CustomData_copy_layer_type_data(source, dest, type, src_indices[0], dest_index, 1); - } - - /* if there are multiple source & dest layers of the same type, - * we don't want to copy all source layers to the same dest, so - * increment dest_i - */ - dest_i++; - } - } - - float fac = 1.0f / count; - - for (dest_i = 0; dest_i < dest->totlayer; dest_i++) { - CustomDataLayer *layer_dst = &dest->layers[dest_i]; - const int type = layer_dst->type; - if (type == CD_MVERT) { - MVert *mv = &((MVert *)layer_dst->data)[dest_index]; - mul_v3_fl(co, fac); - bweight *= fac; - CLAMP_MAX(bweight, 255); - - copy_v3_v3(mv->co, co); -#ifdef USE_WELD_NORMALS - mul_v3_fl(no, fac); - short *mv_no = mv->no; - mv_no[0] = (short)no[0]; - mv_no[1] = (short)no[1]; - mv_no[2] = (short)no[2]; -#endif - - mv->flag = (char)flag; - mv->bweight = (char)bweight; - } - else if (type == CD_MEDGE) { - MEdge *me = &((MEdge *)layer_dst->data)[dest_index]; - crease *= fac; - bweight *= fac; - CLAMP_MAX(crease, 255); - CLAMP_MAX(bweight, 255); - - me->crease = (char)crease; - me->bweight = (char)bweight; - me->flag = flag; - } - else if (CustomData_layer_has_interp(dest, dest_i)) { - /* Already calculated. */ - } - else if (CustomData_layer_has_math(dest, dest_i)) { - const int size = CustomData_sizeof(type); - void *dst_data = layer_dst->data; - void *v_dst = POINTER_OFFSET(dst_data, (size_t)dest_index * size); - CustomData_data_multiply(type, v_dst, fac); - } - } -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Weld Modifier Main - * \{ */ - -#ifdef USE_BVHTREEKDOP -struct WeldOverlapData { - const MVert *mvert; - float merge_dist_sq; -}; -static bool bvhtree_weld_overlap_cb(void *userdata, int index_a, int index_b, int UNUSED(thread)) -{ - if (index_a < index_b) { - struct WeldOverlapData *data = userdata; - const MVert *mvert = data->mvert; - const float dist_sq = len_squared_v3v3(mvert[index_a].co, mvert[index_b].co); - BLI_assert(dist_sq <= ((data->merge_dist_sq + FLT_EPSILON) * 3)); - return dist_sq <= data->merge_dist_sq; - } - return false; -} -#endif - -/** Use for #MOD_WELD_MODE_CONNECTED calculation. */ -struct WeldVertexCluster { - float co[3]; - uint merged_verts; -}; - -static Mesh *weldModifier_doWeld(WeldModifierData *wmd, - const ModifierEvalContext *UNUSED(ctx), - Mesh *mesh) +static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *UNUSED(ctx), Mesh *mesh) { - Mesh *result = mesh; - - BLI_bitmap *v_mask = NULL; - int v_mask_act = 0; - - const MVert *mvert; - const MLoop *mloop; - const MPoly *mpoly, *mp; - uint totvert, totedge, totloop, totpoly; - - mvert = mesh->mvert; - totvert = mesh->totvert; + WeldModifierData *wmd = (WeldModifierData *)md; - /* Vertex Group. */ + uint totvert = mesh->totvert; + bool *mask = MEM_malloc_arrayN(totvert, sizeof(*mask), __func__); const int defgrp_index = BKE_id_defgroup_name_index(&mesh->id, wmd->defgrp_name); if (defgrp_index != -1) { MDeformVert *dvert, *dv; @@ -1598,403 +76,29 @@ static Mesh *weldModifier_doWeld(WeldModifierData *wmd, if (dvert) { const bool invert_vgroup = (wmd->flag & MOD_WELD_INVERT_VGROUP) != 0; dv = &dvert[0]; - v_mask = BLI_BITMAP_NEW(totvert, __func__); for (uint i = 0; i < totvert; i++, dv++) { const bool found = BKE_defvert_find_weight(dv, defgrp_index) > 0.0f; if (found != invert_vgroup) { - BLI_BITMAP_ENABLE(v_mask, i); - v_mask_act++; - } - } - } - } - - /* From the original index of the vertex. - * This indicates which vert it is or is going to be merged. */ - uint *vert_dest_map = MEM_malloc_arrayN(totvert, sizeof(*vert_dest_map), __func__); - uint vert_kill_len = 0; - if (wmd->mode == MOD_WELD_MODE_ALL) -#ifdef USE_BVHTREEKDOP - { - /* Get overlap map. */ - struct BVHTreeFromMesh treedata; - BVHTree *bvhtree = bvhtree_from_mesh_verts_ex(&treedata, - mvert, - totvert, - false, - v_mask, - v_mask_act, - wmd->merge_dist / 2, - 2, - 6, - 0, - NULL, - NULL); - - if (bvhtree) { - struct WeldOverlapData data; - data.mvert = mvert; - data.merge_dist_sq = square_f(wmd->merge_dist); - - uint overlap_len; - BVHTreeOverlap *overlap = BLI_bvhtree_overlap_ex(bvhtree, - bvhtree, - &overlap_len, - bvhtree_weld_overlap_cb, - &data, - 1, - BVH_OVERLAP_RETURN_PAIRS); - - free_bvhtree_from_mesh(&treedata); - if (overlap) { - range_vn_u(vert_dest_map, totvert, 0); - - const BVHTreeOverlap *overlap_iter = &overlap[0]; - for (uint i = 0; i < overlap_len; i++, overlap_iter++) { - uint indexA = overlap_iter->indexA; - uint indexB = overlap_iter->indexB; - - BLI_assert(indexA < indexB); - - uint va_dst = vert_dest_map[indexA]; - while (va_dst != vert_dest_map[va_dst]) { - va_dst = vert_dest_map[va_dst]; - } - uint vb_dst = vert_dest_map[indexB]; - while (vb_dst != vert_dest_map[vb_dst]) { - vb_dst = vert_dest_map[vb_dst]; - } - if (va_dst == vb_dst) { - continue; - } - if (va_dst > vb_dst) { - SWAP(uint, va_dst, vb_dst); - } - vert_kill_len++; - vert_dest_map[vb_dst] = va_dst; + mask[i] = true; } - - /* Fix #r_vert_dest_map for next step. */ - for (uint i = 0; i < totvert; i++) { - if (i == vert_dest_map[i]) { - vert_dest_map[i] = OUT_OF_CONTEXT; - } - else { - uint v = i; - while (v != vert_dest_map[v] && vert_dest_map[v] != OUT_OF_CONTEXT) { - v = vert_dest_map[v]; - } - vert_dest_map[v] = v; - vert_dest_map[i] = v; - } + else { + mask[i] = false; } - - MEM_freeN(overlap); - } - } - } -#else - { - KDTree_3d *tree = BLI_kdtree_3d_new(v_mask ? v_mask_act : totvert); - for (uint i = 0; i < totvert; i++) { - if (!v_mask || BLI_BITMAP_TEST(v_mask, i)) { - BLI_kdtree_3d_insert(tree, i, mvert[i].co); } - vert_dest_map[i] = OUT_OF_CONTEXT; } - - BLI_kdtree_3d_balance(tree); - vert_kill_len = BLI_kdtree_3d_calc_duplicates_fast( - tree, wmd->merge_dist, false, (int *)vert_dest_map); - BLI_kdtree_3d_free(tree); } -#endif else { - BLI_assert(wmd->mode == MOD_WELD_MODE_CONNECTED); - - MEdge *medge, *me; - - medge = mesh->medge; - totvert = mesh->totvert; - totedge = mesh->totedge; - - struct WeldVertexCluster *vert_clusters = MEM_malloc_arrayN( - totvert, sizeof(*vert_clusters), __func__); - struct WeldVertexCluster *vc = &vert_clusters[0]; - for (uint i = 0; i < totvert; i++, vc++) { - copy_v3_v3(vc->co, mvert[i].co); - vc->merged_verts = 0; - } - const float merge_dist_sq = square_f(wmd->merge_dist); - - range_vn_u(vert_dest_map, totvert, 0); - - /* Collapse Edges that are shorter than the threshold. */ - me = &medge[0]; - for (uint i = 0; i < totedge; i++, me++) { - uint v1 = me->v1; - uint v2 = me->v2; - - if (wmd->flag & MOD_WELD_LOOSE_EDGES && (me->flag & ME_LOOSEEDGE) == 0) { - continue; - } - while (v1 != vert_dest_map[v1]) { - v1 = vert_dest_map[v1]; - } - while (v2 != vert_dest_map[v2]) { - v2 = vert_dest_map[v2]; - } - if (v1 == v2) { - continue; - } - if (v_mask && (!BLI_BITMAP_TEST(v_mask, v1) || !BLI_BITMAP_TEST(v_mask, v2))) { - continue; - } - if (v1 > v2) { - SWAP(uint, v1, v2); - } - struct WeldVertexCluster *v1_cluster = &vert_clusters[v1]; - struct WeldVertexCluster *v2_cluster = &vert_clusters[v2]; - - float edgedir[3]; - sub_v3_v3v3(edgedir, v2_cluster->co, v1_cluster->co); - const float dist_sq = len_squared_v3(edgedir); - if (dist_sq <= merge_dist_sq) { - float influence = (v2_cluster->merged_verts + 1) / - (float)(v1_cluster->merged_verts + v2_cluster->merged_verts + 2); - madd_v3_v3fl(v1_cluster->co, edgedir, influence); - - v1_cluster->merged_verts += v2_cluster->merged_verts + 1; - vert_dest_map[v2] = v1; - vert_kill_len++; - } + for (int i = 0; i < totvert; i++) { + mask[i] = true; } - - MEM_freeN(vert_clusters); - - for (uint i = 0; i < totvert; i++) { - if (i == vert_dest_map[i]) { - vert_dest_map[i] = OUT_OF_CONTEXT; - } - else { - uint v = i; - while ((v != vert_dest_map[v]) && (vert_dest_map[v] != OUT_OF_CONTEXT)) { - v = vert_dest_map[v]; - } - vert_dest_map[v] = v; - vert_dest_map[i] = v; - } - } - } - - if (v_mask) { - MEM_freeN(v_mask); } - if (vert_kill_len) { - WeldMesh weld_mesh; - weld_mesh_context_create(mesh, vert_dest_map, vert_kill_len, &weld_mesh); - - mloop = mesh->mloop; - mpoly = mesh->mpoly; - - totedge = mesh->totedge; - totloop = mesh->totloop; - totpoly = mesh->totpoly; - - const int result_nverts = totvert - weld_mesh.vert_kill_len; - const int result_nedges = totedge - weld_mesh.edge_kill_len; - const int result_nloops = totloop - weld_mesh.loop_kill_len; - const int result_npolys = totpoly - weld_mesh.poly_kill_len + weld_mesh.wpoly_new_len; - - result = BKE_mesh_new_nomain_from_template( - mesh, result_nverts, result_nedges, 0, result_nloops, result_npolys); - - /* Vertices */ - - uint *vert_final = vert_dest_map; - uint *index_iter = &vert_final[0]; - int dest_index = 0; - for (uint i = 0; i < totvert; i++, index_iter++) { - int source_index = i; - int count = 0; - while (i < totvert && *index_iter == OUT_OF_CONTEXT) { - *index_iter = dest_index + count; - index_iter++; - count++; - i++; - } - if (count) { - CustomData_copy_data(&mesh->vdata, &result->vdata, source_index, dest_index, count); - dest_index += count; - } - if (i == totvert) { - break; - } - if (*index_iter != ELEM_MERGED) { - struct WeldGroup *wgroup = &weld_mesh.vert_groups[*index_iter]; - customdata_weld(&mesh->vdata, - &result->vdata, - &weld_mesh.vert_groups_buffer[wgroup->ofs], - wgroup->len, - dest_index); - *index_iter = dest_index; - dest_index++; - } - } - - BLI_assert(dest_index == result_nverts); - - /* Edges */ - - uint *edge_final = weld_mesh.edge_groups_map; - index_iter = &edge_final[0]; - dest_index = 0; - for (uint i = 0; i < totedge; i++, index_iter++) { - int source_index = i; - int count = 0; - while (i < totedge && *index_iter == OUT_OF_CONTEXT) { - *index_iter = dest_index + count; - index_iter++; - count++; - i++; - } - if (count) { - CustomData_copy_data(&mesh->edata, &result->edata, source_index, dest_index, count); - MEdge *me = &result->medge[dest_index]; - dest_index += count; - for (; count--; me++) { - me->v1 = vert_final[me->v1]; - me->v2 = vert_final[me->v2]; - } - } - if (i == totedge) { - break; - } - if (*index_iter != ELEM_MERGED) { - struct WeldGroupEdge *wegrp = &weld_mesh.edge_groups[*index_iter]; - customdata_weld(&mesh->edata, - &result->edata, - &weld_mesh.edge_groups_buffer[wegrp->group.ofs], - wegrp->group.len, - dest_index); - MEdge *me = &result->medge[dest_index]; - me->v1 = vert_final[wegrp->v1]; - me->v2 = vert_final[wegrp->v2]; - me->flag |= ME_LOOSEEDGE; - - *index_iter = dest_index; - dest_index++; - } - } - - BLI_assert(dest_index == result_nedges); - - /* Polys/Loops */ - - mp = &mpoly[0]; - MPoly *r_mp = &result->mpoly[0]; - MLoop *r_ml = &result->mloop[0]; - uint r_i = 0; - int loop_cur = 0; - uint *group_buffer = BLI_array_alloca(group_buffer, weld_mesh.max_poly_len); - for (uint i = 0; i < totpoly; i++, mp++) { - int loop_start = loop_cur; - uint poly_ctx = weld_mesh.poly_map[i]; - if (poly_ctx == OUT_OF_CONTEXT) { - uint mp_loop_len = mp->totloop; - CustomData_copy_data(&mesh->ldata, &result->ldata, mp->loopstart, loop_cur, mp_loop_len); - loop_cur += mp_loop_len; - for (; mp_loop_len--; r_ml++) { - r_ml->v = vert_final[r_ml->v]; - r_ml->e = edge_final[r_ml->e]; - } - } - else { - WeldPoly *wp = &weld_mesh.wpoly[poly_ctx]; - WeldLoopOfPolyIter iter; - if (!weld_iter_loop_of_poly_begin( - &iter, wp, weld_mesh.wloop, mloop, weld_mesh.loop_map, group_buffer)) { - continue; - } - - if (wp->poly_dst != OUT_OF_CONTEXT) { - continue; - } - while (weld_iter_loop_of_poly_next(&iter)) { - customdata_weld(&mesh->ldata, &result->ldata, group_buffer, iter.group_len, loop_cur); - uint v = vert_final[iter.v]; - uint e = edge_final[iter.e]; - r_ml->v = v; - r_ml->e = e; - r_ml++; - loop_cur++; - if (iter.type) { - result->medge[e].flag &= ~ME_LOOSEEDGE; - } - BLI_assert((result->medge[e].flag & ME_LOOSEEDGE) == 0); - } - } - - CustomData_copy_data(&mesh->pdata, &result->pdata, i, r_i, 1); - r_mp->loopstart = loop_start; - r_mp->totloop = loop_cur - loop_start; - r_mp++; - r_i++; - } - - WeldPoly *wp = &weld_mesh.wpoly_new[0]; - for (uint i = 0; i < weld_mesh.wpoly_new_len; i++, wp++) { - int loop_start = loop_cur; - WeldLoopOfPolyIter iter; - if (!weld_iter_loop_of_poly_begin( - &iter, wp, weld_mesh.wloop, mloop, weld_mesh.loop_map, group_buffer)) { - continue; - } - - if (wp->poly_dst != OUT_OF_CONTEXT) { - continue; - } - while (weld_iter_loop_of_poly_next(&iter)) { - customdata_weld(&mesh->ldata, &result->ldata, group_buffer, iter.group_len, loop_cur); - uint v = vert_final[iter.v]; - uint e = edge_final[iter.e]; - r_ml->v = v; - r_ml->e = e; - r_ml++; - loop_cur++; - if (iter.type) { - result->medge[e].flag &= ~ME_LOOSEEDGE; - } - BLI_assert((result->medge[e].flag & ME_LOOSEEDGE) == 0); - } - - r_mp->loopstart = loop_start; - r_mp->totloop = loop_cur - loop_start; - r_mp++; - r_i++; - } - - BLI_assert((int)r_i == result_npolys); - BLI_assert(loop_cur == result_nloops); + Mesh *result = GEO_mesh_merge_by_distance(mesh, mask, wmd->merge_dist, wmd->mode); + MEM_freeN(mask); - /* is this needed? */ - /* recalculate normals */ - result->runtime.cd_dirty_vert |= CD_MASK_NORMAL; - - weld_mesh_context_free(&weld_mesh); - } - - MEM_freeN(vert_dest_map); return result; } -static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *mesh) -{ - WeldModifierData *wmd = (WeldModifierData *)md; - return weldModifier_doWeld(wmd, ctx, mesh); -} - static void initData(ModifierData *md) { WeldModifierData *wmd = (WeldModifierData *)md; @@ -2028,7 +132,7 @@ static void panel_draw(const bContext *UNUSED(C), Panel *panel) uiItemR(layout, ptr, "mode", 0, NULL, ICON_NONE); uiItemR(layout, ptr, "merge_threshold", 0, IFACE_("Distance"), ICON_NONE); - if (weld_mode == MOD_WELD_MODE_CONNECTED) { + if (weld_mode == WELD_MODE_CONNECTED) { uiItemR(layout, ptr, "loose_edges", 0, NULL, ICON_NONE); } modifier_vgroup_ui(layout, ptr, &ob_ptr, "vertex_group", "invert_vertex_group", NULL); diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index 8680fcee49a..198b1ddcaab 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -33,6 +33,7 @@ set(INC ../bmesh ../depsgraph ../functions + ../geometry ../gpu ../imbuf ../makesdna @@ -161,6 +162,7 @@ set(SRC geometry/nodes/node_geo_attribute_vector_rotate.cc geometry/nodes/node_geo_boolean.cc geometry/nodes/node_geo_bounding_box.cc + geometry/nodes/node_geo_collapse.cc geometry/nodes/node_geo_collection_info.cc geometry/nodes/node_geo_common.cc geometry/nodes/node_geo_convex_hull.cc @@ -183,12 +185,15 @@ set(SRC geometry/nodes/node_geo_curve_to_points.cc geometry/nodes/node_geo_curve_trim.cc geometry/nodes/node_geo_delete_geometry.cc + geometry/nodes/node_geo_dissolve.cc geometry/nodes/node_geo_edge_split.cc geometry/nodes/node_geo_input_material.cc geometry/nodes/node_geo_is_viewport.cc geometry/nodes/node_geo_join_geometry.cc geometry/nodes/node_geo_material_assign.cc geometry/nodes/node_geo_material_replace.cc + geometry/nodes/node_geo_mesh_extrude.cc + geometry/nodes/node_geo_mesh_inset.cc geometry/nodes/node_geo_mesh_primitive_circle.cc geometry/nodes/node_geo_mesh_primitive_cone.cc geometry/nodes/node_geo_mesh_primitive_cube.cc @@ -207,13 +212,18 @@ set(SRC geometry/nodes/node_geo_point_separate.cc geometry/nodes/node_geo_point_translate.cc geometry/nodes/node_geo_points_to_volume.cc + geometry/nodes/node_geo_merge_by_distance.cc geometry/nodes/node_geo_raycast.cc geometry/nodes/node_geo_select_by_material.cc geometry/nodes/node_geo_separate_components.cc + geometry/nodes/node_geo_solidify.cc geometry/nodes/node_geo_subdivision_surface.cc geometry/nodes/node_geo_switch.cc + geometry/nodes/node_geo_remesh_voxel.cc + geometry/nodes/node_geo_remesh_blocks.cc geometry/nodes/node_geo_transform.cc geometry/nodes/node_geo_triangulate.cc + geometry/nodes/node_geo_unsubdivide.cc geometry/nodes/node_geo_viewer.cc geometry/nodes/node_geo_volume_to_mesh.cc geometry/node_geometry_exec.cc @@ -380,9 +390,24 @@ set(SRC set(LIB bf_bmesh bf_functions + bf_geometry bf_intern_sky ) +if(WITH_OPENVDB) + list(APPEND INC + ../../../intern/openvdb + ) + list(APPEND INC_SYS + ${OPENVDB_INCLUDE_DIRS} + ) + list(APPEND LIB + bf_intern_openvdb + ${OPENVDB_LIBRARIES} + ) + add_definitions(-DWITH_OPENVDB ${OPENVDB_DEFINITIONS}) +endif() + if(WITH_BULLET) list(APPEND INC_SYS ${BULLET_INCLUDE_DIRS} diff --git a/source/blender/nodes/NOD_geometry.h b/source/blender/nodes/NOD_geometry.h index 856d787c8d0..196c656d085 100644 --- a/source/blender/nodes/NOD_geometry.h +++ b/source/blender/nodes/NOD_geometry.h @@ -49,6 +49,7 @@ void register_node_type_geo_attribute_vector_math(void); void register_node_type_geo_attribute_vector_rotate(void); void register_node_type_geo_boolean(void); void register_node_type_geo_bounding_box(void); +void register_node_type_geo_collapse(void); void register_node_type_geo_collection_info(void); void register_node_type_geo_convex_hull(void); void register_node_type_geo_curve_endpoints(void); @@ -69,12 +70,16 @@ void register_node_type_geo_curve_to_mesh(void); void register_node_type_geo_curve_to_points(void); void register_node_type_geo_curve_trim(void); void register_node_type_geo_delete_geometry(void); +void register_node_type_geo_dissolve(void); void register_node_type_geo_edge_split(void); void register_node_type_geo_input_material(void); void register_node_type_geo_is_viewport(void); void register_node_type_geo_join_geometry(void); void register_node_type_geo_material_assign(void); void register_node_type_geo_material_replace(void); +void register_node_type_geo_merge_by_distance(void); +void register_node_type_geo_mesh_extrude(void); +void register_node_type_geo_mesh_inset(void); void register_node_type_geo_mesh_primitive_circle(void); void register_node_type_geo_mesh_primitive_cone(void); void register_node_type_geo_mesh_primitive_cube(void); @@ -94,14 +99,18 @@ void register_node_type_geo_point_separate(void); void register_node_type_geo_point_translate(void); void register_node_type_geo_points_to_volume(void); void register_node_type_geo_raycast(void); +void register_node_type_geo_remesh_voxel(void); +void register_node_type_geo_remesh_blocks(void); void register_node_type_geo_sample_texture(void); void register_node_type_geo_select_by_handle_type(void); void register_node_type_geo_select_by_material(void); void register_node_type_geo_separate_components(void); +void register_node_type_geo_solidify(void); void register_node_type_geo_subdivision_surface(void); void register_node_type_geo_switch(void); void register_node_type_geo_transform(void); void register_node_type_geo_triangulate(void); +void register_node_type_geo_unsubdivide(void); void register_node_type_geo_viewer(void); void register_node_type_geo_volume_to_mesh(void); diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index 4da8648173d..2eb59b52f16 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -289,6 +289,7 @@ DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_VECTOR_MATH, def_geo_attribute_vector_m DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_VECTOR_ROTATE, def_geo_attribute_vector_rotate, "ATTRIBUTE_VECTOR_ROTATE", AttributeVectorRotate, "Attribute Vector Rotate", "") DefNode(GeometryNode, GEO_NODE_BOOLEAN, def_geo_boolean, "BOOLEAN", Boolean, "Boolean", "") DefNode(GeometryNode, GEO_NODE_BOUNDING_BOX, 0, "BOUNDING_BOX", BoundBox, "Bounding Box", "") +DefNode(GeometryNode, GEO_NODE_COLLAPSE, def_geo_collapse, "COLLAPSE", Collapse, "Collapse", "") DefNode(GeometryNode, GEO_NODE_COLLECTION_INFO, def_geo_collection_info, "COLLECTION_INFO", CollectionInfo, "Collection Info", "") DefNode(GeometryNode, GEO_NODE_CONVEX_HULL, 0, "CONVEX_HULL", ConvexHull, "Convex Hull", "") DefNode(GeometryNode, GEO_NODE_CURVE_ENDPOINTS, 0, "CURVE_ENDPOINTS", CurveEndpoints, "Curve Endpoints", "") @@ -310,12 +311,15 @@ DefNode(GeometryNode, GEO_NODE_CURVE_TO_MESH, 0, "CURVE_TO_MESH", CurveToMesh, " DefNode(GeometryNode, GEO_NODE_CURVE_TO_POINTS, def_geo_curve_to_points, "CURVE_TO_POINTS", CurveToPoints, "Curve to Points", "") DefNode(GeometryNode, GEO_NODE_CURVE_TRIM, def_geo_curve_trim, "CURVE_TRIM", CurveTrim, "Curve Trim", "") DefNode(GeometryNode, GEO_NODE_DELETE_GEOMETRY, 0, "DELETE_GEOMETRY", DeleteGeometry, "Delete Geometry", "") +DefNode(GeometryNode, GEO_NODE_DISSOLVE, def_geo_dissolve, "DISSOLVE", Dissolve, "Dissolve", "") DefNode(GeometryNode, GEO_NODE_EDGE_SPLIT, 0, "EDGE_SPLIT", EdgeSplit, "Edge Split", "") DefNode(GeometryNode, GEO_NODE_INPUT_MATERIAL, def_geo_input_material, "INPUT_MATERIAL", InputMaterial, "Material", "") DefNode(GeometryNode, GEO_NODE_IS_VIEWPORT, 0, "IS_VIEWPORT", IsViewport, "Is Viewport", "") DefNode(GeometryNode, GEO_NODE_JOIN_GEOMETRY, 0, "JOIN_GEOMETRY", JoinGeometry, "Join Geometry", "") DefNode(GeometryNode, GEO_NODE_MATERIAL_ASSIGN, 0, "MATERIAL_ASSIGN", MaterialAssign, "Material Assign", "") DefNode(GeometryNode, GEO_NODE_MATERIAL_REPLACE, 0, "MATERIAL_REPLACE", MaterialReplace, "Material Replace", "") +DefNode(GeometryNode, GEO_NODE_MESH_EXTRUDE, def_geo_mesh_extrude, "MESH_EXTRUDE", MeshExtrude, "Mesh Extrude", "") +DefNode(GeometryNode, GEO_NODE_MESH_INSET, def_geo_mesh_inset, "MESH_INSET", MeshInset, "Mesh Inset", "") DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_CIRCLE, def_geo_mesh_circle, "MESH_PRIMITIVE_CIRCLE", MeshCircle, "Mesh Circle", "") DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_CONE, def_geo_mesh_cone, "MESH_PRIMITIVE_CONE", MeshCone, "Cone", "") DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_CUBE, 0, "MESH_PRIMITIVE_CUBE", MeshCube, "Cube", "") @@ -335,12 +339,17 @@ DefNode(GeometryNode, GEO_NODE_POINT_SEPARATE, 0, "POINT_SEPARATE", PointSeparat DefNode(GeometryNode, GEO_NODE_POINT_TRANSLATE, def_geo_point_translate, "POINT_TRANSLATE", PointTranslate, "Point Translate", "") DefNode(GeometryNode, GEO_NODE_POINTS_TO_VOLUME, def_geo_points_to_volume, "POINTS_TO_VOLUME", PointsToVolume, "Points to Volume", "") DefNode(GeometryNode, GEO_NODE_RAYCAST, def_geo_raycast, "RAYCAST", Raycast, "Raycast", "") +DefNode(GeometryNode, GEO_NODE_MERGE_BY_DISTANCE, def_geo_merge_by_distance, "MERGE_BY_DISTANCE", MergeByDistance, "Merge by Distance", "") DefNode(GeometryNode, GEO_NODE_SELECT_BY_MATERIAL, 0, "SELECT_BY_MATERIAL", SelectByMaterial, "Select by Material", "") DefNode(GeometryNode, GEO_NODE_SEPARATE_COMPONENTS, 0, "SEPARATE_COMPONENTS", SeparateComponents, "Separate Components", "") +DefNode(GeometryNode, GEO_NODE_SOLIDIFY, def_geo_solidify, "SOLIDIFY", Solidify, "Solidify", "") DefNode(GeometryNode, GEO_NODE_SUBDIVISION_SURFACE, def_geo_subdivision_surface, "SUBDIVISION_SURFACE", SubdivisionSurface, "Subdivision Surface", "") DefNode(GeometryNode, GEO_NODE_SWITCH, def_geo_switch, "SWITCH", Switch, "Switch", "") +DefNode(GeometryNode, GEO_NODE_REMESH_VOXEL, 0, "REMESH_VOXEL", RemeshVoxel, "Voxel Remesh", "") +DefNode(GeometryNode, GEO_NODE_REMESH_BLOCKS, def_geo_remesh_blocks, "REMESH_BLOCKS", RemeshBlocks, "Remesh Blocks", "") DefNode(GeometryNode, GEO_NODE_TRANSFORM, 0, "TRANSFORM", Transform, "Transform", "") DefNode(GeometryNode, GEO_NODE_TRIANGULATE, def_geo_triangulate, "TRIANGULATE", Triangulate, "Triangulate", "") +DefNode(GeometryNode, GEO_NODE_UNSUBDIVIDE, 0, "UNSUBDIVIDE", Unsubdivide, "Unsubdivide", "") DefNode(GeometryNode, GEO_NODE_VIEWER, 0, "VIEWER", Viewer, "Viewer", "") DefNode(GeometryNode, GEO_NODE_VOLUME_TO_MESH, def_geo_volume_to_mesh, "VOLUME_TO_MESH", VolumeToMesh, "Volume to Mesh", "") diff --git a/source/blender/nodes/geometry/nodes/node_geo_collapse.cc b/source/blender/nodes/geometry/nodes/node_geo_collapse.cc new file mode 100644 index 00000000000..c13ab362f17 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_collapse.cc @@ -0,0 +1,122 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BKE_mesh.h" + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "bmesh.h" +#include "bmesh_tools.h" + +#include "node_geometry_util.hh" + +static bNodeSocketTemplate geo_node_collapse_in[] = { + {SOCK_GEOMETRY, N_("Geometry")}, + {SOCK_FLOAT, N_("Factor"), 1.0f, 0.0f, 0.0f, 0.0f, 0, 1.0f, PROP_FACTOR}, + {SOCK_STRING, N_("Selection")}, + {-1, ""}, +}; + +static bNodeSocketTemplate geo_node_collapse_out[] = { + {SOCK_GEOMETRY, N_("Geometry")}, + {-1, ""}, +}; + +static void geo_node_collapse_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiLayoutSetPropSep(layout, true); + uiLayoutSetPropDecorate(layout, false); + uiItemR(layout, ptr, "symmetry_axis", 0, nullptr, ICON_NONE); +} + +static void geo_node_collapse_init(bNodeTree *UNUSED(tree), bNode *node) +{ + NodeGeometryCollapse *node_storage = (NodeGeometryCollapse *)MEM_callocN( + sizeof(NodeGeometryCollapse), __func__); + + node->storage = node_storage; + node_storage->symmetry_axis = GEO_NODE_COLLAPSE_SYMMETRY_AXIS_NONE; +} + +namespace blender::nodes { + +static Mesh *collapse_mesh(const float factor, + const VArray<float> &selection, + const bool triangulate, + const int symmetry_axis, + const Mesh *mesh) +{ + const BMeshCreateParams bmesh_create_params = {0}; + const BMeshFromMeshParams bmesh_from_mesh_params = { + true, 0, 0, 0, {CD_MASK_ORIGINDEX, CD_MASK_ORIGINDEX, CD_MASK_ORIGINDEX}}; + BMesh *bm = BKE_mesh_to_bmesh_ex(mesh, &bmesh_create_params, &bmesh_from_mesh_params); + + const float symmetry_eps = 0.00002f; + Array<float> mask(selection.size()); + selection.materialize(mask); + + BM_mesh_decimate_collapse( + bm, factor, mask.data(), 1.0f, triangulate, symmetry_axis, symmetry_eps); + Mesh *result = BKE_mesh_from_bmesh_for_eval_nomain(bm, NULL, mesh); + BM_mesh_free(bm); + + result->runtime.cd_dirty_vert |= CD_MASK_NORMAL; + return result; +} + +static void geo_node_collapse_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); + const float factor = params.extract_input<float>("Factor"); + + if (factor < 1.0f && geometry_set.has_mesh()) { + MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>(); + + const float default_factor = 1.0f; + GVArray_Typed<float> selection_attribute = params.get_input_attribute<float>( + "Selection", mesh_component, ATTR_DOMAIN_POINT, default_factor); + // VArray<float> selection(selection_attribute.to); + const Mesh *input_mesh = mesh_component.get_for_read(); + + const bNode &node = params.node(); + const NodeGeometryCollapse &node_storage = *(NodeGeometryCollapse *)node.storage; + Mesh *result = collapse_mesh( + factor, selection_attribute, false, node_storage.symmetry_axis, input_mesh); + geometry_set.replace_mesh(result); + } + + params.set_output("Geometry", std::move(geometry_set)); +} +} // namespace blender::nodes + +void register_node_type_geo_collapse() +{ + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_COLLAPSE, "Collapse", NODE_CLASS_GEOMETRY, 0); + node_type_socket_templates(&ntype, geo_node_collapse_in, geo_node_collapse_out); + node_type_storage( + &ntype, "NodeGeometryCollapse", node_free_standard_storage, node_copy_standard_storage); + node_type_init(&ntype, geo_node_collapse_init); + ntype.geometry_node_execute = blender::nodes::geo_node_collapse_exec; + ntype.draw_buttons = geo_node_collapse_layout; + ntype.width = 180; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_dissolve.cc b/source/blender/nodes/geometry/nodes/node_geo_dissolve.cc new file mode 100644 index 00000000000..55e7050543f --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_dissolve.cc @@ -0,0 +1,138 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BKE_mesh.h" + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "bmesh.h" +#include "bmesh_tools.h" +#include "math.h" +#include "node_geometry_util.hh" + +static bNodeSocketTemplate geo_node_dissolve_in[] = { + {SOCK_GEOMETRY, N_("Geometry")}, + {SOCK_FLOAT, N_("Angle"), 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, M_PI, PROP_ANGLE}, + {SOCK_BOOLEAN, N_("All Boundaries"), false}, + {SOCK_STRING, N_("Selection")}, + {-1, ""}, +}; + +static bNodeSocketTemplate geo_node_dissolve_out[] = { + {SOCK_GEOMETRY, N_("Geometry")}, + {-1, ""}, +}; + +static void geo_node_dissolve_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiLayoutSetPropSep(layout, true); + uiLayoutSetPropDecorate(layout, false); + uiItemR(layout, ptr, "selection_type", 0, nullptr, ICON_NONE); +} + +static void geo_node_dissolve_init(bNodeTree *UNUSED(tree), bNode *node) +{ + NodeGeometryDissolve *node_storage = (NodeGeometryDissolve *)MEM_callocN( + sizeof(NodeGeometryDissolve), __func__); + + node->storage = node_storage; + node_storage->selection_type = GEO_NODE_DISSOLVE_DELIMITTER_UNSELECTED; +} + +namespace blender::nodes { +static Mesh *dissolve_mesh(const float angle, + const bool all_boundaries, + const int delimiter, + const Span<bool> selection, + const Mesh *mesh) +{ + const BMeshCreateParams bmesh_create_params = {0}; + const BMeshFromMeshParams bmesh_from_mesh_params = { + true, 0, 0, 0, {CD_MASK_ORIGINDEX, CD_MASK_ORIGINDEX, CD_MASK_ORIGINDEX}}; + BMesh *bm = BKE_mesh_to_bmesh_ex(mesh, &bmesh_create_params, &bmesh_from_mesh_params); + if (delimiter & BMO_DELIM_FACE_SELECTION) { + BM_tag_faces(bm, selection.data()); + } + else { + BM_select_edges(bm, selection.data()); + } + + BM_mesh_decimate_dissolve(bm, angle, all_boundaries, (BMO_Delimit)delimiter); + + Mesh *result = BKE_mesh_from_bmesh_for_eval_nomain(bm, NULL, mesh); + BM_mesh_free(bm); + result->runtime.cd_dirty_vert |= CD_MASK_NORMAL; + return result; +} + +static void geo_node_dissolve_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); + const float angle = params.extract_input<float>("Angle"); + + if (angle > 0.0f && geometry_set.has_mesh()) { + const MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>(); + const Mesh *input_mesh = mesh_component.get_for_read(); + + const bool all_boundaries = params.extract_input<bool>("All Boundaries"); + const bNode &node = params.node(); + const NodeGeometryDissolve &node_storage = *(NodeGeometryDissolve *)node.storage; + + bool default_selection = true; + AttributeDomain selection_domain = ATTR_DOMAIN_FACE; + BMO_Delimit delimiter = BMO_DELIM_FACE_SELECTION; + + if (node_storage.selection_type == GEO_NODE_DISSOLVE_DELIMITTER_UNSELECTED) { + selection_domain = ATTR_DOMAIN_EDGE; + delimiter = BMO_DELIM_EDGE_SELECTION_INVSE; + default_selection = true; + } + else if (node_storage.selection_type == GEO_NODE_DISSOLVE_DELIMITTER_LIMIT) { + selection_domain = ATTR_DOMAIN_EDGE; + delimiter = BMO_DELIM_EDGE_SELECTION; + default_selection = false; + }; + + GVArray_Typed<bool> selection_attribute = params.get_input_attribute<bool>( + "Selection", mesh_component, selection_domain, default_selection); + VArray_Span<bool> selection{selection_attribute}; + + Mesh *result = dissolve_mesh(angle, all_boundaries, delimiter, selection, input_mesh); + geometry_set.replace_mesh(result); + } + + params.set_output("Geometry", std::move(geometry_set)); +} +} // namespace blender::nodes + +void register_node_type_geo_dissolve() +{ + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_DISSOLVE, "Dissolve", NODE_CLASS_GEOMETRY, 0); + node_type_socket_templates(&ntype, geo_node_dissolve_in, geo_node_dissolve_out); + node_type_storage( + &ntype, "NodeGeometryDissolve", node_free_standard_storage, node_copy_standard_storage); + node_type_init(&ntype, geo_node_dissolve_init); + ntype.geometry_node_execute = blender::nodes::geo_node_dissolve_exec; + ntype.draw_buttons = geo_node_dissolve_layout; + ntype.width = 165; + nodeRegisterType(&ntype); +}
\ No newline at end of file diff --git a/source/blender/nodes/geometry/nodes/node_geo_merge_by_distance.cc b/source/blender/nodes/geometry/nodes/node_geo_merge_by_distance.cc new file mode 100644 index 00000000000..500471637b3 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_merge_by_distance.cc @@ -0,0 +1,116 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BKE_pointcloud.h" + +#include "BLI_float3.hh" +#include "BLI_span.hh" + +#include "DNA_pointcloud_types.h" + +#include "GEO_mesh_merge_by_distance.h" +#include "GEO_pointcloud_merge_by_distance.h" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "node_geometry_util.hh" + +using blender::Array; +using blender::float3; +using blender::Span; +using blender::Vector; + +static bNodeSocketTemplate geo_node_merge_by_distance_in[] = { + {SOCK_GEOMETRY, N_("Geometry")}, + {SOCK_FLOAT, N_("Distance"), 0.0f, 0, 0, 0, 0, 10000.0f, PROP_DISTANCE}, + {SOCK_STRING, N_("Selection")}, + {-1, ""}, +}; + +static bNodeSocketTemplate geo_node_merge_by_distance_out[] = { + {SOCK_GEOMETRY, N_("Geometry")}, + {-1, ""}, +}; + +static void geo_node_merge_by_distance_layout(uiLayout *layout, + bContext *UNUSED(C), + PointerRNA *ptr) +{ + uiItemR(layout, ptr, "merge_mode", 0, "", ICON_NONE); +} + +static void geo_merge_by_distance_init(bNodeTree *UNUSED(ntree), bNode *node) +{ + node->custom1 = WELD_MODE_ALL; +} + +namespace blender::nodes { +static void geo_node_merge_by_distance_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); + geometry_set = geometry_set_realize_instances(geometry_set); + + const char weld_mode = params.node().custom1; + const float distance = params.extract_input<float>("Distance"); + + if (geometry_set.has_mesh()) { + MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>(); + const Mesh *input_mesh = mesh_component.get_for_read(); + + const bool default_selection = true; + GVArray_Typed<bool> selection_attribute = params.get_input_attribute<bool>( + "Selection", mesh_component, ATTR_DOMAIN_POINT, default_selection); + VArray_Span<bool> selection{selection_attribute}; + + Mesh *result = GEO_mesh_merge_by_distance(input_mesh, selection.data(), distance, weld_mode); + if (result != input_mesh) { + geometry_set.replace_mesh(result); + } + } + + if (geometry_set.has_pointcloud()) { + PointCloudComponent &pointcloud_component = + geometry_set.get_component_for_write<PointCloudComponent>(); + const PointCloud *pointcloud = pointcloud_component.get_for_read(); + const bool default_selection = true; + GVArray_Typed<bool> selection_attribute = params.get_input_attribute<bool>( + "Selection", pointcloud_component, ATTR_DOMAIN_POINT, default_selection); + VArray_Span<bool> selection{selection_attribute}; + pointcloud_component.replace(merge_by_distance_pointcloud(*pointcloud, distance, selection)); + } + + if (geometry_set.has_volume()) { + params.error_message_add(NodeWarningType::Warning, + TIP_("This Node does not operate on volumes")); + } + + params.set_output("Geometry", std::move(geometry_set)); +} +} // namespace blender::nodes + +void register_node_type_geo_merge_by_distance() +{ + static bNodeType ntype; + geo_node_type_base( + &ntype, GEO_NODE_MERGE_BY_DISTANCE, "Merge By Distance", NODE_CLASS_GEOMETRY, 0); + node_type_socket_templates( + &ntype, geo_node_merge_by_distance_in, geo_node_merge_by_distance_out); + node_type_init(&ntype, geo_merge_by_distance_init); + ntype.geometry_node_execute = blender::nodes::geo_node_merge_by_distance_exec; + ntype.draw_buttons = geo_node_merge_by_distance_layout; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_extrude.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_extrude.cc new file mode 100644 index 00000000000..40169228f23 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_extrude.cc @@ -0,0 +1,253 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" + +#include "BKE_mesh.h" +#include "BKE_node.h" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "bmesh.h" + +#include "node_geometry_util.hh" + +static bNodeSocketTemplate geo_node_mesh_extrude_in[] = { + {SOCK_GEOMETRY, N_("Geometry")}, + {SOCK_VECTOR, N_("Offset"), 0.0f, 0.0f, 1.0f, 0.0f, -FLT_MAX, FLT_MAX, PROP_TRANSLATION}, + {SOCK_STRING, N_("Selection")}, + {SOCK_STRING, N_("Out Selection")}, + + {-1, ""}, +}; + +static bNodeSocketTemplate geo_node_mesh_extrude_out[] = { + {SOCK_GEOMETRY, N_("Geometry")}, + {-1, ""}, +}; + +static void geo_node_mesh_extrude_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "extrude_mode", 0, "", ICON_NONE); +} + +static void geo_node_mesh_extrude_init(bNodeTree *UNUSED(ntree), bNode *node) +{ + node->custom1 = 0; +} + +namespace blender::nodes { + +static void SetOutputSelection(const std::string &selection_out_attribute_name, + BMesh *bm, + Mesh *result) +{ + MeshComponent component; + component.replace(result, GeometryOwnershipType::Editable); + + if (!selection_out_attribute_name.empty()) { + bke::OutputAttribute_Typed<bool> attribute = component.attribute_try_get_for_output_only<bool>( + selection_out_attribute_name, ATTR_DOMAIN_POINT); + BM_get_selected_vertices(bm, attribute.as_span().data()); + attribute.save(); + } +} + +static Mesh *extrude_vertices(const Mesh *mesh, + const Span<bool> selection, + const float3 offset, + const std::string selection_out_attribute_name) +{ + const BMeshCreateParams bmesh_create_params = {true}; + const BMeshFromMeshParams bmesh_from_mesh_params = { + true, 0, 0, 0, {CD_MASK_ORIGINDEX, CD_MASK_ORIGINDEX, CD_MASK_ORIGINDEX}}; + + BMesh *bm = BKE_mesh_to_bmesh_ex(mesh, &bmesh_create_params, &bmesh_from_mesh_params); + + BM_select_vertices(bm, selection.data()); + + BMOperator extrude_op; + BMO_op_initf(bm, &extrude_op, 0, "extrude_vert_indiv verts=%hv", BM_ELEM_SELECT); + BMO_op_exec(bm, &extrude_op); + + float _offset[3] = {offset.x, offset.y, offset.z}; + BMOperator move_op; + + BM_mesh_elem_hflag_disable_all(bm, BM_VERT, BM_ELEM_SELECT, false); + BMO_slot_buffer_hflag_enable( + bm, extrude_op.slots_out, "verts.out", BM_VERT, BM_ELEM_SELECT, false); + + BMO_op_initf(bm, &move_op, 0, "translate vec=%v verts=%hv", _offset, BM_ELEM_SELECT); + + BMO_op_exec(bm, &move_op); + BMO_op_finish(bm, &move_op); + BMO_op_finish(bm, &extrude_op); + + CustomData_MeshMasks cd_mask_extra = {CD_MASK_ORIGINDEX, CD_MASK_ORIGINDEX, CD_MASK_ORIGINDEX}; + Mesh *result = BKE_mesh_from_bmesh_for_eval_nomain(bm, &cd_mask_extra, mesh); + + SetOutputSelection(selection_out_attribute_name, bm, result); + + BM_mesh_free(bm); + BKE_mesh_normals_tag_dirty(result); + + return result; +} + +static Mesh *extrude_edges(const Mesh *mesh, + const Span<bool> selection, + const float3 offset, + const std::string selection_out_attribute_name) +{ + const BMeshCreateParams bmesh_create_params = {true}; + const BMeshFromMeshParams bmesh_from_mesh_params = { + true, 0, 0, 0, {CD_MASK_ORIGINDEX, CD_MASK_ORIGINDEX, CD_MASK_ORIGINDEX}}; + + BMesh *bm = BKE_mesh_to_bmesh_ex(mesh, &bmesh_create_params, &bmesh_from_mesh_params); + + BM_select_edges(bm, selection.data()); + + BMOperator extrude_op; + BMO_op_initf(bm, + &extrude_op, + 0, + "extrude_edge_only edges=%he use_select_history=%b", + BM_ELEM_SELECT, + true); + BMO_op_exec(bm, &extrude_op); + + float _offset[3] = {offset.x, offset.y, offset.z}; + BMOperator move_op; + + BM_mesh_elem_hflag_disable_all(bm, BM_VERT, BM_ELEM_SELECT, false); + BMO_slot_buffer_hflag_enable( + bm, extrude_op.slots_out, "geom.out", BM_VERT, BM_ELEM_SELECT, false); + + BMO_op_initf(bm, &move_op, 0, "translate vec=%v verts=%hv", _offset, BM_ELEM_SELECT); + + BMO_op_exec(bm, &move_op); + BMO_op_finish(bm, &move_op); + BMO_op_finish(bm, &extrude_op); + + CustomData_MeshMasks cd_mask_extra = {CD_MASK_ORIGINDEX, CD_MASK_ORIGINDEX, CD_MASK_ORIGINDEX}; + Mesh *result = BKE_mesh_from_bmesh_for_eval_nomain(bm, &cd_mask_extra, mesh); + + SetOutputSelection(selection_out_attribute_name, bm, result); + + BM_mesh_free(bm); + BKE_mesh_normals_tag_dirty(result); + + return result; +} + +static Mesh *extrude_faces(const Mesh *mesh, + const Span<bool> selection, + const float3 offset, + std::string selection_out_attribute_name) +{ + const BMeshCreateParams bmesh_create_params = {true}; + const BMeshFromMeshParams bmesh_from_mesh_params = { + true, 0, 0, 0, {CD_MASK_ORIGINDEX, CD_MASK_ORIGINDEX, CD_MASK_ORIGINDEX}}; + + BMesh *bm = BKE_mesh_to_bmesh_ex(mesh, &bmesh_create_params, &bmesh_from_mesh_params); + + BM_select_faces(bm, selection.data()); + + BMOperator extrude_op; + BMO_op_initf(bm, &extrude_op, 0, "extrude_face_region geom=%hf", BM_ELEM_SELECT); + BMO_op_exec(bm, &extrude_op); + + float o[3] = {offset.x, offset.y, offset.z}; + BMOperator move_op; + + BM_mesh_elem_hflag_disable_all(bm, BM_VERT, BM_ELEM_SELECT, false); + BMO_slot_buffer_hflag_enable( + bm, extrude_op.slots_out, "geom.out", BM_VERT, BM_ELEM_SELECT, false); + + BMO_op_initf(bm, &move_op, 0, "translate vec=%v verts=%hv", o, BM_ELEM_SELECT); + + BMO_op_exec(bm, &move_op); + BMO_op_finish(bm, &move_op); + BMO_op_finish(bm, &extrude_op); + + CustomData_MeshMasks cd_mask_extra = {CD_MASK_ORIGINDEX, CD_MASK_ORIGINDEX, CD_MASK_ORIGINDEX}; + Mesh *result = BKE_mesh_from_bmesh_for_eval_nomain(bm, &cd_mask_extra, mesh); + + SetOutputSelection(selection_out_attribute_name, bm, result); + + BM_mesh_free(bm); + BKE_mesh_normals_tag_dirty(result); + + return result; +} + +static void geo_node_mesh_extrude_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); + + geometry_set = bke::geometry_set_realize_instances(geometry_set); + MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>(); + + const float3 offset = params.extract_input<float3>("Offset"); + + if (offset.length() > 0 && mesh_component.has_mesh()) { + const Mesh *input_mesh = mesh_component.get_for_read(); + + AttributeDomain domain = ATTR_DOMAIN_POINT; + if (params.node().custom1 == 1) { + domain = ATTR_DOMAIN_EDGE; + } + else if (params.node().custom1 == 2) { + domain = ATTR_DOMAIN_FACE; + } + + const bool default_selection = true; + GVArray_Typed<bool> selection_attribute = params.get_input_attribute<bool>( + "Selection", mesh_component, domain, default_selection); + VArray_Span<bool> selection{selection_attribute}; + + std::string out_selection_attribute_name = params.get_input<std::string>("Out Selection"); + + Mesh *result; + if (params.node().custom1 == 1) { + result = extrude_edges(input_mesh, selection, offset, out_selection_attribute_name); + } + else if (params.node().custom1 == 2) { + result = extrude_faces(input_mesh, selection, offset, out_selection_attribute_name); + } + else { + result = extrude_vertices(input_mesh, selection, offset, out_selection_attribute_name); + } + geometry_set.replace_mesh(result); + } + + params.set_output("Geometry", std::move(geometry_set)); +} +} // namespace blender::nodes + +void register_node_type_geo_mesh_extrude() +{ + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_MESH_EXTRUDE, "Mesh Extrude", NODE_CLASS_GEOMETRY, 0); + node_type_socket_templates(&ntype, geo_node_mesh_extrude_in, geo_node_mesh_extrude_out); + node_type_init(&ntype, geo_node_mesh_extrude_init); + ntype.geometry_node_execute = blender::nodes::geo_node_mesh_extrude_exec; + ntype.draw_buttons = geo_node_mesh_extrude_layout; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_inset.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_inset.cc new file mode 100644 index 00000000000..4bfda0376e0 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_inset.cc @@ -0,0 +1,215 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" + +#include "BKE_mesh.h" +#include "BKE_node.h" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "bmesh.h" +#include "bmesh_tools.h" + +#include "node_geometry_util.hh" + +static bNodeSocketTemplate geo_node_mesh_inset_in[] = { + {SOCK_GEOMETRY, N_("Geometry")}, + {SOCK_STRING, N_("Distance")}, + {SOCK_FLOAT, N_("Distance"), 0.0f, 0, 0, 0, FLT_MIN, FLT_MAX, PROP_DISTANCE}, + {SOCK_STRING, N_("Inset")}, + {SOCK_FLOAT, N_("Inset"), 0.0f, 0, 0, 0, FLT_MIN, FLT_MAX, PROP_DISTANCE}, + {SOCK_BOOLEAN, N_("Individual")}, + {SOCK_STRING, N_("Selection")}, + {SOCK_STRING, N_("Top Face")}, + {SOCK_STRING, N_("Side Face")}, + {-1, ""}, +}; + +static bNodeSocketTemplate geo_node_mesh_inset_out[] = { + {SOCK_GEOMETRY, N_("Geometry")}, + {-1, ""}, +}; + +static void geo_node_mesh_inset_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiLayoutSetPropSep(layout, true); + uiLayoutSetPropDecorate(layout, false); + uiItemR(layout, ptr, "distance_mode", 0, nullptr, ICON_NONE); + uiItemR(layout, ptr, "inset_mode", 0, nullptr, ICON_NONE); +} + +static void geo_node_mesh_inset_init(bNodeTree *UNUSED(tree), bNode *node) +{ + node->custom1 = GEO_NODE_ATTRIBUTE_INPUT_FLOAT; + node->custom2 = GEO_NODE_ATTRIBUTE_INPUT_FLOAT; +} + +static void geo_node_mesh_inset_update(bNodeTree *UNUSED(ntree), bNode *node) +{ + blender::nodes::update_attribute_input_socket_availabilities( + *node, "Distance", (GeometryNodeAttributeInputMode)node->custom1, true); + blender::nodes::update_attribute_input_socket_availabilities( + *node, "Inset", (GeometryNodeAttributeInputMode)node->custom2, true); +} + +using blender::Span; + +static Mesh *mesh_inset_mesh(const Mesh *mesh, + const Span<bool> selection, + const Span<float> distance, + const Span<float> mesh_inset, + const bool mesh_inset_individual_faces, + std::string selection_top_faces_out_attribute_name, + std::string selection_side_faces_out_attribute_name) +{ + const BMeshCreateParams bmesh_create_params = {true}; + const BMeshFromMeshParams bmesh_from_mesh_params = { + true, 0, 0, 0, {CD_MASK_ORIGINDEX, CD_MASK_ORIGINDEX, CD_MASK_ORIGINDEX}}; + + BMesh *bm = BKE_mesh_to_bmesh_ex(mesh, &bmesh_create_params, &bmesh_from_mesh_params); + + BM_select_faces(bm, selection.data()); + BMOperator op; + if (mesh_inset_individual_faces) { + BMO_op_initf(bm, + &op, + 0, + "inset_individual faces=%hf use_even_offset=%b thickness=%f depth=%f " + "thickness_array=%p depth_array=%p use_attributes=%b", + BM_ELEM_SELECT, + true, + mesh_inset[0], + distance[0], + mesh_inset.data(), + distance.data(), + true); + } + else { + BMO_op_initf(bm, + &op, + 0, + "inset_region faces=%hf use_boundary=%b use_even_offset=%b thickness=%f depth=%f " + "thickness_array=%p depth_array=%p use_attributes=%b", + BM_ELEM_SELECT, + true, + true, + mesh_inset[0], + distance[0], + mesh_inset.data(), + distance.data(), + true); + } + // BM_tag_faces_from_operator_slot(bm, &op, "faces.out", BM_FACE); + BM_untag_faces_by_tag(bm, BM_ELEM_SELECT); + + BMO_op_exec(bm, &op); + BMO_op_finish(bm, &op); + + CustomData_MeshMasks cd_mask_extra = {CD_MASK_ORIGINDEX, CD_MASK_ORIGINDEX, CD_MASK_ORIGINDEX}; + + Mesh *result = BKE_mesh_from_bmesh_for_eval_nomain(bm, &cd_mask_extra, mesh); + + MeshComponent component; + component.replace(result, GeometryOwnershipType::Editable); + + if (!selection_top_faces_out_attribute_name.empty()) { + blender::bke::OutputAttribute_Typed<bool> attribute = + component.attribute_try_get_for_output_only<bool>(selection_top_faces_out_attribute_name, + ATTR_DOMAIN_FACE); + BM_get_selected_faces(bm, attribute.as_span().data()); + attribute.save(); + } + + if (!selection_side_faces_out_attribute_name.empty()) { + blender::bke::OutputAttribute_Typed<bool> attribute = + component.attribute_try_get_for_output_only<bool>(selection_side_faces_out_attribute_name, + ATTR_DOMAIN_FACE); + BM_get_tagged_faces(bm, attribute.as_span().data()); + attribute.save(); + } + + BM_mesh_free(bm); + BKE_mesh_normals_tag_dirty(result); + + return result; +} + +namespace blender::nodes { +static void geo_node_mesh_inset_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); + + geometry_set = geometry_set_realize_instances(geometry_set); + + MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>(); + if (mesh_component.has_mesh()) { + const bool default_selection = true; + GVArray_Typed<bool> selection_attribute = params.get_input_attribute<bool>( + "Selection", mesh_component, ATTR_DOMAIN_FACE, default_selection); + VArray_Span<bool> selection{selection_attribute}; + const Mesh *input_mesh = mesh_component.get_for_read(); + + AttributeDomain attribute_domain = ATTR_DOMAIN_POINT; + const bool mesh_inset_individual_faces = params.extract_input<bool>("Individual"); + + if (mesh_inset_individual_faces) { + attribute_domain = ATTR_DOMAIN_FACE; + } + + const float default_distance = 0; + GVArray_Typed<float> distance_attribute = params.get_input_attribute<float>( + "Distance", mesh_component, attribute_domain, default_distance); + VArray_Span<float> distance{distance_attribute}; + + const float default_mesh_inset = 0; + GVArray_Typed<float> mesh_inset_attribute = params.get_input_attribute<float>( + "Inset", mesh_component, attribute_domain, default_mesh_inset); + VArray_Span<float> mesh_inset{mesh_inset_attribute}; + + std::string selection_top_faces_out_attribute_name = params.get_input<std::string>("Top Face"); + std::string selection_side_faces_out_attribute_name = params.get_input<std::string>( + "Side Face"); + + Mesh *result = mesh_inset_mesh(input_mesh, + selection, + distance, + mesh_inset, + mesh_inset_individual_faces, + selection_top_faces_out_attribute_name, + selection_side_faces_out_attribute_name); + + geometry_set.replace_mesh(result); + } + + params.set_output("Geometry", std::move(geometry_set)); +} +} // namespace blender::nodes + +void register_node_type_geo_mesh_inset() +{ + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_MESH_INSET, "Mesh Inset", NODE_CLASS_GEOMETRY, 0); + node_type_socket_templates(&ntype, geo_node_mesh_inset_in, geo_node_mesh_inset_out); + node_type_init(&ntype, geo_node_mesh_inset_init); + node_type_update(&ntype, geo_node_mesh_inset_update); + ntype.draw_buttons = geo_node_mesh_inset_layout; + ntype.geometry_node_execute = blender::nodes::geo_node_mesh_inset_exec; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_remesh_blocks.cc b/source/blender/nodes/geometry/nodes/node_geo_remesh_blocks.cc new file mode 100644 index 00000000000..25ae8ab3e9e --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_remesh_blocks.cc @@ -0,0 +1,90 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BKE_mesh.h" + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_modifier_types.h" + +#include "GEO_mesh_remesh_blocks.h" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "node_geometry_util.hh" + +static bNodeSocketTemplate geo_node_remesh_blocks_in[] = { + {SOCK_GEOMETRY, N_("Geometry")}, + {SOCK_INT, N_("Depth"), 4, 0, 0, 0, 2, 64}, + {SOCK_FLOAT, N_("Scale"), 0.9f, 0, 0, 0, 0.0f, 0.99f}, + {SOCK_FLOAT, N_("Threshold"), 1.0f, 0, 0, 0, 0.01f, FLT_MAX}, + {-1, ""}, +}; + +static bNodeSocketTemplate geo_node_remesh_blocks_out[] = { + {SOCK_GEOMETRY, N_("Geometry")}, + {-1, ""}, +}; + +static void geo_node_remesh_blocks_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "remesh_blocks_mode", 0, "", ICON_NONE); +} + +static void geo_remesh_blocks_init(bNodeTree *UNUSED(ntree), bNode *node) +{ + node->custom1 = 0; +} + +namespace blender::nodes { +static void geo_node_remesh_blocks_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); + const char flag = 0; + const char mode = params.node().custom1; + const int hermite_num = 1; + const int depth = params.extract_input<int>("Depth"); + const float scale = min_ff(params.extract_input<float>("Scale"), 0.99f); + const float threshold = params.extract_input<float>("Threshold"); + + if (geometry_set.has_mesh()) { + const Mesh *input_mesh = geometry_set.get_mesh_for_read(); + + Mesh *output_mesh = GEO_mesh_remesh_blocks( + input_mesh, flag, mode, threshold, hermite_num, scale, depth); + + BKE_mesh_copy_parameters_for_eval(output_mesh, input_mesh); + BKE_mesh_calc_edges(output_mesh, true, false); + BKE_mesh_normals_tag_dirty(output_mesh); + + geometry_set.replace_mesh(output_mesh); + } + params.set_output("Geometry", std::move(geometry_set)); +} +} // namespace blender::nodes + +void register_node_type_geo_remesh_blocks() +{ + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_REMESH_BLOCKS, "Remesh Blocks", NODE_CLASS_GEOMETRY, 0); + node_type_socket_templates(&ntype, geo_node_remesh_blocks_in, geo_node_remesh_blocks_out); + node_type_init(&ntype, geo_remesh_blocks_init); + ntype.geometry_node_execute = blender::nodes::geo_node_remesh_blocks_exec; + ntype.draw_buttons = geo_node_remesh_blocks_layout; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_remesh_voxel.cc b/source/blender/nodes/geometry/nodes/node_geo_remesh_voxel.cc new file mode 100644 index 00000000000..a9f5719d5f6 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_remesh_voxel.cc @@ -0,0 +1,60 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "UI_interface.h" + +#include "BKE_mesh_remesh_voxel.h" + +#include "node_geometry_util.hh" + +static bNodeSocketTemplate geo_node_remesh_voxel_in[] = { + {SOCK_GEOMETRY, N_("Geometry")}, + {SOCK_FLOAT, N_("Voxel Size"), 1.0f, 0, 0, 0, 0.01f, FLT_MAX}, + {SOCK_FLOAT, N_("Adaptivity"), 0.0f, 0, 0, 0, 0.0f, 1.0f}, + {-1, ""}, +}; + +static bNodeSocketTemplate geo_node_remesh_voxel_out[] = { + {SOCK_GEOMETRY, N_("Geometry")}, + {-1, ""}, +}; + +namespace blender::nodes { +static void geo_node_remesh_voxel_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); + const float voxel_size = params.extract_input<float>("Voxel Size"); + const float adaptivity = params.extract_input<float>("Adaptivity"); + + if (geometry_set.has_mesh()) { + const Mesh *input_mesh = geometry_set.get_mesh_for_read(); + + Mesh *output_mesh = BKE_mesh_remesh_voxel(input_mesh, voxel_size, adaptivity, 0.0f); + geometry_set.replace_mesh(output_mesh); + } + params.set_output("Geometry", std::move(geometry_set)); +} +} // namespace blender::nodes + +void register_node_type_geo_remesh_voxel() +{ + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_REMESH_VOXEL, "Voxel Remesh", NODE_CLASS_GEOMETRY, 0); + node_type_socket_templates(&ntype, geo_node_remesh_voxel_in, geo_node_remesh_voxel_out); + ntype.geometry_node_execute = blender::nodes::geo_node_remesh_voxel_exec; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_solidify.cc b/source/blender/nodes/geometry/nodes/node_geo_solidify.cc new file mode 100644 index 00000000000..197aa0a09c1 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_solidify.cc @@ -0,0 +1,187 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BKE_node.h" + +#include "DNA_mesh_types.h" +#include "DNA_modifier_types.h" +#include "DNA_node_types.h" + +#include "GEO_solidifiy.h" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "node_geometry_util.hh" + +static bNodeSocketTemplate geo_node_solidify_in[] = { + {SOCK_GEOMETRY, N_("Geometry")}, + {SOCK_STRING, N_("Thickness")}, + {SOCK_FLOAT, N_("Thickness"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX, PROP_DISTANCE}, + {SOCK_FLOAT, N_("Clamp"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 2.0f, PROP_FACTOR}, + {SOCK_FLOAT, N_("Offset"), -1.0f, 0.0f, 0.0f, 0.0f, -1.0f, 1.0f, PROP_FACTOR}, + {SOCK_BOOLEAN, N_("Fill"), true}, + {SOCK_BOOLEAN, N_("Rim"), true}, + {SOCK_STRING, N_("Fill Faces")}, + {SOCK_STRING, N_("Rim Faces")}, + {-1, ""}, +}; + +static bNodeSocketTemplate geo_node_solidify_out[] = { + {SOCK_GEOMETRY, N_("Geometry")}, + {-1, ""}, +}; + +namespace blender::nodes { + +static void geo_node_solidify_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiLayoutSetPropSep(layout, true); + uiLayoutSetPropDecorate(layout, false); + uiItemR(layout, ptr, "thickness_mode", 0, nullptr, ICON_NONE); + uiItemR(layout, ptr, "nonmanifold_offset_mode", 0, nullptr, ICON_NONE); + uiItemR(layout, ptr, "nonmanifold_boundary_mode", 0, nullptr, ICON_NONE); +} + +static void geo_node_solidify_init(bNodeTree *UNUSED(tree), bNode *node) +{ + NodeGeometrySolidify *node_storage = (NodeGeometrySolidify *)MEM_callocN( + sizeof(NodeGeometrySolidify), __func__); + + node->storage = node_storage; + node_storage->thickness_mode = GEO_NODE_ATTRIBUTE_INPUT_FLOAT; +} + +static void geo_node_solidify_update(bNodeTree *UNUSED(ntree), bNode *node) +{ + const NodeGeometrySolidify *node_storage = (NodeGeometrySolidify *)node->storage; + + update_attribute_input_socket_availabilities( + *node, "Thickness", (GeometryNodeAttributeInputMode)node_storage->thickness_mode, true); +} + +static void geo_node_solidify_exec(GeoNodeExecParams params) +{ + const bNode &node = params.node(); + NodeGeometrySolidify &node_storage = *(NodeGeometrySolidify *)node.storage; + const Object *self_object = params.self_object(); + + GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); + bool add_fill = params.extract_input<bool>("Fill"); + bool add_rim = params.extract_input<bool>("Rim"); + + char flag = 0; + + if (add_fill) { + flag |= MOD_SOLIDIFY_SHELL; + } + + if (add_rim) { + flag |= MOD_SOLIDIFY_RIM; + } + + float offset = params.extract_input<float>("Offset"); + float offset_clamp = params.extract_input<float>("Clamp"); + + geometry_set = geometry_set_realize_instances(geometry_set); + + if (geometry_set.has<MeshComponent>()) { + MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>(); + Mesh *input_mesh = mesh_component.get_for_write(); + + const float default_thickness = 0; + GVArray_Typed<float> thickness_attribute = params.get_input_attribute<float>( + + "Thickness", mesh_component, ATTR_DOMAIN_POINT, default_thickness); + VArray_Span<float> thickness{thickness_attribute}; + + SolidifyData solidify_node_data = { + self_object, + 1, + offset, + 0.0f, + offset_clamp, + node_storage.nonmanifold_offset_mode, + node_storage.nonmanifold_boundary_mode, + flag, + 0.01f, + 0.0f, + thickness.data(), + }; + + bool *shell_verts = nullptr; + bool *rim_verts = nullptr; + bool *shell_faces = nullptr; + bool *rim_faces = nullptr; + + Mesh *output_mesh = solidify_nonmanifold( + &solidify_node_data, input_mesh, &shell_verts, &rim_verts, &shell_faces, &rim_faces); + + if (output_mesh != input_mesh) { + geometry_set.replace_mesh(output_mesh); + } + + const AttributeDomain result_face_domain = ATTR_DOMAIN_FACE; + + const std::string shell_faces_attribute_name = params.get_input<std::string>("Fill Faces"); + const std::string rim_faces_attribute_name = params.get_input<std::string>("Rim Faces"); + + if (solidify_node_data.flag & MOD_SOLIDIFY_SHELL) { + if (!shell_faces_attribute_name.empty()) { + OutputAttribute_Typed<bool> shell_faces_attribute = + mesh_component.attribute_try_get_for_output_only<bool>(shell_faces_attribute_name, + result_face_domain); + Span<bool> shell_faces_span(shell_faces, shell_faces_attribute->size()); + shell_faces_attribute->set_all(shell_faces_span); + shell_faces_attribute.save(); + } + } + + if (solidify_node_data.flag & MOD_SOLIDIFY_RIM) { + if (!rim_faces_attribute_name.empty()) { + OutputAttribute_Typed<bool> rim_faces_attribute = + mesh_component.attribute_try_get_for_output_only<bool>(rim_faces_attribute_name, + result_face_domain); + Span<bool> rim_faces_span(rim_faces, rim_faces_attribute->size()); + rim_faces_attribute->set_all(rim_faces_span); + rim_faces_attribute.save(); + } + } + + MEM_freeN(shell_verts); + MEM_freeN(rim_verts); + MEM_freeN(shell_faces); + MEM_freeN(rim_faces); + } + params.set_output("Geometry", geometry_set); +} + +} // namespace blender::nodes + +void register_node_type_geo_solidify() +{ + static bNodeType ntype; + geo_node_type_base(&ntype, GEO_NODE_SOLIDIFY, "Solidify", NODE_CLASS_GEOMETRY, 0); + node_type_socket_templates(&ntype, geo_node_solidify_in, geo_node_solidify_out); + node_type_storage( + &ntype, "NodeGeometrySolidify", node_free_standard_storage, node_copy_standard_storage); + node_type_init(&ntype, blender::nodes::geo_node_solidify_init); + node_type_size(&ntype, 172, 100, 600); + node_type_update(&ntype, blender::nodes::geo_node_solidify_update); + ntype.geometry_node_execute = blender::nodes::geo_node_solidify_exec; + ntype.draw_buttons = blender::nodes::geo_node_solidify_layout; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_unsubdivide.cc b/source/blender/nodes/geometry/nodes/node_geo_unsubdivide.cc new file mode 100644 index 00000000000..7f0fc66ff28 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_unsubdivide.cc @@ -0,0 +1,87 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BKE_mesh.h" + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" + +#include "bmesh.h" +#include "bmesh_tools.h" +#include "node_geometry_util.hh" + +static bNodeSocketTemplate geo_node_unsubdivide_in[] = { + {SOCK_GEOMETRY, N_("Geometry")}, + {SOCK_INT, N_("Iterations"), 1, 0, 0, 0, 0, 10}, + {SOCK_STRING, N_("Selection")}, + {-1, ""}, +}; + +static bNodeSocketTemplate geo_node_unsubdivide_out[] = { + {SOCK_GEOMETRY, N_("Geometry")}, + {-1, ""}, +}; + +namespace blender::nodes { + +static Mesh *unsubdivide_mesh(const int iterations, const Array<bool> &selection, const Mesh *mesh) +{ + const BMeshCreateParams bmesh_create_params = {0}; + const BMeshFromMeshParams bmesh_from_mesh_params = { + true, 0, 0, 0, {CD_MASK_ORIGINDEX, CD_MASK_ORIGINDEX, CD_MASK_ORIGINDEX}}; + BMesh *bm = BKE_mesh_to_bmesh_ex(mesh, &bmesh_create_params, &bmesh_from_mesh_params); + BM_tag_vertices(bm, selection.data()); + BM_mesh_decimate_unsubdivide_ex(bm, iterations, true); + + Mesh *result = BKE_mesh_from_bmesh_for_eval_nomain(bm, NULL, mesh); + BM_mesh_free(bm); + result->runtime.cd_dirty_vert |= CD_MASK_NORMAL; + + return result; +} + +static void geo_node_unsubdivide_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry"); + const int iterations = params.extract_input<int>("Iterations"); + if (iterations > 0 && geometry_set.has_mesh()) { + const MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>(); + const Mesh *input_mesh = mesh_component.get_for_read(); + + const bool default_selection = true; + GVArray_Typed<bool> selection_attribute = params.get_input_attribute<bool>( + "Selection", mesh_component, ATTR_DOMAIN_POINT, default_selection); + VArray_Span<bool> selection{selection_attribute}; + + Mesh *result = unsubdivide_mesh(iterations, selection, input_mesh); + if (result != input_mesh) { + geometry_set.replace_mesh(result); + } + } + params.set_output("Geometry", std::move(geometry_set)); +} +} // namespace blender::nodes + +void register_node_type_geo_unsubdivide() +{ + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_UNSUBDIVIDE, "Unsubdivide", NODE_CLASS_GEOMETRY, 0); + node_type_socket_templates(&ntype, geo_node_unsubdivide_in, geo_node_unsubdivide_out); + ntype.geometry_node_execute = blender::nodes::geo_node_unsubdivide_exec; + ntype.width = 165; + nodeRegisterType(&ntype); +} diff --git a/source/blender/python/intern/CMakeLists.txt b/source/blender/python/intern/CMakeLists.txt index 2be2105d327..193d9fccaf7 100644 --- a/source/blender/python/intern/CMakeLists.txt +++ b/source/blender/python/intern/CMakeLists.txt @@ -302,8 +302,8 @@ if(WITH_MOD_OCEANSIM) add_definitions(-DWITH_OCEANSIM) endif() -if(WITH_MOD_REMESH) - add_definitions(-DWITH_MOD_REMESH) +if(WITH_REMESH_DUALCON) + add_definitions(-DWITH_REMESH_DUALCON) endif() if(WITH_MOD_FLUID) diff --git a/source/blender/python/intern/bpy_app_build_options.c b/source/blender/python/intern/bpy_app_build_options.c index 676d1b8045f..4e58a33aa62 100644 --- a/source/blender/python/intern/bpy_app_build_options.c +++ b/source/blender/python/intern/bpy_app_build_options.c @@ -253,7 +253,7 @@ static PyObject *make_builtopts_info(void) SetObjIncref(Py_False); #endif -#ifdef WITH_MOD_REMESH +#ifdef WITH_REMESH_DUALCON SetObjIncref(Py_True); #else SetObjIncref(Py_False); |