Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt5
-rw-r--r--build_files/cmake/config/blender_full.cmake2
-rw-r--r--build_files/cmake/config/blender_lite.cmake2
-rw-r--r--build_files/cmake/config/blender_release.cmake2
-rw-r--r--intern/CMakeLists.txt2
-rw-r--r--release/scripts/startup/nodeitems_builtins.py9
-rw-r--r--source/blender/CMakeLists.txt1
-rw-r--r--source/blender/blenkernel/BKE_node.h10
-rw-r--r--source/blender/blenkernel/intern/node.cc9
-rw-r--r--source/blender/bmesh/intern/bmesh_mesh.c240
-rw-r--r--source/blender/bmesh/intern/bmesh_mesh.h33
-rw-r--r--source/blender/bmesh/intern/bmesh_opdefines.c6
-rw-r--r--source/blender/bmesh/intern/bmesh_operator_api.h3
-rw-r--r--source/blender/bmesh/operators/bmo_inset.c53
-rw-r--r--source/blender/bmesh/tools/bmesh_decimate_dissolve.c16
-rw-r--r--source/blender/geometry/CMakeLists.txt61
-rw-r--r--source/blender/geometry/GEO_mesh_merge_by_distance.h39
-rw-r--r--source/blender/geometry/GEO_mesh_remesh_blocks.h48
-rw-r--r--source/blender/geometry/GEO_pointcloud_merge_by_distance.h33
-rw-r--r--source/blender/geometry/GEO_solidifiy.h43
-rw-r--r--source/blender/geometry/intern/mesh_merge_by_distance.c1927
-rw-r--r--source/blender/geometry/intern/pointcloud_merge_by_distance.cc120
-rw-r--r--source/blender/geometry/intern/remesh_blocks.c171
-rw-r--r--source/blender/geometry/intern/solidify_nonmanifold.c (renamed from source/blender/modifiers/intern/MOD_solidify_nonmanifold.c)315
-rw-r--r--source/blender/makesdna/DNA_defaults.h1
-rw-r--r--source/blender/makesdna/DNA_mesh_types.h6
-rw-r--r--source/blender/makesdna/DNA_modifier_defaults.h2
-rw-r--r--source/blender/makesdna/DNA_modifier_types.h14
-rw-r--r--source/blender/makesdna/DNA_node_types.h31
-rw-r--r--source/blender/makesrna/RNA_enum_types.h65
-rw-r--r--source/blender/makesrna/intern/CMakeLists.txt1
-rw-r--r--source/blender/makesrna/intern/rna_modifier.c78
-rw-r--r--source/blender/makesrna/intern/rna_nodetree.c142
-rw-r--r--source/blender/modifiers/CMakeLists.txt5
-rw-r--r--source/blender/modifiers/intern/MOD_remesh.c156
-rw-r--r--source/blender/modifiers/intern/MOD_solidify.c154
-rw-r--r--source/blender/modifiers/intern/MOD_solidify_util.h5
-rw-r--r--source/blender/modifiers/intern/MOD_weld.c1926
-rw-r--r--source/blender/nodes/CMakeLists.txt25
-rw-r--r--source/blender/nodes/NOD_geometry.h9
-rw-r--r--source/blender/nodes/NOD_static_types.h9
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_collapse.cc122
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_dissolve.cc138
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_merge_by_distance.cc116
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_extrude.cc253
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_inset.cc215
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_remesh_blocks.cc90
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_remesh_voxel.cc60
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_solidify.cc187
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_unsubdivide.cc87
-rw-r--r--source/blender/python/intern/CMakeLists.txt4
-rw-r--r--source/blender/python/intern/bpy_app_build_options.c2
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);