diff options
-rw-r--r-- | release/scripts/startup/bl_ui/properties_data_mesh.py | 17 | ||||
-rw-r--r-- | release/scripts/startup/bl_ui/space_view3d_toolbar.py | 33 | ||||
-rw-r--r-- | source/blender/blenkernel/BKE_remesh.h | 46 | ||||
-rw-r--r-- | source/blender/blenkernel/CMakeLists.txt | 2 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/mesh.c | 1 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/remesh.c | 198 | ||||
-rw-r--r-- | source/blender/blenloader/intern/versioning_280.c | 5 | ||||
-rw-r--r-- | source/blender/editors/include/ED_sculpt.h | 3 | ||||
-rw-r--r-- | source/blender/editors/object/CMakeLists.txt | 1 | ||||
-rw-r--r-- | source/blender/editors/object/object_intern.h | 3 | ||||
-rw-r--r-- | source/blender/editors/object/object_ops.c | 2 | ||||
-rw-r--r-- | source/blender/editors/object/object_remesh.c | 159 | ||||
-rw-r--r-- | source/blender/editors/sculpt_paint/sculpt.c | 36 | ||||
-rw-r--r-- | source/blender/editors/sculpt_paint/sculpt_intern.h | 19 | ||||
-rw-r--r-- | source/blender/editors/sculpt_paint/sculpt_undo.c | 131 | ||||
-rw-r--r-- | source/blender/makesdna/DNA_mesh_types.h | 5 | ||||
-rw-r--r-- | source/blender/makesrna/intern/rna_mesh.c | 24 |
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( |