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--release/scripts/startup/bl_ui/properties_data_mesh.py17
-rw-r--r--release/scripts/startup/bl_ui/space_view3d_toolbar.py33
-rw-r--r--source/blender/blenkernel/BKE_remesh.h46
-rw-r--r--source/blender/blenkernel/CMakeLists.txt2
-rw-r--r--source/blender/blenkernel/intern/mesh.c1
-rw-r--r--source/blender/blenkernel/intern/remesh.c198
-rw-r--r--source/blender/blenloader/intern/versioning_280.c5
-rw-r--r--source/blender/editors/include/ED_sculpt.h3
-rw-r--r--source/blender/editors/object/CMakeLists.txt1
-rw-r--r--source/blender/editors/object/object_intern.h3
-rw-r--r--source/blender/editors/object/object_ops.c2
-rw-r--r--source/blender/editors/object/object_remesh.c159
-rw-r--r--source/blender/editors/sculpt_paint/sculpt.c36
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_intern.h19
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_undo.c131
-rw-r--r--source/blender/makesdna/DNA_mesh_types.h5
-rw-r--r--source/blender/makesrna/intern/rna_mesh.c24
17 files changed, 634 insertions, 51 deletions
diff --git a/release/scripts/startup/bl_ui/properties_data_mesh.py b/release/scripts/startup/bl_ui/properties_data_mesh.py
index 63e4d44eada..47c90199031 100644
--- a/release/scripts/startup/bl_ui/properties_data_mesh.py
+++ b/release/scripts/startup/bl_ui/properties_data_mesh.py
@@ -459,6 +459,22 @@ class DATA_PT_vertex_colors(MeshButtonsPanel, Panel):
col.operator("mesh.vertex_color_add", icon='ADD', text="")
col.operator("mesh.vertex_color_remove", icon='REMOVE', text="")
+class DATA_PT_remesh(MeshButtonsPanel, Panel):
+ bl_label = "Remesh"
+ bl_options = {'DEFAULT_CLOSED'}
+ COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
+
+ def draw(self, context):
+ layout = self.layout
+ layout.use_property_split = True
+ col = layout.column()
+
+ mesh = context.mesh
+ col.prop(mesh, "remesh_voxel_size")
+ col.prop(mesh, "remesh_smooth_normals")
+ col.prop(mesh, "remesh_preserve_paint_mask")
+ col.operator("object.voxel_remesh", text="Voxel Remesh")
+
class DATA_PT_customdata(MeshButtonsPanel, Panel):
bl_label = "Geometry Data"
@@ -512,6 +528,7 @@ classes = (
DATA_PT_normals,
DATA_PT_normals_auto_smooth,
DATA_PT_texture_space,
+ DATA_PT_remesh,
DATA_PT_customdata,
DATA_PT_custom_props_mesh,
)
diff --git a/release/scripts/startup/bl_ui/space_view3d_toolbar.py b/release/scripts/startup/bl_ui/space_view3d_toolbar.py
index cd18d857242..5246d8fa864 100644
--- a/release/scripts/startup/bl_ui/space_view3d_toolbar.py
+++ b/release/scripts/startup/bl_ui/space_view3d_toolbar.py
@@ -1139,8 +1139,38 @@ class VIEW3D_PT_sculpt_dyntopo_remesh(Panel, View3DPaintPanel):
col = flow.column()
col.operator("sculpt.detail_flood_fill")
-# TODO, move to space_view3d.py
+class VIEW3D_PT_sculpt_voxel_remesh(Panel, View3DPaintPanel):
+ bl_context = ".sculpt_mode" # dot on purpose (access from topbar)
+ bl_label = "Remesh"
+ bl_options = {'DEFAULT_CLOSED'}
+ bl_ui_units_x = 12
+ @classmethod
+ def poll(cls, context):
+ return (context.sculpt_object and context.tool_settings.sculpt)
+
+ def draw_header(self, context):
+ is_popover = self.is_popover
+ layout = self.layout
+ layout.operator(
+ "object.voxel_remesh",
+ text="",
+ emboss=is_popover,
+ )
+
+ def draw(self, context):
+ layout = self.layout
+ layout.use_property_split = True
+ layout.use_property_decorate = False
+
+ col = layout.column()
+ mesh = context.active_object.data
+ col.prop(mesh, "remesh_voxel_size")
+ col.prop(mesh, "remesh_smooth_normals")
+ col.prop(mesh, "remesh_preserve_paint_mask")
+ col.operator("object.voxel_remesh", text="Remesh")
+
+# TODO, move to space_view3d.py
class VIEW3D_PT_sculpt_options(Panel, View3DPaintPanel):
bl_context = ".sculpt_mode" # dot on purpose (access from topbar)
@@ -2152,6 +2182,7 @@ classes = (
VIEW3D_PT_sculpt_options,
VIEW3D_PT_sculpt_options_unified,
VIEW3D_PT_sculpt_options_gravity,
+ VIEW3D_PT_sculpt_voxel_remesh,
VIEW3D_PT_tools_weightpaint_symmetry,
VIEW3D_PT_tools_weightpaint_symmetry_for_topbar,
VIEW3D_PT_tools_weightpaint_options,
diff --git a/source/blender/blenkernel/BKE_remesh.h b/source/blender/blenkernel/BKE_remesh.h
new file mode 100644
index 00000000000..fd99af36bdc
--- /dev/null
+++ b/source/blender/blenkernel/BKE_remesh.h
@@ -0,0 +1,46 @@
+/*
+ * 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) 2019 by Blender Foundation
+ * All rights reserved.
+ */
+#ifndef __BKE_REMESH_H__
+#define __BKE_REMESH_H__
+
+/** \file
+ * \ingroup bke
+ */
+
+#include "DNA_mesh_types.h"
+
+#ifdef WITH_OPENVDB
+# include "openvdb_capi.h"
+#endif
+
+/* OpenVDB Voxel Remesher */
+#ifdef WITH_OPENVDB
+struct OpenVDBLevelSet *BKE_remesh_voxel_ovdb_mesh_to_level_set_create(
+ Mesh *mesh, struct OpenVDBTransform *transform);
+Mesh *BKE_remesh_voxel_ovdb_volume_to_mesh_nomain(struct OpenVDBLevelSet *level_set,
+ double isovalue,
+ double adaptivity,
+ bool relax_disoriented_triangles);
+#endif
+Mesh *BKE_remesh_voxel_to_mesh_nomain(Mesh *mesh, float voxel_size);
+
+/* Data reprojection functions */
+void BKE_remesh_reproject_paint_mask(Mesh *target, Mesh *source);
+
+#endif /* __BKE_REMESH_H__ */
diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt
index d9bd87d97b5..1f125b93b3f 100644
--- a/source/blender/blenkernel/CMakeLists.txt
+++ b/source/blender/blenkernel/CMakeLists.txt
@@ -178,6 +178,7 @@ set(SRC
intern/pbvh.c
intern/pbvh_bmesh.c
intern/pointcache.c
+ intern/remesh.c
intern/report.c
intern/rigidbody.c
intern/scene.c
@@ -317,6 +318,7 @@ set(SRC
BKE_particle.h
BKE_pbvh.h
BKE_pointcache.h
+ BKE_remesh.h
BKE_report.h
BKE_rigidbody.h
BKE_scene.h
diff --git a/source/blender/blenkernel/intern/mesh.c b/source/blender/blenkernel/intern/mesh.c
index 7e755e54eaa..4f39d34574e 100644
--- a/source/blender/blenkernel/intern/mesh.c
+++ b/source/blender/blenkernel/intern/mesh.c
@@ -536,6 +536,7 @@ void BKE_mesh_init(Mesh *me)
me->size[0] = me->size[1] = me->size[2] = 1.0;
me->smoothresh = DEG2RADF(30);
me->texflag = ME_AUTOSPACE;
+ me->remesh_voxel_size = 0.1f;
CustomData_reset(&me->vdata);
CustomData_reset(&me->edata);
diff --git a/source/blender/blenkernel/intern/remesh.c b/source/blender/blenkernel/intern/remesh.c
new file mode 100644
index 00000000000..4dd38c900c0
--- /dev/null
+++ b/source/blender/blenkernel/intern/remesh.c
@@ -0,0 +1,198 @@
+/*
+ * 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) 2019 by Blender Foundation
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup bke
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include <time.h>
+#include <float.h>
+#include <ctype.h>
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_blenlib.h"
+#include "BLI_math.h"
+#include "BLI_utildefines.h"
+
+#include "DNA_object_types.h"
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+
+#include "BKE_mesh.h"
+#include "BKE_mesh_runtime.h"
+#include "BKE_library.h"
+#include "BKE_customdata.h"
+#include "BKE_bvhutils.h"
+#include "BKE_remesh.h"
+
+#ifdef WITH_OPENVDB
+# include "openvdb_capi.h"
+#endif
+
+#ifdef WITH_OPENVDB
+struct OpenVDBLevelSet *BKE_remesh_voxel_ovdb_mesh_to_level_set_create(
+ Mesh *mesh, struct OpenVDBTransform *transform)
+{
+ BKE_mesh_runtime_looptri_recalc(mesh);
+ const MLoopTri *looptri = BKE_mesh_runtime_looptri_ensure(mesh);
+ MVertTri *verttri = MEM_callocN(sizeof(*verttri) * BKE_mesh_runtime_looptri_len(mesh),
+ "remesh_looptri");
+ BKE_mesh_runtime_verttri_from_looptri(
+ verttri, mesh->mloop, looptri, BKE_mesh_runtime_looptri_len(mesh));
+
+ unsigned int totfaces = BKE_mesh_runtime_looptri_len(mesh);
+ unsigned int totverts = mesh->totvert;
+ float *verts = (float *)MEM_malloc_arrayN(totverts * 3, sizeof(float), "remesh_input_verts");
+ unsigned int *faces = (unsigned int *)MEM_malloc_arrayN(
+ totfaces * 3, sizeof(unsigned int), "remesh_intput_faces");
+
+ for (unsigned int i = 0; i < totverts; i++) {
+ MVert *mvert = &mesh->mvert[i];
+ verts[i * 3] = mvert->co[0];
+ verts[i * 3 + 1] = mvert->co[1];
+ verts[i * 3 + 2] = mvert->co[2];
+ }
+
+ for (unsigned int i = 0; i < totfaces; i++) {
+ MVertTri *vt = &verttri[i];
+ faces[i * 3] = vt->tri[0];
+ faces[i * 3 + 1] = vt->tri[1];
+ faces[i * 3 + 2] = vt->tri[2];
+ }
+
+ struct OpenVDBLevelSet *level_set = OpenVDBLevelSet_create(false, NULL);
+ OpenVDBLevelSet_mesh_to_level_set(level_set, verts, faces, totverts, totfaces, transform);
+
+ MEM_freeN(verts);
+ MEM_freeN(faces);
+ MEM_freeN(verttri);
+
+ return level_set;
+}
+
+Mesh *BKE_remesh_voxel_ovdb_volume_to_mesh_nomain(struct OpenVDBLevelSet *level_set,
+ double isovalue,
+ double adaptivity,
+ bool relax_disoriented_triangles)
+{
+# ifdef WITH_OPENVDB
+ struct OpenVDBVolumeToMeshData output_mesh;
+ OpenVDBLevelSet_volume_to_mesh(
+ level_set, &output_mesh, isovalue, adaptivity, relax_disoriented_triangles);
+# endif
+
+ Mesh *mesh = BKE_mesh_new_nomain(
+ output_mesh.totvertices, 0, output_mesh.totquads + output_mesh.tottriangles, 0, 0);
+ int q = output_mesh.totquads;
+
+ for (int i = 0; i < output_mesh.totvertices; i++) {
+ float vco[3] = {output_mesh.vertices[i * 3],
+ output_mesh.vertices[i * 3 + 1],
+ output_mesh.vertices[i * 3 + 2]};
+ copy_v3_v3(mesh->mvert[i].co, vco);
+ }
+
+ for (int i = 0; i < output_mesh.totquads; i++) {
+ mesh->mface[i].v4 = output_mesh.quads[i * 4];
+ mesh->mface[i].v3 = output_mesh.quads[i * 4 + 1];
+ mesh->mface[i].v2 = output_mesh.quads[i * 4 + 2];
+ mesh->mface[i].v1 = output_mesh.quads[i * 4 + 3];
+ }
+
+ for (int i = 0; i < output_mesh.tottriangles; i++) {
+ mesh->mface[i + q].v4 = 0;
+ mesh->mface[i + q].v3 = output_mesh.triangles[i * 3];
+ mesh->mface[i + q].v2 = output_mesh.triangles[i * 3 + 1];
+ mesh->mface[i + q].v1 = output_mesh.triangles[i * 3 + 2];
+ }
+
+ BKE_mesh_calc_edges_tessface(mesh);
+ BKE_mesh_convert_mfaces_to_mpolys(mesh);
+ BKE_mesh_tessface_clear(mesh);
+ BKE_mesh_calc_normals(mesh);
+
+ MEM_freeN(output_mesh.quads);
+ MEM_freeN(output_mesh.vertices);
+
+ if (output_mesh.tottriangles > 0) {
+ MEM_freeN(output_mesh.triangles);
+ }
+
+ return mesh;
+}
+#endif
+
+Mesh *BKE_remesh_voxel_to_mesh_nomain(Mesh *mesh, float voxel_size)
+{
+ Mesh *new_mesh = NULL;
+#ifdef WITH_OPENVDB
+ struct OpenVDBLevelSet *level_set;
+ struct OpenVDBTransform *xform = OpenVDBTransform_create();
+ OpenVDBTransform_create_linear_transform(xform, (double)voxel_size);
+ level_set = BKE_remesh_voxel_ovdb_mesh_to_level_set_create(mesh, xform);
+ new_mesh = BKE_remesh_voxel_ovdb_volume_to_mesh_nomain(level_set, 0.0, 0.0, false);
+ OpenVDBLevelSet_free(level_set);
+ OpenVDBTransform_free(xform);
+#endif
+ return new_mesh;
+}
+
+void BKE_remesh_reproject_paint_mask(Mesh *target, Mesh *source)
+{
+ BVHTreeFromMesh bvhtree = {
+ .nearest_callback = NULL,
+ };
+ BKE_bvhtree_from_mesh_get(&bvhtree, source, BVHTREE_FROM_VERTS, 2);
+ MVert *target_verts = CustomData_get_layer(&target->vdata, CD_MVERT);
+
+ float *target_mask;
+ if (CustomData_has_layer(&target->vdata, CD_PAINT_MASK)) {
+ target_mask = CustomData_get_layer(&target->vdata, CD_PAINT_MASK);
+ }
+ else {
+ target_mask = CustomData_add_layer(
+ &target->vdata, CD_PAINT_MASK, CD_CALLOC, NULL, target->totvert);
+ }
+
+ float *source_mask;
+ if (CustomData_has_layer(&source->vdata, CD_PAINT_MASK)) {
+ source_mask = CustomData_get_layer(&source->vdata, CD_PAINT_MASK);
+ }
+ else {
+ source_mask = CustomData_add_layer(
+ &source->vdata, CD_PAINT_MASK, CD_CALLOC, NULL, source->totvert);
+ }
+
+ for (int i = 0; i < target->totvert; i++) {
+ float from_co[3];
+ BVHTreeNearest nearest;
+ nearest.index = -1;
+ nearest.dist_sq = FLT_MAX;
+ copy_v3_v3(from_co, target_verts[i].co);
+ BLI_bvhtree_find_nearest(bvhtree.tree, from_co, &nearest, bvhtree.nearest_callback, &bvhtree);
+ if (nearest.index != -1) {
+ target_mask[i] = source_mask[nearest.index];
+ }
+ }
+ free_bvhtree_from_mesh(&bvhtree);
+}
diff --git a/source/blender/blenloader/intern/versioning_280.c b/source/blender/blenloader/intern/versioning_280.c
index 52d1269dc1b..462d32d40bd 100644
--- a/source/blender/blenloader/intern/versioning_280.c
+++ b/source/blender/blenloader/intern/versioning_280.c
@@ -3752,5 +3752,10 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain)
}
}
}
+ for (Mesh *mesh = bmain->meshes.first; mesh; mesh = mesh->id.next) {
+ if (mesh->remesh_voxel_size == 0.0f) {
+ mesh->remesh_voxel_size = 0.1f;
+ }
+ }
}
}
diff --git a/source/blender/editors/include/ED_sculpt.h b/source/blender/editors/include/ED_sculpt.h
index 86e108a26c6..034e002f86a 100644
--- a/source/blender/editors/include/ED_sculpt.h
+++ b/source/blender/editors/include/ED_sculpt.h
@@ -45,4 +45,7 @@ bool ED_sculpt_mask_box_select(struct bContext *C,
/* sculpt_undo.c */
void ED_sculpt_undosys_type(struct UndoType *ut);
+void ED_sculpt_undo_geometry_begin(struct Object *ob);
+void ED_sculpt_undo_geometry_end(struct Object *ob);
+
#endif /* __ED_SCULPT_H__ */
diff --git a/source/blender/editors/object/CMakeLists.txt b/source/blender/editors/object/CMakeLists.txt
index eaef9313431..2490f88b5eb 100644
--- a/source/blender/editors/object/CMakeLists.txt
+++ b/source/blender/editors/object/CMakeLists.txt
@@ -57,6 +57,7 @@ set(SRC
object_ops.c
object_random.c
object_relations.c
+ object_remesh.c
object_select.c
object_shader_fx.c
object_shapekey.c
diff --git a/source/blender/editors/object/object_intern.h b/source/blender/editors/object/object_intern.h
index e697c25b37f..4b369c10e4d 100644
--- a/source/blender/editors/object/object_intern.h
+++ b/source/blender/editors/object/object_intern.h
@@ -279,6 +279,9 @@ void OBJECT_OT_bake(wmOperatorType *ot);
/* object_random.c */
void TRANSFORM_OT_vertex_random(struct wmOperatorType *ot);
+/* object_remesh.c */
+void OBJECT_OT_voxel_remesh(struct wmOperatorType *ot);
+
/* object_transfer_data.c */
void OBJECT_OT_data_transfer(struct wmOperatorType *ot);
void OBJECT_OT_datalayout_transfer(struct wmOperatorType *ot);
diff --git a/source/blender/editors/object/object_ops.c b/source/blender/editors/object/object_ops.c
index b653c7fa70c..38c06319450 100644
--- a/source/blender/editors/object/object_ops.c
+++ b/source/blender/editors/object/object_ops.c
@@ -257,6 +257,8 @@ void ED_operatortypes_object(void)
WM_operatortype_append(OBJECT_OT_hide_view_clear);
WM_operatortype_append(OBJECT_OT_hide_view_set);
WM_operatortype_append(OBJECT_OT_hide_collection);
+
+ WM_operatortype_append(OBJECT_OT_voxel_remesh);
}
void ED_operatormacros_object(void)
diff --git a/source/blender/editors/object/object_remesh.c b/source/blender/editors/object/object_remesh.c
new file mode 100644
index 00000000000..a7e7256757d
--- /dev/null
+++ b/source/blender/editors/object/object_remesh.c
@@ -0,0 +1,159 @@
+/*
+ * 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) 2019 by Blender Foundation
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup edobj
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include <float.h>
+#include <ctype.h>
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_blenlib.h"
+#include "BLI_math.h"
+#include "BLI_utildefines.h"
+
+#include "DNA_scene_types.h"
+#include "DNA_object_types.h"
+#include "DNA_meshdata_types.h"
+#include "DNA_mesh_types.h"
+
+#include "BKE_context.h"
+#include "BKE_global.h"
+#include "BKE_main.h"
+#include "BKE_mesh.h"
+#include "BKE_object.h"
+#include "BKE_paint.h"
+#include "BKE_report.h"
+#include "BKE_scene.h"
+#include "BKE_customdata.h"
+#include "BKE_remesh.h"
+
+#include "DEG_depsgraph.h"
+#include "DEG_depsgraph_build.h"
+
+#include "ED_mesh.h"
+#include "ED_object.h"
+#include "ED_screen.h"
+#include "ED_sculpt.h"
+#include "ED_undo.h"
+
+#include "RNA_access.h"
+#include "RNA_define.h"
+#include "RNA_enum_types.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+#include "WM_message.h"
+#include "WM_toolsystem.h"
+
+#include "object_intern.h" // own include
+
+static bool object_remesh_poll(bContext *C)
+{
+ Object *ob = CTX_data_active_object(C);
+
+ if (BKE_object_is_in_editmode(ob)) {
+ CTX_wm_operator_poll_msg_set(C, "The voxel remesher cannot run from edit mode.");
+ return false;
+ }
+
+ if (ob->mode == OB_MODE_SCULPT && ob->sculpt->bm) {
+ CTX_wm_operator_poll_msg_set(C, "The voxel remesher cannot run with dyntopo activated.");
+ }
+
+ return ED_operator_object_active_editable_mesh(C);
+}
+
+static int voxel_remesh_exec(bContext *C, wmOperator *op)
+{
+ Object *ob = CTX_data_active_object(C);
+ Main *bmain = CTX_data_main(C);
+
+ Mesh *mesh = ob->data;
+ Mesh *new_mesh;
+
+ if (mesh->remesh_voxel_size <= 0.0f) {
+ BKE_report(op->reports, RPT_ERROR, "Voxel remesher cannot run with a voxel size of 0.0.");
+ return OPERATOR_CANCELLED;
+ }
+
+ if (ob->mode == OB_MODE_SCULPT) {
+ ED_sculpt_undo_geometry_begin(ob);
+ }
+
+ new_mesh = BKE_remesh_voxel_to_mesh_nomain(mesh, mesh->remesh_voxel_size);
+
+ if (!new_mesh) {
+ return OPERATOR_CANCELLED;
+ }
+
+ Mesh *obj_mesh_copy;
+ if (mesh->flag & ME_REMESH_REPROJECT_PAINT_MASK) {
+ obj_mesh_copy = BKE_mesh_new_nomain_from_template(mesh, mesh->totvert, 0, 0, 0, 0);
+ CustomData_copy(
+ &mesh->vdata, &obj_mesh_copy->vdata, CD_MASK_MESH.vmask, CD_DUPLICATE, mesh->totvert);
+ for (int i = 0; i < mesh->totvert; i++) {
+ copy_v3_v3(obj_mesh_copy->mvert[i].co, mesh->mvert[i].co);
+ }
+ }
+
+ BKE_mesh_nomain_to_mesh(new_mesh, mesh, ob, &CD_MASK_MESH, true);
+ BKE_mesh_free(new_mesh);
+
+ if (mesh->flag & ME_REMESH_REPROJECT_PAINT_MASK) {
+ BKE_remesh_reproject_paint_mask(mesh, obj_mesh_copy);
+ BKE_mesh_free(obj_mesh_copy);
+ }
+
+ if (mesh->flag & ME_REMESH_SMOOTH_NORMALS) {
+ BKE_mesh_smooth_flag_set(ob, true);
+ }
+
+ if (ob->mode == OB_MODE_SCULPT) {
+ ED_sculpt_undo_geometry_end(ob);
+ }
+
+ BKE_mesh_batch_cache_dirty_tag(ob->data, BKE_MESH_BATCH_DIRTY_ALL);
+ DEG_relations_tag_update(bmain);
+ DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
+ WM_event_add_notifier(C, NC_GEOM | ND_DATA, ob->data);
+
+ return OPERATOR_FINISHED;
+}
+
+void OBJECT_OT_voxel_remesh(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Voxel Remesh";
+ ot->description =
+ "Calculates a new manifold mesh based on the volume of the current mesh. All data layers "
+ "will be lost";
+ ot->idname = "OBJECT_OT_voxel_remesh";
+
+ /* api callbacks */
+ ot->poll = object_remesh_poll;
+ ot->exec = voxel_remesh_exec;
+
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c
index 285e6aff7d0..efaac6e97cf 100644
--- a/source/blender/editors/sculpt_paint/sculpt.c
+++ b/source/blender/editors/sculpt_paint/sculpt.c
@@ -5695,31 +5695,19 @@ static void sculpt_dynamic_topology_disable_ex(
CustomData_free(&me->pdata, me->totpoly);
/* Copy over stored custom data */
- me->totvert = unode->bm_enter_totvert;
- me->totloop = unode->bm_enter_totloop;
- me->totpoly = unode->bm_enter_totpoly;
- me->totedge = unode->bm_enter_totedge;
+ me->totvert = unode->geom_totvert;
+ me->totloop = unode->geom_totloop;
+ me->totpoly = unode->geom_totpoly;
+ me->totedge = unode->geom_totedge;
me->totface = 0;
- CustomData_copy(&unode->bm_enter_vdata,
- &me->vdata,
- CD_MASK_MESH.vmask,
- CD_DUPLICATE,
- unode->bm_enter_totvert);
- CustomData_copy(&unode->bm_enter_edata,
- &me->edata,
- CD_MASK_MESH.emask,
- CD_DUPLICATE,
- unode->bm_enter_totedge);
- CustomData_copy(&unode->bm_enter_ldata,
- &me->ldata,
- CD_MASK_MESH.lmask,
- CD_DUPLICATE,
- unode->bm_enter_totloop);
- CustomData_copy(&unode->bm_enter_pdata,
- &me->pdata,
- CD_MASK_MESH.pmask,
- CD_DUPLICATE,
- unode->bm_enter_totpoly);
+ CustomData_copy(
+ &unode->geom_vdata, &me->vdata, CD_MASK_MESH.vmask, CD_DUPLICATE, unode->geom_totvert);
+ CustomData_copy(
+ &unode->geom_edata, &me->edata, CD_MASK_MESH.emask, CD_DUPLICATE, unode->geom_totedge);
+ CustomData_copy(
+ &unode->geom_ldata, &me->ldata, CD_MASK_MESH.lmask, CD_DUPLICATE, unode->geom_totloop);
+ CustomData_copy(
+ &unode->geom_pdata, &me->pdata, CD_MASK_MESH.pmask, CD_DUPLICATE, unode->geom_totpoly);
BKE_mesh_update_customdata_pointers(me, false);
}
diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.h b/source/blender/editors/sculpt_paint/sculpt_intern.h
index e66e1c49685..e646accf108 100644
--- a/source/blender/editors/sculpt_paint/sculpt_intern.h
+++ b/source/blender/editors/sculpt_paint/sculpt_intern.h
@@ -63,6 +63,7 @@ typedef enum {
SCULPT_UNDO_DYNTOPO_BEGIN,
SCULPT_UNDO_DYNTOPO_END,
SCULPT_UNDO_DYNTOPO_SYMMETRIZE,
+ SCULPT_UNDO_GEOMETRY,
} SculptUndoType;
typedef struct SculptUndoNode {
@@ -94,18 +95,20 @@ typedef struct SculptUndoNode {
/* bmesh */
struct BMLogEntry *bm_entry;
bool applied;
- CustomData bm_enter_vdata;
- CustomData bm_enter_edata;
- CustomData bm_enter_ldata;
- CustomData bm_enter_pdata;
- int bm_enter_totvert;
- int bm_enter_totedge;
- int bm_enter_totloop;
- int bm_enter_totpoly;
/* shape keys */
char shapeName[sizeof(((KeyBlock *)0))->name];
+ /* geometry modification operations and bmesh enter data */
+ CustomData geom_vdata;
+ CustomData geom_edata;
+ CustomData geom_ldata;
+ CustomData geom_pdata;
+ int geom_totvert;
+ int geom_totedge;
+ int geom_totloop;
+ int geom_totpoly;
+
size_t undo_size;
} SculptUndoNode;
diff --git a/source/blender/editors/sculpt_paint/sculpt_undo.c b/source/blender/editors/sculpt_paint/sculpt_undo.c
index d4c97faa0a6..3a3487227a3 100644
--- a/source/blender/editors/sculpt_paint/sculpt_undo.c
+++ b/source/blender/editors/sculpt_paint/sculpt_undo.c
@@ -44,6 +44,7 @@
#include "BKE_ccg.h"
#include "BKE_context.h"
+#include "BKE_customdata.h"
#include "BKE_multires.h"
#include "BKE_paint.h"
#include "BKE_key.h"
@@ -425,6 +426,32 @@ static void sculpt_undo_bmesh_restore_end(bContext *C,
}
}
+static void sculpt_undo_geometry_restore(SculptUndoNode *unode, Object *ob)
+{
+ Mesh *me;
+ sculpt_pbvh_clear(ob);
+ me = ob->data;
+ CustomData_free(&me->vdata, me->totvert);
+ CustomData_free(&me->edata, me->totedge);
+ CustomData_free(&me->fdata, me->totface);
+ CustomData_free(&me->ldata, me->totloop);
+ CustomData_free(&me->pdata, me->totpoly);
+ me->totvert = unode->geom_totvert;
+ me->totedge = unode->geom_totedge;
+ me->totloop = unode->geom_totloop;
+ me->totpoly = unode->geom_totpoly;
+ me->totface = 0;
+ CustomData_copy(
+ &unode->geom_vdata, &me->vdata, CD_MASK_MESH.vmask, CD_DUPLICATE, unode->geom_totvert);
+ CustomData_copy(
+ &unode->geom_edata, &me->edata, CD_MASK_MESH.emask, CD_DUPLICATE, unode->geom_totedge);
+ CustomData_copy(
+ &unode->geom_ldata, &me->ldata, CD_MASK_MESH.lmask, CD_DUPLICATE, unode->geom_totloop);
+ CustomData_copy(
+ &unode->geom_pdata, &me->pdata, CD_MASK_MESH.pmask, CD_DUPLICATE, unode->geom_totpoly);
+ BKE_mesh_update_customdata_pointers(me, false);
+}
+
/* Handle all dynamic-topology updates
*
* Returns true if this was a dynamic-topology undo step, otherwise
@@ -442,7 +469,6 @@ static int sculpt_undo_bmesh_restore(bContext *C,
case SCULPT_UNDO_DYNTOPO_END:
sculpt_undo_bmesh_restore_end(C, unode, ob, ss);
return true;
-
default:
if (ss->bm_log) {
sculpt_undo_bmesh_restore_generic(C, unode, ob, ss);
@@ -480,6 +506,24 @@ static void sculpt_undo_restore_list(bContext *C, Depsgraph *depsgraph, ListBase
DEG_id_tag_update(&ob->id, ID_RECALC_SHADING);
+ if (lb->first) {
+ unode = lb->first;
+ if (unode->type == SCULPT_UNDO_GEOMETRY) {
+ if (unode->applied) {
+ sculpt_undo_geometry_restore(unode->next, ob);
+ unode->next->applied = true;
+ unode->applied = false;
+ }
+ else {
+ sculpt_undo_geometry_restore(unode, ob);
+ unode->next->applied = false;
+ unode->applied = true;
+ }
+ BKE_sculpt_update_object_for_edit(depsgraph, ob, false, need_mask);
+ return;
+ }
+ }
+
BKE_sculpt_update_object_for_edit(depsgraph, ob, false, need_mask);
if (lb->first && sculpt_undo_bmesh_restore(C, lb->first, ob, ss)) {
@@ -487,6 +531,7 @@ static void sculpt_undo_restore_list(bContext *C, Depsgraph *depsgraph, ListBase
}
for (unode = lb->first; unode; unode = unode->next) {
+
if (!STREQ(unode->idname, ob->id.name)) {
continue;
}
@@ -530,6 +575,8 @@ static void sculpt_undo_restore_list(bContext *C, Depsgraph *depsgraph, ListBase
case SCULPT_UNDO_DYNTOPO_SYMMETRIZE:
BLI_assert(!"Dynamic topology should've already been handled");
break;
+ case SCULPT_UNDO_GEOMETRY:
+ break;
}
}
@@ -617,17 +664,17 @@ static void sculpt_undo_free_list(ListBase *lb)
BM_log_entry_drop(unode->bm_entry);
}
- if (unode->bm_enter_totvert) {
- CustomData_free(&unode->bm_enter_vdata, unode->bm_enter_totvert);
+ if (unode->geom_totvert) {
+ CustomData_free(&unode->geom_vdata, unode->geom_totvert);
}
- if (unode->bm_enter_totedge) {
- CustomData_free(&unode->bm_enter_edata, unode->bm_enter_totedge);
+ if (unode->geom_totedge) {
+ CustomData_free(&unode->geom_edata, unode->geom_totedge);
}
- if (unode->bm_enter_totloop) {
- CustomData_free(&unode->bm_enter_ldata, unode->bm_enter_totloop);
+ if (unode->geom_totloop) {
+ CustomData_free(&unode->geom_ldata, unode->geom_totloop);
}
- if (unode->bm_enter_totpoly) {
- CustomData_free(&unode->bm_enter_pdata, unode->bm_enter_totpoly);
+ if (unode->geom_totpoly) {
+ CustomData_free(&unode->geom_pdata, unode->geom_totpoly);
}
MEM_freeN(unode);
@@ -743,6 +790,7 @@ static SculptUndoNode *sculpt_undo_alloc_node(Object *ob, PBVHNode *node, Sculpt
case SCULPT_UNDO_DYNTOPO_END:
case SCULPT_UNDO_DYNTOPO_SYMMETRIZE:
BLI_assert(!"Dynamic topology should've already been handled");
+ case SCULPT_UNDO_GEOMETRY:
break;
}
@@ -824,6 +872,36 @@ static void sculpt_undo_store_mask(Object *ob, SculptUndoNode *unode)
BKE_pbvh_vertex_iter_end;
}
+static SculptUndoNode *sculpt_undo_geometry_push(Object *ob, SculptUndoType type)
+{
+ UndoSculpt *usculpt = sculpt_undo_get_nodes();
+ Mesh *me = ob->data;
+ bool applied;
+
+ SculptUndoNode *unode = usculpt->nodes.first;
+ /* Store the original mesh in the first node, modifications in the second */
+ applied = unode != NULL;
+
+ unode = MEM_callocN(sizeof(*unode), __func__);
+
+ BLI_strncpy(unode->idname, ob->id.name, sizeof(unode->idname));
+ unode->type = type;
+ unode->applied = applied;
+
+ CustomData_copy(&me->vdata, &unode->geom_vdata, CD_MASK_MESH.vmask, CD_DUPLICATE, me->totvert);
+ CustomData_copy(&me->edata, &unode->geom_edata, CD_MASK_MESH.emask, CD_DUPLICATE, me->totedge);
+ CustomData_copy(&me->ldata, &unode->geom_ldata, CD_MASK_MESH.lmask, CD_DUPLICATE, me->totloop);
+ CustomData_copy(&me->pdata, &unode->geom_pdata, CD_MASK_MESH.pmask, CD_DUPLICATE, me->totpoly);
+ unode->geom_totvert = me->totvert;
+ unode->geom_totedge = me->totedge;
+ unode->geom_totloop = me->totloop;
+ unode->geom_totpoly = me->totpoly;
+
+ BLI_addtail(&usculpt->nodes, unode);
+
+ return unode;
+}
+
static SculptUndoNode *sculpt_undo_bmesh_push(Object *ob, PBVHNode *node, SculptUndoType type)
{
UndoSculpt *usculpt = sculpt_undo_get_nodes();
@@ -852,17 +930,17 @@ static SculptUndoNode *sculpt_undo_bmesh_push(Object *ob, PBVHNode *node, Sculpt
* (converting polys to triangles) that the BMLog can't
* fully restore from */
CustomData_copy(
- &me->vdata, &unode->bm_enter_vdata, CD_MASK_MESH.vmask, CD_DUPLICATE, me->totvert);
+ &me->vdata, &unode->geom_vdata, CD_MASK_MESH.vmask, CD_DUPLICATE, me->totvert);
CustomData_copy(
- &me->edata, &unode->bm_enter_edata, CD_MASK_MESH.emask, CD_DUPLICATE, me->totedge);
+ &me->edata, &unode->geom_edata, CD_MASK_MESH.emask, CD_DUPLICATE, me->totedge);
CustomData_copy(
- &me->ldata, &unode->bm_enter_ldata, CD_MASK_MESH.lmask, CD_DUPLICATE, me->totloop);
+ &me->ldata, &unode->geom_ldata, CD_MASK_MESH.lmask, CD_DUPLICATE, me->totloop);
CustomData_copy(
- &me->pdata, &unode->bm_enter_pdata, CD_MASK_MESH.pmask, CD_DUPLICATE, me->totpoly);
- unode->bm_enter_totvert = me->totvert;
- unode->bm_enter_totedge = me->totedge;
- unode->bm_enter_totloop = me->totloop;
- unode->bm_enter_totpoly = me->totpoly;
+ &me->pdata, &unode->geom_pdata, CD_MASK_MESH.pmask, CD_DUPLICATE, me->totpoly);
+ unode->geom_totvert = me->totvert;
+ unode->geom_totedge = me->totedge;
+ unode->geom_totloop = me->totloop;
+ unode->geom_totpoly = me->totpoly;
unode->bm_entry = BM_log_entry_add(ss->bm_log);
BM_log_all_added(ss->bm, ss->bm_log);
@@ -906,6 +984,7 @@ static SculptUndoNode *sculpt_undo_bmesh_push(Object *ob, PBVHNode *node, Sculpt
case SCULPT_UNDO_DYNTOPO_BEGIN:
case SCULPT_UNDO_DYNTOPO_END:
case SCULPT_UNDO_DYNTOPO_SYMMETRIZE:
+ case SCULPT_UNDO_GEOMETRY:
break;
}
}
@@ -928,6 +1007,11 @@ SculptUndoNode *sculpt_undo_push_node(Object *ob, PBVHNode *node, SculptUndoType
BLI_thread_unlock(LOCK_CUSTOM1);
return unode;
}
+ else if (type == SCULPT_UNDO_GEOMETRY) {
+ unode = sculpt_undo_geometry_push(ob, type);
+ BLI_thread_unlock(LOCK_CUSTOM1);
+ return unode;
+ }
else if ((unode = sculpt_undo_get_node(node))) {
BLI_thread_unlock(LOCK_CUSTOM1);
return unode;
@@ -967,6 +1051,7 @@ SculptUndoNode *sculpt_undo_push_node(Object *ob, PBVHNode *node, SculptUndoType
case SCULPT_UNDO_DYNTOPO_END:
case SCULPT_UNDO_DYNTOPO_SYMMETRIZE:
BLI_assert(!"Dynamic topology should've already been handled");
+ case SCULPT_UNDO_GEOMETRY:
break;
}
@@ -1163,6 +1248,18 @@ static void sculpt_undosys_step_free(UndoStep *us_p)
sculpt_undo_free_list(&us->data.nodes);
}
+void ED_sculpt_undo_geometry_begin(struct Object *ob)
+{
+ sculpt_undo_push_begin("voxel remesh");
+ sculpt_undo_push_node(ob, NULL, SCULPT_UNDO_GEOMETRY);
+}
+
+void ED_sculpt_undo_geometry_end(struct Object *ob)
+{
+ sculpt_undo_push_node(ob, NULL, SCULPT_UNDO_GEOMETRY);
+ sculpt_undo_push_end();
+}
+
/* Export for ED_undo_sys. */
void ED_sculpt_undosys_type(UndoType *ut)
{
diff --git a/source/blender/makesdna/DNA_mesh_types.h b/source/blender/makesdna/DNA_mesh_types.h
index 8a9a69ac178..caa7c98335a 100644
--- a/source/blender/makesdna/DNA_mesh_types.h
+++ b/source/blender/makesdna/DNA_mesh_types.h
@@ -193,9 +193,10 @@ typedef struct Mesh {
short totcol;
+ float remesh_voxel_size;
+ char _pad1[4];
/** Deprecated multiresolution modeling data, only keep for loading old files. */
struct Multires *mr DNA_DEPRECATED;
- void *_pad1;
Mesh_Runtime runtime;
} Mesh;
@@ -250,6 +251,8 @@ enum {
ME_FLAG_UNUSED_8 = 1 << 8, /* cleared */
ME_DS_EXPAND = 1 << 9,
ME_SCULPT_DYNAMIC_TOPOLOGY = 1 << 10,
+ ME_REMESH_SMOOTH_NORMALS = 1 << 11,
+ ME_REMESH_REPROJECT_PAINT_MASK = 1 << 12,
};
/* me->cd_flag */
diff --git a/source/blender/makesrna/intern/rna_mesh.c b/source/blender/makesrna/intern/rna_mesh.c
index 03173bcb3da..966dc071641 100644
--- a/source/blender/makesrna/intern/rna_mesh.c
+++ b/source/blender/makesrna/intern/rna_mesh.c
@@ -2998,6 +2998,30 @@ static void rna_def_mesh(BlenderRNA *brna)
rna_def_paint_mask(brna, prop);
/* End paint mask */
+ /* Remesh */
+ prop = RNA_def_property(srna, "remesh_voxel_size", PROP_FLOAT, PROP_DISTANCE);
+ RNA_def_property_float_sdna(prop, NULL, "remesh_voxel_size");
+ RNA_def_property_float_default(prop, 0.1f);
+ RNA_def_property_range(prop, 0.00001f, FLT_MAX);
+ RNA_def_property_ui_range(prop, 0.0001f, FLT_MAX, 0.01, 4);
+ RNA_def_property_ui_text(prop,
+ "Voxel size",
+ "Size of the voxel in object space used for volume evaluation. Lower "
+ "values preserve finer details");
+ RNA_def_property_update(prop, 0, "rna_Mesh_update_draw");
+
+ prop = RNA_def_property(srna, "remesh_smooth_normals", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", ME_REMESH_SMOOTH_NORMALS);
+ RNA_def_property_ui_text(prop, "Smooth normals", "Smooth the normals of the remesher result");
+ RNA_def_property_update(prop, 0, "rna_Mesh_update_draw");
+
+ prop = RNA_def_property(srna, "remesh_preserve_paint_mask", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", ME_REMESH_REPROJECT_PAINT_MASK);
+ RNA_def_property_boolean_default(prop, false);
+ RNA_def_property_ui_text(prop, "Preserve Paint Mask", "Keep the current mask on the new mesh");
+ RNA_def_property_update(prop, 0, "rna_Mesh_update_draw");
+ /* End remesh */
+
prop = RNA_def_property(srna, "use_auto_smooth", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", ME_AUTOSMOOTH);
RNA_def_property_ui_text(