From d1fae4250e296fe370e20f1bd2f8092fcb537dbf Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Sat, 11 Feb 2023 00:53:29 -0800 Subject: [PATCH 001/279] temp-sculpt-dyntopo: Rebase on top of master --- .clang-format | 1 + intern/atomic/intern/atomic_ops_msvc.h | 30 +- .../icons/ops.armature.extrude.cursor.dat | Bin 1502 -> 0 bytes .../datafiles/icons/ops.armature.extrude.dat | Bin 1250 -> 0 bytes .../icons/ops.curve.dupli_extrude_cursor.dat | Bin 4202 -> 0 bytes source/blender/blenfont/intern/blf_font.c | 2 +- source/blender/blenkernel/BKE_attribute.h | 11 +- source/blender/blenkernel/BKE_brush.h | 24 +- source/blender/blenkernel/BKE_customdata.h | 64 +- source/blender/blenkernel/BKE_dyntopo.h | 24 + source/blender/blenkernel/BKE_mesh.h | 7 +- source/blender/blenkernel/BKE_mesh_fair.h | 1 + source/blender/blenkernel/BKE_mesh_mapping.h | 63 +- source/blender/blenkernel/BKE_mesh_mirror.h | 3 +- .../blenkernel/BKE_mesh_remesh_voxel.h | 10 + source/blender/blenkernel/BKE_mesh_types.h | 3 +- source/blender/blenkernel/BKE_multires.h | 2 + source/blender/blenkernel/BKE_object.h | 2 + source/blender/blenkernel/BKE_paint.h | 411 +- source/blender/blenkernel/BKE_pbvh.h | 720 +- source/blender/blenkernel/BKE_subdiv_ccg.h | 11 + source/blender/blenkernel/CMakeLists.txt | 90 +- .../blender/blenkernel/intern/DerivedMesh.cc | 2 +- source/blender/blenkernel/intern/anim_data.c | 2 +- source/blender/blenkernel/intern/attribute.cc | 11 +- source/blender/blenkernel/intern/blender.c | 2 + source/blender/blenkernel/intern/brush.cc | 51 + source/blender/blenkernel/intern/collection.c | 7 +- source/blender/blenkernel/intern/colortools.c | 113 +- .../blender/blenkernel/intern/crazyspace.cc | 5 +- .../blender/blenkernel/intern/curve_bevel.c | 2 +- .../blender/blenkernel/intern/customdata.cc | 1049 ++- source/blender/blenkernel/intern/dyntopo.cc | 4030 ++++++++++ .../blenkernel/intern/dyntopo_collapse.cc | 828 ++ .../blenkernel/intern/dyntopo_intern.hh | 724 ++ .../blender/blenkernel/intern/idprop_utils.c | 2 +- source/blender/blenkernel/intern/lib_id.c | 2 +- source/blender/blenkernel/intern/mesh.cc | 23 +- source/blender/blenkernel/intern/mesh_fair.cc | 303 +- .../blenkernel/intern/mesh_legacy_convert.cc | 2 +- .../blender/blenkernel/intern/mesh_mapping.cc | 338 +- source/blender/blenkernel/intern/mesh_merge.c | 12 +- .../intern/mesh_merge_customdata.cc | 5 +- .../blender/blenkernel/intern/mesh_mirror.cc | 12 +- .../blender/blenkernel/intern/mesh_remap.cc | 14 +- .../blenkernel/intern/mesh_remesh_voxel.cc | 14 +- .../blender/blenkernel/intern/mesh_tangent.cc | 2 +- .../blenkernel/intern/mesh_validate.cc | 6 +- source/blender/blenkernel/intern/multires.cc | 466 +- .../blenkernel/intern/multires_reshape.cc | 6 + .../intern/multires_reshape_apply_base.cc | 5 +- .../intern/multires_reshape_smooth.cc | 32 + .../intern/multires_reshape_util.cc | 2 + .../blenkernel/intern/multires_unsubdivide.cc | 5 +- source/blender/blenkernel/intern/object.cc | 11 + .../blenkernel/intern/object_update.cc | 2 + source/blender/blenkernel/intern/paint.cc | 1293 ++- source/blender/blenkernel/intern/pbvh.c | 5831 ++++++++++++++ source/blender/blenkernel/intern/pbvh.cc | 2624 +++++- .../blender/blenkernel/intern/pbvh_bmesh.cc | 7154 ++++++++++++----- .../blenkernel/intern/pbvh_cache_test_main.c | 31 + .../blender/blenkernel/intern/pbvh_colors.cc | 96 +- .../blenkernel/intern/pbvh_displacement.c | 266 + .../blender/blenkernel/intern/pbvh_intern.hh | 248 +- source/blender/blenkernel/intern/scene.cc | 7 +- .../blender/blenkernel/intern/subdiv_ccg.cc | 36 + .../intern/subdiv_displacement_multires.cc | 1 + .../blender/blenkernel/intern/subdiv_mesh.cc | 4 +- source/blender/blenlib/BLI_asan.h | 58 +- source/blender/blenlib/BLI_assert.h | 4 +- source/blender/blenlib/BLI_compiler_attrs.h | 17 + source/blender/blenlib/BLI_even_spline.hh | 733 ++ source/blender/blenlib/BLI_ghash.h | 88 +- source/blender/blenlib/BLI_heap_minmax.h | 118 + source/blender/blenlib/BLI_heap_minmax.hh | 103 + source/blender/blenlib/BLI_linklist_stack.h | 2 +- source/blender/blenlib/BLI_memarena.h | 4 +- source/blender/blenlib/BLI_mempool.h | 42 + source/blender/blenlib/BLI_mempool_lockfree.h | 42 + source/blender/blenlib/BLI_smallhash.h | 26 +- source/blender/blenlib/BLI_strict_flags.h | 24 +- source/blender/blenlib/BLI_utildefines.h | 8 +- source/blender/blenlib/CMakeLists.txt | 8 + source/blender/blenlib/intern/BLI_ghash.c | 39 + source/blender/blenlib/intern/BLI_memarena.c | 11 + source/blender/blenlib/intern/BLI_mempool.c | 572 +- .../blender/blenlib/intern/BLI_table_gset.c | 153 + source/blender/blenlib/intern/heap_minmax.c | 642 ++ .../blenlib/intern/lockfree_mempool.cc | 374 + source/blender/blenlib/intern/smallhash.c | 269 +- source/blender/blenlib/intern/task_pool.cc | 3 +- source/blender/blenloader/CMakeLists.txt | 1 + .../blenloader/intern/versioning_280.c | 3 +- source/blender/bmesh/CMakeLists.txt | 11 +- source/blender/bmesh/bmesh.h | 1 - source/blender/bmesh/bmesh_class.h | 96 +- source/blender/bmesh/bmesh_log.h | 4 + source/blender/bmesh/intern/bmesh_construct.c | 689 +- source/blender/bmesh/intern/bmesh_construct.h | 3 + source/blender/bmesh/intern/bmesh_core.c | 1495 +++- source/blender/bmesh/intern/bmesh_core.h | 29 +- .../blender/bmesh/intern/bmesh_data_attr.cc | 387 + source/blender/bmesh/intern/bmesh_data_attr.h | 275 + source/blender/bmesh/intern/bmesh_idmap.cc | 342 + source/blender/bmesh/intern/bmesh_idmap.h | 66 + source/blender/bmesh/intern/bmesh_inline.h | 6 +- source/blender/bmesh/intern/bmesh_interp.c | 765 +- source/blender/bmesh/intern/bmesh_interp.h | 19 +- .../blender/bmesh/intern/bmesh_iterators.cc | 1 + .../bmesh/intern/bmesh_iterators_inline.h | 3 +- source/blender/bmesh/intern/bmesh_log.c | 1033 --- source/blender/bmesh/intern/bmesh_log.cc | 1637 ++++ source/blender/bmesh/intern/bmesh_log.h | 218 - .../blender/bmesh/intern/bmesh_log_intern.h | 305 + source/blender/bmesh/intern/bmesh_marking.c | 8 +- source/blender/bmesh/intern/bmesh_mesh.cc | 977 ++- source/blender/bmesh/intern/bmesh_mesh.h | 48 +- .../bmesh/intern/bmesh_mesh_convert.cc | 606 +- .../blender/bmesh/intern/bmesh_mesh_convert.h | 21 +- .../intern/bmesh_mesh_convert_threaded.cc | 1374 ++++ .../bmesh/intern/bmesh_mesh_duplicate.c | 13 + source/blender/bmesh/intern/bmesh_mods.c | 21 +- source/blender/bmesh/intern/bmesh_mods.h | 12 +- source/blender/bmesh/intern/bmesh_opdefines.c | 23 +- .../blender/bmesh/intern/bmesh_operator_api.h | 187 +- .../bmesh/intern/bmesh_operator_api_inline.h | 1 + source/blender/bmesh/intern/bmesh_operators.c | 227 +- source/blender/bmesh/intern/bmesh_polygon.c | 30 +- source/blender/bmesh/intern/bmesh_polygon.h | 40 +- source/blender/bmesh/intern/bmesh_private.h | 2 + source/blender/bmesh/intern/bmesh_structure.c | 24 + source/blender/bmesh/intern/bmesh_structure.h | 11 + source/blender/bmesh/operators/bmo_dupe.c | 19 +- source/blender/bmesh/operators/bmo_extrude.c | 33 +- .../blender/bmesh/operators/bmo_fill_grid.c | 2 +- source/blender/bmesh/operators/bmo_inset.c | 7 + .../bmesh/operators/bmo_mesh_convert.c | 4 +- .../blender/bmesh/operators/bmo_primitive.c | 4 +- .../bmesh/operators/bmo_removedoubles.c | 24 +- .../blender/bmesh/operators/bmo_subdivide.c | 6 + .../blender/bmesh/operators/bmo_symmetrize.c | 6 + source/blender/bmesh/tests/bmesh_core_test.cc | 4 +- .../bmesh/tools/bmesh_decimate_collapse.c | 6 +- source/blender/draw/CMakeLists.txt | 3 + source/blender/draw/DRW_engine.h | 9 + source/blender/draw/DRW_pbvh.hh | 26 +- .../draw/engines/overlay/overlay_sculpt.cc | 11 +- .../shaders/overlay_sculpt_mask_frag.glsl | 34 +- .../shaders/overlay_sculpt_mask_vert.glsl | 4 +- source/blender/draw/intern/draw_cache.c | 41 + .../draw/intern/draw_cache_impl_mesh.cc | 2 +- .../intern/draw_cache_impl_subdivision.cc | 6 +- source/blender/draw/intern/draw_debug.cc | 2 + source/blender/draw/intern/draw_manager.c | 1 + .../blender/draw/intern/draw_manager_data.cc | 1 + source/blender/draw/intern/draw_pbvh.cc | 345 +- .../mesh_extractors/extract_mesh_vbo_uv.cc | 4 + source/blender/editors/curves/CMakeLists.txt | 1 + .../blender/editors/datafiles/CMakeLists.txt | 1 - .../blender/editors/geometry/CMakeLists.txt | 1 + .../editors/geometry/geometry_intern.hh | 1 + .../blender/editors/geometry/geometry_ops.cc | 1 + source/blender/editors/gpencil/CMakeLists.txt | 1 + source/blender/editors/include/ED_object.h | 6 +- .../blender/editors/interface/CMakeLists.txt | 1 + .../editors/interface/interface_handlers.cc | 4 + .../editors/interface/interface_intern.hh | 11 + source/blender/editors/mesh/CMakeLists.txt | 2 + source/blender/editors/mesh/editmesh_fair.c | 169 + .../editors/mesh/editmesh_mask_extract.cc | 94 +- .../blender/editors/mesh/editmesh_polybuild.c | 4 +- source/blender/editors/mesh/editmesh_tools.c | 124 +- source/blender/editors/mesh/editmesh_undo.cc | 4 +- source/blender/editors/mesh/editmesh_utils.c | 6 + source/blender/editors/mesh/mesh_data.cc | 62 + source/blender/editors/mesh/mesh_intern.h | 6 + source/blender/editors/mesh/mesh_ops.c | 5 + source/blender/editors/mesh/meshtools.cc | 139 + source/blender/editors/object/CMakeLists.txt | 6 + source/blender/editors/object/object_edit.c | 7 + source/blender/editors/object/object_intern.h | 1 + source/blender/editors/object/object_modes.cc | 14 +- .../blender/editors/object/object_modifier.cc | 15 +- .../blender/editors/object/object_relations.c | 104 +- .../blender/editors/object/object_remesh.cc | 11 +- .../blender/editors/object/object_vgroup.cc | 3 +- source/blender/editors/render/CMakeLists.txt | 1 + .../editors/sculpt_paint/CMakeLists.txt | 8 + .../editors/sculpt_paint/curves_sculpt_ops.cc | 2 +- .../editors/sculpt_paint/paint_cursor.cc | 130 +- .../editors/sculpt_paint/paint_hide.cc | 28 +- .../sculpt_paint/paint_image_ops_paint.cc | 2 +- .../editors/sculpt_paint/paint_image_proj.cc | 16 +- .../editors/sculpt_paint/paint_intern.h | 48 +- .../editors/sculpt_paint/paint_mask.cc | 471 +- .../editors/sculpt_paint/paint_stroke.cc | 4 +- .../editors/sculpt_paint/paint_utils.c | 149 +- .../editors/sculpt_paint/paint_vertex.cc | 68 +- source/blender/editors/sculpt_paint/sculpt.cc | 1480 +++- .../editors/sculpt_paint/sculpt_api.cc | 638 ++ .../editors/sculpt_paint/sculpt_array.c | 1106 +++ .../sculpt_paint/sculpt_automasking.cc | 68 +- .../editors/sculpt_paint/sculpt_boundary.cc | 1250 ++- .../sculpt_paint/sculpt_brush_machine.c | 0 .../sculpt_paint/sculpt_brush_types.cc | 104 +- .../editors/sculpt_paint/sculpt_cloth.cc | 14 +- .../editors/sculpt_paint/sculpt_curvature.cc | 261 + .../editors/sculpt_paint/sculpt_detail.cc | 126 +- .../sculpt_paint/sculpt_displacement.c | 1 + .../sculpt_paint/sculpt_displacement.h | 5 + .../editors/sculpt_paint/sculpt_dyntopo.cc | 1567 +++- .../editors/sculpt_paint/sculpt_expand.cc | 478 +- .../editors/sculpt_paint/sculpt_face_set.cc | 2161 ++++- .../sculpt_paint/sculpt_face_set_topology.c | 208 + .../sculpt_paint/sculpt_filter_color.cc | 6 +- .../sculpt_paint/sculpt_filter_mask.cc | 892 ++ .../sculpt_paint/sculpt_filter_mesh.cc | 52 +- .../editors/sculpt_paint/sculpt_geodesic.cc | 776 +- .../editors/sculpt_paint/sculpt_gradient.c | 263 + .../editors/sculpt_paint/sculpt_intern.hh | 1093 ++- .../sculpt_paint/sculpt_mask_expand.cc | 8 +- .../sculpt_paint/sculpt_multiplane_scrape.cc | 7 +- .../editors/sculpt_paint/sculpt_new.cc | 29 + .../editors/sculpt_paint/sculpt_new.hh | 960 +++ .../editors/sculpt_paint/sculpt_ops.cc | 788 +- .../sculpt_paint/sculpt_paint_color.cc | 163 +- .../sculpt_paint/sculpt_paint_image.cc | 26 +- .../editors/sculpt_paint/sculpt_poly_loop.c | 322 + .../editors/sculpt_paint/sculpt_pose.cc | 150 +- .../editors/sculpt_paint/sculpt_replay.c | 1235 +++ .../editors/sculpt_paint/sculpt_smooth.cc | 444 +- .../editors/sculpt_paint/sculpt_symmetrize.c | 302 + .../editors/sculpt_paint/sculpt_transform.cc | 62 +- .../editors/sculpt_paint/sculpt_undo.cc | 1368 +++- .../blender/editors/sculpt_paint/sculpt_uv.c | 1 + .../editors/space_buttons/CMakeLists.txt | 1 + .../editors/space_buttons/buttons_context.c | 7 +- .../editors/space_buttons/space_buttons.c | 16 +- source/blender/editors/space_file/fsmenu.c | 2 +- .../editors/space_image/CMakeLists.txt | 1 + .../blender/editors/space_info/CMakeLists.txt | 1 + .../blender/editors/space_info/info_stats.cc | 6 +- .../editors/space_view3d/CMakeLists.txt | 1 + .../editors/space_view3d/space_view3d.cc | 11 +- .../blender/editors/transform/CMakeLists.txt | 1 + .../transform/transform_snap_object.cc | 6 + source/blender/editors/undo/CMakeLists.txt | 1 + source/blender/editors/undo/memfile_undo.cc | 5 + source/blender/editors/util/CMakeLists.txt | 1 + source/blender/editors/util/ed_util.cc | 2 +- .../editors/uvedit/uvedit_unwrap_ops.cc | 4 +- source/blender/gpu/CMakeLists.txt | 5 +- source/blender/gpu/GPU_vertex_format.h | 2 + source/blender/gpu/intern/gpu_batch.cc | 3 + source/blender/gpu/opengl/gl_shader.cc | 11 +- .../io/alembic/exporter/abc_writer_mesh.cc | 6 +- source/blender/io/collada/collada_utils.cpp | 4 +- .../wavefront_obj/exporter/obj_export_mesh.cc | 3 +- source/blender/makesdna/DNA_brush_defaults.h | 19 + source/blender/makesdna/DNA_brush_enums.h | 101 +- source/blender/makesdna/DNA_brush_types.h | 47 +- source/blender/makesdna/DNA_color_types.h | 8 +- .../blender/makesdna/DNA_customdata_types.h | 24 +- source/blender/makesdna/DNA_mesh_types.h | 11 +- source/blender/makesdna/DNA_meshdata_types.h | 41 + .../blender/makesdna/DNA_modifier_defaults.h | 25 + source/blender/makesdna/DNA_modifier_types.h | 79 +- source/blender/makesdna/DNA_object_types.h | 7 + source/blender/makesdna/DNA_scene_defaults.h | 2 +- source/blender/makesdna/DNA_scene_types.h | 43 +- source/blender/makesdna/DNA_space_types.h | 1 + source/blender/makesdna/DNA_texture_types.h | 1 + source/blender/makesdna/DNA_userdef_types.h | 10 +- source/blender/makesdna/DNA_view3d_defaults.h | 1 + source/blender/makesdna/DNA_view3d_types.h | 12 +- source/blender/makesdna/intern/dna_defaults.c | 2 + .../intern/rna_access_compare_override.c | 1 + .../blender/makesrna/intern/rna_attribute.c | 12 +- source/blender/makesrna/intern/rna_brush.c | 457 +- source/blender/makesrna/intern/rna_internal.h | 16 + source/blender/makesrna/intern/rna_material.c | 1 + source/blender/makesrna/intern/rna_mesh.c | 26 +- source/blender/makesrna/intern/rna_modifier.c | 235 +- source/blender/makesrna/intern/rna_scene.c | 32 + .../makesrna/intern/rna_sculpt_paint.c | 126 +- source/blender/makesrna/intern/rna_space.c | 22 +- source/blender/makesrna/intern/rna_userdef.c | 10 + source/blender/makesrna/intern/rna_wm_api.c | 11 + source/blender/modifiers/CMakeLists.txt | 1 + source/blender/modifiers/intern/MOD_bevel.c | 3 +- .../blender/modifiers/intern/MOD_boolean.cc | 6 +- .../modifiers/intern/MOD_correctivesmooth.cc | 2 +- .../blender/modifiers/intern/MOD_decimate.c | 3 +- .../blender/modifiers/intern/MOD_edgesplit.c | 3 +- source/blender/modifiers/intern/MOD_skin.c | 2 +- .../modifiers/intern/MOD_triangulate.cc | 2 +- .../blender/modifiers/intern/MOD_wireframe.c | 3 +- source/blender/nodes/CMakeLists.txt | 1 + .../node_geo_mesh_primitive_ico_sphere.cc | 3 +- .../geometry/nodes/node_geo_triangulate.cc | 2 +- source/blender/nodes/texture/CMakeLists.txt | 1 + source/blender/python/bmesh/bmesh_py_types.c | 140 +- .../python/bmesh/bmesh_py_types_customdata.c | 2 + source/blender/render/RE_bake.h | 1 + source/blender/render/intern/bake.cc | 6 + source/blender/windowmanager/CMakeLists.txt | 1 + source/blender/windowmanager/WM_api.h | 1 + source/blender/windowmanager/intern/wm_draw.c | 2 + .../blender/windowmanager/intern/wm_files.cc | 43 +- .../windowmanager/intern/wm_operators.c | 1 + .../windowmanager/intern/wm_toolsystem.c | 5 +- source/creator/creator.c | 7 +- 312 files changed, 57358 insertions(+), 6629 deletions(-) delete mode 100644 release/datafiles/icons/ops.armature.extrude.cursor.dat delete mode 100644 release/datafiles/icons/ops.armature.extrude.dat delete mode 100644 release/datafiles/icons/ops.curve.dupli_extrude_cursor.dat create mode 100644 source/blender/blenkernel/BKE_dyntopo.h create mode 100644 source/blender/blenkernel/intern/dyntopo.cc create mode 100644 source/blender/blenkernel/intern/dyntopo_collapse.cc create mode 100644 source/blender/blenkernel/intern/dyntopo_intern.hh create mode 100644 source/blender/blenkernel/intern/pbvh.c create mode 100644 source/blender/blenkernel/intern/pbvh_cache_test_main.c create mode 100644 source/blender/blenkernel/intern/pbvh_displacement.c create mode 100644 source/blender/blenlib/BLI_even_spline.hh create mode 100644 source/blender/blenlib/BLI_heap_minmax.h create mode 100644 source/blender/blenlib/BLI_heap_minmax.hh create mode 100644 source/blender/blenlib/BLI_mempool_lockfree.h create mode 100644 source/blender/blenlib/intern/BLI_table_gset.c create mode 100644 source/blender/blenlib/intern/heap_minmax.c create mode 100644 source/blender/blenlib/intern/lockfree_mempool.cc create mode 100644 source/blender/bmesh/bmesh_log.h create mode 100644 source/blender/bmesh/intern/bmesh_data_attr.cc create mode 100644 source/blender/bmesh/intern/bmesh_data_attr.h create mode 100644 source/blender/bmesh/intern/bmesh_idmap.cc create mode 100644 source/blender/bmesh/intern/bmesh_idmap.h delete mode 100644 source/blender/bmesh/intern/bmesh_log.c create mode 100644 source/blender/bmesh/intern/bmesh_log.cc create mode 100644 source/blender/bmesh/intern/bmesh_log_intern.h create mode 100644 source/blender/bmesh/intern/bmesh_mesh_convert_threaded.cc create mode 100644 source/blender/editors/mesh/editmesh_fair.c create mode 100644 source/blender/editors/sculpt_paint/sculpt_api.cc create mode 100644 source/blender/editors/sculpt_paint/sculpt_array.c create mode 100644 source/blender/editors/sculpt_paint/sculpt_brush_machine.c create mode 100644 source/blender/editors/sculpt_paint/sculpt_curvature.cc create mode 100644 source/blender/editors/sculpt_paint/sculpt_displacement.c create mode 100644 source/blender/editors/sculpt_paint/sculpt_displacement.h create mode 100644 source/blender/editors/sculpt_paint/sculpt_face_set_topology.c create mode 100644 source/blender/editors/sculpt_paint/sculpt_gradient.c create mode 100644 source/blender/editors/sculpt_paint/sculpt_new.cc create mode 100644 source/blender/editors/sculpt_paint/sculpt_new.hh create mode 100644 source/blender/editors/sculpt_paint/sculpt_poly_loop.c create mode 100644 source/blender/editors/sculpt_paint/sculpt_replay.c create mode 100644 source/blender/editors/sculpt_paint/sculpt_symmetrize.c diff --git a/.clang-format b/.clang-format index 7e67aae1111..cdbd321c140 100644 --- a/.clang-format +++ b/.clang-format @@ -266,6 +266,7 @@ ForEachMacros: - SET_SLOT_PROBING_BEGIN - MAP_SLOT_PROBING_BEGIN - VECTOR_SET_SLOT_PROBING_BEGIN + - TGSET_ITER - WL_ARRAY_FOR_EACH - FOREACH_SPECTRUM_CHANNEL diff --git a/intern/atomic/intern/atomic_ops_msvc.h b/intern/atomic/intern/atomic_ops_msvc.h index e65691d3970..4d0dd1586fe 100644 --- a/intern/atomic/intern/atomic_ops_msvc.h +++ b/intern/atomic/intern/atomic_ops_msvc.h @@ -63,17 +63,17 @@ /* Unsigned */ ATOMIC_INLINE uint64_t atomic_add_and_fetch_uint64(uint64_t *p, uint64_t x) { - return InterlockedExchangeAdd64((int64_t *)p, (int64_t)x) + x; + return (uint64_t)InterlockedExchangeAdd64((int64_t *)p, (int64_t)x) + x; } ATOMIC_INLINE uint64_t atomic_sub_and_fetch_uint64(uint64_t *p, uint64_t x) { - return InterlockedExchangeAdd64((int64_t *)p, -((int64_t)x)) - x; + return (uint64_t)InterlockedExchangeAdd64((int64_t *)p, -((int64_t)x)) - x; } ATOMIC_INLINE uint64_t atomic_cas_uint64(uint64_t *v, uint64_t old, uint64_t _new) { - return InterlockedCompareExchange64((int64_t *)v, _new, old); + return (uint64_t)InterlockedCompareExchange64((int64_t *)v, _new, old); } ATOMIC_INLINE uint64_t atomic_load_uint64(const uint64_t *v) @@ -88,12 +88,12 @@ ATOMIC_INLINE void atomic_store_uint64(uint64_t *p, uint64_t v) ATOMIC_INLINE uint64_t atomic_fetch_and_add_uint64(uint64_t *p, uint64_t x) { - return InterlockedExchangeAdd64((int64_t *)p, (int64_t)x); + return (uint32_t)InterlockedExchangeAdd64((int64_t *)p, (int64_t)x); } ATOMIC_INLINE uint64_t atomic_fetch_and_sub_uint64(uint64_t *p, uint64_t x) { - return InterlockedExchangeAdd64((int64_t *)p, -((int64_t)x)); + return (uint32_t)InterlockedExchangeAdd64((int64_t *)p, -((int64_t)x)); } /* Signed */ @@ -137,17 +137,17 @@ ATOMIC_INLINE int64_t atomic_fetch_and_sub_int64(int64_t *p, int64_t x) /* Unsigned */ ATOMIC_INLINE uint32_t atomic_add_and_fetch_uint32(uint32_t *p, uint32_t x) { - return InterlockedExchangeAdd(p, x) + x; + return (uint32_t)InterlockedExchangeAdd(p, x) + x; } ATOMIC_INLINE uint32_t atomic_sub_and_fetch_uint32(uint32_t *p, uint32_t x) { - return InterlockedExchangeAdd(p, -((int32_t)x)) - x; + return (uint32_t)InterlockedExchangeAdd(p, -((int32_t)x)) - x; } ATOMIC_INLINE uint32_t atomic_cas_uint32(uint32_t *v, uint32_t old, uint32_t _new) { - return InterlockedCompareExchange((long *)v, _new, old); + return (uint32_t)InterlockedCompareExchange((long *)v, _new, old); } ATOMIC_INLINE uint32_t atomic_load_uint32(const uint32_t *v) @@ -162,17 +162,17 @@ ATOMIC_INLINE void atomic_store_uint32(uint32_t *p, uint32_t v) ATOMIC_INLINE uint32_t atomic_fetch_and_add_uint32(uint32_t *p, uint32_t x) { - return InterlockedExchangeAdd(p, x); + return (uint32_t)InterlockedExchangeAdd(p, x); } ATOMIC_INLINE uint32_t atomic_fetch_and_or_uint32(uint32_t *p, uint32_t x) { - return InterlockedOr((long *)p, x); + return (uint32_t)InterlockedOr((long *)p, x); } ATOMIC_INLINE uint32_t atomic_fetch_and_and_uint32(uint32_t *p, uint32_t x) { - return InterlockedAnd((long *)p, x); + return (uint32_t)InterlockedAnd((long *)p, x); } /* Signed */ @@ -259,9 +259,9 @@ ATOMIC_INLINE uint8_t atomic_fetch_and_or_uint8(uint8_t *p, uint8_t b) ATOMIC_INLINE int8_t atomic_fetch_and_and_int8(int8_t *p, int8_t b) { #if (LG_SIZEOF_PTR == 8 || LG_SIZEOF_INT == 8) - return InterlockedAnd8((char *)p, (char)b); + return (int8_t)InterlockedAnd8((char *)p, (char)b); #else - return _InterlockedAnd8((char *)p, (char)b); + return (int8_t)_InterlockedAnd8((char *)p, (char)b); #endif } @@ -269,9 +269,9 @@ ATOMIC_INLINE int8_t atomic_fetch_and_and_int8(int8_t *p, int8_t b) ATOMIC_INLINE int8_t atomic_fetch_and_or_int8(int8_t *p, int8_t b) { #if (LG_SIZEOF_PTR == 8 || LG_SIZEOF_INT == 8) - return InterlockedOr8((char *)p, (char)b); + return (int8_t)InterlockedOr8((char *)p, (char)b); #else - return _InterlockedOr8((char *)p, (char)b); + return (int8_t)_InterlockedOr8((char *)p, (char)b); #endif } diff --git a/release/datafiles/icons/ops.armature.extrude.cursor.dat b/release/datafiles/icons/ops.armature.extrude.cursor.dat deleted file mode 100644 index ec8f2432052d155cdedc2d4650857d7e88d87db1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1502 zcmeH`-)fss9LIf}95pr7q+aD}HyJekORQs-j*P(8-tQH1xzJh@sjVi1r zl~Xz8e~FPtu(J$x%T_JZ?|1B;?RdvM*9{g|_a5=c;C3*9Tn~Nh++GleIQN&)U>W|r zi)Zm{@i33((T_NU1dGC-4E>?+yV$|tMgwq1%WxUSGiru0b?9Cg7@g1I1lDQ$i_tZ@ zT1Pv9eAF%7YPC(nG(Kpy`n7dtS~RDHb3&bM8_3A~6kQ*G}U^1FQ?g|P7<~X44pWo?YmaI4HB-^Z?iYv*I zEP39*dh?LLoDx`NPr#Ek$$&ot2MS!#n{Ad8lfNE;L$A3Hnx3SELFm z?;)P&W%dA7QMDwM_8|f)qcY2JEXS779;8wdk%+2njjM4o==6@l-=`+WQb$BJj^|+t fJJRl39_MfMy59lAF!1mHpL(fpZMWOyueJUGq^9P# diff --git a/release/datafiles/icons/ops.armature.extrude.dat b/release/datafiles/icons/ops.armature.extrude.dat deleted file mode 100644 index 2194c5bf55674e9945e3309e4779aa334ff4cb0a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1250 zcmeH_%}&BV5P*3bpxCgX)@^F=;>A;iiwRm-EB-%G>a6?rAZ*vt6aody-F!iXV%Cee6 zh+&I)F(+hso)HQnE3&&q7Gf_i=EW?ZLpfB~LJVSfMr40j>Od7nliq5kG)zS|2Cmg| zT919x@%(8rPKNO?e)Am+p{Wy%nufcM(9Ic*Lv(;?*mV88u z*JMg#7>5yfV3A>0Rwg?Wmplg>1=Iy{R;mhN_>P zn#y^-t+bU()z)n7+R!1oK*|;{K$!I1)^tPdz&ubntvj5{zjv_dF8{sF|KVTl?i(cqc!dA} diff --git a/release/datafiles/icons/ops.curve.dupli_extrude_cursor.dat b/release/datafiles/icons/ops.curve.dupli_extrude_cursor.dat deleted file mode 100644 index 509ca7c94408599c4062d72d186a6c029510038a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4202 zcmeHKT~8Wm6!t#|1PP^7FSik8U|?Vv1`wnu#cI2XBPi%B!cHkwKBG1?^uyS%*lGxC zLFA*uP5~toTJO8zqSyKpdetA{p6PGcbaxZx%^S}9oO7P%naKe%zuG(lS62fA#Hi*b zc9S%s22yV!bSBbBjA>QGs2S0!i7^YQBQ1p0rng$iY0BoG^16K!E7O$Kj@hj?n-gEvN`S3M)!ovv+M}EeKRYQv}f5hPkNpAro5IV*ZgeoR%m8r zZgu9jsjwNa9A`YQyY$>ohg7V)MIwj^M-!cynTQo73RJoRf3rdE&0~9_cmDPq>V3gUddR zdyH<+@+9pD&aB)DP13G;%M$6G^4k2CrP<(A_*Q5tJR21JxqlZNqB^Yx92!ua>WOPxBuomh;Dl*-SRK z{rJgN?rAnx*vlWh+poMmKFlBN?mXYkJ$;#dvG;tpoPS?9KHSe8K6~@(@L6H+-F__n za1$8g4|k$liFi7Zc$7|Tr_$N&R5r8yxbn7kP|jyFPqwnTCtJHaxu=D_((C*|{-E?) zV1Hi__+Mt9@4n3L=U(iU^Tj{j7wRYFYUP;c&e_jRwp2N;AMvL=_qoXx*i!xIw9{$w zr|kk)oG7Iv>uu?0NN^ z?N;0E8e2c9)yh?#1B?aU6Hq(_W{v}9wsZ=doo2hxE^%-Z3wF^3p*2gy2>4L&JIIGp_C-oDy1Wt?XVy6VvKs&qBcZZW5^8u($TnI6&K0{=M5-DZli&(cPFzPsh;&S1R1(+mVN{HW&_Qg( zsKke{K~#(ms)p2Z)irbkQ(_|q2{wp})beqq>KZP^MTBHrsZk=sCK(|y4{4O>2r^8{ zO)~S4Nroxaazv_@sfKWcNlwZUkw$_@fmcCXN5v3}FyJ&Qu|d5U6@!XQEk{St8<^4{ z(TlM`=vIs?aj8Lqi^i3RLZdXva48`Hl^f^?sDO@Iu9hKEM5>X1Q?XubkU+K6B!eZ# z6}SS}VWBrPKfDMv28ocx6#_1xpeZ)VsOUn>d!LT4Z-$aogo=9auO>rDI_+nuh(8?6 ztU??J`x$?DA-1@&u<6ePQh+jn@LFVX!<$^#T#K$nLX0=*OHye%7Gl;P_>%q%m0sVZ zW9tv-SRl2yK}8p13o$xQZ>(=pktK$T0&hH&^fM4gg21_&41@!Wp8*xn@n@D(fH48k zf$q3BxfWez)*{{{7563S*jhBi_>#*he+E=GXy{msQBf+o1PpW>xS)GEUtS>3xP#tU3TPK}rr~2jVpKvwUhA@yP0tTnLa1!FLe|`CS(d%9IdKZ_y z?>|1zE_xTe@0W1CJnI22WZ-o19rC^2*#+bw6ta;2_U+qMfA^ok&zgb6)#+7#_n$$3 j2L2l}@Uy=6eVzV)On(Oce={&JFaYo7|Lp#^e*6Chrm2=& diff --git a/source/blender/blenfont/intern/blf_font.c b/source/blender/blenfont/intern/blf_font.c index 2c877ecb41d..a053b44dc61 100644 --- a/source/blender/blenfont/intern/blf_font.c +++ b/source/blender/blenfont/intern/blf_font.c @@ -1679,7 +1679,7 @@ bool blf_font_size(FontBLF *font, float size) font->ft_size->generic.finalizer = blf_size_finalizer; } else { - if (FT_Set_Char_Size(font->face, 0, ft_size, BLF_DPI, BLF_DPI) != FT_Err_Ok) { + if (FT_Set_Char_Size(font->face, 0, (FT_F26Dot6)ft_size, BLF_DPI, BLF_DPI) != FT_Err_Ok) { return false; } font->ft_size = font->face->size; diff --git a/source/blender/blenkernel/BKE_attribute.h b/source/blender/blenkernel/BKE_attribute.h index 7c01a9205fc..45ba439609c 100644 --- a/source/blender/blenkernel/BKE_attribute.h +++ b/source/blender/blenkernel/BKE_attribute.h @@ -53,8 +53,7 @@ typedef enum eAttrDomainMask { bool BKE_id_attributes_supported(const struct ID *id); bool BKE_attribute_allow_procedural_access(const char *attribute_name); -/** - * Create a new attribute layer. +/** Create a new attribute layer. */ struct CustomDataLayer *BKE_id_attribute_new( struct ID *id, const char *name, int type, eAttrDomain domain, struct ReportList *reports); @@ -87,7 +86,8 @@ bool BKE_id_attribute_rename(struct ID *id, int BKE_id_attributes_length(const struct ID *id, eAttrDomainMask domain_mask, - eCustomDataMask mask); + eCustomDataMask mask, + bool skip_temporary); struct CustomDataLayer *BKE_id_attributes_active_get(struct ID *id); void BKE_id_attributes_active_set(struct ID *id, const char *name); @@ -106,11 +106,11 @@ int BKE_id_attribute_to_index(const struct ID *id, eCustomDataMask layer_mask); /** - * Sets up a temporary ID with arbitrary CustomData domains. `r_id` will + * Sets up a temporary ID with arbitrary CustomData domains. r_id will * be zero initialized with ID type id_type and any non-nullptr * CustomData parameter will be copied into the appropriate struct members. * - * \param r_id: Pointer to storage sufficient for ID type-code id_type. + * \param r_id Pointer to storage sufficient for ID typecode id_type. */ void BKE_id_attribute_copy_domains_temp(short id_type, const struct CustomData *vdata, @@ -130,6 +130,7 @@ void BKE_id_attributes_default_color_set(struct ID *id, const char *name); struct CustomDataLayer *BKE_id_attributes_color_find(const struct ID *id, const char *name); bool BKE_id_attribute_calc_unique_name(struct ID *id, const char *name, char *outname); +struct CustomDataLayer *BKE_id_attributes_color_find(const struct ID *id, const char *name); const char *BKE_uv_map_vert_select_name_get(const char *uv_map_name, char *buffer); const char *BKE_uv_map_edge_select_name_get(const char *uv_map_name, char *buffer); diff --git a/source/blender/blenkernel/BKE_brush.h b/source/blender/blenkernel/BKE_brush.h index 369b7e56a6c..d0610f81151 100644 --- a/source/blender/blenkernel/BKE_brush.h +++ b/source/blender/blenkernel/BKE_brush.h @@ -23,6 +23,9 @@ struct MTex; struct Scene; struct ToolSettings; struct UnifiedPaintSettings; +struct DynTopoSettings; +struct Sculpt; +struct CurveMapping; // enum eCurveMappingPreset; @@ -168,7 +171,6 @@ void BKE_brush_weight_set(const struct Scene *scene, struct Brush *brush, float bool BKE_brush_use_locked_size(const struct Scene *scene, const struct Brush *brush); bool BKE_brush_use_alpha_pressure(const struct Brush *brush); bool BKE_brush_use_size_pressure(const struct Brush *brush); - bool BKE_brush_sculpt_has_secondary_color(const struct Brush *brush); /** @@ -185,6 +187,8 @@ void BKE_brush_scale_size(int *r_brush_size, float new_unprojected_radius, float old_unprojected_radius); +void BKE_brush_default_input_curves_set(struct Brush *brush); + /* Accessors */ #define BKE_brush_tool_get(brush, p) \ (CHECK_TYPE_ANY(brush, struct Brush *, const struct Brush *), \ @@ -192,13 +196,31 @@ void BKE_brush_scale_size(int *r_brush_size, #define BKE_brush_tool_set(brush, p, tool) \ { \ CHECK_TYPE_ANY(brush, struct Brush *); \ + char _old = *(char *)POINTER_OFFSET(brush, (p)->runtime.tool_offset); \ *(char *)POINTER_OFFSET(brush, (p)->runtime.tool_offset) = tool; \ + if ((p)->runtime.ob_mode == OB_MODE_SCULPT) { \ + if (_old != tool) { \ + BKE_brush_sculpt_reset(brush); \ + } \ + } \ } \ ((void)0) /* debugging only */ void BKE_brush_debug_print_state(struct Brush *br); +void BKE_brush_get_dyntopo(struct Brush *brush, struct Sculpt *sd, struct DynTopoSettings *out); + +bool BKE_brush_hard_edge_mode_get(const struct Scene *scene, const struct Brush *brush); +void BKE_brush_hard_edge_mode_set(struct Scene *scene, struct Brush *brush, bool val); + +float BKE_brush_fset_slide_get(const struct Scene *scene, const struct Brush *brush); +float BKE_brush_curve_strength_ex(int curve_preset, + const struct CurveMapping *curve, + float p, + const float len, + const bool invert); + #ifdef __cplusplus } #endif diff --git a/source/blender/blenkernel/BKE_customdata.h b/source/blender/blenkernel/BKE_customdata.h index 905eb43179a..62ca82f7265 100644 --- a/source/blender/blenkernel/BKE_customdata.h +++ b/source/blender/blenkernel/BKE_customdata.h @@ -126,6 +126,8 @@ bool CustomData_has_interp(const struct CustomData *data); */ bool CustomData_bmesh_has_free(const struct CustomData *data); +bool CustomData_layout_is_same(const struct CustomData *_a, const struct CustomData *_b); + /** * Checks if any of the custom-data layers is referenced. */ @@ -170,6 +172,10 @@ void CustomData_copy(const struct CustomData *source, /* BMESH_TODO, not really a public function but readfile.c needs it */ void CustomData_update_typemap(struct CustomData *data); +/* copies all customdata layers without allocating data, + * and without respect to type masks or NO_COPY/etc flags*/ +void CustomData_copy_all_layout(const struct CustomData *source, struct CustomData *dest); + /** * Same as the above, except that this will preserve existing layers, and only * add the layers that were not there yet. @@ -281,7 +287,9 @@ bool CustomData_has_layer(const struct CustomData *data, int type); * Returns the number of layers with this type. */ int CustomData_number_of_layers(const struct CustomData *data, int type); -int CustomData_number_of_layers_typemask(const struct CustomData *data, eCustomDataMask mask); +int CustomData_number_of_layers_typemask(const struct CustomData *data, + eCustomDataMask mask, + bool skip_temporary); /** * Duplicate all the layers with flag NOFREE, and remove the flag from duplicated layers. @@ -317,6 +325,16 @@ void CustomData_copy_data_named(const struct CustomData *source, int dest_index, int count); void CustomData_copy_elements(int type, void *src_data_ofs, void *dst_data_ofs, int count); + +// ignores CD_MESH_ID layer if it exists +void CustomData_bmesh_swap_data(struct CustomData *source, + struct CustomData *dest, + void *src_block, + void **dest_block); + +// simple pointer swap; will unswaps ids if a CD_MESH_ID layer exists +void CustomData_bmesh_swap_data_simple(CustomData *data, void **block1, void **block2); + void CustomData_bmesh_copy_data(const struct CustomData *source, struct CustomData *dest, void *src_block, @@ -572,6 +590,14 @@ int CustomData_name_max_length_calc(blender::StringRef name); */ void CustomData_set_layer_unique_name(struct CustomData *data, int index); +/* get unique layer name for a layer that doesn't currently exist */ +void CustomData_find_unique_layer_name(CustomData *data, + int type, + const char *name, + char *outname); + +/* try to find layer with name name; if it does not exist, + load the active layer name into outname*/ void CustomData_validate_layer_name(const struct CustomData *data, int type, const char *name, @@ -585,6 +611,22 @@ bool CustomData_verify_versions(struct CustomData *data, int index); /* BMesh specific custom-data stuff. */ +void CustomData_bmesh_update_active_layers(struct CustomData *fdata, struct CustomData *ldata); + +/** + * Update active indices for active/render/clone/stencil custom data layers + * based on indices from fdata layers + * used by do_versions in `readfile.c` when creating pdata and ldata for pre-bmesh + * meshes and needed to preserve active/render/clone/stencil flags set in pre-bmesh files. + */ +void CustomData_bmesh_do_versions_update_active_layers(struct CustomData *fdata, + struct CustomData *ldata); + +void CustomData_bmesh_init_pool_ex(CustomData *data, + int totelem, + const char htype, + const char *memtag); + void CustomData_bmesh_init_pool(struct CustomData *data, int totelem, char htype); /** @@ -743,7 +785,25 @@ void CustomData_blend_write(BlendWriter *writer, void CustomData_blend_read(struct BlendDataReader *reader, struct CustomData *data, int count); -size_t CustomData_get_elem_size(struct CustomDataLayer *layer); +void CustomData_unmark_temporary_nocopy(struct CustomData *data); +void CustomData_mark_temporary_nocopy(struct CustomData *data); + +int CustomData_get_elem_size(CustomDataLayer *layer); +void CustomData_regen_active_refs(CustomData *data); + +void CustomData_bmesh_asan_poison(const CustomData *data, void *block); +void CustomData_bmesh_asan_unpoison(const CustomData *data, void *block); +int CustomData_get_named_offset(const CustomData *data, int type, const char *name); + +void CustomData_setDefaultData(eCustomDataType type, void *block, int totelem); +size_t CustomData_getTypeSize(eCustomDataType type); +void CustomData_freeData(eCustomDataType type, void *block, int totelem); +void CustomData_interpData(eCustomDataType type, + void *block, + int tot, + const void **srcs, + const float *ws, + const float *sub_ws); #ifndef NDEBUG struct DynStr; diff --git a/source/blender/blenkernel/BKE_dyntopo.h b/source/blender/blenkernel/BKE_dyntopo.h new file mode 100644 index 00000000000..7944567a141 --- /dev/null +++ b/source/blender/blenkernel/BKE_dyntopo.h @@ -0,0 +1,24 @@ +/* + * 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 bke + * \brief Dynamic topology remeshing API + */ + +typedef struct DynTopo DynTopo; diff --git a/source/blender/blenkernel/BKE_mesh.h b/source/blender/blenkernel/BKE_mesh.h index 6b4553f7246..abc7ae12ff6 100644 --- a/source/blender/blenkernel/BKE_mesh.h +++ b/source/blender/blenkernel/BKE_mesh.h @@ -6,6 +6,10 @@ * \ingroup bke */ +#include "BKE_attribute.h" +#include "BKE_customdata.h" +#include "BKE_mesh_types.h" + #include "BLI_compiler_attrs.h" #include "BLI_compiler_compat.h" #include "BLI_utildefines.h" @@ -76,7 +80,8 @@ void BKE_mesh_tag_edges_split(struct Mesh *mesh); /* *** mesh.c *** */ -struct BMesh *BKE_mesh_to_bmesh_ex(const struct Mesh *me, +struct BMesh *BKE_mesh_to_bmesh_ex(const struct Object *ob, + const struct Mesh *me, const struct BMeshCreateParams *create_params, const struct BMeshFromMeshParams *convert_params); struct BMesh *BKE_mesh_to_bmesh(struct Mesh *me, diff --git a/source/blender/blenkernel/BKE_mesh_fair.h b/source/blender/blenkernel/BKE_mesh_fair.h index 5aeb94ceffc..9bc01ce2c13 100644 --- a/source/blender/blenkernel/BKE_mesh_fair.h +++ b/source/blender/blenkernel/BKE_mesh_fair.h @@ -21,6 +21,7 @@ extern "C" { typedef enum eMeshFairingDepth { MESH_FAIRING_DEPTH_POSITION = 1, MESH_FAIRING_DEPTH_TANGENCY = 2, + MESH_FAIRING_DEPTH_CURVATURE = 3, } eMeshFairingDepth; /* affect_vertices is used to define the fairing area. Indexed by vertex index, set to true when diff --git a/source/blender/blenkernel/BKE_mesh_mapping.h b/source/blender/blenkernel/BKE_mesh_mapping.h index 40d3b95139c..8286539a181 100644 --- a/source/blender/blenkernel/BKE_mesh_mapping.h +++ b/source/blender/blenkernel/BKE_mesh_mapping.h @@ -99,8 +99,11 @@ typedef struct MeshElemMap { int count; } MeshElemMap; -/* mapping */ - +/** + * Generates a map where the key is the vertex and the value + * is a list of polys that use that vertex as a corner. + * The lists are allocated from one memory pool. + */ UvVertMap *BKE_mesh_uv_vert_map_create(const struct MPoly *mpoly, const bool *hide_poly, const bool *select_poly, @@ -114,35 +117,40 @@ UvVertMap *BKE_mesh_uv_vert_map_create(const struct MPoly *mpoly, UvMapVert *BKE_mesh_uv_vert_map_get_vert(UvVertMap *vmap, unsigned int v); void BKE_mesh_uv_vert_map_free(UvVertMap *vmap); -/** - * Generates a map where the key is the vertex and the value - * is a list of polys that use that vertex as a corner. - * The lists are allocated from one memory pool. - */ -void BKE_mesh_vert_poly_map_create(MeshElemMap **r_map, - int **r_mem, - const struct MPoly *mpoly, - const struct MLoop *mloop, - int totvert, - int totpoly, - int totloop); /** * Generates a map where the key is the vertex and the value * is a list of loops that use that vertex as a corner. * The lists are allocated from one memory pool. */ -void BKE_mesh_vert_loop_map_create(MeshElemMap **r_map, - int **r_mem, - const struct MPoly *mpoly, - const struct MLoop *mloop, - int totvert, - int totpoly, - int totloop); +void BKE_mesh_vert_poly_map_create( + MeshElemMap **r_map, + int **r_mem, + const float (*vert_positions)[3], // only needed if sort_disk_cycles is true + const struct MEdge *medge, // only needed if sort_disk_cycles is true + const struct MPoly *mpoly, + const struct MLoop *mloop, + int totvert, + int totpoly, + int totloop, + const bool sort_disk_cycles); // put polys in sorted geometric order + /** * Generates a map where the key is the edge and the value * is a list of looptris that use that edge. * The lists are allocated from one memory pool. */ +void BKE_mesh_vert_loop_map_create( + MeshElemMap **r_map, + int **r_mem, + const float (*vert_cos)[3], // only needed if sort_disk_cycles is true + const struct MEdge *medge, // only needed if sort_disk_cycles is true + const struct MPoly *mpoly, + const struct MLoop *mloop, + int totvert, + int totpoly, + int totloop, + const bool sort_disk_cycles); // put loops in sorted geometric order + void BKE_mesh_vert_looptri_map_create(MeshElemMap **r_map, int **r_mem, int totvert, @@ -156,11 +164,14 @@ void BKE_mesh_vert_looptri_map_create(MeshElemMap **r_map, * The lists are allocated from one memory pool. */ void BKE_mesh_vert_edge_map_create( - MeshElemMap **r_map, int **r_mem, const struct MEdge *medge, int totvert, int totedge); -/** - * A version of #BKE_mesh_vert_edge_map_create that references connected vertices directly - * (not their edges). - */ + MeshElemMap **r_map, + int **r_mem, + const float (*vert_cos)[3], // only needed if sort_disk_cycles is true + const struct MEdge *medge, + int totvert, + int totedge, + bool sort_disk_cycles); // sort verts in geometric order around edges + void BKE_mesh_vert_edge_vert_map_create( MeshElemMap **r_map, int **r_mem, const struct MEdge *medge, int totvert, int totedge); /** diff --git a/source/blender/blenkernel/BKE_mesh_mirror.h b/source/blender/blenkernel/BKE_mesh_mirror.h index 582f107e4d9..d3eb42d9706 100644 --- a/source/blender/blenkernel/BKE_mesh_mirror.h +++ b/source/blender/blenkernel/BKE_mesh_mirror.h @@ -16,7 +16,8 @@ struct Mesh; struct MirrorModifierData; struct Object; -struct Mesh *BKE_mesh_mirror_bisect_on_mirror_plane_for_modifier(struct MirrorModifierData *mmd, +struct Mesh *BKE_mesh_mirror_bisect_on_mirror_plane_for_modifier(struct Object *ob, + struct MirrorModifierData *mmd, const struct Mesh *mesh, int axis, const float plane_co[3], diff --git a/source/blender/blenkernel/BKE_mesh_remesh_voxel.h b/source/blender/blenkernel/BKE_mesh_remesh_voxel.h index d9f5a75ca61..34ce8d6cebf 100644 --- a/source/blender/blenkernel/BKE_mesh_remesh_voxel.h +++ b/source/blender/blenkernel/BKE_mesh_remesh_voxel.h @@ -27,10 +27,20 @@ struct Mesh *BKE_mesh_remesh_quadriflow(const struct Mesh *mesh, void (*update_cb)(void *, float progress, int *cancel), void *update_cb_data); +struct Mesh *BKE_mesh_remesh_instant_meshes(const Mesh *input_mesh, + int target_faces, + int iterations, + void (*update_cb)(void *, float progress, int *cancel), + void *update_cb_data); + /* Data reprojection functions */ void BKE_mesh_remesh_reproject_paint_mask(struct Mesh *target, const struct Mesh *source); void BKE_remesh_reproject_vertex_paint(struct Mesh *target, const struct Mesh *source); void BKE_remesh_reproject_sculpt_face_sets(struct Mesh *target, const struct Mesh *source); +void BKE_remesh_reproject_materials(struct Mesh *target, const struct Mesh *source); +void BKE_mesh_remesh_sculpt_array_update(struct Object *ob, + struct Mesh *target, + struct Mesh *source); #ifdef __cplusplus } diff --git a/source/blender/blenkernel/BKE_mesh_types.h b/source/blender/blenkernel/BKE_mesh_types.h index 2fe8bd21b7a..9e08413bace 100644 --- a/source/blender/blenkernel/BKE_mesh_types.h +++ b/source/blender/blenkernel/BKE_mesh_types.h @@ -30,6 +30,7 @@ struct MLoopTri; struct ShrinkwrapBoundaryData; struct SubdivCCG; struct SubsurfRuntimeData; +struct Mesh; #endif @@ -85,7 +86,7 @@ struct MeshRuntime { /* Evaluated mesh for objects which do not have effective modifiers. * This mesh is used as a result of modifier stack evaluation. * Since modifier stack evaluation is threaded on object level we need some synchronization. */ - Mesh *mesh_eval = nullptr; + struct Mesh *mesh_eval = nullptr; std::mutex eval_mutex; /* A separate mutex is needed for normal calculation, because sometimes diff --git a/source/blender/blenkernel/BKE_multires.h b/source/blender/blenkernel/BKE_multires.h index 5faafbe957c..ef6845bf451 100644 --- a/source/blender/blenkernel/BKE_multires.h +++ b/source/blender/blenkernel/BKE_multires.h @@ -23,6 +23,7 @@ struct MultiresModifierData; struct Object; struct Scene; struct SubdivCCG; +struct BMesh; struct MPoly; @@ -234,6 +235,7 @@ BLI_INLINE void BKE_multires_construct_tangent_matrix(float tangent_matrix[3][3] const float dPdv[3], int corner); +void BKE_multires_bmesh_space_set(struct Object *ob, struct BMesh *bm, int mode); /* Versioning. */ /** diff --git a/source/blender/blenkernel/BKE_object.h b/source/blender/blenkernel/BKE_object.h index c77faf98b25..cfadf8f2060 100644 --- a/source/blender/blenkernel/BKE_object.h +++ b/source/blender/blenkernel/BKE_object.h @@ -10,7 +10,9 @@ #include "BLI_compiler_attrs.h" #include "BLI_sys_types.h" +#include "BKE_lib_id.h" #include "DNA_object_enums.h" +#include "DNA_userdef_types.h" #ifdef __cplusplus extern "C" { diff --git a/source/blender/blenkernel/BKE_paint.h b/source/blender/blenkernel/BKE_paint.h index 1a380addfbb..c97655f9e41 100644 --- a/source/blender/blenkernel/BKE_paint.h +++ b/source/blender/blenkernel/BKE_paint.h @@ -7,22 +7,31 @@ * \ingroup bke */ +#include "BKE_attribute.h" +#include "BKE_pbvh.h" + #include "BLI_bitmap.h" #include "BLI_compiler_compat.h" #include "BLI_utildefines.h" #include "DNA_brush_enums.h" +#include "DNA_customdata_types.h" #include "DNA_object_enums.h" #include "BKE_attribute.h" #include "BKE_pbvh.h" +#include "bmesh_log.h" + #ifdef __cplusplus extern "C" { #endif +struct SculptAttribute; +struct MSculptVert; struct BMFace; struct BMesh; +struct BMIdMap; struct BlendDataReader; struct BlendLibReader; struct BlendWriter; @@ -237,7 +246,8 @@ void BKE_paint_face_set_overlay_color_get(int face_set, int seed, uchar r_color[ bool paint_calculate_rake_rotation(struct UnifiedPaintSettings *ups, struct Brush *brush, - const float mouse_pos[2]); + const float mouse_pos[2], + const float initial_mouse_pos[2]); void paint_update_brush_rake_rotation(struct UnifiedPaintSettings *ups, struct Brush *brush, float rotation); @@ -300,6 +310,11 @@ typedef struct SculptPoseIKChain { SculptPoseIKChainSegment *segments; int tot_segments; float grab_delta_offset[3]; + float bend_mat[4][4]; + float bend_mat_inv[4][4]; + float bend_factor; + float bend_limit; + float bend_upper_limit; } SculptPoseIKChain; /* Cloth Brush */ @@ -329,6 +344,59 @@ typedef enum eSculptClothConstraintType { SCULPT_CLOTH_CONSTRAINT_PIN = 3, } eSculptClothConstraintType; +#define CLOTH_NO_POS_PTR + +typedef struct SculptClothConstraint { + signed char ctype, thread_nr; + + /* Index in #SculptClothSimulation.node_state of the node from where this constraint was + * created. This constraints will only be used by the solver if the state is active. */ + short node; + + float strength; + + /* Elements that are affected by the constraint. */ + /* Element a should always be a mesh vertex + * with the index stored in elem_index_a as + * it is \ + * always deformed. Element b could be + * another vertex of the same mesh or any + * other position \ + * (arbitrary point, position for a previous + * state). In that case, elem_index_a and \ + * elem_index_b should be the same to avoid + * affecting two different vertices when + * solving the \ + * constraints. *elem_position points to the + * position which is owned by the element. */ + + struct { + int index; +#ifndef CLOTH_NO_POS_PTR + float *position; +#endif + } elems[]; +} SculptClothConstraint; + +#ifndef CLOTH_NO_POS_PTR +# define MAKE_CONSTRAINT_STRUCT(totelem) \ + signed char ctype, thread_nr; \ + short node; \ + float strength; \ + struct { \ + int index; \ + float *position; \ + } elems[totelem] +#else +# define MAKE_CONSTRAINT_STRUCT(totelem) \ + signed char ctype, thread_nr; \ + short node; \ + float strength; \ + struct { \ + int index; \ + } elems[totelem] +#endif + typedef struct SculptClothLengthConstraint { /* Elements that are affected by the constraint. */ /* Element a should always be a mesh vertex with the index stored in elem_index_a as it is always @@ -385,19 +453,17 @@ typedef struct SculptClothSimulation { eSculptClothNodeSimState *node_state; } SculptClothSimulation; -typedef struct SculptPersistentBase { - float co[3]; - float no[3]; - float disp; -} SculptPersistentBase; - typedef struct SculptVertexInfo { /* Indexed by base mesh vertex index, stores if that vertex is a boundary. */ BLI_bitmap *boundary; + + /* Indexed by vertex, stores the symmetrical topology vertex index found by symmetrize. */ + int *symmetrize_map; } SculptVertexInfo; typedef struct SculptBoundaryEditInfo { /* Vertex index from where the topology propagation reached this vertex. */ + PBVHVertRef original_vertex; int original_vertex_i; /* How many steps were needed to reach this vertex from the boundary. */ @@ -413,6 +479,14 @@ typedef struct SculptBoundaryPreviewEdge { PBVHVertRef v2; } SculptBoundaryPreviewEdge; +#define MAX_STORED_COTANGENTW_EDGES 7 + +typedef struct StoredCotangentW { + float static_weights[MAX_STORED_COTANGENTW_EDGES]; + float *weights; + int length; +} StoredCotangentW; + typedef struct SculptBoundary { /* Vertex indices of the active boundary. */ PBVHVertRef *verts; @@ -424,6 +498,14 @@ typedef struct SculptBoundary { * a distance of 0. */ float *distance; + float (*smoothco)[3]; + float *boundary_dist; // distances from verts to boundary + float (*boundary_tangents)[3]; + + StoredCotangentW *boundary_cotangents; + PBVHVertRef *boundary_closest; + int sculpt_totvert; + /* Data for drawing the preview. */ SculptBoundaryPreviewEdge *edges; int edges_capacity; @@ -457,7 +539,7 @@ typedef struct SculptBoundary { /* Bend Deform type. */ struct { float (*pivot_rotation_axis)[3]; - float (*pivot_positions)[3]; + float (*pivot_positions)[4]; } bend; /* Slide Deform type. */ @@ -470,8 +552,63 @@ typedef struct SculptBoundary { float rotation_axis[3]; float pivot_position[3]; } twist; + + /* Cicrle Deform type. */ + struct { + float (*origin)[3]; + float *radius; + } circle; + + int deform_target; } SculptBoundary; +/* Array Brush. */ +typedef struct SculptArrayCopy { + int index; + int symm_pass; + float mat[4][4]; + float imat[4][4]; + float origin[3]; +} SculptArrayCopy; + +typedef struct ScultpArrayPathPoint { + float length; + float strength; + float co[3]; + float orco[3]; + float direction[3]; +} ScultpArrayPathPoint; + +typedef struct SculptArray { + SculptArrayCopy *copies[PAINT_SYMM_AREAS]; + int num_copies; + + struct { + ScultpArrayPathPoint *points; + int tot_points; + int capacity; + float total_length; + } path; + + int mode; + float normal[3]; + float direction[3]; + float radial_angle; + float initial_radial_angle; + + bool source_mat_valid; + float source_origin[3]; + float source_mat[4][4]; + float source_imat[4][4]; + float (*orco)[3]; + + int *copy_index; + int *symmetry_pass; + + float *smooth_strength; + struct SculptAttribute *scl_inst, *scl_sym; +} SculptArray; + typedef struct SculptFakeNeighbors { bool use_fake_neighbors; @@ -479,7 +616,7 @@ typedef struct SculptFakeNeighbors { float current_max_distance; /* Indexed by vertex, stores the vertex index of its fake neighbor if available. */ - int *fake_neighbor_index; + PBVHVertRef *fake_neighbor_index; } SculptFakeNeighbors; @@ -496,6 +633,8 @@ typedef struct SculptAttributeParams { */ int permanent : 1; /* Cannot be combined with simple_array. */ int stroke_only : 1; /* Release layer at end of struct */ + int nointerp : 1; + int nocopy : 1; } SculptAttributeParams; typedef struct SculptAttribute { @@ -561,6 +700,32 @@ typedef struct SculptAttributePointers { /* BMesh */ SculptAttribute *dyntopo_node_id_vertex; SculptAttribute *dyntopo_node_id_face; + SculptAttribute *rake_temp; + + SculptAttribute *face_areas; + + SculptAttribute *smooth_bdist; + SculptAttribute *smooth_vel; + + SculptAttribute *boundary_flags; + SculptAttribute *sculpt_vert; + SculptAttribute *stroke_id; + + SculptAttribute *smear_previous; + SculptAttribute *hide_poly; + SculptAttribute *limit_surface; + + SculptAttribute *orig_fsets; + + SculptAttribute *layer_disp; + SculptAttribute *layer_id; + + SculptAttribute *prefairing_co; + SculptAttribute *fairing_fade; + SculptAttribute *fairing_mask; + + /* Stores the displacement produced by the laplacian step of HC smooth. */ + SculptAttribute *laplacian_disp; } SculptAttributePointers; typedef struct SculptSession { @@ -574,11 +739,20 @@ typedef struct SculptSession { /* Depsgraph for the Cloth Brush solver to get the colliders. */ struct Depsgraph *depsgraph; - /* These are always assigned to base mesh data when using PBVH_FACES and PBVH_GRIDS. */ + CustomData temp_vdata, temp_pdata; + int temp_vdata_elems, temp_pdata_elems; + float (*vert_positions)[3]; + + const struct MVert *mvert; + const struct MEdge *medge; const struct MPoly *mpoly; const struct MLoop *mloop; + const int *material_index; + + CustomData *vdata, *edata, *ldata, *pdata; + /* These contain the vertex and poly counts of the final mesh. */ int totvert, totpoly; @@ -593,8 +767,7 @@ typedef struct SculptSession { /* Mesh connectivity maps. */ /* Vertices to adjacent polys. */ - struct MeshElemMap *pmap; - int *pmap_mem; + SculptPMap *pmap; /* Edges to adjacent polys. */ struct MeshElemMap *epmap; @@ -606,7 +779,7 @@ typedef struct SculptSession { /* Mesh Face Sets */ /* Total number of polys of the base mesh. */ - int totfaces; + int totedges, totloops, totfaces; /* The 0 ID is not used by the tools or the visibility system, it is just used when creating new * geometry (the trim tool, for example) to detect which geometry was just added, so it can be @@ -619,23 +792,49 @@ typedef struct SculptSession { */ bool *hide_poly; + bool *select_poly; + /* BMesh for dynamic topology sculpting */ struct BMesh *bm; + struct BMIdMap *bm_idmap; + + /* TODO: get rid of these cd_ members and use + * .attrs.XXX.bmesh_cd_offset directly. + */ + int cd_sculpt_vert; + int cd_vert_node_offset; + int cd_face_node_offset; + int cd_vcol_offset; + int cd_vert_mask_offset; + int cd_faceset_offset; + int cd_face_areas; + int cd_hide_poly; + + int totuv; + bool bm_smooth_shading; + bool ignore_uvs; + /* Undo/redo log for dynamic topology sculpting */ - struct BMLog *bm_log; + BMLog *bm_log; /* Limit surface/grids. */ struct SubdivCCG *subdiv_ccg; /* PBVH acceleration structure */ struct PBVH *pbvh; + struct PBVH *last_pbvh; + + /* Setting this to true allows a PBVH rebuild when evaluating the object even if the stroke or + * filter caches are active. */ + bool needs_pbvh_rebuild; /* Painting on deformed mesh */ bool deform_modifiers_active; /* Object is deformed with some modifiers. */ float (*orig_cos)[3]; /* Coords of un-deformed mesh. */ float (*deform_cos)[3]; /* Coords of deformed mesh but without stroke displacement. */ float (*deform_imats)[3][3]; /* Crazy-space deformation matrices. */ + float *face_areas; /* cached face areas for PBVH_FACES and PBVH_GRIDS */ /* Pool for texture evaluations. */ struct ImagePool *tex_pool; @@ -646,8 +845,8 @@ typedef struct SculptSession { /* Cursor data and active vertex for tools */ PBVHVertRef active_vertex; + PBVHFaceRef active_face; - int active_face_index; int active_grid_index; /* When active, the cursor draws with faded colors, indicating that there is an action @@ -664,6 +863,7 @@ typedef struct SculptSession { * when * the gesture starts (intersection with the surface and if they ray hit the surface or not). */ + float gesture_initial_back_location[3]; float gesture_initial_location[3]; float gesture_initial_normal[3]; bool gesture_initial_hit; @@ -672,6 +872,14 @@ typedef struct SculptSession { struct RegionView3D *rv3d; struct View3D *v3d; struct Scene *scene; + int cd_origvcol_offset; + int cd_origco_offset; + int cd_origno_offset; + + /* Face Sets by topology. */ + int face_set_last_created; + PBVHFaceRef face_set_last_poly; + PBVHEdgeRef face_set_last_edge; /* Dynamic mesh preview */ PBVHVertRef *preview_vert_list; @@ -687,19 +895,22 @@ typedef struct SculptSession { SculptVertexInfo vertex_info; SculptFakeNeighbors fake_neighbors; + /* Array. */ + SculptArray *array; + /* Transform operator */ float pivot_pos[3]; float pivot_rot[4]; float pivot_scale[3]; - float init_pivot_pos[3]; - float init_pivot_rot[4]; - float init_pivot_scale[3]; - float prev_pivot_pos[3]; float prev_pivot_rot[4]; float prev_pivot_scale[3]; + float init_pivot_pos[3]; + float init_pivot_rot[4]; + float init_pivot_scale[3]; + union { struct { struct SculptVertexPaintGeomMap gmap; @@ -729,6 +940,14 @@ typedef struct SculptSession { */ char needs_flush_to_id; + // id of current stroke, used to detect + // if vertex original data needs to be updated + int boundary_symmetry; + + bool fast_draw; // hides facesets/masks and forces smooth to save GPU bandwidth + struct MSculptVert *msculptverts; // for non-bmesh + int last_msculptverts_count; + /* This is a fixed-size array so we can pass pointers to its elements * to client code. This is important to keep bmesh offsets up to date. */ @@ -745,7 +964,7 @@ typedef struct SculptSession { */ bool sticky_shading_color; - uchar stroke_id; + ushort stroke_id; /** * Last used painting canvas key. @@ -753,18 +972,56 @@ typedef struct SculptSession { char *last_paint_canvas_key; float last_normal[3]; + /* Used to derive initial tip rotation. */ + float last_grab_delta[3]; + + bool save_temp_layers; + const float (*vert_normals)[3]; + int last_automasking_settings_hash; uchar last_automask_stroke_id; + bool *sharp_edge; bool islands_valid; /* Is attrs.topology_island_key valid? */ + + bool hard_edge_mode; } SculptSession; +typedef enum eSculptBoundary { + SCULPT_BOUNDARY_NONE = 0, + SCULPT_BOUNDARY_MESH = 1 << 0, + SCULPT_BOUNDARY_FACE_SET = 1 << 1, + SCULPT_BOUNDARY_SEAM = 1 << 2, + SCULPT_BOUNDARY_SHARP = 1 << 3, + SCULPT_BOUNDARY_UV = 1 << 4, + SCULPT_BOUNDARY_NEEDS_UPDATE = 1 << 5, + SCULPT_BOUNDARY_ALL = (1 << 0) | (1 << 1) | (1 << 2) | (1 << 3) | (1 << 4), + SCULPT_BOUNDARY_DEFAULT = (1 << 0) | (1 << 3) | (1 << 4) // mesh and sharp +} eSculptBoundary; + +ENUM_OPERATORS(eSculptBoundary, SCULPT_BOUNDARY_NEEDS_UPDATE); + +/* Note: This is stored in a single attribute with boundary flags */ +typedef enum eSculptCorner { + SCULPT_CORNER_NONE = 0, + SCULPT_CORNER_MESH = 1 << 6, + SCULPT_CORNER_FACE_SET = 1 << 7, + SCULPT_CORNER_SEAM = 1 << 8, + SCULPT_CORNER_SHARP = 1 << 9, + SCULPT_CORNER_UV = 1 << 10, +} eSculptCorner; + +ENUM_OPERATORS(eSculptCorner, SCULPT_CORNER_UV); + void BKE_sculptsession_free(struct Object *ob); void BKE_sculptsession_free_deformMats(struct SculptSession *ss); void BKE_sculptsession_free_vwpaint_data(struct SculptSession *ss); + void BKE_sculptsession_bm_to_me(struct Object *ob, bool reorder); void BKE_sculptsession_bm_to_me_for_render(struct Object *object); int BKE_sculptsession_vertex_count(const SculptSession *ss); +void BKE_sculpt_ensure_idmap(struct Object *ob); + /* Ensure an attribute layer exists. */ SculptAttribute *BKE_sculpt_attribute_ensure(struct Object *ob, eAttrDomain domain, @@ -791,52 +1048,33 @@ void BKE_sculpt_attribute_destroy_temporary_all(struct Object *ob); /* Destroy attributes that were marked as stroke only in SculptAttributeParams. */ void BKE_sculpt_attributes_destroy_temporary_stroke(struct Object *ob); -BLI_INLINE void *BKE_sculpt_vertex_attr_get(const PBVHVertRef vertex, const SculptAttribute *attr) -{ - if (attr->data) { - char *p = (char *)attr->data; - int idx = (int)vertex.i; +bool BKE_sculptsession_check_sculptverts(struct Object *ob, struct PBVH *pbvh, int totvert); - if (attr->data_for_bmesh) { - BMElem *v = (BMElem *)vertex.i; - idx = v->head.index; - } +struct BMesh *BKE_sculptsession_empty_bmesh_create(void); +void BKE_sculptsession_bmesh_attr_update_internal(struct Object *ob); - return p + attr->elem_size * (int)idx; - } - else { - BMElem *v = (BMElem *)vertex.i; - return BM_ELEM_CD_GET_VOID_P(v, attr->bmesh_cd_offset); - } +void BKE_sculptsession_sync_attributes(struct Object *ob, struct Mesh *me); - return NULL; -} +void BKE_sculptsession_bmesh_add_layers(struct Object *ob); +SculptAttribute *BKE_sculptsession_attr_layer_get(struct Object *ob, + eAttrDomain domain, + int proptype, + const char *name, + SculptAttributeParams *params, + bool *r_is_new); +bool BKE_sculptsession_attr_release_layer(struct Object *ob, SculptAttribute *scl); +void BKE_sculptsession_update_attr_refs(struct Object *ob); -BLI_INLINE void *BKE_sculpt_face_attr_get(const PBVHFaceRef vertex, const SculptAttribute *attr) -{ - if (attr->data) { - char *p = (char *)attr->data; - int idx = (int)vertex.i; +int BKE_sculptsession_get_totvert(const SculptSession *ss); - if (attr->data_for_bmesh) { - BMElem *v = (BMElem *)vertex.i; - idx = v->head.index; - } - - return p + attr->elem_size * (int)idx; - } - else { - BMElem *v = (BMElem *)vertex.i; - return BM_ELEM_CD_GET_VOID_P(v, attr->bmesh_cd_offset); - } - - return NULL; -} +void BKE_sculptsession_ignore_uvs_set(struct Object *ob, bool value); +void BKE_sculptsession_free_attribute_refs(struct Object *ob); /** * Create new color layer on object if it doesn't have one and if experimental feature set has * sculpt vertex color enabled. Returns truth if new layer has been added, false otherwise. */ + void BKE_sculpt_color_layer_create_if_needed(struct Object *object); /** @@ -856,13 +1094,13 @@ void BKE_sculpt_update_object_after_eval(struct Depsgraph *depsgraph, struct Obj */ struct MultiresModifierData *BKE_sculpt_multires_active(const struct Scene *scene, struct Object *ob); -int *BKE_sculpt_face_sets_ensure(struct Mesh *mesh); +int *BKE_sculpt_face_sets_ensure(struct Object *ob); /** * Create the attribute used to store face visibility and retrieve its data. * Note that changes to the face visibility have to be propagated to other domains * (see #SCULPT_visibility_sync_all_from_faces). */ -bool *BKE_sculpt_hide_poly_ensure(struct Mesh *mesh); +bool *BKE_sculpt_hide_poly_ensure(struct Object *ob); /** * Ensures a mask layer exists. If depsgraph and bmain are non-null, @@ -891,6 +1129,8 @@ void BKE_sculpt_sync_face_visibility_to_grids(struct Mesh *mesh, struct SubdivCC */ bool BKE_sculptsession_use_pbvh_draw(const struct Object *ob, const struct RegionView3D *rv3d); +char BKE_get_fset_boundary_symflag(struct Object *object); + enum { SCULPT_MASK_LAYER_CALC_VERT = (1 << 0), SCULPT_MASK_LAYER_CALC_LOOP = (1 << 1), @@ -930,4 +1170,59 @@ struct CurveMapping *BKE_sculpt_default_cavity_curve(void); #ifdef __cplusplus } + +template +Tptr BKE_sculpt_vertex_attr_get(const PBVHVertRef vertex, const SculptAttribute *attr) +{ + void *ptr = nullptr; + + if (attr->data) { + char *p = (char *)attr->data; + int idx = (int)vertex.i; + + if (attr->data_for_bmesh) { + BMElem *v = (BMElem *)vertex.i; + idx = v->head.index; + } + + ptr = p + attr->elem_size * (int)idx; + } + else { + BMElem *v = (BMElem *)vertex.i; + ptr = BM_ELEM_CD_GET_VOID_P(v, attr->bmesh_cd_offset); + } + + return static_cast(ptr); +} + +template +static Tptr BKE_sculpt_face_attr_get(const PBVHFaceRef face, const SculptAttribute *attr) +{ + void *ptr = nullptr; + + if (attr->data) { + char *p = (char *)attr->data; + int idx = (int)face.i; + + if (attr->data_for_bmesh) { + BMElem *v = (BMElem *)face.i; + idx = v->head.index; + } + + ptr = p + attr->elem_size * (int)idx; + } + else { + BMElem *v = (BMElem *)face.i; + ptr = BM_ELEM_CD_GET_VOID_P(v, attr->bmesh_cd_offset); + } + + return static_cast(ptr); +} + +BLI_INLINE void BKE_sculpt_boundary_flag_update(SculptSession *ss, PBVHVertRef vertex) +{ + int *flags = BKE_sculpt_vertex_attr_get(vertex, ss->attrs.boundary_flags); + *flags |= SCULPT_BOUNDARY_NEEDS_UPDATE; +} + #endif diff --git a/source/blender/blenkernel/BKE_pbvh.h b/source/blender/blenkernel/BKE_pbvh.h index 367dc9a3035..2439d6799a6 100644 --- a/source/blender/blenkernel/BKE_pbvh.h +++ b/source/blender/blenkernel/BKE_pbvh.h @@ -12,29 +12,63 @@ #include "BLI_ghash.h" #include "bmesh.h" +#include "bmesh_log.h" /* For embedding CCGKey in iterator. */ #include "BKE_attribute.h" #include "BKE_ccg.h" + +#include + +//#define DEFRAGMENT_MEMORY + #include "DNA_customdata_types.h" #ifdef __cplusplus extern "C" { #endif -struct BMLog; +/* Experimental feature to detect quad diagonals and mark (but not dissolve) them. */ +//#define SCULPT_DIAGONAL_EDGE_MARKS +typedef struct SculptPMap { + struct MeshElemMap *pmap; + int *pmap_mem; + int refcount; +} SculptPMap; + +#if 0 +typedef struct SculptLoopRef { + intptr_t i; +} SculptLoopRef; +#endif + +#ifdef DEFRAGMENT_MEMORY +# include "BLI_smallhash.h" +#endif + +//#define WITH_PBVH_CACHE + struct BMesh; +struct BMVert; +struct BMEdge; +struct BMFace; +struct BMIdMap; +struct Scene; struct CCGElem; +struct MeshElemMap; struct CCGKey; struct CustomData; +struct TableGSet; struct DMFlagMat; struct IsectRayPrecalc; struct MLoop; struct MLoopTri; +struct MSculptVert; struct MPoly; struct Mesh; struct MeshElemMap; struct PBVH; +struct MEdge; struct PBVHBatches; struct PBVHNode; struct PBVH_GPU_Args; @@ -47,6 +81,79 @@ struct MeshElemMap; typedef struct PBVH PBVH; typedef struct PBVHNode PBVHNode; +//#define PROXY_ADVANCED + +// experimental performance test of "data-based programming" approach +#ifdef PROXY_ADVANCED +typedef struct ProxyKey { + int node; + int pindex; +} ProxyKey; + +# define MAX_PROXY_NEIGHBORS 12 + +typedef struct ProxyVertArray { + float **ownerco; + short **ownerno; + float (*co)[3]; + float (*fno)[3]; + short (*no)[3]; + float *mask, **ownermask; + PBVHVertRef *index; + float **ownercolor, (*color)[4]; + + ProxyKey (*neighbors)[MAX_PROXY_NEIGHBORS]; + + int size; + int datamask; + bool neighbors_dirty; + + GHash *indexmap; +} ProxyVertArray; + +typedef enum { + PV_OWNERCO = 1, + PV_OWNERNO = 2, + PV_CO = 4, + PV_NO = 8, + PV_MASK = 16, + PV_OWNERMASK = 32, + PV_INDEX = 64, + PV_OWNERCOLOR = 128, + PV_COLOR = 256, + PV_NEIGHBORS = 512 +} ProxyVertField; + +typedef struct ProxyVertUpdateRec { + float *co, *no, *mask, *color; + PBVHVertRef index, newindex; +} ProxyVertUpdateRec; + +# define PBVH_PROXY_DEFAULT CO | INDEX | MASK + +struct SculptSession; + +void BKE_pbvh_ensure_proxyarrays( + struct SculptSession *ss, PBVH *pbvh, PBVHNode **nodes, int totnode, int mask); +void BKE_pbvh_load_proxyarrays(PBVH *pbvh, PBVHNode **nodes, int totnode, int mask); + +void BKE_pbvh_ensure_proxyarray( + struct SculptSession *ss, + struct PBVH *pbvh, + struct PBVHNode *node, + int mask, + struct GHash + *vert_node_map, // vert_node_map maps vertex PBVHVertRefs to PBVHNode indices; optional + bool check_indexmap, + bool force_update); +void BKE_pbvh_gather_proxyarray(PBVH *pbvh, PBVHNode **nodes, int totnode); + +void BKE_pbvh_free_proxyarray(struct PBVH *pbvh, struct PBVHNode *node); +void BKE_pbvh_update_proxyvert(struct PBVH *pbvh, struct PBVHNode *node, ProxyVertUpdateRec *rec); +ProxyVertArray *BKE_pbvh_get_proxyarrays(struct PBVH *pbvh, struct PBVHNode *node); + +#endif + typedef enum { PBVH_FACES, PBVH_GRIDS, @@ -112,12 +219,37 @@ typedef struct PBVHFaceRef { #define PBVH_REF_NONE -1LL +typedef struct PBVHTri { + int v[3]; // references into PBVHTriBuf->verts + int eflag; // bitmask of which edges in the tri are real edges in the mesh + intptr_t l[3]; // loops + + float no[3]; + PBVHFaceRef f; +} PBVHTri; + +typedef struct PBVHTriBuf { + PBVHTri *tris; + PBVHVertRef *verts; + int *edges; + int totvert, totedge, tottri; + int verts_size, edges_size, tris_size; + + SmallHash vertmap; // maps vertex ptrs to indices within verts + + // private field + intptr_t *loops; + int totloop, mat_nr; + float min[3], max[3]; +} PBVHTriBuf; + typedef struct { float (*co)[3]; } PBVHProxyNode; typedef struct { float (*color)[4]; + int size; } PBVHColorBufferNode; typedef struct PBVHPixels { @@ -162,10 +294,19 @@ typedef enum { PBVH_UpdateTopology = 1 << 13, PBVH_UpdateColor = 1 << 14, - PBVH_RebuildPixels = 1 << 15, - PBVH_TexLeaf = 1 << 16, - PBVH_TopologyUpdated = 1 << 17, /* Used internally by pbvh_bmesh.c */ + PBVH_RebuildPixels = 1 << 15, + PBVH_Delete = 1 << 16, + PBVH_UpdateCurvatureDir = 1 << 17, + PBVH_UpdateTris = 1 << 18, + PBVH_RebuildNodeVerts = 1 << 19, + + /* tri areas are not guaranteed to be up to date, tools should + update all nodes on first step of brush*/ + PBVH_UpdateTriAreas = 1 << 20, + PBVH_UpdateOtherVerts = 1 << 21, + PBVH_TexLeaf = 1 << 22, + PBVH_TopologyUpdated = 1 << 23, /* Used internally by pbvh_bmesh.c */ } PBVHNodeFlags; ENUM_OPERATORS(PBVHNodeFlags, PBVH_TopologyUpdated); @@ -277,6 +418,9 @@ typedef void (*BKE_pbvh_HitOccludedCallback)(PBVHNode *node, void *data, float * typedef void (*BKE_pbvh_SearchNearestCallback)(PBVHNode *node, void *data, float *tmin); +void BKE_pbvh_get_nodes(PBVH *pbvh, int flag, PBVHNode ***r_array, int *r_totnode); +PBVHNode *BKE_pbvh_get_node(PBVH *pbvh, int node); + /* Building */ PBVH *BKE_pbvh_new(PBVHType type); @@ -291,12 +435,16 @@ void BKE_pbvh_build_mesh(PBVH *pbvh, const struct MPoly *mpoly, const struct MLoop *mloop, float (*vert_positions)[3], + struct MSculptVert *msculptverts, int totvert, struct CustomData *vdata, struct CustomData *ldata, struct CustomData *pdata, const struct MLoopTri *looptri, - int looptri_num); + int looptri_num, + bool fast_draw, + float *face_areas, + SculptPMap *pmap); /** * Do a full rebuild with on Grids data structure. */ @@ -307,17 +455,36 @@ void BKE_pbvh_build_grids(PBVH *pbvh, void **gridfaces, struct DMFlagMat *flagmats, unsigned int **grid_hidden, + bool fast_draw, + float *face_areas, struct Mesh *me, struct SubdivCCG *subdiv_ccg); /** * Build a PBVH from a BMesh. */ void BKE_pbvh_build_bmesh(PBVH *pbvh, + struct Mesh *me, struct BMesh *bm, bool smooth_shading, - struct BMLog *log, - int cd_vert_node_offset, - int cd_face_node_offset); + BMLog *log, + struct BMIdMap *idmap, + const int cd_vert_node_offset, + const int cd_face_node_offset, + const int cd_sculpt_vert, + const int cd_face_areas, + const int cd_boundary_flag, + bool fast_draw, + bool update_sculptverts); + +void BKE_pbvh_set_idmap(PBVH *pbvh, struct BMIdMap *idmap); + +void BKE_pbvh_update_offsets(PBVH *pbvh, + const int cd_vert_node_offset, + const int cd_face_node_offset, + const int cd_sculpt_vert, + const int cd_face_areas, + const int cd_hide_poly, + const int cd_boudnary_flags); void BKE_pbvh_update_bmesh_offsets(PBVH *pbvh, int cd_vert_node_offset, int cd_face_node_offset); @@ -327,6 +494,28 @@ void BKE_pbvh_build_pixels(PBVH *pbvh, struct ImageUser *image_user); void BKE_pbvh_free(PBVH *pbvh); +void BKE_pbvh_set_bm_log(PBVH *pbvh, BMLog *log); +BMLog *BKE_pbvh_get_bm_log(PBVH *pbvh); + +/* update MSculptVerts, doesn't take pbvh argument to allow usage if pbvh doesn't currently exist + */ +void BKE_pbvh_update_sculpt_verts(PBVH *pbvh); + +/** update original data, only data whose r_** parameters are passed in will be updated*/ +bool BKE_pbvh_get_origvert( + PBVH *pbvh, PBVHVertRef vertex, const float **r_co, float **r_no, float **r_color); + +/** +checks if original data needs to be updated for v, and if so updates it. Stroke_id +is provided by the sculpt code and is used to detect updates. The reason we do it +inside the verts and not in the nodes is to allow splitting of the pbvh during the stroke. +*/ +bool BKE_pbvh_bmesh_check_origdata(PBVH *pbvh, struct BMVert *v, int stroke_id); + +/** used so pbvh can differentiate between different strokes, + see BKE_pbvh_bmesh_check_origdata */ +void BKE_pbvh_set_stroke_id(PBVH *pbvh, int stroke_id); + /* Hierarchical Search in the BVH, two methods: * - For each hit calling a callback. * - Gather nodes in an array (easy to multi-thread). */ @@ -355,7 +544,8 @@ void BKE_pbvh_raycast(PBVH *pbvh, void *data, const float ray_start[3], const float ray_normal[3], - bool original); + bool original, + int stroke_id); bool BKE_pbvh_node_raycast(PBVH *pbvh, PBVHNode *node, @@ -364,12 +554,16 @@ bool BKE_pbvh_node_raycast(PBVH *pbvh, const float ray_start[3], const float ray_normal[3], struct IsectRayPrecalc *isect_precalc, + int *hit_count, float *depth, - PBVHVertRef *active_vertex, - int *active_face_grid_index, - float *face_normal); + float *back_depth, + PBVHVertRef *active_vertex_index, + PBVHFaceRef *active_face_grid_index, + float *face_normal, + int stroke_id); -bool BKE_pbvh_bmesh_node_raycast_detail(PBVHNode *node, +bool BKE_pbvh_bmesh_node_raycast_detail(PBVH *pbvh, + PBVHNode *node, const float ray_start[3], struct IsectRayPrecalc *isect_precalc, float *depth, @@ -396,11 +590,13 @@ bool BKE_pbvh_node_find_nearest_to_ray(PBVH *pbvh, const float ray_start[3], const float ray_normal[3], float *depth, - float *dist_sq); + float *dist_sq, + int stroke_id); /* Drawing */ void BKE_pbvh_draw_cb(PBVH *pbvh, + struct Mesh *me, bool update_only_visible, PBVHFrustumPlanes *update_frustum, PBVHFrustumPlanes *draw_frustum, @@ -458,30 +654,77 @@ int BKE_pbvh_get_grid_num_faces(const PBVH *pbvh); /** * Only valid for type == #PBVH_BMESH. */ -void BKE_pbvh_bmesh_detail_size_set(PBVH *pbvh, float detail_size); +void BKE_pbvh_bmesh_detail_size_set(PBVH *pbvh, float detail_size, float detail_range); typedef enum { - PBVH_Subdivide = 1, - PBVH_Collapse = 2, + PBVH_Subdivide = 1 << 0, + PBVH_Collapse = 1 << 1, + PBVH_Cleanup = 1 << 2, // dissolve verts surrounded by either 3 or 4 triangles then triangulate + PBVH_LocalSubdivide = 1 << 3, + PBVH_LocalCollapse = 1 << 4 } PBVHTopologyUpdateMode; -ENUM_OPERATORS(PBVHTopologyUpdateMode, PBVH_Collapse); -/** - * Collapse short edges, subdivide long edges. - */ -bool BKE_pbvh_bmesh_update_topology(PBVH *pbvh, - PBVHTopologyUpdateMode mode, - const float center[3], - const float view_normal[3], - float radius, - bool use_frontface, - bool use_projected); +ENUM_OPERATORS(PBVHTopologyUpdateMode, PBVH_LocalCollapse); +typedef float (*DyntopoMaskCB)(PBVHVertRef vertex, void *userdata); + +bool BKE_pbvh_bmesh_update_topology( + PBVH *pbvh, + PBVHTopologyUpdateMode mode, + const float center[3], + const float view_normal[3], + float radius, + const bool use_frontface, + const bool use_projected, + int symaxis, + bool updatePBVH, + DyntopoMaskCB mask_cb, + void *mask_cb_data, + int custom_max_steps, // if 0, will use defaul hueristics for max steps + bool disable_surface_relax, + bool is_snake_hook); + +bool BKE_pbvh_bmesh_update_topology_nodes(PBVH *pbvh, + bool (*searchcb)(PBVHNode *node, void *data), + void (*undopush)(PBVHNode *node, void *data), + void *searchdata, + PBVHTopologyUpdateMode mode, + const float center[3], + const float view_normal[3], + float radius, + const bool use_frontface, + const bool use_projected, + int sym_axis, + bool updatePBVH, + DyntopoMaskCB mask_cb, + void *mask_cb_data, + bool disable_surface_relax, + bool is_snake_hook); /* Node Access */ +void BKE_pbvh_check_tri_areas(PBVH *pbvh, PBVHNode *node); +void BKE_pbvh_face_areas_begin(PBVH *pbvh); + +// updates boundaries and valences for whole mesh +void BKE_pbvh_bmesh_on_mesh_change(PBVH *pbvh); +bool BKE_pbvh_bmesh_check_valence(PBVH *pbvh, PBVHVertRef vertex); +void BKE_pbvh_bmesh_update_valence(int cd_sculpt_vert, PBVHVertRef vertex); +void BKE_pbvh_bmesh_update_all_valence(PBVH *pbvh); +void BKE_pbvh_bmesh_flag_all_disk_sort(PBVH *pbvh); +bool BKE_pbvh_bmesh_mark_update_valence(PBVH *pbvh, PBVHVertRef vertex); + +/* if pbvh uses a split index buffer, will call BKE_pbvh_vert_tag_update_normal_triangulation; + otherwise does nothing. returns true if BKE_pbvh_vert_tag_update_normal_triangulation was + called.*/ +bool BKE_pbvh_node_mark_update_index_buffer(PBVH *pbvh, PBVHNode *node); +void BKE_pbvh_vert_tag_update_normal_triangulation(PBVHNode *node); +void BKE_pbvh_node_mark_original_update(PBVHNode *node); +void BKE_pbvh_vert_tag_update_normal_tri_area(PBVHNode *node); +void BKE_pbvh_update_all_tri_areas(PBVH *pbvh); void BKE_pbvh_node_mark_update(PBVHNode *node); void BKE_pbvh_node_mark_update_mask(PBVHNode *node); void BKE_pbvh_node_mark_update_color(PBVHNode *node); +void BKE_pbvh_vert_tag_update_normal_visibility(PBVHNode *node); void BKE_pbvh_node_mark_update_face_sets(PBVHNode *node); void BKE_pbvh_node_mark_update_visibility(PBVHNode *node); void BKE_pbvh_node_mark_rebuild_draw(PBVHNode *node); @@ -494,6 +737,7 @@ void BKE_pbvh_node_fully_masked_set(PBVHNode *node, int fully_masked); bool BKE_pbvh_node_fully_masked_get(PBVHNode *node); void BKE_pbvh_node_fully_unmasked_set(PBVHNode *node, int fully_masked); bool BKE_pbvh_node_fully_unmasked_get(PBVHNode *node); +void BKE_pbvh_node_mark_curvature_update(PBVHNode *node); void BKE_pbvh_mark_rebuild_pixels(PBVH *pbvh); void BKE_pbvh_vert_tag_update_normal(PBVH *pbvh, PBVHVertRef vertex); @@ -531,20 +775,14 @@ bool BKE_pbvh_node_frustum_contain_AABB(PBVHNode *node, void *frustum); */ bool BKE_pbvh_node_frustum_exclude_AABB(PBVHNode *node, void *frustum); -struct GSet *BKE_pbvh_bmesh_node_unique_verts(PBVHNode *node); -struct GSet *BKE_pbvh_bmesh_node_other_verts(PBVHNode *node); -struct GSet *BKE_pbvh_bmesh_node_faces(PBVHNode *node); -/** - * In order to perform operations on the original node coordinates - * (currently just ray-cast), store the node's triangles and vertices. - * - * Skips triangles that are hidden. - */ -void BKE_pbvh_bmesh_node_save_orig(struct BMesh *bm, - struct BMLog *log, - PBVHNode *node, - bool use_original); -void BKE_pbvh_bmesh_after_stroke(PBVH *pbvh); +struct TableGSet *BKE_pbvh_bmesh_node_unique_verts(PBVHNode *node); +struct TableGSet *BKE_pbvh_bmesh_node_other_verts(PBVHNode *node); +struct TableGSet *BKE_pbvh_bmesh_node_faces(PBVHNode *node); + +void BKE_pbvh_bmesh_regen_node_verts(PBVH *pbvh); +void BKE_pbvh_bmesh_mark_node_regen(PBVH *pbvh, PBVHNode *node); + +void BKE_pbvh_bmesh_after_stroke(PBVH *pbvh, bool force_balance); /* Update Bounding Box/Redraw and clear flags. */ @@ -560,7 +798,7 @@ void BKE_pbvh_grids_update(PBVH *pbvh, struct DMFlagMat *flagmats, unsigned int **grid_hidden, struct CCGKey *key); -void BKE_pbvh_subdiv_cgg_set(PBVH *pbvh, struct SubdivCCG *subdiv_ccg); +void BKE_pbvh_subdiv_ccg_set(PBVH *pbvh, struct SubdivCCG *subdiv_ccg); void BKE_pbvh_face_sets_set(PBVH *pbvh, int *face_sets); /** @@ -575,9 +813,9 @@ void BKE_pbvh_respect_hide_set(PBVH *pbvh, bool respect_hide); /* Vertex Deformer. */ -float (*BKE_pbvh_vert_coords_alloc(struct PBVH *pbvh))[3]; -void BKE_pbvh_vert_coords_apply(struct PBVH *pbvh, const float (*vertCos)[3], int totvert); -bool BKE_pbvh_is_deformed(struct PBVH *pbvh); +float (*BKE_pbvh_vert_coords_alloc(PBVH *pbvh))[3]; +void BKE_pbvh_vert_coords_apply(PBVH *pbvh, const float (*vertCos)[3], int totvert); +bool BKE_pbvh_is_deformed(PBVH *pbvh); /* Vertex Iterator. */ @@ -621,10 +859,14 @@ typedef struct PBVHVertexIter { bool is_mesh; /* bmesh */ - struct GSetIterator bm_unique_verts; - struct GSetIterator bm_other_verts; + int bi; + struct TableGSet *bm_cur_set; + struct TableGSet *bm_unique_verts, *bm_other_verts; + struct CustomData *bm_vdata; + int cd_sculpt_vert; int cd_vert_mask_offset; + int cd_vcol_offset; /* result: these are all computed in the macro, but we assume * that compiler optimization's will skip the ones we don't use */ @@ -636,6 +878,9 @@ typedef struct PBVHVertexIter { bool visible; } PBVHVertexIter; +#define BKE_PBVH_SCULPTVERT(cd_sculpt_vert, v) \ + ((MSculptVert *)BM_ELEM_CD_GET_VOID_P(v, cd_sculpt_vert)) + void pbvh_vertex_iter_init(PBVH *pbvh, PBVHNode *node, PBVHVertexIter *vi, int mode); #define BKE_pbvh_vertex_iter_begin(pbvh, node, vi, mode) \ @@ -690,22 +935,36 @@ void pbvh_vertex_iter_init(PBVH *pbvh, PBVHNode *node, PBVHVertexIter *vi, int m } \ } \ else { \ - if (!BLI_gsetIterator_done(&vi.bm_unique_verts)) { \ - vi.bm_vert = (BMVert *)BLI_gsetIterator_getKey(&vi.bm_unique_verts); \ - BLI_gsetIterator_step(&vi.bm_unique_verts); \ + BMVert *bv = NULL; \ + while (!bv) { \ + if (!vi.bm_cur_set->elems || vi.bi >= vi.bm_cur_set->cur) { \ + if (vi.bm_cur_set != vi.bm_other_verts && mode != PBVH_ITER_UNIQUE) { \ + vi.bm_cur_set = vi.bm_other_verts; \ + vi.bi = 0; \ + if (!vi.bm_cur_set->elems || vi.bi >= vi.bm_other_verts->cur) { \ + break; \ + } \ + } \ + else { \ + break; \ + } \ + } \ + else { \ + bv = (BMVert *)vi.bm_cur_set->elems[vi.bi++]; \ + } \ } \ - else { \ - vi.bm_vert = (BMVert *)BLI_gsetIterator_getKey(&vi.bm_other_verts); \ - BLI_gsetIterator_step(&vi.bm_other_verts); \ + if (!bv) { \ + continue; \ } \ + vi.bm_vert = bv; \ + vi.vertex.i = (intptr_t)bv; \ + vi.index = BM_elem_index_get(vi.bm_vert); \ vi.visible = !BM_elem_flag_test_bool(vi.bm_vert, BM_ELEM_HIDDEN); \ if (mode == PBVH_ITER_UNIQUE && !vi.visible) { \ continue; \ } \ vi.co = vi.bm_vert->co; \ vi.fno = vi.bm_vert->no; \ - vi.vertex = BKE_pbvh_make_vref((intptr_t)vi.bm_vert); \ - vi.index = BM_elem_index_get(vi.bm_vert); \ vi.mask = (float *)BM_ELEM_CD_GET_VOID_P(vi.bm_vert, vi.cd_vert_mask_offset); \ } @@ -715,6 +974,18 @@ void pbvh_vertex_iter_init(PBVH *pbvh, PBVHNode *node, PBVHVertexIter *vi, int m } \ ((void)0) +#define BKE_pbvh_vertex_to_index(pbvh, v) \ + (BKE_pbvh_type(pbvh) == PBVH_BMESH && v.i != -1 ? BM_elem_index_get((BMVert *)(v.i)) : (v.i)) +PBVHVertRef BKE_pbvh_index_to_vertex(PBVH *pbvh, int idx); + +#define BKE_pbvh_edge_to_index(pbvh, v) \ + (BKE_pbvh_type(pbvh) == PBVH_BMESH && v.i != -1 ? BM_elem_index_get((BMEdge *)(v.i)) : (v.i)) +PBVHEdgeRef BKE_pbvh_index_to_edge(PBVH *pbvh, int idx); + +#define BKE_pbvh_face_to_index(pbvh, v) \ + (BKE_pbvh_type(pbvh) == PBVH_BMESH && v.i != -1 ? BM_elem_index_get((BMFace *)(v.i)) : (v.i)) +PBVHFaceRef BKE_pbvh_index_to_face(PBVH *pbvh, int idx); + #define PBVH_FACE_ITER_VERTS_RESERVED 8 typedef struct PBVHFaceIter { @@ -731,7 +1002,8 @@ typedef struct PBVHFaceIter { const PBVHNode *node_; PBVHType pbvh_type_; int verts_size_; - GSetIterator bm_faces_iter_; + int bm_faces_iter_; + const struct TableGSet *bm_faces_; int cd_hide_poly_, cd_face_set_; bool *hide_poly_; int *face_sets_; @@ -766,11 +1038,6 @@ void BKE_pbvh_node_get_proxies(PBVHNode *node, PBVHProxyNode **proxies, int *pro void BKE_pbvh_node_free_proxies(PBVHNode *node); PBVHProxyNode *BKE_pbvh_node_add_proxy(PBVH *pbvh, PBVHNode *node); void BKE_pbvh_gather_proxies(PBVH *pbvh, PBVHNode ***r_array, int *r_tot); -void BKE_pbvh_node_get_bm_orco_data(PBVHNode *node, - int (**r_orco_tris)[3], - int *r_orco_tris_num, - float (**r_orco_coords)[3], - struct BMVert ***r_orco_verts); /** * \note doing a full search on all vertices here seems expensive, @@ -782,10 +1049,12 @@ bool BKE_pbvh_node_has_vert_with_normal_update_tag(PBVH *pbvh, PBVHNode *node); // void BKE_pbvh_node_BB_reset(PBVHNode *node); // void BKE_pbvh_node_BB_expand(PBVHNode *node, float co[3]); -bool pbvh_has_mask(const PBVH *pbvh); +bool BKE_pbvh_draw_mask(const PBVH *pbvh); +bool BKE_pbvh_has_mask(const PBVH *pbvh); + void pbvh_show_mask_set(PBVH *pbvh, bool show_mask); -bool pbvh_has_face_sets(PBVH *pbvh); +bool BKE_pbvh_draw_face_sets(PBVH *pbvh); void pbvh_show_face_sets_set(PBVH *pbvh, bool show_face_sets); /* Parallelization. */ @@ -801,27 +1070,25 @@ bool *BKE_pbvh_get_vert_hide_for_write(PBVH *pbvh); const bool *BKE_pbvh_get_poly_hide(const PBVH *pbvh); -PBVHColorBufferNode *BKE_pbvh_node_color_buffer_get(PBVHNode *node); -void BKE_pbvh_node_color_buffer_free(PBVH *pbvh); bool BKE_pbvh_get_color_layer(const struct Mesh *me, CustomDataLayer **r_layer, eAttrDomain *r_attr); /* Swaps colors at each element in indices (of domain pbvh->vcol_domain) - * with values in colors. */ + * with values in colors. PBVH_FACES only.*/ void BKE_pbvh_swap_colors(PBVH *pbvh, const int *indices, const int indices_num, float (*colors)[4]); /* Stores colors from the elements in indices (of domain pbvh->vcol_domain) - * into colors. */ + * into colors. PBVH_FACES only.*/ void BKE_pbvh_store_colors(PBVH *pbvh, const int *indices, const int indices_num, float (*colors)[4]); -/* Like BKE_pbvh_store_colors but handles loop->vert conversion */ +/* Like BKE_pbvh_store_colors but handles loop->vert conversion. PBVH_FACES only. */ void BKE_pbvh_store_colors_vertex(PBVH *pbvh, const int *indices, const int indices_num, @@ -834,7 +1101,6 @@ void BKE_pbvh_is_drawing_set(PBVH *pbvh, bool val); void BKE_pbvh_node_num_loops(PBVH *pbvh, PBVHNode *node, int *r_totloop); void BKE_pbvh_update_active_vcol(PBVH *pbvh, const struct Mesh *mesh); -void BKE_pbvh_pmap_set(PBVH *pbvh, const struct MeshElemMap *pmap); void BKE_pbvh_vertex_color_set(PBVH *pbvh, PBVHVertRef vertex, const float color[4]); void BKE_pbvh_vertex_color_get(const PBVH *pbvh, PBVHVertRef vertex, float r_color[4]); @@ -843,6 +1109,320 @@ void BKE_pbvh_ensure_node_loops(PBVH *pbvh); bool BKE_pbvh_draw_cache_invalid(const PBVH *pbvh); int BKE_pbvh_debug_draw_gen_get(PBVHNode *node); +int BKE_pbvh_get_node_index(PBVH *pbvh, PBVHNode *node); +int BKE_pbvh_get_node_id(PBVH *pbvh, PBVHNode *node); +void BKE_pbvh_set_flat_vcol_shading(PBVH *pbvh, bool value); + +#define DYNTOPO_CD_INTERP + +void SCULPT_update_flat_vcol_shading(struct Object *ob, struct Scene *scene); + +void BKE_pbvh_curvature_update_set(PBVHNode *node, bool state); +bool BKE_pbvh_curvature_update_get(PBVHNode *node); + +int BKE_pbvh_get_totnodes(PBVH *pbvh); + +bool BKE_pbvh_bmesh_check_tris(PBVH *pbvh, PBVHNode *node); +PBVHTriBuf *BKE_pbvh_bmesh_get_tris(PBVH *pbvh, PBVHNode *node); +void BKE_pbvh_bmesh_free_tris(PBVH *pbvh, PBVHNode *node); + +/*recalculates boundary flags for *all* vertices. used by + symmetrize.*/ +void BKE_pbvh_recalc_bmesh_boundary(PBVH *pbvh); +void BKE_pbvh_set_boundary_flags(PBVH *pbvh, int *boundary_flags); + +/* saves all bmesh references to internal indices, to be restored later */ +void BKE_pbvh_bmesh_save_indices(PBVH *pbvh); + +/* restore bmesh references from previously indices saved by BKE_pbvh_bmesh_save_indices */ +void BKE_pbvh_bmesh_from_saved_indices(PBVH *pbvh); + +/* wraps calls to BM_mesh_toolflags_set in BKE_pbvh_bmesh_save_indices and + * BKE_pbvh_bmesh_from_saved_indices */ +void BKE_pbvh_bmesh_set_toolflags(PBVH *pbvh, bool use_toolflags); + +void BKE_pbvh_bmesh_remove_face(PBVH *pbvh, struct BMFace *f, bool log_face); +void BKE_pbvh_bmesh_remove_edge(PBVH *pbvh, struct BMEdge *e, bool log_vert); +void BKE_pbvh_bmesh_remove_vertex(PBVH *pbvh, struct BMVert *v, bool log_vert); + +void BKE_pbvh_bmesh_add_face(PBVH *pbvh, struct BMFace *f, bool log_face, bool force_tree_walk); + +// note that e_tri and f_example are allowed to be NULL +struct BMFace *BKE_pbvh_face_create_bmesh(PBVH *pbvh, + struct BMVert *v_tri[3], + struct BMEdge *e_tri[3], + const struct BMFace *f_example); + +// if node is NULL, one will be foudn in the pbvh, which potentially can be slow +struct BMVert *BKE_pbvh_vert_create_bmesh( + PBVH *pbvh, float co[3], float no[3], PBVHNode *node, struct BMVert *v_example); +PBVHNode *BKE_pbvh_node_from_face_bmesh(PBVH *pbvh, struct BMFace *f); +PBVHNode *BKE_pbvh_node_from_index(PBVH *pbvh, int node_i); + +struct BMesh *BKE_pbvh_reorder_bmesh(PBVH *pbvh); +void BKE_pbvh_update_vert_boundary(int cd_sculpt_vert, + int cd_faceset_offset, + int cd_vert_node_offset, + int cd_face_node_offset, + int cd_vcol, + int cd_boundary_flag, + struct BMVert *v, + int bound_symmetry, + const CustomData *ldata, + const int totuv, + const bool do_uvs); + +#define DYNTOPO_DYNAMIC_TESS + +PBVHNode *BKE_pbvh_get_node_leaf_safe(PBVH *pbvh, int i); + +void BKE_pbvh_get_vert_face_areas(PBVH *pbvh, PBVHVertRef vertex, float *r_areas, int valence); +void BKE_pbvh_set_symmetry(PBVH *pbvh, int symmetry, int boundary_symmetry); + +#if 0 +typedef enum { + SCULPT_TEXTURE_UV = 1 << 0, // per-uv + SCULPT_TEXTURE_GRIDS = 1<<1 +} SculptTextureType; + +typedef int TexLayerRef; + +/* +Texture points are texels projected into 3d. +*/ +typedef intptr_t TexPointRef; + +void *BKE_pbvh_get_tex_settings(PBVH *pbvh, PBVHNode *node, TexLayerRef vdm); +void *BKE_pbvh_get_tex_data(PBVH *pbvh, PBVHNode *node, TexPointRef vdm); + +typedef struct SculptTextureDef { + SculptTextureType type; + int settings_size; + + void (*build_begin)(PBVH *pbvh, PBVHNode *node, TexLayerRef vdm); + + void (*calc_bounds)(PBVH *pbvh, PBVHNode *node, float r_min[3], float r_max[3], TexLayerRef vdm); + + /*vdms can cache data per node, which is freed to maintain memory limit. + they store cache in the same structure they return in buildNodeData.*/ + void (*freeCachedData)(PBVH *pbvh, PBVHNode *node, TexLayerRef vdm); + void (*ensuredCachedData)(PBVH *pbvh, PBVHNode *node, TexLayerRef vdm); + + /*builds all data that isn't cached.*/ + void *(*buildNodeData)(PBVH *pbvh, PBVHNode *node); + bool (*validate)(PBVH *pbvh, TexLayerRef vdm); + + void (*setVertexCos)(PBVH *pbvh, PBVHNode *node, PBVHVertRef *verts, int totvert, TexLayerRef vdm); + + void (*getPointsFromNode)(PBVH *pbvh, + PBVHNode *node, + TexLayerRef vdm, + TexPointRef **r_ids, + float ***r_cos, + float ***r_nos, + int *r_totpoint); + void (*releaseNodePoints)( + PBVH *pbvh, PBVHNode *node, TexLayerRef vdm, TexPointRef *ids, float **cos, float **nos); + +# if 0 + int (*getTrisFromNode)(PBVH *pbvh, + PBVHNode *node, + TexLayerRef vdm, + TexPointRef *((*r_tris)[3]), + TexPointRef **r_ids, + int tottri, + int totid); + void (*getTriInterpWeightsFromNode)(PBVH *pbvh, + PBVHNode *node, + TexLayerRef vdm, + float *((*r_tris)[3]), + SculptLoopRef ***r_src_loops, + int tottri, + int totloop); + int (*getTriCount)(PBVH *pbvh, PBVHNode *node, TexLayerRef vdm); +# endif + + void (*getPointNeighbors)(PBVH *pbvh, + PBVHNode *node, + TexLayerRef vdm, + TexPointRef id, + TexPointRef **r_neighbor_ids, + int *r_totneighbor, + int maxneighbors, + TexPointRef **r_duplicates_id, + int r_totduplicate, + int maxduplicates); + void (*getPointValence)(PBVH *pbvh, PBVHNode *node, TexLayerRef vdm, TexPointRef id); + void (*freeNodeData)(PBVH *pbvh, PBVHNode *node, TexLayerRef vdm, void *settings); + + void (*getPointsFromIds)( + PBVH *pbvh, PBVHNode *node, TexLayerRef vdm, TexPointRef *ids, int totid); + + /*displacement texture stuff*/ + // can be tangent, object space displacement + void (*worldToDelta)(PBVH *pbvh, PBVHNode *node, TexLayerRef vdm, TexPointRef *ids, int totid); + void (*deltaToWorld)(PBVH *pbvh, PBVHNode *node, TexLayerRef vdm, TexPointRef *ids, int totid); +} SculptDisplacementDef; + +typedef struct SculptLayerEntry { + char name[64]; + int type; + void *settings; + float factor; + struct SculptLayerEntry *parent; +} SculptLayerEntry; + +#endif + +int BKE_pbvh_do_fset_symmetry(int fset, const int symflag, const float *co); +bool BKE_pbvh_check_vert_boundary(PBVH *pbvh, struct BMVert *v); + +void BKE_pbvh_update_vert_boundary_faces(int *boundary_flags, + const int *face_sets, + const bool *hide_poly, + const float (*vert_positions)[3], + const struct MEdge *medge, + const struct MLoop *mloop, + const struct MPoly *mpoly, + struct MSculptVert *msculptverts, + const struct MeshElemMap *pmap, + PBVHVertRef vertex, + const bool *sharp_edges); +void BKE_pbvh_update_vert_boundary_grids(PBVH *pbvh, + struct SubdivCCG *subdiv_ccg, + PBVHVertRef vertex); + +#if 0 +# include "DNA_meshdata_types.h" +ATTR_NO_OPT static void MV_ADD_FLAG(MSculptVert *mv, int flag) +{ + if (flag & SCULPTVERT_NEED_BOUNDARY) { + flag |= flag; + } + + mv->flag |= flag; +} +#else +# define MV_ADD_FLAG(mv, flag1) (mv)->flag |= (flag1) +#endif + +#if 1 +# include "atomic_ops.h" +# include +# include + +/*why is atomic_ops defining near & far macros?*/ +# ifdef near +# undef near +# endif +# ifdef far +# undef far +# endif + +// static global to limit the number of reports per source file +static int _bke_pbvh_report_count = 0; + +# define PBVH_NAN_REPORT_LIMIT 16 + +// for debugging NaNs that don't appear on developer's machines +BLI_INLINE bool _pbvh_nan_check(const float *co, const char *func, const char *file, int line) +{ + bool bad = false; + + if (_bke_pbvh_report_count > PBVH_NAN_REPORT_LIMIT) { + return false; + } + + for (int i = 0; i < 3; i++) { + if (isnan(co[i]) || !isfinite(co[i])) { + const char *type = !isfinite(co[i]) ? "infinity" : "nan"; + printf("float corruption (vector[%d] was %s): %s:%d\n\t%s\n", i, type, func, line, file); + bad = true; + } + } + + if (bad) { + atomic_add_and_fetch_int32(&_bke_pbvh_report_count, 1); + } + + return bad; +} +# define PBVH_CHECK_NAN(co) _pbvh_nan_check(co, __func__, __FILE__, __LINE__) +#else +# define PBVH_CHECK_NAN(co) +#endif + +typedef struct DynTopoState DynTopoState; + +typedef struct DynRemeshParams { + float edge_size; + float detail_range; + float relax_strength; +} DynRemeshParams; + +/* +Simple wrapper api to use the dyntopo remesher in +non-sculpt contexts. + +existing_pbvh can be NULL. + +Note that all the sculpt customdata layers will be created +if they don't exist, so cd_vert/face_node_offset, cd_mask_offset, +cd_sculpt_vert, etc*/ +DynTopoState *BKE_dyntopo_init(struct BMesh *bm, PBVH *existing_pbvh); +void BKE_dyntopo_free(DynTopoState *ds); +void BKE_dyntopo_default_params(DynRemeshParams *params, float edge_size); +void BKE_dyntopo_remesh(DynTopoState *ds, + DynRemeshParams *params, + int steps, + PBVHTopologyUpdateMode mode); +/* + +use pmap to build an array of edge indices surrounding vertex +r_edges, r_edges_size, heap_alloc define an existing array to put data in. + +final array is similarly put in these pointers. note that calling code +may pass a stack allocated array (*heap_alloc should be false), and must +check if heap_alloc is true afterwards and free *r_edges. + +r_polys is an array of integer pairs and must be same logical size as r_edges +*/ +void BKE_pbvh_pmap_to_edges(PBVH *pbvh, + PBVHVertRef vertex, + int **r_edges, + int *r_edges_size, + bool *heap_alloc, + int **r_polys); +void BKE_pbvh_set_vemap(PBVH *pbvh, struct MeshElemMap *vemap); + +void BKE_pbvh_ignore_uvs_set(PBVH *pbvh, bool value); +bool BKE_pbvh_cache_is_valid(const struct Object *ob, + const struct Mesh *me, + const PBVH *pbvh, + int pbvh_type); + +#ifdef WITH_PBVH_CACHE +bool BKE_pbvh_cache(const struct Mesh *me, PBVH *pbvh); +PBVH *BKE_pbvh_get_or_free_cached(struct Object *ob, struct Mesh *me, PBVHType pbvh_type); +void BKE_pbvh_invalidate_cache(struct Object *ob); +void BKE_pbvh_set_cached(struct Object *ob, PBVH *pbvh); +#endif + +void BKE_pbvh_set_face_areas(PBVH *pbvh, float *face_areas); +void BKE_pbvh_set_sculpt_verts(PBVH *pbvh, struct MSculptVert *sverts); +void BKE_pbvh_set_pmap(PBVH *pbvh, SculptPMap *pmap); +SculptPMap *BKE_pbvh_get_pmap(PBVH *pbvh); +void BKE_pbvh_cache_remove(PBVH *pbvh); +void BKE_pbvh_set_bmesh(PBVH *pbvh, struct BMesh *bm); +void BKE_pbvh_free_bmesh(PBVH *pbvh, struct BMesh *bm); +void BKE_pbvh_system_init(void); +void BKE_pbvh_system_exit(void); + +SculptPMap *BKE_pbvh_make_pmap(const struct Mesh *me); +void BKE_pbvh_pmap_aquire(SculptPMap *pmap); +bool BKE_pbvh_pmap_release(SculptPMap *pmap); +void BKE_pbvh_clear_cache(PBVH *preserve); + #ifdef __cplusplus } #endif diff --git a/source/blender/blenkernel/BKE_subdiv_ccg.h b/source/blender/blenkernel/BKE_subdiv_ccg.h index ced7ff2aa71..ff9475e3ba7 100644 --- a/source/blender/blenkernel/BKE_subdiv_ccg.h +++ b/source/blender/blenkernel/BKE_subdiv_ccg.h @@ -299,6 +299,17 @@ void BKE_subdiv_ccg_eval_limit_point(const SubdivCCG *subdiv_ccg, const SubdivCCGCoord *coord, float r_point[3]); +void BKE_subdiv_ccg_eval_limit_point_and_derivatives(const SubdivCCG *subdiv_ccg, + const SubdivCCGCoord *coord, + float r_point[3], + float r_dPdu[3], + float r_dPdv[3]); + +void BKE_subdiv_ccg_get_tangent_matrix(const SubdivCCG *subdiv_ccg, + const SubdivCCGCoord *coord, + float mat[3][3], + float r_point[3]); + typedef enum SubdivCCGAdjacencyType { SUBDIV_CCG_ADJACENT_NONE, SUBDIV_CCG_ADJACENT_VERTEX, diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt index 850c55de7e9..8dd0868e701 100644 --- a/source/blender/blenkernel/CMakeLists.txt +++ b/source/blender/blenkernel/CMakeLists.txt @@ -48,6 +48,8 @@ set(INC_SYS # For `vfontdata_freetype.c`. ${FREETYPE_INCLUDE_DIRS} + + ${EIGEN3_INCLUDE_DIRS} ) set(SRC @@ -121,6 +123,8 @@ set(SRC intern/data_transfer.cc intern/deform.cc intern/displist.cc + intern/dyntopo.cc + intern/dyntopo_collapse.cc intern/dynamicpaint.cc intern/editlattice.c intern/editmesh.cc @@ -249,8 +253,9 @@ set(SRC intern/particle_distribute.c intern/particle_system.c intern/pbvh.cc - intern/pbvh_colors.cc intern/pbvh_bmesh.cc + intern/pbvh_displacement.c + intern/pbvh_colors.cc intern/pbvh_pixels.cc intern/pbvh_uv_islands.cc intern/pointcache.c @@ -498,6 +503,7 @@ set(SRC intern/CCGSubSurf.h intern/CCGSubSurf_inline.h intern/CCGSubSurf_intern.h + intern/dyntopo_intern.hh intern/attribute_access_intern.hh intern/data_transfer_intern.h intern/lib_intern.h @@ -773,6 +779,16 @@ if(WITH_OPENVDB) add_definitions(-DWITH_OPENVDB ${OPENVDB_DEFINITIONS}) endif() +if(WITH_INSTANT_MESHES) + list(APPEND INC + ../../../intern/instant-meshes + ) + list(APPEND LIB + bf_intern_instant_meshes + ) + add_definitions(-DWITH_INSTANT_MESHES) +endif() + if(WITH_QUADRIFLOW) list(APPEND INC ../../../intern/quadriflow @@ -849,3 +865,75 @@ if(WITH_GTESTS) # RNA_prototypes.h add_dependencies(bf_blenkernel_tests bf_rna) endif() + +if(false) + set(PBVH_CACHE_TEST_INC + . + ../blenfont + ../blenlib + ../blenloader + ../blentranslation + ../bmesh + ../depsgraph + ../draw + ../functions + ../gpencil_modifiers + ../gpu + ../ikplugin + ../imbuf + ../makesdna + ../makesrna + ../modifiers + ../nodes + ../render + ../sequencer + ../shader_fx + ../simulation + ../../../intern/eigen + ../../../intern/ghost + ../../../intern/glew-mx + ../../../intern/guardedalloc + ../../../intern/iksolver/extern + ../../../intern/atomic + ../../../intern/clog + ../../../intern/libmv + ../../../intern/mantaflow/extern + ../../../intern/memutil + ../../../intern/mikktspace + ../../../intern/opensubdiv + ../../../extern/curve_fit_nd + ) + + set(PBVH_CACHE_TEST_SRC + intern/pbvh_cache_test_main.c + ) + + setup_libdirs() + + add_executable(pbvh_cache_test ${PBVH_CACHE_TEST_SRC} ${PBVH_CACHE_TEST_INC}) + setup_platform_linker_flags(pbvh_cache_test) + + target_link_libraries(pbvh_cache_test bf_blenkernel bf_bmesh bf_intern_ghost bf_blenlib bf_intern_guardedalloc) + + if(WIN32) + set_target_properties(pbvh_cache_test PROPERTIES VS_GLOBAL_VcpkgEnabled "false") + set_target_properties(pbvh_cache_test PROPERTIES + PDB_NAME "pbvh_cache_test_private" + PDB_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/$") + if(WITH_WINDOWS_PDB AND WITH_WINDOWS_STRIPPED_PDB) + # This is slightly messy, but single target generators like ninja will not have the + # CMAKE_CFG_INTDIR variable and multitarget generators like msbuild will not have + # CMAKE_BUILD_TYPE. This can be simplified by target_link_options and the $ + # generator expression in newer cmake (2.13+) but until that time this fill have suffice. + if(CMAKE_BUILD_TYPE) + set_property(TARGET pbvh_cache_test APPEND_STRING PROPERTY LINK_FLAGS " /PDBSTRIPPED:${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_BUILD_TYPE}/pbvh_cache_test_public.pdb") + else() + set_property(TARGET pbvh_cache_test APPEND_STRING PROPERTY LINK_FLAGS " /PDBSTRIPPED:${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/pbvh_cache_test_public.pdb") + endif() + endif() + endif() + + if (WIN32) + target_link_libraries(pbvh_cache_test Vfw32.lib Imm32.lib Version.lib Comctl32.lib Shcore.lib Pathcch.lib) + endif() +endif() diff --git a/source/blender/blenkernel/intern/DerivedMesh.cc b/source/blender/blenkernel/intern/DerivedMesh.cc index b17f58e6872..d4228837617 100644 --- a/source/blender/blenkernel/intern/DerivedMesh.cc +++ b/source/blender/blenkernel/intern/DerivedMesh.cc @@ -811,7 +811,7 @@ static void mesh_calc_modifiers(struct Depsgraph *depsgraph, unsupported = true; } - if (scene->toolsettings->sculpt->flags & SCULPT_ONLY_DEFORM) { + if (scene->toolsettings->sculpt && scene->toolsettings->sculpt->flags & SCULPT_ONLY_DEFORM) { unsupported |= (mti->type != eModifierTypeType_OnlyDeform); } diff --git a/source/blender/blenkernel/intern/anim_data.c b/source/blender/blenkernel/intern/anim_data.c index 9b68c19c6e2..0647d9a2c16 100644 --- a/source/blender/blenkernel/intern/anim_data.c +++ b/source/blender/blenkernel/intern/anim_data.c @@ -377,7 +377,7 @@ void BKE_animdata_copy_id_action(Main *bmain, ID *id) void BKE_animdata_duplicate_id_action(struct Main *bmain, struct ID *id, - const eDupli_ID_Flags duplicate_flags) + const uint duplicate_flags) { if (duplicate_flags & USER_DUP_ACT) { animdata_copy_id_action(bmain, id, true, (duplicate_flags & USER_DUP_LINKED_ID) != 0); diff --git a/source/blender/blenkernel/intern/attribute.cc b/source/blender/blenkernel/intern/attribute.cc index 32d12e1af17..12e7056cbe4 100644 --- a/source/blender/blenkernel/intern/attribute.cc +++ b/source/blender/blenkernel/intern/attribute.cc @@ -502,7 +502,10 @@ CustomDataLayer *BKE_id_attribute_search(ID *id, return nullptr; } -int BKE_id_attributes_length(const ID *id, eAttrDomainMask domain_mask, eCustomDataMask mask) +int BKE_id_attributes_length(const ID *id, + eAttrDomainMask domain_mask, + eCustomDataMask mask, + bool skip_temporary) { DomainInfo info[ATTR_DOMAIN_NUM]; get_domains(id, info); @@ -515,8 +518,8 @@ int BKE_id_attributes_length(const ID *id, eAttrDomainMask domain_mask, eCustomD continue; } - if ((1 << int(domain)) & domain_mask) { - length += CustomData_number_of_layers_typemask(customdata, mask); + if ((1 << (int)domain) & domain_mask) { + length += CustomData_number_of_layers_typemask(customdata, mask, skip_temporary); } } @@ -593,7 +596,7 @@ bool BKE_id_attribute_required(const ID *id, const char *name) CustomDataLayer *BKE_id_attributes_active_get(ID *id) { int active_index = *BKE_id_attributes_active_index_p(id); - if (active_index > BKE_id_attributes_length(id, ATTR_DOMAIN_MASK_ALL, CD_MASK_PROP_ALL)) { + if (active_index > BKE_id_attributes_length(id, ATTR_DOMAIN_MASK_ALL, CD_MASK_PROP_ALL, false)) { active_index = 0; } diff --git a/source/blender/blenkernel/intern/blender.c b/source/blender/blenkernel/intern/blender.c index 3598201d906..da650217657 100644 --- a/source/blender/blenkernel/intern/blender.c +++ b/source/blender/blenkernel/intern/blender.c @@ -34,6 +34,7 @@ #include "BKE_layer.h" #include "BKE_main.h" #include "BKE_node.h" +#include "BKE_pbvh.h" #include "BKE_report.h" #include "BKE_scene.h" #include "BKE_screen.h" @@ -75,6 +76,7 @@ void BKE_blender_free(void) DEG_free_node_types(); BKE_brush_system_exit(); + BKE_pbvh_system_exit(); RE_texture_rng_exit(); BKE_callback_global_finalize(); diff --git a/source/blender/blenkernel/intern/brush.cc b/source/blender/blenkernel/intern/brush.cc index 8d5737e940d..14de4ce8e80 100644 --- a/source/blender/blenkernel/intern/brush.cc +++ b/source/blender/blenkernel/intern/brush.cc @@ -1717,6 +1717,9 @@ void BKE_brush_sculpt_reset(Brush *br) * assign this so logic below can remain the same. */ br->alpha = 0.5f; + br->flag2 |= BRUSH_SMOOTH_PRESERVE_FACE_SETS | BRUSH_SMOOTH_USE_AREA_WEIGHT; + bool disable_dyntopo = false; + /* Brush settings */ switch (br->sculpt_tool) { case SCULPT_TOOL_DRAW_SHARP: @@ -1728,11 +1731,13 @@ void BKE_brush_sculpt_reset(Brush *br) br->curve_preset = BRUSH_CURVE_SMOOTHER; br->spacing = 10; br->alpha = 1.0f; + disable_dyntopo = true; break; case SCULPT_TOOL_SLIDE_RELAX: br->spacing = 10; br->alpha = 1.0f; br->slide_deform_type = BRUSH_SLIDE_DEFORM_DRAG; + disable_dyntopo = true; break; case SCULPT_TOOL_CLAY: br->flag |= BRUSH_SIZE_PRESSURE; @@ -1780,6 +1785,7 @@ void BKE_brush_sculpt_reset(Brush *br) break; case SCULPT_TOOL_ROTATE: br->alpha = 1.0; + disable_dyntopo = true; break; case SCULPT_TOOL_SMOOTH: br->flag &= ~BRUSH_SPACE_ATTEN; @@ -1792,6 +1798,11 @@ void BKE_brush_sculpt_reset(Brush *br) case SCULPT_TOOL_SNAKE_HOOK: br->alpha = 1.0f; br->rake_factor = 1.0f; + br->dyntopo.inherit = DYNTOPO_INHERIT_BITMASK & + ~(DYNTOPO_INHERIT_ALL | DYNTOPO_LOCAL_COLLAPSE | + DYNTOPO_INHERIT_DETAIL_RANGE); + br->dyntopo.flag |= DYNTOPO_LOCAL_COLLAPSE; + br->dyntopo.detail_range = 0.4f; break; case SCULPT_TOOL_THUMB: br->size = 75; @@ -1832,6 +1843,7 @@ void BKE_brush_sculpt_reset(Brush *br) br->flag &= ~BRUSH_ALPHA_PRESSURE; br->flag &= ~BRUSH_SPACE; br->flag &= ~BRUSH_SPACE_ATTEN; + disable_dyntopo = true; break; case SCULPT_TOOL_CLOTH: br->cloth_mass = 1.0f; @@ -1840,6 +1852,7 @@ void BKE_brush_sculpt_reset(Brush *br) br->cloth_sim_falloff = 0.75f; br->cloth_deform_type = BRUSH_CLOTH_DEFORM_DRAG; br->flag &= ~(BRUSH_ALPHA_PRESSURE | BRUSH_SIZE_PRESSURE); + disable_dyntopo = true; break; case SCULPT_TOOL_LAYER: br->flag &= ~BRUSH_SPACE_ATTEN; @@ -1856,6 +1869,7 @@ void BKE_brush_sculpt_reset(Brush *br) br->tip_roundness = 1.0f; br->density = 1.0f; br->flag &= ~BRUSH_SPACE_ATTEN; + disable_dyntopo = true; copy_v3_fl(br->rgb, 1.0f); zero_v3(br->secondary_rgb); break; @@ -1925,6 +1939,7 @@ void BKE_brush_sculpt_reset(Brush *br) case SCULPT_TOOL_POSE: case SCULPT_TOOL_BOUNDARY: case SCULPT_TOOL_SLIDE_RELAX: + disable_dyntopo = true; br->add_col[0] = 1.0f; br->add_col[1] = 0.95f; br->add_col[2] = 0.005f; @@ -1958,6 +1973,11 @@ void BKE_brush_sculpt_reset(Brush *br) default: break; } + + if (disable_dyntopo) { + // disabled flag is never inherited + br->dyntopo.flag |= DYNTOPO_DISABLED; + } } void BKE_brush_curve_preset(Brush *b, eCurveMappingPreset preset) @@ -2561,3 +2581,34 @@ struct ImBuf *BKE_brush_gen_radial_control_imbuf(Brush *br, bool secondary, bool return im; } + +bool BKE_brush_hard_edge_mode_get(const Scene *scene, const Brush *brush) +{ + UnifiedPaintSettings *ups = &scene->toolsettings->unified_paint_settings; + bool ret = (ups->flag & UNIFIED_PAINT_FLAG_HARD_EDGE_MODE) ? ups->hard_edge_mode : + brush->flag2 & BRUSH_HARD_EDGE_MODE; + + return ret; +} + +void BKE_brush_hard_edge_mode_set(Scene *scene, Brush *brush, bool val) +{ + UnifiedPaintSettings *ups = &scene->toolsettings->unified_paint_settings; + + if (ups->flag & UNIFIED_PAINT_FLAG_HARD_EDGE_MODE) { + ups->hard_edge_mode = val; + } + else { + if (val) { + brush->flag2 |= BRUSH_HARD_EDGE_MODE; + } + else { + brush->flag2 &= ~BRUSH_HARD_EDGE_MODE; + } + } +} + +float BKE_brush_fset_slide_get(const Scene *scene, const Brush *brush) +{ + return BKE_brush_hard_edge_mode_get(scene, brush) ? 0.0f : brush->autosmooth_fset_slide; +} diff --git a/source/blender/blenkernel/intern/collection.c b/source/blender/blenkernel/intern/collection.c index a28c623057f..b9c56085274 100644 --- a/source/blender/blenkernel/intern/collection.c +++ b/source/blender/blenkernel/intern/collection.c @@ -673,9 +673,12 @@ static Collection *collection_duplicate_recursive(Main *bmain, Collection *BKE_collection_duplicate(Main *bmain, Collection *parent, Collection *collection, - eDupli_ID_Flags duplicate_flags, - eLibIDDuplicateFlags duplicate_options) + const uint duplicate_flags_in, // it's not const!! - joeedh + const uint duplicate_options_in) // not const! { + uint duplicate_flags = duplicate_flags_in; + uint duplicate_options = duplicate_options_in; + const bool is_subprocess = (duplicate_options & LIB_ID_DUPLICATE_IS_SUBPROCESS) != 0; const bool is_root_id = (duplicate_options & LIB_ID_DUPLICATE_IS_ROOT_ID) != 0; diff --git a/source/blender/blenkernel/intern/colortools.c b/source/blender/blenkernel/intern/colortools.c index 882abe6cc8d..c32731e0d35 100644 --- a/source/blender/blenkernel/intern/colortools.c +++ b/source/blender/blenkernel/intern/colortools.c @@ -104,16 +104,62 @@ void BKE_curvemapping_free(CurveMapping *cumap) { if (cumap) { BKE_curvemapping_free_data(cumap); + cumap->flag |= CUMA_IS_FREED; MEM_freeN(cumap); } } +static void *my_dupalloc_id(void *mem, const char *tag) +{ + size_t size = MEM_allocN_len(mem); + + void *ret = MEM_mallocN(size, tag); + memcpy(ret, mem, size); + + return ret; +}; + +void BKE_curvemapping_copy_data_tag_ex(CurveMapping *target, + const CurveMapping *cumap, + const char *tag) +{ + int a; + + bool not_cache = !(target->flag & CUMA_PART_OF_CACHE); + + *target = *cumap; + + if (not_cache) { + target->flag &= ~CUMA_PART_OF_CACHE; + } + + target->cache_users = 0; + + for (a = 0; a < CM_TOT; a++) { + if (cumap->cm[a].curve) { + target->cm[a].curve = my_dupalloc_id(cumap->cm[a].curve, tag); + } + if (cumap->cm[a].table) { + target->cm[a].table = my_dupalloc_id(cumap->cm[a].table, tag); + } + if (cumap->cm[a].premultable) { + target->cm[a].premultable = my_dupalloc_id(cumap->cm[a].premultable, tag); + } + } +} + void BKE_curvemapping_copy_data(CurveMapping *target, const CurveMapping *cumap) { int a; + bool not_cache = !(target->flag & CUMA_PART_OF_CACHE); + *target = *cumap; + if (not_cache) { + target->flag &= ~CUMA_PART_OF_CACHE; + } + for (a = 0; a < CM_TOT; a++) { if (cumap->cm[a].curve) { target->cm[a].curve = MEM_dupallocN(cumap->cm[a].curve); @@ -132,6 +178,8 @@ CurveMapping *BKE_curvemapping_copy(const CurveMapping *cumap) if (cumap) { CurveMapping *cumapn = MEM_dupallocN(cumap); BKE_curvemapping_copy_data(cumapn, cumap); + cumapn->flag &= ~CUMA_PART_OF_CACHE; + cumapn->cache_users = 0; return cumapn; } return NULL; @@ -287,6 +335,15 @@ void BKE_curvemap_reset(CurveMap *cuma, const rctf *clipr, int preset, int slope case CURVE_PRESET_BELL: cuma->totpoint = 3; break; + case CURVE_PRESET_POW2: + cuma->totpoint = 5; + break; + case CURVE_PRESET_POW3: + cuma->totpoint = 6; + break; + case CURVE_PRESET_POW15: + cuma->totpoint = 6; + break; } cuma->curve = MEM_callocN(cuma->totpoint * sizeof(CurveMapPoint), "curve points"); @@ -383,8 +440,60 @@ void BKE_curvemap_reset(CurveMap *cuma, const rctf *clipr, int preset, int slope cuma->curve[2].x = 1.0f; cuma->curve[2].y = 0.025f; break; - } + case CURVE_PRESET_POW2: + cuma->curve[0].x = 0.0f; + cuma->curve[0].y = 0.0f; + cuma->curve[1].x = 0.25f; + cuma->curve[1].y = 0.0625f; + + cuma->curve[2].x = 0.5; + cuma->curve[2].y = 0.25; + + cuma->curve[3].x = 0.75f; + cuma->curve[3].y = 0.5625f; + + cuma->curve[4].x = 1.0f; + cuma->curve[4].y = 1.0f; + + case CURVE_PRESET_POW3: + cuma->curve[0].x = 0.0f; + cuma->curve[0].y = 0.0f; + + cuma->curve[1].x = 0.135f; + cuma->curve[1].y = 0.002f; + + cuma->curve[2].x = 0.318; + cuma->curve[2].y = 0.032; + + cuma->curve[3].x = 0.528; + cuma->curve[3].y = 0.147f; + + cuma->curve[4].x = 0.757f; + cuma->curve[4].y = 0.433f; + + cuma->curve[5].x = 1.0f; + cuma->curve[5].y = 1.0f; + case CURVE_PRESET_POW15: + cuma->curve[0].x = 0.0f; + cuma->curve[0].y = 0.0f; + + cuma->curve[1].x = 0.135f; + cuma->curve[1].y = 0.002f; + + cuma->curve[2].x = 0.318; + cuma->curve[2].y = 0.032; + + cuma->curve[3].x = 0.528; + cuma->curve[3].y = 0.147f; + + cuma->curve[4].x = 0.757f; + cuma->curve[4].y = 0.433f; + + cuma->curve[5].x = 1.0f; + cuma->curve[5].y = 1.0f; + } + //[[0,0], [0.134,0.002], [0.318,0.032], [0.528,0.147], [0.757,0.433], [1,1] /* mirror curve in x direction to have positive slope * rather than default negative slope */ if (slope == CURVEMAP_SLOPE_POSITIVE) { @@ -1300,7 +1409,7 @@ void BKE_curvemapping_curves_blend_write(BlendWriter *writer, const CurveMapping void BKE_curvemapping_blend_read(BlendDataReader *reader, CurveMapping *cumap) { /* flag seems to be able to hang? Maybe old files... not bad to clear anyway */ - cumap->flag &= ~CUMA_PREMULLED; + cumap->flag &= ~(CUMA_PREMULLED | CUMA_PART_OF_CACHE); for (int a = 0; a < CM_TOT; a++) { BLO_read_data_address(reader, &cumap->cm[a].curve); diff --git a/source/blender/blenkernel/intern/crazyspace.cc b/source/blender/blenkernel/intern/crazyspace.cc index 906d4c82623..d77f831166a 100644 --- a/source/blender/blenkernel/intern/crazyspace.cc +++ b/source/blender/blenkernel/intern/crazyspace.cc @@ -340,7 +340,10 @@ static void crazyspace_init_verts_and_matrices(const Mesh *mesh, static bool crazyspace_modifier_supports_deform_matrices(ModifierData *md) { - if (ELEM(md->type, eModifierType_Subsurf, eModifierType_Multires)) { + if (md->type == eModifierType_Multires) { + return true; + } + if (md->type == eModifierType_Subsurf && md->mode & eModifierMode_OnCage) { return true; } const ModifierTypeInfo *mti = BKE_modifier_get_info(static_cast(md->type)); diff --git a/source/blender/blenkernel/intern/curve_bevel.c b/source/blender/blenkernel/intern/curve_bevel.c index 6f32f0f5e6f..5a350de9512 100644 --- a/source/blender/blenkernel/intern/curve_bevel.c +++ b/source/blender/blenkernel/intern/curve_bevel.c @@ -86,7 +86,7 @@ static void curve_bevel_make_extrude_and_fill(const Curve *cu, * for #Curve.bevresol is 32. */ float *quarter_coords_x = alloca(sizeof(float) * (cu->bevresol + 1)); float *quarter_coords_y = alloca(sizeof(float) * (cu->bevresol + 1)); - bevel_quarter_fill(cu, quarter_coords_x, quarter_coords_y); + bevel_quarter_fill((Curve *)cu, quarter_coords_x, quarter_coords_y); int nr; if (fill_type == FULL) { diff --git a/source/blender/blenkernel/intern/customdata.cc b/source/blender/blenkernel/intern/customdata.cc index 0e7e81a2383..9959d9da7b4 100644 --- a/source/blender/blenkernel/intern/customdata.cc +++ b/source/blender/blenkernel/intern/customdata.cc @@ -17,8 +17,10 @@ #include "DNA_customdata_types.h" #include "DNA_meshdata_types.h" +#include "BLI_asan.h" #include "BLI_bitmap.h" #include "BLI_color.hh" +#include "BLI_compiler_attrs.h" #include "BLI_endian_switch.h" #include "BLI_index_range.hh" #include "BLI_math.h" @@ -68,11 +70,42 @@ using blender::Vector; /* number of layers to add when growing a CustomData object */ #define CUSTOMDATA_GROW 5 +#define BM_ASAN_PAD 32 +#if defined(__SANITIZE_ADDRESS__) || __has_feature(address_sanitizer) +# define CD_HAVE_ASAN +#endif + /* ensure typemap size is ok */ BLI_STATIC_ASSERT(ARRAY_SIZE(((CustomData *)nullptr)->typemap) == CD_NUMTYPES, "size mismatch"); static CLG_LogRef LOG = {"bke.customdata"}; +bool CustomData_layout_is_same(const CustomData *_a, const CustomData *_b) +{ + CustomData a = *_a; + CustomData b = *_b; + + a.layers = b.layers = NULL; + a.pool = b.pool = NULL; + + if (memcmp((void *)&a, (void *)&b, sizeof(CustomData)) != 0) { + return false; + } + + for (int i = 0; i < a.totlayer; i++) { + CustomDataLayer cla = _a->layers[i]; + CustomDataLayer clb = _b->layers[i]; + + cla.data = clb.data = NULL; + + if (memcmp((void *)&cla, (void *)&clb, sizeof(CustomDataLayer)) != 0) { + return false; + } + } + + return true; +} + /* -------------------------------------------------------------------- */ /** \name Mesh Mask Utilities * \{ */ @@ -188,6 +221,7 @@ struct LayerTypeInfo { /** a function to determine max allowed number of layers, * should be null or return -1 if no limit */ int (*layers_max)(); + bool use_default_data; }; /** \} */ @@ -1516,6 +1550,87 @@ static bool layerValidate_propfloat2(void *data, const uint totitems, const bool return has_errors; } +static void layerDynTopoVert_copy(const void *source, void *dest, int count) +{ + memcpy(dest, source, count * sizeof(MSculptVert)); +} + +static void layerDynTopoVert_interp( + const void **sources, const float *weights, const float *sub_weights, int count, void *dest) +{ + float co[3], no[3], origmask, color[4], curv; + MSculptVert *mv = (MSculptVert *)dest; + + // float totweight = 0.0f; + + if (count == 0) { + memset(mv, 0, sizeof(*mv)); + return; + } + + zero_v3(co); + zero_v3(no); + origmask = 0.0f; + curv = 0.0f; + zero_v4(color); + + for (int i = 0; i < count; i++) { + MSculptVert *mv2 = (MSculptVert *)sources[i]; + float w; + + if (i == 0) { // copy flag from first source + mv->flag = mv2->flag; + mv->stroke_id = mv2->stroke_id; + } + + w = weights[i]; + + madd_v3_v3fl(co, mv2->origco, w); + madd_v3_v3fl(no, mv2->origno, w); + madd_v4_v4fl(color, mv2->origcolor, w); + origmask += (float)mv2->origmask * w; + curv += (float)mv2->curv * w; + + // totweight += w; + } + + normalize_v3(no); + +#if 0 + if (fabsf(totweight - 1.0) > 0.001) { + printf("eek\n"); + } + float mul = 1.0f / totweight; + + mul_v3_fl(co, mul); + + mul_v4_fl(color, mul); + origmask *= mul; +#endif + + copy_v3_v3(mv->origco, co); + copy_v3_v3(mv->origno, no); + copy_v4_v4(mv->origcolor, color); + + mv->curv = (short)curv; + mv->origmask = (short)origmask; +} + +static void layerInterp_noop(const void **, const float *, const float *, int, void *) +{ + // do nothing +} + +static void layerDefault_mesh_id(void *data, int count) +{ + int *val = (int *)data; + + for (int i = 0; i < count; i++) { + // val[i] = -1; + val[i] = 0; + } +} + static bool layerEqual_propfloat2(const void *data1, const void *data2) { const float2 &a = *static_cast(data1); @@ -1671,7 +1786,21 @@ static const LayerTypeInfo LAYERTYPEINFO[CD_NUMTYPES] = { nullptr, nullptr, layerInterp_propInt, - nullptr}, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + true}, /* 12: CD_PROP_STRING */ {sizeof(MStringProperty), "MStringProperty", @@ -1968,133 +2097,184 @@ static const LayerTypeInfo LAYERTYPEINFO[CD_NUMTYPES] = { nullptr}, /* 51: CD_HAIRLENGTH */ {sizeof(float), "float", 1, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr}, + /*52 CD_MESH_ID */ + {sizeof(unsigned int), + "MIntProperty", + 1, + nullptr, // flag singleton layer + nullptr, + nullptr, + layerInterp_noop, + nullptr, + layerDefault_mesh_id}, + /* 53 CD_DYNTOPO_VERT */ + {sizeof(MSculptVert), + "MSculptVert", + 1, + nullptr, // flag singleton layer + layerDynTopoVert_copy, + nullptr, + layerDynTopoVert_interp}, + /*54 CD_BMESH_TOOLFLAGS */ + {sizeof(MToolFlags), + "MToolFlags", + 1, + nullptr, // flag singleton layer + nullptr, + nullptr, + layerInterp_noop}, }; -static const char *LAYERTYPENAMES[CD_NUMTYPES] = { - /* 0-4 */ "CDMVert", - "CDMSticky", - "CDMDeformVert", - "CDMEdge", - "CDMFace", - /* 5-9 */ "CDMTFace", - "CDMCol", - "CDOrigIndex", - "CDNormal", - "CDFaceMap", - /* 10-14 */ "CDMFloatProperty", - "CDMIntProperty", - "CDMStringProperty", - "CDOrigSpace", - "CDOrco", - /* 15-19 */ "CDMTexPoly", - "CDMLoopUV", - "CDMloopCol", - "CDTangent", - "CDMDisps", - /* 20-24 */ "CDPreviewMCol", - "CDIDMCol", - "CDTextureMCol", - "CDClothOrco", - "CDMRecast", +static const char *LAYERTYPENAMES[CD_NUMTYPES] = {/* 0-4 */ + "CDMVert", + "CDMSticky", + "CDMDeformVert", + "CDMEdge", + "CDMFace", + /* 5-9 */ + "CDMTFace", + "CDMCol", + "CDOrigIndex", + "CDNormal", + "CDFaceMap", + /* 10-14 */ + "CDMFloatProperty", + "CDMIntProperty", + "CDMStringProperty", + "CDOrigSpace", + "CDOrco", + /* 15-19 */ + "CDMTexPoly", + "CDMLoopUV", + "CDMloopCol", + "CDTangent", + "CDMDisps", + /* 20-24 */ + "CDPreviewMCol", + "CDIDMCol", + "CDTextureMCol", + "CDClothOrco", + "CDMRecast", - /* BMESH ONLY */ - /* 25-29 */ "CDMPoly", - "CDMLoop", - "CDShapeKeyIndex", - "CDShapeKey", - "CDBevelWeight", - /* 30-34 */ "CDSubSurfCrease", - "CDOrigSpaceLoop", - "CDPreviewLoopCol", - "CDBMElemPyPtr", - "CDPaintMask", - /* 35-36 */ "CDGridPaintMask", - "CDMVertSkin", - /* 37-38 */ "CDFreestyleEdge", - "CDFreestyleFace", - /* 39-42 */ "CDMLoopTangent", - "CDTessLoopNormal", - "CDCustomLoopNormal", - "CDSculptFaceGroups", - /* 43-46 */ "CDHairPoint", - "CDPropInt8", - "CDHairMapping", - "CDPoint", - "CDPropCol", - "CDPropFloat3", - "CDPropFloat2", - "CDPropBoolean", - "CDHairLength", -}; + /* BMESH ONLY */ + /* 25-29 */ + "CDMPoly", + "CDMLoop", + "CDShapeKeyIndex", + "CDShapeKey", + "CDBevelWeight", + /* 30-34 */ + "CDSubSurfCrease", + "CDOrigSpaceLoop", + "CDPreviewLoopCol", + "CDBMElemPyPtr", + "CDPaintMask", + /* 35-36 */ + "CDGridPaintMask", + "CDMVertSkin", + /* 37-38 */ + "CDFreestyleEdge", + "CDFreestyleFace", + /* 39-42 */ + "CDMLoopTangent", + "CDTessLoopNormal", + "CDCustomLoopNormal", + "CDSculptFaceGroups", + /* 43-46 */ + "CDHairPoint", + "CDHairMapping", + "CDPropInt8", + "CDPoint", + /* 47-50 */ + "CDPropCol", + "CDPropFloat3", + "CDPropFloat2", + "CDPropBoolean", + /*51-53*/ + "CDHairLength", + "CDMeshID", + "CDDyntopoVert", + "CDPropInt16"}; const CustomData_MeshMasks CD_MASK_BAREMESH = { - /*vmask*/ CD_MASK_PROP_FLOAT3, - /*emask*/ CD_MASK_MEDGE, + /*vmask*/ CD_MASK_PROP_FLOAT3 | CD_MASK_MESH_ID, + /*emask*/ CD_MASK_MEDGE | CD_MASK_MESH_ID, /*fmask*/ 0, - /*pmask*/ CD_MASK_MPOLY | CD_MASK_FACEMAP, - /*lmask*/ CD_MASK_MLOOP, + /*pmask*/ CD_MASK_MPOLY | CD_MASK_FACEMAP | CD_MASK_MESH_ID, + /*lmask*/ CD_MASK_MLOOP | CD_MASK_MESH_ID, }; const CustomData_MeshMasks CD_MASK_BAREMESH_ORIGINDEX = { - /*vmask*/ CD_MASK_PROP_FLOAT3 | CD_MASK_ORIGINDEX, - /*emask*/ CD_MASK_MEDGE | CD_MASK_ORIGINDEX, + /*vmask*/ CD_MASK_PROP_FLOAT3 | CD_MASK_ORIGINDEX | CD_MASK_MESH_ID, + /*emask*/ CD_MASK_MEDGE | CD_MASK_ORIGINDEX | CD_MASK_MESH_ID, /*fmask*/ 0, - /*pmask*/ CD_MASK_MPOLY | CD_MASK_FACEMAP | CD_MASK_ORIGINDEX, - /*lmask*/ CD_MASK_MLOOP, + /*pmask*/ CD_MASK_MPOLY | CD_MASK_FACEMAP | CD_MASK_ORIGINDEX | CD_MASK_MESH_ID, + /*lmask*/ CD_MASK_MLOOP | CD_MASK_MESH_ID, }; const CustomData_MeshMasks CD_MASK_MESH = { /*vmask*/ (CD_MASK_PROP_FLOAT3 | CD_MASK_MDEFORMVERT | CD_MASK_MVERT_SKIN | - CD_MASK_PAINT_MASK | CD_MASK_PROP_ALL | CD_MASK_CREASE | CD_MASK_BWEIGHT), + CD_MASK_PAINT_MASK | CD_MASK_PROP_ALL | CD_MASK_CREASE | CD_MASK_BWEIGHT | + CD_MASK_MESH_ID), /*emask*/ - (CD_MASK_MEDGE | CD_MASK_FREESTYLE_EDGE | CD_MASK_PROP_ALL | CD_MASK_BWEIGHT | CD_MASK_CREASE), + (CD_MASK_MEDGE | CD_MASK_FREESTYLE_EDGE | CD_MASK_PROP_ALL | CD_MASK_BWEIGHT | CD_MASK_CREASE | + CD_MASK_MESH_ID), /*fmask*/ 0, /*pmask*/ - (CD_MASK_MPOLY | CD_MASK_FACEMAP | CD_MASK_FREESTYLE_FACE | CD_MASK_PROP_ALL), + (CD_MASK_MPOLY | CD_MASK_FACEMAP | CD_MASK_FREESTYLE_FACE | CD_MASK_PROP_ALL | + CD_MASK_MESH_ID), /*lmask*/ (CD_MASK_MLOOP | CD_MASK_MDISPS | CD_MASK_CUSTOMLOOPNORMAL | CD_MASK_GRID_PAINT_MASK | - CD_MASK_PROP_ALL), + CD_MASK_PROP_ALL | CD_MASK_MESH_ID), }; + const CustomData_MeshMasks CD_MASK_DERIVEDMESH = { /*vmask*/ (CD_MASK_ORIGINDEX | CD_MASK_MDEFORMVERT | CD_MASK_SHAPEKEY | CD_MASK_MVERT_SKIN | CD_MASK_PAINT_MASK | CD_MASK_ORCO | CD_MASK_CLOTH_ORCO | CD_MASK_PROP_ALL | - CD_MASK_CREASE | CD_MASK_BWEIGHT), + CD_MASK_CREASE | CD_MASK_BWEIGHT | CD_MASK_MESH_ID), /*emask*/ (CD_MASK_ORIGINDEX | CD_MASK_FREESTYLE_EDGE | CD_MASK_BWEIGHT | CD_MASK_PROP_ALL | - CD_MASK_CREASE), + CD_MASK_CREASE | CD_MASK_MESH_ID), /*fmask*/ (CD_MASK_ORIGINDEX | CD_MASK_ORIGSPACE | CD_MASK_PREVIEW_MCOL | CD_MASK_TANGENT), /*pmask*/ - (CD_MASK_ORIGINDEX | CD_MASK_FREESTYLE_FACE | CD_MASK_FACEMAP | CD_MASK_PROP_ALL), + (CD_MASK_ORIGINDEX | CD_MASK_FREESTYLE_FACE | CD_MASK_FACEMAP | CD_MASK_PROP_ALL | + CD_MASK_MESH_ID), /*lmask*/ (CD_MASK_CUSTOMLOOPNORMAL | CD_MASK_PREVIEW_MLOOPCOL | CD_MASK_ORIGSPACE_MLOOP | - CD_MASK_PROP_ALL), /* XXX: MISSING #CD_MASK_MLOOPTANGENT ? */ + CD_MASK_PROP_ALL | CD_MASK_MESH_ID), /* XXX: MISSING #CD_MASK_MLOOPTANGENT ? */ }; const CustomData_MeshMasks CD_MASK_BMESH = { /*vmask*/ (CD_MASK_MDEFORMVERT | CD_MASK_BWEIGHT | CD_MASK_MVERT_SKIN | CD_MASK_SHAPEKEY | - CD_MASK_SHAPE_KEYINDEX | CD_MASK_PAINT_MASK | CD_MASK_PROP_ALL | CD_MASK_CREASE), - /*emask*/ (CD_MASK_BWEIGHT | CD_MASK_CREASE | CD_MASK_FREESTYLE_EDGE | CD_MASK_PROP_ALL), + CD_MASK_SHAPE_KEYINDEX | CD_MASK_PAINT_MASK | CD_MASK_PROP_ALL | CD_MASK_CREASE | + CD_MASK_MESH_ID), + /*emask*/ + (CD_MASK_BWEIGHT | CD_MASK_CREASE | CD_MASK_FREESTYLE_EDGE | CD_MASK_PROP_ALL | + CD_MASK_MESH_ID), /*fmask*/ 0, /*pmask*/ - (CD_MASK_FREESTYLE_FACE | CD_MASK_FACEMAP | CD_MASK_PROP_ALL), + (CD_MASK_FREESTYLE_FACE | CD_MASK_FACEMAP | CD_MASK_PROP_ALL | CD_MASK_MESH_ID), /*lmask*/ - (CD_MASK_MDISPS | CD_MASK_CUSTOMLOOPNORMAL | CD_MASK_GRID_PAINT_MASK | CD_MASK_PROP_ALL), + (CD_MASK_MDISPS | CD_MASK_CUSTOMLOOPNORMAL | CD_MASK_GRID_PAINT_MASK | CD_MASK_PROP_ALL | + CD_MASK_MESH_ID), }; const CustomData_MeshMasks CD_MASK_EVERYTHING = { /*vmask*/ (CD_MASK_BM_ELEM_PYPTR | CD_MASK_ORIGINDEX | CD_MASK_MDEFORMVERT | CD_MASK_BWEIGHT | CD_MASK_MVERT_SKIN | CD_MASK_ORCO | CD_MASK_CLOTH_ORCO | CD_MASK_SHAPEKEY | - CD_MASK_SHAPE_KEYINDEX | CD_MASK_PAINT_MASK | CD_MASK_PROP_ALL | CD_MASK_CREASE), + CD_MASK_SHAPE_KEYINDEX | CD_MASK_PAINT_MASK | CD_MASK_PROP_ALL | CD_MASK_CREASE | + CD_MASK_MESH_ID), /*emask*/ (CD_MASK_MEDGE | CD_MASK_BM_ELEM_PYPTR | CD_MASK_ORIGINDEX | CD_MASK_BWEIGHT | CD_MASK_CREASE | - CD_MASK_FREESTYLE_EDGE | CD_MASK_PROP_ALL), + CD_MASK_FREESTYLE_EDGE | CD_MASK_PROP_ALL | CD_MASK_MESH_ID), /*fmask*/ (CD_MASK_MFACE | CD_MASK_ORIGINDEX | CD_MASK_NORMAL | CD_MASK_MTFACE | CD_MASK_MCOL | CD_MASK_ORIGSPACE | CD_MASK_TANGENT | CD_MASK_TESSLOOPNORMAL | CD_MASK_PREVIEW_MCOL | - CD_MASK_PROP_ALL), + CD_MASK_PROP_ALL | CD_MASK_MESH_ID), /*pmask*/ (CD_MASK_MPOLY | CD_MASK_BM_ELEM_PYPTR | CD_MASK_ORIGINDEX | CD_MASK_FACEMAP | - CD_MASK_FREESTYLE_FACE | CD_MASK_PROP_ALL), + + CD_MASK_FREESTYLE_FACE | CD_MASK_PROP_ALL | CD_MASK_MESH_ID), /*lmask*/ (CD_MASK_MLOOP | CD_MASK_BM_ELEM_PYPTR | CD_MASK_MDISPS | CD_MASK_NORMAL | CD_MASK_CUSTOMLOOPNORMAL | CD_MASK_MLOOPTANGENT | CD_MASK_PREVIEW_MLOOPCOL | - CD_MASK_ORIGSPACE_MLOOP | CD_MASK_GRID_PAINT_MASK | CD_MASK_PROP_ALL), + CD_MASK_ORIGSPACE_MLOOP | CD_MASK_GRID_PAINT_MASK | CD_MASK_PROP_ALL | CD_MASK_MESH_ID), }; static const LayerTypeInfo *layerType_getInfo(int type) @@ -2185,6 +2365,95 @@ void CustomData_update_typemap(CustomData *data) } } +void CustomData_regen_active_refs(CustomData *data) +{ + int i, j; + + for (int i = 0; i < CD_NUMTYPES; i++) { + data->typemap[i] = -1; + } + + for (i = 0, j = 0; i < data->totlayer; i++) { + CustomDataLayer *layer = &data->layers[i]; + + if (data->typemap[layer->type] == -1) { + data->typemap[layer->type] = i; + } + } + + /* explicitly flag active layers */ + for (i = 0, j = 0; i < data->totlayer; i++) { + CustomDataLayer *layer = &data->layers[i]; + CustomDataLayer *base = data->layers + data->typemap[layer->type]; + int n = layer - base; + + if (layer == base) { + continue; + } + + layer->active = n == base->active; + layer->active_clone = n == base->active_clone; + layer->active_mask = n == base->active_mask; + layer->active_rnd = n == base->active_rnd; + } + + /* handle case of base layers being active */ + for (int i = 0; i < CD_NUMTYPES; i++) { + if (data->typemap[i] == -1) { + continue; + } + + CustomDataLayer *base = data->layers + data->typemap[i]; + + base->active = !base->active; + base->active_mask = !base->active_mask; + base->active_clone = !base->active_clone; + base->active_rnd = !base->active_rnd; + } + + /* regenerate active refs */ + /* set active n in base layer for all types */ + for (i = 0; i < data->totlayer; i++) { + CustomDataLayer *layer = data->layers + i; + CustomDataLayer *base = data->layers + data->typemap[layer->type]; + + int n = layer - base; + + if (n < 0) { + printf("error!\n"); + for (int j = 0; j < data->totlayer; j++) { + printf("%s", i == j ? "->" : " "); + printf("%d : \"%s\"\n", + data->layers[i].type, + data->layers[i].name ? data->layers[i].name : ""); + } + } + if (layer->active) { + base->active = n; + } + if (layer->active_mask) { + base->active_mask = n; + } + if (layer->active_clone) { + base->active_clone = n; + } + if (layer->active_rnd) { + base->active_rnd = n; + } + } + + /* set active n in all layers */ + for (i = 0; i < data->totlayer; i++) { + CustomDataLayer *layer = &data->layers[i]; + CustomDataLayer *base = data->layers + data->typemap[layer->type]; + + layer->active = base->active; + layer->active_mask = base->active_mask; + layer->active_clone = base->active_clone; + layer->active_rnd = base->active_rnd; + } +} + /* currently only used in BLI_assert */ #ifndef NDEBUG static bool customdata_typemap_is_valid(const CustomData *data) @@ -2195,6 +2464,29 @@ static bool customdata_typemap_is_valid(const CustomData *data) } #endif +/* copies all customdata layers without allocating data, + * and without respect to type masks or NO_COPY/etc flags*/ +void CustomData_copy_all_layout(const struct CustomData *source, struct CustomData *dest) +{ + *dest = *source; + + if (dest->pool) { + dest->pool = NULL; + } + + if (source->layers) { + dest->layers = static_cast( + MEM_mallocN(sizeof(*dest->layers) * source->totlayer, __func__)); + + for (int i = 0; i < source->totlayer; i++) { + dest->layers[i] = source->layers[i]; + dest->layers[i].data = NULL; + } + } + + CustomData_regen_active_refs(dest); +} + bool CustomData_merge(const CustomData *source, CustomData *dest, eCustomDataMask mask, @@ -2214,6 +2506,10 @@ bool CustomData_merge(const CustomData *source, int type = layer->type; int flag = layer->flag; + if (flag & CD_FLAG_NOCOPY) { + continue; + } + if (type != lasttype) { number = 0; maxnumber = CustomData_layertype_layers_max(type); @@ -2261,6 +2557,10 @@ bool CustomData_merge(const CustomData *source, } if (newlayer) { + if (layer->default_data) { + newlayer->default_data = MEM_dupallocN(layer->default_data); + } + newlayer->uid = layer->uid; newlayer->active = lastactive; @@ -2286,6 +2586,8 @@ bool CustomData_merge(const CustomData *source, } CustomData_update_typemap(dest); + CustomData_regen_active_refs(dest); + return changed; } @@ -2381,6 +2683,10 @@ static void customData_free_layer__internal(CustomDataLayer *layer, const int to if (layer->data) { MEM_freeN(layer->data); } + + if (layer->default_data) { + MEM_freeN(layer->default_data); + } } } @@ -2435,17 +2741,130 @@ static void customData_update_offsets(CustomData *data) const LayerTypeInfo *typeInfo; int offset = 0; - for (int i = 0; i < data->totlayer; i++) { - typeInfo = layerType_getInfo(data->layers[i].type); + // sort by alignment + int aligns[] = {16, 8, 4, 2, 1}; + BLI_bitmap *donemap = BLI_BITMAP_NEW_ALLOCA(data->totlayer); + int alignment = 1; - data->layers[i].offset = offset; - offset += typeInfo->size; + // do large structs first + for (int j = 0; j < data->totlayer; j++) { + CustomDataLayer *layer = data->layers + j; + + typeInfo = layerType_getInfo(layer->type); + int size = (int)typeInfo->size; + + /* Float vectors get 4-byte alignment. */ + if (ELEM(layer->type, CD_PROP_COLOR, CD_PROP_FLOAT2, CD_PROP_FLOAT3)) { + alignment = max_ii(alignment, 4); + } + else if (size > 4) { + alignment = max_ii(alignment, 8); + } + else if (size > 2) { + alignment = max_ii(alignment, 4); + } + else if (size > 1) { + alignment = max_ii(alignment, 2); + } + else { + alignment = max_ii(alignment, 1); + } + + /* Detect large structures */ + if (size > 8) { + BLI_BITMAP_SET(donemap, j, true); + + // align to 8-byte boundary + if (size & 7) { + size += 8 - (size & 7); + } + +#ifdef CD_HAVE_ASAN + offset += BM_ASAN_PAD; +#endif + + layer->offset = offset; + offset += size; + +#ifdef CD_HAVE_ASAN + offset += BM_ASAN_PAD; +#endif + } + } + + for (int i = 0; i < ARRAY_SIZE(aligns) + 1; i++) { + for (int j = 0; j < data->totlayer; j++) { + CustomDataLayer *layer = data->layers + j; + + if (BLI_BITMAP_TEST(donemap, j)) { + continue; + } + + typeInfo = layerType_getInfo(layer->type); + int size = (int)typeInfo->size; + + if (i < ARRAY_SIZE(aligns) && (size % aligns[i]) != 0) { + continue; + } + +#ifdef CD_HAVE_ASAN + offset += BM_ASAN_PAD; +#endif + + BLI_BITMAP_SET(donemap, j, true); + + int align2 = aligns[i] - (offset % aligns[i]); + if (align2 != aligns[i]) { + offset += align2; + } + + layer->offset = offset; + offset += size; + +#ifdef CD_HAVE_ASAN + offset += BM_ASAN_PAD; +#endif + } + } + + if (offset % alignment != 0) { + offset += alignment - (offset % alignment); } data->totsize = offset; + CustomData_update_typemap(data); } +void CustomData_bmesh_asan_poison(const CustomData *data, void *block) +{ +#ifdef CD_HAVE_ASAN + if (!block) { + return; + } + + char *ptr = (char *)block; + + BLI_asan_unpoison(block, data->totsize); + + for (int i = 0; i < data->totlayer; i++) { + CustomDataLayer *layer = data->layers + i; + const LayerTypeInfo *typeInfo = layerType_getInfo(layer->type); + + BLI_asan_poison((ptr + layer->offset - BM_ASAN_PAD), BM_ASAN_PAD); + BLI_asan_poison((ptr + layer->offset + typeInfo->size), BM_ASAN_PAD); + } +#endif +} + +void CustomData_bmesh_asan_unpoison(const CustomData *data, void *block) +{ + if (!block) { + return; + } + BLI_asan_unpoison(block, data->totsize); +} + /* to use when we're in the middle of modifying layers */ static int CustomData_get_layer_index__notypemap(const CustomData *data, const int type) { @@ -2991,12 +3410,17 @@ int CustomData_number_of_layers(const CustomData *data, const int type) return number; } -int CustomData_number_of_layers_typemask(const CustomData *data, const eCustomDataMask mask) +int CustomData_number_of_layers_typemask(const CustomData *data, + eCustomDataMask mask, + bool skip_temporary) { int number = 0; for (int i = 0; i < data->totlayer; i++) { - if (mask & CD_TYPE_AS_MASK(data->layers[i].type)) { + bool ok = mask & CD_TYPE_AS_MASK(data->layers[i].type); + ok = ok && (!skip_temporary || !(data->layers[i].flag & (int)CD_FLAG_TEMPORARY)); + + if (ok) { number++; } } @@ -3045,10 +3469,42 @@ void CustomData_duplicate_referenced_layers(CustomData *data, const int totelem) } } +bool CustomData_is_referenced_layer(CustomData *data, const int type) +{ + int layer_index = CustomData_get_active_layer_index(data, type); + if (layer_index == -1) { + return false; + } + + CustomDataLayer *layer = &data->layers[layer_index]; + + return (layer->flag & CD_FLAG_NOFREE) != 0; +} + +void CustomData_unmark_temporary_nocopy(CustomData *data) +{ + for (int i = 0; i < data->totlayer; i++) { + if (data->layers[i].flag & CD_FLAG_TEMPORARY) { + data->layers[i].flag &= ~CD_FLAG_NOCOPY; + } + } +} + +void CustomData_mark_temporary_nocopy(CustomData *data) +{ + for (int i = 0; i < data->totlayer; i++) { + if (data->layers[i].flag & CD_FLAG_TEMPORARY) { + data->layers[i].flag |= CD_FLAG_NOCOPY; + } + } +} + void CustomData_free_temporary(CustomData *data, const int totelem) { int i, j; bool changed = false; + + /* free temp layers */ for (i = 0, j = 0; i < data->totlayer; i++) { CustomDataLayer *layer = &data->layers[i]; @@ -3059,6 +3515,8 @@ void CustomData_free_temporary(CustomData *data, const int totelem) if ((layer->flag & CD_FLAG_TEMPORARY) == CD_FLAG_TEMPORARY) { customData_free_layer__internal(layer, totelem); changed = true; + + // compact data->layers by not incrementing j here } else { j++; @@ -3072,6 +3530,9 @@ void CustomData_free_temporary(CustomData *data, const int totelem) changed = true; } + CustomData_update_typemap(data); + CustomData_regen_active_refs(data); + if (changed) { customData_update_offsets(data); } @@ -3292,10 +3753,32 @@ void CustomData_interp(const CustomData *source, if (dest->layers[dest_i].type == source->layers[src_i].type) { void *src_data = source->layers[src_i].data; + if (dest->layers[dest_i].type == CD_MESH_ID) { + continue; // paranoia check that we don't process id layers + } + for (int j = 0; j < count; j++) { sources[j] = POINTER_OFFSET(src_data, size_t(src_indices[j]) * typeInfo->size); } + if (dest->layers[dest_i].flag & CD_FLAG_ELEM_NOINTERP) { + if (!(dest->layers[dest_i].flag & CD_FLAG_ELEM_NOCOPY)) { + if (typeInfo->copy) { + typeInfo->copy( + sources[0], + POINTER_OFFSET(dest->layers[dest_i].data, (size_t)dest_index * typeInfo->size), + 1); + } + else { + memcpy(POINTER_OFFSET(dest->layers[dest_i].data, (size_t)dest_index * typeInfo->size), + sources[0], + typeInfo->size); + } + } + + continue; + } + typeInfo->interp( sources, weights, @@ -3445,6 +3928,13 @@ int CustomData_get_offset(const CustomData *data, const int type) return data->layers[layer_index].offset; } +int CustomData_get_named_offset(const CustomData *data, int type, const char *name) +{ + int idx = CustomData_get_named_layer_index(data, type, name); + + return idx == -1 ? -1 : data->layers[idx].offset; +} + int CustomData_get_n_offset(const CustomData *data, const int type, const int n) { int layer_index = CustomData_get_layer_index_n(data, type, n); @@ -3487,7 +3977,49 @@ const char *CustomData_get_layer_name(const CustomData *data, const int type, co /* BMesh functions */ +void CustomData_bmesh_update_active_layers(CustomData *fdata, CustomData *ldata) +{ + int act; + + if (CustomData_has_layer(ldata, CD_MLOOPUV)) { + act = CustomData_get_active_layer(ldata, CD_MLOOPUV); + CustomData_set_layer_active(fdata, CD_MTFACE, act); + + act = CustomData_get_render_layer(ldata, CD_MLOOPUV); + CustomData_set_layer_render(fdata, CD_MTFACE, act); + + act = CustomData_get_clone_layer(ldata, CD_MLOOPUV); + CustomData_set_layer_clone(fdata, CD_MTFACE, act); + + act = CustomData_get_stencil_layer(ldata, CD_MLOOPUV); + CustomData_set_layer_stencil(fdata, CD_MTFACE, act); + } + + if (CustomData_has_layer(ldata, CD_PROP_BYTE_COLOR)) { + act = CustomData_get_active_layer(ldata, CD_PROP_BYTE_COLOR); + CustomData_set_layer_active(fdata, CD_MCOL, act); + + act = CustomData_get_render_layer(ldata, CD_PROP_BYTE_COLOR); + CustomData_set_layer_render(fdata, CD_MCOL, act); + + act = CustomData_get_clone_layer(ldata, CD_PROP_BYTE_COLOR); + CustomData_set_layer_clone(fdata, CD_MCOL, act); + + act = CustomData_get_stencil_layer(ldata, CD_PROP_BYTE_COLOR); + CustomData_set_layer_stencil(fdata, CD_MCOL, act); + } +} + +#ifndef USE_BMESH_PAGE_CUSTOMDATA void CustomData_bmesh_init_pool(CustomData *data, const int totelem, const char htype) +{ + CustomData_bmesh_init_pool_ex(data, totelem, htype, __func__); +} + +void CustomData_bmesh_init_pool_ex(CustomData *data, + int totelem, + const char htype, + const char *memtag) { int chunksize; @@ -3515,9 +4047,10 @@ void CustomData_bmesh_init_pool(CustomData *data, const int totelem, const char /* If there are no layers, no pool is needed just yet */ if (data->totlayer) { - data->pool = BLI_mempool_create(data->totsize, totelem, chunksize, BLI_MEMPOOL_NOP); + data->pool = BLI_mempool_create_ex(data->totsize, totelem, chunksize, BLI_MEMPOOL_NOP, memtag); } } +#endif bool CustomData_bmesh_merge(const CustomData *source, CustomData *dest, @@ -3527,7 +4060,7 @@ bool CustomData_bmesh_merge(const CustomData *source, const char htype) { - if (CustomData_number_of_layers_typemask(source, mask) == 0) { + if (CustomData_number_of_layers_typemask(source, mask, false) == 0) { return false; } @@ -3617,6 +4150,8 @@ void CustomData_bmesh_free_block(CustomData *data, void **block) return; } + CustomData_bmesh_asan_unpoison(data, *block); + for (int i = 0; i < data->totlayer; i++) { if (!(data->layers[i].flag & CD_FLAG_NOFREE)) { const LayerTypeInfo *typeInfo = layerType_getInfo(data->layers[i].type); @@ -3640,6 +4175,9 @@ void CustomData_bmesh_free_block_data(CustomData *data, void *block) if (block == nullptr) { return; } + + CustomData_bmesh_asan_unpoison(data, block); + for (int i = 0; i < data->totlayer; i++) { if (!(data->layers[i].flag & CD_FLAG_NOFREE)) { const LayerTypeInfo *typeInfo = layerType_getInfo(data->layers[i].type); @@ -3649,11 +4187,15 @@ void CustomData_bmesh_free_block_data(CustomData *data, void *block) } } } + if (data->totsize) { memset(block, 0, data->totsize); } + + CustomData_bmesh_asan_poison(data, block); } +#ifndef USE_BMESH_PAGE_CUSTOMDATA static void CustomData_bmesh_alloc_block(CustomData *data, void **block) { if (*block) { @@ -3662,11 +4204,26 @@ static void CustomData_bmesh_alloc_block(CustomData *data, void **block) if (data->totsize > 0) { *block = BLI_mempool_alloc(data->pool); + + CustomData_bmesh_asan_poison(data, *block); + + /*clear toolflags pointer when created for the first time*/ + int cd_tflags = data->typemap[CD_TOOLFLAGS]; + if (cd_tflags != -1) { + cd_tflags = data->layers[cd_tflags].offset; + + char *ptr = (char *)*block; + ptr += cd_tflags; + + MToolFlags *flags = (MToolFlags *)ptr; + flags->flag = nullptr; + } } else { *block = nullptr; } } +#endif void CustomData_bmesh_free_block_data_exclude_by_type(CustomData *data, void *block, @@ -3675,6 +4232,9 @@ void CustomData_bmesh_free_block_data_exclude_by_type(CustomData *data, if (block == nullptr) { return; } + + CustomData_bmesh_asan_unpoison(data, block); + for (int i = 0; i < data->totlayer; i++) { if ((CD_TYPE_AS_MASK(data->layers[i].type) & mask_exclude) == 0) { const LayerTypeInfo *typeInfo = layerType_getInfo(data->layers[i].type); @@ -3687,10 +4247,18 @@ void CustomData_bmesh_free_block_data_exclude_by_type(CustomData *data, memset(POINTER_OFFSET(block, offset), 0, typeInfo->size); } } + + CustomData_bmesh_asan_poison(data, block); } +#ifndef USE_BMESH_PAGE_CUSTOMDATA static void CustomData_bmesh_set_default_n(CustomData *data, void **block, const int n) { + if (ELEM(data->layers[n].type, CD_TOOLFLAGS, CD_MESH_ID)) { + /* do not do toolflags or mesh ids */ + return; + } + int offset = data->layers[n].offset; const LayerTypeInfo *typeInfo = layerType_getInfo(data->layers[n].type); @@ -3698,7 +4266,12 @@ static void CustomData_bmesh_set_default_n(CustomData *data, void **block, const typeInfo->set_default_value(POINTER_OFFSET(*block, offset), 1); } else { - memset(POINTER_OFFSET(*block, offset), 0, typeInfo->size); + if (typeInfo->use_default_data && data->layers[n].default_data) { + memcpy(POINTER_OFFSET(*block, offset), data->layers[n].default_data, typeInfo->size); + } + else { + memset(POINTER_OFFSET(*block, offset), 0, typeInfo->size); + } } } @@ -3712,6 +4285,121 @@ void CustomData_bmesh_set_default(CustomData *data, void **block) CustomData_bmesh_set_default_n(data, block, i); } } +#endif + +void CustomData_bmesh_swap_data_simple(CustomData *data, void **block1, void **block2) +{ + CustomData_bmesh_asan_unpoison(data, *block1); + CustomData_bmesh_asan_unpoison(data, *block2); + + int cd_id = data->typemap[CD_MESH_ID]; + cd_id = cd_id >= 0 ? data->layers[cd_id].offset : -1; + + void *tmp = *block1; + *block1 = *block2; + *block2 = tmp; + + int cd_tflags = data->typemap[CD_TOOLFLAGS]; + cd_tflags = cd_tflags != -1 ? data->layers[cd_tflags].offset : -1; + + // unswap ids if they exist + if (*block1 && *block2) { + if (cd_id != -1) { + int itmp; + int *id1 = (int *)(((char *)*block1) + cd_id); + int *id2 = (int *)(((char *)*block2) + cd_id); + + itmp = *id1; + *id1 = *id2; + *id2 = itmp; + } + + if (cd_tflags != -1) { + MToolFlags tmp; + MToolFlags *flags1 = (MToolFlags *)(((char *)*block1) + cd_tflags); + MToolFlags *flags2 = (MToolFlags *)(((char *)*block2) + cd_tflags); + + tmp = *flags1; + *flags1 = *flags2; + *flags2 = tmp; + } + } + + CustomData_bmesh_asan_poison(data, *block1); + CustomData_bmesh_asan_poison(data, *block2); +} + +void CustomData_bmesh_swap_data(CustomData *source, + CustomData *dest, + void *src_block, + void **dest_block) +{ + int src_i = 0; + int dest_i = 0; + int dest_i_start = 0; + + if (*dest_block == NULL) { + CustomData_bmesh_alloc_block(dest, dest_block); + + if (*dest_block) { + CustomData_bmesh_asan_unpoison(dest, *dest_block); + memset(*dest_block, 0, dest->totsize); + CustomData_bmesh_asan_poison(dest, *dest_block); + + CustomData_bmesh_set_default(dest, dest_block); + } + } + + for (src_i = 0; src_i < source->totlayer; src_i++) { + /* find the first dest layer with type >= the source type + * (this should work because layers are ordered by type) + */ + while (dest_i_start < dest->totlayer && + dest->layers[dest_i_start].type < source->layers[src_i].type) { + dest_i_start++; + } + + if (ELEM(source->layers[src_i].type, CD_MESH_ID, CD_TOOLFLAGS)) { + // do not swap ids or toolflags + continue; + } + + /* if there are no more dest layers, we're done */ + if (dest_i_start >= dest->totlayer) { + return; + } + + dest_i = dest_i_start; + + while (dest_i < dest->totlayer && dest->layers[dest_i].type == source->layers[src_i].type) { + /* if we found a matching layer, copy the data */ + if (dest->layers[dest_i].type == source->layers[src_i].type && + STREQ(dest->layers[dest_i].name, source->layers[src_i].name)) { + void *src_data = POINTER_OFFSET(src_block, source->layers[src_i].offset); + void *dest_data = POINTER_OFFSET(*dest_block, dest->layers[dest_i].offset); + const LayerTypeInfo *typeInfo = layerType_getInfo(source->layers[src_i].type); + const uint size = typeInfo->size; + + // swap data + char *bsrc = (char *)src_data; + char *bdst = (char *)dest_data; + + for (int j = 0; j < size; j++) { + char t = *bsrc; + *bsrc = *bdst; + *bdst = t; + + bsrc++; + bdst++; + } + + break; + } + + dest_i++; + } + } +} void CustomData_bmesh_copy_data_exclude_by_type(const CustomData *source, CustomData *dest, @@ -3723,57 +4411,79 @@ void CustomData_bmesh_copy_data_exclude_by_type(const CustomData *source, * would cause too much duplicate code, so add a check instead. */ const bool no_mask = (mask_exclude == 0); + /* + Note that we don't handle id/toolflag layers as a special case, + instead relying on CD_ELEM_NO_COPY semantics. + + This is so BM_data_layer_add can reallocate customdata blocks without + zeroing those two layers. + */ + if (*dest_block == nullptr) { CustomData_bmesh_alloc_block(dest, dest_block); + if (*dest_block) { + CustomData_bmesh_asan_unpoison(dest, *dest_block); memset(*dest_block, 0, dest->totsize); + CustomData_bmesh_asan_poison(dest, *dest_block); } } + for (int dest_i = 0; dest_i < dest->totlayer; dest_i++) { + CustomData_bmesh_set_default_n(dest, dest_block, dest_i); + } + /* copies a layer at a time */ - int dest_i = 0; + int dest_i_start = 0; + for (int src_i = 0; src_i < source->totlayer; src_i++) { /* 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 < source->layers[src_i].type) { - CustomData_bmesh_set_default_n(dest, dest_block, dest_i); - dest_i++; + while (dest_i_start < dest->totlayer && + dest->layers[dest_i_start].type < source->layers[src_i].type) { + dest_i_start++; } /* if there are no more dest layers, we're done */ - if (dest_i >= dest->totlayer) { + if (dest_i_start >= dest->totlayer) { return; } - /* if we found a matching layer, copy the data */ - if (dest->layers[dest_i].type == source->layers[src_i].type && - STREQ(dest->layers[dest_i].name, source->layers[src_i].name)) { - if (no_mask || ((CD_TYPE_AS_MASK(dest->layers[dest_i].type) & mask_exclude) == 0)) { - const void *src_data = POINTER_OFFSET(src_block, source->layers[src_i].offset); - void *dest_data = POINTER_OFFSET(*dest_block, dest->layers[dest_i].offset); - const LayerTypeInfo *typeInfo = layerType_getInfo(source->layers[src_i].type); - if (typeInfo->copy) { - typeInfo->copy(src_data, dest_data, 1); - } - else { - memcpy(dest_data, src_data, typeInfo->size); + int dest_i = dest_i_start; + + /*Previously this code was only checking one source layer against one destination. + Now it scans all the layers of that type. - joeedh + */ + while (dest_i < dest->totlayer && dest->layers[dest_i].type == source->layers[src_i].type) { + /* if we found a matching layer, copy the data */ + if (STREQ(dest->layers[dest_i].name, source->layers[src_i].name)) { + if (no_mask || ((CD_TYPE_AS_MASK(dest->layers[dest_i].type) & mask_exclude) == 0)) { + /* CD_FLAG_ELEM_NOCOPY is used to forcibly exclude copying CD_MESH_ID and CD_TOOLFLAGS + layers */ + + if (dest->layers[dest_i].flag & CD_FLAG_ELEM_NOCOPY) { + break; + } + + const void *src_data = POINTER_OFFSET(src_block, source->layers[src_i].offset); + void *dest_data = POINTER_OFFSET(*dest_block, dest->layers[dest_i].offset); + const LayerTypeInfo *typeInfo = layerType_getInfo(source->layers[src_i].type); + if (typeInfo->copy) { + typeInfo->copy(src_data, dest_data, 1); + } + else { + memcpy(dest_data, src_data, typeInfo->size); + } } + + break; } - /* 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++; } } - - while (dest_i < dest->totlayer) { - CustomData_bmesh_set_default_n(dest, dest_block, dest_i); - dest_i++; - } } void CustomData_bmesh_copy_data(const CustomData *source, @@ -4016,6 +4726,7 @@ void CustomData_bmesh_interp_n(CustomData *data, typeInfo->interp(src_blocks_ofs, weights, sub_weights, count, dst_block_ofs); } +#ifndef USE_BMESH_PAGE_CUSTOMDATA void CustomData_bmesh_interp(CustomData *data, const void **src_blocks, const float *weights, @@ -4050,6 +4761,34 @@ void CustomData_bmesh_interp(CustomData *data, for (int i = 0; i < data->totlayer; i++) { CustomDataLayer *layer = &data->layers[i]; const LayerTypeInfo *typeInfo = layerType_getInfo(layer->type); + + // ignore id and toolflag layers + if (ELEM(layer->type, CD_MESH_ID, CD_TOOLFLAGS)) { + continue; + } + + if (layer->flag & CD_FLAG_ELEM_NOINTERP) { + if (!(layer->flag & CD_FLAG_ELEM_NOCOPY)) { + if (typeInfo->copy) { + typeInfo->copy(POINTER_OFFSET(src_blocks[0], layer->offset), + POINTER_OFFSET(dst_block, layer->offset), + 1); + } + else { + if (layer->default_data && typeInfo->use_default_data) { + memcpy(POINTER_OFFSET(dst_block, layer->offset), layer->default_data, typeInfo->size); + } + else { + memcpy(POINTER_OFFSET(dst_block, layer->offset), + POINTER_OFFSET(src_blocks[0], layer->offset), + typeInfo->size); + } + } + } + + continue; + } + if (typeInfo->interp) { for (int j = 0; j < count; j++) { sources[j] = POINTER_OFFSET(src_blocks[j], layer->offset); @@ -4175,6 +4914,7 @@ void CustomData_from_bmesh_block(const CustomData *source, } } } +#endif void CustomData_file_write_info(int type, const char **r_struct_name, int *r_struct_num) { @@ -4189,7 +4929,7 @@ void CustomData_blend_write_prepare(CustomData &data, const Set &skip_names) { for (const CustomDataLayer &layer : Span(data.layers, data.totlayer)) { - if (layer.flag & CD_FLAG_NOCOPY) { + if (layer.flag & (CD_FLAG_NOCOPY | CD_FLAG_TEMPORARY)) { continue; } if (layer.anonymous_id != nullptr) { @@ -5052,6 +5792,12 @@ void CustomData_blend_write(BlendWriter *writer, writer, CustomDataLayer, data->totlayer, data->layers, layers_to_write.data()); for (const CustomDataLayer &layer : layers_to_write) { + const LayerTypeInfo *typeInfo = layerType_getInfo(layer.type); + + if (typeInfo->use_default_data && layer.default_data) { + BLO_write_struct_by_name(writer, typeInfo->structname, layer.default_data); + } + switch (layer.type) { case CD_MDEFORMVERT: BKE_defvert_blend_write(writer, count, static_cast(layer.data)); @@ -5162,6 +5908,14 @@ void CustomData_blend_read(BlendDataReader *reader, CustomData *data, const int int i = 0; while (i < data->totlayer) { CustomDataLayer *layer = &data->layers[i]; + const LayerTypeInfo *typeInfo = layerType_getInfo(layer->type); + + if (layer->default_data && typeInfo->use_default_data) { + BLO_read_data_address(reader, &layer->default_data); + } + else { + layer->default_data = nullptr; + } if (layer->flag & CD_FLAG_EXTERNAL) { layer->flag &= ~CD_FLAG_IN_MEMORY; @@ -5200,6 +5954,65 @@ void CustomData_blend_read(BlendDataReader *reader, CustomData *data, const int data->maxlayer = data->totlayer; CustomData_update_typemap(data); + CustomData_regen_active_refs(data); // check for corrupted active layer refs +} + +size_t CustomData_getTypeSize(eCustomDataType type) +{ + const LayerTypeInfo *info = layerType_getInfo(type); + + return info ? info->size : 0ULL; +} + +void CustomData_setDefaultData(eCustomDataType type, void *block, int totelem) +{ + const LayerTypeInfo *info = layerType_getInfo(type); + + if (info->set_default_value) { + info->set_default_value(block, totelem); + } + else { + memset(block, 0, info->size * totelem); + } +} + +void CustomData_freeData(eCustomDataType type, void *block, int totelem) +{ + const LayerTypeInfo *info = layerType_getInfo(type); + + if (info->free) { + info->free(block, totelem, info->size); + } +} + +#include "BLI_alloca.h" + +void CustomData_interpData(eCustomDataType type, + void *block, + int tot, + const void **srcs, + const float *ws, + const float *sub_ws) +{ + const LayerTypeInfo *info = layerType_getInfo(type); + + if (!info->interp) { + return; + } + + if (!ws) { + float *ws2 = static_cast(BLI_array_alloca(ws2, tot)); + float w = 1.0f / (float)tot; + + for (int i = 0; i < tot; i++) { + ws2[i] = w; + } + + info->interp(srcs, ws2, sub_ws, 1, block); + } + else { + info->interp(srcs, ws, sub_ws, 1, block); + } } /** \} */ @@ -5308,7 +6121,7 @@ eCustomDataType cpp_type_to_custom_data_type(const blender::CPPType &type) } // namespace blender::bke -size_t CustomData_get_elem_size(CustomDataLayer *layer) +int CustomData_get_elem_size(CustomDataLayer *layer) { return LAYERTYPEINFO[layer->type].size; } diff --git a/source/blender/blenkernel/intern/dyntopo.cc b/source/blender/blenkernel/intern/dyntopo.cc new file mode 100644 index 00000000000..672698cf221 --- /dev/null +++ b/source/blender/blenkernel/intern/dyntopo.cc @@ -0,0 +1,4030 @@ +#include "MEM_guardedalloc.h" + +#include "DNA_customdata_types.h" +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_modifier_types.h" + +#include "BLI_array.hh" +#include "BLI_index_range.hh" +#include "BLI_map.hh" +#include "BLI_set.hh" +#include "BLI_task.hh" +#include "BLI_vector.hh" + +#include "BLI_alloca.h" +#include "BLI_array.h" +#include "BLI_asan.h" +#include "BLI_bitmap.h" +#include "BLI_buffer.h" +#include "BLI_compiler_attrs.h" +#include "BLI_compiler_compat.h" +#include "BLI_ghash.h" +#include "BLI_heap.h" +#include "BLI_heap_minmax.h" +#include "BLI_heap_simple.h" +#include "BLI_linklist.h" +#include "BLI_math.h" +#include "BLI_memarena.h" +#include "BLI_rand.h" +#include "BLI_smallhash.h" +#include "BLI_task.h" +#include "BLI_utildefines.h" +#include "PIL_time.h" +#include "atomic_ops.h" + +#include "BKE_customdata.h" +#include "BKE_dyntopo.h" +#include "BKE_paint.h" +#include "BKE_pbvh.h" + +#include "bmesh.h" +#include "bmesh_log.h" + +#include "dyntopo_intern.hh" +#include "pbvh_intern.hh" + +#include + +namespace blender::dyntopo { + +using blender::float2; +using blender::float3; +using blender::float4; +using blender::IndexRange; +using blender::Map; +using blender::Set; +using blender::Vector; + +static void pbvh_split_edges(struct EdgeQueueContext *eq_ctx, + PBVH *pbvh, + BMesh *bm, + BMEdge **edges, + int totedge, + bool ignore_isolated_edges); +extern "C" { +void bmesh_disk_edge_append(BMEdge *e, BMVert *v); +void bmesh_radial_loop_append(BMEdge *e, BMLoop *l); +void bm_kill_only_edge(BMesh *bm, BMEdge *e); +void bm_kill_only_loop(BMesh *bm, BMLoop *l); +void bm_kill_only_face(BMesh *bm, BMFace *f); +} + +static bool edge_queue_test(struct EdgeQueueContext *eq_ctx, PBVH *pbvh, BMEdge *e); +static void edge_queue_create_local(struct EdgeQueueContext *eq_ctx, + PBVH *pbvh, + const float center[3], + const float view_normal[3], + float radius, + const bool use_frontface, + const bool use_projected, + PBVHTopologyUpdateMode local_mode); + +BLI_INLINE void surface_smooth_v_safe(PBVH *pbvh, BMVert *v, float fac) +{ + float co[3]; + float origco[3], origco1[3]; + float origno1[3]; + float tan[3]; + float tot = 0.0; + + MSculptVert *mv1 = BKE_PBVH_SCULPTVERT(pbvh->cd_sculpt_vert, v); + + if (mv1->stroke_id != pbvh->stroke_id) { + copy_v3_v3(origco1, v->co); + copy_v3_v3(origno1, v->no); + } + else { + copy_v3_v3(origco1, mv1->origco); + copy_v3_v3(origno1, dot_v3v3(mv1->origno, mv1->origno) == 0.0f ? v->no : mv1->origno); + } + + zero_v3(co); + zero_v3(origco); + + // this is a manual edge walk + + BMEdge *e = v->e; + if (!e) { + return; + } + + if (pbvh_boundary_needs_update_bmesh(pbvh, v)) { + pbvh_check_vert_boundary(pbvh, v); + } + + // pbvh_check_vert_boundary(pbvh, v); + + const int cd_sculpt_vert = pbvh->cd_sculpt_vert; + const int boundflag = BM_ELEM_CD_GET_INT(v, pbvh->cd_boundary_flag); + + const bool bound1 = boundflag & SCULPTVERT_SMOOTH_BOUNDARY; + + if (boundflag & SCULPTVERT_SMOOTH_CORNER) { + return; + } + + if (bound1) { + fac *= 0.1; + } + + do { + BMVert *v2 = e->v1 == v ? e->v2 : e->v1; + + // can't check for boundary here, thread + // pbvh_check_vert_boundary(pbvh, v2); + + MSculptVert *mv2 = BKE_PBVH_SCULPTVERT(cd_sculpt_vert, v2); + int boundflag2 = BM_ELEM_CD_GET_INT(v2, pbvh->cd_boundary_flag); + + const bool bound2 = boundflag2 & SCULPTVERT_SMOOTH_BOUNDARY; + + if (bound1 && !bound2) { + continue; + } + + sub_v3_v3v3(tan, v2->co, v->co); + float d = dot_v3v3(tan, v->no); + + madd_v3_v3fl(tan, v->no, -d * 0.99f); + add_v3_v3(co, tan); + + if (mv2->stroke_id == pbvh->stroke_id) { + sub_v3_v3v3(tan, mv2->origco, origco1); + } + else { + sub_v3_v3v3(tan, v2->co, origco1); + } + + d = dot_v3v3(tan, origno1); + madd_v3_v3fl(tan, origno1, -d * 0.99f); + add_v3_v3(origco, tan); + + tot += 1.0f; + + } while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e); + + if (tot == 0.0f) { + return; + } + + mul_v3_fl(co, 1.0f / tot); + mul_v3_fl(origco, 1.0f / tot); + + volatile float x = v->co[0], y = v->co[1], z = v->co[2]; + volatile float nx = x + co[0] * fac, ny = y + co[1] * fac, nz = z + co[2] * fac; + + // conflicts here should be pretty rare. + atomic_cas_float(&v->co[0], x, nx); + atomic_cas_float(&v->co[1], y, ny); + atomic_cas_float(&v->co[2], z, nz); + + // conflicts here should be pretty rare. + x = mv1->origco[0]; + y = mv1->origco[1]; + z = mv1->origco[2]; + + nx = x + origco[0] * fac; + ny = y + origco[1] * fac; + nz = z + origco[2] * fac; + + atomic_cas_float(&mv1->origco[0], x, nx); + atomic_cas_float(&mv1->origco[1], y, ny); + atomic_cas_float(&mv1->origco[2], z, nz); + + PBVH_CHECK_NAN(mv1->origco); + PBVH_CHECK_NAN(v->co); + // atomic_cas_int32(&mv1->stroke_id, stroke_id, pbvh->stroke_id); +} + +static void pbvh_kill_vert(PBVH *pbvh, BMVert *v, bool log_vert, bool log_edges) +{ + BMEdge *e = v->e; + bm_logstack_push(); + + if (log_edges) { + if (e) { + do { + BM_log_edge_removed(pbvh->header.bm, pbvh->bm_log, e); + } while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e); + } + } + + if (log_vert) { + BM_log_vert_removed(pbvh->header.bm, pbvh->bm_log, v); + } + +#ifdef USE_NEW_IDMAP + BM_idmap_release(pbvh->bm_idmap, (BMElem *)v, true); +#endif + BM_vert_kill(pbvh->header.bm, v); + bm_logstack_pop(); +} + +/** + * A version of #BM_face_exists, optimized for triangles + * when we know the loop and the opposite vertex. + * + * Check if any triangle is formed by (l_radial_first->v, l_radial_first->next->v, v_opposite), + * at either winding (since its a triangle no special checks are needed). + * + *
+ * l_radial_first->v & l_radial_first->next->v
+ * +---+
+ * |  /
+ * | /
+ * + v_opposite
+ * 
+ * + * Its assumed that \a l_radial_first is never forming the target face. + */ +static BMFace *bm_face_exists_tri_from_loop_vert(BMLoop *l_radial_first, BMVert *v_opposite) +{ + BLI_assert( + !ELEM(v_opposite, l_radial_first->v, l_radial_first->next->v, l_radial_first->prev->v)); + if (l_radial_first->radial_next != l_radial_first) { + BMLoop *l_radial_iter = l_radial_first->radial_next; + do { + BLI_assert(l_radial_iter->f->len == 3); + if (l_radial_iter->prev->v == v_opposite) { + return l_radial_iter->f; + } + } while ((l_radial_iter = l_radial_iter->radial_next) != l_radial_first); + } + return nullptr; +} + +static BMVert *pbvh_bmesh_vert_create(PBVH *pbvh, + int node_index, + const float co[3], + const float no[3], + BMVert *v_example, + const int cd_vert_mask_offset) +{ + PBVHNode *node = &pbvh->nodes[node_index]; + + BLI_assert((pbvh->totnode == 1 || node_index) && node_index <= pbvh->totnode); + + /* avoid initializing customdata because its quite involved */ + BMVert *v = BM_vert_create(pbvh->header.bm, co, nullptr, BM_CREATE_NOP); + MSculptVert *mv = BKE_PBVH_SCULPTVERT(pbvh->cd_sculpt_vert, v); + + pbvh_boundary_update_bmesh(pbvh, v); + MV_ADD_FLAG(mv, SCULPTVERT_NEED_DISK_SORT | SCULPTVERT_NEED_VALENCE); + + if (v_example) { + v->head.hflag = v_example->head.hflag; + + CustomData_bmesh_copy_data( + &pbvh->header.bm->vdata, &pbvh->header.bm->vdata, v_example->head.data, &v->head.data); + + /* This value is logged below */ + copy_v3_v3(v->no, no); + + // keep MSculptVert copied from v_example as-is + } + else { + MSculptVert *mv = BKE_PBVH_SCULPTVERT(pbvh->cd_sculpt_vert, v); + + copy_v3_v3(mv->origco, co); + copy_v3_v3(mv->origno, no); + mv->origmask = 0.0f; + + /* This value is logged below */ + copy_v3_v3(v->no, no); + } + + BLI_table_gset_insert(node->bm_unique_verts, v); + BM_ELEM_CD_SET_INT(v, pbvh->cd_vert_node_offset, node_index); + + node->flag |= PBVH_UpdateDrawBuffers | PBVH_UpdateBB | PBVH_UpdateTris | PBVH_UpdateOtherVerts; + + /* Log the new vertex */ + BM_log_vert_added(pbvh->header.bm, pbvh->bm_log, v); + v->head.index = pbvh->header.bm->totvert; // set provisional index + + return v; +} + +static BMFace *bmesh_face_create_edge_log(PBVH *pbvh, + BMVert *v_tri[3], + BMEdge *e_tri[3], + const BMFace *f_example) +{ + BMFace *f; + + if (!e_tri) { + BMEdge *e_tri2[3]; + + for (int i = 0; i < 3; i++) { + BMVert *v1 = v_tri[i]; + BMVert *v2 = v_tri[(i + 1) % 3]; + + BMEdge *e = BM_edge_exists(v1, v2); + + if (!e) { + e = BM_edge_create(pbvh->header.bm, v1, v2, nullptr, BM_CREATE_NOP); + BM_log_edge_added(pbvh->header.bm, pbvh->bm_log, e); + } + + e_tri2[i] = e; + } + + // f = BM_face_create_verts(pbvh->header.bm, v_tri, 3, f_example, BM_CREATE_NOP, true); + f = BM_face_create(pbvh->header.bm, v_tri, e_tri2, 3, f_example, BM_CREATE_NOP); + } + else { + f = BM_face_create(pbvh->header.bm, v_tri, e_tri, 3, f_example, BM_CREATE_NOP); + } + + if (f_example) { + f->head.hflag = f_example->head.hflag; + } + + return f; +} + +/** + * \note Callers are responsible for checking if the face exists before adding. + */ +static BMFace *pbvh_bmesh_face_create(PBVH *pbvh, + int node_index, + BMVert *v_tri[3], + BMEdge *e_tri[3], + const BMFace *f_example, + bool ensure_verts, + bool log_face) +{ + PBVHNode *node = &pbvh->nodes[node_index]; + + /* ensure we never add existing face */ + BLI_assert(!BM_face_exists(v_tri, 3)); + + BMFace *f = bmesh_face_create_edge_log(pbvh, v_tri, e_tri, f_example); + + BLI_table_gset_insert(node->bm_faces, f); + BM_ELEM_CD_SET_INT(f, pbvh->cd_face_node_offset, node_index); + + /* mark node for update */ + node->flag |= PBVH_UpdateDrawBuffers | PBVH_UpdateNormals | PBVH_UpdateTris | + PBVH_UpdateOtherVerts; + node->flag &= ~PBVH_FullyHidden; + + /* Log the new face */ + if (log_face) { + BM_log_face_added(pbvh->header.bm, pbvh->bm_log, f); + } + + int cd_vert_node = pbvh->cd_vert_node_offset; + + if (ensure_verts) { + BMLoop *l = f->l_first; + do { + int ni = BM_ELEM_CD_GET_INT(l->v, cd_vert_node); + + if (ni == DYNTOPO_NODE_NONE) { + BLI_table_gset_add(node->bm_unique_verts, l->v); + BM_ELEM_CD_SET_INT(l->v, cd_vert_node, node_index); + + node->flag |= PBVH_UpdateDrawBuffers | PBVH_UpdateBB | PBVH_UpdateTris | + PBVH_UpdateOtherVerts; + } + + pbvh_boundary_update_bmesh(pbvh, l->v); + + MSculptVert *mv = BKE_PBVH_SCULPTVERT(pbvh->cd_sculpt_vert, l->v); + MV_ADD_FLAG(mv, SCULPTVERT_NEED_DISK_SORT | SCULPTVERT_NEED_VALENCE); + + l = l->next; + } while (l != f->l_first); + } + else { + BMLoop *l = f->l_first; + do { + pbvh_boundary_update_bmesh(pbvh, l->v); + + MSculptVert *mv = BKE_PBVH_SCULPTVERT(pbvh->cd_sculpt_vert, l->v); + MV_ADD_FLAG(mv, SCULPTVERT_NEED_DISK_SORT | SCULPTVERT_NEED_VALENCE); + } while ((l = l->next) != f->l_first); + } + + return f; +} + +BMVert *BKE_pbvh_vert_create_bmesh( + PBVH *pbvh, float co[3], float no[3], PBVHNode *node, BMVert *v_example) +{ + if (!node) { + for (int i = 0; i < pbvh->totnode; i++) { + PBVHNode *node2 = pbvh->nodes + i; + + if (!(node2->flag & PBVH_Leaf)) { + continue; + } + + // ensure we have at least some node somewhere picked + node = node2; + + bool ok = true; + + for (int j = 0; j < 3; j++) { + if (co[j] < node2->vb.bmin[j] || co[j] >= node2->vb.bmax[j]) { + continue; + } + } + + if (ok) { + break; + } + } + } + + BMVert *v; + + if (!node) { + printf("possible pbvh error\n"); + v = BM_vert_create(pbvh->header.bm, co, v_example, BM_CREATE_NOP); + BM_ELEM_CD_SET_INT(v, pbvh->cd_vert_node_offset, DYNTOPO_NODE_NONE); + + pbvh_boundary_update_bmesh(pbvh, v); + MSculptVert *mv = (MSculptVert *)BM_ELEM_CD_GET_VOID_P(v, pbvh->cd_sculpt_vert); + MV_ADD_FLAG(mv, SCULPTVERT_NEED_VALENCE | SCULPTVERT_NEED_DISK_SORT); + + copy_v3_v3(mv->origco, co); + + return v; + } + + return pbvh_bmesh_vert_create( + pbvh, node - pbvh->nodes, co, no, v_example, pbvh->cd_vert_mask_offset); +} + +PBVHNode *BKE_pbvh_node_from_face_bmesh(PBVH *pbvh, BMFace *f) +{ + return pbvh->nodes + BM_ELEM_CD_GET_INT(f, pbvh->cd_face_node_offset); +} + +BMFace *BKE_pbvh_face_create_bmesh(PBVH *pbvh, + BMVert *v_tri[3], + BMEdge *e_tri[3], + const BMFace *f_example) +{ + int ni = DYNTOPO_NODE_NONE; + + for (int i = 0; i < 3; i++) { + BMVert *v = v_tri[i]; + BMLoop *l; + BMIter iter; + + BM_ITER_ELEM (l, &iter, v, BM_LOOPS_OF_VERT) { + int ni2 = BM_ELEM_CD_GET_INT(l->f, pbvh->cd_face_node_offset); + if (ni2 != DYNTOPO_NODE_NONE) { + ni = ni2; + break; + } + } + } + + if (ni == DYNTOPO_NODE_NONE) { + BMFace *f; + + // no existing nodes? find one + for (int i = 0; i < pbvh->totnode; i++) { + PBVHNode *node = pbvh->nodes + i; + + if (!(node->flag & PBVH_Leaf)) { + continue; + } + + for (int j = 0; j < 3; j++) { + BMVert *v = v_tri[j]; + + bool ok = true; + + for (int k = 0; k < 3; k++) { + if (v->co[k] < node->vb.bmin[k] || v->co[k] >= node->vb.bmax[k]) { + ok = false; + } + } + + if (ok && + (ni == DYNTOPO_NODE_NONE || BLI_table_gset_len(node->bm_faces) < pbvh->leaf_limit)) { + ni = i; + break; + } + } + + if (ni != DYNTOPO_NODE_NONE) { + break; + } + } + + if (ni == DYNTOPO_NODE_NONE) { + // empty pbvh? + printf("possibly pbvh error\n"); + + f = bmesh_face_create_edge_log(pbvh, v_tri, e_tri, f_example); + + BM_ELEM_CD_SET_INT(f, pbvh->cd_face_node_offset, DYNTOPO_NODE_NONE); + + return f; + } + } + + return pbvh_bmesh_face_create(pbvh, ni, v_tri, e_tri, f_example, true, true); +} + +#define pbvh_bmesh_node_vert_use_count_is_equal(pbvh, node, v, n) \ + (pbvh_bmesh_node_vert_use_count_at_most(pbvh, node, v, (n) + 1) == n) + +static int pbvh_bmesh_node_vert_use_count_at_most(PBVH *pbvh, + PBVHNode *node, + BMVert *v, + const int count_max) +{ + int count = 0; + BMFace *f; + + BM_FACES_OF_VERT_ITER_BEGIN (f, v) { + PBVHNode *f_node = pbvh_bmesh_node_from_face(pbvh, f); + if (f_node == node) { + count++; + if (count == count_max) { + return count; + } + } + } + BM_FACES_OF_VERT_ITER_END; + + return count; +} + +/* Return a node that uses vertex 'v' other than its current owner */ +static PBVHNode *pbvh_bmesh_vert_other_node_find(PBVH *pbvh, BMVert *v) +{ + PBVHNode *current_node = pbvh_bmesh_node_from_vert(pbvh, v); + BMFace *f; + + BM_FACES_OF_VERT_ITER_BEGIN (f, v) { + PBVHNode *f_node = pbvh_bmesh_node_from_face(pbvh, f); + + if (f_node != current_node) { + return f_node; + } + } + BM_FACES_OF_VERT_ITER_END; + + return nullptr; +} + +static void pbvh_bmesh_vert_ownership_transfer(PBVH *pbvh, PBVHNode *new_owner, BMVert *v) +{ + PBVHNode *current_owner = pbvh_bmesh_node_from_vert(pbvh, v); + /* mark node for update */ + + if (current_owner) { + current_owner->flag |= PBVH_UpdateDrawBuffers | PBVH_UpdateBB; + + BLI_assert(current_owner != new_owner); + + /* Remove current ownership */ + BLI_table_gset_remove(current_owner->bm_unique_verts, v, nullptr); + } + + /* Set new ownership */ + BM_ELEM_CD_SET_INT(v, pbvh->cd_vert_node_offset, new_owner - pbvh->nodes); + BLI_table_gset_insert(new_owner->bm_unique_verts, v); + + /* mark node for update */ + new_owner->flag |= PBVH_UpdateDrawBuffers | PBVH_UpdateBB | PBVH_UpdateOtherVerts; +} + +void pbvh_bmesh_vert_remove(PBVH *pbvh, BMVert *v) +{ + /* never match for first time */ + int f_node_index_prev = DYNTOPO_NODE_NONE; + const int updateflag = PBVH_UpdateDrawBuffers | PBVH_UpdateBB | PBVH_UpdateTris | + PBVH_UpdateNormals | PBVH_UpdateOtherVerts; + + PBVHNode *v_node = pbvh_bmesh_node_from_vert(pbvh, v); + + if (v_node) { + BLI_table_gset_remove(v_node->bm_unique_verts, v, nullptr); + v_node->flag |= (PBVHNodeFlags)updateflag; + } + + BM_ELEM_CD_SET_INT(v, pbvh->cd_vert_node_offset, DYNTOPO_NODE_NONE); + + /* Have to check each neighboring face's node */ + BMFace *f; + BM_FACES_OF_VERT_ITER_BEGIN (f, v) { + const int f_node_index = pbvh_bmesh_node_index_from_face(pbvh, f); + + if (f_node_index == DYNTOPO_NODE_NONE) { + continue; + } + + /* faces often share the same node, + * quick check to avoid redundant #BLI_table_gset_remove calls */ + if (f_node_index_prev != f_node_index) { + f_node_index_prev = f_node_index; + + PBVHNode *f_node = &pbvh->nodes[f_node_index]; + f_node->flag |= (PBVHNodeFlags)updateflag; // flag update of bm_other_verts + + BLI_assert(!BLI_table_gset_haskey(f_node->bm_unique_verts, v)); + } + } + BM_FACES_OF_VERT_ITER_END; +} + +void pbvh_bmesh_face_remove( + PBVH *pbvh, BMFace *f, bool log_face, bool check_verts, bool ensure_ownership_transfer) +{ + PBVHNode *f_node = pbvh_bmesh_node_from_face(pbvh, f); + + if (!f_node || !(f_node->flag & PBVH_Leaf)) { + printf("pbvh corruption\n"); + fflush(stdout); + return; + } + + bm_logstack_push(); + + /* Check if any of this face's vertices need to be removed + * from the node */ + if (check_verts) { + BMLoop *l_first = BM_FACE_FIRST_LOOP(f); + BMLoop *l_iter = l_first; + do { + BMVert *v = l_iter->v; + if (pbvh_bmesh_node_vert_use_count_is_equal(pbvh, f_node, v, 1)) { + if (BM_ELEM_CD_GET_INT(v, pbvh->cd_vert_node_offset) == f_node - pbvh->nodes) { + // if (BLI_table_gset_haskey(f_node->bm_unique_verts, v)) { + /* Find a different node that uses 'v' */ + PBVHNode *new_node; + + new_node = pbvh_bmesh_vert_other_node_find(pbvh, v); + // BLI_assert(new_node || BM_vert_face_count_is_equal(v, 1)); + + if (new_node) { + pbvh_bmesh_vert_ownership_transfer(pbvh, new_node, v); + } + else if (ensure_ownership_transfer && !BM_vert_face_count_is_equal(v, 1)) { + pbvh_bmesh_vert_remove(pbvh, v); + + f_node->flag |= PBVH_RebuildNodeVerts | PBVH_UpdateOtherVerts; + // printf("failed to find new_node\n"); + } + } + } + } while ((l_iter = l_iter->next) != l_first); + } + + /* Remove face from node and top level */ + BLI_table_gset_remove(f_node->bm_faces, f, nullptr); + BM_ELEM_CD_SET_INT(f, pbvh->cd_face_node_offset, DYNTOPO_NODE_NONE); + + /* Log removed face */ + if (log_face) { + BM_log_face_removed(pbvh->header.bm, pbvh->bm_log, f); + } + + /* mark node for update */ + f_node->flag |= PBVH_UpdateDrawBuffers | PBVH_UpdateNormals | PBVH_UpdateTris | + PBVH_UpdateOtherVerts; + + bm_logstack_pop(); +} + +/****************************** EdgeQueue *****************************/ + +static float maskcb_get(EdgeQueueContext *eq_ctx, BMVert *v1, BMVert *v2) +{ + if (eq_ctx->mask_cb) { + PBVHVertRef sv1 = {(intptr_t)v1}; + PBVHVertRef sv2 = {(intptr_t)v2}; + + float w1 = eq_ctx->mask_cb(sv1, eq_ctx->mask_cb_data); + float w2 = eq_ctx->mask_cb(sv2, eq_ctx->mask_cb_data); + + // float limit = 0.5; + // if (w1 > limit || w2 > limit) { + return min_ff(w1, w2); + //} + + // return (w1 + w2) * 0.5f; + } + + return 1.0f; +} + +BLI_INLINE float calc_edge_length(EdgeQueueContext *eq_ctx, BMVert *v1, BMVert *v2) +{ + return len_squared_v3v3(v1->co, v2->co); +} + +BLI_INLINE float calc_weighted_length(EdgeQueueContext *eq_ctx, BMVert *v1, BMVert *v2, float sign) +{ + float w = 1.0 - maskcb_get(eq_ctx, v1, v2); + float len = len_squared_v3v3(v1->co, v2->co); + + w = 1.0 + w * sign; + + return len * w; +} + +static void edge_queue_insert_unified(EdgeQueueContext *eq_ctx, BMEdge *e) +{ + if (/*BLI_mm_heap_len(eq_ctx->heap_mm) < eq_ctx->max_heap_mm &&*/ !(e->head.hflag & + BM_ELEM_TAG)) { + float lensqr = len_squared_v3v3(e->v1->co, e->v2->co); + + float len = sqrtf(lensqr); + + eq_ctx->avg_elen += len; + eq_ctx->min_elen = min_ff(eq_ctx->min_elen, len); + eq_ctx->max_elen = max_ff(eq_ctx->max_elen, len); + eq_ctx->totedge++; + + // lensqr += (BLI_thread_frand(0) - 0.5f) * 0.1 * eq_ctx->limit_mid; + + BLI_mm_heap_insert(eq_ctx->heap_mm, lensqr, e); + e->head.hflag |= BM_ELEM_TAG; + } +} + +static void edge_queue_insert_val34_vert(EdgeQueueContext *eq_ctx, BMVert *v) +{ + // BLI_table_gset_add(eq_ctx->used_verts, v); + eq_ctx->tot_used_verts++; + + if (!eq_ctx->used_verts || eq_ctx->tot_used_verts >= eq_ctx->used_verts_size) { + int newlen = (eq_ctx->tot_used_verts + 1); + newlen += newlen >> 1; + + eq_ctx->used_verts_size = newlen; + if (eq_ctx->used_verts) { + eq_ctx->used_verts = (BMVert **)MEM_reallocN(eq_ctx->used_verts, newlen * sizeof(BMVert *)); + } + else { + eq_ctx->used_verts = (BMVert **)MEM_malloc_arrayN( + newlen, sizeof(BMVert *), "eq_ctx->used_verts"); + } + } + + eq_ctx->used_verts[eq_ctx->tot_used_verts - 1] = v; +} + +BLI_INLINE float calc_curvature_weight(EdgeQueueContext *eq_ctx, BMVert *v1, BMVert *v2) +{ +#ifdef WITH_ADAPTIVE_CURVATURE + MSculptVert *mv1 = BKE_PBVH_SCULPTVERT(eq_ctx->cd_sculpt_vert, v1); + MSculptVert *mv2 = BKE_PBVH_SCULPTVERT(eq_ctx->cd_sculpt_vert, v2); + + float c1 = (float)mv1->curv / 65535.0f; + float c2 = (float)mv2->curv / 65535.0f; + float fac = 1.0f + powf((c1 + c2) * 100.0, 4.0f); + fac = min_ff(fac, 4.0f); + + return fac; +#else + return 1.0; +#endif +} + +static bool edge_queue_vert_in_sphere(const EdgeQueueContext *eq_ctx, BMVert *v) +{ + /* Check if triangle intersects the sphere */ + return len_squared_v3v3(eq_ctx->center, v->co) <= eq_ctx->radius_squared; +} + +/* + Profiling revealed the accurate distance to tri in blenlib was too slow, + so we use a simpler version here + */ +/* reduce script + +on factor; +off period; + +load_package "avector"; + +comment: origin at p; + +p := avec(0, 0, 0); +n :=- avec(nx, ny, nz); +v1 := avec(v1x, v1y, v1z); +v2 := avec(v2x, v2y, v2z); +v3 := avec(v3x, v3y, v3z); + +comment: -((p - v1) dot n);simplified to this; +fac := v1 dot n; + +co := fac*n; + +a := co - v1; +b := co - v2; +c := co - v3; + +a := v1; +b := v2; +c := v3; + +t1 := a cross b; +t2 := b cross c; +t3 := c cross a; + +on fort; +w1 := t1 dot n; +w2 := t2 dot n; +w3 := t3 dot n; +off fort; + +inside := sign(a dot n) + sign(b dot n) + sign(c dot n); + + +*/ +static bool point_in_tri_v3(float p[3], float v1[3], float v2[3], float v3[3], float n[3]) +{ + float t1[3], t2[3], t3[3]; + sub_v3_v3v3(t1, v1, p); + sub_v3_v3v3(t2, v2, p); + sub_v3_v3v3(t3, v3, p); + + float c1[3], c2[3], c3[3]; + cross_v3_v3v3(c1, t1, t2); + cross_v3_v3v3(c2, t2, t3); + cross_v3_v3v3(c3, t3, t1); + + bool w1 = dot_v3v3(c1, n) >= 0.0f; + bool w2 = dot_v3v3(c2, n) >= 0.0f; + bool w3 = dot_v3v3(c3, n) >= 0.0f; + + return w1 == w2 && w2 == w3; + +#if 0 + const float nx = n[0], ny = n[1], nz = n[2]; + + float v1x = v1[0] - p[0], v1y = v1[1] - p[1], v1z = v1[2] - p[2]; + float v2x = v2[0] - p[0], v2y = v2[1] - p[1], v2z = v2[2] - p[2]; + float v3x = v3[0] - p[0], v3y = v3[1] - p[1], v3z = v3[2] - p[2]; + + const float w1 = -(nx * v1y * v2z - nx * v1z * v2y - ny * v1x * v2z + ny * v1z * v2x + + nz * v1x * v2y - nz * v1y * v2x); + const float w2 = -(nx * v2y * v3z - nx * v2z * v3y - ny * v2x * v3z + ny * v2z * v3x + + nz * v2x * v3y - nz * v2y * v3x); + const float w3 = nx * v1y * v3z - nx * v1z * v3y - ny * v1x * v3z + ny * v1z * v3x + + nz * v1x * v3y - nz * v1y * v3x; + return !((w1 >= 0.0f) && (w2 >= 0.0f) && (w3 >= 0.0f)); +#endif +} + +static float dist_to_tri_sphere_simple( + float p[3], float v1[3], float v2[3], float v3[3], float n[3]) +{ + float co[3]; + float t1[3], t2[3], t3[3]; + + if (dot_v3v3(n, n) == 0.0f) { + normal_tri_v3(n, v1, v2, v3); + } + + if (point_in_tri_v3(p, v1, v2, v3, n)) { + sub_v3_v3v3(co, p, v2); + + float dist = dot_v3v3(co, n); + return dist * dist; + } + + sub_v3_v3v3(co, p, v1); + madd_v3_v3fl(co, n, -dot_v3v3(n, co)); + + sub_v3_v3v3(t1, v1, co); + sub_v3_v3v3(t2, v2, co); + sub_v3_v3v3(t3, v3, co); + + float dis = len_squared_v3v3(p, v1); + dis = fmin(dis, len_squared_v3v3(p, v2)); + dis = fmin(dis, len_squared_v3v3(p, v3)); + + add_v3_v3v3(co, v1, v2); + mul_v3_fl(co, 0.5f); + dis = fmin(dis, len_squared_v3v3(p, co)); + + add_v3_v3v3(co, v2, v3); + mul_v3_fl(co, 0.5f); + dis = fmin(dis, len_squared_v3v3(p, co)); + + add_v3_v3v3(co, v3, v1); + mul_v3_fl(co, 0.5f); + dis = fmin(dis, len_squared_v3v3(p, co)); + + add_v3_v3v3(co, v1, v2); + add_v3_v3(co, v3); + mul_v3_fl(co, 1.0f / 3.0f); + dis = fmin(dis, len_squared_v3v3(p, co)); + + return dis; +} + +static bool edge_queue_tri_in_sphere(const EdgeQueueContext *q, BMVert *vs[3], float no[3]) +{ +#if 0 + float cent[3]; + + zero_v3(cent); + add_v3_v3(cent, l->v->co); + add_v3_v3(cent, l->next->v->co); + add_v3_v3(cent, l->prev->v->co); + + mul_v3_fl(cent, 1.0f / 3.0f); + return len_squared_v3v3(cent, q->center) < q->radius_squared; +#endif + + /* Check if triangle intersects the sphere */ +#if 1 + float dis = dist_to_tri_sphere_simple( + (float *)q->center, (float *)vs[0]->co, (float *)vs[1]->co, (float *)vs[2]->co, (float *)no); +#else + float dis = len_squared_v3v3(q->center, l->v->co); +#endif + + return dis <= q->radius_squared; +} + +static bool edge_queue_tri_in_circle(const EdgeQueueContext *q, BMVert *v_tri[3], float no[3]) +{ + float c[3]; + float tri_proj[3][3]; + + project_plane_normalized_v3_v3v3(tri_proj[0], v_tri[0]->co, q->view_normal); + project_plane_normalized_v3_v3v3(tri_proj[1], v_tri[1]->co, q->view_normal); + project_plane_normalized_v3_v3v3(tri_proj[2], v_tri[2]->co, q->view_normal); + + closest_on_tri_to_point_v3(c, q->center_proj, tri_proj[0], tri_proj[1], tri_proj[2]); + + /* Check if triangle intersects the sphere */ + return len_squared_v3v3(q->center_proj, c) <= q->radius_squared; +} + +typedef struct EdgeQueueThreadData { + PBVH *pbvh; + PBVHNode *node; + BMEdge **edges; + EdgeQueueContext *eq_ctx; + int totedge; + int size; + bool is_collapse; + int seed; +} EdgeQueueThreadData; + +static void edge_thread_data_insert(EdgeQueueThreadData *tdata, BMEdge *e) +{ + if (tdata->size <= tdata->totedge) { + tdata->size = (tdata->totedge + 1) << 1; + if (!tdata->edges) { + tdata->edges = (BMEdge **)MEM_mallocN(sizeof(void *) * tdata->size, + "edge_thread_data_insert"); + } + else { + tdata->edges = (BMEdge **)MEM_reallocN(tdata->edges, sizeof(void *) * tdata->size); + } + } + + BMElem elem; + memcpy(&elem, (BMElem *)e, sizeof(BMElem)); + + elem.head.hflag = e->head.hflag | BM_ELEM_TAG; + int64_t iold = *((int64_t *)&e->head.index); + int64_t inew = *((int64_t *)&elem.head.index); + + atomic_cas_int64((int64_t *)&e->head.index, iold, inew); + + tdata->edges[tdata->totedge] = e; + tdata->totedge++; +} + +static bool edge_queue_vert_in_circle(const EdgeQueueContext *eq_ctx, BMVert *v) +{ + float c[3]; + + project_plane_normalized_v3_v3v3(c, v->co, eq_ctx->view_normal); + + return len_squared_v3v3(eq_ctx->center_proj, c) <= eq_ctx->radius_squared; +} + +static void long_edge_queue_edge_add_recursive(EdgeQueueContext *eq_ctx, + BMLoop *l_edge, + BMLoop *l_end, + const float len_sq, + float limit_len, + int depth) +{ + BLI_assert(len_sq > square_f(limit_len)); + + if (depth > DEPTH_START_LIMIT && eq_ctx->use_view_normal) { + if (dot_v3v3(l_edge->f->no, eq_ctx->view_normal) < 0.0f) { + return; + } + } + + edge_queue_insert_unified(eq_ctx, l_edge->e); + + if ((l_edge->radial_next != l_edge)) { + const float len_sq_cmp = len_sq * EVEN_EDGELEN_THRESHOLD; + + limit_len *= EVEN_GENERATION_SCALE; + const float limit_len_sq = square_f(limit_len); + + BMLoop *l_iter = l_edge; + do { + BMLoop *l_adjacent[2] = {l_iter->next, l_iter->prev}; + for (int i = 0; i < (int)ARRAY_SIZE(l_adjacent); i++) { + float len_sq_other = calc_weighted_length( + eq_ctx, l_adjacent[i]->e->v1, l_adjacent[i]->e->v2, -1.0f); + + bool insert_ok = len_sq_other > max_ff(len_sq_cmp, limit_len_sq); + if (!insert_ok) { + continue; + } + + long_edge_queue_edge_add_recursive( + eq_ctx, l_adjacent[i]->radial_next, l_adjacent[i], len_sq_other, limit_len, depth + 1); + } + } while ((l_iter = l_iter->radial_next) != l_end); + } +} + +static void long_edge_queue_edge_add_recursive_2(EdgeQueueThreadData *tdata, + BMLoop *l_edge, + BMLoop *l_end, + const float len_sq, + float limit_len, + int depth, + bool insert) +{ + BLI_assert(len_sq > square_f(limit_len)); + + if (l_edge->e->head.hflag & BM_ELEM_TAG) { + // return; + } + +#ifdef USE_EDGEQUEUE_FRONTFACE + if (depth > DEPTH_START_LIMIT && tdata->eq_ctx->use_view_normal) { + if (dot_v3v3(l_edge->f->no, tdata->eq_ctx->view_normal) < 0.0f) { + return; + } + } +#endif + + if (insert) { + edge_thread_data_insert(tdata, l_edge->e); + } + + if ((l_edge->radial_next != l_edge)) { + const float len_sq_cmp = len_sq * EVEN_EDGELEN_THRESHOLD; + + limit_len *= EVEN_GENERATION_SCALE; + const float limit_len_sq = square_f(limit_len); + + BMLoop *l_iter = l_edge; + do { + BMLoop *l_adjacent[2] = {l_iter->next, l_iter->prev}; + for (int i = 0; i < (int)ARRAY_SIZE(l_adjacent); i++) { + float len_sq_other = calc_edge_length( + tdata->eq_ctx, l_adjacent[i]->e->v1, l_adjacent[i]->e->v2); + + bool insert_ok = len_sq_other > max_ff(len_sq_cmp, limit_len_sq); +#ifdef EVEN_NO_TEST_DEPTH_LIMIT + if (!insert_ok && depth >= EVEN_NO_TEST_DEPTH_LIMIT) { + continue; + } +#else + if (!insert_ok) { + continue; + } +#endif + + long_edge_queue_edge_add_recursive_2(tdata, + l_adjacent[i]->radial_next, + l_adjacent[i], + len_sq_other, + limit_len, + depth + 1, + insert_ok); + } + } while ((l_iter = l_iter->radial_next) != l_end); + } +} + +BLI_INLINE int dyntopo_thread_rand(int seed) +{ + // glibc + const uint32_t multiplier = 1103515245; + const uint32_t addend = 12345; + const uint32_t mask = (1 << 30) - 1; + + return (seed * multiplier + addend) & mask; +} + +static void unified_edge_queue_task_cb(void *__restrict userdata, + const int n, + const TaskParallelTLS *__restrict tls) +{ + EdgeQueueThreadData *tdata = ((EdgeQueueThreadData *)userdata) + n; + PBVH *pbvh = tdata->pbvh; + PBVHNode *node = tdata->node; + EdgeQueueContext *eq_ctx = tdata->eq_ctx; + RNG *rng = BLI_rng_new(POINTER_AS_UINT(tdata)); + + int seed = tdata->seed + n; + + BMFace *f; + bool do_smooth = eq_ctx->surface_smooth_fac > 0.0f; + + BKE_pbvh_bmesh_check_tris(pbvh, node); + int ni = node - pbvh->nodes; + + const char facetag = BM_ELEM_TAG_ALT; + +#if 1 +# if 0 + // try to be nice to branch predictor + int off = (seed = dyntopo_thread_rand(seed)); + int stepi = off & 65535; +# endif + /* + we care more about convergence to accurate results + then accuracy in any individual runs. profiling + has shown this loop overwhelms the L3 cache, + so randomly skip bits of it. + */ + TGSET_ITER (f, node->bm_faces) { + BMLoop *l = f->l_first; + + f->head.hflag &= ~facetag; + +# if 0 + if ((stepi++) & 3) { + continue; + } +# else + if ((seed = dyntopo_thread_rand(seed)) & 3) { + continue; + } +# endif + do { + /* kind of tricky to atomicly update flags here. . . */ + BMEdge edge = *l->e; + edge.head.hflag &= ~BM_ELEM_TAG; + + intptr_t *t1 = (intptr_t *)&edge.head.index; + intptr_t *t2 = (intptr_t *)&l->e->head.index; + + atomic_cas_int64(t2, *t2, *t1); + + l->e->head.hflag &= ~BM_ELEM_TAG; + l = l->next; + } while (l != f->l_first); + } + TGSET_ITER_END +#endif + + PBVHTriBuf *tribuf = node->tribuf; + for (int i = 0; i < node->tribuf->tottri; i++) { + PBVHTri *tri = node->tribuf->tris + i; + BMFace *f = (BMFace *)tri->f.i; + + if (f->head.hflag & facetag) { + continue; + } + +#ifdef USE_EDGEQUEUE_FRONTFACE + if (eq_ctx->use_view_normal) { + if (dot_v3v3(f->no, eq_ctx->view_normal) < 0.0f) { + continue; + } + } +#endif + + BMVert *vs[3] = {(BMVert *)tribuf->verts[tri->v[0]].i, + (BMVert *)tribuf->verts[tri->v[1]].i, + (BMVert *)tribuf->verts[tri->v[2]].i}; + if (eq_ctx->edge_queue_tri_in_range(eq_ctx, vs, f->no)) { + f->head.hflag |= facetag; + + /* Check each edge of the face */ + BMLoop *l_first = BM_FACE_FIRST_LOOP(f); + BMLoop *l_iter = l_first; + do { + /* are we owned by this node? if so, make sure origdata is up to date */ + if (BM_ELEM_CD_GET_INT(l_iter->v, pbvh->cd_vert_node_offset) == ni) { + BKE_pbvh_bmesh_check_origdata(pbvh, l_iter->v, pbvh->stroke_id); + } + + /* try to improve convergence by applying a small amount of smoothing to topology, + but tangentially to surface. + */ + int randval = (seed = dyntopo_thread_rand(seed)) & 255; + + if (do_smooth && randval > 127) { + PBVHVertRef sv = {(intptr_t)l_iter->v}; + surface_smooth_v_safe(tdata->pbvh, + l_iter->v, + eq_ctx->surface_smooth_fac * + eq_ctx->mask_cb(sv, eq_ctx->mask_cb_data)); + } + + float len_sq = calc_edge_length(eq_ctx, l_iter->e->v1, l_iter->e->v2); + + /* subdivide walks the mesh a bit for better transitions in the topology */ + + if ((eq_ctx->mode & PBVH_Subdivide) && (len_sq > eq_ctx->limit_len_max_sqr)) { + long_edge_queue_edge_add_recursive_2( + tdata, l_iter->radial_next, l_iter, len_sq, eq_ctx->limit_len_max, 0, true); + } + + if (edge_queue_test(eq_ctx, pbvh, l_iter->e)) { + edge_thread_data_insert(tdata, l_iter->e); + } + } while ((l_iter = l_iter->next) != l_first); + } + } + + BLI_rng_free(rng); +} + +bool check_face_is_tri(PBVH *pbvh, BMFace *f) +{ +#if DYNTOPO_DISABLE_FLAG & DYNTOPO_DISABLE_TRIANGULATOR + return true; +#endif + + if (f->len == 3) { + return true; + } + + if (f->len < 3) { + printf("pbvh had < 3 vert face!\n"); + BKE_pbvh_bmesh_remove_face(pbvh, f, false); + return false; + } + + bm_logstack_push(); + + LinkNode *dbl = nullptr; + + Vector fs; + Vector es; + + BMLoop *l = f->l_first; + do { + validate_vert(pbvh, pbvh->header.bm, l->v, true, true); + + if (l->e->head.index == -1) { + l->e->head.index = 0; + } + } while ((l = l->next) != f->l_first); + + // BKE_pbvh_bmesh_remove_face(pbvh, f, true); + pbvh_bmesh_face_remove(pbvh, f, false, true, true); + BM_log_face_removed(pbvh->header.bm, pbvh->bm_log, f); +#ifdef USE_NEW_IDMAP + BM_idmap_release(pbvh->bm_idmap, (BMElem *)f, true); +#endif + + int len = (f->len - 2) * 3; + + fs.resize(len); + es.resize(len); + + int totface = 0; + int totedge = 0; + MemArena *arena = nullptr; + struct Heap *heap = nullptr; + + if (f->len > 4) { + arena = BLI_memarena_new(512, "ngon arena"); + heap = BLI_heap_new(); + } + + BM_face_triangulate(pbvh->header.bm, + f, + fs.data(), + &totface, + es.data(), + &totedge, + &dbl, + MOD_TRIANGULATE_QUAD_FIXED, + MOD_TRIANGULATE_NGON_BEAUTY, + false, + arena, + heap); + + while (totface && dbl) { + BMFace *f2 = (BMFace *)dbl->link; + LinkNode *next = dbl->next; + + for (int i = 0; i < totface; i++) { + if (fs[i] == f2) { + fs[i] = nullptr; + } + } + + if (f == f2) { + BM_log_face_added(pbvh->header.bm, pbvh->bm_log, f); + BM_log_face_removed_no_check(pbvh->header.bm, pbvh->bm_log, f); + f = nullptr; + } + +#ifdef USE_NEW_IDMAP + BM_idmap_release(pbvh->bm_idmap, (BMElem *)dbl->link, true); +#endif + BM_face_kill(pbvh->header.bm, (BMFace *)dbl->link); + + MEM_freeN(dbl); + dbl = next; + } + + for (int i = 0; i < totface; i++) { + BMFace *f2 = fs[i]; + + if (!f2) { + continue; + } + + if (f == f2) { + printf("%s: error\n", __func__); + continue; + } + + // detect new edges + BMLoop *l = f2->l_first; + do { + if (l->e->head.index == -1) { + BM_log_edge_added(pbvh->header.bm, pbvh->bm_log, l->e); + l->e->head.index = 0; + } + } while ((l = l->next) != f2->l_first); + + validate_face(pbvh, pbvh->header.bm, f2, false, true); + + BKE_pbvh_bmesh_add_face(pbvh, f2, false, true); + // BM_log_face_post(pbvh->bm_log, f2); + BM_log_face_added(pbvh->header.bm, pbvh->bm_log, f2); + } + + if (f) { + BKE_pbvh_bmesh_add_face(pbvh, f, false, true); + BM_log_face_added(pbvh->header.bm, pbvh->bm_log, f); + } + + if (arena) { + BLI_memarena_free(arena); + } + + if (heap) { + BLI_heap_free(heap, nullptr); + } + + bm_logstack_pop(); + + return false; +} + +bool destroy_nonmanifold_fins(PBVH *pbvh, BMEdge *e_root) +{ +#if !(DYNTOPO_DISABLE_FLAG & DYNTOPO_DISABLE_FIN_REMOVAL) + bm_logstack_push(); + + static int max_faces = 64; + Vector stack; + + BMLoop *l = e_root->l; + Vector ls; + Vector minfs; + + if (!l) { + bm_logstack_pop(); + return false; + } + + do { + ls.append(l); + } while ((l = l->radial_next) != e_root->l); + + for (int i = 0; i < ls.size(); i++) { + Set visit; + + BMLoop *l = ls[i]; + BMFace *f = l->f; + Vector fs2; + stack.clear(); + + stack.append(f); + fs2.append(f); + + visit.add(f); + + bool bad = false; + + while (stack.size() > 0) { + f = stack.pop_last(); + BMLoop *l = f->l_first; + + do { + if (l->radial_next == l || l->radial_next->radial_next != l) { + continue; + } + + BMFace *f2 = l->radial_next->f; + + if (visit.add(f2)) { + if (fs2.size() > max_faces) { + bad = true; + break; + } + + stack.append(f2); + fs2.append(f2); + } + } while ((l = l->next) != f->l_first); + + if (bad) { + break; + } + } + + if (!bad && fs2.size() && (minfs.size() == 0 || fs2.size() < minfs.size())) { + minfs = fs2; + } + } + + int nupdateflag = PBVH_UpdateOtherVerts | PBVH_UpdateDrawBuffers | PBVH_UpdateBB | + PBVH_UpdateTriAreas; + nupdateflag = nupdateflag | PBVH_UpdateNormals | PBVH_UpdateTris | PBVH_RebuildDrawBuffers; + + if (!minfs.size()) { + bm_logstack_pop(); + return false; + } + + printf("manifold fin size: %d\n", (int)minfs.size()); + const int tag = BM_ELEM_TAG_ALT; + + for (int i = 0; i < minfs.size(); i++) { + BMFace *f = minfs[i]; + + BMLoop *l = f->l_first; + do { + BMLoop *l2 = l; + do { + BMLoop *l3 = l2; + do { + l3->v->head.hflag &= ~tag; + l3->e->head.hflag &= ~tag; + } while ((l3 = l3->next) != l2); + } while ((l2 = l2->radial_next) != l); + + l->v->head.hflag &= ~tag; + l->e->head.hflag &= ~tag; + } while ((l = l->next) != f->l_first); + } + + Vector vs; + Vector es; + + for (int i = 0; i < minfs.size(); i++) { + BMFace *f = minfs[i]; + + BMLoop *l = f->l_first; + do { + if (!(l->v->head.hflag & tag)) { + l->v->head.hflag |= tag; + vs.append(l->v); + } + + if (!(l->e->head.hflag & tag)) { + l->e->head.hflag |= tag; + es.append(l->e); + } + } while ((l = l->next) != f->l_first); + } + + for (int i = 0; i < minfs.size(); i++) { + for (int j = 0; j < minfs.size(); j++) { + if (i != j && minfs[i] == minfs[j]) { + printf("%s: duplicate faces\n", __func__); + continue; + } + } + } + + for (int i = 0; i < minfs.size(); i++) { + BMFace *f = minfs[i]; + + if (f->head.htype != BM_FACE) { + printf("%s: corruption!\n", __func__); + continue; + } + + int ni = BM_ELEM_CD_GET_INT(f, pbvh->cd_face_node_offset); + if (ni >= 0 && ni < pbvh->totnode) { + pbvh->nodes[ni].flag |= (PBVHNodeFlags)nupdateflag; + } + + pbvh_bmesh_face_remove(pbvh, f, true, false, false); +# ifdef USE_NEW_IDMAP + BM_idmap_release(pbvh->bm_idmap, (BMElem *)f, true); +# endif + BM_face_kill(pbvh->header.bm, f); + } + + const int mupdateflag = SCULPTVERT_NEED_DISK_SORT | SCULPTVERT_NEED_VALENCE; + + for (int i = 0; i < es.size(); i++) { + BMEdge *e = es[i]; + + if (!e->l) { + BM_log_edge_removed(pbvh->header.bm, pbvh->bm_log, e); +# ifdef USE_NEW_IDMAP + BM_idmap_release(pbvh->bm_idmap, (BMElem *)e, true); +# endif + BM_edge_kill(pbvh->header.bm, e); + } + } + + for (int i = 0; i < vs.size(); i++) { + BMVert *v = vs[i]; + + if (!v->e) { + pbvh_bmesh_vert_remove(pbvh, v); + + BM_log_vert_removed(pbvh->header.bm, pbvh->bm_log, v); +# ifdef USE_NEW_IDMAP + BM_idmap_release(pbvh->bm_idmap, (BMElem *)v, true); +# endif + BM_vert_kill(pbvh->header.bm, v); + } + else { + pbvh_boundary_update_bmesh(pbvh, v); + + MSculptVert *mv = BKE_PBVH_SCULPTVERT(pbvh->cd_sculpt_vert, v); + mv->flag |= mupdateflag; + } + } + + bm_logstack_pop(); + return true; +#else + return false; +#endif +} + +bool check_for_fins(PBVH *pbvh, BMVert *v) +{ + BMEdge *e = v->e; + if (!e) { + return false; + } + + bm_logstack_push(); + + do { + if (!e) { + printf("%s: e was nullptr\n", __func__); + break; + } + if (e->l) { + BMLoop *l = e->l->f->l_first; + + do { + if (l != l->radial_next && l != l->radial_next->radial_next) { + if (destroy_nonmanifold_fins(pbvh, e)) { + bm_logstack_pop(); + return true; + } + } + } while ((l = l->next) != e->l->f->l_first); + } + } while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e); + + bm_logstack_pop(); + return false; +} + +bool check_vert_fan_are_tris(PBVH *pbvh, BMVert *v) +{ + MSculptVert *mv = BKE_PBVH_SCULPTVERT(pbvh->cd_sculpt_vert, v); + + if (!(mv->flag & SCULPTVERT_NEED_TRIANGULATE)) { + return true; + } + + bm_log_message(" == triangulate == "); + + Vector fs; + + validate_vert(pbvh, pbvh->header.bm, v, true, true); + + if (v->head.htype != BM_VERT) { + printf("non-vert %p fed to %s\n", v, __func__); + return false; + } + + BMIter iter; + BMFace *f; + BM_ITER_ELEM (f, &iter, v, BM_FACES_OF_VERT) { + BMLoop *l = f->l_first; + + do { + MSculptVert *mv_l = BKE_PBVH_SCULPTVERT(pbvh->cd_sculpt_vert, l->v); + pbvh_boundary_update_bmesh(pbvh, l->v); + + MV_ADD_FLAG(mv_l, SCULPTVERT_NEED_VALENCE | SCULPTVERT_NEED_DISK_SORT); + } while ((l = l->next) != f->l_first); + fs.append(f); + } + + mv->flag &= ~SCULPTVERT_NEED_TRIANGULATE; + + for (int i = 0; i < fs.size(); i++) { + check_face_is_tri(pbvh, fs[i]); + } + + return false; +} + +static void edge_queue_init(EdgeQueueContext *eq_ctx, + bool use_projected, + bool use_frontface, + const float center[3], + const float view_normal[3], + const float radius) +{ + if (use_projected) { + eq_ctx->edge_queue_tri_in_range = edge_queue_tri_in_circle; + eq_ctx->edge_queue_vert_in_range = edge_queue_vert_in_circle; + project_plane_normalized_v3_v3v3(eq_ctx->center_proj, center, view_normal); + } + else { + eq_ctx->edge_queue_tri_in_range = edge_queue_tri_in_sphere; + eq_ctx->edge_queue_vert_in_range = edge_queue_vert_in_sphere; + } + + eq_ctx->center = center; + copy_v3_v3(eq_ctx->view_normal, view_normal); + eq_ctx->radius_squared = radius * radius; + eq_ctx->limit_len_min_sqr = eq_ctx->limit_len_min * eq_ctx->limit_len_min; + eq_ctx->limit_len_max_sqr = eq_ctx->limit_len_max * eq_ctx->limit_len_max; + +#ifdef USE_EDGEQUEUE_FRONTFACE + eq_ctx->use_view_normal = use_frontface; +#else + UNUSED_VARS(use_frontface); +#endif +} + +static bool edge_queue_test(EdgeQueueContext *eq_ctx, PBVH *pbvh, BMEdge *e) +{ + float len = len_squared_v3v3(e->v1->co, e->v2->co); + float min = eq_ctx->limit_len_min_sqr; + float max = eq_ctx->limit_len_max_sqr; + + bool ret = false; + + if (eq_ctx->mode & PBVH_Subdivide) { + ret |= len > max; + } + + if (eq_ctx->mode & PBVH_Collapse) { + ret |= len < min; + } + + return ret; +} + +/* Create a priority queue containing vertex pairs connected by a long + * edge as defined by PBVH.bm_max_edge_len. + * + * Only nodes marked for topology update are checked, and in those + * nodes only edges used by a face intersecting the (center, radius) + * sphere are checked. + * + * The highest priority (lowest number) is given to the longest edge. + */ +static void unified_edge_queue_create(EdgeQueueContext *eq_ctx, + PBVH *pbvh, + const float center[3], + const float view_normal[3], + float radius, + const bool use_frontface, + const bool use_projected, + PBVHTopologyUpdateMode local_mode) +{ + if (local_mode) { + edge_queue_create_local( + eq_ctx, pbvh, center, view_normal, radius, use_frontface, use_projected, local_mode); + return; + } + + eq_ctx->radius_squared = radius * radius; + + eq_ctx->limit_len_min = pbvh->bm_min_edge_len; + eq_ctx->limit_len_max = pbvh->bm_max_edge_len; + + edge_queue_init(eq_ctx, use_projected, use_frontface, center, eq_ctx->view_normal, radius); + +#ifdef USE_EDGEQUEUE_TAG_VERIFY + pbvh_bmesh_edge_tag_verify(pbvh); +#endif + + Vector tdata; + + bool push_subentry = false; + + for (int n = 0; n < pbvh->totnode; n++) { + PBVHNode *node = &pbvh->nodes[n]; + + /* Check leaf nodes marked for topology update */ + bool ok = ((node->flag & PBVH_Leaf) && (node->flag & PBVH_UpdateTopology) && + !(node->flag & PBVH_FullyHidden)); + + if (!ok) { + continue; + } + + EdgeQueueThreadData td; + + memset(&td, 0, sizeof(td)); + + td.seed = BLI_thread_rand(0); + td.pbvh = pbvh; + td.node = node; + td.eq_ctx = eq_ctx; + + tdata.append(td); + + /* Check each face */ + /* + BMFace *f; + TGSET_ITER (f, node->bm_faces) { + long_edge_queue_face_add(eq_ctx, f); + } + TGSET_ITER_END + */ + } + + int count = tdata.size(); + + TaskParallelSettings settings; + + BLI_parallel_range_settings_defaults(&settings); +#ifdef DYNTOPO_NO_THREADING + settings.use_threading = false; +#endif + + BLI_task_parallel_range(0, count, (void *)tdata.data(), unified_edge_queue_task_cb, &settings); + + const int cd_sculpt_vert = pbvh->cd_sculpt_vert; + + for (int i = 0; i < count; i++) { + EdgeQueueThreadData *td = &tdata[i]; + BMEdge **edges = td->edges; + + for (int j = 0; j < td->totedge; j++) { + BMEdge *e = edges[j]; + + e->head.hflag &= ~BM_ELEM_TAG; + } + } + + Vector verts; + + for (int i = 0; i < count; i++) { + EdgeQueueThreadData *td = &tdata[i]; + + BMEdge **edges = td->edges; + for (int j = 0; j < td->totedge; j++) { + BMEdge *e = edges[j]; + + if (bm_elem_is_free((BMElem *)e, BM_EDGE)) { + continue; + } + + e->head.hflag &= ~BM_ELEM_TAG; + + if (e->l && e->l != e->l->radial_next->radial_next) { + // deal with non-manifold iffyness + destroy_nonmanifold_fins(pbvh, e); + push_subentry = true; + + if (bm_elem_is_free((BMElem *)e, BM_EDGE)) { + continue; + } + } + + MSculptVert *mv1 = BKE_PBVH_SCULPTVERT(cd_sculpt_vert, e->v1); + MSculptVert *mv2 = BKE_PBVH_SCULPTVERT(cd_sculpt_vert, e->v2); + + if (mv1->flag & SCULPTVERT_NEED_VALENCE) { + BKE_pbvh_bmesh_update_valence(pbvh->cd_sculpt_vert, {(intptr_t)e->v1}); + } + + if (mv2->flag & SCULPTVERT_NEED_VALENCE) { + BKE_pbvh_bmesh_update_valence(pbvh->cd_sculpt_vert, {(intptr_t)e->v2}); + } + + // check_vert_fan_are_tris(pbvh, e->v1); + // check_vert_fan_are_tris(pbvh, e->v2); + + // float w = -calc_weighted_edge_split(eq_ctx, e->v1, e->v2); + // float w2 = maskcb_get(eq_ctx, e); + + // w *= w2 * w2; + + if (eq_ctx->use_view_normal && (dot_v3v3(e->v1->no, eq_ctx->view_normal) < 0.0f && + dot_v3v3(e->v2->no, eq_ctx->view_normal) < 0.0f)) { + return; + } + + verts.append(e->v1); + verts.append(e->v2); + + e->v1->head.hflag |= BM_ELEM_TAG; + e->v2->head.hflag |= BM_ELEM_TAG; + + if (edge_queue_test(eq_ctx, pbvh, e)) { + edge_queue_insert_unified(eq_ctx, e); + } + + // edge_queue_insert(eq_ctx, e, w, eq_ctx->limit_len); + } + + MEM_SAFE_FREE(td->edges); + } + + for (int i = 0; i < verts.size(); i++) { + BMVert *v = verts[i]; + + if (v->head.hflag & BM_ELEM_TAG) { + v->head.hflag &= ~BM_ELEM_TAG; + + edge_queue_insert_val34_vert(eq_ctx, v); + } + } + + if (push_subentry) { + BM_log_entry_add_ex(pbvh->header.bm, pbvh->bm_log, true); + } +} + +static void short_edge_queue_task_cb_local(void *__restrict userdata, + const int n, + const TaskParallelTLS *__restrict tls) +{ + EdgeQueueThreadData *tdata = ((EdgeQueueThreadData *)userdata) + n; + PBVHNode *node = tdata->node; + EdgeQueueContext *eq_ctx = tdata->eq_ctx; + + BMFace *f; + + TGSET_ITER (f, node->bm_faces) { +#ifdef USE_EDGEQUEUE_FRONTFACE + if (eq_ctx->use_view_normal) { + if (dot_v3v3(f->no, eq_ctx->view_normal) < 0.0f) { + continue; + } + } +#endif + + BMVert *vs[3] = {f->l_first->v, f->l_first->next->v, f->l_first->next->next->v}; + if (eq_ctx->edge_queue_tri_in_range(eq_ctx, vs, f->no)) { + BMLoop *l = f->l_first; + + do { + edge_thread_data_insert(tdata, l->e); + + } while ((l = l->next) != f->l_first); + } + } + TGSET_ITER_END +} + +static void edge_queue_create_local(EdgeQueueContext *eq_ctx, + PBVH *pbvh, + const float center[3], + const float view_normal[3], + float radius, + const bool use_frontface, + const bool use_projected, + PBVHTopologyUpdateMode local_mode) +{ + eq_ctx->limit_len_min = pbvh->bm_min_edge_len; + eq_ctx->limit_len_max = pbvh->bm_max_edge_len; + eq_ctx->local_mode = true; + + edge_queue_init(eq_ctx, use_projected, use_frontface, center, eq_ctx->view_normal, radius); + + Vector tdata; + + for (int n = 0; n < pbvh->totnode; n++) { + PBVHNode *node = &pbvh->nodes[n]; + EdgeQueueThreadData td; + + if ((node->flag & PBVH_Leaf) && (node->flag & PBVH_UpdateTopology) && + !(node->flag & PBVH_FullyHidden)) { + memset(&td, 0, sizeof(td)); + td.pbvh = pbvh; + td.node = node; + td.is_collapse = local_mode & PBVH_LocalCollapse; + td.eq_ctx = eq_ctx; + + tdata.append(td); + } + } + + int count = tdata.size(); + + TaskParallelSettings settings; + + BLI_parallel_range_settings_defaults(&settings); +#ifdef DYNTOPO_NO_THREADING + settings.use_threading = false; +#endif + + BLI_task_parallel_range( + 0, count, (void *)tdata.data(), short_edge_queue_task_cb_local, &settings); + + const int cd_sculpt_vert = pbvh->cd_sculpt_vert; + Vector lens; + Vector edges; + + for (int i = 0; i < count; i++) { + EdgeQueueThreadData *td = &tdata[i]; + + BMEdge **edges2 = td->edges; + for (int j = 0; j < td->totedge; j++) { + edges2[j]->head.hflag &= ~BM_ELEM_TAG; + } + } + + for (int i = 0; i < count; i++) { + EdgeQueueThreadData *td = &tdata[i]; + + BMEdge **edges2 = td->edges; + for (int j = 0; j < td->totedge; j++) { + BMEdge *e = edges2[j]; + + e->v1->head.hflag &= ~BM_ELEM_TAG; + e->v2->head.hflag &= ~BM_ELEM_TAG; + + if (!(e->head.hflag & BM_ELEM_TAG)) { + edges.append(e); + e->head.hflag |= BM_ELEM_TAG; + } + } + } + + for (int i = 0; i < count; i++) { + EdgeQueueThreadData *td = &tdata[i]; + MEM_SAFE_FREE(td->edges); + } + + for (int i = 0; i < edges.size(); i++) { + BMEdge *e = edges[i]; + float len = len_v3v3(e->v1->co, e->v2->co); + + for (int j = 0; j < 2; j++) { + BMVert *v = j ? e->v2 : e->v1; + + if (!(local_mode & PBVH_LocalCollapse)) { + if (!(v->head.hflag & BM_ELEM_TAG)) { + v->head.hflag |= BM_ELEM_TAG; + MSculptVert *mv = BKE_PBVH_SCULPTVERT(pbvh->cd_sculpt_vert, v); + + if (mv->flag & SCULPTVERT_NEED_VALENCE) { + BKE_pbvh_bmesh_update_valence(pbvh->cd_sculpt_vert, {(intptr_t)v}); + } + + edge_queue_insert_val34_vert(eq_ctx, v); + } + } + } + e->head.index = i; + lens.append(len); + } + + // make sure tags around border edges are unmarked + for (int i = 0; i < edges.size(); i++) { + BMEdge *e = edges[i]; + + for (int j = 0; j < 2; j++) { + BMVert *v1 = j ? e->v2 : e->v1; + BMEdge *e1 = v1->e; + + do { + e1->head.hflag &= ~BM_ELEM_TAG; + + e1 = BM_DISK_EDGE_NEXT(e1, v1); + } while (e1 != v1->e); + } + } + + // re-tag edge list + for (int i = 0; i < edges.size(); i++) { + edges[i]->head.hflag |= BM_ELEM_TAG; + } + + int totstep = 3; + + // blur lengths + for (int step = 0; step < totstep; step++) { + for (int i = 0; i < edges.size(); i++) { + BMEdge *e = edges[i]; + + float len = lens[i]; + float totlen = 0.0f; + + for (int j = 0; j < 2; j++) { + BMVert *v1 = j ? e->v2 : e->v1; + BMEdge *e1 = v1->e; + + do { + if (e1->head.hflag & BM_ELEM_TAG) { + len += lens[e1->head.index]; + totlen += 1.0f; + } + + e1 = BM_DISK_EDGE_NEXT(e1, v1); + } while (e1 != v1->e); + } + + if (totlen != 0.0f) { + len /= totlen; + lens[i] += (len - lens[i]) * 0.5; + } + } + } + + pbvh->header.bm->elem_index_dirty |= BM_EDGE; + + float limit = 0.0f; + float tot = 0.0f; + + for (int i = 0; i < edges.size(); i++) { + BMEdge *e = edges[i]; + MSculptVert *mv1, *mv2; + + e->head.hflag &= ~BM_ELEM_TAG; + + mv1 = BKE_PBVH_SCULPTVERT(cd_sculpt_vert, e->v1); + mv2 = BKE_PBVH_SCULPTVERT(cd_sculpt_vert, e->v2); + + pbvh_check_vert_boundary(pbvh, e->v1); + pbvh_check_vert_boundary(pbvh, e->v2); + + int boundflag1 = BM_ELEM_CD_GET_INT(e->v1, pbvh->cd_boundary_flag); + int boundflag2 = BM_ELEM_CD_GET_INT(e->v2, pbvh->cd_boundary_flag); + + if ((boundflag1 & SCULPTVERT_ALL_CORNER) || (boundflag2 & SCULPTVERT_ALL_CORNER)) { + continue; + } + + if ((boundflag1 & SCULPTVERT_ALL_BOUNDARY) != (boundflag2 & SCULPTVERT_ALL_BOUNDARY)) { + continue; + } + + // check seam/sharp flags here + // if (!(e->head.hflag & BM_ELEM_SMOOTH) || e->head.hflag & BM_ELEM_SEAM) { + // continue; + // } + + limit += lens[i]; + tot += 1.0f; + } + + if (tot > 0.0f) { + limit /= tot; + + if (local_mode & PBVH_LocalCollapse) { + eq_ctx->limit_len_min = limit * pbvh->bm_detail_range; + eq_ctx->limit_len_min_sqr = eq_ctx->limit_len_min * eq_ctx->limit_len_min; + } + + if (local_mode & PBVH_LocalSubdivide) { + eq_ctx->limit_len_max = limit; + eq_ctx->limit_len_max_sqr = eq_ctx->limit_len_max * eq_ctx->limit_len_max; + } + } + + for (int i = 0; i < edges.size(); i++) { + BMEdge *e = edges[i]; + + MSculptVert *mv1 = BKE_PBVH_SCULPTVERT(cd_sculpt_vert, e->v1); + MSculptVert *mv2 = BKE_PBVH_SCULPTVERT(cd_sculpt_vert, e->v2); + + int boundflag1 = BM_ELEM_CD_GET_INT(e->v1, pbvh->cd_boundary_flag); + int boundflag2 = BM_ELEM_CD_GET_INT(e->v2, pbvh->cd_boundary_flag); + + if ((boundflag1 & SCULPTVERT_ALL_CORNER) || (boundflag2 & SCULPTVERT_ALL_CORNER)) { + continue; + } + + if ((boundflag1 & SCULPTVERT_ALL_BOUNDARY) != (boundflag2 & SCULPTVERT_ALL_BOUNDARY)) { + continue; + } + + bool ok = false; + + bool a = eq_ctx->mode & (PBVH_Subdivide | PBVH_LocalSubdivide); + bool b = eq_ctx->mode & (PBVH_Collapse | PBVH_LocalCollapse); + + float len = len_squared_v3v3(e->v1->co, e->v2->co); + + if (a && b) { + ok = len < eq_ctx->limit_len_min_sqr || len > eq_ctx->limit_len_max_sqr; + ok = ok || (len < pbvh->bm_min_edge_len || len > pbvh->bm_max_edge_len); + } + else if (a) { + ok = len > eq_ctx->limit_len_max || len > pbvh->bm_max_edge_len; + } + else if (b) { + ok = len < eq_ctx->limit_len_min || len < pbvh->bm_min_edge_len; + } + + if (!ok) { + continue; + } + + edge_queue_insert_unified(eq_ctx, e); + } +} + +// need to file a CLANG bug, getting weird behavior here +#ifdef __clang__ +__attribute__((optnone)) +#endif + +static bool +cleanup_valence_3_4(EdgeQueueContext *ectx, + PBVH *pbvh, + const float center[3], + const float view_normal[3], + float radius, + const bool use_frontface, + const bool use_projected) +{ + return false; // XXX not working with local collapse/subdivide mode + bool modified = false; + + bm_logstack_push(); + + bm_log_message(" == cleanup_valence_3_4 == "); + + // push log subentry + BM_log_entry_add_ex(pbvh->header.bm, pbvh->bm_log, true); + + float radius2 = radius * 1.25; + float rsqr = radius2 * radius2; + + const int cd_vert_node = pbvh->cd_vert_node_offset; + + int updateflag = SCULPTVERT_NEED_DISK_SORT | SCULPTVERT_NEED_VALENCE; + + for (int i = 0; i < ectx->tot_used_verts; i++) { + BMVert *v = ectx->used_verts[i]; + + // TGSET_ITER (v, ectx->used_verts) { + if (bm_elem_is_free((BMElem *)v, BM_VERT)) { + continue; + } + + const int n = BM_ELEM_CD_GET_INT(v, cd_vert_node); + + if (n == DYNTOPO_NODE_NONE) { + continue; + } + + MSculptVert *mv = BKE_PBVH_SCULPTVERT(pbvh->cd_sculpt_vert, v); + + BKE_pbvh_bmesh_check_valence(pbvh, {(intptr_t)v}); + + int val = mv->valence; + if (val != 4 && val != 3) { + continue; + } + + PBVHVertRef sv = {(intptr_t)v}; + + if (len_squared_v3v3(v->co, center) >= rsqr || !v->e || + ectx->mask_cb(sv, ectx->mask_cb_data) < 0.5f) { + continue; + } + + validate_vert(pbvh, pbvh->header.bm, v, false, true); + check_vert_fan_are_tris(pbvh, v); + validate_vert(pbvh, pbvh->header.bm, v, true, true); + +#if 0 + // check valence again + val = BM_vert_edge_count(v); + if (val != 4 && val != 3) { + printf("pbvh valence error\n"); + continue; + } +#endif + + pbvh_check_vert_boundary(pbvh, v); + int boundflag = BM_ELEM_CD_GET_INT(v, pbvh->cd_boundary_flag); + + if (boundflag & SCULPTVERT_ALL_BOUNDARY) { + continue; + } + + BMIter iter; + BMLoop *l; + BMLoop *ls[4]; + BMVert *vs[4]; + + l = v->e->l; + + if (!l) { + continue; + } + + if (l->v != v) { + l = l->next; + } + + bool bad = false; + int ls_i = 0; + + for (int j = 0; j < val; j++) { + ls[ls_i++] = l->v == v ? l->next : l; + + MSculptVert *mv_l; + + if (l->v == v) { + mv_l = BKE_PBVH_SCULPTVERT(pbvh->cd_sculpt_vert, l->next->v); + pbvh_boundary_update_bmesh(pbvh, l->next->v); + } + else { + mv_l = BKE_PBVH_SCULPTVERT(pbvh->cd_sculpt_vert, l->v); + pbvh_boundary_update_bmesh(pbvh, l->v); + } + + MV_ADD_FLAG(mv_l, updateflag); + + l = l->prev->radial_next; + + if (l->v != v) { + l = l->next; + } + + /*ignore non-manifold edges along with ones flagged as sharp*/ + if (l->radial_next == l || l->radial_next->radial_next != l || + !(l->e->head.hflag & BM_ELEM_SMOOTH)) { + bad = true; + break; + } + + if (l->radial_next != l && l->radial_next->v == l->v) { + bad = true; // bad normals + break; + } + + for (int k = 0; k < j; k++) { + if (ls[k]->v == ls[j]->v) { + if (ls[j]->next->v != v) { + ls[j] = ls[j]->next; + } + else { + bad = true; + break; + } + } + + // check for non-manifold edges + if (ls[k] != ls[k]->radial_next->radial_next) { + bad = true; + break; + } + + if (ls[k]->f == ls[j]->f) { + bad = true; + break; + } + } + } + + if (bad) { + continue; + } + + int ni = BM_ELEM_CD_GET_INT(v, pbvh->cd_vert_node_offset); + + if (ni < 0) { + printf("cleanup_valence_3_4 error!\n"); + + // attempt to recover + + BMFace *f; + BM_ITER_ELEM (f, &iter, v, BM_FACES_OF_VERT) { + int ni2 = BM_ELEM_CD_GET_INT(f, pbvh->cd_face_node_offset); + + if (ni2 != DYNTOPO_NODE_NONE) { + PBVHNode *node2 = pbvh->nodes + ni2; + + BLI_table_gset_remove(node2->bm_unique_verts, v, nullptr); + } + } + } + + pbvh_bmesh_vert_remove(pbvh, v); + + BMFace *f; + BM_ITER_ELEM (f, &iter, v, BM_FACES_OF_VERT) { + int ni2 = BM_ELEM_CD_GET_INT(f, pbvh->cd_face_node_offset); + + if (ni2 != DYNTOPO_NODE_NONE) { + // PBVHNode *node2 = pbvh->nodes + ni2; + // BLI_table_gset_remove(node2->bm_unique_verts, v, nullptr); + + pbvh_bmesh_face_remove(pbvh, f, true, true, true); + } + else { + BM_log_face_removed(pbvh->header.bm, pbvh->bm_log, f); + } + } + + modified = true; + + if (!v->e) { + printf("mesh error!\n"); + continue; + } + + validate_vert(pbvh, pbvh->header.bm, v, false, true); + + l = v->e->l; + + if (val == 4) { + // check which quad diagonal to use to split quad + // try to preserve hard edges + + float n1[3], n2[3], th1, th2; + normal_tri_v3(n1, ls[0]->v->co, ls[1]->v->co, ls[2]->v->co); + normal_tri_v3(n2, ls[0]->v->co, ls[2]->v->co, ls[3]->v->co); + + th1 = dot_v3v3(n1, n2); + + normal_tri_v3(n1, ls[1]->v->co, ls[2]->v->co, ls[3]->v->co); + normal_tri_v3(n2, ls[1]->v->co, ls[3]->v->co, ls[0]->v->co); + + th2 = dot_v3v3(n1, n2); + + if (th1 > th2) { + BMLoop *ls2[4] = {ls[0], ls[1], ls[2], ls[3]}; + + for (int j = 0; j < 4; j++) { + ls[j] = ls2[(j + 1) % 4]; + } + } + } + + vs[0] = ls[0]->v; + vs[1] = ls[1]->v; + vs[2] = ls[2]->v; + + validate_vert(pbvh, pbvh->header.bm, v, false, false); + + MSculptVert *mv1 = BKE_PBVH_SCULPTVERT(pbvh->cd_sculpt_vert, vs[0]); + MSculptVert *mv2 = BKE_PBVH_SCULPTVERT(pbvh->cd_sculpt_vert, vs[1]); + MSculptVert *mv3 = BKE_PBVH_SCULPTVERT(pbvh->cd_sculpt_vert, vs[2]); + + pbvh_boundary_update_bmesh(pbvh, vs[0]); + pbvh_boundary_update_bmesh(pbvh, vs[1]); + pbvh_boundary_update_bmesh(pbvh, vs[2]); + + MV_ADD_FLAG(mv1, updateflag); + MV_ADD_FLAG(mv2, updateflag); + MV_ADD_FLAG(mv3, updateflag); + + BMFace *f1 = nullptr; + bool ok1 = vs[0] != vs[1] && vs[1] != vs[2] && vs[0] != vs[2]; + ok1 = ok1 && !BM_face_exists(vs, 3); + + if (ok1) { + f1 = pbvh_bmesh_face_create(pbvh, n, vs, nullptr, l->f, true, false); + normal_tri_v3( + f1->no, f1->l_first->v->co, f1->l_first->next->v->co, f1->l_first->prev->v->co); + + validate_face(pbvh, pbvh->header.bm, f1, false, false); + } + else { + // printf("eek1!\n"); + } + + BMFace *f2 = nullptr; + + if (val == 4) { + vs[0] = ls[0]->v; + vs[1] = ls[2]->v; + vs[2] = ls[3]->v; + } + + bool ok2 = val == 4 && vs[0] != vs[2] && vs[2] != vs[3] && vs[0] != vs[3]; + ok2 = ok2 && !BM_face_exists(vs, 3); + + if (ok2) { + MSculptVert *mv1 = BKE_PBVH_SCULPTVERT(pbvh->cd_sculpt_vert, vs[0]); + MSculptVert *mv2 = BKE_PBVH_SCULPTVERT(pbvh->cd_sculpt_vert, vs[1]); + MSculptVert *mv3 = BKE_PBVH_SCULPTVERT(pbvh->cd_sculpt_vert, vs[2]); + + pbvh_boundary_update_bmesh(pbvh, vs[0]); + pbvh_boundary_update_bmesh(pbvh, vs[1]); + pbvh_boundary_update_bmesh(pbvh, vs[2]); + + MV_ADD_FLAG(mv1, updateflag); + MV_ADD_FLAG(mv2, updateflag); + MV_ADD_FLAG(mv3, updateflag); + + BMFace *example = nullptr; + if (v->e && v->e->l) { + example = v->e->l->f; + } + + f2 = pbvh_bmesh_face_create(pbvh, n, vs, nullptr, example, true, false); + + CustomData_bmesh_swap_data_simple( + &pbvh->header.bm->ldata, &f2->l_first->prev->head.data, &ls[3]->head.data); + CustomData_bmesh_copy_data(&pbvh->header.bm->ldata, + &pbvh->header.bm->ldata, + ls[0]->head.data, + &f2->l_first->head.data); + CustomData_bmesh_copy_data(&pbvh->header.bm->ldata, + &pbvh->header.bm->ldata, + ls[2]->head.data, + &f2->l_first->next->head.data); + + normal_tri_v3( + f2->no, f2->l_first->v->co, f2->l_first->next->v->co, f2->l_first->prev->v->co); + BM_log_face_added(pbvh->header.bm, pbvh->bm_log, f2); + + validate_face(pbvh, pbvh->header.bm, f2, false, false); + } + + if (f1) { + CustomData_bmesh_swap_data_simple( + &pbvh->header.bm->ldata, &f1->l_first->head.data, &ls[0]->head.data); + CustomData_bmesh_swap_data_simple( + &pbvh->header.bm->ldata, &f1->l_first->next->head.data, &ls[1]->head.data); + CustomData_bmesh_swap_data_simple( + &pbvh->header.bm->ldata, &f1->l_first->prev->head.data, &ls[2]->head.data); + + BM_log_face_added(pbvh->header.bm, pbvh->bm_log, f1); + } + + validate_vert(pbvh, pbvh->header.bm, v, false, false); + pbvh_kill_vert(pbvh, v, true, true); + + if (f1 && !bm_elem_is_free((BMElem *)f1, BM_FACE)) { + if (!bm_elem_is_free((BMElem *)f1, BM_FACE)) { + check_face_is_manifold(pbvh, pbvh->header.bm, f1); + } + } + + if (f2 && !bm_elem_is_free((BMElem *)f2, BM_FACE)) { + if (!bm_elem_is_free((BMElem *)f2, BM_FACE)) { + check_face_is_manifold(pbvh, pbvh->header.bm, f2); + } + } + } +#ifdef DYNTOPO_USE_MINMAX_HEAP + // TGSET_ITER_END; +#endif + + if (modified) { + pbvh->header.bm->elem_index_dirty |= BM_VERT | BM_FACE | BM_EDGE; + pbvh->header.bm->elem_table_dirty |= BM_VERT | BM_FACE | BM_EDGE; + } + + bm_logstack_pop(); + + return modified; +} + +//#define DEFRAGMENT_MEMORY +bool BM_defragment_vertex(BMesh *bm, + BMVert *v, + RNG *rand, + void (*on_vert_swap)(BMVert *a, BMVert *b, void *userdata), + void *userdata); + +typedef struct SwapData { + PBVH *pbvh; +} SwapData; + +void pbvh_tribuf_swap_verts( + PBVH *pbvh, PBVHNode *node, PBVHNode *node2, PBVHTriBuf *tribuf, BMVert *v1, BMVert *v2) +{ + + void *val; + + bool ok = BLI_smallhash_remove_p(&tribuf->vertmap, (uintptr_t)v1, &val); + + if (!ok) { + // printf("eek, missing vertex!"); + return; + } + + int idx = POINTER_AS_INT(val); + + tribuf->verts[idx].i = (intptr_t)v2; + + void **val2; + if (BLI_smallhash_ensure_p(&tribuf->vertmap, (intptr_t)v2, &val2)) { + // v2 was already in hash? add v1 back in then with v2's index + + int idx2 = POINTER_AS_INT(*val2); + BLI_smallhash_insert(&tribuf->vertmap, (intptr_t)v1, POINTER_FROM_INT(idx2)); + } + + *val2 = POINTER_FROM_INT(idx); +} + +void pbvh_node_tribuf_swap_verts( + PBVH *pbvh, PBVHNode *node, PBVHNode *node2, BMVert *v1, BMVert *v2) +{ + if (node != node2) { + BLI_table_gset_remove(node->bm_unique_verts, v1, nullptr); + BLI_table_gset_add(node->bm_unique_verts, v2); + } + + if (node->tribuf) { + pbvh_tribuf_swap_verts(pbvh, node, node2, node->tribuf, v1, v2); + } + + for (int i = 0; i < node->tot_tri_buffers; i++) { + pbvh_tribuf_swap_verts(pbvh, node, node2, node->tri_buffers + i, v1, v2); + } +} + +static bool do_cleanup_3_4(EdgeQueueContext *eq_ctx, + PBVH *pbvh, + const float center[3], + const float view_normal[3], + float radius, + bool use_frontface, + bool use_projected) +{ + bool modified = false; + +#if 1 + if (!(eq_ctx->mode & + (PBVH_Subdivide | PBVH_Collapse | PBVH_LocalCollapse | PBVH_LocalSubdivide))) { + for (int n = 0; n < pbvh->totnode; n++) { + PBVHNode *node = pbvh->nodes + n; + BMVert *v; + + if (!(node->flag & PBVH_Leaf) || !(node->flag & PBVH_UpdateTopology)) { + continue; + } + + TGSET_ITER (v, node->bm_unique_verts) { + if (!eq_ctx->edge_queue_vert_in_range(eq_ctx, v)) { + continue; + } + + if (use_frontface && dot_v3v3(v->no, view_normal) < 0.0f) { + continue; + } + + MSculptVert *mv = BKE_PBVH_SCULPTVERT(pbvh->cd_sculpt_vert, v); + + if (mv->flag & SCULPTVERT_NEED_VALENCE) { + BKE_pbvh_bmesh_update_valence(pbvh->cd_sculpt_vert, {(intptr_t)v}); + } + + if (mv->valence < 5) { + edge_queue_insert_val34_vert(eq_ctx, v); + } + } + TGSET_ITER_END; + } + } +#endif + + BM_log_entry_add_ex(pbvh->header.bm, pbvh->bm_log, true); + + pbvh_bmesh_check_nodes(pbvh); + + modified |= cleanup_valence_3_4( + eq_ctx, pbvh, center, view_normal, radius, use_frontface, use_projected); + pbvh_bmesh_check_nodes(pbvh); + + return modified; +} + +float mask_cb_nop(PBVHVertRef vertex, void *userdata) +{ + return 1.0f; +} + +/* Collapse short edges, subdivide long edges */ +extern "C" bool BKE_pbvh_bmesh_update_topology(PBVH *pbvh, + PBVHTopologyUpdateMode mode, + const float center[3], + const float view_normal[3], + float radius, + const bool use_frontface, + const bool use_projected, + int sym_axis, + bool updatePBVH, + DyntopoMaskCB mask_cb, + void *mask_cb_data, + int custom_max_steps, + bool disable_surface_relax, + bool is_snake_hook) +{ + /* Disable surface smooth if uv layers are present, to avoid expensive reprojection operation. */ + if (!is_snake_hook && CustomData_has_layer(&pbvh->header.bm->ldata, CD_PROP_FLOAT2)) { + disable_surface_relax = true; + } + + /* Push a subentry. */ + BM_log_entry_add_ex(pbvh->header.bm, pbvh->bm_log, true); + + /* 2 is enough for edge faces - manifold edge */ + BLI_buffer_declare_static(BMLoop *, edge_loops, BLI_BUFFER_NOP, 2); + BLI_buffer_declare_static(BMFace *, deleted_faces, BLI_BUFFER_NOP, 32); + + const int cd_vert_mask_offset = CustomData_get_offset(&pbvh->header.bm->vdata, CD_PAINT_MASK); + const int cd_vert_node_offset = pbvh->cd_vert_node_offset; + const int cd_face_node_offset = pbvh->cd_face_node_offset; + const int cd_sculpt_vert = pbvh->cd_sculpt_vert; + // float ratio = 1.0f; + + bool modified = false; + + if (view_normal) { + BLI_assert(len_squared_v3(view_normal) != 0.0f); + } + +#ifdef DYNTOPO_REPORT + { + BMesh *bm = pbvh->header.bm; + + int vmem = (int)((size_t)bm->totvert * (sizeof(BMVert) + bm->vdata.totsize)); + int emem = (int)((size_t)bm->totedge * (sizeof(BMEdge) + bm->edata.totsize)); + int lmem = (int)((size_t)bm->totloop * (sizeof(BMLoop) + bm->ldata.totsize)); + int fmem = (int)((size_t)bm->totface * (sizeof(BMFace) + bm->pdata.totsize)); + + double fvmem = (double)vmem / 1024.0 / 1024.0; + double femem = (double)emem / 1024.0 / 1024.0; + double flmem = (double)lmem / 1024.0 / 1024.0; + double ffmem = (double)fmem / 1024.0 / 1024.0; + + printf("totmem: %.2fmb\n", fvmem + femem + flmem + ffmem); + printf("v: %.2f e: %.2f l: %.2f f: %.2f\n", fvmem, femem, flmem, ffmem); + + printf("custom attributes only:\n"); + vmem = (int)((size_t)bm->totvert * (bm->vdata.totsize)); + emem = (int)((size_t)bm->totedge * (bm->edata.totsize)); + lmem = (int)((size_t)bm->totloop * (bm->ldata.totsize)); + fmem = (int)((size_t)bm->totface * (bm->pdata.totsize)); + + fvmem = (double)vmem / 1024.0 / 1024.0; + femem = (double)emem / 1024.0 / 1024.0; + flmem = (double)lmem / 1024.0 / 1024.0; + ffmem = (double)fmem / 1024.0 / 1024.0; + + printf("v: %.2f e: %.2f l: %.2f f: %.2f\n", fvmem, femem, flmem, ffmem); + } +#endif + + float safe_smooth; + + if (disable_surface_relax) { + safe_smooth = 0.0f; + } + else if ((mode & PBVH_Subdivide) && (!(mode & PBVH_Collapse) || (mode & PBVH_LocalCollapse))) { + safe_smooth = DYNTOPO_SAFE_SMOOTH_SUBD_ONLY_FAC; + } + else { + safe_smooth = DYNTOPO_SAFE_SMOOTH_FAC; + } + + EdgeQueueContext eq_ctx; + memset(static_cast(&eq_ctx), 0, sizeof(eq_ctx)); + + eq_ctx.pool = nullptr; + eq_ctx.bm = pbvh->header.bm; + eq_ctx.mask_cb = mask_cb; + eq_ctx.mask_cb_data = mask_cb_data; + + eq_ctx.cd_sculpt_vert = cd_sculpt_vert; + eq_ctx.cd_vert_mask_offset = cd_vert_mask_offset; + eq_ctx.cd_vert_node_offset = cd_vert_node_offset; + eq_ctx.cd_face_node_offset = cd_face_node_offset; + eq_ctx.avg_elen = 0.0f; + eq_ctx.max_elen = -1e17; + eq_ctx.min_elen = 1e17; + eq_ctx.totedge = 0.0f; + eq_ctx.local_mode = false; + eq_ctx.surface_smooth_fac = safe_smooth; + eq_ctx.mode = mode; + +#ifdef DYNTOPO_USE_MINMAX_HEAP + eq_ctx.heap_mm = BLI_mm_heap_new_ex(max_ii(DYNTOPO_MAX_ITER, custom_max_steps)); + // eq_ctx.used_verts = BLI_table_gset_new(__func__); + eq_ctx.max_heap_mm = DYNTOPO_MAX_ITER << 8; + eq_ctx.limit_min = pbvh->bm_min_edge_len; + eq_ctx.limit_max = pbvh->bm_max_edge_len; + eq_ctx.limit_mid = eq_ctx.limit_max * 0.5f + eq_ctx.limit_min * 0.5f; + + eq_ctx.use_view_normal = use_frontface; + if (view_normal) { + copy_v3_v3(eq_ctx.view_normal, view_normal); + } + else { + zero_v3(eq_ctx.view_normal); + eq_ctx.view_normal[2] = 1.0f; + } + +#endif + + if (mode & PBVH_LocalSubdivide) { + mode |= PBVH_Subdivide; + } + if (mode & PBVH_LocalSubdivide) { + mode |= PBVH_Collapse; + } + +#if DYNTOPO_DISABLE_FLAG & DYNTOPO_DISABLE_COLLAPSE + mode &= ~PBVH_Collapse; +#endif +#if DYNTOPO_DISABLE_FLAG & DYNTOPO_DISABLE_SPLIT_EDGES + mode &= ~PBVH_Subdivide; +#endif + + if (mode & (PBVH_Subdivide | PBVH_Collapse)) { + unified_edge_queue_create(&eq_ctx, + pbvh, + center, + view_normal, + radius, + use_frontface, + use_projected, + mode & (PBVH_LocalSubdivide | PBVH_LocalCollapse)); + } + else { + edge_queue_init(&eq_ctx, use_projected, use_frontface, center, eq_ctx.view_normal, radius); + } + +#ifdef SKINNY_EDGE_FIX + // prevent remesher thrashing by throttling edge splitting in pathological case of skinny + // edges + float avg_elen = eq_ctx.avg_elen; + if (eq_ctx.totedge > 0.0f) { + avg_elen /= eq_ctx.totedge; + + float emax = eq_ctx.max_elen; + if (emax == 0.0f) { + emax = 0.0001f; + } + + if (pbvh->bm_min_edge_len > 0.0f && avg_elen > 0.0f) { + ratio = avg_elen / (pbvh->bm_min_edge_len * 0.5 + emax * 0.5); + ratio = MAX2(ratio, 0.25f); + ratio = MIN2(ratio, 5.0f); + } + } +#else + // ratio = 1.0f; +#endif + + int steps[2] = {0, 0}; + + if ((mode & PBVH_Subdivide) && (mode & PBVH_Collapse)) { + steps[0] = 4096; + steps[1] = 1024; + } + else if (mode & PBVH_Subdivide) { + steps[0] = 4096; + } + else if (mode & PBVH_Collapse) { + steps[0] = 4096; + } + + int edges_size = steps[0]; + BMEdge **edges = (BMEdge **)MEM_malloc_arrayN(edges_size, sizeof(void *), __func__); + int etot = 0; + + PBVHTopologyUpdateMode ops[2]; + int count = 0; + int i = 0; + int max_steps = max_ii(custom_max_steps, DYNTOPO_MAX_ITER) << 1; + + int totop = 0; + + if (mode & PBVH_Subdivide) { + ops[totop++] = PBVH_Subdivide; + } + if (mode & PBVH_Collapse) { + ops[totop++] = PBVH_Collapse; + } + + int curop = 0; + float limit_len_subd = eq_ctx.limit_len_max_sqr; + float limit_len_cold = eq_ctx.limit_len_min_sqr; + // limit_len_cold = limit_len_cold * limit_len_cold; + + // printf(" minmax queue size: %d\n", BLI_mm_heap_len(eq_ctx.heap_mm)); + + SmallHash subd_edges; + BLI_smallhash_init(&subd_edges); + + while (totop > 0 && !BLI_mm_heap_is_empty(eq_ctx.heap_mm) && i < max_steps) { + BMEdge *e = nullptr; + + if (count >= steps[curop]) { + if (ops[curop] == PBVH_Subdivide) { + modified = true; + BLI_smallhash_clear(&subd_edges, 0); + pbvh_split_edges(&eq_ctx, pbvh, pbvh->header.bm, edges, etot, false); + VALIDATE_LOG(pbvh->bm_log); + etot = 0; + } + + curop = (curop + 1) % totop; + count = 0; + } + + switch (ops[curop]) { + case PBVH_Subdivide: { + if (BLI_mm_heap_max_value(eq_ctx.heap_mm) < limit_len_subd) { + break; + } + + e = (BMEdge *)BLI_mm_heap_pop_max(eq_ctx.heap_mm); + while (!BLI_mm_heap_is_empty(eq_ctx.heap_mm) && e && + (bm_elem_is_free((BMElem *)e, BM_EDGE) || + calc_weighted_length(&eq_ctx, e->v1, e->v2, -1.0) < limit_len_subd)) { + e = (BMEdge *)BLI_mm_heap_pop_max(eq_ctx.heap_mm); + } + + if (!e || bm_elem_is_free((BMElem *)e, BM_EDGE)) { + break; + } + + /* add complete triangles */ + BMLoop *l = e->l; + if (l) { + do { + BMLoop *l2 = l; + do { + if (etot >= edges_size) { + break; + } + + void **val = nullptr; + + if (!BLI_smallhash_ensure_p(&subd_edges, (uintptr_t)l->e, &val)) { + *val = nullptr; + edges[etot++] = e; + } + } while ((l2 = l2->next) != l); + } while ((l = l->radial_next) != e->l); + } + + // edges[etot++] = e; + break; + } + case PBVH_Collapse: { + if (BLI_mm_heap_min_value(eq_ctx.heap_mm) > limit_len_cold) { + break; + } + + e = (BMEdge *)BLI_mm_heap_pop_min(eq_ctx.heap_mm); + while (!BLI_mm_heap_is_empty(eq_ctx.heap_mm) && e && + (bm_elem_is_free((BMElem *)e, BM_EDGE) || + calc_weighted_length(&eq_ctx, e->v1, e->v2, 1.0) > limit_len_cold)) { + e = (BMEdge *)BLI_mm_heap_pop_min(eq_ctx.heap_mm); + } + + if (!e || bm_elem_is_free((BMElem *)e, BM_EDGE)) { + break; + } + + if (bm_elem_is_free((BMElem *)e->v1, BM_VERT) || + bm_elem_is_free((BMElem *)e->v2, BM_VERT)) { + printf("%s: error! operated on freed bmesh elements! e: %p, e->v1: %p, e->v2: %p\n", + __func__, + e, + e->v1, + e->v2); + break; + } + + printf("\n"); + modified = true; + pbvh_bmesh_collapse_edge(pbvh, e, e->v1, e->v2, nullptr, nullptr, &eq_ctx); + VALIDATE_LOG(pbvh->bm_log); + printf("\n"); + + // XXX + BM_log_entry_add_ex(pbvh->header.bm, pbvh->bm_log, true); + break; + } + default: + break; + } + + count++; + i++; + } + + if (etot > 0) { + modified = true; + BLI_smallhash_clear(&subd_edges, 0); + pbvh_split_edges(&eq_ctx, pbvh, pbvh->header.bm, edges, etot, false); + VALIDATE_LOG(pbvh->bm_log); + etot = 0; + } + + MEM_SAFE_FREE(edges); + BLI_smallhash_release(&subd_edges); + + if (mode & PBVH_Cleanup) { + modified |= do_cleanup_3_4( + &eq_ctx, pbvh, center, eq_ctx.view_normal, radius, use_frontface, use_projected); + + VALIDATE_LOG(pbvh->bm_log); + } + + if (modified) { + // avoid potential infinite loops + const int totnode = pbvh->totnode; + + for (int i = 0; i < totnode; i++) { + PBVHNode *node = pbvh->nodes + i; + + if ((node->flag & PBVH_Leaf) && (node->flag & PBVH_UpdateTopology) && + !(node->flag & PBVH_FullyHidden)) { + + /* do not clear PBVH_UpdateTopology here in case split messes with it */ + + /* Recursively split nodes that have gotten too many + * elements */ + if (updatePBVH) { + pbvh_bmesh_node_limit_ensure(pbvh, i); + } + } + } + } + + /* clear PBVH_UpdateTopology flags */ + for (int i = 0; i < pbvh->totnode; i++) { + PBVHNode *node = pbvh->nodes + i; + + if (!(node->flag & PBVH_Leaf)) { + continue; + } + + node->flag &= ~PBVH_UpdateTopology; + } + +#ifdef DYNTOPO_USE_MINMAX_HEAP + if (eq_ctx.heap_mm) { + BLI_mm_heap_free(eq_ctx.heap_mm, nullptr); + } + + if (eq_ctx.used_verts) { + MEM_SAFE_FREE(eq_ctx.used_verts); + // BLI_table_gset_free(eq_ctx.used_verts, nullptr); + } +#endif + + BLI_buffer_free(&edge_loops); + BLI_buffer_free(&deleted_faces); + +#ifdef USE_VERIFY + pbvh_bmesh_verify(pbvh); +#endif + + // ensure triangulations are all up to date + for (int i = 0; i < pbvh->totnode; i++) { + PBVHNode *node = pbvh->nodes + i; + + if (node->flag & PBVH_Leaf) { + // BKE_pbvh_bmesh_check_tris(pbvh, node); + } + } + + /* Push a subentry. */ + BM_log_entry_add_ex(pbvh->header.bm, pbvh->bm_log, true); + + return modified; +} + +#define SPLIT_TAG BM_ELEM_TAG + +/* + +#generate shifted and mirrored patterns +# [number of verts, vert_connections... ] +table = [ + [4, -1, 3, -1, -1], + [5, -1, 3, -1, 0, -1], + [6, -1, 3, -1, 5, -1, 1] +] + +table2 = {} + +def getmask(row): + mask = 0 + for i in range(len(row)): + if row[i] >= 0: + mask |= 1 << i + return mask + +ii = 0 +for row in table: + n = row[0] + row = row[1:] + + mask = getmask(row) + table2[mask] = [n] + row + + for step in range(2): + for i in range(n): + row2 = [] + for j in range(n): + j2 = row[(j + i) % n] + + if j2 != -1: + j2 = (j2 - i + n) % n + + row2.append(j2) + + if row2[0] != -1: + continue + + mask2 = getmask(row2) + if mask2 not in table2: + table2[mask2] = [n] + row2 + + #reverse row + for i in range(n): + if row[i] != -1: + row[i] = n - row[i] + + row.reverse() + +maxk = 0 +for k in table2: + maxk = max(maxk, k) + +buf = 'static const int splitmap[%i][16] = {\n' % (maxk+1) +buf += ' //{numverts, vert_connections...}\n' + +for k in range(maxk+1): + line = "" + + if k not in table2: + line += ' {-1},' + else: + line += ' {' + row = table2[k] + for j in range(len(row)): + if j > 0: + line += ", " + line += str(row[j]) + line += '},' + + while len(line) < 35: + line += " " + line += "//" + str(k) + " " + + if k in table2: + for i in range(table2[k][0]): + ch = "1" if k & (1 << i) else "0" + line += str(ch) + " " + + buf += line + "\n" +buf += '};\n' +print(buf) + +*/ +static const int splitmap[43][16] = { + //{numverts, vert_connections...} + {-1}, // 0 + {-1}, // 1 + {4, -1, 3, -1, -1}, // 2 0 1 0 0 + {-1}, // 3 + {4, -1, -1, 0, -1}, // 4 0 0 1 0 + {-1}, // 5 + {-1}, // 6 + {-1}, // 7 + {4, -1, -1, -1, 1}, // 8 0 0 0 1 + {-1}, // 9 + {5, -1, 3, -1, 0, -1}, // 10 0 1 0 1 0 + {-1}, // 11 + {-1}, // 12 + {-1}, // 13 + {-1}, // 14 + {-1}, // 15 + {-1}, // 16 + {-1}, // 17 + {5, -1, 3, -1, -1, 1}, // 18 0 1 0 0 1 + {-1}, // 19 + {5, -1, -1, 4, -1, 1}, // 20 0 0 1 0 1 + {-1}, // 21 + {-1}, // 22 + {-1}, // 23 + {-1}, // 24 + {-1}, // 25 + {-1}, // 26 + {-1}, // 27 + {-1}, // 28 + {-1}, // 29 + {-1}, // 30 + {-1}, // 31 + {-1}, // 32 + {-1}, // 33 + {-1}, // 34 + {-1}, // 35 + {-1}, // 36 + {-1}, // 37 + {-1}, // 38 + {-1}, // 39 + {-1}, // 40 + {-1}, // 41 + {6, -1, 3, -1, 5, -1, 1}, // 42 0 1 0 1 0 1 +}; + +static void pbvh_split_edges(EdgeQueueContext *eq_ctx, + PBVH *pbvh, + BMesh *bm, + BMEdge **edges1, + int totedge, + bool ignore_isolated_edges) +{ + bm_logstack_push(); + bm_log_message(" == split edges == "); + + BMEdge **edges = edges1; + Vector faces; + + const int node_updateflag = PBVH_UpdateBB | PBVH_UpdateOriginalBB | PBVH_UpdateNormals | + PBVH_UpdateOtherVerts | PBVH_UpdateCurvatureDir | + PBVH_UpdateTriAreas | PBVH_UpdateDrawBuffers | + PBVH_RebuildDrawBuffers | PBVH_UpdateTris | PBVH_UpdateNormals; + + for (int i = 0; i < totedge; i++) { + BMEdge *e = edges[i]; + + check_vert_fan_are_tris(pbvh, e->v1); + check_vert_fan_are_tris(pbvh, e->v2); + } + + for (int i = 0; i < totedge; i++) { + BMEdge *e = edges[i]; + BMLoop *l = e->l; + + /* Clear tags. */ + e->head.hflag &= ~SPLIT_TAG; + e->v1->head.hflag &= ~SPLIT_TAG; + e->v2->head.hflag &= ~SPLIT_TAG; + + if (!l) { + continue; + } + + /* Clear tags in wider neighborhood and flag valence/boundary for update. */ + do { + BMLoop *l2 = l->f->l_first; + + do { + l2->e->head.hflag &= ~SPLIT_TAG; + l2->v->head.hflag &= ~SPLIT_TAG; + + pbvh_boundary_update_bmesh(pbvh, l2->v); + MSculptVert *mv = BKE_PBVH_SCULPTVERT(pbvh->cd_sculpt_vert, l2->v); + MV_ADD_FLAG( + mv, SCULPTVERT_NEED_VALENCE | SCULPTVERT_NEED_DISK_SORT | SCULPTVERT_NEED_TRIANGULATE); + } while ((l2 = l2->next) != l->f->l_first); + + l->f->head.hflag &= ~SPLIT_TAG; + } while ((l = l->radial_next) != e->l); + } + + /* Tag edges and faces to split. */ + for (int i = 0; i < totedge; i++) { + BMEdge *e = edges[i]; + BMLoop *l = e->l; + + e->head.index = 0; + e->head.hflag |= SPLIT_TAG; + + if (!l) { + continue; + } + + do { + if (!(l->f->head.hflag & SPLIT_TAG)) { + BMLoop *l2 = l; + do { + l2->v->head.hflag &= ~BM_ELEM_TAG; + } while ((l2 = l2->next) != l); + l->f->head.hflag |= SPLIT_TAG; + + if (l->f->len == 3) { + l->f->head.index = l->f->len; + faces.append(l->f); + } + } + + } while ((l = l->radial_next) != e->l); + } + + int totface = faces.size(); + for (int i = 0; i < totface; i++) { + BMFace *f = faces[i]; + BMLoop *l = f->l_first; + + if (!ignore_isolated_edges) { + f->head.hflag |= SPLIT_TAG; + BM_log_face_removed(pbvh->header.bm, pbvh->bm_log, f); + BM_idmap_release(pbvh->bm_idmap, (BMElem *)f, true); + } + else { + f->head.hflag &= ~SPLIT_TAG; + } + + int mask = 0; + int j = 0; + int count = 0; + + do { + if (l->e->head.hflag & SPLIT_TAG) { + mask |= 1 << j; + count++; + } + + j++; + } while ((l = l->next) != f->l_first); + + if (ignore_isolated_edges) { + do { + l->e->head.index = MAX2(l->e->head.index, count); + } while ((l = l->next) != f->l_first); + } + + f->head.index = mask; + } + + bm_log_message(" == split edges (edge split) == "); + + for (int i = 0; i < totedge; i++) { + BMEdge *e = edges[i]; + + if (ignore_isolated_edges && e->head.index < 2) { + BMLoop *l = e->l; + + do { + l->f->head.hflag &= ~SPLIT_TAG; + } while ((l = l->radial_next) != e->l); + + edges[i] = nullptr; + continue; + } + + if (ignore_isolated_edges) { + BMLoop *l = e->l; + do { + if (!(l->f->head.hflag & SPLIT_TAG)) { + l->f->head.hflag |= SPLIT_TAG; + BM_log_face_removed(pbvh->header.bm, pbvh->bm_log, l->f); + } + } while ((l = l->radial_next) != e->l); + } + } + + for (int i = 0; i < totedge; i++) { + BMEdge *e = edges[i]; + BMVert *v1 = e->v1; + BMVert *v2 = e->v2; + BMEdge *newe = nullptr; + + if (!e || !(e->head.hflag & SPLIT_TAG)) { + continue; + } + + e->head.hflag &= ~SPLIT_TAG; + + MSculptVert *mv1 = BKE_PBVH_SCULPTVERT(pbvh->cd_sculpt_vert, e->v1); + MSculptVert *mv2 = BKE_PBVH_SCULPTVERT(pbvh->cd_sculpt_vert, e->v2); + + if (mv1->stroke_id != pbvh->stroke_id) { + BKE_pbvh_bmesh_check_origdata(pbvh, e->v1, pbvh->stroke_id); + } + if (mv2->stroke_id != pbvh->stroke_id) { + BKE_pbvh_bmesh_check_origdata(pbvh, e->v2, pbvh->stroke_id); + } + + if (mv1->stroke_id != mv2->stroke_id) { + printf("stroke_id error\n"); + } + + validate_edge(pbvh, pbvh->header.bm, e, true, true); + + BM_idmap_check_assign(pbvh->bm_idmap, (BMElem *)e->v1); + BM_idmap_check_assign(pbvh->bm_idmap, (BMElem *)e->v2); + BM_log_edge_removed(pbvh->header.bm, pbvh->bm_log, e); + + BM_idmap_release(pbvh->bm_idmap, (BMElem *)e, true); + + // BMVert *newv = BM_log_edge_split_do(pbvh->bm_log, e, e->v1, &newe, 0.5f); + BMVert *newv = BM_edge_split(pbvh->header.bm, e, e->v1, &newe, 0.5f); + newe->head.hflag &= ~SPLIT_TAG; + + BM_log_vert_added(bm, pbvh->bm_log, newv); + BM_log_edge_added(bm, pbvh->bm_log, e); + BM_log_edge_added(bm, pbvh->bm_log, newe); + + edge_queue_insert_val34_vert(eq_ctx, newv); + +#ifdef DYNTOPO_USE_MINMAX_HEAP + const float elimit = eq_ctx->limit_len_max; + + if (0 && e->l) { + e->head.hflag &= ~BM_ELEM_TAG; + newe->head.hflag &= ~BM_ELEM_TAG; + + long_edge_queue_edge_add_recursive( + eq_ctx, e->l->radial_next, e->l, len_squared_v3v3(e->v1->co, e->v2->co), elimit, 0); + long_edge_queue_edge_add_recursive(eq_ctx, + newe->l->radial_next, + newe->l, + len_squared_v3v3(newe->v1->co, newe->v2->co), + elimit, + 0); + } + + // edge_queue_insert_unified(eq_ctx, e); + // edge_queue_insert_unified(eq_ctx, newe); + + // BLI_table_gset_add(eq_ctx->used_verts, newv); +#endif + + PBVH_CHECK_NAN(newv->co); + + validate_edge(pbvh, pbvh->header.bm, e, true, true); + validate_edge(pbvh, pbvh->header.bm, newe, true, true); + validate_vert(pbvh, pbvh->header.bm, newv, true, true); + + MSculptVert *mv = BKE_PBVH_SCULPTVERT(pbvh->cd_sculpt_vert, newv); + + newv->head.hflag |= SPLIT_TAG; + + pbvh_boundary_update_bmesh(pbvh, newv); + MV_ADD_FLAG(mv, + SCULPTVERT_NEED_VALENCE | SCULPTVERT_NEED_DISK_SORT | SCULPTVERT_NEED_TRIANGULATE); + mv->stroke_id = pbvh->stroke_id; + + mv = BKE_PBVH_SCULPTVERT(pbvh->cd_sculpt_vert, e->v1 != newv ? e->v1 : e->v2); + pbvh_boundary_update_bmesh(pbvh, e->v1 != newv ? e->v1 : e->v2); + MV_ADD_FLAG(mv, + SCULPTVERT_NEED_DISK_SORT | SCULPTVERT_NEED_VALENCE | SCULPTVERT_NEED_TRIANGULATE); + + BM_ELEM_CD_SET_INT(newv, pbvh->cd_vert_node_offset, DYNTOPO_NODE_NONE); + + int ni = BM_ELEM_CD_GET_INT(v1, pbvh->cd_vert_node_offset); + + if (ni == DYNTOPO_NODE_NONE) { + ni = BM_ELEM_CD_GET_INT(v2, pbvh->cd_vert_node_offset); + } + + if (ni >= pbvh->totnode || !(pbvh->nodes[ni].flag & PBVH_Leaf)) { + printf("%s: error\n", __func__); + } + /* this should rarely happen */ + // if (ni < 0 || ni >= pbvh->totnode || !(pbvh->nodes[ni].flag & PBVH_Leaf)) { + if (ni == DYNTOPO_NODE_NONE) { + ni = DYNTOPO_NODE_NONE; + + for (int j = 0; j < 2; j++) { + BMVert *v = nullptr; + + switch (j) { + case 0: + v = v1; + break; + case 1: + v = v2; + break; + } + + if (!v->e) { + continue; + } + + BMEdge *e2 = v->e; + do { + if (!e2->l) { + break; + } + + BMLoop *l = e2->l; + do { + int ni2 = BM_ELEM_CD_GET_INT(l->f, pbvh->cd_face_node_offset); + + if (ni2 >= 0 && ni2 < pbvh->totnode && (pbvh->nodes[ni2].flag & PBVH_Leaf)) { + ni = ni2; + goto outerbreak; + } + } while ((l = l->radial_next) != e2->l); + } while ((e2 = BM_DISK_EDGE_NEXT(e2, v)) != v->e); + } + outerbreak:; + } + + if (ni != DYNTOPO_NODE_NONE) { + PBVHNode *node = pbvh->nodes + ni; + + if (!(node->flag & PBVH_Leaf)) { + printf("pbvh error in pbvh_split_edges!\n"); + + BM_ELEM_CD_SET_INT(newv, pbvh->cd_vert_node_offset, DYNTOPO_NODE_NONE); + + continue; + } + + node->flag |= (PBVHNodeFlags)node_updateflag; + + BLI_table_gset_add(node->bm_unique_verts, newv); + + BM_ELEM_CD_SET_INT(newv, pbvh->cd_vert_node_offset, ni); + // BM_ELEM_CD_SET_INT(newv, pbvh->cd_vert_node_offset, -1); + } + else { + BM_ELEM_CD_SET_INT(newv, pbvh->cd_vert_node_offset, DYNTOPO_NODE_NONE); + printf("%s: error!\n", __func__); + } + } + + bm_log_message(" == split edges (triangulate) == "); + + /* Subdivide from template. */ + + Vector vs; + Vector newfaces; + + for (int i = 0; i < totface; i++) { + BMFace *f = faces[i]; + int mask = 0; + + if (!(f->head.hflag & SPLIT_TAG)) { + continue; + } + + int ni = BM_ELEM_CD_GET_INT(f, pbvh->cd_face_node_offset); + + if (ni < 0 || ni >= pbvh->totnode || !(pbvh->nodes[ni].flag & PBVH_Leaf)) { + printf("%s: error!\n", __func__); + ni = DYNTOPO_NODE_NONE; + } + + BMLoop *l = f->l_first; + int j = 0; + int totmask = 0; + + do { + if (l->v->head.hflag & SPLIT_TAG) { + mask |= 1 << j; + totmask++; + } + j++; + } while ((l = l->next) != f->l_first); + + int flen = j; + + if (mask < 0 || mask >= (int)ARRAY_SIZE(splitmap)) { + printf("splitmap error! flen: %d totmask: %d mask: %d\n", flen, totmask, mask); + continue; + } + + const int *pat = splitmap[mask]; + int n = pat[0]; + + if (n < 0) { + continue; + } + + if (n != f->len || n != flen) { + printf("%s: error! %d %d\n", __func__, n, flen); + continue; + } + + BMFace *f2 = f; + + vs.resize(n); + + l = f->l_first; + j = 0; + do { + vs[j++] = l->v; + } while ((l = l->next) != f->l_first); + + if (j != n) { + printf("error! %s\n", __func__); + continue; + } + + newfaces.resize(newfaces.size() + n); + + int count = 0; + + for (j = 0; j < n; j++) { + if (pat[j + 1] < 0) { + continue; + } + + BMVert *v1 = vs[j], *v2 = vs[pat[j + 1]]; + BMLoop *l1 = nullptr, *l2 = nullptr; + BMLoop *rl = nullptr; + + BMLoop *l3 = f2->l_first; + do { + if (l3->v == v1) { + l1 = l3; + } + else if (l3->v == v2) { + l2 = l3; + } + } while ((l3 = l3->next) != f2->l_first); + + if (l1 == l2 || !l1 || !l2) { + printf("errorl!\n"); + continue; + } + + validate_face(pbvh, bm, f2, false, true); + + bool log_edge = true; + BMFace *newf = nullptr; + BMEdge *exist_e; + + if ((exist_e = BM_edge_exists(v1, v2))) { + log_edge = false; + + BMLoop *l1 = exist_e->l; + + if (l1 && l1->f == f2) { + l1 = l1->radial_next; + } + + if (l1 && l1->f != f2) { + // newf = l1->f; + } + } + else { + newf = BM_face_split(bm, f2, l1, l2, &rl, nullptr, false); + } + + if (newf) { + // rl->e->head.hflag &= ~BM_ELEM_TAG; + // edge_queue_insert_unified(eq_ctx, rl->e); + /* + long_edge_queue_edge_add_recursive_3(eq_ctx, + rl->radial_next, + rl, + len_squared_v3v3(rl->e->v1->co, rl->e->v2->co), + pbvh->bm_max_edge_len, + 0); + */ + + check_face_is_manifold(pbvh, bm, newf); + check_face_is_manifold(pbvh, bm, f2); + check_face_is_manifold(pbvh, bm, f); + + validate_face(pbvh, bm, f2, false, true); + validate_face(pbvh, bm, newf, false, true); + + if (log_edge) { + BM_log_edge_added(bm, pbvh->bm_log, rl->e); + } + + bool ok = ni != DYNTOPO_NODE_NONE; + ok = ok && BM_ELEM_CD_GET_INT(v1, pbvh->cd_vert_node_offset) != DYNTOPO_NODE_NONE; + ok = ok && BM_ELEM_CD_GET_INT(v2, pbvh->cd_vert_node_offset) != DYNTOPO_NODE_NONE; + + if (ok) { + PBVHNode *node = pbvh->nodes + ni; + + node->flag |= (PBVHNodeFlags)node_updateflag; + + BLI_table_gset_add(node->bm_faces, newf); + BM_ELEM_CD_SET_INT(newf, pbvh->cd_face_node_offset, ni); + } + else { + BM_ELEM_CD_SET_INT(newf, pbvh->cd_face_node_offset, DYNTOPO_NODE_NONE); + } + + if (count < n) { + newfaces[count++] = newf; + } + else { + printf("%s: error 4!\n", __func__); + } + f2 = newf; + } + else { + printf("%s: split error 2!\n", __func__); + continue; + } + } + + for (j = 0; j < count; j++) { + if (BM_ELEM_CD_GET_INT(newfaces[j], pbvh->cd_face_node_offset) == DYNTOPO_NODE_NONE) { + BKE_pbvh_bmesh_add_face(pbvh, newfaces[j], false, true); + } + + if (newfaces[j] != f) { + BM_log_face_added(bm, pbvh->bm_log, newfaces[j]); + } +#if 1 + if (newfaces[j]->len != 3) { + printf("%s: tesselation error!\n", __func__); + } +#endif + } + + if (f->len != 3) { + printf("%s: tesselation error!\n", __func__); + } + + if (BM_ELEM_CD_GET_INT(f, pbvh->cd_face_node_offset) == DYNTOPO_NODE_NONE) { + BKE_pbvh_bmesh_add_face(pbvh, f, false, true); + } + + BM_log_face_added(bm, pbvh->bm_log, f); + } + + bm_logstack_pop(); +} + +typedef struct DynTopoState { + PBVH *pbvh; + bool is_fake_pbvh; +} DynTopoState; + +/* existing_pbvh may be nullptr, if so a fake one will be created. +Note that all the sculpt customdata layers will be created +if they don't exist, so cd_vert/face_node_offset, cd_mask_offset, +cd_sculpt_vert, etc*/ +DynTopoState *BKE_dyntopo_init(BMesh *bm, PBVH *existing_pbvh) +{ + PBVH *pbvh; + + if (!existing_pbvh) { + pbvh = MEM_cnew("pbvh"); + + pbvh->nodes = static_cast(MEM_callocN(sizeof(PBVHNode), "PBVHNode")); + pbvh->header.type = PBVH_BMESH; + pbvh->totnode = 1; + + PBVHNode *node = pbvh->nodes; + + node->flag = PBVH_Leaf | PBVH_UpdateTris | PBVH_UpdateTriAreas; + node->bm_faces = BLI_table_gset_new_ex("node->bm_faces", bm->totface); + node->bm_unique_verts = BLI_table_gset_new_ex("node->bm_unique_verts", bm->totvert); + } + else { + pbvh = existing_pbvh; + } + + if (!pbvh->bm_idmap) { + pbvh->bm_idmap = BM_idmap_new(bm, BM_VERT | BM_EDGE | BM_FACE); + } + + const bool isfake = pbvh != existing_pbvh; + + BMCustomLayerReq vlayers[] = {{CD_PAINT_MASK, nullptr, 0}, + {CD_DYNTOPO_VERT, nullptr, CD_FLAG_TEMPORARY | CD_FLAG_NOCOPY}, + {CD_PROP_INT32, + SCULPT_ATTRIBUTE_NAME(dyntopo_node_id_vertex), + CD_FLAG_TEMPORARY | CD_FLAG_NOCOPY}}; + + BMCustomLayerReq flayers[] = { + {CD_PROP_FLOAT2, SCULPT_ATTRIBUTE_NAME(face_areas), CD_FLAG_TEMPORARY | CD_FLAG_NOCOPY}, + {CD_PROP_INT32, ".sculpt_face_set", 0}, + {CD_PROP_INT32, + SCULPT_ATTRIBUTE_NAME(dyntopo_node_id_face), + CD_FLAG_TEMPORARY | CD_FLAG_NOCOPY}}; + + BM_data_layers_ensure(bm, &bm->vdata, vlayers, 3); + BM_data_layers_ensure(bm, &bm->pdata, flayers, 3); + + pbvh->header.bm = bm; + + pbvh->cd_vert_node_offset = CustomData_get_named_offset( + &bm->vdata, CD_PROP_INT32, SCULPT_ATTRIBUTE_NAME(dyntopo_node_id_vertex)); + + pbvh->cd_face_node_offset = CustomData_get_named_offset( + &bm->pdata, CD_PROP_INT32, SCULPT_ATTRIBUTE_NAME(dyntopo_node_id_face)); + + pbvh->cd_face_area = CustomData_get_named_offset( + &bm->pdata, CD_PROP_FLOAT2, SCULPT_ATTRIBUTE_NAME(face_areas)); + + pbvh->cd_sculpt_vert = CustomData_get_offset(&bm->vdata, CD_DYNTOPO_VERT); + pbvh->cd_vert_mask_offset = CustomData_get_offset(&bm->vdata, CD_PAINT_MASK); + pbvh->cd_faceset_offset = CustomData_get_offset_named( + &bm->pdata, CD_PROP_INT32, ".sculpt_face_set"); + pbvh->cd_vcol_offset = -1; + + if (isfake) { + pbvh->bm_log = BM_log_create(bm, pbvh->bm_idmap); + } + + BMVert *v; + BMFace *f; + BMIter iter; + + if (isfake) { + BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) { + BM_ELEM_CD_SET_INT(v, pbvh->cd_vert_node_offset, 0); + BLI_table_gset_add(pbvh->nodes->bm_unique_verts, v); + } + + BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { + BM_ELEM_CD_SET_INT(f, pbvh->cd_face_node_offset, 0); + BLI_table_gset_add(pbvh->nodes->bm_faces, f); + } + + BKE_pbvh_bmesh_check_tris(pbvh, pbvh->nodes); + } + + DynTopoState *ds = MEM_cnew("DynTopoState"); + + ds->pbvh = pbvh; + ds->is_fake_pbvh = isfake; + + return ds; +} + +void BKE_dyntopo_default_params(DynRemeshParams *params, float edge_size) +{ + memset(params, 0, sizeof(*params)); + params->detail_range = 0.45f; + params->edge_size = edge_size; +} + +void BKE_dyntopo_free(DynTopoState *ds) +{ + if (ds->is_fake_pbvh) { + BM_log_free(ds->pbvh->bm_log, false); + BM_idmap_destroy(ds->pbvh->bm_idmap); + + PBVHNode *node = ds->pbvh->nodes; + + if (node->tribuf || node->tri_buffers) { + BKE_pbvh_bmesh_free_tris(ds->pbvh, node); + } + + BLI_table_gset_free(node->bm_faces, nullptr); + BLI_table_gset_free(node->bm_unique_verts, nullptr); + + MEM_freeN(ds->pbvh->nodes); + MEM_freeN(ds->pbvh); + } + + MEM_freeN(ds); +} + +/* +bool BKE_pbvh_bmesh_update_topology(PBVH *pbvh, + PBVHTopologyUpdateMode mode, + const float center[3], + const float view_normal[3], + float radius, + const bool use_frontface, + const bool use_projected, + int sym_axis, + bool updatePBVH, + DyntopoMaskCB mask_cb, + void *mask_cb_data, + int custom_max_steps, + bool disable_surface_relax) +*/ +void BKE_dyntopo_remesh(DynTopoState *ds, + DynRemeshParams *params, + int steps, + PBVHTopologyUpdateMode mode) +{ + float cent[3] = {0.0f, 0.0f, 0.0f}; + int totcent = 0; + float view[3] = {0.0f, 0.0f, 1.0f}; + + BMIter iter; + BMVert *v; + + BM_ITER_MESH (v, &iter, ds->pbvh->header.bm, BM_VERTS_OF_MESH) { + MSculptVert *mv = BKE_PBVH_SCULPTVERT(ds->pbvh->cd_sculpt_vert, v); + + pbvh_boundary_update_bmesh(ds->pbvh, v); + mv->flag |= SCULPTVERT_NEED_TRIANGULATE; + mv->valence = BM_vert_edge_count(v); + + pbvh_check_vert_boundary(ds->pbvh, v); + + add_v3_v3(cent, v->co); + totcent++; + } + + if (totcent) { + mul_v3_fl(cent, 1.0f / (float)totcent); + } + + ds->pbvh->bm_max_edge_len = params->edge_size; + ds->pbvh->bm_min_edge_len = params->edge_size * params->detail_range; + ds->pbvh->bm_detail_range = params->detail_range; + + /* subdivide once */ + if (mode & PBVH_Subdivide) { + BKE_pbvh_bmesh_update_topology(ds->pbvh, + PBVH_Subdivide, + cent, + view, + 1e17, + false, + false, + 0, + false, + mask_cb_nop, + nullptr, + ds->pbvh->header.bm->totedge, + false, + false); + } + + for (int i = 0; i < steps; i++) { + for (int j = 0; j < ds->pbvh->totnode; j++) { + PBVHNode *node = ds->pbvh->nodes + j; + + if (node->flag & PBVH_Leaf) { + node->flag |= PBVH_UpdateTopology; + } + } + + BKE_pbvh_bmesh_update_topology(ds->pbvh, + mode, + cent, + view, + 1e17, + false, + false, + 0, + false, + mask_cb_nop, + nullptr, + ds->pbvh->header.bm->totedge * 5, + true, + false); + + BKE_pbvh_update_normals(ds->pbvh, nullptr); + + BM_ITER_MESH (v, &iter, ds->pbvh->header.bm, BM_VERTS_OF_MESH) { + MSculptVert *mv = BKE_PBVH_SCULPTVERT(ds->pbvh->cd_sculpt_vert, v); + + pbvh_check_vert_boundary(ds->pbvh, v); + int boundflag = BM_ELEM_CD_GET_INT(v, ds->pbvh->cd_boundary_flag); + + float avg[3] = {0.0f, 0.0f, 0.0f}; + float totw = 0.0f; + + bool bound1 = boundflag & SCULPTVERT_ALL_BOUNDARY; + if (bound1) { + continue; + } + + if (boundflag & SCULPTVERT_ALL_CORNER) { + continue; + } + + if (!v->e) { + continue; + } + + BMEdge *e = v->e; + do { + BMVert *v2 = BM_edge_other_vert(e, v); + MSculptVert *mv2 = BKE_PBVH_SCULPTVERT(ds->pbvh->cd_sculpt_vert, v2); + + pbvh_check_vert_boundary(ds->pbvh, v2); + int boundflag2 = BM_ELEM_CD_GET_INT(v2, ds->pbvh->cd_boundary_flag); + + bool bound2 = boundflag2 & SCULPTVERT_ALL_BOUNDARY; + + if (bound1 && !bound2) { + continue; + } + + float tmp[3]; + float w = 1.0f; + + sub_v3_v3v3(tmp, v2->co, v->co); + madd_v3_v3fl(tmp, v->no, -dot_v3v3(v->no, tmp) * 0.75); + add_v3_v3(tmp, v->co); + madd_v3_v3fl(avg, tmp, w); + + totw += w; + } while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e); + + if (totw == 0.0f) { + continue; + } + + mul_v3_fl(avg, 1.0f / totw); + interp_v3_v3v3(v->co, v->co, avg, 0.5f); + } + } +} +} // namespace blender::dyntopo + +void BKE_pbvh_bmesh_remove_face(PBVH *pbvh, BMFace *f, bool log_face) +{ + blender::dyntopo::pbvh_bmesh_face_remove(pbvh, f, log_face, true, true); +} + +void BKE_pbvh_bmesh_remove_edge(PBVH *pbvh, BMEdge *e, bool log_edge) +{ + if (log_edge) { + bm_logstack_push(); + BM_log_edge_removed(pbvh->header.bm, pbvh->bm_log, e); + bm_logstack_pop(); + } +} + +void BKE_pbvh_bmesh_remove_vertex(PBVH *pbvh, BMVert *v, bool log_vert) +{ + blender::dyntopo::pbvh_bmesh_vert_remove(pbvh, v); + + if (log_vert) { + BM_log_vert_removed(pbvh->header.bm, pbvh->bm_log, v); + } +} + +void BKE_pbvh_bmesh_add_face(PBVH *pbvh, struct BMFace *f, bool log_face, bool force_tree_walk) +{ + bm_logstack_push(); + + int ni = DYNTOPO_NODE_NONE; + + if (force_tree_walk) { + bke_pbvh_insert_face(pbvh, f); + + if (log_face) { + BM_log_face_added(pbvh->header.bm, pbvh->bm_log, f); + } + + bm_logstack_pop(); + return; + } + + // look for node in surrounding geometry + BMLoop *l = f->l_first; + do { + ni = BM_ELEM_CD_GET_INT(l->radial_next->f, pbvh->cd_face_node_offset); + + if (ni >= 0 && (!(pbvh->nodes[ni].flag & PBVH_Leaf) || ni >= pbvh->totnode)) { + printf("%s: error: ni: %d totnode: %d\n", __func__, ni, pbvh->totnode); + l = l->next; + continue; + } + + if (ni >= 0 && (pbvh->nodes[ni].flag & PBVH_Leaf)) { + break; + } + + l = l->next; + } while (l != f->l_first); + + if (ni < 0) { + bke_pbvh_insert_face(pbvh, f); + } + else { + BM_ELEM_CD_SET_INT(f, pbvh->cd_face_node_offset, ni); + bke_pbvh_insert_face_finalize(pbvh, f, ni); + } + + if (log_face) { + BM_log_face_added(pbvh->header.bm, pbvh->bm_log, f); + } + + bm_logstack_pop(); +} diff --git a/source/blender/blenkernel/intern/dyntopo_collapse.cc b/source/blender/blenkernel/intern/dyntopo_collapse.cc new file mode 100644 index 00000000000..146c782d2ff --- /dev/null +++ b/source/blender/blenkernel/intern/dyntopo_collapse.cc @@ -0,0 +1,828 @@ +#include "MEM_guardedalloc.h" + +#include "DNA_customdata_types.h" +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_modifier_types.h" + +#include "BLI_array.hh" +#include "BLI_asan.h" +#include "BLI_buffer.h" +#include "BLI_index_range.hh" +#include "BLI_map.hh" +#include "BLI_set.hh" +#include "BLI_task.hh" +#include "BLI_vector.hh" + +#include "BLI_alloca.h" +#include "BLI_compiler_attrs.h" +#include "BLI_compiler_compat.h" +#include "BLI_math.h" +#include "BLI_utildefines.h" + +#include "PIL_time.h" +#include "atomic_ops.h" + +#include "BKE_customdata.h" +#include "BKE_dyntopo.h" +#include "BKE_paint.h" +#include "BKE_pbvh.h" + +#include "bmesh.h" +#include "bmesh_log.h" + +#include "dyntopo_intern.hh" +#include "pbvh_intern.hh" + +#include +#include + +using blender::float2; +using blender::float3; +using blender::float4; +using blender::IndexRange; +using blender::Map; +using blender::Set; +using blender::Vector; + +namespace blender::dyntopo { + +typedef struct TraceData { + PBVH *pbvh; + BMEdge *e; +} TraceData; + +// copied from decimate modifier code +inline bool bm_edge_collapse_is_degenerate_topology(BMEdge *e_first) +{ + /* simply check that there is no overlap between faces and edges of each vert, + * (excluding the 2 faces attached to 'e' and 'e' its self) */ + + BMEdge *e_iter; + + /* clear flags on both disks */ + e_iter = e_first; + do { + if (!bm_edge_is_manifold_or_boundary(e_iter->l)) { + return true; + } + bm_edge_tag_disable(e_iter); + } while ((e_iter = BM_DISK_EDGE_NEXT(e_iter, e_first->v1)) != e_first); + + e_iter = e_first; + do { + if (!bm_edge_is_manifold_or_boundary(e_iter->l)) { + return true; + } + bm_edge_tag_disable(e_iter); + } while ((e_iter = BM_DISK_EDGE_NEXT(e_iter, e_first->v2)) != e_first); + + /* now enable one side... */ + e_iter = e_first; + do { + bm_edge_tag_enable(e_iter); + } while ((e_iter = BM_DISK_EDGE_NEXT(e_iter, e_first->v1)) != e_first); + + /* ... except for the edge we will collapse, we know that's shared, + * disable this to avoid false positive. We could be smart and never enable these + * face/edge tags in the first place but easier to do this */ + // bm_edge_tag_disable(e_first); + /* do inline... */ + { +#if 0 + BMIter iter; + BMIter liter; + BMLoop *l; + BMVert *v; + BM_ITER_ELEM (l, &liter, e_first, BM_LOOPS_OF_EDGE) { + BM_elem_flag_disable(l->f, BM_ELEM_TAG); + BM_ITER_ELEM (v, &iter, l->f, BM_VERTS_OF_FACE) { + BM_elem_flag_disable(v, BM_ELEM_TAG); + } + } +#else + /* we know each face is a triangle, no looping/iterators needed here */ + + BMLoop *l_radial; + BMLoop *l_face; + + l_radial = e_first->l; + l_face = l_radial; + BLI_assert(l_face->f->len == 3); + BM_elem_flag_disable(l_face->f, BM_ELEM_TAG); + BM_elem_flag_disable((l_face = l_radial)->v, BM_ELEM_TAG); + BM_elem_flag_disable((l_face = l_face->next)->v, BM_ELEM_TAG); + BM_elem_flag_disable((l_face->next)->v, BM_ELEM_TAG); + l_face = l_radial->radial_next; + if (l_radial != l_face) { + BLI_assert(l_face->f->len == 3); + BM_elem_flag_disable(l_face->f, BM_ELEM_TAG); + BM_elem_flag_disable((l_face = l_radial->radial_next)->v, BM_ELEM_TAG); + BM_elem_flag_disable((l_face = l_face->next)->v, BM_ELEM_TAG); + BM_elem_flag_disable((l_face->next)->v, BM_ELEM_TAG); + } +#endif + } + + /* and check for overlap */ + e_iter = e_first; + do { + if (bm_edge_tag_test(e_iter)) { + return true; + } + } while ((e_iter = BM_DISK_EDGE_NEXT(e_iter, e_first->v2)) != e_first); + + return false; +} + +/* Faces *outside* the ring region are tagged with facetag, used to detect + * border edges. + */ +static void vert_ring_do_tag(BMVert *v, int tag, int facetag, int depth) +{ + + BMEdge *e = v->e; + do { + BMVert *v2 = BM_edge_other_vert(e, v); + + if (depth > 0) { + vert_ring_do_tag(v2, tag, facetag, depth - 1); + } + + e->head.hflag |= tag; + v2->head.hflag |= tag; + + if (!e->l) { + continue; + } + + BMLoop *l = e->l; + do { + l->f->head.hflag |= tag; + + BMLoop *l2 = l; + do { + l2->v->head.hflag |= tag; + l2->e->head.hflag |= tag; + l2->f->head.hflag |= tag; + + /*set up face tags for faces outside this region*/ + BMLoop *l3 = l2->radial_next; + + do { + l3->f->head.hflag |= facetag; + } while ((l3 = l3->radial_next) != l2); + + } while ((l2 = l2->next) != l); + } while ((l = l->radial_next) != e->l); + } while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e); +} + +static void vert_ring_untag_inner_faces(BMVert *v, int tag, int facetag, int depth) +{ + if (!v->e) { + return; + } + + BMEdge *e = v->e; + + /* untag faces inside this region with facetag */ + do { + BMLoop *l = e->l; + + if (depth > 0) { + BMVert *v2 = BM_edge_other_vert(e, v); + vert_ring_untag_inner_faces(v2, tag, facetag, depth - 1); + } + + if (!l) { + continue; + } + + do { + l->f->head.hflag &= ~facetag; + } while ((l = l->radial_next) != e->l); + } while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e); +} + +void vert_ring_do_apply(BMVert *v, + std::function callback, + void *userdata, + int tag, + int facetag, + int depth) +{ + BMEdge *e = v->e; + + callback((BMElem *)v, userdata); + v->head.hflag &= ~tag; + + e = v->e; + do { + BMVert *v2 = BM_edge_other_vert(e, v); + + if (depth > 0) { + vert_ring_do_apply(v2, callback, userdata, tag, facetag, depth - 1); + } + + if (v2->head.hflag & tag) { + v2->head.hflag &= ~tag; + callback((BMElem *)v2, userdata); + } + if (e->head.hflag & tag) { + e->head.hflag &= ~tag; + callback((BMElem *)e, userdata); + } + + if (!e->l) { + continue; + } + + BMLoop *l = e->l; + do { + BMLoop *l2 = l; + + do { + if (l2->v->head.hflag & tag) { + l2->v->head.hflag &= ~tag; + callback((BMElem *)l2->v, userdata); + } + + if (l2->e->head.hflag & tag) { + l2->e->head.hflag &= ~tag; + callback((BMElem *)l2->e, userdata); + } + + if (l2->f->head.hflag & tag) { + l2->f->head.hflag &= ~tag; + callback((BMElem *)l2->f, userdata); + } + } while ((l2 = l2->next) != l); + } while ((l = l->radial_next) != e->l); + } while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e); +} + +const int COLLAPSE_TAG = BM_ELEM_INTERNAL_TAG; +const int COLLAPSE_FACE_TAG = BM_ELEM_TAG_ALT; + +static void collapse_ring_callback_pre(BMElem *elem, void *userdata) +{ + TraceData *data = static_cast(userdata); + + BM_idmap_check_assign(data->pbvh->bm_idmap, elem); + BMesh *bm = data->pbvh->header.bm; + + switch (elem->head.htype) { + case BM_VERT: { + BMVert *v = reinterpret_cast(elem); + + BM_log_vert_removed(bm, data->pbvh->bm_log, v); + pbvh_bmesh_vert_remove(data->pbvh, v); + BM_idmap_release(data->pbvh->bm_idmap, elem, false); + break; + } + case BM_EDGE: { + BMEdge *e = reinterpret_cast(elem); + BM_log_edge_removed(bm, data->pbvh->bm_log, e); + BM_idmap_release(data->pbvh->bm_idmap, elem, false); + break; + } + case BM_FACE: { + BMFace *f = reinterpret_cast(elem); + BM_log_face_removed(bm, data->pbvh->bm_log, f); + pbvh_bmesh_face_remove(data->pbvh, f, false, false, false); + BM_idmap_release(data->pbvh->bm_idmap, elem, false); + break; + } + } +} + +static void check_new_elem_id(BMElem *elem, TraceData *data) +{ + int id = BM_ELEM_CD_GET_INT(elem, data->pbvh->bm_idmap->cd_id_off[elem->head.htype]); + if (id >= 0) { + BMElem *existing = id < data->pbvh->bm_idmap->map_size ? + BM_idmap_lookup(data->pbvh->bm_idmap, id) : + nullptr; + + if (existing) { + BM_idmap_release(data->pbvh->bm_idmap, existing, true); + } + + BM_idmap_assign(data->pbvh->bm_idmap, elem, id); + + if (existing) { + BM_idmap_check_assign(data->pbvh->bm_idmap, existing); + } + } + else { + BM_idmap_check_assign(data->pbvh->bm_idmap, elem); + } +} + +static void collapse_ring_callback_post(BMElem *elem, void *userdata) +{ + TraceData *data = static_cast(userdata); + BMesh *bm = data->pbvh->header.bm; + + switch (elem->head.htype) { + case BM_VERT: { + BMVert *v = reinterpret_cast(elem); + + MSculptVert *mv = (MSculptVert *)BM_ELEM_CD_GET_VOID_P(v, data->pbvh->cd_sculpt_vert); + mv->flag |= SCULPTVERT_NEED_VALENCE | SCULPTVERT_NEED_DISK_SORT; + + check_new_elem_id(elem, data); + BM_log_vert_added(bm, data->pbvh->bm_log, v); + break; + } + case BM_EDGE: { + BMEdge *e = reinterpret_cast(elem); + check_new_elem_id(elem, data); + + BM_log_edge_added(bm, data->pbvh->bm_log, e); + break; + } + case BM_FACE: { + BMFace *f = reinterpret_cast(elem); + check_new_elem_id(elem, data); + + BM_log_face_added(bm, data->pbvh->bm_log, f); + BKE_pbvh_bmesh_add_face(data->pbvh, f, false, false); + break; + } + } +} + +static void edge_ring_do_old(BMEdge *e, + std::function callback, + void *userdata, + int tag, + int facetag, + int depth) +{ + + vert_ring_do_tag(e->v1, tag, facetag, depth); + vert_ring_do_tag(e->v2, tag, facetag, depth); + + vert_ring_untag_inner_faces(e->v1, tag, facetag, depth); + vert_ring_untag_inner_faces(e->v2, tag, facetag, depth); + + vert_ring_do_apply(e->v1, callback, userdata, tag, facetag, depth); + vert_ring_do_apply(e->v2, callback, userdata, tag, facetag, depth); +} + +static void vert_ring_do(BMVert *v, + BMVert *v_extra, + void (*callback)(BMElem *elem, void *userdata), + void *userdata, + int tag, + int facetag, + int depth) +{ + blender::Set faces; + + std::function recurse = [&](BMVert *v, int depth) { + if (!v->e) { + return; + } + + const int max_depth = 1; + BMEdge *e = v->e; + do { + BMVert *v2 = BM_edge_other_vert(e, v); + + if (!e->l) { + if (depth < max_depth) { + recurse(v2, depth + 1); + } + continue; + } + + BMLoop *l = e->l; + do { + faces.add(l->f); + } while ((l = l->radial_next) != e->l); + + if (depth < max_depth) { + recurse(v2, depth + 1); + } + } while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e); + }; + + recurse(v, 0); + if (v_extra) { + recurse(v_extra, 0); + } + + blender::Set verts; + blender::Set edges; + + for (BMFace *f : faces) { + BMLoop *l = f->l_first; + + do { + bool bad = false; + + BMLoop *l2 = l->radial_next; + do { + if (!faces.contains(l2->f)) { + bad = true; + break; + } + } while ((l2 = l2->radial_next) != l); + + if (!bad) { + edges.add(l->e); + } + } while ((l = l->next) != f->l_first); + } + + for (BMFace *f : faces) { + BMLoop *l = f->l_first; + do { + bool bad = false; + BMEdge *e = l->v->e; + + do { + if (!edges.contains(e)) { + bad = true; + break; + } + } while ((e = BM_DISK_EDGE_NEXT(e, l->v)) != l->v->e); + + if (!bad) { + verts.add(l->v); + } + } while ((l = l->next) != f->l_first); + } + + for (BMFace *f : faces) { + callback(reinterpret_cast(f), userdata); + } + for (BMEdge *e : edges) { + callback(reinterpret_cast(e), userdata); + } + for (BMVert *v2 : verts) { + callback(reinterpret_cast(v2), userdata); + } +} + +bool pbvh_bmesh_collapse_edge_uvs( + PBVH *pbvh, BMEdge *e, BMVert *v_conn, BMVert *v_del, EdgeQueueContext *eq_ctx) +{ + bm_logstack_push(); + + MSculptVert *mv1 = BKE_PBVH_SCULPTVERT(pbvh->cd_sculpt_vert, v_conn); + MSculptVert *mv2 = BKE_PBVH_SCULPTVERT(pbvh->cd_sculpt_vert, v_del); + int boundflag1 = BM_ELEM_CD_GET_INT(v_conn, pbvh->cd_boundary_flag); + int boundflag2 = BM_ELEM_CD_GET_INT(v_del, pbvh->cd_boundary_flag); + + int uvidx = pbvh->header.bm->ldata.typemap[CD_PROP_FLOAT2]; + CustomDataLayer *uv_layer = nullptr; + int totuv = 0; + + if (uvidx >= 0) { + uv_layer = pbvh->header.bm->ldata.layers + uvidx; + totuv = 0; + + while (uvidx < pbvh->header.bm->ldata.totlayer && + pbvh->header.bm->ldata.layers[uvidx].type == CD_PROP_FLOAT2) { + uvidx++; + totuv++; + } + } + + /*have to check edge flags directly, vertex flag test above isn't specific enough and + can sometimes let bad edges through*/ + if ((boundflag1 & SCULPT_BOUNDARY_SHARP) && (e->head.hflag & BM_ELEM_SMOOTH)) { + bm_logstack_pop(); + return false; + } + if ((boundflag1 & SCULPT_BOUNDARY_SEAM) && !(e->head.hflag & BM_ELEM_SEAM)) { + bm_logstack_pop(); + return false; + } + + bool snap = !(boundflag2 & SCULPTVERT_ALL_CORNER); + + /* snap customdata */ + if (snap) { + int ni_conn = BM_ELEM_CD_GET_INT(v_conn, pbvh->cd_vert_node_offset); + + const float v_ws[2] = {0.5f, 0.5f}; + const void *v_blocks[2] = {v_del->head.data, v_conn->head.data}; + + CustomData_bmesh_interp( + &pbvh->header.bm->vdata, v_blocks, v_ws, nullptr, 2, v_conn->head.data); + BM_ELEM_CD_SET_INT(v_conn, pbvh->cd_vert_node_offset, ni_conn); + } + + // deal with UVs + if (e->l) { + BMLoop *l = e->l; + + for (int step = 0; step < 2; step++) { + BMVert *v = step ? e->v2 : e->v1; + BMEdge *e2 = v->e; + + if (!e2) { + continue; + } + + do { + BMLoop *l2 = e2->l; + + if (!l2) { + continue; + } + + do { + BMLoop *l3 = l2->v != v ? l2->next : l2; + + /* store visit bits for each uv layer in l3->head.index */ + l3->head.index = 0; + } while ((l2 = l2->radial_next) != e2->l); + } while ((e2 = BM_DISK_EDGE_NEXT(e2, v)) != v->e); + } + + float(*uv)[2] = static_cast(BLI_array_alloca(uv, 4 * totuv)); + + do { + const void *ls2[2] = {l->head.data, l->next->head.data}; + float ws2[2] = {0.5f, 0.5f}; + + if (!snap) { + const int axis = l->v == v_del ? 0 : 1; + + ws2[axis] = 0.0f; + ws2[axis ^ 1] = 1.0f; + } + + for (int step = 0; uv_layer && step < 2; step++) { + BMLoop *l1 = step ? l : l->next; + + for (int k = 0; k < totuv; k++) { + float *luv = (float *)BM_ELEM_CD_GET_VOID_P(l1, uv_layer[k].offset); + + copy_v2_v2(uv[k * 2 + step], luv); + } + } + + CustomData_bmesh_interp(&pbvh->header.bm->ldata, ls2, ws2, nullptr, 2, l->head.data); + CustomData_bmesh_copy_data( + &pbvh->header.bm->ldata, &pbvh->header.bm->ldata, l->head.data, &l->next->head.data); + + for (int step = 0; totuv >= 0 && step < 2; step++) { + BMVert *v = step ? l->next->v : l->v; + BMLoop *l1 = step ? l->next : l; + BMEdge *e2 = v->e; + + do { + BMLoop *l2 = e2->l; + + if (!l2) { + continue; + } + + do { + BMLoop *l3 = l2->v != v ? l2->next : l2; + + if (!l3 || l3 == l1 || l3 == l || l3 == l->next) { + continue; + } + + for (int k = 0; k < totuv; k++) { + const int flag = 1 << k; + + if (l3->head.index & flag) { + continue; + } + + const int cd_uv = uv_layer[k].offset; + + float *luv1 = (float *)BM_ELEM_CD_GET_VOID_P(l1, cd_uv); + float *luv2 = (float *)BM_ELEM_CD_GET_VOID_P(l3, cd_uv); + + float dx = luv2[0] - uv[k * 2 + step][0]; + float dy = luv2[1] - uv[k * 2 + step][1]; + + float delta = dx * dx + dy * dy; + + if (delta < 0.001) { + l3->head.index |= flag; + copy_v2_v2(luv2, luv1); + } + } + } while ((l2 = l2->radial_next) != e2->l); + } while ((e2 = BM_DISK_EDGE_NEXT(e2, v)) != v->e); + } + } while ((l = l->radial_next) != e->l); + } + + bm_logstack_pop(); + return snap; +} + +/* + * This function is rather complicated. It has to + * snap UVs, log geometry and free ids. + */ +BMVert *pbvh_bmesh_collapse_edge(PBVH *pbvh, + BMEdge *e, + BMVert *v1, + BMVert *v2, + GHash *deleted_verts, + BLI_Buffer *deleted_faces, + EdgeQueueContext *eq_ctx) +{ + bm_logstack_push(); + + BMVert *v_del, *v_conn; + + if (pbvh->dyntopo_stop) { + bm_logstack_pop(); + return nullptr; + } + + pbvh_check_vert_boundary(pbvh, v1); + pbvh_check_vert_boundary(pbvh, v2); + + TraceData tdata; + + tdata.pbvh = pbvh; + tdata.e = e; + + const int mupdateflag = SCULPTVERT_NEED_VALENCE | SCULPTVERT_NEED_DISK_SORT; + // updateflag |= SCULPTVERT_NEED_TRIANGULATE; // to check for non-manifold flaps + + validate_edge(pbvh, pbvh->header.bm, e, true, true); + + check_vert_fan_are_tris(pbvh, e->v1); + check_vert_fan_are_tris(pbvh, e->v2); + + MSculptVert *mv1 = BKE_PBVH_SCULPTVERT(pbvh->cd_sculpt_vert, v1); + MSculptVert *mv2 = BKE_PBVH_SCULPTVERT(pbvh->cd_sculpt_vert, v2); + int boundflag1 = BM_ELEM_CD_GET_INT(v1, pbvh->cd_boundary_flag); + int boundflag2 = BM_ELEM_CD_GET_INT(v2, pbvh->cd_boundary_flag); + + /* one of the two vertices may be masked, select the correct one for deletion */ + if (!(boundflag1 & SCULPTVERT_ALL_CORNER) || DYNTOPO_MASK(eq_ctx->cd_vert_mask_offset, v1) < + DYNTOPO_MASK(eq_ctx->cd_vert_mask_offset, v2)) { + v_del = v1; + v_conn = v2; + } + else { + v_del = v2; + v_conn = v1; + + SWAP(MSculptVert *, mv1, mv2); + SWAP(int, boundflag1, boundflag2); + } + + /* Don't collapse across boundaries. */ + if ((boundflag1 & SCULPTVERT_ALL_CORNER) || + (boundflag1 & SCULPTVERT_ALL_BOUNDARY) != (boundflag2 & SCULPTVERT_ALL_BOUNDARY)) { + bm_logstack_pop(); + return nullptr; + } + + bool uvs_snapped = pbvh_bmesh_collapse_edge_uvs(pbvh, e, v_conn, v_del, eq_ctx); + + validate_vert_faces(pbvh, pbvh->header.bm, v_conn, false, true); + + BMEdge *e2; + + const int tag = COLLAPSE_TAG; + const int facetag = COLLAPSE_FACE_TAG; + const int log_rings = 1; + + if (deleted_verts) { + BLI_ghash_insert(deleted_verts, (void *)v_del, nullptr); + } + + BMTracer tracer; + BM_empty_tracer(&tracer, &tdata); + + vert_ring_do(e->v1, e->v2, collapse_ring_callback_pre, &tdata, tag, facetag, log_rings - 1); + + if (!uvs_snapped) { + float co[3]; + + copy_v3_v3(co, v_conn->co); + + // full non-manifold collapse + BM_edge_collapse(pbvh->header.bm, e, v_del, true, true, true, true, &tracer); + copy_v3_v3(v_conn->co, co); + } + else { + float co[3]; + + add_v3_v3v3(co, v_del->co, v_conn->co); + mul_v3_fl(co, 0.5f); + + // full non-manifold collapse + BM_edge_collapse(pbvh->header.bm, e, v_del, true, true, true, true, &tracer); + copy_v3_v3(v_conn->co, co); + } + + vert_ring_do(v_conn, + nullptr, + collapse_ring_callback_post, + static_cast(&tdata), + tag, + facetag, + log_rings - 1); + + if (!v_conn->e) { + printf("%s: pbvh error, v_conn->e was null\n", __func__); + return v_conn; + } + + validate_vert_faces(pbvh, pbvh->header.bm, v_conn, false, true); + + /* Flag boundaries for update */ + e2 = v_conn->e; + do { + BMLoop *l = e2->l; + + if (!l) { + continue; + } + + do { + BMLoop *l2 = l->f->l_first; + do { + pbvh_boundary_update_bmesh(pbvh, l2->v); + + MSculptVert *mv = BKE_PBVH_SCULPTVERT(pbvh->cd_sculpt_vert, l2->v); + mv->flag |= mupdateflag; + } while ((l2 = l2->next) != l->f->l_first); + } while ((l = l->radial_next) != e2->l); + } while ((e2 = BM_DISK_EDGE_NEXT(e2, v_conn)) != v_conn->e); + + pbvh_bmesh_check_nodes(pbvh); + + if (!v_conn) { + bm_logstack_pop(); + return nullptr; + } + + MSculptVert *mv_conn = BKE_PBVH_SCULPTVERT(pbvh->cd_sculpt_vert, v_conn); + pbvh_boundary_update_bmesh(pbvh, v_conn); + + MV_ADD_FLAG(mv_conn, mupdateflag); + +#if 0 + e2 = v_conn->e; + BMEdge *enext; + do { + if (!e2) { + break; + } + + enext = BM_DISK_EDGE_NEXT(e2, v_conn); + + // kill wire edge + if (!e2->l) { + BM_log_edge_pre(pbvh->header.bm, pbvh->bm_log, e2); + BM_idmap_release(pbvh->bm_idmap, (BMElem *)e2, true); + BM_edge_kill(pbvh->header.bm, e2); + } + } while (v_conn->e && (e2 = enext) != v_conn->e); +#endif + + MSculptVert *mv3 = BKE_PBVH_SCULPTVERT(pbvh->cd_sculpt_vert, v_conn); + pbvh_boundary_update_bmesh(pbvh, v_conn); + + MV_ADD_FLAG(mv3, mupdateflag); + + if (!v_conn->e) { + // delete isolated vertex + if (BM_ELEM_CD_GET_INT(v_conn, pbvh->cd_vert_node_offset) != DYNTOPO_NODE_NONE) { + blender::dyntopo::pbvh_bmesh_vert_remove(pbvh, v_conn); + } + + BM_log_vert_removed(pbvh->header.bm, pbvh->bm_log, v_conn); + BM_vert_kill(pbvh->header.bm, v_conn); + + bm_logstack_pop(); + return nullptr; + } + + if (BM_ELEM_CD_GET_INT(v_conn, pbvh->cd_vert_node_offset) == DYNTOPO_NODE_NONE) { + printf("%s: error: failed to remove vert from pbvh?\n", __func__); + } + + if (v_conn) { + check_for_fins(pbvh, v_conn); + } + + validate_vert_faces(pbvh, pbvh->header.bm, v_conn, false, true); + + bm_logstack_pop(); + PBVH_CHECK_NAN(v_conn->co); + + return v_conn; +} +} // namespace blender::dyntopo diff --git a/source/blender/blenkernel/intern/dyntopo_intern.hh b/source/blender/blenkernel/intern/dyntopo_intern.hh new file mode 100644 index 00000000000..25f0490ccbd --- /dev/null +++ b/source/blender/blenkernel/intern/dyntopo_intern.hh @@ -0,0 +1,724 @@ +//#define DYNTOPO_VALIDATE_LOG + +#include "BKE_paint.h" +#include "BKE_pbvh.h" +#include "bmesh.h" +#include "pbvh_intern.hh" +#include "BLI_asan.h" + +struct MinMaxHeap; +struct GHash; +struct BLI_Buffer; + +#define DYNTOPO_DISABLE_SPLIT_EDGES 1 +#define DYNTOPO_DISABLE_FIN_REMOVAL 2 +#define DYNTOPO_DISABLE_COLLAPSE 4 +#define DYNTOPO_DISABLE_TRIANGULATOR 8 + +//#define DYNTOPO_DISABLE_FLAG \ +// (DYNTOPO_DISABLE_FIN_REMOVAL | DYNTOPO_DISABLE_COLLAPSE | DYNTOPO_DISABLE_TRIANGULATOR) +#define DYNTOPO_DISABLE_FLAG 0 + +extern "C" { +void bmesh_disk_edge_append(BMEdge *e, BMVert *v); +void bmesh_radial_loop_append(BMEdge *e, BMLoop *l); +void bm_kill_only_edge(BMesh *bm, BMEdge *e); +void bm_kill_only_loop(BMesh *bm, BMLoop *l); +void bm_kill_only_face(BMesh *bm, BMFace *f); +} + +namespace blender::dyntopo { + +static int elem_sizes[] = {-1, + (int)sizeof(BMVert), + (int)sizeof(BMEdge), + 0, + (int)sizeof(BMLoop), + -1, + -1, + -1, + (int)sizeof(BMFace)}; + +inline bool bm_elem_is_free(BMElem *elem, int htype) +{ + BLI_asan_unpoison(elem, elem_sizes[htype]); + + bool ret = elem->head.htype != htype; + + if (ret) { + BLI_asan_poison(elem, elem_sizes[htype]); + } + + return ret; +} + +#ifdef DYNTOPO_VALIDATE_LOG +# define VALIDATE_LOG(log) BM_log_validate_cur(log) +#else +# define VALIDATE_LOG(log) +#endif + +//#define DYNTOPO_REPORT +//#define WITH_ADAPTIVE_CURVATURE +//#define DYNTOPO_NO_THREADING + +#define SCULPTVERT_VALENCE_TEMP SCULPTVERT_SPLIT_TEMP + +#define SCULPTVERT_SMOOTH_BOUNDARY \ + (SCULPT_BOUNDARY_MESH | SCULPT_BOUNDARY_FACE_SET | SCULPT_BOUNDARY_SHARP | \ + SCULPT_BOUNDARY_SEAM | SCULPT_BOUNDARY_UV) +#define SCULPTVERT_ALL_BOUNDARY \ + (SCULPT_BOUNDARY_MESH | SCULPT_BOUNDARY_FACE_SET | SCULPT_BOUNDARY_SHARP | \ + SCULPT_BOUNDARY_SEAM | SCULPT_BOUNDARY_UV) +#define SCULPTVERT_SMOOTH_CORNER \ + (SCULPT_CORNER_MESH | SCULPT_CORNER_FACE_SET | SCULPT_CORNER_SHARP | SCULPT_CORNER_SEAM | \ + SCULPT_CORNER_UV) +#define SCULPTVERT_ALL_CORNER \ + (SCULPT_CORNER_MESH | SCULPT_CORNER_FACE_SET | SCULPT_CORNER_SHARP | SCULPT_CORNER_SEAM | \ + SCULPT_CORNER_UV) + +#define DYNTOPO_MAX_ITER 4096 + +#define DYNTOPO_USE_HEAP +#define DYNTOPO_USE_MINMAX_HEAP + +#ifndef DYNTOPO_USE_HEAP +/* don't add edges into the queue multiple times */ +# define USE_EDGEQUEUE_TAG +#endif + +/* Avoid skinny faces */ +#define USE_EDGEQUEUE_EVEN_SUBDIV + +/* How much longer we need to be to consider for subdividing + * (avoids subdividing faces which are only *slightly* skinny) */ +#define EVEN_EDGELEN_THRESHOLD 1.2f +/* How much the limit increases per recursion + * (avoids performing subdivisions too far away). */ +#define EVEN_GENERATION_SCALE 1.1f + +/* recursion depth to start applying front face test */ +#define DEPTH_START_LIMIT 5 + +//#define FANCY_EDGE_WEIGHTS <= too slow +//#define SKINNY_EDGE_FIX + +/* slightly relax geometry by this factor along surface tangents + to improve convergence of remesher */ +#define DYNTOPO_SAFE_SMOOTH_FAC 0.05f + +#define DYNTOPO_SAFE_SMOOTH_SUBD_ONLY_FAC 0.075f + +#ifdef USE_EDGEQUEUE_EVEN_SUBDIV +# include "BKE_global.h" +#endif + +/* Support for only operating on front-faces */ +#define USE_EDGEQUEUE_FRONTFACE + +/** + * Ensure we don't have dirty tags for the edge queue, and that they are left cleared. + * (slow, even for debug mode, so leave disabled for now). + */ +#if defined(USE_EDGEQUEUE_TAG) && 0 +# if !defined(NDEBUG) +# define USE_EDGEQUEUE_TAG_VERIFY +# endif +#endif + +// #define USE_VERIFY + +#define DYNTOPO_MASK(cd_mask_offset, v) BM_ELEM_CD_GET_FLOAT(v, cd_mask_offset) + +#ifdef USE_VERIFY +static void pbvh_bmesh_verify(PBVH *pbvh); +#endif + +/* -------------------------------------------------------------------- */ +/** \name BMesh Utility API + * + * Use some local functions which assume triangles. + * \{ */ + +/** + * Typically using BM_LOOPS_OF_VERT and BM_FACES_OF_VERT iterators are fine, + * however this is an area where performance matters so do it in-line. + * + * Take care since 'break' won't works as expected within these macros! + */ + +#define BM_DISK_EDGE(e, v) (&((&(e)->v1_disk_link)[(v) == (e)->v2])) + +#define BM_LOOPS_OF_VERT_ITER_BEGIN(l_iter_radial_, v_) \ + { \ + struct { \ + BMVert *v; \ + BMEdge *e_iter, *e_first; \ + BMLoop *l_iter_radial; \ + } _iter; \ + _iter.v = v_; \ + if (_iter.v->e) { \ + _iter.e_iter = _iter.e_first = _iter.v->e; \ + do { \ + if (_iter.e_iter->l) { \ + _iter.l_iter_radial = _iter.e_iter->l; \ + do { \ + if (_iter.l_iter_radial->v == _iter.v) { \ + l_iter_radial_ = _iter.l_iter_radial; + +#define BM_LOOPS_OF_VERT_ITER_END \ + } \ + } \ + while ((_iter.l_iter_radial = _iter.l_iter_radial->radial_next) != _iter.e_iter->l) \ + ; \ + } \ + } \ + while ((_iter.e_iter = BM_DISK_EDGE_NEXT(_iter.e_iter, _iter.v)) != _iter.e_first) \ + ; \ + } \ + } \ + ((void)0) + +#define BM_FACES_OF_VERT_ITER_BEGIN(f_iter_, v_) \ + { \ + BMLoop *l_iter_radial_; \ + BM_LOOPS_OF_VERT_ITER_BEGIN (l_iter_radial_, v_) { \ + f_iter_ = l_iter_radial_->f; + +#define BM_FACES_OF_VERT_ITER_END \ + } \ + BM_LOOPS_OF_VERT_ITER_END; \ + } \ + ((void)0) + +struct EdgeQueue; + +typedef struct EdgeQueueContext { + BLI_mempool *pool; + BMesh *bm; + DyntopoMaskCB mask_cb; + void *mask_cb_data; + int cd_sculpt_vert; + int cd_vert_mask_offset; + int cd_vert_node_offset; + int cd_face_node_offset; + float avg_elen; + float max_elen; + float min_elen; + float totedge; + bool local_mode; + float surface_smooth_fac; + + struct MinMaxHeap *heap_mm; + int max_heap_mm; + // TableGSet *used_verts; + BMVert **used_verts; + int used_verts_size; + int tot_used_verts; + + float view_normal[3]; + bool use_view_normal; + float limit_min, limit_max, limit_mid; + + const float *center; + float center_proj[3]; /* for when we use projected coords. */ + float radius_squared; + float limit_len_min; + float limit_len_max; + float limit_len_min_sqr; + float limit_len_max_sqr; + + bool (*edge_queue_tri_in_range)(const struct EdgeQueueContext *q, BMVert *vs[3], float no[3]); + bool (*edge_queue_vert_in_range)(const struct EdgeQueueContext *q, BMVert *v); + + PBVHTopologyUpdateMode mode; +} EdgeQueueContext; + +bool destroy_nonmanifold_fins(PBVH *pbvh, BMEdge *e_root); +bool check_face_is_tri(PBVH *pbvh, BMFace *f); +bool check_vert_fan_are_tris(PBVH *pbvh, BMVert *v); + +BMVert *pbvh_bmesh_collapse_edge(PBVH *pbvh, + BMEdge *e, + BMVert *v1, + BMVert *v2, + struct GHash *deleted_verts, + struct BLI_Buffer *deleted_faces, + struct EdgeQueueContext *eq_ctx); + +extern "C" void bm_log_message(const char *fmt, ...); +void pbvh_bmesh_vert_remove(PBVH *pbvh, BMVert *v); +inline bool bm_edge_tag_test(BMEdge *e) +{ + /* is the edge or one of its faces tagged? */ + return (BM_elem_flag_test(e->v1, BM_ELEM_TAG) || BM_elem_flag_test(e->v2, BM_ELEM_TAG) || + (e->l && + (BM_elem_flag_test(e->l->f, BM_ELEM_TAG) || + (e->l != e->l->radial_next && BM_elem_flag_test(e->l->radial_next->f, BM_ELEM_TAG))))); +} + +inline void bm_edge_tag_disable(BMEdge *e) +{ + BM_elem_flag_disable(e->v1, BM_ELEM_TAG); + BM_elem_flag_disable(e->v2, BM_ELEM_TAG); + if (e->l) { + BM_elem_flag_disable(e->l->f, BM_ELEM_TAG); + if (e->l != e->l->radial_next) { + BM_elem_flag_disable(e->l->radial_next->f, BM_ELEM_TAG); + } + } +} + +/* takes the edges loop */ +BLI_INLINE int bm_edge_is_manifold_or_boundary(BMLoop *l) +{ +#if 0 + /* less optimized version of check below */ + return (BM_edge_is_manifold(l->e) || BM_edge_is_boundary(l->e); +#else + /* if the edge is a boundary it points to its self, else this must be a manifold */ + return LIKELY(l) && LIKELY(l->radial_next->radial_next == l); +#endif +} + +inline void bm_edge_tag_enable(BMEdge *e) +{ + BM_elem_flag_enable(e->v1, BM_ELEM_TAG); + BM_elem_flag_enable(e->v2, BM_ELEM_TAG); + if (e->l) { + BM_elem_flag_enable(e->l->f, BM_ELEM_TAG); + if (e->l != e->l->radial_next) { + BM_elem_flag_enable(e->l->radial_next->f, BM_ELEM_TAG); + } + } +} + +void pbvh_bmesh_face_remove( + PBVH *pbvh, BMFace *f, bool log_face, bool check_verts, bool ensure_ownership_transfer); + +bool check_for_fins(PBVH *pbvh, BMVert *v); + +} // namespace blender::dyntopo + +extern "C" { +bool BKE_pbvh_bmesh_update_topology(PBVH *pbvh, + PBVHTopologyUpdateMode mode, + const float center[3], + const float view_normal[3], + float radius, + const bool use_frontface, + const bool use_projected, + int sym_axis, + bool updatePBVH, + DyntopoMaskCB mask_cb, + void *mask_cb_data, + int custom_max_steps, + bool disable_surface_relax, + bool is_snake_hook); + +void BKE_pbvh_bmesh_remove_face(PBVH *pbvh, BMFace *f, bool log_face); +void BKE_pbvh_bmesh_remove_edge(PBVH *pbvh, BMEdge *e, bool log_edge); +void BKE_pbvh_bmesh_remove_vertex(PBVH *pbvh, BMVert *v, bool log_vert); +void BKE_pbvh_bmesh_add_face(PBVH *pbvh, struct BMFace *f, bool log_face, bool force_tree_walk); +} + +/*************************** Topology update **************************/ + +/**** Debugging Tools ********/ + +inline void fix_mesh(PBVH *pbvh, BMesh *bm) +{ + BMIter iter; + BMVert *v; + BMEdge *e; + BMFace *f; + + printf("fixing mesh. . .\n"); + + BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) { + v->e = nullptr; + MSculptVert *mv = BKE_PBVH_SCULPTVERT(pbvh->cd_sculpt_vert, v); + + MV_ADD_FLAG(mv, + SCULPTVERT_NEED_VALENCE | SCULPTVERT_NEED_DISK_SORT | SCULPTVERT_NEED_TRIANGULATE); + } + + BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) { + e->v1_disk_link.next = e->v1_disk_link.prev = nullptr; + e->v2_disk_link.next = e->v2_disk_link.prev = nullptr; + e->l = nullptr; + + if (e->v1 == e->v2) { + bm_kill_only_edge(bm, e); + } + } + + // rebuild disk cycles + BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) { + if (BM_edge_exists(e->v1, e->v2)) { + printf("duplicate edge %p!\n", e); + bm_kill_only_edge(bm, e); + + continue; + } + + bmesh_disk_edge_append(e, e->v1); + bmesh_disk_edge_append(e, e->v2); + } + + BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { + BMLoop *l = f->l_first; + + do { + if (f->len < 3) { + break; + } + + if (l->next->v == l->v) { + BMLoop *l_del = l->next; + + l->next = l_del->next; + l_del->next->prev = l; + + f->len--; + + if (f->l_first == l_del) { + f->l_first = l; + } + + bm_kill_only_loop(bm, l_del); + + if (f->len < 3) { + break; + } + } + } while ((l = l->next) != f->l_first); + + if (f->len < 3) { + int ni = BM_ELEM_CD_GET_INT(f, pbvh->cd_face_node_offset); + + if (ni >= 0 && ni < pbvh->totnode && (pbvh->nodes[ni].flag & PBVH_Leaf)) { + BLI_table_gset_remove(pbvh->nodes[ni].bm_faces, f, nullptr); + } + + bm_kill_only_face(bm, f); + continue; + } + + do { + l->e = BM_edge_exists(l->v, l->next->v); + + if (!l->e) { + l->e = BM_edge_create(bm, l->v, l->next->v, nullptr, BM_CREATE_NOP); + } + + bmesh_radial_loop_append(l->e, l); + } while ((l = l->next) != f->l_first); + } + + bm->elem_table_dirty |= BM_VERT | BM_EDGE | BM_FACE; + bm->elem_index_dirty |= BM_VERT | BM_EDGE | BM_FACE; + + printf("done fixing mesh.\n"); +} + +//#define CHECKMESH +//#define TEST_INVALID_NORMALS + +#ifndef CHECKMESH +# define validate_vert(pbvh, bm, v, autofix, check_manifold) true +# define validate_edge(pbvh, bm, e, autofix, check_manifold) true +# define validate_face(pbvh, bm, f, autofix, check_manifold) true +# define validate_vert_faces(pbvh, bm, v, autofix, check_manifold) true +# define check_face_is_manifold(pbvh, bm, f) true +#else + +# define CHECKMESH_ATTR ATTR_NO_OPT + +CHECKMESH_ATTR static void _debugprint(const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + vprintf(fmt, args); + va_end(args); +} + +CHECKMESH_ATTR static bool check_face_is_manifold(PBVH *pbvh, BMesh *bm, BMFace *f) +{ + BMLoop *l = f->l_first; + + do { + if (l->radial_next != l && l->radial_next->radial_next != l) { + //_debugprint("non-manifold edge in loop\n"); + + BMVert *v1 = l->e->v1, *v2 = l->e->v2; + + for (int i = 0; i < 2; i++) { + BMVert *v = i ? v2 : v1; + BMEdge *e = v->e; + + if (!e) { + continue; + } + + int i = 0; + + do { + if (!e) { + break; + } + + bool same = e->v1 == v1 && e->v2 == v2; + same = same || (e->v1 == v2 && e->v2 == v1); + + if (same && e != l->e) { + // printf("duplicate edges in face!\n"); + } + + if (i++ > 5000) { + printf("infinite loop in edge disk cycle! v: %p, e: %p\n", v, e); + break; + } + } while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e); + } + l->e->head.hflag |= BM_ELEM_SELECT; + l->f->head.hflag |= BM_ELEM_SELECT; + l->v->head.hflag |= BM_ELEM_SELECT; + + // pbvh->dyntopo_stop = true; + + return false; + } + +# ifdef TEST_INVALID_NORMALS + if (l != l->radial_next && l->v == l->radial_next->v) { + _debugprint("invalid normals\n"); + return false; + } +# endif + } while ((l = l->next) != f->l_first); + + return true; +} + +CHECKMESH_ATTR +static bool validate_vert(PBVH *pbvh, BMesh *bm, BMVert *v, bool autofix, bool check_manifold) +{ + if (v->head.htype != BM_VERT) { + _debugprint("bad vertex\n"); + return false; + } + + BMEdge *e = v->e; + int i = 0; + + if (!e) { + return true; + } + + do { + if (e->v1 != v && e->v2 != v) { + _debugprint("edge does not contain v\n"); + goto error; + } + + if (e->l) { + int j = 0; + + BMLoop *l = e->l; + do { + if (l->e->v1 != v && l->e->v2 != v) { + _debugprint("loop's edges doesn't contain v\n"); + goto error; + } + + if (l->v != v && l->next->v != v) { + _debugprint("loop and loop->next don't contain v\n"); + goto error; + } + + j++; + if (j > 1000) { + _debugprint("corrupted radial cycle\n"); + goto error; + } + + if (check_manifold) { + check_face_is_manifold(pbvh, bm, l->f); + } + } while ((l = l->radial_next) != e->l); + } + if (i > 10000) { + _debugprint("corrupted disk cycle\n"); + goto error; + } + + e = BM_DISK_EDGE_NEXT(e, v); + i++; + } while (e != v->e); + + return true; + +error: + + if (autofix) { + fix_mesh(pbvh, bm); + } + + return false; +} + +CHECKMESH_ATTR +static bool validate_edge(PBVH *pbvh, BMesh *bm, BMEdge *e, bool autofix, bool check_manifold) +{ + if (e->head.htype != BM_EDGE) { + _debugprint("corrupted edge!\n"); + return false; + } + + bool ret = validate_vert(pbvh, bm, e->v1, false, check_manifold) && + validate_vert(pbvh, bm, e->v2, false, check_manifold); + + if (!ret && autofix) { + fix_mesh(pbvh, bm); + } + + return ret; +} + +CHECKMESH_ATTR bool face_verts_are_same(PBVH *pbvh, BMesh *bm, BMFace *f1, BMFace *f2) +{ + BMLoop *l1 = f1->l_first; + BMLoop *l2 = f2->l_first; + + int count1 = 0; + + do { + count1++; + } while ((l1 = l1->next) != f1->l_first); + + do { + bool ok = false; + + do { + if (l2->v == l1->v) { + ok = true; + break; + } + } while ((l2 = l2->next) != f2->l_first); + + if (!ok) { + return false; + } + } while ((l1 = l1->next) != f1->l_first); + + return true; +} + +CHECKMESH_ATTR +static bool validate_face(PBVH *pbvh, BMesh *bm, BMFace *f, bool autofix, bool check_manifold) +{ + if (f->head.htype != BM_FACE) { + _debugprint("corrupted edge!\n"); + return false; + } + + BMLoop **ls = nullptr; + BLI_array_staticdeclare(ls, 32); + + BMLoop *l = f->l_first; + int i = 0; + do { + i++; + + if (i > 100000) { + _debugprint("Very corrupted face!\n"); + goto error; + } + + if (!validate_edge(pbvh, bm, l->e, false, check_manifold)) { + goto error; + } + + BMLoop *l2 = l->radial_next; + do { + if (l2->f != f && face_verts_are_same(pbvh, bm, l2->f, f)) { + _debugprint("Duplicate faces!\n"); + goto error; + } + } while ((l2 = l2->radial_next) != l); + + BLI_array_append(ls, l); + } while ((l = l->next) != f->l_first); + + for (int i = 0; i < BLI_array_len(ls); i++) { + BMLoop *l1 = ls[i]; + for (int j = 0; j < BLI_array_len(ls); j++) { + BMLoop *l2 = ls[j]; + + if (i != j && l1->v == l2->v) { + _debugprint("duplicate verts in face!\n"); + goto error; + } + + if (BM_edge_exists(l->v, l->next->v) != l->e) { + _debugprint("loop has wrong edge!\n"); + goto error; + } + } + } + + BLI_array_free(ls); + return true; + +error: + BLI_array_free(ls); + + if (autofix) { + fix_mesh(pbvh, bm); + } + + return false; +} + +CHECKMESH_ATTR bool validate_vert_faces( + PBVH *pbvh, BMesh *bm, BMVert *v, int autofix, bool check_manifold) +{ + if (!validate_vert(pbvh, bm, v, autofix, check_manifold)) { + return false; + } + + if (!v->e) { + return true; + } + + BMEdge *e = v->e; + do { + BMLoop *l = e->l; + + if (!l) { + continue; + } + + do { + if (!validate_edge(pbvh, bm, l->e, false, false)) { + goto error; + } + + if (!validate_face(pbvh, bm, l->f, false, check_manifold)) { + goto error; + } + } while ((l = l->radial_next) != e->l); + } while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e); + + return true; + +error: + + if (autofix) { + fix_mesh(pbvh, bm); + } + + return false; +} +#endif diff --git a/source/blender/blenkernel/intern/idprop_utils.c b/source/blender/blenkernel/intern/idprop_utils.c index 0ade0aa654e..e9eeb189ee2 100644 --- a/source/blender/blenkernel/intern/idprop_utils.c +++ b/source/blender/blenkernel/intern/idprop_utils.c @@ -182,7 +182,7 @@ static void idp_repr_fn_recursive(struct ReprState *state, const IDProperty *pro const ID *id = prop->data.pointer; if (id != NULL) { STR_APPEND_STR("bpy.data."); - STR_APPEND_STR(BKE_idtype_idcode_to_name_plural(GS(id->name))); + STR_APPEND_STR(BKE_idtype_idcode_to_name_plural((short)GS(id->name))); STR_APPEND_STR("["); STR_APPEND_STR_QUOTE(id->name + 2); STR_APPEND_STR("]"); diff --git a/source/blender/blenkernel/intern/lib_id.c b/source/blender/blenkernel/intern/lib_id.c index fcf585dfcfc..7b576c493ba 100644 --- a/source/blender/blenkernel/intern/lib_id.c +++ b/source/blender/blenkernel/intern/lib_id.c @@ -676,7 +676,7 @@ ID *BKE_id_copy(Main *bmain, const ID *id) ID *BKE_id_copy_for_duplicate(Main *bmain, ID *id, - const eDupli_ID_Flags duplicate_flags, + const uint duplicate_flags, const int copy_flags) { if (id == NULL) { diff --git a/source/blender/blenkernel/intern/mesh.cc b/source/blender/blenkernel/intern/mesh.cc index ee1129f6b4b..b9c7d0cdcd4 100644 --- a/source/blender/blenkernel/intern/mesh.cc +++ b/source/blender/blenkernel/intern/mesh.cc @@ -40,6 +40,7 @@ #include "BLT_translation.h" #include "BKE_anim_data.h" +#include "BKE_attribute.h" #include "BKE_attribute.hh" #include "BKE_bpath.h" #include "BKE_deform.h" @@ -1135,14 +1136,15 @@ Mesh *BKE_mesh_copy_for_eval(const Mesh *source, bool reference) return result; } -BMesh *BKE_mesh_to_bmesh_ex(const Mesh *me, +BMesh *BKE_mesh_to_bmesh_ex(const Object *ob, + const Mesh *me, const struct BMeshCreateParams *create_params, const struct BMeshFromMeshParams *convert_params) { const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(me); BMesh *bm = BM_mesh_create(&allocsize, create_params); - BM_mesh_bm_from_me(bm, me, convert_params); + BM_mesh_bm_from_me((Object *)ob, bm, me, convert_params); return bm; } @@ -1158,7 +1160,7 @@ BMesh *BKE_mesh_to_bmesh(Mesh *me, bmesh_from_mesh_params.add_key_index = add_key_index; bmesh_from_mesh_params.use_shapekey = true; bmesh_from_mesh_params.active_shapekey = ob->shapenr; - return BKE_mesh_to_bmesh_ex(me, params, &bmesh_from_mesh_params); + return BKE_mesh_to_bmesh_ex(ob, me, params, &bmesh_from_mesh_params); } Mesh *BKE_mesh_from_bmesh_nomain(BMesh *bm, @@ -1166,9 +1168,14 @@ Mesh *BKE_mesh_from_bmesh_nomain(BMesh *bm, const Mesh *me_settings) { BLI_assert(params->calc_object_remap == false); + Mesh *mesh = (Mesh *)BKE_id_new_nomain(ID_ME, nullptr); - BM_mesh_bm_to_me(nullptr, bm, mesh, params); - BKE_mesh_copy_parameters_for_eval(mesh, me_settings); + BM_mesh_bm_to_me(nullptr, nullptr, bm, mesh, params); + + if (me_settings) { + BKE_mesh_copy_parameters_for_eval(mesh, me_settings); + } + return mesh; } @@ -1178,7 +1185,11 @@ Mesh *BKE_mesh_from_bmesh_for_eval_nomain(BMesh *bm, { Mesh *mesh = (Mesh *)BKE_id_new_nomain(ID_ME, nullptr); BM_mesh_bm_to_me_for_eval(bm, mesh, cd_mask_extra); - BKE_mesh_copy_parameters_for_eval(mesh, me_settings); + + if (me_settings) { + BKE_mesh_copy_parameters_for_eval(mesh, me_settings); + } + return mesh; } diff --git a/source/blender/blenkernel/intern/mesh_fair.cc b/source/blender/blenkernel/intern/mesh_fair.cc index ddf6b342236..bed0f3e2d50 100644 --- a/source/blender/blenkernel/intern/mesh_fair.cc +++ b/source/blender/blenkernel/intern/mesh_fair.cc @@ -56,6 +56,12 @@ class FairingContext { /* Get the other vertex index for a loop. */ virtual int other_vertex_index_from_loop(const int loop, const uint v) = 0; + virtual float *vertex_deformation_co_get(const int v) = 0; + virtual void vertex_deformation_co_set(const int v, const float co[3]) = 0; + + virtual int vertex_index_from_loop(const int loop) = 0; + virtual float cotangent_loop_weight_get(const int loop) = 0; + int vertex_count_get() { return totvert_; @@ -63,7 +69,7 @@ class FairingContext { int loop_count_get() { - return totvert_; + return totloop_; } MeshElemMap *vertex_loop_map_get(const int v) @@ -71,11 +77,6 @@ class FairingContext { return &vlmap_[v]; } - float *vertex_deformation_co_get(const int v) - { - return co_[v]; - } - virtual ~FairingContext() = default; void fair_verts(bool *affected, @@ -88,8 +89,6 @@ class FairingContext { } protected: - Vector co_; - int totvert_; int totloop_; @@ -112,8 +111,10 @@ class FairingContext { EIG_linear_solver_matrix_add(solver, i, j, -multiplier); return; } + + const float *co = vertex_deformation_co_get(v); for (int j = 0; j < 3; j++) { - EIG_linear_solver_right_hand_side_add(solver, j, i, multiplier * co_[v][j]); + EIG_linear_solver_right_hand_side_add(solver, j, i, multiplier * co[j]); } return; } @@ -180,9 +181,11 @@ class FairingContext { for (auto item : vert_col_map.items()) { const int v = item.key; const int col = item.value; + float co[3]; for (int j = 0; j < 3; j++) { - co_[v][j] = EIG_linear_solver_variable_get(solver, j, col); + co[j] = EIG_linear_solver_variable_get(solver, j, col); } + vertex_deformation_co_set(v, co); } /* Free solver data */ @@ -201,34 +204,71 @@ class MeshFairingContext : public FairingContext { medge_ = mesh->edges(); mpoly_ = mesh->polys(); mloop_ = mesh->loops(); - BKE_mesh_vert_loop_map_create(&vlmap_, - &vlmap_mem_, - mpoly_.data(), - mloop_.data(), - mesh->totvert, + + BKE_mesh_vert_loop_map_create( + &vlmap_, + &vlmap_mem_, + reinterpret_cast(mesh->vert_positions().data()), + medge_.data(), + mpoly_.data(), + mloop_.data(), + mesh->totvert, + mesh->totpoly, + mesh->totloop, + false); + + BKE_mesh_edge_loop_map_create(&elmap_, + &elmap_mem_, + mesh->medge, + mesh->totedge, + mesh->mpoly, mesh->totpoly, + mesh->mloop, mesh->totloop); + auto vert_positions = mesh->vert_positions_for_write().data(); + /* Deformation coords. */ - co_.reserve(mesh->totvert); - if (!deform_positions.is_empty()) { - for (int i = 0; i < mesh->totvert; i++) { - co_[i] = deform_positions[i]; + if (vert_positions) { + deform_mvert_ = reinterpret_cast(vert_positions); +#if 0 + co_.reserve(mesh->totvert); + + if (!deform_positions.is_empty()) { + for (int i = 0; i < mesh->totvert; i++) { + co_[i] = deform_positions[i]; + } } - } +#endif +#if 0 else { for (int i = 0; i < mesh->totvert; i++) { co_[i] = positions[i]; } } +#endif - loop_to_poly_map_ = blender::bke::mesh_topology::build_loop_to_poly_map(mpoly_, mloop_.size()); + loop_to_poly_map_ = blender::bke::mesh_topology::build_loop_to_poly_map(mpoly_, + mloop_.size()); + } } ~MeshFairingContext() override { MEM_SAFE_FREE(vlmap_); MEM_SAFE_FREE(vlmap_mem_); + MEM_SAFE_FREE(elmap_); + MEM_SAFE_FREE(elmap_mem_); + } + + float *vertex_deformation_co_get(const int v) + { + return deform_mvert_[v]; + } + + void vertex_deformation_co_set(const int v, const float co[3]) + { + copy_v3_v3(deform_mvert_[v], co); } void adjacents_coords_from_loop(const int loop, @@ -238,8 +278,8 @@ class MeshFairingContext : public FairingContext { const int vert = mloop_[loop].v; const MPoly *p = &mpoly_[loop_to_poly_map_[loop]]; const int corner = poly_find_loop_from_vert(p, &mloop_[p->loopstart], vert); - copy_v3_v3(r_adj_next, co_[ME_POLY_LOOP_NEXT(mloop_, p, corner)->v]); - copy_v3_v3(r_adj_prev, co_[ME_POLY_LOOP_PREV(mloop_, p, corner)->v]); + copy_v3_v3(r_adj_next, deform_mvert_[ME_POLY_LOOP_NEXT(mloop_, p, corner)->v]); + copy_v3_v3(r_adj_prev, deform_mvert_[ME_POLY_LOOP_PREV(mloop_, p, corner)->v]); } int other_vertex_index_from_loop(const int loop, const uint v) override @@ -251,12 +291,28 @@ class MeshFairingContext : public FairingContext { return e->v1; } + int vertex_index_from_loop(const int loop) override + { + return mloop_[loop].v; + } + + float cotangent_loop_weight_get(const int /*UNUSED(loop)*/) override + { + /* TODO: Implement cotangent loop weights for meshes. */ + return 1.0f; + } + protected: Mesh *mesh_; Span mloop_; Span mpoly_; Span medge_; Array loop_to_poly_map_; + + float (*deform_mvert_)[3]; + + MeshElemMap *elmap_; + int *elmap_mem_; }; class BMeshFairingContext : public FairingContext { @@ -271,11 +327,13 @@ class BMeshFairingContext : public FairingContext { BM_mesh_elem_index_ensure(bm, BM_LOOP); /* Deformation coords. */ +#if 0 // merge error ? co_.reserve(bm->totvert); for (int i = 0; i < bm->totvert; i++) { BMVert *v = BM_vert_at_index(bm, i); co_[i] = v->co; } +#endif bmloop_.reserve(bm->totloop); vlmap_ = (MeshElemMap *)MEM_calloc_arrayN(bm->totvert, sizeof(MeshElemMap), "bmesh loop map"); @@ -292,6 +350,7 @@ class BMeshFairingContext : public FairingContext { int loop_count = 0; const int vert_index = BM_elem_index_get(v); vlmap_[vert_index].indices = &vlmap_mem_[index_iter]; + BM_ITER_ELEM (l, &loop_iter, v, BM_LOOPS_OF_VERT) { const int loop_index = BM_elem_index_get(l); bmloop_[loop_index] = l; @@ -309,6 +368,16 @@ class BMeshFairingContext : public FairingContext { MEM_SAFE_FREE(vlmap_mem_); } + float *vertex_deformation_co_get(const int v) + { + return BM_vert_at_index(bm, v)->co; + } + + void vertex_deformation_co_set(const int v, const float co[3]) + { + copy_v3_v3(BM_vert_at_index(bm, v)->co, co); + } + void adjacents_coords_from_loop(const int loop, float r_adj_next[3], float r_adj_prev[3]) override @@ -325,6 +394,47 @@ class BMeshFairingContext : public FairingContext { return BM_elem_index_get(bm_other_vert); } + int vertex_index_from_loop(const int loop) override + { + return BM_elem_index_get(bmloop_[loop]->v); + } + + float cotangent_loop_weight_get(const int loop) override + { + return 1.0f; + + /* TODO: enable this when it works. */ + BMLoop *l = bmloop_[loop]; + float *co_c[2]; + int co_c_count = 1; + + float *co_a = l->v->co; + float *co_b = l->next->v->co; + co_c[0] = l->prev->v->co; + if (!BM_edge_is_boundary(l->e)) { + co_c_count = 2; + co_c[1] = l->radial_next->next->next->v->co; + } + + float weight = 0.0f; + for (int c = 0; c < co_c_count; c++) { + float v1[3]; + float v2[3]; + sub_v3_v3v3(v1, co_a, co_c[c]); + sub_v3_v3v3(v2, co_b, co_c[c]); + const float angle = angle_v3v3(v1, v2); + const float tangent = tan(angle); + if (tangent != 0) { + weight += 1.0f / tangent; + } + else { + weight += 1e-4; + } + } + weight *= 0.5f; + return weight; + } + protected: BMesh *bm; Vector bmloop_; @@ -335,25 +445,35 @@ class UniformVertexWeight : public VertexWeight { UniformVertexWeight(FairingContext *fairing_context) { const int totvert = fairing_context->vertex_count_get(); - vertex_weights_.reserve(totvert); + fairing_context_ = fairing_context; + vertex_weights_.resize(totvert); + cached_.resize(totvert); for (int i = 0; i < totvert; i++) { - const int tot_loop = fairing_context->vertex_loop_map_get(i)->count; - if (tot_loop != 0) { - vertex_weights_[i] = 1.0f / tot_loop; - } - else { - vertex_weights_[i] = FLT_MAX; - } + cached_[i] = false; } } float weight_at_index(const int index) override { + if (!cached_[index]) { + vertex_weights_[index] = uniform_weight_at_index(index); + cached_[index] = true; + } return vertex_weights_[index]; } private: + float uniform_weight_at_index(const int index) + { + const int tot_loop = fairing_context_->vertex_loop_map_get(index)->count; + if (tot_loop != 0) { + return 1.0f / tot_loop; + } + return FLT_MAX; + } Vector vertex_weights_; + Vector cached_; + FairingContext *fairing_context_; }; class VoronoiVertexWeight : public VertexWeight { @@ -361,52 +481,65 @@ class VoronoiVertexWeight : public VertexWeight { public: VoronoiVertexWeight(FairingContext *fairing_context) { + fairing_context_ = fairing_context; const int totvert = fairing_context->vertex_count_get(); - vertex_weights_.reserve(totvert); + vertex_weights_.resize(totvert); + cached_.resize(totvert); for (int i = 0; i < totvert; i++) { - - float area = 0.0f; - float a[3]; - copy_v3_v3(a, fairing_context->vertex_deformation_co_get(i)); - const float acute_threshold = M_PI_2; - - MeshElemMap *vlmap_elem = fairing_context->vertex_loop_map_get(i); - for (int l = 0; l < vlmap_elem->count; l++) { - const int l_index = vlmap_elem->indices[l]; - - float b[3], c[3], d[3]; - fairing_context->adjacents_coords_from_loop(l_index, b, c); - - if (angle_v3v3v3(c, fairing_context->vertex_deformation_co_get(i), b) < acute_threshold) { - calc_circumcenter(d, a, b, c); - } - else { - add_v3_v3v3(d, b, c); - mul_v3_fl(d, 0.5f); - } - - float t[3]; - add_v3_v3v3(t, a, b); - mul_v3_fl(t, 0.5f); - area += area_tri_v3(a, t, d); - - add_v3_v3v3(t, a, c); - mul_v3_fl(t, 0.5f); - area += area_tri_v3(a, d, t); - } - - vertex_weights_[i] = area != 0.0f ? 1.0f / area : 1e12; + cached_[i] = false; } } float weight_at_index(const int index) override { + if (!cached_[index]) { + vertex_weights_[index] = voronoi_weight_at_index(index); + cached_[index] = true; + } return vertex_weights_[index]; } private: Vector vertex_weights_; + Vector cached_; + FairingContext *fairing_context_; + + float voronoi_weight_at_index(const int index) + { + float area = 0.0f; + float a[3]; + copy_v3_v3(a, fairing_context_->vertex_deformation_co_get(index)); + const float acute_threshold = M_PI_2; + + MeshElemMap *vlmap_elem = fairing_context_->vertex_loop_map_get(index); + for (int l = 0; l < vlmap_elem->count; l++) { + const int l_index = vlmap_elem->indices[l]; + + float b[3], c[3], d[3]; + fairing_context_->adjacents_coords_from_loop(l_index, b, c); + + if (angle_v3v3v3(c, fairing_context_->vertex_deformation_co_get(index), b) < + acute_threshold) { + calc_circumcenter(d, a, b, c); + } + else { + add_v3_v3v3(d, b, c); + mul_v3_fl(d, 0.5f); + } + + float t[3]; + add_v3_v3v3(t, a, b); + mul_v3_fl(t, 0.5f); + area += area_tri_v3(a, t, d); + + add_v3_v3v3(t, a, c); + mul_v3_fl(t, 0.5f); + area += area_tri_v3(a, d, t); + } + + return area != 0.0f ? 1.0f / area : 1e12; + } void calc_circumcenter(float r[3], const float a[3], const float b[3], const float c[3]) { @@ -447,6 +580,35 @@ class UniformLoopWeight : public LoopWeight { } }; +class CotangentLoopWeight : public LoopWeight { + public: + CotangentLoopWeight(FairingContext *fairing_context) + { + const int totloop = fairing_context->loop_count_get(); + fairing_context_ = fairing_context; + loop_weights_.resize(totloop); + cached_.resize(totloop); + for (int i = 0; i < totloop; i++) { + cached_[i] = false; + } + } + ~CotangentLoopWeight() = default; + + float weight_at_index(const int index) override + { + if (!cached_[index]) { + loop_weights_[index] = fairing_context_->cotangent_loop_weight_get(index); + cached_[index] = true; + } + return loop_weights_[index]; + } + + private: + Vector loop_weights_; + Vector cached_; + FairingContext *fairing_context_; +}; + static void prefair_and_fair_verts(FairingContext *fairing_context, bool *affected_verts, const eMeshFairingDepth depth) @@ -454,16 +616,21 @@ static void prefair_and_fair_verts(FairingContext *fairing_context, /* Prefair. */ UniformVertexWeight *uniform_vertex_weights = new UniformVertexWeight(fairing_context); UniformLoopWeight *uniform_loop_weights = new UniformLoopWeight(); - fairing_context->fair_verts(affected_verts, depth, uniform_vertex_weights, uniform_loop_weights); + fairing_context->fair_verts( + affected_verts, MESH_FAIRING_DEPTH_POSITION, uniform_vertex_weights, uniform_loop_weights); + delete uniform_vertex_weights; + delete uniform_loop_weights; /* Fair. */ VoronoiVertexWeight *voronoi_vertex_weights = new VoronoiVertexWeight(fairing_context); - /* TODO: Implement cotangent loop weights. */ - fairing_context->fair_verts(affected_verts, depth, voronoi_vertex_weights, uniform_loop_weights); + CotangentLoopWeight *cotangent_loop_weights = new CotangentLoopWeight(fairing_context); + + fairing_context->fair_verts( + affected_verts, depth, voronoi_vertex_weights, cotangent_loop_weights); - delete uniform_loop_weights; delete voronoi_vertex_weights; + delete cotangent_loop_weights; } void BKE_mesh_prefair_and_fair_verts(struct Mesh *mesh, diff --git a/source/blender/blenkernel/intern/mesh_legacy_convert.cc b/source/blender/blenkernel/intern/mesh_legacy_convert.cc index dee96b42e57..a72d3256923 100644 --- a/source/blender/blenkernel/intern/mesh_legacy_convert.cc +++ b/source/blender/blenkernel/intern/mesh_legacy_convert.cc @@ -712,7 +712,7 @@ void BKE_mesh_convert_mfaces_to_mpolys(Mesh *mesh) * used when creating pdata and ldata for pre-bmesh * meshes and needed to preserve active/render/clone/stencil flags set in pre-bmesh files. */ -static void CustomData_bmesh_do_versions_update_active_layers(CustomData *fdata, CustomData *ldata) +void CustomData_bmesh_do_versions_update_active_layers(CustomData *fdata, CustomData *ldata) { int act; diff --git a/source/blender/blenkernel/intern/mesh_mapping.cc b/source/blender/blenkernel/intern/mesh_mapping.cc index 427f3a44ddf..5e28ac515e9 100644 --- a/source/blender/blenkernel/intern/mesh_mapping.cc +++ b/source/blender/blenkernel/intern/mesh_mapping.cc @@ -12,15 +12,19 @@ #include "DNA_meshdata_types.h" #include "DNA_vec_types.h" +#include "BLI_alloca.h" #include "BLI_array.hh" #include "BLI_bitmap.h" #include "BLI_buffer.h" #include "BLI_function_ref.hh" #include "BLI_math.h" +#include "BLI_sort.h" +#include "BLI_sort_utils.h" #include "BLI_task.hh" #include "BLI_utildefines.h" #include "BKE_customdata.h" +#include "BKE_mesh.h" #include "BKE_mesh_mapping.h" #include "BLI_memarena.h" @@ -184,6 +188,271 @@ void BKE_mesh_uv_vert_map_free(UvVertMap *vmap) } } +typedef struct DiskCycleSortData { + float th; + int i, elem; + const float *co; +} DiskCycleSortData; + +/** + * Calculate a normal from a vertex cloud. + * + * \note We could make a higher quality version that takes all vertices into account. + * Currently it finds 4 outer most points returning its normal. + */ +static void calc_cloud_normal(DiskCycleSortData *varr, + int varr_len, + float r_normal[3], + float r_center[3], + int *r_index_tangent) +{ + const float varr_len_inv = 1.0f / (float)varr_len; + + /* Get the center point and collect vector array since we loop over these a lot. */ + float center[3] = {0.0f, 0.0f, 0.0f}; + for (int i = 0; i < varr_len; i++) { + madd_v3_v3fl(center, varr[i].co, varr_len_inv); + } + + /* Find the 'co_a' point from center. */ + int co_a_index = 0; + const float *co_a = nullptr; + { + float dist_sq_max = -1.0f; + for (int i = 0; i < varr_len; i++) { + const float dist_sq_test = len_squared_v3v3(varr[i].co, center); + if (!(dist_sq_test <= dist_sq_max)) { + co_a = varr[i].co; + co_a_index = i; + dist_sq_max = dist_sq_test; + } + } + } + + float dir_a[3]; + const float *co_a_opposite = nullptr; + const float *co_b_opposite = nullptr; + + sub_v3_v3v3(dir_a, co_a, center); + normalize_v3(dir_a); + + const float *co_b = nullptr; + float dir_b[3] = {0.0f, 0.0f, 0.0f}; + { + float dist_sq_max = -1.0f; + for (int i = 0; i < varr_len; i++) { + if (varr[i].co == co_a) { + continue; + } + float dir_test[3]; + sub_v3_v3v3(dir_test, varr[i].co, center); + project_plane_normalized_v3_v3v3(dir_test, dir_test, dir_a); + const float dist_sq_test = len_squared_v3(dir_test); + if (!(dist_sq_test <= dist_sq_max)) { + co_b = varr[i].co; + dist_sq_max = dist_sq_test; + copy_v3_v3(dir_b, dir_test); + } + } + } + + if (varr_len <= 3) { + normal_tri_v3(r_normal, center, co_a, co_b); + goto finally; + } + + normalize_v3(dir_b); + + { + float dot_a_min = FLT_MAX; + float dot_b_min = FLT_MAX; + for (int i = 0; i < varr_len; i++) { + const float *co_test = varr[i].co; + float dot_test; + + if (co_test != co_a) { + dot_test = dot_v3v3(dir_a, co_test); + if (dot_test < dot_a_min) { + dot_a_min = dot_test; + co_a_opposite = co_test; + } + } + + if (co_test != co_b) { + dot_test = dot_v3v3(dir_b, co_test); + if (dot_test < dot_b_min) { + dot_b_min = dot_test; + co_b_opposite = co_test; + } + } + } + } + + normal_quad_v3(r_normal, co_a, co_b, co_a_opposite, co_b_opposite); + +finally: + if (r_center != nullptr) { + copy_v3_v3(r_center, center); + } + if (r_index_tangent != nullptr) { + *r_index_tangent = co_a_index; + } +} + +static bool build_disk_cycle_face(const MPoly *mpoly, + const MLoop *mloop, + const MEdge *medge, + const float (*vert_cos)[3], + int vertex_i, + MeshElemMap *elem, + int *doneset, + int *donelen, + DiskCycleSortData *sortdata) +{ + *donelen = 0; + + for (int i = 0; i < elem->count; i++) { + const MPoly *mp = mpoly + elem->indices[i]; + int loops[2]; + + if (poly_get_adj_loops_from_vert(mp, mloop, vertex_i, loops)) { + for (int j = 0; j < 2; j++) { + if (loops[j] != vertex_i) { + bool ok = true; + + for (int k = 0; k < *donelen; k++) { + if ((int)doneset[k] == loops[j]) { + ok = false; + } + } + + if (ok) { + doneset[*donelen] = (int)loops[j]; + sortdata[*donelen].elem = elem->indices[i]; + sortdata[*donelen].co = vert_cos[loops[j]]; + (*donelen)++; + + break; + } + } + } + } + else { + printf("sort error in sort_disk_cycle_face\n"); + continue; + } + } + + return *donelen == elem->count; +} + +static bool build_disk_cycle_loop(const MPoly *mpoly, + const MLoop *mloop, + const MEdge *medge, + const float (*vert_cos)[3], + int vertex_i, + MeshElemMap *elem, + int *doneset, + int *donelen, + DiskCycleSortData *sortdata) +{ + *donelen = 0; + + for (int i = 0; i < elem->count; i++) { + int l1 = elem->indices[i]; + const MLoop *ml = mloop + l1; + const MEdge *me = medge + ml->e; + + unsigned int v = me->v1 != (unsigned int)vertex_i ? me->v1 : me->v2; + + sortdata[i].co = vert_cos[v]; + sortdata[i].elem = l1; + sortdata[i].i = i; + + (*donelen)++; + } + + return *donelen == elem->count; +} + +static bool build_disk_cycle_edge(const MPoly *mpoly, + const MLoop *mloop, + const MEdge *medge, + const float (*vert_cos)[3], + int vertex_i, + MeshElemMap *elem, + int *doneset, + int *donelen, + DiskCycleSortData *sortdata) +{ + *donelen = 0; + + for (int i = 0; i < elem->count; i++) { + const MEdge *me = medge + elem->indices[i]; + + unsigned int v = me->v1 != (unsigned int)vertex_i ? me->v1 : me->v2; + + sortdata[i].co = vert_cos[v]; + sortdata[i].elem = elem->indices[i]; + sortdata[i].i = i; + + (*donelen)++; + } + + return *donelen == elem->count; +} + +static bool sort_disk_cycle(const MPoly *mpoly, + const MLoop *mloop, + const MEdge *medge, + const float (*vert_cos)[3], + int vertex_i, + MeshElemMap *elem, + bool is_loops, + bool is_edges) +{ + DiskCycleSortData *sortdata = (DiskCycleSortData *)BLI_array_alloca(sortdata, + (unsigned int)elem->count); + int *doneset = (int *)BLI_array_alloca(doneset, (unsigned int)elem->count); + int donelen = 0; + + if (is_loops) { + if (!build_disk_cycle_face( + mpoly, mloop, medge, vert_cos, vertex_i, elem, doneset, &donelen, sortdata)) { + return false; + } + } + else if (is_edges) { + if (!build_disk_cycle_edge( + mpoly, mloop, medge, vert_cos, vertex_i, elem, doneset, &donelen, sortdata)) { + return false; + } + } + else { + if (!build_disk_cycle_loop( + mpoly, mloop, medge, vert_cos, vertex_i, elem, doneset, &donelen, sortdata)) { + return false; + } + } + + float no[3], cent[3]; + int vadj; + + calc_cloud_normal(sortdata, donelen, no, cent, &vadj); + + for (int i = 0; i < donelen; i++) { + sortdata[i].th = angle_signed_on_axis_v3v3v3_v3(sortdata[vadj].co, cent, sortdata[i].co, no); + } + + qsort((void *)sortdata, (size_t)donelen, sizeof(DiskCycleSortData), BLI_sortutil_cmp_float); + + for (int i = 0; i < donelen; i++) { + elem->indices[i] = sortdata[i].elem; + } + + return true; +} + /** * Generates a map where the key is the vertex and the value is a list * of polys or loops that use that vertex as a corner. The lists are allocated @@ -193,12 +462,15 @@ void BKE_mesh_uv_vert_map_free(UvVertMap *vmap) */ static void mesh_vert_poly_or_loop_map_create(MeshElemMap **r_map, int **r_mem, + const float (*vert_cos)[3], + const MEdge *medge, const MPoly *mpoly, const MLoop *mloop, int totvert, int totpoly, int totloop, - const bool do_loops) + const bool do_loops, + const bool sort_disk_cycles) { MeshElemMap *map = MEM_cnew_array(size_t(totvert), __func__); int *indices, *index_iter; @@ -237,30 +509,62 @@ static void mesh_vert_poly_or_loop_map_create(MeshElemMap **r_map, } } + if (sort_disk_cycles) { + for (i = 0; i < totvert; i++) { + sort_disk_cycle(mpoly, mloop, medge, vert_cos, i, map + i, do_loops, false); + } + } + *r_map = map; *r_mem = indices; } void BKE_mesh_vert_poly_map_create(MeshElemMap **r_map, int **r_mem, + const float (*vert_cos)[3], + const MEdge *medge, const MPoly *mpoly, const MLoop *mloop, int totvert, int totpoly, - int totloop) + int totloop, + const bool sort_disk_cycles) { - mesh_vert_poly_or_loop_map_create(r_map, r_mem, mpoly, mloop, totvert, totpoly, totloop, false); + mesh_vert_poly_or_loop_map_create(r_map, + r_mem, + vert_cos, + medge, + mpoly, + mloop, + totvert, + totpoly, + totloop, + false, + sort_disk_cycles); } void BKE_mesh_vert_loop_map_create(MeshElemMap **r_map, int **r_mem, + const float (*vert_cos)[3], + const MEdge *medge, const MPoly *mpoly, const MLoop *mloop, int totvert, int totpoly, - int totloop) + int totloop, + const bool sort_disk_cycles) { - mesh_vert_poly_or_loop_map_create(r_map, r_mem, mpoly, mloop, totvert, totpoly, totloop, true); + mesh_vert_poly_or_loop_map_create(r_map, + r_mem, + vert_cos, + medge, + mpoly, + mloop, + totvert, + totpoly, + totloop, + true, + sort_disk_cycles); } void BKE_mesh_vert_looptri_map_create(MeshElemMap **r_map, @@ -306,8 +610,18 @@ void BKE_mesh_vert_looptri_map_create(MeshElemMap **r_map, *r_mem = indices; } -void BKE_mesh_vert_edge_map_create( - MeshElemMap **r_map, int **r_mem, const MEdge *medge, int totvert, int totedge) +/** + * Generates a map where the key is the vertex and the value + * is a list of edges that use that vertex as an endpoint. + * The lists are allocated from one memory pool. + */ +void BKE_mesh_vert_edge_map_create(MeshElemMap **r_map, + int **r_mem, + const float (*vert_cos)[3], + const MEdge *medge, + int totvert, + int totedge, + bool sort_disk_cycles) { MeshElemMap *map = MEM_cnew_array(size_t(totvert), __func__); int *indices = static_cast(MEM_mallocN(sizeof(int[2]) * size_t(totedge), __func__)); @@ -341,6 +655,12 @@ void BKE_mesh_vert_edge_map_create( map[v[1]].count++; } + if (sort_disk_cycles) { + for (i = 0; i < totvert; i++) { + sort_disk_cycle(nullptr, nullptr, medge, vert_cos, i, map + i, false, true); + } + } + *r_map = map; *r_mem = indices; } @@ -1158,8 +1478,8 @@ static bool mesh_calc_islands_loop_poly_uv(const MEdge *edges, edge_innercut_indices); } - MEM_freeN(edge_poly_map); - MEM_freeN(edge_poly_mem); + MEM_SAFE_FREE(edge_poly_map); + MEM_SAFE_FREE(edge_poly_mem); if (luvs) { MEM_freeN(edge_loop_map); diff --git a/source/blender/blenkernel/intern/mesh_merge.c b/source/blender/blenkernel/intern/mesh_merge.c index fbc4ac3d208..38fd0212d04 100644 --- a/source/blender/blenkernel/intern/mesh_merge.c +++ b/source/blender/blenkernel/intern/mesh_merge.c @@ -334,8 +334,16 @@ Mesh *BKE_mesh_merge_verts(Mesh *mesh, /* Can we optimize by reusing an old `pmap`? How do we know an old `pmap` is stale? */ /* When called by `MOD_array.c` the `cddm` has just been created, so it has no valid `pmap`. */ - BKE_mesh_vert_poly_map_create( - &poly_map, &poly_map_mem, src_polys, src_loops, totvert, totpoly, totloop); + BKE_mesh_vert_poly_map_create(&poly_map, + &poly_map_mem, + NULL, + src_edges, + src_polys, + src_loops, + totvert, + totpoly, + totloop, + false); } /* done preparing for fast poly compare */ BLI_bitmap *vert_tag = BLI_BITMAP_NEW(mesh->totvert, __func__); diff --git a/source/blender/blenkernel/intern/mesh_merge_customdata.cc b/source/blender/blenkernel/intern/mesh_merge_customdata.cc index c10c9d851b9..58cec58548b 100644 --- a/source/blender/blenkernel/intern/mesh_merge_customdata.cc +++ b/source/blender/blenkernel/intern/mesh_merge_customdata.cc @@ -116,11 +116,14 @@ void BKE_mesh_merge_customdata_for_apply_modifier(Mesh *me) struct MeshElemMap *vert_to_loop; BKE_mesh_vert_loop_map_create(&vert_to_loop, &vert_map_mem, + NULL, + BKE_mesh_edges(me), BKE_mesh_polys(me), BKE_mesh_loops(me), me->totvert, me->totpoly, - me->totloop); + me->totloop, + false); Vector mloopuv_layers; mloopuv_layers.reserve(mloopuv_layers_num); diff --git a/source/blender/blenkernel/intern/mesh_mirror.cc b/source/blender/blenkernel/intern/mesh_mirror.cc index 95f8008a4f9..8e99d814801 100644 --- a/source/blender/blenkernel/intern/mesh_mirror.cc +++ b/source/blender/blenkernel/intern/mesh_mirror.cc @@ -25,7 +25,8 @@ #include "MOD_modifiertypes.h" -Mesh *BKE_mesh_mirror_bisect_on_mirror_plane_for_modifier(MirrorModifierData *mmd, +Mesh *BKE_mesh_mirror_bisect_on_mirror_plane_for_modifier(Object *ob, + MirrorModifierData *mmd, const Mesh *mesh, int axis, const float plane_co[3], @@ -51,7 +52,7 @@ Mesh *BKE_mesh_mirror_bisect_on_mirror_plane_for_modifier(MirrorModifierData *mm bmesh_from_mesh_params.cd_mask_extra.emask = CD_MASK_ORIGINDEX; bmesh_from_mesh_params.cd_mask_extra.pmask = CD_MASK_ORIGINDEX; - bm = BKE_mesh_to_bmesh_ex(mesh, &bmesh_create_params, &bmesh_from_mesh_params); + bm = BKE_mesh_to_bmesh_ex(nullptr, mesh, &bmesh_create_params, &bmesh_from_mesh_params); /* Define bisecting plane (aka mirror plane). */ float plane[4]; @@ -96,7 +97,8 @@ void BKE_mesh_mirror_apply_mirror_on_axis(struct Main *bmain, bmesh_from_mesh_params.calc_vert_normal = true; bmesh_from_mesh_params.cd_mask_extra.vmask = CD_MASK_SHAPEKEY; - BMesh *bm = BKE_mesh_to_bmesh_ex(mesh, &bmesh_create_params, &bmesh_from_mesh_params); + BMesh *bm = BKE_mesh_to_bmesh_ex(nullptr, mesh, &bmesh_create_params, &bmesh_from_mesh_params); + BMO_op_callf(bm, (BMO_FLAG_DEFAULTS & ~BMO_FLAG_RESPECT_HIDE), "symmetrize input=%avef direction=%i dist=%f use_shapekey=%b", @@ -107,7 +109,7 @@ void BKE_mesh_mirror_apply_mirror_on_axis(struct Main *bmain, BMeshToMeshParams bmesh_to_mesh_params{}; bmesh_to_mesh_params.calc_object_remap = true; - BM_mesh_bm_to_me(bmain, bm, mesh, &bmesh_to_mesh_params); + BM_mesh_bm_to_me(bmain, nullptr, bm, mesh, &bmesh_to_mesh_params); BM_mesh_free(bm); } @@ -184,7 +186,7 @@ Mesh *BKE_mesh_mirror_apply_mirror_on_axis_for_modifier(MirrorModifierData *mmd, Mesh *mesh_bisect = nullptr; if (do_bisect) { mesh_bisect = BKE_mesh_mirror_bisect_on_mirror_plane_for_modifier( - mmd, mesh, axis, plane_co, plane_no); + ob, mmd, mesh, axis, plane_co, plane_no); mesh = mesh_bisect; } diff --git a/source/blender/blenkernel/intern/mesh_remap.cc b/source/blender/blenkernel/intern/mesh_remap.cc index 5f3961398fb..a2220325ffa 100644 --- a/source/blender/blenkernel/intern/mesh_remap.cc +++ b/source/blender/blenkernel/intern/mesh_remap.cc @@ -742,9 +742,11 @@ void BKE_mesh_remap_calc_edges_from_mesh(const int mode, BKE_mesh_vert_edge_map_create(&vert_to_edge_src_map, &vert_to_edge_src_map_mem, + NULL, edges_src, num_verts_src, - num_edges_src); + num_edges_src, + false); BKE_bvhtree_from_mesh_get(&treedata, me_src, BVHTREE_FROM_VERTS, 2); nearest.index = -1; @@ -1405,19 +1407,25 @@ void BKE_mesh_remap_calc_loops_from_mesh(const int mode, if (use_from_vert) { BKE_mesh_vert_loop_map_create(&vert_to_loop_map_src, &vert_to_loop_map_src_buff, + nullptr, + edges_src, polys_src, loops_src, num_verts_src, num_polys_src, - num_loops_src); + num_loops_src, + false); if (mode & MREMAP_USE_POLY) { BKE_mesh_vert_poly_map_create(&vert_to_poly_map_src, &vert_to_poly_map_src_buff, + nullptr, + edges_src, polys_src, loops_src, num_verts_src, num_polys_src, - num_loops_src); + num_loops_src, + false); } } diff --git a/source/blender/blenkernel/intern/mesh_remesh_voxel.cc b/source/blender/blenkernel/intern/mesh_remesh_voxel.cc index dfbc1298960..7f571a98652 100644 --- a/source/blender/blenkernel/intern/mesh_remesh_voxel.cc +++ b/source/blender/blenkernel/intern/mesh_remesh_voxel.cc @@ -421,19 +421,25 @@ void BKE_remesh_reproject_vertex_paint(Mesh *target, const Mesh *source) if (!source_lmap) { BKE_mesh_vert_loop_map_create(&source_lmap, &source_lmap_mem, + nullptr, + source->edges().data(), source->polys().data(), source->loops().data(), source->totvert, source->totpoly, - source->totloop); + source->totloop, + false); BKE_mesh_vert_loop_map_create(&target_lmap, &target_lmap_mem, + nullptr, + target->edges().data(), target->polys().data(), target->loops().data(), target->totvert, target->totpoly, - target->totloop); + target->totloop, + false); } blender::threading::parallel_for( @@ -511,7 +517,7 @@ struct Mesh *BKE_mesh_remesh_voxel_fix_poles(const Mesh *mesh) BMeshFromMeshParams bmesh_from_mesh_params{}; bmesh_from_mesh_params.calc_face_normal = true; bmesh_from_mesh_params.calc_vert_normal = true; - BM_mesh_bm_from_me(bm, mesh, &bmesh_from_mesh_params); + BM_mesh_bm_from_me(nullptr, bm, mesh, &bmesh_from_mesh_params); BMVert *v; BMEdge *ed, *ed_next; @@ -545,7 +551,7 @@ struct Mesh *BKE_mesh_remesh_voxel_fix_poles(const Mesh *mesh) if (BM_elem_flag_test(ed, BM_ELEM_TAG)) { float co[3]; mid_v3_v3v3(co, ed->v1->co, ed->v2->co); - BMVert *vc = BM_edge_collapse(bm, ed, ed->v1, true, true); + BMVert *vc = BM_edge_collapse(bm, ed, ed->v1, true, true, true, true, nullptr); copy_v3_v3(vc->co, co); } } diff --git a/source/blender/blenkernel/intern/mesh_tangent.cc b/source/blender/blenkernel/intern/mesh_tangent.cc index b0bc16b2732..2f7463114e3 100644 --- a/source/blender/blenkernel/intern/mesh_tangent.cc +++ b/source/blender/blenkernel/intern/mesh_tangent.cc @@ -25,7 +25,7 @@ #include "BKE_mesh_tangent.h" #include "BKE_report.h" -#include "BLI_strict_flags.h" +//#include "BLI_strict_flags.h" #include "atomic_ops.h" #include "mikktspace.hh" diff --git a/source/blender/blenkernel/intern/mesh_validate.cc b/source/blender/blenkernel/intern/mesh_validate.cc index 0d6afdd4dd3..12ca8073cb8 100644 --- a/source/blender/blenkernel/intern/mesh_validate.cc +++ b/source/blender/blenkernel/intern/mesh_validate.cc @@ -716,7 +716,11 @@ bool BKE_mesh_validate_arrays(Mesh *mesh, if ((p1_nv == p2_nv) && (memcmp(p1_v, p2_v, p1_nv * sizeof(*p1_v)) == 0)) { if (do_verbose) { /* TODO: convert list to string */ - PRINT_ERR("\tPolys %u and %u use same vertices (%d", prev_sp->index, sp->index, *p1_v); + PRINT_ERR("\tPolys %u(len=%d) and %u use same vertices (%d", + prev_sp->index, + p1_nv, + sp->index, + *p1_v); for (j = 1; j < p1_nv; j++) { PRINT_ERR(", %d", p1_v[j]); } diff --git a/source/blender/blenkernel/intern/multires.cc b/source/blender/blenkernel/intern/multires.cc index aaa35e02747..565d9d2fe5a 100644 --- a/source/blender/blenkernel/intern/multires.cc +++ b/source/blender/blenkernel/intern/multires.cc @@ -17,22 +17,31 @@ #include "BLI_bitmap.h" #include "BLI_blenlib.h" +#include "BLI_edgehash.h" #include "BLI_math.h" #include "BLI_task.h" #include "BLI_utildefines.h" #include "BKE_ccg.h" #include "BKE_cdderivedmesh.h" +#include "BKE_collection.h" +#include "BKE_context.h" #include "BKE_editmesh.h" +#include "BKE_global.h" +#include "BKE_layer.h" +#include "BKE_main.h" #include "BKE_mesh.h" #include "BKE_mesh_mapping.h" #include "BKE_mesh_runtime.h" #include "BKE_modifier.h" #include "BKE_multires.h" +#include "BKE_object.h" #include "BKE_paint.h" #include "BKE_pbvh.h" #include "BKE_scene.h" +#include "BKE_subdiv.h" #include "BKE_subdiv_ccg.h" +#include "BKE_subdiv_eval.h" #include "BKE_subsurf.h" #include "BKE_object.h" @@ -41,6 +50,8 @@ #include "DEG_depsgraph_query.h" +#include "bmesh.h" +#include "multires_inline.h" #include "multires_reshape.hh" #include @@ -470,9 +481,8 @@ void multires_force_sculpt_rebuild(Object *object) object->sculpt->pbvh = nullptr; } - MEM_SAFE_FREE(ss->pmap); - - MEM_SAFE_FREE(ss->pmap_mem); + BKE_pbvh_pmap_release(ss->pmap); + ss->pmap = nullptr; } void multires_force_external_reload(Object *object) @@ -853,17 +863,461 @@ static void grid_tangent_matrix(float mat[3][3], const CCGKey *key, int x, int y struct MultiresThreadedData { DispOp op; + MultiResSpace bmop; + BMesh *bm; + int lvl; CCGElem **gridData, **subGridData; CCGKey *key; CCGKey *sub_key; + Subdiv *sd; const MPoly *mpoly; MDisps *mdisps; GridPaintMask *grid_paint_mask; int *gridOffset; + int cd_mdisps_off, cd_mask_off; int gridSize, dGridSize, dSkip; float (*smat)[3]; + bool has_grid_mask; }; +extern "C" Object *multires_dump_grids_bmesh(Object *bmob, BMesh *bm) +{ + if (!CustomData_has_layer(&bm->ldata, CD_MDISPS)) { + printf("multires_dump_grids_bmesh: error: no multires grids\n"); + return nullptr; + } + + bool spaceset = false; + + if (bm->multiresSpace != MULTIRES_SPACE_ABSOLUTE) { + BKE_multires_bmesh_space_set(bmob, bm, MULTIRES_SPACE_ABSOLUTE); + spaceset = true; + } + + Main *bmain = G.main; + const char *name = "multires_dump"; + + bContext *ctx = CTX_create(); + CTX_data_main_set(ctx, bmain); + CTX_wm_manager_set(ctx, (wmWindowManager *)G.main->wm.first); + CTX_data_scene_set(ctx, (Scene *)G.main->scenes.first); + + ViewLayer *view_layer = CTX_data_view_layer(ctx); + Object *ob = BKE_object_add_only_object(bmain, OB_MESH, name); + LayerCollection *layer_collection; + + ob->data = BKE_object_obdata_add_from_type(bmain, OB_MESH, name); + DEG_id_tag_update_ex( + bmain, &ob->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_ANIMATION); + // DEG_id_tag_update_ex( + // bmain, &ob->data, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_ANIMATION); + + layer_collection = BKE_layer_collection_get_active(view_layer); + BKE_collection_object_add(bmain, layer_collection->collection, ob); + + DEG_id_type_tag(bmain, ID_OB); + DEG_relations_tag_update(bmain); + if (ob->data != nullptr) { + DEG_id_tag_update_ex(bmain, (ID *)ob->data, ID_RECALC_EDITORS); + } + + ob->modifiers.first = ob->modifiers.last = nullptr; + zero_v3(ob->loc); + + printf("users: %d\n", ob->id.us); + + Mesh *me = (Mesh *)ob->data; + + BMIter iter; + BMFace *f; + + int cd_mdisp_off = CustomData_get_offset(&bm->ldata, CD_MDISPS); + int dimen = 0; + + BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { + BMLoop *l = f->l_first; + MDisps *md = (MDisps *)BM_ELEM_CD_GET_VOID_P(l, cd_mdisp_off); + dimen = (int)floor(sqrt(md->totdisp) + 0.00001); + break; + } + + if (!dimen) { + printf("multires_dump_grids_bmesh: error: corrupted multires data\n"); + return nullptr; + } + + int totvert = bm->totloop * dimen * dimen; + int totface = bm->totloop * (dimen - 1) * (dimen - 1); + int totloop = totface * 4; + + 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 = totvert; + me->totpoly = totface; + me->totloop = totloop; + me->totedge = totvert + totface; + me->totface = 0; + me->act_face = -1; + + EdgeHash *eh = BLI_edgehash_new_ex("multires_dump_bmesh", me->totedge); + + MVert *mvert = me->totvert ? (MVert *)MEM_callocN(sizeof(MVert) * me->totvert, + "multires_dump_grids_bmesh.vert") : + nullptr; + MEdge *medge = me->totedge ? (MEdge *)MEM_callocN(sizeof(MEdge) * me->totedge, + "multires_dump_grids_bmesh.edge") : + nullptr; + MLoop *mloop = me->totloop ? (MLoop *)MEM_callocN(sizeof(MLoop) * me->totloop, + "multires_dump_grids_bmesh.loop") : + nullptr; + MPoly *mpoly = me->totpoly ? (MPoly *)MEM_callocN(sizeof(MPoly) * me->totpoly, + "multires_dump_grids_bmesh.poly") : + nullptr; + + me->cd_flag = 0; + + me->mvert = (MVert *)CustomData_add_layer(&me->vdata, CD_MVERT, CD_ASSIGN, mvert, me->totvert); + me->medge = (MEdge *)CustomData_add_layer(&me->edata, CD_MEDGE, CD_ASSIGN, medge, me->totedge); + me->mloop = (MLoop *)CustomData_add_layer(&me->ldata, CD_MLOOP, CD_ASSIGN, mloop, me->totloop); + me->mpoly = (MPoly *)CustomData_add_layer(&me->pdata, CD_MPOLY, CD_ASSIGN, mpoly, me->totpoly); + + int loopi = 0; + int outli = 0; + int medi = 0; + +#define VINDEX(i, j) (loopi * dimen * dimen + ((j)*dimen + (i))) + + float(*vert_positions)[3] = BKE_mesh_vert_positions_for_write(me); + + // CustomData_daata_ + BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { + BMLoop *l = f->l_first; + do { + MDisps *md = (MDisps *)BM_ELEM_CD_GET_VOID_P(l, cd_mdisp_off); + + for (int i = 0; i < dimen; i++) { + for (int j = 0; j < dimen; j++) { + int vidx = loopi * dimen * dimen + (j * dimen + i); + int idx = j * dimen + i; + float *co = md->disps[idx]; + + copy_v3_v3(vert_positions[vidx], co); + } + } + + for (int i = 0; i < dimen - 1; i++) { + for (int j = 0; j < dimen - 1; j++) { + // do face + int fidx = loopi * (dimen - 1) * (dimen - 1) + (j * (dimen - 1) + i); + MPoly *mp = mpoly + fidx; + + mp->totloop = 4; + mp->loopstart = outli; + + MLoop *ml = mloop + outli; + + ml[0].v = VINDEX(i, j); + ml[1].v = VINDEX(i, j + 1); + ml[2].v = VINDEX(i + 1, j + 1); + ml[3].v = VINDEX(i + 1, j); + + for (int i2 = 0; i2 < 4; i2++) { + int a = ml[i2].v, b = ml[(i2 + 1) % 4].v; + int e; + + if (!BLI_edgehash_haskey(eh, a, b)) { + BLI_edgehash_insert(eh, a, b, POINTER_FROM_INT(medi)); + e = medi; + + MEdge *med = medge + medi; + + med->v1 = a; + med->v2 = b; + + medi++; + } + else { + e = POINTER_AS_INT(BLI_edgehash_lookup(eh, a, b)); + } + + ml[i2].e = e; + } + + outli += 4; + } + } + + loopi++; + l = l->next; + } while (l != f->l_first); + } + + for (int i = 0; i < me->totpoly; i++) { + if (!mpoly[i].totloop) { + printf("error 1! %d %d\n", i, me->totpoly); + } + if (mpoly[i].loopstart >= me->totloop) { + printf( + "error 2! %d %d l: %d totl: %d\n", i, me->totpoly, mpoly[i].loopstart, mpoly[i].totloop); + } + } + + if (spaceset) { + BKE_multires_bmesh_space_set(bmob, bm, MULTIRES_SPACE_TANGENT); + } + + BKE_mesh_calc_normals(me); + + return ob; +} + +//#define LIMIT_MAX_DISPLACEMENT + +static void multires_bmesh_space_set_cb(void *__restrict userdata, + const int pidx, + const TaskParallelTLS *__restrict) +{ + MultiresThreadedData *tdata = (MultiresThreadedData *)userdata; + + int cd_mdisps_off = tdata->cd_mdisps_off; + BMesh *bm = tdata->bm; + MultiResSpace op = tdata->bmop; + BMFace *f = bm->ftable[pidx]; + int gridSize = tdata->gridSize; + + int S, x, y; + + BMLoop *l; + +#ifdef LIMIT_MAX_DISPLACEMENT + l = f->l_first; + float cent[3]; + int tot = 0; + + // get face center to calculate maximum allowable displacement length + zero_v3(cent); + do { + add_v3_v3(cent, l->v->co); + tot++; + l = l->next; + } while (l != f->l_first); + + mul_v3_fl(cent, 1.0f / (float)tot); +#endif + + l = f->l_first; + S = 0; + do { + MDisps *mdisp = (MDisps *)BM_ELEM_CD_GET_VOID_P(l, cd_mdisps_off); + float(*dispgrid)[3] = nullptr; + + dispgrid = mdisp->disps; + +#ifdef LIMIT_MAX_DISPLACEMENT + /*try to limit numerical instability by clamping max displacement*/ + + float maxlen = len_v3v3(l->v->co, cent) * 15.0f; + maxlen = MAX2(maxlen, 0.00001f); +#endif + + for (y = 0; y < gridSize; y++) { + for (x = 0; x < gridSize; x++) { + float sco[8], udv[3], vdv[3]; + float *data = dispgrid[gridSize * y + x]; + float mat[3][3], disp[3]; + + float grid_u = (float)x / (float)(gridSize - 1); + float grid_v = (float)y / (float)(gridSize - 1); + float u, v; + + int corner = S; + if (f->len == 4) { + BKE_subdiv_rotate_grid_to_quad(corner, grid_u, grid_v, &u, &v); + } + else { + u = 1.0 - grid_v; + v = 1.0 - grid_u; + } + + BKE_subdiv_eval_limit_point_and_derivatives(tdata->sd, l->head.index, u, v, sco, udv, vdv); + BKE_multires_construct_tangent_matrix(mat, udv, vdv, f->len == 4 ? corner : 0); + + copy_v3_v3(disp, data); + + switch (op) { + case MULTIRES_SPACE_ABSOLUTE: + /* Convert displacement to object space + * and add to grid points */ + mul_v3_m3v3(disp, mat, data); + add_v3_v3v3(data, disp, sco); + break; + case MULTIRES_SPACE_TANGENT: + /* Calculate displacement between new and old + * grid points and convert to tangent space */ + invert_m3(mat); + + sub_v3_v3v3(disp, data, sco); + mul_v3_m3v3(data, mat, disp); + + // try to prevent errors + float len = len_v3(data); +#ifdef LIMIT_MAX_DISPLACEMENT + if (len > maxlen) { + mul_v3_fl(data, maxlen / len); + } + else if (isnan(len)) { + zero_v3(data); + } +#else + if (isnan(len)) { + zero_v3(data); + } +#endif + break; + } + } + } + + S++; + l = l->next; + } while (l != f->l_first); +} + +/* The original version of this function was broken (and subsequently removed) + because it didn't properly set the subdivision level; it also used the old + multires system. The new subdiv API is now used instead. + */ +void BKE_multires_bmesh_space_set(Object *ob, BMesh *bm, int mode) +{ + if (!bm->totface || !CustomData_has_layer(&bm->ldata, CD_MDISPS)) { + return; + } + + // get multires settings + MultiresModifierData *mmd = bm->haveMultiResSettings ? &bm->multires : nullptr; + + if (!mmd && ob) { + mmd = get_multires_modifier(nullptr, ob, true); + } + + if (!mmd || !CustomData_has_layer(&bm->ldata, CD_MDISPS)) { + return; + } + + // cache multires settings in bmesh + bm->multiresSpace = mode; + + // create temporary mesh structure + Mesh _me, *me = &_me; + memset(me, 0, sizeof(Mesh)); + CustomData_reset(&me->vdata); + CustomData_reset(&me->edata); + CustomData_reset(&me->ldata); + CustomData_reset(&me->fdata); + CustomData_reset(&me->pdata); + + CustomData_MeshMasks extra = CD_MASK_DERIVEDMESH; + extra.lmask |= CD_MASK_MDISPS; + + BM_mesh_bm_to_me_for_eval(bm, me, &extra); + SubdivSettings settings2; + + // copy the settings and then set subdivision level to max + MultiresModifierData mmdcpy = blender::dna::shallow_copy(*mmd); + mmdcpy.lvl = mmdcpy.sculptlvl = mmdcpy.renderlvl = mmdcpy.totlvl; + + // set up subdivision surface + BKE_multires_subdiv_settings_init(&settings2, &mmdcpy); + Subdiv *sd = BKE_subdiv_new_from_mesh(&settings2, me); + BKE_subdiv_eval_begin_from_mesh(sd, me, nullptr, SUBDIV_EVALUATOR_TYPE_CPU, nullptr); + + // create a fake object with .sculpt set to nullptr + Object fakeob; + if (ob) { + memcpy(static_cast(&fakeob), static_cast(ob), sizeof(*ob)); + fakeob.sculpt = nullptr; + } + else { + memset(&fakeob, 0, sizeof(fakeob)); + fakeob.data = me; + BLI_addtail(&fakeob.modifiers, &mmdcpy); + } + + int i, gridSize; + int totpoly = bm->totface; + + // force paranoia recalc of indices and lookup tables + bm->elem_index_dirty |= BM_FACE; + bm->elem_table_dirty |= BM_FACE; + + BM_mesh_elem_index_ensure(bm, BM_FACE); + BM_mesh_elem_table_ensure(bm, BM_FACE); + + gridSize = multires_side_tot[mmd->totlvl]; + + int cd_disp_off = CustomData_get_offset(&bm->ldata, CD_MDISPS); + + BMFace *f; + BMIter iter; + i = 0; + + /*check that all grids are allocated and also set some indices*/ + BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { + BMIter iter2; + BMLoop *l; + + f->head.index = i; + + BM_ITER_ELEM (l, &iter2, f, BM_LOOPS_OF_FACE) { + MDisps *mdisp = (MDisps *)BM_ELEM_CD_GET_VOID_P(l, cd_disp_off); + + /* allocate new disps, this can happen with newly created faces */ + if (!mdisp->disps) { + multires_reallocate_mdisps(1, mdisp, mmd->totlvl); + } + + l->head.index = i; + + if (f->len != 4) { + i++; + } + } + + if (f->len == 4) { + i++; + } + } + + // do the space conversion + + TaskParallelSettings settings; + BLI_parallel_range_settings_defaults(&settings); + settings.min_iter_per_thread = CCG_TASK_LIMIT; + + MultiresThreadedData data; + + memset(static_cast(&data), 0, sizeof(data)); + + data.bmop = (MultiResSpace)mode; + data.sd = sd; + data.lvl = mmd->totlvl; + data.bm = bm; + data.cd_mdisps_off = cd_disp_off; + data.gridSize = gridSize; + + BLI_task_parallel_range(0, totpoly, &data, multires_bmesh_space_set_cb, &settings); + + BKE_mesh_free_data_for_undo(me); + BKE_subdiv_free(sd); + + bm->elem_index_dirty |= BM_FACE | BM_LOOP; + bm->elem_table_dirty |= BM_FACE | BM_LOOP; +} + static void multires_disp_run_cb(void *__restrict userdata, const int pidx, const TaskParallelTLS *__restrict /*tls*/) @@ -1233,7 +1687,7 @@ void multires_stitch_grids(Object *ob) int num_faces; BKE_pbvh_get_grid_updates(pbvh, false, (void ***)&faces, &num_faces); if (num_faces) { - BKE_subdiv_ccg_average_stitch_faces(subdiv_ccg, faces, num_faces); + // XXX BKE_subdiv_ccg_average_stitch_faces(subdiv_ccg, faces, num_faces); MEM_freeN(faces); } } @@ -1351,7 +1805,7 @@ void old_mdisps_bilinear(float out[3], float (*disps)[3], const int st, float u, urat = u - x; vrat = v - y; - uopp = 1 - urat; + uopp = 1.0f - urat; mul_v3_v3fl(d[0], disps[y * st + x], uopp); mul_v3_v3fl(d[1], disps[y * st + x2], urat); @@ -1360,7 +1814,7 @@ void old_mdisps_bilinear(float out[3], float (*disps)[3], const int st, float u, add_v3_v3v3(d2[0], d[0], d[1]); add_v3_v3v3(d2[1], d[2], d[3]); - mul_v3_fl(d2[0], 1 - vrat); + mul_v3_fl(d2[0], 1.0f - vrat); mul_v3_fl(d2[1], vrat); add_v3_v3v3(out, d2[0], d2[1]); diff --git a/source/blender/blenkernel/intern/multires_reshape.cc b/source/blender/blenkernel/intern/multires_reshape.cc index d4e842dc10c..ba7f20f38dd 100644 --- a/source/blender/blenkernel/intern/multires_reshape.cc +++ b/source/blender/blenkernel/intern/multires_reshape.cc @@ -269,6 +269,12 @@ void multiresModifier_base_apply(Depsgraph *depsgraph, Object *object, MultiresM multires_reshape_apply_base_update_mesh_coords(&reshape_context); multires_reshape_apply_base_refit_base_mesh(&reshape_context); + /** + * Tag update so deform modifiers (e.g. smooth corrective) + * get updated original coordinates + */ + DEG_id_tag_update((ID *)object, ID_RECALC_GEOMETRY); + /* Reshape to the stored final state. * Not that the base changed, so the subdiv is to be refined to the new positions. Unfortunately, * this can not be done foe entirely cheap: if there were deformation modifiers prior to the diff --git a/source/blender/blenkernel/intern/multires_reshape_apply_base.cc b/source/blender/blenkernel/intern/multires_reshape_apply_base.cc index f2c2df9d7ce..f32de373529 100644 --- a/source/blender/blenkernel/intern/multires_reshape_apply_base.cc +++ b/source/blender/blenkernel/intern/multires_reshape_apply_base.cc @@ -75,11 +75,14 @@ void multires_reshape_apply_base_refit_base_mesh(MultiresReshapeContext *reshape int *pmap_mem; BKE_mesh_vert_poly_map_create(&pmap, &pmap_mem, + reshape_context->base_positions, + reshape_context->base_edges, reshape_context->base_polys, reshape_context->base_loops, base_mesh->totvert, base_mesh->totpoly, - base_mesh->totloop); + base_mesh->totloop, + false); float(*origco)[3] = static_cast( MEM_calloc_arrayN(base_mesh->totvert, sizeof(float[3]), __func__)); diff --git a/source/blender/blenkernel/intern/multires_reshape_smooth.cc b/source/blender/blenkernel/intern/multires_reshape_smooth.cc index 19cad4e8e71..081b2b0a107 100644 --- a/source/blender/blenkernel/intern/multires_reshape_smooth.cc +++ b/source/blender/blenkernel/intern/multires_reshape_smooth.cc @@ -32,6 +32,38 @@ #include "atomic_ops.h" #include "subdiv_converter.h" +bool debug_invert_m3_m3(float m1[3][3], const float m2[3][3], const char *func, int line) +{ + float det; + int a, b; + bool success; + + /* calc adjoint */ + adjoint_m3_m3(m1, m2); + + /* then determinant old matrix! */ + det = determinant_m3_array(m2); + + if (det > -0.0001 && det < 0.0001) { + fprintf(stderr, "matrix inverse error %s:%i\n\n", func, line); + } + + success = (det != 0.0f); + + if (LIKELY(det != 0.0f)) { + det = 1.0f / det; + for (a = 0; a < 3; a++) { + for (b = 0; b < 3; b++) { + m1[a][b] *= det; + } + } + } + + return success; +} + +//#define invert_m3_m3(m1, m2) debug_invert_m3_m3(m1, m2, __func__, __LINE__) + /* -------------------------------------------------------------------- */ /** \name Local Structs * \{ */ diff --git a/source/blender/blenkernel/intern/multires_reshape_util.cc b/source/blender/blenkernel/intern/multires_reshape_util.cc index 93d1662de14..bf51c619ff2 100644 --- a/source/blender/blenkernel/intern/multires_reshape_util.cc +++ b/source/blender/blenkernel/intern/multires_reshape_util.cc @@ -325,6 +325,8 @@ void multires_reshape_free_original_grids(MultiresReshapeContext *reshape_contex void multires_reshape_context_free(MultiresReshapeContext *reshape_context) { + ModifierData *md; + if (reshape_context->need_free_subdiv) { BKE_subdiv_free(reshape_context->subdiv); } diff --git a/source/blender/blenkernel/intern/multires_unsubdivide.cc b/source/blender/blenkernel/intern/multires_unsubdivide.cc index d6c2e8fd80e..e7ca4992c32 100644 --- a/source/blender/blenkernel/intern/multires_unsubdivide.cc +++ b/source/blender/blenkernel/intern/multires_unsubdivide.cc @@ -865,6 +865,7 @@ static BMesh *get_bmesh_from_mesh(Mesh *mesh) { const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(mesh); + BMeshCreateParams bm_create_params{}; bm_create_params.use_toolflags = true; BMesh *bm = BM_mesh_create(&allocsize, &bm_create_params); @@ -872,7 +873,7 @@ static BMesh *get_bmesh_from_mesh(Mesh *mesh) BMeshFromMeshParams bm_from_me_params{}; bm_from_me_params.calc_face_normal = true; bm_from_me_params.calc_vert_normal = true; - BM_mesh_bm_from_me(bm, mesh, &bm_from_me_params); + BM_mesh_bm_from_me(nullptr, bm, mesh, &bm_from_me_params); return bm; } @@ -1150,7 +1151,7 @@ bool multires_unsubdivide_to_basemesh(MultiresUnsubdivideContext *context) BMeshToMeshParams bm_to_me_params{}; bm_to_me_params.calc_object_remap = true; - BM_mesh_bm_to_me(nullptr, bm_base_mesh, context->base_mesh, &bm_to_me_params); + BM_mesh_bm_to_me(nullptr, nullptr, bm_base_mesh, context->base_mesh, &bm_to_me_params); BM_mesh_free(bm_base_mesh); /* Initialize bmesh and maps for the original mesh and extract the grids. */ diff --git a/source/blender/blenkernel/intern/object.cc b/source/blender/blenkernel/intern/object.cc index 984de28394f..c012596efda 100644 --- a/source/blender/blenkernel/intern/object.cc +++ b/source/blender/blenkernel/intern/object.cc @@ -540,10 +540,19 @@ static void object_blend_write(BlendWriter *writer, ID *id, const void *id_addre ob->mode &= ~OB_MODE_EDIT; } + PBVH *cached_pbvh = ob->cached_pbvh; + if (!is_undo) { + ob->cached_pbvh = NULL; + } + /* write LibData */ BLO_write_id_struct(writer, Object, id_address, &ob->id); BKE_id_blend_write(writer, &ob->id); + if (!is_undo) { + ob->cached_pbvh = cached_pbvh; + } + if (ob->adt) { BKE_animdata_blend_write(writer, ob->adt); } @@ -5146,6 +5155,8 @@ void BKE_object_runtime_reset_on_copy(Object *object, const int /*flag*/) runtime->crazyspace_deform_imats = nullptr; runtime->crazyspace_deform_cos = nullptr; + + object->cached_pbvh = runtime->cached_pbvh = NULL; } void BKE_object_runtime_free_data(Object *object) diff --git a/source/blender/blenkernel/intern/object_update.cc b/source/blender/blenkernel/intern/object_update.cc index 5abd5188d1e..f75a8a732d9 100644 --- a/source/blender/blenkernel/intern/object_update.cc +++ b/source/blender/blenkernel/intern/object_update.cc @@ -261,6 +261,8 @@ void BKE_object_sync_to_original(Depsgraph *depsgraph, Object *object) object_orig->transflag = object->transflag; object_orig->flag = object->flag; + // object_orig->cached_pbvh2 = object->cached_pbvh2; + /* Copy back error messages from modifiers. */ for (ModifierData *md = static_cast(object->modifiers.first), *md_orig = static_cast(object_orig->modifiers.first); diff --git a/source/blender/blenkernel/intern/paint.cc b/source/blender/blenkernel/intern/paint.cc index 0ee517311d2..dec3042666c 100644 --- a/source/blender/blenkernel/intern/paint.cc +++ b/source/blender/blenkernel/intern/paint.cc @@ -12,6 +12,7 @@ #include "DNA_brush_types.h" #include "DNA_gpencil_types.h" +#include "DNA_key_types.h" #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" #include "DNA_modifier_types.h" @@ -21,12 +22,15 @@ #include "DNA_view3d_types.h" #include "DNA_workspace_types.h" +#include "BLI_array.h" #include "BLI_bitmap.h" #include "BLI_hash.h" #include "BLI_listbase.h" #include "BLI_math_vector.h" #include "BLI_string_utf8.h" +#include "BLI_string_utils.h" #include "BLI_utildefines.h" +#include "BLI_vector.hh" #include "BLT_translation.h" @@ -38,6 +42,7 @@ #include "BKE_context.h" #include "BKE_crazyspace.h" #include "BKE_deform.h" +#include "BKE_global.h" #include "BKE_gpencil.h" #include "BKE_idtype.h" #include "BKE_image.h" @@ -57,6 +62,7 @@ #include "BKE_scene.h" #include "BKE_subdiv_ccg.h" #include "BKE_subsurf.h" +#include "BKE_undo_system.h" #include "DEG_depsgraph.h" #include "DEG_depsgraph_query.h" @@ -65,7 +71,14 @@ #include "BLO_read_write.h" +#include "../../bmesh/intern/bmesh_idmap.h" #include "bmesh.h" +#include "bmesh_log.h" + +// TODO: figure out bad cross module refs +extern "C" void SCULPT_undo_ensure_bmlog(Object *ob); + +static void init_sculptvert_layer(SculptSession *ss, PBVH *pbvh, int totvert); using blender::float3; using blender::MutableSpan; @@ -77,10 +90,13 @@ static SculptAttribute *sculpt_attribute_ensure_ex(Object *ob, eCustomDataType proptype, const char *name, const SculptAttributeParams *params, - PBVHType pbvhtype, - bool flat_array_for_bmesh); + PBVHType pbvhtype); static void sculptsession_bmesh_add_layers(Object *ob); +using blender::MutableSpan; +using blender::Span; +using blender::Vector; + static void palette_init_data(ID *id) { Palette *palette = (Palette *)id; @@ -615,7 +631,7 @@ ePaintMode BKE_paintmode_get_from_tool(const bToolRef *tref) Brush *BKE_paint_brush(Paint *p) { - return (Brush *)BKE_paint_brush_for_read((const Paint *)p); + return p ? (p->brush_eval ? p->brush_eval : p->brush) : nullptr; } const Brush *BKE_paint_brush_for_read(const Paint *p) @@ -1097,11 +1113,12 @@ bool BKE_paint_ensure(ToolSettings *ts, Paint **r_paint) Sculpt *data = MEM_cnew(__func__); paint = &data->paint; - /* Turn on X plane mirror symmetry by default. */ + /* Turn on X plane mirror symmetry by default */ paint->symmetry_flags |= PAINT_SYMM_X; - /* Make sure at least dyntopo subdivision is enabled. */ - data->flags |= SCULPT_DYNTOPO_SUBDIVIDE | SCULPT_DYNTOPO_COLLAPSE; + /* Make sure at least dyntopo subdivision is enabled */ + data->flags |= SCULPT_DYNTOPO_SUBDIVIDE | SCULPT_DYNTOPO_COLLAPSE | SCULPT_DYNTOPO_CLEANUP | + SCULPT_DYNTOPO_ENABLED; } else if ((GpPaint **)r_paint == &ts->gp_paint) { GpPaint *data = MEM_cnew(__func__); @@ -1328,13 +1345,27 @@ static bool paint_rake_rotation_active(const Brush &brush) bool paint_calculate_rake_rotation(UnifiedPaintSettings *ups, Brush *brush, - const float mouse_pos[2]) + const float mouse_pos[2], + const float initial_mouse_pos[2]) { bool ok = false; if (paint_rake_rotation_active(*brush)) { const float r = RAKE_THRESHHOLD; float rotation; + if (brush->flag & BRUSH_DRAG_DOT) { + const float dx = mouse_pos[0] - initial_mouse_pos[0]; + const float dy = mouse_pos[1] - initial_mouse_pos[1]; + + if (dx * dx + dy * dy > 0.5f) { + ups->brush_rotation = ups->brush_rotation_sec = atan2f(dx, dy) + (float)M_PI; + return true; + } + else { + return false; + } + } + float dpos[2]; sub_v2_v2v2(dpos, ups->last_rake, mouse_pos); @@ -1362,6 +1393,43 @@ bool paint_calculate_rake_rotation(UnifiedPaintSettings *ups, return ok; } +static bool sculpt_boundary_flags_ensure(Object *ob, PBVH *pbvh, int totvert) +{ + SculptSession *ss = ob->sculpt; + bool ret = false; + + if (!ss->attrs.boundary_flags) { + SculptAttributeParams params = {0}; + + params.nointerp = true; + // params.nocopy = true; + + ss->attrs.boundary_flags = sculpt_attribute_ensure_ex(ob, + ATTR_DOMAIN_POINT, + CD_PROP_INT32, + SCULPT_ATTRIBUTE_NAME(boundary_flags), + ¶ms, + BKE_pbvh_type(pbvh)); + + for (int i = 0; i < totvert; i++) { + PBVHVertRef vertex = BKE_pbvh_index_to_vertex(pbvh, i); + BKE_sculpt_boundary_flag_update(ss, vertex); + } + + ret = true; + } + + BKE_pbvh_set_boundary_flags(pbvh, reinterpret_cast(ss->attrs.boundary_flags->data)); + + return ret; +} + +bool BKE_sculptsession_boundary_flags_ensure(Object *ob) +{ + return sculpt_boundary_flags_ensure( + ob, ob->sculpt->pbvh, BKE_sculptsession_vertex_count(ob->sculpt)); +} + void BKE_sculptsession_free_deformMats(SculptSession *ss) { MEM_SAFE_FREE(ss->orig_cos); @@ -1403,17 +1471,16 @@ static void sculptsession_bm_to_me_update_data_only(Object *ob, bool reorder) if (ss->bm) { if (ob->data) { - BMIter iter; - BMFace *efa; - BM_ITER_MESH (efa, &iter, ss->bm, BM_FACES_OF_MESH) { - BM_elem_flag_set(efa, BM_ELEM_SMOOTH, ss->bm_smooth_shading); - } - if (reorder) { - BM_log_mesh_elems_reorder(ss->bm, ss->bm_log); - } - BMeshToMeshParams params{}; - params.calc_object_remap = false; - BM_mesh_bm_to_me(nullptr, ss->bm, static_cast(ob->data), ¶ms); + // Mesh *me = BKE_object_get_original_mesh(ob); + + BMeshToMeshParams params = {0}; + params.update_shapekey_indices = true; + + params.cd_mask_extra.vmask = CD_MASK_MESH_ID | CD_MASK_DYNTOPO_VERT; + params.cd_mask_extra.emask = CD_MASK_MESH_ID; + params.cd_mask_extra.pmask = CD_MASK_MESH_ID; + + BM_mesh_bm_to_me(nullptr, nullptr, ss->bm, static_cast(ob->data), ¶ms); } } } @@ -1438,13 +1505,31 @@ static void sculptsession_free_pbvh(Object *object) } if (ss->pbvh) { +#ifdef WITH_PBVH_CACHE + if (ss->needs_pbvh_rebuild) { + if (ss->pmap) { + BKE_pbvh_pmap_release(ss->pmap); + ss->pmap = nullptr; + } + + BKE_pbvh_cache_remove(ss->pbvh); + BKE_pbvh_free(ss->pbvh); + } + else { + BKE_pbvh_set_cached(object, ss->pbvh); + } +#else + if (ss->pmap) { + BKE_pbvh_pmap_release(ss->pmap); + ss->pmap = nullptr; + } + BKE_pbvh_free(ss->pbvh); +#endif + ss->needs_pbvh_rebuild = false; ss->pbvh = nullptr; } - MEM_SAFE_FREE(ss->pmap); - MEM_SAFE_FREE(ss->pmap_mem); - MEM_SAFE_FREE(ss->epmap); MEM_SAFE_FREE(ss->epmap_mem); @@ -1455,6 +1540,7 @@ static void sculptsession_free_pbvh(Object *object) ss->preview_vert_count = 0; MEM_SAFE_FREE(ss->vertex_info.boundary); + MEM_SAFE_FREE(ss->vertex_info.symmetrize_map); MEM_SAFE_FREE(ss->fake_neighbors.fake_neighbor_index); } @@ -1486,28 +1572,44 @@ void BKE_sculptsession_free(Object *ob) if (ob && ob->sculpt) { SculptSession *ss = ob->sculpt; + if (ss->msculptverts) { + ss->msculptverts = nullptr; + } + + if (ss->bm_idmap) { + BM_idmap_destroy(ss->bm_idmap); + ss->bm_idmap = nullptr; + } + + if (ss->bm_log && BM_log_free(ss->bm_log, true)) { + ss->bm_log = nullptr; + } + + /*try to save current mesh*/ BKE_sculpt_attribute_destroy_temporary_all(ob); if (ss->bm) { BKE_sculptsession_bm_to_me(ob, true); - BM_mesh_free(ss->bm); + ss->bm = nullptr; + // BM_mesh_free(ss->bm); + } + + CustomData_free(&ss->temp_vdata, ss->temp_vdata_elems); + CustomData_free(&ss->temp_pdata, ss->temp_pdata_elems); + + if (!ss->pbvh) { + BKE_pbvh_pmap_release(ss->pmap); + ss->pmap = nullptr; } sculptsession_free_pbvh(ob); - MEM_SAFE_FREE(ss->pmap); - MEM_SAFE_FREE(ss->pmap_mem); - MEM_SAFE_FREE(ss->epmap); MEM_SAFE_FREE(ss->epmap_mem); MEM_SAFE_FREE(ss->vemap); MEM_SAFE_FREE(ss->vemap_mem); - if (ss->bm_log) { - BM_log_free(ss->bm_log); - } - if (ss->tex_pool) { BKE_image_pool_free(ss->tex_pool); } @@ -1644,6 +1746,46 @@ static bool sculpt_modifiers_active(Scene *scene, Sculpt *sd, Object *ob) return false; } +void BKE_sculpt_ensure_idmap(Object *ob) +{ + if (!ob->sculpt->bm_idmap) { + ob->sculpt->bm_idmap = BM_idmap_new(ob->sculpt->bm, BM_VERT | BM_EDGE | BM_FACE); + BM_idmap_check_ids(ob->sculpt->bm_idmap); + + BKE_sculptsession_update_attr_refs(ob); + } +} + +char BKE_get_fset_boundary_symflag(Object *object) +{ + const Mesh *mesh = BKE_mesh_from_object(object); + return mesh->flag & ME_SCULPT_MIRROR_FSET_BOUNDARIES ? mesh->symmetry : 0; +} + +void BKE_sculptsession_ignore_uvs_set(Object *ob, bool value) +{ + ob->sculpt->ignore_uvs = value; + + if (ob->sculpt->pbvh) { + BKE_pbvh_ignore_uvs_set(ob->sculpt->pbvh, value); + } +} + +static void sculpt_check_face_areas(Object *ob, PBVH *pbvh) +{ + SculptSession *ss = ob->sculpt; + + if (!ss->attrs.face_areas) { + SculptAttributeParams params = {0}; + ss->attrs.face_areas = sculpt_attribute_ensure_ex(ob, + ATTR_DOMAIN_FACE, + CD_PROP_FLOAT2, + SCULPT_ATTRIBUTE_NAME(face_areas), + ¶ms, + BKE_pbvh_type(pbvh)); + } +} + /* Helper function to keep persistent base attribute references up to * date. This is a bit more tricky since they persist across strokes. */ @@ -1674,14 +1816,24 @@ static void sculpt_update_object( ss->depsgraph = depsgraph; + ss->bm_smooth_shading = scene->toolsettings->sculpt->flags & SCULPT_DYNTOPO_SMOOTH_SHADING; + ss->ignore_uvs = me->flag & ME_SCULPT_IGNORE_UVS; + ss->deform_modifiers_active = sculpt_modifiers_active(scene, sd, ob); ss->building_vp_handle = false; ss->scene = scene; + if (scene->toolsettings) { + ss->save_temp_layers = scene->toolsettings->save_temp_layers; + } + ss->boundary_symmetry = (int)BKE_get_fset_boundary_symflag(ob); ss->shapekey_active = (mmd == nullptr) ? BKE_keyblock_from_object(ob) : nullptr; + ss->material_index = (int *)CustomData_get_layer_named( + &me->pdata, CD_PROP_INT32, "material_index"); + /* NOTE: Weight pPaint require mesh info for loop lookup, but it never uses multires code path, * so no extra checks is needed here. */ if (mmd) { @@ -1691,20 +1843,33 @@ static void sculpt_update_object( ss->totvert = me_eval->totvert; ss->totpoly = me_eval->totpoly; ss->totfaces = me->totpoly; + ss->totloops = me->totloop; + ss->totedges = me->totedge; /* These are assigned to the base mesh in Multires. This is needed because Face Sets operators * and tools use the Face Sets data from the base mesh when Multires is active. */ ss->vert_positions = BKE_mesh_vert_positions_for_write(me); ss->mpoly = BKE_mesh_polys(me); + ss->medge = BKE_mesh_edges(me); ss->mloop = BKE_mesh_loops(me); } else { ss->totvert = me->totvert; ss->totpoly = me->totpoly; ss->totfaces = me->totpoly; + ss->totloops = me->totloop; + ss->totedges = me->totedge; ss->vert_positions = BKE_mesh_vert_positions_for_write(me); ss->mpoly = BKE_mesh_polys(me); + ss->medge = BKE_mesh_edges(me); ss->mloop = BKE_mesh_loops(me); + ss->vdata = &me->vdata; + ss->pdata = &me->pdata; + ss->edata = &me->edata; + ss->ldata = &me->ldata; + + ss->sharp_edge = (bool *)CustomData_get_layer_named_for_write( + &me->edata, CD_PROP_BOOL, "sharp_edge", me->totedge); ss->multires.active = false; ss->multires.modifier = nullptr; ss->multires.level = 0; @@ -1747,12 +1912,28 @@ static void sculpt_update_object( &me->pdata, CD_PROP_BOOL, ".hide_poly", me->totpoly); ss->subdiv_ccg = me_eval->runtime->subdiv_ccg; + ss->fast_draw = (scene->toolsettings->sculpt->flags & SCULPT_FAST_DRAW) != 0; PBVH *pbvh = BKE_sculpt_object_pbvh_ensure(depsgraph, ob); + sculpt_check_face_areas(ob, pbvh); + + sculpt_boundary_flags_ensure(ob, pbvh, BKE_sculptsession_vertex_count(ss)); + + BKE_pbvh_update_active_vcol(pbvh, me); + + if (BKE_pbvh_type(pbvh) == PBVH_FACES) { + ss->vert_normals = BKE_pbvh_get_vert_normals(ss->pbvh); + } + else { + ss->vert_normals = nullptr; + } + BLI_assert(pbvh == ss->pbvh); UNUSED_VARS_NDEBUG(pbvh); - BKE_pbvh_subdiv_cgg_set(ss->pbvh, ss->subdiv_ccg); + if (ss->subdiv_ccg) { + BKE_pbvh_subdiv_ccg_set(ss->pbvh, ss->subdiv_ccg); + } BKE_pbvh_face_sets_set(ss->pbvh, ss->face_sets); BKE_pbvh_update_hide_attributes_from_mesh(ss->pbvh); @@ -1762,16 +1943,20 @@ static void sculpt_update_object( sculpt_update_persistent_base(ob); if (need_pmap && ob->type == OB_MESH && !ss->pmap) { - BKE_mesh_vert_poly_map_create(&ss->pmap, - &ss->pmap_mem, - BKE_mesh_polys(me), - BKE_mesh_loops(me), - me->totvert, - me->totpoly, - me->totloop); + if (!ss->pmap && ss->pbvh) { + ss->pmap = BKE_pbvh_get_pmap(ss->pbvh); - if (ss->pbvh) { - BKE_pbvh_pmap_set(ss->pbvh, ss->pmap); + if (ss->pmap) { + BKE_pbvh_pmap_aquire(ss->pmap); + } + } + + if (!ss->pmap) { + ss->pmap = BKE_pbvh_make_pmap(me); + + if (ss->pbvh) { + BKE_pbvh_set_pmap(ss->pbvh, ss->pmap); + } } } @@ -1813,6 +1998,18 @@ static void sculpt_update_object( for (a = 0; a < me->totvert; a++) { invert_m3(ss->deform_imats[a]); + + float *co = ss->deform_cos[a]; + float ff = dot_v3v3(co, co); + if (isnan(ff) || !isfinite(ff)) { + printf("%s: nan1! %.4f %.4f %.4f\n", __func__, co[0], co[1], co[2]); + } + + ff = determinant_m3_array(ss->deform_imats[a]); + + if (isnan(ff) || !isfinite(ff)) { + printf("%s: nan2!\n", __func__); + } } } } @@ -1845,6 +2042,129 @@ static void sculpt_update_object( } } + int totvert = 0; + + switch (BKE_pbvh_type(pbvh)) { + case PBVH_FACES: + totvert = me->totvert; + break; + case PBVH_BMESH: + totvert = ss->bm ? ss->bm->totvert : me->totvert; + break; + case PBVH_GRIDS: + totvert = BKE_pbvh_get_grid_num_verts(ss->pbvh); + break; + } + + BKE_sculptsession_check_sculptverts(ob, pbvh, totvert); + + if (ss->bm && me->key && ob->shapenr != ss->bm->shapenr) { + KeyBlock *actkey = static_cast(BLI_findlink(&me->key->block, ss->bm->shapenr - 1)); + KeyBlock *newkey = static_cast(BLI_findlink(&me->key->block, ob->shapenr - 1)); + + bool updatePBVH = false; + + if (!actkey) { + printf("%s: failed to find active shapekey\n", __func__); + if (!ss->bm->shapenr || !CustomData_has_layer(&ss->bm->vdata, CD_SHAPEKEY)) { + printf("allocating shapekeys. . .\n"); + + // need to allocate customdata for keys + for (KeyBlock *key = (KeyBlock *)me->key->block.first; key; key = key->next) { + + int idx = CustomData_get_named_layer_index(&ss->bm->vdata, CD_SHAPEKEY, key->name); + + if (idx == -1) { + BM_data_layer_add_named(ss->bm, &ss->bm->vdata, CD_SHAPEKEY, key->name); + BKE_sculptsession_update_attr_refs(ob); + + idx = CustomData_get_named_layer_index(&ss->bm->vdata, CD_SHAPEKEY, key->name); + ss->bm->vdata.layers[idx].uid = key->uid; + } + + int cd_shapeco = ss->bm->vdata.layers[idx].offset; + BMVert *v; + BMIter iter; + + BM_ITER_MESH (v, &iter, ss->bm, BM_VERTS_OF_MESH) { + float *keyco = (float *)BM_ELEM_CD_GET_VOID_P(v, cd_shapeco); + + copy_v3_v3(keyco, v->co); + } + } + } + + updatePBVH = true; + ss->bm->shapenr = ob->shapenr; + } + + if (!newkey) { + printf("%s: failed to find new active shapekey\n", __func__); + } + + if (actkey && newkey) { + int cd_co1 = CustomData_get_named_layer_index(&ss->bm->vdata, CD_SHAPEKEY, actkey->name); + int cd_co2 = CustomData_get_named_layer_index(&ss->bm->vdata, CD_SHAPEKEY, newkey->name); + + BMVert *v; + BMIter iter; + + if (cd_co1 == -1) { // non-recoverable error + printf("%s: failed to find active shapekey in customdata.\n", __func__); + return; + } + else if (cd_co2 == -1) { + printf("%s: failed to find new shapekey in customdata; allocating . . .\n", __func__); + + BM_data_layer_add_named(ss->bm, &ss->bm->vdata, CD_SHAPEKEY, newkey->name); + int idx = CustomData_get_named_layer_index(&ss->bm->vdata, CD_SHAPEKEY, newkey->name); + + int cd_co = ss->bm->vdata.layers[idx].offset; + ss->bm->vdata.layers[idx].uid = newkey->uid; + + BKE_sculptsession_update_attr_refs(ob); + + BM_ITER_MESH (v, &iter, ss->bm, BM_VERTS_OF_MESH) { + float *keyco = (float *)BM_ELEM_CD_GET_VOID_P(v, cd_co); + copy_v3_v3(keyco, v->co); + } + + cd_co2 = idx; + } + + cd_co1 = ss->bm->vdata.layers[cd_co1].offset; + cd_co2 = ss->bm->vdata.layers[cd_co2].offset; + + BM_ITER_MESH (v, &iter, ss->bm, BM_VERTS_OF_MESH) { + float *co1 = (float *)BM_ELEM_CD_GET_VOID_P(v, cd_co1); + float *co2 = (float *)BM_ELEM_CD_GET_VOID_P(v, cd_co2); + + copy_v3_v3(co1, v->co); + copy_v3_v3(v->co, co2); + } + + ss->bm->shapenr = ob->shapenr; + + updatePBVH = true; + } + + if (updatePBVH && ss->pbvh) { + PBVHNode **nodes; + int totnode; + BKE_pbvh_get_nodes(ss->pbvh, PBVH_Leaf, &nodes, &totnode); + + for (int i = 0; i < totnode; i++) { + BKE_pbvh_node_mark_update(nodes[i]); + BKE_pbvh_vert_tag_update_normal_tri_area(nodes[i]); + } + } + /* We could be more precise when we have access to the active tool. */ + const bool use_paint_slots = (ob->mode & OB_MODE_SCULPT) != 0; + if (use_paint_slots) { + BKE_texpaint_slots_refresh_object(scene, ob); + } + } + if (is_paint_tool) { /* * We should rebuild the PBVH_pixels when painting canvas changes. @@ -1860,16 +2180,36 @@ static void sculpt_update_object( ss->last_paint_canvas_key = paint_canvas_key; BKE_pbvh_mark_rebuild_pixels(ss->pbvh); } - else { - MEM_freeN(paint_canvas_key); + + /* + * We should rebuild the PBVH_pixels when painting canvas changes. + * + * The relevant changes are stored/encoded in the paint canvas key. + * These include the active uv map, and resolutions. + */ + if (U.experimental.use_sculpt_texture_paint && ss->pbvh) { + char *paint_canvas_key = BKE_paint_canvas_key_get(&scene->toolsettings->paint_mode, ob); + if (ss->last_paint_canvas_key == nullptr || + !STREQ(paint_canvas_key, ss->last_paint_canvas_key)) { + MEM_SAFE_FREE(ss->last_paint_canvas_key); + ss->last_paint_canvas_key = paint_canvas_key; + BKE_pbvh_mark_rebuild_pixels(ss->pbvh); + } + else { + MEM_freeN(paint_canvas_key); + } + } + + /* We could be more precise when we have access to the active tool. */ + const bool use_paint_slots = (ob->mode & OB_MODE_SCULPT) != 0; + if (use_paint_slots) { + BKE_texpaint_slots_refresh_object(scene, ob); } } + } - /* We could be more precise when we have access to the active tool. */ - const bool use_paint_slots = (ob->mode & OB_MODE_SCULPT) != 0; - if (use_paint_slots) { - BKE_texpaint_slots_refresh_object(scene, ob); - } + if (ss->pbvh) { + BKE_pbvh_set_sculpt_verts(ss->pbvh, ss->msculptverts); } } @@ -1878,8 +2218,8 @@ void BKE_sculpt_update_object_before_eval(Object *ob_eval) /* Update before mesh evaluation in the dependency graph. */ SculptSession *ss = ob_eval->sculpt; - if (ss && ss->building_vp_handle == false) { - if (!ss->cache && !ss->filter_cache && !ss->expand_cache) { + if (ss && (ss->building_vp_handle == false || ss->needs_pbvh_rebuild)) { + if (ss->needs_pbvh_rebuild || (!ss->cache && !ss->filter_cache && !ss->expand_cache)) { /* We free pbvh on changes, except in the middle of drawing a stroke * since it can't deal with changing PVBH node organization, we hope * topology does not change in the meantime .. weak. */ @@ -1890,7 +2230,7 @@ void BKE_sculpt_update_object_before_eval(Object *ob_eval) /* In vertex/weight paint, force maps to be rebuilt. */ BKE_sculptsession_free_vwpaint_data(ob_eval->sculpt); } - else { + else if (ss->pbvh) { PBVHNode **nodes; int n, totnode; @@ -1900,7 +2240,7 @@ void BKE_sculpt_update_object_before_eval(Object *ob_eval) BKE_pbvh_node_mark_update(nodes[n]); } - MEM_freeN(nodes); + MEM_SAFE_FREE(nodes); } } } @@ -1910,8 +2250,10 @@ void BKE_sculpt_update_object_after_eval(Depsgraph *depsgraph, Object *ob_eval) /* Update after mesh evaluation in the dependency graph, to rebuild PBVH or * other data when modifiers change the mesh. */ Object *ob_orig = DEG_get_original_object(ob_eval); + Mesh *me_orig = BKE_object_get_original_mesh(ob_orig); sculpt_update_object(depsgraph, ob_orig, ob_eval, false, false); + BKE_sculptsession_sync_attributes(ob_orig, me_orig); } void BKE_sculpt_color_layer_create_if_needed(Object *object) @@ -1938,22 +2280,51 @@ void BKE_sculpt_color_layer_create_if_needed(Object *object) if (object->sculpt && object->sculpt->pbvh) { BKE_pbvh_update_active_vcol(object->sculpt->pbvh, orig_me); } + + BKE_sculptsession_update_attr_refs(object); } void BKE_sculpt_update_object_for_edit( Depsgraph *depsgraph, Object *ob_orig, bool need_pmap, bool /*need_mask*/, bool is_paint_tool) { - BLI_assert(ob_orig == DEG_get_original_object(ob_orig)); - + /* Update from sculpt operators and undo, to update sculpt session + * and PBVH after edits. */ + Scene *scene_eval = DEG_get_evaluated_scene(depsgraph); Object *ob_eval = DEG_get_evaluated_object(depsgraph, ob_orig); sculpt_update_object(depsgraph, ob_orig, ob_eval, need_pmap, is_paint_tool); } -int *BKE_sculpt_face_sets_ensure(Mesh *mesh) +int *BKE_sculpt_face_sets_ensure(Object *ob) { + Mesh *mesh = BKE_object_get_original_mesh(ob); + SculptSession *ss = ob->sculpt; + + if (ss && ss->bm) { + if (CustomData_get_named_layer_index(&ss->bm->pdata, CD_PROP_INT32, ".sculpt_face_set") != + -1) { + return nullptr; + } + + BM_data_layer_add_named(ss->bm, &ss->bm->pdata, CD_PROP_INT32, ".sculpt_face_set"); + + const int cd_faceset_offset = CustomData_get_offset_named( + &ss->bm->pdata, CD_PROP_INT32, ".sculpt_face_set"); + + BMIter iter; + BMFace *f; + BM_ITER_MESH (f, &iter, ss->bm, BM_FACES_OF_MESH) { + BM_ELEM_CD_SET_INT(f, cd_faceset_offset, 1); + } + + BKE_sculptsession_update_attr_refs(ob); + + return nullptr; + } + using namespace blender; using namespace blender::bke; + MutableAttributeAccessor attributes = mesh->attributes_for_write(); if (!attributes.contains(".sculpt_face_set")) { SpanAttributeWriter face_sets = attributes.lookup_or_add_for_write_only_span( @@ -1967,15 +2338,23 @@ int *BKE_sculpt_face_sets_ensure(Mesh *mesh) &mesh->pdata, CD_PROP_INT32, ".sculpt_face_set", mesh->totpoly)); } -bool *BKE_sculpt_hide_poly_ensure(Mesh *mesh) +bool *BKE_sculpt_hide_poly_ensure(Object *ob) { + Mesh *mesh = BKE_object_get_original_mesh(ob); + bool *hide_poly = static_cast(CustomData_get_layer_named_for_write( &mesh->pdata, CD_PROP_BOOL, ".hide_poly", mesh->totpoly)); if (hide_poly != nullptr) { return hide_poly; } - return static_cast(CustomData_add_layer_named( - &mesh->pdata, CD_PROP_BOOL, CD_SET_DEFAULT, nullptr, mesh->totpoly, ".hide_poly")); + + SculptAttributeParams params = {0}; + params.permanent = true; + + ob->sculpt->attrs.hide_poly = BKE_sculpt_attribute_ensure( + ob, ATTR_DOMAIN_FACE, CD_PROP_BOOL, ".hide_poly", ¶ms); + + return ob->sculpt->hide_poly = static_cast(ob->sculpt->attrs.hide_poly->data); } int BKE_sculpt_mask_layers_ensure(Depsgraph *depsgraph, @@ -2055,6 +2434,10 @@ int BKE_sculpt_mask_layers_ensure(Depsgraph *depsgraph, ret |= SCULPT_MASK_LAYER_CALC_VERT; } + if (ob->sculpt) { + BKE_sculptsession_update_attr_refs(ob); + } + return ret; } @@ -2064,11 +2447,30 @@ void BKE_sculpt_toolsettings_data_ensure(Scene *scene) Sculpt *sd = scene->toolsettings->sculpt; if (!sd->detail_size) { - sd->detail_size = 12; + sd->detail_size = 8.0f; } + + if (!sd->dyntopo_radius_scale) { + sd->dyntopo_radius_scale = 1.0f; + } + + // we check these flags here in case versioning code fails + if (!sd->detail_range || !sd->dyntopo_spacing) { + sd->flags |= SCULPT_DYNTOPO_CLEANUP | SCULPT_DYNTOPO_ENABLED; + } + + if (!sd->detail_range) { + sd->detail_range = 0.4f; + } + if (!sd->detail_percent) { sd->detail_percent = 25; } + + if (!sd->dyntopo_spacing) { + sd->dyntopo_spacing = 35; + } + if (sd->constant_detail == 0.0f) { sd->constant_detail = 3.0f; } @@ -2145,8 +2547,8 @@ void BKE_sculpt_sync_face_visibility_to_grids(Mesh *mesh, SubdivCCG *subdiv_ccg) const bool is_hidden = hide_poly_span[face_index]; /* Avoid creating and modifying the grid_hidden bitmap if the base mesh face is visible and - * there is not bitmap for the grid. This is because missing grid_hidden implies grid is fully - * visible. */ + * there is not bitmap for the grid. This is because missing grid_hidden implies grid is + * fully visible. */ if (is_hidden) { BKE_subdiv_ccg_grid_hidden_ensure(subdiv_ccg, i); } @@ -2158,53 +2560,136 @@ void BKE_sculpt_sync_face_visibility_to_grids(Mesh *mesh, SubdivCCG *subdiv_ccg) } } -static PBVH *build_pbvh_for_dynamic_topology(Object *ob) +static PBVH *build_pbvh_for_dynamic_topology(Object *ob, bool update_sculptverts) { - PBVH *pbvh = ob->sculpt->pbvh = BKE_pbvh_new(PBVH_BMESH); + Mesh *me = BKE_object_get_original_mesh(ob); - sculptsession_bmesh_add_layers(ob); +#ifdef WITH_PBVH_CACHE + PBVH *pbvh = BKE_pbvh_get_or_free_cached(ob, me, PBVH_BMESH); +#else + PBVH *pbvh = nullptr; +#endif + + if (!pbvh) { + pbvh = ob->sculpt->pbvh = BKE_pbvh_new(PBVH_BMESH); + BKE_pbvh_set_bmesh(pbvh, ob->sculpt->bm); + + BM_mesh_elem_table_ensure(ob->sculpt->bm, BM_VERT | BM_EDGE | BM_FACE); + + BKE_sculptsession_update_attr_refs(ob); + sculpt_boundary_flags_ensure(ob, pbvh, ob->sculpt->bm->totvert); + sculpt_check_face_areas(ob, pbvh); + + BKE_sculpt_ensure_idmap(ob); + + sculptsession_bmesh_add_layers(ob); + BKE_pbvh_build_bmesh(pbvh, + BKE_object_get_original_mesh(ob), + ob->sculpt->bm, + ob->sculpt->bm_smooth_shading, + ob->sculpt->bm_log, + ob->sculpt->bm_idmap, + ob->sculpt->attrs.dyntopo_node_id_vertex->bmesh_cd_offset, + ob->sculpt->attrs.dyntopo_node_id_face->bmesh_cd_offset, + ob->sculpt->cd_sculpt_vert, + ob->sculpt->attrs.face_areas->bmesh_cd_offset, + ob->sculpt->attrs.boundary_flags->bmesh_cd_offset, + ob->sculpt->fast_draw, + update_sculptverts); + } + else { + BMesh *bm = BKE_pbvh_get_bmesh(pbvh); + + if (bm && bm != ob->sculpt->bm) { + printf("%s: ss->bm != bm!\n", __func__); + + ob->sculpt->bm = bm; + + if (ob->sculpt->bm_log) { + BM_log_set_bm(bm, ob->sculpt->bm_log); + } + } + else if (!bm) { + BKE_pbvh_set_bmesh(pbvh, ob->sculpt->bm); + } + } + + if (ob->sculpt->bm_log) { + BKE_pbvh_set_bm_log(pbvh, ob->sculpt->bm_log); + } + + BKE_pbvh_set_symmetry(pbvh, 0, (int)BKE_get_fset_boundary_symflag(ob)); + +#ifdef WITH_PBVH_CACHE + BKE_pbvh_set_cached(ob, pbvh); +#endif - BKE_pbvh_build_bmesh(pbvh, - ob->sculpt->bm, - ob->sculpt->bm_smooth_shading, - ob->sculpt->bm_log, - ob->sculpt->attrs.dyntopo_node_id_vertex->bmesh_cd_offset, - ob->sculpt->attrs.dyntopo_node_id_face->bmesh_cd_offset); return pbvh; } static PBVH *build_pbvh_from_regular_mesh(Object *ob, Mesh *me_eval_deform, bool respect_hide) { + SculptSession *ss = ob->sculpt; Mesh *me = BKE_object_get_original_mesh(ob); const int looptris_num = poly_to_tri_count(me->totpoly, me->totloop); - PBVH *pbvh = BKE_pbvh_new(PBVH_FACES); - BKE_pbvh_respect_hide_set(pbvh, respect_hide); + +#ifdef WITH_PBVH_CACHE + PBVH *pbvh = BKE_pbvh_get_or_free_cached(ob, me, PBVH_FACES); +#else + PBVH *pbvh = nullptr; +#endif MutableSpan positions = me->vert_positions_for_write(); const Span polys = me->polys(); const Span loops = me->loops(); - MLoopTri *looptri = static_cast( - MEM_malloc_arrayN(looptris_num, sizeof(*looptri), __func__)); + if (!ss->pmap && pbvh) { + ss->pmap = BKE_pbvh_get_pmap(pbvh); + BKE_pbvh_pmap_aquire(ss->pmap); + } + else if (!ss->pmap) { + ss->pmap = BKE_pbvh_make_pmap(me); + } - BKE_mesh_recalc_looptri(loops.data(), - polys.data(), - reinterpret_cast(positions.data()), - me->totloop, - me->totpoly, - looptri); + if (!pbvh) { + pbvh = ob->sculpt->pbvh = BKE_pbvh_new(PBVH_FACES); - BKE_pbvh_build_mesh(pbvh, - me, - polys.data(), - loops.data(), - reinterpret_cast(positions.data()), - me->totvert, - &me->vdata, - &me->ldata, - &me->pdata, - looptri, - looptris_num); + BKE_sculptsession_update_attr_refs(ob); + sculpt_check_face_areas(ob, pbvh); + BKE_pbvh_respect_hide_set(pbvh, respect_hide); + + float(*vert_cos)[3] = BKE_mesh_vert_positions_for_write(me); + const Span polys = me->polys(); + const Span loops = me->loops(); + + MLoopTri *looptri = static_cast( + MEM_malloc_arrayN(looptris_num, sizeof(*looptri), __func__)); + + BKE_mesh_recalc_looptri( + loops.data(), polys.data(), vert_cos, me->totloop, me->totpoly, looptri); + BKE_sculptsession_check_sculptverts(ob, pbvh, me->totvert); + + BKE_pbvh_build_mesh(pbvh, + me, + me->polys().data(), + me->loops().data(), + vert_cos, + ss->msculptverts, + me->totvert, + &me->vdata, + &me->ldata, + &me->pdata, + looptri, + looptris_num, + ss->fast_draw, + (float *)ss->attrs.face_areas->data, + ss->pmap); + } + + BKE_pbvh_set_pmap(pbvh, ss->pmap); + BKE_sculptsession_check_sculptverts(ob, pbvh, me->totvert); + + BKE_pbvh_set_sculpt_verts(pbvh, ss->msculptverts); const bool is_deformed = check_sculpt_object_deformed(ob, true); if (is_deformed && me_eval_deform != nullptr) { @@ -2214,41 +2699,173 @@ static PBVH *build_pbvh_from_regular_mesh(Object *ob, Mesh *me_eval_deform, bool MEM_freeN(v_cos); } +#ifdef WITH_PBVH_CACHE + BKE_pbvh_set_cached(ob, pbvh); +#endif + return pbvh; } static PBVH *build_pbvh_from_ccg(Object *ob, SubdivCCG *subdiv_ccg, bool respect_hide) { + SculptSession *ss = ob->sculpt; + CCGKey key; BKE_subdiv_ccg_key_top_level(&key, subdiv_ccg); - PBVH *pbvh = BKE_pbvh_new(PBVH_GRIDS); - BKE_pbvh_respect_hide_set(pbvh, respect_hide); Mesh *base_mesh = BKE_mesh_from_object(ob); +#ifdef WITH_PBVH_CACHE + PBVH *pbvh = BKE_pbvh_get_or_free_cached(ob, base_mesh, PBVH_GRIDS); +#else + PBVH *pbvh = nullptr; +#endif + + int totgridfaces = base_mesh->totloop * (key.grid_size - 1) * (key.grid_size - 1); BKE_sculpt_sync_face_visibility_to_grids(base_mesh, subdiv_ccg); - BKE_pbvh_build_grids(pbvh, - subdiv_ccg->grids, - subdiv_ccg->num_grids, - &key, - (void **)subdiv_ccg->grid_faces, - subdiv_ccg->grid_flag_mats, - subdiv_ccg->grid_hidden, - base_mesh, - subdiv_ccg); + if (!pbvh) { + pbvh = ob->sculpt->pbvh = BKE_pbvh_new(PBVH_GRIDS); + + BKE_sculptsession_update_attr_refs(ob); + BKE_pbvh_respect_hide_set(pbvh, respect_hide); + sculpt_check_face_areas(ob, pbvh); + + BKE_pbvh_build_grids(pbvh, + subdiv_ccg->grids, + subdiv_ccg->num_grids, + &key, + (void **)subdiv_ccg->grid_faces, + subdiv_ccg->grid_flag_mats, + subdiv_ccg->grid_hidden, + ob->sculpt->fast_draw, + (float *)ss->attrs.face_areas->data, + base_mesh, + subdiv_ccg); + } + else { + BKE_pbvh_subdiv_ccg_set(pbvh, subdiv_ccg); + } + + BKE_pbvh_respect_hide_set(pbvh, respect_hide); + BKE_pbvh_set_sculpt_verts(pbvh, ss->msculptverts); + + CustomData_reset(&ob->sculpt->temp_vdata); + CustomData_reset(&ob->sculpt->temp_pdata); + + ss->temp_vdata_elems = BKE_pbvh_get_grid_num_verts(pbvh); + ss->temp_pdata_elems = ss->totfaces; + + if (!ss->pmap) { + ss->pmap = BKE_pbvh_make_pmap(BKE_object_get_original_mesh(ob)); + } + + BKE_pbvh_set_pmap(pbvh, ss->pmap); + + BKE_sculptsession_check_sculptverts(ob, pbvh, BKE_pbvh_get_grid_num_verts(pbvh)); + +#ifdef WITH_PBVH_CACHE + BKE_pbvh_set_cached(ob, pbvh); +#endif + return pbvh; } +extern "C" bool BKE_sculptsession_check_sculptverts(Object *ob, struct PBVH *pbvh, int totvert) +{ + SculptSession *ss = ob->sculpt; + + sculpt_boundary_flags_ensure(ob, pbvh, totvert); + + if (!ss->attrs.sculpt_vert || !ss->attrs.sculpt_vert->data) { + SculptAttributeParams params = {0}; + + ss->attrs.sculpt_vert = sculpt_attribute_ensure_ex( + ob, ATTR_DOMAIN_POINT, CD_DYNTOPO_VERT, "", ¶ms, BKE_pbvh_type(pbvh)); + } + + ss->msculptverts = static_cast(ss->attrs.sculpt_vert->data); + BKE_pbvh_set_sculpt_verts(pbvh, ss->msculptverts); + + return false; +} + +static void init_sculptvert_layer_faces(SculptSession *ss, PBVH *pbvh, int totvert) +{ + BKE_pbvh_set_sculpt_verts(pbvh, ss->msculptverts); + + MSculptVert *mv = ss->msculptverts; + const bool is_grid = BKE_pbvh_type(pbvh) == PBVH_GRIDS; + + for (int i = 0; i < totvert; i++, mv++) { + MV_ADD_FLAG(mv, SCULPTVERT_NEED_VALENCE | SCULPTVERT_NEED_DISK_SORT); + mv->stroke_id = -1; + + PBVHVertRef vertex = {i}; + + BKE_sculpt_boundary_flag_update(ss, vertex); + + copy_v3_v3(mv->origco, ss->vert_positions[i]); + + BKE_pbvh_update_vert_boundary_faces(static_cast(ss->attrs.boundary_flags->data), + ss->face_sets, + ss->hide_poly, + ss->vert_positions, + ss->medge, + ss->mloop, + ss->mpoly, + ss->msculptverts, + ss->pmap->pmap, + vertex, + ss->sharp_edge); + + // can't fully update boundary here, so still flag for update + BKE_sculpt_boundary_flag_update(ss, vertex); + } +} + +static void init_sculptvert_layer_grids(SculptSession *ss, PBVH *pbvh, int totvert) +{ + BKE_pbvh_set_sculpt_verts(pbvh, ss->msculptverts); + + MSculptVert *mv = ss->msculptverts; + + for (int i = 0; i < totvert; i++, mv++) { + MV_ADD_FLAG(mv, SCULPTVERT_NEED_VALENCE | SCULPTVERT_NEED_DISK_SORT); + mv->stroke_id = -1; + + PBVHVertRef vertex = {i}; + + BKE_sculpt_boundary_flag_update(ss, vertex); + BKE_pbvh_update_vert_boundary_grids(pbvh, ss->subdiv_ccg, vertex); + + // can't fully update boundary here, so still flag for update + BKE_sculpt_boundary_flag_update(ss, vertex); + } +} + +static void init_sculptvert_layer(SculptSession *ss, PBVH *pbvh, int totvert) +{ + if (BKE_pbvh_type(pbvh) == PBVH_FACES) { + init_sculptvert_layer_faces(ss, pbvh, totvert); + } + else if (BKE_pbvh_type(pbvh) == PBVH_GRIDS) { + init_sculptvert_layer_grids(ss, pbvh, totvert); + } +} + PBVH *BKE_sculpt_object_pbvh_ensure(Depsgraph *depsgraph, Object *ob) { if (ob == nullptr || ob->sculpt == nullptr) { return nullptr; } + Scene *scene = DEG_get_input_scene(depsgraph); const bool respect_hide = true; PBVH *pbvh = ob->sculpt->pbvh; if (pbvh != nullptr) { + SCULPT_update_flat_vcol_shading(ob, scene); + /* NOTE: It is possible that grids were re-allocated due to modifier * stack. Need to update those pointers. */ if (BKE_pbvh_type(pbvh) == PBVH_GRIDS) { @@ -2260,17 +2877,75 @@ PBVH *BKE_sculpt_object_pbvh_ensure(Depsgraph *depsgraph, Object *ob) } } + BKE_sculptsession_sync_attributes(ob, BKE_object_get_original_mesh(ob)); BKE_pbvh_update_active_vcol(pbvh, BKE_object_get_original_mesh(ob)); - BKE_pbvh_pmap_set(pbvh, ob->sculpt->pmap); return pbvh; } if (ob->sculpt->bm != nullptr) { /* Sculpting on a BMesh (dynamic-topology) gets a special PBVH. */ - pbvh = build_pbvh_for_dynamic_topology(ob); + pbvh = build_pbvh_for_dynamic_topology(ob, false); + + ob->sculpt->pbvh = pbvh; } else { +#if 1 // def WHEN_GLOBAL_UNDO_WORKS + /*detect if we are loading from an undo memfile step*/ + Mesh *mesh_orig = BKE_object_get_original_mesh(ob); + bool is_dyntopo = (mesh_orig->flag & ME_SCULPT_DYNAMIC_TOPOLOGY); + + if (is_dyntopo) { + /* check if the pbvh cache has a valid bmesh */ +# ifdef WITH_PBVH_CACHE + pbvh = BKE_pbvh_get_or_free_cached(ob, mesh_orig, PBVH_BMESH); +# else + pbvh = nullptr; +# endif + + BMesh *bm = nullptr; + + if (pbvh) { + bm = BKE_pbvh_get_bmesh(pbvh); + } + + if (!bm) { + bm = BKE_sculptsession_empty_bmesh_create(); + + BMeshFromMeshParams params = {0}; + params.calc_face_normal = true; + params.use_shapekey = true; + params.active_shapekey = ob->shapenr; + params.create_shapekey_layers = true; + params.ignore_id_layers = false; + params.copy_temp_cdlayers = true; + + params.cd_mask_extra.vmask = CD_MASK_DYNTOPO_VERT; + + BM_mesh_bm_from_me(nullptr, bm, mesh_orig, ¶ms); + } + + ob->sculpt->bm = bm; + + SCULPT_undo_ensure_bmlog(ob); + + /* Note build_pbvh_for_dynamic_topology respects the pbvh cache. */ + pbvh = build_pbvh_for_dynamic_topology(ob, true); + + BKE_sculptsession_update_attr_refs(ob); + } + else { + Object *object_eval = DEG_get_evaluated_object(depsgraph, ob); + Mesh *mesh_eval = static_cast(object_eval->data); + if (mesh_eval->runtime->subdiv_ccg != nullptr) { + pbvh = build_pbvh_from_ccg(ob, mesh_eval->runtime->subdiv_ccg, respect_hide); + } + else if (ob->type == OB_MESH) { + Mesh *me_eval_deform = object_eval->runtime.mesh_deform_eval; + pbvh = build_pbvh_from_regular_mesh(ob, me_eval_deform, respect_hide); + } + } +#else Object *object_eval = DEG_get_evaluated_object(depsgraph, ob); Mesh *mesh_eval = static_cast(object_eval->data); if (mesh_eval->runtime->subdiv_ccg != nullptr) { @@ -2278,14 +2953,27 @@ PBVH *BKE_sculpt_object_pbvh_ensure(Depsgraph *depsgraph, Object *ob) } else if (ob->type == OB_MESH) { Mesh *me_eval_deform = object_eval->runtime.mesh_deform_eval; + + BKE_sculptsession_check_sculptverts(ob, me_eval_deform->totvert); + pbvh = build_pbvh_from_regular_mesh(ob, me_eval_deform, respect_hide); } +#endif } - BKE_pbvh_pmap_set(pbvh, ob->sculpt->pmap); + if (!ob->sculpt->pmap) { + ob->sculpt->pmap = BKE_pbvh_make_pmap(BKE_object_get_original_mesh(ob)); + } + + BKE_pbvh_set_pmap(pbvh, ob->sculpt->pmap); ob->sculpt->pbvh = pbvh; sculpt_attribute_update_refs(ob); + + if (pbvh) { + SCULPT_update_flat_vcol_shading(ob, scene); + } + return pbvh; } @@ -2309,6 +2997,12 @@ bool BKE_sculptsession_use_pbvh_draw(const Object *ob, const RegionView3D *rv3d) return false; } +#if 0 + if (BKE_pbvh_type(ss->pbvh) == PBVH_GRIDS) { + return !(v3d && (v3d->shading.type > OB_SOLID)); + } +#endif + if (BKE_pbvh_type(ss->pbvh) == PBVH_FACES) { /* Regular mesh only draws from PBVH without modifiers and shape keys, or for * external engines that do not have access to the PBVH like Eevee does. */ @@ -2352,6 +3046,163 @@ int BKE_sculptsession_vertex_count(const SculptSession *ss) return 0; } +/** + Syncs customdata layers with internal bmesh, but ignores deleted layers. +*/ +void BKE_sculptsession_sync_attributes(struct Object *ob, struct Mesh *me) +{ + SculptSession *ss = ob->sculpt; + + if (!ss) { + return; + } + else if (!ss->bm) { + BKE_sculptsession_update_attr_refs(ob); + return; + } + + bool modified = false; + BMesh *bm = ss->bm; + + CustomData *cd1[4] = {&me->vdata, &me->edata, &me->ldata, &me->pdata}; + CustomData *cd2[4] = {&bm->vdata, &bm->edata, &bm->ldata, &bm->pdata}; + int badmask = CD_MASK_MLOOP | CD_MASK_MEDGE | CD_MASK_MPOLY | CD_MASK_ORIGINDEX | + CD_MASK_ORIGSPACE | CD_MASK_MFACE; + + for (int i = 0; i < 4; i++) { + Vector newlayers; + + CustomData *data1 = cd1[i]; + CustomData *data2 = cd2[i]; + + if (!data1->layers) { + modified |= data2->layers != nullptr; + continue; + } + + for (int j = 0; j < data1->totlayer; j++) { + CustomDataLayer *cl1 = data1->layers + j; + + if ((1 << cl1->type) & badmask) { + continue; + } + + int idx = CustomData_get_named_layer_index(data2, cl1->type, cl1->name); + if (idx < 0) { + newlayers.append(cl1); + } + } + + for (CustomDataLayer *layer : newlayers) { + BM_data_layer_add_named(bm, data2, layer->type, layer->name); + modified = true; + } + + /* sync various ids */ + for (int j = 0; j < data1->totlayer; j++) { + CustomDataLayer *cl1 = data1->layers + j; + + if ((1 << cl1->type) & badmask) { + continue; + } + + int idx = CustomData_get_named_layer_index(data2, cl1->type, cl1->name); + + if (idx == -1) { + continue; + } + + CustomDataLayer *cl2 = data2->layers + idx; + + cl2->anonymous_id = cl1->anonymous_id; + cl2->uid = cl1->uid; + } + + bool typemap[CD_NUMTYPES] = {0}; + + for (int j = 0; j < data1->totlayer; j++) { + CustomDataLayer *cl1 = data1->layers + j; + + if ((1 << cl1->type) & badmask) { + continue; + } + + if (typemap[cl1->type]) { + continue; + } + + typemap[cl1->type] = true; + + // find first layer + int baseidx = CustomData_get_layer_index(data2, cl1->type); + + if (baseidx < 0) { + modified |= true; + continue; + } + + CustomDataLayer *cl2 = data2->layers + baseidx; + + int idx = CustomData_get_named_layer_index(data2, cl1->type, cl1[cl1->active].name); + if (idx >= 0) { + modified |= idx - baseidx != cl2->active; + cl2->active = idx - baseidx; + } + + idx = CustomData_get_named_layer_index(data2, cl1->type, cl1[cl1->active_rnd].name); + if (idx >= 0) { + modified |= idx - baseidx != cl2->active_rnd; + cl2->active_rnd = idx - baseidx; + } + + idx = CustomData_get_named_layer_index(data2, cl1->type, cl1[cl1->active_mask].name); + if (idx >= 0) { + modified |= idx - baseidx != cl2->active_mask; + cl2->active_mask = idx - baseidx; + } + + idx = CustomData_get_named_layer_index(data2, cl1->type, cl1[cl1->active_clone].name); + if (idx >= 0) { + modified |= idx - baseidx != cl2->active_clone; + cl2->active_clone = idx - baseidx; + } + } + } + + if (modified && ss->bm) { + CustomData_regen_active_refs(&ss->bm->vdata); + CustomData_regen_active_refs(&ss->bm->edata); + CustomData_regen_active_refs(&ss->bm->ldata); + CustomData_regen_active_refs(&ss->bm->pdata); + } + + BKE_sculptsession_update_attr_refs(ob); +} + +BMesh *BKE_sculptsession_empty_bmesh_create() +{ + BMAllocTemplate allocsize; + + allocsize.totvert = 2048 * 1; + allocsize.totface = 2048 * 16; + allocsize.totloop = 4196 * 16; + allocsize.totedge = 2048 * 16; + + BMeshCreateParams params = {0}; + + params.use_toolflags = false; + params.create_unique_ids = true; + params.id_elem_mask = BM_VERT | BM_EDGE | BM_FACE; + params.id_map = true; + params.temporary_ids = false; + + params.no_reuse_ids = false; + + BMesh *bm = BM_mesh_create(&allocsize, ¶ms); + + return bm; +} + /** Returns pointer to a CustomData associated with a given domain, if * one exists. If not nullptr is returned (this may happen with e.g. * multires and ATTR_DOMAIN_POINT). @@ -2397,7 +3248,21 @@ static int sculpt_attr_elem_count_get(Object *ob, eAttrDomain domain) switch (domain) { case ATTR_DOMAIN_POINT: - return BKE_sculptsession_vertex_count(ss); + /* Cannot rely on prescence of ss->pbvh. */ + + if (ss->bm) { + return ss->bm->totvert; + } + else if (ss->subdiv_ccg) { + CCGKey key; + BKE_subdiv_ccg_key_top_level(&key, ss->subdiv_ccg); + return ss->subdiv_ccg->num_grids * key.grid_area; + } + else { + Mesh *me = BKE_object_get_original_mesh(ob); + + return me->totvert; + } break; case ATTR_DOMAIN_FACE: return ss->totfaces; @@ -2415,8 +3280,7 @@ static bool sculpt_attribute_create(SculptSession *ss, const char *name, SculptAttribute *out, const SculptAttributeParams *params, - PBVHType pbvhtype, - bool flat_array_for_bmesh) + PBVHType pbvhtype) { Mesh *me = BKE_object_get_original_mesh(ob); @@ -2429,10 +3293,10 @@ static bool sculpt_attribute_create(SculptSession *ss, BLI_strncpy_utf8(out->name, name, sizeof(out->name)); /* Force non-CustomData simple_array mode if not PBVH_FACES. */ - if (pbvhtype == PBVH_GRIDS || (pbvhtype == PBVH_BMESH && flat_array_for_bmesh)) { + if (pbvhtype == PBVH_GRIDS) { if (permanent) { printf( - "%s: error: tried to make permanent customdata in multires or bmesh mode; will make " + "%s: error: tried to make permanent customdata in multires; will make " "local " "array " "instead.\n", @@ -2463,9 +3327,9 @@ static bool sculpt_attribute_create(SculptSession *ss, return true; } - out->simple_array = false; + switch (pbvhtype) { + out->simple_array = false; - switch (BKE_pbvh_type(ss->pbvh)) { case PBVH_BMESH: { CustomData *cdata = nullptr; out->data_for_bmesh = true; @@ -2487,8 +3351,9 @@ static bool sculpt_attribute_create(SculptSession *ss, BM_data_layer_add_named(ss->bm, cdata, proptype, name); int index = CustomData_get_named_layer_index(cdata, proptype, name); - if (!permanent) { + if (!permanent && !ss->save_temp_layers) { cdata->layers[index].flag |= CD_FLAG_TEMPORARY | CD_FLAG_NOCOPY; + out->params.permanent = true; } out->data = nullptr; @@ -2517,7 +3382,8 @@ static bool sculpt_attribute_create(SculptSession *ss, CustomData_add_layer_named(cdata, proptype, CD_SET_DEFAULT, nullptr, totelem, name); int index = CustomData_get_named_layer_index(cdata, proptype, name); - if (!permanent) { + if (!permanent && !ss->save_temp_layers) { + out->params.permanent = true; cdata->layers[index].flag |= CD_FLAG_TEMPORARY | CD_FLAG_NOCOPY; } @@ -2570,6 +3436,7 @@ static bool sculpt_attr_update(Object *ob, SculptAttribute *attr) if (!bad) { if (attr->data_for_bmesh) { attr->bmesh_cd_offset = cdata->layers[layer_index].offset; + attr->data = nullptr; } else { attr->data = cdata->layers[layer_index].data; @@ -2577,20 +3444,27 @@ static bool sculpt_attr_update(Object *ob, SculptAttribute *attr) } } + PBVHType pbvhtype; + if (ss->pbvh) { + pbvhtype = BKE_pbvh_type(ss->pbvh); + } + else if (ss->bm) { + pbvhtype = PBVH_BMESH; + } + else if (ss->subdiv_ccg) { + pbvhtype = PBVH_GRIDS; + } + else { + pbvhtype = PBVH_FACES; + } + if (bad) { if (attr->simple_array) { MEM_SAFE_FREE(attr->data); } - sculpt_attribute_create(ss, - ob, - attr->domain, - attr->proptype, - attr->name, - attr, - &attr->params, - BKE_pbvh_type(ss->pbvh), - attr->data_for_bmesh); + sculpt_attribute_create( + ss, ob, attr->domain, attr->proptype, attr->name, attr, &attr->params, pbvhtype); } return bad; @@ -2669,19 +3543,7 @@ SculptAttribute *BKE_sculpt_attribute_get(struct Object *ob, int index = CustomData_get_named_layer_index(cdata, proptype, name); if (index != -1) { - int totelem = 0; - - switch (domain) { - case ATTR_DOMAIN_POINT: - totelem = BKE_sculptsession_vertex_count(ss); - break; - case ATTR_DOMAIN_FACE: - totelem = ss->totfaces; - break; - default: - BLI_assert_unreachable(); - break; - } + int totelem = sculpt_attr_elem_count_get(ob, ATTR_DOMAIN_POINT); attr = sculpt_alloc_attr(ss); @@ -2707,8 +3569,7 @@ static SculptAttribute *sculpt_attribute_ensure_ex(Object *ob, eCustomDataType proptype, const char *name, const SculptAttributeParams *params, - PBVHType pbvhtype, - bool flat_array_for_bmesh) + PBVHType pbvhtype) { SculptSession *ss = ob->sculpt; SculptAttribute *attr = BKE_sculpt_attribute_get(ob, domain, proptype, name); @@ -2722,8 +3583,7 @@ static SculptAttribute *sculpt_attribute_ensure_ex(Object *ob, attr = sculpt_alloc_attr(ss); /* Create attribute. */ - sculpt_attribute_create( - ss, ob, domain, proptype, name, attr, params, pbvhtype, flat_array_for_bmesh); + sculpt_attribute_create(ss, ob, domain, proptype, name, attr, params, pbvhtype); sculpt_attribute_update_refs(ob); return attr; @@ -2738,7 +3598,7 @@ SculptAttribute *BKE_sculpt_attribute_ensure(Object *ob, SculptAttributeParams temp_params = *params; return sculpt_attribute_ensure_ex( - ob, domain, proptype, name, &temp_params, BKE_pbvh_type(ob->sculpt->pbvh), true); + ob, domain, proptype, name, &temp_params, BKE_pbvh_type(ob->sculpt->pbvh)); } static void sculptsession_bmesh_attr_update_internal(Object *ob) @@ -2748,9 +3608,26 @@ static void sculptsession_bmesh_attr_update_internal(Object *ob) sculptsession_bmesh_add_layers(ob); if (ss->pbvh) { - BKE_pbvh_update_bmesh_offsets(ss->pbvh, - ob->sculpt->attrs.dyntopo_node_id_vertex->bmesh_cd_offset, - ob->sculpt->attrs.dyntopo_node_id_face->bmesh_cd_offset); + int cd_sculpt_vert = CustomData_get_offset(&ss->bm->vdata, CD_DYNTOPO_VERT); + int cd_face_area = ss->attrs.face_areas ? ss->attrs.face_areas->bmesh_cd_offset : -1; + int cd_hide_poly = ss->attrs.hide_poly ? ss->attrs.hide_poly->bmesh_cd_offset : -1; + int cd_boundary_flags = ss->attrs.boundary_flags ? ss->attrs.boundary_flags->bmesh_cd_offset : + -1; + int cd_dyntopo_vert = ss->attrs.dyntopo_node_id_vertex ? + ss->attrs.dyntopo_node_id_vertex->bmesh_cd_offset : + -1; + int cd_dyntopo_face = ss->attrs.dyntopo_node_id_face ? + ss->attrs.dyntopo_node_id_face->bmesh_cd_offset : + -1; + + BKE_pbvh_set_idmap(ss->pbvh, ss->bm_idmap); + BKE_pbvh_update_offsets(ss->pbvh, + cd_dyntopo_vert, + cd_dyntopo_face, + cd_sculpt_vert, + cd_face_area, + cd_hide_poly, + cd_boundary_flags); } } @@ -2759,23 +3636,46 @@ static void sculptsession_bmesh_add_layers(Object *ob) SculptSession *ss = ob->sculpt; SculptAttributeParams params = {0}; - ss->attrs.dyntopo_node_id_vertex = sculpt_attribute_ensure_ex( - ob, - ATTR_DOMAIN_POINT, - CD_PROP_INT32, - SCULPT_ATTRIBUTE_NAME(dyntopo_node_id_vertex), - ¶ms, - PBVH_BMESH, - false); + if (!ss->attrs.sculpt_vert) { + ss->attrs.sculpt_vert = sculpt_attribute_ensure_ex( + ob, ATTR_DOMAIN_POINT, CD_DYNTOPO_VERT, "", ¶ms, PBVH_BMESH); + } - ss->attrs.dyntopo_node_id_face = sculpt_attribute_ensure_ex( - ob, - ATTR_DOMAIN_FACE, - CD_PROP_INT32, - SCULPT_ATTRIBUTE_NAME(dyntopo_node_id_face), - ¶ms, - PBVH_BMESH, - false); + if (!ss->attrs.face_areas) { + SculptAttributeParams params = {0}; + ss->attrs.face_areas = sculpt_attribute_ensure_ex(ob, + ATTR_DOMAIN_FACE, + CD_PROP_FLOAT2, + SCULPT_ATTRIBUTE_NAME(face_areas), + ¶ms, + PBVH_BMESH); + } + + if (!ss->attrs.dyntopo_node_id_vertex) { + ss->attrs.dyntopo_node_id_vertex = sculpt_attribute_ensure_ex( + ob, + ATTR_DOMAIN_POINT, + CD_PROP_INT32, + SCULPT_ATTRIBUTE_NAME(dyntopo_node_id_vertex), + ¶ms, + PBVH_BMESH); + } + + if (!ss->attrs.dyntopo_node_id_face) { + ss->attrs.dyntopo_node_id_face = sculpt_attribute_ensure_ex( + ob, + ATTR_DOMAIN_FACE, + CD_PROP_INT32, + SCULPT_ATTRIBUTE_NAME(dyntopo_node_id_face), + ¶ms, + PBVH_BMESH); + } + + ss->cd_vert_node_offset = ss->attrs.dyntopo_node_id_vertex->bmesh_cd_offset; + ss->cd_face_node_offset = ss->attrs.dyntopo_node_id_face->bmesh_cd_offset; + ss->cd_face_areas = ss->attrs.face_areas ? ss->attrs.face_areas->bmesh_cd_offset : -1; + ss->cd_sculpt_vert = ss->attrs.sculpt_vert->bmesh_cd_offset; + ss->cd_hide_poly = ss->attrs.hide_poly ? ss->attrs.hide_poly->bmesh_cd_offset : -1; } void BKE_sculpt_attributes_destroy_temporary_stroke(Object *ob) @@ -2786,11 +3686,65 @@ void BKE_sculpt_attributes_destroy_temporary_stroke(Object *ob) SculptAttribute *attr = ss->temp_attributes + i; if (attr->params.stroke_only) { + /* Don't free BMesh attribute as it is quite expensive; + * note that temporary attributes are still freed on + * exiting sculpt mode. + * + * Attributes allocated as simple arrays are fine however. + */ + + if (!attr->params.simple_array && ss->bm) { + continue; + } + BKE_sculpt_attribute_destroy(ob, attr); } } } +static void update_bmesh_offsets(Mesh *me, SculptSession *ss) +{ + ss->cd_sculpt_vert = ss->attrs.sculpt_vert ? ss->attrs.sculpt_vert->bmesh_cd_offset : -1; + ss->cd_vert_node_offset = ss->attrs.dyntopo_node_id_vertex ? + ss->attrs.dyntopo_node_id_vertex->bmesh_cd_offset : + -1; + ss->cd_face_node_offset = ss->attrs.dyntopo_node_id_face ? + ss->attrs.dyntopo_node_id_face->bmesh_cd_offset : + -1; + + CustomDataLayer *layer = BKE_id_attributes_active_color_get(&me->id); + if (layer) { + eAttrDomain domain = BKE_id_attribute_domain(&me->id, layer); + + CustomData *cdata = domain == ATTR_DOMAIN_POINT ? &ss->bm->vdata : &ss->bm->ldata; + int layer_i = CustomData_get_named_layer_index(cdata, layer->type, layer->name); + + ss->cd_vcol_offset = layer_i != -1 ? cdata->layers[layer_i].offset : -1; + } + else { + ss->cd_vcol_offset = -1; + } + + ss->cd_vert_mask_offset = CustomData_get_offset(&ss->bm->vdata, CD_PAINT_MASK); + ss->cd_faceset_offset = CustomData_get_offset_named( + &ss->bm->pdata, CD_PROP_INT32, ".sculpt_face_set"); + ss->cd_face_areas = ss->attrs.face_areas ? ss->attrs.face_areas->bmesh_cd_offset : -1; + ss->cd_hide_poly = ss->attrs.hide_poly ? ss->attrs.hide_poly->bmesh_cd_offset : -1; + + int cd_boundary_flags = ss->attrs.boundary_flags ? ss->attrs.boundary_flags->bmesh_cd_offset : + -1; + + if (ss->pbvh) { + BKE_pbvh_update_offsets(ss->pbvh, + ss->cd_vert_node_offset, + ss->cd_face_node_offset, + ss->cd_sculpt_vert, + ss->cd_face_areas, + ss->cd_hide_poly, + cd_boundary_flags); + } +} + static void sculpt_attribute_update_refs(Object *ob) { SculptSession *ss = ob->sculpt; @@ -2816,6 +3770,19 @@ static void sculpt_attribute_update_refs(Object *ob) if (ss->pbvh) { BKE_pbvh_update_active_vcol(ss->pbvh, me); } + + if (ss->attrs.face_areas && ss->pbvh) { + BKE_pbvh_set_face_areas(ss->pbvh, (float *)ss->attrs.face_areas->data); + } + + if (ss->bm) { + update_bmesh_offsets(me, ss); + } +} + +void BKE_sculptsession_update_attr_refs(Object *ob) +{ + sculpt_attribute_update_refs(ob); } void BKE_sculpt_attribute_destroy_temporary_all(Object *ob) @@ -2833,6 +3800,10 @@ void BKE_sculpt_attribute_destroy_temporary_all(Object *ob) bool BKE_sculpt_attribute_destroy(Object *ob, SculptAttribute *attr) { + if (!attr || !attr->used) { + return false; + } + SculptSession *ss = ob->sculpt; eAttrDomain domain = attr->domain; @@ -2865,9 +3836,11 @@ bool BKE_sculpt_attribute_destroy(Object *ob, SculptAttribute *attr) MEM_SAFE_FREE(attr->data); } else if (ss->bm) { - CustomData *cdata = attr->domain == ATTR_DOMAIN_POINT ? &ss->bm->vdata : &ss->bm->pdata; + if (attr->data_for_bmesh) { + CustomData *cdata = attr->domain == ATTR_DOMAIN_POINT ? &ss->bm->vdata : &ss->bm->pdata; - BM_data_layer_free_named(ss->bm, cdata, attr->name); + BM_data_layer_free_named(ss->bm, cdata, attr->name); + } } else { CustomData *cdata = nullptr; diff --git a/source/blender/blenkernel/intern/pbvh.c b/source/blender/blenkernel/intern/pbvh.c new file mode 100644 index 00000000000..45ac9290dc9 --- /dev/null +++ b/source/blender/blenkernel/intern/pbvh.c @@ -0,0 +1,5831 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup bke + */ + +#include "MEM_guardedalloc.h" + +#include "BLI_utildefines.h" + +#include "BLI_alloca.h" +#include "BLI_array.h" +#include "BLI_bitmap.h" +#include "BLI_ghash.h" +#include "BLI_listbase.h" +#include "BLI_math.h" +#include "BLI_rand.h" +#include "BLI_string.h" +#include "BLI_task.h" + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" + +#include "BKE_attribute.h" +#include "BKE_ccg.h" +#include "BKE_main.h" +#include "BKE_mesh.h" /* for BKE_mesh_calc_normals */ +#include "BKE_mesh_mapping.h" +#include "BKE_object.h" +#include "BKE_paint.h" +#include "BKE_pbvh.h" +#include "BKE_subdiv_ccg.h" + +#include "DEG_depsgraph_query.h" + +#include "DRW_pbvh.h" +#include "PIL_time.h" + +#include "PIL_time.h" + +#include "bmesh.h" + +#include "atomic_ops.h" + +#include "pbvh_intern.hh" + +#include + +#define LEAF_LIMIT 4000 + +/* Uncomment to test if triangles of the same face are + * properly clustered into single nodes. + */ +//#define TEST_PBVH_FACE_SPLIT + +/* Uncomment to test that faces are only assigned to one PBVHNode */ +//#define VALIDATE_UNIQUE_NODE_FACES + +//#define PERFCNTRS + +typedef struct PBVHStack { + PBVHNode *node; + bool revisiting; +} PBVHStack; + +typedef struct PBVHIter { + PBVH *pbvh; + BKE_pbvh_SearchCallback scb; + void *search_data; + + PBVHStack *stack; + int stacksize; + + PBVHStack stackfixed[PBVH_STACK_FIXED_DEPTH]; + int stackspace; +} PBVHIter; + +void BB_reset(BB *bb) +{ + bb->bmin[0] = bb->bmin[1] = bb->bmin[2] = FLT_MAX; + bb->bmax[0] = bb->bmax[1] = bb->bmax[2] = -FLT_MAX; +} + +void BB_intersect(BB *r_out, BB *a, BB *b) +{ + for (int i = 0; i < 3; i++) { + r_out->bmin[i] = max_ff(a->bmin[i], b->bmin[i]); + r_out->bmax[i] = min_ff(a->bmax[i], b->bmax[i]); + + if (r_out->bmax[i] < r_out->bmin[i]) { + r_out->bmax[i] = r_out->bmin[i] = 0.0f; + } + } +} + +float BB_volume(const BB *bb) +{ + float dx = bb->bmax[0] - bb->bmin[0]; + float dy = bb->bmax[1] - bb->bmin[1]; + float dz = bb->bmax[2] - bb->bmin[2]; + + return dx * dy * dz; +} + +/* Expand the bounding box to include a new coordinate */ +void BB_expand(BB *bb, const float co[3]) +{ + for (int i = 0; i < 3; i++) { + bb->bmin[i] = min_ff(bb->bmin[i], co[i]); + bb->bmax[i] = max_ff(bb->bmax[i], co[i]); + } +} + +void BB_expand_with_bb(BB *bb, BB *bb2) +{ + for (int i = 0; i < 3; i++) { + bb->bmin[i] = min_ff(bb->bmin[i], bb2->bmin[i]); + bb->bmax[i] = max_ff(bb->bmax[i], bb2->bmax[i]); + } +} + +int BB_widest_axis(const BB *bb) +{ + float dim[3]; + + for (int i = 0; i < 3; i++) { + dim[i] = bb->bmax[i] - bb->bmin[i]; + } + + if (dim[0] > dim[1]) { + if (dim[0] > dim[2]) { + return 0; + } + + return 2; + } + + if (dim[1] > dim[2]) { + return 1; + } + + return 2; +} + +void BBC_update_centroid(BBC *bbc) +{ + for (int i = 0; i < 3; i++) { + bbc->bcentroid[i] = (bbc->bmin[i] + bbc->bmax[i]) * 0.5f; + } +} + +/* Not recursive */ +static void update_node_vb(PBVH *pbvh, PBVHNode *node, int updateflag) +{ + if (!(updateflag & (PBVH_UpdateBB | PBVH_UpdateOriginalBB))) { + return; + } + + /* cannot clear flag here, causes leaky pbvh */ + // node->flag &= ~(updateflag & (PBVH_UpdateBB | PBVH_UpdateOriginalBB)); + + BB vb; + BB orig_vb; + + BB_reset(&vb); + BB_reset(&orig_vb); + + bool do_orig = true; // XXX updateflag & PBVH_UpdateOriginalBB; + bool do_normal = true; // XXX updateflag & PBVH_UpdateBB; + + if (node->flag & PBVH_Leaf) { + PBVHVertexIter vd; + + BKE_pbvh_vertex_iter_begin (pbvh, node, vd, PBVH_ITER_ALL) { + if (do_normal) { + BB_expand(&vb, vd.co); + } + + if (do_orig) { + MSculptVert *mv = pbvh->header.type == PBVH_BMESH ? + BM_ELEM_CD_GET_VOID_P(vd.bm_vert, pbvh->cd_sculpt_vert) : + pbvh->msculptverts + vd.index; + + if (mv->stroke_id != pbvh->stroke_id) { + BB_expand(&orig_vb, vd.co); + } + else { + BB_expand(&orig_vb, mv->origco); + } + } + } + BKE_pbvh_vertex_iter_end; + } + else { + if (do_normal) { + BB_expand_with_bb(&vb, &pbvh->nodes[node->children_offset].vb); + BB_expand_with_bb(&vb, &pbvh->nodes[node->children_offset + 1].vb); + } + + if (do_orig) { + BB_expand_with_bb(&orig_vb, &pbvh->nodes[node->children_offset].orig_vb); + BB_expand_with_bb(&orig_vb, &pbvh->nodes[node->children_offset + 1].orig_vb); + } + } + + if (do_normal) { + node->vb = vb; + } + + if (do_orig) { +#if 0 + float size[3]; + + sub_v3_v3v3(size, orig_vb.bmax, orig_vb.bmin); + mul_v3_fl(size, 0.05); + + sub_v3_v3(orig_vb.bmin, size); + add_v3_v3(orig_vb.bmax, size); +#endif + node->orig_vb = orig_vb; + } +} + +// void BKE_pbvh_node_BB_reset(PBVHNode *node) +//{ +// BB_reset(&node->vb); +//} +// +// void BKE_pbvh_node_BB_expand(PBVHNode *node, float co[3]) +//{ +// BB_expand(&node->vb, co); +//} + +static bool face_materials_match(const PBVH *pbvh, const int a, const int b) +{ + if (pbvh->material_indices) { + if (pbvh->material_indices[a] != pbvh->material_indices[b]) { + return false; + } + } + return (pbvh->mpoly[a].flag & ME_SMOOTH) == (pbvh->mpoly[b].flag & ME_SMOOTH); +} + +static bool grid_materials_match(const DMFlagMat *f1, const DMFlagMat *f2) +{ + return ((f1->flag & ME_SMOOTH) == (f2->flag & ME_SMOOTH) && (f1->mat_nr == f2->mat_nr)); +} + +/* Adapted from BLI_kdopbvh.c */ +/* Returns the index of the first element on the right of the partition */ +static int partition_indices_faces(int *prim_indices, + int *prim_scratch, + int lo, + int hi, + int axis, + float mid, + BBC *prim_bbc, + const MLoopTri *looptri) +{ + for (int i = lo; i < hi; i++) { + prim_scratch[i - lo] = prim_indices[i]; + } + + int lo2 = lo, hi2 = hi - 1; + int i1 = lo, i2 = 0; + + while (i1 < hi) { + int poly = looptri[prim_scratch[i2]].poly; + bool side = prim_bbc[prim_scratch[i2]].bcentroid[axis] >= mid; + + while (i1 < hi && looptri[prim_scratch[i2]].poly == poly) { + prim_indices[side ? hi2-- : lo2++] = prim_scratch[i2]; + i1++; + i2++; + } + } + + return lo2; +} + +static int partition_indices_grids(int *prim_indices, + int *prim_scratch, + int lo, + int hi, + int axis, + float mid, + BBC *prim_bbc, + SubdivCCG *subdiv_ccg) +{ + for (int i = lo; i < hi; i++) { + prim_scratch[i - lo] = prim_indices[i]; + } + + int lo2 = lo, hi2 = hi - 1; + int i1 = lo, i2 = 0; + + while (i1 < hi) { + int poly = BKE_subdiv_ccg_grid_to_face_index(subdiv_ccg, prim_scratch[i2]); + bool side = prim_bbc[prim_scratch[i2]].bcentroid[axis] >= mid; + + while (i1 < hi && BKE_subdiv_ccg_grid_to_face_index(subdiv_ccg, prim_scratch[i2]) == poly) { + prim_indices[side ? hi2-- : lo2++] = prim_scratch[i2]; + i1++; + i2++; + } + } + + return lo2; +} + +/* Returns the index of the first element on the right of the partition */ +static int partition_indices_material(PBVH *pbvh, int lo, int hi) +{ + const MLoopTri *looptri = pbvh->looptri; + const DMFlagMat *flagmats = pbvh->grid_flag_mats; + const int *indices = pbvh->prim_indices; + int i = lo, j = hi; + + for (;;) { + if (pbvh->looptri) { + const int first = looptri[pbvh->prim_indices[lo]].poly; + for (; face_materials_match(pbvh, first, looptri[indices[i]].poly); i++) { + /* pass */ + } + for (; !face_materials_match(pbvh, first, looptri[indices[j]].poly); j--) { + /* pass */ + } + } + else { + const DMFlagMat *first = &flagmats[pbvh->prim_indices[lo]]; + for (; grid_materials_match(first, &flagmats[indices[i]]); i++) { + /* pass */ + } + for (; !grid_materials_match(first, &flagmats[indices[j]]); j--) { + /* pass */ + } + } + + if (!(i < j)) { + return i; + } + + SWAP(int, pbvh->prim_indices[i], pbvh->prim_indices[j]); + i++; + } +} + +void pbvh_grow_nodes(PBVH *pbvh, int totnode) +{ + if (UNLIKELY(totnode > pbvh->node_mem_count)) { + pbvh->node_mem_count = pbvh->node_mem_count + (pbvh->node_mem_count / 3); + if (pbvh->node_mem_count < totnode) { + pbvh->node_mem_count = totnode; + } + pbvh->nodes = MEM_recallocN(pbvh->nodes, sizeof(PBVHNode) * pbvh->node_mem_count); + } + + pbvh->totnode = totnode; + + for (int i = 0; i < pbvh->totnode; i++) { + PBVHNode *node = pbvh->nodes + i; + + if (!node->id) { + node->id = ++pbvh->idgen; + } + } +} + +/* Add a vertex to the map, with a positive value for unique vertices and + * a negative value for additional vertices */ +static int map_insert_vert(PBVH *pbvh, GHash *map, uint *face_verts, uint *uniq_verts, int vertex) +{ + void *key, **value_p; + + key = POINTER_FROM_INT(vertex); + if (!BLI_ghash_ensure_p(map, key, &value_p)) { + int value_i; + if (!pbvh->vert_bitmap[vertex]) { + pbvh->vert_bitmap[vertex] = true; + value_i = *uniq_verts; + (*uniq_verts)++; + } + else { + value_i = ~(*face_verts); + (*face_verts)++; + } + *value_p = POINTER_FROM_INT(value_i); + return value_i; + } + + return POINTER_AS_INT(*value_p); +} + +/* Find vertices used by the faces in this node and update the draw buffers */ +static void build_mesh_leaf_node(PBVH *pbvh, PBVHNode *node) +{ + bool has_visible = false; + + node->uniq_verts = node->face_verts = 0; + const int totface = node->totprim; + + /* reserve size is rough guess */ + GHash *map = BLI_ghash_int_new_ex("build_mesh_leaf_node gh", 2 * totface); + + int(*face_vert_indices)[3] = MEM_mallocN(sizeof(int[3]) * totface, "bvh node face vert indices"); + + node->face_vert_indices = (const int(*)[3])face_vert_indices; + + if (pbvh->respect_hide == false) { + has_visible = true; + } + + for (int i = 0; i < totface; i++) { + const MLoopTri *lt = &pbvh->looptri[node->prim_indices[i]]; + for (int j = 0; j < 3; j++) { + face_vert_indices[i][j] = map_insert_vert( + pbvh, map, &node->face_verts, &node->uniq_verts, pbvh->mloop[lt->tri[j]].v); + } + + if (has_visible == false) { + if (!paint_is_face_hidden(lt, pbvh->hide_poly)) { + has_visible = true; + } + } + } + + int *vert_indices = MEM_callocN(sizeof(int) * (node->uniq_verts + node->face_verts), + "bvh node vert indices"); + node->vert_indices = vert_indices; + + /* Build the vertex list, unique verts first */ + GHashIterator gh_iter; + GHASH_ITER (gh_iter, map) { + void *value = BLI_ghashIterator_getValue(&gh_iter); + int ndx = POINTER_AS_INT(value); + + if (ndx < 0) { + ndx = -ndx + node->uniq_verts - 1; + } + + vert_indices[ndx] = POINTER_AS_INT(BLI_ghashIterator_getKey(&gh_iter)); + } + + for (int i = 0; i < totface; i++) { + const int sides = 3; + + for (int j = 0; j < sides; j++) { + if (face_vert_indices[i][j] < 0) { + face_vert_indices[i][j] = -face_vert_indices[i][j] + node->uniq_verts - 1; + } + } + } + + BKE_pbvh_node_mark_rebuild_draw(node); + + BKE_pbvh_node_fully_hidden_set(node, !has_visible); + BKE_pbvh_vert_tag_update_normal_tri_area(node); + + BLI_ghash_free(map, NULL, NULL); +} + +static void update_vb(PBVH *pbvh, PBVHNode *node, BBC *prim_bbc, int offset, int count) +{ + BB_reset(&node->vb); + for (int i = offset + count - 1; i >= offset; i--) { + BB_expand_with_bb(&node->vb, (BB *)(&prim_bbc[pbvh->prim_indices[i]])); + } + node->orig_vb = node->vb; +} + +int BKE_pbvh_count_grid_quads(BLI_bitmap **grid_hidden, + const int *grid_indices, + int totgrid, + int gridsize, + int display_gridsize) +{ + const int gridarea = (gridsize - 1) * (gridsize - 1); + int totquad = 0; + + /* grid hidden layer is present, so have to check each grid for + * visibility */ + + int depth1 = (int)(log2((double)gridsize - 1.0) + DBL_EPSILON); + int depth2 = (int)(log2((double)display_gridsize - 1.0) + DBL_EPSILON); + + int skip = depth2 < depth1 ? 1 << (depth1 - depth2 - 1) : 1; + + for (int i = 0; i < totgrid; i++) { + const BLI_bitmap *gh = grid_hidden[grid_indices[i]]; + + if (gh) { + /* grid hidden are present, have to check each element */ + for (int y = 0; y < gridsize - skip; y += skip) { + for (int x = 0; x < gridsize - skip; x += skip) { + if (!paint_is_grid_face_hidden(gh, gridsize, x, y)) { + totquad++; + } + } + } + } + else { + totquad += gridarea; + } + } + + return totquad; +} + +static void build_grid_leaf_node(PBVH *pbvh, PBVHNode *node) +{ + int totquads = BKE_pbvh_count_grid_quads(pbvh->grid_hidden, + node->prim_indices, + node->totprim, + pbvh->gridkey.grid_size, + pbvh->gridkey.grid_size); + BKE_pbvh_node_fully_hidden_set(node, (totquads == 0)); + BKE_pbvh_node_mark_rebuild_draw(node); + BKE_pbvh_vert_tag_update_normal_tri_area(node); +} + +static void build_leaf(PBVH *pbvh, int node_index, BBC *prim_bbc, int offset, int count) +{ + pbvh->nodes[node_index].flag |= PBVH_Leaf; + + pbvh->nodes[node_index].prim_indices = pbvh->prim_indices + offset; + pbvh->nodes[node_index].totprim = count; + + /* Still need vb for searches */ + update_vb(pbvh, &pbvh->nodes[node_index], prim_bbc, offset, count); + + if (pbvh->looptri) { + build_mesh_leaf_node(pbvh, pbvh->nodes + node_index); + } + else { + build_grid_leaf_node(pbvh, pbvh->nodes + node_index); + } +} + +/* Return zero if all primitives in the node can be drawn with the + * same material (including flat/smooth shading), non-zero otherwise */ +static bool leaf_needs_material_split(PBVH *pbvh, int offset, int count) +{ + if (count <= 1) { + return false; + } + + if (pbvh->looptri) { + const MLoopTri *first = &pbvh->looptri[pbvh->prim_indices[offset]]; + for (int i = offset + count - 1; i > offset; i--) { + int prim = pbvh->prim_indices[i]; + if (!face_materials_match(pbvh, first->poly, pbvh->looptri[prim].poly)) { + return true; + } + } + } + else { + const DMFlagMat *first = &pbvh->grid_flag_mats[pbvh->prim_indices[offset]]; + + for (int i = offset + count - 1; i > offset; i--) { + int prim = pbvh->prim_indices[i]; + if (!grid_materials_match(first, &pbvh->grid_flag_mats[prim])) { + return true; + } + } + } + + return false; +} + +#ifdef TEST_PBVH_FACE_SPLIT +static void test_face_boundaries(PBVH *pbvh) +{ + int faces_num = BKE_pbvh_num_faces(pbvh); + int *node_map = MEM_calloc_arrayN(faces_num, sizeof(int), __func__); + for (int i = 0; i < faces_num; i++) { + node_map[i] = -1; + } + + for (int i = 0; i < pbvh->totnode; i++) { + PBVHNode *node = pbvh->nodes + i; + + if (!(node->flag & PBVH_Leaf)) { + continue; + } + + switch (BKE_pbvh_type(pbvh)) { + case PBVH_FACES: { + for (int j = 0; j < node->totprim; j++) { + int poly = pbvh->looptri[node->prim_indices[j]].poly; + + if (node_map[poly] >= 0 && node_map[poly] != i) { + int old_i = node_map[poly]; + int prim_i = node->prim_indices - pbvh->prim_indices + j; + + printf("PBVH split error; poly: %d, prim_i: %d, node1: %d, node2: %d, totprim: %d\n", + poly, + prim_i, + old_i, + i, + node->totprim); + } + + node_map[poly] = i; + } + break; + } + case PBVH_GRIDS: + break; + case PBVH_BMESH: + break; + } + } + + MEM_SAFE_FREE(node_map); +} +#endif + +/* Recursively build a node in the tree + * + * vb is the voxel box around all of the primitives contained in + * this node. + * + * cb is the bounding box around all the centroids of the primitives + * contained in this node + * + * offset and start indicate a range in the array of primitive indices + */ + +static void build_sub(PBVH *pbvh, + int node_index, + BB *cb, + BBC *prim_bbc, + int offset, + int count, + int *prim_scratch, + int depth) +{ + int end; + BB cb_backing; + + if (!prim_scratch) { + prim_scratch = MEM_malloc_arrayN(pbvh->totprim, sizeof(int), __func__); + } + + /* Decide whether this is a leaf or not */ + const bool below_leaf_limit = count <= pbvh->leaf_limit || depth == PBVH_STACK_FIXED_DEPTH - 1; + if (below_leaf_limit) { + if (!leaf_needs_material_split(pbvh, offset, count)) { + build_leaf(pbvh, node_index, prim_bbc, offset, count); + + if (node_index == 0) { + MEM_SAFE_FREE(prim_scratch); + } + + return; + } + } + + /* Add two child nodes */ + pbvh->nodes[node_index].children_offset = pbvh->totnode; + pbvh_grow_nodes(pbvh, pbvh->totnode + 2); + + /* Update parent node bounding box */ + update_vb(pbvh, &pbvh->nodes[node_index], prim_bbc, offset, count); + + if (!below_leaf_limit) { + /* Find axis with widest range of primitive centroids */ + if (!cb) { + cb = &cb_backing; + BB_reset(cb); + for (int i = offset + count - 1; i >= offset; i--) { + BB_expand(cb, prim_bbc[pbvh->prim_indices[i]].bcentroid); + } + } + const int axis = BB_widest_axis(cb); + + /* Partition primitives along that axis */ + if (pbvh->header.type == PBVH_FACES) { + end = partition_indices_faces(pbvh->prim_indices, + prim_scratch, + offset, + offset + count, + axis, + (cb->bmax[axis] + cb->bmin[axis]) * 0.5f, + prim_bbc, + pbvh->looptri); + } + else { + end = partition_indices_grids(pbvh->prim_indices, + prim_scratch, + offset, + offset + count, + axis, + (cb->bmax[axis] + cb->bmin[axis]) * 0.5f, + prim_bbc, + pbvh->subdiv_ccg); + } + } + else { + /* Partition primitives by material */ + end = partition_indices_material(pbvh, offset, offset + count - 1); + } + + /* Build children */ + build_sub(pbvh, + pbvh->nodes[node_index].children_offset, + NULL, + prim_bbc, + offset, + end - offset, + prim_scratch, + depth + 1); + build_sub(pbvh, + pbvh->nodes[node_index].children_offset + 1, + NULL, + prim_bbc, + end, + offset + count - end, + prim_scratch, + depth + 1); + + if (node_index == 0) { + MEM_SAFE_FREE(prim_scratch); + } +} + +static void pbvh_build(PBVH *pbvh, BB *cb, BBC *prim_bbc, int totprim) +{ + if (totprim != pbvh->totprim) { + pbvh->totprim = totprim; + if (pbvh->nodes) { + MEM_freeN(pbvh->nodes); + } + if (pbvh->prim_indices) { + MEM_freeN(pbvh->prim_indices); + } + pbvh->prim_indices = MEM_mallocN(sizeof(int) * totprim, "bvh prim indices"); + for (int i = 0; i < totprim; i++) { + pbvh->prim_indices[i] = i; + } + pbvh->totnode = 0; + if (pbvh->node_mem_count < 100) { + pbvh->node_mem_count = 100; + pbvh->nodes = MEM_callocN(sizeof(PBVHNode) * pbvh->node_mem_count, "bvh initial nodes"); + } + } + + pbvh->totnode = 1; + build_sub(pbvh, 0, cb, prim_bbc, 0, totprim, NULL, 0); +} + +void BKE_pbvh_set_face_areas(PBVH *pbvh, float *face_areas) +{ + pbvh->face_areas = face_areas; +} + +/* XXX investigate this global. */ +bool pbvh_show_orig_co; + +static void pbvh_draw_args_init(PBVH *pbvh, PBVH_GPU_Args *args, PBVHNode *node) +{ + memset((void *)args, 0, sizeof(*args)); + + args->pbvh_type = pbvh->header.type; + args->mesh_verts_num = pbvh->totvert; + args->mesh_grids_num = pbvh->totgrid; + args->node = node; + + BKE_pbvh_node_num_verts(pbvh, node, NULL, &args->node_verts_num); + + args->grid_hidden = pbvh->grid_hidden; + args->face_sets_color_default = pbvh->face_sets_color_default; + args->face_sets_color_seed = pbvh->face_sets_color_seed; + args->vert_positions = pbvh->vert_positions; + args->mloop = pbvh->mloop; + args->mpoly = pbvh->mpoly; + args->mlooptri = pbvh->looptri; + args->flat_vcol_shading = pbvh->flat_vcol_shading; + args->show_orig = pbvh_show_orig_co; + args->updategen = node->updategen; + args->msculptverts = pbvh->msculptverts; + + if (ELEM(pbvh->header.type, PBVH_FACES, PBVH_GRIDS)) { + args->hide_poly = pbvh->pdata ? + CustomData_get_layer_named(pbvh->pdata, CD_PROP_BOOL, ".hide_poly") : + NULL; + } + + switch (pbvh->header.type) { + case PBVH_FACES: + args->mesh_faces_num = pbvh->mesh->totpoly; + args->vdata = pbvh->vdata; + args->ldata = pbvh->ldata; + args->pdata = pbvh->pdata; + args->totprim = node->totprim; + args->me = pbvh->mesh; + args->mpoly = pbvh->mpoly; + args->vert_normals = pbvh->vert_normals; + + args->active_color = pbvh->mesh->active_color_attribute; + args->render_color = pbvh->mesh->default_color_attribute; + + args->prim_indices = node->prim_indices; + args->face_sets = pbvh->face_sets; + break; + case PBVH_GRIDS: + args->vdata = pbvh->vdata; + args->ldata = pbvh->ldata; + args->pdata = pbvh->pdata; + args->ccg_key = pbvh->gridkey; + args->me = pbvh->mesh; + args->totprim = node->totprim; + args->grid_indices = node->prim_indices; + args->subdiv_ccg = pbvh->subdiv_ccg; + args->face_sets = pbvh->face_sets; + args->mpoly = pbvh->mpoly; + + args->active_color = pbvh->mesh->active_color_attribute; + args->render_color = pbvh->mesh->default_color_attribute; + + args->mesh_grids_num = pbvh->totgrid; + args->grids = pbvh->grids; + args->gridfaces = pbvh->gridfaces; + args->grid_flag_mats = pbvh->grid_flag_mats; + args->vert_normals = pbvh->vert_normals; + + args->face_sets = pbvh->face_sets; + break; + case PBVH_BMESH: + args->bm = pbvh->header.bm; + + args->active_color = pbvh->mesh->active_color_attribute; + args->render_color = pbvh->mesh->default_color_attribute; + + args->me = pbvh->mesh; + args->vdata = &args->bm->vdata; + args->ldata = &args->bm->ldata; + args->pdata = &args->bm->pdata; + args->bm_faces = node->bm_faces; + args->bm_other_verts = node->bm_other_verts; + args->bm_unique_vert = node->bm_unique_verts; + args->totprim = BLI_table_gset_len(node->bm_faces); + args->cd_mask_layer = CustomData_get_offset(&pbvh->header.bm->vdata, CD_PAINT_MASK); + + args->tribuf = node->tribuf; + args->tri_buffers = node->tri_buffers; + args->tot_tri_buffers = node->tot_tri_buffers; + + break; + } +} + +#ifdef VALIDATE_UNIQUE_NODE_FACES +static void pbvh_validate_node_prims(PBVH *pbvh) +{ + int totface = 0; + + if (pbvh->header.type == PBVH_BMESH) { + return; + } + + for (int i = 0; i < pbvh->totnode; i++) { + PBVHNode *node = pbvh->nodes + i; + + if (!(node->flag & PBVH_Leaf)) { + continue; + } + + for (int j = 0; j < node->totprim; j++) { + int poly; + + if (pbvh->header.type == PBVH_FACES) { + poly = pbvh->looptri[node->prim_indices[j]].poly; + } + else { + poly = BKE_subdiv_ccg_grid_to_face_index(pbvh->subdiv_ccg, node->prim_indices[j]); + } + + totface = max_ii(totface, poly + 1); + } + } + + int *facemap = (int *)MEM_malloc_arrayN(totface, sizeof(*facemap), __func__); + + for (int i = 0; i < totface; i++) { + facemap[i] = -1; + } + + for (int i = 0; i < pbvh->totnode; i++) { + PBVHNode *node = pbvh->nodes + i; + + if (!(node->flag & PBVH_Leaf)) { + continue; + } + + for (int j = 0; j < node->totprim; j++) { + int poly; + + if (pbvh->header.type == PBVH_FACES) { + poly = pbvh->looptri[node->prim_indices[j]].poly; + } + else { + poly = BKE_subdiv_ccg_grid_to_face_index(pbvh->subdiv_ccg, node->prim_indices[j]); + } + + if (facemap[poly] != -1 && facemap[poly] != i) { + printf("%s: error: face spanned multiple nodes (old: %d new: %d)\n", + __func__, + facemap[poly], + i); + } + + facemap[poly] = i; + } + } + MEM_SAFE_FREE(facemap); +} +#endif + +void BKE_pbvh_build_mesh(PBVH *pbvh, + Mesh *mesh, + const MPoly *mpoly, + const MLoop *mloop, + float (*vert_positions)[3], + MSculptVert *msculptverts, + int totvert, + struct CustomData *vdata, + struct CustomData *ldata, + struct CustomData *pdata, + const MLoopTri *looptri, + int looptri_num, + bool fast_draw, + float *face_areas, + SculptPMap *pmap) +{ + BBC *prim_bbc = NULL; + BB cb; + + if (pbvh->pmap != pmap) { + BKE_pbvh_pmap_aquire(pmap); + } + + pbvh->pmap = pmap; + pbvh->face_areas = face_areas; + pbvh->mesh = mesh; + pbvh->header.type = PBVH_FACES; + pbvh->mpoly = mpoly; + pbvh->hide_poly = (bool *)CustomData_get_layer_named_for_write( + &mesh->pdata, CD_PROP_BOOL, ".hide_poly", mesh->totpoly); + pbvh->material_indices = (const int *)CustomData_get_layer_named( + &mesh->pdata, CD_PROP_INT32, "material_index"); + pbvh->mloop = mloop; + pbvh->looptri = looptri; + pbvh->msculptverts = msculptverts; + pbvh->vert_positions = vert_positions; + BKE_mesh_vertex_normals_ensure(mesh); + pbvh->vert_normals = BKE_mesh_vertex_normals_for_write(mesh); + pbvh->hide_vert = (bool *)CustomData_get_layer_named_for_write( + &mesh->vdata, CD_PROP_BOOL, ".hide_vert", mesh->totvert); + pbvh->vert_bitmap = MEM_calloc_arrayN(totvert, sizeof(bool), "bvh->vert_bitmap"); + pbvh->totvert = totvert; + +#ifdef TEST_PBVH_FACE_SPLIT + /* Use lower limit to increase probability of + * edge cases. + */ + pbvh->leaf_limit = 100; +#else + pbvh->leaf_limit = LEAF_LIMIT; +#endif + + pbvh->vdata = vdata; + pbvh->ldata = ldata; + pbvh->pdata = pdata; + pbvh->faces_num = mesh->totpoly; + + pbvh->face_sets_color_seed = mesh->face_sets_color_seed; + pbvh->face_sets_color_default = mesh->face_sets_color_default; + + BB_reset(&cb); + + /* For each face, store the AABB and the AABB centroid */ + prim_bbc = MEM_mallocN(sizeof(BBC) * looptri_num, "prim_bbc"); + + for (int i = 0; i < mesh->totvert; i++) { + msculptverts[i].flag &= ~SCULPTVERT_NEED_VALENCE; + msculptverts[i].valence = pmap->pmap[i].count; + } + + for (int i = 0; i < looptri_num; i++) { + const MLoopTri *lt = &looptri[i]; + const int sides = 3; + BBC *bbc = prim_bbc + i; + + BB_reset((BB *)bbc); + + for (int j = 0; j < sides; j++) { + BB_expand((BB *)bbc, vert_positions[pbvh->mloop[lt->tri[j]].v]); + } + + BBC_update_centroid(bbc); + + BB_expand(&cb, bbc->bcentroid); + } + + if (looptri_num) { + pbvh_build(pbvh, &cb, prim_bbc, looptri_num); + +#ifdef TEST_PBVH_FACE_SPLIT + test_face_boundaries(pbvh); +#endif + } + + if (fast_draw) { + pbvh->flags |= PBVH_FAST_DRAW; + } + + MEM_freeN(prim_bbc); + + /* Clear the bitmap so it can be used as an update tag later on. */ + memset(pbvh->vert_bitmap, 0, sizeof(bool) * totvert); + + BKE_pbvh_update_active_vcol(pbvh, mesh); + +#ifdef VALIDATE_UNIQUE_NODE_FACES + pbvh_validate_node_prims(pbvh); +#endif +} + +void BKE_pbvh_build_grids(PBVH *pbvh, + CCGElem **grids, + int totgrid, + CCGKey *key, + void **gridfaces, + DMFlagMat *flagmats, + BLI_bitmap **grid_hidden, + bool fast_draw, + float *face_areas, + Mesh *me, + SubdivCCG *subdiv_ccg) +{ + const int gridsize = key->grid_size; + + pbvh->header.type = PBVH_GRIDS; + pbvh->face_areas = face_areas; + pbvh->grids = grids; + pbvh->gridfaces = gridfaces; + pbvh->grid_flag_mats = flagmats; + pbvh->totgrid = totgrid; + pbvh->gridkey = *key; + pbvh->grid_hidden = grid_hidden; + pbvh->subdiv_ccg = subdiv_ccg; + pbvh->faces_num = me->totpoly; + + /* Find maximum number of grids per face. */ + int max_grids = 1; + const MPoly *mpoly = BKE_mesh_polys(me); + + for (int i = 0; i < me->totpoly; i++) { + max_grids = max_ii(max_grids, mpoly[i].totloop); + } + + /* Ensure leaf limit is at least 4 so there's room + * to split at original face boundaries. + * Fixes T102209. + */ + pbvh->leaf_limit = max_ii(LEAF_LIMIT / (gridsize * gridsize), max_grids); + + /* We need the base mesh attribute layout for PBVH draw. */ + pbvh->vdata = &me->vdata; + pbvh->ldata = &me->ldata; + pbvh->pdata = &me->pdata; + + pbvh->mpoly = BKE_mesh_polys(me); + pbvh->mloop = BKE_mesh_loops(me); + + /* We also need the base mesh for PBVH draw. */ + pbvh->mesh = me; + + BB cb; + BB_reset(&cb); + + /* For each grid, store the AABB and the AABB centroid */ + BBC *prim_bbc = MEM_mallocN(sizeof(BBC) * totgrid, "prim_bbc"); + + for (int i = 0; i < totgrid; i++) { + CCGElem *grid = grids[i]; + BBC *bbc = prim_bbc + i; + + BB_reset((BB *)bbc); + + for (int j = 0; j < gridsize * gridsize; j++) { + BB_expand((BB *)bbc, CCG_elem_offset_co(key, grid, j)); + } + + BBC_update_centroid(bbc); + + BB_expand(&cb, bbc->bcentroid); + } + + if (totgrid) { + pbvh_build(pbvh, &cb, prim_bbc, totgrid); + +#ifdef TEST_PBVH_FACE_SPLIT + test_face_boundaries(pbvh); +#endif + } + + if (fast_draw) { + pbvh->flags |= PBVH_FAST_DRAW; + } + + MEM_freeN(prim_bbc); +#ifdef VALIDATE_UNIQUE_NODE_FACES + pbvh_validate_node_prims(pbvh); +#endif +} + +PBVH *BKE_pbvh_new(PBVHType type) +{ + PBVH *pbvh = MEM_callocN(sizeof(PBVH), "pbvh"); + pbvh->respect_hide = true; + pbvh->draw_cache_invalid = true; + pbvh->header.type = type; + + /* Initialize this to true, instead of waiting for a draw engine + * to set it. Prevents a crash in draw manager instancing code. + */ + pbvh->is_drawing = true; + return pbvh; +} + +void BKE_pbvh_free(PBVH *pbvh) +{ +#ifdef WITH_PBVH_CACHE + BKE_pbvh_cache_remove(pbvh); +#endif + + for (int i = 0; i < pbvh->totnode; i++) { + PBVHNode *node = &pbvh->nodes[i]; + + if (node->flag & PBVH_Leaf) { + if (node->draw_batches) { + DRW_pbvh_node_free(node->draw_batches); + } + if (node->vert_indices) { + MEM_freeN((void *)node->vert_indices); + } + if (node->loop_indices) { + MEM_freeN(node->loop_indices); + } + if (node->face_vert_indices) { + MEM_freeN((void *)node->face_vert_indices); + } + if (node->bm_faces) { + BLI_table_gset_free(node->bm_faces, NULL); + } + if (node->bm_unique_verts) { + BLI_table_gset_free(node->bm_unique_verts, NULL); + } + if (node->bm_other_verts) { + BLI_table_gset_free(node->bm_other_verts, NULL); + } + + if (node->tribuf || node->tri_buffers) { + BKE_pbvh_bmesh_free_tris(pbvh, node); + } + +#ifdef PROXY_ADVANCED + BKE_pbvh_free_proxyarray(pbvh, node); +#endif + pbvh_node_pixels_free(node); + } + } + + if (pbvh->deformed) { + if (pbvh->vert_positions) { + /* if pbvh was deformed, new memory was allocated for verts/faces -- free it */ + + MEM_freeN((void *)pbvh->vert_positions); + } + + pbvh->vert_positions = NULL; + } + + if (pbvh->looptri) { + MEM_freeN((void *)pbvh->looptri); + } + + if (pbvh->nodes) { + MEM_freeN(pbvh->nodes); + } + + if (pbvh->prim_indices) { + MEM_freeN(pbvh->prim_indices); + } + + MEM_SAFE_FREE(pbvh->vert_bitmap); + + BKE_pbvh_pmap_release(pbvh->pmap); + pbvh->pmap = NULL; + + pbvh->invalid = true; + pbvh_pixels_free(pbvh); + + MEM_freeN(pbvh); +} + +static void pbvh_iter_begin(PBVHIter *iter, + PBVH *pbvh, + BKE_pbvh_SearchCallback scb, + void *search_data) +{ + iter->pbvh = pbvh; + iter->scb = scb; + iter->search_data = search_data; + + iter->stack = iter->stackfixed; + iter->stackspace = PBVH_STACK_FIXED_DEPTH; + + iter->stack[0].node = pbvh->nodes; + iter->stack[0].revisiting = false; + iter->stacksize = 1; +} + +static void pbvh_iter_end(PBVHIter *iter) +{ + if (iter->stackspace > PBVH_STACK_FIXED_DEPTH) { + MEM_freeN(iter->stack); + } +} + +static void pbvh_stack_push(PBVHIter *iter, PBVHNode *node, bool revisiting) +{ + if (UNLIKELY(iter->stacksize == iter->stackspace)) { + iter->stackspace *= 2; + if (iter->stackspace != (PBVH_STACK_FIXED_DEPTH * 2)) { + iter->stack = MEM_reallocN(iter->stack, sizeof(PBVHStack) * iter->stackspace); + } + else { + iter->stack = MEM_mallocN(sizeof(PBVHStack) * iter->stackspace, "PBVHStack"); + memcpy(iter->stack, iter->stackfixed, sizeof(PBVHStack) * iter->stacksize); + } + } + + iter->stack[iter->stacksize].node = node; + iter->stack[iter->stacksize].revisiting = revisiting; + iter->stacksize++; +} + +static PBVHNode *pbvh_iter_next(PBVHIter *iter, PBVHNodeFlags leaf_flag = PBVH_Leaf) +{ + /* purpose here is to traverse tree, visiting child nodes beforse their + * parents, this order is necessary for e.g. computing bounding boxes */ + + while (iter->stacksize) { + /* pop node */ + iter->stacksize--; + PBVHNode *node = iter->stack[iter->stacksize].node; + + /* on a mesh with no faces this can happen + * can remove this check if we know meshes have at least 1 face */ + if (node == NULL) { + return NULL; + } + + bool revisiting = iter->stack[iter->stacksize].revisiting; + + /* revisiting node already checked */ + if (revisiting) { + return node; + } + + if (iter->scb && !iter->scb(node, iter->search_data)) { + continue; /* don't traverse, outside of search zone */ + } + + if (node->flag & leaf_flag) { + /* immediately hit leaf node */ + return node; + } + + /* come back later when children are done */ + pbvh_stack_push(iter, node, true); + + /* push two child nodes on the stack */ + pbvh_stack_push(iter, iter->pbvh->nodes + node->children_offset + 1, false); + pbvh_stack_push(iter, iter->pbvh->nodes + node->children_offset, false); + } + + return NULL; +} + +static PBVHNode *pbvh_iter_next_occluded(PBVHIter *iter) +{ + while (iter->stacksize) { + /* pop node */ + iter->stacksize--; + PBVHNode *node = iter->stack[iter->stacksize].node; + + /* on a mesh with no faces this can happen + * can remove this check if we know meshes have at least 1 face */ + if (node == NULL) { + return NULL; + } + + float ff = dot_v3v3(node->vb.bmin, node->vb.bmax); + if (isnan(ff) || !isfinite(ff)) { + printf("%s: nan!\n", __func__); + } + + if (iter->scb && !iter->scb(node, iter->search_data)) { + continue; /* don't traverse, outside of search zone */ + } + + if (node->flag & PBVH_Leaf) { + /* immediately hit leaf node */ + return node; + } + + pbvh_stack_push(iter, iter->pbvh->nodes + node->children_offset + 1, false); + pbvh_stack_push(iter, iter->pbvh->nodes + node->children_offset, false); + } + + return NULL; +} + +void BKE_pbvh_search_gather_ex(PBVH *pbvh, + BKE_pbvh_SearchCallback scb, + void *search_data, + PBVHNode ***r_array, + int *r_tot, + PBVHNodeFlags leaf_flag) +{ + PBVHIter iter; + PBVHNode **array = NULL, *node; + int tot = 0, space = 0; + + pbvh_iter_begin(&iter, pbvh, scb, search_data); + + while ((node = pbvh_iter_next(&iter, leaf_flag))) { + if (node->flag & leaf_flag) { + if (UNLIKELY(tot == space)) { + /* resize array if needed */ + space = (tot == 0) ? 32 : space * 2; + array = MEM_recallocN_id(array, sizeof(PBVHNode *) * space, __func__); + } + + array[tot] = node; + tot++; + } + } + + pbvh_iter_end(&iter); + + if (tot == 0 && array) { + MEM_freeN(array); + array = NULL; + } + + *r_array = array; + *r_tot = tot; +} + +void BKE_pbvh_search_gather( + PBVH *pbvh, BKE_pbvh_SearchCallback scb, void *search_data, PBVHNode ***r_array, int *r_tot) +{ + BKE_pbvh_search_gather_ex(pbvh, scb, search_data, r_array, r_tot, PBVH_Leaf); +} + +void BKE_pbvh_search_callback(PBVH *pbvh, + BKE_pbvh_SearchCallback scb, + void *search_data, + BKE_pbvh_HitCallback hcb, + void *hit_data) +{ + PBVHIter iter; + PBVHNode *node; + + pbvh_iter_begin(&iter, pbvh, scb, search_data); + + while ((node = pbvh_iter_next(&iter))) { + if (node->flag & PBVH_Leaf) { + hcb(node, hit_data); + } + } + + pbvh_iter_end(&iter); +} + +typedef struct node_tree { + PBVHNode *data; + + struct node_tree *left; + struct node_tree *right; +} node_tree; + +static void node_tree_insert(node_tree *tree, node_tree *new_node) +{ + if (new_node->data->tmin < tree->data->tmin) { + if (tree->left) { + node_tree_insert(tree->left, new_node); + } + else { + tree->left = new_node; + } + } + else { + if (tree->right) { + node_tree_insert(tree->right, new_node); + } + else { + tree->right = new_node; + } + } +} + +static void traverse_tree(node_tree *tree, + BKE_pbvh_HitOccludedCallback hcb, + void *hit_data, + float *tmin) +{ + if (tree->left) { + traverse_tree(tree->left, hcb, hit_data, tmin); + } + + hcb(tree->data, hit_data, tmin); + + if (tree->right) { + traverse_tree(tree->right, hcb, hit_data, tmin); + } +} + +static void free_tree(node_tree *tree) +{ + if (tree->left) { + free_tree(tree->left); + tree->left = NULL; + } + + if (tree->right) { + free_tree(tree->right); + tree->right = NULL; + } + + free(tree); +} + +float BKE_pbvh_node_get_tmin(PBVHNode *node) +{ + return node->tmin; +} + +static void BKE_pbvh_search_callback_occluded(PBVH *pbvh, + BKE_pbvh_SearchCallback scb, + void *search_data, + BKE_pbvh_HitOccludedCallback hcb, + void *hit_data) +{ + PBVHIter iter; + PBVHNode *node; + node_tree *tree = NULL; + + pbvh_iter_begin(&iter, pbvh, scb, search_data); + + while ((node = pbvh_iter_next_occluded(&iter))) { + if (node->flag & PBVH_Leaf) { + node_tree *new_node = malloc(sizeof(node_tree)); + + new_node->data = node; + + new_node->left = NULL; + new_node->right = NULL; + + if (tree) { + node_tree_insert(tree, new_node); + } + else { + tree = new_node; + } + } + } + + pbvh_iter_end(&iter); + + if (tree) { + float tmin = FLT_MAX; + traverse_tree(tree, hcb, hit_data, &tmin); + free_tree(tree); + } +} + +static bool update_search_cb(PBVHNode *node, void *data_v) +{ + int flag = POINTER_AS_INT(data_v); + + if (node->flag & PBVH_Leaf) { + return (node->flag & flag) != 0; + } + + return true; +} + +typedef struct PBVHUpdateData { + PBVH *pbvh; + PBVHNode **nodes; + int totnode; + + float (*vert_normals)[3]; + int flag; + bool show_sculpt_face_sets; + bool flat_vcol_shading; + Mesh *mesh; + PBVHAttrReq *attrs; + int attrs_num; +} PBVHUpdateData; + +static void pbvh_update_normals_clear_task_cb(void *__restrict userdata, + const int n, + const TaskParallelTLS *__restrict UNUSED(tls)) +{ + PBVHUpdateData *data = userdata; + PBVH *pbvh = data->pbvh; + PBVHNode *node = data->nodes[n]; + float(*vert_normals)[3] = data->vert_normals; + + if (node->flag & PBVH_UpdateNormals) { + const int *verts = node->vert_indices; + const int totvert = node->uniq_verts; + for (int i = 0; i < totvert; i++) { + const int v = verts[i]; + if (pbvh->vert_bitmap[v]) { + zero_v3(vert_normals[v]); + } + } + } +} + +static void pbvh_update_normals_accum_task_cb(void *__restrict userdata, + const int n, + const TaskParallelTLS *__restrict UNUSED(tls)) +{ + PBVHUpdateData *data = userdata; + + PBVH *pbvh = data->pbvh; + PBVHNode *node = data->nodes[n]; + float(*vert_normals)[3] = data->vert_normals; + + if (node->flag & PBVH_UpdateNormals) { + uint mpoly_prev = UINT_MAX; + float fn[3]; + + const int *faces = node->prim_indices; + const int totface = node->totprim; + + for (int i = 0; i < totface; i++) { + const MLoopTri *lt = &pbvh->looptri[faces[i]]; + const uint vtri[3] = { + pbvh->mloop[lt->tri[0]].v, + pbvh->mloop[lt->tri[1]].v, + pbvh->mloop[lt->tri[2]].v, + }; + const int sides = 3; + + /* Face normal and mask */ + if (lt->poly != mpoly_prev) { + const MPoly *mp = &pbvh->mpoly[lt->poly]; + BKE_mesh_calc_poly_normal(mp, &pbvh->mloop[mp->loopstart], pbvh->vert_positions, fn); + mpoly_prev = lt->poly; + } + + for (int j = sides; j--;) { + const int v = vtri[j]; + + if (pbvh->vert_bitmap[v]) { + /* NOTE: This avoids `lock, add_v3_v3, unlock` + * and is five to ten times quicker than a spin-lock. + * Not exact equivalent though, since atomicity is only ensured for one component + * of the vector at a time, but here it shall not make any sensible difference. */ + for (int k = 3; k--;) { + atomic_add_and_fetch_fl(&vert_normals[v][k], fn[k]); + } + } + } + } + } +} + +static void pbvh_update_normals_store_task_cb(void *__restrict userdata, + const int n, + const TaskParallelTLS *__restrict UNUSED(tls)) +{ + PBVHUpdateData *data = userdata; + PBVH *pbvh = data->pbvh; + PBVHNode *node = data->nodes[n]; + float(*vert_normals)[3] = data->vert_normals; + + if (node->flag & PBVH_UpdateNormals) { + const int *verts = node->vert_indices; + const int totvert = node->uniq_verts; + + for (int i = 0; i < totvert; i++) { + const int v = verts[i]; + + /* No atomics necessary because we are iterating over uniq_verts only, + * so we know only this thread will handle this vertex. */ + if (pbvh->vert_bitmap[v]) { + normalize_v3(vert_normals[v]); + pbvh->vert_bitmap[v] = false; + } + } + + node->flag &= ~PBVH_UpdateNormals; + } +} + +static void pbvh_faces_update_normals(PBVH *pbvh, PBVHNode **nodes, int totnode) +{ + /* subtle assumptions: + * - We know that for all edited vertices, the nodes with faces + * adjacent to these vertices have been marked with PBVH_UpdateNormals. + * This is true because if the vertex is inside the brush radius, the + * bounding box of its adjacent faces will be as well. + * - However this is only true for the vertices that have actually been + * edited, not for all vertices in the nodes marked for update, so we + * can only update vertices marked in the `vert_bitmap`. + */ + + PBVHUpdateData data = { + .pbvh = pbvh, + .nodes = nodes, + .vert_normals = pbvh->vert_normals, + }; + + TaskParallelSettings settings; + BKE_pbvh_parallel_range_settings(&settings, true, totnode); + + /* Zero normals before accumulation. */ + BLI_task_parallel_range(0, totnode, &data, pbvh_update_normals_clear_task_cb, &settings); + BLI_task_parallel_range(0, totnode, &data, pbvh_update_normals_accum_task_cb, &settings); + BLI_task_parallel_range(0, totnode, &data, pbvh_update_normals_store_task_cb, &settings); +} + +static void pbvh_update_mask_redraw_task_cb(void *__restrict userdata, + const int n, + const TaskParallelTLS *__restrict UNUSED(tls)) +{ + + PBVHUpdateData *data = userdata; + PBVH *pbvh = data->pbvh; + PBVHNode *node = data->nodes[n]; + if (node->flag & PBVH_UpdateMask) { + + bool has_unmasked = false; + bool has_masked = true; + if (node->flag & PBVH_Leaf) { + PBVHVertexIter vd; + + BKE_pbvh_vertex_iter_begin (pbvh, node, vd, PBVH_ITER_ALL) { + if (vd.mask && *vd.mask < 1.0f) { + has_unmasked = true; + } + if (vd.mask && *vd.mask > 0.0f) { + has_masked = false; + } + } + BKE_pbvh_vertex_iter_end; + } + else { + has_unmasked = true; + has_masked = true; + } + BKE_pbvh_node_fully_masked_set(node, !has_unmasked); + BKE_pbvh_node_fully_unmasked_set(node, has_masked); + + node->flag &= ~PBVH_UpdateMask; + } +} + +static void pbvh_update_mask_redraw(PBVH *pbvh, PBVHNode **nodes, int totnode, int flag) +{ + PBVHUpdateData data = { + .pbvh = pbvh, + .nodes = nodes, + .flag = flag, + }; + + TaskParallelSettings settings; + BKE_pbvh_parallel_range_settings(&settings, true, totnode); + BLI_task_parallel_range(0, totnode, &data, pbvh_update_mask_redraw_task_cb, &settings); +} + +static void pbvh_update_visibility_redraw_task_cb(void *__restrict userdata, + const int n, + const TaskParallelTLS *__restrict UNUSED(tls)) +{ + + PBVHUpdateData *data = userdata; + PBVH *pbvh = data->pbvh; + PBVHNode *node = data->nodes[n]; + if (node->flag & PBVH_UpdateVisibility) { + node->flag &= ~PBVH_UpdateVisibility; + BKE_pbvh_node_fully_hidden_set(node, true); + if (node->flag & PBVH_Leaf) { + PBVHVertexIter vd; + BKE_pbvh_vertex_iter_begin (pbvh, node, vd, PBVH_ITER_ALL) { + if (vd.visible) { + BKE_pbvh_node_fully_hidden_set(node, false); + return; + } + } + BKE_pbvh_vertex_iter_end; + } + } +} + +static void pbvh_update_visibility_redraw(PBVH *pbvh, PBVHNode **nodes, int totnode, int flag) +{ + PBVHUpdateData data = { + .pbvh = pbvh, + .nodes = nodes, + .flag = flag, + }; + + TaskParallelSettings settings; + BKE_pbvh_parallel_range_settings(&settings, true, totnode); + BLI_task_parallel_range(0, totnode, &data, pbvh_update_visibility_redraw_task_cb, &settings); +} + +static void pbvh_update_BB_redraw_task_cb(void *__restrict userdata, + const int n, + const TaskParallelTLS *__restrict UNUSED(tls)) +{ + PBVHUpdateData *data = userdata; + PBVH *pbvh = data->pbvh; + PBVHNode *node = data->nodes[n]; + const int flag = data->flag; + + update_node_vb(pbvh, node, flag); + + if ((flag & PBVH_UpdateRedraw) && (node->flag & PBVH_UpdateRedraw)) { + node->flag &= ~PBVH_UpdateRedraw; + } +} + +void pbvh_update_BB_redraw(PBVH *pbvh, PBVHNode **nodes, int totnode, int flag) +{ + /* update BB, redraw flag */ + PBVHUpdateData data = { + .pbvh = pbvh, + .nodes = nodes, + .flag = flag, + }; + + TaskParallelSettings settings; + BKE_pbvh_parallel_range_settings(&settings, true, totnode); + BLI_task_parallel_range(0, totnode, &data, pbvh_update_BB_redraw_task_cb, &settings); +} + +bool BKE_pbvh_get_color_layer(const Mesh *me, CustomDataLayer **r_layer, eAttrDomain *r_attr) +{ + CustomDataLayer *layer = BKE_id_attributes_color_find(&me->id, me->active_color_attribute); + + if (!layer || !ELEM(layer->type, CD_PROP_COLOR, CD_PROP_BYTE_COLOR)) { + *r_layer = NULL; + *r_attr = ATTR_DOMAIN_POINT; + return false; + } + + eAttrDomain domain = BKE_id_attribute_domain(&me->id, layer); + + if (!ELEM(domain, ATTR_DOMAIN_POINT, ATTR_DOMAIN_CORNER)) { + *r_layer = NULL; + *r_attr = ATTR_DOMAIN_POINT; + return false; + } + + *r_layer = layer; + *r_attr = domain; + + return true; +} + +static void pbvh_update_draw_buffer_cb(void *__restrict userdata, + const int n, + const TaskParallelTLS *__restrict UNUSED(tls)) +{ + /* Create and update draw buffers. The functions called here must not + * do any OpenGL calls. Flags are not cleared immediately, that happens + * after GPU_pbvh_buffer_flush() which does the final OpenGL calls. */ + PBVHUpdateData *data = userdata; + PBVH *pbvh = data->pbvh; + PBVHNode *node = data->nodes[n]; + Mesh *me = data->mesh; + + CustomDataLayer *vcol_layer = NULL; + eAttrDomain vcol_domain; + + BKE_pbvh_get_color_layer(me, &vcol_layer, &vcol_domain); + + CustomData *vdata, *ldata; + + if (!pbvh->header.bm) { + vdata = pbvh->vdata ? pbvh->vdata : &me->vdata; + ldata = pbvh->ldata ? pbvh->ldata : &me->ldata; + } + else { + vdata = &pbvh->header.bm->vdata; + ldata = &pbvh->header.bm->ldata; + } + + Mesh me_query; + BKE_id_attribute_copy_domains_temp(ID_ME, vdata, NULL, ldata, NULL, NULL, &me_query.id); + me_query.active_color_attribute = me->active_color_attribute; + + if (!pbvh->header.bm) { + vdata = pbvh->vdata; + ldata = pbvh->ldata; + } + else { + vdata = &pbvh->header.bm->vdata; + ldata = &pbvh->header.bm->ldata; + } + + if (node->flag & PBVH_RebuildDrawBuffers) { + PBVH_GPU_Args args; + pbvh_draw_args_init(pbvh, &args, node); + + node->draw_batches = DRW_pbvh_node_create(&args); + } + + if (node->flag & PBVH_UpdateDrawBuffers) { + node->updategen++; + node->debug_draw_gen++; + + if (node->draw_batches) { + PBVH_GPU_Args args; + + pbvh_draw_args_init(pbvh, &args, node); + DRW_pbvh_node_update(node->draw_batches, &args); + } + } +} + +void BKE_pbvh_set_flat_vcol_shading(PBVH *pbvh, bool value) +{ + if (value != pbvh->flat_vcol_shading) { + for (int i = 0; i < pbvh->totnode; i++) { + PBVHNode *node = pbvh->nodes + i; + + if (!(node->flag & PBVH_Leaf)) { + continue; + } + + BKE_pbvh_node_mark_rebuild_draw(node); + } + } + + pbvh->flat_vcol_shading = value; +} + +void pbvh_free_draw_buffers(PBVH *UNUSED(pbvh), PBVHNode *node) +{ + if (node->draw_batches) { + DRW_pbvh_node_free(node->draw_batches); + node->draw_batches = NULL; + } +} + +static void pbvh_update_draw_buffers( + PBVH *pbvh, Mesh *me, PBVHNode **nodes, int totnode, int update_flag) +{ + const CustomData *vdata; + + switch (pbvh->header.type) { + case PBVH_BMESH: + if (!pbvh->header.bm) { + /* BMesh hasn't been created yet */ + return; + } + + vdata = &pbvh->header.bm->vdata; + break; + case PBVH_FACES: + vdata = pbvh->vdata; + break; + case PBVH_GRIDS: + vdata = NULL; + break; + } + UNUSED_VARS(vdata); + + if ((update_flag & PBVH_RebuildDrawBuffers) || ELEM(pbvh->header.type, PBVH_GRIDS, PBVH_BMESH)) { + /* Free buffers uses OpenGL, so not in parallel. */ + for (int n = 0; n < totnode; n++) { + PBVHNode *node = nodes[n]; + if (node->flag & PBVH_RebuildDrawBuffers) { + pbvh_free_draw_buffers(pbvh, node); + } + else if ((node->flag & PBVH_UpdateDrawBuffers) && node->draw_batches) { + PBVH_GPU_Args args; + + pbvh_draw_args_init(pbvh, &args, node); + DRW_pbvh_update_pre(node->draw_batches, &args); + } + } + } + + /* Parallel creation and update of draw buffers. */ + PBVHUpdateData data = { + .pbvh = pbvh, .nodes = nodes, .flat_vcol_shading = pbvh->flat_vcol_shading, .mesh = me}; + + TaskParallelSettings settings; + BKE_pbvh_parallel_range_settings(&settings, true, totnode); + BLI_task_parallel_range(0, totnode, &data, pbvh_update_draw_buffer_cb, &settings); + + for (int i = 0; i < totnode; i++) { + PBVHNode *node = nodes[i]; + + if (node->flag & PBVH_UpdateDrawBuffers) { + /* Flush buffers uses OpenGL, so not in parallel. */ + if (node->draw_batches) { + DRW_pbvh_node_gpu_flush(node->draw_batches); + } + } + + node->flag &= ~(PBVH_RebuildDrawBuffers | PBVH_UpdateDrawBuffers); + } +} + +static int pbvh_flush_bb(PBVH *pbvh, PBVHNode *node, int flag) +{ + int update = 0; + + /* Difficult to multi-thread well, we just do single threaded recursive. */ + if (node->flag & PBVH_Leaf) { + if (flag & PBVH_UpdateBB) { + update |= (node->flag & PBVH_UpdateBB); + node->flag &= ~PBVH_UpdateBB; + } + + if (flag & PBVH_UpdateOriginalBB) { + update |= (node->flag & PBVH_UpdateOriginalBB); + node->flag &= ~PBVH_UpdateOriginalBB; + } + + return update; + } + + update |= pbvh_flush_bb(pbvh, pbvh->nodes + node->children_offset, flag); + update |= pbvh_flush_bb(pbvh, pbvh->nodes + node->children_offset + 1, flag); + + update_node_vb(pbvh, node, update); + + return update; +} + +void BKE_pbvh_update_bounds(PBVH *pbvh, int flag) +{ + if (!pbvh->nodes) { + return; + } + + PBVHNode **nodes; + int totnode; + + BKE_pbvh_search_gather(pbvh, update_search_cb, POINTER_FROM_INT(flag), &nodes, &totnode); + + if (flag & (PBVH_UpdateBB | PBVH_UpdateOriginalBB | PBVH_UpdateRedraw)) { + pbvh_update_BB_redraw(pbvh, nodes, totnode, flag); + } + + if (flag & (PBVH_UpdateBB | PBVH_UpdateOriginalBB)) { + pbvh_flush_bb(pbvh, pbvh->nodes, flag); + } + + MEM_SAFE_FREE(nodes); +} + +void BKE_pbvh_update_vertex_data(PBVH *pbvh, int flag) +{ + if (!pbvh->nodes) { + return; + } + + PBVHNode **nodes; + int totnode; + + BKE_pbvh_search_gather(pbvh, update_search_cb, POINTER_FROM_INT(flag), &nodes, &totnode); + + if (flag & (PBVH_UpdateMask)) { + pbvh_update_mask_redraw(pbvh, nodes, totnode, flag); + } + + if (flag & (PBVH_UpdateColor)) { + for (int i = 0; i < totnode; i++) { + nodes[i]->flag |= PBVH_UpdateRedraw | PBVH_UpdateDrawBuffers | PBVH_UpdateColor; + } + } + + if (flag & (PBVH_UpdateVisibility)) { + pbvh_update_visibility_redraw(pbvh, nodes, totnode, flag); + } + + if (nodes) { + MEM_freeN(nodes); + } +} + +static void pbvh_faces_node_visibility_update(PBVH *pbvh, PBVHNode *node) +{ + int totvert, i; + BKE_pbvh_node_num_verts(pbvh, node, NULL, &totvert); + const int *vert_indices = BKE_pbvh_node_get_vert_indices(node); + + if (pbvh->hide_vert == NULL) { + BKE_pbvh_node_fully_hidden_set(node, false); + return; + } + for (i = 0; i < totvert; i++) { + if (!(pbvh->hide_vert[vert_indices[i]])) { + BKE_pbvh_node_fully_hidden_set(node, false); + return; + } + } + + BKE_pbvh_node_fully_hidden_set(node, true); +} + +static void pbvh_grids_node_visibility_update(PBVH *pbvh, PBVHNode *node) +{ + CCGElem **grids; + BLI_bitmap **grid_hidden; + int *grid_indices, totgrid, i; + + BKE_pbvh_node_get_grids(pbvh, node, &grid_indices, &totgrid, NULL, NULL, &grids); + grid_hidden = BKE_pbvh_grid_hidden(pbvh); + CCGKey key = *BKE_pbvh_get_grid_key(pbvh); + + for (i = 0; i < totgrid; i++) { + int g = grid_indices[i], x, y; + BLI_bitmap *gh = grid_hidden[g]; + + if (!gh) { + BKE_pbvh_node_fully_hidden_set(node, false); + return; + } + + for (y = 0; y < key.grid_size; y++) { + for (x = 0; x < key.grid_size; x++) { + if (!BLI_BITMAP_TEST(gh, y * key.grid_size + x)) { + BKE_pbvh_node_fully_hidden_set(node, false); + return; + } + } + } + } + BKE_pbvh_node_fully_hidden_set(node, true); +} + +static void pbvh_bmesh_node_visibility_update(PBVHNode *node) +{ + TableGSet *unique, *other; + + unique = BKE_pbvh_bmesh_node_unique_verts(node); + other = BKE_pbvh_bmesh_node_other_verts(node); + + BMVert *v; + + TGSET_ITER (v, unique) { + if (!BM_elem_flag_test(v, BM_ELEM_HIDDEN)) { + BKE_pbvh_node_fully_hidden_set(node, false); + return; + } + } + TGSET_ITER_END + + TGSET_ITER (v, other) { + if (!BM_elem_flag_test(v, BM_ELEM_HIDDEN)) { + BKE_pbvh_node_fully_hidden_set(node, false); + return; + } + } + TGSET_ITER_END + + BKE_pbvh_node_fully_hidden_set(node, true); +} + +static void pbvh_update_visibility_task_cb(void *__restrict userdata, + const int n, + const TaskParallelTLS *__restrict UNUSED(tls)) +{ + + PBVHUpdateData *data = userdata; + PBVH *pbvh = data->pbvh; + PBVHNode *node = data->nodes[n]; + if (node->flag & PBVH_UpdateVisibility) { + switch (BKE_pbvh_type(pbvh)) { + case PBVH_FACES: + pbvh_faces_node_visibility_update(pbvh, node); + break; + case PBVH_GRIDS: + pbvh_grids_node_visibility_update(pbvh, node); + break; + case PBVH_BMESH: + pbvh_bmesh_node_visibility_update(node); + break; + } + node->flag &= ~PBVH_UpdateVisibility; + } +} + +static void pbvh_update_visibility(PBVH *pbvh, PBVHNode **nodes, int totnode) +{ + PBVHUpdateData data = { + .pbvh = pbvh, + .nodes = nodes, + }; + + TaskParallelSettings settings; + BKE_pbvh_parallel_range_settings(&settings, true, totnode); + BLI_task_parallel_range(0, totnode, &data, pbvh_update_visibility_task_cb, &settings); +} + +void BKE_pbvh_update_visibility(PBVH *pbvh) +{ + if (!pbvh->nodes) { + return; + } + + PBVHNode **nodes; + int totnode; + + BKE_pbvh_search_gather( + pbvh, update_search_cb, POINTER_FROM_INT(PBVH_UpdateVisibility), &nodes, &totnode); + pbvh_update_visibility(pbvh, nodes, totnode); + + if (nodes) { + MEM_freeN(nodes); + } +} + +void BKE_pbvh_redraw_BB(PBVH *pbvh, float bb_min[3], float bb_max[3]) +{ + PBVHIter iter; + PBVHNode *node; + BB bb; + + BB_reset(&bb); + + pbvh_iter_begin(&iter, pbvh, NULL, NULL); + + while ((node = pbvh_iter_next(&iter))) { + if (node->flag & PBVH_UpdateRedraw) { + BB_expand_with_bb(&bb, &node->vb); + } + } + + pbvh_iter_end(&iter); + + copy_v3_v3(bb_min, bb.bmin); + copy_v3_v3(bb_max, bb.bmax); +} + +void BKE_pbvh_get_grid_updates(PBVH *pbvh, bool clear, void ***r_gridfaces, int *r_totface) +{ + GSet *face_set = BLI_gset_ptr_new(__func__); + PBVHNode *node; + PBVHIter iter; + + pbvh_iter_begin(&iter, pbvh, NULL, NULL); + + while ((node = pbvh_iter_next(&iter))) { + if (node->flag & PBVH_UpdateNormals) { + for (uint i = 0; i < node->totprim; i++) { + void *face = pbvh->gridfaces[node->prim_indices[i]]; + BLI_gset_add(face_set, face); + } + + if (clear) { + node->flag &= ~PBVH_UpdateNormals; + } + } + } + + pbvh_iter_end(&iter); + + const int tot = BLI_gset_len(face_set); + if (tot == 0) { + *r_totface = 0; + *r_gridfaces = NULL; + BLI_gset_free(face_set, NULL); + return; + } + + void **faces = MEM_mallocN(sizeof(*faces) * tot, "PBVH Grid Faces"); + + GSetIterator gs_iter; + int i; + GSET_ITER_INDEX (gs_iter, face_set, i) { + faces[i] = BLI_gsetIterator_getKey(&gs_iter); + } + + BLI_gset_free(face_set, NULL); + + *r_totface = tot; + *r_gridfaces = faces; +} + +/***************************** PBVH Access ***********************************/ + +bool BKE_pbvh_has_faces(const PBVH *pbvh) +{ + if (pbvh->header.type == PBVH_BMESH) { + return (pbvh->header.bm->totface != 0); + } + + return (pbvh->totprim != 0); +} + +void BKE_pbvh_bounding_box(const PBVH *pbvh, float min[3], float max[3]) +{ + if (pbvh->totnode) { + const BB *bb = &pbvh->nodes[0].vb; + copy_v3_v3(min, bb->bmin); + copy_v3_v3(max, bb->bmax); + } + else { + zero_v3(min); + zero_v3(max); + } +} + +BLI_bitmap **BKE_pbvh_grid_hidden(const PBVH *pbvh) +{ + BLI_assert(pbvh->header.type == PBVH_GRIDS); + return pbvh->grid_hidden; +} + +const CCGKey *BKE_pbvh_get_grid_key(const PBVH *pbvh) +{ + BLI_assert(pbvh->header.type == PBVH_GRIDS); + return &pbvh->gridkey; +} + +struct CCGElem **BKE_pbvh_get_grids(const PBVH *pbvh) +{ + BLI_assert(pbvh->header.type == PBVH_GRIDS); + return pbvh->grids; +} + +BLI_bitmap **BKE_pbvh_get_grid_visibility(const PBVH *pbvh) +{ + BLI_assert(pbvh->header.type == PBVH_GRIDS); + return pbvh->grid_hidden; +} + +int BKE_pbvh_get_grid_num_verts(const PBVH *pbvh) +{ + BLI_assert(pbvh->header.type == PBVH_GRIDS); + return pbvh->totgrid * pbvh->gridkey.grid_area; +} + +int BKE_pbvh_get_grid_num_faces(const PBVH *pbvh) +{ + BLI_assert(pbvh->header.type == PBVH_GRIDS); + return pbvh->totgrid * (pbvh->gridkey.grid_size - 1) * (pbvh->gridkey.grid_size - 1); +} + +/***************************** Node Access ***********************************/ + +void BKE_pbvh_node_mark_original_update(PBVHNode *node) +{ + node->flag |= PBVH_UpdateOriginalBB; +} + +void BKE_pbvh_node_mark_update(PBVHNode *node) +{ + node->flag |= PBVH_UpdateNormals | PBVH_UpdateBB | PBVH_UpdateOriginalBB | + PBVH_UpdateDrawBuffers | PBVH_UpdateRedraw | PBVH_UpdateCurvatureDir | + PBVH_RebuildPixels | PBVH_UpdateTriAreas; +} + +void BKE_pbvh_node_mark_update_mask(PBVHNode *node) +{ + node->flag |= PBVH_UpdateMask | PBVH_UpdateDrawBuffers | PBVH_UpdateRedraw; +} + +void BKE_pbvh_node_mark_update_color(PBVHNode *node) +{ + node->flag |= PBVH_UpdateColor | PBVH_UpdateDrawBuffers | PBVH_UpdateRedraw; +} + +void BKE_pbvh_node_mark_update_face_sets(PBVHNode *node) +{ + node->flag |= PBVH_UpdateDrawBuffers | PBVH_UpdateRedraw; +} + +void BKE_pbvh_mark_rebuild_pixels(PBVH *pbvh) +{ + for (int n = 0; n < pbvh->totnode; n++) { + PBVHNode *node = &pbvh->nodes[n]; + if (node->flag & PBVH_Leaf) { + node->flag |= PBVH_RebuildPixels; + } + } +} + +void BKE_pbvh_node_mark_update_visibility(PBVHNode *node) +{ + node->flag |= PBVH_UpdateVisibility | PBVH_RebuildDrawBuffers | PBVH_UpdateDrawBuffers | + PBVH_UpdateRedraw | PBVH_UpdateTris; +} + +void BKE_pbvh_vert_tag_update_normal_visibility(PBVHNode *node) +{ + node->flag |= PBVH_UpdateVisibility | PBVH_RebuildDrawBuffers | PBVH_UpdateDrawBuffers | + PBVH_UpdateRedraw | PBVH_UpdateCurvatureDir | PBVH_UpdateTris; +} + +void BKE_pbvh_node_mark_rebuild_draw(PBVHNode *node) +{ + node->flag |= PBVH_RebuildDrawBuffers | PBVH_UpdateDrawBuffers | PBVH_UpdateRedraw | + PBVH_UpdateCurvatureDir; +} + +void BKE_pbvh_node_mark_redraw(PBVHNode *node) +{ + node->flag |= PBVH_UpdateDrawBuffers | PBVH_UpdateRedraw; +} + +void BKE_pbvh_node_mark_normals_update(PBVHNode *node) +{ + node->flag |= PBVH_UpdateNormals | PBVH_UpdateCurvatureDir; +} + +void BKE_pbvh_node_mark_curvature_update(PBVHNode *node) +{ + node->flag |= PBVH_UpdateCurvatureDir; +} + +void BKE_pbvh_curvature_update_set(PBVHNode *node, bool state) +{ + if (state) { + node->flag |= PBVH_UpdateCurvatureDir; + } + else { + node->flag &= ~PBVH_UpdateCurvatureDir; + } +} + +bool BKE_pbvh_curvature_update_get(PBVHNode *node) +{ + return node->flag & PBVH_UpdateCurvatureDir; +} + +void BKE_pbvh_node_fully_hidden_set(PBVHNode *node, int fully_hidden) +{ + BLI_assert(node->flag & PBVH_Leaf); + + if (fully_hidden) { + node->flag |= PBVH_FullyHidden; + } + else { + node->flag &= ~PBVH_FullyHidden; + } +} + +bool BKE_pbvh_node_fully_hidden_get(PBVHNode *node) +{ + return (node->flag & PBVH_Leaf) && (node->flag & PBVH_FullyHidden); +} + +void BKE_pbvh_node_fully_masked_set(PBVHNode *node, int fully_masked) +{ + BLI_assert(node->flag & PBVH_Leaf); + + if (fully_masked) { + node->flag |= PBVH_FullyMasked; + } + else { + node->flag &= ~PBVH_FullyMasked; + } +} + +bool BKE_pbvh_node_fully_masked_get(PBVHNode *node) +{ + return (node->flag & PBVH_Leaf) && (node->flag & PBVH_FullyMasked); +} + +void BKE_pbvh_node_fully_unmasked_set(PBVHNode *node, int fully_masked) +{ + BLI_assert(node->flag & PBVH_Leaf); + + if (fully_masked) { + node->flag |= PBVH_FullyUnmasked; + } + else { + node->flag &= ~PBVH_FullyUnmasked; + } +} + +bool BKE_pbvh_node_fully_unmasked_get(PBVHNode *node) +{ + return (node->flag & PBVH_Leaf) && (node->flag & PBVH_FullyUnmasked); +} + +void BKE_pbvh_vert_tag_update_normal(PBVH *pbvh, PBVHVertRef vertex) +{ + BLI_assert(pbvh->header.type == PBVH_FACES); + pbvh->vert_bitmap[vertex.i] = true; +} + +void BKE_pbvh_node_get_loops(PBVH *pbvh, + PBVHNode *node, + const int **r_loop_indices, + const MLoop **r_loops) +{ + BLI_assert(BKE_pbvh_type(pbvh) == PBVH_FACES); + + if (r_loop_indices) { + *r_loop_indices = node->loop_indices; + } + + if (r_loops) { + *r_loops = pbvh->mloop; + } +} + +int BKE_pbvh_num_faces(const PBVH *pbvh) +{ + switch (pbvh->header.type) { + case PBVH_GRIDS: + case PBVH_FACES: + return pbvh->faces_num; + case PBVH_BMESH: + return pbvh->header.bm->totface; + } + + BLI_assert_unreachable(); + return 0; +} + +const int *BKE_pbvh_node_get_vert_indices(PBVHNode *node) + +{ + return node->vert_indices; +} + +void BKE_pbvh_node_num_verts(PBVH *pbvh, PBVHNode *node, int *r_uniquevert, int *r_totvert) +{ + int tot; + + switch (pbvh->header.type) { + case PBVH_GRIDS: + tot = node->totprim * pbvh->gridkey.grid_area; + if (r_totvert) { + *r_totvert = tot; + } + if (r_uniquevert) { + *r_uniquevert = tot; + } + break; + case PBVH_FACES: + if (r_totvert) { + *r_totvert = node->uniq_verts + node->face_verts; + } + if (r_uniquevert) { + *r_uniquevert = node->uniq_verts; + } + break; + case PBVH_BMESH: + // not a leaf? return zero + if (!(node->flag & PBVH_Leaf)) { + if (r_totvert) { + *r_totvert = 0; + } + + if (r_uniquevert) { + *r_uniquevert = 0; + } + + return; + } + + pbvh_bmesh_check_other_verts(node); + + tot = BLI_table_gset_len(node->bm_unique_verts); + if (r_totvert) { + *r_totvert = tot + BLI_table_gset_len(node->bm_other_verts); + } + if (r_uniquevert) { + *r_uniquevert = tot; + } + break; + } +} + +void BKE_pbvh_node_get_grids(PBVH *pbvh, + PBVHNode *node, + int **r_grid_indices, + int *r_totgrid, + int *r_maxgrid, + int *r_gridsize, + CCGElem ***r_griddata) +{ + switch (pbvh->header.type) { + case PBVH_GRIDS: + if (r_grid_indices) { + *r_grid_indices = node->prim_indices; + } + if (r_totgrid) { + *r_totgrid = node->totprim; + } + if (r_maxgrid) { + *r_maxgrid = pbvh->totgrid; + } + if (r_gridsize) { + *r_gridsize = pbvh->gridkey.grid_size; + } + if (r_griddata) { + *r_griddata = pbvh->grids; + } + break; + case PBVH_FACES: + case PBVH_BMESH: + if (r_grid_indices) { + *r_grid_indices = NULL; + } + if (r_totgrid) { + *r_totgrid = 0; + } + if (r_maxgrid) { + *r_maxgrid = 0; + } + if (r_gridsize) { + *r_gridsize = 0; + } + if (r_griddata) { + *r_griddata = NULL; + } + break; + } +} + +void BKE_pbvh_node_get_BB(PBVHNode *node, float bb_min[3], float bb_max[3]) +{ + copy_v3_v3(bb_min, node->vb.bmin); + copy_v3_v3(bb_max, node->vb.bmax); +} + +void BKE_pbvh_node_get_original_BB(PBVHNode *node, float bb_min[3], float bb_max[3]) +{ + copy_v3_v3(bb_min, node->orig_vb.bmin); + copy_v3_v3(bb_max, node->orig_vb.bmax); +} + +void BKE_pbvh_node_get_proxies(PBVHNode *node, PBVHProxyNode **proxies, int *proxy_count) +{ + if (node->proxy_count > 0) { + if (proxies) { + *proxies = node->proxies; + } + if (proxy_count) { + *proxy_count = node->proxy_count; + } + } + else { + if (proxies) { + *proxies = NULL; + } + if (proxy_count) { + *proxy_count = 0; + } + } +} + +bool BKE_pbvh_node_has_vert_with_normal_update_tag(PBVH *pbvh, PBVHNode *node) +{ + BLI_assert(pbvh->header.type == PBVH_FACES); + const int *verts = node->vert_indices; + const int totvert = node->uniq_verts + node->face_verts; + + for (int i = 0; i < totvert; i++) { + const int v = verts[i]; + + if (pbvh->vert_bitmap[v]) { + return true; + } + } + + return false; +} + +/********************************* Ray-cast ***********************************/ + +typedef struct { + struct IsectRayAABB_Precalc ray; + bool original; + int stroke_id; +} RaycastData; + +static bool ray_aabb_intersect(PBVHNode *node, void *data_v) +{ + RaycastData *rcd = data_v; + const float *bb_min, *bb_max; + + if (rcd->original) { + /* BKE_pbvh_node_get_original_BB */ + bb_min = node->orig_vb.bmin; + bb_max = node->orig_vb.bmax; + } + else { + /* BKE_pbvh_node_get_BB */ + bb_min = node->vb.bmin; + bb_max = node->vb.bmax; + } + + return isect_ray_aabb_v3(&rcd->ray, bb_min, bb_max, &node->tmin); +} + +void BKE_pbvh_raycast(PBVH *pbvh, + BKE_pbvh_HitOccludedCallback cb, + void *data, + const float ray_start[3], + const float ray_normal[3], + bool original, + int stroke_id) +{ + RaycastData rcd; + + isect_ray_aabb_v3_precalc(&rcd.ray, ray_start, ray_normal); + + rcd.original = original; + rcd.stroke_id = stroke_id; + pbvh->stroke_id = stroke_id; + + BKE_pbvh_search_callback_occluded(pbvh, ray_aabb_intersect, &rcd, cb, data); +} + +bool ray_face_intersection_quad(const float ray_start[3], + struct IsectRayPrecalc *isect_precalc, + const float t0[3], + const float t1[3], + const float t2[3], + const float t3[3], + float *depth) +{ + float depth_test; + + if ((isect_ray_tri_watertight_v3(ray_start, isect_precalc, t0, t1, t2, &depth_test, NULL) && + (depth_test < *depth)) || + (isect_ray_tri_watertight_v3(ray_start, isect_precalc, t0, t2, t3, &depth_test, NULL) && + (depth_test < *depth))) { + *depth = depth_test; + return true; + } + + return false; +} + +bool ray_face_intersection_tri(const float ray_start[3], + struct IsectRayPrecalc *isect_precalc, + const float t0[3], + const float t1[3], + const float t2[3], + float *depth) +{ + float depth_test; + if (isect_ray_tri_watertight_v3(ray_start, isect_precalc, t0, t1, t2, &depth_test, NULL) && + (depth_test < *depth)) { + *depth = depth_test; + return true; + } + + return false; +} + +bool ray_update_depth_and_hit_count(const float depth_test, + float *r_depth, + float *r_back_depth, + int *hit_count) +{ + (*hit_count)++; + if (depth_test < *r_depth) { + *r_back_depth = *r_depth; + *r_depth = depth_test; + return true; + } + else if (depth_test > *r_depth && depth_test <= *r_back_depth) { + *r_back_depth = depth_test; + return false; + } + + return false; +} + +bool ray_face_intersection_depth_quad(const float ray_start[3], + struct IsectRayPrecalc *isect_precalc, + const float t0[3], + const float t1[3], + const float t2[3], + const float t3[3], + float *r_depth, + float *r_back_depth, + int *hit_count) +{ + float depth_test; + if (!(isect_ray_tri_watertight_v3(ray_start, isect_precalc, t0, t1, t2, &depth_test, NULL) || + isect_ray_tri_watertight_v3(ray_start, isect_precalc, t0, t2, t3, &depth_test, NULL))) { + return false; + } + return ray_update_depth_and_hit_count(depth_test, r_depth, r_back_depth, hit_count); +} + +bool ray_face_intersection_depth_tri(const float ray_start[3], + struct IsectRayPrecalc *isect_precalc, + const float t0[3], + const float t1[3], + const float t2[3], + float *r_depth, + float *r_back_depth, + int *hit_count) +{ + float depth_test; + + if (!isect_ray_tri_watertight_v3(ray_start, isect_precalc, t0, t1, t2, &depth_test, NULL)) { + return false; + } + return ray_update_depth_and_hit_count(depth_test, r_depth, r_back_depth, hit_count); +} + +/* Take advantage of the fact we know this won't be an intersection. + * Just handle ray-tri edges. */ +static float dist_squared_ray_to_tri_v3_fast(const float ray_origin[3], + const float ray_direction[3], + const float v0[3], + const float v1[3], + const float v2[3], + float r_point[3], + float *r_depth) +{ + const float *tri[3] = {v0, v1, v2}; + float dist_sq_best = FLT_MAX; + for (int i = 0, j = 2; i < 3; j = i++) { + float point_test[3], depth_test = FLT_MAX; + const float dist_sq_test = dist_squared_ray_to_seg_v3( + ray_origin, ray_direction, tri[i], tri[j], point_test, &depth_test); + if (dist_sq_test < dist_sq_best || i == 0) { + copy_v3_v3(r_point, point_test); + *r_depth = depth_test; + dist_sq_best = dist_sq_test; + } + } + return dist_sq_best; +} + +bool ray_face_nearest_quad(const float ray_start[3], + const float ray_normal[3], + const float t0[3], + const float t1[3], + const float t2[3], + const float t3[3], + float *depth, + float *dist_sq) +{ + float dist_sq_test; + float co[3], depth_test; + + if ((dist_sq_test = dist_squared_ray_to_tri_v3_fast( + ray_start, ray_normal, t0, t1, t2, co, &depth_test)) < *dist_sq) { + *dist_sq = dist_sq_test; + *depth = depth_test; + if ((dist_sq_test = dist_squared_ray_to_tri_v3_fast( + ray_start, ray_normal, t0, t2, t3, co, &depth_test)) < *dist_sq) { + *dist_sq = dist_sq_test; + *depth = depth_test; + } + return true; + } + + return false; +} + +bool ray_face_nearest_tri(const float ray_start[3], + const float ray_normal[3], + const float t0[3], + const float t1[3], + const float t2[3], + float *depth, + float *dist_sq) +{ + float dist_sq_test; + float co[3], depth_test; + + if ((dist_sq_test = dist_squared_ray_to_tri_v3_fast( + ray_start, ray_normal, t0, t1, t2, co, &depth_test)) < *dist_sq) { + *dist_sq = dist_sq_test; + *depth = depth_test; + return true; + } + + return false; +} + +static bool pbvh_faces_node_raycast(PBVH *pbvh, + const PBVHNode *node, + float (*origco)[3], + const float ray_start[3], + const float ray_normal[3], + struct IsectRayPrecalc *isect_precalc, + int *hit_count, + float *depth, + float *depth_back, + PBVHVertRef *r_active_vertex_index, + PBVHFaceRef *r_active_face_index, + float *r_face_normal, + int stroke_id) +{ + const float(*positions)[3] = pbvh->vert_positions; + const MLoop *mloop = pbvh->mloop; + const int *faces = node->prim_indices; + int totface = node->totprim; + bool hit = false; + float nearest_vertex_co[3] = {0.0f}; + + for (int i = 0; i < totface; i++) { + const MLoopTri *lt = &pbvh->looptri[faces[i]]; + const int *face_verts = node->face_vert_indices[i]; + + if (pbvh->respect_hide && paint_is_face_hidden(lt, pbvh->hide_poly)) { + continue; + } + + const float *co[3]; + if (origco) { + /* Intersect with backed up original coordinates. */ + co[0] = origco[face_verts[0]]; + co[1] = origco[face_verts[1]]; + co[2] = origco[face_verts[2]]; + } + else { + /* intersect with current coordinates */ + co[0] = positions[mloop[lt->tri[0]].v]; + co[1] = positions[mloop[lt->tri[1]].v]; + co[2] = positions[mloop[lt->tri[2]].v]; + } + + if (!ray_face_intersection_depth_tri( + ray_start, isect_precalc, co[0], co[1], co[2], depth, depth_back, hit_count)) { + continue; + } + + hit = true; + + if (r_face_normal) { + normal_tri_v3(r_face_normal, co[0], co[1], co[2]); + } + + if (r_active_vertex_index) { + float location[3] = {0.0f}; + madd_v3_v3v3fl(location, ray_start, ray_normal, *depth); + for (int j = 0; j < 3; j++) { + /* Always assign nearest_vertex_co in the first iteration to avoid comparison against + * uninitialized values. This stores the closest vertex in the current intersecting + * triangle. */ + if (j == 0 || + len_squared_v3v3(location, co[j]) < len_squared_v3v3(location, nearest_vertex_co)) { + copy_v3_v3(nearest_vertex_co, co[j]); + *r_active_vertex_index = (PBVHVertRef){.i = mloop[lt->tri[j]].v}; + *r_active_face_index = (PBVHFaceRef){.i = lt->poly}; + } + } + } + } + + return hit; +} + +static bool pbvh_grids_node_raycast(PBVH *pbvh, + PBVHNode *node, + float (*origco)[3], + const float ray_start[3], + const float ray_normal[3], + struct IsectRayPrecalc *isect_precalc, + int *hit_count, + float *depth, + float *back_depth, + + PBVHVertRef *r_active_vertex, + PBVHFaceRef *r_active_grid, + float *r_face_normal) +{ + const int totgrid = node->totprim; + const int gridsize = pbvh->gridkey.grid_size; + bool hit = false; + float nearest_vertex_co[3] = {0.0}; + const CCGKey *gridkey = &pbvh->gridkey; + + for (int i = 0; i < totgrid; i++) { + const int grid_index = node->prim_indices[i]; + CCGElem *grid = pbvh->grids[grid_index]; + BLI_bitmap *gh; + + if (!grid) { + continue; + } + + gh = pbvh->grid_hidden[grid_index]; + + for (int y = 0; y < gridsize - 1; y++) { + for (int x = 0; x < gridsize - 1; x++) { + /* check if grid face is hidden */ + if (gh) { + if (paint_is_grid_face_hidden(gh, gridsize, x, y)) { + continue; + } + } + + const float *co[4]; + if (origco) { + co[0] = origco[(y + 1) * gridsize + x]; + co[1] = origco[(y + 1) * gridsize + x + 1]; + co[2] = origco[y * gridsize + x + 1]; + co[3] = origco[y * gridsize + x]; + } + else { + co[0] = CCG_grid_elem_co(gridkey, grid, x, y + 1); + co[1] = CCG_grid_elem_co(gridkey, grid, x + 1, y + 1); + co[2] = CCG_grid_elem_co(gridkey, grid, x + 1, y); + co[3] = CCG_grid_elem_co(gridkey, grid, x, y); + } + + if (!ray_face_intersection_depth_quad(ray_start, + isect_precalc, + co[0], + co[1], + co[2], + co[3], + depth, + back_depth, + hit_count)) { + continue; + } + hit = true; + + if (r_face_normal) { + normal_quad_v3(r_face_normal, co[0], co[1], co[2], co[3]); + } + + if (r_active_vertex) { + float location[3] = {0.0}; + madd_v3_v3v3fl(location, ray_start, ray_normal, *depth); + + const int x_it[4] = {0, 1, 1, 0}; + const int y_it[4] = {1, 1, 0, 0}; + + for (int j = 0; j < 4; j++) { + /* Always assign nearest_vertex_co in the first iteration to avoid comparison against + * uninitialized values. This stores the closest vertex in the current intersecting + * quad. */ + if (j == 0 || len_squared_v3v3(location, co[j]) < + len_squared_v3v3(location, nearest_vertex_co)) { + copy_v3_v3(nearest_vertex_co, co[j]); + + r_active_vertex->i = gridkey->grid_area * grid_index + + (y + y_it[j]) * gridkey->grid_size + (x + x_it[j]); + } + } + } + + if (r_active_grid) { + r_active_grid->i = grid_index; + } + } + } + + if (origco) { + origco += gridsize * gridsize; + } + } + + return hit; +} + +bool BKE_pbvh_node_raycast(PBVH *pbvh, + PBVHNode *node, + float (*origco)[3], + bool use_origco, + const float ray_start[3], + const float ray_normal[3], + struct IsectRayPrecalc *isect_precalc, + int *hit_count, + float *depth, + float *back_depth, + PBVHVertRef *active_vertex, + PBVHFaceRef *active_face_grid, + float *face_normal, + int stroke_id) +{ + bool hit = false; + + if (node->flag & PBVH_FullyHidden) { + return false; + } + + switch (pbvh->header.type) { + case PBVH_FACES: + hit |= pbvh_faces_node_raycast(pbvh, + node, + origco, + ray_start, + ray_normal, + isect_precalc, + hit_count, + depth, + back_depth, + active_vertex, + active_face_grid, + face_normal, + stroke_id); + + break; + case PBVH_GRIDS: + hit |= pbvh_grids_node_raycast(pbvh, + node, + origco, + ray_start, + ray_normal, + isect_precalc, + hit_count, + depth, + back_depth, + active_vertex, + active_face_grid, + face_normal); + break; + case PBVH_BMESH: + hit = pbvh_bmesh_node_raycast(pbvh, + node, + ray_start, + ray_normal, + isect_precalc, + hit_count, + depth, + back_depth, + use_origco, + active_vertex, + active_face_grid, + face_normal, + stroke_id); + break; + } + + return hit; +} + +void BKE_pbvh_raycast_project_ray_root( + PBVH *pbvh, bool original, float ray_start[3], float ray_end[3], float ray_normal[3]) +{ + if (pbvh->nodes) { + float rootmin_start, rootmin_end; + float bb_min_root[3], bb_max_root[3], bb_center[3], bb_diff[3]; + struct IsectRayAABB_Precalc ray; + float ray_normal_inv[3]; + float offset = 1.0f + 1e-3f; + const float offset_vec[3] = {1e-3f, 1e-3f, 1e-3f}; + + if (original) { + BKE_pbvh_node_get_original_BB(pbvh->nodes, bb_min_root, bb_max_root); + } + else { + BKE_pbvh_node_get_BB(pbvh->nodes, bb_min_root, bb_max_root); + } + + /* Slightly offset min and max in case we have a zero width node + * (due to a plane mesh for instance), or faces very close to the bounding box boundary. */ + mid_v3_v3v3(bb_center, bb_max_root, bb_min_root); + /* diff should be same for both min/max since it's calculated from center */ + sub_v3_v3v3(bb_diff, bb_max_root, bb_center); + /* handles case of zero width bb */ + add_v3_v3(bb_diff, offset_vec); + madd_v3_v3v3fl(bb_max_root, bb_center, bb_diff, offset); + madd_v3_v3v3fl(bb_min_root, bb_center, bb_diff, -offset); + + /* first project start ray */ + isect_ray_aabb_v3_precalc(&ray, ray_start, ray_normal); + if (!isect_ray_aabb_v3(&ray, bb_min_root, bb_max_root, &rootmin_start)) { + return; + } + + /* then the end ray */ + mul_v3_v3fl(ray_normal_inv, ray_normal, -1.0); + isect_ray_aabb_v3_precalc(&ray, ray_end, ray_normal_inv); + /* unlikely to fail exiting if entering succeeded, still keep this here */ + if (!isect_ray_aabb_v3(&ray, bb_min_root, bb_max_root, &rootmin_end)) { + return; + } + + madd_v3_v3v3fl(ray_start, ray_start, ray_normal, rootmin_start); + madd_v3_v3v3fl(ray_end, ray_end, ray_normal_inv, rootmin_end); + } +} + +/* -------------------------------------------------------------------- */ + +typedef struct { + struct DistRayAABB_Precalc dist_ray_to_aabb_precalc; + bool original; +} FindNearestRayData; + +static bool nearest_to_ray_aabb_dist_sq(PBVHNode *node, void *data_v) +{ + FindNearestRayData *rcd = data_v; + const float *bb_min, *bb_max; + + if (rcd->original) { + /* BKE_pbvh_node_get_original_BB */ + bb_min = node->orig_vb.bmin; + bb_max = node->orig_vb.bmax; + } + else { + /* BKE_pbvh_node_get_BB */ + bb_min = node->vb.bmin; + bb_max = node->vb.bmax; + } + + float co_dummy[3], depth; + node->tmin = dist_squared_ray_to_aabb_v3( + &rcd->dist_ray_to_aabb_precalc, bb_min, bb_max, co_dummy, &depth); + /* Ideally we would skip distances outside the range. */ + return depth > 0.0f; +} + +void BKE_pbvh_find_nearest_to_ray(PBVH *pbvh, + BKE_pbvh_SearchNearestCallback cb, + void *data, + const float ray_start[3], + const float ray_normal[3], + bool original) +{ + FindNearestRayData ncd; + + dist_squared_ray_to_aabb_v3_precalc(&ncd.dist_ray_to_aabb_precalc, ray_start, ray_normal); + ncd.original = original; + + BKE_pbvh_search_callback_occluded(pbvh, nearest_to_ray_aabb_dist_sq, &ncd, cb, data); +} + +static bool pbvh_faces_node_nearest_to_ray(PBVH *pbvh, + const PBVHNode *node, + float (*origco)[3], + const float ray_start[3], + const float ray_normal[3], + float *depth, + float *dist_sq) +{ + const float(*positions)[3] = pbvh->vert_positions; + const MLoop *mloop = pbvh->mloop; + const int *faces = node->prim_indices; + int i, totface = node->totprim; + bool hit = false; + + for (i = 0; i < totface; i++) { + const MLoopTri *lt = &pbvh->looptri[faces[i]]; + const int *face_verts = node->face_vert_indices[i]; + + if (pbvh->respect_hide && paint_is_face_hidden(lt, pbvh->hide_poly)) { + continue; + } + + if (origco) { + /* Intersect with backed-up original coordinates. */ + hit |= ray_face_nearest_tri(ray_start, + ray_normal, + origco[face_verts[0]], + origco[face_verts[1]], + origco[face_verts[2]], + depth, + dist_sq); + } + else { + /* intersect with current coordinates */ + hit |= ray_face_nearest_tri(ray_start, + ray_normal, + positions[mloop[lt->tri[0]].v], + positions[mloop[lt->tri[1]].v], + positions[mloop[lt->tri[2]].v], + depth, + dist_sq); + } + } + + return hit; +} + +static bool pbvh_grids_node_nearest_to_ray(PBVH *pbvh, + PBVHNode *node, + float (*origco)[3], + const float ray_start[3], + const float ray_normal[3], + float *depth, + float *dist_sq) +{ + const int totgrid = node->totprim; + const int gridsize = pbvh->gridkey.grid_size; + bool hit = false; + + for (int i = 0; i < totgrid; i++) { + CCGElem *grid = pbvh->grids[node->prim_indices[i]]; + BLI_bitmap *gh; + + if (!grid) { + continue; + } + + gh = pbvh->grid_hidden[node->prim_indices[i]]; + + for (int y = 0; y < gridsize - 1; y++) { + for (int x = 0; x < gridsize - 1; x++) { + /* check if grid face is hidden */ + if (gh) { + if (paint_is_grid_face_hidden(gh, gridsize, x, y)) { + continue; + } + } + + if (origco) { + hit |= ray_face_nearest_quad(ray_start, + ray_normal, + origco[y * gridsize + x], + origco[y * gridsize + x + 1], + origco[(y + 1) * gridsize + x + 1], + origco[(y + 1) * gridsize + x], + depth, + dist_sq); + } + else { + hit |= ray_face_nearest_quad(ray_start, + ray_normal, + CCG_grid_elem_co(&pbvh->gridkey, grid, x, y), + CCG_grid_elem_co(&pbvh->gridkey, grid, x + 1, y), + CCG_grid_elem_co(&pbvh->gridkey, grid, x + 1, y + 1), + CCG_grid_elem_co(&pbvh->gridkey, grid, x, y + 1), + depth, + dist_sq); + } + } + } + + if (origco) { + origco += gridsize * gridsize; + } + } + + return hit; +} + +bool BKE_pbvh_node_find_nearest_to_ray(PBVH *pbvh, + PBVHNode *node, + float (*origco)[3], + bool use_origco, + const float ray_start[3], + const float ray_normal[3], + float *depth, + float *dist_sq, + int stroke_id) +{ + bool hit = false; + + if (node->flag & PBVH_FullyHidden) { + return false; + } + + switch (pbvh->header.type) { + case PBVH_FACES: + hit |= pbvh_faces_node_nearest_to_ray( + pbvh, node, origco, ray_start, ray_normal, depth, dist_sq); + break; + case PBVH_GRIDS: + hit |= pbvh_grids_node_nearest_to_ray( + pbvh, node, origco, ray_start, ray_normal, depth, dist_sq); + break; + case PBVH_BMESH: + hit = pbvh_bmesh_node_nearest_to_ray( + pbvh, node, ray_start, ray_normal, depth, dist_sq, use_origco, stroke_id); + break; + } + + return hit; +} + +typedef enum { + ISECT_INSIDE, + ISECT_OUTSIDE, + ISECT_INTERSECT, +} PlaneAABBIsect; + +/* Adapted from: + * http://www.gamedev.net/community/forums/topic.asp?topic_id=512123 + * Returns true if the AABB is at least partially within the frustum + * (ok, not a real frustum), false otherwise. + */ +static PlaneAABBIsect test_frustum_aabb(const float bb_min[3], + const float bb_max[3], + PBVHFrustumPlanes *frustum) +{ + PlaneAABBIsect ret = ISECT_INSIDE; + float(*planes)[4] = frustum->planes; + + for (int i = 0; i < frustum->num_planes; i++) { + float vmin[3], vmax[3]; + + for (int axis = 0; axis < 3; axis++) { + if (planes[i][axis] < 0) { + vmin[axis] = bb_min[axis]; + vmax[axis] = bb_max[axis]; + } + else { + vmin[axis] = bb_max[axis]; + vmax[axis] = bb_min[axis]; + } + } + + if (dot_v3v3(planes[i], vmin) + planes[i][3] < 0) { + return ISECT_OUTSIDE; + } + if (dot_v3v3(planes[i], vmax) + planes[i][3] <= 0) { + ret = ISECT_INTERSECT; + } + } + + return ret; +} + +bool BKE_pbvh_node_frustum_contain_AABB(PBVHNode *node, void *data) +{ + const float *bb_min, *bb_max; + /* BKE_pbvh_node_get_BB */ + bb_min = node->vb.bmin; + bb_max = node->vb.bmax; + + return test_frustum_aabb(bb_min, bb_max, data) != ISECT_OUTSIDE; +} + +bool BKE_pbvh_node_frustum_exclude_AABB(PBVHNode *node, void *data) +{ + const float *bb_min, *bb_max; + /* BKE_pbvh_node_get_BB */ + bb_min = node->vb.bmin; + bb_max = node->vb.bmax; + + return test_frustum_aabb(bb_min, bb_max, data) != ISECT_INSIDE; +} + +void BKE_pbvh_update_normals(PBVH *pbvh, struct SubdivCCG *subdiv_ccg) +{ + /* Update normals */ + PBVHNode **nodes; + int totnode; + + if (pbvh->header.type == PBVH_BMESH) { + for (int i = 0; i < pbvh->totnode; i++) { + if (pbvh->nodes[i].flag & PBVH_Leaf) { + BKE_pbvh_bmesh_check_tris(pbvh, pbvh->nodes + i); + } + } + } + + BKE_pbvh_search_gather( + pbvh, update_search_cb, POINTER_FROM_INT(PBVH_UpdateNormals), &nodes, &totnode); + + if (totnode > 0) { + if (pbvh->header.type == PBVH_BMESH) { + pbvh_bmesh_normals_update(pbvh, nodes, totnode); + } + else if (pbvh->header.type == PBVH_FACES) { + pbvh_faces_update_normals(pbvh, nodes, totnode); + } + else if (pbvh->header.type == PBVH_GRIDS) { + struct CCGFace **faces; + int num_faces; + BKE_pbvh_get_grid_updates(pbvh, true, (void ***)&faces, &num_faces); + if (num_faces > 0) { + BKE_subdiv_ccg_update_normals(subdiv_ccg, faces, num_faces); + MEM_freeN(faces); + } + } + } + + MEM_SAFE_FREE(nodes); +} + +void BKE_pbvh_face_sets_color_set(PBVH *pbvh, int seed, int color_default) +{ + pbvh->face_sets_color_seed = seed; + pbvh->face_sets_color_default = color_default; +} + +/** + * PBVH drawing, updating draw buffers as needed and culling any nodes outside + * the specified frustum. + */ +typedef struct PBVHDrawSearchData { + PBVHFrustumPlanes *frustum; + int accum_update_flag; + PBVHAttrReq *attrs; + int attrs_num; +} PBVHDrawSearchData; + +static bool pbvh_draw_search_cb(PBVHNode *node, void *data_v) +{ + PBVHDrawSearchData *data = data_v; + if (data->frustum && !BKE_pbvh_node_frustum_contain_AABB(node, data->frustum)) { + return false; + } + + data->accum_update_flag |= node->flag; + return true; +} + +void BKE_pbvh_draw_cb(PBVH *pbvh, + Mesh *me, + bool update_only_visible, + PBVHFrustumPlanes *update_frustum, + PBVHFrustumPlanes *draw_frustum, + void (*draw_fn)(void *user_data, PBVHBatches *batches, PBVH_GPU_Args *args), + void *user_data, + bool UNUSED(full_render), + PBVHAttrReq *attrs, + int attrs_num) +{ + PBVHNode **nodes; + int totnode; + int update_flag = 0; + + pbvh->draw_cache_invalid = false; + + /* Search for nodes that need updates. */ + if (update_only_visible) { + /* Get visible nodes with draw updates. */ + PBVHDrawSearchData data = { + .frustum = update_frustum, .accum_update_flag = 0, attrs, attrs_num}; + BKE_pbvh_search_gather(pbvh, pbvh_draw_search_cb, &data, &nodes, &totnode); + update_flag = data.accum_update_flag; + } + else { + /* Get all nodes with draw updates, also those outside the view. */ + const int search_flag = PBVH_RebuildDrawBuffers | PBVH_UpdateDrawBuffers; + BKE_pbvh_search_gather( + pbvh, update_search_cb, POINTER_FROM_INT(search_flag), &nodes, &totnode); + update_flag = PBVH_RebuildDrawBuffers | PBVH_UpdateDrawBuffers; + } + + /* Update draw buffers. */ + if (totnode != 0 && (update_flag & (PBVH_RebuildDrawBuffers | PBVH_UpdateDrawBuffers))) { + // check that need_full_render is set to GPU_pbvh_need_full_render_get(), + // but only if nodes need updating} + pbvh_update_draw_buffers(pbvh, me, nodes, totnode, update_flag); + } + MEM_SAFE_FREE(nodes); + + /* Draw visible nodes. */ + PBVHDrawSearchData draw_data = {.frustum = draw_frustum, .accum_update_flag = 0}; + BKE_pbvh_search_gather(pbvh, pbvh_draw_search_cb, &draw_data, &nodes, &totnode); + + PBVH_GPU_Args args; + + for (int i = 0; i < totnode; i++) { + PBVHNode *node = nodes[i]; + if (!(node->flag & PBVH_FullyHidden)) { + pbvh_draw_args_init(pbvh, &args, node); + + draw_fn(user_data, node->draw_batches, &args); + } + } + + MEM_SAFE_FREE(nodes); +} + +void BKE_pbvh_draw_debug_cb(PBVH *pbvh, + void (*draw_fn)(PBVHNode *node, + void *user_data, + const float bmin[3], + const float bmax[3], + PBVHNodeFlags flag), + void *user_data) +{ + for (int a = 0; a < pbvh->totnode; a++) { + PBVHNode *node = &pbvh->nodes[a]; + + if (pbvh_show_orig_co) { + draw_fn(node, user_data, node->orig_vb.bmin, node->orig_vb.bmax, node->flag); + } + else { + draw_fn(node, user_data, node->vb.bmin, node->vb.bmax, node->flag); + } + } +} + +void BKE_pbvh_grids_update(PBVH *pbvh, + CCGElem **grids, + void **gridfaces, + DMFlagMat *flagmats, + BLI_bitmap **grid_hidden, + CCGKey *key) +{ + pbvh->gridkey = *key; + pbvh->grids = grids; + pbvh->gridfaces = gridfaces; + + if (flagmats != pbvh->grid_flag_mats || pbvh->grid_hidden != grid_hidden) { + pbvh->grid_flag_mats = flagmats; + pbvh->grid_hidden = grid_hidden; + + for (int a = 0; a < pbvh->totnode; a++) { + BKE_pbvh_node_mark_rebuild_draw(&pbvh->nodes[a]); + } + } +} + +float (*BKE_pbvh_vert_coords_alloc(PBVH *pbvh))[3] +{ + float(*vertCos)[3] = NULL; + + if (pbvh->vert_positions) { + vertCos = MEM_malloc_arrayN(pbvh->totvert, sizeof(float[3]), __func__); + memcpy(vertCos, pbvh->vert_positions, sizeof(float[3]) * pbvh->totvert); + } + + return vertCos; +} + +void BKE_pbvh_vert_coords_apply(PBVH *pbvh, const float (*vertCos)[3], const int totvert) +{ + if (totvert != pbvh->totvert) { + BLI_assert_msg(0, "PBVH: Given deforming vcos number does not match PBVH vertex number!"); + return; + } + + if (!pbvh->deformed) { + if (pbvh->vert_positions) { + /* if pbvh is not already deformed, verts/faces points to the */ + /* original data and applying new coords to this arrays would lead to */ + /* unneeded deformation -- duplicate verts/faces to avoid this */ + + pbvh->vert_positions = MEM_dupallocN(pbvh->vert_positions); + /* No need to dupalloc pbvh->looptri, this one is 'totally owned' by pbvh, + * it's never some mesh data. */ + + pbvh->deformed = true; + } + } + + if (pbvh->vert_positions) { + float(*positions)[3] = pbvh->vert_positions; + /* copy new verts coords */ + for (int a = 0; a < pbvh->totvert; a++) { + /* no need for float comparison here (memory is exactly equal or not) */ + if (memcmp(positions[a], vertCos[a], sizeof(float[3])) != 0) { + copy_v3_v3(positions[a], vertCos[a]); + BKE_pbvh_vert_tag_update_normal(pbvh, BKE_pbvh_make_vref(a)); + } + } + + for (int a = 0; a < pbvh->totnode; a++) { + BKE_pbvh_node_mark_update(&pbvh->nodes[a]); + } + + BKE_pbvh_update_bounds(pbvh, PBVH_UpdateBB | PBVH_UpdateOriginalBB); + } +} + +bool BKE_pbvh_is_deformed(PBVH *pbvh) +{ + return pbvh->deformed; +} +/* Proxies */ + +PBVHProxyNode *BKE_pbvh_node_add_proxy(PBVH *pbvh, PBVHNode *node) +{ + int index, totverts; + + index = node->proxy_count; + + node->proxy_count++; + + if (node->proxies) { + node->proxies = MEM_reallocN(node->proxies, node->proxy_count * sizeof(PBVHProxyNode)); + } + else { + node->proxies = MEM_mallocN(sizeof(PBVHProxyNode), "PBVHNodeProxy"); + } + + BKE_pbvh_node_num_verts(pbvh, node, &totverts, NULL); + node->proxies[index].co = MEM_callocN(sizeof(float[3]) * totverts, "PBVHNodeProxy.co"); + + return node->proxies + index; +} + +void BKE_pbvh_node_free_proxies(PBVHNode *node) +{ + for (int p = 0; p < node->proxy_count; p++) { + MEM_freeN(node->proxies[p].co); + node->proxies[p].co = NULL; + } + + MEM_SAFE_FREE(node->proxies); + node->proxies = NULL; + + node->proxy_count = 0; +} + +void BKE_pbvh_gather_proxies(PBVH *pbvh, PBVHNode ***r_array, int *r_tot) +{ + PBVHNode **array = NULL; + int tot = 0, space = 0; + + for (int n = 0; n < pbvh->totnode; n++) { + PBVHNode *node = pbvh->nodes + n; + + if (node->proxy_count > 0) { + if (tot == space) { + /* resize array if needed */ + space = (tot == 0) ? 32 : space * 2; + array = MEM_recallocN_id(array, sizeof(PBVHNode *) * space, __func__); + } + + array[tot] = node; + tot++; + } + } + + if (tot == 0 && array) { + MEM_freeN(array); + array = NULL; + } + + *r_array = array; + *r_tot = tot; +} + +void pbvh_vertex_iter_init(PBVH *pbvh, PBVHNode *node, PBVHVertexIter *vi, int mode) +{ + struct CCGElem **grids; + int *grid_indices; + int totgrid, gridsize, uniq_verts, totvert; + + vi->grid = NULL; + vi->no = NULL; + vi->fno = NULL; + vi->vert_positions = NULL; + vi->vertex.i = 0LL; + vi->index = 0; + + vi->respect_hide = pbvh->respect_hide; + if (pbvh->respect_hide == false) { + /* The same value for all vertices. */ + vi->visible = true; + } + + BKE_pbvh_node_get_grids(pbvh, node, &grid_indices, &totgrid, NULL, &gridsize, &grids); + BKE_pbvh_node_num_verts(pbvh, node, &uniq_verts, &totvert); + const int *vert_indices = BKE_pbvh_node_get_vert_indices(node); + vi->key = pbvh->gridkey; + + vi->grids = grids; + vi->grid_indices = grid_indices; + vi->totgrid = (grids) ? totgrid : 1; + vi->gridsize = gridsize; + + if (mode == PBVH_ITER_ALL) { + vi->totvert = totvert; + } + else { + vi->totvert = uniq_verts; + } + vi->vert_indices = vert_indices; + vi->vert_positions = pbvh->vert_positions; + vi->is_mesh = pbvh->vert_positions != NULL; + + if (pbvh->header.type == PBVH_BMESH) { + if (mode == PBVH_ITER_ALL) { + pbvh_bmesh_check_other_verts(node); + } + + vi->vert_positions = NULL; + + vi->bi = 0; + vi->bm_cur_set = node->bm_unique_verts; + vi->bm_unique_verts = node->bm_unique_verts; + vi->bm_other_verts = node->bm_other_verts; + vi->bm_vdata = &pbvh->header.bm->vdata; + vi->bm_vert = NULL; + + vi->cd_sculpt_vert = CustomData_get_offset(vi->bm_vdata, CD_DYNTOPO_VERT); + vi->cd_vert_mask_offset = CustomData_get_offset(vi->bm_vdata, CD_PAINT_MASK); + } + + vi->gh = NULL; + if (vi->grids && mode == PBVH_ITER_UNIQUE) { + vi->grid_hidden = pbvh->grid_hidden; + } + + vi->mask = NULL; + if (pbvh->header.type == PBVH_FACES) { + vi->vert_normals = pbvh->vert_normals; + vi->hide_vert = pbvh->hide_vert; + + vi->vmask = CustomData_get_layer_for_write(pbvh->vdata, CD_PAINT_MASK, pbvh->mesh->totvert); + } +} + +bool BKE_pbvh_draw_mask(const PBVH *pbvh) +{ + return BKE_pbvh_has_mask(pbvh) && !(pbvh->flags & PBVH_FAST_DRAW); +} + +bool BKE_pbvh_has_mask(const PBVH *pbvh) +{ + switch (pbvh->header.type) { + case PBVH_GRIDS: + return (pbvh->gridkey.has_mask != 0); + case PBVH_FACES: + return (pbvh->vdata && CustomData_get_layer(pbvh->vdata, CD_PAINT_MASK)); + case PBVH_BMESH: + return (pbvh->header.bm && + (CustomData_get_offset(&pbvh->header.bm->vdata, CD_PAINT_MASK) != -1)); + } + + return false; +} + +bool BKE_pbvh_draw_face_sets(PBVH *pbvh) +{ + if (pbvh->flags & PBVH_FAST_DRAW) { + return false; + } + + switch (pbvh->header.type) { + case PBVH_GRIDS: + case PBVH_FACES: + return pbvh->pdata && + CustomData_get_layer_named(pbvh->pdata, CD_PROP_INT32, ".sculpt_face_set") != NULL; + case PBVH_BMESH: + return (pbvh->header.bm && CustomData_get_named_layer_index(&pbvh->header.bm->pdata, + CD_PROP_INT32, + ".sculpt_face_set") != -1); + } + + return false; +} + +void BKE_pbvh_set_frustum_planes(PBVH *pbvh, PBVHFrustumPlanes *planes) +{ + pbvh->num_planes = planes->num_planes; + for (int i = 0; i < pbvh->num_planes; i++) { + copy_v4_v4(pbvh->planes[i], planes->planes[i]); + } +} + +void BKE_pbvh_get_frustum_planes(PBVH *pbvh, PBVHFrustumPlanes *planes) +{ + planes->num_planes = pbvh->num_planes; + for (int i = 0; i < planes->num_planes; i++) { + copy_v4_v4(planes->planes[i], pbvh->planes[i]); + } +} + +#include "BKE_global.h" +void BKE_pbvh_parallel_range_settings(TaskParallelSettings *settings, + bool use_threading, + int totnode) +{ + memset(settings, 0, sizeof(*settings)); + settings->use_threading = use_threading && totnode > 1 && G.debug_value != 890; +} + +float (*BKE_pbvh_get_vert_positions(const PBVH *pbvh))[3] +{ + BLI_assert(pbvh->header.type == PBVH_FACES); + return pbvh->vert_positions; +} + +const float (*BKE_pbvh_get_vert_normals(const PBVH *pbvh))[3] +{ + BLI_assert(pbvh->header.type == PBVH_FACES); + return pbvh->vert_normals; +} + +const bool *BKE_pbvh_get_vert_hide(const PBVH *pbvh) +{ + BLI_assert(pbvh->header.type == PBVH_FACES); + return pbvh->hide_vert; +} + +const bool *BKE_pbvh_get_poly_hide(const PBVH *pbvh) +{ + BLI_assert(ELEM(pbvh->header.type, PBVH_FACES, PBVH_GRIDS)); + return pbvh->hide_poly; +} + +bool *BKE_pbvh_get_vert_hide_for_write(PBVH *pbvh) +{ + BLI_assert(pbvh->header.type == PBVH_FACES); + if (pbvh->hide_vert) { + return pbvh->hide_vert; + } + pbvh->hide_vert = CustomData_get_layer_named_for_write( + &pbvh->mesh->vdata, CD_PROP_BOOL, ".hide_vert", pbvh->mesh->totvert); + if (pbvh->hide_vert) { + return pbvh->hide_vert; + } + pbvh->hide_vert = (bool *)CustomData_add_layer_named( + &pbvh->mesh->vdata, CD_PROP_BOOL, CD_SET_DEFAULT, NULL, pbvh->mesh->totvert, ".hide_vert"); + return pbvh->hide_vert; +} + +void BKE_pbvh_subdiv_ccg_set(PBVH *pbvh, SubdivCCG *subdiv_ccg) +{ + pbvh->subdiv_ccg = subdiv_ccg; + pbvh->gridfaces = (void **)subdiv_ccg->grid_faces; + pbvh->grid_hidden = subdiv_ccg->grid_hidden; + pbvh->grid_flag_mats = subdiv_ccg->grid_flag_mats; + pbvh->grids = subdiv_ccg->grids; +} + +void BKE_pbvh_face_sets_set(PBVH *pbvh, int *face_sets) +{ + pbvh->face_sets = face_sets; +} + +void BKE_pbvh_update_hide_attributes_from_mesh(PBVH *pbvh) +{ + if (pbvh->header.type == PBVH_FACES) { + pbvh->hide_vert = CustomData_get_layer_named_for_write( + &pbvh->mesh->vdata, CD_PROP_BOOL, ".hide_vert", pbvh->mesh->totvert); + pbvh->hide_poly = CustomData_get_layer_named_for_write( + &pbvh->mesh->pdata, CD_PROP_BOOL, ".hide_poly", pbvh->mesh->totpoly); + } +} + +void BKE_pbvh_respect_hide_set(PBVH *pbvh, bool respect_hide) +{ + pbvh->respect_hide = respect_hide; +} + +int BKE_pbvh_get_node_index(PBVH *pbvh, PBVHNode *node) +{ + return (int)(node - pbvh->nodes); +} + +int BKE_pbvh_get_totnodes(PBVH *pbvh) +{ + return pbvh->totnode; +} + +int BKE_pbvh_get_node_id(PBVH *pbvh, PBVHNode *node) +{ + return node->id; +} + +void BKE_pbvh_get_nodes(PBVH *pbvh, int flag, PBVHNode ***r_array, int *r_totnode) +{ + BKE_pbvh_search_gather(pbvh, update_search_cb, POINTER_FROM_INT(flag), r_array, r_totnode); +} + +PBVHNode *BKE_pbvh_node_from_index(PBVH *pbvh, int node_i) +{ + return pbvh->nodes + node_i; +} + +#ifdef PROXY_ADVANCED +// TODO: if this really works, make sure to pull the neighbor iterator out of sculpt.c and put it +// here +/* clang-format off */ +# include "BKE_context.h" +# include "DNA_object_types.h" +# include "DNA_scene_types.h" +# include "../../editors/sculpt_paint/sculpt_intern.h" +/* clang-format on */ + +int checkalloc(void **data, int esize, int oldsize, int newsize, int emask, int umask) +{ + // update channel if it already was allocated once, or is requested by umask + if (newsize != oldsize && (*data || (emask & umask))) { + if (*data) { + *data = MEM_reallocN(*data, newsize * esize); + } + else { + *data = MEM_mallocN(newsize * esize, "pbvh proxy vert arrays"); + } + return emask; + } + + return 0; +} + +void BKE_pbvh_ensure_proxyarray_indexmap(PBVH *pbvh, PBVHNode *node, GHash *vert_node_map) +{ + ProxyVertArray *p = &node->proxyverts; + + int totvert = 0; + BKE_pbvh_node_num_verts(pbvh, node, &totvert, NULL); + + bool update = !p->indexmap || p->size != totvert; + update = update || (p->indexmap && BLI_ghash_len(p->indexmap) != totvert); + + if (!update) { + return; + } + + if (p->indexmap) { + BLI_ghash_free(p->indexmap, NULL, NULL); + } + + GHash *gs = p->indexmap = BLI_ghash_ptr_new("BKE_pbvh_ensure_proxyarray_indexmap"); + + PBVHVertexIter vd; + + int i = 0; + BKE_pbvh_vertex_iter_begin (pbvh, node, vd, PBVH_ITER_UNIQUE) { + BLI_ghash_insert(gs, (void *)vd.vertex.i, (void *)i); + i++; + } + BKE_pbvh_vertex_iter_end; +} + +bool pbvh_proxyarray_needs_update(PBVH *pbvh, PBVHNode *node, int mask) +{ + ProxyVertArray *p = &node->proxyverts; + int totvert = 0; + + if (!(node->flag & PBVH_Leaf) || !node->bm_unique_verts) { + return false; + } + + BKE_pbvh_node_num_verts(pbvh, node, &totvert, NULL); + + bool bad = p->size != totvert; + bad = bad || ((mask & PV_NEIGHBORS) && p->neighbors_dirty); + bad = bad || (p->datamask & mask) != mask; + + bad = bad && totvert > 0; + + return bad; +} + +GHash *pbvh_build_vert_node_map(PBVH *pbvh, PBVHNode **nodes, int totnode, int mask) +{ + GHash *vert_node_map = BLI_ghash_ptr_new("BKE_pbvh_ensure_proxyarrays"); + + for (int i = 0; i < totnode; i++) { + PBVHVertexIter vd; + PBVHNode *node = nodes[i]; + + if (!(node->flag & PBVH_Leaf)) { + continue; + } + + BKE_pbvh_vertex_iter_begin (pbvh, node, vd, PBVH_ITER_UNIQUE) { + BLI_ghash_insert(vert_node_map, (void *)vd.vertex.i, (void *)(node - pbvh->nodes)); + } + BKE_pbvh_vertex_iter_end; + } + + return vert_node_map; +} + +void BKE_pbvh_ensure_proxyarrays( + SculptSession *ss, PBVH *pbvh, PBVHNode **nodes, int totnode, int mask) +{ + + bool update = false; + + for (int i = 0; i < totnode; i++) { + if (pbvh_proxyarray_needs_update(pbvh, nodes[i], mask)) { + update = true; + break; + } + } + + if (!update) { + return; + } + + GHash *vert_node_map = pbvh_build_vert_node_map(pbvh, nodes, totnode, mask); + + for (int i = 0; i < totnode; i++) { + if (nodes[i]->flag & PBVH_Leaf) { + BKE_pbvh_ensure_proxyarray_indexmap(pbvh, nodes[i], vert_node_map); + } + } + + for (int i = 0; i < totnode; i++) { + if (nodes[i]->flag & PBVH_Leaf) { + BKE_pbvh_ensure_proxyarray(ss, pbvh, nodes[i], mask, vert_node_map, false, false); + } + } + + if (vert_node_map) { + BLI_ghash_free(vert_node_map, NULL, NULL); + } +} + +void BKE_pbvh_ensure_proxyarray(SculptSession *ss, + PBVH *pbvh, + PBVHNode *node, + int mask, + GHash *vert_node_map, + bool check_indexmap, + bool force_update) +{ + ProxyVertArray *p = &node->proxyverts; + + if (check_indexmap) { + BKE_pbvh_ensure_proxyarray_indexmap(pbvh, node, vert_node_map); + } + + GHash *gs = p->indexmap; + + int totvert = 0; + BKE_pbvh_node_num_verts(pbvh, node, &totvert, NULL); + + if (!totvert) { + return; + } + + int updatemask = 0; + +# define UPDATETEST(name, emask, esize) \ + if (mask & emask) { \ + updatemask |= checkalloc((void **)&p->name, esize, p->size, totvert, emask, mask); \ + } + + UPDATETEST(ownerco, PV_OWNERCO, sizeof(void *)) + UPDATETEST(ownerno, PV_OWNERNO, sizeof(void *)) + UPDATETEST(ownermask, PV_OWNERMASK, sizeof(void *)) + UPDATETEST(ownercolor, PV_OWNERCOLOR, sizeof(void *)) + UPDATETEST(co, PV_CO, sizeof(float) * 3) + UPDATETEST(no, PV_NO, sizeof(short) * 3) + UPDATETEST(fno, PV_NO, sizeof(float) * 3) + UPDATETEST(mask, PV_MASK, sizeof(float)) + UPDATETEST(color, PV_COLOR, sizeof(float) * 4) + UPDATETEST(index, PV_INDEX, sizeof(PBVHVertRef)) + UPDATETEST(neighbors, PV_NEIGHBORS, sizeof(ProxyKey) * MAX_PROXY_NEIGHBORS) + + p->size = totvert; + + if (force_update) { + updatemask |= mask; + } + + if ((mask & PV_NEIGHBORS) && p->neighbors_dirty) { + updatemask |= PV_NEIGHBORS; + } + + if (!updatemask) { + return; + } + + p->datamask |= mask; + + PBVHVertexIter vd; + + int i = 0; + +# if 1 + BKE_pbvh_vertex_iter_begin (pbvh, node, vd, PBVH_ITER_UNIQUE) { + void **val; + + if (!BLI_ghash_ensure_p(gs, (void *)vd.vertex.i, &val)) { + *val = (void *)i; + }; + i++; + } + BKE_pbvh_vertex_iter_end; +# endif + + if (updatemask & PV_NEIGHBORS) { + p->neighbors_dirty = false; + } + + i = 0; + BKE_pbvh_vertex_iter_begin (pbvh, node, vd, PBVH_ITER_UNIQUE) { + if (i >= p->size) { + printf("error!! %s\n", __func__); + break; + } + + if (updatemask & PV_OWNERCO) { + p->ownerco[i] = vd.co; + } + if (updatemask & PV_INDEX) { + p->index[i] = vd.vertex; + } + if (updatemask & PV_OWNERNO) { + p->ownerno[i] = vd.no; + } + if (updatemask & PV_NO) { + if (vd.fno) { + if (p->fno) { + copy_v3_v3(p->fno[i], vd.fno); + } + normal_float_to_short_v3(p->no[i], vd.fno); + } + else if (vd.no) { + copy_v3_v3_short(p->no[i], vd.no); + if (p->fno) { + normal_short_to_float_v3(p->fno[i], vd.no); + } + } + else { + p->no[i][0] = p->no[i][1] = p->no[i][2] = 0; + if (p->fno) { + zero_v3(p->fno[i]); + } + } + } + if (updatemask & PV_CO) { + copy_v3_v3(p->co[i], vd.co); + } + if (updatemask & PV_OWNERMASK) { + p->ownermask[i] = vd.mask; + } + if (updatemask & PV_MASK) { + p->mask[i] = vd.mask ? *vd.mask : 0.0f; + } + if (updatemask & PV_COLOR) { + if (vd.vcol) { + copy_v4_v4(p->color[i], vd.vcol->color); + } + } + + if (updatemask & PV_NEIGHBORS) { + int j = 0; + SculptVertexNeighborIter ni; + + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.vertex, ni) { + if (j >= MAX_PROXY_NEIGHBORS - 1) { + break; + } + + ProxyKey key; + + int *pindex = (int *)BLI_ghash_lookup_p(gs, (void *)ni.vertex.i); + + if (!pindex) { + if (vert_node_map) { + int *nindex = (int *)BLI_ghash_lookup_p(vert_node_map, (void *)ni.vertex.i); + + if (!nindex) { + p->neighbors_dirty = true; + continue; + } + + PBVHNode *node2 = pbvh->nodes + *nindex; + if (node2->proxyverts.indexmap) { + pindex = (int *)BLI_ghash_lookup_p(node2->proxyverts.indexmap, (void *)ni.vertex.i); + } + else { + pindex = NULL; + } + + if (!pindex) { + p->neighbors_dirty = true; + continue; + } + + key.node = (int)(node2 - pbvh->nodes); + key.pindex = *pindex; + //* + if (node2->proxyverts.size != 0 && + (key.pindex < 0 || key.pindex >= node2->proxyverts.size)) { + printf("error! %s\n", __func__); + fflush(stdout); + p->neighbors_dirty = true; + continue; + } + //*/ + } + else { + p->neighbors_dirty = true; + continue; + } + } + else { + key.node = (int)(node - pbvh->nodes); + key.pindex = *pindex; + } + + p->neighbors[i][j++] = key; + } + SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); + + p->neighbors[i][j].node = -1; + } + + i++; + } + BKE_pbvh_vertex_iter_end; +} + +typedef struct GatherProxyThread { + PBVHNode **nodes; + PBVH *pbvh; + int mask; +} GatherProxyThread; + +static void pbvh_load_proxyarray_exec(void *__restrict userdata, + const int n, + const TaskParallelTLS *__restrict tls) +{ + GatherProxyThread *data = (GatherProxyThread *)userdata; + PBVHNode *node = data->nodes[n]; + PBVHVertexIter vd; + ProxyVertArray *p = &node->proxyverts; + int i = 0; + + int mask = p->datamask; + + BKE_pbvh_ensure_proxyarray(NULL, data->pbvh, node, data->mask, NULL, false, true); +} + +void BKE_pbvh_load_proxyarrays(PBVH *pbvh, PBVHNode **nodes, int totnode, int mask) +{ + GatherProxyThread data = {.nodes = nodes, .pbvh = pbvh, .mask = mask}; + + mask = mask & ~PV_NEIGHBORS; // don't update neighbors in threaded code? + + TaskParallelSettings settings; + BKE_pbvh_parallel_range_settings(&settings, true, totnode); + BLI_task_parallel_range(0, totnode, &data, pbvh_load_proxyarray_exec, &settings); +} + +static void pbvh_gather_proxyarray_exec(void *__restrict userdata, + const int n, + const TaskParallelTLS *__restrict tls) +{ + GatherProxyThread *data = (GatherProxyThread *)userdata; + PBVHNode *node = data->nodes[n]; + PBVHVertexIter vd; + ProxyVertArray *p = &node->proxyverts; + int i = 0; + + int mask = p->datamask; + + BKE_pbvh_vertex_iter_begin (data->pbvh, node, vd, PBVH_ITER_UNIQUE) { + if (mask & PV_CO) { + copy_v3_v3(vd.co, p->co[i]); + } + + if (mask & PV_COLOR && vd.col) { + copy_v4_v4(vd.col, p->color[i]); + } + + if (vd.mask && (mask & PV_MASK)) { + *vd.mask = p->mask[i]; + } + + i++; + } + BKE_pbvh_vertex_iter_end; +} + +void BKE_pbvh_gather_proxyarray(PBVH *pbvh, PBVHNode **nodes, int totnode) +{ + GatherProxyThread data = {.nodes = nodes, .pbvh = pbvh}; + + TaskParallelSettings settings; + BKE_pbvh_parallel_range_settings(&settings, true, totnode); + BLI_task_parallel_range(0, totnode, &data, pbvh_gather_proxyarray_exec, &settings); +} + +void BKE_pbvh_free_proxyarray(PBVH *pbvh, PBVHNode *node) +{ + ProxyVertArray *p = &node->proxyverts; + + if (p->indexmap) { + BLI_ghash_free(p->indexmap, NULL, NULL); + } + if (p->co) + MEM_freeN(p->co); + if (p->no) + MEM_freeN(p->no); + if (p->index) + MEM_freeN(p->index); + if (p->mask) + MEM_freeN(p->mask); + if (p->ownerco) + MEM_freeN(p->ownerco); + if (p->ownerno) + MEM_freeN(p->ownerno); + if (p->ownermask) + MEM_freeN(p->ownermask); + if (p->ownercolor) + MEM_freeN(p->ownercolor); + if (p->color) + MEM_freeN(p->color); + if (p->neighbors) + MEM_freeN(p->neighbors); + + memset(p, 0, sizeof(*p)); +} + +void BKE_pbvh_update_proxyvert(PBVH *pbvh, PBVHNode *node, ProxyVertUpdateRec *rec) +{ +} + +ProxyVertArray *BKE_pbvh_get_proxyarrays(PBVH *pbvh, PBVHNode *node) +{ + return &node->proxyverts; +} + +#endif + +/* checks if pbvh needs to sync its flat vcol shading flag with scene tool settings + scene and ob are allowd to be NULL (in which case nothing is done). +*/ +void SCULPT_update_flat_vcol_shading(Object *ob, Scene *scene) +{ + if (!scene || !ob || !ob->sculpt || !ob->sculpt->pbvh) { + return; + } + + if (ob->sculpt->pbvh) { + bool flat_vcol_shading = ((scene->toolsettings->sculpt->flags & + SCULPT_DYNTOPO_FLAT_VCOL_SHADING) != 0); + + BKE_pbvh_set_flat_vcol_shading(ob->sculpt->pbvh, flat_vcol_shading); + } +} + +PBVHNode *BKE_pbvh_get_node(PBVH *pbvh, int node) +{ + return pbvh->nodes + node; +} + +bool BKE_pbvh_node_mark_update_index_buffer(PBVH *pbvh, PBVHNode *node) +{ + bool split_indexed = pbvh->header.bm && + (pbvh->flags & (PBVH_DYNTOPO_SMOOTH_SHADING | PBVH_FAST_DRAW)); + + if (split_indexed) { + BKE_pbvh_vert_tag_update_normal_triangulation(node); + } + + return split_indexed; +} + +void BKE_pbvh_vert_tag_update_normal_triangulation(PBVHNode *node) +{ + node->flag |= PBVH_UpdateTris; +} + +void BKE_pbvh_vert_tag_update_normal_tri_area(PBVHNode *node) +{ + node->flag |= PBVH_UpdateTriAreas; +} + +/* must be called outside of threads */ +void BKE_pbvh_face_areas_begin(PBVH *pbvh) +{ + pbvh->face_area_i ^= 1; +} + +void BKE_pbvh_update_all_tri_areas(PBVH *pbvh) +{ + /* swap read/write face area buffers */ + pbvh->face_area_i ^= 1; + + for (int i = 0; i < pbvh->totnode; i++) { + PBVHNode *node = pbvh->nodes + i; + if (node->flag & PBVH_Leaf) { + node->flag |= PBVH_UpdateTriAreas; +#if 0 + // ensure node triangulations are valid + // so we don't end up doing it inside brush threads + BKE_pbvh_bmesh_check_tris(pbvh, node); +#endif + } + } +} + +void BKE_pbvh_check_tri_areas(PBVH *pbvh, PBVHNode *node) +{ + if (!(node->flag & PBVH_UpdateTriAreas)) { + return; + } + + if (pbvh->header.type == PBVH_BMESH) { + if (node->flag & PBVH_UpdateTris) { + BKE_pbvh_bmesh_check_tris(pbvh, node); + } + + if (!node->tribuf || !node->tribuf->tottri) { + return; + } + } + + node->flag &= ~PBVH_UpdateTriAreas; + + const int cur_i = pbvh->face_area_i ^ 1; + + switch (BKE_pbvh_type(pbvh)) { + case PBVH_FACES: { + for (int i = 0; i < (int)node->totprim; i++) { + const MLoopTri *lt = &pbvh->looptri[node->prim_indices[i]]; + + if (pbvh->hide_poly && pbvh->hide_poly[lt->poly]) { + /* Skip hidden faces. */ + continue; + } + + pbvh->face_areas[lt->poly * 2 + cur_i] = 0.0f; + } + + for (int i = 0; i < (int)node->totprim; i++) { + const MLoopTri *lt = &pbvh->looptri[node->prim_indices[i]]; + + if (pbvh->hide_poly && pbvh->hide_poly[lt->poly]) { + /* Skip hidden faces. */ + continue; + } + + float area = area_tri_v3(pbvh->vert_positions[pbvh->mloop[lt->tri[0]].v], + pbvh->vert_positions[pbvh->mloop[lt->tri[1]].v], + pbvh->vert_positions[pbvh->mloop[lt->tri[2]].v]); + + pbvh->face_areas[lt->poly * 2 + cur_i] += area; + + /* sanity check on read side of read write buffer */ + if (pbvh->face_areas[lt->poly * 2 + (cur_i ^ 1)] == 0.0f) { + pbvh->face_areas[lt->poly * 2 + (cur_i ^ 1)] = pbvh->face_areas[lt->poly * 2 + cur_i]; + } + } + break; + } + case PBVH_GRIDS: + break; + case PBVH_BMESH: { + BMFace *f; + const int cd_face_area = pbvh->cd_face_area; + + TGSET_ITER (f, node->bm_faces) { + float *areabuf = BM_ELEM_CD_GET_VOID_P(f, cd_face_area); + areabuf[cur_i] = 0.0f; + } + TGSET_ITER_END; + + for (int i = 0; i < node->tribuf->tottri; i++) { + PBVHTri *tri = node->tribuf->tris + i; + + BMVert *v1 = (BMVert *)(node->tribuf->verts[tri->v[0]].i); + BMVert *v2 = (BMVert *)(node->tribuf->verts[tri->v[1]].i); + BMVert *v3 = (BMVert *)(node->tribuf->verts[tri->v[2]].i); + BMFace *f = (BMFace *)tri->f.i; + + float *areabuf = BM_ELEM_CD_GET_VOID_P(f, cd_face_area); + areabuf[cur_i] += area_tri_v3(v1->co, v2->co, v3->co); + } + + TGSET_ITER (f, node->bm_faces) { + float *areabuf = BM_ELEM_CD_GET_VOID_P(f, cd_face_area); + + /* sanity check on read side of read write buffer */ + if (areabuf[cur_i ^ 1] == 0.0f) { + areabuf[cur_i ^ 1] = areabuf[cur_i]; + } + } + TGSET_ITER_END; + + break; + } + default: + break; + } +} + +static void pbvh_pmap_to_edges_add(PBVH *pbvh, + PBVHVertRef vertex, + int **r_edges, + int *r_edges_size, + bool *heap_alloc, + int e, + int p, + int *len, + int **r_polys) +{ + for (int i = 0; i < *len; i++) { + if ((*r_edges)[i] == e) { + if ((*r_polys)[i * 2 + 1] == -1) { + (*r_polys)[i * 2 + 1] = p; + } + return; + } + } + + if (*len >= *r_edges_size) { + int newsize = *len + ((*len) >> 1) + 1; + + int *r_edges_new = MEM_malloc_arrayN(newsize, sizeof(*r_edges_new), "r_edges_new"); + int *r_polys_new = MEM_malloc_arrayN(newsize * 2, sizeof(*r_polys_new), "r_polys_new"); + + memcpy((void *)r_edges_new, (void *)*r_edges, sizeof(int) * (*r_edges_size)); + memcpy((void *)r_polys_new, (void *)(*r_polys), sizeof(int) * 2 * (*r_edges_size)); + + *r_edges_size = newsize; + + if (*heap_alloc) { + MEM_freeN(*r_polys); + MEM_freeN(*r_edges); + } + + *r_edges = r_edges_new; + *r_polys = r_polys_new; + + *heap_alloc = true; + } + + (*r_polys)[*len * 2] = p; + (*r_polys)[*len * 2 + 1] = -1; + + (*r_edges)[*len] = e; + (*len)++; +} + +void BKE_pbvh_pmap_to_edges(PBVH *pbvh, + PBVHVertRef vertex, + int **r_edges, + int *r_edges_size, + bool *r_heap_alloc, + int **r_polys) +{ + MeshElemMap *map = pbvh->pmap->pmap + vertex.i; + int len = 0; + + for (int i = 0; i < map->count; i++) { + const MPoly *mp = pbvh->mpoly + map->indices[i]; + const MLoop *ml = pbvh->mloop + mp->loopstart; + + if (pbvh->hide_poly && pbvh->hide_poly[map->indices[i]]) { + /* Skip connectivity from hidden faces. */ + continue; + } + + for (int j = 0; j < mp->totloop; j++, ml++) { + if (ml->v == vertex.i) { + pbvh_pmap_to_edges_add(pbvh, + vertex, + r_edges, + r_edges_size, + r_heap_alloc, + ME_POLY_LOOP_PREV(pbvh->mloop, mp, j)->e, + map->indices[i], + &len, + r_polys); + pbvh_pmap_to_edges_add(pbvh, + vertex, + r_edges, + r_edges_size, + r_heap_alloc, + ml->e, + map->indices[i], + &len, + r_polys); + } + } + } + + *r_edges_size = len; +} + +void BKE_pbvh_set_vemap(PBVH *pbvh, MeshElemMap *vemap) +{ + pbvh->vemap = vemap; +} + +void BKE_pbvh_get_vert_face_areas(PBVH *pbvh, PBVHVertRef vertex, float *r_areas, int valence) +{ + const int cur_i = pbvh->face_area_i; + + switch (BKE_pbvh_type(pbvh)) { + case PBVH_FACES: { + int *edges = BLI_array_alloca(edges, 16); + int *polys = BLI_array_alloca(polys, 32); + bool heap_alloc = false; + int len = 16; + + BKE_pbvh_pmap_to_edges(pbvh, vertex, &edges, &len, &heap_alloc, &polys); + len = MIN2(len, valence); + + if (pbvh->vemap) { + /* sort poly references by vemap edge ordering */ + MeshElemMap *emap = pbvh->vemap + vertex.i; + + int *polys_old = BLI_array_alloca(polys, len * 2); + memcpy((void *)polys_old, (void *)polys, sizeof(int) * len * 2); + + /* note that wire edges will break this, but + should only result in incorrect weights + and isn't worth fixing */ + + for (int i = 0; i < len; i++) { + for (int j = 0; j < len; j++) { + if (emap->indices[i] == edges[j]) { + polys[i * 2] = polys_old[j * 2]; + polys[i * 2 + 1] = polys_old[j * 2 + 1]; + } + } + } + } + for (int i = 0; i < len; i++) { + r_areas[i] = pbvh->face_areas[polys[i * 2] * 2 + cur_i]; + + if (polys[i * 2 + 1] != -1) { + r_areas[i] += pbvh->face_areas[polys[i * 2 + 1] * 2 + cur_i]; + r_areas[i] *= 0.5f; + } + } + + if (heap_alloc) { + MEM_freeN(edges); + MEM_freeN(polys); + } + + break; + } + case PBVH_BMESH: { + BMVert *v = (BMVert *)vertex.i; + BMEdge *e = v->e; + + if (!e) { + for (int i = 0; i < valence; i++) { + r_areas[i] = 1.0f; + } + + return; + } + + const int cd_face_area = pbvh->cd_face_area; + int j = 0; + + do { + float w = 0.0f; + + if (!e->l) { + w = 0.0f; + } + else { + float *a1 = BM_ELEM_CD_GET_VOID_P(e->l->f, cd_face_area); + float *a2 = BM_ELEM_CD_GET_VOID_P(e->l->radial_next->f, cd_face_area); + + w += a1[cur_i] * 0.5f; + w += a2[cur_i] * 0.5f; + } + + if (j >= valence) { + printf("%s: error, corrupt edge cycle\n", __func__); + break; + } + + r_areas[j++] = w; + + e = v == e->v1 ? e->v1_disk_link.next : e->v2_disk_link.next; + } while (e != v->e); + + for (; j < valence; j++) { + r_areas[j] = 1.0f; + } + + break; + } + + case PBVH_GRIDS: { /* estimate from edge lengths */ + int index = (int)vertex.i; + + const CCGKey *key = BKE_pbvh_get_grid_key(pbvh); + const int grid_index = index / key->grid_area; + const int vertex_index = index - grid_index * key->grid_area; + + SubdivCCGCoord coord = {.grid_index = grid_index, + .x = vertex_index % key->grid_size, + .y = vertex_index / key->grid_size}; + + SubdivCCGNeighbors neighbors; + BKE_subdiv_ccg_neighbor_coords_get(pbvh->subdiv_ccg, &coord, false, &neighbors); + + float *co1 = CCG_elem_co(key, CCG_elem_offset(key, pbvh->grids[grid_index], vertex_index)); + float totw = 0.0f; + int i = 0; + + for (i = 0; i < neighbors.size; i++) { + SubdivCCGCoord *coord2 = neighbors.coords + i; + + int vertex_index2 = coord2->y * key->grid_size + coord2->x; + + float *co2 = CCG_elem_co( + key, CCG_elem_offset(key, pbvh->grids[coord2->grid_index], vertex_index2)); + float w = len_v3v3(co1, co2); + + r_areas[i] = w; + totw += w; + } + + if (neighbors.size != valence) { + printf("%s: error!\n", __func__); + } + if (totw < 0.000001f) { + for (int i = 0; i < neighbors.size; i++) { + r_areas[i] = 1.0f; + } + } + + for (; i < valence; i++) { + r_areas[i] = 1.0f; + } + + break; + } + } +} + +void BKE_pbvh_set_stroke_id(PBVH *pbvh, int stroke_id) +{ + pbvh->stroke_id = stroke_id; +} + +static void pbvh_boundaries_flag_update(PBVH *pbvh) +{ + + if (pbvh->header.bm) { + BMVert *v; + BMIter iter; + + BM_ITER_MESH (v, &iter, pbvh->header.bm, BM_VERTS_OF_MESH) { + pbvh_boundary_update_bmesh(pbvh, v); + } + } + else { + int totvert = pbvh->totvert; + + if (BKE_pbvh_type(pbvh) == PBVH_GRIDS) { + totvert = BKE_pbvh_get_grid_num_verts(pbvh); + } + + for (int i = 0; i < totvert; i++) { + pbvh->boundary_flags[i] |= SCULPT_BOUNDARY_NEEDS_UPDATE; + } + } +} + +void BKE_pbvh_set_symmetry(PBVH *pbvh, int symmetry, int boundary_symmetry) +{ + if (symmetry == pbvh->symmetry && boundary_symmetry == pbvh->boundary_symmetry) { + return; + } + + pbvh->symmetry = symmetry; + pbvh->boundary_symmetry = boundary_symmetry; + + pbvh_boundaries_flag_update(pbvh); +} + +void BKE_pbvh_set_sculpt_verts(PBVH *pbvh, struct MSculptVert *msculptverts) +{ + pbvh->msculptverts = msculptverts; +} + +void BKE_pbvh_update_vert_boundary_grids(PBVH *pbvh, + struct SubdivCCG *subdiv_ccg, + PBVHVertRef vertex) +{ + MSculptVert *mv = pbvh->msculptverts + vertex.i; + + int *flags = pbvh->boundary_flags + vertex.i; + *flags = 0; + + /* TODO: finish this function. */ + + int index = (int)vertex.i; + + /* TODO: optimize this. We could fill #SculptVertexNeighborIter directly, + * maybe provide coordinate and mask pointers directly rather than converting + * back and forth between #CCGElem and global index. */ + const CCGKey *key = BKE_pbvh_get_grid_key(pbvh); + const int grid_index = index / key->grid_area; + const int vertex_index = index - grid_index * key->grid_area; + + SubdivCCGCoord coord = {.grid_index = grid_index, + .x = vertex_index % key->grid_size, + .y = vertex_index / key->grid_size}; + + SubdivCCGNeighbors neighbors; + BKE_subdiv_ccg_neighbor_coords_get(subdiv_ccg, &coord, false, &neighbors); + + mv->valence = neighbors.size; + mv->flag &= ~SCULPTVERT_NEED_VALENCE; +} + +void BKE_pbvh_update_vert_boundary_faces(int *boundary_flags, + const int *face_sets, + const bool *hide_poly, + const float (*vert_positions)[3], + const MEdge *medge, + const MLoop *mloop, + const MPoly *mpoly, + MSculptVert *msculptverts, + const MeshElemMap *pmap, + PBVHVertRef vertex, + const bool *sharp_edges) +{ + MSculptVert *mv = msculptverts + vertex.i; + const MeshElemMap *vert_map = &pmap[vertex.i]; + + mv->flag &= ~SCULPTVERT_VERT_FSET_HIDDEN; + + int last_fset = -1; + int last_fset2 = -1; + + int *flags = boundary_flags + vertex.i; + *flags = 0; + + int totsharp = 0, totseam = 0; + int visible = false; + + for (int i = 0; i < vert_map->count; i++) { + int f_i = vert_map->indices[i]; + + const MPoly *mp = mpoly + f_i; + const MLoop *ml = mloop + mp->loopstart; + int j = 0; + + for (j = 0; j < mp->totloop; j++, ml++) { + if (ml->v == (int)vertex.i) { + break; + } + } + + if (j < mp->totloop) { + const MEdge *me = medge + ml->e; + if (sharp_edges && sharp_edges[ml->e]) { + *flags |= SCULPT_BOUNDARY_SHARP; + totsharp++; + } + + if (me->flag & ME_SEAM) { + *flags |= SCULPT_BOUNDARY_SEAM; + totseam++; + } + } + + int fset = face_sets ? abs(face_sets[f_i]) : 1; + + if (!hide_poly || !hide_poly[f_i]) { + visible = true; + } + + if (i > 0 && fset != last_fset) { + *flags |= SCULPT_BOUNDARY_FACE_SET; + + if (i > 1 && last_fset2 != last_fset && last_fset != -1 && last_fset2 != -1 && fset != -1 && + last_fset2 != fset) { + *flags |= SCULPT_CORNER_FACE_SET; + } + } + + if (i > 0 && last_fset != fset) { + last_fset2 = last_fset; + } + + last_fset = fset; + } + + if (!visible) { + mv->flag |= SCULPTVERT_VERT_FSET_HIDDEN; + } + + if (totsharp > 2) { + *flags |= SCULPT_CORNER_SHARP; + } + + if (totseam > 2) { + *flags |= SCULPT_CORNER_SEAM; + } +} + +void BKE_pbvh_ignore_uvs_set(PBVH *pbvh, bool value) +{ + if (!!(pbvh->flags & PBVH_IGNORE_UVS) == value) { + return; // no change + } + + if (value) { + pbvh->flags |= PBVH_IGNORE_UVS; + } + else { + pbvh->flags &= ~PBVH_IGNORE_UVS; + } + + pbvh_boundaries_flag_update(pbvh); +} + +bool BKE_pbvh_cache(const struct Mesh *me, PBVH *pbvh) +{ + memset(&pbvh->cached_data, 0, sizeof(pbvh->cached_data)); + + if (pbvh->invalid) { + printf("invalid pbvh!\n"); + return false; + } + + switch (pbvh->header.type) { + case PBVH_BMESH: + if (!pbvh->header.bm) { + return false; + } + + pbvh->cached_data.bm = pbvh->header.bm; + + pbvh->cached_data.vdata = pbvh->header.bm->vdata; + pbvh->cached_data.edata = pbvh->header.bm->edata; + pbvh->cached_data.ldata = pbvh->header.bm->ldata; + pbvh->cached_data.pdata = pbvh->header.bm->pdata; + + pbvh->cached_data.totvert = pbvh->header.bm->totvert; + pbvh->cached_data.totedge = pbvh->header.bm->totedge; + pbvh->cached_data.totloop = pbvh->header.bm->totloop; + pbvh->cached_data.totpoly = pbvh->header.bm->totface; + break; + case PBVH_GRIDS: + pbvh->cached_data.vdata = me->vdata; + pbvh->cached_data.edata = me->edata; + pbvh->cached_data.ldata = me->ldata; + pbvh->cached_data.pdata = me->pdata; + + int grid_side = pbvh->gridkey.grid_size; + + pbvh->cached_data.totvert = pbvh->totgrid * grid_side * grid_side; + pbvh->cached_data.totedge = me->totedge; + pbvh->cached_data.totloop = me->totloop; + pbvh->cached_data.totpoly = pbvh->totgrid * (grid_side - 1) * (grid_side - 1); + break; + case PBVH_FACES: + pbvh->cached_data.vdata = me->vdata; + pbvh->cached_data.edata = me->edata; + pbvh->cached_data.ldata = me->ldata; + pbvh->cached_data.pdata = me->pdata; + + pbvh->cached_data.totvert = me->totvert; + pbvh->cached_data.totedge = me->totedge; + pbvh->cached_data.totloop = me->totloop; + pbvh->cached_data.totpoly = me->totpoly; + break; + } + + return true; +} + +static bool customdata_is_same(const CustomData *a, const CustomData *b) +{ + return memcmp(a, b, sizeof(CustomData)) == 0; +} + +bool BKE_pbvh_cache_is_valid(const struct Object *ob, + const struct Mesh *me, + const PBVH *pbvh, + int pbvh_type) +{ + if (pbvh->invalid) { + printf("pbvh invalid!\n"); + return false; + } + + if (pbvh->header.type != pbvh_type) { + return false; + } + + bool ok = true; + int totvert = 0, totedge = 0, totloop = 0, totpoly = 0; + const CustomData *vdata, *edata, *ldata, *pdata; + + MultiresModifierData *mmd = NULL; + + LISTBASE_FOREACH (ModifierData *, md, &ob->modifiers) { + if (md->type == eModifierType_Multires) { + mmd = (MultiresModifierData *)md; + break; + } + } + + if (mmd && (mmd->flags & eModifierMode_Realtime)) { + // return false; + } + + switch (pbvh_type) { + case PBVH_BMESH: + if (!pbvh->header.bm || pbvh->header.bm != pbvh->cached_data.bm) { + return false; + } + + totvert = pbvh->header.bm->totvert; + totedge = pbvh->header.bm->totedge; + totloop = pbvh->header.bm->totloop; + totpoly = pbvh->header.bm->totface; + + vdata = &pbvh->header.bm->vdata; + edata = &pbvh->header.bm->edata; + ldata = &pbvh->header.bm->ldata; + pdata = &pbvh->header.bm->pdata; + break; + case PBVH_FACES: + totvert = me->totvert; + totedge = me->totedge; + totloop = me->totloop; + totpoly = me->totpoly; + + vdata = &me->vdata; + edata = &me->edata; + ldata = &me->ldata; + pdata = &me->pdata; + break; + case PBVH_GRIDS: { + if (!mmd) { + return false; + } + + int grid_side = 1 + (1 << (mmd->sculptlvl - 1)); + + totvert = me->totloop * grid_side * grid_side; + totedge = me->totedge; + totloop = me->totloop; + totpoly = me->totloop * (grid_side - 1) * (grid_side - 1); + + vdata = &me->vdata; + edata = &me->edata; + ldata = &me->ldata; + pdata = &me->pdata; + break; + } + } + + ok = ok && totvert == pbvh->cached_data.totvert; + ok = ok && totedge == pbvh->cached_data.totedge; + ok = ok && totloop == pbvh->cached_data.totloop; + ok = ok && totpoly == pbvh->cached_data.totpoly; + + ok = ok && customdata_is_same(vdata, &pbvh->cached_data.vdata); + ok = ok && customdata_is_same(edata, &pbvh->cached_data.edata); + ok = ok && customdata_is_same(ldata, &pbvh->cached_data.ldata); + ok = ok && customdata_is_same(pdata, &pbvh->cached_data.pdata); + + return ok; +} + +GHash *cached_pbvhs = NULL; +static void pbvh_clear_cached_pbvhs(PBVH *exclude) +{ + PBVH **pbvhs = NULL; + BLI_array_staticdeclare(pbvhs, 8); + + GHashIterator iter; + GHASH_ITER (iter, cached_pbvhs) { + PBVH *pbvh = BLI_ghashIterator_getValue(&iter); + + if (pbvh != exclude) { + BLI_array_append(pbvhs, pbvh); + } + } + + for (int i = 0; i < BLI_array_len(pbvhs); i++) { + PBVH *pbvh = pbvhs[i]; + + if (pbvh->header.bm) { + BM_mesh_free(pbvh->header.bm); + } + + BKE_pbvh_free(pbvh); + } + + BLI_array_free(pbvhs); + BLI_ghash_clear(cached_pbvhs, MEM_freeN, NULL); +} + +void BKE_pbvh_clear_cache(PBVH *preserve) +{ + pbvh_clear_cached_pbvhs(NULL); +} + +#define PBVH_CACHE_KEY_SIZE 1024 + +static void pbvh_make_cached_key(Object *ob, char out[PBVH_CACHE_KEY_SIZE]) +{ + sprintf(out, "%s:%p", ob->id.name, G.main); +} + +void BKE_pbvh_invalidate_cache(Object *ob) +{ + Object *ob_orig = DEG_get_original_object(ob); + + char key[PBVH_CACHE_KEY_SIZE]; + pbvh_make_cached_key(ob_orig, key); + +#ifdef WITH_PBVH_CACHE + PBVH *pbvh = BLI_ghash_lookup(cached_pbvhs, key); + + if (pbvh) { + BKE_pbvh_cache_remove(pbvh); + } +#endif +} + +PBVH *BKE_pbvh_get_or_free_cached(Object *ob, Mesh *me, PBVHType pbvh_type) +{ + Object *ob_orig = DEG_get_original_object(ob); + + char key[PBVH_CACHE_KEY_SIZE]; + pbvh_make_cached_key(ob_orig, key); + + PBVH *pbvh = BLI_ghash_lookup(cached_pbvhs, key); + + if (!pbvh) { + return NULL; + } + + if (BKE_pbvh_cache_is_valid(ob, me, pbvh, pbvh_type)) { + switch (pbvh_type) { + case PBVH_BMESH: + break; + case PBVH_FACES: + pbvh->vert_normals = BKE_mesh_vertex_normals_for_write(me); + case PBVH_GRIDS: + if (!pbvh->deformed) { + pbvh->vert_positions = BKE_mesh_vert_positions_for_write(me); + } + + pbvh->mloop = me->mloop; + pbvh->mpoly = me->mpoly; + pbvh->vdata = &me->vdata; + pbvh->ldata = &me->ldata; + pbvh->pdata = &me->pdata; + + pbvh->face_sets = (int *)CustomData_get_layer_named( + &me->pdata, CD_PROP_INT32, ".sculpt_face_set"); + + break; + } + + BKE_pbvh_update_active_vcol(pbvh, me); + + return pbvh; + } + + pbvh_clear_cached_pbvhs(NULL); + return NULL; +} + +void BKE_pbvh_set_cached(Object *ob, PBVH *pbvh) +{ + if (!pbvh) { + return; + } + + Object *ob_orig = DEG_get_original_object(ob); + + char key[PBVH_CACHE_KEY_SIZE]; + pbvh_make_cached_key(ob_orig, key); + + PBVH *exist = BLI_ghash_lookup(cached_pbvhs, key); + + if (pbvh->invalid) { + printf("pbvh invalid!"); + } + + if (exist && exist->invalid) { + printf("pbvh invalid!"); + } + + if (!exist || exist != pbvh) { + pbvh_clear_cached_pbvhs(pbvh); + + char key[PBVH_CACHE_KEY_SIZE]; + pbvh_make_cached_key(ob_orig, key); + + BLI_ghash_insert(cached_pbvhs, BLI_strdup(key), pbvh); + } + +#ifdef WITH_PBVH_CACHE + BKE_pbvh_cache(BKE_object_get_original_mesh(ob_orig), pbvh); +#endif +} + +struct SculptPMap *BKE_pbvh_get_pmap(PBVH *pbvh) +{ + return pbvh->pmap; +} + +void BKE_pbvh_set_pmap(PBVH *pbvh, SculptPMap *pmap) +{ + if (pbvh->pmap != pmap) { + BKE_pbvh_pmap_aquire(pmap); + } + + pbvh->pmap = pmap; +} + +/** Does not free pbvh itself. */ +void BKE_pbvh_cache_remove(PBVH *pbvh) +{ + char **keys = NULL; + BLI_array_staticdeclare(keys, 32); + + GHashIterator iter; + GHASH_ITER (iter, cached_pbvhs) { + PBVH *pbvh2 = BLI_ghashIterator_getValue(&iter); + + if (pbvh2 == pbvh) { + BLI_array_append(keys, (char *)BLI_ghashIterator_getKey(&iter)); + break; + } + } + + for (int i = 0; i < BLI_array_len(keys); i++) { + BLI_ghash_remove(cached_pbvhs, keys[i], MEM_freeN, NULL); + } + + BLI_array_free(keys); +} + +void BKE_pbvh_set_bmesh(PBVH *pbvh, BMesh *bm) +{ + pbvh->header.bm = bm; +} + +void BKE_pbvh_free_bmesh(PBVH *pbvh, BMesh *bm) +{ + if (pbvh) { + pbvh->header.bm = NULL; + } + + BM_mesh_free(bm); + + GHashIterator iter; + char **keys = NULL; + BLI_array_staticdeclare(keys, 32); + + PBVH **pbvhs = NULL; + BLI_array_staticdeclare(pbvhs, 8); + + GHASH_ITER (iter, cached_pbvhs) { + PBVH *pbvh2 = BLI_ghashIterator_getValue(&iter); + + if (pbvh2->header.bm == bm) { + pbvh2->header.bm = NULL; + + if (pbvh2 != pbvh) { + bool ok = true; + + for (int i = 0; i < BLI_array_len(pbvhs); i++) { + if (pbvhs[i] == pbvh2) { + ok = false; + } + } + + if (ok) { + BLI_array_append(pbvhs, pbvh2); + } + } + + BLI_array_append(keys, BLI_ghashIterator_getKey(&iter)); + } + } + + for (int i = 0; i < BLI_array_len(keys); i++) { + BLI_ghash_remove(cached_pbvhs, keys[i], MEM_freeN, NULL); + } + + for (int i = 0; i < BLI_array_len(pbvhs); i++) { + BKE_pbvh_free(pbvhs[i]); + } + + BLI_array_free(pbvhs); + BLI_array_free(keys); +} + +BMLog *BKE_pbvh_get_bm_log(PBVH *pbvh) +{ + return pbvh->bm_log; +} + +void BKE_pbvh_system_init() +{ + cached_pbvhs = BLI_ghash_str_new("pbvh cache ghash"); +} + +void BKE_pbvh_system_exit() +{ + pbvh_clear_cached_pbvhs(NULL); + BLI_ghash_free(cached_pbvhs, NULL, NULL); +} + +SculptPMap *BKE_pbvh_make_pmap(const struct Mesh *me) +{ + SculptPMap *pmap = MEM_callocN(sizeof(*pmap), "SculptPMap"); + + BKE_mesh_vert_poly_map_create(&pmap->pmap, + &pmap->pmap_mem, + BKE_mesh_vert_positions(me), + BKE_mesh_edges(me), + BKE_mesh_polys(me), + BKE_mesh_loops(me), + me->totvert, + me->totpoly, + me->totloop, + false); + + pmap->refcount = 1; + + return pmap; +} + +void BKE_pbvh_pmap_aquire(SculptPMap *pmap) +{ + pmap->refcount++; +} + +bool BKE_pbvh_pmap_release(SculptPMap *pmap) +{ + if (!pmap) { + return false; + } + + pmap->refcount--; + + // if (pmap->refcount < 0) { + // printf("%s: error!\n", __func__); + //} + + if (1 && pmap->refcount == 0) { + MEM_SAFE_FREE(pmap->pmap); + MEM_SAFE_FREE(pmap->pmap_mem); + MEM_SAFE_FREE(pmap); + + return true; + } + + return false; +} + +bool BKE_pbvh_is_drawing(const PBVH *pbvh) +{ + return pbvh->is_drawing; +} + +bool BKE_pbvh_draw_cache_invalid(const PBVH *pbvh) +{ + return pbvh->draw_cache_invalid; +} + +void BKE_pbvh_is_drawing_set(PBVH *pbvh, bool val) +{ + pbvh->is_drawing = val; +} + +void BKE_pbvh_node_num_loops(PBVH *pbvh, PBVHNode *node, int *r_totloop) +{ + UNUSED_VARS(pbvh); + BLI_assert(BKE_pbvh_type(pbvh) == PBVH_FACES); + + if (r_totloop) { + *r_totloop = node->loop_indices_num; + } +} + +void BKE_pbvh_update_active_vcol(PBVH *pbvh, const Mesh *mesh) +{ + CustomDataLayer *last_layer = pbvh->color_layer; + + Mesh me_query; + const CustomData *vdata, *ldata; + + if (pbvh->header.type == PBVH_BMESH && pbvh->header.bm) { + vdata = &pbvh->header.bm->vdata; + ldata = &pbvh->header.bm->ldata; + } + else { + vdata = &mesh->vdata; + ldata = &mesh->ldata; + } + + BKE_id_attribute_copy_domains_temp(ID_ME, vdata, NULL, ldata, NULL, NULL, &me_query.id); + me_query.active_color_attribute = mesh->active_color_attribute; + + BKE_pbvh_get_color_layer(&me_query, &pbvh->color_layer, &pbvh->color_domain); + + if (pbvh->color_layer && pbvh->header.bm) { + pbvh->cd_vcol_offset = pbvh->color_layer->offset; + } + else { + pbvh->cd_vcol_offset = -1; + } + + if (pbvh->color_layer != last_layer) { + for (int i = 0; i < pbvh->totnode; i++) { + PBVHNode *node = pbvh->nodes + i; + + if (node->flag & PBVH_Leaf) { + BKE_pbvh_node_mark_update_color(node); + } + } + } +} + +void BKE_pbvh_ensure_node_loops(PBVH *pbvh) +{ + BLI_assert(BKE_pbvh_type(pbvh) == PBVH_FACES); + + int totloop = 0; + + /* Check if nodes already have loop indices. */ + for (int i = 0; i < pbvh->totnode; i++) { + PBVHNode *node = pbvh->nodes + i; + + if (!(node->flag & PBVH_Leaf)) { + continue; + } + + if (node->loop_indices) { + return; + } + + totloop += node->totprim * 3; + } + + BLI_bitmap *visit = BLI_BITMAP_NEW(totloop, __func__); + + /* Create loop indices from node loop triangles. */ + for (int i = 0; i < pbvh->totnode; i++) { + PBVHNode *node = pbvh->nodes + i; + + if (!(node->flag & PBVH_Leaf)) { + continue; + } + + node->loop_indices = MEM_malloc_arrayN(node->totprim * 3, sizeof(int), __func__); + node->loop_indices_num = 0; + + for (int j = 0; j < (int)node->totprim; j++) { + const MLoopTri *mlt = pbvh->looptri + node->prim_indices[j]; + + for (int k = 0; k < 3; k++) { + if (!BLI_BITMAP_TEST(visit, mlt->tri[k])) { + node->loop_indices[node->loop_indices_num++] = mlt->tri[k]; + BLI_BITMAP_ENABLE(visit, mlt->tri[k]); + } + } + } + } + + MEM_SAFE_FREE(visit); +} + +bool BKE_pbvh_get_origvert( + PBVH *pbvh, PBVHVertRef vertex, const float **r_co, float **r_no, float **r_color) +{ + MSculptVert *mv; + + switch (pbvh->header.type) { + case PBVH_FACES: + case PBVH_GRIDS: + mv = pbvh->msculptverts + vertex.i; + + if (mv->stroke_id != pbvh->stroke_id) { + mv->stroke_id = pbvh->stroke_id; + float *mask = NULL; + + if (pbvh->header.type == PBVH_FACES) { + copy_v3_v3(mv->origco, pbvh->vert_positions[vertex.i]); + copy_v3_v3(mv->origno, pbvh->vert_normals[vertex.i]); + mask = (float *)CustomData_get_layer(pbvh->vdata, CD_PAINT_MASK); + + if (mask) { + mask += vertex.i; + } + } + else { + const CCGKey *key = BKE_pbvh_get_grid_key(pbvh); + const int grid_index = vertex.i / key->grid_area; + const int vertex_index = vertex.i - grid_index * key->grid_area; + CCGElem *elem = BKE_pbvh_get_grids(pbvh)[grid_index]; + + copy_v3_v3(mv->origco, CCG_elem_co(key, CCG_elem_offset(key, elem, vertex_index))); + copy_v3_v3(mv->origno, CCG_elem_no(key, CCG_elem_offset(key, elem, vertex_index))); + mask = key->has_mask ? CCG_elem_mask(key, CCG_elem_offset(key, elem, vertex_index)) : + NULL; + } + + if (mask) { + mv->origmask = (ushort)(*mask * 65535.0f); + } + + if (pbvh->color_layer) { + BKE_pbvh_vertex_color_get(pbvh, vertex, mv->origcolor); + } + } + break; + case PBVH_BMESH: { + BMVert *v = (BMVert *)vertex.i; + mv = BKE_PBVH_SCULPTVERT(pbvh->cd_sculpt_vert, v); + + if (mv->stroke_id != pbvh->stroke_id) { + mv->stroke_id = pbvh->stroke_id; + + copy_v3_v3(mv->origco, v->co); + copy_v3_v3(mv->origno, v->no); + + if (pbvh->cd_vert_mask_offset != -1) { + mv->origmask = (short)(BM_ELEM_CD_GET_FLOAT(v, pbvh->cd_vert_mask_offset) * 65535.0f); + } + + if (pbvh->cd_vcol_offset != -1) { + BKE_pbvh_vertex_color_get(pbvh, vertex, mv->origcolor); + } + } + break; + } + } + + if (r_co) { + *r_co = mv->origco; + } + + if (r_no) { + *r_no = mv->origno; + } + + if (r_color) { + *r_color = mv->origcolor; + } + + return true; +} + +int BKE_pbvh_debug_draw_gen_get(PBVHNode *node) +{ + return node->debug_draw_gen; +} + +void BKE_pbvh_set_boundary_flags(PBVH *pbvh, int *boundary_flags) +{ + pbvh->boundary_flags = boundary_flags; +} + +static void pbvh_face_iter_verts_reserve(PBVHFaceIter *fd, int verts_num) +{ + if (verts_num >= fd->verts_size_) { + fd->verts_size_ = (verts_num + 1) << 2; + + if (fd->verts != fd->verts_reserved_) { + MEM_SAFE_FREE(fd->verts); + } + + fd->verts = MEM_malloc_arrayN(fd->verts_size_, sizeof(void *), __func__); + } + + fd->verts_num = verts_num; +} + +BLI_INLINE int face_iter_prim_to_face(PBVHFaceIter *fd, int prim_index) +{ + if (fd->subdiv_ccg_) { + return BKE_subdiv_ccg_grid_to_face_index(fd->subdiv_ccg_, prim_index); + } + + return fd->looptri_[prim_index].poly; +} + +static void pbvh_face_iter_step(PBVHFaceIter *fd, bool do_step) +{ + if (do_step) { + fd->i++; + } + + switch (fd->pbvh_type_) { + case PBVH_BMESH: { + if (do_step) { + fd->bm_faces_iter_++; + + while (fd->bm_faces_iter_ < fd->bm_faces_->cur && + !fd->bm_faces_->elems[fd->bm_faces_iter_]) { + fd->bm_faces_iter_++; + } + + if (fd->bm_faces_iter_ >= fd->bm_faces_->cur) { + return; + } + } + + BMFace *f = (BMFace *)fd->bm_faces_->elems[fd->bm_faces_iter_]; + fd->face.i = (intptr_t)f; + fd->index = f->head.index; + + if (fd->cd_face_set_ != -1) { + fd->face_set = (int *)BM_ELEM_CD_GET_VOID_P(f, fd->cd_face_set_); + } + + if (fd->cd_hide_poly_ != -1) { + fd->hide = (bool *)BM_ELEM_CD_GET_VOID_P(f, fd->cd_hide_poly_); + } + + pbvh_face_iter_verts_reserve(fd, f->len); + int vertex_i = 0; + + BMLoop *l = f->l_first; + do { + fd->verts[vertex_i++].i = (intptr_t)l->v; + } while ((l = l->next) != f->l_first); + + break; + } + case PBVH_GRIDS: + case PBVH_FACES: { + int face_index = 0; + + if (do_step) { + fd->prim_index_++; + + while (fd->prim_index_ < fd->node_->totprim) { + face_index = face_iter_prim_to_face(fd, fd->node_->prim_indices[fd->prim_index_]); + + if (face_index != fd->last_face_index_) { + break; + } + + fd->prim_index_++; + } + } + else if (fd->prim_index_ < fd->node_->totprim) { + face_index = face_iter_prim_to_face(fd, fd->node_->prim_indices[fd->prim_index_]); + } + + if (fd->prim_index_ >= fd->node_->totprim) { + return; + } + + fd->last_face_index_ = face_index; + const MPoly *mp = fd->mpoly_ + face_index; + + fd->face.i = fd->index = face_index; + + if (fd->face_sets_) { + fd->face_set = fd->face_sets_ + face_index; + } + if (fd->hide_poly_) { + fd->hide = fd->hide_poly_ + face_index; + } + + pbvh_face_iter_verts_reserve(fd, mp->totloop); + + const MLoop *ml = fd->mloop_ + mp->loopstart; + const int grid_area = fd->subdiv_key_.grid_area; + + for (int i = 0; i < mp->totloop; i++, ml++) { + if (fd->pbvh_type_ == PBVH_GRIDS) { + /* Grid corners. */ + fd->verts[i].i = (mp->loopstart + i) * grid_area + grid_area - 1; + } + else { + fd->verts[i].i = ml->v; + } + } + break; + } + } +} + +void BKE_pbvh_face_iter_step(PBVHFaceIter *fd) +{ + pbvh_face_iter_step(fd, true); +} + +void BKE_pbvh_face_iter_init(PBVH *pbvh, PBVHNode *node, PBVHFaceIter *fd) +{ + memset(fd, 0, sizeof(*fd)); + + fd->node_ = node; + fd->pbvh_type_ = BKE_pbvh_type(pbvh); + fd->verts = fd->verts_reserved_; + fd->verts_size_ = PBVH_FACE_ITER_VERTS_RESERVED; + + switch (BKE_pbvh_type(pbvh)) { + case PBVH_GRIDS: + fd->subdiv_ccg_ = pbvh->subdiv_ccg; + fd->subdiv_key_ = pbvh->gridkey; + ATTR_FALLTHROUGH; + case PBVH_FACES: + fd->mpoly_ = pbvh->mpoly; + fd->mloop_ = pbvh->mloop; + fd->looptri_ = pbvh->looptri; + fd->hide_poly_ = pbvh->hide_poly; + fd->face_sets_ = pbvh->face_sets; + fd->last_face_index_ = -1; + + break; + case PBVH_BMESH: + fd->bm = pbvh->header.bm; + fd->cd_face_set_ = CustomData_get_offset_named( + &pbvh->header.bm->pdata, CD_PROP_INT32, ".sculpt_face_set"); + fd->cd_hide_poly_ = CustomData_get_offset_named( + &pbvh->header.bm->pdata, CD_PROP_INT32, ".hide_poly"); + + fd->bm_faces_iter_ = 0; + fd->bm_faces_ = node->bm_faces; + break; + } + + if (!BKE_pbvh_face_iter_done(fd)) { + pbvh_face_iter_step(fd, false); + } +} + +void BKE_pbvh_face_iter_finish(PBVHFaceIter *fd) +{ + if (fd->verts != fd->verts_reserved_) { + MEM_SAFE_FREE(fd->verts); + } +} + +bool BKE_pbvh_face_iter_done(PBVHFaceIter *fd) +{ + switch (fd->pbvh_type_) { + case PBVH_FACES: + case PBVH_GRIDS: + return fd->prim_index_ >= fd->node_->totprim; + case PBVH_BMESH: + return fd->bm_faces_iter_ >= fd->bm_faces_->cur; + default: + BLI_assert_unreachable(); + return true; + } +} + +void BKE_pbvh_sync_visibility_from_verts(PBVH *pbvh, Mesh *mesh) +{ + switch (pbvh->header.type) { + case PBVH_FACES: { + BKE_mesh_flush_hidden_from_verts(mesh); + BKE_pbvh_update_hide_attributes_from_mesh(pbvh); + break; + } + case PBVH_BMESH: { + BMIter iter; + BMVert *v; + BMEdge *e; + BMFace *f; + + BM_ITER_MESH (f, &iter, pbvh->header.bm, BM_FACES_OF_MESH) { + BM_elem_flag_disable(f, BM_ELEM_HIDDEN); + } + + BM_ITER_MESH (e, &iter, pbvh->header.bm, BM_EDGES_OF_MESH) { + BM_elem_flag_disable(e, BM_ELEM_HIDDEN); + } + + BM_ITER_MESH (v, &iter, pbvh->header.bm, BM_VERTS_OF_MESH) { + if (!BM_elem_flag_test(v, BM_ELEM_HIDDEN)) { + continue; + } + BMIter iter_l; + BMLoop *l; + + BM_ITER_ELEM (l, &iter_l, v, BM_LOOPS_OF_VERT) { + BM_elem_flag_enable(l->e, BM_ELEM_HIDDEN); + BM_elem_flag_enable(l->f, BM_ELEM_HIDDEN); + } + } + break; + } + case PBVH_GRIDS: { + const MPoly *mp = BKE_mesh_polys(mesh); + CCGKey key = pbvh->gridkey; + + bool *hide_poly = (bool *)CustomData_get_layer_named_for_write( + &mesh->pdata, CD_PROP_BOOL, ".hide_poly", mesh->totpoly); + + bool delete_hide_poly = true; + for (int face_index = 0; face_index < mesh->totpoly; face_index++, mp++) { + bool hidden = false; + + for (int loop_index = 0; !hidden && loop_index < mp->totloop; loop_index++) { + int grid_index = mp->loopstart + loop_index; + + if (pbvh->grid_hidden[grid_index] && + BLI_BITMAP_TEST(pbvh->grid_hidden[grid_index], key.grid_area - 1)) { + hidden = true; + + break; + } + } + + if (hidden && !hide_poly) { + hide_poly = (bool *)CustomData_get_layer_named_for_write( + &mesh->pdata, CD_PROP_BOOL, ".hide_poly", mesh->totpoly); + + if (!hide_poly) { + CustomData_add_layer_named( + &mesh->pdata, CD_PROP_BOOL, CD_CONSTRUCT, NULL, mesh->totpoly, ".hide_poly"); + + hide_poly = (bool *)CustomData_get_layer_named_for_write( + &mesh->pdata, CD_PROP_BOOL, ".hide_poly", mesh->totpoly); + } + } + + if (hide_poly) { + delete_hide_poly = delete_hide_poly && !hidden; + hide_poly[face_index] = hidden; + } + } + + if (delete_hide_poly) { + CustomData_free_layer_named(&mesh->pdata, ".hide_poly", mesh->totpoly); + } + + BKE_mesh_flush_hidden_from_polys(mesh); + BKE_pbvh_update_hide_attributes_from_mesh(pbvh); + break; + } + } +} diff --git a/source/blender/blenkernel/intern/pbvh.cc b/source/blender/blenkernel/intern/pbvh.cc index 7c829d32a82..42ad108eeca 100644 --- a/source/blender/blenkernel/intern/pbvh.cc +++ b/source/blender/blenkernel/intern/pbvh.cc @@ -6,26 +6,40 @@ #include "MEM_guardedalloc.h" -#include +#include "BLI_utildefines.h" +#include "BLI_alloca.h" #include "BLI_bitmap.h" #include "BLI_ghash.h" +#include "BLI_listbase.h" #include "BLI_math.h" #include "BLI_rand.h" +#include "BLI_string.h" #include "BLI_task.h" -#include "BLI_utildefines.h" + +#include "BLI_index_range.hh" +#include "BLI_map.hh" +#include "BLI_math_vector_types.hh" +#include "BLI_set.hh" +#include "BLI_vector.hh" #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" #include "BKE_attribute.h" #include "BKE_ccg.h" -#include "BKE_mesh.h" +#include "BKE_main.h" +#include "BKE_mesh.h" /* for BKE_mesh_calc_normals */ #include "BKE_mesh_mapping.h" +#include "BKE_object.h" #include "BKE_paint.h" #include "BKE_pbvh.h" #include "BKE_subdiv_ccg.h" +#include "DEG_depsgraph_query.h" + #include "DRW_pbvh.hh" #include "PIL_time.h" @@ -36,7 +50,15 @@ #include "pbvh_intern.hh" -#define LEAF_LIMIT 10000 +#include + +#define LEAF_LIMIT 4000 + +using blender::float3; +using blender::IndexRange; +using blender::Map; +using blender::Set; +using blender::Vector; /* Uncomment to test if triangles of the same face are * properly clustered into single nodes. @@ -47,14 +69,13 @@ //#define VALIDATE_UNIQUE_NODE_FACES //#define PERFCNTRS -#define STACK_FIXED_DEPTH 100 -struct PBVHStack { +typedef struct PBVHStack { PBVHNode *node; bool revisiting; -}; +} PBVHStack; -struct PBVHIter { +typedef struct PBVHIter { PBVH *pbvh; BKE_pbvh_SearchCallback scb; void *search_data; @@ -62,9 +83,9 @@ struct PBVHIter { PBVHStack *stack; int stacksize; - PBVHStack stackfixed[STACK_FIXED_DEPTH]; + PBVHStack stackfixed[PBVH_STACK_FIXED_DEPTH]; int stackspace; -}; +} PBVHIter; void BB_reset(BB *bb) { @@ -72,6 +93,28 @@ void BB_reset(BB *bb) bb->bmax[0] = bb->bmax[1] = bb->bmax[2] = -FLT_MAX; } +void BB_intersect(BB *r_out, BB *a, BB *b) +{ + for (int i = 0; i < 3; i++) { + r_out->bmin[i] = max_ff(a->bmin[i], b->bmin[i]); + r_out->bmax[i] = min_ff(a->bmax[i], b->bmax[i]); + + if (r_out->bmax[i] < r_out->bmin[i]) { + r_out->bmax[i] = r_out->bmin[i] = 0.0f; + } + } +} + +float BB_volume(const BB *bb) +{ + float dx = bb->bmax[0] - bb->bmin[0]; + float dy = bb->bmax[1] - bb->bmin[1]; + float dz = bb->bmax[2] - bb->bmin[2]; + + return dx * dy * dz; +} + +/* Expand the bounding box to include a new coordinate */ void BB_expand(BB *bb, const float co[3]) { for (int i = 0; i < 3; i++) { @@ -119,26 +162,75 @@ void BBC_update_centroid(BBC *bbc) } /* Not recursive */ -static void update_node_vb(PBVH *pbvh, PBVHNode *node) +static void update_node_vb(PBVH *pbvh, PBVHNode *node, int updateflag) { + if (!(updateflag & (PBVH_UpdateBB | PBVH_UpdateOriginalBB))) { + return; + } + + /* cannot clear flag here, causes leaky pbvh */ + // node->flag &= ~(updateflag & (PBVH_UpdateBB | PBVH_UpdateOriginalBB)); + BB vb; + BB orig_vb; BB_reset(&vb); + BB_reset(&orig_vb); + + bool do_orig = true; // XXX updateflag & PBVH_UpdateOriginalBB; + bool do_normal = true; // XXX updateflag & PBVH_UpdateBB; if (node->flag & PBVH_Leaf) { PBVHVertexIter vd; BKE_pbvh_vertex_iter_begin (pbvh, node, vd, PBVH_ITER_ALL) { - BB_expand(&vb, vd.co); + if (do_normal) { + BB_expand(&vb, vd.co); + } + + if (do_orig) { + MSculptVert *mv = pbvh->header.type == PBVH_BMESH ? (MSculptVert *)BM_ELEM_CD_GET_VOID_P( + vd.bm_vert, pbvh->cd_sculpt_vert) : + pbvh->msculptverts + vd.index; + + if (mv->stroke_id != pbvh->stroke_id) { + BB_expand(&orig_vb, vd.co); + } + else { + BB_expand(&orig_vb, mv->origco); + } + } } BKE_pbvh_vertex_iter_end; } else { - BB_expand_with_bb(&vb, &pbvh->nodes[node->children_offset].vb); - BB_expand_with_bb(&vb, &pbvh->nodes[node->children_offset + 1].vb); + if (do_normal) { + BB_expand_with_bb(&vb, &pbvh->nodes[node->children_offset].vb); + BB_expand_with_bb(&vb, &pbvh->nodes[node->children_offset + 1].vb); + } + + if (do_orig) { + BB_expand_with_bb(&orig_vb, &pbvh->nodes[node->children_offset].orig_vb); + BB_expand_with_bb(&orig_vb, &pbvh->nodes[node->children_offset + 1].orig_vb); + } } - node->vb = vb; + if (do_normal) { + node->vb = vb; + } + + if (do_orig) { +#if 0 + float size[3]; + + sub_v3_v3v3(size, orig_vb.bmax, orig_vb.bmin); + mul_v3_fl(size, 0.05); + + sub_v3_v3(orig_vb.bmin, size); + add_v3_v3(orig_vb.bmax, size); +#endif + node->orig_vb = orig_vb; + } } // void BKE_pbvh_node_BB_reset(PBVHNode *node) @@ -272,11 +364,18 @@ void pbvh_grow_nodes(PBVH *pbvh, int totnode) if (pbvh->node_mem_count < totnode) { pbvh->node_mem_count = totnode; } - pbvh->nodes = static_cast( - MEM_recallocN(pbvh->nodes, sizeof(PBVHNode) * pbvh->node_mem_count)); + pbvh->nodes = (PBVHNode *)MEM_recallocN(pbvh->nodes, sizeof(PBVHNode) * pbvh->node_mem_count); } pbvh->totnode = totnode; + + for (int i = 0; i < pbvh->totnode; i++) { + PBVHNode *node = pbvh->nodes + i; + + if (!node->id) { + node->id = ++pbvh->idgen; + } + } } /* Add a vertex to the map, with a positive value for unique vertices and @@ -315,8 +414,8 @@ static void build_mesh_leaf_node(PBVH *pbvh, PBVHNode *node) /* reserve size is rough guess */ GHash *map = BLI_ghash_int_new_ex("build_mesh_leaf_node gh", 2 * totface); - int(*face_vert_indices)[3] = static_cast( - MEM_mallocN(sizeof(int[3]) * totface, __func__)); + int(*face_vert_indices)[3] = (int(*)[3])MEM_mallocN(sizeof(int[3]) * totface, + "bvh node face vert indices"); node->face_vert_indices = (const int(*)[3])face_vert_indices; @@ -338,8 +437,8 @@ static void build_mesh_leaf_node(PBVH *pbvh, PBVHNode *node) } } - int *vert_indices = static_cast( - MEM_callocN(sizeof(int) * (node->uniq_verts + node->face_verts), __func__)); + int *vert_indices = (int *)MEM_callocN(sizeof(int) * (node->uniq_verts + node->face_verts), + "bvh node vert indices"); node->vert_indices = vert_indices; /* Build the vertex list, unique verts first */ @@ -368,6 +467,7 @@ static void build_mesh_leaf_node(PBVH *pbvh, PBVHNode *node) BKE_pbvh_node_mark_rebuild_draw(node); BKE_pbvh_node_fully_hidden_set(node, !has_visible); + BKE_pbvh_vert_tag_update_normal_tri_area(node); BLI_ghash_free(map, nullptr, nullptr); } @@ -393,8 +493,8 @@ int BKE_pbvh_count_grid_quads(BLI_bitmap **grid_hidden, /* grid hidden layer is present, so have to check each grid for * visibility */ - int depth1 = int(log2(double(gridsize) - 1.0) + DBL_EPSILON); - int depth2 = int(log2(double(display_gridsize) - 1.0) + DBL_EPSILON); + int depth1 = (int)(log2((double)gridsize - 1.0) + DBL_EPSILON); + int depth2 = (int)(log2((double)display_gridsize - 1.0) + DBL_EPSILON); int skip = depth2 < depth1 ? 1 << (depth1 - depth2 - 1) : 1; @@ -428,6 +528,7 @@ static void build_grid_leaf_node(PBVH *pbvh, PBVHNode *node) pbvh->gridkey.grid_size); BKE_pbvh_node_fully_hidden_set(node, (totquads == 0)); BKE_pbvh_node_mark_rebuild_draw(node); + BKE_pbvh_vert_tag_update_normal_tri_area(node); } static void build_leaf(PBVH *pbvh, int node_index, BBC *prim_bbc, int offset, int count) @@ -551,11 +652,11 @@ static void build_sub(PBVH *pbvh, BB cb_backing; if (!prim_scratch) { - prim_scratch = static_cast(MEM_malloc_arrayN(pbvh->totprim, sizeof(int), __func__)); + prim_scratch = (int *)MEM_malloc_arrayN(pbvh->totprim, sizeof(int), __func__); } /* Decide whether this is a leaf or not */ - const bool below_leaf_limit = count <= pbvh->leaf_limit || depth >= STACK_FIXED_DEPTH - 1; + const bool below_leaf_limit = count <= pbvh->leaf_limit || depth == PBVH_STACK_FIXED_DEPTH - 1; if (below_leaf_limit) { if (!leaf_needs_material_split(pbvh, offset, count)) { build_leaf(pbvh, node_index, prim_bbc, offset, count); @@ -646,15 +747,15 @@ static void pbvh_build(PBVH *pbvh, BB *cb, BBC *prim_bbc, int totprim) if (pbvh->prim_indices) { MEM_freeN(pbvh->prim_indices); } - pbvh->prim_indices = static_cast(MEM_mallocN(sizeof(int) * totprim, __func__)); + pbvh->prim_indices = (int *)MEM_mallocN(sizeof(int) * totprim, "bvh prim indices"); for (int i = 0; i < totprim; i++) { pbvh->prim_indices[i] = i; } pbvh->totnode = 0; if (pbvh->node_mem_count < 100) { pbvh->node_mem_count = 100; - pbvh->nodes = static_cast( - MEM_callocN(sizeof(PBVHNode) * pbvh->node_mem_count, __func__)); + pbvh->nodes = (PBVHNode *)MEM_callocN(sizeof(PBVHNode) * pbvh->node_mem_count, + "bvh initial nodes"); } } @@ -662,6 +763,14 @@ static void pbvh_build(PBVH *pbvh, BB *cb, BBC *prim_bbc, int totprim) build_sub(pbvh, 0, cb, prim_bbc, 0, totprim, nullptr, 0); } +void BKE_pbvh_set_face_areas(PBVH *pbvh, float *face_areas) +{ + pbvh->face_areas = face_areas; +} + +/* XXX investigate this global. */ +extern "C" bool pbvh_show_orig_co = false; + static void pbvh_draw_args_init(PBVH *pbvh, PBVH_GPU_Args *args, PBVHNode *node) { memset((void *)args, 0, sizeof(*args)); @@ -680,11 +789,15 @@ static void pbvh_draw_args_init(PBVH *pbvh, PBVH_GPU_Args *args, PBVHNode *node) args->mloop = pbvh->mloop; args->mpoly = pbvh->mpoly; args->mlooptri = pbvh->looptri; + args->flat_vcol_shading = pbvh->flat_vcol_shading; + args->show_orig = pbvh_show_orig_co; + args->updategen = node->updategen; + args->msculptverts = pbvh->msculptverts; if (ELEM(pbvh->header.type, PBVH_FACES, PBVH_GRIDS)) { - args->hide_poly = pbvh->pdata ? static_cast(CustomData_get_layer_named( - pbvh->pdata, CD_PROP_BOOL, ".hide_poly")) : - nullptr; + args->hide_poly = (const bool *)(pbvh->pdata ? CustomData_get_layer_named( + pbvh->pdata, CD_PROP_BOOL, ".hide_poly") : + nullptr); } switch (pbvh->header.type) { @@ -729,15 +842,24 @@ static void pbvh_draw_args_init(PBVH *pbvh, PBVH_GPU_Args *args, PBVHNode *node) break; case PBVH_BMESH: args->bm = pbvh->header.bm; + + args->active_color = pbvh->mesh->active_color_attribute; + args->render_color = pbvh->mesh->default_color_attribute; + + args->me = pbvh->mesh; args->vdata = &args->bm->vdata; args->ldata = &args->bm->ldata; args->pdata = &args->bm->pdata; args->bm_faces = node->bm_faces; args->bm_other_verts = node->bm_other_verts; args->bm_unique_vert = node->bm_unique_verts; - args->totprim = BLI_gset_len(node->bm_faces); + args->totprim = BLI_table_gset_len(node->bm_faces); args->cd_mask_layer = CustomData_get_offset(&pbvh->header.bm->vdata, CD_PAINT_MASK); + args->tribuf = node->tribuf; + args->tri_buffers = node->tri_buffers; + args->tot_tri_buffers = node->tot_tri_buffers; + break; } } @@ -814,32 +936,42 @@ void BKE_pbvh_build_mesh(PBVH *pbvh, const MPoly *mpoly, const MLoop *mloop, float (*vert_positions)[3], + MSculptVert *msculptverts, int totvert, - CustomData *vdata, - CustomData *ldata, - CustomData *pdata, + struct CustomData *vdata, + struct CustomData *ldata, + struct CustomData *pdata, const MLoopTri *looptri, - int looptri_num) + int looptri_num, + bool fast_draw, + float *face_areas, + SculptPMap *pmap) { BBC *prim_bbc = nullptr; BB cb; + if (pbvh->pmap != pmap) { + BKE_pbvh_pmap_aquire(pmap); + } + + pbvh->pmap = pmap; + pbvh->face_areas = face_areas; pbvh->mesh = mesh; pbvh->header.type = PBVH_FACES; pbvh->mpoly = mpoly; - pbvh->hide_poly = static_cast(CustomData_get_layer_named_for_write( - &mesh->pdata, CD_PROP_BOOL, ".hide_poly", mesh->totpoly)); - pbvh->material_indices = static_cast( - CustomData_get_layer_named(&mesh->pdata, CD_PROP_INT32, "material_index")); + pbvh->hide_poly = (bool *)CustomData_get_layer_named_for_write( + &mesh->pdata, CD_PROP_BOOL, ".hide_poly", mesh->totpoly); + pbvh->material_indices = (const int *)CustomData_get_layer_named( + &mesh->pdata, CD_PROP_INT32, "material_index"); pbvh->mloop = mloop; pbvh->looptri = looptri; + pbvh->msculptverts = msculptverts; pbvh->vert_positions = vert_positions; BKE_mesh_vertex_normals_ensure(mesh); pbvh->vert_normals = BKE_mesh_vertex_normals_for_write(mesh); - pbvh->hide_vert = static_cast(CustomData_get_layer_named_for_write( - &mesh->vdata, CD_PROP_BOOL, ".hide_vert", mesh->totvert)); - pbvh->vert_bitmap = static_cast( - MEM_calloc_arrayN(totvert, sizeof(bool), "bvh->vert_bitmap")); + pbvh->hide_vert = (bool *)CustomData_get_layer_named_for_write( + &mesh->vdata, CD_PROP_BOOL, ".hide_vert", mesh->totvert); + pbvh->vert_bitmap = (bool *)MEM_calloc_arrayN(totvert, sizeof(bool), "bvh->vert_bitmap"); pbvh->totvert = totvert; #ifdef TEST_PBVH_FACE_SPLIT @@ -862,7 +994,12 @@ void BKE_pbvh_build_mesh(PBVH *pbvh, BB_reset(&cb); /* For each face, store the AABB and the AABB centroid */ - prim_bbc = static_cast(MEM_mallocN(sizeof(BBC) * looptri_num, __func__)); + prim_bbc = (BBC *)MEM_mallocN(sizeof(BBC) * looptri_num, "prim_bbc"); + + for (int i = 0; i < mesh->totvert; i++) { + msculptverts[i].flag &= ~SCULPTVERT_NEED_VALENCE; + msculptverts[i].valence = pmap->pmap[i].count; + } for (int i = 0; i < looptri_num; i++) { const MLoopTri *lt = &looptri[i]; @@ -888,6 +1025,10 @@ void BKE_pbvh_build_mesh(PBVH *pbvh, #endif } + if (fast_draw) { + pbvh->flags |= PBVH_FAST_DRAW; + } + MEM_freeN(prim_bbc); /* Clear the bitmap so it can be used as an update tag later on. */ @@ -907,12 +1048,15 @@ void BKE_pbvh_build_grids(PBVH *pbvh, void **gridfaces, DMFlagMat *flagmats, BLI_bitmap **grid_hidden, + bool fast_draw, + float *face_areas, Mesh *me, SubdivCCG *subdiv_ccg) { const int gridsize = key->grid_size; pbvh->header.type = PBVH_GRIDS; + pbvh->face_areas = face_areas; pbvh->grids = grids; pbvh->gridfaces = gridfaces; pbvh->grid_flag_mats = flagmats; @@ -951,7 +1095,7 @@ void BKE_pbvh_build_grids(PBVH *pbvh, BB_reset(&cb); /* For each grid, store the AABB and the AABB centroid */ - BBC *prim_bbc = static_cast(MEM_mallocN(sizeof(BBC) * totgrid, __func__)); + BBC *prim_bbc = (BBC *)MEM_mallocN(sizeof(BBC) * totgrid, "prim_bbc"); for (int i = 0; i < totgrid; i++) { CCGElem *grid = grids[i]; @@ -976,6 +1120,10 @@ void BKE_pbvh_build_grids(PBVH *pbvh, #endif } + if (fast_draw) { + pbvh->flags |= PBVH_FAST_DRAW; + } + MEM_freeN(prim_bbc); #ifdef VALIDATE_UNIQUE_NODE_FACES pbvh_validate_node_prims(pbvh); @@ -984,7 +1132,7 @@ void BKE_pbvh_build_grids(PBVH *pbvh, PBVH *BKE_pbvh_new(PBVHType type) { - PBVH *pbvh = MEM_cnew(__func__); + PBVH *pbvh = (PBVH *)MEM_callocN(sizeof(PBVH), "pbvh"); pbvh->respect_hide = true; pbvh->draw_cache_invalid = true; pbvh->header.type = type; @@ -998,6 +1146,10 @@ PBVH *BKE_pbvh_new(PBVHType type) void BKE_pbvh_free(PBVH *pbvh) { +#ifdef WITH_PBVH_CACHE + BKE_pbvh_cache_remove(pbvh); +#endif + for (int i = 0; i < pbvh->totnode; i++) { PBVHNode *node = &pbvh->nodes[i]; @@ -1015,17 +1167,22 @@ void BKE_pbvh_free(PBVH *pbvh) MEM_freeN((void *)node->face_vert_indices); } if (node->bm_faces) { - BLI_gset_free(node->bm_faces, nullptr); + BLI_table_gset_free(node->bm_faces, nullptr); } if (node->bm_unique_verts) { - BLI_gset_free(node->bm_unique_verts, nullptr); + BLI_table_gset_free(node->bm_unique_verts, nullptr); } if (node->bm_other_verts) { - BLI_gset_free(node->bm_other_verts, nullptr); + BLI_table_gset_free(node->bm_other_verts, nullptr); } - } - if (node->flag & (PBVH_Leaf | PBVH_TexLeaf)) { + if (node->tribuf || node->tri_buffers) { + BKE_pbvh_bmesh_free_tris(pbvh, node); + } + +#ifdef PROXY_ADVANCED + BKE_pbvh_free_proxyarray(pbvh, node); +#endif pbvh_node_pixels_free(node); } } @@ -1036,6 +1193,8 @@ void BKE_pbvh_free(PBVH *pbvh) MEM_freeN((void *)pbvh->vert_positions); } + + pbvh->vert_positions = nullptr; } if (pbvh->looptri) { @@ -1052,6 +1211,10 @@ void BKE_pbvh_free(PBVH *pbvh) MEM_SAFE_FREE(pbvh->vert_bitmap); + BKE_pbvh_pmap_release(pbvh->pmap); + pbvh->pmap = nullptr; + + pbvh->invalid = true; pbvh_pixels_free(pbvh); MEM_freeN(pbvh); @@ -1067,7 +1230,7 @@ static void pbvh_iter_begin(PBVHIter *iter, iter->search_data = search_data; iter->stack = iter->stackfixed; - iter->stackspace = STACK_FIXED_DEPTH; + iter->stackspace = PBVH_STACK_FIXED_DEPTH; iter->stack[0].node = pbvh->nodes; iter->stack[0].revisiting = false; @@ -1076,7 +1239,7 @@ static void pbvh_iter_begin(PBVHIter *iter, static void pbvh_iter_end(PBVHIter *iter) { - if (iter->stackspace > STACK_FIXED_DEPTH) { + if (iter->stackspace > PBVH_STACK_FIXED_DEPTH) { MEM_freeN(iter->stack); } } @@ -1085,14 +1248,12 @@ static void pbvh_stack_push(PBVHIter *iter, PBVHNode *node, bool revisiting) { if (UNLIKELY(iter->stacksize == iter->stackspace)) { iter->stackspace *= 2; - if (iter->stackspace != (STACK_FIXED_DEPTH * 2)) { - iter->stack = static_cast( - MEM_reallocN(iter->stack, sizeof(PBVHStack) * iter->stackspace)); + if (iter->stackspace != (PBVH_STACK_FIXED_DEPTH * 2)) { + iter->stack = (PBVHStack *)MEM_reallocN(iter->stack, sizeof(PBVHStack) * iter->stackspace); } else { - iter->stack = static_cast( - MEM_mallocN(sizeof(PBVHStack) * iter->stackspace, "PBVHStack")); - memcpy(iter->stack, iter->stackfixed, sizeof(PBVHStack) * iter->stacksize); + iter->stack = (PBVHStack *)MEM_mallocN(sizeof(PBVHStack) * iter->stackspace, "PBVHStack"); + memcpy((void *)iter->stack, (void *)iter->stackfixed, sizeof(PBVHStack) * iter->stacksize); } } @@ -1101,7 +1262,7 @@ static void pbvh_stack_push(PBVHIter *iter, PBVHNode *node, bool revisiting) iter->stacksize++; } -static PBVHNode *pbvh_iter_next(PBVHIter *iter, PBVHNodeFlags leaf_flag) +static PBVHNode *pbvh_iter_next(PBVHIter *iter, PBVHNodeFlags leaf_flag = PBVH_Leaf) { /* purpose here is to traverse tree, visiting child nodes before their * parents, this order is necessary for e.g. computing bounding boxes */ @@ -1157,6 +1318,11 @@ static PBVHNode *pbvh_iter_next_occluded(PBVHIter *iter) return nullptr; } + float ff = dot_v3v3(node->vb.bmin, node->vb.bmax); + if (isnan(ff) || !isfinite(ff)) { + printf("%s: nan!\n", __func__); + } + if (iter->scb && !iter->scb(node, iter->search_data)) { continue; /* don't traverse, outside of search zone */ } @@ -1191,8 +1357,7 @@ void BKE_pbvh_search_gather_ex(PBVH *pbvh, if (UNLIKELY(tot == space)) { /* resize array if needed */ space = (tot == 0) ? 32 : space * 2; - array = static_cast( - MEM_recallocN_id(array, sizeof(PBVHNode *) * space, __func__)); + array = (PBVHNode **)MEM_recallocN_id(array, sizeof(PBVHNode *) * space, __func__); } array[tot] = node; @@ -1210,7 +1375,6 @@ void BKE_pbvh_search_gather_ex(PBVH *pbvh, *r_array = array; *r_tot = tot; } - void BKE_pbvh_search_gather( PBVH *pbvh, BKE_pbvh_SearchCallback scb, void *search_data, PBVHNode ***r_array, int *r_tot) { @@ -1228,7 +1392,7 @@ void BKE_pbvh_search_callback(PBVH *pbvh, pbvh_iter_begin(&iter, pbvh, scb, search_data); - while ((node = pbvh_iter_next(&iter, PBVH_Leaf))) { + while ((node = pbvh_iter_next(&iter))) { if (node->flag & PBVH_Leaf) { hcb(node, hit_data); } @@ -1237,12 +1401,12 @@ void BKE_pbvh_search_callback(PBVH *pbvh, pbvh_iter_end(&iter); } -struct node_tree { +typedef struct node_tree { PBVHNode *data; - node_tree *left; - node_tree *right; -}; + struct node_tree *left; + struct node_tree *right; +} node_tree; static void node_tree_insert(node_tree *tree, node_tree *new_node) { @@ -1314,7 +1478,7 @@ static void BKE_pbvh_search_callback_occluded(PBVH *pbvh, while ((node = pbvh_iter_next_occluded(&iter))) { if (node->flag & PBVH_Leaf) { - node_tree *new_node = static_cast(malloc(sizeof(node_tree))); + node_tree *new_node = (node_tree *)malloc(sizeof(node_tree)); new_node->data = node; @@ -1350,7 +1514,7 @@ static bool update_search_cb(PBVHNode *node, void *data_v) return true; } -struct PBVHUpdateData { +typedef struct PBVHUpdateData { PBVH *pbvh; PBVHNode **nodes; int totnode; @@ -1358,15 +1522,17 @@ struct PBVHUpdateData { float (*vert_normals)[3]; int flag; bool show_sculpt_face_sets; + bool flat_vcol_shading; + Mesh *mesh; PBVHAttrReq *attrs; int attrs_num; -}; +} PBVHUpdateData; static void pbvh_update_normals_clear_task_cb(void *__restrict userdata, const int n, - const TaskParallelTLS *__restrict /*tls*/) + const TaskParallelTLS *__restrict) { - PBVHUpdateData *data = static_cast(userdata); + PBVHUpdateData *data = (PBVHUpdateData *)userdata; PBVH *pbvh = data->pbvh; PBVHNode *node = data->nodes[n]; float(*vert_normals)[3] = data->vert_normals; @@ -1385,9 +1551,9 @@ static void pbvh_update_normals_clear_task_cb(void *__restrict userdata, static void pbvh_update_normals_accum_task_cb(void *__restrict userdata, const int n, - const TaskParallelTLS *__restrict /*tls*/) + const TaskParallelTLS *__restrict) { - PBVHUpdateData *data = static_cast(userdata); + PBVHUpdateData *data = (PBVHUpdateData *)userdata; PBVH *pbvh = data->pbvh; PBVHNode *node = data->nodes[n]; @@ -1435,9 +1601,9 @@ static void pbvh_update_normals_accum_task_cb(void *__restrict userdata, static void pbvh_update_normals_store_task_cb(void *__restrict userdata, const int n, - const TaskParallelTLS *__restrict /*tls*/) + const TaskParallelTLS *__restrict) { - PBVHUpdateData *data = static_cast(userdata); + PBVHUpdateData *data = (PBVHUpdateData *)userdata; PBVH *pbvh = data->pbvh; PBVHNode *node = data->nodes[n]; float(*vert_normals)[3] = data->vert_normals; @@ -1473,10 +1639,11 @@ static void pbvh_faces_update_normals(PBVH *pbvh, PBVHNode **nodes, int totnode) * can only update vertices marked in the `vert_bitmap`. */ - PBVHUpdateData data{}; - data.pbvh = pbvh; - data.nodes = nodes; - data.vert_normals = pbvh->vert_normals; + PBVHUpdateData data = { + .pbvh = pbvh, + .nodes = nodes, + .vert_normals = pbvh->vert_normals, + }; TaskParallelSettings settings; BKE_pbvh_parallel_range_settings(&settings, true, totnode); @@ -1489,10 +1656,10 @@ static void pbvh_faces_update_normals(PBVH *pbvh, PBVHNode **nodes, int totnode) static void pbvh_update_mask_redraw_task_cb(void *__restrict userdata, const int n, - const TaskParallelTLS *__restrict /*tls*/) + const TaskParallelTLS *__restrict) { - PBVHUpdateData *data = static_cast(userdata); + PBVHUpdateData *data = (PBVHUpdateData *)userdata; PBVH *pbvh = data->pbvh; PBVHNode *node = data->nodes[n]; if (node->flag & PBVH_UpdateMask) { @@ -1525,10 +1692,11 @@ static void pbvh_update_mask_redraw_task_cb(void *__restrict userdata, static void pbvh_update_mask_redraw(PBVH *pbvh, PBVHNode **nodes, int totnode, int flag) { - PBVHUpdateData data{}; - data.pbvh = pbvh; - data.nodes = nodes; - data.flag = flag; + PBVHUpdateData data = { + .pbvh = pbvh, + .nodes = nodes, + .flag = flag, + }; TaskParallelSettings settings; BKE_pbvh_parallel_range_settings(&settings, true, totnode); @@ -1537,10 +1705,10 @@ static void pbvh_update_mask_redraw(PBVH *pbvh, PBVHNode **nodes, int totnode, i static void pbvh_update_visibility_redraw_task_cb(void *__restrict userdata, const int n, - const TaskParallelTLS *__restrict /*tls*/) + const TaskParallelTLS *__restrict) { - PBVHUpdateData *data = static_cast(userdata); + PBVHUpdateData *data = (PBVHUpdateData *)userdata; PBVH *pbvh = data->pbvh; PBVHNode *node = data->nodes[n]; if (node->flag & PBVH_UpdateVisibility) { @@ -1561,10 +1729,11 @@ static void pbvh_update_visibility_redraw_task_cb(void *__restrict userdata, static void pbvh_update_visibility_redraw(PBVH *pbvh, PBVHNode **nodes, int totnode, int flag) { - PBVHUpdateData data{}; - data.pbvh = pbvh; - data.nodes = nodes; - data.flag = flag; + PBVHUpdateData data = { + .pbvh = pbvh, + .nodes = nodes, + .flag = flag, + }; TaskParallelSettings settings; BKE_pbvh_parallel_range_settings(&settings, true, totnode); @@ -1573,22 +1742,14 @@ static void pbvh_update_visibility_redraw(PBVH *pbvh, PBVHNode **nodes, int totn static void pbvh_update_BB_redraw_task_cb(void *__restrict userdata, const int n, - const TaskParallelTLS *__restrict /*tls*/) + const TaskParallelTLS *__restrict) { - PBVHUpdateData *data = static_cast(userdata); + PBVHUpdateData *data = (PBVHUpdateData *)userdata; PBVH *pbvh = data->pbvh; PBVHNode *node = data->nodes[n]; const int flag = data->flag; - if ((flag & PBVH_UpdateBB) && (node->flag & PBVH_UpdateBB)) { - /* don't clear flag yet, leave it for flushing later */ - /* Note that bvh usage is read-only here, so no need to thread-protect it. */ - update_node_vb(pbvh, node); - } - - if ((flag & PBVH_UpdateOriginalBB) && (node->flag & PBVH_UpdateOriginalBB)) { - node->orig_vb = node->vb; - } + update_node_vb(pbvh, node, flag); if ((flag & PBVH_UpdateRedraw) && (node->flag & PBVH_UpdateRedraw)) { node->flag &= ~PBVH_UpdateRedraw; @@ -1598,10 +1759,11 @@ static void pbvh_update_BB_redraw_task_cb(void *__restrict userdata, void pbvh_update_BB_redraw(PBVH *pbvh, PBVHNode **nodes, int totnode, int flag) { /* update BB, redraw flag */ - PBVHUpdateData data{}; - data.pbvh = pbvh; - data.nodes = nodes; - data.flag = flag; + PBVHUpdateData data = { + .pbvh = pbvh, + .nodes = nodes, + .flag = flag, + }; TaskParallelSettings settings; BKE_pbvh_parallel_range_settings(&settings, true, totnode); @@ -1634,14 +1796,44 @@ bool BKE_pbvh_get_color_layer(const Mesh *me, CustomDataLayer **r_layer, eAttrDo static void pbvh_update_draw_buffer_cb(void *__restrict userdata, const int n, - const TaskParallelTLS *__restrict /*tls*/) + const TaskParallelTLS *__restrict) { /* Create and update draw buffers. The functions called here must not * do any OpenGL calls. Flags are not cleared immediately, that happens * after GPU_pbvh_buffer_flush() which does the final OpenGL calls. */ - PBVHUpdateData *data = static_cast(userdata); + PBVHUpdateData *data = (PBVHUpdateData *)userdata; PBVH *pbvh = data->pbvh; PBVHNode *node = data->nodes[n]; + Mesh *me = data->mesh; + + CustomDataLayer *vcol_layer = nullptr; + eAttrDomain vcol_domain; + + BKE_pbvh_get_color_layer(me, &vcol_layer, &vcol_domain); + + CustomData *vdata, *ldata; + + if (!pbvh->header.bm) { + vdata = pbvh->vdata ? pbvh->vdata : &me->vdata; + ldata = pbvh->ldata ? pbvh->ldata : &me->ldata; + } + else { + vdata = &pbvh->header.bm->vdata; + ldata = &pbvh->header.bm->ldata; + } + + Mesh me_query; + BKE_id_attribute_copy_domains_temp(ID_ME, vdata, nullptr, ldata, nullptr, nullptr, &me_query.id); + me_query.active_color_attribute = me->active_color_attribute; + + if (!pbvh->header.bm) { + vdata = pbvh->vdata; + ldata = pbvh->ldata; + } + else { + vdata = &pbvh->header.bm->vdata; + ldata = &pbvh->header.bm->ldata; + } if (node->flag & PBVH_RebuildDrawBuffers) { PBVH_GPU_Args args; @@ -1651,6 +1843,7 @@ static void pbvh_update_draw_buffer_cb(void *__restrict userdata, } if (node->flag & PBVH_UpdateDrawBuffers) { + node->updategen++; node->debug_draw_gen++; if (node->draw_batches) { @@ -1662,7 +1855,24 @@ static void pbvh_update_draw_buffer_cb(void *__restrict userdata, } } -void pbvh_free_draw_buffers(PBVH * /*pbvh*/, PBVHNode *node) +void BKE_pbvh_set_flat_vcol_shading(PBVH *pbvh, bool value) +{ + if (value != pbvh->flat_vcol_shading) { + for (int i = 0; i < pbvh->totnode; i++) { + PBVHNode *node = pbvh->nodes + i; + + if (!(node->flag & PBVH_Leaf)) { + continue; + } + + BKE_pbvh_node_mark_rebuild_draw(node); + } + } + + pbvh->flat_vcol_shading = value; +} + +void pbvh_free_draw_buffers(PBVH * /* pbvh */, PBVHNode *node) { if (node->draw_batches) { DRW_pbvh_node_free(node->draw_batches); @@ -1670,7 +1880,8 @@ void pbvh_free_draw_buffers(PBVH * /*pbvh*/, PBVHNode *node) } } -static void pbvh_update_draw_buffers(PBVH *pbvh, PBVHNode **nodes, int totnode, int update_flag) +static void pbvh_update_draw_buffers( + PBVH *pbvh, Mesh *me, PBVHNode **nodes, int totnode, int update_flag) { const CustomData *vdata; @@ -1709,9 +1920,8 @@ static void pbvh_update_draw_buffers(PBVH *pbvh, PBVHNode **nodes, int totnode, } /* Parallel creation and update of draw buffers. */ - PBVHUpdateData data{}; - data.pbvh = pbvh; - data.nodes = nodes; + PBVHUpdateData data = { + .pbvh = pbvh, .nodes = nodes, .flat_vcol_shading = pbvh->flat_vcol_shading, .mesh = me}; TaskParallelSettings settings; BKE_pbvh_parallel_range_settings(&settings, true, totnode); @@ -1722,7 +1932,6 @@ static void pbvh_update_draw_buffers(PBVH *pbvh, PBVHNode **nodes, int totnode, if (node->flag & PBVH_UpdateDrawBuffers) { /* Flush buffers uses OpenGL, so not in parallel. */ - if (node->draw_batches) { DRW_pbvh_node_gpu_flush(node->draw_batches); } @@ -1754,12 +1963,7 @@ static int pbvh_flush_bb(PBVH *pbvh, PBVHNode *node, int flag) update |= pbvh_flush_bb(pbvh, pbvh->nodes + node->children_offset, flag); update |= pbvh_flush_bb(pbvh, pbvh->nodes + node->children_offset + 1, flag); - if (update & PBVH_UpdateBB) { - update_node_vb(pbvh, node); - } - if (update & PBVH_UpdateOriginalBB) { - node->orig_vb = node->vb; - } + update_node_vb(pbvh, node, update); return update; } @@ -1869,38 +2073,38 @@ static void pbvh_grids_node_visibility_update(PBVH *pbvh, PBVHNode *node) static void pbvh_bmesh_node_visibility_update(PBVHNode *node) { - GSet *unique, *other; + TableGSet *unique, *other; unique = BKE_pbvh_bmesh_node_unique_verts(node); other = BKE_pbvh_bmesh_node_other_verts(node); - GSetIterator gs_iter; + BMVert *v; - GSET_ITER (gs_iter, unique) { - BMVert *v = static_cast(BLI_gsetIterator_getKey(&gs_iter)); + TGSET_ITER (v, unique) { if (!BM_elem_flag_test(v, BM_ELEM_HIDDEN)) { BKE_pbvh_node_fully_hidden_set(node, false); return; } } + TGSET_ITER_END - GSET_ITER (gs_iter, other) { - BMVert *v = static_cast(BLI_gsetIterator_getKey(&gs_iter)); + TGSET_ITER (v, other) { if (!BM_elem_flag_test(v, BM_ELEM_HIDDEN)) { BKE_pbvh_node_fully_hidden_set(node, false); return; } } + TGSET_ITER_END BKE_pbvh_node_fully_hidden_set(node, true); } static void pbvh_update_visibility_task_cb(void *__restrict userdata, const int n, - const TaskParallelTLS *__restrict /*tls*/) + const TaskParallelTLS *__restrict) { - PBVHUpdateData *data = static_cast(userdata); + PBVHUpdateData *data = (PBVHUpdateData *)userdata; PBVH *pbvh = data->pbvh; PBVHNode *node = data->nodes[n]; if (node->flag & PBVH_UpdateVisibility) { @@ -1921,9 +2125,10 @@ static void pbvh_update_visibility_task_cb(void *__restrict userdata, static void pbvh_update_visibility(PBVH *pbvh, PBVHNode **nodes, int totnode) { - PBVHUpdateData data{}; - data.pbvh = pbvh; - data.nodes = nodes; + PBVHUpdateData data = { + .pbvh = pbvh, + .nodes = nodes, + }; TaskParallelSettings settings; BKE_pbvh_parallel_range_settings(&settings, true, totnode); @@ -1958,7 +2163,7 @@ void BKE_pbvh_redraw_BB(PBVH *pbvh, float bb_min[3], float bb_max[3]) pbvh_iter_begin(&iter, pbvh, nullptr, nullptr); - while ((node = pbvh_iter_next(&iter, PBVH_Leaf))) { + while ((node = pbvh_iter_next(&iter))) { if (node->flag & PBVH_UpdateRedraw) { BB_expand_with_bb(&bb, &node->vb); } @@ -1978,7 +2183,7 @@ void BKE_pbvh_get_grid_updates(PBVH *pbvh, bool clear, void ***r_gridfaces, int pbvh_iter_begin(&iter, pbvh, nullptr, nullptr); - while ((node = pbvh_iter_next(&iter, PBVH_Leaf))) { + while ((node = pbvh_iter_next(&iter))) { if (node->flag & PBVH_UpdateNormals) { for (uint i = 0; i < node->totprim; i++) { void *face = pbvh->gridfaces[node->prim_indices[i]]; @@ -2001,7 +2206,7 @@ void BKE_pbvh_get_grid_updates(PBVH *pbvh, bool clear, void ***r_gridfaces, int return; } - void **faces = static_cast(MEM_mallocN(sizeof(*faces) * tot, __func__)); + void **faces = (void **)MEM_mallocN(sizeof(*faces) * tot, "PBVH Grid Faces"); GSetIterator gs_iter; int i; @@ -2051,7 +2256,7 @@ const CCGKey *BKE_pbvh_get_grid_key(const PBVH *pbvh) return &pbvh->gridkey; } -CCGElem **BKE_pbvh_get_grids(const PBVH *pbvh) +struct CCGElem **BKE_pbvh_get_grids(const PBVH *pbvh) { BLI_assert(pbvh->header.type == PBVH_GRIDS); return pbvh->grids; @@ -2077,10 +2282,16 @@ int BKE_pbvh_get_grid_num_faces(const PBVH *pbvh) /***************************** Node Access ***********************************/ +void BKE_pbvh_node_mark_original_update(PBVHNode *node) +{ + node->flag |= PBVH_UpdateOriginalBB; +} + void BKE_pbvh_node_mark_update(PBVHNode *node) { node->flag |= PBVH_UpdateNormals | PBVH_UpdateBB | PBVH_UpdateOriginalBB | - PBVH_UpdateDrawBuffers | PBVH_UpdateRedraw | PBVH_RebuildPixels; + PBVH_UpdateDrawBuffers | PBVH_UpdateRedraw | PBVH_UpdateCurvatureDir | + PBVH_RebuildPixels | PBVH_UpdateTriAreas; } void BKE_pbvh_node_mark_update_mask(PBVHNode *node) @@ -2111,12 +2322,19 @@ void BKE_pbvh_mark_rebuild_pixels(PBVH *pbvh) void BKE_pbvh_node_mark_update_visibility(PBVHNode *node) { node->flag |= PBVH_UpdateVisibility | PBVH_RebuildDrawBuffers | PBVH_UpdateDrawBuffers | - PBVH_UpdateRedraw; + PBVH_UpdateRedraw | PBVH_UpdateTris; +} + +void BKE_pbvh_vert_tag_update_normal_visibility(PBVHNode *node) +{ + node->flag |= PBVH_UpdateVisibility | PBVH_RebuildDrawBuffers | PBVH_UpdateDrawBuffers | + PBVH_UpdateRedraw | PBVH_UpdateCurvatureDir | PBVH_UpdateTris; } void BKE_pbvh_node_mark_rebuild_draw(PBVHNode *node) { - node->flag |= PBVH_RebuildDrawBuffers | PBVH_UpdateDrawBuffers | PBVH_UpdateRedraw; + node->flag |= PBVH_RebuildDrawBuffers | PBVH_UpdateDrawBuffers | PBVH_UpdateRedraw | + PBVH_UpdateCurvatureDir; } void BKE_pbvh_node_mark_redraw(PBVHNode *node) @@ -2126,7 +2344,27 @@ void BKE_pbvh_node_mark_redraw(PBVHNode *node) void BKE_pbvh_node_mark_normals_update(PBVHNode *node) { - node->flag |= PBVH_UpdateNormals; + node->flag |= PBVH_UpdateNormals | PBVH_UpdateCurvatureDir; +} + +void BKE_pbvh_node_mark_curvature_update(PBVHNode *node) +{ + node->flag |= PBVH_UpdateCurvatureDir; +} + +void BKE_pbvh_curvature_update_set(PBVHNode *node, bool state) +{ + if (state) { + node->flag |= PBVH_UpdateCurvatureDir; + } + else { + node->flag &= ~PBVH_UpdateCurvatureDir; + } +} + +bool BKE_pbvh_curvature_update_get(PBVHNode *node) +{ + return node->flag & PBVH_UpdateCurvatureDir; } void BKE_pbvh_node_fully_hidden_set(PBVHNode *node, int fully_hidden) @@ -2245,9 +2483,24 @@ void BKE_pbvh_node_num_verts(PBVH *pbvh, PBVHNode *node, int *r_uniquevert, int } break; case PBVH_BMESH: - tot = BLI_gset_len(node->bm_unique_verts); + // not a leaf? return zero + if (!(node->flag & PBVH_Leaf)) { + if (r_totvert) { + *r_totvert = 0; + } + + if (r_uniquevert) { + *r_uniquevert = 0; + } + + return; + } + + pbvh_bmesh_check_other_verts(node); + + tot = BLI_table_gset_len(node->bm_unique_verts); if (r_totvert) { - *r_totvert = tot + BLI_gset_len(node->bm_other_verts); + *r_totvert = tot + BLI_table_gset_len(node->bm_other_verts); } if (r_uniquevert) { *r_uniquevert = tot; @@ -2335,21 +2588,6 @@ void BKE_pbvh_node_get_proxies(PBVHNode *node, PBVHProxyNode **proxies, int *pro } } -void BKE_pbvh_node_get_bm_orco_data(PBVHNode *node, - int (**r_orco_tris)[3], - int *r_orco_tris_num, - float (**r_orco_coords)[3], - BMVert ***r_orco_verts) -{ - *r_orco_tris = node->bm_ortri; - *r_orco_tris_num = node->bm_tot_ortri; - *r_orco_coords = node->bm_orco; - - if (r_orco_verts) { - *r_orco_verts = node->bm_orvert; - } -} - bool BKE_pbvh_node_has_vert_with_normal_update_tag(PBVH *pbvh, PBVHNode *node) { BLI_assert(pbvh->header.type == PBVH_FACES); @@ -2369,14 +2607,15 @@ bool BKE_pbvh_node_has_vert_with_normal_update_tag(PBVH *pbvh, PBVHNode *node) /********************************* Ray-cast ***********************************/ -struct RaycastData { - IsectRayAABB_Precalc ray; +typedef struct { + struct IsectRayAABB_Precalc ray; bool original; -}; + int stroke_id; +} RaycastData; static bool ray_aabb_intersect(PBVHNode *node, void *data_v) { - RaycastData *rcd = static_cast(data_v); + RaycastData *rcd = (RaycastData *)data_v; const float *bb_min, *bb_max; if (rcd->original) { @@ -2398,18 +2637,22 @@ void BKE_pbvh_raycast(PBVH *pbvh, void *data, const float ray_start[3], const float ray_normal[3], - bool original) + bool original, + int stroke_id) { RaycastData rcd; isect_ray_aabb_v3_precalc(&rcd.ray, ray_start, ray_normal); + rcd.original = original; + rcd.stroke_id = stroke_id; + pbvh->stroke_id = stroke_id; BKE_pbvh_search_callback_occluded(pbvh, ray_aabb_intersect, &rcd, cb, data); } bool ray_face_intersection_quad(const float ray_start[3], - IsectRayPrecalc *isect_precalc, + struct IsectRayPrecalc *isect_precalc, const float t0[3], const float t1[3], const float t2[3], @@ -2430,7 +2673,7 @@ bool ray_face_intersection_quad(const float ray_start[3], } bool ray_face_intersection_tri(const float ray_start[3], - IsectRayPrecalc *isect_precalc, + struct IsectRayPrecalc *isect_precalc, const float t0[3], const float t1[3], const float t2[3], @@ -2446,6 +2689,60 @@ bool ray_face_intersection_tri(const float ray_start[3], return false; } +bool ray_update_depth_and_hit_count(const float depth_test, + float *r_depth, + float *r_back_depth, + int *hit_count) +{ + (*hit_count)++; + if (depth_test < *r_depth) { + *r_back_depth = *r_depth; + *r_depth = depth_test; + return true; + } + else if (depth_test > *r_depth && depth_test <= *r_back_depth) { + *r_back_depth = depth_test; + return false; + } + + return false; +} + +bool ray_face_intersection_depth_quad(const float ray_start[3], + struct IsectRayPrecalc *isect_precalc, + const float t0[3], + const float t1[3], + const float t2[3], + const float t3[3], + float *r_depth, + float *r_back_depth, + int *hit_count) +{ + float depth_test; + if (!(isect_ray_tri_watertight_v3(ray_start, isect_precalc, t0, t1, t2, &depth_test, nullptr) || + isect_ray_tri_watertight_v3(ray_start, isect_precalc, t0, t2, t3, &depth_test, nullptr))) { + return false; + } + return ray_update_depth_and_hit_count(depth_test, r_depth, r_back_depth, hit_count); +} + +bool ray_face_intersection_depth_tri(const float ray_start[3], + struct IsectRayPrecalc *isect_precalc, + const float t0[3], + const float t1[3], + const float t2[3], + float *r_depth, + float *r_back_depth, + int *hit_count) +{ + float depth_test; + + if (!isect_ray_tri_watertight_v3(ray_start, isect_precalc, t0, t1, t2, &depth_test, nullptr)) { + return false; + } + return ray_update_depth_and_hit_count(depth_test, r_depth, r_back_depth, hit_count); +} + /* Take advantage of the fact we know this won't be an intersection. * Just handle ray-tri edges. */ static float dist_squared_ray_to_tri_v3_fast(const float ray_origin[3], @@ -2524,11 +2821,14 @@ static bool pbvh_faces_node_raycast(PBVH *pbvh, float (*origco)[3], const float ray_start[3], const float ray_normal[3], - IsectRayPrecalc *isect_precalc, + struct IsectRayPrecalc *isect_precalc, + int *hit_count, float *depth, - PBVHVertRef *r_active_vertex, - int *r_active_face_index, - float *r_face_normal) + float *depth_back, + PBVHVertRef *r_active_vertex_index, + PBVHFaceRef *r_active_face_index, + float *r_face_normal, + int stroke_id) { const float(*positions)[3] = pbvh->vert_positions; const MLoop *mloop = pbvh->mloop; @@ -2559,26 +2859,29 @@ static bool pbvh_faces_node_raycast(PBVH *pbvh, co[2] = positions[mloop[lt->tri[2]].v]; } - if (ray_face_intersection_tri(ray_start, isect_precalc, co[0], co[1], co[2], depth)) { - hit = true; + if (!ray_face_intersection_depth_tri( + ray_start, isect_precalc, co[0], co[1], co[2], depth, depth_back, hit_count)) { + continue; + } - if (r_face_normal) { - normal_tri_v3(r_face_normal, co[0], co[1], co[2]); - } + hit = true; - if (r_active_vertex) { - float location[3] = {0.0f}; - madd_v3_v3v3fl(location, ray_start, ray_normal, *depth); - for (int j = 0; j < 3; j++) { - /* Always assign nearest_vertex_co in the first iteration to avoid comparison against - * uninitialized values. This stores the closest vertex in the current intersecting - * triangle. */ - if (j == 0 || - len_squared_v3v3(location, co[j]) < len_squared_v3v3(location, nearest_vertex_co)) { - copy_v3_v3(nearest_vertex_co, co[j]); - r_active_vertex->i = mloop[lt->tri[j]].v; - *r_active_face_index = lt->poly; - } + if (r_face_normal) { + normal_tri_v3(r_face_normal, co[0], co[1], co[2]); + } + + if (r_active_vertex_index) { + float location[3] = {0.0f}; + madd_v3_v3v3fl(location, ray_start, ray_normal, *depth); + for (int j = 0; j < 3; j++) { + /* Always assign nearest_vertex_co in the first iteration to avoid comparison against + * uninitialized values. This stores the closest vertex in the current intersecting + * triangle. */ + if (j == 0 || + len_squared_v3v3(location, co[j]) < len_squared_v3v3(location, nearest_vertex_co)) { + copy_v3_v3(nearest_vertex_co, co[j]); + *r_active_vertex_index = (PBVHVertRef){.i = mloop[lt->tri[j]].v}; + *r_active_face_index = (PBVHFaceRef){.i = lt->poly}; } } } @@ -2592,10 +2895,13 @@ static bool pbvh_grids_node_raycast(PBVH *pbvh, float (*origco)[3], const float ray_start[3], const float ray_normal[3], - IsectRayPrecalc *isect_precalc, + struct IsectRayPrecalc *isect_precalc, + int *hit_count, float *depth, + float *back_depth, + PBVHVertRef *r_active_vertex, - int *r_active_grid_index, + PBVHFaceRef *r_active_grid, float *r_face_normal) { const int totgrid = node->totprim; @@ -2638,37 +2944,46 @@ static bool pbvh_grids_node_raycast(PBVH *pbvh, co[3] = CCG_grid_elem_co(gridkey, grid, x, y); } - if (ray_face_intersection_quad( - ray_start, isect_precalc, co[0], co[1], co[2], co[3], depth)) { - hit = true; + if (!ray_face_intersection_depth_quad(ray_start, + isect_precalc, + co[0], + co[1], + co[2], + co[3], + depth, + back_depth, + hit_count)) { + continue; + } + hit = true; - if (r_face_normal) { - normal_quad_v3(r_face_normal, co[0], co[1], co[2], co[3]); - } + if (r_face_normal) { + normal_quad_v3(r_face_normal, co[0], co[1], co[2], co[3]); + } - if (r_active_vertex) { - float location[3] = {0.0}; - madd_v3_v3v3fl(location, ray_start, ray_normal, *depth); + if (r_active_vertex) { + float location[3] = {0.0}; + madd_v3_v3v3fl(location, ray_start, ray_normal, *depth); - const int x_it[4] = {0, 1, 1, 0}; - const int y_it[4] = {1, 1, 0, 0}; + const int x_it[4] = {0, 1, 1, 0}; + const int y_it[4] = {1, 1, 0, 0}; - for (int j = 0; j < 4; j++) { - /* Always assign nearest_vertex_co in the first iteration to avoid comparison against - * uninitialized values. This stores the closest vertex in the current intersecting - * quad. */ - if (j == 0 || len_squared_v3v3(location, co[j]) < - len_squared_v3v3(location, nearest_vertex_co)) { - copy_v3_v3(nearest_vertex_co, co[j]); + for (int j = 0; j < 4; j++) { + /* Always assign nearest_vertex_co in the first iteration to avoid comparison against + * uninitialized values. This stores the closest vertex in the current intersecting + * quad. */ + if (j == 0 || len_squared_v3v3(location, co[j]) < + len_squared_v3v3(location, nearest_vertex_co)) { + copy_v3_v3(nearest_vertex_co, co[j]); - r_active_vertex->i = gridkey->grid_area * grid_index + - (y + y_it[j]) * gridkey->grid_size + (x + x_it[j]); - } + r_active_vertex->i = gridkey->grid_area * grid_index + + (y + y_it[j]) * gridkey->grid_size + (x + x_it[j]); } } - if (r_active_grid_index) { - *r_active_grid_index = grid_index; - } + } + + if (r_active_grid) { + r_active_grid->i = grid_index; } } } @@ -2687,11 +3002,14 @@ bool BKE_pbvh_node_raycast(PBVH *pbvh, bool use_origco, const float ray_start[3], const float ray_normal[3], - IsectRayPrecalc *isect_precalc, + struct IsectRayPrecalc *isect_precalc, + int *hit_count, float *depth, + float *back_depth, PBVHVertRef *active_vertex, - int *active_face_grid_index, - float *face_normal) + PBVHFaceRef *active_face_grid, + float *face_normal, + int stroke_id) { bool hit = false; @@ -2707,10 +3025,14 @@ bool BKE_pbvh_node_raycast(PBVH *pbvh, ray_start, ray_normal, isect_precalc, + hit_count, depth, + back_depth, active_vertex, - active_face_grid_index, - face_normal); + active_face_grid, + face_normal, + stroke_id); + break; case PBVH_GRIDS: hit |= pbvh_grids_node_raycast(pbvh, @@ -2719,21 +3041,27 @@ bool BKE_pbvh_node_raycast(PBVH *pbvh, ray_start, ray_normal, isect_precalc, + hit_count, depth, + back_depth, active_vertex, - active_face_grid_index, + active_face_grid, face_normal); break; case PBVH_BMESH: - BM_mesh_elem_index_ensure(pbvh->header.bm, BM_VERT); - hit = pbvh_bmesh_node_raycast(node, + hit = pbvh_bmesh_node_raycast(pbvh, + node, ray_start, ray_normal, isect_precalc, + hit_count, depth, + back_depth, use_origco, active_vertex, - face_normal); + active_face_grid, + face_normal, + stroke_id); break; } @@ -2746,7 +3074,7 @@ void BKE_pbvh_raycast_project_ray_root( if (pbvh->nodes) { float rootmin_start, rootmin_end; float bb_min_root[3], bb_max_root[3], bb_center[3], bb_diff[3]; - IsectRayAABB_Precalc ray; + struct IsectRayAABB_Precalc ray; float ray_normal_inv[3]; float offset = 1.0f + 1e-3f; const float offset_vec[3] = {1e-3f, 1e-3f, 1e-3f}; @@ -2789,14 +3117,14 @@ void BKE_pbvh_raycast_project_ray_root( /* -------------------------------------------------------------------- */ -struct FindNearestRayData { - DistRayAABB_Precalc dist_ray_to_aabb_precalc; +typedef struct { + struct DistRayAABB_Precalc dist_ray_to_aabb_precalc; bool original; -}; +} FindNearestRayData; static bool nearest_to_ray_aabb_dist_sq(PBVHNode *node, void *data_v) { - FindNearestRayData *rcd = static_cast(data_v); + FindNearestRayData *rcd = (FindNearestRayData *)data_v; const float *bb_min, *bb_max; if (rcd->original) { @@ -2948,7 +3276,8 @@ bool BKE_pbvh_node_find_nearest_to_ray(PBVH *pbvh, const float ray_start[3], const float ray_normal[3], float *depth, - float *dist_sq) + float *dist_sq, + int stroke_id) { bool hit = false; @@ -2967,18 +3296,18 @@ bool BKE_pbvh_node_find_nearest_to_ray(PBVH *pbvh, break; case PBVH_BMESH: hit = pbvh_bmesh_node_nearest_to_ray( - node, ray_start, ray_normal, depth, dist_sq, use_origco); + pbvh, node, ray_start, ray_normal, depth, dist_sq, use_origco, stroke_id); break; } return hit; } -enum PlaneAABBIsect { +typedef enum { ISECT_INSIDE, ISECT_OUTSIDE, ISECT_INTERSECT, -}; +} PlaneAABBIsect; /* Adapted from: * http://www.gamedev.net/community/forums/topic.asp?topic_id=512123 @@ -3024,8 +3353,7 @@ bool BKE_pbvh_node_frustum_contain_AABB(PBVHNode *node, void *data) bb_min = node->vb.bmin; bb_max = node->vb.bmax; - return test_frustum_aabb(bb_min, bb_max, static_cast(data)) != - ISECT_OUTSIDE; + return test_frustum_aabb(bb_min, bb_max, (PBVHFrustumPlanes *)data) != ISECT_OUTSIDE; } bool BKE_pbvh_node_frustum_exclude_AABB(PBVHNode *node, void *data) @@ -3035,27 +3363,35 @@ bool BKE_pbvh_node_frustum_exclude_AABB(PBVHNode *node, void *data) bb_min = node->vb.bmin; bb_max = node->vb.bmax; - return test_frustum_aabb(bb_min, bb_max, static_cast(data)) != ISECT_INSIDE; + return test_frustum_aabb(bb_min, bb_max, (PBVHFrustumPlanes *)data) != ISECT_INSIDE; } -void BKE_pbvh_update_normals(PBVH *pbvh, SubdivCCG *subdiv_ccg) +void BKE_pbvh_update_normals(PBVH *pbvh, struct SubdivCCG *subdiv_ccg) { /* Update normals */ PBVHNode **nodes; int totnode; + if (pbvh->header.type == PBVH_BMESH) { + for (int i = 0; i < pbvh->totnode; i++) { + if (pbvh->nodes[i].flag & PBVH_Leaf) { + BKE_pbvh_bmesh_check_tris(pbvh, pbvh->nodes + i); + } + } + } + BKE_pbvh_search_gather( pbvh, update_search_cb, POINTER_FROM_INT(PBVH_UpdateNormals), &nodes, &totnode); if (totnode > 0) { if (pbvh->header.type == PBVH_BMESH) { - pbvh_bmesh_normals_update(nodes, totnode); + pbvh_bmesh_normals_update(pbvh, nodes, totnode); } else if (pbvh->header.type == PBVH_FACES) { pbvh_faces_update_normals(pbvh, nodes, totnode); } else if (pbvh->header.type == PBVH_GRIDS) { - CCGFace **faces; + struct CCGFace **faces; int num_faces; BKE_pbvh_get_grid_updates(pbvh, true, (void ***)&faces, &num_faces); if (num_faces > 0) { @@ -3078,16 +3414,16 @@ void BKE_pbvh_face_sets_color_set(PBVH *pbvh, int seed, int color_default) * PBVH drawing, updating draw buffers as needed and culling any nodes outside * the specified frustum. */ -struct PBVHDrawSearchData { +typedef struct PBVHDrawSearchData { PBVHFrustumPlanes *frustum; int accum_update_flag; PBVHAttrReq *attrs; int attrs_num; -}; +} PBVHDrawSearchData; static bool pbvh_draw_search_cb(PBVHNode *node, void *data_v) { - PBVHDrawSearchData *data = static_cast(data_v); + PBVHDrawSearchData *data = (PBVHDrawSearchData *)data_v; if (data->frustum && !BKE_pbvh_node_frustum_contain_AABB(node, data->frustum)) { return false; } @@ -3097,12 +3433,13 @@ static bool pbvh_draw_search_cb(PBVHNode *node, void *data_v) } void BKE_pbvh_draw_cb(PBVH *pbvh, + Mesh *me, bool update_only_visible, PBVHFrustumPlanes *update_frustum, PBVHFrustumPlanes *draw_frustum, void (*draw_fn)(void *user_data, PBVHBatches *batches, PBVH_GPU_Args *args), void *user_data, - bool /*full_render*/, + bool /* full_render */, PBVHAttrReq *attrs, int attrs_num) { @@ -3115,11 +3452,8 @@ void BKE_pbvh_draw_cb(PBVH *pbvh, /* Search for nodes that need updates. */ if (update_only_visible) { /* Get visible nodes with draw updates. */ - PBVHDrawSearchData data{}; - data.frustum = update_frustum; - data.accum_update_flag = 0; - data.attrs = attrs; - data.attrs_num = attrs_num; + PBVHDrawSearchData data = { + .frustum = update_frustum, .accum_update_flag = 0, attrs, attrs_num}; BKE_pbvh_search_gather(pbvh, pbvh_draw_search_cb, &data, &nodes, &totnode); update_flag = data.accum_update_flag; } @@ -3133,14 +3467,14 @@ void BKE_pbvh_draw_cb(PBVH *pbvh, /* Update draw buffers. */ if (totnode != 0 && (update_flag & (PBVH_RebuildDrawBuffers | PBVH_UpdateDrawBuffers))) { - pbvh_update_draw_buffers(pbvh, nodes, totnode, update_flag); + // check that need_full_render is set to GPU_pbvh_need_full_render_get(), + // but only if nodes need updating} + pbvh_update_draw_buffers(pbvh, me, nodes, totnode, update_flag); } MEM_SAFE_FREE(nodes); /* Draw visible nodes. */ - PBVHDrawSearchData draw_data{}; - draw_data.frustum = draw_frustum; - draw_data.accum_update_flag = 0; + PBVHDrawSearchData draw_data = {.frustum = draw_frustum, .accum_update_flag = 0}; BKE_pbvh_search_gather(pbvh, pbvh_draw_search_cb, &draw_data, &nodes, &totnode); PBVH_GPU_Args args; @@ -3165,25 +3499,15 @@ void BKE_pbvh_draw_debug_cb(PBVH *pbvh, PBVHNodeFlags flag), void *user_data) { - PBVHNodeFlags flag = PBVH_Leaf; - for (int a = 0; a < pbvh->totnode; a++) { PBVHNode *node = &pbvh->nodes[a]; - if (node->flag & PBVH_TexLeaf) { - flag = PBVH_TexLeaf; - break; + if (pbvh_show_orig_co) { + draw_fn(node, user_data, node->orig_vb.bmin, node->orig_vb.bmax, node->flag); } - } - - for (int a = 0; a < pbvh->totnode; a++) { - PBVHNode *node = &pbvh->nodes[a]; - - if (!(node->flag & flag)) { - continue; + else { + draw_fn(node, user_data, node->vb.bmin, node->vb.bmax, node->flag); } - - draw_fn(node, user_data, node->vb.bmin, node->vb.bmax, node->flag); } } @@ -3213,8 +3537,7 @@ float (*BKE_pbvh_vert_coords_alloc(PBVH *pbvh))[3] float(*vertCos)[3] = nullptr; if (pbvh->vert_positions) { - vertCos = static_cast( - MEM_malloc_arrayN(pbvh->totvert, sizeof(float[3]), __func__)); + vertCos = (float(*)[3])MEM_malloc_arrayN(pbvh->totvert, sizeof(float[3]), __func__); memcpy(vertCos, pbvh->vert_positions, sizeof(float[3]) * pbvh->totvert); } @@ -3234,7 +3557,7 @@ void BKE_pbvh_vert_coords_apply(PBVH *pbvh, const float (*vertCos)[3], const int /* original data and applying new coords to this arrays would lead to */ /* unneeded deformation -- duplicate verts/faces to avoid this */ - pbvh->vert_positions = static_cast(MEM_dupallocN(pbvh->vert_positions)); + pbvh->vert_positions = (float(*)[3])MEM_dupallocN(pbvh->vert_positions); /* No need to dupalloc pbvh->looptri, this one is 'totally owned' by pbvh, * it's never some mesh data. */ @@ -3276,16 +3599,16 @@ PBVHProxyNode *BKE_pbvh_node_add_proxy(PBVH *pbvh, PBVHNode *node) node->proxy_count++; if (node->proxies) { - node->proxies = static_cast( - MEM_reallocN(node->proxies, node->proxy_count * sizeof(PBVHProxyNode))); + node->proxies = (PBVHProxyNode *)MEM_reallocN(node->proxies, + node->proxy_count * sizeof(PBVHProxyNode)); } else { - node->proxies = static_cast(MEM_mallocN(sizeof(PBVHProxyNode), __func__)); + node->proxies = (PBVHProxyNode *)MEM_mallocN(sizeof(PBVHProxyNode), "PBVHNodeProxy"); } BKE_pbvh_node_num_verts(pbvh, node, &totverts, nullptr); - node->proxies[index].co = static_cast( - MEM_callocN(sizeof(float[3]) * totverts, __func__)); + node->proxies[index].co = (float(*)[3])MEM_callocN(sizeof(float[3]) * totverts, + "PBVHNodeProxy.co"); return node->proxies + index; } @@ -3297,7 +3620,7 @@ void BKE_pbvh_node_free_proxies(PBVHNode *node) node->proxies[p].co = nullptr; } - MEM_freeN(node->proxies); + MEM_SAFE_FREE(node->proxies); node->proxies = nullptr; node->proxy_count = 0; @@ -3315,8 +3638,7 @@ void BKE_pbvh_gather_proxies(PBVH *pbvh, PBVHNode ***r_array, int *r_tot) if (tot == space) { /* resize array if needed */ space = (tot == 0) ? 32 : space * 2; - array = static_cast( - MEM_recallocN_id(array, sizeof(PBVHNode *) * space, __func__)); + array = (PBVHNode **)MEM_recallocN_id(array, sizeof(PBVHNode *) * space, __func__); } array[tot] = node; @@ -3333,30 +3655,9 @@ void BKE_pbvh_gather_proxies(PBVH *pbvh, PBVHNode ***r_array, int *r_tot) *r_tot = tot; } -PBVHColorBufferNode *BKE_pbvh_node_color_buffer_get(PBVHNode *node) -{ - - if (!node->color_buffer.color) { - node->color_buffer.color = static_cast( - MEM_callocN(sizeof(float[4]) * node->uniq_verts, "Color buffer")); - } - return &node->color_buffer; -} - -void BKE_pbvh_node_color_buffer_free(PBVH *pbvh) -{ - PBVHNode **nodes; - int totnode; - BKE_pbvh_search_gather(pbvh, nullptr, nullptr, &nodes, &totnode); - for (int i = 0; i < totnode; i++) { - MEM_SAFE_FREE(nodes[i]->color_buffer.color); - } - MEM_SAFE_FREE(nodes); -} - void pbvh_vertex_iter_init(PBVH *pbvh, PBVHNode *node, PBVHVertexIter *vi, int mode) { - CCGElem **grids; + struct CCGElem **grids; int *grid_indices; int totgrid, gridsize, uniq_verts, totvert; @@ -3365,6 +3666,7 @@ void pbvh_vertex_iter_init(PBVH *pbvh, PBVHNode *node, PBVHVertexIter *vi, int m vi->fno = nullptr; vi->vert_positions = nullptr; vi->vertex.i = 0LL; + vi->index = 0; vi->respect_hide = pbvh->respect_hide; if (pbvh->respect_hide == false) { @@ -3393,9 +3695,20 @@ void pbvh_vertex_iter_init(PBVH *pbvh, PBVHNode *node, PBVHVertexIter *vi, int m vi->is_mesh = pbvh->vert_positions != nullptr; if (pbvh->header.type == PBVH_BMESH) { - BLI_gsetIterator_init(&vi->bm_unique_verts, node->bm_unique_verts); - BLI_gsetIterator_init(&vi->bm_other_verts, node->bm_other_verts); + if (mode == PBVH_ITER_ALL) { + pbvh_bmesh_check_other_verts(node); + } + + vi->vert_positions = nullptr; + + vi->bi = 0; + vi->bm_cur_set = node->bm_unique_verts; + vi->bm_unique_verts = node->bm_unique_verts; + vi->bm_other_verts = node->bm_other_verts; vi->bm_vdata = &pbvh->header.bm->vdata; + vi->bm_vert = nullptr; + + vi->cd_sculpt_vert = CustomData_get_offset(vi->bm_vdata, CD_DYNTOPO_VERT); vi->cd_vert_mask_offset = CustomData_get_offset(vi->bm_vdata, CD_PAINT_MASK); } @@ -3409,12 +3722,17 @@ void pbvh_vertex_iter_init(PBVH *pbvh, PBVHNode *node, PBVHVertexIter *vi, int m vi->vert_normals = pbvh->vert_normals; vi->hide_vert = pbvh->hide_vert; - vi->vmask = static_cast( - CustomData_get_layer_for_write(pbvh->vdata, CD_PAINT_MASK, pbvh->mesh->totvert)); + vi->vmask = (float *)CustomData_get_layer_for_write( + pbvh->vdata, CD_PAINT_MASK, pbvh->mesh->totvert); } } -bool pbvh_has_mask(const PBVH *pbvh) +bool BKE_pbvh_draw_mask(const PBVH *pbvh) +{ + return BKE_pbvh_has_mask(pbvh) && !(pbvh->flags & PBVH_FAST_DRAW); +} + +bool BKE_pbvh_has_mask(const PBVH *pbvh) { switch (pbvh->header.type) { case PBVH_GRIDS: @@ -3429,15 +3747,21 @@ bool pbvh_has_mask(const PBVH *pbvh) return false; } -bool pbvh_has_face_sets(PBVH *pbvh) +bool BKE_pbvh_draw_face_sets(PBVH *pbvh) { + if (pbvh->flags & PBVH_FAST_DRAW) { + return false; + } + switch (pbvh->header.type) { case PBVH_GRIDS: case PBVH_FACES: return pbvh->pdata && CustomData_get_layer_named(pbvh->pdata, CD_PROP_INT32, ".sculpt_face_set") != nullptr; case PBVH_BMESH: - return false; + return (pbvh->header.bm && CustomData_get_named_layer_index(&pbvh->header.bm->pdata, + CD_PROP_INT32, + ".sculpt_face_set") != -1); } return false; @@ -3459,12 +3783,13 @@ void BKE_pbvh_get_frustum_planes(PBVH *pbvh, PBVHFrustumPlanes *planes) } } +#include "BKE_global.h" void BKE_pbvh_parallel_range_settings(TaskParallelSettings *settings, bool use_threading, int totnode) { memset(settings, 0, sizeof(*settings)); - settings->use_threading = use_threading && totnode > 1; + settings->use_threading = use_threading && totnode > 1 && G.debug_value != 890; } float (*BKE_pbvh_get_vert_positions(const PBVH *pbvh))[3] @@ -3497,23 +3822,23 @@ bool *BKE_pbvh_get_vert_hide_for_write(PBVH *pbvh) if (pbvh->hide_vert) { return pbvh->hide_vert; } - pbvh->hide_vert = static_cast(CustomData_get_layer_named_for_write( - &pbvh->mesh->vdata, CD_PROP_BOOL, ".hide_vert", pbvh->mesh->totvert)); + pbvh->hide_vert = (bool *)CustomData_get_layer_named_for_write( + &pbvh->mesh->vdata, CD_PROP_BOOL, ".hide_vert", pbvh->mesh->totvert); if (pbvh->hide_vert) { return pbvh->hide_vert; } - pbvh->hide_vert = static_cast(CustomData_add_layer_named(&pbvh->mesh->vdata, - CD_PROP_BOOL, - CD_SET_DEFAULT, - nullptr, - pbvh->mesh->totvert, - ".hide_vert")); + pbvh->hide_vert = (bool *)CustomData_add_layer_named( + &pbvh->mesh->vdata, CD_PROP_BOOL, CD_SET_DEFAULT, nullptr, pbvh->mesh->totvert, ".hide_vert"); return pbvh->hide_vert; } -void BKE_pbvh_subdiv_cgg_set(PBVH *pbvh, SubdivCCG *subdiv_ccg) +void BKE_pbvh_subdiv_ccg_set(PBVH *pbvh, SubdivCCG *subdiv_ccg) { pbvh->subdiv_ccg = subdiv_ccg; + pbvh->gridfaces = (void **)subdiv_ccg->grid_faces; + pbvh->grid_hidden = subdiv_ccg->grid_hidden; + pbvh->grid_flag_mats = subdiv_ccg->grid_flag_mats; + pbvh->grids = subdiv_ccg->grids; } void BKE_pbvh_face_sets_set(PBVH *pbvh, int *face_sets) @@ -3524,10 +3849,10 @@ void BKE_pbvh_face_sets_set(PBVH *pbvh, int *face_sets) void BKE_pbvh_update_hide_attributes_from_mesh(PBVH *pbvh) { if (pbvh->header.type == PBVH_FACES) { - pbvh->hide_vert = static_cast(CustomData_get_layer_named_for_write( - &pbvh->mesh->vdata, CD_PROP_BOOL, ".hide_vert", pbvh->mesh->totvert)); - pbvh->hide_poly = static_cast(CustomData_get_layer_named_for_write( - &pbvh->mesh->pdata, CD_PROP_BOOL, ".hide_poly", pbvh->mesh->totpoly)); + pbvh->hide_vert = (bool *)CustomData_get_layer_named_for_write( + &pbvh->mesh->vdata, CD_PROP_BOOL, ".hide_vert", pbvh->mesh->totvert); + pbvh->hide_poly = (bool *)CustomData_get_layer_named_for_write( + &pbvh->mesh->pdata, CD_PROP_BOOL, ".hide_poly", pbvh->mesh->totpoly); } } @@ -3536,6 +3861,1498 @@ void BKE_pbvh_respect_hide_set(PBVH *pbvh, bool respect_hide) pbvh->respect_hide = respect_hide; } +int BKE_pbvh_get_node_index(PBVH *pbvh, PBVHNode *node) +{ + return (int)(node - pbvh->nodes); +} + +int BKE_pbvh_get_totnodes(PBVH *pbvh) +{ + return pbvh->totnode; +} + +int BKE_pbvh_get_node_id(PBVH *pbvh, PBVHNode *node) +{ + return node->id; +} + +void BKE_pbvh_get_nodes(PBVH *pbvh, int flag, PBVHNode ***r_array, int *r_totnode) +{ + BKE_pbvh_search_gather(pbvh, update_search_cb, POINTER_FROM_INT(flag), r_array, r_totnode); +} + +PBVHNode *BKE_pbvh_node_from_index(PBVH *pbvh, int node_i) +{ + return pbvh->nodes + node_i; +} + +#ifdef PROXY_ADVANCED +// TODO: if this really works, make sure to pull the neighbor iterator out of sculpt.c and put it +// here +/* clang-format off */ +# include "BKE_context.h" +# include "DNA_object_types.h" +# include "DNA_scene_types.h" +# include "../../editors/sculpt_paint/sculpt_intern.h" +/* clang-format on */ + +int checkalloc(void **data, int esize, int oldsize, int newsize, int emask, int umask) +{ + // update channel if it already was allocated once, or is requested by umask + if (newsize != oldsize && (*data || (emask & umask))) { + if (*data) { + *data = MEM_reallocN(*data, newsize * esize); + } + else { + *data = MEM_mallocN(newsize * esize, "pbvh proxy vert arrays"); + } + return emask; + } + + return 0; +} + +void BKE_pbvh_ensure_proxyarray_indexmap(PBVH *pbvh, PBVHNode *node, GHash *vert_node_map) +{ + ProxyVertArray *p = &node->proxyverts; + + int totvert = 0; + BKE_pbvh_node_num_verts(pbvh, node, &totvert, nullptr); + + bool update = !p->indexmap || p->size != totvert; + update = update || (p->indexmap && BLI_ghash_len(p->indexmap) != totvert); + + if (!update) { + return; + } + + if (p->indexmap) { + BLI_ghash_free(p->indexmap, nullptr, nullptr); + } + + GHash *gs = p->indexmap = BLI_ghash_ptr_new("BKE_pbvh_ensure_proxyarray_indexmap"); + + PBVHVertexIter vd; + + int i = 0; + BKE_pbvh_vertex_iter_begin (pbvh, node, vd, PBVH_ITER_UNIQUE) { + BLI_ghash_insert(gs, (void *)vd.vertex.i, (void *)i); + i++; + } + BKE_pbvh_vertex_iter_end; +} + +bool pbvh_proxyarray_needs_update(PBVH *pbvh, PBVHNode *node, int mask) +{ + ProxyVertArray *p = &node->proxyverts; + int totvert = 0; + + if (!(node->flag & PBVH_Leaf) || !node->bm_unique_verts) { + return false; + } + + BKE_pbvh_node_num_verts(pbvh, node, &totvert, nullptr); + + bool bad = p->size != totvert; + bad = bad || ((mask & PV_NEIGHBORS) && p->neighbors_dirty); + bad = bad || (p->datamask & mask) != mask; + + bad = bad && totvert > 0; + + return bad; +} + +GHash *pbvh_build_vert_node_map(PBVH *pbvh, PBVHNode **nodes, int totnode, int mask) +{ + GHash *vert_node_map = BLI_ghash_ptr_new("BKE_pbvh_ensure_proxyarrays"); + + for (int i = 0; i < totnode; i++) { + PBVHVertexIter vd; + PBVHNode *node = nodes[i]; + + if (!(node->flag & PBVH_Leaf)) { + continue; + } + + BKE_pbvh_vertex_iter_begin (pbvh, node, vd, PBVH_ITER_UNIQUE) { + BLI_ghash_insert(vert_node_map, (void *)vd.vertex.i, (void *)(node - pbvh->nodes)); + } + BKE_pbvh_vertex_iter_end; + } + + return vert_node_map; +} + +void BKE_pbvh_ensure_proxyarrays( + SculptSession *ss, PBVH *pbvh, PBVHNode **nodes, int totnode, int mask) +{ + + bool update = false; + + for (int i = 0; i < totnode; i++) { + if (pbvh_proxyarray_needs_update(pbvh, nodes[i], mask)) { + update = true; + break; + } + } + + if (!update) { + return; + } + + GHash *vert_node_map = pbvh_build_vert_node_map(pbvh, nodes, totnode, mask); + + for (int i = 0; i < totnode; i++) { + if (nodes[i]->flag & PBVH_Leaf) { + BKE_pbvh_ensure_proxyarray_indexmap(pbvh, nodes[i], vert_node_map); + } + } + + for (int i = 0; i < totnode; i++) { + if (nodes[i]->flag & PBVH_Leaf) { + BKE_pbvh_ensure_proxyarray(ss, pbvh, nodes[i], mask, vert_node_map, false, false); + } + } + + if (vert_node_map) { + BLI_ghash_free(vert_node_map, nullptr, nullptr); + } +} + +void BKE_pbvh_ensure_proxyarray(SculptSession *ss, + PBVH *pbvh, + PBVHNode *node, + int mask, + GHash *vert_node_map, + bool check_indexmap, + bool force_update) +{ + ProxyVertArray *p = &node->proxyverts; + + if (check_indexmap) { + BKE_pbvh_ensure_proxyarray_indexmap(pbvh, node, vert_node_map); + } + + GHash *gs = p->indexmap; + + int totvert = 0; + BKE_pbvh_node_num_verts(pbvh, node, &totvert, nullptr); + + if (!totvert) { + return; + } + + int updatemask = 0; + +# define UPDATETEST(name, emask, esize) \ + if (mask & emask) { \ + updatemask |= checkalloc((void **)&p->name, esize, p->size, totvert, emask, mask); \ + } + + UPDATETEST(ownerco, PV_OWNERCO, sizeof(void *)) + UPDATETEST(ownerno, PV_OWNERNO, sizeof(void *)) + UPDATETEST(ownermask, PV_OWNERMASK, sizeof(void *)) + UPDATETEST(ownercolor, PV_OWNERCOLOR, sizeof(void *)) + UPDATETEST(co, PV_CO, sizeof(float) * 3) + UPDATETEST(no, PV_NO, sizeof(short) * 3) + UPDATETEST(fno, PV_NO, sizeof(float) * 3) + UPDATETEST(mask, PV_MASK, sizeof(float)) + UPDATETEST(color, PV_COLOR, sizeof(float) * 4) + UPDATETEST(index, PV_INDEX, sizeof(PBVHVertRef)) + UPDATETEST(neighbors, PV_NEIGHBORS, sizeof(ProxyKey) * MAX_PROXY_NEIGHBORS) + + p->size = totvert; + + if (force_update) { + updatemask |= mask; + } + + if ((mask & PV_NEIGHBORS) && p->neighbors_dirty) { + updatemask |= PV_NEIGHBORS; + } + + if (!updatemask) { + return; + } + + p->datamask |= mask; + + PBVHVertexIter vd; + + int i = 0; + +# if 1 + BKE_pbvh_vertex_iter_begin (pbvh, node, vd, PBVH_ITER_UNIQUE) { + void **val; + + if (!BLI_ghash_ensure_p(gs, (void *)vd.vertex.i, &val)) { + *val = (void *)i; + }; + i++; + } + BKE_pbvh_vertex_iter_end; +# endif + + if (updatemask & PV_NEIGHBORS) { + p->neighbors_dirty = false; + } + + i = 0; + BKE_pbvh_vertex_iter_begin (pbvh, node, vd, PBVH_ITER_UNIQUE) { + if (i >= p->size) { + printf("error!! %s\n", __func__); + break; + } + + if (updatemask & PV_OWNERCO) { + p->ownerco[i] = vd.co; + } + if (updatemask & PV_INDEX) { + p->index[i] = vd.vertex; + } + if (updatemask & PV_OWNERNO) { + p->ownerno[i] = vd.no; + } + if (updatemask & PV_NO) { + if (vd.fno) { + if (p->fno) { + copy_v3_v3(p->fno[i], vd.fno); + } + normal_float_to_short_v3(p->no[i], vd.fno); + } + else if (vd.no) { + copy_v3_v3_short(p->no[i], vd.no); + if (p->fno) { + normal_short_to_float_v3(p->fno[i], vd.no); + } + } + else { + p->no[i][0] = p->no[i][1] = p->no[i][2] = 0; + if (p->fno) { + zero_v3(p->fno[i]); + } + } + } + if (updatemask & PV_CO) { + copy_v3_v3(p->co[i], vd.co); + } + if (updatemask & PV_OWNERMASK) { + p->ownermask[i] = vd.mask; + } + if (updatemask & PV_MASK) { + p->mask[i] = vd.mask ? *vd.mask : 0.0f; + } + if (updatemask & PV_COLOR) { + if (vd.vcol) { + copy_v4_v4(p->color[i], vd.vcol->color); + } + } + + if (updatemask & PV_NEIGHBORS) { + int j = 0; + SculptVertexNeighborIter ni; + + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.vertex, ni) { + if (j >= MAX_PROXY_NEIGHBORS - 1) { + break; + } + + ProxyKey key; + + int *pindex = (int *)BLI_ghash_lookup_p(gs, (void *)ni.vertex.i); + + if (!pindex) { + if (vert_node_map) { + int *nindex = (int *)BLI_ghash_lookup_p(vert_node_map, (void *)ni.vertex.i); + + if (!nindex) { + p->neighbors_dirty = true; + continue; + } + + PBVHNode *node2 = pbvh->nodes + *nindex; + if (node2->proxyverts.indexmap) { + pindex = (int *)BLI_ghash_lookup_p(node2->proxyverts.indexmap, (void *)ni.vertex.i); + } + else { + pindex = nullptr; + } + + if (!pindex) { + p->neighbors_dirty = true; + continue; + } + + key.node = (int)(node2 - pbvh->nodes); + key.pindex = *pindex; + //* + if (node2->proxyverts.size != 0 && + (key.pindex < 0 || key.pindex >= node2->proxyverts.size)) { + printf("error! %s\n", __func__); + fflush(stdout); + p->neighbors_dirty = true; + continue; + } + //*/ + } + else { + p->neighbors_dirty = true; + continue; + } + } + else { + key.node = (int)(node - pbvh->nodes); + key.pindex = *pindex; + } + + p->neighbors[i][j++] = key; + } + SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); + + p->neighbors[i][j].node = -1; + } + + i++; + } + BKE_pbvh_vertex_iter_end; +} + +typedef struct GatherProxyThread { + PBVHNode **nodes; + PBVH *pbvh; + int mask; +} GatherProxyThread; + +static void pbvh_load_proxyarray_exec(void *__restrict userdata, + const int n, + const TaskParallelTLS *__restrict tls) +{ + GatherProxyThread *data = (GatherProxyThread *)userdata; + PBVHNode *node = data->nodes[n]; + PBVHVertexIter vd; + ProxyVertArray *p = &node->proxyverts; + int i = 0; + + int mask = p->datamask; + + BKE_pbvh_ensure_proxyarray(nullptr, data->pbvh, node, data->mask, nullptr, false, true); +} + +void BKE_pbvh_load_proxyarrays(PBVH *pbvh, PBVHNode **nodes, int totnode, int mask) +{ + GatherProxyThread data = {.nodes = nodes, .pbvh = pbvh, .mask = mask}; + + mask = mask & ~PV_NEIGHBORS; // don't update neighbors in threaded code? + + TaskParallelSettings settings; + BKE_pbvh_parallel_range_settings(&settings, true, totnode); + BLI_task_parallel_range(0, totnode, &data, pbvh_load_proxyarray_exec, &settings); +} + +static void pbvh_gather_proxyarray_exec(void *__restrict userdata, + const int n, + const TaskParallelTLS *__restrict tls) +{ + GatherProxyThread *data = (GatherProxyThread *)userdata; + PBVHNode *node = data->nodes[n]; + PBVHVertexIter vd; + ProxyVertArray *p = &node->proxyverts; + int i = 0; + + int mask = p->datamask; + + BKE_pbvh_vertex_iter_begin (data->pbvh, node, vd, PBVH_ITER_UNIQUE) { + if (mask & PV_CO) { + copy_v3_v3(vd.co, p->co[i]); + } + + if (mask & PV_COLOR && vd.col) { + copy_v4_v4(vd.col, p->color[i]); + } + + if (vd.mask && (mask & PV_MASK)) { + *vd.mask = p->mask[i]; + } + + i++; + } + BKE_pbvh_vertex_iter_end; +} + +void BKE_pbvh_gather_proxyarray(PBVH *pbvh, PBVHNode **nodes, int totnode) +{ + GatherProxyThread data = {.nodes = nodes, .pbvh = pbvh}; + + TaskParallelSettings settings; + BKE_pbvh_parallel_range_settings(&settings, true, totnode); + BLI_task_parallel_range(0, totnode, &data, pbvh_gather_proxyarray_exec, &settings); +} + +void BKE_pbvh_free_proxyarray(PBVH *pbvh, PBVHNode *node) +{ + ProxyVertArray *p = &node->proxyverts; + + if (p->indexmap) { + BLI_ghash_free(p->indexmap, nullptr, nullptr); + } + if (p->co) + MEM_freeN(p->co); + if (p->no) + MEM_freeN(p->no); + if (p->index) + MEM_freeN(p->index); + if (p->mask) + MEM_freeN(p->mask); + if (p->ownerco) + MEM_freeN(p->ownerco); + if (p->ownerno) + MEM_freeN(p->ownerno); + if (p->ownermask) + MEM_freeN(p->ownermask); + if (p->ownercolor) + MEM_freeN(p->ownercolor); + if (p->color) + MEM_freeN(p->color); + if (p->neighbors) + MEM_freeN(p->neighbors); + + memset(p, 0, sizeof(*p)); +} + +void BKE_pbvh_update_proxyvert(PBVH *pbvh, PBVHNode *node, ProxyVertUpdateRec *rec) +{ +} + +ProxyVertArray *BKE_pbvh_get_proxyarrays(PBVH *pbvh, PBVHNode *node) +{ + return &node->proxyverts; +} + +#endif + +/* checks if pbvh needs to sync its flat vcol shading flag with scene tool settings + scene and ob are allowd to be nullptr (in which case nothing is done). +*/ +void SCULPT_update_flat_vcol_shading(Object *ob, Scene *scene) +{ + if (!scene || !ob || !ob->sculpt || !ob->sculpt->pbvh) { + return; + } + + if (ob->sculpt->pbvh) { + bool flat_vcol_shading = ((scene->toolsettings->sculpt->flags & + SCULPT_DYNTOPO_FLAT_VCOL_SHADING) != 0); + + BKE_pbvh_set_flat_vcol_shading(ob->sculpt->pbvh, flat_vcol_shading); + } +} + +PBVHNode *BKE_pbvh_get_node(PBVH *pbvh, int node) +{ + return pbvh->nodes + node; +} + +bool BKE_pbvh_node_mark_update_index_buffer(PBVH *pbvh, PBVHNode *node) +{ + bool split_indexed = pbvh->header.bm && + (pbvh->flags & (PBVH_DYNTOPO_SMOOTH_SHADING | PBVH_FAST_DRAW)); + + if (split_indexed) { + BKE_pbvh_vert_tag_update_normal_triangulation(node); + } + + return split_indexed; +} + +void BKE_pbvh_vert_tag_update_normal_triangulation(PBVHNode *node) +{ + node->flag |= PBVH_UpdateTris; +} + +void BKE_pbvh_vert_tag_update_normal_tri_area(PBVHNode *node) +{ + node->flag |= PBVH_UpdateTriAreas; +} + +/* must be called outside of threads */ +void BKE_pbvh_face_areas_begin(PBVH *pbvh) +{ + pbvh->face_area_i ^= 1; +} + +void BKE_pbvh_update_all_tri_areas(PBVH *pbvh) +{ + /* swap read/write face area buffers */ + pbvh->face_area_i ^= 1; + + for (int i = 0; i < pbvh->totnode; i++) { + PBVHNode *node = pbvh->nodes + i; + if (node->flag & PBVH_Leaf) { + node->flag |= PBVH_UpdateTriAreas; +#if 0 + // ensure node triangulations are valid + // so we don't end up doing it inside brush threads + BKE_pbvh_bmesh_check_tris(pbvh, node); +#endif + } + } +} + +void BKE_pbvh_check_tri_areas(PBVH *pbvh, PBVHNode *node) +{ + if (!(node->flag & PBVH_UpdateTriAreas)) { + return; + } + + if (pbvh->header.type == PBVH_BMESH) { + if (node->flag & PBVH_UpdateTris) { + BKE_pbvh_bmesh_check_tris(pbvh, node); + } + + if (!node->tribuf || !node->tribuf->tottri) { + return; + } + } + + node->flag &= ~PBVH_UpdateTriAreas; + + const int cur_i = pbvh->face_area_i ^ 1; + + switch (BKE_pbvh_type(pbvh)) { + case PBVH_FACES: { + for (int i = 0; i < (int)node->totprim; i++) { + const MLoopTri *lt = &pbvh->looptri[node->prim_indices[i]]; + + if (pbvh->hide_poly && pbvh->hide_poly[lt->poly]) { + /* Skip hidden faces. */ + continue; + } + + pbvh->face_areas[lt->poly * 2 + cur_i] = 0.0f; + } + + for (int i = 0; i < (int)node->totprim; i++) { + const MLoopTri *lt = &pbvh->looptri[node->prim_indices[i]]; + + if (pbvh->hide_poly && pbvh->hide_poly[lt->poly]) { + /* Skip hidden faces. */ + continue; + } + + float area = area_tri_v3(pbvh->vert_positions[pbvh->mloop[lt->tri[0]].v], + pbvh->vert_positions[pbvh->mloop[lt->tri[1]].v], + pbvh->vert_positions[pbvh->mloop[lt->tri[2]].v]); + + pbvh->face_areas[lt->poly * 2 + cur_i] += area; + + /* sanity check on read side of read write buffer */ + if (pbvh->face_areas[lt->poly * 2 + (cur_i ^ 1)] == 0.0f) { + pbvh->face_areas[lt->poly * 2 + (cur_i ^ 1)] = pbvh->face_areas[lt->poly * 2 + cur_i]; + } + } + break; + } + case PBVH_GRIDS: + break; + case PBVH_BMESH: { + BMFace *f; + const int cd_face_area = pbvh->cd_face_area; + + TGSET_ITER (f, node->bm_faces) { + float *areabuf = (float *)BM_ELEM_CD_GET_VOID_P(f, cd_face_area); + areabuf[cur_i] = 0.0f; + } + TGSET_ITER_END; + + for (int i = 0; i < node->tribuf->tottri; i++) { + PBVHTri *tri = node->tribuf->tris + i; + + BMVert *v1 = (BMVert *)(node->tribuf->verts[tri->v[0]].i); + BMVert *v2 = (BMVert *)(node->tribuf->verts[tri->v[1]].i); + BMVert *v3 = (BMVert *)(node->tribuf->verts[tri->v[2]].i); + BMFace *f = (BMFace *)tri->f.i; + + float *areabuf = (float *)BM_ELEM_CD_GET_VOID_P(f, cd_face_area); + areabuf[cur_i] += area_tri_v3(v1->co, v2->co, v3->co); + } + + TGSET_ITER (f, node->bm_faces) { + float *areabuf = (float *)BM_ELEM_CD_GET_VOID_P(f, cd_face_area); + + /* sanity check on read side of read write buffer */ + if (areabuf[cur_i ^ 1] == 0.0f) { + areabuf[cur_i ^ 1] = areabuf[cur_i]; + } + } + TGSET_ITER_END; + + break; + } + default: + break; + } +} + +static void pbvh_pmap_to_edges_add(PBVH *pbvh, + PBVHVertRef vertex, + int **r_edges, + int *r_edges_size, + bool *heap_alloc, + int e, + int p, + int *len, + int **r_polys) +{ + for (int i = 0; i < *len; i++) { + if ((*r_edges)[i] == e) { + if ((*r_polys)[i * 2 + 1] == -1) { + (*r_polys)[i * 2 + 1] = p; + } + return; + } + } + + if (*len >= *r_edges_size) { + int newsize = *len + ((*len) >> 1) + 1; + + int *r_edges_new = (int *)MEM_malloc_arrayN(newsize, sizeof(*r_edges_new), "r_edges_new"); + int *r_polys_new = (int *)MEM_malloc_arrayN(newsize * 2, sizeof(*r_polys_new), "r_polys_new"); + + memcpy((void *)r_edges_new, (void *)*r_edges, sizeof(int) * (*r_edges_size)); + memcpy((void *)r_polys_new, (void *)(*r_polys), sizeof(int) * 2 * (*r_edges_size)); + + *r_edges_size = newsize; + + if (*heap_alloc) { + MEM_freeN(*r_polys); + MEM_freeN(*r_edges); + } + + *r_edges = r_edges_new; + *r_polys = r_polys_new; + + *heap_alloc = true; + } + + (*r_polys)[*len * 2] = p; + (*r_polys)[*len * 2 + 1] = -1; + + (*r_edges)[*len] = e; + (*len)++; +} + +void BKE_pbvh_pmap_to_edges(PBVH *pbvh, + PBVHVertRef vertex, + int **r_edges, + int *r_edges_size, + bool *r_heap_alloc, + int **r_polys) +{ + MeshElemMap *map = pbvh->pmap->pmap + vertex.i; + int len = 0; + + for (int i = 0; i < map->count; i++) { + const MPoly *mp = pbvh->mpoly + map->indices[i]; + const MLoop *ml = pbvh->mloop + mp->loopstart; + + if (pbvh->hide_poly && pbvh->hide_poly[map->indices[i]]) { + /* Skip connectivity from hidden faces. */ + continue; + } + + for (int j = 0; j < mp->totloop; j++, ml++) { + if (ml->v == vertex.i) { + pbvh_pmap_to_edges_add(pbvh, + vertex, + r_edges, + r_edges_size, + r_heap_alloc, + ME_POLY_LOOP_PREV(pbvh->mloop, mp, j)->e, + map->indices[i], + &len, + r_polys); + pbvh_pmap_to_edges_add(pbvh, + vertex, + r_edges, + r_edges_size, + r_heap_alloc, + ml->e, + map->indices[i], + &len, + r_polys); + } + } + } + + *r_edges_size = len; +} + +void BKE_pbvh_set_vemap(PBVH *pbvh, MeshElemMap *vemap) +{ + pbvh->vemap = vemap; +} + +void BKE_pbvh_get_vert_face_areas(PBVH *pbvh, PBVHVertRef vertex, float *r_areas, int valence) +{ + const int cur_i = pbvh->face_area_i; + + switch (BKE_pbvh_type(pbvh)) { + case PBVH_FACES: { + int *edges = BLI_array_alloca(edges, 16); + int *polys = BLI_array_alloca(polys, 32); + bool heap_alloc = false; + int len = 16; + + BKE_pbvh_pmap_to_edges(pbvh, vertex, &edges, &len, &heap_alloc, &polys); + len = MIN2(len, valence); + + if (pbvh->vemap) { + /* sort poly references by vemap edge ordering */ + MeshElemMap *emap = pbvh->vemap + vertex.i; + + int *polys_old = BLI_array_alloca(polys, len * 2); + memcpy((void *)polys_old, (void *)polys, sizeof(int) * len * 2); + + /* note that wire edges will break this, but + should only result in incorrect weights + and isn't worth fixing */ + + for (int i = 0; i < len; i++) { + for (int j = 0; j < len; j++) { + if (emap->indices[i] == edges[j]) { + polys[i * 2] = polys_old[j * 2]; + polys[i * 2 + 1] = polys_old[j * 2 + 1]; + } + } + } + } + for (int i = 0; i < len; i++) { + r_areas[i] = pbvh->face_areas[polys[i * 2] * 2 + cur_i]; + + if (polys[i * 2 + 1] != -1) { + r_areas[i] += pbvh->face_areas[polys[i * 2 + 1] * 2 + cur_i]; + r_areas[i] *= 0.5f; + } + } + + if (heap_alloc) { + MEM_freeN(edges); + MEM_freeN(polys); + } + + break; + } + case PBVH_BMESH: { + BMVert *v = (BMVert *)vertex.i; + BMEdge *e = v->e; + + if (!e) { + for (int i = 0; i < valence; i++) { + r_areas[i] = 1.0f; + } + + return; + } + + const int cd_face_area = pbvh->cd_face_area; + int j = 0; + + do { + float w = 0.0f; + + if (!e->l) { + w = 0.0f; + } + else { + float *a1 = (float *)BM_ELEM_CD_GET_VOID_P(e->l->f, cd_face_area); + float *a2 = (float *)BM_ELEM_CD_GET_VOID_P(e->l->radial_next->f, cd_face_area); + + w += a1[cur_i] * 0.5f; + w += a2[cur_i] * 0.5f; + } + + if (j >= valence) { + printf("%s: error, corrupt edge cycle\n", __func__); + break; + } + + r_areas[j++] = w; + + e = v == e->v1 ? e->v1_disk_link.next : e->v2_disk_link.next; + } while (e != v->e); + + for (; j < valence; j++) { + r_areas[j] = 1.0f; + } + + break; + } + + case PBVH_GRIDS: { /* estimate from edge lengths */ + int index = (int)vertex.i; + + const CCGKey *key = BKE_pbvh_get_grid_key(pbvh); + const int grid_index = index / key->grid_area; + const int vertex_index = index - grid_index * key->grid_area; + + SubdivCCGCoord coord = {.grid_index = grid_index, + .x = vertex_index % key->grid_size, + .y = vertex_index / key->grid_size}; + + SubdivCCGNeighbors neighbors; + BKE_subdiv_ccg_neighbor_coords_get(pbvh->subdiv_ccg, &coord, false, &neighbors); + + float *co1 = CCG_elem_co(key, CCG_elem_offset(key, pbvh->grids[grid_index], vertex_index)); + float totw = 0.0f; + int i = 0; + + for (i = 0; i < neighbors.size; i++) { + SubdivCCGCoord *coord2 = neighbors.coords + i; + + int vertex_index2 = coord2->y * key->grid_size + coord2->x; + + float *co2 = CCG_elem_co( + key, CCG_elem_offset(key, pbvh->grids[coord2->grid_index], vertex_index2)); + float w = len_v3v3(co1, co2); + + r_areas[i] = w; + totw += w; + } + + if (neighbors.size != valence) { + printf("%s: error!\n", __func__); + } + if (totw < 0.000001f) { + for (int i = 0; i < neighbors.size; i++) { + r_areas[i] = 1.0f; + } + } + + for (; i < valence; i++) { + r_areas[i] = 1.0f; + } + + break; + } + } +} + +void BKE_pbvh_set_stroke_id(PBVH *pbvh, int stroke_id) +{ + pbvh->stroke_id = stroke_id; +} + +static void pbvh_boundaries_flag_update(PBVH *pbvh) +{ + + if (pbvh->header.bm) { + BMVert *v; + BMIter iter; + + BM_ITER_MESH (v, &iter, pbvh->header.bm, BM_VERTS_OF_MESH) { + pbvh_boundary_update_bmesh(pbvh, v); + } + } + else { + int totvert = pbvh->totvert; + + if (BKE_pbvh_type(pbvh) == PBVH_GRIDS) { + totvert = BKE_pbvh_get_grid_num_verts(pbvh); + } + + for (int i = 0; i < totvert; i++) { + pbvh->boundary_flags[i] |= SCULPT_BOUNDARY_NEEDS_UPDATE; + } + } +} + +void BKE_pbvh_set_symmetry(PBVH *pbvh, int symmetry, int boundary_symmetry) +{ + if (symmetry == pbvh->symmetry && boundary_symmetry == pbvh->boundary_symmetry) { + return; + } + + pbvh->symmetry = symmetry; + pbvh->boundary_symmetry = boundary_symmetry; + + pbvh_boundaries_flag_update(pbvh); +} + +void BKE_pbvh_set_sculpt_verts(PBVH *pbvh, struct MSculptVert *msculptverts) +{ + pbvh->msculptverts = msculptverts; +} + +void BKE_pbvh_update_vert_boundary_grids(PBVH *pbvh, + struct SubdivCCG *subdiv_ccg, + PBVHVertRef vertex) +{ + MSculptVert *mv = pbvh->msculptverts + vertex.i; + + int *flags = pbvh->boundary_flags + vertex.i; + *flags = 0; + + /* TODO: finish this function. */ + + int index = (int)vertex.i; + + /* TODO: optimize this. We could fill #SculptVertexNeighborIter directly, + * maybe provide coordinate and mask pointers directly rather than converting + * back and forth between #CCGElem and global index. */ + const CCGKey *key = BKE_pbvh_get_grid_key(pbvh); + const int grid_index = index / key->grid_area; + const int vertex_index = index - grid_index * key->grid_area; + + SubdivCCGCoord coord = {.grid_index = grid_index, + .x = vertex_index % key->grid_size, + .y = vertex_index / key->grid_size}; + + SubdivCCGNeighbors neighbors; + BKE_subdiv_ccg_neighbor_coords_get(subdiv_ccg, &coord, false, &neighbors); + + mv->valence = neighbors.size; + mv->flag &= ~SCULPTVERT_NEED_VALENCE; +} + +void BKE_pbvh_update_vert_boundary_faces(int *boundary_flags, + const int *face_sets, + const bool *hide_poly, + const float (*vert_positions)[3], + const MEdge *medge, + const MLoop *mloop, + const MPoly *mpoly, + MSculptVert *msculptverts, + const MeshElemMap *pmap, + PBVHVertRef vertex, + const bool *sharp_edges) +{ + MSculptVert *mv = msculptverts + vertex.i; + const MeshElemMap *vert_map = &pmap[vertex.i]; + + mv->flag &= ~SCULPTVERT_VERT_FSET_HIDDEN; + + int last_fset = -1; + int last_fset2 = -1; + + int *flags = boundary_flags + vertex.i; + *flags = 0; + + int totsharp = 0, totseam = 0; + int visible = false; + + for (int i = 0; i < vert_map->count; i++) { + int f_i = vert_map->indices[i]; + + const MPoly *mp = mpoly + f_i; + const MLoop *ml = mloop + mp->loopstart; + int j = 0; + + for (j = 0; j < mp->totloop; j++, ml++) { + if (ml->v == (int)vertex.i) { + break; + } + } + + if (j < mp->totloop) { + const MEdge *me = medge + ml->e; + if (sharp_edges && sharp_edges[ml->e]) { + *flags |= SCULPT_BOUNDARY_SHARP; + totsharp++; + } + + if (me->flag & ME_SEAM) { + *flags |= SCULPT_BOUNDARY_SEAM; + totseam++; + } + } + + int fset = face_sets ? abs(face_sets[f_i]) : 1; + + if (!hide_poly || !hide_poly[f_i]) { + visible = true; + } + + if (i > 0 && fset != last_fset) { + *flags |= SCULPT_BOUNDARY_FACE_SET; + + if (i > 1 && last_fset2 != last_fset && last_fset != -1 && last_fset2 != -1 && fset != -1 && + last_fset2 != fset) { + *flags |= SCULPT_CORNER_FACE_SET; + } + } + + if (i > 0 && last_fset != fset) { + last_fset2 = last_fset; + } + + last_fset = fset; + } + + if (!visible) { + mv->flag |= SCULPTVERT_VERT_FSET_HIDDEN; + } + + if (totsharp > 2) { + *flags |= SCULPT_CORNER_SHARP; + } + + if (totseam > 2) { + *flags |= SCULPT_CORNER_SEAM; + } +} + +void BKE_pbvh_ignore_uvs_set(PBVH *pbvh, bool value) +{ + if (!!(pbvh->flags & PBVH_IGNORE_UVS) == value) { + return; // no change + } + + if (value) { + pbvh->flags |= PBVH_IGNORE_UVS; + } + else { + pbvh->flags &= ~PBVH_IGNORE_UVS; + } + + pbvh_boundaries_flag_update(pbvh); +} + +bool BKE_pbvh_cache(const struct Mesh *me, PBVH *pbvh) +{ + memset(&pbvh->cached_data, 0, sizeof(pbvh->cached_data)); + + if (pbvh->invalid) { + printf("invalid pbvh!\n"); + return false; + } + + switch (pbvh->header.type) { + case PBVH_BMESH: + if (!pbvh->header.bm) { + return false; + } + + pbvh->cached_data.bm = pbvh->header.bm; + + pbvh->cached_data.vdata = pbvh->header.bm->vdata; + pbvh->cached_data.edata = pbvh->header.bm->edata; + pbvh->cached_data.ldata = pbvh->header.bm->ldata; + pbvh->cached_data.pdata = pbvh->header.bm->pdata; + + pbvh->cached_data.totvert = pbvh->header.bm->totvert; + pbvh->cached_data.totedge = pbvh->header.bm->totedge; + pbvh->cached_data.totloop = pbvh->header.bm->totloop; + pbvh->cached_data.totpoly = pbvh->header.bm->totface; + break; + case PBVH_GRIDS: { + pbvh->cached_data.vdata = me->vdata; + pbvh->cached_data.edata = me->edata; + pbvh->cached_data.ldata = me->ldata; + pbvh->cached_data.pdata = me->pdata; + + int grid_side = pbvh->gridkey.grid_size; + + pbvh->cached_data.totvert = pbvh->totgrid * grid_side * grid_side; + pbvh->cached_data.totedge = me->totedge; + pbvh->cached_data.totloop = me->totloop; + pbvh->cached_data.totpoly = pbvh->totgrid * (grid_side - 1) * (grid_side - 1); + break; + } + case PBVH_FACES: + pbvh->cached_data.vdata = me->vdata; + pbvh->cached_data.edata = me->edata; + pbvh->cached_data.ldata = me->ldata; + pbvh->cached_data.pdata = me->pdata; + + pbvh->cached_data.totvert = me->totvert; + pbvh->cached_data.totedge = me->totedge; + pbvh->cached_data.totloop = me->totloop; + pbvh->cached_data.totpoly = me->totpoly; + break; + } + + return true; +} + +static bool customdata_is_same(const CustomData *a, const CustomData *b) +{ + return memcmp(a, b, sizeof(CustomData)) == 0; +} + +bool BKE_pbvh_cache_is_valid(const struct Object *ob, + const struct Mesh *me, + const PBVH *pbvh, + int pbvh_type) +{ + if (pbvh->invalid) { + printf("pbvh invalid!\n"); + return false; + } + + if (pbvh->header.type != pbvh_type) { + return false; + } + + bool ok = true; + int totvert = 0, totedge = 0, totloop = 0, totpoly = 0; + const CustomData *vdata, *edata, *ldata, *pdata; + + MultiresModifierData *mmd = nullptr; + + LISTBASE_FOREACH (ModifierData *, md, &ob->modifiers) { + if (md->type == eModifierType_Multires) { + mmd = (MultiresModifierData *)md; + break; + } + } + + if (mmd && (mmd->flags & eModifierMode_Realtime)) { + // return false; + } + + switch (pbvh_type) { + case PBVH_BMESH: + if (!pbvh->header.bm || pbvh->header.bm != pbvh->cached_data.bm) { + return false; + } + + totvert = pbvh->header.bm->totvert; + totedge = pbvh->header.bm->totedge; + totloop = pbvh->header.bm->totloop; + totpoly = pbvh->header.bm->totface; + + vdata = &pbvh->header.bm->vdata; + edata = &pbvh->header.bm->edata; + ldata = &pbvh->header.bm->ldata; + pdata = &pbvh->header.bm->pdata; + break; + case PBVH_FACES: + totvert = me->totvert; + totedge = me->totedge; + totloop = me->totloop; + totpoly = me->totpoly; + + vdata = &me->vdata; + edata = &me->edata; + ldata = &me->ldata; + pdata = &me->pdata; + break; + case PBVH_GRIDS: { + if (!mmd) { + return false; + } + + int grid_side = 1 + (1 << (mmd->sculptlvl - 1)); + + totvert = me->totloop * grid_side * grid_side; + totedge = me->totedge; + totloop = me->totloop; + totpoly = me->totloop * (grid_side - 1) * (grid_side - 1); + + vdata = &me->vdata; + edata = &me->edata; + ldata = &me->ldata; + pdata = &me->pdata; + break; + } + } + + ok = ok && totvert == pbvh->cached_data.totvert; + ok = ok && totedge == pbvh->cached_data.totedge; + ok = ok && totloop == pbvh->cached_data.totloop; + ok = ok && totpoly == pbvh->cached_data.totpoly; + + ok = ok && customdata_is_same(vdata, &pbvh->cached_data.vdata); + ok = ok && customdata_is_same(edata, &pbvh->cached_data.edata); + ok = ok && customdata_is_same(ldata, &pbvh->cached_data.ldata); + ok = ok && customdata_is_same(pdata, &pbvh->cached_data.pdata); + + return ok; +} + +GHash *cached_pbvhs = nullptr; +static void pbvh_clear_cached_pbvhs(PBVH *exclude) +{ + Vector pbvhs; + + GHashIterator iter; + GHASH_ITER (iter, cached_pbvhs) { + PBVH *pbvh = (PBVH *)BLI_ghashIterator_getValue(&iter); + + if (pbvh != exclude) { + pbvhs.append(pbvh); + } + } + + for (int i = 0; i < pbvhs.size(); i++) { + PBVH *pbvh = pbvhs[i]; + + if (pbvh->header.bm) { + BM_mesh_free(pbvh->header.bm); + } + + BKE_pbvh_free(pbvh); + } + + BLI_ghash_clear(cached_pbvhs, MEM_freeN, nullptr); +} + +void BKE_pbvh_clear_cache(PBVH *preserve) +{ + pbvh_clear_cached_pbvhs(nullptr); +} + +#define PBVH_CACHE_KEY_SIZE 1024 + +static void pbvh_make_cached_key(Object *ob, char out[PBVH_CACHE_KEY_SIZE]) +{ + sprintf(out, "%s:%p", ob->id.name, G.main); +} + +void BKE_pbvh_invalidate_cache(Object *ob) +{ + Object *ob_orig = DEG_get_original_object(ob); + + char key[PBVH_CACHE_KEY_SIZE]; + pbvh_make_cached_key(ob_orig, key); + +#ifdef WITH_PBVH_CACHE + PBVH *pbvh = BLI_ghash_lookup(cached_pbvhs, key); + + if (pbvh) { + BKE_pbvh_cache_remove(pbvh); + } +#endif +} + +PBVH *BKE_pbvh_get_or_free_cached(Object *ob, Mesh *me, PBVHType pbvh_type) +{ + Object *ob_orig = DEG_get_original_object(ob); + + char key[PBVH_CACHE_KEY_SIZE]; + pbvh_make_cached_key(ob_orig, key); + + PBVH *pbvh = (PBVH *)BLI_ghash_lookup(cached_pbvhs, key); + + if (!pbvh) { + return nullptr; + } + + if (BKE_pbvh_cache_is_valid(ob, me, pbvh, pbvh_type)) { + switch (pbvh_type) { + case PBVH_BMESH: + break; + case PBVH_FACES: + pbvh->vert_normals = BKE_mesh_vertex_normals_for_write(me); + case PBVH_GRIDS: + if (!pbvh->deformed) { + pbvh->vert_positions = BKE_mesh_vert_positions_for_write(me); + } + + pbvh->mloop = me->mloop; + pbvh->mpoly = me->mpoly; + pbvh->vdata = &me->vdata; + pbvh->ldata = &me->ldata; + pbvh->pdata = &me->pdata; + + pbvh->face_sets = (int *)CustomData_get_layer_named( + &me->pdata, CD_PROP_INT32, ".sculpt_face_set"); + + break; + } + + BKE_pbvh_update_active_vcol(pbvh, me); + + return pbvh; + } + + pbvh_clear_cached_pbvhs(nullptr); + return nullptr; +} + +void BKE_pbvh_set_cached(Object *ob, PBVH *pbvh) +{ + if (!pbvh) { + return; + } + + Object *ob_orig = DEG_get_original_object(ob); + + char key[PBVH_CACHE_KEY_SIZE]; + pbvh_make_cached_key(ob_orig, key); + + PBVH *exist = (PBVH *)BLI_ghash_lookup(cached_pbvhs, key); + + if (pbvh->invalid) { + printf("pbvh invalid!"); + } + + if (exist && exist->invalid) { + printf("pbvh invalid!"); + } + + if (!exist || exist != pbvh) { + pbvh_clear_cached_pbvhs(pbvh); + + char key[PBVH_CACHE_KEY_SIZE]; + pbvh_make_cached_key(ob_orig, key); + + BLI_ghash_insert(cached_pbvhs, BLI_strdup(key), pbvh); + } + +#ifdef WITH_PBVH_CACHE + BKE_pbvh_cache(BKE_object_get_original_mesh(ob_orig), pbvh); +#endif +} + +struct SculptPMap *BKE_pbvh_get_pmap(PBVH *pbvh) +{ + return pbvh->pmap; +} + +void BKE_pbvh_set_pmap(PBVH *pbvh, SculptPMap *pmap) +{ + if (pbvh->pmap != pmap) { + BKE_pbvh_pmap_aquire(pmap); + } + + pbvh->pmap = pmap; +} + +/** Does not free pbvh itself. */ +void BKE_pbvh_cache_remove(PBVH *pbvh) +{ + Vector keys; + + GHashIterator iter; + GHASH_ITER (iter, cached_pbvhs) { + PBVH *pbvh2 = (PBVH *)BLI_ghashIterator_getValue(&iter); + + if (pbvh2 == pbvh) { + keys.append((char *)BLI_ghashIterator_getKey(&iter)); + break; + } + } + + for (int i = 0; i < keys.size(); i++) { + BLI_ghash_remove(cached_pbvhs, keys[i], MEM_freeN, nullptr); + } +} + +void BKE_pbvh_set_bmesh(PBVH *pbvh, BMesh *bm) +{ + pbvh->header.bm = bm; +} + +void BKE_pbvh_free_bmesh(PBVH *pbvh, BMesh *bm) +{ + if (pbvh) { + pbvh->header.bm = nullptr; + } + + BM_mesh_free(bm); + + GHashIterator iter; + Vector keys; + Vector pbvhs; + + GHASH_ITER (iter, cached_pbvhs) { + PBVH *pbvh2 = (PBVH *)BLI_ghashIterator_getValue(&iter); + + if (pbvh2->header.bm == bm) { + pbvh2->header.bm = nullptr; + + if (pbvh2 != pbvh) { + bool ok = true; + + for (int i = 0; i < pbvhs.size(); i++) { + if (pbvhs[i] == pbvh2) { + ok = false; + } + } + + if (ok) { + pbvhs.append(pbvh2); + } + } + + keys.append((char *)BLI_ghashIterator_getKey(&iter)); + } + } + + for (int i = 0; i < keys.size(); i++) { + BLI_ghash_remove(cached_pbvhs, keys[i], MEM_freeN, nullptr); + } + + for (int i = 0; i < pbvhs.size(); i++) { + BKE_pbvh_free(pbvhs[i]); + } +} + +BMLog *BKE_pbvh_get_bm_log(PBVH *pbvh) +{ + return pbvh->bm_log; +} + +void BKE_pbvh_system_init() +{ + cached_pbvhs = BLI_ghash_str_new("pbvh cache ghash"); +} + +void BKE_pbvh_system_exit() +{ + pbvh_clear_cached_pbvhs(nullptr); + BLI_ghash_free(cached_pbvhs, nullptr, nullptr); +} + +SculptPMap *BKE_pbvh_make_pmap(const struct Mesh *me) +{ + SculptPMap *pmap = (SculptPMap *)MEM_callocN(sizeof(*pmap), "SculptPMap"); + + BKE_mesh_vert_poly_map_create(&pmap->pmap, + &pmap->pmap_mem, + BKE_mesh_vert_positions(me), + BKE_mesh_edges(me), + BKE_mesh_polys(me), + BKE_mesh_loops(me), + me->totvert, + me->totpoly, + me->totloop, + false); + + pmap->refcount = 1; + + return pmap; +} + +void BKE_pbvh_pmap_aquire(SculptPMap *pmap) +{ + pmap->refcount++; +} + +bool BKE_pbvh_pmap_release(SculptPMap *pmap) +{ + if (!pmap) { + return false; + } + + pmap->refcount--; + + // if (pmap->refcount < 0) { + // printf("%s: error!\n", __func__); + //} + + if (1 && pmap->refcount == 0) { + MEM_SAFE_FREE(pmap->pmap); + MEM_SAFE_FREE(pmap->pmap_mem); + MEM_SAFE_FREE(pmap); + + return true; + } + + return false; +} + bool BKE_pbvh_is_drawing(const PBVH *pbvh) { return pbvh->is_drawing; @@ -3563,12 +5380,41 @@ void BKE_pbvh_node_num_loops(PBVH *pbvh, PBVHNode *node, int *r_totloop) void BKE_pbvh_update_active_vcol(PBVH *pbvh, const Mesh *mesh) { - BKE_pbvh_get_color_layer(mesh, &pbvh->color_layer, &pbvh->color_domain); -} + CustomDataLayer *last_layer = pbvh->color_layer; -void BKE_pbvh_pmap_set(PBVH *pbvh, const MeshElemMap *pmap) -{ - pbvh->pmap = pmap; + Mesh me_query; + const CustomData *vdata, *ldata; + + if (pbvh->header.type == PBVH_BMESH && pbvh->header.bm) { + vdata = &pbvh->header.bm->vdata; + ldata = &pbvh->header.bm->ldata; + } + else { + vdata = &mesh->vdata; + ldata = &mesh->ldata; + } + + BKE_id_attribute_copy_domains_temp(ID_ME, vdata, nullptr, ldata, nullptr, nullptr, &me_query.id); + me_query.active_color_attribute = mesh->active_color_attribute; + + BKE_pbvh_get_color_layer(&me_query, &pbvh->color_layer, &pbvh->color_domain); + + if (pbvh->color_layer && pbvh->header.bm) { + pbvh->cd_vcol_offset = pbvh->color_layer->offset; + } + else { + pbvh->cd_vcol_offset = -1; + } + + if (pbvh->color_layer != last_layer) { + for (int i = 0; i < pbvh->totnode; i++) { + PBVHNode *node = pbvh->nodes + i; + + if (node->flag & PBVH_Leaf) { + BKE_pbvh_node_mark_update_color(node); + } + } + } } void BKE_pbvh_ensure_node_loops(PBVH *pbvh) @@ -3602,11 +5448,10 @@ void BKE_pbvh_ensure_node_loops(PBVH *pbvh) continue; } - node->loop_indices = static_cast( - MEM_malloc_arrayN(node->totprim * 3, sizeof(int), __func__)); + node->loop_indices = (int *)MEM_malloc_arrayN(node->totprim * 3, sizeof(int), __func__); node->loop_indices_num = 0; - for (int j = 0; j < node->totprim; j++) { + for (int j = 0; j < (int)node->totprim; j++) { const MLoopTri *mlt = pbvh->looptri + node->prim_indices[j]; for (int k = 0; k < 3; k++) { @@ -3621,11 +5466,97 @@ void BKE_pbvh_ensure_node_loops(PBVH *pbvh) MEM_SAFE_FREE(visit); } +bool BKE_pbvh_get_origvert( + PBVH *pbvh, PBVHVertRef vertex, const float **r_co, float **r_no, float **r_color) +{ + MSculptVert *mv; + + switch (pbvh->header.type) { + case PBVH_FACES: + case PBVH_GRIDS: + mv = pbvh->msculptverts + vertex.i; + + if (mv->stroke_id != pbvh->stroke_id) { + mv->stroke_id = pbvh->stroke_id; + float *mask = nullptr; + + if (pbvh->header.type == PBVH_FACES) { + copy_v3_v3(mv->origco, pbvh->vert_positions[vertex.i]); + copy_v3_v3(mv->origno, pbvh->vert_normals[vertex.i]); + mask = (float *)CustomData_get_layer(pbvh->vdata, CD_PAINT_MASK); + + if (mask) { + mask += vertex.i; + } + } + else { + const CCGKey *key = BKE_pbvh_get_grid_key(pbvh); + const int grid_index = vertex.i / key->grid_area; + const int vertex_index = vertex.i - grid_index * key->grid_area; + CCGElem *elem = BKE_pbvh_get_grids(pbvh)[grid_index]; + + copy_v3_v3(mv->origco, CCG_elem_co(key, CCG_elem_offset(key, elem, vertex_index))); + copy_v3_v3(mv->origno, CCG_elem_no(key, CCG_elem_offset(key, elem, vertex_index))); + mask = key->has_mask ? CCG_elem_mask(key, CCG_elem_offset(key, elem, vertex_index)) : + nullptr; + } + + if (mask) { + mv->origmask = (ushort)(*mask * 65535.0f); + } + + if (pbvh->color_layer) { + BKE_pbvh_vertex_color_get(pbvh, vertex, mv->origcolor); + } + } + break; + case PBVH_BMESH: { + BMVert *v = (BMVert *)vertex.i; + mv = BKE_PBVH_SCULPTVERT(pbvh->cd_sculpt_vert, v); + + if (mv->stroke_id != pbvh->stroke_id) { + mv->stroke_id = pbvh->stroke_id; + + copy_v3_v3(mv->origco, v->co); + copy_v3_v3(mv->origno, v->no); + + if (pbvh->cd_vert_mask_offset != -1) { + mv->origmask = (short)(BM_ELEM_CD_GET_FLOAT(v, pbvh->cd_vert_mask_offset) * 65535.0f); + } + + if (pbvh->cd_vcol_offset != -1) { + BKE_pbvh_vertex_color_get(pbvh, vertex, mv->origcolor); + } + } + break; + } + } + + if (r_co) { + *r_co = mv->origco; + } + + if (r_no) { + *r_no = mv->origno; + } + + if (r_color) { + *r_color = mv->origcolor; + } + + return true; +} + int BKE_pbvh_debug_draw_gen_get(PBVHNode *node) { return node->debug_draw_gen; } +void BKE_pbvh_set_boundary_flags(PBVH *pbvh, int *boundary_flags) +{ + pbvh->boundary_flags = boundary_flags; +} + static void pbvh_face_iter_verts_reserve(PBVHFaceIter *fd, int verts_num) { if (verts_num >= fd->verts_size_) { @@ -3635,8 +5566,7 @@ static void pbvh_face_iter_verts_reserve(PBVHFaceIter *fd, int verts_num) MEM_SAFE_FREE(fd->verts); } - fd->verts = static_cast( - MEM_malloc_arrayN(fd->verts_size_, sizeof(void *), __func__)); + fd->verts = (PBVHVertRef *)MEM_malloc_arrayN(fd->verts_size_, sizeof(void *), __func__); } fd->verts_num = verts_num; @@ -3660,14 +5590,20 @@ static void pbvh_face_iter_step(PBVHFaceIter *fd, bool do_step) switch (fd->pbvh_type_) { case PBVH_BMESH: { if (do_step) { - BLI_gsetIterator_step(&fd->bm_faces_iter_); - if (BLI_gsetIterator_done(&fd->bm_faces_iter_)) { + fd->bm_faces_iter_++; + + while (fd->bm_faces_iter_ < fd->bm_faces_->cur && + !fd->bm_faces_->elems[fd->bm_faces_iter_]) { + fd->bm_faces_iter_++; + } + + if (fd->bm_faces_iter_ >= fd->bm_faces_->cur) { return; } } - BMFace *f = (BMFace *)BLI_gsetIterator_getKey(&fd->bm_faces_iter_); - fd->face.i = intptr_t(f); + BMFace *f = (BMFace *)fd->bm_faces_->elems[fd->bm_faces_iter_]; + fd->face.i = (intptr_t)f; fd->index = f->head.index; if (fd->cd_face_set_ != -1) { @@ -3683,7 +5619,7 @@ static void pbvh_face_iter_step(PBVHFaceIter *fd, bool do_step) BMLoop *l = f->l_first; do { - fd->verts[vertex_i++].i = intptr_t(l->v); + fd->verts[vertex_i++].i = (intptr_t)l->v; } while ((l = l->next) != f->l_first); break; @@ -3779,7 +5715,8 @@ void BKE_pbvh_face_iter_init(PBVH *pbvh, PBVHNode *node, PBVHFaceIter *fd) fd->cd_hide_poly_ = CustomData_get_offset_named( &pbvh->header.bm->pdata, CD_PROP_INT32, ".hide_poly"); - BLI_gsetIterator_init(&fd->bm_faces_iter_, node->bm_faces); + fd->bm_faces_iter_ = 0; + fd->bm_faces_ = node->bm_faces; break; } @@ -3802,7 +5739,7 @@ bool BKE_pbvh_face_iter_done(PBVHFaceIter *fd) case PBVH_GRIDS: return fd->prim_index_ >= fd->node_->totprim; case PBVH_BMESH: - return BLI_gsetIterator_done(&fd->bm_faces_iter_); + return fd->bm_faces_iter_ >= fd->bm_faces_->cur; default: BLI_assert_unreachable(); return true; @@ -3849,8 +5786,8 @@ void BKE_pbvh_sync_visibility_from_verts(PBVH *pbvh, Mesh *mesh) const MPoly *mp = BKE_mesh_polys(mesh); CCGKey key = pbvh->gridkey; - bool *hide_poly = static_cast(CustomData_get_layer_named_for_write( - &mesh->pdata, CD_PROP_BOOL, ".hide_poly", mesh->totpoly)); + bool *hide_poly = (bool *)CustomData_get_layer_named_for_write( + &mesh->pdata, CD_PROP_BOOL, ".hide_poly", mesh->totpoly); bool delete_hide_poly = true; for (int face_index = 0; face_index < mesh->totpoly; face_index++, mp++) { @@ -3868,12 +5805,15 @@ void BKE_pbvh_sync_visibility_from_verts(PBVH *pbvh, Mesh *mesh) } if (hidden && !hide_poly) { - hide_poly = static_cast(CustomData_get_layer_named_for_write( - &mesh->pdata, CD_PROP_BOOL, ".hide_poly", mesh->totpoly)); + hide_poly = (bool *)CustomData_get_layer_named_for_write( + &mesh->pdata, CD_PROP_BOOL, ".hide_poly", mesh->totpoly); if (!hide_poly) { - hide_poly = static_cast(CustomData_add_layer_named( - &mesh->pdata, CD_PROP_BOOL, CD_CONSTRUCT, nullptr, mesh->totpoly, ".hide_poly")); + CustomData_add_layer_named( + &mesh->pdata, CD_PROP_BOOL, CD_CONSTRUCT, nullptr, mesh->totpoly, ".hide_poly"); + + hide_poly = (bool *)CustomData_get_layer_named_for_write( + &mesh->pdata, CD_PROP_BOOL, ".hide_poly", mesh->totpoly); } } diff --git a/source/blender/blenkernel/intern/pbvh_bmesh.cc b/source/blender/blenkernel/intern/pbvh_bmesh.cc index 35aa523c69c..861c3d9512a 100644 --- a/source/blender/blenkernel/intern/pbvh_bmesh.cc +++ b/source/blender/blenkernel/intern/pbvh_bmesh.cc @@ -4,181 +4,248 @@ * \ingroup bke */ +/* + +TODO: + +Convergence improvements: +1. DONE: Limit number of edges processed per run. +2. DONE: Scale split steps by ratio of long to short edges to + prevent runaway tesselation. +3. DONE: Detect and dissolve three and four valence vertices that are surrounded by + all tris. +4. DONE: Use different (coarser) brush spacing for applying dyntopo + +Drawing improvements: +4. PARTIAL DONE: Build and cache vertex index buffers, to reduce GPU bandwidth + +Topology rake: +5. DONE: Enable new curvature topology rake code and add to UI. +6. DONE: Add code to cache curvature data per vertex in a CD layer. + +*/ + #include "MEM_guardedalloc.h" +#include "BLI_alloca.h" #include "BLI_buffer.h" #include "BLI_ghash.h" +#include "BLI_hash.h" #include "BLI_heap_simple.h" #include "BLI_math.h" #include "BLI_memarena.h" +#include "BLI_rand.h" +#include "BLI_sort_utils.h" +#include "BLI_task.h" #include "BLI_utildefines.h" +#include "BLI_index_range.hh" +#include "BLI_map.hh" +#include "BLI_math_vector_types.hh" +#include "BLI_set.hh" +#include "BLI_vector.hh" + +#include "PIL_time.h" +#include "atomic_ops.h" + +#include "DNA_material_types.h" +#include "DNA_mesh_types.h" + #include "BKE_DerivedMesh.h" #include "BKE_ccg.h" +#include "BKE_context.h" +#include "BKE_global.h" +#include "BKE_paint.h" #include "BKE_pbvh.h" #include "DRW_pbvh.hh" +#include "atomic_ops.h" #include "bmesh.h" +#include "bmesh_log.h" +#include "dyntopo_intern.hh" #include "pbvh_intern.hh" -/* Avoid skinny faces */ -#define USE_EDGEQUEUE_EVEN_SUBDIV -#ifdef USE_EDGEQUEUE_EVEN_SUBDIV -# include "BKE_global.h" -#endif +#include +#include +#include -/* Support for only operating on front-faces */ -#define USE_EDGEQUEUE_FRONTFACE +#include -/* don't add edges into the queue multiple times */ -#define USE_EDGEQUEUE_TAG -/** - * Ensure we don't have dirty tags for the edge queue, and that they are left cleared. - * (slow, even for debug mode, so leave disabled for now). - */ -#if defined(USE_EDGEQUEUE_TAG) && 0 -# if !defined(NDEBUG) -# define USE_EDGEQUEUE_TAG_VERIFY -# endif -#endif +using blender::float2; +using blender::float3; +using blender::IndexRange; +using blender::Map; +using blender::Set; +using blender::Vector; -// #define USE_VERIFY - -#ifdef USE_VERIFY -static void pbvh_bmesh_verify(PBVH *pbvh); -#endif - -/* -------------------------------------------------------------------- */ -/** \name BMesh Utility API - * - * Use some local functions which assume triangles. - * \{ */ - -/** - * Typically using BM_LOOPS_OF_VERT and BM_FACES_OF_VERT iterators are fine, - * however this is an area where performance matters so do it in-line. - * - * Take care since 'break' won't works as expected within these macros! - */ - -#define BM_LOOPS_OF_VERT_ITER_BEGIN(l_iter_radial_, v_) \ - { \ - struct { \ - BMVert *v; \ - BMEdge *e_iter, *e_first; \ - BMLoop *l_iter_radial; \ - } _iter; \ - _iter.v = v_; \ - if (_iter.v->e) { \ - _iter.e_iter = _iter.e_first = _iter.v->e; \ - do { \ - if (_iter.e_iter->l) { \ - _iter.l_iter_radial = _iter.e_iter->l; \ - do { \ - if (_iter.l_iter_radial->v == _iter.v) { \ - l_iter_radial_ = _iter.l_iter_radial; - -#define BM_LOOPS_OF_VERT_ITER_END \ - } \ - } \ - while ((_iter.l_iter_radial = _iter.l_iter_radial->radial_next) != _iter.e_iter->l) \ - ; \ - } \ - } \ - while ((_iter.e_iter = BM_DISK_EDGE_NEXT(_iter.e_iter, _iter.v)) != _iter.e_first) \ - ; \ - } \ - } \ - ((void)0) - -#define BM_FACES_OF_VERT_ITER_BEGIN(f_iter_, v_) \ - { \ - BMLoop *l_iter_radial_; \ - BM_LOOPS_OF_VERT_ITER_BEGIN (l_iter_radial_, v_) { \ - f_iter_ = l_iter_radial_->f; - -#define BM_FACES_OF_VERT_ITER_END \ - } \ - BM_LOOPS_OF_VERT_ITER_END; \ - } \ - ((void)0) - -static void bm_edges_from_tri(BMesh *bm, BMVert *v_tri[3], BMEdge *e_tri[3]) +template T *c_array_from_vector(Vector &array) { - e_tri[0] = BM_edge_create(bm, v_tri[0], v_tri[1], nullptr, BM_CREATE_NO_DOUBLE); - e_tri[1] = BM_edge_create(bm, v_tri[1], v_tri[2], nullptr, BM_CREATE_NO_DOUBLE); - e_tri[2] = BM_edge_create(bm, v_tri[2], v_tri[0], nullptr, BM_CREATE_NO_DOUBLE); + T *ret = MEM_cnew_array(array.size(), __func__); + memcpy(static_cast(ret), static_cast(array.data()), sizeof(T) * array.size()); + return ret; } -BLI_INLINE void bm_face_as_array_index_tri(BMFace *f, int r_index[3]) +static void _debugprint(const char *fmt, ...) { - BMLoop *l = BM_FACE_FIRST_LOOP(f); - - BLI_assert(f->len == 3); - - r_index[0] = BM_elem_index_get(l->v); - l = l->next; - r_index[1] = BM_elem_index_get(l->v); - l = l->next; - r_index[2] = BM_elem_index_get(l->v); + va_list args; + va_start(args, fmt); + vprintf(fmt, args); + va_end(args); } -/** - * A version of #BM_face_exists, optimized for triangles - * when we know the loop and the opposite vertex. - * - * Check if any triangle is formed by (l_radial_first->v, l_radial_first->next->v, v_opposite), - * at either winding (since its a triangle no special checks are needed). - * - *
- * l_radial_first->v & l_radial_first->next->v
- * +---+
- * |  /
- * | /
- * + v_opposite
- * 
- * - * Its assumed that \a l_radial_first is never forming the target face. - */ -static BMFace *bm_face_exists_tri_from_loop_vert(BMLoop *l_radial_first, BMVert *v_opposite) +#ifdef PBVH_BMESH_DEBUG +ATTR_NO_OPT void pbvh_bmesh_check_nodes_simple(PBVH *pbvh) { - BLI_assert( - !ELEM(v_opposite, l_radial_first->v, l_radial_first->next->v, l_radial_first->prev->v)); - if (l_radial_first->radial_next != l_radial_first) { - BMLoop *l_radial_iter = l_radial_first->radial_next; - do { - BLI_assert(l_radial_iter->f->len == 3); - if (l_radial_iter->prev->v == v_opposite) { - return l_radial_iter->f; + for (int i = 0; i < pbvh->totnode; i++) { + PBVHNode *node = pbvh->nodes + i; + BMFace *f; + + if (!(node->flag & PBVH_Leaf)) { + continue; + } + + TGSET_ITER (f, node->bm_faces) { + if (!f || f->head.htype != BM_FACE) { + _debugprint("Corrupted (freed?) face in node->bm_faces\n"); + continue; } - } while ((l_radial_iter = l_radial_iter->radial_next) != l_radial_first); + + if (BM_ELEM_CD_GET_INT(f, pbvh->cd_face_node_offset) != i) { + _debugprint("Face in more then one node\n"); + } + } + TGSET_ITER_END; } - return nullptr; } -/** - * Uses a map of vertices to lookup the final target. - * References can't point to previous items (would cause infinite loop). - */ -static BMVert *bm_vert_hash_lookup_chain(GHash *deleted_verts, BMVert *v) +ATTR_NO_OPT void pbvh_bmesh_check_nodes(PBVH *pbvh) { - while (true) { - BMVert **v_next_p = (BMVert **)BLI_ghash_lookup_p(deleted_verts, v); - if (v_next_p == nullptr) { - /* Not remapped. */ - return v; + for (int i = 0; i < pbvh->totnode; i++) { + PBVHNode *node = pbvh->nodes + i; + + if (node->flag & PBVH_Leaf) { + pbvh_bmesh_check_other_verts(node); } - if (*v_next_p == nullptr) { - /* removed and not remapped */ - return nullptr; + } + + BMVert *v; + BMIter iter; + + BM_ITER_MESH (v, &iter, pbvh->header.bm, BM_VERTS_OF_MESH) { + int ni = BM_ELEM_CD_GET_INT(v, pbvh->cd_vert_node_offset); + + if (ni >= 0 && (!v->e || !v->e->l)) { + _debugprint("wire vert had node reference: %p (type %d)\n", v, v->head.htype); + // BM_ELEM_CD_SET_INT(v, pbvh->cd_vert_node_offset, DYNTOPO_NODE_NONE); } - /* remapped */ - v = *v_next_p; + if (ni < -1 || ni >= pbvh->totnode) { + _debugprint("vert node ref was invalid: %p (type %d)\n", v, v->head.htype); + continue; + } + + if (ni == -1) { + continue; + } + + PBVHNode *node = pbvh->nodes + ni; + if (!(node->flag & PBVH_Leaf) || !node->bm_unique_verts) { + _debugprint("vert node ref was in non leaf node"); + continue; + } + + if (!BLI_table_gset_haskey(node->bm_unique_verts, v)) { + _debugprint("vert not in node->bm_unique_verts\n"); + } + + if (BLI_table_gset_haskey(node->bm_other_verts, v)) { + _debugprint("vert in node->bm_other_verts"); + } + + MSculptVert *mv = BKE_PBVH_SCULPTVERT(pbvh->cd_sculpt_vert, v); + BKE_pbvh_bmesh_check_valence(pbvh, (PBVHVertRef){.i = (intptr_t)v}); + + if (BM_vert_edge_count(v) != mv->valence) { + _debugprint("cached vertex valence mismatch; old: %d, should be: %d\n", + mv->valence, + BM_vert_edge_count(v)); + } + } + + for (int i = 0; i < pbvh->totnode; i++) { + PBVHNode *node = pbvh->nodes + i; + BMVert *v; + BMFace *f; + + // delete nodes should + if (node->flag & PBVH_Delete) { + _debugprint("orphaned delete node\n"); + } + + if (!(node->flag & PBVH_Leaf)) { + if (node->bm_unique_verts || node->bm_other_verts || node->bm_faces) { + _debugprint("dangling leaf pointers in non-leaf node\n"); + } + + continue; + } + + TGSET_ITER (v, node->bm_unique_verts) { + int ni = BM_ELEM_CD_GET_INT(v, pbvh->cd_vert_node_offset); + + if (ni != i) { + if (ni >= 0 && ni < pbvh->totnode) { + PBVHNode *node2 = pbvh->nodes + ni; + _debugprint("v node offset is wrong, %d\n", + !node2->bm_unique_verts ? 0 : + BLI_table_gset_haskey(node2->bm_unique_verts, v)); + } + else { + _debugprint("v node offset is wrong\n"); + } + } + + if (!v || v->head.htype != BM_VERT) { + _debugprint("corruption in pbvh! bm_unique_verts\n"); + } + else if (BLI_table_gset_haskey(node->bm_other_verts, v)) { + _debugprint("v in both unique and other verts\n"); + } + } + TGSET_ITER_END; + + TGSET_ITER (f, node->bm_faces) { + if (!f || f->head.htype != BM_FACE) { + _debugprint("corruption in pbvh! bm_faces\n"); + continue; + } + + int ni = BM_ELEM_CD_GET_INT(f, pbvh->cd_face_node_offset); + if (pbvh->nodes + ni != node) { + _debugprint("face in multiple nodes!\n"); + } + } + TGSET_ITER_END; + + TGSET_ITER (v, node->bm_other_verts) { + if (!v || v->head.htype != BM_VERT) { + _debugprint("corruption in pbvh! bm_other_verts\n"); + } + else if (BLI_table_gset_haskey(node->bm_unique_verts, v)) { + _debugprint("v in both unique and other verts\n"); + } + } + TGSET_ITER_END; } } +ATTR_NO_OPT void pbvh_bmesh_pbvh_bmesh_check_nodes(PBVH *pbvh) +{ + pbvh_bmesh_check_nodes(pbvh); +} +#endif + /** \} */ /****************************** Building ******************************/ @@ -187,21 +254,27 @@ static BMVert *bm_vert_hash_lookup_chain(GHash *deleted_verts, BMVert *v) static void pbvh_bmesh_node_finalize(PBVH *pbvh, const int node_index, const int cd_vert_node_offset, - const int cd_face_node_offset) + const int cd_face_node_offset, + bool add_orco) { - GSetIterator gs_iter; PBVHNode *n = &pbvh->nodes[node_index]; bool has_visible = false; + n->draw_batches = nullptr; + /* Create vert hash sets */ - n->bm_unique_verts = BLI_gset_ptr_new("bm_unique_verts"); - n->bm_other_verts = BLI_gset_ptr_new("bm_other_verts"); + if (!n->bm_unique_verts) { + n->bm_unique_verts = BLI_table_gset_new("bm_unique_verts"); + } + n->bm_other_verts = BLI_table_gset_new("bm_other_verts"); BB_reset(&n->vb); + BB_reset(&n->orig_vb); + BMFace *f; - GSET_ITER (gs_iter, n->bm_faces) { - BMFace *f = static_cast(BLI_gsetIterator_getKey(&gs_iter)); + int cd_hide_poly = pbvh->cd_hide_poly; + TGSET_ITER (f, n->bm_faces) { /* Update ownership of faces */ BM_ELEM_CD_SET_INT(f, cd_face_node_offset, node_index); @@ -211,64 +284,146 @@ static void pbvh_bmesh_node_finalize(PBVH *pbvh, do { BMVert *v = l_iter->v; - if (!BLI_gset_haskey(n->bm_unique_verts, v)) { + MSculptVert *mv = BKE_PBVH_SCULPTVERT(pbvh->cd_sculpt_vert, v); + + int *flags = BM_ELEM_CD_PTR(v, pbvh->cd_boundary_flag); + *flags |= SCULPT_BOUNDARY_NEEDS_UPDATE; + + if (!BLI_table_gset_haskey(n->bm_unique_verts, v)) { if (BM_ELEM_CD_GET_INT(v, cd_vert_node_offset) != DYNTOPO_NODE_NONE) { - BLI_gset_add(n->bm_other_verts, v); + BLI_table_gset_add(n->bm_other_verts, v); } else { - BLI_gset_insert(n->bm_unique_verts, v); + BLI_table_gset_insert(n->bm_unique_verts, v); BM_ELEM_CD_SET_INT(v, cd_vert_node_offset, node_index); } } /* Update node bounding box */ BB_expand(&n->vb, v->co); + BB_expand(&n->orig_vb, mv->origco); } while ((l_iter = l_iter->next) != l_first); - if (!BM_elem_flag_test(f, BM_ELEM_HIDDEN)) { + if (cd_hide_poly == -1 || !BM_ELEM_CD_GET_BOOL(f, cd_hide_poly)) { has_visible = true; } } + TGSET_ITER_END BLI_assert(n->vb.bmin[0] <= n->vb.bmax[0] && n->vb.bmin[1] <= n->vb.bmax[1] && n->vb.bmin[2] <= n->vb.bmax[2]); - n->orig_vb = n->vb; - /* Build GPU buffers for new node and update vertex normals */ BKE_pbvh_node_mark_rebuild_draw(n); BKE_pbvh_node_fully_hidden_set(n, !has_visible); - n->flag |= PBVH_UpdateNormals; + n->flag |= PBVH_UpdateNormals | PBVH_UpdateCurvatureDir | PBVH_UpdateTris; + n->flag |= PBVH_UpdateBB | PBVH_UpdateOriginalBB; + + if (add_orco) { + BKE_pbvh_bmesh_check_tris(pbvh, n); + } +} + +static void pbvh_print_mem_size(PBVH *pbvh) +{ + BMesh *bm = pbvh->header.bm; + CustomData *cdatas[4] = {&bm->vdata, &bm->edata, &bm->ldata, &bm->pdata}; + + int tots[4] = {bm->totvert, bm->totedge, bm->totloop, bm->totface}; + int sizes[4] = { + (int)sizeof(BMVert), (int)sizeof(BMEdge), (int)sizeof(BMLoop), (int)sizeof(BMFace)}; + + float memsize1[4] = {0.0f, 0.0f, 0.0f, 0.0f}; + float memsize2[4] = {0.0f, 0.0f, 0.0f, 0.0f}; + float tot = 0.0f; + + for (int i = 0; i < 4; i++) { + CustomData *cdata = cdatas[i]; + + memsize1[i] = (float)(sizes[i] * tots[i]) / 1024.0f / 1024.0f; + memsize2[i] = (float)(cdata->totsize * tots[i]) / 1024.0f / 1024.0f; + + tot += memsize1[i] + memsize2[i]; + } + + printf("base sizes:\n"); + printf(" v: %.2fmb e: %.2fmb l: %.2fmb f: %.2fmb\n", + memsize1[0], + memsize1[1], + memsize1[2], + memsize1[3]); + + printf("custom attribute sizes:\n"); + printf(" v: %.2fmb e: %.2fmb l: %.2fmb f: %.2fmb\n", + memsize2[0], + memsize2[1], + memsize2[2], + memsize2[3]); + + int ptrsize = (int)sizeof(void *); + + float memsize3[3] = {(float)(ptrsize * bm->idmap.map_size) / 1024.0 / 1024.0, + (float)(ptrsize * bm->idmap.freelist_len) / 1024.0 / 1024.0, + (float)(4 * bm->idmap.free_ids_size) / 1024.0 / 1024.0}; + + printf("idmap sizes:\n map_size: %.2fmb freelist_len: %.2fmb free_ids_size: %.2fmb\n", + memsize3[0], + memsize3[1], + memsize3[2]); + + tot += memsize3[0] + memsize3[1] + memsize3[2]; + + printf("total: %.2f\n", tot); + +#ifdef WITH_BM_ID_FREELIST + if (bm->idmap.free_idx_map) { + printf("freelist length: %d\n", bm->idmap.freelist_len); + /* printf("free_idx_map: nentries %d, size %d: nfreecells: %d\n", + bm->idmap.free_idx_map->nentries, + bm->idmap.free_idx_map->nbuckets, + bm->idmap.free_idx_map->nfreecells);*/ + } +#endif } /* Recursively split the node if it exceeds the leaf_limit */ -static void pbvh_bmesh_node_split(PBVH *pbvh, const BBC *bbc_array, int node_index) +static void pbvh_bmesh_node_split( + PBVH *pbvh, const BBC *bbc_array, int node_index, bool add_orco, int depth) { const int cd_vert_node_offset = pbvh->cd_vert_node_offset; const int cd_face_node_offset = pbvh->cd_face_node_offset; PBVHNode *n = &pbvh->nodes[node_index]; - if (BLI_gset_len(n->bm_faces) <= pbvh->leaf_limit) { +#ifdef PROXY_ADVANCED + BKE_pbvh_free_proxyarray(pbvh, n); +#endif + + if (n->depth >= PBVH_STACK_FIXED_DEPTH || BLI_table_gset_len(n->bm_faces) <= pbvh->leaf_limit) { /* Node limit not exceeded */ - pbvh_bmesh_node_finalize(pbvh, node_index, cd_vert_node_offset, cd_face_node_offset); + pbvh_bmesh_node_finalize(pbvh, node_index, cd_vert_node_offset, cd_face_node_offset, add_orco); return; } /* Calculate bounding box around primitive centroids */ BB cb; BB_reset(&cb); - GSetIterator gs_iter; - GSET_ITER (gs_iter, n->bm_faces) { - const BMFace *f = static_cast(BLI_gsetIterator_getKey(&gs_iter)); + BMFace *f; + + TGSET_ITER (f, n->bm_faces) { const BBC *bbc = &bbc_array[BM_elem_index_get(f)]; BB_expand(&cb, bbc->bcentroid); } + TGSET_ITER_END /* Find widest axis and its midpoint */ const int axis = BB_widest_axis(&cb); const float mid = (cb.bmax[axis] + cb.bmin[axis]) * 0.5f; + if (isnan(mid)) { + printf("NAN ERROR! %s\n", __func__); + } + /* Add two new child nodes */ const int children = pbvh->totnode; n->children_offset = children; @@ -279,69 +434,101 @@ static void pbvh_bmesh_node_split(PBVH *pbvh, const BBC *bbc_array, int node_ind /* Initialize children */ PBVHNode *c1 = &pbvh->nodes[children], *c2 = &pbvh->nodes[children + 1]; + + c1->draw_batches = c2->draw_batches = nullptr; + c1->depth = c2->depth = n->depth + 1; + c1->flag |= PBVH_Leaf; c2->flag |= PBVH_Leaf; - c1->bm_faces = BLI_gset_ptr_new_ex("bm_faces", BLI_gset_len(n->bm_faces) / 2); - c2->bm_faces = BLI_gset_ptr_new_ex("bm_faces", BLI_gset_len(n->bm_faces) / 2); + + c1->bm_faces = BLI_table_gset_new_ex("bm_faces", BLI_table_gset_len(n->bm_faces) / 2); + c2->bm_faces = BLI_table_gset_new_ex("bm_faces", BLI_table_gset_len(n->bm_faces) / 2); + + c1->bm_unique_verts = BLI_table_gset_new("bm_unique_verts"); + c2->bm_unique_verts = BLI_table_gset_new("bm_unique_verts"); + + c1->bm_other_verts = c2->bm_other_verts = nullptr; /* Partition the parent node's faces between the two children */ - GSET_ITER (gs_iter, n->bm_faces) { - BMFace *f = static_cast(BLI_gsetIterator_getKey(&gs_iter)); + TGSET_ITER (f, n->bm_faces) { const BBC *bbc = &bbc_array[BM_elem_index_get(f)]; if (bbc->bcentroid[axis] < mid) { - BLI_gset_insert(c1->bm_faces, f); + BLI_table_gset_insert(c1->bm_faces, f); } else { - BLI_gset_insert(c2->bm_faces, f); + BLI_table_gset_insert(c2->bm_faces, f); } } - - /* Enforce at least one primitive in each node */ - GSet *empty = nullptr, *other; - if (BLI_gset_len(c1->bm_faces) == 0) { + TGSET_ITER_END +#if 0 + /* Enforce at least one primitive in each node */ + TableGSet *empty = nullptr,*other; + if (BLI_table_gset_len(c1->bm_faces) == 0) { empty = c1->bm_faces; other = c2->bm_faces; - } - else if (BLI_gset_len(c2->bm_faces) == 0) { + } else if (BLI_table_gset_len(c2->bm_faces) == 0) { empty = c2->bm_faces; other = c1->bm_faces; } + if (empty) { - GSET_ITER (gs_iter, other) { - void *key = BLI_gsetIterator_getKey(&gs_iter); - BLI_gset_insert(empty, key); - BLI_gset_remove(other, key, nullptr); + void *key; + TGSET_ITER (key,other) { + BLI_table_gset_insert(empty,key); + BLI_table_gset_remove(other,key,nullptr); break; } + TGSET_ITER_END } - +#endif /* Clear this node */ - /* Mark this node's unique verts as unclaimed */ + BMVert *v; + + /* Assign verts to c1 and c2. Note that the previous + method of simply marking them as untaken and rebuilding + unique verts later doesn't work, as it assumes that dyntopo + never assigns verts to nodes that don't contain their + faces.*/ if (n->bm_unique_verts) { - GSET_ITER (gs_iter, n->bm_unique_verts) { - BMVert *v = static_cast(BLI_gsetIterator_getKey(&gs_iter)); - BM_ELEM_CD_SET_INT(v, cd_vert_node_offset, DYNTOPO_NODE_NONE); + TGSET_ITER (v, n->bm_unique_verts) { + if (v->co[axis] < mid) { + BM_ELEM_CD_SET_INT(v, cd_vert_node_offset, (c1 - pbvh->nodes)); + BLI_table_gset_add(c1->bm_unique_verts, v); + } + else { + BM_ELEM_CD_SET_INT(v, cd_vert_node_offset, (c2 - pbvh->nodes)); + BLI_table_gset_add(c2->bm_unique_verts, v); + } } - BLI_gset_free(n->bm_unique_verts, nullptr); + TGSET_ITER_END + + BLI_table_gset_free(n->bm_unique_verts, nullptr); } - /* Unclaim faces */ - GSET_ITER (gs_iter, n->bm_faces) { - BMFace *f = static_cast(BLI_gsetIterator_getKey(&gs_iter)); - BM_ELEM_CD_SET_INT(f, cd_face_node_offset, DYNTOPO_NODE_NONE); + if (n->bm_faces) { + /* Unclaim faces */ + TGSET_ITER (f, n->bm_faces) { + BM_ELEM_CD_SET_INT(f, cd_face_node_offset, DYNTOPO_NODE_NONE); + } + TGSET_ITER_END + + BLI_table_gset_free(n->bm_faces, nullptr); } - BLI_gset_free(n->bm_faces, nullptr); if (n->bm_other_verts) { - BLI_gset_free(n->bm_other_verts, nullptr); + BLI_table_gset_free(n->bm_other_verts, nullptr); } if (n->layer_disp) { MEM_freeN(n->layer_disp); } + if (n->tribuf || n->tri_buffers) { + BKE_pbvh_bmesh_free_tris(pbvh, n); + } + n->bm_faces = nullptr; n->bm_unique_verts = nullptr; n->bm_other_verts = nullptr; @@ -349,12 +536,13 @@ static void pbvh_bmesh_node_split(PBVH *pbvh, const BBC *bbc_array, int node_ind if (n->draw_batches) { DRW_pbvh_node_free(n->draw_batches); + n->draw_batches = nullptr; } n->flag &= ~PBVH_Leaf; /* Recurse */ - pbvh_bmesh_node_split(pbvh, bbc_array, children); - pbvh_bmesh_node_split(pbvh, bbc_array, children + 1); + pbvh_bmesh_node_split(pbvh, bbc_array, children, add_orco, depth + 1); + pbvh_bmesh_node_split(pbvh, bbc_array, children + 1, add_orco, depth + 1); /* Array maybe reallocated, update current node pointer */ n = &pbvh->nodes[node_index]; @@ -367,11 +555,15 @@ static void pbvh_bmesh_node_split(PBVH *pbvh, const BBC *bbc_array, int node_ind } /* Recursively split the node if it exceeds the leaf_limit */ -static bool pbvh_bmesh_node_limit_ensure(PBVH *pbvh, int node_index) +bool pbvh_bmesh_node_limit_ensure(PBVH *pbvh, int node_index) { - GSet *bm_faces = pbvh->nodes[node_index].bm_faces; - const int bm_faces_size = BLI_gset_len(bm_faces); - if (bm_faces_size <= pbvh->leaf_limit) { + TableGSet *bm_faces = pbvh->nodes[node_index].bm_faces; + const int bm_faces_size = BLI_table_gset_len(bm_faces); + + // pbvh_bmesh_check_nodes(pbvh); + + if (bm_faces_size <= pbvh->leaf_limit || + pbvh->nodes[node_index].depth >= PBVH_STACK_FIXED_DEPTH) { /* Node limit not exceeded */ return false; } @@ -380,12 +572,22 @@ static bool pbvh_bmesh_node_limit_ensure(PBVH *pbvh, int node_index) pbvh->draw_cache_invalid = true; /* For each BMFace, store the AABB and AABB centroid */ - BBC *bbc_array = static_cast(MEM_mallocN(sizeof(BBC) * bm_faces_size, "BBC")); + BBC *bbc_array = MEM_cnew_array(bm_faces_size, "BBC"); + + BMFace *f; - GSetIterator gs_iter; int i; - GSET_ITER_INDEX (gs_iter, bm_faces, i) { - BMFace *f = static_cast(BLI_gsetIterator_getKey(&gs_iter)); + + /* + TGSET_ITER_INDEX(f, bm_faces, i) + { + } + TGSET_ITER_INDEX_END + printf("size: %d %d\n", i + 1, bm_faces_size); + */ + + TGSET_ITER_INDEX(f, bm_faces, i) + { BBC *bbc = &bbc_array[i]; BB_reset((BB *)bbc); @@ -399,1172 +601,396 @@ static bool pbvh_bmesh_node_limit_ensure(PBVH *pbvh, int node_index) /* so we can do direct lookups on 'bbc_array' */ BM_elem_index_set(f, i); /* set_dirty! */ } + TGSET_ITER_INDEX_END + /* Likely this is already dirty. */ pbvh->header.bm->elem_index_dirty |= BM_FACE; - pbvh_bmesh_node_split(pbvh, bbc_array, node_index); + pbvh_bmesh_node_split(pbvh, bbc_array, node_index, false, 0); MEM_freeN(bbc_array); + // pbvh_bmesh_check_nodes(pbvh); + return true; } /**********************************************************************/ -#if 0 -static int pbvh_bmesh_node_offset_from_elem(PBVH *pbvh, BMElem *ele) +static bool point_in_node(const PBVHNode *node, const float co[3]) { - switch (ele->head.htype) { - case BM_VERT: - return pbvh->cd_vert_node_offset; - default: - BLI_assert(ele->head.htype == BM_FACE); - return pbvh->cd_face_node_offset; + return co[0] >= node->vb.bmin[0] && co[0] <= node->vb.bmax[0] && co[1] >= node->vb.bmin[1] && + co[1] <= node->vb.bmax[1] && co[2] >= node->vb.bmin[2] && co[2] <= node->vb.bmax[2]; +} + +void bke_pbvh_insert_face_finalize(PBVH *pbvh, BMFace *f, const int ni) +{ + PBVHNode *node = pbvh->nodes + ni; + BM_ELEM_CD_SET_INT(f, pbvh->cd_face_node_offset, ni); + + if (!(node->flag & PBVH_Leaf)) { + printf("%s: major pbvh corruption error\n", __func__); + return; } + + BLI_table_gset_add(node->bm_faces, f); + + PBVHNodeFlags updateflag = PBVH_UpdateTris | PBVH_UpdateBB | PBVH_UpdateDrawBuffers | + PBVH_UpdateCurvatureDir | PBVH_UpdateOtherVerts; + updateflag |= PBVH_UpdateColor | PBVH_UpdateMask | PBVH_UpdateNormals | PBVH_UpdateOriginalBB; + updateflag |= PBVH_UpdateVisibility | PBVH_UpdateRedraw | PBVH_RebuildDrawBuffers; + + node->flag |= updateflag; + + // ensure verts are in pbvh + BMLoop *l = f->l_first; + do { + const int ni2 = BM_ELEM_CD_GET_INT(l->v, pbvh->cd_vert_node_offset); + MSculptVert *mv = BKE_PBVH_SCULPTVERT(pbvh->cd_sculpt_vert, l->v); + + BB_expand(&node->vb, l->v->co); + BB_expand(&node->orig_vb, mv->origco); + + if (ni2 == DYNTOPO_NODE_NONE) { + BM_ELEM_CD_SET_INT(l->v, pbvh->cd_vert_node_offset, ni); + BLI_table_gset_add(node->bm_unique_verts, l->v); + } + else { + PBVHNode *node2 = pbvh->nodes + ni2; + + if (ni != ni2) { + BLI_table_gset_add(node->bm_other_verts, l->v); + } + + node2->flag |= updateflag; + + BB_expand(&node2->vb, l->v->co); + BB_expand(&node2->orig_vb, mv->origco); + } + l = l->next; + } while (l != f->l_first); } -static int pbvh_bmesh_node_index_from_elem(PBVH *pbvh, void *key) +void bke_pbvh_insert_face(PBVH *pbvh, struct BMFace *f) { - const int cd_node_offset = pbvh_bmesh_node_offset_from_elem(pbvh, key); - const int node_index = BM_ELEM_CD_GET_INT((BMElem *)key, cd_node_offset); + int i = 0; + bool ok = false; + int ni = -1; - BLI_assert(node_index != DYNTOPO_NODE_NONE); - BLI_assert(node_index < pbvh->totnode); - (void)pbvh; + while (i < pbvh->totnode) { + PBVHNode *node = pbvh->nodes + i; + bool ok2 = false; - return node_index; -} + if (node->flag & PBVH_Leaf) { + ok = true; + ni = i; + break; + } -static PBVHNode *pbvh_bmesh_node_from_elem(PBVH *pbvh, void *key) -{ - return &pbvh->nodes[pbvh_bmesh_node_index_from_elem(pbvh, key)]; -} + if (node->children_offset == 0) { + continue; + } -/* typecheck */ -# define pbvh_bmesh_node_index_from_elem(pbvh, key) \ - (CHECK_TYPE_ANY(key, BMFace *, BMVert *), pbvh_bmesh_node_index_from_elem(pbvh, key)) -# define pbvh_bmesh_node_from_elem(pbvh, key) \ - (CHECK_TYPE_ANY(key, BMFace *, BMVert *), pbvh_bmesh_node_from_elem(pbvh, key)) -#endif + for (int j = 0; j < 2; j++) { + int ni2 = node->children_offset + j; + if (ni2 == 0) { + continue; + } -BLI_INLINE int pbvh_bmesh_node_index_from_vert(PBVH *pbvh, const BMVert *key) -{ - const int node_index = BM_ELEM_CD_GET_INT((const BMElem *)key, pbvh->cd_vert_node_offset); - BLI_assert(node_index != DYNTOPO_NODE_NONE); - BLI_assert(node_index < pbvh->totnode); - return node_index; -} + PBVHNode *node2 = pbvh->nodes + ni2; + BMLoop *l = f->l_first; -BLI_INLINE int pbvh_bmesh_node_index_from_face(PBVH *pbvh, const BMFace *key) -{ - const int node_index = BM_ELEM_CD_GET_INT((const BMElem *)key, pbvh->cd_face_node_offset); - BLI_assert(node_index != DYNTOPO_NODE_NONE); - BLI_assert(node_index < pbvh->totnode); - return node_index; -} + do { + if (point_in_node(node2, l->v->co)) { + i = ni2; + ok2 = true; + break; + } -BLI_INLINE PBVHNode *pbvh_bmesh_node_from_vert(PBVH *pbvh, const BMVert *key) -{ - return &pbvh->nodes[pbvh_bmesh_node_index_from_vert(pbvh, key)]; -} + l = l->next; + } while (l != f->l_first); -BLI_INLINE PBVHNode *pbvh_bmesh_node_from_face(PBVH *pbvh, const BMFace *key) -{ - return &pbvh->nodes[pbvh_bmesh_node_index_from_face(pbvh, key)]; -} + if (ok2) { + break; + } + } -static BMVert *pbvh_bmesh_vert_create(PBVH *pbvh, - int node_index, - const float co[3], - const float no[3], - const int cd_vert_mask_offset) -{ - PBVHNode *node = &pbvh->nodes[node_index]; - - BLI_assert((pbvh->totnode == 1 || node_index) && node_index <= pbvh->totnode); - - /* avoid initializing customdata because its quite involved */ - BMVert *v = BM_vert_create(pbvh->header.bm, co, nullptr, BM_CREATE_SKIP_CD); - CustomData_bmesh_set_default(&pbvh->header.bm->vdata, &v->head.data); - - /* This value is logged below */ - copy_v3_v3(v->no, no); - - BLI_gset_insert(node->bm_unique_verts, v); - BM_ELEM_CD_SET_INT(v, pbvh->cd_vert_node_offset, node_index); - - node->flag |= PBVH_UpdateDrawBuffers | PBVH_UpdateBB | PBVH_TopologyUpdated; - - /* Log the new vertex */ - BM_log_vert_added(pbvh->bm_log, v, cd_vert_mask_offset); - - return v; -} - -/** - * \note Callers are responsible for checking if the face exists before adding. - */ -static BMFace *pbvh_bmesh_face_create( - PBVH *pbvh, int node_index, BMVert *v_tri[3], BMEdge *e_tri[3], const BMFace *f_example) -{ - PBVHNode *node = &pbvh->nodes[node_index]; - - /* ensure we never add existing face */ - BLI_assert(!BM_face_exists(v_tri, 3)); - - BMFace *f = BM_face_create(pbvh->header.bm, v_tri, e_tri, 3, f_example, BM_CREATE_NOP); - f->head.hflag = f_example->head.hflag; - - BLI_gset_insert(node->bm_faces, f); - BM_ELEM_CD_SET_INT(f, pbvh->cd_face_node_offset, node_index); - - /* mark node for update */ - node->flag |= PBVH_UpdateDrawBuffers | PBVH_UpdateNormals | PBVH_TopologyUpdated; - node->flag &= ~PBVH_FullyHidden; - - /* Log the new face */ - BM_log_face_added(pbvh->bm_log, f); - - return f; -} - -/* Return the number of faces in 'node' that use vertex 'v' */ -#if 0 -static int pbvh_bmesh_node_vert_use_count(PBVH *pbvh, PBVHNode *node, BMVert *v) -{ - BMFace *f; - int count = 0; - - BM_FACES_OF_VERT_ITER_BEGIN (f, v) { - PBVHNode *f_node = pbvh_bmesh_node_from_face(pbvh, f); - if (f_node == node) { - count++; + if (!ok2) { + break; } } - BM_FACES_OF_VERT_ITER_END; - return count; -} -#endif + if (!ok) { + // find closest node + float co[3]; + int tot = 0; + BMLoop *l = f->l_first; -#define pbvh_bmesh_node_vert_use_count_is_equal(pbvh, node, v, n) \ - (pbvh_bmesh_node_vert_use_count_at_most(pbvh, node, v, (n) + 1) == n) + zero_v3(co); -static int pbvh_bmesh_node_vert_use_count_at_most(PBVH *pbvh, - PBVHNode *node, - BMVert *v, - const int count_max) -{ - int count = 0; - BMFace *f; + do { + add_v3_v3(co, l->v->co); + l = l->next; + tot++; + } while (l != f->l_first); - BM_FACES_OF_VERT_ITER_BEGIN (f, v) { - PBVHNode *f_node = pbvh_bmesh_node_from_face(pbvh, f); - if (f_node == node) { - count++; - if (count == count_max) { - return count; + mul_v3_fl(co, 1.0f / (float)tot); + float mindis = 1e17; + + for (int i = 0; i < pbvh->totnode; i++) { + PBVHNode *node = pbvh->nodes + i; + + if (!(node->flag & PBVH_Leaf)) { + continue; + } + + float cent[3]; + add_v3_v3v3(cent, node->vb.bmin, node->vb.bmax); + mul_v3_fl(cent, 0.5f); + + float dis = len_squared_v3v3(co, cent); + if (dis < mindis) { + mindis = dis; + ni = i; } } } - BM_FACES_OF_VERT_ITER_END; - return count; + if (ni < 0 || !(pbvh->nodes[ni].flag & PBVH_Leaf)) { + fprintf(stderr, "pbvh error! failed to find node to insert face into!\n"); + fflush(stderr); + return; + } + + bke_pbvh_insert_face_finalize(pbvh, f, ni); } -/* Return a node that uses vertex 'v' other than its current owner */ -static PBVHNode *pbvh_bmesh_vert_other_node_find(PBVH *pbvh, BMVert *v) +static void pbvh_bmesh_regen_node_verts(PBVH *pbvh, PBVHNode *node) { - PBVHNode *current_node = pbvh_bmesh_node_from_vert(pbvh, v); + node->flag &= ~PBVH_RebuildNodeVerts; + + int usize = BLI_table_gset_len(node->bm_unique_verts); + int osize = BLI_table_gset_len(node->bm_other_verts); + + TableGSet *old_unique_verts = node->bm_unique_verts; + + BLI_table_gset_free(node->bm_other_verts, nullptr); + + BMVert *v; + TGSET_ITER (v, old_unique_verts) { + BM_ELEM_CD_SET_INT(v, pbvh->cd_vert_node_offset, -1); + } + TGSET_ITER_END; + + node->bm_unique_verts = BLI_table_gset_new("bm_unique_verts"); + node->bm_other_verts = BLI_table_gset_new("bm_other_verts"); + + const int cd_vert_node = pbvh->cd_vert_node_offset; + const int ni = (int)(node - pbvh->nodes); + + bool update = false; + BMFace *f; + TGSET_ITER (f, node->bm_faces) { + BMLoop *l = f->l_first; + do { + int ni2 = BM_ELEM_CD_GET_INT(l->v, cd_vert_node); - BM_FACES_OF_VERT_ITER_BEGIN (f, v) { - PBVHNode *f_node = pbvh_bmesh_node_from_face(pbvh, f); + if (ni2 == DYNTOPO_NODE_NONE) { + BM_ELEM_CD_SET_INT(l->v, cd_vert_node, ni); + ni2 = ni; + update = true; + } - if (f_node != current_node) { - return f_node; + if (ni2 == ni) { + BLI_table_gset_add(node->bm_unique_verts, l->v); + } + else { + BLI_table_gset_add(node->bm_other_verts, l->v); + } + } while ((l = l->next) != f->l_first); + } + TGSET_ITER_END; + + TGSET_ITER (v, old_unique_verts) { + if (BM_ELEM_CD_GET_INT(v, pbvh->cd_vert_node_offset) == -1) { + // try to find node to insert into + BMIter iter2; + BMFace *f2; + bool ok = false; + + BM_ITER_ELEM (f2, &iter2, v, BM_FACES_OF_VERT) { + int ni2 = BM_ELEM_CD_GET_INT(f2, pbvh->cd_face_node_offset); + + if (ni2 >= 0) { + BM_ELEM_CD_SET_INT(v, pbvh->cd_vert_node_offset, ni2); + PBVHNode *node = pbvh->nodes + ni2; + + BLI_table_gset_add(node->bm_unique_verts, v); + BLI_table_gset_remove(node->bm_other_verts, v, nullptr); + + ok = true; + break; + } + } + + if (!ok) { + printf("pbvh error: orphaned vert node reference\n"); + } + } + } + TGSET_ITER_END; + + if (usize != BLI_table_gset_len(node->bm_unique_verts)) { + update = true; +#if 0 + printf("possible pbvh error: bm_unique_verts might have had bad data. old: %d, new: %d\n", + usize, + BLI_table_gset_len(node->bm_unique_verts)); +#endif + } + + if (osize != BLI_table_gset_len(node->bm_other_verts)) { + update = true; +#if 0 + printf("possible pbvh error: bm_other_verts might have had bad data. old: %d, new: %d\n", + osize, + BLI_table_gset_len(node->bm_other_verts)); +#endif + } + + if (update) { + node->flag |= PBVH_UpdateNormals | PBVH_UpdateDrawBuffers | PBVH_RebuildDrawBuffers | + PBVH_UpdateBB; + node->flag |= PBVH_UpdateOriginalBB | PBVH_UpdateRedraw | PBVH_UpdateColor | PBVH_UpdateTris | + PBVH_UpdateVisibility; + } + + BLI_table_gset_free(old_unique_verts, nullptr); +} + +void BKE_pbvh_bmesh_mark_node_regen(PBVH *pbvh, PBVHNode *node) +{ + node->flag |= PBVH_RebuildNodeVerts; +} + +PBVHNode *BKE_pbvh_get_node_leaf_safe(PBVH *pbvh, int i) +{ + if (i >= 0 && i < pbvh->totnode) { + PBVHNode *node = pbvh->nodes + i; + if ((node->flag & PBVH_Leaf) && !(node->flag & PBVH_Delete)) { + return node; } } - BM_FACES_OF_VERT_ITER_END; return nullptr; } -static void pbvh_bmesh_vert_ownership_transfer(PBVH *pbvh, PBVHNode *new_owner, BMVert *v) +void BKE_pbvh_bmesh_regen_node_verts(PBVH *pbvh) { - PBVHNode *current_owner = pbvh_bmesh_node_from_vert(pbvh, v); - /* mark node for update */ - current_owner->flag |= PBVH_UpdateDrawBuffers | PBVH_UpdateBB | PBVH_TopologyUpdated; + for (int i = 0; i < pbvh->totnode; i++) { + PBVHNode *node = pbvh->nodes + i; - BLI_assert(current_owner != new_owner); - - /* Remove current ownership */ - BLI_gset_remove(current_owner->bm_unique_verts, v, nullptr); - - /* Set new ownership */ - BM_ELEM_CD_SET_INT(v, pbvh->cd_vert_node_offset, new_owner - pbvh->nodes); - BLI_gset_insert(new_owner->bm_unique_verts, v); - BLI_gset_remove(new_owner->bm_other_verts, v, nullptr); - BLI_assert(!BLI_gset_haskey(new_owner->bm_other_verts, v)); - - /* mark node for update */ - new_owner->flag |= PBVH_UpdateDrawBuffers | PBVH_UpdateBB | PBVH_TopologyUpdated; -} - -static void pbvh_bmesh_vert_remove(PBVH *pbvh, BMVert *v) -{ - /* never match for first time */ - int f_node_index_prev = DYNTOPO_NODE_NONE; - - PBVHNode *v_node = pbvh_bmesh_node_from_vert(pbvh, v); - BLI_gset_remove(v_node->bm_unique_verts, v, nullptr); - BM_ELEM_CD_SET_INT(v, pbvh->cd_vert_node_offset, DYNTOPO_NODE_NONE); - - /* Have to check each neighboring face's node */ - BMFace *f; - BM_FACES_OF_VERT_ITER_BEGIN (f, v) { - const int f_node_index = pbvh_bmesh_node_index_from_face(pbvh, f); - - /* faces often share the same node, - * quick check to avoid redundant #BLI_gset_remove calls */ - if (f_node_index_prev != f_node_index) { - f_node_index_prev = f_node_index; - - PBVHNode *f_node = &pbvh->nodes[f_node_index]; - f_node->flag |= PBVH_UpdateDrawBuffers | PBVH_UpdateBB | PBVH_TopologyUpdated; - - /* Remove current ownership */ - BLI_gset_remove(f_node->bm_other_verts, v, nullptr); - - BLI_assert(!BLI_gset_haskey(f_node->bm_unique_verts, v)); - BLI_assert(!BLI_gset_haskey(f_node->bm_other_verts, v)); - } - } - BM_FACES_OF_VERT_ITER_END; -} - -static void pbvh_bmesh_face_remove(PBVH *pbvh, BMFace *f) -{ - PBVHNode *f_node = pbvh_bmesh_node_from_face(pbvh, f); - - /* Check if any of this face's vertices need to be removed - * from the node */ - BMLoop *l_first = BM_FACE_FIRST_LOOP(f); - BMLoop *l_iter = l_first; - do { - BMVert *v = l_iter->v; - if (pbvh_bmesh_node_vert_use_count_is_equal(pbvh, f_node, v, 1)) { - if (BLI_gset_haskey(f_node->bm_unique_verts, v)) { - /* Find a different node that uses 'v' */ - PBVHNode *new_node; - - new_node = pbvh_bmesh_vert_other_node_find(pbvh, v); - BLI_assert(new_node || BM_vert_face_count_is_equal(v, 1)); - - if (new_node) { - pbvh_bmesh_vert_ownership_transfer(pbvh, new_node, v); - } - } - else { - /* Remove from other verts */ - BLI_gset_remove(f_node->bm_other_verts, v, nullptr); - } - } - } while ((l_iter = l_iter->next) != l_first); - - /* Remove face from node and top level */ - BLI_gset_remove(f_node->bm_faces, f, nullptr); - BM_ELEM_CD_SET_INT(f, pbvh->cd_face_node_offset, DYNTOPO_NODE_NONE); - - /* Log removed face */ - BM_log_face_removed(pbvh->bm_log, f); - - /* mark node for update */ - f_node->flag |= PBVH_UpdateDrawBuffers | PBVH_UpdateNormals | PBVH_TopologyUpdated; -} - -static void pbvh_bmesh_edge_loops(BLI_Buffer *buf, BMEdge *e) -{ - /* fast-path for most common case where an edge has 2 faces, - * no need to iterate twice. - * This assumes that the buffer */ - BMLoop **data = static_cast(buf->data); - BLI_assert(buf->alloc_count >= 2); - if (LIKELY(BM_edge_loop_pair(e, &data[0], &data[1]))) { - buf->count = 2; - } - else { - BLI_buffer_reinit(buf, BM_edge_face_count(e)); - BM_iter_as_array(nullptr, BM_LOOPS_OF_EDGE, e, static_cast(buf->data), buf->count); - } -} - -static void pbvh_bmesh_node_drop_orig(PBVHNode *node) -{ - MEM_SAFE_FREE(node->bm_orco); - MEM_SAFE_FREE(node->bm_ortri); - MEM_SAFE_FREE(node->bm_orvert); - node->bm_tot_ortri = 0; -} - -/****************************** EdgeQueue *****************************/ - -struct EdgeQueue { - HeapSimple *heap; - const float *center; - float center_proj[3]; /* for when we use projected coords. */ - float radius_squared; - float limit_len_squared; -#ifdef USE_EDGEQUEUE_EVEN_SUBDIV - float limit_len; -#endif - - bool (*edge_queue_tri_in_range)(const EdgeQueue *q, BMFace *f); - - const float *view_normal; -#ifdef USE_EDGEQUEUE_FRONTFACE - uint use_view_normal : 1; -#endif -}; - -struct EdgeQueueContext { - EdgeQueue *q; - BLI_mempool *pool; - BMesh *bm; - int cd_vert_mask_offset; - int cd_vert_node_offset; - int cd_face_node_offset; -}; - -/* Only tagged edges are in the queue. */ -#ifdef USE_EDGEQUEUE_TAG -# define EDGE_QUEUE_TEST(e) BM_elem_flag_test((CHECK_TYPE_INLINE(e, BMEdge *), e), BM_ELEM_TAG) -# define EDGE_QUEUE_ENABLE(e) \ - BM_elem_flag_enable((CHECK_TYPE_INLINE(e, BMEdge *), e), BM_ELEM_TAG) -# define EDGE_QUEUE_DISABLE(e) \ - BM_elem_flag_disable((CHECK_TYPE_INLINE(e, BMEdge *), e), BM_ELEM_TAG) -#endif - -#ifdef USE_EDGEQUEUE_TAG_VERIFY -/* simply check no edges are tagged - * (it's a requirement that edges enter and leave a clean tag state) */ -static void pbvh_bmesh_edge_tag_verify(PBVH *pbvh) -{ - for (int n = 0; n < pbvh->totnode; n++) { - PBVHNode *node = &pbvh->nodes[n]; - if (node->bm_faces) { - GSetIterator gs_iter; - GSET_ITER (gs_iter, node->bm_faces) { - BMFace *f = BLI_gsetIterator_getKey(&gs_iter); - BMEdge *e_tri[3]; - BMLoop *l_iter; - - BLI_assert(f->len == 3); - l_iter = BM_FACE_FIRST_LOOP(f); - e_tri[0] = l_iter->e; - l_iter = l_iter->next; - e_tri[1] = l_iter->e; - l_iter = l_iter->next; - e_tri[2] = l_iter->e; - - BLI_assert((EDGE_QUEUE_TEST(e_tri[0]) == false) && (EDGE_QUEUE_TEST(e_tri[1]) == false) && - (EDGE_QUEUE_TEST(e_tri[2]) == false)); - } - } - } -} -#endif - -static bool edge_queue_tri_in_sphere(const EdgeQueue *q, BMFace *f) -{ - BMVert *v_tri[3]; - float c[3]; - - /* Get closest point in triangle to sphere center */ - BM_face_as_array_vert_tri(f, v_tri); - - closest_on_tri_to_point_v3(c, q->center, v_tri[0]->co, v_tri[1]->co, v_tri[2]->co); - - /* Check if triangle intersects the sphere */ - return len_squared_v3v3(q->center, c) <= q->radius_squared; -} - -static bool edge_queue_tri_in_circle(const EdgeQueue *q, BMFace *f) -{ - BMVert *v_tri[3]; - float c[3]; - float tri_proj[3][3]; - - /* Get closest point in triangle to sphere center */ - BM_face_as_array_vert_tri(f, v_tri); - - project_plane_normalized_v3_v3v3(tri_proj[0], v_tri[0]->co, q->view_normal); - project_plane_normalized_v3_v3v3(tri_proj[1], v_tri[1]->co, q->view_normal); - project_plane_normalized_v3_v3v3(tri_proj[2], v_tri[2]->co, q->view_normal); - - closest_on_tri_to_point_v3(c, q->center_proj, tri_proj[0], tri_proj[1], tri_proj[2]); - - /* Check if triangle intersects the sphere */ - return len_squared_v3v3(q->center_proj, c) <= q->radius_squared; -} - -/* Return true if the vertex mask is less than 1.0, false otherwise */ -static bool check_mask(EdgeQueueContext *eq_ctx, BMVert *v) -{ - return BM_ELEM_CD_GET_FLOAT(v, eq_ctx->cd_vert_mask_offset) < 1.0f; -} - -static void edge_queue_insert(EdgeQueueContext *eq_ctx, BMEdge *e, float priority) -{ - /* Don't let topology update affect fully masked vertices. This used to - * have a 50% mask cutoff, with the reasoning that you can't do a 50% - * topology update. But this gives an ugly border in the mesh. The mask - * should already make the brush move the vertices only 50%, which means - * that topology updates will also happen less frequent, that should be - * enough. */ - if (((eq_ctx->cd_vert_mask_offset == -1) || - (check_mask(eq_ctx, e->v1) || check_mask(eq_ctx, e->v2))) && - !(BM_elem_flag_test_bool(e->v1, BM_ELEM_HIDDEN) || - BM_elem_flag_test_bool(e->v2, BM_ELEM_HIDDEN))) { - BMVert **pair = static_cast(BLI_mempool_alloc(eq_ctx->pool)); - pair[0] = e->v1; - pair[1] = e->v2; - BLI_heapsimple_insert(eq_ctx->q->heap, priority, pair); -#ifdef USE_EDGEQUEUE_TAG - BLI_assert(EDGE_QUEUE_TEST(e) == false); - EDGE_QUEUE_ENABLE(e); -#endif - } -} - -static void long_edge_queue_edge_add(EdgeQueueContext *eq_ctx, BMEdge *e) -{ -#ifdef USE_EDGEQUEUE_TAG - if (EDGE_QUEUE_TEST(e) == false) -#endif - { - const float len_sq = BM_edge_calc_length_squared(e); - if (len_sq > eq_ctx->q->limit_len_squared) { - edge_queue_insert(eq_ctx, e, -len_sq); - } - } -} - -#ifdef USE_EDGEQUEUE_EVEN_SUBDIV -static void long_edge_queue_edge_add_recursive( - EdgeQueueContext *eq_ctx, BMLoop *l_edge, BMLoop *l_end, const float len_sq, float limit_len) -{ - BLI_assert(len_sq > square_f(limit_len)); - -# ifdef USE_EDGEQUEUE_FRONTFACE - if (eq_ctx->q->use_view_normal) { - if (dot_v3v3(l_edge->f->no, eq_ctx->q->view_normal) < 0.0f) { - return; - } - } -# endif - -# ifdef USE_EDGEQUEUE_TAG - if (EDGE_QUEUE_TEST(l_edge->e) == false) -# endif - { - edge_queue_insert(eq_ctx, l_edge->e, -len_sq); - } - - /* temp support previous behavior! */ - if (UNLIKELY(G.debug_value == 1234)) { - return; - } - - if (l_edge->radial_next != l_edge) { - /* How much longer we need to be to consider for subdividing - * (avoids subdividing faces which are only *slightly* skinny) */ -# define EVEN_EDGELEN_THRESHOLD 1.2f - /* How much the limit increases per recursion - * (avoids performing subdivisions too far away). */ -# define EVEN_GENERATION_SCALE 1.6f - - const float len_sq_cmp = len_sq * EVEN_EDGELEN_THRESHOLD; - - limit_len *= EVEN_GENERATION_SCALE; - const float limit_len_sq = square_f(limit_len); - - BMLoop *l_iter = l_edge; - do { - BMLoop *l_adjacent[2] = {l_iter->next, l_iter->prev}; - for (int i = 0; i < ARRAY_SIZE(l_adjacent); i++) { - float len_sq_other = BM_edge_calc_length_squared(l_adjacent[i]->e); - if (len_sq_other > max_ff(len_sq_cmp, limit_len_sq)) { - // edge_queue_insert(eq_ctx, l_adjacent[i]->e, -len_sq_other); - long_edge_queue_edge_add_recursive( - eq_ctx, l_adjacent[i]->radial_next, l_adjacent[i], len_sq_other, limit_len); - } - } - } while ((l_iter = l_iter->radial_next) != l_end); - -# undef EVEN_EDGELEN_THRESHOLD -# undef EVEN_GENERATION_SCALE - } -} -#endif /* USE_EDGEQUEUE_EVEN_SUBDIV */ - -static void short_edge_queue_edge_add(EdgeQueueContext *eq_ctx, BMEdge *e) -{ -#ifdef USE_EDGEQUEUE_TAG - if (EDGE_QUEUE_TEST(e) == false) -#endif - { - const float len_sq = BM_edge_calc_length_squared(e); - if (len_sq < eq_ctx->q->limit_len_squared) { - edge_queue_insert(eq_ctx, e, len_sq); - } - } -} - -static void long_edge_queue_face_add(EdgeQueueContext *eq_ctx, BMFace *f) -{ -#ifdef USE_EDGEQUEUE_FRONTFACE - if (eq_ctx->q->use_view_normal) { - if (dot_v3v3(f->no, eq_ctx->q->view_normal) < 0.0f) { - return; - } - } -#endif - - if (eq_ctx->q->edge_queue_tri_in_range(eq_ctx->q, f)) { - /* Check each edge of the face */ - BMLoop *l_first = BM_FACE_FIRST_LOOP(f); - BMLoop *l_iter = l_first; - do { -#ifdef USE_EDGEQUEUE_EVEN_SUBDIV - const float len_sq = BM_edge_calc_length_squared(l_iter->e); - if (len_sq > eq_ctx->q->limit_len_squared) { - long_edge_queue_edge_add_recursive( - eq_ctx, l_iter->radial_next, l_iter, len_sq, eq_ctx->q->limit_len); - } -#else - long_edge_queue_edge_add(eq_ctx, l_iter->e); -#endif - } while ((l_iter = l_iter->next) != l_first); - } -} - -static void short_edge_queue_face_add(EdgeQueueContext *eq_ctx, BMFace *f) -{ -#ifdef USE_EDGEQUEUE_FRONTFACE - if (eq_ctx->q->use_view_normal) { - if (dot_v3v3(f->no, eq_ctx->q->view_normal) < 0.0f) { - return; - } - } -#endif - - if (eq_ctx->q->edge_queue_tri_in_range(eq_ctx->q, f)) { - BMLoop *l_iter; - BMLoop *l_first; - - /* Check each edge of the face */ - l_iter = l_first = BM_FACE_FIRST_LOOP(f); - do { - short_edge_queue_edge_add(eq_ctx, l_iter->e); - } while ((l_iter = l_iter->next) != l_first); - } -} - -/* Create a priority queue containing vertex pairs connected by a long - * edge as defined by PBVH.bm_max_edge_len. - * - * Only nodes marked for topology update are checked, and in those - * nodes only edges used by a face intersecting the (center, radius) - * sphere are checked. - * - * The highest priority (lowest number) is given to the longest edge. - */ -static void long_edge_queue_create(EdgeQueueContext *eq_ctx, - PBVH *pbvh, - const float center[3], - const float view_normal[3], - float radius, - const bool use_frontface, - const bool use_projected) -{ - eq_ctx->q->heap = BLI_heapsimple_new(); - eq_ctx->q->center = center; - eq_ctx->q->radius_squared = radius * radius; - eq_ctx->q->limit_len_squared = pbvh->bm_max_edge_len * pbvh->bm_max_edge_len; -#ifdef USE_EDGEQUEUE_EVEN_SUBDIV - eq_ctx->q->limit_len = pbvh->bm_max_edge_len; -#endif - - eq_ctx->q->view_normal = view_normal; - -#ifdef USE_EDGEQUEUE_FRONTFACE - eq_ctx->q->use_view_normal = use_frontface; -#else - UNUSED_VARS(use_frontface); -#endif - - if (use_projected) { - eq_ctx->q->edge_queue_tri_in_range = edge_queue_tri_in_circle; - project_plane_normalized_v3_v3v3(eq_ctx->q->center_proj, center, view_normal); - } - else { - eq_ctx->q->edge_queue_tri_in_range = edge_queue_tri_in_sphere; - } - -#ifdef USE_EDGEQUEUE_TAG_VERIFY - pbvh_bmesh_edge_tag_verify(pbvh); -#endif - - for (int n = 0; n < pbvh->totnode; n++) { - PBVHNode *node = &pbvh->nodes[n]; - - /* Check leaf nodes marked for topology update */ - if ((node->flag & PBVH_Leaf) && (node->flag & PBVH_UpdateTopology) && - !(node->flag & PBVH_FullyHidden)) { - GSetIterator gs_iter; - - /* Check each face */ - GSET_ITER (gs_iter, node->bm_faces) { - BMFace *f = static_cast(BLI_gsetIterator_getKey(&gs_iter)); - - long_edge_queue_face_add(eq_ctx, f); - } - } - } -} - -/* Create a priority queue containing vertex pairs connected by a - * short edge as defined by PBVH.bm_min_edge_len. - * - * Only nodes marked for topology update are checked, and in those - * nodes only edges used by a face intersecting the (center, radius) - * sphere are checked. - * - * The highest priority (lowest number) is given to the shortest edge. - */ -static void short_edge_queue_create(EdgeQueueContext *eq_ctx, - PBVH *pbvh, - const float center[3], - const float view_normal[3], - float radius, - const bool use_frontface, - const bool use_projected) -{ - eq_ctx->q->heap = BLI_heapsimple_new(); - eq_ctx->q->center = center; - eq_ctx->q->radius_squared = radius * radius; - eq_ctx->q->limit_len_squared = pbvh->bm_min_edge_len * pbvh->bm_min_edge_len; -#ifdef USE_EDGEQUEUE_EVEN_SUBDIV - eq_ctx->q->limit_len = pbvh->bm_min_edge_len; -#endif - - eq_ctx->q->view_normal = view_normal; - -#ifdef USE_EDGEQUEUE_FRONTFACE - eq_ctx->q->use_view_normal = use_frontface; -#else - UNUSED_VARS(use_frontface); -#endif - - if (use_projected) { - eq_ctx->q->edge_queue_tri_in_range = edge_queue_tri_in_circle; - project_plane_normalized_v3_v3v3(eq_ctx->q->center_proj, center, view_normal); - } - else { - eq_ctx->q->edge_queue_tri_in_range = edge_queue_tri_in_sphere; - } - - for (int n = 0; n < pbvh->totnode; n++) { - PBVHNode *node = &pbvh->nodes[n]; - - /* Check leaf nodes marked for topology update */ - if ((node->flag & PBVH_Leaf) && (node->flag & PBVH_UpdateTopology) && - !(node->flag & PBVH_FullyHidden)) { - GSetIterator gs_iter; - - /* Check each face */ - GSET_ITER (gs_iter, node->bm_faces) { - BMFace *f = static_cast(BLI_gsetIterator_getKey(&gs_iter)); - - short_edge_queue_face_add(eq_ctx, f); - } - } - } -} - -/*************************** Topology update **************************/ - -static void pbvh_bmesh_split_edge(EdgeQueueContext *eq_ctx, - PBVH *pbvh, - BMEdge *e, - BLI_Buffer *edge_loops) -{ - float co_mid[3], no_mid[3]; - - /* Get all faces adjacent to the edge */ - pbvh_bmesh_edge_loops(edge_loops, e); - - /* Create a new vertex in current node at the edge's midpoint */ - mid_v3_v3v3(co_mid, e->v1->co, e->v2->co); - mid_v3_v3v3(no_mid, e->v1->no, e->v2->no); - normalize_v3(no_mid); - - int node_index = BM_ELEM_CD_GET_INT(e->v1, eq_ctx->cd_vert_node_offset); - BMVert *v_new = pbvh_bmesh_vert_create( - pbvh, node_index, co_mid, no_mid, eq_ctx->cd_vert_mask_offset); - - /* update paint mask */ - if (eq_ctx->cd_vert_mask_offset != -1) { - float mask_v1 = BM_ELEM_CD_GET_FLOAT(e->v1, eq_ctx->cd_vert_mask_offset); - float mask_v2 = BM_ELEM_CD_GET_FLOAT(e->v2, eq_ctx->cd_vert_mask_offset); - float mask_v_new = 0.5f * (mask_v1 + mask_v2); - - BM_ELEM_CD_SET_FLOAT(v_new, eq_ctx->cd_vert_mask_offset, mask_v_new); - } - - /* For each face, add two new triangles and delete the original */ - for (int i = 0; i < edge_loops->count; i++) { - BMLoop *l_adj = BLI_buffer_at(edge_loops, BMLoop *, i); - BMFace *f_adj = l_adj->f; - BMFace *f_new; - BMVert *v_opp, *v1, *v2; - BMVert *v_tri[3]; - BMEdge *e_tri[3]; - - BLI_assert(f_adj->len == 3); - int ni = BM_ELEM_CD_GET_INT(f_adj, eq_ctx->cd_face_node_offset); - - /* Find the vertex not in the edge */ - v_opp = l_adj->prev->v; - - /* Get e->v1 and e->v2 in the order they appear in the - * existing face so that the new faces' winding orders - * match */ - v1 = l_adj->v; - v2 = l_adj->next->v; - - if (ni != node_index && i == 0) { - pbvh_bmesh_vert_ownership_transfer(pbvh, &pbvh->nodes[ni], v_new); - } - - /** - * The 2 new faces created and assigned to `f_new` have their - * verts & edges shuffled around. - * - * - faces wind anticlockwise in this example. - * - original edge is `(v1, v2)` - * - original face is `(v1, v2, v3)` - * - *
-     *         + v3(v_opp)
-     *        /|\
-     *       / | \
-     *      /  |  \
-     *   e4/   |   \ e3
-     *    /    |e5  \
-     *   /     |     \
-     *  /  e1  |  e2  \
-     * +-------+-------+
-     * v1      v4(v_new) v2
-     *  (first) (second)
-     * 
- * - * - f_new (first): `v_tri=(v1, v4, v3), e_tri=(e1, e5, e4)` - * - f_new (second): `v_tri=(v4, v2, v3), e_tri=(e2, e3, e5)` - */ - - /* Create two new faces */ - v_tri[0] = v1; - v_tri[1] = v_new; - v_tri[2] = v_opp; - bm_edges_from_tri(pbvh->header.bm, v_tri, e_tri); - f_new = pbvh_bmesh_face_create(pbvh, ni, v_tri, e_tri, f_adj); - long_edge_queue_face_add(eq_ctx, f_new); - - v_tri[0] = v_new; - v_tri[1] = v2; - /* v_tri[2] = v_opp; */ /* unchanged */ - e_tri[0] = BM_edge_create(pbvh->header.bm, v_tri[0], v_tri[1], nullptr, BM_CREATE_NO_DOUBLE); - e_tri[2] = e_tri[1]; /* switched */ - e_tri[1] = BM_edge_create(pbvh->header.bm, v_tri[1], v_tri[2], nullptr, BM_CREATE_NO_DOUBLE); - f_new = pbvh_bmesh_face_create(pbvh, ni, v_tri, e_tri, f_adj); - long_edge_queue_face_add(eq_ctx, f_new); - - /* Delete original */ - pbvh_bmesh_face_remove(pbvh, f_adj); - BM_face_kill(pbvh->header.bm, f_adj); - - /* Ensure new vertex is in the node */ - if (!BLI_gset_haskey(pbvh->nodes[ni].bm_unique_verts, v_new)) { - BLI_gset_add(pbvh->nodes[ni].bm_other_verts, v_new); - } - - if (BM_vert_edge_count_is_over(v_opp, 8)) { - BMIter bm_iter; - BMEdge *e2; - - BM_ITER_ELEM (e2, &bm_iter, v_opp, BM_EDGES_OF_VERT) { - long_edge_queue_edge_add(eq_ctx, e2); - } - } - } - - BM_edge_kill(pbvh->header.bm, e); -} - -static bool pbvh_bmesh_subdivide_long_edges(EdgeQueueContext *eq_ctx, - PBVH *pbvh, - BLI_Buffer *edge_loops) -{ - bool any_subdivided = false; - - while (!BLI_heapsimple_is_empty(eq_ctx->q->heap)) { - BMVert **pair = static_cast(BLI_heapsimple_pop_min(eq_ctx->q->heap)); - BMVert *v1 = pair[0], *v2 = pair[1]; - BMEdge *e; - - BLI_mempool_free(eq_ctx->pool, pair); - pair = nullptr; - - /* Check that the edge still exists */ - if (!(e = BM_edge_exists(v1, v2))) { - continue; - } -#ifdef USE_EDGEQUEUE_TAG - EDGE_QUEUE_DISABLE(e); -#endif - - /* At the moment edges never get shorter (subdivision will make new edges) - * unlike collapse where edges can become longer. */ -#if 0 - if (len_squared_v3v3(v1->co, v2->co) <= eq_ctx->q->limit_len_squared) { - continue; - } -#else - BLI_assert(len_squared_v3v3(v1->co, v2->co) > eq_ctx->q->limit_len_squared); -#endif - - /* Check that the edge's vertices are still in the PBVH. It's - * possible that an edge collapse has deleted adjacent faces - * and the node has been split, thus leaving wire edges and - * associated vertices. */ - if ((BM_ELEM_CD_GET_INT(e->v1, eq_ctx->cd_vert_node_offset) == DYNTOPO_NODE_NONE) || - (BM_ELEM_CD_GET_INT(e->v2, eq_ctx->cd_vert_node_offset) == DYNTOPO_NODE_NONE)) { + if (!(node->flag & PBVH_Leaf) || !(node->flag & PBVH_RebuildNodeVerts)) { continue; } - any_subdivided = true; - - pbvh_bmesh_split_edge(eq_ctx, pbvh, e, edge_loops); + pbvh_bmesh_regen_node_verts(pbvh, node); } - -#ifdef USE_EDGEQUEUE_TAG_VERIFY - pbvh_bmesh_edge_tag_verify(pbvh); -#endif - - return any_subdivided; } -static void pbvh_bmesh_collapse_edge(PBVH *pbvh, - BMEdge *e, - BMVert *v1, - BMVert *v2, - GHash *deleted_verts, - BLI_Buffer *deleted_faces, - EdgeQueueContext *eq_ctx) +/************************* Called from pbvh.c *************************/ + +static bool pbvh_poly_hidden(PBVH *pbvh, BMFace *f) { - BMVert *v_del, *v_conn; - - /* one of the two vertices may be masked, select the correct one for deletion */ - if (BM_ELEM_CD_GET_FLOAT(v1, eq_ctx->cd_vert_mask_offset) < - BM_ELEM_CD_GET_FLOAT(v2, eq_ctx->cd_vert_mask_offset)) { - v_del = v1; - v_conn = v2; - } - else { - v_del = v2; - v_conn = v1; - } - - /* Remove the merge vertex from the PBVH */ - pbvh_bmesh_vert_remove(pbvh, v_del); - - /* Remove all faces adjacent to the edge */ - BMLoop *l_adj; - while ((l_adj = e->l)) { - BMFace *f_adj = l_adj->f; - - pbvh_bmesh_face_remove(pbvh, f_adj); - BM_face_kill(pbvh->header.bm, f_adj); - } - - /* Kill the edge */ - BLI_assert(BM_edge_is_wire(e)); - BM_edge_kill(pbvh->header.bm, e); - - /* For all remaining faces of v_del, create a new face that is the - * same except it uses v_conn instead of v_del */ - /* NOTE: this could be done with BM_vert_splice(), but that - * requires handling other issues like duplicate edges, so doesn't - * really buy anything. */ - BLI_buffer_clear(deleted_faces); - - BMLoop *l; - - BM_LOOPS_OF_VERT_ITER_BEGIN (l, v_del) { - BMFace *existing_face; - - /* Get vertices, replace use of v_del with v_conn */ - // BM_iter_as_array(nullptr, BM_VERTS_OF_FACE, f, (void **)v_tri, 3); - BMFace *f = l->f; -#if 0 - BMVert *v_tri[3]; - BM_face_as_array_vert_tri(f, v_tri); - for (int i = 0; i < 3; i++) { - if (v_tri[i] == v_del) { - v_tri[i] = v_conn; - } - } -#endif - - /* Check if a face using these vertices already exists. If so, - * skip adding this face and mark the existing one for - * deletion as well. Prevents extraneous "flaps" from being - * created. */ -#if 0 - if (UNLIKELY(existing_face = BM_face_exists(v_tri, 3))) -#else - if (UNLIKELY(existing_face = bm_face_exists_tri_from_loop_vert(l->next, v_conn))) -#endif - { - BLI_buffer_append(deleted_faces, BMFace *, existing_face); - } - else - { - BMVert *v_tri[3] = {v_conn, l->next->v, l->prev->v}; - - BLI_assert(!BM_face_exists(v_tri, 3)); - BMEdge *e_tri[3]; - PBVHNode *n = pbvh_bmesh_node_from_face(pbvh, f); - int ni = n - pbvh->nodes; - bm_edges_from_tri(pbvh->header.bm, v_tri, e_tri); - pbvh_bmesh_face_create(pbvh, ni, v_tri, e_tri, f); - - /* Ensure that v_conn is in the new face's node */ - if (!BLI_gset_haskey(n->bm_unique_verts, v_conn)) { - BLI_gset_add(n->bm_other_verts, v_conn); - } - } - - BLI_buffer_append(deleted_faces, BMFace *, f); - } - BM_LOOPS_OF_VERT_ITER_END; - - /* Delete the tagged faces */ - for (int i = 0; i < deleted_faces->count; i++) { - BMFace *f_del = BLI_buffer_at(deleted_faces, BMFace *, i); - - /* Get vertices and edges of face */ - BLI_assert(f_del->len == 3); - BMLoop *l_iter = BM_FACE_FIRST_LOOP(f_del); - BMVert *v_tri[3]; - BMEdge *e_tri[3]; - v_tri[0] = l_iter->v; - e_tri[0] = l_iter->e; - l_iter = l_iter->next; - v_tri[1] = l_iter->v; - e_tri[1] = l_iter->e; - l_iter = l_iter->next; - v_tri[2] = l_iter->v; - e_tri[2] = l_iter->e; - - /* Remove the face */ - pbvh_bmesh_face_remove(pbvh, f_del); - BM_face_kill(pbvh->header.bm, f_del); - - /* Check if any of the face's edges are now unused by any - * face, if so delete them */ - for (int j = 0; j < 3; j++) { - if (BM_edge_is_wire(e_tri[j])) { - BM_edge_kill(pbvh->header.bm, e_tri[j]); - } - } - - /* Check if any of the face's vertices are now unused, if so - * remove them from the PBVH */ - for (int j = 0; j < 3; j++) { - if ((v_tri[j] != v_del) && (v_tri[j]->e == nullptr)) { - pbvh_bmesh_vert_remove(pbvh, v_tri[j]); - - BM_log_vert_removed(pbvh->bm_log, v_tri[j], eq_ctx->cd_vert_mask_offset); - - if (v_tri[j] == v_conn) { - v_conn = nullptr; - } - BLI_ghash_insert(deleted_verts, v_tri[j], nullptr); - BM_vert_kill(pbvh->header.bm, v_tri[j]); - } - } - } - - /* Move v_conn to the midpoint of v_conn and v_del (if v_conn still exists, it - * may have been deleted above) */ - if (v_conn != nullptr) { - BM_log_vert_before_modified(pbvh->bm_log, v_conn, eq_ctx->cd_vert_mask_offset); - mid_v3_v3v3(v_conn->co, v_conn->co, v_del->co); - add_v3_v3(v_conn->no, v_del->no); - normalize_v3(v_conn->no); - - /* Update bounding boxes attached to the connected vertex. - * Note that we can often get-away without this but causes T48779. */ - BM_LOOPS_OF_VERT_ITER_BEGIN (l, v_conn) { - PBVHNode *f_node = pbvh_bmesh_node_from_face(pbvh, l->f); - f_node->flag |= PBVH_UpdateDrawBuffers | PBVH_UpdateNormals | PBVH_UpdateBB; - } - BM_LOOPS_OF_VERT_ITER_END; - } - - /* Delete v_del */ - BLI_assert(!BM_vert_face_check(v_del)); - BM_log_vert_removed(pbvh->bm_log, v_del, eq_ctx->cd_vert_mask_offset); - /* v_conn == nullptr is OK */ - BLI_ghash_insert(deleted_verts, v_del, v_conn); - BM_vert_kill(pbvh->header.bm, v_del); + return pbvh->cd_hide_poly != -1 && BM_ELEM_CD_GET_BOOL(f, pbvh->cd_hide_poly); } -static bool pbvh_bmesh_collapse_short_edges(EdgeQueueContext *eq_ctx, - PBVH *pbvh, - BLI_Buffer *deleted_faces) +bool BKE_pbvh_bmesh_check_origdata(PBVH *pbvh, BMVert *v, int stroke_id) { - const float min_len_squared = pbvh->bm_min_edge_len * pbvh->bm_min_edge_len; - bool any_collapsed = false; - /* deleted verts point to vertices they were merged into, or nullptr when removed. */ - GHash *deleted_verts = BLI_ghash_ptr_new("deleted_verts"); + PBVHVertRef vertex = {(intptr_t)v}; - while (!BLI_heapsimple_is_empty(eq_ctx->q->heap)) { - BMVert **pair = static_cast(BLI_heapsimple_pop_min(eq_ctx->q->heap)); - BMVert *v1 = pair[0], *v2 = pair[1]; - BLI_mempool_free(eq_ctx->pool, pair); - pair = nullptr; - - /* Check the verts still exist */ - if (!(v1 = bm_vert_hash_lookup_chain(deleted_verts, v1)) || - !(v2 = bm_vert_hash_lookup_chain(deleted_verts, v2)) || (v1 == v2)) { - continue; - } - - /* Check that the edge still exists */ - BMEdge *e; - if (!(e = BM_edge_exists(v1, v2))) { - continue; - } -#ifdef USE_EDGEQUEUE_TAG - EDGE_QUEUE_DISABLE(e); -#endif - - if (len_squared_v3v3(v1->co, v2->co) >= min_len_squared) { - continue; - } - - /* Check that the edge's vertices are still in the PBVH. It's - * possible that an edge collapse has deleted adjacent faces - * and the node has been split, thus leaving wire edges and - * associated vertices. */ - if ((BM_ELEM_CD_GET_INT(e->v1, eq_ctx->cd_vert_node_offset) == DYNTOPO_NODE_NONE) || - (BM_ELEM_CD_GET_INT(e->v2, eq_ctx->cd_vert_node_offset) == DYNTOPO_NODE_NONE)) { - continue; - } - - any_collapsed = true; - - pbvh_bmesh_collapse_edge(pbvh, e, v1, v2, deleted_verts, deleted_faces, eq_ctx); - } - - BLI_ghash_free(deleted_verts, nullptr, nullptr); - - return any_collapsed; + return BKE_pbvh_get_origvert(pbvh, vertex, nullptr, nullptr, nullptr); } -/************************* Called from pbvh.cc *************************/ - -bool pbvh_bmesh_node_raycast(PBVHNode *node, +bool pbvh_bmesh_node_raycast(PBVH *pbvh, + PBVHNode *node, const float ray_start[3], const float ray_normal[3], - IsectRayPrecalc *isect_precalc, + struct IsectRayPrecalc *isect_precalc, + int *hit_count, float *depth, + float *back_depth, bool use_original, PBVHVertRef *r_active_vertex, - float *r_face_normal) + PBVHFaceRef *r_active_face, + float *r_face_normal, + int stroke_id) { bool hit = false; float nearest_vertex_co[3] = {0.0f}; - BLI_assert(!use_original || (BLI_gset_len(node->bm_faces) > 0 && node->bm_tot_ortri)); - - use_original = use_original && node->bm_tot_ortri; - GSetIterator gs_iter; - if (use_original && node->bm_tot_ortri) { - for (int i = 0; i < node->bm_tot_ortri; i++) { - float *cos[3]; + BKE_pbvh_bmesh_check_tris(pbvh, node); - cos[0] = node->bm_orco[node->bm_ortri[i][0]]; - cos[1] = node->bm_orco[node->bm_ortri[i][1]]; - cos[2] = node->bm_orco[node->bm_ortri[i][2]]; + for (int i = 0; i < node->tribuf->tottri; i++) { + PBVHTri *tri = node->tribuf->tris + i; + BMVert *verts[3] = { + (BMVert *)node->tribuf->verts[tri->v[0]].i, + (BMVert *)node->tribuf->verts[tri->v[1]].i, + (BMVert *)node->tribuf->verts[tri->v[2]].i, + }; - if (ray_face_intersection_tri(ray_start, isect_precalc, cos[0], cos[1], cos[2], depth)) { - hit = true; + float *cos[3]; + float *nos[3]; - if (r_face_normal) { - normal_tri_v3(r_face_normal, cos[0], cos[1], cos[2]); - } + if (use_original) { + BKE_pbvh_bmesh_check_origdata(pbvh, verts[0], stroke_id); + BKE_pbvh_bmesh_check_origdata(pbvh, verts[1], stroke_id); + BKE_pbvh_bmesh_check_origdata(pbvh, verts[2], stroke_id); - if (r_active_vertex) { - float location[3] = {0.0f}; - madd_v3_v3v3fl(location, ray_start, ray_normal, *depth); - for (int j = 0; j < 3; j++) { - if (j == 0 || len_squared_v3v3(location, cos[j]) < - len_squared_v3v3(location, nearest_vertex_co)) { - copy_v3_v3(nearest_vertex_co, cos[j]); - r_active_vertex->i = intptr_t(node->bm_orvert[node->bm_ortri[i][j]]); - } + MSculptVert *mv1 = BM_ELEM_CD_PTR(verts[0], pbvh->cd_sculpt_vert); + MSculptVert *mv2 = BM_ELEM_CD_PTR(verts[1], pbvh->cd_sculpt_vert); + MSculptVert *mv3 = BM_ELEM_CD_PTR(verts[2], pbvh->cd_sculpt_vert); + + cos[0] = mv1->origco; + cos[1] = mv2->origco; + cos[2] = mv3->origco; + + nos[0] = mv1->origno; + nos[1] = mv2->origno; + nos[2] = mv3->origno; + } + else { + for (int j = 0; j < 3; j++) { + cos[j] = verts[j]->co; + nos[j] = verts[j]->no; + } + } + + if (ray_face_intersection_depth_tri( + ray_start, isect_precalc, cos[0], cos[1], cos[2], depth, back_depth, hit_count)) { + hit = true; + + if (r_face_normal) { + normal_tri_v3(r_face_normal, cos[0], cos[1], cos[2]); + } + + if (r_active_vertex) { + float location[3] = {0.0f}; + madd_v3_v3v3fl(location, ray_start, ray_normal, *depth); + for (int j = 0; j < 3; j++) { + if (j == 0 || + len_squared_v3v3(location, cos[j]) < len_squared_v3v3(location, nearest_vertex_co)) { + copy_v3_v3(nearest_vertex_co, cos[j]); + r_active_vertex->i = (intptr_t)verts[j]; } } } - } - } - else { - GSET_ITER (gs_iter, node->bm_faces) { - BMFace *f = static_cast(BLI_gsetIterator_getKey(&gs_iter)); - BLI_assert(f->len == 3); - - if (!BM_elem_flag_test(f, BM_ELEM_HIDDEN)) { - BMVert *v_tri[3]; - - BM_face_as_array_vert_tri(f, v_tri); - if (ray_face_intersection_tri( - ray_start, isect_precalc, v_tri[0]->co, v_tri[1]->co, v_tri[2]->co, depth)) { - hit = true; - - if (r_face_normal) { - normal_tri_v3(r_face_normal, v_tri[0]->co, v_tri[1]->co, v_tri[2]->co); - } - - if (r_active_vertex) { - float location[3] = {0.0f}; - madd_v3_v3v3fl(location, ray_start, ray_normal, *depth); - for (int j = 0; j < 3; j++) { - if (j == 0 || len_squared_v3v3(location, v_tri[j]->co) < - len_squared_v3v3(location, nearest_vertex_co)) { - copy_v3_v3(nearest_vertex_co, v_tri[j]->co); - r_active_vertex->i = intptr_t(v_tri[j]); - } - } - } - } + if (r_active_face) { + *r_active_face = tri->f; } } } @@ -1572,111 +998,307 @@ bool pbvh_bmesh_node_raycast(PBVHNode *node, return hit; } -bool BKE_pbvh_bmesh_node_raycast_detail(PBVHNode *node, +bool BKE_pbvh_bmesh_node_raycast_detail(PBVH *pbvh, + PBVHNode *node, const float ray_start[3], - IsectRayPrecalc *isect_precalc, + struct IsectRayPrecalc *isect_precalc, float *depth, float *r_edge_length) { if (node->flag & PBVH_FullyHidden) { - return 0; + return false; } - GSetIterator gs_iter; - bool hit = false; - BMFace *f_hit = nullptr; + BKE_pbvh_bmesh_check_tris(pbvh, node); + for (int i = 0; i < node->tribuf->tottri; i++) { + PBVHTri *tri = node->tribuf->tris + i; + BMVert *v1 = (BMVert *)node->tribuf->verts[tri->v[0]].i; + BMVert *v2 = (BMVert *)node->tribuf->verts[tri->v[1]].i; + BMVert *v3 = (BMVert *)node->tribuf->verts[tri->v[2]].i; + BMFace *f = (BMFace *)tri->f.i; - GSET_ITER (gs_iter, node->bm_faces) { - BMFace *f = static_cast(BLI_gsetIterator_getKey(&gs_iter)); + if (pbvh_poly_hidden(pbvh, f)) { + continue; + } - BLI_assert(f->len == 3); - if (!BM_elem_flag_test(f, BM_ELEM_HIDDEN)) { - BMVert *v_tri[3]; - bool hit_local; - BM_face_as_array_vert_tri(f, v_tri); - hit_local = ray_face_intersection_tri( - ray_start, isect_precalc, v_tri[0]->co, v_tri[1]->co, v_tri[2]->co, depth); + bool hit_local = ray_face_intersection_tri( + ray_start, isect_precalc, v1->co, v2->co, v3->co, depth); - if (hit_local) { - f_hit = f; - hit = true; - } + if (hit_local) { + float len1 = len_squared_v3v3(v1->co, v2->co); + float len2 = len_squared_v3v3(v2->co, v3->co); + float len3 = len_squared_v3v3(v3->co, v1->co); + + /* detail returned will be set to the maximum allowed size, so take max here */ + *r_edge_length = sqrtf(max_fff(len1, len2, len3)); + + return true; } } - if (hit) { - BMVert *v_tri[3]; - BM_face_as_array_vert_tri(f_hit, v_tri); - float len1 = len_squared_v3v3(v_tri[0]->co, v_tri[1]->co); - float len2 = len_squared_v3v3(v_tri[1]->co, v_tri[2]->co); - float len3 = len_squared_v3v3(v_tri[2]->co, v_tri[0]->co); - - /* detail returned will be set to the maximum allowed size, so take max here */ - *r_edge_length = sqrtf(max_fff(len1, len2, len3)); - } - - return hit; + return false; } -bool pbvh_bmesh_node_nearest_to_ray(PBVHNode *node, +bool pbvh_bmesh_node_nearest_to_ray(PBVH *pbvh, + PBVHNode *node, const float ray_start[3], const float ray_normal[3], float *depth, float *dist_sq, - bool use_original) + bool use_original, + int stroke_id) { bool hit = false; - if (use_original && node->bm_tot_ortri) { - for (int i = 0; i < node->bm_tot_ortri; i++) { - const int *t = node->bm_ortri[i]; - hit |= ray_face_nearest_tri(ray_start, - ray_normal, - node->bm_orco[t[0]], - node->bm_orco[t[1]], - node->bm_orco[t[2]], - depth, - dist_sq); + BKE_pbvh_bmesh_check_tris(pbvh, node); + PBVHTriBuf *tribuf = node->tribuf; + const int cd_sculpt_vert = pbvh->cd_sculpt_vert; + + for (int i = 0; i < tribuf->tottri; i++) { + PBVHTri *tri = tribuf->tris + i; + BMFace *f = (BMFace *)tri->f.i; + + if (pbvh_poly_hidden(pbvh, f)) { + continue; } - } - else { - GSetIterator gs_iter; - GSET_ITER (gs_iter, node->bm_faces) { - BMFace *f = static_cast(BLI_gsetIterator_getKey(&gs_iter)); + BMVert *v1 = (BMVert *)tribuf->verts[tri->v[0]].i; + BMVert *v2 = (BMVert *)tribuf->verts[tri->v[1]].i; + BMVert *v3 = (BMVert *)tribuf->verts[tri->v[2]].i; - BLI_assert(f->len == 3); - if (!BM_elem_flag_test(f, BM_ELEM_HIDDEN)) { - BMVert *v_tri[3]; + float *co1, *co2, *co3; - BM_face_as_array_vert_tri(f, v_tri); - hit |= ray_face_nearest_tri( - ray_start, ray_normal, v_tri[0]->co, v_tri[1]->co, v_tri[2]->co, depth, dist_sq); - } + if (use_original) { + BKE_pbvh_bmesh_check_origdata(pbvh, v1, stroke_id); + BKE_pbvh_bmesh_check_origdata(pbvh, v2, stroke_id); + BKE_pbvh_bmesh_check_origdata(pbvh, v3, stroke_id); + + co1 = BKE_PBVH_SCULPTVERT(cd_sculpt_vert, v1)->origco; + co2 = BKE_PBVH_SCULPTVERT(cd_sculpt_vert, v2)->origco; + co3 = BKE_PBVH_SCULPTVERT(cd_sculpt_vert, v3)->origco; } + else { + co1 = v1->co; + co2 = v2->co; + co3 = v3->co; + } + + hit |= ray_face_nearest_tri(ray_start, ray_normal, co1, co2, co3, depth, dist_sq); } return hit; } -void pbvh_bmesh_normals_update(PBVHNode **nodes, int totnode) +struct UpdateNormalsTaskData { + PBVHNode *node; + Vector border_verts; + int cd_sculpt_vert; + int cd_vert_node_offset; + int cd_face_node_offset; + int node_nr; +}; + +static void pbvh_update_normals_task_cb(void *__restrict userdata, + const int n, + const TaskParallelTLS *__restrict /* tls */) +{ + BMVert *v; + BMFace *f; + UpdateNormalsTaskData *data = ((UpdateNormalsTaskData *)userdata) + n; + PBVHNode *node = data->node; + const int node_nr = data->node_nr; + + const int cd_face_node_offset = data->cd_face_node_offset; + const int cd_vert_node_offset = data->cd_vert_node_offset; + + node->flag |= PBVH_UpdateCurvatureDir; + +#ifdef NORMAL_VERT_BAD +# undef NORMAL_VERT_BAD +#endif +#define NORMAL_VERT_BAD(v) \ + (!v->e || BM_ELEM_CD_GET_INT((v), cd_vert_node_offset) != node_nr || \ + (BKE_PBVH_SCULPTVERT(data->cd_sculpt_vert, (v))->flag & SCULPTVERT_PBVH_BOUNDARY)) + + const char tag = BM_ELEM_TAG_ALT; + + TGSET_ITER (v, node->bm_unique_verts) { + PBVH_CHECK_NAN(v->no); + + if (NORMAL_VERT_BAD(v)) { + v->head.hflag |= tag; + data->border_verts.append(v); + continue; + } + + v->head.hflag &= ~tag; + + BMEdge *e = v->e; + do { + BMLoop *l = e->l; + + if (!l) { + continue; + } + + do { + if (BM_ELEM_CD_GET_INT(l->f, cd_face_node_offset) != node_nr) { + v->head.hflag |= tag; + goto loop_exit; + } + } while ((l = l->radial_next) != e->l); + } while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e); + loop_exit: + + if (v->head.hflag & tag) { + data->border_verts.append(v); + continue; + } + + zero_v3(v->no); + } + TGSET_ITER_END + + TGSET_ITER (f, node->bm_faces) { + BM_face_normal_update(f); + + PBVH_CHECK_NAN(f->no); + + BMLoop *l = f->l_first; + do { + PBVH_CHECK_NAN(l->v->no); + + if (BM_ELEM_CD_GET_INT(l->v, cd_vert_node_offset) == node_nr && !(l->v->head.hflag & tag)) { + add_v3_v3(l->v->no, f->no); + } + } while ((l = l->next) != f->l_first); + } + TGSET_ITER_END + + TGSET_ITER (v, node->bm_unique_verts) { + PBVH_CHECK_NAN(v->no); + + if (dot_v3v3(v->no, v->no) == 0.0f) { + data->border_verts.append(v); + + continue; + } + + if (!(v->head.hflag & tag)) { + normalize_v3(v->no); + } + } + TGSET_ITER_END + + node->flag &= ~PBVH_UpdateNormals; +} + +void pbvh_bmesh_normals_update(PBVH *pbvh, PBVHNode **nodes, int totnode) +{ + TaskParallelSettings settings; + Vector datas; + datas.resize(totnode); + + for (int i : IndexRange(totnode)) { + datas[i].node = nodes[i]; + datas[i].cd_sculpt_vert = pbvh->cd_sculpt_vert; + datas[i].cd_vert_node_offset = pbvh->cd_vert_node_offset; + datas[i].cd_face_node_offset = pbvh->cd_face_node_offset; + datas[i].node_nr = nodes[i] - pbvh->nodes; + + BKE_pbvh_bmesh_check_tris(pbvh, nodes[i]); + } + + BKE_pbvh_parallel_range_settings(&settings, true, totnode); + BLI_task_parallel_range(0, totnode, datas.data(), pbvh_update_normals_task_cb, &settings); + + /* not sure it's worth calling BM_mesh_elem_index_ensure here */ +#if 0 + BLI_bitmap *visit = BLI_BITMAP_NEW(bm->totvert, "visit"); + BM_mesh_elem_index_ensure(bm, BM_VERT); +#endif + + for (int i = 0; i < totnode; i++) { + UpdateNormalsTaskData *data = &datas[i]; + +#if 0 + printf("%.2f%% : %d %d\n", + 100.0f * (float)data->tot_border_verts / (float)data->node->bm_unique_verts->length, + data->tot_border_verts, + data->node->bm_unique_verts->length); +#endif + + for (int j = 0; j < data->border_verts.size(); j++) { + BMVert *v = data->border_verts[j]; + + if (BM_elem_is_free((BMElem *)v, BM_VERT)) { + printf("%s: error, v was freed!\n", __func__); + continue; + } + +#if 0 + if (v->head.index < 0 || v->head.index >= bm->totvert) { + printf("%s: error, v->head.index was out of bounds!\n", __func__); + continue; + } + + if (BLI_BITMAP_TEST(visit, v->head.index)) { + continue; + } + + BLI_BITMAP_ENABLE(visit, v->head.index); +#endif + + // manual iteration + BMEdge *e = v->e; + + if (!e) { + continue; + } + + zero_v3(v->no); + + do { + if (e->l) { + add_v3_v3(v->no, e->l->f->no); + } + e = BM_DISK_EDGE_NEXT(e, v); + } while (e != v->e); + + normalize_v3(v->no); + } + } + +#if 0 + MEM_SAFE_FREE(visit); +#endif +} + +static void pbvh_bmesh_normals_update_old(PBVHNode **nodes, int totnode) { for (int n = 0; n < totnode; n++) { PBVHNode *node = nodes[n]; if (node->flag & PBVH_UpdateNormals) { - GSetIterator gs_iter; + BMVert *v; + BMFace *f; - GSET_ITER (gs_iter, node->bm_faces) { - BM_face_normal_update(static_cast(BLI_gsetIterator_getKey(&gs_iter))); + TGSET_ITER (f, node->bm_faces) { + BM_face_normal_update(f); } - GSET_ITER (gs_iter, node->bm_unique_verts) { - BM_vert_normal_update(static_cast(BLI_gsetIterator_getKey(&gs_iter))); + TGSET_ITER_END + + TGSET_ITER (v, node->bm_unique_verts) { + BM_vert_normal_update(v); } + TGSET_ITER_END + /* This should be unneeded normally */ - GSET_ITER (gs_iter, node->bm_other_verts) { - BM_vert_normal_update(static_cast(BLI_gsetIterator_getKey(&gs_iter))); + TGSET_ITER (v, node->bm_other_verts) { + BM_vert_normal_update(v); } + TGSET_ITER_END + node->flag &= ~PBVH_UpdateNormals; } } @@ -1685,8 +1307,12 @@ void pbvh_bmesh_normals_update(PBVHNode **nodes, int totnode) struct FastNodeBuildInfo { int totface; /* number of faces */ int start; /* start of faces in array */ - FastNodeBuildInfo *child1; - FastNodeBuildInfo *child2; + int depth; + int node_index; + struct FastNodeBuildInfo *child1; + struct FastNodeBuildInfo *child2; + float cent[3], no[3]; + int tag; }; /** @@ -1694,12 +1320,16 @@ struct FastNodeBuildInfo { * This function is multi-thread-able since each invocation applies * to a sub part of the arrays. */ -static void pbvh_bmesh_node_limit_ensure_fast( - PBVH *pbvh, BMFace **nodeinfo, BBC *bbc_array, FastNodeBuildInfo *node, MemArena *arena) +static void pbvh_bmesh_node_limit_ensure_fast(PBVH *pbvh, + BMFace **nodeinfo, + BBC *bbc_array, + FastNodeBuildInfo *node, + Vector &leaves, + MemArena *arena) { FastNodeBuildInfo *child1, *child2; - if (node->totface <= pbvh->leaf_limit) { + if (node->totface <= pbvh->leaf_limit || node->depth >= PBVH_STACK_FIXED_DEPTH) { return; } @@ -1776,146 +1406,815 @@ static void pbvh_bmesh_node_limit_ensure_fast( * each sequential part belonging to one node only */ BLI_assert((num_child1 + num_child2) == node->totface); - node->child1 = child1 = static_cast( - BLI_memarena_alloc(arena, sizeof(FastNodeBuildInfo))); - node->child2 = child2 = static_cast( - BLI_memarena_alloc(arena, sizeof(FastNodeBuildInfo))); + node->child1 = child1 = (FastNodeBuildInfo *)BLI_memarena_alloc(arena, + sizeof(FastNodeBuildInfo)); + node->child2 = child2 = (FastNodeBuildInfo *)BLI_memarena_alloc(arena, + sizeof(FastNodeBuildInfo)); child1->totface = num_child1; child1->start = node->start; + child1->depth = node->depth + 1; + child2->totface = num_child2; child2->start = node->start + num_child1; + child2->depth = node->depth + 2; + child1->child1 = child1->child2 = child2->child1 = child2->child2 = nullptr; - pbvh_bmesh_node_limit_ensure_fast(pbvh, nodeinfo, bbc_array, child1, arena); - pbvh_bmesh_node_limit_ensure_fast(pbvh, nodeinfo, bbc_array, child2, arena); + pbvh_bmesh_node_limit_ensure_fast(pbvh, nodeinfo, bbc_array, child1, leaves, arena); + pbvh_bmesh_node_limit_ensure_fast(pbvh, nodeinfo, bbc_array, child2, leaves, arena); + + if (!child1->child1 && !child1->child2) { + leaves.append(child1); + } + + if (!child2->child1 && !child2->child2) { + leaves.append(child2); + } } -static void pbvh_bmesh_create_nodes_fast_recursive( - PBVH *pbvh, BMFace **nodeinfo, BBC *bbc_array, FastNodeBuildInfo *node, int node_index) +struct LeafBuilderThreadData { + PBVH *pbvh; + BMFace **nodeinfo; + BBC *bbc_array; + Vector leaves; +}; + +static void pbvh_bmesh_create_leaf_fast_task_cb(void *__restrict userdata, + const int i, + const TaskParallelTLS *__restrict /* tls */) +{ + LeafBuilderThreadData *data = (LeafBuilderThreadData *)userdata; + PBVH *pbvh = data->pbvh; + BMFace **nodeinfo = data->nodeinfo; + BBC *bbc_array = data->bbc_array; + struct FastNodeBuildInfo *node = data->leaves[i]; + + /* node does not have children so it's a leaf node, populate with faces and tag accordingly + * this is an expensive part but it's not so easily thread-able due to vertex node indices */ + // const int cd_vert_node_offset = pbvh->cd_vert_node_offset; + const int cd_face_node_offset = pbvh->cd_face_node_offset; + + PBVHNode *n = pbvh->nodes + node->node_index; + const int node_index = node->node_index; + + bool has_visible = false; + + /* Build GPU buffers for new node */ + + n->flag = PBVH_Leaf | PBVH_UpdateTris | PBVH_UpdateBB | PBVH_UpdateOriginalBB | + PBVH_UpdateTriAreas | PBVH_UpdateColor | PBVH_UpdateVisibility | + PBVH_UpdateDrawBuffers | PBVH_RebuildDrawBuffers | PBVH_UpdateCurvatureDir | + PBVH_UpdateTriAreas | PBVH_UpdateMask | PBVH_UpdateRedraw; + + n->bm_faces = BLI_table_gset_new_ex("bm_faces", node->totface); + + /* Create vert hash sets */ + n->bm_unique_verts = BLI_table_gset_new_ex("bm_unique_verts", node->totface * 3); + n->bm_other_verts = BLI_table_gset_new_ex("bm_other_verts", node->totface * 3); + + BB_reset(&n->vb); + + const int end = node->start + node->totface; + + for (int i = node->start; i < end; i++) { + BMFace *f = nodeinfo[i]; + BBC *bbc = &bbc_array[BM_elem_index_get(f)]; + + /* Update ownership of faces */ + BLI_table_gset_insert(n->bm_faces, f); + BM_ELEM_CD_SET_INT(f, cd_face_node_offset, node_index); + + /* Update vertices */ + BMLoop *l_first = BM_FACE_FIRST_LOOP(f); + BMLoop *l_iter = l_first; + do { + BMVert *v = l_iter->v; + + int old = BM_ELEM_CD_GET_INT(v, pbvh->cd_vert_node_offset); + + char *ptr = (char *)v->head.data; + ptr += pbvh->cd_vert_node_offset; + + if (old == DYNTOPO_NODE_NONE && + atomic_cas_int32((int32_t *)ptr, DYNTOPO_NODE_NONE, node_index) == DYNTOPO_NODE_NONE) { + BLI_table_gset_insert(n->bm_unique_verts, v); + } + else { + BLI_table_gset_add(n->bm_other_verts, v); + } + } while ((l_iter = l_iter->next) != l_first); + + /* Update node bounding box */ + if (!pbvh_poly_hidden(pbvh, f)) { + has_visible = true; + } + + BB_expand_with_bb(&n->vb, (BB *)bbc); + } + + BLI_assert(n->vb.bmin[0] <= n->vb.bmax[0] && n->vb.bmin[1] <= n->vb.bmax[1] && + n->vb.bmin[2] <= n->vb.bmax[2]); + + n->orig_vb = n->vb; + + BKE_pbvh_node_fully_hidden_set(n, !has_visible); + n->flag |= PBVH_UpdateNormals | PBVH_UpdateCurvatureDir; +} + +static void pbvh_bmesh_create_nodes_fast_recursive_create(PBVH *pbvh, + BMFace **nodeinfo, + BBC *bbc_array, + struct FastNodeBuildInfo *node) { - PBVHNode *n = pbvh->nodes + node_index; /* two cases, node does not have children or does have children */ if (node->child1) { int children_offset = pbvh->totnode; - - n->children_offset = children_offset; pbvh_grow_nodes(pbvh, pbvh->totnode + 2); - pbvh_bmesh_create_nodes_fast_recursive( - pbvh, nodeinfo, bbc_array, node->child1, children_offset); - pbvh_bmesh_create_nodes_fast_recursive( - pbvh, nodeinfo, bbc_array, node->child2, children_offset + 1); - n = &pbvh->nodes[node_index]; + PBVHNode *n = pbvh->nodes + node->node_index; + n->children_offset = children_offset; + + n->depth = node->depth; + (n + 1)->depth = node->child1->depth; + (n + 2)->depth = node->child2->depth; + + node->child1->node_index = children_offset; + node->child2->node_index = children_offset + 1; + + pbvh_bmesh_create_nodes_fast_recursive_create(pbvh, nodeinfo, bbc_array, node->child1); + pbvh_bmesh_create_nodes_fast_recursive_create(pbvh, nodeinfo, bbc_array, node->child2); + } +} + +static void pbvh_bmesh_create_nodes_fast_recursive_final(PBVH *pbvh, + BMFace **nodeinfo, + BBC *bbc_array, + struct FastNodeBuildInfo *node) +{ + /* two cases, node does not have children or does have children */ + if (node->child1) { + pbvh_bmesh_create_nodes_fast_recursive_final(pbvh, nodeinfo, bbc_array, node->child1); + pbvh_bmesh_create_nodes_fast_recursive_final(pbvh, nodeinfo, bbc_array, node->child2); + + PBVHNode *n = pbvh->nodes + node->node_index; /* Update bounding box */ BB_reset(&n->vb); - BB_expand_with_bb(&n->vb, &pbvh->nodes[n->children_offset].vb); - BB_expand_with_bb(&n->vb, &pbvh->nodes[n->children_offset + 1].vb); + BB_expand_with_bb(&n->vb, &pbvh->nodes[node->child1->node_index].vb); + BB_expand_with_bb(&n->vb, &pbvh->nodes[node->child2->node_index].vb); n->orig_vb = n->vb; } - else { - /* node does not have children so it's a leaf node, populate with faces and tag accordingly - * this is an expensive part but it's not so easily thread-able due to vertex node indices */ - const int cd_vert_node_offset = pbvh->cd_vert_node_offset; - const int cd_face_node_offset = pbvh->cd_face_node_offset; - - bool has_visible = false; - - n->flag = PBVH_Leaf; - n->bm_faces = BLI_gset_ptr_new_ex("bm_faces", node->totface); - - /* Create vert hash sets */ - n->bm_unique_verts = BLI_gset_ptr_new("bm_unique_verts"); - n->bm_other_verts = BLI_gset_ptr_new("bm_other_verts"); - - BB_reset(&n->vb); - - const int end = node->start + node->totface; - - for (int i = node->start; i < end; i++) { - BMFace *f = nodeinfo[i]; - BBC *bbc = &bbc_array[BM_elem_index_get(f)]; - - /* Update ownership of faces */ - BLI_gset_insert(n->bm_faces, f); - BM_ELEM_CD_SET_INT(f, cd_face_node_offset, node_index); - - /* Update vertices */ - BMLoop *l_first = BM_FACE_FIRST_LOOP(f); - BMLoop *l_iter = l_first; - do { - BMVert *v = l_iter->v; - if (!BLI_gset_haskey(n->bm_unique_verts, v)) { - if (BM_ELEM_CD_GET_INT(v, cd_vert_node_offset) != DYNTOPO_NODE_NONE) { - BLI_gset_add(n->bm_other_verts, v); - } - else { - BLI_gset_insert(n->bm_unique_verts, v); - BM_ELEM_CD_SET_INT(v, cd_vert_node_offset, node_index); - } - } - /* Update node bounding box */ - } while ((l_iter = l_iter->next) != l_first); - - if (!BM_elem_flag_test(f, BM_ELEM_HIDDEN)) { - has_visible = true; - } - - BB_expand_with_bb(&n->vb, (BB *)bbc); - } - - BLI_assert(n->vb.bmin[0] <= n->vb.bmax[0] && n->vb.bmin[1] <= n->vb.bmax[1] && - n->vb.bmin[2] <= n->vb.bmax[2]); - - n->orig_vb = n->vb; - - /* Build GPU buffers for new node and update vertex normals */ - BKE_pbvh_node_mark_rebuild_draw(n); - - BKE_pbvh_node_fully_hidden_set(n, !has_visible); - n->flag |= PBVH_UpdateNormals; - } } /***************************** Public API *****************************/ -void BKE_pbvh_update_bmesh_offsets(PBVH *pbvh, int cd_vert_node_offset, int cd_face_node_offset) +static float sculpt_corner_angle(float *base, float *co1, float *co2) { - pbvh->cd_vert_node_offset = cd_vert_node_offset; - pbvh->cd_face_node_offset = cd_face_node_offset; + float t1[3], t2[3]; + sub_v3_v3v3(t1, co1, base); + sub_v3_v3v3(t2, co2, base); + + normalize_v3(t1); + normalize_v3(t2); + + float th = dot_v3v3(t1, t2); + + return saacos(th); } +struct FSetTemp { + BMVert *v; + int fset; + bool boundary; +}; + +int BKE_pbvh_do_fset_symmetry(int fset, const int symflag, const float *co) +{ + fset = abs(fset); + + // don't affect base face set + if (fset == 1) { + return 1; + } + + /* + flag symmetry by shifting by 24 bits; + surely we don't need more then 8 million face sets? + */ + if (co[0] < 0.0f) { + fset |= (symflag & 1) << 24; + } + + if (co[1] < 0.0f) { + fset |= (symflag & 2) << 24; + } + + if (co[2] < 0.0f) { + fset |= (symflag & 4) << 24; + } + + return fset; +} + +//#define MV_COLOR_BOUNDARY + +#ifdef MV_COLOR_BOUNDARY +static int color_boundary_key(float col[4]) +{ + const float steps = 2.0f; + float hsv[3]; + + rgb_to_hsv(col[0], col[1], col[2], hsv, hsv + 1, hsv + 2); + + int x = (int)((hsv[0] * 0.5f + 0.5f) * steps + 0.5f); + int y = (int)(hsv[1] * steps + 0.5f); + int z = (int)(hsv[2] * steps + 0.5f); + + return z * steps * steps + y * steps + x; +} +#endif + +/* calls atomic_cas_uint32 on two adjacent (and int aligned) shorts */ +BLI_INLINE void atomic_cas_short2(ushort *base, ushort olda, ushort oldb, ushort newa, ushort newb) +{ + uint oldi, newi; + + ((ushort *)&oldi)[0] = olda; + ((ushort *)&oldi)[1] = oldb; + + ((ushort *)&newi)[0] = newa; + ((ushort *)&newi)[1] = newb; + + atomic_cas_uint32((uint32_t *)base, oldi, newi); +} + +void bke_pbvh_update_vert_boundary(int cd_sculpt_vert, + int cd_faceset_offset, + int cd_vert_node_offset, + int cd_face_node_offset, + int cd_vcol, + int cd_boundary_flag, + BMVert *v, + int bound_symmetry, + const CustomData *ldata, + const int totuv) +{ + MSculptVert *mv = BKE_PBVH_SCULPTVERT(cd_sculpt_vert, v); + + float curv = 0.0f, totcurv = 0.0f; + + int newflag = mv->flag; + int oldflag = newflag; + int oldval = mv->valence; + int boundflag = 0; + + BMEdge *e = v->e; + newflag &= ~(SCULPTVERT_VERT_FSET_HIDDEN | SCULPTVERT_PBVH_BOUNDARY); + + ushort stroke_id = (ushort)mv->stroke_id; + + if (!e) { + boundflag |= SCULPT_BOUNDARY_MESH; + + int oldboundflag = BM_ELEM_CD_GET_INT(v, cd_boundary_flag); + + atomic_cas_int32(&mv->flag, oldflag, newflag); + atomic_cas_int32(BM_ELEM_CD_PTR(v, cd_boundary_flag), oldboundflag, boundflag); + + atomic_cas_short2(&mv->valence, (ushort)oldval, stroke_id, 0, stroke_id); + + return; + } + + int val = 0; + + int ni = BM_ELEM_CD_GET_INT(v, cd_vert_node_offset); + + int sharpcount = 0; + int seamcount = 0; + int quadcount = 0; + +#ifdef MV_COLOR_BOUNDARY + int last_key = -1; +#endif + +#if 0 + struct FaceSetRef { + int fset; + BMVert *v2; + BMEdge *e; + } *fsets = nullptr; +#endif + Vector fsets; + + float(*lastuv)[2] = (float(*)[2])BLI_array_alloca(lastuv, totuv); + float(*lastuv2)[2] = (float(*)[2])BLI_array_alloca(lastuv2, totuv); + + int *disjount_uv_count = (int *)BLI_array_alloca(disjount_uv_count, totuv); + int *cd_uvs = (int *)BLI_array_alloca(cd_uvs, totuv); + int base_uv_idx = ldata->typemap[CD_PROP_FLOAT2]; + bool uv_first = true; + + for (int i = 0; i < totuv; i++) { + CustomDataLayer *layer = ldata->layers + base_uv_idx + i; + cd_uvs[i] = layer->offset; + disjount_uv_count[i] = 0; + } + + do { + BMVert *v2 = v == e->v1 ? e->v2 : e->v1; + +#if 0 + float tmp[3]; + sub_v3_v3v3(tmp, v2->co, v->co); + madd_v3_v3fl(avg, v->no, -dot_v3v3(v->no, tmp)); + // madd_v3_v3fl(tmp, v->no, -dot_v3v3(v->no, tmp)); + add_v3_v3(avg, tmp); + + avg_len += len_squared_v3(tmp); + totcurv += 1.0f; +#endif + + if (BM_ELEM_CD_GET_INT(v2, cd_vert_node_offset) != ni) { + newflag |= SCULPTVERT_PBVH_BOUNDARY; + } + + if (e->head.hflag & BM_ELEM_SEAM) { + boundflag |= SCULPT_BOUNDARY_SEAM; + seamcount++; + + if (seamcount > 2) { + boundflag |= SCULPT_CORNER_SEAM; + } + } + +#ifdef MV_COLOR_BOUNDARY +# error "fix me! need to handle loops too" +#endif + +#ifdef MV_COLOR_BOUNDARY // CURRENTLY BROKEN + if (cd_vcol >= 0) { + float *color1 = BM_ELEM_CD_PTR(v, cd_vcol); + float *color2 = BM_ELEM_CD_PTR(v2, cd_vcol); + + int colorkey1 = color_boundary_key(color1); + int colorkey2 = color_boundary_key(color2); + + if (colorkey1 != colorkey2) { + boundflag |= SCUKPT_BOUNDARY_FACE_SETS; + } + } +#endif + + if (!(e->head.hflag & BM_ELEM_SMOOTH)) { + boundflag |= SCULPT_BOUNDARY_SHARP; + sharpcount++; + + if (sharpcount > 2) { + boundflag |= SCULPT_CORNER_SHARP; + } + } + + if (e->l) { + /* detect uv island boundaries */ + if (totuv) { + BMLoop *l_iter = e->l; + do { + BMLoop *l = l_iter->v != v ? l_iter->next : l_iter; + + for (int i = 0; i < totuv; i++) { + float *luv = BM_ELEM_CD_PTR(l, cd_uvs[i]); + + if (uv_first) { + copy_v2_v2(lastuv[i], luv); + copy_v2_v2(lastuv2[i], luv); + + continue; + } + + const float uv_snap_limit = 0.01f * 0.01f; + + float dist = len_squared_v2v2(luv, lastuv[i]); + bool same = dist <= uv_snap_limit; + + bool corner = len_squared_v2v2(lastuv[i], lastuv2[i]) > uv_snap_limit && + len_squared_v2v2(lastuv[i], luv) > uv_snap_limit && + len_squared_v2v2(lastuv2[i], luv) > uv_snap_limit; + + if (!same) { + boundflag |= SCULPT_BOUNDARY_UV; + } + + if (corner) { + boundflag |= SCULPT_CORNER_UV; + } + + if (!same) { + copy_v2_v2(lastuv2[i], lastuv[i]); + copy_v2_v2(lastuv[i], luv); + } + } + + uv_first = false; + } while ((l_iter = l_iter->radial_next) != e->l); + } + + if (BM_ELEM_CD_GET_INT(e->l->f, cd_face_node_offset) != ni) { + newflag |= SCULPTVERT_PBVH_BOUNDARY; + } + + if (e->l != e->l->radial_next) { + if (e->l->f->len > 3) { + quadcount++; + } + + if (e->l->radial_next->f->len > 3) { + quadcount++; + } + + if (BM_ELEM_CD_GET_INT(e->l->radial_next->f, cd_face_node_offset) != ni) { + newflag |= SCULPTVERT_PBVH_BOUNDARY; + } + } + + if (e->l->f->len > 3) { + newflag |= SCULPTVERT_NEED_TRIANGULATE; + } + + if (cd_faceset_offset != -1) { + int fset = BKE_pbvh_do_fset_symmetry( + BM_ELEM_CD_GET_INT(e->l->f, cd_faceset_offset), bound_symmetry, v2->co); + + bool ok = true; + for (int i = 0; i < fsets.size(); i++) { + if (fsets[i] == fset) { + ok = false; + } + } + + if (ok) { + fsets.append(fset); + } + } + + // also check e->l->radial_next, in case we are not manifold + // which can mess up the loop order + if (e->l->radial_next != e->l) { + float th = saacos(dot_v3v3(e->l->f->no, e->l->radial_next->f->no)); + + th *= M_1_PI * 0.25f; + // th = th * 0.5 + 0.5; + curv += th; + totcurv += 1.0f; + + if (cd_faceset_offset != -1) { + // fset = abs(BM_ELEM_CD_GET_INT(e->l->radial_next->f, cd_faceset_offset)); + int fset2 = BKE_pbvh_do_fset_symmetry( + BM_ELEM_CD_GET_INT(e->l->radial_next->f, cd_faceset_offset), bound_symmetry, v2->co); + + bool ok2 = true; + for (int i = 0; i < fsets.size(); i++) { + if (fsets[i] == fset2) { + ok2 = false; + } + } + + if (ok2) { + fsets.append(fset2); + } + } + + if (e->l->radial_next->f->len > 3) { + newflag |= SCULPTVERT_NEED_TRIANGULATE; + } + } + } + + if (!e->l || e->l->radial_next == e->l) { + boundflag |= SCULPT_BOUNDARY_MESH; + } + + val++; + } while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e); + + if (fsets.size() > 1) { + boundflag |= SCULPT_BOUNDARY_FACE_SET; + } + + if (fsets.size() > 2) { + boundflag |= SCULPT_CORNER_FACE_SET; + } + + if (sharpcount == 1) { + boundflag |= SCULPT_CORNER_SHARP; + } + + if (seamcount == 1) { + boundflag |= SCULPT_CORNER_SEAM; + } + + if ((boundflag & SCULPT_BOUNDARY_MESH) && quadcount >= 3) { + boundflag |= SCULPT_CORNER_MESH; + } + +#if 0 + if (totcurv > 0.0f) { + mul_v3_fl(avg, 1.0f / totcurv); + avg_len /= totcurv; + } + + if (avg_len > 0.0f) { + curv = len_squared_v3(avg) / avg_len; + } + else { + curv = 0.0f; + } +#else + if (totcurv > 0.0f) { + curv /= totcurv; + } +#endif + + int oldboundflag = BM_ELEM_CD_GET_INT(v, cd_boundary_flag); + + atomic_cas_int32(&mv->flag, oldflag, newflag); + atomic_cas_int32(BM_ELEM_CD_PTR(v, cd_boundary_flag), oldboundflag, boundflag); + + atomic_cas_short2(&mv->valence, (ushort)oldval, stroke_id, (ushort)val, stroke_id); + + /* no atomic_cas_int16, so do origmask and curv at once */ + + ushort newcurv = (unsigned short)(min_ff(fabsf(curv), 1.0f) * 65535.0f); + ushort oldcurv = mv->curv; + ushort origmask = mv->origmask; + + atomic_cas_short2(&mv->origmask, origmask, oldcurv, origmask, newcurv); +} + +bool BKE_pbvh_check_vert_boundary(PBVH *pbvh, BMVert *v) +{ + return pbvh_check_vert_boundary(pbvh, v); +} + +void BKE_pbvh_update_vert_boundary(int cd_sculpt_vert, + int cd_faceset_offset, + int cd_vert_node_offset, + int cd_face_node_offset, + int cd_vcol, + int cd_boundary_flag, + BMVert *v, + int bound_symmetry, + const CustomData *ldata, + const int totuv, + const bool do_uvs) +{ + bke_pbvh_update_vert_boundary(cd_sculpt_vert, + cd_faceset_offset, + cd_vert_node_offset, + cd_face_node_offset, + cd_vcol, + cd_boundary_flag, + v, + bound_symmetry, + ldata, + do_uvs ? totuv : 0); +} + +/*Used by symmetrize to update boundary flags*/ +void BKE_pbvh_recalc_bmesh_boundary(PBVH *pbvh) +{ + BMVert *v; + BMIter iter; + + BM_ITER_MESH (v, &iter, pbvh->header.bm, BM_VERTS_OF_MESH) { + bke_pbvh_update_vert_boundary(pbvh->cd_sculpt_vert, + pbvh->cd_faceset_offset, + pbvh->cd_vert_node_offset, + pbvh->cd_face_node_offset, + pbvh->cd_vcol_offset, + pbvh->cd_boundary_flag, + v, + pbvh->boundary_symmetry, + &pbvh->header.bm->ldata, + pbvh->flags & PBVH_IGNORE_UVS ? 0 : pbvh->totuv); + } +} + +struct FaceBinThread { + MemArena *arena; + FastNodeBuildInfo *nodes, **leaves; + int totnode, totleaf; +}; + +static void coalese_pbvh(PBVH *pbvh, BMesh *bm) +{ + BMIter iter; + BMFace *f; + + const char tag = BM_ELEM_TAG_ALT; + + BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { + f->head.hflag &= ~tag; + } + + int leafsize = 1000; + double f1 = pow(2.0, ceil(log2(bm->totface / (double)leafsize))); + leafsize = (int)ceil(bm->totface / f1); + + printf("leafsize: %d\n", leafsize); + + int *fmap = (int *)MEM_calloc_arrayN(bm->totface, sizeof(int), "pbvh face map"); + int totleaf = 0; + + Vector nodes; + + const int qsize = leafsize; + BMFace **queue = MEM_cnew_array(qsize, "pbvh queue"); + + BM_mesh_elem_table_ensure(bm, BM_FACE | BM_VERT); + + BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { + int qhead = 0; + int qtail = 0; + + if (f->head.hflag & tag) { + continue; + } + + int ni = totleaf++; + nodes.resize(nodes.size() + 1); + + FastNodeBuildInfo *node = &nodes.last(0); + copy_v3_v3(node->cent, f->l_first->v->co); + copy_v3_v3(node->no, f->no); + + queue[0] = f; + qhead = 1; + + while (qhead != qtail) { + BMFace *f2 = queue[qtail]; + qtail = (qtail + 1) % qsize; + + fmap[f2->head.index] = ni; + node->totface++; + + BMLoop *l = f2->l_first; + do { + BMLoop *l2 = l; + do { + if (!(l2->f->head.hflag & tag)) { + l2->f->head.hflag |= tag; + + queue[qhead] = l2->f; + qhead = (qhead + 1) % qsize; + } + } while ((l2 = l2->radial_next) != l); + } while ((l = l->next) != f2->l_first); + } + } + + printf("totleafs: %d\n", totleaf); + nodes.resize(nodes.size() << 1); + + const int tag2 = 1; + int pi = totleaf; + int starti = 0; + int endi = totleaf; + + for (int step = 0; step < 55; step++) { + if (endi - starti <= 1) { + break; + } + + for (int i = starti; i < endi; i++) { + FastNodeBuildInfo *n1 = &nodes[i], *n2 = nullptr; + + if (n1->tag & tag2) { + continue; + } + + float mindis = 1e17; + + for (int j = starti; j < endi; j++) { + FastNodeBuildInfo *n3 = &nodes[j]; + + if (j == i || n3->tag) { + continue; + } + + float dis = len_squared_v3v3(n1->cent, n3->cent); + if (dis < mindis) { + mindis = dis; + n2 = n3; + } + } + + if (!n2) { + break; + } + + FastNodeBuildInfo *parent = &nodes[pi]; + pi++; + + n1->tag |= tag2; + n2->tag |= tag2; + parent->tag = 0; + parent->child1 = n1; + parent->child2 = n2; + } + + starti = endi; + endi = pi; + } + + MEM_SAFE_FREE(queue); + MEM_SAFE_FREE(fmap); +} + +void BKE_pbvh_update_sculpt_verts(PBVH *pbvh) +{ + int totvert = pbvh->totvert; + + for (int i = 0; i < totvert; i++) { + PBVHVertRef vertex = BKE_pbvh_index_to_vertex(pbvh, i); + + BKE_pbvh_get_origvert(pbvh, vertex, nullptr, nullptr, nullptr); + } +} + +void BKE_pbvh_set_idmap(PBVH *pbvh, BMIdMap *idmap) +{ + pbvh->bm_idmap = idmap; +} + +/* Build a PBVH from a BMesh */ void BKE_pbvh_build_bmesh(PBVH *pbvh, + Mesh *me, BMesh *bm, bool smooth_shading, BMLog *log, + BMIdMap *idmap, const int cd_vert_node_offset, - const int cd_face_node_offset) + const int cd_face_node_offset, + const int cd_sculpt_vert, + const int cd_face_areas, + const int cd_boundary_flag, + bool fast_draw, + bool update_sculptverts) { + // coalese_pbvh(pbvh, bm); + + pbvh->bm_idmap = idmap; + + pbvh->cd_hide_poly = CustomData_get_offset_named( + &bm->pdata, CD_PROP_INT32, ".sculpt_face_areas"); + pbvh->cd_face_area = cd_face_areas; + pbvh->cd_vert_node_offset = cd_vert_node_offset; + pbvh->cd_face_node_offset = cd_face_node_offset; + pbvh->cd_vert_mask_offset = CustomData_get_offset(&bm->vdata, CD_PAINT_MASK); + pbvh->cd_sculpt_vert = cd_sculpt_vert; + pbvh->cd_boundary_flag = cd_boundary_flag; + + pbvh->mesh = me; + + smooth_shading |= fast_draw; + pbvh->header.bm = bm; - BKE_pbvh_bmesh_detail_size_set(pbvh, 0.75); + BKE_pbvh_bmesh_detail_size_set(pbvh, 0.75f, 0.4f); pbvh->header.type = PBVH_BMESH; pbvh->bm_log = log; + pbvh->cd_faceset_offset = CustomData_get_offset_named( + &pbvh->header.bm->pdata, CD_PROP_INT32, ".sculpt_face_set"); + + int tottri = poly_to_tri_count(bm->totface, bm->totloop); /* TODO: choose leaf limit better */ - pbvh->leaf_limit = 400; + if (tottri > 4 * 1000 * 1000) { + pbvh->leaf_limit = 10000; + } + else { + pbvh->leaf_limit = 1000; + } - BKE_pbvh_update_bmesh_offsets(pbvh, cd_vert_node_offset, cd_face_node_offset); + BMIter iter; + BMVert *v; if (smooth_shading) { pbvh->flags |= PBVH_DYNTOPO_SMOOTH_SHADING; } + if (fast_draw) { + pbvh->flags |= PBVH_FAST_DRAW; + } + /* bounding box array of all faces, no need to recalculate every time */ - BBC *bbc_array = static_cast(MEM_mallocN(sizeof(BBC) * bm->totface, "BBC")); - BMFace **nodeinfo = static_cast( - MEM_mallocN(sizeof(*nodeinfo) * bm->totface, "nodeinfo")); + BBC *bbc_array = MEM_cnew_array(bm->totface, "BBC"); + BMFace **nodeinfo = MEM_cnew_array(bm->totface, "nodeinfo"); MemArena *arena = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, "fast PBVH node storage"); - BMIter iter; BMFace *f; int i; BM_ITER_MESH_INDEX (f, &iter, bm, BM_FACES_OF_MESH, i) { @@ -1923,6 +2222,11 @@ void BKE_pbvh_build_bmesh(PBVH *pbvh, BMLoop *l_first = BM_FACE_FIRST_LOOP(f); BMLoop *l_iter = l_first; + // check for currupted faceset + if (pbvh->cd_faceset_offset != -1 && BM_ELEM_CD_GET_INT(f, pbvh->cd_faceset_offset) == 0) { + BM_ELEM_CD_SET_INT(f, pbvh->cd_faceset_offset, 1); + } + BB_reset((BB *)bbc); do { BB_expand((BB *)bbc, l_iter->v->co); @@ -1937,222 +2241,1633 @@ void BKE_pbvh_build_bmesh(PBVH *pbvh, /* Likely this is already dirty. */ bm->elem_index_dirty |= BM_FACE; - BMVert *v; BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) { + MSculptVert *mv = BKE_PBVH_SCULPTVERT(pbvh->cd_sculpt_vert, v); + + mv->flag |= SCULPTVERT_NEED_VALENCE | SCULPTVERT_NEED_TRIANGULATE | SCULPTVERT_NEED_DISK_SORT; + + int *flags = BM_ELEM_CD_PTR(v, pbvh->cd_boundary_flag); + *flags |= SCULPT_BOUNDARY_NEEDS_UPDATE; + BM_ELEM_CD_SET_INT(v, cd_vert_node_offset, DYNTOPO_NODE_NONE); } /* setup root node */ - FastNodeBuildInfo rootnode = {0}; + struct FastNodeBuildInfo rootnode = {0}; + + Vector leaves; + rootnode.totface = bm->totface; + int totleaf = 0; /* start recursion, assign faces to nodes accordingly */ - pbvh_bmesh_node_limit_ensure_fast(pbvh, nodeinfo, bbc_array, &rootnode, arena); + pbvh_bmesh_node_limit_ensure_fast(pbvh, nodeinfo, bbc_array, &rootnode, leaves, arena); - /* We now have all faces assigned to a node, - * next we need to assign those to the gsets of the nodes. */ + pbvh_grow_nodes(pbvh, 1); + rootnode.node_index = 0; - /* Start with all faces in the root node */ - pbvh->nodes = MEM_cnew(__func__); - pbvh->totnode = 1; + pbvh_bmesh_create_nodes_fast_recursive_create(pbvh, nodeinfo, bbc_array, &rootnode); + totleaf = leaves.size(); + + if (!totleaf) { + leaves.append(&rootnode); + totleaf = 1; + } + + /* build leaf nodes */ + LeafBuilderThreadData tdata; + tdata.pbvh = pbvh; + tdata.nodeinfo = nodeinfo; + tdata.bbc_array = bbc_array; + tdata.leaves = leaves; + + TaskParallelSettings settings; + BKE_pbvh_parallel_range_settings(&settings, true, totleaf); + BLI_task_parallel_range(0, totleaf, &tdata, pbvh_bmesh_create_leaf_fast_task_cb, &settings); + + // pbvh_bmesh_create_leaf_fast(pbvh, nodeinfo, bbc_array, leaves[i]); /* take root node and visit and populate children recursively */ - pbvh_bmesh_create_nodes_fast_recursive(pbvh, nodeinfo, bbc_array, &rootnode, 0); + pbvh_bmesh_create_nodes_fast_recursive_final(pbvh, nodeinfo, bbc_array, &rootnode); BLI_memarena_free(arena); MEM_freeN(bbc_array); MEM_freeN(nodeinfo); -} -bool BKE_pbvh_bmesh_update_topology(PBVH *pbvh, - PBVHTopologyUpdateMode mode, - const float center[3], - const float view_normal[3], - float radius, - const bool use_frontface, - const bool use_projected) -{ - /* 2 is enough for edge faces - manifold edge */ - BLI_buffer_declare_static(BMLoop *, edge_loops, BLI_BUFFER_NOP, 2); - BLI_buffer_declare_static(BMFace *, deleted_faces, BLI_BUFFER_NOP, 32); - const int cd_vert_mask_offset = CustomData_get_offset(&pbvh->header.bm->vdata, CD_PAINT_MASK); - const int cd_vert_node_offset = pbvh->cd_vert_node_offset; - const int cd_face_node_offset = pbvh->cd_face_node_offset; + if (me) { // ensure pbvh->vcol_type, vcol_domain and cd_vcol_offset are up to date + CustomDataLayer *cl; + eAttrDomain domain; - bool modified = false; - - if (view_normal) { - BLI_assert(len_squared_v3(view_normal) != 0.0f); + BKE_pbvh_get_color_layer(me, &cl, &domain); } - if (mode & PBVH_Collapse) { - EdgeQueue q; - BLI_mempool *queue_pool = BLI_mempool_create(sizeof(BMVert *) * 2, 0, 128, BLI_MEMPOOL_NOP); - EdgeQueueContext eq_ctx = { - &q, - queue_pool, - pbvh->header.bm, - cd_vert_mask_offset, - cd_vert_node_offset, - cd_face_node_offset, - }; + /*final check that nodes are sufficiently subdivided*/ + int totnode = pbvh->totnode; - short_edge_queue_create( - &eq_ctx, pbvh, center, view_normal, radius, use_frontface, use_projected); - modified |= pbvh_bmesh_collapse_short_edges(&eq_ctx, pbvh, &deleted_faces); - BLI_heapsimple_free(q.heap, nullptr); - BLI_mempool_destroy(queue_pool); - } + for (int i = 0; i < totnode; i++) { + PBVHNode *n = pbvh->nodes + i; - if (mode & PBVH_Subdivide) { - EdgeQueue q; - BLI_mempool *queue_pool = BLI_mempool_create(sizeof(BMVert *) * 2, 0, 128, BLI_MEMPOOL_NOP); - EdgeQueueContext eq_ctx = { - &q, - queue_pool, - pbvh->header.bm, - cd_vert_mask_offset, - cd_vert_node_offset, - cd_face_node_offset, - }; - - long_edge_queue_create( - &eq_ctx, pbvh, center, view_normal, radius, use_frontface, use_projected); - modified |= pbvh_bmesh_subdivide_long_edges(&eq_ctx, pbvh, &edge_loops); - BLI_heapsimple_free(q.heap, nullptr); - BLI_mempool_destroy(queue_pool); - } - - /* Unmark nodes. */ - for (int n = 0; n < pbvh->totnode; n++) { - PBVHNode *node = &pbvh->nodes[n]; - - if (node->flag & PBVH_Leaf && node->flag & PBVH_UpdateTopology) { - node->flag &= ~PBVH_UpdateTopology; - } - } - BLI_buffer_free(&edge_loops); - BLI_buffer_free(&deleted_faces); - - /* Go over all changed nodes and check if anything needs to be updated. */ - for (int n = 0; n < pbvh->totnode; n++) { - PBVHNode *node = &pbvh->nodes[n]; - - if (node->flag & PBVH_Leaf && node->flag & PBVH_TopologyUpdated) { - node->flag &= ~PBVH_TopologyUpdated; - - if (node->bm_ortri) { - /* Reallocate original triangle data. */ - pbvh_bmesh_node_drop_orig(node); - BKE_pbvh_bmesh_node_save_orig(pbvh->header.bm, pbvh->bm_log, node, true); - } - } - } - -#ifdef USE_VERIFY - pbvh_bmesh_verify(pbvh); + if (totnode != pbvh->totnode) { +#ifdef PROXY_ADVANCED + BKE_pbvh_free_proxyarray(pbvh, n); #endif - - return modified; -} - -void BKE_pbvh_bmesh_node_save_orig(BMesh *bm, BMLog *log, PBVHNode *node, bool use_original) -{ - /* Skip if original coords/triangles are already saved */ - if (node->bm_orco) { - return; - } - - const int totvert = BLI_gset_len(node->bm_unique_verts) + BLI_gset_len(node->bm_other_verts); - - const int tottri = BLI_gset_len(node->bm_faces); - - node->bm_orco = static_cast( - MEM_mallocN(sizeof(*node->bm_orco) * totvert, __func__)); - node->bm_ortri = static_cast(MEM_mallocN(sizeof(*node->bm_ortri) * tottri, __func__)); - node->bm_orvert = static_cast( - MEM_mallocN(sizeof(*node->bm_orvert) * totvert, __func__)); - - /* Copy out the vertices and assign a temporary index */ - int i = 0; - GSetIterator gs_iter; - GSET_ITER (gs_iter, node->bm_unique_verts) { - BMVert *v = static_cast(BLI_gsetIterator_getKey(&gs_iter)); - const float *origco = BM_log_original_vert_co(log, v); - - if (use_original && origco) { - copy_v3_v3(node->bm_orco[i], origco); - } - else { - copy_v3_v3(node->bm_orco[i], v->co); } - node->bm_orvert[i] = v; - BM_elem_index_set(v, i); /* set_dirty! */ - i++; - } - GSET_ITER (gs_iter, node->bm_other_verts) { - BMVert *v = static_cast(BLI_gsetIterator_getKey(&gs_iter)); - const float *origco = BM_log_original_vert_co(log, v); - - if (use_original && origco) { - copy_v3_v3(node->bm_orco[i], BM_log_original_vert_co(log, v)); - } - else { - copy_v3_v3(node->bm_orco[i], v->co); - } - - node->bm_orvert[i] = v; - BM_elem_index_set(v, i); /* set_dirty! */ - i++; - } - /* Likely this is already dirty. */ - bm->elem_index_dirty |= BM_VERT; - - /* Copy the triangles */ - i = 0; - GSET_ITER (gs_iter, node->bm_faces) { - BMFace *f = static_cast(BLI_gsetIterator_getKey(&gs_iter)); - - if (BM_elem_flag_test(f, BM_ELEM_HIDDEN)) { - continue; - } - -#if 0 - BMIter bm_iter; - BMVert *v; - int j = 0; - BM_ITER_ELEM (v, &bm_iter, f, BM_VERTS_OF_FACE) { - node->bm_ortri[i][j] = BM_elem_index_get(v); - j++; - } -#else - bm_face_as_array_index_tri(f, node->bm_ortri[i]); -#endif - i++; - } - node->bm_tot_ortri = i; -} - -void BKE_pbvh_bmesh_after_stroke(PBVH *pbvh) -{ - for (int i = 0; i < pbvh->totnode; i++) { - PBVHNode *n = &pbvh->nodes[i]; if (n->flag & PBVH_Leaf) { - /* Free orco/ortri data */ - pbvh_bmesh_node_drop_orig(n); - /* Recursively split nodes that have gotten too many * elements */ pbvh_bmesh_node_limit_ensure(pbvh, i); } } + + if (update_sculptverts) { + BKE_pbvh_update_sculpt_verts(pbvh); + } + + pbvh_print_mem_size(pbvh); + + /* update face areas */ + const int cd_face_area = pbvh->cd_face_area; + for (int i = 0; i < pbvh->totnode; i++) { + PBVHNode *node = pbvh->nodes + i; + + if (!(node->flag & PBVH_Leaf)) { + continue; + } + + BKE_pbvh_bmesh_check_tris(pbvh, node); + + node->flag |= PBVH_UpdateTriAreas; + BKE_pbvh_check_tri_areas(pbvh, node); + + int area_src_i = pbvh->face_area_i ^ 1; + int area_dst_i = pbvh->face_area_i; + + /* make sure read side of double buffer is set too */ + TGSET_ITER (f, node->bm_faces) { + float *areabuf = BM_ELEM_CD_PTR(f, cd_face_area); + areabuf[area_dst_i] = areabuf[area_src_i]; + } + TGSET_ITER_END; + } } -void BKE_pbvh_bmesh_detail_size_set(PBVH *pbvh, float detail_size) +void BKE_pbvh_set_bm_log(PBVH *pbvh, BMLog *log) +{ + pbvh->bm_log = log; + BM_log_set_idmap(log, pbvh->bm_idmap); +} + +bool BKE_pbvh_bmesh_update_topology_nodes(PBVH *pbvh, + bool (*searchcb)(PBVHNode *node, void *data), + void (*undopush)(PBVHNode *node, void *data), + void *searchdata, + PBVHTopologyUpdateMode mode, + const float center[3], + const float view_normal[3], + float radius, + const bool use_frontface, + const bool use_projected, + int sym_axis, + bool updatePBVH, + DyntopoMaskCB mask_cb, + void *mask_cb_data, + bool disable_surface_relax, + bool is_snake_hook) +{ + bool modified = false; + Vector nodes; + + for (int i = 0; i < pbvh->totnode; i++) { + PBVHNode *node = pbvh->nodes + i; + + if (!(node->flag & PBVH_Leaf) || !searchcb(node, searchdata)) { + continue; + } + + if (node->flag & PBVH_Leaf) { + undopush(node, searchdata); + + nodes.append(node); + } + } + + for (PBVHNode *node : nodes) { + node->flag |= PBVH_UpdateCurvatureDir; + BKE_pbvh_node_mark_topology_update(node); + } + + modified = BKE_pbvh_bmesh_update_topology(pbvh, + mode, + center, + view_normal, + radius, + use_frontface, + use_projected, + sym_axis, + updatePBVH, + mask_cb, + mask_cb_data, + 0, // is_snake_hook ? 40960 : 0, + disable_surface_relax, + is_snake_hook); + + return modified; +} + +static void pbvh_free_tribuf(PBVHTriBuf *tribuf) +{ + MEM_SAFE_FREE(tribuf->verts); + MEM_SAFE_FREE(tribuf->tris); + MEM_SAFE_FREE(tribuf->loops); + MEM_SAFE_FREE(tribuf->edges); + + BLI_smallhash_release(&tribuf->vertmap); + + tribuf->verts = nullptr; + tribuf->tris = nullptr; + tribuf->loops = nullptr; + tribuf->edges = nullptr; + + tribuf->totloop = tribuf->tottri = tribuf->totedge = tribuf->totvert = 0; + + tribuf->verts_size = 0; + tribuf->tris_size = 0; + tribuf->edges_size = 0; +} + +PBVHTriBuf *BKE_pbvh_bmesh_get_tris(PBVH *pbvh, PBVHNode *node) +{ + BKE_pbvh_bmesh_check_tris(pbvh, node); + + return node->tribuf; +} + +void BKE_pbvh_bmesh_free_tris(PBVH *pbvh, PBVHNode *node) +{ + if (node->tribuf) { + pbvh_free_tribuf(node->tribuf); + MEM_freeN(node->tribuf); + node->tribuf = nullptr; + } + + if (node->tri_buffers) { + for (int i = 0; i < node->tot_tri_buffers; i++) { + pbvh_free_tribuf(node->tri_buffers + i); + } + + MEM_SAFE_FREE(node->tri_buffers); + + node->tri_buffers = nullptr; + node->tot_tri_buffers = 0; + } +} + +/* +generate triangle buffers with split uv islands. +currently unused (and untested). +*/ +static bool pbvh_bmesh_split_tris(PBVH *pbvh, PBVHNode *node) +{ + BMFace *f; + + BM_mesh_elem_index_ensure(pbvh->header.bm, BM_VERT | BM_FACE); + + // split by uvs + int layeri = CustomData_get_layer_index(&pbvh->header.bm->ldata, CD_PROP_FLOAT2); + if (layeri < 0) { + return false; + } + + int totlayer = 0; + + while (layeri < pbvh->header.bm->ldata.totlayer && + pbvh->header.bm->ldata.layers[layeri].type == CD_PROP_FLOAT2) { + totlayer++; + layeri++; + } + + const int cd_uv = pbvh->header.bm->ldata.layers[layeri].offset; + const int cd_size = CustomData_sizeof(CD_PROP_FLOAT2); + + Vector verts; + Vector tris; + Vector loops; + + TGSET_ITER (f, node->bm_faces) { + BMLoop *l = f->l_first; + + do { + l->head.index = -1; + l = l->next; + } while (l != f->l_first); + } + TGSET_ITER_END + + int vi = 0; + + TGSET_ITER (f, node->bm_faces) { + BMLoop *l = f->l_first; + + do { + if (l->head.index >= 0) { + continue; + } + + l->head.index = vi++; + loops.append((intptr_t)l); + + PBVHVertRef sv = {(intptr_t)l->v}; + verts.append(sv); + + BMIter iter; + BMLoop *l2; + + BM_ITER_ELEM (l2, &iter, l, BM_LOOPS_OF_VERT) { + bool ok = true; + + for (int i = 0; i < totlayer; i++) { + float *uv1 = BM_ELEM_CD_PTR(l, cd_uv + cd_size * i); + float *uv2 = BM_ELEM_CD_PTR(l2, cd_uv + cd_size * i); + + if (len_v3v3(uv1, uv2) > 0.001) { + ok = false; + break; + } + } + + if (ok) { + l2->head.index = l->head.index; + } + } + } while (l != f->l_first); + } + TGSET_ITER_END + + TGSET_ITER (f, node->bm_faces) { + BMLoop *l1 = f->l_first, *l2 = f->l_first->next, *l3 = f->l_first->prev; + + PBVHTri tri; + tri.f.i = (intptr_t)f; + + tri.v[0] = l1->head.index; + tri.v[1] = l2->head.index; + tri.v[2] = l3->head.index; + + copy_v3_v3(tri.no, f->no); + tris.append(tri); + } + TGSET_ITER_END + + if (node->tribuf) { + pbvh_free_tribuf(node->tribuf); + } + else { + node->tribuf = MEM_cnew("node->tribuf"); + } + + node->tribuf->verts = c_array_from_vector(verts); + node->tribuf->loops = c_array_from_vector(loops); + node->tribuf->tris = c_array_from_vector(tris); + + node->tribuf->tottri = tris.size(); + node->tribuf->totvert = verts.size(); + node->tribuf->totloop = loops.size(); + + return true; +} + +BLI_INLINE PBVHTri *pbvh_tribuf_add_tri(PBVHTriBuf *tribuf) +{ + tribuf->tottri++; + + if (tribuf->tottri >= tribuf->tris_size) { + size_t newsize = (size_t)32 + (size_t)tribuf->tris_size + (size_t)(tribuf->tris_size >> 1); + + if (!tribuf->tris) { + tribuf->tris = MEM_cnew_array(newsize, "tribuf tris"); + } + else { + tribuf->tris = (PBVHTri *)MEM_reallocN_id( + (void *)tribuf->tris, sizeof(*tribuf->tris) * newsize, "tribuf tris"); + } + + tribuf->tris_size = newsize; + } + + return tribuf->tris + tribuf->tottri - 1; +} + +BLI_INLINE void pbvh_tribuf_add_vert(PBVHTriBuf *tribuf, PBVHVertRef vertex, BMLoop *l) +{ + tribuf->totvert++; + tribuf->totloop++; + + if (tribuf->totvert >= tribuf->verts_size) { + size_t newsize = (size_t)32 + (size_t)(tribuf->verts_size << 1); + + if (!tribuf->verts) { + tribuf->verts = MEM_cnew_array(newsize, "tribuf verts"); + tribuf->loops = MEM_cnew_array(newsize, "tribuf loops"); + } + else { + tribuf->verts = (PBVHVertRef *)MEM_reallocN_id( + (void *)tribuf->verts, sizeof(*tribuf->verts) * newsize, "tribuf verts"); + tribuf->loops = (intptr_t *)MEM_reallocN_id( + (void *)tribuf->loops, sizeof(*tribuf->loops) * newsize, "tribuf loops"); + } + + tribuf->verts_size = newsize; + } + + tribuf->verts[tribuf->totvert - 1] = vertex; + tribuf->loops[tribuf->totloop - 1] = (uintptr_t)l; +} + +BLI_INLINE void pbvh_tribuf_add_edge(PBVHTriBuf *tribuf, int v1, int v2) +{ + tribuf->totedge++; + + if (tribuf->totedge >= tribuf->edges_size) { + size_t newsize = (size_t)32 + (size_t)(tribuf->edges_size << 1); + + if (!tribuf->edges) { + tribuf->edges = MEM_cnew_array(2ULL * newsize, "tribuf edges"); + } + else { + tribuf->edges = (int *)MEM_reallocN_id( + (void *)tribuf->edges, sizeof(*tribuf->edges) * 2ULL * newsize, "tribuf edges"); + } + + tribuf->edges_size = newsize; + } + + int i = (tribuf->totedge - 1) * 2; + + tribuf->edges[i] = v1; + tribuf->edges[i + 1] = v2; +} + +void pbvh_bmesh_check_other_verts(PBVHNode *node) +{ + if (!(node->flag & PBVH_UpdateOtherVerts)) { + return; + } + + node->flag &= ~PBVH_UpdateOtherVerts; + + if (node->bm_other_verts) { + BLI_table_gset_free(node->bm_other_verts, nullptr); + } + + node->bm_other_verts = BLI_table_gset_new("bm_other_verts"); + BMFace *f; + + TGSET_ITER (f, node->bm_faces) { + BMLoop *l = f->l_first; + + do { + if (!BLI_table_gset_haskey(node->bm_unique_verts, l->v)) { + BLI_table_gset_add(node->bm_other_verts, l->v); + } + } while ((l = l->next) != f->l_first); + } + TGSET_ITER_END; +} + +static void pbvh_init_tribuf(PBVHNode *node, PBVHTriBuf *tribuf) +{ + tribuf->tottri = 0; + tribuf->tris_size = 0; + tribuf->verts_size = 0; + tribuf->mat_nr = 0; + tribuf->tottri = 0; + tribuf->totvert = 0; + tribuf->totloop = 0; + tribuf->totedge = 0; + + tribuf->edges = nullptr; + tribuf->verts = nullptr; + tribuf->tris = nullptr; + tribuf->loops = nullptr; + + BLI_smallhash_init_ex(&tribuf->vertmap, node->bm_unique_verts->length); +} + +static uintptr_t tri_loopkey(BMLoop *l, int mat_nr, int cd_fset, int cd_uvs[], int totuv) +{ + uintptr_t key = (uintptr_t)mat_nr; + + key ^= (uintptr_t)l->v; + + if (cd_fset >= 0) { + // key ^= (uintptr_t)BLI_hash_int(BM_ELEM_CD_GET_INT(l->f, cd_fset)); + key ^= (uintptr_t)BM_ELEM_CD_GET_INT(l->f, cd_fset); + } + + for (int i = 0; i < totuv; i++) { + float *luv = BM_ELEM_CD_PTR(l, cd_uvs[i]); + float snap = 4196.0f; + + uintptr_t x = (uintptr_t)(luv[0] * snap); + uintptr_t y = (uintptr_t)(luv[1] * snap); + + uintptr_t key2 = y * snap + x; + key ^= key2; + } + + return key; +} +/* In order to perform operations on the original node coordinates + * (currently just raycast), store the node's triangles and vertices. + * + * Skips triangles that are hidden. */ +bool BKE_pbvh_bmesh_check_tris(PBVH *pbvh, PBVHNode *node) +{ + BMesh *bm = pbvh->header.bm; + + if (!(node->flag & PBVH_UpdateTris) && node->tribuf) { + return false; + } + + int totuv = CustomData_number_of_layers(&bm->ldata, CD_PROP_FLOAT2); + int *cd_uvs = (int *)BLI_array_alloca(cd_uvs, totuv); + + for (int i = 0; i < totuv; i++) { + int idx = CustomData_get_layer_index_n(&bm->ldata, CD_PROP_FLOAT2, i); + cd_uvs[i] = bm->ldata.layers[idx].offset; + } + + node->flag |= PBVH_UpdateOtherVerts; + + int mat_map[MAXMAT]; + + for (int i = 0; i < MAXMAT; i++) { + mat_map[i] = -1; + } + + if (node->tribuf || node->tri_buffers) { + BKE_pbvh_bmesh_free_tris(pbvh, node); + } + + node->tribuf = MEM_cnew("node->tribuf"); + pbvh_init_tribuf(node, node->tribuf); + + Vector loops; + Vector loops_idx; + Vector tribufs; + + node->flag &= ~PBVH_UpdateTris; + + const int edgeflag = BM_ELEM_TAG_ALT; + + BMFace *f; + + float min[3], max[3]; + + INIT_MINMAX(min, max); + + TGSET_ITER (f, node->bm_faces) { + if (pbvh_poly_hidden(pbvh, f)) { + continue; + } + +#ifdef SCULPT_DIAGONAL_EDGE_MARKS + int ecount = 0; +#endif + + // clear edgeflag for building edge indices later + BMLoop *l = f->l_first; + do { +#ifdef SCULPT_DIAGONAL_EDGE_MARKS + BMEdge *e2 = l->v->e; + do { + if (e2->head.hflag & BM_ELEM_DRAW) { + ecount++; + } + } while ((e2 = BM_DISK_EDGE_NEXT(e2, l->v)) != l->v->e); +#endif + l->e->head.hflag &= ~edgeflag; + } while ((l = l->next) != f->l_first); + + const int mat_nr = f->mat_nr; + + if (mat_map[mat_nr] == -1) { + PBVHTriBuf _tribuf = {0}; + + mat_map[mat_nr] = tribufs.size(); + + pbvh_init_tribuf(node, &_tribuf); + _tribuf.mat_nr = mat_nr; + tribufs.append(_tribuf); + } + +#ifdef DYNTOPO_DYNAMIC_TESS + int tottri = (f->len - 2); + + loops.resize(f->len); + loops_idx.resize(tottri); + + BM_face_calc_tessellation(f, true, loops.data(), (uint(*)[3])loops_idx.data()); + + for (int i = 0; i < tottri; i++) { + PBVHTri *tri = pbvh_tribuf_add_tri(node->tribuf); + PBVHTriBuf *mat_tribuf = &tribufs[mat_map[mat_nr]]; + PBVHTri *mat_tri = pbvh_tribuf_add_tri(mat_tribuf); + + tri->eflag = mat_tri->eflag = 0; + + for (int j = 0; j < 3; j++) { + // BMLoop *l0 = loops[loops_idx[i][(j + 2) % 3]]; + BMLoop *l = loops[loops_idx[i][j]]; + BMLoop *l2 = loops[loops_idx[i][(j + 1) % 3]]; + + void **val = nullptr; + BMEdge *e = BM_edge_exists(l->v, l2->v); + +# ifdef SCULPT_DIAGONAL_EDGE_MARKS + if (e && (e->head.hflag & BM_ELEM_DRAW)) { +# else + if (e) { +# endif + tri->eflag |= 1 << j; + mat_tri->eflag |= 1 << j; + } + + uintptr_t loopkey = tri_loopkey(l, mat_nr, pbvh->cd_faceset_offset, cd_uvs, totuv); + + if (!BLI_smallhash_ensure_p(&node->tribuf->vertmap, loopkey, &val)) { + PBVHVertRef sv = {(intptr_t)l->v}; + + minmax_v3v3_v3(min, max, l->v->co); + + *val = POINTER_FROM_INT(node->tribuf->totvert); + pbvh_tribuf_add_vert(node->tribuf, sv, l); + } + + tri->v[j] = (intptr_t)val[0]; + tri->l[j] = (intptr_t)l; + + val = nullptr; + if (!BLI_smallhash_ensure_p(&mat_tribuf->vertmap, loopkey, &val)) { + PBVHVertRef sv = {(intptr_t)l->v}; + + minmax_v3v3_v3(min, max, l->v->co); + + *val = POINTER_FROM_INT(mat_tribuf->totvert); + pbvh_tribuf_add_vert(mat_tribuf, sv, l); + } + + mat_tri->v[j] = (intptr_t)val[0]; + mat_tri->l[j] = (intptr_t)l; + } + + copy_v3_v3(tri->no, f->no); + copy_v3_v3(mat_tri->no, f->no); + tri->f.i = (intptr_t)f; + mat_tri->f.i = (intptr_t)f; + } +#else + PBVHTri *tri = pbvh_tribuf_add_tri(node->tribuf); + PBVHTriBuf *mat_tribuf = tribufs + mat_map[mat_nr]; + PBVHTri *mat_tri = pbvh_tribuf_add_tri(mat_tribuf); + + BMLoop *l = f->l_first; + int j = 0; + + do { + void **val = nullptr; + + if (!BLI_ghash_ensure_p(vmap, l->v, &val)) { + PBVHVertRef sv = {(intptr_t)l->v}; + + minmax_v3v3_v3(min, max, l->v->co); + + *val = (void *)node->tribuf->totvert; + pbvh_tribuf_add_vert(node->tribuf, sv); + } + + tri->v[j] = (intptr_t)val[0]; + tri->l[j] = (intptr_t)l; + + val = nullptr; + if (!BLI_ghash_ensure_p(mat_vmaps[mat_nr], l->v, &val)) { + PBVHVertRef sv = {(intptr_t)l->v}; + + minmax_v3v3_v3(min, max, l->v->co); + + *val = (void *)mat_tribuf->totvert; + pbvh_tribuf_add_vert(mat_tribuf, sv); + } + + mat_tri->v[j] = (intptr_t)val[0]; + mat_tri->l[j] = (intptr_t)l; + + j++; + + if (j >= 3) { + break; + } + + l = l->next; + } while (l != f->l_first); + + copy_v3_v3(tri->no, f->no); + tri->f.i = (intptr_t)f; +#endif + } + TGSET_ITER_END + + TGSET_ITER (f, node->bm_faces) { + if (pbvh_poly_hidden(pbvh, f)) { + continue; + } + + int mat_nr = f->mat_nr; + PBVHTriBuf *mat_tribuf = &tribufs[mat_map[mat_nr]]; + + BMLoop *l = f->l_first; + do { + if (l->e->head.hflag & edgeflag) { + continue; + } + + l->e->head.hflag |= edgeflag; + + int v1 = POINTER_AS_INT(BLI_smallhash_lookup(&node->tribuf->vertmap, (uintptr_t)l->e->v1)); + int v2 = POINTER_AS_INT(BLI_smallhash_lookup(&node->tribuf->vertmap, (uintptr_t)l->e->v2)); + + pbvh_tribuf_add_edge(node->tribuf, v1, v2); + + v1 = POINTER_AS_INT(BLI_smallhash_lookup(&mat_tribuf->vertmap, (uintptr_t)l->e->v1)); + v2 = POINTER_AS_INT(BLI_smallhash_lookup(&mat_tribuf->vertmap, (uintptr_t)l->e->v2)); + + pbvh_tribuf_add_edge(mat_tribuf, v1, v2); + } while ((l = l->next) != f->l_first); + } + TGSET_ITER_END + + bm->elem_index_dirty |= BM_VERT; + + node->tri_buffers = c_array_from_vector(tribufs); + node->tot_tri_buffers = tribufs.size(); + + if (node->tribuf->totvert) { + copy_v3_v3(node->tribuf->min, min); + copy_v3_v3(node->tribuf->max, max); + } + else { + zero_v3(node->tribuf->min); + zero_v3(node->tribuf->max); + } + + return true; +} + +static int pbvh_count_subtree_verts(PBVH *pbvh, PBVHNode *n) +{ + if (n->flag & PBVH_Leaf) { + n->subtree_tottri = BLI_table_gset_len( + n->bm_faces); // n->tm_unique_verts->length + n->tm_other_verts->length; + return n->subtree_tottri; + } + + int ni = n->children_offset; + + int ret = pbvh_count_subtree_verts(pbvh, pbvh->nodes + ni); + ret += pbvh_count_subtree_verts(pbvh, pbvh->nodes + ni + 1); + + n->subtree_tottri = ret; + + return ret; +} + +void BKE_pbvh_bmesh_flag_all_disk_sort(PBVH *pbvh) +{ + BMVert *v; + BMIter iter; + + BM_ITER_MESH (v, &iter, pbvh->header.bm, BM_VERTS_OF_MESH) { + MSculptVert *mv = BKE_PBVH_SCULPTVERT(pbvh->cd_sculpt_vert, v); + mv->flag |= SCULPTVERT_NEED_DISK_SORT; + } +} + +void BKE_pbvh_bmesh_update_all_valence(PBVH *pbvh) +{ + BMIter iter; + BMVert *v; + + BM_ITER_MESH (v, &iter, pbvh->header.bm, BM_VERTS_OF_MESH) { + BKE_pbvh_bmesh_update_valence(pbvh->cd_sculpt_vert, (PBVHVertRef){(intptr_t)v}); + } +} + +void BKE_pbvh_bmesh_on_mesh_change(PBVH *pbvh) +{ + BMIter iter; + BMVert *v; + + for (int i = 0; i < pbvh->totnode; i++) { + PBVHNode *node = pbvh->nodes + i; + + if (node->flag & PBVH_Leaf) { + node->flag |= PBVH_UpdateTriAreas; + } + } + + const int cd_sculpt_vert = pbvh->cd_sculpt_vert; + + BM_ITER_MESH (v, &iter, pbvh->header.bm, BM_VERTS_OF_MESH) { + MSculptVert *mv = BKE_PBVH_SCULPTVERT(cd_sculpt_vert, v); + int *flags = BM_ELEM_CD_PTR(v, pbvh->cd_boundary_flag); + *flags |= SCULPT_BOUNDARY_NEEDS_UPDATE; + + MV_ADD_FLAG(mv, SCULPTVERT_NEED_DISK_SORT | SCULPTVERT_NEED_TRIANGULATE); + BKE_pbvh_bmesh_update_valence(pbvh->cd_sculpt_vert, (PBVHVertRef){.i = (intptr_t)v}); + } +} + +bool BKE_pbvh_bmesh_mark_update_valence(PBVH *pbvh, PBVHVertRef vertex) +{ + BMVert *v = (BMVert *)vertex.i; + MSculptVert *mv = BM_ELEM_CD_PTR(v, pbvh->cd_sculpt_vert); + + bool ret = mv->flag & SCULPTVERT_NEED_VALENCE; + + mv->flag |= SCULPTVERT_NEED_VALENCE; + + return ret; +} + +bool BKE_pbvh_bmesh_check_valence(PBVH *pbvh, PBVHVertRef vertex) +{ + BMVert *v = (BMVert *)vertex.i; + MSculptVert *mv = BM_ELEM_CD_PTR(v, pbvh->cd_sculpt_vert); + + if (mv->flag & SCULPTVERT_NEED_VALENCE) { + BKE_pbvh_bmesh_update_valence(pbvh->cd_sculpt_vert, vertex); + return true; + } + + return false; +} + +void BKE_pbvh_bmesh_update_valence(int cd_sculpt_vert, PBVHVertRef vertex) +{ + BMVert *v = (BMVert *)vertex.i; + BMEdge *e; + + MSculptVert *mv = BM_ELEM_CD_PTR(v, cd_sculpt_vert); + + mv->flag &= ~SCULPTVERT_NEED_VALENCE; + + if (!v->e) { + mv->valence = 0; + return; + } + + mv->valence = 0; + + e = v->e; + + if (!e) { + return; + } + + do { + mv->valence++; + + e = v == e->v1 ? e->v1_disk_link.next : e->v2_disk_link.next; + + if (!e) { + printf("bmesh error!\n"); + break; + } + } while (e != v->e); +} + +static void pbvh_bmesh_join_subnodes(PBVH *pbvh, PBVHNode *node, PBVHNode *parent) +{ + if (!(node->flag & PBVH_Leaf)) { + int ni = node->children_offset; + + if (ni > 0 && ni < pbvh->totnode - 1) { + pbvh_bmesh_join_subnodes(pbvh, pbvh->nodes + ni, parent); + pbvh_bmesh_join_subnodes(pbvh, pbvh->nodes + ni + 1, parent); + } + else { + printf("node corruption: %d\n", ni); + return; + } + if (node != parent) { + node->flag |= PBVH_Delete; // mark for deletion + } + + return; + } + + if (node != parent) { + node->flag |= PBVH_Delete; // mark for deletion + } + + BMVert *v; + + TGSET_ITER (v, node->bm_unique_verts) { + BLI_table_gset_add(parent->bm_unique_verts, v); + + int *flags = BM_ELEM_CD_PTR(v, pbvh->cd_boundary_flag); + *flags |= SCULPT_BOUNDARY_NEEDS_UPDATE; + + BM_ELEM_CD_SET_INT(v, pbvh->cd_vert_node_offset, DYNTOPO_NODE_NONE); + } + TGSET_ITER_END + + // printf(" subtotface: %d\n", BLI_table_gset_len(node->bm_faces)); + + BMFace *f; + TGSET_ITER (f, node->bm_faces) { + BLI_table_gset_add(parent->bm_faces, f); + BM_ELEM_CD_SET_INT(f, pbvh->cd_face_node_offset, DYNTOPO_NODE_NONE); + } + TGSET_ITER_END +} + +static void BKE_pbvh_bmesh_correct_tree(PBVH *pbvh, PBVHNode *node, PBVHNode *parent) +{ + const int size_lower = pbvh->leaf_limit - (pbvh->leaf_limit >> 1); + + if (node->flag & PBVH_Leaf) { + // pbvh_bmesh_node_limit_ensure(pbvh, (int)(node - pbvh->nodes)); + return; + } + + if (node->subtree_tottri < size_lower && node != pbvh->nodes) { + node->bm_unique_verts = BLI_table_gset_new("bm_unique_verts"); + node->bm_other_verts = BLI_table_gset_new("bm_other_verts"); + node->bm_faces = BLI_table_gset_new("bm_faces"); + + pbvh_bmesh_join_subnodes(pbvh, pbvh->nodes + node->children_offset, node); + pbvh_bmesh_join_subnodes(pbvh, pbvh->nodes + node->children_offset + 1, node); + + node->children_offset = 0; + node->flag |= PBVH_Leaf | PBVH_UpdateRedraw | PBVH_UpdateBB | PBVH_UpdateDrawBuffers | + PBVH_RebuildDrawBuffers | PBVH_UpdateOriginalBB | PBVH_UpdateMask | + PBVH_UpdateVisibility | PBVH_UpdateColor | PBVH_UpdateNormals | PBVH_UpdateTris; + + TableGSet *other = BLI_table_gset_new(__func__); + BMVert *v; + + node->children_offset = 0; + node->draw_batches = nullptr; + + // rebuild bm_other_verts + BMFace *f; + TGSET_ITER (f, node->bm_faces) { + BMLoop *l = f->l_first; + + BM_ELEM_CD_SET_INT(f, pbvh->cd_face_node_offset, DYNTOPO_NODE_NONE); + + do { + if (!BLI_table_gset_haskey(node->bm_unique_verts, l->v)) { + BLI_table_gset_add(other, l->v); + } + l = l->next; + } while (l != f->l_first); + } + TGSET_ITER_END + + BLI_table_gset_free(node->bm_other_verts, nullptr); + node->bm_other_verts = other; + + BB_reset(&node->vb); + +#if 1 + TGSET_ITER (v, node->bm_unique_verts) { + BB_expand(&node->vb, v->co); + } + TGSET_ITER_END + + TGSET_ITER (v, node->bm_other_verts) { + BB_expand(&node->vb, v->co); + } + TGSET_ITER_END +#endif + + // printf("totface: %d\n", BLI_table_gset_len(node->bm_faces)); + node->orig_vb = node->vb; + + return; + } + + int ni = node->children_offset; + + for (int i = 0; i < 2; i++, ni++) { + PBVHNode *child = pbvh->nodes + ni; + BKE_pbvh_bmesh_correct_tree(pbvh, child, node); + } +} + +// deletes PBVH_Delete marked nodes +static void pbvh_bmesh_compact_tree(PBVH *bvh) +{ + // compact nodes + int totnode = 0; + for (int i = 0; i < bvh->totnode; i++) { + PBVHNode *n = bvh->nodes + i; + + if (!(n->flag & PBVH_Delete)) { + if (!(n->flag & PBVH_Leaf)) { + PBVHNode *n1 = bvh->nodes + n->children_offset; + PBVHNode *n2 = bvh->nodes + n->children_offset + 1; + + if ((n1->flag & PBVH_Delete) != (n2->flag & PBVH_Delete)) { + printf("un-deleting an empty node\n"); + PBVHNode *n3 = n1->flag & PBVH_Delete ? n1 : n2; + + n3->flag = PBVH_Leaf | PBVH_UpdateTris; + n3->bm_unique_verts = BLI_table_gset_new("bm_unique_verts"); + n3->bm_other_verts = BLI_table_gset_new("bm_other_verts"); + n3->bm_faces = BLI_table_gset_new("bm_faces"); + n3->tribuf = nullptr; + n3->draw_batches = nullptr; + } + else if ((n1->flag & PBVH_Delete) && (n2->flag & PBVH_Delete)) { + n->children_offset = 0; + n->flag |= PBVH_Leaf | PBVH_UpdateTris; + + if (!n->bm_unique_verts) { + // should not happen + n->bm_unique_verts = BLI_table_gset_new("bm_unique_verts"); + n->bm_other_verts = BLI_table_gset_new("bm_other_verts"); + n->bm_faces = BLI_table_gset_new("bm_faces"); + n->tribuf = nullptr; + n->draw_batches = nullptr; + } + } + } + + totnode++; + } + } + + int *map = MEM_cnew_array(bvh->totnode, "bmesh map temp"); + + // build idx map for child offsets + int j = 0; + for (int i = 0; i < bvh->totnode; i++) { + PBVHNode *n = bvh->nodes + i; + + if (!(n->flag & PBVH_Delete)) { + map[i] = j++; + } + else if (1) { + if (n->layer_disp) { + MEM_freeN(n->layer_disp); + n->layer_disp = nullptr; + } + + pbvh_free_draw_buffers(bvh, n); + + if (n->vert_indices) { + MEM_freeN((void *)n->vert_indices); + n->vert_indices = nullptr; + } + if (n->face_vert_indices) { + MEM_freeN((void *)n->face_vert_indices); + n->face_vert_indices = nullptr; + } + + if (n->tribuf || n->tri_buffers) { + BKE_pbvh_bmesh_free_tris(bvh, n); + } + + if (n->bm_unique_verts) { + BLI_table_gset_free(n->bm_unique_verts, nullptr); + n->bm_unique_verts = nullptr; + } + + if (n->bm_other_verts) { + BLI_table_gset_free(n->bm_other_verts, nullptr); + n->bm_other_verts = nullptr; + } + + if (n->bm_faces) { + BLI_table_gset_free(n->bm_faces, nullptr); + n->bm_faces = nullptr; + } + +#ifdef PROXY_ADVANCED + BKE_pbvh_free_proxyarray(bvh, n); +#endif + } + } + + // compact node array + j = 0; + for (int i = 0; i < bvh->totnode; i++) { + if (!(bvh->nodes[i].flag & PBVH_Delete)) { + if (bvh->nodes[i].children_offset >= bvh->totnode - 1) { + printf("error %i %i\n", i, bvh->nodes[i].children_offset); + continue; + } + + int i1 = map[bvh->nodes[i].children_offset]; + int i2 = map[bvh->nodes[i].children_offset + 1]; + + if (bvh->nodes[i].children_offset >= bvh->totnode) { + printf("bad child node reference %d->%d, totnode: %d\n", + i, + bvh->nodes[i].children_offset, + bvh->totnode); + continue; + } + + if (bvh->nodes[i].children_offset && i2 != i1 + 1) { + printf(" pbvh corruption during node join %d %d\n", i1, i2); + } + + bvh->nodes[j] = bvh->nodes[i]; + bvh->nodes[j].children_offset = i1; + + j++; + } + } + + if (j != totnode) { + printf("pbvh error: %s", __func__); + } + + if (bvh->totnode != j) { + memset(bvh->nodes + j, 0, sizeof(*bvh->nodes) * (bvh->totnode - j)); + bvh->node_mem_count = j; + } + + bvh->totnode = j; + + // set vert/face node indices again + for (int i = 0; i < bvh->totnode; i++) { + PBVHNode *n = bvh->nodes + i; + + if (!(n->flag & PBVH_Leaf)) { + continue; + } + + if (!n->bm_unique_verts) { + printf("ERROR!\n"); + n->bm_unique_verts = BLI_table_gset_new("bleh"); + n->bm_other_verts = BLI_table_gset_new("bleh"); + n->bm_faces = BLI_table_gset_new("bleh"); + } + + BMVert *v; + + TGSET_ITER (v, n->bm_unique_verts) { + BM_ELEM_CD_SET_INT(v, bvh->cd_vert_node_offset, i); + } + TGSET_ITER_END + + BMFace *f; + + TGSET_ITER (f, n->bm_faces) { + BM_ELEM_CD_SET_INT(f, bvh->cd_face_node_offset, i); + } + TGSET_ITER_END + } + + Vector scratch; + + for (int i = 0; i < bvh->totnode; i++) { + PBVHNode *n = bvh->nodes + i; + + if (!(n->flag & PBVH_Leaf)) { + continue; + } + + scratch.clear(); + BMVert *v; + + TGSET_ITER (v, n->bm_other_verts) { + int ni = BM_ELEM_CD_GET_INT(v, bvh->cd_vert_node_offset); + if (ni == DYNTOPO_NODE_NONE) { + scratch.append(v); + } + // BM_ELEM_CD_SET_INT(v, bvh->cd_vert_node_offset, i); + } + TGSET_ITER_END + + int slen = scratch.size(); + for (int j = 0; j < slen; j++) { + BMVert *v = scratch[j]; + + BLI_table_gset_remove(n->bm_other_verts, v, nullptr); + BLI_table_gset_add(n->bm_unique_verts, v); + BM_ELEM_CD_SET_INT(v, bvh->cd_vert_node_offset, i); + } + } + + MEM_freeN(map); +} + +static void recursive_delete_nodes(PBVH *pbvh, int ni) +{ + PBVHNode *node = pbvh->nodes + ni; + + node->flag |= PBVH_Delete; + + if (!(node->flag & PBVH_Leaf) && node->children_offset) { + if (node->children_offset < pbvh->totnode) { + recursive_delete_nodes(pbvh, node->children_offset); + } + + if (node->children_offset + 1 < pbvh->totnode) { + recursive_delete_nodes(pbvh, node->children_offset + 1); + } + } +} + +// static float bbox_overlap() +/* works by detect overlay of leaf nodes, destroying them + and then re-inserting them*/ +static void pbvh_bmesh_balance_tree(PBVH *pbvh) +{ + float *overlaps = MEM_cnew_array(pbvh->totnode, "overlaps"); + PBVHNode **parentmap = MEM_cnew_array(pbvh->totnode, "parentmap"); + int *depthmap = MEM_cnew_array(pbvh->totnode, "depthmap"); + + Vector stack; + Vector faces; + Vector substack; + + for (int i = 0; i < pbvh->totnode; i++) { + PBVHNode *node = pbvh->nodes + i; + + if ((node->flag & PBVH_Leaf) || node->children_offset == 0) { + continue; + } + + if (node->children_offset < pbvh->totnode) { + parentmap[node->children_offset] = node; + } + + if (node->children_offset + 1 < pbvh->totnode) { + parentmap[node->children_offset + 1] = node; + } + } + +#if 0 + for (int i = 0; i < pbvh->totnode; i++) { + PBVHNode *node = pbvh->nodes + i; + PBVHNode *parent = parentmap[i]; + int depth = 0; + + while (parent) { + parent = parentmap[parent - pbvh->nodes]; + depth++; + } + + depthmap[i] = depth; + } +#endif + + const int cd_vert_node = pbvh->cd_vert_node_offset; + const int cd_face_node = pbvh->cd_face_node_offset; + + bool modified = false; + + stack.append(pbvh->nodes); + + while (stack.size() > 0) { + PBVHNode *node = stack.pop_last(); + BB clip; + + if (!(node->flag & PBVH_Leaf) && node->children_offset > 0) { + PBVHNode *child1 = pbvh->nodes + node->children_offset; + PBVHNode *child2 = pbvh->nodes + node->children_offset + 1; + + float volume = BB_volume(&child1->vb) + BB_volume(&child2->vb); + + /* dissolve nodes whose children overlap by more then a percentage + of the total volume. we use a simple huerstic to calculate the + cutoff threshold.*/ + + BB_intersect(&clip, &child1->vb, &child2->vb); + float overlap = BB_volume(&clip); + float factor; + + /* use higher threshold for the root node and its immediate children */ + switch (stack.size()) { + case 0: + factor = 0.5; + break; + case 1: + case 2: + factor = 0.2; + break; + default: + factor = 0.2; + break; + } + +#if 0 + for (int k = 0; k < stack.size(); k++) { + printf(" "); + } + + printf("factor: %.3f\n", factor); +#endif + + bool bad = overlap > volume * factor; + + bad |= child1->bm_faces && !BLI_table_gset_len(child1->bm_faces); + bad |= child2->bm_faces && !BLI_table_gset_len(child2->bm_faces); + + if (bad) { + modified = true; + + substack.clear(); + substack.append(child1); + substack.append(child2); + + while (substack.size() > 0) { + PBVHNode *node2 = substack.pop_last(); + + node2->flag |= PBVH_Delete; + + if (node2->flag & PBVH_Leaf) { + BMFace *f; + BMVert *v; + + TGSET_ITER (f, node2->bm_faces) { + if (BM_ELEM_CD_GET_INT(f, cd_face_node) == -1) { + // eek! + continue; + } + + BM_ELEM_CD_SET_INT(f, cd_face_node, DYNTOPO_NODE_NONE); + faces.append(f); + } + TGSET_ITER_END; + + TGSET_ITER (v, node2->bm_unique_verts) { + int *flags = BM_ELEM_CD_PTR(v, pbvh->cd_boundary_flag); + *flags |= SCULPT_BOUNDARY_NEEDS_UPDATE; + + BM_ELEM_CD_SET_INT(v, cd_vert_node, DYNTOPO_NODE_NONE); + } + TGSET_ITER_END; + } + else if (node2->children_offset > 0 && node2->children_offset < pbvh->totnode) { + substack.append(pbvh->nodes + node2->children_offset); + + if (node2->children_offset + 1 < pbvh->totnode) { + substack.append(pbvh->nodes + node2->children_offset + 1); + } + } + } + } + + if (node->children_offset < pbvh->totnode) { + stack.append(child1); + } + + if (node->children_offset + 1 < pbvh->totnode) { + stack.append(child2); + } + } + } + + if (modified) { + pbvh_bmesh_compact_tree(pbvh); + + printf("joined nodes; %d faces\n", (int)faces.size()); + + for (int i : IndexRange(faces.size())) { + if (BM_elem_is_free((BMElem *)faces[i], BM_FACE)) { + printf("corrupted face in pbvh tree; faces[i]: %p\n", faces[i]); + continue; + } + + if (BM_ELEM_CD_GET_INT(faces[i], cd_face_node) != DYNTOPO_NODE_NONE) { + // printf("duplicate faces in pbvh_bmesh_balance_tree!\n"); + continue; + } + + bke_pbvh_insert_face(pbvh, faces[i]); + } + } + + MEM_SAFE_FREE(parentmap); + MEM_SAFE_FREE(overlaps); + MEM_SAFE_FREE(depthmap); +} + +static void pbvh_bmesh_join_nodes(PBVH *bvh) +{ + if (bvh->totnode < 2) { + return; + } + + pbvh_count_subtree_verts(bvh, bvh->nodes); + BKE_pbvh_bmesh_correct_tree(bvh, bvh->nodes, nullptr); + + // compact nodes + int totnode = 0; + for (int i = 0; i < bvh->totnode; i++) { + PBVHNode *n = bvh->nodes + i; + + if (0 && !(n->flag & PBVH_Delete)) { + if (!(n->flag & PBVH_Leaf)) { + PBVHNode *n1 = bvh->nodes + n->children_offset; + PBVHNode *n2 = bvh->nodes + n->children_offset + 1; + + if ((n1->flag & PBVH_Delete) != (n2->flag & PBVH_Delete)) { + printf("%s: Un-deleting an empty node!\n", __func__); + PBVHNode *n3 = n1->flag & PBVH_Delete ? n1 : n2; + + n3->flag = PBVH_Leaf | PBVH_UpdateTris; + n3->bm_unique_verts = BLI_table_gset_new("bm_unique_verts"); + n3->bm_other_verts = BLI_table_gset_new("bm_other_verts"); + n3->bm_faces = BLI_table_gset_new("bm_faces"); + n3->tribuf = nullptr; + n3->draw_batches = nullptr; + } + else if ((n1->flag & PBVH_Delete) && (n2->flag & PBVH_Delete)) { + n->children_offset = 0; + n->flag |= PBVH_Leaf | PBVH_UpdateTris; + + if (!n->bm_unique_verts) { + // should not happen + n->bm_unique_verts = BLI_table_gset_new("bm_unique_verts"); + n->bm_other_verts = BLI_table_gset_new("bm_other_verts"); + n->bm_faces = BLI_table_gset_new("bm_faces"); + } + + n->tribuf = nullptr; + n->draw_batches = nullptr; + } + } + + totnode++; + } + } + + int *map = MEM_cnew_array(bvh->totnode, "bmesh map temp"); + + for (int i = 0; i < bvh->totnode; i++) { + for (int j = 0; j < bvh->totnode; j++) { + if (i == j || !bvh->nodes[i].draw_batches) { + continue; + } + + if (bvh->nodes[i].draw_batches == bvh->nodes[j].draw_batches) { + printf("%s: error %d %d\n", __func__, i, j); + + bvh->nodes[j].draw_batches = nullptr; + } + } + } + + // build idx map for child offsets + int j = 0; + for (int i = 0; i < bvh->totnode; i++) { + PBVHNode *n = bvh->nodes + i; + + if (!(n->flag & PBVH_Delete)) { + map[i] = j++; + } + else { + if (n->layer_disp) { + MEM_freeN(n->layer_disp); + n->layer_disp = nullptr; + } + + pbvh_free_draw_buffers(bvh, n); + + if (n->vert_indices) { + MEM_freeN((void *)n->vert_indices); + n->vert_indices = nullptr; + } + if (n->face_vert_indices) { + MEM_freeN((void *)n->face_vert_indices); + n->face_vert_indices = nullptr; + } + + if (n->tribuf || n->tri_buffers) { + BKE_pbvh_bmesh_free_tris(bvh, n); + } + + if (n->bm_unique_verts) { + BLI_table_gset_free(n->bm_unique_verts, nullptr); + n->bm_unique_verts = nullptr; + } + + if (n->bm_other_verts) { + BLI_table_gset_free(n->bm_other_verts, nullptr); + n->bm_other_verts = nullptr; + } + + if (n->bm_faces) { + BLI_table_gset_free(n->bm_faces, nullptr); + n->bm_faces = nullptr; + } + +#ifdef PROXY_ADVANCED + BKE_pbvh_free_proxyarray(bvh, n); +#endif + } + } + + // compact node array + j = 0; + for (int i = 0; i < bvh->totnode; i++) { + if (!(bvh->nodes[i].flag & PBVH_Delete)) { + if (bvh->nodes[i].children_offset >= bvh->totnode - 1) { + printf("%s: error %i %i\n", __func__, i, bvh->nodes[i].children_offset); + continue; + } + + int i1 = map[bvh->nodes[i].children_offset]; + int i2 = map[bvh->nodes[i].children_offset + 1]; + + if (bvh->nodes[i].children_offset >= bvh->totnode) { + printf("%s: Bad child node reference %d->%d, totnode: %d\n", + __func__, + i, + bvh->nodes[i].children_offset, + bvh->totnode); + continue; + } + + if (bvh->nodes[i].children_offset && i2 != i1 + 1) { + printf(" pbvh corruption during node join %d %d\n", i1, i2); + } + + bvh->nodes[j] = bvh->nodes[i]; + bvh->nodes[j].children_offset = i1; + + j++; + } + } + + if (j != totnode) { + printf("%s: pbvh error.", __func__); + } + + if (bvh->totnode != j) { + memset(bvh->nodes + j, 0, sizeof(*bvh->nodes) * (bvh->totnode - j)); + bvh->node_mem_count = j; + } + + bvh->totnode = j; + + // set vert/face node indices again + for (int i = 0; i < bvh->totnode; i++) { + PBVHNode *n = bvh->nodes + i; + + if (!(n->flag & PBVH_Leaf)) { + continue; + } + + if (!n->bm_unique_verts) { + printf("%s: ERROR!\n", __func__); + n->bm_unique_verts = BLI_table_gset_new("bleh"); + n->bm_other_verts = BLI_table_gset_new("bleh"); + n->bm_faces = BLI_table_gset_new("bleh"); + } + + BMVert *v; + + TGSET_ITER (v, n->bm_unique_verts) { + BM_ELEM_CD_SET_INT(v, bvh->cd_vert_node_offset, i); + } + TGSET_ITER_END + + BMFace *f; + + TGSET_ITER (f, n->bm_faces) { + BM_ELEM_CD_SET_INT(f, bvh->cd_face_node_offset, i); + } + TGSET_ITER_END + } + + Vector scratch; + + for (int i = 0; i < bvh->totnode; i++) { + PBVHNode *n = bvh->nodes + i; + + if (!(n->flag & PBVH_Leaf)) { + continue; + } + + scratch.clear(); + BMVert *v; + + TGSET_ITER (v, n->bm_other_verts) { + int ni = BM_ELEM_CD_GET_INT(v, bvh->cd_vert_node_offset); + if (ni == DYNTOPO_NODE_NONE) { + scratch.append(v); + } + // BM_ELEM_CD_SET_INT(v, bvh->cd_vert_node_offset, i); + } + TGSET_ITER_END + + for (int j : IndexRange(scratch.size())) { + BMVert *v = scratch[j]; + + BLI_table_gset_remove(n->bm_other_verts, v, nullptr); + BLI_table_gset_add(n->bm_unique_verts, v); + BM_ELEM_CD_SET_INT(v, bvh->cd_vert_node_offset, i); + } + } + + MEM_freeN(map); +} + +void BKE_pbvh_bmesh_after_stroke(PBVH *pbvh, bool force_balance) +{ + int totnode = pbvh->totnode; + + BKE_pbvh_update_bounds(pbvh, (PBVH_UpdateBB | PBVH_UpdateOriginalBB | PBVH_UpdateRedraw)); + + pbvh_bmesh_check_nodes(pbvh); + pbvh_bmesh_join_nodes(pbvh); + pbvh_bmesh_check_nodes(pbvh); + + BKE_pbvh_update_bounds(pbvh, (PBVH_UpdateBB | PBVH_UpdateOriginalBB | PBVH_UpdateRedraw)); + + if (force_balance || pbvh->balance_counter++ == 10) { + pbvh_bmesh_balance_tree(pbvh); + pbvh_bmesh_check_nodes(pbvh); + pbvh->balance_counter = 0; + + totnode = pbvh->totnode; + + for (int i = 0; i < totnode; i++) { + PBVHNode *n = pbvh->nodes + i; + + if (totnode != pbvh->totnode) { +#ifdef PROXY_ADVANCED + BKE_pbvh_free_proxyarray(pbvh, n); +#endif + } + + if (n->flag & PBVH_Leaf) { + /* Recursively split nodes that have gotten too many + * elements */ + pbvh_bmesh_node_limit_ensure(pbvh, i); + } + } + } + + pbvh_print_mem_size(pbvh); +} + +void BKE_pbvh_bmesh_detail_size_set(PBVH *pbvh, float detail_size, float detail_range) { pbvh->bm_max_edge_len = detail_size; - pbvh->bm_min_edge_len = pbvh->bm_max_edge_len * 0.4f; + pbvh->bm_min_edge_len = pbvh->bm_max_edge_len * detail_range; + pbvh->bm_detail_range = detail_range; } void BKE_pbvh_node_mark_topology_update(PBVHNode *node) @@ -2160,240 +3875,2061 @@ void BKE_pbvh_node_mark_topology_update(PBVHNode *node) node->flag |= PBVH_UpdateTopology; } -GSet *BKE_pbvh_bmesh_node_unique_verts(PBVHNode *node) +TableGSet *BKE_pbvh_bmesh_node_unique_verts(PBVHNode *node) { return node->bm_unique_verts; } -GSet *BKE_pbvh_bmesh_node_other_verts(PBVHNode *node) +TableGSet *BKE_pbvh_bmesh_node_other_verts(PBVHNode *node) { + pbvh_bmesh_check_other_verts(node); return node->bm_other_verts; } -GSet *BKE_pbvh_bmesh_node_faces(PBVHNode *node) +struct TableGSet *BKE_pbvh_bmesh_node_faces(PBVHNode *node) { return node->bm_faces; } /****************************** Debugging *****************************/ -#if 0 - -static void pbvh_bmesh_print(PBVH *pbvh) +void BKE_pbvh_update_offsets(PBVH *pbvh, + const int cd_vert_node_offset, + const int cd_face_node_offset, + const int cd_sculpt_vert, + const int cd_face_areas, + const int cd_hide_poly, + const int cd_boundary_flag) { - fprintf(stderr, "\npbvh=%p\n", pbvh); - fprintf(stderr, "bm_face_to_node:\n"); + pbvh->cd_hide_poly = cd_hide_poly; + pbvh->cd_face_node_offset = cd_face_node_offset; + pbvh->cd_vert_node_offset = cd_vert_node_offset; + pbvh->cd_face_area = cd_face_areas; + pbvh->cd_vert_mask_offset = CustomData_get_offset(&pbvh->header.bm->vdata, CD_PAINT_MASK); + pbvh->cd_sculpt_vert = cd_sculpt_vert; + pbvh->cd_faceset_offset = CustomData_get_offset_named( + &pbvh->header.bm->pdata, CD_PROP_INT32, ".sculpt_face_set"); + + pbvh->totuv = CustomData_number_of_layers(&pbvh->header.bm->ldata, CD_PROP_FLOAT2); + pbvh->cd_boundary_flag = cd_boundary_flag; + + if (pbvh->bm_idmap) { + BM_idmap_check_attributes(pbvh->bm_idmap); + } +} + +static void scan_edge_split(BMesh *bm, BMEdge **edges, int totedge) +{ + Vector faces; + Vector newedges; + Vector newverts; + Vector fmap; + Vector emap; + + // remove e from radial list of e->v2 + for (int i = 0; i < totedge; i++) { + BMEdge *e = edges[i]; + + BMDiskLink *prev; + BMDiskLink *next; + + if (e->v2_disk_link.prev->v1 == e->v2) { + prev = &e->v2_disk_link.prev->v1_disk_link; + } + else { + prev = &e->v2_disk_link.prev->v2_disk_link; + } + + if (e->v2_disk_link.next->v1 == e->v2) { + next = &e->v2_disk_link.next->v1_disk_link; + } + else { + next = &e->v2_disk_link.next->v2_disk_link; + } + + prev->next = e->v2_disk_link.next; + next->prev = e->v2_disk_link.prev; + } + + for (int i = 0; i < totedge; i++) { + BMEdge *e = edges[i]; + + BMVert *v2 = static_cast(BLI_mempool_alloc(bm->vpool)); + memset(v2, 0, sizeof(*v2)); + v2->head.data = BLI_mempool_alloc(bm->vdata.pool); + + newverts.append(v2); + + BMEdge *e2 = static_cast(BLI_mempool_alloc(bm->epool)); + newedges.append(e2); + + memset(e2, 0, sizeof(*e2)); + if (bm->edata.pool) { + e2->head.data = BLI_mempool_alloc(bm->edata.pool); + } + + BMLoop *l = e->l; + + if (!l) { + continue; + } + + do { + faces.append(l->f); + BMFace *f2 = static_cast(BLI_mempool_alloc(bm->fpool)); + + faces.append(l->f); + fmap.append(v2); + emap.append(i); + + faces.append(f2); + fmap.append(v2); + emap.append(i); + + memset(f2, 0, sizeof(*f2)); + f2->head.data = BLI_mempool_alloc(bm->ldata.pool); + + BMLoop *prev = nullptr; + BMLoop *l2 = nullptr; + + for (int j = 0; j < 3; j++) { + l2 = (BMLoop *)BLI_mempool_alloc(bm->lpool); + memset(l2, 0, sizeof(*l2)); + l2->head.data = BLI_mempool_alloc(bm->ldata.pool); + + l2->prev = prev; + + if (prev) { + prev->next = l2; + } + else { + f2->l_first = l2; + } + } + + f2->l_first->prev = l2; + l2->next = f2->l_first; + + faces.append(f2); + l = l->radial_next; + } while (l != e->l); + } + + for (int i : IndexRange(newedges.size())) { + BMEdge *e1 = edges[i]; + BMEdge *e2 = newedges[i]; + BMVert *v = newverts[i]; + + add_v3_v3v3(v->co, e1->v1->co, e1->v2->co); + mul_v3_fl(v->co, 0.5f); + + e2->v1 = v; + e2->v2 = e1->v2; + e1->v2 = v; + + v->e = e1; + + e1->v2_disk_link.next = e1->v2_disk_link.prev = e2; + e2->v1_disk_link.next = e2->v1_disk_link.prev = e1; + } + + for (int i : IndexRange(faces.size())) { + BMFace *f1 = faces[i], *f2 = faces[i + 1]; + BMEdge *e1 = edges[emap[i]]; + BMEdge *e2 = newedges[emap[i]]; + BMVert *nv = fmap[i]; + + // make sure first loop points to e1->v1 + BMLoop *l = f1->l_first; + do { + if (l->v == e1->v1) { + break; + } + l = l->next; + } while (l != f1->l_first); + + f1->l_first = l; + + BMLoop *l2 = f2->l_first; + + l2->f = l2->next->f = l2->prev->f = f2; + l2->v = nv; + l2->next->v = l->next->v; + l2->prev->v = l->prev->v; + l2->e = e2; + l2->next->e = l->next->e; + l2->prev->e = l->prev->e; + + l->next->v = nv; + l->next->e = e2; + } +} + +#define MAX_RE_CHILD 3 +struct ReVertNode { + int totvert, totchild; + struct ReVertNode *parent; + struct ReVertNode *children[MAX_RE_CHILD]; + BMVert *verts[]; +}; + +BMesh *BKE_pbvh_reorder_bmesh(PBVH *pbvh) +{ + /*try to compute size of verts per node*/ + int vsize = sizeof(BMVert); + vsize += pbvh->header.bm->vdata.totsize; + + // perhaps aim for l2 cache? + const int limit = 1024; + int leaf_limit = MAX2(limit / vsize, 4); + + BLI_mempool *pool = BLI_mempool_create(sizeof(ReVertNode) + sizeof(void *) * vsize, 0, 8192, 0); + ReVertNode **vnodemap = (ReVertNode **)MEM_calloc_arrayN( + pbvh->header.bm->totvert, sizeof(void *), "vnodemap"); + + printf("leaf_limit: %d\n", leaf_limit); BMIter iter; - BMFace *f; - BM_ITER_MESH (f, &iter, pbvh->header.bm, BM_FACES_OF_MESH) { - fprintf(stderr, " %d -> %d\n", BM_elem_index_get(f), pbvh_bmesh_node_index_from_face(pbvh, f)); - } - - fprintf(stderr, "bm_vert_to_node:\n"); BMVert *v; - BM_ITER_MESH (v, &iter, pbvh->header.bm, BM_FACES_OF_MESH) { - fprintf(stderr, " %d -> %d\n", BM_elem_index_get(v), pbvh_bmesh_node_index_from_vert(pbvh, v)); + const char flag = BM_ELEM_TAG_ALT; + int i = 0; + + BM_ITER_MESH (v, &iter, pbvh->header.bm, BM_VERTS_OF_MESH) { + v->head.hflag &= ~flag; + v->head.index = i++; } - for (int n = 0; n < pbvh->totnode; n++) { - PBVHNode *node = &pbvh->nodes[n]; + Vector stack; + + BM_ITER_MESH (v, &iter, pbvh->header.bm, BM_VERTS_OF_MESH) { + if (v->head.hflag & flag) { + continue; + } + + ReVertNode *node = (ReVertNode *)BLI_mempool_calloc(pool); + + stack.clear(); + stack.append(v); + + v->head.hflag |= flag; + + vnodemap[v->head.index] = node; + node->verts[node->totvert++] = v; + + while (stack.size() > 0) { + BMVert *v2 = stack.pop_last(); + BMEdge *e; + + if (node->totvert >= leaf_limit) { + break; + } + + if (!v2->e) { + continue; + } + + int len = node->totvert; + + e = v2->e; + do { + BMVert *v3 = BM_edge_other_vert(e, v2); + + if (!BM_elem_flag_test(v3, flag) && len < leaf_limit) { + v3->head.hflag |= flag; + + vnodemap[v3->head.index] = node; + node->verts[node->totvert++] = v3; + + len++; + + stack.append(v3); + } + + e = e->v1 == v2 ? e->v1_disk_link.next : e->v2_disk_link.next; + } while (e != v2->e); + } + } + + const int steps = 4; + Vector roots; + + for (int step = 0; step < steps; step++) { + const bool last_step = step == steps - 1; + + BM_ITER_MESH_INDEX (v, &iter, pbvh->header.bm, BM_VERTS_OF_MESH, i) { + BMEdge *e = v->e; + + if (!e) { + continue; + } + + ReVertNode *node = vnodemap[v->head.index]; + if (node->parent) { + continue; + } + + ReVertNode *parent = (ReVertNode *)BLI_mempool_calloc(pool); + parent->children[0] = node; + parent->totchild = 1; + + do { + BMVert *v2 = BM_edge_other_vert(e, v); + + ReVertNode *node2 = vnodemap[v2->head.index]; + + bool ok = node != node2 && !node2->parent; + ok = ok && parent->totchild < MAX_RE_CHILD; + + for (int j = 0; j < parent->totchild; j++) { + if (parent->children[j] == node2) { + ok = false; + break; + } + } + + if (ok) { + parent->children[parent->totchild++] = node2; + node2->parent = parent; + break; + } + + e = e->v1 == v ? e->v1_disk_link.next : e->v2_disk_link.next; + } while (e != v->e); + + if (last_step) { + roots.append(parent); + } + + for (int j = 0; j < parent->totchild; j++) { + parent->children[j]->parent = parent; + } + } + + BM_ITER_MESH_INDEX (v, &iter, pbvh->header.bm, BM_VERTS_OF_MESH, i) { + while (vnodemap[i]->parent) { + vnodemap[i] = vnodemap[i]->parent; + } + } + } + + BLI_mempool_iter loopiter; + BLI_mempool_iternew(pbvh->header.bm->lpool, &loopiter); + BMLoop *l = (BMLoop *)BLI_mempool_iterstep(&loopiter); + BMEdge *e; + BMFace *f; + + for (i = 0; l; l = (BMLoop *)BLI_mempool_iterstep(&loopiter), i++) { + l->head.hflag &= ~flag; + } + BM_ITER_MESH (e, &iter, pbvh->header.bm, BM_EDGES_OF_MESH) { + e->head.hflag &= ~flag; + } + + BM_ITER_MESH (f, &iter, pbvh->header.bm, BM_FACES_OF_MESH) { + f->head.hflag &= ~flag; + } + + int totroot = roots.size(); + Vector nstack; + int vorder = 0, eorder = 0, lorder = 0, forder = 0; + + for (i = 0; i < totroot; i++) { + nstack.clear(); + + ReVertNode *node = roots[i]; + nstack.append(node); + + while (nstack.size() > 0) { + ReVertNode *node2 = nstack.pop_last(); + + if (node2->totchild == 0) { + for (int j = 0; j < node2->totvert; j++) { + v = node2->verts[j]; + +#if 0 + const int cd_vcol = CustomData_get_offset(&pbvh->header.bm->vdata,CD_PROP_COLOR); + + if (cd_vcol >= 0) { + MPropCol *col = BM_ELEM_CD_PTR(node2->verts[j],cd_vcol); + + float r = 0.0f,g = 0.0f,b = 0.0f; + + ReVertNode *parent = node2->parent; + for (int j = 0; parent->parent && j < 2; j++) { + parent = parent->parent; + } + + unsigned int p = (unsigned int)node2->parent; + p = p % 65535; + + unsigned int p2 = (unsigned int)parent; + p2 = p2 % 65535; + + r = ((float)vorder) * 0.01; + g = ((float)p2) / 65535.0f; + b = ((float)p2) / 65535.0f; + + r = cosf(r * 17.2343) * 0.5 + 0.5; + g = cosf(g * 11.2343) * 0.5 + 0.5; + b = cosf(b * 19.2343) * 0.5 + 0.5; + + col->color[0] = r; + col->color[1] = g; + col->color[2] = b; + col->color[3] = 1.0f; + } +#endif + v->head.index = vorder++; + + BMEdge *e = v->e; + if (!e) { + continue; + } + + do { + if (!(e->head.hflag & flag)) { + e->head.hflag |= flag; + e->head.index = eorder++; + } + + if (e->l) { + BMLoop *l = e->l; + + do { + if (!(l->head.hflag & flag)) { + l->head.hflag |= flag; + l->head.index = lorder++; + } + + if (!(l->f->head.hflag & flag)) { + l->f->head.hflag |= flag; + l->f->head.index = forder++; + } + + l = l->radial_next; + } while (l != e->l); + } + e = e->v1 == v ? e->v1_disk_link.next : e->v2_disk_link.next; + } while (e != v->e); + } + } + else { + for (int j = 0; j < node2->totchild; j++) { + nstack.append(node2->children[j]); + } + } + } + } + + uint *vidx, *eidx, *lidx, *fidx; + + vidx = MEM_cnew_array(pbvh->header.bm->totvert, "vorder"); + eidx = MEM_cnew_array(pbvh->header.bm->totedge, "eorder"); + lidx = MEM_cnew_array(pbvh->header.bm->totloop, "lorder"); + fidx = MEM_cnew_array(pbvh->header.bm->totface, "forder"); + + printf("v %d %d\n", vorder, pbvh->header.bm->totvert); + printf("e %d %d\n", eorder, pbvh->header.bm->totedge); + printf("l %d %d\n", lorder, pbvh->header.bm->totloop); + printf("f %d %d\n", forder, pbvh->header.bm->totface); + + BM_ITER_MESH_INDEX (v, &iter, pbvh->header.bm, BM_VERTS_OF_MESH, i) { + vidx[i] = (uint)v->head.index; + } + + BM_ITER_MESH_INDEX (e, &iter, pbvh->header.bm, BM_EDGES_OF_MESH, i) { + eidx[i] = (uint)e->head.index; + } + BM_ITER_MESH_INDEX (f, &iter, pbvh->header.bm, BM_FACES_OF_MESH, i) { + fidx[i] = (uint)f->head.index; + } + + BLI_mempool_iternew(pbvh->header.bm->lpool, &loopiter); + l = (BMLoop *)BLI_mempool_iterstep(&loopiter); + + for (i = 0; l; l = (BMLoop *)BLI_mempool_iterstep(&loopiter), i++) { + // handle orphaned loops + if (!(l->head.hflag & flag)) { + printf("warning in %s: orphaned loop!\n", __func__); + l->head.index = lorder++; + } + + lidx[i] = (uint)l->head.index; + } + + printf("roots: %d\n", (int)roots.size()); + + BM_mesh_remap(pbvh->header.bm, vidx, eidx, fidx, lidx); + + MEM_SAFE_FREE(vidx); + MEM_SAFE_FREE(eidx); + MEM_SAFE_FREE(lidx); + MEM_SAFE_FREE(fidx); + + MEM_SAFE_FREE(nstack); + MEM_SAFE_FREE(roots); + BLI_mempool_destroy(pool); + MEM_SAFE_FREE(stack); + MEM_SAFE_FREE(vnodemap); + + return pbvh->header.bm; +} + +BMesh *BKE_pbvh_reorder_bmesh2(PBVH *pbvh) +{ + if (BKE_pbvh_type(pbvh) != PBVH_BMESH || pbvh->totnode == 0) { + return pbvh->header.bm; + } + + // try to group memory allocations by node + struct TempNodeData { + Vector verts; + Vector edges; + Vector faces; + }; + + Vector nodedata; + nodedata.resize(pbvh->totnode); + + BMIter iter; + int types[3] = {BM_VERTS_OF_MESH, BM_EDGES_OF_MESH, BM_FACES_OF_MESH}; + +#define VISIT_TAG BM_ELEM_TAG + + BM_mesh_elem_index_ensure(pbvh->header.bm, BM_VERT | BM_EDGE | BM_FACE); + BM_mesh_elem_table_ensure(pbvh->header.bm, BM_VERT | BM_EDGE | BM_FACE); + + for (int i = 0; i < 3; i++) { + BMHeader *elem; + + BM_ITER_MESH (elem, &iter, pbvh->header.bm, types[i]) { + elem->hflag &= ~VISIT_TAG; + } + } + + for (int i = 0; i < pbvh->totnode; i++) { + PBVHNode *node = pbvh->nodes + i; + if (!(node->flag & PBVH_Leaf)) { continue; } - GSetIterator gs_iter; - fprintf(stderr, "node %d\n faces:\n", n); - GSET_ITER (gs_iter, node->bm_faces) - fprintf(stderr, " %d\n", BM_elem_index_get((BMFace *)BLI_gsetIterator_getKey(&gs_iter))); - fprintf(stderr, " unique verts:\n"); - GSET_ITER (gs_iter, node->bm_unique_verts) - fprintf(stderr, " %d\n", BM_elem_index_get((BMVert *)BLI_gsetIterator_getKey(&gs_iter))); - fprintf(stderr, " other verts:\n"); - GSET_ITER (gs_iter, node->bm_other_verts) - fprintf(stderr, " %d\n", BM_elem_index_get((BMVert *)BLI_gsetIterator_getKey(&gs_iter))); - } -} + Vector &verts = nodedata[i].verts; + Vector &edges = nodedata[i].edges; + Vector &faces = nodedata[i].faces; -static void print_flag_factors(int flag) -{ - printf("flag=0x%x:\n", flag); - for (int i = 0; i < 32; i++) { - if (flag & (1 << i)) { - printf(" %d (1 << %d)\n", 1 << i, i); - } - } -} -#endif - -#ifdef USE_VERIFY - -static void pbvh_bmesh_verify(PBVH *pbvh) -{ - /* build list of faces & verts to lookup */ - GSet *faces_all = BLI_gset_ptr_new_ex(__func__, pbvh->header.bm->totface); - BMIter iter; - - { - BMFace *f; - BM_ITER_MESH (f, &iter, pbvh->header.bm, BM_FACES_OF_MESH) { - BLI_assert(BM_ELEM_CD_GET_INT(f, pbvh->cd_face_node_offset) != DYNTOPO_NODE_NONE); - BLI_gset_insert(faces_all, f); - } - } - - GSet *verts_all = BLI_gset_ptr_new_ex(__func__, pbvh->header.bm->totvert); - { BMVert *v; - BM_ITER_MESH (v, &iter, pbvh->header.bm, BM_VERTS_OF_MESH) { - if (BM_ELEM_CD_GET_INT(v, pbvh->cd_vert_node_offset) != DYNTOPO_NODE_NONE) { - BLI_gset_insert(verts_all, v); - } - } - } - - /* Check vert/face counts */ - { - int totface = 0, totvert = 0; - for (int i = 0; i < pbvh->totnode; i++) { - PBVHNode *n = &pbvh->nodes[i]; - totface += n->bm_faces ? BLI_gset_len(n->bm_faces) : 0; - totvert += n->bm_unique_verts ? BLI_gset_len(n->bm_unique_verts) : 0; - } - - BLI_assert(totface == BLI_gset_len(faces_all)); - BLI_assert(totvert == BLI_gset_len(verts_all)); - } - - { BMFace *f; - BM_ITER_MESH (f, &iter, pbvh->header.bm, BM_FACES_OF_MESH) { - BMIter bm_iter; - BMVert *v; - PBVHNode *n = pbvh_bmesh_node_lookup(pbvh, f); - /* Check that the face's node is a leaf */ - BLI_assert(n->flag & PBVH_Leaf); - - /* Check that the face's node knows it owns the face */ - BLI_assert(BLI_gset_haskey(n->bm_faces, f)); - - /* Check the face's vertices... */ - BM_ITER_ELEM (v, &bm_iter, f, BM_VERTS_OF_FACE) { - PBVHNode *nv; - - /* Check that the vertex is in the node */ - BLI_assert(BLI_gset_haskey(n->bm_unique_verts, v) ^ BLI_gset_haskey(n->bm_other_verts, v)); - - /* Check that the vertex has a node owner */ - nv = pbvh_bmesh_node_lookup(pbvh, v); - - /* Check that the vertex's node knows it owns the vert */ - BLI_assert(BLI_gset_haskey(nv->bm_unique_verts, v)); - - /* Check that the vertex isn't duplicated as an 'other' vert */ - BLI_assert(!BLI_gset_haskey(nv->bm_other_verts, v)); - } - } - } - - /* Check verts */ - { - BMVert *v; - BM_ITER_MESH (v, &iter, pbvh->header.bm, BM_VERTS_OF_MESH) { - /* vertex isn't tracked */ - if (BM_ELEM_CD_GET_INT(v, pbvh->cd_vert_node_offset) == DYNTOPO_NODE_NONE) { + TGSET_ITER (v, node->bm_unique_verts) { + if (v->head.hflag & VISIT_TAG) { continue; } - PBVHNode *n = pbvh_bmesh_node_lookup(pbvh, v); + v->head.hflag |= VISIT_TAG; + verts.append(v); - /* Check that the vert's node is a leaf */ - BLI_assert(n->flag & PBVH_Leaf); - - /* Check that the vert's node knows it owns the vert */ - BLI_assert(BLI_gset_haskey(n->bm_unique_verts, v)); - - /* Check that the vertex isn't duplicated as an 'other' vert */ - BLI_assert(!BLI_gset_haskey(n->bm_other_verts, v)); - - /* Check that the vert's node also contains one of the vert's - * adjacent faces */ - bool found = false; - BMIter bm_iter; - BMFace *f = nullptr; - BM_ITER_ELEM (f, &bm_iter, v, BM_FACES_OF_VERT) { - if (pbvh_bmesh_node_lookup(pbvh, f) == n) { - found = true; - break; + BMEdge *e = v->e; + do { + if (!(e->head.hflag & VISIT_TAG)) { + e->head.hflag |= VISIT_TAG; + edges.append(e); } - } - BLI_assert(found || f == nullptr); - -# if 1 - /* total freak stuff, check if node exists somewhere else */ - /* Slow */ - for (int i = 0; i < pbvh->totnode; i++) { - PBVHNode *n_other = &pbvh->nodes[i]; - if ((n != n_other) && (n_other->bm_unique_verts)) { - BLI_assert(!BLI_gset_haskey(n_other->bm_unique_verts, v)); - } - } -# endif + e = v == e->v1 ? e->v1_disk_link.next : e->v2_disk_link.next; + } while (e != v->e); } + TGSET_ITER_END; + + TGSET_ITER (f, node->bm_faces) { + if (f->head.hflag & VISIT_TAG) { + continue; + } + + faces.append(f); + f->head.hflag |= VISIT_TAG; + } + TGSET_ITER_END; } -# if 0 - /* check that every vert belongs somewhere */ - /* Slow */ - BM_ITER_MESH (vi, &iter, pbvh->header.bm, BM_VERTS_OF_MESH) { - bool has_unique = false; - for (int i = 0; i < pbvh->totnode; i++) { - PBVHNode *n = &pbvh->nodes[i]; - if ((n->bm_unique_verts != nullptr) && BLI_gset_haskey(n->bm_unique_verts, vi)) { - has_unique = true; - } - } - BLI_assert(has_unique); - vert_count++; - } + BMAllocTemplate templ = {pbvh->header.bm->totvert, + pbvh->header.bm->totedge, + pbvh->header.bm->totloop, + pbvh->header.bm->totface}; + struct BMeshCreateParams params = {0}; - /* If totvert differs from number of verts inside the hash. hash-totvert is checked above. */ - BLI_assert(vert_count == pbvh->header.bm->totvert); -# endif + BMesh *bm2 = BM_mesh_create(&templ, ¶ms); + + CustomData_copy_all_layout(&pbvh->header.bm->vdata, &bm2->vdata); + CustomData_copy_all_layout(&pbvh->header.bm->edata, &bm2->edata); + CustomData_copy_all_layout(&pbvh->header.bm->ldata, &bm2->ldata); + CustomData_copy_all_layout(&pbvh->header.bm->pdata, &bm2->pdata); + + CustomData_bmesh_init_pool(&bm2->vdata, pbvh->header.bm->totvert, BM_VERT); + CustomData_bmesh_init_pool(&bm2->edata, pbvh->header.bm->totedge, BM_EDGE); + CustomData_bmesh_init_pool(&bm2->ldata, pbvh->header.bm->totloop, BM_LOOP); + CustomData_bmesh_init_pool(&bm2->pdata, pbvh->header.bm->totface, BM_FACE); + + Vector verts; + Vector edges; + Vector faces; - /* Check that node elements are recorded in the top level */ for (int i = 0; i < pbvh->totnode; i++) { - PBVHNode *n = &pbvh->nodes[i]; - if (n->flag & PBVH_Leaf) { - GSetIterator gs_iter; + for (int j = 0; j < nodedata[i].verts.size(); j++) { + BMVert *v1 = nodedata[i].verts[j]; + BMVert *v2 = BM_vert_create(bm2, v1->co, nullptr, BM_CREATE_NOP); + BM_elem_attrs_copy_ex(pbvh->header.bm, bm2, v1, v2, 0, 0L); - GSET_ITER (gs_iter, n->bm_faces) { - BMFace *f = BLI_gsetIterator_getKey(&gs_iter); - PBVHNode *n_other = pbvh_bmesh_node_lookup(pbvh, f); - BLI_assert(n == n_other); - BLI_assert(BLI_gset_haskey(faces_all, f)); - } - - GSET_ITER (gs_iter, n->bm_unique_verts) { - BMVert *v = BLI_gsetIterator_getKey(&gs_iter); - PBVHNode *n_other = pbvh_bmesh_node_lookup(pbvh, v); - BLI_assert(!BLI_gset_haskey(n->bm_other_verts, v)); - BLI_assert(n == n_other); - BLI_assert(BLI_gset_haskey(verts_all, v)); - } - - GSET_ITER (gs_iter, n->bm_other_verts) { - BMVert *v = BLI_gsetIterator_getKey(&gs_iter); - /* this happens sometimes and seems harmless */ - // BLI_assert(!BM_vert_face_check(v)); - BLI_assert(BLI_gset_haskey(verts_all, v)); - } + v2->head.index = v1->head.index = verts.size(); + verts.append(v2); } } - BLI_gset_free(faces_all, nullptr); - BLI_gset_free(verts_all, nullptr); + for (int i = 0; i < pbvh->totnode; i++) { + for (int j = 0; j < nodedata[i].edges.size(); j++) { + BMEdge *e1 = nodedata[i].edges[j]; + BMEdge *e2 = BM_edge_create( + bm2, verts[e1->v1->head.index], verts[e1->v2->head.index], nullptr, BM_CREATE_NOP); + BM_elem_attrs_copy_ex(pbvh->header.bm, bm2, e1, e2, 0, 0L); + + e2->head.index = e1->head.index = edges.size(); + edges.append(e2); + } + } + + Vector fvs; + Vector fes; + + for (int i = 0; i < pbvh->totnode; i++) { + for (int j = 0; j < nodedata[i].faces.size(); j++) { + BMFace *f1 = nodedata[i].faces[j]; + + fvs.clear(); + fes.clear(); + + int totloop = 0; + BMLoop *l1 = f1->l_first; + do { + fvs.append(verts[l1->v->head.index]); + fes.append(edges[l1->e->head.index]); + l1 = l1->next; + totloop++; + } while (l1 != f1->l_first); + + BMFace *f2 = BM_face_create(bm2, fvs.data(), fes.data(), totloop, nullptr, BM_CREATE_NOP); + f1->head.index = f2->head.index = faces.size(); + faces.append(f2); + + // CustomData_bmesh_copy_data(&pbvh->header.bm->pdata, &bm2->pdata, f1->head.data, + // &f2->head.data); + BM_elem_attrs_copy_ex(pbvh->header.bm, bm2, f1, f2, 0, 0L); + + BMLoop *l2 = f2->l_first; + do { + BM_elem_attrs_copy_ex(pbvh->header.bm, bm2, l1, l2, 0, 0L); + + l1 = l1->next; + l2 = l2->next; + } while (l2 != f2->l_first); + } + } + + for (int i = 0; i < pbvh->totnode; i++) { + PBVHNode *node = pbvh->nodes + i; + + if (!(node->flag & PBVH_Leaf)) { + continue; + } + + int totunique = node->bm_unique_verts->length; + int totother = node->bm_other_verts->length; + int totface = node->bm_faces->length; + + TableGSet *bm_faces = BLI_table_gset_new_ex("bm_faces", totface); + TableGSet *bm_other_verts = BLI_table_gset_new_ex("bm_other_verts", totunique); + TableGSet *bm_unique_verts = BLI_table_gset_new_ex("bm_unique_verts", totother); + + BMVert *v; + BMFace *f; + + TGSET_ITER (v, node->bm_unique_verts) { + BLI_table_gset_insert(bm_unique_verts, verts[v->head.index]); + } + TGSET_ITER_END; + TGSET_ITER (v, node->bm_other_verts) { + BLI_table_gset_insert(bm_other_verts, verts[v->head.index]); + } + TGSET_ITER_END; + TGSET_ITER (f, node->bm_faces) { + BLI_table_gset_insert(bm_faces, faces[f->head.index]); + } + TGSET_ITER_END; + + BLI_table_gset_free(node->bm_faces, nullptr); + BLI_table_gset_free(node->bm_other_verts, nullptr); + BLI_table_gset_free(node->bm_unique_verts, nullptr); + + node->bm_faces = bm_faces; + node->bm_other_verts = bm_other_verts; + node->bm_unique_verts = bm_unique_verts; + + node->flag |= PBVH_UpdateTris | PBVH_UpdateRedraw; + } + + MEM_SAFE_FREE(fvs); + MEM_SAFE_FREE(fes); + + for (int i = 0; i < pbvh->totnode; i++) { + MEM_SAFE_FREE(nodedata[i].verts); + MEM_SAFE_FREE(nodedata[i].edges); + MEM_SAFE_FREE(nodedata[i].faces); + } + + MEM_SAFE_FREE(verts); + MEM_SAFE_FREE(edges); + MEM_SAFE_FREE(faces); + + BM_mesh_free(pbvh->header.bm); + pbvh->header.bm = bm2; + + return bm2; } +struct SortElem { + BMElem *elem; + int index; + int cd_node_off; +}; + +static int sort_verts_faces(const void *va, const void *vb) +{ + SortElem *a = (SortElem *)va; + SortElem *b = (SortElem *)vb; + int ni1 = BM_ELEM_CD_GET_INT(a->elem, a->cd_node_off); + int ni2 = BM_ELEM_CD_GET_INT(b->elem, b->cd_node_off); + + return ni1 - ni2; +} + +static int sort_edges(const void *va, const void *vb) +{ + SortElem *a = (SortElem *)va; + SortElem *b = (SortElem *)vb; + + BMEdge *e1 = (BMEdge *)a->elem; + BMEdge *e2 = (BMEdge *)b->elem; + + int ni1 = BM_ELEM_CD_GET_INT(e1->v1, a->cd_node_off); + int ni2 = BM_ELEM_CD_GET_INT(e1->v2, a->cd_node_off); + int ni3 = BM_ELEM_CD_GET_INT(e2->v1, b->cd_node_off); + int ni4 = BM_ELEM_CD_GET_INT(e2->v2, b->cd_node_off); + + return (ni1 + ni2) - (ni3 + ni4); +} + +BMesh *BKE_pbvh_reorder_bmesh1(PBVH *pbvh) +{ + BMesh *bm = pbvh->header.bm; + + Vector> save_other_vs; + Vector> save_unique_vs; + Vector> save_fs; + + save_other_vs.resize(pbvh->totnode); + save_unique_vs.resize(pbvh->totnode); + save_fs.resize(pbvh->totnode); + + SortElem *verts = MEM_cnew_array(bm->totvert, __func__); + SortElem *edges = MEM_cnew_array(bm->totedge, __func__); + SortElem *faces = MEM_cnew_array(bm->totface, __func__); + + BMIter iter; + BMVert *v; + BMEdge *e; + BMFace *f; + + int i = 0; + + BM_ITER_MESH_INDEX (v, &iter, bm, BM_VERTS_OF_MESH, i) { + verts[i].elem = (BMElem *)v; + verts[i].cd_node_off = pbvh->cd_vert_node_offset; + verts[i].index = i; + v->head.index = i; + } + BM_ITER_MESH_INDEX (e, &iter, bm, BM_EDGES_OF_MESH, i) { + edges[i].elem = (BMElem *)e; + edges[i].cd_node_off = pbvh->cd_vert_node_offset; + edges[i].index = i; + e->head.index = i; + } + BM_ITER_MESH_INDEX (f, &iter, bm, BM_FACES_OF_MESH, i) { + faces[i].elem = (BMElem *)f; + faces[i].cd_node_off = pbvh->cd_face_node_offset; + faces[i].index = i; + f->head.index = i; + } + + for (i = 0; i < pbvh->totnode; i++) { + Vector other_vs; + Vector unique_vs; + Vector fs; + + PBVHNode *node = pbvh->nodes + i; + if (!(node->flag & PBVH_Leaf)) { + continue; + } + + BMVert *v; + BMFace *f; + + TGSET_ITER (v, node->bm_unique_verts) { + unique_vs.append(v->head.index); + } + TGSET_ITER_END; + TGSET_ITER (v, node->bm_other_verts) { + other_vs.append(v->head.index); + } + TGSET_ITER_END; + TGSET_ITER (f, node->bm_faces) { + fs.append(f->head.index); + } + TGSET_ITER_END; + + save_unique_vs[i] = unique_vs; + save_other_vs[i] = other_vs; + save_fs[i] = fs; + } + + qsort(verts, bm->totvert, sizeof(SortElem), sort_verts_faces); + qsort(edges, bm->totedge, sizeof(SortElem), sort_edges); + qsort(faces, bm->totface, sizeof(SortElem), sort_verts_faces); + + uint *vs = MEM_cnew_array(bm->totvert, __func__); + uint *es = MEM_cnew_array(bm->totedge, __func__); + uint *fs = MEM_cnew_array(bm->totface, __func__); + + for (i = 0; i < bm->totvert; i++) { + vs[i] = (uint)verts[i].index; + verts[i].elem->head.index = verts[i].index; + } + for (i = 0; i < bm->totedge; i++) { + es[i] = (uint)edges[i].index; + edges[i].elem->head.index = edges[i].index; + } + for (i = 0; i < bm->totface; i++) { + fs[i] = (uint)faces[i].index; + faces[i].elem->head.index = faces[i].index; + } + + BM_mesh_remap(bm, vs, es, fs, nullptr); + + // create new mappings + BMVert **mapvs = MEM_cnew_array(bm->totvert, __func__); + BMEdge **mapes = MEM_cnew_array(bm->totedge, __func__); + BMFace **mapfs = MEM_cnew_array(bm->totface, __func__); + + BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) { + mapvs[v->head.index] = v; + } + BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) { + mapes[e->head.index] = e; + } + BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { + mapfs[f->head.index] = f; + } + + // rebuild bm_unique_verts bm_other_verts and bm_faces in pbvh nodes + for (i = 0; i < pbvh->totnode; i++) { + PBVHNode *node = pbvh->nodes + i; + + if (!(node->flag & PBVH_Leaf)) { + continue; + } + + int tot_unique_vs = BLI_table_gset_len(node->bm_unique_verts); + int tot_other_vs = BLI_table_gset_len(node->bm_other_verts); + int tot_fs = BLI_table_gset_len(node->bm_faces); + + BLI_table_gset_free(node->bm_unique_verts, nullptr); + BLI_table_gset_free(node->bm_other_verts, nullptr); + BLI_table_gset_free(node->bm_faces, nullptr); + + node->bm_unique_verts = BLI_table_gset_new("bm_unique_verts"); + node->bm_other_verts = BLI_table_gset_new("bm_other_verts"); + node->bm_faces = BLI_table_gset_new("bm_faces"); + + Vector &unique_vs = save_unique_vs[i]; + Vector &other_vs = save_other_vs[i]; + Vector &fs = save_fs[i]; + + for (int j = 0; j < tot_unique_vs; j++) { + BLI_table_gset_add(node->bm_unique_verts, mapvs[unique_vs[j]]); + } + for (int j = 0; j < tot_other_vs; j++) { + BLI_table_gset_add(node->bm_other_verts, mapvs[other_vs[j]]); + } + + for (int j = 0; j < tot_fs; j++) { + BLI_table_gset_add(node->bm_faces, mapfs[fs[j]]); + } + + node->flag |= PBVH_UpdateTris; + } + + MEM_SAFE_FREE(mapvs); + MEM_SAFE_FREE(mapes); + MEM_SAFE_FREE(mapfs); + + bm->elem_index_dirty |= BM_VERT | BM_EDGE | BM_FACE; + bm->elem_table_dirty |= BM_VERT | BM_EDGE | BM_FACE; + + MEM_SAFE_FREE(vs); + MEM_SAFE_FREE(es); + MEM_SAFE_FREE(fs); + + MEM_SAFE_FREE(verts); + MEM_SAFE_FREE(edges); + MEM_SAFE_FREE(faces); + + return pbvh->header.bm; +} + +// only floats! and 8 byte aligned! +struct CacheParams { + float vchunk, echunk, lchunk, pchunk; + int cluster_steps, cluster_size; +}; + +struct CacheParamDef { + char name[32]; + float defvalue, min, max; +}; + +CacheParamDef pbvh_bmesh_cache_param_def[] = {{"vchunk", 512.0f, 256.0f, 1024.0f * 12.0f}, + {"echunk", 512.0f, 256.0f, 1024.0f * 12.0f}, + {"lchunk", 512.0f, 256.0f, 1024.0f * 12.0f}, + {"pchunk", 512.0f, 256.0f, 1024.0f * 12.0f}, + {"cluster_steps", 512.0f, 1.0f, 256.0f}, + {"cluster_size", 512.0f, 1.0f, 8192.0f * 32.0f}}; + +int pbvh_bmesh_cache_test_totparams() +{ + return sizeof(pbvh_bmesh_cache_param_def) / sizeof(*pbvh_bmesh_cache_param_def); +} + +void pbvh_bmesh_cache_test_default_params(CacheParams *params) +{ + float *fparams = (float *)params; + int totparam = pbvh_bmesh_cache_test_totparams(); + + for (int i = 0; i < totparam; i++) { + fparams[i] = pbvh_bmesh_cache_param_def[i].defvalue; + } +} + +static void *hashco(float fx, float fy, float fz, float fdimen) +{ + double x = (double)fx; + double y = (double)fy; + double z = (double)fz; + double dimen = (double)fdimen; + + return (void *)((intptr_t)(z * dimen * dimen * dimen + y * dimen * dimen + x * dimen)); +} + +struct MeshTest { + float (*v_co)[3]; + float (*v_no)[3]; + int *v_e; + int *v_index; + int *v_flag; + + int *e_v1; + int *e_v2; + int *e_v1_next; + int *e_v2_next; + int *e_l; + int *e_flag; + int *e_index; + + int *l_v; + int *l_e; + int *l_f; + int *l_next; + int *l_prev; + int *l_radial_next; + int *l_radial_prev; + + int *f_l; + int *f_index; + int *f_flag; + + int totvert, totedge, totloop, totface; + MemArena *arena; +}; + +struct ElemHeader { + short type, hflag; + int index; + void *data; +} ElemHeder; + +struct MeshVert2 { + ElemHeader head; + float co[3]; + float no[3]; + int e; +}; + +struct MeshEdge2 { + ElemHeader head; + int v1, v2; + int v1_next, v2_next; + int l; +}; + +struct MeshLoop2 { + ElemHeader head; + int v, e, f, next, prev; + int radial_next, radial_prev; +}; + +struct MeshFace2 { + ElemHeader head; + int l, len; + float no[3]; +}; + +struct MeshTest2 { + MeshVert2 *verts; + MeshEdge2 *edges; + MeshLoop2 *loops; + MeshFace2 *faces; + + int totvert, totedge, totloop, totface; + MemArena *arena; +}; + +static MeshTest2 *meshtest2_from_bm(BMesh *bm) +{ + MeshTest2 *m2 = (MeshTest2 *)MEM_callocN(sizeof(MeshTest2), "MeshTest2"); + m2->arena = BLI_memarena_new(1024 * 32, "MeshTest2 arena"); + + m2->totvert = bm->totvert; + m2->totedge = bm->totedge; + m2->totloop = bm->totloop; + m2->totface = bm->totface; + + BMVert *v; + BMEdge *e; + BMFace *f; + BMIter iter; + + int lindex = 0; + + BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { + BMLoop *l = f->l_first; + do { + l->head.index = lindex++; + } while ((l = l->next) != f->l_first); + } + + m2->totloop = lindex; + + m2->verts = (MeshVert2 *)MEM_calloc_arrayN(bm->totvert, sizeof(MeshVert2), "MeshVert2s"); + m2->edges = (MeshEdge2 *)MEM_calloc_arrayN(bm->totedge, sizeof(MeshEdge2), "MeshEdge2s"); + m2->loops = (MeshLoop2 *)MEM_calloc_arrayN(m2->totloop, sizeof(MeshLoop2), "MeshLoop2s"); + m2->faces = (MeshFace2 *)MEM_calloc_arrayN(bm->totface, sizeof(MeshFace2), "MeshFace2s"); + + bm->elem_index_dirty |= BM_VERT | BM_EDGE | BM_FACE; + BM_mesh_elem_index_ensure(bm, BM_VERT | BM_EDGE | BM_FACE); + + BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) { + const int vi = v->head.index; + + copy_v3_v3(m2->verts[vi].co, v->co); + copy_v3_v3(m2->verts[vi].no, v->no); + + m2->verts[vi].e = v->e ? v->e->head.index : -1; + } + + BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) { + const int ei = e->head.index; + + m2->edges[ei].v1 = e->v1->head.index; + m2->edges[ei].v2 = e->v2->head.index; + m2->edges[ei].l = e->l ? e->l->head.index : -1; + + m2->edges[ei].v1_next = e->v1_disk_link.next->head.index; + m2->edges[ei].v2_next = e->v2_disk_link.next->head.index; + } + + BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { + const int fi = f->head.index; + + m2->faces[fi].len = f->len; + copy_v3_v3(m2->faces[fi].no, f->no); + + BMLoop *l = f->l_first; + do { + int li = l->head.index; + + m2->loops[li].v = l->v->head.index; + m2->loops[li].e = l->e->head.index; + m2->loops[li].f = l->f->head.index; + + m2->loops[li].radial_next = l->radial_next->head.index; + m2->loops[li].radial_prev = l->radial_prev->head.index; + + m2->loops[li].next = l->next->head.index; + m2->loops[li].prev = l->prev->head.index; + } while ((l = l->next) != f->l_first); + } + + return m2; +} + +static void free_meshtest2(MeshTest2 *m2) +{ + BLI_memarena_free(m2->arena); + MEM_freeN(m2); +} + +static MeshTest *meshtest_from_bm(BMesh *bm) +{ + MeshTest *m = MEM_cnew("MeshTest"); + m->arena = BLI_memarena_new(1024 * 32, "m->arena"); + + m->v_co = (float(*)[3])BLI_memarena_alloc(m->arena, bm->totvert * sizeof(*m->v_co)); + m->v_no = (float(*)[3])BLI_memarena_alloc(m->arena, bm->totvert * sizeof(*m->v_no)); + m->v_e = (int *)BLI_memarena_alloc(m->arena, bm->totvert * sizeof(*m->v_e)); + m->v_flag = (int *)BLI_memarena_alloc(m->arena, bm->totvert * sizeof(*m->v_flag)); + m->v_index = (int *)BLI_memarena_alloc(m->arena, bm->totvert * sizeof(*m->v_index)); + + m->e_v1 = (int *)BLI_memarena_alloc(m->arena, bm->totedge * sizeof(*m->e_v1)); + m->e_v1_next = (int *)BLI_memarena_alloc(m->arena, bm->totedge * sizeof(*m->e_v1)); + m->e_v2 = (int *)BLI_memarena_alloc(m->arena, bm->totedge * sizeof(*m->e_v1)); + m->e_v2_next = (int *)BLI_memarena_alloc(m->arena, bm->totedge * sizeof(*m->e_v1)); + m->e_l = (int *)BLI_memarena_alloc(m->arena, bm->totedge * sizeof(*m->e_v1)); + m->e_index = (int *)BLI_memarena_alloc(m->arena, bm->totedge * sizeof(*m->e_v1)); + m->e_flag = (int *)BLI_memarena_alloc(m->arena, bm->totedge * sizeof(*m->e_v1)); + + m->l_v = (int *)BLI_memarena_alloc(m->arena, bm->totloop * sizeof(*m->l_e)); + m->l_e = (int *)BLI_memarena_alloc(m->arena, bm->totloop * sizeof(*m->l_e)); + m->l_f = (int *)BLI_memarena_alloc(m->arena, bm->totloop * sizeof(*m->l_e)); + m->l_next = (int *)BLI_memarena_alloc(m->arena, bm->totloop * sizeof(*m->l_e)); + m->l_prev = (int *)BLI_memarena_alloc(m->arena, bm->totloop * sizeof(*m->l_e)); + m->l_radial_next = (int *)BLI_memarena_alloc(m->arena, bm->totloop * sizeof(*m->l_e)); + m->l_radial_prev = (int *)BLI_memarena_alloc(m->arena, bm->totloop * sizeof(*m->l_e)); + + m->f_l = (int *)BLI_memarena_alloc(m->arena, bm->totface * sizeof(*m->f_l)); + + m->totvert = bm->totvert; + m->totedge = bm->totedge; + m->totface = bm->totface; + m->totloop = bm->totloop; + + BMVert *v; + BMEdge *e; + BMFace *f; + BMIter iter; + + int lindex = 0; + + BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { + BMLoop *l = f->l_first; + do { + l->head.index = lindex++; + } while ((l = l->next) != f->l_first); + } + + bm->elem_index_dirty |= BM_VERT | BM_EDGE | BM_FACE; + BM_mesh_elem_index_ensure(bm, BM_VERT | BM_EDGE | BM_FACE); + + BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) { + copy_v3_v3(m->v_co[v->head.index], v->co); + copy_v3_v3(m->v_no[v->head.index], v->no); + + m->v_e[v->head.index] = v->e ? v->e->head.index : -1; + } + + BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) { + m->e_v1[e->head.index] = e->v1->head.index; + m->e_v2[e->head.index] = e->v2->head.index; + + m->e_v1_next[e->head.index] = e->v1_disk_link.next->head.index; + m->e_v2_next[e->head.index] = e->v2_disk_link.next->head.index; + + m->e_l[e->head.index] = e->l ? e->l->head.index : -1; + } + + BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { + m->f_l[f->head.index] = f->l_first->head.index; + + BMLoop *l = f->l_first; + do { + const int li = l->head.index; + + m->l_e[li] = l->e->head.index; + m->l_v[li] = l->v->head.index; + m->l_f[li] = f->head.index; + m->l_next[li] = l->next->head.index; + m->l_prev[li] = l->prev->head.index; + m->l_radial_next[li] = l->radial_next->head.index; + m->l_radial_prev[li] = l->radial_prev->head.index; + } while ((l = l->next) != f->l_first); + } + + return m; +} + +static void free_meshtest(MeshTest *m) +{ + BLI_memarena_free(m->arena); + MEM_freeN(m); +} + +#define SMOOTH_TEST_STEPS 20 + +double pbvh_bmesh_smooth_test(BMesh *bm, PBVH *pbvh) +{ + double average = 0.0f; + double average_tot = 0.0f; + + for (int iter = 0; iter < SMOOTH_TEST_STEPS; iter++) { + RNG *rng = BLI_rng_new(0); + + double time1 = PIL_check_seconds_timer(); + + for (int step = 0; step < 5; step++) { + for (int i = 0; i < bm->totvert; i++) { + int vi = BLI_rng_get_int(rng) % bm->totvert; + BMVert *v = bm->vtable[vi]; + BMEdge *e = v->e; + float co[3]; + + zero_v3(co); + int tot = 0.0; + + if (!e) { + continue; + } + + do { + BMVert *v2 = BM_edge_other_vert(e, v); + float co2[3]; + + sub_v3_v3v3(co2, v2->co, v->co); + madd_v3_v3fl(co2, v->no, -dot_v3v3(v->no, co2) * 0.9f); + add_v3_v3(co, co2); + + tot++; + + e = e->v1 == v ? e->v1_disk_link.next : e->v2_disk_link.next; + } while (e != v->e); + + if (tot == 0.0) { + continue; + } + + mul_v3_fl(co, 1.0f / (float)tot); + madd_v3_v3fl(v->co, co, 0.5f); + } + } + + double time2 = PIL_check_seconds_timer(); + + double time = time2 - time1; + + printf(" time: %.5f, %d of %d\n", time, iter, SMOOTH_TEST_STEPS); + + // skip first five + if (iter >= 5) { + average += time; + average_tot += 1.0f; + } + + BLI_rng_free(rng); + } + + printf("time: %.5f\n", average / average_tot); + return average / average_tot; +} + +double pbvh_meshtest2_smooth_test(MeshTest2 *m2, PBVH *pbvh) +{ + double average = 0.0f; + double average_tot = 0.0f; + + for (int iter = 0; iter < SMOOTH_TEST_STEPS; iter++) { + RNG *rng = BLI_rng_new(0); + + double time1 = PIL_check_seconds_timer(); + + for (int step = 0; step < 5; step++) { + for (int i = 0; i < m2->totvert; i++) { + int vi = BLI_rng_get_int(rng) % m2->totvert; + MeshVert2 *v = m2->verts + vi; + MeshEdge2 *e = v->e != -1 ? m2->edges + v->e : nullptr; + float co[3]; + + zero_v3(co); + int tot = 0.0; + + if (!e) { + continue; + } + + int enext = -1; + + do { + MeshVert2 *v2 = vi == e->v1 ? m2->verts + e->v2 : m2->verts + e->v1; + float co2[3]; + + sub_v3_v3v3(co2, v2->co, v->co); + madd_v3_v3fl(co2, v->no, -dot_v3v3(v->no, co2) * 0.9f); + add_v3_v3(co, co2); + + tot++; + + enext = e->v1 == vi ? e->v1_next : e->v2_next; + e = m2->edges + enext; + } while (enext != v->e); + + if (tot == 0.0) { + continue; + } + + mul_v3_fl(co, 1.0f / (float)tot); + madd_v3_v3fl(v->co, co, 0.5f); + } + } + + double time2 = PIL_check_seconds_timer(); + + double time = time2 - time1; + + printf(" time: %.5f, %d of %d\n", time, iter, SMOOTH_TEST_STEPS); + + // skip first five + if (iter >= 5) { + average += time; + average_tot += 1.0f; + } + + BLI_rng_free(rng); + } + + printf("time: %.5f\n", average / average_tot); + return average / average_tot; +} + +double pbvh_meshtest_smooth_test(MeshTest *m, PBVH *pbvh) +{ + double average = 0.0f; + double average_tot = 0.0f; + + for (int iter = 0; iter < SMOOTH_TEST_STEPS; iter++) { + RNG *rng = BLI_rng_new(0); + + double time1 = PIL_check_seconds_timer(); + + for (int step = 0; step < 5; step++) { + for (int i = 0; i < m->totvert; i++) { + int vi = BLI_rng_get_int(rng) % m->totvert; + // BMVert *v = bm->vtable[vi]; + const int startei = m->v_e[vi]; + int ei = startei; + + float co[3]; + + zero_v3(co); + int tot = 0.0; + + if (ei == -1) { + continue; + } + + const float *no = m->v_no[vi]; + const float *vco = m->v_co[vi]; + + do { + int ev1 = m->e_v1[ei]; + int ev2 = m->e_v2[ei]; + + int v2i = ev1 == vi ? ev2 : ev1; + + float co2[3]; + + sub_v3_v3v3(co2, m->v_co[v2i], vco); + madd_v3_v3fl(co2, no, -dot_v3v3(no, co2) * 0.9f); + add_v3_v3(co, co2); + + tot++; + + ei = ev1 == vi ? m->e_v1_next[ei] : m->e_v2_next[ei]; + } while (ei != startei); + + if (tot == 0.0) { + continue; + } + + mul_v3_fl(co, 1.0f / (float)tot); + madd_v3_v3fl(m->v_co[vi], co, 0.5f); + } + } + + double time2 = PIL_check_seconds_timer(); + + double time = time2 - time1; + + printf(" time: %.5f, %d of %d\n", time, iter + 1, SMOOTH_TEST_STEPS); + + // skip first five + if (iter >= 5) { + average += time; + average_tot += 1.0f; + } + + BLI_rng_free(rng); + } + + printf("time: %.5f\n", average / average_tot); + return average / average_tot; +} + +/* +test results from blenderartists thread: + +random, cluster, percent, data, data_perc, indices, ind_perc, mem (gb) + [1.22, 1.04, 14.42, 0.73, 67, 0.94, 29, 0], + [1.49, 1.46, 2.35, 1.10, 36, 1.17, 27, 0], + [1.29, 1.13, 14, 0.75, 71.54, 0.89, 45.08 , 0], + [1.58, 1.40, 12.3, 1.09, 44.7, 1.11, 42.42, 16], + [1.53, 1.36, 12.77, 1.08, 41.6, 1.07, 42.91, 0], + [1.56, 1.39, 12.47, 1.09, 42.65, 1.10, 42.15, 16], + [1.22, 1.06, 15.05, 0.75, 63.85, 0.82, 49.67, 32] + +[random] average: 1.41 variange: 0.15 median: 1.49 +[cluster] average: 1.26 variange: 0.17 median: 1.36 +[cluster-percent] average: 11.91 variange: 4.02 median: 12.77 +[data] average: 0.94 variange: 0.17 median: 1.08 +[data-percent] average: 52.48 variange: 13.37 median: 44.70 +[indices] average: 1.01 variange: 0.12 median: 1.07 +[indices-percent] average: 39.75 variange: 7.82 median: 42.42 + +So looks like the biggest gain is from replacing pointers with indices +(which lessens total memory bandwidth). The pure data-oriented version +is a tad bit faster then the index-replacement one, but not by that much. +*/ + +void pbvh_bmesh_cache_test(CacheParams *params, BMesh **r_bm, PBVH **r_pbvh_out) +{ + // build mesh + const int steps = 325; + + printf("== Starting Test ==\n"); + + printf("building test mesh. . .\n"); + + BMAllocTemplate templ = {0, 0, 0, 0}; + BMeshCreateParams bmparams = {}; + + bmparams.id_elem_mask = BM_VERT | BM_EDGE | BM_FACE; + bmparams.id_map = true; + bmparams.create_unique_ids = true; + bmparams.temporary_ids = false; + bmparams.no_reuse_ids = false; + + BMesh *bm = BM_mesh_create(&templ, &bmparams); + + // reinit pools + BLI_mempool_destroy(bm->vpool); + BLI_mempool_destroy(bm->epool); + BLI_mempool_destroy(bm->lpool); + BLI_mempool_destroy(bm->fpool); + + bm->vpool = BLI_mempool_create(sizeof(BMVert), 0, (int)params->vchunk, BLI_MEMPOOL_ALLOW_ITER); + bm->epool = BLI_mempool_create(sizeof(BMEdge), 0, (int)params->echunk, BLI_MEMPOOL_ALLOW_ITER); + bm->lpool = BLI_mempool_create(sizeof(BMLoop), 0, (int)params->lchunk, BLI_MEMPOOL_ALLOW_ITER); + bm->fpool = BLI_mempool_create(sizeof(BMFace), 0, (int)params->pchunk, BLI_MEMPOOL_ALLOW_ITER); + + GHash *vhash = BLI_ghash_ptr_new("vhash"); + + float df = 1.0f / (float)steps; + + int hashdimen = steps * 8; + + BMVert **grid = MEM_cnew_array(steps * steps, "bmvert grid"); + + BM_data_layer_add_named(bm, &bm->vdata, CD_PROP_INT32, "__dyntopo_vert_node"); + BM_data_layer_add_named(bm, &bm->pdata, CD_PROP_INT32, "__dyntopo_face_node"); + BM_data_layer_add_named(bm, &bm->pdata, CD_PROP_INT32, ".sculpt_face_set"); + BM_data_layer_add_named(bm, &bm->pdata, CD_PROP_INT32, ".sculpt_boundary_flags"); + + BM_data_layer_add(bm, &bm->vdata, CD_PAINT_MASK); + BM_data_layer_add(bm, &bm->vdata, CD_DYNTOPO_VERT); + BM_data_layer_add(bm, &bm->vdata, CD_PROP_COLOR); + + BMIdMap *idmap = BM_idmap_new(bm, BM_VERT | BM_EDGE | BM_FACE); + + for (int side = 0; side < 6; side++) { + int axis = side >= 3 ? side - 3 : side; + float sign = side >= 3 ? -1.0f : 1.0f; + + printf("AXIS: %d\n", axis); + + float u = 0.0f; + + for (int i = 0; i < steps; i++, u += df) { + float v = 0.0f; + + for (int j = 0; j < steps; j++, v += df) { + float co[3]; + + co[axis] = u; + co[(axis + 1) % 3] = v; + co[(axis + 2) % 3] = sign; + + // turn into sphere + normalize_v3(co); + + void *key = hashco(co[0], co[1], co[2], hashdimen); + +#if 0 + printf("%.3f %.3f %.3f, key: %p i: %d j: %d df: %f, u: %f v: %f\n", + co[0], + co[1], + co[2], + key, + i, + j, + df, + u, + v); #endif + + void **val = nullptr; + + if (!BLI_ghash_ensure_p(vhash, key, &val)) { + BMVert *v2 = BM_vert_create(bm, co, nullptr, BM_CREATE_NOP); + + *val = (void *)v2; + } + + BMVert *v2 = (BMVert *)*val; + int idx = j * steps + i; + + grid[idx] = v2; + } + } + + for (int i = 0; i < steps - 1; i++) { + for (int j = 0; j < steps - 1; j++) { + int idx1 = j * steps + i; + int idx2 = (j + 1) * steps + i; + int idx3 = (j + 1) * steps + i + 1; + int idx4 = j * steps + i + 1; + + BMVert *v1 = grid[idx1]; + BMVert *v2 = grid[idx2]; + BMVert *v3 = grid[idx3]; + BMVert *v4 = grid[idx4]; + + if (v1 == v2 || v1 == v3 || v1 == v4 || v2 == v3 || v2 == v4 || v3 == v4) { + printf("ERROR!\n"); + continue; + } + + if (sign < 0) { + BMVert *vs[4] = {v4, v3, v2, v1}; + BM_face_create_verts(bm, vs, 4, nullptr, BM_CREATE_NOP, true); + } + else { + BMVert *vs[4] = {v1, v2, v3, v4}; + BM_face_create_verts(bm, vs, 4, nullptr, BM_CREATE_NOP, true); + } + } + } + } + + // randomize + uint *rands[4]; + uint tots[4] = {bm->totvert, bm->totedge, bm->totloop, bm->totface}; + + RNG *rng = BLI_rng_new(0); + + for (uint i = 0; i < 4; i++) { + rands[i] = MEM_cnew_array(tots[i], "rands[i]"); + + for (uint j = 0; j < tots[i]; j++) { + rands[i][j] = j; + } + + for (uint j = 0; j < tots[i] >> 1; j++) { + int j2 = BLI_rng_get_int(rng) % tots[i]; + SWAP(uint, rands[i][j], rands[i][j2]); + } + } + + BM_mesh_remap(bm, rands[0], rands[1], rands[3], rands[2]); + + for (int i = 0; i < 4; i++) { + MEM_SAFE_FREE(rands[i]); + } + + BLI_rng_free(rng); + BLI_ghash_free(vhash, nullptr, nullptr); + MEM_SAFE_FREE(grid); + + printf("totvert: %d, totface: %d, tottri: %d\n", bm->totvert, bm->totface, bm->totface * 2); + + int cd_vert_node = CustomData_get_named_layer_index( + &bm->vdata, CD_PROP_INT32, "__dyntopo_vert_node"); + int cd_face_node = CustomData_get_named_layer_index( + &bm->pdata, CD_PROP_INT32, "__dyntopo_face_node"); + int cd_face_area = CustomData_get_named_layer_index( + &bm->pdata, CD_PROP_FLOAT2, "__dyntopo_face_areas"); + int cd_boundary_flag = CustomData_get_named_layer_index( + &bm->vdata, CD_PROP_INT32, ".sculpt_boundary_flags"); + + cd_vert_node = bm->vdata.layers[cd_vert_node].offset; + cd_boundary_flag = bm->vdata.layers[cd_boundary_flag].offset; + cd_face_node = bm->pdata.layers[cd_face_node].offset; + cd_face_area = bm->pdata.layers[cd_face_area].offset; + + const int cd_sculpt_vert = CustomData_get_offset(&bm->vdata, CD_DYNTOPO_VERT); + BMLog *bmlog = BM_log_create(bm, idmap); + + PBVH *pbvh = BKE_pbvh_new(PBVH_BMESH); + + bm->elem_table_dirty |= BM_VERT | BM_EDGE | BM_FACE; + bm->elem_index_dirty |= BM_VERT | BM_EDGE | BM_FACE; + BM_mesh_elem_table_ensure(bm, BM_VERT | BM_FACE); + BM_mesh_elem_index_ensure(bm, BM_VERT | BM_EDGE | BM_FACE); + + BKE_pbvh_build_bmesh(pbvh, + nullptr, + bm, + false, + bmlog, + idmap, + cd_vert_node, + cd_face_node, + cd_sculpt_vert, + cd_face_area, + cd_boundary_flag, + false, + true); + + int loop_size = sizeof(BMLoop) - sizeof(void *) * 4; + + size_t s1 = 0, s2 = 0, s3 = 0; + s1 = sizeof(BMVert) * (size_t)bm->totvert + sizeof(BMEdge) * (size_t)bm->totedge + + sizeof(BMLoop) * (size_t)bm->totloop + sizeof(BMFace) * (size_t)bm->totface; + s2 = sizeof(MeshVert2) * (size_t)bm->totvert + sizeof(MeshEdge2) * (size_t)bm->totedge + + sizeof(MeshLoop2) * (size_t)bm->totloop + sizeof(MeshFace2) * (size_t)bm->totface; + s3 = (size_t)loop_size * (size_t)bm->totvert + sizeof(BMEdge) * (size_t)bm->totedge + + sizeof(BMLoop) * (size_t)bm->totloop + sizeof(BMFace) * (size_t)bm->totface; + + double times[4]; + const char *names[4]; + + int cd_overhead = 0; + CustomData *cdatas[4] = {&bm->vdata, &bm->edata, &bm->ldata, &bm->pdata}; + int ctots[4] = {bm->totvert, bm->totedge, bm->totloop, bm->totface}; + for (int i = 0; i < 4; i++) { + cd_overhead += cdatas[i]->totsize * ctots[i]; + } + + s1 += cd_overhead; + s2 += cd_overhead; + + printf(" bmesh mem size: %.2fmb %.2fmb\n", + (float)s1 / 1024.0f / 1024.0f, + (float)s3 / 1024.0f / 1024.0f); + printf("meshtest2 mem size: %.2fmb\n", (float)s2 / 1024.0f / 1024.0f); + + printf("= BMesh random order\n"); + times[0] = pbvh_bmesh_smooth_test(bm, pbvh); + names[0] = "random order"; + + BMesh *bm2 = BKE_pbvh_reorder_bmesh(pbvh); + + printf("= BMesh vertex cluster order\n"); + + bm2->elem_table_dirty |= BM_VERT | BM_EDGE | BM_FACE; + bm2->elem_index_dirty |= BM_VERT | BM_EDGE | BM_FACE; + BM_mesh_elem_table_ensure(bm2, BM_VERT | BM_FACE); + BM_mesh_elem_index_ensure(bm2, BM_VERT | BM_EDGE | BM_FACE); + + times[1] = pbvh_bmesh_smooth_test(bm2, pbvh); + names[1] = "vertex cluser"; + + printf("= Pure data-oriented (struct of arrays)\n"); + MeshTest *m = meshtest_from_bm(bm2); + + times[2] = pbvh_meshtest_smooth_test(m, pbvh); + names[2] = "data-oriented"; + + free_meshtest(m); + + printf("= Object-oriented but with integer indices instead of pointers\n"); + MeshTest2 *m2 = meshtest2_from_bm(bm2); + + times[3] = pbvh_meshtest2_smooth_test(m2, pbvh); + names[3] = "integer indices"; + + free_meshtest2(m2); + + if (bm2 && bm2 != bm) { + BM_mesh_free(bm2); + } + + if (r_bm) { + *r_bm = bm; + } + else { + BM_mesh_free(bm); + } + + if (r_pbvh_out) { + *r_pbvh_out = pbvh; + } + else { + BKE_pbvh_free(pbvh); + } + + printf("\n== Times ==\n"); + + for (int i = 0; i < ARRAY_SIZE(times); i++) { + if (i > 0) { + double perc = (times[0] / times[i] - 1.0) * 100.0; + printf(" %s : %.2f (%.2f%% improvement)\n", names[i], times[i], perc); + } + else { + printf(" %s : %.2f\n", names[i], times[i]); + } + } + + printf("== Test Finished ==\n"); +} + +#include "BLI_smallhash.h" + +static void hash_test() +{ + const int count = 1024 * 1024 * 4; + + int *data = MEM_cnew_array(count, "test data"); + + TableGSet *gs = BLI_table_gset_new("test"); + GHash *gh = BLI_ghash_ptr_new("test"); + SmallHash sh; + + BLI_smallhash_init(&sh); + RNG *rng = BLI_rng_new(0); + + for (int i = 0; i < count; i++) { + data[i] = i; + } + + printf("== creation: table_gset ==\n"); + double t = PIL_check_seconds_timer(); + + for (int i = 0; i < count; i++) { + int ri = BLI_rng_get_int(rng) % count; + int *ptr = (int *)POINTER_FROM_INT(data[ri]); + + BLI_table_gset_add(gs, ptr); + } + printf(" %.3f\n", PIL_check_seconds_timer() - t); + + printf("== creation: ghash ==\n"); + t = PIL_check_seconds_timer(); + + for (int i = 0; i < count; i++) { + int ri = BLI_rng_get_int(rng) % count; + int *ptr = (int *)POINTER_FROM_INT(data[ri]); + + BLI_ghash_insert(gh, ptr, POINTER_FROM_INT(i)); + } + printf(" %.3f\n", PIL_check_seconds_timer() - t); + + printf("== creation: small hash ==\n"); + t = PIL_check_seconds_timer(); + + for (int i = 0; i < count; i++) { + int ri = BLI_rng_get_int(rng) % count; + int *ptr = (int *)POINTER_FROM_INT(data[ri]); + + BLI_smallhash_insert(&sh, (uintptr_t)ptr, POINTER_FROM_INT(i)); + } + printf(" %.3f\n", PIL_check_seconds_timer() - t); + + printf("== lookup: g hash ==\n"); + t = PIL_check_seconds_timer(); + + for (int i = 0; i < count; i++) { + int ri = BLI_rng_get_int(rng) % count; + int *ptr = (int *)POINTER_FROM_INT(data[ri]); + + BLI_ghash_lookup(gh, ptr); + } + printf(" %.3f\n", PIL_check_seconds_timer() - t); + + printf("== lookup: small hash ==\n"); + t = PIL_check_seconds_timer(); + + for (int i = 0; i < count; i++) { + int ri = BLI_rng_get_int(rng) % count; + int *ptr = (int *)POINTER_FROM_INT(data[ri]); + + BLI_smallhash_lookup(&sh, (uintptr_t)ptr); + } + printf(" %.3f\n", PIL_check_seconds_timer() - t); + + BLI_rng_free(rng); + BLI_ghash_free(gh, nullptr, nullptr); + BLI_smallhash_release(&sh); + BLI_table_gset_free(gs, nullptr); + + MEM_freeN(data); +} + +void pbvh_bmesh_do_cache_test() +{ + for (int i = 0; i < 15; i++) { + printf("\n\n====== %d of %d =====\n", i + 1, 15); + hash_test(); + } + // pbvh_bmesh_cache_test_default_params(¶ms); + // pbvh_bmesh_cache_test(¶ms, &bm, &pbvh); +} + +/* saves all bmesh references to internal indices, to be restored later */ +void BKE_pbvh_bmesh_save_indices(PBVH *pbvh) +{ + BM_mesh_elem_index_ensure(pbvh->header.bm, BM_VERT | BM_EDGE | BM_FACE); + + BMFace *f; + BMVert *v; + BMIter iter; + + int j = 0; + + BM_ITER_MESH (f, &iter, pbvh->header.bm, BM_FACES_OF_MESH) { + BMLoop *l = f->l_first; + + do { + l->head.index = j++; + } while ((l = l->next) != f->l_first); + } + + for (int i = 0; i < pbvh->totnode; i++) { + PBVHNode *node = pbvh->nodes + i; + + if (!(node->flag & PBVH_Leaf)) { + continue; + } + + node->prim_indices = (int *)MEM_calloc_arrayN(1 + node->bm_faces->length + + node->bm_unique_verts->length, + sizeof(int), + "saved bmesh indices"); + + int j = 0; + + TGSET_ITER (f, node->bm_faces) { + node->prim_indices[j++] = f->head.index; + } + TGSET_ITER_END; + + // flag start of vertex array + node->prim_indices[j++] = -1; + + TGSET_ITER (v, node->bm_unique_verts) { + node->prim_indices[j++] = v->head.index; + } + TGSET_ITER_END; + + node->totprim = j; + + // don't try to save invalid triangulation + if (node->flag & PBVH_UpdateTris) { + continue; + } + + // now do tribufs + for (j = 0; j < node->tot_tri_buffers + 1; j++) { + PBVHTriBuf *tribuf = j == node->tot_tri_buffers ? node->tribuf : node->tri_buffers + j; + + if (!tribuf) { + break; + } + + for (int k = 0; k < tribuf->totvert; k++) { + if (i == 35 && k == 12) { + printf("eek!"); + } + + tribuf->verts[k].i = ((BMVert *)tribuf->verts[k].i)->head.index; + } + + for (int k = 0; k < tribuf->totloop; k++) { + tribuf->loops[k] = ((BMLoop *)tribuf->loops[k])->head.index; + } + + for (int k = 0; k < tribuf->tottri; k++) { + PBVHTri *tri = tribuf->tris + k; + BMFace *f = (BMFace *)tri->f.i; + + tri->f.i = f->head.index; + + for (int l = 0; l < 3; l++) { + tri->l[l] = ((BMLoop *)tri->l[l])->head.index; + } + } + } + } +} + +/* restore bmesh references from previously indices saved by BKE_pbvh_bmesh_save_indices */ +void BKE_pbvh_bmesh_from_saved_indices(PBVH *pbvh) +{ + BM_mesh_elem_table_ensure(pbvh->header.bm, BM_VERT | BM_EDGE | BM_FACE); + BM_mesh_elem_index_ensure(pbvh->header.bm, BM_VERT | BM_EDGE | BM_FACE); + + Vector ltable; + + BMFace *f; + BMIter iter; + int i = 0; + + BM_ITER_MESH (f, &iter, pbvh->header.bm, BM_FACES_OF_MESH) { + BMLoop *l = f->l_first; + + do { + l->head.index = i++; + ltable.append(l); + } while ((l = l->next) != f->l_first); + } + + for (int i = 0; i < pbvh->totnode; i++) { + PBVHNode *node = pbvh->nodes + i; + + if (!(node->flag & PBVH_Leaf)) { + continue; + } + + BLI_table_gset_free(node->bm_unique_verts, nullptr); + BLI_table_gset_free(node->bm_faces, nullptr); + + if (node->bm_other_verts) { + BLI_table_gset_free(node->bm_other_verts, nullptr); + } + + node->bm_other_verts = BLI_table_gset_new("bm_other_verts"); + node->flag |= PBVH_UpdateOtherVerts; + + node->bm_faces = BLI_table_gset_new("bm_faces"); + node->bm_unique_verts = BLI_table_gset_new("bm_verts"); + + int j = 0; + int *data = node->prim_indices; + + while (data[j] != -1 && j < node->totprim) { + BMFace *f = pbvh->header.bm->ftable[data[j]]; + BM_ELEM_CD_SET_INT(f, pbvh->cd_face_node_offset, i); + + BLI_table_gset_insert(node->bm_faces, f); + j++; + } + + j++; + + while (j < node->totprim) { + if (data[j] < 0 || data[j] >= pbvh->header.bm->totvert) { + printf("%s: bad vertex at index %d!\n", __func__, data[j]); + continue; + } + BMVert *v = pbvh->header.bm->vtable[data[j]]; + BM_ELEM_CD_SET_INT(v, pbvh->cd_vert_node_offset, i); + + BLI_table_gset_insert(node->bm_unique_verts, v); + j++; + } + + MEM_SAFE_FREE(node->prim_indices); + + // don't try to load invalid triangulation + if (node->flag & PBVH_UpdateTris) { + continue; + } + + for (j = 0; j < node->tot_tri_buffers + 1; j++) { + PBVHTriBuf *tribuf = j == node->tot_tri_buffers ? node->tribuf : node->tri_buffers + j; + + if (!tribuf) { + break; + } + + for (int k = 0; k < tribuf->totvert; k++) { + tribuf->verts[k].i = (intptr_t)pbvh->header.bm->vtable[tribuf->verts[k].i]; + } + + for (int k = 0; k < tribuf->totloop; k++) { + tribuf->loops[k] = (uintptr_t)ltable[tribuf->loops[k]]; + } + + for (int k = 0; k < tribuf->tottri; k++) { + PBVHTri *tri = tribuf->tris + k; + + for (int l = 0; l < 3; l++) { + tri->l[l] = (uintptr_t)ltable[tri->l[l]]; + } + + tri->f.i = (intptr_t)pbvh->header.bm->ftable[tri->f.i]; + } + } + + node->prim_indices = nullptr; + node->totprim = 0; + } +} + +static void pbvh_bmesh_fetch_cdrefs(PBVH *pbvh) +{ + BMesh *bm = pbvh->header.bm; + + int idx = CustomData_get_named_layer_index( + &bm->vdata, CD_PROP_INT32, SCULPT_ATTRIBUTE_NAME(dyntopo_node_id_vertex)); + pbvh->cd_vert_node_offset = bm->vdata.layers[idx].offset; + + idx = CustomData_get_named_layer_index( + &bm->pdata, CD_PROP_INT32, SCULPT_ATTRIBUTE_NAME(dyntopo_node_id_face)); + pbvh->cd_face_node_offset = bm->pdata.layers[idx].offset; + + idx = CustomData_get_named_layer_index( + &bm->pdata, CD_PROP_FLOAT2, SCULPT_ATTRIBUTE_NAME(face_areas)); + pbvh->cd_face_area = bm->pdata.layers[idx].offset; + + pbvh->cd_vert_mask_offset = CustomData_get_offset(&bm->vdata, CD_PAINT_MASK); + pbvh->cd_faceset_offset = CustomData_get_offset_named( + &pbvh->header.bm->pdata, CD_PROP_INT32, ".sculpt_face_set"); + pbvh->cd_sculpt_vert = CustomData_get_offset(&bm->vdata, CD_DYNTOPO_VERT); +} + +void BKE_pbvh_bmesh_set_toolflags(PBVH *pbvh, bool use_toolflags) +{ + if (use_toolflags == pbvh->header.bm->use_toolflags) { + return; + } + + // BKE_pbvh_bmesh_save_indices(pbvh); + BM_mesh_toolflags_set(pbvh->header.bm, use_toolflags); + + // customdata layout might've changed + pbvh_bmesh_fetch_cdrefs(pbvh); + + // BKE_pbvh_bmesh_from_saved_indices(pbvh); +} diff --git a/source/blender/blenkernel/intern/pbvh_cache_test_main.c b/source/blender/blenkernel/intern/pbvh_cache_test_main.c new file mode 100644 index 00000000000..a01ab8a4453 --- /dev/null +++ b/source/blender/blenkernel/intern/pbvh_cache_test_main.c @@ -0,0 +1,31 @@ +#include "MEM_guardedalloc.h" + +#include "BLI_compiler_compat.h" +#include "BLI_math.h" +#include "BLI_utildefines.h" + +#include "BKE_customdata.h" +#include "BKE_pbvh.h" + +#include "DNA_customdata_types.h" +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" + +#include "bmesh.h" +#include "pbvh_intern.h" + +#include +#include + +#include + +// void pbvh_bmesh_do_cache_test(void); + +int main(int argc, char **argv) +{ + printf("argc: %d\n", argc); + + // pbvh_bmesh_do_cache_test(); + + return 0; +} diff --git a/source/blender/blenkernel/intern/pbvh_colors.cc b/source/blender/blenkernel/intern/pbvh_colors.cc index 2ec4139d8c4..917ca6c55d0 100644 --- a/source/blender/blenkernel/intern/pbvh_colors.cc +++ b/source/blender/blenkernel/intern/pbvh_colors.cc @@ -89,7 +89,7 @@ static void pbvh_vertex_color_get(const PBVH &pbvh, PBVHVertRef vertex, float r_ int index = vertex.i; if (pbvh.color_domain == ATTR_DOMAIN_CORNER) { - const MeshElemMap &melem = pbvh.pmap[index]; + const MeshElemMap &melem = pbvh.pmap->pmap[index]; int count = 0; zero_v4(r_color); @@ -124,7 +124,7 @@ static void pbvh_vertex_color_set(PBVH &pbvh, PBVHVertRef vertex, const float co int index = vertex.i; if (pbvh.color_domain == ATTR_DOMAIN_CORNER) { - const MeshElemMap &melem = pbvh.pmap[index]; + const MeshElemMap &melem = pbvh.pmap->pmap[index]; for (const int i_poly : Span(melem.indices, melem.count)) { const MPoly &mp = pbvh.mpoly[i_poly]; @@ -143,6 +143,82 @@ static void pbvh_vertex_color_set(PBVH &pbvh, PBVHVertRef vertex, const float co } } +template +static void pbvh_vertex_color_get_bmesh(const PBVH &pbvh, PBVHVertRef vertex, float r_color[4]) +{ + BMVert *v = reinterpret_cast(vertex.i); + + if (pbvh.color_domain == ATTR_DOMAIN_CORNER) { + float4 color = {}; + int count = 0; + + int cd_color = pbvh.cd_vcol_offset; + + BMEdge *e = v->e; + do { + BMLoop *l = e->l; + + if (!l) { + continue; + } + + if (l->v != v) { + l = l->next; + } + + float4 color2; + T vcol = *reinterpret_cast BM_ELEM_CD_GET_VOID_P(l, cd_color); + + to_float(vcol, color2); + + color += color2; + count++; + } while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e); + + if (count > 0) { + color *= 1.0f / (float)count; + } + + copy_v4_v4(r_color, color); + } + else { + to_float(*static_cast(BM_ELEM_CD_GET_VOID_P(v, pbvh.cd_vcol_offset)), r_color); + } +} + +template +static void pbvh_vertex_color_set_bmesh(PBVH &pbvh, PBVHVertRef vertex, const float color[4]) +{ + int index = vertex.i; + + BMVert *v = reinterpret_cast(vertex.i); + + if (pbvh.color_domain == ATTR_DOMAIN_CORNER) { + BMEdge *e = v->e; + int cd_color = pbvh.cd_vcol_offset; + + do { + BMLoop *l = e->l; + do { + T *l_color; + + if (l->v != v) { + l_color = reinterpret_cast(BM_ELEM_CD_GET_VOID_P(l->next, cd_color)); + } + else { + l_color = reinterpret_cast(BM_ELEM_CD_GET_VOID_P(l, cd_color)); + } + + from_float(color, *l_color); + } while ((l = l->radial_next) != e->l); + + } while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e); + } + else { + from_float(color, *reinterpret_cast(BM_ELEM_CD_GET_VOID_P(v, pbvh.cd_vcol_offset))); + } +} + } // namespace blender::bke extern "C" { @@ -150,7 +226,13 @@ void BKE_pbvh_vertex_color_get(const PBVH *pbvh, PBVHVertRef vertex, float r_col { blender::bke::to_static_color_type(eCustomDataType(pbvh->color_layer->type), [&](auto dummy) { using T = decltype(dummy); - blender::bke::pbvh_vertex_color_get(*pbvh, vertex, r_color); + + if (BKE_pbvh_type(pbvh) == PBVH_BMESH) { + blender::bke::pbvh_vertex_color_get_bmesh(*pbvh, vertex, r_color); + } + else { + blender::bke::pbvh_vertex_color_get(*pbvh, vertex, r_color); + } }); } @@ -158,7 +240,13 @@ void BKE_pbvh_vertex_color_set(PBVH *pbvh, PBVHVertRef vertex, const float color { blender::bke::to_static_color_type(eCustomDataType(pbvh->color_layer->type), [&](auto dummy) { using T = decltype(dummy); - blender::bke::pbvh_vertex_color_set(*pbvh, vertex, color); + + if (BKE_pbvh_type(pbvh) == PBVH_BMESH) { + blender::bke::pbvh_vertex_color_set_bmesh(*pbvh, vertex, color); + } + else { + blender::bke::pbvh_vertex_color_set(*pbvh, vertex, color); + } }); } diff --git a/source/blender/blenkernel/intern/pbvh_displacement.c b/source/blender/blenkernel/intern/pbvh_displacement.c new file mode 100644 index 00000000000..61550c2095a --- /dev/null +++ b/source/blender/blenkernel/intern/pbvh_displacement.c @@ -0,0 +1,266 @@ +#if 0 +# include "MEM_guardedalloc.h" + +# include "BLI_alloca.h" +# include "BLI_array.h" +# include "BLI_compiler_attrs.h" +# include "BLI_compiler_compat.h" +# include "BLI_ghash.h" +# include "BLI_linklist.h" +# include "BLI_math.h" +# include "BLI_memarena.h" +# include "BLI_memblock.h" +# include "BLI_mempool.h" +# include "BLI_utildefines.h" + +# include "BLI_hash.h" + +# include "BKE_context.h" +# include "BKE_global.h" +# include "BKE_image.h" +# include "BKE_mesh.h" +# include "BKE_multires.h" +# include "BKE_object.h" +# include "BKE_pbvh.h" +# include "BKE_scene.h" + +# include "BLI_bitmap.h" +# include "DNA_customdata_types.h" +# include "DNA_image_types.h" +# include "DNA_material_types.h" +# include "DNA_mesh_types.h" +# include "DNA_meshdata_types.h" +# include "DNA_object_types.h" +# include "DNA_scene_types.h" + +# include "pbvh_intern.hh" + +# include "bmesh.h" + +void *BKE_pbvh_get_tex_settings(PBVH *pbvh, PBVHNode *node, TexLayerRef vdm) +{ + return NULL; // implement me! +} + +void *BKE_pbvh_get_tex_data(PBVH *pbvh, PBVHNode *node, TexPointRef vdm) +{ + return NULL; // implement me! +} + +typedef union TexelKey { + struct { + int idx; // index in image + int co_key; // used to differentiate same texel used in different 3d points in space + } key; + intptr_t i; +} TexelKey; + +BLI_INLINE int calc_co_key(const float *co) +{ + const int mul = 65535; + const int mask = 65535; + + int x = (int)co[0] + (((int)co[0] * mul) & mask); + int y = (int)co[0] + (((int)co[0] * mul) & mask); + int z = (int)co[0] + (((int)co[0] * mul) & mask); + + return BLI_hash_int_3d(x, y, z); +} + +typedef struct TextureVDMSettings { + ImageUser ima_user; + ID *image; + bool tangent_space; + + char uv_layer[64]; + + // used by texture_vdm_get_points + // BLI_bitmap *texel_used_map; + GSet *texel_used_map; + + int width, height; + bool invalid; +} TextureVDMSettings; + +typedef struct TextureNodeData { + TexPointRef *point_ids; + float (*point_cos)[3]; + float (*point_uvs)[2]; + float **point_cos_ptrs; + int totpoint; +} TextureNodeData; + +void texture_vdm_begin(PBVH *pbvh, PBVHNode *node, TexLayerRef vdm) +{ + TextureVDMSettings *settings = BKE_pbvh_get_tex_settings(pbvh, node, vdm); + + if (!settings->image) { + return; + } + + Image *image = (Image *)settings->image; + + int w = 0, h = 0; + BKE_image_get_size(image, &settings->ima_user, &w, &h); + + // Image *image = settings->image. + settings->width = w; + settings->height = h; + // settings->texel_used_map = BLI_BITMAP_NEW(w * h, "texel_used_map"); + settings->texel_used_map = BLI_gset_ptr_new("texel_used_map"); +} + +void texture_vdm_build_points(PBVH *pbvh, PBVHNode *node, TexLayerRef vdm) +{ + TextureVDMSettings *settings = BKE_pbvh_get_tex_settings(pbvh, node, vdm); + TextureNodeData *data = BKE_pbvh_get_tex_data(pbvh, node, vdm); + + int idx; + + if (!settings->uv_layer[0]) { + idx = CustomData_get_layer_index(&pbvh->bm->ldata, CD_MLOOPUV); + } + else { + idx = CustomData_get_named_layer_index(&pbvh->bm->ldata, CD_MLOOPUV, settings->uv_layer); + } + + if (idx < 0) { + settings->invalid = true; + return; + } + + const int cd_uv = pbvh->bm->ldata.layers[idx].offset; + const int w = settings->width, h = settings->height; + + float **point_cos_ptrs = NULL; + float *uvs = NULL; + float *cos = NULL; + TexPointRef *ids = NULL; + + BLI_array_declare(point_cos_ptrs); + BLI_array_declare(uvs); + BLI_array_declare(cos); + BLI_array_declare(ids); + + for (int i = 0; i < node->tribuf->tottri; i++) { + PBVHTri *tri = node->tribuf->tris + i; + + BMLoop *ls[3] = {(BMLoop *)tri->l[0], (BMLoop *)tri->l[1], (BMLoop *)tri->l[2]}; + float min[2] = {FLT_MAX, FLT_MAX}, max[2] = {FLT_MIN, FLT_MIN}; + + float tricos[3][3]; + + copy_v3_v3(tricos[0], ls[0]->v->co); + copy_v3_v3(tricos[1], ls[1]->v->co); + copy_v3_v3(tricos[2], ls[2]->v->co); + + for (int j = 0; j < 3; j++) { + MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(ls[j], cd_uv); + minmax_v2v2_v2(min, max, luv->uv); + } + + int dw = (int)((max[0] - min[0]) * (float)w + 0.000001f); + int dh = (int)((max[1] - min[1]) * (float)h + 0.000001f); + + dw = MAX2(dw, 1); + dh = MAX2(dh, 1); + + float du = (max[0] - min[0]) / dw; + float dv = (max[1] - min[1]) / dh; + + float u = min[0], v = min[1]; + for (int y = 0; y < dh; y++, v += dv) { + u = min[0]; + + for (int x = 0; x < dw; x++, u += du) { + int idx = y * w + x; + float co[3]; + + interp_barycentric_tri_v3(tricos, u, v, co); + + TexelKey key; + key.key.idx = idx; + key.key.co_key = calc_co_key(co); + + if (BLI_gset_haskey(settings->texel_used_map, (void *)key.i)) { + continue; + } + + BLI_gset_insert(settings->texel_used_map, (void *)key.i); + + BLI_array_append(uvs, u); + BLI_array_append(uvs, v); + + BLI_array_append(cos, co[0]); + BLI_array_append(cos, co[1]); + BLI_array_append(cos, co[2]); + BLI_array_append(ids, (TexPointRef)key.i); + } + } + } + + settings->invalid = false; + MEM_SAFE_FREE(data->point_cos); + MEM_SAFE_FREE(data->point_ids); + MEM_SAFE_FREE(data->point_uvs); + MEM_SAFE_FREE(data->point_cos_ptrs); + + int totpoint = BLI_array_len(ids); + + data->totpoint = totpoint; + + data->point_cos_ptrs = MEM_malloc_arrayN(totpoint, sizeof(void *), "point_cos_ptrs"); + + // dumb casting trick + union { + float *cos; + float (*cos3)[3]; + } castcos; + + union { + float *uvs; + float (*uvs2)[2]; + } castuvs; + + castcos.cos = cos; + castuvs.uvs = uvs; + + data->point_cos = castcos.cos3; + data->point_ids = ids; + data->point_uvs = castuvs.uvs2; + + for (int i = 0; i < totpoint; i++) { + data->point_cos_ptrs[i] = cos + i * 3; + } +} + +void texture_vdm_get_points(PBVH *pbvh, + PBVHNode *node, + TexLayerRef vdm, + TexPointRef **r_ids, + float ***r_cos, + float ***r_nos, + int *r_totpoint) +{ + TextureVDMSettings *settings = BKE_pbvh_get_tex_settings(pbvh, node, vdm); + TextureNodeData *data = BKE_pbvh_get_tex_data(pbvh, node, vdm); + + if (r_totpoint) { + *r_totpoint = data->totpoint; + } + + if (r_cos) { + *r_cos = data->point_cos_ptrs; + } + + if (r_ids) { + *r_ids = data->point_ids; + } +} + +static SculptDisplacementDef texture_vdm = { + .type = SCULPT_TEXTURE_UV, + .settings_size = sizeof(TextureNodeData), + .getPointsFromNode = texture_vdm_get_points, +}; +#endif diff --git a/source/blender/blenkernel/intern/pbvh_intern.hh b/source/blender/blenkernel/intern/pbvh_intern.hh index d41eeb0b70b..3b80a3e3c6c 100644 --- a/source/blender/blenkernel/intern/pbvh_intern.hh +++ b/source/blender/blenkernel/intern/pbvh_intern.hh @@ -2,13 +2,34 @@ #pragma once +#include "BLI_compiler_compat.h" +#include "BLI_ghash.h" + +#include "DNA_customdata_types.h" +#include "DNA_material_types.h" + +#include "BKE_attribute.h" +#include "BKE_paint.h" /* for SCULPT_BOUNDARY_NEEDS_UPDATE */ +#include "BKE_pbvh.h" + +#include "../../bmesh/intern/bmesh_idmap.h" +#include "bmesh.h" + +#define PBVH_STACK_FIXED_DEPTH 100 + +struct PBVHGPUFormat; + /** \file * \ingroup bke */ +struct MSculptVert; +struct CustomData; +struct PBVHTriBuf; struct PBVHGPUFormat; struct MLoop; struct MLoopTri; +struct BMIdMap; struct MPoly; struct MeshElemMap; @@ -35,6 +56,9 @@ struct PBVHNode { /* For internal nodes, the offset of the children in the PBVH * 'nodes' array. */ int children_offset; + int subtree_tottri; + + int depth; /* List of primitives for this node. Semantics depends on * PBVH type: @@ -100,43 +124,49 @@ struct PBVHNode { int proxy_count; PBVHProxyNode *proxies; - /* Dyntopo */ - /* GSet of pointers to the BMFaces used by this node. - * NOTE: PBVH_BMESH only. Faces are always triangles - * (dynamic topology forcibly triangulates the mesh). + * NOTE: PBVH_BMESH only. */ - GSet *bm_faces; - GSet *bm_unique_verts; - GSet *bm_other_verts; + TableGSet *bm_faces; + TableGSet *bm_unique_verts; + TableGSet *bm_other_verts; - /* Deprecated. Stores original coordinates of triangles. */ - float (*bm_orco)[3]; - int (*bm_ortri)[3]; - BMVert **bm_orvert; - int bm_tot_ortri; + PBVHTriBuf *tribuf; // all triangles + PBVHTriBuf *tri_buffers; // tribuffers, one per material used + int tot_tri_buffers; - /* Used to store the brush color during a stroke and composite it over the original color */ - PBVHColorBufferNode color_buffer; + int updategen; + +#ifdef PROXY_ADVANCED + ProxyVertArray proxyverts; +#endif PBVHPixelsNode pixels; /* Used to flash colors of updated node bounding boxes in * debug draw mode (when G.debug_value / bpy.app.debug_value is 889). */ int debug_draw_gen; + int id; }; -enum PBVHFlags { +typedef enum { PBVH_DYNTOPO_SMOOTH_SHADING = 1, -}; -ENUM_OPERATORS(PBVHFlags, PBVH_DYNTOPO_SMOOTH_SHADING); + PBVH_FAST_DRAW = 2, // hides facesets/masks and forces smooth to save GPU bandwidth + PBVH_IGNORE_UVS = 4 +} PBVHFlags; +ENUM_OPERATORS(PBVHFlags, PBVH_IGNORE_UVS); typedef struct PBVHBMeshLog PBVHBMeshLog; +struct DMFlagMat; struct PBVH { PBVHPublic header; PBVHFlags flags; + int idgen; + + bool dyntopo_stop; + PBVHNode *nodes; int node_mem_count, totnode; @@ -151,6 +181,9 @@ struct PBVH { int depth_limit; /* Mesh data */ + struct MeshElemMap *vemap; + SculptPMap *pmap; + Mesh *mesh; /* NOTE: Normals are not `const` because they can be updated for drawing by sculpt code. */ @@ -163,6 +196,7 @@ struct PBVH { const int *material_indices; const MLoop *mloop; const MLoopTri *looptri; + struct MSculptVert *msculptverts; CustomData *vdata; CustomData *ldata; CustomData *pdata; @@ -170,12 +204,14 @@ struct PBVH { int face_sets_color_seed; int face_sets_color_default; int *face_sets; + float *face_areas; /* float2 vector, double buffered to avoid thread contention */ + int face_area_i; /* Grid Data */ CCGKey gridkey; CCGElem **grids; void **gridfaces; - const DMFlagMat *grid_flag_mats; + const struct DMFlagMat *grid_flag_mats; int totgrid; BLI_bitmap **grid_hidden; @@ -194,16 +230,46 @@ struct PBVH { /* Dynamic topology */ float bm_max_edge_len; float bm_min_edge_len; + float bm_detail_range; + struct BMIdMap *bm_idmap; + + int cd_sculpt_vert; int cd_vert_node_offset; int cd_face_node_offset; + int cd_vert_mask_offset; + int cd_faceset_offset; + int cd_face_area; + int cd_vcol_offset; + int cd_hide_poly; + + int totuv; float planes[6][4]; int num_planes; - BMLog *bm_log; - SubdivCCG *subdiv_ccg; + int symmetry; + int boundary_symmetry; - const MeshElemMap *pmap; + BMLog *bm_log; + struct SubdivCCG *subdiv_ccg; + + bool flat_vcol_shading; + bool need_full_render; // used by pbvh drawing for PBVH_BMESH + + int balance_counter; + int stroke_id; // used to keep origdata up to date in PBVH_BMESH + + bool is_cached; + + /* This data is for validating cached PBVHs; + * it is not guaranteed to be valid in any way! */ + struct { + CustomData vdata, edata, ldata, pdata; + int totvert, totedge, totloop, totpoly; + struct BMesh *bm; + } cached_data; + + bool invalid; CustomDataLayer *color_layer; eAttrDomain color_domain; @@ -213,6 +279,8 @@ struct PBVH { /* Used by DynTopo to invalidate the draw cache. */ bool draw_cache_invalid; + int *boundary_flags; + int cd_boundary_flag; PBVHGPUFormat *vbo_id; PBVHPixels pixels; @@ -234,6 +302,9 @@ void BBC_update_centroid(BBC *bbc); * Return 0, 1, or 2 to indicate the widest axis of the bounding box. */ int BB_widest_axis(const BB *bb); +void BB_intersect(BB *r_out, BB *a, BB *b); +float BB_volume(const BB *bb); + void pbvh_grow_nodes(PBVH *bvh, int totnode); bool ray_face_intersection_quad(const float ray_start[3], IsectRayPrecalc *isect_precalc, @@ -267,24 +338,124 @@ bool ray_face_nearest_tri(const float ray_start[3], void pbvh_update_BB_redraw(PBVH *bvh, PBVHNode **nodes, int totnode, int flag); +bool ray_face_intersection_depth_tri(const float ray_start[3], + struct IsectRayPrecalc *isect_precalc, + const float t0[3], + const float t1[3], + const float t2[3], + float *r_depth, + float *r_back_depth, + int *hit_count); /* pbvh_bmesh.cc */ -bool pbvh_bmesh_node_raycast(PBVHNode *node, +/* pbvh_bmesh.c */ +bool pbvh_bmesh_node_raycast(PBVH *pbvh, + PBVHNode *node, const float ray_start[3], const float ray_normal[3], - IsectRayPrecalc *isect_precalc, - float *dist, + struct IsectRayPrecalc *isect_precalc, + int *hit_count, + float *depth, + float *back_depth, bool use_original, - PBVHVertRef *r_active_vertex, - float *r_face_normal); -bool pbvh_bmesh_node_nearest_to_ray(PBVHNode *node, + PBVHVertRef *r_active_vertex_index, + PBVHFaceRef *r_active_face_index, + float *r_face_normal, + int stroke_id); + +bool pbvh_bmesh_node_nearest_to_ray(PBVH *pbvh, + PBVHNode *node, const float ray_start[3], const float ray_normal[3], float *depth, float *dist_sq, - bool use_original); + bool use_original, + int stroke_id); -void pbvh_bmesh_normals_update(PBVHNode **nodes, int totnode); +void pbvh_bmesh_normals_update(PBVH *pbvh, PBVHNode **nodes, int totnode); + +void pbvh_update_free_all_draw_buffers(PBVH *pbvh, PBVHNode *node); + +BLI_INLINE int pbvh_bmesh_node_index_from_vert(PBVH *pbvh, const BMVert *key) +{ + const int node_index = BM_ELEM_CD_GET_INT((const BMElem *)key, pbvh->cd_vert_node_offset); + BLI_assert(node_index != DYNTOPO_NODE_NONE); + BLI_assert(node_index << pbvh->totnode); + return node_index; +} + +BLI_INLINE int pbvh_bmesh_node_index_from_face(PBVH *pbvh, const BMFace *key) +{ + const int node_index = BM_ELEM_CD_GET_INT((const BMElem *)key, pbvh->cd_face_node_offset); + BLI_assert(node_index != DYNTOPO_NODE_NONE); + BLI_assert(node_index < pbvh->totnode); + return node_index; +} + +BLI_INLINE PBVHNode *pbvh_bmesh_node_from_vert(PBVH *pbvh, const BMVert *key) +{ + int ni = pbvh_bmesh_node_index_from_vert(pbvh, key); + + return ni >= 0 ? pbvh->nodes + ni : NULL; + // return &pbvh->nodes[pbvh_bmesh_node_index_from_vert(pbvh, key)]; +} + +BLI_INLINE PBVHNode *pbvh_bmesh_node_from_face(PBVH *pbvh, const BMFace *key) +{ + int ni = pbvh_bmesh_node_index_from_face(pbvh, key); + + return ni >= 0 ? pbvh->nodes + ni : NULL; + // return &pbvh->nodes[pbvh_bmesh_node_index_from_face(pbvh, key)]; +} + +bool pbvh_bmesh_node_limit_ensure(PBVH *pbvh, int node_index); + +//#define PBVH_BMESH_DEBUG + +#ifdef PBVH_BMESH_DEBUG +void pbvh_bmesh_check_nodes(PBVH *pbvh); +void pbvh_bmesh_check_nodes_simple(PBVH *pbvh); +#else +# define pbvh_bmesh_check_nodes(pbvh) +# define pbvh_bmesh_check_nodes_simple(pbvh) +#endif + +void bke_pbvh_insert_face_finalize(PBVH *pbvh, BMFace *f, const int ni); +void bke_pbvh_insert_face(PBVH *pbvh, struct BMFace *f); +void bke_pbvh_update_vert_boundary(int cd_sculpt_vert, + int cd_faceset_offset, + int cd_vert_node_offset, + int cd_face_node_offset, + int cd_vcol_offset, + int cd_boundary_flags, + BMVert *v, + int bound_symmetry, + const struct CustomData *ldata, + const int totuv); + +BLI_INLINE bool pbvh_check_vert_boundary(PBVH *pbvh, struct BMVert *v) +{ + int *flag = (int *)BM_ELEM_CD_GET_VOID_P(v, pbvh->cd_boundary_flag); + + if (*flag & SCULPT_BOUNDARY_NEEDS_UPDATE) { + bke_pbvh_update_vert_boundary(pbvh->cd_sculpt_vert, + pbvh->cd_faceset_offset, + pbvh->cd_vert_node_offset, + pbvh->cd_face_node_offset, + pbvh->cd_vcol_offset, + pbvh->cd_boundary_flag, + v, + pbvh->boundary_symmetry, + &pbvh->header.bm->ldata, + pbvh->flags & PBVH_IGNORE_UVS ? 0 : pbvh->totuv); + return true; + } + + return false; +} + +void pbvh_bmesh_check_other_verts(PBVHNode *node); +//#define DEFRAGMENT_MEMORY /* pbvh_pixels.hh */ @@ -292,3 +463,22 @@ void pbvh_node_pixels_free(PBVHNode *node); void pbvh_pixels_free(PBVH *pbvh); void pbvh_pixels_free_brush_test(PBVHNode *node); void pbvh_free_draw_buffers(PBVH *pbvh, PBVHNode *node); + +BLI_INLINE bool pbvh_boundary_needs_update_bmesh(PBVH *pbvh, BMVert *v) +{ + int *flags = (int *)BM_ELEM_CD_GET_VOID_P(v, pbvh->cd_boundary_flag); + + return *flags & SCULPT_BOUNDARY_NEEDS_UPDATE; +} + +BLI_INLINE void pbvh_boundary_update_bmesh(PBVH *pbvh, BMVert *v) +{ + if (pbvh->cd_boundary_flag == -1) { + printf("%s: error!\n", __func__); + return; + } + + int *flags = (int *)BM_ELEM_CD_GET_VOID_P(v, pbvh->cd_boundary_flag); + *flags |= SCULPT_BOUNDARY_NEEDS_UPDATE; +} + diff --git a/source/blender/blenkernel/intern/scene.cc b/source/blender/blenkernel/intern/scene.cc index 6a020343f55..494917c2aaf 100644 --- a/source/blender/blenkernel/intern/scene.cc +++ b/source/blender/blenkernel/intern/scene.cc @@ -672,6 +672,7 @@ static void scene_foreach_toolsettings(LibraryForeachIDData *data, do_undo_restore, reader, &toolsett_old->sculpt->paint)); + BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS_IDSUPER(data, toolsett->sculpt->gravity_object, do_undo_restore, @@ -680,6 +681,7 @@ static void scene_foreach_toolsettings(LibraryForeachIDData *data, toolsett_old->sculpt->gravity_object, IDWALK_CB_NOP); } + if (toolsett->uvsculpt) { BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS_FUNCTION_CALL( data, @@ -972,6 +974,7 @@ static void scene_blend_write(BlendWriter *writer, ID *id, const void *id_addres } if (tos->sculpt) { BLO_write_struct(writer, Sculpt, tos->sculpt); + if (tos->sculpt->automasking_cavity_curve) { BKE_curvemapping_blend_write(writer, tos->sculpt->automasking_cavity_curve); } @@ -1797,6 +1800,7 @@ ToolSettings *BKE_toolsettings_copy(ToolSettings *toolsettings, const int flag) } if (ts->sculpt) { ts->sculpt = static_cast(MEM_dupallocN(ts->sculpt)); + BKE_paint_copy(&ts->sculpt->paint, &ts->sculpt->paint, flag); if (ts->sculpt->automasking_cavity_curve) { @@ -1875,6 +1879,7 @@ void BKE_toolsettings_free(ToolSettings *toolsettings) } BKE_paint_free(&toolsettings->sculpt->paint); + MEM_freeN(toolsettings->sculpt); } if (toolsettings->uvsculpt) { @@ -2613,7 +2618,7 @@ static void prepare_mesh_for_viewport_render(Main *bmain, BMeshToMeshParams params{}; params.calc_object_remap = true; params.update_shapekey_indices = true; - BM_mesh_bm_to_me(bmain, bm, mesh, ¶ms); + BM_mesh_bm_to_me(bmain, nullptr, bm, mesh, ¶ms); DEG_id_tag_update(&mesh->id, 0); } } diff --git a/source/blender/blenkernel/intern/subdiv_ccg.cc b/source/blender/blenkernel/intern/subdiv_ccg.cc index bf09be444b1..93207552ba0 100644 --- a/source/blender/blenkernel/intern/subdiv_ccg.cc +++ b/source/blender/blenkernel/intern/subdiv_ccg.cc @@ -17,6 +17,8 @@ #include "BLI_math_vector.h" #include "BLI_task.h" +#include "multires_inline.h" + #include "BKE_DerivedMesh.h" #include "BKE_ccg.h" #include "BKE_global.h" @@ -2100,4 +2102,38 @@ void BKE_subdiv_ccg_eval_limit_point(const SubdivCCG *subdiv_ccg, BKE_subdiv_eval_limit_point(subdiv, ptex_face_index, u, v, r_point); } +void BKE_subdiv_ccg_eval_limit_point_and_derivatives(const SubdivCCG *subdiv_ccg, + const SubdivCCGCoord *coord, + float r_point[3], + float r_dPdu[3], + float r_dPdv[3]) +{ + Subdiv *subdiv = subdiv_ccg->subdiv; + int ptex_face_index; + float u, v; + subdiv_ccg_coord_to_ptex_coord(subdiv_ccg, coord, &ptex_face_index, &u, &v); + BKE_subdiv_eval_limit_point_and_derivatives( + subdiv, ptex_face_index, u, v, r_point, r_dPdu, r_dPdv); +} + +void BKE_subdiv_ccg_get_tangent_matrix(const SubdivCCG *subdiv_ccg, + const SubdivCCGCoord *coord, + float mat[3][3], + float r_point[3]) +{ + int ptex_face_index; + float u, v; + float du[3], dv[3]; + + const int face_index = BKE_subdiv_ccg_grid_to_face_index(subdiv_ccg, coord->grid_index); + const SubdivCCGFace *faces = subdiv_ccg->faces; + const SubdivCCGFace *face = &faces[face_index]; + const float corner = coord->grid_index - face->start_grid_index; + + subdiv_ccg_coord_to_ptex_coord(subdiv_ccg, coord, &ptex_face_index, &u, &v); + + BKE_subdiv_ccg_eval_limit_point_and_derivatives(subdiv_ccg, coord, r_point, du, dv); + BKE_multires_construct_tangent_matrix(mat, du, dv, corner); +} + /** \} */ diff --git a/source/blender/blenkernel/intern/subdiv_displacement_multires.cc b/source/blender/blenkernel/intern/subdiv_displacement_multires.cc index b076a4b7ad5..9ca3498e27b 100644 --- a/source/blender/blenkernel/intern/subdiv_displacement_multires.cc +++ b/source/blender/blenkernel/intern/subdiv_displacement_multires.cc @@ -351,6 +351,7 @@ static void eval_displacement(SubdivDisplacement *displacement, BKE_multires_construct_tangent_matrix(tangent_matrix, dPdu, dPdv, corner_of_quad); mul_v3_m3v3(r_D, tangent_matrix, tangent_D); /* For the boundary points of grid average two (or all) neighbor grids. */ + const int corner = displacement_get_face_corner(data, ptex_face_index, u, v); average_displacement(displacement, average_with, ptex_face_index, corner, grid_u, grid_v, r_D); } diff --git a/source/blender/blenkernel/intern/subdiv_mesh.cc b/source/blender/blenkernel/intern/subdiv_mesh.cc index ee9e02883c6..3ed4caafd26 100644 --- a/source/blender/blenkernel/intern/subdiv_mesh.cc +++ b/source/blender/blenkernel/intern/subdiv_mesh.cc @@ -1103,9 +1103,11 @@ static void subdiv_mesh_vertex_of_loose_edge(const SubdivForeachContext *foreach if (ctx->vert_to_edge_map == nullptr) { BKE_mesh_vert_edge_map_create(&ctx->vert_to_edge_map, &ctx->vert_to_edge_buffer, + ctx->coarse_positions, ctx->coarse_edges, coarse_mesh->totvert, - ctx->coarse_mesh->totedge); + ctx->coarse_mesh->totedge, + false); } } diff --git a/source/blender/blenlib/BLI_asan.h b/source/blender/blenlib/BLI_asan.h index deeb00b8ce1..bdf6471254b 100644 --- a/source/blender/blenlib/BLI_asan.h +++ b/source/blender/blenlib/BLI_asan.h @@ -7,7 +7,7 @@ # define __has_feature(x) 0 #endif -#if (defined(__SANITIZE_ADDRESS__) || __has_feature(address_sanitizer)) && !defined(_MSC_VER) +#if (defined(__SANITIZE_ADDRESS__) || __has_feature(address_sanitizer)) # include "sanitizer/asan_interface.h" #else /* Ensure return value is used. Just using UNUSED_VARS results in a warning. */ @@ -26,3 +26,59 @@ * Mark a region of memory as usable again. */ #define BLI_asan_unpoison(addr, size) ASAN_UNPOISON_MEMORY_REGION(addr, size) + +#if (defined(__SANITIZE_ADDRESS__) || __has_feature(address_sanitizer)) +# include "MEM_guardedalloc.h" + +static void *BLI_asan_safe_malloc(size_t size, const char *tag) +{ + // align size at 16 bytes + size += 15 - (size & 15); + + // add safe padding + size += 32; + + void *ret = MEM_mallocN(size, tag); + + int *iptr = (int *)ret; + *iptr = (int)size; + + char *ptr = (char *)ret; + + ptr[4] = 't'; + ptr[5] = 'a'; + ptr[6] = 'g'; + ptr[7] = '1'; + + BLI_asan_poison(ptr, 16); + BLI_asan_poison(ptr + size - 16, 16); + + ret = (void *)(ptr + 16); + + return ret; +} + +static void BLI_asan_safe_free(void *mem) +{ + if (!mem) { + return; + } + + mem = (void *)(((char *)mem) - 16); + + BLI_asan_unpoison(mem, 16); + int *iptr = (int *)mem; + volatile char *ptr = (char *)mem; + + if (ptr[4] != 't' || ptr[5] != 'a' || ptr[6] != 'g' || ptr[7] != '1') { + BLI_asan_poison(mem, 16); + *ptr = 1; // deliberately trigger asan fault + } + + BLI_asan_unpoison(ptr + iptr[0] - 16, 16); + MEM_freeN((void *)ptr); +} +#else +# define BLI_asan_safe_malloc(size, tag) MEM_mallocN(size, tag) +# define BLI_asan_safe_free(mem) MEM_SAFE_FREE(mem) +#endif diff --git a/source/blender/blenlib/BLI_assert.h b/source/blender/blenlib/BLI_assert.h index 4292620e462..c0cd31155f0 100644 --- a/source/blender/blenlib/BLI_assert.h +++ b/source/blender/blenlib/BLI_assert.h @@ -58,8 +58,8 @@ void _BLI_assert_unreachable_print(const char *file, int line, const char *funct NULL)) : \ NULL) #else -# define BLI_assert(a) ((void)0) -# define BLI_assert_msg(a, msg) ((void)0) +# define BLI_assert(a) ((void *)0) +# define BLI_assert_msg(a, msg) ((void *)0) #endif #if defined(__cplusplus) diff --git a/source/blender/blenlib/BLI_compiler_attrs.h b/source/blender/blenlib/BLI_compiler_attrs.h index f0566e0b3e2..e1add6638ce 100644 --- a/source/blender/blenlib/BLI_compiler_attrs.h +++ b/source/blender/blenlib/BLI_compiler_attrs.h @@ -17,6 +17,12 @@ /* hint to mark function arguments expected to be non-null * if no arguments are given to the macro, all of pointer * arguments would be expected to be non-null + * + * ONE-INDEXED! + * + * Example: + * + * void func(void *a, void *b, void *b) ATTR_NONNULL(1, 2, 3) */ #ifdef __GNUC__ # define ATTR_NONNULL(args...) __attribute__((nonnull(args))) @@ -83,6 +89,17 @@ # define ATTR_ALIGN(x) __attribute__((aligned(x))) #endif +/* Disable optimization for a function (for debugging use only!)*/ +#ifdef __clang__ +# define ATTR_NO_OPT __attribute__((optnone)) +#elif defined(_MSC_VER) +# define ATTR_NO_OPT __pragma(optimize("", off)) +#elif defined(__GNUC__) +# define ATTR_NO_OPT __attribute__((optimize("O0"))) +#else +# define ATTR_NO_OPT +#endif + /* Alignment directive */ #ifdef _WIN64 # define ALIGN_STRUCT __declspec(align(64)) diff --git a/source/blender/blenlib/BLI_even_spline.hh b/source/blender/blenlib/BLI_even_spline.hh new file mode 100644 index 00000000000..074ef3da657 --- /dev/null +++ b/source/blender/blenlib/BLI_even_spline.hh @@ -0,0 +1,733 @@ +#pragma once + +#include "BLI_compiler_attrs.h" +#include "BLI_compiler_compat.h" + +#include "BLI_math.h" +#include "BLI_math_vector_types.hh" +#include "BLI_vector.hh" + +#include +#include + +//#define FINITE_DIFF + +/* + * Arc length parameterized spline library. + */ +namespace blender { +/* + Abstract curve interface. + +template class Curve { + using Vector = VecBase; + + public: + Float length; + + Vector evaluate(Float s); + Vector derivative(Float s); + Vector derivative2(Float s); + Float curvature(float s); + + void update(); +}; +*/ + +/* +comment: Reduce algebra script; + +on factor; +off period; + +procedure bez(a, b); + a + (b - a) * t; + +lin := bez(k1, k2); +quad := bez(lin, sub(k2=k3, k1=k2, lin)); + +cubic := bez(quad, sub(k3=k4, k2=k3, k1=k2, quad)); +dcubic := df(cubic, t); +icubic := int(cubic, t); + +x1 := 0; +y1 := 0; + +dx := sub(k1=x1, k2=x2, k3=x3, k4=x4, dcubic); +dy := sub(k1=y1, k2=y2, k3=y3, k4=y4, dcubic); +darc := sqrt(dx**2 + dy**2); + +arcstep := darc*dt + 0.5*df(darc, t)*dt*dt; + +d2x := df(dx / darc, t); +d2y := df(dy / darc, t); + +gentran +begin +declare << +x1,x2,x3,x4 : float; +y1,y2,y3,y4 : float; +dt,t : float; +>>; +return eval(arcstep) +end; + +on fort; +cubic; +dcubic; +icubic; +arcstep; +d2x; +d2y; +off fort; + +*/ +template class CubicBezier { + using Vector = VecBase; + + public: + Vector ps[4]; + + CubicBezier(Vector a, Vector b, Vector c, Vector d) + { + ps[0] = a; + ps[1] = b; + ps[2] = c; + ps[3] = d; + + deleted = false; + _arc_to_t = new Float[table_size]; + } + + ~CubicBezier() + { + deleted = true; + + if (_arc_to_t) { + delete[] _arc_to_t; + _arc_to_t = nullptr; + } + } + + CubicBezier() + { + deleted = false; + _arc_to_t = new Float[table_size]; + } + + CubicBezier(const CubicBezier &b) + { + _arc_to_t = new Float[table_size]; + *this = b; + deleted = false; + } + + CubicBezier &operator=(const CubicBezier &b) + { + ps[0] = b.ps[0]; + ps[1] = b.ps[1]; + ps[2] = b.ps[2]; + ps[3] = b.ps[3]; + + length = b.length; + + if (!_arc_to_t) { + _arc_to_t = new Float[table_size]; + } + + if (b._arc_to_t) { + for (int i = 0; i < table_size; i++) { + _arc_to_t[i] = b._arc_to_t[i]; + } + } + + return *this; + } + +#if 1 + CubicBezier(CubicBezier &&b) + { + *this = b; + } + + CubicBezier &operator=(CubicBezier &&b) + { + ps[0] = b.ps[0]; + ps[1] = b.ps[1]; + ps[2] = b.ps[2]; + ps[3] = b.ps[3]; + + length = b.length; + + if (b._arc_to_t) { + _arc_to_t = std::move(b._arc_to_t); + b._arc_to_t = nullptr; + } + else { + _arc_to_t = new Float[table_size]; + } + + return *this; + } +#endif + + Float length; + + void update() + { + Float t = 0.0, dt = 1.0 / (Float)table_size; + Float s = 0.0; + + if (!_arc_to_t) { + _arc_to_t = new Float[table_size]; + } + + auto table = _arc_to_t; + + for (int i = 0; i < table_size; i++) { + table[i] = -1.0; + } + + length = 0.0; + + for (int i = 0; i < table_size; i++, t += dt) { + Float dlen = 0.0; + for (int j = 0; j < axes; j++) { + float dv = dcubic(ps[0][j], ps[1][j], ps[2][j], ps[3][j], t); + + dlen += dv * dv; + } + + dlen = sqrt(dlen) * dt; + + length += dlen; + } + + const int samples = table_size; + dt = 1.0 / (Float)samples; + + t = 0.0; + s = 0.0; + + for (int i = 0; i < samples; i++, t += dt) { + Float dlen = 0.0; + for (int j = 0; j < axes; j++) { + float dv = dcubic(ps[0][j], ps[1][j], ps[2][j], ps[3][j], t); + + dlen += dv * dv; + } + + dlen = sqrt(dlen) * dt; + + int j = (int)((s / length) * (Float)table_size * 0.999999); + j = min_ii(j, table_size - 1); + + table[j] = t; + + s += dlen; + } + + table[0] = 0.0; + table[table_size - 1] = 1.0; + +#if 1 + /* Interpolate gaps in table. */ + for (int i = 0; i < table_size - 1; i++) { + if (table[i] == -1.0 || table[i + 1] != -1.0) { + continue; + } + + int i1 = i; + int i2 = i + 1; + + while (table[i2] == -1.0) { + i2++; + } + + int start = table[i1]; + int end = table[i2]; + Float dt2 = 1.0 / (i2 - i1); + + for (int j = i1 + 1; j < i2; j++) { + Float factor = (Float)(j - i1) * dt2; + table[j] = start + (end - start) * factor; + } + + i = i2 - 1; + } + +# if 0 + for (int i = 0; i < table_size; i++) { + printf("%.3f ", table[i]); + } + printf("\n\n"); +# endif +#endif + } + + inline Vector evaluate(Float s) + { + Float t = arc_to_t(s); + Vector r; + + for (int i = 0; i < axes; i++) { + r[i] = cubic(ps[0][i], ps[1][i], ps[2][i], ps[3][i], t); + } + + return r; + } + + Vector derivative(Float s, bool exact = true) + { + Float t = arc_to_t(s); + Vector r; + + for (int i = 0; i < axes; i++) { + r[i] = dcubic(ps[0][i], ps[1][i], ps[2][i], ps[3][i], t) * length; + } + + /* Real arc length parameterized tangent has unit length. */ + if (exact) { + Float len = sqrt(_dot(r, r)); + + if (len > 0.00001) { + r = r / len; + } + } + + return r; + } + + Vector derivative2(Float s) + { +#ifdef FINITE_DIFF + const Float df = 0.0005; + Float s1, s2; + + if (s >= 1.0 - df) { + s1 = s - df; + s2 = s; + } + else { + s1 = s; + s2 = s + df; + } + + Vector a = derivative(s1); + Vector b = derivative(s2); + + return (b - a) / df; +#else + Float t = arc_to_t(s); + Vector r; + + Float dx = dcubic(ps[0][0], ps[1][0], ps[2][0], ps[3][0], t); + Float d2x = d2cubic(ps[0][0], ps[1][0], ps[2][0], ps[3][0], t); + Float dy = dcubic(ps[0][1], ps[1][1], ps[2][1], ps[3][1], t); + Float d2y = d2cubic(ps[0][1], ps[1][1], ps[2][1], ps[3][1], t); + + /* + comment: arc length second derivative; + + operator x, y, z, dx, dy, dz, d2x, d2y, d2z; + forall t let df(x(t), t) = dx(t); + forall t let df(y(t), t) = dy(t); + forall t let df(z(t), t) = dz(t); + forall t let df(dx(t), t) = d2x(t); + forall t let df(dy(t), t) = d2y(t); + forall t let df(dz(t), t) = d2z(t); + + comment: arc length first derivative is just the normalized tangent; + + comment: 2d case; + + dlen := sqrt(df(x(t), t)**2 + df(y(t), t)**2); + + df(df(x(t), t) / dlen, t); + df(df(y(t), t) / dlen, t); + + comment: 3d case; + + dlen := sqrt(df(x(t), t)**2 + df(y(t), t)**2 + df(z(t), t)**2); + + comment: final derivatives; + + df(df(x(t), t) / dlen, t); + df(df(y(t), t) / dlen, t); + df(df(z(t), t) / dlen, t); + */ + if constexpr (axes == 2) { + /* Basically the 2d perpidicular normalized tangent multiplied by the curvature. */ + + Float div = sqrt(dx * dx + dy * dy) * (dx * dx + dy * dy); + + r[0] = ((d2x * dy - d2y * dx) * dy) / div; + r[1] = (-(d2x * dy - d2y * dx) * dx) / div; + } + else if constexpr (axes == 3) { + Float dz = dcubic(ps[0][2], ps[1][2], ps[2][2], ps[3][2], t); + Float d2z = d2cubic(ps[0][2], ps[1][2], ps[2][2], ps[3][2], t); + + Float div = sqrt(dx * dx + dy * dy + dz * dz) * (dy * dy + dz * dz + dx * dx); + + r[0] = (d2x * dy * dy + d2x * dz * dz - d2y * dx * dy - d2z * dx * dz) / div; + r[1] = (-(d2x * dx * dy - d2y * dx * dx - d2y * dz * dz + d2z * dy * dz)) / div; + r[2] = (-(d2x * dx * dz + d2y * dy * dz - d2z * dx * dx - d2z * dy * dy)) / div; + } + else { + for (int i = 0; i < axes; i++) { + r[i] = d2cubic(ps[0][i], ps[1][i], ps[2][i], ps[3][i], t) * length; + } + } + + return r; +#endif + } + + Float curvature(Float s) + { + Vector dv2 = derivative2(s); + + if constexpr (axes == 2) { + Vector dv = derivative(s, true); + + /* Calculate signed curvature. Remember that dv is normalized. */ + return dv[0] * dv2[1] - dv[1] * dv2[0]; + } + + return sqrt(_dot(dv2, dv2)); + } + + private: + Float *_arc_to_t; + bool deleted = false; + + Float cubic(Float k1, Float k2, Float k3, Float k4, Float t) + { + return -(((3.0 * (t - 1.0) * k3 - k4 * t) * t - 3.0 * (t - 1.0) * (t - 1.0) * k2) * t + + (t - 1) * (t - 1) * (t - 1) * k1); + } + + Float dcubic(Float k1, Float k2, Float k3, Float k4, Float t) + { + return -3.0 * ((t - 1.0) * (t - 1.0) * k1 - k4 * t * t + (3.0 * t - 2.0) * k3 * t - + (3.0 * t - 1.0) * (t - 1.0) * k2); + } + + Float d2cubic(Float k1, Float k2, Float k3, Float k4, Float t) + { + return -6.0 * (k1 * t - k1 - 3.0 * k2 * t + 2.0 * k2 + 3.0 * k3 * t - k3 - k4 * t); + } + + Float _dot(Vector a, Vector b) + { + Float sum = 0.0; + + for (int i = 0; i < axes; i++) { + sum += a[i] * b[i]; + } + + return sum; + } + + Float clamp_s(Float s) + { + s = s < 0.0 ? 0.0 : s; + s = s >= length ? length * 0.999999 : s; + + return s; + } + + Float arc_to_t(Float s) + { + if (length == 0.0) { + return 0.0; + } + + s = clamp_s(s); + + Float t = s * (Float)(table_size - 1) / length; + + int i1 = floorf(t); + int i2 = min_ii(i1 + 1, table_size - 1); + + t -= (Float)i1; + + Float s1 = _arc_to_t[i1]; + Float s2 = _arc_to_t[i2]; + + return s1 + (s2 - s1) * t; + } +}; + +template class BezierSpline { + using Vector = VecBase; + struct Segment { + CubicBezier bezier; + Float start = 0.0; + + Segment(const CubicBezier &bez) + { + bezier = bez; + } + + Segment(const Segment &b) + { + *this = b; + } + + Segment &operator=(const Segment &b) + { + bezier = b.bezier; + start = b.start; + + return *this; + } + + Segment() + { + } + }; + + public: + Float length = 0.0; + bool deleted = false; + blender::Vector segments; + + void clear() + { + segments.clear(); + } + + BezierSpline() + { + } + + ~BezierSpline() + { + deleted = true; + } + + void add(CubicBezier &bez) + { + need_update = true; + + Segment seg; + + seg.bezier = bez; + segments.append(seg); + + update(); + } + + void update() + { + need_update = false; + + length = 0.0; + for (Segment &seg : segments) { + seg.start = length; + length += seg.bezier.length; + } + } + + inline Vector evaluate(Float s) + { + if (s == 0.0) { + return segments[0].bezier.ps[0]; + } + + if (s >= length) { + return segments[segments.size() - 1].bezier.ps[3]; + } + + Segment *seg = get_segment(s); + + return seg->bezier.evaluate(s - seg->start); + } + + Vector derivative(Float s, bool exact = true) + { + if (segments.size() == 0) { + return Vector(); + } + + s = clamp_s(s); + Segment *seg = get_segment(s); + + return seg->bezier.derivative(s - seg->start, exact); + } + + Vector derivative2(Float s) + { + if (segments.size() == 0) { + return Vector(); + } + + s = clamp_s(s); + Segment *seg = get_segment(s); + + return seg->bezier.derivative2(s - seg->start); + } + + Float curvature(Float s) + { + if (segments.size() == 0) { + return 0.0; + } + + s = clamp_s(s); + Segment *seg = get_segment(s); + + return seg->bezier.curvature(s - seg->start); + } + + /* Find the closest point on the spline. Uses a bisecting root finding approach. + * Note: in thoery we could split the spline into quadratic segments and solve + * for the closest point directy. + */ + Vector closest_point(const Vector p, Float &r_s, Vector &r_tan, Float &r_dis) + { + if (segments.size() == 0) { + return Vector(); + } + + const int steps = 12; + Float s = 0.0, ds = length / steps; + Float mindis = FLT_MAX; + Vector minp; + Float mins = 0.0; + bool found = false; + + Vector lastdv, lastp; + Vector b, dvb; + + for (int i = 0; i < steps + 1; i++, s += ds, lastp = b, lastdv = dvb) { + b = evaluate(s); + dvb = derivative(s, false); /* We don't need real normalized derivative here. */ + + if (i == 0) { + continue; + } + + Vector dva = lastdv; + Vector a = lastp; + + Vector vec1 = a - p; + Vector vec2 = b - p; + + Float sign1 = _dot(vec1, dva); + Float sign2 = _dot(vec2, dvb); + + if ((sign1 < 0.0) == (sign2 < 0.0)) { + found = true; + + Float len = _dot(vec1, vec1); + + if (len < mindis) { + mindis = len; + mins = s; + minp = evaluate(s); + } + continue; + } + + found = true; + + Float start = s - ds; + Float end = s; + Float mid = (start + end) * 0.5; + const int binary_steps = 10; + + for (int j = 0; j < binary_steps; j++) { + Vector dvmid = derivative(mid, false); + Vector vecmid = evaluate(mid) - p; + Float sign_mid = _dot(vecmid, dvmid); + + if ((sign_mid < 0.0) == (sign1 < 0.0)) { + start = mid; + } + else { + end = mid; + } + mid = (start + end) * 0.5; + } + + Vector p2 = evaluate(mid); + Vector vec_mid = p2 - p; + Float len = _dot(vec_mid, vec_mid); + + if (len < mindis) { + mindis = len; + minp = p2; + mins = mid; + } + } + + if (!found) { + mins = 0.0; + minp = evaluate(mins); + Vector vec = minp - p; + mindis = _dot(vec, vec); + } + + r_tan = derivative(mins, true); + r_s = mins; + r_dis = sqrtf(mindis); + + return minp; + } + + void pop_front(int n = 1) + { + for (int i = 0; i < segments.size() - n; i++) { + segments[i] = segments[i + n]; + } + + segments.resize(segments.size() - n); + update(); + } + + private: + bool need_update; + + Float _dot(Vector a, Vector b) + { + Float sum = 0.0; + + for (int i = 0; i < axes; i++) { + sum += a[i] * b[i]; + } + + return sum; + } + + Float clamp_s(Float s) + { + s = s < 0.0 ? 0.0 : s; + s = s >= length ? length * 0.999999 : s; + + return s; + } + + Segment *get_segment(Float s) + { + // printf("\n"); + + for (Segment &seg : segments) { + // printf("s: %f %f\n", seg.start, seg.start + seg.bezier.length); + + if (s >= seg.start && s < seg.start + seg.bezier.length) { + return &seg; + } + } + + // printf("\n"); + + return nullptr; + } +}; + +using BezierSpline2f = BezierSpline; +using BezierSpline3f = BezierSpline; +} // namespace blender diff --git a/source/blender/blenlib/BLI_ghash.h b/source/blender/blenlib/BLI_ghash.h index 8743b135dff..ac2b8a70205 100644 --- a/source/blender/blenlib/BLI_ghash.h +++ b/source/blender/blenlib/BLI_ghash.h @@ -162,7 +162,22 @@ void *BLI_ghash_lookup_default(const GHash *gh, */ void **BLI_ghash_lookup_p(GHash *gh, const void *key) ATTR_WARN_UNUSED_RESULT; /** - * Ensure \a key is exists in \a gh. + * Lookup a pointer to the value of \a key in \a gh. + * + * \param key: The key to lookup. + * \param r_key: Pointer to variable to store the key as stored in the ghash + * \param r_val: Similar to BLI_ghash_ensure_p, pointer to the ghash pointer + * that stores the value + * + * \returns true if the value was found + * + * \note This has 2 main benefits over #BLI_ghash_lookup. + * - The value can be modified in-place without further function calls (faster). + * - The key as stored in the ghash is returned in r_key + */ +bool BLI_ghash_lookup_p_ex(GHash *gh, const void *key, void **r_key, void ***r_val); + +/** Ensure \a key is exists in \a gh. * * This handles the common situation where the caller needs ensure a key is added to \a gh, * constructing a new value in the case the key isn't found. @@ -339,6 +354,77 @@ BLI_INLINE bool BLI_ghashIterator_done(const GHashIterator *ghi) typedef struct GSet GSet; +struct SmallHash; + +#include "BLI_smallhash.h" + +typedef struct TableGSet { + struct SmallHash ptr_to_idx; + void **elems; + int size, length; + int cur; +} TableGSet; + +TableGSet *BLI_table_gset_new(const char *info); +TableGSet *BLI_table_gset_new_ex(const char *info, int size); +void BLI_table_gset_free(TableGSet *ts, GHashKeyFreeFP freefp); +void BLI_table_gset_insert(TableGSet *ts, void *elem); +bool BLI_table_gset_add(TableGSet *ts, void *elem); +void BLI_table_gset_remove(TableGSet *ts, void *elem, GHashKeyFreeFP freefp); +bool BLI_table_gset_haskey(TableGSet *ts, void *elem); + +int BLI_table_gset_len(TableGSet *ts); + +#ifdef __cplusplus +# define TGSET_ITER(v, ts) \ + { \ + int _i1; \ + for (_i1 = 0; _i1 < (ts)->cur; _i1++) { \ + if (!(ts)->elems[_i1]) \ + continue; \ + v = static_cast((ts)->elems[_i1]); +#else +# define TGSET_ITER(v, ts) \ + { \ + int _i1; \ + for (_i1 = 0; _i1 < (ts)->cur; _i1++) { \ + if (!(ts)->elems[_i1]) \ + continue; \ + v = (ts)->elems[_i1]; +#endif + +#define TGSET_ITER_END \ + } \ + } + +#define TGSET_ITER_INDEX(v, ts, index) \ + { \ + int _i1; \ + index = -1; \ + for (_i1 = 0; _i1 < (ts)->cur; _i1++) { \ + if (!(ts)->elems[_i1]) \ + continue; \ + v = static_cast((ts)->elems[_i1]); \ + index++; + +#define TGSET_ITER_INDEX_END \ + } \ + } + +#define TGSET_FOREACH_BEGIN(type, v, ts) \ + { \ + type v; \ + int _i1; \ + for (_i1 = 0; _i1 < (ts)->cur; _i1++) { \ + if (!(ts)->elems[_i1]) \ + continue; \ + v = (type)(ts)->elems[_i1]; + +#define TGSET_FOREACH_END() \ + } \ + } \ + while (0) + typedef GHashHashFP GSetHashFP; typedef GHashCmpFP GSetCmpFP; typedef GHashKeyFreeFP GSetKeyFreeFP; diff --git a/source/blender/blenlib/BLI_heap_minmax.h b/source/blender/blenlib/BLI_heap_minmax.h new file mode 100644 index 00000000000..a1d65fcba19 --- /dev/null +++ b/source/blender/blenlib/BLI_heap_minmax.h @@ -0,0 +1,118 @@ +/* + * 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 bli + * \brief A min-heap / priority queue ADT + */ + +#include "BLI_math.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct MinMaxHeap; +struct MinMaxHeapNode; +typedef struct MinMaxHeap MinMaxHeap; +typedef struct MinMaxHeapNode MinMaxHeapNode; + +typedef void (*MinMaxHeapFreeFP)(void *ptr); + +/** + * Creates a new heap. Removed nodes are recycled, so memory usage will not shrink. + * + * \note Use when the size of the heap is known in advance. + */ +MinMaxHeap *BLI_mm_heap_new_ex(unsigned int tot_reserve) ATTR_WARN_UNUSED_RESULT; +MinMaxHeap *BLI_mm_heap_new(void) ATTR_WARN_UNUSED_RESULT; +void BLI_mm_heap_clear(MinMaxHeap *heap, MinMaxHeapFreeFP ptrfreefp) ATTR_NONNULL(1); +void BLI_mm_heap_free(MinMaxHeap *heap, MinMaxHeapFreeFP ptrfreefp) ATTR_NONNULL(1); +/** + * Insert heap node with a value (often a 'cost') and pointer into the heap, + * duplicate values are allowed. + */ +MinMaxHeapNode *BLI_mm_heap_insert(MinMaxHeap *heap, float value, void *ptr) ATTR_NONNULL(1); +/** + * Convenience function since this is a common pattern. + */ +void BLI_mm_heap_insert_or_update(MinMaxHeap *heap, + MinMaxHeapNode **node_p, + float value, + void *ptr) ATTR_NONNULL(1, 2); +bool BLI_mm_heap_is_empty(const MinMaxHeap *heap) ATTR_NONNULL(1); +unsigned int BLI_mm_heap_len(const MinMaxHeap *heap) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1); + +/** + * Return the top node of the heap. + * This is the node with the lowest value. + */ +MinMaxHeapNode *BLI_mm_heap_min(const MinMaxHeap *heap) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1); +/** + * Return the value of top node of the heap. + * This is the node with the lowest value. + */ +float BLI_mm_heap_min_value(const MinMaxHeap *heap) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1); + +/** + * Return the top node of the heap. + * This is the node with the lowest value. + */ +MinMaxHeapNode *BLI_mm_heap_max(const MinMaxHeap *heap) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1); +/** + * Return the value of top node of the heap. + * This is the node with the lowest value. + */ +float BLI_mm_heap_max_value(const MinMaxHeap *heap) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1); + +/** + * Pop the top node off the heap and return its pointer. + */ +void *BLI_mm_heap_pop_min(MinMaxHeap *heap) ATTR_NONNULL(1); + +/** + * Pop the top node off the heap and return its pointer. + */ +void *BLI_mm_heap_pop_max(MinMaxHeap *heap) ATTR_NONNULL(1); + +/** + * Can be used to avoid #BLI_mm_heap_remove, #BLI_mm_heap_insert calls, + * balancing the tree still has a performance cost, + * but is often much less than remove/insert, difference is most noticeable with large heaps. + */ +MinMaxHeapNode *BLI_mm_heap_node_value_update(MinMaxHeap *heap, MinMaxHeapNode *node, float value) + ATTR_NONNULL(1, 2); + +MinMaxHeapNode *BLI_mm_heap_node_value_update_ptr(MinMaxHeap *heap, + MinMaxHeapNode *node, + float value, + void *ptr); + +/** + * Return the value or pointer of a heap node. + */ +float BLI_mm_heap_node_value(const MinMaxHeapNode *heap) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1); +void *BLI_mm_heap_node_ptr(const MinMaxHeapNode *heap) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1); +/** + * Only for checking internal errors (gtest). + */ +bool BLI_mm_heap_is_valid(const MinMaxHeap *heap); + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/blenlib/BLI_heap_minmax.hh b/source/blender/blenlib/BLI_heap_minmax.hh new file mode 100644 index 00000000000..1467c675240 --- /dev/null +++ b/source/blender/blenlib/BLI_heap_minmax.hh @@ -0,0 +1,103 @@ + +#pragma once + +/** \file + * \ingroup bli + * \brief A min-heap / priority queue ADT + */ + +#include "BLI_index_range.hh" +#include "BLI_math.h" +#include "BLI_vector.hh" + +using blender::IndexRange; +using blender::Vector; + +namespace blender { +template class MinMaxHeapNode { + public: + private: + T *value; + float value; + int child1 = -1, child2 = -1, parent = -1; +}; + +template class MinMaxHeap { + public: + MinMaxHeap(int reserved = 0) + { + nodes.reserve(reserved); + } + + ~MinMaxHeap() + { + } + + private: + MinMaxHeapNode *heap_make_node() + { + nodes.append(MinMaxHeapNode()); + return &nodes[nodes.size() - 1]; + } + + MinMaxHeapNode *heap_descent_min2(MinMaxHeapNode *n) + { + if (n->child1 != -1 && n->child2 != -1) { + MinMaxHeapNode *n1 = &nodes[n->child1]; + MinMaxHeapNode *n2 = &nodes[n->child2]; + + return n1->value < n2->value ? n1 : n2; + } + else if (n->child1 != -1) { + return &nodes[n->child1]; + } + else if (n->child2 != -1) { + return &nodes[n->child2]; + } + + return n; + } + + MinMaxHeapNode *heap_descent_max2(MinMaxHeapNode *n) + { + if (n->child1 != -1 && n->child2 != -1) { + MinMaxHeapNode *n1 = &nodes[n->child1]; + MinMaxHeapNode *n2 = &nodes[n->child2]; + + return n1->value > n2->value ? n1 : n2; + } + else if (n->child1 != -1) { + return &nodes[n->child1]; + } + else if (n->child2 != -1) { + return &nodes[n->child2]; + } + + return n; + } + + /* find node grandchild */ + MinMaxHeapNode *heap_descent_max(MinMaxHeapNode *node) + { + if (node->child1 != -1 && node->child2 != -1) { + MinMaxHeapNode *n1 = &nodes[node->child1]; + MinMaxHeapNode *n2 = &nodes[node->child2]; + + n1 = heap_descent_max2(heap, n1); + n2 = heap_descent_max2(heap, n2); + + return n1->value > n2->value ? n1 : n2; + } + else if (node->child1 != -1) { + return heap_descent_max2(&nodes[node->child1]); + } + else if (node->child2 != -1) { + return heap_descent_max2(&nodes[node->child2]); + } + + return NULL; + } + + Vector> nodes; +}; +} // namespace blender diff --git a/source/blender/blenlib/BLI_linklist_stack.h b/source/blender/blenlib/BLI_linklist_stack.h index e4eecd9a583..436f36b0711 100644 --- a/source/blender/blenlib/BLI_linklist_stack.h +++ b/source/blender/blenlib/BLI_linklist_stack.h @@ -36,7 +36,7 @@ #define BLI_LINKSTACK_SIZE(var) BLI_mempool_len(var##_pool_) /* check for typeof() */ -#ifdef __GNUC__ +#if defined(__GNUC__) || defined(__clang__) # define BLI_LINKSTACK_PUSH(var, ptr) \ (CHECK_TYPE_INLINE(ptr, typeof(var##_type_)), \ BLI_linklist_prepend_pool(&(var), ptr, var##_pool_)) diff --git a/source/blender/blenlib/BLI_memarena.h b/source/blender/blenlib/BLI_memarena.h index 2c9e4c4700c..388128ccdf6 100644 --- a/source/blender/blenlib/BLI_memarena.h +++ b/source/blender/blenlib/BLI_memarena.h @@ -22,7 +22,9 @@ extern "C" { struct MemArena; typedef struct MemArena MemArena; -struct MemArena *BLI_memarena_new(size_t bufsize, +int BLI_memarena_size(const struct MemArena *ma); + +struct MemArena *BLI_memarena_new(const size_t bufsize, const char *name) ATTR_WARN_UNUSED_RESULT ATTR_RETURNS_NONNULL ATTR_NONNULL(2) ATTR_MALLOC; void BLI_memarena_free(struct MemArena *ma) ATTR_NONNULL(1); diff --git a/source/blender/blenlib/BLI_mempool.h b/source/blender/blenlib/BLI_mempool.h index 0313b743401..9fa430f9cc6 100644 --- a/source/blender/blenlib/BLI_mempool.h +++ b/source/blender/blenlib/BLI_mempool.h @@ -19,11 +19,29 @@ struct BLI_mempool_chunk; typedef struct BLI_mempool BLI_mempool; +BLI_mempool *BLI_mempool_create_ex(unsigned int esize, + unsigned int totelem, + unsigned int pchunk, + unsigned int flag, + const char *tag) + ATTR_MALLOC ATTR_WARN_UNUSED_RESULT ATTR_RETURNS_NONNULL; +; + +//#define DEBUG_MEMPOOL_LEAKS + +#ifdef DEBUG_MEMPOOL_LEAKS +ATTR_MALLOC ATTR_WARN_UNUSED_RESULT ATTR_RETURNS_NONNULL; + +# define BLI_mempool_create(esize, totelem, pchunk, flag) \ + BLI_mempool_create_ex(esize, totelem, pchunk, flag, __func__) +#else BLI_mempool *BLI_mempool_create(unsigned int esize, unsigned int elem_num, unsigned int pchunk, unsigned int flag) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT ATTR_RETURNS_NONNULL; +#endif + void *BLI_mempool_alloc(BLI_mempool *pool) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT ATTR_RETURNS_NONNULL ATTR_NONNULL(1); void *BLI_mempool_calloc(BLI_mempool *pool) @@ -105,6 +123,10 @@ enum { * order of allocation when no chunks have been freed. */ BLI_MEMPOOL_ALLOW_ITER = (1 << 0), + + /* allow random access, implies BLI_MEMPOOL_ALLOW_ITER since we + need the freewords to detect free state of elements*/ + BLI_MEMPOOL_RANDOM_ACCESS = (1 << 1) | (1 << 0) }; /** @@ -116,6 +138,26 @@ void BLI_mempool_iternew(BLI_mempool *pool, BLI_mempool_iter *iter) ATTR_NONNULL */ void *BLI_mempool_iterstep(BLI_mempool_iter *iter) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); +/* +This preallocates a mempool suitable for threading. totelem elements are preallocated +in chunks of size pchunk, and returned in r_chunks. +*/ + +BLI_mempool *BLI_mempool_create_for_tasks(const unsigned int esize, + int totelem, + const int pchunk, + void ***r_chunks, + int *r_totchunk, + int *r_esize, + int flag); + +// memory coherence stuff +int BLI_mempool_find_elems_fuzzy( + BLI_mempool *pool, int idx, int range, void **r_elems, int r_elems_size); + +int BLI_mempool_get_size(BLI_mempool *pool); +int BLI_mempool_find_real_index(BLI_mempool *pool, void *ptr); + #ifdef __cplusplus } #endif diff --git a/source/blender/blenlib/BLI_mempool_lockfree.h b/source/blender/blenlib/BLI_mempool_lockfree.h new file mode 100644 index 00000000000..4d779a3b8fc --- /dev/null +++ b/source/blender/blenlib/BLI_mempool_lockfree.h @@ -0,0 +1,42 @@ +#pragma once + +#include "BLI_task.h" + +typedef struct BLI_lfmempool { + void *unused; +} BLI_lfmempool; + +typedef struct BLI_lfmempool_iter { + void *chunk; + BLI_lfmempool *pool; + int i; + void **curchunk_threaded_shared; +} BLI_lfmempool_iter; + +#ifdef __cplusplus +extern "C" { +#endif + +BLI_lfmempool *BLI_lfmempool_create(int esize, int psize); +void BLI_lfmempool_destroy(BLI_lfmempool *pool); +void *BLI_lfmempool_alloc(BLI_lfmempool *pool); +void *BLI_lfmempool_calloc(BLI_lfmempool *pool); +void BLI_lfmempool_free(BLI_lfmempool *pool, void *mem); +void BLI_lfmempool_iternew(BLI_lfmempool *_pool, BLI_lfmempool_iter *iter); +void *BLI_lfmempool_iterstep(BLI_lfmempool_iter *iter); +// int BLI_lfmempool_len(BLI_lfmempool *pool); +void *BLI_lfmempool_iterstep_threaded(BLI_lfmempool *iter); +void *BLI_lfmempool_findelem(BLI_lfmempool *pool, int index); + +typedef struct ParallelLFMempoolTaskData { + BLI_lfmempool_iter ts_iter; + TaskParallelTLS tls; +} ParallelLFMempoolTaskData; + +ParallelLFMempoolTaskData *lfmempool_iter_threadsafe_create(BLI_lfmempool *pool, + const size_t num_iter); +void lfmempool_iter_threadsafe_destroy(ParallelLFMempoolTaskData *iter_arr); + +#ifdef __cplusplus +} +#endif //__cplusplus diff --git a/source/blender/blenlib/BLI_smallhash.h b/source/blender/blenlib/BLI_smallhash.h index 55b0fafe596..1a703a11e66 100644 --- a/source/blender/blenlib/BLI_smallhash.h +++ b/source/blender/blenlib/BLI_smallhash.h @@ -25,11 +25,14 @@ typedef struct { #define SMSTACKSIZE 131 typedef struct SmallHash { unsigned int nbuckets; - unsigned int nentries; + unsigned int nentries, nfreecells; unsigned int cursize; SmallHashEntry *buckets; SmallHashEntry buckets_stack[SMSTACKSIZE]; + + bool use_pointer_hash; + bool using_stack; } SmallHash; typedef struct { @@ -37,7 +40,9 @@ typedef struct { unsigned int i; } SmallHashIter; -void BLI_smallhash_init_ex(SmallHash *sh, unsigned int nentries_reserve) ATTR_NONNULL(1); +int BLI_smallhash_memuse(SmallHash *sh); + +void BLI_smallhash_init_ex(SmallHash *sh, const unsigned int nentries_reserve) ATTR_NONNULL(1); void BLI_smallhash_init(SmallHash *sh) ATTR_NONNULL(1); /** * \note does *not* free *sh itself! only the direct data! @@ -53,22 +58,25 @@ void BLI_smallhash_insert(SmallHash *sh, uintptr_t key, void *item) ATTR_NONNULL */ bool BLI_smallhash_reinsert(SmallHash *sh, uintptr_t key, void *item) ATTR_NONNULL(1); bool BLI_smallhash_remove(SmallHash *sh, uintptr_t key) ATTR_NONNULL(1); -void *BLI_smallhash_lookup(const SmallHash *sh, uintptr_t key) +void *BLI_smallhash_lookup(SmallHash *sh, uintptr_t key) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT; +void **BLI_smallhash_lookup_p(SmallHash *sh, uintptr_t key) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT; -void **BLI_smallhash_lookup_p(const SmallHash *sh, uintptr_t key) - ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT; -bool BLI_smallhash_haskey(const SmallHash *sh, uintptr_t key) ATTR_NONNULL(1); -int BLI_smallhash_len(const SmallHash *sh) ATTR_NONNULL(1); +bool BLI_smallhash_haskey(SmallHash *sh, uintptr_t key) ATTR_NONNULL(1); +int BLI_smallhash_len(SmallHash *sh) ATTR_NONNULL(1); void *BLI_smallhash_iternext(SmallHashIter *iter, uintptr_t *key) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT; void **BLI_smallhash_iternext_p(SmallHashIter *iter, uintptr_t *key) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT; -void *BLI_smallhash_iternew(const SmallHash *sh, SmallHashIter *iter, uintptr_t *key) +void *BLI_smallhash_iternew(SmallHash *sh, SmallHashIter *iter, uintptr_t *key) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT; -void **BLI_smallhash_iternew_p(const SmallHash *sh, SmallHashIter *iter, uintptr_t *key) +void **BLI_smallhash_iternew_p(SmallHash *sh, SmallHashIter *iter, uintptr_t *key) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT; /* void BLI_smallhash_print(SmallHash *sh); */ /* UNUSED */ +void BLI_smallhash_clear(SmallHash *sh, uintptr_t key); +bool BLI_smallhash_ensure_p(SmallHash *sh, uintptr_t key, void ***item); +bool BLI_smallhash_remove_p(SmallHash *sh, uintptr_t key, void **val); + #ifdef DEBUG /** * Measure how well the hash function performs diff --git a/source/blender/blenlib/BLI_strict_flags.h b/source/blender/blenlib/BLI_strict_flags.h index 26355430f5c..5224723f109 100644 --- a/source/blender/blenlib/BLI_strict_flags.h +++ b/source/blender/blenlib/BLI_strict_flags.h @@ -27,10 +27,22 @@ #endif #ifdef _MSC_VER -# pragma warning(error : 4018) /* signed/unsigned mismatch */ -# pragma warning(error : 4244) /* conversion from 'type1' to 'type2', possible loss of data */ -# pragma warning(error : 4245) /* conversion from 'int' to 'unsigned int' */ -# pragma warning(error : 4267) /* conversion from 'size_t' to 'type', possible loss of data */ -# pragma warning(error : 4305) /* truncation from 'type1' to 'type2' */ -# pragma warning(error : 4389) /* signed/unsigned mismatch */ +/* While regular clang defines __GNUC__ and is handled by the code above, clang-cl does not and + * needs to be handled separately. */ +# ifdef __clang__ +# pragma clang diagnostic error "-Wsign-conversion" +# pragma clang diagnostic error "-Wsign-compare" +# pragma clang diagnostic error "-Wimplicit-float-conversion" +# pragma clang diagnostic error "-Wimplicit-int-conversion" +# pragma clang diagnostic error "-Wimplicit-int" +# pragma clang diagnostic error "-Wshadow" +/* Normal MSVC */ +# else +# pragma warning(error : 4018) /* signed/unsigned mismatch */ +# pragma warning(error : 4244) /* conversion from 'type1' to 'type2', possible loss of data */ +# pragma warning(error : 4245) /* conversion from 'int' to 'unsigned int' */ +# pragma warning(error : 4267) /* conversion from 'size_t' to 'type', possible loss of data */ +# pragma warning(error : 4305) /* truncation from 'type1' to 'type2' */ +# pragma warning(error : 4389) /* signed/unsigned mismatch */ +# endif #endif diff --git a/source/blender/blenlib/BLI_utildefines.h b/source/blender/blenlib/BLI_utildefines.h index 5cc91a22944..26f6c6cb4eb 100644 --- a/source/blender/blenlib/BLI_utildefines.h +++ b/source/blender/blenlib/BLI_utildefines.h @@ -283,6 +283,12 @@ extern "C" { (_VA_ELEM15(v, a, b, c, d, e, f, g, h, i, j, k, l, m, n) || _VA_ELEM2(v, o)) #define _VA_ELEM17(v, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p) \ (_VA_ELEM16(v, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o) || _VA_ELEM2(v, p)) +#define _VA_ELEM18(v, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q) \ + (_VA_ELEM17(v, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p) || _VA_ELEM2(v, q)) +#define _VA_ELEM19(v, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r) \ + (_VA_ELEM18(v, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q) || _VA_ELEM2(v, r)) +#define _VA_ELEM20(v, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s) \ + (_VA_ELEM19(v, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r) || _VA_ELEM2(v, s)) /* clang-format on */ /* reusable ELEM macro */ @@ -844,7 +850,7 @@ extern bool BLI_memory_is_zero(const void *arr, size_t arr_size); */ #define BLI_ENABLE_IF(condition) typename std::enable_if_t<(condition)> * = nullptr -#if defined(_MSC_VER) +#if defined(_MSC_VER) && !defined(__clang__) # define BLI_NO_UNIQUE_ADDRESS [[msvc::no_unique_address]] #elif defined(__has_cpp_attribute) # if __has_cpp_attribute(no_unique_address) diff --git a/source/blender/blenlib/CMakeLists.txt b/source/blender/blenlib/CMakeLists.txt index 7c24fb8b5e7..09ab4689fbf 100644 --- a/source/blender/blenlib/CMakeLists.txt +++ b/source/blender/blenlib/CMakeLists.txt @@ -81,6 +81,7 @@ set(SRC intern/hash_md5.c intern/hash_mm2a.c intern/hash_mm3.c + intern/heap_minmax.c intern/index_mask.cc intern/jitter_2d.c intern/kdtree_1d.c @@ -88,6 +89,7 @@ set(SRC intern/kdtree_3d.c intern/kdtree_4d.c intern/lasso_2d.c + intern/lockfree_mempool.cc intern/lazy_threading.cc intern/length_parameterize.cc intern/listbase.cc @@ -139,6 +141,7 @@ set(SRC intern/string_search.cc intern/string_utf8.c intern/string_utils.c + #intern/string_regex.cc intern/system.c intern/task_graph.cc intern/task_iterator.c @@ -155,6 +158,7 @@ set(SRC intern/voxel.c intern/winstuff.c intern/winstuff_dir.c + intern/BLI_table_gset.c # Private headers. intern/BLI_mempool_private.h @@ -237,6 +241,7 @@ set(SRC BLI_hash_tables.hh BLI_heap.h BLI_heap_simple.h + BLI_heap_minmax.h BLI_index_mask.hh BLI_index_mask_ops.hh BLI_index_range.hh @@ -291,6 +296,7 @@ set(SRC BLI_memory_utils.h BLI_memory_utils.hh BLI_mempool.h + BLI_mempool_lockfree.h BLI_mesh_boolean.hh BLI_mesh_intersect.hh BLI_mmap.h @@ -321,6 +327,7 @@ set(SRC BLI_sort.h BLI_sort.hh BLI_sort_utils.h + BLI_even_spline.hh BLI_span.hh BLI_stack.h BLI_stack.hh @@ -331,6 +338,7 @@ set(SRC BLI_string_search.h BLI_string_utf8.h BLI_string_utils.h + #BLI_string_regex.h BLI_sys_types.h BLI_system.h BLI_task.h diff --git a/source/blender/blenlib/intern/BLI_ghash.c b/source/blender/blenlib/intern/BLI_ghash.c index e6ff5bab8a1..d6f142a4495 100644 --- a/source/blender/blenlib/intern/BLI_ghash.c +++ b/source/blender/blenlib/intern/BLI_ghash.c @@ -752,6 +752,45 @@ void **BLI_ghash_lookup_p(GHash *gh, const void *key) return e ? &e->val : NULL; } +/** + * Lookup a pointer to the value of \a key in \a gh. + * + * \param key: The key to lookup. + * \returns true if the value was found + * + * \note This has 2 main benefits over #BLI_ghash_lookup. + * - The value can be modified in-place without further function calls (faster). + * - The key as stored in the ghash in returned + */ +bool BLI_ghash_lookup_p_ex(GHash *gh, const void *key, void **r_key, void ***r_val) +{ + GHashEntry *e = (GHashEntry *)ghash_lookup_entry(gh, key); + BLI_assert(!(gh->flag & GHASH_FLAG_IS_GSET)); + + if (e) { + *r_key = e->e.key; + *r_val = &e->val; + return true; + } + else { + return false; + } +} + +/** + * Ensure \a key is exists in \a gh. + * + * This handles the common situation where the caller needs ensure a key is added to \a gh, + * constructing a new value in the case the key isn't found. + * Otherwise use the existing value. + * + * Such situations typically incur multiple lookups, however this function + * avoids them by ensuring the key is added, + * returning a pointer to the value so it can be used or initialized by the caller. + * + * \returns true when the value didn't need to be added. + * (when false, the caller _must_ initialize the value). + */ bool BLI_ghash_ensure_p(GHash *gh, void *key, void ***r_val) { const uint hash = ghash_keyhash(gh, key); diff --git a/source/blender/blenlib/intern/BLI_memarena.c b/source/blender/blenlib/intern/BLI_memarena.c index 3c353a1c8c8..aee24173fa1 100644 --- a/source/blender/blenlib/intern/BLI_memarena.c +++ b/source/blender/blenlib/intern/BLI_memarena.c @@ -48,6 +48,17 @@ struct MemArena { bool use_calloc; }; +int BLI_memarena_size(const struct MemArena *ma) +{ + int ret = 0; + + for (struct MemBuf *buf = ma->bufs; buf; buf = buf->next) { + ret += (int)ma->bufsize; + } + + return ret; +} + static void memarena_buf_free_all(struct MemBuf *mb) { while (mb != NULL) { diff --git a/source/blender/blenlib/intern/BLI_mempool.c b/source/blender/blenlib/intern/BLI_mempool.c index f70b5ddd766..48d7175ea42 100644 --- a/source/blender/blenlib/intern/BLI_mempool.c +++ b/source/blender/blenlib/intern/BLI_mempool.c @@ -13,16 +13,23 @@ * (optionally when using the #BLI_MEMPOOL_ALLOW_ITER flag). */ +#include #include #include #include "atomic_ops.h" +#include "BLI_string.h" #include "BLI_utildefines.h" - +# +#include "BLI_asan.h" #include "BLI_mempool.h" /* own include */ #include "BLI_mempool_private.h" /* own include */ +#ifndef WITH_DNA_GHASH +# include "BLI_threads.h" +#endif + #include "MEM_guardedalloc.h" #include "BLI_strict_flags.h" /* keep last */ @@ -31,6 +38,13 @@ # include "valgrind/memcheck.h" #endif +#if (defined(__SANITIZE_ADDRESS__) || __has_feature(address_sanitizer)) +# define POISON_REDZONE_SIZE 32 +# define HAVE_MEMPOOL_ASAN +#else +# define POISON_REDZONE_SIZE 0 +#endif + /* NOTE: copied from BLO_blend_defs.h, don't use here because we're in BLI. */ #ifdef __BIG_ENDIAN__ /* Big Endian */ @@ -95,12 +109,21 @@ typedef struct BLI_mempool_chunk { * The mempool, stores and tracks memory \a chunks and elements within those chunks \a free. */ struct BLI_mempool { +#if !defined(WITH_DNA_GHASH) && (defined(__SANITIZE_ADDRESS__) || __has_feature(address_sanitizer)) + ThreadMutex mutex; +#endif /** Single linked list of allocated chunks. */ BLI_mempool_chunk *chunks; /** Keep a pointer to the last, so we can append new chunks there * this is needed for iteration so we can loop over chunks in the order added. */ BLI_mempool_chunk *chunk_tail; + /* only used if BLI_MEMPOOL_RANDOM_ACCESS is true*/ + BLI_mempool_chunk **chunktable; + + /* only used if BLI_MEMPOOL_RANDOM_ACCESS is true*/ + int totchunk; + /** Element size in bytes. */ uint esize; /** Chunk size in bytes. */ @@ -110,16 +133,23 @@ struct BLI_mempool { uint flag; /* keeps aligned to 16 bits */ +#ifdef HAVE_MEMPOOL_ASAN + char poisoned[256]; +#endif + /** Free element list. Interleaved into chunk data. */ BLI_freenode *free; /** Use to know how many chunks to keep for #BLI_mempool_clear. */ uint maxchunks; /** Number of elements currently in use. */ - uint totused; + int totused; #ifdef USE_TOTALLOC /** Number of elements allocated in total. */ uint totalloc; #endif + + char *memtag; + char *memtag_chunk; }; #define MEMPOOL_ELEM_SIZE_MIN (sizeof(void *) * 2) @@ -132,6 +162,54 @@ struct BLI_mempool { /** Extra bytes implicitly used for every chunk alloc. */ #define CHUNK_OVERHEAD (uint)(MEM_SIZE_OVERHEAD + sizeof(BLI_mempool_chunk)) +static void mempool_poison(BLI_mempool *pool) +{ +#if (defined(__SANITIZE_ADDRESS__) || __has_feature(address_sanitizer)) +# ifndef WITH_DNA_GHASH + BLI_mutex_unlock(&pool->mutex); +# endif + +# ifndef WITH_DNA_GHASH + char *ptr = (char *)pool; + size_t offset = offsetof(BLI_mempool, mutex) + sizeof(ThreadMutex); + BLI_asan_poison(ptr + offset, sizeof(*pool) - offset); +# else + BLI_asan_poison(pool, sizeof(*pool)); +# endif +#endif +} + +static void mempool_poison_no_unlock(BLI_mempool *pool) +{ +#if (defined(__SANITIZE_ADDRESS__) || __has_feature(address_sanitizer)) + +# ifndef WITH_DNA_GHASH + char *ptr = (char *)pool; + size_t offset = offsetof(BLI_mempool, mutex) + sizeof(ThreadMutex); + BLI_asan_poison(ptr + offset, sizeof(*pool) - offset); +# else + BLI_asan_poison(pool, sizeof(*pool)); +# endif +#endif +} + +static void mempool_unpoison(BLI_mempool *pool) +{ +#if (defined(__SANITIZE_ADDRESS__) || __has_feature(address_sanitizer)) +# ifndef WITH_DNA_GHASH + BLI_mutex_lock(&pool->mutex); +# endif + +# ifndef WITH_DNA_GHASH + char *ptr = (char *)pool; + size_t offset = offsetof(BLI_mempool, mutex) + sizeof(ThreadMutex); + BLI_asan_unpoison(ptr + offset, sizeof(*pool) - offset); +# else + BLI_asan_unpoison(pool, sizeof(*pool)); +# endif +#endif +} + #ifdef USE_CHUNK_POW2 static uint power_of_2_max_u(uint x) { @@ -145,6 +223,33 @@ static uint power_of_2_max_u(uint x) } #endif +static void mempool_update_chunktable(BLI_mempool *pool) +{ + if (!(pool->flag & BLI_MEMPOOL_RANDOM_ACCESS)) { + return; + } + + pool->totchunk = 0; + BLI_mempool_chunk *chunk = pool->chunks; + + while (chunk) { + pool->totchunk++; + chunk = chunk->next; + } + + MEM_SAFE_FREE(pool->chunktable); + pool->chunktable = (BLI_mempool_chunk **)MEM_mallocN( + sizeof(pool->chunktable) * (size_t)pool->totchunk, "mempool chunktable"); + + int i = 0; + chunk = pool->chunks; + + while (chunk) { + pool->chunktable[i++] = chunk; + chunk = chunk->next; + } +} + BLI_INLINE BLI_mempool_chunk *mempool_chunk_find(BLI_mempool_chunk *head, uint index) { while (index-- && head) { @@ -166,7 +271,7 @@ BLI_INLINE uint mempool_maxchunks(const uint elem_num, const uint pchunk) static BLI_mempool_chunk *mempool_chunk_alloc(BLI_mempool *pool) { - return MEM_mallocN(sizeof(BLI_mempool_chunk) + (size_t)pool->csize, "BLI_Mempool Chunk"); + return BLI_asan_safe_malloc(sizeof(BLI_mempool_chunk) + (size_t)pool->csize, pool->memtag); } /** @@ -186,6 +291,26 @@ static BLI_freenode *mempool_chunk_add(BLI_mempool *pool, BLI_freenode *curnode = CHUNK_DATA(mpchunk); uint j; + if (pool->flag & BLI_MEMPOOL_RANDOM_ACCESS) { + if (!pool->chunktable || + MEM_allocN_len(pool->chunktable) / sizeof(void *) <= (size_t)pool->totchunk) { + void *old = pool->chunktable; + + int size = (int)pool->totchunk + 2; + size += size >> 1; + + pool->chunktable = MEM_mallocN(sizeof(void *) * (size_t)size, "mempool chunktable"); + + if (old) { + memcpy(pool->chunktable, old, sizeof(void *) * (size_t)pool->totchunk); + } + + MEM_SAFE_FREE(old); + } + + pool->chunktable[pool->totchunk++] = mpchunk; + } + /* append */ if (pool->chunk_tail) { pool->chunk_tail->next = mpchunk; @@ -206,22 +331,41 @@ static BLI_freenode *mempool_chunk_add(BLI_mempool *pool, j = pool->pchunk; if (pool->flag & BLI_MEMPOOL_ALLOW_ITER) { while (j--) { - curnode->next = NODE_STEP_NEXT(curnode); + BLI_freenode *next; + + BLI_asan_unpoison(curnode, pool->esize - POISON_REDZONE_SIZE); + + curnode->next = next = NODE_STEP_NEXT(curnode); curnode->freeword = FREEWORD; - curnode = curnode->next; + + BLI_asan_poison(curnode, pool->esize); + + curnode = next; } } else { while (j--) { - curnode->next = NODE_STEP_NEXT(curnode); - curnode = curnode->next; + BLI_freenode *next; + + BLI_asan_unpoison(curnode, pool->esize - POISON_REDZONE_SIZE); + curnode->next = next = NODE_STEP_NEXT(curnode); + BLI_asan_poison(curnode, pool->esize); + + curnode = next; } } /* terminate the list (rewind one) * will be overwritten if 'curnode' gets passed in again as 'last_tail' */ + + BLI_asan_unpoison(curnode, pool->esize - POISON_REDZONE_SIZE); + BLI_asan_poison(curnode, pool->esize); + curnode = NODE_STEP_PREV(curnode); + + BLI_asan_unpoison(curnode, pool->esize - POISON_REDZONE_SIZE); curnode->next = NULL; + BLI_asan_poison(curnode, pool->esize); #ifdef USE_TOTALLOC pool->totalloc += pool->pchunk; @@ -229,35 +373,172 @@ static BLI_freenode *mempool_chunk_add(BLI_mempool *pool, /* final pointer in the previously allocated chunk is wrong */ if (last_tail) { + BLI_asan_unpoison(last_tail, pool->esize - POISON_REDZONE_SIZE); last_tail->next = CHUNK_DATA(mpchunk); + BLI_asan_poison(last_tail, pool->esize); } return curnode; } -static void mempool_chunk_free(BLI_mempool_chunk *mpchunk) +/** + * Preallocates a mempool suitable for threading. elem_num elements are preallocated + * in chunks of size pchunk, and returned in r_chunks. The idea is to pass these + * to tasks. + */ +BLI_mempool *BLI_mempool_create_for_tasks(const unsigned int esize, + int elem_num, + const int pchunk, + void ***r_chunks, + int *r_totchunk, + int *r_esize, + int flag) { - MEM_freeN(mpchunk); + BLI_mempool *pool = BLI_mempool_create(esize, 0, (uint)pchunk, (uint)flag); + + mempool_unpoison(pool); + + // override pchunk, may not be a power of 2 + pool->pchunk = (uint)pchunk; + pool->csize = (uint)pchunk * pool->esize; + + if (elem_num % pchunk == 0) { + pool->maxchunks = (uint)elem_num / (uint)pchunk; + } + else { + pool->maxchunks = (uint)elem_num / (uint)pchunk + 1; + } + + if (elem_num) { + BLI_freenode *last_tail = NULL; + + /* Allocate the actual chunks. */ + for (uint i = 0; i < pool->maxchunks; i++) { + BLI_mempool_chunk *mpchunk = mempool_chunk_alloc(pool); + last_tail = mempool_chunk_add(pool, mpchunk, last_tail); + } + } + + void **chunks = MEM_callocN(sizeof(void *) * pool->maxchunks, + "BLI_mempool_create_for_tasks r_chunks"); + + unsigned int totalloc = 0; + *r_totchunk = 0; + + BLI_mempool_chunk *chunk = pool->chunks, *lastchunk = NULL; + + while (chunk) { + lastchunk = chunk; + totalloc += pool->pchunk; + chunk = chunk->next; + } + + pool->totused = (int)totalloc; + pool->free = NULL; + + int i = (int)pool->pchunk - 1; + + while (lastchunk && totalloc > (uint)elem_num) { + if (i < 0) { + BLI_mempool_chunk *lastchunk2 = NULL; + + for (chunk = pool->chunks; chunk; chunk = chunk->next) { + if (chunk == lastchunk) { + lastchunk = lastchunk2; + } + } + + if (!lastchunk) { + break; + } + + i = (int)pool->pchunk - 1; + } + + char *elem = CHUNK_DATA(lastchunk); + elem += pool->esize * (unsigned int)i; + + BLI_mempool_free(pool, elem); + + totalloc--; + i--; + } + + int ci = 0; + + chunk = pool->chunks; + while (chunk && chunk != lastchunk) { + chunks[ci++] = CHUNK_DATA(chunk); + chunk = chunk->next; + } + + if (lastchunk && i >= 0) { + chunks[ci++] = CHUNK_DATA(lastchunk); + } + + *r_totchunk = ci; + *r_chunks = (void **)chunks; + *r_esize = (int)pool->esize; + + return pool; } -static void mempool_chunk_free_all(BLI_mempool_chunk *mpchunk) +static void mempool_chunk_free(BLI_mempool_chunk *mpchunk, BLI_mempool *pool) +{ + BLI_asan_unpoison(mpchunk, sizeof(BLI_mempool_chunk) + pool->esize * pool->csize); + BLI_asan_safe_free(mpchunk); +} + +ATTR_NO_OPT static void mempool_chunk_free_all(BLI_mempool_chunk *mpchunk, BLI_mempool *pool) { BLI_mempool_chunk *mpchunk_next; for (; mpchunk; mpchunk = mpchunk_next) { mpchunk_next = mpchunk->next; - mempool_chunk_free(mpchunk); + mempool_chunk_free(mpchunk, pool); } } +#ifdef BLI_mempool_create +# undef BLI_mempool_create +#endif + BLI_mempool *BLI_mempool_create(uint esize, uint elem_num, uint pchunk, uint flag) +{ + return BLI_mempool_create_ex(esize, elem_num, pchunk, flag, ""); +} + +BLI_mempool *BLI_mempool_create_ex( + uint esize, uint elem_num, uint pchunk, uint flag, const char *tag) { BLI_mempool *pool; BLI_freenode *last_tail = NULL; uint i, maxchunks; + char buf[512]; + if (tag) { + sprintf(buf, "Mempool:%s", tag); + } + else { + sprintf(buf, "memory pool"); + } + + char *memtag = strdup(buf); + /* allocate the pool structure */ - pool = MEM_mallocN(sizeof(BLI_mempool), "memory pool"); + pool = MEM_mallocN(sizeof(BLI_mempool), memtag); + + strcat(buf, " chunk"); + +#if !defined(WITH_DNA_GHASH) && (defined(__SANITIZE_ADDRESS__) || __has_feature(address_sanitizer)) + BLI_mutex_init(&pool->mutex); +#endif + + pool->memtag = memtag; + pool->memtag_chunk = strdup(buf); + + pool->totchunk = 0; + pool->chunktable = NULL; /* set the elem size */ if (esize < (int)MEMPOOL_ELEM_SIZE_MIN) { @@ -268,6 +549,8 @@ BLI_mempool *BLI_mempool_create(uint esize, uint elem_num, uint pchunk, uint fla esize = MAX2(esize, (uint)sizeof(BLI_freenode)); } + esize += POISON_REDZONE_SIZE; + maxchunks = mempool_maxchunks(elem_num, pchunk); pool->chunks = NULL; @@ -313,11 +596,19 @@ BLI_mempool *BLI_mempool_create(uint esize, uint elem_num, uint pchunk, uint fla VALGRIND_CREATE_MEMPOOL(pool, 0, false); #endif +#ifdef HAVE_MEMPOOL_ASAN + BLI_asan_poison(pool->poisoned, sizeof(pool->poisoned)); +#endif + + mempool_poison_no_unlock(pool); + return pool; } void *BLI_mempool_alloc(BLI_mempool *pool) { + mempool_unpoison(pool); + BLI_freenode *free_pop; if (UNLIKELY(pool->free == NULL)) { @@ -328,6 +619,8 @@ void *BLI_mempool_alloc(BLI_mempool *pool) free_pop = pool->free; + BLI_asan_unpoison(free_pop, pool->esize - POISON_REDZONE_SIZE); + BLI_assert(pool->chunk_tail->next == NULL); if (pool->flag & BLI_MEMPOOL_ALLOW_ITER) { @@ -341,18 +634,122 @@ void *BLI_mempool_alloc(BLI_mempool *pool) VALGRIND_MEMPOOL_ALLOC(pool, free_pop, pool->esize); #endif + mempool_poison(pool); + return (void *)free_pop; } void *BLI_mempool_calloc(BLI_mempool *pool) { void *retval = BLI_mempool_alloc(pool); - memset(retval, 0, (size_t)pool->esize); + + mempool_unpoison(pool); + memset(retval, 0, (size_t)pool->esize - POISON_REDZONE_SIZE); + mempool_poison(pool); + return retval; } +int BLI_mempool_find_real_index(BLI_mempool *pool, void *ptr) +{ + mempool_unpoison(pool); + + BLI_mempool_chunk *chunk = pool->chunks; + uintptr_t uptr = (uintptr_t)ptr; + uintptr_t cptr; + int chunki = 0; + + while (chunk) { + cptr = (uintptr_t)chunk; + + if (uptr >= cptr && uptr < cptr + pool->csize) { + break; + } + + chunk = chunk->next; + chunki++; + } + + if (!chunk) { + mempool_poison(pool); + return -1; // failed + } + + int ret = chunki * (int)pool->pchunk + ((int)(uptr - cptr)) / (int)pool->esize; + + mempool_poison(pool); + + return ret; +} + +/*finds an element in pool that's roughly at idx, idx*/ +int BLI_mempool_find_elems_fuzzy( + BLI_mempool *pool, int idx, int range, void **r_elems, int r_elems_size) +{ + mempool_unpoison(pool); + + int istart = idx - range, iend = idx + range; + istart = MAX2(istart, 0); + + int elem_num = 0; + + for (int i = istart; i < iend; i++) { + int chunki = i / (int)pool->pchunk; + if (chunki >= (int)pool->totchunk) { + break; + } + + int idx2 = i % (int)pool->pchunk; + + BLI_mempool_chunk *chunk = pool->chunktable[chunki]; + char *data = (char *)CHUNK_DATA(chunk); + void *ptr = data + idx2 * (int)pool->esize; + + BLI_asan_unpoison(ptr, pool->esize - POISON_REDZONE_SIZE); + + BLI_freenode *fnode = (BLI_freenode *)ptr; + if (fnode->freeword == FREEWORD) { + BLI_asan_poison(ptr, pool->esize); + continue; + } + + r_elems[elem_num++] = ptr; + + if (elem_num == r_elems_size) { + break; + } + } + + mempool_poison(pool); + return elem_num; +} + +int BLI_mempool_get_size(BLI_mempool *pool) +{ + mempool_unpoison(pool); + + BLI_mempool_chunk *chunk = pool->chunks; + int ret = 0; + + while (chunk) { + chunk = chunk->next; + + ret += (int)pool->pchunk; + } + + mempool_poison(pool); + return ret; +} + +/** + * Free an element from the mempool. + * + * \note doesn't protect against double frees, take care! + */ void BLI_mempool_free(BLI_mempool *pool, void *addr) { + mempool_unpoison(pool); + BLI_freenode *newhead = addr; #ifndef NDEBUG @@ -372,7 +769,7 @@ void BLI_mempool_free(BLI_mempool *pool, void *addr) /* Enable for debugging. */ if (UNLIKELY(mempool_debug_memset)) { - memset(addr, 255, pool->esize); + memset(addr, 255, pool->esize - POISON_REDZONE_SIZE); } #endif @@ -387,8 +784,16 @@ void BLI_mempool_free(BLI_mempool *pool, void *addr) newhead->next = pool->free; pool->free = newhead; + BLI_asan_poison(newhead, pool->esize); + pool->totused--; + if (pool->totused < 0) { + fprintf(stderr, "Corrupted mempool\n"); + fflush(stderr); + abort(); + } + #ifdef WITH_MEM_VALGRIND VALGRIND_MEMPOOL_FREE(pool, addr); #endif @@ -401,10 +806,12 @@ void BLI_mempool_free(BLI_mempool *pool, void *addr) BLI_mempool_chunk *first; first = pool->chunks; - mempool_chunk_free_all(first->next); + mempool_chunk_free_all(first->next, pool); first->next = NULL; pool->chunk_tail = first; + mempool_update_chunktable(pool); + #ifdef USE_TOTALLOC pool->totalloc = pool->pchunk; #endif @@ -419,28 +826,46 @@ void BLI_mempool_free(BLI_mempool *pool, void *addr) j = pool->pchunk; while (j--) { - curnode->next = NODE_STEP_NEXT(curnode); - curnode = curnode->next; + BLI_asan_unpoison(curnode, pool->esize - POISON_REDZONE_SIZE); + BLI_freenode *next = curnode->next = NODE_STEP_NEXT(curnode); + BLI_asan_poison(curnode, pool->esize); + curnode = next; } - curnode = NODE_STEP_PREV(curnode); + + BLI_asan_unpoison(curnode, pool->esize - POISON_REDZONE_SIZE); + BLI_freenode *prev = NODE_STEP_PREV(curnode); + BLI_asan_poison(curnode, pool->esize); + + curnode = prev; + + BLI_asan_unpoison(curnode, pool->esize - POISON_REDZONE_SIZE); curnode->next = NULL; /* terminate the list */ + BLI_asan_poison(curnode, pool->esize); #ifdef WITH_MEM_VALGRIND VALGRIND_MEMPOOL_FREE(pool, CHUNK_DATA(first)); #endif } + + mempool_poison(pool); } int BLI_mempool_len(const BLI_mempool *pool) { - return (int)pool->totused; + mempool_unpoison((BLI_mempool *)pool); + int ret = pool->totused; + mempool_poison((BLI_mempool *)pool); + + return ret; } void *BLI_mempool_findelem(BLI_mempool *pool, uint index) { + mempool_unpoison(pool); + BLI_assert(pool->flag & BLI_MEMPOOL_ALLOW_ITER); - if (index < pool->totused) { + if (index < (uint)pool->totused) { /* We could have some faster mem chunk stepping code inline. */ BLI_mempool_iter iter; void *elem; @@ -448,9 +873,12 @@ void *BLI_mempool_findelem(BLI_mempool *pool, uint index) for (elem = BLI_mempool_iterstep(&iter); index-- != 0; elem = BLI_mempool_iterstep(&iter)) { /* pass */ } + + mempool_poison(pool); return elem; } + mempool_poison(pool); return NULL; } @@ -459,12 +887,20 @@ void BLI_mempool_as_table(BLI_mempool *pool, void **data) BLI_mempool_iter iter; void *elem; void **p = data; + + mempool_unpoison(pool); BLI_assert(pool->flag & BLI_MEMPOOL_ALLOW_ITER); + mempool_poison(pool); + BLI_mempool_iternew(pool, &iter); + while ((elem = BLI_mempool_iterstep(&iter))) { *p++ = elem; } - BLI_assert((uint)(p - data) == pool->totused); + + mempool_unpoison(pool); + BLI_assert((int)(p - data) == pool->totused); + mempool_poison(pool); } void **BLI_mempool_as_tableN(BLI_mempool *pool, const char *allocstr) @@ -476,21 +912,29 @@ void **BLI_mempool_as_tableN(BLI_mempool *pool, const char *allocstr) void BLI_mempool_as_array(BLI_mempool *pool, void *data) { - const uint esize = pool->esize; + mempool_unpoison(pool); + + const uint esize = pool->esize - (uint)POISON_REDZONE_SIZE; BLI_mempool_iter iter; char *elem, *p = data; + BLI_assert(pool->flag & BLI_MEMPOOL_ALLOW_ITER); + + mempool_poison(pool); + BLI_mempool_iternew(pool, &iter); while ((elem = BLI_mempool_iterstep(&iter))) { memcpy(p, elem, (size_t)esize); p = NODE_STEP_NEXT(p); } - BLI_assert((uint)(p - (char *)data) == pool->totused * esize); } void *BLI_mempool_as_arrayN(BLI_mempool *pool, const char *allocstr) { - char *data = MEM_malloc_arrayN(pool->totused, pool->esize, allocstr); + mempool_unpoison(pool); + char *data = MEM_malloc_arrayN((size_t)pool->totused, pool->esize, allocstr); + mempool_poison(pool); + BLI_mempool_as_array(pool, data); return data; } @@ -500,7 +944,9 @@ void BLI_mempool_iternew(BLI_mempool *pool, BLI_mempool_iter *iter) BLI_assert(pool->flag & BLI_MEMPOOL_ALLOW_ITER); iter->pool = pool; + mempool_unpoison(pool); iter->curchunk = pool->chunks; + mempool_poison(pool); iter->curindex = 0; } @@ -580,12 +1026,16 @@ void *BLI_mempool_iterstep(BLI_mempool_iter *iter) return NULL; } + mempool_unpoison(iter->pool); + const uint esize = iter->pool->esize; BLI_freenode *curnode = POINTER_OFFSET(CHUNK_DATA(iter->curchunk), (esize * iter->curindex)); BLI_freenode *ret; do { ret = curnode; + BLI_asan_unpoison(ret, iter->pool->esize - POISON_REDZONE_SIZE); + if (++iter->curindex != iter->pool->pchunk) { curnode = POINTER_OFFSET(curnode, esize); } @@ -593,12 +1043,21 @@ void *BLI_mempool_iterstep(BLI_mempool_iter *iter) iter->curindex = 0; iter->curchunk = iter->curchunk->next; if (UNLIKELY(iter->curchunk == NULL)) { - return (ret->freeword == FREEWORD) ? NULL : ret; + BLI_asan_unpoison(ret, iter->pool->esize - POISON_REDZONE_SIZE); + void *ret2 = (ret->freeword == FREEWORD) ? NULL : ret; + + if (ret->freeword == FREEWORD) { + BLI_asan_poison(ret, iter->pool->esize); + } + + mempool_poison(iter->pool); + return ret2; } curnode = CHUNK_DATA(iter->curchunk); } } while (ret->freeword == FREEWORD); + mempool_poison(iter->pool); return ret; } @@ -609,12 +1068,16 @@ void *mempool_iter_threadsafe_step(BLI_mempool_threadsafe_iter *ts_iter) return NULL; } + mempool_unpoison(iter->pool); + const uint esize = iter->pool->esize; BLI_freenode *curnode = POINTER_OFFSET(CHUNK_DATA(iter->curchunk), (esize * iter->curindex)); BLI_freenode *ret; do { ret = curnode; + BLI_asan_unpoison(ret, esize - POISON_REDZONE_SIZE); + if (++iter->curindex != iter->pool->pchunk) { curnode = POINTER_OFFSET(curnode, esize); } @@ -630,25 +1093,52 @@ void *mempool_iter_threadsafe_step(BLI_mempool_threadsafe_iter *ts_iter) /* pass. */ } if (UNLIKELY(iter->curchunk == NULL)) { - return (ret->freeword == FREEWORD) ? NULL : ret; + if (ret->freeword == FREEWORD) { + BLI_asan_poison(ret, esize); + mempool_poison(iter->pool); + return NULL; + } + else { + mempool_poison(iter->pool); + return ret; + } } /* End `threadsafe` exception. */ iter->curchunk = iter->curchunk->next; if (UNLIKELY(iter->curchunk == NULL)) { - return (ret->freeword == FREEWORD) ? NULL : ret; + if (ret->freeword == FREEWORD) { + BLI_asan_poison(ret, iter->pool->esize); + mempool_poison(iter->pool); + return NULL; + } + else { + mempool_poison(iter->pool); + return ret; + } } + curnode = CHUNK_DATA(iter->curchunk); } - } while (ret->freeword == FREEWORD); + if (ret->freeword == FREEWORD) { + BLI_asan_poison(ret, iter->pool->esize); + } + else { + break; + } + } while (true); + + mempool_poison(iter->pool); return ret; } #endif -void BLI_mempool_clear_ex(BLI_mempool *pool, const int totelem_reserve) +void BLI_mempool_clear_ex(BLI_mempool *pool, const int elem_num_reserve) { + mempool_unpoison(pool); + BLI_mempool_chunk *mpchunk; BLI_mempool_chunk *mpchunk_next; uint maxchunks; @@ -661,11 +1151,11 @@ void BLI_mempool_clear_ex(BLI_mempool *pool, const int totelem_reserve) VALGRIND_CREATE_MEMPOOL(pool, 0, false); #endif - if (totelem_reserve == -1) { + if (elem_num_reserve == -1) { maxchunks = pool->maxchunks; } else { - maxchunks = mempool_maxchunks((uint)totelem_reserve, pool->pchunk); + maxchunks = mempool_maxchunks((uint)elem_num_reserve, pool->pchunk); } /* Free all after 'pool->maxchunks'. */ @@ -678,7 +1168,7 @@ void BLI_mempool_clear_ex(BLI_mempool *pool, const int totelem_reserve) do { mpchunk_next = mpchunk->next; - mempool_chunk_free(mpchunk); + mempool_chunk_free(mpchunk, pool); } while ((mpchunk = mpchunk_next)); } @@ -697,6 +1187,8 @@ void BLI_mempool_clear_ex(BLI_mempool *pool, const int totelem_reserve) chunks_temp = mpchunk->next; last_tail = mempool_chunk_add(pool, mpchunk, last_tail); } + + mempool_poison(pool); } void BLI_mempool_clear(BLI_mempool *pool) @@ -706,12 +1198,26 @@ void BLI_mempool_clear(BLI_mempool *pool) void BLI_mempool_destroy(BLI_mempool *pool) { - mempool_chunk_free_all(pool->chunks); + mempool_unpoison(pool); + mempool_chunk_free_all(pool->chunks, pool); + + if (pool->memtag) { + free(pool->memtag); + } + if (pool->memtag_chunk) { + free(pool->memtag_chunk); + } + + MEM_SAFE_FREE(pool->chunktable); #ifdef WITH_MEM_VALGRIND VALGRIND_DESTROY_MEMPOOL(pool); #endif +#ifdef HAVE_MEMPOOL_ASAN + BLI_asan_unpoison(pool->poisoned, sizeof(pool->poisoned)); +#endif + MEM_freeN(pool); } diff --git a/source/blender/blenlib/intern/BLI_table_gset.c b/source/blender/blenlib/intern/BLI_table_gset.c new file mode 100644 index 00000000000..88e3cc133b1 --- /dev/null +++ b/source/blender/blenlib/intern/BLI_table_gset.c @@ -0,0 +1,153 @@ +#include "MEM_guardedalloc.h" + +#include "BLI_compiler_attrs.h" +#include "BLI_compiler_compat.h" +# +#include "BLI_smallhash.h" +#include "BLI_utildefines.h" + +#include "BLI_ghash.h" + +//#define PTR_TO_IDX(ts) ((GHash *)ts->ptr_to_idx.buckets) +#define PTR_TO_IDX(ts) &(ts)->ptr_to_idx + +TableGSet *BLI_table_gset_new(const char *info) +{ + TableGSet *ts = MEM_callocN(sizeof(TableGSet), info); + + // ts->ptr_to_idx.buckets = (void *)BLI_ghash_ptr_new(info); + BLI_smallhash_init(&ts->ptr_to_idx); + + return ts; +} + +TableGSet *BLI_table_gset_new_ex(const char *info, int size) +{ + TableGSet *ts = MEM_callocN(sizeof(TableGSet), info); + + // ts->ptr_to_idx.buckets = (void *)BLI_ghash_ptr_new_ex(info, (uint)size); + BLI_smallhash_init_ex(&ts->ptr_to_idx, size); + + if (size) { + ts->elems = MEM_callocN(sizeof(void *) * (uint)size, info); + ts->size = size; + ts->length = 0; + ts->cur = 0; + } + + return ts; +} + +void BLI_table_gset_free(TableGSet *ts, GHashKeyFreeFP freefp) +{ + if (!PTR_TO_IDX(ts)) { + return; + } + + if (ts->elems) { + MEM_freeN(ts->elems); + } + + // BLI_ghash_free(PTR_TO_IDX(ts), freefp, NULL); + BLI_smallhash_release(&ts->ptr_to_idx); + + MEM_freeN(ts); +} + +static void table_gset_resize(TableGSet *ts) +{ + if (ts->cur >= ts->size) { + uint newsize = (uint)(ts->cur + 1); + newsize = (newsize << 1U) - (newsize >> 1U); + newsize = MAX2(newsize, 8U); + + if (!ts->elems) { + ts->elems = (void *)MEM_mallocN(sizeof(void *) * newsize, "ts->elems"); + } + else { + ts->elems = (void *)MEM_reallocN(ts->elems, newsize * sizeof(void *)); + } + + // BLI_smallhash_clear(PTR_TO_IDX(ts), 0ULL); + + // compact + int i = 0, j = 0; + for (i = 0; i < ts->cur; i++) { + void *elem2 = ts->elems[i]; + + if (elem2) { + void **val; + BLI_smallhash_ensure_p(PTR_TO_IDX(ts), (uintptr_t)elem2, &val); + + // BLI_smallhash_insert(PTR_TO_IDX(ts), elem2, (void *)j); + *val = POINTER_FROM_INT(j); + + ts->elems[j++] = elem2; + } + } + + ts->size = (int)newsize; + ts->cur = j; + } +} + +bool BLI_table_gset_add(TableGSet *ts, void *elem) +{ + void **val; + + table_gset_resize(ts); + + bool ret = BLI_smallhash_ensure_p(PTR_TO_IDX(ts), (uintptr_t)elem, &val); + + if (!ret) { + *val = POINTER_FROM_INT(ts->cur); + + ts->elems[ts->cur++] = elem; + ts->length++; + } + + return ret; +} + +void BLI_table_gset_insert(TableGSet *ts, void *elem) +{ + table_gset_resize(ts); + + BLI_smallhash_insert(PTR_TO_IDX(ts), (uintptr_t)elem, (void *)ts->cur); + + ts->elems[ts->cur++] = elem; + ts->length++; +} + +void BLI_table_gset_remove(TableGSet *ts, void *elem, GHashKeyFreeFP freefp) +{ + if (!elem || !ts) { + return; + } + + int *idx = (int *)BLI_smallhash_lookup_p(PTR_TO_IDX(ts), (uintptr_t)elem); + if (!idx) { + return; + } + + int idx2 = *idx; + + BLI_smallhash_remove(PTR_TO_IDX(ts), (uintptr_t)elem); + + if (!ts->elems || ts->elems[idx2] != elem) { + return; + } + + ts->length--; + ts->elems[idx2] = NULL; +} + +bool BLI_table_gset_haskey(TableGSet *ts, void *elem) +{ + return BLI_smallhash_haskey(PTR_TO_IDX(ts), (uintptr_t)elem); +} + +int BLI_table_gset_len(TableGSet *ts) +{ + return ts->length; +} diff --git a/source/blender/blenlib/intern/heap_minmax.c b/source/blender/blenlib/intern/heap_minmax.c new file mode 100644 index 00000000000..4367ac0dff1 --- /dev/null +++ b/source/blender/blenlib/intern/heap_minmax.c @@ -0,0 +1,642 @@ +#include "MEM_guardedalloc.h" + +#include "BLI_heap_minmax.h" +#include "BLI_mempool.h" +#include "BLI_utildefines.h" + +#include "BLI_compiler_attrs.h" +#include "BLI_compiler_compat.h" + +typedef struct MinMaxHeapNode { + void *ptr; + float value; + int child1, child2, parent; +} MinMaxHeapNode; + +typedef struct MinMaxHeap { + MinMaxHeapNode *nodes; + int totnode, reserved; +} MinMaxHeap; + +static void heap_reserve(MinMaxHeap *heap, unsigned int n) +{ + if (heap->reserved >= n) { + return; + } + + n++; + n += n >> 1; + + if (heap->nodes) { + heap->nodes = MEM_recallocN(heap->nodes, sizeof(MinMaxHeapNode) * n); + } + else { + heap->nodes = MEM_calloc_arrayN(n, sizeof(MinMaxHeapNode), "MinMaxHeap nodes"); + } + + heap->reserved = n; +} + +/** + * Creates a new heap. Removed nodes are recycled, so memory usage will not shrink. + * + * \note Use when the size of the heap is known in advance. + */ +MinMaxHeap *BLI_mm_heap_new_ex(unsigned int tot_reserve) +{ + MinMaxHeap *heap = MEM_callocN(sizeof(*heap), __func__); + if (tot_reserve) { + heap_reserve(heap, tot_reserve); + } + + return heap; +} + +MinMaxHeap *BLI_mm_heap_new() +{ + return BLI_mm_heap_new_ex(256); +} + +void BLI_mm_heap_clear(MinMaxHeap *heap, MinMaxHeapFreeFP ptrfreefp) +{ + if (ptrfreefp) { + for (int i = 0; i < heap->totnode; i++) { + ptrfreefp(heap->nodes[i].ptr); + } + } + + MEM_SAFE_FREE(heap->nodes); + heap->totnode = 0; +} + +void BLI_mm_heap_free(MinMaxHeap *heap, MinMaxHeapFreeFP ptrfreefp) +{ + BLI_mm_heap_clear(heap, ptrfreefp); + MEM_freeN(heap); +} + +static MinMaxHeapNode *heap_make_node(MinMaxHeap *heap) +{ + heap_reserve(heap, heap->totnode + 1); + + MinMaxHeapNode *node = heap->nodes + heap->totnode; + node->child1 = node->child2 = node->parent = -1; + + heap->totnode++; + + return node; +} + +static MinMaxHeapNode *heap_descent_min2(MinMaxHeap *heap, MinMaxHeapNode *n) +{ + if (n->child1 != -1 && n->child2 != -1) { + MinMaxHeapNode *n1 = heap->nodes + n->child1; + MinMaxHeapNode *n2 = heap->nodes + n->child2; + + return n1->value < n2->value ? n1 : n2; + } + else if (n->child1 != -1) { + return heap->nodes + n->child1; + } + else if (n->child2 != -1) { + return heap->nodes + n->child2; + } + + return n; +} + +/* find node grandchild */ +static MinMaxHeapNode *heap_descent_min(MinMaxHeap *heap, MinMaxHeapNode *node) +{ + if (node->child1 != -1 && node->child2 != -1) { + MinMaxHeapNode *n1 = heap->nodes + node->child1; + MinMaxHeapNode *n2 = heap->nodes + node->child2; + + n1 = heap_descent_min2(heap, n1); + n2 = heap_descent_min2(heap, n2); + + return n1->value < n2->value ? n1 : n2; + } + else if (node->child1 != -1) { + return heap_descent_min2(heap, heap->nodes + node->child1); + } + else if (node->child2 != -1) { + return heap_descent_min2(heap, heap->nodes + node->child2); + } + + return NULL; +} + +static MinMaxHeapNode *heap_descent_max2(MinMaxHeap *heap, MinMaxHeapNode *n) +{ + if (n->child1 != -1 && n->child2 != -1) { + MinMaxHeapNode *n1 = heap->nodes + n->child1; + MinMaxHeapNode *n2 = heap->nodes + n->child2; + + return n1->value > n2->value ? n1 : n2; + } + else if (n->child1 != -1) { + return heap->nodes + n->child1; + } + else if (n->child2 != -1) { + return heap->nodes + n->child2; + } + + return n; +} + +/* find node grandchild */ +static MinMaxHeapNode *heap_descent_max(MinMaxHeap *heap, MinMaxHeapNode *node) +{ + if (node->child1 != -1 && node->child2 != -1) { + MinMaxHeapNode *n1 = heap->nodes + node->child1; + MinMaxHeapNode *n2 = heap->nodes + node->child2; + + n1 = heap_descent_max2(heap, n1); + n2 = heap_descent_max2(heap, n2); + + return n1->value > n2->value ? n1 : n2; + } + else if (node->child1 != -1) { + return heap_descent_max2(heap, heap->nodes + node->child1); + } + else if (node->child2 != -1) { + return heap_descent_max2(heap, heap->nodes + node->child2); + } + + return NULL; +} + +static MinMaxHeapNode *heap_push_down_min(MinMaxHeap *heap, MinMaxHeapNode *node) +{ + MinMaxHeapNode *ret = NULL; + + while (node->child1 >= 0 || node->child2 >= 0) { + MinMaxHeapNode *node2 = heap_descent_min(heap, node); + + if (!node2) { + break; + } + + if (node2->value < node->value) { + SWAP(float, node2->value, node->value); + SWAP(void *, node2->ptr, node->ptr); + + if (node2->parent != node - heap->nodes) { + MinMaxHeapNode *parent = heap->nodes + node2->parent; + + if (node2->value > parent->value) { + SWAP(float, node2->value, parent->value); + SWAP(void *, node2->ptr, parent->ptr); + + /* this is a bit tricky, our return node has now + moved into the other interleaved heap side */ + if (!ret) { + ret = parent; + } + } + } + + node = node2; + } + else { + break; + } + } + + return ret ? ret : node; +} + +static MinMaxHeapNode *heap_push_down_max(MinMaxHeap *heap, MinMaxHeapNode *node) +{ + MinMaxHeapNode *ret = NULL; + + while (node->child1 >= 0 || node->child2 >= 0) { + MinMaxHeapNode *node2 = heap_descent_max(heap, node); + + if (node2->value > node->value) { + SWAP(float, node2->value, node->value); + SWAP(void *, node2->ptr, node->ptr); + + if (node2->parent != node - heap->nodes) { + MinMaxHeapNode *parent = heap->nodes + node2->parent; + + if (node2->value < parent->value) { + SWAP(float, node2->value, parent->value); + SWAP(void *, node2->ptr, parent->ptr); + + /* this is a bit tricky, our return node has now + moved into the other interleaved heap side */ + if (!ret) { + ret = parent; + } + } + } + + node = node2; + } + else { + break; + } + } + + return ret ? ret : node; +} + +static int heap_get_level(const MinMaxHeap *heap, const MinMaxHeapNode *node) +{ + int i = 0; + + while (node->parent != -1) { + node = heap->nodes + node->parent; + i++; + } + + return i; +} + +static MinMaxHeapNode *heap_push_down(MinMaxHeap *heap, MinMaxHeapNode *node) +{ + int i = heap_get_level(heap, node); + + if (i & 1) { + return heap_push_down_max(heap, node); + } + else { + return heap_push_down_min(heap, node); + } +} + +static MinMaxHeapNode *heap_push_up_min(MinMaxHeap *heap, MinMaxHeapNode *node) +{ + while (node->parent != -1) { + MinMaxHeapNode *parent = heap->nodes + node->parent; + + if (parent->parent != -1 && node->value < heap->nodes[parent->parent].value) { + parent = heap->nodes + parent->parent; + + SWAP(float, node->value, parent->value); + SWAP(void *, node->ptr, parent->ptr); + + node = parent; + } + else { + break; + } + } + + return node; +} + +static MinMaxHeapNode *heap_push_up_max(MinMaxHeap *heap, MinMaxHeapNode *node) +{ + while (node->parent != -1) { + MinMaxHeapNode *parent = heap->nodes + node->parent; + + if (parent->parent != -1 && node->value > heap->nodes[parent->parent].value) { + parent = heap->nodes + parent->parent; + + SWAP(float, node->value, parent->value); + SWAP(void *, node->ptr, parent->ptr); + + node = parent; + } + else { + break; + } + } + + return node; +} + +static MinMaxHeapNode *heap_push_up(MinMaxHeap *heap, MinMaxHeapNode *node) +{ + int i = heap_get_level(heap, node); + + if ((i & 1) == 0) { + MinMaxHeapNode *parent = heap->nodes + node->parent; + + if (node->value > parent->value) { + SWAP(float, node->value, parent->value); + SWAP(void *, node->ptr, parent->ptr); + + return heap_push_up_max(heap, parent); + } + else { + return heap_push_up_min(heap, node); + } + } + else { + MinMaxHeapNode *parent = heap->nodes + node->parent; + + if (node->value < parent->value) { + SWAP(float, node->value, parent->value); + SWAP(void *, node->ptr, parent->ptr); + + return heap_push_up_min(heap, parent); + } + else { + return heap_push_up_max(heap, node); + } + } + + return node; +} + +/** + * Insert heap node with a value (often a 'cost') and pointer into the heap, + * duplicate values are allowed. + */ +MinMaxHeapNode *BLI_mm_heap_insert(MinMaxHeap *heap, float value, void *ptr) +{ + MinMaxHeapNode *node = heap_make_node(heap); + + node->ptr = ptr; + node->value = value; + + if (heap->totnode == 1) { + return node; + } + + int i = node - heap->nodes; + + node->parent = (i - 1) >> 1; + + MinMaxHeapNode *parent = heap->nodes + node->parent; + if (parent->child1 == -1) { + parent->child1 = i; + } + else { + parent->child2 = i; + } + + return heap_push_up(heap, node); +} + +/** + * Convenience function since this is a common pattern. + */ +void BLI_mm_heap_insert_or_update(MinMaxHeap *heap, + MinMaxHeapNode **node_p, + float value, + void *ptr) +{ + MinMaxHeapNode *node = *node_p; + + if (!node) { + *node_p = BLI_mm_heap_insert(heap, value, ptr); + return; + } + + node = heap_push_down(heap, node); + node = heap_push_up(heap, node); + + *node_p = node; +} + +bool BLI_mm_heap_is_empty(const MinMaxHeap *heap) +{ + return heap->totnode == 0; +} + +unsigned int BLI_mm_heap_len(const MinMaxHeap *heap) +{ + return heap->totnode; +} + +MinMaxHeapNode *BLI_mm_heap_min(const MinMaxHeap *heap) +{ + return heap->nodes; +} + +float BLI_mm_heap_min_value(const MinMaxHeap *heap) +{ + return heap->nodes[0].value; +} + +MinMaxHeapNode *BLI_mm_heap_max(const MinMaxHeap *heap) +{ + if (heap->totnode == 1) { + return heap->nodes; + } + + if (heap->nodes[0].child1 != -1) { + return heap->nodes + heap->nodes[0].child1; + } + else { + return heap->nodes + heap->nodes[0].child2; + } +} + +float BLI_mm_heap_max_value(const MinMaxHeap *heap) +{ + return BLI_mm_heap_max(heap)->value; +} + +static MinMaxHeapNode *heap_pop_last(MinMaxHeap *heap) +{ + MinMaxHeapNode *last = heap->nodes + heap->totnode - 1; + + if (last->parent) { + MinMaxHeapNode *parent = heap->nodes + last->parent; + if (parent->child1 == heap->totnode - 1) { + parent->child1 = -1; + } + else { + parent->child2 = -1; + } + } + + heap->totnode--; + + return last; +} + +void *BLI_mm_heap_pop_min(MinMaxHeap *heap) +{ + void *ret = heap->nodes[0].ptr; + + if (heap->totnode == 1) { + heap->totnode--; + return ret; + } + +#ifdef BLI_MINMAX_HEAP_VALIDATE + if (!BLI_mm_heap_is_valid(heap)) { + printf("invalid heap!\n"); + } +#endif + + MinMaxHeapNode *last = heap_pop_last(heap); + + SWAP(float, last->value, heap->nodes->value); + SWAP(void *, last->ptr, heap->nodes->ptr); + + heap_push_down(heap, heap->nodes); + +#ifdef BLI_MINMAX_HEAP_VALIDATE + if (!BLI_mm_heap_is_valid(heap)) { + printf("invalid heap!\n"); + } +#endif + + return ret; +} + +void *BLI_mm_heap_pop_max(MinMaxHeap *heap) +{ + MinMaxHeapNode *node = BLI_mm_heap_max(heap); + + void *ret = node->ptr; + + if (heap->totnode == 1) { + heap->totnode--; + return ret; + } + +#ifdef BLI_MINMAX_HEAP_VALIDATE + if (!BLI_mm_heap_is_valid(heap)) { + printf("invalid heap!\n"); + } +#endif + + MinMaxHeapNode *last = heap_pop_last(heap); + + node->value = last->value; + node->ptr = last->ptr; + + // node = heap_push_up(heap, node); + node = heap_push_down(heap, node); + // heap_push_down(heap, heap->nodes); + +#if 0 + if (heap->nodes[0].child2 != -1 && heap->nodes[0].child1 != 0) { + MinMaxHeapNode *n1 = heap->nodes + heap->nodes[0].child1; + MinMaxHeapNode *n2 = heap->nodes + heap->nodes[0].child2; + + if (n1->value < n2->value) { + SWAP(float, n1->value, n2->value); + SWAP(void *, n1->ptr, n2->ptr); + + heap_push_down(heap, n1); + heap_push_down(heap, n2); + } + } +#endif + +#ifdef BLI_MINMAX_HEAP_VALIDATE + if (!BLI_mm_heap_is_valid(heap)) { + printf("invalid heap!\n"); + } +#endif + + return ret; +} + +MinMaxHeapNode *BLI_mm_heap_node_value_update(MinMaxHeap *heap, MinMaxHeapNode *node, float value) +{ + node->value = value; + + node = heap_push_down(heap, node); + node = heap_push_up(heap, node); + + return node; +} + +MinMaxHeapNode *BLI_mm_heap_node_value_update_ptr(MinMaxHeap *heap, + MinMaxHeapNode *node, + float value, + void *ptr) +{ + node->value = value; + node->ptr = ptr; + + node = heap_push_down(heap, node); + node = heap_push_up(heap, node); + + return node; +} + +/** + * Return the value or pointer of a heap node. + */ +float BLI_mm_heap_node_value(const MinMaxHeapNode *node) +{ + return node->value; +} + +void *BLI_mm_heap_node_ptr(const MinMaxHeapNode *node) +{ + return node->ptr; +} +/** + * Only for checking internal errors (gtest). + */ +bool BLI_mm_heap_is_valid(const MinMaxHeap *heap) +{ + for (int i = 0; i < heap->totnode; i++) { + MinMaxHeapNode *node = heap->nodes + i; + + int level = heap_get_level(heap, node); + + if (i > 0 && (node->parent < 0 || node->parent >= heap->totnode)) { + return false; + } + + if (i < 3) { + continue; + } + + MinMaxHeapNode *parent = heap->nodes + node->parent; + + if (parent->parent < 0 || parent->parent >= heap->totnode) { + return false; + } + + parent = heap->nodes + parent->parent; + bool test = parent->value < node->value; + + if (parent->value == node->value) { + continue; + } + + test = test ^ (level & 1); + + if (!test) { + return false; + } + } + + return true; +} + +#include "BLI_rand.h" + +void test_mm_heap() +{ + MinMaxHeap *heap = BLI_mm_heap_new(); + RNG *rng = BLI_rng_new(0); + const int steps = 1024; + void *ptr = NULL; + + for (int i = 0; i < steps; i++) { + float f = floorf(BLI_rng_get_float(rng) * 10.0); + + BLI_mm_heap_insert(heap, f, ptr); + } + + for (int i = 0; i < steps; i++) { + if (i & 1) { + BLI_mm_heap_pop_max(heap); + } + else { + BLI_mm_heap_pop_min(heap); + } + + BLI_mm_heap_is_valid(heap); + } + + BLI_rng_free(rng); + BLI_mm_heap_is_valid(heap); + BLI_mm_heap_free(heap, NULL); +} diff --git a/source/blender/blenlib/intern/lockfree_mempool.cc b/source/blender/blenlib/intern/lockfree_mempool.cc new file mode 100644 index 00000000000..055de7b6a6f --- /dev/null +++ b/source/blender/blenlib/intern/lockfree_mempool.cc @@ -0,0 +1,374 @@ +#include +#include +#include + +#include "BLI_compiler_attrs.h" +#include "BLI_compiler_compat.h" + +#include "atomic_ops.h" //for one atomic variable exposed to C, curchunk_threaded_shared in BLI_lfmempool_iter +#include +#include + +#include "MEM_guardedalloc.h" + +#include "BLI_mempool_lockfree.h" + +/* NOTE: copied from BLO_blend_defs.h, don't use here because we're in BLI. */ +#ifdef __BIG_ENDIAN__ +/* Big Endian */ +# define MAKE_ID(a, b, c, d) ((int)(a) << 24 | (int)(b) << 16 | (c) << 8 | (d)) +# define MAKE_ID_8(a, b, c, d, e, f, g, h) \ + ((int64_t)(a) << 56 | (int64_t)(b) << 48 | (int64_t)(c) << 40 | (int64_t)(d) << 32 | \ + (int64_t)(e) << 24 | (int64_t)(f) << 16 | (int64_t)(g) << 8 | (h)) +#else +/* Little Endian */ +# define MAKE_ID(a, b, c, d) ((int)(d) << 24 | (int)(c) << 16 | (b) << 8 | (a)) +# define MAKE_ID_8(a, b, c, d, e, f, g, h) \ + ((int64_t)(h) << 56 | (int64_t)(g) << 48 | (int64_t)(f) << 40 | (int64_t)(e) << 32 | \ + (int64_t)(d) << 24 | (int64_t)(c) << 16 | (int64_t)(b) << 8 | (a)) +#endif + +/** + * Important that this value is an is _not_ aligned with `sizeof(void *)`. + * So having a pointer to 2/4/8... aligned memory is enough to ensure + * the `freeword` will never be used. + * To be safe, use a word that's the same in both directions. + */ +#define FREEWORD \ + ((sizeof(void *) > sizeof(int32_t)) ? MAKE_ID_8('e', 'e', 'r', 'f', 'f', 'r', 'e', 'e') : \ + MAKE_ID('e', 'f', 'f', 'e')) + +/** + * The 'used' word just needs to be set to something besides FREEWORD. + */ +#define USEDWORD MAKE_ID('u', 's', 'e', 'd') + +namespace blender { + +struct LockFreeElem { + struct LockFreeElem *next; + uintptr_t freeword; +}; + +struct LockFreeChunk { + struct LockFreeChunk *next, *prev; + + // we are convienently aligned to 16 bytes here +}; + +static void *data_from_chunk(LockFreeChunk *chunk) +{ + return reinterpret_cast(chunk + 1); +} + +static LockFreeElem *elem_from_chunk(LockFreeChunk *chunk) +{ + return reinterpret_cast(data_from_chunk(chunk)); +} + +static LockFreeElem *elem_next(LockFreeElem *elem, int esize) +{ + char *ptr = reinterpret_cast(elem); + ptr += esize; + return reinterpret_cast(ptr); +} + +static_assert(sizeof(std::atomic) == sizeof(void *), "std:atomic has space overhead!"); + +struct LockFreePool { + struct { + std::atomic first; + std::atomic last; + } chunks; + + std::atomic totchunk; + std::atomic totused; + + std::atomic free_elem; + + int esize, psize, csize; + + LockFreePool(int _esize, int psize) : psize(psize) + { + esize = std::max(_esize, (int)(sizeof(void *) * 2)); + + if (esize & 8) { + esize += 8 - (esize & 7); + } + + csize = esize * psize + sizeof(LockFreeChunk); + totused.store(0); + + free_elem.store(nullptr); + chunks.first.store(nullptr); + chunks.last.store(nullptr); + } + + ~LockFreePool() + { + LockFreeChunk *chunk, *next; + + for (chunk = chunks.last; chunk; chunk = next) { + next = chunk->prev; + + MEM_freeN(reinterpret_cast(chunk)); + } + } + + void add_chunk() + { + LockFreeChunk *chunk = reinterpret_cast(MEM_mallocN(csize, "LockFreeChunk")); + LockFreeElem *elem = elem_from_chunk(chunk), *last; + LockFreeElem *first = elem; + + chunk->next = chunk->prev = nullptr; + + for (int i = 0; i < psize; i++, elem = elem_next(elem, esize)) { + elem->freeword = FREEWORD; + + if (i == psize - 1) { + last = elem; + elem->next = nullptr; + } + else { + elem->next = elem_next(elem, esize); + } + } + + // last->next = free_elem + // free_elem = last; + + while (1) { + last->next = free_elem.load(); + + if (free_elem.compare_exchange_strong(last->next, first)) { + break; + } + } + + while (1) { + chunk->prev = chunks.last.load(); + + if (chunks.last.compare_exchange_strong(chunk->prev, chunk)) { + if (!chunk->prev) { + // chunks.first is not accessed in threading cases, only when pool + // is destroyed + chunks.first.store(chunk); + } + else { + chunk->prev->next = chunk; + } + break; + } + } + } + + void *alloc() + { + totused++; + + while (1) { + if (!free_elem.load()) { + add_chunk(); + } + + LockFreeElem *cur = free_elem.load(); + + if (free_elem.compare_exchange_strong(cur, cur->next)) { + cur->freeword = 0; + return reinterpret_cast(cur); + } + } + } + + void free(void *mem) + { + totused--; + LockFreeElem *elem = reinterpret_cast(mem); + + elem->freeword = FREEWORD; + + while (!free_elem.compare_exchange_strong(elem->next, elem)) { + } + } +}; + +static LockFreePool *cast_pool(BLI_lfmempool *pool) +{ + return reinterpret_cast(pool); +} + +extern "C" { + +BLI_lfmempool *BLI_lfmempool_create(int esize, int psize) +{ + LockFreePool *pool = MEM_new(__func__, esize, psize); + return reinterpret_cast(pool); +} + +void BLI_lfmempool_destroy(BLI_lfmempool *pool) +{ + MEM_delete(cast_pool(pool)); +} + +void *BLI_lfmempool_alloc(BLI_lfmempool *pool) +{ + return cast_pool(pool)->alloc(); +} + +void *BLI_lfmempool_calloc(BLI_lfmempool *_pool) +{ + void *mem = BLI_lfmempool_alloc(_pool); + LockFreePool *pool = cast_pool(_pool); + + memset(mem, 0, pool->esize); + + return mem; +} + +void BLI_lfmempool_free(BLI_lfmempool *pool, void *mem) +{ + return cast_pool(pool)->free(mem); +} + +void BLI_lfmempool_iternew(BLI_lfmempool *_pool, BLI_lfmempool_iter *iter) +{ + LockFreePool *pool = cast_pool(_pool); + iter->pool = _pool; + iter->chunk = reinterpret_cast(pool->chunks.first.load()); + iter->i = 0; + iter->curchunk_threaded_shared = nullptr; +} + +static void *chunk_next(void *vchunk) +{ + LockFreeChunk *chunk = reinterpret_cast(vchunk); + + return reinterpret_cast(chunk->next); +} + +void *BLI_lfmempool_iterstep_threadsafe(BLI_lfmempool_iter *iter) +{ + if (!iter->chunk) { + return nullptr; + } + + LockFreePool *pool = cast_pool(iter->pool); + LockFreeChunk *chunk = reinterpret_cast(iter->chunk); + + char *data = reinterpret_cast(data_from_chunk(chunk)); + void *ret = reinterpret_cast(data + pool->esize * iter->i); + + iter->i++; + + if (iter->i >= pool->psize) { + iter->i = 0; + /* Begin unique to the `threadsafe` version of this function. */ + for (iter->chunk = *iter->curchunk_threaded_shared; + (iter->chunk != NULL) && (atomic_cas_ptr((void **)iter->curchunk_threaded_shared, + iter->chunk, + chunk_next(iter->chunk)) != iter->chunk); + iter->chunk = *iter->curchunk_threaded_shared) { + /* pass. */ + } + iter->chunk = reinterpret_cast(chunk->next); + } + + LockFreeElem *elem = reinterpret_cast(ret); + if (elem->freeword == FREEWORD) { + return BLI_lfmempool_iterstep_threadsafe(iter); + } + + return ret; +} + +void *BLI_lfmempool_iterstep(BLI_lfmempool_iter *iter) +{ + if (!iter->chunk) { + return nullptr; + } + + LockFreePool *pool = cast_pool(iter->pool); + LockFreeChunk *chunk = reinterpret_cast(iter->chunk); + + char *data = reinterpret_cast(data_from_chunk(chunk)); + void *ret = reinterpret_cast(data + pool->esize * iter->i); + + iter->i++; + + if (iter->i >= pool->psize) { + iter->i = 0; + iter->chunk = reinterpret_cast(chunk->next); + } + + LockFreeElem *elem = reinterpret_cast(ret); + if (elem->freeword == FREEWORD) { + return BLI_lfmempool_iterstep(iter); + } + + return ret; +} + +void *BLI_lfmempool_findelem(BLI_lfmempool *pool, int index) +{ + BLI_lfmempool_iter iter; + BLI_lfmempool_iternew(pool, &iter); + void *item = BLI_lfmempool_iterstep(&iter); + + int i = 0; + + for (; item; item = BLI_lfmempool_iterstep(&iter), i++) { + if (i == index) { + return item; + } + } + + return nullptr; +} + +/** + * Initialize an array of mempool iterators, #BLI_MEMPOOL_ALLOW_ITER flag must be set. + * + * This is used in threaded code, to generate as much iterators as needed + * (each task should have its own), + * such that each iterator goes over its own single chunk, + * and only getting the next chunk to iterate over has to be + * protected against concurrency (which can be done in a lockless way). + * + * To be used when creating a task for each single item in the pool is totally overkill. + * + * See BLI_task_parallel_mempool implementation for detailed usage example. + */ +ParallelLFMempoolTaskData *lfmempool_iter_threadsafe_create(BLI_lfmempool *pool, + const size_t num_iter) +{ + BLI_assert(pool->flag & BLI_MEMPOOL_ALLOW_ITER); + + ParallelLFMempoolTaskData *iter_arr = (ParallelLFMempoolTaskData *)MEM_mallocN( + sizeof(*iter_arr) * num_iter, __func__); + LockFreeChunk **curchunk_threaded_shared = (LockFreeChunk **)MEM_mallocN(sizeof(void *), + __func__); + + BLI_lfmempool_iternew(pool, &iter_arr->ts_iter); + + *curchunk_threaded_shared = reinterpret_cast(iter_arr->ts_iter.chunk); + iter_arr->ts_iter.curchunk_threaded_shared = reinterpret_cast(curchunk_threaded_shared); + for (size_t i = 1; i < num_iter; i++) { + iter_arr[i].ts_iter = iter_arr[0].ts_iter; + *curchunk_threaded_shared = ((*curchunk_threaded_shared) ? (*curchunk_threaded_shared)->next : + NULL); + iter_arr[i].ts_iter.chunk = reinterpret_cast(*curchunk_threaded_shared); + } + + return iter_arr; +} + +void lfmempool_iter_threadsafe_destroy(ParallelLFMempoolTaskData *iter_arr) +{ + BLI_assert(iter_arr->ts_iter.curchunk_threaded_shared != NULL); + + MEM_freeN(iter_arr->ts_iter.curchunk_threaded_shared); + MEM_freeN(iter_arr); +} +} +} // namespace blender diff --git a/source/blender/blenlib/intern/smallhash.c b/source/blender/blenlib/intern/smallhash.c index bcc866aaab6..cf80f7b30ca 100644 --- a/source/blender/blenlib/intern/smallhash.c +++ b/source/blender/blenlib/intern/smallhash.c @@ -1,6 +1,16 @@ /* SPDX-License-Identifier: GPL-2.0-or-later * Copyright 2008 Blender Foundation. All rights reserved. */ +#ifdef __GNUC__ +/* I can't even *cast* signed ints in gcc's sign-conversion warning? gcc 10.3.0 -joeedh */ +# pragma GCC diagnostic ignored "-Wsign-conversion" +#endif + +#ifdef __GNUC__ +/* I can't even *cast* signed ints in gcc's sign-conversion warning? gcc 10.3.0 -joeedh */ +# pragma GCC diagnostic ignored "-Wsign-conversion" +#endif + /** \file * \ingroup bli * @@ -39,20 +49,46 @@ #include "BLI_smallhash.h" +#include "BLI_asan.h" #include "BLI_strict_flags.h" -#define SMHASH_KEY_UNUSED ((uintptr_t)(UINTPTR_MAX - 0)) -#define SMHASH_CELL_FREE ((void *)(UINTPTR_MAX - 1)) -#define SMHASH_CELL_UNUSED ((void *)(UINTPTR_MAX - 2)) +/* NOTE: copied from BLO_blend_defs.h, don't use here because we're in BLI. */ +#ifdef __BIG_ENDIAN__ +/* Big Endian */ +# define MAKE_ID(a, b, c, d) ((int)(a) << 24 | (int)(b) << 16 | (c) << 8 | (d)) +# define MAKE_ID_8(a, b, c, d, e, f, g, h) \ + ((int64_t)(a) << 56 | (int64_t)(b) << 48 | (int64_t)(c) << 40 | (int64_t)(d) << 32 | \ + (int64_t)(e) << 24 | (int64_t)(f) << 16 | (int64_t)(g) << 8 | (h)) +#else +/* Little Endian */ +# define MAKE_ID(a, b, c, d) ((int)(d) << 24 | (int)(c) << 16 | (b) << 8 | (a)) +# define MAKE_ID_8(a, b, c, d, e, f, g, h) \ + ((int64_t)(h) << 56 | (int64_t)(g) << 48 | (int64_t)(f) << 40 | (int64_t)(e) << 32 | \ + (int64_t)(d) << 24 | (int64_t)(c) << 16 | (int64_t)(b) << 8 | (a)) +#endif + +#define SMHASH_KEY_UNUSED (uintptr_t)(MAKE_ID_8('s', 'm', 'h', 'k', 'u', 'n', 'u', 's')) +#define SMHASH_CELL_FREE (void *)(MAKE_ID_8('s', 'm', 'h', 'c', 'f', 'r', 'e', 'e')) +#define SMHASH_CELL_UNUSED (void *)(MAKE_ID_8('s', 'm', 'h', 'c', 'u', 'n', 'u', 's')) + +#define USE_REMOVE /* typically this re-assigns 'h' */ #define SMHASH_NEXT(h, hoff) \ - (CHECK_TYPE_INLINE(&(h), uint *), \ - CHECK_TYPE_INLINE(&(hoff), uint *), \ - ((h) + (((hoff) = ((hoff)*2) + 1), (hoff)))) + (CHECK_TYPE_INLINE(&(h), uintptr_t *), \ + CHECK_TYPE_INLINE(&(hoff), uintptr_t *), \ + ((h) + (((hoff) = ((hoff)*3) + 1), (hoff)))) -/* nothing uses BLI_smallhash_remove yet */ -// #define USE_REMOVE +BLI_INLINE bool check_stack_move(SmallHash *sh) +{ + if (sh->using_stack && sh->buckets != sh->buckets_stack) { + sh->buckets = sh->buckets_stack; + + return true; + } + + return false; +} BLI_INLINE bool smallhash_val_is_used(const void *val) { @@ -66,18 +102,45 @@ BLI_INLINE bool smallhash_val_is_used(const void *val) extern const uint BLI_ghash_hash_sizes[]; #define hashsizes BLI_ghash_hash_sizes -BLI_INLINE uint smallhash_key(const uintptr_t key) +int BLI_smallhash_memuse(SmallHash *sh) { - return (uint)key; + return (int)sh->nbuckets * (int)sizeof(SmallHashEntry) + (int)sizeof(SmallHash); } +#if 0 +BLI_INLINE uintptr_t smallhash_key(const uintptr_t key) +{ +# if 1 + return key; +# else + uintptr_t y = (size_t)key; + /* bottom 3 or 4 bits are likely to be 0; rotate y by 4 to avoid + * excessive hash collisions for dicts and sets */ + + return (uintptr_t)(y >> 4) | ((uintptr_t)y << (sizeof(uintptr_t[8]) - 4)); +# endif +} +#endif + +#ifdef _keyrot +# undef _keyrot +#endif + +#define _keyrot(y) ((uintptr_t)(y) >> 4) | ((uintptr_t)(y) << (sizeof(uintptr_t[8]) - 4)) +#define smallhash_key(key) sh->use_pointer_hash ? _keyrot(key) : (key) /** * Check if the number of items in the smallhash is large enough to require more buckets. */ -BLI_INLINE bool smallhash_test_expand_buckets(const uint nentries, const uint nbuckets) +BLI_INLINE bool smallhash_test_expand_buckets(const uint nentries, + const uint nbuckets, + const uint nfreecells) { + if (nfreecells < 3) { + return true; + } + /* (approx * 1.5) */ - return (nentries + (nentries >> 1)) > nbuckets; + return (nentries + (nentries >> 1)) > nbuckets || nfreecells < 3; } BLI_INLINE void smallhash_init_empty(SmallHash *sh) @@ -95,23 +158,26 @@ BLI_INLINE void smallhash_init_empty(SmallHash *sh) */ BLI_INLINE void smallhash_buckets_reserve(SmallHash *sh, const uint nentries_reserve) { - while (smallhash_test_expand_buckets(nentries_reserve, sh->nbuckets)) { + while (smallhash_test_expand_buckets(nentries_reserve, sh->nbuckets, sh->nbuckets + 5)) { sh->nbuckets = hashsizes[++sh->cursize]; + sh->nfreecells = sh->nbuckets; } } -BLI_INLINE SmallHashEntry *smallhash_lookup(const SmallHash *sh, const uintptr_t key) +BLI_INLINE SmallHashEntry *smallhash_lookup(SmallHash *sh, const uintptr_t key) { + check_stack_move(sh); + SmallHashEntry *e; - uint h = smallhash_key(key); - uint hoff = 1; + uintptr_t h = smallhash_key(key); + uintptr_t hoff = 1; BLI_assert(key != SMHASH_KEY_UNUSED); /* NOTE: there are always more buckets than entries, * so we know there will always be a free bucket if the key isn't found. */ - for (e = &sh->buckets[h % sh->nbuckets]; e->val != SMHASH_CELL_FREE; - h = SMHASH_NEXT(h, hoff), e = &sh->buckets[h % sh->nbuckets]) { + for (e = &sh->buckets[h % (uintptr_t)sh->nbuckets]; e->val != SMHASH_CELL_FREE; + h = SMHASH_NEXT(h, hoff), e = &sh->buckets[h % (uintptr_t)sh->nbuckets]) { if (e->key == key) { /* should never happen because unused keys are zero'd */ BLI_assert(e->val != SMHASH_CELL_UNUSED); @@ -124,12 +190,14 @@ BLI_INLINE SmallHashEntry *smallhash_lookup(const SmallHash *sh, const uintptr_t BLI_INLINE SmallHashEntry *smallhash_lookup_first_free(SmallHash *sh, const uintptr_t key) { - SmallHashEntry *e; - uint h = smallhash_key(key); - uint hoff = 1; + check_stack_move(sh); - for (e = &sh->buckets[h % sh->nbuckets]; smallhash_val_is_used(e->val); - h = SMHASH_NEXT(h, hoff), e = &sh->buckets[h % sh->nbuckets]) { + SmallHashEntry *e; + uintptr_t h = smallhash_key(key); + uintptr_t hoff = 1; + + for (e = &sh->buckets[h % (uintptr_t)sh->nbuckets]; smallhash_val_is_used(e->val); + h = SMHASH_NEXT(h, hoff), e = &sh->buckets[h % (uintptr_t)sh->nbuckets]) { /* pass */ } @@ -138,6 +206,8 @@ BLI_INLINE SmallHashEntry *smallhash_lookup_first_free(SmallHash *sh, const uint BLI_INLINE void smallhash_resize_buckets(SmallHash *sh, const uint nbuckets) { + check_stack_move(sh); + SmallHashEntry *buckets_old = sh->buckets; const uint nbuckets_old = sh->nbuckets; const bool was_alloc = (buckets_old != sh->buckets_stack); @@ -153,21 +223,28 @@ BLI_INLINE void smallhash_resize_buckets(SmallHash *sh, const uint nbuckets) } else { sh->buckets = MEM_mallocN(sizeof(*sh->buckets) * nbuckets, __func__); + sh->using_stack = false; } sh->nbuckets = nbuckets; + sh->nfreecells = nbuckets; + sh->nentries = 0; smallhash_init_empty(sh); for (i = 0; i < nbuckets_old; i++) { if (smallhash_val_is_used(buckets_old[i].val)) { SmallHashEntry *e = smallhash_lookup_first_free(sh, buckets_old[i].key); + e->key = buckets_old[i].key; e->val = buckets_old[i].val; + + sh->nfreecells--; + sh->nentries++; } } - if (was_alloc) { + if (was_alloc && buckets_old) { MEM_freeN(buckets_old); } } @@ -178,7 +255,9 @@ void BLI_smallhash_init_ex(SmallHash *sh, const uint nentries_reserve) sh->nentries = 0; sh->cursize = 2; + sh->using_stack = true; sh->nbuckets = hashsizes[sh->cursize]; + sh->nfreecells = sh->nbuckets; sh->buckets = sh->buckets_stack; @@ -187,6 +266,8 @@ void BLI_smallhash_init_ex(SmallHash *sh, const uint nentries_reserve) if (sh->nbuckets > SMSTACKSIZE) { sh->buckets = MEM_mallocN(sizeof(*sh->buckets) * sh->nbuckets, __func__); + + sh->using_stack = false; } } @@ -200,24 +281,85 @@ void BLI_smallhash_init(SmallHash *sh) void BLI_smallhash_release(SmallHash *sh) { - if (sh->buckets != sh->buckets_stack) { + check_stack_move(sh); + + if (sh->buckets && sh->buckets != sh->buckets_stack) { MEM_freeN(sh->buckets); } } +bool BLI_smallhash_ensure_p(SmallHash *sh, uintptr_t key, void ***item) +{ + check_stack_move(sh); + + SmallHashEntry *e = NULL; + uintptr_t h = smallhash_key(key); + uintptr_t hoff = 1; + + if (UNLIKELY(smallhash_test_expand_buckets(sh->nentries, sh->nbuckets, sh->nfreecells))) { + smallhash_resize_buckets(sh, hashsizes[++sh->cursize]); + } + + BLI_assert(key != SMHASH_KEY_UNUSED); + + /* NOTE: there are always more buckets than entries, + * so we know there will always be a free bucket if the key isn't found. */ + for (e = &sh->buckets[h % (uintptr_t)sh->nbuckets]; e->val != SMHASH_CELL_FREE; + h = SMHASH_NEXT(h, hoff), e = &sh->buckets[h % (uintptr_t)sh->nbuckets]) { + if (e->key == key) { + /* should never happen because unused keys are zero'd */ + BLI_assert(e->val != SMHASH_CELL_UNUSED); + break; + } + } + + bool ret; + + if (e->val == SMHASH_CELL_FREE || e->val == SMHASH_CELL_UNUSED) { + sh->nentries++; + + if (e->val == SMHASH_CELL_FREE) { + sh->nfreecells--; + } + + ret = false; + e->val = NULL; + } + else { + ret = true; + } + + e->key = key; + + *item = &e->val; + + return ret; +} + void BLI_smallhash_insert(SmallHash *sh, uintptr_t key, void *item) { + check_stack_move(sh); + SmallHashEntry *e; BLI_assert(key != SMHASH_KEY_UNUSED); BLI_assert(smallhash_val_is_used(item)); BLI_assert(BLI_smallhash_haskey(sh, key) == false); - if (UNLIKELY(smallhash_test_expand_buckets(++sh->nentries, sh->nbuckets))) { + if (UNLIKELY(smallhash_test_expand_buckets(sh->nentries, sh->nbuckets, sh->nfreecells))) { smallhash_resize_buckets(sh, hashsizes[++sh->cursize]); } e = smallhash_lookup_first_free(sh, key); + + if (e->val == SMHASH_CELL_FREE) { + sh->nentries++; + sh->nfreecells--; + } + else if (e->val == SMHASH_CELL_UNUSED) { + sh->nentries++; + } + e->key = key; e->val = item; } @@ -236,10 +378,47 @@ bool BLI_smallhash_reinsert(SmallHash *sh, uintptr_t key, void *item) #ifdef USE_REMOVE bool BLI_smallhash_remove(SmallHash *sh, uintptr_t key) +{ + check_stack_move(sh); + + // SmallHashEntry *e = smallhash_lookup(sh, key); + + SmallHashEntry *e; + uintptr_t h = smallhash_key(key); + uintptr_t hoff = 1; + + for (e = &sh->buckets[h % (uintptr_t)sh->nbuckets]; e->val != SMHASH_CELL_FREE; + h = SMHASH_NEXT(h, hoff), e = &sh->buckets[h % (uintptr_t)sh->nbuckets]) { + if (e->key == key) { + /* should never happen because unused keys are zero'd */ + BLI_assert(e->val != SMHASH_CELL_UNUSED); + break; + } + } + + if (e && e->key == key) { + h = SMHASH_NEXT(h, hoff); + SmallHashEntry *e2 = &sh->buckets[h & sh->nbuckets]; + + e->key = SMHASH_KEY_UNUSED; + e->val = SMHASH_CELL_UNUSED; + + sh->nentries--; + + return true; + } + else { + return false; + } +} + +bool BLI_smallhash_remove_p(SmallHash *sh, uintptr_t key, void **val) { SmallHashEntry *e = smallhash_lookup(sh, key); if (e) { + *val = e->val; + e->key = SMHASH_KEY_UNUSED; e->val = SMHASH_CELL_UNUSED; sh->nentries--; @@ -252,28 +431,42 @@ bool BLI_smallhash_remove(SmallHash *sh, uintptr_t key) } #endif -void *BLI_smallhash_lookup(const SmallHash *sh, uintptr_t key) +void *BLI_smallhash_lookup(SmallHash *sh, uintptr_t key) { SmallHashEntry *e = smallhash_lookup(sh, key); return e ? e->val : NULL; } -void **BLI_smallhash_lookup_p(const SmallHash *sh, uintptr_t key) +void **BLI_smallhash_lookup_p(SmallHash *sh, uintptr_t key) { SmallHashEntry *e = smallhash_lookup(sh, key); return e ? &e->val : NULL; } -bool BLI_smallhash_haskey(const SmallHash *sh, uintptr_t key) +void BLI_smallhash_clear(SmallHash *sh, uintptr_t key) +{ + check_stack_move(sh); + + SmallHashEntry *e = sh->buckets; + + for (uint i = 0; i < sh->nbuckets; i++, e++) { + e->key = SMHASH_KEY_UNUSED; + e->val = SMHASH_CELL_FREE; + } + + sh->nentries = 0; +} + +bool BLI_smallhash_haskey(SmallHash *sh, uintptr_t key) { SmallHashEntry *e = smallhash_lookup(sh, key); return (e != NULL); } -int BLI_smallhash_len(const SmallHash *sh) +int BLI_smallhash_len(SmallHash *sh) { return (int)sh->nentries; } @@ -309,16 +502,20 @@ void **BLI_smallhash_iternext_p(SmallHashIter *iter, uintptr_t *key) return e ? &e->val : NULL; } -void *BLI_smallhash_iternew(const SmallHash *sh, SmallHashIter *iter, uintptr_t *key) +void *BLI_smallhash_iternew(SmallHash *sh, SmallHashIter *iter, uintptr_t *key) { + check_stack_move(sh); + iter->sh = sh; iter->i = 0; return BLI_smallhash_iternext(iter, key); } -void **BLI_smallhash_iternew_p(const SmallHash *sh, SmallHashIter *iter, uintptr_t *key) +void **BLI_smallhash_iternew_p(SmallHash *sh, SmallHashIter *iter, uintptr_t *key) { + check_stack_move(sh); + iter->sh = sh; iter->i = 0; @@ -377,11 +574,11 @@ double BLI_smallhash_calc_quality(SmallHash *sh) if (sh->buckets[i].key != SMHASH_KEY_UNUSED) { uint64_t count = 0; SmallHashEntry *e, *e_final = &sh->buckets[i]; - uint h = smallhash_key(e_final->key); - uint hoff = 1; + uintptr_t h = smallhash_key(e_final->key); + uintptr_t hoff = 1; - for (e = &sh->buckets[h % sh->nbuckets]; e != e_final; - h = SMHASH_NEXT(h, hoff), e = &sh->buckets[h % sh->nbuckets]) { + for (e = &sh->buckets[h % (uintptr_t)sh->nbuckets]; e != e_final; + h = SMHASH_NEXT(h, hoff), e = &sh->buckets[h % (uintptr_t)sh->nbuckets]) { count += 1; } diff --git a/source/blender/blenlib/intern/task_pool.cc b/source/blender/blenlib/intern/task_pool.cc index c335d04413c..ac2de9bb2ea 100644 --- a/source/blender/blenlib/intern/task_pool.cc +++ b/source/blender/blenlib/intern/task_pool.cc @@ -76,7 +76,8 @@ class Task { other.freedata = nullptr; } -#if defined(WITH_TBB) && TBB_INTERFACE_VERSION_MAJOR < 10 +#if (defined(WITH_TBB) && TBB_INTERFACE_VERSION_MAJOR < 10) || \ + (defined(__clang__) && defined(WIN32)) Task(const Task &other) : pool(other.pool), run(other.run), diff --git a/source/blender/blenloader/CMakeLists.txt b/source/blender/blenloader/CMakeLists.txt index c660360bde7..581eece9804 100644 --- a/source/blender/blenloader/CMakeLists.txt +++ b/source/blender/blenloader/CMakeLists.txt @@ -20,6 +20,7 @@ set(INC ../windowmanager ../../../intern/clog ../../../intern/guardedalloc + ../../../intern/atomic ../bmesh # for writefile.c: dna_type_offsets.h diff --git a/source/blender/blenloader/intern/versioning_280.c b/source/blender/blenloader/intern/versioning_280.c index 2e9a16bc945..6b5838dcc72 100644 --- a/source/blender/blenloader/intern/versioning_280.c +++ b/source/blender/blenloader/intern/versioning_280.c @@ -3463,7 +3463,7 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain) for (Mesh *me = bmain->meshes.first; me; me = me->id.next) { me->flag &= ~(ME_FLAG_UNUSED_0 | ME_FLAG_UNUSED_1 | ME_FLAG_UNUSED_3 | ME_FLAG_UNUSED_4 | - ME_FLAG_UNUSED_6 | ME_FLAG_UNUSED_7 | ME_REMESH_REPROJECT_VERTEX_COLORS); + ME_REMESH_REPROJECT_VERTEX_COLORS); } for (Material *mat = bmain->materials.first; mat; mat = mat->id.next) { @@ -4407,7 +4407,6 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain) for (Scene *scene = bmain->scenes.first; scene; scene = scene->id.next) { ToolSettings *ts = scene->toolsettings; UnifiedPaintSettings *ups = &ts->unified_paint_settings; - ups->flag &= ~(UNIFIED_PAINT_FLAG_UNUSED_0 | UNIFIED_PAINT_FLAG_UNUSED_1); } /* Set the default render pass in the viewport to Combined. */ diff --git a/source/blender/bmesh/CMakeLists.txt b/source/blender/bmesh/CMakeLists.txt index 83bbc8cbf02..ef56f3b9881 100644 --- a/source/blender/bmesh/CMakeLists.txt +++ b/source/blender/bmesh/CMakeLists.txt @@ -13,6 +13,7 @@ set(INC ../../../intern/eigen ../../../intern/guardedalloc ../../../extern/rangetree + ../python/bmesh ) set(INC_SYS @@ -66,22 +67,27 @@ set(SRC intern/bmesh_construct.h intern/bmesh_core.c intern/bmesh_core.h + intern/bmesh_data_attr.h + intern/bmesh_data_attr.cc intern/bmesh_delete.c intern/bmesh_delete.h intern/bmesh_edgeloop.c intern/bmesh_edgeloop.h + intern/bmesh_idmap.cc + intern/bmesh_idmap.h intern/bmesh_inline.h intern/bmesh_interp.c intern/bmesh_interp.h intern/bmesh_iterators.cc intern/bmesh_iterators.h intern/bmesh_iterators_inline.h - intern/bmesh_log.c - intern/bmesh_log.h + intern/bmesh_log.cc + intern/bmesh_log_intern.h intern/bmesh_marking.c intern/bmesh_marking.h intern/bmesh_mesh.cc intern/bmesh_mesh.h + intern/bmesh_mesh_convert_threaded.cc intern/bmesh_mesh_convert.cc intern/bmesh_mesh_convert.h intern/bmesh_mesh_debug.c @@ -162,6 +168,7 @@ set(SRC tools/bmesh_wireframe.h bmesh_class.h + bmesh_log.h # public includes bmesh.h diff --git a/source/blender/bmesh/bmesh.h b/source/blender/bmesh/bmesh.h index 1157b8f0a58..2bcb121f771 100644 --- a/source/blender/bmesh/bmesh.h +++ b/source/blender/bmesh/bmesh.h @@ -193,7 +193,6 @@ #include "intern/bmesh_edgeloop.h" #include "intern/bmesh_interp.h" #include "intern/bmesh_iterators.h" -#include "intern/bmesh_log.h" #include "intern/bmesh_marking.h" #include "intern/bmesh_mesh.h" #include "intern/bmesh_mesh_convert.h" diff --git a/source/blender/bmesh/bmesh_class.h b/source/blender/bmesh/bmesh_class.h index a724a8783d4..7066a8d15f0 100644 --- a/source/blender/bmesh/bmesh_class.h +++ b/source/blender/bmesh/bmesh_class.h @@ -2,6 +2,8 @@ #pragma once +#include "DNA_modifier_types.h" + /** \file * \ingroup bmesh * @@ -10,6 +12,9 @@ */ #include "BLI_assert.h" +#include "BLI_compiler_compat.h" +#include "BLI_compiler_typecheck.h" +#include "BLI_utildefines.h" #ifdef __cplusplus extern "C" { @@ -24,6 +29,7 @@ struct BMFace; struct BMLoop; struct BMVert; struct BMesh; +struct GSet; struct MLoopNorSpaceArray; @@ -101,10 +107,9 @@ typedef struct BMVert { struct BMEdge *e; } BMVert; -typedef struct BMVert_OFlag { - BMVert base; - struct BMFlagLayer *oflags; -} BMVert_OFlag; +#define BMVert_OFlag BMVert +#define BMEdge_OFlag BMEdge +#define BMFace_OFlag BMFace /* disk link structure, only used by edges */ typedef struct BMDiskLink { @@ -140,11 +145,6 @@ typedef struct BMEdge { BMDiskLink v1_disk_link, v2_disk_link; } BMEdge; -typedef struct BMEdge_OFlag { - BMEdge base; - struct BMFlagLayer *oflags; -} BMEdge_OFlag; - typedef struct BMLoop { BMHeader head; /* notice no flags layer */ @@ -286,17 +286,16 @@ typedef struct BMFace { // short _pad[3]; } BMFace; -typedef struct BMFace_OFlag { - BMFace base; - struct BMFlagLayer *oflags; -} BMFace_OFlag; - typedef struct BMFlagLayer { short f; /* flags */ } BMFlagLayer; // #pragma GCC diagnostic ignored "-Wpadded" +struct RangeTreeUInt; + +#define WITH_BM_ID_FREELIST + typedef struct BMesh { int totvert, totedge, totloop, totface; int totvertsel, totedgesel, totfacesel; @@ -380,8 +379,44 @@ typedef struct BMesh { * instead of crashing on invalid memory access. */ void *py_handle; + MultiresModifierData multires; // copy of multires settings + bool haveMultiResSettings; + int multiresSpace; + + struct { + int flag; +#ifdef WITH_BM_ID_FREELIST + uint *freelist; + int freelist_len, freelist_size; + uint *free_ids, free_ids_size; + + /* maps ids to their position within the freelist + only used if freelist is bigger then a certain size, + see FREELIST_HASHMAP_THRESHOLD_HIGH in bmesh_construct.c.*/ + struct GHash *free_idx_map; +#else + struct RangeTreeUInt *idtree; +#endif + uint maxid; + struct BMElem **map; // used if BM_NO_REUSE_IDS is false + struct GHash *ghash; // used if BM_NO_REUSE_IDS is true + int map_size; + int cd_id_off[15]; + } idmap; + +#ifdef USE_BMESH_PAGE_CUSTOMDATA + struct BMeshAttrList *attr_list; +#endif } BMesh; +enum { + // firsst four bits are reserved for BM_VERT/EDGE/LOOP/FACE + BM_HAS_IDS = 1 << 4, + BM_HAS_ID_MAP = 1 << 5, + BM_NO_REUSE_IDS = 1 << 6, + BM_PERMANENT_IDS = 1 << 7 +}; + /** #BMHeader.htype (char) */ enum { BM_VERT = 1, @@ -423,12 +458,10 @@ enum { /* args for _Generic */ #define _BM_GENERIC_TYPE_ELEM_NONCONST \ - void *, BMVert *, BMEdge *, BMLoop *, BMFace *, BMVert_OFlag *, BMEdge_OFlag *, BMFace_OFlag *, \ - BMElem *, BMElemF *, BMHeader * + void *, BMVert *, BMEdge *, BMLoop *, BMFace *, BMElem *, BMElemF *, BMHeader * #define _BM_GENERIC_TYPE_ELEM_CONST \ - const void *, const BMVert *, const BMEdge *, const BMLoop *, const BMFace *, \ - const BMVert_OFlag *, const BMEdge_OFlag *, const BMFace_OFlag *, const BMElem *, \ + const void *, const BMVert *, const BMEdge *, const BMLoop *, const BMFace *, const BMElem *, \ const BMElemF *, const BMHeader *, void *const, BMVert *const, BMEdge *const, \ BMLoop *const, BMFace *const, BMElem *const, BMElemF *const, BMHeader *const @@ -440,22 +473,22 @@ enum { CHECK_TYPE_ANY(ele, _BM_GENERIC_TYPE_ELEM_NONCONST, _BM_GENERIC_TYPE_ELEM_CONST) /* vert */ -#define _BM_GENERIC_TYPE_VERT_NONCONST BMVert *, BMVert_OFlag * -#define _BM_GENERIC_TYPE_VERT_CONST const BMVert *, const BMVert_OFlag * +#define _BM_GENERIC_TYPE_VERT_NONCONST BMVert * +#define _BM_GENERIC_TYPE_VERT_CONST const BMVert * #define BM_CHECK_TYPE_VERT_CONST(ele) CHECK_TYPE_ANY(ele, _BM_GENERIC_TYPE_VERT_CONST) #define BM_CHECK_TYPE_VERT_NONCONST(ele) CHECK_TYPE_ANY(ele, _BM_GENERIC_TYPE_ELEM_NONCONST) #define BM_CHECK_TYPE_VERT(ele) \ CHECK_TYPE_ANY(ele, _BM_GENERIC_TYPE_VERT_NONCONST, _BM_GENERIC_TYPE_VERT_CONST) /* edge */ -#define _BM_GENERIC_TYPE_EDGE_NONCONST BMEdge *, BMEdge_OFlag * -#define _BM_GENERIC_TYPE_EDGE_CONST const BMEdge *, const BMEdge_OFlag * +#define _BM_GENERIC_TYPE_EDGE_NONCONST BMEdge * +#define _BM_GENERIC_TYPE_EDGE_CONST const BMEdge * #define BM_CHECK_TYPE_EDGE_CONST(ele) CHECK_TYPE_ANY(ele, _BM_GENERIC_TYPE_EDGE_CONST) #define BM_CHECK_TYPE_EDGE_NONCONST(ele) CHECK_TYPE_ANY(ele, _BM_GENERIC_TYPE_ELEM_NONCONST) #define BM_CHECK_TYPE_EDGE(ele) \ CHECK_TYPE_ANY(ele, _BM_GENERIC_TYPE_EDGE_NONCONST, _BM_GENERIC_TYPE_EDGE_CONST) /* face */ -#define _BM_GENERIC_TYPE_FACE_NONCONST BMFace *, BMFace_OFlag * -#define _BM_GENERIC_TYPE_FACE_CONST const BMFace *, const BMFace_OFlag * +#define _BM_GENERIC_TYPE_FACE_NONCONST BMFace * +#define _BM_GENERIC_TYPE_FACE_CONST const BMFace * #define BM_CHECK_TYPE_FACE_CONST(ele) CHECK_TYPE_ANY(ele, _BM_GENERIC_TYPE_FACE_CONST) #define BM_CHECK_TYPE_FACE_NONCONST(ele) CHECK_TYPE_ANY(ele, _BM_GENERIC_TYPE_ELEM_NONCONST) #define BM_CHECK_TYPE_FACE(ele) \ @@ -559,6 +592,17 @@ typedef bool (*BMLoopPairFilterFunc)(const BMLoop *, const BMLoop *, void *user_ (BLI_assert(offset != -1), (void *)((char *)(ele)->head.data + (offset))) #endif +#ifdef __cplusplus +} + +template inline Tptr BM_ELEM_CD_PTR(ElemType e, int offset) +{ + return reinterpret_cast(BM_ELEM_CD_GET_VOID_P(e, offset)); +} + +extern "C" { +#endif + #define BM_ELEM_CD_SET_FLOAT(ele, offset, f) \ { \ CHECK_TYPE_NONCONST(ele); \ @@ -673,6 +717,10 @@ typedef bool (*BMLoopPairFilterFunc)(const BMLoop *, const BMLoop *, void *user_ # define BM_OMP_LIMIT 10000 #endif +/* note does not check if ids are enabled for a given element type */ +#define BM_ELEM_GET_ID(bm, elem) \ + BM_ELEM_CD_GET_INT(elem, bm->idmap.cd_id_off[(int)(elem)->head.htype]) + #ifdef __cplusplus } #endif diff --git a/source/blender/bmesh/bmesh_log.h b/source/blender/bmesh/bmesh_log.h new file mode 100644 index 00000000000..bdb9106837b --- /dev/null +++ b/source/blender/bmesh/bmesh_log.h @@ -0,0 +1,4 @@ +#pragma once + +#include "bmesh.h" +#include "intern/bmesh_log_intern.h" diff --git a/source/blender/bmesh/intern/bmesh_construct.c b/source/blender/bmesh/intern/bmesh_construct.c index b549580f354..2ead78d5892 100644 --- a/source/blender/bmesh/intern/bmesh_construct.c +++ b/source/blender/bmesh/intern/bmesh_construct.c @@ -10,6 +10,9 @@ #include "MEM_guardedalloc.h" #include "BLI_alloca.h" +#include "BLI_array.h" +#include "BLI_bitmap.h" +#include "BLI_ghash.h" #include "BLI_math.h" #include "BLI_sort_utils.h" @@ -21,6 +24,314 @@ #include "bmesh.h" #include "intern/bmesh_private.h" +#include "range_tree.h" + +#ifdef USE_BMESH_PAGE_CUSTOMDATA +# include "intern/bmesh_data_attr.h" +#endif + +#define SELECT 1 + +#ifdef WITH_BM_ID_FREELIST + +/* if freelist is bigger then this, allocate free_idx_map */ +# define FREELIST_HASHMAP_THRESHOLD_HIGH 1024 + +/* if freelist is smaller then this, free free_idx_map*/ +# define FREELIST_HASHMAP_THRESHOLD_LOW 700 + +static void bm_id_freelist_check_hashmap(BMesh *bm) +{ + if (!bm->idmap.free_idx_map && bm->idmap.freelist_len >= FREELIST_HASHMAP_THRESHOLD_HIGH) { + printf("switching on freelist idx map\n"); + bm->idmap.free_idx_map = BLI_ghash_ptr_new("free_idx_map"); + + for (int i = 0; i < bm->idmap.freelist_len; i++) { + BLI_ghash_insert( + bm->idmap.free_idx_map, POINTER_FROM_UINT(bm->idmap.freelist[i]), POINTER_FROM_INT(i)); + } + } + else if (bm->idmap.free_idx_map && bm->idmap.freelist_len <= FREELIST_HASHMAP_THRESHOLD_LOW) { + BLI_ghash_free(bm->idmap.free_idx_map, NULL, NULL); + bm->idmap.free_idx_map = NULL; + + printf("switching off freelist idx map\n"); + } +} + +static uint bm_id_freelist_pop(BMesh *bm) +{ + bm_id_freelist_check_hashmap(bm); + + if (bm->idmap.freelist_len > 0) { + int i = --bm->idmap.freelist_len; + uint id = bm->idmap.freelist[i]; + + if (bm->idmap.free_idx_map) { + BLI_ghash_remove(bm->idmap.free_idx_map, POINTER_FROM_UINT(id), NULL, NULL); + } + + return id; + } + + return 0; +} + +void bm_free_ids_check(BMesh *bm, uint id) +{ + if (!bm->idmap.free_ids || id >> 2UL >= (uint)bm->idmap.free_ids_size) { + size_t size = (size_t)(id >> 2) + 2ULL; + size += size >> 1ULL; + + if (!bm->idmap.free_ids) { + bm->idmap.free_ids = MEM_callocN(sizeof(int) * size, "free_ids"); + } + else { + bm->idmap.free_ids = MEM_recallocN(bm->idmap.free_ids, sizeof(int) * size); + } + + bm->idmap.free_ids_size = (uint)size; + } +} + +void bm_id_freelist_take(BMesh *bm, uint id) +{ + bm_free_ids_check(bm, id); + + if (!bm->idmap.free_ids || !BLI_BITMAP_TEST(bm->idmap.free_ids, id)) { + return; + } + + BLI_BITMAP_DISABLE(bm->idmap.free_ids, id); + + if (bm->idmap.free_idx_map) { + void **val = BLI_ghash_lookup_p(bm->idmap.free_idx_map, POINTER_FROM_UINT(id)); + + if (val) { + uint i = POINTER_AS_UINT(*val); + + uint end = bm->idmap.freelist[bm->idmap.freelist_len - 1]; + + // swap with end + void **endval = BLI_ghash_lookup_p(bm->idmap.free_idx_map, POINTER_FROM_UINT(end)); + if (endval) { + *endval = POINTER_FROM_UINT(i); + } + else { + printf("%s: end id %d was not in free_idx_map; id was %d\n", __func__, end, id); + BLI_ghash_insert(bm->idmap.free_idx_map, POINTER_FROM_UINT(end), POINTER_FROM_UINT(i)); + } + + bm->idmap.freelist[i] = bm->idmap.freelist[bm->idmap.freelist_len - 1]; + bm->idmap.freelist_len--; + } + + BLI_ghash_remove(bm->idmap.free_idx_map, POINTER_FROM_UINT(id), NULL, NULL); + } + else { + for (int i = 0; i < bm->idmap.freelist_len; i++) { + if (bm->idmap.freelist[i] == id) { + // swap with end + bm->idmap.freelist[i] = bm->idmap.freelist[bm->idmap.freelist_len - 1]; + bm->idmap.freelist_len--; + } + } + } +} + +static bool bm_id_freelist_has(BMesh *bm, uint id) +{ + if (!bm->idmap.free_ids) { + return false; + } + + return id < bm->idmap.free_ids_size && BLI_BITMAP_TEST(bm->idmap.free_ids, id); +} + +void bm_id_freelist_push(BMesh *bm, uint id) +{ + bm_id_freelist_check_hashmap(bm); + bm_free_ids_check(bm, id); + + bm->idmap.freelist_len++; + + if (bm->idmap.freelist_len >= bm->idmap.freelist_size) { + int size = 2 + bm->idmap.freelist_size + (bm->idmap.freelist_size >> 1); + + uint *newlist; + + if (bm->idmap.freelist) { + newlist = MEM_reallocN(bm->idmap.freelist, size * sizeof(uint)); + } + else { + newlist = MEM_malloc_arrayN(size, sizeof(uint), "bm->idmap.freelist"); + } + + bm->idmap.freelist_size = size; + bm->idmap.freelist = newlist; + } + + if (bm->idmap.free_idx_map) { + void **val; + + if (!BLI_ghash_ensure_p(bm->idmap.free_idx_map, POINTER_FROM_UINT(id), &val)) { + *val = POINTER_FROM_INT(bm->idmap.freelist_len - 1); + } + } + + bm->idmap.freelist[bm->idmap.freelist_len - 1] = id; + BLI_BITMAP_ENABLE(bm->idmap.free_ids, id); +} +#endif + +// static const int _typemap[] = {0, 0, 1, 0, 2, 0, 0, 0, 3}; + +void bm_assign_id_intern(BMesh *bm, BMElem *elem, uint id) +{ + // CustomData *cdata = &bm->vdata + _typemap[elem->head.htype]; + // int cd_id_off = cdata->layers[cdata->typemap[CD_MESH_ID]].offset; + + BM_ELEM_CD_SET_INT(elem, bm->idmap.cd_id_off[elem->head.htype], id); + bm->idmap.maxid = MAX2(bm->idmap.maxid, id); + + if (bm->idmap.flag & BM_HAS_ID_MAP) { + if (!(bm->idmap.flag & BM_NO_REUSE_IDS)) { + if (!bm->idmap.map || bm->idmap.map_size <= (int)bm->idmap.maxid) { + int size = 2 + bm->idmap.maxid + (bm->idmap.maxid >> 1); + + BMElem **idmap = MEM_callocN(sizeof(void *) * size, "bmesh idmap"); + + if (bm->idmap.map) { + memcpy((void *)idmap, (void *)bm->idmap.map, sizeof(void *) * bm->idmap.map_size); + MEM_freeN(bm->idmap.map); + } + + bm->idmap.map = idmap; + bm->idmap.map_size = size; + } + + bm->idmap.map[id] = elem; + } + else { + void **val = NULL; + + BLI_ghash_ensure_p(bm->idmap.ghash, POINTER_FROM_UINT(id), &val); + *val = (void *)elem; + } + } +} + +static unsigned char *bm_get_htype_str(int htype) +{ + switch (htype) { + case BM_VERT: + return "vertex"; + case BM_EDGE: + return "edge"; + case BM_LOOP: + return "loop"; + case BM_FACE: + return "face"; + default: + return "unknown type"; + } +} + +void bm_assign_id(BMesh *bm, BMElem *elem, uint id, bool check_unqiue) +{ + if (check_unqiue && (bm->idmap.flag & BM_HAS_ID_MAP)) { + BMElem *old; + + if (old = (BMElem *)BM_ELEM_FROM_ID(bm, id)) { + printf("id conflict in bm_assign_id; elem %p (a %s) is being reassinged to id %d.\n", + elem, + bm_get_htype_str((int)elem->head.htype), + (int)id); + printf(" elem %p (a %s) will get a new id\n", old, bm_get_htype_str((int)old->head.htype)); + + bm_free_id(bm, old); + +#ifdef WITH_BM_ID_FREELIST + bm_id_freelist_take(bm, id); +#else + range_tree_uint_retake(bm->idmap.idtree, id); +#endif + + bm_assign_id_intern(bm, elem, id); + + bm_alloc_id(bm, old); + } + } + +#ifdef WITH_BM_ID_FREELIST + bm_id_freelist_take(bm, id); +#else + range_tree_uint_retake(bm->idmap.idtree, id); +#endif + bm_assign_id_intern(bm, elem, id); +} + +void bm_alloc_id(BMesh *bm, BMElem *elem) +{ + if ((bm->idmap.flag & (elem->head.htype | BM_HAS_IDS)) != (elem->head.htype | BM_HAS_IDS)) { + return; + } + + uint id; + +#ifdef WITH_BM_ID_FREELIST + if (bm->idmap.freelist_len > 0 && !(bm->idmap.flag & BM_NO_REUSE_IDS)) { + id = bm_id_freelist_pop(bm); + } + else { + id = bm->idmap.maxid + 1; + } +#else + if (!(bm->idmap.flag & BM_NO_REUSE_IDS)) { + id = range_tree_uint_take_any(bm->idmap.idtree); + } + else { + id = bm->idmap.maxid + 1; + } +#endif + + bm_assign_id_intern(bm, elem, id); +} + +void bm_free_id(BMesh *bm, BMElem *elem) +{ + if ((bm->idmap.flag & (elem->head.htype | BM_HAS_IDS)) != (elem->head.htype | BM_HAS_IDS)) { + return; + } + + uint id = (uint)BM_ELEM_CD_GET_INT(elem, bm->idmap.cd_id_off[elem->head.htype]); + +#ifndef WITH_BM_ID_FREELIST + + if (!(bm->idmap.flag & BM_NO_REUSE_IDS) && !range_tree_uint_has(bm->idmap.idtree, id)) { + range_tree_uint_release(bm->idmap.idtree, id); + } +#else + if (!(bm->idmap.flag & BM_NO_REUSE_IDS)) { + bm_id_freelist_push(bm, id); + } +#endif + + if ((bm->idmap.flag & BM_HAS_ID_MAP)) { + if (!(bm->idmap.flag & BM_NO_REUSE_IDS) && bm->idmap.map && (int)id < bm->idmap.map_size) { + bm->idmap.map[id] = NULL; + } + else if (bm->idmap.flag & BM_NO_REUSE_IDS) { + BLI_ghash_remove(bm->idmap.ghash, POINTER_FROM_UINT(id), NULL, NULL); + } + } +} + +/** + * Fill in a vertex array from an edge array. + * + * \returns false if any verts aren't found. + */ bool BM_verts_from_edges(BMVert **vert_arr, BMEdge **edge_arr, const int len) { int i, i_prev = len - 1; @@ -319,6 +630,79 @@ void BM_verts_sort_radial_plane(BMVert **vert_arr, int len) } } +void BM_sort_disk_cycle(BMVert *v) +{ + BMVert **vs = NULL; + BLI_array_staticdeclare(vs, 64); + BMEdge **es = NULL; + BLI_array_staticdeclare(es, 64); + + if (!v->e) { + return; + } + + BMEdge *e = v->e; + do { + BMVert *v2 = BM_edge_other_vert(e, v); + + BLI_array_append(es, e); + BLI_array_append(vs, v2); + + e = v == e->v1 ? e->v1_disk_link.next : e->v2_disk_link.next; + } while (e != v->e); + + if (BLI_array_len(vs) < 2) { + return; + } + + int totvert = BLI_array_len(vs); + + struct SortIntByFloat *vang = BLI_array_alloca(vang, totvert); + BMVert **vert_arr_map = BLI_array_alloca(vert_arr_map, totvert); + + float nor[3], cent[3]; + int index_tangent = 0; + BM_verts_calc_normal_from_cloud_ex(vs, totvert, nor, cent, &index_tangent); + const float *far = vs[index_tangent]->co; + + /* Now calculate every points angle around the normal (signed). */ + for (int i = 0; i < totvert; i++) { + vang[i].sort_value = angle_signed_on_axis_v3v3v3_v3(far, cent, vs[i]->co, nor); + vang[i].data = i; + vert_arr_map[i] = vs[i]; + } + + /* sort by angle and magic! - we have our ngon */ + qsort(vang, totvert, sizeof(*vang), BLI_sortutil_cmp_float); + + BMEdge **es2 = BLI_array_alloca(es2, totvert); + + /* --- */ + + for (int i = 0; i < totvert; i++) { + es2[i] = es[vang[i].data]; + } + + // rebuild disk cycle + for (int i = 0; i < totvert; i++) { + int prev = (i + totvert - 1) % totvert; + int next = (i + 1) % totvert; + BMEdge *e = es2[i]; + + if (e->v1 == v) { + e->v1_disk_link.prev = es2[prev]; + e->v1_disk_link.next = es2[next]; + } + else { + e->v2_disk_link.prev = es2[prev]; + e->v2_disk_link.next = es2[next]; + } + } + + BLI_array_free(es); + BLI_array_free(vs); +} + /*************************************************************/ static void bm_vert_attrs_copy( @@ -331,9 +715,14 @@ static void bm_vert_attrs_copy( if ((mask_exclude & CD_MASK_NORMAL) == 0) { copy_v3_v3(v_dst->no, v_src->no); } + + int id = bm_save_id(bm_dst, (BMElem *)v_dst); + CustomData_bmesh_free_block_data_exclude_by_type(&bm_dst->vdata, v_dst->head.data, mask_exclude); CustomData_bmesh_copy_data_exclude_by_type( &bm_src->vdata, &bm_dst->vdata, v_src->head.data, &v_dst->head.data, mask_exclude); + + bm_restore_id(bm_dst, (BMElem *)v_dst, id); } static void bm_edge_attrs_copy( @@ -343,9 +732,14 @@ static void bm_edge_attrs_copy( BLI_assert_msg(0, "BMEdge: source and target match"); return; } + + int id = bm_save_id(bm_dst, (BMElem *)e_dst); + CustomData_bmesh_free_block_data_exclude_by_type(&bm_dst->edata, e_dst->head.data, mask_exclude); CustomData_bmesh_copy_data_exclude_by_type( &bm_src->edata, &bm_dst->edata, e_src->head.data, &e_dst->head.data, mask_exclude); + + bm_restore_id(bm_dst, (BMElem *)e_dst, id); } static void bm_loop_attrs_copy( @@ -355,9 +749,14 @@ static void bm_loop_attrs_copy( BLI_assert_msg(0, "BMLoop: source and target match"); return; } + + int id = bm_save_id(bm_dst, (BMElem *)l_dst); + CustomData_bmesh_free_block_data_exclude_by_type(&bm_dst->ldata, l_dst->head.data, mask_exclude); CustomData_bmesh_copy_data_exclude_by_type( &bm_src->ldata, &bm_dst->ldata, l_src->head.data, &l_dst->head.data, mask_exclude); + + bm_restore_id(bm_dst, (BMElem *)l_dst, id); } static void bm_face_attrs_copy( @@ -370,10 +769,15 @@ static void bm_face_attrs_copy( if ((mask_exclude & CD_MASK_NORMAL) == 0) { copy_v3_v3(f_dst->no, f_src->no); } + + int id = bm_save_id(bm_dst, (BMElem *)f_dst); + CustomData_bmesh_free_block_data_exclude_by_type(&bm_dst->pdata, f_dst->head.data, mask_exclude); CustomData_bmesh_copy_data_exclude_by_type( &bm_src->pdata, &bm_dst->pdata, f_src->head.data, &f_dst->head.data, mask_exclude); f_dst->mat_nr = f_src->mat_nr; + + bm_restore_id(bm_dst, (BMElem *)f_dst, id); } void BM_elem_attrs_copy_ex(BMesh *bm_src, @@ -413,20 +817,32 @@ void BM_elem_attrs_copy_ex(BMesh *bm_src, /* Copy specific attributes */ switch (ele_dst->htype) { case BM_VERT: - bm_vert_attrs_copy( - bm_src, bm_dst, (const BMVert *)ele_src, (BMVert *)ele_dst, cd_mask_exclude); + bm_vert_attrs_copy(bm_src, + bm_dst, + (const BMVert *)ele_src, + (BMVert *)ele_dst, + cd_mask_exclude | CD_MASK_MESH_ID | CD_MASK_TOOLFLAGS); break; case BM_EDGE: - bm_edge_attrs_copy( - bm_src, bm_dst, (const BMEdge *)ele_src, (BMEdge *)ele_dst, cd_mask_exclude); + bm_edge_attrs_copy(bm_src, + bm_dst, + (const BMEdge *)ele_src, + (BMEdge *)ele_dst, + cd_mask_exclude | CD_MASK_MESH_ID | CD_MASK_TOOLFLAGS); break; case BM_LOOP: - bm_loop_attrs_copy( - bm_src, bm_dst, (const BMLoop *)ele_src, (BMLoop *)ele_dst, cd_mask_exclude); + bm_loop_attrs_copy(bm_src, + bm_dst, + (const BMLoop *)ele_src, + (BMLoop *)ele_dst, + cd_mask_exclude | CD_MASK_MESH_ID | CD_MASK_TOOLFLAGS); break; case BM_FACE: - bm_face_attrs_copy( - bm_src, bm_dst, (const BMFace *)ele_src, (BMFace *)ele_dst, cd_mask_exclude); + bm_face_attrs_copy(bm_src, + bm_dst, + (const BMFace *)ele_src, + (BMFace *)ele_dst, + cd_mask_exclude | CD_MASK_MESH_ID | CD_MASK_TOOLFLAGS); break; default: BLI_assert(0); @@ -473,7 +889,8 @@ static BMFace *bm_mesh_copy_new_face( j++; } while ((l_iter = l_iter->next) != l_first); - f_new = BM_face_create(bm_new, verts, edges, f->len, NULL, BM_CREATE_SKIP_CD); + f_new = BM_face_create( + bm_new, verts, edges, f->len, NULL, BM_CREATE_SKIP_CD | BM_CREATE_SKIP_ID); if (UNLIKELY(f_new == NULL)) { return NULL; @@ -485,6 +902,8 @@ static BMFace *bm_mesh_copy_new_face( BM_elem_attrs_copy_ex(bm_old, bm_new, f, f_new, 0xff, 0x0); f_new->head.hflag = f->head.hflag; /* low level! don't do this for normal api use */ + bm_elem_check_toolflags(bm_new, (BMElem *)f); + j = 0; l_iter = l_first = BM_FACE_FIRST_LOOP(f_new); do { @@ -554,15 +973,46 @@ void BM_mesh_copy_init_customdata(BMesh *bm_dst, BMesh *bm_src, const BMAllocTem allocsize = &bm_mesh_allocsize_default; } - CustomData_copy(&bm_src->vdata, &bm_dst->vdata, CD_MASK_BMESH.vmask, CD_SET_DEFAULT, 0); - CustomData_copy(&bm_src->edata, &bm_dst->edata, CD_MASK_BMESH.emask, CD_SET_DEFAULT, 0); - CustomData_copy(&bm_src->ldata, &bm_dst->ldata, CD_MASK_BMESH.lmask, CD_SET_DEFAULT, 0); - CustomData_copy(&bm_src->pdata, &bm_dst->pdata, CD_MASK_BMESH.pmask, CD_SET_DEFAULT, 0); + // forcibly copy mesh_id layers + CustomData *srcdatas[4] = {&bm_src->vdata, &bm_src->edata, &bm_src->ldata, &bm_src->pdata}; + CustomData *dstdatas[4] = {&bm_dst->vdata, &bm_dst->edata, &bm_dst->ldata, &bm_dst->pdata}; + + for (int i = 0; i < 4; i++) { + CustomData *cdata = srcdatas[i]; + + if (CustomData_has_layer(cdata, CD_MESH_ID)) { + int idx = CustomData_get_layer_index(cdata, CD_MESH_ID); + + cdata->layers[idx].flag &= ~(CD_FLAG_TEMPORARY | CD_FLAG_ELEM_NOCOPY); + } + } + + CustomData_copy( + &bm_src->vdata, &bm_dst->vdata, CD_MASK_BMESH.vmask | CD_MASK_MESH_ID, CD_SET_DEFAULT, 0); + CustomData_copy( + &bm_src->edata, &bm_dst->edata, CD_MASK_BMESH.emask | CD_MASK_MESH_ID, CD_SET_DEFAULT, 0); + CustomData_copy( + &bm_src->ldata, &bm_dst->ldata, CD_MASK_BMESH.lmask | CD_MASK_MESH_ID, CD_SET_DEFAULT, 0); + CustomData_copy( + &bm_src->pdata, &bm_dst->pdata, CD_MASK_BMESH.pmask | CD_MASK_MESH_ID, CD_SET_DEFAULT, 0); CustomData_bmesh_init_pool(&bm_dst->vdata, allocsize->totvert, BM_VERT); CustomData_bmesh_init_pool(&bm_dst->edata, allocsize->totedge, BM_EDGE); CustomData_bmesh_init_pool(&bm_dst->ldata, allocsize->totloop, BM_LOOP); CustomData_bmesh_init_pool(&bm_dst->pdata, allocsize->totface, BM_FACE); + + // flag mesh id layer as temporary + if (!(bm_dst->idmap.flag & BM_PERMANENT_IDS)) { + for (int i = 0; i < 4; i++) { + CustomData *cdata = dstdatas[i]; + + if (CustomData_has_layer(cdata, CD_MESH_ID)) { + int idx = CustomData_get_layer_index(cdata, CD_MESH_ID); + + cdata->layers[idx].flag |= CD_FLAG_TEMPORARY | CD_FLAG_ELEM_NOCOPY; + } + } + } } void BM_mesh_copy_init_customdata_all_layers(BMesh *bm_dst, @@ -596,9 +1046,11 @@ void BM_mesh_copy_init_customdata_all_layers(BMesh *bm_dst, } CustomData_bmesh_init_pool(dst, size, htypes[i]); } + + bm_update_idmap_cdlayers(bm_dst); } -BMesh *BM_mesh_copy(BMesh *bm_old) +BMesh *BM_mesh_copy_ex(BMesh *bm_old, struct BMeshCreateParams *params) { BMesh *bm_new; BMVert *v, *v_new, **vtable = NULL; @@ -609,14 +1061,54 @@ BMesh *BM_mesh_copy(BMesh *bm_old) BMIter iter; int i; const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_BM(bm_old); + struct BMeshCreateParams _params; + + if (!params) { + _params = ((struct BMeshCreateParams){ + .use_toolflags = bm_old->use_toolflags, + .id_elem_mask = bm_old->idmap.flag & (BM_VERT | BM_EDGE | BM_LOOP | BM_FACE), + .create_unique_ids = !!(bm_old->idmap.flag & BM_HAS_IDS), + .id_map = !!(bm_old->idmap.flag & BM_HAS_ID_MAP), + .temporary_ids = !(bm_old->idmap.flag & BM_PERMANENT_IDS), + .no_reuse_ids = !!(bm_old->idmap.flag & BM_NO_REUSE_IDS)}); + params = &_params; + } /* allocate a bmesh */ - bm_new = BM_mesh_create(&allocsize, - &((struct BMeshCreateParams){ - .use_toolflags = bm_old->use_toolflags, - })); + bm_new = BM_mesh_create(&allocsize, params); - BM_mesh_copy_init_customdata(bm_new, bm_old, &allocsize); + if (params->copy_all_layers) { + BM_mesh_copy_init_customdata_all_layers( + bm_new, bm_old, BM_VERT | BM_EDGE | BM_LOOP | BM_FACE, &allocsize); + } + else { + BM_mesh_copy_init_customdata(bm_new, bm_old, &allocsize); + } + + if (bm_old->idmap.flag & BM_HAS_IDS) { + MEM_SAFE_FREE(bm_new->idmap.map); + + if ((bm_old->idmap.flag & BM_HAS_ID_MAP)) { + if (!(bm_old->idmap.flag & BM_NO_REUSE_IDS)) { + bm_new->idmap.map_size = bm_old->idmap.map_size; + bm_new->idmap.flag = bm_old->idmap.flag; + + if (bm_new->idmap.map_size) { + bm_new->idmap.map = MEM_callocN(sizeof(void *) * bm_old->idmap.map_size, "bm idmap"); + } + else { + bm_new->idmap.map = NULL; + } + } + else { + BLI_ghash_free(bm_new->idmap.ghash, NULL, NULL); + bm_new->idmap.ghash = BLI_ghash_ptr_new_ex( + "idmap.ghash", bm_old->totvert + bm_old->totedge + bm_old->totface); + } + } + + bm_init_idmap_cdlayers(bm_new); + } vtable = MEM_mallocN(sizeof(BMVert *) * bm_old->totvert, "BM_mesh_copy vtable"); etable = MEM_mallocN(sizeof(BMEdge *) * bm_old->totedge, "BM_mesh_copy etable"); @@ -624,8 +1116,13 @@ BMesh *BM_mesh_copy(BMesh *bm_old) BM_ITER_MESH_INDEX (v, &iter, bm_old, BM_VERTS_OF_MESH, i) { /* copy between meshes so can't use 'example' argument */ - v_new = BM_vert_create(bm_new, v->co, NULL, BM_CREATE_SKIP_CD); + v_new = BM_vert_create(bm_new, v->co, NULL, BM_CREATE_SKIP_CD | BM_CREATE_SKIP_ID); + BM_elem_attrs_copy_ex(bm_old, bm_new, v, v_new, 0xff, 0x0); + + bm_alloc_id(bm_new, (BMElem *)v_new); + bm_elem_check_toolflags(bm_new, (BMElem *)v_new); + v_new->head.hflag = v->head.hflag; /* low level! don't do this for normal api use */ vtable[i] = v_new; BM_elem_index_set(v, i); /* set_inline */ @@ -642,9 +1139,12 @@ BMesh *BM_mesh_copy(BMesh *bm_old) vtable[BM_elem_index_get(e->v1)], vtable[BM_elem_index_get(e->v2)], e, - BM_CREATE_SKIP_CD); + BM_CREATE_SKIP_CD | BM_CREATE_SKIP_ID); BM_elem_attrs_copy_ex(bm_old, bm_new, e, e_new, 0xff, 0x0); + bm_alloc_id(bm_new, (BMElem *)e_new); + bm_elem_check_toolflags(bm_new, (BMElem *)e_new); + e_new->head.hflag = e->head.hflag; /* low level! don't do this for normal api use */ etable[i] = e_new; BM_elem_index_set(e, i); /* set_inline */ @@ -660,6 +1160,16 @@ BMesh *BM_mesh_copy(BMesh *bm_old) BM_elem_index_set(f, i); /* set_inline */ f_new = bm_mesh_copy_new_face(bm_new, bm_old, vtable, etable, f); + bm_alloc_id(bm_new, (BMElem *)f_new); + + if (bm_new->idmap.flag & BM_LOOP) { + BMLoop *l_new = f_new->l_first; + + do { + bm_alloc_id(bm_new, (BMElem *)l_new); + l_new = l_new->next; + } while (l_new != f_new->l_first); + } ftable[i] = f_new; @@ -715,3 +1225,140 @@ BMesh *BM_mesh_copy(BMesh *bm_old) return bm_new; } + +BMesh *BM_mesh_copy(BMesh *bm_old) +{ + struct BMeshCreateParams params = {0}; +#ifndef USE_NEW_IDMAP + params.create_unique_ids = bm_old->idmap.flag &BM_HAS_IDS; + params.id_elem_mask = bm_old->idmap.flag & (BM_VERT | BM_EDGE | BM_LOOP | BM_FACE); + params.id_map = bm_old->idmap.flag & BM_HAS_ID_MAP; + params.no_reuse_ids = bm_old->idmap.flag & BM_NO_REUSE_IDS; +#endif + + params.copy_all_layers = true; + return BM_mesh_copy_ex(bm_old, ¶ms); +} + +void bm_init_idmap_cdlayers(BMesh *bm) +{ + if (!(bm->idmap.flag & BM_HAS_IDS)) { + return; + } + + bool temp_ids = !(bm->idmap.flag & BM_PERMANENT_IDS); + + int types[4] = {BM_VERT, BM_EDGE, BM_LOOP, BM_FACE}; + CustomData *cdatas[4] = {&bm->vdata, &bm->edata, &bm->ldata, &bm->pdata}; + + for (int i = 0; i < 4; i++) { + CustomDataLayer *layer; + + if (!(bm->idmap.flag & types[i])) { + continue; + } + + if (!CustomData_has_layer(cdatas[i], CD_MESH_ID)) { + BM_data_layer_add(bm, cdatas[i], CD_MESH_ID); + } + + layer = cdatas[i]->layers + CustomData_get_layer_index(cdatas[i], CD_MESH_ID); + layer->flag |= CD_FLAG_ELEM_NOCOPY; + + if (temp_ids) { + layer->flag |= CD_FLAG_TEMPORARY; + } + else { + layer->flag &= ~CD_FLAG_TEMPORARY; + } + } + + bm_update_idmap_cdlayers(bm); +} + +void bm_update_idmap_cdlayers(BMesh *bm) +{ + if (!(bm->idmap.flag & BM_HAS_IDS)) { + return; + } + + bm->idmap.cd_id_off[BM_VERT] = CustomData_get_offset(&bm->vdata, CD_MESH_ID); + bm->idmap.cd_id_off[BM_EDGE] = CustomData_get_offset(&bm->edata, CD_MESH_ID); + bm->idmap.cd_id_off[BM_LOOP] = CustomData_get_offset(&bm->ldata, CD_MESH_ID); + bm->idmap.cd_id_off[BM_FACE] = CustomData_get_offset(&bm->pdata, CD_MESH_ID); +} + +void bm_rebuild_idmap(BMesh *bm) +{ + CustomData *cdatas[4] = { + &bm->vdata, + &bm->edata, + &bm->ldata, + &bm->pdata, + }; + + if (bm->idmap.flag & BM_HAS_ID_MAP) { + if (bm->idmap.flag & BM_NO_REUSE_IDS) { + if (bm->idmap.ghash) { + BLI_ghash_clear(bm->idmap.ghash, NULL, NULL); + } + else { + bm->idmap.ghash = BLI_ghash_ptr_new("bm->idmap.ghash"); + } + } + else if (bm->idmap.map) { + memset(bm->idmap.map, 0, sizeof(void *) * bm->idmap.map_size); + } + } + + for (int i = 0; i < 4; i++) { + int type = 1 << i; + + if (!(bm->idmap.flag & type)) { + continue; + } + + int cd_off = bm->idmap.cd_id_off[type]; + cd_off = CustomData_get_offset(cdatas[i], CD_MESH_ID); + + if (bm->idmap.flag & BM_NO_REUSE_IDS) { + BLI_mempool_iter iter; + + BLI_mempool_iternew((&bm->vpool)[i], &iter); + BMElem *elem = (BMElem *)BLI_mempool_iterstep(&iter); + for (; elem; elem = (BMElem *)BLI_mempool_iterstep(&iter)) { + void **val; + + if (!BLI_ghash_ensure_p(bm->idmap.ghash, (void *)elem, &val)) { + *val = POINTER_FROM_INT(BM_ELEM_CD_GET_INT(elem, cd_off)); + } + } + } + else { + BLI_mempool_iter iter; + + BLI_mempool_iternew((&bm->vpool)[i], &iter); + BMElem *elem = (BMElem *)BLI_mempool_iterstep(&iter); + for (; elem; elem = (BMElem *)BLI_mempool_iterstep(&iter)) { + void **val; + int id = BM_ELEM_CD_GET_INT(elem, cd_off); + + if (!bm->idmap.map || bm->idmap.map_size <= id) { + int size = (2 + id); + size += size >> 1; + + if (!bm->idmap.map) { + bm->idmap.map = MEM_calloc_arrayN(size, sizeof(*bm->idmap.map), "bm->idmap.map"); + } + else { + bm->idmap.map = MEM_recallocN(bm->idmap.map, sizeof(void *) * size); + } + + bm->idmap.map_size = size; + } + + bm->idmap.map[BM_ELEM_CD_GET_INT(elem, cd_off)] = elem; + } + } + } +} diff --git a/source/blender/bmesh/intern/bmesh_construct.h b/source/blender/bmesh/intern/bmesh_construct.h index 036605e6b67..75ce48becdb 100644 --- a/source/blender/bmesh/intern/bmesh_construct.h +++ b/source/blender/bmesh/intern/bmesh_construct.h @@ -170,6 +170,9 @@ void BM_mesh_copy_init_customdata_all_layers(BMesh *bm_dst, const struct BMAllocTemplate *allocsize); BMesh *BM_mesh_copy(BMesh *bm_old); +/* BM -> ME */ +void BM_sort_disk_cycle(BMVert *v); + #ifdef __cplusplus } #endif diff --git a/source/blender/bmesh/intern/bmesh_core.c b/source/blender/bmesh/intern/bmesh_core.c index 5fbbd087d7e..939a33b9974 100644 --- a/source/blender/bmesh/intern/bmesh_core.c +++ b/source/blender/bmesh/intern/bmesh_core.c @@ -6,12 +6,20 @@ * Core BMesh functions for adding, removing BMesh elements. */ +//#define FORCE_BMESH_CHECK + +#if !defined(NDEBUG) || defined(FORCE_BMESH_CHECK) +# define BMESH_DEBUG +#endif + #include "MEM_guardedalloc.h" #include "BLI_alloca.h" #include "BLI_array.h" +#include "BLI_asan.h" #include "BLI_linklist_stack.h" #include "BLI_math_vector.h" +#include "BLI_smallhash.h" #include "BLI_utildefines_stack.h" #include "BLT_translation.h" @@ -22,7 +30,11 @@ #include "BKE_mesh.h" #include "bmesh.h" +#include "bmesh_log_intern.h" #include "intern/bmesh_private.h" +#include "range_tree.h" + +#include /* use so valgrinds memcheck alerts us when undefined index is used. * TESTING ONLY! */ @@ -38,6 +50,43 @@ #endif +void bm_elem_check_toolflags(BMesh *bm, BMElem *elem) +{ + int cd_off = -1; + MToolFlags *flags; + BLI_mempool *flagpool; + + switch (elem->head.htype) { + case BM_VERT: + cd_off = bm->vdata.typemap[CD_TOOLFLAGS]; + cd_off = cd_off != -1 ? bm->vdata.layers[cd_off].offset : -1; + flagpool = bm->vtoolflagpool; + break; + case BM_EDGE: + cd_off = bm->edata.typemap[CD_TOOLFLAGS]; + cd_off = cd_off != -1 ? bm->edata.layers[cd_off].offset : -1; + flagpool = bm->etoolflagpool; + break; + case BM_FACE: + cd_off = bm->pdata.typemap[CD_TOOLFLAGS]; + cd_off = cd_off != -1 ? bm->pdata.layers[cd_off].offset : -1; + flagpool = bm->ftoolflagpool; + break; + } + + if (cd_off == -1 || !flagpool) { + return; + } + + flags = (MToolFlags *)BM_ELEM_CD_GET_VOID_P(elem, cd_off); + if (!flags->flag) { + flags->flag = BLI_mempool_calloc(flagpool); + } +} + +/** + * \brief Main function for creating a new vertex. + */ BMVert *BM_vert_create(BMesh *bm, const float co[3], const BMVert *v_example, @@ -61,11 +110,6 @@ BMVert *BM_vert_create(BMesh *bm, v->head.hflag = 0; v->head.api_flag = 0; - /* allocate flags */ - if (bm->use_toolflags) { - ((BMVert_OFlag *)v)->oflags = bm->vtoolflagpool ? BLI_mempool_calloc(bm->vtoolflagpool) : NULL; - } - /* 'v->no' is handled by BM_elem_attrs_copy */ if (co) { copy_v3_v3(v->co, co); @@ -103,8 +147,13 @@ BMVert *BM_vert_create(BMesh *bm, } else { CustomData_bmesh_set_default(&bm->vdata, &v->head.data); + zero_v3(v->no); } + + if (!(create_flag & BM_CREATE_SKIP_ID)) { + bm_alloc_id(bm, (BMElem *)v); + } } else { if (v_example) { @@ -115,6 +164,13 @@ BMVert *BM_vert_create(BMesh *bm, } } + /* allocate flags */ + if (bm->use_toolflags && v->head.data) { + int cd_tflags = bm->vdata.layers[bm->vdata.typemap[CD_TOOLFLAGS]].offset; + MToolFlags *flags = (MToolFlags *)BM_ELEM_CD_GET_VOID_P(v, cd_tflags); + flags->flag = BLI_mempool_calloc(bm->vtoolflagpool); + } + BM_CHECK_ELEMENT(v); return v; @@ -149,11 +205,6 @@ BMEdge *BM_edge_create( e->head.hflag = BM_ELEM_SMOOTH | BM_ELEM_DRAW; e->head.api_flag = 0; - /* allocate flags */ - if (bm->use_toolflags) { - ((BMEdge_OFlag *)e)->oflags = bm->etoolflagpool ? BLI_mempool_calloc(bm->etoolflagpool) : NULL; - } - e->v1 = v1; e->v2 = v2; e->l = NULL; @@ -178,6 +229,17 @@ BMEdge *BM_edge_create( else { CustomData_bmesh_set_default(&bm->edata, &e->head.data); } + + if (!(create_flag & BM_CREATE_SKIP_ID)) { + bm_alloc_id(bm, (BMElem *)e); + } + } + + /* allocate flags */ + if (bm->use_toolflags && e->head.data) { + int cd_tflags = bm->edata.layers[bm->edata.typemap[CD_TOOLFLAGS]].offset; + MToolFlags *flags = (MToolFlags *)BM_ELEM_CD_GET_VOID_P(e, cd_tflags); + flags->flag = BLI_mempool_calloc(bm->etoolflagpool); } BM_CHECK_ELEMENT(e); @@ -204,7 +266,7 @@ static BMLoop *bm_loop_create(BMesh *bm, BLI_assert((l_example == NULL) || (l_example->head.htype == BM_LOOP)); BLI_assert(!(create_flag & 1)); -#ifndef NDEBUG +#ifdef BMESH_DEBUG if (l_example) { /* ensure passing a loop is either sharing the same vertex, or entirely disconnected * use to catch mistake passing in loop offset-by-one. */ @@ -251,6 +313,10 @@ static BMLoop *bm_loop_create(BMesh *bm, else { CustomData_bmesh_set_default(&bm->ldata, &l->head.data); } + + if (!(create_flag & BM_CREATE_SKIP_ID)) { + bm_alloc_id(bm, (BMElem *)l); + } } return l; @@ -324,14 +390,19 @@ BMFace *BM_face_copy( i++; } while ((l_iter = l_iter->next) != l_first); - f_copy = BM_face_create(bm_dst, verts, edges, f->len, NULL, BM_CREATE_SKIP_CD); + f_copy = BM_face_create( + bm_dst, verts, edges, f->len, NULL, BM_CREATE_SKIP_CD | BM_CREATE_SKIP_ID); BM_elem_attrs_copy(bm_src, bm_dst, f, f_copy); + bm_alloc_id(bm_dst, (BMElem *)f_copy); + bm_elem_check_toolflags(bm_dst, (BMElem *)f_copy); l_iter = l_first = BM_FACE_FIRST_LOOP(f); l_copy = BM_FACE_FIRST_LOOP(f_copy); do { BM_elem_attrs_copy(bm_src, bm_dst, l_iter, l_copy); + bm_alloc_id(bm_dst, (BMElem *)l_copy); + l_copy = l_copy->next; } while ((l_iter = l_iter->next) != l_first); @@ -362,11 +433,6 @@ BLI_INLINE BMFace *bm_face_create__internal(BMesh *bm) f->head.hflag = 0; f->head.api_flag = 0; - /* allocate flags */ - if (bm->use_toolflags) { - ((BMFace_OFlag *)f)->oflags = bm->ftoolflagpool ? BLI_mempool_calloc(bm->ftoolflagpool) : NULL; - } - #ifdef USE_BMESH_HOLES BLI_listbase_clear(&f->loops); #else @@ -446,6 +512,10 @@ BMFace *BM_face_create(BMesh *bm, CustomData_bmesh_set_default(&bm->pdata, &f->head.data); zero_v3(f->no); } + + if (!(create_flag & BM_CREATE_SKIP_ID)) { + bm_alloc_id(bm, (BMElem *)f); + } } else { if (f_example) { @@ -456,6 +526,13 @@ BMFace *BM_face_create(BMesh *bm, } } + /* allocate flags */ + if (bm->use_toolflags && f->head.data) { + int cd_tflags = bm->pdata.layers[bm->pdata.typemap[CD_TOOLFLAGS]].offset; + MToolFlags *flags = (MToolFlags *)BM_ELEM_CD_GET_VOID_P(f, cd_tflags); + flags->flag = BLI_mempool_calloc(bm->ftoolflagpool); + } + BM_CHECK_ELEMENT(f); return f; @@ -482,44 +559,47 @@ BMFace *BM_face_create_verts(BMesh *bm, return BM_face_create(bm, vert_arr, edge_arr, len, f_example, create_flag); } -#ifndef NDEBUG +typedef enum { + IS_OK = 0, + IS_NULL = (1 << 0), + IS_WRONG_TYPE = (1 << 1), + + IS_VERT_WRONG_EDGE_TYPE = (1 << 2), + + IS_EDGE_NULL_DISK_LINK = (1 << 3), + IS_EDGE_WRONG_LOOP_TYPE = (1 << 4), + IS_EDGE_WRONG_FACE_TYPE = (1 << 5), + IS_EDGE_NULL_RADIAL_LINK = (1 << 6), + IS_EDGE_ZERO_FACE_LENGTH = (1 << 7), + + IS_LOOP_WRONG_FACE_TYPE = (1 << 8), + IS_LOOP_WRONG_EDGE_TYPE = (1 << 9), + IS_LOOP_WRONG_VERT_TYPE = (1 << 10), + IS_LOOP_VERT_NOT_IN_EDGE = (1 << 11), + IS_LOOP_NULL_CYCLE_LINK = (1 << 12), + IS_LOOP_ZERO_FACE_LENGTH = (1 << 13), + IS_LOOP_WRONG_FACE_LENGTH = (1 << 14), + IS_LOOP_WRONG_RADIAL_LENGTH = (1 << 15), + + IS_FACE_NULL_LOOP = (1 << 16), + IS_FACE_WRONG_LOOP_FACE = (1 << 17), + IS_FACE_NULL_EDGE = (1 << 18), + IS_FACE_NULL_VERT = (1 << 19), + IS_FACE_LOOP_VERT_NOT_IN_EDGE = (1 << 20), + IS_FACE_LOOP_WRONG_RADIAL_LENGTH = (1 << 21), + IS_FACE_LOOP_WRONG_DISK_LENGTH = (1 << 22), + IS_FACE_LOOP_DUPE_LOOP = (1 << 23), + IS_FACE_LOOP_DUPE_VERT = (1 << 24), + IS_FACE_LOOP_DUPE_EDGE = (1 << 25), + IS_FACE_WRONG_LENGTH = (1 << 26), +} BMeshInternalError; + +#ifdef BMESH_DEBUG int bmesh_elem_check(void *element, const char htype) { BMHeader *head = element; - enum { - IS_NULL = (1 << 0), - IS_WRONG_TYPE = (1 << 1), - - IS_VERT_WRONG_EDGE_TYPE = (1 << 2), - - IS_EDGE_NULL_DISK_LINK = (1 << 3), - IS_EDGE_WRONG_LOOP_TYPE = (1 << 4), - IS_EDGE_WRONG_FACE_TYPE = (1 << 5), - IS_EDGE_NULL_RADIAL_LINK = (1 << 6), - IS_EDGE_ZERO_FACE_LENGTH = (1 << 7), - - IS_LOOP_WRONG_FACE_TYPE = (1 << 8), - IS_LOOP_WRONG_EDGE_TYPE = (1 << 9), - IS_LOOP_WRONG_VERT_TYPE = (1 << 10), - IS_LOOP_VERT_NOT_IN_EDGE = (1 << 11), - IS_LOOP_NULL_CYCLE_LINK = (1 << 12), - IS_LOOP_ZERO_FACE_LENGTH = (1 << 13), - IS_LOOP_WRONG_FACE_LENGTH = (1 << 14), - IS_LOOP_WRONG_RADIAL_LENGTH = (1 << 15), - - IS_FACE_NULL_LOOP = (1 << 16), - IS_FACE_WRONG_LOOP_FACE = (1 << 17), - IS_FACE_NULL_EDGE = (1 << 18), - IS_FACE_NULL_VERT = (1 << 19), - IS_FACE_LOOP_VERT_NOT_IN_EDGE = (1 << 20), - IS_FACE_LOOP_WRONG_RADIAL_LENGTH = (1 << 21), - IS_FACE_LOOP_WRONG_DISK_LENGTH = (1 << 22), - IS_FACE_LOOP_DUPE_LOOP = (1 << 23), - IS_FACE_LOOP_DUPE_VERT = (1 << 24), - IS_FACE_LOOP_DUPE_EDGE = (1 << 25), - IS_FACE_WRONG_LENGTH = (1 << 26), - } err = 0; + BMeshInternalError err = IS_OK; if (!element) { return IS_NULL; @@ -694,56 +774,147 @@ int bmesh_elem_check(void *element, const char htype) break; } - BMESH_ASSERT(err == 0); + // BMESH_ASSERT(err == 0); return err; } -#endif /* NDEBUG */ +#endif /* BMESH_DEBUG */ /** * low level function, only frees the vert, * doesn't change or adjust surrounding geometry */ -static void bm_kill_only_vert(BMesh *bm, BMVert *v) +void bm_kill_only_vert(BMesh *bm, BMVert *v) { bm->totvert--; bm->elem_index_dirty |= BM_VERT; bm->elem_table_dirty |= BM_VERT; bm->spacearr_dirty |= BM_SPACEARR_DIRTY_ALL; + bm_free_id(bm, (BMElem *)v); + BM_select_history_remove(bm, v); + if (bm->vtoolflagpool) { + MToolFlags *flags = BM_ELEM_CD_GET_VOID_P( + v, bm->vdata.layers[bm->vdata.typemap[CD_TOOLFLAGS]].offset); + + BLI_mempool_free(bm->vtoolflagpool, flags->flag); + flags->flag = NULL; + } + if (v->head.data) { CustomData_bmesh_free_block(&bm->vdata, &v->head.data); } - if (bm->vtoolflagpool) { - BLI_mempool_free(bm->vtoolflagpool, ((BMVert_OFlag *)v)->oflags); - } BLI_mempool_free(bm->vpool, v); } +#ifdef WITH_BM_ID_FREELIST +void bm_id_freelist_push(BMesh *bm, uint id); +#endif + +// does not modify actual element ids +void BM_clear_ids(BMesh *bm) +{ + if (!(bm->idmap.flag & BM_HAS_IDS)) { + return; + } + + if (bm->idmap.map) { + memset(bm->idmap.map, 0, sizeof(void *) * bm->idmap.map_size); + } + else if (bm->idmap.ghash) { + BLI_ghash_clear(bm->idmap.ghash, NULL, NULL); + } + +#ifndef WITH_BM_ID_FREELIST + if (bm->idmap.idtree) { + range_tree_uint_free(bm->idmap.idtree); + } + + bm->idmap.idtree = range_tree_uint_alloc(0, (uint)-1); +#else + if (bm->idmap.freelist) { + MEM_freeN(bm->idmap.freelist); + bm->idmap.freelist = NULL; + } + + if (bm->idmap.free_ids) { + MEM_SAFE_FREE(bm->idmap.free_ids); + bm->idmap.free_ids = NULL; + } + + bm->idmap.freelist_len = 0; + bm->idmap.freelist_size = 0; +#endif +} + +void BM_reassign_ids(BMesh *bm) +{ + BM_clear_ids(bm); + + int iters[] = {BM_VERTS_OF_MESH, BM_EDGES_OF_MESH, BM_FACES_OF_MESH, BM_FACES_OF_MESH}; + + for (int i = 0; i < 4; i++) { + int htype = 1 << i; + + if (!(bm->idmap.flag & htype)) { + continue; + } + + BMElem *elem; + BMIter iter; + + if (htype == BM_LOOP) { + BMFace *f; + BM_ITER_MESH (f, &iter, bm, iters[i]) { + + BMLoop *l = f->l_first; + do { + l = l->next; + } while (l != f->l_first); + + bm_alloc_id(bm, (BMElem *)l); + } + } + else { + BM_ITER_MESH (elem, &iter, bm, iters[i]) { + bm_alloc_id(bm, elem); + } + } + } +} + /** * low level function, only frees the edge, * doesn't change or adjust surrounding geometry */ -static void bm_kill_only_edge(BMesh *bm, BMEdge *e) +void bm_kill_only_edge(BMesh *bm, BMEdge *e) { bm->totedge--; bm->elem_index_dirty |= BM_EDGE; bm->elem_table_dirty |= BM_EDGE; bm->spacearr_dirty |= BM_SPACEARR_DIRTY_ALL; + bm_free_id(bm, (BMElem *)e); + BM_select_history_remove(bm, (BMElem *)e); + if (bm->etoolflagpool) { + MToolFlags *flags = BM_ELEM_CD_GET_VOID_P( + e, bm->edata.layers[bm->edata.typemap[CD_TOOLFLAGS]].offset); + + BLI_mempool_free(bm->etoolflagpool, flags->flag); + } + if (e->head.data) { CustomData_bmesh_free_block(&bm->edata, &e->head.data); } - if (bm->etoolflagpool) { - BLI_mempool_free(bm->etoolflagpool, ((BMEdge_OFlag *)e)->oflags); - } + e->l = NULL; + BLI_mempool_free(bm->epool, e); } @@ -751,7 +922,7 @@ static void bm_kill_only_edge(BMesh *bm, BMEdge *e) * low level function, only frees the face, * doesn't change or adjust surrounding geometry */ -static void bm_kill_only_face(BMesh *bm, BMFace *f) +void bm_kill_only_face(BMesh *bm, BMFace *f) { if (bm->act_face == f) { bm->act_face = NULL; @@ -762,15 +933,21 @@ static void bm_kill_only_face(BMesh *bm, BMFace *f) bm->elem_table_dirty |= BM_FACE; bm->spacearr_dirty |= BM_SPACEARR_DIRTY_ALL; + bm_free_id(bm, (BMElem *)f); + BM_select_history_remove(bm, (BMElem *)f); + if (bm->ftoolflagpool) { + MToolFlags *flags = BM_ELEM_CD_GET_VOID_P( + f, bm->pdata.layers[bm->pdata.typemap[CD_TOOLFLAGS]].offset); + + BLI_mempool_free(bm->ftoolflagpool, flags->flag); + } + if (f->head.data) { CustomData_bmesh_free_block(&bm->pdata, &f->head.data); } - if (bm->ftoolflagpool) { - BLI_mempool_free(bm->ftoolflagpool, ((BMFace_OFlag *)f)->oflags); - } BLI_mempool_free(bm->fpool, f); } @@ -778,12 +955,14 @@ static void bm_kill_only_face(BMesh *bm, BMFace *f) * low level function, only frees the loop, * doesn't change or adjust surrounding geometry */ -static void bm_kill_only_loop(BMesh *bm, BMLoop *l) +void bm_kill_only_loop(BMesh *bm, BMLoop *l) { bm->totloop--; bm->elem_index_dirty |= BM_LOOP; bm->spacearr_dirty |= BM_SPACEARR_DIRTY_ALL; + bm_free_id(bm, (BMElem *)l); + if (l->head.data) { CustomData_bmesh_free_block(&bm->ldata, &l->head.data); } @@ -831,7 +1010,7 @@ void BM_face_kill(BMesh *bm, BMFace *f) BMLoopList *ls, *ls_next; #endif -#ifdef NDEBUG +#ifndef BMESH_DEBUG /* check length since we may be removing degenerate faces */ if (f->len >= 3) { BM_CHECK_ELEMENT(f); @@ -1021,7 +1200,7 @@ void bmesh_kernel_loop_reverse(BMesh *bm, /* step to next (now swapped) */ } while ((l_iter = l_iter->prev) != l_first); -#ifndef NDEBUG +#ifdef BMESH_DEBUG /* validate radial */ int i; for (i = 0, l_iter = l_first; i < f->len; i++, l_iter = l_iter->next) { @@ -1332,6 +1511,13 @@ static BMFace *bm_face_create__sfme(BMesh *bm, BMFace *f_example) #endif BM_elem_attrs_copy(bm, bm, f_example, f); + bm_alloc_id(bm, (BMElem *)f); + + if (bm->use_toolflags && f->head.data) { + int cd_tflags = bm->pdata.layers[bm->pdata.typemap[CD_TOOLFLAGS]].offset; + MToolFlags *flags = (MToolFlags *)BM_ELEM_CD_GET_VOID_P(f, cd_tflags); + flags->flag = BLI_mempool_calloc(bm->ftoolflagpool); + } return f; } @@ -1480,7 +1666,7 @@ BMVert *bmesh_kernel_split_edge_make_vert(BMesh *bm, BMVert *tv, BMEdge *e, BMEd BMLoop *l_next; BMEdge *e_new; BMVert *v_new, *v_old; -#ifndef NDEBUG +#ifdef BMESH_DEBUG int valence1, valence2; bool edok; int i; @@ -1490,7 +1676,7 @@ BMVert *bmesh_kernel_split_edge_make_vert(BMesh *bm, BMVert *tv, BMEdge *e, BMEd v_old = BM_edge_other_vert(e, tv); -#ifndef NDEBUG +#ifdef BMESH_DEBUG valence1 = bmesh_disk_count(v_old); valence2 = bmesh_disk_count(tv); #endif @@ -1511,7 +1697,7 @@ BMVert *bmesh_kernel_split_edge_make_vert(BMesh *bm, BMVert *tv, BMEdge *e, BMEd /* add e_new to tv's disk cycle */ bmesh_disk_edge_append(e_new, tv); -#ifndef NDEBUG +#ifdef BMESH_DEBUG /* verify disk cycles */ edok = bmesh_disk_validate(valence1, v_old->e, v_old); BMESH_ASSERT(edok != false); @@ -1526,7 +1712,7 @@ BMVert *bmesh_kernel_split_edge_make_vert(BMesh *bm, BMVert *tv, BMEdge *e, BMEd e->l = NULL; if (l_next) { BMLoop *l_new, *l; -#ifndef NDEBUG +#ifdef BMESH_DEBUG int radlen = bmesh_radial_length(l_next); #endif bool is_first = true; @@ -1574,7 +1760,7 @@ BMVert *bmesh_kernel_split_edge_make_vert(BMesh *bm, BMVert *tv, BMEdge *e, BMEd } } -#ifndef NDEBUG +#ifdef BMESH_DEBUG /* verify length of radial cycle */ edok = bmesh_radial_validate(radlen, e->l); BMESH_ASSERT(edok != false); @@ -1639,7 +1825,7 @@ BMEdge *bmesh_kernel_join_edge_kill_vert(BMesh *bm, BMEdge *e_old; BMVert *v_old, *v_target; BMLoop *l_kill; -#ifndef NDEBUG +#ifdef BMESH_DEBUG int radlen, i; bool edok; #endif @@ -1651,7 +1837,7 @@ BMEdge *bmesh_kernel_join_edge_kill_vert(BMesh *bm, } if (bmesh_disk_count_at_most(v_kill, 3) == 2) { -#ifndef NDEBUG +#ifdef BMESH_DEBUG int valence1, valence2; BMLoop *l; #endif @@ -1672,7 +1858,7 @@ BMEdge *bmesh_kernel_join_edge_kill_vert(BMesh *bm, /* Candidates for being duplicate. */ BLI_SMALLSTACK_DECLARE(faces_duplicate_candidate, BMFace *); -#ifndef NDEBUG +#ifdef BMESH_DEBUG /* For verification later, count valence of 'v_old' and 'v_target' */ valence1 = bmesh_disk_count(v_old); valence2 = bmesh_disk_count(v_target); @@ -1687,7 +1873,7 @@ BMEdge *bmesh_kernel_join_edge_kill_vert(BMesh *bm, /* remove e_kill from 'v_target's disk cycle */ bmesh_disk_edge_remove(e_kill, v_target); -#ifndef NDEBUG +#ifdef BMESH_DEBUG /* deal with radial cycle of e_kill */ radlen = bmesh_radial_length(e_kill->l); #endif @@ -1725,7 +1911,7 @@ BMEdge *bmesh_kernel_join_edge_kill_vert(BMesh *bm, } while ((l_kill = l_kill_next) != e_kill->l); /* `e_kill->l` is invalid but the edge is freed next. */ -#ifndef NDEBUG +#ifdef BMESH_DEBUG /* Validate radial cycle of e_old */ edok = bmesh_radial_validate(radlen, e_old->l); BMESH_ASSERT(edok != false); @@ -1742,7 +1928,7 @@ BMEdge *bmesh_kernel_join_edge_kill_vert(BMesh *bm, v_kill->e = NULL; } -#ifndef NDEBUG +#ifdef BMESH_DEBUG /* Validate disk cycle lengths of 'v_old', 'v_target' are unchanged */ edok = bmesh_disk_validate(valence1, v_old->e, v_old); BMESH_ASSERT(edok != false); @@ -1766,7 +1952,7 @@ BMEdge *bmesh_kernel_join_edge_kill_vert(BMesh *bm, if (check_edge_exists) { if (e_splice) { /* removes e_splice */ - BM_edge_splice(bm, e_old, e_splice); + BM_edge_splice(bm, e_old, e_splice, false); } } @@ -1795,12 +1981,1101 @@ BMEdge *bmesh_kernel_join_edge_kill_vert(BMesh *bm, return NULL; } +static void check_vert_faces(BMVert *v_target) +{ + BMEdge *e = v_target->e; + if (e) { + do { + BM_CHECK_ELEMENT(e); + if (e->l) { + BMLoop *l = e->l; + + do { + BM_CHECK_ELEMENT(l->f); + } while ((l = l->radial_next) != e->l); + } + } while ((e = BM_DISK_EDGE_NEXT(e, v_target)) != v_target->e); + } +} + +#ifdef _ +# undef _ +#endif + +#define _(s) \ + case s: \ + return #s; + +static const char *get_err_code_str(BMeshInternalError code) +{ + switch (code) { + _(IS_OK) + _(IS_NULL) + _(IS_WRONG_TYPE) + + _(IS_VERT_WRONG_EDGE_TYPE) + + _(IS_EDGE_NULL_DISK_LINK) + _(IS_EDGE_WRONG_LOOP_TYPE) + _(IS_EDGE_WRONG_FACE_TYPE) + _(IS_EDGE_NULL_RADIAL_LINK) + _(IS_EDGE_ZERO_FACE_LENGTH) + + _(IS_LOOP_WRONG_FACE_TYPE) + _(IS_LOOP_WRONG_EDGE_TYPE) + _(IS_LOOP_WRONG_VERT_TYPE) + _(IS_LOOP_VERT_NOT_IN_EDGE) + _(IS_LOOP_NULL_CYCLE_LINK) + _(IS_LOOP_ZERO_FACE_LENGTH) + _(IS_LOOP_WRONG_FACE_LENGTH) + _(IS_LOOP_WRONG_RADIAL_LENGTH) + + _(IS_FACE_NULL_LOOP) + _(IS_FACE_WRONG_LOOP_FACE) + _(IS_FACE_NULL_EDGE) + _(IS_FACE_NULL_VERT) + _(IS_FACE_LOOP_VERT_NOT_IN_EDGE) + _(IS_FACE_LOOP_WRONG_RADIAL_LENGTH) + _(IS_FACE_LOOP_WRONG_DISK_LENGTH) + _(IS_FACE_LOOP_DUPE_LOOP) + _(IS_FACE_LOOP_DUPE_VERT) + _(IS_FACE_LOOP_DUPE_EDGE) + _(IS_FACE_WRONG_LENGTH) + } + + return "(unknown-code)"; +} +#undef _ + +static char *get_err_str(int err) +{ + static char buf[1024]; + buf[0] = 0; + + for (int i = 0; i < 27; i++) { + if (err & (1 << i)) { + strcat(buf, get_err_code_str(1 << i)); + } + } + + return buf; +} + +static void bm_local_obj_free(char *str, char *fixed) +{ + if (str != fixed) { + MEM_freeN(str); + } +} + +#define LOCAL_OBJ_SIZE 512 + +static char *obj_append_line(const char *line, char *str, char *fixed, int *size, int *i) +{ + int len = (int)strlen(line); + + if (*i + len + 1 >= *size) { + *size += len + ((*size) >> 1); + + if (str == fixed) { + str = MEM_mallocN(*size, "buf"); + memcpy(str, fixed, LOCAL_OBJ_SIZE); + } + else { + str = MEM_reallocN(str, *size); + } + } + + memcpy(str + *i, line, len); + str[*i + len] = 0; + + *i += len; + + return str; +} + +static char *bm_save_local_obj_text( + BMesh *bm, int depth, char buf[LOCAL_OBJ_SIZE], const char *fmt, ...) +{ + va_list vl; + va_start(vl, fmt); + + buf[0] = 0; + + BMVert **vs = NULL, **initial_vs = NULL; + BMEdge **es = NULL, **initial_es = NULL; + BMFace **fs = NULL, **initial_fs = NULL; + + BLI_array_staticdeclare(vs, 64); + BLI_array_staticdeclare(es, 64); + BLI_array_staticdeclare(fs, 64); + + BLI_array_staticdeclare(initial_vs, 8); + BLI_array_staticdeclare(initial_es, 8); + BLI_array_staticdeclare(initial_fs, 8); + + SmallHash visit; + BLI_smallhash_init(&visit); + + const char *c = fmt; + while (*c) { + if (*c == ' ' || *c == '\t') { + c++; + continue; + } + + void *ptr = va_arg(vl, void *); + + switch (*c) { + case 'v': + BLI_array_append(vs, (BMVert *)ptr); + BLI_array_append(initial_vs, (BMVert *)ptr); + break; + case 'e': + BLI_array_append(es, (BMEdge *)ptr); + BLI_array_append(initial_es, (BMEdge *)ptr); + break; + case 'f': + BLI_array_append(fs, (BMFace *)ptr); + BLI_array_append(initial_fs, (BMFace *)ptr); + break; + } + + c++; + } + + va_end(vl); + + int tag = 4; + for (int i = 0; i < BLI_array_len(fs); i++) { + BMFace *f = fs[i]; + BMLoop *l = f->l_first; + + do { + l->v->head.api_flag &= ~tag; + l->e->head.api_flag &= ~tag; + } while ((l = l->next) != f->l_first); + } + + for (int i = 0; i < BLI_array_len(es); i++) { + BMEdge *e = es[i]; + + e->v1->head.api_flag &= ~tag; + e->v2->head.api_flag &= ~tag; + } + + for (int i = 0; i < BLI_array_len(vs); i++) { + vs[i]->head.api_flag |= tag; + } + for (int i = 0; i < BLI_array_len(es); i++) { + BMEdge *e = es[i]; + + if (!(e->v1->head.api_flag & tag)) { + BLI_array_append(vs, e->v1); + e->v1->head.api_flag |= tag; + } + + if (!(e->v2->head.api_flag & tag)) { + BLI_array_append(vs, e->v2); + e->v2->head.api_flag |= tag; + } + + e->head.api_flag |= tag; + } + + for (int i = 0; i < BLI_array_len(fs); i++) { + BMFace *f = fs[i]; + BMLoop *l = f->l_first; + + do { + if (!(l->v->head.api_flag & tag)) { + BLI_array_append(vs, l->v); + l->v->head.api_flag |= tag; + } + + if (!(l->e->head.api_flag & tag)) { + BLI_array_append(es, l->e); + l->e->head.api_flag |= tag; + } + } while ((l = l->next) != f->l_first); + } + + struct stack { + BMVert *v; + int depth; + } *stack = NULL; + BLI_array_staticdeclare(stack, 256); + + SmallHash elemset; + BLI_smallhash_init(&elemset); + + for (int i = 0; i < BLI_array_len(vs); i++) { + BLI_smallhash_insert(&elemset, (uintptr_t)vs[i], NULL); + } + for (int i = 0; i < BLI_array_len(es); i++) { + BLI_smallhash_insert(&elemset, (uintptr_t)es[i], NULL); + } + for (int i = 0; i < BLI_array_len(fs); i++) { + BLI_smallhash_insert(&elemset, (uintptr_t)fs[i], NULL); + } + + for (int i = 0; i < BLI_array_len(vs); i++) { + int si = 0; + + BLI_array_clear(stack); + + // connected islands only + if (i > 0) { + break; + } + + BLI_array_grow_one(stack); + + stack[si].v = vs[i]; + stack[si].depth = 0; + si++; + + while (si > 0) { + BLI_array_len_set(stack, BLI_array_len(stack) - 1); + + if (si >= 8192) { + printf("%s: stack error\n", __func__); + } + + si--; + + BMVert *v = stack[si].v; + int startdepth = stack[si].depth; + + void **val; + + if (!BLI_smallhash_ensure_p(&elemset, (uintptr_t)v, &val)) { + *val = NULL; + BLI_array_append(vs, v); + } + + if (!v->e || stack[si].depth > depth) { + continue; + } + + BMEdge *e = v->e; + do { + if (!BLI_smallhash_ensure_p(&visit, (uintptr_t)e, &val)) { + BLI_array_grow_one(stack); + + *val = NULL; + stack[si].v = e->v1; + stack[si].depth = startdepth + 1; + si++; + + BLI_array_grow_one(stack); + + stack[si].v = e->v2; + stack[si].depth = startdepth + 1; + si++; + } + + if (!e->l) { + continue; + } + + BMLoop *l = e->l; + do { + if (!BLI_smallhash_ensure_p(&visit, (uintptr_t)l->f, &val)) { + if (!BLI_smallhash_ensure_p(&elemset, (uintptr_t)l->f, &val)) { + *val = NULL; + BLI_array_append(fs, l->f); + } + + BMLoop *l2 = l; + do { + if (!BLI_smallhash_ensure_p(&visit, (uintptr_t)l->v, &val)) { + BLI_array_grow_one(stack); + + *val = NULL; + stack[si].v = l->v; + stack[si].depth = startdepth + 1; + si++; + } + } while ((l2 = l2->next) != l); + } + } while ((l = l->radial_next) != e->l); + } while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e); + } + } + + char *str = buf; + int size = LOCAL_OBJ_SIZE - 1; + int stri = 0; + + for (int i = 0; i < BLI_array_len(es); i++) { + es[i]->head.api_flag &= ~tag; + } + + char line[128]; + line[0] = 0; + + for (int i = 0; i < BLI_array_len(vs); i++) { + vs[i]->head.api_flag &= ~tag; + } + for (int i = 0; i < BLI_array_len(es); i++) { + es[i]->head.api_flag &= ~tag; + } + for (int i = 0; i < BLI_array_len(fs); i++) { + fs[i]->head.api_flag &= ~tag; + } + + for (int i = 0; i < BLI_array_len(initial_vs); i++) { + initial_vs[i]->head.api_flag |= tag; + } + + for (int i = 0; i < BLI_array_len(initial_es); i++) { + initial_es[i]->head.api_flag |= tag; + initial_es[i]->v1->head.api_flag |= tag; + initial_es[i]->v2->head.api_flag |= tag; + } + + for (int i = 0; i < BLI_array_len(initial_fs); i++) { + BMFace *f = initial_fs[i]; + + f->head.api_flag |= tag; + BMLoop *l = f->l_first; + + do { + l->v->head.api_flag |= tag; + } while ((l = l->next) != f->l_first); + } + + for (int i = 0; i < BLI_array_len(vs); i++) { + BMVert *v = vs[i]; + + if (v->head.api_flag & tag) { + sprintf(line, "#select\n"); + str = obj_append_line(line, str, buf, &size, &stri); + } + + v->head.index = i + 1; + sprintf(line, "v %.4f %.4f %.4f\n", v->co[0], v->co[1], v->co[2]); + + str = obj_append_line(line, str, buf, &size, &stri); + } + + /* save wire edges */ + for (int i = 0; i < BLI_array_len(es); i++) { + BMEdge *e = es[i]; + + if (e->l) { + continue; + } + + sprintf(line, "l %d %d\n", e->v1->head.index, e->v2->head.index); + str = obj_append_line(line, str, buf, &size, &stri); + } + + for (int i = 0; i < BLI_array_len(fs); i++) { + BMFace *f = fs[i]; + BMLoop *l = f->l_first; + + sprintf(line, "f"); + str = obj_append_line(line, str, buf, &size, &stri); + + do { + sprintf(line, " %d", l->v->head.index); + + str = obj_append_line(line, str, buf, &size, &stri); + } while ((l = l->next) != f->l_first); + + str = obj_append_line("\n", str, buf, &size, &stri); + } + + BLI_smallhash_release(&visit); + BLI_smallhash_release(&elemset); + + BLI_array_free(vs); + BLI_array_free(es); + BLI_array_free(fs); + BLI_array_free(stack); + + BLI_array_free(initial_vs); + BLI_array_free(initial_es); + BLI_array_free(initial_fs); + + return str; +} +/** + * \brief Join Vert Kill Edge (JVKE) + * + * Collapse an edge, merging surrounding data. + * + * Unlike #BM_vert_collapse_edge & #bmesh_kernel_join_edge_kill_vert + * which only handle 2 valence verts, + * this can handle any number of connected edges/faces. + * + *
+ * Before: -> After:
+ * +-+-+-+    +-+-+-+
+ * | | | |    | \ / |
+ * +-+-+-+    +--+--+
+ * | | | |    | / \ |
+ * +-+-+-+    +-+-+-+
+ * 
+ */ + +static void trigger_jvke_error(int err, char *obj_text) +{ + printf("========= ERROR %s============\n\n%s\n\n", get_err_str(err), obj_text); +} + +char *_last_local_obj = NULL; + +//#define JVKE_DEBUG + +#ifdef JVKE_DEBUG +# define JVKE_CHECK_ELEMENT(elem) \ + { \ + int err = 0; \ + if ((err = bmesh_elem_check(elem, (elem)->head.htype))) { \ + trigger_jvke_error(err, saved_obj); \ + } \ + } +#else +# define JVKE_CHECK_ELEMENT(elem) +#endif + +static bool cleanup_vert(BMesh *bm, BMVert *v, const BMTracer *tracer) +{ + BMEdge *e = v->e; + + if (!e->l || e->l->f == e->l->radial_next->f) { + return false; + } + + bm_logstack_push(); + + BMFace *f_example = NULL; + + do { + BMLoop *l = e->l; + if (!l) { + continue; + } + + f_example = l->f; + + BMLoop *lnext; + + do { + if (tracer) { + tracer->on_face_kill(bm, l->f, tracer->userdata); + } + } while ((l = l->radial_next) != e->l); + } while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e); + + if (tracer) { + do { + tracer->on_edge_kill(bm, e, tracer->userdata); + } while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e); + } + + if (tracer) { + tracer->on_vert_kill(bm, v, tracer->userdata); + } + + BMVert *v1 = BM_edge_other_vert(v->e, v); + BMVert *v2 = BM_edge_other_vert(BM_DISK_EDGE_NEXT(v->e, v), v); + BMVert *v3 = BM_edge_other_vert(BM_DISK_EDGE_NEXT(BM_DISK_EDGE_NEXT(v->e, v), v), v); + + BMFace *f = BM_face_create_quad_tri(bm, v1, v2, v3, NULL, f_example, BM_CREATE_NOP); + BMLoop *l = f->l_first; + + // ensure correct winding + do { + if (l->radial_next != l && l->radial_next->v == l->v) { + BM_face_normal_flip(bm, f); + break; + } + } while ((l = l->next) != f->l_first); + + BM_vert_kill(bm, v); + + if (tracer) { + tracer->on_face_create(bm, f, tracer->userdata); + } + + bm_logstack_pop(); + return true; +} + +static void bmesh_kernel_check_val3_vert(BMesh *bm, BMEdge *e, const BMTracer *tracer) +{ + if (!e->l) { + return; + } + + bm_logstack_push(); + + bool stop; + + do { + stop = true; + + BMLoop *l = e->l; + + if (!l) { + break; + } + + do { + BMLoop *l2 = l->prev; + + if (l2 == l2->radial_next) { + continue; + } + + if (BM_vert_edge_count(l2->v) == 3) { + if (cleanup_vert(bm, l2->v, tracer)) { + stop = false; + break; + } + } + } while ((l = l->radial_next) != e->l); + } while (!stop); + + bm_logstack_pop(); +} + BMVert *bmesh_kernel_join_vert_kill_edge(BMesh *bm, - BMEdge *e_kill, + BMEdge *e, BMVert *v_kill, const bool do_del, - const bool check_edge_exists, - const bool kill_degenerate_faces) + const bool combine_flags, + const BMTracer *tracer) +{ + BMVert *v_conn = BM_edge_other_vert(e, v_kill); + +#ifdef JVKE_DEBUG + char buf[LOCAL_OBJ_SIZE]; + + bool have_boundary = false; + + if (_last_local_obj) { + free(_last_local_obj); + } + + char *saved_obj = bm_save_local_obj_text(bm, 2, buf, "e", e); + _last_local_obj = strdup(saved_obj); +#endif + + /* Free any surrounding valence-3 rings disconnected from the edge. */ + bmesh_kernel_check_val3_vert(bm, e, tracer); + + BMFace **fs = NULL; + BMEdge **deles = NULL; + BLI_array_staticdeclare(fs, 32); + BLI_array_staticdeclare(deles, 32); + + BMEdge **es = NULL; + BLI_array_staticdeclare(es, 32); + + BMVert *v_del = BM_edge_other_vert(e, v_conn); + const int tag = _FLAG_WALK_ALT; // using bmhead.api_flag here + const int dup_tag = _FLAG_OVERLAP; + const int final_tag = _FLAG_JF; + + JVKE_CHECK_ELEMENT(v_conn); + JVKE_CHECK_ELEMENT(v_del); + +#define _OTHER_TRACES // paranoia (and likely duplicate) calls to tracer callbacks + + /* first clear tags */ + for (int i = 0; i < 2; i++) { + BMVert *v = i ? v_del : v_conn; + + if (!v->e) { + continue; + } + + BMEdge *e2 = v->e; + do { + /* build list of edges if needed for tracing */ + if (tracer) { + bool ok = true; + + for (int j = 0; j < BLI_array_len(es); j++) { + if (es[j] == e2) { + ok = false; + break; + } + } + + if (ok) { + BLI_array_append(es, e2); + } + } + + if (!e2->l) { + continue; + } + + BMLoop *l = e2->l; + do { + BM_ELEM_API_FLAG_DISABLE(l->f, tag); + +#ifdef JVKE_DEBUG + if (l->radial_next == l) { + have_boundary = true; + } +#endif + + BM_ELEM_API_FLAG_DISABLE(l->f, dup_tag); + BM_ELEM_API_FLAG_DISABLE(l->e, dup_tag); + BM_ELEM_API_FLAG_DISABLE(l->v, dup_tag); + } while ((l = l->radial_next) != e2->l); + } while ((e2 = BM_DISK_EDGE_NEXT(e2, v)) != v->e); + } + + /* now build face list */ + for (int i = 0; i < 2; i++) { + BMVert *v = i ? v_del : v_conn; + BMEdge *e2 = v->e; + + if (!e2 || !e2->l) { + continue; + } + + do { + BMLoop *l = e2->l; + + if (!l) { + continue; + } + + do { + if (!BM_ELEM_API_FLAG_TEST(l->f, tag)) { + BM_ELEM_API_FLAG_ENABLE(l->f, tag); + + BMLoop *l2 = l; + do { + BM_ELEM_API_FLAG_DISABLE(l2->e, tag); + } while ((l2 = l2->next) != l); + + BLI_array_append(fs, l->f); + } + } while ((l = l->radial_next) != e2->l); + } while ((e2 = BM_DISK_EDGE_NEXT(e2, v)) != v->e); + } + + if (tracer) { + for (int i = 0; i < BLI_array_len(fs); i++) { + tracer->on_face_kill(bm, fs[i], tracer->userdata); + } + } + + if (tracer) { + for (int i = 0; i < BLI_array_len(es); i++) { + tracer->on_edge_kill(bm, es[i], tracer->userdata); + } + } + + /* unlink loops */ + for (int i = 0; i < BLI_array_len(fs); i++) { + BMFace *f = fs[i]; + BMLoop *l = f->l_first; + + do { + BMEdge *e2 = l->e; + + l->radial_next->radial_prev = l->radial_prev; + l->radial_prev->radial_next = l->radial_next; + + if (l == e2->l) { + e2->l = l->radial_next; + } + + if (l == e2->l) { + e2->l = NULL; + } + } while ((l = l->next) != f->l_first); + } + + /* swap verts */ + for (int i = 0; i < BLI_array_len(fs); i++) { + BMFace *f = fs[i]; + BMLoop *l = f->l_first, *lnext = NULL; + + do { + lnext = l->next; + + if (l->v == v_del) { + l->v = v_conn; + } + + BM_ELEM_API_FLAG_DISABLE(l->v, tag); + + for (int step = 0; step < 2; step++) { + BMVert *v_edge = step ? l->e->v2 : l->e->v1; + BMVert *v_other = BM_edge_other_vert(l->e, v_edge); + + if (v_edge != v_del) { + continue; + } + + if (v_other == v_conn) { + /* flag for later selection */ + if (!BM_ELEM_API_FLAG_TEST(l->e, tag)) { + BLI_array_append(deles, l->e); + } + + BM_ELEM_API_FLAG_ENABLE(l->e, tag); + } + else { + BMEdge *e3; + + if ((e3 = BM_edge_exists(v_conn, v_other))) { + if (combine_flags) { + /* TODO: stop flagging sharp edges by the abscene of the BM_ELEM_SMOOTH flag*/ + bool remove_smooth = !BM_elem_flag_test(l->e, BM_ELEM_SMOOTH); + remove_smooth = remove_smooth || !BM_elem_flag_test(e3, BM_ELEM_SMOOTH); + + e3->head.hflag |= l->e->head.hflag; + + if (remove_smooth) { + BM_elem_flag_disable(e3, BM_ELEM_SMOOTH); + } + } + + /* flag for later deletion */ + if (!BM_ELEM_API_FLAG_TEST(l->e, tag)) { + BLI_array_append(deles, l->e); + } + + BM_ELEM_API_FLAG_ENABLE(l->e, tag); + + l->e = e3; + } + else { + bmesh_disk_vert_replace(l->e, v_conn, v_del); + } + } + } + } while ((l = lnext) != f->l_first); + } + + for (int i = 0; i < BLI_array_len(deles); i++) { + BMEdge *e2 = deles[i]; + + if (e2->l != NULL) { + printf("%s: e2->l was not null!\n", __func__); + continue; + } + + e2->l = NULL; + +#ifdef _OTHER_TRACES + if (tracer) { + bool found = false; + for (int j = 0; j < BLI_array_len(es); j++) { + if (es[j] == deles[i]) { + found = true; + break; + } + } + + if (!found) { + tracer->on_edge_kill(bm, deles[i], tracer->userdata); + } + } +#endif + + if (deles[i]->l) { + printf("%s: edge is not cleared\n", __func__); + } + + BM_edge_kill(bm, deles[i]); + } + + for (int i = 0; i < BLI_array_len(fs); i++) { + BMFace *f = fs[i]; + BMLoop *l, *lnext; + + /* validate */ + l = f->l_first; + do { + lnext = l == l->next ? NULL : l->next; + + if (l->v == l->next->v) { + l->prev->next = l->next; + l->next->prev = l->prev; + + if (l == l->f->l_first) { + l->f->l_first = l->next; + } + + l->f->len--; + + if (l == l->f->l_first) { + l->f->l_first = NULL; + } + + bm_kill_only_loop(bm, l); + } + } while (lnext && (l = lnext) != f->l_first); + + if (f->len <= 2) { +#if 0 // def _OTHER_TRACES + if (tracer) { + tracer->on_face_kill(bm, f, tracer->userdata); + } +#endif + + /* kill face */ + while (f->l_first) { + BMLoop *l2 = f->l_first; + + l2->prev->next = l2->next; + l2->next->prev = l2->prev; + f->l_first = l2->next; + + bm_kill_only_loop(bm, l2); + + if (f->l_first == l2) { + f->l_first = NULL; + } + } + + bm_kill_only_face(bm, f); + fs[i] = NULL; + } + } + + bm->elem_index_dirty |= BM_VERT | BM_EDGE | BM_FACE; + bm->elem_table_dirty |= BM_VERT | BM_EDGE | BM_FACE; + + /* relink */ + for (int i = 0; i < BLI_array_len(fs); i++) { + BMFace *f = fs[i]; + + if (!f) { + continue; + } + + BM_ELEM_API_FLAG_ENABLE(f, final_tag); + + BMLoop *l = f->l_first; + do { + l->e = BM_edge_exists(l->v, l->next->v); + + if (!l->e) { + printf("warning: missing edge! %p %p\n", l->v, l->next->v); + l->e = BM_edge_create(bm, l->v, l->next->v, NULL, BM_CREATE_NOP); + } + + bmesh_radial_loop_append(l->e, l); + + BM_ELEM_API_FLAG_DISABLE(l->e, dup_tag); + BM_ELEM_API_FLAG_DISABLE(l->v, dup_tag); + BM_ELEM_API_FLAG_DISABLE(l->f, dup_tag); + } while ((l = l->next) != f->l_first); + } + + JVKE_CHECK_ELEMENT(v_conn); + +#ifdef JVKE_DEBUG + for (int step = 0; step < 2; step++) { + BMVert *v = step ? v_conn : v_del; + BMEdge *e1 = v->e; + + if (e1) { + do { + JVKE_CHECK_ELEMENT(e1); + + BMLoop *l = e1->l; + + if (!l) { + continue; + } + + /* boundary? */ + if (l == l->radial_next && !have_boundary) { + trigger_jvke_error(IS_LOOP_WRONG_RADIAL_LENGTH, saved_obj); + } + + if (!l) { + continue; + } + + do { + JVKE_CHECK_ELEMENT(l); + JVKE_CHECK_ELEMENT(l->v); + JVKE_CHECK_ELEMENT(l->e); + JVKE_CHECK_ELEMENT(l->f); + } while ((l = l->radial_next) != e1->l); + } while ((e1 = BM_DISK_EDGE_NEXT(e1, v)) != v->e); + } + } +#endif + + /* use euler criteria to check for duplicate faces */ + if (do_del && v_conn->e) { + int tote = 0, totv = 0, totf = 0; + + BMVert *v = v_conn; + BMEdge *e2 = v->e; + + if (!BM_ELEM_API_FLAG_TEST(v, dup_tag)) { + BM_ELEM_API_FLAG_ENABLE(v, dup_tag); + totv++; + } + + do { + BMVert *v2 = BM_edge_other_vert(e2, v); + + if (!BM_ELEM_API_FLAG_TEST(e2, dup_tag)) { + BM_ELEM_API_FLAG_ENABLE(e2, dup_tag); + tote++; + } + if (!BM_ELEM_API_FLAG_TEST(v2, dup_tag)) { + BM_ELEM_API_FLAG_ENABLE(v2, dup_tag); + totv++; + } + + if (e2->l) { + BMLoop *l_radial = e2->l; + do { + if (BM_ELEM_API_FLAG_TEST(l_radial->f, dup_tag)) { + continue; + } + + totf++; + + BM_ELEM_API_FLAG_ENABLE(l_radial->f, dup_tag); + BMLoop *l = l_radial; + + do { + if (!BM_ELEM_API_FLAG_TEST(l->v, dup_tag)) { + BM_ELEM_API_FLAG_ENABLE(l->v, dup_tag); + totv++; + } + + if (!BM_ELEM_API_FLAG_TEST(l->e, dup_tag)) { + BM_ELEM_API_FLAG_ENABLE(l->e, dup_tag); + tote++; + } + } while ((l = l->next) != l_radial); + } while ((l_radial = l_radial->radial_next) != e2->l); + } + } while ((e2 = BM_DISK_EDGE_NEXT(e2, v)) != v->e); + + int eul = totv - tote + totf; + if (eul != 1) { + // printf("%s: possible duplicate geometry! %d\n", __func__, eul); + e2 = v->e; + + do { + BMLoop *l = e2->l; + + if (!l) { + continue; + } + + BMLoop *l_next = l; + + do { + /* no guarantee each face has only one loop in radial + list */ + l_next = l->radial_next; + + while (l_next != l && l_next->f == l->f) { + l_next = l->radial_next; + } + + BMFace *f; + + if ((f = BM_face_find_double(l->f))) { + if (tracer) { + tracer->on_face_kill(bm, l->f, tracer->userdata); + } + BM_face_kill(bm, l->f); + } + } while (e2->l && (l = l_next) != e2->l); + } while ((e2 = BM_DISK_EDGE_NEXT(e2, v)) != v->e); + } + } + // printf("v_del: %p, v_conn: %p\n", v_del->e, v_conn->e); + if (do_del) { + JVKE_CHECK_ELEMENT(v_del); + if (tracer) { + tracer->on_vert_kill(bm, v_del, tracer->userdata); + } + + if (v_del->e && v_del->e->l) { + printf("%s: vert is not cleared\n", __func__); + } + + if (!(v_del->e && v_del->e->l)) { + BM_vert_kill(bm, v_del); + } + } + + if (tracer && v_conn->e) { + e = v_conn->e; + do { + tracer->on_edge_create(bm, e, tracer->userdata); + } while ((e = BM_DISK_EDGE_NEXT(e, v_conn)) != v_conn->e); + + do { + BMLoop *l = e->l; + if (!l) { + continue; + } + + do { + if (BM_ELEM_API_FLAG_TEST(l->f, final_tag)) { + BM_ELEM_API_FLAG_DISABLE(l->f, final_tag); + + tracer->on_face_create(bm, l->f, tracer->userdata); + } + } while ((l = l->radial_next) != e->l); + } while ((e = BM_DISK_EDGE_NEXT(e, v_conn)) != v_conn->e); + } + +#ifdef JVKE_DEBUG + if (v_conn && v_conn->e) { + BMEdge *e = v_conn->e; + do { + BMLoop *l = e->l; + + if (!l) { + continue; + } + + /* boundary? */ + if (l == l->radial_next && !have_boundary) { + trigger_jvke_error(IS_LOOP_WRONG_RADIAL_LENGTH, saved_obj); + } + } while ((e = BM_DISK_EDGE_NEXT(e, v_conn)) != v_conn->e); + } + + bm_local_obj_free(saved_obj, buf); +#endif + + BLI_array_free(deles); + BLI_array_free(fs); + + return v_conn; +} +#ifdef _OTHER_TRACES +# undef _OTHER_TRACES +#endif + +/*original version of bmesh_kernel_join_vert_kill_edge*/ +BMVert *bmesh_kernel_join_vert_kill_edge_fast(BMesh *bm, + BMEdge *e_kill, + BMVert *v_kill, + const bool do_del, + const bool check_edge_exists, + const bool kill_degenerate_faces, + const bool combine_flags) { BLI_SMALLSTACK_DECLARE(faces_degenerate, BMFace *); BMVert *v_target = BM_edge_other_vert(e_kill, v_kill); @@ -1829,6 +3104,7 @@ BMVert *bmesh_kernel_join_vert_kill_edge(BMesh *bm, BLI_SMALLSTACK_PUSH(faces_degenerate, l_kill->f); } } + l_kill_next = l_kill->radial_next; bm_kill_only_loop(bm, l_kill); @@ -1846,7 +3122,7 @@ BMVert *bmesh_kernel_join_vert_kill_edge(BMesh *bm, /* inline BM_vert_splice(bm, v_target, v_kill); */ BMEdge *e; while ((e = v_kill->e)) { - BMEdge *e_target; + BMEdge *e_target = NULL; if (check_edge_exists) { e_target = BM_edge_exists(v_target, BM_edge_other_vert(e, v_kill)); @@ -1857,7 +3133,7 @@ BMVert *bmesh_kernel_join_vert_kill_edge(BMesh *bm, if (check_edge_exists) { if (e_target) { - BM_edge_splice(bm, e_target, e); + BM_edge_splice(bm, e_target, e, combine_flags); } } } @@ -1875,6 +3151,8 @@ BMVert *bmesh_kernel_join_vert_kill_edge(BMesh *bm, bm_kill_only_vert(bm, v_kill); } + BM_CHECK_ELEMENT(v_target); + return v_target; } @@ -1975,7 +3253,10 @@ BMFace *bmesh_kernel_join_face_kill_edge(BMesh *bm, BMFace *f1, BMFace *f2, BMEd /* deallocate edge and its two loops as well as f2 */ if (bm->etoolflagpool) { - BLI_mempool_free(bm->etoolflagpool, ((BMEdge_OFlag *)l_f1->e)->oflags); + MToolFlags *flags = (MToolFlags *)BM_ELEM_CD_GET_VOID_P( + l_f1->e, bm->edata.layers[bm->edata.typemap[CD_TOOLFLAGS]].offset); + + BLI_mempool_free(bm->etoolflagpool, flags->flag); } BLI_mempool_free(bm->epool, l_f1->e); bm->totedge--; @@ -1983,9 +3264,14 @@ BMFace *bmesh_kernel_join_face_kill_edge(BMesh *bm, BMFace *f1, BMFace *f2, BMEd bm->totloop--; BLI_mempool_free(bm->lpool, l_f2); bm->totloop--; + if (bm->ftoolflagpool) { - BLI_mempool_free(bm->ftoolflagpool, ((BMFace_OFlag *)f2)->oflags); + MToolFlags *flags = (MToolFlags *)BM_ELEM_CD_GET_VOID_P( + f2, bm->pdata.layers[bm->pdata.typemap[CD_TOOLFLAGS]].offset); + + BLI_mempool_free(bm->ftoolflagpool, flags->flag); } + BLI_mempool_free(bm->fpool, f2); bm->totface--; /* account for both above */ @@ -2218,7 +3504,8 @@ static void bmesh_kernel_vert_separate__cleanup(BMesh *bm, LinkNode *edges_separ do { BMEdge *e = n_step->link; BLI_assert(e != e_orig); - if ((e->v1 == e_orig->v1) && (e->v2 == e_orig->v2) && BM_edge_splice(bm, e_orig, e)) { + if ((e->v1 == e_orig->v1) && (e->v2 == e_orig->v2) && + BM_edge_splice(bm, e_orig, e, false)) { /* don't visit again */ n_prev->next = n_step->next; } @@ -2329,7 +3616,17 @@ void BM_vert_separate_tested_edges(BMesh *UNUSED(bm), /** \} */ -bool BM_edge_splice(BMesh *bm, BMEdge *e_dst, BMEdge *e_src) +/** + * \brief Splice Edge + * + * Splice two unique edges which share the same two vertices into one edge. + * (\a e_src into \a e_dst, removing e_src). + * + * \return Success + * + * \note Edges must already have the same vertices. + */ +bool BM_edge_splice(BMesh *bm, BMEdge *e_dst, BMEdge *e_src, bool combine_flags) { BMLoop *l; @@ -2356,6 +3653,18 @@ bool BM_edge_splice(BMesh *bm, BMEdge *e_dst, BMEdge *e_src) BM_CHECK_ELEMENT(e_src); BM_CHECK_ELEMENT(e_dst); + if (combine_flags) { + /* sharp flag is inverted to BM_ELEM_SMOOTH, which we + must take into account*/ + + if (!(e_dst->head.hflag & BM_ELEM_SMOOTH) || !(e_src->head.hflag & BM_ELEM_SMOOTH)) { + e_dst->head.hflag = (e_dst->head.hflag | e_src->head.hflag) & ~BM_ELEM_SMOOTH; + } + else { + e_dst->head.hflag |= e_src->head.hflag; + } + } + /* removes from disks too */ BM_edge_kill(bm, e_src); @@ -2365,7 +3674,7 @@ bool BM_edge_splice(BMesh *bm, BMEdge *e_dst, BMEdge *e_src) void bmesh_kernel_edge_separate(BMesh *bm, BMEdge *e, BMLoop *l_sep, const bool copy_select) { BMEdge *e_new; -#ifndef NDEBUG +#ifdef BMESH_DEBUG const int radlen = bmesh_radial_length(e->l); #endif @@ -2442,7 +3751,7 @@ BMVert *bmesh_kernel_unglue_region_make_vert(BMesh *bm, BMLoop *l_sep) edges[0] = l_sep->e; edges[1] = l_sep->prev->e; - for (i = 0; i < ARRAY_SIZE(edges); i++) { + for (i = 0; i < (int)ARRAY_SIZE(edges); i++) { BMEdge *e = edges[i]; bmesh_edge_vert_swap(e, v_new, v_sep); } @@ -2488,7 +3797,7 @@ BMVert *bmesh_kernel_unglue_region_make_vert_multi(BMesh *bm, BMLoop **larr, int BM_ELEM_API_FLAG_ENABLE(l_sep->prev, LOOP_VISIT); BMLoop *loop_pair[2] = {l_sep, l_sep->prev}; - for (int j = 0; j < ARRAY_SIZE(loop_pair); j++) { + for (int j = 0; j < (int)ARRAY_SIZE(loop_pair); j++) { BMEdge *e = loop_pair[j]->e; if (!BM_ELEM_API_FLAG_TEST(e, EDGE_VISIT)) { BM_ELEM_API_FLAG_ENABLE(e, EDGE_VISIT); @@ -2545,7 +3854,7 @@ BMVert *bmesh_kernel_unglue_region_make_vert_multi(BMesh *bm, BMLoop **larr, int else { v_new = BM_vert_create(bm, v_sep->co, v_sep, BM_CREATE_NOP); - for (i = 0; i < STACK_SIZE(edges); i++) { + for (i = 0; i < (int)STACK_SIZE(edges); i++) { BMEdge *e = edges[i]; BMLoop *l_iter, *l_first, *l_next; BMEdge *e_new; diff --git a/source/blender/bmesh/intern/bmesh_core.h b/source/blender/bmesh/intern/bmesh_core.h index 4df0463f134..27ffd2bcd94 100644 --- a/source/blender/bmesh/intern/bmesh_core.h +++ b/source/blender/bmesh/intern/bmesh_core.h @@ -10,6 +10,8 @@ extern "C" { #endif +struct BMTracer; + BMFace *BM_face_copy(BMesh *bm_dst, BMesh *bm_src, BMFace *f, bool copy_verts, bool copy_edges); typedef enum eBMCreateFlag { @@ -21,9 +23,14 @@ typedef enum eBMCreateFlag { * use if we immediately write custom-data into the element so this skips copying from 'example' * arguments or setting defaults, speeds up conversion when data is converted all at once. */ - BM_CREATE_SKIP_CD = (1 << 2), + BM_CREATE_SKIP_CD = (1 << 2), /* if true, you must call bm_elem_check_toolflags(bm, elem) later + if toolflags are on */ + BM_CREATE_SKIP_ID = (1 << 3) } eBMCreateFlag; +/* if toolflags are enabled, checks that internal pointer to toolflags it not null */ +void bm_elem_check_toolflags(BMesh *bm, BMElem *elem); + /** * \brief Main function for creating a new vertex. */ @@ -104,7 +111,8 @@ void BM_vert_kill(BMesh *bm, BMVert *v); * * \note Edges must already have the same vertices. */ -bool BM_edge_splice(BMesh *bm, BMEdge *e_dst, BMEdge *e_src); +bool BM_edge_splice(BMesh *bm, BMEdge *e_dst, BMEdge *e_src, bool combine_flags); + /** * \brief Splice Vert * @@ -336,9 +344,18 @@ BMEdge *bmesh_kernel_join_edge_kill_vert(BMesh *bm, BMVert *bmesh_kernel_join_vert_kill_edge(BMesh *bm, BMEdge *e_kill, BMVert *v_kill, - bool do_del, - bool check_edge_exists, - bool kill_degenerate_faces); + const bool do_del, + const bool combine_flags, + const struct BMTracer *tracer); + +BMVert *bmesh_kernel_join_vert_kill_edge_fast(BMesh *bm, + BMEdge *e_kill, + BMVert *v_kill, + const bool do_del, + const bool check_edge_exists, + const bool kill_degenerate_faces, + const bool combine_flags); + /** * \brief Join Face Kill Edge (JFKE) * @@ -396,6 +413,8 @@ BMVert *bmesh_kernel_unglue_region_make_vert_multi(BMesh *bm, BMLoop **larr, int */ BMVert *bmesh_kernel_unglue_region_make_vert_multi_isolated(BMesh *bm, BMLoop *l_sep); +void BM_reassign_ids(BMesh *bm); +void BM_clear_ids(BMesh *bm); #ifdef __cplusplus } #endif diff --git a/source/blender/bmesh/intern/bmesh_data_attr.cc b/source/blender/bmesh/intern/bmesh_data_attr.cc new file mode 100644 index 00000000000..ecfdf8dc145 --- /dev/null +++ b/source/blender/bmesh/intern/bmesh_data_attr.cc @@ -0,0 +1,387 @@ +#include "DNA_customdata_types.h" + +#include "BLI_index_range.hh" +#include "BLI_mempool.h" + +#include "BKE_customdata.h" + +#include "bmesh.h" +#include "bmesh_data_attr.h" + +#ifdef USE_BMESH_PAGE_CUSTOMDATA + +using blender::IndexRange; +using blender::bmesh::BMAttrDomain; +using blender::bmesh::PageArray; +using blender::bmesh::PageElemRef; + +namespace blender { +namespace bmesh { + +BMeshAttrList *BMAttr_new() +{ + BMeshAttrList *list = static_cast( + MEM_callocN(sizeof(BMeshAttrList), "BMeshAttrList")); + + for (int i : IndexRange(ATTR_DOMAIN_NUM)) { + list->domains[i] = new BMAttrDomain(static_cast(i)); + } + + return list; +} + +void BMAttr_reset(BMeshAttrList *list) +{ + if (list->arrays) { + MEM_freeN(static_cast(list->arrays)); + } + + list->arrays = nullptr; + list->totarray = 0; + + for (int i : IndexRange(ATTR_DOMAIN_NUM)) { + delete list->domains[i]; + } + + for (int i : IndexRange(ATTR_DOMAIN_NUM)) { + list->domains[i] = new BMAttrDomain(static_cast(i)); + } +} + +void BMAttr_free(BMeshAttrList *list) +{ + for (int i : IndexRange(ATTR_DOMAIN_NUM)) { + delete list->domains[i]; + } + + if (list->arrays) { + MEM_freeN(static_cast(list->arrays)); + } + + MEM_freeN(static_cast(list)); +} + +static void bm_update_page_pointers(BMeshAttrList *list) +{ + for (int i : IndexRange(list->totarray)) { + PageArray *page_array = reinterpret_cast *>( + list->arrays[i]); + list->arrays[i]->pages = page_array->pages.data(); + } +} + +int BMAttr_allocElem(BMeshAttrList *list, eAttrDomain domain) +{ + bool hadNewPage; + + int ret = static_cast(list->domains[domain]->alloc(hadNewPage)); + + if (hadNewPage) { + bm_update_page_pointers(list); + } + + return ret; +} + +void BMAttr_freeElem(BMeshAttrList *list, eAttrDomain domain, int elem) +{ + bool pageRemoved; + + list->domains[domain]->free(static_cast(elem), pageRemoved); + + if (pageRemoved) { + bm_update_page_pointers(list); + } +} + +int BMAttr_addLayer(BMeshAttrList *list, eAttrDomain domain, eCustomDataType type) +{ + list->totarray++; + + if (!list->arrays) { + list->arrays = static_cast( + MEM_malloc_arrayN(list->totarray, sizeof(void *), __func__)); + } + else { + list->arrays = static_cast( + MEM_reallocN(static_cast(list->arrays), list->totarray * sizeof(void *))); + } + + BMeshPageArray *bmarray = list->arrays[list->totarray - 1]; + PageArray *page_array = list->domains[domain]->addLayer(type); + + bmarray->cppClass = reinterpret_cast(page_array); + bmarray->pages = page_array->pages.data(); + + for (PageElemRef i : IndexRange(list->domains[domain]->totalloc)) { + page_array->setDefault(i); + } + + return list->totarray - 1; +} + +void BMAttr_init(BMesh *bm) +{ +# ifdef USE_BMESH_PAGE_CUSTOMDATA + CustomData *domains[ATTR_DOMAIN_NUM] = {nullptr}; + + domains[ATTR_DOMAIN_POINT] = &bm->vdata; + domains[ATTR_DOMAIN_EDGE] = &bm->edata; + domains[ATTR_DOMAIN_CORNER] = &bm->ldata; + domains[ATTR_DOMAIN_FACE] = &bm->pdata; + + if (!bm->attr_list) { + bm->attr_list = BMAttr_new(); + } + + BMAttr_fromCData(bm->attr_list, domains); +# endif +} + +void BMAttr_fromCData(BMeshAttrList *list, CustomData *domains[ATTR_DOMAIN_NUM]) +{ + eAttrDomain ds[4] = {ATTR_DOMAIN_POINT, ATTR_DOMAIN_EDGE, ATTR_DOMAIN_CORNER, ATTR_DOMAIN_FACE}; + + for (int i : IndexRange(4)) { + CustomData *cdata = domains[ds[i]]; + + cdata->bm_attrs = static_cast(list); + cdata->_pad[0] = static_cast(domains[ds[i]]); + + for (int j : IndexRange(cdata->totlayer)) { + CustomDataLayer *layer = cdata->layers + j; + + layer->offset = BMAttr_addLayer(list, ds[i], static_cast(layer->type)); + } + } +} + +static eAttrDomain domain_map[] = { + ATTR_DOMAIN_AUTO, // 0 + ATTR_DOMAIN_POINT, // 1 + ATTR_DOMAIN_EDGE, // 2 + ATTR_DOMAIN_AUTO, // 3 + ATTR_DOMAIN_CORNER, // 4 + ATTR_DOMAIN_AUTO, // 5 + ATTR_DOMAIN_AUTO, // 6 + ATTR_DOMAIN_AUTO, // 7 + ATTR_DOMAIN_FACE, // 8 +}; + +void bmesh_update_attr_refs(BMesh *bm) +{ + CustomData *cdata = &bm->vdata; + + for (int i = 0; i < 4; i++, cdata++) { + cdata->_pad[0] = static_cast(domain_map[htype]); + cdata->bm_attrs = bm->attr_list; + } +} + +void CustomData_bmesh_init_pool(CustomData *data, int totelem, const char htype) +{ + CustomData_bmesh_init_pool_ex(data, totelem, htype, __func__); +} + +void CustomData_bmesh_init_pool_ex(CustomData *data, + int totelem, + const char htype, + const char *memtag) +{ + // store domain in _pad[0] for the purposes of this test + data->_pad[0] = static_cast(domain_map[htype]); + + if (data->pool) { + BLI_mempool_destroy(data->pool); + } + + data->pool = BLI_mempool_create(sizeof(BMeshPageRef), 0, 1024, 0); +} + +static void CustomData_bmesh_alloc_block(CustomData *data, void **block) +{ + BMeshAttrList *list = static_cast(data->bm_attrs); + BMeshPageRef *ref = static_cast(BLI_mempool_calloc(data->pool)); + + eAttrDomain domain = static_cast(POINTER_AS_UINT(data->_pad[0])); + + ref->attrs = list; + ref->idx = BMAttr_allocElem(list, domain); + ref->domain = domain; + + *block = static_cast(ref); +} + +static void CustomData_bmesh_set_default_n(CustomData *data, void **block, int n) +{ + if (ELEM(data->layers[n].type, CD_TOOLFLAGS, CD_MESH_ID)) { + /* do not do toolflags or mesh ids */ + return; + } + + BMeshAttrList *list = static_cast(data->bm_attrs); + BMeshPageRef *ref = static_cast(*block); + + list->domains[ref->domain]->arrays[n]->setDefault(static_cast(ref->idx)); +} + +static void CustomData_bmesh_set_default(CustomData *data, void **block) +{ + if (!*block) { + CustomData_bmesh_alloc_block(data, block, n); + } + + BMeshAttrList *list = static_cast(data->bm_attrs); + BMeshPageRef *ref = static_cast(*block); + eAttrDomain domain = static_cast(POINTER_AS_UINT(data->_pad[0])); + + list->domains[domain].setDefault(static_cast(ref->idx)); +} + +static void alloc_block(CustomData *data, BMeshAttrList *list, eAttrDomain domain, void **block) +{ + CustomData_bmesh_alloc_block(data, block); + BMeshPageRef *ref = static_cast * block; + + ref->attrs = list; + ref->idx = BMAttr_allocElem(list, domain); +} + +void CustomData_bmesh_interp(CustomData *data, + const void **src_blocks, + const float *weights, + const float *sub_weights, + int count, + void *dst_block) +{ + BMeshPageRef *ref = static_cast dst_block; + eAttrDomain domain = static_cast(ref->domain); + + PageElemRef *elems = BLI_array_alloca(elems, count); + + for (int i : IndexRange(count)) { + BMeshPageRef *ref2 = static_cast src_blocks[i]; + elems[i] = ref2->idx; + } + + ref->attrs->domains[domain]->interp( + static_cast(ref->idx), count, elems, weights, sub_weights); +} + +void CustomData_to_bmesh_block(const CustomData *source, + CustomData *_dest, + int src_index, + void **dest_block, + bool use_default_init) +{ + if (*dest_block == nullptr) { + CustomData_bmesh_alloc_block(dest, dest_block); + } + + BMeshPageRef *block = static_cast dest_block; + BMeshAttrList *list = static_cast dest->bm_attrs; + auto *dest = list->domains[ref->domain]; + + /* copies a layer at a time */ + int dest_i = 0; + for (int src_i = 0; src_i < source->totlayer; src_i++) { + auto *array = list->domains[ref->domain]->arrays[dest_i]; + + /* find the first dest layer with type >= the source type + * (this should work because layers are ordered by type) + */ + while (dest_i < dest->arrays.size() && + dest->arrays[dest_i].type < source->layers[src_i].type) { + if (use_default_init) { + CustomData_bmesh_set_default_n(dest, dest_block, dest_i); + } + dest_i++; + } + + /* if there are no more dest layers, we're done */ + if (dest_i >= dest->arrays.size()) { + break; + } + + /* if we found a matching layer, copy the data */ + if (dest->arrays[dest_i].type == source->layers[src_i].type) { + const void *src_data = source->layers[src_i].data; + void *dest_data = dest->getElemPtr(static_cast(ref->idx)); + + const LayerTypeInfo *typeInfo = layerType_getInfo(dest->arrays[dest_i].type); + const size_t src_offset = (size_t)src_index * typeInfo->size; + + if (typeInfo->copy) { + typeInfo->copy(POINTER_OFFSET(src_data, src_offset), dest_data, 1); + } + else { + memcpy(dest_data, POINTER_OFFSET(src_data, src_offset), typeInfo->size); + } + + /* 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++; + } + } + + if (use_default_init) { + while (dest_i < dest->arrays.size()) { + CustomData_bmesh_set_default_n(dest, dest_block, dest_i); + dest_i++; + } + } +} + +void CustomData_from_bmesh_block(const CustomData *source, + CustomData *dest, + void *src_block, + int dest_index) +{ + /* copies a layer at a time */ + int dest_i = 0; + for (int src_i = 0; src_i < source->totlayer; src_i++) { + if (source->layers[src_i].flag & CD_FLAG_NOCOPY) { + continue; + } + + /* 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 < source->layers[src_i].type) { + dest_i++; + } + + /* if there are no more dest layers, we're done */ + if (dest_i >= dest->totlayer) { + return; + } + + /* if we found a matching layer, copy the data */ + if (dest->layers[dest_i].type == source->layers[src_i].type) { + const LayerTypeInfo *typeInfo = layerType_getInfo(dest->layers[dest_i].type); + int offset = source->layers[src_i].offset; + const void *src_data = POINTER_OFFSET(src_block, offset); + void *dst_data = POINTER_OFFSET(dest->layers[dest_i].data, + (size_t)dest_index * typeInfo->size); + + if (typeInfo->copy) { + typeInfo->copy(src_data, dst_data, 1); + } + else { + memcpy(dst_data, src_data, typeInfo->size); + } + + /* 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++; + } + } +} + +} // namespace bmesh +} // namespace blender +#endif diff --git a/source/blender/bmesh/intern/bmesh_data_attr.h b/source/blender/bmesh/intern/bmesh_data_attr.h new file mode 100644 index 00000000000..f10c1939f94 --- /dev/null +++ b/source/blender/bmesh/intern/bmesh_data_attr.h @@ -0,0 +1,275 @@ +#pragma once + +/* + +This is a minimal page-based CustomData backend for bmesh. +It's purpose is to test whether a page-based system would +be faster then the current block-based one. + +The idea is to plug into the existing API in as minimal +a way as possible. + +*/ + +#include "BKE_attribute.h" +#include "bmesh.h" + +#ifdef USE_BMESH_PAGE_CUSTOMDATA + +# define BM_PAGE_SHIFT 10 +# define BM_PAGE_SIZE (1 << BM_PAGE_SHIFT) +# define BM_PAGE_MASK (BM_PAGE_SIZE - 1) + +# ifdef __cplusplus + +# include "BLI_alloca.h" +# include "BLI_array.hh" +# include + +namespace blender { +namespace bmesh { + +using PageElemRef = int; + +template struct PageArray { + std::vector pages; + size_t elemSize; + eCustomDataType type; + + PageArray(const PageArray &b) + { + elemSize = b.elemSize; + pages = b.pages; + } + + PageArray(const PageArray &&b) + { + elemSize = b.elemSize; + pages = std::move(b.pages); + } + + PageArray(eCustomDataType t, size_t size = 0) : type(t) + { + elemSize = CustomData_getTypeSize(type); + + reserve(size); + } + + ~PageArray() + { + } + + void *getElemPtr(PageElemRef elem) + { + size_t page = elem >> PageSizeShift; + size_t elem_i = elem & ((1 << PageSizeShift) - 1); + + char *ptr = static_cast(pages[page]); + ptr += elem_i * elemSize; + + return static_cast(ptr); + } + + void interp(PageElemRef elem, int count, PageElemRef *srcs, float *ws, float *sub_ws) + { + void **blocks = static_cast(BLI_array_alloca(blocks, count)); + + for (int i : IndexRange(count)) { + blocks[i] = getElemPtr(srcs[i]); + } + + CustomData_interpData(type, getElemPtr(elem), count, (const void **)blocks, ws, sub_ws); + } + + void free(PageElemRef elem) + { + CustomData_freeData(type, getElemPtr(elem), 1); + } + + void setDefault(PageElemRef elem) + { + CustomData_setDefaultData(type, getElemPtr(elem), 1); + } + + void reserve(size_t size) + { + size_t totpage = size >> PageSizeShift; + // const size_t pagesize = 1ULL << PageSizeShift; + + int curpage = pages.size(); + pages.resize(totpage); + + for (int i = curpage; i < totpage; i++) { + newPage(); + } + } + + void newPage() + { + const int pagesize = 1 << PageSizeShift; + + pages.resize(pages.size() + 1); + pages[pages.size() - 1] = MEM_malloc_arrayN(pagesize, elemSize, "bmesh attribute page"); + } +}; + +struct BMAttrDomain { + std::vector freelist; + std::vector *> arrays; + + eAttrDomain domain; + int totpage; + int totelem; + int totalloc; + + BMAttrDomain(eAttrDomain d) : domain(d), totpage(0), totelem(0) + { + } + + ~BMAttrDomain() + { + for (PageArray *array : arrays) { + delete array; + } + } + + void interp(PageElemRef elem, int count, PageElemRef *srcs, float *ws, float *sub_ws) + { + for (PageArray *array : arrays) { + array->interp(elem, count, srcs, ws, sub_ws); + } + } + + PageArray *addLayer(eCustomDataType type) + { + PageArray *array = new PageArray(type); + array->reserve(totelem); + + /* keep layers ordered by type */ + + bool state = false; + for (auto iter = arrays.begin(); iter != arrays.end(); ++iter) { + if ((*iter)->type == type) { + state = true; + // now find next layer with wrong type, we + // will insert before it. + } + else if (state) { + arrays.insert(iter, array); + return array; + } + } + + arrays.push_back(array); + return array; + } + + PageElemRef alloc(bool &haveNewPage) + { + if (freelist.size() == 0) { + haveNewPage = true; + newPage(); + } + else { + haveNewPage = false; + } + + totelem++; + PageElemRef r = freelist[freelist.size() - 1]; + freelist.pop_back(); + + for (auto *array : arrays) { + array->setDefault(r); + } + + return r; + } + + void setDefault(PageElemRef ref) + { + for (auto *array : arrays) { + array->setDefault(ref); + } + } + + void free(PageElemRef ref, bool &removedPage) + { + removedPage = false; + + totelem--; + freelist.push_back(ref); + } + + private: + void newPage() + { + totpage++; + totalloc += BM_PAGE_SIZE; + + for (PageArray<> *array : arrays) { + array->newPage(); + } + + size_t count = 1 << BM_PAGE_SHIFT; + for (size_t i = 0; i < count; i++) { + freelist.push_back(i); + } + } +}; + +extern "C" { +# else +struct BMAttrDomain; +# endif //_cplusplus + +# include "BLI_compiler_compat.h" +# include "bmesh_class.h" + +struct BMesh; + +typedef struct BMeshPageArray { + int esize, psize; + void **pages; + void *cppClass; +} BmeshPageArray; + +typedef struct BMeshAttrList { + BMeshPageArray **arrays; + int totarray; + struct BMAttrDomain *domains[ATTR_DOMAIN_NUM]; +} BMeshAttrList; + +typedef struct BMeshPageRef { + // point to arrays, attribute for all domains go into one + // list of arrays + BMeshAttrList *attrs; + int idx; // index into attribute list, NOT element index + int domain; +} BMeshPageRef; + +BLI_INLINE void *BM_ELEM_CD_GET_VOID_P_2(BMElem *elem, int offset) +{ + BMeshPageRef *ref = (BMeshPageRef *)elem->head.data; + BmeshPageArray *array = ref->attrs->arrays[offset]; + size_t page = ref->idx >> BM_PAGE_SHIFT; + size_t off = ref->idx & BM_PAGE_MASK; + + char *ptr = (char *)array->pages[page]; + ptr += off * array->esize; + + return (void *)ptr; +} + +BMeshAttrList *BMAttr_new(); +void BMAttr_reset(BMeshAttrList *list); +void BMAttr_free(BMeshAttrList *list); +void BMAttr_fromCData(BMeshAttrList *list, CustomData *domains[ATTR_DOMAIN_NUM]); +void BMAttr_init(struct BMesh *bm); + +# ifdef __cplusplus +} + +} // namespace bmesh +} // namespace blender +# endif +#endif diff --git a/source/blender/bmesh/intern/bmesh_idmap.cc b/source/blender/bmesh/intern/bmesh_idmap.cc new file mode 100644 index 00000000000..d9dea1155f0 --- /dev/null +++ b/source/blender/bmesh/intern/bmesh_idmap.cc @@ -0,0 +1,342 @@ +#include "MEM_guardedalloc.h" + +#include "BLI_assert.h" +#include "BLI_compiler_attrs.h" +#include "BLI_compiler_compat.h" +#include "BLI_index_range.hh" +#include "BLI_map.hh" +#include "BLI_set.hh" +#include "BLI_vector.hh" + +#include "BKE_customdata.h" + +#include "DNA_customdata_types.h" +#include "DNA_meshdata_types.h" + +#include "bmesh_idmap.h" +#include + +using namespace blender; + +#define FREELIST_HASHMAP_THRESHOLD_HIGH 1024 +#define FREELIST_HASHMAP_THRESHOLD_LOW 700 + +BMIdMap *BM_idmap_new(BMesh *bm, int elem_mask) +{ + BMIdMap *idmap = MEM_new("BMIdMap"); + + for (int i = 0; i < ARRAY_SIZE(idmap->cd_id_off); i++) { + idmap->cd_id_off[i] = -1; + } + + idmap->flag = elem_mask; + idmap->bm = bm; + + BM_idmap_check_attributes(idmap); + + return idmap; +} + +static void idmap_grow_map(BMIdMap *idmap, int newid) +{ + if (idmap->map_size > newid) { + return; + } + + int newsize = (newid + 1); + newsize += newsize >> 1; + + if (idmap->map) { + idmap->map = (BMElem **)MEM_recallocN((void *)idmap->map, sizeof(void *) * newsize); + } + else { + idmap->map = (BMElem **)MEM_calloc_arrayN(newsize, sizeof(void *), "bm idmap"); + } + + idmap->map_size = newsize; +} + +void BM_idmap_check_ids(BMIdMap *idmap) +{ + BMIter iter; + BMVert *v; + BMEdge *e; + BMFace *f; + + BM_idmap_check_attributes(idmap); + + idmap->freelist.clear(); + if (idmap->free_idx_map) { + MEM_delete(idmap->free_idx_map); + idmap->free_idx_map = nullptr; + } + + int max_id = 0; + + if (idmap->flag & BM_VERT) { + BM_ITER_MESH (v, &iter, idmap->bm, BM_VERTS_OF_MESH) { + int id = BM_ELEM_CD_GET_INT(v, idmap->cd_id_off[BM_VERT]); + + max_id = max_ii(max_id, id); + } + } + if (idmap->flag & BM_EDGE) { + BM_ITER_MESH (e, &iter, idmap->bm, BM_EDGES_OF_MESH) { + int id = BM_ELEM_CD_GET_INT(e, idmap->cd_id_off[BM_EDGE]); + + max_id = max_ii(max_id, id); + } + } + if (idmap->flag & (BM_FACE | BM_LOOP)) { + BM_ITER_MESH (f, &iter, idmap->bm, BM_FACES_OF_MESH) { + if (idmap->flag & BM_FACE) { + int id = BM_ELEM_CD_GET_INT(f, idmap->cd_id_off[BM_FACE]); + max_id = max_ii(max_id, id); + } + + if (idmap->flag & BM_LOOP) { + BMLoop *l = f->l_first; + do { + int id = BM_ELEM_CD_GET_INT(l, idmap->cd_id_off[BM_LOOP]); + max_id = max_ii(max_id, id); + } while ((l = l->next) != f->l_first); + } + } + } + + max_id++; + + if (idmap->map_size >= max_id) { + memset((void *)idmap->map, 0, sizeof(void *) * idmap->map_size); + } + else { + MEM_SAFE_FREE(idmap->map); + idmap->map_size = max_id + 1; + idmap->map = (BMElem **)MEM_calloc_arrayN(max_id + 1, sizeof(BMElem *), "bm idmap->map"); + } + + auto check_elem = [&](auto *elem) { + int id = BM_ELEM_CD_GET_INT(elem, idmap->cd_id_off[(int)elem->head.htype]); + + if (id < 0 || id >= idmap->map_size || idmap->map[id]) { + id = max_id++; + BM_ELEM_CD_SET_INT(elem, idmap->cd_id_off[(int)elem->head.htype], id); + } + + idmap_grow_map(idmap, id); + idmap->map[id] = reinterpret_cast(elem); + }; + + if (idmap->flag & BM_VERT) { + BM_ITER_MESH (v, &iter, idmap->bm, BM_VERTS_OF_MESH) { + check_elem(v); + } + } + if (idmap->flag & BM_EDGE) { + BM_ITER_MESH (e, &iter, idmap->bm, BM_EDGES_OF_MESH) { + check_elem(e); + } + } + if (idmap->flag & (BM_FACE | BM_LOOP)) { + BM_ITER_MESH (f, &iter, idmap->bm, BM_FACES_OF_MESH) { + check_elem(f); + if (idmap->flag & BM_LOOP) { + BMLoop *l = f->l_first; + + do { + check_elem(l); + } while ((l = l->next) != f->l_first); + } + } + } + if (idmap->flag & BM_VERT) { + BM_ITER_MESH (v, &iter, idmap->bm, BM_VERTS_OF_MESH) { + check_elem(v); + } + } + + idmap->maxid = max_id; +} + +void BM_idmap_check_attributes(BMIdMap *idmap) +{ + auto check_attr = [&](int type) { + if (!(idmap->flag & type)) { + return; + } + + CustomData *cdata; + const char *name; + + switch (type) { + case BM_VERT: + name = "vertex_id"; + cdata = &idmap->bm->vdata; + break; + case BM_EDGE: + name = "edge_id"; + cdata = &idmap->bm->edata; + break; + case BM_LOOP: + name = "loop_id"; + cdata = &idmap->bm->ldata; + break; + case BM_FACE: + name = "face_id"; + cdata = &idmap->bm->pdata; + break; + default: + BLI_assert_unreachable(); + return; + } + + int idx = CustomData_get_named_layer_index(cdata, CD_PROP_INT32, name); + + if (idx == -1) { + BM_data_layer_add_named(idmap->bm, cdata, CD_PROP_INT32, name); + idx = CustomData_get_named_layer_index(cdata, CD_PROP_INT32, name); + } + + if (!cdata->layers[idx].default_data) { + cdata->layers[idx].default_data = MEM_cnew("MIntProperty"); + } + + cdata->layers[idx].flag |= CD_FLAG_ELEM_NOINTERP | CD_FLAG_ELEM_NOCOPY; + + int *default_data = static_cast(cdata->layers[idx].default_data); + *default_data = -1; + + idmap->cd_id_off[type] = cdata->layers[idx].offset; + }; + + check_attr(BM_VERT); + check_attr(BM_EDGE); + check_attr(BM_LOOP); + check_attr(BM_FACE); +} + +void BM_idmap_destroy(BMIdMap *idmap) +{ + MEM_SAFE_FREE(idmap->map); + MEM_delete(idmap); +} + +static void check_idx_map(BMIdMap *idmap) +{ + if (idmap->free_idx_map && idmap->freelist.size() < FREELIST_HASHMAP_THRESHOLD_LOW) { + // printf("%s: Deleting free_idx_map\n", __func__); + + MEM_delete(idmap->free_idx_map); + idmap->free_idx_map = nullptr; + } + else if (!idmap->free_idx_map && idmap->freelist.size() < FREELIST_HASHMAP_THRESHOLD_HIGH) { + // printf("%s: Adding free_idx_map\n", __func__); + + idmap->free_idx_map = MEM_new("BMIdMap::FreeIdxMap"); + + for (int i : IndexRange(idmap->freelist.size())) { + idmap->free_idx_map->add(idmap->freelist[i], i); + } + } +} + +int BM_idmap_alloc(BMIdMap *idmap, BMElem *elem) +{ + int id = -1; +#ifdef USE_NEW_IDMAP + + while (idmap->freelist.size()) { + id = idmap->freelist.pop_last(); + + if (id == -1) { + continue; + } + + if (idmap->free_idx_map) { + idmap->free_idx_map->remove(id); + } + + break; + } + + if (id == -1) { + id = idmap->maxid++; + } + + idmap_grow_map(idmap, id); + idmap->map[id] = elem; + + BM_ELEM_CD_SET_INT(elem, idmap->cd_id_off[elem->head.htype], id); +#endif + return id; +} + +void BM_idmap_assign(BMIdMap *idmap, BMElem *elem, int id) +{ +#ifdef USE_NEW_IDMAP + /* Remove id from freelist. */ + if (idmap->free_idx_map) { + const int *val; + + if ((val = idmap->free_idx_map->lookup_ptr(id))) { + idmap->freelist[*val] = -1; + idmap->free_idx_map->remove(id); + } + } + else { + for (int i : IndexRange(idmap->freelist.size())) { + if (idmap->freelist[i] == id) { + idmap->freelist[i] = -1; + } + } + } + + BM_ELEM_CD_SET_INT(elem, idmap->cd_id_off[elem->head.htype], id); + + idmap_grow_map(idmap, id); + idmap->map[id] = elem; + + check_idx_map(idmap); +#endif +} + +void BM_idmap_release(BMIdMap *idmap, BMElem *elem, bool clear_id) +{ +#ifdef USE_NEW_IDMAP + int id = BM_ELEM_CD_GET_INT(elem, idmap->cd_id_off[(int)elem->head.htype]); + + if (id == -1) { + printf("%s: unassigned id!\n", __func__); + return; + }; + if (id < 0 || id >= idmap->map_size || (idmap->map[id] && idmap->map[id] != elem)) { + printf("%s: id corruptions\n", __func__); + } + else { + idmap->map[id] = nullptr; + } + + idmap->freelist.append(id); + + if (idmap->free_idx_map) { + idmap->free_idx_map->add(id, idmap->freelist.size() - 1); + } + + check_idx_map(idmap); + + if (clear_id) { + BM_ELEM_CD_SET_INT(elem, idmap->cd_id_off[elem->head.htype], -1); + } +#endif +} + +int BM_idmap_check_assign(BMIdMap *idmap, BMElem *elem) +{ + int id = BM_ELEM_CD_GET_INT(elem, idmap->cd_id_off[(int)elem->head.htype]); + + if (id == -1) { + return BM_idmap_alloc(idmap, elem); + } + + return id; +} diff --git a/source/blender/bmesh/intern/bmesh_idmap.h b/source/blender/bmesh/intern/bmesh_idmap.h new file mode 100644 index 00000000000..1f79983f9db --- /dev/null +++ b/source/blender/bmesh/intern/bmesh_idmap.h @@ -0,0 +1,66 @@ +#include "BLI_compiler_attrs.h" +#include "BLI_compiler_compat.h" +#include "BLI_sys_types.h" + +#include "bmesh.h" + +#ifndef WITH_BM_ID_FREELIST +# define WITH_BM_ID_FREELIST +#endif + +//#define USE_NEW_IDMAP + +#ifdef __cplusplus +# include "BLI_map.hh" +# include "BLI_vector.hh" +#endif + +typedef struct BMIdMap { + int flag; + + uint maxid; + int cd_id_off[15]; + BMesh *bm; + + BMElem **map; + int map_size; + +#ifdef __cplusplus + blender::Vector freelist; + + using FreeIdxMap = blender::Map; + +/* maps ids to their position within the freelist + only used if freelist is bigger then a certain size, + see FREELIST_HASHMAP_THRESHOLD_HIGH in bmesh_construct.c.*/ + FreeIdxMap *free_idx_map; +#endif +} BMIdMap; + +#ifdef __cplusplus +extern "C" { +#endif + +BMIdMap *BM_idmap_new(BMesh *bm, int elem_mask); +void BM_idmap_check_attributes(BMIdMap *idmap); +void BM_idmap_check_ids(BMIdMap *idmap); +void BM_idmap_destroy(BMIdMap *idmap); + +int BM_idmap_alloc(BMIdMap *idmap, BMElem *elem); +void BM_idmap_assign(BMIdMap *idmap, BMElem *elem, int id); +void BM_idmap_release(BMIdMap *idmap, BMElem *elem, bool clear_id); +int BM_idmap_check_assign(BMIdMap *idmap, BMElem *elem); + +BLI_INLINE int BM_idmap_get_id(BMIdMap *map, BMElem *elem) +{ + return BM_ELEM_CD_GET_INT(elem, map->cd_id_off[(int)elem->head.htype]); +} + +BLI_INLINE BMElem *BM_idmap_lookup(BMIdMap *map, int elem) +{ + return elem >= 0 ? map->map[elem] : NULL; +} + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/bmesh/intern/bmesh_inline.h b/source/blender/bmesh/intern/bmesh_inline.h index 0fe89e79721..f897ec73af7 100644 --- a/source/blender/bmesh/intern/bmesh_inline.h +++ b/source/blender/bmesh/intern/bmesh_inline.h @@ -65,9 +65,9 @@ BLI_INLINE void _bm_elem_flag_merge(BMHeader *head_a, BMHeader *head_b) BLI_INLINE void _bm_elem_flag_merge_ex(BMHeader *head_a, BMHeader *head_b, const char hflag_and) { - if (((head_a->hflag & head_b->hflag) & hflag_and) == 0) { - head_a->hflag &= ~hflag_and; - head_b->hflag &= ~hflag_and; + if (((head_a->hflag & head_b->hflag) & hflag_and) == (char)0) { + head_a->hflag &= (char)(~hflag_and); + head_b->hflag &= (char)(~hflag_and); } _bm_elem_flag_merge(head_a, head_b); } diff --git a/source/blender/bmesh/intern/bmesh_interp.c b/source/blender/bmesh/intern/bmesh_interp.c index 16db91595ec..d6586f19bed 100644 --- a/source/blender/bmesh/intern/bmesh_interp.c +++ b/source/blender/bmesh/intern/bmesh_interp.c @@ -12,11 +12,13 @@ #include "DNA_meshdata_types.h" #include "BLI_alloca.h" +#include "BLI_array.h" #include "BLI_linklist.h" #include "BLI_math.h" #include "BLI_memarena.h" #include "BLI_string.h" #include "BLI_task.h" +#include "BLI_utildefines.h" #include "BKE_attribute.h" #include "BKE_customdata.h" @@ -25,8 +27,56 @@ #include "bmesh.h" #include "intern/bmesh_private.h" +int bm_save_id(BMesh *bm, BMElem *elem) +{ + if (!elem->head.data) { + return -1; + } + + if (bm->idmap.flag & elem->head.htype) { + return BM_ELEM_GET_ID(bm, elem); + } + else { + return -1; + } +} + +void bm_restore_id(BMesh *bm, BMElem *elem, int id) +{ + if (!elem->head.data || id == -1) { + return; + } + + if (bm->idmap.flag & elem->head.htype) { + BM_ELEM_CD_SET_INT(elem, bm->idmap.cd_id_off[elem->head.htype], id); + } +} + +static void copy_cdata_simple(BMesh *bm, + CustomData *data_layer, + BMElem *ele_dst, + const BMElem *ele_src) +{ + int id = bm_save_id(bm, ele_dst); + int cd_tflags; + MToolFlags saved_tflags; + + if ((cd_tflags = CustomData_get_offset(data_layer, CD_TOOLFLAGS)) != -1) { + saved_tflags = *(MToolFlags *)BM_ELEM_CD_GET_VOID_P(ele_dst, cd_tflags); + } + + CustomData_bmesh_free_block_data(data_layer, ele_dst->head.data); + CustomData_bmesh_copy_data(data_layer, data_layer, ele_src->head.data, &ele_dst->head.data); + + if (cd_tflags != -1) { + *(MToolFlags *)BM_ELEM_CD_GET_VOID_P(ele_dst, cd_tflags) = saved_tflags; + } + bm_restore_id(bm, ele_dst, id); +} + /* edge and vertex share, currently there's no need to have different logic */ -static void bm_data_interp_from_elem(CustomData *data_layer, +static void bm_data_interp_from_elem(BMesh *bm, + CustomData *data_layer, const BMElem *ele_src_1, const BMElem *ele_src_2, BMElem *ele_dst, @@ -39,9 +89,7 @@ static void bm_data_interp_from_elem(CustomData *data_layer, /* do nothing */ } else { - CustomData_bmesh_free_block_data(data_layer, ele_dst->head.data); - CustomData_bmesh_copy_data( - data_layer, data_layer, ele_src_1->head.data, &ele_dst->head.data); + copy_cdata_simple(bm, data_layer, ele_dst, ele_src_1); } } else if (fac >= 1.0f) { @@ -49,9 +97,7 @@ static void bm_data_interp_from_elem(CustomData *data_layer, /* do nothing */ } else { - CustomData_bmesh_free_block_data(data_layer, ele_dst->head.data); - CustomData_bmesh_copy_data( - data_layer, data_layer, ele_src_2->head.data, &ele_dst->head.data); + copy_cdata_simple(bm, data_layer, ele_dst, ele_src_2); } } else { @@ -71,14 +117,14 @@ void BM_data_interp_from_verts( BMesh *bm, const BMVert *v_src_1, const BMVert *v_src_2, BMVert *v_dst, const float fac) { bm_data_interp_from_elem( - &bm->vdata, (const BMElem *)v_src_1, (const BMElem *)v_src_2, (BMElem *)v_dst, fac); + bm, &bm->vdata, (const BMElem *)v_src_1, (const BMElem *)v_src_2, (BMElem *)v_dst, fac); } void BM_data_interp_from_edges( BMesh *bm, const BMEdge *e_src_1, const BMEdge *e_src_2, BMEdge *e_dst, const float fac) { bm_data_interp_from_elem( - &bm->edata, (const BMElem *)e_src_1, (const BMElem *)e_src_2, (BMElem *)e_dst, fac); + bm, &bm->edata, (const BMElem *)e_src_1, (const BMElem *)e_src_2, (BMElem *)e_dst, fac); } /** @@ -270,17 +316,52 @@ static bool quad_co(const float v1[3], /* rotate */ poly_rotate_plane(n, projverts, 5); + float projverts2[4][3]; + /* subtract origin */ for (i = 0; i < 4; i++) { sub_v2_v2(projverts[i], projverts[4]); + + copy_v3_v3(projverts2[i], projverts[i]); } - if (!isect_point_quad_v2(origin, projverts[0], projverts[1], projverts[2], projverts[3])) { + // expand quad a bit +#if 0 + float eps = FLT_EPSILON * 40000; + float c[3]; + + mid_v3_v3v3v3v3(c, projverts[0], projverts[1], projverts[2], projverts[3]); + + sub_v3_v3(projverts2[0], c); + sub_v3_v3(projverts2[1], c); + sub_v3_v3(projverts2[2], c); + sub_v3_v3(projverts2[3], c); + mul_v3_fl(projverts2[0], 1.0f + eps); + mul_v3_fl(projverts2[1], 1.0f + eps); + mul_v3_fl(projverts2[2], 1.0f + eps); + mul_v3_fl(projverts2[3], 1.0f + eps); + add_v3_v3(projverts2[0], c); + add_v3_v3(projverts2[1], c); + add_v3_v3(projverts2[2], c); + add_v3_v3(projverts2[3], c); +#endif + + if (!isect_point_quad_v2(origin, projverts2[0], projverts2[1], projverts2[2], projverts2[3])) { return false; } resolve_quad_uv_v2(r_uv, origin, projverts[0], projverts[3], projverts[2], projverts[1]); +#if 0 + float eps2 = FLT_EPSILON * 4000; + if (r_uv[0] < -eps2 || r_uv[1] < -eps2 || r_uv[0] > 1.0 + eps2 || r_uv[1] > 1.0 + eps2) { + return false; + } +#endif + + CLAMP(r_uv[0], 0.0f, 0.99999f); + CLAMP(r_uv[1], 0.0f, 0.99999f); + return true; } @@ -313,8 +394,7 @@ static bool mdisp_in_mdispquad(BMLoop *l_src, float r_axis_y[3], float r_uv[2]) { - float v1[3], v2[3], c[3], v3[3], v4[3], e1[3], e2[3]; - float eps = FLT_EPSILON * 4000; + float v1[3], v2[3], v3[3], v4[3], e1[3], e2[3]; if (is_zero_v3(l_src->v->no)) { BM_vert_normal_update_all(l_src->v); @@ -326,6 +406,8 @@ static bool mdisp_in_mdispquad(BMLoop *l_src, compute_mdisp_quad(l_dst, l_dst_f_center, v1, v2, v3, v4, e1, e2); /* expand quad a bit */ + float c[3]; + float eps = FLT_EPSILON * 400; mid_v3_v3v3v3v3(c, v1, v2, v3, v4); sub_v3_v3(v1, c); @@ -407,8 +489,10 @@ typedef struct BMLoopInterpMultiresData { BMLoop *l_src_first; int cd_loop_mdisp_offset; + int space; MDisps *md_dst; const float *f_src_center; + const float *f_dst_center; float *axis_x, *axis_y; float *v1, *v4; @@ -427,6 +511,7 @@ static void loop_interp_multires_cb(void *__restrict userdata, BMLoop *l_first = data->l_src_first; BMLoop *l_dst = data->l_dst; const int cd_loop_mdisp_offset = data->cd_loop_mdisp_offset; + int space = data->space; MDisps *md_dst = data->md_dst; const float *f_src_center = data->f_src_center; @@ -441,6 +526,19 @@ static void loop_interp_multires_cb(void *__restrict userdata, const int res = data->res; const float d = data->d; + float quad[4][3]; + + float n1[3], n2[3]; + normal_tri_v3(n1, l_dst->v->co, l_dst->next->v->co, data->f_dst_center); + + if (space == MULTIRES_SPACE_ABSOLUTE) { + BMLoop *l = l_dst; + + copy_v3_v3(quad[0], data->f_dst_center); + interp_v3_v3v3(quad[1], l->v->co, l->next->v->co, 0.5); + copy_v3_v3(quad[2], l->v->co); + interp_v3_v3v3(quad[3], l->v->co, l->prev->v->co, 0.5); + } float x = d * ix, y; int iy; @@ -452,24 +550,71 @@ static void loop_interp_multires_cb(void *__restrict userdata, madd_v3_v3v3fl(co2, v4, e2, y); interp_v3_v3v3(co, co1, co2, x); + float sum[3]; + int tot = 0; + zero_v3(sum); + float mindis = 1e17; + + float baseco[3]; + if (space == MULTIRES_SPACE_ABSOLUTE) { + interp_bilinear_quad_v3(quad, x, y, baseco); + } + do { MDisps *md_src; float src_axis_x[3], src_axis_y[3]; float uv[2]; + normal_tri_v3(n2, l_iter->v->co, l_iter->next->v->co, data->f_src_center); + float th = dot_v3v3(n1, n2); + if (th < 0.0f) { + negate_v3(n2); + } + + th = acos(dot_v3v3(n1, n2) * 0.999999f); + if (th > M_PI * 0.1) { + continue; + } + md_src = BM_ELEM_CD_GET_VOID_P(l_iter, cd_loop_mdisp_offset); if (mdisp_in_mdispquad(l_dst, l_iter, f_src_center, co, res, src_axis_x, src_axis_y, uv)) { - old_mdisps_bilinear(md_dst->disps[iy * res + ix], md_src->disps, res, uv[0], uv[1]); - bm_loop_flip_disp(src_axis_x, src_axis_y, axis_x, axis_y, md_dst->disps[iy * res + ix]); + float disp[3]; + copy_v3_v3(disp, md_dst->disps[iy * res + ix]); - break; + old_mdisps_bilinear(disp, md_src->disps, res, uv[0], uv[1]); + + if (space == MULTIRES_SPACE_TANGENT) { + bm_loop_flip_disp(src_axis_x, src_axis_y, axis_x, axis_y, disp); + } + + float l = len_v3v3(disp, baseco); + if (l < mindis) { + mindis = l; + // tot++; + // copy_v3_v3(sum, disp); + } + add_v3_v3(sum, disp); + tot++; + // break; } } while ((l_iter = l_iter->next) != l_first); + + if (tot) { + mul_v3_fl(sum, 1.0 / (float)tot); + copy_v3_v3(md_dst->disps[iy * res + ix], sum); + } + else { + // printf("failed to set disp: %f %f\n", x, y); + if (space == MULTIRES_SPACE_ABSOLUTE) { + // copy_v3_v3(md_dst->disps[iy * res + ix], baseco); + // copy_v3_v3(md_dst->disps[iy * res + ix], baseco); + } + } } } -void BM_loop_interp_multires_ex(BMesh *UNUSED(bm), +void BM_loop_interp_multires_ex(BMesh *bm, BMLoop *l_dst, const BMFace *f_src, const float f_dst_center[3], @@ -511,7 +656,9 @@ void BM_loop_interp_multires_ex(BMesh *UNUSED(bm), .cd_loop_mdisp_offset = cd_loop_mdisp_offset, .md_dst = md_dst, .f_src_center = f_src_center, + .f_dst_center = f_dst_center, .axis_x = axis_x, + .space = bm->multiresSpace, .axis_y = axis_y, .v1 = v1, .v4 = v4, @@ -554,6 +701,8 @@ void BM_face_interp_multires_ex(BMesh *bm, BM_loop_interp_multires_ex( bm, l_iter, f_src, f_dst_center, f_src_center, cd_loop_mdisp_offset); } while ((l_iter = l_iter->next) != l_first); + + BM_face_multires_bounds_smooth(bm, f_dst); } void BM_face_interp_multires(BMesh *bm, BMFace *f_dst, const BMFace *f_src) @@ -571,54 +720,277 @@ void BM_face_interp_multires(BMesh *bm, BMFace *f_dst, const BMFace *f_src) } } -void BM_face_multires_bounds_smooth(BMesh *bm, BMFace *f) +// smooth with weight falloff towards center of grids +static void bm_multires_smooth(BMesh *bm, BMFace *f, bool no_boundary) { const int cd_loop_mdisp_offset = CustomData_get_offset(&bm->ldata, CD_MDISPS); + float(*orig)[3] = NULL; + BLI_array_staticdeclare(orig, 256 * 256); + + if (cd_loop_mdisp_offset < 0) { + return; + } + + float cent[3]; + zero_v3(cent); + + int ctot = 0; + BMLoop *cl = f->l_first; + do { + add_v3_v3(cent, cl->v->co); + cl = cl->next; + ctot++; + } while (cl != f->l_first); + mul_v3_fl(cent, 1.0f / (float)ctot); + + const int offs[][2] = { + {0, 0}, + // {-1, -1}, + {-1, 0}, + // {-1, 1}, + {0, 1}, + // {1, 1}, + {1, 0}, + // {1, -1}, + {0, -1}, + }; + + int totoff = sizeof(offs) / sizeof(*offs); + +#ifndef ABS +# define ABS(a) ((a) < 0 ? -(a) : (a)) +#endif + + // int space = bm->multiresSpace; + BMLoop *l = f->l_first; + do { + MDisps *md = BM_ELEM_CD_GET_VOID_P(l, cd_loop_mdisp_offset); + if (!md->disps) + continue; + + int res = (int)floor(sqrt((double)md->totdisp) + 0.000001); + + int start = no_boundary ? 1 : 0; + int end = no_boundary ? res - 1 : res; + float df = 1.0f / (float)(res - 1); + float u = 0.0; + + BLI_array_clear(orig); + BLI_array_reserve(orig, md->totdisp * 3); + memcpy(orig, md->disps, sizeof(float) * 3 * md->totdisp); + + for (int x = start; x < end; x++, u += df) { + float v = 0.0; + + for (int y = start; y < end; y++, v += df) { + float co[3]; + float tot = 0.0f; + + zero_v3(co); + + int idx1 = y * res + x; + + for (int oi = 0; oi < totoff; oi++) { + int ox = x + offs[oi][0]; + int oy = y + offs[oi][1]; + MDisps *md2 = md; + + if (1 && (ox < 0 || oy < 0 || ox >= res || oy >= res)) { + BMLoop *l2 = NULL; + BMLoop *ls = l; + + if (ox < 0 && oy < 0) { + l2 = ls->next->next; + ox = ABS(ox); + oy = ABS(oy); + } + else if (ox < 0 && oy >= 0 && oy < res) { + l2 = ls->prev; + int t = oy; + + oy = -ox; + ox = t; + } + else if (oy < 0 && ox >= 0 && ox < res) { + l2 = ls->next; + int t = oy; + + oy = ox; + ox = -t; + } + else if (ox >= res && oy >= 0 && oy < res) { + l2 = ls->radial_next->next; + + if (ls->v == l2->v) { + int t = oy; + + oy = 2 * res - ox - 1; + ox = t; + } + else { + l2 = l2->prev; + ox = res - ox; + } + + // XXX disables this branch + // ox = oy = -1; + } + else if (oy >= res && ox >= 0 && ox < res) { + l2 = ls->prev->radial_next; + if (l2->v == ls->v) { + int t = ox; + + ox = 2 * res - oy - 1; + oy = t; + } + else { + l2 = l2->next; + oy = 2 * res - oy - 1; + } + // XXX disables this branch + // ox = oy = -1; + } + else { + printf("ignoring non-4-valence multires corner %d %d %d %d : %d %d %d\t", + ox, + oy, + offs[oi][0], + offs[oi][1], + x, + y, + res); + l2 = NULL; + } + + if (l2) { + // ox = res - ox - 1; + // oy = res - oy - 1; + md2 = BM_ELEM_CD_GET_VOID_P(l2, cd_loop_mdisp_offset); + } + } + + if (!md2->disps || oy < 0 || oy >= res || ox < 0 || ox >= res) { + continue; + } + + int idx2 = oy * res + ox; + float *oco2 = md == md2 ? orig[idx2] : md2->disps[idx2]; + float co2[3]; + + copy_v3_v3(co2, oco2); + + float dx = (float)offs[oi][0]; + float dy = (float)offs[oi][1]; + + float w = 2.0f - dx * dx + dy * dy; + + if (no_boundary && (ox == 0 || oy == 0 || ox == res - 1 || oy == res - 1)) { + // w = 2.0; + } + else if (ox == x && oy == y) { + // blend less away from edges + float au = fabs(u - 0.5) * 2.0, av = fabs(v - 0.5) * 2.0; + float w2 = au * au + av * av; + + w = 4.0 * w2; + } + w = 1.0; + + mul_v3_fl(co2, w); + + tot += w; + add_v3_v3(co, co2); + } + + /* + float vec[3]; + copy_v3_v3(vec, f->no); + mul_v3_fl(vec, 0.4); + + add_v3_v3(md->disps[idx1], vec); + sub_v3_v3(md->disps[idx1], cent); + mul_v3_fl(md->disps[idx1], 1.2); + add_v3_v3(md->disps[idx1], cent); + + continue; + //*/ + + if (tot > 0.0f) { + mul_v3_fl(co, 1.0f / tot); + copy_v3_v3(md->disps[idx1], co); + } + } + } + + l = l->next; + } while (l != f->l_first); + + BLI_array_free(orig); +} + +struct Object *multires_dump_grids_bmesh(struct Object *bmob, BMesh *bm); + +void bmo_test_mres_smooth_exec(BMesh *bm, BMOperator *op) +{ + BMIter iter; + BMFace *f; + + if (!CustomData_has_layer(&bm->ldata, CD_MDISPS)) { + return; + } + + BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { + bool ok = !!BM_elem_flag_test(f, BM_ELEM_SELECT); + ok = ok && !BM_elem_flag_test(f, BM_ELEM_HIDDEN); + + if (!ok) { + continue; + } + + // bm_multires_smooth(bm, f, true); + // BM_multires_smooth(bm, f, false); + // BM_multires_smooth(bm, f, false); + // for (int i=0; i<5; i++) { + BM_face_multires_bounds_smooth(bm, f); + // } + } + + multires_dump_grids_bmesh(NULL, bm); +} + +void BM_face_multires_bounds_smooth(BMesh *bm, BMFace *f) +{ + return; + if (bm->multiresSpace == MULTIRES_SPACE_ABSOLUTE) { + BM_face_multires_stitch(bm, f); + + // for (int i=0; i<5; i++) { + // bm_multires_smooth(bm, f, true); + //} + } +} + +/** + * smooths boundaries between multires grids, + * including some borders in adjacent faces + */ +void BM_face_multires_stitch(BMesh *bm, BMFace *f) +{ BMLoop *l; BMIter liter; + float co[3]; + const int cd_loop_mdisp_offset = CustomData_get_offset(&bm->ldata, CD_MDISPS); + int sides = 0; if (cd_loop_mdisp_offset == -1) { return; } - BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) { - MDisps *mdp = BM_ELEM_CD_GET_VOID_P(l->prev, cd_loop_mdisp_offset); - MDisps *mdl = BM_ELEM_CD_GET_VOID_P(l, cd_loop_mdisp_offset); - MDisps *mdn = BM_ELEM_CD_GET_VOID_P(l->next, cd_loop_mdisp_offset); - float co1[3]; - int sides; - int y; - - /** - * mdisps is a grid of displacements, ordered thus: - *
-     *                    v4/next
-     *                      |
-     *  |      v1/cent-----mid2 ---> x
-     *  |         |         |
-     *  |         |         |
-     * v2/prev---mid1-----v3/cur
-     *            |
-     *            V
-     *            y
-     * 
- */ - - sides = (int)sqrt(mdp->totdisp); - for (y = 0; y < sides; y++) { - mid_v3_v3v3(co1, mdn->disps[y * sides], mdl->disps[y]); - - copy_v3_v3(mdn->disps[y * sides], co1); - copy_v3_v3(mdl->disps[y], co1); - } - } - BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) { MDisps *mdl1 = BM_ELEM_CD_GET_VOID_P(l, cd_loop_mdisp_offset); MDisps *mdl2; - float co1[3], co2[3], co[3]; - int sides; - int y; + float co1[3], co2[3]; + int x, y; /** * mdisps is a grid of displacements, ordered thus: @@ -646,7 +1018,7 @@ void BM_face_multires_bounds_smooth(BMesh *bm, BMFace *f) mdl2 = BM_ELEM_CD_GET_VOID_P(l->radial_next->next, cd_loop_mdisp_offset); } - sides = (int)sqrt(mdl1->totdisp); + sides = (int)floor(sqrt(mdl1->totdisp) + FLT_EPSILON); for (y = 0; y < sides; y++) { int a1, a2, o1, o2; @@ -658,24 +1030,128 @@ void BM_face_multires_bounds_smooth(BMesh *bm, BMFace *f) o2 = (sides - 1) * sides + y; } else { - a1 = sides * y + sides - 2; - a2 = sides * y + sides - 2; o1 = sides * y + sides - 1; o2 = sides * y + sides - 1; } - /* magic blending numbers, hardcoded! */ - add_v3_v3v3(co1, mdl1->disps[a1], mdl2->disps[a2]); - mul_v3_fl(co1, 0.18); - - add_v3_v3v3(co2, mdl1->disps[o1], mdl2->disps[o2]); - mul_v3_fl(co2, 0.32); - - add_v3_v3v3(co, co1, co2); + mid_v3_v3v3(co, mdl1->disps[o1], mdl2->disps[o2]); copy_v3_v3(mdl1->disps[o1], co); copy_v3_v3(mdl2->disps[o2], co); } + + BMLoop *l2 = l->prev->radial_next; + bool reverse = false; + + if (l2->v != l->v) { + reverse = true; + l2 = l2->next; + } + + mdl2 = BM_ELEM_CD_GET_VOID_P(l2, cd_loop_mdisp_offset); + y = sides - 1; + + if (!mdl2->disps) { + continue; + } + + for (x = 0; x < sides; x++) { + int x2, y2, o1, o2; + + if (!reverse) { + x2 = sides - 1; + y2 = x; + } + else { + x2 = x; + y2 = y; + } + + o1 = y * sides + x; + o2 = y2 * sides + x2; + + mid_v3_v3v3(co, mdl1->disps[o1], mdl2->disps[o2]); + copy_v3_v3(mdl1->disps[o1], co); + copy_v3_v3(mdl2->disps[o2], co); + } + } + + // do exterior corners + BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) { + MDisps *md1 = BM_ELEM_CD_GET_VOID_P(l, cd_loop_mdisp_offset); + BMIter l2iter; + BMLoop *l2; + int x = sides - 1, y = sides - 1; + int idx = y * sides + x; + int tot = 1; + + zero_v3(co); + + if (!md1->disps) { + continue; + } + + add_v3_v3(co, md1->disps[idx]); + + BM_ITER_ELEM (l2, &l2iter, l->v, BM_LOOPS_OF_VERT) { + if (l2->v != l->v) { // winding is flipped + l2 = l2->next; + } + + MDisps *md2 = BM_ELEM_CD_GET_VOID_P(l2, cd_loop_mdisp_offset); + + if (l == l2 || !md2->disps) { + continue; + } + + add_v3_v3(co, md2->disps[idx]); + tot++; + } + + mul_v3_fl(co, 1.0f / (float)tot); + + BM_ITER_ELEM (l2, &l2iter, l->v, BM_LOOPS_OF_VERT) { + if (l2->v != l->v) { // winding is flipped + l2 = l2->next; + } + + MDisps *md2 = BM_ELEM_CD_GET_VOID_P(l2, cd_loop_mdisp_offset); + + if (l == l2 || !md2->disps) { + continue; + } + + copy_v3_v3(md2->disps[idx], co); + } + } + + // do interior corners + int tot = 0; + zero_v3(co); + + BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) { + MDisps *md1 = BM_ELEM_CD_GET_VOID_P(l, cd_loop_mdisp_offset); + + if (!md1->disps) { + continue; + } + + add_v3_v3(co, md1->disps[0]); + tot++; + } + + if (tot) { + mul_v3_fl(co, 1.0f / (float)tot); + } + + BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) { + MDisps *md1 = BM_ELEM_CD_GET_VOID_P(l, cd_loop_mdisp_offset); + + if (!md1->disps) { + continue; + } + + copy_v3_v3(md1->disps[0], co); } } @@ -760,16 +1236,31 @@ void BM_vert_interp_from_face(BMesh *bm, BMVert *v_dst, const BMFace *f_src) CustomData_bmesh_interp(&bm->vdata, blocks, w, NULL, f_src->len, v_dst->head.data); } +void BPy_bm_new_customdata_layout(BMesh *bm, CustomData *cdata, void *state, char htype); +void *BPy_bm_new_customdata_layout_pre(BMesh *bm, CustomData *cdata, char htype); + static void update_data_blocks(BMesh *bm, CustomData *olddata, CustomData *data) { BMIter iter; BLI_mempool *oldpool = olddata->pool; void *block; + CustomDataLayer **nocopy_layers = NULL; + BLI_array_staticdeclare(nocopy_layers, 1024); + + // temporarily clear CD_FLAG_ELEM_NOCOPY flags + for (int i = 0; i < data->totlayer; i++) { + if (data->layers[i].flag & CD_FLAG_ELEM_NOCOPY) { + data->layers[i].flag &= ~CD_FLAG_ELEM_NOCOPY; + BLI_array_append(nocopy_layers, data->layers + i); + } + } + if (data == &bm->vdata) { BMVert *eve; - CustomData_bmesh_init_pool(data, bm->totvert, BM_VERT); + CustomData_bmesh_init_pool_ex(data, bm->totvert, BM_VERT, __func__); + void *state = BPy_bm_new_customdata_layout_pre(bm, olddata, BM_VERT); BM_ITER_MESH (eve, &iter, bm, BM_VERTS_OF_MESH) { block = NULL; @@ -778,11 +1269,15 @@ static void update_data_blocks(BMesh *bm, CustomData *olddata, CustomData *data) CustomData_bmesh_free_block(olddata, &eve->head.data); eve->head.data = block; } + + BPy_bm_new_customdata_layout(bm, &bm->vdata, state, BM_VERT); + MEM_SAFE_FREE(state); } else if (data == &bm->edata) { BMEdge *eed; - CustomData_bmesh_init_pool(data, bm->totedge, BM_EDGE); + CustomData_bmesh_init_pool_ex(data, bm->totedge, BM_EDGE, __func__); + void *state = BPy_bm_new_customdata_layout_pre(bm, olddata, BM_EDGE); BM_ITER_MESH (eed, &iter, bm, BM_EDGES_OF_MESH) { block = NULL; @@ -791,13 +1286,18 @@ static void update_data_blocks(BMesh *bm, CustomData *olddata, CustomData *data) CustomData_bmesh_free_block(olddata, &eed->head.data); eed->head.data = block; } + + BPy_bm_new_customdata_layout(bm, &bm->edata, state, BM_EDGE); + MEM_SAFE_FREE(state); } else if (data == &bm->ldata) { BMIter liter; BMFace *efa; BMLoop *l; - CustomData_bmesh_init_pool(data, bm->totloop, BM_LOOP); + CustomData_bmesh_init_pool_ex(data, bm->totloop, BM_LOOP, __func__); + void *state = BPy_bm_new_customdata_layout_pre(bm, olddata, BM_LOOP); + BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) { BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { block = NULL; @@ -807,11 +1307,15 @@ static void update_data_blocks(BMesh *bm, CustomData *olddata, CustomData *data) l->head.data = block; } } + + BPy_bm_new_customdata_layout(bm, &bm->ldata, state, BM_LOOP); + MEM_SAFE_FREE(state); } else if (data == &bm->pdata) { BMFace *efa; - CustomData_bmesh_init_pool(data, bm->totface, BM_FACE); + CustomData_bmesh_init_pool_ex(data, bm->totface, BM_FACE, __func__); + void *state = BPy_bm_new_customdata_layout_pre(bm, olddata, BM_FACE); BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) { block = NULL; @@ -820,18 +1324,137 @@ static void update_data_blocks(BMesh *bm, CustomData *olddata, CustomData *data) CustomData_bmesh_free_block(olddata, &efa->head.data); efa->head.data = block; } + + BPy_bm_new_customdata_layout(bm, &bm->pdata, state, BM_FACE); + MEM_SAFE_FREE(state); } else { /* should never reach this! */ BLI_assert(0); } + for (int i = 0; i < BLI_array_len(nocopy_layers); i++) { + nocopy_layers[i]->flag |= CD_FLAG_ELEM_NOCOPY; + } + + BLI_array_free(nocopy_layers); + if (oldpool) { /* this should never happen but can when dissolve fails - T28960. */ BLI_assert(data->pool != oldpool); BLI_mempool_destroy(oldpool); } + + bm_update_idmap_cdlayers(bm); +} + +void BM_data_layers_ensure(BMesh *bm, CustomData *data, BMCustomLayerReq *layers, int totlayer) +{ + bool modified = false; + CustomData old = *data; + CustomData temp; + eCustomDataMask mask = 0; + + if (old.layers) { + old.layers = MEM_dupallocN(old.layers); + } + + memset(&temp, 0, sizeof(temp)); + CustomData_reset(&temp); + + for (int i = 0; i < totlayer; i++) { + BMCustomLayerReq *req = layers + i; + int idx; + + mask |= 1ULL << (eCustomDataMask)req->type; + + if (req->name) { + idx = CustomData_get_named_layer_index(data, req->type, req->name); + } + else { + idx = CustomData_get_layer_index(data, req->type); + } + + if (idx < 0) { + modified = true; + + if (req->name) { + CustomData_add_layer_named(&temp, req->type, CD_ASSIGN, NULL, 0, req->name); + } + else { + CustomData_add_layer(&temp, req->type, CD_ASSIGN, NULL, 0); + } + } + } + + int htype; + if (data == &bm->vdata) { + htype = BM_VERT; + } + else if (data == &bm->edata) { + htype = BM_EDGE; + } + else if (data == &bm->ldata) { + htype = BM_LOOP; + } + else if (data == &bm->pdata) { + htype = BM_FACE; + } + else { + printf("error in %s!\n", __func__); + CustomData_free(&temp, 0); + return; + } + + CustomDataLayer **nocopy_layers = NULL; + BLI_array_declare(nocopy_layers); + + for (int i = 0; i < data->totlayer; i++) { + if (data->layers[i].flag & CD_FLAG_NOCOPY) { + BLI_array_append(nocopy_layers, &data->layers[i]); + } + } + + if (modified) { + CustomData_merge(&temp, data, mask, CD_ASSIGN, 0); + } + + for (int i = 0; i < BLI_array_len(nocopy_layers); i++) { + nocopy_layers[i]->flag |= CD_FLAG_NOCOPY; + } + + BLI_array_free(nocopy_layers); + + for (int i = 0; i < totlayer; i++) { + BMCustomLayerReq *req = layers + i; + int idx; + + mask |= 1LL << req->type; + + if (req->name) { + idx = CustomData_get_named_layer_index(data, req->type, req->name); + } + else { + idx = CustomData_get_layer_index(data, req->type); + } + + data->layers[idx].flag |= req->flag; + } + + if (modified) { + /* the pool is now owned by olddata and must not be shared */ + data->pool = NULL; + + update_data_blocks(bm, &old, data); + bm_update_idmap_cdlayers(bm); + } + + if (old.layers) { + MEM_freeN(old.layers); + } + + CustomData_free(&temp, 0); } void BM_data_layer_add(BMesh *bm, CustomData *data, int type) @@ -847,6 +1470,8 @@ void BM_data_layer_add(BMesh *bm, CustomData *data, int type) if (olddata.layers) { MEM_freeN(olddata.layers); } + + bm_update_idmap_cdlayers(bm); } void BM_data_layer_add_named(BMesh *bm, CustomData *data, int type, const char *name) @@ -862,6 +1487,8 @@ void BM_data_layer_add_named(BMesh *bm, CustomData *data, int type, const char * if (olddata.layers) { MEM_freeN(olddata.layers); } + + bm_update_idmap_cdlayers(bm); } void BM_data_layer_ensure_named(BMesh *bm, CustomData *data, int type, const char *name) @@ -935,6 +1562,8 @@ void BM_data_layer_free(BMesh *bm, CustomData *data, int type) if (olddata.layers) { MEM_freeN(olddata.layers); } + + bm_update_idmap_cdlayers(bm); } bool BM_data_layer_free_named(BMesh *bm, CustomData *data, const char *name) @@ -978,6 +1607,8 @@ void BM_data_layer_free_n(BMesh *bm, CustomData *data, int type, int n) if (olddata.layers) { MEM_freeN(olddata.layers); } + + bm_update_idmap_cdlayers(bm); } void BM_data_layer_copy(BMesh *bm, CustomData *data, int type, int src_n, int dst_n) diff --git a/source/blender/bmesh/intern/bmesh_interp.h b/source/blender/bmesh/intern/bmesh_interp.h index 013ed9d237f..8ee0b78f0f5 100644 --- a/source/blender/bmesh/intern/bmesh_interp.h +++ b/source/blender/bmesh/intern/bmesh_interp.h @@ -9,10 +9,18 @@ struct LinkNode; struct MemArena; +typedef struct BMCustomLayerReq { + int type; + const char *name; // can be NULL + int flag; +} BMCustomLayerReq; + #ifdef __cplusplus extern "C" { #endif +void BM_face_multires_stitch(BMesh *bm, BMFace *f); + void BM_loop_interp_multires_ex(BMesh *bm, BMLoop *l_dst, const BMFace *f_src, @@ -58,8 +66,15 @@ void BM_data_interp_from_edges( * Walks around the faces of \a e and interpolates * the loop data between two sources. */ -void BM_data_interp_face_vert_edge( - BMesh *bm, const BMVert *v_src_1, const BMVert *v_src_2, BMVert *v, BMEdge *e, float fac); +void BM_data_interp_face_vert_edge(BMesh *bm, + const BMVert *v_src_1, + const BMVert *v_src_2, + BMVert *v, + BMEdge *e, + const float fac); + +void BM_data_layers_ensure(BMesh *bm, CustomData *data, BMCustomLayerReq *layers, int totlayer); + void BM_data_layer_add(BMesh *bm, CustomData *data, int type); void BM_data_layer_add_named(BMesh *bm, CustomData *data, int type, const char *name); void BM_data_layer_ensure_named(BMesh *bm, CustomData *data, int type, const char *name); diff --git a/source/blender/bmesh/intern/bmesh_iterators.cc b/source/blender/bmesh/intern/bmesh_iterators.cc index 91ece97fa21..0cc1ae14b2d 100644 --- a/source/blender/bmesh/intern/bmesh_iterators.cc +++ b/source/blender/bmesh/intern/bmesh_iterators.cc @@ -11,6 +11,7 @@ #include "MEM_guardedalloc.h" #include "BLI_bitmap.h" +#include "BLI_compiler_attrs.h" #include "BLI_utildefines.h" #include "bmesh.h" diff --git a/source/blender/bmesh/intern/bmesh_iterators_inline.h b/source/blender/bmesh/intern/bmesh_iterators_inline.h index 0fdc2f39ee8..43a86082706 100644 --- a/source/blender/bmesh/intern/bmesh_iterators_inline.h +++ b/source/blender/bmesh/intern/bmesh_iterators_inline.h @@ -28,8 +28,7 @@ ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1) BLI_INLINE void *BM_iter_step(BMIter *it * it with the appropriate function pointers based * upon its type. */ -ATTR_NONNULL(1) -BLI_INLINE bool BM_iter_init(BMIter *iter, BMesh *bm, const char itype, void *data) +ATTR_NONNULL(1) BLI_INLINE bool BM_iter_init(BMIter *iter, BMesh *bm, const char itype, void *data) { /* int argtype; */ iter->itype = itype; diff --git a/source/blender/bmesh/intern/bmesh_log.c b/source/blender/bmesh/intern/bmesh_log.c deleted file mode 100644 index a8a9390f525..00000000000 --- a/source/blender/bmesh/intern/bmesh_log.c +++ /dev/null @@ -1,1033 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ - -/** \file - * \ingroup bmesh - * - * The BMLog is an interface for storing undo/redo steps as a BMesh is - * modified. It only stores changes to the BMesh, not full copies. - * - * Currently it supports the following types of changes: - * - * - Adding and removing vertices - * - Adding and removing faces - * - Moving vertices - * - Setting vertex paint-mask values - * - Setting vertex hflags - */ - -#include "MEM_guardedalloc.h" - -#include "BLI_ghash.h" -#include "BLI_listbase.h" -#include "BLI_math.h" -#include "BLI_mempool.h" -#include "BLI_utildefines.h" - -#include "BKE_customdata.h" - -#include "bmesh.h" -#include "bmesh_log.h" -#include "range_tree.h" - -#include "BLI_strict_flags.h" - -struct BMLogEntry { - struct BMLogEntry *next, *prev; - - /* The following #GHash members map from an element ID to one of the log types above. */ - - /** Elements that were in the previous entry, but have been deleted. */ - GHash *deleted_verts; - GHash *deleted_faces; - /** Elements that were not in the previous entry, but are in the result of this entry. */ - GHash *added_verts; - GHash *added_faces; - - /** Vertices whose coordinates, mask value, or hflag have changed. */ - GHash *modified_verts; - GHash *modified_faces; - - BLI_mempool *pool_verts; - BLI_mempool *pool_faces; - - /** - * This is only needed for dropping BMLogEntries while still in - * dynamic-topology mode, as that should release vert/face IDs - * back to the BMLog but no BMLog pointer is available at that time. - * - * This field is not guaranteed to be valid, any use of it should - * check for NULL. - */ - BMLog *log; -}; - -struct BMLog { - /** Tree of free IDs */ - struct RangeTreeUInt *unused_ids; - - /** - * Mapping from unique IDs to vertices and faces - * - * Each vertex and face in the log gets a unique `uint` - * assigned. That ID is taken from the set managed by the - * unused_ids range tree. - * - * The ID is needed because element pointers will change as they - * are created and deleted. - */ - GHash *id_to_elem; - GHash *elem_to_id; - - /** All #BMLogEntrys, ordered from earliest to most recent. */ - ListBase entries; - - /** - * The current log entry from entries list - * - * If null, then the original mesh from before any of the log - * entries is current (i.e. there is nothing left to undo.) - * - * If equal to the last entry in the entries list, then all log - * entries have been applied (i.e. there is nothing left to redo.) - */ - BMLogEntry *current_entry; -}; - -typedef struct { - float co[3]; - float no[3]; - char hflag; - float mask; -} BMLogVert; - -typedef struct { - uint v_ids[3]; - char hflag; -} BMLogFace; - -/************************* Get/set element IDs ************************/ - -/* bypass actual hashing, the keys don't overlap */ -#define logkey_hash BLI_ghashutil_inthash_p_simple -#define logkey_cmp BLI_ghashutil_intcmp - -/* Get the vertex's unique ID from the log */ -static uint bm_log_vert_id_get(BMLog *log, BMVert *v) -{ - BLI_assert(BLI_ghash_haskey(log->elem_to_id, v)); - return POINTER_AS_UINT(BLI_ghash_lookup(log->elem_to_id, v)); -} - -/* Set the vertex's unique ID in the log */ -static void bm_log_vert_id_set(BMLog *log, BMVert *v, uint id) -{ - void *vid = POINTER_FROM_UINT(id); - - BLI_ghash_reinsert(log->id_to_elem, vid, v, NULL, NULL); - BLI_ghash_reinsert(log->elem_to_id, v, vid, NULL, NULL); -} - -/* Get a vertex from its unique ID */ -static BMVert *bm_log_vert_from_id(BMLog *log, uint id) -{ - void *key = POINTER_FROM_UINT(id); - BLI_assert(BLI_ghash_haskey(log->id_to_elem, key)); - return BLI_ghash_lookup(log->id_to_elem, key); -} - -/* Get the face's unique ID from the log */ -static uint bm_log_face_id_get(BMLog *log, BMFace *f) -{ - BLI_assert(BLI_ghash_haskey(log->elem_to_id, f)); - return POINTER_AS_UINT(BLI_ghash_lookup(log->elem_to_id, f)); -} - -/* Set the face's unique ID in the log */ -static void bm_log_face_id_set(BMLog *log, BMFace *f, uint id) -{ - void *fid = POINTER_FROM_UINT(id); - - BLI_ghash_reinsert(log->id_to_elem, fid, f, NULL, NULL); - BLI_ghash_reinsert(log->elem_to_id, f, fid, NULL, NULL); -} - -/* Get a face from its unique ID */ -static BMFace *bm_log_face_from_id(BMLog *log, uint id) -{ - void *key = POINTER_FROM_UINT(id); - BLI_assert(BLI_ghash_haskey(log->id_to_elem, key)); - return BLI_ghash_lookup(log->id_to_elem, key); -} - -/************************ BMLogVert / BMLogFace ***********************/ - -/* Get a vertex's paint-mask value - * - * Returns zero if no paint-mask layer is present */ -static float vert_mask_get(BMVert *v, const int cd_vert_mask_offset) -{ - if (cd_vert_mask_offset != -1) { - return BM_ELEM_CD_GET_FLOAT(v, cd_vert_mask_offset); - } - return 0.0f; -} - -/* Set a vertex's paint-mask value - * - * Has no effect is no paint-mask layer is present */ -static void vert_mask_set(BMVert *v, const float new_mask, const int cd_vert_mask_offset) -{ - if (cd_vert_mask_offset != -1) { - BM_ELEM_CD_SET_FLOAT(v, cd_vert_mask_offset, new_mask); - } -} - -/* Update a BMLogVert with data from a BMVert */ -static void bm_log_vert_bmvert_copy(BMLogVert *lv, BMVert *v, const int cd_vert_mask_offset) -{ - copy_v3_v3(lv->co, v->co); - copy_v3_v3(lv->no, v->no); - lv->mask = vert_mask_get(v, cd_vert_mask_offset); - lv->hflag = v->head.hflag; -} - -/* Allocate and initialize a BMLogVert */ -static BMLogVert *bm_log_vert_alloc(BMLog *log, BMVert *v, const int cd_vert_mask_offset) -{ - BMLogEntry *entry = log->current_entry; - BMLogVert *lv = BLI_mempool_alloc(entry->pool_verts); - - bm_log_vert_bmvert_copy(lv, v, cd_vert_mask_offset); - - return lv; -} - -/* Allocate and initialize a BMLogFace */ -static BMLogFace *bm_log_face_alloc(BMLog *log, BMFace *f) -{ - BMLogEntry *entry = log->current_entry; - BMLogFace *lf = BLI_mempool_alloc(entry->pool_faces); - BMVert *v[3]; - - BLI_assert(f->len == 3); - - // BM_iter_as_array(NULL, BM_VERTS_OF_FACE, f, (void **)v, 3); - BM_face_as_array_vert_tri(f, v); - - lf->v_ids[0] = bm_log_vert_id_get(log, v[0]); - lf->v_ids[1] = bm_log_vert_id_get(log, v[1]); - lf->v_ids[2] = bm_log_vert_id_get(log, v[2]); - - lf->hflag = f->head.hflag; - return lf; -} - -/************************ Helpers for undo/redo ***********************/ - -static void bm_log_verts_unmake(BMesh *bm, BMLog *log, GHash *verts) -{ - const int cd_vert_mask_offset = CustomData_get_offset(&bm->vdata, CD_PAINT_MASK); - - GHashIterator gh_iter; - GHASH_ITER (gh_iter, verts) { - void *key = BLI_ghashIterator_getKey(&gh_iter); - BMLogVert *lv = BLI_ghashIterator_getValue(&gh_iter); - uint id = POINTER_AS_UINT(key); - BMVert *v = bm_log_vert_from_id(log, id); - - /* Ensure the log has the final values of the vertex before - * deleting it */ - bm_log_vert_bmvert_copy(lv, v, cd_vert_mask_offset); - - BM_vert_kill(bm, v); - } -} - -static void bm_log_faces_unmake(BMesh *bm, BMLog *log, GHash *faces) -{ - GHashIterator gh_iter; - GHASH_ITER (gh_iter, faces) { - void *key = BLI_ghashIterator_getKey(&gh_iter); - uint id = POINTER_AS_UINT(key); - BMFace *f = bm_log_face_from_id(log, id); - BMEdge *e_tri[3]; - BMLoop *l_iter; - int i; - - l_iter = BM_FACE_FIRST_LOOP(f); - for (i = 0; i < 3; i++, l_iter = l_iter->next) { - e_tri[i] = l_iter->e; - } - - /* Remove any unused edges */ - BM_face_kill(bm, f); - for (i = 0; i < 3; i++) { - if (BM_edge_is_wire(e_tri[i])) { - BM_edge_kill(bm, e_tri[i]); - } - } - } -} - -static void bm_log_verts_restore(BMesh *bm, BMLog *log, GHash *verts) -{ - const int cd_vert_mask_offset = CustomData_get_offset(&bm->vdata, CD_PAINT_MASK); - - GHashIterator gh_iter; - GHASH_ITER (gh_iter, verts) { - void *key = BLI_ghashIterator_getKey(&gh_iter); - BMLogVert *lv = BLI_ghashIterator_getValue(&gh_iter); - BMVert *v = BM_vert_create(bm, lv->co, NULL, BM_CREATE_NOP); - vert_mask_set(v, lv->mask, cd_vert_mask_offset); - v->head.hflag = lv->hflag; - copy_v3_v3(v->no, lv->no); - bm_log_vert_id_set(log, v, POINTER_AS_UINT(key)); - } -} - -static void bm_log_faces_restore(BMesh *bm, BMLog *log, GHash *faces) -{ - GHashIterator gh_iter; - const int cd_face_sets = CustomData_get_offset_named( - &bm->pdata, CD_PROP_INT32, ".sculpt_face_set"); - - GHASH_ITER (gh_iter, faces) { - void *key = BLI_ghashIterator_getKey(&gh_iter); - BMLogFace *lf = BLI_ghashIterator_getValue(&gh_iter); - BMVert *v[3] = { - bm_log_vert_from_id(log, lf->v_ids[0]), - bm_log_vert_from_id(log, lf->v_ids[1]), - bm_log_vert_from_id(log, lf->v_ids[2]), - }; - BMFace *f; - - f = BM_face_create_verts(bm, v, 3, NULL, BM_CREATE_NOP, true); - f->head.hflag = lf->hflag; - bm_log_face_id_set(log, f, POINTER_AS_UINT(key)); - - /* Ensure face sets have valid values. Fixes T80174. */ - if (cd_face_sets != -1) { - BM_ELEM_CD_SET_INT(f, cd_face_sets, 1); - } - } -} - -static void bm_log_vert_values_swap(BMesh *bm, BMLog *log, GHash *verts) -{ - const int cd_vert_mask_offset = CustomData_get_offset(&bm->vdata, CD_PAINT_MASK); - - GHashIterator gh_iter; - GHASH_ITER (gh_iter, verts) { - void *key = BLI_ghashIterator_getKey(&gh_iter); - BMLogVert *lv = BLI_ghashIterator_getValue(&gh_iter); - uint id = POINTER_AS_UINT(key); - BMVert *v = bm_log_vert_from_id(log, id); - float mask; - - swap_v3_v3(v->co, lv->co); - swap_v3_v3(v->no, lv->no); - SWAP(char, v->head.hflag, lv->hflag); - mask = lv->mask; - lv->mask = vert_mask_get(v, cd_vert_mask_offset); - vert_mask_set(v, mask, cd_vert_mask_offset); - } -} - -static void bm_log_face_values_swap(BMLog *log, GHash *faces) -{ - GHashIterator gh_iter; - GHASH_ITER (gh_iter, faces) { - void *key = BLI_ghashIterator_getKey(&gh_iter); - BMLogFace *lf = BLI_ghashIterator_getValue(&gh_iter); - uint id = POINTER_AS_UINT(key); - BMFace *f = bm_log_face_from_id(log, id); - - SWAP(char, f->head.hflag, lf->hflag); - } -} - -/**********************************************************************/ - -/* Assign unique IDs to all vertices and faces already in the BMesh */ -static void bm_log_assign_ids(BMesh *bm, BMLog *log) -{ - BMIter iter; - BMVert *v; - BMFace *f; - - /* Generate vertex IDs */ - BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) { - uint id = range_tree_uint_take_any(log->unused_ids); - bm_log_vert_id_set(log, v, id); - } - - /* Generate face IDs */ - BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { - uint id = range_tree_uint_take_any(log->unused_ids); - bm_log_face_id_set(log, f, id); - } -} - -/* Allocate an empty log entry */ -static BMLogEntry *bm_log_entry_create(void) -{ - BMLogEntry *entry = MEM_callocN(sizeof(BMLogEntry), __func__); - - entry->deleted_verts = BLI_ghash_new(logkey_hash, logkey_cmp, __func__); - entry->deleted_faces = BLI_ghash_new(logkey_hash, logkey_cmp, __func__); - entry->added_verts = BLI_ghash_new(logkey_hash, logkey_cmp, __func__); - entry->added_faces = BLI_ghash_new(logkey_hash, logkey_cmp, __func__); - entry->modified_verts = BLI_ghash_new(logkey_hash, logkey_cmp, __func__); - entry->modified_faces = BLI_ghash_new(logkey_hash, logkey_cmp, __func__); - - entry->pool_verts = BLI_mempool_create(sizeof(BMLogVert), 0, 64, BLI_MEMPOOL_NOP); - entry->pool_faces = BLI_mempool_create(sizeof(BMLogFace), 0, 64, BLI_MEMPOOL_NOP); - - return entry; -} - -/* Free the data in a log entry - * - * NOTE: does not free the log entry itself. */ -static void bm_log_entry_free(BMLogEntry *entry) -{ - BLI_ghash_free(entry->deleted_verts, NULL, NULL); - BLI_ghash_free(entry->deleted_faces, NULL, NULL); - BLI_ghash_free(entry->added_verts, NULL, NULL); - BLI_ghash_free(entry->added_faces, NULL, NULL); - BLI_ghash_free(entry->modified_verts, NULL, NULL); - BLI_ghash_free(entry->modified_faces, NULL, NULL); - - BLI_mempool_destroy(entry->pool_verts); - BLI_mempool_destroy(entry->pool_faces); -} - -static void bm_log_id_ghash_retake(RangeTreeUInt *unused_ids, GHash *id_ghash) -{ - GHashIterator gh_iter; - - GHASH_ITER (gh_iter, id_ghash) { - void *key = BLI_ghashIterator_getKey(&gh_iter); - uint id = POINTER_AS_UINT(key); - - range_tree_uint_retake(unused_ids, id); - } -} - -static int uint_compare(const void *a_v, const void *b_v) -{ - const uint *a = a_v; - const uint *b = b_v; - return (*a) < (*b); -} - -/* Remap IDs to contiguous indices - * - * E.g. if the vertex IDs are (4, 1, 10, 3), the mapping will be: - * 4 -> 2 - * 1 -> 0 - * 10 -> 3 - * 3 -> 1 - */ -static GHash *bm_log_compress_ids_to_indices(uint *ids, uint totid) -{ - GHash *map = BLI_ghash_int_new_ex(__func__, totid); - uint i; - - qsort(ids, totid, sizeof(*ids), uint_compare); - - for (i = 0; i < totid; i++) { - void *key = POINTER_FROM_UINT(ids[i]); - void *val = POINTER_FROM_UINT(i); - BLI_ghash_insert(map, key, val); - } - - return map; -} - -/* Release all ID keys in id_ghash */ -static void bm_log_id_ghash_release(BMLog *log, GHash *id_ghash) -{ - GHashIterator gh_iter; - - GHASH_ITER (gh_iter, id_ghash) { - void *key = BLI_ghashIterator_getKey(&gh_iter); - uint id = POINTER_AS_UINT(key); - range_tree_uint_release(log->unused_ids, id); - } -} - -/***************************** Public API *****************************/ - -BMLog *BM_log_create(BMesh *bm) -{ - BMLog *log = MEM_callocN(sizeof(*log), __func__); - const uint reserve_num = (uint)(bm->totvert + bm->totface); - - log->unused_ids = range_tree_uint_alloc(0, (uint)-1); - log->id_to_elem = BLI_ghash_new_ex(logkey_hash, logkey_cmp, __func__, reserve_num); - log->elem_to_id = BLI_ghash_ptr_new_ex(__func__, reserve_num); - - /* Assign IDs to all existing vertices and faces */ - bm_log_assign_ids(bm, log); - - return log; -} - -void BM_log_cleanup_entry(BMLogEntry *entry) -{ - BMLog *log = entry->log; - - if (log) { - /* Take all used IDs */ - bm_log_id_ghash_retake(log->unused_ids, entry->deleted_verts); - bm_log_id_ghash_retake(log->unused_ids, entry->deleted_faces); - bm_log_id_ghash_retake(log->unused_ids, entry->added_verts); - bm_log_id_ghash_retake(log->unused_ids, entry->added_faces); - bm_log_id_ghash_retake(log->unused_ids, entry->modified_verts); - bm_log_id_ghash_retake(log->unused_ids, entry->modified_faces); - - /* delete entries to avoid releasing ids in node cleanup */ - BLI_ghash_clear(entry->deleted_verts, NULL, NULL); - BLI_ghash_clear(entry->deleted_faces, NULL, NULL); - BLI_ghash_clear(entry->added_verts, NULL, NULL); - BLI_ghash_clear(entry->added_faces, NULL, NULL); - BLI_ghash_clear(entry->modified_verts, NULL, NULL); - } -} - -BMLog *BM_log_from_existing_entries_create(BMesh *bm, BMLogEntry *entry) -{ - BMLog *log = BM_log_create(bm); - - if (entry->prev) { - log->current_entry = entry; - } - else { - log->current_entry = NULL; - } - - /* Let BMLog manage the entry list again */ - log->entries.first = log->entries.last = entry; - - { - while (entry->prev) { - entry = entry->prev; - log->entries.first = entry; - } - entry = log->entries.last; - while (entry->next) { - entry = entry->next; - log->entries.last = entry; - } - } - - for (entry = log->entries.first; entry; entry = entry->next) { - entry->log = log; - - /* Take all used IDs */ - bm_log_id_ghash_retake(log->unused_ids, entry->deleted_verts); - bm_log_id_ghash_retake(log->unused_ids, entry->deleted_faces); - bm_log_id_ghash_retake(log->unused_ids, entry->added_verts); - bm_log_id_ghash_retake(log->unused_ids, entry->added_faces); - bm_log_id_ghash_retake(log->unused_ids, entry->modified_verts); - bm_log_id_ghash_retake(log->unused_ids, entry->modified_faces); - } - - return log; -} - -void BM_log_free(BMLog *log) -{ - BMLogEntry *entry; - - if (log->unused_ids) { - range_tree_uint_free(log->unused_ids); - } - - if (log->id_to_elem) { - BLI_ghash_free(log->id_to_elem, NULL, NULL); - } - - if (log->elem_to_id) { - BLI_ghash_free(log->elem_to_id, NULL, NULL); - } - - /* Clear the BMLog references within each entry, but do not free - * the entries themselves */ - for (entry = log->entries.first; entry; entry = entry->next) { - entry->log = NULL; - } - - MEM_freeN(log); -} - -int BM_log_length(const BMLog *log) -{ - return BLI_listbase_count(&log->entries); -} - -void BM_log_mesh_elems_reorder(BMesh *bm, BMLog *log) -{ - uint *varr; - uint *farr; - - GHash *id_to_idx; - - BMIter bm_iter; - BMVert *v; - BMFace *f; - - uint i; - - /* Put all vertex IDs into an array */ - varr = MEM_mallocN(sizeof(int) * (size_t)bm->totvert, __func__); - BM_ITER_MESH_INDEX (v, &bm_iter, bm, BM_VERTS_OF_MESH, i) { - varr[i] = bm_log_vert_id_get(log, v); - } - - /* Put all face IDs into an array */ - farr = MEM_mallocN(sizeof(int) * (size_t)bm->totface, __func__); - BM_ITER_MESH_INDEX (f, &bm_iter, bm, BM_FACES_OF_MESH, i) { - farr[i] = bm_log_face_id_get(log, f); - } - - /* Create BMVert index remap array */ - id_to_idx = bm_log_compress_ids_to_indices(varr, (uint)bm->totvert); - BM_ITER_MESH_INDEX (v, &bm_iter, bm, BM_VERTS_OF_MESH, i) { - const uint id = bm_log_vert_id_get(log, v); - const void *key = POINTER_FROM_UINT(id); - const void *val = BLI_ghash_lookup(id_to_idx, key); - varr[i] = POINTER_AS_UINT(val); - } - BLI_ghash_free(id_to_idx, NULL, NULL); - - /* Create BMFace index remap array */ - id_to_idx = bm_log_compress_ids_to_indices(farr, (uint)bm->totface); - BM_ITER_MESH_INDEX (f, &bm_iter, bm, BM_FACES_OF_MESH, i) { - const uint id = bm_log_face_id_get(log, f); - const void *key = POINTER_FROM_UINT(id); - const void *val = BLI_ghash_lookup(id_to_idx, key); - farr[i] = POINTER_AS_UINT(val); - } - BLI_ghash_free(id_to_idx, NULL, NULL); - - BM_mesh_remap(bm, varr, NULL, farr); - - MEM_freeN(varr); - MEM_freeN(farr); -} - -BMLogEntry *BM_log_entry_add(BMLog *log) -{ - /* WARNING: this is now handled by the UndoSystem: BKE_UNDOSYS_TYPE_SCULPT - * freeing here causes unnecessary complications. */ - BMLogEntry *entry; -#if 0 - /* Delete any entries after the current one */ - entry = log->current_entry; - if (entry) { - BMLogEntry *next; - for (entry = entry->next; entry; entry = next) { - next = entry->next; - bm_log_entry_free(entry); - BLI_freelinkN(&log->entries, entry); - } - } -#endif - - /* Create and append the new entry */ - entry = bm_log_entry_create(); - BLI_addtail(&log->entries, entry); - entry->log = log; - log->current_entry = entry; - - return entry; -} - -void BM_log_entry_drop(BMLogEntry *entry) -{ - BMLog *log = entry->log; - - if (!log) { - /* Unlink */ - BLI_assert(!(entry->prev && entry->next)); - if (entry->prev) { - entry->prev->next = NULL; - } - else if (entry->next) { - entry->next->prev = NULL; - } - - bm_log_entry_free(entry); - MEM_freeN(entry); - return; - } - - if (!entry->prev) { - /* Release IDs of elements that are deleted by this - * entry. Since the entry is at the beginning of the undo - * stack, and it's being deleted, those elements can never be - * restored. Their IDs can go back into the pool. */ - - /* This would never happen usually since first entry of log is - * usually dyntopo enable, which, when reverted will free the log - * completely. However, it is possible have a stroke instead of - * dyntopo enable as first entry if nodes have been cleaned up - * after sculpting on a different object than A, B. - * - * The steps are: - * A dyntopo enable - sculpt - * B dyntopo enable - sculpt - undo (A objects operators get cleaned up) - * A sculpt (now A's log has a sculpt operator as first entry) - * - * Causing a cleanup at this point will call the code below, however - * this will invalidate the state of the log since the deleted vertices - * have been reclaimed already on step 2 (see BM_log_cleanup_entry) - * - * Also, design wise, a first entry should not have any deleted vertices since it - * should not have anything to delete them -from- - */ - // bm_log_id_ghash_release(log, entry->deleted_faces); - // bm_log_id_ghash_release(log, entry->deleted_verts); - } - else if (!entry->next) { - /* Release IDs of elements that are added by this entry. Since - * the entry is at the end of the undo stack, and it's being - * deleted, those elements can never be restored. Their IDs - * can go back into the pool. */ - bm_log_id_ghash_release(log, entry->added_faces); - bm_log_id_ghash_release(log, entry->added_verts); - } - else { - BLI_assert_msg(0, "Cannot drop BMLogEntry from middle"); - } - - if (log->current_entry == entry) { - log->current_entry = entry->prev; - } - - bm_log_entry_free(entry); - BLI_freelinkN(&log->entries, entry); -} - -void BM_log_undo(BMesh *bm, BMLog *log) -{ - BMLogEntry *entry = log->current_entry; - - if (entry) { - log->current_entry = entry->prev; - - /* Delete added faces and verts */ - bm_log_faces_unmake(bm, log, entry->added_faces); - bm_log_verts_unmake(bm, log, entry->added_verts); - - /* Restore deleted verts and faces */ - bm_log_verts_restore(bm, log, entry->deleted_verts); - bm_log_faces_restore(bm, log, entry->deleted_faces); - - /* Restore vertex coordinates, mask, and hflag */ - bm_log_vert_values_swap(bm, log, entry->modified_verts); - bm_log_face_values_swap(log, entry->modified_faces); - } -} - -void BM_log_redo(BMesh *bm, BMLog *log) -{ - BMLogEntry *entry = log->current_entry; - - if (!entry) { - /* Currently at the beginning of the undo stack, move to first entry */ - entry = log->entries.first; - } - else if (entry->next) { - /* Move to next undo entry */ - entry = entry->next; - } - else { - /* Currently at the end of the undo stack, nothing left to redo */ - return; - } - - log->current_entry = entry; - - if (entry) { - /* Re-delete previously deleted faces and verts */ - bm_log_faces_unmake(bm, log, entry->deleted_faces); - bm_log_verts_unmake(bm, log, entry->deleted_verts); - - /* Restore previously added verts and faces */ - bm_log_verts_restore(bm, log, entry->added_verts); - bm_log_faces_restore(bm, log, entry->added_faces); - - /* Restore vertex coordinates, mask, and hflag */ - bm_log_vert_values_swap(bm, log, entry->modified_verts); - bm_log_face_values_swap(log, entry->modified_faces); - } -} - -void BM_log_vert_before_modified(BMLog *log, BMVert *v, const int cd_vert_mask_offset) -{ - BMLogEntry *entry = log->current_entry; - BMLogVert *lv; - uint v_id = bm_log_vert_id_get(log, v); - void *key = POINTER_FROM_UINT(v_id); - void **val_p; - - /* Find or create the BMLogVert entry */ - if ((lv = BLI_ghash_lookup(entry->added_verts, key))) { - bm_log_vert_bmvert_copy(lv, v, cd_vert_mask_offset); - } - else if (!BLI_ghash_ensure_p(entry->modified_verts, key, &val_p)) { - lv = bm_log_vert_alloc(log, v, cd_vert_mask_offset); - *val_p = lv; - } -} - -void BM_log_vert_added(BMLog *log, BMVert *v, const int cd_vert_mask_offset) -{ - BMLogVert *lv; - uint v_id = range_tree_uint_take_any(log->unused_ids); - void *key = POINTER_FROM_UINT(v_id); - - bm_log_vert_id_set(log, v, v_id); - lv = bm_log_vert_alloc(log, v, cd_vert_mask_offset); - BLI_ghash_insert(log->current_entry->added_verts, key, lv); -} - -void BM_log_face_modified(BMLog *log, BMFace *f) -{ - BMLogFace *lf; - uint f_id = bm_log_face_id_get(log, f); - void *key = POINTER_FROM_UINT(f_id); - - lf = bm_log_face_alloc(log, f); - BLI_ghash_insert(log->current_entry->modified_faces, key, lf); -} - -void BM_log_face_added(BMLog *log, BMFace *f) -{ - BMLogFace *lf; - uint f_id = range_tree_uint_take_any(log->unused_ids); - void *key = POINTER_FROM_UINT(f_id); - - /* Only triangles are supported for now */ - BLI_assert(f->len == 3); - - bm_log_face_id_set(log, f, f_id); - lf = bm_log_face_alloc(log, f); - BLI_ghash_insert(log->current_entry->added_faces, key, lf); -} - -void BM_log_vert_removed(BMLog *log, BMVert *v, const int cd_vert_mask_offset) -{ - BMLogEntry *entry = log->current_entry; - uint v_id = bm_log_vert_id_get(log, v); - void *key = POINTER_FROM_UINT(v_id); - - /* if it has a key, it shouldn't be NULL */ - BLI_assert(!!BLI_ghash_lookup(entry->added_verts, key) == - !!BLI_ghash_haskey(entry->added_verts, key)); - - if (BLI_ghash_remove(entry->added_verts, key, NULL, NULL)) { - range_tree_uint_release(log->unused_ids, v_id); - } - else { - BMLogVert *lv, *lv_mod; - - lv = bm_log_vert_alloc(log, v, cd_vert_mask_offset); - BLI_ghash_insert(entry->deleted_verts, key, lv); - - /* If the vertex was modified before deletion, ensure that the - * original vertex values are stored */ - if ((lv_mod = BLI_ghash_lookup(entry->modified_verts, key))) { - (*lv) = (*lv_mod); - BLI_ghash_remove(entry->modified_verts, key, NULL, NULL); - } - } -} - -void BM_log_face_removed(BMLog *log, BMFace *f) -{ - BMLogEntry *entry = log->current_entry; - uint f_id = bm_log_face_id_get(log, f); - void *key = POINTER_FROM_UINT(f_id); - - /* if it has a key, it shouldn't be NULL */ - BLI_assert(!!BLI_ghash_lookup(entry->added_faces, key) == - !!BLI_ghash_haskey(entry->added_faces, key)); - - if (BLI_ghash_remove(entry->added_faces, key, NULL, NULL)) { - range_tree_uint_release(log->unused_ids, f_id); - } - else { - BMLogFace *lf; - - lf = bm_log_face_alloc(log, f); - BLI_ghash_insert(entry->deleted_faces, key, lf); - } -} - -void BM_log_all_added(BMesh *bm, BMLog *log) -{ - const int cd_vert_mask_offset = CustomData_get_offset(&bm->vdata, CD_PAINT_MASK); - BMIter bm_iter; - BMVert *v; - BMFace *f; - - /* avoid unnecessary resizing on initialization */ - if (BLI_ghash_len(log->current_entry->added_verts) == 0) { - BLI_ghash_reserve(log->current_entry->added_verts, (uint)bm->totvert); - } - - if (BLI_ghash_len(log->current_entry->added_faces) == 0) { - BLI_ghash_reserve(log->current_entry->added_faces, (uint)bm->totface); - } - - /* Log all vertices as newly created */ - BM_ITER_MESH (v, &bm_iter, bm, BM_VERTS_OF_MESH) { - BM_log_vert_added(log, v, cd_vert_mask_offset); - } - - /* Log all faces as newly created */ - BM_ITER_MESH (f, &bm_iter, bm, BM_FACES_OF_MESH) { - BM_log_face_added(log, f); - } -} - -void BM_log_before_all_removed(BMesh *bm, BMLog *log) -{ - const int cd_vert_mask_offset = CustomData_get_offset(&bm->vdata, CD_PAINT_MASK); - BMIter bm_iter; - BMVert *v; - BMFace *f; - - /* Log deletion of all faces */ - BM_ITER_MESH (f, &bm_iter, bm, BM_FACES_OF_MESH) { - BM_log_face_removed(log, f); - } - - /* Log deletion of all vertices */ - BM_ITER_MESH (v, &bm_iter, bm, BM_VERTS_OF_MESH) { - BM_log_vert_removed(log, v, cd_vert_mask_offset); - } -} - -const float *BM_log_original_vert_co(BMLog *log, BMVert *v) -{ - BMLogEntry *entry = log->current_entry; - const BMLogVert *lv; - uint v_id = bm_log_vert_id_get(log, v); - void *key = POINTER_FROM_UINT(v_id); - - BLI_assert(entry); - - BLI_assert(BLI_ghash_haskey(entry->modified_verts, key)); - - lv = BLI_ghash_lookup(entry->modified_verts, key); - return lv->co; -} - -const float *BM_log_original_vert_no(BMLog *log, BMVert *v) -{ - BMLogEntry *entry = log->current_entry; - const BMLogVert *lv; - uint v_id = bm_log_vert_id_get(log, v); - void *key = POINTER_FROM_UINT(v_id); - - BLI_assert(entry); - - BLI_assert(BLI_ghash_haskey(entry->modified_verts, key)); - - lv = BLI_ghash_lookup(entry->modified_verts, key); - return lv->no; -} - -float BM_log_original_mask(BMLog *log, BMVert *v) -{ - BMLogEntry *entry = log->current_entry; - const BMLogVert *lv; - uint v_id = bm_log_vert_id_get(log, v); - void *key = POINTER_FROM_UINT(v_id); - - BLI_assert(entry); - - BLI_assert(BLI_ghash_haskey(entry->modified_verts, key)); - - lv = BLI_ghash_lookup(entry->modified_verts, key); - return lv->mask; -} - -void BM_log_original_vert_data(BMLog *log, BMVert *v, const float **r_co, const float **r_no) -{ - BMLogEntry *entry = log->current_entry; - const BMLogVert *lv; - uint v_id = bm_log_vert_id_get(log, v); - void *key = POINTER_FROM_UINT(v_id); - - BLI_assert(entry); - - BLI_assert(BLI_ghash_haskey(entry->modified_verts, key)); - - lv = BLI_ghash_lookup(entry->modified_verts, key); - *r_co = lv->co; - *r_no = lv->no; -} - -/************************ Debugging and Testing ***********************/ - -BMLogEntry *BM_log_current_entry(BMLog *log) -{ - return log->current_entry; -} - -RangeTreeUInt *BM_log_unused_ids(BMLog *log) -{ - return log->unused_ids; -} - -#if 0 -/* Print the list of entries, marking the current one - * - * Keep around for debugging */ -void bm_log_print(const BMLog *log, const char *description) -{ - const BMLogEntry *entry; - const char *current = " <-- current"; - int i; - - printf("%s:\n", description); - printf(" % 2d: [ initial ]%s\n", 0, (!log->current_entry) ? current : ""); - for (entry = log->entries.first, i = 1; entry; entry = entry->next, i++) { - printf(" % 2d: [%p]%s\n", i, entry, (entry == log->current_entry) ? current : ""); - } -} -#endif - -void BM_log_print_entry(BMesh *bm, BMLogEntry *entry) -{ - if (bm) { - printf("BM { totvert=%d totedge=%d totloop=%d totpoly=%d\n", - bm->totvert, - bm->totedge, - bm->totloop, - bm->totface); - - if (!bm->totvert) { - printf("%s: Warning: empty bmesh\n", __func__); - } - } - else { - printf("BM { totvert=unknown totedge=unknown totloop=unknown totpoly=unknown\n"); - } - - printf("v | added: %d, removed: %d, modified: %d\n", - (int)BLI_ghash_len(entry->added_verts), - (int)BLI_ghash_len(entry->deleted_verts), - (int)BLI_ghash_len(entry->modified_verts)); - printf("f | added: %d, removed: %d, modified: %d\n", - (int)BLI_ghash_len(entry->added_faces), - (int)BLI_ghash_len(entry->deleted_faces), - (int)BLI_ghash_len(entry->modified_faces)); - printf("}\n"); -} diff --git a/source/blender/bmesh/intern/bmesh_log.cc b/source/blender/bmesh/intern/bmesh_log.cc new file mode 100644 index 00000000000..e98aea0080e --- /dev/null +++ b/source/blender/bmesh/intern/bmesh_log.cc @@ -0,0 +1,1637 @@ +#include "MEM_guardedalloc.h" + +#include "BKE_customdata.h" +#include "BKE_mesh.h" + +#include "DNA_customdata_types.h" +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" + +#include "BLI_hash.hh" +#include "BLI_listbase_wrapper.hh" +#include "BLI_map.hh" +#include "BLI_math.h" +#include "BLI_math_vector_types.hh" +#include "BLI_mempool.h" +#include "BLI_set.hh" +#include "BLI_vector.hh" + +#include "bmesh.h" +#include "bmesh_idmap.h" +#include "bmesh_log_intern.h" +extern "C" { +#include "bmesh_structure.h" +} + +#include +#include +#include +#include +#include +#include + +#ifdef DO_LOG_PRINT +static int msg_idgen = 1; +static char msg_buffer[256] = {0}; + +# define SET_MSG(le) memcpy(le->msg, msg_buffer, sizeof(le->msg)) +# define GET_MSG(le) (le)->msg +# ifdef DEBUG_LOG_CALL_STACKS +# define LOGPRINT(entry, ...) \ + fprintf(DEBUG_FILE, "%d: %s: ", entry->id, bm_logstack_head()); \ + fprintf(DEBUG_FILE, __VA_ARGS__) +# else +# define LOGPRINT(entry, ...) \ + fprintf(DEBUG_FILE, "%s: ", __func__); \ + fprintf(DEBUG_FILE, __VA_ARGS__) +# endif +struct Mesh; +#else +# define GET_MSG(le) le ? "" : " " +# define SET_MSG(le) +# define LOGPRINT(...) +#endif + +extern "C" void bm_log_message(const char *fmt, ...) +{ + char msg[64]; + + va_list args; + va_start(args, fmt); + vsnprintf(msg, sizeof(msg), fmt, args); + va_end(args); + +#ifdef DO_LOG_PRINT + BLI_snprintf(msg_buffer, 64, "%d %s", msg_idgen, msg); + msg_idgen++; + + fprintf(DEBUG_FILE, "%s\n", msg); +#endif +} + +/* Avoid C++ runtime type ids. */ +enum BMLogSetType { LOG_SET_DIFF, LOG_SET_FULL }; + +namespace blender { + +template struct BMID { + int id = -1; + + BMID(int _id) : id(_id) + { + } + + T *lookup(BMIdMap *idmap) + { + return reinterpret_cast(BM_idmap_lookup(idmap, id)); + } + + uint64_t hash() const + { + return (uint64_t)id; + } + + bool operator==(const BMID &b) const + { + return id == b.id; + } +}; + +template struct BMLogElem { + BMID id = BMID(-1); + void *customdata = nullptr; + char flag = 0; +}; + +template struct LogElemAlloc { + BLI_mempool *pool; + + LogElemAlloc() + { + /* We need an iterable pool to call individual destructors in ~LogElemAlloc(). */ + pool = BLI_mempool_create(sizeof(T), 0, 256, BLI_MEMPOOL_ALLOW_ITER); + } + + LogElemAlloc(const LogElemAlloc &b) = delete; + + LogElemAlloc(LogElemAlloc &&b) + { + pool = b.pool; + b.pool = nullptr; + } + + int calc_size() + { + return BLI_mempool_get_size(pool); + } + + void operator=(LogElemAlloc &&b) + { + pool = b.pool; + b.pool = nullptr; + } + + ~LogElemAlloc() + { + if (pool) { + BLI_mempool_iter iter; + BLI_mempool_iternew(pool, &iter); + while (void *entry = BLI_mempool_iterstep(&iter)) { + T *ptr = static_cast(entry); + ptr->~T(); + } + + BLI_mempool_destroy(pool); + } + } + + T *alloc() + { + void *mem = BLI_mempool_alloc(pool); + return new (mem) T(); + } + + void free(T *elem) + { + elem->~T(); + + BLI_mempool_free(pool, static_cast(elem)); + } +}; + +struct BMLogVert : public BMLogElem { + float3 co; + float3 no; +}; + +struct BMLogEdge : public BMLogElem { + BMID v1 = BMID(-1); + BMID v2 = BMID(-1); +}; + +struct BMLogFace : public BMLogElem { + Vector, 5> verts; + Vector loop_customdata; + // int material_index; +}; + +struct BMLogEntry; + +struct BMLogSetBase { + BMLogSetType type; + BMLogEntry *entry = nullptr; /* Parent entry */ + + BMLogSetBase(BMLogEntry *_entry, BMLogSetType _type) : type(_type), entry(_entry) + { + } + + virtual ~BMLogSetBase() + { + } + + virtual void undo(BMesh *bm, BMLogCallbacks *callbacks) + { + } + virtual void redo(BMesh *bm, BMLogCallbacks *callbacks) + { + } +}; + +struct BMLogSetDiff : public BMLogSetBase { + BMLogSetDiff(BMLogEntry *entry) : BMLogSetBase(entry, LOG_SET_DIFF) + { + } + + Map, BMLogVert *> modified_verts; + Map, BMLogEdge *> modified_edges; + Map, BMLogFace *> modified_faces; + + Map, BMLogVert *> removed_verts; + Map, BMLogEdge *> removed_edges; + Map, BMLogFace *> removed_faces; + + Map, BMLogVert *> added_verts; + Map, BMLogEdge *> added_edges; + Map, BMLogFace *> added_faces; + + void add_vert(BMesh *bm, BMVert *v); + void remove_vert(BMesh *bm, BMVert *v); + void modify_vert(BMesh *bm, BMVert *v); + void add_edge(BMesh *bm, BMEdge *e); + void remove_edge(BMesh *bm, BMEdge *e); + void modify_edge(BMesh *bm, BMEdge *e); + void add_face(BMesh *bm, BMFace *f); + void remove_face(BMesh *bm, BMFace *f, bool no_check = false); + void modify_face(BMesh *bm, BMFace *f); + + void undo(BMesh *bm, BMLogCallbacks *callbacks) override; + void redo(BMesh *bm, BMLogCallbacks *callbacks) override; + + void restore_verts(BMesh *bm, + blender::Map, BMLogVert *> verts, + BMLogCallbacks *callbacks); + void remove_verts(BMesh *bm, + blender::Map, BMLogVert *> verts, + BMLogCallbacks *callbacks); + void swap_verts(BMesh *bm, + blender::Map, BMLogVert *> verts, + BMLogCallbacks *callbacks); + void restore_edges(BMesh *bm, + blender::Map, BMLogEdge *> edges, + BMLogCallbacks *callbacks); + void remove_edges(BMesh *bm, + blender::Map, BMLogEdge *> edges, + BMLogCallbacks *callbacks); + void swap_edges(BMesh *bm, + blender::Map, BMLogEdge *> edges, + BMLogCallbacks *callbacks); + + void restore_faces(BMesh *bm, + blender::Map, BMLogFace *> faces, + BMLogCallbacks *callbacks); + void remove_faces(BMesh *bm, + blender::Map, BMLogFace *> faces, + BMLogCallbacks *callbacks); + void swap_faces(BMesh *bm, + blender::Map, BMLogFace *> faces, + BMLogCallbacks *callbacks); +}; + +struct BMLogSetFull : public BMLogSetBase { + BMLogSetFull(BMesh *bm, BMLogEntry *entry) : BMLogSetBase(entry, LOG_SET_FULL) + { + BMeshToMeshParams params = {}; + params.update_shapekey_indices = false; + params.calc_object_remap = false; + params.copy_temp_cdlayers = true; + params.ignore_mesh_id_layers = false; + + mesh = BKE_mesh_from_bmesh_nomain(bm, ¶ms, nullptr); + } + + void ATTR_NO_OPT swap(BMesh *bm) + { + CustomData_MeshMasks cd_mask_extra = {CD_MASK_DYNTOPO_VERT | CD_MASK_SHAPEKEY, 0, 0, 0, 0}; + + BMeshToMeshParams params = {}; + params.update_shapekey_indices = false; + params.calc_object_remap = false; + params.copy_temp_cdlayers = true; + params.ignore_mesh_id_layers = false; + + Mesh *current_mesh = BKE_mesh_from_bmesh_nomain(bm, ¶ms, nullptr); + + int shapenr = bm->shapenr; + BMeshFromMeshParams params2 = {}; + params2.copy_temp_cdlayers = true; + params2.cd_mask_extra = cd_mask_extra; + params2.calc_face_normal = params2.add_key_index = params2.use_shapekey = + params2.ignore_id_layers = false; + + BM_mesh_clear(bm); + BM_mesh_bm_from_me(nullptr, + bm, + mesh, // note we stored shapekeys as customdata layers, + // that's why the shapekey params are false + ¶ms2); + + bm->shapenr = shapenr; + + bm->elem_index_dirty |= BM_VERT | BM_EDGE | BM_FACE; + bm->elem_table_dirty |= BM_VERT | BM_EDGE | BM_FACE; + + BM_mesh_elem_table_ensure(bm, BM_VERT | BM_EDGE | BM_FACE); + BM_mesh_elem_index_ensure(bm, BM_VERT | BM_EDGE | BM_FACE); + + BKE_mesh_free_data_for_undo(mesh); + mesh = current_mesh; + } + + void undo(BMesh *bm, BMLogCallbacks *callbacks) override + { + swap(bm); + } + + void redo(BMesh *bm, BMLogCallbacks *callbacks) override + { + swap(bm); + } + + Mesh *mesh = nullptr; +}; + +static const char *get_elem_htype_str(int htype) +{ + switch (htype) { + case BM_VERT: + return "vertex"; + case BM_EDGE: + return "edge"; + case BM_LOOP: + return "loop"; + case BM_FACE: + return "face"; + default: + return "unknown type"; + } +} + +template constexpr char get_elem_type() +{ + if constexpr (std::is_same_v) { + return BM_VERT; + } + else if constexpr (std::is_same_v) { + return BM_EDGE; + } + else if constexpr (std::is_same_v) { + return BM_LOOP; + } + else if constexpr (std::is_same_v) { + return BM_FACE; + } +} + +struct BMLogEntry { + struct BMLogEntry *next = nullptr, *prev = nullptr; + + Vector sets; + LogElemAlloc vpool; + LogElemAlloc epool; + LogElemAlloc fpool; + + CustomData vdata, edata, ldata, pdata; + BMIdMap *idmap = nullptr; + + BMLog *log = nullptr; + + ATTR_NO_OPT BMLogEntry(BMIdMap *_idmap, + CustomData *src_vdata, + CustomData *src_edata, + CustomData *src_ldata, + CustomData *src_pdata) + : idmap(_idmap) + { + CustomData_copy_all_layout(src_vdata, &vdata); + CustomData_copy_all_layout(src_edata, &edata); + CustomData_copy_all_layout(src_ldata, &ldata); + CustomData_copy_all_layout(src_pdata, &pdata); + + CustomData_bmesh_init_pool(&vdata, 0, BM_VERT); + CustomData_bmesh_init_pool(&edata, 0, BM_EDGE); + CustomData_bmesh_init_pool(&ldata, 0, BM_LOOP); + CustomData_bmesh_init_pool(&pdata, 0, BM_FACE); + } + + ATTR_NO_OPT ~BMLogEntry() + { + for (BMLogSetBase *set : sets) { + switch (set->type) { + case LOG_SET_DIFF: + delete static_cast(set); + break; + case LOG_SET_FULL: + delete static_cast(set); + break; + } + } + + if (vdata.pool) { + BLI_mempool_destroy(vdata.pool); + } + if (edata.pool) { + BLI_mempool_destroy(edata.pool); + } + if (ldata.pool) { + BLI_mempool_destroy(ldata.pool); + } + if (pdata.pool) { + BLI_mempool_destroy(pdata.pool); + } + + CustomData_free(&vdata, 0); + CustomData_free(&edata, 0); + CustomData_free(&ldata, 0); + CustomData_free(&pdata, 0); + } + + template T *get_elem_from_id(BMesh *bm, BMID id) + { +#ifdef USE_NEW_IDMAP + return reinterpret_cast(BM_idmap_lookup(idmap, id.id)); +#else + auto *idmap = &bm->idmap; + + if (idmap->flag & BM_NO_REUSE_IDS) { + return reinterpret_cast(BLI_ghash_lookup(bm->idmap.ghash, POINTER_FROM_INT(id.id))); + } + else if (id.id < 0 || id.id >= idmap->map_size || !idmap->map[id.id] || + idmap->map[id.id]->head.htype != get_elem_type()) { + printf("%s: invalid ID %d!\n", __func__, id.id); + return nullptr; + } + + return reinterpret_cast(idmap->map[id.id]); +#endif + } + + template void assign_elem_id(BMesh *bm, T *elem, BMID _id, bool check_unique) + { + int id = _id.id; + +#ifdef USE_NEW_IDMAP + if (check_unique) { + BMElem *old; + old = BM_idmap_lookup(idmap, id); + + if (old && old != (BMElem *)elem) { + printf("id conflict in bm_assign_id; elem %p (a %s) is being reassinged to id %d.\n", + elem, + get_elem_htype_str((int)elem->head.htype), + (int)id); + printf( + " elem %p (a %s) will get a new id\n", old, get_elem_htype_str((int)old->head.htype)); + + BM_idmap_assign(idmap, reinterpret_cast(elem), id); + return; + } + } + + BM_idmap_assign(idmap, reinterpret_cast(elem), id); +#else + BMElem *bmelem = reinterpret_cast(elem); + bm_assign_id(bm, bmelem, (uint)id, check_unique); +#endif + } + + template BMID get_elem_id(BMesh *bm, T *elem) + { +#ifdef USE_NEW_IDMAP + BM_idmap_check_assign(idmap, reinterpret_cast(elem)); + return BM_idmap_get_id(idmap, reinterpret_cast(elem)); +#else + return BM_ELEM_GET_ID(bm, elem); +#endif + } + + ATTR_NO_OPT void push_set(BMesh *bm, BMLogSetType type) + { + switch (type) { + case LOG_SET_DIFF: + sets.append(static_cast(new BMLogSetDiff(this))); + break; + case LOG_SET_FULL: + sets.append(static_cast(new BMLogSetFull(bm, this))); + break; + } + } + + BMLogSetDiff *current_diff_set(BMesh *bm) + { + if (sets.size() == 0 || sets[sets.size() - 1]->type != LOG_SET_DIFF) { + push_set(bm, LOG_SET_DIFF); + } + + return static_cast(sets[sets.size() - 1]); + } + + ATTR_NO_OPT void update_logvert(BMesh *bm, BMVert *v, BMLogVert *lv) + { + if (vdata.pool) { + CustomData_bmesh_copy_data(&bm->vdata, &vdata, v->head.data, &lv->customdata); + } + + lv->co = v->co; + lv->no = v->no; + lv->flag = v->head.hflag; + } + + ATTR_NO_OPT void swap_logvert(BMesh *bm, BMID id, BMVert *v, BMLogVert *lv) + { + if (v->head.data && lv->customdata) { + CustomData_bmesh_swap_data(&vdata, &bm->vdata, lv->customdata, &v->head.data); + } + + std::swap(v->head.hflag, lv->flag); + swap_v3_v3(v->co, lv->co); + swap_v3_v3(v->no, lv->no); + } + + ATTR_NO_OPT void swap_logedge(BMesh *bm, BMID id, BMEdge *e, BMLogEdge *le) + { + if (e->head.data && le->customdata) { + CustomData_bmesh_swap_data(&edata, &bm->edata, le->customdata, &e->head.data); + } + } + + ATTR_NO_OPT void swap_logface(BMesh *bm, BMID id, BMFace *f, BMLogFace *lf) + { + if (f->head.data && lf->customdata) { + CustomData_bmesh_swap_data(&pdata, &bm->pdata, lf->customdata, &f->head.data); + } + + if (f->len != lf->verts.size()) { + printf("%s: error: wrong length for face, was %d, should be %d\n", + __func__, + f->len, + (int)lf->verts.size()); + return; + } + + if (lf->loop_customdata[0]) { + BMLoop *l = f->l_first; + + int i = 0; + do { + CustomData_bmesh_swap_data(&ldata, &bm->ldata, lf->loop_customdata[i], &l->head.data); + i++; + } while ((l = l->next) != f->l_first); + } + std::swap(f->head.hflag, lf->flag); + } + + ATTR_NO_OPT BMLogVert *alloc_logvert(BMesh *bm, BMVert *v) + { + BMID id = get_elem_id(bm, v); + BMLogVert *lv = vpool.alloc(); + + lv->id = id; + + update_logvert(bm, v, lv); + + return lv; + } + + ATTR_NO_OPT void free_logvert(BMLogVert *lv) + { + if (lv->customdata) { + BLI_mempool_free(vdata.pool, lv->customdata); + } + + vpool.free(lv); + } + + ATTR_NO_OPT void load_vert(BMesh *bm, BMVert *v, BMLogVert *lv) + { + if (v->head.data && lv->customdata) { + CustomData_bmesh_copy_data(&vdata, &bm->vdata, lv->customdata, &v->head.data); + } + + v->head.hflag = lv->flag; + copy_v3_v3(v->co, lv->co); + copy_v3_v3(v->no, lv->no); + } + + BMLogEdge *alloc_logedge(BMesh *bm, BMEdge *e) + { + BMLogEdge *le = epool.alloc(); + + CustomData_bmesh_copy_data(&bm->edata, &edata, e->head.data, &le->customdata); + le->id = get_elem_id(bm, e); + le->v1 = get_elem_id(bm, e->v1); + le->v2 = get_elem_id(bm, e->v2); + + return le; + } + + void update_logedge(BMesh *bm, BMEdge *e, BMLogEdge *le) + { + le->flag = e->head.hflag; + CustomData_bmesh_copy_data(&bm->edata, &edata, e->head.data, &le->customdata); + } + + void free_logedge(BMesh *bm, BMLogEdge *e) + { + epool.free(e); + } + + BMLogFace *alloc_logface(BMesh *bm, BMFace *f) + { + BMLogFace *lf = fpool.alloc(); + + lf->id = get_elem_id(bm, f); + lf->flag = f->head.hflag; + CustomData_bmesh_copy_data(&bm->pdata, &pdata, f->head.data, &lf->customdata); + + BMLoop *l = f->l_first; + do { + lf->verts.append(get_elem_id(bm, l->v)); + void *loop_customdata = nullptr; + + if (l->head.data) { + CustomData_bmesh_copy_data(&bm->ldata, &ldata, l->head.data, &loop_customdata); + } + + lf->loop_customdata.append(loop_customdata); + } while ((l = l->next) != f->l_first); + + return lf; + } + + void update_logface(BMesh *bm, BMLogFace *lf, BMFace *f) + { + lf->flag = f->head.hflag; + CustomData_bmesh_copy_data(&bm->pdata, &pdata, f->head.data, &lf->customdata); + + if (f->len != lf->verts.size()) { + printf("%s: error: face length changed.\n", __func__); + return; + } + + BMLoop *l = f->l_first; + int i = 0; + do { + void *loop_customdata = nullptr; + + if (l->head.data) { + CustomData_bmesh_copy_data(&bm->ldata, &ldata, l->head.data, &loop_customdata); + } + + lf->loop_customdata[i++] = loop_customdata; + } while ((l = l->next) != f->l_first); + } + + void free_logface(BMesh *bm, BMLogFace *lf) + { + if (lf->loop_customdata[0]) { + for (int i = 0; i < lf->verts.size(); i++) { + BLI_mempool_free(ldata.pool, lf->loop_customdata[i]); + } + } + + if (lf->customdata) { + BLI_mempool_free(pdata.pool, lf->customdata); + } + + fpool.free(lf); + } + + void add_vert(BMesh *bm, BMVert *v) + { + current_diff_set(bm)->add_vert(bm, v); + } + + void remove_vert(BMesh *bm, BMVert *v) + { + current_diff_set(bm)->remove_vert(bm, v); + } + + void modify_vert(BMesh *bm, BMVert *v) + { + current_diff_set(bm)->modify_vert(bm, v); + } + + void add_edge(BMesh *bm, BMEdge *e) + { + current_diff_set(bm)->add_edge(bm, e); + } + + void remove_edge(BMesh *bm, BMEdge *e) + { + current_diff_set(bm)->remove_edge(bm, e); + } + + void modify_edge(BMesh *bm, BMEdge *e) + { + current_diff_set(bm)->modify_edge(bm, e); + } + + void add_face(BMesh *bm, BMFace *f) + { + current_diff_set(bm)->add_face(bm, f); + } + void remove_face(BMesh *bm, BMFace *f, bool no_check = false) + { + current_diff_set(bm)->remove_face(bm, f, no_check); + } + void modify_face(BMesh *bm, BMFace *f) + { + current_diff_set(bm)->modify_face(bm, f); + } + + ATTR_NO_OPT void undo(BMesh *bm, BMLogCallbacks *callbacks) + { + for (int i = sets.size() - 1; i >= 0; i--) { + //printf(" - %d of %d\n", i, (int)(sets.size() - 1)); + sets[i]->undo(bm, callbacks); + } + } + + ATTR_NO_OPT void redo(BMesh *bm, BMLogCallbacks *callbacks) + { + for (int i = 0; i < sets.size(); i++) { + sets[i]->redo(bm, callbacks); + } + } + + int calc_size() + { + int ret = 0; + + ret += vdata.pool ? (int)BLI_mempool_get_size(vdata.pool) : 0; + ret += edata.pool ? (int)BLI_mempool_get_size(edata.pool) : 0; + ret += ldata.pool ? (int)BLI_mempool_get_size(ldata.pool) : 0; + ret += pdata.pool ? (int)BLI_mempool_get_size(pdata.pool) : 0; + + ret += vpool.calc_size(); + ret += epool.calc_size(); + ret += fpool.calc_size(); + + return ret; + } +}; + +struct BMLog { + BMIdMap *idmap = nullptr; + BMLogEntry *current_entry = nullptr; + BMLogEntry *first_entry = nullptr; + int refcount = 1; + bool dead = false; + + BMLog(BMIdMap *_idmap) : idmap(_idmap) + { + } + + ~BMLog() + { + } + + ATTR_NO_OPT void set_idmap(BMIdMap *idmap) + { + BMLogEntry *entry = first_entry; + while (entry) { + entry->idmap = idmap; + entry = entry->next; + } + } + + ATTR_NO_OPT bool free_all_entries() + { + BMLogEntry *entry = first_entry; + + if (!entry) { + return false; + } + + while (entry) { + BMLogEntry *next = entry->next; + + MEM_delete(entry); + entry = next; + } + + return true; + } + + ATTR_NO_OPT BMLogEntry *push_entry(BMesh *bm) + { + BMLogEntry *entry = MEM_new( + "BMLogEntry", idmap, &bm->vdata, &bm->edata, &bm->ldata, &bm->pdata); + + /* Truncate undo list. */ + BMLogEntry *entry2 = current_entry ? current_entry->next : nullptr; + while (entry2) { + BMLogEntry *next = entry2->next; + MEM_delete(entry2); + + entry2 = next; + } + + entry->prev = current_entry; + + if (!first_entry) { + first_entry = entry; + } + else { + current_entry->next = entry; + } + + current_entry = entry; + return entry; + } + + void load_entries(BMLogEntry *entry) + { + first_entry = current_entry = entry; + while (first_entry->prev) { + first_entry = first_entry->prev; + } + + entry = first_entry; + while (entry) { + entry->log = this; + entry->idmap = idmap; + entry = entry->next; + } + } + + ATTR_NO_OPT void ensure_entry(BMesh *bm) + { + if (!current_entry) { + push_entry(bm); + } + } + + void add_vert(BMesh *bm, BMVert *v) + { + ensure_entry(bm); + current_entry->add_vert(bm, v); + } + + void remove_vert(BMesh *bm, BMVert *v) + { + ensure_entry(bm); + current_entry->remove_vert(bm, v); + } + + void modify_vert(BMesh *bm, BMVert *v) + { + ensure_entry(bm); + current_entry->modify_vert(bm, v); + } + + void add_edge(BMesh *bm, BMEdge *e) + { + ensure_entry(bm); + current_entry->add_edge(bm, e); + } + + void remove_edge(BMesh *bm, BMEdge *e) + { + ensure_entry(bm); + current_entry->remove_edge(bm, e); + } + + void modify_edge(BMesh *bm, BMEdge *e) + { + ensure_entry(bm); + current_entry->modify_edge(bm, e); + } + + void add_face(BMesh *bm, BMFace *f) + { + ensure_entry(bm); + current_entry->add_face(bm, f); + } + + void remove_face(BMesh *bm, BMFace *f, bool no_check = false) + { + ensure_entry(bm); + current_entry->remove_face(bm, f, no_check); + } + + void modify_face(BMesh *bm, BMFace *f) + { + ensure_entry(bm); + current_entry->modify_face(bm, f); + } + + void full_mesh(BMesh *bm) + { + ensure_entry(bm); + current_entry->push_set(bm, LOG_SET_FULL); + } + + void skip(int dir) + { + if (current_entry) { + current_entry = dir > 0 ? current_entry->next : current_entry->prev; + } + } + + void undo(BMesh *bm, BMLogCallbacks *callbacks) + { + current_entry->undo(bm, callbacks); + current_entry = current_entry->prev; + } + + void redo(BMesh *bm, BMLogCallbacks *callbacks) + { + current_entry = current_entry->next; + if (current_entry) { + current_entry->redo(bm, callbacks); + } + } +}; + +void BMLogSetDiff::add_vert(BMesh *bm, BMVert *v) +{ + BMID id = entry->get_elem_id(bm, v); + + BMLogVert **modified_lv = modified_verts.lookup_ptr(id); + if (modified_lv) { + modified_verts.remove(id); + entry->free_logvert(*modified_lv); + } + + if (added_verts.contains(id)) { + return; + } + + added_verts.add(id, entry->alloc_logvert(bm, v)); +} + +void BMLogSetDiff::remove_vert(BMesh *bm, BMVert *v) +{ + BMID id = entry->get_elem_id(bm, v); + + BMLogVert **added_lv = added_verts.lookup_ptr(id); + if (added_lv) { + added_verts.remove(id); + entry->free_logvert(*added_lv); + return; + } + + if (removed_verts.contains(id)) { + return; + } + + BMLogVert *lv; + BMLogVert **modified_lv = modified_verts.lookup_ptr(id); + if (modified_lv) { + modified_verts.remove(id); + lv = *modified_lv; + } + else { + lv = entry->alloc_logvert(bm, v); + } + + removed_verts.add(id, lv); +} + +void BMLogSetDiff::modify_vert(BMesh *bm, BMVert *v) +{ + BMID id = entry->get_elem_id(bm, v); + if (modified_verts.contains(id)) { + return; + } + + modified_verts.add(id, entry->alloc_logvert(bm, v)); +} + +void BMLogSetDiff::add_edge(BMesh *bm, BMEdge *e) +{ + BMID id = entry->get_elem_id(bm, e); + BMLogEdge *le; + + BMLogEdge **modified_le = modified_edges.lookup_ptr(id); + if (modified_le) { + le = *modified_le; + modified_edges.remove(id); + } + else { + le = entry->alloc_logedge(bm, e); + } + + added_edges.add_or_modify( + id, [&](BMLogEdge **le_out) { *le_out = le; }, [&](BMLogEdge **le_out) { *le_out = le; }); +} + +void BMLogSetDiff::remove_edge(BMesh *bm, BMEdge *e) +{ + BMID id = entry->get_elem_id(bm, e); + + if (added_edges.remove(id) || removed_edges.contains(id)) { + return; + } + + BMLogEdge *le; + BMLogEdge **modified_le = modified_edges.lookup_ptr(id); + if (modified_le) { + le = *modified_le; + modified_edges.remove(id); + } + else { + le = entry->alloc_logedge(bm, e); + } + + removed_edges.add(id, le); +} + +void BMLogSetDiff::modify_edge(BMesh *bm, BMEdge *e) +{ +} + +void BMLogSetDiff::add_face(BMesh *bm, BMFace *f) +{ +#ifdef USE_NEW_IDMAP + BM_idmap_check_assign(entry->idmap, (BMElem *)f); +#endif + + BMID id = entry->get_elem_id(bm, f); + + if (added_faces.contains(id)) { + return; + } + + BMLogFace *lf; + + if (BMLogFace **ptr = modified_faces.lookup_ptr(id)) { + lf = *ptr; + modified_faces.remove(id); + entry->update_logface(bm, lf, f); + } + else { + lf = entry->alloc_logface(bm, f); + } + + added_faces.add(id, lf); +} + +ATTR_NO_OPT void BMLogSetDiff::remove_face(BMesh *bm, BMFace *f, bool no_check) +{ + BMID id = entry->get_elem_id(bm, f); + + if (!no_check && (added_faces.remove(id) || removed_faces.contains(id))) { + return; + } + + BMLogFace *lf; + + if (BMLogFace **ptr = modified_faces.lookup_ptr(id)) { + lf = *ptr; + modified_faces.remove(id); + entry->update_logface(bm, lf, f); + } + else { + lf = entry->alloc_logface(bm, f); + } + + removed_faces.add(id, lf); +} + +void BMLogSetDiff::modify_face(BMesh *bm, BMFace *f) +{ + BMID id = entry->get_elem_id(bm, f); + + BMLogFace *lf; + + if (BMLogFace **ptr = modified_faces.lookup_ptr(id)) { + lf = *ptr; + entry->update_logface(bm, lf, f); + } + else { + lf = entry->alloc_logface(bm, f); + modified_faces.add(id, lf); + } +} + +ATTR_NO_OPT void BMLogSetDiff::swap_verts(BMesh *bm, + blender::Map, BMLogVert *> verts, + BMLogCallbacks *callbacks) +{ + void *old_customdata = entry->vdata.pool ? BLI_mempool_alloc(bm->vdata.pool) : nullptr; + +#ifdef USE_NEW_IDMAP + const int cd_id = entry->idmap->cd_id_off[BM_VERT]; +#else + const int cd_id = bm->idmap.cd_id_off[BM_VERT]; +#endif + + for (BMLogVert *lv : verts.values()) { + BMVert *v = entry->get_elem_from_id(bm, lv->id); + + if (!v) { + printf("modified_verts: invalid vertex %d\n", lv->id.id); + continue; + } + + if (old_customdata) { + memcpy(old_customdata, v->head.data, bm->vdata.totsize); + } + + entry->swap_logvert(bm, lv->id, v, lv); + + /* Ensure id wasn't mangled in customdata swap. */ + BM_ELEM_CD_SET_INT(v, cd_id, lv->id.id); + + if (callbacks) { + callbacks->on_vert_change(v, callbacks->userdata, old_customdata); + } + } + + if (old_customdata) { + BLI_mempool_free(bm->vdata.pool, old_customdata); + } +} + +ATTR_NO_OPT void BMLogSetDiff::restore_verts(BMesh *bm, + blender::Map, BMLogVert *> verts, + BMLogCallbacks *callbacks) +{ + for (BMLogVert *lv : verts.values()) { + BMVert *v = BM_vert_create(bm, lv->co, nullptr, BM_CREATE_SKIP_ID); + + v->head.hflag = lv->flag; + copy_v3_v3(v->no, lv->no); + + CustomData_bmesh_copy_data(&entry->vdata, &bm->vdata, lv->customdata, &v->head.data); + entry->assign_elem_id(bm, v, lv->id, true); + + if (callbacks && callbacks->on_vert_add) { + callbacks->on_vert_add(v, callbacks->userdata); + } + } + + bm->elem_index_dirty |= BM_VERT | BM_EDGE; + bm->elem_table_dirty |= BM_VERT | BM_EDGE; +} + +ATTR_NO_OPT void BMLogSetDiff::remove_verts(BMesh *bm, + blender::Map, BMLogVert *> verts, + BMLogCallbacks *callbacks) +{ + for (BMLogVert *lv : verts.values()) { + BMVert *v = entry->get_elem_from_id(bm, lv->id); + + if (!v) { + printf("%s: Failed to find vertex %d\b", __func__, lv->id.id); + continue; + } + + if (callbacks && callbacks->on_vert_kill) { + callbacks->on_vert_kill(v, callbacks->userdata); + } + +#ifdef USE_NEW_IDMAP + BM_idmap_release(entry->idmap, (BMElem *)v, false); +#endif + BM_vert_kill(bm, v); + } + + bm->elem_index_dirty |= BM_VERT | BM_EDGE; + bm->elem_table_dirty |= BM_VERT | BM_EDGE; +} + +void BMLogSetDiff::restore_edges(BMesh *bm, + blender::Map, BMLogEdge *> edges, + BMLogCallbacks *callbacks) +{ + for (BMLogEdge *le : edges.values()) { + BMVert *v1 = entry->get_elem_from_id(bm, le->v1); + BMVert *v2 = entry->get_elem_from_id(bm, le->v2); + + if (!v1) { + printf("%s: missing vertex v1 %d\n", __func__, le->v1.id); + continue; + } + + if (!v2) { + printf("%s: missing vertex v2 %d\n", __func__, le->v2.id); + continue; + } + + BMEdge *e = BM_edge_create(bm, v1, v2, nullptr, BM_CREATE_SKIP_ID); + CustomData_bmesh_copy_data(&entry->edata, &bm->edata, le->customdata, &e->head.data); + + entry->assign_elem_id(bm, e, le->id, true); + + if (callbacks->on_edge_add) { + callbacks->on_edge_add(e, callbacks->userdata); + } + } +} + +void BMLogSetDiff::remove_edges(BMesh *bm, + blender::Map, BMLogEdge *> edges, + BMLogCallbacks *callbacks) +{ + for (BMLogEdge *le : edges.values()) { + BMEdge *e = entry->get_elem_from_id(bm, le->id); + + if (!e) { + printf("%s: failed to find edge %d\n", __func__, le->id.id); + continue; + } + + if (callbacks && callbacks->on_edge_kill) { + callbacks->on_edge_kill(e, callbacks->userdata); + } + +#ifdef USE_NEW_IDMAP + BM_idmap_release(entry->idmap, reinterpret_cast(e), true); +#endif + BM_edge_kill(bm, e); + } +} + +void BMLogSetDiff::swap_edges(BMesh *bm, + blender::Map, BMLogEdge *> edges, + BMLogCallbacks *callbacks) +{ + void *old_customdata = entry->edata.pool ? BLI_mempool_alloc(bm->edata.pool) : nullptr; + + for (BMLogEdge *le : edges.values()) { + BMEdge *e = entry->get_elem_from_id(bm, le->id); + + if (!e) { + printf("%s: failed to find edge %d\n", __func__, le->id.id); + continue; + } + + if (old_customdata) { + memcpy(old_customdata, e->head.data, bm->edata.totsize); + } + + entry->swap_logedge(bm, le->id, e, le); + + if (callbacks->on_edge_change) { + callbacks->on_edge_change(e, callbacks->userdata, old_customdata); + } + } + + if (old_customdata) { + BLI_mempool_free(bm->edata.pool, old_customdata); + } +} + +ATTR_NO_OPT void BMLogSetDiff::restore_faces(BMesh *bm, + blender::Map, BMLogFace *> faces, + BMLogCallbacks *callbacks) +{ + Vector verts; + + for (BMLogFace *lf : faces.values()) { + bool ok = true; + verts.clear(); + + for (BMID v_id : lf->verts) { + BMVert *v = entry->get_elem_from_id(bm, v_id); + + if (!v) { + printf("%s: Error looking up vertex %d\n", __func__, v_id.id); + ok = false; + continue; + } + + verts.append(v); + } + + if (!ok) { + continue; + } + + BMFace *f = BM_face_create_verts( + bm, verts.data(), verts.size(), nullptr, BM_CREATE_SKIP_ID, true); + + f->head.hflag = lf->flag; + CustomData_bmesh_copy_data(&entry->pdata, &bm->pdata, lf->customdata, &f->head.data); + entry->assign_elem_id(bm, f, lf->id, true); + + BMLoop *l = f->l_first; + int i = 0; + + if (lf->loop_customdata[0]) { + do { + CustomData_bmesh_copy_data( + &entry->ldata, &bm->ldata, lf->loop_customdata[i], &l->head.data); + i++; + } while ((l = l->next) != f->l_first); + } + + if (callbacks && callbacks->on_face_add) { + callbacks->on_face_add(f, callbacks->userdata); + } + } + + bm->elem_index_dirty |= BM_FACE; + bm->elem_table_dirty |= BM_FACE; +} + +ATTR_NO_OPT void BMLogSetDiff::remove_faces(BMesh *bm, + blender::Map, BMLogFace *> faces, + BMLogCallbacks *callbacks) +{ + for (BMLogFace *lf : faces.values()) { + BMFace *f = entry->get_elem_from_id(bm, lf->id); + + if (!f) { + printf("%s: error finding face %d\n", __func__, lf->id.id); + continue; + } + + if (callbacks && callbacks->on_face_kill) { + callbacks->on_face_kill(f, callbacks->userdata); + } + +#ifdef USE_NEW_IDMAP + BM_idmap_release(entry->idmap, reinterpret_cast(f), true); +#endif + BM_face_kill(bm, f); + } + + bm->elem_index_dirty |= BM_FACE; + bm->elem_table_dirty |= BM_FACE; +} + +ATTR_NO_OPT void BMLogSetDiff::swap_faces(BMesh *bm, + blender::Map, BMLogFace *> faces, + BMLogCallbacks *callbacks) +{ + void *old_customdata = entry->pdata.pool ? BLI_mempool_alloc(bm->pdata.pool) : nullptr; + +#ifdef USE_NEW_IDMAP + const int cd_id = entry->idmap->cd_id_off[BM_FACE]; +#else + const int cd_id = bm->idmap.cd_id_off[BM_FACE]; +#endif + + for (BMLogFace *lf : faces.values()) { + BMFace *f = entry->get_elem_from_id(bm, lf->id); + + if (!f) { + printf("modified_verts: invalid vertex %d\n", lf->id.id); + continue; + } + + if (old_customdata) { + memcpy(old_customdata, f->head.data, bm->pdata.totsize); + } + + entry->swap_logface(bm, lf->id, f, lf); + + /* Ensure id wasn't mangled in customdata swap. */ + BM_ELEM_CD_SET_INT(f, cd_id, lf->id.id); + + if (callbacks) { + callbacks->on_face_change(f, callbacks->userdata, old_customdata); + } + } + + if (old_customdata) { + BLI_mempool_free(bm->pdata.pool, old_customdata); + } +} + +ATTR_NO_OPT void BMLogSetDiff::undo(BMesh *bm, BMLogCallbacks *callbacks) +{ + remove_faces(bm, added_faces, callbacks); + remove_edges(bm, added_edges, callbacks); + remove_verts(bm, added_verts, callbacks); + + restore_verts(bm, removed_verts, callbacks); + restore_edges(bm, removed_edges, callbacks); + restore_faces(bm, removed_faces, callbacks); + + swap_faces(bm, modified_faces, callbacks); + swap_verts(bm, modified_verts, callbacks); +} + +ATTR_NO_OPT void BMLogSetDiff::redo(BMesh *bm, BMLogCallbacks *callbacks) +{ + remove_faces(bm, removed_faces, callbacks); + remove_edges(bm, removed_edges, callbacks); + remove_verts(bm, removed_verts, callbacks); + + restore_verts(bm, added_verts, callbacks); + restore_edges(bm, added_edges, callbacks); + restore_faces(bm, added_faces, callbacks); + + swap_faces(bm, modified_faces, callbacks); + swap_edges(bm, modified_edges, callbacks); + swap_verts(bm, modified_verts, callbacks); +} +} // namespace blender + +ATTR_NO_OPT BMLog *BM_log_from_existing_entries_create(BMesh *bm, + BMIdMap *idmap, + BMLogEntry *entry) +{ + BMLog *log = BM_log_create(bm, idmap); + log->load_entries(entry); + + return log; +} + +ATTR_NO_OPT BMLog *BM_log_create(BMesh *bm, BMIdMap *idmap) +{ + BMLog *log = MEM_new("BMLog", idmap); + + return log; +} + +ATTR_NO_OPT void BM_log_set_idmap(BMLog *log, struct BMIdMap *idmap) +{ + log->set_idmap(idmap); +} + +/* Free all the data in a BMLog including the log itself + * safe_mode means log->refcount will be checked, and if nonzero log will not be freed + */ +ATTR_NO_OPT static bool bm_log_free_direct(BMLog *log, bool safe_mode) +{ + if (safe_mode && log->refcount) { + return false; + } + + log->dead = true; + log->free_all_entries(); + + return true; +} + +// if true, make sure to call BM_log_free on the log +ATTR_NO_OPT bool BM_log_is_dead(BMLog *log) +{ + return log->dead; +} + +ATTR_NO_OPT bool BM_log_free(BMLog *log, bool safe_mode) +{ + if (log->dead) { + MEM_delete(log); + return true; + } + + if (bm_log_free_direct(log, safe_mode)) { + MEM_delete(log); + return true; + } + + return false; +} + +ATTR_NO_OPT BMLogEntry *BM_log_entry_add_ex(BMesh *bm, BMLog *log, bool combine_with_last) +{ + if (combine_with_last && log->current_entry) { + log->current_entry->push_set(bm, LOG_SET_DIFF); + } + else { + log->push_entry(bm); + } + + log->current_entry->push_set(bm, LOG_SET_DIFF); + return log->current_entry; +} + +ATTR_NO_OPT BMLogEntry *BM_log_entry_add(BMesh *bm, BMLog *log) +{ + log->push_entry(bm)->push_set(bm, LOG_SET_DIFF); + return log->current_entry; +} + +void BM_log_vert_added(BMesh *bm, BMLog *log, BMVert *v) +{ + log->add_vert(bm, v); +} + +void BM_log_vert_removed(BMesh *bm, BMLog *log, BMVert *v) +{ + log->remove_vert(bm, v); +} + +void BM_log_vert_before_modified(BMesh *bm, BMLog *log, BMVert *v) +{ + log->modify_vert(bm, v); +} + +BMLogEntry *BM_log_entry_check_customdata(BMesh *bm, BMLog *log) +{ + BMLogEntry *entry = log->current_entry; + + if (!entry) { + fprintf(stdout, "no current entry; creating...\n"); + fflush(stdout); + return BM_log_entry_add_ex(bm, log, false); + } + + CustomData *cd1[4] = {&bm->vdata, &bm->edata, &bm->ldata, &bm->pdata}; + CustomData *cd2[4] = {&entry->vdata, &entry->edata, &entry->ldata, &entry->pdata}; + + for (int i = 0; i < 4; i++) { + if (!CustomData_layout_is_same(cd1[i], cd2[i])) { + fprintf(stdout, "Customdata changed for undo\n"); + fflush(stdout); + return BM_log_entry_add_ex(bm, log, false); + } + } + + return entry; +} + +void BM_log_edge_added(BMesh *bm, BMLog *log, BMEdge *e) +{ + log->add_edge(bm, e); +} +void BM_log_edge_modified(BMesh *bm, BMLog *log, BMEdge *e) +{ + log->modify_edge(bm, e); +} +void BM_log_edge_removed(BMesh *bm, BMLog *log, BMEdge *e) +{ + log->remove_edge(bm, e); +} + +ATTR_NO_OPT void BM_log_face_added(BMesh *bm, BMLog *log, BMFace *f) +{ + log->add_face(bm, f); +} +ATTR_NO_OPT void BM_log_face_modified(BMesh *bm, BMLog *log, BMFace *f) +{ + log->modify_face(bm, f); +} +void BM_log_face_removed(BMesh *bm, BMLog *log, BMFace *f) +{ + log->remove_face(bm, f); +} +void BM_log_face_removed_no_check(BMesh *bm, BMLog *log, BMFace *f) +{ + log->remove_face(bm, f, true); +} + +void BM_log_full_mesh(BMesh *bm, BMLog *log) +{ + log->full_mesh(bm); +} + +BMVert *BM_log_id_vert_get(BMesh *bm, BMLog *log, uint id) +{ +#ifdef USE_NEW_IDMAP + return reinterpret_cast(BM_idmap_lookup(log->idmap, id)); +#else + return reinterpret_cast(BM_ELEM_FROM_ID(bm, id)); +#endif +} + +uint BM_log_vert_id_get(BMesh *bm, BMLog *log, BMVert *v) +{ +#ifdef USE_NEW_IDMAP + return BM_idmap_get_id(log->idmap, reinterpret_cast(v)); +#else + return BM_ELEM_GET_ID(bm, reinterpret_cast(v)); +#endif +} + +BMFace *BM_log_id_face_get(BMesh *bm, BMLog *log, uint id) +{ +#ifdef USE_NEW_IDMAP + return reinterpret_cast(BM_idmap_lookup(log->idmap, id)); +#else + return reinterpret_cast(BM_ELEM_FROM_ID(bm, id)); +#endif +} + +uint BM_log_face_id_get(BMesh *bm, BMLog *log, BMFace *f) +{ +#ifdef USE_NEW_IDMAP + return BM_idmap_get_id(log->idmap, reinterpret_cast(f)); +#else + return BM_ELEM_GET_ID(bm, reinterpret_cast(f)); +#endif +} + +int BM_log_entry_size(BMLogEntry *entry) +{ + return entry->calc_size(); +} + +void BM_log_undo(BMesh *bm, BMLog *log, BMLogCallbacks *callbacks) +{ + log->undo(bm, callbacks); +} + +void BM_log_redo(BMesh *bm, BMLog *log, BMLogCallbacks *callbacks) +{ + log->redo(bm, callbacks); +} + +void BM_log_undo_skip(BMesh *bm, BMLog *log) +{ + log->skip(-1); +} + +void BM_log_redo_skip(BMesh *bm, BMLog *log) +{ + log->skip(1); +} + +BMLogEntry *BM_log_entry_prev(BMLogEntry *entry) +{ + return entry->prev; +} + +BMLogEntry *BM_log_entry_next(BMLogEntry *entry) +{ + return entry->next; +} + +void BM_log_set_current_entry(BMLog *log, BMLogEntry *entry) +{ + log->current_entry = entry; +} + +bool BM_log_entry_drop(BMLogEntry *entry) +{ + if (entry->prev) { + entry->prev->next = entry->next; + } + if (entry->next) { + entry->next->prev = entry->prev; + } + if (entry->log && entry == entry->log->current_entry) { + entry->log->current_entry = entry->prev; + } + + MEM_delete(entry); + return true; +} diff --git a/source/blender/bmesh/intern/bmesh_log.h b/source/blender/bmesh/intern/bmesh_log.h index 329bb94e7b4..e69de29bb2d 100644 --- a/source/blender/bmesh/intern/bmesh_log.h +++ b/source/blender/bmesh/intern/bmesh_log.h @@ -1,218 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ - -#pragma once - -/** \file - * \ingroup bmesh - */ - -struct BMFace; -struct BMVert; -struct BMesh; -struct RangeTreeUInt; - -typedef struct BMLog BMLog; -typedef struct BMLogEntry BMLogEntry; - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * Allocate, initialize, and assign a new BMLog. - */ -BMLog *BM_log_create(BMesh *bm); - -/** - * Allocate and initialize a new #BMLog using existing #BMLogEntries - * - * The 'entry' should be the last entry in the #BMLog. Its `prev` pointer - * will be followed back to find the first entry. - * - * The unused IDs field of the log will be initialized by taking all - * keys from all GHashes in the log entry. - */ -BMLog *BM_log_from_existing_entries_create(BMesh *bm, BMLogEntry *entry); - -/** - * Free all the data in a BMLog including the log itself. - */ -void BM_log_free(BMLog *log); - -/** - * Get the number of log entries. - */ -int BM_log_length(const BMLog *log); - -/** Apply a consistent ordering to BMesh vertices and faces. */ -void BM_log_mesh_elems_reorder(BMesh *bm, BMLog *log); - -/** - * Start a new log entry and update the log entry list. - * - * If the log entry list is empty, or if the current log entry is the - * last entry, the new entry is simply appended to the end. - * - * Otherwise, the new entry is added after the current entry and all - * following entries are deleted. - * - * In either case, the new entry is set as the current log entry. - */ -BMLogEntry *BM_log_entry_add(BMLog *log); - -/** Mark all used ids as unused for this node */ -void BM_log_cleanup_entry(BMLogEntry *entry); - -/** - * Remove an entry from the log. - * - * Uses entry->log as the log. If the log is NULL, the entry will be - * free'd but not removed from any list, nor shall its IDs be released. - * - * This operation is only valid on the first and last entries in the - * log. Deleting from the middle will assert. - */ -void BM_log_entry_drop(BMLogEntry *entry); - -/** - * Undo one #BMLogEntry. - * - * Has no effect if there's nothing left to undo. - */ -void BM_log_undo(BMesh *bm, BMLog *log); - -/** - * Redo one #BMLogEntry. - * - * Has no effect if there's nothing left to redo. - */ -void BM_log_redo(BMesh *bm, BMLog *log); - -/** - * Log a vertex before it is modified. - * - * Before modifying vertex coordinates, masks, or hflags, call this - * function to log its current values. This is better than logging - * after the coordinates have been modified, because only those - * vertices that are modified need to have their original values - * stored. - * - * Handles two separate cases: - * - * If the vertex was added in the current log entry, update the - * vertex in the map of added vertices. - * - * If the vertex already existed prior to the current log entry, a - * separate key/value map of modified vertices is used (using the - * vertex's ID as the key). The values stored in that case are - * the vertex's original state so that an undo can restore the - * previous state. - * - * On undo, the current vertex state will be swapped with the stored - * state so that a subsequent redo operation will restore the newer - * vertex state. - */ -void BM_log_vert_before_modified(BMLog *log, struct BMVert *v, int cd_vert_mask_offset); - -/** - * Log a new vertex as added to the #BMesh. - * - * The new vertex gets a unique ID assigned. It is then added to a map - * of added vertices, with the key being its ID and the value - * containing everything needed to reconstruct that vertex. - */ -void BM_log_vert_added(BMLog *log, struct BMVert *v, int cd_vert_mask_offset); - -/** - * Log a face before it is modified. - * - * This is intended to handle only header flags and we always - * assume face has been added before. - */ -void BM_log_face_modified(BMLog *log, struct BMFace *f); - -/** - * Log a new face as added to the #BMesh. - * - * The new face gets a unique ID assigned. It is then added to a map - * of added faces, with the key being its ID and the value containing - * everything needed to reconstruct that face. - */ -void BM_log_face_added(BMLog *log, struct BMFace *f); - -/** - * Log a vertex as removed from the #BMesh. - * - * A couple things can happen here: - * - * If the vertex was added as part of the current log entry, then it's - * deleted and forgotten about entirely. Its unique ID is returned to - * the unused pool. - * - * If the vertex was already part of the #BMesh before the current log - * entry, it is added to a map of deleted vertices, with the key being - * its ID and the value containing everything needed to reconstruct - * that vertex. - * - * If there's a move record for the vertex, that's used as the - * vertices original location, then the move record is deleted. - */ -void BM_log_vert_removed(BMLog *log, struct BMVert *v, int cd_vert_mask_offset); - -/** - * Log a face as removed from the #BMesh. - * - * A couple things can happen here: - * - * If the face was added as part of the current log entry, then it's - * deleted and forgotten about entirely. Its unique ID is returned to - * the unused pool. - * - * If the face was already part of the #BMesh before the current log - * entry, it is added to a map of deleted faces, with the key being - * its ID and the value containing everything needed to reconstruct - * that face. - */ -void BM_log_face_removed(BMLog *log, struct BMFace *f); - -/** - * Log all vertices/faces in the #BMesh as added. - */ -void BM_log_all_added(BMesh *bm, BMLog *log); - -/** Log all vertices/faces in the #BMesh as removed. */ -void BM_log_before_all_removed(BMesh *bm, BMLog *log); - -/** - * Get the logged coordinates of a vertex. - * - * Does not modify the log or the vertex. - */ -const float *BM_log_original_vert_co(BMLog *log, BMVert *v); - -/** - * Get the logged normal of a vertex - * - * Does not modify the log or the vertex. - */ -const float *BM_log_original_vert_no(BMLog *log, BMVert *v); - -/** Get the logged mask of a vertex - * - * Does not modify the log or the vertex. - */ -float BM_log_original_mask(BMLog *log, BMVert *v); - -/** Get the logged data of a vertex (avoid multiple lookups). */ -void BM_log_original_vert_data(BMLog *log, BMVert *v, const float **r_co, const float **r_no); - -/** For internal use only (unit testing). */ -BMLogEntry *BM_log_current_entry(BMLog *log); -/** For internal use only (unit testing) */ -struct RangeTreeUInt *BM_log_unused_ids(BMLog *log); - -void BM_log_print_entry(BMesh *bm, BMLogEntry *entry); - -#ifdef __cplusplus -} -#endif diff --git a/source/blender/bmesh/intern/bmesh_log_intern.h b/source/blender/bmesh/intern/bmesh_log_intern.h new file mode 100644 index 00000000000..a571e77e129 --- /dev/null +++ b/source/blender/bmesh/intern/bmesh_log_intern.h @@ -0,0 +1,305 @@ +/* + * 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 bmesh + */ + +//#define BM_LOG_TRACE +#ifdef BM_LOG_TRACE +# define BMLOG_DEBUG_ARGS , const char *func, int line +# define BMLOG_DEBUG_ARGS_VALUES , func, line +# define BMLOG_DEBUG_ARGS_INVOKE , __func__, __LINE__ +#else +# define BMLOG_DEBUG_ARGS +# define BMLOG_DEBUG_ARGS_VALUES +# define BMLOG_DEBUG_ARGS_INVOKE +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +struct BMFace; +struct BMVert; +struct BMesh; +struct RangeTreeUInt; +struct BMIdMap; + +#ifdef __cplusplus +namespace blender { +struct BMLog; +struct BMLogEntry; +} // namespace blender + +using BMLog = blender::BMLog; +using BMLogEntry = blender::BMLogEntry; +#else +typedef struct BMLog BMLog; +typedef struct BMLogEntry BMLogEntry; +#endif + +typedef struct BMLogCallbacks { + void (*on_vert_add)(struct BMVert *v, void *userdata); + void (*on_vert_kill)(struct BMVert *v, void *userdata); + void (*on_vert_change)(struct BMVert *v, void *userdata, void *old_customdata); + + void (*on_edge_add)(struct BMEdge *e, void *userdata); + void (*on_edge_kill)(struct BMEdge *e, void *userdata); + void (*on_edge_change)(struct BMEdge *e, void *userdata, void *old_customdata); + + void (*on_face_add)(struct BMFace *f, void *userdata); + void (*on_face_kill)(struct BMFace *f, void *userdata); + void (*on_face_change)(struct BMFace *f, void *userdata, void *old_customdata); + + void (*on_full_mesh_load)(void *userdata); + void (*on_mesh_id_restore)(void *userdata); + void *userdata; +} BMLogCallbacks; + +//#define DEBUG_LOG_CALL_STACKS + +#ifdef DEBUG_LOG_CALL_STACKS +void _bm_logstack_pop(); +const char *_bm_logstack_head(); +void _bm_logstack_push(const char *name); +# define bm_logstack_push() _bm_logstack_push(__func__) +# define bm_logstack_pop() _bm_logstack_pop() +#else +# define bm_logstack_push() +# define bm_logstack_head "" +# define bm_logstack_pop() +#endif + +struct BMIdMap; + +/* Allocate and initialize a new BMLog */ +BMLog *BM_log_create(BMesh *bm, struct BMIdMap *idmap); + +/* Allocate and initialize a new BMLog using existing BMLogEntries */ +/* Allocate and initialize a new BMLog using existing BMLogEntries + * + * The 'entry' should be the last entry in the BMLog. Its prev pointer + * will be followed back to find the first entry. + * + * The unused IDs field of the log will be initialized by taking all + * keys from all GHashes in the log entry. + */ +BMLog *BM_log_from_existing_entries_create(BMesh *bm, struct BMIdMap *idmap, BMLogEntry *entry); + +/* Free all the data in a BMLog including the log itself */ +bool BM_log_free(BMLog *log, bool safe_mode); + +BMLog *BM_log_unfreeze(BMesh *bm, BMLogEntry *entry); + +void BM_log_set_bm(BMesh *bm, BMLog *log); + +/* Get the number of log entries */ +/* Get the number of log entries */ +int BM_log_length(const BMLog *log); + +/* Apply a consistent ordering to BMesh vertices and faces */ +/* Apply a consistent ordering to BMesh vertices */ +void BM_log_mesh_elems_reorder(BMesh *bm, BMLog *log); + +/* Start a new log entry and update the log entry list */ +BMLogEntry *BM_log_entry_add(BMesh *bm, BMLog *log); +BMLogEntry *BM_log_entry_add_ex(BMesh *bm, BMLog *log, bool combine_with_last); +BMLogEntry *BM_log_all_ids(BMesh *bm, BMLog *log, BMLogEntry *entry); + +BMLogEntry *BM_log_entry_check_customdata(BMesh *bm, BMLog *log); + +/* Mark all used ids as unused for this node */ +void BM_log_cleanup_entry(BMLogEntry *entry); + +/* Remove an entry from the log. + returns true if the log's refcount + reached zero and was freed*/ +bool BM_log_entry_drop(BMLogEntry *entry); +bool BM_log_is_dead(BMLog *log); + +/* Undo one BMLogEntry. */ +void BM_log_undo(BMesh *bm, BMLog *log, BMLogCallbacks *callbacks); + +/* Redo one BMLogEntry */ +void BM_log_redo(BMesh *bm, BMLog *log, BMLogCallbacks *callbacks); + +/* Log a vertex before it is modified */ +void BM_log_vert_before_modified(BMesh *bm, BMLog *log, BMVert *v); + +/* Log a vertex before it is modified + * + * Before modifying vertex coordinates, masks, or hflags, call this + * function to log its current values. This is better than logging + * after the coordinates have been modified, because only those + * vertices that are modified need to have their original values + * stored. + * + * Handles two separate cases: + * + * If the vertex was added in the current log entry, update the + * vertex in the map of added vertices. + * + * If the vertex already existed prior to the current log entry, a + * separate key/value map of modified vertices is used (using the + * vertex's ID as the key). The values stored in that case are + * the vertex's original state so that an undo can restore the + * previous state. + * + * On undo, the current vertex state will be swapped with the stored + * state so that a subsequent redo operation will restore the newer + * vertex state. + */ +void BM_log_edge_before_modified(BMLog *log, BMEdge *v, bool log_customdata); + +/* Log a new vertex as added to the BMesh */ +/* Log a new vertex as added to the BMesh + * + * The new vertex gets a unique ID assigned. It is then added to a map + * of added vertices, with the key being its ID and the value + * containing everything needed to reconstruct that vertex. + */ +void BM_log_vert_added(BMesh *bm, BMLog *log, struct BMVert *v); + +/* Log a new edge as added to the BMesh */ +void BM_log_edge_added(BMesh *bm, BMLog *log, BMEdge *e); + +/* Log a face before it is modified */ +/* Log a face before it is modified + * + * This is intended to handle only header flags and we always + * assume face has been added before + */ +void BM_log_face_modified(BMesh *bm, BMLog *log, struct BMFace *f); + +/* Log a new face as added to the BMesh */ +/* Log a new face as added to the BMesh + * + * The new face gets a unique ID assigned. It is then added to a map + * of added faces, with the key being its ID and the value containing + * everything needed to reconstruct that face. + */ +void BM_log_face_added(BMesh *bm, BMLog *log, struct BMFace *f); + +/* Log a vertex as removed from the BMesh */ +/* Log a vertex as removed from the BMesh + * + * A couple things can happen here: + * + * If the vertex was added as part of the current log entry, then it's + * deleted and forgotten about entirely. Its unique ID is returned to + * the unused pool. + * + * If the vertex was already part of the BMesh before the current log + * entry, it is added to a map of deleted vertices, with the key being + * its ID and the value containing everything needed to reconstruct + * that vertex. + * + * If there's a move record for the vertex, that's used as the + * vertices original location, then the move record is deleted. + */ +void BM_log_vert_removed(BMesh *bm, BMLog *log, struct BMVert *v); + +/* Log an edge as removed from the BMesh */ +void BM_log_edge_removed(BMesh *bm, BMLog *log, BMEdge *e); + +/* Log a face as removed from the BMesh */ +/* Log a face as removed from the BMesh + * + * A couple things can happen here: + * + * If the face was added as part of the current log entry, then it's + * deleted and forgotten about entirely. Its unique ID is returned to + * the unused pool. + * + * If the face was already part of the BMesh before the current log + * entry, it is added to a map of deleted faces, with the key being + * its ID and the value containing everything needed to reconstruct + * that face. + */ +void BM_log_face_removed(BMesh *bm, BMLog *log, struct BMFace *f); +void BM_log_face_removed_no_check(BMesh *bm, BMLog *log, struct BMFace *f); + +/* Log all vertices/faces in the BMesh as added */ +/* Log all vertices/faces in the BMesh as added */ +void BM_log_all_added(BMesh *bm, BMLog *log); + +void BM_log_full_mesh(BMesh *bm, BMLog *log); + +/* Log all vertices/faces in the BMesh as removed */ +/* Log all vertices/faces in the BMesh as removed */ +void BM_log_before_all_removed(BMesh *bm, BMLog *log); + +/* Get the logged coordinates of a vertex */ +/* Get the logged coordinates of a vertex + * + * Does not modify the log or the vertex */ +const float *BM_log_original_vert_co(BMLog *log, BMVert *v); + +/* Get the logged normal of a vertex + * + * Does not modify the log or the vertex */ +const float *BM_log_original_vert_no(BMLog *log, BMVert *v); + +/* Get the logged mask of a vertex */ +/* Get the logged mask of a vertex + * + * Does not modify the log or the vertex */ +float BM_log_original_mask(BMLog *log, BMVert *v); + +/* Get the logged data of a vertex (avoid multiple lookups) */ +void BM_log_original_vert_data(BMLog *log, BMVert *v, const float **r_co, const float **r_no); + +/* For internal use only (unit testing) */ +/* For internal use only (unit testing) */ +BMLogEntry *BM_log_current_entry(BMLog *log); +void BM_log_set_current_entry(BMLog *log, BMLogEntry *entry); +BMLogEntry *BM_log_entry_prev(BMLogEntry *entry); +BMLogEntry *BM_log_entry_next(BMLogEntry *entry); + +uint BM_log_vert_id_get(BMesh *bm, BMLog *log, BMVert *v); +BMVert *BM_log_id_vert_get(BMesh *bm, BMLog *log, uint id); +uint BM_log_face_id_get(BMesh *bm, BMLog *log, BMFace *f); +BMFace *BM_log_id_face_get(BMesh *bm, BMLog *log, uint id); + +void BM_log_print_entry(BMLog *log, BMLogEntry *entry); +void BM_log_redo_skip(BMesh *bm, BMLog *log); +void BM_log_undo_skip(BMesh *bm, BMLog *log); +BMVert *BM_log_edge_split_do(BMLog *log, BMEdge *e, BMVert *v, BMEdge **newe, float t); + +void BM_log_set_idmap(BMLog *log, struct BMIdMap *idmap); + +int BM_log_entry_size(BMLogEntry *entry); + +bool BM_log_has_vert(BMLog *log, BMVert *v); +bool BM_log_has_edge(BMLog *log, BMEdge *e); +bool BM_log_has_face(BMLog *log, BMFace *f); + +bool BM_log_has_vert_post(BMLog *log, BMVert *v); +bool BM_log_has_edge_post(BMLog *log, BMEdge *e); +bool BM_log_has_face_post(BMLog *log, BMFace *f); + +bool BM_log_has_vert_pre(BMLog *log, BMVert *v); +bool BM_log_has_edge_pre(BMLog *log, BMEdge *e); +bool BM_log_has_face_pre(BMLog *log, BMFace *f); + +bool BM_log_validate(BMesh *inbm, BMLogEntry *entry, bool is_applied); +bool BM_log_validate_cur(BMLog *log); +#ifdef __cplusplus +} +#endif diff --git a/source/blender/bmesh/intern/bmesh_marking.c b/source/blender/bmesh/intern/bmesh_marking.c index a1c2815ab2f..6ac33a3dc6e 100644 --- a/source/blender/bmesh/intern/bmesh_marking.c +++ b/source/blender/bmesh/intern/bmesh_marking.c @@ -152,13 +152,19 @@ static bool recount_totsels_are_ok(BMesh *bm) static bool bm_vert_is_edge_select_any_other(const BMVert *v, const BMEdge *e_first) { const BMEdge *e_iter = e_first; + int i = 0; /* start by stepping over the current edge */ - while ((e_iter = bmesh_disk_edge_next(e_iter, v)) != e_first) { + while ((e_iter = bmesh_disk_edge_next(e_iter, v)) != e_first && i++ < 1000) { if (BM_elem_flag_test(e_iter, BM_ELEM_SELECT)) { return true; } } + + if (i >= 1000) { + fprintf(stderr, "bmesh mesh error in %s\n", __func__); + } + return false; } diff --git a/source/blender/bmesh/intern/bmesh_mesh.cc b/source/blender/bmesh/intern/bmesh_mesh.cc index d8fe0c36108..ec60d65400f 100644 --- a/source/blender/bmesh/intern/bmesh_mesh.cc +++ b/source/blender/bmesh/intern/bmesh_mesh.cc @@ -11,18 +11,29 @@ #include "DNA_listBase.h" #include "DNA_scene_types.h" +#include "BLI_alloca.h" #include "BLI_listbase.h" #include "BLI_math.h" +#include "BLI_rand.h" #include "BLI_utildefines.h" +#include "BLI_vector.hh" #include "BKE_customdata.h" #include "BKE_mesh.h" +#include "DNA_meshdata_types.h" + #include "bmesh.h" +#include "bmesh_private.h" +#include "range_tree.h" + +using blender::Vector; const BMAllocTemplate bm_mesh_allocsize_default = {512, 1024, 2048, 512}; const BMAllocTemplate bm_mesh_chunksize_default = {512, 1024, 2048, 512}; +static void bm_alloc_toolflags(BMesh *bm); + static void bm_mempool_init_ex(const BMAllocTemplate *allocsize, const bool use_toolflags, BLI_mempool **r_vpool, @@ -55,7 +66,7 @@ static void bm_mempool_init_ex(const BMAllocTemplate *allocsize, } if (r_lpool) { *r_lpool = BLI_mempool_create( - loop_size, allocsize->totloop, bm_mesh_chunksize_default.totloop, BLI_MEMPOOL_NOP); + loop_size, allocsize->totloop, bm_mesh_chunksize_default.totloop, BLI_MEMPOOL_ALLOW_ITER); } if (r_fpool) { *r_fpool = BLI_mempool_create( @@ -76,6 +87,16 @@ void BM_mesh_elem_toolflags_ensure(BMesh *bm) { BLI_assert(bm->use_toolflags); + if (!CustomData_has_layer(&bm->vdata, CD_TOOLFLAGS)) { + if (bm->vtoolflagpool) { + printf("%s: Error: toolflags were deallocated improperly\n", __func__); + + BM_mesh_elem_toolflags_clear(bm); + + bm_alloc_toolflags_cdlayers(bm, true); + } + } + if (bm->vtoolflagpool && bm->etoolflagpool && bm->ftoolflagpool) { return; } @@ -84,30 +105,15 @@ void BM_mesh_elem_toolflags_ensure(BMesh *bm) bm->etoolflagpool = BLI_mempool_create(sizeof(BMFlagLayer), bm->totedge, 512, BLI_MEMPOOL_NOP); bm->ftoolflagpool = BLI_mempool_create(sizeof(BMFlagLayer), bm->totface, 512, BLI_MEMPOOL_NOP); - BMIter iter; - BMVert_OFlag *v_olfag; - BLI_mempool *toolflagpool = bm->vtoolflagpool; - BM_ITER_MESH (v_olfag, &iter, bm, BM_VERTS_OF_MESH) { - v_olfag->oflags = static_cast(BLI_mempool_calloc(toolflagpool)); - } - - BMEdge_OFlag *e_olfag; - toolflagpool = bm->etoolflagpool; - BM_ITER_MESH (e_olfag, &iter, bm, BM_EDGES_OF_MESH) { - e_olfag->oflags = static_cast(BLI_mempool_calloc(toolflagpool)); - } - - BMFace_OFlag *f_olfag; - toolflagpool = bm->ftoolflagpool; - BM_ITER_MESH (f_olfag, &iter, bm, BM_FACES_OF_MESH) { - f_olfag->oflags = static_cast(BLI_mempool_calloc(toolflagpool)); - } + bm_alloc_toolflags(bm); bm->totflags = 1; } void BM_mesh_elem_toolflags_clear(BMesh *bm) { + bool haveflags = bm->vtoolflagpool || bm->etoolflagpool || bm->ftoolflagpool; + if (bm->vtoolflagpool) { BLI_mempool_destroy(bm->vtoolflagpool); bm->vtoolflagpool = nullptr; @@ -120,6 +126,29 @@ void BM_mesh_elem_toolflags_clear(BMesh *bm) BLI_mempool_destroy(bm->ftoolflagpool); bm->ftoolflagpool = nullptr; } + + if (haveflags) { + BM_data_layer_free(bm, &bm->vdata, CD_TOOLFLAGS); + BM_data_layer_free(bm, &bm->edata, CD_TOOLFLAGS); + BM_data_layer_free(bm, &bm->pdata, CD_TOOLFLAGS); + } +} + +// int cdmap[8] = {0, 1, -1, -1, 2, -1, -1, -1, 3}; + +static void bm_swap_cd_data(int htype, BMesh *bm, CustomData *cd, void *a, void *b) +{ + int tot = cd->totsize; + // int cd_id = bm->idmap.cd_id_off[htype]; + + char *sa = (char *)a; + char *sb = (char *)b; + + for (int i = 0; i < tot; i++, sa++, sb++) { + char tmp = *sa; + *sa = *sb; + *sb = tmp; + } } BMesh *BM_mesh_create(const BMAllocTemplate *allocsize, const struct BMeshCreateParams *params) @@ -130,6 +159,45 @@ BMesh *BM_mesh_create(const BMAllocTemplate *allocsize, const struct BMeshCreate /* allocate the memory pools for the mesh elements */ bm_mempool_init(bm, allocsize, params->use_toolflags); + bm->idmap.flag = 0; + + if (!params->temporary_ids) { + bm->idmap.flag |= BM_PERMANENT_IDS; + } + + if (params->id_map) { + bm->idmap.flag |= BM_HAS_ID_MAP; + } + + if (params->no_reuse_ids) { + bm->idmap.flag |= BM_NO_REUSE_IDS; + } + + if (params->create_unique_ids) { + bm->idmap.flag |= BM_HAS_IDS; + + bm->idmap.flag |= params->id_elem_mask; + +#ifndef WITH_BM_ID_FREELIST + bm->idmap.idtree = range_tree_uint_alloc(0, (uint)-1); +#endif + } + + if (bm->idmap.flag & BM_HAS_ID_MAP) { + if (bm->idmap.flag & BM_NO_REUSE_IDS) { + bm->idmap.ghash = BLI_ghash_ptr_new("idmap.ghash"); + } + else { + bm->idmap.map_size = BM_DEFAULT_IDMAP_SIZE; + bm->idmap.map = (BMElem **)MEM_callocN(sizeof(void *) * bm->idmap.map_size, "bmesh idmap"); + bm->idmap.ghash = nullptr; + } + } + else { + bm->idmap.map = nullptr; + bm->idmap.ghash = nullptr; + } + /* allocate one flag pool that we don't get rid of. */ bm->use_toolflags = params->use_toolflags; bm->toolflag_index = 0; @@ -140,6 +208,42 @@ BMesh *BM_mesh_create(const BMAllocTemplate *allocsize, const struct BMeshCreate CustomData_reset(&bm->ldata); CustomData_reset(&bm->pdata); + bool init_cdata_pools = false; + + if (bm->use_toolflags) { + init_cdata_pools = true; + bm_alloc_toolflags_cdlayers(bm, false); + } + + if (params->create_unique_ids) { + bm_init_idmap_cdlayers(bm); + init_cdata_pools = true; + } + + if (init_cdata_pools) { + if (bm->vdata.totlayer) { + CustomData_bmesh_init_pool_ex(&bm->vdata, 0, BM_VERT, __func__); + } + if (bm->edata.totlayer) { + CustomData_bmesh_init_pool_ex(&bm->edata, 0, BM_EDGE, __func__); + } + if (bm->ldata.totlayer) { + CustomData_bmesh_init_pool_ex(&bm->ldata, 0, BM_LOOP, __func__); + } + if (bm->pdata.totlayer) { + CustomData_bmesh_init_pool_ex(&bm->pdata, 0, BM_FACE, __func__); + } + } + + if (bm->use_toolflags) { + BM_mesh_elem_toolflags_ensure(bm); + } + +#ifdef USE_BMESH_PAGE_CUSTOMDATA + bmesh_update_attr_refs(bm); + BMAttr_init(bm); +#endif + return bm; } @@ -153,6 +257,30 @@ void BM_mesh_data_free(BMesh *bm) BMIter iter; BMIter itersub; +#ifndef WITH_BM_ID_FREELIST + if (bm->idmap.idtree) { + range_tree_uint_free(bm->idmap.idtree); + } +#else + MEM_SAFE_FREE(bm->idmap.free_ids); + MEM_SAFE_FREE(bm->idmap.freelist); + bm->idmap.freelist = nullptr; + bm->idmap.free_ids = nullptr; +#endif + + MEM_SAFE_FREE(bm->idmap.map); + + if (bm->idmap.ghash) { + BLI_ghash_free(bm->idmap.ghash, nullptr, nullptr); + } + +#ifdef WITH_BM_ID_FREELIST + if (bm->idmap.free_idx_map) { + BLI_ghash_free(bm->idmap.free_idx_map, nullptr, nullptr); + bm->idmap.free_idx_map = nullptr; + } +#endif + const bool is_ldata_free = CustomData_bmesh_has_free(&bm->ldata); const bool is_pdata_free = CustomData_bmesh_has_free(&bm->pdata); @@ -217,8 +345,20 @@ void BM_mesh_data_free(BMesh *bm) MEM_freeN(bm->ftable); } - /* destroy flag pool */ - BM_mesh_elem_toolflags_clear(bm); + /* destroy flag pools */ + + if (bm->vtoolflagpool) { + BLI_mempool_destroy(bm->vtoolflagpool); + bm->vtoolflagpool = nullptr; + } + if (bm->etoolflagpool) { + BLI_mempool_destroy(bm->etoolflagpool); + bm->etoolflagpool = nullptr; + } + if (bm->ftoolflagpool) { + BLI_mempool_destroy(bm->ftoolflagpool); + bm->ftoolflagpool = nullptr; + } #ifdef USE_BMESH_HOLES BLI_mempool_destroy(bm->looplistpool); @@ -232,11 +372,17 @@ void BM_mesh_data_free(BMesh *bm) } BMO_error_clear(bm); + +#ifdef USE_BMESH_PAGE_CUSTOMDATA + BMAttr_free(bm->attr_list); + bm->attr_list = nullptr; +#endif } void BM_mesh_clear(BMesh *bm) { const bool use_toolflags = bm->use_toolflags; + const int idmap_flags = bm->idmap.flag; /* free old mesh */ BM_mesh_data_free(bm); @@ -253,6 +399,37 @@ void BM_mesh_clear(BMesh *bm) CustomData_reset(&bm->edata); CustomData_reset(&bm->ldata); CustomData_reset(&bm->pdata); + + bm->idmap.flag = idmap_flags; + + if (bm->idmap.flag & BM_HAS_IDS) { + bm->idmap.map = nullptr; + bm->idmap.ghash = nullptr; + bm->idmap.map_size = 0; + +#ifndef WITH_BM_ID_FREELIST + bm->idmap.idtree = range_tree_uint_alloc(0, (uint)-1); +#else + MEM_SAFE_FREE(bm->idmap.free_ids); + MEM_SAFE_FREE(bm->idmap.freelist); + + bm->idmap.freelist_len = bm->idmap.freelist_size = 0; + bm->idmap.free_ids = nullptr; + bm->idmap.freelist = nullptr; +#endif + bm_init_idmap_cdlayers(bm); + } + +#ifdef USE_BMESH_PAGE_CUSTOMDATA + if (!bm->attr_list) { + bm->attr_list = BMAttr_new(); + } + else { + BMAttr_reset(bm->attr_list); + } + + BMAttr_init(bm); +#endif } void BM_mesh_free(BMesh *bm) @@ -269,42 +446,34 @@ void BM_mesh_free(BMesh *bm) MEM_freeN(bm); } -void bmesh_edit_begin(BMesh * /*bm*/, BMOpTypeFlag /*type_flag*/) +/** + * \brief BMesh Begin Edit + * + * Functions for setting up a mesh for editing and cleaning up after + * the editing operations are done. These are called by the tools/operator + * API for each time a tool is executed. + */ +void bmesh_edit_begin(BMesh *bm, BMOpTypeFlag type_flag) { - /* Most operators seem to be using BMO_OPTYPE_FLAG_UNTAN_MULTIRES to change the MDisps to - * absolute space during mesh edits. With this enabled, changes to the topology - * (loop cuts, edge subdivides, etc) are not reflected in the higher levels of - * the mesh at all, which doesn't seem right. Turning off completely for now, - * until this is shown to be better for certain types of mesh edits. */ -#ifdef BMOP_UNTAN_MULTIRES_ENABLED /* switch multires data out of tangent space */ if ((type_flag & BMO_OPTYPE_FLAG_UNTAN_MULTIRES) && CustomData_has_layer(&bm->ldata, CD_MDISPS)) { - bmesh_mdisps_space_set(bm, MULTIRES_SPACE_TANGENT, MULTIRES_SPACE_ABSOLUTE); - + BM_enter_multires_space(nullptr, bm, MULTIRES_SPACE_ABSOLUTE); /* ensure correct normals, if possible */ - bmesh_rationalize_normals(bm, 0); - BM_mesh_normals_update(bm); + // bmesh_rationalize_normals(bm, 0); + // BM_mesh_normals_update(bm); } -#endif } void bmesh_edit_end(BMesh *bm, BMOpTypeFlag type_flag) { ListBase select_history; - /* BMO_OPTYPE_FLAG_UNTAN_MULTIRES disabled for now, see comment above in bmesh_edit_begin. */ -#ifdef BMOP_UNTAN_MULTIRES_ENABLED /* switch multires data into tangent space */ - if ((flag & BMO_OPTYPE_FLAG_UNTAN_MULTIRES) && CustomData_has_layer(&bm->ldata, CD_MDISPS)) { - /* set normals to their previous winding */ - bmesh_rationalize_normals(bm, 1); - bmesh_mdisps_space_set(bm, MULTIRES_SPACE_ABSOLUTE, MULTIRES_SPACE_TANGENT); + if ((type_flag & BMO_OPTYPE_FLAG_UNTAN_MULTIRES) && + CustomData_has_layer(&bm->ldata, CD_MDISPS)) { + BM_enter_multires_space(nullptr, bm, MULTIRES_SPACE_TANGENT); } - else if (flag & BMO_OP_FLAG_RATIONALIZE_NORMALS) { - bmesh_rationalize_normals(bm, 1); - } -#endif /* compute normals, clear temp flags and flush selections */ if (type_flag & BMO_OPTYPE_FLAG_NORMALS_CALC) { @@ -726,7 +895,25 @@ int BM_mesh_elem_count(BMesh *bm, const char htype) } } -void BM_mesh_remap(BMesh *bm, const uint *vert_idx, const uint *edge_idx, const uint *face_idx) +/** + * Remaps the vertices, edges and/or faces of the bmesh as indicated by vert/edge/face_idx arrays + * (xxx_idx[org_index] = new_index). + * + * A nullptr array means no changes. + * + * \note + * - Does not mess with indices, just sets elem_index_dirty flag. + * - For verts/edges/faces only (as loops must remain "ordered" and "aligned" + * on a per-face basis...). + * + * \warning Be careful if you keep pointers to affected BM elements, + * or arrays, when using this func! + */ +void BM_mesh_remap(BMesh *bm, + const uint *vert_idx, + const uint *edge_idx, + const uint *face_idx, + const uint *loop_idx) { /* Mapping old to new pointers. */ GHash *vptr_map = nullptr, *eptr_map = nullptr, *fptr_map = nullptr; @@ -743,6 +930,21 @@ void BM_mesh_remap(BMesh *bm, const uint *vert_idx, const uint *edge_idx, const BM_mesh_elem_table_ensure( bm, (vert_idx ? BM_VERT : 0) | (edge_idx ? BM_EDGE : 0) | (face_idx ? BM_FACE : 0)); + CustomData *cdatas[4] = {&bm->vdata, &bm->edata, &bm->ldata, &bm->pdata}; + +#define DO_SWAP(ci, cdata, v, vp) *(v) = *(vp); + +// NOT WORKING +/* unswaps customdata blocks*/ +#define DO_SWAP2(ci, cdata, v, vp) \ + void *cdold = (v)->head.data; \ + void *cdnew = (vp)->head.data; \ + *(v) = *(vp); \ + if (cdold) { \ + (v)->head.data = cdold; \ + memcpy(cdold, cdnew, bm->cdata.totsize); \ + } + /* Remap Verts */ if (vert_idx) { BMVert **verts_pool, *verts_copy, **vep; @@ -764,7 +966,9 @@ void BM_mesh_remap(BMesh *bm, const uint *vert_idx, const uint *edge_idx, const nullptr; for (i = totvert, ve = verts_copy + totvert - 1, vep = verts_pool + totvert - 1; i--; ve--, vep--) { + *ve = **vep; + // printf("*vep: %p, verts_pool[%d]: %p\n", *vep, i, verts_pool[i]); if (cd_vert_pyptr != -1) { void **pyptr = static_cast(BM_ELEM_CD_GET_VOID_P(((BMElem *)ve), cd_vert_pyptr)); @@ -778,12 +982,15 @@ void BM_mesh_remap(BMesh *bm, const uint *vert_idx, const uint *edge_idx, const vep = verts_pool + totvert - 1; /* old, org pointer */ for (i = totvert; i--; new_idx--, ve--, vep--) { BMVert *new_vep = verts_pool[*new_idx]; - *new_vep = *ve; + + DO_SWAP(0, vdata, new_vep, ve); + + BLI_ghash_insert(vptr_map, *vep, new_vep); #if 0 printf( "mapping vert from %d to %d (%p/%p to %p)\n", i, *new_idx, *vep, verts_pool[i], new_vep); #endif - BLI_ghash_insert(vptr_map, *vep, new_vep); + if (cd_vert_pyptr != -1) { void **pyptr = static_cast( BM_ELEM_CD_GET_VOID_P(((BMElem *)new_vep), cd_vert_pyptr)); @@ -799,6 +1006,77 @@ void BM_mesh_remap(BMesh *bm, const uint *vert_idx, const uint *edge_idx, const } } + GHash *lptr_map = nullptr; + + /* Remap Loops */ + if (loop_idx) { + BMLoop **ltable = (BMLoop **)MEM_malloc_arrayN(bm->totloop, sizeof(*ltable), "ltable"); + + BMLoop *ed; + BLI_mempool_iter liter; + BLI_mempool_iternew(bm->lpool, &liter); + BMLoop *l = (BMLoop *)BLI_mempool_iterstep(&liter); + + int i = 0; + for (; l; l = (BMLoop *)BLI_mempool_iterstep(&liter), i++) { + l->head.index = i; + ltable[i] = l; + } + + BMLoop **loops_pool, *loops_copy, **edl; + int totloop = bm->totloop; + const uint *new_idx; + /* Special case: Python uses custom data layers to hold PyObject references. + * These have to be kept in place, else the PyObjects we point to, won't point back to us. */ + const int cd_loop_pyptr = CustomData_get_offset(&bm->ldata, CD_BM_ELEM_PYPTR); + + /* Init the old-to-new vert pointers mapping */ + lptr_map = BLI_ghash_ptr_new_ex("BM_mesh_remap loop pointers mapping", bm->totloop); + + /* Make a copy of all vertices. */ + loops_pool = ltable; + loops_copy = (BMLoop *)MEM_mallocN(sizeof(BMLoop) * totloop, "BM_mesh_remap loops copy"); + + void **pyptrs = (cd_loop_pyptr != -1) ? + (void **)MEM_mallocN(sizeof(void *) * totloop, __func__) : + nullptr; + for (i = totloop, ed = loops_copy + totloop - 1, edl = loops_pool + totloop - 1; i--; + ed--, edl--) { + + *ed = **edl; + + if (cd_loop_pyptr != -1) { + void **pyptr = (void **)BM_ELEM_CD_GET_VOID_P(((BMElem *)ed), cd_loop_pyptr); + pyptrs[i] = *pyptr; + } + } + + /* Copy back verts to their new place, and update old2new pointers mapping. */ + new_idx = loop_idx + totloop - 1; + ed = loops_copy + totloop - 1; + edl = loops_pool + totloop - 1; /* old, org pointer */ + for (i = totloop; i--; new_idx--, ed--, edl--) { + BMLoop *new_edl = loops_pool[*new_idx]; + *new_edl = *ed; + + DO_SWAP(2, ldata, new_edl, ed); + + BLI_ghash_insert(lptr_map, *edl, new_edl); +#if 0 + printf( + "mapping loop from %d to %d (%p/%p to %p)\n", i, *new_idx, *edl, loops_pool[i], new_edl); +#endif + if (cd_loop_pyptr != -1) { + void **pyptr = (void **)BM_ELEM_CD_GET_VOID_P(((BMElem *)new_edl), cd_loop_pyptr); + *pyptr = pyptrs[*new_idx]; + } + } + + MEM_SAFE_FREE(ltable); + MEM_SAFE_FREE(loops_copy); + MEM_SAFE_FREE(pyptrs); + } + /* Remap Edges */ if (edge_idx) { BMEdge **edges_pool, *edges_copy, **edp; @@ -833,7 +1111,13 @@ void BM_mesh_remap(BMesh *bm, const uint *vert_idx, const uint *edge_idx, const edp = edges_pool + totedge - 1; /* old, org pointer */ for (i = totedge; i--; new_idx--, ed--, edp--) { BMEdge *new_edp = edges_pool[*new_idx]; - *new_edp = *ed; + + DO_SWAP(1, edata, new_edp, ed); + + if (new_edp->l && lptr_map) { + new_edp->l = (BMLoop *)BLI_ghash_lookup(lptr_map, (BMLoop *)new_edp->l); + } + BLI_ghash_insert(eptr_map, *edp, new_edp); #if 0 printf( @@ -890,6 +1174,24 @@ void BM_mesh_remap(BMesh *bm, const uint *vert_idx, const uint *edge_idx, const BMFace *new_fap = faces_pool[*new_idx]; *new_fap = *fa; BLI_ghash_insert(fptr_map, *fap, new_fap); + + DO_SWAP(3, pdata, new_fap, fa); + + if (lptr_map) { + new_fap->l_first = (BMLoop *)BLI_ghash_lookup(lptr_map, (void *)new_fap->l_first); + + BMLoop *l = new_fap->l_first; + + do { + l->next = (BMLoop *)BLI_ghash_lookup(lptr_map, (void *)l->next); + l->prev = (BMLoop *)BLI_ghash_lookup(lptr_map, (void *)l->prev); + l->radial_next = (BMLoop *)BLI_ghash_lookup(lptr_map, (void *)l->radial_next); + l->radial_prev = (BMLoop *)BLI_ghash_lookup(lptr_map, (void *)l->radial_prev); + + l = l->next; + } while (l != new_fap->l_first); + } + if (cd_poly_pyptr != -1) { void **pyptr = static_cast( BM_ELEM_CD_GET_VOID_P(((BMElem *)new_fap), cd_poly_pyptr)); @@ -1028,6 +1330,69 @@ void BM_mesh_remap(BMesh *bm, const uint *vert_idx, const uint *edge_idx, const if (fptr_map) { BLI_ghash_free(fptr_map, nullptr, nullptr); } + + // regenerate idmap + if ((bm->idmap.flag & BM_HAS_IDS) && (bm->idmap.flag & BM_HAS_ID_MAP) && bm->idmap.map) { + memset(bm->idmap.map, 0, sizeof(void *) * bm->idmap.map_size); + + char iters[4] = {BM_VERTS_OF_MESH, BM_EDGES_OF_MESH, 0, BM_FACES_OF_MESH}; + const bool have_loop = bm->idmap.flag & BM_LOOP; + + for (int i = 0; i < 4; i++) { + int type = 1 << i; + + if (type == BM_LOOP) { // handle loops with faces + continue; + } + + int cd_id = CustomData_get_offset(cdatas[i], CD_MESH_ID); + int cd_loop_id = CustomData_get_offset(&bm->ldata, CD_MESH_ID); + + BMIter iter; + BMElem *elem; + + if (cd_id < 0 && !(type == BM_FACE && have_loop)) { + continue; + } + + BM_ITER_MESH (elem, &iter, bm, iters[i]) { + if (type == BM_FACE && have_loop) { + BMFace *f = (BMFace *)elem; + BMLoop *l = f->l_first; + + do { + int id_loop = BM_ELEM_CD_GET_INT(l, cd_loop_id); + + if (bm->idmap.ghash) { + void **l_val; + + BLI_ghash_ensure_p(bm->idmap.ghash, POINTER_FROM_INT(id_loop), &l_val); + *l_val = (void *)l; + } + else { + bm->idmap.map[id_loop] = (BMElem *)l; + } + } while ((l = l->next) != f->l_first); + } + + if (cd_id < 0) { + continue; + } + + int id = BM_ELEM_CD_GET_INT(elem, cd_id); + + if (bm->idmap.ghash) { + void **val; + + BLI_ghash_ensure_p(bm->idmap.ghash, POINTER_FROM_INT(id), &val); + *val = (void *)elem; + } + else { + bm->idmap.map[id] = elem; + } + } + } + } } void BM_mesh_rebuild(BMesh *bm, @@ -1063,10 +1428,10 @@ void BM_mesh_rebuild(BMesh *bm, BMVert *v_dst = static_cast(BLI_mempool_alloc(vpool_dst)); memcpy(v_dst, v_src, sizeof(BMVert)); if (use_toolflags) { - ((BMVert_OFlag *)v_dst)->oflags = bm->vtoolflagpool ? - static_cast( - BLI_mempool_calloc(bm->vtoolflagpool)) : - nullptr; + MToolFlags *flags = (MToolFlags *)BM_ELEM_CD_GET_VOID_P( + v_dst, bm->vdata.layers[bm->vdata.typemap[CD_TOOLFLAGS]].offset); + + flags->flag = bm->vtoolflagpool ? (short *)BLI_mempool_calloc(bm->vtoolflagpool) : nullptr; } vtable_dst[index] = v_dst; @@ -1082,10 +1447,10 @@ void BM_mesh_rebuild(BMesh *bm, BMEdge *e_dst = static_cast(BLI_mempool_alloc(epool_dst)); memcpy(e_dst, e_src, sizeof(BMEdge)); if (use_toolflags) { - ((BMEdge_OFlag *)e_dst)->oflags = bm->etoolflagpool ? - static_cast( - BLI_mempool_calloc(bm->etoolflagpool)) : - nullptr; + MToolFlags *flags = (MToolFlags *)BM_ELEM_CD_GET_VOID_P( + e_dst, bm->edata.layers[bm->edata.typemap[CD_TOOLFLAGS]].offset); + + flags->flag = bm->etoolflagpool ? (short *)BLI_mempool_calloc(bm->etoolflagpool) : nullptr; } etable_dst[index] = e_dst; @@ -1102,11 +1467,13 @@ void BM_mesh_rebuild(BMesh *bm, if (remap & BM_FACE) { BMFace *f_dst = static_cast(BLI_mempool_alloc(fpool_dst)); memcpy(f_dst, f_src, sizeof(BMFace)); + if (use_toolflags) { - ((BMFace_OFlag *)f_dst)->oflags = bm->ftoolflagpool ? - static_cast( - BLI_mempool_calloc(bm->ftoolflagpool)) : - nullptr; + MToolFlags *flags = (MToolFlags *)BM_ELEM_CD_GET_VOID_P( + f_dst, bm->pdata.layers[bm->pdata.typemap[CD_TOOLFLAGS]].offset); + + flags->flag = bm->ftoolflagpool ? (short *)BLI_mempool_calloc(bm->ftoolflagpool) : + nullptr; } ftable_dst[index] = f_dst; @@ -1286,6 +1653,68 @@ void BM_mesh_rebuild(BMesh *bm, BLI_mempool_destroy(bm->fpool); bm->fpool = fpool_dst; } + + bm_rebuild_idmap(bm); +} + +void bm_alloc_toolflags_cdlayers(BMesh *bm, bool set_elems) +{ + CustomData *cdatas[3] = {&bm->vdata, &bm->edata, &bm->pdata}; + int iters[3] = {BM_VERTS_OF_MESH, BM_EDGES_OF_MESH, BM_FACES_OF_MESH}; + + for (int i = 0; i < 3; i++) { + CustomData *cdata = cdatas[i]; + int cd_tflags = CustomData_get_offset(cdata, CD_TOOLFLAGS); + + if (cd_tflags == -1) { + if (set_elems) { + BM_data_layer_add(bm, cdata, CD_TOOLFLAGS); + } + else { + CustomData_add_layer(cdata, CD_TOOLFLAGS, CD_ASSIGN, nullptr, 0); + } + + int idx = CustomData_get_layer_index(cdata, CD_TOOLFLAGS); + + cdata->layers[idx].flag |= CD_FLAG_TEMPORARY | CD_FLAG_NOCOPY | CD_FLAG_ELEM_NOCOPY; + cd_tflags = cdata->layers[idx].offset; + + if (set_elems) { + BMIter iter; + BMElem *elem; + + BM_ITER_MESH (elem, &iter, bm, iters[i]) { + MToolFlags *flags = (MToolFlags *)BM_ELEM_CD_GET_VOID_P(elem, cd_tflags); + + flags->flag = nullptr; + } + } + } + } +} + +static void bm_alloc_toolflags(BMesh *bm) +{ + bm_alloc_toolflags_cdlayers(bm, true); + + CustomData *cdatas[3] = {&bm->vdata, &bm->edata, &bm->pdata}; + BLI_mempool *flagpools[3] = {bm->vtoolflagpool, bm->etoolflagpool, bm->ftoolflagpool}; + BLI_mempool *elempools[3] = {bm->vpool, bm->epool, bm->fpool}; + + for (int i = 0; i < 3; i++) { + CustomData *cdata = cdatas[i]; + int cd_tflags = CustomData_get_offset(cdata, CD_TOOLFLAGS); + + BLI_mempool_iter iter; + BLI_mempool_iternew(elempools[i], &iter); + BMElem *elem = (BMElem *)BLI_mempool_iterstep(&iter); + + for (; elem; elem = (BMElem *)BLI_mempool_iterstep(&iter)) { + MToolFlags *flags = (MToolFlags *)BM_ELEM_CD_GET_VOID_P(elem, cd_tflags); + + flags->flag = (short *)BLI_mempool_calloc(flagpools[i]); + } + } } void BM_mesh_toolflags_set(BMesh *bm, bool use_toolflags) @@ -1294,14 +1723,6 @@ void BM_mesh_toolflags_set(BMesh *bm, bool use_toolflags) return; } - const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_BM(bm); - - BLI_mempool *vpool_dst = nullptr; - BLI_mempool *epool_dst = nullptr; - BLI_mempool *fpool_dst = nullptr; - - bm_mempool_init_ex(&allocsize, use_toolflags, &vpool_dst, &epool_dst, nullptr, &fpool_dst); - if (use_toolflags == false) { BLI_mempool_destroy(bm->vtoolflagpool); BLI_mempool_destroy(bm->etoolflagpool); @@ -1310,13 +1731,20 @@ void BM_mesh_toolflags_set(BMesh *bm, bool use_toolflags) bm->vtoolflagpool = nullptr; bm->etoolflagpool = nullptr; bm->ftoolflagpool = nullptr; - } - struct BMeshCreateParams params = {}; - params.use_toolflags = use_toolflags; - BM_mesh_rebuild(bm, ¶ms, vpool_dst, epool_dst, nullptr, fpool_dst); + BM_data_layer_free(bm, &bm->vdata, CD_TOOLFLAGS); + BM_data_layer_free(bm, &bm->edata, CD_TOOLFLAGS); + BM_data_layer_free(bm, &bm->pdata, CD_TOOLFLAGS); + } + else { + bm_alloc_toolflags_cdlayers(bm, true); + } bm->use_toolflags = use_toolflags; + + if (use_toolflags) { + BM_mesh_elem_toolflags_ensure(bm); + } } /* -------------------------------------------------------------------- */ @@ -1364,4 +1792,411 @@ void BM_mesh_vert_coords_apply_with_mat4(BMesh *bm, } } +void bm_swap_ids(BMesh *bm, BMElem *e1, BMElem *e2) +{ + int cd_id = bm->idmap.cd_id_off[e1->head.htype]; + + if (cd_id < 0) { + return; + } + + int id1 = BM_ELEM_CD_GET_INT(e1, cd_id); + int id2 = BM_ELEM_CD_GET_INT(e2, cd_id); + + if (bm->idmap.map) { + SWAP(BMElem *, bm->idmap.map[id1], bm->idmap.map[id2]); + } + else if (bm->idmap.ghash) { + void **val1, **val2; + + BLI_ghash_ensure_p(bm->idmap.ghash, POINTER_FROM_INT(id1), &val1); + BLI_ghash_ensure_p(bm->idmap.ghash, POINTER_FROM_INT(id2), &val2); + + *val1 = (void *)e2; + *val2 = (void *)e1; + } +} +static void bm_swap_elements_post(BMesh *bm, CustomData *cdata, BMElem *e1, BMElem *e2) +{ + // unswap customdata pointers + SWAP(void *, e1->head.data, e2->head.data); + + // swap contents of customdata instead + bm_swap_cd_data(e1->head.htype, bm, cdata, e1->head.data, e2->head.data); + + // unswap index + SWAP(int, e1->head.index, e2->head.index); + + bm_swap_ids(bm, e1, e2); +} + +void BM_swap_verts(BMesh *bm, BMVert *v1, BMVert *v2) +{ + if (v1 == v2) { + return; + } + + Vector ls1; + Vector ls2; + Vector es1; + Vector es2; + Vector sides1; + Vector sides2; + + for (int i = 0; i < 2; i++) { + BMVert *v = i ? v2 : v1; + + BMEdge *e = v->e, *starte = e; + + if (!e) { + continue; + } + + // int count = 0; + + do { + // if (count++ > 10000) { + // printf("error!\n"); + // break; + // } + + int side = 0; + if (e->v1 == v) { + side |= 1; + } + + if (e->v2 == v) { + side |= 2; + } + + if (i) { + es2.append(e); + sides2.append(side); + } + else { + es1.append(e); + sides1.append(side); + } + } while ((e = BM_DISK_EDGE_NEXT(e, v)) != starte); + } + + for (int i = 0; i < 2; i++) { + BMVert *v = i ? v2 : v1; + BMVert *v_2 = i ? v1 : v2; + + auto &es = i ? es2 : es1; + auto &sides = i ? sides2 : sides1; + + int elen = i = es.size(); + for (int j = 0; j < elen; j++) { + BMEdge *e = es[j]; + int side = sides[j]; + + // if (side == 3) { + // printf("edge had duplicate verts!\n"); + //} + + if (side & 1) { + e->v1 = v_2; + } + + if (side & 2) { + e->v2 = v_2; + } + +#if 1 + BMLoop *l = e->l; + if (l) { + + do { + BMLoop *l2 = l; + + do { + if (l2->v == v) { + if (i) { + ls2.append(l2); + } + else { + ls1.append(l2); + } + } + } while ((l2 = l2->next) != l); + } while ((l = l->radial_next) != e->l); + } +#endif + // e = enext; + } // while (e != starte); + } + + for (int i = 0; i < 2; i++) { + BMVert *v_2 = i ? v1 : v2; + auto &ls = i ? ls2 : ls1; + + int llen = ls.size(); + for (int j = 0; j < llen; j++) { + ls[j]->v = v_2; + } + } + + SWAP(BMVert, (*v1), (*v2)); + // swap contents of customdata, don't swap pointers + bm_swap_elements_post(bm, &bm->vdata, (BMElem *)v1, (BMElem *)v2); + + bm->elem_table_dirty |= BM_VERT; + bm->elem_index_dirty |= BM_VERT; +} + +void BM_swap_edges(BMesh *bm, BMEdge *e1, BMEdge *e2) +{ + for (int i = 0; i < 2; i++) { + BMEdge *e = i ? e2 : e1; + BMEdge *e_2 = i ? e1 : e2; + + for (int j = 0; j < 2; j++) { + BMVert *v = j ? e->v2 : e->v1; + + if (v->e == e) { + v->e = e_2; + } + } + + BMLoop *l = e->l; + if (l) { + do { + l->e = e_2; + } while ((l = l->radial_next) != e->l); + } + } + + SWAP(BMEdge, *e1, *e2); + // swap contents of customdata, don't swap pointers + bm_swap_elements_post(bm, &bm->edata, (BMElem *)e1, (BMElem *)e2); +} + +void BM_swap_loops(BMesh *bm, BMLoop *l1, BMLoop *l2) +{ + for (int i = 0; i < 2; i++) { + BMLoop *l = i ? l2 : l1; + + l->prev->next = l2; + l->next->prev = l2; + + if (l != l->radial_next) { + l->radial_next->radial_prev = l2; + l->radial_prev->radial_next = l2; + } + + if (l == l->e->l) { + l->e->l = l2; + } + + if (l == l->f->l_first) { + l->f->l_first = l2; + } + } + + // swap contents of customdata, don't swap pointers + SWAP(BMLoop, *l1, *l2); + // swap contents of customdata, don't swap pointers + bm_swap_elements_post(bm, &bm->ldata, (BMElem *)l1, (BMElem *)l2); +} + +// memory coherence defragmentation + +#ifndef ABSLL +# define ABSLL(a) ((a) < 0LL ? -(a) : (a)) +#endif + +#define DEFRAG_FLAG BM_ELEM_TAG_ALT + +bool BM_defragment_vertex(BMesh *bm, + BMVert *v, + RNG *rand, + void (*on_vert_swap)(BMVert *a, BMVert *b, void *userdata), + void *userdata) +{ + BMEdge *e = v->e; + +#if 1 + int cd_vcol = CustomData_get_offset(&bm->vdata, CD_PROP_COLOR); + + if (cd_vcol >= 0) { + float *color = (float *)BM_ELEM_CD_GET_VOID_P(v, cd_vcol); + int idx = BLI_mempool_find_real_index(bm->vpool, (void *)v); + int size = BLI_mempool_get_size(bm->vpool); + + float f = (float)idx / (float)size / 2.0f; + + color[0] = color[1] = color[2] = f; + color[3] = 1.0f; + } +#endif + + // return false; + + // return false; + + // BM_mesh_elem_table_ensure(bm, BM_VERT|BM_EDGE|BM_FACE); + if (!e) { + return false; + } + + bool bad = false; + int limit = 128; + + int vlimit = sizeof(BMVert *) * limit; + int elimit = sizeof(BMEdge *) * limit; + int llimit = sizeof(BMLoop *) * limit; + // int flimit = sizeof(BMFace *) * limit; + + intptr_t iv = (intptr_t)v; + + BMEdge *laste = nullptr; + do { + BMVert *v2 = BM_edge_other_vert(e, v); + intptr_t iv2 = (intptr_t)v2; + intptr_t ie = (intptr_t)e; + + v2->head.hflag &= DEFRAG_FLAG; + e->head.hflag &= ~DEFRAG_FLAG; + + if (ABSLL(iv2 - iv) > vlimit) { + bad = true; + break; + } + + if (laste) { + intptr_t ilaste = (intptr_t)laste; + if (ABSLL(ilaste - ie) > elimit) { + bad = true; + break; + } + } + + BMLoop *l = e->l; + if (l) { + do { + intptr_t il = (intptr_t)l; + intptr_t ilnext = (intptr_t)l->next; + + if (ABSLL(il - ilnext) > llimit) { + bad = true; + break; + } + + BMLoop *l2 = l->f->l_first; + do { + l2->head.hflag &= ~DEFRAG_FLAG; + } while ((l2 = l2->next) != l->f->l_first); + + l2->f->head.hflag &= ~DEFRAG_FLAG; + + l = l->radial_next; + } while (l != e->l); + } + laste = e; + } while (!bad && (e = BM_DISK_EDGE_NEXT(e, v)) != v->e); + + float prob = 1.0; + + if (!bad || BLI_rng_get_float(rand) > prob) { + return false; + } + + // find sort candidates + // BLI_mempool_find_elems_fuzzy + + int vidx = BLI_mempool_find_real_index(bm->vpool, (void *)v); + const int count = 5; + BMVert **elems = static_cast(BLI_array_alloca(elems, count)); + + do { + BMVert *v2 = BM_edge_other_vert(e, v); + int totelem = BLI_mempool_find_elems_fuzzy(bm->vpool, vidx, 4, (void **)elems, count); + + for (int i = 0; i < totelem; i++) { + if (elems[i] == v2 || elems[i] == v) { + continue; + } + + elems[i]->head.hflag &= ~DEFRAG_FLAG; + } + + bool ok = false; + + for (int i = 0; i < totelem; i++) { + if (elems[i] == v2 || elems[i] == v || (elems[i]->head.hflag & DEFRAG_FLAG)) { + continue; + } + + if (elems[i]->head.htype != BM_VERT) { + printf("ERROR!\n"); + } + // found one + v2->head.hflag |= DEFRAG_FLAG; + elems[i]->head.hflag |= DEFRAG_FLAG; + + on_vert_swap(v2, elems[i], userdata); + BM_swap_verts(bm, v2, elems[i]); + +#if 0 + BMIter iter; + BMEdge *et; + int f = 0; + BM_ITER_ELEM (et, &iter, v2, BM_EDGES_OF_VERT) { + printf("an edge %d\n", f++); + } + + f = 0; + BM_ITER_ELEM (et, &iter, v, BM_EDGES_OF_VERT) { + printf("an 1edge %d\n", f++); + } +#endif + + // BM_swap_verts(bm, v2, elems[i]); + + ok = true; + break; + } + + if (ok) { + break; + } + } while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e); + + return true; +} + +static void on_vert_kill(BMesh *bm, BMVert *v, void *userdata) +{ +} +static void on_edge_kill(BMesh *bm, BMEdge *e, void *userdata) +{ +} +static void on_face_kill(BMesh *bm, BMFace *f, void *userdata) +{ +} + +static void on_vert_create(BMesh *bm, BMVert *v, void *userdata) +{ +} +static void on_edge_create(BMesh *bm, BMEdge *v, void *userdata) +{ +} +static void on_face_create(BMesh *bm, BMFace *v, void *userdata) +{ +} + +void BM_empty_tracer(BMTracer *tracer, void *userdata) +{ + tracer->userdata = userdata; + + tracer->on_vert_create = on_vert_create; + tracer->on_edge_create = on_edge_create; + tracer->on_face_create = on_face_create; + + tracer->on_vert_kill = on_vert_kill; + tracer->on_edge_kill = on_edge_kill; + tracer->on_face_kill = on_face_kill; +} /** \} */ diff --git a/source/blender/bmesh/intern/bmesh_mesh.h b/source/blender/bmesh/intern/bmesh_mesh.h index a4cfa00aeb3..98816f308a3 100644 --- a/source/blender/bmesh/intern/bmesh_mesh.h +++ b/source/blender/bmesh/intern/bmesh_mesh.h @@ -6,21 +6,53 @@ * \ingroup bmesh */ +#include "BLI_compiler_compat.h" #include "bmesh_class.h" +typedef struct BMTracer { + void (*on_vert_kill)(BMesh *bm, BMVert *v, void *userdata); + void (*on_edge_kill)(BMesh *bm, BMEdge *e, void *userdata); + void (*on_face_kill)(BMesh *bm, BMFace *f, void *userdata); + + void (*on_vert_create)(BMesh *bm, BMVert *v, void *userdata); + void (*on_edge_create)(BMesh *bm, BMEdge *v, void *userdata); + void (*on_face_create)(BMesh *bm, BMFace *v, void *userdata); + + void *userdata; +} BMTracer; + +typedef enum { + MULTIRES_SPACE_TANGENT, // convert absolute to tangent + MULTIRES_SPACE_ABSOLUTE // convert tangent to absolute +} MultiResSpace; + #ifdef __cplusplus extern "C" { #endif struct BMAllocTemplate; +void BM_empty_tracer(BMTracer *tracer, void *userdata); + void BM_mesh_elem_toolflags_ensure(BMesh *bm); void BM_mesh_elem_toolflags_clear(BMesh *bm); struct BMeshCreateParams { + bool create_unique_ids : 1; + int id_elem_mask : 8; // which element types to make unique ids for + bool id_map : 1; // maintain an id to element lookup table bool use_toolflags : 1; + bool no_reuse_ids : 1; // do not reuse IDs; a GHash will be used internally instead of a lookup + // array + bool temporary_ids : 1; + bool copy_all_layers : 1; // used by BM_mesh_copy_ex }; +// used to temporary save/restore element IDs +// when changing out customdata +int bm_save_id(BMesh *bm, BMElem *elem); +void bm_restore_id(BMesh *bm, BMElem *elem, int id); + /** * \brief BMesh Make Mesh * @@ -155,7 +187,11 @@ int BM_mesh_elem_count(BMesh *bm, char htype); * \warning Be careful if you keep pointers to affected BM elements, * or arrays, when using this func! */ -void BM_mesh_remap(BMesh *bm, const uint *vert_idx, const uint *edge_idx, const uint *face_idx); +void BM_mesh_remap(BMesh *bm, + const uint *vert_idx, + const uint *edge_idx, + const uint *face_idx, + const uint *loop_idx); /** * Use new memory pools for this mesh. @@ -206,6 +242,16 @@ void BM_mesh_vert_coords_apply_with_mat4(BMesh *bm, const float (*vert_coords)[3], const float mat[4][4]); +#define BM_ELEM_FROM_ID(bm, id) \ + ((bm->idmap.flag & BM_NO_REUSE_IDS) ? \ + BLI_ghash_lookup(bm->idmap.ghash, POINTER_FROM_UINT(id)) : \ + bm->idmap.map[id]) + +#define BM_ELEM_FROM_ID_SAFE(bm, id) \ + (((id) >= 0 && (id) < (bm)->idmap.maxid) ? (BM_ELEM_FROM_ID(bm, id)) : NULL) + +bool BM_elem_is_free(BMElem *elem, int htype); + #ifdef __cplusplus } #endif diff --git a/source/blender/bmesh/intern/bmesh_mesh_convert.cc b/source/blender/bmesh/intern/bmesh_mesh_convert.cc index 35f59a2f143..6db9e36bca1 100644 --- a/source/blender/bmesh/intern/bmesh_mesh_convert.cc +++ b/source/blender/bmesh/intern/bmesh_mesh_convert.cc @@ -78,7 +78,9 @@ #include "MEM_guardedalloc.h" #include "BLI_alloca.h" +#include "BLI_array.h" #include "BLI_array.hh" +#include "BLI_bitmap.h" #include "BLI_index_range.hh" #include "BLI_listbase.h" #include "BLI_math_vector.h" @@ -99,6 +101,68 @@ #include "bmesh.h" #include "intern/bmesh_private.h" /* For element checking. */ +#include "range_tree.h" + +static void bm_free_cd_pools(BMesh *bm) +{ + if (bm->vdata.pool) { + BLI_mempool_destroy(bm->vdata.pool); + } + if (bm->edata.pool) { + BLI_mempool_destroy(bm->edata.pool); + } + if (bm->ldata.pool) { + BLI_mempool_destroy(bm->ldata.pool); + } + if (bm->pdata.pool) { + BLI_mempool_destroy(bm->pdata.pool); + } +} + +//#define bm_assign_id(a, b, c, d) +//#define bm_alloc_id(a, b) + +static void bm_unmark_temp_cdlayers(BMesh *bm) +{ + CustomData_unmark_temporary_nocopy(&bm->vdata); + CustomData_unmark_temporary_nocopy(&bm->edata); + CustomData_unmark_temporary_nocopy(&bm->ldata); + CustomData_unmark_temporary_nocopy(&bm->pdata); +} + +static void bm_mark_temp_cdlayers(BMesh *bm) +{ + CustomData_mark_temporary_nocopy(&bm->vdata); + CustomData_mark_temporary_nocopy(&bm->edata); + CustomData_mark_temporary_nocopy(&bm->ldata); + CustomData_mark_temporary_nocopy(&bm->pdata); +} + +#if 0 +# define CustomData_to_bmesh_block(srcdata, destdata, i, block, set_default) \ + { \ + CustomDataLayer *cl = (srcdata)->layers, *cl2 = (destdata)->layers; \ + int size = 0; \ + if (!*block) { \ + *block = BLI_mempool_alloc((destdata)->pool); \ + } \ + for (int j = 0; j < (srcdata)->totlayer; j++, cl++) { \ + if ((destdata)->typemap[cl->type] < 0) { \ + continue; \ + } \ + while (cl2->type != cl->type) { \ + cl2++; \ + } \ + char *ptr = (char *)cl->data; \ + size = cl2 != (destdata)->layers ? cl2->offset - (cl2 - 1)->offset : cl2->offset; \ + ptr += size * i; \ + char *ptr2 = (char *)*block; \ + ptr2 += cl2->offset; \ + memcpy(ptr2, ptr, size); \ + cl2++; \ + } \ + } +#endif #include "CLG_log.h" @@ -164,8 +228,63 @@ static BMFace *bm_face_create_from_mpoly(BMesh &bm, return BM_face_create(&bm, verts.data(), edges.data(), loops.size(), nullptr, BM_CREATE_SKIP_CD); } -void BM_mesh_bm_from_me(BMesh *bm, const Mesh *me, const struct BMeshFromMeshParams *params) +void BM_enter_multires_space(Object *ob, BMesh *bm, int space) { + if (!bm->haveMultiResSettings && ob) { + MultiresModifierData *mmd = get_multires_modifier(nullptr, ob, true); + if (mmd) { + memcpy((void *)&bm->multires, (void *)&mmd, sizeof(*mmd)); + bm->haveMultiResSettings = true; + bm->multiresSpace = MULTIRES_SPACE_TANGENT; + } + } + + if (!bm->haveMultiResSettings || !CustomData_has_layer(&bm->ldata, CD_MDISPS) || + space == bm->multiresSpace) { + return; + } + + BKE_multires_bmesh_space_set(ob, bm, space); + bm->multiresSpace = space; +} + +#include "BLI_compiler_attrs.h" + +/** + * \brief Mesh -> BMesh + * \param ob: object that owns bm, may be nullptr (which will disable multires space change) + * \param bm: The mesh to write into, while this is typically a newly created BMesh, + * merging into existing data is supported. + * Note the custom-data layout isn't used. + * If more comprehensive merging is needed we should move this into a separate function + * since this should be kept fast for edit-mode switching and storing undo steps. + * + * \warning This function doesn't calculate face normals. + * + * Mesh IDs will be imported unless requested. If the bmesh was created + * with id map enabled then IDs will be checked for uniqueness, otherwise + * they are imported as is. + */ + +void BM_mesh_bm_from_me(Object *ob, + BMesh *bm, + const Mesh *me, + const struct BMeshFromMeshParams *params) +{ + static int totlayers = 0; + + for (int i = 0; i < 4; i++) { + CustomData *cdata = (&bm->vdata) + i; + + for (int j = 0; j < cdata->totlayer; j++) { + if (cdata->layers[j].type == CD_TOOLFLAGS || cdata->layers[j].type == CD_MESH_ID) { + continue; + } + + totlayers++; + } + } + if (!me) { /* Sanity check. */ return; @@ -174,9 +293,61 @@ void BM_mesh_bm_from_me(BMesh *bm, const Mesh *me, const struct BMeshFromMeshPar bm->pdata.totlayer || bm->ldata.totlayer)); KeyBlock *actkey; float(*keyco)[3] = nullptr; + CustomData_MeshMasks mask = CD_MASK_BMESH; CustomData_MeshMasks_update(&mask, ¶ms->cd_mask_extra); + MultiresModifierData *mmd = ob ? get_multires_modifier(nullptr, ob, true) : nullptr; + const CustomData *cdatas[] = {&me->vdata, &me->edata, &me->ldata, &me->pdata}; + // const CustomData *bmdatas[] = {&bm->vdata, &bm->edata, &bm->ldata, &bm->pdata}; + + bool check_id_unqiue = false; + + if (!params->ignore_id_layers) { + for (int i = 0; i < 4; i++) { + int type = 1 << i; + + if (CustomData_has_layer(cdatas[i], CD_MESH_ID)) { + bm->idmap.flag |= type | BM_HAS_IDS; + } + } + + bm_init_idmap_cdlayers(bm); + } + + // check_id_unqiue + if ((bm->idmap.flag & BM_HAS_IDS)) { +#ifndef WITH_BM_ID_FREELIST + if (!bm->idmap.idtree) { + bm->idmap.idtree = range_tree_uint_alloc(0, (uint)-1); + } +#endif + + if (bm->idmap.flag & BM_HAS_ID_MAP) { + check_id_unqiue = true; + } + } + + if (params->copy_temp_cdlayers) { + bm_unmark_temp_cdlayers(bm); + } + + if (params->copy_temp_cdlayers) { + mask.vmask |= CD_MASK_MESH_ID; + mask.emask |= CD_MASK_MESH_ID; + mask.lmask |= CD_MASK_MESH_ID; + mask.pmask |= CD_MASK_MESH_ID; + } + + if (mmd) { + memcpy((void *)&bm->multires, (void *)&mmd, sizeof(*mmd)); + bm->haveMultiResSettings = true; + bm->multiresSpace = MULTIRES_SPACE_TANGENT; + } + else { + bm->haveMultiResSettings = false; + } + CustomData mesh_vdata = CustomData_shallow_copy_remove_non_bmesh_attributes(&me->vdata, mask.vmask); CustomData mesh_edata = CustomData_shallow_copy_remove_non_bmesh_attributes(&me->edata, @@ -233,12 +404,29 @@ void BM_mesh_bm_from_me(BMesh *bm, const Mesh *me, const struct BMeshFromMeshPar CustomData_copy(&mesh_pdata, &bm->pdata, mask.pmask, CD_CONSTRUCT, 0); CustomData_copy(&mesh_ldata, &bm->ldata, mask.lmask, CD_CONSTRUCT, 0); - CustomData_bmesh_init_pool(&bm->vdata, me->totvert, BM_VERT); - CustomData_bmesh_init_pool(&bm->edata, me->totedge, BM_EDGE); - CustomData_bmesh_init_pool(&bm->ldata, me->totloop, BM_LOOP); - CustomData_bmesh_init_pool(&bm->pdata, me->totpoly, BM_FACE); + CustomData_bmesh_init_pool_ex(&bm->vdata, me->totvert, BM_VERT, __func__); + CustomData_bmesh_init_pool_ex(&bm->edata, me->totedge, BM_EDGE, __func__); + CustomData_bmesh_init_pool_ex(&bm->ldata, me->totloop, BM_LOOP, __func__); + CustomData_bmesh_init_pool_ex(&bm->pdata, me->totpoly, BM_FACE, __func__); } - return; + +#ifdef USE_BMESH_PAGE_CUSTOMDATA + bmesh_update_attr_refs(bm); +#endif + + if (params->copy_temp_cdlayers) { + bm_mark_temp_cdlayers(bm); + } + + if (bm->idmap.flag & BM_HAS_IDS) { + bm_init_idmap_cdlayers(bm); + } + + if (bm->use_toolflags) { + bm_alloc_toolflags_cdlayers(bm, true); + } + + return; /* Sanity check. */ } const float(*vert_normals)[3] = nullptr; @@ -247,6 +435,7 @@ void BM_mesh_bm_from_me(BMesh *bm, const Mesh *me, const struct BMeshFromMeshPar } if (is_new) { + bm_free_cd_pools(bm); CustomData_copy(&mesh_vdata, &bm->vdata, mask.vmask, CD_SET_DEFAULT, 0); CustomData_copy(&mesh_edata, &bm->edata, mask.emask, CD_SET_DEFAULT, 0); CustomData_copy(&mesh_pdata, &bm->pdata, mask.pmask, CD_SET_DEFAULT, 0); @@ -284,9 +473,9 @@ void BM_mesh_bm_from_me(BMesh *bm, const Mesh *me, const struct BMeshFromMeshPar * At the moment it's simplest to assume all original meshes use the key-block and meshes * that are evaluated (through the modifier stack for example) use custom-data layers. */ - BLI_assert(!CustomData_has_layer(&me->vdata, CD_SHAPEKEY)); + BLI_assert(!CustomData_has_layer(&mesh_vdata, CD_SHAPEKEY)); } - if (is_new == false) { + if (is_new == false && CustomData_has_layer(&bm->vdata, CD_SHAPEKEY)) { tot_shape_keys = min_ii(tot_shape_keys, CustomData_number_of_layers(&bm->vdata, CD_SHAPEKEY)); } const float(**shape_key_table)[3] = tot_shape_keys ? (const float(**)[3])BLI_array_alloca( @@ -307,7 +496,7 @@ void BM_mesh_bm_from_me(BMesh *bm, const Mesh *me, const struct BMeshFromMeshPar } if (tot_shape_keys) { - if (is_new) { + if (is_new || params->create_shapekey_layers) { /* Check if we need to generate unique ids for the shape-keys. * This also exists in the file reading code, but is here for a sanity check. */ if (!me->key->uidgen) { @@ -325,39 +514,103 @@ void BM_mesh_bm_from_me(BMesh *bm, const Mesh *me, const struct BMeshFromMeshPar if (actkey && actkey->totelem == me->totvert) { keyco = params->use_shapekey ? static_cast(actkey->data) : nullptr; - if (is_new) { + if (is_new || params->create_shapekey_layers) { bm->shapenr = params->active_shapekey; } } int i; + KeyBlock *block; for (i = 0, block = static_cast(me->key->block.first); i < tot_shape_keys; block = block->next, i++) { if (is_new) { CustomData_add_layer_named(&bm->vdata, CD_SHAPEKEY, CD_ASSIGN, nullptr, 0, block->name); - int j = CustomData_get_layer_index_n(&bm->vdata, CD_SHAPEKEY, i); - bm->vdata.layers[j].uid = block->uid; } - shape_key_table[i] = static_cast(block->data); + else { + BM_data_layer_add_named(bm, &bm->vdata, CD_SHAPEKEY, block->name); + } + + int j = CustomData_get_layer_index_n(&bm->vdata, CD_SHAPEKEY, i); + + bm->vdata.layers[j].uid = block->uid; + shape_key_table[i] = (const float(*)[3])block->data; + } + } + + if (bm->use_toolflags) { + bm_alloc_toolflags_cdlayers(bm, !is_new); + + if (!bm->vtoolflagpool) { + bm->vtoolflagpool = BLI_mempool_create( + sizeof(BMFlagLayer), bm->totvert, 512, BLI_MEMPOOL_NOP); + bm->etoolflagpool = BLI_mempool_create( + sizeof(BMFlagLayer), bm->totedge, 512, BLI_MEMPOOL_NOP); + bm->ftoolflagpool = BLI_mempool_create( + sizeof(BMFlagLayer), bm->totface, 512, BLI_MEMPOOL_NOP); + + bm->totflags = 1; } } if (is_new) { - CustomData_bmesh_init_pool(&bm->vdata, me->totvert, BM_VERT); - CustomData_bmesh_init_pool(&bm->edata, me->totedge, BM_EDGE); - CustomData_bmesh_init_pool(&bm->ldata, me->totloop, BM_LOOP); - CustomData_bmesh_init_pool(&bm->pdata, me->totpoly, BM_FACE); + CustomData_bmesh_init_pool_ex(&bm->vdata, me->totvert, BM_VERT, __func__); + CustomData_bmesh_init_pool_ex(&bm->edata, me->totedge, BM_EDGE, __func__); + CustomData_bmesh_init_pool_ex(&bm->ldata, me->totloop, BM_LOOP, __func__); + CustomData_bmesh_init_pool_ex(&bm->pdata, me->totpoly, BM_FACE, __func__); } +#define IS_GARBAGE_ID(id) ((id) < 0 || (id) > id_garbage_threshold) + + int *existing_id_layers[4] = {nullptr, nullptr, nullptr, nullptr}; + + /* threshold to detect garbage IDs, number of elements with ids multiplied by 5 */ + int id_garbage_threshold = 0; + + int use_exist_ids = 0; + int has_ids = bm->idmap.flag & BM_HAS_IDS ? + (bm->idmap.flag & (BM_VERT | BM_EDGE | BM_LOOP | BM_FACE)) : + 0; + if (bm->idmap.flag & BM_HAS_IDS) { + int tots[4] = {me->totvert + bm->totvert, + me->totedge + bm->totedge, + me->totloop + bm->totloop, + me->totpoly + bm->totface}; + + if (!params->ignore_id_layers) { + for (int i = 0; i < 4; i++) { + existing_id_layers[i] = (int *)CustomData_get_layer(cdatas[i], CD_MESH_ID); + + if (existing_id_layers[i]) { + id_garbage_threshold += tots[i]; + + use_exist_ids |= 1 << i; + } + } + } + + use_exist_ids &= bm->idmap.flag; + + bm_init_idmap_cdlayers(bm); + } + + id_garbage_threshold *= 5; + + int *cd_shape_key_offset = static_cast( + tot_shape_keys ? MEM_mallocN(sizeof(int) * tot_shape_keys, "cd_shape_key_offset") : nullptr); + /* Only copy these values over if the source mesh is flagged to be using them. * Even if `bm` has these layers, they may have been added from another mesh, when `!is_new`. */ - const int cd_shape_key_offset = tot_shape_keys ? CustomData_get_offset(&bm->vdata, CD_SHAPEKEY) : - -1; + -1; const int cd_shape_keyindex_offset = is_new && (tot_shape_keys || params->add_key_index) ? CustomData_get_offset(&bm->vdata, CD_SHAPE_KEYINDEX) : -1; + for (int i = 0; i < tot_shape_keys; i++) { + int idx = CustomData_get_layer_index_n(&bm->vdata, CD_SHAPEKEY, i); + cd_shape_key_offset[i] = bm->vdata.layers[idx].offset; + } + const bool *select_vert = (const bool *)CustomData_get_layer_named( &me->vdata, CD_PROP_BOOL, ".select_vert"); const bool *select_edge = (const bool *)CustomData_get_layer_named( @@ -396,6 +649,17 @@ void BM_mesh_bm_from_me(BMesh *bm, const Mesh *me, const struct BMeshFromMeshPar /* Copy Custom Data */ CustomData_to_bmesh_block(&mesh_vdata, &bm->vdata, i, &v->head.data, true); + bm_elem_check_toolflags(bm, (BMElem *)v); + + if (has_ids & BM_VERT) { + if ((use_exist_ids & BM_VERT) && !IS_GARBAGE_ID(existing_id_layers[0][i])) { + bm_assign_id(bm, (BMElem *)v, existing_id_layers[0][i], false); + } + else { + bm_alloc_id(bm, (BMElem *)v); + } + } + /* Set shape key original index. */ if (cd_shape_keyindex_offset != -1) { BM_ELEM_CD_SET_INT(v, cd_shape_keyindex_offset, i); @@ -403,9 +667,10 @@ void BM_mesh_bm_from_me(BMesh *bm, const Mesh *me, const struct BMeshFromMeshPar /* Set shape-key data. */ if (tot_shape_keys) { - float(*co_dst)[3] = (float(*)[3])BM_ELEM_CD_GET_VOID_P(v, cd_shape_key_offset); - for (int j = 0; j < tot_shape_keys; j++, co_dst++) { - copy_v3_v3(*co_dst, shape_key_table[j][i]); + for (int j = 0; j < tot_shape_keys; j++) { + float(*co_dst)[3] = static_cast( + BM_ELEM_CD_GET_VOID_P(v, cd_shape_key_offset[j])); + copy_v3_v3(co_dst[0], shape_key_table[j][i]); } } } @@ -434,6 +699,17 @@ void BM_mesh_bm_from_me(BMesh *bm, const Mesh *me, const struct BMeshFromMeshPar /* Copy Custom Data */ CustomData_to_bmesh_block(&mesh_edata, &bm->edata, i, &e->head.data, true); + + bm_elem_check_toolflags(bm, (BMElem *)e); + + if (has_ids & BM_EDGE) { + if ((use_exist_ids & BM_EDGE) && !IS_GARBAGE_ID(existing_id_layers[1][i])) { + bm_assign_id(bm, (BMElem *)e, existing_id_layers[1][i], false); + } + else { + bm_alloc_id(bm, (BMElem *)e); + } + } } if (is_new) { bm->elem_index_dirty &= ~BM_EDGE; /* Added in order, clear dirty flag. */ @@ -493,11 +769,31 @@ void BM_mesh_bm_from_me(BMesh *bm, const Mesh *me, const struct BMeshFromMeshPar /* Save index of corresponding #MLoop. */ CustomData_to_bmesh_block(&mesh_ldata, &bm->ldata, j++, &l_iter->head.data, true); + + if (has_ids & BM_LOOP) { + if ((use_exist_ids & BM_LOOP) && !IS_GARBAGE_ID(existing_id_layers[2][j - 1])) { + bm_assign_id(bm, (BMElem *)l_iter, existing_id_layers[2][j - 1], false); + } + else { + bm_alloc_id(bm, (BMElem *)l_iter); + } + } } while ((l_iter = l_iter->next) != l_first); /* Copy Custom Data */ CustomData_to_bmesh_block(&mesh_pdata, &bm->pdata, i, &f->head.data, true); + bm_elem_check_toolflags(bm, (BMElem *)f); + + if (has_ids & BM_FACE) { + if ((use_exist_ids & BM_FACE) && !IS_GARBAGE_ID(existing_id_layers[3][i])) { + bm_assign_id(bm, (BMElem *)f, existing_id_layers[3][i], false); + } + else { + bm_alloc_id(bm, (BMElem *)f); + } + } + if (params->calc_face_normal) { BM_face_normal_update(f); } @@ -506,6 +802,175 @@ void BM_mesh_bm_from_me(BMesh *bm, const Mesh *me, const struct BMeshFromMeshPar bm->elem_index_dirty &= ~(BM_FACE | BM_LOOP); /* Added in order, clear dirty flag. */ } + if (check_id_unqiue) { + bm_update_idmap_cdlayers(bm); + + // validate IDs + + // first clear idmap, we want it to have the first elements + // in each id run, not the last + if (bm->idmap.map) { + memset(bm->idmap.map, 0, sizeof(void *) * bm->idmap.map_size); + } + else if (bm->idmap.ghash) { + BLI_ghash_free(bm->idmap.ghash, nullptr, nullptr); + bm->idmap.ghash = BLI_ghash_ptr_new("bm->idmap.ghash"); + } + + int iters[] = {BM_VERTS_OF_MESH, BM_EDGES_OF_MESH, -1, BM_FACES_OF_MESH}; + +#ifdef WITH_BM_ID_FREELIST + uint max_id = 0; +#endif + + // find first element in each id run and assign to map + for (int i = 0; i < 4; i++) { + int type = 1 << i; + int iter = iters[i]; + + if (!(bm->idmap.flag & type)) { + continue; + } + + if (iter == -1) { + iter = BM_FACES_OF_MESH; + } + + BMElem *elem; + BMIter iterstate; + BM_ITER_MESH (elem, &iterstate, bm, iter) { + if (i == 2) { // loops + BMFace *f = (BMFace *)elem; + BMLoop *l = f->l_first; + + do { + uint id = (uint)BM_ELEM_GET_ID(bm, (BMElem *)l); +#ifdef WITH_BM_ID_FREELIST + max_id = MAX2(max_id, i); +#endif + if (!BM_ELEM_FROM_ID(bm, id)) { + bm_assign_id_intern(bm, (BMElem *)l, id); + } + } while ((l = l->next) != f->l_first); + } + else { + uint id = (uint)BM_ELEM_GET_ID(bm, elem); +#ifdef WITH_BM_ID_FREELIST + max_id = MAX2(max_id, i); +#endif + + if (!BM_ELEM_FROM_ID(bm, id)) { + bm_assign_id_intern(bm, elem, id); + } + } + } + } + + // now assign new IDs where necessary + + for (int i = 0; i < 4; i++) { + int type = 1 << i; + int iter = iters[i]; + + if (!(bm->idmap.flag & type)) { + continue; + } + + if (iter == -1) { + iter = BM_FACES_OF_MESH; + } + + BMElem *elem; + BMIter iterstate; + + BM_ITER_MESH (elem, &iterstate, bm, iter) { + if (i == 2) { // loops + BMFace *f = (BMFace *)elem; + BMLoop *l = f->l_first; + + do { + uint id = (uint)BM_ELEM_GET_ID(bm, (BMElem *)l); + + if (BM_ELEM_FROM_ID(bm, id) != (BMElem *)l) { + bm_alloc_id(bm, (BMElem *)l); + } + } while ((l = l->next) != f->l_first); + } + else { + uint id = (uint)BM_ELEM_GET_ID(bm, elem); + + if (BM_ELEM_FROM_ID(bm, id) != elem) { + bm_alloc_id(bm, elem); + + id = (uint)BM_ELEM_GET_ID(bm, elem); +#ifdef WITH_BM_ID_FREELIST + max_id = MAX2(max_id, id); +#endif + } + } + } + } + +#ifdef WITH_BM_ID_FREELIST + max_id = MAX2(bm->idmap.maxid, max_id); + bm->idmap.maxid = max_id; +#endif + } + +#ifdef WITH_BM_ID_FREELIST + /*ensure correct id freelist*/ + if (bm->idmap.flag & BM_HAS_IDS) { + bm_update_idmap_cdlayers(bm); + bm_free_ids_check(bm, bm->idmap.maxid); + + MEM_SAFE_FREE(bm->idmap.freelist); + bm->idmap.freelist_len = 0; + bm->idmap.freelist_size = 0; + bm->idmap.freelist = nullptr; + + memset(bm->idmap.free_ids, 0, bm->idmap.free_ids_size * sizeof(*bm->idmap.free_ids)); + + BLI_mempool_iter miter; + for (int i = 0; i < 4; i++) { + int htype = 1 << i; + + if (!(bm->idmap.flag & htype)) { + continue; + } + + BLI_mempool *pool = (&bm->vpool)[i]; + BLI_mempool_iternew(pool, &miter); + BMElem *elem = (BMElem *)BLI_mempool_iterstep(&miter); + +# if 0 + for (; elem; elem = (BMElem *)BLI_mempool_iterstep(&miter)) { + uint id = (uint)BM_ELEM_GET_ID(bm, elem); + + if (id > bm->idmap.maxid) { + printf("%s: corrupted id: %d > maxid(%d)\n", __func__, (int)id, (int)bm->idmap.maxid); + //BM_ELEM_CD_SET_INT(elem, bm->idmap.cd_id_off[elem->head.htype], 0); + bm_alloc_id(bm, elem); + } + } +# endif + + for (; elem; elem = (BMElem *)BLI_mempool_iterstep(&miter)) { + uint id = (uint)BM_ELEM_GET_ID(bm, elem); + + if ((id >> 2UL) < bm->idmap.free_ids_size) { + BLI_BITMAP_SET(bm->idmap.free_ids, id, true); + } + } + } + + for (uint i = 0; i < bm->idmap.maxid; i++) { + if (!BLI_BITMAP_TEST(bm->idmap.free_ids, i)) { + bm_id_freelist_push(bm, i); + } + } + } +#endif + /* -------------------------------------------------------------------- */ /* MSelect clears the array elements (to avoid adding multiple times). * @@ -540,12 +1005,31 @@ void BM_mesh_bm_from_me(BMesh *bm, const Mesh *me, const struct BMeshFromMeshPar else { BM_select_history_clear(bm); } + + if (params->copy_temp_cdlayers) { + bm_mark_temp_cdlayers(bm); + } + + if (bm->idmap.flag & BM_HAS_IDS) { + CustomData *cdatas[] = {&bm->vdata, &bm->edata, &bm->ldata, &bm->pdata}; + + for (int i = 0; i < 4; i++) { + int idx = CustomData_get_layer_index(cdatas[i], CD_MESH_ID); + + if (idx >= 0) { + // set layer flags + cdatas[i]->layers[idx].flag |= CD_FLAG_TEMPORARY | CD_FLAG_ELEM_NOCOPY; + } + } + } + + MEM_SAFE_FREE(cd_shape_key_offset); } /** * \brief BMesh -> Mesh */ -static BMVert **bm_to_mesh_vertex_map(BMesh *bm, int ototvert) +BMVert **bm_to_mesh_vertex_map(BMesh *bm, int ototvert) { const int cd_shape_keyindex_offset = CustomData_get_offset(&bm->vdata, CD_SHAPE_KEYINDEX); BMVert **vertMap = nullptr; @@ -668,7 +1152,7 @@ static BMVert **bm_to_mesh_vertex_map(BMesh *bm, int ototvert) * Returns custom-data shape-key index from a key-block or -1 * \note could split this out into a more generic function. */ -static int bm_to_mesh_shape_layer_index_from_kb(BMesh *bm, KeyBlock *currkey) +int bm_to_mesh_shape_layer_index_from_kb(BMesh *bm, KeyBlock *currkey) { int i; int j = 0; @@ -998,7 +1482,8 @@ static void convert_bmesh_selection_flags_to_mesh_attributes(BMesh &bm, } } -void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *me, const struct BMeshToMeshParams *params) +void BM_mesh_bm_to_me( + Main *bmain, Object *ob, BMesh *bm, Mesh *me, const struct BMeshToMeshParams *params) { using namespace blender; BMVert *v, *eve; @@ -1007,10 +1492,40 @@ void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *me, const struct BMeshToMesh BMIter iter; int i, j; + CustomData *srcdatas[] = {&bm->vdata, &bm->edata, &bm->ldata, &bm->pdata}; + + if (params->copy_temp_cdlayers) { + bm_unmark_temp_cdlayers(bm); + } + + // ensure multires space is correct + if (bm->haveMultiResSettings && bm->multiresSpace != MULTIRES_SPACE_TANGENT) { + BM_enter_multires_space(ob, bm, MULTIRES_SPACE_TANGENT); + } + const int cd_shape_keyindex_offset = CustomData_get_offset(&bm->vdata, CD_SHAPE_KEYINDEX); const int ototvert = me->totvert; + if (me->key && (cd_shape_keyindex_offset != -1)) { + /* Keep the old verts in case we are working on* a key, which is done at the end. */ + + /* Use the array in-place instead of duplicating the array. */ +#if 0 +# if 0 + oldverts = MEM_dupallocN(me->verts().data()); +# else + oldverts = me->mvert; + me->mvert = nullptr; + CustomData_update_typemap(&me->vdata); + CustomData_set_layer(&me->vdata, CD_MVERT, nullptr); +# endif +#endif + } + + // undo mesh? + // bool non_id_mesh = GS(me->id.name) != ID_ME; + /* Free custom data. */ CustomData_free(&me->vdata, me->totvert); CustomData_free(&me->edata, me->totedge); @@ -1030,6 +1545,7 @@ void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *me, const struct BMeshToMesh me->totface = 0; me->act_face = -1; + int id_flags[4] = {-1, -1, -1, -1}; /* Mark UV selection layers which are all false as 'nocopy'. */ for (const int layer_index : IndexRange(CustomData_number_of_layers(&bm->ldata, CD_PROP_FLOAT2))) { @@ -1081,14 +1597,29 @@ void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *me, const struct BMeshToMesh { CustomData_MeshMasks mask = CD_MASK_MESH; CustomData_MeshMasks_update(&mask, ¶ms->cd_mask_extra); + + /* Copy id layers? temporarily clear cd_temporary and cd_flag_elem_nocopy flags. */ + if (!params->ignore_mesh_id_layers) { + for (int i = 0; i < 4; i++) { + int idx = CustomData_get_layer_index(srcdatas[i], CD_MESH_ID); + + if (idx >= 0) { + id_flags[i] = srcdatas[i]->layers[idx].flag; + srcdatas[i]->layers[idx].flag &= ~(CD_FLAG_TEMPORARY | CD_FLAG_ELEM_NOCOPY); + } + } + } + CustomData_copy(&bm->vdata, &me->vdata, mask.vmask, CD_SET_DEFAULT, me->totvert); CustomData_copy(&bm->edata, &me->edata, mask.emask, CD_SET_DEFAULT, me->totedge); CustomData_copy(&bm->ldata, &me->ldata, mask.lmask, CD_SET_DEFAULT, me->totloop); CustomData_copy(&bm->pdata, &me->pdata, mask.pmask, CD_SET_DEFAULT, me->totpoly); } - CustomData_add_layer_named( - &me->vdata, CD_PROP_FLOAT3, CD_CONSTRUCT, nullptr, me->totvert, "position"); + if (CustomData_get_named_layer_index(&me->vdata, CD_PROP_FLOAT3, "position") == -1) { + CustomData_add_layer_named( + &me->vdata, CD_PROP_FLOAT3, CD_CONSTRUCT, nullptr, me->totvert, "position"); + } CustomData_add_layer(&me->edata, CD_MEDGE, CD_SET_DEFAULT, nullptr, me->totedge); CustomData_add_layer(&me->ldata, CD_MLOOP, CD_SET_DEFAULT, nullptr, me->totloop); CustomData_add_layer(&me->pdata, CD_MPOLY, CD_SET_DEFAULT, nullptr, me->totpoly); @@ -1108,8 +1639,6 @@ void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *me, const struct BMeshToMesh i = 0; BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) { - copy_v3_v3(positions[i], v->co); - if (BM_elem_flag_test(v, BM_ELEM_HIDDEN)) { need_hide_vert = true; } @@ -1122,6 +1651,8 @@ void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *me, const struct BMeshToMesh /* Copy over custom-data. */ CustomData_from_bmesh_block(&bm->vdata, &me->vdata, v->head.data, i); + copy_v3_v3(positions[i], v->co); + i++; BM_CHECK_ELEMENT(v); @@ -1271,7 +1802,7 @@ void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *me, const struct BMeshToMesh } if (vertMap) { - MEM_freeN(vertMap); + MEM_freeN(static_cast(vertMap)); } } @@ -1321,6 +1852,20 @@ void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *me, const struct BMeshToMesh /* Topology could be changed, ensure #CD_MDISPS are ok. */ multires_topology_changed(me); + + // restore original cd layer flags to bmesh id layers + if (!params->ignore_mesh_id_layers) { + for (int i = 0; i < 4; i++) { + int idx = CustomData_get_layer_index(srcdatas[i], CD_MESH_ID); + if (idx >= 0) { + srcdatas[i]->layers[idx].flag = id_flags[i]; + } + } + } + + if (params && params->copy_temp_cdlayers) { + bm_mark_temp_cdlayers(bm); + } } namespace blender { @@ -1538,6 +2083,7 @@ void BM_mesh_bm_to_me_for_eval(BMesh *bm, Mesh *me, const CustomData_MeshMasks * CustomData_MeshMasks_update(&mask, cd_mask_extra); } mask.vmask &= ~CD_MASK_SHAPEKEY; + CustomData_merge(&bm->vdata, &me->vdata, mask.vmask, CD_CONSTRUCT, me->totvert); CustomData_merge(&bm->edata, &me->edata, mask.emask, CD_CONSTRUCT, me->totedge); CustomData_merge(&bm->ldata, &me->ldata, mask.lmask, CD_CONSTRUCT, me->totloop); diff --git a/source/blender/bmesh/intern/bmesh_mesh_convert.h b/source/blender/bmesh/intern/bmesh_mesh_convert.h index ef968594998..c98abf35d58 100644 --- a/source/blender/bmesh/intern/bmesh_mesh_convert.h +++ b/source/blender/bmesh/intern/bmesh_mesh_convert.h @@ -28,8 +28,12 @@ struct Main; struct Mesh; struct BMeshFromMeshParams { + /* automatically create shapekey layers */ + uint create_shapekey_layers; + bool calc_face_normal; bool calc_vert_normal; + /* add a vertex CD_SHAPE_KEYINDEX layer */ bool add_key_index; /* set vertex coordinates from the shapekey */ @@ -37,7 +41,11 @@ struct BMeshFromMeshParams { /* define the active shape key (index + 1) */ int active_shapekey; struct CustomData_MeshMasks cd_mask_extra; + uint copy_temp_cdlayers : 1; + uint ignore_id_layers : 1; }; + +struct Object; /** * \brief Mesh -> BMesh * \param bm: The mesh to write into, while this is typically a newly created BMesh, @@ -48,8 +56,10 @@ struct BMeshFromMeshParams { * * \warning This function doesn't calculate face normals. */ -void BM_mesh_bm_from_me(BMesh *bm, const struct Mesh *me, const struct BMeshFromMeshParams *params) - ATTR_NONNULL(1, 3); +void BM_mesh_bm_from_me(struct Object *ob, + BMesh *bm, + const struct Mesh *me, + const struct BMeshFromMeshParams *params) ATTR_NONNULL(2, 4); struct BMeshToMeshParams { /** Update object hook indices & vertex parents. */ @@ -70,15 +80,20 @@ struct BMeshToMeshParams { */ bool active_shapekey_to_mvert; struct CustomData_MeshMasks cd_mask_extra; + uint copy_temp_cdlayers : 1; + uint ignore_mesh_id_layers : 1; }; +void BM_enter_multires_space(struct Object *ob, struct BMesh *bm, int space); + /** * \param bmain: May be NULL in case \a calc_object_remap parameter option is not set. */ void BM_mesh_bm_to_me(struct Main *bmain, + struct Object *ob, BMesh *bm, struct Mesh *me, - const struct BMeshToMeshParams *params) ATTR_NONNULL(2, 3, 4); + const struct BMeshToMeshParams *params) ATTR_NONNULL(3, 4, 5); /** * A version of #BM_mesh_bm_to_me intended for getting the mesh diff --git a/source/blender/bmesh/intern/bmesh_mesh_convert_threaded.cc b/source/blender/bmesh/intern/bmesh_mesh_convert_threaded.cc new file mode 100644 index 00000000000..1e42f690c0b --- /dev/null +++ b/source/blender/bmesh/intern/bmesh_mesh_convert_threaded.cc @@ -0,0 +1,1374 @@ +#if 0 +# include "MEM_guardedalloc.h" + +# include "BLI_compiler_attrs.h" +# include "BLI_compiler_compat.h" +# include "BLI_index_range.hh" +# include "BLI_map.hh" +# include "BLI_math_vec_types.hh" +# include "BLI_set.hh" +# include "BLI_task.h" +# include "BLI_task.hh" +# include "BLI_vector.hh" + +# include "DNA_key_types.h" +# include "DNA_mesh_types.h" +# include "DNA_meshdata_types.h" +# include "DNA_modifier_types.h" +# include "DNA_object_types.h" + +# include "BKE_customdata.h" +# include "BKE_mesh.h" +# include "BKE_mesh_runtime.h" +# include "BKE_multires.h" + +# include "BKE_key.h" +# include "BKE_main.h" + +# include "DEG_depsgraph_query.h" + +# include "atomic_ops.h" +# include "bmesh.h" +# include "intern/bmesh_private.h" /* For element checking. */ + +using blender::float2; +using blender::float3; +using blender::IndexRange; +using blender::Map; +using blender::Set; +using blender::Vector; +using namespace blender::threading; + +extern "C" void BM_mesh_bm_to_me_threaded( + Main *bmain, Object *ob, BMesh *bm, Mesh *me, const struct BMeshToMeshParams *params) +{ + BM_mesh_elem_index_ensure(bm, BM_VERT | BM_EDGE | BM_FACE); + BM_mesh_elem_table_ensure(bm, BM_VERT | BM_EDGE | BM_FACE); + + CustomData_reset(&me->vdata); + CustomData_reset(&me->edata); + CustomData_reset(&me->ldata); + CustomData_reset(&me->pdata); + + me->totvert = bm->totvert; + me->totedge = bm->totedge; + me->totloop = bm->totloop; + me->totpoly = bm->totface; + me->totface = 0; + + CustomData_merge(&bm->vdata, + &me->vdata, + params->cd_mask_extra.vmask | CD_MASK_MESH.vmask, + CD_CONSTRUCT, + bm->totvert); + CustomData_merge(&bm->edata, + &me->edata, + params->cd_mask_extra.emask | CD_MASK_MESH.emask, + CD_CONSTRUCT, + bm->totedge); + CustomData_merge(&bm->ldata, + &me->ldata, + params->cd_mask_extra.lmask | CD_MASK_MESH.lmask, + CD_CONSTRUCT, + bm->totloop); + CustomData_merge(&bm->pdata, + &me->pdata, + params->cd_mask_extra.pmask | CD_MASK_MESH.pmask, + CD_CONSTRUCT, + bm->totface); + + MVert *mvert = me->mvert = static_cast( + CustomData_add_layer(&me->vdata, CD_MVERT, CD_CONSTRUCT, nullptr, bm->totvert)); + MEdge *medge = static_cast( + CustomData_add_layer(&me->vdata, CD_MEDGE, CD_CONSTRUCT, nullptr, bm->totedge)); + MLoop *mloop = static_cast( + CustomData_add_layer(&me->vdata, CD_MLOOP, CD_CONSTRUCT, nullptr, bm->totloop)); + MPoly *mpoly = static_cast( + CustomData_add_layer(&me->vdata, CD_MPOLY, CD_CONSTRUCT, nullptr, bm->totface)); + float(*normals)[3] = BKE_mesh_vertex_normals_for_write(me); + + bool *hide_vert = static_cast( + CustomData_get_layer_named(&me->vdata, CD_PROP_BOOL, ".hide_vert")); + bool *hide_edge = static_cast( + CustomData_get_layer_named(&me->vdata, CD_PROP_BOOL, ".hide_edge")); + bool *hide_poly = static_cast( + CustomData_get_layer_named(&me->pdata, CD_PROP_BOOL, ".hide_poly")); + + parallel_for(IndexRange(bm->totvert), 512, [&](IndexRange range) { + for (int i : range) { + MVert *mv = mvert + i; + BMVert *v = bm->vtable[i]; + + copy_v3_v3(mv->co, v->co); + copy_v3_v3(normals[i], v->no); + + CustomData_from_bmesh_block(&bm->vdata, &me->vdata, v->head.data, i); + + if (hide_vert) { + hide_vert[i] = v->head.hflag & BM_ELEM_HIDDEN; + } + } + }); + + parallel_for(IndexRange(bm->totedge), 512, [&](IndexRange range) { + for (int i : range) { + MEdge *med = medge + i; + BMEdge *e = bm->etable[i]; + + med->v1 = e->v1->head.index; + med->v2 = e->v2->head.index; + + CustomData_from_bmesh_block(&bm->edata, &me->edata, e->head.data, i); + + if (hide_edge) { + hide_edge[i] = e->head.hflag & BM_ELEM_HIDDEN; + } + } + }); + + BMIter iter; + BMFace *f; + int loop_i = 0; + + BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { + BMLoop *l = f->l_first; + do { + l->head.index = loop_i++; + } while ((l = l->next) != f->l_first); + } + + int *material_index = static_cast( + CustomData_get_layer_named(&me->pdata, CD_PROP_INT32, "material_index")); + + parallel_for(IndexRange(bm->totface), 512, [&](IndexRange range) { + for (int i : range) { + MPoly *mp = mpoly + i; + BMFace *f = bm->ftable[i]; + + mp->loopstart = f->l_first->head.index; + mp->totloop = 0; + mp->flag = 0; + + if (material_index) { + material_index[i] = f->mat_nr; + } + + CustomData_from_bmesh_block(&bm->pdata, &me->pdata, f->head.data, i); + if (hide_poly) { + hide_poly[i] = f->head.hflag & BM_ELEM_HIDDEN; + } + + BMLoop *l = f->l_first; + do { + int l_i = l->head.index; + MLoop *ml = mloop + l_i; + + ml->v = l->v->head.index; + ml->e = l->e->head.index; + + CustomData_from_bmesh_block(&bm->ldata, &me->ldata, l->head.data, l_i); + mp->totloop++; + } while ((l = l->next) != f->l_first); + } + }); +} +#endif + +#if 0 +# include "DNA_key_types.h" +# include "DNA_mesh_types.h" +# include "DNA_meshdata_types.h" +# include "DNA_modifier_types.h" +# include "DNA_object_types.h" + +# include "MEM_guardedalloc.h" + +# include "BLI_alloca.h" +# include "BLI_compiler_attrs.h" +# include "BLI_listbase.h" +# include "BLI_math_vector.h" +# include "BLI_task.h" +# include "BLI_threads.h" + +# include "BKE_customdata.h" +# include "BKE_mesh.h" +# include "BKE_mesh_runtime.h" +# include "BKE_multires.h" + +# include "BKE_key.h" +# include "BKE_main.h" + +# include "DEG_depsgraph_query.h" + +# include "bmesh.h" +# include "intern/bmesh_private.h" /* For element checking. */ + +# include "BLI_task.h" + +# include "atomic_ops.h" + +# define ECHUNK 512 +# define VCHUNK 512 +# define FCHUNK 512 +# define LCHUNK 1024 + +typedef struct BMThreadData { + BMesh *bm; + Object *ob; + const Mesh *me; + + struct BMeshFromMeshParams *params; + + void **vdata, **edata, **ldata, **fdata; + int totdv, totde, totdl, totdf; + int vsize, esize, lsize, fsize; + + int vchunk, echunk, lchunk, fchunk; + + BMVert **verts; + BMEdge **edges; + BMLoop **loops; + BMFace **faces; + + float (**shape_key_table)[3]; + int tot_shape_keys; + + int cd_vert_bweight; + int cd_edge_bweight; + int cd_crease; + + int cdvsize, cdesize, cdlsize, cdfsize; + + // chunk sizes + int totcv, totce, totcl, totcf; +} BMThreadData; + +# define ELEM_NEXT(type, ptr, size) ((type *)(((char *)ptr) + size)) + +static void bm_vert_task(void *__restrict userdata, + const int n, + const TaskParallelTLS *__restrict tls) +{ + BMThreadData *data = userdata; + BMesh *bm = data->bm; + const Mesh *me = data->me; + + int starti = n * VCHUNK; + + int ilen = starti + VCHUNK > bm->totvert ? bm->totvert - starti : VCHUNK; + MVert *mv = me->mvert + starti; + BMVert *v = data->verts[n]; + char *cdblock = data->vdata ? (char *)data->vdata[n] : NULL; + + for (int i = 0; i < ilen; i++, mv++) { + if (cdblock) { + v->head.data = (void *)cdblock; + cdblock += data->cdvsize; + } + else { + v->head.data = NULL; + } + + v->head.htype = BM_VERT; + v->head.hflag = BM_vert_flag_from_mflag(mv->flag); + v->head.api_flag = 0; + + copy_v3_v3(v->co, mv->co); + // XXX normal_short_to_float_v3(v->no, mv->no); + + v->e = NULL; + v->head.index = i + starti; + v = ELEM_NEXT(BMVert, v, data->vsize); + } + + if (data->vdata) { + v = data->verts[n]; + for (int i = 0; i < ilen; i++) { + CustomData_to_bmesh_block(&me->vdata, &bm->vdata, i + starti, &v->head.data, true); + v = ELEM_NEXT(BMVert, v, data->vsize); + } + } +} + +static void bm_edge_task(void *__restrict userdata, + const int n, + const TaskParallelTLS *__restrict tls) +{ + BMThreadData *data = userdata; + BMesh *bm = data->bm; + const Mesh *me = data->me; + + int starti = n * ECHUNK; + + int ilen = starti + ECHUNK > bm->totedge ? bm->totedge - starti : ECHUNK; + MEdge *med = me->medge + starti; + BMEdge *e = data->edges[n]; + char *cdblock = data->edata ? (char *)data->edata[n] : NULL; + + for (int i = 0; i < ilen; i++, med++) { + if (cdblock) { + e->head.data = (void *)cdblock; + cdblock += data->cdesize; + } + else { + e->head.data = NULL; + } + + e->head.htype = BM_EDGE; + e->head.hflag = BM_edge_flag_from_mflag(med->flag); + e->head.api_flag = 0; + + e->v1 = &data->verts[med->v1 / VCHUNK][med->v1 % VCHUNK]; + e->v2 = &data->verts[med->v2 / VCHUNK][med->v2 % VCHUNK]; + + e->l = NULL; + e->v1_disk_link.next = e->v1_disk_link.prev = NULL; + e->v2_disk_link.next = e->v2_disk_link.prev = NULL; + + e = ELEM_NEXT(BMEdge, e, data->esize); + } + + if (data->edata) { + e = data->edges[n]; + for (int i = 0; i < ilen; i++) { + CustomData_to_bmesh_block(&me->edata, &bm->edata, i + starti, &e->head.data, true); + e = ELEM_NEXT(BMEdge, e, data->esize); + } + } +} + +static void bm_loop_task(void *__restrict userdata, + const int n, + const TaskParallelTLS *__restrict tls) +{ + BMThreadData *data = userdata; + BMesh *bm = data->bm; + const Mesh *me = data->me; + + int starti = n * LCHUNK; + + int ilen = starti + LCHUNK > bm->totloop ? bm->totloop - starti : LCHUNK; + MLoop *ml = me->mloop + starti; + BMLoop *l = data->loops[n]; + char *cdblock = data->ldata ? (char *)data->ldata[n] : NULL; + + for (int i = 0; i < ilen; i++, ml++) { + if (cdblock) { + l->head.data = (void *)cdblock; + cdblock += data->cdlsize; + } + else { + l->head.data = NULL; + } + + l->head.htype = BM_LOOP; + l->head.hflag = 0; + l->head.api_flag = 0; + + l->v = data->verts[ml->v / VCHUNK] + (ml->v % VCHUNK); + l->e = data->edges[ml->e / ECHUNK] + (ml->e % ECHUNK); + l->radial_next = l->radial_prev = l->next = l->prev = NULL; + l->f = NULL; + + l = ELEM_NEXT(BMLoop, l, data->lsize); + } + + if (data->ldata) { + l = data->loops[n]; + for (int i = 0; i < ilen; i++) { + CustomData_to_bmesh_block(&me->ldata, &bm->ldata, i + starti, &l->head.data, true); + l = ELEM_NEXT(BMLoop, l, data->lsize); + } + } +} + +static void bm_face_task(void *__restrict userdata, + const int n, + const TaskParallelTLS *__restrict tls) +{ + BMThreadData *data = userdata; + BMesh *bm = data->bm; + const Mesh *me = data->me; + + int starti = n * FCHUNK; + + int ilen = starti + FCHUNK > bm->totface ? bm->totface - starti : FCHUNK; + MPoly *mp = me->mpoly + starti; + BMFace *f = data->faces[n]; + char *cdblock = data->fdata ? (char *)data->fdata[n] : NULL; + + for (int i = 0; i < ilen; i++, mp++) { + if (cdblock) { + f->head.data = (void *)cdblock; + cdblock += data->cdfsize; + } + else { + f->head.data = NULL; + } + + f->head.htype = BM_FACE; + f->head.hflag = BM_face_flag_from_mflag(mp->flag); + f->head.api_flag = 0; + + f->len = mp->totloop; + f->mat_nr = mp->mat_nr; + zero_v3(f->no); + + int li = mp->loopstart; + BMLoop *lastl = NULL; + + for (int j = 0; j < mp->totloop; j++, li++) { + BMLoop *l = data->loops[li / LCHUNK] + (li % LCHUNK); + + l->f = f; + + if (j == 0) { + f->l_first = l; + } + else { + lastl->next = l; + l->prev = lastl; + } + + lastl = l; + } + + lastl->next = f->l_first; + f->l_first->prev = lastl; + + f = ELEM_NEXT(BMFace, f, data->fsize); + } + + if (data->fdata) { + f = data->faces[n]; + for (int i = 0; i < ilen; i++) { + CustomData_to_bmesh_block(&me->pdata, &bm->pdata, i + starti, &f->head.data, true); + f = ELEM_NEXT(BMFace, f, data->fsize); + } + } +} + +static void bm_mesh_cd_flag_apply(BMesh *bm, const char cd_flag) +{ + /* CustomData_bmesh_init_pool() must run first */ + BLI_assert(bm->vdata.totlayer == 0 || bm->vdata.pool != NULL); + BLI_assert(bm->edata.totlayer == 0 || bm->edata.pool != NULL); + BLI_assert(bm->pdata.totlayer == 0 || bm->pdata.pool != NULL); + + if (cd_flag & ME_CDFLAG_VERT_BWEIGHT) { + if (!CustomData_has_layer(&bm->vdata, CD_BWEIGHT)) { + CustomData_add_layer(&bm->vdata, CD_BWEIGHT, CD_ASSIGN, NULL, 0); + } + } + else { + if (CustomData_has_layer(&bm->vdata, CD_BWEIGHT)) { + CustomData_free_layer_active(&bm->vdata, CD_BWEIGHT, 0); + } + } + + if (cd_flag & ME_CDFLAG_EDGE_BWEIGHT) { + if (!CustomData_has_layer(&bm->edata, CD_BWEIGHT)) { + CustomData_add_layer(&bm->edata, CD_BWEIGHT, CD_ASSIGN, NULL, 0); + } + } + else { + if (CustomData_has_layer(&bm->edata, CD_BWEIGHT)) { + CustomData_free_layer_active(&bm->edata, CD_BWEIGHT, 0); + } + } + + if (cd_flag & ME_CDFLAG_EDGE_CREASE) { + if (!CustomData_has_layer(&bm->edata, CD_CREASE)) { + CustomData_add_layer(&bm->edata, CD_CREASE, CD_ASSIGN, NULL, 0); + } + } + else { + if (CustomData_has_layer(&bm->edata, CD_CREASE)) { + CustomData_free_layer_active(&bm->edata, CD_CREASE, 0); + } + } +} + +BMesh *BM_mesh_bm_from_me_threaded(BMesh *bm, + Object *ob, + const Mesh *me, + const struct BMeshFromMeshParams *params) +{ + if (!bm) { + bm = MEM_callocN(sizeof(BMesh), "BM_mesh_bm_from_me_threaded bm"); + } + else { + BM_mesh_data_free(bm); + memset((void *)bm, 0, sizeof(*bm)); + } + const bool is_new = true; + + bm->totvert = me->totvert; + bm->totedge = me->totedge; + bm->totface = me->totpoly; + bm->totloop = me->totloop; + + bm->elem_index_dirty = bm->elem_table_dirty = BM_VERT | BM_EDGE | BM_LOOP | BM_FACE; + bm->spacearr_dirty = BM_SPACEARR_DIRTY_ALL; + + BMVert **verts; + BMEdge **edges; + BMLoop **loops; + BMFace **faces; + + void **vdata = NULL, **edata = NULL, **ldata = NULL, **fdata = NULL; + int totdv = 0, totde = 0, totdl = 0, totdf = 0; + + int totcv = 0, totce = 0, totcl = 0, totcf = 0; + + BMThreadData data = {0}; + + int vsize, esize, lsize, fsize; + + bm->vpool = BLI_mempool_create_for_tasks(sizeof(BMVert), + bm->totvert, + VCHUNK, + (void ***)&verts, + &totcv, + &vsize, + BLI_MEMPOOL_ALLOW_ITER); + bm->epool = BLI_mempool_create_for_tasks(sizeof(BMEdge), + bm->totedge, + ECHUNK, + (void ***)&edges, + &totce, + &esize, + BLI_MEMPOOL_ALLOW_ITER); + bm->lpool = BLI_mempool_create_for_tasks(sizeof(BMLoop), + bm->totloop, + LCHUNK, + (void ***)&loops, + &totcl, + &lsize, + BLI_MEMPOOL_ALLOW_ITER); + bm->fpool = BLI_mempool_create_for_tasks(sizeof(BMFace), + bm->totface, + FCHUNK, + (void ***)&faces, + &totcf, + &fsize, + BLI_MEMPOOL_ALLOW_ITER); + + data.verts = verts; + data.edges = edges; + data.loops = loops; + data.faces = faces; + + data.vsize = vsize; + data.esize = esize; + data.lsize = lsize; + data.fsize = fsize; + + data.totcv = totcv; + data.totce = totce; + data.totcl = totcl; + data.totcf = totcf; + + data.bm = bm; + data.me = me; + + // bm->vpool = BLI_mem + + KeyBlock *actkey, *block; + BMEdge *e; + BMFace *f; + float(*keyco)[3] = NULL; + int i; + CustomData_MeshMasks mask = CD_MASK_BMESH; + CustomData_MeshMasks_update(&mask, ¶ms->cd_mask_extra); + + MultiresModifierData *mmd = ob ? get_multires_modifier(NULL, ob, true) : NULL; + + if (mmd) { + bm->multires = *mmd; + bm->haveMultiResSettings = true; + bm->multiresSpace = MULTIRES_SPACE_TANGENT; + } + else { + bm->haveMultiResSettings = false; + } + + CustomData_copy(&me->vdata, &bm->vdata, mask.vmask, CD_ASSIGN, CD_FLAG_NOCOPY); + CustomData_copy(&me->edata, &bm->edata, mask.emask, CD_ASSIGN, CD_FLAG_NOCOPY); + CustomData_copy(&me->ldata, &bm->ldata, mask.lmask, CD_ASSIGN, CD_FLAG_NOCOPY); + CustomData_copy(&me->pdata, &bm->pdata, mask.pmask, CD_ASSIGN, CD_FLAG_NOCOPY); + + CustomData *cds[] = {&bm->vdata, &bm->edata, &bm->ldata, &bm->pdata}; + + // clear customdata->layers[X].data pointers + for (int i = 0; i < 4; i++) { + CustomData *cd = cds[i]; + for (int j = 0; j < cd->totlayer; j++) { + cd->layers[j].data = NULL; + } + } + bm_mesh_cd_flag_apply(bm, me->cd_flag); + + data.cd_vert_bweight = CustomData_get_offset(&bm->vdata, CD_BWEIGHT); + data.cd_edge_bweight = CustomData_get_offset(&bm->edata, CD_BWEIGHT); + data.cd_crease = CustomData_get_offset(&bm->edata, CD_CREASE); + + if (bm->vdata.totlayer) { + bm->vdata.pool = BLI_mempool_create_for_tasks( + bm->vdata.totsize, bm->totvert, VCHUNK, &vdata, &totdv, &data.cdvsize, BLI_MEMPOOL_NOP); + } + if (bm->edata.totlayer) { + bm->edata.pool = BLI_mempool_create_for_tasks( + bm->edata.totsize, bm->totedge, ECHUNK, &edata, &totde, &data.cdesize, BLI_MEMPOOL_NOP); + } + if (bm->ldata.totlayer) { + bm->ldata.pool = BLI_mempool_create_for_tasks( + bm->ldata.totsize, bm->totloop, LCHUNK, &ldata, &totdl, &data.cdlsize, BLI_MEMPOOL_NOP); + } + if (bm->pdata.totlayer) { + bm->pdata.pool = BLI_mempool_create_for_tasks( + bm->pdata.totsize, bm->totface, FCHUNK, &fdata, &totdf, &data.cdfsize, BLI_MEMPOOL_NOP); + } + + data.vdata = vdata; + data.edata = edata; + data.ldata = ldata; + data.fdata = fdata; + + data.totdv = totdv; + data.totde = totde; + data.totdl = totdl; + data.totdf = totdf; + + /* -------------------------------------------------------------------- */ + /* Shape Key */ + int tot_shape_keys = 0; + if (me->key != NULL && DEG_is_original_id(&me->id)) { + /* Evaluated meshes can be topologically inconsistent with their shape keys. + * Shape keys are also already integrated into the state of the evaluated + * mesh, so considering them here would kind of apply them twice. */ + tot_shape_keys = BLI_listbase_count(&me->key->block); + + /* Original meshes must never contain a shape-key custom-data layers. + * + * This may happen if and object's mesh data is accidentally + * set to the output from the modifier stack, causing it to be an "original" ID, + * even though the data isn't fully compatible (hence this assert). + * + * This results in: + * - The newly created #BMesh having twice the number of custom-data layers. + * - When converting the #BMesh back to a regular mesh, + * At least one of the extra shape-key blocks will be created in #Mesh.key + * depending on the value of #CustomDataLayer.uid. + * + * We could support mixing both kinds of data if there is a compelling use-case for it. + * At the moment it's simplest to assume all original meshes use the key-block and meshes + * that are evaluated (through the modifier stack for example) use custom-data layers. + */ + BLI_assert(!CustomData_has_layer(&me->vdata, CD_SHAPEKEY)); + } + if (is_new == false) { + tot_shape_keys = min_ii(tot_shape_keys, CustomData_number_of_layers(&bm->vdata, CD_SHAPEKEY)); + } + + float(**shape_key_table)[3] = tot_shape_keys ? + BLI_array_alloca(shape_key_table, tot_shape_keys) : + NULL; + + if ((params->active_shapekey != 0) && tot_shape_keys > 0) { + actkey = BLI_findlink(&me->key->block, params->active_shapekey - 1); + } + else { + actkey = NULL; + } + + if (is_new) { + if (tot_shape_keys || params->add_key_index) { + CustomData_add_layer(&bm->vdata, CD_SHAPE_KEYINDEX, CD_ASSIGN, NULL, 0); + } + } + + if (tot_shape_keys) { + if (is_new) { + /* Check if we need to generate unique ids for the shape-keys. + * This also exists in the file reading code, but is here for a sanity check. */ + if (!me->key->uidgen) { + fprintf(stderr, + "%s had to generate shape key uid's in a situation we shouldn't need to! " + "(bmesh internal error)\n", + __func__); + + me->key->uidgen = 1; + for (block = me->key->block.first; block; block = block->next) { + block->uid = me->key->uidgen++; + } + } + } + + if (actkey && actkey->totelem == me->totvert) { + keyco = params->use_shapekey ? actkey->data : NULL; + if (is_new) { + bm->shapenr = params->active_shapekey; + } + } + + for (i = 0, block = me->key->block.first; i < tot_shape_keys; block = block->next, i++) { + if (is_new) { + CustomData_add_layer_named(&bm->vdata, CD_SHAPEKEY, CD_ASSIGN, NULL, 0, block->name); + int j = CustomData_get_layer_index_n(&bm->vdata, CD_SHAPEKEY, i); + bm->vdata.layers[j].uid = block->uid; + } + shape_key_table[i] = (float(*)[3])block->data; + } + } + + data.tot_shape_keys = tot_shape_keys; + data.shape_key_table = shape_key_table; + + TaskParallelSettings settings; + + BLI_parallel_range_settings_defaults(&settings); + BLI_task_parallel_range(0, data.totcv, &data, bm_vert_task, &settings); + BLI_task_parallel_range(0, data.totce, &data, bm_edge_task, &settings); + BLI_task_parallel_range(0, data.totcl, &data, bm_loop_task, &settings); + BLI_task_parallel_range(0, data.totcf, &data, bm_face_task, &settings); + + BMIter iter; + + // link edges + BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) { + bmesh_disk_edge_append(e, e->v1); + bmesh_disk_edge_append(e, e->v2); + } + + // link radial lists + i = 0; + BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { + BMLoop *l = f->l_first; + + do { + bmesh_radial_loop_append(l->e, l); + + l = l->next; + } while (l != f->l_first); + + i++; + } + + printf("totface: %d\n", i); + + bm->elem_index_dirty = BM_VERT | BM_EDGE | BM_FACE; + bm->elem_table_dirty = BM_VERT | BM_EDGE | BM_FACE; + + return bm; +} + +static void bm_unmark_temp_cdlayers(BMesh *bm) +{ + CustomData_unmark_temporary_nocopy(&bm->vdata); + CustomData_unmark_temporary_nocopy(&bm->edata); + CustomData_unmark_temporary_nocopy(&bm->ldata); + CustomData_unmark_temporary_nocopy(&bm->pdata); +} + +static void bm_mark_temp_cdlayers(BMesh *bm) +{ + CustomData_mark_temporary_nocopy(&bm->vdata); + CustomData_mark_temporary_nocopy(&bm->edata); + CustomData_mark_temporary_nocopy(&bm->ldata); + CustomData_mark_temporary_nocopy(&bm->pdata); +} + +typedef struct BMToMeTask { + Mesh *me; + BMesh *bm; + Object *ob; + Main *bmain; + const struct BMeshToMeshParams *params; + struct CustomData_MeshMasks mask; + uint64_t extra2; +} BMToMeTask; + +static void me_vert_task(void *__restrict userdata) +{ + BMToMeTask *data = (BMToMeTask *)userdata; + Mesh *me = data->me; + BMesh *bm = data->bm; + + CustomData_free(&me->vdata, me->totvert); + me->totvert = bm->totvert; + + CustomData_copy( + &bm->vdata, &me->vdata, data->mask.vmask | data->extra2, CD_SET_DEFAULT, me->totvert); + + MVert *mvert = bm->totvert ? MEM_callocN(sizeof(MVert) * bm->totvert, "bm_to_me.vert") : NULL; + CustomData_add_layer(&me->vdata, CD_MVERT, CD_ASSIGN, mvert, me->totvert); + + BMVert *v; + BMIter iter; + int i = 0; + + const int cd_vert_bweight_offset = CustomData_get_offset(&bm->vdata, CD_BWEIGHT); + + BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) { + copy_v3_v3(mvert->co, v->co); + // XXX normal_float_to_short_v3(mvert->no, v->no); + + mvert->flag = BM_vert_flag_to_mflag(v); + + /* Copy over custom-data. */ + CustomData_from_bmesh_block(&bm->vdata, &me->vdata, v->head.data, i); + + if (cd_vert_bweight_offset != -1) { + mvert->bweight = BM_ELEM_CD_GET_FLOAT_AS_UCHAR(v, cd_vert_bweight_offset); + } + + mvert++; + i++; + + BM_CHECK_ELEMENT(v); + } +} + +BLI_INLINE void bmesh_quick_edgedraw_flag(MEdge *med, BMEdge *e) +{ + /* This is a cheap way to set the edge draw, its not precise and will + * pick the first 2 faces an edge uses. + * The dot comparison is a little arbitrary, but set so that a 5 subd + * IcoSphere won't vanish but subd 6 will (as with pre-bmesh Blender). */ + + if (/* (med->flag & ME_EDGEDRAW) && */ /* Assume to be true. */ + (e->l && (e->l != e->l->radial_next)) && + (dot_v3v3(e->l->f->no, e->l->radial_next->f->no) > 0.9995f)) { + med->flag &= ~ME_EDGEDRAW; + } + else { + med->flag |= ME_EDGEDRAW; + } +} + +static void me_edge_task(void *__restrict userdata) +{ + BMToMeTask *data = (BMToMeTask *)userdata; + Mesh *me = data->me; + BMesh *bm = data->bm; + MEdge *med; + + CustomData_free(&me->edata, me->totedge); + me->totedge = bm->totedge; + + CustomData_copy( + &bm->edata, &me->edata, data->mask.emask | data->extra2, CD_SET_DEFAULT, me->totvert); + + MEdge *medge = bm->totedge ? MEM_callocN(sizeof(MEdge) * bm->totedge, "bm_to_me.edge") : NULL; + CustomData_add_layer(&me->edata, CD_MEDGE, CD_ASSIGN, medge, me->totedge); + const int cd_edge_bweight_offset = CustomData_get_offset(&bm->edata, CD_BWEIGHT); + + BMEdge *e; + BMIter iter; + int i = 0; + + const int cd_edge_crease_offset = CustomData_get_offset(&bm->edata, CD_CREASE); + + med = medge; + i = 0; + BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) { + med->v1 = BM_elem_index_get(e->v1); + med->v2 = BM_elem_index_get(e->v2); + + med->flag = BM_edge_flag_to_mflag(e); + + /* Copy over custom-data. */ + CustomData_from_bmesh_block(&bm->edata, &me->edata, e->head.data, i); + + bmesh_quick_edgedraw_flag(med, e); + + if (cd_edge_crease_offset != -1) { + med->crease = BM_ELEM_CD_GET_FLOAT_AS_UCHAR(e, cd_edge_crease_offset); + } + if (cd_edge_bweight_offset != -1) { + med->bweight = BM_ELEM_CD_GET_FLOAT_AS_UCHAR(e, cd_edge_bweight_offset); + } + + i++; + med++; + BM_CHECK_ELEMENT(e); + } +} + +static void me_face_task(void *__restrict userdata) +{ + BMToMeTask *data = (BMToMeTask *)userdata; + Mesh *me = data->me; + BMesh *bm = data->bm; + MPoly *mpoly; + MLoop *mloop; + + // set up polys + CustomData_free(&me->pdata, me->totpoly); + me->totpoly = bm->totface; + + CustomData_copy( + &bm->pdata, &me->pdata, data->mask.pmask | data->extra2, CD_SET_DEFAULT, me->totpoly); + + mpoly = bm->totface ? MEM_callocN(sizeof(MPoly) * bm->totface, "bm_to_me.poly") : NULL; + CustomData_add_layer(&me->pdata, CD_MPOLY, CD_ASSIGN, mpoly, me->totpoly); + + // set up loops + CustomData_free(&me->ldata, me->totloop); + me->totloop = bm->totloop; + + CustomData_copy( + &bm->ldata, &me->ldata, data->mask.lmask | data->extra2, CD_SET_DEFAULT, me->totloop); + + mloop = bm->totloop ? MEM_callocN(sizeof(MLoop) * bm->totloop, "bm_to_me.loop") : NULL; + CustomData_add_layer(&me->ldata, CD_MLOOP, CD_ASSIGN, mloop, me->totloop); + + // convert + + BMIter iter; + BMFace *f; + + int i, j; + i = 0; + j = 0; + BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { + BMLoop *l_iter, *l_first; + mpoly->loopstart = j; + mpoly->totloop = f->len; + mpoly->mat_nr = f->mat_nr; + mpoly->flag = BM_face_flag_to_mflag(f); + + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + mloop->e = BM_elem_index_get(l_iter->e); + mloop->v = BM_elem_index_get(l_iter->v); + + /* Copy over custom-data. */ + CustomData_from_bmesh_block(&bm->ldata, &me->ldata, l_iter->head.data, j); + + j++; + mloop++; + BM_CHECK_ELEMENT(l_iter); + BM_CHECK_ELEMENT(l_iter->e); + BM_CHECK_ELEMENT(l_iter->v); + } while ((l_iter = l_iter->next) != l_first); + + if (f == bm->act_face) { + me->act_face = i; + } + + /* Copy over custom-data. */ + CustomData_from_bmesh_block(&bm->pdata, &me->pdata, f->head.data, i); + + i++; + mpoly++; + BM_CHECK_ELEMENT(f); + } +} + +BMVert **bm_to_mesh_vertex_map(BMesh *bm, int ototvert); +int bm_to_mesh_shape_layer_index_from_kb(BMesh *bm, KeyBlock *currkey); + +typedef struct Test { + BMToMeTask *data; + int n; +} Test; +static void *test(void *userdata) +{ + Test *test = (Test *)userdata; + switch (test->n) { + case 0: + me_vert_task(test->data); + break; + case 1: + me_edge_task(test->data); + break; + case 2: + me_face_task(test->data); + break; + } + + return NULL; +} + +void BM_mesh_bm_to_me_threaded( + Main *bmain, Object *ob, BMesh *bm, Mesh *me, const struct BMeshToMeshParams *params) +{ + BMVert *eve; + BMIter iter; + + MVert *oldverts = NULL, *mvert = NULL; + const int ototvert = me->totvert; + const int cd_shape_keyindex_offset = CustomData_get_offset(&bm->vdata, CD_SHAPE_KEYINDEX); + + int i, j; + + BM_mesh_elem_index_ensure(bm, BM_VERT | BM_EDGE | BM_FACE); + + if (me->key && (cd_shape_keyindex_offset != -1)) { + /* Keep the old verts in case we are working on* a key, which is done at the end. */ + + /* Use the array in-place instead of duplicating the array. */ +# if 0 + oldverts = MEM_dupallocN(me->mvert); +# else + oldverts = me->mvert; + me->mvert = NULL; + CustomData_update_typemap(&me->vdata); + CustomData_set_layer(&me->vdata, CD_MVERT, NULL); +# endif + } + + BMToMeTask taskdata = {.params = params, .bm = bm, .me = me, .ob = ob, .bmain = bmain}; + + if (params->copy_temp_cdlayers) { + bm_unmark_temp_cdlayers(bm); + } + + // ensure multires space is correct + if (bm->haveMultiResSettings && bm->multiresSpace != MULTIRES_SPACE_TANGENT) { + BM_enter_multires_space(ob, bm, MULTIRES_SPACE_TANGENT); + } + + CustomData_MeshMasks mask = CD_MASK_MESH; + CustomData_MeshMasks_update(&mask, ¶ms->cd_mask_extra); + eCustomDataMask extra2 = !params->ignore_mesh_id_layers ? CD_MASK_MESH_ID : 0; + CustomData *srcdatas[] = {&bm->vdata, &bm->edata, &bm->ldata, &bm->pdata}; + int id_flags[4] = {-1, -1, -1, -1}; + + taskdata.mask = mask; + taskdata.extra2 = extra2; + + // copy id layers? temporarily clear cd_temporary and cd_flag_elem_nocopy flags + if (!params->ignore_mesh_id_layers) { + + for (int i = 0; i < 4; i++) { + int idx = CustomData_get_layer_index(srcdatas[i], CD_MESH_ID); + if (idx >= 0) { + id_flags[i] = srcdatas[i]->layers[idx].flag; + srcdatas[i]->layers[idx].flag &= ~(CD_FLAG_TEMPORARY | CD_FLAG_ELEM_NOCOPY); + } + } + } + + me->cd_flag = BM_mesh_cd_flag_from_bmesh(bm); + +# if 0 + struct TaskGraph *taskgraph = BLI_task_graph_create(); + struct TaskNode *node; + + node = BLI_task_graph_node_create(taskgraph, me_vert_task, &taskdata, NULL); + BLI_task_graph_node_push_work(node); + + node = BLI_task_graph_node_create(taskgraph, me_edge_task, &taskdata, NULL); + BLI_task_graph_node_push_work(node); + + node = BLI_task_graph_node_create(taskgraph, me_face_task, &taskdata, NULL); + BLI_task_graph_node_push_work(node); + + BLI_task_graph_work_and_wait(taskgraph); + BLI_task_graph_free(taskgraph); +# else + ListBase threadpool; + Test datas[3] = {{&taskdata, 0}, {&taskdata, 1}, {&taskdata, 2}}; + + BLI_threadpool_init(&threadpool, test, 3); + BLI_threadpool_insert(&threadpool, &datas[0]); + BLI_threadpool_insert(&threadpool, &datas[1]); + BLI_threadpool_insert(&threadpool, &datas[2]); + BLI_threadpool_end(&threadpool); + +// BLI_threadpool_ +# endif + // undo changes to source bmesh's id layers' flags + if (!params->ignore_mesh_id_layers) { + for (int i = 0; i < 4; i++) { + int idx = CustomData_get_layer_index(srcdatas[i], CD_MESH_ID); + + if (id_flags[i] >= 0 && idx >= 0) { + srcdatas[i]->layers[idx].flag = id_flags[i]; + } + } + } + + if (me->fdata.layers) { + CustomData_free(&me->fdata, me->totface); + } + + CustomData_reset(&me->fdata); + + /* Will be overwritten with a valid value if 'dotess' is set, otherwise we + * end up with 'me->totface' and me->mface == NULL which can crash T28625. */ + me->totface = 0; + me->act_face = -1; + + BKE_mesh_update_customdata_pointers(me, 0); + + /* Patch hook indices and vertex parents. */ + if (params->calc_object_remap && (ototvert > 0)) { + BLI_assert(bmain != NULL); + Object *ob; + ModifierData *md; + BMVert **vertMap = NULL; + + for (ob = bmain->objects.first; ob; ob = ob->id.next) { + if ((ob->parent) && (ob->parent->data == me) && ELEM(ob->partype, PARVERT1, PARVERT3)) { + + if (vertMap == NULL) { + vertMap = bm_to_mesh_vertex_map(bm, ototvert); + } + + if (ob->par1 < ototvert) { + eve = vertMap[ob->par1]; + if (eve) { + ob->par1 = BM_elem_index_get(eve); + } + } + if (ob->par2 < ototvert) { + eve = vertMap[ob->par2]; + if (eve) { + ob->par2 = BM_elem_index_get(eve); + } + } + if (ob->par3 < ototvert) { + eve = vertMap[ob->par3]; + if (eve) { + ob->par3 = BM_elem_index_get(eve); + } + } + } + if (ob->data == me) { + for (md = ob->modifiers.first; md; md = md->next) { + if (md->type == eModifierType_Hook) { + HookModifierData *hmd = (HookModifierData *)md; + + if (vertMap == NULL) { + vertMap = bm_to_mesh_vertex_map(bm, ototvert); + } + + for (i = j = 0; i < hmd->indexar_num; i++) { + if (hmd->indexar[i] < ototvert) { + eve = vertMap[hmd->indexar[i]]; + + if (eve) { + hmd->indexar[j++] = BM_elem_index_get(eve); + } + } + else { + j++; + } + } + + hmd->indexar_num = j; + } + } + } + } + + if (vertMap) { + MEM_freeN(vertMap); + } + } + + /* This is called again, 'dotess' arg is used there. */ + BKE_mesh_update_customdata_pointers(me, false); + + { + BMEditSelection *selected; + me->totselect = BLI_listbase_count(&(bm->selected)); + + MEM_SAFE_FREE(me->mselect); + if (me->totselect != 0) { + me->mselect = MEM_mallocN(sizeof(MSelect) * me->totselect, "Mesh selection history"); + } + + for (i = 0, selected = bm->selected.first; selected; i++, selected = selected->next) { + if (selected->htype == BM_VERT) { + me->mselect[i].type = ME_VSEL; + } + else if (selected->htype == BM_EDGE) { + me->mselect[i].type = ME_ESEL; + } + else if (selected->htype == BM_FACE) { + me->mselect[i].type = ME_FSEL; + } + + me->mselect[i].index = BM_elem_index_get(selected->ele); + } + } + + /* See comment below, this logic is in twice. */ + + if (me->key) { + KeyBlock *currkey; + KeyBlock *actkey = BLI_findlink(&me->key->block, bm->shapenr - 1); + + float(*ofs)[3] = NULL; + + /* Go through and find any shape-key custom-data layers + * that might not have corresponding KeyBlocks, and add them if necessary. */ + for (i = 0; i < bm->vdata.totlayer; i++) { + if (bm->vdata.layers[i].type != CD_SHAPEKEY) { + continue; + } + + for (currkey = me->key->block.first; currkey; currkey = currkey->next) { + if (currkey->uid == bm->vdata.layers[i].uid) { + break; + } + } + + if (!currkey) { + currkey = BKE_keyblock_add(me->key, bm->vdata.layers[i].name); + currkey->uid = bm->vdata.layers[i].uid; + } + } + + /* Editing the base key should update others. */ + if (/* Only need offsets for relative shape keys. */ + (me->key->type == KEY_RELATIVE) && + + /* Unlikely, but the active key may not be valid if the + * BMesh and the mesh are out of sync. */ + (actkey != NULL) && + + /* Not used here, but 'oldverts' is used later for applying 'ofs'. */ + (oldverts != NULL) && + + /* Needed for referencing oldverts. */ + (cd_shape_keyindex_offset != -1)) { + + const bool act_is_basis = BKE_keyblock_is_basis(me->key, bm->shapenr - 1); + + /* Active key is a base. */ + if (act_is_basis) { + const float(*fp)[3] = actkey->data; + + ofs = MEM_callocN(sizeof(float[3]) * bm->totvert, "currkey->data"); + mvert = me->mvert; + BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, i) { + const int keyi = BM_ELEM_CD_GET_INT(eve, cd_shape_keyindex_offset); + + /* Could use 'eve->co' or 'mvert->co', they're the same at this point. */ + if (keyi != ORIGINDEX_NONE && keyi < actkey->totelem) { + sub_v3_v3v3(ofs[i], mvert->co, fp[keyi]); + } + else { + /* If there are new vertices in the mesh, we can't propagate the offset + * because it will only work for the existing vertices and not the new + * ones, creating a mess when doing e.g. subdivide + translate. */ + MEM_freeN(ofs); + ofs = NULL; + break; + } + + mvert++; + } + } + } + + for (currkey = me->key->block.first; currkey; currkey = currkey->next) { + int keyi; + const float(*ofs_pt)[3] = ofs; + float *newkey, (*oldkey)[3], *fp; + + const int currkey_uuid = bm_to_mesh_shape_layer_index_from_kb(bm, currkey); + const int cd_shape_offset = (currkey_uuid == -1) ? -1 : + CustomData_get_n_offset(&bm->vdata, + CD_SHAPEKEY, + currkey_uuid); + const bool apply_offset = (cd_shape_offset != -1) && (ofs != NULL) && (currkey != actkey) && + (bm->shapenr - 1 == currkey->relative); + + fp = newkey = MEM_callocN(me->key->elemsize * bm->totvert, "currkey->data"); + oldkey = currkey->data; + + mvert = me->mvert; + BM_ITER_MESH (eve, &iter, bm, BM_VERTS_OF_MESH) { + + if (currkey == actkey) { + copy_v3_v3(fp, eve->co); + + if (actkey != me->key->refkey) { /* Important see bug T30771. */ + if (cd_shape_keyindex_offset != -1) { + if (oldverts) { + keyi = BM_ELEM_CD_GET_INT(eve, cd_shape_keyindex_offset); + if (keyi != ORIGINDEX_NONE && keyi < currkey->totelem) { /* Valid old vertex. */ + copy_v3_v3(mvert->co, oldverts[keyi].co); + } + } + } + } + } + else if (cd_shape_offset != -1) { + /* In most cases this runs. */ + copy_v3_v3(fp, BM_ELEM_CD_GET_VOID_P(eve, cd_shape_offset)); + } + else if ((oldkey != NULL) && (cd_shape_keyindex_offset != -1) && + ((keyi = BM_ELEM_CD_GET_INT(eve, cd_shape_keyindex_offset)) != ORIGINDEX_NONE) && + (keyi < currkey->totelem)) { + /* Old method of reconstructing keys via vertices original key indices, + * currently used if the new method above fails + * (which is theoretically possible in certain cases of undo). */ + copy_v3_v3(fp, oldkey[keyi]); + } + else { + /* Fail! fill in with dummy value. */ + copy_v3_v3(fp, mvert->co); + } + + /* Propagate edited basis offsets to other shapes. */ + if (apply_offset) { + add_v3_v3(fp, *ofs_pt++); + /* Apply back new coordinates shape-keys that have offset into BMesh. + * Otherwise, in case we call again #BM_mesh_bm_to_me on same BMesh, + * we'll apply diff from previous call to #BM_mesh_bm_to_me, + * to shape-key values from *original creation of the BMesh*. See T50524. */ + copy_v3_v3(BM_ELEM_CD_GET_VOID_P(eve, cd_shape_offset), fp); + } + + fp += 3; + mvert++; + } + + currkey->totelem = bm->totvert; + if (currkey->data) { + MEM_freeN(currkey->data); + } + currkey->data = newkey; + } + + if (ofs) { + MEM_freeN(ofs); + } + } + + /* Run this even when shape keys aren't used since it may be used for hooks or vertex parents. */ + if (params->update_shapekey_indices) { + /* We have written a new shape key, if this mesh is _not_ going to be freed, + * update the shape key indices to match the newly updated. */ + if (cd_shape_keyindex_offset != -1) { + BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, i) { + BM_ELEM_CD_SET_INT(eve, cd_shape_keyindex_offset, i); + } + } + } + + me->cd_flag = BM_mesh_cd_flag_from_bmesh(bm); + + if (oldverts != NULL) { + MEM_freeN(oldverts); + } + + /* Topology could be changed, ensure #CD_MDISPS are ok. */ + multires_topology_changed(me); + + /* To be removed as soon as COW is enabled by default. */ + BKE_mesh_runtime_clear_geometry(me); + + if (params->copy_temp_cdlayers) { + bm_mark_temp_cdlayers(bm); + } +} +#endif diff --git a/source/blender/bmesh/intern/bmesh_mesh_duplicate.c b/source/blender/bmesh/intern/bmesh_mesh_duplicate.c index cb6d0861696..83a84012c36 100644 --- a/source/blender/bmesh/intern/bmesh_mesh_duplicate.c +++ b/source/blender/bmesh/intern/bmesh_mesh_duplicate.c @@ -20,6 +20,10 @@ static BMVert *bm_vert_copy(BMesh *bm_src, BMesh *bm_dst, BMVert *v_src) { BMVert *v_dst = BM_vert_create(bm_dst, v_src->co, NULL, BM_CREATE_SKIP_CD); BM_elem_attrs_copy(bm_src, bm_dst, v_src, v_dst); + + bm_alloc_id(bm_dst, (BMElem *)v_dst); + bm_elem_check_toolflags(bm_dst, (BMElem *)v_dst); + return v_dst; } @@ -31,7 +35,12 @@ static BMEdge *bm_edge_copy_with_arrays(BMesh *bm_src, BMVert *e_dst_v1 = verts_dst[BM_elem_index_get(e_src->v1)]; BMVert *e_dst_v2 = verts_dst[BM_elem_index_get(e_src->v2)]; BMEdge *e_dst = BM_edge_create(bm_dst, e_dst_v1, e_dst_v2, NULL, BM_CREATE_SKIP_CD); + BM_elem_attrs_copy(bm_src, bm_dst, e_src, e_dst); + + bm_alloc_id(bm_dst, (BMElem *)e_dst); + bm_elem_check_toolflags(bm_dst, (BMElem *)e_dst); + return e_dst; } @@ -61,11 +70,15 @@ static BMFace *bm_face_copy_with_arrays( /* Copy attributes. */ BM_elem_attrs_copy(bm_src, bm_dst, f_src, f_dst); + bm_alloc_id(bm_dst, (BMElem *)f_dst); + bm_elem_check_toolflags(bm_dst, (BMElem *)f_dst); + /* Copy per-loop custom data. */ l_iter_src = l_first_src; l_iter_dst = BM_FACE_FIRST_LOOP(f_dst); do { BM_elem_attrs_copy(bm_src, bm_dst, l_iter_src, l_iter_dst); + bm_alloc_id(bm_dst, (BMElem *)l_iter_dst); } while ((void)(l_iter_dst = l_iter_dst->next), (l_iter_src = l_iter_src->next) != l_first_src); return f_dst; diff --git a/source/blender/bmesh/intern/bmesh_mods.c b/source/blender/bmesh/intern/bmesh_mods.c index a1842b598d5..dae816f89e3 100644 --- a/source/blender/bmesh/intern/bmesh_mods.c +++ b/source/blender/bmesh/intern/bmesh_mods.c @@ -439,10 +439,25 @@ BMEdge *BM_vert_collapse_edge(BMesh *bm, #undef DO_V_INTERP -BMVert *BM_edge_collapse( - BMesh *bm, BMEdge *e_kill, BMVert *v_kill, const bool do_del, const bool kill_degenerate_faces) +/** + * Collapse and edge into a single vertex. + */ +BMVert *BM_edge_collapse(BMesh *bm, + BMEdge *e_kill, + BMVert *v_kill, + const bool do_del, + const bool kill_degenerate_faces, + const bool combine_flags, + const bool full_non_manifold_collapse, + const BMTracer *tracer) { - return bmesh_kernel_join_vert_kill_edge(bm, e_kill, v_kill, do_del, true, kill_degenerate_faces); + if (full_non_manifold_collapse || true) { + return bmesh_kernel_join_vert_kill_edge(bm, e_kill, v_kill, do_del, combine_flags, tracer); + } + else { + return bmesh_kernel_join_vert_kill_edge_fast( + bm, e_kill, v_kill, do_del, true, kill_degenerate_faces, combine_flags); + } } BMVert *BM_edge_split(BMesh *bm, BMEdge *e, BMVert *v, BMEdge **r_e, float fac) diff --git a/source/blender/bmesh/intern/bmesh_mods.h b/source/blender/bmesh/intern/bmesh_mods.h index ef974f932c4..4396da83b3b 100644 --- a/source/blender/bmesh/intern/bmesh_mods.h +++ b/source/blender/bmesh/intern/bmesh_mods.h @@ -2,6 +2,8 @@ #pragma once +struct BMTracer; + /** \file * \ingroup bmesh */ @@ -155,8 +157,14 @@ BMEdge *BM_vert_collapse_edge(BMesh *bm, /** * Collapse and edge into a single vertex. */ -BMVert *BM_edge_collapse( - BMesh *bm, BMEdge *e_kill, BMVert *v_kill, bool do_del, bool kill_degenerate_faces); +BMVert *BM_edge_collapse(BMesh *bm, + BMEdge *e_kill, + BMVert *v_kill, + const bool do_del, + const bool kill_degenerate_faces, + const bool combine_flags, + const bool full_non_manifold_collapse, + const struct BMTracer *tracer); /** * \brief Edge Split diff --git a/source/blender/bmesh/intern/bmesh_opdefines.c b/source/blender/bmesh/intern/bmesh_opdefines.c index a1c69da6f7a..062248ac3cd 100644 --- a/source/blender/bmesh/intern/bmesh_opdefines.c +++ b/source/blender/bmesh/intern/bmesh_opdefines.c @@ -1061,12 +1061,28 @@ static BMOpDefine bmo_extrude_face_region_def = { }, /* slots_out */ {{"geom.out", BMO_OP_SLOT_ELEMENT_BUF, {BM_VERT | BM_EDGE | BM_FACE}}, + {"side_geom.out", BMO_OP_SLOT_ELEMENT_BUF, {BM_VERT | BM_EDGE | BM_FACE}}, {{'\0'}}, }, bmo_extrude_face_region_exec, (BMO_OPTYPE_FLAG_NORMALS_CALC), }; +extern void bmo_test_mres_smooth_exec(BMesh *bm, BMOperator *op); + +static BMOpDefine bmo_test_mres_smooth_def = { + "test_mres_smooth", + /* slots_in */ + {{{'\0'}}}, /* no input */ + {{{'\0'}}}, /* no output */ + bmo_test_mres_smooth_exec, + ( + BMO_OPTYPE_FLAG_UNTAN_MULTIRES | + BMO_OPTYPE_FLAG_NORMALS_CALC | + BMO_OPTYPE_FLAG_SELECT_FLUSH | + BMO_OPTYPE_FLAG_SELECT_VALIDATE) +}; + /* * Dissolve Verts. */ @@ -1915,7 +1931,7 @@ static BMOpDefine bmo_inset_individual_def = { }, bmo_inset_individual_exec, /* caller needs to handle BMO_OPTYPE_FLAG_SELECT_FLUSH */ - (BMO_OPTYPE_FLAG_NORMALS_CALC), + (BMO_OPTYPE_FLAG_NORMALS_CALC | BMO_OPTYPE_FLAG_UNTAN_MULTIRES), }; /* @@ -1944,7 +1960,7 @@ static BMOpDefine bmo_inset_region_def = { }, bmo_inset_region_exec, (BMO_OPTYPE_FLAG_NORMALS_CALC | - BMO_OPTYPE_FLAG_SELECT_FLUSH), + BMO_OPTYPE_FLAG_SELECT_FLUSH | BMO_OPTYPE_FLAG_UNTAN_MULTIRES), }; /* @@ -1965,7 +1981,7 @@ static BMOpDefine bmo_offset_edgeloops_def = { }, bmo_offset_edgeloops_exec, (BMO_OPTYPE_FLAG_NORMALS_CALC | - BMO_OPTYPE_FLAG_SELECT_FLUSH), + BMO_OPTYPE_FLAG_SELECT_FLUSH | BMO_OPTYPE_FLAG_UNTAN_MULTIRES), }; /* @@ -2185,6 +2201,7 @@ const BMOpDefine *bmo_opdefines[] = { &bmo_unsubdivide_def, &bmo_weld_verts_def, &bmo_wireframe_def, + &bmo_test_mres_smooth_def, }; const int bmo_opdefines_total = ARRAY_SIZE(bmo_opdefines); diff --git a/source/blender/bmesh/intern/bmesh_operator_api.h b/source/blender/bmesh/intern/bmesh_operator_api.h index 24b86c71452..7e4e608a8a5 100644 --- a/source/blender/bmesh/intern/bmesh_operator_api.h +++ b/source/blender/bmesh/intern/bmesh_operator_api.h @@ -6,8 +6,11 @@ * \ingroup bmesh */ +#include "BLI_compiler_compat.h" #include "BLI_ghash.h" +#include "DNA_meshdata_types.h" + #include #ifdef __cplusplus @@ -54,122 +57,142 @@ extern "C" { struct GHashIterator; -BLI_INLINE BMFlagLayer *BMO_elem_flag_from_header(BMHeader *ele_head) +BLI_INLINE BMFlagLayer *BMO_elem_flag_from_header(BMesh *bm, BMElem *ele) { - switch (ele_head->htype) { + int cd_tflags; + + switch (ele->head.htype) { case BM_VERT: - return ((BMVert_OFlag *)ele_head)->oflags; + cd_tflags = bm->vdata.layers[bm->vdata.typemap[CD_TOOLFLAGS]].offset; + break; case BM_EDGE: - return ((BMEdge_OFlag *)ele_head)->oflags; + cd_tflags = bm->edata.layers[bm->edata.typemap[CD_TOOLFLAGS]].offset; + break; + case BM_FACE: + cd_tflags = bm->pdata.layers[bm->pdata.typemap[CD_TOOLFLAGS]].offset; + break; default: - return ((BMFace_OFlag *)ele_head)->oflags; + return NULL; } + + MToolFlags *flags = (MToolFlags *)BM_ELEM_CD_GET_VOID_P(ele, cd_tflags); + + return (BMFlagLayer *)flags->flag; } #define BMO_elem_flag_test(bm, ele, oflag) \ - _bmo_elem_flag_test(bm, BMO_elem_flag_from_header(&(ele)->head), oflag) + _bmo_elem_flag_test(bm, BMO_elem_flag_from_header(bm, (BMElem *)(ele)), oflag) #define BMO_elem_flag_test_bool(bm, ele, oflag) \ - _bmo_elem_flag_test_bool(bm, BMO_elem_flag_from_header(&(ele)->head), oflag) + _bmo_elem_flag_test_bool(bm, BMO_elem_flag_from_header(bm, (BMElem *)(ele)), oflag) #define BMO_elem_flag_enable(bm, ele, oflag) \ _bmo_elem_flag_enable( \ - bm, (BM_CHECK_TYPE_ELEM_NONCONST(ele), BMO_elem_flag_from_header(&(ele)->head)), oflag) + bm, \ + (BM_CHECK_TYPE_ELEM_NONCONST(ele), BMO_elem_flag_from_header(bm, (BMElem *)(ele))), \ + oflag) #define BMO_elem_flag_disable(bm, ele, oflag) \ _bmo_elem_flag_disable( \ - bm, (BM_CHECK_TYPE_ELEM_NONCONST(ele), BMO_elem_flag_from_header(&(ele)->head)), oflag) + bm, \ + (BM_CHECK_TYPE_ELEM_NONCONST(ele), BMO_elem_flag_from_header(bm, (BMElem *)(ele))), \ + oflag) #define BMO_elem_flag_set(bm, ele, oflag, val) \ - _bmo_elem_flag_set(bm, \ - (BM_CHECK_TYPE_ELEM_NONCONST(ele), BMO_elem_flag_from_header(&(ele)->head)), \ - oflag, \ - val) + _bmo_elem_flag_set( \ + bm, \ + (BM_CHECK_TYPE_ELEM_NONCONST(ele), BMO_elem_flag_from_header(bm, (BMElem *)(ele))), \ + oflag, \ + val) #define BMO_elem_flag_toggle(bm, ele, oflag) \ _bmo_elem_flag_toggle( \ - bm, (BM_CHECK_TYPE_ELEM_NONCONST(ele), BMO_elem_flag_from_header(&(ele)->head)), oflag) + bm, \ + (BM_CHECK_TYPE_ELEM_NONCONST(ele), BMO_elem_flag_from_header(bm, (BMElem *)(ele))), \ + oflag) /* take care not to instantiate args multiple times */ -#ifdef __GNUC___ -# define _BMO_CAST_V_CONST(e) \ - ({ \ +#ifdef __GNUC__ +/* clang-format off */ +#define _BMO_CAST_V_CONST(bm, e) ({\ typeof(e) _e = e; \ (BM_CHECK_TYPE_VERT(_e), \ - BLI_assert(((const BMHeader *)_e)->htype == BM_VERT), \ - (const BMVert_OFlag *)_e); \ - }) -# define _BMO_CAST_E_CONST(e) \ - ({ \ - typeof(e) _e = e; \ - (BM_CHECK_TYPE_EDGE(_e), \ - BLI_assert(((const BMHeader *)_e)->htype == BM_EDGE), \ - (const BMEdge_OFlag *)_e); \ - }) -# define _BMO_CAST_F_CONST(e) \ - ({ \ - typeof(e) _e = e; \ - (BM_CHECK_TYPE_FACE(_e), \ - BLI_assert(((const BMHeader *)_e)->htype == BM_FACE), \ - (const BMFace_OFlag *)_e); \ - }) -# define _BMO_CAST_V(e) \ - ({ \ + (const BMFlagLayer*) BMO_elem_flag_from_header(bm, \ + (BLI_assert(((const BMHeader *)_e)->htype == BM_VERT), \ + (BMElem*)_e))); \ +}) +#define _BMO_CAST_V(bm, e) ({\ typeof(e) _e = e; \ (BM_CHECK_TYPE_VERT_NONCONST(_e), \ - BLI_assert(((BMHeader *)_e)->htype == BM_VERT), \ - (BMVert_OFlag *)_e); \ - }) -# define _BMO_CAST_E(e) \ - ({ \ + BMO_elem_flag_from_header(bm, \ + (BLI_assert(((const BMHeader *)_e)->htype == BM_VERT), \ + (BMElem*)_e))); \ +}) + +#define _BMO_CAST_E_CONST(bm, e) ({\ + typeof(e) _e = e; \ + (BM_CHECK_TYPE_EDGE(_e), \ + (const BMFlagLayer*) BMO_elem_flag_from_header(bm, \ + (BLI_assert(((const BMHeader *)_e)->htype == BM_EDGE), \ + (BMElem*)_e))); \ +}) +#define _BMO_CAST_E(bm, e) ({\ typeof(e) _e = e; \ (BM_CHECK_TYPE_EDGE_NONCONST(_e), \ - BLI_assert(((BMHeader *)_e)->htype == BM_EDGE), \ - (BMEdge_OFlag *)_e); \ - }) -# define _BMO_CAST_F(e) \ - ({ \ + BMO_elem_flag_from_header(bm, \ + (BLI_assert(((const BMHeader *)_e)->htype == BM_EDGE), \ + (BMElem*)_e))); \ +}) + +#define _BMO_CAST_F_CONST(bm, e) ({\ + typeof(e) _e = e; \ + (BM_CHECK_TYPE_FACE(_e), \ + (const BMFlagLayer*) BMO_elem_flag_from_header(bm, \ + (BLI_assert(((const BMHeader *)_e)->htype == BM_FACE), \ + (BMElem*)_e))); \ +}) +#define _BMO_CAST_F(bm, e) ({\ typeof(e) _e = e; \ (BM_CHECK_TYPE_FACE_NONCONST(_e), \ - BLI_assert(((BMHeader *)_e)->htype == BM_FACE), \ - (BMFace_OFlag *)_e); \ - }) + BMO_elem_flag_from_header(bm, \ + (BLI_assert(((const BMHeader *)_e)->htype == BM_FACE), \ + (BMElem*)_e))); \ +}) + +/* clang-format on */ #else -# define _BMO_CAST_V_CONST(e) (BM_CHECK_TYPE_VERT(e), (const BMVert_OFlag *)e) -# define _BMO_CAST_E_CONST(e) (BM_CHECK_TYPE_EDGE(e), (const BMEdge_OFlag *)e) -# define _BMO_CAST_F_CONST(e) (BM_CHECK_TYPE_FACE(e), (const BMFace_OFlag *)e) -# define _BMO_CAST_V(e) (BM_CHECK_TYPE_VERT_NONCONST(e), (BMVert_OFlag *)e) -# define _BMO_CAST_E(e) (BM_CHECK_TYPE_EDGE_NONCONST(e), (BMEdge_OFlag *)e) -# define _BMO_CAST_F(e) (BM_CHECK_TYPE_FACE_NONCONST(e), (BMFace_OFlag *)e) +# define _BMO_CAST_V_CONST(bm, e) \ + (BM_CHECK_TYPE_VERT(e), (const BMFlagLayer *)BMO_elem_flag_from_header(bm, (BMElem *)(e))) +# define _BMO_CAST_E_CONST(bm, e) \ + (BM_CHECK_TYPE_EDGE(e), (const BMFlagLayer *)BMO_elem_flag_from_header(bm, (BMElem *)(e))) +# define _BMO_CAST_F_CONST(bm, e) \ + (BM_CHECK_TYPE_FACE(e), (const BMFlagLayer *)BMO_elem_flag_from_header(bm, (BMElem *)(e))) +# define _BMO_CAST_V(bm, e) \ + (BM_CHECK_TYPE_VERT_NONCONST(e), BMO_elem_flag_from_header(bm, (BMElem *)(e))) +# define _BMO_CAST_E(bm, e) \ + (BM_CHECK_TYPE_EDGE_NONCONST(e), BMO_elem_flag_from_header(bm, (BMElem *)(e))) +# define _BMO_CAST_F(bm, e) \ + (BM_CHECK_TYPE_FACE_NONCONST(e), BMO_elem_flag_from_header(bm, (BMElem *)(e))) #endif -#define BMO_vert_flag_test(bm, e, oflag) \ - _bmo_elem_flag_test(bm, _BMO_CAST_V_CONST(e)->oflags, oflag) +#define BMO_vert_flag_test(bm, e, oflag) _bmo_elem_flag_test(bm, _BMO_CAST_V_CONST(bm, e), oflag) #define BMO_vert_flag_test_bool(bm, e, oflag) \ - _bmo_elem_flag_test_bool(bm, _BMO_CAST_V_CONST(e)->oflags, oflag) -#define BMO_vert_flag_enable(bm, e, oflag) _bmo_elem_flag_enable(bm, _BMO_CAST_V(e)->oflags, oflag) -#define BMO_vert_flag_disable(bm, e, oflag) \ - _bmo_elem_flag_disable(bm, _BMO_CAST_V(e)->oflags, oflag) -#define BMO_vert_flag_set(bm, e, oflag, val) \ - _bmo_elem_flag_set(bm, _BMO_CAST_V(e)->oflags, oflag, val) -#define BMO_vert_flag_toggle(bm, e, oflag) _bmo_elem_flag_toggle(bm, _BMO_CAST_V(e)->oflags, oflag) + _bmo_elem_flag_test_bool(bm, _BMO_CAST_V_CONST(bm, e), oflag) +#define BMO_vert_flag_enable(bm, e, oflag) _bmo_elem_flag_enable(bm, _BMO_CAST_V(bm, e), oflag) +#define BMO_vert_flag_disable(bm, e, oflag) _bmo_elem_flag_disable(bm, _BMO_CAST_V(bm, e), oflag) +#define BMO_vert_flag_set(bm, e, oflag, val) _bmo_elem_flag_set(bm, _BMO_CAST_V(bm, e), oflag, val) +#define BMO_vert_flag_toggle(bm, e, oflag) _bmo_elem_flag_toggle(bm, _BMO_CAST_V(bm, e), oflag) -#define BMO_edge_flag_test(bm, e, oflag) \ - _bmo_elem_flag_test(bm, _BMO_CAST_E_CONST(e)->oflags, oflag) +#define BMO_edge_flag_test(bm, e, oflag) _bmo_elem_flag_test(bm, _BMO_CAST_E_CONST(bm, e), oflag) #define BMO_edge_flag_test_bool(bm, e, oflag) \ - _bmo_elem_flag_test_bool(bm, _BMO_CAST_E_CONST(e)->oflags, oflag) -#define BMO_edge_flag_enable(bm, e, oflag) _bmo_elem_flag_enable(bm, _BMO_CAST_E(e)->oflags, oflag) -#define BMO_edge_flag_disable(bm, e, oflag) \ - _bmo_elem_flag_disable(bm, _BMO_CAST_E(e)->oflags, oflag) -#define BMO_edge_flag_set(bm, e, oflag, val) \ - _bmo_elem_flag_set(bm, _BMO_CAST_E(e)->oflags, oflag, val) -#define BMO_edge_flag_toggle(bm, e, oflag) _bmo_elem_flag_toggle(bm, _BMO_CAST_E(e)->oflags, oflag) + _bmo_elem_flag_test_bool(bm, _BMO_CAST_E_CONST(bm, e), oflag) +#define BMO_edge_flag_enable(bm, e, oflag) _bmo_elem_flag_enable(bm, _BMO_CAST_E(bm, e), oflag) +#define BMO_edge_flag_disable(bm, e, oflag) _bmo_elem_flag_disable(bm, _BMO_CAST_E(bm, e), oflag) +#define BMO_edge_flag_set(bm, e, oflag, val) _bmo_elem_flag_set(bm, _BMO_CAST_E(bm, e), oflag, val) +#define BMO_edge_flag_toggle(bm, e, oflag) _bmo_elem_flag_toggle(bm, _BMO_CAST_E(bm, e), oflag) -#define BMO_face_flag_test(bm, e, oflag) \ - _bmo_elem_flag_test(bm, _BMO_CAST_F_CONST(e)->oflags, oflag) +#define BMO_face_flag_test(bm, e, oflag) _bmo_elem_flag_test(bm, _BMO_CAST_F_CONST(bm, e), oflag) #define BMO_face_flag_test_bool(bm, e, oflag) \ - _bmo_elem_flag_test_bool(bm, _BMO_CAST_F_CONST(e)->oflags, oflag) -#define BMO_face_flag_enable(bm, e, oflag) _bmo_elem_flag_enable(bm, _BMO_CAST_F(e)->oflags, oflag) -#define BMO_face_flag_disable(bm, e, oflag) \ - _bmo_elem_flag_disable(bm, _BMO_CAST_F(e)->oflags, oflag) -#define BMO_face_flag_set(bm, e, oflag, val) \ - _bmo_elem_flag_set(bm, _BMO_CAST_F(e)->oflags, oflag, val) -#define BMO_face_flag_toggle(bm, e, oflag) _bmo_elem_flag_toggle(bm, _BMO_CAST_F(e)->oflags, oflag) + _bmo_elem_flag_test_bool(bm, _BMO_CAST_F_CONST(bm, e), oflag) +#define BMO_face_flag_enable(bm, e, oflag) _bmo_elem_flag_enable(bm, _BMO_CAST_F(bm, e), oflag) +#define BMO_face_flag_disable(bm, e, oflag) _bmo_elem_flag_disable(bm, _BMO_CAST_F(bm, e), oflag) +#define BMO_face_flag_set(bm, e, oflag, val) _bmo_elem_flag_set(bm, _BMO_CAST_F(bm, e), oflag, val) +#define BMO_face_flag_toggle(bm, e, oflag) _bmo_elem_flag_toggle(bm, _BMO_CAST_F(bm, e), oflag) BLI_INLINE short _bmo_elem_flag_test(BMesh *bm, const BMFlagLayer *oflags, short oflag); BLI_INLINE bool _bmo_elem_flag_test_bool(BMesh *bm, const BMFlagLayer *oflags, short oflag); diff --git a/source/blender/bmesh/intern/bmesh_operator_api_inline.h b/source/blender/bmesh/intern/bmesh_operator_api_inline.h index 3cb4923eb78..ceb821186ac 100644 --- a/source/blender/bmesh/intern/bmesh_operator_api_inline.h +++ b/source/blender/bmesh/intern/bmesh_operator_api_inline.h @@ -16,6 +16,7 @@ ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1, 2) BLI_INLINE short _bmo_elem_flag_test(BMesh *bm, const BMFlagLayer *oflags, const short oflag) { BLI_assert(bm->use_toolflags); + return oflags[bm->toolflag_index].f & oflag; } diff --git a/source/blender/bmesh/intern/bmesh_operators.c b/source/blender/bmesh/intern/bmesh_operators.c index ddecab23ed4..c2479f940d4 100644 --- a/source/blender/bmesh/intern/bmesh_operators.c +++ b/source/blender/bmesh/intern/bmesh_operators.c @@ -17,9 +17,13 @@ #include "BLT_translation.h" +#include "BKE_customdata.h" + #include "bmesh.h" #include "intern/bmesh_private.h" +#include "DNA_meshdata_types.h" + /* forward declarations */ static void bmo_flag_layer_alloc(BMesh *bm); static void bmo_flag_layer_free(BMesh *bm); @@ -1151,6 +1155,68 @@ void BMO_slot_buffer_flag_disable(BMesh *bm, } } +static void bmo_flag_layer_do(BMesh *bm, + int new_totflags, + void (*callback)(BMesh *bm, + int cd_tflags, + int itertype, + int htype, + int totelem, + int new_totflags, + BLI_mempool **pool_ptr)) +{ + int iters[3] = {BM_VERTS_OF_MESH, BM_EDGES_OF_MESH, BM_FACES_OF_MESH}; + int types[3] = {BM_VERT, BM_EDGE, BM_FACE}; + int tots[3] = {bm->totvert, bm->totedge, bm->totface}; + + BLI_mempool **pools[3] = {&bm->vtoolflagpool, &bm->etoolflagpool, &bm->ftoolflagpool}; + CustomData *cdatas[3] = {&bm->vdata, &bm->edata, &bm->pdata}; + + for (int i = 0; i < 3; i++) { + int cd_tflags = CustomData_get_offset(cdatas[i], CD_TOOLFLAGS); + + if (cd_tflags == -1) { + printf("%s: missing toolflags cd layer!\n", __func__); + } + + callback(bm, cd_tflags, iters[i], types[i], tots[i], new_totflags, pools[i]); + } + + bm->totflags = new_totflags; +} + +static void bmo_flag_layer_alloc_do(BMesh *bm, + int cd_tflags, + int itertype, + int htype, + int totelem, + int new_totflags, + BLI_mempool **pool_ptr) +{ + BMIter iter; + BMElem *elem; + int i; + + const size_t old_totflags_size = (bm->totflags * sizeof(BMFlagLayer)); + BLI_mempool *oldpool = *pool_ptr; + BLI_mempool *newpool = BLI_mempool_create( + sizeof(BMFlagLayer) * new_totflags, totelem, 512, BLI_MEMPOOL_NOP); + + BM_ITER_MESH_INDEX (elem, &iter, bm, itertype, i) { + MToolFlags *flags = BM_ELEM_CD_GET_VOID_P(elem, cd_tflags); + + short *oldflags = flags->flag; + flags->flag = BLI_mempool_calloc(newpool); + + memcpy(flags->flag, oldflags, old_totflags_size); + BM_elem_index_set(elem, i); /* set_inline */ + BM_ELEM_API_FLAG_CLEAR((BMElemF *)elem); + } + + *pool_ptr = newpool; + BLI_mempool_destroy(oldpool); +} + /** * \brief ALLOC/FREE FLAG LAYER * @@ -1166,163 +1232,40 @@ void BMO_slot_buffer_flag_disable(BMesh *bm, */ static void bmo_flag_layer_alloc(BMesh *bm) { - /* set the index values since we are looping over all data anyway, - * may save time later on */ - - BLI_mempool *voldpool = bm->vtoolflagpool; /* old flag pool */ - BLI_mempool *eoldpool = bm->etoolflagpool; /* old flag pool */ - BLI_mempool *foldpool = bm->ftoolflagpool; /* old flag pool */ - - /* store memcpy size for reuse */ - const size_t old_totflags_size = (bm->totflags * sizeof(BMFlagLayer)); - - bm->totflags++; - - bm->vtoolflagpool = BLI_mempool_create( - sizeof(BMFlagLayer) * bm->totflags, bm->totvert, 512, BLI_MEMPOOL_NOP); - bm->etoolflagpool = BLI_mempool_create( - sizeof(BMFlagLayer) * bm->totflags, bm->totedge, 512, BLI_MEMPOOL_NOP); - bm->ftoolflagpool = BLI_mempool_create( - sizeof(BMFlagLayer) * bm->totflags, bm->totface, 512, BLI_MEMPOOL_NOP); - - /* now go through and memcpy all the flags. Loops don't get a flag layer at this time. */ - BMIter iter; - int i; - - BMVert_OFlag *v_oflag; - BLI_mempool *newpool = bm->vtoolflagpool; - BM_ITER_MESH_INDEX (v_oflag, &iter, bm, BM_VERTS_OF_MESH, i) { - void *oldflags = v_oflag->oflags; - v_oflag->oflags = BLI_mempool_calloc(newpool); - memcpy(v_oflag->oflags, oldflags, old_totflags_size); - BM_elem_index_set(&v_oflag->base, i); /* set_inline */ - BM_ELEM_API_FLAG_CLEAR((BMElemF *)v_oflag); - } - - BMEdge_OFlag *e_oflag; - newpool = bm->etoolflagpool; - BM_ITER_MESH_INDEX (e_oflag, &iter, bm, BM_EDGES_OF_MESH, i) { - void *oldflags = e_oflag->oflags; - e_oflag->oflags = BLI_mempool_calloc(newpool); - memcpy(e_oflag->oflags, oldflags, old_totflags_size); - BM_elem_index_set(&e_oflag->base, i); /* set_inline */ - BM_ELEM_API_FLAG_CLEAR((BMElemF *)e_oflag); - } - - BMFace_OFlag *f_oflag; - newpool = bm->ftoolflagpool; - BM_ITER_MESH_INDEX (f_oflag, &iter, bm, BM_FACES_OF_MESH, i) { - void *oldflags = f_oflag->oflags; - f_oflag->oflags = BLI_mempool_calloc(newpool); - memcpy(f_oflag->oflags, oldflags, old_totflags_size); - BM_elem_index_set(&f_oflag->base, i); /* set_inline */ - BM_ELEM_API_FLAG_CLEAR((BMElemF *)f_oflag); - } - - BLI_mempool_destroy(voldpool); - BLI_mempool_destroy(eoldpool); - BLI_mempool_destroy(foldpool); - + bmo_flag_layer_do(bm, bm->totflags + 1, bmo_flag_layer_alloc_do); bm->elem_index_dirty &= ~(BM_VERT | BM_EDGE | BM_FACE); } static void bmo_flag_layer_free(BMesh *bm) { - /* set the index values since we are looping over all data anyway, - * may save time later on */ - BLI_mempool *voldpool = bm->vtoolflagpool; - BLI_mempool *eoldpool = bm->etoolflagpool; - BLI_mempool *foldpool = bm->ftoolflagpool; + bmo_flag_layer_do(bm, bm->totflags - 1, bmo_flag_layer_alloc_do); + bm->elem_index_dirty &= ~(BM_VERT | BM_EDGE | BM_FACE); +} - /* store memcpy size for reuse */ - const size_t new_totflags_size = ((bm->totflags - 1) * sizeof(BMFlagLayer)); - - /* de-increment the totflags first. */ - bm->totflags--; - - bm->vtoolflagpool = BLI_mempool_create(new_totflags_size, bm->totvert, 512, BLI_MEMPOOL_NOP); - bm->etoolflagpool = BLI_mempool_create(new_totflags_size, bm->totedge, 512, BLI_MEMPOOL_NOP); - bm->ftoolflagpool = BLI_mempool_create(new_totflags_size, bm->totface, 512, BLI_MEMPOOL_NOP); - - /* now go through and memcpy all the flag */ +static void bmo_flag_layer_clear_do(BMesh *bm, + int cd_tflags, + int itertype, + int htype, + int totelem, + int totflag, + BLI_mempool **pool_ptr) +{ BMIter iter; + BMElem *elem; int i; - BMVert_OFlag *v_oflag; - BLI_mempool *newpool = bm->vtoolflagpool; - BM_ITER_MESH_INDEX (v_oflag, &iter, bm, BM_VERTS_OF_MESH, i) { - void *oldflags = v_oflag->oflags; - v_oflag->oflags = BLI_mempool_alloc(newpool); - memcpy(v_oflag->oflags, oldflags, new_totflags_size); - BM_elem_index_set(&v_oflag->base, i); /* set_inline */ - BM_ELEM_API_FLAG_CLEAR((BMElemF *)v_oflag); + BM_ITER_MESH_INDEX (elem, &iter, bm, itertype, i) { + MToolFlags *flags = BM_ELEM_CD_GET_VOID_P(elem, cd_tflags); + flags->flag[totflag - 1] = 0; + + BM_elem_index_set(elem, i); /* set_inline */ } - - BMEdge_OFlag *e_oflag; - newpool = bm->etoolflagpool; - BM_ITER_MESH_INDEX (e_oflag, &iter, bm, BM_EDGES_OF_MESH, i) { - void *oldflags = e_oflag->oflags; - e_oflag->oflags = BLI_mempool_alloc(newpool); - memcpy(e_oflag->oflags, oldflags, new_totflags_size); - BM_elem_index_set(&e_oflag->base, i); /* set_inline */ - BM_ELEM_API_FLAG_CLEAR((BMElemF *)e_oflag); - } - - BMFace_OFlag *f_oflag; - newpool = bm->ftoolflagpool; - BM_ITER_MESH_INDEX (f_oflag, &iter, bm, BM_FACES_OF_MESH, i) { - void *oldflags = f_oflag->oflags; - f_oflag->oflags = BLI_mempool_alloc(newpool); - memcpy(f_oflag->oflags, oldflags, new_totflags_size); - BM_elem_index_set(&f_oflag->base, i); /* set_inline */ - BM_ELEM_API_FLAG_CLEAR((BMElemF *)f_oflag); - } - - BLI_mempool_destroy(voldpool); - BLI_mempool_destroy(eoldpool); - BLI_mempool_destroy(foldpool); - - bm->elem_index_dirty &= ~(BM_VERT | BM_EDGE | BM_FACE); } static void bmo_flag_layer_clear(BMesh *bm) { - /* set the index values since we are looping over all data anyway, - * may save time later on */ - const BMFlagLayer zero_flag = {0}; - - const int totflags_offset = bm->totflags - 1; - - /* now go through and memcpy all the flag */ - { - BMIter iter; - BMVert_OFlag *ele; - int i; - BM_ITER_MESH_INDEX (ele, &iter, bm, BM_VERTS_OF_MESH, i) { - ele->oflags[totflags_offset] = zero_flag; - BM_elem_index_set(&ele->base, i); /* set_inline */ - } - } - { - BMIter iter; - BMEdge_OFlag *ele; - int i; - BM_ITER_MESH_INDEX (ele, &iter, bm, BM_EDGES_OF_MESH, i) { - ele->oflags[totflags_offset] = zero_flag; - BM_elem_index_set(&ele->base, i); /* set_inline */ - } - } - { - BMIter iter; - BMFace_OFlag *ele; - int i; - BM_ITER_MESH_INDEX (ele, &iter, bm, BM_FACES_OF_MESH, i) { - ele->oflags[totflags_offset] = zero_flag; - BM_elem_index_set(&ele->base, i); /* set_inline */ - } - } - + bmo_flag_layer_do(bm, bm->totflags, bmo_flag_layer_clear_do); bm->elem_index_dirty &= ~(BM_VERT | BM_EDGE | BM_FACE); } diff --git a/source/blender/bmesh/intern/bmesh_polygon.c b/source/blender/bmesh/intern/bmesh_polygon.c index 13fde0e8cf4..66f6aa71e63 100644 --- a/source/blender/bmesh/intern/bmesh_polygon.c +++ b/source/blender/bmesh/intern/bmesh_polygon.c @@ -14,6 +14,7 @@ #include "MEM_guardedalloc.h" #include "BLI_alloca.h" +#include "BLI_compiler_attrs.h" #include "BLI_heap.h" #include "BLI_linklist.h" #include "BLI_math.h" @@ -1280,18 +1281,10 @@ void BM_face_splits_check_optimal(BMFace *f, BMLoop *(*loops)[2], int len) } } -void BM_face_as_array_vert_tri(BMFace *f, BMVert *r_verts[3]) -{ - BMLoop *l = BM_FACE_FIRST_LOOP(f); - - BLI_assert(f->len == 3); - - r_verts[0] = l->v; - l = l->next; - r_verts[1] = l->v; - l = l->next; - r_verts[2] = l->v; -} +/** + * faster alternative to: + * BM_iter_as_array(bm, BM_VERTS_OF_FACE, f, (void **)v, 4); + */ void BM_face_as_array_vert_quad(BMFace *f, BMVert *r_verts[4]) { @@ -1308,19 +1301,6 @@ void BM_face_as_array_vert_quad(BMFace *f, BMVert *r_verts[4]) r_verts[3] = l->v; } -void BM_face_as_array_loop_tri(BMFace *f, BMLoop *r_loops[3]) -{ - BMLoop *l = BM_FACE_FIRST_LOOP(f); - - BLI_assert(f->len == 3); - - r_loops[0] = l; - l = l->next; - r_loops[1] = l; - l = l->next; - r_loops[2] = l; -} - void BM_face_as_array_loop_quad(BMFace *f, BMLoop *r_loops[4]) { BMLoop *l = BM_FACE_FIRST_LOOP(f); diff --git a/source/blender/bmesh/intern/bmesh_polygon.h b/source/blender/bmesh/intern/bmesh_polygon.h index 5ca7c3bafaf..8ca878e69bc 100644 --- a/source/blender/bmesh/intern/bmesh_polygon.h +++ b/source/blender/bmesh/intern/bmesh_polygon.h @@ -9,6 +9,7 @@ struct Heap; #include "BLI_compiler_attrs.h" +#include "BLI_compiler_compat.h" #ifdef __cplusplus extern "C" { @@ -248,12 +249,18 @@ void BM_face_splits_check_optimal(BMFace *f, BMLoop *(*loops)[2], int len) ATTR_ * faster alternative to: * BM_iter_as_array(bm, BM_VERTS_OF_FACE, f, (void **)v, 3); */ -void BM_face_as_array_vert_tri(BMFace *f, BMVert *r_verts[3]) ATTR_NONNULL(); -/** - * faster alternative to: - * BM_iter_as_array(bm, BM_VERTS_OF_FACE, f, (void **)v, 4); - */ -void BM_face_as_array_vert_quad(BMFace *f, BMVert *r_verts[4]) ATTR_NONNULL(); +BLI_INLINE void BM_face_as_array_vert_tri(BMFace *f, BMVert *r_verts[3]) +{ + BMLoop *l = BM_FACE_FIRST_LOOP(f); + + BLI_assert(f->len == 3); + + r_verts[0] = l->v; + l = l->next; + r_verts[1] = l->v; + l = l->next; + r_verts[2] = l->v; +} /** * Small utility functions for fast access @@ -261,7 +268,24 @@ void BM_face_as_array_vert_quad(BMFace *f, BMVert *r_verts[4]) ATTR_NONNULL(); * faster alternative to: * BM_iter_as_array(bm, BM_LOOPS_OF_FACE, f, (void **)l, 3); */ -void BM_face_as_array_loop_tri(BMFace *f, BMLoop *r_loops[3]) ATTR_NONNULL(); +BLI_INLINE void BM_face_as_array_loop_tri(BMFace *f, BMLoop *r_loops[3]) +{ + BMLoop *l = BM_FACE_FIRST_LOOP(f); + + BLI_assert(f->len == 3); + + r_loops[0] = l; + l = l->next; + r_loops[1] = l; + l = l->next; + r_loops[2] = l; +} + +/** + * faster alternative to: + * BM_iter_as_array(bm, BM_VERTS_OF_FACE, f, (void **)v, 4); + */ +void BM_face_as_array_vert_quad(BMFace *f, BMVert *r_verts[4]) ATTR_NONNULL(); /** * faster alternative to: * BM_iter_as_array(bm, BM_LOOPS_OF_FACE, f, (void **)l, 4); @@ -287,6 +311,8 @@ void BM_vert_tri_calc_tangent_edge(BMVert *verts[3], float r_tangent[3]); */ void BM_vert_tri_calc_tangent_edge_pair(BMVert *verts[3], float r_tangent[3]); +void BM_face_as_array_vert_quad(BMFace *f, BMVert *r_verts[4]); + #ifdef __cplusplus } #endif diff --git a/source/blender/bmesh/intern/bmesh_private.h b/source/blender/bmesh/intern/bmesh_private.h index 2225841e278..c92bd3dc0d4 100644 --- a/source/blender/bmesh/intern/bmesh_private.h +++ b/source/blender/bmesh/intern/bmesh_private.h @@ -46,6 +46,8 @@ int bmesh_elem_check(void *element, char htype); int bmesh_radial_length(const BMLoop *l); int bmesh_disk_count_at_most(const BMVert *v, int count_max); int bmesh_disk_count(const BMVert *v); +void bm_rebuild_idmap(BMesh *bm); +void bm_alloc_toolflags_cdlayers(BMesh *bm, bool set_elems); /** * Internal BMHeader.api_flag diff --git a/source/blender/bmesh/intern/bmesh_structure.c b/source/blender/bmesh/intern/bmesh_structure.c index 7d340f02f2e..d929ed7a316 100644 --- a/source/blender/bmesh/intern/bmesh_structure.c +++ b/source/blender/bmesh/intern/bmesh_structure.c @@ -7,6 +7,7 @@ * Low level routines for manipulating the BM structure. */ +#include "BLI_asan.h" #include "BLI_utildefines.h" #include "bmesh.h" @@ -566,3 +567,26 @@ bool bmesh_loop_validate(BMFace *f) return true; } + +static int sizes[] = {-1, + (int)sizeof(BMVert), + (int)sizeof(BMEdge), + 0, + (int)sizeof(BMLoop), + -1, + -1, + -1, + (int)sizeof(BMFace)}; + +bool BM_elem_is_free(BMElem *elem, int htype) +{ + BLI_asan_unpoison(elem, sizes[htype]); + + bool ret = elem->head.htype != htype; + + if (ret) { + BLI_asan_poison(elem, sizes[htype]); + } + + return ret; +} diff --git a/source/blender/bmesh/intern/bmesh_structure.h b/source/blender/bmesh/intern/bmesh_structure.h index 06234318627..54f46901121 100644 --- a/source/blender/bmesh/intern/bmesh_structure.h +++ b/source/blender/bmesh/intern/bmesh_structure.h @@ -134,4 +134,15 @@ BMEdge *bmesh_disk_edge_exists(const BMVert *v1, const BMVert *v2) ATTR_WARN_UNU ATTR_NONNULL(); bool bmesh_disk_validate(int len, BMEdge *e, BMVert *v) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); +#define BM_DEFAULT_IDMAP_SIZE (1 << 12) + #include "intern/bmesh_structure_inline.h" + +void bm_assign_id(BMesh *bm, BMElem *elem, uint id, bool check_unique); +void bm_assign_id_intern(BMesh *bm, BMElem *elem, uint id); +void bm_alloc_id(BMesh *bm, BMElem *elem); +void bm_free_id(BMesh *bm, BMElem *elem); +void bm_init_idmap_cdlayers(BMesh *bm); +void bm_update_idmap_cdlayers(BMesh *bm); +void bm_free_ids_check(BMesh *bm, uint id); +void bm_id_freelist_push(BMesh *bm, uint id); diff --git a/source/blender/bmesh/operators/bmo_dupe.c b/source/blender/bmesh/operators/bmo_dupe.c index 1ed9d763e8c..766774ad281 100644 --- a/source/blender/bmesh/operators/bmo_dupe.c +++ b/source/blender/bmesh/operators/bmo_dupe.c @@ -14,6 +14,7 @@ #include "bmesh.h" #include "intern/bmesh_operators_private.h" /* own include */ +#include "intern/bmesh_structure.h" /* local flag define */ #define DUPE_INPUT 1 /* input from operator */ @@ -46,6 +47,13 @@ static BMVert *bmo_vert_copy(BMOperator *op, /* Copy attributes */ BM_elem_attrs_copy(bm_src, bm_dst, v_src, v_dst); + /* Handle Ids */ + bm_alloc_id(bm_dst, (BMElem *)v_dst); + // short **flags = BM_ELEM_CD_GET_VOID_P( + // v_dst, bm_dst->vdata.layers[bm_dst->vdata.typemap[CD_TOOLFLAGS]].offset); + + bm_elem_check_toolflags(bm_dst, (BMElem *)v_dst); + // printf("%p\n", flags); /* Mark the vert for output */ BMO_vert_flag_enable(bm_dst, v_dst, DUPE_NEW); @@ -108,6 +116,10 @@ static BMEdge *bmo_edge_copy(BMOperator *op, /* Copy attributes */ BM_elem_attrs_copy(bm_src, bm_dst, e_src, e_dst); + /* Handle Ids */ + bm_alloc_id(bm_dst, (BMElem *)e_dst); + bm_elem_check_toolflags(bm_dst, (BMElem *)e_dst); + /* Mark the edge for output */ BMO_edge_flag_enable(bm_dst, e_dst, DUPE_NEW); @@ -160,11 +172,16 @@ static BMFace *bmo_face_copy(BMOperator *op, /* Copy attributes */ BM_elem_attrs_copy(bm_src, bm_dst, f_src, f_dst); + /* Handle Ids and toolflags */ + bm_alloc_id(bm_dst, (BMElem *)f_dst); + bm_elem_check_toolflags(bm_dst, (BMElem *)f_dst); + /* copy per-loop custom data */ l_iter_src = l_first_src; l_iter_dst = BM_FACE_FIRST_LOOP(f_dst); do { BM_elem_attrs_copy(bm_src, bm_dst, l_iter_src, l_iter_dst); + bm_alloc_id(bm_dst, (BMElem *)l_iter_dst); } while ((void)(l_iter_dst = l_iter_dst->next), (l_iter_src = l_iter_src->next) != l_first_src); /* Mark the face for output */ @@ -576,7 +593,7 @@ void bmo_spin_exec(BMesh *bm, BMOperator *op) BMEdge *e_src = (BMEdge *)elem_array[i]; BMEdge *e_dst = BM_edge_find_double(e_src); if (e_dst != NULL) { - BM_edge_splice(bm, e_dst, e_src); + BM_edge_splice(bm, e_dst, e_src, false); elem_array_len--; elem_array[i] = elem_array[elem_array_len]; continue; diff --git a/source/blender/bmesh/operators/bmo_extrude.c b/source/blender/bmesh/operators/bmo_extrude.c index 48d8cded95d..592c57c0a37 100644 --- a/source/blender/bmesh/operators/bmo_extrude.c +++ b/source/blender/bmesh/operators/bmo_extrude.c @@ -21,12 +21,7 @@ #define USE_EDGE_REGION_FLAGS -enum { - EXT_INPUT = 1, - EXT_KEEP = 2, - EXT_DEL = 4, - EXT_TAG = 8, -}; +enum { EXT_INPUT = 1, EXT_KEEP = 2, EXT_DEL = 4, EXT_TAG = 8, EXT_ALT = 16 }; #define VERT_MARK 1 #define EDGE_MARK 1 @@ -318,13 +313,17 @@ void bmo_extrude_face_region_exec(BMesh *bm, BMOperator *op) BMFace *f; bool found, delorig = false; BMOpSlot *slot_facemap_out; + BMOpSlot *slot_sidemap_out; BMOpSlot *slot_edges_exclude; + const bool use_normal_flip = BMO_slot_bool_get(op->slots_in, "use_normal_flip"); const bool use_normal_from_adjacent = BMO_slot_bool_get(op->slots_in, "use_normal_from_adjacent"); const bool use_dissolve_ortho_edges = BMO_slot_bool_get(op->slots_in, "use_dissolve_ortho_edges"); + bool side_tag = EXT_ALT; + /* initialize our sub-operators */ BMO_op_initf(bm, &dupeop, @@ -538,29 +537,37 @@ void bmo_extrude_face_region_exec(BMesh *bm, BMOperator *op) char e_hflag[2]; bool e_hflag_ok = bm_extrude_region_edge_flag(f_verts[2], e_hflag); f_edges[1] = BM_edge_create(bm, f_verts[1], f_verts[2], NULL, BM_CREATE_NOP); + if (e_hflag_ok) { BM_elem_flag_enable(f_edges[1], e_hflag[0]); BM_elem_flag_disable(f_edges[1], e_hflag[1]); } } + BMO_elem_flag_set(bm, f_edges[1], side_tag, true); f_edges[3] = BM_edge_exists(f_verts[3], f_verts[0]); + if (f_edges[3] == NULL) { char e_hflag[2]; bool e_hflag_ok = bm_extrude_region_edge_flag(f_verts[3], e_hflag); f_edges[3] = BM_edge_create(bm, f_verts[3], f_verts[0], NULL, BM_CREATE_NOP); + if (e_hflag_ok) { BM_elem_flag_enable(f_edges[3], e_hflag[0]); BM_elem_flag_disable(f_edges[3], e_hflag[1]); } } + BMO_elem_flag_set(bm, f_edges[3], side_tag, true); f = BM_face_create(bm, f_verts, f_edges, 4, NULL, BM_CREATE_NOP); + #else f = BM_face_create_verts(bm, f_verts, 4, NULL, BM_CREATE_NOP, true); #endif bm_extrude_copy_face_loop_attributes(bm, f); + BMO_elem_flag_set(bm, (BMElem *)f, side_tag, true); + if (join_face) { BMVert *v1 = e->v1; BMVert *v2 = e->v2; @@ -591,7 +598,7 @@ void bmo_extrude_face_region_exec(BMesh *bm, BMOperator *op) } } - BM_edge_create(bm, v, v2, NULL, BM_CREATE_NO_DOUBLE); + BMO_elem_flag_set(bm, BM_edge_create(bm, v, v2, NULL, BM_CREATE_NO_DOUBLE), side_tag, true); } if (dissolve_verts) { @@ -602,7 +609,14 @@ void bmo_extrude_face_region_exec(BMesh *bm, BMOperator *op) BMEdge *e_other = BM_DISK_EDGE_NEXT(e, v); if ((e_other == e) || (BM_DISK_EDGE_NEXT(e_other, v) == e)) { /* Loose edge or BMVert is edge pair. */ - BM_edge_collapse(bm, BMO_elem_flag_test(bm, e, EXT_TAG) ? e : e_other, v, true, true); + BM_edge_collapse(bm, + BMO_elem_flag_test(bm, e, EXT_TAG) ? e : e_other, + v, + true, + true, + false, + false, + NULL); } else { BLI_assert(!BM_vert_is_edge_pair(v)); @@ -611,6 +625,9 @@ void bmo_extrude_face_region_exec(BMesh *bm, BMOperator *op) MEM_freeN(dissolve_verts); } + BMO_slot_buffer_from_enabled_flag( + bm, op, op->slots_out, "side_geom.out", BM_FACE | BM_EDGE, side_tag); + /* cleanup */ if (delorig) { BMO_op_finish(bm, &delop); diff --git a/source/blender/bmesh/operators/bmo_fill_grid.c b/source/blender/bmesh/operators/bmo_fill_grid.c index e256e261eaa..fefc60a8366 100644 --- a/source/blender/bmesh/operators/bmo_fill_grid.c +++ b/source/blender/bmesh/operators/bmo_fill_grid.c @@ -706,7 +706,7 @@ void bmo_grid_fill_exec(BMesh *bm, BMOperator *op) GSetIterator gs_iter; GSET_ITER (gs_iter, split_edges) { BMEdge *e = BLI_gsetIterator_getKey(&gs_iter); - BM_edge_collapse(bm, e, e->v2, true, true); + BM_edge_collapse(bm, e, e->v2, true, true, false, false, NULL); } BLI_gset_free(split_edges, NULL); } diff --git a/source/blender/bmesh/operators/bmo_inset.c b/source/blender/bmesh/operators/bmo_inset.c index f46b9158c45..16595f2075d 100644 --- a/source/blender/bmesh/operators/bmo_inset.c +++ b/source/blender/bmesh/operators/bmo_inset.c @@ -1241,11 +1241,18 @@ void bmo_inset_region_exec(BMesh *bm, BMOperator *op) InterpFace *iface = iface_array[BM_elem_index_get(es->l->f)]; const int i_a = BM_elem_index_get(l_a_other); const int i_b = BM_elem_index_get(l_b_other); + + int ida = bm_save_id(bm, (BMElem *)l_a); + int idb = bm_save_id(bm, (BMElem *)l_b); + CustomData_bmesh_free_block_data(&bm->ldata, l_b->head.data); CustomData_bmesh_free_block_data(&bm->ldata, l_a->head.data); CustomData_bmesh_copy_data(&bm->ldata, &bm->ldata, iface->blocks_l[i_a], &l_b->head.data); CustomData_bmesh_copy_data(&bm->ldata, &bm->ldata, iface->blocks_l[i_b], &l_a->head.data); + bm_restore_id(bm, (BMElem *)l_a, ida); + bm_restore_id(bm, (BMElem *)l_b, idb); + #ifdef USE_LOOP_CUSTOMDATA_MERGE if (has_math_ldata) { BMEdge *e_connect; diff --git a/source/blender/bmesh/operators/bmo_mesh_convert.c b/source/blender/bmesh/operators/bmo_mesh_convert.c index 7cbbba80bb7..1e27b9f54d4 100644 --- a/source/blender/bmesh/operators/bmo_mesh_convert.c +++ b/source/blender/bmesh/operators/bmo_mesh_convert.c @@ -25,7 +25,8 @@ void bmo_mesh_to_bmesh_exec(BMesh *bm, BMOperator *op) Mesh *me = BMO_slot_ptr_get(op->slots_in, "mesh"); bool set_key = BMO_slot_bool_get(op->slots_in, "use_shapekey"); - BM_mesh_bm_from_me(bm, + BM_mesh_bm_from_me(NULL, + bm, me, (&(struct BMeshFromMeshParams){ .use_shapekey = set_key, @@ -52,6 +53,7 @@ void bmo_bmesh_to_mesh_exec(BMesh *bm, BMOperator *op) /* Object *ob = BMO_slot_ptr_get(op, "object"); */ BM_mesh_bm_to_me(G.main, + NULL, bm, me, (&(struct BMeshToMeshParams){ diff --git a/source/blender/bmesh/operators/bmo_primitive.c b/source/blender/bmesh/operators/bmo_primitive.c index 70984ba10bd..0380645f2f5 100644 --- a/source/blender/bmesh/operators/bmo_primitive.c +++ b/source/blender/bmesh/operators/bmo_primitive.c @@ -1471,7 +1471,7 @@ void bmo_create_cone_exec(BMesh *bm, BMOperator *op) for (int i = 0; i < side_faces_len; i++) { f = side_faces[i]; BMLoop *l = BM_FACE_FIRST_LOOP(f); - BM_edge_collapse(bm, l->prev->e, l->prev->v, true, true); + BM_edge_collapse(bm, l->prev->e, l->prev->v, true, true, false, false, NULL); } } @@ -1483,7 +1483,7 @@ void bmo_create_cone_exec(BMesh *bm, BMOperator *op) for (int i = 0; i < side_faces_len; i++) { f = side_faces[i]; BMLoop *l = BM_FACE_FIRST_LOOP(f); - BM_edge_collapse(bm, l->next->e, l->next->v, true, true); + BM_edge_collapse(bm, l->next->e, l->next->v, true, true, false, false, NULL); } } diff --git a/source/blender/bmesh/operators/bmo_removedoubles.c b/source/blender/bmesh/operators/bmo_removedoubles.c index bf04228efef..b588b4a2c1e 100644 --- a/source/blender/bmesh/operators/bmo_removedoubles.c +++ b/source/blender/bmesh/operators/bmo_removedoubles.c @@ -14,6 +14,7 @@ #include "BLI_math.h" #include "BLI_stack.h" #include "BLI_utildefines_stack.h" +#include "DNA_meshdata_types.h" #include "BKE_customdata.h" @@ -276,7 +277,12 @@ void bmo_weld_verts_exec(BMesh *bm, BMOperator *op) bmesh_face_swap_data(f_new, f); if (bm->use_toolflags) { - SWAP(BMFlagLayer *, ((BMFace_OFlag *)f)->oflags, ((BMFace_OFlag *)f_new)->oflags); + MToolFlags *flags = (MToolFlags *)BM_ELEM_CD_GET_VOID_P( + f, bm->pdata.layers[bm->pdata.typemap[CD_TOOLFLAGS]].offset); + MToolFlags *flags_new = (MToolFlags *)BM_ELEM_CD_GET_VOID_P( + f_new, bm->pdata.layers[bm->pdata.typemap[CD_TOOLFLAGS]].offset); + + SWAP(short *, flags->flag, flags_new->flag); } BMO_face_flag_disable(bm, f, ELE_DEL); @@ -435,6 +441,8 @@ void bmo_pointmerge_exec(BMesh *bm, BMOperator *op) BMO_op_finish(bm, &weldop); } +#define USE_BM_EDGE_COLLAPSE + void bmo_collapse_exec(BMesh *bm, BMOperator *op) { BMOperator weldop; @@ -501,6 +509,12 @@ void bmo_collapse_exec(BMesh *bm, BMOperator *op) uint j; BLI_stack_pop(edge_stack, &e); +#ifdef USE_BM_EDGE_COLLAPSE + if (e->head.htype != BM_EDGE) { + continue; + } +#endif + for (j = 0; j < 2; j++) { BMVert *v_src = *((&e->v1) + j); @@ -508,6 +522,11 @@ void bmo_collapse_exec(BMesh *bm, BMOperator *op) if ((v_src != v_tar) && !BM_elem_flag_test(v_src, BM_ELEM_TAG)) { BM_elem_flag_enable(v_src, BM_ELEM_TAG); BMO_slot_map_elem_insert(&weldop, slot_targetmap, v_src, v_tar); + +#ifdef USE_BM_EDGE_COLLAPSE + BM_edge_collapse(bm, e, v_src, true, true, true, true, NULL); + break; +#endif } } } @@ -516,7 +535,10 @@ void bmo_collapse_exec(BMesh *bm, BMOperator *op) BLI_stack_free(edge_stack); +#ifndef USE_BM_EDGE_COLLAPSE BMO_op_exec(bm, &weldop); +#endif + BMO_op_finish(bm, &weldop); BMW_end(&walker); diff --git a/source/blender/bmesh/operators/bmo_subdivide.c b/source/blender/bmesh/operators/bmo_subdivide.c index c036fe86d9e..89a8ef1f857 100644 --- a/source/blender/bmesh/operators/bmo_subdivide.c +++ b/source/blender/bmesh/operators/bmo_subdivide.c @@ -139,6 +139,7 @@ static BMEdge *connect_smallest_face(BMesh *bm, BMVert *v_a, BMVert *v_b, BMFace if (r_f_new) { *r_f_new = f_new; } + return l_new ? l_new->e : NULL; } @@ -847,6 +848,11 @@ static void tri_3edge_subdivide(BMesh *bm, for (j = 0; j < i; j++) { e = connect_smallest_face(bm, lines[i][j], lines[i + 1][j + 1], &f_new); + if (!e) { + printf("%s: subdivide error;\n", __func__); + continue; + } + BMO_edge_flag_enable(bm, e, ELE_INNER); BMO_face_flag_enable(bm, f_new, ELE_INNER); diff --git a/source/blender/bmesh/operators/bmo_symmetrize.c b/source/blender/bmesh/operators/bmo_symmetrize.c index 13afff41263..d46ba6304be 100644 --- a/source/blender/bmesh/operators/bmo_symmetrize.c +++ b/source/blender/bmesh/operators/bmo_symmetrize.c @@ -14,6 +14,8 @@ #define ELE_OUT 1 +#include "BLI_compiler_attrs.h" + void bmo_symmetrize_exec(BMesh *bm, BMOperator *op) { const float dist = BMO_slot_float_get(op->slots_in, "dist"); @@ -82,6 +84,10 @@ void bmo_symmetrize_exec(BMesh *bm, BMOperator *op) slot_targetmap = BMO_slot_get(op_weld.slots_in, "targetmap"); BMO_ITER (v, &siter, op_bisect.slots_out, "geom_cut.out", BM_VERT) { + if (!BM_vert_is_boundary(v)) { + continue; + } + BMVert *v_dupe = BMO_slot_map_elem_get(slot_vertmap, v); BMO_slot_map_elem_insert(&op_weld, slot_targetmap, v_dupe, v); } diff --git a/source/blender/bmesh/tests/bmesh_core_test.cc b/source/blender/bmesh/tests/bmesh_core_test.cc index a0f6ea2706b..9faa1448221 100644 --- a/source/blender/bmesh/tests/bmesh_core_test.cc +++ b/source/blender/bmesh/tests/bmesh_core_test.cc @@ -12,8 +12,10 @@ TEST(bmesh_core, BMVertCreate) BMVert *bv1, *bv2, *bv3; const float co1[3] = {1.0f, 2.0f, 0.0f}; - BMeshCreateParams bmesh_create_params{}; + BMeshCreateParams bmesh_create_params = {0}; + bmesh_create_params.use_toolflags = true; + bm = BM_mesh_create(&bm_mesh_allocsize_default, &bmesh_create_params); EXPECT_EQ(bm->totvert, 0); /* make a custom layer so we can see if it is copied properly */ diff --git a/source/blender/bmesh/tools/bmesh_decimate_collapse.c b/source/blender/bmesh/tools/bmesh_decimate_collapse.c index 1ea799f64e0..90735e9cee2 100644 --- a/source/blender/bmesh/tools/bmesh_decimate_collapse.c +++ b/source/blender/bmesh/tools/bmesh_decimate_collapse.c @@ -1022,8 +1022,8 @@ static bool bm_edge_collapse(BMesh *bm, e_a_other[1]->head.hflag |= e_a_other[0]->head.hflag; e_b_other[1]->head.hflag |= e_b_other[0]->head.hflag; - BM_edge_splice(bm, e_a_other[1], e_a_other[0]); - BM_edge_splice(bm, e_b_other[1], e_b_other[0]); + BM_edge_splice(bm, e_a_other[1], e_a_other[0], false); + BM_edge_splice(bm, e_b_other[1], e_b_other[0], false); #ifdef USE_SYMMETRY /* update mirror map */ @@ -1082,7 +1082,7 @@ static bool bm_edge_collapse(BMesh *bm, BM_vert_splice(bm, v_other, v_clear); e_a_other[1]->head.hflag |= e_a_other[0]->head.hflag; - BM_edge_splice(bm, e_a_other[1], e_a_other[0]); + BM_edge_splice(bm, e_a_other[1], e_a_other[0], false); #ifdef USE_SYMMETRY /* update mirror map */ diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt index ce3d09e46d9..bf97de19075 100644 --- a/source/blender/draw/CMakeLists.txt +++ b/source/blender/draw/CMakeLists.txt @@ -791,6 +791,9 @@ if(WITH_GTESTS) endif() endif() +remove_cc_flag("-fsanitize=address") +remove_cc_flag("/fsanitize=address") + if(WITH_TBB) add_definitions(-DWITH_TBB) diff --git a/source/blender/draw/DRW_engine.h b/source/blender/draw/DRW_engine.h index 8c5f1b70cc0..2bb23d1c2a8 100644 --- a/source/blender/draw/DRW_engine.h +++ b/source/blender/draw/DRW_engine.h @@ -27,6 +27,9 @@ struct GPUVertFormat; struct CustomDataLayer; struct CustomData; struct GPUViewport; +struct GPUVertFormat; +struct CustomData; +struct CustomDataLayer; struct ID; struct Main; struct Object; @@ -74,6 +77,12 @@ void DRW_draw_view(const struct bContext *C); * Draw render engine info. */ void DRW_draw_region_engine_info(int xoffset, int *yoffset, int line_height); +void DRW_make_cdlayer_attr_aliases(struct GPUVertFormat *format, + char *base_name, + const struct CustomData *data, + const struct CustomDataLayer *cl, + bool is_active_render, + bool is_active_layer); /** * Used for both regular and off-screen drawing. diff --git a/source/blender/draw/DRW_pbvh.hh b/source/blender/draw/DRW_pbvh.hh index 74cf1f93594..891c2ff360c 100644 --- a/source/blender/draw/DRW_pbvh.hh +++ b/source/blender/draw/DRW_pbvh.hh @@ -31,13 +31,13 @@ struct BMesh; struct PBVH_GPU_Args { int pbvh_type; - BMesh *bm; - const Mesh *me; + struct BMesh *bm; + const struct Mesh *me; const float (*vert_positions)[3]; - const MLoop *mloop; - const MPoly *mpoly; + const struct MLoop *mloop; + const struct MPoly *mpoly; int mesh_verts_num, mesh_faces_num, mesh_grids_num; - CustomData *vdata, *ldata, *pdata; + struct CustomData *vdata, *ldata, *pdata; const float (*vert_normals)[3]; const char *active_color; @@ -46,8 +46,8 @@ struct PBVH_GPU_Args { int face_sets_color_seed, face_sets_color_default; int *face_sets; /* for PBVH_FACES and PBVH_GRIDS */ - SubdivCCG *subdiv_ccg; - const DMFlagMat *grid_flag_mats; + struct SubdivCCG *subdiv_ccg; + const struct DMFlagMat *grid_flag_mats; const int *grid_indices; CCGKey ccg_key; CCGElem **grids; @@ -61,12 +61,18 @@ struct PBVH_GPU_Args { int node_verts_num; - const MLoopTri *mlooptri; - PBVHNode *node; + const struct MLoopTri *mlooptri; + struct PBVHNode *node; + + bool flat_vcol_shading; + bool show_orig; /* BMesh. */ - GSet *bm_unique_vert, *bm_other_verts, *bm_faces; + struct TableGSet *bm_unique_vert, *bm_other_verts, *bm_faces; int cd_mask_layer; + struct PBVHTriBuf *tribuf, *tri_buffers; + int tot_tri_buffers, updategen; + struct MSculptVert *msculptverts; }; void DRW_pbvh_node_update(PBVHBatches *batches, PBVH_GPU_Args *args); diff --git a/source/blender/draw/engines/overlay/overlay_sculpt.cc b/source/blender/draw/engines/overlay/overlay_sculpt.cc index 3377aa5394b..b5a5691781b 100644 --- a/source/blender/draw/engines/overlay/overlay_sculpt.cc +++ b/source/blender/draw/engines/overlay/overlay_sculpt.cc @@ -25,9 +25,18 @@ void OVERLAY_sculpt_cache_init(OVERLAY_Data *vedata) GPUShader *sh = OVERLAY_shader_sculpt_mask(); pd->sculpt_mask_grp = grp = DRW_shgroup_create(sh, psl->sculpt_mask_ps); + DRW_shgroup_uniform_float_copy(grp, "maskOpacity", pd->overlay.sculpt_mode_mask_opacity); + DRW_shgroup_uniform_float_copy( grp, "faceSetsOpacity", pd->overlay.sculpt_mode_face_sets_opacity); + DRW_shgroup_uniform_float_copy( + grp, "faceSetsPatSeed", pd->overlay.sculpt_mode_face_sets_moire_seed); + DRW_shgroup_uniform_bool_copy( + grp, "useMoire", pd->overlay.sculpt_flag & V3D_OVERLAY_SCULPT_FSET_MOIRE); + + DRW_shgroup_uniform_float_copy( + grp, "faceSetsPatScale", 0.5f * (1.1f - pd->overlay.sculpt_mode_face_sets_moire_scale)); } void OVERLAY_sculpt_cache_populate(OVERLAY_Data *vedata, Object *ob) @@ -45,7 +54,7 @@ void OVERLAY_sculpt_cache_populate(OVERLAY_Data *vedata, Object *ob) return; } - if (!pbvh_has_mask(pbvh) && !pbvh_has_face_sets(pbvh)) { + if (!BKE_pbvh_draw_mask(pbvh) && !BKE_pbvh_draw_face_sets(pbvh)) { /* The SculptSession and the PBVH can be created without a Mask data-layer or Face Set * data-layer. (masks data-layers are created after using a mask tool), so in these cases there * is nothing to draw. */ diff --git a/source/blender/draw/engines/overlay/shaders/overlay_sculpt_mask_frag.glsl b/source/blender/draw/engines/overlay/shaders/overlay_sculpt_mask_frag.glsl index 9650af755c5..10f17c21b1d 100644 --- a/source/blender/draw/engines/overlay/shaders/overlay_sculpt_mask_frag.glsl +++ b/source/blender/draw/engines/overlay/shaders/overlay_sculpt_mask_frag.glsl @@ -1,5 +1,37 @@ +uniform float faceSetsPatSeed; +uniform float faceSetsPatScale; +uniform bool useMoire; + +float tent(float f) +{ + return 1.0 - abs(fract(f) - 0.5) * 2.0; +} void main() { - fragColor = vec4(faceset_color * vec3(mask_color), 1.0); + vec3 final_color = faceset_color; + vec3 white = vec3(1.0, 1.0, 1.0); + + if (!useMoire) { + fragColor = vec4(final_color * vec3(mask_color), 1.0); + return; + } + + vec2 xy = gl_FragCoord.xy * faceSetsPatScale; + + /* Basic moire pattern */ + + float seed = 1.0 / 3.0 + faceSetsPatSeed; + + float dx1 = tent(xy.x); + float dy1 = tent(xy.y); + float dx2 = tent(tent(seed) * xy.x + tent(seed * seed + 0.5) * xy.y); + float dy2 = tent(tent(seed) * xy.y - tent(seed * seed + 0.5) * xy.x); + + float fac = (dx1 + dy1 + dx2 + dy2) / 4.0; + fac = step(fac, 1.0 - faceSetsOpacity); + + final_color += (white - final_color) * fac; + + fragColor = vec4(final_color * vec3(mask_color), 1.0); } diff --git a/source/blender/draw/engines/overlay/shaders/overlay_sculpt_mask_vert.glsl b/source/blender/draw/engines/overlay/shaders/overlay_sculpt_mask_vert.glsl index 36c0e6a0acf..0a03464962c 100644 --- a/source/blender/draw/engines/overlay/shaders/overlay_sculpt_mask_vert.glsl +++ b/source/blender/draw/engines/overlay/shaders/overlay_sculpt_mask_vert.glsl @@ -1,3 +1,5 @@ +uniform bool useMoire; + #pragma BLENDER_REQUIRE(common_view_clipping_lib.glsl) #pragma BLENDER_REQUIRE(common_view_lib.glsl) @@ -6,7 +8,7 @@ void main() vec3 world_pos = point_object_to_world(pos); gl_Position = point_world_to_ndc(world_pos); - faceset_color = mix(vec3(1.0), fset, faceSetsOpacity); + faceset_color = !useMoire ? mix(vec3(1.0), fset, sqrt(faceSetsOpacity)) : fset; mask_color = 1.0 - (msk * maskOpacity); view_clipping_distances(world_pos); diff --git a/source/blender/draw/intern/draw_cache.c b/source/blender/draw/intern/draw_cache.c index f1f62d8aee9..8f5275a9beb 100644 --- a/source/blender/draw/intern/draw_cache.c +++ b/source/blender/draw/intern/draw_cache.c @@ -21,6 +21,7 @@ #include "BLI_math.h" #include "BLI_utildefines.h" +#include "BKE_customdata.h" #include "BKE_object.h" #include "BKE_paint.h" @@ -495,6 +496,46 @@ static void sphere_lat_lon_vert(GPUVertBuf *vbo, int *v_ofs, float lat, float lo (*v_ofs)++; } +void DRW_make_cdlayer_attr_aliases(GPUVertFormat *format, + char *base_name, + const CustomData *data, + const CustomDataLayer *cl, + bool is_active_render, + bool is_active_layer) +{ + char attr_name[32], attr_safe_name[GPU_MAX_SAFE_ATTR_NAME]; + const char *layer_name = cl->name; + + int i = (int)(cl - data->typemap[cl->type]); + + GPU_vertformat_safe_attr_name(layer_name, attr_safe_name, GPU_MAX_SAFE_ATTR_NAME); + + /* Attribute layer name. */ + BLI_snprintf(attr_name, sizeof(attr_name), "%s%s", base_name, attr_safe_name); + GPU_vertformat_alias_add(format, attr_name); + + /* Auto layer name. */ + BLI_snprintf(attr_name, sizeof(attr_name), "a%s", attr_safe_name); + GPU_vertformat_alias_add(format, attr_name); + + /* Active render layer name. */ + if (is_active_render) { + GPU_vertformat_alias_add(format, base_name); + } + + /* Active display layer name. */ + if (is_active_layer) { + BLI_snprintf(attr_name, sizeof(attr_name), "a%s", base_name); + GPU_vertformat_alias_add(format, attr_name); + } + + /* Stencil mask layer name. */ + if (i == CustomData_get_stencil_layer(data, cl->type)) { + BLI_snprintf(attr_name, sizeof(attr_name), "m%s", base_name); + GPU_vertformat_alias_add(format, attr_name); + } +} + GPUBatch *DRW_cache_sphere_get(const eDRWLevelOfDetail level_of_detail) { BLI_assert(level_of_detail >= DRW_LOD_LOW && level_of_detail < DRW_LOD_MAX); diff --git a/source/blender/draw/intern/draw_cache_impl_mesh.cc b/source/blender/draw/intern/draw_cache_impl_mesh.cc index bfeafdf7dcb..2e39d32fd0b 100644 --- a/source/blender/draw/intern/draw_cache_impl_mesh.cc +++ b/source/blender/draw/intern/draw_cache_impl_mesh.cc @@ -1358,7 +1358,7 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, bool cd_uv_update = false; /* Early out */ - if (cache->batch_requested == 0) { + if (!cache || cache->batch_requested == 0) { #ifdef DEBUG drw_mesh_batch_cache_check_available(task_graph, me); #endif diff --git a/source/blender/draw/intern/draw_cache_impl_subdivision.cc b/source/blender/draw/intern/draw_cache_impl_subdivision.cc index 25ec4a578d5..8d205a0fd70 100644 --- a/source/blender/draw/intern/draw_cache_impl_subdivision.cc +++ b/source/blender/draw/intern/draw_cache_impl_subdivision.cc @@ -2208,9 +2208,11 @@ void DRW_subdivide_loose_geom(DRWSubdivCache *subdiv_cache, MeshBufferCache *cac MeshElemMap *vert_to_edge_map; BKE_mesh_vert_edge_map_create(&vert_to_edge_map, &vert_to_edge_buffer, + reinterpret_cast(coarse_positions.data()), coarse_edges.data(), - coarse_mesh->totvert, - coarse_edges.size()); + coarse_positions.size(), + coarse_edges.size(), + false); for (int i = 0; i < coarse_loose_edge_len; i++) { const int coarse_edge_index = cache->loose_geom.edges[i]; diff --git a/source/blender/draw/intern/draw_debug.cc b/source/blender/draw/intern/draw_debug.cc index ca17aa1de82..e584a48c2e0 100644 --- a/source/blender/draw/intern/draw_debug.cc +++ b/source/blender/draw/intern/draw_debug.cc @@ -7,6 +7,8 @@ * \brief Simple API to draw debug shapes in the viewport. */ +#define DRAW_DEBUG + #include "BKE_object.h" #include "BLI_link_utils.h" #include "BLI_math_matrix.hh" diff --git a/source/blender/draw/intern/draw_manager.c b/source/blender/draw/intern/draw_manager.c index 1af10275e2a..e70e21df752 100644 --- a/source/blender/draw/intern/draw_manager.c +++ b/source/blender/draw/intern/draw_manager.c @@ -14,6 +14,7 @@ #include "BLI_string.h" #include "BLI_task.h" #include "BLI_threads.h" +#include "BLI_utildefines.h" #include "BLF_api.h" diff --git a/source/blender/draw/intern/draw_manager_data.cc b/source/blender/draw/intern/draw_manager_data.cc index de78c8216de..fd5c6fbabb7 100644 --- a/source/blender/draw/intern/draw_manager_data.cc +++ b/source/blender/draw/intern/draw_manager_data.cc @@ -1356,6 +1356,7 @@ static void drw_sculpt_generate_calls(DRWSculptCallbackData *scd) BKE_pbvh_update_normals(pbvh, mesh->runtime->subdiv_ccg); BKE_pbvh_draw_cb(pbvh, + mesh, update_only_visible, &update_frustum, &draw_frustum, diff --git a/source/blender/draw/intern/draw_pbvh.cc b/source/blender/draw/intern/draw_pbvh.cc index 84dd9e1f46b..1c654ed3815 100644 --- a/source/blender/draw/intern/draw_pbvh.cc +++ b/source/blender/draw/intern/draw_pbvh.cc @@ -179,6 +179,7 @@ struct PBVHBatches { bool needs_tri_index = false; int material_index = 0; + bool freed = false; /* Stuff for displaying coarse multires grids. */ GPUIndexBuf *tri_index_coarse = nullptr; @@ -213,12 +214,7 @@ struct PBVHBatches { break; } case PBVH_BMESH: { - GSET_FOREACH_BEGIN (BMFace *, f, args->bm_faces) { - if (!BM_elem_flag_test(f, BM_ELEM_HIDDEN)) { - count++; - } - } - GSET_FOREACH_END(); + count = args->flat_vcol_shading ? args->tribuf->tottri * 6 : args->tribuf->tottri; } } @@ -247,6 +243,8 @@ struct PBVHBatches { GPU_INDEXBUF_DISCARD_SAFE(tri_index); GPU_INDEXBUF_DISCARD_SAFE(lines_index); + + freed = true; GPU_INDEXBUF_DISCARD_SAFE(tri_index_coarse); GPU_INDEXBUF_DISCARD_SAFE(lines_index_coarse); } @@ -585,11 +583,21 @@ struct PBVHBatches { switch (vbo.type) { case CD_PBVH_CO_TYPE: - foreach_faces( - [&](int /*buffer_i*/, int /*tri_i*/, int vertex_i, const MLoopTri * /*tri*/) { - *static_cast( - GPU_vertbuf_raw_step(&access)) = args->vert_positions[vertex_i]; - }); + if (args->show_orig) { + foreach_faces( + [&](int /*buffer_i*/, int /*tri_i*/, int vertex_i, const MLoopTri * /*tri*/) { + MSculptVert *mv = args->msculptverts + vertex_i; + + *static_cast(GPU_vertbuf_raw_step(&access)) = mv->origco; + }); + } + else { + foreach_faces( + [&](int /*buffer_i*/, int /*tri_i*/, int vertex_i, const MLoopTri * /*tri*/) { + *static_cast( + GPU_vertbuf_raw_step(&access)) = args->vert_positions[vertex_i]; + }); + } break; case CD_PBVH_NO_TYPE: fill_vbo_normal_faces(vbo, args, foreach_faces, &access); @@ -754,21 +762,119 @@ struct PBVHBatches { void fill_vbo_bmesh(PBVHVbo &vbo, PBVH_GPU_Args *args) { - auto foreach_bmesh = [&](std::function callback) { - GSET_FOREACH_BEGIN (BMFace *, f, args->bm_faces) { - if (BM_elem_flag_test(f, BM_ELEM_HIDDEN)) { - continue; - } + auto foreach_bmesh_normal = [&](std::function callback) { + for (int i : IndexRange(args->tribuf->tottri)) { + PBVHTri *tri = args->tribuf->tris + i; - BMLoop *l = f->l_first; - callback(l->prev); - callback(l); - callback(l->next); + for (int j = 0; j < 3; j++) { + callback(reinterpret_cast(tri->l[j])); + } } - GSET_FOREACH_END(); }; - faces_count = tris_count = count_faces(args); + BMVert v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12; + + auto foreach_bmesh_flat_vcol = [&](std::function callback) { + for (int i : IndexRange(args->tribuf->tottri)) { + PBVHTri *tri = args->tribuf->tris + i; + + BMLoop *la = reinterpret_cast(tri->l[0]); + BMLoop *lb = reinterpret_cast(tri->l[1]); + BMLoop *lc = reinterpret_cast(tri->l[2]); + + BMLoop l9 = *la, l1 = *la, l2 = *la; + BMLoop l3 = *lb, l4 = *lb, l5 = *lb; + BMLoop l6 = *lc, l7 = *lc, l8 = *lc; + BMLoop l10 = *la, l11 = *lb, l12 = *lc; + + v9 = *la->v, v1 = *la->v, v2 = *la->v; + v3 = *lb->v, v4 = *lb->v, v5 = *lb->v; + v6 = *lc->v, v7 = *lc->v, v8 = *lc->v; + v10 = *la->v, v11 = *lb->v, v12 = *lc->v; + + if (vbo.type == CD_PBVH_CO_TYPE) { + l1.v = &v1; + l2.v = &v2; + l3.v = &v3; + l4.v = &v4; + l5.v = &v5; + l6.v = &v6; + l7.v = &v7; + l8.v = &v8; + l9.v = &v9; + l10.v = &v10; + l11.v = &v11; + l12.v = &v12; + + float3 cent = la->v->co; + cent += lb->v->co; + cent += lc->v->co; + cent *= 1.0f / 3.0f; + + copy_v3_v3(v10.co, cent); + copy_v3_v3(v11.co, cent); + copy_v3_v3(v12.co, cent); + + float3 cent1 = la->v->co; + cent1 += lb->v->co; + cent1 *= 0.5f; + copy_v3_v3(v2.co, cent1); + copy_v3_v3(v3.co, cent1); + + float3 cent2 = lb->v->co; + cent2 += lc->v->co; + cent2 *= 0.5f; + copy_v3_v3(v5.co, cent2); + copy_v3_v3(v6.co, cent2); + + float3 cent3 = lc->v->co; + cent3 += la->v->co; + cent3 *= 0.5f; + copy_v3_v3(v8.co, cent3); + copy_v3_v3(v9.co, cent3); + } + + /* v4 + b + v3 v5 + v2 cents v6 + a c + v1 v9 v8 v7 + */ + callback(&l7); + callback(&l8); + callback(&l6); + callback(&l8); + callback(&l12); + callback(&l6); + + callback(&l1); + callback(&l2); + callback(&l9); + callback(&l2); + callback(&l10); + callback(&l9); + + callback(&l4); + callback(&l5); + callback(&l3); + callback(&l5); + callback(&l11); + callback(&l3); + } + }; + + std::function)> foreach_bmesh; + + if (args->flat_vcol_shading) { + foreach_bmesh = foreach_bmesh_flat_vcol; + } + else { + foreach_bmesh = foreach_bmesh_normal; + } + + faces_count = args->flat_vcol_shading ? args->tribuf->tottri * 6 : args->tribuf->tottri; + tris_count = faces_count; int existing_num = GPU_vertbuf_get_vertex_len(vbo.vert_buf); void *existing_data = GPU_vertbuf_get_data(vbo.vert_buf); @@ -783,6 +889,22 @@ struct PBVHBatches { GPUVertBufRaw access; GPU_vertbuf_attr_get_raw_data(vbo.vert_buf, 0, &access); + CustomData *cdata = nullptr; + switch (vbo.domain) { + case ATTR_DOMAIN_POINT: + cdata = &args->bm->vdata; + break; + case ATTR_DOMAIN_EDGE: + cdata = &args->bm->edata; + break; + case ATTR_DOMAIN_CORNER: + cdata = &args->bm->ldata; + break; + case ATTR_DOMAIN_FACE: + cdata = &args->bm->pdata; + break; + } + #if 0 /* Enable to fuzz GPU data (to check for over-allocation). */ existing_data = GPU_vertbuf_get_data(vbo.vert_buf); uchar *c = static_cast(existing_data); @@ -792,18 +914,86 @@ struct PBVHBatches { #endif switch (vbo.type) { - case CD_PROP_COLOR: - case CD_PROP_BYTE_COLOR: { - ushort4 white = {USHRT_MAX, USHRT_MAX, USHRT_MAX, USHRT_MAX}; + case CD_PROP_FLOAT2: { + const int cd_uv = CustomData_get_offset_named( + &args->bm->ldata, CD_PROP_FLOAT2, vbo.name.c_str()); - foreach_bmesh([&](BMLoop * /*l*/) { - *static_cast(GPU_vertbuf_raw_step(&access)) = white; + foreach_bmesh([&](BMLoop *l) { + float *uv = static_cast BM_ELEM_CD_GET_VOID_P(l, cd_uv); + + *static_cast(GPU_vertbuf_raw_step(&access)) = uv; }); + + break; + } + case CD_PROP_COLOR: { + ushort color[4]; + const bool do_loop = vbo.domain == ATTR_DOMAIN_CORNER; + + const int cd_color = CustomData_get_offset_named(cdata, CD_PROP_COLOR, vbo.name.c_str()); + + foreach_bmesh([&](BMLoop *l) { + MPropCol *col; + + if (do_loop) { + col = static_cast(BM_ELEM_CD_GET_VOID_P(l, cd_color)); + } + else { + col = static_cast(BM_ELEM_CD_GET_VOID_P(l->v, cd_color)); + } + + color[0] = unit_float_to_ushort_clamp(col->color[0]); + color[1] = unit_float_to_ushort_clamp(col->color[1]); + color[2] = unit_float_to_ushort_clamp(col->color[2]); + color[3] = unit_float_to_ushort_clamp(col->color[3]); + + *static_cast(GPU_vertbuf_raw_step(&access)) = color; + }); + + break; + } + case CD_PROP_BYTE_COLOR: { + ushort color[4]; + const bool do_loop = vbo.domain == ATTR_DOMAIN_CORNER; + + const int cd_color = CustomData_get_offset_named( + cdata, CD_PROP_BYTE_COLOR, vbo.name.c_str()); + foreach_bmesh([&](BMLoop *l) { + MLoopCol *col; + + if (do_loop) { + col = static_cast(BM_ELEM_CD_GET_VOID_P(l, cd_color)); + } + else { + col = static_cast(BM_ELEM_CD_GET_VOID_P(l->v, cd_color)); + } + + color[0] = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[col->r]); + color[1] = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[col->g]); + color[2] = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[col->b]); + color[3] = col->a * 257; + + *static_cast(GPU_vertbuf_raw_step(&access)) = color; + }); + break; } case CD_PBVH_CO_TYPE: - foreach_bmesh( - [&](BMLoop *l) { *static_cast(GPU_vertbuf_raw_step(&access)) = l->v->co; }); + if (args->show_orig) { + int cd_sculpt_vert = CustomData_get_offset(&args->bm->vdata, CD_DYNTOPO_VERT); + + foreach_bmesh([&](BMLoop *l) { + MSculptVert *mv = static_cast( + BM_ELEM_CD_GET_VOID_P(l->v, cd_sculpt_vert)); + + *static_cast(GPU_vertbuf_raw_step(&access)) = mv->origco; + }); + } + else { + foreach_bmesh([&](BMLoop *l) { + *static_cast(GPU_vertbuf_raw_step(&access)) = l->v->co; + }); + } break; case CD_PBVH_NO_TYPE: @@ -833,11 +1023,63 @@ struct PBVHBatches { break; } case CD_PBVH_FSET_TYPE: { - uchar3 white(UCHAR_MAX, UCHAR_MAX, UCHAR_MAX); + int cd_fset = CustomData_get_offset_named( + &args->bm->pdata, CD_PROP_INT32, ".sculpt_face_set"); - foreach_bmesh([&](BMLoop * /*l*/) { - *static_cast(GPU_vertbuf_raw_step(&access)) = white; + if (cd_fset == -1) { + uchar3 white(UCHAR_MAX, UCHAR_MAX, UCHAR_MAX); + + foreach_bmesh([&](BMLoop * /*l*/) { + *static_cast(GPU_vertbuf_raw_step(&access)) = white; + }); + } + else { + foreach_bmesh([&](BMLoop *l) { + uchar face_set_color[4]; + int fset = BM_ELEM_CD_GET_INT(l->f, cd_fset); + + if (fset != args->face_sets_color_default) { + BKE_paint_face_set_overlay_color_get( + fset, args->face_sets_color_seed, face_set_color); + } + else { + face_set_color[0] = face_set_color[1] = face_set_color[2] = 255; + } + + *static_cast(GPU_vertbuf_raw_step(&access)) = face_set_color; + }); + } + + break; + } + case CD_PROP_INT32: { + const int cd_prop = CustomData_get_offset_named(cdata, CD_PROP_INT32, vbo.name.c_str()); + + foreach_bmesh([&](BMLoop *l) { + BMElem *elem; + + switch (vbo.domain) { + case ATTR_DOMAIN_POINT: + elem = reinterpret_cast(l->v); + break; + case ATTR_DOMAIN_EDGE: + elem = reinterpret_cast(l->e); + break; + case ATTR_DOMAIN_CORNER: + elem = reinterpret_cast(l); + break; + case ATTR_DOMAIN_FACE: + elem = reinterpret_cast(l->f); + break; + default: + return; + } + + int val = BM_ELEM_CD_GET_INT(elem, cd_prop); + *static_cast(GPU_vertbuf_raw_step(&access)) = val; }); + + break; } } } @@ -892,6 +1134,16 @@ struct PBVHBatches { GPU_vertformat_attr_add(&format, "f", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); need_aliases = true; break; + case CD_PROP_INT32: { + char attr_name[32], attr_safe_name[GPU_MAX_SAFE_ATTR_NAME]; + GPU_vertformat_safe_attr_name(name.c_str(), attr_safe_name, GPU_MAX_SAFE_ATTR_NAME); + /* Attributes use auto-name. */ + BLI_snprintf(attr_name, sizeof(attr_name), "a%s", attr_safe_name); + + GPU_vertformat_attr_add(&format, attr_name, GPU_COMP_I32, 1, GPU_FETCH_INT_TO_FLOAT); + need_aliases = true; + break; + } case CD_PROP_COLOR: case CD_PROP_BYTE_COLOR: { GPU_vertformat_attr_add(&format, "c", GPU_COMP_U16, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); @@ -924,6 +1176,9 @@ struct PBVHBatches { case CD_PROP_FLOAT2: prefix = "u"; break; + case CD_PROP_INT32: + prefix = "a"; + break; default: break; } @@ -1043,19 +1298,26 @@ struct PBVHBatches { int v_index = 0; lines_count = 0; - GSET_FOREACH_BEGIN (BMFace *, f, args->bm_faces) { - if (BM_elem_flag_test(f, BM_ELEM_HIDDEN)) { - continue; + for (int i : IndexRange(args->tribuf->tottri)) { + PBVHTri *tri = args->tribuf->tris + i; + + if (tri->eflag & 1) { + GPU_indexbuf_add_line_verts(&elb_lines, v_index, v_index + 1); + lines_count++; } - GPU_indexbuf_add_line_verts(&elb_lines, v_index, v_index + 1); - GPU_indexbuf_add_line_verts(&elb_lines, v_index + 1, v_index + 2); - GPU_indexbuf_add_line_verts(&elb_lines, v_index + 2, v_index); + if (tri->eflag & 2) { + GPU_indexbuf_add_line_verts(&elb_lines, v_index + 1, v_index + 2); + lines_count++; + } + + if (tri->eflag & 4) { + GPU_indexbuf_add_line_verts(&elb_lines, v_index + 2, v_index); + lines_count++; + } - lines_count += 3; v_index += 3; } - GSET_FOREACH_END(); lines_index = GPU_indexbuf_build(&elb_lines); } @@ -1126,7 +1388,7 @@ struct PBVHBatches { uint offset = 0; const uint grid_vert_len = gridsize * gridsize; for (int i = 0; i < totgrid; i++, offset += grid_vert_len) { - uint v0, v1, v2, v3; + uint v0 = 0, v1 = 0, v2 = 0, v3 = 0; bool grid_visible = false; BLI_bitmap *gh = args->grid_hidden[args->grid_indices[i]]; @@ -1137,6 +1399,7 @@ struct PBVHBatches { if (gh && paint_is_grid_face_hidden(gh, gridsize, k, j)) { continue; } + /* Indices in a Clockwise QUAD disposition. */ v0 = offset + j * gridsize + k; v1 = offset + j * gridsize + k + skip; diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_uv.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_uv.cc index 2af8fa40844..46b1738ec9e 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_uv.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_uv.cc @@ -43,6 +43,10 @@ static bool mesh_extract_uv_format_init(GPUVertFormat *format, char attr_name[32], attr_safe_name[GPU_MAX_SAFE_ATTR_NAME]; const char *layer_name = CustomData_get_layer_name(cd_ldata, CD_PROP_FLOAT2, i); + if (!layer_name) { + continue; + } + GPU_vertformat_safe_attr_name(layer_name, attr_safe_name, GPU_MAX_SAFE_ATTR_NAME); /* UV layer name. */ BLI_snprintf(attr_name, sizeof(attr_name), "a%s", attr_safe_name); diff --git a/source/blender/editors/curves/CMakeLists.txt b/source/blender/editors/curves/CMakeLists.txt index 873df89b40c..661d945a52b 100644 --- a/source/blender/editors/curves/CMakeLists.txt +++ b/source/blender/editors/curves/CMakeLists.txt @@ -14,6 +14,7 @@ set(INC ../../windowmanager ../../../../intern/clog ../../../../intern/guardedalloc + ../../../../intern/atomic ../../bmesh # RNA_prototypes.h diff --git a/source/blender/editors/datafiles/CMakeLists.txt b/source/blender/editors/datafiles/CMakeLists.txt index 0a8130ffb56..0e7ad7579e3 100644 --- a/source/blender/editors/datafiles/CMakeLists.txt +++ b/source/blender/editors/datafiles/CMakeLists.txt @@ -729,7 +729,6 @@ set_property(GLOBAL PROPERTY ICON_GEOM_NAMES brush.particle.puff brush.particle.smooth brush.particle.weight - brush.sculpt.blob brush.sculpt.boundary brush.sculpt.clay brush.sculpt.clay_strips diff --git a/source/blender/editors/geometry/CMakeLists.txt b/source/blender/editors/geometry/CMakeLists.txt index 6e28bb3e8ec..726c40817c3 100644 --- a/source/blender/editors/geometry/CMakeLists.txt +++ b/source/blender/editors/geometry/CMakeLists.txt @@ -9,6 +9,7 @@ set(INC ../../makesdna ../../makesrna ../../windowmanager + ../../../../intern/atomic ../../../../intern/guardedalloc ../../bmesh ) diff --git a/source/blender/editors/geometry/geometry_intern.hh b/source/blender/editors/geometry/geometry_intern.hh index 0ae63d07c6d..2ab6dc70c59 100644 --- a/source/blender/editors/geometry/geometry_intern.hh +++ b/source/blender/editors/geometry/geometry_intern.hh @@ -13,6 +13,7 @@ namespace blender::ed::geometry { /* *** geometry_attributes.cc *** */ void GEOMETRY_OT_attribute_add(struct wmOperatorType *ot); +void GEOMETRY_OT_color_attribute_add(struct wmOperatorType *ot); void GEOMETRY_OT_attribute_remove(struct wmOperatorType *ot); void GEOMETRY_OT_color_attribute_add(struct wmOperatorType *ot); void GEOMETRY_OT_color_attribute_remove(struct wmOperatorType *ot); diff --git a/source/blender/editors/geometry/geometry_ops.cc b/source/blender/editors/geometry/geometry_ops.cc index 79a0468f51a..cb800ce0117 100644 --- a/source/blender/editors/geometry/geometry_ops.cc +++ b/source/blender/editors/geometry/geometry_ops.cc @@ -18,6 +18,7 @@ void ED_operatortypes_geometry(void) using namespace blender::ed::geometry; WM_operatortype_append(GEOMETRY_OT_attribute_add); + WM_operatortype_append(GEOMETRY_OT_color_attribute_add); WM_operatortype_append(GEOMETRY_OT_attribute_remove); WM_operatortype_append(GEOMETRY_OT_color_attribute_add); WM_operatortype_append(GEOMETRY_OT_color_attribute_remove); diff --git a/source/blender/editors/gpencil/CMakeLists.txt b/source/blender/editors/gpencil/CMakeLists.txt index af2ad0b7536..4fd28b4ac6f 100644 --- a/source/blender/editors/gpencil/CMakeLists.txt +++ b/source/blender/editors/gpencil/CMakeLists.txt @@ -13,6 +13,7 @@ set(INC ../../makesrna ../../windowmanager ../../../../intern/guardedalloc + ../../../../intern/atomic ../../bmesh # RNA_prototypes.h ${CMAKE_BINARY_DIR}/source/blender/makesrna diff --git a/source/blender/editors/include/ED_object.h b/source/blender/editors/include/ED_object.h index cc1b615f6b1..cc36641b843 100644 --- a/source/blender/editors/include/ED_object.h +++ b/source/blender/editors/include/ED_object.h @@ -318,8 +318,10 @@ void ED_object_sculptmode_enter_ex(struct Main *bmain, struct Depsgraph *depsgraph, struct Scene *scene, struct Object *ob, - bool force_dyntopo, - struct ReportList *reports); + const bool force_dyntopo, + struct ReportList *reports, + bool do_undo); + void ED_object_sculptmode_enter(struct bContext *C, struct Depsgraph *depsgraph, struct ReportList *reports); diff --git a/source/blender/editors/interface/CMakeLists.txt b/source/blender/editors/interface/CMakeLists.txt index 5b1a17a0ac6..420b8a829de 100644 --- a/source/blender/editors/interface/CMakeLists.txt +++ b/source/blender/editors/interface/CMakeLists.txt @@ -21,6 +21,7 @@ set(INC ../../render ../../windowmanager ../../../../intern/guardedalloc + ../../../../intern/atomic ../../bmesh # RNA_prototypes.h ${CMAKE_BINARY_DIR}/source/blender/makesrna diff --git a/source/blender/editors/interface/interface_handlers.cc b/source/blender/editors/interface/interface_handlers.cc index 6b2786ed77b..0c9f925914e 100644 --- a/source/blender/editors/interface/interface_handlers.cc +++ b/source/blender/editors/interface/interface_handlers.cc @@ -6261,6 +6261,8 @@ static int ui_do_but_COLOR(bContext *C, uiBut *but, uiHandleButtonData *data, co } else { Scene *scene = CTX_data_scene(C); + ePaintMode mode = BKE_paintmode_get_active_from_context(C); + bool updated = false; if (but->rnaprop && RNA_property_subtype(but->rnaprop) == PROP_COLOR_GAMMA) { @@ -8578,7 +8580,9 @@ static void button_activate_exit( #ifdef USE_ALLSELECT { /* only RNA from this button is used */ + uiBut but_temp = *but; + uiSelectContextStore *selctx_data = &data->select_others; for (int i = 0; i < selctx_data->elems_len; i++) { uiSelectContextElem *other = &selctx_data->elems[i]; diff --git a/source/blender/editors/interface/interface_intern.hh b/source/blender/editors/interface/interface_intern.hh index 68a81de4e4a..46d10503728 100644 --- a/source/blender/editors/interface/interface_intern.hh +++ b/source/blender/editors/interface/interface_intern.hh @@ -235,6 +235,7 @@ struct uiBut { uchar unit_type = 0; short iconadd = 0; + char poison1[512]; /** #UI_BTYPE_BLOCK data */ uiBlockCreateFunc block_create_func = nullptr; @@ -249,6 +250,7 @@ struct uiBut { int rnaindex = 0; /* Operator data */ + char poison2[512]; wmOperatorType *optype = nullptr; PointerRNA *opptr = nullptr; wmOperatorCallContext opcontext = WM_OP_INVOKE_DEFAULT; @@ -258,6 +260,8 @@ struct uiBut { ListBase extra_op_icons = {nullptr, nullptr}; /** #uiButExtraOpIcon */ + char poison3[512]; + /* Drag-able data, type is WM_DRAG_... */ char dragtype = WM_DRAG_ID; short dragflag = 0; @@ -265,6 +269,8 @@ struct uiBut { ImBuf *imb = nullptr; float imb_scale = 0; + char poison4[512]; + /** Active button data (set when the user is hovering or interacting with a button). */ uiHandleButtonData *active = nullptr; @@ -275,6 +281,8 @@ struct uiBut { double *editval = nullptr; float *editvec = nullptr; + char poison5[512]; + uiButPushedStateFunc pushed_state_func = nullptr; const void *pushed_state_arg = nullptr; @@ -1458,6 +1466,9 @@ bool ui_jump_to_target_button_poll(bContext *C); void ui_interface_tag_script_reload_queries(); +void poison_ui_but(struct uiBut *but); +void unpoison_ui_but(struct uiBut *but); + /* interface_view.cc */ void ui_block_free_views(uiBlock *block); diff --git a/source/blender/editors/mesh/CMakeLists.txt b/source/blender/editors/mesh/CMakeLists.txt index a0769fbaa5f..3b2ee8ace74 100644 --- a/source/blender/editors/mesh/CMakeLists.txt +++ b/source/blender/editors/mesh/CMakeLists.txt @@ -19,6 +19,7 @@ set(INC ../../windowmanager ../../../../intern/clog ../../../../intern/guardedalloc + ../../../../intern/atomic # RNA_prototypes.h ${CMAKE_BINARY_DIR}/source/blender/makesrna ) @@ -34,6 +35,7 @@ set(SRC editmesh_extrude_screw.c editmesh_extrude_spin.c editmesh_extrude_spin_gizmo.c + editmesh_fair.c editmesh_inset.c editmesh_intersect.c editmesh_knife.c diff --git a/source/blender/editors/mesh/editmesh_fair.c b/source/blender/editors/mesh/editmesh_fair.c new file mode 100644 index 00000000000..267ed14b541 --- /dev/null +++ b/source/blender/editors/mesh/editmesh_fair.c @@ -0,0 +1,169 @@ +/* + * 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) 2020 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup edmesh + * + * Interactive editmesh knife tool. + */ + +#ifdef _MSC_VER +# define _USE_MATH_DEFINES +#endif + +#include "MEM_guardedalloc.h" + +#include "DNA_object_types.h" + +#include "BLI_alloca.h" +#include "BLI_array.h" +#include "BLI_linklist.h" +#include "BLI_listbase.h" +#include "BLI_math.h" +#include "BLI_memarena.h" +#include "BLI_smallhash.h" +#include "BLI_string.h" + +#include "BLT_translation.h" + +#include "BKE_bvhutils.h" +#include "BKE_context.h" +#include "BKE_editmesh.h" +#include "BKE_editmesh_bvh.h" +#include "BKE_layer.h" +#include "BKE_mesh_fair.h" +#include "BKE_report.h" + +#include "ED_mesh.h" +#include "ED_screen.h" +#include "ED_space_api.h" +#include "ED_view3d.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "DNA_object_types.h" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "RNA_access.h" +#include "RNA_define.h" + +#include "DEG_depsgraph.h" +#include "DEG_depsgraph_query.h" + +#include "mesh_intern.h" /* own include */ + +static EnumPropertyItem prop_edit_mesh_fair_selection_mode_items[] = { + { + MESH_FAIRING_DEPTH_POSITION, + "POSITION", + 0, + "Position", + "Fair positions", + }, + { + MESH_FAIRING_DEPTH_TANGENCY, + "TANGENCY", + 0, + "Tangency", + "Fair tangency", + }, + /* + { + MESH_FAIRING_DEPTH_CURVATURE, + "CURVATURE", + 0, + "Curvature", + "Fair curvature", + }, + */ + {0, NULL, 0, NULL, NULL}, +}; + +static int edbm_fair_vertices_exec(bContext *C, wmOperator *op) +{ + const int mode = RNA_enum_get(op->ptr, "mode"); + ViewLayer *view_layer = CTX_data_view_layer(C); + uint objects_len = 0; + Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data( + CTX_data_scene(C), view_layer, CTX_wm_view3d(C), &objects_len); + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *obedit = objects[ob_index]; + BMEditMesh *em = BKE_editmesh_from_object(obedit); + if ((em->bm->totvertsel == 0)) { + continue; + } + + BMesh *bm = em->bm; + BMVert *v; + BMIter iter; + int i; + bool *fairing_mask = MEM_calloc_arrayN(bm->totvert, sizeof(bool), "fairing mask"); + BM_ITER_MESH_INDEX (v, &iter, bm, BM_VERTS_OF_MESH, i) { + if (!BM_elem_flag_test(v, BM_ELEM_SELECT)) { + continue; + } + if (BM_vert_is_boundary(v)) { + continue; + } + if (!BM_vert_is_manifold(v)) { + continue; + } + fairing_mask[i] = true; + } + BKE_bmesh_prefair_and_fair_verts(bm, fairing_mask, mode); + MEM_freeN(fairing_mask); + + EDBM_mesh_normals_update(em); + EDBM_update(obedit->data, + &(const struct EDBMUpdate_Params){ + .calc_looptri = false, + .calc_normals = true, + .is_destructive = true, + }); + } + + MEM_freeN(objects); + return OPERATOR_FINISHED; +} + +void MESH_OT_fair_vertices(wmOperatorType *ot) +{ + /* description */ + ot->name = "Fair Vertices"; + ot->idname = "MESH_OT_fair_vertices"; + ot->description = "Create a smooth as possible geometry patch"; + + /* callbacks */ + ot->exec = edbm_fair_vertices_exec; + ot->poll = ED_operator_editmesh; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + RNA_def_enum(ot->srna, + "mode", + prop_edit_mesh_fair_selection_mode_items, + MESH_FAIRING_DEPTH_POSITION, + "Mode", + ""); +} diff --git a/source/blender/editors/mesh/editmesh_mask_extract.cc b/source/blender/editors/mesh/editmesh_mask_extract.cc index cea882b0db7..5c236f8bafe 100644 --- a/source/blender/editors/mesh/editmesh_mask_extract.cc +++ b/source/blender/editors/mesh/editmesh_mask_extract.cc @@ -46,6 +46,8 @@ #include "mesh_intern.h" /* own include */ +#include "../sculpt_paint/sculpt_intern.hh" + static bool geometry_extract_poll(bContext *C) { Object *ob = CTX_data_active_object(C); @@ -106,7 +108,7 @@ static int geometry_extract_apply(bContext *C, BMeshFromMeshParams mesh_to_bm_params{}; mesh_to_bm_params.calc_face_normal = true; mesh_to_bm_params.calc_vert_normal = true; - BM_mesh_bm_from_me(bm, new_mesh, &mesh_to_bm_params); + BM_mesh_bm_from_me(nullptr, bm, new_mesh, &mesh_to_bm_params); BMEditMesh *em = BKE_editmesh_create(bm); @@ -363,6 +365,7 @@ static int face_set_extract_invoke(bContext *C, wmOperator *op, const wmEvent * ED_workspace_status_text(C, TIP_("Click on the mesh to select a Face Set")); WM_cursor_modal_set(CTX_wm_window(C), WM_CURSOR_EYEDROPPER); WM_event_add_modal_handler(C, op); + return OPERATOR_RUNNING_MODAL; } @@ -496,14 +499,23 @@ static int paint_mask_slice_exec(bContext *C, wmOperator *op) ED_sculpt_undo_geometry_begin(ob, op); } + BMesh *bm; + const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(new_mesh); BMeshCreateParams bm_create_params{}; bm_create_params.use_toolflags = true; - BMesh *bm = BM_mesh_create(&allocsize, &bm_create_params); - BMeshFromMeshParams mesh_to_bm_params{}; mesh_to_bm_params.calc_face_normal = true; - BM_mesh_bm_from_me(bm, new_mesh, &mesh_to_bm_params); + + if (ob->sculpt && ob->sculpt->bm) { + bm = ob->sculpt->bm; + BM_mesh_elem_toolflags_ensure(bm); + } + else { + bm = BM_mesh_create(&allocsize, &bm_create_params); + + BM_mesh_bm_from_me(nullptr, bm, new_mesh, &mesh_to_bm_params); + } slice_paint_mask( bm, false, RNA_boolean_get(op->ptr, "fill_holes"), RNA_float_get(op->ptr, "mask_threshold")); @@ -511,7 +523,13 @@ static int paint_mask_slice_exec(bContext *C, wmOperator *op) BMeshToMeshParams bm_to_mesh_params{}; bm_to_mesh_params.calc_object_remap = false; new_mesh = BKE_mesh_from_bmesh_nomain(bm, &bm_to_mesh_params, mesh); - BM_mesh_free(bm); + + if (!ob->sculpt || !ob->sculpt->bm) { + BM_mesh_free(bm); + } + else { + BM_mesh_elem_toolflags_clear(bm); + } if (RNA_boolean_get(op->ptr, "new_object")) { ushort local_view_bits = 0; @@ -525,7 +543,7 @@ static int paint_mask_slice_exec(bContext *C, wmOperator *op) const BMAllocTemplate allocsize_new_ob = BMALLOC_TEMPLATE_FROM_ME(new_ob_mesh); bm = BM_mesh_create(&allocsize_new_ob, &bm_create_params); - BM_mesh_bm_from_me(bm, new_ob_mesh, &mesh_to_bm_params); + BM_mesh_bm_from_me(nullptr, bm, new_ob_mesh, &mesh_to_bm_params); slice_paint_mask(bm, true, @@ -553,16 +571,66 @@ static int paint_mask_slice_exec(bContext *C, wmOperator *op) if (ob->mode == OB_MODE_SCULPT) { SculptSession *ss = ob->sculpt; - ss->face_sets = static_cast(CustomData_get_layer_named_for_write( - &mesh->pdata, CD_PROP_INT32, ".sculpt_face_set", mesh->totpoly)); - if (ss->face_sets) { - /* Assign a new Face Set ID to the new faces created by the slice operation. */ - const int next_face_set_id = ED_sculpt_face_sets_find_next_available_id(mesh); - ED_sculpt_face_sets_initialize_none_to_id(mesh, next_face_set_id); + BKE_sculptsession_update_attr_refs(ob); + + /* Assign a new Face Set ID to the new faces created by the slice operation. */ + switch (BKE_pbvh_type(ss->pbvh)) { + case PBVH_GRIDS: + case PBVH_FACES: + ss->face_sets = (int *)CustomData_get_layer_named( + &mesh->pdata, CD_PROP_INT32, ".sculpt_face_set"); + + if (ss->face_sets) { + const int next_face_set_id = ED_sculpt_face_sets_find_next_available_id(mesh); + ED_sculpt_face_sets_initialize_none_to_id(mesh, next_face_set_id); + } + break; + case PBVH_BMESH: { + const int cd_fset = CustomData_get_named_offset( + &ss->bm->pdata, CD_PROP_INT32, ".sculpt_face_set"); + const int cd_boundary_flag = CustomData_get_offset_named( + &ss->bm->vdata, CD_PROP_INT32, SCULPT_ATTRIBUTE_NAME(boundary_flags)); + + if (ss->bm && cd_fset != -1) { + const int cd_sculptvert = CustomData_get_offset(&ss->bm->vdata, CD_DYNTOPO_VERT); + + BMFace *f; + BMVert *v; + BMIter iter; + + const int next_face_set_id = SCULPT_face_set_next_available_get(ss); + + const int updateflag = SCULPTVERT_NEED_VALENCE | SCULPTVERT_NEED_TRIANGULATE | + SCULPTVERT_NEED_DISK_SORT; + + BM_ITER_MESH (v, &iter, ss->bm, BM_VERTS_OF_MESH) { + MSculptVert *mv = (MSculptVert *)BM_ELEM_CD_GET_VOID_P(v, cd_sculptvert); + + if (cd_boundary_flag != -1) { + int *flag = (int *)BM_ELEM_CD_GET_VOID_P(v, cd_boundary_flag); + *flag |= SCULPT_BOUNDARY_NEEDS_UPDATE; + } + + mv->flag |= updateflag; + } + + BM_ITER_MESH (f, &iter, ss->bm, BM_FACES_OF_MESH) { + int fset = BM_ELEM_CD_GET_INT(f, cd_fset); + + if (fset == SCULPT_FACE_SET_NONE) { + BM_ELEM_CD_SET_INT(f, cd_fset, next_face_set_id); + } + } + } + break; + } } - ED_sculpt_undo_geometry_end(ob); + + ss->needs_pbvh_rebuild = true; } + ED_sculpt_undo_geometry_end(ob); + BKE_mesh_batch_cache_dirty_tag(mesh, BKE_MESH_BATCH_DIRTY_ALL); DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); WM_event_add_notifier(C, NC_GEOM | ND_DATA, mesh); diff --git a/source/blender/editors/mesh/editmesh_polybuild.c b/source/blender/editors/mesh/editmesh_polybuild.c index cfd07a6ff35..344df8b39e5 100644 --- a/source/blender/editors/mesh/editmesh_polybuild.c +++ b/source/blender/editors/mesh/editmesh_polybuild.c @@ -213,7 +213,7 @@ static int edbm_polybuild_delete_at_cursor_invoke(bContext *C, if (ele_act->head.htype == BM_VERT) { BMVert *v_act = (BMVert *)ele_act; if (BM_vert_is_edge_pair(v_act) && !BM_vert_is_wire(v_act)) { - BM_edge_collapse(bm, v_act->e, v_act, true, true); + BM_edge_collapse(bm, v_act->e, v_act, true, true, false, false, NULL); changed = true; } else { @@ -565,7 +565,7 @@ static int edbm_polybuild_dissolve_at_cursor_invoke(bContext *C, else if (ele_act->head.htype == BM_VERT) { BMVert *v_act = (BMVert *)ele_act; if (BM_vert_is_edge_pair(v_act)) { - BM_edge_collapse(bm, v_act->e, v_act, true, true); + BM_edge_collapse(bm, v_act->e, v_act, true, true, false, false, NULL); } else { /* too involved to do inline */ diff --git a/source/blender/editors/mesh/editmesh_tools.c b/source/blender/editors/mesh/editmesh_tools.c index caa94a3f49d..90776ce004d 100644 --- a/source/blender/editors/mesh/editmesh_tools.c +++ b/source/blender/editors/mesh/editmesh_tools.c @@ -4434,8 +4434,8 @@ static Base *mesh_separate_tagged( &((struct BMeshCreateParams){ .use_toolflags = true, })); - BM_mesh_elem_toolflags_ensure(bm_new); /* Needed for 'duplicate' BMO. */ + BM_mesh_elem_toolflags_ensure(bm_new); /* Needed for 'duplicate' BMO. */ BM_mesh_copy_init_customdata(bm_new, bm_old, &bm_mesh_allocsize_default); /* Take into account user preferences for duplicating actions. */ @@ -4472,7 +4472,8 @@ static Base *mesh_separate_tagged( BM_mesh_normals_update(bm_new); - BM_mesh_bm_to_me(bmain, bm_new, base_new->object->data, (&(struct BMeshToMeshParams){0})); + BM_mesh_bm_to_me( + bmain, base_new->object, bm_new, base_new->object->data, (&(struct BMeshToMeshParams){0})); BM_mesh_free(bm_new); ((Mesh *)base_new->object->data)->edit_mesh = NULL; @@ -4538,7 +4539,8 @@ static Base *mesh_separate_arrays(Main *bmain, BM_vert_kill(bm_old, verts[i]); } - BM_mesh_bm_to_me(bmain, bm_new, base_new->object->data, (&(struct BMeshToMeshParams){0})); + BM_mesh_bm_to_me( + bmain, base_new->object, bm_new, base_new->object->data, (&(struct BMeshToMeshParams){0})); BM_mesh_free(bm_new); ((Mesh *)base_new->object->data)->edit_mesh = NULL; @@ -4721,6 +4723,7 @@ static bool mesh_separate_loose( if (clear_object_data) { BM_mesh_bm_to_me(NULL, + base_old->object, bm_old, me_old, (&(struct BMeshToMeshParams){ @@ -4814,7 +4817,7 @@ static int edbm_separate_exec(bContext *C, wmOperator *op) .use_toolflags = true, })); - BM_mesh_bm_from_me(bm_old, me, (&(struct BMeshFromMeshParams){0})); + BM_mesh_bm_from_me(NULL, bm_old, me, (&(struct BMeshFromMeshParams){0})); switch (type) { case MESH_SEPARATE_MATERIAL: @@ -4830,6 +4833,7 @@ static int edbm_separate_exec(bContext *C, wmOperator *op) if (changed) { BM_mesh_bm_to_me(bmain, + ob, bm_old, me, (&(struct BMeshToMeshParams){ @@ -6027,6 +6031,116 @@ static void edbm_dissolve_prop__use_boundary_tear(wmOperatorType *ot) "Split off face corners instead of merging faces"); } +static int edbm_mres_test_exec(bContext *C, wmOperator *op) +{ + const bool use_face_split = RNA_boolean_get(op->ptr, "use_face_split"); + const bool use_boundary_tear = RNA_boolean_get(op->ptr, "use_boundary_tear"); + + ViewLayer *view_layer = CTX_data_view_layer(C); + uint objects_len = 0; + Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data( + CTX_data_scene(C), view_layer, CTX_wm_view3d(C), &objects_len); + + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *obedit = objects[ob_index]; + BMEditMesh *em = BKE_editmesh_from_object(obedit); + + if (em->bm->totvertsel == 0) { + continue; + } + + BM_custom_loop_normals_to_vector_layer(em->bm); + + if (!EDBM_op_callf(em, op, "test_mres_smooth")) { + continue; + } + + BM_custom_loop_normals_from_vector_layer(em->bm, false); + + EDBM_update(obedit->data, + &(const struct EDBMUpdate_Params){ + .calc_looptri = true, + .calc_normals = false, + .is_destructive = true, + }); + // EDBM_update_generic(obedit->data, true, true); + } + + MEM_freeN(objects); + return OPERATOR_FINISHED; +} + +extern Object *multires_dump_grids_bmesh(Object *bmob, BMesh *bm); + +static int edbm_dump_mres_grids_exec(bContext *C, wmOperator *op) +{ + const bool use_face_split = RNA_boolean_get(op->ptr, "use_face_split"); + const bool use_boundary_tear = RNA_boolean_get(op->ptr, "use_boundary_tear"); + + ViewLayer *view_layer = CTX_data_view_layer(C); + uint objects_len = 0; + Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data( + CTX_data_scene(C), view_layer, CTX_wm_view3d(C), &objects_len); + + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *obedit = objects[ob_index]; + BMEditMesh *em = BKE_editmesh_from_object(obedit); + + multires_dump_grids_bmesh(obedit, em->bm); + } + + WM_event_add_notifier(C, NC_SCENE | ND_LAYER_CONTENT, CTX_data_scene(C)); + + MEM_freeN(objects); + return OPERATOR_FINISHED; +} + +static bool mres_test_poll(bContext *C) +{ + Object *obedit = CTX_data_edit_object(C); + if (obedit && obedit->type == OB_MESH) { + BMEditMesh *em = BKE_editmesh_from_object(obedit); + + if (!em || !CustomData_has_layer(&em->bm->ldata, CD_MDISPS)) { + return false; + } + + return true; + } + + return false; +} + +void MESH_OT_dump_mres_grids(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Dump Multires Grids"; + ot->description = "Dump Multires Grids"; + ot->idname = "MESH_OT_dump_mres_grids"; + + /* api callbacks */ + ot->exec = edbm_dump_mres_grids_exec; + ot->poll = mres_test_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +void MESH_OT_mres_test(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Test Multires Boundary Smooth"; + ot->description = "Test multires boundary smooth"; + ot->idname = "MESH_OT_mres_test"; + + /* api callbacks */ + ot->exec = edbm_mres_test_exec; + ot->poll = mres_test_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + static int edbm_dissolve_verts_exec(bContext *C, wmOperator *op) { const bool use_face_split = RNA_boolean_get(op->ptr, "use_face_split"); @@ -7110,7 +7224,7 @@ static void sort_bmelem_flag(bContext *C, } } - BM_mesh_remap(em->bm, map[0], map[1], map[2]); + BM_mesh_remap(em->bm, map[0], map[1], map[2], NULL); EDBM_update(ob->data, &(const struct EDBMUpdate_Params){ diff --git a/source/blender/editors/mesh/editmesh_undo.cc b/source/blender/editors/mesh/editmesh_undo.cc index 565ce28a3c1..17993273b89 100644 --- a/source/blender/editors/mesh/editmesh_undo.cc +++ b/source/blender/editors/mesh/editmesh_undo.cc @@ -610,7 +610,7 @@ static void *undomesh_from_editmesh(UndoMesh *um, BMEditMesh *em, Key *key, Undo params.update_shapekey_indices = false; params.cd_mask_extra = cd_mask_extra; params.active_shapekey_to_mvert = true; - BM_mesh_bm_to_me(nullptr, em->bm, &um->me, ¶ms); + BM_mesh_bm_to_me(nullptr, nullptr, em->bm, &um->me, ¶ms); um->selectmode = em->selectmode; um->shapenr = em->bm->shapenr; @@ -678,7 +678,7 @@ static void undomesh_to_editmesh(UndoMesh *um, Object *ob, BMEditMesh *em) convert_params.calc_face_normal = false; convert_params.calc_vert_normal = false; convert_params.active_shapekey = um->shapenr; - BM_mesh_bm_from_me(bm, &um->me, &convert_params); + BM_mesh_bm_from_me(nullptr, bm, &um->me, &convert_params); em_tmp = BKE_editmesh_create(bm); *em = *em_tmp; diff --git a/source/blender/editors/mesh/editmesh_utils.c b/source/blender/editors/mesh/editmesh_utils.c index cde57c7226b..deee7e29c08 100644 --- a/source/blender/editors/mesh/editmesh_utils.c +++ b/source/blender/editors/mesh/editmesh_utils.c @@ -28,6 +28,7 @@ #include "BKE_main.h" #include "BKE_mesh.h" #include "BKE_mesh_mapping.h" +#include "BKE_pbvh.h" #include "BKE_report.h" #include "DEG_depsgraph.h" @@ -300,7 +301,12 @@ void EDBM_mesh_load_ex(Main *bmain, Object *ob, bool free_data) bm->shapenr = 1; } +#ifdef WITH_PBVH_CACHE + BKE_pbvh_invalidate_cache(ob); +#endif + BM_mesh_bm_to_me(bmain, + ob, bm, me, (&(struct BMeshToMeshParams){ diff --git a/source/blender/editors/mesh/mesh_data.cc b/source/blender/editors/mesh/mesh_data.cc index 263f7164fe6..54cc6518647 100644 --- a/source/blender/editors/mesh/mesh_data.cc +++ b/source/blender/editors/mesh/mesh_data.cc @@ -25,6 +25,8 @@ #include "BKE_editmesh.h" #include "BKE_mesh.h" #include "BKE_mesh_runtime.h" +#include "BKE_object.h" +#include "BKE_paint.h" #include "BKE_report.h" #include "DEG_depsgraph.h" @@ -712,6 +714,66 @@ static int mesh_customdata_add_exec__internal(bContext *C, char htype, int type) return CustomData_has_layer(data, type) ? OPERATOR_FINISHED : OPERATOR_CANCELLED; } +/* Clear Mask */ +static bool mesh_customdata_ids_clear_poll(bContext *C) +{ + Object *ob = ED_object_context(C); + if (ob && ob->type == OB_MESH) { + Mesh *me = (Mesh *)ob->data; + + if (me->edit_mesh) { + return false; + } + + /* special case - can't run this if we're in sculpt mode */ + if (ob->mode & OB_MODE_SCULPT) { + return false; + } + + if (!ID_IS_LINKED(me)) { + bool ret = CustomData_has_layer(GET_CD_DATA(me, vdata), CD_MESH_ID); + ret |= CustomData_has_layer(GET_CD_DATA(me, edata), CD_MESH_ID); + ret |= CustomData_has_layer(GET_CD_DATA(me, ldata), CD_MESH_ID); + ret |= CustomData_has_layer(GET_CD_DATA(me, pdata), CD_MESH_ID); + + return ret; + } + } + return false; +} +static int mesh_customdata_ids_clear_exec(bContext *C, wmOperator * /* op */) +{ + bool ret = false; + + for (int i = 0; i < 4; i++) { + int type = 1 << i; + + ret |= mesh_customdata_clear_exec__internal(C, type, CD_MESH_ID); + } + + if (ret) { + return OPERATOR_FINISHED; + } + + return OPERATOR_CANCELLED; +} + +void MESH_OT_customdata_ids_clear(wmOperatorType *ot) +{ + + /* identifiers */ + ot->name = "Clear Element ID Data"; + ot->idname = "MESH_OT_customdata_ids_clear"; + ot->description = "Clear element ID layers"; + + /* api callbacks */ + ot->exec = mesh_customdata_ids_clear_exec; + ot->poll = mesh_customdata_ids_clear_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + /* Clear Mask */ static bool mesh_customdata_mask_clear_poll(bContext *C) { diff --git a/source/blender/editors/mesh/mesh_intern.h b/source/blender/editors/mesh/mesh_intern.h index 856918fa365..85c3dbc1d22 100644 --- a/source/blender/editors/mesh/mesh_intern.h +++ b/source/blender/editors/mesh/mesh_intern.h @@ -294,6 +294,7 @@ void MESH_OT_set_normals_from_faces(struct wmOperatorType *ot); void MESH_OT_average_normals(struct wmOperatorType *ot); void MESH_OT_smooth_normals(struct wmOperatorType *ot); void MESH_OT_mod_weighted_strength(struct wmOperatorType *ot); +void MESH_OT_mres_test(struct wmOperatorType *ot); void MESH_OT_flip_quad_tessellation(struct wmOperatorType *ot); /* *** editmesh_mask_extract.cc *** */ @@ -302,6 +303,9 @@ void MESH_OT_paint_mask_extract(struct wmOperatorType *ot); void MESH_OT_face_set_extract(struct wmOperatorType *ot); void MESH_OT_paint_mask_slice(struct wmOperatorType *ot); +/* *** editmesh_fair.c *** */ +void MESH_OT_fair_vertices(struct wmOperatorType *ot); + /** Called in transform_ops.c, on each regeneration of key-maps. */ struct wmKeyMap *point_normals_modal_keymap(wmKeyConfig *keyconf); @@ -319,6 +323,8 @@ void MESH_OT_customdata_skin_add(struct wmOperatorType *ot); void MESH_OT_customdata_skin_clear(struct wmOperatorType *ot); void MESH_OT_customdata_custom_splitnormals_add(struct wmOperatorType *ot); void MESH_OT_customdata_custom_splitnormals_clear(struct wmOperatorType *ot); +void MESH_OT_dump_mres_grids(struct wmOperatorType *ot); +void MESH_OT_customdata_ids_clear(struct wmOperatorType *ot); void MESH_OT_customdata_bevel_weight_vertex_add(struct wmOperatorType *ot); void MESH_OT_customdata_bevel_weight_vertex_clear(struct wmOperatorType *ot); void MESH_OT_customdata_bevel_weight_edge_add(struct wmOperatorType *ot); diff --git a/source/blender/editors/mesh/mesh_ops.c b/source/blender/editors/mesh/mesh_ops.c index b9afeae275b..d3df744b22f 100644 --- a/source/blender/editors/mesh/mesh_ops.c +++ b/source/blender/editors/mesh/mesh_ops.c @@ -135,6 +135,7 @@ void ED_operatortypes_mesh(void) WM_operatortype_append(MESH_OT_uv_texture_add); WM_operatortype_append(MESH_OT_uv_texture_remove); WM_operatortype_append(MESH_OT_customdata_mask_clear); + WM_operatortype_append(MESH_OT_customdata_ids_clear); WM_operatortype_append(MESH_OT_customdata_skin_add); WM_operatortype_append(MESH_OT_customdata_skin_clear); WM_operatortype_append(MESH_OT_customdata_custom_splitnormals_add); @@ -163,6 +164,8 @@ void ED_operatortypes_mesh(void) WM_operatortype_append(MESH_OT_bevel); + WM_operatortype_append(MESH_OT_fair_vertices); + WM_operatortype_append(MESH_OT_bridge_edge_loops); WM_operatortype_append(MESH_OT_inset); WM_operatortype_append(MESH_OT_offset_edge_loops); @@ -193,6 +196,8 @@ void ED_operatortypes_mesh(void) WM_operatortype_append(MESH_OT_average_normals); WM_operatortype_append(MESH_OT_smooth_normals); WM_operatortype_append(MESH_OT_mod_weighted_strength); + WM_operatortype_append(MESH_OT_mres_test); + WM_operatortype_append(MESH_OT_dump_mres_grids); WM_operatortype_append(MESH_OT_flip_quad_tessellation); } diff --git a/source/blender/editors/mesh/meshtools.cc b/source/blender/editors/mesh/meshtools.cc index b2cbb8c47ea..243845718bb 100644 --- a/source/blender/editors/mesh/meshtools.cc +++ b/source/blender/editors/mesh/meshtools.cc @@ -66,6 +66,134 @@ using blender::Span; /* join selected meshes into the active mesh, context sensitive * return 0 if no join is made (error) and 1 if the join is done */ +static void get_id_range(Mesh *mesh, + CustomData *vdata, + CustomData *edata, + CustomData *ldata, + CustomData *pdata, + int totvert, + int totedge, + int totloop, + int totpoly, + int *r_min, + int *r_max) +{ + const CustomData *datas[4] = {vdata, edata, ldata, pdata}; + int tots[4] = {totvert, totedge, totloop, totpoly}; + int min_id = 0, max_id = 0; + + for (int i = 0; i < 4; i++) { + int *ids = (int *)CustomData_get_layer(datas[i], CD_MESH_ID); + if (!ids) { + continue; + } + + if (!tots[i]) { + continue; + } + + if (i == 0) { + min_id = max_id = *ids; + } + else { + min_id = MIN2(min_id, *ids); + max_id = MAX2(max_id, *ids); + } + + ids++; + + for (int j = 1; j < tots[i]; j++, ids++) { + min_id = MIN2(min_id, *ids); + max_id = MAX2(max_id, *ids); + } + } + + *r_min = min_id; + *r_max = max_id; +} + +static void handle_missing_id_layers(Mesh *src, + Mesh *dst, + CustomData *vdata, + CustomData *edata, + CustomData *ldata, + CustomData *pdata, + int totvert, + int totedge, + int totloop, + int totpoly) +{ + const CustomData *src_datas[4] = {&src->vdata, &src->edata, &src->ldata, &src->pdata}; + const CustomData *dst_datas[4] = {vdata, edata, ldata, pdata}; + int srctots[4] = {src->totvert, src->totedge, src->totloop, src->totpoly}; + int dsttots[4] = {totvert, totedge, totloop, totpoly}; + + // find starting max id in dst + int dst_range[2], src_range[2]; + + get_id_range(src, + &src->vdata, + &src->edata, + &src->ldata, + &src->pdata, + src->totvert, + src->totedge, + src->totloop, + src->totpoly, + src_range, + src_range + 1); + + get_id_range(dst, + vdata, + edata, + ldata, + pdata, + totvert, + totedge, + totloop, + totpoly, + dst_range, + dst_range + 1); + + for (int i = 0; i < 4; i++) { + const CustomData *srcdata = src_datas[i]; + const CustomData *dstdata = dst_datas[i]; + + const bool haveid_src = CustomData_has_layer(srcdata, CD_MESH_ID); + const bool haveid_dst = CustomData_has_layer(dstdata, CD_MESH_ID); + + if (haveid_dst && haveid_src) { + // assign ids + int offset = dst_range[1] - src_range[0] + 1; + + int *srcids = (int *)CustomData_get_layer(srcdata, CD_MESH_ID); + int *dstids = (int *)CustomData_get_layer(dstdata, CD_MESH_ID); + + int start = dsttots[i]; + int end = start + srctots[i]; + + // offset ids + dstids += start; + for (int i = start; i < end; i++, dstids++, srcids++) { + *dstids = (*srcids) + offset; + } + } + else if (haveid_dst) { + int curid = dst_range[1] + 1; + int *dstids = (int *)CustomData_get_layer(dstdata, CD_MESH_ID); + + int start = dsttots[i]; + int end = start + srctots[i]; + dstids += start; + + // assign new ids + for (int i = start; i < end; i++, dstids++) { + *dstids = curid++; + } + } + } +} + static void join_mesh_single(Depsgraph *depsgraph, Main *bmain, Scene *scene, @@ -288,6 +416,17 @@ static void join_mesh_single(Depsgraph *depsgraph, } } + handle_missing_id_layers(me, + (Mesh *)ob_dst->data, + vdata, + edata, + ldata, + pdata, + *vertofs, + *edgeofs, + *loopofs, + *polyofs); + /* these are used for relinking (cannot be set earlier, or else reattaching goes wrong) */ *vertofs += me->totvert; *vert_positions_pp += me->totvert; diff --git a/source/blender/editors/object/CMakeLists.txt b/source/blender/editors/object/CMakeLists.txt index 3bfa4167d9b..ce11926acb9 100644 --- a/source/blender/editors/object/CMakeLists.txt +++ b/source/blender/editors/object/CMakeLists.txt @@ -22,6 +22,7 @@ set(INC ../../windowmanager ../../../../intern/clog ../../../../intern/guardedalloc + ../../../../intern/atomic # dna_type_offsets.h in BLO_read_write.h ${CMAKE_BINARY_DIR}/source/blender/makesdna/intern @@ -75,6 +76,11 @@ if(WITH_PYTHON) add_definitions(-DWITH_PYTHON) endif() + +if(WITH_INSTANT_MESHES) + add_definitions(-DWITH_INSTANT_MESHES) +endif() + if(WITH_EXPERIMENTAL_FEATURES) add_definitions(-DWITH_SIMULATION_DATABLOCK) add_definitions(-DWITH_POINT_CLOUD) diff --git a/source/blender/editors/object/object_edit.c b/source/blender/editors/object/object_edit.c index d1473f8dd7a..e7ed2f5b6c7 100644 --- a/source/blender/editors/object/object_edit.c +++ b/source/blender/editors/object/object_edit.c @@ -61,6 +61,7 @@ #include "BKE_object.h" #include "BKE_paint.h" #include "BKE_particle.h" +#include "BKE_pbvh.h" #include "BKE_pointcache.h" #include "BKE_report.h" #include "BKE_scene.h" @@ -800,6 +801,12 @@ bool ED_object_editmode_enter_ex(Main *bmain, Scene *scene, Object *ob, int flag return false; } +#ifdef WITH_PBVH_CACHE + if (ob->type == OB_MESH) { + BKE_pbvh_invalidate_cache(ob); + } +#endif + ob->restore_mode = ob->mode; ob->mode = OB_MODE_EDIT; diff --git a/source/blender/editors/object/object_intern.h b/source/blender/editors/object/object_intern.h index c58ce0d8424..fddcb59cc32 100644 --- a/source/blender/editors/object/object_intern.h +++ b/source/blender/editors/object/object_intern.h @@ -348,6 +348,7 @@ void TRANSFORM_OT_vertex_random(struct wmOperatorType *ot); void OBJECT_OT_voxel_remesh(struct wmOperatorType *ot); void OBJECT_OT_voxel_size_edit(struct wmOperatorType *ot); void OBJECT_OT_quadriflow_remesh(struct wmOperatorType *ot); +void OBJECT_OT_instant_meshes_remesh(struct wmOperatorType *ot); /* object_transfer_data.c */ diff --git a/source/blender/editors/object/object_modes.cc b/source/blender/editors/object/object_modes.cc index 95b487c199d..5fa58fd02c2 100644 --- a/source/blender/editors/object/object_modes.cc +++ b/source/blender/editors/object/object_modes.cc @@ -7,11 +7,17 @@ * actual mode switching logic is per-object type. */ +#include "MEM_guardedalloc.h" + #include "DNA_gpencil_types.h" +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" #include "DNA_workspace_types.h" +#include "PIL_time.h" + #include "BLI_kdopbvh.h" #include "BLI_math.h" #include "BLI_utildefines.h" @@ -24,6 +30,8 @@ #include "BKE_gpencil_modifier.h" #include "BKE_layer.h" #include "BKE_main.h" +#include "BKE_mesh_runtime.h" +#include "BKE_mesh_types.h" #include "BKE_modifier.h" #include "BKE_object.h" #include "BKE_paint.h" @@ -36,6 +44,7 @@ #include "RNA_access.h" #include "RNA_define.h" +#include "RNA_enum_types.h" #include "DEG_depsgraph.h" #include "DEG_depsgraph_query.h" @@ -43,10 +52,13 @@ #include "ED_armature.h" #include "ED_gpencil.h" #include "ED_screen.h" +#include "ED_space_api.h" #include "ED_transform_snap_object_context.h" #include "ED_undo.h" #include "ED_view3d.h" +#include "UI_resources.h" + #include "WM_toolsystem.h" #include "ED_object.h" /* own include */ @@ -530,4 +542,4 @@ void OBJECT_OT_transfer_mode(wmOperatorType *ot) "Flash the target object when transferring the mode"); } -/** \} */ +/** \} */ \ No newline at end of file diff --git a/source/blender/editors/object/object_modifier.cc b/source/blender/editors/object/object_modifier.cc index 15d90729e9f..1855aefe51e 100644 --- a/source/blender/editors/object/object_modifier.cc +++ b/source/blender/editors/object/object_modifier.cc @@ -1143,7 +1143,17 @@ bool ED_object_modifier_apply(Main *bmain, BKE_report(reports, RPT_ERROR, "Modifiers cannot be applied in edit mode"); return false; } - if (mode != MODIFIER_APPLY_SHAPE && ID_REAL_USERS(ob->data) > 1) { + + bool allow_multi_user = mode == MODIFIER_APPLY_SHAPE; + if (md) { + const ModifierTypeInfo *mti = BKE_modifier_get_info((ModifierType)md->type); + + allow_multi_user |= ELEM( + mti->type, eModifierTypeType_NonGeometrical, eModifierTypeType_OnlyDeform); + } + + // bool allow_multi_user = md && md->type == eModifierType_DataTransfer || md->flag & ; + if (!allow_multi_user && ID_REAL_USERS(ob->data) > 1) { BKE_report(reports, RPT_ERROR, "Modifiers cannot be applied to multi-user data"); return false; } @@ -2954,7 +2964,8 @@ static Object *modifier_skin_armature_create(Depsgraph *depsgraph, Main *bmain, CustomData_get_layer_for_write(&me->vdata, CD_MVERT_SKIN, me->totvert)); int *emap_mem; MeshElemMap *emap; - BKE_mesh_vert_edge_map_create(&emap, &emap_mem, me_edges.data(), me->totvert, me->totedge); + BKE_mesh_vert_edge_map_create( + &emap, &emap_mem, nullptr, me_edges.data(), me->totvert, me->totedge, false); BLI_bitmap *edges_visited = BLI_BITMAP_NEW(me->totedge, "edge_visited"); diff --git a/source/blender/editors/object/object_relations.c b/source/blender/editors/object/object_relations.c index 0af1e908789..eac9a8541bc 100644 --- a/source/blender/editors/object/object_relations.c +++ b/source/blender/editors/object/object_relations.c @@ -22,6 +22,7 @@ #include "DNA_light_types.h" #include "DNA_material_types.h" #include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" #include "DNA_meta_types.h" #include "DNA_object_types.h" #include "DNA_particle_types.h" @@ -48,6 +49,7 @@ #include "BKE_context.h" #include "BKE_curve.h" #include "BKE_curves.h" +#include "BKE_customdata.h" #include "BKE_displist.h" #include "BKE_editmesh.h" #include "BKE_fcurve.h" @@ -69,9 +71,11 @@ #include "BKE_modifier.h" #include "BKE_node.h" #include "BKE_object.h" +#include "BKE_paint.h" #include "BKE_pointcloud.h" #include "BKE_report.h" #include "BKE_scene.h" +#include "BKE_screen.h" #include "BKE_speaker.h" #include "BKE_texture.h" #include "BKE_volume.h" @@ -97,6 +101,7 @@ #include "ED_mesh.h" #include "ED_object.h" #include "ED_screen.h" +#include "ED_sculpt.h" #include "ED_view3d.h" #include "MOD_nodes.h" @@ -2825,6 +2830,73 @@ char *ED_object_ot_drop_named_material_tooltip(bContext *C, const char *name, co return result; } +static void drop_named_material_face_set_slots_update(bContext *C, + Object *ob, + const wmEvent *event) +{ + Main *bmain = CTX_data_main(C); + Mesh *mesh = BKE_mesh_from_object(ob); + + bScreen *screen = CTX_wm_screen(C); + ARegion *region = BKE_screen_find_main_region_at_xy(screen, SPACE_VIEW3D, event->xy); + + const float mval[2] = {event->xy[0] - region->winrct.xmin, event->xy[1] - region->winrct.ymin}; + const int face_set_id = ED_sculpt_face_sets_active_update_and_get(C, ob, mval); + + int *face_sets = (int *)CustomData_get_layer_named( + &mesh->pdata, CD_PROP_INT32, ".sculpt_face_sets"); + int *mat_nr = (int *)CustomData_get_layer_named(&mesh->pdata, CD_PROP_INT32, "material_index"); + + if (!mat_nr) { + printf("%s: no material index!\n", __func__); + return; + } + + bool create_new_slot = false; + short face_set_nr = -1; + + if (face_sets) { + for (int i = 0; i < mesh->totpoly; i++) { + if (face_sets[i] != face_set_id) { + continue; + } + face_set_nr = mat_nr[i]; + break; + } + + for (int i = 0; i < mesh->totpoly; i++) { + if (face_sets[i] == face_set_id) { + if (mat_nr[i] != face_set_nr) { + create_new_slot = true; + break; + } + } + else { + if (mat_nr[i] == face_set_nr) { + create_new_slot = true; + break; + } + } + } + } + + if (create_new_slot) { + BKE_object_material_slot_add(bmain, ob); + } + else { + ob->actcol = face_set_nr + 1; + } + + const short active_mat_slot = ob->actcol; + const short material_nr = active_mat_slot - 1; + for (int i = 0; i < mesh->totpoly; i++) { + if (face_sets[i] != face_set_id) { + continue; + } + mat_nr[i] = material_nr; + } +} + static int drop_named_material_invoke(bContext *C, wmOperator *op, const wmEvent *event) { Main *bmain = CTX_data_main(C); @@ -2839,6 +2911,10 @@ static int drop_named_material_invoke(bContext *C, wmOperator *op, const wmEvent return OPERATOR_CANCELLED; } + if (ob->mode == OB_MODE_SCULPT) { + drop_named_material_face_set_slots_update(C, ob, event); + } + BKE_object_material_assign(CTX_data_main(C), ob, ma, mat_slot, BKE_MAT_ASSIGN_USERPREF); DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM); @@ -2850,6 +2926,32 @@ static int drop_named_material_invoke(bContext *C, wmOperator *op, const wmEvent return OPERATOR_FINISHED; } +bool ED_operator_drop_material_poll(bContext *C) +{ + Scene *scene = CTX_data_scene(C); + Object *obact = CTX_data_active_object(C); + + if (scene == NULL || ID_IS_LINKED(scene)) { + CTX_wm_operator_poll_msg_set(C, "Missing scene in context"); + return false; + } + if (CTX_data_edit_object(C)) { + CTX_wm_operator_poll_msg_set(C, "Cannot be used in edit mode"); + return false; + } + + if (obact && !ELEM(obact->mode, OB_MODE_OBJECT, OB_MODE_SCULPT)) { + CTX_wm_operator_poll_msg_set(C, "Only supported in object and sculpt modes"); + return false; + } + + return true; +} + +/** + * Used for drop-box. + * Assigns to object under cursor, only first material slot. + */ void OBJECT_OT_drop_named_material(wmOperatorType *ot) { /* identifiers */ @@ -2858,7 +2960,7 @@ void OBJECT_OT_drop_named_material(wmOperatorType *ot) /* api callbacks */ ot->invoke = drop_named_material_invoke; - ot->poll = ED_operator_objectmode_poll_msg; + ot->poll = ED_operator_drop_material_poll; /* flags */ ot->flag = OPTYPE_UNDO | OPTYPE_INTERNAL; diff --git a/source/blender/editors/object/object_remesh.cc b/source/blender/editors/object/object_remesh.cc index a808ca1337b..22c9e0a89dc 100644 --- a/source/blender/editors/object/object_remesh.cc +++ b/source/blender/editors/object/object_remesh.cc @@ -16,6 +16,7 @@ #include "BLI_math.h" #include "BLI_string.h" #include "BLI_string_utf8.h" +#include "BLI_threads.h" #include "BLI_utildefines.h" #include "DNA_mesh_types.h" @@ -34,6 +35,7 @@ #include "BKE_mesh_mirror.h" #include "BKE_mesh_remesh_voxel.h" #include "BKE_mesh_runtime.h" +#include "BKE_mesh_types.h" #include "BKE_modifier.h" #include "BKE_object.h" #include "BKE_paint.h" @@ -150,6 +152,7 @@ static int voxel_remesh_exec(bContext *C, wmOperator *op) if (ob->mode == OB_MODE_SCULPT) { ED_sculpt_undo_geometry_begin(ob, op); + ob->sculpt->needs_pbvh_rebuild = true; } if (mesh->flag & ME_REMESH_FIX_POLES && mesh->remesh_voxel_adaptivity <= 0.0f) { @@ -767,7 +770,7 @@ static void quadriflow_update_job(void *customdata, float progress, int *cancel) *(qj->progress) = progress; } -static Mesh *remesh_symmetry_bisect(Mesh *mesh, eSymmetryAxes symmetry_axes) +static Mesh *remesh_symmetry_bisect(Object *ob, Mesh *mesh, eSymmetryAxes symmetry_axes) { MirrorModifierData mmd = {{nullptr}}; mmd.tolerance = QUADRIFLOW_MIRROR_BISECT_TOLERANCE; @@ -788,8 +791,10 @@ static Mesh *remesh_symmetry_bisect(Mesh *mesh, eSymmetryAxes symmetry_axes) zero_v3(plane_no); plane_no[axis] = -1.0f; mesh_bisect_temp = mesh_bisect; + mesh_bisect = BKE_mesh_mirror_bisect_on_mirror_plane_for_modifier( - &mmd, mesh_bisect, axis, plane_co, plane_no); + ob, &mmd, mesh_bisect, axis, plane_co, plane_no); + if (mesh_bisect_temp != mesh_bisect) { BKE_id_free(nullptr, mesh_bisect_temp); } @@ -858,7 +863,7 @@ static void quadriflow_start_job(void *customdata, bool *stop, bool *do_update, bisect_mesh = BKE_mesh_copy_for_eval(mesh, false); /* Bisect the input mesh using the paint symmetry settings */ - bisect_mesh = remesh_symmetry_bisect(bisect_mesh, qj->symmetry_axes); + bisect_mesh = remesh_symmetry_bisect(ob, bisect_mesh, qj->symmetry_axes); new_mesh = BKE_mesh_remesh_quadriflow(bisect_mesh, qj->target_faces, diff --git a/source/blender/editors/object/object_vgroup.cc b/source/blender/editors/object/object_vgroup.cc index b1066c02ee4..76ec444f1f2 100644 --- a/source/blender/editors/object/object_vgroup.cc +++ b/source/blender/editors/object/object_vgroup.cc @@ -1945,7 +1945,8 @@ static void vgroup_smooth_subset(Object *ob, emap_mem = nullptr; } else { - BKE_mesh_vert_edge_map_create(&emap, &emap_mem, me->edges().data(), me->totvert, me->totedge); + BKE_mesh_vert_edge_map_create( + &emap, &emap_mem, nullptr, me->edges().data(), me->totvert, me->totedge, false); } weight_accum_prev = static_cast( diff --git a/source/blender/editors/render/CMakeLists.txt b/source/blender/editors/render/CMakeLists.txt index a91a63201c4..f4e8b9a494a 100644 --- a/source/blender/editors/render/CMakeLists.txt +++ b/source/blender/editors/render/CMakeLists.txt @@ -18,6 +18,7 @@ set(INC ../../sequencer ../../windowmanager ../../../../intern/guardedalloc + ../../../../intern/atomic # RNA_prototypes.h ${CMAKE_BINARY_DIR}/source/blender/makesrna ) diff --git a/source/blender/editors/sculpt_paint/CMakeLists.txt b/source/blender/editors/sculpt_paint/CMakeLists.txt index 957fb248481..17b81230b15 100644 --- a/source/blender/editors/sculpt_paint/CMakeLists.txt +++ b/source/blender/editors/sculpt_paint/CMakeLists.txt @@ -60,6 +60,9 @@ set(SRC paint_vertex_proj.c paint_vertex_weight_ops.cc paint_vertex_weight_utils.c + + sculpt_api.cc + sculpt_curvature.cc sculpt.cc sculpt_automasking.cc sculpt_boundary.cc @@ -85,6 +88,11 @@ set(SRC sculpt_undo.cc sculpt_uv.c + sculpt_new.cc + sculpt_new.hh + + sculpt_brush_machine.c + curves_sculpt_intern.h curves_sculpt_intern.hh paint_intern.h diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc b/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc index 89f0f9031df..c20ad6085f4 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc +++ b/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc @@ -266,7 +266,7 @@ static void SCULPT_CURVES_OT_brush_stroke(struct wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - paint_stroke_operator_properties(ot); + paint_stroke_operator_properties(ot, false); } /** \} */ diff --git a/source/blender/editors/sculpt_paint/paint_cursor.cc b/source/blender/editors/sculpt_paint/paint_cursor.cc index 11b8ae1b3fb..3772683a76b 100644 --- a/source/blender/editors/sculpt_paint/paint_cursor.cc +++ b/source/blender/editors/sculpt_paint/paint_cursor.cc @@ -1375,7 +1375,7 @@ static void paint_cursor_sculpt_session_update_and_init(PaintCursorContext *pcon pcontext->prev_active_vertex = ss->active_vertex; if (!ups->stroke_active) { pcontext->is_cursor_over_mesh = SCULPT_cursor_geometry_info_update( - C, &gi, mval_fl, (pcontext->brush->falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE)); + C, &gi, mval_fl, (pcontext->brush->falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE), false); copy_v3_v3(pcontext->location, gi.location); copy_v3_v3(pcontext->normal, gi.normal); } @@ -1461,6 +1461,99 @@ static void paint_draw_3D_view_inactive_brush_cursor(PaintCursorContext *pcontex 80); } +static void sculpt_cursor_draw_active_face_set_color_set(PaintCursorContext *pcontext) +{ + + SculptSession *ss = pcontext->ss; + + if (BKE_pbvh_type(ss->pbvh) != PBVH_FACES) { + return; + } + + const int active_face_set = SCULPT_active_face_set_get(ss); + uchar color[4] = {UCHAR_MAX, UCHAR_MAX, UCHAR_MAX, UCHAR_MAX}; + Object *ob = CTX_data_active_object(pcontext->C); + Mesh *mesh = (Mesh *)ob->data; + if (active_face_set != mesh->face_sets_color_default) { + BKE_paint_face_set_overlay_color_get(active_face_set, mesh->face_sets_color_seed, color); + color[3] = UCHAR_MAX; + } + else { + color[3] /= 2; + } + + immUniformColor4ubv(color); +} + +static void sculpt_cursor_draw_3D_face_set_preview(PaintCursorContext *pcontext) +{ + + SculptSession *ss = pcontext->ss; + + if (BKE_pbvh_type(ss->pbvh) != PBVH_FACES) { + return; + } + + GPU_line_width(1.0f); + sculpt_cursor_draw_active_face_set_color_set(pcontext); + + /* + MPoly *poly = &ss->mpoly[fi]; + MLoop *loops = ss->mloop; + const int totpoints = poly->totloop; + + immBegin(GPU_PRIM_LINE_STRIP, totpoints + 1); + for (int i = 0; i < totpoints; i++) { + float co[3]; + copy_v3_v3(co, SCULPT_vertex_co_get(ss, loops[poly->loopstart + i].v)); + immVertex3fv(pcontext->pos, co); + } + immVertex3fv(pcontext->pos, SCULPT_vertex_co_get(ss, loops[poly->loopstart].v)); + immEnd(); + */ + + /* + int v_in_poly = 0; + for (int i = 0; i < totpoints; i++) { + if (ss->active_vertex == loops[poly->loopstart + i].v) { + v_in_poly = i; + } + } + const int next_v = v_in_poly == poly->totloop - 1? 0 : v_in_poly + 1; + const int prev_v = v_in_poly == 0? poly->totloop - 1 : v_in_poly - 1; + + + immBegin(GPU_PRIM_LINES, 4); + immVertex3fv(pcontext->pos, SCULPT_vertex_co_get(ss, ss->active_vertex)); + immVertex3fv(pcontext->pos, SCULPT_vertex_co_get(ss, loops[poly->loopstart + next_v].v)); + + + immVertex3fv(pcontext->pos, SCULPT_vertex_co_get(ss, ss->active_vertex)); + immVertex3fv(pcontext->pos, SCULPT_vertex_co_get(ss, loops[poly->loopstart + prev_v].v)); + + immEnd(); + */ + + if (!ss->pmap) { + return; + } + + int total = 0; + SculptVertexNeighborIter ni; + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, ss->active_vertex, ni) { + total++; + } + SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); + + immBegin(GPU_PRIM_LINES, total * 2); + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, ss->active_vertex, ni) { + immVertex3fv(pcontext->pos, SCULPT_active_vertex_co_get(ss)); + immVertex3fv(pcontext->pos, SCULPT_vertex_co_get(ss, ni.vertex)); + } + SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); + immEnd(); +} + static void paint_cursor_update_object_space_radius(PaintCursorContext *pcontext) { if (!BKE_brush_use_locked_size(pcontext->scene, pcontext->brush)) { @@ -1560,7 +1653,7 @@ static void paint_cursor_preview_boundary_data_update(PaintCursorContext *pconte } ss->boundary_preview = SCULPT_boundary_data_init( - pcontext->vc.obact, pcontext->brush, ss->active_vertex, pcontext->radius); + pcontext->sd, pcontext->vc.obact, pcontext->brush, ss->active_vertex, pcontext->radius); } static void paint_cursor_draw_3d_view_brush_cursor_inactive(PaintCursorContext *pcontext) @@ -1606,6 +1699,7 @@ static void paint_cursor_draw_3d_view_brush_cursor_inactive(PaintCursorContext * } if (len_v3v3(active_vertex_co, pcontext->location) < pcontext->radius) { immUniformColor3fvAlpha(pcontext->outline_col, pcontext->outline_alpha); + sculpt_cursor_draw_active_face_set_color_set(pcontext); cursor_draw_point_with_symmetry(pcontext->pos, pcontext->region, active_vertex_co, @@ -1651,6 +1745,15 @@ static void paint_cursor_draw_3d_view_brush_cursor_inactive(PaintCursorContext * 2); } + /* Transform Pivot. */ + if (pcontext->paint && pcontext->paint->flags & PAINT_SCULPT_SHOW_PIVOT) { + cursor_draw_point_screen_space(pcontext->pos, + pcontext->region, + pcontext->ss->pivot_pos, + pcontext->vc.obact->object_to_world, + 2); + } + if (is_brush_tool && brush->sculpt_tool == SCULPT_TOOL_BOUNDARY) { paint_cursor_preview_boundary_data_update(pcontext, update_previews); paint_cursor_preview_boundary_data_pivot_draw(pcontext); @@ -1689,6 +1792,9 @@ static void paint_cursor_draw_3d_view_brush_cursor_inactive(PaintCursorContext * SCULPT_boundary_pivot_line_preview_draw(pcontext->pos, pcontext->ss); } + /* Face Set Preview. */ + sculpt_cursor_draw_3D_face_set_preview(pcontext); + GPU_matrix_pop(); /* Drawing Cursor overlays in Paint Cursor space (as additional info on top of the brush cursor) @@ -1706,8 +1812,16 @@ static void paint_cursor_draw_3d_view_brush_cursor_inactive(PaintCursorContext * /* This functions sets its own drawing space in order to draw the simulation limits when the * cursor is active. When used here, this cursor overlay is already in cursor space, so its * position and normal should be set to 0. */ - SCULPT_cloth_simulation_limits_draw( - pcontext->pos, brush, zero_v, zero_v, pcontext->radius, 1.0f, white, 0.25f); + SCULPT_cloth_simulation_limits_draw(pcontext->ss, + pcontext->sd, + pcontext->pos, + brush, + zero_v, + zero_v, + pcontext->radius, + 1.0f, + white, + 0.25f); } /* Layer brush height. */ @@ -1786,7 +1900,9 @@ static void paint_cursor_cursor_draw_3d_view_brush_cursor_active(PaintCursorCont if (len_v3v3(ss->cache->true_location, ss->cache->true_initial_location) > ss->cache->radius * (1.0f + brush->cloth_sim_limit)) { const float red[3] = {1.0f, 0.2f, 0.2f}; - SCULPT_cloth_simulation_limits_draw(pcontext->pos, + SCULPT_cloth_simulation_limits_draw(pcontext->ss, + pcontext->sd, + pcontext->pos, brush, ss->cache->true_initial_location, ss->cache->true_initial_normal, @@ -1848,7 +1964,8 @@ static void paint_cursor_update_rake_rotation(PaintCursorContext *pcontext) * and we may get interference with the stroke itself. * For line strokes, such interference is visible. */ if (!pcontext->ups->stroke_active) { - paint_calculate_rake_rotation(pcontext->ups, pcontext->brush, pcontext->translation); + paint_calculate_rake_rotation( + pcontext->ups, pcontext->brush, pcontext->translation, pcontext->translation); } } @@ -1954,3 +2071,4 @@ void ED_paint_cursor_start(Paint *p, bool (*poll)(bContext *C)) /* Invalidate the paint cursors. */ BKE_paint_invalidate_overlay_all(); } + diff --git a/source/blender/editors/sculpt_paint/paint_hide.cc b/source/blender/editors/sculpt_paint/paint_hide.cc index 6a5acfccb62..07edf381589 100644 --- a/source/blender/editors/sculpt_paint/paint_hide.cc +++ b/source/blender/editors/sculpt_paint/paint_hide.cc @@ -193,17 +193,15 @@ static void partialvis_update_grids(Depsgraph *depsgraph, } static void partialvis_update_bmesh_verts(BMesh *bm, - GSet *verts, + TableGSet *verts, PartialVisAction action, PartialVisArea area, float planes[4][4], bool *any_changed, bool *any_visible) { - GSetIterator gs_iter; - - GSET_ITER (gs_iter, verts) { - BMVert *v = static_cast(BLI_gsetIterator_getKey(&gs_iter)); + BMVert *v; + TGSET_ITER (v, verts) { float *vmask = static_cast( CustomData_bmesh_get(&bm->vdata, v->head.data, CD_PAINT_MASK)); @@ -222,14 +220,16 @@ static void partialvis_update_bmesh_verts(BMesh *bm, (*any_visible) = true; } } + TGSET_ITER_END } -static void partialvis_update_bmesh_faces(GSet *faces) +static void partialvis_update_bmesh_faces(TableGSet *faces, int cd_hide_poly) { - GSetIterator gs_iter; - - GSET_ITER (gs_iter, faces) { - BMFace *f = static_cast(BLI_gsetIterator_getKey(&gs_iter)); + BMFace *f; + + TGSET_ITER (f, faces) { + bool hidden = paint_is_bmesh_face_hidden(f); + BM_ELEM_CD_SET_BOOL(f, cd_hide_poly, hidden); if (paint_is_bmesh_face_hidden(f)) { BM_elem_flag_enable(f, BM_ELEM_HIDDEN); @@ -238,6 +238,7 @@ static void partialvis_update_bmesh_faces(GSet *faces) BM_elem_flag_disable(f, BM_ELEM_HIDDEN); } } + TGSET_ITER_END } static void partialvis_update_bmesh(Object *ob, @@ -248,7 +249,7 @@ static void partialvis_update_bmesh(Object *ob, float planes[4][4]) { BMesh *bm; - GSet *unique, *other, *faces; + TableGSet *unique, *other, *faces; bool any_changed = false, any_visible = false; bm = BKE_pbvh_get_bmesh(pbvh); @@ -263,11 +264,12 @@ static void partialvis_update_bmesh(Object *ob, partialvis_update_bmesh_verts(bm, other, action, area, planes, &any_changed, &any_visible); /* Finally loop over node faces and tag the ones that are fully hidden. */ - partialvis_update_bmesh_faces(faces); + partialvis_update_bmesh_faces(faces, ob->sculpt->attrs.hide_poly->bmesh_cd_offset); if (any_changed) { BKE_pbvh_node_mark_rebuild_draw(node); BKE_pbvh_node_fully_hidden_set(node, !any_visible); + BKE_pbvh_vert_tag_update_normal_triangulation(node); } } @@ -343,6 +345,8 @@ static int hide_show_exec(bContext *C, wmOperator *op) clip_planes_from_rect(C, depsgraph, clip_planes, &rect); pbvh = BKE_sculpt_object_pbvh_ensure(depsgraph, ob); + BKE_sculpt_hide_poly_ensure(ob); + BLI_assert(ob->sculpt->pbvh == pbvh); get_pbvh_nodes(pbvh, &nodes, &totnode, clip_planes, area); diff --git a/source/blender/editors/sculpt_paint/paint_image_ops_paint.cc b/source/blender/editors/sculpt_paint/paint_image_ops_paint.cc index d5c5aa5cebd..7228815554a 100644 --- a/source/blender/editors/sculpt_paint/paint_image_ops_paint.cc +++ b/source/blender/editors/sculpt_paint/paint_image_ops_paint.cc @@ -522,6 +522,6 @@ void PAINT_OT_image_paint(wmOperatorType *ot) /* flags */ ot->flag = OPTYPE_BLOCKING; - paint_stroke_operator_properties(ot); + paint_stroke_operator_properties(ot, false); } } diff --git a/source/blender/editors/sculpt_paint/paint_image_proj.cc b/source/blender/editors/sculpt_paint/paint_image_proj.cc index 9bf9f668a6b..3a755439f52 100644 --- a/source/blender/editors/sculpt_paint/paint_image_proj.cc +++ b/source/blender/editors/sculpt_paint/paint_image_proj.cc @@ -1088,10 +1088,11 @@ static bool pixel_bounds_uv(const float uv_quad[4][2], #endif static bool pixel_bounds_array( - float (*uv)[2], rcti *bounds_px, const int ibuf_x, const int ibuf_y, int tot) + float (*in_uv)[2], rcti *bounds_px, const int ibuf_x, const int ibuf_y, int tot) { /* UV bounds */ float min_uv[2], max_uv[2]; + float(*uv)[2] = in_uv; if (tot == 0) { return false; @@ -1099,9 +1100,8 @@ static bool pixel_bounds_array( INIT_MINMAX2(min_uv, max_uv); - while (tot--) { + for (int i = 0; i < tot; i++, uv++) { minmax_v2v2_v2(min_uv, max_uv, (*uv)); - uv++; } bounds_px->xmin = int(ibuf_x * min_uv[0]); @@ -1110,6 +1110,15 @@ static bool pixel_bounds_array( bounds_px->xmax = int(ibuf_x * max_uv[0]) + 1; bounds_px->ymax = int(ibuf_y * max_uv[1]) + 1; + const int d = -1000; + if (bounds_px->xmin < d || bounds_px->ymin < d || bounds_px->xmax < d || bounds_px->ymax < d) { + printf("error! xmin %d xmax %d ymin %d ymax %d\n", + bounds_px->xmin, + bounds_px->xmax, + bounds_px->ymin, + bounds_px->ymax); + } + // printf("%d %d %d %d\n", min_px[0], min_px[1], max_px[0], max_px[1]); /* face uses no UV area when quantized to pixels? */ @@ -5933,6 +5942,7 @@ void *paint_proj_new_stroke(bContext *C, Object *ob, const float mouse[2], int m ProjStrokeHandle *ps_handle; Scene *scene = CTX_data_scene(C); ToolSettings *settings = scene->toolsettings; + ePaintMode paintmode = BKE_paintmode_get_active_from_context(C); char symmetry_flag_views[ARRAY_SIZE(ps_handle->ps_views)] = {0}; ps_handle = MEM_cnew("ProjStrokeHandle"); diff --git a/source/blender/editors/sculpt_paint/paint_intern.h b/source/blender/editors/sculpt_paint/paint_intern.h index 313941ee654..ec1de133aa4 100644 --- a/source/blender/editors/sculpt_paint/paint_intern.h +++ b/source/blender/editors/sculpt_paint/paint_intern.h @@ -18,7 +18,16 @@ #include "DNA_scene_types.h" #ifdef __cplusplus +namespace blender { +template class BezierSpline; +} + +using BezierSpline2f = blender::BezierSpline; +using BezierSpline3f = blender::BezierSpline; extern "C" { +#else +typedef struct BezierSpline2f BezierSpline2f; +typedef struct BezierSpline3f BezierSpline3f; #endif struct ARegion; @@ -46,6 +55,10 @@ typedef struct CoNo { float no[3]; } CoNo; +#include "DNA_listBase.h" +#include "DNA_scene_types.h" +#include "ED_view3d.h" + /* paint_stroke.cc */ typedef bool (*StrokeGetLocation)(struct bContext *C, @@ -58,7 +71,7 @@ typedef void (*StrokeUpdateStep)(struct bContext *C, struct PaintStroke *stroke, struct PointerRNA *itemptr); typedef void (*StrokeRedraw)(const struct bContext *C, struct PaintStroke *stroke, bool final); -typedef void (*StrokeDone)(const struct bContext *C, struct PaintStroke *stroke); +typedef void (*StrokeDone)(const struct bContext *C, struct PaintStroke *stroke);; struct PaintStroke *paint_stroke_new(struct bContext *C, struct wmOperator *op, @@ -110,6 +123,19 @@ bool PAINT_brush_tool_poll(struct bContext *C); */ void paint_cursor_delete_textures(void); +/** + * used by various actions that have their own spacing that + * is coarser then the brush spacing. e.g. sculpt dyntopo. + * + * \param state: pointer to a float used for internal state, should be initialized to zero at start + * of stroke \return false if the action should be skipped. + * + */ +bool paint_stroke_apply_subspacing(struct PaintStroke *stroke, + const float spacing, + const enum ePaintMode mode, + float *state); + /* paint_vertex.c */ bool weight_paint_poll(struct bContext *C); @@ -369,9 +395,10 @@ void paint_get_tex_pixel_col(const struct MTex *mtex, void paint_sample_color( struct bContext *C, struct ARegion *region, int x, int y, bool texpaint_proj, bool palette); -void paint_stroke_operator_properties(struct wmOperatorType *ot); +void paint_stroke_operator_properties(struct wmOperatorType *ot, bool mode_skip_save); void BRUSH_OT_curve_preset(struct wmOperatorType *ot); +void BRUSH_OT_curve_preset_load(struct wmOperatorType *ot); void PAINT_OT_face_select_linked(struct wmOperatorType *ot); void PAINT_OT_face_select_linked_pick(struct wmOperatorType *ot); @@ -391,6 +418,7 @@ bool mask_paint_poll(struct bContext *C); bool paint_curve_poll(struct bContext *C); bool facemask_paint_poll(struct bContext *C); + /** * Uses symm to selectively flip any axis of a coordinate. */ @@ -525,6 +553,22 @@ void paint_init_pivot(struct Object *ob, struct Scene *scene); /* paint curve defines */ #define PAINT_CURVE_NUM_SEGMENTS 40 +bool paint_stroke_has_cubic(const struct PaintStroke *stroke); +float bezier3_arclength_v2(const float control[4][2]); +float bezier3_arclength_v3(const float control[4][3]); +void evaluate_cubic_bezier(const float control[4][3], float t, float r_pos[3], float r_tangent[3]); + +void paint_project_spline(struct bContext *C, + struct StrokeCache *cache, + struct PaintStroke *stroke); +; +void paint_calc_cubic_uv_v3(struct PaintStroke *stroke, + struct StrokeCache *cache, + const float co[3], + float r_out[3], + float r_tan[3]); +float paint_stroke_spline_length(struct PaintStroke *stroke); + #ifdef __cplusplus } #endif diff --git a/source/blender/editors/sculpt_paint/paint_mask.cc b/source/blender/editors/sculpt_paint/paint_mask.cc index 0ca6e9e0d74..cc0db5d0827 100644 --- a/source/blender/editors/sculpt_paint/paint_mask.cc +++ b/source/blender/editors/sculpt_paint/paint_mask.cc @@ -29,6 +29,8 @@ #include "BKE_context.h" #include "BKE_lib_id.h" #include "BKE_mesh.h" +#include "BKE_mesh_fair.h" +#include "BKE_mesh_types.h" #include "BKE_multires.h" #include "BKE_paint.h" #include "BKE_pbvh.h" @@ -649,8 +651,8 @@ static bool sculpt_gesture_is_effected_lasso(SculptGestureContext *sgcontext, co ED_view3d_project_float_v2_m4( sgcontext->vc.region, co_final, scr_co_f, sgcontext->lasso.projviewobjmat); - scr_co_s[0] = scr_co_f[0]; - scr_co_s[1] = scr_co_f[1]; + scr_co_s[0] = (int)scr_co_f[0]; + scr_co_s[1] = (int)scr_co_f[1]; /* Clip against lasso boundbox. */ LassoGestureData *lasso = &sgcontext->lasso; @@ -765,7 +767,7 @@ static void face_set_gesture_apply_task_cb(void *__restrict userdata, BKE_pbvh_face_iter_end(fd); if (any_updated) { - BKE_pbvh_node_mark_update_visibility(node); + BKE_pbvh_vert_tag_update_normal_visibility(node); } } @@ -790,7 +792,7 @@ static void sculpt_gesture_init_face_set_properties(SculptGestureContext *sgcont sgcontext->operation = reinterpret_cast( MEM_cnew(__func__)); - sgcontext->ss->face_sets = BKE_sculpt_face_sets_ensure(mesh); + sgcontext->ss->face_sets = BKE_sculpt_face_sets_ensure(sgcontext->vc.obact); SculptGestureFaceSetOperation *face_set_operation = (SculptGestureFaceSetOperation *) sgcontext->operation; @@ -957,6 +959,24 @@ static EnumPropertyItem prop_trim_orientation_types[] = { {0, nullptr, 0, nullptr, nullptr}, }; +typedef enum eSculptTrimLocationType { + SCULPT_GESTURE_TRIM_LOCATION_DEPTH_SURFACE, + SCULPT_GESTURE_TRIM_LOCATION_DEPTH_VOLUME, +} eSculptTrimLocationType; +static EnumPropertyItem prop_trim_location_types[] = { + {SCULPT_GESTURE_TRIM_LOCATION_DEPTH_SURFACE, + "DEPTH_SURFACE", + 0, + "Surface", + "Use the surface under the cursor to locate the trimming shape"}, + {SCULPT_GESTURE_TRIM_LOCATION_DEPTH_VOLUME, + "DEPTH_VOLUME", + 0, + "Volume", + "Use the volume of the mesh to locate the trimming shape in the center of the volume"}, + {0, NULL, 0, NULL, NULL}, +}; + enum eSculptTrimExtrudeMode { SCULPT_GESTURE_TRIM_EXTRUDE_PROJECT, SCULPT_GESTURE_TRIM_EXTRUDE_FIXED @@ -980,11 +1000,13 @@ struct SculptGestureTrimOperation { float depth_front; float depth_back; + float avg_edge_len; bool use_cursor_depth; eSculptTrimOperationType mode; eSculptTrimOrientationType orientation; + eSculptTrimLocationType location; eSculptTrimExtrudeMode extrude_mode; }; @@ -1002,7 +1024,7 @@ static void sculpt_gesture_trim_normals_update(SculptGestureContext *sgcontext) BMeshFromMeshParams bm_from_me_params{}; bm_from_me_params.calc_face_normal = true; bm_from_me_params.calc_vert_normal = true; - BM_mesh_bm_from_me(bm, trim_mesh, &bm_from_me_params); + BM_mesh_bm_from_me(nullptr, bm, trim_mesh, &bm_from_me_params); BM_mesh_elem_hflag_enable_all(bm, BM_FACE, BM_ELEM_TAG, false); BMO_op_callf(bm, @@ -1069,6 +1091,7 @@ static void sculpt_gesture_trim_calculate_depth(SculptGestureContext *sgcontext) PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); const float *vco = SCULPT_vertex_co_get(ss, vertex); + /* Convert the coordinates to world space to calculate the depth. When generating the trimming * mesh, coordinates are first calculated in world space, then converted to object space to * store them. */ @@ -1081,9 +1104,20 @@ static void sculpt_gesture_trim_calculate_depth(SculptGestureContext *sgcontext) if (trim_operation->use_cursor_depth) { float world_space_gesture_initial_location[3]; - mul_v3_m4v3(world_space_gesture_initial_location, - vc->obact->object_to_world, - ss->gesture_initial_location); + + switch (trim_operation->location) { + case SCULPT_GESTURE_TRIM_LOCATION_DEPTH_SURFACE: { + mul_v3_m4v3(world_space_gesture_initial_location, + vc->obact->object_to_world, + ss->gesture_initial_location); + + } break; + case SCULPT_GESTURE_TRIM_LOCATION_DEPTH_VOLUME: { + float center_co[3]; + mid_v3_v3v3(center_co, ss->gesture_initial_location, ss->gesture_initial_back_location); + mul_v3_m4v3(world_space_gesture_initial_location, vc->obact->object_to_world, center_co); + } break; + } float mid_point_depth; if (trim_operation->orientation == SCULPT_GESTURE_TRIM_ORIENTATION_VIEW) { @@ -1096,6 +1130,17 @@ static void sculpt_gesture_trim_calculate_depth(SculptGestureContext *sgcontext) /* When using normal orientation, if the stroke started over the mesh, position the mid point * at 0 distance from the shape plane. This positions the trimming shape half inside of the * surface. */ + if (SCULPT_GESTURE_TRIM_LOCATION_DEPTH_VOLUME) { + mid_point_depth = ss->gesture_initial_hit ? + dist_signed_to_plane_v3(world_space_gesture_initial_location, + shape_plane) : + (trim_operation->depth_back + trim_operation->depth_front) * 0.5f; + } + else { + mid_point_depth = ss->gesture_initial_hit ? + 0.0f : + (trim_operation->depth_back + trim_operation->depth_front) * 0.5f; + } mid_point_depth = ss->gesture_initial_hit ? 0.0f : (trim_operation->depth_back + trim_operation->depth_front) * 0.5f; @@ -1185,6 +1230,17 @@ static void sculpt_gesture_trim_geometry_generate(SculptGestureContext *sgcontex copy_v3_v3(positions[i], new_point); } + float avg_elen = 0.0f; + + for (int i = 0; i < tot_screen_points; i++) { + float *v1 = trim_operation->true_mesh_co[i]; + float *v2 = trim_operation->true_mesh_co[((i + 1) % tot_screen_points)]; + + avg_elen += len_v3v3(v1, v2); + } + + trim_operation->avg_edge_len = avg_elen / (float)tot_screen_points; + /* Write vertices coordinates for the back face. */ madd_v3_v3v3fl(depth_point, shape_origin, shape_normal, depth_back); for (int i = 0; i < tot_screen_points; i++) { @@ -1277,8 +1333,29 @@ static void sculpt_gesture_trim_geometry_generate(SculptGestureContext *sgcontex BKE_mesh_calc_edges(trim_operation->mesh, false, false); sculpt_gesture_trim_normals_update(sgcontext); -} + mp = trim_operation->mesh->mpoly + tot_tris_face * 2; + + bool *sharp_edge = (bool *)CustomData_get_layer_named_for_write( + &trim_operation->mesh->edata, CD_PROP_BOOL, "sharp_edge", trim_operation->mesh->totedge); + + if (!sharp_edge) { + CustomData_add_layer(&trim_operation->mesh->edata, + CD_PROP_BOOL, + CD_CONSTRUCT, + nullptr, + trim_operation->mesh->totedge); + sharp_edge = (bool *)CustomData_get_layer_named_for_write( + &trim_operation->mesh->edata, CD_PROP_BOOL, "sharp_edge", trim_operation->mesh->totedge); + } + + /* flag edges as sharp for dyntopo remesher */ + for (int i = 0; i < tot_screen_points * 2; i++, mp++) { + ml = trim_operation->mesh->mloop + mp->loopstart; + + sharp_edge[ml[1].e] = true; + } +} static void sculpt_gesture_trim_geometry_free(SculptGestureContext *sgcontext) { SculptGestureTrimOperation *trim_operation = (SculptGestureTrimOperation *)sgcontext->operation; @@ -1288,7 +1365,7 @@ static void sculpt_gesture_trim_geometry_free(SculptGestureContext *sgcontext) static int bm_face_isect_pair(BMFace *f, void * /*user_data*/) { - return BM_elem_flag_test(f, BM_ELEM_DRAW) ? 1 : 0; + return BM_elem_flag_test(f, BM_ELEM_DRAW) ? 0 : 1; } static void sculpt_gesture_apply_trim(SculptGestureContext *sgcontext) @@ -1297,17 +1374,62 @@ static void sculpt_gesture_apply_trim(SculptGestureContext *sgcontext) Mesh *sculpt_mesh = BKE_mesh_from_object(sgcontext->vc.obact); Mesh *trim_mesh = trim_operation->mesh; + BMesh *bm; + + if (sgcontext->ss && sgcontext->ss->bm) { + bm = sgcontext->ss->bm; + BM_mesh_normals_update(bm); + } + + else { + const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(sculpt_mesh, trim_mesh); + BMeshCreateParams params = {0}; + params.use_toolflags = false; + + BMeshFromMeshParams params2 = {0}; + params2.calc_face_normal = true; + + bm = BM_mesh_create(&allocsize, ¶ms); + + BM_mesh_bm_from_me(nullptr, bm, sculpt_mesh, ¶ms2); + } + + BMeshCreateParams params1 = {0}; + params1.create_unique_ids = params1.id_map = params1.copy_all_layers = true; + params1.id_elem_mask = BM_VERT | BM_EDGE | BM_FACE; + const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(sculpt_mesh, trim_mesh); + BMesh *trimbm = BM_mesh_create(&allocsize, ¶ms1); - BMeshCreateParams bm_create_params{}; - bm_create_params.use_toolflags = false; - BMesh *bm = BM_mesh_create(&allocsize, &bm_create_params); + BMeshFromMeshParams params3 = {0}; + params3.calc_face_normal = params3.calc_vert_normal = true; - BMeshFromMeshParams bm_from_me_params{}; - bm_from_me_params.calc_face_normal = true; - bm_from_me_params.calc_vert_normal = true; - BM_mesh_bm_from_me(bm, trim_mesh, &bm_from_me_params); - BM_mesh_bm_from_me(bm, sculpt_mesh, &bm_from_me_params); + BM_mesh_bm_from_me(nullptr, trimbm, trim_mesh, ¶ms3); + + BM_mesh_normals_update(bm); + +#if 0 + // remesh + DynTopoState *ds = BKE_dyntopo_init(trimbm, NULL); + DynRemeshParams params; + BKE_dyntopo_default_params(¶ms, trim_operation->avg_edge_len * 4.0); + BKE_dyntopo_remesh(ds, ¶ms, 10, PBVH_Collapse | PBVH_Cleanup | PBVH_Subdivide); + + BM_mesh_toolflags_set(bm, true); + + BKE_dyntopo_free(ds); +#endif + + BM_mesh_toolflags_set(bm, true); + + BMO_op_callf(trimbm, BMO_FLAG_DEFAULTS, "duplicate geom=%avef dest=%p", bm, 3); + + BKE_sculptsession_update_attr_refs(sgcontext->vc.obact); + BM_mesh_free(trimbm); + + BMeshFromMeshParams params2 = {0}; + params2.calc_face_normal = params2.calc_vert_normal = true; + BM_mesh_bm_from_me(nullptr, bm, sculpt_mesh, ¶ms2); const int looptris_tot = poly_to_tri_count(bm->totface, bm->totloop); BMLoop *(*looptris)[3] = static_cast( @@ -1373,13 +1495,34 @@ static void sculpt_gesture_apply_trim(SculptGestureContext *sgcontext) MEM_freeN(looptris); - BMeshToMeshParams convert_params{}; - convert_params.calc_object_remap = false; - Mesh *result = BKE_mesh_from_bmesh_nomain(bm, &convert_params, sculpt_mesh); + if (sgcontext->ss && sgcontext->ss->bm) { // rebuild pbvh + BKE_pbvh_free(sgcontext->ss->pbvh); + sgcontext->ss->pbvh = BKE_pbvh_new(PBVH_BMESH); - BM_mesh_free(bm); - BKE_mesh_nomain_to_mesh( - result, static_cast(sgcontext->vc.obact->data), sgcontext->vc.obact); + BKE_pbvh_build_bmesh(sgcontext->ss->pbvh, + sculpt_mesh, + sgcontext->ss->bm, + sgcontext->ss->bm_smooth_shading, + sgcontext->ss->bm_log, + sgcontext->ss->bm_idmap, + sgcontext->ss->cd_vert_node_offset, + sgcontext->ss->cd_face_node_offset, + sgcontext->ss->cd_sculpt_vert, + sgcontext->ss->cd_face_areas, + sgcontext->ss->attrs.boundary_flags->bmesh_cd_offset, + sgcontext->ss->fast_draw, + true); + } + else { // save result to mesh + BMeshToMeshParams params = {0}; + params.calc_object_remap = false; + + Mesh *result = BKE_mesh_from_bmesh_nomain(bm, ¶ms, sculpt_mesh); + BM_mesh_free(bm); + + BKE_mesh_normals_tag_dirty(result); + BKE_mesh_nomain_to_mesh(result, (Mesh *)sgcontext->vc.obact->data, sgcontext->vc.obact); + } } static void sculpt_gesture_trim_begin(bContext *C, SculptGestureContext *sgcontext) @@ -1387,7 +1530,7 @@ static void sculpt_gesture_trim_begin(bContext *C, SculptGestureContext *sgconte Object *object = sgcontext->vc.obact; SculptSession *ss = object->sculpt; Mesh *mesh = (Mesh *)object->data; - ss->face_sets = BKE_sculpt_face_sets_ensure(mesh); + ss->face_sets = BKE_sculpt_face_sets_ensure(object); Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); sculpt_gesture_trim_calculate_depth(sgcontext); @@ -1445,6 +1588,7 @@ static void sculpt_gesture_init_trim_properties(SculptGestureContext *sgcontext, trim_operation->mode = eSculptTrimOperationType(RNA_enum_get(op->ptr, "trim_mode")); trim_operation->use_cursor_depth = RNA_boolean_get(op->ptr, "use_cursor_depth"); + trim_operation->location = (eSculptTrimLocationType)RNA_enum_get(op->ptr, "trim_location"); trim_operation->orientation = eSculptTrimOrientationType( RNA_enum_get(op->ptr, "trim_orientation")); trim_operation->extrude_mode = eSculptTrimExtrudeMode( @@ -1476,6 +1620,13 @@ static void sculpt_trim_gesture_operator_properties(wmOperatorType *ot) SCULPT_GESTURE_TRIM_ORIENTATION_VIEW, "Shape Orientation", nullptr); + + RNA_def_enum(ot->srna, + "trim_location", + prop_trim_location_types, + SCULPT_GESTURE_TRIM_LOCATION_DEPTH_SURFACE, + "Shape Location", + nullptr); RNA_def_enum(ot->srna, "trim_extrude_mode", prop_trim_extrude_modes, @@ -1485,15 +1636,43 @@ static void sculpt_trim_gesture_operator_properties(wmOperatorType *ot) } /* Project Gesture Operation. */ +typedef enum eSculptProjectDeformationMode { + SCULPT_GESTURE_PROJECT_DEFORM_PROJECT, + SCULPT_GESTURE_PROJECT_DEFORM_FAIR, +} eSculptProjectDeformationMode; + +static EnumPropertyItem prop_project_deformation_mode_types[] = { + {SCULPT_GESTURE_PROJECT_DEFORM_PROJECT, + "PROJECT", + 0, + "Project to Plane", + "Project the affected geometry into the gesture plane"}, + {SCULPT_GESTURE_PROJECT_DEFORM_FAIR, + "FAIR", + 0, + "Fair Positions", + "Use position fairing in the affected area"}, + {0, NULL, 0, NULL, NULL}, +}; struct SculptGestureProjectOperation { SculptGestureOperation operation; + eSculptProjectDeformationMode deformation_mode; + bool *fairing_mask; }; static void sculpt_gesture_project_begin(bContext *C, SculptGestureContext *sgcontext) { + SculptGestureProjectOperation *project_operation = (SculptGestureProjectOperation *) + sgcontext->operation; Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); - BKE_sculpt_update_object_for_edit(depsgraph, sgcontext->vc.obact, false, false, false); + BKE_sculpt_update_object_for_edit(depsgraph, sgcontext->vc.obact, true, false, false); + + if (project_operation->deformation_mode == SCULPT_GESTURE_PROJECT_DEFORM_FAIR) { + const int totvert = SCULPT_vertex_count_get(sgcontext->ss); + project_operation->fairing_mask = (bool *)MEM_calloc_arrayN( + totvert, sizeof(bool), "fairing mask"); + } } static void project_line_gesture_apply_task_cb(void *__restrict userdata, @@ -1536,27 +1715,138 @@ static void project_line_gesture_apply_task_cb(void *__restrict userdata, } } +static void project_gesture_tag_fairing_task_cb(void *__restrict userdata, + const int i, + const TaskParallelTLS *__restrict /* tls */) +{ + SculptGestureContext *sgcontext = (SculptGestureContext *)userdata; + SculptGestureProjectOperation *project_operation = (SculptGestureProjectOperation *) + sgcontext->operation; + + PBVHNode *node = sgcontext->nodes[i]; + PBVHVertexIter vd; + bool any_updated = false; + + SCULPT_undo_push_node(sgcontext->vc.obact, node, SCULPT_UNDO_COORDS); + + BKE_pbvh_vertex_iter_begin (sgcontext->ss->pbvh, node, vd, PBVH_ITER_UNIQUE) { + if (!sculpt_gesture_is_vertex_effected(sgcontext, vd.vertex)) { + continue; + } + project_operation->fairing_mask[vd.index] = true; + if (vd.is_mesh) { + BKE_pbvh_vert_tag_update_normal(sgcontext->ss->pbvh, vd.vertex); + } + any_updated = true; + } + BKE_pbvh_vertex_iter_end; + + if (any_updated) { + BKE_pbvh_node_mark_update(node); + } +} + +static void project_gesture_project_fairing_boundary_task_cb( + void *__restrict userdata, const int i, const TaskParallelTLS *__restrict /* tls */) +{ + SculptGestureContext *sgcontext = (SculptGestureContext *)userdata; + SculptGestureProjectOperation *project_operation = (SculptGestureProjectOperation *) + sgcontext->operation; + SculptSession *ss = sgcontext->ss; + + PBVHNode *node = sgcontext->nodes[i]; + PBVHVertexIter vd; + BKE_pbvh_vertex_iter_begin (sgcontext->ss->pbvh, node, vd, PBVH_ITER_UNIQUE) { + bool project_vertex = false; + bool vertex_fairing_mask = project_operation->fairing_mask[vd.index]; + + if (!project_operation->fairing_mask[vd.index]) { + // continue; + } + + SculptVertexNeighborIter ni; + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.vertex, ni) { + if (project_operation->fairing_mask[ni.index] != vertex_fairing_mask) { + project_vertex = true; + break; + } + } + SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); + + if (!project_vertex) { + continue; + } + + closest_to_plane_v3(vd.co, sgcontext->line.plane, vd.co); + + if (vd.is_mesh) { + BKE_pbvh_vert_tag_update_normal(sgcontext->ss->pbvh, vd.vertex); + } + } + BKE_pbvh_vertex_iter_end; +} + static void sculpt_gesture_project_apply_for_symmetry_pass(bContext * /*C*/, SculptGestureContext *sgcontext) { + SculptGestureProjectOperation *project_operation = (SculptGestureProjectOperation *) + sgcontext->operation; TaskParallelSettings settings; BKE_pbvh_parallel_range_settings(&settings, true, sgcontext->totnode); - switch (sgcontext->shape_type) { - case SCULPT_GESTURE_SHAPE_LINE: + switch (project_operation->deformation_mode) { + case SCULPT_GESTURE_PROJECT_DEFORM_PROJECT: + BLI_assert(sgcontext->shape_type == SCULPT_GESTURE_SHAPE_LINE); BLI_task_parallel_range( 0, sgcontext->totnode, sgcontext, project_line_gesture_apply_task_cb, &settings); break; - case SCULPT_GESTURE_SHAPE_LASSO: - case SCULPT_GESTURE_SHAPE_BOX: - /* Gesture shape projection not implemented yet. */ - BLI_assert(false); + case SCULPT_GESTURE_PROJECT_DEFORM_FAIR: + BLI_task_parallel_range( + 0, sgcontext->totnode, sgcontext, project_gesture_tag_fairing_task_cb, &settings); + if (sgcontext->shape_type == SCULPT_GESTURE_SHAPE_LINE) { + /* TODO: this needs to loop over all nodes to avoid artifacts. */ + /* + BLI_task_parallel_range(0, + sgcontext->totnode, + sgcontext, + project_gesture_project_fairing_boundary_task_cb, + &settings); + */ + } break; } } +static void sculpt_gesture_fairing_apply(SculptGestureContext *sgcontext) +{ + SculptSession *ss = sgcontext->vc.obact->sculpt; + SculptGestureProjectOperation *project_operation = (SculptGestureProjectOperation *) + sgcontext->operation; + switch (BKE_pbvh_type(ss->pbvh)) { + case PBVH_FACES: { + Mesh *mesh = (Mesh *)sgcontext->vc.obact->data; + float(*vert_cos)[3] = SCULPT_mesh_deformed_positions_get(ss); + BKE_mesh_prefair_and_fair_verts( + mesh, vert_cos, project_operation->fairing_mask, MESH_FAIRING_DEPTH_POSITION); + } break; + case PBVH_BMESH: { + BKE_bmesh_prefair_and_fair_verts( + ss->bm, project_operation->fairing_mask, MESH_FAIRING_DEPTH_POSITION); + } break; + case PBVH_GRIDS: + BLI_assert(false); + } +} + static void sculpt_gesture_project_end(bContext *C, SculptGestureContext *sgcontext) { + SculptGestureProjectOperation *project_operation = (SculptGestureProjectOperation *) + sgcontext->operation; + if (project_operation->deformation_mode == SCULPT_GESTURE_PROJECT_DEFORM_FAIR) { + sculpt_gesture_fairing_apply(sgcontext); + MEM_SAFE_FREE(project_operation->fairing_mask); + } + SculptSession *ss = sgcontext->ss; Sculpt *sd = CTX_data_tool_settings(C)->sculpt; if (ss->deform_modifiers_active || ss->shapekey_active) { @@ -1567,8 +1857,7 @@ static void sculpt_gesture_project_end(bContext *C, SculptGestureContext *sgcont SCULPT_flush_update_done(C, sgcontext->vc.obact, SCULPT_UPDATE_COORDS); } -static void sculpt_gesture_init_project_properties(SculptGestureContext *sgcontext, - wmOperator * /*op*/) +static void sculpt_gesture_init_project_properties(SculptGestureContext *sgcontext, wmOperator *op) { sgcontext->operation = reinterpret_cast( MEM_cnew(__func__)); @@ -1576,12 +1865,32 @@ static void sculpt_gesture_init_project_properties(SculptGestureContext *sgconte SculptGestureProjectOperation *project_operation = (SculptGestureProjectOperation *) sgcontext->operation; + if (sgcontext->shape_type == SCULPT_GESTURE_SHAPE_LINE) { + project_operation->deformation_mode = (eSculptProjectDeformationMode)RNA_enum_get( + op->ptr, "deformation_mode"); + } + else { + /* All gesture shapes that are not a line need to be deformed by fairing as they can't be + * projected to a plane. */ + project_operation->deformation_mode = SCULPT_GESTURE_PROJECT_DEFORM_FAIR; + } + project_operation->operation.sculpt_gesture_begin = sculpt_gesture_project_begin; project_operation->operation.sculpt_gesture_apply_for_symmetry_pass = sculpt_gesture_project_apply_for_symmetry_pass; project_operation->operation.sculpt_gesture_end = sculpt_gesture_project_end; } +static void sculpt_project_gesture_operator_properties(wmOperatorType *ot) +{ + RNA_def_enum(ot->srna, + "deformation_mode", + prop_project_deformation_mode_types, + SCULPT_GESTURE_PROJECT_DEFORM_PROJECT, + "Deformation mode", + NULL); +} + static int paint_mask_gesture_box_exec(bContext *C, wmOperator *op) { SculptGestureContext *sgcontext = sculpt_gesture_init_from_box(C, op); @@ -1646,8 +1955,8 @@ static int sculpt_trim_gesture_box_exec(bContext *C, wmOperator *op) { Object *object = CTX_data_active_object(C); SculptSession *ss = object->sculpt; - if (BKE_pbvh_type(ss->pbvh) != PBVH_FACES) { - /* Not supported in Multires and Dyntopo. */ + if (BKE_pbvh_type(ss->pbvh) == PBVH_GRIDS) { + /* Not supported in Multires. */ return OPERATOR_CANCELLED; } @@ -1675,7 +1984,7 @@ static int sculpt_trim_gesture_box_invoke(bContext *C, wmOperator *op, const wmE SculptCursorGeometryInfo sgi; const float mval_fl[2] = {float(event->mval[0]), float(event->mval[1])}; SCULPT_vertex_random_access_ensure(ss); - ss->gesture_initial_hit = SCULPT_cursor_geometry_info_update(C, &sgi, mval_fl, false); + ss->gesture_initial_hit = SCULPT_cursor_geometry_info_update(C, &sgi, mval_fl, false, false); if (ss->gesture_initial_hit) { copy_v3_v3(ss->gesture_initial_location, sgi.location); copy_v3_v3(ss->gesture_initial_normal, sgi.normal); @@ -1688,7 +1997,8 @@ static int sculpt_trim_gesture_lasso_exec(bContext *C, wmOperator *op) { Object *object = CTX_data_active_object(C); SculptSession *ss = object->sculpt; - if (BKE_pbvh_type(ss->pbvh) != PBVH_FACES) { + + if (BKE_pbvh_type(ss->pbvh) == PBVH_GRIDS) { /* Not supported in Multires and Dyntopo. */ return OPERATOR_CANCELLED; } @@ -1716,9 +2026,10 @@ static int sculpt_trim_gesture_lasso_invoke(bContext *C, wmOperator *op, const w SculptCursorGeometryInfo sgi; const float mval_fl[2] = {float(event->mval[0]), float(event->mval[1])}; SCULPT_vertex_random_access_ensure(ss); - ss->gesture_initial_hit = SCULPT_cursor_geometry_info_update(C, &sgi, mval_fl, false); + ss->gesture_initial_hit = SCULPT_cursor_geometry_info_update(C, &sgi, mval_fl, false, true); if (ss->gesture_initial_hit) { copy_v3_v3(ss->gesture_initial_location, sgi.location); + copy_v3_v3(ss->gesture_initial_back_location, sgi.back_location); copy_v3_v3(ss->gesture_initial_normal, sgi.normal); } @@ -1737,6 +2048,44 @@ static int project_gesture_line_exec(bContext *C, wmOperator *op) return OPERATOR_FINISHED; } +static int project_gesture_lasso_exec(bContext *C, wmOperator *op) +{ + Object *ob = CTX_data_active_object(C); + SculptSession *ss = ob->sculpt; + if (BKE_pbvh_type(ss->pbvh) == PBVH_GRIDS) { + /* Fairing operations are not supported in Multires. */ + return OPERATOR_CANCELLED; + } + + SculptGestureContext *sgcontext = sculpt_gesture_init_from_lasso(C, op); + if (!sgcontext) { + return OPERATOR_CANCELLED; + } + sculpt_gesture_init_project_properties(sgcontext, op); + sculpt_gesture_apply(C, sgcontext, op); + sculpt_gesture_context_free(sgcontext); + return OPERATOR_FINISHED; +} + +static int project_gesture_box_exec(bContext *C, wmOperator *op) +{ + Object *ob = CTX_data_active_object(C); + SculptSession *ss = ob->sculpt; + if (BKE_pbvh_type(ss->pbvh) == PBVH_GRIDS) { + /* Fairing operations are not supported in Multires. */ + return OPERATOR_CANCELLED; + } + + SculptGestureContext *sgcontext = sculpt_gesture_init_from_box(C, op); + if (!sgcontext) { + return OPERATOR_CANCELLED; + } + sculpt_gesture_init_project_properties(sgcontext, op); + sculpt_gesture_apply(C, sgcontext, op); + sculpt_gesture_context_free(sgcontext); + return OPERATOR_FINISHED; +} + void PAINT_OT_mask_lasso_gesture(wmOperatorType *ot) { ot->name = "Mask Lasso Gesture"; @@ -1897,4 +2246,48 @@ void SCULPT_OT_project_line_gesture(wmOperatorType *ot) /* Properties. */ WM_operator_properties_gesture_straightline(ot, WM_CURSOR_EDIT); sculpt_gesture_operator_properties(ot); + + sculpt_project_gesture_operator_properties(ot); +} + +void SCULPT_OT_project_lasso_gesture(wmOperatorType *ot) +{ + ot->name = "Project Lasso Gesture"; + ot->idname = "SCULPT_OT_project_lasso_gesture"; + ot->description = "Project by fairing the geometry to the curve defined by the lasso gesture"; + + ot->invoke = WM_gesture_lasso_invoke; + ot->modal = WM_gesture_lasso_modal; + ot->exec = project_gesture_lasso_exec; + + ot->poll = SCULPT_mode_poll; + + ot->flag = OPTYPE_REGISTER; + + /* Properties. */ + WM_operator_properties_gesture_lasso(ot); + sculpt_gesture_operator_properties(ot); + + sculpt_project_gesture_operator_properties(ot); +} + +void SCULPT_OT_project_box_gesture(wmOperatorType *ot) +{ + ot->name = "Project Box Gesture"; + ot->idname = "SCULPT_OT_project_box_gesture"; + ot->description = "Project by fairing the geometry to the box defined by the gesture"; + + ot->invoke = WM_gesture_box_invoke; + ot->modal = WM_gesture_box_modal; + ot->exec = project_gesture_box_exec; + + ot->poll = SCULPT_mode_poll; + + ot->flag = OPTYPE_REGISTER; + + /* Properties. */ + WM_operator_properties_border(ot); + sculpt_gesture_operator_properties(ot); + + sculpt_project_gesture_operator_properties(ot); } diff --git a/source/blender/editors/sculpt_paint/paint_stroke.cc b/source/blender/editors/sculpt_paint/paint_stroke.cc index 1665fc4fedb..3c829947cba 100644 --- a/source/blender/editors/sculpt_paint/paint_stroke.cc +++ b/source/blender/editors/sculpt_paint/paint_stroke.cc @@ -440,7 +440,7 @@ static bool paint_brush_update(bContext *C, } /* curve strokes do their own rake calculation */ else if (!(brush->flag & BRUSH_CURVE)) { - if (!paint_calculate_rake_rotation(ups, brush, mouse_init)) { + if (!paint_calculate_rake_rotation(ups, brush, mouse, mouse_init)) { /* Not enough motion to define an angle. */ if (!stroke->rake_started) { is_dry_run = true; @@ -1556,7 +1556,7 @@ int paint_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event, PaintS (br->mask_mtex.brush_angle_mode & MTEX_ANGLE_RAKE)) { copy_v2_v2(stroke->ups->last_rake, stroke->last_mouse_position); } - paint_calculate_rake_rotation(stroke->ups, br, mouse); + paint_calculate_rake_rotation(stroke->ups, br, mouse, mouse); } } else if (first_modal || diff --git a/source/blender/editors/sculpt_paint/paint_utils.c b/source/blender/editors/sculpt_paint/paint_utils.c index 98fefd86d95..f4955b820b9 100644 --- a/source/blender/editors/sculpt_paint/paint_utils.c +++ b/source/blender/editors/sculpt_paint/paint_utils.c @@ -8,6 +8,8 @@ #include #include +#include "MEM_guardedalloc.h" + #include "DNA_material_types.h" #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" @@ -25,6 +27,7 @@ #include "BLT_translation.h" #include "BKE_brush.h" +#include "BKE_colortools.h" #include "BKE_context.h" #include "BKE_customdata.h" #include "BKE_image.h" @@ -40,6 +43,7 @@ #include "RNA_access.h" #include "RNA_define.h" +#include "RNA_path.h" #include "RNA_prototypes.h" #include "GPU_framebuffer.h" @@ -187,7 +191,7 @@ void paint_get_tex_pixel_col(const MTex *mtex, clamp_v4(rgba, 0.0f, 1.0f); } -void paint_stroke_operator_properties(wmOperatorType *ot) +void paint_stroke_operator_properties(wmOperatorType *ot, bool mode_skip_save) { static const EnumPropertyItem stroke_mode_items[] = { {BRUSH_STROKE_NORMAL, "NORMAL", 0, "Regular", "Apply brush normally"}, @@ -209,12 +213,18 @@ void paint_stroke_operator_properties(wmOperatorType *ot) prop = RNA_def_collection_runtime(ot->srna, "stroke", &RNA_OperatorStrokeElement, "Stroke", ""); RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); - RNA_def_enum(ot->srna, - "mode", - stroke_mode_items, - BRUSH_STROKE_NORMAL, - "Stroke Mode", - "Action taken when a paint stroke is made"); + prop = RNA_def_enum(ot->srna, + "mode", + stroke_mode_items, + BRUSH_STROKE_NORMAL, + "Stroke Mode", + "Action taken when a paint stroke is made"); + + if (mode_skip_save) { + /* probably a good idea to enable this for all paint modes, since otherwise + keymaps can do weird things if a user forgets to explicitly set this prop - joeedh */ + RNA_def_property_flag(prop, PROP_SKIP_SAVE); + } } /* 3D Paint */ @@ -402,6 +412,8 @@ void paint_sample_color( SpaceImage *sima = CTX_wm_space_image(C); const View3D *v3d = CTX_wm_view3d(C); + ePaintMode mode = BKE_paintmode_get_active_from_context(C); + if (v3d && texpaint_proj) { /* first try getting a color directly from the mesh faces if possible */ ViewLayer *view_layer = CTX_data_view_layer(C); @@ -610,6 +622,129 @@ void BRUSH_OT_curve_preset(wmOperatorType *ot) BLT_I18NCONTEXT_ID_CURVE_LEGACY); /* Abusing id_curve :/ */ } +/** + * Attempt to retrieve the rna pointer/property from an rna path. + * + * \return 0 for failure, 1 for success, and also 1 if property is not set. + */ +static int curvemapping_preset_get_path( + PointerRNA *ctx_ptr, wmOperator *op, const char *name, PointerRNA *r_ptr, PropertyRNA **r_prop) +{ + PropertyRNA *unused_prop; + + /* get an rna string path from the operator's properties */ + char *str; + if (!(str = RNA_string_get_alloc(op->ptr, name, NULL, 0, NULL))) { + return 1; + } + + if (str[0] == '\0') { + if (r_prop) { + *r_prop = NULL; + } + MEM_freeN(str); + return 1; + } + + if (!r_prop) { + r_prop = &unused_prop; + } + + /* get rna from path */ + if (!RNA_path_resolve(ctx_ptr, str, r_ptr, r_prop)) { + MEM_freeN(str); + + BKE_reportf(op->reports, RPT_ERROR, "Could not resolve path '%s'", name); + return 0; + } + + if (*r_prop) { + PropertyType prop_type = RNA_property_type(*r_prop); + if (!*r_prop || prop_type != PROP_POINTER) { + MEM_freeN(str); + BKE_reportf(op->reports, RPT_ERROR, "Property from path '%s' is not a curve", name); + return 0; + } + + if (RNA_property_pointer_type(r_ptr, *r_prop) != &RNA_CurveMapping) { + MEM_freeN(str); + BKE_reportf(op->reports, RPT_ERROR, "Property from path '%s' is not a curve", name); + return 0; + } + } + else if (r_ptr->type != &RNA_CurveMapping) { + MEM_freeN(str); + BKE_reportf(op->reports, RPT_ERROR, "Property from path '%s' is not a curve", name); + return 0; + } + + /* success */ + MEM_freeN(str); + return 1; +} + +static int curvemapping_preset_exec(bContext *C, wmOperator *op) +{ + PointerRNA ctx_ptr; + RNA_pointer_create(NULL, &RNA_Context, C, &ctx_ptr); + + PointerRNA ptr; + PropertyRNA *prop; + + if (!curvemapping_preset_get_path(&ctx_ptr, op, "path", &ptr, &prop)) { + return OPERATOR_CANCELLED; + } + + CurveMapping *cumap = prop ? RNA_property_pointer_get(&ptr, prop).data : ptr.data; + int preset = RNA_enum_get(op->ptr, "shape"); + + cumap->flag &= ~CUMA_EXTEND_EXTRAPOLATE; + cumap->preset = preset; + + CurveMap *cuma = cumap->cm; + + int slope = RNA_boolean_get(op->ptr, "invert") ? CURVEMAP_SLOPE_NEGATIVE : + CURVEMAP_SLOPE_POSITIVE; + + BKE_curvemap_reset(cuma, &cumap->clipr, cumap->preset, slope); + BKE_curvemapping_changed(cumap, false); + + return OPERATOR_FINISHED; +} + +static bool curvemapping_preset_poll(bContext *C) +{ + return true; +} + +void BRUSH_OT_curve_preset_load(wmOperatorType *ot) +{ + PropertyRNA *prop; + static const EnumPropertyItem prop_shape_items[] = { + {CURVE_PRESET_SHARP, "SHARP", 0, "Sharp", ""}, + {CURVE_PRESET_SMOOTH, "SMOOTH", 0, "Smooth", ""}, + {CURVE_PRESET_MAX, "MAX", 0, "Max", ""}, + {CURVE_PRESET_LINE, "LINE", 0, "Line", ""}, + {CURVE_PRESET_ROUND, "ROUND", 0, "Round", ""}, + {CURVE_PRESET_ROOT, "ROOT", 0, "Root", ""}, + {0, NULL, 0, NULL, NULL}, + }; + + ot->name = "Preset"; + ot->description = "Load Curve Preset"; + ot->idname = "BRUSH_OT_curve_preset_load"; + + ot->exec = curvemapping_preset_exec; + ot->poll = curvemapping_preset_poll; + + prop = RNA_def_enum(ot->srna, "shape", prop_shape_items, CURVE_PRESET_SMOOTH, "Mode", ""); + RNA_def_property_translation_context(prop, + BLT_I18NCONTEXT_ID_CURVE_LEGACY); /* Abusing id_curve :/ */ + + prop = RNA_def_string(ot->srna, "path", NULL, 256, "rna path", "RNA path to curve mapping"); + prop = RNA_def_boolean(ot->srna, "invert", false, "invert", "Invert curve"); +} + /* face-select ops */ static int paint_select_linked_exec(bContext *C, wmOperator *UNUSED(op)) { diff --git a/source/blender/editors/sculpt_paint/paint_vertex.cc b/source/blender/editors/sculpt_paint/paint_vertex.cc index e2ca932fbc7..f0a32471896 100644 --- a/source/blender/editors/sculpt_paint/paint_vertex.cc +++ b/source/blender/editors/sculpt_paint/paint_vertex.cc @@ -40,6 +40,8 @@ #include "BKE_lib_id.h" #include "BKE_mesh.h" #include "BKE_mesh_mapping.h" +#include "BKE_mesh_types.h" +#include "BKE_modifier.h" #include "BKE_object.h" #include "BKE_object_deform.h" #include "BKE_paint.h" @@ -1177,7 +1179,7 @@ static void vertex_paint_init_session(Depsgraph *depsgraph, BKE_sculpt_toolsettings_data_ensure(scene); BLI_assert(ob->sculpt == nullptr); - ob->sculpt = (SculptSession *)MEM_callocN(sizeof(SculptSession), "sculpt session"); + BKE_object_sculpt_data_create(ob); ob->sculpt->mode_type = object_mode; BKE_sculpt_update_object_for_edit(depsgraph, ob, true, false, true); @@ -1250,18 +1252,24 @@ static void vertex_paint_init_session_data(const ToolSettings *ts, Object *ob) gmap->vert_to_poly = nullptr; BKE_mesh_vert_loop_map_create(&gmap->vert_to_loop, &gmap->vert_map_mem, - polys.data(), - loops.data(), + reinterpret_cast(me->vert_positions().data()), + me->medge, + me->mpoly, + me->mloop, me->totvert, me->totpoly, - me->totloop); + me->totloop, + false); BKE_mesh_vert_poly_map_create(&gmap->vert_to_poly, &gmap->poly_map_mem, - polys.data(), - loops.data(), + BKE_mesh_vert_positions(me), + me->medge, + me->mpoly, + me->mloop, me->totvert, me->totpoly, - me->totloop); + me->totloop, + false); } /* Create average brush arrays */ @@ -1337,7 +1345,7 @@ static void ed_vwpaintmode_enter_generic( /* Create vertex/weight paint mode session data */ if (ob->sculpt) { if (ob->sculpt->cache) { - SCULPT_cache_free(ob->sculpt->cache); + SCULPT_cache_free(ob->sculpt, ob, ob->sculpt->cache); ob->sculpt->cache = nullptr; } BKE_sculptsession_free(ob); @@ -1406,7 +1414,7 @@ static void ed_vwpaintmode_exit_generic(Object *ob, const eObjectMode mode_flag) /* If the cache is not released by a cancel or a done, free it now. */ if (ob->sculpt && ob->sculpt->cache) { - SCULPT_cache_free(ob->sculpt->cache); + SCULPT_cache_free(ob->sculpt, ob, ob->sculpt->cache); ob->sculpt->cache = nullptr; } @@ -1645,7 +1653,8 @@ static void vwpaint_update_cache_invariants( } copy_v2_v2(cache->mouse, cache->initial_mouse); - const Brush *brush = vp->paint.brush; + Brush *brush = vp->paint.brush; + /* Truly temporary data that isn't stored in properties */ cache->vc = vc; cache->brush = brush; @@ -1956,8 +1965,7 @@ static void do_wpaint_brush_blur_task_cb_ex(void *__restrict userdata, const bool use_vert_sel = (data->me->editflag & ME_EDIT_PAINT_VERT_SEL) != 0; SculptBrushTest test; - SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( - ss, &test, data->brush->falloff_shape); + SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init(ss, &test); const float *sculpt_normal_frontface = SCULPT_brush_frontface_normal_from_falloff_shape( ss, data->brush->falloff_shape); @@ -2058,8 +2066,7 @@ static void do_wpaint_brush_smear_task_cb_ex(void *__restrict userdata, if (cache->is_last_valid && (normalize_v3(brush_dir) != 0.0f)) { SculptBrushTest test; - SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( - ss, &test, data->brush->falloff_shape); + SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init(ss, &test); const float *sculpt_normal_frontface = SCULPT_brush_frontface_normal_from_falloff_shape( ss, data->brush->falloff_shape); @@ -2163,8 +2170,7 @@ static void do_wpaint_brush_draw_task_cb_ex(void *__restrict userdata, const bool use_vert_sel = (data->me->editflag & ME_EDIT_PAINT_VERT_SEL) != 0; SculptBrushTest test; - SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( - ss, &test, data->brush->falloff_shape); + SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init(ss, &test); const float *sculpt_normal_frontface = SCULPT_brush_frontface_normal_from_falloff_shape( ss, data->brush->falloff_shape); @@ -2233,8 +2239,7 @@ static void do_wpaint_brush_calc_average_weight_cb_ex(void *__restrict userdata, accum->value = 0.0; SculptBrushTest test; - SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( - ss, &test, data->brush->falloff_shape); + SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init(ss, &test); const float *sculpt_normal_frontface = SCULPT_brush_frontface_normal_from_falloff_shape( ss, data->brush->falloff_shape); @@ -2639,7 +2644,7 @@ static void wpaint_stroke_done(const bContext *C, PaintStroke *stroke) WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob); - SCULPT_cache_free(ob->sculpt->cache); + SCULPT_cache_free(ob->sculpt, ob, ob->sculpt->cache); ob->sculpt->cache = nullptr; } @@ -2690,7 +2695,7 @@ static void wpaint_cancel(bContext *C, wmOperator *op) { Object *ob = CTX_data_active_object(C); if (ob->sculpt->cache) { - SCULPT_cache_free(ob->sculpt->cache); + SCULPT_cache_free(ob->sculpt, ob, ob->sculpt->cache); ob->sculpt->cache = nullptr; } @@ -2719,7 +2724,7 @@ void PAINT_OT_weight_paint(wmOperatorType *ot) /* flags */ ot->flag = OPTYPE_UNDO | OPTYPE_BLOCKING; - paint_stroke_operator_properties(ot); + paint_stroke_operator_properties(ot, false); } /** \} */ @@ -2993,8 +2998,7 @@ static void do_vpaint_brush_blur_loops(bContext *C, const bool use_face_sel = (me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0; SculptBrushTest test; - SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( - ss, &test, brush->falloff_shape); + SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init(ss, &test); const float *sculpt_normal_frontface = SCULPT_brush_frontface_normal_from_falloff_shape( ss, brush->falloff_shape); @@ -3138,8 +3142,7 @@ static void do_vpaint_brush_blur_verts(bContext *C, const bool use_face_sel = (me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0; SculptBrushTest test; - SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( - ss, &test, brush->falloff_shape); + SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init(ss, &test); const float *sculpt_normal_frontface = SCULPT_brush_frontface_normal_from_falloff_shape( ss, brush->falloff_shape); @@ -3292,8 +3295,7 @@ static void do_vpaint_brush_smear(bContext *C, if (cache->is_last_valid && (normalize_v3(brush_dir) != 0.0f)) { SculptBrushTest test; - SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( - ss, &test, brush->falloff_shape); + SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init(ss, &test); const float *sculpt_normal_frontface = SCULPT_brush_frontface_normal_from_falloff_shape( ss, brush->falloff_shape); @@ -3460,8 +3462,7 @@ static void calculate_average_color(VPaintData *vpd, memset(accum2->value, 0, sizeof(accum2->value)); SculptBrushTest test; - SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( - ss, &test, brush->falloff_shape); + SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init(ss, &test); /* For each vertex */ PBVHVertexIter vd; @@ -3577,8 +3578,7 @@ static void vpaint_do_draw(bContext *C, const bool use_face_sel = (me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0; SculptBrushTest test; - SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( - ss, &test, brush->falloff_shape); + SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init(ss, &test); const float *sculpt_normal_frontface = SCULPT_brush_frontface_normal_from_falloff_shape( ss, brush->falloff_shape); @@ -3984,7 +3984,7 @@ static void vpaint_stroke_done(const bContext *C, PaintStroke *stroke) SCULPT_undo_push_end(ob); - SCULPT_cache_free(ob->sculpt->cache); + SCULPT_cache_free(ob->sculpt, ob, ob->sculpt->cache); ob->sculpt->cache = nullptr; } @@ -4044,7 +4044,7 @@ static void vpaint_cancel(bContext *C, wmOperator *op) { Object *ob = CTX_data_active_object(C); if (ob->sculpt->cache) { - SCULPT_cache_free(ob->sculpt->cache); + SCULPT_cache_free(ob->sculpt, ob, ob->sculpt->cache); ob->sculpt->cache = nullptr; } @@ -4073,7 +4073,7 @@ void PAINT_OT_vertex_paint(wmOperatorType *ot) /* flags */ ot->flag = OPTYPE_UNDO | OPTYPE_BLOCKING; - paint_stroke_operator_properties(ot); + paint_stroke_operator_properties(ot, false); } /** \} */ diff --git a/source/blender/editors/sculpt_paint/sculpt.cc b/source/blender/editors/sculpt_paint/sculpt.cc index 10ce391c397..0177e8b5e4b 100644 --- a/source/blender/editors/sculpt_paint/sculpt.cc +++ b/source/blender/editors/sculpt_paint/sculpt.cc @@ -12,10 +12,12 @@ #include "MEM_guardedalloc.h" +#include "BLI_alloca.h" #include "BLI_blenlib.h" #include "BLI_dial_2d.h" #include "BLI_ghash.h" #include "BLI_gsqueue.h" +#include "BLI_index_range.hh" #include "BLI_math.h" #include "BLI_set.hh" #include "BLI_task.h" @@ -76,6 +78,7 @@ #include "bmesh.h" using blender::float3; +using blender::IndexRange; using blender::MutableSpan; using blender::Set; using blender::Vector; @@ -92,13 +95,108 @@ using blender::Vector; void SCULPT_vertex_random_access_ensure(SculptSession *ss) { - if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) { - BM_mesh_elem_index_ensure(ss->bm, BM_VERT); - BM_mesh_elem_table_ensure(ss->bm, BM_VERT); + if (ss->bm) { + ss->totfaces = ss->totpoly = ss->bm->totface; + ss->totvert = ss->bm->totvert; + + BM_mesh_elem_index_ensure(ss->bm, BM_VERT | BM_EDGE | BM_FACE); + BM_mesh_elem_table_ensure(ss->bm, BM_VERT | BM_EDGE | BM_FACE); } } -int SCULPT_vertex_count_get(SculptSession *ss) +void SCULPT_face_normal_get(SculptSession *ss, PBVHFaceRef face, float no[3]) +{ + switch (BKE_pbvh_type(ss->pbvh)) { + case PBVH_BMESH: { + BMFace *f = (BMFace *)face.i; + + copy_v3_v3(no, f->no); + break; + } + + case PBVH_FACES: + case PBVH_GRIDS: { + const MPoly *mp = ss->mpoly + face.i; + BKE_mesh_calc_poly_normal(mp, ss->mloop + mp->loopstart, ss->vert_positions, no); + break; + } + default: // failed + zero_v3(no); + break; + } +} + +/* Sculpt PBVH abstraction API + * + * This is read-only, for writing use PBVH vertex iterators. There vd.index matches + * the indices used here. + * + * For multi-resolution, the same vertex in multiple grids is counted multiple times, with + * different index for each grid. */ + +void SCULPT_face_random_access_ensure(SculptSession *ss) +{ + if (ss->bm) { + ss->totfaces = ss->totpoly = ss->bm->totface; + ss->totvert = ss->bm->totvert; + + BM_mesh_elem_index_ensure(ss->bm, BM_FACE); + BM_mesh_elem_table_ensure(ss->bm, BM_FACE); + } +} + +MSculptVert *SCULPT_vertex_get_sculptvert(const SculptSession *ss, PBVHVertRef vertex) +{ + switch (BKE_pbvh_type(ss->pbvh)) { + case PBVH_BMESH: { + BMVert *v = (BMVert *)vertex.i; + return BKE_PBVH_SCULPTVERT(ss->cd_sculpt_vert, v); + } + + case PBVH_GRIDS: + case PBVH_FACES: { + return ss->msculptverts + vertex.i; + } + } + + return nullptr; +} + +float *SCULPT_vertex_origco_get(SculptSession *ss, PBVHVertRef vertex) +{ + switch (BKE_pbvh_type(ss->pbvh)) { + case PBVH_BMESH: { + BMVert *v = (BMVert *)vertex.i; + return BKE_PBVH_SCULPTVERT(ss->cd_sculpt_vert, v)->origco; + } + + case PBVH_GRIDS: + case PBVH_FACES: { + return ss->msculptverts[vertex.i].origco; + } + } + + return nullptr; +} + +float *SCULPT_vertex_origno_get(SculptSession *ss, PBVHVertRef vertex) +{ + switch (BKE_pbvh_type(ss->pbvh)) { + case PBVH_BMESH: { + BMVert *v = (BMVert *)vertex.i; + return BKE_PBVH_SCULPTVERT(ss->cd_sculpt_vert, v)->origno; + } + + case PBVH_GRIDS: + case PBVH_FACES: { + return ss->msculptverts[vertex.i].origno; + } + } + + return nullptr; +} + +int SCULPT_vertex_count_get(const SculptSession *ss) { switch (BKE_pbvh_type(ss->pbvh)) { case PBVH_FACES: @@ -192,10 +290,25 @@ void SCULPT_vertex_normal_get(SculptSession *ss, PBVHVertRef vertex, float no[3] } } +bool SCULPT_has_persistent_base(SculptSession *ss) +{ + if (ss->bm) { + return CustomData_get_named_layer_index( + &ss->bm->vdata, CD_PROP_FLOAT3, SCULPT_ATTRIBUTE_NAME(persistent_co)) != -1; + } + else if (ss->vdata) { + return CustomData_get_named_layer_index( + ss->vdata, CD_PROP_FLOAT3, SCULPT_ATTRIBUTE_NAME(persistent_co)) != -1; + } + + /* Detect multires. */ + return ss->attrs.persistent_co; +} + const float *SCULPT_vertex_persistent_co_get(SculptSession *ss, PBVHVertRef vertex) { if (ss->attrs.persistent_co) { - return (const float *)SCULPT_vertex_attr_get(vertex, ss->attrs.persistent_co); + return SCULPT_vertex_attr_get(vertex, ss->attrs.persistent_co); } return SCULPT_vertex_co_get(ss, vertex); @@ -220,30 +333,35 @@ const float *SCULPT_vertex_co_for_grab_active_get(SculptSession *ss, PBVHVertRef void SCULPT_vertex_limit_surface_get(SculptSession *ss, PBVHVertRef vertex, float r_co[3]) { - switch (BKE_pbvh_type(ss->pbvh)) { - case PBVH_FACES: - case PBVH_BMESH: - copy_v3_v3(r_co, SCULPT_vertex_co_get(ss, vertex)); - break; - case PBVH_GRIDS: { - const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh); - const int grid_index = vertex.i / key->grid_area; - const int vertex_index = vertex.i - grid_index * key->grid_area; - - SubdivCCGCoord coord{}; - coord.grid_index = grid_index; - coord.x = vertex_index % key->grid_size; - coord.y = vertex_index / key->grid_size; - BKE_subdiv_ccg_eval_limit_point(ss->subdiv_ccg, &coord, r_co); - break; + if (BKE_pbvh_type(ss->pbvh) != PBVH_GRIDS) { + if (ss->attrs.limit_surface) { + float *f = SCULPT_vertex_attr_get(vertex, ss->attrs.limit_surface); + copy_v3_v3(r_co, f); } + else { + copy_v3_v3(r_co, SCULPT_vertex_co_get(ss, vertex)); + } + + return; } + + const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh); + const int grid_index = vertex.i / key->grid_area; + const int vertex_index = vertex.i - grid_index * key->grid_area; + + SubdivCCGCoord coord{}; + + coord.grid_index = grid_index; + coord.x = vertex_index % key->grid_size; + coord.y = vertex_index / key->grid_size; + + BKE_subdiv_ccg_eval_limit_point(ss->subdiv_ccg, &coord, r_co); } void SCULPT_vertex_persistent_normal_get(SculptSession *ss, PBVHVertRef vertex, float no[3]) { if (ss->attrs.persistent_no) { - copy_v3_v3(no, (float *)SCULPT_vertex_attr_get(vertex, ss->attrs.persistent_no)); + copy_v3_v3(no, SCULPT_vertex_attr_get(vertex, ss->attrs.persistent_no)); return; } SCULPT_vertex_normal_get(ss, vertex, no); @@ -335,12 +453,16 @@ ePaintSymmetryFlags SCULPT_mesh_symmetry_xyz_get(Object *object) int SCULPT_active_face_set_get(SculptSession *ss) { + if (ss->active_face.i == PBVH_REF_NONE) { + return SCULPT_FACE_SET_NONE; + } + switch (BKE_pbvh_type(ss->pbvh)) { case PBVH_FACES: if (!ss->face_sets) { return SCULPT_FACE_SET_NONE; } - return ss->face_sets[ss->active_face_index]; + return ss->face_sets[ss->active_face.i]; case PBVH_GRIDS: { if (!ss->face_sets) { return SCULPT_FACE_SET_NONE; @@ -350,6 +472,11 @@ int SCULPT_active_face_set_get(SculptSession *ss) return ss->face_sets[face_index]; } case PBVH_BMESH: + if (ss->cd_faceset_offset && ss->active_face.i) { + BMFace *f = (BMFace *)ss->active_face.i; + return BM_ELEM_CD_GET_INT(f, ss->cd_faceset_offset); + } + return SCULPT_FACE_SET_NONE; } return SCULPT_FACE_SET_NONE; @@ -363,11 +490,9 @@ void SCULPT_vertex_visible_set(SculptSession *ss, PBVHVertRef vertex, bool visib hide_vert[vertex.i] = visible; break; } - case PBVH_BMESH: { - BMVert *v = (BMVert *)vertex.i; - BM_elem_flag_set(v, BM_ELEM_HIDDEN, !visible); + case PBVH_BMESH: + BM_elem_flag_set((BMVert *)vertex.i, BM_ELEM_HIDDEN, !visible); break; - } case PBVH_GRIDS: break; } @@ -381,11 +506,12 @@ bool SCULPT_vertex_visible_get(SculptSession *ss, PBVHVertRef vertex) return hide_vert == nullptr || !hide_vert[vertex.i]; } case PBVH_BMESH: - return !BM_elem_flag_test((BMVert *)vertex.i, BM_ELEM_HIDDEN); + return !BM_elem_flag_test(((BMVert *)vertex.i), BM_ELEM_HIDDEN); case PBVH_GRIDS: { const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh); const int grid_index = vertex.i / key->grid_area; const int vertex_index = vertex.i - grid_index * key->grid_area; + BLI_bitmap **grid_hidden = BKE_pbvh_get_grid_visibility(ss->pbvh); if (grid_hidden && grid_hidden[grid_index]) { return !BLI_BITMAP_TEST(grid_hidden[grid_index], vertex_index); @@ -409,8 +535,32 @@ void SCULPT_face_set_visibility_set(SculptSession *ss, int face_set, bool visibl ss->hide_poly[i] = !visible; } break; - case PBVH_BMESH: + case PBVH_BMESH: { + BMIter iter; + BMFace *f; + + if (!ss->attrs.hide_poly) { + return; + } + + int cd_hide_poly = ss->attrs.hide_poly->bmesh_cd_offset; + + BM_ITER_MESH (f, &iter, ss->bm, BM_FACES_OF_MESH) { + int fset = BM_ELEM_CD_GET_INT(f, ss->cd_faceset_offset); + int node = BM_ELEM_CD_GET_INT(f, ss->cd_face_node_offset); + + if (fset != face_set) { + continue; + } + + BM_ELEM_CD_SET_BOOL(f, cd_hide_poly, !visible); + + if (node != DYNTOPO_NODE_NONE) { + BKE_pbvh_vert_tag_update_normal_triangulation(BKE_pbvh_node_from_index(ss->pbvh, node)); + } + } break; + } } } @@ -430,9 +580,13 @@ void SCULPT_face_visibility_all_invert(SculptSession *ss) case PBVH_BMESH: { BMIter iter; BMFace *f; + int cd_hide_poly = ss->attrs.hide_poly->bmesh_cd_offset; BM_ITER_MESH (f, &iter, ss->bm, BM_FACES_OF_MESH) { - BM_elem_flag_toggle(f, BM_ELEM_HIDDEN); + int state = BM_ELEM_CD_GET_BOOL(f, cd_hide_poly); + state ^= true; + + BM_ELEM_CD_SET_BOOL(f, cd_hide_poly, state); } break; } @@ -441,23 +595,27 @@ void SCULPT_face_visibility_all_invert(SculptSession *ss) void SCULPT_face_visibility_all_set(SculptSession *ss, bool visible) { + if (visible && !ss->attrs.hide_poly) { + /* This case is allowed. */ + return; + } SCULPT_topology_islands_invalidate(ss); switch (BKE_pbvh_type(ss->pbvh)) { case PBVH_FACES: case PBVH_GRIDS: + /* Note: no need to use the generic loop in PBVH_BMESH, just memset ss->hide_poly. */ + BLI_assert(ss->hide_poly != nullptr); + memset(ss->hide_poly, !visible, sizeof(bool) * ss->totfaces); break; - case PBVH_BMESH: { - BMIter iter; - BMFace *f; + case PBVH_BMESH: + for (int i = 0; i < ss->totfaces; i++) { + PBVHFaceRef face = BKE_pbvh_index_to_face(ss->pbvh, i); - BM_ITER_MESH (f, &iter, ss->bm, BM_FACES_OF_MESH) { - BM_elem_flag_set(f, BM_ELEM_HIDDEN, !visible); + *BKE_sculpt_face_attr_get(face, ss->attrs.hide_poly) = !visible; } - break; - } } } @@ -468,16 +626,29 @@ bool SCULPT_vertex_any_face_visible_get(SculptSession *ss, PBVHVertRef vertex) if (!ss->hide_poly) { return true; } - const MeshElemMap *vert_map = &ss->pmap[vertex.i]; - for (int j = 0; j < ss->pmap[vertex.i].count; j++) { + const MeshElemMap *vert_map = &ss->pmap->pmap[vertex.i]; + for (int j = 0; j < ss->pmap->pmap[vertex.i].count; j++) { if (!ss->hide_poly[vert_map->indices[j]]) { return true; } } return false; } - case PBVH_BMESH: - return true; + case PBVH_BMESH: { + BMIter iter; + BMLoop *l; + BMVert *v = (BMVert *)vertex.i; + + int cd_hide_poly = ss->attrs.hide_poly->bmesh_cd_offset; + + BM_ITER_ELEM (l, &iter, v, BM_LOOPS_OF_VERT) { + if (!BM_ELEM_CD_GET_BOOL(l->f, cd_hide_poly)) { + return true; + } + } + + return false; + } case PBVH_GRIDS: return true; } @@ -491,7 +662,7 @@ bool SCULPT_vertex_all_faces_visible_get(const SculptSession *ss, PBVHVertRef ve if (!ss->hide_poly) { return true; } - const MeshElemMap *vert_map = &ss->pmap[vertex.i]; + const MeshElemMap *vert_map = &ss->pmap->pmap[vertex.i]; for (int j = 0; j < vert_map->count; j++) { if (ss->hide_poly[vert_map->indices[j]]) { return false; @@ -502,6 +673,7 @@ bool SCULPT_vertex_all_faces_visible_get(const SculptSession *ss, PBVHVertRef ve case PBVH_BMESH: { BMVert *v = (BMVert *)vertex.i; BMEdge *e = v->e; + int cd_hide_poly = ss->attrs.hide_poly->bmesh_cd_offset; if (!e) { return true; @@ -515,7 +687,7 @@ bool SCULPT_vertex_all_faces_visible_get(const SculptSession *ss, PBVHVertRef ve } do { - if (BM_elem_flag_test(l->f, BM_ELEM_HIDDEN)) { + if (BM_ELEM_CD_GET_BOOL(l->f, cd_hide_poly)) { return false; } } while ((l = l->radial_next) != e->l); @@ -538,22 +710,51 @@ bool SCULPT_vertex_all_faces_visible_get(const SculptSession *ss, PBVHVertRef ve void SCULPT_vertex_face_set_set(SculptSession *ss, PBVHVertRef vertex, int face_set) { + BKE_sculpt_boundary_flag_update(ss, vertex); + switch (BKE_pbvh_type(ss->pbvh)) { case PBVH_FACES: { BLI_assert(ss->face_sets != nullptr); - const MeshElemMap *vert_map = &ss->pmap[vertex.i]; + const MeshElemMap *vert_map = &ss->pmap->pmap[vertex.i]; for (int j = 0; j < vert_map->count; j++) { const int poly_index = vert_map->indices[j]; if (ss->hide_poly && ss->hide_poly[poly_index]) { /* Skip hidden faces connected to the vertex. */ continue; } + + const MPoly *mp = ss->mpoly + poly_index; + const MLoop *ml = ss->mloop + mp->loopstart; + + for (int k = 0; k < mp->totloop; k++, ml++) { + PBVHVertRef vertex2 = {ml->v}; + BKE_sculpt_boundary_flag_update(ss, vertex2); + } + ss->face_sets[poly_index] = face_set; } + break; } - case PBVH_BMESH: + case PBVH_BMESH: { + BMIter iter; + BMLoop *l; + BMVert *v = (BMVert *)vertex.i; + + int cd_hide_poly = ss->attrs.hide_poly->bmesh_cd_offset; + + BM_ITER_ELEM (l, &iter, v, BM_LOOPS_OF_VERT) { + int fset = BM_ELEM_CD_GET_INT(l->f, ss->cd_faceset_offset); + if (!BM_ELEM_CD_GET_BOOL(l->f, cd_hide_poly) && fset != face_set) { + BM_ELEM_CD_SET_INT(l->f, ss->cd_faceset_offset, abs(face_set)); + } + + PBVHVertRef vertex2 = {(intptr_t)l->v}; + BKE_sculpt_boundary_flag_update(ss, vertex2); + } + break; + } case PBVH_GRIDS: { BLI_assert(ss->face_sets != nullptr); const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh); @@ -569,6 +770,49 @@ void SCULPT_vertex_face_set_set(SculptSession *ss, PBVHVertRef vertex, int face_ } } +void SCULPT_vertex_face_set_increase(SculptSession *ss, PBVHVertRef vertex, const int increase) +{ + switch (BKE_pbvh_type(ss->pbvh)) { + case PBVH_FACES: { + int index = (int)vertex.i; + MeshElemMap *vert_map = &ss->pmap->pmap[index]; + for (int j = 0; j < ss->pmap->pmap[index].count; j++) { + if (ss->face_sets[vert_map->indices[j]] > 0) { + ss->face_sets[vert_map->indices[j]] += increase; + } + } + } break; + case PBVH_BMESH: { + BMVert *v = (BMVert *)vertex.i; + BMIter iter; + BMFace *f; + + BM_ITER_ELEM (f, &iter, v, BM_FACES_OF_VERT) { + int fset = BM_ELEM_CD_GET_INT(f, ss->cd_faceset_offset); + + if (fset <= 0) { + continue; + } + + fset += increase; + + BM_ELEM_CD_SET_INT(f, ss->cd_faceset_offset, fset); + } + break; + } + case PBVH_GRIDS: { + int index = (int)vertex.i; + const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh); + const int grid_index = index / key->grid_area; + const int face_index = BKE_subdiv_ccg_grid_to_face_index(ss->subdiv_ccg, grid_index); + if (ss->face_sets[face_index] > 0) { + ss->face_sets[face_index] += increase; + } + + } break; + } +} + int SCULPT_vertex_face_set_get(SculptSession *ss, PBVHVertRef vertex) { switch (BKE_pbvh_type(ss->pbvh)) { @@ -576,7 +820,7 @@ int SCULPT_vertex_face_set_get(SculptSession *ss, PBVHVertRef vertex) if (!ss->face_sets) { return SCULPT_FACE_SET_NONE; } - const MeshElemMap *vert_map = &ss->pmap[vertex.i]; + const MeshElemMap *vert_map = &ss->pmap->pmap[vertex.i]; int face_set = 0; for (int i = 0; i < vert_map->count; i++) { if (ss->face_sets[vert_map->indices[i]] > face_set) { @@ -585,8 +829,23 @@ int SCULPT_vertex_face_set_get(SculptSession *ss, PBVHVertRef vertex) } return face_set; } - case PBVH_BMESH: - return 0; + case PBVH_BMESH: { + BMIter iter; + BMLoop *l; + BMVert *v = (BMVert *)vertex.i; + int ret = -1; + + BM_ITER_ELEM (l, &iter, v, BM_LOOPS_OF_VERT) { + int fset = BM_ELEM_CD_GET_INT(l->f, ss->cd_faceset_offset); + fset = abs(fset); + + if (fset > ret) { + ret = fset; + } + } + + return ret; + } case PBVH_GRIDS: { if (!ss->face_sets) { return SCULPT_FACE_SET_NONE; @@ -607,7 +866,7 @@ bool SCULPT_vertex_has_face_set(SculptSession *ss, PBVHVertRef vertex, int face_ if (!ss->face_sets) { return face_set == SCULPT_FACE_SET_NONE; } - const MeshElemMap *vert_map = &ss->pmap[vertex.i]; + const MeshElemMap *vert_map = &ss->pmap->pmap[vertex.i]; for (int i = 0; i < vert_map->count; i++) { if (ss->face_sets[vert_map->indices[i]] == face_set) { return true; @@ -615,8 +874,38 @@ bool SCULPT_vertex_has_face_set(SculptSession *ss, PBVHVertRef vertex, int face_ } return false; } - case PBVH_BMESH: - return true; + case PBVH_BMESH: { + BMEdge *e; + BMVert *v = (BMVert *)vertex.i; + + if (ss->cd_faceset_offset == -1) { + return false; + } + + e = v->e; + + if (UNLIKELY(!e)) { + return false; + } + + do { + BMLoop *l = e->l; + + if (UNLIKELY(!l)) { + continue; + } + + do { + BMFace *f = l->f; + + if (abs(BM_ELEM_CD_GET_INT(f, ss->cd_faceset_offset)) == abs(face_set)) { + return true; + } + } while ((l = l->radial_next) != e->l); + } while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e); + + return false; + } case PBVH_GRIDS: { if (!ss->face_sets) { return face_set == SCULPT_FACE_SET_NONE; @@ -656,6 +945,22 @@ void SCULPT_visibility_sync_all_from_faces(Object *ob) BMIter iter; BMFace *f; + if (!ss->attrs.hide_poly) { + BM_ITER_MESH (f, &iter, ss->bm, BM_FACES_OF_MESH) { + BM_elem_flag_disable(f, BM_ELEM_HIDDEN); + + BMLoop *l = f->l_first; + do { + BM_elem_flag_disable(l->v, BM_ELEM_HIDDEN); + BM_elem_flag_disable(l->e, BM_ELEM_HIDDEN); + } while ((l = l->next) != f->l_first); + } + + return; + } + + int cd_hide_poly = ss->attrs.hide_poly->bmesh_cd_offset; + /* Hide all verts and edges attached to faces. */ BM_ITER_MESH (f, &iter, ss->bm, BM_FACES_OF_MESH) { BMLoop *l = f->l_first; @@ -667,10 +972,13 @@ void SCULPT_visibility_sync_all_from_faces(Object *ob) /* Unhide verts and edges attached to visible faces. */ BM_ITER_MESH (f, &iter, ss->bm, BM_FACES_OF_MESH) { - if (BM_elem_flag_test(f, BM_ELEM_HIDDEN)) { + if (BM_ELEM_CD_GET_BOOL(f, cd_hide_poly)) { + BM_elem_flag_enable(f, BM_ELEM_HIDDEN); continue; } + BM_elem_flag_disable(f, BM_ELEM_HIDDEN); + BMLoop *l = f->l_first; do { BM_elem_flag_disable(l->v, BM_ELEM_HIDDEN); @@ -682,12 +990,12 @@ void SCULPT_visibility_sync_all_from_faces(Object *ob) } } -static bool sculpt_check_unique_face_set_in_base_mesh(SculptSession *ss, int index) +static bool sculpt_check_unique_face_set_in_base_mesh(const SculptSession *ss, int index) { if (!ss->face_sets) { return true; } - const MeshElemMap *vert_map = &ss->pmap[index]; + const MeshElemMap *vert_map = &ss->pmap->pmap[index]; int face_set = -1; for (int i = 0; i < vert_map->count; i++) { if (face_set == -1) { @@ -708,7 +1016,7 @@ static bool sculpt_check_unique_face_set_in_base_mesh(SculptSession *ss, int ind */ static bool sculpt_check_unique_face_set_for_edge_in_base_mesh(SculptSession *ss, int v1, int v2) { - const MeshElemMap *vert_map = &ss->pmap[v1]; + const MeshElemMap *vert_map = &ss->pmap->pmap[v1]; int p1 = -1, p2 = -1; for (int i = 0; i < vert_map->count; i++) { const MPoly *p = &ss->mpoly[vert_map->indices[i]]; @@ -734,43 +1042,17 @@ static bool sculpt_check_unique_face_set_for_edge_in_base_mesh(SculptSession *ss return true; } -bool SCULPT_vertex_has_unique_face_set(SculptSession *ss, PBVHVertRef vertex) +bool SCULPT_vertex_has_unique_face_set(const SculptSession *ss, PBVHVertRef vertex) { - switch (BKE_pbvh_type(ss->pbvh)) { - case PBVH_FACES: { - return sculpt_check_unique_face_set_in_base_mesh(ss, vertex.i); - } - case PBVH_BMESH: - return true; - case PBVH_GRIDS: { - if (!ss->face_sets) { - return true; - } - const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh); - const int grid_index = vertex.i / key->grid_area; - const int vertex_index = vertex.i - grid_index * key->grid_area; - SubdivCCGCoord coord{}; - coord.grid_index = grid_index; - coord.x = vertex_index % key->grid_size; - coord.y = vertex_index / key->grid_size; - int v1, v2; - const SubdivCCGAdjacencyType adjacency = BKE_subdiv_ccg_coarse_mesh_adjacency_info_get( - ss->subdiv_ccg, &coord, ss->mloop, ss->mpoly, &v1, &v2); - switch (adjacency) { - case SUBDIV_CCG_ADJACENT_VERTEX: - return sculpt_check_unique_face_set_in_base_mesh(ss, v1); - case SUBDIV_CCG_ADJACENT_EDGE: - return sculpt_check_unique_face_set_for_edge_in_base_mesh(ss, v1, v2); - case SUBDIV_CCG_ADJACENT_NONE: - return true; - } - } - } - return false; + return !SCULPT_vertex_is_boundary(ss, vertex, SCULPT_BOUNDARY_FACE_SET); } int SCULPT_face_set_next_available_get(SculptSession *ss) { + if (ss->cd_faceset_offset == -1) { + return 0; + } + switch (BKE_pbvh_type(ss->pbvh)) { case PBVH_FACES: case PBVH_GRIDS: { @@ -786,22 +1068,37 @@ int SCULPT_face_set_next_available_get(SculptSession *ss) next_face_set++; return next_face_set; } - case PBVH_BMESH: - return 0; + case PBVH_BMESH: { + int next_face_set = 0; + BMIter iter; + BMFace *f; + if (!ss->cd_faceset_offset) { + return 0; + } + + BM_ITER_MESH (f, &iter, ss->bm, BM_FACES_OF_MESH) { + int fset = BM_ELEM_CD_GET_INT(f, ss->cd_faceset_offset); + if (fset > next_face_set) { + next_face_set = fset; + } + } + + next_face_set++; + return next_face_set; + } } return 0; } /* Sculpt Neighbor Iterators */ -#define SCULPT_VERTEX_NEIGHBOR_FIXED_CAPACITY 256 - static void sculpt_vertex_neighbor_add(SculptVertexNeighborIter *iter, PBVHVertRef neighbor, + PBVHEdgeRef edge, int neighbor_index) { for (int i = 0; i < iter->size; i++) { - if (iter->neighbors[i].i == neighbor.i) { + if (iter->neighbors[i].vertex.i == neighbor.i) { return; } } @@ -810,103 +1107,260 @@ static void sculpt_vertex_neighbor_add(SculptVertexNeighborIter *iter, iter->capacity += SCULPT_VERTEX_NEIGHBOR_FIXED_CAPACITY; if (iter->neighbors == iter->neighbors_fixed) { - iter->neighbors = static_cast( - MEM_mallocN(iter->capacity * sizeof(PBVHVertRef), "neighbor array")); - memcpy(iter->neighbors, iter->neighbors_fixed, sizeof(PBVHVertRef) * iter->size); - } - else { - iter->neighbors = static_cast(MEM_reallocN_id( - iter->neighbors, iter->capacity * sizeof(PBVHVertRef), "neighbor array")); - } + iter->neighbors = (struct _SculptNeighborRef *)MEM_mallocN( + iter->capacity * sizeof(*iter->neighbors), "neighbor array"); + iter->neighbor_indices = (int *)MEM_mallocN(iter->capacity * sizeof(*iter->neighbor_indices), + "neighbor array"); - if (iter->neighbor_indices == iter->neighbor_indices_fixed) { - iter->neighbor_indices = static_cast( - MEM_mallocN(iter->capacity * sizeof(int), "neighbor array")); - memcpy(iter->neighbor_indices, iter->neighbor_indices_fixed, sizeof(int) * iter->size); + memcpy((void *)iter->neighbors, + (void *)iter->neighbors_fixed, + sizeof(*iter->neighbors) * iter->size); + memcpy((void *)iter->neighbor_indices, + (void *)iter->neighbor_indices_fixed, + sizeof(*iter->neighbor_indices) * iter->size); } else { - iter->neighbor_indices = static_cast( - MEM_reallocN_id(iter->neighbor_indices, iter->capacity * sizeof(int), "neighbor array")); + iter->neighbors = (struct _SculptNeighborRef *)MEM_reallocN_id( + iter->neighbors, iter->capacity * sizeof(*iter->neighbors), "neighbor array"); + iter->neighbor_indices = (int *)MEM_reallocN_id(iter->neighbor_indices, + iter->capacity * + sizeof(*iter->neighbor_indices), + "neighbor array"); } } - iter->neighbors[iter->size] = neighbor; + iter->neighbors[iter->size].vertex = neighbor; + iter->neighbors[iter->size].edge = edge; iter->neighbor_indices[iter->size] = neighbor_index; iter->size++; } -static void sculpt_vertex_neighbors_get_bmesh(PBVHVertRef vertex, SculptVertexNeighborIter *iter) +static void sculpt_vertex_neighbor_add_nocheck(SculptVertexNeighborIter *iter, + PBVHVertRef neighbor, + PBVHEdgeRef edge, + int neighbor_index) { - BMVert *v = (BMVert *)vertex.i; - BMIter liter; - BMLoop *l; + if (iter->size >= iter->capacity) { + iter->capacity += SCULPT_VERTEX_NEIGHBOR_FIXED_CAPACITY; + + if (iter->neighbors == iter->neighbors_fixed) { + iter->neighbors = (struct _SculptNeighborRef *)MEM_mallocN( + iter->capacity * sizeof(*iter->neighbors), "neighbor array"); + iter->neighbor_indices = (int *)MEM_mallocN(iter->capacity * sizeof(*iter->neighbor_indices), + "neighbor array"); + + memcpy(iter->neighbors, iter->neighbors_fixed, sizeof(*iter->neighbors) * iter->size); + + memcpy(iter->neighbor_indices, + iter->neighbor_indices_fixed, + sizeof(*iter->neighbor_indices) * iter->size); + } + else { + iter->neighbors = (struct _SculptNeighborRef *)MEM_reallocN_id( + iter->neighbors, iter->capacity * sizeof(*iter->neighbors), "neighbor array"); + iter->neighbor_indices = (int *)MEM_reallocN_id(iter->neighbor_indices, + iter->capacity * + sizeof(*iter->neighbor_indices), + "neighbor array"); + } + } + + iter->neighbors[iter->size].vertex = neighbor; + iter->neighbors[iter->size].edge = edge; + + iter->neighbor_indices[iter->size] = neighbor_index; + iter->size++; +} + +static void sculpt_vertex_neighbors_get_bmesh(const SculptSession *ss, + PBVHVertRef index, + SculptVertexNeighborIter *iter) +{ + BMVert *v = (BMVert *)index.i; + + iter->is_duplicate = false; + iter->size = 0; iter->num_duplicates = 0; + iter->has_edge = true; iter->capacity = SCULPT_VERTEX_NEIGHBOR_FIXED_CAPACITY; iter->neighbors = iter->neighbors_fixed; iter->neighbor_indices = iter->neighbor_indices_fixed; + iter->i = 0; + iter->no_free = false; - BM_ITER_ELEM (l, &liter, v, BM_LOOPS_OF_VERT) { - const BMVert *adj_v[2] = {l->prev->v, l->next->v}; - for (int i = 0; i < ARRAY_SIZE(adj_v); i++) { - const BMVert *v_other = adj_v[i]; - if (v_other != v) { - sculpt_vertex_neighbor_add( - iter, BKE_pbvh_make_vref(intptr_t(v_other)), BM_elem_index_get(v_other)); - } + // cache profiling revealed a hotspot here, don't use BM_ITER + BMEdge *e = v->e; + + if (!v->e) { + return; + } + + BMEdge *e2 = nullptr; + + do { + BMVert *v2; + e2 = BM_DISK_EDGE_NEXT(e, v); + v2 = v == e->v1 ? e->v2 : e->v1; + + MSculptVert *mv = BKE_PBVH_SCULPTVERT(ss->cd_sculpt_vert, v2); + + if (!(mv->flag & SCULPTVERT_VERT_FSET_HIDDEN)) { // && (e->head.hflag & BM_ELEM_DRAW)) { + sculpt_vertex_neighbor_add_nocheck(iter, + BKE_pbvh_make_vref((intptr_t)v2), + BKE_pbvh_make_eref((intptr_t)e), + BM_elem_index_get(v2)); + } + } while ((e = e2) != v->e); + + if (ss->fake_neighbors.use_fake_neighbors) { + int index = BM_elem_index_get(v); + + BLI_assert(ss->fake_neighbors.fake_neighbor_index != nullptr); + if (ss->fake_neighbors.fake_neighbor_index[index].i != FAKE_NEIGHBOR_NONE) { + sculpt_vertex_neighbor_add(iter, + ss->fake_neighbors.fake_neighbor_index[index], + BKE_pbvh_make_eref(PBVH_REF_NONE), + ss->fake_neighbors.fake_neighbor_index[index].i); } } } -static void sculpt_vertex_neighbors_get_faces(SculptSession *ss, +static void sculpt_vertex_neighbors_get_faces(const SculptSession *ss, PBVHVertRef vertex, SculptVertexNeighborIter *iter) { - const MeshElemMap *vert_map = &ss->pmap[vertex.i]; iter->size = 0; iter->num_duplicates = 0; iter->capacity = SCULPT_VERTEX_NEIGHBOR_FIXED_CAPACITY; iter->neighbors = iter->neighbors_fixed; iter->neighbor_indices = iter->neighbor_indices_fixed; + iter->is_duplicate = false; + iter->has_edge = true; + iter->no_free = false; + int *edges = (int *)BLI_array_alloca(edges, SCULPT_VERTEX_NEIGHBOR_FIXED_CAPACITY); + int *unused_polys = (int *)BLI_array_alloca(unused_polys, + SCULPT_VERTEX_NEIGHBOR_FIXED_CAPACITY * 2); + bool heap_alloc = false; + int len = SCULPT_VERTEX_NEIGHBOR_FIXED_CAPACITY; + + BKE_pbvh_pmap_to_edges(ss->pbvh, vertex, &edges, &len, &heap_alloc, &unused_polys); + /* length of array is now in len */ + + for (int i = 0; i < len; i++) { + const MEdge *e = ss->medge + edges[i]; + int v2 = e->v1 == vertex.i ? e->v2 : e->v1; + + sculpt_vertex_neighbor_add(iter, BKE_pbvh_make_vref(v2), BKE_pbvh_make_eref(edges[i]), v2); + } + + if (heap_alloc) { + MEM_freeN(unused_polys); + MEM_freeN(edges); + } +#if 0 for (int i = 0; i < vert_map->count; i++) { if (ss->hide_poly && ss->hide_poly[vert_map->indices[i]]) { /* Skip connectivity from hidden faces. */ continue; } const MPoly *p = &ss->mpoly[vert_map->indices[i]]; - int f_adj_v[2]; - if (poly_get_adj_loops_from_vert(p, ss->mloop, vertex.i, f_adj_v) != -1) { - for (int j = 0; j < ARRAY_SIZE(f_adj_v); j += 1) { - if (f_adj_v[j] != vertex.i) { - sculpt_vertex_neighbor_add(iter, BKE_pbvh_make_vref(f_adj_v[j]), f_adj_v[j]); + uint f_adj_v[2]; + + MLoop *l = &ss->mloop[p->loopstart]; + int e1, e2; + + bool ok = false; + + for (int j = 0; j < p->totloop; j++, l++) { + if (l->v == index) { + f_adj_v[0] = ME_POLY_LOOP_PREV(ss->mloop, p, j)->v; + f_adj_v[1] = ME_POLY_LOOP_NEXT(ss->mloop, p, j)->v; + + e1 = ME_POLY_LOOP_PREV(ss->mloop, p, j)->e; + e2 = l->e; + ok = true; + break; + } + } + + if (ok) { + for (int j = 0; j < 2; j += 1) { + if (f_adj_v[j] != index) { + sculpt_vertex_neighbor_add( + iter, BKE_pbvh_make_vref(f_adj_v[j]), BKE_pbvh_make_eref(j ? e2 : e1), f_adj_v[j]); } } } } +#endif if (ss->fake_neighbors.use_fake_neighbors) { BLI_assert(ss->fake_neighbors.fake_neighbor_index != nullptr); - if (ss->fake_neighbors.fake_neighbor_index[vertex.i] != FAKE_NEIGHBOR_NONE) { - sculpt_vertex_neighbor_add( - iter, - BKE_pbvh_make_vref(ss->fake_neighbors.fake_neighbor_index[vertex.i]), - ss->fake_neighbors.fake_neighbor_index[vertex.i]); + if (ss->fake_neighbors.fake_neighbor_index[vertex.i].i != FAKE_NEIGHBOR_NONE) { + sculpt_vertex_neighbor_add(iter, + ss->fake_neighbors.fake_neighbor_index[vertex.i], + BKE_pbvh_make_eref(PBVH_REF_NONE), + vertex.i); } } } -static void sculpt_vertex_neighbors_get_grids(SculptSession *ss, +static void sculpt_vertex_neighbors_get_faces_vemap(const SculptSession *ss, + PBVHVertRef vertex, + SculptVertexNeighborIter *iter) +{ + int index = BKE_pbvh_vertex_to_index(ss->pbvh, vertex); + + MeshElemMap *vert_map = &ss->vemap[index]; + iter->size = 0; + iter->num_duplicates = 0; + iter->capacity = SCULPT_VERTEX_NEIGHBOR_FIXED_CAPACITY; + iter->neighbors = iter->neighbors_fixed; + iter->neighbor_indices = iter->neighbor_indices_fixed; + iter->is_duplicate = false; + iter->no_free = false; + + for (int i = 0; i < vert_map->count; i++) { + const MEdge *me = &ss->medge[vert_map->indices[i]]; + + unsigned int v = me->v1 == (unsigned int)vertex.i ? me->v2 : me->v1; + MSculptVert *mv = ss->msculptverts + v; + + if (mv->flag & SCULPTVERT_VERT_FSET_HIDDEN) { + /* Skip connectivity from hidden faces. */ + continue; + } + + sculpt_vertex_neighbor_add( + iter, BKE_pbvh_make_vref(v), BKE_pbvh_make_eref(vert_map->indices[i]), v); + } + + if (ss->fake_neighbors.use_fake_neighbors) { + BLI_assert(ss->fake_neighbors.fake_neighbor_index != nullptr); + if (ss->fake_neighbors.fake_neighbor_index[index].i != FAKE_NEIGHBOR_NONE) { + sculpt_vertex_neighbor_add(iter, + ss->fake_neighbors.fake_neighbor_index[index], + BKE_pbvh_make_eref(PBVH_REF_NONE), + ss->fake_neighbors.fake_neighbor_index[index].i); + } + } +} + +static void sculpt_vertex_neighbors_get_grids(const SculptSession *ss, const PBVHVertRef vertex, const bool include_duplicates, SculptVertexNeighborIter *iter) { + int index = (int)vertex.i; + /* TODO: optimize this. We could fill #SculptVertexNeighborIter directly, * maybe provide coordinate and mask pointers directly rather than converting * back and forth between #CCGElem and global index. */ const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh); - const int grid_index = vertex.i / key->grid_area; - const int vertex_index = vertex.i - grid_index * key->grid_area; + const int grid_index = index / key->grid_area; + const int vertex_index = index - grid_index * key->grid_area; SubdivCCGCoord coord{}; coord.grid_index = grid_index; @@ -916,24 +1370,30 @@ static void sculpt_vertex_neighbors_get_grids(SculptSession *ss, SubdivCCGNeighbors neighbors; BKE_subdiv_ccg_neighbor_coords_get(ss->subdiv_ccg, &coord, include_duplicates, &neighbors); + iter->is_duplicate = include_duplicates; + iter->size = 0; iter->num_duplicates = neighbors.num_duplicates; iter->capacity = SCULPT_VERTEX_NEIGHBOR_FIXED_CAPACITY; iter->neighbors = iter->neighbors_fixed; iter->neighbor_indices = iter->neighbor_indices_fixed; + iter->no_free = false; for (int i = 0; i < neighbors.size; i++) { - int v = neighbors.coords[i].grid_index * key->grid_area + - neighbors.coords[i].y * key->grid_size + neighbors.coords[i].x; + int idx = neighbors.coords[i].grid_index * key->grid_area + + neighbors.coords[i].y * key->grid_size + neighbors.coords[i].x; - sculpt_vertex_neighbor_add(iter, BKE_pbvh_make_vref(v), v); + sculpt_vertex_neighbor_add( + iter, BKE_pbvh_make_vref(idx), BKE_pbvh_make_eref(PBVH_REF_NONE), idx); } if (ss->fake_neighbors.use_fake_neighbors) { BLI_assert(ss->fake_neighbors.fake_neighbor_index != nullptr); - if (ss->fake_neighbors.fake_neighbor_index[vertex.i] != FAKE_NEIGHBOR_NONE) { - int v = ss->fake_neighbors.fake_neighbor_index[vertex.i]; - sculpt_vertex_neighbor_add(iter, BKE_pbvh_make_vref(v), v); + if (ss->fake_neighbors.fake_neighbor_index[index].i != FAKE_NEIGHBOR_NONE) { + sculpt_vertex_neighbor_add(iter, + ss->fake_neighbors.fake_neighbor_index[index], + BKE_pbvh_make_eref(PBVH_REF_NONE), + ss->fake_neighbors.fake_neighbor_index[index].i); } } @@ -942,17 +1402,26 @@ static void sculpt_vertex_neighbors_get_grids(SculptSession *ss, } } -void SCULPT_vertex_neighbors_get(SculptSession *ss, +void SCULPT_vertex_neighbors_get(const SculptSession *ss, const PBVHVertRef vertex, const bool include_duplicates, SculptVertexNeighborIter *iter) { + iter->no_free = false; + switch (BKE_pbvh_type(ss->pbvh)) { case PBVH_FACES: - sculpt_vertex_neighbors_get_faces(ss, vertex, iter); + /* use vemap if it exists, so result is in disk cycle order */ + if (ss->vemap) { + BKE_pbvh_set_vemap(ss->pbvh, ss->vemap); + sculpt_vertex_neighbors_get_faces_vemap(ss, vertex, iter); + } + else { + sculpt_vertex_neighbors_get_faces(ss, vertex, iter); + } return; case PBVH_BMESH: - sculpt_vertex_neighbors_get_bmesh(vertex, iter); + sculpt_vertex_neighbors_get_bmesh(ss, vertex, iter); return; case PBVH_GRIDS: sculpt_vertex_neighbors_get_grids(ss, vertex, include_duplicates, iter); @@ -1186,10 +1655,12 @@ void SCULPT_floodfill_add_initial(SculptFloodFill *flood, PBVHVertRef vertex) BLI_gsqueue_push(flood->queue, &vertex); } -void SCULPT_floodfill_add_and_skip_initial(SculptFloodFill *flood, PBVHVertRef vertex) +void SCULPT_floodfill_add_and_skip_initial(SculptSession *ss, + SculptFloodFill *flood, + PBVHVertRef vertex) { BLI_gsqueue_push(flood->queue, &vertex); - BLI_BITMAP_ENABLE(flood->visited_verts, vertex.i); + BLI_BITMAP_ENABLE(flood->visited_verts, BKE_pbvh_vertex_to_index(ss->pbvh, vertex)); } void SCULPT_floodfill_add_initial_with_symmetry(Sculpt *sd, @@ -1319,7 +1790,8 @@ static bool sculpt_tool_needs_original(const char sculpt_tool) SCULPT_TOOL_ELASTIC_DEFORM, SCULPT_TOOL_SMOOTH, SCULPT_TOOL_BOUNDARY, - SCULPT_TOOL_POSE); + SCULPT_TOOL_POSE, + SCULPT_TOOL_PAINT); } static bool sculpt_tool_is_proxy_used(const char sculpt_tool) @@ -1413,27 +1885,27 @@ void SCULPT_orig_vert_data_init(SculptOrigVertData *data, SCULPT_orig_vert_data_unode_init(data, ob, unode); } -void SCULPT_orig_vert_data_update(SculptOrigVertData *orig_data, PBVHVertexIter *iter) +/** + * DEPRECATED use Update a #SculptOrigVertData for a particular vertex from the PBVH iterator. + */ +void SCULPT_orig_vert_data_update(SculptOrigVertData *orig_data, PBVHVertRef vertex) { - if (orig_data->unode->type == SCULPT_UNDO_COORDS) { - if (orig_data->bm_log) { - BM_log_original_vert_data(orig_data->bm_log, iter->bm_vert, &orig_data->co, &orig_data->no); - } - else { - orig_data->co = orig_data->coords[iter->i]; - orig_data->no = orig_data->normals[iter->i]; - } + // check if we need to update original data for current stroke + MSculptVert *mv = SCULPT_vertex_get_sculptvert(orig_data->ss, vertex); + + SCULPT_vertex_check_origdata(orig_data->ss, vertex); + + if (orig_data->datatype == SCULPT_UNDO_COORDS) { + float *no = mv->origno; + + orig_data->no = no; + orig_data->co = mv->origco; } - else if (orig_data->unode->type == SCULPT_UNDO_COLOR) { - orig_data->col = orig_data->colors[iter->i]; + else if (orig_data->datatype == SCULPT_UNDO_COLOR) { + orig_data->col = mv->origcolor; } - else if (orig_data->unode->type == SCULPT_UNDO_MASK) { - if (orig_data->bm_log) { - orig_data->mask = BM_log_original_mask(orig_data->bm_log, iter->bm_vert); - } - else { - orig_data->mask = orig_data->vmasks[iter->i]; - } + else if (orig_data->datatype == SCULPT_UNDO_MASK) { + orig_data->mask = (float)mv->origmask / 65535.0f; } } @@ -1510,25 +1982,29 @@ static void paint_mesh_restore_co_task_cb(void *__restrict userdata, SculptThreadedTaskData *data = static_cast(userdata); SculptSession *ss = data->ob->sculpt; - SculptUndoNode *unode; - SculptUndoType type; + SculptUndoType type = (SculptUndoType)0; - switch (data->brush->sculpt_tool) { + switch (SCULPT_get_tool(ss, data->brush)) { case SCULPT_TOOL_MASK: - type = SCULPT_UNDO_MASK; + type |= SCULPT_UNDO_MASK; break; case SCULPT_TOOL_PAINT: case SCULPT_TOOL_SMEAR: - type = SCULPT_UNDO_COLOR; + type |= SCULPT_UNDO_COLOR; + break; + case SCULPT_TOOL_VCOL_BOUNDARY: + type |= SCULPT_UNDO_COLOR | SCULPT_UNDO_COORDS; break; case SCULPT_TOOL_DRAW_FACE_SETS: type = ss->cache->alt_smooth ? SCULPT_UNDO_COORDS : SCULPT_UNDO_FACE_SETS; break; default: - type = SCULPT_UNDO_COORDS; + type |= SCULPT_UNDO_COORDS; break; } + SculptUndoNode *unode; + if (ss->bm) { unode = SCULPT_undo_push_node(data->ob, data->nodes[n], type); } @@ -1558,17 +2034,13 @@ static void paint_mesh_restore_co_task_cb(void *__restrict userdata, } PBVHVertexIter vd; - SculptOrigVertData orig_vert_data; - SculptOrigFaceData orig_face_data; - if (type != SCULPT_UNDO_FACE_SETS) { - SCULPT_orig_vert_data_unode_init(&orig_vert_data, data->ob, unode); - } - else { - SCULPT_orig_face_data_unode_init(&orig_face_data, data->ob, unode); - } + bool modified = false; if (unode->type == SCULPT_UNDO_FACE_SETS) { + SculptOrigFaceData orig_face_data; + SCULPT_orig_face_data_unode_init(&orig_face_data, data->ob, unode); + PBVHFaceIter fd; BKE_pbvh_face_iter_begin (ss->pbvh, data->nodes[n], fd) { SCULPT_orig_face_data_update(&orig_face_data, &fd); @@ -1583,28 +2055,54 @@ static void paint_mesh_restore_co_task_cb(void *__restrict userdata, } BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { - SCULPT_orig_vert_data_update(&orig_vert_data, &vd); + SCULPT_vertex_check_origdata(ss, vd.vertex); + MSculptVert *mv = SCULPT_vertex_get_sculptvert(ss, vd.vertex); + + if (type & SCULPT_UNDO_COORDS) { + if (len_squared_v3v3(vd.co, mv->origco) > FLT_EPSILON) { + modified = true; + } + + copy_v3_v3(vd.co, mv->origco); - if (orig_vert_data.unode->type == SCULPT_UNDO_COORDS) { - copy_v3_v3(vd.co, orig_vert_data.co); if (vd.no) { - copy_v3_v3(vd.no, orig_vert_data.no); + copy_v3_v3(vd.no, mv->origno); } else { - copy_v3_v3(vd.fno, orig_vert_data.no); + copy_v3_v3(vd.fno, mv->origno); } if (vd.is_mesh) { BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); } } - else if (orig_vert_data.unode->type == SCULPT_UNDO_MASK) { - *vd.mask = orig_vert_data.mask; + + if (type & SCULPT_UNDO_MASK) { + if ((*vd.mask - mv->origmask) * (*vd.mask - mv->origmask) > FLT_EPSILON) { + modified = true; + } + + *vd.mask = mv->origmask; } - else if (orig_vert_data.unode->type == SCULPT_UNDO_COLOR) { - SCULPT_vertex_color_set(ss, vd.vertex, orig_vert_data.col); + + if (type & SCULPT_UNDO_COLOR) { + float color[4]; + + if (SCULPT_has_colors(ss)) { + SCULPT_vertex_color_get(ss, vd.vertex, color); + + if (len_squared_v4v4(color, mv->origcolor) > FLT_EPSILON) { + modified = true; + } + + SCULPT_vertex_color_set(ss, vd.vertex, mv->origcolor); + } } } BKE_pbvh_vertex_iter_end; + + if (modified && (type & SCULPT_UNDO_COORDS)) { + BKE_pbvh_node_mark_update(data->nodes[n]); + } } static void paint_mesh_restore_co(Sculpt *sd, Object *ob) @@ -1617,11 +2115,6 @@ static void paint_mesh_restore_co(Sculpt *sd, Object *ob) BKE_pbvh_search_gather(ss->pbvh, nullptr, nullptr, &nodes, &totnode); - /** - * Disable multi-threading when dynamic-topology is enabled. Otherwise, - * new entries might be inserted by #SCULPT_undo_push_node() into the #GHash - * used internally by #BM_log_original_vert_co() by a different thread. See T33787. - */ SculptThreadedTaskData data{}; data.sd = sd; data.ob = ob; @@ -1629,11 +2122,9 @@ static void paint_mesh_restore_co(Sculpt *sd, Object *ob) data.nodes = nodes; TaskParallelSettings settings; - BKE_pbvh_parallel_range_settings(&settings, true && !ss->bm, totnode); + BKE_pbvh_parallel_range_settings(&settings, true, totnode); BLI_task_parallel_range(0, totnode, &data, paint_mesh_restore_co_task_cb, &settings); - BKE_pbvh_node_color_buffer_free(ss->pbvh); - MEM_SAFE_FREE(nodes); } @@ -1703,7 +2194,7 @@ void ED_sculpt_redraw_planes_get(float planes[4][4], ARegion *region, Object *ob /************************ Brush Testing *******************/ -void SCULPT_brush_test_init(SculptSession *ss, SculptBrushTest *test) +SculptBrushTestFn SCULPT_brush_test_init(const SculptSession *ss, SculptBrushTest *test) { RegionView3D *rv3d = ss->cache ? ss->cache->vc->rv3d : ss->rv3d; View3D *v3d = ss->cache ? ss->cache->vc->v3d : ss->v3d; @@ -1738,6 +2229,52 @@ void SCULPT_brush_test_init(SculptSession *ss, SculptBrushTest *test) else { test->clip_rv3d = nullptr; } + + test->falloff_shape = PAINT_FALLOFF_SHAPE_SPHERE; + return SCULPT_brush_test_sphere_sq; +} + +SculptBrushTestFn SCULPT_brush_test_init_ex(const SculptSession *ss, + SculptBrushTest *test, + char falloff_shape, + float tip_roundness, + float tip_scale_x) +{ + SCULPT_brush_test_init_with_falloff_shape(ss, test, falloff_shape); + + test->tip_roundness = tip_roundness; + test->tip_scale_x = tip_scale_x; + test->test_cube_z = true; + + /* XXX this is likely wrong. */ + if (ss->cache) { + copy_m4_m4(test->cube_matrix, ss->cache->brush_local_mat); + } + else { + test->cube_matrix[0][0] = test->cube_matrix[1][1] = test->cube_matrix[2][2] = + test->cube_matrix[3][3] = 1.0f; + } + + mul_v3_fl(test->cube_matrix[0], tip_scale_x); + + return SCULPT_brush_test; +} + +bool SCULPT_brush_test(SculptBrushTest *test, const float co[3]) +{ + if (test->tip_roundness >= 1.0f) { + return SCULPT_brush_test_sphere_sq(test, co); + } + + bool ret = SCULPT_brush_test_cube(test, + co, + test->cube_matrix, + test->tip_roundness, + test->falloff_shape != PAINT_FALLOFF_SHAPE_TUBE); + + test->dist *= test->dist; + + return ret; } BLI_INLINE bool sculpt_brush_test_clipping(const SculptBrushTest *test, const float co[3]) @@ -1813,7 +2350,8 @@ bool SCULPT_brush_test_circle_sq(SculptBrushTest *test, const float co[3]) bool SCULPT_brush_test_cube(SculptBrushTest *test, const float co[3], const float local[4][4], - const float roundness) + const float roundness, + bool test_z) { float side = 1.0f; float local_co[3]; @@ -1835,7 +2373,7 @@ bool SCULPT_brush_test_cube(SculptBrushTest *test, const float constant_side = hardness * side; const float falloff_side = roundness * side; - if (!(local_co[0] <= side && local_co[1] <= side && local_co[2] <= side)) { + if (!(local_co[0] <= side && local_co[1] <= side && (local_co[2] <= side || !test_z))) { /* Outside the square. */ return false; } @@ -1857,7 +2395,7 @@ bool SCULPT_brush_test_cube(SculptBrushTest *test, return true; } -SculptBrushTestFn SCULPT_brush_test_init_with_falloff_shape(SculptSession *ss, +SculptBrushTestFn SCULPT_brush_test_init_with_falloff_shape(const SculptSession *ss, SculptBrushTest *test, char falloff_shape) { @@ -1865,6 +2403,8 @@ SculptBrushTestFn SCULPT_brush_test_init_with_falloff_shape(SculptSession *ss, falloff_shape = PAINT_FALLOFF_SHAPE_SPHERE; } + test->falloff_shape = falloff_shape; + SCULPT_brush_test_init(ss, test); SculptBrushTestFn sculpt_brush_test_sq_fn; if (falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE) { @@ -1887,7 +2427,7 @@ SculptBrushTestFn SCULPT_brush_test_init_with_falloff_shape(SculptSession *ss, return sculpt_brush_test_sq_fn; } -const float *SCULPT_brush_frontface_normal_from_falloff_shape(SculptSession *ss, +const float *SCULPT_brush_frontface_normal_from_falloff_shape(const SculptSession *ss, char falloff_shape) { if (falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE) { @@ -2054,8 +2594,8 @@ static void calc_area_normal_and_center_task_cb(void *__restrict userdata, } SculptBrushTest normal_test; - SculptBrushTestFn sculpt_brush_normal_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( - ss, &normal_test, data->brush->falloff_shape); + SculptBrushTestFn sculpt_brush_normal_test_sq_fn = SCULPT_brush_test_init_ex( + ss, &normal_test, (eBrushFalloffShape)data->brush->falloff_shape, 1.0f, 1.0f); /* Update the test radius to sample the normal using the normal radius of the brush. */ if (data->brush->ob_mode == OB_MODE_SCULPT) { @@ -2066,14 +2606,14 @@ static void calc_area_normal_and_center_task_cb(void *__restrict userdata, } SculptBrushTest area_test; - SculptBrushTestFn sculpt_brush_area_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( - ss, &area_test, data->brush->falloff_shape); + SculptBrushTestFn sculpt_brush_area_test_sq_fn = SCULPT_brush_test_init_ex( + ss, &area_test, (eBrushFalloffShape)data->brush->falloff_shape, 1.0f, 1.0f); if (data->brush->ob_mode == OB_MODE_SCULPT) { float test_radius = sqrtf(area_test.radius_squared); /* Layer brush produces artifacts with normal and area radius */ /* Enable area radius control only on Scrape for now */ - if (ELEM(data->brush->sculpt_tool, SCULPT_TOOL_SCRAPE, SCULPT_TOOL_FILL) && + if (ELEM(SCULPT_get_tool(ss, data->brush), SCULPT_TOOL_SCRAPE, SCULPT_TOOL_FILL) && data->brush->area_radius_factor > 0.0f) { test_radius *= data->brush->area_radius_factor; if (ss->cache && data->brush->flag2 & BRUSH_AREA_RADIUS_PRESSURE) { @@ -2090,18 +2630,18 @@ static void calc_area_normal_and_center_task_cb(void *__restrict userdata, /* When the mesh is edited we can't rely on original coords * (original mesh may not even have verts in brush radius). */ if (use_original && data->has_bm_orco) { - float(*orco_coords)[3]; - int(*orco_tris)[3]; - int orco_tris_num; + PBVHTriBuf *tribuf = BKE_pbvh_bmesh_get_tris(ss->pbvh, data->nodes[n]); - BKE_pbvh_node_get_bm_orco_data( - data->nodes[n], &orco_tris, &orco_tris_num, &orco_coords, nullptr); + for (int i = 0; i < tribuf->tottri; i++) { + PBVHTri *tri = tribuf->tris + i; + PBVHVertRef v1 = tribuf->verts[tri->v[0]]; + PBVHVertRef v2 = tribuf->verts[tri->v[1]]; + PBVHVertRef v3 = tribuf->verts[tri->v[2]]; - for (int i = 0; i < orco_tris_num; i++) { const float *co_tri[3] = { - orco_coords[orco_tris[i][0]], - orco_coords[orco_tris[i][1]], - orco_coords[orco_tris[i][2]], + SCULPT_vertex_origco_get(ss, v1), + SCULPT_vertex_origco_get(ss, v2), + SCULPT_vertex_origco_get(ss, v3), }; float co[3]; @@ -2153,11 +2693,11 @@ static void calc_area_normal_and_center_task_cb(void *__restrict userdata, if (use_original) { if (unode->bm_entry) { - const float *temp_co; - const float *temp_no_s; - BM_log_original_vert_data(ss->bm_log, vd.bm_vert, &temp_co, &temp_no_s); - copy_v3_v3(co, temp_co); - copy_v3_v3(no_s, temp_no_s); + BMVert *v = vd.bm_vert; + MSculptVert *mv = BKE_PBVH_SCULPTVERT(vd.cd_sculpt_vert, v); + + copy_v3_v3(no_s, mv->origno); + copy_v3_v3(co, mv->origco); } else { copy_v3_v3(co, unode->co[vd.i]); @@ -2879,14 +3419,17 @@ static void calc_sculpt_normal( static void update_sculpt_normal(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) { - const Brush *brush = BKE_paint_brush(&sd->paint); StrokeCache *cache = ob->sculpt->cache; + const Brush *brush = cache->brush; // BKE_paint_brush(&sd->paint); + int tool = SCULPT_get_tool(ob->sculpt, brush); + /* Grab brush does not update the sculpt normal during a stroke. */ - const bool update_normal = - !(brush->flag & BRUSH_ORIGINAL_NORMAL) && !(brush->sculpt_tool == SCULPT_TOOL_GRAB) && - !(brush->sculpt_tool == SCULPT_TOOL_THUMB && !(brush->flag & BRUSH_ANCHORED)) && - !(brush->sculpt_tool == SCULPT_TOOL_ELASTIC_DEFORM) && - !(brush->sculpt_tool == SCULPT_TOOL_SNAKE_HOOK && cache->normal_weight > 0.0f); + const bool update_normal = !((brush->flag & BRUSH_ORIGINAL_NORMAL) && + !(tool == SCULPT_TOOL_GRAB) && + !(tool == SCULPT_TOOL_THUMB && !(brush->flag & BRUSH_ANCHORED)) && + !(tool == SCULPT_TOOL_ELASTIC_DEFORM) && + !(tool == SCULPT_TOOL_SNAKE_HOOK && cache->normal_weight > 0.0f)) || + dot_v3v3(cache->sculpt_normal, cache->sculpt_normal) == 0.0f; if (cache->mirror_symmetry_pass == 0 && cache->radial_symmetry_pass == 0 && (SCULPT_stroke_is_first_brush_step_of_symmetry_pass(cache) || update_normal)) { @@ -3054,10 +3597,9 @@ struct SculptRaycastData { bool original; PBVHVertRef active_vertex; + PBVHFaceRef active_face; float *face_normal; - int active_face_grid_index; - IsectRayPrecalc isect_precalc; }; @@ -3354,75 +3896,265 @@ void SCULPT_vertcos_to_key(Object *ob, KeyBlock *kb, const float (*vertCos)[3]) BKE_keyblock_update_from_vertcos(ob, kb, vertCos); } -/* NOTE: we do the topology update before any brush actions to avoid +static void topology_undopush_cb(PBVHNode *node, void *data) +{ + SculptSearchSphereData *sdata = (SculptSearchSphereData *)data; + + SCULPT_ensure_dyntopo_node_undo( + sdata->ob, + node, + SCULPT_get_tool(sdata->ob->sculpt, sdata->brush) == SCULPT_TOOL_MASK ? SCULPT_UNDO_MASK : + SCULPT_UNDO_COORDS, + 0); + + BKE_pbvh_node_mark_update(node); +} + +int SCULPT_get_symmetry_pass(const SculptSession *ss) +{ + int symidx = ss->cache->mirror_symmetry_pass + (ss->cache->radial_symmetry_pass * 8); + + if (symidx >= SCULPT_MAX_SYMMETRY_PASSES) { + symidx = SCULPT_MAX_SYMMETRY_PASSES - 1; + } + + return symidx; +} + +typedef struct DynTopoAutomaskState { + AutomaskingCache *cache; + SculptSession *ss; + AutomaskingCache _fixed; + bool free_automasking; +} DynTopoAutomaskState; + +static float sculpt_topology_automasking_cb(PBVHVertRef vertex, void *vdata) +{ + DynTopoAutomaskState *state = (DynTopoAutomaskState *)vdata; + + float mask = SCULPT_automasking_factor_get(state->cache, state->ss, vertex, nullptr); + float mask2 = 1.0f - SCULPT_vertex_mask_get(state->ss, vertex); + + return mask * mask2; +} + +static float sculpt_topology_automasking_mask_cb(PBVHVertRef vertex, void *vdata) +{ + DynTopoAutomaskState *state = (DynTopoAutomaskState *)vdata; + return 1.0f - SCULPT_vertex_mask_get(state->ss, vertex); +} + +static float sculpt_null_mask_cb(PBVHVertRef vertex, void *vdata) +{ + return 1.0f; +} + +bool SCULPT_dyntopo_automasking_init(const SculptSession *ss, + Sculpt *sd, + const Brush *br, + Object *ob, + DyntopoMaskCB *r_mask_cb, + void **r_mask_cb_data) +{ + if (!SCULPT_is_automasking_enabled(sd, ss, br)) { + if (CustomData_has_layer(&ss->bm->vdata, CD_PAINT_MASK)) { + DynTopoAutomaskState *state = (DynTopoAutomaskState *)MEM_callocN( + sizeof(DynTopoAutomaskState), "DynTopoAutomaskState"); + + if (!ss->cache) { + state->cache = SCULPT_automasking_cache_init(sd, br, ob); + } + else { + state->cache = ss->cache->automasking; + } + + state->ss = (SculptSession *)ss; + + *r_mask_cb_data = (void *)state; + *r_mask_cb = sculpt_topology_automasking_mask_cb; + + return true; + } + else { + *r_mask_cb_data = nullptr; + *r_mask_cb = sculpt_null_mask_cb; + return false; + } + } + + DynTopoAutomaskState *state = (DynTopoAutomaskState *)MEM_callocN(sizeof(DynTopoAutomaskState), + "DynTopoAutomaskState"); + if (!ss->cache) { + state->cache = SCULPT_automasking_cache_init(sd, br, ob); + state->free_automasking = true; + } + else { + state->cache = ss->cache->automasking; + } + + state->ss = (SculptSession *)ss; + + *r_mask_cb_data = (void *)state; + *r_mask_cb = sculpt_topology_automasking_cb; + + return true; +} + +void SCULPT_dyntopo_automasking_end(void *mask_data) +{ + MEM_SAFE_FREE(mask_data); +} + +bool SCULPT_needs_area_normal(SculptSession *ss, Sculpt *sd, Brush *brush) +{ + return brush->tip_roundness != 1.0f || brush->tip_scale_x != 1.0f; +} + +/* Note: we do the topology update before any brush actions to avoid * issues with the proxies. The size of the proxy can't change, so * topology must be updated first. */ static void sculpt_topology_update(Sculpt *sd, Object *ob, Brush *brush, - UnifiedPaintSettings * /*ups*/, + UnifiedPaintSettings * /* ups */, PaintModeSettings * /*paint_mode_settings*/) { SculptSession *ss = ob->sculpt; - int n, totnode; + /* build brush radius scale */ + float radius_scale = 1.0f; + /* Build a list of all nodes that are potentially within the brush's area of influence. */ - const bool use_original = sculpt_tool_needs_original(brush->sculpt_tool) ? true : - ss->cache->original; - const float radius_scale = 1.25f; - PBVHNode **nodes = sculpt_pbvh_gather_generic( - ob, sd, brush, use_original, radius_scale, &totnode); + const bool use_original = sculpt_tool_needs_original(SCULPT_get_tool(ss, brush)) ? + true : + ss->cache->original; - /* Only act if some verts are inside the brush area. */ - if (totnode == 0) { - return; - } - - /* Free index based vertex info as it will become invalid after modifying the topology during the - * stroke. */ + /* Free index based vertex info as it will become invalid after modifying the topology during + * the stroke. */ MEM_SAFE_FREE(ss->vertex_info.boundary); + MEM_SAFE_FREE(ss->vertex_info.symmetrize_map); PBVHTopologyUpdateMode mode = PBVHTopologyUpdateMode(0); float location[3]; - if (!(sd->flags & SCULPT_DYNTOPO_DETAIL_MANUAL)) { - if (sd->flags & SCULPT_DYNTOPO_SUBDIVIDE) { + int dyntopo_mode = brush->dyntopo.flag; + int dyntopo_detail_mode = brush->dyntopo.mode; + + if (dyntopo_detail_mode != DYNTOPO_DETAIL_MANUAL) { + if (dyntopo_mode & DYNTOPO_SUBDIVIDE) { mode |= PBVH_Subdivide; } + else if (dyntopo_mode & DYNTOPO_LOCAL_SUBDIVIDE) { + mode |= PBVH_LocalSubdivide | PBVH_Subdivide; + } - if ((sd->flags & SCULPT_DYNTOPO_COLLAPSE) || (brush->sculpt_tool == SCULPT_TOOL_SIMPLIFY)) { + if (dyntopo_mode & DYNTOPO_COLLAPSE) { mode |= PBVH_Collapse; } - } - - for (n = 0; n < totnode; n++) { - SCULPT_undo_push_node(ob, - nodes[n], - brush->sculpt_tool == SCULPT_TOOL_MASK ? SCULPT_UNDO_MASK : - SCULPT_UNDO_COORDS); - BKE_pbvh_node_mark_update(nodes[n]); - - if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) { - BKE_pbvh_node_mark_topology_update(nodes[n]); - BKE_pbvh_bmesh_node_save_orig(ss->bm, ss->bm_log, nodes[n], false); + else if (dyntopo_mode & DYNTOPO_LOCAL_COLLAPSE) { + mode |= PBVH_LocalCollapse | PBVH_Collapse; } } - if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) { - BKE_pbvh_bmesh_update_topology(ss->pbvh, - mode, - ss->cache->location, - ss->cache->view_normal, - ss->cache->radius, - (brush->flag & BRUSH_FRONTFACE) != 0, - (brush->falloff_shape != PAINT_FALLOFF_SHAPE_SPHERE)); + if (dyntopo_mode & DYNTOPO_CLEANUP) { + mode |= PBVH_Cleanup; } - MEM_SAFE_FREE(nodes); + SculptSearchSphereData sdata{}; + sdata.ss = ss, sdata.sd = sd, sdata.ob = ob; + sdata.radius_squared = square_f(ss->cache->radius * radius_scale * 1.25f); + sdata.original = use_original; + sdata.ignore_fully_ineffective = SCULPT_get_tool(ss, brush) != SCULPT_TOOL_MASK; + sdata.center = nullptr; + sdata.brush = brush; + + int symidx = SCULPT_get_symmetry_pass(ss); + + void *mask_cb_data; + DyntopoMaskCB mask_cb; + + BKE_pbvh_set_bm_log(ss->pbvh, ss->bm_log); + + SCULPT_dyntopo_automasking_init(ss, sd, brush, ob, &mask_cb, &mask_cb_data); + + int actv = -1, actf = -1; + + if (ss->active_vertex.i != PBVH_REF_NONE) { + actv = BM_ELEM_GET_ID(ss->bm, (BMElem *)ss->active_vertex.i); + } + + if (ss->active_face.i != PBVH_REF_NONE) { + actf = BM_ELEM_GET_ID(ss->bm, (BMElem *)ss->active_face.i); + } + + /* do nodes under the brush cursor */ + BKE_pbvh_bmesh_update_topology_nodes(ss->pbvh, + SCULPT_search_sphere_cb, + topology_undopush_cb, + &sdata, + mode, + ss->cache->location, + ss->cache->view_normal, + ss->cache->radius * radius_scale, + (brush->flag & BRUSH_FRONTFACE) != 0, + (brush->falloff_shape != PAINT_FALLOFF_SHAPE_SPHERE), + symidx, + true, + mask_cb, + mask_cb_data, + /*XXX implement me: disable dyntopo smooth setting*/ false, + brush->sculpt_tool == SCULPT_TOOL_SNAKE_HOOK); + + SCULPT_dyntopo_automasking_end(mask_cb_data); + + if (actv != -1) { + BMVert *v = (BMVert *)BM_ELEM_FROM_ID_SAFE(ss->bm, actv); + + if (v && v->head.htype == BM_VERT) { + ss->active_vertex.i = (intptr_t)v; + } + else { + ss->active_vertex.i = PBVH_REF_NONE; + } + } + + if (actf != -1) { + BMFace *f = (BMFace *)BM_ELEM_FROM_ID_SAFE(ss->bm, actf); + + if (f && f->head.htype == BM_FACE) { + ss->active_face.i = (intptr_t)f; + } + else { + ss->active_face.i = PBVH_REF_NONE; + } + } /* Update average stroke position. */ copy_v3_v3(location, ss->cache->true_location); mul_m4_v3(ob->object_to_world, location); + + ss->totfaces = ss->totpoly = ss->bm->totface; + ss->totvert = ss->bm->totvert; +} + +static void do_check_origco_cb(void *__restrict userdata, + const int n, + const TaskParallelTLS *__restrict /* tls */) +{ + SculptThreadedTaskData *data = (SculptThreadedTaskData *)userdata; + SculptSession *ss = data->ob->sculpt; + PBVHVertexIter vd; + + bool modified = false; + + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { + modified |= SCULPT_vertex_check_origdata(ss, vd.vertex); + } + BKE_pbvh_vertex_iter_end; + + if (modified) { + BKE_pbvh_node_mark_original_update(data->nodes[n]); + } } static void do_brush_action_task_cb(void *__restrict userdata, @@ -3553,13 +4285,25 @@ static void do_brush_action(Sculpt *sd, /* Initialize surface smooth cache. */ if ((brush->sculpt_tool == SCULPT_TOOL_SMOOTH) && (brush->smooth_deform_type == BRUSH_SMOOTH_DEFORM_SURFACE)) { - BLI_assert(ss->cache->surface_smooth_laplacian_disp == nullptr); - ss->cache->surface_smooth_laplacian_disp = static_cast( - MEM_callocN(sizeof(float[3]) * SCULPT_vertex_count_get(ss), "HC smooth laplacian b")); + SCULPT_surface_smooth_laplacian_init(ob); } } } + if (ss->cache->original) { + SculptThreadedTaskData task_data{}; + task_data.sd = sd; + task_data.ob = ob; + task_data.brush = brush; + task_data.nodes = nodes; + + TaskParallelSettings settings; + BKE_pbvh_parallel_range_settings(&settings, true, totnode); + BLI_task_parallel_range(0, totnode, &task_data, do_check_origco_cb, &settings); + + BKE_pbvh_update_bounds(ss->pbvh, PBVH_UpdateOriginalBB); + } + /* Only act if some verts are inside the brush area. */ if (totnode == 0) { MEM_SAFE_FREE(texnodes); @@ -3567,7 +4311,7 @@ static void do_brush_action(Sculpt *sd, } float location[3]; - if (!use_pixels) { + if (!use_pixels && !ss->bm) { SculptThreadedTaskData task_data{}; task_data.sd = sd; task_data.ob = ob; @@ -3578,6 +4322,56 @@ static void do_brush_action(Sculpt *sd, BKE_pbvh_parallel_range_settings(&settings, true, totnode); BLI_task_parallel_range(0, totnode, &task_data, do_brush_action_task_cb, &settings); } + else if (ss->bm) { + SculptUndoType undo_type; + int extra_type = 0; + + if (SCULPT_tool_is_paint(brush->sculpt_tool)) { + undo_type = SCULPT_UNDO_COLOR; + } + else if (brush->sculpt_tool == SCULPT_TOOL_DRAW_FACE_SETS) { + if (ss->cache->invert) { + undo_type = SCULPT_UNDO_COORDS; + } + else { + undo_type = SCULPT_UNDO_FACE_SETS; + } + } + else if (brush->sculpt_tool == SCULPT_TOOL_MASK) { + undo_type = SCULPT_UNDO_MASK; + } + else { + undo_type = SCULPT_UNDO_COORDS; + } + + if (ss->cache->supports_gravity && sd->gravity_factor > 0.0f && + undo_type != SCULPT_UNDO_COORDS) { + extra_type = int(SCULPT_UNDO_COORDS); + } + + for (int i : IndexRange(totnode)) { + SCULPT_ensure_dyntopo_node_undo(ob, nodes[i], undo_type, extra_type); + + switch (undo_type) { + case SCULPT_UNDO_FACE_SETS: + BKE_pbvh_node_mark_update_face_sets(nodes[i]); + break; + case SCULPT_UNDO_MASK: + BKE_pbvh_node_mark_update_mask(nodes[i]); + break; + case SCULPT_UNDO_COLOR: + BKE_pbvh_node_mark_update_color(nodes[i]); + break; + case SCULPT_UNDO_COORDS: + BKE_pbvh_node_mark_update(nodes[i]); + break; + } + + if (extra_type == int(SCULPT_UNDO_COORDS)) { + BKE_pbvh_node_mark_update(nodes[i]); + } + } + } if (sculpt_brush_needs_normal(ss, sd, brush)) { update_sculpt_normal(sd, ob, nodes, totnode); @@ -3710,7 +4504,8 @@ static void do_brush_action(Sculpt *sd, SCULPT_do_displacement_smear_brush(sd, ob, nodes, totnode); break; case SCULPT_TOOL_PAINT: - SCULPT_do_paint_brush(paint_mode_settings, sd, ob, nodes, totnode, texnodes, texnodes_num); + SCULPT_do_paint_brush( + paint_mode_settings, ss->scene, sd, ob, nodes, totnode, texnodes, texnodes_num); break; case SCULPT_TOOL_SMEAR: SCULPT_do_smear_brush(sd, ob, nodes, totnode); @@ -3810,12 +4605,7 @@ static void sculpt_combine_proxies_node(Object &object, float val[3]; if (use_orco) { - if (ss->bm) { - copy_v3_v3(val, BM_log_original_vert_co(ss->bm_log, vd.bm_vert)); - } - else { - copy_v3_v3(val, orco[vd.i]); - } + copy_v3_v3(val, SCULPT_vertex_origco_get(ss, vd.vertex)); } else { copy_v3_v3(val, vd.co); @@ -4260,10 +5050,9 @@ static const char *sculpt_tool_name(Sculpt *sd) /* Operator for applying a stroke (various attributes including mouse path) * using the current brush. */ -void SCULPT_cache_free(StrokeCache *cache) +void SCULPT_cache_free(SculptSession *ss, struct Object *ob, StrokeCache *cache) { MEM_SAFE_FREE(cache->dial); - MEM_SAFE_FREE(cache->surface_smooth_laplacian_disp); MEM_SAFE_FREE(cache->layer_displacement_factor); MEM_SAFE_FREE(cache->prev_colors); MEM_SAFE_FREE(cache->detail_directions); @@ -4401,6 +5190,11 @@ static void sculpt_update_cache_invariants( float max_scale; int mode; + ss->hard_edge_mode = ups->hard_edge_mode; + + Mesh *me = BKE_object_get_original_mesh(ob); + BKE_sculptsession_ignore_uvs_set(ob, me->flag & ME_SCULPT_IGNORE_UVS); + ss->cache = cache; /* Set scaling adjustment. */ @@ -4968,6 +5762,9 @@ static void sculpt_raycast_cb(PBVHNode *node, void *data_v, float *tmin) } } + float back_depth = 0.0f; + int hit_count = 0; + if (BKE_pbvh_node_raycast(srd->ss->pbvh, node, origco, @@ -4975,10 +5772,13 @@ static void sculpt_raycast_cb(PBVHNode *node, void *data_v, float *tmin) srd->ray_start, srd->ray_normal, &srd->isect_precalc, + &hit_count, &srd->depth, + &back_depth, &srd->active_vertex, - &srd->active_face_grid_index, - srd->face_normal)) { + &srd->active_face, + srd->face_normal, + srd->ss->stroke_id)) { srd->hit = true; *tmin = srd->depth; } @@ -5012,7 +5812,8 @@ static void sculpt_find_nearest_to_ray_cb(PBVHNode *node, void *data_v, float *t srd->ray_start, srd->ray_normal, &srd->depth, - &srd->dist_sq_to_ray)) { + &srd->dist_sq_to_ray, + srd->ss->stroke_id)) { srd->hit = true; *tmin = srd->dist_sq_to_ray; } @@ -5058,7 +5859,8 @@ float SCULPT_raycast_init(ViewContext *vc, bool SCULPT_cursor_geometry_info_update(bContext *C, SculptCursorGeometryInfo *out, const float mval[2], - bool use_sampled_normal) + bool use_sampled_normal, + bool /*use_back*/) { Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); Scene *scene = CTX_data_scene(C); @@ -5099,7 +5901,8 @@ bool SCULPT_cursor_geometry_info_update(bContext *C, srd.face_normal = face_normal; isect_ray_tri_watertight_v3_precalc(&srd.isect_precalc, ray_normal); - BKE_pbvh_raycast(ss->pbvh, sculpt_raycast_cb, &srd, ray_start, ray_normal, srd.original); + BKE_pbvh_raycast( + ss->pbvh, sculpt_raycast_cb, &srd, ray_start, ray_normal, srd.original, ss->stroke_id); /* Cursor is not over the mesh, return default values. */ if (!srd.hit) { @@ -5116,15 +5919,15 @@ bool SCULPT_cursor_geometry_info_update(bContext *C, switch (BKE_pbvh_type(ss->pbvh)) { case PBVH_FACES: - ss->active_face_index = srd.active_face_grid_index; + ss->active_face = srd.active_face; ss->active_grid_index = 0; break; case PBVH_GRIDS: - ss->active_face_index = 0; - ss->active_grid_index = srd.active_face_grid_index; + ss->active_face = srd.active_face; + ss->active_grid_index = ss->active_face.i; break; case PBVH_BMESH: - ss->active_face_index = 0; + ss->active_face.i = PBVH_REF_NONE; ss->active_grid_index = 0; break; } @@ -5228,7 +6031,8 @@ bool SCULPT_stroke_get_location(bContext *C, srd.face_normal = face_normal; isect_ray_tri_watertight_v3_precalc(&srd.isect_precalc, ray_normal); - BKE_pbvh_raycast(ss->pbvh, sculpt_raycast_cb, &srd, ray_start, ray_normal, srd.original); + BKE_pbvh_raycast( + ss->pbvh, sculpt_raycast_cb, &srd, ray_start, ray_normal, srd.original, ss->stroke_id); if (srd.hit) { hit = true; copy_v3_v3(out, ray_normal); @@ -5503,13 +6307,13 @@ void SCULPT_flush_update_done(const bContext *C, Object *ob, SculptUpdateType up BKE_pbvh_update_vertex_data(ss->pbvh, PBVH_UpdateColor); } + if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) { + BKE_pbvh_bmesh_after_stroke(ss->pbvh, false); + } + BKE_sculpt_attributes_destroy_temporary_stroke(ob); if (update_flags & SCULPT_UPDATE_COORDS) { - if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) { - BKE_pbvh_bmesh_after_stroke(ss->pbvh); - } - /* Optimization: if there is locked key and active modifiers present in */ /* the stack, keyblock is updating at each step. otherwise we could update */ /* keyblock only when stroke is finished. */ @@ -5569,8 +6373,7 @@ bool SCULPT_handles_colors_report(SculptSession *ss, ReportList *reports) case PBVH_FACES: return true; case PBVH_BMESH: - BKE_report(reports, RPT_ERROR, "Not supported in dynamic topology mode"); - return false; + return true; case PBVH_GRIDS: BKE_report(reports, RPT_ERROR, "Not supported in multiresolution mode"); return false; @@ -5609,7 +6412,7 @@ static bool sculpt_stroke_test_start(bContext *C, wmOperator *op, const float mv sculpt_update_cache_invariants(C, sd, ss, op, mval); SculptCursorGeometryInfo sgi; - SCULPT_cursor_geometry_info_update(C, &sgi, mval, false); + SCULPT_cursor_geometry_info_update(C, &sgi, mval, false, false); sculpt_stroke_undo_begin(C, op); @@ -5642,15 +6445,17 @@ static void sculpt_stroke_update_step(bContext *C, if (sd->flags & (SCULPT_DYNTOPO_DETAIL_CONSTANT | SCULPT_DYNTOPO_DETAIL_MANUAL)) { float object_space_constant_detail = 1.0f / (sd->constant_detail * mat4_to_scale(ob->object_to_world)); - BKE_pbvh_bmesh_detail_size_set(ss->pbvh, object_space_constant_detail); + BKE_pbvh_bmesh_detail_size_set(ss->pbvh, object_space_constant_detail, 0.4f); } else if (sd->flags & SCULPT_DYNTOPO_DETAIL_BRUSH) { - BKE_pbvh_bmesh_detail_size_set(ss->pbvh, ss->cache->radius * sd->detail_percent / 100.0f); + BKE_pbvh_bmesh_detail_size_set( + ss->pbvh, ss->cache->radius * sd->detail_percent / 100.0f, 0.4f); } else { BKE_pbvh_bmesh_detail_size_set(ss->pbvh, (ss->cache->radius / ss->cache->dyntopo_pixel_radius) * - (sd->detail_size * U.pixelsize) / 0.4f); + (sd->detail_size * U.pixelsize) / 0.4f, + 0.4f); } if (SCULPT_stroke_is_dynamic_topology(ss, brush)) { @@ -5737,11 +6542,10 @@ static void sculpt_stroke_done(const bContext *C, PaintStroke * /*stroke*/) } if (SCULPT_is_automasking_enabled(sd, ss, brush)) { - SCULPT_automasking_cache_free(ss->cache->automasking); + SCULPT_automasking_cache_free(ss, ob, ss->cache->automasking); } - BKE_pbvh_node_color_buffer_free(ss->pbvh); - SCULPT_cache_free(ss->cache); + SCULPT_cache_free(ss, ob, ss->cache); ss->cache = nullptr; sculpt_stroke_undo_end(C, brush); @@ -5801,8 +6605,7 @@ static int sculpt_brush_stroke_invoke(bContext *C, wmOperator *op, const wmEvent BKE_sculpt_mask_layers_ensure(CTX_data_depsgraph_pointer(C), CTX_data_main(C), ob, mmd); } if (SCULPT_tool_is_face_sets(brush->sculpt_tool)) { - Mesh *mesh = BKE_object_get_original_mesh(ob); - ss->face_sets = BKE_sculpt_face_sets_ensure(mesh); + ss->face_sets = BKE_sculpt_face_sets_ensure(ob); } stroke = paint_stroke_new(C, @@ -5873,7 +6676,7 @@ static void sculpt_brush_stroke_cancel(bContext *C, wmOperator *op) paint_stroke_cancel(C, op, static_cast(op->customdata)); if (ss->cache) { - SCULPT_cache_free(ss->cache); + SCULPT_cache_free(ss, ob, ss->cache); ss->cache = nullptr; } @@ -5931,7 +6734,7 @@ void SCULPT_OT_brush_stroke(wmOperatorType *ot) /* Properties. */ - paint_stroke_operator_properties(ot); + paint_stroke_operator_properties(ot, true); RNA_def_boolean(ot->srna, "ignore_background_click", @@ -5970,10 +6773,10 @@ enum { static void SCULPT_fake_neighbor_init(SculptSession *ss, const float max_dist) { const int totvert = SCULPT_vertex_count_get(ss); - ss->fake_neighbors.fake_neighbor_index = static_cast( + ss->fake_neighbors.fake_neighbor_index = static_cast( MEM_malloc_arrayN(totvert, sizeof(int), "fake neighbor")); for (int i = 0; i < totvert; i++) { - ss->fake_neighbors.fake_neighbor_index[i] = FAKE_NEIGHBOR_NONE; + ss->fake_neighbors.fake_neighbor_index[i].i = FAKE_NEIGHBOR_NONE; } ss->fake_neighbors.current_max_distance = max_dist; @@ -5984,9 +6787,9 @@ static void SCULPT_fake_neighbor_add(SculptSession *ss, PBVHVertRef v_a, PBVHVer int v_index_a = BKE_pbvh_vertex_to_index(ss->pbvh, v_a); int v_index_b = BKE_pbvh_vertex_to_index(ss->pbvh, v_b); - if (ss->fake_neighbors.fake_neighbor_index[v_index_a] == FAKE_NEIGHBOR_NONE) { - ss->fake_neighbors.fake_neighbor_index[v_index_a] = v_index_b; - ss->fake_neighbors.fake_neighbor_index[v_index_b] = v_index_a; + if (ss->fake_neighbors.fake_neighbor_index[v_index_a].i == FAKE_NEIGHBOR_NONE) { + ss->fake_neighbors.fake_neighbor_index[v_index_a].i = v_index_b; + ss->fake_neighbors.fake_neighbor_index[v_index_b].i = v_index_a; } } @@ -6014,7 +6817,7 @@ static void do_fake_neighbor_search_task_cb(void *__restrict userdata, BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { int vd_topology_id = SCULPT_vertex_island_get(ss, vd.vertex); if (vd_topology_id != nvtd->current_topology_id && - ss->fake_neighbors.fake_neighbor_index[vd.index] == FAKE_NEIGHBOR_NONE) { + ss->fake_neighbors.fake_neighbor_index[vd.index].i == FAKE_NEIGHBOR_NONE) { float distance_squared = len_squared_v3v3(vd.co, data->nearest_vertex_search_co); if (distance_squared < nvtd->nearest_vertex_distance_squared && distance_squared < data->max_distance_squared) { @@ -6096,7 +6899,9 @@ struct SculptTopologyIDFloodFillData { void SCULPT_boundary_info_ensure(Object *object) { SculptSession *ss = object->sculpt; - if (ss->vertex_info.boundary) { + + /* PBVH_BMESH now handles boundaries itself. */ + if (ss->bm || ss->vertex_info.boundary) { return; } @@ -6128,6 +6933,21 @@ void SCULPT_boundary_info_ensure(Object *object) MEM_freeN(adjacent_faces_edge_count); } +void SCULPT_ensure_epmap(SculptSession *ss) +{ + if (BKE_pbvh_type(ss->pbvh) != PBVH_BMESH && !ss->epmap) { + + BKE_mesh_edge_poly_map_create(&ss->epmap, + &ss->epmap_mem, + ss->medge, + ss->totedges, + ss->mpoly, + ss->totfaces, + ss->mloop, + ss->totloops); + } +} + void SCULPT_fake_neighbors_ensure(Sculpt *sd, Object *ob, const float max_dist) { SculptSession *ss = ob->sculpt; @@ -6148,7 +6968,7 @@ void SCULPT_fake_neighbors_ensure(Sculpt *sd, Object *ob, const float max_dist) const PBVHVertRef from_v = BKE_pbvh_index_to_vertex(ss->pbvh, i); /* This vertex does not have a fake neighbor yet, search one for it. */ - if (ss->fake_neighbors.fake_neighbor_index[i] == FAKE_NEIGHBOR_NONE) { + if (ss->fake_neighbors.fake_neighbor_index[i].i == FAKE_NEIGHBOR_NONE) { const PBVHVertRef to_v = SCULPT_fake_neighbor_search(sd, ob, from_v, max_dist); if (to_v.i != PBVH_REF_NONE) { /* Add the fake neighbor if available. */ @@ -6206,7 +7026,7 @@ void SCULPT_automasking_node_update(SculptSession * /*ss*/, PBVHVertexIter *vd) { if (automask_data->have_orig_data) { - SCULPT_orig_vert_data_update(&automask_data->orig_data, vd); + SCULPT_orig_vert_data_update(&automask_data->orig_data, vd->vertex); } } @@ -6240,32 +7060,19 @@ bool SCULPT_vertex_is_occluded(SculptSession *ss, PBVHVertRef vertex, bool origi srd.face_normal = face_normal; isect_ray_tri_watertight_v3_precalc(&srd.isect_precalc, ray_normal); - BKE_pbvh_raycast(ss->pbvh, sculpt_raycast_cb, &srd, ray_start, ray_normal, srd.original); + BKE_pbvh_raycast( + ss->pbvh, sculpt_raycast_cb, &srd, ray_start, ray_normal, srd.original, ss->stroke_id); return srd.hit; } void SCULPT_stroke_id_next(Object *ob) { - /* Manually wrap in int32 space to avoid tripping up undefined behavior - * sanitizers. - */ - ob->sculpt->stroke_id = uchar((int(ob->sculpt->stroke_id) + 1) & 255); -} + ushort *id = reinterpret_cast(&ob->sculpt->stroke_id); -void SCULPT_stroke_id_ensure(Object *ob) -{ - SculptSession *ss = ob->sculpt; - - if (!ss->attrs.automasking_stroke_id) { - SculptAttributeParams params = {0}; - ss->attrs.automasking_stroke_id = BKE_sculpt_attribute_ensure( - ob, - ATTR_DOMAIN_POINT, - CD_PROP_INT8, - SCULPT_ATTRIBUTE_NAME(automasking_stroke_id), - ¶ms); - } + /* Try to avoid offending undefined behavior sanitizers. */ + id[0] = ushort((int(id[0]) + 1) % 65535); + id[1] = 0; } int SCULPT_face_set_get(const SculptSession *ss, PBVHFaceRef face) @@ -6297,7 +7104,7 @@ void SCULPT_face_set_set(SculptSession *ss, PBVHFaceRef face, int fset) int SCULPT_vertex_island_get(SculptSession *ss, PBVHVertRef vertex) { if (ss->attrs.topology_island_key) { - return *static_cast(SCULPT_vertex_attr_get(vertex, ss->attrs.topology_island_key)); + return *SCULPT_vertex_attr_get(vertex, ss->attrs.topology_island_key); } return -1; @@ -6344,8 +7151,7 @@ void SCULPT_topology_islands_ensure(Object *ob) PBVHVertRef vertex2 = stack.pop_last(); SculptVertexNeighborIter ni; - *static_cast( - SCULPT_vertex_attr_get(vertex2, ss->attrs.topology_island_key)) = island_nr; + *SCULPT_vertex_attr_get(vertex2, ss->attrs.topology_island_key) = island_nr; SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vertex2, ni) { if (visit.add(ni.vertex) && SCULPT_vertex_any_face_visible_get(ss, ni.vertex)) { diff --git a/source/blender/editors/sculpt_paint/sculpt_api.cc b/source/blender/editors/sculpt_paint/sculpt_api.cc new file mode 100644 index 00000000000..432b6962732 --- /dev/null +++ b/source/blender/editors/sculpt_paint/sculpt_api.cc @@ -0,0 +1,638 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2006 by Nicholas Bishop. All rights reserved. */ + +/** \file + * \ingroup edsculpt + * Implements the Sculpt Mode tools. + */ + +#include +#include +#include + +#include "MEM_guardedalloc.h" + +#include "BLI_alloca.h" +#include "BLI_array.h" +#include "BLI_bitmap.h" +#include "BLI_blenlib.h" +#include "BLI_dial_2d.h" +#include "BLI_ghash.h" +#include "BLI_gsqueue.h" +#include "BLI_hash.h" +#include "BLI_link_utils.h" +#include "BLI_linklist.h" +#include "BLI_linklist_stack.h" +#include "BLI_listbase.h" +#include "BLI_math.h" +#include "BLI_math_color.h" +#include "BLI_math_color_blend.h" +#include "BLI_memarena.h" +#include "BLI_rand.h" +#include "BLI_set.hh" +#include "BLI_task.h" +#include "BLI_task.hh" +#include "BLI_timeit.hh" +#include "BLI_utildefines.h" +#include "BLI_vector.hh" + +#include "PIL_time.h" + +#include "DNA_brush_types.h" +#include "DNA_customdata_types.h" +#include "DNA_listBase.h" +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_node_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" +#include "DNA_view3d_types.h" + +#include "BKE_attribute.h" +#include "BKE_attribute.hh" +#include "BKE_brush.h" +#include "BKE_ccg.h" +#include "BKE_colortools.h" +#include "BKE_context.h" +#include "BKE_image.h" +#include "BKE_key.h" +#include "BKE_lib_id.h" +#include "BKE_main.h" +#include "BKE_mesh.h" +#include "BKE_mesh_fair.h" +#include "BKE_mesh_mapping.h" +#include "BKE_modifier.h" +#include "BKE_multires.h" +#include "BKE_node_runtime.hh" +#include "BKE_object.h" +#include "BKE_paint.h" +#include "BKE_particle.h" +#include "BKE_pbvh.h" +#include "BKE_pointcache.h" +#include "BKE_report.h" +#include "BKE_scene.h" +#include "BKE_subdiv_ccg.h" +#include "BKE_subsurf.h" + +#include "NOD_texture.h" + +#include "DEG_depsgraph.h" +#include "DEG_depsgraph_query.h" + +#include "IMB_colormanagement.h" + +#include "GPU_batch.h" +#include "GPU_batch_presets.h" +#include "GPU_immediate.h" +#include "GPU_immediate_util.h" +#include "GPU_matrix.h" +#include "GPU_state.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "ED_paint.h" +#include "ED_screen.h" +#include "ED_sculpt.h" +#include "ED_space_api.h" +#include "ED_transform_snap_object_context.h" +#include "ED_view3d.h" + +#include "paint_intern.h" +#include "sculpt_intern.hh" + +#include "RNA_access.h" +#include "RNA_define.h" + +#include "atomic_ops.h" + +#include "bmesh.h" +#include "bmesh_log.h" +#include "bmesh_tools.h" + +#include "UI_resources.h" + +#include +#include +#include + +/** + * Checks if the face sets of the adjacent faces to the edge between \a v1 and \a v2 + * in the base mesh are equal. + */ +static bool sculpt_check_unique_face_set_for_edge_in_base_mesh(const SculptSession *ss, + int v1, + int v2) +{ + if (!ss->face_sets) { + return true; + } + + const MeshElemMap *vert_map = &ss->pmap->pmap[v1]; + int p1 = -1, p2 = -1; + for (int i = 0; i < vert_map->count; i++) { + const MPoly *p = &ss->mpoly[vert_map->indices[i]]; + for (int l = 0; l < p->totloop; l++) { + const MLoop *loop = &ss->mloop[p->loopstart + l]; + if (loop->v == v2) { + if (p1 == -1) { + p1 = vert_map->indices[i]; + break; + } + + if (p2 == -1) { + p2 = vert_map->indices[i]; + break; + } + } + } + } + + if (p1 != -1 && p2 != -1) { + return abs(ss->face_sets[p1]) == (ss->face_sets[p2]); + } + return true; +} + +static bool sculpt_check_boundary_vertex_in_base_mesh(const SculptSession *ss, int index) +{ + BLI_assert(ss->vertex_info.boundary); + + return BLI_BITMAP_TEST(ss->vertex_info.boundary, index); +} + +static bool sculpt_check_unique_face_set_in_base_mesh(const SculptSession *ss, int index) +{ + if (!ss->face_sets) { + return true; + } + const MeshElemMap *vert_map = &ss->pmap->pmap[index]; + int face_set = -1; + for (int i = 0; i < vert_map->count; i++) { + if (face_set == -1) { + face_set = ss->face_sets[vert_map->indices[i]]; + } + else { + if (ss->face_sets[vert_map->indices[i]] != face_set) { + return false; + } + } + } + return true; +} + +eSculptBoundary SCULPT_edge_is_boundary(const SculptSession *ss, + const PBVHEdgeRef edge, + eSculptBoundary typemask) +{ + + int ret = 0; + + switch (BKE_pbvh_type(ss->pbvh)) { + case PBVH_BMESH: { + BMEdge *e = (BMEdge *)edge.i; + + if (typemask & SCULPT_BOUNDARY_MESH) { + ret |= (!e->l || e->l == e->l->radial_next) ? SCULPT_BOUNDARY_MESH : 0; + } + + if ((typemask & SCULPT_BOUNDARY_FACE_SET) && e->l && e->l != e->l->radial_next) { + if (ss->boundary_symmetry) { + // TODO: calc and cache this properly + + int boundflag1 = BM_ELEM_CD_GET_INT(e->v1, ss->attrs.boundary_flags->bmesh_cd_offset); + int boundflag2 = BM_ELEM_CD_GET_INT(e->v2, ss->attrs.boundary_flags->bmesh_cd_offset); + + ret |= (boundflag1 | boundflag2) & SCULPT_BOUNDARY_FACE_SET; + } + else { + int fset1 = BM_ELEM_CD_GET_INT(e->l->f, ss->cd_faceset_offset); + int fset2 = BM_ELEM_CD_GET_INT(e->l->radial_next->f, ss->cd_faceset_offset); + + bool ok = (fset1 < 0) != (fset2 < 0); + + ok = ok || fset1 != fset2; + + ret |= ok ? SCULPT_BOUNDARY_FACE_SET : 0; + } + } + + if (typemask & SCULPT_BOUNDARY_UV) { + int boundflag1 = BM_ELEM_CD_GET_INT(e->v1, ss->attrs.boundary_flags->bmesh_cd_offset); + int boundflag2 = BM_ELEM_CD_GET_INT(e->v2, ss->attrs.boundary_flags->bmesh_cd_offset); + + ret |= (boundflag1 | boundflag2) & SCULPT_BOUNDARY_UV; + } + + if (typemask & SCULPT_BOUNDARY_SHARP) { + ret |= !BM_elem_flag_test(e, BM_ELEM_SMOOTH) ? SCULPT_BOUNDARY_SHARP : 0; + } + + if (typemask & SCULPT_BOUNDARY_SEAM) { + ret |= BM_elem_flag_test(e, BM_ELEM_SEAM) ? SCULPT_BOUNDARY_SEAM : 0; + } + + break; + } + case PBVH_FACES: { + eSculptBoundary mask = typemask & (SCULPT_BOUNDARY_MESH | SCULPT_BOUNDARY_FACE_SET); + PBVHVertRef v1, v2; + + SCULPT_edge_get_verts(ss, edge, &v1, &v2); + + if (mask) { // use less accurate approximation for now + eSculptBoundary a = SCULPT_vertex_is_boundary(ss, v1, mask); + eSculptBoundary b = SCULPT_vertex_is_boundary(ss, v2, mask); + + ret |= a & b; + } + + if (typemask & SCULPT_BOUNDARY_SHARP) { + ret |= (ss->sharp_edge && ss->sharp_edge[edge.i]) ? SCULPT_BOUNDARY_SHARP : 0; + } + + if (typemask & SCULPT_BOUNDARY_SEAM) { + ret |= ss->medge[edge.i].flag & ME_SEAM ? SCULPT_BOUNDARY_SEAM : 0; + } + + break; + } + case PBVH_GRIDS: { + // not implemented + break; + } + } + + return (eSculptBoundary)ret; +} + +void SCULPT_edge_get_verts(const SculptSession *ss, + const PBVHEdgeRef edge, + PBVHVertRef *r_v1, + PBVHVertRef *r_v2) +{ + switch (BKE_pbvh_type(ss->pbvh)) { + case PBVH_BMESH: { + BMEdge *e = (BMEdge *)edge.i; + r_v1->i = (intptr_t)e->v1; + r_v2->i = (intptr_t)e->v2; + break; + } + + case PBVH_FACES: { + r_v1->i = (intptr_t)ss->medge[edge.i].v1; + r_v2->i = (intptr_t)ss->medge[edge.i].v2; + break; + } + case PBVH_GRIDS: + // not supported yet + r_v1->i = r_v2->i = PBVH_REF_NONE; + break; + } +} + +PBVHVertRef SCULPT_edge_other_vertex(const SculptSession *ss, + const PBVHEdgeRef edge, + const PBVHVertRef vertex) +{ + PBVHVertRef v1, v2; + + SCULPT_edge_get_verts(ss, edge, &v1, &v2); + + return v1.i == vertex.i ? v2 : v1; +} + +static void grids_update_boundary_flags(const SculptSession *ss, PBVHVertRef vertex) +{ + int *flag = SCULPT_vertex_attr_get(vertex, ss->attrs.boundary_flags); + + *flag = 0; + + int index = (int)vertex.i; + const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh); + const int grid_index = index / key->grid_area; + const int vertex_index = index - grid_index * key->grid_area; + SubdivCCGCoord coord{}; + + coord.grid_index = grid_index; + coord.x = vertex_index % key->grid_size; + coord.y = vertex_index / key->grid_size; + + int v1, v2; + const SubdivCCGAdjacencyType adjacency = BKE_subdiv_ccg_coarse_mesh_adjacency_info_get( + ss->subdiv_ccg, &coord, ss->mloop, ss->mpoly, &v1, &v2); + + switch (adjacency) { + case SUBDIV_CCG_ADJACENT_VERTEX: + if (sculpt_check_unique_face_set_in_base_mesh(ss, v1)) { + *flag |= SCULPT_BOUNDARY_FACE_SET; + } + if (sculpt_check_boundary_vertex_in_base_mesh(ss, v2)) { + *flag |= SCULPT_BOUNDARY_MESH; + } + break; + case SUBDIV_CCG_ADJACENT_EDGE: + if (sculpt_check_unique_face_set_for_edge_in_base_mesh(ss, v1, v2)) { + *flag |= SCULPT_BOUNDARY_FACE_SET; + } + + if (sculpt_check_boundary_vertex_in_base_mesh(ss, v1) && + sculpt_check_boundary_vertex_in_base_mesh(ss, v2)) { + *flag |= SCULPT_BOUNDARY_MESH; + } + break; + case SUBDIV_CCG_ADJACENT_NONE: + break; + } +} + +static void faces_update_boundary_flags(const SculptSession *ss, const PBVHVertRef vertex) +{ + BKE_pbvh_update_vert_boundary_faces((int *)ss->attrs.boundary_flags->data, + ss->face_sets, + ss->hide_poly, + ss->vert_positions, + ss->medge, + ss->mloop, + ss->mpoly, + ss->msculptverts, + ss->pmap->pmap, + vertex, + ss->sharp_edge); + + /* We have to handle boundary here seperately. */ + + int *flag = SCULPT_vertex_attr_get(vertex, ss->attrs.boundary_flags); + *flag &= ~(SCULPT_CORNER_MESH | SCULPT_BOUNDARY_MESH); + + if (sculpt_check_boundary_vertex_in_base_mesh(ss, vertex.i)) { + *flag |= SCULPT_BOUNDARY_MESH; + + if (ss->pmap->pmap[vertex.i].count < 4) { + bool ok = true; + + for (int i = 0; i < ss->pmap->pmap[vertex.i].count; i++) { + const MPoly *mp = ss->mpoly + ss->pmap->pmap[vertex.i].indices[i]; + if (mp->totloop < 4) { + ok = false; + } + } + if (ok) { + *flag |= SCULPT_CORNER_MESH; + } + else { + *flag &= ~SCULPT_CORNER_MESH; + } + } + } +} +eSculptCorner SCULPT_vertex_is_corner(const SculptSession *ss, + const PBVHVertRef vertex, + eSculptCorner cornertype) +{ + eSculptCorner flag = *SCULPT_vertex_attr_get(vertex, ss->attrs.boundary_flags); + bool needs_update = flag & SCULPT_BOUNDARY_NEEDS_UPDATE; + + switch (BKE_pbvh_type(ss->pbvh)) { + case PBVH_BMESH: { + if (needs_update) { + BKE_pbvh_update_vert_boundary(ss->cd_sculpt_vert, + ss->cd_faceset_offset, + ss->cd_vert_node_offset, + ss->cd_face_node_offset, + ss->cd_vcol_offset, + ss->attrs.boundary_flags->bmesh_cd_offset, + (BMVert *)vertex.i, + ss->boundary_symmetry, + &ss->bm->ldata, + ss->totuv, + !ss->ignore_uvs); + } + + break; + } + case PBVH_FACES: + if (needs_update) { + faces_update_boundary_flags(ss, vertex); + } + break; + case PBVH_GRIDS: { + if (needs_update) { + grids_update_boundary_flags(ss, vertex); + } + break; + } + } + + return flag & (SCULPT_CORNER_MESH | SCULPT_CORNER_FACE_SET | SCULPT_CORNER_SEAM | + SCULPT_CORNER_SHARP | SCULPT_CORNER_UV); +} + +eSculptBoundary SCULPT_vertex_is_boundary(const SculptSession *ss, + const PBVHVertRef vertex, + eSculptBoundary boundary_types) +{ + eSculptBoundary flag = *SCULPT_vertex_attr_get(vertex, + ss->attrs.boundary_flags); + bool needs_update = flag & SCULPT_BOUNDARY_NEEDS_UPDATE; + + switch (BKE_pbvh_type(ss->pbvh)) { + case PBVH_BMESH: { + if (needs_update) { + BKE_pbvh_update_vert_boundary(ss->cd_sculpt_vert, + ss->cd_faceset_offset, + ss->cd_vert_node_offset, + ss->cd_face_node_offset, + ss->cd_vcol_offset, + ss->attrs.boundary_flags->bmesh_cd_offset, + (BMVert *)vertex.i, + ss->boundary_symmetry, + &ss->bm->ldata, + ss->totuv, + !ss->ignore_uvs); + } + + break; + } + case PBVH_FACES: { + if (needs_update) { + faces_update_boundary_flags(ss, vertex); + } + break; + } + + case PBVH_GRIDS: { + const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh); + const int grid_index = vertex.i / key->grid_area; + const int vertex_index = vertex.i - grid_index * key->grid_area; + SubdivCCGCoord coord{}; + coord.grid_index = grid_index; + coord.x = vertex_index % key->grid_size; + coord.y = vertex_index / key->grid_size; + int v1, v2; + const SubdivCCGAdjacencyType adjacency = BKE_subdiv_ccg_coarse_mesh_adjacency_info_get( + ss->subdiv_ccg, &coord, ss->mloop, ss->mpoly, &v1, &v2); + + switch (adjacency) { + case SUBDIV_CCG_ADJACENT_VERTEX: + return sculpt_check_boundary_vertex_in_base_mesh(ss, v1) ? SCULPT_BOUNDARY_MESH : + (eSculptBoundary)0; + case SUBDIV_CCG_ADJACENT_EDGE: + if (sculpt_check_boundary_vertex_in_base_mesh(ss, v1) && + sculpt_check_boundary_vertex_in_base_mesh(ss, v2)) { + return SCULPT_BOUNDARY_MESH; + } + case SUBDIV_CCG_ADJACENT_NONE: + return (eSculptBoundary)0; + } + } + } + + return flag & (SCULPT_BOUNDARY_MESH | SCULPT_BOUNDARY_FACE_SET | SCULPT_BOUNDARY_SEAM | + SCULPT_BOUNDARY_SHARP | SCULPT_BOUNDARY_UV); +} + +bool SCULPT_attr_ensure_layer(SculptSession *ss, + Object *ob, + eAttrDomain domain, + int proptype, + const char *name, + SculptAttributeParams *params) +{ + bool is_newlayer = !BKE_sculpt_attribute_exists(ob, domain, (eCustomDataType)proptype, name); + BKE_sculpt_attribute_ensure(ob, domain, (eCustomDataType)proptype, name, params); + + return is_newlayer; +} + +/* TODO: thoroughly test this function */ +bool SCULPT_attr_has_layer(SculptSession *ss, eAttrDomain domain, int proptype, const char *name) +{ + CustomData *vdata = nullptr, *pdata = nullptr, *data = nullptr; + + switch (BKE_pbvh_type(ss->pbvh)) { + case PBVH_BMESH: + vdata = &ss->bm->vdata; + pdata = &ss->bm->pdata; + break; + case PBVH_FACES: + pdata = ss->pdata; + vdata = ss->vdata; + break; + case PBVH_GRIDS: + pdata = ss->pdata; + break; + } + + switch (domain) { + case ATTR_DOMAIN_POINT: + data = vdata; + break; + case ATTR_DOMAIN_FACE: + data = pdata; + break; + default: + return false; + } + + if (data) { + return CustomData_get_named_layer_index(data, proptype, name) >= 0; + } + + return false; +} + +bool SCULPT_attr_release_layer(SculptSession *ss, Object *ob, SculptAttribute *scl) +{ + return BKE_sculpt_attribute_destroy(ob, scl); +} + +SculptAttribute *SCULPT_attr_get_layer(SculptSession *ss, + Object *ob, + eAttrDomain domain, + int proptype, + const char *name, + SculptAttributeParams *params) +{ + return BKE_sculpt_attribute_ensure(ob, domain, (eCustomDataType)proptype, name, params); +} + +bool SCULPT_vertex_check_origdata(SculptSession *ss, PBVHVertRef vertex) +{ + return BKE_pbvh_get_origvert(ss->pbvh, vertex, nullptr, nullptr, nullptr); +} + +int SCULPT_vertex_valence_get(const struct SculptSession *ss, PBVHVertRef vertex) +{ + SculptVertexNeighborIter ni; + MSculptVert *mv = SCULPT_vertex_get_sculptvert(ss, vertex); + + if (mv->flag & SCULPTVERT_NEED_VALENCE) { + mv->flag &= ~SCULPTVERT_NEED_VALENCE; + + int tot = 0; + + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vertex, ni) { + tot++; + } + SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); + + mv->valence = tot; + } +#if 0 + else { + + int tot = 0; + + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vertex, ni) { + tot++; + } + SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); + + if (tot != mv->valence) { + printf("%s: error: valence error!\n", __func__); + } + } +#endif + + return mv->valence; +} + +/* See SCULPT_stroke_id_test. */ +void SCULPT_stroke_id_ensure(Object *ob) +{ + SculptSession *ss = ob->sculpt; + + if (!ss->attrs.automasking_stroke_id) { + SculptAttributeParams params = {0}; + ss->attrs.automasking_stroke_id = BKE_sculpt_attribute_ensure( + ob, + ATTR_DOMAIN_POINT, + CD_PROP_INT32, + SCULPT_ATTRIBUTE_NAME(automasking_stroke_id), + ¶ms); + } +} + +int SCULPT_get_tool(const SculptSession *ss, const Brush *br) +{ + if (ss->cache && ss->cache->tool_override) { + return ss->cache->tool_override; + } + + return br->sculpt_tool; +} + +void SCULPT_ensure_persistent_layers(SculptSession *ss, Object *ob) +{ + SculptAttributeParams params = {.permanent = true, .simple_array = false}; + + if (!ss->attrs.persistent_co) { + ss->attrs.persistent_co = BKE_sculpt_attribute_ensure( + ob, ATTR_DOMAIN_POINT, CD_PROP_FLOAT3, SCULPT_ATTRIBUTE_NAME(persistent_co), ¶ms); + ss->attrs.persistent_no = BKE_sculpt_attribute_ensure( + ob, ATTR_DOMAIN_POINT, CD_PROP_FLOAT3, SCULPT_ATTRIBUTE_NAME(persistent_no), ¶ms); + ss->attrs.persistent_disp = BKE_sculpt_attribute_ensure( + ob, ATTR_DOMAIN_POINT, CD_PROP_FLOAT, SCULPT_ATTRIBUTE_NAME(persistent_disp), ¶ms); + } +} diff --git a/source/blender/editors/sculpt_paint/sculpt_array.c b/source/blender/editors/sculpt_paint/sculpt_array.c new file mode 100644 index 00000000000..fffcb6feb38 --- /dev/null +++ b/source/blender/editors/sculpt_paint/sculpt_array.c @@ -0,0 +1,1106 @@ +/* + * 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) 2020 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup edsculpt + */ + +#include "MEM_guardedalloc.h" + +#include "BLI_blenlib.h" +#include "BLI_hash.h" +#include "BLI_math.h" +#include "BLI_task.h" + +#include "DNA_brush_types.h" +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_object_types.h" + +#include "BKE_brush.h" +#include "BKE_ccg.h" +#include "BKE_colortools.h" +#include "BKE_context.h" +#include "BKE_lib_id.h" +#include "BKE_mesh.h" +#include "BKE_multires.h" +#include "BKE_node.h" +#include "BKE_object.h" +#include "BKE_paint.h" +#include "BKE_pbvh.h" +#include "BKE_scene.h" + +#include "paint_intern.h" +#include "sculpt_intern.h" + +#include "GPU_immediate.h" +#include "GPU_immediate_util.h" +#include "GPU_matrix.h" +#include "GPU_state.h" + +#include "ED_sculpt.h" + +#include "bmesh.h" +#include "bmesh_tools.h" + +#include +#include + +static const char array_symmetry_pass_cd_name[] = "v_symmetry_pass"; +static const char array_instance_cd_name[] = "v_array_instance"; + +#define ARRAY_INSTANCE_ORIGINAL -1 + +static void sculpt_vertex_array_data_get(SculptArray *array, + const int vertex, + int *r_copy, + int *r_symmetry_pass) +{ + if (!array->copy_index) { + printf("NO ARRAY COPY\n"); + *r_copy = ARRAY_INSTANCE_ORIGINAL; + *r_symmetry_pass = 0; + return; + } + *r_copy = array->copy_index[vertex]; + *r_symmetry_pass = array->symmetry_pass[vertex]; +} + +static void sculpt_array_datalayers_init(Object *ob, SculptArray *array, SculptSession *ss) +{ + SculptAttributeParams params = {.permanent = true, .simple_array = false}; + + array->scl_inst = BKE_sculpt_attribute_ensure( + ob, ATTR_DOMAIN_POINT, CD_PROP_INT32, array_instance_cd_name, ¶ms); + array->scl_sym = BKE_sculpt_attribute_ensure( + ob, ATTR_DOMAIN_POINT, CD_PROP_INT32, array_symmetry_pass_cd_name, ¶ms); +} + +static void sculpt_array_datalayers_add(SculptArray *array, SculptSession *ss, Mesh *mesh) +{ + + // int *v_array_instance = CustomData_add_layer_named( + // &mesh->vdata, CD_PROP_INT32, CD_CALLOC, NULL, mesh->totvert, array_instance_cd_name); + int totvert = SCULPT_vertex_count_get(ss); + const SculptAttribute *scl = array->scl_inst; + + for (int i = 0; i < totvert; i++) { + PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); + + *SCULPT_vertex_attr_get(vertex, scl) = ARRAY_INSTANCE_ORIGINAL; + *SCULPT_vertex_attr_get(vertex, array->scl_sym) = 0; + } +} + +void SCULPT_array_datalayers_free(SculptArray *array, Object *ob) +{ + SculptSession *ss = ob->sculpt; + +#if 0 + Mesh *mesh = BKE_object_get_original_mesh(ob); + + // update cdata pointers for SCULPT_attr_release_layer + if (BKE_pbvh_type(ss->pbvh) != PBVH_BMESH) { + ss->vdata = &mesh->vdata; + ss->edata = &mesh->edata; + ss->ldata = &mesh->ldata; + ss->pdata = &mesh->pdata; + + ss->mvert = (MVert *)CustomData_get_layer(ss->vdata, CD_MVERT); + ss->medge = (MEdge *)CustomData_get_layer(ss->edata, CD_MEDGE); + ss->mloop = (MLoop *)CustomData_get_layer(ss->ldata, CD_MLOOP); + ss->mpoly = (MPoly *)CustomData_get_layer(ss->pdata, CD_MPOLY); + + ss->face_sets = (int *)CustomData_get_layer(ss->pdata, CD_SCULPT_FACE_SETS); + + ss->totfaces = ss->totpoly = mesh->totpoly; + ss->totedges = mesh->totedge; + + if (BKE_pbvh_type(ss->pbvh) == PBVH_FACES) { + ss->totvert = mesh->totvert; + } + + BKE_sculptsession_check_sculptverts(ob, ss->pbvh, SCULPT_vertex_count_get(ss)); + } +#endif + + SculptAttributeParams params = {.permanent = true, .simple_array = false}; + + if (array->scl_inst) { + BKE_sculpt_attribute_destroy(ob, array->scl_inst); + } + + if (array->scl_sym) { + BKE_sculpt_attribute_destroy(ob, array->scl_sym); + } + + BKE_sculptsession_update_attr_refs(ob); + + array->scl_inst = NULL; + array->scl_sym = NULL; + +#if 0 + // Mesh *mesh = BKE_object_get_original_mesh(ob); + int v_layer_index = CustomData_get_named_layer_index( + &mesh->vdata, CD_PROP_INT32, array_instance_cd_name); + if (v_layer_index != -1) { + CustomData_free_layer(&mesh->vdata, CD_PROP_INT32, mesh->totvert, v_layer_index); + } + + v_layer_index = CustomData_get_named_layer_index( + &mesh->vdata, CD_PROP_INT32, array_symmetry_pass_cd_name); + if (v_layer_index != -1) { + CustomData_free_layer(&mesh->vdata, CD_PROP_INT32, mesh->totvert, v_layer_index); + } +#endif +} + +const float source_geometry_threshold = 0.5f; + +static BMesh *sculpt_array_source_build(Object *ob, Brush *brush, SculptArray *array) +{ + bool have_bmesh = ob->sculpt->bm && ob->sculpt->pbvh && + BKE_pbvh_type(ob->sculpt->pbvh) == PBVH_BMESH; + + Mesh *sculpt_mesh = BKE_object_get_original_mesh(ob); + + BMesh *srcbm; + + BMesh *BM_mesh_copy_ex(BMesh * bm_old, struct BMeshCreateParams * params); + + if (have_bmesh) { + srcbm = BM_mesh_copy_ex( + ob->sculpt->bm, + &((struct BMeshCreateParams){.use_toolflags = true, + .id_map = false, + .id_elem_mask = ob->sculpt->bm->idmap.flag & + (BM_VERT | BM_EDGE | BM_FACE | BM_LOOP), + .create_unique_ids = true, + .copy_all_layers = true})); + } + else { + const BMAllocTemplate allocsizea = BMALLOC_TEMPLATE_FROM_ME(sculpt_mesh); + srcbm = BM_mesh_create(&allocsizea, + &((struct BMeshCreateParams){ + .use_toolflags = true, + })); + + BM_mesh_bm_from_me(NULL, + srcbm, + sculpt_mesh, + &((struct BMeshFromMeshParams){ + .calc_face_normal = true, + .active_shapekey = ob->shapenr, + .use_shapekey = true, + .create_shapekey_layers = true, + })); + } + + BM_mesh_elem_table_ensure(srcbm, BM_VERT); + BM_mesh_elem_index_ensure(srcbm, BM_VERT); + + int vert_count = 0; + zero_v3(array->source_origin); + + SculptSession *ss = ob->sculpt; + for (int i = 0; i < srcbm->totvert; i++) { + PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); + + AutomaskingNodeData automask_data = {0}; + automask_data.have_orig_data = true; + automask_data.orig_data.co = SCULPT_vertex_origco_get(ss, vertex); + automask_data.orig_data.no = SCULPT_vertex_origno_get(ss, vertex); + + const float automask = SCULPT_automasking_factor_get( + ss->cache->automasking, ss, vertex, &automask_data); + const float mask = 1.0f - SCULPT_vertex_mask_get(ss, vertex); + const float influence = mask * automask; + + BMVert *vert = BM_vert_at_index(srcbm, i); + if (influence >= source_geometry_threshold) { + vert_count++; + add_v3_v3(array->source_origin, vert->co); + continue; + } + BM_elem_flag_set(vert, BM_ELEM_TAG, true); + } + + if (vert_count == 0) { + return srcbm; + } + + mul_v3_fl(array->source_origin, 1.0f / vert_count); + + /* TODO(pablodp606): Handle individual Face Sets for Face Set automasking. */ + BM_mesh_delete_hflag_context(srcbm, BM_ELEM_TAG, DEL_VERTS); + + const bool fill_holes = brush->flag2 & BRUSH_ARRAY_FILL_HOLES; + if (fill_holes) { + BM_mesh_elem_hflag_disable_all(srcbm, BM_VERT | BM_EDGE | BM_FACE, BM_ELEM_TAG, false); + BM_mesh_elem_hflag_enable_all(srcbm, BM_EDGE, BM_ELEM_TAG, false); + BM_mesh_edgenet(srcbm, false, true); + BM_mesh_normals_update(srcbm); + BMO_op_callf(srcbm, + (BMO_FLAG_DEFAULTS & ~BMO_FLAG_RESPECT_HIDE), + "triangulate faces=%hf quad_method=%i ngon_method=%i", + BM_ELEM_TAG, + 0, + 0); + + BM_mesh_elem_hflag_enable_all(srcbm, BM_FACE, BM_ELEM_TAG, false); + BMO_op_callf(srcbm, + (BMO_FLAG_DEFAULTS & ~BMO_FLAG_RESPECT_HIDE), + "recalc_face_normals faces=%hf", + BM_ELEM_TAG); + BM_mesh_elem_hflag_disable_all(srcbm, BM_VERT | BM_EDGE | BM_FACE, BM_ELEM_TAG, false); + } + + return srcbm; +} + +void sculpt_array_source_datalayer_update(BMesh *bm, const int symm_pass, const int copy_index) +{ + const int cd_array_instance_index = CustomData_get_named_layer_index( + &bm->vdata, CD_PROP_INT32, array_instance_cd_name); + const int cd_array_instance_offset = CustomData_get_n_offset( + &bm->vdata, CD_PROP_INT32, cd_array_instance_index); + + const int cd_array_symm_pass_index = CustomData_get_named_layer_index( + &bm->vdata, CD_PROP_INT32, array_symmetry_pass_cd_name); + const int cd_array_symm_pass_offset = CustomData_get_n_offset( + &bm->vdata, CD_PROP_INT32, cd_array_symm_pass_index); + + BM_mesh_elem_table_ensure(bm, BM_VERT); + BM_mesh_elem_index_ensure(bm, BM_VERT); + + BMVert *v; + BMIter iter; + BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) { + BM_ELEM_CD_SET_INT(v, cd_array_instance_offset, copy_index); + BM_ELEM_CD_SET_INT(v, cd_array_symm_pass_offset, symm_pass); + } +} + +static void sculpt_array_final_mesh_write(Object *ob, BMesh *final_mesh) +{ + SculptSession *ss = ob->sculpt; + Mesh *sculpt_mesh = BKE_object_get_original_mesh(ob); + Mesh *result = BKE_mesh_from_bmesh_for_eval_nomain(final_mesh, NULL, sculpt_mesh); + + BKE_mesh_normals_tag_dirty(result); + BKE_mesh_nomain_to_mesh(result, ob->data, ob); + BKE_mesh_batch_cache_dirty_tag(ob->data, BKE_MESH_BATCH_DIRTY_ALL); + + const int next_face_set_id = ED_sculpt_face_sets_find_next_available_id(ob->data); + ED_sculpt_face_sets_initialize_none_to_id(ob->data, next_face_set_id); + + ss->needs_pbvh_rebuild = true; +} + +static void sculpt_array_ensure_geometry_indices(Object *ob, SculptArray *array) +{ + if (array->copy_index) { + return; + } + + printf("ALLOCATION COPY INDEX\n"); + + SculptSession *ss = ob->sculpt; + int totvert = SCULPT_vertex_count_get(ss); + + BKE_sculptsession_update_attr_refs(ob); + + SculptAttributeParams params = {.permanent = true, .simple_array = false}; + + array->scl_inst = BKE_sculpt_attribute_ensure( + ob, ATTR_DOMAIN_POINT, CD_PROP_INT32, array_instance_cd_name, ¶ms); + array->scl_sym = BKE_sculpt_attribute_ensure( + ob, ATTR_DOMAIN_POINT, CD_PROP_INT32, array_symmetry_pass_cd_name, ¶ms); + + array->copy_index = MEM_malloc_arrayN(totvert, sizeof(int), "array copy index"); + array->symmetry_pass = MEM_malloc_arrayN(totvert, sizeof(int), "array symmetry pass index"); + + for (int i = 0; i < totvert; i++) { + PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); + + array->copy_index[i] = *(int *)SCULPT_vertex_attr_get(vertex, array->scl_inst); + array->symmetry_pass[i] = *(int *)SCULPT_vertex_attr_get(vertex, array->scl_sym); + } + + SCULPT_array_datalayers_free(array, ob); +} + +static void sculpt_array_mesh_build(Sculpt *sd, Object *ob, SculptArray *array) +{ + bool have_bmesh = ob->sculpt->bm && ob->sculpt->pbvh && + BKE_pbvh_type(ob->sculpt->pbvh) == PBVH_BMESH; + + Mesh *sculpt_mesh = BKE_object_get_original_mesh(ob); + Brush *brush = BKE_paint_brush(&sd->paint); + + sculpt_array_datalayers_init(ob, array, ob->sculpt); + sculpt_array_datalayers_add(array, ob->sculpt, sculpt_mesh); + + BMesh *srcbm = sculpt_array_source_build(ob, brush, array); + + BMesh *destbm; + const BMAllocTemplate allocsizeb = BMALLOC_TEMPLATE_FROM_ME(sculpt_mesh); + + if (!have_bmesh) { + destbm = BM_mesh_create(&allocsizeb, + &((struct BMeshCreateParams){ + .use_toolflags = true, + })); + BM_mesh_bm_from_me(NULL, + destbm, + sculpt_mesh, + &((struct BMeshFromMeshParams){ + .calc_face_normal = true, + .active_shapekey = ob->shapenr, + .use_shapekey = true, + .create_shapekey_layers = true, + })); + } + else { + destbm = ob->sculpt->bm; + } + + BM_mesh_toolflags_set(destbm, true); + BM_mesh_toolflags_set(srcbm, true); + + BM_mesh_elem_toolflags_ensure(destbm); + BM_mesh_elem_toolflags_ensure(srcbm); + + const char symm = SCULPT_mesh_symmetry_xyz_get(ob); + for (char symm_it = 0; symm_it <= symm; symm_it++) { + if (!SCULPT_is_symmetry_iteration_valid(symm_it, symm)) { + continue; + } + + for (int copy_index = 0; copy_index < array->num_copies; copy_index++) { + sculpt_array_source_datalayer_update(srcbm, symm_it, copy_index); + + if (1) { //! have_bmesh) { + // BM_mesh_copy_init_customdata(destbm, srcbm, &bm_mesh_allocsize_default); + } + + const int opflag = (BMO_FLAG_DEFAULTS & ~BMO_FLAG_RESPECT_HIDE); + BMO_op_callf(srcbm, opflag, "duplicate geom=%avef dest=%p", destbm); + } + } + + if (!have_bmesh) { + sculpt_array_final_mesh_write(ob, destbm); + BM_mesh_free(destbm); + } + else { + BKE_sculptsession_update_attr_refs(ob); + ob->sculpt->needs_pbvh_rebuild = true; + } + + BM_mesh_free(srcbm); +} + +static SculptArray *sculpt_array_cache_create(Object *ob, + eBrushArrayDeformType deform_type, + const int num_copies) +{ + + SculptArray *array = MEM_callocN(sizeof(SculptArray), "Sculpt Array"); + array->num_copies = num_copies; + + array->mode = deform_type; + + const char symm = SCULPT_mesh_symmetry_xyz_get(ob); + for (char symm_it = 0; symm_it <= symm; symm_it++) { + if (!SCULPT_is_symmetry_iteration_valid(symm_it, symm)) { + continue; + } + array->copies[symm_it] = MEM_malloc_arrayN( + num_copies, sizeof(SculptArrayCopy), "Sculpt array copies"); + } + return array; +} + +static void sculpt_array_cache_free(SculptArray *array) +{ + return; + for (int symm_pass = 0; symm_pass < PAINT_SYMM_AREAS; symm_pass++) { + MEM_SAFE_FREE(array->copies[symm_pass]); + } + MEM_freeN(array->copy_index); + MEM_freeN(array->symmetry_pass); + MEM_freeN(array); +} + +static void sculpt_array_init(Object *ob, Brush *brush, SculptArray *array) +{ + SculptSession *ss = ob->sculpt; + + /* TODO: add options. */ + copy_v3_v3(array->normal, ss->cache->view_normal); + array->radial_angle = 2.0f * M_PI; + + for (int symm_pass = 0; symm_pass < PAINT_SYMM_AREAS; symm_pass++) { + if (array->copies[symm_pass] == NULL) { + continue; + } + for (int copy_index = 0; copy_index < array->num_copies; copy_index++) { + SculptArrayCopy *copy = &array->copies[symm_pass][copy_index]; + unit_m4(copy->mat); + copy->symm_pass = symm_pass; + copy->index = copy_index; + float symm_location[3]; + flip_v3_v3(symm_location, ss->cache->location, symm_pass); + copy_v3_v3(copy->origin, ss->cache->location); + } + } +} + +static void sculpt_array_position_in_path_search( + float *r_position, float *r_direction, float *r_scale, SculptArray *array, const int index) +{ + const float path_length = array->path.points[array->path.tot_points - 1].length; + const float step_distance = path_length / (float)array->num_copies; + const float copy_distance = step_distance * (index + 1); + + if (array->path.tot_points == 1) { + zero_v3(r_position); + if (r_direction) { + zero_v3(r_direction); + } + if (r_scale) { + *r_scale = 1.0f; + } + return; + } + + for (int i = 1; i < array->path.tot_points; i++) { + ScultpArrayPathPoint *path_point = &array->path.points[i]; + if (copy_distance >= path_point->length) { + continue; + } + ScultpArrayPathPoint *prev_path_point = &array->path.points[i - 1]; + + const float remaining_dist = copy_distance - prev_path_point->length; + const float segment_length = path_point->length - prev_path_point->length; + const float interp_factor = remaining_dist / segment_length; + interp_v3_v3v3(r_position, prev_path_point->co, path_point->co, interp_factor); + if (r_direction) { + if (i == array->path.tot_points - 1) { + copy_v3_v3(r_direction, prev_path_point->direction); + } + else { + copy_v3_v3(r_direction, path_point->direction); + } + } + if (r_scale) { + const float s = 1.0f - interp_factor; + *r_scale = s * prev_path_point->strength + interp_factor * path_point->strength; + } + return; + } + + ScultpArrayPathPoint *last_path_point = &array->path.points[array->path.tot_points - 1]; + copy_v3_v3(r_position, last_path_point->co); + if (r_direction) { + ScultpArrayPathPoint *prev_path_point = &array->path.points[array->path.tot_points - 2]; + copy_v3_v3(r_direction, prev_path_point->direction); + } + if (r_scale) { + ScultpArrayPathPoint *prev_path_point = &array->path.points[array->path.tot_points - 2]; + *r_scale = prev_path_point->strength; + } +} + +static void scultp_array_basis_from_direction(float r_mat[4][4], + SculptArray *array, + const float direction[3]) +{ + float direction_normalized[3]; + normalize_v3_v3(direction_normalized, direction); + copy_v3_v3(r_mat[0], direction_normalized); + cross_v3_v3v3(r_mat[2], r_mat[0], array->normal); + cross_v3_v3v3(r_mat[1], r_mat[0], r_mat[2]); + normalize_v3(r_mat[0]); + normalize_v3(r_mat[1]); + normalize_v3(r_mat[2]); +} + +static float *sculpt_array_delta_from_path(SculptArray *array) +{ + return array->path.points[array->path.tot_points - 1].co; +} + +static void sculpt_array_update_copy(StrokeCache *cache, + SculptArray *array, + SculptArrayCopy *copy, + Brush *brush) +{ + + unit_m4(copy->mat); + + float scale = 1.0f; + float direction[3]; + + BrushChannelSet *chset = cache->channels_final ? cache->channels_final : brush->channels; + eBrushArrayDeformType array_type = BRUSHSET_GET_INT( + chset, array_deform_type, &cache->input_mapping); + + float delta[3]; + copy_v3_v3(delta, sculpt_array_delta_from_path(array)); + + switch (array_type) { + case BRUSH_ARRAY_DEFORM_LINEAR: { + const float fade = ((float)copy->index + 1.0f) / (float)(array->num_copies); + mul_v3_v3fl(copy->mat[3], delta, fade); + normalize_v3_v3(direction, delta); + scale = cache->bstrength; + } break; + + case BRUSH_ARRAY_DEFORM_RADIAL: { + float pos[3]; + const float fade = ((float)copy->index + 1.0f) / (float)(array->num_copies); + copy_v3_v3(pos, delta); + rotate_v3_v3v3fl(copy->mat[3], pos, array->normal, fade * array->radial_angle); + copy_v3_v3(direction, copy->mat[3]); + // sub_v3_v3v3(direction, copy->mat[3], array->source_origin); + scale = cache->bstrength; + } break; + case BRUSH_ARRAY_DEFORM_PATH: + sculpt_array_position_in_path_search(copy->mat[3], direction, &scale, array, copy->index); + break; + } + + if (!(brush->flag2 & BRUSH_ARRAY_LOCK_ORIENTATION)) { + scultp_array_basis_from_direction(copy->mat, array, direction); + } + + /* + copy->mat[3][0] += (BLI_hash_int_01(copy->index) * 2.0f - 0.5f) * cache->radius; + copy->mat[3][1] += (BLI_hash_int_01(copy->index + 1) * 2.0f - 0.5f) * cache->radius; + copy->mat[3][2] += (BLI_hash_int_01(copy->index + 2) * 2.0f - 0.5f) * cache->radius; + */ + + mul_v3_fl(copy->mat[0], scale); + mul_v3_fl(copy->mat[1], scale); + mul_v3_fl(copy->mat[2], scale); + + /* + copy->mat[0][0] = scale; + copy->mat[1][1] = scale; + copy->mat[2][2] = scale; + */ +} + +static void sculpt_array_update(Object *ob, Brush *brush, SculptArray *array) +{ + SculptSession *ss = ob->sculpt; + + /* Main symmetry pass. */ + for (int copy_index = 0; copy_index < array->num_copies; copy_index++) { + SculptArrayCopy *copy = &array->copies[0][copy_index]; + unit_m4(copy->mat); + sculpt_array_update_copy(ss->cache, array, copy, brush); + } + + for (int symm_pass = 1; symm_pass < PAINT_SYMM_AREAS; symm_pass++) { + if (array->copies[symm_pass] == NULL) { + continue; + } + + float symm_orig[3]; + flip_v3_v3(symm_orig, array->source_origin, symm_pass); + + for (int copy_index = 0; copy_index < array->num_copies; copy_index++) { + SculptArrayCopy *copy = &array->copies[symm_pass][copy_index]; + SculptArrayCopy *main_copy = &array->copies[0][copy_index]; + unit_m4(copy->mat); + for (int m = 0; m < 4; m++) { + flip_v3_v3(copy->mat[m], main_copy->mat[m], symm_pass); + } + } + } + + for (int symm_pass = 0; symm_pass < PAINT_SYMM_AREAS; symm_pass++) { + + if (array->copies[symm_pass] == NULL) { + continue; + } + for (int copy_index = 0; copy_index < array->num_copies; copy_index++) { + SculptArrayCopy *copy = &array->copies[symm_pass][copy_index]; + invert_m4_m4(copy->imat, copy->mat); + } + } +} + +static void do_array_deform_task_cb_ex(void *__restrict userdata, + const int n, + const TaskParallelTLS *__restrict tls) +{ + SculptThreadedTaskData *data = userdata; + SculptSession *ss = data->ob->sculpt; + SculptArray *array = ss->array; + + bool any_modified = false; + + PBVHVertexIter vd; + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { + int array_index = ARRAY_INSTANCE_ORIGINAL; + int array_symm_pass = 0; + sculpt_vertex_array_data_get(array, vd.index, &array_index, &array_symm_pass); + + if (array_index == ARRAY_INSTANCE_ORIGINAL) { + continue; + } + + SculptArrayCopy *copy = &array->copies[array_symm_pass][array_index]; + + float co[3]; + copy_v3_v3(co, array->orco[vd.index]); + mul_v3_m4v3(co, array->source_imat, array->orco[vd.index]); + mul_v3_m4v3(co, copy->mat, co); + float source_origin_symm[3]; + flip_v3_v3(source_origin_symm, array->source_origin, array_symm_pass); + add_v3_v3v3(vd.co, co, source_origin_symm); + + any_modified = true; + + if (vd.is_mesh) { + BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); + } + } + BKE_pbvh_vertex_iter_end; + + if (any_modified) { + BKE_pbvh_node_mark_update(data->nodes[n]); + } +} + +static void sculpt_array_deform(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) +{ + /* Threaded loop over nodes. */ + SculptThreadedTaskData data = { + .sd = sd, + .ob = ob, + .nodes = nodes, + }; + + TaskParallelSettings settings; + BKE_pbvh_parallel_range_settings(&settings, true, totnode); + BLI_task_parallel_range(0, totnode, &data, do_array_deform_task_cb_ex, &settings); +} + +static void do_array_smooth_task_cb_ex(void *__restrict userdata, + const int n, + const TaskParallelTLS *__restrict tls) +{ + SculptThreadedTaskData *data = userdata; + SculptSession *ss = data->ob->sculpt; + SculptArray *array = ss->array; + + bool any_modified = false; + + bool check_fsets = ss->cache->brush->flag2 & BRUSH_SMOOTH_PRESERVE_FACE_SETS; + const bool weighted = (ss->cache->brush->flag2 & BRUSH_SMOOTH_USE_AREA_WEIGHT); + + PBVHVertexIter vd; + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { + int array_index = ARRAY_INSTANCE_ORIGINAL; + int array_symm_pass = 0; + sculpt_vertex_array_data_get(array, vd.index, &array_index, &array_symm_pass); + + const float fade = array->smooth_strength[vd.index]; + + if (fade == 0.0f) { + continue; + } + + float smooth_co[3]; + SCULPT_neighbor_coords_average( + ss, smooth_co, vd.vertex, ss->cache->brush->autosmooth_projection, check_fsets, weighted); + float disp[3]; + sub_v3_v3v3(disp, smooth_co, vd.co); + mul_v3_fl(disp, fade); + add_v3_v3(vd.co, disp); + + /* + if (array_index == ARRAY_INSTANCE_ORIGINAL) { + continue; + } + + bool do_smooth = false; + SculptVertexNeighborIter ni; + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.index, ni) { + int neighbor_array_index = ARRAY_INSTANCE_ORIGINAL; + int neighbor_symm_pass = 0; + sculpt_vertex_array_data_get(array, ni.index, &neighbor_array_index,&neighbor_symm_pass); + if (neighbor_array_index != array_index) { + do_smooth = true; + } + } + SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); + + if (!do_smooth) { + continue; + } + */ + + any_modified = true; + + if (vd.is_mesh) { + BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); + } + } + BKE_pbvh_vertex_iter_end; + + if (any_modified) { + BKE_pbvh_node_mark_update(data->nodes[n]); + } +} + +static void sculpt_array_smooth(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) +{ + + /* Threaded loop over nodes. */ + SculptSession *ss = ob->sculpt; + SculptArray *array = ss->array; + + if (!array) { + return; + } + + if (!array->smooth_strength) { + return; + } + SculptThreadedTaskData data = { + .sd = sd, + .ob = ob, + .nodes = nodes, + }; + + TaskParallelSettings settings; + BKE_pbvh_parallel_range_settings(&settings, true, totnode); + BLI_task_parallel_range(0, totnode, &data, do_array_smooth_task_cb_ex, &settings); +} + +static void sculpt_array_ensure_original_coordinates(Object *ob, SculptArray *array) +{ + SculptSession *ss = ob->sculpt; + const int totvert = SCULPT_vertex_count_get(ss); + + if (array->orco) { + return; + } + + array->orco = MEM_malloc_arrayN(totvert, sizeof(float) * 3, "array orco"); + + for (int i = 0; i < totvert; i++) { + PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); + + copy_v3_v3(array->orco[i], SCULPT_vertex_co_get(ss, vertex)); + } +} + +static void sculpt_array_ensure_base_transform(Sculpt *sd, Object *ob, SculptArray *array) +{ + SculptSession *ss = ob->sculpt; + Brush *brush = BKE_paint_brush(&sd->paint); + + if (array->source_mat_valid) { + return; + } + + unit_m4(array->source_mat); + + if (brush->flag2 & BRUSH_ARRAY_LOCK_ORIENTATION) { + unit_m4(array->source_mat); + copy_v3_v3(array->source_mat[3], array->source_origin); + invert_m4_m4(array->source_imat, array->source_mat); + array->source_mat_valid = true; + return; + } + + if (is_zero_v3(ss->cache->grab_delta)) { + return; + } + + scultp_array_basis_from_direction(array->source_mat, array, ss->cache->grab_delta); + copy_v3_v3(array->source_mat[3], array->source_origin); + invert_m4_m4(array->source_imat, array->source_mat); + + array->source_mat_valid = true; + return; +} + +static void sculpt_array_path_point_update(SculptArray *array, const int path_point_index) +{ + if (path_point_index == 0) { + return; + } + + const int prev_path_point_index = path_point_index - 1; + + ScultpArrayPathPoint *path_point = &array->path.points[path_point_index]; + ScultpArrayPathPoint *prev_path_point = &array->path.points[prev_path_point_index]; + + if (len_v3v3(prev_path_point->co, path_point->co) <= 0.0001f) { + return; + } + sub_v3_v3v3(prev_path_point->direction, path_point->co, prev_path_point->co); + path_point->length = prev_path_point->length + normalize_v3(prev_path_point->direction); +} + +static void sculpt_array_stroke_sample_add(Object *ob, SculptArray *array) +{ + SculptSession *ss = ob->sculpt; + + if (!array->path.points) { + array->path.points = MEM_malloc_arrayN(9999, sizeof(ScultpArrayPathPoint), "Array Path"); + } + + const int current_point_index = array->path.tot_points; + const int prev_point_index = current_point_index - 1; + + ScultpArrayPathPoint *path_point = &array->path.points[current_point_index]; + + // add_v3_v3v3(path_point->co, ss->cache->orig_grab_location, ss->cache->grab_delta); + copy_v3_v3(path_point->co, ss->cache->grab_delta); + path_point->strength = ss->cache->bstrength; + + if (current_point_index == 0) { + /* First point of the path. */ + path_point->length = 0.0f; + } + else { + ScultpArrayPathPoint *prev_path_point = &array->path.points[prev_point_index]; + if (len_v3v3(prev_path_point->co, path_point->co) <= 0.0001f) { + return; + } + sub_v3_v3v3(prev_path_point->direction, path_point->co, prev_path_point->co); + path_point->length = prev_path_point->length + normalize_v3(prev_path_point->direction); + } + + array->path.tot_points++; +} + +void SCULPT_do_array_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) +{ + SculptSession *ss = ob->sculpt; + Brush *brush = BKE_paint_brush(&sd->paint); + + if (ss->cache->invert) { + if (!ss->array) { + return; + } + + if (SCULPT_stroke_is_first_brush_step(ss->cache)) { + SculptArray *array = ss->array; + const int totvert = SCULPT_vertex_count_get(ss); + + /* Rebuild smooth strength cache. */ + MEM_SAFE_FREE(array->smooth_strength); + array->smooth_strength = MEM_calloc_arrayN(sizeof(float), totvert, "smooth_strength"); + + for (int i = 0; i < totvert; i++) { + PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); + + int array_index = ARRAY_INSTANCE_ORIGINAL; + int array_symm_pass = 0; + sculpt_vertex_array_data_get(array, i, &array_index, &array_symm_pass); + + if (array_index == ARRAY_INSTANCE_ORIGINAL) { + continue; + } + + /* TODO: this can be cached. */ + SculptVertexNeighborIter ni; + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vertex, ni) { + int neighbor_array_index = ARRAY_INSTANCE_ORIGINAL; + int neighbor_symm_pass = 0; + sculpt_vertex_array_data_get( + array, ni.index, &neighbor_array_index, &neighbor_symm_pass); + if (neighbor_array_index != array_index) { + array->smooth_strength[i] = 1.0f; + break; + } + } + SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); + } + + for (int smooth_iterations = 0; smooth_iterations < 4; smooth_iterations++) { + for (int i = 0; i < totvert; i++) { + PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); + + float avg = array->smooth_strength[i]; + int count = 1; + SculptVertexNeighborIter ni; + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vertex, ni) { + avg += array->smooth_strength[ni.index]; + count++; + } + SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); + array->smooth_strength[i] = avg / count; + } + } + + /* Update Array Path Orco. */ + for (int i = 0; i < array->path.tot_points; i++) { + ScultpArrayPathPoint *point = &array->path.points[i]; + copy_v3_v3(point->orco, point->co); + } + array->initial_radial_angle = array->radial_angle; + + /* Update Geometry Orco. */ + for (int i = 0; i < totvert; i++) { + PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); + + int array_index = ARRAY_INSTANCE_ORIGINAL; + int array_symm_pass = 0; + sculpt_vertex_array_data_get(array, i, &array_index, &array_symm_pass); + + if (array_index == ARRAY_INSTANCE_ORIGINAL) { + continue; + } + SculptArrayCopy *copy = &array->copies[array_symm_pass][array_index]; + // sub_v3_v3v3(array->orco[i], SCULPT_vertex_co_get(ss, i), copy->mat[3]); + float co[3]; + float source_origin_symm[3]; + copy_v3_v3(co, SCULPT_vertex_co_get(ss, vertex)); + flip_v3_v3(source_origin_symm, array->source_origin, array_symm_pass); + mul_v3_m4v3(co, copy->imat, co); + mul_v3_m4v3(co, array->source_imat, co); + // sub_v3_v3v3(co, co, source_origin_symm); + + copy_v3_v3(array->orco[i], co); + } + } + + SculptArray *array = ss->array; + if (array->mode == BRUSH_ARRAY_DEFORM_PATH) { + /* Deform path */ + for (int i = 0; i < array->path.tot_points; i++) { + ScultpArrayPathPoint *point = &array->path.points[i]; + float point_co[3]; + add_v3_v3v3(point_co, point->orco, array->source_origin); + const float len = len_v3v3(ss->cache->true_location, point_co); + const float fade = ss->cache->bstrength * + BKE_brush_curve_strength(brush, len, ss->cache->radius); + if (fade <= 0.0f) { + continue; + } + madd_v3_v3v3fl(point->co, point->orco, ss->cache->grab_delta, fade); + } + for (int i = 0; i < array->path.tot_points; i++) { + sculpt_array_path_point_update(array, i); + } + } + else { + /* Tweak radial angle. */ + /* + const float factor = 1.0f - ( len_v3(ss->cache->grab_delta) / ss->cache->initial_radius); + array->radial_angle = array->initial_radial_angle * clamp_f(factor, 0.0f, 1.0f); + */ + + float array_disp_co[3]; + float brush_co[3]; + add_v3_v3v3(brush_co, ss->cache->initial_location, ss->cache->grab_delta); + sub_v3_v3(brush_co, array->source_origin); + normalize_v3(brush_co); + normalize_v3_v3(array_disp_co, sculpt_array_delta_from_path(array)); + array->radial_angle = angle_signed_on_axis_v3v3_v3(brush_co, array_disp_co, array->normal); + } + + sculpt_array_update(ob, brush, ss->array); + sculpt_array_deform(sd, ob, nodes, totnode); + for (int i = 0; i < 5; i++) { + sculpt_array_smooth(sd, ob, nodes, totnode); + } + + return; + } + + if (SCULPT_get_int(ss, array_count, sd, brush) == 0) { + return; + } + + if (!SCULPT_stroke_is_main_symmetry_pass(ss->cache)) { + /* This brush manages its own symmetry. */ + return; + } + + if (SCULPT_stroke_is_first_brush_step(ss->cache)) { + if (ss->array) { + sculpt_array_cache_free(ss->array); + } + + ss->array = sculpt_array_cache_create(ob, + SCULPT_get_int(ss, array_deform_type, sd, brush), + SCULPT_get_int(ss, array_count, sd, brush)); + sculpt_array_init(ob, brush, ss->array); + sculpt_array_stroke_sample_add(ob, ss->array); + sculpt_array_mesh_build(sd, ob, ss->array); + /* Original coordinates can't be stored yet as the SculptSession data needs to be updated after + * the mesh modifications performed when building the array geometry. */ + return; + } + + SCULPT_vertex_random_access_ensure(ss); + + sculpt_array_ensure_base_transform(sd, ob, ss->array); + sculpt_array_ensure_original_coordinates(ob, ss->array); + sculpt_array_ensure_geometry_indices(ob, ss->array); + + sculpt_array_stroke_sample_add(ob, ss->array); + + sculpt_array_update(ob, brush, ss->array); + + sculpt_array_deform(sd, ob, nodes, totnode); +} + +void SCULPT_array_path_draw(const uint gpuattr, Brush *brush, SculptSession *ss) +{ + + SculptArray *array = ss->array; + + /* Disable debug drawing. */ + return; + + if (!array) { + return; + } + + if (!array->path.points) { + return; + } + + if (array->path.tot_points < 2) { + return; + } + + const int tot_points = array->path.tot_points; + immBegin(GPU_PRIM_LINE_STRIP, tot_points); + for (int i = 0; i < tot_points; i++) { + float co[3]; + copy_v3_v3(co, array->path.points[i].co); + add_v3_v3(co, array->source_origin); + immVertex3fv(gpuattr, co); + } + immEnd(); +} diff --git a/source/blender/editors/sculpt_paint/sculpt_automasking.cc b/source/blender/editors/sculpt_paint/sculpt_automasking.cc index 7801741d5d8..61b287f2b77 100644 --- a/source/blender/editors/sculpt_paint/sculpt_automasking.cc +++ b/source/blender/editors/sculpt_paint/sculpt_automasking.cc @@ -257,13 +257,13 @@ static float automasking_view_normal_factor(AutomaskingCache *automasking, static float automasking_view_occlusion_factor(AutomaskingCache *automasking, SculptSession *ss, PBVHVertRef vertex, - uchar stroke_id, - AutomaskingNodeData * /*automask_data*/) + AutomaskingNodeData * /*automask_data*/, + bool force = false) { - char f = *(char *)SCULPT_vertex_attr_get(vertex, ss->attrs.automasking_occlusion); + char f = *SCULPT_vertex_attr_get(vertex, ss->attrs.automasking_occlusion); - if (stroke_id != automasking->current_stroke_id) { - f = *(char *)SCULPT_vertex_attr_get( + if (force || SCULPT_stroke_id_test(ss, vertex, STROKEID_USER_OCCLUSION)) { + f = *SCULPT_vertex_attr_get( vertex, ss->attrs.automasking_occlusion) = SCULPT_vertex_is_occluded(ss, vertex, true) ? 2 : 1; } @@ -277,9 +277,8 @@ static float automasking_factor_end(SculptSession *ss, PBVHVertRef vertex, float value) { - if (ss->attrs.automasking_stroke_id) { - *(uchar *)SCULPT_vertex_attr_get( - vertex, ss->attrs.automasking_stroke_id) = automasking->current_stroke_id; + if (ss->attrs.stroke_id) { + SCULPT_stroke_id_test(ss, vertex, STROKEID_USER_AUTOMASKING); } return value; @@ -439,7 +438,7 @@ static void sculpt_calc_blurred_cavity(SculptSession *ss, factor_sum = sculpt_cavity_calc_factor(automasking, factor_sum); - *(float *)SCULPT_vertex_attr_get(vertex, ss->attrs.automasking_cavity) = factor_sum; + *SCULPT_vertex_attr_get(vertex, ss->attrs.automasking_cavity) = factor_sum; } int SCULPT_automasking_settings_hash(Object *ob, AutomaskingCache *automasking) @@ -493,13 +492,11 @@ static float sculpt_automasking_cavity_factor(AutomaskingCache *automasking, SculptSession *ss, PBVHVertRef vertex) { - uchar stroke_id = *(uchar *)SCULPT_vertex_attr_get(vertex, ss->attrs.automasking_stroke_id); - - if (stroke_id != automasking->current_stroke_id) { + if (SCULPT_stroke_id_test_no_update(ss, vertex, STROKEID_USER_AUTOMASKING)) { sculpt_calc_blurred_cavity(ss, automasking, automasking->settings.cavity_blur_steps, vertex); } - float factor = *(float *)SCULPT_vertex_attr_get(vertex, ss->attrs.automasking_cavity); + float factor = *SCULPT_vertex_attr_get(vertex, ss->attrs.automasking_cavity); bool inverted = automasking->settings.flags & BRUSH_AUTOMASKING_CAVITY_INVERTED; if ((automasking->settings.flags & BRUSH_AUTOMASKING_CAVITY_ALL) && @@ -534,7 +531,7 @@ float SCULPT_automasking_factor_get(AutomaskingCache *automasking, * automasking information can't be computed in real time per vertex and needs to be * initialized for the whole mesh when the stroke starts. */ if (ss->attrs.automasking_factor) { - float factor = *(float *)SCULPT_vertex_attr_get(vert, ss->attrs.automasking_factor); + float factor = *SCULPT_vertex_attr_get(vert, ss->attrs.automasking_factor); if (automasking->settings.flags & BRUSH_AUTOMASKING_CAVITY_ALL) { factor *= sculpt_automasking_cavity_factor(automasking, ss, vert); @@ -543,15 +540,10 @@ float SCULPT_automasking_factor_get(AutomaskingCache *automasking, return automasking_factor_end(ss, automasking, vert, factor * mask); } - uchar stroke_id = ss->attrs.automasking_stroke_id ? - *(uchar *)SCULPT_vertex_attr_get(vert, ss->attrs.automasking_stroke_id) : - -1; - bool do_occlusion = (automasking->settings.flags & (BRUSH_AUTOMASKING_VIEW_OCCLUSION | BRUSH_AUTOMASKING_VIEW_NORMAL)) == (BRUSH_AUTOMASKING_VIEW_OCCLUSION | BRUSH_AUTOMASKING_VIEW_NORMAL); - if (do_occlusion && - automasking_view_occlusion_factor(automasking, ss, vert, stroke_id, automask_data)) { + if (do_occlusion && automasking_view_occlusion_factor(automasking, ss, vert, automask_data)) { return automasking_factor_end(ss, automasking, vert, 0.0f); } @@ -568,7 +560,7 @@ float SCULPT_automasking_factor_get(AutomaskingCache *automasking, } if (automasking->settings.flags & BRUSH_AUTOMASKING_BOUNDARY_EDGES) { - if (SCULPT_vertex_is_boundary(ss, vert)) { + if (SCULPT_vertex_is_boundary(ss, vert, SCULPT_BOUNDARY_MESH)) { return 0.0f; } } @@ -595,7 +587,7 @@ float SCULPT_automasking_factor_get(AutomaskingCache *automasking, return automasking_factor_end(ss, automasking, vert, mask); } -void SCULPT_automasking_cache_free(AutomaskingCache *automasking) +void SCULPT_automasking_cache_free(SculptSession *ss, Object *ob, AutomaskingCache *automasking) { if (!automasking) { return; @@ -616,8 +608,8 @@ static bool automask_floodfill_cb( { AutomaskFloodFillData *data = (AutomaskFloodFillData *)userdata; - *(float *)SCULPT_vertex_attr_get(to_v, ss->attrs.automasking_factor) = 1.0f; - *(float *)SCULPT_vertex_attr_get(from_v, ss->attrs.automasking_factor) = 1.0f; + *SCULPT_vertex_attr_get(to_v, ss->attrs.automasking_factor) = 1.0f; + *SCULPT_vertex_attr_get(from_v, ss->attrs.automasking_factor) = 1.0f; return (!data->use_radius || SCULPT_is_vertex_inside_brush_radius_symm( SCULPT_vertex_co_get(ss, to_v), data->location, data->radius, data->symm)); @@ -637,7 +629,7 @@ static void SCULPT_topology_automasking_init(Sculpt *sd, Object *ob) for (int i : IndexRange(totvert)) { PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); - (*(float *)SCULPT_vertex_attr_get(vertex, ss->attrs.automasking_factor)) = 0.0f; + (*SCULPT_vertex_attr_get(vertex, ss->attrs.automasking_factor)) = 0.0f; } /* Flood fill automask to connected vertices. Limited to vertices inside @@ -678,7 +670,7 @@ static void sculpt_face_sets_automasking_init(Sculpt *sd, Object *ob) PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); if (!SCULPT_vertex_has_face_set(ss, vertex, active_face_set)) { - *(float *)SCULPT_vertex_attr_get(vertex, ss->attrs.automasking_factor) = 0.0f; + *SCULPT_vertex_attr_get(vertex, ss->attrs.automasking_factor) = 0.0f; } } } @@ -705,7 +697,7 @@ static void SCULPT_boundary_automasking_init(Object *ob, edge_distance[i] = EDGE_DISTANCE_INF; switch (mode) { case AUTOMASK_INIT_BOUNDARY_EDGES: - if (SCULPT_vertex_is_boundary(ss, vertex)) { + if (SCULPT_vertex_is_boundary(ss, vertex, SCULPT_BOUNDARY_MESH)) { edge_distance[i] = 0; } break; @@ -743,17 +735,17 @@ static void SCULPT_boundary_automasking_init(Object *ob, const float p = 1.0f - (float(edge_distance[i]) / float(propagation_steps)); const float edge_boundary_automask = pow2f(p); - *(float *)SCULPT_vertex_attr_get( + *SCULPT_vertex_attr_get( vertex, ss->attrs.automasking_factor) *= (1.0f - edge_boundary_automask); } MEM_SAFE_FREE(edge_distance); } -static void SCULPT_automasking_cache_settings_update(AutomaskingCache *automasking, - SculptSession *ss, - Sculpt *sd, - Brush *brush) +void SCULPT_automasking_cache_settings_update(AutomaskingCache *automasking, + SculptSession *ss, + const Sculpt *sd, + const Brush *brush) { automasking->settings.flags = sculpt_automasking_mode_effective_bits(sd, brush); automasking->settings.initial_face_set = SCULPT_active_face_set_get(ss); @@ -789,21 +781,21 @@ static void sculpt_normal_occlusion_automasking_fill(AutomaskingCache *automaski for (int i = 0; i < totvert; i++) { PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); - float f = *(float *)SCULPT_vertex_attr_get(vertex, ss->attrs.automasking_factor); + float f = *SCULPT_vertex_attr_get(vertex, ss->attrs.automasking_factor); if (int(mode) & BRUSH_AUTOMASKING_VIEW_NORMAL) { if (int(mode) & BRUSH_AUTOMASKING_VIEW_OCCLUSION) { - f *= automasking_view_occlusion_factor(automasking, ss, vertex, -1, &nodedata); + f *= automasking_view_occlusion_factor(automasking, ss, vertex, &nodedata, true); } f *= automasking_view_normal_factor(automasking, ss, vertex, &nodedata); } if (ss->attrs.automasking_stroke_id) { - *(uchar *)SCULPT_vertex_attr_get(vertex, ss->attrs.automasking_stroke_id) = ss->stroke_id; + SCULPT_stroke_id_test(ss, vertex, STROKEID_USER_AUTOMASKING); } - *(float *)SCULPT_vertex_attr_get(vertex, ss->attrs.automasking_factor) = f; + *SCULPT_vertex_attr_get(vertex, ss->attrs.automasking_factor) = f; } } @@ -816,7 +808,7 @@ bool SCULPT_tool_can_reuse_automask(int sculpt_tool) SCULPT_TOOL_DRAW_FACE_SETS); } -AutomaskingCache *SCULPT_automasking_cache_init(Sculpt *sd, Brush *brush, Object *ob) +AutomaskingCache *SCULPT_automasking_cache_init(Sculpt *sd, const Brush *brush, Object *ob) { SculptSession *ss = ob->sculpt; const int totvert = SCULPT_vertex_count_get(ss); @@ -922,7 +914,7 @@ AutomaskingCache *SCULPT_automasking_cache_init(Sculpt *sd, Brush *brush, Object for (int i : IndexRange(totvert)) { PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); - (*(float *)SCULPT_vertex_attr_get(vertex, ss->attrs.automasking_factor)) = initial_value; + (*SCULPT_vertex_attr_get(vertex, ss->attrs.automasking_factor)) = initial_value; } const int boundary_propagation_steps = brush ? diff --git a/source/blender/editors/sculpt_paint/sculpt_boundary.cc b/source/blender/editors/sculpt_paint/sculpt_boundary.cc index bebb040fe18..8acac32495e 100644 --- a/source/blender/editors/sculpt_paint/sculpt_boundary.cc +++ b/source/blender/editors/sculpt_paint/sculpt_boundary.cc @@ -7,6 +7,9 @@ #include "MEM_guardedalloc.h" +#include "BLI_alloca.h" +#include "BLI_array.h" +#include "BLI_blenlib.h" #include "BLI_edgehash.h" #include "BLI_math.h" #include "BLI_task.h" @@ -19,6 +22,14 @@ #include "BKE_ccg.h" #include "BKE_colortools.h" #include "BKE_context.h" +#include "BKE_global.h" +#include "BKE_layer.h" +#include "BKE_lib_id.h" +#include "BKE_main.h" +#include "BKE_mesh.h" +#include "BKE_multires.h" +#include "BKE_node.h" +#include "BKE_object.h" #include "BKE_paint.h" #include "BKE_pbvh.h" @@ -28,12 +39,40 @@ #include "GPU_immediate.h" #include "GPU_state.h" -#include -#include +#include "bmesh.h" + +#include "ED_mesh.h" +#include "ED_object.h" + +#include "DEG_depsgraph.h" +#include "DEG_depsgraph_build.h" +#include "WM_api.h" +#include "WM_types.h" + +#include +#include + +#if 1 +# ifdef NDEBUG +# define NDEBUG_UNDEFD +# undef NDEBUG +# endif + +# include "BLI_assert.h" + +# ifdef NDEBUG_UNDEFD +# define NDEBUG 1 +# endif +#endif #define BOUNDARY_VERTEX_NONE -1 #define BOUNDARY_STEPS_NONE -1 +#define TSTN 4 + +static void boundary_color_vis(SculptSession *ss, SculptBoundary *boundary); +static void SCULPT_boundary_build_smoothco(SculptSession *ss, SculptBoundary *boundary); + struct BoundaryInitialVertexFloodFillData { PBVHVertRef initial_vertex; int initial_vertex_i; @@ -64,7 +103,7 @@ static bool boundary_initial_vertex_floodfill_cb( data->floodfill_steps[to_v_i] = data->floodfill_steps[from_v_i]; } - if (SCULPT_vertex_is_boundary(ss, to_v)) { + if (SCULPT_vertex_is_boundary(ss, to_v, SCULPT_BOUNDARY_MESH)) { if (data->floodfill_steps[to_v_i] < data->boundary_initial_vertex_steps) { data->boundary_initial_vertex_steps = data->floodfill_steps[to_v_i]; data->boundary_initial_vertex_i = to_v_i; @@ -83,8 +122,7 @@ static PBVHVertRef sculpt_boundary_get_closest_boundary_vertex(SculptSession *ss const PBVHVertRef initial_vertex, const float radius) { - - if (SCULPT_vertex_is_boundary(ss, initial_vertex)) { + if (SCULPT_vertex_is_boundary(ss, initial_vertex, SCULPT_BOUNDARY_MESH)) { return initial_vertex; } @@ -98,7 +136,7 @@ static PBVHVertRef sculpt_boundary_get_closest_boundary_vertex(SculptSession *ss fdata.boundary_initial_vertex_steps = INT_MAX; fdata.radius_sq = radius * radius; - fdata.floodfill_steps = MEM_cnew_array(SCULPT_vertex_count_get(ss), __func__); + fdata.floodfill_steps = MEM_cnew_array(TSTN, __func__); SCULPT_floodfill_execute(ss, &flood, boundary_initial_vertex_floodfill_cb, &fdata); SCULPT_floodfill_free(&flood); @@ -146,10 +184,10 @@ static void sculpt_boundary_preview_edge_add(SculptBoundary *boundary, if (boundary->edges_num >= boundary->edges_capacity) { boundary->edges_capacity += BOUNDARY_INDICES_BLOCK_SIZE; - boundary->edges = static_cast( - MEM_reallocN_id(boundary->edges, - boundary->edges_capacity * sizeof(SculptBoundaryPreviewEdge), - "boundary edges")); + boundary->edges = (SculptBoundaryPreviewEdge *)MEM_reallocN_id( + boundary->edges, + boundary->edges_capacity * sizeof(SculptBoundaryPreviewEdge) * TSTN, + "boundary edges"); } }; @@ -171,7 +209,7 @@ static bool sculpt_boundary_is_vertex_in_editable_boundary(SculptSession *ss, SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, initial_vertex, ni) { if (SCULPT_vertex_visible_get(ss, ni.vertex)) { neighbor_count++; - if (SCULPT_vertex_is_boundary(ss, ni.vertex)) { + if (SCULPT_vertex_is_boundary(ss, ni.vertex, SCULPT_BOUNDARY_MESH)) { boundary_vertex_count++; } } @@ -212,7 +250,8 @@ static bool boundary_floodfill_cb( BoundaryFloodFillData *data = static_cast(userdata); SculptBoundary *boundary = data->boundary; - if (!SCULPT_vertex_is_boundary(ss, to_v)) { + + if (!SCULPT_vertex_is_boundary(ss, to_v, SCULPT_BOUNDARY_MESH)) { return false; } const float edge_len = len_v3v3(SCULPT_vertex_co_get(ss, from_v), @@ -228,10 +267,161 @@ static bool boundary_floodfill_cb( return sculpt_boundary_is_vertex_in_editable_boundary(ss, to_v); } -static void sculpt_boundary_indices_init(SculptSession *ss, +static float *calc_boundary_tangent(SculptSession *ss, SculptBoundary *boundary) +{ + const int totvert = SCULPT_vertex_count_get(ss); + float dir[3]; + + float(*tangents)[3] = MEM_cnew_array(totvert, "boundary->boundary_tangents"); + + for (int i = 0; i < totvert; i++) { + float f1 = boundary->boundary_dist[i]; + + if (f1 == FLT_MAX) { + continue; + } + + PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); + const float *co1 = SCULPT_vertex_co_get(ss, vertex); + + zero_v3(dir); + + SculptVertexNeighborIter ni; + + float no1[3]; + SCULPT_vertex_normal_get(ss, vertex, no1); + +#if 0 + volatile int val = SCULPT_vertex_valence_get(ss, vertex); + float *ws = BLI_array_alloca(ws, val); + float *cot1 = BLI_array_alloca(cot1, val); + float *cot2 = BLI_array_alloca(cot2, val); + float *areas = BLI_array_alloca(areas, val); + float totarea; + + SCULPT_get_cotangents(ss, vertex, ws, cot1, cot2, areas, &totarea); + + float(*cos)[3] = BLI_array_alloca(cos, val); + float *scalars = BLI_array_alloca(scalars, val); + + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vertex, ni) { + scalars[ni.i] = boundary->boundary_dist[ni.index]; + copy_v3_v3(cos[ni.i], SCULPT_vertex_co_get(ss, ni.vertex)); + } + SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); + + for (int j1 = 0; j1 < val; j1++) { + int j2 = (j1 + 1) % val; + + float *co2 = cos[j1]; + float *co3 = cos[j2]; + float dir2[3]; + float dir3[3]; + + float f2 = scalars[j1]; + float f3 = scalars[j2]; + + if (f2 == FLT_MAX || f1 == FLT_MAX) { + continue; + } + + float du = f2 - f1; + float dv = f3 - f1; + + sub_v3_v3v3(dir2, co2, co1); + sub_v3_v3v3(dir3, co3, co1); + + mul_v3_fl(dir2, du); + mul_v3_fl(dir3, dv); + + add_v3_v3(dir2, dir3); + // normalize_v3(dir2); + + float w = 1.0; // ws[j1]; + + madd_v3_v3v3fl(dir, dir, dir2, w); + } + + normalize_v3(dir); + copy_v3_v3(tangents[i], dir); + +#else + + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vertex, ni) { + const float *co2 = SCULPT_vertex_co_get(ss, ni.vertex); + float no2[3]; + + SCULPT_vertex_normal_get(ss, ni.vertex, no2); + + // int i2 = BKE_pbvh_vertex_to_index(ss->pbvh, ni.vertex); + int i2 = ni.index; + + float f2 = boundary->boundary_dist[i2]; + float dir2[3]; + + sub_v3_v3v3(dir2, co2, co1); + + if (f2 == FLT_MAX) { + continue; + } + + float distsqr = len_squared_v3v3(co1, co2); + if (distsqr == 0.0f) { + continue; + } + + float w = (f2 - f1) / distsqr; + + mul_v3_fl(dir2, w); + add_v3_v3(dir, dir2); + } + SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); + + normalize_v3(dir); + negate_v3(dir); + + copy_v3_v3(tangents[i], dir); +#endif + } + + return (float *)tangents; +} + +static void sculpt_boundary_cotan_init(SculptSession *ss, SculptBoundary *boundary) +{ + const int totvert = SCULPT_vertex_count_get(ss); + boundary->boundary_cotangents = MEM_cnew_array(totvert, "StoredCotangentW"); + StoredCotangentW *cotw = boundary->boundary_cotangents; + + for (int i = 0; i < totvert; i++, cotw++) { + if (boundary->boundary_dist[i] == FLT_MAX) { + cotw->length = 0; + cotw->weights = nullptr; + continue; + } + + PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); + const int val = SCULPT_vertex_valence_get(ss, vertex); + + cotw->length = val; + + if (val < MAX_STORED_COTANGENTW_EDGES) { + cotw->weights = cotw->static_weights; + } + else { + cotw->weights = (float *)MEM_malloc_arrayN(val, sizeof(*cotw->weights), "cotw->weights"); + } + + SCULPT_get_cotangents(ss, vertex, cotw->weights, nullptr, nullptr, nullptr, nullptr); + } +} + +static void sculpt_boundary_indices_init(Object *ob, + SculptSession *ss, SculptBoundary *boundary, const bool init_boundary_distances, - const PBVHVertRef initial_boundary_vertex) + const PBVHVertRef initial_boundary_vertex, + float radius) { const int totvert = SCULPT_vertex_count_get(ss); @@ -239,10 +429,11 @@ static void sculpt_boundary_indices_init(SculptSession *ss, MEM_malloc_arrayN(BOUNDARY_INDICES_BLOCK_SIZE, sizeof(PBVHVertRef), __func__)); if (init_boundary_distances) { - boundary->distance = static_cast(MEM_calloc_arrayN(totvert, sizeof(float), __func__)); + boundary->distance = (float *)MEM_calloc_arrayN( + totvert, sizeof(float) * TSTN, "boundary distances"); } - boundary->edges = static_cast( - MEM_malloc_arrayN(BOUNDARY_INDICES_BLOCK_SIZE, sizeof(SculptBoundaryPreviewEdge), __func__)); + boundary->edges = (SculptBoundaryPreviewEdge *)MEM_malloc_arrayN( + BOUNDARY_INDICES_BLOCK_SIZE, sizeof(SculptBoundaryPreviewEdge) * TSTN, "boundary edges"); GSet *included_verts = BLI_gset_int_new_ex("included verts", BOUNDARY_INDICES_BLOCK_SIZE); SculptFloodFill flood; @@ -267,6 +458,128 @@ static void sculpt_boundary_indices_init(SculptSession *ss, SCULPT_floodfill_execute(ss, &flood, boundary_floodfill_cb, &fdata); SCULPT_floodfill_free(&flood); + GSet *boundary_verts; + + if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) { + boundary_verts = BLI_gset_int_new_ex("included vertices", BOUNDARY_INDICES_BLOCK_SIZE); + + GSetIterator gi; + + GSET_ITER (gi, included_verts) { + BMVert *v = (BMVert *)BLI_gsetIterator_getKey(&gi); + BLI_gset_add(boundary_verts, POINTER_FROM_INT(v->head.index)); + } + } + else { + boundary_verts = included_verts; + } + + boundary->boundary_closest = MEM_cnew_array(totvert, "boundary_closest"); + boundary->boundary_dist = SCULPT_geodesic_distances_create( + ob, boundary_verts, radius, boundary->boundary_closest, nullptr); + + sculpt_boundary_cotan_init(ss, boundary); + +#if 0 // smooth geodesic scalar field + float *boundary_dist = MEM_calloc_arrayN(totvert, sizeof(float), "boundary_dist"); + + for (int iteration = 0; iteration < 4; iteration++) { + for (int i = 0; i < totvert; i++) { + if (boundary->boundary_dist[i] == FLT_MAX) { + boundary_dist[i] = FLT_MAX; + continue; + } + + PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); + float tot = 0.0f; + + StoredCotangentW *cotw = boundary->boundary_cotangents + i; + + SculptVertexNeighborIter ni; + int j = 0; + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vertex, ni) { + if (boundary->boundary_dist[ni.index] == FLT_MAX) { + j++; + continue; + } + + const float w = cotw->weights[j]; + + boundary_dist[i] += boundary->boundary_dist[ni.index] * w; + + tot += w; + j++; + } + SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); + + if (tot == 0.0f) { + boundary_dist[i] = FLT_MAX; + } + else { + boundary_dist[i] /= tot; + } + } + + SWAP(float *, boundary_dist, boundary->boundary_dist); + } + + MEM_SAFE_FREE(boundary_dist); +#endif + + boundary->boundary_tangents = (float(*)[3])calc_boundary_tangent(ss, boundary); + +#if 1 // smooth geodesic tangent field + float(*boundary_tangents)[3] = MEM_cnew_array(totvert, "boundary_tangents"); + + for (int iteration = 0; iteration < 4; iteration++) { + for (int i = 0; i < totvert; i++) { + + if (boundary->boundary_dist[i] == FLT_MAX) { + copy_v3_v3(boundary_tangents[i], boundary->boundary_tangents[i]); + continue; + } + + PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); + float tot = 0.0f; + + // StoredCotangentW *cotw = boundary->boundary_cotangents + i; + float tan[3] = {0.0f, 0.0f, 0.0f}; + + SculptVertexNeighborIter ni; + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vertex, ni) { + if (boundary->boundary_dist[ni.index] == FLT_MAX) { + continue; + } + + add_v3_v3(tan, boundary->boundary_tangents[ni.index]); + + tot += 1.0f; + } + SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); + + if (tot == 0.0f) { + continue; + } + + normalize_v3(tan); + interp_v3_v3v3(boundary_tangents[i], boundary->boundary_tangents[i], tan, 0.75f); + normalize_v3(boundary_tangents[i]); + } + + float(*tmp)[3] = boundary_tangents; + boundary_tangents = boundary->boundary_tangents; + boundary->boundary_tangents = tmp; + } + + MEM_SAFE_FREE(boundary_tangents); +#endif + + boundary_color_vis(ss, boundary); + + if (boundary_verts != included_verts) { + BLI_gset_free(boundary_verts, nullptr); + } + /* Check if the boundary loops into itself and add the extra preview edge to close the loop. */ if (fdata.last_visited_vertex.i != BOUNDARY_VERTEX_NONE && sculpt_boundary_is_vertex_in_editable_boundary(ss, fdata.last_visited_vertex)) { @@ -284,6 +597,54 @@ static void sculpt_boundary_indices_init(SculptSession *ss, BLI_gset_free(included_verts, nullptr); } +static void boundary_color_vis(SculptSession *ss, SculptBoundary *boundary) +{ + if (boundary->boundary_dist && G.debug_value == 890 && ss->bm && + CustomData_has_layer(&ss->bm->vdata, CD_PROP_COLOR)) { + const int cd_color = CustomData_get_offset(&ss->bm->vdata, CD_PROP_COLOR); + BM_mesh_elem_index_ensure(ss->bm, BM_VERT); + + BMIter iter; + BMVert *v; + int i = 0; + + float min = 1e17f, max = -1e17f; + + // calc bounds + BM_ITER_MESH_INDEX (v, &iter, ss->bm, BM_VERTS_OF_MESH, i) { + float f = boundary->boundary_dist[i]; + + if (f == FLT_MAX) { + continue; + } + + min = MIN2(min, f); + max = MAX2(max, f); + } + + float scale = max != min ? 1.0f / (max - min) : 0.0f; + + BM_ITER_MESH_INDEX (v, &iter, ss->bm, BM_VERTS_OF_MESH, i) { + MPropCol *mcol = BM_ELEM_CD_PTR(v, cd_color); + + float f = boundary->boundary_dist[i]; + + if (f == FLT_MAX) { + mcol->color[0] = mcol->color[1] = 1.0f; + mcol->color[2] = 0.0f; + mcol->color[3] = 1.0f; + continue; + } + else { + f = (f - min) * scale; + } + + mcol->color[0] = mcol->color[1] = mcol->color[2] = f; + mcol->color[3] = 1.0f; + } + } +} + /** * This functions initializes all data needed to calculate falloffs and deformation from the * boundary into the mesh into a #SculptBoundaryEditInfo array. This includes how many steps are @@ -299,8 +660,8 @@ static void sculpt_boundary_edit_data_init(SculptSession *ss, const bool has_duplicates = BKE_pbvh_type(ss->pbvh) == PBVH_GRIDS; - boundary->edit_info = static_cast( - MEM_malloc_arrayN(totvert, sizeof(SculptBoundaryEditInfo), __func__)); + boundary->edit_info = (SculptBoundaryEditInfo *)MEM_malloc_arrayN( + totvert, sizeof(SculptBoundaryEditInfo) * TSTN, "Boundary edit info"); for (int i = 0; i < totvert; i++) { boundary->edit_info[i].original_vertex_i = BOUNDARY_VERTEX_NONE; @@ -350,16 +711,17 @@ static void sculpt_boundary_edit_data_init(SculptSession *ss, while (!BLI_gsqueue_is_empty(current_iteration)) { PBVHVertRef from_v; BLI_gsqueue_pop(current_iteration, &from_v); - - int from_v_i = BKE_pbvh_vertex_to_index(ss->pbvh, from_v); + const int from_v_i = BKE_pbvh_vertex_to_index(ss->pbvh, from_v); SculptVertexNeighborIter ni; SCULPT_VERTEX_DUPLICATES_AND_NEIGHBORS_ITER_BEGIN (ss, from_v, ni) { const bool is_visible = SCULPT_vertex_visible_get(ss, ni.vertex); + if (!is_visible || boundary->edit_info[ni.index].propagation_steps_num != BOUNDARY_STEPS_NONE) { continue; } + boundary->edit_info[ni.index].original_vertex_i = boundary->edit_info[from_v_i].original_vertex_i; @@ -426,14 +788,14 @@ static void sculpt_boundary_edit_data_init(SculptSession *ss, /* This functions assigns a falloff factor to each one of the SculptBoundaryEditInfo structs based * on the brush curve and its propagation steps. The falloff goes from the boundary into the mesh. */ -static void sculpt_boundary_falloff_factor_init(SculptSession *ss, - SculptBoundary *boundary, - Brush *brush, - const float radius) +static void sculpt_boundary_falloff_factor_init( + SculptSession *ss, Sculpt *sd, SculptBoundary *boundary, Brush *brush, const float radius) { const int totvert = SCULPT_vertex_count_get(ss); BKE_curvemapping_init(brush->curve); + int boundary_type = brush->boundary_falloff_type; + for (int i = 0; i < totvert; i++) { if (boundary->edit_info[i].propagation_steps_num != -1) { boundary->edit_info[i].strength_factor = BKE_brush_curve_strength( @@ -457,17 +819,17 @@ static void sculpt_boundary_falloff_factor_init(SculptSession *ss, float falloff_distance = 0.0f; float direction = 1.0f; - switch (brush->boundary_falloff_type) { + switch (boundary_type) { case BRUSH_BOUNDARY_FALLOFF_RADIUS: falloff_distance = boundary_distance; break; case BRUSH_BOUNDARY_FALLOFF_LOOP: { - const int div = boundary_distance / radius; + const int div = (int)(boundary_distance / radius); const float mod = fmodf(boundary_distance, radius); falloff_distance = div % 2 == 0 ? mod : radius - mod; } break; case BRUSH_BOUNDARY_FALLOFF_LOOP_INVERT: { - const int div = boundary_distance / radius; + const int div = (int)(boundary_distance / radius); const float mod = fmodf(boundary_distance, radius); falloff_distance = div % 2 == 0 ? mod : radius - mod; /* Inverts the falloff in the intervals 1 2 5 6 9 10 ... etc. */ @@ -485,17 +847,22 @@ static void sculpt_boundary_falloff_factor_init(SculptSession *ss, } } -SculptBoundary *SCULPT_boundary_data_init(Object *object, - Brush *brush, - const PBVHVertRef initial_vertex, - const float radius) +/* Main function to get SculptBoundary data both for brush deformation and viewport preview. Can + * return nullptr if there is no boundary from the given vertex using the given radius. */ +SculptBoundary *SCULPT_boundary_data_init( + Sculpt *sd, Object *object, Brush *brush, const PBVHVertRef initial_vertex, const float radius) { - SculptSession *ss = object->sculpt; - if (initial_vertex.i == PBVH_REF_NONE) { return nullptr; } + SculptSession *ss = object->sculpt; + + // XXX force update of BMVert->head.index + if (ss->bm) { + ss->bm->elem_index_dirty |= BM_VERT; + } + SCULPT_vertex_random_access_ensure(ss); SCULPT_boundary_info_ensure(object); @@ -512,17 +879,26 @@ SculptBoundary *SCULPT_boundary_data_init(Object *object, return nullptr; } - SculptBoundary *boundary = MEM_cnew(__func__); + SculptBoundary *boundary = (SculptBoundary *)MEM_callocN(sizeof(SculptBoundary) * TSTN, + "Boundary edit data"); + + boundary->deform_target = brush->deform_target; const bool init_boundary_distances = brush ? brush->boundary_falloff_type != BRUSH_BOUNDARY_FALLOFF_CONSTANT : false; - sculpt_boundary_indices_init(ss, boundary, init_boundary_distances, boundary_initial_vertex); - const float boundary_radius = brush ? radius * (1.0f + brush->boundary_offset) : radius; + + sculpt_boundary_indices_init( + object, ss, boundary, init_boundary_distances, boundary_initial_vertex, boundary_radius); + sculpt_boundary_edit_data_init(ss, boundary, boundary_initial_vertex, boundary_radius); + if (ss->cache) { + SCULPT_boundary_build_smoothco(ss, boundary); + } + return boundary; } @@ -531,56 +907,514 @@ void SCULPT_boundary_data_free(SculptBoundary *boundary) MEM_SAFE_FREE(boundary->verts); MEM_SAFE_FREE(boundary->edges); MEM_SAFE_FREE(boundary->distance); + + MEM_SAFE_FREE(boundary->boundary_dist); + MEM_SAFE_FREE(boundary->boundary_tangents); + MEM_SAFE_FREE(boundary->boundary_closest); + MEM_SAFE_FREE(boundary->smoothco); + MEM_SAFE_FREE(boundary->edit_info); MEM_SAFE_FREE(boundary->bend.pivot_positions); MEM_SAFE_FREE(boundary->bend.pivot_rotation_axis); MEM_SAFE_FREE(boundary->slide.directions); + MEM_SAFE_FREE(boundary->circle.origin); + MEM_SAFE_FREE(boundary->circle.radius); + + StoredCotangentW *cotw = boundary->boundary_cotangents; + + if (cotw) { + for (int i = 0; i < boundary->sculpt_totvert; i++, cotw++) { + if (cotw->weights != cotw->static_weights) { + MEM_SAFE_FREE(cotw->weights); + } + } + } + + MEM_SAFE_FREE(boundary->boundary_cotangents); MEM_SAFE_FREE(boundary); } +typedef struct ScalarFieldWalkData { + PBVHVertRef v; + float co[3]; + + struct { + PBVHVertRef v1, v2; + } edge; + + float t, f; + bool has_edge; +} ScalarFieldWalkData; + +static void sculpt_walk_scalar_field_init(SculptSession *ss, + PBVHVertRef v, + ScalarFieldWalkData *wd, + float *field) +{ + wd->v = v; + copy_v3_v3(wd->co, SCULPT_vertex_co_get(ss, v)); + wd->has_edge = false; + wd->t = 0.0f; + + int i = BKE_pbvh_vertex_to_index(ss->pbvh, v); + wd->f = field[i]; +} + +/*walk in decreasing direction of scalar field*/ +static bool sculpt_walk_scalar_field(SculptSession *ss, + ScalarFieldWalkData *wd, + float *field, + float (*dfield)[3]) +{ + SculptVertexNeighborIter ni; + SculptVertexNeighborIter ni2; + PBVHVertRef v = wd->v, minv1 = {-1LL}, minv2 = {-1LL}; + float mindis1 = FLT_MAX, mindis2 = FLT_MAX; + float minf1 = 0.0, minf2 = 0.0; + + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, v, ni) { + const float *co2 = SCULPT_vertex_co_get(ss, ni.vertex); + float f2 = field[ni.index]; + + if (ni.vertex.i == v.i) { + continue; + } + + if (f2 > wd->f) { + continue; + } + + float len = len_v3v3(co2, wd->co); + float dist = f2 * len; + + if (dist >= mindis1) { + continue; + } + + mindis1 = dist; + minf1 = f2; + minv1 = ni.vertex; + + mindis2 = FLT_MAX; + + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, ni.vertex, ni2) { + if (ni2.vertex.i == ni.vertex.i) { + continue; + } + + const float *co3 = SCULPT_vertex_co_get(ss, ni2.vertex); + float f3 = field[ni2.index]; + + float dist2 = f3 * len; + + if (dist2 < mindis2) { + mindis2 = dist2; + minf2 = f3; + minv2 = ni2.vertex; + } + } + SCULPT_VERTEX_NEIGHBORS_ITER_END(ni2); + } + SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); + + if (minv1.i == -1LL) { + // didn't find anything + return false; + } + + if (minv2.i == -1LL) { + wd->v = minv1; + copy_v3_v3(wd->co, SCULPT_vertex_co_get(ss, minv1)); + wd->has_edge = false; + wd->f = minf1; + + return true; + } + + wd->has_edge = true; + wd->edge.v1 = minv1; + wd->edge.v2 = minv2; + + /* + on factor + load_package "avector"; + + comment: relative to wd.co; + a := avec(ax, ay, az); + b := avec(bx, by, bz); + + dva := avec(dvax, dvay, dvaz); + dvb := avec(dvbx, dvby, dvbz); + + la := a dot a; + lb := b dot b; + + f2 := a + (b - a) * t; + df2 := dva + (dvb - dva)*t; + + ll := f2 dot f2; + f1 := (minf1 + (minf2 - minf1)*t) * ll; + + ff := solve(df(f1, t, 2), t); + f := part(ff, 1, 2); + + + */ + + const float *a = SCULPT_vertex_co_get(ss, minv1); + const float *b = SCULPT_vertex_co_get(ss, minv2); + + float ax = a[0] - wd->co[0]; + float ay = a[1] - wd->co[1]; + float az = a[2] - wd->co[2]; + + float bx = b[0] - wd->co[0]; + float by = b[1] - wd->co[1]; + float bz = b[2] - wd->co[2]; + + float div = (by * by + bz * bz + bx * bx + (az - 2.0 * bz) * az + (ay - 2.0 * by) * ay + + (ax - 2.0 * bx) * ax); + + float t = ((ay - by) * ay + (az - bz) * az + (ax - bx) * ax) / div; + + float m1m2 = minf1 + minf2; + + float ans4 = -2.0 * (by * by + bz * bz + bx * bx) * m1m2 * az * bz * minf1 - + (2.0 * (minf1 + minf2) * bz - az * minf2) * az * az * az * minf2; + + float sqr2 = (by * by + bz * bz + bx * bx); + sqr2 = sqr2 * sqr2; + + float ans3 = + (2.0 * + ((4.0 * (minf1 * minf1 - minf1 * minf2 + minf2 * minf2) * by - + (minf1 + minf2) * ay * minf2) * + ay + + (4.0 * (minf1 * minf1 - minf1 * minf2 + minf2 * minf2) * bz - + (minf1 + minf2) * az * minf2) * + az - + (by * by + bz * bz + bx * bx) * m1m2 * minf1) * + bx - + (2.0 * m1m2 * bx - ax * minf2) * ax * ax * minf2 - + ((by * by + bz * bz) * (3.0 * minf1 * minf1 - 8.0 * minf1 * minf2 + 3.0 * minf2 * minf2) - + (minf1 * minf1 + 4.0 * minf1 * minf2 + minf2 * minf2) * bx * bx + + 2.0 * (m1m2 * bz - az * minf2) * az * minf2 + + 2.0 * (m1m2 * by - ay * minf2) * ay * minf2) * + ax) * + ax - + (2.0 * + ((by * by + bz * bz + bx * bx) * m1m2 * minf1 - + (4.0 * (minf1 * minf1 - minf1 * minf2 + minf2 * minf2) * bz - m1m2 * az * minf2) * + az) * + by + + (2.0 * (minf1 + minf2) * by - ay * minf2) * ay * ay * minf2 + + ((bx * bx + bz * bz) * (3.0 * minf1 * minf1 - 8.0 * minf1 * minf2 + 3.0 * minf2 * minf2) - + (minf1 * minf1 + 4.0 * minf1 * minf2 + minf2 * minf2) * by * by + + 2.0 * (m1m2 * bz - az * minf2) * az * minf2) * + ay) * + ay - + ((bx * bx + by * by) * (3.0 * minf1 * minf1 - 8.0 * minf1 * minf2 + 3.0 * minf2 * minf2) - + (minf1 * minf1 + 4.0 * minf1 * minf2 + minf2 * minf2) * bz * bz) * + az * az + + sqr2 * minf1 * minf1 + ans4; + + float ans2 = sqrtf(ans3); + + float ans1 = (by * by + bz * bz + bx * bx) * minf1 + + ((3.0 * minf1 - 2.0 * minf2) * az - 2.0 * (2.0 * minf1 - minf2) * bz) * az + + ((3.0 * minf1 - 2.0 * minf2) * ay - 2.0 * (2.0 * minf1 - minf2) * by) * ay + + ((3.0 * minf1 - 2.0 * minf2) * ax - 2.0 * (2.0 * minf1 - minf2) * bx) * ax + ans2; + + t = ans1 / (3.0 * + (by * by + bz * bz + bx * bx + (az - 2.0 * bz) * az + (ay - 2.0 * by) * ay + + (ax - 2.0 * bx) * ax) * + (minf1 - minf2)); + +#if 1 + t = (((3.0 * minf1 - 2.0 * minf2) * ay - 2.0 * (2.0 * minf1 - minf2) * by) * ay + + ((3.0 * minf1 - 2.0 * minf2) * az - 2.0 * (2.0 * minf1 - minf2) * bz) * az + + ((3.0 * minf1 - 2.0 * minf2) * ax - 2.0 * (2.0 * minf1 - minf2) * bx) * ax + + (by * by + bz * bz + bx * bx) * minf1) / + (3.0 * + (by * by + bz * bz + bx * bx + (az - 2.0 * bz) * az + (ay - 2.0 * by) * ay + + (ax - 2.0 * bx) * ax) * + (minf1 - minf2)); +#endif + + t = t < 0.0f ? 0.0f : t; + t = t > 1.0f ? 1.0f : t; + + t = 0.5f; + wd->t = t; + + wd->v = minv1; + wd->f = minf1 + (minf2 - minf1) * wd->t; + float co[3]; + + interp_v3_v3v3(co, SCULPT_vertex_co_get(ss, minv1), SCULPT_vertex_co_get(ss, minv2), wd->t); + + float f3 = wd->f * len_v3v3(wd->co, co); + if (f3 > mindis1 || f3 > mindis2) { + wd->f = minf1; + t = 0.0f; + interp_v3_v3v3(co, SCULPT_vertex_co_get(ss, minv1), SCULPT_vertex_co_get(ss, minv2), wd->t); + } + + copy_v3_v3(wd->co, co); + + return true; +} + +Object *sculpt_get_vis_object(bContext *C, SculptSession *ss, char *name) +{ + if (!C) { + C = ss->cache->C; + } + + Scene *scene = CTX_data_scene(C); + ViewLayer *vlayer = CTX_data_view_layer(C); + Main *bmain = CTX_data_main(C); + Object *actob = CTX_data_active_object(C); + + Object *ob = (Object *)BKE_libblock_find_name(bmain, ID_OB, name); + + if (!ob) { + Mesh *me = BKE_mesh_add(bmain, name); + + ob = BKE_object_add_only_object(bmain, OB_MESH, name); + ob->data = (void *)me; + id_us_plus((ID *)me); + + DEG_id_tag_update_ex( + bmain, &ob->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_ANIMATION); + + LayerCollection *layer_collection = BKE_layer_collection_get_active(vlayer); + BKE_collection_object_add(bmain, layer_collection->collection, ob); + } + + copy_v3_v3(ob->loc, actob->loc); + copy_v3_v3(ob->rot, actob->rot); + BKE_object_to_mat4(ob, ob->object_to_world); + + DEG_id_type_tag(bmain, ID_OB); + DEG_relations_tag_update(bmain); + WM_event_add_notifier(C, NC_SCENE | ND_LAYER_CONTENT, scene); + DEG_id_tag_update(&scene->id, 0); + + Mesh *me = (Mesh *)ob->data; + + DEG_id_tag_update(&me->id, ID_RECALC_ALL); + return ob; +} + +void sculpt_end_vis_object(bContext *C, SculptSession *ss, Object *ob, BMesh *bm) +{ + if (!C) { + C = ss->cache->C; + } + + Main *bmain = CTX_data_main(C); + + Mesh *me = (Mesh *)ob->data; + BMeshToMeshParams params = {}; + + params.calc_object_remap = false; + params.update_shapekey_indices = false; + params.copy_temp_cdlayers = false; + + BM_mesh_bm_to_me(bmain, nullptr, bm, me, ¶ms); + + DEG_id_tag_update(&me->id, ID_RECALC_ALL); +} + +//#define VISBM + /* These functions initialize the required vectors for the desired deformation using the * SculptBoundaryEditInfo. They calculate the data using the vertices that have the * max_propagation_steps value and them this data is copied to the rest of the vertices using the * original vertex index. */ -static void sculpt_boundary_bend_data_init(SculptSession *ss, SculptBoundary *boundary) +static void sculpt_boundary_bend_data_init(SculptSession *ss, + SculptBoundary *boundary, + float radius) { +#ifdef VISBM + Object *visob = get_vis_object(ss, "_vis_sculpt_boundary_bend_data_init"); + BMAllocTemplate alloc = {512, 512, 512, 512}; + BMesh *visbm = BM_mesh_create(&alloc, + (&(struct BMeshCreateParams){.use_unique_ids = 0, + .use_id_elem_mask = 0, + .use_id_map = 0, + .use_toolflags = 0, + .no_reuse_ids = 0})); +#endif + const int totvert = SCULPT_vertex_count_get(ss); - boundary->bend.pivot_rotation_axis = static_cast( - MEM_calloc_arrayN(totvert, sizeof(float[3]), __func__)); - boundary->bend.pivot_positions = static_cast( - MEM_calloc_arrayN(totvert, sizeof(float[3]), __func__)); + boundary->bend.pivot_rotation_axis = MEM_cnew_array(totvert, __func__); + boundary->bend.pivot_positions = MEM_cnew_array(totvert, __func__); for (int i = 0; i < totvert; i++) { + + if (boundary->edit_info[i].propagation_steps_num != boundary->max_propagation_steps) { + continue; + } + } + + for (int i = 0; i < totvert; i++) { + PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); + + if (boundary->edit_info[i].original_vertex_i == BOUNDARY_VERTEX_NONE) { + continue; + } + if (boundary->edit_info[i].propagation_steps_num != boundary->max_propagation_steps) { continue; } - PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); + if (boundary->edit_info[i].original_vertex.i == -1) { + continue; + } + + const float *co1 = SCULPT_vertex_co_get(ss, vertex); float dir[3]; float normal[3]; SCULPT_vertex_normal_get(ss, vertex, normal); - sub_v3_v3v3( - dir, - SCULPT_vertex_co_get( - ss, BKE_pbvh_index_to_vertex(ss->pbvh, boundary->edit_info[i].original_vertex_i)), - SCULPT_vertex_co_get(ss, vertex)); + sub_v3_v3v3(dir, SCULPT_vertex_co_get(ss, boundary->edit_info[i].original_vertex), co1); + + normalize_v3(dir); + + float olddir[3]; + copy_v3_v3(olddir, dir); + + if (boundary->boundary_dist[i] != FLT_MAX) { + zero_v3(dir); + copy_v3_v3(dir, boundary->boundary_tangents[i]); + + if (dot_v3v3(dir, dir) < 0.00001f) { + sub_v3_v3v3(dir, + SCULPT_vertex_co_get(ss, boundary->edit_info[i].original_vertex), + SCULPT_vertex_co_get(ss, vertex)); + } + } + else { + // continue; + } + cross_v3_v3v3( boundary->bend.pivot_rotation_axis[boundary->edit_info[i].original_vertex_i], dir, normal); normalize_v3(boundary->bend.pivot_rotation_axis[boundary->edit_info[i].original_vertex_i]); - copy_v3_v3(boundary->bend.pivot_positions[boundary->edit_info[i].original_vertex_i], - SCULPT_vertex_co_get(ss, vertex)); + + float pos[3]; + + copy_v3_v3(pos, co1); + + copy_v3_v3(boundary->bend.pivot_positions[boundary->edit_info[i].original_vertex_i], pos); + boundary->bend.pivot_positions[boundary->edit_info[i].original_vertex_i][3] = 1.0f; } for (int i = 0; i < totvert; i++) { + if (boundary->bend.pivot_positions[i][3] > 1.0f) { + mul_v3_fl(boundary->bend.pivot_positions[i], 1.0f / boundary->bend.pivot_positions[i][3]); + boundary->bend.pivot_positions[i][3] = 1.0f; + } + } + + // fix any remaining boundaries without pivots + for (int vi = 0; vi < boundary->verts_num; vi++) { + PBVHVertRef v = boundary->verts[vi]; + const float *co1 = SCULPT_vertex_co_get(ss, v); + int i = BKE_pbvh_vertex_to_index(ss->pbvh, v); + + if (boundary->bend.pivot_positions[i][3] != 0.0f) { + continue; + } + + float minlen = FLT_MAX; + + // nasty inner loop here + for (int j = 0; j < totvert; j++) { + if (boundary->edit_info[j].propagation_steps_num != boundary->max_propagation_steps) { + continue; + } + + PBVHVertRef v2 = BKE_pbvh_index_to_vertex(ss->pbvh, j); + const float *co2 = SCULPT_vertex_co_get(ss, v2); + + float len = len_v3v3(co2, co1); + + if (len < minlen) { + minlen = len; + copy_v3_v3(boundary->bend.pivot_positions[i], co2); + boundary->bend.pivot_positions[i][3] = 1.0f; + } + } + } + + for (int i = 0; i < totvert; i++) { + PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); + const float *co1 = SCULPT_vertex_co_get(ss, vertex); + float dir[3]; + if (boundary->edit_info[i].propagation_steps_num == BOUNDARY_STEPS_NONE) { continue; } - copy_v3_v3(boundary->bend.pivot_positions[i], - boundary->bend.pivot_positions[boundary->edit_info[i].original_vertex_i]); - copy_v3_v3(boundary->bend.pivot_rotation_axis[i], - boundary->bend.pivot_rotation_axis[boundary->edit_info[i].original_vertex_i]); + float pos[3], oco[3]; + copy_v3_v3(pos, boundary->bend.pivot_positions[boundary->edit_info[i].original_vertex_i]); + copy_v3_v3(oco, SCULPT_vertex_co_get(ss, boundary->edit_info[i].original_vertex)); + + if (boundary->boundary_dist[i] != FLT_MAX) { + float no[3]; + + SCULPT_vertex_normal_get(ss, vertex, no); + + // snap to radial plane + cross_v3_v3v3(dir, no, boundary->boundary_tangents[i]); + normalize_v3(dir); + //* + + sub_v3_v3(pos, oco); + normalize_v3(pos); + mul_v3_fl(pos, radius); + add_v3_v3(pos, oco); + + sub_v3_v3(pos, co1); + madd_v3_v3fl(pos, dir, -dot_v3v3(dir, pos)); + add_v3_v3(pos, co1); + + //*/ + + copy_v3_v3(boundary->bend.pivot_rotation_axis[i], dir); + } + else { + zero_v3(dir); + + // printf("boundary info missing tangent\n"); + copy_v3_v3(boundary->bend.pivot_rotation_axis[i], + boundary->bend.pivot_rotation_axis[boundary->edit_info[i].original_vertex_i]); + } + + copy_v3_v3(boundary->bend.pivot_positions[i], pos); + +#ifdef VISBM + { + BMVert *v1, *v2; + v1 = BM_vert_create(visbm, co1, nullptr, BM_CREATE_NOP); + + v2 = BM_vert_create(visbm, pos, nullptr, BM_CREATE_NOP); + BM_edge_create(visbm, v1, v2, nullptr, BM_CREATE_NOP); + + float tmp[3]; + madd_v3_v3v3fl(tmp, co1, dir, 0.35); + + v2 = BM_vert_create(visbm, tmp, nullptr, BM_CREATE_NOP); + BM_edge_create(visbm, v1, v2, nullptr, BM_CREATE_NOP); + } +#endif } + +#ifdef VISBM + end_vis_object(ss, visob, visbm); +#endif } static void sculpt_boundary_slide_data_init(SculptSession *ss, SculptBoundary *boundary) @@ -590,14 +1424,16 @@ static void sculpt_boundary_slide_data_init(SculptSession *ss, SculptBoundary *b MEM_calloc_arrayN(totvert, sizeof(float[3]), "slide directions")); for (int i = 0; i < totvert; i++) { + PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); + if (boundary->edit_info[i].propagation_steps_num != boundary->max_propagation_steps) { continue; } - sub_v3_v3v3( - boundary->slide.directions[boundary->edit_info[i].original_vertex_i], - SCULPT_vertex_co_get( - ss, BKE_pbvh_index_to_vertex(ss->pbvh, boundary->edit_info[i].original_vertex_i)), - SCULPT_vertex_co_get(ss, BKE_pbvh_index_to_vertex(ss->pbvh, i))); + + sub_v3_v3v3(boundary->slide.directions[boundary->edit_info[i].original_vertex_i], + SCULPT_vertex_co_get(ss, boundary->edit_info[i].original_vertex), + SCULPT_vertex_co_get(ss, vertex)); + normalize_v3(boundary->slide.directions[boundary->edit_info[i].original_vertex_i]); } @@ -610,6 +1446,66 @@ static void sculpt_boundary_slide_data_init(SculptSession *ss, SculptBoundary *b } } +static void do_boundary_brush_circle_task_cb_ex(void *__restrict userdata, + const int n, + const TaskParallelTLS *__restrict /* tls */) +{ + SculptThreadedTaskData *data = static_cast(userdata); + SculptSession *ss = data->ob->sculpt; + const int symmetry_pass = ss->cache->mirror_symmetry_pass; + const SculptBoundary *boundary = ss->cache->boundaries[symmetry_pass]; + const ePaintSymmetryFlags symm = SCULPT_mesh_symmetry_xyz_get(data->ob); + + const float strength = ss->cache->bstrength; + + PBVHVertexIter vd; + SculptOrigVertData orig_data; + SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COORDS); + + AutomaskingNodeData automask_data; + SCULPT_automasking_node_begin( + data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]); + + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { + if (boundary->edit_info[vd.index].propagation_steps_num == -1) { + continue; + } + + SCULPT_orig_vert_data_update(&orig_data, vd.vertex); + if (!SCULPT_check_vertex_pivot_symmetry( + orig_data.co, boundary->initial_vertex_position, symm)) { + continue; + } + + SCULPT_automasking_node_update(ss, &automask_data, &vd); + + const int propagation_steps = boundary->edit_info[vd.index].propagation_steps_num; + float *circle_origin = boundary->circle.origin[propagation_steps]; + float circle_disp[3]; + sub_v3_v3v3(circle_disp, circle_origin, orig_data.co); + normalize_v3(circle_disp); + mul_v3_fl(circle_disp, -boundary->circle.radius[propagation_steps]); + float target_circle_co[3]; + add_v3_v3v3(target_circle_co, circle_origin, circle_disp); + + float disp[3]; + sub_v3_v3v3(disp, target_circle_co, vd.co); + float *target_co = SCULPT_brush_deform_target_vertex_co_get(ss, boundary->deform_target, &vd); + const float mask = vd.mask ? 1.0f - *vd.mask : 1.0f; + const float automask = SCULPT_automasking_factor_get( + ss->cache->automasking, ss, vd.vertex, &automask_data); + madd_v3_v3v3fl(target_co, + vd.co, + disp, + boundary->edit_info[vd.index].strength_factor * mask * automask * strength); + + if (vd.is_mesh) { + BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); + } + } + BKE_pbvh_vertex_iter_end; +} + static void sculpt_boundary_twist_data_init(SculptSession *ss, SculptBoundary *boundary) { zero_v3(boundary->twist.pivot_position); @@ -632,6 +1528,51 @@ static void sculpt_boundary_twist_data_init(SculptSession *ss, SculptBoundary *b MEM_freeN(poly_verts); } +static void sculpt_boundary_circle_data_init(SculptSession *ss, SculptBoundary *boundary) +{ + + const int totvert = SCULPT_vertex_count_get(ss); + const int totcircles = boundary->max_propagation_steps + 1; + + boundary->circle.radius = MEM_cnew_array(totcircles, "radius"); + boundary->circle.origin = MEM_cnew_array(totcircles, "origin"); + + int *count = MEM_cnew_array(totcircles, "count"); + for (int i = 0; i < totvert; i++) { + const int propagation_step_index = boundary->edit_info[i].propagation_steps_num; + if (propagation_step_index == -1) { + continue; + } + + PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); + + add_v3_v3(boundary->circle.origin[propagation_step_index], SCULPT_vertex_co_get(ss, vertex)); + count[propagation_step_index]++; + } + + for (int i = 0; i < totcircles; i++) { + mul_v3_fl(boundary->circle.origin[i], 1.0f / count[i]); + } + + for (int i = 0; i < totvert; i++) { + const int propagation_step_index = boundary->edit_info[i].propagation_steps_num; + if (propagation_step_index == -1) { + continue; + } + + PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); + + boundary->circle.radius[propagation_step_index] += len_v3v3( + boundary->circle.origin[propagation_step_index], SCULPT_vertex_co_get(ss, vertex)); + } + + for (int i = 0; i < totcircles; i++) { + boundary->circle.radius[i] *= 1.0f / count[i]; + } + + MEM_freeN(count); +} + static float sculpt_boundary_displacement_from_grab_delta_get(SculptSession *ss, SculptBoundary *boundary) { @@ -655,7 +1596,6 @@ static void do_boundary_brush_bend_task_cb_ex(void *__restrict userdata, const int symm_area = ss->cache->mirror_symmetry_pass; SculptBoundary *boundary = ss->cache->boundaries[symm_area]; const ePaintSymmetryFlags symm = SCULPT_mesh_symmetry_xyz_get(data->ob); - const Brush *brush = data->brush; const float strength = ss->cache->bstrength; @@ -680,7 +1620,7 @@ static void do_boundary_brush_bend_task_cb_ex(void *__restrict userdata, } SCULPT_automasking_node_update(ss, &automask_data, &vd); - SCULPT_orig_vert_data_update(&orig_data, &vd); + SCULPT_orig_vert_data_update(&orig_data, vd.vertex); if (!SCULPT_check_vertex_pivot_symmetry( orig_data.co, boundary->initial_vertex_position, symm)) { continue; @@ -690,7 +1630,8 @@ static void do_boundary_brush_bend_task_cb_ex(void *__restrict userdata, const float automask = SCULPT_automasking_factor_get( ss->cache->automasking, ss, vd.vertex, &automask_data); float t_orig_co[3]; - float *target_co = SCULPT_brush_deform_target_vertex_co_get(ss, brush->deform_target, &vd); + float *target_co = SCULPT_brush_deform_target_vertex_co_get(ss, boundary->deform_target, &vd); + sub_v3_v3v3(t_orig_co, orig_data.co, boundary->bend.pivot_positions[vd.index]); rotate_v3_v3v3fl(target_co, t_orig_co, @@ -714,7 +1655,6 @@ static void do_boundary_brush_slide_task_cb_ex(void *__restrict userdata, const int symm_area = ss->cache->mirror_symmetry_pass; SculptBoundary *boundary = ss->cache->boundaries[symm_area]; const ePaintSymmetryFlags symm = SCULPT_mesh_symmetry_xyz_get(data->ob); - const Brush *brush = data->brush; const float strength = ss->cache->bstrength; @@ -733,7 +1673,7 @@ static void do_boundary_brush_slide_task_cb_ex(void *__restrict userdata, } SCULPT_automasking_node_update(ss, &automask_data, &vd); - SCULPT_orig_vert_data_update(&orig_data, &vd); + SCULPT_orig_vert_data_update(&orig_data, vd.vertex); if (!SCULPT_check_vertex_pivot_symmetry( orig_data.co, boundary->initial_vertex_position, symm)) { continue; @@ -742,7 +1682,7 @@ static void do_boundary_brush_slide_task_cb_ex(void *__restrict userdata, const float mask = vd.mask ? 1.0f - *vd.mask : 1.0f; const float automask = SCULPT_automasking_factor_get( ss->cache->automasking, ss, vd.vertex, &automask_data); - float *target_co = SCULPT_brush_deform_target_vertex_co_get(ss, brush->deform_target, &vd); + float *target_co = SCULPT_brush_deform_target_vertex_co_get(ss, boundary->deform_target, &vd); madd_v3_v3v3fl(target_co, orig_data.co, boundary->slide.directions[vd.index], @@ -765,7 +1705,6 @@ static void do_boundary_brush_inflate_task_cb_ex(void *__restrict userdata, const int symm_area = ss->cache->mirror_symmetry_pass; SculptBoundary *boundary = ss->cache->boundaries[symm_area]; const ePaintSymmetryFlags symm = SCULPT_mesh_symmetry_xyz_get(data->ob); - const Brush *brush = data->brush; const float strength = ss->cache->bstrength; @@ -784,7 +1723,7 @@ static void do_boundary_brush_inflate_task_cb_ex(void *__restrict userdata, } SCULPT_automasking_node_update(ss, &automask_data, &vd); - SCULPT_orig_vert_data_update(&orig_data, &vd); + SCULPT_orig_vert_data_update(&orig_data, vd.vertex); if (!SCULPT_check_vertex_pivot_symmetry( orig_data.co, boundary->initial_vertex_position, symm)) { continue; @@ -793,7 +1732,10 @@ static void do_boundary_brush_inflate_task_cb_ex(void *__restrict userdata, const float mask = vd.mask ? 1.0f - *vd.mask : 1.0f; const float automask = SCULPT_automasking_factor_get( ss->cache->automasking, ss, vd.vertex, &automask_data); - float *target_co = SCULPT_brush_deform_target_vertex_co_get(ss, brush->deform_target, &vd); + float normal[3]; + copy_v3_v3(normal, orig_data.no); + + float *target_co = SCULPT_brush_deform_target_vertex_co_get(ss, boundary->deform_target, &vd); madd_v3_v3v3fl(target_co, orig_data.co, orig_data.no, @@ -816,7 +1758,6 @@ static void do_boundary_brush_grab_task_cb_ex(void *__restrict userdata, const int symm_area = ss->cache->mirror_symmetry_pass; SculptBoundary *boundary = ss->cache->boundaries[symm_area]; const ePaintSymmetryFlags symm = SCULPT_mesh_symmetry_xyz_get(data->ob); - const Brush *brush = data->brush; const float strength = ss->cache->bstrength; @@ -833,7 +1774,7 @@ static void do_boundary_brush_grab_task_cb_ex(void *__restrict userdata, } SCULPT_automasking_node_update(ss, &automask_data, &vd); - SCULPT_orig_vert_data_update(&orig_data, &vd); + SCULPT_orig_vert_data_update(&orig_data, vd.vertex); if (!SCULPT_check_vertex_pivot_symmetry( orig_data.co, boundary->initial_vertex_position, symm)) { continue; @@ -842,7 +1783,7 @@ static void do_boundary_brush_grab_task_cb_ex(void *__restrict userdata, const float mask = vd.mask ? 1.0f - *vd.mask : 1.0f; const float automask = SCULPT_automasking_factor_get( ss->cache->automasking, ss, vd.vertex, &automask_data); - float *target_co = SCULPT_brush_deform_target_vertex_co_get(ss, brush->deform_target, &vd); + float *target_co = SCULPT_brush_deform_target_vertex_co_get(ss, boundary->deform_target, &vd); madd_v3_v3v3fl(target_co, orig_data.co, ss->cache->grab_delta_symmetry, @@ -864,7 +1805,6 @@ static void do_boundary_brush_twist_task_cb_ex(void *__restrict userdata, const int symm_area = ss->cache->mirror_symmetry_pass; SculptBoundary *boundary = ss->cache->boundaries[symm_area]; const ePaintSymmetryFlags symm = SCULPT_mesh_symmetry_xyz_get(data->ob); - const Brush *brush = data->brush; const float strength = ss->cache->bstrength; @@ -889,7 +1829,7 @@ static void do_boundary_brush_twist_task_cb_ex(void *__restrict userdata, } SCULPT_automasking_node_update(ss, &automask_data, &vd); - SCULPT_orig_vert_data_update(&orig_data, &vd); + SCULPT_orig_vert_data_update(&orig_data, vd.vertex); if (!SCULPT_check_vertex_pivot_symmetry( orig_data.co, boundary->initial_vertex_position, symm)) { continue; @@ -899,7 +1839,7 @@ static void do_boundary_brush_twist_task_cb_ex(void *__restrict userdata, const float automask = SCULPT_automasking_factor_get( ss->cache->automasking, ss, vd.vertex, &automask_data); float t_orig_co[3]; - float *target_co = SCULPT_brush_deform_target_vertex_co_get(ss, brush->deform_target, &vd); + float *target_co = SCULPT_brush_deform_target_vertex_co_get(ss, boundary->deform_target, &vd); sub_v3_v3v3(t_orig_co, orig_data.co, boundary->twist.pivot_position); rotate_v3_v3v3fl(target_co, t_orig_co, @@ -923,7 +1863,6 @@ static void do_boundary_brush_smooth_task_cb_ex(void *__restrict userdata, const int symmetry_pass = ss->cache->mirror_symmetry_pass; const SculptBoundary *boundary = ss->cache->boundaries[symmetry_pass]; const ePaintSymmetryFlags symm = SCULPT_mesh_symmetry_xyz_get(data->ob); - const Brush *brush = data->brush; const float strength = ss->cache->bstrength; @@ -936,7 +1875,7 @@ static void do_boundary_brush_smooth_task_cb_ex(void *__restrict userdata, continue; } - SCULPT_orig_vert_data_update(&orig_data, &vd); + SCULPT_orig_vert_data_update(&orig_data, vd.vertex); if (!SCULPT_check_vertex_pivot_symmetry( orig_data.co, boundary->initial_vertex_position, symm)) { continue; @@ -962,7 +1901,7 @@ static void do_boundary_brush_smooth_task_cb_ex(void *__restrict userdata, const float mask = vd.mask ? 1.0f - *vd.mask : 1.0f; mul_v3_v3fl(avg, coord_accum, 1.0f / total_neighbors); sub_v3_v3v3(disp, avg, vd.co); - float *target_co = SCULPT_brush_deform_target_vertex_co_get(ss, brush->deform_target, &vd); + float *target_co = SCULPT_brush_deform_target_vertex_co_get(ss, boundary->deform_target, &vd); madd_v3_v3v3fl( target_co, vd.co, disp, boundary->edit_info[vd.index].strength_factor * mask * strength); @@ -973,11 +1912,122 @@ static void do_boundary_brush_smooth_task_cb_ex(void *__restrict userdata, BKE_pbvh_vertex_iter_end; } +static void SCULPT_boundary_autosmooth(SculptSession *ss, SculptBoundary *boundary) +{ + PBVHNode **nodes; + int totnode; + + const int max_iterations = 4; + const float fract = 1.0f / max_iterations; + float bstrength = ss->cache->brush->autosmooth_factor; + + CLAMP(bstrength, 0.0f, 1.0f); + + const int count = (int)(bstrength * max_iterations); + const float last = max_iterations * (bstrength - count * fract); + + const float boundary_radius = ss->cache->radius * (1.0f + ss->cache->brush->boundary_offset) * + ss->cache->brush->autosmooth_radius_factor; + + BKE_curvemapping_init(ss->cache->brush->curve); + + BKE_pbvh_get_nodes(ss->pbvh, PBVH_Leaf, &nodes, &totnode); + + float projection = ss->cache->brush->autosmooth_projection; + float slide_fset = ss->cache->brush->autosmooth_fset_slide; + + for (int iteration = 0; iteration <= count; iteration++) { + for (int i = 0; i < totnode; i++) { + const float strength = (iteration != count) ? 1.0f : last; + + PBVHNode *node = nodes[i]; + PBVHVertexIter vd; + + BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) { + if (boundary->boundary_dist[vd.index] == FLT_MAX) { + continue; + } + + if (boundary->edit_info[vd.index].propagation_steps_num == BOUNDARY_STEPS_NONE) { + continue; + } + + float fac = boundary->boundary_dist[vd.index] / boundary_radius; + + if (fac > 1.0f) { + continue; + } + + fac = BKE_brush_curve_strength(ss->cache->brush, fac, 1.0f); + + float sco[3]; + + SCULPT_neighbor_coords_average_interior(ss, sco, vd.vertex, projection, slide_fset); + + float *co = SCULPT_brush_deform_target_vertex_co_get(ss, boundary->deform_target, &vd); + + interp_v3_v3v3(co, co, sco, strength * fac); + BKE_pbvh_node_mark_update(node); + } + BKE_pbvh_vertex_iter_end; + } + } + + MEM_SAFE_FREE(nodes); +} + +static void SCULPT_boundary_build_smoothco(SculptSession *ss, SculptBoundary *boundary) +{ + const int totvert = SCULPT_vertex_count_get(ss); + PBVHNode **nodes; + int totnode; + + boundary->smoothco = MEM_cnew_array(totvert, "boundary->smoothco"); + + float projection = ss->cache->brush->autosmooth_projection; + float slide_fset = ss->cache->brush->autosmooth_fset_slide; + + BKE_pbvh_get_nodes(ss->pbvh, PBVH_Leaf, &nodes, &totnode); + + for (int iteration = 0; iteration < 3; iteration++) { + for (int i = 0; i < totnode; i++) { + PBVHNode *node = nodes[i]; + PBVHVertexIter vd; + + BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) { + if (boundary->boundary_dist[vd.index] == FLT_MAX) { + continue; + } + + float sco[3]; + + SCULPT_neighbor_coords_average_interior(ss, sco, vd.vertex, projection, slide_fset); + + float *co = SCULPT_brush_deform_target_vertex_co_get(ss, boundary->deform_target, &vd); + + interp_v3_v3v3(sco, sco, co, 0.25); + BKE_pbvh_node_mark_update(node); + + copy_v3_v3(boundary->smoothco[vd.index], sco); + } + BKE_pbvh_vertex_iter_end; + } + } + + MEM_SAFE_FREE(nodes); +} + +/* Main Brush Function. */ void SCULPT_do_boundary_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) { SculptSession *ss = ob->sculpt; Brush *brush = BKE_paint_brush(&sd->paint); + SCULPT_cotangents_begin(ob, ss); + + const float radius = ss->cache->radius; + const float boundary_radius = brush ? radius * brush->boundary_offset : radius; + const ePaintSymmetryFlags symm_area = ss->cache->mirror_symmetry_pass; if (SCULPT_stroke_is_first_brush_step_of_symmetry_pass(ss->cache)) { @@ -994,13 +2044,12 @@ void SCULPT_do_boundary_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totn } ss->cache->boundaries[symm_area] = SCULPT_boundary_data_init( - ob, brush, initial_vertex, ss->cache->initial_radius); + sd, ob, brush, initial_vertex, ss->cache->initial_radius); if (ss->cache->boundaries[symm_area]) { - switch (brush->boundary_deform_type) { case BRUSH_BOUNDARY_DEFORM_BEND: - sculpt_boundary_bend_data_init(ss, ss->cache->boundaries[symm_area]); + sculpt_boundary_bend_data_init(ss, ss->cache->boundaries[symm_area], boundary_radius); break; case BRUSH_BOUNDARY_DEFORM_EXPAND: sculpt_boundary_slide_data_init(ss, ss->cache->boundaries[symm_area]); @@ -1008,6 +2057,9 @@ void SCULPT_do_boundary_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totn case BRUSH_BOUNDARY_DEFORM_TWIST: sculpt_boundary_twist_data_init(ss, ss->cache->boundaries[symm_area]); break; + case BRUSH_BOUNDARY_DEFORM_CIRCLE: + sculpt_boundary_circle_data_init(ss, ss->cache->boundaries[symm_area]); + break; case BRUSH_BOUNDARY_DEFORM_INFLATE: case BRUSH_BOUNDARY_DEFORM_GRAB: /* Do nothing. These deform modes don't need any extra data to be precomputed. */ @@ -1015,7 +2067,34 @@ void SCULPT_do_boundary_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totn } sculpt_boundary_falloff_factor_init( - ss, ss->cache->boundaries[symm_area], brush, ss->cache->initial_radius); + ss, sd, ss->cache->boundaries[symm_area], brush, ss->cache->initial_radius); + } + + if (ss->bm && ss->cache->boundaries[symm_area] && + ss->cache->boundaries[symm_area]->boundary_dist) { + PBVHNode **nodes2; + int totnode2 = 0; + + BKE_pbvh_get_nodes(ss->pbvh, PBVH_Leaf, &nodes2, &totnode2); + + for (int i = 0; i < totnode2; i++) { + PBVHNode *node = nodes2[i]; + PBVHVertexIter vd; + + bool ok = false; + + BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) { + if (ss->cache->boundaries[symm_area]->boundary_dist[vd.index] != FLT_MAX) { + ok = true; + break; + } + } + BKE_pbvh_vertex_iter_end; + + if (ok) { + SCULPT_ensure_dyntopo_node_undo(ob, node, SCULPT_UNDO_COORDS, -1); + } + } } } @@ -1052,6 +2131,15 @@ void SCULPT_do_boundary_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totn case BRUSH_BOUNDARY_DEFORM_SMOOTH: BLI_task_parallel_range(0, totnode, &data, do_boundary_brush_smooth_task_cb_ex, &settings); break; + case BRUSH_BOUNDARY_DEFORM_CIRCLE: + BLI_task_parallel_range(0, totnode, &data, do_boundary_brush_circle_task_cb_ex, &settings); + break; + } + + if (brush->autosmooth_factor > 0.0f) { + BKE_pbvh_update_normals(ss->pbvh, ss->subdiv_ccg); + + SCULPT_boundary_autosmooth(ss, ss->cache->boundaries[symm_area]); } } diff --git a/source/blender/editors/sculpt_paint/sculpt_brush_machine.c b/source/blender/editors/sculpt_paint/sculpt_brush_machine.c new file mode 100644 index 00000000000..e69de29bb2d diff --git a/source/blender/editors/sculpt_paint/sculpt_brush_types.cc b/source/blender/editors/sculpt_paint/sculpt_brush_types.cc index 8bec48bfb65..e56108a3631 100644 --- a/source/blender/editors/sculpt_paint/sculpt_brush_types.cc +++ b/source/blender/editors/sculpt_paint/sculpt_brush_types.cc @@ -1033,7 +1033,7 @@ static void do_clay_strips_brush_task_cb_ex(void *__restrict userdata, data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]); BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { - if (!SCULPT_brush_test_cube(&test, vd.co, mat, brush->tip_roundness)) { + if (!SCULPT_brush_test_cube(&test, vd.co, mat, brush->tip_roundness, true)) { continue; } @@ -1346,7 +1346,7 @@ static void do_thumb_brush_task_cb_ex(void *__restrict userdata, data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]); BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { - SCULPT_orig_vert_data_update(&orig_data, &vd); + SCULPT_orig_vert_data_update(&orig_data, vd.vertex); if (!sculpt_brush_test_sq_fn(&test, orig_data.co)) { continue; @@ -1425,7 +1425,7 @@ static void do_rotate_brush_task_cb_ex(void *__restrict userdata, data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]); BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { - SCULPT_orig_vert_data_update(&orig_data, &vd); + SCULPT_orig_vert_data_update(&orig_data, vd.vertex); if (!sculpt_brush_test_sq_fn(&test, orig_data.co)) { continue; @@ -1505,7 +1505,7 @@ static void do_layer_brush_task_cb_ex(void *__restrict userdata, data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]); BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { - SCULPT_orig_vert_data_update(&orig_data, &vd); + SCULPT_orig_vert_data_update(&orig_data, vd.vertex); if (!sculpt_brush_test_sq_fn(&test, orig_data.co)) { continue; @@ -1526,7 +1526,7 @@ static void do_layer_brush_task_cb_ex(void *__restrict userdata, const int vi = vd.index; float *disp_factor; if (use_persistent_base) { - disp_factor = (float *)SCULPT_vertex_attr_get(vd.vertex, ss->attrs.persistent_disp); + disp_factor = SCULPT_vertex_attr_get(vd.vertex, ss->attrs.persistent_disp); } else { disp_factor = &ss->cache->layer_displacement_factor[vi]; @@ -2027,7 +2027,7 @@ static void do_grab_brush_task_cb_ex(void *__restrict userdata, data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]); BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { - SCULPT_orig_vert_data_update(&orig_data, &vd); + SCULPT_orig_vert_data_update(&orig_data, vd.vertex); if (!sculpt_brush_test_sq_fn(&test, orig_data.co)) { continue; @@ -2133,7 +2133,7 @@ static void do_elastic_deform_brush_task_cb_ex(void *__restrict userdata, ¶ms, ss->cache->radius, force, 1.0f, brush->elastic_deform_volume_preservation); BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { - SCULPT_orig_vert_data_update(&orig_data, &vd); + SCULPT_orig_vert_data_update(&orig_data, vd.vertex); SCULPT_automasking_node_update(ss, &automask_data, &vd); float final_disp[3]; @@ -2236,7 +2236,7 @@ static void do_draw_sharp_brush_task_cb_ex(void *__restrict userdata, data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]); BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { - SCULPT_orig_vert_data_update(&orig_data, &vd); + SCULPT_orig_vert_data_update(&orig_data, vd.vertex); if (!sculpt_brush_test_sq_fn(&test, orig_data.co)) { continue; } @@ -2326,7 +2326,7 @@ static void do_topology_slide_task_cb_ex(void *__restrict userdata, data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]); BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { - SCULPT_orig_vert_data_update(&orig_data, &vd); + SCULPT_orig_vert_data_update(&orig_data, vd.vertex); if (!sculpt_brush_test_sq_fn(&test, orig_data.co)) { continue; } @@ -2385,53 +2385,66 @@ static void do_topology_slide_task_cb_ex(void *__restrict userdata, void SCULPT_relax_vertex(SculptSession *ss, PBVHVertexIter *vd, float factor, - bool filter_boundary_face_sets, + eSculptBoundary boundary_mask, float *r_final_pos) { float smooth_pos[3]; float final_disp[3]; - float boundary_normal[3]; int avg_count = 0; int neighbor_count = 0; zero_v3(smooth_pos); - zero_v3(boundary_normal); - const bool is_boundary = SCULPT_vertex_is_boundary(ss, vd->vertex); + + eSculptBoundary bset = boundary_mask; + + // forcibly enable if no ss->cache + if (ss->cache && (ss->cache->brush->flag2 & BRUSH_SMOOTH_PRESERVE_FACE_SETS)) { + bset |= SCULPT_BOUNDARY_FACE_SET; + } + + if (SCULPT_vertex_is_corner(ss, vd->vertex, (eSculptCorner)bset)) { + copy_v3_v3(r_final_pos, vd->co); + return; + } + + const eSculptBoundary is_boundary = SCULPT_vertex_is_boundary(ss, vd->vertex, bset); + + float boundary_tan_a[3]; + float boundary_tan_b[3]; + bool have_boundary_tan_a = false; SculptVertexNeighborIter ni; SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd->vertex, ni) { neighbor_count++; - if (!filter_boundary_face_sets || - (filter_boundary_face_sets && !SCULPT_vertex_has_unique_face_set(ss, ni.vertex))) { - /* When the vertex to relax is boundary, use only connected boundary vertices for the average - * position. */ - if (is_boundary) { - if (!SCULPT_vertex_is_boundary(ss, ni.vertex)) { - continue; - } - add_v3_v3(smooth_pos, SCULPT_vertex_co_get(ss, ni.vertex)); - avg_count++; + /* When the vertex to relax is boundary, use only connected boundary vertices for the + * average position. */ + if (is_boundary) { + if (SCULPT_vertex_is_boundary(ss, ni.vertex, bset) == SCULPT_BOUNDARY_NONE) { + continue; + } + add_v3_v3(smooth_pos, SCULPT_vertex_co_get(ss, ni.vertex)); + avg_count++; - /* Calculate a normal for the constraint plane using the edges of the boundary. */ - float to_neighbor[3]; - sub_v3_v3v3(to_neighbor, SCULPT_vertex_co_get(ss, ni.vertex), vd->co); - normalize_v3(to_neighbor); - add_v3_v3(boundary_normal, to_neighbor); + /* Calculate a normal for the constraint plane using the edges of the boundary. */ + float to_neighbor[3]; + sub_v3_v3v3(to_neighbor, SCULPT_vertex_co_get(ss, ni.vertex), vd->co); + normalize_v3(to_neighbor); + + if (!have_boundary_tan_a) { + copy_v3_v3(boundary_tan_a, to_neighbor); + have_boundary_tan_a = true; } else { - add_v3_v3(smooth_pos, SCULPT_vertex_co_get(ss, ni.vertex)); - avg_count++; + copy_v3_v3(boundary_tan_b, to_neighbor); } } + else { + add_v3_v3(smooth_pos, SCULPT_vertex_co_get(ss, ni.vertex)); + avg_count++; + } } SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); - /* Don't modify corner vertices. */ - if (neighbor_count <= 2) { - copy_v3_v3(r_final_pos, vd->co); - return; - } - if (avg_count > 0) { mul_v3_fl(smooth_pos, 1.0f / avg_count); } @@ -2444,8 +2457,9 @@ void SCULPT_relax_vertex(SculptSession *ss, float smooth_closest_plane[3]; float vno[3]; - if (is_boundary && avg_count == 2) { - normalize_v3_v3(vno, boundary_normal); + if ((is_boundary) && avg_count == 2 && fabsf(dot_v3v3(boundary_tan_a, boundary_tan_b)) < 0.99f) { + cross_v3_v3v3(vno, boundary_tan_a, boundary_tan_b); + normalize_v3(vno); } else { SCULPT_vertex_normal_get(ss, vd->vertex, vno); @@ -2490,7 +2504,7 @@ static void do_topology_relax_task_cb_ex(void *__restrict userdata, data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]); BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { - SCULPT_orig_vert_data_update(&orig_data, &vd); + SCULPT_orig_vert_data_update(&orig_data, vd.vertex); if (!sculpt_brush_test_sq_fn(&test, orig_data.co)) { continue; } @@ -2507,7 +2521,7 @@ static void do_topology_relax_task_cb_ex(void *__restrict userdata, thread_id, &automask_data); - SCULPT_relax_vertex(ss, &vd, fade * bstrength, false, vd.co); + SCULPT_relax_vertex(ss, &vd, fade * bstrength, SCULPT_BOUNDARY_MESH, vd.co); if (vd.is_mesh) { BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); } @@ -2833,7 +2847,9 @@ static void do_topology_rake_bmesh_task_cb_ex(void *__restrict userdata, float avg[3], val[3]; - SCULPT_bmesh_four_neighbor_average(avg, direction, vd.bm_vert); + int cd_temp = data->scl->bmesh_cd_offset; + SCULPT_bmesh_four_neighbor_average( + ss, avg, direction, vd.bm_vert, 1.0f, true, cd_temp, ss->cd_sculpt_vert, false); sub_v3_v3v3(val, avg, vd.co); @@ -2852,6 +2868,7 @@ void SCULPT_bmesh_topology_rake( Sculpt *sd, Object *ob, PBVHNode **nodes, const int totnode, float bstrength) { Brush *brush = BKE_paint_brush(&sd->paint); + SculptSession *ss = ob->sculpt; const float strength = clamp_f(bstrength, 0.0f, 1.0f); /* Interactions increase both strength and quality. */ @@ -2861,6 +2878,10 @@ void SCULPT_bmesh_topology_rake( const int count = iterations * strength + 1; const float factor = iterations * strength / count; + SculptAttributeParams params = {}; + ss->attrs.rake_temp = BKE_sculpt_attribute_ensure( + ob, ATTR_DOMAIN_POINT, CD_PROP_COLOR, SCULPT_ATTRIBUTE_NAME(rake_temp), ¶ms); + for (iteration = 0; iteration <= count; iteration++) { SculptThreadedTaskData data{}; @@ -2869,6 +2890,7 @@ void SCULPT_bmesh_topology_rake( data.brush = brush; data.nodes = nodes; data.strength = factor; + data.scl = ss->attrs.rake_temp; TaskParallelSettings settings; BKE_pbvh_parallel_range_settings(&settings, true, totnode); diff --git a/source/blender/editors/sculpt_paint/sculpt_cloth.cc b/source/blender/editors/sculpt_paint/sculpt_cloth.cc index 3ceab5431ec..208f3732baa 100644 --- a/source/blender/editors/sculpt_paint/sculpt_cloth.cc +++ b/source/blender/editors/sculpt_paint/sculpt_cloth.cc @@ -1245,14 +1245,16 @@ void SCULPT_cloth_simulation_free(SculptClothSimulation *cloth_sim) MEM_SAFE_FREE(cloth_sim); } -void SCULPT_cloth_simulation_limits_draw(const uint gpuattr, +void SCULPT_cloth_simulation_limits_draw(const SculptSession *ss, + const Sculpt *sd, + const uint gpuattr, const Brush *brush, const float location[3], const float normal[3], - const float rds, - const float line_width, + float rds, + float line_width, const float outline_col[3], - const float alpha) + float alpha) { float cursor_trans[4][4], cursor_rot[4][4]; const float z_axis[4] = {0.0f, 0.0f, 1.0f, 0.0f}; @@ -1497,7 +1499,7 @@ static int sculpt_cloth_filter_modal(bContext *C, wmOperator *op, const wmEvent float filter_strength = RNA_float_get(op->ptr, "strength"); if (event->type == LEFTMOUSE && event->val == KM_RELEASE) { - SCULPT_filter_cache_free(ss); + SCULPT_filter_cache_free(ss, ob); SCULPT_undo_push_end(ob); SCULPT_flush_update_done(C, ob, SCULPT_UPDATE_COORDS); return OPERATOR_FINISHED; @@ -1561,7 +1563,7 @@ static int sculpt_cloth_filter_invoke(bContext *C, wmOperator *op, const wmEvent /* Update the active vertex */ float mval_fl[2] = {float(event->mval[0]), float(event->mval[1])}; SculptCursorGeometryInfo sgi; - SCULPT_cursor_geometry_info_update(C, &sgi, mval_fl, false); + SCULPT_cursor_geometry_info_update(C, &sgi, mval_fl, false, false); SCULPT_vertex_random_access_ensure(ss); diff --git a/source/blender/editors/sculpt_paint/sculpt_curvature.cc b/source/blender/editors/sculpt_paint/sculpt_curvature.cc new file mode 100644 index 00000000000..d2c1d8b8584 --- /dev/null +++ b/source/blender/editors/sculpt_paint/sculpt_curvature.cc @@ -0,0 +1,261 @@ +/* + * 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) 2021 by Joseph Eagar + * All rights reserved. + * Implements curvature analysis for sculpt tools + */ + +/** \file + * \ingroup edsculpt + */ + +#include "MEM_guardedalloc.h" + +#include "BLI_alloca.h" +#include "BLI_array.h" +#include "BLI_blenlib.h" +#include "BLI_dial_2d.h" +#include "BLI_ghash.h" +#include "BLI_gsqueue.h" +#include "BLI_hash.h" +#include "BLI_math.h" +#include "BLI_math_color_blend.h" +#include "BLI_math_solvers.h" +#include "BLI_task.h" +#include "BLI_utildefines.h" + +#include "BLT_translation.h" + +#include "PIL_time.h" + +#include "DNA_brush_types.h" +#include "DNA_customdata_types.h" +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_node_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" + +#include "BKE_brush.h" +#include "BKE_ccg.h" +#include "BKE_colortools.h" +#include "BKE_context.h" +#include "BKE_image.h" +#include "BKE_kelvinlet.h" +#include "BKE_key.h" +#include "BKE_lib_id.h" +#include "BKE_main.h" +#include "BKE_mesh.h" +#include "BKE_mesh_mapping.h" +#include "BKE_mesh_mirror.h" +#include "BKE_modifier.h" +#include "BKE_multires.h" +#include "BKE_node.h" +#include "BKE_object.h" +#include "BKE_paint.h" +#include "BKE_particle.h" +#include "BKE_pbvh.h" +#include "BKE_pointcache.h" +#include "BKE_report.h" +#include "BKE_scene.h" +#include "BKE_screen.h" +#include "BKE_subdiv_ccg.h" +#include "BKE_subsurf.h" + +#include "DEG_depsgraph.h" + +#include "IMB_colormanagement.h" + +#include "GPU_immediate.h" +#include "GPU_immediate_util.h" +#include "GPU_matrix.h" +#include "GPU_state.h" + +#include "WM_api.h" +#include "WM_message.h" +#include "WM_toolsystem.h" +#include "WM_types.h" + +#include "ED_object.h" +#include "ED_screen.h" +#include "ED_sculpt.h" +#include "ED_space_api.h" +#include "ED_view3d.h" +#include "paint_intern.h" +#include "sculpt_intern.hh" + +#include "RNA_access.h" +#include "RNA_define.h" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "bmesh.h" +#include "bmesh_tools.h" + +#include +#include +#include + +/* +If you're working with uniform triangle tesselations, the math for +calculating principle curvatures reduces to doing an eigen decomposition +of the smoothed normal covariance matrix. + +The normal covariance matrix is just: + +nx*nx nx*ny nx*nz +ny*nx ny*ny ny*nz +nz*nx nz*ny nz*nz + +To find principle curvatures, simply subtract neighboring covariance matrices. +You can do this over any number of neighborhood rings to get more accurate result + +*/ + +BLI_INLINE void normal_covariance(float mat[3][3], float no[3]) +{ + mat[0][0] = no[0] * no[0]; + mat[0][1] = no[0] * no[1]; + mat[0][2] = no[0] * no[2]; + mat[1][0] = no[1] * no[0]; + mat[1][1] = no[1] * no[1]; + mat[1][2] = no[1] * no[2]; + mat[2][0] = no[2] * no[0]; + mat[2][1] = no[2] * no[1]; + mat[2][2] = no[2] * no[2]; +} + +bool SCULPT_calc_principle_curvatures(SculptSession *ss, + PBVHVertRef vertex, + SculptCurvatureData *out, + bool useAccurateSolver) +{ + SculptVertexNeighborIter ni; + float nmat[3][3], nmat2[3][3]; + float no[3], no2[3]; + + memset(out, 0, sizeof(SculptCurvatureData)); + + SCULPT_vertex_normal_get(ss, vertex, no); + normal_covariance(nmat, no); + + if (useAccurateSolver) { + int val = SCULPT_vertex_valence_get(ss, vertex); + float *ws = BLI_array_alloca(ws, val); + float *cot1 = BLI_array_alloca(cot1, val); + float *cot2 = BLI_array_alloca(cot2, val); + float *areas = BLI_array_alloca(areas, val); + float totarea = 0.0f; + + SCULPT_get_cotangents(ss, vertex, ws, cot1, cot2, areas, &totarea); + + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vertex, ni) { + SCULPT_vertex_normal_get(ss, ni.vertex, no2); + sub_v3_v3(no2, no); + + normal_covariance(nmat2, no2); + madd_m3_m3m3fl(nmat, nmat, nmat2, ws[ni.i]); + } + SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); + } + else { + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vertex, ni) { + SCULPT_vertex_normal_get(ss, ni.vertex, no2); + sub_v3_v3(no2, no); + + normal_covariance(nmat2, no2); + add_m3_m3m3(nmat, nmat, nmat2); + } + SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); + } + + if (!useAccurateSolver || !BLI_eigen_solve_selfadjoint_m3(nmat, out->ks, out->principle)) { + // do simple power solve in one direction + + float t[3]; + float t2[3]; + + SCULPT_vertex_normal_get(ss, vertex, no); + copy_v3_v3(t, no); + + for (int i = 0; i < 15; i++) { + if (i > 0) { + normalize_v3(t); + + if (i > 1 && len_squared_v3v3(t, t2) < 0.0001) { + break; + } + + copy_v3_v3(t2, t); + } + + mul_m3_v3(nmat, t); + } + + out->ks[1] = normalize_v3(t); + copy_v3_v3(out->principle[1], t); + + cross_v3_v3v3(out->principle[0], out->principle[1], no); + normalize_v3(out->principle[0]); + } + + return true; +} + +void SCULPT_curvature_dir_get(SculptSession *ss, + PBVHVertRef v, + float dir[3], + bool useAccurateSolver) +{ + if (BKE_pbvh_type(ss->pbvh) != PBVH_BMESH) { + SculptCurvatureData curv; + SCULPT_calc_principle_curvatures(ss, v, &curv, useAccurateSolver); + + copy_v3_v3(dir, curv.principle[0]); + return; + } + + BMVert *bv = (BMVert *)v.i; + MSculptVert *mv = BKE_PBVH_SCULPTVERT(ss->cd_sculpt_vert, bv); + + copy_v3_v3(dir, mv->curvature_dir); +} + +void SCULPT_curvature_begin(SculptSession *ss, struct PBVHNode *node, bool useAccurateSolver) +{ + if (BKE_pbvh_type(ss->pbvh) != PBVH_BMESH) { + // caching only happens for bmesh for now + return; + } + + if (BKE_pbvh_curvature_update_get(node)) { + PBVHVertexIter vi; + + BKE_pbvh_curvature_update_set(node, false); + + BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vi, PBVH_ITER_UNIQUE) { + BMVert *v = (BMVert *)vi.vertex.i; + MSculptVert *mv = BKE_PBVH_SCULPTVERT(ss->cd_sculpt_vert, v); + + SculptCurvatureData curv; + SCULPT_calc_principle_curvatures(ss, vi.vertex, &curv, useAccurateSolver); + + copy_v3_v3(mv->curvature_dir, curv.principle[0]); + } + BKE_pbvh_vertex_iter_end; + } +} diff --git a/source/blender/editors/sculpt_paint/sculpt_detail.cc b/source/blender/editors/sculpt_paint/sculpt_detail.cc index 0bc52e83e58..fff3cefeca5 100644 --- a/source/blender/editors/sculpt_paint/sculpt_detail.cc +++ b/source/blender/editors/sculpt_paint/sculpt_detail.cc @@ -50,16 +50,19 @@ struct SculptDetailRaycastData { float depth; float edge_length; - IsectRayPrecalc isect_precalc; + struct IsectRayPrecalc isect_precalc; + SculptSession *ss; }; static bool sculpt_and_constant_or_manual_detail_poll(bContext *C) { Object *ob = CTX_data_active_object(C); - Sculpt *sd = CTX_data_tool_settings(C)->sculpt; + // Sculpt *sd = CTX_data_tool_settings(C)->sculpt; - return SCULPT_mode_poll(C) && ob->sculpt->bm && - (sd->flags & (SCULPT_DYNTOPO_DETAIL_CONSTANT | SCULPT_DYNTOPO_DETAIL_MANUAL)); + /*checking for constant/manual mode isn't necassary since we do this on the python side + in the ui scripts*/ + return SCULPT_mode_poll(C) && ob->sculpt->bm; /*&& + (sd->flags & (SCULPT_DYNTOPO_DETAIL_CONSTANT | SCULPT_DYNTOPO_DETAIL_MANUAL));*/ } static bool sculpt_and_dynamic_topology_poll(bContext *C) @@ -79,6 +82,8 @@ static int sculpt_detail_flood_fill_exec(bContext *C, wmOperator *op) { Sculpt *sd = CTX_data_tool_settings(C)->sculpt; Object *ob = CTX_data_active_object(C); + Brush *brush = BKE_paint_brush(&sd->paint); + SculptSession *ss = ob->sculpt; float size; float bb_min[3], bb_max[3], center[3], dim[3]; @@ -101,26 +106,104 @@ static int sculpt_detail_flood_fill_exec(bContext *C, wmOperator *op) sub_v3_v3v3(dim, bb_max, bb_min); size = max_fff(dim[0], dim[1], dim[2]); + float constant_detail = brush->dyntopo.constant_detail; + float detail_range = brush->dyntopo.detail_range; + /* Update topology size. */ float object_space_constant_detail = 1.0f / (sd->constant_detail * mat4_to_scale(ob->object_to_world)); - BKE_pbvh_bmesh_detail_size_set(ss->pbvh, object_space_constant_detail); + BKE_pbvh_bmesh_detail_size_set(ss->pbvh, object_space_constant_detail, detail_range); + BKE_pbvh_set_bm_log(ss->pbvh, ss->bm_log); + + bool enable_surface_relax = true; /* XXX should be user-configurable. */ SCULPT_undo_push_begin(ob, op); SCULPT_undo_push_node(ob, nullptr, SCULPT_UNDO_COORDS); - while (BKE_pbvh_bmesh_update_topology( - ss->pbvh, PBVH_Collapse | PBVH_Subdivide, center, nullptr, size, false, false)) { - for (int i = 0; i < totnodes; i++) { - BKE_pbvh_node_mark_topology_update(nodes[i]); + DyntopoMaskCB mask_cb; + void *mask_cb_data; + + SCULPT_dyntopo_automasking_init(ss, sd, nullptr, ob, &mask_cb, &mask_cb_data); + + const int max_steps = 2; + const int max_dyntopo_steps_coll = 1 << 13; + const int max_dyntopo_steps_subd = 1 << 15; + + int i = 0; + bool modified = true; + + while (modified) { + modified = BKE_pbvh_bmesh_update_topology(ss->pbvh, + PBVH_Collapse, + center, + nullptr, + size, + false, + false, + -1, + false, + mask_cb, + mask_cb_data, + max_dyntopo_steps_coll, + enable_surface_relax, + false); + + for (int j = 0; j < totnodes; j++) { + BKE_pbvh_node_mark_topology_update(nodes[j]); + } + + modified |= BKE_pbvh_bmesh_update_topology(ss->pbvh, + PBVH_Subdivide, + center, + nullptr, + size, + false, + false, + -1, + false, + mask_cb, + mask_cb_data, + max_dyntopo_steps_subd, + enable_surface_relax, + false); + for (int j = 0; j < totnodes; j++) { + BKE_pbvh_node_mark_topology_update(nodes[j]); + } + + if (i++ > max_steps) { + break; } } + /* one more time, but with cleanup valence 3/4 verts enabled */ + for (i = 0; i < 2; i++) { + for (int j = 0; j < totnodes; j++) { + BKE_pbvh_node_mark_topology_update(nodes[j]); + } + + BKE_pbvh_bmesh_update_topology(ss->pbvh, + PBVH_Cleanup, + center, + nullptr, + size, + false, + false, + -1, + false, + mask_cb, + mask_cb_data, + max_dyntopo_steps_coll, + enable_surface_relax, + false); + } + + SCULPT_dyntopo_automasking_end(mask_cb_data); + MEM_SAFE_FREE(nodes); SCULPT_undo_push_end(ob); /* Force rebuild of PBVH for better BB placement. */ - SCULPT_pbvh_clear(ob); + SCULPT_pbvh_clear(ob, false); /* Redraw. */ WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob); @@ -170,7 +253,7 @@ static void sample_detail_voxel(bContext *C, ViewContext *vc, const int mval[2]) /* Update the active vertex. */ const float mval_fl[2] = {float(mval[0]), float(mval[1])}; - SCULPT_cursor_geometry_info_update(C, &sgi, mval_fl, false); + SCULPT_cursor_geometry_info_update(C, &sgi, mval_fl, false, false); BKE_sculpt_update_object_for_edit(depsgraph, ob, true, false, false); /* Average the edge length of the connected edges to the active vertex. */ @@ -193,8 +276,13 @@ static void sculpt_raycast_detail_cb(PBVHNode *node, void *data_v, float *tmin) { if (BKE_pbvh_node_get_tmin(node) < *tmin) { SculptDetailRaycastData *srd = static_cast(data_v); - if (BKE_pbvh_bmesh_node_raycast_detail( - node, srd->ray_start, &srd->isect_precalc, &srd->depth, &srd->edge_length)) { + + if (BKE_pbvh_bmesh_node_raycast_detail(srd->ss->pbvh, + node, + srd->ray_start, + &srd->isect_precalc, + &srd->depth, + &srd->edge_length)) { srd->hit = true; *tmin = srd->depth; } @@ -215,16 +303,24 @@ static void sample_detail_dyntopo(bContext *C, ViewContext *vc, const int mval[2 SculptDetailRaycastData srd; srd.hit = 0; + srd.ss = ob->sculpt; + srd.ray_start = ray_start; srd.depth = depth; srd.edge_length = 0.0f; isect_ray_tri_watertight_v3_precalc(&srd.isect_precalc, ray_normal); - BKE_pbvh_raycast(ob->sculpt->pbvh, sculpt_raycast_detail_cb, &srd, ray_start, ray_normal, false); + BKE_pbvh_raycast(ob->sculpt->pbvh, + sculpt_raycast_detail_cb, + &srd, + ray_start, + ray_normal, + false, + srd.ss->stroke_id); if (srd.hit && srd.edge_length > 0.0f) { /* Convert edge length to world space detail resolution. */ - sd->constant_detail = 1 / (srd.edge_length * mat4_to_scale(ob->object_to_world)); + brush->dyntopo.constant_detail = 1.0f / (srd.edge_length * mat4_to_scale(ob->object_to_world)); } } diff --git a/source/blender/editors/sculpt_paint/sculpt_displacement.c b/source/blender/editors/sculpt_paint/sculpt_displacement.c new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/source/blender/editors/sculpt_paint/sculpt_displacement.c @@ -0,0 +1 @@ + diff --git a/source/blender/editors/sculpt_paint/sculpt_displacement.h b/source/blender/editors/sculpt_paint/sculpt_displacement.h new file mode 100644 index 00000000000..ea38e1be42e --- /dev/null +++ b/source/blender/editors/sculpt_paint/sculpt_displacement.h @@ -0,0 +1,5 @@ +#pragma once + +typedef struct SculptDisplacer { + +} SculptDisplacer; diff --git a/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc b/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc index 986974f2e20..0167c328230 100644 --- a/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc +++ b/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc @@ -10,16 +10,30 @@ #include "MEM_guardedalloc.h" +#include "BLI_alloca.h" +#include "BLI_array.hh" +#include "BLI_blenlib.h" +#include "BLI_compiler_attrs.h" +#include "BLI_hash.h" +#include "BLI_linklist.h" +#include "BLI_math.h" +#include "BLI_memarena.h" +#include "BLI_polyfill_2d.h" +#include "BLI_task.h" + #include "BLT_translation.h" #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" #include "DNA_modifier_types.h" +#include "BKE_brush.h" +#include "BKE_colortools.h" #include "BKE_context.h" #include "BKE_global.h" #include "BKE_main.h" #include "BKE_mesh.h" +#include "BKE_mesh_mapping.h" #include "BKE_modifier.h" #include "BKE_object.h" #include "BKE_paint.h" @@ -41,73 +55,614 @@ #include "UI_interface.h" #include "UI_resources.h" +#include "../../bmesh/intern/bmesh_idmap.h" #include "bmesh.h" +#include "bmesh_log.h" #include "bmesh_tools.h" -using blender::IndexRange; +#include +#include -void SCULPT_dynamic_topology_triangulate(BMesh *bm) +using blender::Vector; + +BMesh *SCULPT_dyntopo_empty_bmesh() { - if (bm->totloop != bm->totface * 3) { - BM_mesh_triangulate(bm, - MOD_TRIANGULATE_QUAD_BEAUTY, - MOD_TRIANGULATE_NGON_EARCLIP, - 4, - false, - nullptr, - nullptr, - nullptr); + return BKE_sculptsession_empty_bmesh_create(); +} +// TODO: check if (mathematically speaking) is it really necassary +// to sort the edge lists around verts + +// from http://rodolphe-vaillant.fr/?e=20 +static float tri_voronoi_area(const float p[3], const float q[3], const float r[3]) +{ + float pr[3]; + float pq[3]; + + sub_v3_v3v3(pr, p, r); + sub_v3_v3v3(pq, p, q); + + float angles[3]; + + angle_tri_v3(angles, p, q, r); + + if (angles[0] > (float)M_PI * 0.5f) { + return area_tri_v3(p, q, r) / 2.0f; + } + else if (angles[1] > (float)M_PI * 0.5f || angles[2] > (float)M_PI * 0.5f) { + return area_tri_v3(p, q, r) / 4.0f; + } + else { + + float dpr = dot_v3v3(pr, pr); + float dpq = dot_v3v3(pq, pq); + + float area = (1.0f / 8.0f) * + (dpr * cotangent_tri_weight_v3(q, p, r) + dpq * cotangent_tri_weight_v3(r, q, p)); + + return area; } } -void SCULPT_pbvh_clear(Object *ob) +static float cotangent_tri_weight_v3_proj(const float n[3], + const float v1[3], + const float v2[3], + const float v3[3]) +{ + float a[3], b[3], c[3], c_len; + + sub_v3_v3v3(a, v2, v1); + sub_v3_v3v3(b, v3, v1); + + madd_v3_v3fl(a, n, -dot_v3v3(n, a)); + madd_v3_v3fl(b, n, -dot_v3v3(n, b)); + + cross_v3_v3v3(c, a, b); + + c_len = len_v3(c); + + if (c_len > FLT_EPSILON) { + return dot_v3v3(a, b) / c_len; + } + + return 0.0f; +} + +void SCULPT_dyntopo_get_cotangents(SculptSession *ss, + PBVHVertRef vertex, + float *r_ws, + float *r_cot1, + float *r_cot2, + float *r_area, + float *r_totarea) +{ + SCULPT_dyntopo_check_disk_sort(ss, vertex); + + BMVert *v = (BMVert *)vertex.i; + BMEdge *e = v->e; + + if (!e) { + return; + } + + int i = 0; + float totarea = 0.0f; + // float totw = 0.0f; + + do { + BMEdge *eprev = v == e->v1 ? e->v1_disk_link.prev : e->v2_disk_link.prev; + BMEdge *enext = v == e->v1 ? e->v1_disk_link.next : e->v2_disk_link.next; + + BMVert *v1 = BM_edge_other_vert(eprev, v); + BMVert *v2 = BM_edge_other_vert(e, v); + BMVert *v3 = BM_edge_other_vert(enext, v); + + float cot1 = cotangent_tri_weight_v3(v1->co, v->co, v2->co); + float cot2 = cotangent_tri_weight_v3(v3->co, v2->co, v->co); + + float area = tri_voronoi_area(v->co, v1->co, v2->co); + + r_ws[i] = (cot1 + cot2); + // totw += r_ws[i]; + + totarea += area; + + if (r_cot1) { + r_cot1[i] = cot1; + } + + if (r_cot2) { + r_cot2[i] = cot2; + } + + if (r_area) { + r_area[i] = area; + } + + i++; + e = enext; + } while (e != v->e); + + if (r_totarea) { + *r_totarea = totarea; + } + + int count = i; + + float mul = 1.0f / (totarea * 2.0); + + for (i = 0; i < count; i++) { + r_ws[i] *= mul; + } +} + +void SCULPT_faces_get_cotangents(SculptSession *ss, + PBVHVertRef vertex, + float *r_ws, + float *r_cot1, + float *r_cot2, + float *r_area, + float *r_totarea) +{ + // sculpt vemap should always be sorted in disk cycle order + + float totarea = 0.0; + float totw = 0.0; + + MeshElemMap *elem = ss->vemap + vertex.i; + for (int i = 0; i < elem->count; i++) { + int i1 = (i + elem->count - 1) % elem->count; + int i2 = i; + int i3 = (i + 1) % elem->count; + + const float *v = ss->vert_positions[vertex.i]; + const MEdge *e1 = ss->medge + elem->indices[i1]; + const MEdge *e2 = ss->medge + elem->indices[i2]; + const MEdge *e3 = ss->medge + elem->indices[i3]; + + const float *v1 = (unsigned int)vertex.i == e1->v1 ? ss->vert_positions[e1->v2] : + ss->vert_positions[e1->v1]; + const float *v2 = (unsigned int)vertex.i == e2->v1 ? ss->vert_positions[e2->v2] : + ss->vert_positions[e2->v1]; + const float *v3 = (unsigned int)vertex.i == e3->v1 ? ss->vert_positions[e3->v2] : + ss->vert_positions[e3->v1]; + + float cot1 = cotangent_tri_weight_v3(v1, v, v2); + float cot2 = cotangent_tri_weight_v3(v3, v2, v); + + float area = tri_voronoi_area(v, v1, v2); + + r_ws[i] = (cot1 + cot2); + totw += r_ws[i]; + + totarea += area; + + if (r_cot1) { + r_cot1[i] = cot1; + } + + if (r_cot2) { + r_cot2[i] = cot2; + } + + if (r_area) { + r_area[i] = area; + } + } + + if (r_totarea) { + *r_totarea = totarea; + } + + float mul = 1.0f / (totarea * 2.0); + + for (int i = 0; i < elem->count; i++) { + r_ws[i] *= mul; + } +} + +void SCULPT_cotangents_begin(Object *ob, SculptSession *ss) +{ + SCULPT_vertex_random_access_ensure(ss); + int totvert = SCULPT_vertex_count_get(ss); + + switch (BKE_pbvh_type(ss->pbvh)) { + case PBVH_BMESH: { + for (int i = 0; i < totvert; i++) { + PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); + SCULPT_dyntopo_check_disk_sort(ss, vertex); + } + break; + } + case PBVH_FACES: { + Mesh *mesh = BKE_object_get_original_mesh(ob); + + if (!ss->vemap) { + BKE_mesh_vert_edge_map_create(&ss->vemap, + &ss->vemap_mem, + ss->vert_positions, + BKE_mesh_edges(mesh), + mesh->totvert, + mesh->totedge, + true); + } + + break; + } + case PBVH_GRIDS: // not supported yet + break; + } +} + +void SCULPT_get_cotangents(SculptSession *ss, + PBVHVertRef vertex, + float *r_ws, + float *r_cot1, + float *r_cot2, + float *r_area, + float *r_totarea) +{ + switch (BKE_pbvh_type(ss->pbvh)) { + case PBVH_BMESH: + SCULPT_dyntopo_get_cotangents(ss, vertex, r_ws, r_cot1, r_cot2, r_area, r_totarea); + break; + case PBVH_FACES: + SCULPT_faces_get_cotangents(ss, vertex, r_ws, r_cot1, r_cot2, r_area, r_totarea); + break; + case PBVH_GRIDS: { + { + // not supported, return uniform weights; + + int val = SCULPT_vertex_valence_get(ss, vertex); + + for (int i = 0; i < val; i++) { + r_ws[i] = 1.0f; + } + } + break; + } + } +} + +void SCULT_dyntopo_flag_all_disk_sort(SculptSession *ss) +{ + BKE_pbvh_bmesh_flag_all_disk_sort(ss->pbvh); +} + +// returns true if edge disk list around vertex was sorted +bool SCULPT_dyntopo_check_disk_sort(SculptSession *ss, PBVHVertRef vertex) +{ + BMVert *v = (BMVert *)vertex.i; + MSculptVert *mv = BKE_PBVH_SCULPTVERT(ss->cd_sculpt_vert, v); + + if (mv->flag & SCULPTVERT_NEED_DISK_SORT) { + mv->flag &= ~SCULPTVERT_NEED_DISK_SORT; + + BM_sort_disk_cycle(v); + + return true; + } + + return false; +} + +/* +Copies the bmesh, but orders the elements +according to PBVH node to improve memory locality +*/ +void SCULPT_reorder_bmesh(SculptSession *ss) +{ +#if 0 + SCULPT_face_random_access_ensure(ss); + SCULPT_vertex_random_access_ensure(ss); + + int actv = ss->active_vertex.i ? + BKE_pbvh_vertex_to_index(ss->pbvh, ss->active_vertex) : + -1; + int actf = ss->active_face.i ? + BKE_pbvh_face_to_index(ss->pbvh, ss->active_face) : + -1; + + if (ss->bm_log) { + BM_log_full_mesh(ss->bm, ss->bm_log); + } + + ss->bm = BKE_pbvh_reorder_bmesh(ss->pbvh); + + SCULPT_face_random_access_ensure(ss); + SCULPT_vertex_random_access_ensure(ss); + + if (actv >= 0) { + ss->active_vertex = BKE_pbvh_index_to_vertex(ss->pbvh, actv); + } + if (actf >= 0) { + ss->active_face = BKE_pbvh_index_to_face(ss->pbvh, actf); + } + + BKE_sculptsession_update_attr_refs(ob); + + if (ss->bm_log) { + BM_log_set_bm(ss->bm, ss->bm_log); + } +#endif +} + +void SCULPT_dynamic_topology_triangulate(SculptSession *ss, BMesh *bm) +{ + if (bm->totloop == bm->totface * 3) { + ss->totfaces = ss->totpoly = ss->bm->totface; + ss->totvert = ss->bm->totvert; + + return; + } + + BMIter iter; + BMFace *f; + + BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { + BM_elem_flag_enable(f, BM_ELEM_TAG); + } + + MemArena *pf_arena = BLI_memarena_new(BLI_POLYFILL_ARENA_SIZE, __func__); + LinkNode *f_double = NULL; + + Vector(faces_array); + + BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { + if (f->len <= 3) { + continue; + } + + bool sel = BM_elem_flag_test(f, BM_ELEM_SELECT); + + int faces_array_tot = f->len; + faces_array.resize(faces_array_tot); + + BM_face_triangulate(bm, + f, + faces_array.data(), + &faces_array_tot, + NULL, + NULL, + &f_double, + MOD_TRIANGULATE_QUAD_BEAUTY, + MOD_TRIANGULATE_NGON_EARCLIP, + true, + pf_arena, + NULL); + + for (int i = 0; i < faces_array_tot; i++) { + BMFace *f2 = faces_array[i]; + + // forcibly copy selection state + if (sel) { + BM_face_select_set(bm, f2, true); + + // restore original face selection state too, triangulate code unset it + BM_face_select_set(bm, f, true); + } + + // paranoia check that tag flag wasn't copied over + BM_elem_flag_disable(f2, BM_ELEM_TAG); + } + } + + while (f_double) { + LinkNode *next = f_double->next; + BM_face_kill(bm, (BMFace *)f_double->link); + MEM_freeN(f_double); + f_double = next; + } + + BLI_memarena_free(pf_arena); + MEM_SAFE_FREE(faces_array); + + ss->totfaces = ss->totpoly = ss->bm->totface; + ss->totvert = ss->bm->totvert; + + // BM_mesh_triangulate( + // bm, MOD_TRIANGULATE_QUAD_BEAUTY, MOD_TRIANGULATE_NGON_EARCLIP, 4, false, NULL, NULL, + // NULL); +} + +void SCULPT_pbvh_clear(Object *ob, bool cache_pbvh) { SculptSession *ss = ob->sculpt; + BKE_pbvh_pmap_release(ss->pmap); + ss->pmap = NULL; + /* Clear out any existing DM and PBVH. */ if (ss->pbvh) { +#ifdef WITH_PBVH_CACHE + if (cache_pbvh) { + BKE_pbvh_set_cached(ob, ss->pbvh); + } + else { + BKE_pbvh_cache_remove(ss->pbvh); + BKE_pbvh_free(ss->pbvh); + } +#else BKE_pbvh_free(ss->pbvh); +#endif + ss->pbvh = nullptr; } - MEM_SAFE_FREE(ss->pmap); - - MEM_SAFE_FREE(ss->pmap_mem); - BKE_object_free_derived_caches(ob); /* Tag to rebuild PBVH in depsgraph. */ DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); } +/** + Syncs customdata layers with internal bmesh, but ignores deleted layers. +*/ +void SCULPT_dynamic_topology_sync_layers(Object *ob, Mesh *me) +{ + BKE_sculptsession_sync_attributes(ob, me); +} + +BMesh *BM_mesh_bm_from_me_threaded(BMesh *bm, + Object *ob, + const Mesh *me, + const struct BMeshFromMeshParams *params); + +static void customdata_strip_templayers(CustomData *cdata, int totelem) +{ + for (int i = 0; i < cdata->totlayer; i++) { + CustomDataLayer *layer = cdata->layers + i; + + if (layer->flag & CD_FLAG_TEMPORARY) { + CustomData_free_layer(cdata, layer->type, totelem, i); + i--; + } + } +} + void SCULPT_dynamic_topology_enable_ex(Main *bmain, Depsgraph *depsgraph, Scene *scene, Object *ob) { SculptSession *ss = ob->sculpt; - Mesh *me = static_cast(ob->data); - const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(me); + Mesh *me = BKE_object_get_original_mesh(ob); - SCULPT_pbvh_clear(ob); + customdata_strip_templayers(&me->vdata, me->totvert); + customdata_strip_templayers(&me->pdata, me->totpoly); - ss->bm_smooth_shading = (scene->toolsettings->sculpt->flags & SCULPT_DYNTOPO_SMOOTH_SHADING) != - 0; + if (ss->pbvh) { + if (ss->pmap) { + BKE_pbvh_pmap_release(ss->pmap); + ss->pmap = nullptr; + } + +#ifdef WITH_PBVH_CACHE + /* Remove existing pbvh so we can free it ourselves. */ + BKE_pbvh_cache_remove(ss->pbvh); + + /* Free any other pbvhs */ + BKE_pbvh_clear_cache(nullptr); +#endif + } + + if (ss->bm) { + bool ok = ss->bm->totvert == me->totvert && ss->bm->totedge == me->totedge && + ss->bm->totloop == me->totloop && ss->bm->totface == me->totpoly; + + if (!ok) { + /* Ensure ss->pbvh is in the cache so it can be destroyed in BKE_pbvh_free_bmesh. */ + +#ifdef WITH_PBVH_CACHE + if (ss->pbvh) { + BKE_pbvh_set_cached(ob, ss->pbvh); + } +#endif + + /* Destroy all cached PBVHs with this bmesh. */ + BKE_pbvh_free_bmesh(nullptr, ss->bm); + + ss->pbvh = nullptr; + ss->bm = nullptr; + } + } + + if (!ss->bm || !ss->pbvh || BKE_pbvh_type(ss->pbvh) != PBVH_BMESH) { + SCULPT_pbvh_clear(ob, false); + } +#if 0 + else { + /* Sculpt session was set up by paint.c, call BKE_sculptsession_update_attr_refs to be safe. */ + BKE_sculptsession_update_attr_refs(ob); + + BKE_pbvh_update_sculpt_verts(ss->pbvh); + + /* Also check bm_log. */ + if (!ss->bm_log) { + ss->bm_log = BM_log_create(ss->bm, ss->bm_idmap, ss->cd_sculpt_vert); + } + + return; + } +#endif + +#ifdef WITH_PBVH_CACHE + PBVH *pbvh = BKE_pbvh_get_or_free_cached(ob, BKE_object_get_original_mesh(ob), PBVH_BMESH); +#else + PBVH *pbvh = ss->pbvh; +#endif + + if (pbvh) { + BMesh *bm = BKE_pbvh_get_bmesh(pbvh); + + if (!ss->bm) { + ss->bm = bm; + } + else if (ss->bm != bm) { + printf("%s: bmesh differed!\n", __func__); + SCULPT_pbvh_clear(ob, false); + } + } /* Dynamic topology doesn't ensure selection state is valid, so remove T36280. */ BKE_mesh_mselect_clear(me); - /* Create triangles-only BMesh. */ - BMeshCreateParams create_params{}; - create_params.use_toolflags = false; - ss->bm = BM_mesh_create(&allocsize, &create_params); +#if 1 - BMeshFromMeshParams convert_params{}; - convert_params.calc_face_normal = true; - convert_params.calc_vert_normal = true; - convert_params.use_shapekey = true; - convert_params.active_shapekey = ob->shapenr; - BM_mesh_bm_from_me(ss->bm, me, &convert_params); - SCULPT_dynamic_topology_triangulate(ss->bm); + if (!ss->bm) { + ss->bm = BKE_sculptsession_empty_bmesh_create(); - BM_data_layer_add(ss->bm, &ss->bm->vdata, CD_PAINT_MASK); + BMeshFromMeshParams params = {0}; + params.calc_face_normal = true; + params.use_shapekey = true; + params.create_shapekey_layers = true; + params.active_shapekey = ob->shapenr; + + BM_mesh_bm_from_me(nullptr, ss->bm, me, ¶ms); + + if (ss->pbvh) { + BKE_sculptsession_update_attr_refs(ob); + BKE_pbvh_set_bmesh(ss->pbvh, ss->bm); + } + } +#else + ss->bm = BM_mesh_bm_from_me_threaded(nullptr, + nullptr, + me, + (&(struct BMeshFromMeshParams){ + .calc_face_normal = true, + .use_shapekey = true, + .active_shapekey = ob->shapenr, + })); +#endif + +#ifndef DYNTOPO_DYNAMIC_TESS + SCULPT_dynamic_topology_triangulate(ss, ss->bm); +#endif + + if (ss->pbvh) { + BKE_sculptsession_update_attr_refs(ob); + BKE_pbvh_update_sculpt_verts(ss->pbvh); + } + + if (ss->pbvh && SCULPT_has_persistent_base(ss)) { + SCULPT_ensure_persistent_layers(ss, ob); + } + + if (!CustomData_has_layer(&ss->bm->vdata, CD_PAINT_MASK)) { + BM_data_layer_add(ss->bm, &ss->bm->vdata, CD_PAINT_MASK); + } + + if (ss->pbvh) { + BKE_sculptsession_update_attr_refs(ob); + } + + BMIter iter; + BMEdge *e; + + BM_ITER_MESH (e, &iter, ss->bm, BM_EDGES_OF_MESH) { + e->head.hflag |= BM_ELEM_DRAW; + } + + if (ss->pbvh) { + BKE_pbvh_update_sculpt_verts(ss->pbvh); + } /* Make sure the data for existing faces are initialized. */ if (me->totpoly != ss->bm->totface) { @@ -117,12 +672,21 @@ void SCULPT_dynamic_topology_enable_ex(Main *bmain, Depsgraph *depsgraph, Scene /* Enable dynamic topology. */ me->flag |= ME_SCULPT_DYNAMIC_TOPOLOGY; + if (!ss->bm_idmap) { + ss->bm_idmap = BM_idmap_new(ss->bm, BM_VERT | BM_EDGE | BM_FACE); + BKE_sculptsession_update_attr_refs(ob); + } + /* Enable logging for undo/redo. */ - ss->bm_log = BM_log_create(ss->bm); + if (!ss->bm_log) { + ss->bm_log = BM_log_create(ss->bm, ss->bm_idmap); + } /* Update dependency graph, so modifiers that depend on dyntopo being enabled * are re-evaluated and the PBVH is re-created. */ DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); + + // TODO: this line here is being slow, do we need it? - joeedh BKE_scene_graph_update_tagged(depsgraph, bmain); } @@ -136,6 +700,9 @@ static void SCULPT_dynamic_topology_disable_ex( SculptSession *ss = ob->sculpt; Mesh *me = static_cast(ob->data); + /* destroy non-customdata temporary layers (which are rarely (never?) used for PBVH_BMESH) */ + BKE_sculpt_attribute_destroy_temporary_all(ob); + if (ss->attrs.dyntopo_node_id_vertex) { BKE_sculpt_attribute_destroy(ob, ss->attrs.dyntopo_node_id_vertex); } @@ -144,60 +711,29 @@ static void SCULPT_dynamic_topology_disable_ex( BKE_sculpt_attribute_destroy(ob, ss->attrs.dyntopo_node_id_face); } - SCULPT_pbvh_clear(ob); - - if (unode) { - /* Free all existing custom 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); - - /* Copy over stored custom data. */ - SculptUndoNodeGeometry *geometry = &unode->geometry_bmesh_enter; - me->totvert = geometry->totvert; - me->totloop = geometry->totloop; - me->totpoly = geometry->totpoly; - me->totedge = geometry->totedge; - me->totface = 0; - CustomData_copy( - &geometry->vdata, &me->vdata, CD_MASK_MESH.vmask, CD_DUPLICATE, geometry->totvert); - CustomData_copy( - &geometry->edata, &me->edata, CD_MASK_MESH.emask, CD_DUPLICATE, geometry->totedge); - CustomData_copy( - &geometry->ldata, &me->ldata, CD_MASK_MESH.lmask, CD_DUPLICATE, geometry->totloop); - CustomData_copy( - &geometry->pdata, &me->pdata, CD_MASK_MESH.pmask, CD_DUPLICATE, geometry->totpoly); - } - else { - BKE_sculptsession_bm_to_me(ob, true); - - /* Reset Face Sets as they are no longer valid. */ - CustomData_free_layer_named(&me->pdata, ".sculpt_face_set", me->totpoly); - me->face_sets_color_default = 1; - - /* Sync the visibility to vertices manually as the pmap is still not initialized. */ - bool *hide_vert = (bool *)CustomData_get_layer_named_for_write( - &me->vdata, CD_PROP_BOOL, ".hide_vert", me->totvert); - if (hide_vert != nullptr) { - memset(hide_vert, 0, sizeof(bool) * me->totvert); - } - } - + BKE_sculptsession_bm_to_me(ob, true); /* Clear data. */ me->flag &= ~ME_SCULPT_DYNAMIC_TOPOLOGY; - /* Typically valid but with global-undo they can be nullptr, see: T36234. */ - if (ss->bm) { - BM_mesh_free(ss->bm); - ss->bm = nullptr; + if (ss->bm_idmap) { + BM_idmap_destroy(ss->bm_idmap); + ss->bm_idmap = nullptr; } + if (ss->bm_log) { - BM_log_free(ss->bm_log); + BM_log_free(ss->bm_log, true); ss->bm_log = nullptr; } + /* Typically valid but with global-undo they can be NULL, see: T36234. */ + if (ss->bm) { + // PBVH now frees this + // BM_mesh_free(ss->bm); + ss->bm = nullptr; + } + + SCULPT_pbvh_clear(ob, true); + BKE_particlesystem_reset_all(ob); BKE_ptcache_object_reset(scene, ob, PTCACHE_RESET_OUTDATED); @@ -233,6 +769,8 @@ void sculpt_dynamic_topology_disable_with_undo(Main *bmain, if (use_undo) { SCULPT_undo_push_end(ob); } + + ss->active_vertex.i = ss->active_face.i = 0; } } @@ -242,6 +780,7 @@ static void sculpt_dynamic_topology_enable_with_undo(Main *bmain, Object *ob) { SculptSession *ss = ob->sculpt; + if (ss->bm == nullptr) { /* May be false in background mode. */ const bool use_undo = G.background ? (ED_undo_stack_get() != nullptr) : true; @@ -253,6 +792,8 @@ static void sculpt_dynamic_topology_enable_with_undo(Main *bmain, SCULPT_undo_push_node(ob, nullptr, SCULPT_UNDO_DYNTOPO_BEGIN); SCULPT_undo_push_end(ob); } + + ss->active_vertex.i = ss->active_face.i = 0; } } @@ -279,14 +820,33 @@ static int sculpt_dynamic_topology_toggle_exec(bContext *C, wmOperator * /*op*/) return OPERATOR_FINISHED; } +static int dyntopo_error_popup(bContext *C, wmOperatorType *ot, enum eDynTopoWarnFlag flag) +{ + uiPopupMenu *pup = UI_popup_menu_begin(C, IFACE_("Error!"), ICON_ERROR); + uiLayout *layout = UI_popup_menu_layout(pup); + + if (flag & DYNTOPO_ERROR_MULTIRES) { + const char *msg_error = TIP_("Multires modifier detected; cannot enable dyntopo."); + const char *msg = TIP_("Dyntopo and multires cannot be mixed."); + + uiItemL(layout, msg_error, ICON_INFO); + uiItemL(layout, msg, ICON_NONE); + uiItemS(layout); + } + + UI_popup_menu_end(C, pup); + + return OPERATOR_INTERFACE; +} + static int dyntopo_warning_popup(bContext *C, wmOperatorType *ot, enum eDynTopoWarnFlag flag) { uiPopupMenu *pup = UI_popup_menu_begin(C, IFACE_("Warning!"), ICON_ERROR); uiLayout *layout = UI_popup_menu_layout(pup); - if (flag & (DYNTOPO_WARN_VDATA | DYNTOPO_WARN_EDATA | DYNTOPO_WARN_LDATA)) { - const char *msg_error = TIP_("Attribute Data Detected"); - const char *msg = TIP_("Dyntopo will not preserve colors, UVs, or other attributes"); + if (flag & (DYNTOPO_WARN_EDATA)) { + const char *msg_error = TIP_("Edge Data Detected!"); + const char *msg = TIP_("Dyntopo will not preserve custom edge attributes"); uiItemL(layout, msg_error, ICON_INFO); uiItemL(layout, msg, ICON_NONE); uiItemS(layout); @@ -352,19 +912,6 @@ enum eDynTopoWarnFlag SCULPT_dynamic_topology_check(Scene *scene, Object *ob) BLI_assert(ss->bm == nullptr); UNUSED_VARS_NDEBUG(ss); - if (!dyntopo_supports_customdata_layers({me->vdata.layers, me->vdata.totlayer}, me->totvert)) { - flag |= DYNTOPO_WARN_VDATA; - } - if (!dyntopo_supports_customdata_layers({me->edata.layers, me->edata.totlayer}, me->totedge)) { - flag |= DYNTOPO_WARN_EDATA; - } - if (!dyntopo_supports_customdata_layers({me->pdata.layers, me->pdata.totlayer}, me->totpoly)) { - flag |= DYNTOPO_WARN_LDATA; - } - if (!dyntopo_supports_customdata_layers({me->ldata.layers, me->ldata.totlayer}, me->totloop)) { - flag |= DYNTOPO_WARN_LDATA; - } - { VirtualModifierData virtualModifierData; ModifierData *md = BKE_modifiers_get_virtual_modifierlist(ob, &virtualModifierData); @@ -376,6 +923,10 @@ enum eDynTopoWarnFlag SCULPT_dynamic_topology_check(Scene *scene, Object *ob) continue; } + if (md->type == eModifierType_Multires) { + flag |= DYNTOPO_ERROR_MULTIRES; + } + if (mti->type == eModifierTypeType_Constructive) { flag |= DYNTOPO_WARN_MODIFIER; break; @@ -397,7 +948,10 @@ static int sculpt_dynamic_topology_toggle_invoke(bContext *C, Scene *scene = CTX_data_scene(C); enum eDynTopoWarnFlag flag = SCULPT_dynamic_topology_check(scene, ob); - if (flag) { + if (flag & DYNTOPO_ERROR_MULTIRES) { + return dyntopo_error_popup(C, op->type, flag); + } + else if (flag) { /* The mesh has customdata that will be lost, let the user confirm this is OK. */ return dyntopo_warning_popup(C, op->type, flag); } @@ -411,7 +965,10 @@ void SCULPT_OT_dynamic_topology_toggle(wmOperatorType *ot) /* Identifiers. */ ot->name = "Dynamic Topology Toggle"; ot->idname = "SCULPT_OT_dynamic_topology_toggle"; - ot->description = "Dynamic topology alters the mesh topology while sculpting"; + ot->description = + "Dynamic mode; note that you must now check the DynTopo" + "option to enable dynamic remesher (which updates topology will sculpting)" + "this is on by default."; /* API callbacks. */ ot->invoke = sculpt_dynamic_topology_toggle_invoke; @@ -420,3 +977,817 @@ void SCULPT_OT_dynamic_topology_toggle(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } + +#define MAXUVLOOPS 32 +#define MAXUVNEIGHBORS 32 + +struct UVSmoothTri; + +typedef struct UVSmoothVert { + double uv[2]; + float co[3]; // world co + BMVert *v; + double w; + int totw; + bool pinned, boundary; + BMLoop *ls[MAXUVLOOPS]; + + struct UVSmoothVert *neighbors[MAXUVNEIGHBORS]; + struct UVSmoothTri *neighbor_tris[MAXUVNEIGHBORS]; + float neighbor_weights[MAXUVNEIGHBORS]; + + int totloop, totneighbor; + float brushfade; +} UVSmoothVert; + +typedef struct UVSmoothTri { + UVSmoothVert *vs[3]; + float area2d, area3d; +} UVSmoothTri; + +#define CON_MAX_VERTS 16 +typedef struct UVSmoothConstraint { + int type; + double k; + UVSmoothVert *vs[CON_MAX_VERTS]; + UVSmoothTri *tri; + double gs[CON_MAX_VERTS][2]; + int totvert; + double params[8]; +} UVSmoothConstraint; + +enum { CON_ANGLES = 0, CON_AREA = 1 }; + +typedef struct UVSolver { + BLI_mempool *verts; + BLI_mempool *tris; + int totvert, tottri; + float snap_limit; + BLI_mempool *constraints; + GHash *vhash; + GHash *fhash; + int cd_uv; + + double totarea3d; + double totarea2d; + + double strength; + int cd_sculpt_vert; + int cd_boundary_flag; +} UVSolver; + +/*that that currently this tool is *not* threaded*/ + +typedef struct SculptUVThreadData { + SculptThreadedTaskData data; + UVSolver *solver; +} SculptUVThreadData; + +static UVSolver *uvsolver_new(int cd_uv) +{ + UVSolver *solver = (UVSolver *)MEM_callocN(sizeof(*solver), "solver"); + + solver->strength = 1.0; + solver->cd_uv = cd_uv; + solver->snap_limit = 0.0025; + + solver->verts = BLI_mempool_create(sizeof(UVSmoothVert), 0, 512, BLI_MEMPOOL_ALLOW_ITER); + solver->tris = BLI_mempool_create(sizeof(UVSmoothTri), 0, 512, BLI_MEMPOOL_ALLOW_ITER); + solver->constraints = BLI_mempool_create( + sizeof(UVSmoothConstraint), 0, 512, BLI_MEMPOOL_ALLOW_ITER); + + solver->vhash = BLI_ghash_ptr_new("uvsolver"); + solver->fhash = BLI_ghash_ptr_new("uvsolver"); + + return solver; +} + +static void uvsolver_free(UVSolver *solver) +{ + BLI_mempool_destroy(solver->verts); + BLI_mempool_destroy(solver->tris); + BLI_mempool_destroy(solver->constraints); + + BLI_ghash_free(solver->vhash, NULL, NULL); + BLI_ghash_free(solver->fhash, NULL, NULL); + + MEM_freeN(solver); +} + +void *uvsolver_calc_loop_key(UVSolver *solver, BMLoop *l) +{ + // return (void *)l->v; + float *uv = (float *)BM_ELEM_CD_GET_VOID_P(l, solver->cd_uv); + + // float u = floorf(uv[0] / solver->snap_limit) * solver->snap_limit; + // float v = floorf(uv[1] / solver->snap_limit) * solver->snap_limit; + + intptr_t x = (intptr_t)(uv[0] * 16384.0); + intptr_t y = (intptr_t)(uv[1] * 16384.0); + intptr_t key; + int boundflag = BM_ELEM_CD_GET_INT(l->v, solver->cd_boundary_flag); + + if ((boundflag & (SCULPT_BOUNDARY_SEAM | SCULPT_BOUNDARY_UV)) || + (l->e->head.hflag | l->prev->e->head.hflag) & BM_ELEM_SEAM) { + key = y * 16384LL + x; + } + else { + key = (intptr_t)l->v; + } + + return POINTER_FROM_INT(key); +} + +static UVSmoothVert *uvsolver_get_vert(UVSolver *solver, BMLoop *l) +{ + float *uv = (float *)BM_ELEM_CD_GET_VOID_P(l, solver->cd_uv); + + void *pkey = uvsolver_calc_loop_key(solver, l); + void **entry = NULL; + UVSmoothVert *v; + + if (!BLI_ghash_ensure_p(solver->vhash, pkey, &entry)) { + v = (UVSmoothVert *)BLI_mempool_alloc(solver->verts); + memset(v, 0, sizeof(*v)); + + MSculptVert *mv = BKE_PBVH_SCULPTVERT(solver->cd_sculpt_vert, l->v); + MSculptVert *mv2 = BKE_PBVH_SCULPTVERT(solver->cd_sculpt_vert, l->prev->v); + MSculptVert *mv3 = BKE_PBVH_SCULPTVERT(solver->cd_sculpt_vert, l->next->v); + + int bound1 = BM_ELEM_CD_GET_INT(l->v, solver->cd_boundary_flag); + int bound2 = BM_ELEM_CD_GET_INT(l->prev->v, solver->cd_boundary_flag); + int bound3 = BM_ELEM_CD_GET_INT(l->next->v, solver->cd_boundary_flag); + + v->boundary = bound1 & SCULPT_BOUNDARY_SEAM; + if ((bound1 | bound2 | bound3) & SCULPT_CORNER_SHARP) { + v->pinned = true; + } + + // copy_v2_v2(v, uv); + v->uv[0] = (double)uv[0]; + v->uv[1] = (double)uv[1]; + + if (isnan(v->uv[0]) || !isfinite(v->uv[0])) { + v->uv[0] = 0.0f; + } + if (isnan(v->uv[1]) || !isfinite(v->uv[1])) { + v->uv[1] = 0.0f; + } + + copy_v3_v3(v->co, l->v->co); + v->v = l->v; + + *entry = (void *)v; + } + + v = (UVSmoothVert *)*entry; + + if (v->totloop < MAXUVLOOPS) { + v->ls[v->totloop++] = l; + } + + return v; +} + +MINLINE double area_tri_signed_v2_db(const double v1[2], const double v2[2], const double v3[2]) +{ + return 0.5 * ((v1[0] - v2[0]) * (v2[1] - v3[1]) + (v1[1] - v2[1]) * (v3[0] - v2[0])); +} + +MINLINE double area_tri_v2_db(const double v1[2], const double v2[2], const double v3[2]) +{ + return fabs(area_tri_signed_v2_db(v1, v2, v3)); +} + +void cross_tri_v3_db(double n[3], const double v1[3], const double v2[3], const double v3[3]) +{ + double n1[3], n2[3]; + + n1[0] = v1[0] - v2[0]; + n2[0] = v2[0] - v3[0]; + n1[1] = v1[1] - v2[1]; + n2[1] = v2[1] - v3[1]; + n1[2] = v1[2] - v2[2]; + n2[2] = v2[2] - v3[2]; + n[0] = n1[1] * n2[2] - n1[2] * n2[1]; + n[1] = n1[2] * n2[0] - n1[0] * n2[2]; + n[2] = n1[0] * n2[1] - n1[1] * n2[0]; +} + +double area_tri_v3_db(const double v1[3], const double v2[3], const double v3[3]) +{ + double n[3]; + cross_tri_v3_db(n, v1, v2, v3); + return len_v3_db(n) * 0.5; +} + +static UVSmoothTri *uvsolver_ensure_face(UVSolver *solver, BMFace *f) +{ + void **entry = NULL; + + if (BLI_ghash_ensure_p(solver->fhash, (void *)f, &entry)) { + return (UVSmoothTri *)*entry; + } + + UVSmoothTri *tri = (UVSmoothTri *)BLI_mempool_alloc(solver->tris); + memset((void *)tri, 0, sizeof(*tri)); + *entry = (void *)tri; + + BMLoop *l = f->l_first; + + bool nocon = false; + int i = 0; + do { + UVSmoothVert *sv = uvsolver_get_vert(solver, l); + + if (BM_elem_flag_test(l->e, BM_ELEM_SEAM)) { + nocon = true; + } + + tri->vs[i] = sv; + + if (i > 3) { + // bad! + break; + } + + i++; + } while ((l = l->next) != f->l_first); + + double area3d = (double)area_tri_v3(tri->vs[0]->co, tri->vs[1]->co, tri->vs[2]->co); + double area2d = area_tri_signed_v2_db(tri->vs[0]->uv, tri->vs[1]->uv, tri->vs[2]->uv); + + if (fabs(area2d) < 0.0000001) { + tri->vs[0]->uv[0] -= 0.00001; + tri->vs[0]->uv[1] -= 0.00001; + + tri->vs[1]->uv[0] += 0.00001; + tri->vs[2]->uv[1] += 0.00001; + } + + solver->totarea2d += fabs(area2d); + solver->totarea3d += area3d; + + tri->area2d = area2d; + tri->area3d = area3d; + + for (int i = 0; !nocon && i < 3; i++) { + UVSmoothConstraint *con = (UVSmoothConstraint *)BLI_mempool_alloc(solver->constraints); + memset((void *)con, 0, sizeof(*con)); + + con->type = CON_ANGLES; + con->k = 1.0; + + UVSmoothVert *v0 = tri->vs[(i + 2) % 3]; + UVSmoothVert *v1 = tri->vs[i]; + UVSmoothVert *v2 = tri->vs[(i + 1) % 3]; + + con->vs[0] = v0; + con->vs[1] = v1; + con->vs[2] = v2; + con->totvert = 3; + + float t1[3], t2[3]; + + sub_v3_v3v3(t1, v0->co, v1->co); + sub_v3_v3v3(t2, v2->co, v1->co); + + normalize_v3(t1); + normalize_v3(t2); + + float th3d = saacosf(dot_v3v3(t1, t2)); + + con->params[0] = (double)th3d; + + // area constraint + con = (UVSmoothConstraint *)BLI_mempool_alloc(solver->constraints); + memset((void *)con, 0, sizeof(*con)); + + con->type = CON_AREA; + con->k = 1.0; + + con->totvert = 3; + + con->tri = tri; + + con->vs[0] = v0; + con->vs[1] = v1; + con->vs[2] = v2; + } + +#if 1 + for (int i = 0; i < 3; i++) { + UVSmoothVert *v1 = tri->vs[i]; + UVSmoothVert *v2 = tri->vs[(i + 1) % 3]; + + bool ok = true; + + for (int j = 0; j < v1->totneighbor; j++) { + if (v1->neighbors[j] == v2) { + ok = false; + break; + } + } + + ok = ok && v1->totneighbor < MAXUVNEIGHBORS && v2->totneighbor < MAXUVNEIGHBORS; + + if (!ok) { + continue; + } + + v1->neighbor_tris[v1->totneighbor] = tri; + v1->neighbors[v1->totneighbor] = v2; + v1->neighbor_weights[v1->totneighbor] = 1.0; // area3d > 0.0 ? 1.0 / area3d : 100000.0; + v1->totneighbor++; + + v2->neighbor_tris[v2->totneighbor] = tri; + v2->neighbors[v2->totneighbor] = v1; + v2->neighbor_weights[v2->totneighbor] = 1.0; // area3d > 0.0 ? 1.0 / area3d : 100000.0; + v2->totneighbor++; + } +#endif + + return tri; +} + +static double normalize_v2_db(double v[2]) +{ + double len = v[0] * v[0] + v[1] * v[1]; + + if (len < 0.0000001) { + v[0] = v[1] = 0.0; + return 0.0; + } + + len = sqrt(len); + + double mul = 1.0 / len; + + v[0] *= mul; + v[1] *= mul; + + return len; +} + +static double uvsolver_eval_constraint(UVSolver *solver, UVSmoothConstraint *con) +{ + switch (con->type) { + case CON_ANGLES: { + UVSmoothVert *v0 = con->vs[0]; + UVSmoothVert *v1 = con->vs[1]; + UVSmoothVert *v2 = con->vs[2]; + double t1[2], t2[2]; + + sub_v2_v2v2_db(t1, v0->uv, v1->uv); + sub_v2_v2v2_db(t2, v2->uv, v1->uv); + + normalize_v2_db(t1); + normalize_v2_db(t2); + + double th = saacos(dot_v2v2_db(t1, t2)); + + double wind = t1[0] * t2[1] - t1[1] * t2[0]; + + if (wind >= 0.0) { + // th = M_PI - th; + } + + return th - con->params[0]; + } + case CON_AREA: { + UVSmoothVert *v0 = con->vs[0]; + UVSmoothVert *v1 = con->vs[1]; + UVSmoothVert *v2 = con->vs[2]; + + if (con->tri->area3d == 0.0 || solver->totarea3d == 0.0) { + return 0.0; + } + + double area2d = area_tri_signed_v2_db(v0->uv, v1->uv, v2->uv); + double goal = con->tri->area3d * solver->totarea2d / solver->totarea3d; + + con->tri->area2d = area2d; + return (fabs(area2d) - goal) * 1024.0; /* avoid excesively small numbers */ + } + default: + return 0.0; + } +} + +BLI_INLINE float uvsolver_vert_weight(UVSmoothVert *sv) +{ + double w = 1.0; + + if (sv->pinned || sv->boundary || sv->brushfade == 0.0f) { + w = 100000.0; + } + else { + return 1.0 / sv->brushfade; + } + + return w; +} + +static void uvsolver_solve_begin(UVSolver *solver) +{ + UVSmoothVert *sv; + BLI_mempool_iter iter; + + BLI_mempool_iternew(solver->verts, &iter); + sv = (UVSmoothVert *)BLI_mempool_iterstep(&iter); + BMIter liter; + + for (; sv; sv = (UVSmoothVert *)BLI_mempool_iterstep(&iter)) { + BMLoop *l; + sv->pinned = false; + + BM_ITER_ELEM (l, &liter, sv->v, BM_LOOPS_OF_VERT) { + if (!BLI_ghash_haskey(solver->fhash, (void *)l->f)) { + sv->pinned = true; + } + } + } +} + +static void uvsolver_simple_relax(UVSolver *solver, float strength) +{ + BLI_mempool_iter iter; + + UVSmoothVert *sv1; + BLI_mempool_iternew(solver->verts, &iter); + + sv1 = (UVSmoothVert *)BLI_mempool_iterstep(&iter); + for (; sv1; sv1 = (UVSmoothVert *)BLI_mempool_iterstep(&iter)) { + double uv[2] = {0.0, 0.0}; + double tot = 0.0; + int totneighbor = 0; + float strength2 = strength; + + if (!sv1->totneighbor || sv1->pinned) { + continue; + } + + bool lastsign; + + for (int i = 0; i < sv1->totneighbor; i++) { + UVSmoothVert *sv2 = sv1->neighbors[i]; + + if (!sv2 || (sv1->boundary && !sv2->boundary)) { + continue; + } + + bool sign = sv1->neighbor_tris[i]->area2d < 0.0f; + + // try to unfold folded uvs + if (totneighbor > 0 && sign != lastsign) { + strength2 = 1.0; + } + + lastsign = sign; + + double w = (double)sv1->neighbor_weights[i]; + + uv[0] += sv2->uv[0] * w; + uv[1] += sv2->uv[1] * w; + + tot += w; + totneighbor++; + } + + if (totneighbor < 2.0) { + continue; + } + + uv[0] /= tot; + uv[1] /= tot; + + sv1->uv[0] += (uv[0] - sv1->uv[0]) * strength2; + sv1->uv[1] += (uv[1] - sv1->uv[1]) * strength2; + } + + // update real uvs + + const int cd_uv = solver->cd_uv; + + BLI_mempool_iternew(solver->verts, &iter); + UVSmoothVert *sv = (UVSmoothVert *)BLI_mempool_iterstep(&iter); + for (; sv; sv = (UVSmoothVert *)BLI_mempool_iterstep(&iter)) { + for (int i = 0; i < sv->totloop; i++) { + BMLoop *l = sv->ls[i]; + float *uv = (float *)BM_ELEM_CD_GET_VOID_P(l, cd_uv); + + uv[0] = (float)sv->uv[0]; + uv[1] = (float)sv->uv[1]; + } + } +} + +static float uvsolver_solve_step(UVSolver *solver) +{ + BLI_mempool_iter iter; + + if (solver->strength < 0) { + uvsolver_simple_relax(solver, -solver->strength); + return 0.0f; + } + else { + uvsolver_simple_relax(solver, solver->strength * 0.1f); + } + + double error = 0.0; + + const double eval_limit = 0.00001; + const double df = 0.0001; + int totcon = 0; + + BLI_mempool_iternew(solver->constraints, &iter); + UVSmoothConstraint *con = (UVSmoothConstraint *)BLI_mempool_iterstep(&iter); + for (; con; con = (UVSmoothConstraint *)BLI_mempool_iterstep(&iter)) { + double r1 = uvsolver_eval_constraint(solver, con); + + if (fabs(r1) < eval_limit) { + totcon++; + continue; + } + + error += fabs(r1); + totcon++; + + double totg = 0.0; + double totw = 0.0; + + for (int i = 0; i < con->totvert; i++) { + UVSmoothVert *sv = con->vs[i]; + + for (int j = 0; j < 2; j++) { + double orig = sv->uv[j]; + sv->uv[j] += df; + + double r2 = uvsolver_eval_constraint(solver, con); + double g = (r2 - r1) / df; + + con->gs[i][j] = g; + totg += g * g; + + sv->uv[j] = orig; + + totw += uvsolver_vert_weight(sv); + } + } + + if (totg < eval_limit) { + continue; + } + + r1 *= -0.75 * con->k / totg; + + if (totw == 0.0) { + continue; + } + + totw = 1.0 / totw; + + for (int i = 0; i < con->totvert; i++) { + UVSmoothVert *sv = con->vs[i]; + + *(int *)BM_ELEM_CD_GET_VOID_P(sv->v, + solver->cd_boundary_flag) |= SCULPT_BOUNDARY_NEEDS_UPDATE; + + double w = uvsolver_vert_weight(sv) * totw; + w = MIN2(w, 1.0); + w = 1.0; + + for (int j = 0; j < 2; j++) { + double off = r1 * con->gs[i][j] * w; + + CLAMP(off, -0.1, 0.1); + sv->uv[j] += off; + } + } + } + + // update real uvs + + const int cd_uv = solver->cd_uv; + + BLI_mempool_iternew(solver->verts, &iter); + UVSmoothVert *sv = (UVSmoothVert *)BLI_mempool_iterstep(&iter); + for (; sv; sv = (UVSmoothVert *)BLI_mempool_iterstep(&iter)) { + for (int i = 0; i < sv->totloop; i++) { + BMLoop *l = sv->ls[i]; + float *uv = (float *)BM_ELEM_CD_GET_VOID_P(l, cd_uv); + + float fac = solver->strength * sv->brushfade; + + uv[0] += ((float)sv->uv[0] - uv[0]) * fac; + uv[1] += ((float)sv->uv[1] - uv[1]) * fac; + } + } + + return (float)error / (float)totcon; +} + +static void sculpt_uv_brush_cb(void *__restrict userdata, + const int n, + const TaskParallelTLS *__restrict tls) +{ + SculptUVThreadData *data1 = (SculptUVThreadData *)userdata; + SculptThreadedTaskData *data = &data1->data; + SculptSession *ss = data->ob->sculpt; + // const Brush *brush = data->brush; + // const float *offset = data->offset; + + SculptBrushTest test; + SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init(ss, &test); + + PBVHNode *node = data->nodes[n]; + TableGSet *faces = BKE_pbvh_bmesh_node_faces(node); + BMFace *f; + const int cd_uv = CustomData_get_offset(&ss->bm->ldata, CD_MLOOPUV); + + if (cd_uv < 0) { + return; // no uv layers + } + + BKE_pbvh_node_mark_update_color(node); + + TGSET_ITER (f, faces) { + BMLoop *l = f->l_first; + // float mask = 0.0f; + float cent[3] = {0}; + int tot = 0; + + // uvsolver_get_vert + do { + add_v3_v3(cent, l->v->co); + tot++; + } while ((l = l->next) != f->l_first); + + mul_v3_fl(cent, 1.0f / (float)tot); + + if (!sculpt_brush_test_sq_fn(&test, cent)) { + continue; + } + + BM_log_face_modified(ss->bm, ss->bm_log, f); + uvsolver_ensure_face(data1->solver, f); + + do { + BMIter iter; + BMLoop *l2; + int tot2 = 0; + float uv[2] = {0}; + bool ok = true; + UVSmoothVert *lastv = NULL; + + BM_ITER_ELEM (l2, &iter, l->v, BM_LOOPS_OF_VERT) { + if (l2->v != l->v) { + l2 = l2->next; + } + + UVSmoothVert *sv = uvsolver_get_vert(data1->solver, l2); + + if (lastv && lastv != sv) { + ok = false; + lastv->boundary = true; + sv->boundary = true; + } + + lastv = sv; + + float *luv = (float *)BM_ELEM_CD_GET_VOID_P(l2, cd_uv); + + add_v2_v2(uv, luv); + tot2++; + + if (BM_elem_flag_test(l2->e, BM_ELEM_SEAM)) { + ok = false; + sv->boundary = true; + } + } + + ok = ok && tot2; + + if (ok) { + mul_v2_fl(uv, 1.0f / (float)tot2); + + BM_ITER_ELEM (l2, &iter, l->v, BM_LOOPS_OF_VERT) { + if (l2->v != l->v) { + l2 = l2->next; + } + + float *luv = (float *)BM_ELEM_CD_GET_VOID_P(l2, cd_uv); + + if (len_v2v2(luv, uv) < 0.02) { + copy_v2_v2(luv, uv); + } + } + } + } while ((l = l->next) != f->l_first); + +#if 0 + do { + if (!sculpt_brush_test_sq_fn(&test, l->v->co)) { + continue; + } + + if (cd_mask >= 0) { + mask = BM_ELEM_CD_GET_FLOAT(l->v, cd_mask); + } + + PBVHVertRef vertex = {(intptr_t)l->v}; + + float direction2[3]; + const float fade = + bstrength * + SCULPT_brush_strength_factor( + ss, brush, vd.co, sqrtf(test.dist), NULL, l->v->no, mask, vertex, thread_id) * + ss->cache->pressure; + + } while ((l = l->next) != f->l_first); +#endif + } + TGSET_ITER_END; +} + +void SCULPT_uv_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) +{ + SculptSession *ss = ob->sculpt; + Brush *brush = BKE_paint_brush(&sd->paint); + float offset[3]; + + if (!ss->bm || BKE_pbvh_type(ss->pbvh) != PBVH_BMESH) { + // dyntopo only + return; + } + + const int cd_uv = CustomData_get_offset(&ss->bm->ldata, CD_MLOOPUV); + if (cd_uv < 0) { + return; // no uv layer? + } + + // add undo log subentry + BM_log_entry_add_ex(ss->bm, ss->bm_log, true); + + BKE_curvemapping_init(brush->curve); + + UVSolver *solver = uvsolver_new(cd_uv); + + solver->cd_boundary_flag = ss->attrs.boundary_flags->bmesh_cd_offset; + solver->cd_sculpt_vert = ss->cd_sculpt_vert; + // solver->strength = powf(fabs(ss->cache->bstrength), 0.25) * signf(ss->cache->bstrength); + solver->strength = ss->cache->bstrength; + + /* Threaded loop over nodes. */ + SculptUVThreadData data = {.solver = solver, + .data = { + .sd = sd, + .ob = ob, + .brush = brush, + .nodes = nodes, + .offset = offset, + }}; + + TaskParallelSettings settings; + + // for now, be single-threaded + BKE_pbvh_parallel_range_settings(&settings, false, totnode); + BLI_task_parallel_range(0, totnode, &data, sculpt_uv_brush_cb, &settings); + + uvsolver_solve_begin(solver); + + SculptBrushTest test; + SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init(ss, &test); + + BLI_mempool_iter iter; + BLI_mempool_iternew(solver->verts, &iter); + UVSmoothVert *sv = (UVSmoothVert *)BLI_mempool_iterstep(&iter); + + AutomaskingNodeData automask_data = {0}; + automask_data.have_orig_data = false; + + for (; sv; sv = (UVSmoothVert *)BLI_mempool_iterstep(&iter)) { + if (!sculpt_brush_test_sq_fn(&test, sv->v->co)) { + sv->brushfade = 0.0f; + continue; + } + + sv->brushfade = SCULPT_brush_strength_factor(ss, + brush, + sv->v->co, + sqrtf(test.dist), + NULL, + sv->v->no, + 0.0f, + (PBVHVertRef){.i = (intptr_t)sv->v}, + 0, + &automask_data); + } + + for (int i = 0; i < 5; i++) { + uvsolver_solve_step(solver); + } + + // tear down solver + uvsolver_free(solver); +} diff --git a/source/blender/editors/sculpt_paint/sculpt_expand.cc b/source/blender/editors/sculpt_paint/sculpt_expand.cc index e6a69501ac5..c8a3c6e54e3 100644 --- a/source/blender/editors/sculpt_paint/sculpt_expand.cc +++ b/source/blender/editors/sculpt_paint/sculpt_expand.cc @@ -59,7 +59,7 @@ * to generate these falloff values which will create different patterns in the result when using * the operator. These falloff values require algorithms that rely on mesh connectivity, so they * are only valid on parts of the mesh that are in the same connected component as the given - * initial vertices. If needed, these falloff values are propagated from vertex or grids into the + * initial verts. If needed, these falloff values are propagated from vertex or grids into the * base mesh faces. * * - On each modal callback, the operator gets the active vertex and face and gets its falloff @@ -86,7 +86,7 @@ /** * This threshold offsets the required falloff value to start a new loop. This is needed because in - * some situations, vertices which have the same falloff value as max_falloff will start a new + * some situations, verts which have the same falloff value as max_falloff will start a new * loop, which is undesired. */ #define SCULPT_EXPAND_LOOP_THRESHOLD 0.00001f @@ -113,7 +113,8 @@ enum { SCULPT_EXPAND_MODAL_FALLOFF_TOPOLOGY, SCULPT_EXPAND_MODAL_FALLOFF_TOPOLOGY_DIAGONALS, SCULPT_EXPAND_MODAL_FALLOFF_SPHERICAL, - SCULPT_EXPAND_MODAL_SNAP_TOGGLE, + SCULPT_EXPAND_MODAL_SNAP_ENABLE, + SCULPT_EXPAND_MODAL_SNAP_DISABLE, SCULPT_EXPAND_MODAL_LOOP_COUNT_INCREASE, SCULPT_EXPAND_MODAL_LOOP_COUNT_DECREASE, SCULPT_EXPAND_MODAL_BRUSH_GRADIENT_TOGGLE, @@ -121,7 +122,7 @@ enum { SCULPT_EXPAND_MODAL_TEXTURE_DISTORTION_DECREASE, }; -/* Functions for getting the state of mesh elements (vertices and base mesh faces). When the main +/* Functions for getting the state of mesh elements (verts and base mesh faces). When the main * functions for getting the state of an element return true it means that data associated to that * element will be modified by expand. */ @@ -146,10 +147,20 @@ static bool sculpt_expand_is_vert_in_active_component(SculptSession *ss, */ static bool sculpt_expand_is_face_in_active_component(SculptSession *ss, ExpandCache *expand_cache, - const int f) + const PBVHFaceRef f) { - const MLoop *loop = &ss->mloop[ss->mpoly[f].loopstart]; - return sculpt_expand_is_vert_in_active_component(ss, expand_cache, BKE_pbvh_make_vref(loop->v)); + if (ss->bm) { + BMFace *bf = (BMFace *)f.i; + BMLoop *l = bf->l_first; + PBVHVertRef v = {(intptr_t)l->v}; + + return sculpt_expand_is_vert_in_active_component(ss, expand_cache, v); + } + else { + const MLoop *loop = &ss->mloop[ss->mpoly[f.i].loopstart]; + return sculpt_expand_is_vert_in_active_component( + ss, expand_cache, BKE_pbvh_index_to_vertex(ss->pbvh, loop->v)); + } } /** @@ -160,7 +171,7 @@ static float sculpt_expand_falloff_value_vertex_get(SculptSession *ss, ExpandCache *expand_cache, const PBVHVertRef v) { - int v_i = BKE_pbvh_vertex_to_index(ss->pbvh, v); + const int v_i = BKE_pbvh_vertex_to_index(ss->pbvh, v); if (expand_cache->texture_distortion_strength == 0.0f) { return expand_cache->vert_falloff[v_i]; @@ -252,9 +263,13 @@ static bool sculpt_expand_state_get(SculptSession *ss, * Main function to get the state of a face for the current state and settings of a #ExpandCache. * Returns true when the target data should be modified by expand. */ -static bool sculpt_expand_face_state_get(SculptSession *ss, ExpandCache *expand_cache, const int f) +static bool sculpt_expand_face_state_get(SculptSession *ss, + ExpandCache *expand_cache, + const PBVHFaceRef f) { - if (ss->hide_poly && ss->hide_poly[f]) { + int f_i = BKE_pbvh_face_to_index(ss->pbvh, f); + + if (ss->hide_poly && ss->hide_poly[f_i]) { return false; } @@ -269,7 +284,7 @@ static bool sculpt_expand_face_state_get(SculptSession *ss, ExpandCache *expand_ bool enabled = false; if (expand_cache->snap_enabled_face_sets) { - const int face_set = expand_cache->original_face_sets[f]; + const int face_set = expand_cache->original_face_sets[f_i]; enabled = BLI_gset_haskey(expand_cache->snap_enabled_face_sets, POINTER_FROM_INT(face_set)); } else { @@ -277,12 +292,12 @@ static bool sculpt_expand_face_state_get(SculptSession *ss, ExpandCache *expand_ SCULPT_EXPAND_LOOP_THRESHOLD; const float active_factor = fmod(expand_cache->active_falloff, loop_len); - const float falloff_factor = fmod(expand_cache->face_falloff[f], loop_len); - enabled = falloff_factor < active_factor; + const float falloff_factor = fmod(expand_cache->face_falloff[f_i], loop_len); + enabled = falloff_factor <= active_factor; } if (expand_cache->falloff_type == SCULPT_EXPAND_FALLOFF_ACTIVE_FACE_SET) { - if (ss->face_sets[f] == expand_cache->initial_active_face_set) { + if (SCULPT_face_set_get(ss, f) == expand_cache->initial_active_face_set) { enabled = false; } } @@ -333,7 +348,7 @@ static float sculpt_expand_gradient_value_get(SculptSession *ss, return BKE_brush_curve_strength(expand_cache->brush, linear_falloff, 1.0f); } -/* Utility functions for getting all vertices state during expand. */ +/* Utility functions for getting all verts state during expand. */ /** * Returns a bitmap indexed by vertex index which contains if the vertex was enabled or not for a @@ -353,7 +368,7 @@ static BLI_bitmap *sculpt_expand_bitmap_from_enabled(SculptSession *ss, ExpandCa /** * Returns a bitmap indexed by vertex index which contains if the vertex is in the boundary of the - * enabled vertices. This is defined as vertices that are enabled and at least have one connected + * enabled verts. This is defined as verts that are enabled and at least have one connected * vertex that is not enabled. */ static BLI_bitmap *sculpt_expand_boundary_from_enabled(SculptSession *ss, @@ -363,12 +378,11 @@ static BLI_bitmap *sculpt_expand_boundary_from_enabled(SculptSession *ss, const int totvert = SCULPT_vertex_count_get(ss); BLI_bitmap *boundary_verts = BLI_BITMAP_NEW(totvert, "boundary verts"); for (int i = 0; i < totvert; i++) { + PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); if (!BLI_BITMAP_TEST(enabled_verts, i)) { continue; } - PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); - bool is_expand_boundary = false; SculptVertexNeighborIter ni; SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vertex, ni) { @@ -378,7 +392,7 @@ static BLI_bitmap *sculpt_expand_boundary_from_enabled(SculptSession *ss, } SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); - if (use_mesh_boundary && SCULPT_vertex_is_boundary(ss, vertex)) { + if (use_mesh_boundary && SCULPT_vertex_is_boundary(ss, vertex, SCULPT_BOUNDARY_MESH)) { is_expand_boundary = true; } @@ -544,8 +558,8 @@ static float *sculpt_expand_normal_falloff_create(Sculpt *sd, for (int repeat = 0; repeat < blur_steps; repeat++) { for (int i = 0; i < totvert; i++) { PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); - float avg = 0.0f; + SculptVertexNeighborIter ni; SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vertex, ni) { avg += dists[ni.index]; @@ -588,12 +602,12 @@ static float *sculpt_expand_spherical_falloff_create(Object *ob, const PBVHVertR } const PBVHVertRef symm_vertex = sculpt_expand_get_vertex_index_for_symmetry_pass( ob, symm_it, v); - if (symm_vertex.i != SCULPT_EXPAND_VERTEX_NONE) { + if (symm_vertex.i != -1) { const float *co = SCULPT_vertex_co_get(ss, symm_vertex); for (int i = 0; i < totvert; i++) { - PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); - - dists[i] = min_ff(dists[i], len_v3v3(co, SCULPT_vertex_co_get(ss, vertex))); + dists[i] = min_ff( + dists[i], + len_v3v3(co, SCULPT_vertex_co_get(ss, BKE_pbvh_index_to_vertex(ss->pbvh, i)))); } } } @@ -606,7 +620,9 @@ static float *sculpt_expand_spherical_falloff_create(Object *ob, const PBVHVertR * boundary to a falloff value of 0. Then, it propagates that falloff to the rest of the mesh so it * stays parallel to the boundary, increasing the falloff value by 1 on each step. */ -static float *sculpt_expand_boundary_topology_falloff_create(Object *ob, const PBVHVertRef v) +static float *sculpt_expand_boundary_topology_falloff_create(Sculpt *sd, + Object *ob, + const PBVHVertRef v) { SculptSession *ss = ob->sculpt; const int totvert = SCULPT_vertex_count_get(ss); @@ -614,7 +630,7 @@ static float *sculpt_expand_boundary_topology_falloff_create(Object *ob, const P BLI_bitmap *visited_verts = BLI_BITMAP_NEW(totvert, "visited verts"); GSQueue *queue = BLI_gsqueue_new(sizeof(PBVHVertRef)); - /* Search and initialize a boundary per symmetry pass, then mark those vertices as visited. */ + /* Search and initialize a boundary per symmetry pass, then mark those verts as visited. */ const char symm = SCULPT_mesh_symmetry_xyz_get(ob); for (char symm_it = 0; symm_it <= symm; symm_it++) { if (!SCULPT_is_symmetry_iteration_valid(symm_it, symm)) { @@ -624,7 +640,7 @@ static float *sculpt_expand_boundary_topology_falloff_create(Object *ob, const P const PBVHVertRef symm_vertex = sculpt_expand_get_vertex_index_for_symmetry_pass( ob, symm_it, v); - SculptBoundary *boundary = SCULPT_boundary_data_init(ob, nullptr, symm_vertex, FLT_MAX); + SculptBoundary *boundary = SCULPT_boundary_data_init(sd, ob, nullptr, symm_vertex, FLT_MAX); if (!boundary) { continue; } @@ -646,13 +662,15 @@ static float *sculpt_expand_boundary_topology_falloff_create(Object *ob, const P PBVHVertRef v_next; BLI_gsqueue_pop(queue, &v_next); - int v_next_i = BKE_pbvh_vertex_to_index(ss->pbvh, v_next); SculptVertexNeighborIter ni; SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, v_next, ni) { if (BLI_BITMAP_TEST(visited_verts, ni.index)) { continue; } + + const int v_next_i = BKE_pbvh_vertex_to_index(ss->pbvh, v_next); + dists[ni.index] = dists[v_next_i] + 1.0f; BLI_BITMAP_ENABLE(visited_verts, ni.index); BLI_gsqueue_push(queue, &ni.vertex); @@ -677,13 +695,18 @@ static float *sculpt_expand_diagonals_falloff_create(Object *ob, const PBVHVertR float *dists = static_cast(MEM_calloc_arrayN(totvert, sizeof(float), __func__)); /* This algorithm uses mesh data (polys and loops), so this falloff type can't be initialized for - * Multires. It also does not make sense to implement it for dyntopo as the result will be the - * same as Topology falloff. */ - if (BKE_pbvh_type(ss->pbvh) != PBVH_FACES) { + * Multires. Also supports non-tri PBVH_BMESH, though untested until we implement that properly*/ + if (BKE_pbvh_type(ss->pbvh) != PBVH_FACES || + (ss->bm && ss->bm->totloop != ss->bm->totvert * 3)) { return dists; } - /* Search and mask as visited the initial vertices using the enabled symmetry passes. */ + if (ss->bm) { + BM_mesh_elem_index_ensure(ss->bm, BM_VERT); + BM_mesh_elem_table_ensure(ss->bm, BM_VERT); + } + + /* Search and mask as visited the initial verts using the enabled symmetry passes. */ BLI_bitmap *visited_verts = BLI_BITMAP_NEW(totvert, "visited verts"); GSQueue *queue = BLI_gsqueue_new(sizeof(PBVHVertRef)); const char symm = SCULPT_mesh_symmetry_xyz_get(ob); @@ -711,16 +734,45 @@ static float *sculpt_expand_diagonals_falloff_create(Object *ob, const PBVHVertR int v_next_i = BKE_pbvh_vertex_to_index(ss->pbvh, v_next); - for (int j = 0; j < ss->pmap[v_next_i].count; j++) { - const MPoly *p = &ss->mpoly[ss->pmap[v_next_i].indices[j]]; - for (int l = 0; l < p->totloop; l++) { - const PBVHVertRef neighbor_v = BKE_pbvh_make_vref(ss->mloop[p->loopstart + l].v); - if (BLI_BITMAP_TEST(visited_verts, neighbor_v.i)) { - continue; + if (ss->bm) { + BMIter iter; + BMFace *f; + BMVert *v = (BMVert *)v_next.i; + + BM_ITER_ELEM (f, &iter, v, BM_FACES_OF_VERT) { + BMLoop *l = f->l_first; + + do { + BMVert *neighbor_v = l->next->v; + const int neighbor_v_i = BM_elem_index_get(neighbor_v); + + if (BLI_BITMAP_TEST(visited_verts, neighbor_v_i)) { + l = l->next; + continue; + } + + dists[neighbor_v_i] = dists[v_next_i] + 1.0f; + BLI_BITMAP_ENABLE(visited_verts, neighbor_v_i); + BLI_gsqueue_push(queue, &neighbor_v); + + l = l->next; + } while (l != f->l_first); + } + } + else { + for (int j = 0; j < ss->pmap->pmap[v_next_i].count; j++) { + const MPoly *p = &ss->mpoly[ss->pmap->pmap[v_next_i].indices[j]]; + for (int l = 0; l < p->totloop; l++) { + const int neighbor_v = ss->mloop[p->loopstart + l].v; + + if (BLI_BITMAP_TEST(visited_verts, neighbor_v)) { + continue; + } + + dists[neighbor_v] = dists[v_next_i] + 1.0f; + BLI_BITMAP_ENABLE(visited_verts, neighbor_v); + BLI_gsqueue_push(queue, &neighbor_v); } - dists[neighbor_v.i] = dists[v_next_i] + 1.0f; - BLI_BITMAP_ENABLE(visited_verts, neighbor_v.i); - BLI_gsqueue_push(queue, &neighbor_v); } } } @@ -734,7 +786,7 @@ static float *sculpt_expand_diagonals_falloff_create(Object *ob, const PBVHVertR * initializing a new falloff to make sure that this value is always updated. */ /** - * Updates the max_falloff value for vertices in a #ExpandCache based on the current values of the + * Updates the max_falloff value for verts in a #ExpandCache based on the current values of the * falloff, skipping any invalid values initialized to FLT_MAX and not initialized components. */ static void sculpt_expand_update_max_vert_falloff_value(SculptSession *ss, @@ -742,13 +794,14 @@ static void sculpt_expand_update_max_vert_falloff_value(SculptSession *ss, { const int totvert = SCULPT_vertex_count_get(ss); expand_cache->max_vert_falloff = -FLT_MAX; - for (int i = 0; i < totvert; i++) { - PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); + for (int i = 0; i < totvert; i++) { if (expand_cache->vert_falloff[i] == FLT_MAX) { continue; } + PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); + if (!sculpt_expand_is_vert_in_active_component(ss, expand_cache, vertex)) { continue; } @@ -768,11 +821,13 @@ static void sculpt_expand_update_max_face_falloff_factor(SculptSession *ss, const int totface = ss->totfaces; expand_cache->max_face_falloff = -FLT_MAX; for (int i = 0; i < totface; i++) { + PBVHFaceRef f = BKE_pbvh_index_to_face(ss->pbvh, i); + if (expand_cache->face_falloff[i] == FLT_MAX) { continue; } - if (!sculpt_expand_is_face_in_active_component(ss, expand_cache, i)) { + if (!sculpt_expand_is_face_in_active_component(ss, expand_cache, f)) { continue; } @@ -782,9 +837,9 @@ static void sculpt_expand_update_max_face_falloff_factor(SculptSession *ss, } /** - * Functions to get falloff values for faces from the values from the vertices. This is used for + * Functions to get falloff values for faces from the values from the verts. This is used for * expanding Face Sets. Depending on the data type of the #SculptSession, this needs to get the per - * face falloff value from the connected vertices of each face or from the grids stored per loops + * face falloff value from the connected verts of each face or from the grids stored per loops * for each face. */ static void sculpt_expand_grids_to_faces_falloff(SculptSession *ss, @@ -823,6 +878,23 @@ static void sculpt_expand_vertex_to_faces_falloff(Mesh *mesh, ExpandCache *expan } } +static void sculpt_expand_vertex_to_faces_falloff_bmesh(BMesh *bm, ExpandCache *expand_cache) +{ + BMIter iter; + BMFace *f; + BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { + BMLoop *l = f->l_first; + + float accum = 0.0f; + + do { + accum += expand_cache->vert_falloff[BM_elem_index_get(l->v)]; + l = l->next; + } while (l != f->l_first); + + expand_cache->face_falloff[BM_elem_index_get(f)] = accum / f->len; + } +} /** * Main function to update the faces falloff from a already calculated vertex falloff. */ @@ -837,30 +909,32 @@ static void sculpt_expand_mesh_face_falloff_from_vertex_falloff(SculptSession *s MEM_malloc_arrayN(mesh->totpoly, sizeof(float), __func__)); } - if (BKE_pbvh_type(ss->pbvh) == PBVH_FACES) { - sculpt_expand_vertex_to_faces_falloff(mesh, expand_cache); - } - else if (BKE_pbvh_type(ss->pbvh) == PBVH_GRIDS) { - sculpt_expand_grids_to_faces_falloff(ss, mesh, expand_cache); - } - else { - BLI_assert(false); + switch (BKE_pbvh_type(ss->pbvh)) { + case PBVH_FACES: + sculpt_expand_vertex_to_faces_falloff(mesh, expand_cache); + break; + case PBVH_GRIDS: + sculpt_expand_grids_to_faces_falloff(ss, mesh, expand_cache); + break; + case PBVH_BMESH: + sculpt_expand_vertex_to_faces_falloff_bmesh(ss->bm, expand_cache); + break; } } -/* Recursions. These functions will generate new falloff values based on the state of the vertices +/* Recursions. These functions will generate new falloff values based on the state of the verts * from the current ExpandCache options and falloff values. */ /** * Geodesic recursion: Initializes falloff values using geodesic distances from the boundary of the - * current vertices state. + * current verts state. */ static void sculpt_expand_geodesics_from_state_boundary(Object *ob, ExpandCache *expand_cache, BLI_bitmap *enabled_verts) { SculptSession *ss = ob->sculpt; - BLI_assert(BKE_pbvh_type(ss->pbvh) == PBVH_FACES); + BLI_assert(ELEM(BKE_pbvh_type(ss->pbvh), PBVH_GRIDS, PBVH_FACES, PBVH_BMESH)); GSet *initial_verts = BLI_gset_int_new("initial_verts"); BLI_bitmap *boundary_verts = sculpt_expand_boundary_from_enabled(ss, enabled_verts, false); @@ -876,13 +950,14 @@ static void sculpt_expand_geodesics_from_state_boundary(Object *ob, MEM_SAFE_FREE(expand_cache->vert_falloff); MEM_SAFE_FREE(expand_cache->face_falloff); - expand_cache->vert_falloff = SCULPT_geodesic_distances_create(ob, initial_verts, FLT_MAX); + expand_cache->vert_falloff = SCULPT_geodesic_distances_create( + ob, initial_verts, FLT_MAX, nullptr, nullptr); BLI_gset_free(initial_verts, nullptr); } /** * Topology recursion: Initializes falloff values using topology steps from the boundary of the - * current vertices state, increasing the value by 1 each time a new vertex is visited. + * current verts state, increasing the value by 1 each time a new vertex is visited. */ static void sculpt_expand_topology_from_state_boundary(Object *ob, ExpandCache *expand_cache, @@ -904,8 +979,7 @@ static void sculpt_expand_topology_from_state_boundary(Object *ob, continue; } - PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); - SCULPT_floodfill_add_and_skip_initial(&flood, vertex); + SCULPT_floodfill_add_and_skip_initial(ss, &flood, BKE_pbvh_index_to_vertex(ss->pbvh, i)); } MEM_freeN(boundary_verts); @@ -971,17 +1045,18 @@ static void sculpt_expand_initialize_from_face_set_boundary(Object *ob, BLI_bitmap *enabled_verts = BLI_BITMAP_NEW(totvert, "enabled verts"); for (int i = 0; i < totvert; i++) { - PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); + PBVHVertRef vref = BKE_pbvh_index_to_vertex(ss->pbvh, i); - if (!SCULPT_vertex_has_unique_face_set(ss, vertex)) { + if (!SCULPT_vertex_has_unique_face_set(ss, vref)) { continue; } - if (!SCULPT_vertex_has_face_set(ss, vertex, active_face_set)) { + if (!SCULPT_vertex_has_face_set(ss, vref, active_face_set)) { continue; } BLI_BITMAP_ENABLE(enabled_verts, i); } + /* TODO: Default to topology. */ if (BKE_pbvh_type(ss->pbvh) == PBVH_FACES) { sculpt_expand_geodesics_from_state_boundary(ob, expand_cache, enabled_verts); } @@ -993,10 +1068,10 @@ static void sculpt_expand_initialize_from_face_set_boundary(Object *ob, if (internal_falloff) { for (int i = 0; i < totvert; i++) { - PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); + PBVHVertRef vref = BKE_pbvh_index_to_vertex(ss->pbvh, i); - if (!(SCULPT_vertex_has_face_set(ss, vertex, active_face_set) && - SCULPT_vertex_has_unique_face_set(ss, vertex))) { + if (!(SCULPT_vertex_has_face_set(ss, vref, active_face_set) && + SCULPT_vertex_has_unique_face_set(ss, vref))) { continue; } expand_cache->vert_falloff[i] *= -1.0f; @@ -1014,9 +1089,9 @@ static void sculpt_expand_initialize_from_face_set_boundary(Object *ob, } else { for (int i = 0; i < totvert; i++) { - PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); + PBVHVertRef vref = BKE_pbvh_index_to_vertex(ss->pbvh, i); - if (!SCULPT_vertex_has_face_set(ss, vertex, active_face_set)) { + if (!SCULPT_vertex_has_face_set(ss, vref, active_face_set)) { continue; } expand_cache->vert_falloff[i] = 0.0f; @@ -1039,13 +1114,16 @@ static void sculpt_expand_falloff_factors_from_vertex_and_symm_create( expand_cache->falloff_type = falloff_type; SculptSession *ss = ob->sculpt; - const bool has_topology_info = BKE_pbvh_type(ss->pbvh) == PBVH_FACES; + const bool has_topology_info = ELEM(BKE_pbvh_type(ss->pbvh), PBVH_FACES, PBVH_BMESH); switch (falloff_type) { case SCULPT_EXPAND_FALLOFF_GEODESIC: + expand_cache->vert_falloff = sculpt_expand_geodesic_falloff_create(sd, ob, v); + /* expand_cache->vert_falloff = has_topology_info ? sculpt_expand_geodesic_falloff_create(sd, ob, v) : sculpt_expand_spherical_falloff_create(ob, v); + */ break; case SCULPT_EXPAND_FALLOFF_TOPOLOGY: expand_cache->vert_falloff = sculpt_expand_topology_falloff_create(sd, ob, v); @@ -1067,7 +1145,7 @@ static void sculpt_expand_falloff_factors_from_vertex_and_symm_create( expand_cache->vert_falloff = sculpt_expand_spherical_falloff_create(ob, v); break; case SCULPT_EXPAND_FALLOFF_BOUNDARY_TOPOLOGY: - expand_cache->vert_falloff = sculpt_expand_boundary_topology_falloff_create(ob, v); + expand_cache->vert_falloff = sculpt_expand_boundary_topology_falloff_create(sd, ob, v); break; case SCULPT_EXPAND_FALLOFF_BOUNDARY_FACE_SET: sculpt_expand_initialize_from_face_set_boundary( @@ -1089,7 +1167,7 @@ static void sculpt_expand_falloff_factors_from_vertex_and_symm_create( } /** - * Adds to the snapping Face Set `gset` all Face Sets which contain all enabled vertices for the + * Adds to the snapping Face Set `gset` all Face Sets which contain all enabled verts for the * current #ExpandCache state. This improves the usability of snapping, as already enabled elements * won't switch their state when toggling snapping with the modal key-map. */ @@ -1176,7 +1254,9 @@ static void sculpt_expand_restore_face_set_data(SculptSession *ss, ExpandCache * } MEM_freeN(nodes); for (int i = 0; i < ss->totfaces; i++) { - ss->face_sets[i] = expand_cache->original_face_sets[i]; + PBVHFaceRef f = BKE_pbvh_index_to_face(ss->pbvh, i); + + SCULPT_face_set_set(ss, f, expand_cache->original_face_sets[i]); } } @@ -1320,16 +1400,20 @@ static void sculpt_expand_mask_update_task_cb(void *__restrict userdata, static void sculpt_expand_face_sets_update(SculptSession *ss, ExpandCache *expand_cache) { const int totface = ss->totfaces; - for (int f = 0; f < totface; f++) { + + for (int f_i = 0; f_i < totface; f_i++) { + PBVHFaceRef f = BKE_pbvh_index_to_face(ss->pbvh, f_i); + int fset = SCULPT_face_set_get(ss, f); + const bool enabled = sculpt_expand_face_state_get(ss, expand_cache, f); if (!enabled) { continue; } if (expand_cache->preserve) { - ss->face_sets[f] += expand_cache->next_face_set; + SCULPT_face_set_set(ss, f, fset + expand_cache->next_face_set); } else { - ss->face_sets[f] = expand_cache->next_face_set; + SCULPT_face_set_set(ss, f, expand_cache->next_face_set); } } @@ -1418,6 +1502,8 @@ static void sculpt_expand_original_state_store(Object *ob, ExpandCache *expand_c const int totvert = SCULPT_vertex_count_get(ss); const int totface = ss->totfaces; + SCULPT_vertex_random_access_ensure(ss); + /* Face Sets are always stored as they are needed for snapping. */ expand_cache->initial_face_sets = static_cast( MEM_malloc_arrayN(totface, sizeof(int), "initial face set")); @@ -1425,8 +1511,11 @@ static void sculpt_expand_original_state_store(Object *ob, ExpandCache *expand_c MEM_malloc_arrayN(totface, sizeof(int), "original face set")); if (ss->face_sets) { for (int i = 0; i < totface; i++) { - expand_cache->initial_face_sets[i] = ss->face_sets[i]; - expand_cache->original_face_sets[i] = ss->face_sets[i]; + const PBVHFaceRef fref = BKE_pbvh_index_to_face(ss->pbvh, i); + const int fset = SCULPT_face_set_get(ss, fref); + + expand_cache->initial_face_sets[i] = fset; + expand_cache->original_face_sets[i] = fset; } } else { @@ -1438,9 +1527,8 @@ static void sculpt_expand_original_state_store(Object *ob, ExpandCache *expand_c expand_cache->original_mask = static_cast( MEM_malloc_arrayN(totvert, sizeof(float), "initial mask")); for (int i = 0; i < totvert; i++) { - PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); - - expand_cache->original_mask[i] = SCULPT_vertex_mask_get(ss, vertex); + expand_cache->original_mask[i] = SCULPT_vertex_mask_get( + ss, BKE_pbvh_index_to_vertex(ss->pbvh, i)); } } @@ -1461,30 +1549,34 @@ static void sculpt_expand_original_state_store(Object *ob, ExpandCache *expand_c static void sculpt_expand_face_sets_restore(SculptSession *ss, ExpandCache *expand_cache) { const int totfaces = ss->totfaces; + for (int i = 0; i < totfaces; i++) { + PBVHFaceRef fref = BKE_pbvh_index_to_face(ss->pbvh, i); + if (expand_cache->original_face_sets[i] <= 0) { /* Do not modify hidden Face Sets, even when restoring the IDs state. */ continue; } - if (!sculpt_expand_is_face_in_active_component(ss, expand_cache, i)) { + if (!sculpt_expand_is_face_in_active_component(ss, expand_cache, fref)) { continue; } - ss->face_sets[i] = expand_cache->initial_face_sets[i]; + + SCULPT_face_set_set(ss, fref, expand_cache->initial_face_sets[i]); } } static void sculpt_expand_update_for_vertex(bContext *C, Object *ob, const PBVHVertRef vertex) { SculptSession *ss = ob->sculpt; + const int vertex_i = BKE_pbvh_vertex_to_index(ss->pbvh, vertex); + Sculpt *sd = CTX_data_tool_settings(C)->sculpt; ExpandCache *expand_cache = ss->expand_cache; - int vertex_i = BKE_pbvh_vertex_to_index(ss->pbvh, vertex); - /* Update the active factor in the cache. */ if (vertex.i == SCULPT_EXPAND_VERTEX_NONE) { /* This means that the cursor is not over the mesh, so a valid active falloff can't be - * determined. In this situations, don't evaluate enabled states and default all vertices in + * determined. In this situations, don't evaluate enabled states and default all verts in * connected components to enabled. */ expand_cache->active_falloff = expand_cache->max_vert_falloff; expand_cache->all_enabled = true; @@ -1536,14 +1628,16 @@ static PBVHVertRef sculpt_expand_target_vertex_update_and_get(bContext *C, { SculptSession *ss = ob->sculpt; SculptCursorGeometryInfo sgi; - if (SCULPT_cursor_geometry_info_update(C, &sgi, mval, false)) { + if (SCULPT_cursor_geometry_info_update(C, &sgi, mval, false, false)) { return SCULPT_active_vertex_get(ss); } - return BKE_pbvh_make_vref(SCULPT_EXPAND_VERTEX_NONE); + + PBVHVertRef ret = {SCULPT_EXPAND_VERTEX_NONE}; + return ret; } /** - * Moves the sculpt pivot to the average point of the boundary enabled vertices of the current + * Moves the sculpt pivot to the average point of the boundary enabled verts of the current * expand state. Take symmetry and active components into account. */ static void sculpt_expand_reposition_pivot(bContext *C, Object *ob, ExpandCache *expand_cache) @@ -1556,11 +1650,11 @@ static void sculpt_expand_reposition_pivot(bContext *C, Object *ob, ExpandCache expand_cache->invert = false; BLI_bitmap *enabled_verts = sculpt_expand_bitmap_from_enabled(ss, expand_cache); - /* For boundary topology, position the pivot using only the boundary of the enabled vertices, + /* For boundary topology, position the pivot using only the boundary of the enabled verts, * without taking mesh boundary into account. This allows to create deformations like bending the * mesh from the boundary of the mask that was just created. */ - const float use_mesh_boundary = expand_cache->falloff_type != - SCULPT_EXPAND_FALLOFF_BOUNDARY_TOPOLOGY; + const bool use_mesh_boundary = expand_cache->falloff_type != + SCULPT_EXPAND_FALLOFF_BOUNDARY_TOPOLOGY; BLI_bitmap *boundary_verts = sculpt_expand_boundary_from_enabled( ss, enabled_verts, use_mesh_boundary); @@ -1575,12 +1669,12 @@ static void sculpt_expand_reposition_pivot(bContext *C, Object *ob, ExpandCache const float *expand_init_co = SCULPT_vertex_co_get(ss, expand_cache->initial_active_vertex); for (int i = 0; i < totvert; i++) { - PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); - if (!BLI_BITMAP_TEST(boundary_verts, i)) { continue; } + PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); + if (!sculpt_expand_is_vert_in_active_component(ss, expand_cache, vertex)) { continue; } @@ -1685,7 +1779,6 @@ static void sculpt_expand_set_initial_components_for_mouse(bContext *C, copy_v2_v2(ss->expand_cache->initial_mouse, mval); expand_cache->initial_active_vertex = initial_vertex; - expand_cache->initial_active_vertex_i = initial_vertex_i; expand_cache->initial_active_face_set = SCULPT_active_face_set_get(ss); if (expand_cache->next_face_set == SCULPT_FACE_SET_NONE) { @@ -1753,17 +1846,14 @@ static void sculpt_expand_ensure_sculptsession_data(Object *ob) static int sculpt_expand_active_face_set_id_get(SculptSession *ss, ExpandCache *expand_cache) { switch (BKE_pbvh_type(ss->pbvh)) { + case PBVH_BMESH: case PBVH_FACES: - return expand_cache->original_face_sets[ss->active_face_index]; + return expand_cache->original_face_sets[BKE_pbvh_vertex_to_index(ss->pbvh, ss->active_face)]; case PBVH_GRIDS: { const int face_index = BKE_subdiv_ccg_grid_to_face_index(ss->subdiv_ccg, ss->active_grid_index); return expand_cache->original_face_sets[face_index]; } - case PBVH_BMESH: { - /* Dyntopo does not support Face Set functionality. */ - BLI_assert(false); - } } return SCULPT_FACE_SET_NONE; } @@ -1816,20 +1906,18 @@ static int sculpt_expand_modal(bContext *C, wmOperator *op, const wmEvent *event } break; } - case SCULPT_EXPAND_MODAL_SNAP_TOGGLE: { - if (expand_cache->snap) { - expand_cache->snap = false; - if (expand_cache->snap_enabled_face_sets) { - BLI_gset_free(expand_cache->snap_enabled_face_sets, nullptr); - expand_cache->snap_enabled_face_sets = nullptr; - } + case SCULPT_EXPAND_MODAL_SNAP_ENABLE: { + expand_cache->snap = true; + if (!expand_cache->snap_enabled_face_sets) { + expand_cache->snap_enabled_face_sets = BLI_gset_int_new("snap face sets"); } - else { - expand_cache->snap = true; - if (!expand_cache->snap_enabled_face_sets) { - expand_cache->snap_enabled_face_sets = BLI_gset_int_new("snap face sets"); - } - sculpt_expand_snap_initialize_from_enabled(ss, expand_cache); + sculpt_expand_snap_initialize_from_enabled(ss, expand_cache); + } break; + case SCULPT_EXPAND_MODAL_SNAP_DISABLE: { + expand_cache->snap = false; + if (expand_cache->snap_enabled_face_sets) { + BLI_gset_free(expand_cache->snap_enabled_face_sets, NULL); + expand_cache->snap_enabled_face_sets = NULL; } } break; case SCULPT_EXPAND_MODAL_MOVE_TOGGLE: { @@ -1973,6 +2061,122 @@ static int sculpt_expand_modal(bContext *C, wmOperator *op, const wmEvent *event return OPERATOR_RUNNING_MODAL; } +/** + * Deletes the `delete_id` Face Set ID from the mesh Face Sets + * and stores the result in `r_face_set`. + * The faces that were using the `delete_id` Face Set are filled + * using the content from their neighbors. + */ +static void sculpt_expand_delete_face_set_id_bmesh(int *r_face_sets, + SculptSession *ss, + ExpandCache *expand_cache, + const int delete_id) +{ + BMIter iter; + BMFace *f; + int i = 0; + const int totface = ss->totpoly; + + /* Check that all the face sets IDs in the mesh are not equal to `delete_id` + * before attempting to delete it. */ + bool all_same_id = true; + + BM_ITER_MESH (f, &iter, ss->bm, BM_FACES_OF_MESH) { + PBVHFaceRef fref = {(intptr_t)f}; + i++; + + if (!sculpt_expand_is_face_in_active_component(ss, expand_cache, fref)) { + continue; + } + + if (r_face_sets[i] != delete_id) { + all_same_id = false; + break; + } + } + + if (all_same_id) { + return; + } + + BLI_LINKSTACK_DECLARE(queue, BMFace *); + BLI_LINKSTACK_DECLARE(queue_next, BMFace *); + + BLI_LINKSTACK_INIT(queue); + BLI_LINKSTACK_INIT(queue_next); + + for (int i = 0; i < totface; i++) { + PBVHFaceRef fref = BKE_pbvh_index_to_face(ss->pbvh, i); + + if (r_face_sets[i] == delete_id) { + BLI_LINKSTACK_PUSH(queue, (BMFace *)(fref.i)); + } + } + + while (BLI_LINKSTACK_SIZE(queue)) { + bool any_updated = false; + + while (BLI_LINKSTACK_SIZE(queue)) { + const PBVHFaceRef f = {(intptr_t)(BLI_LINKSTACK_POP(queue))}; + BMFace *bf = (BMFace *)f.i; + const int f_index = BM_elem_index_get(bf); + + int other_id = delete_id; + BMLoop *l = bf->l_first; + do { + BMLoop *l2 = l->radial_next; + do { + const int neighbor_face_index = BM_elem_index_get(l2->f); + + if (expand_cache->original_face_sets[neighbor_face_index] <= 0) { + /* Skip picking IDs from hidden Face Sets. */ + continue; + } + + if (r_face_sets[neighbor_face_index] != delete_id) { + other_id = r_face_sets[neighbor_face_index]; + } + + l2 = l2->radial_next; + } while (l2 != l); + + l = l->next; + } while (l != bf->l_first); + + if (other_id != delete_id) { + any_updated = true; + r_face_sets[f_index] = other_id; + } + else { + BLI_LINKSTACK_PUSH(queue_next, bf); + } + } + + if (!any_updated) { + /* No Face Sets where updated in this iteration, which means that no more content to keep + * filling the polys of the deleted Face Set was found. Break to avoid entering an infinite + * loop trying to search for those polys again. */ + break; + } + + BLI_LINKSTACK_SWAP(queue, queue_next); + } + + BLI_LINKSTACK_FREE(queue); + BLI_LINKSTACK_FREE(queue_next); + + /* Ensure that the visibility state of the modified Face Sets is the same as the original ones. + */ + for (int i = 0; i < totface; i++) { + if (expand_cache->original_face_sets[i] >= 0) { + r_face_sets[i] = abs(r_face_sets[i]); + } + else { + r_face_sets[i] = -abs(r_face_sets[i]); + } + } +} + /** * Deletes the `delete_id` Face Set ID from the mesh Face Sets * and stores the result in `r_face_set`. @@ -1985,8 +2189,13 @@ static void sculpt_expand_delete_face_set_id(int *r_face_sets, Mesh *mesh, const int delete_id) { + if (ss->bm) { + sculpt_expand_delete_face_set_id_bmesh(r_face_sets, ss, expand_cache, delete_id); + return; + } + const int totface = ss->totfaces; - MeshElemMap *pmap = ss->pmap; + MeshElemMap *pmap = ss->pmap->pmap; const MPoly *polys = BKE_mesh_polys(mesh); const MLoop *loops = BKE_mesh_loops(mesh); @@ -1994,7 +2203,8 @@ static void sculpt_expand_delete_face_set_id(int *r_face_sets, * before attempting to delete it. */ bool all_same_id = true; for (int i = 0; i < totface; i++) { - if (!sculpt_expand_is_face_in_active_component(ss, expand_cache, i)) { + if (!sculpt_expand_is_face_in_active_component( + ss, expand_cache, BKE_pbvh_index_to_face(ss->pbvh, i))) { continue; } if (r_face_sets[i] != delete_id) { @@ -2080,6 +2290,7 @@ static void sculpt_expand_cache_initial_config_set(bContext *C, /* RNA properties. */ expand_cache->normal_falloff_blur_steps = RNA_int_get(op->ptr, "normal_falloff_smooth"); expand_cache->invert = RNA_boolean_get(op->ptr, "invert"); + expand_cache->preserve_flip_inverse = RNA_boolean_get(op->ptr, "use_preserve_flip_inverse"); expand_cache->preserve = RNA_boolean_get(op->ptr, "use_mask_preserve"); expand_cache->auto_mask = RNA_boolean_get(op->ptr, "use_auto_mask"); expand_cache->falloff_gradient = RNA_boolean_get(op->ptr, "use_falloff_gradient"); @@ -2145,6 +2356,8 @@ static int sculpt_expand_invoke(bContext *C, wmOperator *op, const wmEvent *even SculptSession *ss = ob->sculpt; Sculpt *sd = CTX_data_tool_settings(C)->sculpt; + SCULPT_vertex_random_access_ensure(ss); + SCULPT_face_random_access_ensure(ss); SCULPT_stroke_id_next(ob); /* Create and configure the Expand Cache. */ @@ -2201,7 +2414,7 @@ static int sculpt_expand_invoke(bContext *C, wmOperator *op, const wmEvent *even BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true, needs_colors); - /* Do nothing when the mesh has 0 vertices. */ + /* Do nothing when the mesh has 0 verts. */ const int totvert = SCULPT_vertex_count_get(ss); if (totvert == 0) { sculpt_expand_cache_free(ss); @@ -2210,14 +2423,13 @@ static int sculpt_expand_invoke(bContext *C, wmOperator *op, const wmEvent *even Mesh *mesh = static_cast(ob->data); if (ss->expand_cache->target == SCULPT_EXPAND_TARGET_FACE_SETS) { - ss->face_sets = BKE_sculpt_face_sets_ensure(mesh); + Mesh *mesh = (Mesh *)ob->data; + ss->face_sets = BKE_sculpt_face_sets_ensure(ob); } - /* Face Set operations are not supported in dyntopo. */ - if (ss->expand_cache->target == SCULPT_EXPAND_TARGET_FACE_SETS && - BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) { - sculpt_expand_cache_free(ss); - return OPERATOR_CANCELLED; + if (ss->expand_cache->target == SCULPT_EXPAND_TARGET_MASK) { + MultiresModifierData *mmd = BKE_sculpt_multires_active(ss->scene, ob); + BKE_sculpt_mask_layers_ensure(depsgraph, CTX_data_main(C), ob, mmd); } sculpt_expand_ensure_sculptsession_data(ob); @@ -2250,7 +2462,8 @@ static int sculpt_expand_invoke(bContext *C, wmOperator *op, const wmEvent *even RNA_enum_get(op->ptr, "falloff_type")); /* When starting from a boundary vertex, set the initial falloff to boundary. */ - if (SCULPT_vertex_is_boundary(ss, ss->expand_cache->initial_active_vertex)) { + if (SCULPT_vertex_is_boundary( + ss, ss->expand_cache->initial_active_vertex, SCULPT_BOUNDARY_MESH)) { falloff_type = SCULPT_EXPAND_FALLOFF_BOUNDARY_TOPOLOGY; } @@ -2293,7 +2506,12 @@ void sculpt_expand_modal_keymap(wmKeyConfig *keyconf) "Diagonals Falloff", ""}, {SCULPT_EXPAND_MODAL_FALLOFF_SPHERICAL, "FALLOFF_SPHERICAL", 0, "Spherical Falloff", ""}, - {SCULPT_EXPAND_MODAL_SNAP_TOGGLE, "SNAP_TOGGLE", 0, "Snap expand to Face Sets", ""}, + {SCULPT_EXPAND_MODAL_SNAP_ENABLE, "SNAP_ENABLE", 0, "Snap expand to Face Sets", ""}, + {SCULPT_EXPAND_MODAL_SNAP_DISABLE, + "SNAP_DISABLE", + 0, + "Disable Snap expand to Face Sets", + ""}, {SCULPT_EXPAND_MODAL_LOOP_COUNT_INCREASE, "LOOP_COUNT_INCREASE", 0, @@ -2386,6 +2604,12 @@ void SCULPT_OT_expand(wmOperatorType *ot) "Falloff Type", "Initial falloff of the expand operation"); + ot->prop = RNA_def_boolean(ot->srna, + "use_preserve_flip_inverse", + false, + "Preserve Inverted", + "Flip preserve mode in inverse mode"); + ot->prop = RNA_def_boolean( ot->srna, "invert", false, "Invert", "Invert the expand active elements"); ot->prop = RNA_def_boolean(ot->srna, @@ -2418,8 +2642,8 @@ void SCULPT_OT_expand(wmOperatorType *ot) 0, INT_MAX, "Max Vertex Count for Geodesic Move Preview", - "Maximum number of vertices in the mesh for using geodesic falloff when " - "moving the origin of expand. If the total number of vertices is greater " + "Maximum number of verts in the mesh for using geodesic falloff when " + "moving the origin of expand. If the total number of verts is greater " "than this value, the falloff will be set to spherical when moving", 0, 1000000); diff --git a/source/blender/editors/sculpt_paint/sculpt_face_set.cc b/source/blender/editors/sculpt_paint/sculpt_face_set.cc index 51ecaa168f3..43f2c528ba5 100644 --- a/source/blender/editors/sculpt_paint/sculpt_face_set.cc +++ b/source/blender/editors/sculpt_paint/sculpt_face_set.cc @@ -5,20 +5,16 @@ * \ingroup edsculpt */ -#include -#include -#include - #include "MEM_guardedalloc.h" -#include "BLI_bit_vector.hh" -#include "BLI_function_ref.hh" +#include "BLI_blenlib.h" +#include "BLI_compiler_attrs.h" #include "BLI_hash.h" #include "BLI_math.h" -#include "BLI_math_vector.hh" -#include "BLI_span.hh" +#include "BLI_smallhash.h" #include "BLI_task.h" #include "BLI_task.hh" +#include "BLI_vector.hh" #include "DNA_brush_types.h" #include "DNA_customdata_types.h" @@ -27,7 +23,9 @@ #include "DNA_object_types.h" #include "DNA_scene_types.h" +#include "BKE_attribute.h" #include "BKE_attribute.hh" +#include "BKE_brush.h" #include "BKE_ccg.h" #include "BKE_colortools.h" #include "BKE_context.h" @@ -35,9 +33,14 @@ #include "BKE_mesh.h" #include "BKE_mesh_fair.h" #include "BKE_mesh_mapping.h" +#include "BKE_mesh_types.h" +#include "BKE_multires.h" +#include "BKE_node.h" #include "BKE_object.h" #include "BKE_paint.h" #include "BKE_pbvh.h" +#include "BKE_scene.h" +#include "BKE_subdiv_ccg.h" #include "DEG_depsgraph.h" @@ -52,6 +55,92 @@ #include "RNA_define.h" #include "bmesh.h" +#include "bmesh_log.h" + +#include +#include + +using blender::Vector; + +static int sculpt_face_material_get(SculptSession *ss, PBVHFaceRef face) +{ + switch (BKE_pbvh_type(ss->pbvh)) { + case PBVH_BMESH: { + BMFace *f = (BMFace *)face.i; + return f->mat_nr; + } + case PBVH_GRIDS: + case PBVH_FACES: + return ss->material_index[face.i]; + } + + return -1; +} + +int SCULPT_face_set_get(SculptSession *ss, PBVHFaceRef face) +{ + switch (BKE_pbvh_type(ss->pbvh)) { + case PBVH_BMESH: { + BMFace *f = (BMFace *)face.i; + return BM_ELEM_CD_GET_INT(f, ss->cd_faceset_offset); + } + case PBVH_GRIDS: + case PBVH_FACES: + return ss->face_sets[face.i]; + } + return -1; +} + +void SCULPT_face_check_origdata(SculptSession *ss, PBVHFaceRef face) +{ + if (!ss->attrs.orig_fsets) { + return; + } + + short *s = BKE_sculpt_face_attr_get(face, ss->attrs.orig_fsets); + + // pack ss->stroke_id in higher 16 bits + if (s[1] != ss->stroke_id) { + s[0] = SCULPT_face_set_get(ss, face); + s[1] = ss->stroke_id; + } +} + +int SCULPT_face_set_original_get(SculptSession *ss, PBVHFaceRef face) +{ + if (!ss->attrs.orig_fsets) { + return SCULPT_face_set_get(ss, face); + } + + short *s = BKE_sculpt_face_attr_get(face, ss->attrs.orig_fsets); + + if (s[1] != ss->stroke_id) { + s[0] = SCULPT_face_set_get(ss, face); + s[1] = ss->stroke_id; + } + + return s[0]; +} + +void SCULPT_face_ensure_original(SculptSession *ss, Object *ob) +{ + SculptAttributeParams params = {0}; + + ss->attrs.orig_fsets = BKE_sculpt_attribute_ensure( + ob, ATTR_DOMAIN_FACE, CD_PROP_INT32, SCULPT_ATTRIBUTE_NAME(orig_fsets), ¶ms); +} + +bool SCULPT_face_select_get(SculptSession *ss, PBVHFaceRef face) +{ + if (ss->bm) { + BMFace *f = (BMFace *)face.i; + + return f->head.hflag & BM_ELEM_SELECT; + } + else { + return ss->select_poly ? ss->select_poly[face.i] : false; + } +} /* Utils. */ @@ -65,7 +154,7 @@ int ED_sculpt_face_sets_find_next_available_id(struct Mesh *mesh) int next_face_set_id = 0; for (int i = 0; i < mesh->totpoly; i++) { - next_face_set_id = max_ii(next_face_set_id, face_sets[i]); + next_face_set_id = max_ii(next_face_set_id, abs(face_sets[i])); } next_face_set_id++; @@ -95,55 +184,175 @@ int ED_sculpt_face_sets_active_update_and_get(bContext *C, Object *ob, const flo } SculptCursorGeometryInfo gi; - if (!SCULPT_cursor_geometry_info_update(C, &gi, mval, false)) { + if (!SCULPT_cursor_geometry_info_update(C, &gi, mval, false, false)) { return SCULPT_FACE_SET_NONE; } return SCULPT_active_face_set_get(ss); } +static BMesh *sculpt_faceset_bm_begin(Object *ob, SculptSession *ss, Mesh *mesh) +{ + if (ss->bm) { + return ss->bm; + } + + BMeshCreateParams params = {0}; + params.use_toolflags = true; + + const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(mesh); + BMesh *bm = BM_mesh_create(&allocsize, ¶ms); + + BMeshFromMeshParams cparams = {0}; + + cparams.calc_face_normal = true; + cparams.active_shapekey = ob->shapenr; + cparams.use_shapekey = true; + cparams.create_shapekey_layers = true; + + BM_mesh_bm_from_me(NULL, bm, mesh, &cparams); + return bm; +} + +static void sculpt_faceset_bm_end(SculptSession *ss, BMesh *bm) +{ + if (bm != ss->bm) { + BM_mesh_free(bm); + } +} + /* Draw Face Sets Brush. */ -static void do_draw_face_sets_brush_task_cb_ex(void *__restrict userdata, - const int n, - const TaskParallelTLS *__restrict tls) +static int new_fset_apply_curve(SculptSession *ss, + SculptFaceSetDrawData *data, + int new_fset, + float poly_center[3], + float no[3], + SculptBrushTest *test, + CurveMapping *curve, + int count) { - SculptThreadedTaskData *data = static_cast(userdata); + float fade2; + float tmp[3]; + float n[3]; + + sub_v3_v3v3(tmp, poly_center, test->location); + + cross_v3_v3v3(n, no, data->stroke_direction); + normalize_v3(n); + + // find t along brush line + float t = dot_v3v3(data->stroke_direction, tmp) / ss->cache->radius; + CLAMP(t, -1.0f, 1.0f); + t = t * 0.5 + 0.5; + + // find start and end points; + float start[3], end[3]; + copy_v3_v3(start, ss->cache->last_location); + copy_v3_v3(end, ss->cache->location); + + madd_v3_v3fl(start, data->prev_stroke_direction, 0.5f * ss->cache->radius); + madd_v3_v3fl(end, data->next_stroke_direction, 0.5f * ss->cache->radius); + + float co[3]; + + // interpolate direction and pos across stroke line + float dir[3]; + if (t < 0.5) { + interp_v3_v3v3(co, start, test->location, t * 2.0f); + interp_v3_v3v3(dir, data->prev_stroke_direction, data->stroke_direction, t * 2.0f); + } + else { + interp_v3_v3v3(co, test->location, end, (t - 0.5f) * 2.0f); + interp_v3_v3v3(dir, data->stroke_direction, data->next_stroke_direction, (t - 0.5f) * 2.0f); + } + + sub_v3_v3v3(tmp, poly_center, co); + normalize_v3(dir); + + // get final distance from stroke curve + cross_v3_v3v3(n, no, dir); + normalize_v3(n); + + fade2 = fabsf(dot_v3v3(n, tmp)) / ss->cache->radius; + CLAMP(fade2, 0.0f, 1.0f); + + if (curve) { + fade2 = BKE_curvemapping_evaluateF(curve, 0, fade2); + } + + new_fset += (int)((1.0f - fade2) * (float)count); + + return new_fset; +} + +void do_draw_face_sets_brush_task_cb_ex(void *__restrict userdata, + const int n, + const TaskParallelTLS *__restrict tls) +{ + SculptFaceSetDrawData *data = (SculptFaceSetDrawData *)userdata; SculptSession *ss = data->ob->sculpt; const Brush *brush = data->brush; - const float bstrength = ss->cache->bstrength; + const float bstrength = data->bstrength; + + const bool use_fset_strength = data->use_fset_strength; + const bool use_fset_curve = data->use_fset_curve; + const int count = data->count; + const int active_fset = data->faceset; PBVHVertexIter vd; SculptBrushTest test; - SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( - ss, &test, data->brush->falloff_shape); + SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init(ss, &test); + const int thread_id = BLI_task_parallel_thread_id(tls); - const float(*positions)[3] = SCULPT_mesh_deformed_positions_get(ss); + float(*vert_positions)[3] = SCULPT_mesh_deformed_positions_get(ss); + const float test_limit = 0.05f; + int cd_mask = -1; + + if (ss->bm) { + cd_mask = CustomData_get_offset(&ss->bm->vdata, CD_PAINT_MASK); + } + + /*check if we need to sample the current face set*/ + + bool set_active_faceset = ss->cache->automasking && + (ss->cache->automasking->settings.flags & BRUSH_AUTOMASKING_FACE_SETS); + set_active_faceset = set_active_faceset && ss->cache->invert; + set_active_faceset = set_active_faceset && ss->cache->automasking->settings.initial_face_set == + ss->cache->automasking->settings.current_face_set; + + int automasking_fset_flag = 0; + + if (set_active_faceset) { + // temporarily clear faceset flag + automasking_fset_flag = ss->cache->automasking ? ss->cache->automasking->settings.flags & + BRUSH_AUTOMASKING_FACE_SETS : + 0; + ss->cache->automasking->settings.flags &= ~BRUSH_AUTOMASKING_FACE_SETS; + } + + bool modified = false; + AutomaskingNodeData automask_data; SCULPT_automasking_node_begin( data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]); - bool changed = false; BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { SCULPT_automasking_node_update(ss, &automask_data, &vd); if (BKE_pbvh_type(ss->pbvh) == PBVH_FACES) { - MeshElemMap *vert_map = &ss->pmap[vd.index]; - for (int j = 0; j < ss->pmap[vd.index].count; j++) { + MeshElemMap *vert_map = &ss->pmap->pmap[vd.index]; + for (int j = 0; j < ss->pmap->pmap[vd.index].count; j++) { const MPoly *p = &ss->mpoly[vert_map->indices[j]]; float poly_center[3]; - BKE_mesh_calc_poly_center(p, &ss->mloop[p->loopstart], positions, poly_center); + BKE_mesh_calc_poly_center(p, &ss->mloop[p->loopstart], vert_positions, poly_center); if (!sculpt_brush_test_sq_fn(&test, poly_center)) { continue; } - const bool face_hidden = ss->hide_poly && ss->hide_poly[vert_map->indices[j]]; - if (face_hidden) { - continue; - } const float fade = bstrength * SCULPT_brush_strength_factor(ss, brush, vd.co, @@ -155,9 +364,167 @@ static void do_draw_face_sets_brush_task_cb_ex(void *__restrict userdata, thread_id, &automask_data); - if (fade > 0.05f) { - ss->face_sets[vert_map->indices[j]] = ss->cache->paint_face_set; - changed = true; + int new_fset = active_fset; + + if (use_fset_curve) { + float no[3]; + SCULPT_vertex_normal_get(ss, vd.vertex, no); + + new_fset = new_fset_apply_curve( + ss, data, new_fset, poly_center, no, &test, data->curve, count); + } + + if (fade > test_limit && ss->face_sets[vert_map->indices[j]] > 0) { + bool ok = true; + + int fset = abs(ss->face_sets[vert_map->indices[j]]); + + // XXX kind of hackish, tries to sample faces that are within + // 8 pixels of the center of the brush, and using a crude linear + // scale at that - joeedh + if (set_active_faceset && + fset != abs(ss->cache->automasking->settings.initial_face_set)) { + + float radius = ss->cache->radius; + float pixels = 8; // TODO: multiply with DPI + radius = pixels * (radius / (float)ss->cache->dyntopo_pixel_radius); + + if (sqrtf(test.dist) < radius) { + ss->cache->automasking->settings.initial_face_set = abs(fset); + set_active_faceset = false; + ss->cache->automasking->settings.flags |= BRUSH_AUTOMASKING_FACE_SETS; + } + else { + ok = false; + } + } + + const MLoop *ml = &ss->mloop[p->loopstart]; + + for (int i = 0; i < p->totloop; i++, ml++) { + float *v = vert_positions[ml->v]; + float fno[3]; + + *SCULPT_vertex_attr_get( + BKE_pbvh_make_vref(ml->v), + ss->attrs.boundary_flags) |= SCULPT_BOUNDARY_NEEDS_UPDATE; + + copy_v3_v3(fno, ss->vert_normals[ml->v]); + float mask = ss->vmask ? ss->vmask[ml->v] : 0.0f; + + const float fade2 = bstrength * + SCULPT_brush_strength_factor(ss, + brush, + v, + sqrtf(test.dist), + ss->vert_normals[ml->v], + fno, + mask, + BKE_pbvh_make_vref((intptr_t)ml->v), + thread_id, + &automask_data); + + if (fade2 < test_limit) { + ok = false; + break; + } + } + + if (ok) { + ss->face_sets[vert_map->indices[j]] = new_fset; + modified = true; + } + } + } + } + else if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) { + BMVert *v = vd.bm_vert; + BMIter iter; + BMFace *f; + + BM_ITER_ELEM (f, &iter, v, BM_FACES_OF_VERT) { + float poly_center[3]; + BM_face_calc_center_median(f, poly_center); + + if (sculpt_brush_test_sq_fn(&test, poly_center)) { + const float fade = bstrength * SCULPT_brush_strength_factor(ss, + brush, + vd.co, + sqrtf(test.dist), + vd.no, + vd.fno, + vd.mask ? *vd.mask : 0.0f, + vd.vertex, + thread_id, + &automask_data); + + int new_fset = active_fset; + + if (use_fset_curve) { + float no[3]; + SCULPT_vertex_normal_get(ss, vd.vertex, no); + + new_fset = new_fset_apply_curve( + ss, data, new_fset, poly_center, no, &test, data->curve, count); + } + + int fset = BM_ELEM_CD_GET_INT(f, ss->cd_faceset_offset); + + if ((!use_fset_strength || fade > test_limit) && fset > 0) { + BMLoop *l = f->l_first; + + bool ok = true; + + // XXX kind of hackish, tries to sample faces that are within + // 8 pixels of the center of the brush, and using a crude linear + // scale at that - joeedh + if (set_active_faceset && + abs(fset) != abs(ss->cache->automasking->settings.initial_face_set)) { + + float radius = ss->cache->radius; + float pixels = 8; // TODO: multiple with DPI + radius = pixels * (radius / (float)ss->cache->dyntopo_pixel_radius); + + if (sqrtf(test.dist) < radius) { + ss->cache->automasking->settings.initial_face_set = abs(fset); + set_active_faceset = false; + ss->cache->automasking->settings.flags |= BRUSH_AUTOMASKING_FACE_SETS; + } + else { + ok = false; + } + } + + do { + float mask = cd_mask >= 0 ? BM_ELEM_CD_GET_FLOAT(l->v, cd_mask) : 0.0f; + + const float fade2 = bstrength * + SCULPT_brush_strength_factor(ss, + brush, + l->v->co, + sqrtf(test.dist), + l->v->no, + l->f->no, + mask, + BKE_pbvh_make_vref((intptr_t)l->v), + thread_id, + &automask_data); + + if (fade2 < test_limit) { + ok = false; + break; + } + + *SCULPT_vertex_attr_get( + BKE_pbvh_make_vref((intptr_t)l->v), + ss->attrs.boundary_flags) |= SCULPT_BOUNDARY_NEEDS_UPDATE; + } while ((l = l->next) != f->l_first); + + if (ok) { + BM_ELEM_CD_SET_INT(f, ss->cd_faceset_offset, new_fset); + modified = true; + } + } } } } @@ -175,17 +542,31 @@ static void do_draw_face_sets_brush_task_cb_ex(void *__restrict userdata, vd.vertex, thread_id, &automask_data); + int new_fset = active_fset; - if (fade > 0.05f) { - SCULPT_vertex_face_set_set(ss, vd.vertex, ss->cache->paint_face_set); - changed = true; + if (use_fset_curve) { + float no[3]; + SCULPT_vertex_normal_get(ss, vd.vertex, no); + + new_fset = new_fset_apply_curve( + ss, data, new_fset, ss->cache->location, no, &test, data->curve, count); + } + + if (!use_fset_strength || fade > test_limit) { + SCULPT_vertex_face_set_set(ss, vd.vertex, new_fset); + modified = true; } } } BKE_pbvh_vertex_iter_end; - if (changed) { - SCULPT_undo_push_node(data->ob, data->nodes[n], SCULPT_UNDO_FACE_SETS); + if (modified) { + BKE_pbvh_node_mark_update_index_buffer(ss->pbvh, data->nodes[n]); + } + + // restore automasking flag + if (set_active_faceset) { + ss->cache->automasking->settings.flags |= automasking_fset_flag; } } @@ -193,7 +574,7 @@ static void do_relax_face_sets_brush_task_cb_ex(void *__restrict userdata, const int n, const TaskParallelTLS *__restrict tls) { - SculptThreadedTaskData *data = static_cast(userdata); + SculptFaceSetDrawData *data = (SculptFaceSetDrawData *)userdata; SculptSession *ss = data->ob->sculpt; const Brush *brush = data->brush; float bstrength = ss->cache->bstrength; @@ -201,8 +582,7 @@ static void do_relax_face_sets_brush_task_cb_ex(void *__restrict userdata, PBVHVertexIter vd; SculptBrushTest test; - SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( - ss, &test, data->brush->falloff_shape); + SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init(ss, &test); const bool relax_face_sets = !(ss->cache->iteration_count % 3 == 0); /* This operations needs a strength tweak as the relax deformation is too weak by default. */ @@ -215,6 +595,8 @@ static void do_relax_face_sets_brush_task_cb_ex(void *__restrict userdata, SCULPT_automasking_node_begin( data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]); + bool do_reproject = SCULPT_need_reproject(ss); + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { SCULPT_automasking_node_update(ss, &automask_data, &vd); @@ -225,21 +607,35 @@ static void do_relax_face_sets_brush_task_cb_ex(void *__restrict userdata, continue; } - const float fade = bstrength * SCULPT_brush_strength_factor(ss, - brush, - vd.co, - sqrtf(test.dist), - vd.no, - vd.fno, - vd.mask ? *vd.mask : 0.0f, - vd.vertex, - thread_id, - &automask_data); + float fade = bstrength * SCULPT_brush_strength_factor(ss, + brush, + vd.co, + sqrtf(test.dist), + vd.no, + vd.fno, + vd.mask ? *vd.mask : 0.0f, + vd.vertex, + thread_id, + &automask_data); - SCULPT_relax_vertex(ss, &vd, fade * bstrength, relax_face_sets, vd.co); + CLAMP(fade, 0.0f, 1.0f); + + float oldco[3], oldno[3]; + + copy_v3_v3(oldco, vd.co); + SCULPT_vertex_normal_get(ss, vd.vertex, oldno); + + SCULPT_relax_vertex(ss, + &vd, + fade * bstrength, + (eSculptBoundary)(SCULPT_BOUNDARY_DEFAULT | SCULPT_BOUNDARY_FACE_SET), + vd.co); if (vd.is_mesh) { BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); } + if (do_reproject) { + SCULPT_reproject_cdata(ss, vd.vertex, oldco, oldno); + } } BKE_pbvh_vertex_iter_end; } @@ -247,8 +643,9 @@ static void do_relax_face_sets_brush_task_cb_ex(void *__restrict userdata, void SCULPT_do_draw_face_sets_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) { SculptSession *ss = ob->sculpt; - Brush *brush = BKE_paint_brush(&sd->paint); + Brush *brush = ss->cache->brush ? ss->cache->brush : BKE_paint_brush(&sd->paint); + BKE_sculpt_face_sets_ensure(ob); if (ss->pbvh) { Mesh *mesh = BKE_mesh_from_object(ob); BKE_pbvh_face_sets_color_set( @@ -258,14 +655,41 @@ void SCULPT_do_draw_face_sets_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, in BKE_curvemapping_init(brush->curve); /* Threaded loop over nodes. */ - SculptThreadedTaskData data{}; + SculptFaceSetDrawData data; + data.sd = sd; data.ob = ob; data.brush = brush; data.nodes = nodes; + data.faceset = abs(ss->cache->paint_face_set); + data.use_fset_curve = false; + data.use_fset_strength = true; + data.bstrength = ss->cache->bstrength; + data.count = 1; + bool threaded = true; + + /*for ctrl invert mode we have to set the automasking initial_face_set + to the first non-current faceset that is found*/ + int automasking_flags = brush->automasking_flags | (sd ? sd->automasking_flags : 0); + + if (SCULPT_stroke_is_first_brush_step(ss->cache)) { + if (ss->cache->invert && ss->cache->automasking && + (automasking_flags & BRUSH_AUTOMASKING_FACE_SETS)) { + ss->cache->automasking->settings.current_face_set = + ss->cache->automasking->settings.initial_face_set; + } + } + + if (ss->cache->invert && !ss->cache->alt_smooth && ss->cache->automasking && + ss->cache->automasking->settings.initial_face_set == + ss->cache->automasking->settings.current_face_set) { + threaded = false; + } + + // ctrl-click is single threaded since the tasks will set the initial face set TaskParallelSettings settings; - BKE_pbvh_parallel_range_settings(&settings, true, totnode); + BKE_pbvh_parallel_range_settings(&settings, threaded, totnode); if (ss->cache->alt_smooth) { SCULPT_boundary_info_ensure(ob); for (int i = 0; i < 4; i++) { @@ -316,7 +740,7 @@ static EnumPropertyItem prop_sculpt_face_set_create_types[] = { "Face Set from Edit Mode Selection", "Create an Face Set corresponding to the Edit Mode face selection", }, - {0, nullptr, 0, nullptr, nullptr}, + {0, NULL, 0, NULL, NULL}, }; static int sculpt_face_set_create_exec(bContext *C, wmOperator *op) @@ -328,23 +752,18 @@ static int sculpt_face_set_create_exec(bContext *C, wmOperator *op) const int mode = RNA_enum_get(op->ptr, "mode"); - /* Dyntopo not supported. */ - if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) { - return OPERATOR_CANCELLED; - } - - Mesh *mesh = static_cast(ob->data); - ss->face_sets = BKE_sculpt_face_sets_ensure(mesh); - BKE_sculpt_update_object_for_edit(depsgraph, ob, true, mode == SCULPT_FACE_SET_MASKED, false); + SCULPT_face_random_access_ensure(ss); + SCULPT_vertex_random_access_ensure(ss); + const int tot_vert = SCULPT_vertex_count_get(ss); float threshold = 0.5f; PBVH *pbvh = ob->sculpt->pbvh; PBVHNode **nodes; int totnode; - BKE_pbvh_search_gather(pbvh, nullptr, nullptr, &nodes, &totnode); + BKE_pbvh_search_gather(pbvh, NULL, NULL, &nodes, &totnode); if (!nodes) { return OPERATOR_CANCELLED; @@ -384,6 +803,7 @@ static int sculpt_face_set_create_exec(bContext *C, wmOperator *op) } if (all_visible) { + Mesh *mesh = (Mesh *)ob->data; mesh->face_sets_color_default = next_face_set; BKE_pbvh_face_sets_color_set( ss->pbvh, mesh->face_sets_color_seed, mesh->face_sets_color_default); @@ -407,16 +827,24 @@ static int sculpt_face_set_create_exec(bContext *C, wmOperator *op) } if (mode == SCULPT_FACE_SET_SELECTION) { - const bke::AttributeAccessor attributes = mesh->attributes(); - const VArraySpan select_poly = attributes.lookup_or_default( - ".select_poly", ATTR_DOMAIN_FACE, false); - threading::parallel_for(IndexRange(mesh->totvert), 4096, [&](const IndexRange range) { - for (const int i : range) { - if (select_poly[i]) { - ss->face_sets[i] = next_face_set; - } + const int totface = ss->totfaces; + + for (int i = 0; i < totface; i++) { + PBVHFaceRef fref = BKE_pbvh_index_to_face(ss->pbvh, i); + + // XXX check hidden? + bool ok = true; + + if (ss->attrs.hide_poly) { + ok = *BKE_sculpt_face_attr_get(fref, ss->attrs.hide_poly); } - }); + + ok = ok && SCULPT_face_select_get(ss, fref); + + if (ok) { + SCULPT_face_set_set(ss, fref, next_face_set); + } + } } for (int i = 0; i < totnode; i++) { @@ -525,104 +953,214 @@ static EnumPropertyItem prop_sculpt_face_sets_init_types[] = { "Face Sets from Face Set Boundaries", "Create a Face Set per isolated Face Set", }, - - {0, nullptr, 0, nullptr, nullptr}, + {0, NULL, 0, NULL, NULL}, }; -using FaceSetsFloodFillFn = blender::FunctionRef; +typedef bool (*face_sets_flood_fill_test)( + BMesh *bm, BMFace *from_f, BMEdge *from_e, BMFace *to_f, const float threshold); -static void sculpt_face_sets_init_flood_fill(Object *ob, const FaceSetsFloodFillFn &test_fn) +static bool sculpt_face_sets_init_loose_parts_test(BMesh * /*bm*/, + BMFace * /*from_f*/, + BMEdge * /*from_e*/, + BMFace * /*UNUSED(to_f)*/, + const float /*UNUSED(threshold)*/) +{ + return true; +} + +static bool sculpt_face_sets_init_normals_test(BMesh * /*bm*/, + BMFace *from_f, + BMEdge * /*UNUSED(from_e)*/, + BMFace *to_f, + const float threshold) +{ + return fabsf(dot_v3v3(from_f->no, to_f->no)) > threshold; +} + +static bool sculpt_face_sets_init_uv_seams_test(BMesh * /*UNUSED(bm)*/, + BMFace * /*UNUSED(from_f)*/, + BMEdge *from_e, + BMFace * /*UNUSED(to_f)*/, + const float /*UNUSED(threshold)*/) +{ + return !BM_elem_flag_test(from_e, BM_ELEM_SEAM); +} + +static bool sculpt_face_sets_init_crease_test(BMesh *bm, + BMFace * /*UNUSED(from_f)*/, + BMEdge *from_e, + BMFace * /*UNUSED(to_f)*/, + const float threshold) +{ + return BM_elem_float_data_get(&bm->edata, from_e, CD_CREASE) < threshold; +} + +static bool sculpt_face_sets_init_bevel_weight_test(BMesh *bm, + BMFace * /*UNUSED(from_f)*/, + BMEdge *from_e, + BMFace * /*UNUSED(to_f)*/, + const float threshold) +{ + return BM_elem_float_data_get(&bm->edata, from_e, CD_BWEIGHT) < threshold; +} + +static bool sculpt_face_sets_init_sharp_edges_test(BMesh * /*UNUSED(bm)*/, + BMFace * /*UNUSED(from_f)*/, + BMEdge *from_e, + BMFace * /*UNUSED(to_f)*/, + const float /*UNUSED(threshold)*/) +{ + return BM_elem_flag_test(from_e, BM_ELEM_SMOOTH); +} + +static bool sculpt_face_sets_init_face_set_boundary_test(BMesh *bm, + BMFace *from_f, + BMEdge * /*UNUSED(from_e)*/, + BMFace *to_f, + const float /*UNUSED(threshold)*/) +{ + const int cd_face_sets_offset = CustomData_get_offset_named( + &bm->pdata, CD_PROP_INT32, ".sculpt_face_sets"); + + return BM_ELEM_CD_GET_INT(from_f, cd_face_sets_offset) == + BM_ELEM_CD_GET_INT(to_f, cd_face_sets_offset); +} + +static void sculpt_face_sets_init_flood_fill(Object *ob, + face_sets_flood_fill_test test, + const float threshold) { - using namespace blender; SculptSession *ss = ob->sculpt; - Mesh *mesh = static_cast(ob->data); + Mesh *mesh = (Mesh *)ob->data; + BMesh *bm; - BitVector<> visited_faces(mesh->totpoly, false); + SCULPT_vertex_random_access_ensure(ss); + SCULPT_face_random_access_ensure(ss); - int *face_sets = ss->face_sets; + bm = sculpt_faceset_bm_begin(ob, ss, mesh); - const Span edges = mesh->edges(); - const Span polys = mesh->polys(); - const Span loops = mesh->loops(); + BLI_bitmap *visited_faces = BLI_BITMAP_NEW(ss->totfaces, "visited faces"); + const int totfaces = ss->totfaces; // mesh->totpoly; - if (!ss->epmap) { - BKE_mesh_edge_poly_map_create(&ss->epmap, - &ss->epmap_mem, - edges.size(), - polys.data(), - polys.size(), - loops.data(), - loops.size()); + if (!ss->bm) { + BM_mesh_elem_index_ensure(bm, BM_FACE); + BM_mesh_elem_table_ensure(bm, BM_FACE); } int next_face_set = 1; - for (const int i : polys.index_range()) { - if (visited_faces[i]) { + for (int i = 0; i < totfaces; i++) { + if (BLI_BITMAP_TEST(visited_faces, i)) { continue; } - std::queue queue; + GSQueue *queue; + queue = BLI_gsqueue_new(sizeof(int)); - face_sets[i] = next_face_set; - visited_faces[i].set(true); - queue.push(i); + PBVHFaceRef fref = BKE_pbvh_index_to_face(ss->pbvh, i); + SCULPT_face_set_set(ss, fref, next_face_set); - while (!queue.empty()) { - const int poly_i = queue.front(); - const MPoly &poly = polys[poly_i]; - queue.pop(); + BLI_BITMAP_ENABLE(visited_faces, i); + BLI_gsqueue_push(queue, &i); - for (const MLoop &loop : loops.slice(poly.loopstart, poly.totloop)) { - const int edge_i = loop.e; - const Span neighbor_polys(ss->epmap[edge_i].indices, ss->epmap[edge_i].count); - for (const int neighbor_i : neighbor_polys) { - if (neighbor_i == poly_i) { + while (!BLI_gsqueue_is_empty(queue)) { + int from_f; + BLI_gsqueue_pop(queue, &from_f); + + BMFace *f, *f_neighbor; + BMEdge *ed; + BMIter iter_a, iter_b; + + f = BM_face_at_index(bm, from_f); + + BM_ITER_ELEM (ed, &iter_a, f, BM_EDGES_OF_FACE) { + BM_ITER_ELEM (f_neighbor, &iter_b, ed, BM_FACES_OF_EDGE) { + if (f_neighbor == f) { continue; } - if (visited_faces[neighbor_i]) { + int neighbor_face_index = BM_elem_index_get(f_neighbor); + if (BLI_BITMAP_TEST(visited_faces, neighbor_face_index)) { continue; } - if (!test_fn(poly_i, edge_i, neighbor_i)) { + if (!test(bm, f, ed, f_neighbor, threshold)) { continue; } - face_sets[neighbor_i] = next_face_set; - visited_faces[neighbor_i].set(true); - queue.push(neighbor_i); + PBVHFaceRef fref2 = BKE_pbvh_index_to_face(ss->pbvh, neighbor_face_index); + SCULPT_face_set_set(ss, fref2, next_face_set); + + BLI_BITMAP_ENABLE(visited_faces, neighbor_face_index); + BLI_gsqueue_push(queue, &neighbor_face_index); } } } next_face_set += 1; + + BLI_gsqueue_free(queue); } + + MEM_SAFE_FREE(visited_faces); + + sculpt_faceset_bm_end(ss, bm); } static void sculpt_face_sets_init_loop(Object *ob, const int mode) { - using namespace blender; - Mesh *mesh = static_cast(ob->data); SculptSession *ss = ob->sculpt; - if (mode == SCULPT_FACE_SETS_FROM_MATERIALS) { - const bke::AttributeAccessor attributes = mesh->attributes(); - const VArraySpan material_indices = attributes.lookup_or_default( - "material_index", ATTR_DOMAIN_FACE, 0); - for (const int i : IndexRange(mesh->totpoly)) { - ss->face_sets[i] = material_indices[i] + 1; - } + SCULPT_face_random_access_ensure(ss); + + int cd_fmaps_offset = -1; + if (ss->bm) { + cd_fmaps_offset = CustomData_get_offset(&ss->bm->pdata, CD_FACEMAP); } - else if (mode == SCULPT_FACE_SETS_FROM_FACE_MAPS) { - const int *face_maps = static_cast( - CustomData_get_layer(&mesh->pdata, CD_FACEMAP)); - for (const int i : IndexRange(mesh->totpoly)) { - ss->face_sets[i] = face_maps ? face_maps[i] : 1; + + Mesh *me = NULL; + int *fmaps = NULL; + + if (BKE_pbvh_type(ss->pbvh) == PBVH_GRIDS) { + me = (Mesh *)ob->data; + fmaps = (int *)CustomData_get_layer(&me->pdata, CD_FACEMAP); + } + else if (BKE_pbvh_type(ss->pbvh) == PBVH_FACES) { + fmaps = (int *)CustomData_get_layer(ss->pdata, CD_FACEMAP); + } + + for (int i = 0; i < ss->totfaces; i++) { + PBVHFaceRef fref = BKE_pbvh_index_to_face(ss->pbvh, i); + + if (mode == SCULPT_FACE_SETS_FROM_MATERIALS) { + SCULPT_face_set_set(ss, fref, (int)(sculpt_face_material_get(ss, fref) + 1)); + } + else if (mode == SCULPT_FACE_SETS_FROM_FACE_MAPS) { + int fmap = 1; + + switch (BKE_pbvh_type(ss->pbvh)) { + case PBVH_BMESH: { + BMFace *f = (BMFace *)fref.i; + + if (cd_fmaps_offset >= 0) { + fmap = BM_ELEM_CD_GET_INT(f, cd_fmaps_offset) + 2; + } + + break; + } + case PBVH_FACES: + case PBVH_GRIDS: { + if (fmaps) { + fmap = fmaps[i] + 2; + } + break; + } + } + + SCULPT_face_set_set(ss, fref, fmap); } } } static int sculpt_face_set_init_exec(bContext *C, wmOperator *op) { - using namespace blender; Object *ob = CTX_data_active_object(C); SculptSession *ss = ob->sculpt; Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); @@ -630,6 +1168,7 @@ static int sculpt_face_set_init_exec(bContext *C, wmOperator *op) const int mode = RNA_enum_get(op->ptr, "mode"); BKE_sculpt_update_object_for_edit(depsgraph, ob, true, false, false); + ss->face_sets = BKE_sculpt_face_sets_ensure(ob); /* Dyntopo not supported. */ if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) { @@ -639,7 +1178,7 @@ static int sculpt_face_set_init_exec(bContext *C, wmOperator *op) PBVH *pbvh = ob->sculpt->pbvh; PBVHNode **nodes; int totnode; - BKE_pbvh_search_gather(pbvh, nullptr, nullptr, &nodes, &totnode); + BKE_pbvh_search_gather(pbvh, NULL, NULL, &nodes, &totnode); if (!nodes) { return OPERATOR_CANCELLED; @@ -652,89 +1191,41 @@ static int sculpt_face_set_init_exec(bContext *C, wmOperator *op) const float threshold = RNA_float_get(op->ptr, "threshold"); - Mesh *mesh = static_cast(ob->data); - ss->face_sets = BKE_sculpt_face_sets_ensure(mesh); - const bke::AttributeAccessor attributes = mesh->attributes(); - switch (mode) { - case SCULPT_FACE_SETS_FROM_LOOSE_PARTS: { - const VArray hide_poly = attributes.lookup_or_default( - ".hide_poly", ATTR_DOMAIN_FACE, false); - sculpt_face_sets_init_flood_fill( - ob, [&](const int from_face, const int /*edge*/, const int to_face) { - return hide_poly[from_face] == hide_poly[to_face]; - }); + case SCULPT_FACE_SETS_FROM_LOOSE_PARTS: + sculpt_face_sets_init_flood_fill(ob, sculpt_face_sets_init_loose_parts_test, threshold); break; - } - case SCULPT_FACE_SETS_FROM_MATERIALS: { + case SCULPT_FACE_SETS_FROM_MATERIALS: sculpt_face_sets_init_loop(ob, SCULPT_FACE_SETS_FROM_MATERIALS); break; - } - case SCULPT_FACE_SETS_FROM_NORMALS: { - const Span poly_normals( - reinterpret_cast(BKE_mesh_poly_normals_ensure(mesh)), mesh->totpoly); - sculpt_face_sets_init_flood_fill( - ob, [&](const int from_face, const int /*edge*/, const int to_face) -> bool { - return std::abs(math::dot(poly_normals[from_face], poly_normals[to_face])) > threshold; - }); + case SCULPT_FACE_SETS_FROM_NORMALS: + sculpt_face_sets_init_flood_fill(ob, sculpt_face_sets_init_normals_test, threshold); break; - } - case SCULPT_FACE_SETS_FROM_UV_SEAMS: { - const Span edges = mesh->edges(); - sculpt_face_sets_init_flood_fill( - ob, [&](const int /*from_face*/, const int edge, const int /*to_face*/) -> bool { - return (edges[edge].flag & ME_SEAM) == 0; - }); + case SCULPT_FACE_SETS_FROM_UV_SEAMS: + sculpt_face_sets_init_flood_fill(ob, sculpt_face_sets_init_uv_seams_test, threshold); break; - } - case SCULPT_FACE_SETS_FROM_CREASES: { - const float *creases = static_cast( - CustomData_get_layer(&mesh->edata, CD_CREASE)); - sculpt_face_sets_init_flood_fill( - ob, [&](const int /*from_face*/, const int edge, const int /*to_face*/) -> bool { - return creases ? creases[edge] < threshold : true; - }); + case SCULPT_FACE_SETS_FROM_CREASES: + sculpt_face_sets_init_flood_fill(ob, sculpt_face_sets_init_crease_test, threshold); break; - } - case SCULPT_FACE_SETS_FROM_SHARP_EDGES: { - const VArraySpan sharp_edges = mesh->attributes().lookup_or_default( - "sharp_edge", ATTR_DOMAIN_EDGE, false); - sculpt_face_sets_init_flood_fill( - ob, [&](const int /*from_face*/, const int edge, const int /*to_face*/) -> bool { - return !sharp_edges[edge]; - }); + case SCULPT_FACE_SETS_FROM_SHARP_EDGES: + sculpt_face_sets_init_flood_fill(ob, sculpt_face_sets_init_sharp_edges_test, threshold); break; - } - case SCULPT_FACE_SETS_FROM_BEVEL_WEIGHT: { - const float *bevel_weights = static_cast( - CustomData_get_layer(&mesh->edata, CD_BWEIGHT)); - sculpt_face_sets_init_flood_fill( - ob, [&](const int /*from_face*/, const int edge, const int /*to_face*/) -> bool { - return bevel_weights ? bevel_weights[edge] < threshold : true; - }); + case SCULPT_FACE_SETS_FROM_BEVEL_WEIGHT: + sculpt_face_sets_init_flood_fill(ob, sculpt_face_sets_init_bevel_weight_test, threshold); break; - } - case SCULPT_FACE_SETS_FROM_FACE_SET_BOUNDARIES: { - Array face_sets_copy(Span(ss->face_sets, mesh->totpoly)); + case SCULPT_FACE_SETS_FROM_FACE_SET_BOUNDARIES: sculpt_face_sets_init_flood_fill( - ob, [&](const int from_face, const int /*edge*/, const int to_face) -> bool { - return face_sets_copy[from_face] == face_sets_copy[to_face]; - }); + ob, sculpt_face_sets_init_face_set_boundary_test, threshold); break; - } - case SCULPT_FACE_SETS_FROM_FACE_MAPS: { + case SCULPT_FACE_SETS_FROM_FACE_MAPS: sculpt_face_sets_init_loop(ob, SCULPT_FACE_SETS_FROM_FACE_MAPS); break; - } } SCULPT_undo_push_end(ob); - /* Sync face sets visibility and vertex visibility as now all Face Sets are visible. */ - SCULPT_visibility_sync_all_from_faces(ob); - for (int i = 0; i < totnode; i++) { - BKE_pbvh_node_mark_update_visibility(nodes[i]); + BKE_pbvh_node_mark_redraw(nodes[i]); } BKE_pbvh_update_vertex_data(ss->pbvh, PBVH_UpdateVisibility); @@ -742,7 +1233,7 @@ static int sculpt_face_set_init_exec(bContext *C, wmOperator *op) MEM_SAFE_FREE(nodes); if (BKE_pbvh_type(pbvh) == PBVH_FACES) { - BKE_mesh_flush_hidden_from_verts(mesh); + BKE_mesh_flush_hidden_from_verts((Mesh *)ob->data); } SCULPT_tag_update_overlays(C); @@ -816,21 +1307,46 @@ static EnumPropertyItem prop_sculpt_face_sets_change_visibility_types[] = { {0, nullptr, 0, nullptr, nullptr}, }; +void SCULPT_face_sets_visibility_all_set(SculptSession *ss, bool state) +{ + for (int i = 0; i < ss->totfaces; i++) { + PBVHFaceRef face = BKE_pbvh_index_to_face(ss->pbvh, i); + + *BKE_sculpt_face_attr_get(face, ss->attrs.hide_poly) = !state; + } +} + +void SCULPT_face_sets_visibility_invert(SculptSession *ss) +{ + for (int i = 0; i < ss->totfaces; i++) { + PBVHFaceRef face = BKE_pbvh_index_to_face(ss->pbvh, i); + + *BKE_sculpt_face_attr_get(face, ss->attrs.hide_poly) ^= true; + } +} + +bool sculpt_has_face_sets(Object *ob) +{ + SculptSession *ss = ob->sculpt; + + if (ss->bm) { + return CustomData_get_offset_named(&ss->bm->pdata, CD_PROP_INT32, ".sculpt_face_set") != -1; + } + else { + Mesh *mesh = BKE_object_get_original_mesh(ob); + + return CustomData_get_layer_named(&mesh->pdata, CD_PROP_INT32, ".sculpt_face_set"); + } +} + static int sculpt_face_sets_change_visibility_exec(bContext *C, wmOperator *op) { Object *ob = CTX_data_active_object(C); SculptSession *ss = ob->sculpt; Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); - Mesh *mesh = BKE_object_get_original_mesh(ob); - BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true, false); - /* Not supported for dyntopo. */ - if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) { - return OPERATOR_CANCELLED; - } - const int mode = RNA_enum_get(op->ptr, "mode"); const int tot_vert = SCULPT_vertex_count_get(ss); @@ -845,6 +1361,9 @@ static int sculpt_face_sets_change_visibility_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } + SCULPT_vertex_random_access_ensure(ss); + SCULPT_face_random_access_ensure(ss); + const int active_face_set = SCULPT_active_face_set_get(ss); SCULPT_undo_push_begin(ob, op); @@ -869,22 +1388,22 @@ static int sculpt_face_sets_change_visibility_exec(bContext *C, wmOperator *op) } } - if (ss->hide_poly) { + if (!hidden_vertex && ss->attrs.hide_poly) { for (int i = 0; i < ss->totfaces; i++) { - if (ss->hide_poly[i]) { + PBVHFaceRef face = BKE_pbvh_index_to_face(ss->pbvh, i); + + if (*BKE_sculpt_face_attr_get(face, ss->attrs.hide_poly)) { hidden_vertex = true; break; } } } - ss->hide_poly = BKE_sculpt_hide_poly_ensure(mesh); - if (hidden_vertex) { SCULPT_face_visibility_all_set(ss, true); } else { - if (ss->face_sets) { + if (sculpt_has_face_sets(ob)) { SCULPT_face_visibility_all_set(ss, false); SCULPT_face_set_visibility_set(ss, active_face_set, true); } @@ -895,9 +1414,9 @@ static int sculpt_face_sets_change_visibility_exec(bContext *C, wmOperator *op) break; } case SCULPT_FACE_SET_VISIBILITY_SHOW_ACTIVE: - ss->hide_poly = BKE_sculpt_hide_poly_ensure(mesh); + ss->hide_poly = BKE_sculpt_hide_poly_ensure(ob); - if (ss->face_sets) { + if (sculpt_has_face_sets(ob)) { SCULPT_face_visibility_all_set(ss, false); SCULPT_face_set_visibility_set(ss, active_face_set, true); } @@ -906,9 +1425,9 @@ static int sculpt_face_sets_change_visibility_exec(bContext *C, wmOperator *op) } break; case SCULPT_FACE_SET_VISIBILITY_HIDE_ACTIVE: - ss->hide_poly = BKE_sculpt_hide_poly_ensure(mesh); + ss->hide_poly = BKE_sculpt_hide_poly_ensure(ob); - if (ss->face_sets) { + if (sculpt_has_face_sets(ob)) { SCULPT_face_set_visibility_set(ss, active_face_set, false); } else { @@ -917,7 +1436,7 @@ static int sculpt_face_sets_change_visibility_exec(bContext *C, wmOperator *op) break; case SCULPT_FACE_SET_VISIBILITY_INVERT: - ss->hide_poly = BKE_sculpt_hide_poly_ensure(mesh); + ss->hide_poly = BKE_sculpt_hide_poly_ensure(ob); SCULPT_face_visibility_all_invert(ss); break; } @@ -941,6 +1460,7 @@ static int sculpt_face_sets_change_visibility_exec(bContext *C, wmOperator *op) SCULPT_undo_push_end(ob); for (int i = 0; i < totnode; i++) { BKE_pbvh_node_mark_update_visibility(nodes[i]); + BKE_pbvh_bmesh_check_tris(ss->pbvh, nodes[i]); } BKE_pbvh_update_vertex_data(ss->pbvh, PBVH_UpdateVisibility); @@ -959,12 +1479,12 @@ static int sculpt_face_sets_change_visibility_invoke(bContext *C, Object *ob = CTX_data_active_object(C); SculptSession *ss = ob->sculpt; - /* Update the active vertex and Face Set using the cursor position to avoid relying on the paint - * cursor updates. */ + /* Update the active vertex and Face Set using the cursor position to avoid relying on the + * paint cursor updates. */ SculptCursorGeometryInfo sgi; const float mval_fl[2] = {float(event->mval[0]), float(event->mval[1])}; SCULPT_vertex_random_access_ensure(ss); - SCULPT_cursor_geometry_info_update(C, &sgi, mval_fl, false); + SCULPT_cursor_geometry_info_update(C, &sgi, mval_fl, false, false); return sculpt_face_sets_change_visibility_exec(C, op); } @@ -997,30 +1517,25 @@ static int sculpt_face_sets_randomize_colors_exec(bContext *C, wmOperator * /*op Object *ob = CTX_data_active_object(C); SculptSession *ss = ob->sculpt; - /* Dyntopo not supported. */ - if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) { - return OPERATOR_CANCELLED; - } - - if (!ss->face_sets) { - return OPERATOR_CANCELLED; - } - PBVH *pbvh = ob->sculpt->pbvh; PBVHNode **nodes; int totnode; - Mesh *mesh = static_cast(ob->data); + Mesh *mesh = (Mesh *)ob->data; + + SCULPT_face_random_access_ensure(ss); mesh->face_sets_color_seed += 1; - if (ss->face_sets) { + if (ss->face_sets || (ss->bm && ss->cd_faceset_offset >= 0)) { const int random_index = clamp_i(ss->totfaces * BLI_hash_int_01(mesh->face_sets_color_seed), 0, max_ii(0, ss->totfaces - 1)); - mesh->face_sets_color_default = ss->face_sets[random_index]; + + PBVHFaceRef fref = BKE_pbvh_index_to_face(ss->pbvh, random_index); + mesh->face_sets_color_default = SCULPT_face_set_get(ss, fref); } BKE_pbvh_face_sets_color_set(pbvh, mesh->face_sets_color_seed, mesh->face_sets_color_default); - BKE_pbvh_search_gather(pbvh, nullptr, nullptr, &nodes, &totnode); + BKE_pbvh_search_gather(pbvh, NULL, NULL, &nodes, &totnode); for (int i = 0; i < totnode; i++) { BKE_pbvh_node_mark_redraw(nodes[i]); } @@ -1052,6 +1567,10 @@ enum eSculptFaceSetEditMode { SCULPT_FACE_SET_EDIT_DELETE_GEOMETRY = 2, SCULPT_FACE_SET_EDIT_FAIR_POSITIONS = 3, SCULPT_FACE_SET_EDIT_FAIR_TANGENCY = 4, + SCULPT_FACE_SET_EDIT_FAIR_CURVATURE = 5, + SCULPT_FACE_SET_EDIT_FILL_COMPONENT = 6, + SCULPT_FACE_SET_EDIT_EXTRUDE = 7, + SCULPT_FACE_SET_EDIT_FAIR_ALL_TANGENCY = 8, }; static EnumPropertyItem prop_sculpt_face_sets_edit_types[] = { @@ -1092,27 +1611,99 @@ static EnumPropertyItem prop_sculpt_face_sets_edit_types[] = { "Creates a smooth as possible geometry patch from the Face Set minimizing changes in " "vertex tangents", }, - {0, nullptr, 0, nullptr, nullptr}, + /* + { + SCULPT_FACE_SET_EDIT_FAIR_CURVATURE, + "FAIR_CURVATURE", + 0, + "Fair Curvature", + "Creates a smooth as possible geometry patch from the Face Set minimizing changes in " + "surface curvature", + }, + */ + { + SCULPT_FACE_SET_EDIT_FILL_COMPONENT, + "FILL_COMPONENT", + 0, + "Fill Component", + "Expand a Face Set to fill all affected connected components", + }, + { + SCULPT_FACE_SET_EDIT_EXTRUDE, + "EXTRUDE", + 0, + "Extrude", + "Extrude a Face Set along the normals of the faces", + }, + { + SCULPT_FACE_SET_EDIT_FAIR_ALL_TANGENCY, + "ALL_TANGENCY", + 0, + "All tangency", + "Extrude a Face Set along the normals of the faces", + }, + {0, NULL, 0, NULL, NULL}, }; +static void sculpt_face_set_grow_bmesh(Object *ob, + SculptSession *ss, + const int *prev_face_sets, + const int active_face_set_id, + const bool modify_hidden) +{ + BMesh *bm = ss->bm; + BMIter iter; + BMFace *f; + Vector faces; + + if (ss->cd_faceset_offset < 0) { + return; + } + + BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { + if (BM_elem_flag_test(f, BM_ELEM_HIDDEN) && !modify_hidden) { + continue; + } + + int fset = abs(BM_ELEM_CD_GET_INT(f, ss->cd_faceset_offset)); + + if (fset == active_face_set_id) { + faces.append(f); + } + } + + for (BMFace *f : faces) { + BMLoop *l = f->l_first; + + do { + if (l->radial_next != l) { + BM_ELEM_CD_SET_INT(l->radial_next->f, ss->cd_faceset_offset, active_face_set_id); + } + l = l->next; + } while (l != f->l_first); + } +} + static void sculpt_face_set_grow(Object *ob, SculptSession *ss, const int *prev_face_sets, const int active_face_set_id, const bool modify_hidden) { - Mesh *mesh = BKE_mesh_from_object(ob); - const MPoly *polys = BKE_mesh_polys(mesh); - const MLoop *loops = BKE_mesh_loops(mesh); + if (ss && ss->bm) { + sculpt_face_set_grow_bmesh(ob, ss, prev_face_sets, active_face_set_id, modify_hidden); + return; + } + Mesh *mesh = BKE_mesh_from_object(ob); for (int p = 0; p < mesh->totpoly; p++) { if (!modify_hidden && prev_face_sets[p] <= 0) { continue; } - const MPoly *c_poly = &polys[p]; + const MPoly *c_poly = &mesh->mpoly[p]; for (int l = 0; l < c_poly->totloop; l++) { - const MLoop *c_loop = &loops[c_poly->loopstart + l]; - const MeshElemMap *vert_map = &ss->pmap[c_loop->v]; + const MLoop *c_loop = &mesh->mloop[c_poly->loopstart + l]; + const MeshElemMap *vert_map = &ss->pmap->pmap[c_loop->v]; for (int i = 0; i < vert_map->count; i++) { const int neighbor_face_index = vert_map->indices[i]; if (neighbor_face_index == p) { @@ -1126,24 +1717,111 @@ static void sculpt_face_set_grow(Object *ob, } } +static void sculpt_face_set_fill_component(Object *ob, + SculptSession *ss, + const int active_face_set_id, + const bool /*UNUSED(modify_hidden)*/) +{ + SCULPT_topology_islands_ensure(ob); + GSet *connected_components = BLI_gset_int_new("affected_components"); + + const int totvert = SCULPT_vertex_count_get(ss); + for (int i = 0; i < totvert; i++) { + PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); + + if (!SCULPT_vertex_has_face_set(ss, vertex, active_face_set_id)) { + continue; + } + const int vertex_connected_component = SCULPT_vertex_island_get(ss, vertex); + if (BLI_gset_haskey(connected_components, POINTER_FROM_INT(vertex_connected_component))) { + continue; + } + BLI_gset_add(connected_components, POINTER_FROM_INT(vertex_connected_component)); + } + + for (int i = 0; i < totvert; i++) { + PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); + + const int vertex_connected_component = SCULPT_vertex_island_get(ss, vertex); + if (!BLI_gset_haskey(connected_components, POINTER_FROM_INT(vertex_connected_component))) { + continue; + } + + SCULPT_vertex_face_set_set(ss, vertex, active_face_set_id); + } + + BLI_gset_free(connected_components, NULL); +} + +static void sculpt_face_set_shrink_bmesh(Object *ob, + SculptSession *ss, + const int *prev_face_sets, + const int active_face_set_id, + const bool modify_hidden) +{ + BMesh *bm = ss->bm; + BMIter iter; + BMFace *f; + Vector faces; + + if (ss->cd_faceset_offset < 0) { + return; + } + + BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { + if (BM_elem_flag_test(f, BM_ELEM_HIDDEN) && !modify_hidden) { + continue; + } + + int fset = abs(BM_ELEM_CD_GET_INT(f, ss->cd_faceset_offset)); + + if (fset == active_face_set_id) { + faces.append(f); + } + } + + for (BMFace *f : faces) { + BMLoop *l = f->l_first; + + do { + if (!modify_hidden && BM_elem_flag_test(l->radial_next->f, BM_ELEM_HIDDEN)) { + l = l->next; + continue; + } + + if (l->radial_next != l && + abs(BM_ELEM_CD_GET_INT(l->radial_next->f, ss->cd_faceset_offset)) != + abs(active_face_set_id)) { + int fset = BM_ELEM_CD_GET_INT(l->radial_next->f, ss->cd_faceset_offset); + BM_ELEM_CD_SET_INT(f, ss->cd_faceset_offset, fset); + break; + } + l = l->next; + } while (l != f->l_first); + } +} + static void sculpt_face_set_shrink(Object *ob, SculptSession *ss, const int *prev_face_sets, const int active_face_set_id, const bool modify_hidden) { + if (ss && ss->bm) { + sculpt_face_set_shrink_bmesh(ob, ss, prev_face_sets, active_face_set_id, modify_hidden); + return; + } + Mesh *mesh = BKE_mesh_from_object(ob); - const MPoly *polys = BKE_mesh_polys(mesh); - const MLoop *loops = BKE_mesh_loops(mesh); for (int p = 0; p < mesh->totpoly; p++) { if (!modify_hidden && prev_face_sets[p] <= 0) { continue; } if (abs(prev_face_sets[p]) == active_face_set_id) { - const MPoly *c_poly = &polys[p]; + const MPoly *c_poly = &mesh->mpoly[p]; for (int l = 0; l < c_poly->totloop; l++) { - const MLoop *c_loop = &loops[c_poly->loopstart + l]; - const MeshElemMap *vert_map = &ss->pmap[c_loop->v]; + const MLoop *c_loop = &mesh->mloop[c_poly->loopstart + l]; + const MeshElemMap *vert_map = &ss->pmap->pmap[c_loop->v]; for (int i = 0; i < vert_map->count; i++) { const int neighbor_face_index = vert_map->indices[i]; if (neighbor_face_index == p) { @@ -1158,25 +1836,28 @@ static void sculpt_face_set_shrink(Object *ob, } } -static bool check_single_face_set(SculptSession *ss, - const int *face_sets, - const bool check_visible_only) +static bool check_single_face_set(SculptSession *ss, const bool check_visible_only) { - if (face_sets == nullptr) { + if (!ss->totfaces) { return true; } + int first_face_set = SCULPT_FACE_SET_NONE; + if (check_visible_only) { for (int f = 0; f < ss->totfaces; f++) { - if (ss->hide_poly && ss->hide_poly[f]) { - continue; + PBVHFaceRef fref = BKE_pbvh_index_to_face(ss->pbvh, f); + int fset = SCULPT_face_set_get(ss, fref); + + if (fset > 0) { + first_face_set = fset; + break; } - first_face_set = face_sets[f]; - break; } } else { - first_face_set = face_sets[0]; + PBVHFaceRef fref = BKE_pbvh_index_to_face(ss->pbvh, 0); + first_face_set = abs(SCULPT_face_set_get(ss, fref)); } if (first_face_set == SCULPT_FACE_SET_NONE) { @@ -1184,10 +1865,12 @@ static bool check_single_face_set(SculptSession *ss, } for (int f = 0; f < ss->totfaces; f++) { - if (check_visible_only && ss->hide_poly && ss->hide_poly[f]) { - continue; - } - if (face_sets[f] != first_face_set) { + PBVHFaceRef fref = BKE_pbvh_index_to_face(ss->pbvh, f); + + int fset = SCULPT_face_set_get(ss, fref); + fset = check_visible_only ? abs(fset) : fset; + + if (fset != first_face_set) { return false; } } @@ -1200,63 +1883,95 @@ static void sculpt_face_set_delete_geometry(Object *ob, const bool modify_hidden) { - Mesh *mesh = static_cast(ob->data); + Mesh *mesh = (Mesh *)ob->data; const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(mesh); - BMeshCreateParams create_params{}; - create_params.use_toolflags = true; - BMesh *bm = BM_mesh_create(&allocsize, &create_params); - BMeshFromMeshParams convert_params{}; - convert_params.calc_vert_normal = true; - convert_params.calc_face_normal = true; - BM_mesh_bm_from_me(bm, mesh, &convert_params); + if (ss->bm) { + Vector faces; - BM_mesh_elem_table_init(bm, BM_FACE); - BM_mesh_elem_table_ensure(bm, BM_FACE); - BM_mesh_elem_hflag_disable_all(bm, BM_VERT | BM_EDGE | BM_FACE, BM_ELEM_TAG, false); - BMIter iter; - BMFace *f; - BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { - const int face_index = BM_elem_index_get(f); - if (!modify_hidden && ss->hide_poly && ss->hide_poly[face_index]) { - continue; + BMIter iter; + BMFace *f; + + BM_ITER_MESH (f, &iter, ss->bm, BM_FACES_OF_MESH) { + const int face_set_id = modify_hidden ? abs(BM_ELEM_CD_GET_INT(f, ss->cd_faceset_offset)) : + BM_ELEM_CD_GET_INT(f, ss->cd_faceset_offset); + if (face_set_id == active_face_set_id) { + faces.append(f); + } + } + + for (BMFace *f : faces) { + BKE_pbvh_bmesh_remove_face(ss->pbvh, f, true); } - BM_elem_flag_set(f, BM_ELEM_TAG, ss->face_sets[face_index] == active_face_set_id); } - BM_mesh_delete_hflag_context(bm, BM_ELEM_TAG, DEL_FACES); - BM_mesh_elem_hflag_disable_all(bm, BM_VERT | BM_EDGE | BM_FACE, BM_ELEM_TAG, false); + else { + BMeshCreateParams params = {0}; + params.use_toolflags = true; - BMeshToMeshParams bmesh_to_mesh_params{}; - bmesh_to_mesh_params.calc_object_remap = false; - BM_mesh_bm_to_me(nullptr, bm, mesh, &bmesh_to_mesh_params); + BMesh *bm = BM_mesh_create(&allocsize, ¶ms); - BM_mesh_free(bm); + BMeshFromMeshParams cparams = {0}; + cparams.calc_face_normal = true; + cparams.active_shapekey = ob->shapenr; + cparams.use_shapekey = true; + cparams.create_shapekey_layers = true; + + BM_mesh_bm_from_me(ob, bm, mesh, &cparams); + + BM_mesh_elem_table_init(bm, BM_FACE); + BM_mesh_elem_table_ensure(bm, BM_FACE); + BM_mesh_elem_hflag_disable_all(bm, BM_VERT | BM_EDGE | BM_FACE, BM_ELEM_TAG, false); + BMIter iter; + BMFace *f; + BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { + const int face_index = BM_elem_index_get(f); + const int face_set_id = modify_hidden ? abs(ss->face_sets[face_index]) : + ss->face_sets[face_index]; + BM_elem_flag_set(f, BM_ELEM_TAG, face_set_id == active_face_set_id); + } + BM_mesh_delete_hflag_context(bm, BM_ELEM_TAG, DEL_FACES); + BM_mesh_elem_hflag_disable_all(bm, BM_VERT | BM_EDGE | BM_FACE, BM_ELEM_TAG, false); + + BMeshToMeshParams tparams = {0}; + + BM_mesh_bm_to_me(NULL, ob, bm, (Mesh *)ob->data, &tparams); + + BM_mesh_free(bm); + } } static void sculpt_face_set_edit_fair_face_set(Object *ob, const int active_face_set_id, - const eMeshFairingDepth fair_order) + const int fair_order) { SculptSession *ss = ob->sculpt; + const int totvert = SCULPT_vertex_count_get(ss); - Mesh *mesh = static_cast(ob->data); - bool *fair_verts = static_cast( - MEM_malloc_arrayN(totvert, sizeof(bool), "fair vertices")); + Mesh *mesh = (Mesh *)ob->data; + bool *fair_vertices = (bool *)MEM_malloc_arrayN(totvert, sizeof(bool), "fair vertices"); + SCULPT_vertex_random_access_ensure(ss); SCULPT_boundary_info_ensure(ob); for (int i = 0; i < totvert; i++) { - PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); + PBVHVertRef vref = BKE_pbvh_index_to_vertex(ss->pbvh, i); - fair_verts[i] = !SCULPT_vertex_is_boundary(ss, vertex) && - SCULPT_vertex_has_face_set(ss, vertex, active_face_set_id) && - SCULPT_vertex_has_unique_face_set(ss, vertex); + fair_vertices[i] = !SCULPT_vertex_is_boundary(ss, vref, SCULPT_BOUNDARY_MESH) && + SCULPT_vertex_has_face_set(ss, vref, active_face_set_id) && + SCULPT_vertex_has_unique_face_set(ss, vref); } - float(*positions)[3] = SCULPT_mesh_deformed_positions_get(ss); - BKE_mesh_prefair_and_fair_verts(mesh, positions, fair_verts, fair_order); - MEM_freeN(fair_verts); + if (ss->bm) { + BKE_bmesh_prefair_and_fair_verts(ss->bm, fair_vertices, (eMeshFairingDepth)fair_order); + } + else { + float(*vert_positions)[3] = SCULPT_mesh_deformed_positions_get(ss); + BKE_mesh_prefair_and_fair_verts( + mesh, vert_positions, fair_vertices, (eMeshFairingDepth)fair_order); + + MEM_freeN(fair_vertices); + } } static void sculpt_face_set_apply_edit(Object *ob, @@ -1268,17 +1983,21 @@ static void sculpt_face_set_apply_edit(Object *ob, switch (mode) { case SCULPT_FACE_SET_EDIT_GROW: { - int *prev_face_sets = static_cast(MEM_dupallocN(ss->face_sets)); + int *prev_face_sets = ss->face_sets ? (int *)MEM_dupallocN(ss->face_sets) : NULL; sculpt_face_set_grow(ob, ss, prev_face_sets, active_face_set_id, modify_hidden); MEM_SAFE_FREE(prev_face_sets); break; } case SCULPT_FACE_SET_EDIT_SHRINK: { - int *prev_face_sets = static_cast(MEM_dupallocN(ss->face_sets)); + int *prev_face_sets = ss->face_sets ? (int *)MEM_dupallocN(ss->face_sets) : NULL; sculpt_face_set_shrink(ob, ss, prev_face_sets, active_face_set_id, modify_hidden); MEM_SAFE_FREE(prev_face_sets); break; } + case SCULPT_FACE_SET_EDIT_FILL_COMPONENT: { + sculpt_face_set_fill_component(ob, ss, active_face_set_id, modify_hidden); + break; + } case SCULPT_FACE_SET_EDIT_DELETE_GEOMETRY: sculpt_face_set_delete_geometry(ob, ss, active_face_set_id, modify_hidden); break; @@ -1288,6 +2007,23 @@ static void sculpt_face_set_apply_edit(Object *ob, case SCULPT_FACE_SET_EDIT_FAIR_TANGENCY: sculpt_face_set_edit_fair_face_set(ob, active_face_set_id, MESH_FAIRING_DEPTH_TANGENCY); break; + case SCULPT_FACE_SET_EDIT_FAIR_ALL_TANGENCY: { + GSet *face_sets_ids = BLI_gset_int_new("ids"); + for (int i = 0; i < ss->totfaces; i++) { + BLI_gset_add(face_sets_ids, POINTER_FROM_INT(ss->face_sets[i])); + } + + GSetIterator gs_iter; + GSET_ITER (gs_iter, face_sets_ids) { + const int face_set_id = POINTER_AS_INT(BLI_gsetIterator_getKey(&gs_iter)); + sculpt_face_set_edit_fair_face_set(ob, face_set_id, MESH_FAIRING_DEPTH_TANGENCY); + } + + BLI_gset_free(face_sets_ids, NULL); + } break; + case SCULPT_FACE_SET_EDIT_FAIR_CURVATURE: + sculpt_face_set_edit_fair_face_set(ob, active_face_set_id, MESH_FAIRING_DEPTH_CURVATURE); + break; } } @@ -1295,20 +2031,18 @@ static bool sculpt_face_set_edit_is_operation_valid(SculptSession *ss, const eSculptFaceSetEditMode mode, const bool modify_hidden) { - if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) { - /* Dyntopo is not supported. */ - return false; - } + SCULPT_vertex_random_access_ensure(ss); + SCULPT_face_random_access_ensure(ss); - if (mode == SCULPT_FACE_SET_EDIT_DELETE_GEOMETRY) { + if (ELEM(mode, SCULPT_FACE_SET_EDIT_DELETE_GEOMETRY, SCULPT_FACE_SET_EDIT_EXTRUDE)) { if (BKE_pbvh_type(ss->pbvh) == PBVH_GRIDS) { - /* Modification of base mesh geometry requires special remapping of multi-resolution - * displacement, which does not happen here. - * Disable delete operation. It can be supported in the future by doing similar displacement - * data remapping as what happens in the mesh edit mode. */ + /* Modification of base mesh geometry requires special remapping of multires displacement, + * which does not happen here. + * Disable delete operation. It can be supported in the future by doing similar + * displacement data remapping as what happens in the mesh edit mode. */ return false; } - if (check_single_face_set(ss, ss->face_sets, !modify_hidden)) { + if (check_single_face_set(ss, !modify_hidden)) { /* Cancel the operator if the mesh only contains one Face Set to avoid deleting the * entire object. */ return false; @@ -1334,32 +2068,23 @@ static void sculpt_face_set_edit_modify_geometry(bContext *C, const bool modify_hidden, wmOperator *op) { - Mesh *mesh = static_cast(ob->data); ED_sculpt_undo_geometry_begin(ob, op); sculpt_face_set_apply_edit(ob, abs(active_face_set), mode, modify_hidden); ED_sculpt_undo_geometry_end(ob); - BKE_mesh_batch_cache_dirty_tag(mesh, BKE_MESH_BATCH_DIRTY_ALL); + BKE_mesh_batch_cache_dirty_tag((Mesh *)ob->data, BKE_MESH_BATCH_DIRTY_ALL); DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); - WM_event_add_notifier(C, NC_GEOM | ND_DATA, mesh); + WM_event_add_notifier(C, NC_GEOM | ND_DATA, ob->data); } static void face_set_edit_do_post_visibility_updates(Object *ob, PBVHNode **nodes, int totnode) { SculptSession *ss = ob->sculpt; PBVH *pbvh = ss->pbvh; - Mesh *mesh = static_cast(ob->data); - - /* Sync face sets visibility and vertex visibility as now all Face Sets are visible. */ - SCULPT_visibility_sync_all_from_faces(ob); - - for (int i = 0; i < totnode; i++) { - BKE_pbvh_node_mark_update_visibility(nodes[i]); - } BKE_pbvh_update_vertex_data(ss->pbvh, PBVH_UpdateVisibility); if (BKE_pbvh_type(pbvh) == PBVH_FACES) { - BKE_mesh_flush_hidden_from_verts(mesh); + BKE_mesh_flush_hidden_from_verts((Mesh *)ob->data); } } @@ -1372,7 +2097,7 @@ static void sculpt_face_set_edit_modify_face_sets(Object *ob, PBVH *pbvh = ob->sculpt->pbvh; PBVHNode **nodes; int totnode; - BKE_pbvh_search_gather(pbvh, nullptr, nullptr, &nodes, &totnode); + BKE_pbvh_search_gather(pbvh, NULL, NULL, &nodes, &totnode); if (!nodes) { return; @@ -1398,7 +2123,7 @@ static void sculpt_face_set_edit_modify_coordinates(bContext *C, PBVH *pbvh = ss->pbvh; PBVHNode **nodes; int totnode; - BKE_pbvh_search_gather(pbvh, nullptr, nullptr, &nodes, &totnode); + BKE_pbvh_search_gather(pbvh, NULL, NULL, &nodes, &totnode); SCULPT_undo_push_begin(ob, op); for (int i = 0; i < totnode; i++) { BKE_pbvh_node_mark_update(nodes[i]); @@ -1415,44 +2140,838 @@ static void sculpt_face_set_edit_modify_coordinates(bContext *C, MEM_freeN(nodes); } +typedef struct FaceSetExtrudeCD { + int active_face_set; + float cursor_location[3]; + float (*orig_co)[3]; + float init_mval[2]; + float (*orig_no)[3]; + int *verts; + int totvert; + float start_no[3]; +} FaceSetExtrudeCD; + +static void sculpt_bm_mesh_elem_hflag_disable_all(BMesh *bm, char htype, char hflag) +{ + static int iters[3] = {BM_VERTS_OF_MESH, BM_EDGES_OF_MESH, BM_FACES_OF_MESH}; + static int types[3] = {BM_VERT, BM_EDGE, BM_FACE}; + + for (int i = 0; i < 3; i++) { + int type = types[i]; + + if (!(htype & type)) { + continue; + } + + BMIter iter; + BMElem *elem; + + BM_ITER_MESH (elem, &iter, bm, iters[i]) { + // do not call bm selection api + // BM_elem_select_set(bm, elem, false); + + elem->head.hflag &= ~hflag; + } + } +} + +static void sculpt_face_set_extrude_id(Object *ob, + bool no_islands, + SculptSession *ss, + const int active_face_set_id, + FaceSetExtrudeCD *fsecd) +{ + + Mesh *mesh = (Mesh *)ob->data; + int next_face_set_id = SCULPT_face_set_next_available_get(ss) + 1; + + SculptFaceSetIsland *island = NULL; + + if (no_islands && ss->active_face.i != PBVH_REF_NONE) { + island = SCULPT_face_set_island_get(ss, ss->active_face, active_face_set_id); + + /* convert PBVHFaceRef list into simple integers, only need to do for pbvh_bmesh*/ + if (island && ss->bm) { + SCULPT_face_random_access_ensure(ss); + + for (int i = 0; i < island->totface; i++) { + BMFace *f = (BMFace *)island->faces[i].i; + island->faces[i].i = BM_elem_index_get(f); + } + } + } + + no_islands = no_islands && island != NULL; + + BMesh *bm = sculpt_faceset_bm_begin(ob, ss, mesh); + if (ss->bm) { + BKE_pbvh_bmesh_set_toolflags(ss->pbvh, true); + BKE_sculptsession_update_attr_refs(ob); + } + + BM_mesh_elem_table_init(bm, BM_FACE); + BM_mesh_elem_table_ensure(bm, BM_FACE); + + sculpt_bm_mesh_elem_hflag_disable_all( + bm, BM_ALL_NOLOOP, BM_ELEM_SELECT | BM_ELEM_TAG_ALT | BM_ELEM_TAG); + + BMIter iter; + BMFace *f; + + if (ss->bm && ss->pbvh) { + BKE_pbvh_set_bm_log(ss->pbvh, ss->bm_log); + } + + BM_mesh_select_mode_set(bm, SCE_SELECT_FACE); + + int mupdateflag = SCULPTVERT_NEED_DISK_SORT | SCULPTVERT_NEED_TRIANGULATE | + SCULPTVERT_NEED_VALENCE; + + Vector retvs, vs; + Vector es; + + int cd_faceset_offset = CustomData_get_offset_named( + &bm->pdata, CD_PROP_INT32, ".sculpt_face_sets"); + + const int tag1 = BM_ELEM_SELECT; + const int tag2 = BM_ELEM_TAG_ALT; + const int tag3 = BM_ELEM_TAG; + + int totface = no_islands ? island->totface : bm->totface; + for (int i = 0; i < totface; i++) { + BMFace *f = no_islands ? bm->ftable[island->faces[i].i] : bm->ftable[i]; + + const int face_set_id = BM_ELEM_CD_GET_INT(f, cd_faceset_offset); + + if (face_set_id == active_face_set_id) { + BM_elem_select_set(bm, (BMElem *)f, true); + + if (ss->bm) { + BMLoop *l = f->l_first; + + do { + if (!(BM_elem_flag_test(l->e, tag2))) { + BM_elem_flag_enable(l->e, tag2); + es.append(l->e); + } + + if (!(BM_elem_flag_test(l->v, tag2))) { + BM_elem_flag_enable(l->v, tag2); + vs.append(l->v); + } + + } while ((l = l->next) != f->l_first); + + if (ss->bm) { + BKE_pbvh_bmesh_remove_face(ss->pbvh, f, true); + } + } + } + else { + BM_elem_select_set(bm, (BMElem *)f, false); + } + + BM_elem_flag_set(f, BM_ELEM_TAG, face_set_id == active_face_set_id); + } + + Vector borderfs; + Vector borderes; + Vector bordervs; + + if (ss->bm) { + for (BMEdge *e : es) { + BMLoop *l = e->l; + + bool remove = true; + do { + if (!(BM_elem_flag_test(l->f, tag1))) { + // remove = false; + borderes.append(e); + break; + } + } while ((l = l->radial_next) != e->l); + + if (remove) { + if (!BM_elem_flag_test(e->v1, tag3)) { + BM_log_vert_removed(ss->bm, ss->bm_log, e->v1); + // BKE_pbvh_bmesh_remove_vertex(ss->pbvh, e->v1, true); + BM_elem_flag_enable(e->v1, tag3); + } + + if (!BM_elem_flag_test(e->v2, tag3)) { + BM_log_vert_removed(ss->bm, ss->bm_log, e->v2); + // BKE_pbvh_bmesh_remove_vertex(ss->pbvh, e->v2, true); + BM_elem_flag_enable(e->v2, tag3); + } + + BKE_pbvh_bmesh_remove_edge(ss->pbvh, e, true); + e->head.hflag |= tag1; + } + } + + for (BMVert *v : vs) { + BMEdge *e = v->e; + bool remove = true; + + do { + if (!BM_elem_flag_test(e, tag1)) { + // remove = false; + bordervs.append(v); + break; + } + } while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e); + + if (BM_elem_flag_test(v, tag3)) { + continue; + } + + BM_elem_flag_enable(v, tag3); + + if (remove) { + // BKE_pbvh_bmesh_remove_vertex(ss->pbvh, v, true); + BM_log_vert_removed(ss->bm, ss->bm_log, v); + } + } + } + + for (BMVert *v : bordervs) { + BMFace *f2; + BMIter iter; + + BM_ITER_ELEM (f2, &iter, v, BM_FACES_OF_VERT) { + if (BM_elem_flag_test(f2, tag1) || BM_elem_flag_test(f2, tag2)) { + continue; + } + + if (ss->bm) { + BKE_pbvh_bmesh_remove_face(ss->pbvh, f2, true); + } + + BM_elem_flag_enable(f2, tag2); + borderfs.append(f2); + } + } + + BM_mesh_select_flush(bm); + BM_mesh_select_mode_flush(bm); + + BMOperator extop; + BMO_op_init(bm, &extop, BMO_FLAG_DEFAULTS, "extrude_face_region"); + BMO_slot_bool_set(extop.slots_in, "use_normal_from_adjacent", true); + BMO_slot_bool_set(extop.slots_in, "use_dissolve_ortho_edges", true); + BMO_slot_bool_set(extop.slots_in, "use_select_history", true); + char htype = BM_ALL_NOLOOP; + htype &= ~(BM_VERT | BM_EDGE); + if (htype & BM_FACE) { + htype |= BM_EDGE; + } + + BMO_slot_buffer_from_enabled_hflag(bm, &extop, extop.slots_in, "geom", htype, BM_ELEM_SELECT); + + BMO_op_exec(bm, &extop); + sculpt_bm_mesh_elem_hflag_disable_all( + bm, BM_ALL_NOLOOP, BM_ELEM_SELECT | BM_ELEM_TAG_ALT | BM_ELEM_TAG); + + int cd_sculpt_vert = CustomData_get_offset(&bm->vdata, CD_DYNTOPO_VERT); + cd_faceset_offset = CustomData_get_offset_named( + &bm->pdata, CD_PROP_INT32, ".sculpt_face_sets"); // recalc in case bmop changed it + + const int cd_boundary_flag = CustomData_get_offset_named( + &bm->vdata, CD_PROP_INT32, SCULPT_ATTRIBUTE_NAME(boundary_flags)); + + BMOIter siter; + BMElem *ele; + + if (ss->bm) { /* handle some pbvh stuff */ + for (int step = 0; step < 2; step++) { + BMO_ITER (ele, &siter, extop.slots_out, step ? "side_geom.out" : "geom.out", BM_ALL_NOLOOP) { + if (ele->head.htype == BM_VERT) { + BM_ELEM_CD_SET_INT(ele, ss->cd_vert_node_offset, DYNTOPO_NODE_NONE); + } + else if (ele->head.htype == BM_FACE) { + BM_ELEM_CD_SET_INT(ele, ss->cd_face_node_offset, DYNTOPO_NODE_NONE); + } + } + } + + /*push a log subentry*/ + BM_log_entry_add_ex(bm, ss->bm_log, true); + } + + for (int step = 0; step < 2; step++) { + BMO_ITER (ele, &siter, extop.slots_out, step ? "side_geom.out" : "geom.out", BM_ALL_NOLOOP) { + if (step == 0 && ele->head.htype != BM_VERT) { + BM_elem_flag_set(ele, BM_ELEM_TAG, true); + } + + if (step == 1 && ele->head.htype == BM_FACE) { + BM_ELEM_CD_SET_INT(ele, cd_faceset_offset, next_face_set_id); + } + + if (BM_elem_flag_test(ele, tag1)) { + continue; + } + + BM_elem_flag_enable(ele, tag1); + + switch (ele->head.htype) { + case BM_VERT: + if (ss->bm) { + BM_log_vert_added(ss->bm, ss->bm_log, (BMVert *)ele); + } + + if (step == 0) { + retvs.append((BMVert *)ele); + } + + break; + case BM_EDGE: { + BMEdge *e = (BMEdge *)ele; + + if (ss->bm) { + BM_log_edge_added(ss->bm, ss->bm_log, e); + + if (!BM_elem_flag_test(e->v1, tag1)) { + BM_elem_flag_enable(e->v1, tag1); + BM_log_vert_added(ss->bm, ss->bm_log, e->v1); + } + + if (!BM_elem_flag_test(e->v2, tag1)) { + BM_elem_flag_enable(e->v2, tag1); + BM_log_vert_added(ss->bm, ss->bm_log, e->v2); + } + + if (1 || step == 1) { + BMLoop *l = e->l; + + if (l) { + do { + if (!BM_elem_flag_test(l->f, tag1)) { + BKE_pbvh_bmesh_add_face(ss->pbvh, l->f, false, false); + BM_log_face_added(ss->bm, ss->bm_log, l->f); + } + + BM_elem_flag_enable(l->f, tag1); + } while ((l = l->radial_next) != e->l); + } + } + } + break; + } + case BM_FACE: { + BMFace *f = (BMFace *)ele; + + if (cd_sculpt_vert != -1) { + BMLoop *l = f->l_first; + do { + *(int *)BM_ELEM_CD_GET_VOID_P(l->v, + cd_boundary_flag) |= SCULPT_BOUNDARY_NEEDS_UPDATE; + MSculptVert *mv = BKE_PBVH_SCULPTVERT(cd_sculpt_vert, l->v); + MV_ADD_FLAG(mv, mupdateflag); + } while ((l = l->next) != f->l_first); + } + + if (ss->bm) { + BKE_pbvh_bmesh_add_face(ss->pbvh, f, true, false); + } + + break; + } + default: + break; + } + } + } + +#if 0 + for (int i=0; ibm) { + BKE_pbvh_bmesh_add_face(ss->pbvh, f, false, false); + BM_log_face_added(ss->bm, ss->bm_log, f); + } + } +#endif + + BMO_op_finish(bm, &extop); + + for (BMFace *f : borderfs) { + if (BM_elem_is_free((BMElem *)f, BM_FACE)) { + continue; + } + + if (cd_sculpt_vert >= 0) { + BMLoop *l = f->l_first; + do { + *(int *)BM_ELEM_CD_GET_VOID_P(l->v, cd_boundary_flag) |= SCULPT_BOUNDARY_NEEDS_UPDATE; + + MSculptVert *mv = BKE_PBVH_SCULPTVERT(cd_sculpt_vert, l->v); + MV_ADD_FLAG(mv, mupdateflag); + } while ((l = l->next) != f->l_first); + } + + if (ss->bm && !BM_elem_flag_test(f, tag1)) { + BKE_pbvh_bmesh_add_face(ss->pbvh, f, true, false); + } + + BM_elem_flag_enable(f, tag1); + } + + for (BMVert *v : retvs) { + BM_elem_flag_enable(v, BM_ELEM_TAG); + } + + /* Set the new Face Set ID for the extrusion. */ + const int cd_face_sets_offset = CustomData_get_offset_named( + &bm->pdata, CD_PROP_INT32, ".sculpt_face_sets"); + + BM_mesh_elem_table_ensure(bm, BM_FACE); + BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { + const int face_set_id = BM_ELEM_CD_GET_INT(f, cd_face_sets_offset); + if (abs(face_set_id) == active_face_set_id) { + continue; + } + + const int cd_sculpt_vert = CustomData_get_offset(&bm->vdata, CD_DYNTOPO_VERT); + + BMLoop *l = f->l_first; + + do { + if (cd_boundary_flag != -1) { + *(int *)BM_ELEM_CD_GET_VOID_P(l->v, cd_boundary_flag) |= SCULPT_BOUNDARY_NEEDS_UPDATE; + } + + if (cd_sculpt_vert != -1) { + MSculptVert *mv = (MSculptVert *)BM_ELEM_CD_GET_VOID_P(l->v, cd_sculpt_vert); + + MV_ADD_FLAG(mv, mupdateflag); + } + } while ((l = l->next) != f->l_first); + } + + BM_mesh_elem_hflag_enable_all(bm, BM_FACE, BM_ELEM_TAG, false); + + /* + BMO_op_callf(bm, + (BMO_FLAG_DEFAULTS & ~BMO_FLAG_RESPECT_HIDE), + "recalc_face_normals faces=%hf", + BM_ELEM_TAG); + */ + + BM_mesh_elem_hflag_disable_all(bm, BM_VERT | BM_EDGE | BM_FACE, BM_ELEM_TAG, false); + BM_mesh_elem_hflag_disable_all(bm, BM_VERT | BM_EDGE | BM_FACE, BM_ELEM_SELECT, false); + + fsecd->verts = (int *)MEM_malloc_arrayN(retvs.size(), sizeof(int), "face set extrude verts"); + fsecd->totvert = retvs.size(); + + fsecd->orig_co = static_cast( + MEM_malloc_arrayN(retvs.size(), sizeof(float) * 3, "face set extrude verts")); + fsecd->orig_no = static_cast( + MEM_malloc_arrayN(retvs.size(), sizeof(float) * 3, "face set extrude verts")); + + BM_mesh_elem_index_ensure(bm, BM_VERT | BM_EDGE | BM_FACE); + + for (int i = 0; i < retvs.size(); i++) { + BMVert *v = retvs[i]; + + fsecd->verts[i] = v->head.index; + copy_v3_v3(fsecd->orig_co[i], v->co); + + BMIter iter; + BMFace *f; + + float no[3] = {0.0f, 0.0f, 0.0f}; + + BM_ITER_ELEM (f, &iter, v, BM_FACES_OF_VERT) { + int fset = BM_ELEM_CD_GET_INT(f, cd_faceset_offset); + if (fset == active_face_set_id) { + add_v3_v3(no, f->no); + } + } + + normalize_v3(no); + copy_v3_v3(fsecd->orig_no[i], no); + } + + if (island) { + SCULPT_face_set_island_free(island); + } + + if (!ss->bm) { + BMeshToMeshParams params = {0}; + + BM_mesh_bm_to_me(NULL, NULL, bm, (Mesh *)ob->data, ¶ms); + } + + sculpt_faceset_bm_end(ss, bm); + + if (ss->bm) { + // slow! BKE_pbvh_bmesh_set_toolflags(ss->pbvh, false); + BKE_sculptsession_update_attr_refs(ob); + } +} + +static void island_stack_bmesh_do( + SculptSession *ss, int fset, PBVHFaceRef face, Vector &faces, BLI_bitmap *visit) +{ + BMFace *f = (BMFace *)face.i; + + BMLoop *l = f->l_first; + do { + BMLoop *l2 = l; + + do { + int index = BM_elem_index_get(l2->f); + + bool ok = !BLI_BITMAP_TEST(visit, index); + ok = ok && abs(BM_ELEM_CD_GET_INT(l2->f, ss->cd_faceset_offset)) == fset; + + if (ok) { + BLI_BITMAP_SET(visit, index, true); + + faces.append(BKE_pbvh_make_fref((intptr_t)l2->f)); + } + } while ((l2 = l2->radial_next) != l); + } while ((l = l->next) != f->l_first); +} + +static void island_stack_mesh_do( + SculptSession *ss, int fset, PBVHFaceRef face, Vector &faces, BLI_bitmap *visit) +{ + const MPoly *mp = ss->mpoly + face.i; + const MLoop *ml = ss->mloop + mp->loopstart; + + for (int i = 0; i < mp->totloop; i++, ml++) { + MeshElemMap *ep = ss->epmap + ml->e; + + for (int j = 0; j < ep->count; j++) { + int f2 = ep->indices[j]; + + if (abs(ss->face_sets[f2]) == fset && !BLI_BITMAP_TEST(visit, f2)) { + BLI_BITMAP_SET(visit, f2, true); + PBVHFaceRef face2 = {f2}; + + faces.append(face2); + } + } + } +} + +SculptFaceSetIslands *SCULPT_face_set_islands_get(SculptSession *ss, int fset) +{ + if (BKE_pbvh_type(ss->pbvh) != PBVH_BMESH && !ss->epmap) { + BKE_mesh_edge_poly_map_create(&ss->epmap, + &ss->epmap_mem, + ss->medge, + ss->totedges, + ss->mpoly, + ss->totfaces, + ss->mloop, + ss->totloops); + } + + SculptFaceSetIslands *ret = (SculptFaceSetIslands *)MEM_callocN(sizeof(*ret), "fset islands"); + Vector islands; + + int totface = ss->totfaces; + BLI_bitmap *visit = BLI_BITMAP_NEW(totface, __func__); + Vector stack; + + SCULPT_face_random_access_ensure(ss); + + for (int i = 0; i < totface; i++) { + PBVHFaceRef face = BKE_pbvh_index_to_face(ss->pbvh, i); + + if (abs(SCULPT_face_set_get(ss, face)) != fset) { + continue; + } + + if (BLI_BITMAP_TEST(visit, i)) { + continue; + } + + BLI_BITMAP_SET(visit, i, true); + + stack.resize(0); + stack.append(face); + + Vector faces; + + while (stack.size() > 0) { + // can't use BLI_array_pop since it doesn't work with popping structures + PBVHFaceRef face2 = stack.pop_last(); + faces.append(face2); + + int tot = stack.size(); + + if (ss->bm) { + island_stack_bmesh_do(ss, fset, face2, stack, visit); + } + else { + island_stack_mesh_do(ss, fset, face2, stack, visit); + } + } + + PBVHFaceRef *cfaces = (PBVHFaceRef *)MEM_malloc_arrayN( + faces.size(), sizeof(PBVHFaceRef), __func__); + memcpy((void *)cfaces, (void *)faces.data(), sizeof(PBVHFaceRef) * faces.size()); + + SculptFaceSetIsland island; + + island.faces = cfaces; + island.totface = faces.size(); + + islands.append(island); + } + + SculptFaceSetIsland *cislands = (SculptFaceSetIsland *)MEM_malloc_arrayN( + islands.size(), sizeof(SculptFaceSetIsland *), __func__); + memcpy((void *)cislands, (void *)islands.data(), sizeof(SculptFaceSetIsland *) * islands.size()); + + ret->islands = cislands; + ret->totisland = islands.size(); + + MEM_SAFE_FREE(visit); + return ret; +} + +void SCULPT_face_set_islands_free(SculptSession *ss, SculptFaceSetIslands *islands) +{ + for (int i = 0; i < islands->totisland; i++) { + MEM_SAFE_FREE(islands->islands[i].faces); + } + + MEM_SAFE_FREE(islands->islands); + MEM_SAFE_FREE(islands); +} + +SculptFaceSetIsland *SCULPT_face_set_island_get(SculptSession *ss, PBVHFaceRef face, int fset) +{ + SculptFaceSetIslands *islands = SCULPT_face_set_islands_get(ss, fset); + + for (int i = 0; i < islands->totisland; i++) { + SculptFaceSetIsland *island = islands->islands + i; + + for (int j = 0; j < island->totface; j++) { + if (island->faces[j].i == face.i) { + SculptFaceSetIsland *ret = (SculptFaceSetIsland *)MEM_callocN(sizeof(SculptFaceSetIsland), + "SculptFaceSetIsland"); + + *ret = *island; + + // prevent faces from freeing + island->faces = NULL; + + SCULPT_face_set_islands_free(ss, islands); + return ret; + } + } + } + + SCULPT_face_set_islands_free(ss, islands); + return NULL; +} + +void SCULPT_face_set_island_free(SculptFaceSetIsland *island) +{ + if (island) { + MEM_SAFE_FREE(island->faces); + MEM_freeN(island); + } +} + +static int sculpt_face_set_edit_modal(bContext *C, wmOperator *op, const wmEvent *event) +{ + Object *ob = CTX_data_active_object(C); + SculptSession *ss = ob->sculpt; + const int mode = RNA_enum_get(op->ptr, "mode"); + Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); + BKE_sculpt_update_object_for_edit(depsgraph, ob, true, false, false); + + if (mode != SCULPT_FACE_SET_EDIT_EXTRUDE) { + return OPERATOR_FINISHED; + } + + if (event->type == LEFTMOUSE && event->val == KM_RELEASE) { + FaceSetExtrudeCD *fsecd = (FaceSetExtrudeCD *)op->customdata; + MEM_SAFE_FREE(fsecd->orig_co); + MEM_SAFE_FREE(fsecd->orig_no); + MEM_SAFE_FREE(fsecd->verts); + MEM_SAFE_FREE(op->customdata); + + if (ss->bm) { + SCULPT_undo_push_end(ob); + } + else { + ED_sculpt_undo_geometry_end(ob); + } + + SCULPT_flush_update_done(C, ob, SCULPT_UPDATE_COORDS); + return OPERATOR_FINISHED; + } + + FaceSetExtrudeCD *fsecd = (FaceSetExtrudeCD *)op->customdata; + ViewContext vc; + ED_view3d_viewcontext_init(C, &vc, depsgraph); + + float depth_world_space[3]; + float new_pos[3]; + + mul_v3_m4v3(depth_world_space, ob->object_to_world, fsecd->cursor_location); + + float fmval[2] = {(float)event->mval[0], (float)event->mval[1]}; + + ED_view3d_win_to_3d(vc.v3d, vc.region, depth_world_space, fmval, new_pos); + float extrude_disp = len_v3v3(depth_world_space, new_pos); + + SCULPT_vertex_random_access_ensure(ss); + SCULPT_face_random_access_ensure(ss); + + if (dot_v3v3(fsecd->start_no, fsecd->start_no) == 0.0f && ss->active_face.i != PBVH_REF_NONE) { + float fno[4]; + + SCULPT_face_normal_get(ss, ss->active_face, fno); + fno[3] = 0.0f; + + mul_v4_m4v4(fno, ob->object_to_world, fno); + copy_v3_v3(fsecd->start_no, fno); + // extrude_disp *= -1.0f; + } + + float grabtan[3]; + sub_v3_v3v3(grabtan, new_pos, depth_world_space); + if (dot_v3v3(fsecd->start_no, fsecd->start_no) > 0.0f && + dot_v3v3(grabtan, fsecd->start_no) < 0) { + extrude_disp *= -1.0f; + } + + if (!ss->bm) { + float(*vert_positions)[3] = SCULPT_mesh_deformed_positions_get(ss); + for (int i = 0; i < fsecd->totvert; i++) { + int idx = fsecd->verts[i]; + + madd_v3_v3v3fl(vert_positions[idx], fsecd->orig_co[i], fsecd->orig_no[i], extrude_disp); + BKE_pbvh_vert_tag_update_normal(ss->pbvh, BKE_pbvh_make_vref(idx)); + } + + PBVHNode **nodes; + int totnode; + BKE_pbvh_search_gather(ss->pbvh, NULL, NULL, &nodes, &totnode); + for (int i = 0; i < totnode; i++) { + BKE_pbvh_node_mark_update(nodes[i]); + } + MEM_SAFE_FREE(nodes); + } + else { + BM_mesh_elem_index_ensure(ss->bm, BM_VERT | BM_EDGE | BM_FACE); + + for (int i = 0; i < fsecd->totvert; i++) { + PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, fsecd->verts[i]); + + BMVert *v = (BMVert *)vertex.i; + + int ni = BM_ELEM_CD_GET_INT(v, ss->cd_vert_node_offset); + if (ni != DYNTOPO_NODE_NONE) { + BKE_pbvh_node_mark_update(BKE_pbvh_node_from_index(ss->pbvh, ni)); + } + + madd_v3_v3v3fl(v->co, fsecd->orig_co[i], fsecd->orig_no[i], extrude_disp); + } + } + + SCULPT_flush_update_step(C, SCULPT_UPDATE_COORDS); + return OPERATOR_RUNNING_MODAL; +} + +static void sculpt_face_set_extrude(bContext *C, + wmOperator *op, + const wmEvent *event, + Object *ob, + const int active_face_set, + const float cursor_location[3]) +{ + FaceSetExtrudeCD *fsecd = (FaceSetExtrudeCD *)MEM_callocN(sizeof(FaceSetExtrudeCD), + "face set extrude cd"); + + fsecd->active_face_set = active_face_set; + copy_v3_v3(fsecd->cursor_location, cursor_location); + float fmval[2] = {(float)event->mval[0], (float)event->mval[1]}; + copy_v2_v2(fsecd->init_mval, fmval); + op->customdata = fsecd; + + bool no_islands = RNA_boolean_get(op->ptr, "single_island_only"); + + if (!ob->sculpt->bm) { + ED_sculpt_undo_geometry_begin(ob, op); + } + else { + SCULPT_undo_push_begin(ob, op); + SCULPT_undo_push_node(ob, NULL, SCULPT_UNDO_COORDS); + } + + sculpt_face_set_extrude_id(ob, no_islands, ob->sculpt, active_face_set, fsecd); + + if (!ob->sculpt->bm) { + BKE_mesh_batch_cache_dirty_tag((Mesh *)ob->data, BKE_MESH_BATCH_DIRTY_ALL); + DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); + } + + WM_event_add_notifier(C, NC_GEOM | ND_DATA, ob->data); +} + static int sculpt_face_set_edit_invoke(bContext *C, wmOperator *op, const wmEvent *event) { Object *ob = CTX_data_active_object(C); SculptSession *ss = ob->sculpt; Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); - const eSculptFaceSetEditMode mode = static_cast( - RNA_enum_get(op->ptr, "mode")); + const int mode = RNA_enum_get(op->ptr, "mode"); const bool modify_hidden = RNA_boolean_get(op->ptr, "modify_hidden"); - if (!sculpt_face_set_edit_is_operation_valid(ss, mode, modify_hidden)) { + if (!sculpt_face_set_edit_is_operation_valid(ss, (eSculptFaceSetEditMode)mode, modify_hidden)) { return OPERATOR_CANCELLED; } - ss->face_sets = BKE_sculpt_face_sets_ensure(BKE_mesh_from_object(ob)); + ss->face_sets = BKE_sculpt_face_sets_ensure(ob); BKE_sculpt_update_object_for_edit(depsgraph, ob, true, false, false); /* Update the current active Face Set and Vertex as the operator can be used directly from the * tool without brush cursor. */ SculptCursorGeometryInfo sgi; const float mval_fl[2] = {float(event->mval[0]), float(event->mval[1])}; - if (!SCULPT_cursor_geometry_info_update(C, &sgi, mval_fl, false)) { + if (!SCULPT_cursor_geometry_info_update(C, &sgi, mval_fl, false, false)) { /* The cursor is not over the mesh. Cancel to avoid editing the last updated Face Set ID. */ return OPERATOR_CANCELLED; } + const int active_face_set = SCULPT_active_face_set_get(ss); switch (mode) { + case SCULPT_FACE_SET_EDIT_EXTRUDE: + sculpt_face_set_extrude(C, op, event, ob, active_face_set, sgi.location); + + SCULPT_tag_update_overlays(C); + WM_event_add_modal_handler(C, op); + return OPERATOR_RUNNING_MODAL; case SCULPT_FACE_SET_EDIT_DELETE_GEOMETRY: - sculpt_face_set_edit_modify_geometry(C, ob, active_face_set, mode, modify_hidden, op); + sculpt_face_set_edit_modify_geometry( + C, ob, active_face_set, (eSculptFaceSetEditMode)mode, modify_hidden, op); break; case SCULPT_FACE_SET_EDIT_GROW: case SCULPT_FACE_SET_EDIT_SHRINK: - sculpt_face_set_edit_modify_face_sets(ob, active_face_set, mode, modify_hidden, op); + case SCULPT_FACE_SET_EDIT_FILL_COMPONENT: + sculpt_face_set_edit_modify_face_sets( + ob, active_face_set, (eSculptFaceSetEditMode)mode, modify_hidden, op); break; case SCULPT_FACE_SET_EDIT_FAIR_POSITIONS: case SCULPT_FACE_SET_EDIT_FAIR_TANGENCY: - sculpt_face_set_edit_modify_coordinates(C, ob, active_face_set, mode, op); + case SCULPT_FACE_SET_EDIT_FAIR_CURVATURE: + case SCULPT_FACE_SET_EDIT_FAIR_ALL_TANGENCY: + sculpt_face_set_edit_modify_coordinates( + C, ob, active_face_set, (eSculptFaceSetEditMode)mode, op); break; } @@ -1470,6 +2989,7 @@ void SCULPT_OT_face_sets_edit(struct wmOperatorType *ot) /* Api callbacks. */ ot->invoke = sculpt_face_set_edit_invoke; + ot->modal = sculpt_face_set_edit_modal; ot->poll = SCULPT_mode_poll; ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; @@ -1481,4 +3001,9 @@ void SCULPT_OT_face_sets_edit(struct wmOperatorType *ot) true, "Modify Hidden", "Apply the edit operation to hidden Face Sets"); + ot->prop = RNA_def_boolean(ot->srna, + "single_island_only", + false, + "Ignore Disconnected", + "Apply the edit operation to a single island only"); } diff --git a/source/blender/editors/sculpt_paint/sculpt_face_set_topology.c b/source/blender/editors/sculpt_paint/sculpt_face_set_topology.c new file mode 100644 index 00000000000..d73d9efe02e --- /dev/null +++ b/source/blender/editors/sculpt_paint/sculpt_face_set_topology.c @@ -0,0 +1,208 @@ +/* + * 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) 2020 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup edsculpt + */ + +#include "MEM_guardedalloc.h" + +#include "BLI_blenlib.h" +#include "BLI_hash.h" +#include "BLI_math.h" +#include "BLI_task.h" + +#include "DNA_brush_types.h" +#include "DNA_customdata_types.h" +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" + +#include "BKE_brush.h" +#include "BKE_ccg.h" +#include "BKE_colortools.h" +#include "BKE_context.h" +#include "BKE_customdata.h" +#include "BKE_mesh.h" +#include "BKE_mesh_fair.h" +#include "BKE_mesh_mapping.h" +#include "BKE_multires.h" +#include "BKE_node.h" +#include "BKE_object.h" +#include "BKE_paint.h" +#include "BKE_pbvh.h" +#include "BKE_scene.h" + +#include "DEG_depsgraph.h" + +#include "WM_api.h" +#include "WM_message.h" +#include "WM_toolsystem.h" +#include "WM_types.h" + +#include "ED_object.h" +#include "ED_screen.h" +#include "ED_sculpt.h" +#include "ED_view3d.h" +#include "paint_intern.h" +#include "sculpt_intern.h" + +#include "RNA_access.h" +#include "RNA_define.h" + +#include "bmesh.h" + +#include +#include + +typedef enum eSculptFaceSetByTopologyMode { + SCULPT_FACE_SET_TOPOLOGY_LOOSE_PART = 0, + SCULPT_FACE_SET_TOPOLOGY_POLY_LOOP = 1, +} eSculptFaceSetByTopologyMode; + +static EnumPropertyItem prop_sculpt_face_set_by_topology[] = { + { + SCULPT_FACE_SET_TOPOLOGY_LOOSE_PART, + "LOOSE_PART", + 0, + "Loose Part", + "", + }, + { + SCULPT_FACE_SET_TOPOLOGY_POLY_LOOP, + "POLY_LOOP", + 0, + "Face Loop", + "", + }, + {0, NULL, 0, NULL, NULL}, +}; + +static void sculpt_face_set_by_topology_poly_loop(Object *ob, const int next_face_set_id) +{ + SculptSession *ss = ob->sculpt; + BLI_bitmap *poly_loop = sculpt_poly_loop_from_cursor(ob); + for (int i = 0; i < ss->totfaces; i++) { + if (BLI_BITMAP_TEST(poly_loop, i)) { + ss->face_sets[i] = next_face_set_id; + } + } + MEM_freeN(poly_loop); +} + +static int sculpt_face_set_by_topology_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + Object *ob = CTX_data_active_object(C); + SculptSession *ss = ob->sculpt; + Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); + + const int mode = RNA_enum_get(op->ptr, "mode"); + const bool repeat_previous = RNA_boolean_get(op->ptr, "repeat_previous"); + BKE_sculpt_update_object_for_edit(depsgraph, ob, true, false, false); + printf("FACE SET TOPOLOGY\n"); + + /* Update the current active Face Set and Vertex as the operator can be used directly from the + * tool without brush cursor. */ + SculptCursorGeometryInfo sgi; + const float mouse[2] = {event->mval[0], event->mval[1]}; + if (!SCULPT_cursor_geometry_info_update(C, &sgi, mouse, false, false)) { + /* The cursor is not over the mesh. Cancel to avoid editing the last updated Face Set ID. */ + return OPERATOR_CANCELLED; + } + + PBVHNode **nodes; + int totnode; + BKE_pbvh_search_gather(ss->pbvh, NULL, NULL, &nodes, &totnode); + SCULPT_undo_push_begin(ob, op); + SCULPT_undo_push_node(ob, nodes[0], SCULPT_UNDO_FACE_SETS); + + const PBVHFaceRef initial_poly = ss->active_face; + const PBVHEdgeRef initial_edge = sculpt_poly_loop_initial_edge_from_cursor(ob); + + Mesh *mesh = BKE_object_get_original_mesh(ob); + int new_face_set = SCULPT_FACE_SET_NONE; + + if (repeat_previous && ss->face_set_last_created != SCULPT_FACE_SET_NONE && + initial_poly.i != ss->face_set_last_poly.i && initial_edge.i != ss->face_set_last_edge.i) { + new_face_set = ss->face_set_last_created; + } + else { + new_face_set = ED_sculpt_face_sets_find_next_available_id(mesh); + } + + switch (mode) { + case SCULPT_FACE_SET_TOPOLOGY_LOOSE_PART: + break; + case SCULPT_FACE_SET_TOPOLOGY_POLY_LOOP: + sculpt_face_set_by_topology_poly_loop(ob, new_face_set); + break; + } + + ss->face_set_last_created = new_face_set; + ss->face_set_last_edge = initial_edge; + ss->face_set_last_poly = initial_poly; + + /* Sync face sets visibility and vertex visibility as now all Face Sets are visible. */ + //SCULPT_visibility_sync_all_face_sets_to_vertices(ob); + + for (int i = 0; i < totnode; i++) { + BKE_pbvh_vert_tag_update_normal_visibility(nodes[i]); + } + + BKE_pbvh_update_vertex_data(ss->pbvh, PBVH_UpdateVisibility); + + if (BKE_pbvh_type(ss->pbvh) == PBVH_FACES) { + BKE_mesh_flush_hidden_from_verts(ob->data); + } + + MEM_freeN(nodes); + + SCULPT_undo_push_end(ob); + SCULPT_tag_update_overlays(C); + + return OPERATOR_FINISHED; +} + +void SCULPT_OT_face_set_by_topology(struct wmOperatorType *ot) +{ + /* Identifiers. */ + ot->name = "Face Set by Topology"; + ot->idname = "SCULPT_OT_face_set_by_topology"; + ot->description = "Create a new Face Set following the mesh topology"; + + /* Api callbacks. */ + ot->invoke = sculpt_face_set_by_topology_invoke; + ot->poll = SCULPT_mode_poll; + + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + RNA_def_enum(ot->srna, + "mode", + prop_sculpt_face_set_by_topology, + SCULPT_FACE_SET_TOPOLOGY_POLY_LOOP, + "Mode", + ""); + + RNA_def_boolean(ot->srna, + "repeat_previous", + true, + "Repeat previous Face Set", + "Repeat the latest created Face Set instead of a new one"); +} diff --git a/source/blender/editors/sculpt_paint/sculpt_filter_color.cc b/source/blender/editors/sculpt_paint/sculpt_filter_color.cc index 3ca6449ad50..db50352a1c5 100644 --- a/source/blender/editors/sculpt_paint/sculpt_filter_color.cc +++ b/source/blender/editors/sculpt_paint/sculpt_filter_color.cc @@ -81,7 +81,7 @@ static void color_filter_task_cb(void *__restrict userdata, PBVHVertexIter vd; BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { - SCULPT_orig_vert_data_update(&orig_data, &vd); + SCULPT_orig_vert_data_update(&orig_data, vd.vertex); SCULPT_automasking_node_update(ss, &automask_data, &vd); float orig_color[3], final_color[4], hsv_color[3]; @@ -268,7 +268,7 @@ static int sculpt_color_filter_modal(bContext *C, wmOperator *op, const wmEvent if (event->type == LEFTMOUSE && event->val == KM_RELEASE) { SCULPT_undo_push_end(ob); - SCULPT_filter_cache_free(ss); + SCULPT_filter_cache_free(ss, ob); SCULPT_flush_update_done(C, ob, SCULPT_UPDATE_COLOR); return OPERATOR_FINISHED; } @@ -327,7 +327,7 @@ static int sculpt_color_filter_invoke(bContext *C, wmOperator *op, const wmEvent * Filter Tool. */ float mval_fl[2] = {float(event->mval[0]), float(event->mval[1])}; SculptCursorGeometryInfo sgi; - SCULPT_cursor_geometry_info_update(C, &sgi, mval_fl, false); + SCULPT_cursor_geometry_info_update(C, &sgi, mval_fl, false, false); } /* Disable for multires and dyntopo for now */ diff --git a/source/blender/editors/sculpt_paint/sculpt_filter_mask.cc b/source/blender/editors/sculpt_paint/sculpt_filter_mask.cc index c806ebdfd70..8fe3c0ece6c 100644 --- a/source/blender/editors/sculpt_paint/sculpt_filter_mask.cc +++ b/source/blender/editors/sculpt_paint/sculpt_filter_mask.cc @@ -28,6 +28,8 @@ #include "RNA_access.h" #include "RNA_define.h" +#include "UI_interface.h" + #include "bmesh.h" #include @@ -286,3 +288,893 @@ void SCULPT_OT_mask_filter(wmOperatorType *ot) "Auto Iteration Count", "Use a automatic number of iterations based on the number of vertices of the sculpt"); } + +/******************************************************************************************/ +/* Interactive Preview Mask Filter */ + +#define SCULPT_IPMASK_FILTER_MIN_MULTITHREAD 1000 +#define SCULPT_IPMASK_FILTER_GRANULARITY 100 + +#define SCULPT_IPMASK_FILTER_QUANTIZE_STEP 0.1 + +enum eSculptIPMaskFilterType { + IPMASK_FILTER_SMOOTH_SHARPEN, + IPMASK_FILTER_GROW_SHRINK, + IPMASK_FILTER_HARDER_SOFTER, + IPMASK_FILTER_CONTRAST, + IPMASK_FILTER_ADD_SUBSTRACT, + IPMASK_FILTER_INVERT, + IPMASK_FILTER_QUANTIZE, +}; +ENUM_OPERATORS(eSculptIPMaskFilterType, IPMASK_FILTER_QUANTIZE); + +enum MaskFilterStepDirectionType { + MASK_FILTER_STEP_DIRECTION_FORWARD, + MASK_FILTER_STEP_DIRECTION_BACKWARD, +}; +ENUM_OPERATORS(MaskFilterStepDirectionType, MASK_FILTER_STEP_DIRECTION_BACKWARD); + +/* Grown/Shrink vertex callbacks. */ +static float sculpt_ipmask_vertex_grow_cb(SculptSession *ss, + const PBVHVertRef vertex, + float *current_mask) +{ + float max = 0.0f; + SculptVertexNeighborIter ni; + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vertex, ni) { + float vmask_f = current_mask[ni.index]; + if (vmask_f > max) { + max = vmask_f; + } + } + SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); + return max; +} + +static float sculpt_ipmask_vertex_shrink_cb(SculptSession *ss, + const PBVHVertRef vertex, + float *current_mask) +{ + float min = 1.0f; + SculptVertexNeighborIter ni; + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vertex, ni) { + float vmask_f = current_mask[ni.index]; + if (vmask_f < min) { + min = vmask_f; + } + } + SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); + return min; +} + +/* Smooth/Sharpen vertex callbacks. */ +static float sculpt_ipmask_vertex_smooth_cb(SculptSession *ss, + const PBVHVertRef vertex, + float *current_mask) +{ + int vertex_i = BKE_pbvh_vertex_to_index(ss->pbvh, vertex); + + float accum = current_mask[vertex_i]; + int total = 1; + SculptVertexNeighborIter ni; + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vertex, ni) { + accum += current_mask[ni.index]; + total++; + } + SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); + return total > 0 ? accum / total : current_mask[vertex_i]; +} + +static float sculpt_ipmask_vertex_sharpen_cb(SculptSession *ss, + const PBVHVertRef vertex, + float *current_mask) +{ + int vertex_i = BKE_pbvh_vertex_to_index(ss->pbvh, vertex); + + float accum = 0.0f; + int total = 0; + float vmask = current_mask[vertex_i]; + SculptVertexNeighborIter ni; + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vertex, ni) { + accum += current_mask[ni.index]; + total++; + } + SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); + const float avg = total > 0 ? accum / total : current_mask[vertex_i]; + const float val = avg - vmask; + + float new_mask; + if (vmask > 0.5f) { + new_mask = vmask + 0.03f; + } + else { + new_mask = vmask - 0.03f; + } + new_mask += val / 2.0f; + return clamp_f(new_mask, 0.0f, 1.0f); + +#ifdef SHARP_KERNEL + float accum = 0.0f; + float weight_accum = 0.0f; + const float neighbor_weight = -1.0f; + int neighbor_count = 0; + + SculptVertexNeighborIter ni; + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vertex, ni) { + accum += neighbor_weight * current_mask[ni.index]; + weight_accum += neighbor_weight; + neighbor_count++; + } + SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); + + const float main_weight = (neighbor_count + 1) * 2.0f; + accum += main_weight * current_mask[vertex]; + weight_accum += main_weight; + + return clamp_f(accum / weight_accum, 0.0f, 1.0f); +#endif +} + +/* Harder/Softer callbacks. */ +#define SCULPT_IPMASK_FILTER_HARDER_SOFTER_STEP 0.01f +static float sculpt_ipmask_vertex_harder_cb(SculptSession *ss, + const PBVHVertRef vertex, + float *current_mask) +{ + int vertex_i = BKE_pbvh_vertex_to_index(ss->pbvh, vertex); + + return clamp_f(current_mask[vertex_i] += current_mask[vertex_i] * + SCULPT_IPMASK_FILTER_HARDER_SOFTER_STEP, + 0.0f, + 1.0f); +} + +static float sculpt_ipmask_vertex_softer_cb(SculptSession *ss, + const PBVHVertRef vertex, + float *current_mask) +{ + int vertex_i = BKE_pbvh_vertex_to_index(ss->pbvh, vertex); + + return clamp_f(current_mask[vertex_i] -= current_mask[vertex_i] * + SCULPT_IPMASK_FILTER_HARDER_SOFTER_STEP, + 0.0f, + 1.0f); +} + +/* Contrast Increase/Decrease callbacks. */ + +#define SCULPT_IPMASK_FILTER_CONTRAST_STEP 0.05f +static float sculpt_ipmask_filter_contrast(const float mask, const float contrast) +{ + float offset; + float delta = contrast / 2.0f; + float gain = 1.0f - delta * 2.0f; + if (contrast > 0.0f) { + gain = 1.0f / ((gain != 0.0f) ? gain : FLT_EPSILON); + offset = gain * (-delta); + } + else { + delta *= -1.0f; + offset = gain * (delta); + } + return clamp_f(gain * mask + offset, 0.0f, 1.0f); +} + +static float sculpt_ipmask_vertex_contrast_increase_cb(SculptSession *ss, + const PBVHVertRef vertex, + float *current_mask) +{ + int vertex_i = BKE_pbvh_vertex_to_index(ss->pbvh, vertex); + + return sculpt_ipmask_filter_contrast(current_mask[vertex_i], SCULPT_IPMASK_FILTER_CONTRAST_STEP); +} + +static float sculpt_ipmask_vertex_contrast_decrease_cb(SculptSession *ss, + const PBVHVertRef vertex, + float *current_mask) +{ + int vertex_i = BKE_pbvh_vertex_to_index(ss->pbvh, vertex); + + return sculpt_ipmask_filter_contrast(current_mask[vertex_i], + -1.0f * SCULPT_IPMASK_FILTER_CONTRAST_STEP); +} + +static MaskFilterDeltaStep *sculpt_ipmask_filter_delta_create(const float *current_mask, + const float *next_mask, + const int totvert) +{ + int tot_modified_values = 0; + for (int i = 0; i < totvert; i++) { + if (current_mask[i] == next_mask[i]) { + continue; + } + tot_modified_values++; + } + + MaskFilterDeltaStep *delta_step = MEM_cnew("mask filter delta step"); + delta_step->totelem = tot_modified_values; + delta_step->index = MEM_cnew_array(tot_modified_values, "delta indices"); + delta_step->delta = MEM_cnew_array(tot_modified_values, "delta values"); + + int delta_step_index = 0; + for (int i = 0; i < totvert; i++) { + if (current_mask[i] == next_mask[i]) { + continue; + } + delta_step->index[delta_step_index] = i; + delta_step->delta[delta_step_index] = next_mask[i] - current_mask[i]; + delta_step_index++; + } + return delta_step; +} + +typedef struct SculptIPMaskFilterTaskData { + SculptSession *ss; + float *next_mask; + float *current_mask; + MaskFilterStepDirectionType direction; +} SculptIPMaskFilterTaskData; + +static void ipmask_filter_compute_step_task_cb(void *__restrict userdata, + const int i, + const TaskParallelTLS *__restrict /* tls */) +{ + SculptIPMaskFilterTaskData *data = static_cast(userdata); + PBVHVertRef vertex = BKE_pbvh_index_to_vertex(data->ss->pbvh, i); + + if (data->direction == MASK_FILTER_STEP_DIRECTION_FORWARD) { + data->next_mask[i] = data->ss->filter_cache->mask_filter_step_forward( + data->ss, vertex, data->current_mask); + } + else { + data->next_mask[i] = data->ss->filter_cache->mask_filter_step_backward( + data->ss, vertex, data->current_mask); + } +} + +static float *sculpt_ipmask_step_compute(SculptSession *ss, + float *current_mask, + MaskFilterStepDirectionType direction) +{ + const int totvert = SCULPT_vertex_count_get(ss); + float *next_mask = MEM_cnew_array(totvert, "delta values"); + + SculptIPMaskFilterTaskData data = { + .ss = ss, + .next_mask = next_mask, + .current_mask = current_mask, + .direction = direction, + }; + TaskParallelSettings settings; + memset(&settings, 0, sizeof(TaskParallelSettings)); + settings.use_threading = totvert > SCULPT_IPMASK_FILTER_MIN_MULTITHREAD; + settings.min_iter_per_thread = SCULPT_IPMASK_FILTER_GRANULARITY; + BLI_task_parallel_range(0, totvert, &data, ipmask_filter_compute_step_task_cb, &settings); + + return next_mask; +} + +static float *sculpt_ipmask_current_state_get(SculptSession *ss) +{ + return static_cast(MEM_dupallocN(ss->filter_cache->mask_filter_ref)); +} + +static void sculpt_ipmask_reference_set(SculptSession *ss, float *new_mask) +{ + const int totvert = SCULPT_vertex_count_get(ss); + for (int i = 0; i < totvert; i++) { + ss->filter_cache->mask_filter_ref[i] = new_mask[i]; + } +} + +static void sculpt_ipmask_store_reference_step(SculptSession *ss) +{ + const int totvert = SCULPT_vertex_count_get(ss); + if (!ss->filter_cache->mask_filter_ref) { + ss->filter_cache->mask_filter_ref = MEM_cnew_array(totvert, "delta values"); + } + + for (int i = 0; i < totvert; i++) { + PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); + + ss->filter_cache->mask_filter_ref[i] = SCULPT_vertex_mask_get(ss, vertex); + } +} + +static void ipmask_filter_apply_task_cb(void *__restrict userdata, + const int i, + const TaskParallelTLS *__restrict /* tls */) +{ + SculptThreadedTaskData *data = static_cast(userdata); + SculptSession *ss = data->ss; + FilterCache *filter_cache = ss->filter_cache; + PBVHNode *node = filter_cache->nodes[i]; + PBVHVertexIter vd; + bool update = false; + + AutomaskingNodeData automask_data; + SCULPT_automasking_node_begin(data->ob, ss, filter_cache->automasking, &automask_data, node); + + BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) { + SCULPT_automasking_node_update(ss, &automask_data, &vd); + + if (SCULPT_automasking_factor_get(filter_cache->automasking, ss, vd.vertex, &automask_data) < + 0.5f) { + continue; + } + + float new_mask; + if (data->next_mask) { + new_mask = interpf( + data->next_mask[vd.index], data->new_mask[vd.index], data->mask_interpolation); + } + else { + new_mask = data->new_mask[vd.index]; + } + + if (*vd.mask == new_mask) { + continue; + } + + *vd.mask = new_mask; + update = true; + if (vd.is_mesh) { + BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); + } + } + BKE_pbvh_vertex_iter_end; + + if (update) { + BKE_pbvh_node_mark_redraw(node); + } +} + +static void sculpt_ipmask_apply_mask_data(SculptSession *ss, + float *new_mask, + float *next_mask, + const float interpolation) +{ + FilterCache *filter_cache = ss->filter_cache; + SculptThreadedTaskData data = { + .ss = ss, + .nodes = filter_cache->nodes, + .new_mask = new_mask, + .next_mask = next_mask, + .mask_interpolation = interpolation, + }; + + TaskParallelSettings settings; + BKE_pbvh_parallel_range_settings(&settings, true, filter_cache->totnode); + BLI_task_parallel_range(0, filter_cache->totnode, &data, ipmask_filter_apply_task_cb, &settings); +} + +static float *sculpt_ipmask_apply_delta_step(MaskFilterDeltaStep *delta_step, + const float *current_mask, + const MaskFilterStepDirectionType direction) +{ + float *next_mask = static_cast(MEM_dupallocN(current_mask)); + for (int i = 0; i < delta_step->totelem; i++) { + if (direction == MASK_FILTER_STEP_DIRECTION_FORWARD) { + next_mask[delta_step->index[i]] = current_mask[delta_step->index[i]] + delta_step->delta[i]; + } + else { + next_mask[delta_step->index[i]] = current_mask[delta_step->index[i]] - delta_step->delta[i]; + } + } + return next_mask; +} + +static float *sculpt_ipmask_restore_state_from_delta(SculptSession *ss, + MaskFilterDeltaStep *delta_step, + MaskFilterStepDirectionType direction) +{ + float *current_mask = sculpt_ipmask_current_state_get(ss); + float *next_mask = sculpt_ipmask_apply_delta_step(delta_step, current_mask, direction); + MEM_freeN(current_mask); + return next_mask; +} + +static float *sculpt_ipmask_compute_and_store_step(SculptSession *ss, + const int iterations, + const int delta_index, + MaskFilterStepDirectionType direction) +{ + BLI_assert(iterations > 0); + const int totvert = SCULPT_vertex_count_get(ss); + float *current_mask = sculpt_ipmask_current_state_get(ss); + float *original_mask = static_cast(MEM_dupallocN(current_mask)); + float *next_mask = nullptr; + + /* Compute the filter. */ + for (int i = 0; i < iterations; i++) { + MEM_SAFE_FREE(next_mask); + next_mask = sculpt_ipmask_step_compute(ss, current_mask, direction); + MEM_freeN(current_mask); + current_mask = static_cast(MEM_dupallocN(next_mask)); + } + MEM_freeN(current_mask); + + /* Pack and store the delta step. */ + MaskFilterDeltaStep *delta_step; + if (direction == MASK_FILTER_STEP_DIRECTION_FORWARD) { + delta_step = sculpt_ipmask_filter_delta_create(original_mask, next_mask, totvert); + } + else { + delta_step = sculpt_ipmask_filter_delta_create(next_mask, original_mask, totvert); + } + BLI_ghash_insert(ss->filter_cache->mask_delta_step, POINTER_FROM_INT(delta_index), delta_step); + MEM_freeN(original_mask); + + return next_mask; +} + +static float *sculpt_ipmask_filter_mask_for_step_get(SculptSession *ss, + MaskFilterStepDirectionType direction, + const int iteration_count) +{ + FilterCache *filter_cache = ss->filter_cache; + int next_step = filter_cache->mask_filter_current_step; + int delta_index = next_step; + /* Get the next step and the delta step index associated with it. */ + if (direction == MASK_FILTER_STEP_DIRECTION_FORWARD) { + next_step = filter_cache->mask_filter_current_step + 1; + delta_index = filter_cache->mask_filter_current_step; + } + else { + next_step = filter_cache->mask_filter_current_step - 1; + delta_index = filter_cache->mask_filter_current_step - 1; + } + + /* Update the data one step forward/backward. */ + if (BLI_ghash_haskey(filter_cache->mask_delta_step, POINTER_FROM_INT(delta_index))) { + /* This step was already computed, restore it from the current step and a delta. */ + MaskFilterDeltaStep *delta_step = static_cast( + BLI_ghash_lookup(filter_cache->mask_delta_step, POINTER_FROM_INT(delta_index))); + return sculpt_ipmask_restore_state_from_delta(ss, delta_step, direction); + } + + /* New step that was not yet computed. Compute and store the delta. */ + return sculpt_ipmask_compute_and_store_step(ss, iteration_count, delta_index, direction); +} + +static void sculpt_ipmask_filter_update_to_target_step(SculptSession *ss, + const int target_step, + const int iteration_count, + const float step_interpolation) +{ + FilterCache *filter_cache = ss->filter_cache; + + MaskFilterStepDirectionType direction; + /* Get the next step and the delta step index associated with it. */ + if (target_step > filter_cache->mask_filter_current_step) { + direction = MASK_FILTER_STEP_DIRECTION_FORWARD; + } + else { + direction = MASK_FILTER_STEP_DIRECTION_BACKWARD; + } + + while (filter_cache->mask_filter_current_step != target_step) { + /* Restore or compute a mask in the given direction. */ + float *new_mask = sculpt_ipmask_filter_mask_for_step_get(ss, direction, iteration_count); + + /* Store the full step. */ + sculpt_ipmask_reference_set(ss, new_mask); + MEM_freeN(new_mask); + + /* Update the current step count. */ + if (direction == MASK_FILTER_STEP_DIRECTION_FORWARD) { + filter_cache->mask_filter_current_step += 1; + } + else { + filter_cache->mask_filter_current_step -= 1; + } + } + + if (step_interpolation != 0.0f) { + float *next_mask = sculpt_ipmask_filter_mask_for_step_get( + ss, MASK_FILTER_STEP_DIRECTION_FORWARD, iteration_count); + sculpt_ipmask_apply_mask_data( + ss, filter_cache->mask_filter_ref, next_mask, step_interpolation); + MEM_freeN(next_mask); + } + else { + sculpt_ipmask_apply_mask_data(ss, filter_cache->mask_filter_ref, nullptr, 0.0f); + } +} + +static void ipmask_filter_apply_from_original_task_cb(void *__restrict userdata, + const int i, + const TaskParallelTLS *__restrict /* tls */) +{ + SculptThreadedTaskData *data = static_cast(userdata); + SculptSession *ss = data->ss; + FilterCache *filter_cache = ss->filter_cache; + PBVHNode *node = filter_cache->nodes[i]; + PBVHVertexIter vd; + SculptOrigVertData orig_data; + const eSculptIPMaskFilterType filter_type = static_cast( + data->filter_type); + bool update = false; + + /* Used for quantize filter. */ + const int steps = data->filter_strength / SCULPT_IPMASK_FILTER_QUANTIZE_STEP; + if (steps == 0) { + return; + } + const float step_size = 1.0f / steps; + + AutomaskingNodeData automask_data; + SCULPT_automasking_node_begin(data->ob, ss, filter_cache->automasking, &automask_data, node); + + SCULPT_orig_vert_data_init(&orig_data, data->ob, node, SCULPT_UNDO_COORDS); + BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) { + SCULPT_automasking_node_update(ss, &automask_data, &vd); + + if (SCULPT_automasking_factor_get(filter_cache->automasking, ss, vd.vertex, &automask_data) < + 0.5f) { + continue; + } + SCULPT_orig_vert_data_update(&orig_data, vd.vertex); + float new_mask = orig_data.mask; + switch (filter_type) { + case IPMASK_FILTER_ADD_SUBSTRACT: + new_mask = orig_data.mask + data->filter_strength; + break; + case IPMASK_FILTER_INVERT: { + const float strength = clamp_f(data->filter_strength, 0.0f, 1.0f); + const float mask_invert = 1.0f - orig_data.mask; + new_mask = interpf(mask_invert, orig_data.mask, strength); + break; + } + case IPMASK_FILTER_QUANTIZE: { + const float remainder = fmod(orig_data.mask, step_size); + const float total_steps = (orig_data.mask - remainder) / step_size; + new_mask = total_steps * step_size; + break; + } + default: + BLI_assert(false); + break; + } + new_mask = clamp_f(new_mask, 0.0f, 1.0f); + if (*vd.mask == new_mask) { + continue; + } + + *vd.mask = new_mask; + update = true; + if (vd.is_mesh) { + BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); + } + } + BKE_pbvh_vertex_iter_end; + + if (update) { + BKE_pbvh_node_mark_redraw(node); + } +} + +static void sculpt_ipmask_apply_from_original_mask_data(Object *ob, + eSculptIPMaskFilterType filter_type, + const float strength) +{ + SculptSession *ss = ob->sculpt; + FilterCache *filter_cache = ss->filter_cache; + SculptThreadedTaskData data; + data.ob = ob; + data.ss = ss; + data.nodes = filter_cache->nodes; + data.filter_strength = strength; + data.filter_type = filter_type; + + TaskParallelSettings settings; + BKE_pbvh_parallel_range_settings(&settings, true, filter_cache->totnode); + BLI_task_parallel_range( + 0, filter_cache->totnode, &data, ipmask_filter_apply_from_original_task_cb, &settings); +} + +static bool sculpt_ipmask_filter_uses_apply_from_original( + const eSculptIPMaskFilterType filter_type) +{ + return ELEM( + filter_type, IPMASK_FILTER_INVERT, IPMASK_FILTER_ADD_SUBSTRACT, IPMASK_FILTER_QUANTIZE); +} + +static void ipmask_filter_restore_original_mask_task_cb( + void *__restrict userdata, const int i, const TaskParallelTLS *__restrict /* tls */) +{ + SculptThreadedTaskData *data = static_cast(userdata); + SculptSession *ss = data->ss; + PBVHNode *node = data->nodes[i]; + SculptOrigVertData orig_data; + bool update = false; + SCULPT_orig_vert_data_init(&orig_data, data->ob, node, SCULPT_UNDO_COORDS); + PBVHVertexIter vd; + BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) { + SCULPT_orig_vert_data_update(&orig_data, vd.vertex); + *vd.mask = orig_data.mask; + update = true; + if (vd.is_mesh) { + BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); + } + } + BKE_pbvh_vertex_iter_end; + + if (update) { + BKE_pbvh_node_mark_redraw(node); + } +} + +static void sculpt_ipmask_restore_original_mask(Object *ob) +{ + SculptSession *ss = ob->sculpt; + FilterCache *filter_cache = ss->filter_cache; + SculptThreadedTaskData data = {}; + data.ob = ob; + data.ss = ss; + data.nodes = filter_cache->nodes; + + TaskParallelSettings settings; + BKE_pbvh_parallel_range_settings(&settings, true, filter_cache->totnode); + BLI_task_parallel_range( + 0, filter_cache->totnode, &data, ipmask_filter_restore_original_mask_task_cb, &settings); +} + +static void sculpt_ipmask_filter_cancel(bContext *C, wmOperator * /* op */) +{ + Object *ob = CTX_data_active_object(C); + SculptSession *ss = ob->sculpt; + + sculpt_ipmask_restore_original_mask(ob); + SCULPT_undo_push_end(ob); + SCULPT_filter_cache_free(ss, ob); + SCULPT_flush_update_done(C, ob, SCULPT_UPDATE_MASK); +} + +#define IPMASK_FILTER_STEP_SENSITIVITY 0.05f +#define IPMASK_FILTER_STEPS_PER_FULL_STRENGTH 20 +static int sculpt_ipmask_filter_modal(bContext *C, wmOperator *op, const wmEvent *event) +{ + Object *ob = CTX_data_active_object(C); + Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); + SculptSession *ss = ob->sculpt; + FilterCache *filter_cache = ss->filter_cache; + const eSculptIPMaskFilterType filter_type = (eSculptIPMaskFilterType)RNA_enum_get(op->ptr, + "filter_type"); + const bool use_step_interpolation = RNA_boolean_get(op->ptr, "use_step_interpolation"); + const int iteration_count = RNA_int_get(op->ptr, "iterations"); + + if ((event->type == EVT_ESCKEY && event->val == KM_PRESS) || + (event->type == RIGHTMOUSE && event->val == KM_PRESS)) { + sculpt_ipmask_filter_cancel(C, op); + return OPERATOR_FINISHED; + } + + if (ELEM(event->type, LEFTMOUSE, EVT_RETKEY, EVT_PADENTER)) { + for (int i = 0; i < filter_cache->totnode; i++) { + BKE_pbvh_node_mark_update_mask(filter_cache->nodes[i]); + } + SCULPT_filter_cache_free(ss, ob); + SCULPT_undo_push_end(ob); + SCULPT_flush_update_done(C, ob, SCULPT_UPDATE_MASK); + return OPERATOR_FINISHED; + } + + if (event->type != MOUSEMOVE) { + return OPERATOR_RUNNING_MODAL; + } + + const float len = event->xy[0] - event->prev_press_xy[0]; + const float target_step_fl = len * IPMASK_FILTER_STEP_SENSITIVITY * UI_DPI_FAC; + const int target_step = floorf(target_step_fl); + const float step_interpolation = use_step_interpolation ? target_step_fl - target_step : 0.0f; + const float full_step_strength = target_step_fl / IPMASK_FILTER_STEPS_PER_FULL_STRENGTH; + + BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true, false); + + if (sculpt_ipmask_filter_uses_apply_from_original(filter_type)) { + sculpt_ipmask_apply_from_original_mask_data(ob, filter_type, full_step_strength); + } + else { + sculpt_ipmask_filter_update_to_target_step( + ss, target_step, iteration_count, step_interpolation); + } + + SCULPT_tag_update_overlays(C); + + return OPERATOR_RUNNING_MODAL; +} + +static void sculpt_ipmask_store_initial_undo_step(Object *ob) +{ + SculptSession *ss = ob->sculpt; + for (int i = 0; i < ss->filter_cache->totnode; i++) { + SCULPT_undo_push_node(ob, ss->filter_cache->nodes[i], SCULPT_UNDO_MASK); + } +} + +static FilterCache *sculpt_ipmask_filter_cache_init(Object *ob, + Sculpt *sd, + const eSculptIPMaskFilterType filter_type, + const bool init_automasking) +{ + SculptSession *ss = ob->sculpt; + FilterCache *filter_cache = MEM_cnew("filter cache"); + + filter_cache->active_face_set = SCULPT_FACE_SET_NONE; + if (init_automasking) { + filter_cache->automasking = SCULPT_automasking_cache_init(sd, nullptr, ob); + } + filter_cache->mask_filter_current_step = 0; + + BKE_pbvh_search_gather(ss->pbvh, nullptr, nullptr, &filter_cache->nodes, &filter_cache->totnode); + + filter_cache->mask_delta_step = BLI_ghash_int_new("mask filter delta steps"); + switch (filter_type) { + case IPMASK_FILTER_SMOOTH_SHARPEN: + filter_cache->mask_filter_step_forward = sculpt_ipmask_vertex_smooth_cb; + filter_cache->mask_filter_step_backward = sculpt_ipmask_vertex_sharpen_cb; + break; + case IPMASK_FILTER_GROW_SHRINK: + filter_cache->mask_filter_step_forward = sculpt_ipmask_vertex_grow_cb; + filter_cache->mask_filter_step_backward = sculpt_ipmask_vertex_shrink_cb; + break; + case IPMASK_FILTER_HARDER_SOFTER: + filter_cache->mask_filter_step_forward = sculpt_ipmask_vertex_harder_cb; + filter_cache->mask_filter_step_backward = sculpt_ipmask_vertex_softer_cb; + break; + case IPMASK_FILTER_CONTRAST: + filter_cache->mask_filter_step_forward = sculpt_ipmask_vertex_contrast_increase_cb; + filter_cache->mask_filter_step_backward = sculpt_ipmask_vertex_contrast_decrease_cb; + break; + case IPMASK_FILTER_ADD_SUBSTRACT: + case IPMASK_FILTER_INVERT: + case IPMASK_FILTER_QUANTIZE: + break; + } + + return filter_cache; +} + +static int sculpt_ipmask_filter_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + Object *ob = CTX_data_active_object(C); + Sculpt *sd = CTX_data_tool_settings(C)->sculpt; + SculptSession *ss = ob->sculpt; + Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); + + SCULPT_undo_push_begin(ob, op); + + BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true, false); + + const eSculptIPMaskFilterType filter_type = (eSculptIPMaskFilterType)RNA_enum_get(op->ptr, + "filter_type"); + ss->filter_cache = sculpt_ipmask_filter_cache_init(ob, sd, filter_type, true); + sculpt_ipmask_store_initial_undo_step(ob); + sculpt_ipmask_store_reference_step(ss); + + WM_event_add_modal_handler(C, op); + return OPERATOR_RUNNING_MODAL; +} +static int sculpt_ipmask_filter_exec(bContext *C, wmOperator *op) +{ + Object *ob = CTX_data_active_object(C); + Sculpt *sd = CTX_data_tool_settings(C)->sculpt; + SculptSession *ss = ob->sculpt; + Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); + + const int iteration_count = RNA_int_get(op->ptr, "iterations"); + const float strength = RNA_float_get(op->ptr, "strength"); + const eSculptIPMaskFilterType filter_type = (eSculptIPMaskFilterType)RNA_enum_get(op->ptr, + "filter_type"); + const int direction = RNA_enum_get(op->ptr, "direction"); + + SCULPT_undo_push_begin(ob, op); + BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true, false); + ss->filter_cache = sculpt_ipmask_filter_cache_init(ob, sd, filter_type, false); + sculpt_ipmask_store_initial_undo_step(ob); + sculpt_ipmask_store_reference_step(ss); + + const float target_step = direction == MASK_FILTER_STEP_DIRECTION_FORWARD ? 1 : -1; + if (sculpt_ipmask_filter_uses_apply_from_original(filter_type)) { + sculpt_ipmask_apply_from_original_mask_data(ob, filter_type, strength * target_step); + } + else { + sculpt_ipmask_filter_update_to_target_step(ss, target_step, iteration_count, 0.0f); + } + + SCULPT_tag_update_overlays(C); + SCULPT_filter_cache_free(ss, ob); + SCULPT_undo_push_end(ob); + SCULPT_flush_update_done(C, ob, SCULPT_UPDATE_MASK); + return OPERATOR_FINISHED; +} + +void SCULPT_OT_ipmask_filter(struct wmOperatorType *ot) +{ + /* Identifiers. */ + ot->name = "Interactive Preview Mask Filter"; + ot->idname = "SCULPT_OT_ipmask_filter"; + ot->description = "Applies a filter to modify the current mask"; + + /* API callbacks. */ + ot->exec = sculpt_ipmask_filter_exec; + ot->invoke = sculpt_ipmask_filter_invoke; + ot->modal = sculpt_ipmask_filter_modal; + ot->cancel = sculpt_ipmask_filter_cancel; + ot->poll = SCULPT_mode_poll; + + ot->flag = OPTYPE_REGISTER; + + static EnumPropertyItem prop_ipmask_filter_types[] = { + {IPMASK_FILTER_SMOOTH_SHARPEN, + "SMOOTH_SHARPEN", + 0, + "Smooth/Sharpen", + "Smooth and sharpen the mask"}, + {IPMASK_FILTER_GROW_SHRINK, "GROW_SHRINK", 0, "Grow/Shrink", "Grow and shirnk the mask"}, + {IPMASK_FILTER_HARDER_SOFTER, + "HARDER_SOFTER", + 0, + "Harder/Softer", + "Makes the entire mask harder or softer"}, + {IPMASK_FILTER_ADD_SUBSTRACT, + "ADD_SUBSTRACT", + 0, + "Add/Substract", + "Adds or substract a value to the mask"}, + {IPMASK_FILTER_CONTRAST, + "CONTRAST", + 0, + "Contrast", + "Increases or decreases the contrast of the mask"}, + {IPMASK_FILTER_INVERT, "INVERT", 0, "Invert", "Inverts the mask"}, + {IPMASK_FILTER_QUANTIZE, "QUANTIZE", 0, "Quantize", "Quantizes the mask to intervals"}, + {0, nullptr, 0, nullptr, nullptr}, + }; + + static EnumPropertyItem prop_ipmask_filter_direction_types[] = { + {MASK_FILTER_STEP_DIRECTION_FORWARD, + "FORWARD", + 0, + "Forward", + "Apply the filter in the forward direction"}, + {MASK_FILTER_STEP_DIRECTION_BACKWARD, + "BACKWARD", + 0, + "Backward", + "Apply the filter in the backward direction"}, + {0, nullptr, 0, nullptr, nullptr}, + }; + + /* RNA. */ + RNA_def_enum(ot->srna, + "filter_type", + prop_ipmask_filter_types, + IPMASK_FILTER_GROW_SHRINK, + "Type", + "Filter that is going to be applied to the mask"); + RNA_def_enum(ot->srna, + "direction", + prop_ipmask_filter_direction_types, + MASK_FILTER_STEP_DIRECTION_FORWARD, + "Direction", + "Direction to apply the filter step"); + RNA_def_int(ot->srna, + "iterations", + 1, + 1, + 100, + "Iterations per Step", + "Number of times that the filter is going to be applied per step", + 1, + 100); + RNA_def_boolean( + ot->srna, + "use_step_interpolation", + true, + "Step Interpolation", + "Calculate and render intermediate values between multiple full steps of the filter"); + RNA_def_float( + ot->srna, "strength", 1.0f, -10.0f, 10.0f, "Strength", "Filter strength", -10.0f, 10.0f); +} diff --git a/source/blender/editors/sculpt_paint/sculpt_filter_mesh.cc b/source/blender/editors/sculpt_paint/sculpt_filter_mesh.cc index 82f89c7f8f9..fe9821e02b7 100644 --- a/source/blender/editors/sculpt_paint/sculpt_filter_mesh.cc +++ b/source/blender/editors/sculpt_paint/sculpt_filter_mesh.cc @@ -229,20 +229,19 @@ void SCULPT_filter_cache_init(bContext *C, normalize_v3_v3(ss->filter_cache->view_normal, viewDir); } -void SCULPT_filter_cache_free(SculptSession *ss) +void SCULPT_filter_cache_free(SculptSession *ss, Object *ob) { if (ss->filter_cache->cloth_sim) { SCULPT_cloth_simulation_free(ss->filter_cache->cloth_sim); } if (ss->filter_cache->automasking) { - SCULPT_automasking_cache_free(ss->filter_cache->automasking); + SCULPT_automasking_cache_free(ss, nullptr, ss->filter_cache->automasking); } MEM_SAFE_FREE(ss->filter_cache->nodes); MEM_SAFE_FREE(ss->filter_cache->mask_update_it); MEM_SAFE_FREE(ss->filter_cache->prev_mask); MEM_SAFE_FREE(ss->filter_cache->normal_factor); MEM_SAFE_FREE(ss->filter_cache->prev_face_set); - MEM_SAFE_FREE(ss->filter_cache->surface_smooth_laplacian_disp); MEM_SAFE_FREE(ss->filter_cache->sharpen_factor); MEM_SAFE_FREE(ss->filter_cache->detail_directions); MEM_SAFE_FREE(ss->filter_cache->limit_surface_co); @@ -359,9 +358,14 @@ static void mesh_filter_task_cb(void *__restrict userdata, AutomaskingNodeData automask_data; SCULPT_automasking_node_begin(data->ob, ss, ss->filter_cache->automasking, &automask_data, node); + /* Smooth parameters. */ + float fset_projection = SCULPT_get_fset_projection( + ss, ss->filter_cache->preserve_fset_boundaries ? 1.0f : 0.0f); + float projection = 0.0f; + PBVHVertexIter vd; BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) { - SCULPT_orig_vert_data_update(&orig_data, &vd); + SCULPT_orig_vert_data_update(&orig_data, vd.vertex); SCULPT_automasking_node_update(ss, &automask_data, &vd); float orig_co[3], val[3], avg[3], disp[3], disp2[3], transform[3][3], final_pos[3]; @@ -395,7 +399,7 @@ static void mesh_filter_task_cb(void *__restrict userdata, switch (filter_type) { case MESH_FILTER_SMOOTH: fade = clamp_f(fade, -1.0f, 1.0f); - SCULPT_neighbor_coords_average_interior(ss, avg, vd.vertex); + SCULPT_neighbor_coords_average_interior(ss, avg, vd.vertex, projection, fset_projection); sub_v3_v3v3(val, avg, orig_co); madd_v3_v3v3fl(val, orig_co, val, fade); sub_v3_v3v3(disp, val, orig_co); @@ -444,23 +448,21 @@ static void mesh_filter_task_cb(void *__restrict userdata, break; } case MESH_FILTER_RELAX: { - SCULPT_relax_vertex(ss, &vd, clamp_f(fade, 0.0f, 1.0f), false, val); + SCULPT_relax_vertex(ss, &vd, clamp_f(fade, 0.0f, 1.0f), SCULPT_BOUNDARY_MESH, val); sub_v3_v3v3(disp, val, vd.co); break; } case MESH_FILTER_RELAX_FACE_SETS: { - SCULPT_relax_vertex(ss, &vd, clamp_f(fade, 0.0f, 1.0f), relax_face_sets, val); + eSculptBoundary boundtype = SCULPT_BOUNDARY_MESH; + if (relax_face_sets) { + boundtype |= SCULPT_BOUNDARY_FACE_SET; + } + SCULPT_relax_vertex(ss, &vd, clamp_f(fade, 0.0f, 1.0f), boundtype, val); sub_v3_v3v3(disp, val, vd.co); break; } case MESH_FILTER_SURFACE_SMOOTH: { - SCULPT_surface_smooth_laplacian_step(ss, - disp, - vd.co, - ss->filter_cache->surface_smooth_laplacian_disp, - vd.vertex, - orig_data.co, - ss->filter_cache->surface_smooth_shape_preservation); + SCULPT_surface_smooth_laplacian_step(ss, disp, vd.co, vd.vertex, orig_data.co, 1.0f); break; } case MESH_FILTER_SHARPEN: { @@ -485,7 +487,12 @@ static void mesh_filter_task_cb(void *__restrict userdata, float disp_avg[3]; float avg_co[3]; - SCULPT_neighbor_coords_average(ss, avg_co, vd.vertex); + SCULPT_neighbor_coords_average(ss, + avg_co, + vd.vertex, + projection, + ss->filter_cache->preserve_fset_boundaries ? 0.0f : 1.0f, + true); sub_v3_v3v3(disp_avg, avg_co, vd.co); mul_v3_v3fl( disp_avg, disp_avg, smooth_ratio * pow2f(ss->filter_cache->sharpen_factor[vd.index])); @@ -549,7 +556,7 @@ static void mesh_filter_enhance_details_init_directions(SculptSession *ss) PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); float avg[3]; - SCULPT_neighbor_coords_average(ss, avg, vertex); + SCULPT_neighbor_coords_average(ss, avg, vertex, 0.0f, 1.0f, true); sub_v3_v3v3(filter_cache->detail_directions[i], avg, SCULPT_vertex_co_get(ss, vertex)); } } @@ -561,8 +568,6 @@ static void mesh_filter_surface_smooth_init(SculptSession *ss, const int totvert = SCULPT_vertex_count_get(ss); FilterCache *filter_cache = ss->filter_cache; - filter_cache->surface_smooth_laplacian_disp = static_cast( - MEM_malloc_arrayN(totvert, sizeof(float[3]), __func__)); filter_cache->surface_smooth_shape_preservation = shape_preservation; filter_cache->surface_smooth_current_vertex = current_vertex_displacement; } @@ -601,7 +606,7 @@ static void mesh_filter_sharpen_init(SculptSession *ss, PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); float avg[3]; - SCULPT_neighbor_coords_average(ss, avg, vertex); + SCULPT_neighbor_coords_average(ss, avg, vertex, 0.0f, 1.0f, true); sub_v3_v3v3(filter_cache->detail_directions[i], avg, SCULPT_vertex_co_get(ss, vertex)); filter_cache->sharpen_factor[i] = len_v3(filter_cache->detail_directions[i]); } @@ -673,7 +678,6 @@ static void mesh_filter_surface_smooth_displace_task_cb(void *__restrict userdat SCULPT_surface_smooth_displace_step(ss, vd.co, - ss->filter_cache->surface_smooth_laplacian_disp, vd.vertex, ss->filter_cache->surface_smooth_current_vertex, clamp_f(fade, 0.0f, 1.0f)); @@ -691,7 +695,7 @@ static int sculpt_mesh_filter_modal(bContext *C, wmOperator *op, const wmEvent * float filter_strength = RNA_float_get(op->ptr, "strength"); if (event->type == LEFTMOUSE && event->val == KM_RELEASE) { - SCULPT_filter_cache_free(ss); + SCULPT_filter_cache_free(ss, ob); SCULPT_undo_push_end(ob); SCULPT_flush_update_done(C, ob, SCULPT_UPDATE_COORDS); return OPERATOR_FINISHED; @@ -709,6 +713,10 @@ static int sculpt_mesh_filter_modal(bContext *C, wmOperator *op, const wmEvent * bool needs_pmap = sculpt_mesh_filter_needs_pmap(filter_type); BKE_sculpt_update_object_for_edit(depsgraph, ob, needs_pmap, false, false); + if (filter_type == MESH_FILTER_SURFACE_SMOOTH) { + SCULPT_surface_smooth_laplacian_init(ob); + } + SculptThreadedTaskData data{}; data.sd = sd; data.ob = ob; @@ -770,7 +778,7 @@ static int sculpt_mesh_filter_invoke(bContext *C, wmOperator *op, const wmEvent * Filter Tool. */ float mval_fl[2] = {float(event->mval[0]), float(event->mval[1])}; SculptCursorGeometryInfo sgi; - SCULPT_cursor_geometry_info_update(C, &sgi, mval_fl, false); + SCULPT_cursor_geometry_info_update(C, &sgi, mval_fl, false, false); } SCULPT_vertex_random_access_ensure(ss); diff --git a/source/blender/editors/sculpt_paint/sculpt_geodesic.cc b/source/blender/editors/sculpt_paint/sculpt_geodesic.cc index 84232a295dd..b291ae3e933 100644 --- a/source/blender/editors/sculpt_paint/sculpt_geodesic.cc +++ b/source/blender/editors/sculpt_paint/sculpt_geodesic.cc @@ -10,9 +10,20 @@ #include "MEM_guardedalloc.h" +#include "BLI_alloca.h" +#include "BLI_blenlib.h" +#include "BLI_index_range.hh" #include "BLI_linklist_stack.h" +#include "BLI_map.hh" #include "BLI_math.h" +#include "BLI_math_vector_types.hh" +#include "BLI_memarena.h" +#include "BLI_mempool.h" +#include "BLI_set.hh" +#include "BLI_sort_utils.h" #include "BLI_task.h" +#include "BLI_utildefines.h" +#include "BLI_vector.hh" #include "DNA_brush_types.h" #include "DNA_mesh_types.h" @@ -34,13 +45,22 @@ #define SCULPT_GEODESIC_VERTEX_NONE -1 +using blender::float2; +using blender::float3; +using blender::IndexRange; +using blender::Map; +using blender::Set; +using blender::uint3; +using blender::Vector; + /* Propagate distance from v1 and v2 to v0. */ -static bool sculpt_geodesic_mesh_test_dist_add(const float (*vert_positions)[3], +static bool sculpt_geodesic_mesh_test_dist_add(float (*cos)[3], const int v0, const int v1, const int v2, float *dists, - GSet *initial_verts) + GSet *initial_verts, + PBVHVertRef *r_closest_verts) { if (BLI_gset_haskey(initial_verts, POINTER_FROM_INT(v0))) { return false; @@ -51,23 +71,201 @@ static bool sculpt_geodesic_mesh_test_dist_add(const float (*vert_positions)[3], return false; } + float *co0 = cos[v0]; + float *co1 = cos[v1]; + float *co2 = v2 != SCULPT_GEODESIC_VERTEX_NONE ? cos[v2] : nullptr; + float dist0; if (v2 != SCULPT_GEODESIC_VERTEX_NONE) { BLI_assert(dists[v2] != FLT_MAX); if (dists[v0] <= dists[v2]) { return false; } - dist0 = geodesic_distance_propagate_across_triangle( - vert_positions[v0], vert_positions[v1], vert_positions[v2], dists[v1], dists[v2]); + dist0 = geodesic_distance_propagate_across_triangle(co0, co1, co2, dists[v1], dists[v2]); } else { float vec[3]; - sub_v3_v3v3(vec, vert_positions[v1], vert_positions[v0]); + sub_v3_v3v3(vec, co1, co0); dist0 = dists[v1] + len_v3(vec); } if (dist0 < dists[v0]) { dists[v0] = dist0; + + if (r_closest_verts) { + bool tag1 = r_closest_verts[v1].i != -1LL; + bool tag2 = v2 != SCULPT_GEODESIC_VERTEX_NONE && r_closest_verts[v2].i != -1LL; + + float l1 = len_v3v3(co0, co1); + float l2 = v2 != SCULPT_GEODESIC_VERTEX_NONE ? len_v3v3(co0, co2) : 0.0f; + + if (tag1 && tag2) { + if (l1 < l2) { + r_closest_verts[v0] = r_closest_verts[v1]; + } + else { + r_closest_verts[v0] = r_closest_verts[v2]; + } + } + else if (tag2) { + r_closest_verts[v0] = r_closest_verts[v2]; + } + else if (tag1) { + r_closest_verts[v0] = r_closest_verts[v1]; + } + } + return true; + } + + return false; +} + +/* Propagate distance from v1 and v2 to v0. */ +static bool sculpt_geodesic_grids_test_dist_add(SculptSession *ss, + const int v0, + const int v1, + const int v2, + float *dists, + GSet *initial_verts, + PBVHVertRef *r_closest_verts, + float (*cos)[3]) +{ + if (BLI_gset_haskey(initial_verts, POINTER_FROM_INT(v0))) { + return false; + } + + BLI_assert(dists[v1] != FLT_MAX); + if (dists[v0] <= dists[v1]) { + return false; + } + + const float *co0 = cos ? cos[v0] : + SCULPT_vertex_co_get(ss, BKE_pbvh_index_to_vertex(ss->pbvh, v0)); + const float *co1 = cos ? cos[v1] : + SCULPT_vertex_co_get(ss, BKE_pbvh_index_to_vertex(ss->pbvh, v1)); + const float *co2 = v2 != SCULPT_GEODESIC_VERTEX_NONE ? + (cos ? cos[v2] : + SCULPT_vertex_co_get(ss, BKE_pbvh_index_to_vertex(ss->pbvh, v2))) : + nullptr; + + float dist0; + if (v2 != SCULPT_GEODESIC_VERTEX_NONE) { + BLI_assert(dists[v2] != FLT_MAX); + if (dists[v0] <= dists[v2]) { + return false; + } + dist0 = geodesic_distance_propagate_across_triangle(co0, co1, co2, dists[v1], dists[v2]); + } + else { + float vec[3]; + sub_v3_v3v3(vec, co1, co0); + dist0 = dists[v1] + len_v3(vec); + } + + if (dist0 < dists[v0]) { + dists[v0] = dist0; + + if (r_closest_verts) { + bool tag1 = r_closest_verts[v1].i != -1LL; + bool tag2 = v2 != SCULPT_GEODESIC_VERTEX_NONE && r_closest_verts[v2].i != -1LL; + + float l1 = len_v3v3(co0, co1); + float l2 = v2 != SCULPT_GEODESIC_VERTEX_NONE ? len_v3v3(co0, co2) : 0.0f; + + if (tag1 && tag2) { + if (l1 < l2) { + r_closest_verts[v0] = r_closest_verts[v1]; + } + else { + r_closest_verts[v0] = r_closest_verts[v2]; + } + } + else if (tag2) { + r_closest_verts[v0] = r_closest_verts[v2]; + } + else if (tag1) { + r_closest_verts[v0] = r_closest_verts[v1]; + } + } + return true; + } + + return false; +} + +#define BMESH_INITIAL_VERT_TAG BM_ELEM_TAG_ALT + +static bool sculpt_geodesic_mesh_test_dist_add_bmesh(BMVert *v0, + BMVert *v1, + BMVert *v2, + float *dists, + GSet *initial_verts, + PBVHVertRef *r_closest_verts, + float (*cos)[3]) +{ + const int v0_i = BM_elem_index_get(v0); + const int v1_i = BM_elem_index_get(v1); + const int v2_i = v2 ? BM_elem_index_get(v2) : SCULPT_GEODESIC_VERTEX_NONE; + + const float *v0co = cos ? cos[BM_elem_index_get(v0)] : v0->co; + const float *v1co = cos ? cos[BM_elem_index_get(v1)] : v1->co; + const float *v2co = v2 ? (cos ? cos[BM_elem_index_get(v2)] : v2->co) : nullptr; + + if (BM_elem_flag_test(v0, BMESH_INITIAL_VERT_TAG)) { + return false; + } + + BLI_assert(dists[v1_i] != FLT_MAX); + if (dists[v0_i] <= dists[v1_i]) { + return false; + } + + float dist0; + if (v2_i != SCULPT_GEODESIC_VERTEX_NONE) { + BLI_assert(dists[v2_i] != FLT_MAX); + if (dists[v0_i] <= dists[v2_i]) { + return false; + } + + dist0 = geodesic_distance_propagate_across_triangle( + v0co, v1co, v2co, dists[v1_i], dists[v2_i]); + } + else { + float vec[3]; + sub_v3_v3v3(vec, v1co, v0co); + dist0 = dists[v1_i] + len_v3(vec); + } + + if (dist0 < dists[v0_i]) { + dists[v0_i] = dist0; + + if (r_closest_verts) { + bool tag1 = r_closest_verts[v1_i].i != -1LL; + bool tag2 = v2 && r_closest_verts[v2_i].i != -1LL; + + float l1 = len_v3v3(v0co, v1co); + float l2 = v2 ? len_v3v3(v0co, v2co) : 0.0f; + + if (!tag1 && !tag2) { + printf("bad\n"); + } + + if (tag1 && tag2) { + if (l1 < l2) { // dists[v1_i] < dists[v2_i]) { + r_closest_verts[v0_i] = r_closest_verts[v1_i]; + } + else { + r_closest_verts[v0_i] = r_closest_verts[v2_i]; + } + } + else if (tag2) { + r_closest_verts[v0_i] = r_closest_verts[v2_i]; + } + else if (tag1) { + r_closest_verts[v0_i] = r_closest_verts[v1_i]; + } + } + return true; } @@ -76,7 +274,9 @@ static bool sculpt_geodesic_mesh_test_dist_add(const float (*vert_positions)[3], static float *SCULPT_geodesic_mesh_create(Object *ob, GSet *initial_verts, - const float limit_radius) + const float limit_radius, + PBVHVertRef *r_closest_verts, + float (*cos)[3]) { SculptSession *ss = ob->sculpt; Mesh *mesh = BKE_object_get_original_mesh(ob); @@ -86,7 +286,10 @@ static float *SCULPT_geodesic_mesh_create(Object *ob, const float limit_radius_sq = limit_radius * limit_radius; - float(*vert_positions)[3] = SCULPT_mesh_deformed_positions_get(ss); + if (!cos) { + cos = SCULPT_mesh_deformed_positions_get(ss); + } + const MEdge *edges = BKE_mesh_edges(mesh); const MPoly *polys = BKE_mesh_polys(mesh); const MLoop *loops = BKE_mesh_loops(mesh); @@ -99,7 +302,15 @@ static float *SCULPT_geodesic_mesh_create(Object *ob, &ss->epmap, &ss->epmap_mem, mesh->totedge, polys, mesh->totpoly, loops, mesh->totloop); } if (!ss->vemap) { - BKE_mesh_vert_edge_map_create(&ss->vemap, &ss->vemap_mem, edges, mesh->totvert, mesh->totedge); + const float(*vert_positions)[3] = BKE_mesh_vert_positions(mesh); + + BKE_mesh_vert_edge_map_create(&ss->vemap, + &ss->vemap_mem, + vert_positions, + mesh->medge, + mesh->totvert, + mesh->totedge, + true); } /* Both contain edge indices encoded as *void. */ @@ -111,32 +322,41 @@ static float *SCULPT_geodesic_mesh_create(Object *ob, for (int i = 0; i < totvert; i++) { if (BLI_gset_haskey(initial_verts, POINTER_FROM_INT(i))) { + if (r_closest_verts) { + r_closest_verts[i] = BKE_pbvh_index_to_vertex(ss->pbvh, i); + } + dists[i] = 0.0f; } else { + if (r_closest_verts) { + r_closest_verts[i].i = -1LL; + } + dists[i] = FLT_MAX; } } - /* Masks vertices that are further than limit radius from an initial vertex. As there is no need + /* Masks verts that are further than limit radius from an initial vertex. As there is no need * to define a distance to them the algorithm can stop earlier by skipping them. */ BLI_bitmap *affected_vertex = BLI_BITMAP_NEW(totvert, "affected vertex"); GSetIterator gs_iter; if (limit_radius == FLT_MAX) { - /* In this case, no need to loop through all initial vertices to check distances as they are + /* In this case, no need to loop through all initial verts to check distances as they are * all going to be affected. */ BLI_bitmap_set_all(affected_vertex, true, totvert); } else { /* This is an O(n^2) loop used to limit the geodesic distance calculation to a radius. When * this optimization is needed, it is expected for the tool to request the distance to a low - * number of vertices (usually just 1 or 2). */ + * number of verts (usually just 1 or 2). */ GSET_ITER (gs_iter, initial_verts) { const int v = POINTER_AS_INT(BLI_gsetIterator_getKey(&gs_iter)); - float *v_co = vert_positions[v]; + float *v_co = cos[v]; + for (int i = 0; i < totvert; i++) { - if (len_squared_v3v3(v_co, vert_positions[i]) <= limit_radius_sq) { + if (len_squared_v3v3(v_co, cos[i]) <= limit_radius_sq) { BLI_BITMAP_ENABLE(affected_vertex, i); } } @@ -166,7 +386,7 @@ static float *SCULPT_geodesic_mesh_create(Object *ob, SWAP(int, v1, v2); } sculpt_geodesic_mesh_test_dist_add( - vert_positions, v2, v1, SCULPT_GEODESIC_VERTEX_NONE, dists, initial_verts); + cos, v2, v1, SCULPT_GEODESIC_VERTEX_NONE, dists, initial_verts, r_closest_verts); } if (ss->epmap[e].count != 0) { @@ -184,7 +404,7 @@ static float *SCULPT_geodesic_mesh_create(Object *ob, continue; } if (sculpt_geodesic_mesh_test_dist_add( - vert_positions, v_other, v1, v2, dists, initial_verts)) { + cos, v_other, v1, v2, dists, initial_verts, nullptr)) { for (int edge_map_index = 0; edge_map_index < ss->vemap[v_other].count; edge_map_index++) { const int e_other = ss->vemap[v_other].indices[edge_map_index]; @@ -216,6 +436,204 @@ static float *SCULPT_geodesic_mesh_create(Object *ob, BLI_BITMAP_DISABLE(edge_tag, e); } + BLI_LINKSTACK_SWAP(queue, queue_next); + } while (BLI_LINKSTACK_SIZE(queue)); + + BLI_LINKSTACK_FREE(queue); + BLI_LINKSTACK_FREE(queue_next); + MEM_SAFE_FREE(edge_tag); + MEM_SAFE_FREE(affected_vertex); + + return dists; +} + +static float *SCULPT_geodesic_bmesh_create(Object *ob, + GSet *initial_verts, + const float limit_radius, + PBVHVertRef *r_closest_verts, + float (*cos)[3]) +{ + SculptSession *ss = ob->sculpt; + + if (!ss->bm) { + return nullptr; + } + + BM_mesh_elem_index_ensure(ss->bm, BM_VERT | BM_EDGE | BM_FACE); + + const int totvert = ss->bm->totvert; + const int totedge = ss->bm->totedge; + + if (r_closest_verts) { + for (int i = 0; i < totvert; i++) { + r_closest_verts[i].i = -1LL; + } + } + + const float limit_radius_sq = limit_radius * limit_radius; + + float *dists = static_cast(MEM_malloc_arrayN(totvert, sizeof(float), "distances")); + BLI_bitmap *edge_tag = BLI_BITMAP_NEW(totedge, "edge tag"); + + BLI_LINKSTACK_DECLARE(queue, BMEdge *); + BLI_LINKSTACK_DECLARE(queue_next, BMEdge *); + + BLI_LINKSTACK_INIT(queue); + BLI_LINKSTACK_INIT(queue_next); + + for (int i = 0; i < totvert; i++) { + if (BLI_gset_haskey(initial_verts, POINTER_FROM_INT(i))) { + dists[i] = 0.0f; + + if (r_closest_verts) { + r_closest_verts[i] = BKE_pbvh_index_to_vertex(ss->pbvh, i); + } + } + else { + dists[i] = FLT_MAX; + } + } + + /* Masks verts that are further than limit radius from an initial vertex. As there is no + * need to define a distance to them the algorithm can stop earlier by skipping them. */ + BLI_bitmap *affected_vertex = BLI_BITMAP_NEW(totvert, "affected vertex"); + GSetIterator gs_iter; + + BMVert *v; + BMIter iter; + + BM_ITER_MESH (v, &iter, ss->bm, BM_VERTS_OF_MESH) { + BM_elem_flag_disable(v, BMESH_INITIAL_VERT_TAG); + } + + if (limit_radius == FLT_MAX) { + /* In this case, no need to loop through all initial verts to check distances as they are + * all going to be affected. */ + BLI_bitmap_set_all(affected_vertex, true, totvert); + } + else { + /* This is an O(n^2) loop used to limit the geodesic distance calculation to a radius. + * When this optimization is needed, it is expected for the tool to request the distance + * to a low number of verts (usually just 1 or 2). */ + GSET_ITER (gs_iter, initial_verts) { + const int v_i = POINTER_AS_INT(BLI_gsetIterator_getKey(&gs_iter)); + BMVert *v = (BMVert *)BKE_pbvh_index_to_vertex(ss->pbvh, v_i).i; + float *co1 = cos ? cos[BM_elem_index_get(v)] : v->co; + + BM_elem_flag_enable(v, BMESH_INITIAL_VERT_TAG); + + for (int i = 0; i < totvert; i++) { + BMVert *v2 = (BMVert *)BKE_pbvh_index_to_vertex(ss->pbvh, i).i; + float *co2 = cos ? cos[BM_elem_index_get(v2)] : v2->co; + + if (len_squared_v3v3(co1, co2) <= limit_radius_sq) { + BLI_BITMAP_ENABLE(affected_vertex, i); + } + } + } + } + + BMEdge *e; + int i = 0; + + BM_ITER_MESH (e, &iter, ss->bm, BM_EDGES_OF_MESH) { + const int v1_i = BM_elem_index_get(e->v1); + const int v2_i = BM_elem_index_get(e->v2); + + if (!BLI_BITMAP_TEST(affected_vertex, v1_i) && !BLI_BITMAP_TEST(affected_vertex, v2_i)) { + i++; + continue; + } + if (dists[v1_i] != FLT_MAX || dists[v2_i] != FLT_MAX) { + BLI_LINKSTACK_PUSH(queue, e); + } + + i++; + } + + do { + while (BLI_LINKSTACK_SIZE(queue)) { + BMEdge *e = BLI_LINKSTACK_POP(queue); + + BMVert *v1 = e->v1, *v2 = e->v2; + int v1_i = BM_elem_index_get(e->v1); + int v2_i = BM_elem_index_get(e->v2); + + if (dists[v1_i] == FLT_MAX || dists[v2_i] == FLT_MAX) { + if (dists[v1_i] > dists[v2_i]) { + SWAP(BMVert *, v1, v2); + SWAP(int, v1_i, v2_i); + } + sculpt_geodesic_mesh_test_dist_add_bmesh( + v2, v1, nullptr, dists, initial_verts, r_closest_verts, cos); + } + + BMLoop *l = e->l; + if (l) { + do { + BMFace *f = l->f; + BMLoop *l2 = f->l_first; + + if (BM_ELEM_CD_GET_INT(f, ss->cd_faceset_offset) < 0) { + l = l->radial_next; + continue; + } + + do { + BMVert *v_other = l2->v; + + if (ELEM(v_other, v1, v2)) { + l2 = l2->next; + continue; + } + + const int v_other_i = BM_elem_index_get(v_other); + + if (sculpt_geodesic_mesh_test_dist_add_bmesh( + v_other, v1, v2, dists, initial_verts, r_closest_verts, cos)) { + BMIter eiter; + BMEdge *e_other; + + BM_ITER_ELEM (e_other, &eiter, v_other, BM_EDGES_OF_VERT) { + BMVert *ev_other; + + if (e_other->v1 == v_other) { + ev_other = e_other->v2; + } + else { + ev_other = e_other->v1; + } + + const int ev_other_i = BM_elem_index_get(ev_other); + const int e_other_i = BM_elem_index_get(e_other); + + bool ok = e_other != e && !BLI_BITMAP_TEST(edge_tag, e_other_i); + ok = ok && (!e_other->l || dists[ev_other_i] != FLT_MAX); + ok = ok && (BLI_BITMAP_TEST(affected_vertex, v_other_i) || + BLI_BITMAP_TEST(affected_vertex, ev_other_i)); + + if (ok) { + BLI_BITMAP_ENABLE(edge_tag, e_other_i); + BLI_LINKSTACK_PUSH(queue_next, e_other); + } + } + } + + l2 = l2->next; + } while (l2 != f->l_first); + + l = l->radial_next; + } while (l != e->l); + } + } + + for (LinkNode *lnk = queue_next; lnk; lnk = lnk->next) { + BMEdge *e = (BMEdge *)lnk->link; + const int e_i = BM_elem_index_get(e); + + BLI_BITMAP_DISABLE(edge_tag, e_i); + } + BLI_LINKSTACK_SWAP(queue, queue_next); } while (BLI_LINKSTACK_SIZE(queue)); @@ -228,8 +646,295 @@ static float *SCULPT_geodesic_mesh_create(Object *ob, return dists; } +BLI_INLINE void *hash_edge(int v1, int v2, int totvert) +{ + if (v1 > v2) { + SWAP(int, v1, v2); + } + + intptr_t ret = (intptr_t)v1 + (intptr_t)v2 * (intptr_t)totvert; + return (void *)ret; +} + +typedef struct TempEdge { + int v1, v2; +} TempEdge; + +int find_quad(TempEdge *edges, MeshElemMap *vmap, int v1, int v2, int v3) +{ + for (int i = 0; i < vmap[v1].count; i++) { + TempEdge *te = edges + vmap[v1].indices[i]; + int v = v1 == te->v1 ? te->v2 : te->v1; + + if (v == v2) { + continue; + } + + for (int j = 0; j < vmap[v].count; j++) { + TempEdge *te2 = edges + vmap[v].indices[j]; + int v4 = v == te2->v1 ? te2->v2 : te2->v1; + + if (v4 == v3) { + return v; + } + } + } + + return -1; +} + +static float *SCULPT_geodesic_grids_create(Object *ob, + GSet *initial_verts, + const float limit_radius, + PBVHVertRef *r_closest_verts, + float (*cos)[3]) +{ + SculptSession *ss = ob->sculpt; + + const int totvert = SCULPT_vertex_count_get(ss); + + const float limit_radius_sq = limit_radius * limit_radius; + + float *dists = static_cast(MEM_malloc_arrayN(totvert, sizeof(float), "distances")); + + /* Both contain edge indices encoded as *void. */ + BLI_LINKSTACK_DECLARE(queue, void *); + BLI_LINKSTACK_DECLARE(queue_next, void *); + + BLI_LINKSTACK_INIT(queue); + BLI_LINKSTACK_INIT(queue_next); + + for (int i = 0; i < totvert; i++) { + if (BLI_gset_haskey(initial_verts, POINTER_FROM_INT(i))) { + if (r_closest_verts) { + r_closest_verts[i] = BKE_pbvh_index_to_vertex(ss->pbvh, i); + } + + dists[i] = 0.0f; + } + else { + if (r_closest_verts) { + r_closest_verts[i].i = -1LL; + } + + dists[i] = FLT_MAX; + } + } + + /* Masks verts that are further than limit radius from an initial vertex. As there is no + * need to define a distance to them the algorithm can stop earlier by skipping them. */ + BLI_bitmap *affected_vertex = BLI_BITMAP_NEW(totvert, "affected vertex"); + GSetIterator gs_iter; + + if (limit_radius == FLT_MAX) { + /* In this case, no need to loop through all initial verts to check distances as they are + * all going to be affected. */ + BLI_bitmap_set_all(affected_vertex, true, totvert); + } + else { + /* This is an O(n^2) loop used to limit the geodesic distance calculation to a radius. + * When this optimization is needed, it is expected for the tool to request the distance + * to a low number of verts (usually just 1 or 2). */ + GSET_ITER (gs_iter, initial_verts) { + const int v = POINTER_AS_INT(BLI_gsetIterator_getKey(&gs_iter)); + PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, v); + const float *v_co = cos ? cos[v] : SCULPT_vertex_co_get(ss, vertex); + + for (int i = 0; i < totvert; i++) { + const float *v_co2 = cos ? cos[i] : + SCULPT_vertex_co_get(ss, BKE_pbvh_index_to_vertex(ss->pbvh, i)); + if (len_squared_v3v3(v_co, v_co2) <= limit_radius_sq) { + BLI_BITMAP_ENABLE(affected_vertex, i); + } + } + } + } + + SculptVertexNeighborIter ni; + + Vector edges; + + GHash *ehash = BLI_ghash_ptr_new("geodesic multigrids ghash"); + + MeshElemMap *vmap = static_cast( + MEM_calloc_arrayN(totvert, sizeof(*vmap), "geodesic grids vmap")); + + int totedge = 0; + MemArena *ma = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, "geodesic grids memarena"); + + for (int i = 0; i < totvert; i++) { + PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); + MeshElemMap *map = vmap + i; + + int val = SCULPT_vertex_valence_get(ss, vertex); + map->count = val; + map->indices = (int *)BLI_memarena_alloc(ma, sizeof(int) * val); + + int j = 0; + + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vertex, ni) { + void *ekey = hash_edge(i, ni.index, totvert); + void **val; + + if (!BLI_ghash_ensure_p(ehash, ekey, &val)) { + *val = POINTER_FROM_INT(totedge); + + TempEdge te = {i, ni.index}; + edges.append(te); + totedge++; + } + + map->indices[j] = POINTER_AS_INT(*val); + j++; + } + SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); + } + + int(*e_otherv_map)[4] = static_cast( + MEM_malloc_arrayN(totedge, sizeof(*e_otherv_map), "e_otherv_map")); + + // create an edge map of opposite edge verts in (up to 2) adjacent faces + for (int i = 0; i < totedge; i++) { + int v1a = -1, v2a = -1; + int v1b = -1, v2b = -1; + + TempEdge *te = &edges[i]; + + for (int j = 0; j < vmap[te->v1].count; j++) { + TempEdge *te2 = &edges[vmap[te->v1].indices[j]]; + int v3 = te->v1 == te2->v1 ? te2->v2 : te2->v1; + + if (v3 == te->v2) { + continue; + } + + int p = find_quad(edges.data(), vmap, te->v1, te->v2, v3); + + if (p != -1) { + v1a = p; + v1b = v3; + } + } + + for (int j = 0; j < vmap[te->v2].count; j++) { + TempEdge *te2 = &edges[vmap[te->v2].indices[j]]; + int v3 = te->v2 == te2->v1 ? te2->v2 : te2->v1; + + if (v3 == te->v1) { + continue; + } + + int p = find_quad(edges.data(), vmap, te->v1, te->v2, v3); + + if (p != -1) { + if (v1a != -1) { + v2a = p; + v2b = v3; + } + else { + v1a = p; + v1b = v3; + } + } + } + + e_otherv_map[i][0] = v1a; + e_otherv_map[i][1] = v1b; + e_otherv_map[i][2] = v2a; + e_otherv_map[i][3] = v2b; + } + + BLI_bitmap *edge_tag = BLI_BITMAP_NEW(totedge, "edge tag"); + + /* Add edges adjacent to an initial vertex to the queue. */ + for (int i = 0; i < totedge; i++) { + const int v1 = edges[i].v1; + const int v2 = edges[i].v2; + + if (!BLI_BITMAP_TEST(affected_vertex, v1) && !BLI_BITMAP_TEST(affected_vertex, v2)) { + continue; + } + if (dists[v1] != FLT_MAX || dists[v2] != FLT_MAX) { + BLI_LINKSTACK_PUSH(queue, POINTER_FROM_INT(i)); + } + } + + do { + while (BLI_LINKSTACK_SIZE(queue)) { + const int e = POINTER_AS_INT(BLI_LINKSTACK_POP(queue)); + int v1 = edges[e].v1; + int v2 = edges[e].v2; + + if (dists[v1] == FLT_MAX || dists[v2] == FLT_MAX) { + if (dists[v1] > dists[v2]) { + SWAP(int, v1, v2); + } + sculpt_geodesic_grids_test_dist_add( + ss, v2, v1, SCULPT_GEODESIC_VERTEX_NONE, dists, initial_verts, r_closest_verts, cos); + } + + for (int pi = 0; pi < 4; pi++) { + int v_other = e_otherv_map[e][pi]; + + if (v_other == -1) { + continue; + } + + // XXX not sure how to handle face sets here - joeedh + // if (ss->face_sets[poly] <= 0) { + // continue; + //} + + if (sculpt_geodesic_grids_test_dist_add( + ss, v_other, v1, v2, dists, initial_verts, r_closest_verts, cos)) { + for (int edge_map_index = 0; edge_map_index < vmap[v_other].count; edge_map_index++) { + const int e_other = vmap[v_other].indices[edge_map_index]; + int ev_other; + if (edges[e_other].v1 == (uint)v_other) { + ev_other = edges[e_other].v2; + } + else { + ev_other = edges[e_other].v1; + } + + if (e_other != e && !BLI_BITMAP_TEST(edge_tag, e_other) && + (dists[ev_other] != FLT_MAX)) { + if (BLI_BITMAP_TEST(affected_vertex, v_other) || + BLI_BITMAP_TEST(affected_vertex, ev_other)) { + BLI_BITMAP_ENABLE(edge_tag, e_other); + BLI_LINKSTACK_PUSH(queue_next, POINTER_FROM_INT(e_other)); + } + } + } + } + } + } + + for (LinkNode *lnk = queue_next; lnk; lnk = lnk->next) { + const int e = POINTER_AS_INT(lnk->link); + BLI_BITMAP_DISABLE(edge_tag, e); + } + + BLI_LINKSTACK_SWAP(queue, queue_next); + + } while (BLI_LINKSTACK_SIZE(queue)); + + BLI_LINKSTACK_FREE(queue); + BLI_LINKSTACK_FREE(queue_next); + MEM_SAFE_FREE(edge_tag); + MEM_SAFE_FREE(affected_vertex); + + BLI_memarena_free(ma); + BLI_ghash_free(ehash, nullptr, nullptr); + MEM_SAFE_FREE(edges); + MEM_SAFE_FREE(vmap); + MEM_SAFE_FREE(e_otherv_map); + + return dists; +} + /* For sculpt mesh data that does not support a geodesic distances algorithm, fallback to the - * distance to each vertex. In this case, only one of the initial vertices will be used to + * distance to each vertex. In this case, only one of the initial verts will be used to * calculate the distance. */ static float *SCULPT_geodesic_fallback_create(Object *ob, GSet *initial_verts) { @@ -239,6 +944,7 @@ static float *SCULPT_geodesic_fallback_create(Object *ob, GSet *initial_verts) const int totvert = mesh->totvert; float *dists = static_cast(MEM_malloc_arrayN(totvert, sizeof(float), __func__)); int first_affected = SCULPT_GEODESIC_VERTEX_NONE; + GSetIterator gs_iter; GSET_ITER (gs_iter, initial_verts) { first_affected = POINTER_AS_INT(BLI_gsetIterator_getKey(&gs_iter)); @@ -255,29 +961,37 @@ static float *SCULPT_geodesic_fallback_create(Object *ob, GSet *initial_verts) const float *first_affected_co = SCULPT_vertex_co_get( ss, BKE_pbvh_index_to_vertex(ss->pbvh, first_affected)); for (int i = 0; i < totvert; i++) { - PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); - - dists[i] = len_v3v3(first_affected_co, SCULPT_vertex_co_get(ss, vertex)); + dists[i] = len_v3v3(first_affected_co, + SCULPT_vertex_co_get(ss, BKE_pbvh_index_to_vertex(ss->pbvh, i))); } return dists; } -float *SCULPT_geodesic_distances_create(Object *ob, GSet *initial_verts, const float limit_radius) +float *SCULPT_geodesic_distances_create(Object *ob, + GSet *initial_verts, + const float limit_radius, + PBVHVertRef *r_closest_verts, + float (*vertco_override)[3]) { SculptSession *ss = ob->sculpt; switch (BKE_pbvh_type(ss->pbvh)) { case PBVH_FACES: - return SCULPT_geodesic_mesh_create(ob, initial_verts, limit_radius); + return SCULPT_geodesic_mesh_create( + ob, initial_verts, limit_radius, r_closest_verts, vertco_override); case PBVH_BMESH: + return SCULPT_geodesic_bmesh_create( + ob, initial_verts, limit_radius, r_closest_verts, vertco_override); case PBVH_GRIDS: - return SCULPT_geodesic_fallback_create(ob, initial_verts); + return SCULPT_geodesic_grids_create( + ob, initial_verts, limit_radius, r_closest_verts, vertco_override); + // return SCULPT_geodesic_fallback_create(ob, initial_verts); } BLI_assert(false); return nullptr; } -float *SCULPT_geodesic_from_vertex_and_symm(Sculpt *sd, +float *SCULPT_geodesic_from_vertex_and_symm(struct Sculpt *sd, Object *ob, const PBVHVertRef vertex, const float limit_radius) @@ -304,17 +1018,25 @@ float *SCULPT_geodesic_from_vertex_and_symm(Sculpt *sd, } } - float *dists = SCULPT_geodesic_distances_create(ob, initial_verts, limit_radius); + float *dists = SCULPT_geodesic_distances_create( + ob, initial_verts, limit_radius, nullptr, nullptr); BLI_gset_free(initial_verts, nullptr); return dists; } float *SCULPT_geodesic_from_vertex(Object *ob, const PBVHVertRef vertex, const float limit_radius) { + SculptSession *ss = ob->sculpt; + + SCULPT_vertex_random_access_ensure(ss); + GSet *initial_verts = BLI_gset_int_new("initial_verts"); - BLI_gset_add(initial_verts, - POINTER_FROM_INT(BKE_pbvh_vertex_to_index(ob->sculpt->pbvh, vertex))); - float *dists = SCULPT_geodesic_distances_create(ob, initial_verts, limit_radius); + + BLI_gset_add(initial_verts, POINTER_FROM_INT(BKE_pbvh_vertex_to_index(ss->pbvh, vertex))); + + float *dists = SCULPT_geodesic_distances_create( + ob, initial_verts, limit_radius, nullptr, nullptr); BLI_gset_free(initial_verts, nullptr); + return dists; } diff --git a/source/blender/editors/sculpt_paint/sculpt_gradient.c b/source/blender/editors/sculpt_paint/sculpt_gradient.c new file mode 100644 index 00000000000..408e38b93ff --- /dev/null +++ b/source/blender/editors/sculpt_paint/sculpt_gradient.c @@ -0,0 +1,263 @@ +/* + * 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) 2020 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup edsculpt + */ + +#include "MEM_guardedalloc.h" + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" + +#include "BLI_blenlib.h" +#include "BLI_hash.h" +#include "BLI_math.h" +#include "BLI_math_color_blend.h" +#include "BLI_task.h" + +#include "BKE_brush.h" +#include "BKE_colortools.h" +#include "BKE_context.h" +#include "BKE_mesh.h" +#include "BKE_mesh_mapping.h" +#include "BKE_object.h" +#include "BKE_paint.h" +#include "BKE_pbvh.h" +#include "BKE_scene.h" + +#include "IMB_colormanagement.h" + +#include "DEG_depsgraph.h" + +#include "WM_api.h" +#include "WM_message.h" +#include "WM_toolsystem.h" +#include "WM_types.h" + +#include "ED_object.h" +#include "ED_screen.h" +#include "ED_sculpt.h" +#include "paint_intern.h" +#include "sculpt_intern.h" + +#include "RNA_access.h" +#include "RNA_define.h" + +#include "UI_interface.h" + +#include "bmesh.h" + +#include +#include + +static EnumPropertyItem prop_sculpt_gradient_type[] = { + {SCULPT_GRADIENT_LINEAR, "LINEAR", 0, "Linear", ""}, + {SCULPT_GRADIENT_SPHERICAL, "SPHERICAL", 0, "Spherical", ""}, + {SCULPT_GRADIENT_RADIAL, "RADIAL", 0, "Radial", ""}, + {SCULPT_GRADIENT_ANGLE, "ANGLE", 0, "Angle", ""}, + {SCULPT_GRADIENT_REFLECTED, "REFLECTED", 0, "Reflected", ""}, + {0, NULL, 0, NULL, NULL}, +}; + +static void sculpt_gradient_apply_task_cb(void *__restrict userdata, + const int n, + const TaskParallelTLS *__restrict UNUSED(tls)) +{ + SculptThreadedTaskData *data = userdata; + SculptSession *ss = data->ob->sculpt; + Sculpt *sd = data->sd; + SculptGradientContext *gcontext = ss->filter_cache->gradient_context; + + SculptOrigVertData orig_data; + AutomaskingNodeData automask_data; + + SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COORDS); + SCULPT_automasking_node_begin( + data->ob, ss, ss->filter_cache->automasking, &automask_data, data->nodes[n]); + + PBVHVertexIter vd; + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { + SCULPT_automasking_node_update(ss, &automask_data, &vd); + + float fade = vd.mask ? *vd.mask : 0.0f; + fade *= SCULPT_automasking_factor_get( + ss->filter_cache->automasking, ss, vd.vertex, &automask_data); + if (fade == 0.0f) { + continue; + } + + float world_co[3]; + float projected_co[3]; + + /* TODO: Implement symmetry by flipping this coordinate. */ + float symm_co[3]; + copy_v3_v3(symm_co, vd.co); + + mul_v3_m4v3(world_co, data->ob->object_to_world, symm_co); + /* TOOD: Implement this again. */ + /* ED_view3d_project(gcontext->vc.region, world_co, projected_co); */ + + float gradient_value = 0.0f; + switch (gcontext->gradient_type) { + case SCULPT_GRADIENT_LINEAR: + + break; + case SCULPT_GRADIENT_SPHERICAL: + + break; + case SCULPT_GRADIENT_RADIAL: { + const float dist = len_v2v2(projected_co, gcontext->line_points[0]); + gradient_value = dist / gcontext->line_length; + } break; + case SCULPT_GRADIENT_ANGLE: + break; + case SCULPT_GRADIENT_REFLECTED: + + break; + } + + gradient_value = clamp_f(gradient_value, 0.0f, 1.0f); + gcontext->sculpt_gradient_apply_for_element(sd, ss, &orig_data, &vd, gradient_value, fade); + if (vd.is_mesh) { + BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); + } + } + BKE_pbvh_vertex_iter_end; + gcontext->sculpt_gradient_node_update(data->nodes[n]); +} + +static int sculpt_gradient_update_exec(bContext *C, wmOperator *op, const wmEvent *event) +{ + Object *ob = CTX_data_active_object(C); + SculptSession *ss = ob->sculpt; + Sculpt *sd = CTX_data_tool_settings(C)->sculpt; + SculptGradientContext *gcontext = ss->filter_cache->gradient_context; + + if (event->type != MOUSEMOVE) { + return OPERATOR_RUNNING_MODAL; + } + + gcontext->line_points[0][0] = RNA_int_get(op->ptr, "xstart"); + gcontext->line_points[0][1] = RNA_int_get(op->ptr, "ystart"); + gcontext->line_points[1][0] = RNA_int_get(op->ptr, "xend"); + gcontext->line_points[1][1] = RNA_int_get(op->ptr, "yend"); + gcontext->line_length = len_v2v2(gcontext->line_points[0], gcontext->line_points[1]); + + SculptThreadedTaskData data = { + .sd = sd, + .ob = ob, + .nodes = ss->filter_cache->nodes, + }; + + TaskParallelSettings settings; + BLI_parallel_range_settings_defaults(&settings); + + BKE_pbvh_parallel_range_settings(&settings, true, ss->filter_cache->totnode); + BLI_task_parallel_range( + 0, ss->filter_cache->totnode, &data, sculpt_gradient_apply_task_cb, &settings); + + SCULPT_flush_update_step(C, ss->filter_cache->gradient_context->update_type); + + return OPERATOR_RUNNING_MODAL; +} + +static void sculpt_gradient_properties(wmOperatorType *ot) +{ + RNA_def_enum( + ot->srna, "type", prop_sculpt_gradient_type, SCULPT_GRADIENT_LINEAR, "Gradient Type", ""); +} + +static void sculpt_gradient_context_init_common(bContext *C, + wmOperator *op, + const wmEvent *event, + SculptGradientContext *gcontext) +{ + /* View Context. */ + Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); + ED_view3d_viewcontext_init(C, &gcontext->vc, depsgraph); + + /* Properties */ + gcontext->gradient_type = RNA_enum_get(op->ptr, "type"); + gcontext->strength = RNA_float_get(op->ptr, "strength"); + + /* Symmetry. */ + Object *ob = gcontext->vc.obact; + gcontext->symm = SCULPT_mesh_symmetry_xyz_get(ob); + + /* Depth */ + SculptCursorGeometryInfo sgi; + float mouse[2] = {event->mval[0], event->mval[1]}; + const bool hit = SCULPT_cursor_geometry_info_update(C, &sgi, mouse, false, false); + if (hit) { + copy_v3_v3(gcontext->depth_point, sgi.location); + } + else { + zero_v3(gcontext->depth_point); + } +} + +static SculptGradientContext *sculpt_mask_gradient_context_create(Object *ob, wmOperator *op) +{ + SculptGradientContext *gradient_context = MEM_callocN(sizeof(SculptGradientContext), + "gradient context"); + gradient_context->update_type = SCULPT_UPDATE_MASK; + return gradient_context; +} + +static int sculpt_mask_gradient_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + Object *ob = CTX_data_active_object(C); + SculptSession *ss = ob->sculpt; + Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); + Sculpt *sd = CTX_data_tool_settings(C)->sculpt; + + SCULPT_vertex_random_access_ensure(ss); + BKE_sculpt_update_object_for_edit(depsgraph, ob, false, true, false); + + // XXX get area_normal_radius argument properly + SCULPT_filter_cache_init(C, ob, sd, SCULPT_UNDO_MASK, event->mval, 0.25f); + + ss->filter_cache->gradient_context = sculpt_mask_gradient_context_create(ob, op); + sculpt_gradient_context_init_common(C, op, event, ss->filter_cache->gradient_context); + + return WM_gesture_straightline_invoke(C, op, event); +} + +void SCULPT_OT_mask_gradient(struct wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Mask Gradient"; + ot->idname = "SCULPT_OT_mask_gradient"; + ot->description = "Creates or modifies the mask using a gradient"; + + /* api callbacks */ + /* + ot->invoke = WM_gesture_straightline_invoke; + ot->modal = WM_gesture_straightline_modal; + ot->exec = sculpt_gradient_update_exec; + + ot->poll = SCULPT_mode_poll; + */ + + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* rna */ + sculpt_gradient_properties(ot); +} diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.hh b/source/blender/editors/sculpt_paint/sculpt_intern.hh index 37e7a522ccf..dee1d6f5498 100644 --- a/source/blender/editors/sculpt_paint/sculpt_intern.hh +++ b/source/blender/editors/sculpt_paint/sculpt_intern.hh @@ -14,8 +14,6 @@ #include "DNA_scene_types.h" #include "DNA_vec_types.h" -#include "BKE_paint.h" -#include "BKE_pbvh.h" #include "BLI_bitmap.h" #include "BLI_compiler_attrs.h" #include "BLI_compiler_compat.h" @@ -24,6 +22,14 @@ #include "ED_view3d.h" +#include "BKE_attribute.h" +#include "BKE_paint.h" +#include "BKE_pbvh.h" + +#include "bmesh.h" + +extern "C" { + struct AutomaskingCache; struct AutomaskingNodeData; struct Dial; @@ -35,6 +41,10 @@ struct Object; struct SculptProjectVector; struct SculptUndoNode; struct bContext; +struct BrushChannelSet; +struct TaskParallelTLS; + +enum ePaintSymmetryFlags; struct PaintModeSettings; struct WeightPaintInfo; struct WPaintData; @@ -42,12 +52,22 @@ struct wmKeyConfig; struct wmOperator; struct wmOperatorType; +/* +maximum symmetry passes returned by SCULPT_get_symmetry_pass. +enough for about ~30 radial symmetry passes, which seems like plenty + +used by various code that needs to statically store per-pass state. +*/ +#define SCULPT_MAX_SYMMETRY_PASSES 255 + /* Updates */ /* -------------------------------------------------------------------- */ /** \name Sculpt Types * \{ */ +enum { SCULPT_SHARP_SIMPLE, SCULPT_SHARP_PLANE }; + enum SculptUpdateType { SCULPT_UPDATE_COORDS = 1 << 0, SCULPT_UPDATE_MASK = 1 << 1, @@ -58,20 +78,26 @@ enum SculptUpdateType { struct SculptCursorGeometryInfo { float location[3]; + float back_location[3]; float normal[3]; float active_vertex_co[3]; }; -#define SCULPT_VERTEX_NEIGHBOR_FIXED_CAPACITY 256 +struct _SculptNeighborRef { + PBVHVertRef vertex; + PBVHEdgeRef edge; +}; + +#define SCULPT_VERTEX_NEIGHBOR_FIXED_CAPACITY 12 struct SculptVertexNeighborIter { /* Storage */ - PBVHVertRef *neighbors; + struct _SculptNeighborRef *neighbors; int *neighbor_indices; + int size; int capacity; - - PBVHVertRef neighbors_fixed[SCULPT_VERTEX_NEIGHBOR_FIXED_CAPACITY]; + struct _SculptNeighborRef neighbors_fixed[SCULPT_VERTEX_NEIGHBOR_FIXED_CAPACITY]; int neighbor_indices_fixed[SCULPT_VERTEX_NEIGHBOR_FIXED_CAPACITY]; /* Internal iterator. */ @@ -79,31 +105,48 @@ struct SculptVertexNeighborIter { int i; /* Public */ - int index; PBVHVertRef vertex; + PBVHEdgeRef edge; + int index; + bool has_edge; // does this iteration step have an edge, fake neighbors do not bool is_duplicate; + bool no_free; +}; + +struct SculptFaceSetIsland { + PBVHFaceRef *faces; + int totface; +}; + +struct SculptFaceSetIslands { + SculptFaceSetIsland *islands; + int totisland; }; /* Sculpt Original Data */ struct SculptOrigVertData { - struct BMLog *bm_log; + BMLog *bm_log; - SculptUndoNode *unode; + struct SculptUndoNode *unode; + int datatype; float (*coords)[3]; float (*normals)[3]; const float *vmasks; float (*colors)[4]; + float _no[3]; /* Original coordinate, normal, and mask. */ const float *co; const float *no; float mask; const float *col; + struct PBVH *pbvh; + struct SculptSession *ss; }; struct SculptOrigFaceData { - SculptUndoNode *unode; - struct BMLog *bm_log; + struct SculptUndoNode *unode; + BMLog *bm_log; const int *face_sets; int face_set; }; @@ -119,19 +162,18 @@ enum eBoundaryAutomaskMode { AUTOMASK_INIT_BOUNDARY_FACE_SETS = 2, }; -/* Undo */ - enum SculptUndoType { - SCULPT_UNDO_COORDS, - SCULPT_UNDO_HIDDEN, - SCULPT_UNDO_MASK, - SCULPT_UNDO_DYNTOPO_BEGIN, - SCULPT_UNDO_DYNTOPO_END, - SCULPT_UNDO_DYNTOPO_SYMMETRIZE, - SCULPT_UNDO_GEOMETRY, - SCULPT_UNDO_FACE_SETS, - SCULPT_UNDO_COLOR, + SCULPT_UNDO_COORDS = 1 << 0, + SCULPT_UNDO_HIDDEN = 1 << 1, + SCULPT_UNDO_MASK = 1 << 2, + SCULPT_UNDO_DYNTOPO_BEGIN = 1 << 3, + SCULPT_UNDO_DYNTOPO_END = 1 << 4, + SCULPT_UNDO_DYNTOPO_SYMMETRIZE = 1 << 5, + SCULPT_UNDO_GEOMETRY = 1 << 6, + SCULPT_UNDO_FACE_SETS = 1 << 7, + SCULPT_UNDO_COLOR = 1 << 8, }; +ENUM_OPERATORS(SculptUndoType, SCULPT_UNDO_COLOR); /* Storage of geometry for the undo node. * Is used as a storage for either original or modified geometry. */ @@ -171,7 +213,7 @@ struct SculptUndoNode { /* non-multires */ int maxvert; /* to verify if totvert it still the same */ - int *index; /* Unique vertex indices, to restore into right location */ + int *index; /* to restore into right location */ int maxloop; int *loop_index; @@ -186,6 +228,7 @@ struct SculptUndoNode { /* bmesh */ BMLogEntry *bm_entry; + BMLog *bm_log; bool applied; /* shape keys */ @@ -201,9 +244,6 @@ struct SculptUndoNode { SculptUndoNodeGeometry geometry_original; SculptUndoNodeGeometry geometry_modified; - /* Geometry at the bmesh enter moment. */ - SculptUndoNodeGeometry geometry_bmesh_enter; - /* pivot */ float pivot_pos[3]; float pivot_rot[4]; @@ -211,10 +251,17 @@ struct SculptUndoNode { /* Sculpt Face Sets */ int *face_sets; + // dyntopo stuff + + int *nodemap; + int nodemap_size; + int typemask; + PBVHFaceRef *faces; int faces_num; size_t undo_size; + // int gen, lasthash; }; /* Factor of brush to have rake point following behind @@ -232,11 +279,13 @@ struct SculptRakeData { * altogether after moving to C++ (where we'll be able to use lambdas). */ struct SculptThreadedTaskData { - bContext *C; - Sculpt *sd; - Object *ob; - const Brush *brush; - PBVHNode **nodes; + struct bContext *C; + struct Sculpt *sd; + struct Object *ob; + struct SculptSession *ss; + const struct Brush *brush; + struct PBVHNode **nodes; + int totnode; VPaint *vp; @@ -268,12 +317,12 @@ struct SculptThreadedTaskData { float (*mat)[4]; float (*vertCos)[3]; - /* When true, the displacement stored in the proxies will be applied to the original coordinates - * instead of to the current coordinates. */ + /* When true, the displacement stored in the proxies will be applied to the original + * coordinates instead of to the current coordinates. */ bool use_proxies_orco; - /* X and Z vectors aligned to the stroke direction for operations where perpendicular vectors to - * the stroke direction are needed. */ + /* X and Z vectors aligned to the stroke direction for operations where perpendicular vectors + * to the stroke direction are needed. */ float (*stroke_xz)[3]; int filter_type; @@ -292,8 +341,12 @@ struct SculptThreadedTaskData { bool any_vertex_sampled; float *wet_mix_sampled_color; + float hue_offset; float *prev_mask; + float *new_mask; + float *next_mask; + float mask_interpolation; float *pose_factor; float *pose_initial_co; @@ -338,13 +391,28 @@ struct SculptThreadedTaskData { PBVHVertRef mask_by_color_vertex; float *mask_by_color_floodfill; - int face_set; + int face_set, face_set2; int filter_undo_type; int mask_init_mode; int mask_init_seed; ThreadMutex mutex; + + // Layer brush + int cd_temp, cd_temp2, cd_temp3, cd_sculpt_vert; + + float smooth_projection; + float rake_projection; + SculptAttribute *scl, *scl2; + bool do_origco; + float *brush_color; + + float fset_slide, bound_smooth; + float crease_pinch_factor; + bool use_curvature; + float vel_smooth_fac; + int iterations; int iteration; }; @@ -359,6 +427,12 @@ struct SculptBrushTest { int radial_symmetry_pass; float symm_rot_mat_inv[4][4]; + float tip_roundness; + float tip_scale_x; + bool test_cube_z; + + float cube_matrix[4][4]; + /* For circle (not sphere) projection. */ float plane_view[4]; @@ -367,6 +441,7 @@ struct SculptBrushTest { /* View3d clipping - only set rv3d for clipping */ RegionView3D *clip_rv3d; + char falloff_shape; }; using SculptBrushTestFn = bool (*)(SculptBrushTest *test, const float co[3]); @@ -379,6 +454,8 @@ struct SculptSearchSphereData { bool original; /* This ignores fully masked and fully hidden nodes. */ bool ignore_fully_ineffective; + struct Object *ob; + struct Brush *brush; }; struct SculptSearchCircleData { @@ -406,16 +483,20 @@ enum SculptTransformDisplacementMode { }; #define SCULPT_CLAY_STABILIZER_LEN 10 +#define SCULPT_SPEED_MA_SIZE 4 +#define GRAB_DELTA_MA_SIZE 3 struct AutomaskingSettings { /* Flags from eAutomasking_flag. */ int flags; int initial_face_set; + int current_face_set; // used by faceset draw tool + bool original_normal; int initial_island_nr; float cavity_factor; int cavity_blur_steps; - CurveMapping *cavity_curve; + struct CurveMapping *cavity_curve; float start_normal_limit, start_normal_falloff; float view_normal_limit, view_normal_falloff; @@ -430,17 +511,58 @@ struct AutomaskingCache { uchar current_stroke_id; }; +enum eSculptGradientType { + SCULPT_GRADIENT_LINEAR, + SCULPT_GRADIENT_SPHERICAL, + SCULPT_GRADIENT_RADIAL, + SCULPT_GRADIENT_ANGLE, + SCULPT_GRADIENT_REFLECTED, +}; +struct SculptGradientContext { + eSculptGradientType gradient_type; + ViewContext vc; + + int symm; + + int update_type; + float line_points[2][2]; + + float line_length; + + float depth_point[3]; + + float gradient_plane[4]; + float initial_location[3]; + + float gradient_line[3]; + float initial_projected_location[2]; + + float strength; + void (*sculpt_gradient_begin)(struct bContext *); + + void (*sculpt_gradient_apply_for_element)(struct Sculpt *, + struct SculptSession *, + SculptOrigVertData *orig_data, + PBVHVertexIter *vd, + float gradient_value, + float fade_value); + void (*sculpt_gradient_node_update)(struct PBVHNode *); + void (*sculpt_gradient_end)(struct bContext *); +}; + +/* IPMask filter vertex callback function. */ +typedef float(SculptIPMaskFilterStepVertexCB)(struct SculptSession *, PBVHVertRef, float *); + struct FilterCache { bool enabled_axis[3]; bool enabled_force_axis[3]; int random_seed; - /* Used for alternating between filter operations in filters that need to apply different ones to - * achieve certain effects. */ + /* Used for alternating between filter operations in filters that need to apply different ones + * to achieve certain effects. */ int iteration_count; /* Stores the displacement produced by the laplacian step of HC smooth. */ - float (*surface_smooth_laplacian_disp)[3]; float surface_smooth_shape_preservation; float surface_smooth_current_vertex; @@ -451,6 +573,10 @@ struct FilterCache { float *sharpen_factor; float (*detail_directions)[3]; + /* Sphere mesh filter. */ + float sphere_center[3]; + float sphere_radius; + /* Filter orientation. */ SculptFilterOrientation orientation; float obmat[4][4]; @@ -483,14 +609,32 @@ struct FilterCache { int active_face_set; + /* Transform. */ SculptTransformDisplacementMode transform_displacement_mode; + /* Gradient. */ + SculptGradientContext *gradient_context; + /* Auto-masking. */ AutomaskingCache *automasking; float initial_normal[3]; float view_normal[3]; - /* Pre-smoothed colors used by sharpening. Colors are HSL. */ + /* Mask Filter. */ + int mask_filter_current_step; + float *mask_filter_ref; + SculptIPMaskFilterStepVertexCB *mask_filter_step_forward; + SculptIPMaskFilterStepVertexCB *mask_filter_step_backward; + + GHash *mask_delta_step; + + bool preserve_fset_boundaries; + bool weighted_smooth; + float hard_edge_fac; + bool hard_edge_mode; + float bound_smooth_radius; + float bevel_smooth_fac; + float (*pre_smoothed_color)[4]; ViewContext vc; @@ -516,7 +660,6 @@ struct StrokeCache { float true_last_location[3]; float location[3]; float last_location[3]; - float stroke_distance; /* Used for alternating between deformation in brushes that need to apply different ones to * achieve certain effects. */ @@ -556,35 +699,50 @@ struct StrokeCache { float projection_mat[4][4]; /* Clean this up! */ - ViewContext *vc; - const Brush *brush; + struct ViewContext *vc; + struct Brush *brush; float special_rotation; float grab_delta[3], grab_delta_symmetry[3]; float old_grab_location[3], orig_grab_location[3]; + // next_grab_delta is same as grab_delta except in smooth rake mode + float prev_grab_delta[3], next_grab_delta[3]; + float prev_grab_delta_symmetry[3], next_grab_delta_symmetry[3]; + float grab_delta_avg[GRAB_DELTA_MA_SIZE][3]; + int grab_delta_avg_cur; + /* screen-space rotation defined by mouse motion */ float rake_rotation[4], rake_rotation_symmetry[4]; bool is_rake_rotation_valid; SculptRakeData rake_data; + /* Geodesic distances. */ + float *geodesic_dists[PAINT_SYMM_AREAS]; + /* Face Sets */ int paint_face_set; /* Symmetry index between 0 and 7 bit combo 0 is Brush only; * 1 is X mirror; 2 is Y mirror; 3 is XY; 4 is Z; 5 is XZ; 6 is YZ; 7 is XYZ */ int symmetry; + ePaintSymmetryFlags boundary_symmetry; // controls splitting face sets by mirror axis ePaintSymmetryFlags mirror_symmetry_pass; /* The symmetry pass we are currently on between 0 and 7. */ float true_view_normal[3]; float view_normal[3]; + float view_origin[3]; + float true_view_origin[3]; + /* sculpt_normal gets calculated by calc_sculpt_normal(), then the * sculpt_normal_symm gets updated quickly with the usual symmetry * transforms */ float sculpt_normal[3]; float sculpt_normal_symm[3]; + float cached_area_normal[3]; + /* Used for area texture mode, local_mat gets calculated by * calc_brush_local_mat() and used in tex_strength(). */ float brush_local_mat[4][4]; @@ -599,6 +757,8 @@ struct StrokeCache { bool original; float anchored_location[3]; + /* Fairing. */ + /* Paint Brush. */ struct { float hardness; @@ -631,12 +791,9 @@ struct StrokeCache { /* Boundary brush */ SculptBoundary *boundaries[PAINT_SYMM_AREAS]; - /* Surface Smooth Brush */ - /* Stores the displacement produced by the laplacian step of HC smooth. */ - float (*surface_smooth_laplacian_disp)[3]; - /* Layer brush */ float *layer_displacement_factor; + int *layer_stroke_id; float vertex_rotation; /* amount to rotate the vertices when using rotate brush */ Dial *dial; @@ -646,6 +803,10 @@ struct StrokeCache { int saved_smooth_size; /* smooth tool copies the size of the current tool */ bool alt_smooth; + /* Scene Project Brush */ + struct SnapObjectContext *snap_context; + struct Depsgraph *depsgraph; + float plane_trim_squared; bool supports_gravity; @@ -664,13 +825,43 @@ struct StrokeCache { rcti previous_r; /* previous redraw rectangle */ rcti current_r; /* current redraw rectangle */ + float stroke_distance; // copy of PaintStroke->stroke_distance + float stroke_distance_t; // copy of PaintStroke->stroke_distance_t + float stroke_spacing_t; + float last_stroke_distance_t; + + float last_dyntopo_t; + float last_smooth_t[SCULPT_MAX_SYMMETRY_PASSES]; + float last_rake_t[SCULPT_MAX_SYMMETRY_PASSES]; + + int layer_disp_map_size; + BLI_bitmap *layer_disp_map; + + struct PaintStroke *stroke; + struct bContext *C; + + struct BrushCommandList *commandlist; + bool use_plane_trim; + + struct NeighborCache *ncache; + float speed_avg[SCULPT_SPEED_MA_SIZE]; // moving average for speed + int speed_avg_cur; + double last_speed_time; + + // if nonzero, override brush sculpt tool + int tool_override; + + float mouse_cubic[4][3]; + float world_cubic[4][3]; + float world_cubic_arclength; + float mouse_cubic_arclength; + bool has_cubic; int stroke_id; }; /* -------------------------------------------------------------------- */ /** \name Sculpt Expand * \{ */ - enum eSculptExpandFalloffType { SCULPT_EXPAND_FALLOFF_GEODESIC, SCULPT_EXPAND_FALLOFF_TOPOLOGY, @@ -713,19 +904,20 @@ struct ExpandCache { float *face_falloff; float max_face_falloff; - /* Falloff value of the active element (vertex or base mesh face) that Expand will expand to. */ + /* Falloff value of the active element (vertex or base mesh face) that Expand will expand to. + */ float active_falloff; - /* When set to true, expand skips all falloff computations and considers all elements as enabled. + /* When set to true, expand skips all falloff computations and considers all elements as + * enabled. */ bool all_enabled; - /* Initial mouse and cursor data from where the current falloff started. This data can be changed - * during the execution of Expand by moving the origin. */ + /* Initial mouse and cursor data from where the current falloff started. This data can be + * changed during the execution of Expand by moving the origin. */ float initial_mouse_move[2]; float initial_mouse[2]; PBVHVertRef initial_active_vertex; - int initial_active_vertex_i; int initial_active_face_set; /* Maximum number of vertices allowed in the SculptSession for previewing the falloff using @@ -781,6 +973,9 @@ struct ExpandCache { /* When set to true, preserves the previous state of the data and adds the new one on top. */ bool preserve; + /* When true, preserve mode will flip in inverse mode */ + bool preserve_flip_inverse; + /* When set to true, the mask or colors will be applied as a gradient. */ bool falloff_gradient; @@ -799,8 +994,8 @@ struct ExpandCache { * instead of creating a new one. */ bool modify_active_face_set; - /* When set to true, Expand will reposition the sculpt pivot to the boundary of the expand result - * after finishing the operation. */ + /* When set to true, Expand will reposition the sculpt pivot to the boundary of the expand + * result after finishing the operation. */ bool reposition_pivot; /* If nothing is masked set mask of every vertex to 0. */ @@ -811,10 +1006,10 @@ struct ExpandCache { short blend_mode; /* Face Sets at the first step of the expand operation, before starting modifying the active - * vertex and active falloff. These are not the original Face Sets of the sculpt before starting - * the operator as they could have been modified by Expand when initializing the operator and - * before starting changing the active vertex. These Face Sets are used for restoring and - * checking the Face Sets state while the Expand operation modal runs. */ + * vertex and active falloff. These are not the original Face Sets of the sculpt before + * starting the operator as they could have been modified by Expand when initializing the + * operator and before starting changing the active vertex. These Face Sets are used for + * restoring and checking the Face Sets state while the Expand operation modal runs. */ int *initial_face_sets; /* Original data of the sculpt as it was before running the Expand operator. */ @@ -825,7 +1020,44 @@ struct ExpandCache { bool check_islands; int normal_falloff_blur_steps; }; -/** \} */ + +struct MaskFilterDeltaStep { + int totelem; + int *index; + float *delta; +}; + +struct SculptCurvatureData { + float ks[3]; + float principle[3][3]; // normalized +}; + +struct SculptFaceSetDrawData { + struct Sculpt *sd; + struct Object *ob; + PBVHNode **nodes; + int totnode; + struct Brush *brush; + float bstrength; + + int faceset; + int count; + bool use_fset_curve; + bool use_fset_strength; + + float *prev_stroke_direction; + float *stroke_direction; + float *next_stroke_direction; + struct CurveMapping *curve; + int iteration; +}; + +enum eDynTopoWarnFlag { + DYNTOPO_WARN_EDATA = (1 << 1), + DYNTOPO_WARN_MODIFIER = (1 << 3), + DYNTOPO_ERROR_MULTIRES = (1 << 4) +}; +ENUM_OPERATORS(eDynTopoWarnFlag, DYNTOPO_ERROR_MULTIRES); /** \} */ @@ -862,7 +1094,12 @@ bool SCULPT_handles_colors_report(SculptSession *ss, ReportList *reports); void SCULPT_flush_update_step(bContext *C, SculptUpdateType update_flags); void SCULPT_flush_update_done(const bContext *C, Object *ob, SculptUpdateType update_flags); -void SCULPT_pbvh_clear(Object *ob); +enum PBVHClearFlags { + PBVH_CLEAR_CACHE_PBVH = 1 << 1, + PBVH_CLEAR_FREE_BMESH = 1 << 2, +}; + +void SCULPT_pbvh_clear(Object *ob, bool cache_pbvh); /** * Flush displacement from deformed PBVH to original layer. @@ -891,14 +1128,16 @@ bool SCULPT_stroke_get_location(bContext *C, const float mouse[2], bool force_original); /** - * Gets the normal, location and active vertex location of the geometry under the cursor. This also - * updates the active vertex and cursor related data of the SculptSession using the mouse position + * Gets the normal, location and active vertex location of the geometry under the cursor. This + * also updates the active vertex and cursor related data of the SculptSession using the mouse + * position */ bool SCULPT_cursor_geometry_info_update(bContext *C, SculptCursorGeometryInfo *out, const float mouse[2], - bool use_sampled_normal); -void SCULPT_geometry_preview_lines_update(bContext *C, SculptSession *ss, float radius); + bool use_sampled_normal, + bool use_back_depth); +void SCULPT_geometry_preview_lines_update(bContext *C, struct SculptSession *ss, float radius); void SCULPT_stroke_modifiers_check(const bContext *C, Object *ob, const Brush *brush); float SCULPT_raycast_init(ViewContext *vc, @@ -920,8 +1159,8 @@ bool SCULPT_stroke_is_main_symmetry_pass(StrokeCache *cache); * Return true only once per stroke on the first symmetry pass, regardless of the symmetry passes * enabled. * - * This should be used for functionality that needs to be computed once per stroke of a particular - * tool (allocating memory, updating random seeds...). + * This should be used for functionality that needs to be computed once per stroke of a + * particular tool (allocating memory, updating random seeds...). */ bool SCULPT_stroke_is_first_brush_step(StrokeCache *cache); /** @@ -938,11 +1177,19 @@ bool SCULPT_stroke_is_first_brush_step_of_symmetry_pass(StrokeCache *cache); /** Ensure random access; required for PBVH_BMESH */ void SCULPT_vertex_random_access_ensure(SculptSession *ss); -int SCULPT_vertex_count_get(SculptSession *ss); -const float *SCULPT_vertex_co_get(SculptSession *ss, PBVHVertRef vertex); +/** Ensure random access; required for PBVH_BMESH */ +void SCULPT_face_random_access_ensure(struct SculptSession *ss); -/** Get the normal for a given sculpt vertex; do not modify the result */ +int SCULPT_vertex_valence_get(const struct SculptSession *ss, PBVHVertRef vertex); +int SCULPT_vertex_count_get(const struct SculptSession *ss); + +const float *SCULPT_vertex_co_get(struct SculptSession *ss, PBVHVertRef vertex); void SCULPT_vertex_normal_get(SculptSession *ss, PBVHVertRef vertex, float no[3]); +float *SCULPT_vertex_origco_get(SculptSession *ss, PBVHVertRef vertex); +float *SCULPT_vertex_origno_get(SculptSession *ss, PBVHVertRef vertex); + +const float *SCULPT_vertex_persistent_co_get(SculptSession *ss, PBVHVertRef vertex); +void SCULPT_vertex_persistent_normal_get(SculptSession *ss, PBVHVertRef vertex, float no[3]); float SCULPT_vertex_mask_get(SculptSession *ss, PBVHVertRef vertex); void SCULPT_vertex_color_get(const SculptSession *ss, PBVHVertRef vertex, float r_color[4]); @@ -956,8 +1203,7 @@ bool SCULPT_has_colors(const SculptSession *ss); /** Returns true if the active color attribute is on loop (ATTR_DOMAIN_CORNER) domain. */ bool SCULPT_has_loop_colors(const Object *ob); -const float *SCULPT_vertex_persistent_co_get(SculptSession *ss, PBVHVertRef vertex); -void SCULPT_vertex_persistent_normal_get(SculptSession *ss, PBVHVertRef vertex, float no[3]); +bool SCULPT_has_persistent_base(SculptSession *ss); /** * Coordinates used for manipulating the base mesh when Grab Active Vertex is enabled. @@ -978,34 +1224,50 @@ float *SCULPT_brush_deform_target_vertex_co_get(SculptSession *ss, int deform_target, PBVHVertexIter *iter); -void SCULPT_vertex_neighbors_get(SculptSession *ss, - PBVHVertRef vertex, - bool include_duplicates, +void SCULPT_vertex_neighbors_get(const struct SculptSession *ss, + const PBVHVertRef vref, + const bool include_duplicates, SculptVertexNeighborIter *iter); -/** Iterator over neighboring vertices. */ +/* Iterator over neighboring vertices. */ #define SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN(ss, v_index, neighbor_iterator) \ SCULPT_vertex_neighbors_get(ss, v_index, false, &neighbor_iterator); \ for (neighbor_iterator.i = 0; neighbor_iterator.i < neighbor_iterator.size; \ neighbor_iterator.i++) { \ - neighbor_iterator.vertex = neighbor_iterator.neighbors[neighbor_iterator.i]; \ + neighbor_iterator.has_edge = neighbor_iterator.neighbors[neighbor_iterator.i].edge.i != \ + PBVH_REF_NONE; \ + neighbor_iterator.vertex = neighbor_iterator.neighbors[neighbor_iterator.i].vertex; \ + neighbor_iterator.edge = neighbor_iterator.neighbors[neighbor_iterator.i].edge; \ neighbor_iterator.index = neighbor_iterator.neighbor_indices[neighbor_iterator.i]; -/** Iterate over neighboring and duplicate vertices (for PBVH_GRIDS). Duplicates come +/* Iterate over neighboring and duplicate vertices (for PBVH_GRIDS). Duplicates come * first since they are nearest for floodfill. */ #define SCULPT_VERTEX_DUPLICATES_AND_NEIGHBORS_ITER_BEGIN(ss, v_index, neighbor_iterator) \ SCULPT_vertex_neighbors_get(ss, v_index, true, &neighbor_iterator); \ for (neighbor_iterator.i = neighbor_iterator.size - 1; neighbor_iterator.i >= 0; \ neighbor_iterator.i--) { \ - neighbor_iterator.vertex = neighbor_iterator.neighbors[neighbor_iterator.i]; \ + neighbor_iterator.has_edge = neighbor_iterator.neighbors[neighbor_iterator.i].edge.i != \ + PBVH_REF_NONE; \ + neighbor_iterator.vertex = neighbor_iterator.neighbors[neighbor_iterator.i].vertex; \ + neighbor_iterator.edge = neighbor_iterator.neighbors[neighbor_iterator.i].edge; \ neighbor_iterator.index = neighbor_iterator.neighbor_indices[neighbor_iterator.i]; \ neighbor_iterator.is_duplicate = (neighbor_iterator.i >= \ neighbor_iterator.size - neighbor_iterator.num_duplicates); #define SCULPT_VERTEX_NEIGHBORS_ITER_END(neighbor_iterator) \ } \ - if (neighbor_iterator.neighbors != neighbor_iterator.neighbors_fixed) { \ + if (!neighbor_iterator.no_free && \ + neighbor_iterator.neighbors != neighbor_iterator.neighbors_fixed) { \ MEM_freeN(neighbor_iterator.neighbors); \ + MEM_freeN(neighbor_iterator.neighbor_indices); \ + } \ + ((void)0) + +#define SCULPT_VERTEX_NEIGHBORS_ITER_FREE(neighbor_iterator) \ + if (neighbor_iterator.neighbors && !neighbor_iterator.no_free && \ + neighbor_iterator.neighbors != neighbor_iterator.neighbors_fixed) { \ + MEM_freeN(neighbor_iterator.neighbors); \ + MEM_freeN(neighbor_iterator.neighbor_indices); \ } \ ((void)0) @@ -1028,8 +1290,16 @@ void SCULPT_fake_neighbors_free(Object *ob); /* Vertex Info. */ void SCULPT_boundary_info_ensure(Object *object); + /* Boundary Info needs to be initialized in order to use this function. */ -bool SCULPT_vertex_is_boundary(const SculptSession *ss, PBVHVertRef vertex); +eSculptCorner SCULPT_vertex_is_corner(const SculptSession *ss, + const PBVHVertRef index, + eSculptCorner cornertype); + +/* Boundary Info needs to be initialized in order to use this function. */ +eSculptBoundary SCULPT_vertex_is_boundary(const SculptSession *ss, + const PBVHVertRef index, + eSculptBoundary boundary_types); /** \} */ @@ -1049,6 +1319,23 @@ void SCULPT_visibility_sync_all_from_faces(Object *ob); /** \} */ +/* -------------------------------------------------------------------- */ +/** \name Face API + * \{ */ + +PBVHEdgeRef sculpt_poly_loop_initial_edge_from_cursor(Object *ob); +BLI_bitmap *sculpt_poly_loop_from_cursor(struct Object *ob); + +SculptFaceSetIslands *SCULPT_face_set_islands_get(SculptSession *ss, int fset); +void SCULPT_face_set_islands_free(SculptSession *ss, SculptFaceSetIslands *islands); + +SculptFaceSetIsland *SCULPT_face_set_island_get(SculptSession *ss, PBVHFaceRef face, int fset); +void SCULPT_face_set_island_free(SculptFaceSetIsland *island); + +void SCULPT_face_normal_get(SculptSession *ss, PBVHFaceRef face, float no[3]); + +/** \} */ + /* -------------------------------------------------------------------- */ /** \name Face Sets API * \{ */ @@ -1061,19 +1348,26 @@ int SCULPT_face_set_get(const SculptSession *ss, PBVHFaceRef face); void SCULPT_face_set_set(SculptSession *ss, PBVHFaceRef face, int fset); bool SCULPT_vertex_has_face_set(SculptSession *ss, PBVHVertRef vertex, int face_set); -bool SCULPT_vertex_has_unique_face_set(SculptSession *ss, PBVHVertRef vertex); +bool SCULPT_vertex_has_unique_face_set(const SculptSession *ss, PBVHVertRef vertex); int SCULPT_face_set_next_available_get(SculptSession *ss); void SCULPT_face_set_visibility_set(SculptSession *ss, int face_set, bool visible); +int SCULPT_face_set_original_get(SculptSession *ss, PBVHFaceRef face); + +bool SCULPT_face_select_get(SculptSession *ss, PBVHFaceRef face); + /** \} */ /* -------------------------------------------------------------------- */ /** \name Original Data API * \{ */ +MSculptVert *SCULPT_vertex_get_sculptvert(const SculptSession *ss, PBVHVertRef vertex); + /** + * DEPRECATED: use SCULPT_vertex_check_origdata and SCULPT_vertex_get_sculptvert * Initialize a #SculptOrigVertData for accessing original vertex data; * handles #BMesh, #Mesh, and multi-resolution. */ @@ -1082,14 +1376,28 @@ void SCULPT_orig_vert_data_init(SculptOrigVertData *data, PBVHNode *node, SculptUndoType type); /** + * DEPRECATED: use SCULPT_vertex_check_origdata and SCULPT_vertex_get_sculptvert * Update a #SculptOrigVertData for a particular vertex from the PBVH iterator. */ -void SCULPT_orig_vert_data_update(SculptOrigVertData *orig_data, PBVHVertexIter *iter); +void SCULPT_orig_vert_data_update(SculptOrigVertData *orig_data, PBVHVertRef vertex); + /** + * DEPRECATED: use SCULPT_vertex_check_origdata and SCULPT_vertex_get_sculptvert * Initialize a #SculptOrigVertData for accessing original vertex data; * handles #BMesh, #Mesh, and multi-resolution. */ -void SCULPT_orig_vert_data_unode_init(SculptOrigVertData *data, Object *ob, SculptUndoNode *unode); +void SCULPT_orig_vert_data_unode_init(SculptOrigVertData *data, + Object *ob, + struct SculptUndoNode *unode); + +void SCULPT_face_check_origdata(SculptSession *ss, PBVHFaceRef face); +bool SCULPT_vertex_check_origdata(SculptSession *ss, PBVHVertRef vertex); + +/** check that original face set values are up to date + * TODO: rename to SCULPT_face_set_original_ensure + */ +void SCULPT_face_ensure_original(SculptSession *ss, struct Object *ob); + /** * Initialize a #SculptOrigFaceData for accessing original face data; * handles #BMesh, #Mesh, and multi-resolution. @@ -1191,9 +1499,19 @@ void SCULPT_flip_quat_by_symm_area(float quat[4], const float pivot[3]); /** - * Initialize a point-in-brush test + * Initialize a point-in-brush test with a given falloff shape. + * + * \param falloff_shape: #PAINT_FALLOFF_SHAPE_SPHERE, #PAINT_FALLOFF_SHAPE_TUBE or + * #PAINT_FALLOFF_SHAPE_NOOP \return The brush falloff function, or nullptr if falloff_shape was + * #PAINT_FALLOFF_SHAPE_NOOP */ -void SCULPT_brush_test_init(SculptSession *ss, SculptBrushTest *test); + +SculptBrushTestFn SCULPT_brush_test_init(const SculptSession *ss, SculptBrushTest *test); +SculptBrushTestFn SCULPT_brush_test_init_ex(const SculptSession *ss, + SculptBrushTest *test, + char falloff_shape, + float tip_roundness, + float tip_scale_x); bool SCULPT_brush_test_sphere(SculptBrushTest *test, const float co[3]); bool SCULPT_brush_test_sphere_sq(SculptBrushTest *test, const float co[3]); @@ -1201,8 +1519,10 @@ bool SCULPT_brush_test_sphere_fast(const SculptBrushTest *test, const float co[3 bool SCULPT_brush_test_cube(SculptBrushTest *test, const float co[3], const float local[4][4], - float roundness); + float roundness, + bool test_z); bool SCULPT_brush_test_circle_sq(SculptBrushTest *test, const float co[3]); +bool SCULPT_brush_test(SculptBrushTest *test, const float co[3]); /** * Test AABB against sphere. */ @@ -1221,10 +1541,10 @@ void SCULPT_combine_transform_proxies(Sculpt *sd, Object *ob); * \return The brush falloff function. */ -SculptBrushTestFn SCULPT_brush_test_init_with_falloff_shape(SculptSession *ss, +SculptBrushTestFn SCULPT_brush_test_init_with_falloff_shape(const SculptSession *ss, SculptBrushTest *test, char falloff_shape); -const float *SCULPT_brush_frontface_normal_from_falloff_shape(SculptSession *ss, +const float *SCULPT_brush_frontface_normal_from_falloff_shape(const SculptSession *ss, char falloff_shape); /** @@ -1264,11 +1584,14 @@ void SCULPT_floodfill_add_initial_with_symmetry(Sculpt *sd, Object *ob, SculptSession *ss, SculptFloodFill *flood, - PBVHVertRef vertex, + PBVHVertRef index, float radius); -void SCULPT_floodfill_add_initial(SculptFloodFill *flood, PBVHVertRef vertex); -void SCULPT_floodfill_add_and_skip_initial(SculptFloodFill *flood, PBVHVertRef vertex); -void SCULPT_floodfill_execute(SculptSession *ss, + +void SCULPT_floodfill_add_initial(SculptFloodFill *flood, PBVHVertRef index); +void SCULPT_floodfill_add_and_skip_initial(struct SculptSession *ss, + SculptFloodFill *flood, + PBVHVertRef vertex); +void SCULPT_floodfill_execute(struct SculptSession *ss, SculptFloodFill *flood, bool (*func)(SculptSession *ss, PBVHVertRef from_v, @@ -1284,14 +1607,6 @@ void SCULPT_floodfill_free(SculptFloodFill *flood); /** \name Dynamic topology * \{ */ -enum eDynTopoWarnFlag { - DYNTOPO_WARN_VDATA = (1 << 0), - DYNTOPO_WARN_EDATA = (1 << 1), - DYNTOPO_WARN_LDATA = (1 << 2), - DYNTOPO_WARN_MODIFIER = (1 << 3), -}; -ENUM_OPERATORS(eDynTopoWarnFlag, DYNTOPO_WARN_MODIFIER); - /** Enable dynamic topology; mesh will be triangulated */ void SCULPT_dynamic_topology_enable_ex(Main *bmain, Depsgraph *depsgraph, @@ -1313,12 +1628,15 @@ void sculpt_dynamic_topology_disable_with_undo(Main *bmain, */ bool SCULPT_stroke_is_dynamic_topology(const SculptSession *ss, const Brush *brush); -void SCULPT_dynamic_topology_triangulate(BMesh *bm); +void SCULPT_dynamic_topology_triangulate(struct SculptSession *ss, struct BMesh *bm); +void SCULPT_dynamic_topology_sync_layers(Object *ob, struct Mesh *me); enum eDynTopoWarnFlag SCULPT_dynamic_topology_check(Scene *scene, Object *ob); /** \} */ +void SCULPT_combine_transform_proxies(Sculpt *sd, Object *ob); + /* -------------------------------------------------------------------- */ /** \name Auto-masking. * \{ */ @@ -1348,21 +1666,29 @@ float SCULPT_automasking_factor_get(AutomaskingCache *automasking, PBVHVertRef vertex, AutomaskingNodeData *automask_data); -/* Returns the automasking cache depending on the active tool. Used for code that can run both for - * brushes and filter. */ -AutomaskingCache *SCULPT_automasking_active_cache_get(SculptSession *ss); +/* Returns the automasking cache depending on the active tool. Used for code that can run both + * for brushes and filter. */ +struct AutomaskingCache *SCULPT_automasking_active_cache_get(SculptSession *ss); /* Brush can be null. */ -AutomaskingCache *SCULPT_automasking_cache_init(Sculpt *sd, Brush *brush, Object *ob); -void SCULPT_automasking_cache_free(AutomaskingCache *automasking); +struct AutomaskingCache *SCULPT_automasking_cache_init(Sculpt *sd, const Brush *brush, Object *ob); +void SCULPT_automasking_cache_free(SculptSession *ss, + Object *ob, + struct AutomaskingCache *automasking); -bool SCULPT_is_automasking_mode_enabled(const Sculpt *sd, const Brush *br, eAutomasking_flag mode); +bool SCULPT_is_automasking_mode_enabled(const SculptSession *ss, + const Sculpt *sd, + const Brush *br, + const eAutomasking_flag mode); bool SCULPT_is_automasking_enabled(const Sculpt *sd, const SculptSession *ss, const Brush *br); +void SCULPT_automasking_cache_settings_update(AutomaskingCache *automasking, + SculptSession *ss, + const Sculpt *sd, + const Brush *brush); -float *SCULPT_boundary_automasking_init(Object *ob, - eBoundaryAutomaskMode mode, - int propagation_steps, - float *automask_factor); +void SCULPT_boundary_automasking_init(Object *ob, + eBoundaryAutomaskMode mode, + int propagation_steps); bool SCULPT_automasking_needs_normal(const SculptSession *ss, const Sculpt *sculpt, const Brush *brush); @@ -1376,17 +1702,22 @@ int SCULPT_automasking_settings_hash(Object *ob, AutomaskingCache *automasking); * \{ */ /** - * Returns an array indexed by vertex index containing the geodesic distance to the closest vertex - * in the initial vertex set. The caller is responsible for freeing the array. - * Geodesic distances will only work when used with PBVH_FACES, for other types of PBVH it will - * fallback to euclidean distances to one of the initial vertices in the set. + * Returns an array indexed by vertex index containing the geodesic distance to the closest + * vertex in the initial vertex set. The caller is responsible for freeing the array. Geodesic + * distances will only work when used with PBVH_FACES, for other types of PBVH it will fallback + * to euclidean distances to one of the initial vertices in the set. */ -float *SCULPT_geodesic_distances_create(Object *ob, GSet *initial_verts, float limit_radius); -float *SCULPT_geodesic_from_vertex_and_symm(Sculpt *sd, - Object *ob, - PBVHVertRef vertex, - float limit_radius); -float *SCULPT_geodesic_from_vertex(Object *ob, PBVHVertRef vertex, float limit_radius); +float *SCULPT_geodesic_distances_create(struct Object *ob, + struct GSet *initial_vertices, + const float limit_radius, + PBVHVertRef *r_closest_verts, + float (*vertco_override)[3]); +float *SCULPT_geodesic_from_vertex_and_symm(struct Sculpt *sd, + struct Object *ob, + const PBVHVertRef vertex, + const float limit_radius); +float *SCULPT_geodesic_from_vertex(Object *ob, const PBVHVertRef vertex, const float limit_radius); + /** \} */ /* -------------------------------------------------------------------- */ @@ -1399,8 +1730,8 @@ void SCULPT_filter_cache_init(bContext *C, int undo_type, const int mval[2], float area_normal_radius); -void SCULPT_filter_cache_free(SculptSession *ss); -void SCULPT_mesh_filter_properties(wmOperatorType *ot); +void SCULPT_filter_cache_free(SculptSession *ss, struct Object *ob); +void SCULPT_mesh_filter_properties(struct wmOperatorType *ot); void SCULPT_mask_filter_smooth_apply( Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode, int smooth_iterations); @@ -1423,13 +1754,16 @@ void SCULPT_cloth_simulation_free(SculptClothSimulation *cloth_sim); /* Public functions. */ -SculptClothSimulation *SCULPT_cloth_brush_simulation_create(Object *ob, - float cloth_mass, - float cloth_damping, - float cloth_softbody_strength, - bool use_collisions, - bool needs_deform_coords); -void SCULPT_cloth_brush_simulation_init(SculptSession *ss, SculptClothSimulation *cloth_sim); +struct SculptClothSimulation *SCULPT_cloth_brush_simulation_create( + Object *ob, + const float cloth_mass, + const float cloth_damping, + const float cloth_softbody_strength, + const bool use_collisions, + const bool needs_deform_coords); + +void SCULPT_cloth_brush_simulation_init(struct SculptSession *ss, + struct SculptClothSimulation *cloth_sim); void SCULPT_cloth_sim_activate_nodes(SculptClothSimulation *cloth_sim, PBVHNode **nodes, @@ -1452,8 +1786,10 @@ void SCULPT_cloth_brush_ensure_nodes_constraints(Sculpt *sd, /** * Cursor drawing function. */ -void SCULPT_cloth_simulation_limits_draw(uint gpuattr, - const Brush *brush, +void SCULPT_cloth_simulation_limits_draw(const SculptSession *ss, + const Sculpt *sd, + const uint gpuattr, + const struct Brush *brush, const float location[3], const float normal[3], float rds, @@ -1489,45 +1825,79 @@ BLI_INLINE bool SCULPT_is_cloth_deform_brush(const Brush *brush) * For bmesh: Average surrounding verts based on an orthogonality measure. * Naturally converges to a quad-like structure. */ -void SCULPT_bmesh_four_neighbor_average(float avg[3], float direction[3], BMVert *v); +void SCULPT_bmesh_four_neighbor_average(SculptSession *ss, + float avg[3], + float direction[3], + struct BMVert *v, + float projection, + bool check_fsets, + int cd_temp, + int cd_sculpt_vert, + bool do_origco); -void SCULPT_neighbor_coords_average(SculptSession *ss, float result[3], PBVHVertRef vertex); -float SCULPT_neighbor_mask_average(SculptSession *ss, PBVHVertRef vertex); -void SCULPT_neighbor_color_average(SculptSession *ss, float result[4], PBVHVertRef vertex); +void SCULPT_neighbor_coords_average(SculptSession *ss, + float result[3], + PBVHVertRef index, + float projection, + float fset_projection, + bool weighted); +float SCULPT_neighbor_mask_average(SculptSession *ss, PBVHVertRef index); +void SCULPT_neighbor_color_average(SculptSession *ss, float result[4], PBVHVertRef index); + +/* Mask the mesh boundaries smoothing only the mesh surface without using automasking. */ -/** - * Mask the mesh boundaries smoothing only the mesh surface without using auto-masking. - */ void SCULPT_neighbor_coords_average_interior(SculptSession *ss, float result[3], - PBVHVertRef vertex); + PBVHVertRef vertex, + float projection, + float fset_projection); +BLI_INLINE bool SCULPT_get_fset_projection(SculptSession *ss, float fset_projection) +{ + if (ss->hard_edge_mode) { + return 1.0f; + } -void SCULPT_smooth( - Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode, float bstrength, bool smooth_mask); + return fset_projection; +} + +BLI_INLINE bool SCULPT_need_reproject(SculptSession *ss) +{ + return ss->bm && CustomData_has_layer(&ss->bm->ldata, CD_MLOOPUV); +} + +void SCULPT_reproject_cdata(SculptSession *ss, + PBVHVertRef vertex, + float origco[3], + float origno[3]); + +void SCULPT_smooth(Sculpt *sd, + Object *ob, + PBVHNode **nodes, + const int totnode, + float bstrength, + const bool smooth_mask); void SCULPT_do_smooth_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode); /* Surface Smooth Brush. */ +void SCULPT_surface_smooth_laplacian_init(Object *ob); void SCULPT_surface_smooth_laplacian_step(SculptSession *ss, float *disp, const float co[3], - float (*laplacian_disp)[3], - PBVHVertRef vertex, + const PBVHVertRef vertex, const float origco[3], - float alpha); -void SCULPT_surface_smooth_displace_step(SculptSession *ss, - float *co, - float (*laplacian_disp)[3], - PBVHVertRef vertex, - float beta, - float fade); + const float alpha); + +void SCULPT_surface_smooth_displace_step( + SculptSession *ss, float *co, const PBVHVertRef v_index, const float beta, const float fade); + void SCULPT_do_surface_smooth_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode); /* Slide/Relax */ void SCULPT_relax_vertex(SculptSession *ss, PBVHVertexIter *vd, float factor, - bool filter_boundary_face_sets, + eSculptBoundary boundary_mask, float *r_final_pos); /** \} */ @@ -1548,9 +1918,9 @@ bool SCULPT_pbvh_calc_area_normal(const Brush *brush, */ void SCULPT_cache_calc_brushdata_symm(StrokeCache *cache, ePaintSymmetryFlags symm, - char axis, - float angle); -void SCULPT_cache_free(StrokeCache *cache); + const char axis, + const float angle); +void SCULPT_cache_free(SculptSession *ss, struct Object *ob, StrokeCache *cache); /* -------------------------------------------------------------------- */ /** \name Sculpt Undo @@ -1609,6 +1979,10 @@ void SCULPT_OT_face_set_box_gesture(wmOperatorType *ot); void SCULPT_OT_trim_lasso_gesture(wmOperatorType *ot); void SCULPT_OT_trim_box_gesture(wmOperatorType *ot); +void SCULPT_OT_project_line_gesture(struct wmOperatorType *ot); + +void SCULPT_OT_face_set_by_topology(struct wmOperatorType *ot); + void SCULPT_OT_project_line_gesture(wmOperatorType *ot); /** \} */ @@ -1650,7 +2024,7 @@ void SCULPT_OT_color_filter(wmOperatorType *ot); /** \} */ /* -------------------------------------------------------------------- */ -/** \name Interactive Mask Operators +/** \name Mask Operators * \{ */ /* Mask filter and Dirty Mask. */ @@ -1664,6 +2038,8 @@ void SCULPT_OT_mask_expand(wmOperatorType *ot); /* Mask Init. */ void SCULPT_OT_mask_init(wmOperatorType *ot); +void SCULPT_OT_ipmask_filter(wmOperatorType *ot); + /** \} */ /* Detail size. */ @@ -1724,11 +2100,14 @@ void SCULPT_pose_ik_chain_free(SculptPoseIKChain *ik_chain); * Main function to get #SculptBoundary data both for brush deformation and viewport preview. * Can return NULL if there is no boundary from the given vertex using the given radius. */ -SculptBoundary *SCULPT_boundary_data_init(Object *object, - Brush *brush, - PBVHVertRef initial_vertex, - float radius); -void SCULPT_boundary_data_free(SculptBoundary *boundary); + +struct SculptBoundary *SCULPT_boundary_data_init(struct Sculpt *sd, + Object *object, + Brush *brush, + const PBVHVertRef initial_vertex, + const float radius); +void SCULPT_boundary_data_free(struct SculptBoundary *boundary); + /* Main Brush Function. */ void SCULPT_do_boundary_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode); @@ -1738,6 +2117,11 @@ void SCULPT_boundary_edges_preview_draw(uint gpuattr, float outline_alpha); void SCULPT_boundary_pivot_line_preview_draw(uint gpuattr, SculptSession *ss); +void SCULPT_do_twist_brush(struct Sculpt *sd, + struct Object *ob, + struct PBVHNode **nodes, + int totnode); + /* Multi-plane Scrape Brush. */ /* Main Brush Function. */ void SCULPT_do_multiplane_scrape_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode); @@ -1751,6 +2135,7 @@ void SCULPT_do_draw_face_sets_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, in /* Paint Brush. */ void SCULPT_do_paint_brush(PaintModeSettings *paint_mode_settings, + Scene *scene, Sculpt *sd, Object *ob, PBVHNode **nodes, @@ -1783,33 +2168,147 @@ float SCULPT_clay_thumb_get_stabilized_pressure(StrokeCache *cache); void SCULPT_do_draw_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode); -void SCULPT_do_fill_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode); -void SCULPT_do_scrape_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode); -void SCULPT_do_clay_thumb_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode); -void SCULPT_do_flatten_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode); -void SCULPT_do_clay_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode); -void SCULPT_do_clay_strips_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode); -void SCULPT_do_snake_hook_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode); -void SCULPT_do_thumb_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode); -void SCULPT_do_rotate_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode); -void SCULPT_do_layer_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode); -void SCULPT_do_inflate_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode); -void SCULPT_do_nudge_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode); -void SCULPT_do_crease_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode); -void SCULPT_do_pinch_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode); -void SCULPT_do_grab_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode); -void SCULPT_do_elastic_deform_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode); -void SCULPT_do_draw_sharp_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode); +void SCULPT_do_fill_brush(struct Sculpt *sd, + struct Object *ob, + struct PBVHNode **nodes, + int totnode); +void SCULPT_do_scrape_brush(struct Sculpt *sd, + struct Object *ob, + struct PBVHNode **nodes, + int totnode); +void SCULPT_do_clay_thumb_brush(struct Sculpt *sd, + struct Object *ob, + struct PBVHNode **nodes, + int totnode); +void SCULPT_do_flatten_brush(struct Sculpt *sd, + struct Object *ob, + struct PBVHNode **nodes, + int totnode); +void SCULPT_do_clay_brush(struct Sculpt *sd, + struct Object *ob, + struct PBVHNode **nodes, + int totnode); +void SCULPT_do_clay_strips_brush(struct Sculpt *sd, + struct Object *ob, + struct PBVHNode **nodes, + int totnode); +void SCULPT_do_snake_hook_brush(struct Sculpt *sd, + struct Object *ob, + struct PBVHNode **nodes, + int totnode); +void SCULPT_do_thumb_brush(struct Sculpt *sd, + struct Object *ob, + struct PBVHNode **nodes, + int totnode); +void SCULPT_do_rotate_brush(struct Sculpt *sd, + struct Object *ob, + struct PBVHNode **nodes, + int totnode); +void SCULPT_do_layer_brush(struct Sculpt *sd, + struct Object *ob, + struct PBVHNode **nodes, + int totnode); +void SCULPT_do_inflate_brush(struct Sculpt *sd, + struct Object *ob, + struct PBVHNode **nodes, + int totnode); +void SCULPT_do_nudge_brush(struct Sculpt *sd, + struct Object *ob, + struct PBVHNode **nodes, + int totnode); +void SCULPT_do_crease_brush(struct Sculpt *sd, + struct Object *ob, + struct PBVHNode **nodes, + int totnode); +void SCULPT_do_pinch_brush(struct Sculpt *sd, + struct Object *ob, + struct PBVHNode **nodes, + int totnode); +void SCULPT_do_grab_brush(struct Sculpt *sd, + struct Object *ob, + struct PBVHNode **nodes, + int totnode); +void SCULPT_do_elastic_deform_brush(struct Sculpt *sd, + struct Object *ob, + struct PBVHNode **nodes, + int totnode); +void SCULPT_do_draw_sharp_brush(struct Sculpt *sd, + struct Object *ob, + struct PBVHNode **nodes, + int totnode); +void SCULPT_do_scene_project_brush(struct Sculpt *sd, + struct Object *ob, + struct PBVHNode **nodes, + int totnode); +void SCULPT_do_slide_brush(struct Sculpt *sd, + struct Object *ob, + struct PBVHNode **nodes, + int totnode); void SCULPT_do_slide_relax_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode); - -void SCULPT_do_displacement_smear_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode); -void SCULPT_do_displacement_eraser_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode); -void SCULPT_do_mask_brush_draw(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode); -void SCULPT_do_mask_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode); -/** \} */ - +void SCULPT_do_fairing_brush(struct Sculpt *sd, + struct Object *ob, + struct PBVHNode **nodes, + int totnode); +void SCULPT_do_displacement_smear_brush(struct Sculpt *sd, + struct Object *ob, + struct PBVHNode **nodes, + int totnode); +void SCULPT_do_displacement_eraser_brush(struct Sculpt *sd, + struct Object *ob, + struct PBVHNode **nodes, + int totnode); +void SCULPT_do_draw_brush(struct Sculpt *sd, + struct Object *ob, + struct PBVHNode **nodes, + int totnode); +void SCULPT_do_mask_brush_draw(struct Sculpt *sd, + struct Object *ob, + struct PBVHNode **nodes, + int totnode); +void SCULPT_do_mask_brush(struct Sculpt *sd, + struct Object *ob, + struct PBVHNode **nodes, + int totnode); void SCULPT_bmesh_topology_rake( - Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode, float bstrength); + Sculpt *sd, Object *ob, PBVHNode **nodes, const int totnode, float bstrength); + +void SCULPT_stroke_cache_snap_context_init(struct bContext *C, struct Object *ob); +void SCULPT_fairing_brush_exec_fairing_for_cache(struct Sculpt *sd, struct Object *ob); +void SCULPT_do_auto_face_set(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode); + +void do_draw_face_sets_brush_task_cb_ex(void *__restrict userdata, + const int n, + const struct TaskParallelTLS *__restrict tls); + +void SCULPT_enhance_details_brush(struct Sculpt *sd, + struct Object *ob, + struct PBVHNode **nodes, + const int totnode, + int presteps); +void SCULPT_do_displacement_heal_brush(struct Sculpt *sd, + struct Object *ob, + struct PBVHNode **nodes, + int totnode); + +void SCULPT_do_array_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode); +void SCULPT_array_datalayers_free(SculptArray *array, Object *ob); +void SCULPT_array_path_draw(const uint gpuattr, Brush *brush, SculptSession *ss); + +/* Directional Smooth Brush. */ +void SCULPT_do_directional_smooth_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode); + +/* Uniform Weights Smooth Brush. */ +void SCULPT_do_uniform_weights_smooth_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode); + +void SCULPT_do_draw_face_sets_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode); +void SCULPT_do_smear_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode); + +/* Symmetrize Map. */ +void SCULPT_do_symmetrize_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode); + +void SCULPT_uv_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode); + +/** \} */ /* end sculpt_brush_types.cc */ @@ -1819,6 +2318,173 @@ void SCULPT_OT_brush_stroke(wmOperatorType *ot); /* end sculpt_ops.cc */ +eSculptBoundary SCULPT_edge_is_boundary(const SculptSession *ss, + const PBVHEdgeRef edge, + eSculptBoundary typemask); +void SCULPT_edge_get_verts(const SculptSession *ss, + const PBVHEdgeRef edge, + PBVHVertRef *r_v1, + PBVHVertRef *r_v2); +PBVHVertRef SCULPT_edge_other_vertex(const SculptSession *ss, + const PBVHEdgeRef edge, + const PBVHVertRef vertex); + +//#define SCULPT_REPLAY +#ifdef SCULPT_REPLAY +struct SculptReplayLog; +struct SculptBrushSample; + +# ifdef WIN32 +# define REPLAY_EXPORT __declspec(dllexport) +# else +# define REPLAY_EXPORT +# endif + +void SCULPT_replay_log_free(struct SculptReplayLog *log); +struct SculptReplayLog *SCULPT_replay_log_create(); +void SCULPT_replay_log_end(); +void SCULPT_replay_log_start(); +char *SCULPT_replay_serialize(); +void SCULPT_replay_log_append(struct Sculpt *sd, struct SculptSession *ss, struct Object *ob); +void SCULPT_replay_test(void); + +#endif + +void SCULPT_undo_ensure_bmlog(struct Object *ob); +bool SCULPT_ensure_dyntopo_node_undo(struct Object *ob, + struct PBVHNode *node, + SculptUndoType type, + int extraType); + +struct BMesh *SCULPT_dyntopo_empty_bmesh(); + +/* initializes customdata layer used by SCULPT_neighbor_coords_average_interior when bound_smooth + * > 0.0f*/ +void SCULPT_bound_smooth_ensure(SculptSession *ss, struct Object *ob); + +/** \} */ + +int SCULPT_get_tool(const SculptSession *ss, const struct Brush *br); + +/* Sculpt API to get brush channel data +If ss->cache exists then ss->cache->channels_final +will be used, otherwise brush and tool settings channels +will be used (taking inheritence into account). +*/ + +/* -------------------------------------------------------------------- */ +/** \name Brush channel accessor API + * \{ */ + +/** Get brush channel value. The channel will be + fetched from ss->cache->channels_final. If + ss->cache is NULL, channel will be fetched + from sd->channels and br->channels taking + inheritance flags into account. + + Note that sd or br may be NULL, but not + both.*/ +float SCULPT_get_float_intern(const SculptSession *ss, + const char *idname, + const Sculpt *sd, + const Brush *br); +#define SCULPT_get_float(ss, idname, sd, br) \ + SCULPT_get_float_intern(ss, BRUSH_BUILTIN_##idname, sd, br) + +int SCULPT_get_int_intern(const SculptSession *ss, + const char *idname, + const Sculpt *sd, + const Brush *br); +#define SCULPT_get_int(ss, idname, sd, br) \ + SCULPT_get_int_intern(ss, BRUSH_BUILTIN_##idname, sd, br) +#define SCULPT_get_bool(ss, idname, sd, br) SCULPT_get_int(ss, idname, sd, br) + +int SCULPT_get_vector_intern( + const SculptSession *ss, const char *idname, float out[4], const Sculpt *sd, const Brush *br); +#define SCULPT_get_vector(ss, idname, out, sd, br) \ + SCULPT_get_vector_intern(ss, BRUSH_BUILTIN_##idname, out, sd, br) + +/** \} */ + +float SCULPT_calc_concavity(SculptSession *ss, PBVHVertRef vref); + +/* +If useAccurateSolver is false, a faster but less accurate +power solver will be used. If true then BLI_eigen_solve_selfadjoint_m3 +will be called. +*/ +bool SCULPT_calc_principle_curvatures(SculptSession *ss, + PBVHVertRef vertex, + SculptCurvatureData *out, + bool useAccurateSolver); + +void SCULPT_curvature_begin(SculptSession *ss, struct PBVHNode *node, bool useAccurateSolver); +void SCULPT_curvature_dir_get(SculptSession *ss, + PBVHVertRef v, + float dir[3], + bool useAccurateSolver); + +/* -------------------------------------------------------------------- */ +/** \name Cotangent API + * \{ */ + +bool SCULPT_dyntopo_check_disk_sort(SculptSession *ss, PBVHVertRef vertex); +void SCULT_dyntopo_flag_all_disk_sort(SculptSession *ss); + +// call SCULPT_cotangents_begin in the main thread before any calls to this function +void SCULPT_dyntopo_get_cotangents(SculptSession *ss, + PBVHVertRef vertex, + float *r_ws, + float *r_cot1, + float *r_cot2, + float *r_area, + float *r_totarea); + +/** call SCULPT_cotangents_begin in the main thread before any calls to this function */ +void SCULPT_get_cotangents(SculptSession *ss, + PBVHVertRef vertex, + float *r_ws, + float *r_cot1, + float *r_cot2, + float *r_area, + float *r_totarea); + +/** call this in the main thread before any calls to SCULPT_get_cotangents */ +void SCULPT_cotangents_begin(struct Object *ob, SculptSession *ss); +/** \} */ + +void SCULPT_ensure_persistent_layers(SculptSession *ss, struct Object *ob); +void SCULPT_ensure_epmap(SculptSession *ss); +bool SCULPT_dyntopo_automasking_init(const SculptSession *ss, + Sculpt *sd, + const Brush *br, + Object *ob, + DyntopoMaskCB *r_mask_cb, + void **r_mask_cb_data); +void SCULPT_dyntopo_automasking_end(void *mask_data); + +#define SCULPT_LAYER_PERS_CO "Persistent Base Co" +#define SCULPT_LAYER_PERS_NO "Persistent Base No" +#define SCULPT_LAYER_PERS_DISP "Persistent Base Height" +#define SCULPT_LAYER_DISP "__temp_layer_disp" + +// these tools don't support dynamic pbvh splitting during the stroke +#define DYNTOPO_HAS_DYNAMIC_SPLIT(tool) true + +#define SCULPT_stroke_needs_original(brush) \ + ELEM(brush->sculpt_tool, \ + SCULPT_TOOL_DRAW_SHARP, \ + SCULPT_TOOL_GRAB, \ + SCULPT_TOOL_ROTATE, \ + SCULPT_TOOL_THUMB, \ + SCULPT_TOOL_ELASTIC_DEFORM, \ + SCULPT_TOOL_BOUNDARY, \ + SCULPT_TOOL_POSE) + +// exponent to make boundary_smooth_factor more user-friendly +#define BOUNDARY_SMOOTH_EXP 2.0 + +bool SCULPT_needs_area_normal(SculptSession *ss, Sculpt *sd, Brush *brush); BLI_INLINE bool SCULPT_tool_is_paint(int tool) { return ELEM(tool, SCULPT_TOOL_PAINT, SCULPT_TOOL_SMEAR); @@ -1865,3 +2531,54 @@ int SCULPT_vertex_island_get(SculptSession *ss, PBVHVertRef vertex); #define SCULPT_vertex_attr_get BKE_sculpt_vertex_attr_get #define SCULPT_face_attr_get BKE_sculpt_face_attr_get + +struct StrokeID { + short id; + short userflag; +}; + +enum StrokeIDUser { + STROKEID_USER_AUTOMASKING = 1 << 0, + STROKEID_USER_BOUNDARY = 1 << 1, + STROKEID_USER_SCULPTVERT = 1 << 2, + STROKEID_USER_PREV_COLOR = 1 << 3, + STROKEID_USER_SMOOTH = 1 << 4, + STROKEID_USER_OCCLUSION = 1 << 5, +}; + +BLI_INLINE bool SCULPT_stroke_id_test(SculptSession *ss, PBVHVertRef vertex, StrokeIDUser user) +{ + StrokeID *id = SCULPT_vertex_attr_get(vertex, ss->attrs.stroke_id); + bool ret; + + if (id->id != ss->stroke_id) { + id->id = ss->stroke_id; + id->userflag = 0; + ret = true; + } + else { + ret = !(id->userflag & (int)user); + } + + id->userflag |= (int)user; + + return ret; +} + +BLI_INLINE bool SCULPT_stroke_id_test_no_update(SculptSession *ss, + PBVHVertRef vertex, + StrokeIDUser user) +{ + StrokeID *id = SCULPT_vertex_attr_get(vertex, ss->attrs.stroke_id); + + if (id->id != ss->stroke_id) { + return true; + } + + return !(id->userflag & (int)user); +} + +int SCULPT_get_symmetry_pass(const struct SculptSession *ss); + +#define SCULPT_boundary_flag_update BKE_sculpt_boundary_flag_update +} diff --git a/source/blender/editors/sculpt_paint/sculpt_mask_expand.cc b/source/blender/editors/sculpt_paint/sculpt_mask_expand.cc index fc7971fbde9..f81fdfff58b 100644 --- a/source/blender/editors/sculpt_paint/sculpt_mask_expand.cc +++ b/source/blender/editors/sculpt_paint/sculpt_mask_expand.cc @@ -67,7 +67,7 @@ static void sculpt_mask_expand_cancel(bContext *C, wmOperator *op) if (!create_face_set) { SCULPT_flush_update_step(C, SCULPT_UPDATE_MASK); } - SCULPT_filter_cache_free(ss); + SCULPT_filter_cache_free(ss, ob); SCULPT_undo_push_end(ob); SCULPT_flush_update_done(C, ob, SCULPT_UPDATE_MASK); ED_workspace_status_text(C, nullptr); @@ -162,7 +162,7 @@ static int sculpt_mask_expand_modal(bContext *C, wmOperator *op, const wmEvent * SculptCursorGeometryInfo sgi; const float mval_fl[2] = {float(event->mval[0]), float(event->mval[1])}; - if (SCULPT_cursor_geometry_info_update(C, &sgi, mval_fl, false)) { + if (SCULPT_cursor_geometry_info_update(C, &sgi, mval_fl, false, false)) { int active_vertex_i = BKE_pbvh_vertex_to_index(ss->pbvh, SCULPT_active_vertex_get(ss)); /* The cursor is over the mesh, get the update iteration from the updated active vertex. */ @@ -229,7 +229,7 @@ static int sculpt_mask_expand_modal(bContext *C, wmOperator *op, const wmEvent * BKE_pbvh_node_mark_redraw(ss->filter_cache->nodes[i]); } - SCULPT_filter_cache_free(ss); + SCULPT_filter_cache_free(ss, ob); SCULPT_undo_push_end(ob); SCULPT_flush_update_done(C, ob, SCULPT_UPDATE_MASK); @@ -349,7 +349,7 @@ static int sculpt_mask_expand_invoke(bContext *C, wmOperator *op, const wmEvent op->customdata = MEM_mallocN(sizeof(float[2]), "initial mouse position"); copy_v2_v2(static_cast(op->customdata), mval_fl); - SCULPT_cursor_geometry_info_update(C, &sgi, mval_fl, false); + SCULPT_cursor_geometry_info_update(C, &sgi, mval_fl, false, false); int vertex_count = SCULPT_vertex_count_get(ss); diff --git a/source/blender/editors/sculpt_paint/sculpt_multiplane_scrape.cc b/source/blender/editors/sculpt_paint/sculpt_multiplane_scrape.cc index 86ac514898e..d5c691ae05e 100644 --- a/source/blender/editors/sculpt_paint/sculpt_multiplane_scrape.cc +++ b/source/blender/editors/sculpt_paint/sculpt_multiplane_scrape.cc @@ -47,8 +47,7 @@ static void calc_multiplane_scrape_surface_task_cb(void *__restrict userdata, PBVHVertexIter vd; SculptBrushTest test; - SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( - ss, &test, brush->falloff_shape); + SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init(ss, &test); const int thread_id = BLI_task_parallel_thread_id(tls); /* Apply the brush normal radius to the test before sampling. */ @@ -135,8 +134,7 @@ static void do_multiplane_scrape_brush_task_cb_ex(void *__restrict userdata, proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; SculptBrushTest test; - SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( - ss, &test, data->brush->falloff_shape); + SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init(ss, &test); const int thread_id = BLI_task_parallel_thread_id(tls); AutomaskingNodeData automask_data; @@ -144,7 +142,6 @@ static void do_multiplane_scrape_brush_task_cb_ex(void *__restrict userdata, data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]); BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { - if (!sculpt_brush_test_sq_fn(&test, vd.co)) { continue; } diff --git a/source/blender/editors/sculpt_paint/sculpt_new.cc b/source/blender/editors/sculpt_paint/sculpt_new.cc new file mode 100644 index 00000000000..630d3673bc2 --- /dev/null +++ b/source/blender/editors/sculpt_paint/sculpt_new.cc @@ -0,0 +1,29 @@ +#if 0 +# include "sculpt.hh" + +using BMeshSculpt = blender::sculpt::SculptImpl; + +BMeshSculpt *bmesh_sculpt = new BMeshSculpt(NULL, new blender::sculpt::BMeshPBVH()); + +extern "C" { +void cxx_do_draw_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) +{ + bmesh_sculpt->ss = ob->sculpt; + bmesh_sculpt->pbvh->setSculptSession(ob->sculpt); + + bmesh_sculpt->do_draw_brush(sd, ob, nodes, totnode); +} +} + +void test_cxsculpt() +{ + float dir[3] = {1.0f, 2.0f, 3.0f}; + float cent[3] = {0.0f, 0.0f, 0.0f}; + + bmesh_sculpt->moveVerts(cent, 5.0f, dir); +} +#endif diff --git a/source/blender/editors/sculpt_paint/sculpt_new.hh b/source/blender/editors/sculpt_paint/sculpt_new.hh new file mode 100644 index 00000000000..ec45ebc5385 --- /dev/null +++ b/source/blender/editors/sculpt_paint/sculpt_new.hh @@ -0,0 +1,960 @@ +#if 0 +# pragma optimize("", off) +# pragma once + +/* + +This is a proof of concept of how a C++ sculpt system could work. +It's a design study, not even a proposal. + +We can't really use virtual-based polymorphism for performance reasons, +so the idea is to use templates and C++20's concepts instead. + +*/ + +# if __cplusplus >= 202000 +# include +# endif + +# include +# include + +# include + +//# define COMPILER_EXPLORER + +# ifndef COMPILER_EXPLORER +# include "BKE_pbvh.h" +# include "BLI_bitmap.h" +# include "BLI_map.hh" +# include "BLI_math.h" +# include "BLI_mempool.h" +# include "BLI_task.h" +# include "BLI_task.hh" +# include "MEM_guardedalloc.h" + +# include "DNA_brush_enums.h" +# include "DNA_brush_types.h" +# include "DNA_mesh_types.h" +# include "DNA_meshdata_types.h" +# include "DNA_scene_types.h" + +# include "BKE_brush.h" +# include "BKE_colortools.h" +//#include "BKE_brush_engine.h" +# include "BKE_mesh.h" +# include "BKE_object.h" +# include "BKE_paint.h" +# include "BKE_pbvh.h" + +extern "C" { +# include "sculpt_intern.h" +} + +# include "bmesh.h" + +/* clang-format off */ +#include "../../blenkernel/intern/pbvh_intern.hh" +/* clang-format on */ + +# else +void copy_v3_v3(float *a, float *b) +{ + a[0] = b[0]; + a[1] = b[1]; + a[2] = b[2]; +} + +void madd_v3_v3fl(float *r, float *a, float m) +{ + r[0] += a[0] * m; + r[1] += a[1] * m; + r[2] += a[2] * m; +} + +namespace blender { +class IndexRange { + public: + IndexRange(int start, int end) : _start(start), _end(end), _i(start) + { + } + + IndexRange(const IndexRange &r) : _start(r._start), _end(r._end), _i(r._i) + { + } + + inline IndexRange &operator++() + { + _i++; + + return *this; + } + + inline IndexRange operator++(int) + { + IndexRange tmp(*this); + operator++(); + return tmp; + } + + inline IndexRange begin() + { + IndexRange ret(_start, _end); + ret._i = _start; + + return ret; + } + + inline IndexRange end() + { + IndexRange ret(_start, _end); + ret._i = _end; + + return ret; + } + + inline bool operator==(const IndexRange &b) + { + // detect comparison with end iterator + return _i == b._i; + } + + inline bool operator!=(const IndexRange &b) + { + return !(*this == b); + } + + inline int operator*() + { + return _i; + } + + private: + int _start, _end, _i; +}; + +namespace threading { +void parallel_for(IndexRange range, int n, std::function &&func) +{ + for (int i : range) { + // std::invoke(func, IndexRange(i, i + 1)); + IndexRange subrange(i, i + 1); + func(subrange); + } +} +} // namespace threading +} // namespace blender + +typedef struct PBVHNode { + struct { + void **elems; + int length; + } * bm_unique_verts, *bm_other_verts, *bm_faces; +} PBVHNode; +typedef struct PBVH PBVH; + +typedef struct BMVert { + struct { + char htype, hflag; + short api_flag; + int index; + void *data; + } head; + + struct BMEdge *e; + float co[3]; + float no[3]; +} BMVert; + +struct BMLoop; + +typedef struct BMEdge { + struct { + char htype, hflag; + short api_flag; + int index; + void *data; + } head; + + BMVert *v1, *v2; + struct BMLoop *l; + struct { + BMEdge *next, *prev; + } v1_disk_link, v2_disk_link; +} BMEdge; + +struct BMFace; + +typedef struct BMLoop { + struct { + char htype, hflag; + short api_flag; + int index; + void *data; + } head; + + BMVert *v; + BMEdge *e; + struct BMFace *f; + BMLoop *radial_next, *radial_prev; + BMLoop *next, *prev; +} BMLoop; + +typedef struct BMFace { + struct { + char htype, hflag; + short api_flag; + int index; + void *data; + } head; + + BMLoop *l_first; + int len; +} BMFace; + +typedef struct BMesh { + int totvert, totedge, totloop, totface; +} BMesh; + +typedef struct PBVHVertRef { + intptr_t i; +} PBVHVertRef; + +typedef struct SculptBrushTest { + int value; +} SculptBrushTest; + +struct SculptSession; +typedef float (*SculptBrushTestFn)(SculptBrushTest *test, float *co); +typedef bool (*BKE_pbvh_SearchCallback)(PBVHNode *node, void *data); +void BKE_pbvh_search_gather( + PBVH *pbvh, BKE_pbvh_SearchCallback scb, void *search_data, PBVHNode ***array, int *tot); +SculptBrushTestFn SCULPT_brush_test_init(struct SculptSession *ss, + SculptBrushTest *test, + char falloff_shape); +bool SCULPT_search_sphere_cb(PBVHNode *node, void *data_v); +void BKE_pbvh_vert_tag_update_normal(PBVHNode *node); + +typedef struct { + struct Sculpt *sd; + struct SculptSession *ss; + float radius_squared; + const float *center; + bool original; + /* This ignores fully masked and fully hidden nodes. */ + bool ignore_fully_ineffective; + struct Object *ob; + struct Brush *brush; +} SculptSearchSphereData; + +float SCULPT_brush_strength_factor(struct SculptSession *ss, + const struct Brush *br, + const float point[3], + const float len, + const short vno[3], + const float fno[3], + const float mask, + const PBVHVertRef vertex_index, + const int thread_id); +extern PBVHNode *the_fake_node; +# endif + +namespace blender { +namespace sculpt { + +typedef struct BrushSearchArgs { + float *center; + float radius; + bool use_threads; + bool use_original; + void *userdata; + const Brush *brush; + SculptBrushTest *test; // may be NULL, will be pulled from brush + SculptBrushTestFn test_func; +} BrushSearchArgs; + +class BMeshPBVH { + public: + BMeshPBVH() + { + } + + struct unique_verts_iter : public std::iterator { + public: + ~unique_verts_iter(){}; + + unique_verts_iter(PBVHNode *node) : _node(node) + { + reset(); + loadPointers(); + } + + unique_verts_iter(const unique_verts_iter &a) + { + i = a.i; + _i = a._i; + index = a.index; + vertex = a.vertex; + co = a.co; + no = a.no; + mask = a.mask; + _node = a._node; + color = a.color; + deprecated_vertref = a.deprecated_vertref; + } + + unique_verts_iter() + { + i = _i = index = 0; + vertex = nullptr; + co = nullptr; + no = nullptr; + mask = nullptr; + color = nullptr; + } + + void loadPointers() + { + if (_i >= _node->bm_unique_verts->length) { + vertex = nullptr; + co = nullptr; + no = nullptr; + } + else { + vertex = reinterpret_cast(_node->bm_unique_verts->elems[_i]); + } + + mask = nullptr; + + if (vertex) { + deprecated_vertref.i = reinterpret_cast(vertex); + co = vertex->co; + no = vertex->no; + } + } + + inline unique_verts_iter &operator++() + { + i++; + _i++; + + while (_i < _node->bm_unique_verts->length && _node->bm_unique_verts->elems[_i] == nullptr) { + _i++; + } + + loadPointers(); + + return *this; + } + + inline unique_verts_iter operator++(int) + { + unique_verts_iter tmp(*this); + operator++(); + return tmp; + } + + inline void reset() + { + _i = i = 0; + while (_i < _node->bm_unique_verts->length && _node->bm_unique_verts->elems[_i] == nullptr) { + _i++; + } + + loadPointers(); + } + + inline unique_verts_iter begin() + { + unique_verts_iter ret = *this; + + ret.reset(); + + return ret; + } + + inline unique_verts_iter end() + { + unique_verts_iter ret; + + ret.vertex = nullptr; + + return ret; + } + + inline bool operator==(const unique_verts_iter &b) + { + // detect comparison with end iterator + return (!vertex && !b.vertex) || (_node == b._node && i == b.i); + } + + inline bool operator!=(const unique_verts_iter &b) + { + return !(*this == b); + } + + inline unique_verts_iter &operator*() + { + return *this; + } + + PBVHVertRef deprecated_vertref; + + int i; + int index; + BMVert *vertex; + float *co; + float *no; + float *mask; + float *color; + + private: + PBVHNode *_node; + int _i; + }; + + struct edges_round_vert_iter : public std::iterator { + public: + ~edges_round_vert_iter(){}; + + edges_round_vert_iter(BMVert *v) : v(v) + { + e = v->e; + _start_e = v->e; + operator++(); + } + + edges_round_vert_iter(const edges_round_vert_iter &a) + { + this->v = a.v; + this->e = a.e; + this->_start_e = a._start_e; + } + + inline edges_round_vert_iter &operator++() + { + e = e->v1 == v ? e->v1_disk_link.next : e->v2_disk_link.next; + return *this; + } + + inline edges_round_vert_iter operator++(int) + { + edges_round_vert_iter tmp(*this); + + operator++(); + + return tmp; + } + + inline void reset() + { + e = _start_e; + operator++(); + } + + inline edges_round_vert_iter begin() + { + return edges_round_vert_iter(v); + } + + inline edges_round_vert_iter end() + { + edges_round_vert_iter ret(v); + ret.e = ret._start_e; + + return ret; + } + + inline bool operator==(const edges_round_vert_iter &b) + { + return e == b.e; + } + + inline bool operator!=(const edges_round_vert_iter &b) + { + return this->e != b.e; + } + + inline BMEdge *operator*() + { + return e; + } + + BMEdge *e; + BMVert *v; + + private: + BMEdge *_start_e; + }; + + struct brush_verts_iter : unique_verts_iter { + public: + ~brush_verts_iter(){}; + + brush_verts_iter(SculptSession *ss, + PBVHNode *node, + SculptBrushTest *test, + SculptBrushTestFn test_func, + const Brush *brush, + float *center, + float radius, + int thread_id) + : _node(node), + brush(brush), + radius_scale(1.0f), + test(*test), + test_func(test_func), + _ss(ss), + _thread_id(thread_id), + unique_verts_iter(node) + { + copy_v3_v3((float *)this->center, (float *)center); + this->radius = radius; + this->radius_squared = radius * radius; + } + + brush_verts_iter(const brush_verts_iter &a) : unique_verts_iter(a) + { + copy_v3_v3((float *)center, (float *)a.center); + radius = a.radius; + radius_squared = a.radius_squared; + radius_scale = a.radius_scale; + _thread_id = a._thread_id; + + fade = a.fade; + _node = a._node; + brush = a.brush; + test = a.test; + test_func = a.test_func; + + _ss = a._ss; + } + + inline brush_verts_iter begin() + { + brush_verts_iter ret = *this; + + static_cast(&ret)->reset(); + ret.skip_outside(); + + ret.loadPointers(); + if (!ret.vertex) { + ret.brush = nullptr; + } + // ret = ++ret; + + return ret; + } + + void loadPointers() + { + if (!vertex) { + brush = nullptr; + fade = 0.0f; + + return; + } + + fade = SCULPT_brush_strength_factor(_ss, + brush, + co, + sqrtf(test.dist), + NULL, + no, + mask ? *mask : 0.0f, + deprecated_vertref, + _thread_id); + } + + brush_verts_iter(bool is_end) + { + brush = nullptr; + vertex = nullptr; + } + + inline brush_verts_iter end() + { + return brush_verts_iter(true); + } + + inline brush_verts_iter operator++(int) + { + brush_verts_iter tmp = *this; + operator++(); + return tmp; + } + + inline brush_verts_iter &operator++() + { + unique_verts_iter::operator++(); + + if (!brush || !vertex) { + fade = 0.0f; + vertex = nullptr; + brush = nullptr; + co = nullptr; + no = nullptr; + + return *this; + } + + skip_outside(); + + if (!vertex) { + brush = nullptr; + return *this; + } + + loadPointers(); + + return *this; + } + + inline bool operator==(const brush_verts_iter &b) + { + // detect comparison with end iterator + if (!brush && !b.brush) { + return true; + } + + return false; + // return unique_verts_iter::operator==(static_cast(b)); + } + + inline bool operator!=(const brush_verts_iter &b) + { + return !(*this == b); + } + + inline brush_verts_iter &operator*() + { + return *this; + } + + const Brush *brush; + float center[3]; + float radius; + float radius_scale; + float radius_squared; + + float fade; + + SculptBrushTest test; + SculptBrushTestFn test_func; + + private: + inline void skip_outside() + { + while (vertex && !test_func(&test, co)) { + unique_verts_iter::operator++(); + } + } + + SculptSession *_ss; + PBVHNode *_node; + int _thread_id; + }; + + inline edges_round_vert_iter edgesOfVert(BMVert *v) + { + return edges_round_vert_iter(v); + } + + inline unique_verts_iter forAllUniqueVerts(PBVHNode *node) + { + unique_verts_iter ret(node); + + return ret; + } + + inline float *getVertexCo(BMVert *v) + { + return v->co; + } + + const inline float *getVertexNormal(BMVert *v) + { + return v->no; + } + + inline void setVertexNormal(BMVert *v, float *no) + { + } + + /* + * SculptSession *ss, + PBVHNode *node, + SculptBrushTest test, + SculptBrushTestFn test_func, + Brush *brush, + float center[3], + float radius, + int thread_id) + */ + inline void forVertsInRangeOfNodes( + BrushSearchArgs *args, + PBVHNode **nodes, + int totnode, + std::function + callback, + std::function post_node_callback) + { + SculptSession *ss = _ss; + + /*SculptSession *ss, + PBVHNode *node, + SculptBrushTest test, + SculptBrushTestFn test_func, + Brush *brush, + float *center, + float radius, + int thread_id) + */ + + blender::IndexRange range(0, totnode); + blender::threading::parallel_for( + range, + 5, + [&args, &nodes, &ss, &callback, &post_node_callback](blender::IndexRange &subrange) { + for (auto i : subrange) { + brush_verts_iter biter(ss, + nodes[i], + args->test, + args->test_func, + args->brush, + args->center, + args->radius, + (int)BLI_task_parallel_thread_id(NULL)); + + callback(nodes[i], biter, (int)i, args->userdata); + + if (post_node_callback) { + post_node_callback(nodes[i], (int)i, args->userdata); + } + } + }); + } + + inline void forVertsInRange( + BrushSearchArgs *args, + std::function callback, + std::function node_callback) + { + PBVHNode **nodes = NULL; + int totnode = 0; + + SculptBrushTest default_test; + + if (!args->test) { + args->test = &default_test; + args->test_func = SCULPT_brush_test_init( + _ss, &default_test, /*args->brush->falloff_shape*/ 0); + } + + SculptSearchSphereData data = {_sd, + _ss, + args->radius * args->radius, + args->center, + args->use_original, + false, + nullptr, + nullptr}; + + BKE_pbvh_search_gather(_pbvh, SCULPT_search_sphere_cb, &data, &nodes, &totnode); + + SculptSession *ss = _ss; + + /*SculptSession *ss, + PBVHNode *node, + SculptBrushTest test, + SculptBrushTestFn test_func, + Brush *brush, + float *center, + float radius, + int thread_id) + */ + + blender::IndexRange range(0, totnode); + blender::threading::parallel_for( + range, 1, [&args, &nodes, &ss, &callback, &node_callback](blender::IndexRange &subrange) { + for (auto i : subrange) { + brush_verts_iter biter(ss, + nodes[i], + args->test, + args->test_func, + args->brush, + args->center, + args->radius, + (int)i); + + callback(biter, (int)i, args->userdata); + + if (node_callback) { + node_callback(nodes[i], (int)i, args->userdata); + } + } + }); + } + + void setSculptSession(SculptSession *ss) + { + _ss = ss; + } + + private: + BMesh *_bm; + PBVH *_pbvh; + SculptSession *_ss; + Sculpt *_sd; +}; + +class BMeshBackend { + public: + BMeshBackend() + { + } + + private: +}; + +/* clang-format off */ + +#if __cplusplus >= 202000 +template +concept PBVHBackend = requires(PBVHClass b, V v){ + {(*(b.forAllUniqueVerts(nullptr))).vertex} -> std::same_as; + {b.getVertexCo(v)} -> std::same_as; + {b.getVertexNormal(v)} -> std::same_as; +}; +/* clang-format on */ + +template PBVHClass> +# else +template +# endif +class SculptImpl { + public: + PBVHClass *pbvh; + SculptSession *ss; + + SculptImpl(SculptSession *ss, PBVHClass *pbvh) : pbvh(pbvh), ss(ss) + { + } + + inline void do_draw_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) + { + Brush *brush = BKE_paint_brush(&sd->paint); + float offset[3]; + const float bstrength = ss->cache->bstrength; + + /* Offset with as much as possible factored in already. */ + float effective_normal[3]; + SCULPT_tilt_effective_normal_get(ss, brush, effective_normal); + mul_v3_v3fl(offset, effective_normal, ss->cache->radius); + mul_v3_v3(offset, ss->cache->scale); + mul_v3_fl(offset, bstrength); + + /* XXX: this shouldn't be necessary, but sculpting crashes in blender2.8 otherwise + * initialize before threads so they can do curve mapping. */ + BKE_curvemapping_init(brush->curve); + + SculptBrushTest test; + SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init( + ss, &test, ss->cache->brush->falloff_shape); + + /* + * float *center; + float radius; + bool use_threads; + bool use_original; + void *userdata; + const Brush *brush; + SculptBrushTest *test; // may be NULL, will be pulled from brush + SculptBrushTestFn test_func; + */ + BrushSearchArgs args = {ss->cache->location, + ss->cache->radius, + true, + false, + NULL, + ss->cache->brush, + &test, + sculpt_brush_test_sq_fn}; + + pbvh->forVertsInRangeOfNodes( + &args, + nodes, + totnode, + [&](PBVHNode *node, auto verts, int node_i, void *userdata) { + float(*proxy)[3]; + + proxy = BKE_pbvh_node_add_proxy(ss->pbvh, node)->co; + bool modified = false; + + for (auto viter : verts) { + float fade = viter.fade; + + mul_v3_v3fl(proxy[viter.i], offset, fade); + modified = true; + } + }, + [](PBVHNode *node, int node_i, void *userdata) {}); + } + /* + &((BrushSearchArgs *){0}) + */ + inline void moveVerts(float cent[3], float radius, float offset[3]) + { + BrushSearchArgs args = {cent, radius, true, false, nullptr /*ss->cache->brush*/}; + + /* clang-format off */ + pbvh->forVertsInRange( + &args, + + [&](auto verts, int node_i, void *userdata) { + for (auto viter : verts) { + //add offset to vertex coordinates + for (auto e : pbvh->edgesOfVert(viter.vertex)) { + BMVert *v2 = viter.vertex == e->v1 ? e->v2 : e->v1; + + madd_v3_v3fl(v2->co, offset, viter.fade); + } + + madd_v3_v3fl(viter.co, offset, viter.fade); + printf("test"); + } + }, + + [](PBVHNode *node, int node_i, void *userdata) { + BKE_pbvh_node_mark_update(node); + }); + + /* clang-format on */ + } + + private: +}; + +} // namespace sculpt +} // namespace blender + +# ifdef COMPILER_EXPLORER + +using BMeshSculpt = blender::sculpt::SculptImpl; + +BMeshSculpt *sculpt = new BMeshSculpt(NULL, NULL); + +void test_cxsculpt() +{ + float dir[3] = {1.0f, 2.0f, 3.0f}; + float cent[3] = {0.0f, 0.0f, 0.0f}; + + sculpt->moveVerts(cent, 5.0f, dir); +} +# endif + +#endif diff --git a/source/blender/editors/sculpt_paint/sculpt_ops.cc b/source/blender/editors/sculpt_paint/sculpt_ops.cc index 091bd0d0852..a00c3efc7f5 100644 --- a/source/blender/editors/sculpt_paint/sculpt_ops.cc +++ b/source/blender/editors/sculpt_paint/sculpt_ops.cc @@ -8,9 +8,12 @@ #include "MEM_guardedalloc.h" +#include "BLI_alloca.h" +#include "BLI_array.h" #include "BLI_ghash.h" #include "BLI_gsqueue.h" #include "BLI_math.h" +#include "BLI_rand.h" #include "BLI_task.h" #include "BLI_utildefines.h" @@ -28,12 +31,14 @@ #include "BKE_attribute.h" #include "BKE_brush.h" +#include "BKE_bvhutils.h" #include "BKE_ccg.h" #include "BKE_context.h" #include "BKE_layer.h" #include "BKE_main.h" #include "BKE_mesh.h" #include "BKE_mesh_mirror.h" +#include "BKE_mesh_types.h" #include "BKE_modifier.h" #include "BKE_multires.h" #include "BKE_object.h" @@ -43,6 +48,7 @@ #include "BKE_scene.h" #include "DEG_depsgraph.h" +#include "DEG_depsgraph_query.h" #include "IMB_colormanagement.h" @@ -55,17 +61,30 @@ #include "ED_object.h" #include "ED_screen.h" #include "ED_sculpt.h" +#include "ED_space_api.h" +#include "ED_transform_snap_object_context.h" +#include "ED_view3d.h" #include "paint_intern.h" #include "sculpt_intern.hh" #include "RNA_access.h" #include "RNA_define.h" +#include "RNA_prototypes.h" #include "UI_interface.h" #include "UI_resources.h" +#include "GPU_immediate.h" +#include "GPU_state.h" +#include "GPU_vertex_buffer.h" +#include "GPU_vertex_format.h" + #include "bmesh.h" +#include "bmesh_log.h" +#include "bmesh_tools.h" + +#include "../../bmesh/intern/bmesh_idmap.h" #include #include @@ -79,8 +98,7 @@ static int sculpt_set_persistent_base_exec(bContext *C, wmOperator * /*op*/) Object *ob = CTX_data_active_object(C); SculptSession *ss = ob->sculpt; - /* Do not allow in DynTopo just yet. */ - if (!ss || (ss && ss->bm)) { + if (!ss) { return OPERATOR_FINISHED; } SCULPT_vertex_random_access_ensure(ss); @@ -101,11 +119,11 @@ static int sculpt_set_persistent_base_exec(bContext *C, wmOperator * /*op*/) for (int i = 0; i < totvert; i++) { PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); - copy_v3_v3((float *)SCULPT_vertex_attr_get(vertex, ss->attrs.persistent_co), + copy_v3_v3(SCULPT_vertex_attr_get(vertex, ss->attrs.persistent_co), SCULPT_vertex_co_get(ss, vertex)); SCULPT_vertex_normal_get( - ss, vertex, (float *)SCULPT_vertex_attr_get(vertex, ss->attrs.persistent_no)); - (*(float *)SCULPT_vertex_attr_get(vertex, ss->attrs.persistent_disp)) = 0.0f; + ss, vertex, SCULPT_vertex_attr_get(vertex, ss->attrs.persistent_no)); + (*SCULPT_vertex_attr_get(vertex, ss->attrs.persistent_disp)) = 0.0f; } return OPERATOR_FINISHED; @@ -131,7 +149,7 @@ static int sculpt_optimize_exec(bContext *C, wmOperator * /*op*/) { Object *ob = CTX_data_active_object(C); - SCULPT_pbvh_clear(ob); + SCULPT_pbvh_clear(ob, false); WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob); return OPERATOR_FINISHED; @@ -166,6 +184,69 @@ static bool sculpt_no_multires_poll(bContext *C) return false; } +static bool sculpt_only_bmesh_poll(bContext *C) +{ + Object *ob = CTX_data_active_object(C); + if (SCULPT_mode_poll(C) && ob->sculpt && ob->sculpt->pbvh) { + return BKE_pbvh_type(ob->sculpt->pbvh) == PBVH_BMESH; + } + return false; +} + +static int sculpt_spatial_sort_exec(bContext *C, wmOperator *op) +{ + Object *ob = CTX_data_active_object(C); + SculptSession *ss = ob->sculpt; + PBVH *pbvh = ss->pbvh; + + if (!pbvh) { + return OPERATOR_CANCELLED; + } + + switch (BKE_pbvh_type(pbvh)) { + case PBVH_BMESH: + SCULPT_undo_push_begin(ob, op); + SCULPT_undo_push_node(ob, nullptr, SCULPT_UNDO_GEOMETRY); + + BKE_pbvh_reorder_bmesh(ss->pbvh); + + BKE_pbvh_bmesh_on_mesh_change(ss->pbvh); + BM_log_full_mesh(ss->bm, ss->bm_log); + + ss->active_vertex.i = 0; + ss->active_face.i = 0; + + BKE_pbvh_free(ss->pbvh); + ss->pbvh = nullptr; + + /* Finish undo. */ + SCULPT_undo_push_end(ob); + + break; + case PBVH_FACES: + return OPERATOR_CANCELLED; + case PBVH_GRIDS: + return OPERATOR_CANCELLED; + } + + /* Redraw. */ + DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, ND_DATA | NC_OBJECT | ND_DRAW, ob); + + return OPERATOR_FINISHED; +} +static void SCULPT_OT_spatial_sort_mesh(wmOperatorType *ot) +{ + /* Identifiers. */ + ot->name = "Spatially Sort Mesh"; + ot->idname = "SCULPT_OT_spatial_sort_mesh"; + ot->description = "Spatially sort mesh to improve memory coherency"; + + /* API callbacks. */ + ot->exec = sculpt_spatial_sort_exec; + ot->poll = sculpt_only_bmesh_poll; +} + static int sculpt_symmetrize_exec(bContext *C, wmOperator *op) { Main *bmain = CTX_data_main(C); @@ -189,7 +270,6 @@ static int sculpt_symmetrize_exec(bContext *C, wmOperator *op) * parts that symmetrize modifies). */ SCULPT_undo_push_begin(ob, op); SCULPT_undo_push_node(ob, nullptr, SCULPT_UNDO_DYNTOPO_SYMMETRIZE); - BM_log_before_all_removed(ss->bm, ss->bm_log); BM_mesh_toolflags_set(ss->bm, true); @@ -200,15 +280,23 @@ static int sculpt_symmetrize_exec(bContext *C, wmOperator *op) sd->symmetrize_direction, dist, true); - SCULPT_dynamic_topology_triangulate(ss->bm); - +#ifndef DYNTOPO_DYNAMIC_TESS + SCULPT_dynamic_topology_triangulate(ss, ss->bm); +#endif /* Bisect operator flags edges (keep tags clean for edge queue). */ BM_mesh_elem_hflag_disable_all(ss->bm, BM_EDGE, BM_ELEM_TAG, false); BM_mesh_toolflags_set(ss->bm, false); + BKE_pbvh_recalc_bmesh_boundary(ss->pbvh); + SCULT_dyntopo_flag_all_disk_sort(ss); + + // symmetrize is messing up ids, regenerate them from scratch + BM_reassign_ids(ss->bm); + BM_mesh_toolflags_set(ss->bm, false); + BM_log_full_mesh(ss->bm, ss->bm_log); + /* Finish undo. */ - BM_log_all_added(ss->bm, ss->bm_log); SCULPT_undo_push_end(ob); break; @@ -230,7 +318,7 @@ static int sculpt_symmetrize_exec(bContext *C, wmOperator *op) } /* Redraw. */ - SCULPT_pbvh_clear(ob); + SCULPT_pbvh_clear(ob, false); WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob); return OPERATOR_FINISHED; @@ -271,8 +359,14 @@ static void sculpt_init_session(Main *bmain, Depsgraph *depsgraph, Scene *scene, if (ob->sculpt != nullptr) { BKE_sculptsession_free(ob); } - ob->sculpt = MEM_cnew(__func__); + BKE_object_sculpt_data_create(ob); + ob->sculpt->mode_type = OB_MODE_SCULPT; + ob->sculpt->active_face.i = PBVH_REF_NONE; + ob->sculpt->active_vertex.i = PBVH_REF_NONE; + + CustomData_reset(&ob->sculpt->temp_vdata); + CustomData_reset(&ob->sculpt->temp_pdata); /* Trigger evaluation of modifier stack to ensure * multires modifier sets .runtime.ccg in @@ -285,8 +379,10 @@ static void sculpt_init_session(Main *bmain, Depsgraph *depsgraph, Scene *scene, /* This function expects a fully evaluated depsgraph. */ BKE_sculpt_update_object_for_edit(depsgraph, ob, false, false, false); + BKE_sculptsession_update_attr_refs(ob); + SculptSession *ss = ob->sculpt; - if (ss->face_sets) { + if (ss->face_sets || (ss->bm && ss->cd_faceset_offset != -1)) { /* Here we can detect geometry that was just added to Sculpt Mode as it has the * SCULPT_FACE_SET_NONE assigned, so we can create a new Face Set for it. */ /* In sculpt mode all geometry that is assigned to SCULPT_FACE_SET_NONE is considered as not @@ -297,10 +393,16 @@ static void sculpt_init_session(Main *bmain, Depsgraph *depsgraph, Scene *scene, /* TODO(pablodp606): Based on this we can improve the UX in future tools for creating new * objects, like moving the transform pivot position to the new area or masking existing * geometry. */ + + SCULPT_face_random_access_ensure(ss); const int new_face_set = SCULPT_face_set_next_available_get(ss); + for (int i = 0; i < ss->totfaces; i++) { - if (ss->face_sets[i] == SCULPT_FACE_SET_NONE) { - ss->face_sets[i] = new_face_set; + PBVHFaceRef face = BKE_pbvh_index_to_face(ss->pbvh, i); + + int fset = SCULPT_face_set_get(ss, face); + if (fset == SCULPT_FACE_SET_NONE) { + SCULPT_face_set_set(ss, face, new_face_set); } } } @@ -332,7 +434,8 @@ void ED_object_sculptmode_enter_ex(Main *bmain, Scene *scene, Object *ob, const bool force_dyntopo, - ReportList *reports) + ReportList *reports, + bool do_undo) { const int mode_flag = OB_MODE_SCULPT; Mesh *me = BKE_mesh_from_object(ob); @@ -356,32 +459,26 @@ void ED_object_sculptmode_enter_ex(Main *bmain, ED_paint_cursor_start(paint, SCULPT_mode_poll_view3d); + bool has_multires = false; + /* Check dynamic-topology flag; re-enter dynamic-topology mode when changing modes, * As long as no data was added that is not supported. */ if (me->flag & ME_SCULPT_DYNAMIC_TOPOLOGY) { MultiresModifierData *mmd = BKE_sculpt_multires_active(scene, ob); const char *message_unsupported = nullptr; - if (me->totloop != me->totpoly * 3) { - message_unsupported = TIP_("non-triangle face"); - } - else if (mmd != nullptr) { + if (mmd != nullptr) { message_unsupported = TIP_("multi-res modifier"); + has_multires = true; } else { enum eDynTopoWarnFlag flag = SCULPT_dynamic_topology_check(scene, ob); if (flag == 0) { /* pass */ } - else if (flag & DYNTOPO_WARN_VDATA) { - message_unsupported = TIP_("vertex data"); - } else if (flag & DYNTOPO_WARN_EDATA) { message_unsupported = TIP_("edge data"); } - else if (flag & DYNTOPO_WARN_LDATA) { - message_unsupported = TIP_("face data"); - } else if (flag & DYNTOPO_WARN_MODIFIER) { message_unsupported = TIP_("constructive modifier"); } @@ -390,19 +487,47 @@ void ED_object_sculptmode_enter_ex(Main *bmain, } } - if ((message_unsupported == nullptr) || force_dyntopo) { + if (!has_multires && ((message_unsupported == nullptr) || force_dyntopo)) { /* Needed because we may be entering this mode before the undo system loads. */ wmWindowManager *wm = static_cast(bmain->wm.first); - bool has_undo = wm->undo_stack != nullptr; + bool has_undo = do_undo && wm->undo_stack != nullptr; + /* Undo push is needed to prevent memory leak. */ if (has_undo) { SCULPT_undo_push_begin_ex(ob, "Dynamic topology enable"); } + + bool need_bmlog = !ob->sculpt->bm_log; + SCULPT_dynamic_topology_enable_ex(bmain, depsgraph, scene, ob); + if (has_undo) { SCULPT_undo_push_node(ob, nullptr, SCULPT_UNDO_DYNTOPO_BEGIN); SCULPT_undo_push_end(ob); } + else if (need_bmlog) { + if (ob->sculpt->bm_log) { + BM_log_free(ob->sculpt->bm_log, true); + ob->sculpt->bm_log = nullptr; + } + + if (ob->sculpt->bm_idmap) { + BM_idmap_destroy(ob->sculpt->bm_idmap); + ob->sculpt->bm_idmap = nullptr; + } + + /* Recreate idmap and log. */ + + BKE_sculpt_ensure_idmap(ob); + + /* See if we can rebuild the log from the undo stack. */ + SCULPT_undo_ensure_bmlog(ob); + + /* Create an empty log if reconstruction failed. */ + if (!ob->sculpt->bm_log) { + ob->sculpt->bm_log = BM_log_create(ob->sculpt->bm, ob->sculpt->bm_idmap); + } + } } else { BKE_reportf( @@ -424,7 +549,7 @@ void ED_object_sculptmode_enter(bContext *C, Depsgraph *depsgraph, ReportList *r ViewLayer *view_layer = CTX_data_view_layer(C); BKE_view_layer_synced_ensure(scene, view_layer); Object *ob = BKE_view_layer_active_object_get(view_layer); - ED_object_sculptmode_enter_ex(bmain, depsgraph, scene, ob, false, reports); + ED_object_sculptmode_enter_ex(bmain, depsgraph, scene, ob, false, reports, true); } void ED_object_sculptmode_exit_ex(Main *bmain, Depsgraph *depsgraph, Scene *scene, Object *ob) @@ -446,6 +571,11 @@ void ED_object_sculptmode_exit_ex(Main *bmain, Depsgraph *depsgraph, Scene *scen DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); } + /* Leave sculpt mode. We do this here to prevent + * the depsgraph spawning a PBVH_FACES after disabling + * dynamic topology below. */ + ob->mode &= ~mode_flag; + if (me->flag & ME_SCULPT_DYNAMIC_TOPOLOGY) { /* Dynamic topology must be disabled before exiting sculpt * mode to ensure the undo stack stays in a consistent @@ -456,9 +586,6 @@ void ED_object_sculptmode_exit_ex(Main *bmain, Depsgraph *depsgraph, Scene *scen me->flag |= ME_SCULPT_DYNAMIC_TOPOLOGY; } - /* Leave sculpt mode. */ - ob->mode &= ~mode_flag; - BKE_sculptsession_free(ob); paint_cursor_delete_textures(); @@ -506,7 +633,7 @@ static int sculpt_mode_toggle_exec(bContext *C, wmOperator *op) if (depsgraph) { depsgraph = CTX_data_ensure_evaluated_depsgraph(C); } - ED_object_sculptmode_enter_ex(bmain, depsgraph, scene, ob, false, op->reports); + ED_object_sculptmode_enter_ex(bmain, depsgraph, scene, ob, false, op->reports, true); BKE_paint_toolslots_brush_validate(bmain, &ts->sculpt->paint); if (ob->mode & mode_flag) { @@ -599,6 +726,7 @@ void SCULPT_geometry_preview_lines_update(bContext *C, SculptSession *ss, float if (totpoints + (ni.size * 2) < max_preview_verts) { PBVHVertRef to_v = ni.vertex; int to_v_i = ni.index; + ss->preview_vert_list[totpoints] = from_v; totpoints++; ss->preview_vert_list[totpoints] = to_v; @@ -623,15 +751,153 @@ void SCULPT_geometry_preview_lines_update(bContext *C, SculptSession *ss, float ss->preview_vert_count = totpoints; } -static int sculpt_sample_color_invoke(bContext *C, wmOperator *op, const wmEvent * /*event*/) +#define SAMPLE_COLOR_PREVIEW_SIZE 60 +#define SAMPLE_COLOR_OFFSET_X -15 +#define SAMPLE_COLOR_OFFSET_Y -15 +typedef struct SampleColorCustomData { + void *draw_handle; + Object *active_object; + + float mval[2]; + + float initial_color[4]; + float sampled_color[4]; +} SampleColorCustomData; + +static void sculpt_sample_color_draw(const bContext * /* C */, ARegion * /* ar */, void *arg) { + SampleColorCustomData *sccd = (SampleColorCustomData *)arg; + GPU_line_width(2.0f); + GPU_line_smooth(true); + uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + immBindBuiltinProgram(GPU_SHADER_2D_POINT_UNIFORM_SIZE_UNIFORM_COLOR_AA); + + immUniform1f("size", 16.0f); + + const float origin_x = sccd->mval[0] + SAMPLE_COLOR_OFFSET_X; + const float origin_y = sccd->mval[1] + SAMPLE_COLOR_OFFSET_Y; + + immUniformColor3fvAlpha(sccd->sampled_color, 1.0f); + immRectf(pos, + origin_x, + origin_y, + origin_x - SAMPLE_COLOR_PREVIEW_SIZE, + origin_y - SAMPLE_COLOR_PREVIEW_SIZE); + + immUniformColor3fvAlpha(sccd->initial_color, 1.0f); + immRectf(pos, + origin_x - SAMPLE_COLOR_PREVIEW_SIZE, + origin_y, + origin_x - 2.0f * SAMPLE_COLOR_PREVIEW_SIZE, + origin_y - SAMPLE_COLOR_PREVIEW_SIZE); + + immUnbindProgram(); + GPU_line_smooth(false); +} + +static bool sculpt_sample_color_update_from_base(bContext *C, + const wmEvent *event, + SampleColorCustomData *sccd) +{ + Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); + Base *base_sample = ED_view3d_give_base_under_cursor(C, event->mval); + if (base_sample == nullptr) { + return false; + } + + Object *object_sample = base_sample->object; + if (object_sample->type != OB_MESH) { + return false; + } + + Object *ob_eval = DEG_get_evaluated_object(depsgraph, object_sample); + Mesh *me_eval = BKE_object_get_evaluated_mesh(ob_eval); + MPropCol *vcol = static_cast( + CustomData_get_layer_for_write(&me_eval->vdata, CD_PROP_COLOR, me_eval->totvert)); + + if (!vcol) { + return false; + } + + ARegion *region = CTX_wm_region(C); + float global_loc[3]; + if (!ED_view3d_autodist_simple(region, event->mval, global_loc, 0, nullptr)) { + return false; + } + + float object_loc[3]; + mul_v3_m4v3(object_loc, ob_eval->world_to_object, global_loc); + + BVHTreeFromMesh bvh; + BKE_bvhtree_from_mesh_get(&bvh, me_eval, BVHTREE_FROM_VERTS, 2); + BVHTreeNearest nearest; + nearest.index = -1; + nearest.dist_sq = FLT_MAX; + BLI_bvhtree_find_nearest(bvh.tree, object_loc, &nearest, bvh.nearest_callback, &bvh); + if (nearest.index == -1) { + return false; + } + free_bvhtree_from_mesh(&bvh); + + copy_v4_v4(sccd->sampled_color, vcol[nearest.index].color); + IMB_colormanagement_scene_linear_to_srgb_v3(sccd->sampled_color, sccd->sampled_color); + return true; +} + +static int sculpt_sample_color_modal(bContext *C, wmOperator *op, const wmEvent *event) +{ + ARegion *region = CTX_wm_region(C); + Sculpt *sd = CTX_data_tool_settings(C)->sculpt; + Scene *scene = CTX_data_scene(C); + Brush *brush = BKE_paint_brush(&sd->paint); + Object *ob = CTX_data_active_object(C); + SculptSession *ss = ob->sculpt; + + SampleColorCustomData *sccd = (SampleColorCustomData *)op->customdata; + + ePaintMode mode = BKE_paintmode_get_active_from_context(C); + + /* Finish operation on release. */ + if (event->val == KM_RELEASE) { + float color_srgb[3]; + copy_v3_v3(color_srgb, sccd->sampled_color); + BKE_brush_color_set(scene, brush, sccd->sampled_color); + WM_event_add_notifier(C, NC_BRUSH | NA_EDITED, brush); + ED_region_draw_cb_exit(region->type, sccd->draw_handle); + ED_region_tag_redraw(region); + MEM_freeN(sccd); + ss->draw_faded_cursor = false; + return OPERATOR_FINISHED; + } + + SculptCursorGeometryInfo sgi; + sccd->mval[0] = event->mval[0]; + sccd->mval[1] = event->mval[1]; + + const bool over_mesh = SCULPT_cursor_geometry_info_update(C, &sgi, sccd->mval, false, false); + if (over_mesh) { + PBVHVertRef active_vertex = SCULPT_active_vertex_get(ss); + SCULPT_vertex_color_get(ss, active_vertex, sccd->sampled_color); + IMB_colormanagement_scene_linear_to_srgb_v3(sccd->sampled_color, sccd->sampled_color); + } + else { + sculpt_sample_color_update_from_base(C, event, sccd); + } + + ss->draw_faded_cursor = true; + ED_region_tag_redraw(region); + + return OPERATOR_RUNNING_MODAL; +} + +static int sculpt_sample_color_invoke(bContext *C, wmOperator *op, const wmEvent * /* event */) +{ + ARegion *region = CTX_wm_region(C); Sculpt *sd = CTX_data_tool_settings(C)->sculpt; Scene *scene = CTX_data_scene(C); Object *ob = CTX_data_active_object(C); Brush *brush = BKE_paint_brush(&sd->paint); SculptSession *ss = ob->sculpt; - PBVHVertRef active_vertex = SCULPT_active_vertex_get(ss); - float active_vertex_color[4]; if (!SCULPT_handles_colors_report(ss, op->reports)) { return OPERATOR_CANCELLED; @@ -639,21 +905,28 @@ static int sculpt_sample_color_invoke(bContext *C, wmOperator *op, const wmEvent BKE_sculpt_update_object_for_edit(CTX_data_depsgraph_pointer(C), ob, true, false, false); - /* No color attribute? Set color to white. */ - if (!SCULPT_has_colors(ss)) { - copy_v4_fl(active_vertex_color, 1.0f); - } - else { - SCULPT_vertex_color_get(ss, active_vertex, active_vertex_color); - } + const PBVHVertRef active_vertex = SCULPT_active_vertex_get(ss); + float active_vertex_color[4]; + + SCULPT_vertex_color_get(ss, active_vertex, active_vertex_color); float color_srgb[3]; IMB_colormanagement_scene_linear_to_srgb_v3(color_srgb, active_vertex_color); BKE_brush_color_set(scene, brush, color_srgb); - WM_event_add_notifier(C, NC_BRUSH | NA_EDITED, brush); + SampleColorCustomData *sccd = MEM_cnew("Sample Color Custom Data"); + copy_v4_v4(sccd->sampled_color, active_vertex_color); + copy_v4_v4(sccd->initial_color, BKE_brush_color_get(scene, brush)); - return OPERATOR_FINISHED; + sccd->draw_handle = ED_region_draw_cb_activate( + region->type, sculpt_sample_color_draw, sccd, REGION_DRAW_POST_PIXEL); + + op->customdata = sccd; + + WM_event_add_modal_handler(C, op); + ED_region_tag_redraw(region); + + return OPERATOR_RUNNING_MODAL; } static void SCULPT_OT_sample_color(wmOperatorType *ot) @@ -665,6 +938,7 @@ static void SCULPT_OT_sample_color(wmOperatorType *ot) /* api callbacks */ ot->invoke = sculpt_sample_color_invoke; + ot->modal = sculpt_sample_color_modal; ot->poll = SCULPT_mode_poll; ot->flag = OPTYPE_REGISTER; @@ -724,7 +998,7 @@ struct MaskByColorContiguousFloodFillData { float threshold; bool invert; float *new_mask; - float initial_color[3]; + float initial_color[4]; }; static void do_mask_by_color_contiguous_update_nodes_cb(void *__restrict userdata, @@ -759,11 +1033,11 @@ static void do_mask_by_color_contiguous_update_nodes_cb(void *__restrict userdat static bool sculpt_mask_by_color_contiguous_floodfill_cb( SculptSession *ss, PBVHVertRef from_v, PBVHVertRef to_v, bool is_duplicate, void *userdata) { - int from_v_i = BKE_pbvh_vertex_to_index(ss->pbvh, from_v); - int to_v_i = BKE_pbvh_vertex_to_index(ss->pbvh, to_v); - MaskByColorContiguousFloodFillData *data = static_cast( userdata); + int to_v_i = BKE_pbvh_vertex_to_index(ss->pbvh, to_v); + int from_v_i = BKE_pbvh_vertex_to_index(ss->pbvh, from_v); + float current_color[4]; SCULPT_vertex_color_get(ss, to_v, current_color); @@ -857,11 +1131,12 @@ static void do_mask_by_color_task_cb(void *__restrict userdata, PBVHVertexIter vd; BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { - float col[4]; - SCULPT_vertex_color_get(ss, vd.vertex, col); - const float current_mask = *vd.mask; - const float new_mask = sculpt_mask_by_color_delta_get(active_color, col, threshold, invert); + float vcolor[4]; + + SCULPT_vertex_color_get(ss, vd.vertex, vcolor); + + const float new_mask = sculpt_mask_by_color_delta_get(active_color, vcolor, threshold, invert); *vd.mask = sculpt_mask_by_color_final_mask_get(current_mask, new_mask, invert, preserve_mask); if (current_mask == *vd.mask) { @@ -923,11 +1198,11 @@ static int sculpt_mask_by_color_invoke(bContext *C, wmOperator *op, const wmEven BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true, false); SCULPT_vertex_random_access_ensure(ss); - /* Tools that are not brushes do not have the brush gizmo to update the vertex as the mouse move, - * so it needs to be updated here. */ + /* Tools that are not brushes do not have the brush gizmo to update the vertex as the mouse + * move, so it needs to be updated here. */ SculptCursorGeometryInfo sgi; const float mval_fl[2] = {float(event->mval[0]), float(event->mval[1])}; - SCULPT_cursor_geometry_info_update(C, &sgi, mval_fl, false); + SCULPT_cursor_geometry_info_update(C, &sgi, mval_fl, false, false); SCULPT_undo_push_begin(ob, op); BKE_sculpt_color_layer_create_if_needed(ob); @@ -941,6 +1216,8 @@ static int sculpt_mask_by_color_invoke(bContext *C, wmOperator *op, const wmEven BKE_pbvh_ensure_node_loops(ss->pbvh); } + BKE_sculpt_mask_layers_ensure(depsgraph, CTX_data_main(C), ob, ss->multires.modifier); + if (RNA_boolean_get(op->ptr, "contiguous")) { sculpt_mask_by_color_contiguous(ob, active_vertex, threshold, invert, preserve_mask); } @@ -992,6 +1269,378 @@ static void SCULPT_OT_mask_by_color(wmOperatorType *ot) 1.0f); } +static int sculpt_reset_brushes_exec(bContext *C, wmOperator *op) +{ + Main *bmain = CTX_data_main(C); + + LISTBASE_FOREACH (Brush *, br, &bmain->brushes) { + if (br->ob_mode != OB_MODE_SCULPT) { + continue; + } + BKE_brush_sculpt_reset(br); + WM_event_add_notifier(C, NC_BRUSH | NA_EDITED, br); + } + + return OPERATOR_FINISHED; +} + +static void SCULPT_OT_reset_brushes(struct wmOperatorType *ot) +{ + /* Identifiers. */ + ot->name = "Reset Sculpt Brushes"; + ot->idname = "SCULPT_OT_reset_brushes"; + ot->description = "Resets all sculpt brushes to their default value"; + + /* API callbacks. */ + ot->exec = sculpt_reset_brushes_exec; + ot->poll = SCULPT_mode_poll; + + ot->flag = OPTYPE_REGISTER; +} + +static int sculpt_set_limit_surface_exec(bContext *C, wmOperator * /* op */) +{ + Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); + Object *ob = CTX_data_active_object(C); + BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true, false); + SculptSession *ss = ob->sculpt; + + if (!ss) { + return OPERATOR_FINISHED; + } + + SCULPT_vertex_random_access_ensure(ss); + + SculptAttributeParams params = {.permanent = false, .simple_array = false}; + + if (!ss->attrs.limit_surface) { + ss->attrs.limit_surface = BKE_sculpt_attribute_ensure( + ob, ATTR_DOMAIN_POINT, CD_PROP_FLOAT3, SCULPT_ATTRIBUTE_NAME(limit_surface), ¶ms); + } + + const SculptAttribute *scl = ss->attrs.limit_surface; + + const int totvert = SCULPT_vertex_count_get(ss); + const bool weighted = false; + for (int i = 0; i < totvert; i++) { + PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); + float *f = SCULPT_vertex_attr_get(vertex, scl); + + SCULPT_neighbor_coords_average(ss, f, vertex, 0.0, true, weighted); + } + + return OPERATOR_FINISHED; +} + +static void SCULPT_OT_set_limit_surface(wmOperatorType *ot) +{ + /* Identifiers. */ + ot->name = "Set Limit Surface"; + ot->idname = "SCULPT_OT_set_limit_surface"; + ot->description = "Calculates and stores a limit surface from the current mesh"; + + /* API callbacks. */ + ot->exec = sculpt_set_limit_surface_exec; + ot->poll = SCULPT_mode_poll; + + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +typedef struct BMLinkItem { + struct BMLinkItem *next, *prev; + BMVert *item; + int depth; +} BMLinkItem; + +static int sculpt_regularize_rake_exec(bContext *C, wmOperator *op) +{ + Object *ob = CTX_data_active_object(C); + SculptSession *ss = ob->sculpt; + + Object *sculpt_get_vis_object(bContext * C, SculptSession * ss, char *name); + void sculpt_end_vis_object(bContext * C, SculptSession * ss, Object * ob, BMesh * bm); + + BMeshCreateParams params = {}; + params.create_unique_ids = params.use_toolflags = false; + + Object *visob = sculpt_get_vis_object(C, ss, "rakevis"); + BMesh *visbm = BM_mesh_create(&bm_mesh_allocsize_default, ¶ms); + + if (!ss) { + printf("mising sculpt session\n"); + return OPERATOR_CANCELLED; + } + if (!ss->bm) { + printf("bmesh only!\n"); + return OPERATOR_CANCELLED; + } + + BMesh *bm = ss->bm; + BMIter iter; + BMVert *v; + + PBVHNode **nodes = nullptr; + int totnode; + + int idx = CustomData_get_named_layer_index(&bm->vdata, CD_PROP_COLOR, "_rake_temp"); + if (idx < 0) { + printf("no rake temp\n"); + return OPERATOR_CANCELLED; + } + + int cd_vcol = bm->vdata.layers[idx].offset; + int cd_vcol_vis = -1; + + idx = CustomData_get_named_layer_index(&bm->vdata, CD_PROP_COLOR, "_rake_vis"); + if (idx >= 0) { + cd_vcol_vis = bm->vdata.layers[idx].offset; + } + + for (int step = 0; step < 33; step++) { + BLI_mempool *nodepool = BLI_mempool_create(sizeof(BMLinkItem), bm->totvert, 4196, 0); + + BKE_pbvh_get_nodes(ss->pbvh, PBVH_Leaf, &nodes, &totnode); + + SCULPT_undo_push_begin(ob, op); + for (int i = 0; i < totnode; i++) { + SCULPT_ensure_dyntopo_node_undo(ob, nodes[i], SCULPT_UNDO_COLOR, -1); + BKE_pbvh_node_mark_update_color(nodes[i]); + } + SCULPT_undo_push_end(ob); + + MEM_SAFE_FREE(nodes); + + BMVert **stack = nullptr; + BLI_array_declare(stack); + + bm->elem_index_dirty |= BM_VERT; + BM_mesh_elem_index_ensure(bm, BM_VERT); + + BLI_bitmap *visit = BLI_BITMAP_NEW(bm->totvert, "regularize rake visit bitmap"); + + BMVert **verts = MEM_cnew_array(bm->totvert, "verts"); + BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) { + verts[v->head.index] = v; + v->head.hflag &= ~BM_ELEM_SELECT; + } + + RNG *rng = BLI_rng_new((uint)BLI_thread_rand(0)); + BLI_rng_shuffle_array(rng, verts, sizeof(void *), bm->totvert); + + for (int i = 0; i < bm->totvert; i++) { + BMVert *v = verts[i]; + PBVHVertRef vertex = {(intptr_t)v}; + + MSculptVert *mv = BKE_PBVH_SCULPTVERT(ss->cd_sculpt_vert, v); + int *boundflag = SCULPT_vertex_attr_get(vertex, ss->attrs.boundary_flags); + + if (*boundflag & (SCULPT_CORNER_MESH | SCULPT_CORNER_FACE_SET | SCULPT_CORNER_SHARP | + SCULPT_CORNER_SEAM)) { + continue; + } + + if (BLI_BITMAP_TEST(visit, v->head.index)) { + continue; + } + + // v->head.hflag |= BM_ELEM_SELECT; + + float *dir = BM_ELEM_CD_PTR(v, cd_vcol); + normalize_v3(dir); + + BMLinkItem *node = static_cast(BLI_mempool_alloc(nodepool)); + node->next = node->prev = nullptr; + node->item = v; + node->depth = 0; + + BLI_BITMAP_SET(visit, v->head.index, true); + + ListBase queue = {node, node}; + const int boundtest = SCULPT_BOUNDARY_MESH | SCULPT_BOUNDARY_FACE_SET | + SCULPT_BOUNDARY_SEAM | SCULPT_BOUNDARY_SHARP; + while (queue.first) { + BMLinkItem *node2 = static_cast(BLI_poptail(&queue)); + BMVert *v2 = node2->item; + + float *dir2 = BM_ELEM_CD_PTR(v2, cd_vcol); + + if (cd_vcol_vis >= 0) { + float *color = BM_ELEM_CD_PTR(v2, cd_vcol_vis); + color[0] = color[1] = color[2] = (float)(node2->depth % 5) / 5.0f; + color[3] = 1.0f; + } + + if (step % 5 != 0 && node2->depth > 15) { + // break; + } + // dir2[0] = dir2[1] = dir2[2] = (float)(node2->depth % 5) / 5.0f; + // dir2[3] = 1.0f; + + BMIter viter; + BMEdge *e; + + int closest_vec_to_perp( + float dir[3], float r_dir2[3], float no[3], float *buckets, float w); + + // float buckets[8] = {0}; + float tmp[3]; + float dir32[3]; + float avg[3] = {0.0f}; + float tot = 0.0f; + + // angle_on_axis_v3v3v3_v3 + float tco[3]; + zero_v3(tco); + add_v3_fl(tco, 1000.0f); + // madd_v3_v3fl(tco, v2->no, -dot_v3v3(v2->no, tco)); + + float tanco[3]; + add_v3_v3v3(tanco, v2->co, dir2); + + SCULPT_dyntopo_check_disk_sort(ss, (PBVHVertRef){.i = (intptr_t)v2}); + + float lastdir3[3]; + float firstdir3[3]; + bool first = true; + float thsum = 0.0f; + + // don't propegate across singularities + + BM_ITER_ELEM (e, &viter, v2, BM_EDGES_OF_VERT) { + // e = l->e; + BMVert *v3 = BM_edge_other_vert(e, v2); + float *dir3 = BM_ELEM_CD_PTR(v3, cd_vcol); + float dir32[3]; + + copy_v3_v3(dir32, dir3); + + if (first) { + first = false; + copy_v3_v3(firstdir3, dir32); + } + else { + float th = saacos(dot_v3v3(dir32, lastdir3)); + thsum += th; + } + + copy_v3_v3(lastdir3, dir32); + + add_v3_v3(avg, dir32); + tot += 1.0f; + } + + thsum += saacos(dot_v3v3(lastdir3, firstdir3)); + bool sing = thsum >= M_PI * 0.5f; + + // still apply smoothing even with singularity? + if (tot > 0.0f && !(*boundflag & boundtest)) { + mul_v3_fl(avg, 1.0 / tot); + interp_v3_v3v3(dir2, dir2, avg, sing ? 0.15 : 0.25); + normalize_v3(dir2); + } + + if (sing) { + v2->head.hflag |= BM_ELEM_SELECT; + + if (node2->depth == 0) { + continue; + } + } + + BM_ITER_ELEM (e, &viter, v2, BM_EDGES_OF_VERT) { + BMVert *v3 = BM_edge_other_vert(e, v2); + float *dir3 = BM_ELEM_CD_PTR(v3, cd_vcol); + + if (BLI_BITMAP_TEST(visit, v3->head.index)) { + continue; + } + + copy_v3_v3(dir32, dir3); + madd_v3_v3fl(dir32, v2->no, -dot_v3v3(dir3, v2->no)); + normalize_v3(dir32); + + if (dot_v3v3(dir32, dir2) < 0) { + negate_v3(dir32); + } + + cross_v3_v3v3(tmp, dir32, v2->no); + normalize_v3(tmp); + + if (dot_v3v3(tmp, dir2) < 0) { + negate_v3(tmp); + } + + float th1 = fabsf(saacos(dot_v3v3(dir2, dir32))); + float th2 = fabsf(saacos(dot_v3v3(dir2, tmp))); + + if (th2 < th1) { + copy_v3_v3(dir32, tmp); + } + + madd_v3_v3fl(dir32, v3->no, -dot_v3v3(dir32, v3->no)); + normalize_v3(dir32); + copy_v3_v3(dir3, dir32); + + // int bits = closest_vec_to_perp(dir2, dir32, v2->no, buckets, 1.0f); + +#if 0 + MSculptVert *mv3 = BKE_PBVH_SCULPTVERT(ss->cd_sculpt_vert, v3); + PBVHVertRef vertex3 = {(intptr_t)v3}; + + int *boundflag3 = (int *)SCULPT_vertex_attr_get(vertex3, ss->attrs.boundary_flags); + if (*boundflag3 & boundtest) { + continue; + } +#endif + + BLI_BITMAP_SET(visit, v3->head.index, true); + + BMLinkItem *node3 = (BMLinkItem *)BLI_mempool_alloc(nodepool); + node3->next = node3->prev = nullptr; + node3->item = v3; + node3->depth = node2->depth + 1; + + BLI_addhead(&queue, node3); + } + + BLI_mempool_free(nodepool, node2); + } + } + + MEM_SAFE_FREE(verts); + BLI_array_free(stack); + BLI_mempool_destroy(nodepool); + MEM_SAFE_FREE(visit); + } + + BMVert *v3; + BM_ITER_MESH (v3, &iter, bm, BM_VERTS_OF_MESH) { + float visco[3]; + float *dir3 = BM_ELEM_CD_PTR(v3, cd_vcol); + + madd_v3_v3v3fl(visco, v3->co, v3->no, 0.001); + BMVert *vis1 = BM_vert_create(visbm, visco, nullptr, BM_CREATE_NOP); + + madd_v3_v3v3fl(visco, visco, dir3, 0.003); + BMVert *vis2 = BM_vert_create(visbm, visco, nullptr, BM_CREATE_NOP); + BM_edge_create(visbm, vis1, vis2, nullptr, BM_CREATE_NOP); + + float tan[3]; + cross_v3_v3v3(tan, dir3, v3->no); + madd_v3_v3fl(visco, tan, 0.001); + + vis1 = BM_vert_create(visbm, visco, nullptr, BM_CREATE_NOP); + BM_edge_create(visbm, vis1, vis2, nullptr, BM_CREATE_NOP); + } + + DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_GEOM | ND_DATA, ob->data); + + sculpt_end_vis_object(C, ss, visob, visbm); + + return OPERATOR_FINISHED; +} + enum CavityBakeMixMode { AUTOMASK_BAKE_MIX, AUTOMASK_BAKE_MULTIPLY, @@ -1167,7 +1816,7 @@ static int sculpt_bake_cavity_exec(bContext *C, wmOperator *op) BLI_task_parallel_range(0, totnode, &tdata, sculpt_bake_cavity_exec_task_cb, &settings); MEM_SAFE_FREE(nodes); - SCULPT_automasking_cache_free(tdata.automasking); + SCULPT_automasking_cache_free(ss, ob, tdata.automasking); BKE_pbvh_update_vertex_data(ss->pbvh, PBVH_UpdateMask); SCULPT_undo_push_end(ob); @@ -1178,6 +1827,18 @@ static int sculpt_bake_cavity_exec(bContext *C, wmOperator *op) return OPERATOR_FINISHED; } +void SCULPT_OT_regularize_rake_directions(wmOperatorType *ot) +{ + ot->name = "Regularize Rake Directions"; + ot->idname = "SCULPT_OT_regularize_rake_directions"; + ot->description = "Development operator"; + + /* API callbacks. */ + ot->poll = SCULPT_mode_poll; + ot->exec = sculpt_regularize_rake_exec; + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + static void cavity_bake_ui(bContext *C, wmOperator *op) { uiLayout *layout = op->layout; @@ -1343,10 +2004,10 @@ static int sculpt_reveal_all_exec(bContext *C, wmOperator *op) const int cd_mask = CustomData_get_offset(&ss->bm->vdata, CD_PAINT_MASK); BM_ITER_MESH (v, &iter, ss->bm, BM_VERTS_OF_MESH) { - BM_log_vert_before_modified(ss->bm_log, v, cd_mask); + BM_log_vert_before_modified(ss->bm, ss->bm_log, v); } BM_ITER_MESH (f, &iter, ss->bm, BM_FACES_OF_MESH) { - BM_log_face_modified(ss->bm_log, f); + BM_log_face_modified(ss->bm, ss->bm_log, f); } SCULPT_face_visibility_all_set(ss, true); @@ -1393,6 +2054,7 @@ void ED_operatortypes_sculpt(void) WM_operatortype_append(SCULPT_OT_brush_stroke); WM_operatortype_append(SCULPT_OT_sculptmode_toggle); WM_operatortype_append(SCULPT_OT_set_persistent_base); + WM_operatortype_append(SCULPT_OT_set_limit_surface); WM_operatortype_append(SCULPT_OT_dynamic_topology_toggle); WM_operatortype_append(SCULPT_OT_optimize); WM_operatortype_append(SCULPT_OT_symmetrize); @@ -1406,7 +2068,6 @@ void ED_operatortypes_sculpt(void) WM_operatortype_append(SCULPT_OT_face_sets_create); WM_operatortype_append(SCULPT_OT_face_sets_change_visibility); WM_operatortype_append(SCULPT_OT_face_sets_randomize_colors); - WM_operatortype_append(SCULPT_OT_face_sets_init); WM_operatortype_append(SCULPT_OT_cloth_filter); WM_operatortype_append(SCULPT_OT_face_sets_edit); WM_operatortype_append(SCULPT_OT_face_set_lasso_gesture); @@ -1421,7 +2082,14 @@ void ED_operatortypes_sculpt(void) WM_operatortype_append(SCULPT_OT_dyntopo_detail_size_edit); WM_operatortype_append(SCULPT_OT_mask_init); + WM_operatortype_append(SCULPT_OT_face_sets_init); + WM_operatortype_append(SCULPT_OT_reset_brushes); + WM_operatortype_append(SCULPT_OT_ipmask_filter); + + WM_operatortype_append(SCULPT_OT_spatial_sort_mesh); + WM_operatortype_append(SCULPT_OT_expand); WM_operatortype_append(SCULPT_OT_mask_from_cavity); + WM_operatortype_append(SCULPT_OT_regularize_rake_directions); WM_operatortype_append(SCULPT_OT_reveal_all); } diff --git a/source/blender/editors/sculpt_paint/sculpt_paint_color.cc b/source/blender/editors/sculpt_paint/sculpt_paint_color.cc index 1096639fe95..b1941d36f89 100644 --- a/source/blender/editors/sculpt_paint/sculpt_paint_color.cc +++ b/source/blender/editors/sculpt_paint/sculpt_paint_color.cc @@ -13,6 +13,7 @@ #include "BLI_task.h" #include "DNA_meshdata_types.h" +#include "DNA_scene_types.h" #include "BKE_brush.h" #include "BKE_colorband.h" @@ -44,8 +45,7 @@ static void do_color_smooth_task_cb_exec(void *__restrict userdata, PBVHVertexIter vd; SculptBrushTest test; - SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( - ss, &test, data->brush->falloff_shape); + SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init(ss, &test); const int thread_id = BLI_task_parallel_thread_id(tls); AutomaskingNodeData automask_data; @@ -71,12 +71,17 @@ static void do_color_smooth_task_cb_exec(void *__restrict userdata, &automask_data); float smooth_color[4]; - SCULPT_neighbor_color_average(ss, smooth_color, vd.vertex); - float col[4]; + float color[4]; - SCULPT_vertex_color_get(ss, vd.vertex, col); - blend_color_interpolate_float(col, col, smooth_color, fade); - SCULPT_vertex_color_set(ss, vd.vertex, col); + SCULPT_neighbor_color_average(ss, smooth_color, vd.vertex); + + SCULPT_vertex_color_get(ss, vd.vertex, color); + blend_color_interpolate_float(color, color, smooth_color, fade); + SCULPT_vertex_color_set(ss, vd.vertex, color); + + if (vd.is_mesh) { + BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); + } } BKE_pbvh_vertex_iter_end; } @@ -90,17 +95,14 @@ static void do_paint_brush_task_cb_ex(void *__restrict userdata, const Brush *brush = data->brush; const float bstrength = fabsf(ss->cache->bstrength); + const SculptAttribute *buffer_scl = data->scl; + + const bool do_accum = brush->flag & BRUSH_ACCUMULATE; + PBVHVertexIter vd; - PBVHColorBufferNode *color_buffer; - - SculptOrigVertData orig_data; - SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COLOR); - - color_buffer = BKE_pbvh_node_color_buffer_get(data->nodes[n]); SculptBrushTest test; - SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( - ss, &test, data->brush->falloff_shape); + SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init(ss, &test); const int thread_id = BLI_task_parallel_thread_id(tls); float brush_color[4] = {0.0f, 0.0f, 0.0f, 1.0f}; @@ -111,6 +113,9 @@ static void do_paint_brush_task_cb_ex(void *__restrict userdata, IMB_colormanagement_srgb_to_scene_linear_v3(brush_color, brush_color); + /* get un-pressure-mapped alpha */ + float alpha = BKE_brush_alpha_get(ss->scene, brush); + AutomaskingNodeData automask_data; SCULPT_automasking_node_begin( data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]); @@ -134,12 +139,26 @@ static void do_paint_brush_task_cb_ex(void *__restrict userdata, } BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { - SCULPT_orig_vert_data_update(&orig_data, &vd); + SCULPT_vertex_check_origdata(ss, vd.vertex); + + // check if we have a new stroke, in which we need to zero + // our temp layer. do this here before the brush check + // to ensure any geomtry dyntopo might subdivide has + // valid state. + float *color_buffer = SCULPT_vertex_attr_get(vd.vertex, + buffer_scl); // mv->origcolor; + if (SCULPT_stroke_id_test(ss, vd.vertex, STROKEID_USER_PREV_COLOR)) { + zero_v4(color_buffer); + } bool affect_vertex = false; float distance_to_stroke_location = 0.0f; if (brush->tip_roundness < 1.0f) { - affect_vertex = SCULPT_brush_test_cube(&test, vd.co, data->mat, brush->tip_roundness); + affect_vertex = SCULPT_brush_test_cube(&test, + vd.co, + data->mat, + brush->tip_roundness, + brush->falloff_shape != PAINT_FALLOFF_SHAPE_TUBE); distance_to_stroke_location = ss->cache->radius * test.dist; } else { @@ -186,19 +205,34 @@ static void do_paint_brush_task_cb_ex(void *__restrict userdata, /* Interpolate with the wet_mix color for wet paint mixing. */ blend_color_interpolate_float( paint_color, paint_color, wet_mix_color, ss->cache->paint_brush.wet_mix); - blend_color_mix_float(color_buffer->color[vd.i], color_buffer->color[vd.i], paint_color); + blend_color_mix_float(color_buffer, color_buffer, paint_color); /* Final mix over the original color using brush alpha. We apply auto-making again * at this point to avoid washing out non-binary masking modes like cavity masking. */ float automasking = SCULPT_automasking_factor_get( ss->cache->automasking, ss, vd.vertex, &automask_data); - mul_v4_v4fl(buffer_color, color_buffer->color[vd.i], brush->alpha * automasking); + mul_v4_v4fl(buffer_color, color_buffer, brush->alpha * automasking); - float col[4]; - SCULPT_vertex_color_get(ss, vd.vertex, col); - IMB_blend_color_float(col, orig_data.col, buffer_color, IMB_BlendMode(brush->blend)); - CLAMP4(col, 0.0f, 1.0f); - SCULPT_vertex_color_set(ss, vd.vertex, col); + float vcolor[4]; + SCULPT_vertex_color_get(ss, vd.vertex, vcolor); + + if (do_accum) { + mul_v4_fl(buffer_color, fade); + + IMB_blend_color_float(vcolor, vcolor, buffer_color, IMB_BlendMode(brush->blend)); + vcolor[3] = 1.0f; + } + else { + MSculptVert *mv = SCULPT_vertex_get_sculptvert(ss, vd.vertex); + IMB_blend_color_float(vcolor, mv->origcolor, buffer_color, IMB_BlendMode(brush->blend)); + } + + CLAMP4(vcolor, 0.0f, 1.0f); + SCULPT_vertex_color_set(ss, vd.vertex, vcolor); + + if (vd.is_mesh) { + BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); + } } BKE_pbvh_vertex_iter_end; } @@ -218,8 +252,7 @@ static void do_sample_wet_paint_task_cb(void *__restrict userdata, PBVHVertexIter vd; SculptBrushTest test; - SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( - ss, &test, data->brush->falloff_shape); + SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init(ss, &test); test.radius *= data->brush->wet_paint_radius_factor; test.radius_squared = test.radius * test.radius; @@ -250,6 +283,7 @@ static void sample_wet_paint_reduce(const void *__restrict /*userdata*/, } void SCULPT_do_paint_brush(PaintModeSettings *paint_mode_settings, + Scene *scene, Sculpt *sd, Object *ob, PBVHNode **nodes, @@ -262,8 +296,8 @@ void SCULPT_do_paint_brush(PaintModeSettings *paint_mode_settings, return; } - Brush *brush = BKE_paint_brush(&sd->paint); SculptSession *ss = ob->sculpt; + Brush *brush = BKE_paint_brush(&sd->paint); if (!SCULPT_has_colors(ss)) { return; @@ -307,14 +341,24 @@ void SCULPT_do_paint_brush(PaintModeSettings *paint_mode_settings, } } + float brush_color[4] = {0.0f, 0.0f, 0.0f, 1.0f}; + + if (ss->cache->invert) { + copy_v4_v4(brush_color, BKE_brush_secondary_color_get(scene, brush)); + } + else { + copy_v4_v4(brush_color, BKE_brush_color_get(scene, brush)); + } + /* Smooth colors mode. */ if (ss->cache->alt_smooth) { - SculptThreadedTaskData data{}; + SculptThreadedTaskData data; data.sd = sd; data.ob = ob; data.brush = brush; data.nodes = nodes; data.mat = mat; + data.brush_color = brush_color; TaskParallelSettings settings; BKE_pbvh_parallel_range_settings(&settings, true, totnode); @@ -327,11 +371,12 @@ void SCULPT_do_paint_brush(PaintModeSettings *paint_mode_settings, /* Wet paint color sampling. */ float wet_color[4] = {0.0f}; if (ss->cache->paint_brush.wet_mix > 0.0f) { - SculptThreadedTaskData task_data{}; + SculptThreadedTaskData task_data; task_data.sd = sd; task_data.ob = ob; task_data.nodes = nodes; task_data.brush = brush; + task_data.brush_color = brush_color; SampleWetPaintTLSData swptd; swptd.tot_samples = 0; @@ -361,18 +406,40 @@ void SCULPT_do_paint_brush(PaintModeSettings *paint_mode_settings, } } + SculptAttribute *buffer_scl; + + SculptAttributeParams params; + params.stroke_only = true; + + SculptAttributeParams params_id = {}; + params_id.nointerp = params.stroke_only = true; + + if (!ss->attrs.smear_previous) { + ss->attrs.smear_previous = BKE_sculpt_attribute_ensure( + ob, ATTR_DOMAIN_POINT, CD_PROP_COLOR, SCULPT_ATTRIBUTE_NAME(smear_previous), ¶ms); + } + + buffer_scl = ss->attrs.smear_previous; + SCULPT_stroke_id_ensure(ob); + /* Threaded loop over nodes. */ - SculptThreadedTaskData data{}; + SculptThreadedTaskData data; data.sd = sd; data.ob = ob; data.brush = brush; data.nodes = nodes; data.wet_mix_sampled_color = wet_color; data.mat = mat; + data.scl = buffer_scl; + data.brush_color = brush_color; TaskParallelSettings settings; BKE_pbvh_parallel_range_settings(&settings, true, totnode); BLI_task_parallel_range(0, totnode, &data, do_paint_brush_task_cb_ex, &settings); + + if (brush->vcol_boundary_factor > 0.0f) { + // SCULPT_smooth_vcol_boundary(sd, ob, nodes, totnode, brush->vcol_boundary_factor); + } } static void do_smear_brush_task_cb_exec(void *__restrict userdata, @@ -384,11 +451,12 @@ static void do_smear_brush_task_cb_exec(void *__restrict userdata, const Brush *brush = data->brush; const float bstrength = ss->cache->bstrength; + const float blend = brush->smear_deform_blend; + PBVHVertexIter vd; SculptBrushTest test; - SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( - ss, &test, data->brush->falloff_shape); + SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init(ss, &test); const int thread_id = BLI_task_parallel_thread_id(tls); float brush_delta[3]; @@ -425,7 +493,9 @@ static void do_smear_brush_task_cb_exec(void *__restrict userdata, float current_disp[3]; float current_disp_norm[3]; float interp_color[4]; - copy_v4_v4(interp_color, ss->cache->prev_colors[vd.index]); + float *prev_color = SCULPT_vertex_attr_get(vd.vertex, data->scl); + + copy_v4_v4(interp_color, prev_color); float no[3]; SCULPT_vertex_normal_get(ss, vd.vertex, no); @@ -501,7 +571,7 @@ static void do_smear_brush_task_cb_exec(void *__restrict userdata, continue; } - const float *neighbor_color = ss->cache->prev_colors[ni.index]; + const float *neighbor_color = SCULPT_vertex_attr_get(ni.vertex, data->scl); float color_interp = -dot_v3v3(current_disp_norm, vertex_disp_norm); /* Square directional weight to get a somewhat sharper result. */ @@ -522,7 +592,8 @@ static void do_smear_brush_task_cb_exec(void *__restrict userdata, float col[4]; SCULPT_vertex_color_get(ss, vd.vertex, col); - blend_color_interpolate_float(col, ss->cache->prev_colors[vd.index], interp_color, fade); + + blend_color_interpolate_float(col, prev_color, interp_color, fade * blend); SCULPT_vertex_color_set(ss, vd.vertex, col); } BKE_pbvh_vertex_iter_end; @@ -537,36 +608,38 @@ static void do_smear_store_prev_colors_task_cb_exec(void *__restrict userdata, PBVHVertexIter vd; BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { - SCULPT_vertex_color_get(ss, vd.vertex, ss->cache->prev_colors[vd.index]); + SCULPT_vertex_color_get(ss, vd.vertex, SCULPT_vertex_attr_get(vd.vertex, data->scl)); } BKE_pbvh_vertex_iter_end; } void SCULPT_do_smear_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) { - Brush *brush = BKE_paint_brush(&sd->paint); SculptSession *ss = ob->sculpt; + Brush *brush = BKE_paint_brush(&sd->paint); if (!SCULPT_has_colors(ss) || ss->cache->bstrength == 0.0f) { return; } - const int totvert = SCULPT_vertex_count_get(ss); + SCULPT_vertex_random_access_ensure(ss); - if (!ss->cache->prev_colors) { - ss->cache->prev_colors = MEM_cnew_array(totvert, __func__); - for (int i = 0; i < totvert; i++) { - PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); + if (!ss->attrs.smear_previous) { + SculptAttributeParams params = {}; + params.stroke_only = true; - SCULPT_vertex_color_get(ss, vertex, ss->cache->prev_colors[i]); - } + ss->attrs.smear_previous = BKE_sculpt_attribute_ensure( + ob, ATTR_DOMAIN_POINT, CD_PROP_COLOR, SCULPT_ATTRIBUTE_NAME(smear_previous), ¶ms); } + SculptAttribute *prev_scl = ss->attrs.smear_previous; + BKE_curvemapping_init(brush->curve); - SculptThreadedTaskData data{}; + SculptThreadedTaskData data; data.sd = sd; data.ob = ob; + data.scl = prev_scl; data.brush = brush; data.nodes = nodes; diff --git a/source/blender/editors/sculpt_paint/sculpt_paint_image.cc b/source/blender/editors/sculpt_paint/sculpt_paint_image.cc index 5d7c4687bc1..73a5529fff3 100644 --- a/source/blender/editors/sculpt_paint/sculpt_paint_image.cc +++ b/source/blender/editors/sculpt_paint/sculpt_paint_image.cc @@ -175,22 +175,24 @@ template class PaintingKernel { continue; } + PBVHVertRef fakevert = BKE_pbvh_make_vref(0LL); + float4 color = image_accessor.read_pixel(image_buffer); const float3 normal(0.0f, 0.0f, 0.0f); const float3 face_normal(0.0f, 0.0f, 0.0f); const float mask = 0.0f; - const float falloff_strength = SCULPT_brush_strength_factor( - ss, - brush, - pixel_pos, - sqrtf(test.dist), - normal, - face_normal, - mask, - BKE_pbvh_make_vref(PBVH_REF_NONE), - thread_id, - automask_data); + const float falloff_strength = SCULPT_brush_strength_factor(ss, + brush, + pixel_pos, + sqrtf(test.dist), + normal, + face_normal, + mask, + fakevert, + thread_id, + automask_data); + float4 paint_color = brush_color * falloff_strength * brush_strength; float4 buffer_color; @@ -244,7 +246,7 @@ template class PaintingKernel { } void init_brush_test() { - brush_test_fn = SCULPT_brush_test_init_with_falloff_shape(ss, &test, brush->falloff_shape); + brush_test_fn = SCULPT_brush_test_init(ss, &test); } /** diff --git a/source/blender/editors/sculpt_paint/sculpt_poly_loop.c b/source/blender/editors/sculpt_paint/sculpt_poly_loop.c new file mode 100644 index 00000000000..631dbb04231 --- /dev/null +++ b/source/blender/editors/sculpt_paint/sculpt_poly_loop.c @@ -0,0 +1,322 @@ +/* + * 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) 2020 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup edsculpt + */ + +#include "MEM_guardedalloc.h" + +#include "BLI_blenlib.h" +#include "BLI_hash.h" +#include "BLI_math.h" +#include "BLI_task.h" + +#include "DNA_brush_types.h" +#include "DNA_customdata_types.h" +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" + +#include "BKE_brush.h" +#include "BKE_ccg.h" +#include "BKE_colortools.h" +#include "BKE_context.h" +#include "BKE_customdata.h" +#include "BKE_mesh.h" +#include "BKE_mesh_fair.h" +#include "BKE_mesh_mapping.h" +#include "BKE_multires.h" +#include "BKE_node.h" +#include "BKE_object.h" +#include "BKE_paint.h" +#include "BKE_pbvh.h" +#include "BKE_scene.h" + +#include "DEG_depsgraph.h" + +#include "WM_api.h" +#include "WM_message.h" +#include "WM_toolsystem.h" +#include "WM_types.h" + +#include "ED_object.h" +#include "ED_screen.h" +#include "ED_sculpt.h" +#include "ED_view3d.h" +#include "paint_intern.h" +#include "sculpt_intern.h" + +#include "RNA_access.h" +#include "RNA_define.h" + +#include "bmesh.h" + +#include +#include + +static void sculpt_poly_loop_topology_data_ensure(Object *ob) +{ + SculptSession *ss = ob->sculpt; + Mesh *mesh = BKE_object_get_original_mesh(ob); + + if (!ss->epmap) { + BKE_mesh_edge_poly_map_create(&ss->epmap, + &ss->epmap_mem, + mesh->medge, + mesh->totedge, + mesh->mpoly, + mesh->totpoly, + mesh->mloop, + mesh->totloop); + } + if (!ss->vemap) { + const float(*vert_positions)[3] = BKE_mesh_vert_positions(mesh); + + BKE_mesh_vert_edge_map_create(&ss->vemap, + &ss->vemap_mem, + vert_positions, + mesh->medge, + mesh->totvert, + mesh->totedge, + false); + } +} + +#define SCULPT_FACE_SET_LOOP_STEP_NONE -1 +static bool sculpt_poly_loop_step(SculptSession *ss, + const int from_poly, + const int edge, + int *r_next_poly) +{ + if (!ss->epmap) { + return false; + } + + int next_poly = SCULPT_FACE_SET_LOOP_STEP_NONE; + for (int i = 0; i < ss->epmap[edge].count; i++) { + if (ss->epmap[edge].indices[i] != from_poly) { + next_poly = ss->epmap[edge].indices[i]; + } + } + + if (next_poly == SCULPT_FACE_SET_LOOP_STEP_NONE) { + return false; + } + + *r_next_poly = next_poly; + return true; +} + +static int sculpt_poly_loop_opposite_edge_in_quad(SculptSession *ss, + const int poly, + const int edge) +{ + if (ss->mpoly[poly].totloop != 4) { + return edge; + } + + int edge_index_in_poly = 0; + for (int i = 0; i < ss->mpoly[poly].totloop; i++) { + if (edge == ss->mloop[ss->mpoly[poly].loopstart + i].e) { + edge_index_in_poly = i; + break; + } + } + + const int next_edge_index_in_poly = (edge_index_in_poly + 2) % 4; + return ss->mloop[ss->mpoly[poly].loopstart + next_edge_index_in_poly].e; +} + +PBVHEdgeRef sculpt_poly_loop_initial_edge_from_cursor(Object *ob) +{ + SculptSession *ss = ob->sculpt; + Mesh *mesh = BKE_object_get_original_mesh(ob); + + sculpt_poly_loop_topology_data_ensure(ob); + + float *location = ss->cursor_location; + + float(*vert_positions)[3] = SCULPT_mesh_deformed_positions_get(ss); + MPoly *initial_poly = &mesh->mpoly[ss->active_face.i]; + + if (initial_poly->totloop != 4) { + return (PBVHEdgeRef){.i = PBVH_REF_NONE}; + } + + int closest_vert = mesh->mloop[initial_poly->loopstart].v; + for (int i = 0; i < initial_poly->totloop; i++) { + if (len_squared_v3v3(vert_positions[ss->mloop[initial_poly->loopstart + i].v], location) < + len_squared_v3v3(vert_positions[closest_vert], location)) { + closest_vert = ss->mloop[initial_poly->loopstart + i].v; + } + } + + int initial_edge = ss->vemap[closest_vert].indices[0]; + int closest_vert_on_initial_edge = mesh->medge[initial_edge].v1 == closest_vert ? + mesh->medge[initial_edge].v2 : + mesh->medge[initial_edge].v1; + for (int i = 0; i < ss->vemap[closest_vert].count; i++) { + const int edge_index = ss->vemap[closest_vert].indices[i]; + const int other_vert = mesh->medge[edge_index].v1 == closest_vert ? + mesh->medge[edge_index].v2 : + mesh->medge[edge_index].v1; + if (dist_to_line_segment_v3( + location, vert_positions[closest_vert], vert_positions[other_vert]) < + dist_to_line_segment_v3(location, + vert_positions[closest_vert], + vert_positions[closest_vert_on_initial_edge])) { + initial_edge = edge_index; + closest_vert_on_initial_edge = other_vert; + } + } + return (PBVHEdgeRef){.i = initial_edge}; +} + +static void sculpt_poly_loop_iterate_and_fill(SculptSession *ss, + const int initial_poly, + const int initial_edge, + BLI_bitmap *poly_loop) +{ + int current_poly = initial_poly; + int current_edge = initial_edge; + int next_poly = SCULPT_FACE_SET_LOOP_STEP_NONE; + int max_steps = ss->totfaces; + + BLI_BITMAP_ENABLE(poly_loop, initial_poly); + + while (max_steps && sculpt_poly_loop_step(ss, current_poly, current_edge, &next_poly)) { + if (ss->face_sets[next_poly] == initial_poly) { + break; + } + if (ss->face_sets[next_poly] < 0) { + break; + } + if (ss->mpoly[next_poly].totloop != 4) { + break; + } + + BLI_BITMAP_ENABLE(poly_loop, next_poly); + current_edge = sculpt_poly_loop_opposite_edge_in_quad(ss, next_poly, current_edge); + current_poly = next_poly; + max_steps--; + } +} + +static void sculpt_poly_loop_symm_poly_find(Object *ob, + const int poly_index, + const int edge_index, + const char symm_it, + int *r_poly_index, + int *r_edge_index) +{ + if (symm_it == 0) { + *r_poly_index = poly_index; + *r_edge_index = edge_index; + return; + } + + SculptSession *ss = ob->sculpt; + Mesh *mesh = BKE_object_get_original_mesh(ob); + float(*vert_positions)[3] = SCULPT_mesh_deformed_positions_get(ss); + + MPoly *original_poly = &mesh->mpoly[poly_index]; + float original_poly_center[3]; + BKE_mesh_calc_poly_center( + original_poly, &mesh->mloop[original_poly->loopstart], vert_positions, original_poly_center); + + float symm_poly_center[3]; + flip_v3_v3(symm_poly_center, original_poly_center, symm_it); + + float min_poly_dist = FLT_MAX; + int search_poly_index = poly_index; + + for (int i = 0; i < mesh->totpoly; i++) { + MPoly *poly = &mesh->mpoly[i]; + float poly_center[3]; + BKE_mesh_calc_poly_center(poly, &mesh->mloop[poly->loopstart], vert_positions, poly_center); + const float dist_to_poly_squared = len_squared_v3v3(symm_poly_center, poly_center); + if (dist_to_poly_squared < min_poly_dist) { + min_poly_dist = dist_to_poly_squared; + search_poly_index = i; + } + } + + *r_poly_index = search_poly_index; + MPoly *search_poly = &mesh->mpoly[search_poly_index]; + + float original_edge_center[3]; + MEdge *original_edge = &mesh->medge[edge_index]; + mid_v3_v3v3(original_edge_center, vert_positions[original_edge->v1], vert_positions[original_edge->v2]); + + float symm_edge_center[3]; + flip_v3_v3(symm_edge_center, original_edge_center, symm_it); + + float min_edge_dist = FLT_MAX; + int search_edge_index = edge_index; + + for (int i = 0; i < search_poly->totloop; i++) { + MLoop *loop = &mesh->mloop[search_poly->loopstart + i]; + MEdge *edge = &mesh->medge[loop->e]; + float edge_center[3]; + mid_v3_v3v3(edge_center, vert_positions[edge->v1], vert_positions[edge->v2]); + const float dist_to_edge_squared = len_squared_v3v3(symm_edge_center, edge_center); + if (dist_to_edge_squared < min_edge_dist) { + min_edge_dist = dist_to_edge_squared; + search_edge_index = loop->e; + } + + *r_edge_index = search_edge_index; + } +} + +BLI_bitmap *sculpt_poly_loop_from_cursor(Object *ob) +{ + SculptSession *ss = ob->sculpt; + Mesh *mesh = BKE_object_get_original_mesh(ob); + BLI_bitmap *poly_loop = BLI_BITMAP_NEW(mesh->totpoly, "poly loop"); + + sculpt_poly_loop_topology_data_ensure(ob); + const PBVHEdgeRef initial_edge = sculpt_poly_loop_initial_edge_from_cursor(ob); + const PBVHFaceRef initial_poly = ss->active_face; + + const int initial_edge_i = BKE_pbvh_edge_to_index(ss->pbvh, initial_edge); + const int initial_poly_i = BKE_pbvh_face_to_index(ss->pbvh, initial_poly); + + const char symm = SCULPT_mesh_symmetry_xyz_get(ob); + for (char symm_it = 0; symm_it <= symm; symm_it++) { + if (!SCULPT_is_symmetry_iteration_valid(symm_it, symm)) { + continue; + } + + int initial_poly_symm; + int initial_edge_symm; + sculpt_poly_loop_symm_poly_find( + ob, initial_poly_i, initial_edge_i, symm_it, &initial_poly_symm, &initial_edge_symm); + + const int initial_edge_opposite = sculpt_poly_loop_opposite_edge_in_quad( + ss, initial_poly_symm, initial_edge_symm); + + sculpt_poly_loop_iterate_and_fill(ss, initial_poly_symm, initial_edge_symm, poly_loop); + sculpt_poly_loop_iterate_and_fill(ss, initial_poly_symm, initial_edge_opposite, poly_loop); + } + + return poly_loop; +} diff --git a/source/blender/editors/sculpt_paint/sculpt_pose.cc b/source/blender/editors/sculpt_paint/sculpt_pose.cc index 0dfbe7c59a2..07981588239 100644 --- a/source/blender/editors/sculpt_paint/sculpt_pose.cc +++ b/source/blender/editors/sculpt_paint/sculpt_pose.cc @@ -134,6 +134,75 @@ static void pose_solve_scale_chain(SculptPoseIKChain *ik_chain, const float scal } } +static void do_pose_brush_bend_task_cb_ex(void *__restrict userdata, + const int n, + const TaskParallelTLS *__restrict /* tls */) +{ + SculptThreadedTaskData *data = static_cast(userdata); + SculptSession *ss = data->ob->sculpt; + SculptPoseIKChain *ik_chain = ss->cache->pose_ik_chain; + SculptPoseIKChainSegment *segments = ik_chain->segments; + const Brush *brush = data->brush; + + if (fabsf(ik_chain->bend_factor) <= 0.00001f) { + return; + } + + float final_pos[3]; + + SculptOrigVertData orig_data; + SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COORDS); + + PBVHVertexIter vd; + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { + SCULPT_orig_vert_data_update(&orig_data, vd.vertex); + + const float ik_chain_weight = segments[0].weights[vd.index] * + (1.0f - SCULPT_vertex_mask_get(ss, vd.vertex)); + if (ik_chain_weight == 0.0f) { + continue; + } + + float orig_co[3]; + mul_v3_m4v3(orig_co, ik_chain->bend_mat_inv, orig_data.co); + + const float bend_factor = ik_chain->bend_factor; + + if (fabsf(bend_factor) <= 0.0000001f) { + continue; + } + + if (orig_co[0] < 0.0f) { + continue; + } + + const float theta = orig_co[0] * bend_factor; + const float sint = sinf(theta); + const float cost = cosf(theta); + + float new_co[3]; + new_co[0] = -(orig_co[1] - 1.0f / bend_factor) * sint; + new_co[1] = (orig_co[1] - 1.0f / bend_factor) * cost + 1.0f / bend_factor; + new_co[2] = orig_co[2]; + + float final_co[3]; + float disp[3]; + mul_v3_m4v3(final_co, ik_chain->bend_mat, new_co); + + sub_v3_v3v3(disp, final_co, orig_data.co); + mul_v3_fl(disp, ik_chain_weight); + add_v3_v3v3(final_pos, orig_data.co, disp); + + float *target_co = SCULPT_brush_deform_target_vertex_co_get(ss, brush->deform_target, &vd); + copy_v3_v3(target_co, final_pos); + + if (vd.is_mesh) { + BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); + } + } + BKE_pbvh_vertex_iter_end; +} + static void do_pose_brush_task_cb_ex(void *__restrict userdata, const int n, const TaskParallelTLS *__restrict /*tls*/) @@ -155,7 +224,7 @@ static void do_pose_brush_task_cb_ex(void *__restrict userdata, data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]); BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { - SCULPT_orig_vert_data_update(&orig_data, &vd); + SCULPT_orig_vert_data_update(&orig_data, vd.vertex); SCULPT_automasking_node_update(ss, &automask_data, &vd); float total_disp[3]; @@ -534,8 +603,6 @@ void SCULPT_pose_calc_pose_data(Sculpt *sd, float *r_pose_origin, float *r_pose_factor) { - SCULPT_vertex_random_access_ensure(ss); - /* Calculate the pose rotation point based on the boundaries of the brush factor. */ SculptFloodFill flood; SCULPT_floodfill_init(ss, &flood); @@ -642,6 +709,7 @@ static int pose_brush_num_effective_segments(const Brush *brush) * artifacts in the areas affected by multiple segments. */ if (ELEM(brush->pose_deform_type, BRUSH_POSE_DEFORM_SCALE_TRASLATE, + BRUSH_POSE_DEFORM_BEND, BRUSH_POSE_DEFORM_SQUASH_STRETCH)) { return 1; } @@ -655,6 +723,7 @@ static SculptPoseIKChain *pose_ik_chain_init_topology(Sculpt *sd, const float initial_location[3], const float radius) { + SCULPT_vertex_random_access_ensure(ss); const float chain_segment_len = radius * (1.0f + br->pose_offset); float next_chain_segment_target[3]; @@ -774,6 +843,13 @@ static SculptPoseIKChain *pose_ik_chain_init_face_sets( SCULPT_floodfill_execute(ss, &flood, pose_face_sets_floodfill_cb, &fdata); SCULPT_floodfill_free(&flood); + if (!fdata.next_face_set_found) { + for (int i = s; i < ik_chain->tot_segments; i++) { + zero_v3(ik_chain->segments[i].orig); + } + break; + } + if (fdata.tot_co > 0) { mul_v3_fl(fdata.pose_origin, 1.0f / float(fdata.tot_co)); copy_v3_v3(ik_chain->segments[s].orig, fdata.pose_origin); @@ -946,6 +1022,8 @@ SculptPoseIKChain *SCULPT_pose_ik_chain_init(Sculpt *sd, const bool use_fake_neighbors = !(br->flag2 & BRUSH_USE_CONNECTED_ONLY); + SCULPT_boundary_info_ensure(ob); + if (use_fake_neighbors) { SCULPT_fake_neighbors_ensure(sd, ob, br->disconnected_distance_max); SCULPT_fake_neighbors_enable(ob); @@ -1101,6 +1179,61 @@ static void sculpt_pose_do_squash_stretch_deform(SculptSession *ss, Brush * /*br pose_solve_scale_chain(ik_chain, scale); } +static void sculpt_pose_do_bend_deform(SculptSession *ss, Brush * /* brush */) +{ + const int totvert = SCULPT_vertex_count_get(ss); + SculptPoseIKChain *ik_chain = ss->cache->pose_ik_chain; + + if (SCULPT_stroke_is_first_brush_step_of_symmetry_pass(ss->cache)) { + sub_v3_v3v3(ik_chain->bend_mat[0], + ik_chain->segments[0].initial_head, + ik_chain->segments[0].initial_orig); + normalize_v3(ik_chain->bend_mat[0]); + copy_v3_v3(ik_chain->bend_mat[2], ss->cache->view_normal); + normalize_v3(ik_chain->bend_mat[2]); + cross_v3_v3v3(ik_chain->bend_mat[1], ik_chain->bend_mat[0], ik_chain->bend_mat[2]); + normalize_v3(ik_chain->bend_mat[1]); + copy_v3_v3(ik_chain->bend_mat[3], ik_chain->segments[0].initial_orig); + ik_chain->bend_mat[3][3] = 1.0f; + invert_m4_m4(ik_chain->bend_mat_inv, ik_chain->bend_mat); + + float lower = FLT_MAX; + float upper = -FLT_MAX; + + float smd_limit[2]; + + for (int i = 0; i < totvert; i++) { + PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); + + if (ik_chain->segments[0].weights[i] == 0.0f) { + continue; + } + float bend_space_vert_co[3]; + mul_v3_m4v3(bend_space_vert_co, ik_chain->bend_mat_inv, SCULPT_vertex_co_get(ss, vertex)); + lower = min_ff(lower, bend_space_vert_co[0]); + upper = max_ff(upper, bend_space_vert_co[0]); + } + + ik_chain->bend_upper_limit = upper; + smd_limit[1] = lower + (upper - lower) * 1.0f; + smd_limit[0] = lower + (upper - lower) * 0.0f; + ik_chain->bend_limit = max_ff(FLT_EPSILON, smd_limit[1] - smd_limit[0]); + } + + float *original_dir = ik_chain->bend_mat[0]; + float current_dir[3]; + float brush_location[3]; + add_v3_v3v3(brush_location, ss->cache->initial_location, ss->cache->grab_delta); + sub_v3_v3v3(current_dir, brush_location, ik_chain->segments[0].initial_orig); + ik_chain->bend_factor = angle_signed_on_axis_v3v3_v3( + original_dir, current_dir, ss->cache->view_normal); + if (ik_chain->bend_factor > M_PI) { + ik_chain->bend_factor = ik_chain->bend_factor - (M_PI * 2.0f); + } + + ik_chain->bend_factor = 2.0f * (ik_chain->bend_factor / ik_chain->bend_limit); +} + static void sculpt_pose_align_pivot_local_space(float r_mat[4][4], ePaintSymmetryFlags symm, ePaintSymmetryAreas symm_area, @@ -1148,6 +1281,9 @@ void SCULPT_do_pose_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) case BRUSH_POSE_DEFORM_SQUASH_STRETCH: sculpt_pose_do_squash_stretch_deform(ss, brush); break; + case BRUSH_POSE_DEFORM_BEND: + sculpt_pose_do_bend_deform(ss, brush); + break; } /* Flip the segment chain in all symmetry axis and calculate the transform matrices for each @@ -1217,7 +1353,13 @@ void SCULPT_do_pose_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) TaskParallelSettings settings; BKE_pbvh_parallel_range_settings(&settings, true, totnode); - BLI_task_parallel_range(0, totnode, &data, do_pose_brush_task_cb_ex, &settings); + + if (brush->pose_deform_type == BRUSH_POSE_DEFORM_BEND) { + BLI_task_parallel_range(0, totnode, &data, do_pose_brush_bend_task_cb_ex, &settings); + } + else { + BLI_task_parallel_range(0, totnode, &data, do_pose_brush_task_cb_ex, &settings); + } } void SCULPT_pose_ik_chain_free(SculptPoseIKChain *ik_chain) diff --git a/source/blender/editors/sculpt_paint/sculpt_replay.c b/source/blender/editors/sculpt_paint/sculpt_replay.c new file mode 100644 index 00000000000..cc647912747 --- /dev/null +++ b/source/blender/editors/sculpt_paint/sculpt_replay.c @@ -0,0 +1,1235 @@ +#include "MEM_guardedalloc.h" + +#include "DNA_brush_types.h" +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" +#include "DNA_texture_types.h" + +#include "BKE_context.h" +#include "BKE_main.h" +#include "BKE_object.h" +#include "BKE_paint.h" +#include "BKE_pbvh.h" +#include "BKE_scene.h" +#include "BKE_screen.h" + +#include "DEG_depsgraph.h" +#include "ED_view3d.h" + +#include "BLI_array.h" +#include "BLI_buffer.h" +#include "BLI_compiler_attrs.h" +#include "BLI_compiler_compat.h" +#include "BLI_dynstr.h" +#include "BLI_ghash.h" +#include "BLI_math.h" +#include "BLI_memarena.h" +#include "BLI_mempool.h" +#include "BLI_rand.h" +#include "BLI_smallhash.h" +#include "BLI_string.h" +#include "BLI_utildefines.h" + +#include "PIL_time.h" + +#include "BKE_context.h" +#include "BKE_global.h" +#include "BKE_paint.h" +#include "BKE_pbvh.h" + +#include "paint_intern.h" +#include "sculpt_intern.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "bmesh.h" +#include + +typedef struct SculptBrushSample { + Sculpt sd; // copy of sd settings + + float active_vertex_co[3]; + float active_face_co[3]; + + bool have_active_vertex; + bool have_active_face; + + StrokeCache cache; + UnifiedPaintSettings ups; + PaintStroke stroke; + + double time; +} SculptBrushSample; + +typedef struct SculptReplayLog { + SculptBrushSample *samples; + int totsample, samples_size; + Tex **textures; + int tot_textures, textures_size; + MemArena *arena; + SmallHash texmap; + + bool is_playing; +} SculptReplayLog; + +static SculptReplayLog *current_log = NULL; + +void SCULPT_replay_log_free(SculptReplayLog *log) +{ + MEM_SAFE_FREE(log->samples); + MEM_SAFE_FREE(log->textures); + + BLI_smallhash_release(&log->texmap); + BLI_memarena_free(log->arena); + MEM_freeN(log); +} + +SculptReplayLog *SCULPT_replay_log_create() +{ + SculptReplayLog *log = MEM_callocN(sizeof(*log), "SculptReplayLog"); + + log->arena = BLI_memarena_new(1024, __func__); + BLI_smallhash_init(&log->texmap); + + return log; +} + +void SCULPT_replay_log_end() +{ + if (!current_log) { + printf("could not find log!"); + return; + } + + SCULPT_replay_log_free(current_log); + current_log = NULL; +} +void SCULPT_replay_log_start() +{ + if (current_log) { + printf("%s: recording has already started. . .\n", __func__); + return; + } + + current_log = MEM_callocN(sizeof(*current_log), "sculpt replay log"); + current_log->arena = BLI_memarena_new(8192, "sculpt replay log"); +} + +#if 0 +# define WRITE(key, fmt, ...) \ + { \ + char _buf[256], _prefix[64]; \ + if (shead >= 0) { \ + sprintf(_prefix, "%s%s%s", stack[shead].prefix, stack[shead].op, key); \ + } \ + else { \ + sprintf(_prefix, "%s", key); \ + } \ + sprintf(_buf, "%s " fmt "\n", _prefix, __VA_ARGS__); \ + BLI_dynstr_append(out, _buf); \ + } \ + ((void *)0) + +# define STACK_PUSH(key, memberop) \ + shead++; \ + sprintf(stack[shead].prefix, "%s", key); \ + stack[shead].op = memberop + +# define STACK_POP() shead-- +#endif + +enum { + REPLAY_FLOAT, + REPLAY_INT, + REPLAY_VEC2, + REPLAY_VEC3, + REPLAY_VEC4, + REPLAY_STRUCT, + REPLAY_STRUCT_PTR, + REPLAY_BOOL, + REPLAY_BYTE, + REPLAY_SHORT, +}; + +struct ReplaySerialStruct; +typedef struct ReplaySerialDef { + char name[32]; + int type; //-1 is used for sentinal ending member list + int struct_offset; + struct ReplaySerialStruct *sdef; +} ReplaySerialDef; + +typedef struct ReplaySerialStruct { + char name[32]; + ReplaySerialDef *members; +} ReplaySerialStruct; + +#ifdef DEF +# undef DEF +#endif + +/* clang-format off */ +#define DEF(key, type, structtype, ...) {#key, type, offsetof(structtype, key), __VA_ARGS__} + +static ReplaySerialDef dyntopo_def[] = { + {"detail_range", REPLAY_FLOAT, offsetof(DynTopoSettings, detail_range)}, + {"detail_percent", REPLAY_FLOAT, offsetof(DynTopoSettings, detail_percent)}, + {"detail_size", REPLAY_FLOAT, offsetof(DynTopoSettings, detail_size)}, + {"constant_detail", REPLAY_FLOAT, offsetof(DynTopoSettings, constant_detail)}, + {"flag", REPLAY_SHORT, offsetof(DynTopoSettings, flag)}, + {"mode", REPLAY_SHORT, offsetof(DynTopoSettings, mode)}, + {"inherit", REPLAY_INT, offsetof(DynTopoSettings, inherit)}, + {"spacing", REPLAY_INT, offsetof(DynTopoSettings, spacing)}, + {"", -1, -1}}; +static ReplaySerialStruct DynTopoSettingsDef = {"DynTopoSettings", dyntopo_def}; + +static ReplaySerialDef paint_stroke_def[] = { + DEF(last_mouse_position, REPLAY_VEC2, PaintStroke), + DEF(last_world_space_position, REPLAY_VEC3, PaintStroke), + DEF(stroke_over_mesh, REPLAY_BOOL, PaintStroke), + DEF(stroke_distance, REPLAY_FLOAT, PaintStroke), + DEF(stroke_distance_t, REPLAY_FLOAT, PaintStroke), + DEF(stroke_started, REPLAY_BOOL, PaintStroke), + DEF(rake_started, REPLAY_BOOL, PaintStroke), + DEF(event_type, REPLAY_INT, PaintStroke), + DEF(stroke_init, REPLAY_BOOL, PaintStroke), + DEF(brush_init, REPLAY_BOOL, PaintStroke), + DEF(initial_mouse, REPLAY_VEC2, PaintStroke), + DEF(cached_size_pressure, REPLAY_FLOAT, PaintStroke), + DEF(last_pressure, REPLAY_FLOAT, PaintStroke), + DEF(stroke_mode, REPLAY_INT, PaintStroke), + DEF(last_tablet_event_pressure, REPLAY_FLOAT, PaintStroke), + DEF(pen_flip, REPLAY_INT, PaintStroke), + DEF(x_tilt, REPLAY_FLOAT, PaintStroke), + DEF(y_tilt, REPLAY_FLOAT, PaintStroke), + DEF(spacing, REPLAY_FLOAT, PaintStroke), + DEF(constrain_line, REPLAY_BOOL, PaintStroke), + DEF(constrained_pos, REPLAY_VEC2, PaintStroke), + {"", -1, -1} +}; + +static ReplaySerialStruct PaintStrokeDef = {"PaintStroke", paint_stroke_def}; + +static ReplaySerialDef brush_def[] = { + DEF(weight, REPLAY_FLOAT, Brush), + DEF(size, REPLAY_INT, Brush), + DEF(dyntopo, REPLAY_STRUCT, Brush, &DynTopoSettingsDef), + DEF(flag, REPLAY_INT, Brush), + DEF(flag2, REPLAY_INT, Brush), + DEF(automasking_flags, REPLAY_INT, Brush), + DEF(normal_radius_factor, REPLAY_FLOAT, Brush), + DEF(area_radius_factor, REPLAY_FLOAT, Brush), + DEF(wet_paint_radius_factor, REPLAY_FLOAT, Brush), + DEF(plane_trim, REPLAY_FLOAT, Brush), + DEF(height, REPLAY_FLOAT, Brush), + DEF(vcol_boundary_factor, REPLAY_FLOAT, Brush), + DEF(vcol_boundary_exponent, REPLAY_FLOAT, Brush), + DEF(topology_rake_factor, REPLAY_FLOAT, Brush), + DEF(topology_rake_radius_factor, REPLAY_FLOAT, Brush), + DEF(topology_rake_projection, REPLAY_FLOAT, Brush), + DEF(topology_rake_spacing, REPLAY_FLOAT, Brush), + DEF(tilt_strength_factor, REPLAY_FLOAT, Brush), + DEF(autosmooth_factor, REPLAY_FLOAT, Brush), + DEF(tilt_strength_factor, REPLAY_FLOAT, Brush), + DEF(autosmooth_radius_factor, REPLAY_FLOAT, Brush), + DEF(autosmooth_projection, REPLAY_FLOAT, Brush), + DEF(autosmooth_spacing, REPLAY_FLOAT, Brush), + DEF(boundary_smooth_factor, REPLAY_FLOAT, Brush), + DEF(autosmooth_fset_slide, REPLAY_FLOAT, Brush), + DEF(sculpt_tool, REPLAY_BYTE, Brush), + DEF(falloff_shape, REPLAY_BYTE, Brush), + DEF(falloff_angle, REPLAY_FLOAT, Brush), + DEF(paint_flags, REPLAY_INT, Brush), + DEF(density, REPLAY_FLOAT, Brush), + DEF(wet_persistence, REPLAY_FLOAT, Brush), + DEF(wet_mix, REPLAY_FLOAT, Brush), + DEF(flow, REPLAY_FLOAT, Brush), + DEF(hardness, REPLAY_FLOAT, Brush), + DEF(alpha, REPLAY_FLOAT, Brush), + DEF(rgb, REPLAY_VEC3, Brush), + DEF(rate, REPLAY_FLOAT, Brush), + DEF(smooth_stroke_factor, REPLAY_FLOAT, Brush), + DEF(smooth_stroke_radius, REPLAY_INT, Brush), + DEF(spacing, REPLAY_INT, Brush), + DEF(overlay_flags, REPLAY_INT, Brush), + DEF(mask_pressure, REPLAY_INT, Brush), + DEF(jitter, REPLAY_FLOAT, Brush), + DEF(overlay_flags, REPLAY_INT, Brush), + DEF(sampling_flag, REPLAY_INT, Brush), + DEF(normal_weight, REPLAY_FLOAT, Brush), + DEF(blend, REPLAY_SHORT, Brush), + DEF(concave_mask_factor, REPLAY_FLOAT, Brush), + {"", -1, -1}}; + +static ReplaySerialStruct BrushDef = {"Brush", brush_def}; + +static ReplaySerialDef stroke_cache_def[] = { + DEF(bstrength, REPLAY_FLOAT, StrokeCache), + DEF(radius, REPLAY_FLOAT, StrokeCache), + DEF(pressure, REPLAY_FLOAT, StrokeCache), + DEF(brush, REPLAY_STRUCT_PTR, StrokeCache, &BrushDef), + DEF(location, REPLAY_VEC3, StrokeCache), + DEF(view_normal, REPLAY_VEC3, StrokeCache), + DEF(true_location, REPLAY_VEC3, StrokeCache), + DEF(location, REPLAY_VEC3, StrokeCache), + DEF(initial_radius, REPLAY_FLOAT, StrokeCache), + DEF(dyntopo_pixel_radius, REPLAY_FLOAT, StrokeCache), + DEF(radius_squared, REPLAY_FLOAT, StrokeCache), + DEF(iteration_count, REPLAY_INT, StrokeCache), + DEF(special_rotation, REPLAY_FLOAT, StrokeCache), + DEF(grab_delta, REPLAY_VEC3, StrokeCache), + DEF(grab_delta_symmetry, REPLAY_VEC3, StrokeCache), + DEF(old_grab_location, REPLAY_VEC3, StrokeCache), + DEF(orig_grab_location, REPLAY_VEC3, StrokeCache), + DEF(rake_rotation, REPLAY_VEC4, StrokeCache), + DEF(rake_rotation_symmetry, REPLAY_VEC4, StrokeCache), + DEF(is_rake_rotation_valid, REPLAY_BOOL, StrokeCache), + DEF(paint_face_set, REPLAY_INT, StrokeCache), + DEF(symmetry, REPLAY_INT, StrokeCache), + DEF(boundary_symmetry, REPLAY_INT, StrokeCache), + DEF(mirror_symmetry_pass, REPLAY_INT, StrokeCache), + DEF(true_view_normal, REPLAY_VEC3, StrokeCache), + DEF(view_normal, REPLAY_VEC3, StrokeCache), + DEF(sculpt_normal, REPLAY_VEC3, StrokeCache), + DEF(sculpt_normal_symm, REPLAY_VEC3, StrokeCache), + DEF(plane_offset, REPLAY_VEC3, StrokeCache), + DEF(radial_symmetry_pass, REPLAY_INT, StrokeCache), + DEF(last_center, REPLAY_VEC3, StrokeCache), + DEF(original, REPLAY_BOOL, StrokeCache), + DEF(initial_location, REPLAY_VEC3, StrokeCache), + DEF(true_initial_location, REPLAY_VEC3, StrokeCache), + DEF(initial_normal, REPLAY_VEC3, StrokeCache), + DEF(true_initial_normal, REPLAY_VEC3, StrokeCache), + DEF(vertex_rotation, REPLAY_FLOAT, StrokeCache), + DEF(plane_trim_squared, REPLAY_FLOAT, StrokeCache), + DEF(saved_smooth_size, REPLAY_FLOAT, StrokeCache), + DEF(alt_smooth, REPLAY_BOOL, StrokeCache), + DEF(density_seed, REPLAY_FLOAT, StrokeCache), + DEF(stroke_distance, REPLAY_FLOAT, StrokeCache), + DEF(stroke_distance_t, REPLAY_FLOAT, StrokeCache), + DEF(last_dyntopo_t, REPLAY_FLOAT, StrokeCache), + DEF(scale, REPLAY_VEC3, StrokeCache), + {"", -1, -1} +}; + +static ReplaySerialStruct StrokeCacheDef = {"StrokeCache", stroke_cache_def}; + +static ReplaySerialDef paint_def[] = { + DEF(symmetry_flags, REPLAY_INT, Paint), + {"", -1, -1} +}; +static ReplaySerialStruct PaintDef = {"Paint", paint_def}; + +static ReplaySerialDef sculpt_def[] = { + DEF(paint, REPLAY_STRUCT, Sculpt, &PaintDef), + DEF(detail_size, REPLAY_FLOAT, Sculpt), + DEF(detail_range , REPLAY_FLOAT, Sculpt), + DEF(constant_detail , REPLAY_FLOAT, Sculpt), + DEF(detail_percent , REPLAY_FLOAT, Sculpt), + DEF(dyntopo_spacing , REPLAY_INT, Sculpt), + DEF(automasking_flags, REPLAY_INT, Sculpt), + DEF(flags, REPLAY_INT, Sculpt), + {"", -1, -1} +}; + +static ReplaySerialStruct SculptDef = {"Sculpt", sculpt_def}; + +static ReplaySerialDef ups_def[] = { + DEF(size, REPLAY_INT, UnifiedPaintSettings), + DEF(unprojected_radius, REPLAY_FLOAT, UnifiedPaintSettings), + DEF(alpha, REPLAY_FLOAT, UnifiedPaintSettings), + DEF(weight, REPLAY_FLOAT, UnifiedPaintSettings), + DEF(rgb, REPLAY_VEC3, UnifiedPaintSettings), + DEF(secondary_rgb, REPLAY_VEC3, UnifiedPaintSettings), + DEF(flag, REPLAY_INT, UnifiedPaintSettings), + DEF(last_rake, REPLAY_VEC2, UnifiedPaintSettings), + DEF(last_rake_angle, REPLAY_FLOAT, UnifiedPaintSettings), + DEF(last_stroke_valid, REPLAY_INT, UnifiedPaintSettings), + DEF(average_stroke_accum, REPLAY_VEC3, UnifiedPaintSettings), + DEF(unprojected_radius, REPLAY_FLOAT, UnifiedPaintSettings), + DEF(average_stroke_counter, REPLAY_FLOAT, UnifiedPaintSettings), + DEF(brush_rotation, REPLAY_FLOAT, UnifiedPaintSettings), + DEF(brush_rotation_sec, REPLAY_FLOAT, UnifiedPaintSettings), + DEF(anchored_size, REPLAY_INT, UnifiedPaintSettings), + DEF(overlap_factor, REPLAY_FLOAT, UnifiedPaintSettings), + DEF(draw_inverted, REPLAY_BYTE, UnifiedPaintSettings), + DEF(stroke_active, REPLAY_BYTE, UnifiedPaintSettings), + DEF(draw_anchored, REPLAY_BYTE, UnifiedPaintSettings), + DEF(last_location, REPLAY_VEC3, UnifiedPaintSettings), + DEF(last_hit, REPLAY_INT, UnifiedPaintSettings), + DEF(anchored_initial_mouse, REPLAY_VEC2, UnifiedPaintSettings), + DEF(pixel_radius, REPLAY_FLOAT, UnifiedPaintSettings), + DEF(initial_pixel_radius, REPLAY_FLOAT, UnifiedPaintSettings), + DEF(size_pressure_value, REPLAY_FLOAT, UnifiedPaintSettings), + DEF(tex_mouse, REPLAY_VEC2, UnifiedPaintSettings), + DEF(mask_tex_mouse, REPLAY_VEC2, UnifiedPaintSettings), + {"", -1, -1} +}; +static ReplaySerialStruct UnifiedPaintSettingsDef = { + "UnifiedPaintSettings", ups_def +}; + +static ReplaySerialDef sample_def[] = { + {"active_vertex_co", REPLAY_VEC3, offsetof(SculptBrushSample, active_vertex_co)}, + {"active_face_co", REPLAY_VEC3, offsetof(SculptBrushSample, active_face_co)}, + {"have_active_vertex", REPLAY_BOOL, offsetof(SculptBrushSample, have_active_vertex)}, + {"have_active_face", REPLAY_BOOL, offsetof(SculptBrushSample, have_active_face)}, + {"cache", REPLAY_STRUCT, offsetof(SculptBrushSample, cache), &StrokeCacheDef}, + // {"brush", REPLAY_STRUCT, offsetof(SculptBrushSample, brush), &BrushDef}, + {"sd", REPLAY_STRUCT, offsetof(SculptBrushSample, sd), &SculptDef}, + DEF(ups, REPLAY_STRUCT, SculptBrushSample, &UnifiedPaintSettingsDef), + DEF(stroke, REPLAY_STRUCT, SculptBrushSample, &PaintStrokeDef), + {"", -1, -1}}; + +static ReplaySerialStruct SculptBrushSampleDef = {"SculptBrushSample", sample_def}; + +/* clang-format on */ + +typedef struct ReplaySerializer { + struct { + char prefix[256], op[32]; + } stack[16]; + int stack_head; + DynStr *out; +} ReplaySerializer; + +static void replay_samples_ensure_size(SculptReplayLog *log); + +void replay_write_path(ReplaySerializer *state, char *key) +{ + char buf[512]; + + if (state->stack_head >= 0) { + sprintf(buf, + "%s%s%s", + state->stack[state->stack_head].prefix, + state->stack[state->stack_head].op, + key); + } + else { + sprintf(buf, "%s", key); + } + + BLI_dynstr_append(state->out, buf); +} + +void replay_push_stack(ReplaySerializer *state, char *key, char *op) +{ + state->stack_head++; + + if (state->stack_head > 0) { + sprintf(state->stack[state->stack_head].prefix, + "%s%s%s", + state->stack[state->stack_head - 1].prefix, + state->stack[state->stack_head - 1].op, + key); + } + else { + sprintf(state->stack[state->stack_head].prefix, "%s", key); + } + + sprintf(state->stack[state->stack_head].op, "%s", op); +} + +void replay_pop_stack(ReplaySerializer *state) +{ + state->stack_head--; +} + +#define SKIP_WS \ + while (i < len && ELEM(buf[i], ' ', '\t', '\r')) \ + i++ + +#define SKIP_ALL_WS \ + while (i < len && ELEM(buf[i], ' ', '\t', '\r', '\n')) \ + i++ + +#include + +static int parse_replay_member(const char *buf, int len, ReplaySerialStruct *st, void *data) +{ + char *ptr = (char *)data; + int i = 0; + int n = 0; + + SKIP_WS; + ReplaySerialDef *mdef = NULL; + + while (buf[i] != ':') { + int a = strcspn(buf + i, ".-:"); + + if (a < 0 || i + a >= len) { + break; + } + + char *name = alloca(a + 1); + memcpy(name, buf + i, a); + name[a] = 0; + + i += a; + + while (ELEM(buf[i], '-', '>', '.')) { + i++; + } + + SKIP_WS; + + ReplaySerialDef *mdef2 = st->members; + while (mdef2->type != -1) { + if (STREQ(mdef2->name, name)) { + break; + } + mdef2++; + } + + if (mdef2->type == -1) { + printf("Failed to find memer \"%s!\n", name); + return len; + } + + SKIP_WS; + + ptr += mdef2->struct_offset; + + if (mdef2->type == REPLAY_STRUCT_PTR) { + void **vptr = (void **)ptr; + + if (!*vptr) { + char *line = alloca(len + 1); + memcpy(line, buf, len); + line[len] = 0; + + printf("error; missing memory for %s\n", line); + return len; + } + + ptr = (char *)*vptr; + st = mdef2->sdef; + } + else if (mdef2->type == REPLAY_STRUCT) { + st = mdef2->sdef; + } + + mdef = mdef2; + } + + if (!mdef) { + printf("replay parse error\n"); + return len; + } + + i++; + SKIP_WS; + + switch (mdef->type) { + case REPLAY_FLOAT: { + float f = 0.0; + + sscanf(buf + i, "%f%n", &f, &n); + i += n; + *(float *)ptr = f; + break; + } + case REPLAY_INT: { + int f = 0; + + sscanf(buf + i, "%d%n", &f, &n); + i += n; + *(int *)ptr = f; + break; + } + case REPLAY_BOOL: + case REPLAY_BYTE: { + int f = 0; + + sscanf(buf + i, "%d%n", &f, &n); + i += n; + *(unsigned char *)ptr = (unsigned char)f; + break; + } + case REPLAY_VEC2: { + float f[2]; + + sscanf(buf + i, "[%f,%f]%n", &f[0], &f[1], &n); + i += n; + + copy_v2_v2((float *)ptr, f); + break; + } + case REPLAY_VEC3: { + float f[3]; + + sscanf(buf + i, "[%f,%f,%f]%n", &f[0], &f[1], &f[2], &n); + i += n; + + copy_v3_v3((float *)ptr, f); + break; + } + case REPLAY_VEC4: { + float f[4]; + + sscanf(buf + i, "[%f,%f,%f,%f]%n", &f[0], &f[1], &f[2], &f[3], &n); + i += n; + + copy_v4_v4((float *)ptr, f); + break; + } + case REPLAY_SHORT: { + int f = 0; + + sscanf(buf + i, "%d%n", &f, &n); + i += n; + *(short *)ptr = (short)f; + break; + } + default: + printf("replay parse error: invalid type %d\n", mdef->type); + break; + } + return i; +} + +// data1 is dest, data2 is source +static void replay_load(ReplaySerialStruct *st, void *data1, void *data2) +{ + ReplaySerialDef *mdef = st->members; + + while (mdef->type != -1) { + char *ptr1 = ((char *)data1) + mdef->struct_offset; + char *ptr2 = ((char *)data2) + mdef->struct_offset; + + switch (mdef->type) { + case REPLAY_STRUCT_PTR: { + void **vptr1 = (void **)ptr1; + void **vptr2 = (void **)ptr2; + + if (!*vptr1 || !*vptr2) { + printf("failed to load pointers %p %p\n", *vptr1, *vptr2); + mdef++; + continue; + } + + ptr1 = *vptr1; + ptr2 = *vptr2; + } + case REPLAY_STRUCT: + replay_load(mdef->sdef, ptr1, ptr2); + break; + case REPLAY_INT: + case REPLAY_FLOAT: + memcpy(ptr1, ptr2, sizeof(int)); + break; + case REPLAY_BYTE: + case REPLAY_BOOL: + *ptr1 = *ptr2; + break; + case REPLAY_VEC2: + memcpy(ptr1, ptr2, sizeof(float) * 2); + break; + case REPLAY_VEC3: + memcpy(ptr1, ptr2, sizeof(float) * 3); + break; + case REPLAY_VEC4: + memcpy(ptr1, ptr2, sizeof(float) * 4); + break; + case REPLAY_SHORT: + memcpy(ptr1, ptr2, 2); + break; + } + mdef++; + } +} + +void do_brush_action(struct Sculpt *sd, + struct Object *ob, + struct Brush *brush, + struct UnifiedPaintSettings *ups, + PaintModeSettings *paint_mode_settings); +void sculpt_combine_proxies(Sculpt *sd, Object *ob); +bool sculpt_tool_is_proxy_used(const char sculpt_tool); +void sculpt_stroke_update_step(bContext *C, struct PaintStroke *stroke, PointerRNA *itemptr); + +static void *hashco(float fx, float fy, float fz, float fdimen) +{ + double x = (double)fx; + double y = (double)fy; + double z = (double)fz; + double dimen = (double)fdimen; + + return (void *)((intptr_t)(z * dimen * dimen * dimen + y * dimen * dimen + x * dimen)); +} + +void SCULPT_replay_make_cube(struct bContext *C, int steps) +{ + Object *ob = CTX_data_active_object(C); + SculptSession *ss = ob->sculpt; + + if (!ss || !ss->bm) { + return; + } + + GHash *vhash = BLI_ghash_ptr_new("vhash"); + + float df = 2.0f / (float)(steps - 1); + + int hashdimen = steps * 8; + + BMVert **grid = MEM_malloc_arrayN(steps * steps * 2, sizeof(*grid), "bmvert grid"); + BMesh *bm = ss->bm; + + BM_mesh_clear(bm); + + for (int side = 0; side < 6; side++) { + int axis = side >= 3 ? side - 3 : side; + float sign = side >= 3 ? -1.0f : 1.0f; + + printf("AXIS: %d\n", axis); + + float u = -1.0f; + + for (int i = 0; i < steps; i++, u += df) { + float v = -1.0f; + + for (int j = 0; j < steps; j++, v += df) { + float co[3]; + + co[axis] = u; + co[(axis + 1) % 3] = v; + co[(axis + 2) % 3] = sign; + + // turn into sphere + normalize_v3(co); + // mul_v3_fl(co, 2.0f); + + void *key = hashco(co[0], co[1], co[2], hashdimen); + +#if 0 + printf("%.3f %.3f %.3f, key: %p i: %d j: %d df: %f, u: %f v: %f\n", + co[0], + co[1], + co[2], + key, + i, + j, + df, + u, + v); +#endif + + void **val = NULL; + + if (!BLI_ghash_ensure_p(vhash, key, &val)) { + BMVert *v2 = BM_vert_create(bm, co, NULL, BM_CREATE_NOP); + + *val = (void *)v2; + } + + BMVert *v2 = (BMVert *)*val; + int idx = j * steps + i; + + grid[idx] = v2; + } + } + + for (int i = 0; i < steps - 1; i++) { + for (int j = 0; j < steps - 1; j++) { + int idx1 = j * steps + i; + int idx2 = (j + 1) * steps + i; + int idx3 = (j + 1) * steps + i + 1; + int idx4 = j * steps + i + 1; + + BMVert *v1 = grid[idx1]; + BMVert *v2 = grid[idx2]; + BMVert *v3 = grid[idx3]; + BMVert *v4 = grid[idx4]; + + if (v1 == v2 || v1 == v3 || v1 == v4 || v2 == v3 || v2 == v4 || v3 == v4) { + printf("ERROR!\n"); + continue; + } + + if (sign >= 0) { + BMVert *vs[4] = {v4, v3, v2, v1}; + BM_face_create_verts(bm, vs, 4, NULL, BM_CREATE_NOP, true); + } + else { + BMVert *vs[4] = {v1, v2, v3, v4}; + BM_face_create_verts(bm, vs, 4, NULL, BM_CREATE_NOP, true); + } + } + } + } + + MEM_SAFE_FREE(grid); + BLI_ghash_free(vhash, NULL, NULL); + +#if 1 + // randomize + uint *rands[4]; + uint tots[4] = {bm->totvert, bm->totedge, bm->totloop, bm->totface}; + + RNG *rng = BLI_rng_new(0); + + for (uint i = 0; i < 4; i++) { + rands[i] = MEM_malloc_arrayN(tots[i], sizeof(uint), "rands[i]"); + + for (uint j = 0; j < tots[i]; j++) { + rands[i][j] = j; + } + + for (uint j = 0; j < tots[i] >> 1; j++) { + int j2 = BLI_rng_get_int(rng) % tots[i]; + SWAP(uint, rands[i][j], rands[i][j2]); + } + } + + BM_mesh_remap(bm, rands[0], rands[1], rands[3], rands[2]); + + for (int i = 0; i < 4; i++) { + MEM_SAFE_FREE(rands[i]); + } + + BLI_rng_free(rng); +#endif + + BKE_pbvh_free(ss->pbvh); + ss->pbvh = NULL; + + //XXX call BKE_sculptsession_update_attr_refs here? + + /* Redraw. */ + DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, ND_DATA | NC_OBJECT | ND_DRAW, ob); +} + +void SCULPT_replay(struct bContext *C) +{ + Object *ob = CTX_data_active_object(C); + + if (!ob) { + printf("no object\n"); + return; + } + + Scene *scene = CTX_data_scene(C); + + if (!scene) { + printf("no scene\n"); + return; + } + + Sculpt *sd = scene->toolsettings->sculpt; + + if (!sd) { + printf("no sculpt settings\n"); + return; + } + + SculptSession *ss = ob->sculpt; + + if (!ss) { + printf("object must be in sculpt mode\n"); + return; + } + + if (!current_log) { + printf("%s: no reply data\n", __func__); + return; + } + + SculptReplayLog *log = current_log; + SculptBrushSample *samp = log->samples; + Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); + + bool have_cache = ss->cache; + ViewContext vc; + + log->is_playing = true; + float last_dyntopo_t = 0.0f; + + SCULPT_undo_push_begin_ex(ob, "Replay"); + + if (!have_cache) { + ED_view3d_viewcontext_init(C, &vc, depsgraph); + } + + for (int i = 0; i < log->totsample; i++, samp++) { + if (!have_cache) { + ss->cache = &samp->cache; + ss->cache->vc = &vc; + } + else { + replay_load(&StrokeCacheDef, &samp->cache, ss->cache); + } + + replay_load(&SculptDef, &samp->sd, sd); + replay_load( + &UnifiedPaintSettingsDef, &samp->ups, &scene->toolsettings->unified_paint_settings); + + ss->cache->first_time = i == 0; + samp->ups.last_stroke_valid = i > 0; + + Brush _brush = *ss->cache->brush; + Brush *brush = &_brush; + + samp->stroke.brush = brush; + samp->stroke.ups = &samp->ups; + samp->stroke.vc = vc; + samp->sd.paint.brush = brush; + + ss->cache->stroke = &samp->stroke; + + ss->cache->last_dyntopo_t = last_dyntopo_t; + + // XXX + // sculpt_stroke_update_step(C, ss->cache->stroke, NULL); + last_dyntopo_t = ss->cache->last_dyntopo_t; + continue; + do_brush_action(sd, + ob, + brush, + &scene->toolsettings->unified_paint_settings, + &scene->toolsettings->paint_mode); + sculpt_combine_proxies(sd, ob); + + /* Hack to fix noise texture tearing mesh. */ + // sculpt_fix_noise_tear(sd, ob); + + /* TODO(sergey): This is not really needed for the solid shading, + * which does use pBVH drawing anyway, but texture and wireframe + * requires this. + * + * Could be optimized later, but currently don't think it's so + * much common scenario. + * + * Same applies to the DEG_id_tag_update() invoked from + * sculpt_flush_update_step(). + */ + if (ss->deform_modifiers_active) { + SCULPT_flush_stroke_deform(sd, ob, sculpt_tool_is_proxy_used(brush->sculpt_tool)); + } + else if (ss->shapekey_active) { + // sculpt_update_keyblock(ob); + } + + ss->cache->first_time = false; + copy_v3_v3(ss->cache->true_last_location, ss->cache->true_location); + + /* Cleanup. */ + if (brush->sculpt_tool == SCULPT_TOOL_MASK) { + SCULPT_flush_update_step(C, SCULPT_UPDATE_MASK); + } + else if (ELEM(brush->sculpt_tool, SCULPT_TOOL_PAINT, SCULPT_TOOL_SMEAR)) { + SCULPT_flush_update_step(C, SCULPT_UPDATE_COLOR); + } + else { + SCULPT_flush_update_step(C, SCULPT_UPDATE_COORDS); + } + + int update = SCULPT_UPDATE_COORDS | SCULPT_UPDATE_COLOR | SCULPT_UPDATE_VISIBILITY | + SCULPT_UPDATE_MASK; + SCULPT_flush_update_done(C, ob, update); + } + + if (!have_cache) { + ss->cache = NULL; + } + + SCULPT_undo_push_end(ob); + log->is_playing = false; +} + +void SCULPT_replay_parse(const char *buf) +{ + if (current_log) { + SCULPT_replay_log_end(); + } + + SculptReplayLog *log = current_log = SCULPT_replay_log_create(); + + int i = 0; + int n = 0; + int len = strlen(buf); + + SKIP_ALL_WS; + + int version = 0; + + sscanf(buf + i, "version:%d\n%n", &version, &n); + i += n; + + SKIP_ALL_WS; + + while (i < len) { + // find newline + + SKIP_WS; + + int end = strcspn(buf + i, "\n"); + if (end < 0) { + end = len - 1; // last line? + } + + if (end == 0) { + // empty line + i++; + continue; + } + + int nr = 0; + if (sscanf(buf + i, "samp:%d.%n", &nr, &n) == 0) { + i += end; + SKIP_ALL_WS; + continue; + } + i += n; + + log->totsample = MAX2(log->totsample, nr + 1); + replay_samples_ensure_size(log); + + SculptBrushSample *samp = log->samples + nr; + + if (!samp->cache.brush) { + samp->cache.brush = BLI_memarena_calloc(log->arena, sizeof(Brush)); + } + + i += parse_replay_member(buf + i, end, &SculptBrushSampleDef, samp); + + SKIP_ALL_WS; + } + + return; +} + +void replay_serialize_struct(ReplaySerializer *state, ReplaySerialStruct *def, void *struct_data) +{ + // DynStr *out = state->out; + + ReplaySerialDef *mdef = def->members; + char buf[256]; + + while (mdef->type >= 0) { + char *ptr = (char *)struct_data; + ptr += mdef->struct_offset; + + if (!ELEM(mdef->type, REPLAY_STRUCT, REPLAY_STRUCT_PTR)) { + replay_write_path(state, mdef->name); + } + + switch (mdef->type) { + case REPLAY_STRUCT: + case REPLAY_STRUCT_PTR: + replay_push_stack(state, mdef->name, mdef->type == REPLAY_STRUCT ? "." : "->"); + // BLI_dynstr_append(state->out, " {\n"); + if (mdef->type == REPLAY_STRUCT_PTR) { + replay_serialize_struct(state, mdef->sdef, *(void **)ptr); + } + else { + replay_serialize_struct(state, mdef->sdef, ptr); + } + replay_pop_stack(state); + // BLI_dynstr_append(state->out, "}\n"); + break; + case REPLAY_INT: + sprintf(buf, ": %d\n", *((int *)ptr)); + BLI_dynstr_append(state->out, buf); + break; + case REPLAY_FLOAT: + sprintf(buf, ": %f\n", *((float *)ptr)); + BLI_dynstr_append(state->out, buf); + break; + case REPLAY_VEC2: + sprintf(buf, ": [%f,%f]\n", ((float *)ptr)[0], ((float *)ptr)[1]); + BLI_dynstr_append(state->out, buf); + break; + case REPLAY_VEC3: + sprintf(buf, ": [%f,%f,%f]\n", ((float *)ptr)[0], ((float *)ptr)[1], ((float *)ptr)[2]); + BLI_dynstr_append(state->out, buf); + break; + case REPLAY_VEC4: + sprintf(buf, + ": [%f,%f,%f,%f]\n", + ((float *)ptr)[0], + ((float *)ptr)[1], + ((float *)ptr)[2], + ((float *)ptr)[3]); + BLI_dynstr_append(state->out, buf); + break; + case REPLAY_BOOL: + sprintf(buf, ": %s\n", *ptr ? "1" : "0"); + BLI_dynstr_append(state->out, buf); + break; + case REPLAY_BYTE: + sprintf(buf, ": %d\n", (int)*ptr); + BLI_dynstr_append(state->out, buf); + break; + case REPLAY_SHORT: + sprintf(buf, ": %d\n", (int)*((short *)ptr)); + BLI_dynstr_append(state->out, buf); + break; + } + + mdef++; + } +} + +void replay_state_init(ReplaySerializer *state) +{ + memset(state, 0, sizeof(*state)); + state->stack_head = -1; +} + +char *SCULPT_replay_serialize() +{ + if (!current_log) { + return ""; + } + + SculptReplayLog *log = current_log; + DynStr *out = BLI_dynstr_new(); + + ReplaySerializer state; + + BLI_dynstr_append(out, "version:1\n"); + + replay_state_init(&state); + state.out = out; + + for (int i = 0; i < log->totsample; i++) { + char buf[32]; + + sprintf(buf, "samp:%d", i); + replay_push_stack(&state, buf, "."); + + replay_serialize_struct(&state, &SculptBrushSampleDef, log->samples + i); + + replay_pop_stack(&state); + } + + char *ret = BLI_dynstr_get_cstring(out); + BLI_dynstr_free(out); + + return ret; +} + +static void SCULPT_replay_deserialize(SculptReplayLog *log) +{ +} + +static void replay_samples_ensure_size(SculptReplayLog *log) +{ + if (log->totsample >= log->samples_size) { + int size = (2 + log->samples_size); + size += size >> 1; + + if (!log->samples) { + log->samples = MEM_calloc_arrayN(size, sizeof(*log->samples), "log->samples"); + } + else { + log->samples = MEM_recallocN(log->samples, sizeof(*log->samples) * size); + } + + log->samples_size = size; + } +} + +static bool replay_ensure_tex(SculptReplayLog *log, MTex *tex) +{ + if (!tex->tex) { + return true; + } + + for (int i = 0; i < log->tot_textures; i++) { + if (STREQ(log->textures[i]->id.name, tex->tex->id.name)) { + return true; + } + } + + Tex *texcpy = (Tex *)BLI_memarena_alloc(log->arena, sizeof(Tex)); + *texcpy = *tex->tex; + + tex->tex = texcpy; + + if (texcpy->ima) { + Image *ima = BLI_memarena_alloc(log->arena, sizeof(*ima)); + *ima = *texcpy->ima; + texcpy->ima = ima; + } + // if (texcpy->ima && texcpy->ima->id); + + return false; +} + +void SCULPT_replay_test() +{ + SculptSession ss = {0}; + Sculpt sd = {0}; + Object ob = {0}; + StrokeCache cache = {0}; + Brush brush = {0}; + + brush.size = 1.5f; + brush.weight = 2.0f; + brush.autosmooth_factor = 2.0f; + + ss.cache = &cache; + cache.bstrength = 1.0f; + cache.radius = 1.5f; + cache.brush = &brush; + + ss.active_vertex.i = -1LL; + ss.active_face.i = -1LL; + + SCULPT_replay_log_start(); + SCULPT_replay_log_append(&sd, &ss, &ob); + char *buf = SCULPT_replay_serialize(); + + if (buf) { + printf("=========result=======\n%s\n", buf); + } + + MEM_SAFE_FREE(buf); + SCULPT_replay_log_end(); +} + +void SCULPT_replay_log_append(Sculpt *sd, SculptSession *ss, Object *ob) +{ + SculptReplayLog *log = current_log; + + if (!log || log->is_playing) { + return; + } + + log->totsample++; + replay_samples_ensure_size(log); + + SculptBrushSample *samp = log->samples + log->totsample - 1; + + if (!ss->cache) { + printf("Error!!"); + return; + } + + samp->time = PIL_check_seconds_timer(); + samp->stroke = *ss->cache->stroke; + + samp->sd = *sd; + samp->cache = *ss->cache; + + // replay_ensure_tex(log, &samp->cache->brush.mtex); + + if (ss->active_vertex.i != -1LL) { + samp->have_active_vertex = true; + // copy_v3_v3(samp->active_vertex_co, SCULPT_vertex_co_get(ss, ss->active_vertex)); + } + else { + zero_v3(samp->active_vertex_co); + samp->have_active_vertex = false; + } + + // TODO: active face + samp->have_active_face = false; +} diff --git a/source/blender/editors/sculpt_paint/sculpt_smooth.cc b/source/blender/editors/sculpt_paint/sculpt_smooth.cc index a5aedc58856..ba99ec57a06 100644 --- a/source/blender/editors/sculpt_paint/sculpt_smooth.cc +++ b/source/blender/editors/sculpt_paint/sculpt_smooth.cc @@ -7,8 +7,10 @@ #include "MEM_guardedalloc.h" +#include "BLI_alloca.h" #include "BLI_math.h" #include "BLI_task.h" +#include "BLI_vector.hh" #include "DNA_brush_types.h" #include "DNA_meshdata_types.h" @@ -24,21 +26,34 @@ #include #include -void SCULPT_neighbor_coords_average_interior(SculptSession *ss, - float result[3], - PBVHVertRef vertex) +using blender::Vector; + +ATTR_NO_OPT static void SCULPT_neighbor_coords_average_interior_ex(SculptSession *ss, + float result[3], + PBVHVertRef vertex, + float projection, + float fset_projection, + bool weighted, + eSculptBoundary bound_type, + eSculptCorner corner_type) { float avg[3] = {0.0f, 0.0f, 0.0f}; int total = 0; int neighbor_count = 0; - const bool is_boundary = SCULPT_vertex_is_boundary(ss, vertex); + + const eSculptBoundary is_boundary = SCULPT_vertex_is_boundary(ss, vertex, bound_type); + const eSculptCorner is_corner = SCULPT_vertex_is_corner(ss, vertex, corner_type); + + if ((is_boundary & SCULPT_BOUNDARY_FACE_SET) || (is_corner & SCULPT_CORNER_FACE_SET)) { + projection = max_ff(projection, fset_projection); + } SculptVertexNeighborIter ni; SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vertex, ni) { neighbor_count++; if (is_boundary) { /* Boundary vertices use only other boundary vertices. */ - if (SCULPT_vertex_is_boundary(ss, ni.vertex)) { + if (SCULPT_vertex_is_boundary(ss, ni.vertex, bound_type)) { add_v3_v3(avg, SCULPT_vertex_co_get(ss, ni.vertex)); total++; } @@ -52,7 +67,7 @@ void SCULPT_neighbor_coords_average_interior(SculptSession *ss, SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); /* Do not modify corner vertices. */ - if (neighbor_count <= 2 && is_boundary) { + if (is_corner & (SCULPT_CORNER_MESH | SCULPT_CORNER_SHARP)) { copy_v3_v3(result, SCULPT_vertex_co_get(ss, vertex)); return; } @@ -63,10 +78,42 @@ void SCULPT_neighbor_coords_average_interior(SculptSession *ss, return; } - mul_v3_v3fl(result, avg, 1.0f / total); + float no[3]; + const float *co = SCULPT_vertex_co_get(ss, vertex); + SCULPT_vertex_normal_get(ss, vertex, no); + + mul_v3_fl(avg, 1.0f / (float)total); + sub_v3_v3(avg, co); + float t = dot_v3v3(avg, no); + + madd_v3_v3fl(avg, no, -t * projection); + add_v3_v3(avg, co); + copy_v3_v3(result, avg); } -void SCULPT_bmesh_four_neighbor_average(float avg[3], float direction[3], BMVert *v) +void SCULPT_neighbor_coords_average_interior(SculptSession *ss, + float result[3], + PBVHVertRef vertex, + float projection, + float fset_projection) +{ + eSculptBoundary bound_type = SCULPT_BOUNDARY_MESH | SCULPT_BOUNDARY_FACE_SET | + SCULPT_BOUNDARY_SEAM | SCULPT_BOUNDARY_SHARP; + eSculptCorner corner_type = SCULPT_CORNER_MESH | SCULPT_CORNER_FACE_SET | SCULPT_CORNER_SEAM | + SCULPT_CORNER_SHARP; + SCULPT_neighbor_coords_average_interior_ex( + ss, result, vertex, projection, fset_projection, true, bound_type, corner_type); +} + +void SCULPT_bmesh_four_neighbor_average(SculptSession *ss, + float avg[3], + float direction[3], + struct BMVert *v, + float projection, + bool check_fsets, + int cd_temp, + int cd_sculpt_vert, + bool do_origco) { float avg_co[3] = {0.0f, 0.0f, 0.0f}; @@ -114,24 +161,19 @@ void SCULPT_bmesh_four_neighbor_average(float avg[3], float direction[3], BMVert /* Generic functions for laplacian smoothing. These functions do not take boundary vertices into * account. */ -void SCULPT_neighbor_coords_average(SculptSession *ss, float result[3], PBVHVertRef vertex) +void SCULPT_neighbor_coords_average(SculptSession *ss, + float result[3], + PBVHVertRef vertex, + float projection, + float fset_projection, + bool weighted) { - float avg[3] = {0.0f, 0.0f, 0.0f}; - int total = 0; + eSculptCorner corner_type = SCULPT_CORNER_SHARP | SCULPT_CORNER_FACE_SET; + eSculptBoundary bound_type = SCULPT_BOUNDARY_SHARP | SCULPT_BOUNDARY_SEAM | SCULPT_BOUNDARY_UV | + SCULPT_BOUNDARY_FACE_SET; - SculptVertexNeighborIter ni; - SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vertex, ni) { - add_v3_v3(avg, SCULPT_vertex_co_get(ss, ni.vertex)); - total++; - } - SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); - - if (total > 0) { - mul_v3_v3fl(result, avg, 1.0f / total); - } - else { - copy_v3_v3(result, SCULPT_vertex_co_get(ss, vertex)); - } + SCULPT_neighbor_coords_average_interior_ex( + ss, result, vertex, projection, fset_projection, weighted, bound_type, corner_type); } float SCULPT_neighbor_mask_average(SculptSession *ss, PBVHVertRef vertex) @@ -239,6 +281,10 @@ static void SCULPT_enhance_details_brush(Sculpt *sd, SCULPT_vertex_random_access_ensure(ss); SCULPT_boundary_info_ensure(ob); + float projection = brush->autosmooth_projection; + float fset_projection = brush->autosmooth_fset_slide; + bool use_weighted = brush->flag2 & BRUSH_SMOOTH_USE_AREA_WEIGHT; + if (SCULPT_stroke_is_first_brush_step(ss->cache)) { const int totvert = SCULPT_vertex_count_get(ss); ss->cache->detail_directions = static_cast( @@ -248,7 +294,7 @@ static void SCULPT_enhance_details_brush(Sculpt *sd, PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); float avg[3]; - SCULPT_neighbor_coords_average(ss, avg, vertex); + SCULPT_neighbor_coords_average(ss, avg, vertex, projection, fset_projection, use_weighted); sub_v3_v3v3(ss->cache->detail_directions[i], avg, SCULPT_vertex_co_get(ss, vertex)); } } @@ -288,6 +334,9 @@ static void do_smooth_brush_task_cb_ex(void *__restrict userdata, SCULPT_automasking_node_begin( data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]); + float fset_projection = SCULPT_get_fset_projection(ss, brush->autosmooth_fset_slide); + float projection = brush->autosmooth_projection; + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { if (!sculpt_brush_test_sq_fn(&test, vd.co)) { continue; @@ -314,7 +363,7 @@ static void do_smooth_brush_task_cb_ex(void *__restrict userdata, } else { float avg[3], val[3]; - SCULPT_neighbor_coords_average_interior(ss, avg, vd.vertex); + SCULPT_neighbor_coords_average_interior(ss, avg, vd.vertex, projection, fset_projection); sub_v3_v3v3(val, avg, vd.co); madd_v3_v3v3fl(val, vd.co, val, fade); SCULPT_clip(sd, ss, vd.co, val); @@ -390,13 +439,22 @@ void SCULPT_do_smooth_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnod } } +void SCULPT_surface_smooth_laplacian_init(Object *ob) +{ + SculptAttributeParams params = {}; + + params.stroke_only = true; + + ob->sculpt->attrs.laplacian_disp = BKE_sculpt_attribute_ensure( + ob, ATTR_DOMAIN_POINT, CD_PROP_FLOAT3, SCULPT_ATTRIBUTE_NAME(laplacian_disp), ¶ms); +} + /* HC Smooth Algorithm. */ /* From: Improved Laplacian Smoothing of Noisy Surface Meshes */ void SCULPT_surface_smooth_laplacian_step(SculptSession *ss, float *disp, const float co[3], - float (*laplacian_disp)[3], const PBVHVertRef vertex, const float origco[3], const float alpha) @@ -405,38 +463,37 @@ void SCULPT_surface_smooth_laplacian_step(SculptSession *ss, float weigthed_o[3], weigthed_q[3], d[3]; int v_index = BKE_pbvh_vertex_to_index(ss->pbvh, vertex); - SCULPT_neighbor_coords_average(ss, laplacian_smooth_co, vertex); + SCULPT_neighbor_coords_average(ss, laplacian_smooth_co, vertex, 0.0f, 1.0f, true); mul_v3_v3fl(weigthed_o, origco, alpha); mul_v3_v3fl(weigthed_q, co, 1.0f - alpha); add_v3_v3v3(d, weigthed_o, weigthed_q); - sub_v3_v3v3(laplacian_disp[v_index], laplacian_smooth_co, d); + float *laplacian_disp = SCULPT_vertex_attr_get(vertex, ss->attrs.laplacian_disp); + + sub_v3_v3v3(laplacian_disp, laplacian_smooth_co, d); sub_v3_v3v3(disp, laplacian_smooth_co, co); } -void SCULPT_surface_smooth_displace_step(SculptSession *ss, - float *co, - float (*laplacian_disp)[3], - const PBVHVertRef vertex, - const float beta, - const float fade) +void SCULPT_surface_smooth_displace_step( + SculptSession *ss, float *co, const PBVHVertRef vertex, const float beta, const float fade) { float b_avg[3] = {0.0f, 0.0f, 0.0f}; float b_current_vertex[3]; int total = 0; SculptVertexNeighborIter ni; SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vertex, ni) { - add_v3_v3(b_avg, laplacian_disp[ni.index]); + float *laplacian_disp = SCULPT_vertex_attr_get(ni.vertex, ss->attrs.laplacian_disp); + add_v3_v3(b_avg, laplacian_disp); total++; } SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); if (total > 0) { - int v_index = BKE_pbvh_vertex_to_index(ss->pbvh, vertex); + float *laplacian_disp = SCULPT_vertex_attr_get(vertex, ss->attrs.laplacian_disp); mul_v3_v3fl(b_current_vertex, b_avg, (1.0f - beta) / total); - madd_v3_v3fl(b_current_vertex, laplacian_disp[v_index], beta); + madd_v3_v3fl(b_current_vertex, laplacian_disp, beta); mul_v3_fl(b_current_vertex, clamp_f(fade, 0.0f, 1.0f)); sub_v3_v3(co, b_current_vertex); } @@ -465,7 +522,7 @@ static void SCULPT_do_surface_smooth_brush_laplacian_task_cb_ex( data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]); BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { - SCULPT_orig_vert_data_update(&orig_data, &vd); + SCULPT_orig_vert_data_update(&orig_data, vd.vertex); if (!sculpt_brush_test_sq_fn(&test, vd.co)) { continue; } @@ -484,8 +541,7 @@ static void SCULPT_do_surface_smooth_brush_laplacian_task_cb_ex( &automask_data); float disp[3]; - SCULPT_surface_smooth_laplacian_step( - ss, disp, vd.co, ss->cache->surface_smooth_laplacian_disp, vd.vertex, orig_data.co, alpha); + SCULPT_surface_smooth_laplacian_step(ss, disp, vd.co, vd.vertex, orig_data.co, alpha); madd_v3_v3fl(vd.co, disp, clamp_f(fade, 0.0f, 1.0f)); if (vd.is_mesh) { BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); @@ -530,8 +586,7 @@ static void SCULPT_do_surface_smooth_brush_displace_task_cb_ex( vd.vertex, thread_id, &automask_data); - SCULPT_surface_smooth_displace_step( - ss, vd.co, ss->cache->surface_smooth_laplacian_disp, vd.vertex, beta, fade); + SCULPT_surface_smooth_displace_step(ss, vd.co, vd.vertex, beta, fade); } BKE_pbvh_vertex_iter_end; } @@ -556,3 +611,306 @@ void SCULPT_do_surface_smooth_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, in 0, totnode, &data, SCULPT_do_surface_smooth_brush_displace_task_cb_ex, &settings); } } + +void SCULPT_reproject_cdata(SculptSession *ss, + PBVHVertRef vertex, + float origco[3], + float origno[3]) +{ + BMVert *v = (BMVert *)vertex.i; + + if (!ss->bm || !v->e) { + return; + } + + MSculptVert *mv = BKE_PBVH_SCULPTVERT(ss->cd_sculpt_vert, v); + + // int totuv = CustomData_number_of_layers(&ss->bm->ldata, CD_PROP_FLOAT2); + CustomData *ldata = &ss->bm->ldata; + + int totuv = 0; + CustomDataLayer *uvlayer = NULL; + + if (ldata->typemap[CD_PROP_FLOAT2] != -1) { + for (int i = ldata->typemap[CD_PROP_FLOAT2]; + i < ldata->totlayer && ldata->layers[i].type == CD_PROP_FLOAT2; + i++) { + totuv++; + } + + uvlayer = ldata->layers + ldata->typemap[CD_PROP_FLOAT2]; + } + + BMEdge *e; + int tag = BM_ELEM_TAG_ALT; + + float origin[3]; + float ray[3]; + + copy_v3_v3(origin, v->co); + copy_v3_v3(ray, v->no); + negate_v3(ray); + + struct IsectRayPrecalc precalc; + isect_ray_tri_watertight_v3_precalc(&precalc, ray); + + float *lastuvs = (float *)BLI_array_alloca(lastuvs, totuv * 2); + bool *snapuvs = (bool *)BLI_array_alloca(snapuvs, totuv); + + e = v->e; + + /* first clear some flags */ + do { + e->head.api_flag &= ~tag; + + if (!e->l) { + continue; + } + + BMLoop *l = e->l; + do { + l->head.hflag &= ~tag; + l->next->head.hflag &= ~tag; + l->prev->head.hflag &= ~tag; + } while ((l = l->radial_next) != e->l); + } while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e); + + Vector ls; + + bool first = true; + bool bad = false; + + for (int i = 0; i < totuv; i++) { + snapuvs[i] = true; //!(mv->flag & SCULPTVERT_UV_BOUNDARY); + } + + do { + BMLoop *l = e->l; + + if (!l) { + continue; + } +#if 0 + bool bound = l == l->radial_next; + + // check for faceset boundaries + bound = bound || (BM_ELEM_CD_GET_INT(l->f,ss->cd_faceset_offset) != + BM_ELEM_CD_GET_INT(l->radial_next->f,ss->cd_faceset_offset)); + + // check for seam and sharp edges + bound = bound || (e->head.hflag & BM_ELEM_SEAM) || !(e->head.hflag & BM_ELEM_SMOOTH); + + if (bound) { + continue; + } +#endif + do { + BMLoop *l2 = l->v != v ? l->next : l; + + if (l2->head.hflag & tag) { + continue; + } + + l2->head.hflag |= tag; + ls.append(l2); + + for (int i = 0; i < totuv; i++) { + const int cd_uv = uvlayer[i].offset; + float *luv = BM_ELEM_CD_PTR(l2, cd_uv); + + // check that we are not part of a uv seam + if (!first) { + const float dx = lastuvs[i * 2] - luv[0]; + const float dy = lastuvs[i * 2 + 1] - luv[1]; + const float eps = 0.00001f; + + if (dx * dx + dy * dy > eps) { + bad = true; + snapuvs[i] = false; + } + } + + lastuvs[i * 2] = luv[0]; + lastuvs[i * 2 + 1] = luv[1]; + } + + first = false; + + if (bad) { + break; + } + } while ((l = l->radial_next) != e->l); + + if (bad) { + break; + } + } while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e); + + if (bad || !ls.size()) { + return; + } + + int totloop = ls.size(); + + const float *v_proj_axis = v->no; + float v_proj[3][3]; + + project_plane_normalized_v3_v3v3(v_proj[1], mv->origco, v_proj_axis); + + /* original (l->prev, l, l->next) projections for each loop ('l' remains unchanged) */ + + char *_blocks = (char *)alloca(ldata->totsize * totloop); + void **blocks = BLI_array_alloca(blocks, totloop); + + for (int i = 0; i < totloop; i++, _blocks += ldata->totsize) { + blocks[i] = (void *)_blocks; + } + + float vco[3], vno[3]; + + copy_v3_v3(vco, v->co); + copy_v3_v3(vno, v->no); + + BMFace _fakef, *fakef = &_fakef; + +#if 0 + BMFace *projf = NULL; + // find face vertex projects into + for (int i = 0; i < totloop; i++) { + BMLoop *l = ls[i]; + + copy_v3_v3(ray,l->f->no); + negate_v3(ray); + + float t,uv[2]; + + //* + bool hit = isect_ray_tri_v3(origin,ray,l->prev->v->co,origco,l->next->v->co,&t,uv); + if (hit) { + projf = l->f; + break; + } //*/ + } + + if (!projf) { + return; + } +#endif + + // build fake f with original coordinates + for (int i = 0; i < totloop; i++) { + // create fake face + BMLoop *l = ls[i]; + float no[3] = {0.0f, 0.0f, 0.0f}; + + BMLoop *fakels = BLI_array_alloca(fakels, l->f->len); + BMVert *fakevs = BLI_array_alloca(fakevs, l->f->len); + BMLoop *l2 = l->f->l_first; + BMLoop *fakel = fakels; + BMVert *fakev = fakevs; + int j = 0; + + do { + *fakel = *l2; + fakel->next = fakels + ((j + 1) % l->f->len); + fakel->prev = fakels + ((j + l->f->len - 1) % l->f->len); + + *fakev = *l2->v; + fakel->v = fakev; + + SCULPT_vertex_check_origdata(ss, (PBVHVertRef){.i = (intptr_t)l2->v}); + + if (l2->v == v) { + copy_v3_v3(fakev->co, origco); + copy_v3_v3(fakev->no, origno); + add_v3_v3(no, origno); + } + else { + add_v3_v3(no, l2->v->no); + } + + fakel++; + fakev++; + j++; + } while ((l2 = l2->next) != l->f->l_first); + + *fakef = *l->f; + fakef->l_first = fakels; + + // set original face normal + // normalize_v3(no); + // copy_v3_v3(fakef->no, no); + + // interpolate + BMLoop _interpl, *interpl = &_interpl; + + MSculptVert saved = *mv; + + *interpl = *l; + interpl->head.data = blocks[i]; + // memcpy(interpl->head.data, l2->head.data, ldata->totsize); + + BM_loop_interp_from_face(ss->bm, interpl, fakef, false, false); + + *mv = saved; + + CustomData_bmesh_copy_data(&ss->bm->ldata, &ss->bm->ldata, interpl->head.data, &l->head.data); + } + + int *tots = BLI_array_alloca(tots, totuv); + + for (int i = 0; i < totuv; i++) { + lastuvs[i * 2] = lastuvs[i * 2 + 1] = 0.0f; + tots[i] = 0; + } + + // re-snap uvs + v = (BMVert *)vertex.i; + + e = v->e; + do { + if (!e->l) { + continue; + } + + BMLoop *l_iter = e->l; + do { + BMLoop *l = l_iter->v != v ? l_iter->next : l_iter; + + for (int i = 0; i < totuv; i++) { + const int cd_uv = uvlayer[i].offset; + float *luv = BM_ELEM_CD_PTR(l, cd_uv); + + add_v2_v2(lastuvs + i * 2, luv); + tots[i]++; + } + } while ((l_iter = l_iter->radial_next) != e->l); + } while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e); + + for (int i = 0; i < totuv; i++) { + if (tots[i]) { + mul_v2_fl(lastuvs + i * 2, 1.0f / (float)tots[i]); + } + } + + e = v->e; + do { + if (!e->l) { + continue; + } + + BMLoop *l_iter = e->l; + do { + BMLoop *l = l_iter->v != v ? l_iter->next : l_iter; + + for (int i = 0; i < totuv; i++) { + const int cd_uv = uvlayer[i].offset; + float *luv = BM_ELEM_CD_PTR(l, cd_uv); + + if (snapuvs[i]) { + copy_v2_v2(luv, lastuvs + i * 2); + } + } + } while ((l_iter = l_iter->radial_next) != e->l); + } while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e); +} diff --git a/source/blender/editors/sculpt_paint/sculpt_symmetrize.c b/source/blender/editors/sculpt_paint/sculpt_symmetrize.c new file mode 100644 index 00000000000..cbcf97574fb --- /dev/null +++ b/source/blender/editors/sculpt_paint/sculpt_symmetrize.c @@ -0,0 +1,302 @@ +/* + * 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) 2021 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup edsculpt + */ + +#include "MEM_guardedalloc.h" + +#include "BLI_blenlib.h" +#include "BLI_math.h" +#include "BLI_task.h" + +#include "DNA_brush_types.h" +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_object_types.h" + +#include "BKE_brush.h" +#include "BKE_ccg.h" +#include "BKE_colortools.h" +#include "BKE_context.h" +#include "BKE_mesh.h" +#include "BKE_multires.h" +#include "BKE_node.h" +#include "BKE_object.h" +#include "BKE_paint.h" +#include "BKE_pbvh.h" +#include "BKE_scene.h" + +#include "paint_intern.h" +#include "sculpt_intern.h" + +#include "GPU_immediate.h" +#include "GPU_immediate_util.h" +#include "GPU_matrix.h" +#include "GPU_state.h" + +#include "bmesh.h" + +#include +#include + +typedef uint MirrTopoHash_t; + +typedef struct MirrTopoVert_t { + MirrTopoHash_t hash; + int v_index; +} MirrTopoVert_t; + +static int mirrtopo_hash_sort(const void *l1, const void *l2) +{ + if ((MirrTopoHash_t)(intptr_t)l1 > (MirrTopoHash_t)(intptr_t)l2) { + return 1; + } + if ((MirrTopoHash_t)(intptr_t)l1 < (MirrTopoHash_t)(intptr_t)l2) { + return -1; + } + return 0; +} + +static int mirrtopo_vert_sort(const void *v1, const void *v2) +{ + if (((MirrTopoVert_t *)v1)->hash > ((MirrTopoVert_t *)v2)->hash) { + return 1; + } + if (((MirrTopoVert_t *)v1)->hash < ((MirrTopoVert_t *)v2)->hash) { + return -1; + } + return 0; +} + +void SCULPT_symmetrize_map_ensure(Object *ob) +{ + SculptSession *ss = ob->sculpt; + Mesh *me = BKE_object_get_original_mesh(ob); + + if (ss->vertex_info.symmetrize_map) { + /* Nothing to do. */ + return; + } + + MEdge *medge = NULL, *med; + + int a, last; + int totvert, totedge; + int tot_unique = -1, tot_unique_prev = -1; + int tot_unique_edges = 0, tot_unique_edges_prev; + + MirrTopoHash_t *topo_hash = NULL; + MirrTopoHash_t *topo_hash_prev = NULL; + MirrTopoVert_t *topo_pairs; + MirrTopoHash_t topo_pass = 1; + + int *index_lookup; /* direct access to mesh_topo_store->index_lookup */ + + totvert = me->totvert; + topo_hash = MEM_callocN(totvert * sizeof(MirrTopoHash_t), "TopoMirr"); + + /* Initialize the vert-edge-user counts used to detect unique topology */ + totedge = me->totedge; + medge = me->medge; + + for (a = 0, med = medge; a < totedge; a++, med++) { + const uint i1 = med->v1, i2 = med->v2; + topo_hash[i1]++; + topo_hash[i2]++; + } + + topo_hash_prev = MEM_dupallocN(topo_hash); + + tot_unique_prev = -1; + tot_unique_edges_prev = -1; + while (true) { + /* use the number of edges per vert to give verts unique topology IDs */ + + tot_unique_edges = 0; + + /* This can make really big numbers, wrapping around here is fine */ + for (a = 0, med = medge; a < totedge; a++, med++) { + const uint i1 = med->v1, i2 = med->v2; + topo_hash[i1] += topo_hash_prev[i2] * topo_pass; + topo_hash[i2] += topo_hash_prev[i1] * topo_pass; + tot_unique_edges += (topo_hash[i1] != topo_hash[i2]); + } + memcpy(topo_hash_prev, topo_hash, sizeof(MirrTopoHash_t) * totvert); + + /* sort so we can count unique values */ + qsort(topo_hash_prev, totvert, sizeof(MirrTopoHash_t), mirrtopo_hash_sort); + + tot_unique = 1; /* account for skipping the first value */ + for (a = 1; a < totvert; a++) { + if (topo_hash_prev[a - 1] != topo_hash_prev[a]) { + tot_unique++; + } + } + + if ((tot_unique <= tot_unique_prev) && (tot_unique_edges <= tot_unique_edges_prev)) { + /* Finish searching for unique values when 1 loop doesn't give a + * higher number of unique values compared to the previous loop. */ + break; + } + tot_unique_prev = tot_unique; + tot_unique_edges_prev = tot_unique_edges; + /* Copy the hash calculated this iteration, so we can use them next time */ + memcpy(topo_hash_prev, topo_hash, sizeof(MirrTopoHash_t) * totvert); + + topo_pass++; + } + + /* Hash/Index pairs are needed for sorting to find index pairs */ + topo_pairs = MEM_callocN(sizeof(MirrTopoVert_t) * totvert, "MirrTopoPairs"); + + /* since we are looping through verts, initialize these values here too */ + index_lookup = MEM_mallocN(totvert * sizeof(int), "mesh_topo_lookup"); + + for (a = 0; a < totvert; a++) { + topo_pairs[a].hash = topo_hash[a]; + topo_pairs[a].v_index = a; + + /* initialize lookup */ + index_lookup[a] = -1; + } + + qsort(topo_pairs, totvert, sizeof(MirrTopoVert_t), mirrtopo_vert_sort); + + last = 0; + + /* Get the pairs out of the sorted hashes, note, totvert+1 means we can use the previous 2, + * but you cant ever access the last 'a' index of MirrTopoPairs */ + for (a = 1; a <= totvert; a++) { + if ((a == totvert) || (topo_pairs[a - 1].hash != topo_pairs[a].hash)) { + const int match_count = a - last; + if (match_count == 2) { + const int j = topo_pairs[a - 1].v_index, k = topo_pairs[a - 2].v_index; + index_lookup[j] = k; + index_lookup[k] = j; + } + else if (match_count == 1) { + /* Center vertex. */ + const int j = topo_pairs[a - 1].v_index; + index_lookup[j] = j; + } + last = a; + } + } + + MEM_freeN(topo_pairs); + topo_pairs = NULL; + + MEM_freeN(topo_hash); + MEM_freeN(topo_hash_prev); + + ss->vertex_info.symmetrize_map = index_lookup; +} + +static void do_shape_symmetrize_brush_task_cb(void *__restrict userdata, + const int n, + const TaskParallelTLS *__restrict tls) +{ + SculptThreadedTaskData *data = userdata; + SculptSession *ss = data->ob->sculpt; + const Brush *brush = data->brush; + + SculptBrushTest test; + SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init(ss, &test); + const int thread_id = BLI_task_parallel_thread_id(tls); + + AutomaskingNodeData automask_data; + SCULPT_automasking_node_begin( + data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]); + + PBVHVertexIter vd; + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { + if (!sculpt_brush_test_sq_fn(&test, vd.co)) { + continue; + } + + SCULPT_automasking_node_update(ss, &automask_data, &vd); + + const int symmetrical_index = ss->vertex_info.symmetrize_map[vd.index]; + const PBVHVertRef symmetrical_vertex = BKE_pbvh_index_to_vertex(ss->pbvh, symmetrical_index); + + if (symmetrical_index == -1) { + continue; + } + + float symm_co[3]; + copy_v3_v3(symm_co, SCULPT_vertex_co_get(ss, symmetrical_vertex)); + + symm_co[0] *= -1; + float new_co[3]; + copy_v3_v3(new_co, symm_co); + + const float fade = SCULPT_brush_strength_factor(ss, + brush, + vd.co, + sqrtf(test.dist), + vd.no, + vd.fno, + vd.mask ? *vd.mask : 0.0f, + vd.vertex, + thread_id, + &automask_data); + + float disp[3]; + sub_v3_v3v3(disp, new_co, vd.co); + madd_v3_v3v3fl(vd.co, vd.co, disp, fade); + + if (vd.is_mesh) { + BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); + } + + BKE_pbvh_vertex_iter_end; + } +} + +/* Public functions. */ + +/* Main Brush Function. */ +void SCULPT_do_symmetrize_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) +{ + SculptSession *ss = ob->sculpt; + Brush *brush = BKE_paint_brush(&sd->paint); + + if (BKE_pbvh_type(ss->pbvh) != PBVH_FACES) { + return; + } + + if (!SCULPT_stroke_is_main_symmetry_pass(ss->cache)) { + return; + } + + SCULPT_symmetrize_map_ensure(ob); + + SculptThreadedTaskData data = { + .sd = sd, + .ob = ob, + .brush = brush, + .nodes = nodes, + }; + + TaskParallelSettings settings; + BKE_pbvh_parallel_range_settings(&settings, true, totnode); + BLI_task_parallel_range(0, totnode, &data, do_shape_symmetrize_brush_task_cb, &settings); +} diff --git a/source/blender/editors/sculpt_paint/sculpt_transform.cc b/source/blender/editors/sculpt_paint/sculpt_transform.cc index 9b487675c88..6b97c902aec 100644 --- a/source/blender/editors/sculpt_paint/sculpt_transform.cc +++ b/source/blender/editors/sculpt_paint/sculpt_transform.cc @@ -43,6 +43,9 @@ void ED_sculpt_init_transform(bContext *C, Object *ob, const int mval[2], const SculptSession *ss = ob->sculpt; Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); + /* flag update of original data */ + ss->stroke_id++; + copy_v3_v3(ss->init_pivot_pos, ss->pivot_pos); copy_v4_v4(ss->init_pivot_rot, ss->pivot_rot); copy_v3_v3(ss->init_pivot_scale, ss->pivot_scale); @@ -51,16 +54,38 @@ void ED_sculpt_init_transform(bContext *C, Object *ob, const int mval[2], const copy_v4_v4(ss->prev_pivot_rot, ss->pivot_rot); copy_v3_v3(ss->prev_pivot_scale, ss->pivot_scale); + ss->pivot_rot[3] = 1.0f; + SCULPT_undo_push_begin_ex(ob, undo_name); BKE_sculpt_update_object_for_edit(depsgraph, ob, false, false, false); - ss->pivot_rot[3] = 1.0f; - SCULPT_vertex_random_access_ensure(ss); SCULPT_filter_cache_init(C, ob, sd, SCULPT_UNDO_COORDS, mval, 5.0); - if (sd->transform_mode == SCULPT_TRANSFORM_MODE_RADIUS_ELASTIC) { + switch (sd->transform_deform_target) { + case SCULPT_TRANSFORM_DEFORM_TARGET_GEOMETRY: + BKE_sculpt_update_object_for_edit(depsgraph, ob, false, false, false); + break; + case SCULPT_TRANSFORM_DEFORM_TARGET_CLOTH_SIM: + BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true, false); + ss->filter_cache->cloth_sim = SCULPT_cloth_brush_simulation_create( + ob, 1.0f, 1.0f, 0.0f, true, false); + SCULPT_cloth_brush_simulation_init(ss, ss->filter_cache->cloth_sim); + SCULPT_cloth_brush_store_simulation_state(ss, ss->filter_cache->cloth_sim); + SCULPT_cloth_brush_ensure_nodes_constraints(sd, + ob, + ss->filter_cache->nodes, + ss->filter_cache->totnode, + ss->filter_cache->cloth_sim, + ss->pivot_pos, + FLT_MAX); + + break; + } + + if (sd->transform_mode == SCULPT_TRANSFORM_MODE_RADIUS_ELASTIC || + sd->transform_deform_target == SCULPT_TRANSFORM_DEFORM_TARGET_CLOTH_SIM) { ss->filter_cache->transform_displacement_mode = SCULPT_TRANSFORM_DISPLACEMENT_INCREMENTAL; } else { @@ -79,6 +104,7 @@ static void sculpt_transform_matrices_init(SculptSession *ss, transform_mat[4][4]; float start_pivot_pos[3], start_pivot_rot[4], start_pivot_scale[3]; + switch (t_mode) { case SCULPT_TRANSFORM_DISPLACEMENT_ORIGINAL: copy_v3_v3(start_pivot_pos, ss->init_pivot_pos); @@ -105,13 +131,13 @@ static void sculpt_transform_matrices_init(SculptSession *ss, /* Translation matrix. */ sub_v3_v3v3(d_t, ss->pivot_pos, start_pivot_pos); - SCULPT_flip_v3_by_symm_area(d_t, symm, v_symm, ss->init_pivot_pos); + SCULPT_flip_v3_by_symm_area(d_t, symm, v_symm, start_pivot_pos); translate_m4(t_mat, d_t[0], d_t[1], d_t[2]); /* Rotation matrix. */ sub_qt_qtqt(d_r, ss->pivot_rot, start_pivot_rot); normalize_qt(d_r); - SCULPT_flip_quat_by_symm_area(d_r, symm, v_symm, ss->init_pivot_pos); + SCULPT_flip_quat_by_symm_area(d_r, symm, v_symm, start_pivot_pos); quat_to_mat4(r_mat, d_r); /* Scale matrix. */ @@ -148,9 +174,10 @@ static void sculpt_transform_task_cb(void *__restrict userdata, SCULPT_undo_push_node(data->ob, node, SCULPT_UNDO_COORDS); BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) { - SCULPT_orig_vert_data_update(&orig_data, &vd); - float *start_co; + SCULPT_orig_vert_data_update(&orig_data, vd.vertex); + float transformed_co[3], orig_co[3], disp[3]; + float *start_co; float fade = vd.mask ? *vd.mask : 0.0f; copy_v3_v3(orig_co, orig_data.co); char symm_area = SCULPT_get_vertex_symm_area(orig_co); @@ -168,7 +195,15 @@ static void sculpt_transform_task_cb(void *__restrict userdata, mul_m4_v3(data->transform_mats[int(symm_area)], transformed_co); sub_v3_v3v3(disp, transformed_co, start_co); mul_v3_fl(disp, 1.0f - fade); - add_v3_v3v3(vd.co, start_co, disp); + + switch (data->sd->transform_deform_target) { + case SCULPT_TRANSFORM_DEFORM_TARGET_GEOMETRY: + add_v3_v3v3(vd.co, start_co, disp); + break; + case SCULPT_TRANSFORM_DEFORM_TARGET_CLOTH_SIM: + add_v3_v3v3(ss->filter_cache->cloth_sim->pos[vd.index], start_co, disp); + break; + } if (vd.is_mesh) { BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); @@ -228,7 +263,7 @@ static void sculpt_elastic_transform_task_cb(void *__restrict userdata, PBVHVertexIter vd; BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) { - SCULPT_orig_vert_data_update(&orig_data, &vd); + SCULPT_orig_vert_data_update(&orig_data, vd.vertex); float transformed_co[3], orig_co[3], disp[3]; const float fade = vd.mask ? *vd.mask : 0.0f; copy_v3_v3(orig_co, orig_data.co); @@ -328,6 +363,13 @@ void ED_sculpt_update_modal_transform(bContext *C, Object *ob) copy_v4_v4(ss->prev_pivot_rot, ss->pivot_rot); copy_v3_v3(ss->prev_pivot_scale, ss->pivot_scale); + if (sd->transform_deform_target == SCULPT_TRANSFORM_DEFORM_TARGET_CLOTH_SIM) { + SCULPT_cloth_sim_activate_nodes( + ss->filter_cache->cloth_sim, ss->filter_cache->nodes, ss->filter_cache->totnode); + SCULPT_cloth_brush_do_simulation_step( + sd, ob, ss->filter_cache->cloth_sim, ss->filter_cache->nodes, ss->filter_cache->totnode); + } + if (ss->deform_modifiers_active || ss->shapekey_active) { SCULPT_flush_stroke_deform(sd, ob, true); } @@ -339,7 +381,7 @@ void ED_sculpt_end_transform(bContext *C, Object *ob) { SculptSession *ss = ob->sculpt; if (ss->filter_cache) { - SCULPT_filter_cache_free(ss); + SCULPT_filter_cache_free(ss, ob); } SCULPT_flush_update_done(C, ob, SCULPT_UPDATE_COORDS); } diff --git a/source/blender/editors/sculpt_paint/sculpt_undo.cc b/source/blender/editors/sculpt_paint/sculpt_undo.cc index f5e47b8a868..7c75dd5c29d 100644 --- a/source/blender/editors/sculpt_paint/sculpt_undo.cc +++ b/source/blender/editors/sculpt_paint/sculpt_undo.cc @@ -30,6 +30,7 @@ */ #include +#include #include "MEM_guardedalloc.h" @@ -41,6 +42,7 @@ #include "BLI_threads.h" #include "BLI_utildefines.h" +#include "DNA_key_types.h" #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" #include "DNA_object_types.h" @@ -79,8 +81,10 @@ #include "ED_undo.h" #include "bmesh.h" +#include "bmesh_log.h" #include "sculpt_intern.hh" +#define WHEN_GLOBAL_UNDO_WORKS /* Uncomment to print the undo stack in the console on push/undo/redo. */ //#define SCULPT_UNDO_DEBUG @@ -129,6 +133,7 @@ typedef struct UndoSculpt { ListBase nodes; size_t undo_size; + BMLog *bm_restore; } UndoSculpt; typedef struct SculptAttrRef { @@ -142,6 +147,13 @@ typedef struct SculptUndoStep { UndoStep step; /* NOTE: will split out into list for multi-object-sculpt-mode. */ UndoSculpt data; + int id; + + bool auto_saved; + + // active vcol layer + SculptAttrRef active_attr_start; + SculptAttrRef active_attr_end; /* Active color attribute at the start of this undo step. */ SculptAttrRef active_color_start; @@ -161,6 +173,12 @@ static bool sculpt_attribute_ref_equals(SculptAttrRef *a, SculptAttrRef *b); static void sculpt_save_active_attribute(Object *ob, SculptAttrRef *attr); static UndoSculpt *sculpt_undosys_step_get_nodes(UndoStep *us_p); +static void update_unode_bmesh_memsize(SculptUndoNode *unode); +static UndoSculpt *sculpt_undo_get_nodes(void); +void sculpt_undo_print_nodes(void *active); +static bool check_first_undo_entry_dyntopo(Object *ob); +static void sculpt_undo_push_begin_ex(Object *ob, const char *name, bool no_first_entry_check); + #ifdef SCULPT_UNDO_DEBUG # ifdef _ # undef _ @@ -242,7 +260,7 @@ void sculpt_undo_print_nodes(Object *ob, void *active) UndoStack *ustack = ED_undo_stack_get(); UndoStep *us = ustack->steps.first; - if (active == NULL) { + if (active == nullptr) { active = ustack->step_active; } @@ -280,7 +298,7 @@ static void update_cb(PBVHNode *node, void *rebuild) BKE_pbvh_node_mark_update(node); BKE_pbvh_node_mark_update_mask(node); if (*((bool *)rebuild)) { - BKE_pbvh_node_mark_update_visibility(node); + BKE_pbvh_vert_tag_update_normal_visibility(node); } BKE_pbvh_node_fully_hidden_set(node, 0); } @@ -295,6 +313,8 @@ struct PartialUpdateData { bool *modified_face_set_faces; }; +static UndoSculpt *sculpt_undosys_step_get_nodes(UndoStep *us_p); + /** * A version of #update_cb that tests for the update tag in #PBVH.vert_bitmap. */ @@ -343,7 +363,7 @@ static void update_cb_partial(PBVHNode *node, void *userdata) for (int i = 0; i < verts_num; i++) { if (data->modified_hidden_verts[vert_indices[i]]) { if (data->rebuild) { - BKE_pbvh_node_mark_update_visibility(node); + BKE_pbvh_vert_tag_update_normal_visibility(node); } BKE_pbvh_node_fully_hidden_set(node, 0); break; @@ -373,6 +393,8 @@ static bool test_swap_v3_v3(float a[3], float b[3]) return false; } +// void pbvh_bmesh_check_nodes(PBVH *pbvh); + static bool sculpt_undo_restore_deformed( const SculptSession *ss, SculptUndoNode *unode, int uindex, int oindex, float coord[3]) { @@ -510,6 +532,7 @@ static bool sculpt_undo_restore_hidden(bContext *C, SculptUndoNode *unode, bool if (unode->maxvert) { for (int i = 0; i < unode->totvert; i++) { const int vert_index = unode->index[i]; + if ((BLI_BITMAP_TEST(unode->vert_hidden, i) != 0) != hide_vert[vert_index]) { BLI_BITMAP_FLIP(unode->vert_hidden, i); hide_vert[vert_index] = !hide_vert[vert_index]; @@ -528,6 +551,17 @@ static bool sculpt_undo_restore_hidden(bContext *C, SculptUndoNode *unode, bool return true; } +static int *sculpt_undo_get_indices32(SculptUndoNode *unode, int allvert) +{ + int *indices = (int *)MEM_malloc_arrayN(allvert, sizeof(int), __func__); + + for (int i = 0; i < allvert; i++) { + indices[i] = (int)unode->index[i]; + } + + return indices; +} + static bool sculpt_undo_restore_color(bContext *C, SculptUndoNode *unode, bool *modified_vertices) { const Scene *scene = CTX_data_scene(C); @@ -541,8 +575,12 @@ static bool sculpt_undo_restore_color(bContext *C, SculptUndoNode *unode, bool * /* NOTE: even with loop colors we still store derived * vertex colors for original data lookup. */ if (unode->col && !unode->loop_col) { - BKE_pbvh_swap_colors(ss->pbvh, unode->index, unode->totvert, unode->col); + int *indices = sculpt_undo_get_indices32(unode, unode->totvert); + + BKE_pbvh_swap_colors(ss->pbvh, indices, unode->totvert, unode->col); modified = true; + + MEM_SAFE_FREE(indices); } Mesh *me = BKE_object_get_original_mesh(ob); @@ -621,7 +659,7 @@ static bool sculpt_undo_restore_face_sets(bContext *C, Mesh *me = BKE_object_get_original_mesh(ob); SculptSession *ss = ob->sculpt; - ss->face_sets = BKE_sculpt_face_sets_ensure(me); + ss->face_sets = BKE_sculpt_face_sets_ensure(ob); BKE_pbvh_face_sets_set(ss->pbvh, ss->face_sets); bool modified = false; @@ -638,104 +676,466 @@ static bool sculpt_undo_restore_face_sets(bContext *C, return modified; } -static void sculpt_undo_bmesh_restore_generic_task_cb(void *__restrict userdata, - const int n, - const TaskParallelTLS *__restrict /*tls*/) -{ - PBVHNode **nodes = static_cast(userdata); +typedef struct BmeshUndoData { + PBVH *pbvh; + BMesh *bm; + bool do_full_recalc; + bool balance_pbvh; + int cd_face_node_offset, cd_vert_node_offset; + int cd_sculpt_vert, cd_boundary_flag; + bool regen_all_unique_verts; + bool is_redo; +} BmeshUndoData; - BKE_pbvh_node_mark_redraw(nodes[n]); +static void bmesh_undo_on_vert_kill(BMVert *v, void *userdata) +{ + BmeshUndoData *data = (BmeshUndoData *)userdata; + int ni = BM_ELEM_CD_GET_INT(v, data->cd_vert_node_offset); + // data->do_full_recalc = true; + + if (ni < 0) { +#if 0 // not sure this is really an error + // something went wrong + printf("%s: error, vertex %d is not in pbvh; ni was: %d\n", + __func__, + BM_ELEM_GET_ID(data->bm, v), + ni); + // data->do_full_recalc = true; +#endif + return; + } + + BKE_pbvh_bmesh_remove_vertex(data->pbvh, v, false); + data->balance_pbvh = true; +} + +static void bmesh_undo_on_vert_add(BMVert *v, void *userdata) +{ + BmeshUndoData *data = (BmeshUndoData *)userdata; + + data->balance_pbvh = true; + + // let face add vert + BM_ELEM_CD_SET_INT(v, data->cd_vert_node_offset, -1); + + *(int *)BM_ELEM_CD_GET_VOID_P(v, data->cd_boundary_flag) |= SCULPT_BOUNDARY_NEEDS_UPDATE; + + MSculptVert *mv = BKE_PBVH_SCULPTVERT(data->cd_sculpt_vert, v); + MV_ADD_FLAG(mv, + SCULPTVERT_NEED_DISK_SORT | SCULPTVERT_NEED_VALENCE | SCULPTVERT_NEED_TRIANGULATE); +} + +static void bmesh_undo_on_face_kill(BMFace *f, void *userdata) +{ + BmeshUndoData *data = (BmeshUndoData *)userdata; + int ni = BM_ELEM_CD_GET_INT(f, data->cd_face_node_offset); + + BKE_pbvh_bmesh_remove_face(data->pbvh, f, false); + + if (ni >= 0) { + PBVHNode *node = BKE_pbvh_get_node(data->pbvh, ni); + BKE_pbvh_bmesh_mark_node_regen(data->pbvh, node); + } + + // data->do_full_recalc = true; + data->balance_pbvh = true; +} + +static void bmesh_undo_on_face_add(BMFace *f, void *userdata) +{ + BmeshUndoData *data = (BmeshUndoData *)userdata; + // data->do_full_recalc = true; + + BM_ELEM_CD_SET_INT(f, data->cd_face_node_offset, -1); + BKE_pbvh_bmesh_add_face(data->pbvh, f, false, true); + + int ni = BM_ELEM_CD_GET_INT(f, data->cd_face_node_offset); + PBVHNode *node = BKE_pbvh_get_node(data->pbvh, ni); + + BMLoop *l = f->l_first; + do { + *(int *)BM_ELEM_CD_GET_VOID_P(l->v, data->cd_boundary_flag) |= SCULPT_BOUNDARY_NEEDS_UPDATE; + + MSculptVert *mv = BKE_PBVH_SCULPTVERT(data->cd_sculpt_vert, l->v); + MV_ADD_FLAG(mv, SCULPTVERT_NEED_DISK_SORT); + + int ni_l = BM_ELEM_CD_GET_INT(l->v, data->cd_vert_node_offset); + + if (ni_l < 0 && ni >= 0) { + BM_ELEM_CD_SET_INT(l->v, ni_l, ni); + TableGSet *bm_unique_verts = BKE_pbvh_bmesh_node_unique_verts(node); + + BLI_table_gset_add(bm_unique_verts, l->v); + } + } while ((l = l->next) != f->l_first); + + data->balance_pbvh = true; +} +static void bmesh_undo_full_mesh(void *userdata) +{ + BmeshUndoData *data = (BmeshUndoData *)userdata; + + if (data->pbvh) { + BKE_pbvh_bmesh_update_all_valence(data->pbvh); + } + + data->do_full_recalc = true; +} + +static void bmesh_undo_on_edge_change(BMEdge *v, void *userdata, void *old_customdata) +{ +} + +static void bmesh_undo_on_edge_kill(BMEdge *e, void *userdata) +{ + BmeshUndoData *data = (BmeshUndoData *)userdata; + + MSculptVert *mv1 = BKE_PBVH_SCULPTVERT(data->cd_sculpt_vert, e->v1); + MSculptVert *mv2 = BKE_PBVH_SCULPTVERT(data->cd_sculpt_vert, e->v2); + + *(int *)BM_ELEM_CD_GET_VOID_P(e->v1, data->cd_boundary_flag) |= SCULPT_BOUNDARY_NEEDS_UPDATE; + *(int *)BM_ELEM_CD_GET_VOID_P(e->v2, data->cd_boundary_flag) |= SCULPT_BOUNDARY_NEEDS_UPDATE; + + MV_ADD_FLAG(mv1, + SCULPTVERT_NEED_TRIANGULATE | SCULPTVERT_NEED_DISK_SORT | SCULPTVERT_NEED_VALENCE); + MV_ADD_FLAG(mv2, + SCULPTVERT_NEED_TRIANGULATE | SCULPTVERT_NEED_DISK_SORT | SCULPTVERT_NEED_VALENCE); +} + +static void bmesh_undo_on_edge_add(BMEdge *e, void *userdata) +{ + BmeshUndoData *data = (BmeshUndoData *)userdata; + + MSculptVert *mv1 = BKE_PBVH_SCULPTVERT(data->cd_sculpt_vert, e->v1); + MSculptVert *mv2 = BKE_PBVH_SCULPTVERT(data->cd_sculpt_vert, e->v2); + + *(int *)BM_ELEM_CD_GET_VOID_P(e->v1, data->cd_boundary_flag) |= SCULPT_BOUNDARY_NEEDS_UPDATE; + *(int *)BM_ELEM_CD_GET_VOID_P(e->v2, data->cd_boundary_flag) |= SCULPT_BOUNDARY_NEEDS_UPDATE; + + mv1->flag |= SCULPTVERT_NEED_TRIANGULATE | SCULPTVERT_NEED_DISK_SORT | SCULPTVERT_NEED_VALENCE; + mv2->flag |= SCULPTVERT_NEED_TRIANGULATE | SCULPTVERT_NEED_DISK_SORT | SCULPTVERT_NEED_VALENCE; +} + +static void bmesh_undo_on_vert_change(BMVert *v, void *userdata, void *old_customdata) +{ + BmeshUndoData *data = (BmeshUndoData *)userdata; + + if (!old_customdata) { + BM_ELEM_CD_SET_INT(v, data->cd_vert_node_offset, -1); + data->regen_all_unique_verts = true; + return; + } + + BMElem h; + h.head.data = old_customdata; + + int ni = BM_ELEM_CD_GET_INT(&h, data->cd_vert_node_offset); + + // int ni2 = BM_ELEM_CD_GET_INT(v, data->cd_vert_node_offset); + + // attempt to find old node + PBVHNode *node = BKE_pbvh_get_node_leaf_safe(data->pbvh, ni); + if (node) { + // BKE_pbvh_bmesh_mark_node_regen(data->pbvh, node); + BKE_pbvh_node_mark_update(node); + BM_ELEM_CD_SET_INT(v, data->cd_vert_node_offset, ni); + } + else { + if (ni != DYNTOPO_NODE_NONE) { + printf("%s: error: corrupted vertex. ni: %d\n", __func__, ni); + BM_ELEM_CD_SET_INT(v, data->cd_vert_node_offset, DYNTOPO_NODE_NONE); + } + + data->regen_all_unique_verts = true; + } + + return; + // preserve pbvh node references + + int oldnode_i = BM_ELEM_CD_GET_INT(&h, data->cd_vert_node_offset); + + BM_ELEM_CD_SET_INT(v, data->cd_vert_node_offset, oldnode_i); + + if (oldnode_i >= 0) { + PBVHNode *node = BKE_pbvh_node_from_index(data->pbvh, oldnode_i); + BKE_pbvh_node_mark_update(node); + } +} + +static void bmesh_undo_on_face_change(BMFace *f, void *userdata, void *old_customdata) +{ + BmeshUndoData *data = (BmeshUndoData *)userdata; + + if (!old_customdata) { + data->do_full_recalc = true; // can't recover? + return; + } + + BMElem h; + h.head.data = old_customdata; + + int ni = BM_ELEM_CD_GET_INT(&h, data->cd_face_node_offset); + + // attempt to find old node in old_customdata + PBVHNode *node = BKE_pbvh_get_node_leaf_safe(data->pbvh, ni); + if (node) { + BM_ELEM_CD_SET_INT(f, data->cd_face_node_offset, ni); + BKE_pbvh_node_mark_update(node); + } + else { + printf("pbvh face undo error\n"); + data->do_full_recalc = true; + BM_ELEM_CD_SET_INT(f, data->cd_face_node_offset, -1); + } +} + +static void update_unode_bmesh_memsize(SculptUndoNode *unode) +{ + // update memory size + UndoSculpt *usculpt = sculpt_undo_get_nodes(); + + if (!usculpt) { + return; + } + + // subtract old size + if (usculpt->undo_size >= unode->undo_size) { + usculpt->undo_size -= unode->undo_size; + } + + unode->undo_size = BM_log_entry_size(unode->bm_entry); + + // add new size + usculpt->undo_size += unode->undo_size; } static void sculpt_undo_bmesh_restore_generic(SculptUndoNode *unode, Object *ob, SculptSession *ss) { + BmeshUndoData data = {ss->pbvh, + ss->bm, + false, + false, + ss->cd_face_node_offset, + ss->cd_vert_node_offset, + ss->cd_sculpt_vert, + ss->attrs.boundary_flags->bmesh_cd_offset, + false, + !unode->applied}; + + BMLogCallbacks callbacks = {bmesh_undo_on_vert_add, + bmesh_undo_on_vert_kill, + bmesh_undo_on_vert_change, + bmesh_undo_on_edge_add, + bmesh_undo_on_edge_kill, + bmesh_undo_on_edge_change, + bmesh_undo_on_face_add, + bmesh_undo_on_face_kill, + bmesh_undo_on_face_change, + bmesh_undo_full_mesh, + nullptr, + (void *)&data}; + + BKE_sculptsession_update_attr_refs(ob); + + // pbvh_bmesh_check_nodes(ss->pbvh); + if (unode->applied) { - BM_log_undo(ss->bm, ss->bm_log); + BM_log_undo(ss->bm, ss->bm_log, &callbacks); unode->applied = false; } else { - BM_log_redo(ss->bm, ss->bm_log); + BM_log_redo(ss->bm, ss->bm_log, &callbacks); unode->applied = true; } - if (unode->type == SCULPT_UNDO_MASK) { + update_unode_bmesh_memsize(unode); + + if (!data.do_full_recalc) { int totnode; PBVHNode **nodes; BKE_pbvh_search_gather(ss->pbvh, nullptr, nullptr, &nodes, &totnode); - TaskParallelSettings settings; - BKE_pbvh_parallel_range_settings(&settings, true, totnode); - BLI_task_parallel_range( - 0, totnode, nodes, sculpt_undo_bmesh_restore_generic_task_cb, &settings); + if (data.regen_all_unique_verts) { + for (int i = 0; i < totnode; i++) { + BKE_pbvh_bmesh_mark_node_regen(ss->pbvh, nodes[i]); + } + } + + // pbvh_bmesh_check_nodes(ss->pbvh); + BKE_pbvh_bmesh_regen_node_verts(ss->pbvh); + // pbvh_bmesh_check_nodes(ss->pbvh); + + BKE_pbvh_update_bounds(ss->pbvh, PBVH_UpdateBB | PBVH_UpdateOriginalBB | PBVH_UpdateRedraw); if (nodes) { MEM_freeN(nodes); } + + if (data.balance_pbvh) { + BKE_pbvh_bmesh_after_stroke(ss->pbvh, true); + } + + // pbvh_bmesh_check_nodes(ss->pbvh); } else { - SCULPT_pbvh_clear(ob); + printf("undo triggered pbvh rebuild"); + SCULPT_pbvh_clear(ob, false); } } /* Create empty sculpt BMesh and enable logging. */ -static void sculpt_undo_bmesh_enable(Object *ob, SculptUndoNode *unode) +static void sculpt_undo_bmesh_enable(Object *ob, SculptUndoNode *unode, bool is_redo) { SculptSession *ss = ob->sculpt; Mesh *me = static_cast(ob->data); - SCULPT_pbvh_clear(ob); + SCULPT_pbvh_clear(ob, false); + + ss->active_face.i = ss->active_vertex.i = 0; /* Create empty BMesh and enable logging. */ - BMeshCreateParams bmesh_create_params{}; - bmesh_create_params.use_toolflags = false; + ss->bm = SCULPT_dyntopo_empty_bmesh(); +#if 0 + ss->bm = BM_mesh_create(&bm_mesh_allocsize_default, + &((struct BMeshCreateParams){.use_toolflags = false, + .create_unique_ids = true, + .id_elem_mask = BM_VERT | BM_EDGE | BM_FACE, + .id_map = true, + .temporary_ids = false, + .no_reuse_ids = false})); +#endif - ss->bm = BM_mesh_create(&bm_mesh_allocsize_default, &bmesh_create_params); - BM_data_layer_add(ss->bm, &ss->bm->vdata, CD_PAINT_MASK); + BMeshFromMeshParams params = {0}; + params.calc_face_normal = true; + params.use_shapekey = true; + params.create_shapekey_layers = true; + params.active_shapekey = ob->shapenr; + + BM_mesh_bm_from_me(nullptr, ss->bm, me, ¶ms); + + BKE_sculptsession_update_attr_refs(ob); + + if (ss->pbvh) { + BKE_pbvh_update_sculpt_verts(ss->pbvh); + } + + if (!ss->bm_log) { + /* Restore the BMLog using saved entries. */ + ss->bm_log = BM_log_from_existing_entries_create(ss->bm, ss->bm_idmap, unode->bm_entry); + BMLogEntry *entry = is_redo ? BM_log_entry_prev(unode->bm_entry) : unode->bm_entry; + + BM_log_set_current_entry(ss->bm_log, entry); + } + + if (!CustomData_has_layer(&ss->bm->vdata, CD_PAINT_MASK)) { + BM_data_layer_add(ss->bm, &ss->bm->vdata, CD_PAINT_MASK); + } me->flag |= ME_SCULPT_DYNAMIC_TOPOLOGY; - - /* Restore the BMLog using saved entries. */ - ss->bm_log = BM_log_from_existing_entries_create(ss->bm, unode->bm_entry); } -static void sculpt_undo_bmesh_restore_begin(bContext *C, - SculptUndoNode *unode, - Object *ob, - SculptSession *ss) +static void sculpt_undo_bmesh_restore_begin( + bContext *C, SculptUndoNode *unode, Object *ob, SculptSession *ss, int dir) { if (unode->applied) { + if (ss->bm && ss->bm_log) { + /*note that we can't log ids here. + not entirely sure why, and in thoery it shouldn't be necassary. + ids end up corrupted. + */ + +#if 1 + // BM_log_all_ids(ss->bm, ss->bm_log, unode->bm_entry); + + // need to run bmlog undo on empty log, + // getting a refcount error in the log + // ref counting system otherwise + + if (dir == -1) { + BM_log_undo_skip(ss->bm, ss->bm_log); + } + else { + BM_log_redo_skip(ss->bm, ss->bm_log); + } +#endif + } + SCULPT_dynamic_topology_disable(C, unode); unode->applied = false; } else { - sculpt_undo_bmesh_enable(ob, unode); + /*load bmesh from mesh data*/ + sculpt_undo_bmesh_enable(ob, unode, true); - /* Restore the mesh from the first log entry. */ - BM_log_redo(ss->bm, ss->bm_log); +#if 1 + // need to run bmlog undo on empty log, + // getting a refcount error in the log + // ref counting system otherwise + + if (dir == 1) { + BM_log_redo(ss->bm, ss->bm_log, nullptr); + } + else { + BM_log_undo(ss->bm, ss->bm_log, nullptr); + } +#endif unode->applied = true; } + + if (ss->bm) { + BM_mesh_elem_index_ensure(ss->bm, BM_VERT | BM_FACE); + } } -static void sculpt_undo_bmesh_restore_end(bContext *C, - SculptUndoNode *unode, - Object *ob, - SculptSession *ss) +static void sculpt_undo_bmesh_restore_end( + bContext *C, SculptUndoNode *unode, Object *ob, SculptSession *ss, int dir) { - if (unode->applied) { - sculpt_undo_bmesh_enable(ob, unode); - /* Restore the mesh from the last log entry. */ - BM_log_undo(ss->bm, ss->bm_log); + if (unode->applied) { + /*load bmesh from mesh data*/ + sculpt_undo_bmesh_enable(ob, unode, false); + +#if 1 + // need to run bmlog undo on empty log, + // getting a refcount error in the log + // ref counting system otherwise + + if (dir == -1) { + BM_log_undo(ss->bm, ss->bm_log, nullptr); + } + else { + BM_log_redo(ss->bm, ss->bm_log, nullptr); + } +#endif unode->applied = false; } else { +#if 1 + if (ss->bm && ss->bm_log) { + // need to run bmlog undo on empty log, + // getting a refcount error in the log + // ref counting system otherwise + + if (dir == -1) { + BM_log_undo_skip(ss->bm, ss->bm_log); + } + else { + BM_log_redo_skip(ss->bm, ss->bm_log); + } + } +#endif + /* Disable dynamic topology sculpting. */ SCULPT_dynamic_topology_disable(C, nullptr); unode->applied = true; } + + if (ss->bm) { + BM_mesh_elem_index_ensure(ss->bm, BM_VERT | BM_FACE); + } } static void sculpt_undo_geometry_store_data(SculptUndoNodeGeometry *geometry, Object *object) @@ -805,7 +1205,7 @@ static void sculpt_undo_geometry_free_data(SculptUndoNodeGeometry *geometry) static void sculpt_undo_geometry_restore(SculptUndoNode *unode, Object *object) { if (unode->geometry_clear_pbvh) { - SCULPT_pbvh_clear(object); + SCULPT_pbvh_clear(object, false); } if (unode->applied) { @@ -822,28 +1222,108 @@ static void sculpt_undo_geometry_restore(SculptUndoNode *unode, Object *object) * * Returns true if this was a dynamic-topology undo step, otherwise * returns false to indicate the non-dyntopo code should run. */ -static int sculpt_undo_bmesh_restore(bContext *C, - SculptUndoNode *unode, - Object *ob, - SculptSession *ss) +static int sculpt_undo_bmesh_restore( + bContext *C, SculptUndoNode *unode, Object *ob, SculptSession *ss, int dir) { + // handle transition from another undo type + +#ifdef WHEN_GLOBAL_UNDO_WORKS + if (!ss->bm_log && ss->bm && unode->bm_entry) { // && BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) { + ss->bm_log = BM_log_from_existing_entries_create(ss->bm, ss->bm_idmap, unode->bm_entry); + } +#endif + + if (ss->bm_log && ss->bm && + !ELEM(unode->type, SCULPT_UNDO_DYNTOPO_BEGIN, SCULPT_UNDO_DYNTOPO_END)) { + BKE_sculptsession_update_attr_refs(ob); + +#if 0 + if (ss->active_face.i && ss->active_face.i != -1LL) { + ss->active_face.i = (intptr_t)BM_log_face_id_get(ss->bm_log, + (BMFace *)ss->active_face.i); + } + else { + ss->active_face.i = -1; + } + + if (ss->active_vertex.i && ss->active_vertex.i != -1LL) { + ss->active_vertex.i = (intptr_t)BM_log_vert_id_get( + ss->bm_log, (BMVert *)ss->active_vertex.i); + } + else { + ss->active_vertex.i = -1; + } +#endif + ss->active_face.i = ss->active_vertex.i = 0; + } + else { + ss->active_face.i = ss->active_vertex.i = -1; + } + + bool ret = false; + bool set_active_vertex = true; + switch (unode->type) { case SCULPT_UNDO_DYNTOPO_BEGIN: - sculpt_undo_bmesh_restore_begin(C, unode, ob, ss); - return true; + sculpt_undo_bmesh_restore_begin(C, unode, ob, ss, dir); + SCULPT_vertex_random_access_ensure(ss); + ss->active_face.i = ss->active_vertex.i = 0; + set_active_vertex = false; + + ret = true; + break; case SCULPT_UNDO_DYNTOPO_END: - sculpt_undo_bmesh_restore_end(C, unode, ob, ss); - return true; + ss->active_face.i = ss->active_vertex.i = 0; + set_active_vertex = false; + + sculpt_undo_bmesh_restore_end(C, unode, ob, ss, dir); + SCULPT_vertex_random_access_ensure(ss); + + ret = true; + break; default: if (ss->bm_log) { sculpt_undo_bmesh_restore_generic(unode, ob, ss); - return true; + SCULPT_vertex_random_access_ensure(ss); + ret = true; } break; } - return false; + if (set_active_vertex && ss->bm_log && ss->bm) { + if (ss->active_face.i != -1) { + BMFace *f = BM_log_id_face_get(ss->bm, ss->bm_log, (uint)ss->active_face.i); + if (f && f->head.htype == BM_FACE) { + ss->active_face.i = (intptr_t)f; + } + else { + ss->active_face.i = 0LL; + } + } + else { + ss->active_face.i = 0LL; + } + + if (ss->active_vertex.i != -1) { + BMVert *v = BM_log_id_vert_get(ss->bm, ss->bm_log, (uint)ss->active_vertex.i); + + if (v && v->head.htype == BM_VERT) { + ss->active_vertex.i = (intptr_t)v; + } + else { + ss->active_vertex.i = 0LL; + } + } + else { + ss->active_vertex.i = 0LL; + } + } + else { + ss->active_face.i = ss->active_vertex.i = 0; + } + + return ret; } /* Geometry updates (such as Apply Base, for example) will re-evaluate the object and refine its @@ -873,7 +1353,7 @@ static void sculpt_undo_refine_subdiv(Depsgraph *depsgraph, MEM_freeN(deformed_verts); } -static void sculpt_undo_restore_list(bContext *C, Depsgraph *depsgraph, ListBase *lb) +static void sculpt_undo_restore_list(bContext *C, Depsgraph *depsgraph, ListBase *lb, int dir) { Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); @@ -887,6 +1367,8 @@ static void sculpt_undo_restore_list(bContext *C, Depsgraph *depsgraph, ListBase bool update_face_sets = false; bool need_mask = false; bool need_refine_subdiv = false; + // bool did_first_hack = false; + bool clear_automask_cache = false; for (unode = static_cast(lb->first); unode; unode = unode->next) { @@ -913,16 +1395,19 @@ static void sculpt_undo_restore_list(bContext *C, Depsgraph *depsgraph, ListBase DEG_id_tag_update(&ob->id, ID_RECALC_SHADING); + sculpt_undo_print_nodes(ob, nullptr); + if (lb->first != nullptr) { /* Only do early object update for edits if first node needs this. * Undo steps like geometry does not need object to be updated before they run and will * ensure object is updated after the node is handled. */ const SculptUndoNode *first_unode = (const SculptUndoNode *)lb->first; - if (first_unode->type != SCULPT_UNDO_GEOMETRY) { + if (first_unode->type != SCULPT_UNDO_GEOMETRY && + first_unode->type != SCULPT_UNDO_DYNTOPO_BEGIN) { BKE_sculpt_update_object_for_edit(depsgraph, ob, false, need_mask, false); } - if (sculpt_undo_bmesh_restore(C, static_cast(lb->first), ob, ss)) { + if (sculpt_undo_bmesh_restore(C, (SculptUndoNode *)lb->first, ob, ss, dir)) { return; } } @@ -947,6 +1432,7 @@ static void sculpt_undo_restore_list(bContext *C, Depsgraph *depsgraph, ListBase * continue. */ if (unode->maxvert) { if (ss->totvert != unode->maxvert) { + printf("error! %s\n", __func__); continue; } } @@ -1114,12 +1600,17 @@ static void sculpt_undo_restore_list(bContext *C, Depsgraph *depsgraph, ListBase static void sculpt_undo_free_list(ListBase *lb) { - SculptUndoNode *unode = static_cast(lb->first); + SculptUndoNode *unode = (SculptUndoNode *)lb->first; + while (unode != nullptr) { SculptUndoNode *unode_next = unode->next; if (unode->co) { MEM_freeN(unode->co); } + + if (unode->nodemap) { + MEM_freeN(unode->nodemap); + } if (unode->col) { MEM_freeN(unode->col); } @@ -1161,11 +1652,12 @@ static void sculpt_undo_free_list(ListBase *lb) if (unode->bm_entry) { BM_log_entry_drop(unode->bm_entry); + unode->bm_entry = nullptr; + unode->bm_log = nullptr; } sculpt_undo_geometry_free_data(&unode->geometry_original); sculpt_undo_geometry_free_data(&unode->geometry_modified); - sculpt_undo_geometry_free_data(&unode->geometry_bmesh_enter); if (unode->face_sets) { MEM_freeN(unode->face_sets); @@ -1200,6 +1692,27 @@ static bool sculpt_undo_cleanup(bContext *C, ListBase *lb) } #endif +static int hash_sculpt_colors(SculptUndoNode *node) +{ + if (!node->col) { + return -1; + } + + int i = 0; + int hash = 0; + + for (i = 0; i < node->totvert; i++) { + float *col = node->col[i]; + + for (int j = 0; j < 4; j++) { + hash = hash ^ (int)(col[j] * 2048.0f * 2048.0f); + hash += (1 << 23) - 1; + } + } + + return hash; +} + SculptUndoNode *SCULPT_undo_get_node(PBVHNode *node, SculptUndoType type) { UndoSculpt *usculpt = sculpt_undo_get_nodes(); @@ -1208,6 +1721,10 @@ SculptUndoNode *SCULPT_undo_get_node(PBVHNode *node, SculptUndoType type) return nullptr; } + if (type < 0) { + return (SculptUndoNode *)BLI_findptr(&usculpt->nodes, node, offsetof(SculptUndoNode, node)); + } + LISTBASE_FOREACH (SculptUndoNode *, unode, &usculpt->nodes) { if (unode->node == node && unode->type == type) { return unode; @@ -1439,7 +1956,20 @@ static void sculpt_undo_store_coords(Object *ob, SculptUndoNode *unode) SculptSession *ss = ob->sculpt; PBVHVertexIter vd; - BKE_pbvh_vertex_iter_begin (ss->pbvh, static_cast(unode->node), vd, PBVH_ITER_ALL) { + SculptOrigVertData orig_data; + SCULPT_orig_vert_data_unode_init(&orig_data, ob, unode); + + int totvert, allvert; + BKE_pbvh_node_num_verts(ss->pbvh, (PBVHNode *)unode->node, &totvert, &allvert); + + if (ss->deform_modifiers_active && !unode->orig_co) { + unode->orig_co = (float(*)[3])MEM_malloc_arrayN( + allvert, sizeof(float) * 3, "sculpt unode undo coords"); + } + + bool have_grids = BKE_pbvh_type(ss->pbvh) == PBVH_GRIDS; + + BKE_pbvh_vertex_iter_begin (ss->pbvh, ((PBVHNode *)unode->node), vd, PBVH_ITER_ALL) { copy_v3_v3(unode->co[vd.i], vd.co); if (vd.no) { copy_v3_v3(unode->no[vd.i], vd.no); @@ -1449,7 +1979,15 @@ static void sculpt_undo_store_coords(Object *ob, SculptUndoNode *unode) } if (ss->deform_modifiers_active) { - copy_v3_v3(unode->orig_co[vd.i], ss->orig_cos[unode->index[vd.i]]); + if (!have_grids && ss->shapekey_active) { + float(*cos)[3] = (float(*)[3])ss->shapekey_active->data; + + copy_v3_v3(unode->orig_co[vd.i], cos[vd.index]); + } + else { + MSculptVert *mv = SCULPT_vertex_get_sculptvert(ss, vd.vertex); + copy_v3_v3(unode->orig_co[vd.i], mv->origco); + } } } BKE_pbvh_vertex_iter_end; @@ -1499,13 +2037,18 @@ static void sculpt_undo_store_color(Object *ob, SculptUndoNode *unode) int allvert; BKE_pbvh_node_num_verts(ss->pbvh, static_cast(unode->node), nullptr, &allvert); + int *indices = sculpt_undo_get_indices32(unode, allvert); + /* NOTE: even with loop colors we still store (derived) * vertex colors for original data lookup. */ - BKE_pbvh_store_colors_vertex(ss->pbvh, unode->index, allvert, unode->col); + + BKE_pbvh_store_colors_vertex(ss->pbvh, indices, allvert, unode->col); if (unode->loop_col && unode->totloop) { BKE_pbvh_store_colors(ss->pbvh, unode->loop_index, unode->totloop, unode->loop_col); } + + MEM_SAFE_FREE(indices); } static SculptUndoNodeGeometry *sculpt_undo_geometry_get(SculptUndoNode *unode) @@ -1543,7 +2086,72 @@ static void sculpt_undo_store_face_sets(SculptSession *ss, SculptUndoNode *unode BKE_pbvh_face_iter_end(fd); } -static SculptUndoNode *sculpt_undo_bmesh_push(Object *ob, PBVHNode *node, SculptUndoType type) +void SCULPT_undo_ensure_bmlog(Object *ob) +{ + SculptSession *ss = ob->sculpt; + Mesh *me = BKE_object_get_original_mesh(ob); + + /*log exists or not in sculpt mode? good then*/ + if (ss->bm_log || !ob->sculpt) { + return; + } + + /*try to find log from entries in the undo stack*/ + + UndoStack *ustack = ED_undo_stack_get(); + + if (!ustack) { + return; + } + + UndoStep *us = BKE_undosys_stack_active_with_type(ustack, BKE_UNDOSYS_TYPE_SCULPT); + + if (!us) { + // check next step + if (ustack->step_active && ustack->step_active->next && + ustack->step_active->next->type == BKE_UNDOSYS_TYPE_SCULPT) { + us = ustack->step_active->next; + } + } + + if (!us) { + return; + } + + UndoSculpt *usculpt = sculpt_undosys_step_get_nodes(us); + + if (!ss->bm && !(me->flag & ME_SCULPT_DYNAMIC_TOPOLOGY)) { + return; + } + + if (!usculpt) { + // happens during file load + return; + } + + SculptUndoNode *unode = (SculptUndoNode *)usculpt->nodes.first; + + BKE_sculpt_ensure_idmap(ob); + + /*when transition between undo step types the log might simply + have been freed, look for entries to rebuild it with*/ + if (!ss->bm_log) { + if (unode && unode->bm_entry) { + ss->bm_log = BM_log_from_existing_entries_create(ss->bm, ss->bm_idmap, unode->bm_entry); + } + else { + ss->bm_log = BM_log_create(ss->bm, ss->bm_idmap); + } + + if (ss->pbvh) { + BKE_pbvh_set_bm_log(ss->pbvh, ss->bm_log); + } + } +} + +ATTR_NO_OPT static SculptUndoNode *sculpt_undo_bmesh_push(Object *ob, + PBVHNode *node, + SculptUndoType type) { UndoSculpt *usculpt = sculpt_undo_get_nodes(); SculptSession *ss = ob->sculpt; @@ -1551,76 +2159,263 @@ static SculptUndoNode *sculpt_undo_bmesh_push(Object *ob, PBVHNode *node, Sculpt SculptUndoNode *unode = static_cast(usculpt->nodes.first); + SCULPT_undo_ensure_bmlog(ob); + + if (!ss->bm_log) { + ss->bm_log = BM_log_create(ss->bm, ss->bm_idmap); + } + + bool new_node = false; + if (unode == nullptr) { - unode = MEM_cnew(__func__); + new_node = true; + unode = (SculptUndoNode *)MEM_callocN(sizeof(*unode), __func__); BLI_strncpy(unode->idname, ob->id.name, sizeof(unode->idname)); unode->type = type; unode->applied = true; + /* note that every undo type must push a bm_entry for + so we can recreate the BMLog from chained entries + when going to/from other undo system steps */ + if (type == SCULPT_UNDO_DYNTOPO_END) { - unode->bm_entry = BM_log_entry_add(ss->bm_log); - BM_log_before_all_removed(ss->bm, ss->bm_log); + unode->bm_log = ss->bm_log; + // unode->bm_entry = BM_log_all_ids(ss->bm, ss->bm_log, nullptr); + unode->bm_entry = BM_log_entry_add(ss->bm, ss->bm_log); + + // BM_log_full_mesh(ss->bm, ss->bm_log); + // BM_log_before_all_removed(ss->bm, ss->bm_log); } else if (type == SCULPT_UNDO_DYNTOPO_BEGIN) { - /* Store a copy of the mesh's current vertices, loops, and - * polys. A full copy like this is needed because entering - * dynamic-topology immediately does topological edits - * (converting polys to triangles) that the BMLog can't - * fully restore from. */ - SculptUndoNodeGeometry *geometry = &unode->geometry_bmesh_enter; - sculpt_undo_geometry_store_data(geometry, ob); + unode->bm_log = ss->bm_log; + // unode->bm_entry = BM_log_all_ids(ss->bm, ss->bm_log, nullptr); + unode->bm_entry = BM_log_entry_add(ss->bm, ss->bm_log); - unode->bm_entry = BM_log_entry_add(ss->bm_log); - BM_log_all_added(ss->bm, ss->bm_log); + // BM_log_all_added(ss->bm, ss->bm_log); + // BM_log_full_mesh(ss->bm, ss->bm_log); } else { - unode->bm_entry = BM_log_entry_add(ss->bm_log); + unode->bm_log = ss->bm_log; + unode->bm_entry = BM_log_entry_add(ss->bm, ss->bm_log); } BLI_addtail(&usculpt->nodes, unode); } if (node) { + if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) { + unode->bm_log = ss->bm_log; + unode->bm_entry = BM_log_entry_check_customdata(ss->bm, ss->bm_log); + } + switch (type) { case SCULPT_UNDO_COORDS: case SCULPT_UNDO_MASK: - /* Before any vertex values get modified, ensure their - * original positions are logged. */ - BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_ALL) { - BM_log_vert_before_modified(ss->bm_log, vd.bm_vert, vd.cd_vert_mask_offset); + BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) { + bm_logstack_push(); + BM_log_vert_before_modified(ss->bm, ss->bm_log, vd.bm_vert); + bm_logstack_pop(); } BKE_pbvh_vertex_iter_end; break; case SCULPT_UNDO_HIDDEN: { - GSetIterator gs_iter; - GSet *faces = BKE_pbvh_bmesh_node_faces(node); - BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_ALL) { - BM_log_vert_before_modified(ss->bm_log, vd.bm_vert, vd.cd_vert_mask_offset); + TableGSet *faces = BKE_pbvh_bmesh_node_faces(node); + BMFace *f; + + BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) { + bm_logstack_push(); + BM_log_vert_before_modified(ss->bm, ss->bm_log, vd.bm_vert); + bm_logstack_pop(); } BKE_pbvh_vertex_iter_end; - GSET_ITER (gs_iter, faces) { - BMFace *f = static_cast(BLI_gsetIterator_getKey(&gs_iter)); - BM_log_face_modified(ss->bm_log, f); + TGSET_ITER (f, faces) { + BM_log_face_modified(ss->bm, ss->bm_log, f); } + TGSET_ITER_END break; } + case SCULPT_UNDO_COLOR: { + Mesh *mesh = BKE_object_get_original_mesh(ob); + CustomDataLayer *color_layer = BKE_id_attributes_active_color_get(&mesh->id); + if (!color_layer) { + break; + } + eAttrDomain domain = BKE_id_attribute_domain(&mesh->id, color_layer); + + switch (domain) { + case ATTR_DOMAIN_POINT: { + BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) { + bm_logstack_push(); + BM_log_vert_before_modified(ss->bm, ss->bm_log, vd.bm_vert); + bm_logstack_pop(); + } + BKE_pbvh_vertex_iter_end; + break; + } + case ATTR_DOMAIN_CORNER: { + TableGSet *faces = BKE_pbvh_bmesh_node_faces(node); + BMFace *f; + + TGSET_ITER (f, faces) { + BM_log_face_modified(ss->bm, ss->bm_log, f); + } + TGSET_ITER_END + + break; + } + } + break; + } + case SCULPT_UNDO_FACE_SETS: { + TableGSet *faces = BKE_pbvh_bmesh_node_faces(node); + BMFace *f; + + TGSET_ITER (f, faces) { + BM_log_face_modified(ss->bm, ss->bm_log, f); + } + TGSET_ITER_END + + break; + } case SCULPT_UNDO_DYNTOPO_BEGIN: case SCULPT_UNDO_DYNTOPO_END: case SCULPT_UNDO_DYNTOPO_SYMMETRIZE: case SCULPT_UNDO_GEOMETRY: - case SCULPT_UNDO_FACE_SETS: - case SCULPT_UNDO_COLOR: + break; + } + } + else { + switch (type) { + case SCULPT_UNDO_DYNTOPO_SYMMETRIZE: + case SCULPT_UNDO_GEOMETRY: + BM_log_full_mesh(ss->bm, ss->bm_log); + break; + default: // to avoid warnings break; } } + if (new_node) { + sculpt_undo_print_nodes(ob, nullptr); + } + return unode; } +bool SCULPT_ensure_dyntopo_node_undo(Object *ob, + PBVHNode *node, + SculptUndoType type, + int extraType) +{ + SculptSession *ss = ob->sculpt; + + UndoSculpt *usculpt = sculpt_undo_get_nodes(); + SculptUndoNode *unode = (SculptUndoNode *)usculpt->nodes.first; + + if (!unode) { + unode = sculpt_undo_alloc_node_type(ob, type); + + BLI_strncpy(unode->idname, ob->id.name, sizeof(unode->idname)); + + unode->type = type; + unode->typemask = 1 << type; + unode->applied = true; + unode->bm_log = ss->bm_log; + unode->bm_entry = BM_log_entry_add(ss->bm, ss->bm_log); + + return SCULPT_ensure_dyntopo_node_undo(ob, node, type, extraType); + } + else if (!(unode->typemask & (1 << type))) { + unode->typemask |= 1 << type; + + /* add a log sub-entry */ + BM_log_entry_add_ex(ss->bm, ss->bm_log, true); + } + + if (!node) { + return false; + } + + int n = BKE_pbvh_get_node_id(ss->pbvh, node); + + if (unode->nodemap_size <= n) { + int newsize = (n + 1); + newsize += newsize >> 1; + + if (!unode->nodemap) { + unode->nodemap = (int *)MEM_callocN(sizeof(*unode->nodemap) * newsize, "unode->nodemap"); + } + else { + unode->nodemap = (int *)MEM_recallocN(unode->nodemap, sizeof(*unode->nodemap) * newsize); + } + + unode->nodemap_size = newsize; + } + + if (unode->nodemap[n] & (1 << type)) { + return false; + } + + unode->nodemap[n] |= 1 << type; + sculpt_undo_bmesh_push(ob, node, type); + + if (extraType >= 0) { + sculpt_undo_bmesh_push(ob, node, (SculptUndoType)extraType); + } + + return true; +} + +static bool check_first_undo_entry_dyntopo(Object *ob) +{ + UndoStack *ustack = ED_undo_stack_get(); + if (!ustack || !ob->sculpt || !ob->sculpt->bm) { + return false; + } + + UndoStep *us = ustack->step_init ? ustack->step_init : ustack->step_active; + bool bad = false; + + if (!us) { + bad = true; + } + else if (us->type) { + if (!STREQ(us->type->name, "Sculpt")) { + bad = true; + } + else { + SculptUndoStep *step = (SculptUndoStep *)us; + SculptUndoNode *unode = (SculptUndoNode *)step->data.nodes.first; + + if (!unode) { + bad = true; + } + else { + UndoStep *act = ustack->step_active; + + if (!act->type || !STREQ(act->type->name, "Sculpt")) { + bad = unode->type != SCULPT_UNDO_DYNTOPO_BEGIN; + } + } + } + } + else { + bad = true; + } + + if (bad) { + sculpt_undo_push_begin_ex(ob, "Dyntopo Begin", true); + SCULPT_undo_push_node(ob, nullptr, SCULPT_UNDO_DYNTOPO_BEGIN); + SCULPT_undo_push_end(ob); + } + + return bad; +} + SculptUndoNode *SCULPT_undo_push_node(Object *ob, PBVHNode *node, SculptUndoType type) { SculptSession *ss = ob->sculpt; @@ -1635,15 +2430,18 @@ SculptUndoNode *SCULPT_undo_push_node(Object *ob, PBVHNode *node, SculptUndoType /* Dynamic topology stores only one undo node per stroke, * regardless of the number of PBVH nodes modified. */ unode = sculpt_undo_bmesh_push(ob, node, type); + sculpt_undo_print_nodes(ob, nullptr); BLI_thread_unlock(LOCK_CUSTOM1); return unode; } if (type == SCULPT_UNDO_GEOMETRY) { unode = sculpt_undo_geometry_push(ob, type); + sculpt_undo_print_nodes(ob, nullptr); BLI_thread_unlock(LOCK_CUSTOM1); return unode; } if ((unode = SCULPT_undo_get_node(node, type))) { + sculpt_undo_print_nodes(ob, nullptr); BLI_thread_unlock(LOCK_CUSTOM1); return unode; } @@ -1689,7 +2487,7 @@ SculptUndoNode *SCULPT_undo_push_node(Object *ob, PBVHNode *node, SculptUndoType sculpt_undo_store_hidden(ob, unode); break; case SCULPT_UNDO_MASK: - if (pbvh_has_mask(ss->pbvh)) { + if (BKE_pbvh_has_mask(ss->pbvh)) { sculpt_undo_store_mask(ob, unode); } break; @@ -1719,6 +2517,8 @@ SculptUndoNode *SCULPT_undo_push_node(Object *ob, PBVHNode *node, SculptUndoType unode->shapeName[0] = '\0'; } + sculpt_undo_print_nodes(ob, nullptr); + BLI_thread_unlock(LOCK_CUSTOM1); return unode; @@ -1734,6 +2534,24 @@ static void sculpt_save_active_attribute(Object *ob, SculptAttrRef *attr) Mesh *me = BKE_object_get_original_mesh(ob); const CustomDataLayer *layer; + if (ob && me && (layer = BKE_id_attributes_active_get((ID *)me))) { + attr->domain = BKE_id_attribute_domain((ID *)me, layer); + BLI_strncpy(attr->name, layer->name, sizeof(attr->name)); + attr->type = layer->type; + } + else { + attr->domain = NO_ACTIVE_LAYER; + attr->name[0] = 0; + } + + attr->was_set = true; +} + +static void sculpt_save_active_attribute_color(Object *ob, SculptAttrRef *attr) +{ + Mesh *me = BKE_object_get_original_mesh(ob); + CustomDataLayer *layer; + if (ob && me && (layer = BKE_id_attributes_active_color_get((ID *)me))) { attr->domain = BKE_id_attribute_domain((ID *)me, layer); BLI_strncpy(attr->name, layer->name, sizeof(attr->name)); @@ -1747,16 +2565,17 @@ static void sculpt_save_active_attribute(Object *ob, SculptAttrRef *attr) attr->was_set = true; } -void SCULPT_undo_push_begin(Object *ob, const wmOperator *op) -{ - SCULPT_undo_push_begin_ex(ob, op->type->name); -} - -void SCULPT_undo_push_begin_ex(Object *ob, const char *name) +static void sculpt_undo_push_begin_ex(Object *ob, const char *name, bool no_first_entry_check) { UndoStack *ustack = ED_undo_stack_get(); + SCULPT_undo_ensure_bmlog(ob); + if (ob != nullptr) { + if (!no_first_entry_check && ob->sculpt && ob->sculpt->bm) { + check_first_undo_entry_dyntopo(ob); + } + /* If possible, we need to tag the object and its geometry data as 'changed in the future' in * the previous undo step if it's a memfile one. */ ED_undosys_stack_memfile_id_changed_tag(ustack, &ob->id); @@ -1770,16 +2589,55 @@ void SCULPT_undo_push_begin_ex(Object *ob, const char *name) ustack, C, name, BKE_UNDOSYS_TYPE_SCULPT); if (!us->active_color_start.was_set) { - sculpt_save_active_attribute(ob, &us->active_color_start); + sculpt_save_active_attribute_color(ob, &us->active_color_start); + } + if (!us->active_attr_start.was_set) { + sculpt_save_active_attribute(ob, &us->active_attr_start); } /* Set end attribute in case SCULPT_undo_push_end is not called, * so we don't end up with corrupted state. */ if (!us->active_color_end.was_set) { - sculpt_save_active_attribute(ob, &us->active_color_end); + sculpt_save_active_attribute_color(ob, &us->active_color_end); us->active_color_end.was_set = false; } + + SculptSession *ss = ob->sculpt; + + /*when pusing an undo node after + undoing to the start of the stack + the log ref count hits zero, we have to check it, + do cleanup and recreate it*/ + + if (ss && ss->bm && ss->bm_log && BM_log_is_dead(ss->bm_log)) { + // forcibly destroy all entries? the 'true' parameter + BM_log_free(ss->bm_log, true); + + BKE_sculpt_ensure_idmap(ob); + + ss->bm_log = BM_log_create(ss->bm, ss->bm_idmap); + + if (ss->pbvh) { + BKE_pbvh_set_bm_log(ss->pbvh, ss->bm_log); + } + } + +#ifdef WITH_PBVH_CACHE + if (ss->pbvh) { + BKE_pbvh_set_cached(ob, ss->pbvh); + } +#endif +} + +void SCULPT_undo_push_begin_ex(Object *ob, const char *name) +{ + return sculpt_undo_push_begin_ex(ob, name, false); +} + +void SCULPT_undo_push_begin(Object *ob, const wmOperator *op) +{ + SCULPT_undo_push_begin_ex(ob, op->type->name); } void SCULPT_undo_push_end(Object *ob) @@ -1793,7 +2651,11 @@ void SCULPT_undo_push_end_ex(struct Object *ob, const bool use_nested_undo) SculptUndoNode *unode; /* We don't need normals in the undo stack. */ - for (unode = static_cast(usculpt->nodes.first); unode; unode = unode->next) { + for (unode = (SculptUndoNode *)usculpt->nodes.first; unode; unode = unode->next) { + if (unode->bm_entry) { + update_unode_bmesh_memsize(unode); + } + if (unode->no) { usculpt->undo_size -= MEM_allocN_len(unode->no); MEM_freeN(unode->no); @@ -1816,15 +2678,16 @@ void SCULPT_undo_push_end_ex(struct Object *ob, const bool use_nested_undo) SculptUndoStep *us = (SculptUndoStep *)BKE_undosys_stack_init_or_active_with_type( ustack, BKE_UNDOSYS_TYPE_SCULPT); - sculpt_save_active_attribute(ob, &us->active_color_end); - sculpt_undo_print_nodes(ob, NULL); + sculpt_save_active_attribute(ob, &us->active_attr_end); + sculpt_save_active_attribute_color(ob, &us->active_color_end); + sculpt_undo_print_nodes(ob, nullptr); } /* -------------------------------------------------------------------- */ /** \name Implements ED Undo System * \{ */ -static void sculpt_undo_set_active_layer(struct bContext *C, SculptAttrRef *attr) +static void sculpt_undo_set_active_layer(struct bContext *C, SculptAttrRef *attr, bool is_color) { if (attr->domain == ATTR_DOMAIN_AUTO) { return; @@ -1834,7 +2697,12 @@ static void sculpt_undo_set_active_layer(struct bContext *C, SculptAttrRef *attr Mesh *me = BKE_object_get_original_mesh(ob); SculptAttrRef existing; - sculpt_save_active_attribute(ob, &existing); + if (is_color) { + sculpt_save_active_attribute_color(ob, &existing); + } + else { + sculpt_save_active_attribute(ob, &existing); + } CustomDataLayer *layer; layer = BKE_id_attribute_find(&me->id, attr->name, attr->type, attr->domain); @@ -1877,16 +2745,19 @@ static void sculpt_undo_set_active_layer(struct bContext *C, SculptAttrRef *attr } } } + else if (layer) { + BKE_id_attributes_active_set(&me->id, layer->name); + } } -static void sculpt_undosys_step_encode_init(struct bContext * /*C*/, UndoStep *us_p) +static void sculpt_undosys_step_encode_init(struct bContext *C, UndoStep *us_p) { SculptUndoStep *us = (SculptUndoStep *)us_p; /* Dummy, memory is cleared anyway. */ BLI_listbase_clear(&us->data.nodes); } -static bool sculpt_undosys_step_encode(struct bContext * /*C*/, struct Main *bmain, UndoStep *us_p) +static bool sculpt_undosys_step_encode(struct bContext *C, struct Main *bmain, UndoStep *us_p) { /* Dummy, encoding is done along the way by adding tiles * to the current 'SculptUndoStep' added by encode_init. */ @@ -1911,11 +2782,10 @@ static void sculpt_undosys_step_decode_undo_impl(struct bContext *C, SculptUndoStep *us) { BLI_assert(us->step.is_applied == true); - - sculpt_undo_restore_list(C, depsgraph, &us->data.nodes); + sculpt_undo_restore_list(C, depsgraph, &us->data.nodes, -1); us->step.is_applied = false; - sculpt_undo_print_nodes(CTX_data_active_object(C), NULL); + sculpt_undo_print_nodes(CTX_data_active_object(C), us); } static void sculpt_undosys_step_decode_redo_impl(struct bContext *C, @@ -1923,11 +2793,10 @@ static void sculpt_undosys_step_decode_redo_impl(struct bContext *C, SculptUndoStep *us) { BLI_assert(us->step.is_applied == false); - - sculpt_undo_restore_list(C, depsgraph, &us->data.nodes); + sculpt_undo_restore_list(C, depsgraph, &us->data.nodes, 1); us->step.is_applied = true; - sculpt_undo_print_nodes(CTX_data_active_object(C), NULL); + sculpt_undo_print_nodes(CTX_data_active_object(C), us); } static void sculpt_undosys_step_decode_undo(struct bContext *C, @@ -1948,12 +2817,18 @@ static void sculpt_undosys_step_decode_undo(struct bContext *C, while ((us_iter != us) || (!is_final && us_iter == us)) { BLI_assert(us_iter->step.type == us->step.type); /* Previous loop ensures this. */ - sculpt_undo_set_active_layer(C, &((SculptUndoStep *)us_iter)->active_color_start); + sculpt_undo_set_active_layer(C, &((SculptUndoStep *)us_iter)->active_attr_start, false); + sculpt_undo_set_active_layer(C, &((SculptUndoStep *)us_iter)->active_color_start, true); + sculpt_undosys_step_decode_undo_impl(C, depsgraph, us_iter); + // sculpt_undo_set_active_layer(C, &((SculptUndoStep *)us_iter)->active_attr_start); if (us_iter == us) { if (us_iter->step.prev && us_iter->step.prev->type == BKE_UNDOSYS_TYPE_SCULPT) { - sculpt_undo_set_active_layer(C, &((SculptUndoStep *)us_iter->step.prev)->active_color_end); + sculpt_undo_set_active_layer( + C, &((SculptUndoStep *)us_iter->step.prev)->active_attr_end, false); + sculpt_undo_set_active_layer( + C, &((SculptUndoStep *)us_iter->step.prev)->active_color_end, true); } break; } @@ -1974,11 +2849,13 @@ static void sculpt_undosys_step_decode_redo(struct bContext *C, us_iter = (SculptUndoStep *)us_iter->step.prev; } while (us_iter && (us_iter->step.is_applied == false)) { - sculpt_undo_set_active_layer(C, &((SculptUndoStep *)us_iter)->active_color_start); + sculpt_undo_set_active_layer(C, &((SculptUndoStep *)us_iter)->active_attr_start, false); + sculpt_undo_set_active_layer(C, &((SculptUndoStep *)us_iter)->active_color_start, true); sculpt_undosys_step_decode_redo_impl(C, depsgraph, us_iter); if (us_iter == us) { - sculpt_undo_set_active_layer(C, &((SculptUndoStep *)us_iter)->active_color_end); + sculpt_undo_set_active_layer(C, &((SculptUndoStep *)us_iter)->active_attr_end, false); + sculpt_undo_set_active_layer(C, &((SculptUndoStep *)us_iter)->active_color_end, true); break; } us_iter = (SculptUndoStep *)us_iter->step.next; @@ -2011,11 +2888,12 @@ static void sculpt_undosys_step_decode( * (some) evaluated data. */ BKE_scene_graph_evaluated_ensure(depsgraph, bmain); - Mesh *me = static_cast(ob->data); +#ifndef WHEN_GLOBAL_UNDO_WORKS /* Don't add sculpt topology undo steps when reading back undo state. * The undo steps must enter/exit for us. */ me->flag &= ~ME_SCULPT_DYNAMIC_TOPOLOGY; - ED_object_sculptmode_enter_ex(bmain, depsgraph, scene, ob, true, nullptr); +#endif + ED_object_sculptmode_enter_ex(bmain, depsgraph, scene, ob, true, nullptr, false); } if (ob->sculpt) { @@ -2091,6 +2969,11 @@ static UndoSculpt *sculpt_undosys_step_get_nodes(UndoStep *us_p) static UndoSculpt *sculpt_undo_get_nodes(void) { UndoStack *ustack = ED_undo_stack_get(); + + if (!ustack) { // happens during file load + return nullptr; + } + UndoStep *us = BKE_undosys_stack_init_or_active_with_type(ustack, BKE_UNDOSYS_TYPE_SCULPT); return us ? sculpt_undosys_step_get_nodes(us) : nullptr; } @@ -2140,7 +3023,8 @@ static void sculpt_undo_push_all_grids(Object *object) * to the current operation without making any stroke in between. * * Skip pushing nodes based on the following logic: on redo SCULPT_UNDO_COORDS will ensure - * PBVH for the new base geometry, which will have same coordinates as if we create PBVH here. */ + * PBVH for the new base geometry, which will have same coordinates as if we create PBVH here. + */ if (ss->pbvh == nullptr) { return; } @@ -2189,3 +3073,251 @@ void ED_sculpt_undo_push_multires_mesh_end(bContext *C, const char *str) } /** \} */ + +#ifdef _ +# undef _ +#endif +#define _(type) \ + case type: \ + return #type; +static const char *undo_type_to_str(int type) +{ + switch (type) { + _(SCULPT_UNDO_DYNTOPO_BEGIN) + _(SCULPT_UNDO_DYNTOPO_END) + _(SCULPT_UNDO_COORDS) + _(SCULPT_UNDO_GEOMETRY) + _(SCULPT_UNDO_DYNTOPO_SYMMETRIZE) + _(SCULPT_UNDO_FACE_SETS) + _(SCULPT_UNDO_HIDDEN) + _(SCULPT_UNDO_MASK) + _(SCULPT_UNDO_COLOR) + default: + return "unknown node type"; + } +} +#undef _ + +static int nodeidgen = 1; + +static void print_sculpt_node(SculptUndoNode *node) +{ + int hash = hash_sculpt_colors(node); + + // if (node->lasthash == 0) { + // node->lasthash = hash; + // } + + printf(" %s:%s {applied=%d gen=%d hash=%d}\n", + undo_type_to_str(node->type), + node->idname, + node->applied, + 0, // node->gen, + hash /*- node->lasthash*/); + if (node->bm_entry) { + BM_log_print_entry(nullptr, node->bm_entry); + } +} + +static void print_sculpt_undo_step(UndoStep *us, UndoStep *active, int i) +{ + SculptUndoNode *node; + + if (us->type != BKE_UNDOSYS_TYPE_SCULPT) { + printf("%d %s (non-sculpt): '%s', type:%s, use_memfile_step:%s\n", + i, + us == active ? "->" : " ", + us->name, + us->type->name, + us->use_memfile_step ? "true" : "false"); + return; + } + + int id = -1; + + SculptUndoStep *su = (SculptUndoStep *)us; + if (!su->id) { + su->id = nodeidgen++; + } + + id = su->id; + + printf("id=%d %s %d %s (use_memfile_step=%s)\n", + id, + us == active ? "->" : " ", + i, + us->name, + us->use_memfile_step ? "true" : "false"); + + if (us->type == BKE_UNDOSYS_TYPE_SCULPT) { + UndoSculpt *usculpt = sculpt_undosys_step_get_nodes(us); + + for (node = (SculptUndoNode *)usculpt->nodes.first; node; node = node->next) { + print_sculpt_node(node); + } + } +} + +extern "C" void SCULPT_substep_undo(bContext *C, int dir) +{ + printf("%s: not working!\n"); +#if 0 // XXX + Scene *scene = CTX_data_scene(C); + Object *ob = CTX_data_active_object(C); + + Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); + + if (!scene || !ob || !ob->sculpt) { + printf("not in sculpt mode\n"); + return; + } + + SculptSession *ss = ob->sculpt; + + if (!ss->bm) { + printf("not in dyntopo mode\n"); + return; + } + + BmeshUndoData data = {ss->pbvh, + ss->bm, + false, + false, + ss->cd_face_node_offset, + ss->cd_vert_node_offset, + ss->cd_sculpt_vert, + ss->attrs.boundary_flags->bmesh_cd_offset, + false, + false}; + + BMLogCallbacks callbacks = {bmesh_undo_on_vert_add, + bmesh_undo_on_vert_kill, + bmesh_undo_on_vert_change, + bmesh_undo_on_edge_add, + bmesh_undo_on_edge_kill, + bmesh_undo_on_edge_change, + bmesh_undo_on_face_add, + bmesh_undo_on_face_kill, + bmesh_undo_on_face_change, + bmesh_undo_full_mesh, + nullptr, + (void *)&data}; + + BM_log_undo_single(ss->bm, ss->bm_log, &callbacks, nullptr); + + BKE_sculpt_update_object_for_edit(depsgraph, ob, true, false, false); + DEG_id_tag_update(&ob->id, ID_RECALC_SHADING); +#endif +} + +void BM_log_get_changed(BMesh *bm, struct BMIdMap *idmap, BMLogEntry *_entry, SmallHash *sh); + +void ED_sculpt_fast_save_bmesh(Object *ob) +{ + SculptSession *ss = ob->sculpt; + BMesh *bm = ss->bm; + + if (!bm || !ss) { + return; + } + +#if 1 + struct BMeshToMeshParams params = {0}; + + void BM_mesh_bm_to_me_threaded( + Main * bmain, Object * ob, BMesh * bm, Mesh * me, const struct BMeshToMeshParams *params); + + params.update_shapekey_indices = true; + params.cd_mask_extra.vmask = CD_MASK_MESH_ID | CD_MASK_DYNTOPO_VERT; + params.cd_mask_extra.emask = CD_MASK_MESH_ID; + params.cd_mask_extra.pmask = CD_MASK_MESH_ID; + + // BM_mesh_bm_to_me_threaded(nullptr, ob, bm, (Mesh *)ob->data, ¶ms); + BM_mesh_bm_to_me(nullptr, ob, bm, (Mesh *)ob->data, ¶ms); +#else + SculptUndoStep *last_step = nullptr; + + UndoStack *ustack = ED_undo_stack_get(); + UndoStep *us = ustack->step_active; + + SmallHash elems; + BLI_smallhash_init(&elems); + + bool bad = false; + + if (!us) { + printf("no active undo step!"); + bad = true; + } + else { + while (us) { + us = us->prev; + + if (us->type == BKE_UNDOSYS_TYPE_SCULPT) { + SculptUndoStep *usculpt = (SculptUndoStep *)us; + + LISTBASE_FOREACH (SculptUndoNode *, unode, &usculpt->data.nodes) { + if (unode->bm_entry) { + BM_log_get_changed(bm, ss->bm_idmap, unode->bm_entry, &elems); + } + } + + if (usculpt->auto_saved) { + last_step = usculpt; + break; + } + + if (!last_step) { + usculpt->auto_saved = true; + } + + last_step = usculpt; + } + } + } + + if (!last_step) { + bad = true; + } + else { + last_step->auto_saved = true; + } + + if (bad) { + printf("%s: Failed to find sculpt undo stack entries\n", __func__); + + /* Just save everything */ + struct BMeshToMeshParams params = {0}; + BM_mesh_bm_to_me(nullptr, ob, bm, (Mesh *)ob->data, ¶ms); + return; + } + + int totv = 0, tote = 0, totl = 0, totf = 0; + + BM_mesh_elem_table_ensure(bm, BM_VERT | BM_EDGE | BM_LOOP | BM_FACE); + + SmallHashIter iter; + uintptr_t key; + void *val = BLI_smallhash_iternew(&elems, &iter, &key); + for (; val; val = BLI_smallhash_iternext(&iter, &key)) { + BMElem *elem = (BMElem *)key; + + switch (elem->head.htype) { + case BM_VERT: + totv++; + break; + case BM_EDGE: + tote++; + break; + case BM_LOOP: + totl++; + break; + case BM_FACE: + totf++; + break; + } + } + + BLI_smallhash_release(&elems); +#endif +} diff --git a/source/blender/editors/sculpt_paint/sculpt_uv.c b/source/blender/editors/sculpt_paint/sculpt_uv.c index 998fe50b9ac..7c066948387 100644 --- a/source/blender/editors/sculpt_paint/sculpt_uv.c +++ b/source/blender/editors/sculpt_paint/sculpt_uv.c @@ -862,6 +862,7 @@ static UvSculptData *uv_sculpt_stroke_init(bContext *C, wmOperator *op, const wm alpha = BKE_brush_alpha_get(scene, brush); radius = BKE_brush_size_get(scene, brush); + sima = CTX_wm_space_image(C); ED_space_image_get_size(sima, &width, &height); ED_space_image_get_zoom(sima, region, &zoomx, &zoomy); diff --git a/source/blender/editors/space_buttons/CMakeLists.txt b/source/blender/editors/space_buttons/CMakeLists.txt index d973f96424d..b5f8ef0e109 100644 --- a/source/blender/editors/space_buttons/CMakeLists.txt +++ b/source/blender/editors/space_buttons/CMakeLists.txt @@ -10,6 +10,7 @@ set(INC ../../makesdna ../../makesrna ../../windowmanager + ../../../../intern/atomic ../../../../intern/guardedalloc ../../bmesh diff --git a/source/blender/editors/space_buttons/buttons_context.c b/source/blender/editors/space_buttons/buttons_context.c index c5f457b3fd8..f8f468a39b2 100644 --- a/source/blender/editors/space_buttons/buttons_context.c +++ b/source/blender/editors/space_buttons/buttons_context.c @@ -556,7 +556,7 @@ static bool buttons_context_path( path->len++; } /* No pinned root, use scene as initial root. */ - else if (mainb != BCONTEXT_TOOL) { + else if (!ELEM(mainb, BCONTEXT_TOOL, BCONTEXT_BRUSH_EDITOR)) { RNA_id_pointer_create(&scene->id, &path->ptr[0]); path->len++; @@ -597,6 +597,7 @@ static bool buttons_context_path( case BCONTEXT_COLLECTION: /* This is for Line Art collection flags */ found = buttons_context_path_collection(C, path, window); break; + case BCONTEXT_BRUSH_EDITOR: case BCONTEXT_TOOL: found = true; break; @@ -857,7 +858,7 @@ int /*eContextResult*/ buttons_context(const bContext *C, return CTX_RESULT_MEMBER_NOT_FOUND; } - if (sbuts->mainb == BCONTEXT_TOOL) { + if (ELEM(sbuts->mainb, BCONTEXT_TOOL, BCONTEXT_BRUSH_EDITOR)) { return CTX_RESULT_MEMBER_NOT_FOUND; } @@ -1168,7 +1169,7 @@ int /*eContextResult*/ buttons_context(const bContext *C, static bool buttons_panel_context_poll(const bContext *C, PanelType *UNUSED(pt)) { SpaceProperties *sbuts = CTX_wm_space_properties(C); - return sbuts->mainb != BCONTEXT_TOOL; + return !ELEM(sbuts->mainb, BCONTEXT_TOOL, BCONTEXT_BRUSH_EDITOR); } static void buttons_panel_context_draw(const bContext *C, Panel *panel) diff --git a/source/blender/editors/space_buttons/space_buttons.c b/source/blender/editors/space_buttons/space_buttons.c index 5a333869dea..81aa437b12a 100644 --- a/source/blender/editors/space_buttons/space_buttons.c +++ b/source/blender/editors/space_buttons/space_buttons.c @@ -160,6 +160,10 @@ int ED_buttons_tabs_list(SpaceProperties *sbuts, short *context_tabs_array) context_tabs_array[length] = BCONTEXT_TOOL; length++; } + if (sbuts->pathflag & (1 << BCONTEXT_BRUSH_EDITOR)) { + context_tabs_array[length] = BCONTEXT_BRUSH_EDITOR; + length++; + } if (length != 0) { context_tabs_array[length] = -1; length++; @@ -287,6 +291,8 @@ static const char *buttons_main_region_context_string(const short mainb) return "bone_constraint"; case BCONTEXT_TOOL: return "tool"; + case BCONTEXT_BRUSH_EDITOR: + return "brush_editor"; } /* All the cases should be handled. */ @@ -341,7 +347,7 @@ static bool property_search_for_context(const bContext *C, ARegion *region, Spac { const char *contexts[2] = {buttons_main_region_context_string(sbuts->mainb), NULL}; - if (sbuts->mainb == BCONTEXT_TOOL) { + if (sbuts->mainb == BCONTEXT_TOOL || sbuts->mainb == BCONTEXT_BRUSH_EDITOR) { return false; } @@ -354,7 +360,7 @@ static void property_search_move_to_next_tab_with_results(SpaceProperties *sbuts const int tabs_len) { /* As long as all-tab search in the tool is disabled in the tool context, don't move from it. */ - if (sbuts->mainb == BCONTEXT_TOOL) { + if (sbuts->mainb == BCONTEXT_TOOL || sbuts->mainb == BCONTEXT_BRUSH_EDITOR) { return; } @@ -495,6 +501,9 @@ static void buttons_main_region_layout(const bContext *C, ARegion *region) if (sbuts->mainb == BCONTEXT_TOOL) { ED_view3d_buttons_region_layout_ex(C, region, "Tool"); } + else if (sbuts->mainb == BCONTEXT_BRUSH_EDITOR) { + ED_view3d_buttons_region_layout_ex(C, region, "Tool"); + } else { buttons_main_region_layout_properties(C, sbuts, region); } @@ -579,7 +588,7 @@ static void buttons_header_region_message_subscribe(const wmRegionMessageSubscri WM_msg_subscribe_rna_anon_prop(mbus, ViewLayer, name, &msg_sub_value_region_tag_redraw); } - if (sbuts->mainb == BCONTEXT_TOOL) { + if (ELEM(sbuts->mainb, BCONTEXT_TOOL, BCONTEXT_BRUSH_EDITOR)) { WM_msg_subscribe_rna_anon_prop(mbus, WorkSpace, tools, &msg_sub_value_region_tag_redraw); } } @@ -771,6 +780,7 @@ static void buttons_area_listener(const wmSpaceTypeListenerParams *params) case NC_BRUSH: buttons_area_redraw(area, BCONTEXT_TEXTURE); buttons_area_redraw(area, BCONTEXT_TOOL); + buttons_area_redraw(area, BCONTEXT_BRUSH_EDITOR); sbuts->preview = 1; break; case NC_TEXTURE: diff --git a/source/blender/editors/space_file/fsmenu.c b/source/blender/editors/space_file/fsmenu.c index 959f8b01ec8..4964aa15bec 100644 --- a/source/blender/editors/space_file/fsmenu.c +++ b/source/blender/editors/space_file/fsmenu.c @@ -671,7 +671,7 @@ void fsmenu_read_system(struct FSMenu *fsmenu, int read_bookmarks) CoTaskMemFree(volume_name_wchar); } } - CoTaskMemFree(volume); + CoTaskMemFree((void *)volume); } desktop->lpVtbl->Release(desktop); } diff --git a/source/blender/editors/space_image/CMakeLists.txt b/source/blender/editors/space_image/CMakeLists.txt index 8b822c4fc2a..34f41f03e55 100644 --- a/source/blender/editors/space_image/CMakeLists.txt +++ b/source/blender/editors/space_image/CMakeLists.txt @@ -17,6 +17,7 @@ set(INC ../../render ../../windowmanager ../../../../intern/clog + ../../../../intern/atomic ../../../../intern/guardedalloc # dna_type_offsets.h diff --git a/source/blender/editors/space_info/CMakeLists.txt b/source/blender/editors/space_info/CMakeLists.txt index 7c108c416d3..9285288e4f1 100644 --- a/source/blender/editors/space_info/CMakeLists.txt +++ b/source/blender/editors/space_info/CMakeLists.txt @@ -14,6 +14,7 @@ set(INC ../../makesdna ../../makesrna ../../windowmanager + ../../../../intern/atomic ../../../../intern/guardedalloc # dna_type_offsets.h diff --git a/source/blender/editors/space_info/info_stats.cc b/source/blender/editors/space_info/info_stats.cc index e8b005b2b67..03f32259c93 100644 --- a/source/blender/editors/space_info/info_stats.cc +++ b/source/blender/editors/space_info/info_stats.cc @@ -342,8 +342,10 @@ static void stats_object_sculpt(const Object *ob, SceneStats *stats) stats->totfacesculpt = ss->totfaces; break; case PBVH_BMESH: - stats->totvertsculpt = ob->sculpt->bm->totvert; - stats->tottri = ob->sculpt->bm->totface; + if (ob->sculpt->bm) { + stats->totvertsculpt = ob->sculpt->bm->totvert; + stats->tottri = ob->sculpt->bm->totface; + } break; case PBVH_GRIDS: stats->totvertsculpt = BKE_pbvh_get_grid_num_verts(ss->pbvh); diff --git a/source/blender/editors/space_view3d/CMakeLists.txt b/source/blender/editors/space_view3d/CMakeLists.txt index 691bba4c02d..696d5810a61 100644 --- a/source/blender/editors/space_view3d/CMakeLists.txt +++ b/source/blender/editors/space_view3d/CMakeLists.txt @@ -17,6 +17,7 @@ set(INC ../../render ../../windowmanager ../../../../intern/guardedalloc + ../../../../intern/atomic ../../../../intern/mantaflow/extern # dna_type_offsets.h diff --git a/source/blender/editors/space_view3d/space_view3d.cc b/source/blender/editors/space_view3d/space_view3d.cc index cd7794300f6..1ced76e7eaa 100644 --- a/source/blender/editors/space_view3d/space_view3d.cc +++ b/source/blender/editors/space_view3d/space_view3d.cc @@ -1687,9 +1687,16 @@ void ED_view3d_buttons_region_layout_ex(const bContext *C, case CTX_MODE_POSE: ARRAY_SET_ITEMS(contexts, ".posemode"); break; - case CTX_MODE_SCULPT: - ARRAY_SET_ITEMS(contexts, ".paint_common", ".sculpt_mode"); + case CTX_MODE_SCULPT: { + SpaceProperties *sbuts = CTX_wm_space_properties(C); + if (sbuts && sbuts->mainb == BCONTEXT_BRUSH_EDITOR) { + ARRAY_SET_ITEMS(contexts, ".paint_common", ".brush_editor"); + } + else { + ARRAY_SET_ITEMS(contexts, ".paint_common", ".sculpt_mode"); + } break; + } case CTX_MODE_PAINT_WEIGHT: ARRAY_SET_ITEMS(contexts, ".paint_common", ".weightpaint"); break; diff --git a/source/blender/editors/transform/CMakeLists.txt b/source/blender/editors/transform/CMakeLists.txt index 87da287d6a3..c13e248395a 100644 --- a/source/blender/editors/transform/CMakeLists.txt +++ b/source/blender/editors/transform/CMakeLists.txt @@ -15,6 +15,7 @@ set(INC ../../render ../../sequencer ../../windowmanager + ../../../../intern/atomic ../../../../intern/guardedalloc # RNA_prototypes.h ${CMAKE_BINARY_DIR}/source/blender/makesrna diff --git a/source/blender/editors/transform/transform_snap_object.cc b/source/blender/editors/transform/transform_snap_object.cc index a33f0bf6fca..2014633579c 100644 --- a/source/blender/editors/transform/transform_snap_object.cc +++ b/source/blender/editors/transform/transform_snap_object.cc @@ -826,6 +826,12 @@ static bool raycastEditMesh(SnapObjectContext *sctx, ListBase *r_hit_list) { bool retval = false; + + if (!em) { + printf("%s: em was NULL!\n", __func__); + return false; + } + if (em->bm->totface == 0) { return retval; } diff --git a/source/blender/editors/undo/CMakeLists.txt b/source/blender/editors/undo/CMakeLists.txt index eebc517603b..9b678164b44 100644 --- a/source/blender/editors/undo/CMakeLists.txt +++ b/source/blender/editors/undo/CMakeLists.txt @@ -11,6 +11,7 @@ set(INC ../../makesrna ../../windowmanager ../../../../intern/clog + ../../../../intern/atomic ../../../../intern/guardedalloc ../../bmesh ) diff --git a/source/blender/editors/undo/memfile_undo.cc b/source/blender/editors/undo/memfile_undo.cc index a4d05c31013..fe96282d4fb 100644 --- a/source/blender/editors/undo/memfile_undo.cc +++ b/source/blender/editors/undo/memfile_undo.cc @@ -54,6 +54,11 @@ typedef struct MemFileUndoStep { MemFileUndoData *data; } MemFileUndoStep; +MemFileUndoData *memfile_get_step_data(MemFileUndoStep *us) +{ + return us->data; +} + static bool memfile_undosys_poll(bContext *C) { /* other poll functions must run first, this is a catch-all. */ diff --git a/source/blender/editors/util/CMakeLists.txt b/source/blender/editors/util/CMakeLists.txt index 66c3b9da74c..975c23410dc 100644 --- a/source/blender/editors/util/CMakeLists.txt +++ b/source/blender/editors/util/CMakeLists.txt @@ -17,6 +17,7 @@ set(INC ../../sequencer ../../windowmanager ../../../../intern/clog + ../../../../intern/atomic ../../../../intern/guardedalloc # RNA_prototypes.h ${CMAKE_BINARY_DIR}/source/blender/makesrna diff --git a/source/blender/editors/util/ed_util.cc b/source/blender/editors/util/ed_util.cc index eed46ea1c35..8bab7e1430d 100644 --- a/source/blender/editors/util/ed_util.cc +++ b/source/blender/editors/util/ed_util.cc @@ -156,7 +156,7 @@ void ED_editors_init(bContext *C) else if (mode & OB_MODE_ALL_SCULPT) { if (obact == ob) { if (mode == OB_MODE_SCULPT) { - ED_object_sculptmode_enter_ex(bmain, depsgraph, scene, ob, true, reports); + ED_object_sculptmode_enter_ex(bmain, depsgraph, scene, ob, false, reports, true); } else if (mode == OB_MODE_VERTEX_PAINT) { ED_object_vpaintmode_enter_ex(bmain, depsgraph, scene, ob); diff --git a/source/blender/editors/uvedit/uvedit_unwrap_ops.cc b/source/blender/editors/uvedit/uvedit_unwrap_ops.cc index 050362d0c7f..2770190f534 100644 --- a/source/blender/editors/uvedit/uvedit_unwrap_ops.cc +++ b/source/blender/editors/uvedit/uvedit_unwrap_ops.cc @@ -3176,7 +3176,7 @@ void ED_uvedit_add_simple_uvs(Main *bmain, const Scene *scene, Object *ob) BMeshFromMeshParams bm_from_me_params{}; bm_from_me_params.calc_face_normal = true; bm_from_me_params.calc_vert_normal = true; - BM_mesh_bm_from_me(bm, me, &bm_from_me_params); + BM_mesh_bm_from_me(nullptr, bm, me, &bm_from_me_params); /* Select all UVs for cube_project. */ ED_uvedit_select_all(bm); @@ -3197,7 +3197,7 @@ void ED_uvedit_add_simple_uvs(Main *bmain, const Scene *scene, Object *ob) /* Write back from BMesh to Mesh. */ BMeshToMeshParams bm_to_me_params{}; - BM_mesh_bm_to_me(bmain, bm, me, &bm_to_me_params); + BM_mesh_bm_to_me(bmain, nullptr, bm, me, &bm_to_me_params); BM_mesh_free(bm); if (sync_selection) { diff --git a/source/blender/gpu/CMakeLists.txt b/source/blender/gpu/CMakeLists.txt index 4967d8984c5..f45ed7037af 100644 --- a/source/blender/gpu/CMakeLists.txt +++ b/source/blender/gpu/CMakeLists.txt @@ -35,11 +35,11 @@ set(INC # For node muting stuff. ../nodes - ../../../intern/atomic ../../../intern/clog ../../../intern/ghost ../../../intern/guardedalloc ../../../intern/mantaflow/extern + ../../../intern/atomic ) set(INC_SYS @@ -698,6 +698,9 @@ if(WITH_OPENCOLORIO) add_definitions(-DWITH_OCIO) endif() +remove_cc_flag("-fsanitize=address") +remove_cc_flag("/fsanitize=address") + blender_add_lib(bf_gpu "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") target_link_libraries(bf_gpu PUBLIC bf_compositor_shaders diff --git a/source/blender/gpu/GPU_vertex_format.h b/source/blender/gpu/GPU_vertex_format.h index 0a59903ff04..abb88e329aa 100644 --- a/source/blender/gpu/GPU_vertex_format.h +++ b/source/blender/gpu/GPU_vertex_format.h @@ -98,6 +98,8 @@ void GPU_vertformat_from_shader(GPUVertFormat *format, const struct GPUShader *s uint GPU_vertformat_attr_add( GPUVertFormat *, const char *name, GPUVertCompType, uint comp_len, GPUVertFetchMode); void GPU_vertformat_alias_add(GPUVertFormat *, const char *alias); +void GPU_vertformat_alias_clear(GPUVertFormat *format, int attr_id); +void GPU_vertformat_alias_add_n(GPUVertFormat *format, int attr_id, const char *alias); /** * Makes vertex attribute from the next vertices to be accessible in the vertex shader. diff --git a/source/blender/gpu/intern/gpu_batch.cc b/source/blender/gpu/intern/gpu_batch.cc index 1acdfb0a1a9..cc23a66d1c1 100644 --- a/source/blender/gpu/intern/gpu_batch.cc +++ b/source/blender/gpu/intern/gpu_batch.cc @@ -288,6 +288,9 @@ void GPU_batch_draw_advanced(GPUBatch *gpu_batch, if (batch->elem) { vertex_count = batch->elem_()->index_len_get(); } + else if (!batch->verts_(0)) { + return; + } else { vertex_count = batch->verts_(0)->vertex_len; } diff --git a/source/blender/gpu/opengl/gl_shader.cc b/source/blender/gpu/opengl/gl_shader.cc index 58407db015d..d63af5e8377 100644 --- a/source/blender/gpu/opengl/gl_shader.cc +++ b/source/blender/gpu/opengl/gl_shader.cc @@ -7,6 +7,7 @@ #include "BKE_global.h" +#include "BLI_compiler_attrs.h" #include "BLI_string.h" #include "BLI_vector.hh" @@ -19,6 +20,14 @@ #include "gl_shader.hh" #include "gl_shader_interface.hh" +#if defined(__clang__) || defined(__GCC__) +# define ATTR_NO_ASAN __attribute__((no_sanitize("address"))) +#elif _MSC_VER +# define ATTR_NO_ASAN __declspec(no_sanitize_address) +#else +# define ATTR_NO_ASAN +#endif + using namespace blender; using namespace blender::gpu; using namespace blender::gpu::shader; @@ -971,7 +980,7 @@ void GLShader::compute_shader_from_glsl(MutableSpan sources) compute_shader_ = this->create_shader_stage(GL_COMPUTE_SHADER, sources); } -bool GLShader::finalize(const shader::ShaderCreateInfo *info) +ATTR_NO_ASAN bool GLShader::finalize(const shader::ShaderCreateInfo *info) { if (compilation_failed_) { return false; diff --git a/source/blender/io/alembic/exporter/abc_writer_mesh.cc b/source/blender/io/alembic/exporter/abc_writer_mesh.cc index 77d42d905c7..6a49fdc8a86 100644 --- a/source/blender/io/alembic/exporter/abc_writer_mesh.cc +++ b/source/blender/io/alembic/exporter/abc_writer_mesh.cc @@ -156,11 +156,11 @@ void ABCGenericMeshWriter::do_write(HierarchyContext &context) const int quad_method = args_.export_params->quad_method; const int ngon_method = args_.export_params->ngon_method; - BMeshCreateParams bmesh_create_params{}; - BMeshFromMeshParams bmesh_from_mesh_params{}; + BMeshCreateParams bmesh_create_params = {0}; + BMeshFromMeshParams bmesh_from_mesh_params = {0}; bmesh_from_mesh_params.calc_face_normal = true; bmesh_from_mesh_params.calc_vert_normal = true; - BMesh *bm = BKE_mesh_to_bmesh_ex(mesh, &bmesh_create_params, &bmesh_from_mesh_params); + BMesh *bm = BKE_mesh_to_bmesh_ex(object, mesh, &bmesh_create_params, &bmesh_from_mesh_params); BM_mesh_triangulate(bm, quad_method, ngon_method, 4, tag_only, nullptr, nullptr, nullptr); diff --git a/source/blender/io/collada/collada_utils.cpp b/source/blender/io/collada/collada_utils.cpp index a71fbd50a2e..476872a32cd 100644 --- a/source/blender/io/collada/collada_utils.cpp +++ b/source/blender/io/collada/collada_utils.cpp @@ -414,12 +414,12 @@ void bc_triangulate_mesh(Mesh *me) BMeshFromMeshParams bm_from_me_params{}; bm_from_me_params.calc_face_normal = true; bm_from_me_params.calc_vert_normal = true; - BM_mesh_bm_from_me(bm, me, &bm_from_me_params); + BM_mesh_bm_from_me(nullptr, bm, me, &bm_from_me_params); BM_mesh_triangulate(bm, quad_method, use_beauty, 4, tag_only, nullptr, nullptr, nullptr); BMeshToMeshParams bm_to_me_params{}; bm_to_me_params.calc_object_remap = false; - BM_mesh_bm_to_me(nullptr, bm, me, &bm_to_me_params); + BM_mesh_bm_to_me(nullptr, nullptr, bm, me, &bm_to_me_params); BM_mesh_free(bm); } diff --git a/source/blender/io/wavefront_obj/exporter/obj_export_mesh.cc b/source/blender/io/wavefront_obj/exporter/obj_export_mesh.cc index e2ad7de7291..679beb07a0d 100644 --- a/source/blender/io/wavefront_obj/exporter/obj_export_mesh.cc +++ b/source/blender/io/wavefront_obj/exporter/obj_export_mesh.cc @@ -112,7 +112,8 @@ void OBJMesh::triangulate_mesh_eval() * triangulated here. */ const int triangulate_min_verts = 4; - BMesh *bmesh = BKE_mesh_to_bmesh_ex(export_mesh_, &bm_create_params, &bm_convert_params); + BMesh *bmesh = BKE_mesh_to_bmesh_ex( + nullptr, export_mesh_, &bm_create_params, &bm_convert_params); BM_mesh_triangulate(bmesh, MOD_TRIANGULATE_NGON_BEAUTY, MOD_TRIANGULATE_QUAD_SHORTEDGE, diff --git a/source/blender/makesdna/DNA_brush_defaults.h b/source/blender/makesdna/DNA_brush_defaults.h index 6e88275672a..a4e1bcc7a82 100644 --- a/source/blender/makesdna/DNA_brush_defaults.h +++ b/source/blender/makesdna/DNA_brush_defaults.h @@ -28,7 +28,13 @@ .size = 35, /* radius of the brush in pixels */ \ .alpha = 1.0f, /* brush strength/intensity probably variable should be renamed? */ \ .autosmooth_factor = 0.0f, \ + .autosmooth_projection = 0.0f,\ + .autosmooth_radius_factor = 1.0f,\ + .autosmooth_spacing = 12,\ .topology_rake_factor = 0.0f, \ + .topology_rake_projection = 1.0f,\ + .topology_rake_radius_factor = 1.0f,\ + .topology_rake_spacing = 12,\ .crease_pinch_factor = 0.5f, \ .normal_radius_factor = 0.5f, \ .wet_paint_radius_factor = 0.5f, \ @@ -59,6 +65,7 @@ .smooth_stroke_radius = 75, \ .smooth_stroke_factor = 0.9f, \ \ + .smear_deform_blend = 0.5f,\ /* Time delay between dots of paint or sculpting when doing airbrush mode. */ \ .rate = 0.1f, \ \ @@ -99,6 +106,18 @@ \ .mtex = _DNA_DEFAULT_MTex, \ .mask_mtex = _DNA_DEFAULT_MTex, \ + .dyntopo = {\ + .detail_range = 0.4f,\ + .detail_percent = 25.0f,\ + .detail_size = 12.0f,\ + .constant_detail = 3.0f,\ + .flag = DYNTOPO_COLLAPSE|DYNTOPO_SUBDIVIDE,\ + .mode = DYNTOPO_DETAIL_RELATIVE,\ + .inherit = DYNTOPO_INHERIT_ALL,\ + .spacing = 25,\ + .radius_scale = 1.0f\ + },\ + .concave_mask_factor = 0.75f\ } /** \} */ diff --git a/source/blender/makesdna/DNA_brush_enums.h b/source/blender/makesdna/DNA_brush_enums.h index 72357ea6734..66b2a476963 100644 --- a/source/blender/makesdna/DNA_brush_enums.h +++ b/source/blender/makesdna/DNA_brush_enums.h @@ -224,11 +224,14 @@ typedef enum eBrushClothDeformType { BRUSH_CLOTH_DEFORM_INFLATE = 5, BRUSH_CLOTH_DEFORM_EXPAND = 6, BRUSH_CLOTH_DEFORM_SNAKE_HOOK = 7, + BRUSH_CLOTH_DEFORM_ELASTIC_DRAG = 8, } eBrushClothDeformType; typedef enum eBrushSmoothDeformType { BRUSH_SMOOTH_DEFORM_LAPLACIAN = 0, BRUSH_SMOOTH_DEFORM_SURFACE = 1, + BRUSH_SMOOTH_DEFORM_DIRECTIONAL = 2, + BRUSH_SMOOTH_DEFORM_UNIFORM_WEIGHTS = 3, } eBrushSmoothDeformType; typedef enum eBrushClothForceFalloffType { @@ -246,6 +249,7 @@ typedef enum eBrushPoseDeformType { BRUSH_POSE_DEFORM_ROTATE_TWIST = 0, BRUSH_POSE_DEFORM_SCALE_TRASLATE = 1, BRUSH_POSE_DEFORM_SQUASH_STRETCH = 2, + BRUSH_POSE_DEFORM_BEND = 3, } eBrushPoseDeformType; typedef enum eBrushPoseOriginType { @@ -260,6 +264,12 @@ typedef enum eBrushSmearDeformType { BRUSH_SMEAR_DEFORM_EXPAND = 2, } eBrushSmearDeformType; +typedef enum eBrushArrayDeformType { + BRUSH_ARRAY_DEFORM_LINEAR = 0, + BRUSH_ARRAY_DEFORM_RADIAL = 1, + BRUSH_ARRAY_DEFORM_PATH = 2, +} eBrushArrayDeformType; + typedef enum eBrushSlideDeformType { BRUSH_SLIDE_DEFORM_DRAG = 0, BRUSH_SLIDE_DEFORM_PINCH = 1, @@ -273,6 +283,7 @@ typedef enum eBrushBoundaryDeformType { BRUSH_BOUNDARY_DEFORM_GRAB = 3, BRUSH_BOUNDARY_DEFORM_TWIST = 4, BRUSH_BOUNDARY_DEFORM_SMOOTH = 5, + BRUSH_BOUNDARY_DEFORM_CIRCLE = 6, } eBrushBushBoundaryDeformType; typedef enum eBrushBoundaryFalloffType { @@ -282,6 +293,12 @@ typedef enum eBrushBoundaryFalloffType { BRUSH_BOUNDARY_FALLOFF_LOOP_INVERT = 3, } eBrushBoundaryFalloffType; +typedef enum eBrushSceneProjectDirectionType { + BRUSH_SCENE_PROJECT_DIRECTION_VIEW = 0, + BRUSH_SCENE_PROJECT_DIRECTION_VERTEX_NORMAL = 1, + BRUSH_SCENE_PROJECT_DIRECTION_BRUSH_NORMAL = 2, +} eBrushSceneProjectDirectionType; + typedef enum eBrushSnakeHookDeformType { BRUSH_SNAKE_HOOK_DEFORM_FALLOFF = 0, BRUSH_SNAKE_HOOK_DEFORM_ELASTIC = 1, @@ -412,6 +429,25 @@ typedef enum eBrushFlags2 { BRUSH_CLOTH_USE_COLLISION = (1 << 6), BRUSH_AREA_RADIUS_PRESSURE = (1 << 7), BRUSH_GRAB_SILHOUETTE = (1 << 8), + + BRUSH_USE_SURFACE_FALLOFF = (1 << 9), + BRUSH_ARRAY_LOCK_ORIENTATION = (1 << 10), + BRUSH_ARRAY_FILL_HOLES = (1 << 11), + + BRUSH_CURVATURE_RAKE = (1 << 12), + BRUSH_CUSTOM_AUTOSMOOTH_SPACING = (1 << 13), + BRUSH_CUSTOM_TOPOLOGY_RAKE_SPACING = (1 << 14), + BRUSH_TOPOLOGY_RAKE_IGNORE_BRUSH_FALLOFF = (1 << 15), + BRUSH_SMOOTH_USE_AREA_WEIGHT = (1 << 16), + + /*preserve face set boundaries*/ + BRUSH_SMOOTH_PRESERVE_FACE_SETS = (1 << 17), + + /*topology rake in dynamic mode*/ + BRUSH_DYNAMIC_RAKE = (1 << 18), + + /* sets face set slide to 0.0 */ + BRUSH_HARD_EDGE_MODE = (1 << 19), } eBrushFlags2; typedef enum { @@ -467,6 +503,20 @@ typedef enum eBrushSculptTool { SCULPT_TOOL_BOUNDARY = 30, SCULPT_TOOL_DISPLACEMENT_ERASER = 31, SCULPT_TOOL_DISPLACEMENT_SMEAR = 32, + SCULPT_TOOL_FAIRING = 33, + SCULPT_TOOL_SCENE_PROJECT = 34, + SCULPT_TOOL_SYMMETRIZE = 35, + SCULPT_TOOL_TWIST = 36, + SCULPT_TOOL_ARRAY = 37, + SCULPT_TOOL_VCOL_BOUNDARY = 38, + SCULPT_TOOL_UV_SMOOTH = 39, + + SCULPT_TOOL_TOPOLOGY_RAKE = 40, + SCULPT_TOOL_DYNTOPO = 41, + SCULPT_TOOL_AUTO_FSET = 42, + SCULPT_TOOL_RELAX = 43, + SCULPT_TOOL_ENHANCE_DETAILS = 44, + SCULPT_TOOL_DISPLACEMENT_HEAL = 45 } eBrushSculptTool; /** #Brush.uv_sculpt_tool */ @@ -491,6 +541,8 @@ typedef enum eBrushCurvesSculptTool { CURVES_SCULPT_TOOL_SLIDE = 10, } eBrushCurvesSculptTool; +#define SCULPT_TOOL_HAS_VCOL_BOUNDARY_SMOOTH(t) ELEM(t, SCULPT_TOOL_PAINT, SCULPT_TOOL_SMEAR) + /** When #BRUSH_ACCUMULATE is used */ #define SCULPT_TOOL_HAS_ACCUMULATE(t) \ ELEM(t, \ @@ -515,31 +567,24 @@ typedef enum eBrushCurvesSculptTool { #define SCULPT_TOOL_HAS_DYNTOPO(t) \ (ELEM(t, /* These brushes, as currently coded, cannot support dynamic topology */ \ SCULPT_TOOL_GRAB, \ - SCULPT_TOOL_ROTATE, \ SCULPT_TOOL_CLOTH, \ - SCULPT_TOOL_THUMB, \ - SCULPT_TOOL_LAYER, \ SCULPT_TOOL_DISPLACEMENT_ERASER, \ - SCULPT_TOOL_DRAW_SHARP, \ - SCULPT_TOOL_SLIDE_RELAX, \ + SCULPT_TOOL_FAIRING, \ SCULPT_TOOL_ELASTIC_DEFORM, \ SCULPT_TOOL_BOUNDARY, \ SCULPT_TOOL_POSE, \ SCULPT_TOOL_DRAW_FACE_SETS, \ - SCULPT_TOOL_PAINT, \ - SCULPT_TOOL_SMEAR, \ + SCULPT_TOOL_UV_SMOOTH, \ \ /* These brushes could handle dynamic topology, \ \ * but user feedback indicates it's better not to */ \ - SCULPT_TOOL_SMOOTH, \ SCULPT_TOOL_MASK) == 0) #define SCULPT_TOOL_HAS_TOPOLOGY_RAKE(t) \ (ELEM(t, /* These brushes, as currently coded, cannot support topology rake. */ \ SCULPT_TOOL_GRAB, \ + SCULPT_TOOL_ELASTIC_DEFORM, \ SCULPT_TOOL_ROTATE, \ - SCULPT_TOOL_THUMB, \ - SCULPT_TOOL_DRAW_SHARP, \ SCULPT_TOOL_DISPLACEMENT_ERASER, \ SCULPT_TOOL_SLIDE_RELAX, \ SCULPT_TOOL_MASK) == 0) @@ -632,8 +677,44 @@ typedef enum eBlurKernelType { typedef enum eBrushFalloffShape { PAINT_FALLOFF_SHAPE_SPHERE = 0, PAINT_FALLOFF_SHAPE_TUBE = 1, + PAINT_FALLOFF_NOOP = 2, } eBrushFalloffShape; +// dyntopo flags +// synced with PBVHTopologyUpdateMode +enum { + DYNTOPO_SUBDIVIDE = 1 << 0, + DYNTOPO_COLLAPSE = 1 << 1, + DYNTOPO_DISABLED = 1 << 2, + DYNTOPO_CLEANUP = 1 << 3, + DYNTOPO_LOCAL_COLLAPSE = 1 << 4, + DYNTOPO_LOCAL_SUBDIVIDE = 1 << 5 +}; + +// dyntopo override flags, copies all flags from dyntopo flags +enum { + DYNTOPO_INHERIT_ALL = 1 << 10, + DYNTOPO_INHERIT_DETAIL_RANGE = 1 << 11, + DYNTOPO_INHERIT_DETAIL_PERCENT = 1 << 12, + DYNTOPO_INHERIT_MODE = 1 << 13, + DYNTOPO_INHERIT_CONSTANT_DETAIL = 1 << 14, + DYNTOPO_INHERIT_SPACING = 1 << 15, + DYNTOPO_INHERIT_DETAIL_SIZE = 1 << 16, + DYNTOPO_INHERIT_RADIUS_SCALE = 1 << 17, + // make sure to update DYNTOPO_INHERIT_BITMASK when adding flags here +}; + +// represents all possible inherit flags +#define DYNTOPO_INHERIT_BITMASK ((1 << 18) - 1) + +// dyntopo mode +enum { + DYNTOPO_DETAIL_RELATIVE = 0, + DYNTOPO_DETAIL_MANUAL = 1, + DYNTOPO_DETAIL_BRUSH = 2, + DYNTOPO_DETAIL_CONSTANT = 3 +}; + typedef enum eBrushCurvesSculptFlag { BRUSH_CURVES_SCULPT_FLAG_SCALE_UNIFORM = (1 << 0), BRUSH_CURVES_SCULPT_FLAG_GROW_SHRINK_INVERT = (1 << 1), diff --git a/source/blender/makesdna/DNA_brush_types.h b/source/blender/makesdna/DNA_brush_types.h index fd72a58fe14..9da7c525942 100644 --- a/source/blender/makesdna/DNA_brush_types.h +++ b/source/blender/makesdna/DNA_brush_types.h @@ -145,6 +145,17 @@ typedef struct BrushGpencilSettings { struct Material *material_alt; } BrushGpencilSettings; +typedef struct DynTopoSettings { + float detail_range; + float detail_percent; + float detail_size; + float constant_detail; + short flag, mode; + int inherit; + int spacing; + float radius_scale; +} DynTopoSettings; + typedef struct BrushCurvesSculptSettings { /** Number of curves added by the add brush. */ int add_amount; @@ -176,6 +187,10 @@ typedef struct Brush { struct MTex mtex; struct MTex mask_mtex; + /** Pen Input curves */ + struct CurveMapping *pressure_size_curve; + struct CurveMapping *pressure_strength_curve; + struct Brush *toggle_brush; struct ImBuf *icon_imbuf; @@ -260,7 +275,7 @@ typedef struct Brush { /** Source for fill tool color gradient application. */ char gradient_fill_mode; - char _pad0[5]; + char _pad0[1]; /** Projection shape (sphere, circle). */ char falloff_shape; @@ -288,13 +303,24 @@ typedef struct Brush { char gpencil_weight_tool; /** Active curves sculpt tool (#eBrushCurvesSculptTool). */ char curves_sculpt_tool; - char _pad1[5]; + char _pad1[1]; float autosmooth_factor; + float autosmooth_radius_factor; + float autosmooth_projection; + int autosmooth_spacing; // spacing for BRUSH_CUSTOM_AUTOSMOOTH_SPACING + float boundary_smooth_factor; + float autosmooth_fset_slide; float tilt_strength_factor; float topology_rake_factor; + float topology_rake_radius_factor; + float topology_rake_projection; + int topology_rake_spacing; // spacing for BRUSH_CUSTOM_TOPOLOGY_RAKE_SPACING + + float vcol_boundary_factor; + float vcol_boundary_exponent; float crease_pinch_factor; @@ -350,6 +376,10 @@ typedef struct Brush { float cloth_constraint_softbody_strength; + /* array */ + int array_deform_type; + int array_count; + /* smooth */ int smooth_deform_type; float surface_smooth_shape_preservation; @@ -362,9 +392,14 @@ typedef struct Brush { /* smear */ int smear_deform_type; + float smear_deform_blend; + /* slide/relax */ int slide_deform_type; + /* scene_project */ + int scene_project_direction_type; + /* overlay */ int texture_overlay_alpha; int mask_overlay_alpha; @@ -389,7 +424,15 @@ typedef struct Brush { float mask_stencil_pos[2]; float mask_stencil_dimension[2]; + float concave_mask_factor; + + char _pad2[4]; struct BrushGpencilSettings *gpencil_settings; + + DynTopoSettings dyntopo, cached_dyntopo; + + /* new brush engine stuff */ + struct BrushCurvesSculptSettings *curves_sculpt_settings; int automasking_cavity_blur_steps; diff --git a/source/blender/makesdna/DNA_color_types.h b/source/blender/makesdna/DNA_color_types.h index d3b7c37ab0e..774a06bd14c 100644 --- a/source/blender/makesdna/DNA_color_types.h +++ b/source/blender/makesdna/DNA_color_types.h @@ -81,7 +81,8 @@ typedef struct CurveMapping { float sample[3]; short tone; - char _pad[6]; + char _pad[2]; + int cache_users; } CurveMapping; /** #CurveMapping.flag */ @@ -93,6 +94,8 @@ typedef enum eCurveMappingFlags { /** The curve is extended by extrapolation. When not set the curve is extended horizontally. */ CUMA_EXTEND_EXTRAPOLATE = (1 << 4), + CUMA_PART_OF_CACHE = (1 << 5), + CUMA_IS_FREED = (1 << 6) } eCurveMappingFlags; /** #CurveMapping.preset */ @@ -106,6 +109,9 @@ typedef enum eCurveMappingPreset { CURVE_PRESET_ROOT = 6, CURVE_PRESET_GAUSS = 7, CURVE_PRESET_BELL = 8, + CURVE_PRESET_POW2 = 9, + CURVE_PRESET_POW3 = 10, + CURVE_PRESET_POW15 = 11 } eCurveMappingPreset; /** #CurveMapping.tone */ diff --git a/source/blender/makesdna/DNA_customdata_types.h b/source/blender/makesdna/DNA_customdata_types.h index 3d5cd273eda..e3e9f8a76c3 100644 --- a/source/blender/makesdna/DNA_customdata_types.h +++ b/source/blender/makesdna/DNA_customdata_types.h @@ -53,6 +53,7 @@ typedef struct CustomDataLayer { * attribute was created. */ const AnonymousAttributeIDHandle *anonymous_id; + void *default_data; } CustomDataLayer; #define MAX_CUSTOMDATA_LAYER_NAME 68 @@ -76,8 +77,8 @@ typedef struct CustomData { * MUST be >= CD_NUMTYPES, but we can't use a define here. * Correct size is ensured in CustomData_update_typemap assert(). */ - int typemap[52]; - char _pad[4]; + int typemap[55]; + /** Number of layers, size of layers array. */ int totlayer, maxlayer; /** In editmode, total size of all data layers. */ @@ -86,6 +87,9 @@ typedef struct CustomData { struct BLI_mempool *pool; /** External file storing customdata layers. */ CustomDataExternal *external; + + /** for use with USE_BMESH_PAGE_CUSTOMDATA test, remove later*/ + void *bm_attrs, *_pad[1]; } CustomData; /** #CustomData.type */ @@ -162,8 +166,10 @@ typedef enum eCustomDataType { CD_PROP_BOOL = 50, CD_HAIRLENGTH = 51, - - CD_NUMTYPES = 52, + CD_MESH_ID = 52, + CD_DYNTOPO_VERT = 53, + CD_TOOLFLAGS = 54, + CD_NUMTYPES = 55, } eCustomDataType; /* Bits for eCustomDataMask */ @@ -214,7 +220,10 @@ typedef enum eCustomDataType { #define CD_MASK_PROP_BOOL (1ULL << CD_PROP_BOOL) #define CD_MASK_PROP_INT8 (1ULL << CD_PROP_INT8) +#define CD_MASK_DYNTOPO_VERT (1ULL << CD_DYNTOPO_VERT) +#define CD_MASK_MESH_ID (1ULL << CD_MESH_ID) #define CD_MASK_HAIRLENGTH (1ULL << CD_HAIRLENGTH) +#define CD_MASK_TOOLFLAGS (1ULL << CD_TOOLFLAGS) /** Multi-resolution loop data. */ #define CD_MASK_MULTIRES_GRIDS (CD_MASK_MDISPS | CD_GRID_PAINT_MASK) @@ -246,15 +255,18 @@ enum { /* Indicates layer should not be freed (for layers backed by external data) */ CD_FLAG_NOFREE = (1 << 1), /* Indicates the layer is only temporary, also implies no copy */ - CD_FLAG_TEMPORARY = ((1 << 2) | CD_FLAG_NOCOPY), + CD_FLAG_TEMPORARY = ((1 << 2)), // CD_FLAG_TEMPORARY no longer implies CD_FLAG_NOCOPY, this + // wasn't enforced for bmesh /* Indicates the layer is stored in an external file */ CD_FLAG_EXTERNAL = (1 << 3), /* Indicates external data is read into memory */ CD_FLAG_IN_MEMORY = (1 << 4), #ifdef DNA_DEPRECATED_ALLOW CD_FLAG_COLOR_ACTIVE = (1 << 5), - CD_FLAG_COLOR_RENDER = (1 << 6) + CD_FLAG_COLOR_RENDER = (1 << 6), #endif + CD_FLAG_ELEM_NOCOPY = (1 << 8), // disables CustomData_bmesh_copy_data. + CD_FLAG_ELEM_NOINTERP = (1 << 9), }; /* Limits */ diff --git a/source/blender/makesdna/DNA_mesh_types.h b/source/blender/makesdna/DNA_mesh_types.h index ef2f73ddffb..acbf23767cb 100644 --- a/source/blender/makesdna/DNA_mesh_types.h +++ b/source/blender/makesdna/DNA_mesh_types.h @@ -46,6 +46,11 @@ struct MFace; struct MLoopTri; struct Material; +typedef struct AttributeRef { + int domain, type; + char name[64]; +} AttributeRef; + typedef struct Mesh { DNA_DEFINE_CXX_METHODS(Mesh) @@ -339,12 +344,12 @@ enum { ME_FLAG_UNUSED_3 = 1 << 3, /* cleared */ ME_FLAG_UNUSED_4 = 1 << 4, /* cleared */ ME_AUTOSMOOTH = 1 << 5, - ME_FLAG_UNUSED_6 = 1 << 6, /* cleared */ - ME_FLAG_UNUSED_7 = 1 << 7, /* cleared */ + ME_SCULPT_IGNORE_UVS = 1 << 6, /* cleared */ + ME_REMESH_REPROJECT_MATERIALS = 1 << 7, ME_REMESH_REPROJECT_VERTEX_COLORS = 1 << 8, ME_DS_EXPAND = 1 << 9, ME_SCULPT_DYNAMIC_TOPOLOGY = 1 << 10, - ME_FLAG_UNUSED_8 = 1 << 11, /* cleared */ + ME_SCULPT_MIRROR_FSET_BOUNDARIES = 1 << 11, /* cleared */ ME_REMESH_REPROJECT_PAINT_MASK = 1 << 12, ME_REMESH_FIX_POLES = 1 << 13, ME_REMESH_REPROJECT_VOLUME = 1 << 14, diff --git a/source/blender/makesdna/DNA_meshdata_types.h b/source/blender/makesdna/DNA_meshdata_types.h index 4f748fe6828..060a379b58a 100644 --- a/source/blender/makesdna/DNA_meshdata_types.h +++ b/source/blender/makesdna/DNA_meshdata_types.h @@ -542,6 +542,47 @@ typedef struct MRecast { /** \} */ +typedef struct MSculptVert { + unsigned short valence; + + /* id of current stroke, used to detect + if vertex original data needs to be updated. + */ + short stroke_id; + + int flag; + + /**original coordinates*/ + float origco[3], origno[3]; + + /**original color*/ + float origcolor[4]; + + unsigned short origmask; + + /* curv is a fast curvature approximation used by dyntopo + adaptive curvature. */ + unsigned short curv; + + /* curvature_dir parallels a principle curvature direction */ + float curvature_dir[3]; +} MSculptVert; + +/* MSculptVert->flag */ +enum { + SCULPTVERT_VERT_FSET_HIDDEN = (1 << 1), + SCULPTVERT_NEED_TRIANGULATE = (1 << 4), + SCULPTVERT_NEED_DISK_SORT = (1 << 5), + SCULPTVERT_NEED_VALENCE = (1 << 6), + SCULPTVERT_SPLIT_TEMP = (1 << 13), + SCULPTVERT_PBVH_BOUNDARY = (1 << 14), +}; + +/* for internal bmesh toolflags api */ +typedef struct MToolFlags { + short *flag; +} MToolFlags; + #ifdef __cplusplus } #endif diff --git a/source/blender/makesdna/DNA_modifier_defaults.h b/source/blender/makesdna/DNA_modifier_defaults.h index 44644d7eb78..a9d1ec0b556 100644 --- a/source/blender/makesdna/DNA_modifier_defaults.h +++ b/source/blender/makesdna/DNA_modifier_defaults.h @@ -545,6 +545,12 @@ .projLimit = 0.0f, \ .projAxis = 0, \ .subsurfLevels = 0, \ + .optimizeNormalSteps = 8,\ + .optimizeNormals = 0,\ + .rayShrinkRatio = 1.0f,\ + .boundSmoothSteps = 8,\ + .boundSmoothScale = 3.0f,\ + .optimizeNormalsScale = 1.5f\ } #define _DNA_DEFAULT_SimpleDeformModifierData \ @@ -799,4 +805,23 @@ .mat_ofs = 0, \ } +#define _DNA_DEFAULT_BassReliefModifierData \ + { \ + .target = NULL, \ + .collection = NULL, \ + .vgroup_name = "", \ + .keepDist = 0.0f, \ + .shrinkOpts =\ + MOD_BASSRELIEF_CULL_TARGET_FRONTFACE|\ + MOD_BASSRELIEF_PROJECT_ALLOW_POS_DIR|\ + MOD_BASSRELIEF_OPTIMIZE, \ + .projLimit = 0.0f, \ + .projAxis = 0, \ + .optimizeSteps = 8,\ + .rayShrinkRatio = 1.0f,\ + .boundSmoothSteps = 8,\ + .boundSmoothFalloff = 3.0f,\ + .detailScale = 1.5f\ + } + /* clang-format off */ diff --git a/source/blender/makesdna/DNA_modifier_types.h b/source/blender/makesdna/DNA_modifier_types.h index 7af83f8f888..db353a0609c 100644 --- a/source/blender/makesdna/DNA_modifier_types.h +++ b/source/blender/makesdna/DNA_modifier_types.h @@ -84,6 +84,7 @@ typedef enum ModifierType { eModifierType_MeshToVolume = 58, eModifierType_VolumeDisplace = 59, eModifierType_VolumeToMesh = 60, + eModifierType_BassRelief = 61, NUM_MODIFIER_TYPES, } ModifierType; @@ -1162,7 +1163,16 @@ typedef struct ShrinkwrapModifierData { */ char subsurfLevels; - char _pad[2]; + char optimizeNormalSteps; + char optimizeNormals; + + float rayShrinkRatio; + + char boundSmoothSteps; + char _pad2[3]; + + float boundSmoothScale; /* width of smoothed boundary is divided by this */ + float optimizeNormalsScale; } ShrinkwrapModifierData; /** #ShrinkwrapModifierData.shrinkType */ @@ -1220,6 +1230,65 @@ enum { MOD_SHRINKWRAP_PROJECT_OVER_Z_AXIS = (1 << 2), }; +typedef struct BassReliefModifierData { + ModifierData modifier; + + /** Shrink target. */ + struct Object *target; + /** Shrink target collection */ + struct Collection *collection; + + /** Optional vertexgroup name, MAX_VGROUP_NAME. */ + char vgroup_name[64]; + /** Distance offset to keep from mesh/projection point. */ + float keepDist; + + /** Shrink options. */ + char shrinkOpts; + /** Axis to project over. */ + char projAxis; + + char optimizeSteps; + char boundSmoothSteps; + + float boundSmoothFalloff; /* width of smoothed boundary is divided by this */ + float detailScale; + + /** Limit the projection ray cast. */ + float projLimit; + float rayShrinkRatio; +} BassReliefModifierData; + +/* BassReliefModifier->shrinkOpts */ +enum { + /** allow shrinkwrap to move the vertex in the positive direction of axis */ + MOD_BASSRELIEF_PROJECT_ALLOW_POS_DIR = (1 << 0), + /** allow shrinkwrap to move the vertex in the negative direction of axis */ + MOD_BASSRELIEF_PROJECT_ALLOW_NEG_DIR = (1 << 1), + + /** ignore vertex moves if a vertex ends projected on a front face of the target */ + MOD_BASSRELIEF_CULL_TARGET_FRONTFACE = (1 << 3), + /** ignore vertex moves if a vertex ends projected on a back face of the target */ + MOD_BASSRELIEF_CULL_TARGET_BACKFACE = (1 << 4), + + MOD_BASSRELIEF_INVERT_VGROUP = (1 << 5), + MOD_BASSRELIEF_INVERT_CULL_TARGET = (1 << 6), + + MOD_BASSRELIEF_OPTIMIZE = (1 << 7) +}; + +#define MOD_BASSRELIEF_CULL_TARGET_MASK \ + (MOD_BASSRELIEF_CULL_TARGET_FRONTFACE | MOD_BASSRELIEF_CULL_TARGET_BACKFACE) + +/* Shrinkwrap->projAxis */ +enum { + /** projection over normal is used if no axis is selected */ + MOD_BASSRELIEF_PROJECT_OVER_NORMAL = 0, + MOD_BASSRELIEF_PROJECT_OVER_X_AXIS = (1 << 0), + MOD_BASSRELIEF_PROJECT_OVER_Y_AXIS = (1 << 1), + MOD_BASSRELIEF_PROJECT_OVER_Z_AXIS = (1 << 2), +}; + typedef struct SimpleDeformModifierData { ModifierData modifier; @@ -1902,10 +1971,10 @@ typedef struct CorrectiveSmoothModifierData { /* NOTE: -1 is used to bind. */ unsigned int bind_coords_num; - float lambda, scale; + float lambda, scale, projection; short repeat, flag; char smooth_type, rest_source; - char _pad[6]; + char _pad[2]; /** #MAX_VGROUP_NAME. */ char defgrp_name[64]; @@ -1960,9 +2029,7 @@ typedef struct UVWarpModifierData { } UVWarpModifierData; /** #UVWarpModifierData.flag */ -enum { - MOD_UVWARP_INVERT_VGROUP = 1 << 0, -}; +enum { MOD_UVWARP_INVERT_VGROUP = 1 << 0, MOD_UVWARP_RESTRICT_ISLANDS = 1 << 1 }; /** Mesh cache modifier. */ typedef struct MeshCacheModifierData { diff --git a/source/blender/makesdna/DNA_object_types.h b/source/blender/makesdna/DNA_object_types.h index 889f154c1b8..d2ac38eac49 100644 --- a/source/blender/makesdna/DNA_object_types.h +++ b/source/blender/makesdna/DNA_object_types.h @@ -37,6 +37,7 @@ struct LightgroupMembership; struct Material; struct Mesh; struct Object; +struct PBVH; struct PartDeflect; struct Path; struct RigidBodyOb; @@ -218,6 +219,9 @@ typedef struct Object_Runtime { int crazyspace_verts_num; int _pad3[3]; + + struct PBVH *cached_pbvh; + void *_pad5; } Object_Runtime; typedef struct ObjectLineArt { @@ -453,8 +457,11 @@ typedef struct Object { /** Lightgroup membership information. */ struct LightgroupMembership *lightgroup; + struct PBVH *cached_pbvh; + /** Runtime evaluation data (keep last). */ Object_Runtime runtime; + void *_pad9; } Object; /** DEPRECATED: this is not used anymore because hooks are now modifiers. */ diff --git a/source/blender/makesdna/DNA_scene_defaults.h b/source/blender/makesdna/DNA_scene_defaults.h index 6cc01d254ce..f8fb9dbed1c 100644 --- a/source/blender/makesdna/DNA_scene_defaults.h +++ b/source/blender/makesdna/DNA_scene_defaults.h @@ -276,7 +276,7 @@ .unprojected_radius = 0.29, \ .alpha = 0.5f, \ .weight = 0.5f, \ - .flag = UNIFIED_PAINT_SIZE | UNIFIED_PAINT_ALPHA, \ + .flag = UNIFIED_PAINT_SIZE | UNIFIED_PAINT_ALPHA | UNIFIED_PAINT_FLAG_HARD_EDGE_MODE, \ } #define _DNA_DEFAULTS_ParticleEditSettings \ diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h index 608bf6b3bf7..71ef3e71344 100644 --- a/source/blender/makesdna/DNA_scene_types.h +++ b/source/blender/makesdna/DNA_scene_types.h @@ -923,6 +923,7 @@ typedef struct PaintToolSlot { /** Paint Tool Base. */ typedef struct Paint { struct Brush *brush; + struct Brush *brush_eval; /** * Each tool has its own active brush, @@ -1052,6 +1053,7 @@ typedef struct ParticleEditSettings { /** \name Sculpt * \{ */ +/* ------------------------------------------- */ /** Sculpt. */ typedef struct Sculpt { Paint paint; @@ -1062,6 +1064,11 @@ typedef struct Sculpt { /** Transform tool. */ int transform_mode; + int transform_deform_target; + + /* Factor to tweak the stregtn of alt-smoothing. */ + float smooth_strength_factor; + char _pad0[4]; int automasking_flags; @@ -1071,6 +1078,7 @@ typedef struct Sculpt { /** Maximum edge length for dynamic topology sculpting (in pixels). */ float detail_size; + float detail_range; /** Direction used for `SCULPT_OT_symmetrize` operator. */ int symmetrize_direction; @@ -1082,7 +1090,9 @@ typedef struct Sculpt { /** Constant detail resolution (Blender unit / constant_detail). */ float constant_detail; float detail_percent; + int dyntopo_spacing; + float dyntopo_radius_scale; int automasking_cavity_blur_steps; float automasking_cavity_factor; char _pad[4]; @@ -1363,6 +1373,10 @@ typedef struct UnifiedPaintSettings { */ float pixel_radius; float initial_pixel_radius; + + char _pad[7]; + char hard_edge_mode; + float start_pixel_radius; /** Drawing pressure. */ @@ -1387,9 +1401,9 @@ typedef enum { /** Only used if unified size is enabled, mirrors the brush flag #BRUSH_LOCK_SIZE. */ UNIFIED_PAINT_BRUSH_LOCK_SIZE = (1 << 2), - UNIFIED_PAINT_FLAG_UNUSED_0 = (1 << 3), + UNIFIED_PAINT_FLAG_HARD_EDGE_MODE = (1 << 3), - UNIFIED_PAINT_FLAG_UNUSED_1 = (1 << 4), + UNIFIED_PAINT_FLAG_UI_ADVANCED = (1 << 4), } eUnifiedPaintSettingsFlags; typedef struct CurvePaintSettings { @@ -1682,7 +1696,9 @@ typedef struct ToolSettings { /** Normal Editing. */ float normal_vector[3]; - char _pad6[4]; + char save_temp_layers, show_origco; + + char _pad6[2]; /** * Custom Curve Profile for bevel tool: @@ -2426,6 +2442,7 @@ typedef enum ePaintFlags { PAINT_SHOW_BRUSH_ON_SURFACE = (1 << 2), PAINT_USE_CAVITY_MASK = (1 << 3), PAINT_SCULPT_DELAY_UPDATES = (1 << 4), + PAINT_SCULPT_SHOW_PIVOT = (1 << 5), } ePaintFlags; /** @@ -2483,8 +2500,20 @@ typedef enum eSculptFlags { /** If set, dynamic-topology detail size will be constant in object space. */ SCULPT_DYNTOPO_DETAIL_CONSTANT = (1 << 13), SCULPT_DYNTOPO_DETAIL_BRUSH = (1 << 14), - /* unused = (1 << 15), */ + /* Don't display mask in viewport, but still use it for strokes. */ + SCULPT_HIDE_MASK = (1 << 15), SCULPT_DYNTOPO_DETAIL_MANUAL = (1 << 16), + + /* Don't display face sets in viewport. */ + SCULPT_HIDE_FACE_SETS = (1 << 17), + SCULPT_DYNTOPO_FLAT_VCOL_SHADING = (1 << 18), + SCULPT_DYNTOPO_CLEANUP = (1 << 19), + + // hides facesets/masks and forces indexed mode to save GPU bandwidth + SCULPT_FAST_DRAW = (1 << 20), + SCULPT_DYNTOPO_LOCAL_SUBDIVIDE = (1 << 21), + SCULPT_DYNTOPO_LOCAL_COLLAPSE = (1 << 22), + SCULPT_DYNTOPO_ENABLED = (1 << 23), } eSculptFlags; /** #Sculpt.transform_mode */ @@ -2493,6 +2522,12 @@ typedef enum eSculptTransformMode { SCULPT_TRANSFORM_MODE_RADIUS_ELASTIC = 1, } eSculptTrasnformMode; +/** #Sculpt.transform_deform_target */ +typedef enum eSculptTransformDeformTarget { + SCULPT_TRANSFORM_DEFORM_TARGET_GEOMETRY = 0, + SCULPT_TRANSFORM_DEFORM_TARGET_CLOTH_SIM = 1, +} eSculptTransformDeformTarget; + /** #PaintModeSettings.mode */ typedef enum ePaintCanvasSource { /** Paint on the active node of the active material slot. */ diff --git a/source/blender/makesdna/DNA_space_types.h b/source/blender/makesdna/DNA_space_types.h index 54edf795e6c..a319fd187e8 100644 --- a/source/blender/makesdna/DNA_space_types.h +++ b/source/blender/makesdna/DNA_space_types.h @@ -229,6 +229,7 @@ typedef enum eSpaceButtons_Context { BCONTEXT_SHADERFX = 15, BCONTEXT_OUTPUT = 16, BCONTEXT_COLLECTION = 17, + BCONTEXT_BRUSH_EDITOR = 18, /* Keep last. */ BCONTEXT_TOT, diff --git a/source/blender/makesdna/DNA_texture_types.h b/source/blender/makesdna/DNA_texture_types.h index 4b3daf92d8b..e51adc48351 100644 --- a/source/blender/makesdna/DNA_texture_types.h +++ b/source/blender/makesdna/DNA_texture_types.h @@ -439,6 +439,7 @@ typedef struct ColorMapping { #define MTEX_MAP_MODE_AREA 3 #define MTEX_MAP_MODE_RANDOM 4 #define MTEX_MAP_MODE_STENCIL 5 +#define MTEX_MAP_MODE_ROLL 6 /* brush_angle_mode */ #define MTEX_ANGLE_RANDOM 1 diff --git a/source/blender/makesdna/DNA_userdef_types.h b/source/blender/makesdna/DNA_userdef_types.h index b48c58ee752..79ab8bab209 100644 --- a/source/blender/makesdna/DNA_userdef_types.h +++ b/source/blender/makesdna/DNA_userdef_types.h @@ -650,11 +650,18 @@ typedef struct UserDef_Experimental { char use_sculpt_tools_tilt; char use_extended_asset_browser; char use_override_templates; + + char use_sculpt_uvsmooth; + + char _pad[6]; + + char use_named_attribute_nodes; + char enable_eevee_next; char use_sculpt_texture_paint; char enable_workbench_next; char use_new_volume_nodes; - char _pad[6]; + char _pad2[6]; /** `makesdna` does not allow empty structs. */ } UserDef_Experimental; @@ -950,6 +957,7 @@ typedef struct UserDef { UserDef_Experimental experimental; + char _pad15[8]; /** Runtime data (keep last). */ UserDef_Runtime runtime; } UserDef; diff --git a/source/blender/makesdna/DNA_view3d_defaults.h b/source/blender/makesdna/DNA_view3d_defaults.h index 0c0ed210a2b..465abfdae74 100644 --- a/source/blender/makesdna/DNA_view3d_defaults.h +++ b/source/blender/makesdna/DNA_view3d_defaults.h @@ -50,6 +50,7 @@ * we typically want to see shading too. */ \ .sculpt_mode_mask_opacity = 0.75f, \ .sculpt_mode_face_sets_opacity = 1.0f, \ + .sculpt_mode_face_sets_moire_scale = 0.45f,\ \ .edit_flag = V3D_OVERLAY_EDIT_FACES | V3D_OVERLAY_EDIT_SEAMS | \ V3D_OVERLAY_EDIT_SHARP | V3D_OVERLAY_EDIT_FREESTYLE_EDGE | \ diff --git a/source/blender/makesdna/DNA_view3d_types.h b/source/blender/makesdna/DNA_view3d_types.h index 4c5ba4c43f8..39921d86668 100644 --- a/source/blender/makesdna/DNA_view3d_types.h +++ b/source/blender/makesdna/DNA_view3d_types.h @@ -209,12 +209,17 @@ typedef struct View3DOverlay { float weight_paint_mode_opacity; float sculpt_mode_mask_opacity; float sculpt_mode_face_sets_opacity; + float sculpt_mode_face_sets_moire_seed; + float sculpt_mode_face_sets_moire_scale; + + /** Sculpt mode settings*/ + int sculpt_flag; float viewer_attribute_opacity; /** Armature edit/pose mode settings. */ float xray_alpha_bone; float bone_wire_alpha; - char _pad1[4]; + char _pad1[8]; /** Darken Inactive. */ float fade_alpha; @@ -595,6 +600,11 @@ enum { V3D_OVERLAY_WPAINT_CONTOURS = (1 << 0), }; +/** #View3DOverlay.sculpt_flag */ +enum { + V3D_OVERLAY_SCULPT_FSET_MOIRE = (1 << 0), +}; + /** #View3D.around */ enum { /* center of the bounding box */ diff --git a/source/blender/makesdna/intern/dna_defaults.c b/source/blender/makesdna/intern/dna_defaults.c index 309756ff2da..7a3282ea291 100644 --- a/source/blender/makesdna/intern/dna_defaults.c +++ b/source/blender/makesdna/intern/dna_defaults.c @@ -234,6 +234,7 @@ SDNA_DEFAULT_DECL_STRUCT(World); /* DNA_modifier_defaults.h */ SDNA_DEFAULT_DECL_STRUCT(ArmatureModifierData); SDNA_DEFAULT_DECL_STRUCT(ArrayModifierData); +SDNA_DEFAULT_DECL_STRUCT(BassReliefModifierData); SDNA_DEFAULT_DECL_STRUCT(BevelModifierData); SDNA_DEFAULT_DECL_STRUCT(BooleanModifierData); SDNA_DEFAULT_DECL_STRUCT(BuildModifierData); @@ -476,6 +477,7 @@ const void *DNA_default_table[SDNA_TYPE_MAX] = { /* DNA_modifier_defaults.h */ SDNA_DEFAULT_DECL(ArmatureModifierData), SDNA_DEFAULT_DECL(ArrayModifierData), + SDNA_DEFAULT_DECL(BassReliefModifierData), SDNA_DEFAULT_DECL(BevelModifierData), SDNA_DEFAULT_DECL(BooleanModifierData), SDNA_DEFAULT_DECL(BuildModifierData), diff --git a/source/blender/makesrna/intern/rna_access_compare_override.c b/source/blender/makesrna/intern/rna_access_compare_override.c index af13171f202..e95d361d55e 100644 --- a/source/blender/makesrna/intern/rna_access_compare_override.c +++ b/source/blender/makesrna/intern/rna_access_compare_override.c @@ -613,6 +613,7 @@ static bool rna_property_override_operation_apply(Main *bmain, return success; } +#include "stdio.h" bool RNA_struct_override_matches(Main *bmain, PointerRNA *ptr_local, PointerRNA *ptr_reference, diff --git a/source/blender/makesrna/intern/rna_attribute.c b/source/blender/makesrna/intern/rna_attribute.c index 13d3e08c462..068b43fe186 100644 --- a/source/blender/makesrna/intern/rna_attribute.c +++ b/source/blender/makesrna/intern/rna_attribute.c @@ -514,14 +514,12 @@ PointerRNA rna_AttributeGroup_color_iterator_get(CollectionPropertyIterator *ite int rna_AttributeGroup_color_length(PointerRNA *ptr) { - return BKE_id_attributes_length(ptr->owner_id, - ATTR_DOMAIN_MASK_POINT | ATTR_DOMAIN_MASK_CORNER, - CD_MASK_PROP_COLOR | CD_MASK_PROP_BYTE_COLOR); + return BKE_id_attributes_length(ptr->owner_id, ATTR_DOMAIN_MASK_COLOR, CD_MASK_COLOR_ALL, true); } int rna_AttributeGroup_length(PointerRNA *ptr) { - return BKE_id_attributes_length(ptr->owner_id, ATTR_DOMAIN_MASK_ALL, CD_MASK_PROP_ALL); + return BKE_id_attributes_length(ptr->owner_id, ATTR_DOMAIN_MASK_ALL, CD_MASK_PROP_ALL, false); } static int rna_AttributeGroup_active_index_get(PointerRNA *ptr) @@ -557,7 +555,7 @@ static void rna_AttributeGroup_active_index_range( PointerRNA *ptr, int *min, int *max, int *softmin, int *softmax) { *min = 0; - *max = BKE_id_attributes_length(ptr->owner_id, ATTR_DOMAIN_MASK_ALL, CD_MASK_PROP_ALL); + *max = BKE_id_attributes_length(ptr->owner_id, ATTR_DOMAIN_MASK_ALL, CD_MASK_PROP_ALL, false); *softmin = *min; *softmax = *max; @@ -619,7 +617,7 @@ static void rna_AttributeGroup_active_color_index_range( PointerRNA *ptr, int *min, int *max, int *softmin, int *softmax) { *min = 0; - *max = BKE_id_attributes_length(ptr->owner_id, ATTR_DOMAIN_MASK_COLOR, CD_MASK_COLOR_ALL); + *max = BKE_id_attributes_length(ptr->owner_id, ATTR_DOMAIN_MASK_COLOR, CD_MASK_COLOR_ALL, true); *softmin = *min; *softmax = *max; @@ -664,7 +662,7 @@ static void rna_AttributeGroup_render_color_index_range( PointerRNA *ptr, int *min, int *max, int *softmin, int *softmax) { *min = 0; - *max = BKE_id_attributes_length(ptr->owner_id, ATTR_DOMAIN_MASK_COLOR, CD_MASK_COLOR_ALL); + *max = BKE_id_attributes_length(ptr->owner_id, ATTR_DOMAIN_MASK_COLOR, CD_MASK_COLOR_ALL, true); *softmin = *min; *softmax = *max; diff --git a/source/blender/makesrna/intern/rna_brush.c b/source/blender/makesrna/intern/rna_brush.c index 8e6908841a6..08c8971b3ae 100644 --- a/source/blender/makesrna/intern/rna_brush.c +++ b/source/blender/makesrna/intern/rna_brush.c @@ -77,6 +77,7 @@ static const EnumPropertyItem rna_enum_brush_texture_slot_map_all_mode_items[] = {MTEX_MAP_MODE_3D, "3D", 0, "3D", ""}, {MTEX_MAP_MODE_RANDOM, "RANDOM", 0, "Random", ""}, {MTEX_MAP_MODE_STENCIL, "STENCIL", 0, "Stencil", ""}, + {MTEX_MAP_MODE_ROLL, "ROLL", 0, "Roll", ""}, {0, NULL, 0, NULL, NULL}, }; @@ -117,6 +118,7 @@ const EnumPropertyItem rna_enum_brush_sculpt_tool_items[] = { {SCULPT_TOOL_CREASE, "CREASE", ICON_BRUSH_CREASE, "Crease", ""}, RNA_ENUM_ITEM_SEPR, {SCULPT_TOOL_SMOOTH, "SMOOTH", ICON_BRUSH_SMOOTH, "Smooth", ""}, + {SCULPT_TOOL_ENHANCE_DETAILS, "ENHANCE_DETAILS", ICON_BRUSH_SCULPT_DRAW, "Enhance Details", ""}, {SCULPT_TOOL_FLATTEN, "FLATTEN", ICON_BRUSH_FLATTEN, "Flatten", ""}, {SCULPT_TOOL_FILL, "FILL", ICON_BRUSH_FILL, "Fill", ""}, {SCULPT_TOOL_SCRAPE, "SCRAPE", ICON_BRUSH_SCRAPE, "Scrape", ""}, @@ -132,6 +134,7 @@ const EnumPropertyItem rna_enum_brush_sculpt_tool_items[] = { {SCULPT_TOOL_ROTATE, "ROTATE", ICON_BRUSH_ROTATE, "Rotate", ""}, {SCULPT_TOOL_SLIDE_RELAX, "TOPOLOGY", ICON_BRUSH_GRAB, "Slide Relax", ""}, {SCULPT_TOOL_BOUNDARY, "BOUNDARY", ICON_BRUSH_GRAB, "Boundary", ""}, + {SCULPT_TOOL_TWIST, "TWIST", ICON_BRUSH_GRAB, "Twist", ""}, RNA_ENUM_ITEM_SEPR, {SCULPT_TOOL_CLOTH, "CLOTH", ICON_BRUSH_SCULPT_DRAW, "Cloth", ""}, {SCULPT_TOOL_SIMPLIFY, "SIMPLIFY", ICON_BRUSH_DATA, "Simplify", ""}, @@ -139,8 +142,16 @@ const EnumPropertyItem rna_enum_brush_sculpt_tool_items[] = { {SCULPT_TOOL_DRAW_FACE_SETS, "DRAW_FACE_SETS", ICON_BRUSH_MASK, "Draw Face Sets", ""}, {SCULPT_TOOL_DISPLACEMENT_ERASER, "DISPLACEMENT_ERASER", ICON_BRUSH_SCULPT_DRAW, "Multires Displacement Eraser", ""}, {SCULPT_TOOL_DISPLACEMENT_SMEAR, "DISPLACEMENT_SMEAR", ICON_BRUSH_SCULPT_DRAW, "Multires Displacement Smear", ""}, + {SCULPT_TOOL_PAINT, "PAINT", ICON_BRUSH_SCULPT_DRAW, "Paint", ""}, {SCULPT_TOOL_SMEAR, "SMEAR", ICON_BRUSH_SCULPT_DRAW, "Smear", ""}, + {SCULPT_TOOL_FAIRING, "FAIRING", ICON_BRUSH_MASK, "Fairing", ""}, + {SCULPT_TOOL_SCENE_PROJECT, "SCENE_PROJECT", ICON_BRUSH_MASK, "Scene Project", ""}, + {SCULPT_TOOL_SYMMETRIZE, "SYMMETRIZE", ICON_BRUSH_SCULPT_DRAW, "Symmetrize", ""}, + {SCULPT_TOOL_ARRAY, "ARRAY", ICON_BRUSH_SCULPT_DRAW, "Array", ""}, + {SCULPT_TOOL_VCOL_BOUNDARY, "VCOL_BOUNDARY", ICON_BRUSH_SCULPT_DRAW, "Sharpen Color Boundary", ""}, + {SCULPT_TOOL_UV_SMOOTH, "UV_SMOOTH", ICON_BRUSH_GRAB, "UV Smooth", ""}, + {SCULPT_TOOL_DISPLACEMENT_HEAL, "DISPLACEMENT_HEAL", ICON_BRUSH_GRAB, "Displacement Heal", ""}, {0, NULL, 0, NULL, NULL}, }; /* clang-format on */ @@ -366,6 +377,42 @@ static EnumPropertyItem rna_enum_gpencil_brush_vertex_icons_items[] = { {GP_BRUSH_ICON_VERTEX_REPLACE, "REPLACE", ICON_BRUSH_MIX, "Replace", ""}, {0, NULL, 0, NULL, NULL}, }; + +static EnumPropertyItem rna_enum_brush_dyntopo_mode[] = { + {DYNTOPO_DETAIL_RELATIVE, "RELATIVE", ICON_NONE, "Relative", ""}, + {DYNTOPO_DETAIL_CONSTANT, "CONSTANT", ICON_NONE, "Constant", ""}, + {DYNTOPO_DETAIL_MANUAL, "MANUAL", ICON_NONE, "Manual", ""}, + {DYNTOPO_DETAIL_BRUSH, "BRUSH", ICON_NONE, "Brush", ""}, + {0, NULL, 0, NULL, NULL}, +}; + +static EnumPropertyItem rna_enum_brush_dyntopo_flag[] = { + {DYNTOPO_SUBDIVIDE, "SUBDIVIDE", ICON_NONE, "Subdivide", ""}, + {DYNTOPO_COLLAPSE, "COLLAPSE", ICON_NONE, "Collapse", ""}, + {DYNTOPO_DISABLED, "DISABLED", ICON_NONE, "Disable", ""}, + {DYNTOPO_LOCAL_COLLAPSE, "LOCAL_COLLAPSE", ICON_NONE, "Local Collapse", ""}, + {DYNTOPO_LOCAL_SUBDIVIDE, "LOCAL_SUBDIVIDE", ICON_NONE, "Local Subdivide", ""}, + {0, NULL, 0, NULL, NULL}, +}; + +static EnumPropertyItem rna_enum_brush_dyntopo_inherit[] = { + {DYNTOPO_SUBDIVIDE, "SUBDIVIDE", ICON_NONE, "Subdivide", ""}, + {DYNTOPO_COLLAPSE, "COLLAPSE", ICON_NONE, "Collapse", ""}, + {DYNTOPO_DISABLED, "DISABLED", ICON_NONE, "Disable", ""}, + {DYNTOPO_LOCAL_COLLAPSE, "LOCAL_COLLAPSE", ICON_NONE, "Local Collapse", ""}, + {DYNTOPO_LOCAL_SUBDIVIDE, "LOCAL_SUBDIVIDE", ICON_NONE, "Local Subdivide", ""}, + {DYNTOPO_INHERIT_ALL, "ALL", ICON_NONE, "All", "Inherit All"}, + {DYNTOPO_INHERIT_DETAIL_RANGE, "DETAIL_RANGE", ICON_NONE, "All", ""}, + {DYNTOPO_INHERIT_DETAIL_PERCENT, "DETAIL_PERCENT", ICON_NONE, "Percent", ""}, + {DYNTOPO_INHERIT_MODE, "MODE", ICON_NONE, "Mode", ""}, + {DYNTOPO_INHERIT_CONSTANT_DETAIL, "CONSTANT_DETAIL", ICON_NONE, "Constant Detail", ""}, + {DYNTOPO_INHERIT_SPACING, "SPACING", ICON_NONE, "Spacing", ""}, + {DYNTOPO_CLEANUP, "CLEANUP", ICON_NONE, "Cleanup", ""}, + {DYNTOPO_INHERIT_DETAIL_SIZE, "DETAIL_SIZE", ICON_NONE, "Detail Size", ""}, + {DYNTOPO_INHERIT_RADIUS_SCALE, "RADIUS_SCALE", ICON_NONE, "Radius Scale", ""}, + {0, NULL, 0, NULL, NULL}, +}; + #endif #ifdef RNA_RUNTIME @@ -395,6 +442,12 @@ static bool rna_BrushCapabilitiesSculpt_has_topology_rake_get(PointerRNA *ptr) return SCULPT_TOOL_HAS_TOPOLOGY_RAKE(br->sculpt_tool); } +static bool rna_BrushCapabilitiesSculpt_has_vcol_boundary_smooth_get(PointerRNA *ptr) +{ + Brush *br = (Brush *)ptr->data; + return SCULPT_TOOL_HAS_VCOL_BOUNDARY_SMOOTH(br->sculpt_tool); +} + static bool rna_BrushCapabilitiesSculpt_has_auto_smooth_get(PointerRNA *ptr) { Brush *br = (Brush *)ptr->data; @@ -586,6 +639,7 @@ static bool rna_BrushCapabilitiesSculpt_has_direction_get(PointerRNA *ptr) SCULPT_TOOL_DRAW_SHARP, SCULPT_TOOL_CLAY, SCULPT_TOOL_CLAY_STRIPS, + SCULPT_TOOL_TWIST, SCULPT_TOOL_SMOOTH, SCULPT_TOOL_LAYER, SCULPT_TOOL_INFLATE, @@ -696,6 +750,11 @@ static void rna_Brush_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerR // WM_main_add_notifier(NC_SPACE | ND_SPACE_VIEW3D, NULL); } +static void rna_Brush_dyntopo_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr) +{ + // Brush *br = (Brush *)ptr->data; +} + static void rna_Brush_material_update(bContext *UNUSED(C), PointerRNA *UNUSED(ptr)) { /* number of material users changed */ @@ -784,6 +843,7 @@ static void rna_Brush_set_size(PointerRNA *ptr, int value) /* scale unprojected radius so it stays consistent with brush size */ BKE_brush_scale_unprojected_radius(&brush->unprojected_radius, value, brush->size); + brush->size = value; } @@ -869,6 +929,7 @@ static const EnumPropertyItem *rna_Brush_direction_itemf(bContext *C, case SCULPT_TOOL_LAYER: case SCULPT_TOOL_CLAY: case SCULPT_TOOL_CLAY_STRIPS: + case SCULPT_TOOL_TWIST: return prop_direction_items; case SCULPT_TOOL_SMOOTH: return prop_smooth_direction_items; @@ -1199,6 +1260,124 @@ static void rna_def_brush_texture_slot(BlenderRNA *brna) TEXTURE_CAPABILITY(has_texture_angle, "Has Texture Angle Source"); } +static void rna_def_dyntopo_settings(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, "DynTopoSettings", NULL); + RNA_def_struct_sdna(srna, "DynTopoSettings"); + RNA_def_struct_ui_text(srna, "Dyntopo Settings", ""); + + prop = RNA_def_property(srna, "spacing", PROP_INT, PROP_PERCENTAGE); + RNA_def_property_int_sdna(prop, NULL, "spacing"); + RNA_def_property_range(prop, 1, 1000); + RNA_def_property_ui_range(prop, 1, 500, 5, -1); + RNA_def_property_ui_text( + prop, "Spacing", "Spacing between DynTopo daubs as a percentage of brush diameter"); + RNA_def_property_update(prop, 0, "rna_Brush_dyntopo_update"); + + prop = RNA_def_property(srna, "detail_percent", PROP_FLOAT, PROP_PERCENTAGE); + RNA_def_property_float_sdna(prop, NULL, "detail_percent"); + RNA_def_property_range(prop, 1, 1000); + RNA_def_property_ui_range(prop, 1, 500, 5, -1); + RNA_def_property_ui_text(prop, "Detail Percent", ""); + RNA_def_property_update(prop, 0, "rna_Brush_dyntopo_update"); + + prop = RNA_def_property(srna, "detail_size", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "detail_size"); + RNA_def_property_range(prop, 0.0, 100.0); + RNA_def_property_ui_range(prop, 0.0, 50.0, 0.1, 4); + RNA_def_property_ui_text(prop, "Detail Size", ""); + RNA_def_property_update(prop, 0, "rna_Brush_dyntopo_update"); + + prop = RNA_def_property(srna, "detail_range", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "detail_range"); + RNA_def_property_range(prop, 0.0, 1.0); + RNA_def_property_ui_range(prop, 0.0, 1.0, 0.001, 4); + RNA_def_property_ui_text( + prop, "Detail Range", "Higher values are faster but produce lower quality topology"); + RNA_def_property_update(prop, 0, "rna_Brush_dyntopo_update"); + + prop = RNA_def_property(srna, "constant_detail", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "constant_detail"); + RNA_def_property_range(prop, 0.0001, FLT_MAX); + RNA_def_property_ui_range(prop, 0.001, 1000.0, 10, 2); + RNA_def_property_ui_text(prop, + "Resolution", + "Maximum edge length for dynamic topology sculpting (as divisor " + "of blender unit - higher value means smaller edge length)"); + RNA_def_property_update(prop, 0, "rna_Brush_dyntopo_update"); + + prop = RNA_def_property(srna, "subdivide", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", DYNTOPO_SUBDIVIDE); + RNA_def_property_ui_icon(prop, ICON_NONE, 0); + RNA_def_property_ui_text(prop, "Subdivide", "Enable Dyntopo Subdivision"); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_update(prop, 0, "rna_Brush_dyntopo_update"); + + prop = RNA_def_property(srna, "disabled", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", DYNTOPO_DISABLED); + RNA_def_property_ui_icon(prop, ICON_NONE, 0); + RNA_def_property_ui_text(prop, "Disable", "Disable Dyntopo for this brush"); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_update(prop, 0, "rna_Brush_dyntopo_update"); + + prop = RNA_def_property(srna, "cleanup", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", DYNTOPO_CLEANUP); + RNA_def_property_ui_icon(prop, ICON_NONE, 0); + RNA_def_property_ui_text(prop, "Cleanup", "Dissolve Verts With Only 3 or 4 faces"); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_update(prop, 0, "rna_Brush_dyntopo_update"); + + prop = RNA_def_property(srna, "collapse", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", DYNTOPO_COLLAPSE); + RNA_def_property_ui_icon(prop, ICON_NONE, 0); + RNA_def_property_ui_text(prop, "Collapse", "Enable Dyntopo Decimation"); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_update(prop, 0, "rna_Brush_dyntopo_update"); + + prop = RNA_def_property(srna, "local_collapse", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", DYNTOPO_LOCAL_COLLAPSE); + RNA_def_property_ui_icon(prop, ICON_NONE, 0); + RNA_def_property_ui_text( + prop, + "Local Collapse", + "When collapse is disabled, collapse anyway based on local edge lengths under brush"); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_update(prop, 0, "rna_Brush_dyntopo_update"); + + prop = RNA_def_property(srna, "local_subdivide", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", DYNTOPO_LOCAL_SUBDIVIDE); + RNA_def_property_ui_icon(prop, ICON_NONE, 0); + RNA_def_property_ui_text( + prop, + "Local Subdivide", + "When subdivide is disabled, subdivide anyway based on local edge lengths under brush"); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_update(prop, 0, "rna_Brush_dyntopo_update"); + + prop = RNA_def_property(srna, "mode", PROP_ENUM, 0); + RNA_def_property_enum_sdna(prop, NULL, "mode"); + RNA_def_property_enum_items(prop, rna_enum_brush_dyntopo_mode); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_ui_text(prop, "Mode", "Detail Mode"); + + prop = RNA_def_property(srna, "inherit", PROP_ENUM, 0); + RNA_def_property_enum_sdna(prop, NULL, "inherit"); + RNA_def_property_enum_items(prop, rna_enum_brush_dyntopo_inherit); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_flag(prop, PROP_ENUM_FLAG); + RNA_def_property_ui_text(prop, "Inherit", "Which default dyntopo settings to use"); + + prop = RNA_def_property(srna, "radius_scale", PROP_FLOAT, PROP_PERCENTAGE); + RNA_def_property_float_sdna(prop, NULL, "radius_scale"); + RNA_def_property_range(prop, 0.0f, 15.0f); + RNA_def_property_ui_range(prop, 0.0f, 2.0f, 0.001, 4); + RNA_def_property_ui_text(prop, "Scale dyntopo radius", ""); + RNA_def_property_update(prop, 0, "rna_Brush_dyntopo_update"); +} + static void rna_def_sculpt_capabilities(BlenderRNA *brna) { StructRNA *srna; @@ -1222,6 +1401,7 @@ static void rna_def_sculpt_capabilities(BlenderRNA *brna) SCULPT_TOOL_CAPABILITY(has_accumulate, "Has Accumulate"); SCULPT_TOOL_CAPABILITY(has_auto_smooth, "Has Auto Smooth"); SCULPT_TOOL_CAPABILITY(has_topology_rake, "Has Topology Rake"); + SCULPT_TOOL_CAPABILITY(has_vcol_boundary_smooth, "Has VCol Boundary Smooth"); SCULPT_TOOL_CAPABILITY(has_height, "Has Height"); SCULPT_TOOL_CAPABILITY(has_jitter, "Has Jitter"); SCULPT_TOOL_CAPABILITY(has_normal_weight, "Has Crease/Pinch Factor"); @@ -2016,6 +2196,20 @@ static void rna_def_gpencil_options(BlenderRNA *brna) RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); } +const EnumPropertyItem brush_curve_preset_items[] = { + {BRUSH_CURVE_CUSTOM, "CUSTOM", ICON_RNDCURVE, "Custom", ""}, + {BRUSH_CURVE_SMOOTH, "SMOOTH", ICON_SMOOTHCURVE, "Smooth", ""}, + {BRUSH_CURVE_SMOOTHER, "SMOOTHER", ICON_SMOOTHCURVE, "Smoother", ""}, + {BRUSH_CURVE_SPHERE, "SPHERE", ICON_SPHERECURVE, "Sphere", ""}, + {BRUSH_CURVE_ROOT, "ROOT", ICON_ROOTCURVE, "Root", ""}, + {BRUSH_CURVE_SHARP, "SHARP", ICON_SHARPCURVE, "Sharp", ""}, + {BRUSH_CURVE_LIN, "LIN", ICON_LINCURVE, "Linear", ""}, + {BRUSH_CURVE_POW4, "POW4", ICON_SHARPCURVE, "Sharper", ""}, + {BRUSH_CURVE_INVSQUARE, "INVSQUARE", ICON_INVERSESQUARECURVE, "Inverse Square", ""}, + {BRUSH_CURVE_CONSTANT, "CONSTANT", ICON_NOCURVE, "Constant", ""}, + {0, NULL, 0, NULL, NULL}, +}; + static void rna_def_curves_sculpt_options(BlenderRNA *brna) { StructRNA *srna; @@ -2270,20 +2464,6 @@ static void rna_def_brush(BlenderRNA *brna) {0, NULL, 0, NULL, NULL}, }; - static const EnumPropertyItem brush_curve_preset_items[] = { - {BRUSH_CURVE_CUSTOM, "CUSTOM", ICON_RNDCURVE, "Custom", ""}, - {BRUSH_CURVE_SMOOTH, "SMOOTH", ICON_SMOOTHCURVE, "Smooth", ""}, - {BRUSH_CURVE_SMOOTHER, "SMOOTHER", ICON_SMOOTHCURVE, "Smoother", ""}, - {BRUSH_CURVE_SPHERE, "SPHERE", ICON_SPHERECURVE, "Sphere", ""}, - {BRUSH_CURVE_ROOT, "ROOT", ICON_ROOTCURVE, "Root", ""}, - {BRUSH_CURVE_SHARP, "SHARP", ICON_SHARPCURVE, "Sharp", ""}, - {BRUSH_CURVE_LIN, "LIN", ICON_LINCURVE, "Linear", ""}, - {BRUSH_CURVE_POW4, "POW4", ICON_SHARPCURVE, "Sharper", ""}, - {BRUSH_CURVE_INVSQUARE, "INVSQUARE", ICON_INVERSESQUARECURVE, "Inverse Square", ""}, - {BRUSH_CURVE_CONSTANT, "CONSTANT", ICON_NOCURVE, "Constant", ""}, - {0, NULL, 0, NULL, NULL}, - }; - static const EnumPropertyItem brush_deformation_target_items[] = { {BRUSH_DEFORM_TARGET_GEOMETRY, "GEOMETRY", @@ -2334,6 +2514,7 @@ static void rna_def_brush(BlenderRNA *brna) {BRUSH_CLOTH_DEFORM_GRAB, "GRAB", 0, "Grab", ""}, {BRUSH_CLOTH_DEFORM_EXPAND, "EXPAND", 0, "Expand", ""}, {BRUSH_CLOTH_DEFORM_SNAKE_HOOK, "SNAKE_HOOK", 0, "Snake Hook", ""}, + {BRUSH_CLOTH_DEFORM_ELASTIC_DRAG, "ELASTIC", 0, "Elastic Drag", ""}, {0, NULL, 0, NULL, NULL}, }; @@ -2394,6 +2575,16 @@ static void rna_def_brush(BlenderRNA *brna) 0, "Surface", "Smooths the surface of the mesh, preserving the volume"}, + {BRUSH_SMOOTH_DEFORM_DIRECTIONAL, + "DIRECTIONAL", + 0, + "Directional", + "Smooths the surface taking into account the direction of the stroke"}, + {BRUSH_SMOOTH_DEFORM_UNIFORM_WEIGHTS, + "UNIFORM_WEIGHTS", + 0, + "Uniform Weights", + "Smooths the surface considering that all edges have the same length"}, {0, NULL, 0, NULL, NULL}, }; @@ -2401,6 +2592,14 @@ static void rna_def_brush(BlenderRNA *brna) {BRUSH_POSE_DEFORM_ROTATE_TWIST, "ROTATE_TWIST", 0, "Rotate/Twist", ""}, {BRUSH_POSE_DEFORM_SCALE_TRASLATE, "SCALE_TRANSLATE", 0, "Scale/Translate", ""}, {BRUSH_POSE_DEFORM_SQUASH_STRETCH, "SQUASH_STRETCH", 0, "Squash & Stretch", ""}, + {BRUSH_POSE_DEFORM_BEND, "BEND", 0, "Bend", ""}, + {0, NULL, 0, NULL, NULL}, + }; + + static const EnumPropertyItem brush_array_deform_type_items[] = { + {BRUSH_ARRAY_DEFORM_LINEAR, "LINEAR", 0, "Linear", ""}, + {BRUSH_ARRAY_DEFORM_RADIAL, "RADIAL", 0, "Radial", ""}, + {BRUSH_ARRAY_DEFORM_PATH, "PATH", 0, "Path", ""}, {0, NULL, 0, NULL, NULL}, }; @@ -2445,6 +2644,14 @@ static void rna_def_brush(BlenderRNA *brna) {BRUSH_BOUNDARY_DEFORM_GRAB, "GRAB", 0, "Grab", ""}, {BRUSH_BOUNDARY_DEFORM_TWIST, "TWIST", 0, "Twist", ""}, {BRUSH_BOUNDARY_DEFORM_SMOOTH, "SMOOTH", 0, "Smooth", ""}, + {BRUSH_BOUNDARY_DEFORM_CIRCLE, "CIRCLE", 0, "Circle", ""}, + {0, NULL, 0, NULL, NULL}, + }; + + static const EnumPropertyItem brush_scene_project_direction_type_items[] = { + {BRUSH_SCENE_PROJECT_DIRECTION_VIEW, "VIEW", 0, "View", ""}, + {BRUSH_SCENE_PROJECT_DIRECTION_VERTEX_NORMAL, "VERTEX_NORMAL", 0, "Vertex Normal", ""}, + {BRUSH_SCENE_PROJECT_DIRECTION_BRUSH_NORMAL, "BRUSH_NORMAL", 0, "Brush Normal", ""}, {0, NULL, 0, NULL, NULL}, }; @@ -2551,6 +2758,7 @@ static void rna_def_brush(BlenderRNA *brna) RNA_def_property_enum_items(prop, brush_curve_preset_items); RNA_def_property_ui_text(prop, "Curve Preset", ""); RNA_def_property_update(prop, 0, "rna_Brush_update"); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); prop = RNA_def_property(srna, "deform_target", PROP_ENUM, PROP_NONE); RNA_def_property_enum_items(prop, brush_deformation_target_items); @@ -2603,6 +2811,12 @@ static void rna_def_brush(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Deformation", "Deformation type that is used in the brush"); RNA_def_property_update(prop, 0, "rna_Brush_update"); + prop = RNA_def_property(srna, "smear_deform_blend", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "smear_deform_blend"); + RNA_def_property_range(prop, 0.0f, 1.0f); + RNA_def_property_ui_text(prop, "Blend", "Smear deformation blend"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); + prop = RNA_def_property(srna, "slide_deform_type", PROP_ENUM, PROP_NONE); RNA_def_property_enum_items(prop, brush_slide_deform_type_items); RNA_def_property_ui_text(prop, "Deformation", "Deformation type that is used in the brush"); @@ -2625,6 +2839,26 @@ static void rna_def_brush(BlenderRNA *brna) "Method to set the rotation origins for the segments of the brush"); RNA_def_property_update(prop, 0, "rna_Brush_update"); + prop = RNA_def_property(srna, "scene_project_direction_type", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, brush_scene_project_direction_type_items); + RNA_def_property_ui_text( + prop, + "Project Direction", + "Direction that is going to be used to project the vertices into the scene"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); + + prop = RNA_def_property(srna, "array_deform_type", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, brush_array_deform_type_items); + RNA_def_property_ui_text(prop, "Deformation", "Deformation type that is used in the brush"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); + + prop = RNA_def_property(srna, "array_count", PROP_INT, PROP_UNSIGNED); + RNA_def_property_int_sdna(prop, NULL, "array_count"); + RNA_def_property_range(prop, 1, 10000); + RNA_def_property_ui_range(prop, 1, 50, 1, -1); + RNA_def_property_ui_text(prop, "Count", "Number of copies"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); + prop = RNA_def_property(srna, "jitter_unit", PROP_ENUM, PROP_NONE); /* as an enum */ RNA_def_property_enum_bitflag_sdna(prop, NULL, "flag"); RNA_def_property_enum_items(prop, brush_jitter_unit_items); @@ -2637,6 +2871,7 @@ static void rna_def_brush(BlenderRNA *brna) RNA_def_property_enum_items(prop, falloff_shape_unit_items); RNA_def_property_ui_text(prop, "Falloff Shape", "Use projected or spherical falloff"); RNA_def_property_update(prop, 0, "rna_Brush_update"); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); /* number values */ prop = RNA_def_property(srna, "size", PROP_INT, PROP_PIXEL); @@ -2677,6 +2912,22 @@ static void rna_def_brush(BlenderRNA *brna) prop, "Spacing", "Spacing between brush daubs as a percentage of brush diameter"); RNA_def_property_update(prop, 0, "rna_Brush_update"); + prop = RNA_def_property(srna, "auto_smooth_spacing", PROP_INT, PROP_PERCENTAGE); + RNA_def_property_int_sdna(prop, NULL, "autosmooth_spacing"); + RNA_def_property_range(prop, 1, 1000); + RNA_def_property_ui_range(prop, 1, 500, 5, -1); + RNA_def_property_ui_text( + prop, "Auto-Smooth Spacing", "Autosmooth spacing as a percentage of brush diameter"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); + + prop = RNA_def_property(srna, "topology_rake_spacing", PROP_INT, PROP_PERCENTAGE); + RNA_def_property_int_sdna(prop, NULL, "topology_rake_spacing"); + RNA_def_property_range(prop, 1, 1000); + RNA_def_property_ui_range(prop, 1, 500, 5, -1); + RNA_def_property_ui_text( + prop, "Topology Rake Spacing", "Topology rake spacing as a percentage of brush diameter"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); + prop = RNA_def_property(srna, "grad_spacing", PROP_INT, PROP_PIXEL); RNA_def_property_int_sdna(prop, NULL, "gradient_spacing"); RNA_def_property_range(prop, 1, 10000); @@ -3072,13 +3323,39 @@ static void rna_def_brush(BlenderRNA *brna) RNA_def_property_ui_range(prop, 0.0f, 1.0f, 0.001, 3); RNA_def_property_ui_text( prop, "Auto-Smooth", "Amount of smoothing to automatically apply to each stroke"); + + RNA_def_property_update(prop, 0, "rna_Brush_update"); + + prop = RNA_def_property(srna, "auto_smooth_projection", PROP_FLOAT, PROP_FACTOR); + RNA_def_property_float_sdna(prop, NULL, "autosmooth_projection"); + RNA_def_property_range(prop, 0.0f, 1.0f); + RNA_def_property_ui_range(prop, 0.0f, 1.0f, 0.001, 3); + RNA_def_property_ui_text(prop, "Projection", "How much autosmooth should stick to surface"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); + + RNA_def_property_update(prop, 0, "rna_Brush_update"); + prop = RNA_def_property(srna, "auto_smooth_radius_factor", PROP_FLOAT, PROP_FACTOR); + RNA_def_property_float_sdna(prop, NULL, "autosmooth_radius_factor"); + RNA_def_property_range(prop, 0.001f, 5.0f); + RNA_def_property_ui_range(prop, 0.001f, 2.0f, 0.15, 3); + RNA_def_property_ui_text(prop, + "Smooth Radius", + "Ratio between the brush radius and the radius that is going to be " + "used for smoothing"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); + + prop = RNA_def_property(srna, "concave_mask_factor", PROP_FLOAT, PROP_FACTOR); + RNA_def_property_float_sdna(prop, NULL, "concave_mask_factor"); + RNA_def_property_range(prop, 0.0f, 1.0f); + RNA_def_property_ui_range(prop, 0.0f, 1.0f, 0.001, 3); + RNA_def_property_ui_text(prop, "Cavity Mask", "Mask to concave areas"); RNA_def_property_update(prop, 0, "rna_Brush_update"); prop = RNA_def_property(srna, "topology_rake_factor", PROP_FLOAT, PROP_FACTOR); RNA_def_property_float_sdna(prop, NULL, "topology_rake_factor"); RNA_def_property_float_default(prop, 0); - RNA_def_property_range(prop, 0.0f, 1.0f); - RNA_def_property_ui_range(prop, 0.0f, 1.0f, 0.001, 3); + RNA_def_property_range(prop, 0.0f, 5.0f); + RNA_def_property_ui_range(prop, 0.0f, 2.0f, 0.001, 3); RNA_def_property_ui_text(prop, "Topology Rake", "Automatically align edges to the brush direction to " @@ -3086,6 +3363,63 @@ static void rna_def_brush(BlenderRNA *brna) "Best used on low-poly meshes as it has a performance impact"); RNA_def_property_update(prop, 0, "rna_Brush_update"); + prop = RNA_def_property(srna, "topology_rake_radius_factor", PROP_FLOAT, PROP_FACTOR); + RNA_def_property_float_sdna(prop, NULL, "topology_rake_radius_factor"); + RNA_def_property_range(prop, 0.001f, 5.0f); + RNA_def_property_ui_range(prop, 0.0f, 3.0f, 0.1, 2); + RNA_def_property_ui_text(prop, + "Rake Radius", + "Ratio between the brush radius and the radius that is going to be " + "used for topology rake"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); + + prop = RNA_def_property(srna, "topology_rake_projection", PROP_FLOAT, PROP_FACTOR); + RNA_def_property_float_sdna(prop, NULL, "topology_rake_projection"); + RNA_def_property_range(prop, 0.0f, 1.0f); + RNA_def_property_ui_range(prop, 0.0f, 1.0f, 0.001, 3); + RNA_def_property_ui_text(prop, + "Projection", + "How much topology rake should stick to surface" + "Lower values with have smoothing effect"); + + RNA_def_property_update(prop, 0, "rna_Brush_update"); + + prop = RNA_def_property(srna, "vcol_boundary_factor", PROP_FLOAT, PROP_FACTOR); + RNA_def_property_float_sdna(prop, NULL, "vcol_boundary_factor"); + RNA_def_property_float_default(prop, 0); + RNA_def_property_range(prop, 0.0f, 1.0f); + RNA_def_property_ui_range(prop, 0.0f, 1.0f, 0.001, 3); + RNA_def_property_ui_text(prop, + "Boundary Hardening", + "Automatically align edges on color boundaries" + "to generate sharper features. "); + RNA_def_property_update(prop, 0, "rna_Brush_update"); + + prop = RNA_def_property(srna, "boundary_smooth_factor", PROP_FLOAT, PROP_FACTOR); + RNA_def_property_float_sdna(prop, NULL, "boundary_smooth_factor"); + RNA_def_property_float_default(prop, 0); + RNA_def_property_range(prop, -2.0f, 2.0f); + RNA_def_property_ui_range(prop, 0.0f, 1.0f, 0.001, 3); + RNA_def_property_ui_text(prop, "Boundary Smoothing", "How much to smooth sharp boundaries "); + RNA_def_property_update(prop, 0, "rna_Brush_update"); + + prop = RNA_def_property(srna, "autosmooth_fset_slide", PROP_FLOAT, PROP_FACTOR); + RNA_def_property_float_sdna(prop, NULL, "autosmooth_fset_slide"); + RNA_def_property_float_default(prop, 0); + RNA_def_property_range(prop, -2.0f, 2.0f); + RNA_def_property_ui_range(prop, 0.0f, 1.0f, 0.001, 3); + RNA_def_property_ui_text( + prop, "Face Set Slide", "Slide face set boundaries instead of sharpening them"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); + + prop = RNA_def_property(srna, "vcol_boundary_exponent", PROP_FLOAT, PROP_FACTOR); + RNA_def_property_float_sdna(prop, NULL, "vcol_boundary_exponent"); + RNA_def_property_float_default(prop, 0); + RNA_def_property_range(prop, 0.0f, 6.0f); + RNA_def_property_ui_range(prop, 0.1f, 3.0f, 0.001, 3); + RNA_def_property_ui_text(prop, "Exponent", "Hardening exponent (smaller value smoother edges)"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); + prop = RNA_def_property(srna, "tilt_strength_factor", PROP_FLOAT, PROP_FACTOR); RNA_def_property_float_sdna(prop, NULL, "tilt_strength_factor"); RNA_def_property_float_default(prop, 0); @@ -3232,6 +3566,7 @@ static void rna_def_brush(BlenderRNA *brna) RNA_def_property_range(prop, 0.0f, 5.0f); RNA_def_property_ui_range(prop, 0.0f, 1.0f, 0.1, 3); RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); + RNA_def_property_update(prop, 0, "rna_Brush_update"); prop = RNA_def_property(srna, "automasking_cavity_blur_steps", PROP_INT, PROP_NONE); @@ -3288,12 +3623,74 @@ static void rna_def_brush(BlenderRNA *brna) "Apply the maximum grab strength to the active vertex instead of the cursor location"); RNA_def_property_update(prop, 0, "rna_Brush_update"); + prop = RNA_def_property(srna, "use_weighted_smooth", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag2", BRUSH_SMOOTH_USE_AREA_WEIGHT); + RNA_def_property_ui_text(prop, "Weight By Area", "Weight by face area to get a smoother result"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); + + prop = RNA_def_property(srna, "preserve_faceset_boundary", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag2", BRUSH_SMOOTH_PRESERVE_FACE_SETS); + RNA_def_property_ui_text( + prop, "Preserve Face Sets", "Preserve face set boundaries when smoothing"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); + + prop = RNA_def_property(srna, "hard_edge_mode", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag2", BRUSH_HARD_EDGE_MODE); + RNA_def_property_ui_text( + prop, "Hard Edge Mode", "Hard edge mode; treat all face set boundaries as hard edges"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); + prop = RNA_def_property(srna, "use_grab_silhouette", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag2", BRUSH_GRAB_SILHOUETTE); RNA_def_property_ui_text( prop, "Grab Silhouette", "Grabs trying to automask the silhouette of the object"); RNA_def_property_update(prop, 0, "rna_Brush_update"); + prop = RNA_def_property(srna, "use_surface_falloff", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag2", BRUSH_USE_SURFACE_FALLOFF); + RNA_def_property_ui_text(prop, + "Use Surface Falloff", + "Propagate the falloff of the brush trough the surface of the mesh"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); + + prop = RNA_def_property(srna, "use_array_lock_orientation", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag2", BRUSH_ARRAY_LOCK_ORIENTATION); + RNA_def_property_ui_text(prop, "Lock Orientation", ""); + RNA_def_property_update(prop, 0, "rna_Brush_update"); + + prop = RNA_def_property(srna, "use_array_fill_holes", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag2", BRUSH_ARRAY_FILL_HOLES); + RNA_def_property_ui_text(prop, "Fill Holes", ""); + + prop = RNA_def_property(srna, "use_curvature_rake", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag2", BRUSH_CURVATURE_RAKE); + RNA_def_property_ui_text( + prop, "Curvature Rake", "Topology rake follows curvature instead of brush direction"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); + + prop = RNA_def_property(srna, "ignore_falloff_for_topology_rake", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag2", BRUSH_TOPOLOGY_RAKE_IGNORE_BRUSH_FALLOFF); + RNA_def_property_ui_text( + prop, "Ignore Brush Falloff", "Ignore brush falloff settings for topology rake"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); + + prop = RNA_def_property(srna, "use_custom_auto_smooth_spacing", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag2", BRUSH_CUSTOM_AUTOSMOOTH_SPACING); + RNA_def_property_ui_text( + prop, + "Use Custom Autosmooth Spacing", + "Use custom spacing for autosmooth (must be larger then brush spacing)"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); + + prop = RNA_def_property(srna, "use_custom_topology_rake_spacing", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag2", BRUSH_CUSTOM_TOPOLOGY_RAKE_SPACING); + RNA_def_property_ui_text( + prop, + "Use Custom Rake Spacing", + "Use custom spacing for topology rake (must be larger then brush spacing)"); + + RNA_def_property_update(prop, 0, "rna_Brush_update"); + prop = RNA_def_property(srna, "use_paint_antialiasing", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "sampling_flag", BRUSH_PAINT_ANTIALIASING); RNA_def_property_ui_text(prop, "Anti-Aliasing", "Smooths the edges of the strokes"); @@ -3515,6 +3912,17 @@ static void rna_def_brush(BlenderRNA *brna) RNA_def_property_flag(prop, PROP_NEVER_NULL); RNA_def_property_ui_text(prop, "Curve", "Editable falloff curve"); RNA_def_property_update(prop, 0, "rna_Brush_update"); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); + + prop = RNA_def_property(srna, "pressure_size_curve", PROP_POINTER, PROP_NONE); + RNA_def_property_flag(prop, PROP_NEVER_NULL); + RNA_def_property_ui_text(prop, "Pressure/Size Curve", "Pressure/Size input curve"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); + + prop = RNA_def_property(srna, "pressure_strength_curve", PROP_POINTER, PROP_NONE); + RNA_def_property_flag(prop, PROP_NEVER_NULL); + RNA_def_property_ui_text(prop, "Pressure/Strength Curve", "Pressure/Strength input curve"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); prop = RNA_def_property(srna, "paint_curve", PROP_POINTER, PROP_NONE); RNA_def_property_flag(prop, PROP_EDITABLE); @@ -3728,6 +4136,12 @@ static void rna_def_brush(BlenderRNA *brna) RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_ui_text(prop, "Gpencil Settings", ""); + prop = RNA_def_property(srna, "dyntopo", PROP_POINTER, PROP_NONE); + RNA_def_property_struct_type(prop, "DynTopoSettings"); + RNA_def_property_pointer_sdna(prop, NULL, "dyntopo"); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "Dyntopo Settings", ""); + prop = RNA_def_property(srna, "curves_sculpt_settings", PROP_POINTER, PROP_NONE); RNA_def_property_struct_type(prop, "BrushCurvesSculptSettings"); RNA_def_property_clear_flag(prop, PROP_EDITABLE); @@ -3807,10 +4221,21 @@ static void rna_def_operator_stroke_element(BlenderRNA *brna) /* XXX: i don't think blender currently supports the ability to properly do a remappable modifier * in the middle of a stroke */ + + prop = RNA_def_property(srna, "mouse_cubic", PROP_FLOAT, PROP_COORDS); + RNA_def_property_flag(prop, PROP_IDPROPERTY); + RNA_def_property_array(prop, 8); + RNA_def_property_ui_text(prop, "Mouse", ""); + + prop = RNA_def_property(srna, "world_cubic", PROP_FLOAT, PROP_COORDS); + RNA_def_property_flag(prop, PROP_IDPROPERTY); + RNA_def_property_array(prop, 12); + RNA_def_property_ui_text(prop, "Mouse", ""); } void RNA_def_brush(BlenderRNA *brna) { + rna_def_dyntopo_settings(brna); rna_def_brush(brna); rna_def_brush_capabilities(brna); rna_def_sculpt_capabilities(brna); diff --git a/source/blender/makesrna/intern/rna_internal.h b/source/blender/makesrna/intern/rna_internal.h index a1f9bf776ae..e4e62d67d47 100644 --- a/source/blender/makesrna/intern/rna_internal.h +++ b/source/blender/makesrna/intern/rna_internal.h @@ -29,6 +29,7 @@ struct Object; struct ReportList; struct SDNA; struct ViewLayer; +struct BrushChannel; /* Data structures used during define */ @@ -144,6 +145,11 @@ void RNA_def_attribute(struct BlenderRNA *brna); void RNA_def_asset(struct BlenderRNA *brna); void RNA_def_boid(struct BlenderRNA *brna); void RNA_def_brush(struct BlenderRNA *brna); +void RNA_def_brush_engine(struct BlenderRNA *brna); +void RNA_def_brush_channelset(struct BlenderRNA *brna, + struct PropertyRNA *cprop, + const char *type_prefix, + char name_mem[256]); void RNA_def_cachefile(struct BlenderRNA *brna); void RNA_def_camera(struct BlenderRNA *brna); void RNA_def_cloth(struct BlenderRNA *brna); @@ -665,6 +671,16 @@ const char *rna_translate_ui_text(const char *text, /* Internal functions that cycles uses so we need to declare (not ideal!). */ void rna_RenderPass_rect_set(PointerRNA *ptr, const float *values); +int rna_BrushChannelSet_channels_assignint(struct PointerRNA *ptr, + int key, + const struct PointerRNA *assign_ptr); +int rna_BrushChannelSet_channels_begin(struct CollectionPropertyIterator *iter, + struct PointerRNA *ptr); +int rna_BrushChannelSet_length(struct PointerRNA *rna); +void rna_BrushChannelSet_ensure(struct ID *id, struct BrushChannel *channel); + +/* internal brush channel functions */ + #ifdef RNA_RUNTIME # ifdef __GNUC__ # pragma GCC diagnostic ignored "-Wredundant-decls" diff --git a/source/blender/makesrna/intern/rna_material.c b/source/blender/makesrna/intern/rna_material.c index d1b601e6f96..3f6e74e4403 100644 --- a/source/blender/makesrna/intern/rna_material.c +++ b/source/blender/makesrna/intern/rna_material.c @@ -149,6 +149,7 @@ static void rna_Material_texpaint_begin(CollectionPropertyIterator *iter, Pointe static void rna_Material_active_paint_texture_index_update(bContext *C, PointerRNA *ptr) { + bScreen *screen; Main *bmain = CTX_data_main(C); Material *ma = (Material *)ptr->owner_id; diff --git a/source/blender/makesrna/intern/rna_mesh.c b/source/blender/makesrna/intern/rna_mesh.c index 0f9b11905fa..9f2894b8c84 100644 --- a/source/blender/makesrna/intern/rna_mesh.c +++ b/source/blender/makesrna/intern/rna_mesh.c @@ -1220,7 +1220,10 @@ static bool rna_MeshVertColorLayer_active_get(PointerRNA *ptr) static void rna_MeshVertColorLayer_active_render_set(PointerRNA *ptr, bool value) { - rna_CustomDataLayer_active_set(ptr, rna_mesh_vdata(ptr), value, CD_PROP_COLOR, 1); + CustomDataLayer *layer = (CustomDataLayer *)ptr->data; + + //XXX + printf("%s: error!\n", __func__); } static void rna_MeshVertColorLayer_active_set(PointerRNA *ptr, bool value) @@ -4023,7 +4026,7 @@ static void rna_def_mesh(BlenderRNA *brna) prop = RNA_def_property(srna, "vertex_normals", PROP_COLLECTION, PROP_NONE); RNA_def_property_struct_type(prop, "MeshNormalValue"); - RNA_def_property_override_flag(prop, PROPOVERRIDE_IGNORE); + RNA_def_property_ui_text(prop, "Vertex Normals", "The normal direction of each vertex, defined as the average of the " @@ -4417,6 +4420,12 @@ static void rna_def_mesh(BlenderRNA *brna) RNA_def_property_update(prop, 0, "rna_Mesh_update_draw"); RNA_def_property_flag(prop, PROP_NO_DEG_UPDATE); + prop = RNA_def_property(srna, "use_remesh_preserve_materials", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", ME_REMESH_REPROJECT_MATERIALS); + RNA_def_property_boolean_default(prop, false); + RNA_def_property_ui_text(prop, "Preserve Materials", "Keep the current material slots"); + RNA_def_property_update(prop, 0, "rna_Mesh_update_draw"); + prop = RNA_def_property(srna, "remesh_mode", PROP_ENUM, PROP_NONE); RNA_def_property_enum_sdna(prop, NULL, "remesh_mode"); RNA_def_property_enum_items(prop, rna_enum_mesh_remesh_mode_items); @@ -4449,6 +4458,19 @@ static void rna_def_mesh(BlenderRNA *brna) "Mirror the left/right vertex groups when painting. The symmetry axis " "is determined by the symmetry settings"); RNA_def_property_update(prop, 0, "rna_Mesh_update_draw"); + + prop = RNA_def_property(srna, "use_fset_boundary_mirror", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", ME_SCULPT_MIRROR_FSET_BOUNDARIES); + RNA_def_property_ui_text( + prop, + "Split Face Sets", + "Use mirroring to split face sets for some tools (e.g. boundary smoothing)"); + // RNA_def_property_update(prop, 0, "rna_Mesh_update_draw"); + + prop = RNA_def_property(srna, "sculpt_ignore_uvs", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", ME_SCULPT_IGNORE_UVS); + RNA_def_property_ui_text(prop, "Ignore UVs", ""); + /* End Symmetry */ prop = RNA_def_property(srna, "use_auto_smooth", PROP_BOOLEAN, PROP_NONE); diff --git a/source/blender/makesrna/intern/rna_modifier.c b/source/blender/makesrna/intern/rna_modifier.c index b86e202a791..71436a64e12 100644 --- a/source/blender/makesrna/intern/rna_modifier.c +++ b/source/blender/makesrna/intern/rna_modifier.c @@ -201,6 +201,7 @@ const EnumPropertyItem rna_enum_object_modifier_type_items[] = { ICON_MOD_ARMATURE, "Armature", "Deform the shape using an armature object"}, + {eModifierType_BassRelief, "BASS_RELIEF", ICON_MOD_SHRINKWRAP, "Bass Relief", ""}, {eModifierType_Cast, "CAST", ICON_MOD_CAST, @@ -1118,6 +1119,18 @@ static void rna_ShrinkwrapModifier_face_cull_set(struct PointerRNA *ptr, int val swm->shrinkOpts = (swm->shrinkOpts & ~MOD_SHRINKWRAP_CULL_TARGET_MASK) | value; } +static int rna_BassReliefModifier_face_cull_get(PointerRNA *ptr) +{ + BassReliefModifierData *swm = (BassReliefModifierData *)ptr->data; + return swm->shrinkOpts & MOD_BASSRELIEF_CULL_TARGET_MASK; +} + +static void rna_BassReliefModifier_face_cull_set(struct PointerRNA *ptr, int value) +{ + BassReliefModifierData *swm = (BassReliefModifierData *)ptr->data; + swm->shrinkOpts = (swm->shrinkOpts & ~MOD_BASSRELIEF_CULL_TARGET_MASK) | value; +} + static bool rna_MeshDeformModifier_is_bound_get(PointerRNA *ptr) { return (((MeshDeformModifierData *)ptr->data)->bindcagecos != NULL); @@ -1296,7 +1309,6 @@ static void rna_DataTransferModifier_polys_data_types_set(struct PointerRNA *ptr dtmd->data_types &= ~DT_TYPE_POLY_ALL; dtmd->data_types |= value; } - static const EnumPropertyItem *rna_DataTransferModifier_layers_select_src_itemf(bContext *C, PointerRNA *ptr, PropertyRNA *prop, @@ -3337,6 +3349,13 @@ static void rna_def_modifier_correctivesmooth(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Lambda Factor", "Smooth factor effect"); RNA_def_property_update(prop, 0, "rna_CorrectiveSmoothModifier_update"); + prop = RNA_def_property(srna, "projection", PROP_FLOAT, PROP_FACTOR); + RNA_def_property_float_sdna(prop, NULL, "projection"); + RNA_def_property_range(prop, -FLT_MAX, FLT_MAX); + RNA_def_property_ui_range(prop, 0.0, 1.0, 5, 3); + RNA_def_property_ui_text(prop, "Projection", "Volume preserving projection"); + RNA_def_property_update(prop, 0, "rna_CorrectiveSmoothModifier_update"); + prop = RNA_def_property(srna, "iterations", PROP_INT, PROP_NONE); RNA_def_property_int_sdna(prop, NULL, "repeat"); RNA_def_property_ui_range(prop, 0, 200, 1, -1); @@ -4393,9 +4412,215 @@ static void rna_def_modifier_shrinkwrap(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Invert", "Invert vertex group influence"); RNA_def_property_update(prop, 0, "rna_Modifier_update"); + prop = RNA_def_property(srna, "ray_shrink_ratio", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "rayShrinkRatio"); + RNA_def_property_ui_text( + prop, "Shrink Ratio", "Compress shrinkwrap result by this ratio; useful for reliefs"); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + prop = RNA_def_property(srna, "use_normal_optimizer", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "optimizeNormals", 1); + RNA_def_property_ui_text(prop, + "Optimize Normals", + "Try to optimize normals to match original mesh; useful for making " + "reliefs when combined with Shrink Ratio"); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + prop = RNA_def_property(srna, "normal_optimizer_steps", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, NULL, "optimizeNormalSteps"); + RNA_def_property_ui_text(prop, "Exaggeration Filter", ""); + RNA_def_property_range(prop, 0, 255); + RNA_def_property_ui_range(prop, 0, 10, 1, 1); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + prop = RNA_def_property(srna, "boundary_smooth_steps", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, NULL, "boundSmoothSteps"); + RNA_def_property_ui_text(prop, "Boundary Smooth", "Boundary smooth steps"); + RNA_def_property_range(prop, 0, 255); + RNA_def_property_ui_range(prop, 0, 10, 1, 1); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + prop = RNA_def_property(srna, "boundary_smooth_scale", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "boundSmoothScale"); + RNA_def_property_ui_range(prop, 0.001, 4.0, 0.01, 3); + RNA_def_property_ui_text(prop, "Boundary Scale", "Boundary width"); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + prop = RNA_def_property(srna, "normal_optimizer_scale", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "optimizeNormalsScale"); + RNA_def_property_ui_range(prop, 0.001, 4.0, 0.01, 3); + RNA_def_property_ui_text(prop, "Exaggeration Scale", ""); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + /* + uiItemR(col, ptr, "normal_optimizer_scale", 0, NULL, ICON_NONE); + uiItemR(col, ptr, "normal_optimizer_steps", 0, NULL, ICON_NONE); + uiItemR(col, ptr, "boundary_smooth_scale", 0, NULL, ICON_NONE); + uiItemR(col, ptr, "boundary_smooth_steps", 0, NULL, ICON_NONE); + + */ RNA_define_lib_overridable(false); } +static void rna_def_modifier_bassrelief(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + static const EnumPropertyItem bassrelief_face_cull_items[] = { + //{0, "OFF", 0, "Off", "No culling"}, + {MOD_BASSRELIEF_CULL_TARGET_FRONTFACE, + "FRONT", + 0, + "Front", + "No projection when in front of the face"}, + {MOD_BASSRELIEF_CULL_TARGET_BACKFACE, + "BACK", + 0, + "Back", + "No projection when behind the face"}, + {0, NULL, 0, NULL, NULL}, + }; + + srna = RNA_def_struct(brna, "BassReliefModifier", "Modifier"); + RNA_def_struct_ui_text(srna, "Bass Relief Modifier", "Create bass reliefs"); + RNA_def_struct_sdna(srna, "BassReliefModifierData"); + RNA_def_struct_ui_icon(srna, ICON_MOD_SHRINKWRAP); + + RNA_define_lib_overridable(true); + + prop = RNA_def_property(srna, "cull_face", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "shrinkOpts"); + RNA_def_property_enum_items(prop, bassrelief_face_cull_items); + RNA_def_property_enum_funcs( + prop, "rna_BassReliefModifier_face_cull_get", "rna_BassReliefModifier_face_cull_set", NULL); + RNA_def_property_ui_text( + prop, + "Face Cull", + "Stop vertices from projecting to a face on the target when facing towards/away"); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + prop = RNA_def_property(srna, "target", PROP_POINTER, PROP_NONE); + RNA_def_property_ui_text(prop, "Target", "Mesh target to shrink to"); + RNA_def_property_pointer_funcs( + prop, NULL, "rna_ShrinkwrapModifier_target_set", NULL, "rna_Mesh_object_poll"); + RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK); + RNA_def_property_update(prop, 0, "rna_Modifier_dependency_update"); + + prop = RNA_def_property(srna, "collection", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, NULL, "collection"); + RNA_def_property_ui_text(prop, "Collection Target", "Use meshes in this collection as targets"); + // RNA_def_property_pointer_funcs( + // prop, NULL, "rna_ShrinkwrapModifier_auxTarget_set", NULL, "rna_Mesh_object_poll"); + RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK); + RNA_def_property_update(prop, 0, "rna_Modifier_dependency_update"); + + prop = RNA_def_property(srna, "vertex_group", PROP_STRING, PROP_NONE); + RNA_def_property_string_sdna(prop, NULL, "vgroup_name"); + RNA_def_property_ui_text(prop, "Vertex Group", "Vertex group name"); + // RNA_def_property_string_funcs(prop, NULL, NULL, "rna_BassReliefModifier_vgroup_name_set"); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + prop = RNA_def_property(srna, "offset", PROP_FLOAT, PROP_DISTANCE); + RNA_def_property_float_sdna(prop, NULL, "keepDist"); + RNA_def_property_range(prop, -FLT_MAX, FLT_MAX); + RNA_def_property_ui_range(prop, -100, 100, 1, 2); + RNA_def_property_ui_text(prop, "Offset", "Distance to keep from the target"); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + prop = RNA_def_property(srna, "project_limit", PROP_FLOAT, PROP_DISTANCE); + RNA_def_property_float_sdna(prop, NULL, "projLimit"); + RNA_def_property_range(prop, 0.0, FLT_MAX); + RNA_def_property_ui_range(prop, 0, 100, 1, 2); + RNA_def_property_ui_text( + prop, "Project Limit", "Limit the distance used for projection (zero disables)"); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + prop = RNA_def_property(srna, "use_project_x", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "projAxis", MOD_BASSRELIEF_PROJECT_OVER_X_AXIS); + RNA_def_property_ui_text(prop, "X", ""); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + prop = RNA_def_property(srna, "use_project_y", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "projAxis", MOD_BASSRELIEF_PROJECT_OVER_Y_AXIS); + RNA_def_property_ui_text(prop, "Y", ""); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + prop = RNA_def_property(srna, "use_project_z", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "projAxis", MOD_BASSRELIEF_PROJECT_OVER_Z_AXIS); + RNA_def_property_ui_text(prop, "Z", ""); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + prop = RNA_def_property(srna, "use_negative_direction", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "shrinkOpts", MOD_BASSRELIEF_PROJECT_ALLOW_NEG_DIR); + RNA_def_property_ui_text( + prop, "Negative", "Allow vertices to move in the negative direction of axis"); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + prop = RNA_def_property(srna, "use_positive_direction", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "shrinkOpts", MOD_BASSRELIEF_PROJECT_ALLOW_POS_DIR); + RNA_def_property_ui_text( + prop, "Positive", "Allow vertices to move in the positive direction of axis"); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + prop = RNA_def_property(srna, "use_invert_cull", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "shrinkOpts", MOD_BASSRELIEF_INVERT_CULL_TARGET); + RNA_def_property_ui_text( + prop, "Invert Cull", "When projecting in the negative direction invert the face cull mode"); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + prop = RNA_def_property(srna, "invert_vertex_group", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "shrinkOpts", MOD_BASSRELIEF_INVERT_VGROUP); + RNA_def_property_ui_text(prop, "Invert", "Invert vertex group influence"); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + prop = RNA_def_property(srna, "ray_shrink_ratio", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "rayShrinkRatio"); + RNA_def_property_ui_text( + prop, "Shrink Ratio", "Compress shrinkwrap result by this ratio; useful for reliefs"); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + prop = RNA_def_property(srna, "use_normal_optimizer", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "shrinkOpts", MOD_BASSRELIEF_OPTIMIZE); + RNA_def_property_ui_text(prop, + "Optimize", + "Try to optimize shading to match original mesh; useful for making " + "reliefs when combined with Shrink Ratio"); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + prop = RNA_def_property(srna, "optimizer_steps", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, NULL, "optimizeSteps"); + RNA_def_property_ui_text(prop, "Exaggeration Filter", ""); + RNA_def_property_range(prop, 0, 255); + RNA_def_property_ui_range(prop, 0, 10, 1, 1); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + prop = RNA_def_property(srna, "boundary_smooth_steps", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, NULL, "boundSmoothSteps"); + RNA_def_property_ui_text(prop, "Boundary Smooth", "Boundary smooth steps"); + RNA_def_property_range(prop, 0, 255); + RNA_def_property_ui_range(prop, 0, 10, 1, 1); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + prop = RNA_def_property(srna, "boundary_smooth_falloff", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "boundSmoothFalloff"); + RNA_def_property_ui_range(prop, 0.001, 4.0, 0.01, 3); + RNA_def_property_ui_text(prop, "Boundary Falloff", "Boundary width"); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + + prop = RNA_def_property(srna, "detail_scale", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "detailScale"); + RNA_def_property_ui_range(prop, 0.001, 4.0, 0.01, 3); + RNA_def_property_ui_text(prop, "Exaggeration Scale", ""); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + /* + uiItemR(col, ptr, "normal_optimizer_scale", 0, NULL, ICON_NONE); + uiItemR(col, ptr, "normal_optimizer_steps", 0, NULL, ICON_NONE); + uiItemR(col, ptr, "boundary_smooth_scale", 0, NULL, ICON_NONE); + uiItemR(col, ptr, "boundary_smooth_steps", 0, NULL, ICON_NONE); + + */ + RNA_define_lib_overridable(false); +} static void rna_def_modifier_mask(BlenderRNA *brna) { StructRNA *srna; @@ -4999,6 +5224,13 @@ static void rna_def_modifier_uvwarp(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Invert", "Invert vertex group influence"); RNA_def_property_update(prop, 0, "rna_Modifier_update"); + prop = RNA_def_property(srna, "restrict_to_islands", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", MOD_UVWARP_RESTRICT_ISLANDS); + RNA_def_property_ui_text(prop, + "Island Restrict", + "Don't affect UVs in faces outside of the vertex group's influence"); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + prop = RNA_def_property(srna, "uv_layer", PROP_STRING, PROP_NONE); RNA_def_property_string_sdna(prop, NULL, "uvlayer_name"); RNA_def_property_ui_text(prop, "UV Layer", "UV Layer name"); @@ -7421,6 +7653,7 @@ void RNA_def_modifier(BlenderRNA *brna) rna_def_modifier_mesh_to_volume(brna); rna_def_modifier_volume_displace(brna); rna_def_modifier_volume_to_mesh(brna); + rna_def_modifier_bassrelief(brna); } #endif diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c index d8cb588319d..486cd6d3b1d 100644 --- a/source/blender/makesrna/intern/rna_scene.c +++ b/source/blender/makesrna/intern/rna_scene.c @@ -3053,6 +3053,15 @@ static void rna_def_tool_settings(BlenderRNA *brna) RNA_def_struct_path_func(srna, "rna_ToolSettings_path"); RNA_def_struct_ui_text(srna, "Tool Settings", ""); + prop = RNA_def_property(srna, "save_temp_layers", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "save_temp_layers", 1); + RNA_def_property_ui_text(prop, "Save Temp Layers", ""); + + prop = RNA_def_property(srna, "show_origco", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "show_origco", 1); + RNA_def_property_ui_text(prop, "Show Original", ""); + + prop = RNA_def_property(srna, "sculpt", PROP_POINTER, PROP_NONE); RNA_def_property_struct_type(prop, "Sculpt"); RNA_def_property_ui_text(prop, "Sculpt", ""); @@ -3876,6 +3885,12 @@ static void rna_def_unified_paint_settings(BlenderRNA *brna) "Use Unified Radius", "Instead of per-brush radius, the radius is shared across brushes"); + /* high-level flags to enable or disable unified paint settings */ + prop = RNA_def_property(srna, "use_unified_hard_edge_mode", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", UNIFIED_PAINT_FLAG_HARD_EDGE_MODE); + RNA_def_property_ui_text( + prop, "Use Unified Hard Edge Mode", "Use global setting for hard edge mode"); + prop = RNA_def_property(srna, "use_unified_strength", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", UNIFIED_PAINT_ALPHA); RNA_def_property_ui_text(prop, @@ -3948,6 +3963,23 @@ static void rna_def_unified_paint_settings(BlenderRNA *brna) RNA_def_property_enum_items(prop, brush_size_unit_items); RNA_def_property_ui_text( prop, "Radius Unit", "Measure brush size relative to the view or the scene"); + + prop = RNA_def_property(srna, "hard_edge_mode", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "hard_edge_mode", 1); + RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE); + RNA_def_property_ui_text( + prop, "Hard Edge Mode", "Hard edge mode; treat all face set boundaries as hard edges"); + RNA_def_property_update(prop, 0, "rna_UnifiedPaintSettings_update"); + + prop = RNA_def_property(srna, "brush_editor_mode", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", UNIFIED_PAINT_FLAG_UI_ADVANCED); + RNA_def_property_ui_text(prop, "Workspace Edit Mode", "Brush editor mode"); + RNA_def_property_update(prop, NC_BRUSH | NA_EDITED, NULL); + + prop = RNA_def_property(srna, "brush_editor_advanced", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", 1 << 7); + RNA_def_property_ui_text(prop, "Show Advanced", "Show Advanced Settings"); + RNA_def_property_update(prop, NC_BRUSH | NA_EDITED, NULL); } static void rna_def_curve_paint_settings(BlenderRNA *brna) diff --git a/source/blender/makesrna/intern/rna_sculpt_paint.c b/source/blender/makesrna/intern/rna_sculpt_paint.c index 7c1cae1c44d..59f8c7a1edd 100644 --- a/source/blender/makesrna/intern/rna_sculpt_paint.c +++ b/source/blender/makesrna/intern/rna_sculpt_paint.c @@ -373,6 +373,8 @@ static bool rna_Brush_mode_with_tool_poll(PointerRNA *ptr, PointerRNA value) return brush->ob_mode & mode; } +void SCULPT_update_flat_vcol_shading(Object *ob, Scene *scene); + static void rna_Sculpt_update(bContext *C, PointerRNA *UNUSED(ptr)) { Scene *scene = CTX_data_scene(C); @@ -381,13 +383,16 @@ static void rna_Sculpt_update(bContext *C, PointerRNA *UNUSED(ptr)) Object *ob = BKE_view_layer_active_object_get(view_layer); if (ob) { - DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); - WM_main_add_notifier(NC_OBJECT | ND_MODIFIER, ob); - if (ob->sculpt) { + SCULPT_update_flat_vcol_shading(ob, scene); + ob->sculpt->bm_smooth_shading = ((scene->toolsettings->sculpt->flags & SCULPT_DYNTOPO_SMOOTH_SHADING) != 0); + ob->sculpt->fast_draw = ((scene->toolsettings->sculpt->flags & SCULPT_FAST_DRAW) != 0); } + + DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); + WM_main_add_notifier(NC_OBJECT | ND_MODIFIER | ND_DRAW, ob); } } @@ -554,6 +559,12 @@ static bool rna_ImaPaint_detect_data(ImagePaintSettings *imapaint) return imapaint->missing_data == 0; } +void SCULPT_substep_undo(bContext *ctx, int dir); +static void rna_SCULPT_substep_undo(bContext *ctx, int dir) +{ + SCULPT_substep_undo(ctx, dir); +} + static char *rna_GPencilSculptSettings_path(const PointerRNA *UNUSED(ptr)) { return BLI_strdup("tool_settings.gpencil_sculpt"); @@ -564,6 +575,24 @@ static char *rna_GPencilSculptGuide_path(const PointerRNA *UNUSED(ptr)) return BLI_strdup("tool_settings.gpencil_sculpt.guide"); } +bool SCULPT_has_persistent_base(SculptSession *ss); + +bool rna_Sculpt_has_persistent_base(bContext *C) +{ + ePaintMode mode = BKE_paintmode_get_active_from_context(C); + + if (mode != PAINT_MODE_SCULPT) { + return false; + } + + Object *ob = CTX_data_active_object(C); + if (!ob || ob->type != OB_MESH || !ob->sculpt) { + return false; + } + + return SCULPT_has_persistent_base(ob->sculpt); +} + static void rna_Sculpt_automasking_invert_cavity_set(PointerRNA *ptr, bool val) { Sculpt *sd = (Sculpt *)ptr->data; @@ -659,6 +688,11 @@ static void rna_def_paint(BlenderRNA *brna) prop, "Fast Navigate", "For multires, show low resolution while navigating the view"); RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); + prop = RNA_def_property(srna, "show_sculpt_pivot", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flags", PAINT_SCULPT_SHOW_PIVOT); + RNA_def_property_ui_text(prop, "Show Pivot", "Show Transform Tool Sculpt Pivot"); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); + prop = RNA_def_property(srna, "use_sculpt_delay_updates", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flags", PAINT_SCULPT_DELAY_UPDATES); RNA_def_property_ui_text( @@ -789,6 +823,20 @@ static void rna_def_sculpt(BlenderRNA *brna) {0, NULL, 0, NULL, NULL}, }; + static const EnumPropertyItem sculpt_transform_deform_target_items[] = { + {SCULPT_TRANSFORM_DEFORM_TARGET_GEOMETRY, + "GEOMETRY", + 0, + "Geometry", + "Transform displaces the vertices of the mesh"}, + {SCULPT_TRANSFORM_DEFORM_TARGET_CLOTH_SIM, + "CLOTH_SIM", + 0, + "Cloth Simulation", + "Transform displaces the positions of the cloth simulation"}, + {0, NULL, 0, NULL, NULL}, + }; + StructRNA *srna; PropertyRNA *prop; @@ -828,6 +876,11 @@ static void rna_def_sculpt(BlenderRNA *brna) RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE); RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_Sculpt_update"); + prop = RNA_def_property(srna, "use_dyntopo", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flags", SCULPT_DYNTOPO_ENABLED); + RNA_def_property_ui_text(prop, "DynTopo", "Enable DynTopo remesher in dynamic topology mode."); + RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE); + prop = RNA_def_property(srna, "detail_size", PROP_FLOAT, PROP_PIXEL); RNA_def_property_ui_range(prop, 0.5, 40.0, 0.1, 2); RNA_def_property_ui_scale_type(prop, PROP_SCALE_CUBIC); @@ -843,6 +896,21 @@ static void rna_def_sculpt(BlenderRNA *brna) "Maximum edge length for dynamic topology sculpting (in brush percenage)"); RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); + prop = RNA_def_property(srna, "dyntopo_spacing", PROP_INT, PROP_PERCENTAGE); + RNA_def_property_int_sdna(prop, NULL, "dyntopo_spacing"); + RNA_def_property_range(prop, 1, 1000); + RNA_def_property_ui_range(prop, 1, 500, 5, -1); + RNA_def_property_ui_text( + prop, "DynTopo Spacing", "Spacing between DynTopo daubs as a percentage of brush diameter"); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); + + prop = RNA_def_property(srna, "dyntopo_radius_scale", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "dyntopo_radius_scale"); + RNA_def_property_range(prop, 0.0001f, FLT_MAX); + RNA_def_property_ui_range(prop, 0.001f, 15.0f, 0.001f, 4.0f); + RNA_def_property_ui_text(prop, "Radius Scale", "Scale dyntopo brush radius"); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); + prop = RNA_def_property(srna, "constant_detail_resolution", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "constant_detail"); RNA_def_property_range(prop, 0.0001, FLT_MAX); @@ -862,6 +930,31 @@ static void rna_def_sculpt(BlenderRNA *brna) RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE); RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_Sculpt_update"); + prop = RNA_def_property(srna, "use_fast_draw", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flags", SCULPT_FAST_DRAW); + RNA_def_property_ui_text(prop, + "Fast Draw Mode", + "Forces smooth shading" + "and disables drawing of masks and face sets" + "to speed up drawing. Useful for posing" + "high-poly meshes."); + RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE); + RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_Sculpt_update"); + + prop = RNA_def_property(srna, "use_dyntopo_cleanup", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flags", SCULPT_DYNTOPO_CLEANUP); + RNA_def_property_ui_text(prop, "Cleanup", "Removes verts surrounded by only 3 or 4 edges"); + + prop = RNA_def_property(srna, "use_flat_vcol_shading", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flags", SCULPT_DYNTOPO_FLAT_VCOL_SHADING); + RNA_def_property_ui_text( + prop, + "Draw Color Cells", + "Draw vertex colors in flat cells instead of smoothly interpolating." + "For debugging purposes only, does not effect rendering in eevee or cycles"); + RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE); + RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_Sculpt_update"); + const EnumPropertyItem *entry = rna_enum_brush_automasking_flag_items; do { prop = RNA_def_property(srna, entry->identifier, PROP_BOOLEAN, PROP_NONE); @@ -981,17 +1074,44 @@ static void rna_def_sculpt(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Gravity", "Amount of gravity after each dab"); RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); + prop = RNA_def_property(srna, "smooth_strength_factor", PROP_FLOAT, PROP_FACTOR); + RNA_def_property_float_sdna(prop, NULL, "smooth_strength_factor"); + RNA_def_property_range(prop, 0.0f, 10.0f); + RNA_def_property_ui_range(prop, 0.0f, 2.0f, 0.1, 3); + RNA_def_property_ui_text( + prop, "Smooth Strength", "Factor to control the strength of alt-smooth"); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); + prop = RNA_def_property(srna, "transform_mode", PROP_ENUM, PROP_NONE); RNA_def_property_enum_items(prop, sculpt_transform_mode_items); RNA_def_property_ui_text( prop, "Transform Mode", "How the transformation is going to be applied to the target"); RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); + prop = RNA_def_property(srna, "transform_deform_target", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, sculpt_transform_deform_target_items); + RNA_def_property_ui_text( + prop, "Deformation Target", "Target for the displacement of the transformation"); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); + prop = RNA_def_property(srna, "gravity_object", PROP_POINTER, PROP_NONE); RNA_def_property_flag(prop, PROP_EDITABLE); RNA_def_property_ui_text( prop, "Orientation", "Object whose Z axis defines orientation of gravity"); RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); + + /* functions */ + FunctionRNA *func; + + func = RNA_def_function(srna, "has_persistent_base", "rna_Sculpt_has_persistent_base"); + RNA_def_function_ui_description(func, "Test if sculpt has persistent base (sculpt mode only)"); + RNA_def_function_flag(func, FUNC_USE_CONTEXT | FUNC_NO_SELF); + RNA_def_function_return(func, RNA_def_boolean(func, "has", 1, "Has persistent Base", "")); + + func = RNA_def_function(srna, "debug_substep_undo", "rna_SCULPT_substep_undo"); + RNA_def_function_ui_description(func, "Test function"); + RNA_def_function_flag(func, FUNC_NO_SELF | FUNC_USE_CONTEXT); + RNA_def_int(func, "dir", -1, -1, 1, "dir", "dir", -1, 1); } static void rna_def_uv_sculpt(BlenderRNA *brna) diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c index 7bdbfe75684..0a4503c3e94 100644 --- a/source/blender/makesrna/intern/rna_space.c +++ b/source/blender/makesrna/intern/rna_space.c @@ -480,6 +480,7 @@ const EnumPropertyItem rna_enum_clip_editor_mode_items[] = { * but helps for context-less access (e.g. doc, i18n...). */ static const EnumPropertyItem buttons_context_items[] = { {BCONTEXT_TOOL, "TOOL", ICON_TOOL_SETTINGS, "Tool", "Active Tool and Workspace settings"}, + {BCONTEXT_BRUSH_EDITOR, "BRUSH_EDITOR", ICON_BRUSH_DATA, "Brush", "Brush Settings"}, {BCONTEXT_SCENE, "SCENE", ICON_SCENE_DATA, "Scene", "Scene Properties"}, {BCONTEXT_RENDER, "RENDER", ICON_SCENE, "Render", "Render Properties"}, {BCONTEXT_OUTPUT, "OUTPUT", ICON_OUTPUT, "Output", "Output Properties"}, @@ -3394,8 +3395,9 @@ static struct IDFilterEnumPropertyItem rna_enum_space_file_id_filter_categories[ ICON_WORLD_DATA, "Environment", "Show worlds, lights, cameras and speakers"}, - {FILTER_ID_BR | FILTER_ID_GD | FILTER_ID_PA | FILTER_ID_PAL | FILTER_ID_PC | FILTER_ID_TXT | - FILTER_ID_VF | FILTER_ID_CF | FILTER_ID_WS, + {FILTER_ID_BR, "category_brush", ICON_GREASEPENCIL, "Brushes", "Show brushes"}, + {FILTER_ID_GD | FILTER_ID_PA | FILTER_ID_PAL | FILTER_ID_PC | FILTER_ID_TXT | FILTER_ID_VF | + FILTER_ID_CF | FILTER_ID_WS, "category_misc", ICON_GREASEPENCIL, "Miscellaneous", @@ -4720,6 +4722,22 @@ static void rna_def_space_view3d_overlay(BlenderRNA *brna) RNA_def_property_range(prop, 0.0f, 1.0f); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); + prop = RNA_def_property(srna, "sculpt_mode_face_sets_moire_seed", PROP_FLOAT, PROP_FACTOR); + RNA_def_property_float_sdna(prop, NULL, "overlay.sculpt_mode_face_sets_moire_seed"); + RNA_def_property_ui_text(prop, "Pattern Seed", "Sculpt Face Sets Pattern Seed"); + RNA_def_property_range(prop, 0.0f, 1.0f); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); + + prop = RNA_def_property(srna, "sculpt_mode_use_face_set_moire", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "overlay.sculpt_flag", V3D_OVERLAY_SCULPT_FSET_MOIRE); + RNA_def_property_ui_text(prop, "Moire", "Draw face sets with moire pattern"); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); + + prop = RNA_def_property(srna, "sculpt_mode_face_sets_moire_scale", PROP_FLOAT, PROP_FACTOR); + RNA_def_property_float_sdna(prop, NULL, "overlay.sculpt_mode_face_sets_moire_scale"); + RNA_def_property_ui_text(prop, "Pattern Scale", "Sculpt Face Sets Pattern Scale"); + RNA_def_property_range(prop, 0.0f, 1.0f); + prop = RNA_def_property(srna, "sculpt_show_mask", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "overlay.flag", V3D_OVERLAY_SCULPT_SHOW_MASK); RNA_def_property_ui_text(prop, "Sculpt Show Mask", ""); diff --git a/source/blender/makesrna/intern/rna_userdef.c b/source/blender/makesrna/intern/rna_userdef.c index 347211f5246..0da54fca366 100644 --- a/source/blender/makesrna/intern/rna_userdef.c +++ b/source/blender/makesrna/intern/rna_userdef.c @@ -6431,6 +6431,16 @@ static void rna_def_userdef_experimental(BlenderRNA *brna) RNA_def_property_ui_text( prop, "Override Templates", "Enable library override template in the python API"); + prop = RNA_def_property(srna, "use_sculpt_uvsmooth", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "use_sculpt_uvsmooth", 1); + RNA_def_property_ui_text(prop, "Sculpt UV Smooth", "Enable UV smooth sculpt brush"); + + prop = RNA_def_property(srna, "use_named_attribute_nodes", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "use_named_attribute_nodes", 1); + RNA_def_property_ui_text(prop, + "Named Attribute Nodes", + "Enable named attribute nodes in the geometry nodes add menu"); + prop = RNA_def_property(srna, "enable_eevee_next", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "enable_eevee_next", 1); RNA_def_property_ui_text(prop, "EEVEE Next", "Enable the new EEVEE codebase, requires restart"); diff --git a/source/blender/makesrna/intern/rna_wm_api.c b/source/blender/makesrna/intern/rna_wm_api.c index b82458c4442..e2fa088b4a7 100644 --- a/source/blender/makesrna/intern/rna_wm_api.c +++ b/source/blender/makesrna/intern/rna_wm_api.c @@ -61,6 +61,8 @@ const EnumPropertyItem rna_enum_window_cursor_items[] = { #ifdef RNA_RUNTIME +void wm_autosave_write(Main *bmain, wmWindowManager *wm); + # include "BKE_context.h" # include "BKE_undo_system.h" @@ -114,6 +116,11 @@ static bool rna_event_modal_handler_add(struct bContext *C, struct wmOperator *o return WM_event_add_modal_handler(C, operator) != NULL; } +static void rna_WindowManager_autosave_write(struct wmWindowManager *wm, Main *main) +{ + wm_autosave_write(main, wm); +} + /* XXX, need a way for python to know event types, 0x0110 is hard coded */ static wmTimer *rna_event_timer_add(struct wmWindowManager *wm, float time_step, wmWindow *win) { @@ -763,6 +770,10 @@ void RNA_api_wm(StructRNA *srna) FunctionRNA *func; PropertyRNA *parm; + func = RNA_def_function(srna, "autosave_write", "rna_WindowManager_autosave_write"); + RNA_def_function_ui_description(func, "Force autosave "); + RNA_def_function_flag(func, FUNC_USE_MAIN); + func = RNA_def_function(srna, "fileselect_add", "WM_event_add_fileselect"); RNA_def_function_ui_description( func, diff --git a/source/blender/modifiers/CMakeLists.txt b/source/blender/modifiers/CMakeLists.txt index a0cf583153b..be93ea9dfa6 100644 --- a/source/blender/modifiers/CMakeLists.txt +++ b/source/blender/modifiers/CMakeLists.txt @@ -21,6 +21,7 @@ set(INC ../windowmanager ../../../intern/eigen ../../../intern/guardedalloc + ../../../intern/atomic # dna_type_offsets.h in BLO_read_write.h ${CMAKE_BINARY_DIR}/source/blender/makesdna/intern diff --git a/source/blender/modifiers/intern/MOD_bevel.c b/source/blender/modifiers/intern/MOD_bevel.c index f49888a17fd..f509077d67a 100644 --- a/source/blender/modifiers/intern/MOD_bevel.c +++ b/source/blender/modifiers/intern/MOD_bevel.c @@ -108,7 +108,8 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * const float spread = bmd->spread; const bool invert_vgroup = (bmd->flags & MOD_BEVEL_INVERT_VGROUP) != 0; - bm = BKE_mesh_to_bmesh_ex(mesh, + bm = BKE_mesh_to_bmesh_ex(NULL, + mesh, &(struct BMeshCreateParams){0}, &(struct BMeshFromMeshParams){ .calc_face_normal = true, diff --git a/source/blender/modifiers/intern/MOD_boolean.cc b/source/blender/modifiers/intern/MOD_boolean.cc index 453c66c26a6..a8ce96f6bb2 100644 --- a/source/blender/modifiers/intern/MOD_boolean.cc +++ b/source/blender/modifiers/intern/MOD_boolean.cc @@ -248,7 +248,7 @@ static BMesh *BMD_mesh_bm_create( BMeshFromMeshParams bmesh_from_mesh_params{}; bmesh_from_mesh_params.calc_face_normal = true; bmesh_from_mesh_params.calc_vert_normal = true; - BM_mesh_bm_from_me(bm, mesh_operand_ob, &bmesh_from_mesh_params); + BM_mesh_bm_from_me(nullptr, bm, mesh_operand_ob, &bmesh_from_mesh_params); if (UNLIKELY(*r_is_flip)) { const int cd_loop_mdisp_offset = CustomData_get_offset(&bm->ldata, CD_MDISPS); @@ -259,7 +259,7 @@ static BMesh *BMD_mesh_bm_create( } } - BM_mesh_bm_from_me(bm, mesh, &bmesh_from_mesh_params); + BM_mesh_bm_from_me(nullptr, bm, mesh, &bmesh_from_mesh_params); return bm; } @@ -589,7 +589,7 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * else { BMeshToMeshParams bmesh_to_mesh_params{}; bmesh_to_mesh_params.calc_object_remap = false; - BM_mesh_bm_to_me(nullptr, bm, result, &bmesh_to_mesh_params); + BM_mesh_bm_to_me(nullptr, nullptr, bm, result, &bmesh_to_mesh_params); } BM_mesh_free(bm); } diff --git a/source/blender/modifiers/intern/MOD_correctivesmooth.cc b/source/blender/modifiers/intern/MOD_correctivesmooth.cc index 5a1d4dc637a..ab784038464 100644 --- a/source/blender/modifiers/intern/MOD_correctivesmooth.cc +++ b/source/blender/modifiers/intern/MOD_correctivesmooth.cc @@ -576,7 +576,7 @@ static void correctivesmooth_modifier_do(ModifierData *md, /* XXX, take care! if mesh data itself changes we need to forcefully recalculate deltas */ !cache_settings_equal(csmd) || ((csmd->rest_source == MOD_CORRECTIVESMOOTH_RESTSOURCE_ORCO) && - (((ID *)ob->data)->recalc & ID_RECALC_ALL)); + (((ID *)ob->data)->recalc & (unsigned int)ID_RECALC_ALL)); const MLoop *mloop = BKE_mesh_loops(mesh); const uint loops_num = uint(mesh->totloop); diff --git a/source/blender/modifiers/intern/MOD_decimate.c b/source/blender/modifiers/intern/MOD_decimate.c index cf698669397..a7174aa4493 100644 --- a/source/blender/modifiers/intern/MOD_decimate.c +++ b/source/blender/modifiers/intern/MOD_decimate.c @@ -158,7 +158,8 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * } } - bm = BKE_mesh_to_bmesh_ex(mesh, + bm = BKE_mesh_to_bmesh_ex(NULL, + mesh, &(struct BMeshCreateParams){0}, &(struct BMeshFromMeshParams){ .calc_face_normal = calc_face_normal, diff --git a/source/blender/modifiers/intern/MOD_edgesplit.c b/source/blender/modifiers/intern/MOD_edgesplit.c index 7db6c5e172d..a2bb42f1397 100644 --- a/source/blender/modifiers/intern/MOD_edgesplit.c +++ b/source/blender/modifiers/intern/MOD_edgesplit.c @@ -53,7 +53,8 @@ Mesh *doEdgeSplit(const Mesh *mesh, EdgeSplitModifierData *emd) const bool do_split_all = do_split_angle && emd->split_angle < FLT_EPSILON; const bool calc_face_normals = do_split_angle && !do_split_all; - bm = BKE_mesh_to_bmesh_ex(mesh, + bm = BKE_mesh_to_bmesh_ex(NULL, + mesh, &(struct BMeshCreateParams){0}, &(struct BMeshFromMeshParams){ .calc_face_normal = calc_face_normals, diff --git a/source/blender/modifiers/intern/MOD_skin.c b/source/blender/modifiers/intern/MOD_skin.c index e3fbd072f36..7645673f119 100644 --- a/source/blender/modifiers/intern/MOD_skin.c +++ b/source/blender/modifiers/intern/MOD_skin.c @@ -1929,7 +1929,7 @@ static Mesh *base_skin(Mesh *origmesh, SkinModifierData *smd, eSkinErrorFlag *r_ verts_num = origmesh->totvert; edges_num = origmesh->totedge; - BKE_mesh_vert_edge_map_create(&emap, &emapmem, medge, verts_num, edges_num); + BKE_mesh_vert_edge_map_create(&emap, &emapmem, NULL, medge, verts_num, edges_num, false); emat = build_edge_mats( nodes, vert_positions, verts_num, medge, emap, edges_num, &has_valid_root); diff --git a/source/blender/modifiers/intern/MOD_triangulate.cc b/source/blender/modifiers/intern/MOD_triangulate.cc index 66ffce98c65..f5735ccc5f9 100644 --- a/source/blender/modifiers/intern/MOD_triangulate.cc +++ b/source/blender/modifiers/intern/MOD_triangulate.cc @@ -63,7 +63,7 @@ static Mesh *triangulate_mesh(Mesh *mesh, bmesh_from_mesh_params.calc_vert_normal = false; bmesh_from_mesh_params.cd_mask_extra = cd_mask_extra; - bm = BKE_mesh_to_bmesh_ex(mesh, &bmesh_create_params, &bmesh_from_mesh_params); + bm = BKE_mesh_to_bmesh_ex(nullptr, mesh, &bmesh_create_params, &bmesh_from_mesh_params); BM_mesh_triangulate( bm, quad_method, ngon_method, min_vertices, false, nullptr, nullptr, nullptr); diff --git a/source/blender/modifiers/intern/MOD_wireframe.c b/source/blender/modifiers/intern/MOD_wireframe.c index 8339e1cea76..5bc621ff65a 100644 --- a/source/blender/modifiers/intern/MOD_wireframe.c +++ b/source/blender/modifiers/intern/MOD_wireframe.c @@ -63,7 +63,8 @@ static Mesh *WireframeModifier_do(WireframeModifierData *wmd, Object *ob, Mesh * const int defgrp_index = BKE_id_defgroup_name_index(&mesh->id, wmd->defgrp_name); - bm = BKE_mesh_to_bmesh_ex(mesh, + bm = BKE_mesh_to_bmesh_ex(NULL, + mesh, &(struct BMeshCreateParams){0}, &(struct BMeshFromMeshParams){ .calc_face_normal = true, diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index e6c0510e9f5..6c902267bfc 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -29,6 +29,7 @@ set(INC ../makesrna ../render ../windowmanager + ../../../intern/atomic ../../../intern/guardedalloc # dna_type_offsets.h diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_ico_sphere.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_ico_sphere.cc index d1387ca5cdc..faee393f04f 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_ico_sphere.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_ico_sphere.cc @@ -58,7 +58,8 @@ static Mesh *create_ico_sphere_mesh(const int subdivisions, params.calc_object_remap = false; Mesh *mesh = reinterpret_cast(BKE_id_new_nomain(ID_ME, nullptr)); BKE_id_material_eval_ensure_default_slot(&mesh->id); - BM_mesh_bm_to_me(nullptr, bm, mesh, ¶ms); + BM_mesh_bm_to_me(nullptr, nullptr, bm, mesh, ¶ms); + BM_mesh_free(bm); /* The code above generates a "UVMap" attribute. The code below renames that attribute, we don't diff --git a/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc b/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc index 52c7dbf0605..4e6263061d9 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc @@ -48,7 +48,7 @@ static Mesh *triangulate_mesh_selection(const Mesh &mesh, from_mesh_params.calc_face_normal = true; from_mesh_params.calc_vert_normal = true; from_mesh_params.cd_mask_extra = cd_mask_extra; - BMesh *bm = BKE_mesh_to_bmesh_ex(&mesh, &create_params, &from_mesh_params); + BMesh *bm = BKE_mesh_to_bmesh_ex(nullptr, &mesh, &create_params, &from_mesh_params); /* Tag faces to be triangulated from the selection mask. */ BM_mesh_elem_table_ensure(bm, BM_FACE); diff --git a/source/blender/nodes/texture/CMakeLists.txt b/source/blender/nodes/texture/CMakeLists.txt index 364230ad068..a6965a1e0c9 100644 --- a/source/blender/nodes/texture/CMakeLists.txt +++ b/source/blender/nodes/texture/CMakeLists.txt @@ -15,6 +15,7 @@ set(INC ../../render ../../windowmanager ../../../../intern/guardedalloc + ../../../../intern/atomic ../../bmesh # RNA_prototypes.h ${CMAKE_BINARY_DIR}/source/blender/makesrna diff --git a/source/blender/python/bmesh/bmesh_py_types.c b/source/blender/python/bmesh/bmesh_py_types.c index e218c2384f5..13568ae9e35 100644 --- a/source/blender/python/bmesh/bmesh_py_types.c +++ b/source/blender/python/bmesh/bmesh_py_types.c @@ -5,6 +5,8 @@ * \ingroup pybmesh */ +#include "MEM_guardedalloc.h" + #include "BLI_math.h" #include "BLI_sort.h" #include "BLI_string.h" @@ -1041,7 +1043,7 @@ static PyObject *bpy_bmesh_to_mesh(BPy_BMesh *self, PyObject *args) params.calc_object_remap = true; } - BM_mesh_bm_to_me(bmain, bm, me, ¶ms); + BM_mesh_bm_to_me(bmain, NULL, bm, me, ¶ms); /* we could have the user do this but if they forget blender can easy crash * since the references arrays for the objects derived meshes are now invalid */ @@ -1141,7 +1143,8 @@ static PyObject *bpy_bmesh_from_object(BPy_BMesh *self, PyObject *args, PyObject bm = self->bm; - BM_mesh_bm_from_me(bm, + BM_mesh_bm_from_me(NULL, + bm, me_eval, (&(struct BMeshFromMeshParams){ .calc_face_normal = use_fnorm, @@ -1208,7 +1211,8 @@ static PyObject *bpy_bmesh_from_mesh(BPy_BMesh *self, PyObject *args, PyObject * bm = self->bm; - BM_mesh_bm_from_me(bm, + BM_mesh_bm_from_me(NULL, + bm, me, (&(struct BMeshFromMeshParams){ .calc_face_normal = use_fnorm, @@ -2778,7 +2782,7 @@ static PyObject *bpy_bmelemseq_sort(BPy_BMElemSeq *self, PyObject *args, PyObjec return NULL; } - BM_mesh_remap(bm, vert_idx, edge_idx, face_idx); + BM_mesh_remap(bm, vert_idx, edge_idx, face_idx, NULL); PyMem_FREE(elem_map_idx); PyMem_FREE(elem_idx); @@ -4045,6 +4049,134 @@ int bpy_bm_generic_valid_check_source(BMesh *bm_source, return ret; } +void *BPy_bm_new_customdata_layout_pre(BMesh *bm, CustomData *cdata, char htype) +{ + if (!CustomData_has_layer(cdata, CD_BM_ELEM_PYPTR)) { + return NULL; + } + + BLI_mempool *pool = NULL; + int num = 0; + + switch (htype) { + case BM_VERT: + pool = bm->vpool; + num = bm->totvert; + break; + case BM_EDGE: + pool = bm->epool; + num = bm->totedge; + break; + case BM_LOOP: + pool = bm->fpool; + num = bm->totloop; + break; + case BM_FACE: + pool = bm->fpool; + num = bm->totface; + break; + } + + void **ptrs = MEM_malloc_arrayN(num, sizeof(void *), __func__); + + int cd_py = CustomData_get_offset(cdata, CD_BM_ELEM_PYPTR); + + BMElem *elem; + BLI_mempool_iter iter; + + int i = 0; + + if (htype != BM_LOOP) { + BLI_mempool_iternew(pool, &iter); + while ((elem = BLI_mempool_iterstep(&iter))) { + ptrs[i++] = *((void **)BM_ELEM_CD_GET_VOID_P(elem, cd_py)); + } + } + else { + BMFace *f; + + BLI_mempool_iternew(pool, &iter); + while ((f = BLI_mempool_iterstep(&iter))) { + BMLoop *l = f->l_first; + + do { + ptrs[i++] = *((void **)BM_ELEM_CD_GET_VOID_P(l, cd_py)); + } while ((l = l->next) != f->l_first); + } + } + + return ptrs; +} + +void BPy_bm_new_customdata_layout(BMesh *bm, CustomData *cdata, void *state, char htype) +{ + /* + * Un-invalidate python pointers, which got invalidated when the customdata layout + * changed. + */ + + if (!state || !CustomData_has_layer(cdata, CD_BM_ELEM_PYPTR)) { + return; + } + + BLI_mempool *pool = NULL; + void **ptrs = (void **)state; + + switch (htype) { + case BM_VERT: + pool = bm->vpool; + break; + case BM_EDGE: + pool = bm->epool; + break; + case BM_LOOP: + pool = bm->fpool; + break; + case BM_FACE: + pool = bm->fpool; + break; + } + + int cd_py = CustomData_get_offset(cdata, CD_BM_ELEM_PYPTR); + + BMElem *elem; + BLI_mempool_iter iter; + int i = 0; + + if (htype != BM_LOOP) { + BLI_mempool_iternew(pool, &iter); + while ((elem = BLI_mempool_iterstep(&iter))) { + void **ptr = BM_ELEM_CD_GET_VOID_P(elem, cd_py); + + *ptr = ptrs[i++]; + BPy_BMGeneric *bpy_ptr = (BPy_BMGeneric *)*ptr; + + if (bpy_ptr) { + bpy_ptr->bm = bm; + } + } + } + else { + BMFace *f; + + BLI_mempool_iternew(pool, &iter); + while ((f = BLI_mempool_iterstep(&iter))) { + BMLoop *l = f->l_first; + + do { + void **ptr = BM_ELEM_CD_GET_VOID_P(l, cd_py); + + *ptr = ptrs[i++]; + BPy_BMGeneric *bpy_ptr = (BPy_BMGeneric *)*ptr; + + if (bpy_ptr) { + bpy_ptr->bm = bm; + } + } while ((l = l->next) != f->l_first); + } + } +} + void bpy_bm_generic_invalidate(BPy_BMGeneric *self) { self->bm = NULL; diff --git a/source/blender/python/bmesh/bmesh_py_types_customdata.c b/source/blender/python/bmesh/bmesh_py_types_customdata.c index e1708037095..f10bd5473f1 100644 --- a/source/blender/python/bmesh/bmesh_py_types_customdata.c +++ b/source/blender/python/bmesh/bmesh_py_types_customdata.c @@ -68,6 +68,8 @@ PyDoc_STRVAR(bpy_bmlayeraccess_collection__float_doc, "Generic float custom-data layer.\n\ntype: :class:`BMLayerCollection`"); PyDoc_STRVAR(bpy_bmlayeraccess_collection__int_doc, "Generic int custom-data layer.\n\ntype: :class:`BMLayerCollection`"); +PyDoc_STRVAR(bpy_bmlayeraccess_collection__face_set_doc, + "Sculpt face set custom-data layer.\n\ntype: :class:`BMLayerCollection`"); PyDoc_STRVAR(bpy_bmlayeraccess_collection__float_vector_doc, "Generic 3D vector with float precision custom-data layer.\n\ntype: " ":class:`BMLayerCollection`"); diff --git a/source/blender/render/RE_bake.h b/source/blender/render/RE_bake.h index 717c5053a38..6383e5a15b1 100644 --- a/source/blender/render/RE_bake.h +++ b/source/blender/render/RE_bake.h @@ -13,6 +13,7 @@ struct Depsgraph; struct ImBuf; struct Mesh; struct Render; +struct MLoopUV; #ifdef __cplusplus extern "C" { diff --git a/source/blender/render/intern/bake.cc b/source/blender/render/intern/bake.cc index dc2eb26a70e..365fc9e658f 100644 --- a/source/blender/render/intern/bake.cc +++ b/source/blender/render/intern/bake.cc @@ -949,6 +949,12 @@ void RE_bake_normal_world_to_tangent(const BakePixel pixel_array[], /* converts from world space to local space */ mul_transposed_mat3_m4_v3(mat, nor); + normalize_v3(nor); + + if (dot_v3v3(nor, normal) < 0.0f) { + negate_v3(nor); + } + invert_m3_m3(itsm, tsm); mul_m3_v3(itsm, nor); normalize_v3(nor); diff --git a/source/blender/windowmanager/CMakeLists.txt b/source/blender/windowmanager/CMakeLists.txt index 2db80049e05..f5a8a359815 100644 --- a/source/blender/windowmanager/CMakeLists.txt +++ b/source/blender/windowmanager/CMakeLists.txt @@ -23,6 +23,7 @@ set(INC ../render ../sequencer ../../../intern/clog + ../../../intern/atomic ../../../intern/ghost ../../../intern/guardedalloc ../../../intern/memutil diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h index 0c12bd15b5b..b4c3cba36f4 100644 --- a/source/blender/windowmanager/WM_api.h +++ b/source/blender/windowmanager/WM_api.h @@ -35,6 +35,7 @@ struct ImBuf; struct ImageFormatData; struct Main; struct MenuType; +struct PBVH; struct PointerRNA; struct PropertyRNA; struct ScrArea; diff --git a/source/blender/windowmanager/intern/wm_draw.c b/source/blender/windowmanager/intern/wm_draw.c index be1bdd66560..81fb1fa355f 100644 --- a/source/blender/windowmanager/intern/wm_draw.c +++ b/source/blender/windowmanager/intern/wm_draw.c @@ -27,6 +27,8 @@ #include "BKE_global.h" #include "BKE_image.h" #include "BKE_main.h" +#include "BKE_paint.h" +#include "BKE_pbvh.h" #include "BKE_scene.h" #include "BKE_screen.h" diff --git a/source/blender/windowmanager/intern/wm_files.cc b/source/blender/windowmanager/intern/wm_files.cc index f9555e75005..90e5ee68747 100644 --- a/source/blender/windowmanager/intern/wm_files.cc +++ b/source/blender/windowmanager/intern/wm_files.cc @@ -61,6 +61,7 @@ #include "BKE_appdir.h" #include "BKE_autoexec.h" #include "BKE_blender.h" +#include "BKE_blender_undo.h" #include "BKE_blendfile.h" #include "BKE_callbacks.h" #include "BKE_context.h" @@ -71,7 +72,9 @@ #include "BKE_lib_remap.h" #include "BKE_main.h" #include "BKE_main_namemap.h" +#include "BKE_multires.h" #include "BKE_packedFile.h" +#include "BKE_paint.h" #include "BKE_report.h" #include "BKE_scene.h" #include "BKE_screen.h" @@ -1963,7 +1966,12 @@ static void wm_autosave_location(char filepath[FILE_MAX]) BLI_path_join(filepath, FILE_MAX, tempdir_base, path); } -static void wm_autosave_write(Main *bmain, wmWindowManager *wm) +/* TODO: Move to appropriate headers */ +void ED_sculpt_fast_save_bmesh(Object *ob); +struct MemFileUndoStep; +MemFileUndoData *memfile_get_step_data(struct MemFileUndoStep *us); + +extern "C" void wm_autosave_write(Main *bmain, wmWindowManager *wm) { char filepath[FILE_MAX]; @@ -1972,8 +1980,41 @@ static void wm_autosave_write(Main *bmain, wmWindowManager *wm) /* Fast save of last undo-buffer, now with UI. */ const bool use_memfile = (U.uiflag & USER_GLOBALUNDO) != 0; MemFile *memfile = use_memfile ? ED_undosys_stack_memfile_get_active(wm->undo_stack) : nullptr; + bool update = false; + + LISTBASE_FOREACH (Object *, ob, &bmain->objects) { + if (ob->mode != OB_MODE_SCULPT || !ob->sculpt) { + continue; + } + + /* Flush sculpt data to the mesh, we will append it to the undo memfile. */ + if (ob->sculpt->bm) { + ED_sculpt_fast_save_bmesh(ob); + } + else { + multires_flush_sculpt_updates(ob); + } + + update = true; + } + + MemFileUndoData *mus = NULL; + + if (update && memfile) { + UndoStep *us = BKE_undosys_stack_active_with_type(wm->undo_stack, BKE_UNDOSYS_TYPE_MEMFILE); + + if (us) { + mus = memfile_get_step_data((struct MemFileUndoStep *)us); + mus = BKE_memfile_undo_encode(bmain, mus); + memfile = &mus->memfile; + } + } if (memfile != nullptr) { BLO_memfile_write_file(memfile, filepath); + + if (update) { + BKE_memfile_undo_free(mus); + } } else { if (use_memfile) { diff --git a/source/blender/windowmanager/intern/wm_operators.c b/source/blender/windowmanager/intern/wm_operators.c index 8ffaf8a0603..9686b58679c 100644 --- a/source/blender/windowmanager/intern/wm_operators.c +++ b/source/blender/windowmanager/intern/wm_operators.c @@ -3936,6 +3936,7 @@ static void gesture_box_modal_keymap(wmKeyConfig *keyconf) WM_modalkeymap_assign(keymap, "MASK_OT_select_box"); WM_modalkeymap_assign(keymap, "PAINT_OT_mask_box_gesture"); WM_modalkeymap_assign(keymap, "SCULPT_OT_face_set_box_gesture"); + WM_modalkeymap_assign(keymap, "SCULPT_OT_project_box_gesture"); WM_modalkeymap_assign(keymap, "SCULPT_OT_trim_box_gesture"); WM_modalkeymap_assign(keymap, "VIEW2D_OT_zoom_border"); WM_modalkeymap_assign(keymap, "VIEW3D_OT_clip_border"); diff --git a/source/blender/windowmanager/intern/wm_toolsystem.c b/source/blender/windowmanager/intern/wm_toolsystem.c index 6e08f607bae..da82cd81849 100644 --- a/source/blender/windowmanager/intern/wm_toolsystem.c +++ b/source/blender/windowmanager/intern/wm_toolsystem.c @@ -17,6 +17,7 @@ #include "BLI_utildefines.h" #include "DNA_ID.h" +#include "DNA_brush_types.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" #include "DNA_space_types.h" @@ -177,6 +178,7 @@ static void toolsystem_ref_link(bContext *C, WorkSpace *workspace, bToolRef *tre } else { const ePaintMode paint_mode = BKE_paintmode_get_from_tool(tref); + const eObjectMode ob_paint_mode = BKE_paint_object_mode_from_paintmode(paint_mode); BLI_assert(paint_mode != PAINT_MODE_INVALID); const EnumPropertyItem *items = BKE_paint_get_tool_enum_from_paintmode(paint_mode); BLI_assert(items != NULL); @@ -194,7 +196,8 @@ static void toolsystem_ref_link(bContext *C, WorkSpace *workspace, bToolRef *tre if (brush == NULL) { /* Could make into a function. */ brush = (struct Brush *)BKE_libblock_find_name(bmain, ID_BR, items[i].name); - if (brush && slot_index == BKE_brush_tool_get(brush, paint)) { + if (brush && (brush->ob_mode & ob_paint_mode) && + slot_index == BKE_brush_tool_get(brush, paint)) { /* pass */ } else { diff --git a/source/creator/creator.c b/source/creator/creator.c index 63998b7b4c2..1ec4e24ac0b 100644 --- a/source/creator/creator.c +++ b/source/creator/creator.c @@ -273,6 +273,10 @@ void gmp_blender_init_allocator() * - run #WM_main() event loop, * or exit immediately when running in background-mode. */ + +void BKE_pbvh_system_init(void); +void BKE_pbvh_system_exit(void); + int main(int argc, #ifdef USE_WIN32_UNICODE_ARGS const char **UNUSED(argv_c) @@ -326,7 +330,7 @@ int main(int argc, LocalFree(argv_16); /* free on early-exit */ - app_init_data.argv = argv; + app_init_data.argv = (const char **)argv; app_init_data.argv_num = argv_num; } # endif /* USE_WIN32_UNICODE_ARGS */ @@ -440,6 +444,7 @@ int main(int argc, DEG_register_node_types(); BKE_brush_system_init(); + BKE_pbvh_system_init(); RE_texture_rng_init(); BKE_callback_global_init(); -- 2.30.2 From 3f59466c2968a3e785761133aa0441c95388bcc8 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Sat, 11 Feb 2023 01:52:57 -0800 Subject: [PATCH 002/279] temp-sculpt-dyntopo: Fix compile error --- release/datafiles/locale | 2 +- release/scripts/addons | 2 +- release/scripts/addons_contrib | 2 +- source/blender/blenkernel/intern/mesh_fair.cc | 1 - source/blender/editors/sculpt_paint/sculpt.cc | 1 - source/blender/editors/sculpt_paint/sculpt_dyntopo.cc | 2 ++ source/blender/editors/sculpt_paint/sculpt_face_set.cc | 1 - source/tools | 2 +- 8 files changed, 6 insertions(+), 7 deletions(-) diff --git a/release/datafiles/locale b/release/datafiles/locale index 4331c8e76c2..7084c4ecd97 160000 --- a/release/datafiles/locale +++ b/release/datafiles/locale @@ -1 +1 @@ -Subproject commit 4331c8e76c2f42b9fd903716c333d6cdeaa5cebd +Subproject commit 7084c4ecd97d93459d9d23fd90f81589b09be5df diff --git a/release/scripts/addons b/release/scripts/addons index b3f0ffc587d..a9d4443c244 160000 --- a/release/scripts/addons +++ b/release/scripts/addons @@ -1 +1 @@ -Subproject commit b3f0ffc587d197b37eac9a1566d1d24b7bee7d9a +Subproject commit a9d4443c244f89399ec4bcc427e05a07950528cc diff --git a/release/scripts/addons_contrib b/release/scripts/addons_contrib index 14ab9273409..bdcfdd47ec3 160000 --- a/release/scripts/addons_contrib +++ b/release/scripts/addons_contrib @@ -1 +1 @@ -Subproject commit 14ab9273409ea0231d08ba6e86fdc73d4e459e99 +Subproject commit bdcfdd47ec3451822b21d1cff2ea2db751093c9a diff --git a/source/blender/blenkernel/intern/mesh_fair.cc b/source/blender/blenkernel/intern/mesh_fair.cc index bed0f3e2d50..b087a2ad52b 100644 --- a/source/blender/blenkernel/intern/mesh_fair.cc +++ b/source/blender/blenkernel/intern/mesh_fair.cc @@ -219,7 +219,6 @@ class MeshFairingContext : public FairingContext { BKE_mesh_edge_loop_map_create(&elmap_, &elmap_mem_, - mesh->medge, mesh->totedge, mesh->mpoly, mesh->totpoly, diff --git a/source/blender/editors/sculpt_paint/sculpt.cc b/source/blender/editors/sculpt_paint/sculpt.cc index 0177e8b5e4b..d8f36da11f3 100644 --- a/source/blender/editors/sculpt_paint/sculpt.cc +++ b/source/blender/editors/sculpt_paint/sculpt.cc @@ -6939,7 +6939,6 @@ void SCULPT_ensure_epmap(SculptSession *ss) BKE_mesh_edge_poly_map_create(&ss->epmap, &ss->epmap_mem, - ss->medge, ss->totedges, ss->mpoly, ss->totfaces, diff --git a/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc b/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc index 0167c328230..7689794efe5 100644 --- a/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc +++ b/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc @@ -20,6 +20,7 @@ #include "BLI_memarena.h" #include "BLI_polyfill_2d.h" #include "BLI_task.h" +#include "BLI_index_range.hh" #include "BLT_translation.h" @@ -64,6 +65,7 @@ #include using blender::Vector; +using blender::IndexRange; BMesh *SCULPT_dyntopo_empty_bmesh() { diff --git a/source/blender/editors/sculpt_paint/sculpt_face_set.cc b/source/blender/editors/sculpt_paint/sculpt_face_set.cc index 43f2c528ba5..8c97b44fd9f 100644 --- a/source/blender/editors/sculpt_paint/sculpt_face_set.cc +++ b/source/blender/editors/sculpt_paint/sculpt_face_set.cc @@ -2665,7 +2665,6 @@ SculptFaceSetIslands *SCULPT_face_set_islands_get(SculptSession *ss, int fset) if (BKE_pbvh_type(ss->pbvh) != PBVH_BMESH && !ss->epmap) { BKE_mesh_edge_poly_map_create(&ss->epmap, &ss->epmap_mem, - ss->medge, ss->totedges, ss->mpoly, ss->totfaces, diff --git a/source/tools b/source/tools index e133fc08cd3..e1744b9bd82 160000 --- a/source/tools +++ b/source/tools @@ -1 +1 @@ -Subproject commit e133fc08cd3254bb3d3bd1345028c8486700bca4 +Subproject commit e1744b9bd82527cf7e8af63362b61bd309b5711b -- 2.30.2 From c5a69cead3b019e92d77dd929847683b0bb262d9 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Sat, 11 Feb 2023 11:40:35 -0800 Subject: [PATCH 003/279] temp-sculpt-dyntopo: Remove more unrelated changes * Removed now-unused SCULPT_TOOL_ entries. * Removed BLI_even_spline.h. * Removed some changes to the pose brush. * Cleaned up some brush flags. --- source/blender/blenkernel/intern/brush.cc | 2 +- source/blender/blenlib/BLI_even_spline.hh | 733 ----------- source/blender/blenlib/CMakeLists.txt | 1 - source/blender/editors/sculpt_paint/sculpt.cc | 3 - .../editors/sculpt_paint/sculpt_array.c | 1106 ----------------- .../sculpt_paint/sculpt_brush_types.cc | 6 +- .../editors/sculpt_paint/sculpt_face_set.cc | 12 +- .../editors/sculpt_paint/sculpt_pose.cc | 135 +- .../editors/sculpt_paint/sculpt_symmetrize.c | 302 ----- .../editors/space_buttons/CMakeLists.txt | 2 +- .../editors/space_buttons/buttons_context.c | 7 +- .../editors/space_buttons/space_buttons.c | 16 +- .../editors/space_view3d/CMakeLists.txt | 2 +- .../editors/space_view3d/space_view3d.cc | 11 +- source/blender/gpu/CMakeLists.txt | 5 +- source/blender/gpu/GPU_vertex_format.h | 2 - source/blender/gpu/intern/gpu_batch.cc | 3 - source/blender/gpu/opengl/gl_shader.cc | 11 +- source/blender/makesdna/DNA_brush_enums.h | 54 +- source/blender/makesdna/DNA_space_types.h | 1 - source/blender/makesrna/intern/rna_brush.c | 80 +- source/blender/makesrna/intern/rna_space.c | 1 - 22 files changed, 30 insertions(+), 2465 deletions(-) delete mode 100644 source/blender/blenlib/BLI_even_spline.hh delete mode 100644 source/blender/editors/sculpt_paint/sculpt_array.c delete mode 100644 source/blender/editors/sculpt_paint/sculpt_symmetrize.c diff --git a/source/blender/blenkernel/intern/brush.cc b/source/blender/blenkernel/intern/brush.cc index 14de4ce8e80..50dfa4212a7 100644 --- a/source/blender/blenkernel/intern/brush.cc +++ b/source/blender/blenkernel/intern/brush.cc @@ -1717,7 +1717,7 @@ void BKE_brush_sculpt_reset(Brush *br) * assign this so logic below can remain the same. */ br->alpha = 0.5f; - br->flag2 |= BRUSH_SMOOTH_PRESERVE_FACE_SETS | BRUSH_SMOOTH_USE_AREA_WEIGHT; + br->flag2 |= BRUSH_SMOOTH_USE_AREA_WEIGHT; bool disable_dyntopo = false; /* Brush settings */ diff --git a/source/blender/blenlib/BLI_even_spline.hh b/source/blender/blenlib/BLI_even_spline.hh deleted file mode 100644 index 074ef3da657..00000000000 --- a/source/blender/blenlib/BLI_even_spline.hh +++ /dev/null @@ -1,733 +0,0 @@ -#pragma once - -#include "BLI_compiler_attrs.h" -#include "BLI_compiler_compat.h" - -#include "BLI_math.h" -#include "BLI_math_vector_types.hh" -#include "BLI_vector.hh" - -#include -#include - -//#define FINITE_DIFF - -/* - * Arc length parameterized spline library. - */ -namespace blender { -/* - Abstract curve interface. - -template class Curve { - using Vector = VecBase; - - public: - Float length; - - Vector evaluate(Float s); - Vector derivative(Float s); - Vector derivative2(Float s); - Float curvature(float s); - - void update(); -}; -*/ - -/* -comment: Reduce algebra script; - -on factor; -off period; - -procedure bez(a, b); - a + (b - a) * t; - -lin := bez(k1, k2); -quad := bez(lin, sub(k2=k3, k1=k2, lin)); - -cubic := bez(quad, sub(k3=k4, k2=k3, k1=k2, quad)); -dcubic := df(cubic, t); -icubic := int(cubic, t); - -x1 := 0; -y1 := 0; - -dx := sub(k1=x1, k2=x2, k3=x3, k4=x4, dcubic); -dy := sub(k1=y1, k2=y2, k3=y3, k4=y4, dcubic); -darc := sqrt(dx**2 + dy**2); - -arcstep := darc*dt + 0.5*df(darc, t)*dt*dt; - -d2x := df(dx / darc, t); -d2y := df(dy / darc, t); - -gentran -begin -declare << -x1,x2,x3,x4 : float; -y1,y2,y3,y4 : float; -dt,t : float; ->>; -return eval(arcstep) -end; - -on fort; -cubic; -dcubic; -icubic; -arcstep; -d2x; -d2y; -off fort; - -*/ -template class CubicBezier { - using Vector = VecBase; - - public: - Vector ps[4]; - - CubicBezier(Vector a, Vector b, Vector c, Vector d) - { - ps[0] = a; - ps[1] = b; - ps[2] = c; - ps[3] = d; - - deleted = false; - _arc_to_t = new Float[table_size]; - } - - ~CubicBezier() - { - deleted = true; - - if (_arc_to_t) { - delete[] _arc_to_t; - _arc_to_t = nullptr; - } - } - - CubicBezier() - { - deleted = false; - _arc_to_t = new Float[table_size]; - } - - CubicBezier(const CubicBezier &b) - { - _arc_to_t = new Float[table_size]; - *this = b; - deleted = false; - } - - CubicBezier &operator=(const CubicBezier &b) - { - ps[0] = b.ps[0]; - ps[1] = b.ps[1]; - ps[2] = b.ps[2]; - ps[3] = b.ps[3]; - - length = b.length; - - if (!_arc_to_t) { - _arc_to_t = new Float[table_size]; - } - - if (b._arc_to_t) { - for (int i = 0; i < table_size; i++) { - _arc_to_t[i] = b._arc_to_t[i]; - } - } - - return *this; - } - -#if 1 - CubicBezier(CubicBezier &&b) - { - *this = b; - } - - CubicBezier &operator=(CubicBezier &&b) - { - ps[0] = b.ps[0]; - ps[1] = b.ps[1]; - ps[2] = b.ps[2]; - ps[3] = b.ps[3]; - - length = b.length; - - if (b._arc_to_t) { - _arc_to_t = std::move(b._arc_to_t); - b._arc_to_t = nullptr; - } - else { - _arc_to_t = new Float[table_size]; - } - - return *this; - } -#endif - - Float length; - - void update() - { - Float t = 0.0, dt = 1.0 / (Float)table_size; - Float s = 0.0; - - if (!_arc_to_t) { - _arc_to_t = new Float[table_size]; - } - - auto table = _arc_to_t; - - for (int i = 0; i < table_size; i++) { - table[i] = -1.0; - } - - length = 0.0; - - for (int i = 0; i < table_size; i++, t += dt) { - Float dlen = 0.0; - for (int j = 0; j < axes; j++) { - float dv = dcubic(ps[0][j], ps[1][j], ps[2][j], ps[3][j], t); - - dlen += dv * dv; - } - - dlen = sqrt(dlen) * dt; - - length += dlen; - } - - const int samples = table_size; - dt = 1.0 / (Float)samples; - - t = 0.0; - s = 0.0; - - for (int i = 0; i < samples; i++, t += dt) { - Float dlen = 0.0; - for (int j = 0; j < axes; j++) { - float dv = dcubic(ps[0][j], ps[1][j], ps[2][j], ps[3][j], t); - - dlen += dv * dv; - } - - dlen = sqrt(dlen) * dt; - - int j = (int)((s / length) * (Float)table_size * 0.999999); - j = min_ii(j, table_size - 1); - - table[j] = t; - - s += dlen; - } - - table[0] = 0.0; - table[table_size - 1] = 1.0; - -#if 1 - /* Interpolate gaps in table. */ - for (int i = 0; i < table_size - 1; i++) { - if (table[i] == -1.0 || table[i + 1] != -1.0) { - continue; - } - - int i1 = i; - int i2 = i + 1; - - while (table[i2] == -1.0) { - i2++; - } - - int start = table[i1]; - int end = table[i2]; - Float dt2 = 1.0 / (i2 - i1); - - for (int j = i1 + 1; j < i2; j++) { - Float factor = (Float)(j - i1) * dt2; - table[j] = start + (end - start) * factor; - } - - i = i2 - 1; - } - -# if 0 - for (int i = 0; i < table_size; i++) { - printf("%.3f ", table[i]); - } - printf("\n\n"); -# endif -#endif - } - - inline Vector evaluate(Float s) - { - Float t = arc_to_t(s); - Vector r; - - for (int i = 0; i < axes; i++) { - r[i] = cubic(ps[0][i], ps[1][i], ps[2][i], ps[3][i], t); - } - - return r; - } - - Vector derivative(Float s, bool exact = true) - { - Float t = arc_to_t(s); - Vector r; - - for (int i = 0; i < axes; i++) { - r[i] = dcubic(ps[0][i], ps[1][i], ps[2][i], ps[3][i], t) * length; - } - - /* Real arc length parameterized tangent has unit length. */ - if (exact) { - Float len = sqrt(_dot(r, r)); - - if (len > 0.00001) { - r = r / len; - } - } - - return r; - } - - Vector derivative2(Float s) - { -#ifdef FINITE_DIFF - const Float df = 0.0005; - Float s1, s2; - - if (s >= 1.0 - df) { - s1 = s - df; - s2 = s; - } - else { - s1 = s; - s2 = s + df; - } - - Vector a = derivative(s1); - Vector b = derivative(s2); - - return (b - a) / df; -#else - Float t = arc_to_t(s); - Vector r; - - Float dx = dcubic(ps[0][0], ps[1][0], ps[2][0], ps[3][0], t); - Float d2x = d2cubic(ps[0][0], ps[1][0], ps[2][0], ps[3][0], t); - Float dy = dcubic(ps[0][1], ps[1][1], ps[2][1], ps[3][1], t); - Float d2y = d2cubic(ps[0][1], ps[1][1], ps[2][1], ps[3][1], t); - - /* - comment: arc length second derivative; - - operator x, y, z, dx, dy, dz, d2x, d2y, d2z; - forall t let df(x(t), t) = dx(t); - forall t let df(y(t), t) = dy(t); - forall t let df(z(t), t) = dz(t); - forall t let df(dx(t), t) = d2x(t); - forall t let df(dy(t), t) = d2y(t); - forall t let df(dz(t), t) = d2z(t); - - comment: arc length first derivative is just the normalized tangent; - - comment: 2d case; - - dlen := sqrt(df(x(t), t)**2 + df(y(t), t)**2); - - df(df(x(t), t) / dlen, t); - df(df(y(t), t) / dlen, t); - - comment: 3d case; - - dlen := sqrt(df(x(t), t)**2 + df(y(t), t)**2 + df(z(t), t)**2); - - comment: final derivatives; - - df(df(x(t), t) / dlen, t); - df(df(y(t), t) / dlen, t); - df(df(z(t), t) / dlen, t); - */ - if constexpr (axes == 2) { - /* Basically the 2d perpidicular normalized tangent multiplied by the curvature. */ - - Float div = sqrt(dx * dx + dy * dy) * (dx * dx + dy * dy); - - r[0] = ((d2x * dy - d2y * dx) * dy) / div; - r[1] = (-(d2x * dy - d2y * dx) * dx) / div; - } - else if constexpr (axes == 3) { - Float dz = dcubic(ps[0][2], ps[1][2], ps[2][2], ps[3][2], t); - Float d2z = d2cubic(ps[0][2], ps[1][2], ps[2][2], ps[3][2], t); - - Float div = sqrt(dx * dx + dy * dy + dz * dz) * (dy * dy + dz * dz + dx * dx); - - r[0] = (d2x * dy * dy + d2x * dz * dz - d2y * dx * dy - d2z * dx * dz) / div; - r[1] = (-(d2x * dx * dy - d2y * dx * dx - d2y * dz * dz + d2z * dy * dz)) / div; - r[2] = (-(d2x * dx * dz + d2y * dy * dz - d2z * dx * dx - d2z * dy * dy)) / div; - } - else { - for (int i = 0; i < axes; i++) { - r[i] = d2cubic(ps[0][i], ps[1][i], ps[2][i], ps[3][i], t) * length; - } - } - - return r; -#endif - } - - Float curvature(Float s) - { - Vector dv2 = derivative2(s); - - if constexpr (axes == 2) { - Vector dv = derivative(s, true); - - /* Calculate signed curvature. Remember that dv is normalized. */ - return dv[0] * dv2[1] - dv[1] * dv2[0]; - } - - return sqrt(_dot(dv2, dv2)); - } - - private: - Float *_arc_to_t; - bool deleted = false; - - Float cubic(Float k1, Float k2, Float k3, Float k4, Float t) - { - return -(((3.0 * (t - 1.0) * k3 - k4 * t) * t - 3.0 * (t - 1.0) * (t - 1.0) * k2) * t + - (t - 1) * (t - 1) * (t - 1) * k1); - } - - Float dcubic(Float k1, Float k2, Float k3, Float k4, Float t) - { - return -3.0 * ((t - 1.0) * (t - 1.0) * k1 - k4 * t * t + (3.0 * t - 2.0) * k3 * t - - (3.0 * t - 1.0) * (t - 1.0) * k2); - } - - Float d2cubic(Float k1, Float k2, Float k3, Float k4, Float t) - { - return -6.0 * (k1 * t - k1 - 3.0 * k2 * t + 2.0 * k2 + 3.0 * k3 * t - k3 - k4 * t); - } - - Float _dot(Vector a, Vector b) - { - Float sum = 0.0; - - for (int i = 0; i < axes; i++) { - sum += a[i] * b[i]; - } - - return sum; - } - - Float clamp_s(Float s) - { - s = s < 0.0 ? 0.0 : s; - s = s >= length ? length * 0.999999 : s; - - return s; - } - - Float arc_to_t(Float s) - { - if (length == 0.0) { - return 0.0; - } - - s = clamp_s(s); - - Float t = s * (Float)(table_size - 1) / length; - - int i1 = floorf(t); - int i2 = min_ii(i1 + 1, table_size - 1); - - t -= (Float)i1; - - Float s1 = _arc_to_t[i1]; - Float s2 = _arc_to_t[i2]; - - return s1 + (s2 - s1) * t; - } -}; - -template class BezierSpline { - using Vector = VecBase; - struct Segment { - CubicBezier bezier; - Float start = 0.0; - - Segment(const CubicBezier &bez) - { - bezier = bez; - } - - Segment(const Segment &b) - { - *this = b; - } - - Segment &operator=(const Segment &b) - { - bezier = b.bezier; - start = b.start; - - return *this; - } - - Segment() - { - } - }; - - public: - Float length = 0.0; - bool deleted = false; - blender::Vector segments; - - void clear() - { - segments.clear(); - } - - BezierSpline() - { - } - - ~BezierSpline() - { - deleted = true; - } - - void add(CubicBezier &bez) - { - need_update = true; - - Segment seg; - - seg.bezier = bez; - segments.append(seg); - - update(); - } - - void update() - { - need_update = false; - - length = 0.0; - for (Segment &seg : segments) { - seg.start = length; - length += seg.bezier.length; - } - } - - inline Vector evaluate(Float s) - { - if (s == 0.0) { - return segments[0].bezier.ps[0]; - } - - if (s >= length) { - return segments[segments.size() - 1].bezier.ps[3]; - } - - Segment *seg = get_segment(s); - - return seg->bezier.evaluate(s - seg->start); - } - - Vector derivative(Float s, bool exact = true) - { - if (segments.size() == 0) { - return Vector(); - } - - s = clamp_s(s); - Segment *seg = get_segment(s); - - return seg->bezier.derivative(s - seg->start, exact); - } - - Vector derivative2(Float s) - { - if (segments.size() == 0) { - return Vector(); - } - - s = clamp_s(s); - Segment *seg = get_segment(s); - - return seg->bezier.derivative2(s - seg->start); - } - - Float curvature(Float s) - { - if (segments.size() == 0) { - return 0.0; - } - - s = clamp_s(s); - Segment *seg = get_segment(s); - - return seg->bezier.curvature(s - seg->start); - } - - /* Find the closest point on the spline. Uses a bisecting root finding approach. - * Note: in thoery we could split the spline into quadratic segments and solve - * for the closest point directy. - */ - Vector closest_point(const Vector p, Float &r_s, Vector &r_tan, Float &r_dis) - { - if (segments.size() == 0) { - return Vector(); - } - - const int steps = 12; - Float s = 0.0, ds = length / steps; - Float mindis = FLT_MAX; - Vector minp; - Float mins = 0.0; - bool found = false; - - Vector lastdv, lastp; - Vector b, dvb; - - for (int i = 0; i < steps + 1; i++, s += ds, lastp = b, lastdv = dvb) { - b = evaluate(s); - dvb = derivative(s, false); /* We don't need real normalized derivative here. */ - - if (i == 0) { - continue; - } - - Vector dva = lastdv; - Vector a = lastp; - - Vector vec1 = a - p; - Vector vec2 = b - p; - - Float sign1 = _dot(vec1, dva); - Float sign2 = _dot(vec2, dvb); - - if ((sign1 < 0.0) == (sign2 < 0.0)) { - found = true; - - Float len = _dot(vec1, vec1); - - if (len < mindis) { - mindis = len; - mins = s; - minp = evaluate(s); - } - continue; - } - - found = true; - - Float start = s - ds; - Float end = s; - Float mid = (start + end) * 0.5; - const int binary_steps = 10; - - for (int j = 0; j < binary_steps; j++) { - Vector dvmid = derivative(mid, false); - Vector vecmid = evaluate(mid) - p; - Float sign_mid = _dot(vecmid, dvmid); - - if ((sign_mid < 0.0) == (sign1 < 0.0)) { - start = mid; - } - else { - end = mid; - } - mid = (start + end) * 0.5; - } - - Vector p2 = evaluate(mid); - Vector vec_mid = p2 - p; - Float len = _dot(vec_mid, vec_mid); - - if (len < mindis) { - mindis = len; - minp = p2; - mins = mid; - } - } - - if (!found) { - mins = 0.0; - minp = evaluate(mins); - Vector vec = minp - p; - mindis = _dot(vec, vec); - } - - r_tan = derivative(mins, true); - r_s = mins; - r_dis = sqrtf(mindis); - - return minp; - } - - void pop_front(int n = 1) - { - for (int i = 0; i < segments.size() - n; i++) { - segments[i] = segments[i + n]; - } - - segments.resize(segments.size() - n); - update(); - } - - private: - bool need_update; - - Float _dot(Vector a, Vector b) - { - Float sum = 0.0; - - for (int i = 0; i < axes; i++) { - sum += a[i] * b[i]; - } - - return sum; - } - - Float clamp_s(Float s) - { - s = s < 0.0 ? 0.0 : s; - s = s >= length ? length * 0.999999 : s; - - return s; - } - - Segment *get_segment(Float s) - { - // printf("\n"); - - for (Segment &seg : segments) { - // printf("s: %f %f\n", seg.start, seg.start + seg.bezier.length); - - if (s >= seg.start && s < seg.start + seg.bezier.length) { - return &seg; - } - } - - // printf("\n"); - - return nullptr; - } -}; - -using BezierSpline2f = BezierSpline; -using BezierSpline3f = BezierSpline; -} // namespace blender diff --git a/source/blender/blenlib/CMakeLists.txt b/source/blender/blenlib/CMakeLists.txt index 09ab4689fbf..7aaf3e72d6d 100644 --- a/source/blender/blenlib/CMakeLists.txt +++ b/source/blender/blenlib/CMakeLists.txt @@ -327,7 +327,6 @@ set(SRC BLI_sort.h BLI_sort.hh BLI_sort_utils.h - BLI_even_spline.hh BLI_span.hh BLI_stack.h BLI_stack.hh diff --git a/source/blender/editors/sculpt_paint/sculpt.cc b/source/blender/editors/sculpt_paint/sculpt.cc index d8f36da11f3..f9bd9aedc58 100644 --- a/source/blender/editors/sculpt_paint/sculpt.cc +++ b/source/blender/editors/sculpt_paint/sculpt.cc @@ -1992,9 +1992,6 @@ static void paint_mesh_restore_co_task_cb(void *__restrict userdata, case SCULPT_TOOL_SMEAR: type |= SCULPT_UNDO_COLOR; break; - case SCULPT_TOOL_VCOL_BOUNDARY: - type |= SCULPT_UNDO_COLOR | SCULPT_UNDO_COORDS; - break; case SCULPT_TOOL_DRAW_FACE_SETS: type = ss->cache->alt_smooth ? SCULPT_UNDO_COORDS : SCULPT_UNDO_FACE_SETS; break; diff --git a/source/blender/editors/sculpt_paint/sculpt_array.c b/source/blender/editors/sculpt_paint/sculpt_array.c deleted file mode 100644 index fffcb6feb38..00000000000 --- a/source/blender/editors/sculpt_paint/sculpt_array.c +++ /dev/null @@ -1,1106 +0,0 @@ -/* - * 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) 2020 Blender Foundation. - * All rights reserved. - */ - -/** \file - * \ingroup edsculpt - */ - -#include "MEM_guardedalloc.h" - -#include "BLI_blenlib.h" -#include "BLI_hash.h" -#include "BLI_math.h" -#include "BLI_task.h" - -#include "DNA_brush_types.h" -#include "DNA_mesh_types.h" -#include "DNA_meshdata_types.h" -#include "DNA_object_types.h" - -#include "BKE_brush.h" -#include "BKE_ccg.h" -#include "BKE_colortools.h" -#include "BKE_context.h" -#include "BKE_lib_id.h" -#include "BKE_mesh.h" -#include "BKE_multires.h" -#include "BKE_node.h" -#include "BKE_object.h" -#include "BKE_paint.h" -#include "BKE_pbvh.h" -#include "BKE_scene.h" - -#include "paint_intern.h" -#include "sculpt_intern.h" - -#include "GPU_immediate.h" -#include "GPU_immediate_util.h" -#include "GPU_matrix.h" -#include "GPU_state.h" - -#include "ED_sculpt.h" - -#include "bmesh.h" -#include "bmesh_tools.h" - -#include -#include - -static const char array_symmetry_pass_cd_name[] = "v_symmetry_pass"; -static const char array_instance_cd_name[] = "v_array_instance"; - -#define ARRAY_INSTANCE_ORIGINAL -1 - -static void sculpt_vertex_array_data_get(SculptArray *array, - const int vertex, - int *r_copy, - int *r_symmetry_pass) -{ - if (!array->copy_index) { - printf("NO ARRAY COPY\n"); - *r_copy = ARRAY_INSTANCE_ORIGINAL; - *r_symmetry_pass = 0; - return; - } - *r_copy = array->copy_index[vertex]; - *r_symmetry_pass = array->symmetry_pass[vertex]; -} - -static void sculpt_array_datalayers_init(Object *ob, SculptArray *array, SculptSession *ss) -{ - SculptAttributeParams params = {.permanent = true, .simple_array = false}; - - array->scl_inst = BKE_sculpt_attribute_ensure( - ob, ATTR_DOMAIN_POINT, CD_PROP_INT32, array_instance_cd_name, ¶ms); - array->scl_sym = BKE_sculpt_attribute_ensure( - ob, ATTR_DOMAIN_POINT, CD_PROP_INT32, array_symmetry_pass_cd_name, ¶ms); -} - -static void sculpt_array_datalayers_add(SculptArray *array, SculptSession *ss, Mesh *mesh) -{ - - // int *v_array_instance = CustomData_add_layer_named( - // &mesh->vdata, CD_PROP_INT32, CD_CALLOC, NULL, mesh->totvert, array_instance_cd_name); - int totvert = SCULPT_vertex_count_get(ss); - const SculptAttribute *scl = array->scl_inst; - - for (int i = 0; i < totvert; i++) { - PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); - - *SCULPT_vertex_attr_get(vertex, scl) = ARRAY_INSTANCE_ORIGINAL; - *SCULPT_vertex_attr_get(vertex, array->scl_sym) = 0; - } -} - -void SCULPT_array_datalayers_free(SculptArray *array, Object *ob) -{ - SculptSession *ss = ob->sculpt; - -#if 0 - Mesh *mesh = BKE_object_get_original_mesh(ob); - - // update cdata pointers for SCULPT_attr_release_layer - if (BKE_pbvh_type(ss->pbvh) != PBVH_BMESH) { - ss->vdata = &mesh->vdata; - ss->edata = &mesh->edata; - ss->ldata = &mesh->ldata; - ss->pdata = &mesh->pdata; - - ss->mvert = (MVert *)CustomData_get_layer(ss->vdata, CD_MVERT); - ss->medge = (MEdge *)CustomData_get_layer(ss->edata, CD_MEDGE); - ss->mloop = (MLoop *)CustomData_get_layer(ss->ldata, CD_MLOOP); - ss->mpoly = (MPoly *)CustomData_get_layer(ss->pdata, CD_MPOLY); - - ss->face_sets = (int *)CustomData_get_layer(ss->pdata, CD_SCULPT_FACE_SETS); - - ss->totfaces = ss->totpoly = mesh->totpoly; - ss->totedges = mesh->totedge; - - if (BKE_pbvh_type(ss->pbvh) == PBVH_FACES) { - ss->totvert = mesh->totvert; - } - - BKE_sculptsession_check_sculptverts(ob, ss->pbvh, SCULPT_vertex_count_get(ss)); - } -#endif - - SculptAttributeParams params = {.permanent = true, .simple_array = false}; - - if (array->scl_inst) { - BKE_sculpt_attribute_destroy(ob, array->scl_inst); - } - - if (array->scl_sym) { - BKE_sculpt_attribute_destroy(ob, array->scl_sym); - } - - BKE_sculptsession_update_attr_refs(ob); - - array->scl_inst = NULL; - array->scl_sym = NULL; - -#if 0 - // Mesh *mesh = BKE_object_get_original_mesh(ob); - int v_layer_index = CustomData_get_named_layer_index( - &mesh->vdata, CD_PROP_INT32, array_instance_cd_name); - if (v_layer_index != -1) { - CustomData_free_layer(&mesh->vdata, CD_PROP_INT32, mesh->totvert, v_layer_index); - } - - v_layer_index = CustomData_get_named_layer_index( - &mesh->vdata, CD_PROP_INT32, array_symmetry_pass_cd_name); - if (v_layer_index != -1) { - CustomData_free_layer(&mesh->vdata, CD_PROP_INT32, mesh->totvert, v_layer_index); - } -#endif -} - -const float source_geometry_threshold = 0.5f; - -static BMesh *sculpt_array_source_build(Object *ob, Brush *brush, SculptArray *array) -{ - bool have_bmesh = ob->sculpt->bm && ob->sculpt->pbvh && - BKE_pbvh_type(ob->sculpt->pbvh) == PBVH_BMESH; - - Mesh *sculpt_mesh = BKE_object_get_original_mesh(ob); - - BMesh *srcbm; - - BMesh *BM_mesh_copy_ex(BMesh * bm_old, struct BMeshCreateParams * params); - - if (have_bmesh) { - srcbm = BM_mesh_copy_ex( - ob->sculpt->bm, - &((struct BMeshCreateParams){.use_toolflags = true, - .id_map = false, - .id_elem_mask = ob->sculpt->bm->idmap.flag & - (BM_VERT | BM_EDGE | BM_FACE | BM_LOOP), - .create_unique_ids = true, - .copy_all_layers = true})); - } - else { - const BMAllocTemplate allocsizea = BMALLOC_TEMPLATE_FROM_ME(sculpt_mesh); - srcbm = BM_mesh_create(&allocsizea, - &((struct BMeshCreateParams){ - .use_toolflags = true, - })); - - BM_mesh_bm_from_me(NULL, - srcbm, - sculpt_mesh, - &((struct BMeshFromMeshParams){ - .calc_face_normal = true, - .active_shapekey = ob->shapenr, - .use_shapekey = true, - .create_shapekey_layers = true, - })); - } - - BM_mesh_elem_table_ensure(srcbm, BM_VERT); - BM_mesh_elem_index_ensure(srcbm, BM_VERT); - - int vert_count = 0; - zero_v3(array->source_origin); - - SculptSession *ss = ob->sculpt; - for (int i = 0; i < srcbm->totvert; i++) { - PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); - - AutomaskingNodeData automask_data = {0}; - automask_data.have_orig_data = true; - automask_data.orig_data.co = SCULPT_vertex_origco_get(ss, vertex); - automask_data.orig_data.no = SCULPT_vertex_origno_get(ss, vertex); - - const float automask = SCULPT_automasking_factor_get( - ss->cache->automasking, ss, vertex, &automask_data); - const float mask = 1.0f - SCULPT_vertex_mask_get(ss, vertex); - const float influence = mask * automask; - - BMVert *vert = BM_vert_at_index(srcbm, i); - if (influence >= source_geometry_threshold) { - vert_count++; - add_v3_v3(array->source_origin, vert->co); - continue; - } - BM_elem_flag_set(vert, BM_ELEM_TAG, true); - } - - if (vert_count == 0) { - return srcbm; - } - - mul_v3_fl(array->source_origin, 1.0f / vert_count); - - /* TODO(pablodp606): Handle individual Face Sets for Face Set automasking. */ - BM_mesh_delete_hflag_context(srcbm, BM_ELEM_TAG, DEL_VERTS); - - const bool fill_holes = brush->flag2 & BRUSH_ARRAY_FILL_HOLES; - if (fill_holes) { - BM_mesh_elem_hflag_disable_all(srcbm, BM_VERT | BM_EDGE | BM_FACE, BM_ELEM_TAG, false); - BM_mesh_elem_hflag_enable_all(srcbm, BM_EDGE, BM_ELEM_TAG, false); - BM_mesh_edgenet(srcbm, false, true); - BM_mesh_normals_update(srcbm); - BMO_op_callf(srcbm, - (BMO_FLAG_DEFAULTS & ~BMO_FLAG_RESPECT_HIDE), - "triangulate faces=%hf quad_method=%i ngon_method=%i", - BM_ELEM_TAG, - 0, - 0); - - BM_mesh_elem_hflag_enable_all(srcbm, BM_FACE, BM_ELEM_TAG, false); - BMO_op_callf(srcbm, - (BMO_FLAG_DEFAULTS & ~BMO_FLAG_RESPECT_HIDE), - "recalc_face_normals faces=%hf", - BM_ELEM_TAG); - BM_mesh_elem_hflag_disable_all(srcbm, BM_VERT | BM_EDGE | BM_FACE, BM_ELEM_TAG, false); - } - - return srcbm; -} - -void sculpt_array_source_datalayer_update(BMesh *bm, const int symm_pass, const int copy_index) -{ - const int cd_array_instance_index = CustomData_get_named_layer_index( - &bm->vdata, CD_PROP_INT32, array_instance_cd_name); - const int cd_array_instance_offset = CustomData_get_n_offset( - &bm->vdata, CD_PROP_INT32, cd_array_instance_index); - - const int cd_array_symm_pass_index = CustomData_get_named_layer_index( - &bm->vdata, CD_PROP_INT32, array_symmetry_pass_cd_name); - const int cd_array_symm_pass_offset = CustomData_get_n_offset( - &bm->vdata, CD_PROP_INT32, cd_array_symm_pass_index); - - BM_mesh_elem_table_ensure(bm, BM_VERT); - BM_mesh_elem_index_ensure(bm, BM_VERT); - - BMVert *v; - BMIter iter; - BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) { - BM_ELEM_CD_SET_INT(v, cd_array_instance_offset, copy_index); - BM_ELEM_CD_SET_INT(v, cd_array_symm_pass_offset, symm_pass); - } -} - -static void sculpt_array_final_mesh_write(Object *ob, BMesh *final_mesh) -{ - SculptSession *ss = ob->sculpt; - Mesh *sculpt_mesh = BKE_object_get_original_mesh(ob); - Mesh *result = BKE_mesh_from_bmesh_for_eval_nomain(final_mesh, NULL, sculpt_mesh); - - BKE_mesh_normals_tag_dirty(result); - BKE_mesh_nomain_to_mesh(result, ob->data, ob); - BKE_mesh_batch_cache_dirty_tag(ob->data, BKE_MESH_BATCH_DIRTY_ALL); - - const int next_face_set_id = ED_sculpt_face_sets_find_next_available_id(ob->data); - ED_sculpt_face_sets_initialize_none_to_id(ob->data, next_face_set_id); - - ss->needs_pbvh_rebuild = true; -} - -static void sculpt_array_ensure_geometry_indices(Object *ob, SculptArray *array) -{ - if (array->copy_index) { - return; - } - - printf("ALLOCATION COPY INDEX\n"); - - SculptSession *ss = ob->sculpt; - int totvert = SCULPT_vertex_count_get(ss); - - BKE_sculptsession_update_attr_refs(ob); - - SculptAttributeParams params = {.permanent = true, .simple_array = false}; - - array->scl_inst = BKE_sculpt_attribute_ensure( - ob, ATTR_DOMAIN_POINT, CD_PROP_INT32, array_instance_cd_name, ¶ms); - array->scl_sym = BKE_sculpt_attribute_ensure( - ob, ATTR_DOMAIN_POINT, CD_PROP_INT32, array_symmetry_pass_cd_name, ¶ms); - - array->copy_index = MEM_malloc_arrayN(totvert, sizeof(int), "array copy index"); - array->symmetry_pass = MEM_malloc_arrayN(totvert, sizeof(int), "array symmetry pass index"); - - for (int i = 0; i < totvert; i++) { - PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); - - array->copy_index[i] = *(int *)SCULPT_vertex_attr_get(vertex, array->scl_inst); - array->symmetry_pass[i] = *(int *)SCULPT_vertex_attr_get(vertex, array->scl_sym); - } - - SCULPT_array_datalayers_free(array, ob); -} - -static void sculpt_array_mesh_build(Sculpt *sd, Object *ob, SculptArray *array) -{ - bool have_bmesh = ob->sculpt->bm && ob->sculpt->pbvh && - BKE_pbvh_type(ob->sculpt->pbvh) == PBVH_BMESH; - - Mesh *sculpt_mesh = BKE_object_get_original_mesh(ob); - Brush *brush = BKE_paint_brush(&sd->paint); - - sculpt_array_datalayers_init(ob, array, ob->sculpt); - sculpt_array_datalayers_add(array, ob->sculpt, sculpt_mesh); - - BMesh *srcbm = sculpt_array_source_build(ob, brush, array); - - BMesh *destbm; - const BMAllocTemplate allocsizeb = BMALLOC_TEMPLATE_FROM_ME(sculpt_mesh); - - if (!have_bmesh) { - destbm = BM_mesh_create(&allocsizeb, - &((struct BMeshCreateParams){ - .use_toolflags = true, - })); - BM_mesh_bm_from_me(NULL, - destbm, - sculpt_mesh, - &((struct BMeshFromMeshParams){ - .calc_face_normal = true, - .active_shapekey = ob->shapenr, - .use_shapekey = true, - .create_shapekey_layers = true, - })); - } - else { - destbm = ob->sculpt->bm; - } - - BM_mesh_toolflags_set(destbm, true); - BM_mesh_toolflags_set(srcbm, true); - - BM_mesh_elem_toolflags_ensure(destbm); - BM_mesh_elem_toolflags_ensure(srcbm); - - const char symm = SCULPT_mesh_symmetry_xyz_get(ob); - for (char symm_it = 0; symm_it <= symm; symm_it++) { - if (!SCULPT_is_symmetry_iteration_valid(symm_it, symm)) { - continue; - } - - for (int copy_index = 0; copy_index < array->num_copies; copy_index++) { - sculpt_array_source_datalayer_update(srcbm, symm_it, copy_index); - - if (1) { //! have_bmesh) { - // BM_mesh_copy_init_customdata(destbm, srcbm, &bm_mesh_allocsize_default); - } - - const int opflag = (BMO_FLAG_DEFAULTS & ~BMO_FLAG_RESPECT_HIDE); - BMO_op_callf(srcbm, opflag, "duplicate geom=%avef dest=%p", destbm); - } - } - - if (!have_bmesh) { - sculpt_array_final_mesh_write(ob, destbm); - BM_mesh_free(destbm); - } - else { - BKE_sculptsession_update_attr_refs(ob); - ob->sculpt->needs_pbvh_rebuild = true; - } - - BM_mesh_free(srcbm); -} - -static SculptArray *sculpt_array_cache_create(Object *ob, - eBrushArrayDeformType deform_type, - const int num_copies) -{ - - SculptArray *array = MEM_callocN(sizeof(SculptArray), "Sculpt Array"); - array->num_copies = num_copies; - - array->mode = deform_type; - - const char symm = SCULPT_mesh_symmetry_xyz_get(ob); - for (char symm_it = 0; symm_it <= symm; symm_it++) { - if (!SCULPT_is_symmetry_iteration_valid(symm_it, symm)) { - continue; - } - array->copies[symm_it] = MEM_malloc_arrayN( - num_copies, sizeof(SculptArrayCopy), "Sculpt array copies"); - } - return array; -} - -static void sculpt_array_cache_free(SculptArray *array) -{ - return; - for (int symm_pass = 0; symm_pass < PAINT_SYMM_AREAS; symm_pass++) { - MEM_SAFE_FREE(array->copies[symm_pass]); - } - MEM_freeN(array->copy_index); - MEM_freeN(array->symmetry_pass); - MEM_freeN(array); -} - -static void sculpt_array_init(Object *ob, Brush *brush, SculptArray *array) -{ - SculptSession *ss = ob->sculpt; - - /* TODO: add options. */ - copy_v3_v3(array->normal, ss->cache->view_normal); - array->radial_angle = 2.0f * M_PI; - - for (int symm_pass = 0; symm_pass < PAINT_SYMM_AREAS; symm_pass++) { - if (array->copies[symm_pass] == NULL) { - continue; - } - for (int copy_index = 0; copy_index < array->num_copies; copy_index++) { - SculptArrayCopy *copy = &array->copies[symm_pass][copy_index]; - unit_m4(copy->mat); - copy->symm_pass = symm_pass; - copy->index = copy_index; - float symm_location[3]; - flip_v3_v3(symm_location, ss->cache->location, symm_pass); - copy_v3_v3(copy->origin, ss->cache->location); - } - } -} - -static void sculpt_array_position_in_path_search( - float *r_position, float *r_direction, float *r_scale, SculptArray *array, const int index) -{ - const float path_length = array->path.points[array->path.tot_points - 1].length; - const float step_distance = path_length / (float)array->num_copies; - const float copy_distance = step_distance * (index + 1); - - if (array->path.tot_points == 1) { - zero_v3(r_position); - if (r_direction) { - zero_v3(r_direction); - } - if (r_scale) { - *r_scale = 1.0f; - } - return; - } - - for (int i = 1; i < array->path.tot_points; i++) { - ScultpArrayPathPoint *path_point = &array->path.points[i]; - if (copy_distance >= path_point->length) { - continue; - } - ScultpArrayPathPoint *prev_path_point = &array->path.points[i - 1]; - - const float remaining_dist = copy_distance - prev_path_point->length; - const float segment_length = path_point->length - prev_path_point->length; - const float interp_factor = remaining_dist / segment_length; - interp_v3_v3v3(r_position, prev_path_point->co, path_point->co, interp_factor); - if (r_direction) { - if (i == array->path.tot_points - 1) { - copy_v3_v3(r_direction, prev_path_point->direction); - } - else { - copy_v3_v3(r_direction, path_point->direction); - } - } - if (r_scale) { - const float s = 1.0f - interp_factor; - *r_scale = s * prev_path_point->strength + interp_factor * path_point->strength; - } - return; - } - - ScultpArrayPathPoint *last_path_point = &array->path.points[array->path.tot_points - 1]; - copy_v3_v3(r_position, last_path_point->co); - if (r_direction) { - ScultpArrayPathPoint *prev_path_point = &array->path.points[array->path.tot_points - 2]; - copy_v3_v3(r_direction, prev_path_point->direction); - } - if (r_scale) { - ScultpArrayPathPoint *prev_path_point = &array->path.points[array->path.tot_points - 2]; - *r_scale = prev_path_point->strength; - } -} - -static void scultp_array_basis_from_direction(float r_mat[4][4], - SculptArray *array, - const float direction[3]) -{ - float direction_normalized[3]; - normalize_v3_v3(direction_normalized, direction); - copy_v3_v3(r_mat[0], direction_normalized); - cross_v3_v3v3(r_mat[2], r_mat[0], array->normal); - cross_v3_v3v3(r_mat[1], r_mat[0], r_mat[2]); - normalize_v3(r_mat[0]); - normalize_v3(r_mat[1]); - normalize_v3(r_mat[2]); -} - -static float *sculpt_array_delta_from_path(SculptArray *array) -{ - return array->path.points[array->path.tot_points - 1].co; -} - -static void sculpt_array_update_copy(StrokeCache *cache, - SculptArray *array, - SculptArrayCopy *copy, - Brush *brush) -{ - - unit_m4(copy->mat); - - float scale = 1.0f; - float direction[3]; - - BrushChannelSet *chset = cache->channels_final ? cache->channels_final : brush->channels; - eBrushArrayDeformType array_type = BRUSHSET_GET_INT( - chset, array_deform_type, &cache->input_mapping); - - float delta[3]; - copy_v3_v3(delta, sculpt_array_delta_from_path(array)); - - switch (array_type) { - case BRUSH_ARRAY_DEFORM_LINEAR: { - const float fade = ((float)copy->index + 1.0f) / (float)(array->num_copies); - mul_v3_v3fl(copy->mat[3], delta, fade); - normalize_v3_v3(direction, delta); - scale = cache->bstrength; - } break; - - case BRUSH_ARRAY_DEFORM_RADIAL: { - float pos[3]; - const float fade = ((float)copy->index + 1.0f) / (float)(array->num_copies); - copy_v3_v3(pos, delta); - rotate_v3_v3v3fl(copy->mat[3], pos, array->normal, fade * array->radial_angle); - copy_v3_v3(direction, copy->mat[3]); - // sub_v3_v3v3(direction, copy->mat[3], array->source_origin); - scale = cache->bstrength; - } break; - case BRUSH_ARRAY_DEFORM_PATH: - sculpt_array_position_in_path_search(copy->mat[3], direction, &scale, array, copy->index); - break; - } - - if (!(brush->flag2 & BRUSH_ARRAY_LOCK_ORIENTATION)) { - scultp_array_basis_from_direction(copy->mat, array, direction); - } - - /* - copy->mat[3][0] += (BLI_hash_int_01(copy->index) * 2.0f - 0.5f) * cache->radius; - copy->mat[3][1] += (BLI_hash_int_01(copy->index + 1) * 2.0f - 0.5f) * cache->radius; - copy->mat[3][2] += (BLI_hash_int_01(copy->index + 2) * 2.0f - 0.5f) * cache->radius; - */ - - mul_v3_fl(copy->mat[0], scale); - mul_v3_fl(copy->mat[1], scale); - mul_v3_fl(copy->mat[2], scale); - - /* - copy->mat[0][0] = scale; - copy->mat[1][1] = scale; - copy->mat[2][2] = scale; - */ -} - -static void sculpt_array_update(Object *ob, Brush *brush, SculptArray *array) -{ - SculptSession *ss = ob->sculpt; - - /* Main symmetry pass. */ - for (int copy_index = 0; copy_index < array->num_copies; copy_index++) { - SculptArrayCopy *copy = &array->copies[0][copy_index]; - unit_m4(copy->mat); - sculpt_array_update_copy(ss->cache, array, copy, brush); - } - - for (int symm_pass = 1; symm_pass < PAINT_SYMM_AREAS; symm_pass++) { - if (array->copies[symm_pass] == NULL) { - continue; - } - - float symm_orig[3]; - flip_v3_v3(symm_orig, array->source_origin, symm_pass); - - for (int copy_index = 0; copy_index < array->num_copies; copy_index++) { - SculptArrayCopy *copy = &array->copies[symm_pass][copy_index]; - SculptArrayCopy *main_copy = &array->copies[0][copy_index]; - unit_m4(copy->mat); - for (int m = 0; m < 4; m++) { - flip_v3_v3(copy->mat[m], main_copy->mat[m], symm_pass); - } - } - } - - for (int symm_pass = 0; symm_pass < PAINT_SYMM_AREAS; symm_pass++) { - - if (array->copies[symm_pass] == NULL) { - continue; - } - for (int copy_index = 0; copy_index < array->num_copies; copy_index++) { - SculptArrayCopy *copy = &array->copies[symm_pass][copy_index]; - invert_m4_m4(copy->imat, copy->mat); - } - } -} - -static void do_array_deform_task_cb_ex(void *__restrict userdata, - const int n, - const TaskParallelTLS *__restrict tls) -{ - SculptThreadedTaskData *data = userdata; - SculptSession *ss = data->ob->sculpt; - SculptArray *array = ss->array; - - bool any_modified = false; - - PBVHVertexIter vd; - BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { - int array_index = ARRAY_INSTANCE_ORIGINAL; - int array_symm_pass = 0; - sculpt_vertex_array_data_get(array, vd.index, &array_index, &array_symm_pass); - - if (array_index == ARRAY_INSTANCE_ORIGINAL) { - continue; - } - - SculptArrayCopy *copy = &array->copies[array_symm_pass][array_index]; - - float co[3]; - copy_v3_v3(co, array->orco[vd.index]); - mul_v3_m4v3(co, array->source_imat, array->orco[vd.index]); - mul_v3_m4v3(co, copy->mat, co); - float source_origin_symm[3]; - flip_v3_v3(source_origin_symm, array->source_origin, array_symm_pass); - add_v3_v3v3(vd.co, co, source_origin_symm); - - any_modified = true; - - if (vd.is_mesh) { - BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); - } - } - BKE_pbvh_vertex_iter_end; - - if (any_modified) { - BKE_pbvh_node_mark_update(data->nodes[n]); - } -} - -static void sculpt_array_deform(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) -{ - /* Threaded loop over nodes. */ - SculptThreadedTaskData data = { - .sd = sd, - .ob = ob, - .nodes = nodes, - }; - - TaskParallelSettings settings; - BKE_pbvh_parallel_range_settings(&settings, true, totnode); - BLI_task_parallel_range(0, totnode, &data, do_array_deform_task_cb_ex, &settings); -} - -static void do_array_smooth_task_cb_ex(void *__restrict userdata, - const int n, - const TaskParallelTLS *__restrict tls) -{ - SculptThreadedTaskData *data = userdata; - SculptSession *ss = data->ob->sculpt; - SculptArray *array = ss->array; - - bool any_modified = false; - - bool check_fsets = ss->cache->brush->flag2 & BRUSH_SMOOTH_PRESERVE_FACE_SETS; - const bool weighted = (ss->cache->brush->flag2 & BRUSH_SMOOTH_USE_AREA_WEIGHT); - - PBVHVertexIter vd; - BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { - int array_index = ARRAY_INSTANCE_ORIGINAL; - int array_symm_pass = 0; - sculpt_vertex_array_data_get(array, vd.index, &array_index, &array_symm_pass); - - const float fade = array->smooth_strength[vd.index]; - - if (fade == 0.0f) { - continue; - } - - float smooth_co[3]; - SCULPT_neighbor_coords_average( - ss, smooth_co, vd.vertex, ss->cache->brush->autosmooth_projection, check_fsets, weighted); - float disp[3]; - sub_v3_v3v3(disp, smooth_co, vd.co); - mul_v3_fl(disp, fade); - add_v3_v3(vd.co, disp); - - /* - if (array_index == ARRAY_INSTANCE_ORIGINAL) { - continue; - } - - bool do_smooth = false; - SculptVertexNeighborIter ni; - SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.index, ni) { - int neighbor_array_index = ARRAY_INSTANCE_ORIGINAL; - int neighbor_symm_pass = 0; - sculpt_vertex_array_data_get(array, ni.index, &neighbor_array_index,&neighbor_symm_pass); - if (neighbor_array_index != array_index) { - do_smooth = true; - } - } - SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); - - if (!do_smooth) { - continue; - } - */ - - any_modified = true; - - if (vd.is_mesh) { - BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); - } - } - BKE_pbvh_vertex_iter_end; - - if (any_modified) { - BKE_pbvh_node_mark_update(data->nodes[n]); - } -} - -static void sculpt_array_smooth(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) -{ - - /* Threaded loop over nodes. */ - SculptSession *ss = ob->sculpt; - SculptArray *array = ss->array; - - if (!array) { - return; - } - - if (!array->smooth_strength) { - return; - } - SculptThreadedTaskData data = { - .sd = sd, - .ob = ob, - .nodes = nodes, - }; - - TaskParallelSettings settings; - BKE_pbvh_parallel_range_settings(&settings, true, totnode); - BLI_task_parallel_range(0, totnode, &data, do_array_smooth_task_cb_ex, &settings); -} - -static void sculpt_array_ensure_original_coordinates(Object *ob, SculptArray *array) -{ - SculptSession *ss = ob->sculpt; - const int totvert = SCULPT_vertex_count_get(ss); - - if (array->orco) { - return; - } - - array->orco = MEM_malloc_arrayN(totvert, sizeof(float) * 3, "array orco"); - - for (int i = 0; i < totvert; i++) { - PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); - - copy_v3_v3(array->orco[i], SCULPT_vertex_co_get(ss, vertex)); - } -} - -static void sculpt_array_ensure_base_transform(Sculpt *sd, Object *ob, SculptArray *array) -{ - SculptSession *ss = ob->sculpt; - Brush *brush = BKE_paint_brush(&sd->paint); - - if (array->source_mat_valid) { - return; - } - - unit_m4(array->source_mat); - - if (brush->flag2 & BRUSH_ARRAY_LOCK_ORIENTATION) { - unit_m4(array->source_mat); - copy_v3_v3(array->source_mat[3], array->source_origin); - invert_m4_m4(array->source_imat, array->source_mat); - array->source_mat_valid = true; - return; - } - - if (is_zero_v3(ss->cache->grab_delta)) { - return; - } - - scultp_array_basis_from_direction(array->source_mat, array, ss->cache->grab_delta); - copy_v3_v3(array->source_mat[3], array->source_origin); - invert_m4_m4(array->source_imat, array->source_mat); - - array->source_mat_valid = true; - return; -} - -static void sculpt_array_path_point_update(SculptArray *array, const int path_point_index) -{ - if (path_point_index == 0) { - return; - } - - const int prev_path_point_index = path_point_index - 1; - - ScultpArrayPathPoint *path_point = &array->path.points[path_point_index]; - ScultpArrayPathPoint *prev_path_point = &array->path.points[prev_path_point_index]; - - if (len_v3v3(prev_path_point->co, path_point->co) <= 0.0001f) { - return; - } - sub_v3_v3v3(prev_path_point->direction, path_point->co, prev_path_point->co); - path_point->length = prev_path_point->length + normalize_v3(prev_path_point->direction); -} - -static void sculpt_array_stroke_sample_add(Object *ob, SculptArray *array) -{ - SculptSession *ss = ob->sculpt; - - if (!array->path.points) { - array->path.points = MEM_malloc_arrayN(9999, sizeof(ScultpArrayPathPoint), "Array Path"); - } - - const int current_point_index = array->path.tot_points; - const int prev_point_index = current_point_index - 1; - - ScultpArrayPathPoint *path_point = &array->path.points[current_point_index]; - - // add_v3_v3v3(path_point->co, ss->cache->orig_grab_location, ss->cache->grab_delta); - copy_v3_v3(path_point->co, ss->cache->grab_delta); - path_point->strength = ss->cache->bstrength; - - if (current_point_index == 0) { - /* First point of the path. */ - path_point->length = 0.0f; - } - else { - ScultpArrayPathPoint *prev_path_point = &array->path.points[prev_point_index]; - if (len_v3v3(prev_path_point->co, path_point->co) <= 0.0001f) { - return; - } - sub_v3_v3v3(prev_path_point->direction, path_point->co, prev_path_point->co); - path_point->length = prev_path_point->length + normalize_v3(prev_path_point->direction); - } - - array->path.tot_points++; -} - -void SCULPT_do_array_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) -{ - SculptSession *ss = ob->sculpt; - Brush *brush = BKE_paint_brush(&sd->paint); - - if (ss->cache->invert) { - if (!ss->array) { - return; - } - - if (SCULPT_stroke_is_first_brush_step(ss->cache)) { - SculptArray *array = ss->array; - const int totvert = SCULPT_vertex_count_get(ss); - - /* Rebuild smooth strength cache. */ - MEM_SAFE_FREE(array->smooth_strength); - array->smooth_strength = MEM_calloc_arrayN(sizeof(float), totvert, "smooth_strength"); - - for (int i = 0; i < totvert; i++) { - PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); - - int array_index = ARRAY_INSTANCE_ORIGINAL; - int array_symm_pass = 0; - sculpt_vertex_array_data_get(array, i, &array_index, &array_symm_pass); - - if (array_index == ARRAY_INSTANCE_ORIGINAL) { - continue; - } - - /* TODO: this can be cached. */ - SculptVertexNeighborIter ni; - SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vertex, ni) { - int neighbor_array_index = ARRAY_INSTANCE_ORIGINAL; - int neighbor_symm_pass = 0; - sculpt_vertex_array_data_get( - array, ni.index, &neighbor_array_index, &neighbor_symm_pass); - if (neighbor_array_index != array_index) { - array->smooth_strength[i] = 1.0f; - break; - } - } - SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); - } - - for (int smooth_iterations = 0; smooth_iterations < 4; smooth_iterations++) { - for (int i = 0; i < totvert; i++) { - PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); - - float avg = array->smooth_strength[i]; - int count = 1; - SculptVertexNeighborIter ni; - SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vertex, ni) { - avg += array->smooth_strength[ni.index]; - count++; - } - SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); - array->smooth_strength[i] = avg / count; - } - } - - /* Update Array Path Orco. */ - for (int i = 0; i < array->path.tot_points; i++) { - ScultpArrayPathPoint *point = &array->path.points[i]; - copy_v3_v3(point->orco, point->co); - } - array->initial_radial_angle = array->radial_angle; - - /* Update Geometry Orco. */ - for (int i = 0; i < totvert; i++) { - PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); - - int array_index = ARRAY_INSTANCE_ORIGINAL; - int array_symm_pass = 0; - sculpt_vertex_array_data_get(array, i, &array_index, &array_symm_pass); - - if (array_index == ARRAY_INSTANCE_ORIGINAL) { - continue; - } - SculptArrayCopy *copy = &array->copies[array_symm_pass][array_index]; - // sub_v3_v3v3(array->orco[i], SCULPT_vertex_co_get(ss, i), copy->mat[3]); - float co[3]; - float source_origin_symm[3]; - copy_v3_v3(co, SCULPT_vertex_co_get(ss, vertex)); - flip_v3_v3(source_origin_symm, array->source_origin, array_symm_pass); - mul_v3_m4v3(co, copy->imat, co); - mul_v3_m4v3(co, array->source_imat, co); - // sub_v3_v3v3(co, co, source_origin_symm); - - copy_v3_v3(array->orco[i], co); - } - } - - SculptArray *array = ss->array; - if (array->mode == BRUSH_ARRAY_DEFORM_PATH) { - /* Deform path */ - for (int i = 0; i < array->path.tot_points; i++) { - ScultpArrayPathPoint *point = &array->path.points[i]; - float point_co[3]; - add_v3_v3v3(point_co, point->orco, array->source_origin); - const float len = len_v3v3(ss->cache->true_location, point_co); - const float fade = ss->cache->bstrength * - BKE_brush_curve_strength(brush, len, ss->cache->radius); - if (fade <= 0.0f) { - continue; - } - madd_v3_v3v3fl(point->co, point->orco, ss->cache->grab_delta, fade); - } - for (int i = 0; i < array->path.tot_points; i++) { - sculpt_array_path_point_update(array, i); - } - } - else { - /* Tweak radial angle. */ - /* - const float factor = 1.0f - ( len_v3(ss->cache->grab_delta) / ss->cache->initial_radius); - array->radial_angle = array->initial_radial_angle * clamp_f(factor, 0.0f, 1.0f); - */ - - float array_disp_co[3]; - float brush_co[3]; - add_v3_v3v3(brush_co, ss->cache->initial_location, ss->cache->grab_delta); - sub_v3_v3(brush_co, array->source_origin); - normalize_v3(brush_co); - normalize_v3_v3(array_disp_co, sculpt_array_delta_from_path(array)); - array->radial_angle = angle_signed_on_axis_v3v3_v3(brush_co, array_disp_co, array->normal); - } - - sculpt_array_update(ob, brush, ss->array); - sculpt_array_deform(sd, ob, nodes, totnode); - for (int i = 0; i < 5; i++) { - sculpt_array_smooth(sd, ob, nodes, totnode); - } - - return; - } - - if (SCULPT_get_int(ss, array_count, sd, brush) == 0) { - return; - } - - if (!SCULPT_stroke_is_main_symmetry_pass(ss->cache)) { - /* This brush manages its own symmetry. */ - return; - } - - if (SCULPT_stroke_is_first_brush_step(ss->cache)) { - if (ss->array) { - sculpt_array_cache_free(ss->array); - } - - ss->array = sculpt_array_cache_create(ob, - SCULPT_get_int(ss, array_deform_type, sd, brush), - SCULPT_get_int(ss, array_count, sd, brush)); - sculpt_array_init(ob, brush, ss->array); - sculpt_array_stroke_sample_add(ob, ss->array); - sculpt_array_mesh_build(sd, ob, ss->array); - /* Original coordinates can't be stored yet as the SculptSession data needs to be updated after - * the mesh modifications performed when building the array geometry. */ - return; - } - - SCULPT_vertex_random_access_ensure(ss); - - sculpt_array_ensure_base_transform(sd, ob, ss->array); - sculpt_array_ensure_original_coordinates(ob, ss->array); - sculpt_array_ensure_geometry_indices(ob, ss->array); - - sculpt_array_stroke_sample_add(ob, ss->array); - - sculpt_array_update(ob, brush, ss->array); - - sculpt_array_deform(sd, ob, nodes, totnode); -} - -void SCULPT_array_path_draw(const uint gpuattr, Brush *brush, SculptSession *ss) -{ - - SculptArray *array = ss->array; - - /* Disable debug drawing. */ - return; - - if (!array) { - return; - } - - if (!array->path.points) { - return; - } - - if (array->path.tot_points < 2) { - return; - } - - const int tot_points = array->path.tot_points; - immBegin(GPU_PRIM_LINE_STRIP, tot_points); - for (int i = 0; i < tot_points; i++) { - float co[3]; - copy_v3_v3(co, array->path.points[i].co); - add_v3_v3(co, array->source_origin); - immVertex3fv(gpuattr, co); - } - immEnd(); -} diff --git a/source/blender/editors/sculpt_paint/sculpt_brush_types.cc b/source/blender/editors/sculpt_paint/sculpt_brush_types.cc index e56108a3631..afdeef8d93c 100644 --- a/source/blender/editors/sculpt_paint/sculpt_brush_types.cc +++ b/source/blender/editors/sculpt_paint/sculpt_brush_types.cc @@ -2395,11 +2395,7 @@ void SCULPT_relax_vertex(SculptSession *ss, zero_v3(smooth_pos); eSculptBoundary bset = boundary_mask; - - // forcibly enable if no ss->cache - if (ss->cache && (ss->cache->brush->flag2 & BRUSH_SMOOTH_PRESERVE_FACE_SETS)) { - bset |= SCULPT_BOUNDARY_FACE_SET; - } + bset |= SCULPT_BOUNDARY_FACE_SET; if (SCULPT_vertex_is_corner(ss, vd->vertex, (eSculptCorner)bset)) { copy_v3_v3(r_final_pos, vd->co); diff --git a/source/blender/editors/sculpt_paint/sculpt_face_set.cc b/source/blender/editors/sculpt_paint/sculpt_face_set.cc index 8c97b44fd9f..3f3818be475 100644 --- a/source/blender/editors/sculpt_paint/sculpt_face_set.cc +++ b/source/blender/editors/sculpt_paint/sculpt_face_set.cc @@ -379,9 +379,9 @@ void do_draw_face_sets_brush_task_cb_ex(void *__restrict userdata, int fset = abs(ss->face_sets[vert_map->indices[j]]); - // XXX kind of hackish, tries to sample faces that are within - // 8 pixels of the center of the brush, and using a crude linear - // scale at that - joeedh + /* Sample faces that are within + * 8 pixels of the center of the brush. + */ if (set_active_faceset && fset != abs(ss->cache->automasking->settings.initial_face_set)) { @@ -475,9 +475,9 @@ void do_draw_face_sets_brush_task_cb_ex(void *__restrict userdata, bool ok = true; - // XXX kind of hackish, tries to sample faces that are within - // 8 pixels of the center of the brush, and using a crude linear - // scale at that - joeedh + /* Sample faces that are within + * 8 pixels of the center of the brush. + */ if (set_active_faceset && abs(fset) != abs(ss->cache->automasking->settings.initial_face_set)) { diff --git a/source/blender/editors/sculpt_paint/sculpt_pose.cc b/source/blender/editors/sculpt_paint/sculpt_pose.cc index 07981588239..e864a2f6b26 100644 --- a/source/blender/editors/sculpt_paint/sculpt_pose.cc +++ b/source/blender/editors/sculpt_paint/sculpt_pose.cc @@ -134,75 +134,6 @@ static void pose_solve_scale_chain(SculptPoseIKChain *ik_chain, const float scal } } -static void do_pose_brush_bend_task_cb_ex(void *__restrict userdata, - const int n, - const TaskParallelTLS *__restrict /* tls */) -{ - SculptThreadedTaskData *data = static_cast(userdata); - SculptSession *ss = data->ob->sculpt; - SculptPoseIKChain *ik_chain = ss->cache->pose_ik_chain; - SculptPoseIKChainSegment *segments = ik_chain->segments; - const Brush *brush = data->brush; - - if (fabsf(ik_chain->bend_factor) <= 0.00001f) { - return; - } - - float final_pos[3]; - - SculptOrigVertData orig_data; - SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COORDS); - - PBVHVertexIter vd; - BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { - SCULPT_orig_vert_data_update(&orig_data, vd.vertex); - - const float ik_chain_weight = segments[0].weights[vd.index] * - (1.0f - SCULPT_vertex_mask_get(ss, vd.vertex)); - if (ik_chain_weight == 0.0f) { - continue; - } - - float orig_co[3]; - mul_v3_m4v3(orig_co, ik_chain->bend_mat_inv, orig_data.co); - - const float bend_factor = ik_chain->bend_factor; - - if (fabsf(bend_factor) <= 0.0000001f) { - continue; - } - - if (orig_co[0] < 0.0f) { - continue; - } - - const float theta = orig_co[0] * bend_factor; - const float sint = sinf(theta); - const float cost = cosf(theta); - - float new_co[3]; - new_co[0] = -(orig_co[1] - 1.0f / bend_factor) * sint; - new_co[1] = (orig_co[1] - 1.0f / bend_factor) * cost + 1.0f / bend_factor; - new_co[2] = orig_co[2]; - - float final_co[3]; - float disp[3]; - mul_v3_m4v3(final_co, ik_chain->bend_mat, new_co); - - sub_v3_v3v3(disp, final_co, orig_data.co); - mul_v3_fl(disp, ik_chain_weight); - add_v3_v3v3(final_pos, orig_data.co, disp); - - float *target_co = SCULPT_brush_deform_target_vertex_co_get(ss, brush->deform_target, &vd); - copy_v3_v3(target_co, final_pos); - - if (vd.is_mesh) { - BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); - } - } - BKE_pbvh_vertex_iter_end; -} - static void do_pose_brush_task_cb_ex(void *__restrict userdata, const int n, const TaskParallelTLS *__restrict /*tls*/) @@ -709,7 +640,6 @@ static int pose_brush_num_effective_segments(const Brush *brush) * artifacts in the areas affected by multiple segments. */ if (ELEM(brush->pose_deform_type, BRUSH_POSE_DEFORM_SCALE_TRASLATE, - BRUSH_POSE_DEFORM_BEND, BRUSH_POSE_DEFORM_SQUASH_STRETCH)) { return 1; } @@ -1179,61 +1109,6 @@ static void sculpt_pose_do_squash_stretch_deform(SculptSession *ss, Brush * /*br pose_solve_scale_chain(ik_chain, scale); } -static void sculpt_pose_do_bend_deform(SculptSession *ss, Brush * /* brush */) -{ - const int totvert = SCULPT_vertex_count_get(ss); - SculptPoseIKChain *ik_chain = ss->cache->pose_ik_chain; - - if (SCULPT_stroke_is_first_brush_step_of_symmetry_pass(ss->cache)) { - sub_v3_v3v3(ik_chain->bend_mat[0], - ik_chain->segments[0].initial_head, - ik_chain->segments[0].initial_orig); - normalize_v3(ik_chain->bend_mat[0]); - copy_v3_v3(ik_chain->bend_mat[2], ss->cache->view_normal); - normalize_v3(ik_chain->bend_mat[2]); - cross_v3_v3v3(ik_chain->bend_mat[1], ik_chain->bend_mat[0], ik_chain->bend_mat[2]); - normalize_v3(ik_chain->bend_mat[1]); - copy_v3_v3(ik_chain->bend_mat[3], ik_chain->segments[0].initial_orig); - ik_chain->bend_mat[3][3] = 1.0f; - invert_m4_m4(ik_chain->bend_mat_inv, ik_chain->bend_mat); - - float lower = FLT_MAX; - float upper = -FLT_MAX; - - float smd_limit[2]; - - for (int i = 0; i < totvert; i++) { - PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); - - if (ik_chain->segments[0].weights[i] == 0.0f) { - continue; - } - float bend_space_vert_co[3]; - mul_v3_m4v3(bend_space_vert_co, ik_chain->bend_mat_inv, SCULPT_vertex_co_get(ss, vertex)); - lower = min_ff(lower, bend_space_vert_co[0]); - upper = max_ff(upper, bend_space_vert_co[0]); - } - - ik_chain->bend_upper_limit = upper; - smd_limit[1] = lower + (upper - lower) * 1.0f; - smd_limit[0] = lower + (upper - lower) * 0.0f; - ik_chain->bend_limit = max_ff(FLT_EPSILON, smd_limit[1] - smd_limit[0]); - } - - float *original_dir = ik_chain->bend_mat[0]; - float current_dir[3]; - float brush_location[3]; - add_v3_v3v3(brush_location, ss->cache->initial_location, ss->cache->grab_delta); - sub_v3_v3v3(current_dir, brush_location, ik_chain->segments[0].initial_orig); - ik_chain->bend_factor = angle_signed_on_axis_v3v3_v3( - original_dir, current_dir, ss->cache->view_normal); - if (ik_chain->bend_factor > M_PI) { - ik_chain->bend_factor = ik_chain->bend_factor - (M_PI * 2.0f); - } - - ik_chain->bend_factor = 2.0f * (ik_chain->bend_factor / ik_chain->bend_limit); -} - static void sculpt_pose_align_pivot_local_space(float r_mat[4][4], ePaintSymmetryFlags symm, ePaintSymmetryAreas symm_area, @@ -1281,9 +1156,6 @@ void SCULPT_do_pose_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) case BRUSH_POSE_DEFORM_SQUASH_STRETCH: sculpt_pose_do_squash_stretch_deform(ss, brush); break; - case BRUSH_POSE_DEFORM_BEND: - sculpt_pose_do_bend_deform(ss, brush); - break; } /* Flip the segment chain in all symmetry axis and calculate the transform matrices for each @@ -1354,12 +1226,7 @@ void SCULPT_do_pose_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) TaskParallelSettings settings; BKE_pbvh_parallel_range_settings(&settings, true, totnode); - if (brush->pose_deform_type == BRUSH_POSE_DEFORM_BEND) { - BLI_task_parallel_range(0, totnode, &data, do_pose_brush_bend_task_cb_ex, &settings); - } - else { - BLI_task_parallel_range(0, totnode, &data, do_pose_brush_task_cb_ex, &settings); - } + BLI_task_parallel_range(0, totnode, &data, do_pose_brush_task_cb_ex, &settings); } void SCULPT_pose_ik_chain_free(SculptPoseIKChain *ik_chain) diff --git a/source/blender/editors/sculpt_paint/sculpt_symmetrize.c b/source/blender/editors/sculpt_paint/sculpt_symmetrize.c deleted file mode 100644 index cbcf97574fb..00000000000 --- a/source/blender/editors/sculpt_paint/sculpt_symmetrize.c +++ /dev/null @@ -1,302 +0,0 @@ -/* - * 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) 2021 Blender Foundation. - * All rights reserved. - */ - -/** \file - * \ingroup edsculpt - */ - -#include "MEM_guardedalloc.h" - -#include "BLI_blenlib.h" -#include "BLI_math.h" -#include "BLI_task.h" - -#include "DNA_brush_types.h" -#include "DNA_mesh_types.h" -#include "DNA_meshdata_types.h" -#include "DNA_object_types.h" - -#include "BKE_brush.h" -#include "BKE_ccg.h" -#include "BKE_colortools.h" -#include "BKE_context.h" -#include "BKE_mesh.h" -#include "BKE_multires.h" -#include "BKE_node.h" -#include "BKE_object.h" -#include "BKE_paint.h" -#include "BKE_pbvh.h" -#include "BKE_scene.h" - -#include "paint_intern.h" -#include "sculpt_intern.h" - -#include "GPU_immediate.h" -#include "GPU_immediate_util.h" -#include "GPU_matrix.h" -#include "GPU_state.h" - -#include "bmesh.h" - -#include -#include - -typedef uint MirrTopoHash_t; - -typedef struct MirrTopoVert_t { - MirrTopoHash_t hash; - int v_index; -} MirrTopoVert_t; - -static int mirrtopo_hash_sort(const void *l1, const void *l2) -{ - if ((MirrTopoHash_t)(intptr_t)l1 > (MirrTopoHash_t)(intptr_t)l2) { - return 1; - } - if ((MirrTopoHash_t)(intptr_t)l1 < (MirrTopoHash_t)(intptr_t)l2) { - return -1; - } - return 0; -} - -static int mirrtopo_vert_sort(const void *v1, const void *v2) -{ - if (((MirrTopoVert_t *)v1)->hash > ((MirrTopoVert_t *)v2)->hash) { - return 1; - } - if (((MirrTopoVert_t *)v1)->hash < ((MirrTopoVert_t *)v2)->hash) { - return -1; - } - return 0; -} - -void SCULPT_symmetrize_map_ensure(Object *ob) -{ - SculptSession *ss = ob->sculpt; - Mesh *me = BKE_object_get_original_mesh(ob); - - if (ss->vertex_info.symmetrize_map) { - /* Nothing to do. */ - return; - } - - MEdge *medge = NULL, *med; - - int a, last; - int totvert, totedge; - int tot_unique = -1, tot_unique_prev = -1; - int tot_unique_edges = 0, tot_unique_edges_prev; - - MirrTopoHash_t *topo_hash = NULL; - MirrTopoHash_t *topo_hash_prev = NULL; - MirrTopoVert_t *topo_pairs; - MirrTopoHash_t topo_pass = 1; - - int *index_lookup; /* direct access to mesh_topo_store->index_lookup */ - - totvert = me->totvert; - topo_hash = MEM_callocN(totvert * sizeof(MirrTopoHash_t), "TopoMirr"); - - /* Initialize the vert-edge-user counts used to detect unique topology */ - totedge = me->totedge; - medge = me->medge; - - for (a = 0, med = medge; a < totedge; a++, med++) { - const uint i1 = med->v1, i2 = med->v2; - topo_hash[i1]++; - topo_hash[i2]++; - } - - topo_hash_prev = MEM_dupallocN(topo_hash); - - tot_unique_prev = -1; - tot_unique_edges_prev = -1; - while (true) { - /* use the number of edges per vert to give verts unique topology IDs */ - - tot_unique_edges = 0; - - /* This can make really big numbers, wrapping around here is fine */ - for (a = 0, med = medge; a < totedge; a++, med++) { - const uint i1 = med->v1, i2 = med->v2; - topo_hash[i1] += topo_hash_prev[i2] * topo_pass; - topo_hash[i2] += topo_hash_prev[i1] * topo_pass; - tot_unique_edges += (topo_hash[i1] != topo_hash[i2]); - } - memcpy(topo_hash_prev, topo_hash, sizeof(MirrTopoHash_t) * totvert); - - /* sort so we can count unique values */ - qsort(topo_hash_prev, totvert, sizeof(MirrTopoHash_t), mirrtopo_hash_sort); - - tot_unique = 1; /* account for skipping the first value */ - for (a = 1; a < totvert; a++) { - if (topo_hash_prev[a - 1] != topo_hash_prev[a]) { - tot_unique++; - } - } - - if ((tot_unique <= tot_unique_prev) && (tot_unique_edges <= tot_unique_edges_prev)) { - /* Finish searching for unique values when 1 loop doesn't give a - * higher number of unique values compared to the previous loop. */ - break; - } - tot_unique_prev = tot_unique; - tot_unique_edges_prev = tot_unique_edges; - /* Copy the hash calculated this iteration, so we can use them next time */ - memcpy(topo_hash_prev, topo_hash, sizeof(MirrTopoHash_t) * totvert); - - topo_pass++; - } - - /* Hash/Index pairs are needed for sorting to find index pairs */ - topo_pairs = MEM_callocN(sizeof(MirrTopoVert_t) * totvert, "MirrTopoPairs"); - - /* since we are looping through verts, initialize these values here too */ - index_lookup = MEM_mallocN(totvert * sizeof(int), "mesh_topo_lookup"); - - for (a = 0; a < totvert; a++) { - topo_pairs[a].hash = topo_hash[a]; - topo_pairs[a].v_index = a; - - /* initialize lookup */ - index_lookup[a] = -1; - } - - qsort(topo_pairs, totvert, sizeof(MirrTopoVert_t), mirrtopo_vert_sort); - - last = 0; - - /* Get the pairs out of the sorted hashes, note, totvert+1 means we can use the previous 2, - * but you cant ever access the last 'a' index of MirrTopoPairs */ - for (a = 1; a <= totvert; a++) { - if ((a == totvert) || (topo_pairs[a - 1].hash != topo_pairs[a].hash)) { - const int match_count = a - last; - if (match_count == 2) { - const int j = topo_pairs[a - 1].v_index, k = topo_pairs[a - 2].v_index; - index_lookup[j] = k; - index_lookup[k] = j; - } - else if (match_count == 1) { - /* Center vertex. */ - const int j = topo_pairs[a - 1].v_index; - index_lookup[j] = j; - } - last = a; - } - } - - MEM_freeN(topo_pairs); - topo_pairs = NULL; - - MEM_freeN(topo_hash); - MEM_freeN(topo_hash_prev); - - ss->vertex_info.symmetrize_map = index_lookup; -} - -static void do_shape_symmetrize_brush_task_cb(void *__restrict userdata, - const int n, - const TaskParallelTLS *__restrict tls) -{ - SculptThreadedTaskData *data = userdata; - SculptSession *ss = data->ob->sculpt; - const Brush *brush = data->brush; - - SculptBrushTest test; - SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init(ss, &test); - const int thread_id = BLI_task_parallel_thread_id(tls); - - AutomaskingNodeData automask_data; - SCULPT_automasking_node_begin( - data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]); - - PBVHVertexIter vd; - BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { - if (!sculpt_brush_test_sq_fn(&test, vd.co)) { - continue; - } - - SCULPT_automasking_node_update(ss, &automask_data, &vd); - - const int symmetrical_index = ss->vertex_info.symmetrize_map[vd.index]; - const PBVHVertRef symmetrical_vertex = BKE_pbvh_index_to_vertex(ss->pbvh, symmetrical_index); - - if (symmetrical_index == -1) { - continue; - } - - float symm_co[3]; - copy_v3_v3(symm_co, SCULPT_vertex_co_get(ss, symmetrical_vertex)); - - symm_co[0] *= -1; - float new_co[3]; - copy_v3_v3(new_co, symm_co); - - const float fade = SCULPT_brush_strength_factor(ss, - brush, - vd.co, - sqrtf(test.dist), - vd.no, - vd.fno, - vd.mask ? *vd.mask : 0.0f, - vd.vertex, - thread_id, - &automask_data); - - float disp[3]; - sub_v3_v3v3(disp, new_co, vd.co); - madd_v3_v3v3fl(vd.co, vd.co, disp, fade); - - if (vd.is_mesh) { - BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); - } - - BKE_pbvh_vertex_iter_end; - } -} - -/* Public functions. */ - -/* Main Brush Function. */ -void SCULPT_do_symmetrize_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) -{ - SculptSession *ss = ob->sculpt; - Brush *brush = BKE_paint_brush(&sd->paint); - - if (BKE_pbvh_type(ss->pbvh) != PBVH_FACES) { - return; - } - - if (!SCULPT_stroke_is_main_symmetry_pass(ss->cache)) { - return; - } - - SCULPT_symmetrize_map_ensure(ob); - - SculptThreadedTaskData data = { - .sd = sd, - .ob = ob, - .brush = brush, - .nodes = nodes, - }; - - TaskParallelSettings settings; - BKE_pbvh_parallel_range_settings(&settings, true, totnode); - BLI_task_parallel_range(0, totnode, &data, do_shape_symmetrize_brush_task_cb, &settings); -} diff --git a/source/blender/editors/space_buttons/CMakeLists.txt b/source/blender/editors/space_buttons/CMakeLists.txt index b5f8ef0e109..85696bae61d 100644 --- a/source/blender/editors/space_buttons/CMakeLists.txt +++ b/source/blender/editors/space_buttons/CMakeLists.txt @@ -10,8 +10,8 @@ set(INC ../../makesdna ../../makesrna ../../windowmanager - ../../../../intern/atomic ../../../../intern/guardedalloc + ../../../../intern/atomic ../../bmesh # dna_type_offsets.h diff --git a/source/blender/editors/space_buttons/buttons_context.c b/source/blender/editors/space_buttons/buttons_context.c index f8f468a39b2..c5f457b3fd8 100644 --- a/source/blender/editors/space_buttons/buttons_context.c +++ b/source/blender/editors/space_buttons/buttons_context.c @@ -556,7 +556,7 @@ static bool buttons_context_path( path->len++; } /* No pinned root, use scene as initial root. */ - else if (!ELEM(mainb, BCONTEXT_TOOL, BCONTEXT_BRUSH_EDITOR)) { + else if (mainb != BCONTEXT_TOOL) { RNA_id_pointer_create(&scene->id, &path->ptr[0]); path->len++; @@ -597,7 +597,6 @@ static bool buttons_context_path( case BCONTEXT_COLLECTION: /* This is for Line Art collection flags */ found = buttons_context_path_collection(C, path, window); break; - case BCONTEXT_BRUSH_EDITOR: case BCONTEXT_TOOL: found = true; break; @@ -858,7 +857,7 @@ int /*eContextResult*/ buttons_context(const bContext *C, return CTX_RESULT_MEMBER_NOT_FOUND; } - if (ELEM(sbuts->mainb, BCONTEXT_TOOL, BCONTEXT_BRUSH_EDITOR)) { + if (sbuts->mainb == BCONTEXT_TOOL) { return CTX_RESULT_MEMBER_NOT_FOUND; } @@ -1169,7 +1168,7 @@ int /*eContextResult*/ buttons_context(const bContext *C, static bool buttons_panel_context_poll(const bContext *C, PanelType *UNUSED(pt)) { SpaceProperties *sbuts = CTX_wm_space_properties(C); - return !ELEM(sbuts->mainb, BCONTEXT_TOOL, BCONTEXT_BRUSH_EDITOR); + return sbuts->mainb != BCONTEXT_TOOL; } static void buttons_panel_context_draw(const bContext *C, Panel *panel) diff --git a/source/blender/editors/space_buttons/space_buttons.c b/source/blender/editors/space_buttons/space_buttons.c index 81aa437b12a..5a333869dea 100644 --- a/source/blender/editors/space_buttons/space_buttons.c +++ b/source/blender/editors/space_buttons/space_buttons.c @@ -160,10 +160,6 @@ int ED_buttons_tabs_list(SpaceProperties *sbuts, short *context_tabs_array) context_tabs_array[length] = BCONTEXT_TOOL; length++; } - if (sbuts->pathflag & (1 << BCONTEXT_BRUSH_EDITOR)) { - context_tabs_array[length] = BCONTEXT_BRUSH_EDITOR; - length++; - } if (length != 0) { context_tabs_array[length] = -1; length++; @@ -291,8 +287,6 @@ static const char *buttons_main_region_context_string(const short mainb) return "bone_constraint"; case BCONTEXT_TOOL: return "tool"; - case BCONTEXT_BRUSH_EDITOR: - return "brush_editor"; } /* All the cases should be handled. */ @@ -347,7 +341,7 @@ static bool property_search_for_context(const bContext *C, ARegion *region, Spac { const char *contexts[2] = {buttons_main_region_context_string(sbuts->mainb), NULL}; - if (sbuts->mainb == BCONTEXT_TOOL || sbuts->mainb == BCONTEXT_BRUSH_EDITOR) { + if (sbuts->mainb == BCONTEXT_TOOL) { return false; } @@ -360,7 +354,7 @@ static void property_search_move_to_next_tab_with_results(SpaceProperties *sbuts const int tabs_len) { /* As long as all-tab search in the tool is disabled in the tool context, don't move from it. */ - if (sbuts->mainb == BCONTEXT_TOOL || sbuts->mainb == BCONTEXT_BRUSH_EDITOR) { + if (sbuts->mainb == BCONTEXT_TOOL) { return; } @@ -501,9 +495,6 @@ static void buttons_main_region_layout(const bContext *C, ARegion *region) if (sbuts->mainb == BCONTEXT_TOOL) { ED_view3d_buttons_region_layout_ex(C, region, "Tool"); } - else if (sbuts->mainb == BCONTEXT_BRUSH_EDITOR) { - ED_view3d_buttons_region_layout_ex(C, region, "Tool"); - } else { buttons_main_region_layout_properties(C, sbuts, region); } @@ -588,7 +579,7 @@ static void buttons_header_region_message_subscribe(const wmRegionMessageSubscri WM_msg_subscribe_rna_anon_prop(mbus, ViewLayer, name, &msg_sub_value_region_tag_redraw); } - if (ELEM(sbuts->mainb, BCONTEXT_TOOL, BCONTEXT_BRUSH_EDITOR)) { + if (sbuts->mainb == BCONTEXT_TOOL) { WM_msg_subscribe_rna_anon_prop(mbus, WorkSpace, tools, &msg_sub_value_region_tag_redraw); } } @@ -780,7 +771,6 @@ static void buttons_area_listener(const wmSpaceTypeListenerParams *params) case NC_BRUSH: buttons_area_redraw(area, BCONTEXT_TEXTURE); buttons_area_redraw(area, BCONTEXT_TOOL); - buttons_area_redraw(area, BCONTEXT_BRUSH_EDITOR); sbuts->preview = 1; break; case NC_TEXTURE: diff --git a/source/blender/editors/space_view3d/CMakeLists.txt b/source/blender/editors/space_view3d/CMakeLists.txt index 696d5810a61..c0792eb8a18 100644 --- a/source/blender/editors/space_view3d/CMakeLists.txt +++ b/source/blender/editors/space_view3d/CMakeLists.txt @@ -17,8 +17,8 @@ set(INC ../../render ../../windowmanager ../../../../intern/guardedalloc - ../../../../intern/atomic ../../../../intern/mantaflow/extern + ../../../../intern/atomic # dna_type_offsets.h ${CMAKE_CURRENT_BINARY_DIR}/../../makesdna/intern diff --git a/source/blender/editors/space_view3d/space_view3d.cc b/source/blender/editors/space_view3d/space_view3d.cc index 1ced76e7eaa..cd7794300f6 100644 --- a/source/blender/editors/space_view3d/space_view3d.cc +++ b/source/blender/editors/space_view3d/space_view3d.cc @@ -1687,16 +1687,9 @@ void ED_view3d_buttons_region_layout_ex(const bContext *C, case CTX_MODE_POSE: ARRAY_SET_ITEMS(contexts, ".posemode"); break; - case CTX_MODE_SCULPT: { - SpaceProperties *sbuts = CTX_wm_space_properties(C); - if (sbuts && sbuts->mainb == BCONTEXT_BRUSH_EDITOR) { - ARRAY_SET_ITEMS(contexts, ".paint_common", ".brush_editor"); - } - else { - ARRAY_SET_ITEMS(contexts, ".paint_common", ".sculpt_mode"); - } + case CTX_MODE_SCULPT: + ARRAY_SET_ITEMS(contexts, ".paint_common", ".sculpt_mode"); break; - } case CTX_MODE_PAINT_WEIGHT: ARRAY_SET_ITEMS(contexts, ".paint_common", ".weightpaint"); break; diff --git a/source/blender/gpu/CMakeLists.txt b/source/blender/gpu/CMakeLists.txt index f45ed7037af..4967d8984c5 100644 --- a/source/blender/gpu/CMakeLists.txt +++ b/source/blender/gpu/CMakeLists.txt @@ -35,11 +35,11 @@ set(INC # For node muting stuff. ../nodes + ../../../intern/atomic ../../../intern/clog ../../../intern/ghost ../../../intern/guardedalloc ../../../intern/mantaflow/extern - ../../../intern/atomic ) set(INC_SYS @@ -698,9 +698,6 @@ if(WITH_OPENCOLORIO) add_definitions(-DWITH_OCIO) endif() -remove_cc_flag("-fsanitize=address") -remove_cc_flag("/fsanitize=address") - blender_add_lib(bf_gpu "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") target_link_libraries(bf_gpu PUBLIC bf_compositor_shaders diff --git a/source/blender/gpu/GPU_vertex_format.h b/source/blender/gpu/GPU_vertex_format.h index abb88e329aa..0a59903ff04 100644 --- a/source/blender/gpu/GPU_vertex_format.h +++ b/source/blender/gpu/GPU_vertex_format.h @@ -98,8 +98,6 @@ void GPU_vertformat_from_shader(GPUVertFormat *format, const struct GPUShader *s uint GPU_vertformat_attr_add( GPUVertFormat *, const char *name, GPUVertCompType, uint comp_len, GPUVertFetchMode); void GPU_vertformat_alias_add(GPUVertFormat *, const char *alias); -void GPU_vertformat_alias_clear(GPUVertFormat *format, int attr_id); -void GPU_vertformat_alias_add_n(GPUVertFormat *format, int attr_id, const char *alias); /** * Makes vertex attribute from the next vertices to be accessible in the vertex shader. diff --git a/source/blender/gpu/intern/gpu_batch.cc b/source/blender/gpu/intern/gpu_batch.cc index cc23a66d1c1..1acdfb0a1a9 100644 --- a/source/blender/gpu/intern/gpu_batch.cc +++ b/source/blender/gpu/intern/gpu_batch.cc @@ -288,9 +288,6 @@ void GPU_batch_draw_advanced(GPUBatch *gpu_batch, if (batch->elem) { vertex_count = batch->elem_()->index_len_get(); } - else if (!batch->verts_(0)) { - return; - } else { vertex_count = batch->verts_(0)->vertex_len; } diff --git a/source/blender/gpu/opengl/gl_shader.cc b/source/blender/gpu/opengl/gl_shader.cc index d63af5e8377..58407db015d 100644 --- a/source/blender/gpu/opengl/gl_shader.cc +++ b/source/blender/gpu/opengl/gl_shader.cc @@ -7,7 +7,6 @@ #include "BKE_global.h" -#include "BLI_compiler_attrs.h" #include "BLI_string.h" #include "BLI_vector.hh" @@ -20,14 +19,6 @@ #include "gl_shader.hh" #include "gl_shader_interface.hh" -#if defined(__clang__) || defined(__GCC__) -# define ATTR_NO_ASAN __attribute__((no_sanitize("address"))) -#elif _MSC_VER -# define ATTR_NO_ASAN __declspec(no_sanitize_address) -#else -# define ATTR_NO_ASAN -#endif - using namespace blender; using namespace blender::gpu; using namespace blender::gpu::shader; @@ -980,7 +971,7 @@ void GLShader::compute_shader_from_glsl(MutableSpan sources) compute_shader_ = this->create_shader_stage(GL_COMPUTE_SHADER, sources); } -ATTR_NO_ASAN bool GLShader::finalize(const shader::ShaderCreateInfo *info) +bool GLShader::finalize(const shader::ShaderCreateInfo *info) { if (compilation_failed_) { return false; diff --git a/source/blender/makesdna/DNA_brush_enums.h b/source/blender/makesdna/DNA_brush_enums.h index 66b2a476963..d18afa24461 100644 --- a/source/blender/makesdna/DNA_brush_enums.h +++ b/source/blender/makesdna/DNA_brush_enums.h @@ -224,14 +224,11 @@ typedef enum eBrushClothDeformType { BRUSH_CLOTH_DEFORM_INFLATE = 5, BRUSH_CLOTH_DEFORM_EXPAND = 6, BRUSH_CLOTH_DEFORM_SNAKE_HOOK = 7, - BRUSH_CLOTH_DEFORM_ELASTIC_DRAG = 8, } eBrushClothDeformType; typedef enum eBrushSmoothDeformType { BRUSH_SMOOTH_DEFORM_LAPLACIAN = 0, BRUSH_SMOOTH_DEFORM_SURFACE = 1, - BRUSH_SMOOTH_DEFORM_DIRECTIONAL = 2, - BRUSH_SMOOTH_DEFORM_UNIFORM_WEIGHTS = 3, } eBrushSmoothDeformType; typedef enum eBrushClothForceFalloffType { @@ -264,12 +261,6 @@ typedef enum eBrushSmearDeformType { BRUSH_SMEAR_DEFORM_EXPAND = 2, } eBrushSmearDeformType; -typedef enum eBrushArrayDeformType { - BRUSH_ARRAY_DEFORM_LINEAR = 0, - BRUSH_ARRAY_DEFORM_RADIAL = 1, - BRUSH_ARRAY_DEFORM_PATH = 2, -} eBrushArrayDeformType; - typedef enum eBrushSlideDeformType { BRUSH_SLIDE_DEFORM_DRAG = 0, BRUSH_SLIDE_DEFORM_PINCH = 1, @@ -293,12 +284,6 @@ typedef enum eBrushBoundaryFalloffType { BRUSH_BOUNDARY_FALLOFF_LOOP_INVERT = 3, } eBrushBoundaryFalloffType; -typedef enum eBrushSceneProjectDirectionType { - BRUSH_SCENE_PROJECT_DIRECTION_VIEW = 0, - BRUSH_SCENE_PROJECT_DIRECTION_VERTEX_NORMAL = 1, - BRUSH_SCENE_PROJECT_DIRECTION_BRUSH_NORMAL = 2, -} eBrushSceneProjectDirectionType; - typedef enum eBrushSnakeHookDeformType { BRUSH_SNAKE_HOOK_DEFORM_FALLOFF = 0, BRUSH_SNAKE_HOOK_DEFORM_ELASTIC = 1, @@ -430,24 +415,17 @@ typedef enum eBrushFlags2 { BRUSH_AREA_RADIUS_PRESSURE = (1 << 7), BRUSH_GRAB_SILHOUETTE = (1 << 8), - BRUSH_USE_SURFACE_FALLOFF = (1 << 9), - BRUSH_ARRAY_LOCK_ORIENTATION = (1 << 10), - BRUSH_ARRAY_FILL_HOLES = (1 << 11), - - BRUSH_CURVATURE_RAKE = (1 << 12), - BRUSH_CUSTOM_AUTOSMOOTH_SPACING = (1 << 13), - BRUSH_CUSTOM_TOPOLOGY_RAKE_SPACING = (1 << 14), - BRUSH_TOPOLOGY_RAKE_IGNORE_BRUSH_FALLOFF = (1 << 15), - BRUSH_SMOOTH_USE_AREA_WEIGHT = (1 << 16), - - /*preserve face set boundaries*/ - BRUSH_SMOOTH_PRESERVE_FACE_SETS = (1 << 17), + BRUSH_CURVATURE_RAKE = (1 << 9), + BRUSH_CUSTOM_AUTOSMOOTH_SPACING = (1 << 10), + BRUSH_CUSTOM_TOPOLOGY_RAKE_SPACING = (1 << 11), + BRUSH_TOPOLOGY_RAKE_IGNORE_BRUSH_FALLOFF = (1 << 12), + BRUSH_SMOOTH_USE_AREA_WEIGHT = (1 << 13), /*topology rake in dynamic mode*/ - BRUSH_DYNAMIC_RAKE = (1 << 18), + BRUSH_DYNAMIC_RAKE = (1 << 14), - /* sets face set slide to 0.0 */ - BRUSH_HARD_EDGE_MODE = (1 << 19), + /* Forces face set boundaries to be treated as hard edges. */ + BRUSH_HARD_EDGE_MODE = (1 << 15), } eBrushFlags2; typedef enum { @@ -503,20 +481,6 @@ typedef enum eBrushSculptTool { SCULPT_TOOL_BOUNDARY = 30, SCULPT_TOOL_DISPLACEMENT_ERASER = 31, SCULPT_TOOL_DISPLACEMENT_SMEAR = 32, - SCULPT_TOOL_FAIRING = 33, - SCULPT_TOOL_SCENE_PROJECT = 34, - SCULPT_TOOL_SYMMETRIZE = 35, - SCULPT_TOOL_TWIST = 36, - SCULPT_TOOL_ARRAY = 37, - SCULPT_TOOL_VCOL_BOUNDARY = 38, - SCULPT_TOOL_UV_SMOOTH = 39, - - SCULPT_TOOL_TOPOLOGY_RAKE = 40, - SCULPT_TOOL_DYNTOPO = 41, - SCULPT_TOOL_AUTO_FSET = 42, - SCULPT_TOOL_RELAX = 43, - SCULPT_TOOL_ENHANCE_DETAILS = 44, - SCULPT_TOOL_DISPLACEMENT_HEAL = 45 } eBrushSculptTool; /** #Brush.uv_sculpt_tool */ @@ -569,12 +533,10 @@ typedef enum eBrushCurvesSculptTool { SCULPT_TOOL_GRAB, \ SCULPT_TOOL_CLOTH, \ SCULPT_TOOL_DISPLACEMENT_ERASER, \ - SCULPT_TOOL_FAIRING, \ SCULPT_TOOL_ELASTIC_DEFORM, \ SCULPT_TOOL_BOUNDARY, \ SCULPT_TOOL_POSE, \ SCULPT_TOOL_DRAW_FACE_SETS, \ - SCULPT_TOOL_UV_SMOOTH, \ \ /* These brushes could handle dynamic topology, \ \ * but user feedback indicates it's better not to */ \ diff --git a/source/blender/makesdna/DNA_space_types.h b/source/blender/makesdna/DNA_space_types.h index a319fd187e8..54edf795e6c 100644 --- a/source/blender/makesdna/DNA_space_types.h +++ b/source/blender/makesdna/DNA_space_types.h @@ -229,7 +229,6 @@ typedef enum eSpaceButtons_Context { BCONTEXT_SHADERFX = 15, BCONTEXT_OUTPUT = 16, BCONTEXT_COLLECTION = 17, - BCONTEXT_BRUSH_EDITOR = 18, /* Keep last. */ BCONTEXT_TOT, diff --git a/source/blender/makesrna/intern/rna_brush.c b/source/blender/makesrna/intern/rna_brush.c index 08c8971b3ae..b5d4227cb15 100644 --- a/source/blender/makesrna/intern/rna_brush.c +++ b/source/blender/makesrna/intern/rna_brush.c @@ -118,8 +118,7 @@ const EnumPropertyItem rna_enum_brush_sculpt_tool_items[] = { {SCULPT_TOOL_CREASE, "CREASE", ICON_BRUSH_CREASE, "Crease", ""}, RNA_ENUM_ITEM_SEPR, {SCULPT_TOOL_SMOOTH, "SMOOTH", ICON_BRUSH_SMOOTH, "Smooth", ""}, - {SCULPT_TOOL_ENHANCE_DETAILS, "ENHANCE_DETAILS", ICON_BRUSH_SCULPT_DRAW, "Enhance Details", ""}, - {SCULPT_TOOL_FLATTEN, "FLATTEN", ICON_BRUSH_FLATTEN, "Flatten", ""}, + {SCULPT_TOOL_FLATTEN, "FLATTEN", ICON_BRUSH_FLATTEN, "Flatten", ""}, {SCULPT_TOOL_FILL, "FILL", ICON_BRUSH_FILL, "Fill", ""}, {SCULPT_TOOL_SCRAPE, "SCRAPE", ICON_BRUSH_SCRAPE, "Scrape", ""}, {SCULPT_TOOL_MULTIPLANE_SCRAPE, "MULTIPLANE_SCRAPE", ICON_BRUSH_SCRAPE, "Multi-plane Scrape", ""}, @@ -134,7 +133,6 @@ const EnumPropertyItem rna_enum_brush_sculpt_tool_items[] = { {SCULPT_TOOL_ROTATE, "ROTATE", ICON_BRUSH_ROTATE, "Rotate", ""}, {SCULPT_TOOL_SLIDE_RELAX, "TOPOLOGY", ICON_BRUSH_GRAB, "Slide Relax", ""}, {SCULPT_TOOL_BOUNDARY, "BOUNDARY", ICON_BRUSH_GRAB, "Boundary", ""}, - {SCULPT_TOOL_TWIST, "TWIST", ICON_BRUSH_GRAB, "Twist", ""}, RNA_ENUM_ITEM_SEPR, {SCULPT_TOOL_CLOTH, "CLOTH", ICON_BRUSH_SCULPT_DRAW, "Cloth", ""}, {SCULPT_TOOL_SIMPLIFY, "SIMPLIFY", ICON_BRUSH_DATA, "Simplify", ""}, @@ -145,13 +143,6 @@ const EnumPropertyItem rna_enum_brush_sculpt_tool_items[] = { {SCULPT_TOOL_PAINT, "PAINT", ICON_BRUSH_SCULPT_DRAW, "Paint", ""}, {SCULPT_TOOL_SMEAR, "SMEAR", ICON_BRUSH_SCULPT_DRAW, "Smear", ""}, - {SCULPT_TOOL_FAIRING, "FAIRING", ICON_BRUSH_MASK, "Fairing", ""}, - {SCULPT_TOOL_SCENE_PROJECT, "SCENE_PROJECT", ICON_BRUSH_MASK, "Scene Project", ""}, - {SCULPT_TOOL_SYMMETRIZE, "SYMMETRIZE", ICON_BRUSH_SCULPT_DRAW, "Symmetrize", ""}, - {SCULPT_TOOL_ARRAY, "ARRAY", ICON_BRUSH_SCULPT_DRAW, "Array", ""}, - {SCULPT_TOOL_VCOL_BOUNDARY, "VCOL_BOUNDARY", ICON_BRUSH_SCULPT_DRAW, "Sharpen Color Boundary", ""}, - {SCULPT_TOOL_UV_SMOOTH, "UV_SMOOTH", ICON_BRUSH_GRAB, "UV Smooth", ""}, - {SCULPT_TOOL_DISPLACEMENT_HEAL, "DISPLACEMENT_HEAL", ICON_BRUSH_GRAB, "Displacement Heal", ""}, {0, NULL, 0, NULL, NULL}, }; /* clang-format on */ @@ -639,7 +630,6 @@ static bool rna_BrushCapabilitiesSculpt_has_direction_get(PointerRNA *ptr) SCULPT_TOOL_DRAW_SHARP, SCULPT_TOOL_CLAY, SCULPT_TOOL_CLAY_STRIPS, - SCULPT_TOOL_TWIST, SCULPT_TOOL_SMOOTH, SCULPT_TOOL_LAYER, SCULPT_TOOL_INFLATE, @@ -929,7 +919,6 @@ static const EnumPropertyItem *rna_Brush_direction_itemf(bContext *C, case SCULPT_TOOL_LAYER: case SCULPT_TOOL_CLAY: case SCULPT_TOOL_CLAY_STRIPS: - case SCULPT_TOOL_TWIST: return prop_direction_items; case SCULPT_TOOL_SMOOTH: return prop_smooth_direction_items; @@ -2514,7 +2503,6 @@ static void rna_def_brush(BlenderRNA *brna) {BRUSH_CLOTH_DEFORM_GRAB, "GRAB", 0, "Grab", ""}, {BRUSH_CLOTH_DEFORM_EXPAND, "EXPAND", 0, "Expand", ""}, {BRUSH_CLOTH_DEFORM_SNAKE_HOOK, "SNAKE_HOOK", 0, "Snake Hook", ""}, - {BRUSH_CLOTH_DEFORM_ELASTIC_DRAG, "ELASTIC", 0, "Elastic Drag", ""}, {0, NULL, 0, NULL, NULL}, }; @@ -2575,16 +2563,6 @@ static void rna_def_brush(BlenderRNA *brna) 0, "Surface", "Smooths the surface of the mesh, preserving the volume"}, - {BRUSH_SMOOTH_DEFORM_DIRECTIONAL, - "DIRECTIONAL", - 0, - "Directional", - "Smooths the surface taking into account the direction of the stroke"}, - {BRUSH_SMOOTH_DEFORM_UNIFORM_WEIGHTS, - "UNIFORM_WEIGHTS", - 0, - "Uniform Weights", - "Smooths the surface considering that all edges have the same length"}, {0, NULL, 0, NULL, NULL}, }; @@ -2596,13 +2574,6 @@ static void rna_def_brush(BlenderRNA *brna) {0, NULL, 0, NULL, NULL}, }; - static const EnumPropertyItem brush_array_deform_type_items[] = { - {BRUSH_ARRAY_DEFORM_LINEAR, "LINEAR", 0, "Linear", ""}, - {BRUSH_ARRAY_DEFORM_RADIAL, "RADIAL", 0, "Radial", ""}, - {BRUSH_ARRAY_DEFORM_PATH, "PATH", 0, "Path", ""}, - {0, NULL, 0, NULL, NULL}, - }; - static const EnumPropertyItem brush_pose_origin_type_items[] = { {BRUSH_POSE_ORIGIN_TOPOLOGY, "TOPOLOGY", @@ -2648,13 +2619,6 @@ static void rna_def_brush(BlenderRNA *brna) {0, NULL, 0, NULL, NULL}, }; - static const EnumPropertyItem brush_scene_project_direction_type_items[] = { - {BRUSH_SCENE_PROJECT_DIRECTION_VIEW, "VIEW", 0, "View", ""}, - {BRUSH_SCENE_PROJECT_DIRECTION_VERTEX_NORMAL, "VERTEX_NORMAL", 0, "Vertex Normal", ""}, - {BRUSH_SCENE_PROJECT_DIRECTION_BRUSH_NORMAL, "BRUSH_NORMAL", 0, "Brush Normal", ""}, - {0, NULL, 0, NULL, NULL}, - }; - srna = RNA_def_struct(brna, "Brush", "ID"); RNA_def_struct_ui_text( srna, "Brush", "Brush data-block for storing brush settings for painting and sculpting"); @@ -2839,26 +2803,6 @@ static void rna_def_brush(BlenderRNA *brna) "Method to set the rotation origins for the segments of the brush"); RNA_def_property_update(prop, 0, "rna_Brush_update"); - prop = RNA_def_property(srna, "scene_project_direction_type", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_items(prop, brush_scene_project_direction_type_items); - RNA_def_property_ui_text( - prop, - "Project Direction", - "Direction that is going to be used to project the vertices into the scene"); - RNA_def_property_update(prop, 0, "rna_Brush_update"); - - prop = RNA_def_property(srna, "array_deform_type", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_items(prop, brush_array_deform_type_items); - RNA_def_property_ui_text(prop, "Deformation", "Deformation type that is used in the brush"); - RNA_def_property_update(prop, 0, "rna_Brush_update"); - - prop = RNA_def_property(srna, "array_count", PROP_INT, PROP_UNSIGNED); - RNA_def_property_int_sdna(prop, NULL, "array_count"); - RNA_def_property_range(prop, 1, 10000); - RNA_def_property_ui_range(prop, 1, 50, 1, -1); - RNA_def_property_ui_text(prop, "Count", "Number of copies"); - RNA_def_property_update(prop, 0, "rna_Brush_update"); - prop = RNA_def_property(srna, "jitter_unit", PROP_ENUM, PROP_NONE); /* as an enum */ RNA_def_property_enum_bitflag_sdna(prop, NULL, "flag"); RNA_def_property_enum_items(prop, brush_jitter_unit_items); @@ -3628,12 +3572,6 @@ static void rna_def_brush(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Weight By Area", "Weight by face area to get a smoother result"); RNA_def_property_update(prop, 0, "rna_Brush_update"); - prop = RNA_def_property(srna, "preserve_faceset_boundary", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag2", BRUSH_SMOOTH_PRESERVE_FACE_SETS); - RNA_def_property_ui_text( - prop, "Preserve Face Sets", "Preserve face set boundaries when smoothing"); - RNA_def_property_update(prop, 0, "rna_Brush_update"); - prop = RNA_def_property(srna, "hard_edge_mode", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag2", BRUSH_HARD_EDGE_MODE); RNA_def_property_ui_text( @@ -3646,22 +3584,6 @@ static void rna_def_brush(BlenderRNA *brna) prop, "Grab Silhouette", "Grabs trying to automask the silhouette of the object"); RNA_def_property_update(prop, 0, "rna_Brush_update"); - prop = RNA_def_property(srna, "use_surface_falloff", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag2", BRUSH_USE_SURFACE_FALLOFF); - RNA_def_property_ui_text(prop, - "Use Surface Falloff", - "Propagate the falloff of the brush trough the surface of the mesh"); - RNA_def_property_update(prop, 0, "rna_Brush_update"); - - prop = RNA_def_property(srna, "use_array_lock_orientation", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag2", BRUSH_ARRAY_LOCK_ORIENTATION); - RNA_def_property_ui_text(prop, "Lock Orientation", ""); - RNA_def_property_update(prop, 0, "rna_Brush_update"); - - prop = RNA_def_property(srna, "use_array_fill_holes", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag2", BRUSH_ARRAY_FILL_HOLES); - RNA_def_property_ui_text(prop, "Fill Holes", ""); - prop = RNA_def_property(srna, "use_curvature_rake", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag2", BRUSH_CURVATURE_RAKE); RNA_def_property_ui_text( diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c index 0a4503c3e94..21cd9497042 100644 --- a/source/blender/makesrna/intern/rna_space.c +++ b/source/blender/makesrna/intern/rna_space.c @@ -480,7 +480,6 @@ const EnumPropertyItem rna_enum_clip_editor_mode_items[] = { * but helps for context-less access (e.g. doc, i18n...). */ static const EnumPropertyItem buttons_context_items[] = { {BCONTEXT_TOOL, "TOOL", ICON_TOOL_SETTINGS, "Tool", "Active Tool and Workspace settings"}, - {BCONTEXT_BRUSH_EDITOR, "BRUSH_EDITOR", ICON_BRUSH_DATA, "Brush", "Brush Settings"}, {BCONTEXT_SCENE, "SCENE", ICON_SCENE_DATA, "Scene", "Scene Properties"}, {BCONTEXT_RENDER, "RENDER", ICON_SCENE, "Render", "Render Properties"}, {BCONTEXT_OUTPUT, "OUTPUT", ICON_OUTPUT, "Output", "Output Properties"}, -- 2.30.2 From 5b7845cecc70a185a839cfc54ceb14bd54b066f7 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Sat, 11 Feb 2023 11:45:10 -0800 Subject: [PATCH 004/279] temp-sculpt-dyntopo: Turn on new unique element id api --- source/blender/bmesh/intern/bmesh_idmap.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/bmesh/intern/bmesh_idmap.h b/source/blender/bmesh/intern/bmesh_idmap.h index 1f79983f9db..d9543f19005 100644 --- a/source/blender/bmesh/intern/bmesh_idmap.h +++ b/source/blender/bmesh/intern/bmesh_idmap.h @@ -8,7 +8,7 @@ # define WITH_BM_ID_FREELIST #endif -//#define USE_NEW_IDMAP +#define USE_NEW_IDMAP #ifdef __cplusplus # include "BLI_map.hh" -- 2.30.2 From 03878a08e86ba5c21ec8ee48a9af0ebfb04ff4c9 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Sat, 11 Feb 2023 11:47:25 -0800 Subject: [PATCH 005/279] temp-sculpt-dyntopo: Remove lockfree mempool --- source/blender/blenlib/BLI_mempool_lockfree.h | 42 -- source/blender/blenlib/CMakeLists.txt | 2 - .../blenlib/intern/lockfree_mempool.cc | 374 ------------------ 3 files changed, 418 deletions(-) delete mode 100644 source/blender/blenlib/BLI_mempool_lockfree.h delete mode 100644 source/blender/blenlib/intern/lockfree_mempool.cc diff --git a/source/blender/blenlib/BLI_mempool_lockfree.h b/source/blender/blenlib/BLI_mempool_lockfree.h deleted file mode 100644 index 4d779a3b8fc..00000000000 --- a/source/blender/blenlib/BLI_mempool_lockfree.h +++ /dev/null @@ -1,42 +0,0 @@ -#pragma once - -#include "BLI_task.h" - -typedef struct BLI_lfmempool { - void *unused; -} BLI_lfmempool; - -typedef struct BLI_lfmempool_iter { - void *chunk; - BLI_lfmempool *pool; - int i; - void **curchunk_threaded_shared; -} BLI_lfmempool_iter; - -#ifdef __cplusplus -extern "C" { -#endif - -BLI_lfmempool *BLI_lfmempool_create(int esize, int psize); -void BLI_lfmempool_destroy(BLI_lfmempool *pool); -void *BLI_lfmempool_alloc(BLI_lfmempool *pool); -void *BLI_lfmempool_calloc(BLI_lfmempool *pool); -void BLI_lfmempool_free(BLI_lfmempool *pool, void *mem); -void BLI_lfmempool_iternew(BLI_lfmempool *_pool, BLI_lfmempool_iter *iter); -void *BLI_lfmempool_iterstep(BLI_lfmempool_iter *iter); -// int BLI_lfmempool_len(BLI_lfmempool *pool); -void *BLI_lfmempool_iterstep_threaded(BLI_lfmempool *iter); -void *BLI_lfmempool_findelem(BLI_lfmempool *pool, int index); - -typedef struct ParallelLFMempoolTaskData { - BLI_lfmempool_iter ts_iter; - TaskParallelTLS tls; -} ParallelLFMempoolTaskData; - -ParallelLFMempoolTaskData *lfmempool_iter_threadsafe_create(BLI_lfmempool *pool, - const size_t num_iter); -void lfmempool_iter_threadsafe_destroy(ParallelLFMempoolTaskData *iter_arr); - -#ifdef __cplusplus -} -#endif //__cplusplus diff --git a/source/blender/blenlib/CMakeLists.txt b/source/blender/blenlib/CMakeLists.txt index 7aaf3e72d6d..81db0b16c05 100644 --- a/source/blender/blenlib/CMakeLists.txt +++ b/source/blender/blenlib/CMakeLists.txt @@ -89,7 +89,6 @@ set(SRC intern/kdtree_3d.c intern/kdtree_4d.c intern/lasso_2d.c - intern/lockfree_mempool.cc intern/lazy_threading.cc intern/length_parameterize.cc intern/listbase.cc @@ -296,7 +295,6 @@ set(SRC BLI_memory_utils.h BLI_memory_utils.hh BLI_mempool.h - BLI_mempool_lockfree.h BLI_mesh_boolean.hh BLI_mesh_intersect.hh BLI_mmap.h diff --git a/source/blender/blenlib/intern/lockfree_mempool.cc b/source/blender/blenlib/intern/lockfree_mempool.cc deleted file mode 100644 index 055de7b6a6f..00000000000 --- a/source/blender/blenlib/intern/lockfree_mempool.cc +++ /dev/null @@ -1,374 +0,0 @@ -#include -#include -#include - -#include "BLI_compiler_attrs.h" -#include "BLI_compiler_compat.h" - -#include "atomic_ops.h" //for one atomic variable exposed to C, curchunk_threaded_shared in BLI_lfmempool_iter -#include -#include - -#include "MEM_guardedalloc.h" - -#include "BLI_mempool_lockfree.h" - -/* NOTE: copied from BLO_blend_defs.h, don't use here because we're in BLI. */ -#ifdef __BIG_ENDIAN__ -/* Big Endian */ -# define MAKE_ID(a, b, c, d) ((int)(a) << 24 | (int)(b) << 16 | (c) << 8 | (d)) -# define MAKE_ID_8(a, b, c, d, e, f, g, h) \ - ((int64_t)(a) << 56 | (int64_t)(b) << 48 | (int64_t)(c) << 40 | (int64_t)(d) << 32 | \ - (int64_t)(e) << 24 | (int64_t)(f) << 16 | (int64_t)(g) << 8 | (h)) -#else -/* Little Endian */ -# define MAKE_ID(a, b, c, d) ((int)(d) << 24 | (int)(c) << 16 | (b) << 8 | (a)) -# define MAKE_ID_8(a, b, c, d, e, f, g, h) \ - ((int64_t)(h) << 56 | (int64_t)(g) << 48 | (int64_t)(f) << 40 | (int64_t)(e) << 32 | \ - (int64_t)(d) << 24 | (int64_t)(c) << 16 | (int64_t)(b) << 8 | (a)) -#endif - -/** - * Important that this value is an is _not_ aligned with `sizeof(void *)`. - * So having a pointer to 2/4/8... aligned memory is enough to ensure - * the `freeword` will never be used. - * To be safe, use a word that's the same in both directions. - */ -#define FREEWORD \ - ((sizeof(void *) > sizeof(int32_t)) ? MAKE_ID_8('e', 'e', 'r', 'f', 'f', 'r', 'e', 'e') : \ - MAKE_ID('e', 'f', 'f', 'e')) - -/** - * The 'used' word just needs to be set to something besides FREEWORD. - */ -#define USEDWORD MAKE_ID('u', 's', 'e', 'd') - -namespace blender { - -struct LockFreeElem { - struct LockFreeElem *next; - uintptr_t freeword; -}; - -struct LockFreeChunk { - struct LockFreeChunk *next, *prev; - - // we are convienently aligned to 16 bytes here -}; - -static void *data_from_chunk(LockFreeChunk *chunk) -{ - return reinterpret_cast(chunk + 1); -} - -static LockFreeElem *elem_from_chunk(LockFreeChunk *chunk) -{ - return reinterpret_cast(data_from_chunk(chunk)); -} - -static LockFreeElem *elem_next(LockFreeElem *elem, int esize) -{ - char *ptr = reinterpret_cast(elem); - ptr += esize; - return reinterpret_cast(ptr); -} - -static_assert(sizeof(std::atomic) == sizeof(void *), "std:atomic has space overhead!"); - -struct LockFreePool { - struct { - std::atomic first; - std::atomic last; - } chunks; - - std::atomic totchunk; - std::atomic totused; - - std::atomic free_elem; - - int esize, psize, csize; - - LockFreePool(int _esize, int psize) : psize(psize) - { - esize = std::max(_esize, (int)(sizeof(void *) * 2)); - - if (esize & 8) { - esize += 8 - (esize & 7); - } - - csize = esize * psize + sizeof(LockFreeChunk); - totused.store(0); - - free_elem.store(nullptr); - chunks.first.store(nullptr); - chunks.last.store(nullptr); - } - - ~LockFreePool() - { - LockFreeChunk *chunk, *next; - - for (chunk = chunks.last; chunk; chunk = next) { - next = chunk->prev; - - MEM_freeN(reinterpret_cast(chunk)); - } - } - - void add_chunk() - { - LockFreeChunk *chunk = reinterpret_cast(MEM_mallocN(csize, "LockFreeChunk")); - LockFreeElem *elem = elem_from_chunk(chunk), *last; - LockFreeElem *first = elem; - - chunk->next = chunk->prev = nullptr; - - for (int i = 0; i < psize; i++, elem = elem_next(elem, esize)) { - elem->freeword = FREEWORD; - - if (i == psize - 1) { - last = elem; - elem->next = nullptr; - } - else { - elem->next = elem_next(elem, esize); - } - } - - // last->next = free_elem - // free_elem = last; - - while (1) { - last->next = free_elem.load(); - - if (free_elem.compare_exchange_strong(last->next, first)) { - break; - } - } - - while (1) { - chunk->prev = chunks.last.load(); - - if (chunks.last.compare_exchange_strong(chunk->prev, chunk)) { - if (!chunk->prev) { - // chunks.first is not accessed in threading cases, only when pool - // is destroyed - chunks.first.store(chunk); - } - else { - chunk->prev->next = chunk; - } - break; - } - } - } - - void *alloc() - { - totused++; - - while (1) { - if (!free_elem.load()) { - add_chunk(); - } - - LockFreeElem *cur = free_elem.load(); - - if (free_elem.compare_exchange_strong(cur, cur->next)) { - cur->freeword = 0; - return reinterpret_cast(cur); - } - } - } - - void free(void *mem) - { - totused--; - LockFreeElem *elem = reinterpret_cast(mem); - - elem->freeword = FREEWORD; - - while (!free_elem.compare_exchange_strong(elem->next, elem)) { - } - } -}; - -static LockFreePool *cast_pool(BLI_lfmempool *pool) -{ - return reinterpret_cast(pool); -} - -extern "C" { - -BLI_lfmempool *BLI_lfmempool_create(int esize, int psize) -{ - LockFreePool *pool = MEM_new(__func__, esize, psize); - return reinterpret_cast(pool); -} - -void BLI_lfmempool_destroy(BLI_lfmempool *pool) -{ - MEM_delete(cast_pool(pool)); -} - -void *BLI_lfmempool_alloc(BLI_lfmempool *pool) -{ - return cast_pool(pool)->alloc(); -} - -void *BLI_lfmempool_calloc(BLI_lfmempool *_pool) -{ - void *mem = BLI_lfmempool_alloc(_pool); - LockFreePool *pool = cast_pool(_pool); - - memset(mem, 0, pool->esize); - - return mem; -} - -void BLI_lfmempool_free(BLI_lfmempool *pool, void *mem) -{ - return cast_pool(pool)->free(mem); -} - -void BLI_lfmempool_iternew(BLI_lfmempool *_pool, BLI_lfmempool_iter *iter) -{ - LockFreePool *pool = cast_pool(_pool); - iter->pool = _pool; - iter->chunk = reinterpret_cast(pool->chunks.first.load()); - iter->i = 0; - iter->curchunk_threaded_shared = nullptr; -} - -static void *chunk_next(void *vchunk) -{ - LockFreeChunk *chunk = reinterpret_cast(vchunk); - - return reinterpret_cast(chunk->next); -} - -void *BLI_lfmempool_iterstep_threadsafe(BLI_lfmempool_iter *iter) -{ - if (!iter->chunk) { - return nullptr; - } - - LockFreePool *pool = cast_pool(iter->pool); - LockFreeChunk *chunk = reinterpret_cast(iter->chunk); - - char *data = reinterpret_cast(data_from_chunk(chunk)); - void *ret = reinterpret_cast(data + pool->esize * iter->i); - - iter->i++; - - if (iter->i >= pool->psize) { - iter->i = 0; - /* Begin unique to the `threadsafe` version of this function. */ - for (iter->chunk = *iter->curchunk_threaded_shared; - (iter->chunk != NULL) && (atomic_cas_ptr((void **)iter->curchunk_threaded_shared, - iter->chunk, - chunk_next(iter->chunk)) != iter->chunk); - iter->chunk = *iter->curchunk_threaded_shared) { - /* pass. */ - } - iter->chunk = reinterpret_cast(chunk->next); - } - - LockFreeElem *elem = reinterpret_cast(ret); - if (elem->freeword == FREEWORD) { - return BLI_lfmempool_iterstep_threadsafe(iter); - } - - return ret; -} - -void *BLI_lfmempool_iterstep(BLI_lfmempool_iter *iter) -{ - if (!iter->chunk) { - return nullptr; - } - - LockFreePool *pool = cast_pool(iter->pool); - LockFreeChunk *chunk = reinterpret_cast(iter->chunk); - - char *data = reinterpret_cast(data_from_chunk(chunk)); - void *ret = reinterpret_cast(data + pool->esize * iter->i); - - iter->i++; - - if (iter->i >= pool->psize) { - iter->i = 0; - iter->chunk = reinterpret_cast(chunk->next); - } - - LockFreeElem *elem = reinterpret_cast(ret); - if (elem->freeword == FREEWORD) { - return BLI_lfmempool_iterstep(iter); - } - - return ret; -} - -void *BLI_lfmempool_findelem(BLI_lfmempool *pool, int index) -{ - BLI_lfmempool_iter iter; - BLI_lfmempool_iternew(pool, &iter); - void *item = BLI_lfmempool_iterstep(&iter); - - int i = 0; - - for (; item; item = BLI_lfmempool_iterstep(&iter), i++) { - if (i == index) { - return item; - } - } - - return nullptr; -} - -/** - * Initialize an array of mempool iterators, #BLI_MEMPOOL_ALLOW_ITER flag must be set. - * - * This is used in threaded code, to generate as much iterators as needed - * (each task should have its own), - * such that each iterator goes over its own single chunk, - * and only getting the next chunk to iterate over has to be - * protected against concurrency (which can be done in a lockless way). - * - * To be used when creating a task for each single item in the pool is totally overkill. - * - * See BLI_task_parallel_mempool implementation for detailed usage example. - */ -ParallelLFMempoolTaskData *lfmempool_iter_threadsafe_create(BLI_lfmempool *pool, - const size_t num_iter) -{ - BLI_assert(pool->flag & BLI_MEMPOOL_ALLOW_ITER); - - ParallelLFMempoolTaskData *iter_arr = (ParallelLFMempoolTaskData *)MEM_mallocN( - sizeof(*iter_arr) * num_iter, __func__); - LockFreeChunk **curchunk_threaded_shared = (LockFreeChunk **)MEM_mallocN(sizeof(void *), - __func__); - - BLI_lfmempool_iternew(pool, &iter_arr->ts_iter); - - *curchunk_threaded_shared = reinterpret_cast(iter_arr->ts_iter.chunk); - iter_arr->ts_iter.curchunk_threaded_shared = reinterpret_cast(curchunk_threaded_shared); - for (size_t i = 1; i < num_iter; i++) { - iter_arr[i].ts_iter = iter_arr[0].ts_iter; - *curchunk_threaded_shared = ((*curchunk_threaded_shared) ? (*curchunk_threaded_shared)->next : - NULL); - iter_arr[i].ts_iter.chunk = reinterpret_cast(*curchunk_threaded_shared); - } - - return iter_arr; -} - -void lfmempool_iter_threadsafe_destroy(ParallelLFMempoolTaskData *iter_arr) -{ - BLI_assert(iter_arr->ts_iter.curchunk_threaded_shared != NULL); - - MEM_freeN(iter_arr->ts_iter.curchunk_threaded_shared); - MEM_freeN(iter_arr); -} -} -} // namespace blender -- 2.30.2 From f319e95198a877736ad677a4509a66e4e30c7430 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Sat, 11 Feb 2023 12:56:13 -0800 Subject: [PATCH 006/279] temp-sculpt-dyntopo: Remove multires stuff --- source/blender/blenkernel/intern/mesh.cc | 4 +- .../blender/blenkernel/intern/mesh_mirror.cc | 2 +- .../blenkernel/intern/mesh_remesh_voxel.cc | 2 +- source/blender/blenkernel/intern/multires.cc | 461 +------------- .../blenkernel/intern/multires_reshape.cc | 6 - .../intern/multires_reshape_apply_base.cc | 2 +- .../intern/multires_reshape_smooth.cc | 32 - .../intern/multires_reshape_util.cc | 2 - .../blenkernel/intern/multires_unsubdivide.cc | 5 +- source/blender/blenkernel/intern/paint.cc | 4 +- source/blender/blenkernel/intern/pbvh.cc | 1 + .../blender/blenkernel/intern/pbvh_bmesh.cc | 6 +- source/blender/blenkernel/intern/scene.cc | 2 +- source/blender/bmesh/bmesh_class.h | 5 - source/blender/bmesh/intern/bmesh_construct.c | 31 +- source/blender/bmesh/intern/bmesh_core.h | 6 + source/blender/bmesh/intern/bmesh_interp.c | 576 ++---------------- source/blender/bmesh/intern/bmesh_interp.h | 18 +- source/blender/bmesh/intern/bmesh_log.cc | 5 +- source/blender/bmesh/intern/bmesh_mesh.cc | 76 +-- source/blender/bmesh/intern/bmesh_mesh.h | 4 +- .../bmesh/intern/bmesh_mesh_convert.cc | 556 ++--------------- .../blender/bmesh/intern/bmesh_mesh_convert.h | 7 +- source/blender/bmesh/intern/bmesh_opdefines.c | 16 - .../bmesh/operators/bmo_mesh_convert.c | 4 +- .../editors/mesh/editmesh_mask_extract.cc | 6 +- source/blender/editors/mesh/editmesh_tools.c | 122 +--- source/blender/editors/mesh/editmesh_undo.cc | 4 +- source/blender/editors/mesh/editmesh_utils.c | 1 - source/blender/editors/mesh/mesh_ops.c | 2 - .../editors/sculpt_paint/paint_mask.cc | 8 +- .../editors/sculpt_paint/sculpt_boundary.cc | 2 +- .../editors/sculpt_paint/sculpt_dyntopo.cc | 23 +- .../editors/sculpt_paint/sculpt_face_set.cc | 48 +- .../editors/sculpt_paint/sculpt_intern.hh | 3 - .../editors/sculpt_paint/sculpt_replay.c | 4 +- .../editors/sculpt_paint/sculpt_undo.cc | 6 +- .../editors/uvedit/uvedit_unwrap_ops.cc | 4 +- source/blender/io/collada/collada_utils.cpp | 4 +- .../blender/modifiers/intern/MOD_boolean.cc | 6 +- .../node_geo_mesh_primitive_ico_sphere.cc | 2 +- source/blender/python/bmesh/bmesh_py_types.c | 8 +- 42 files changed, 237 insertions(+), 1849 deletions(-) diff --git a/source/blender/blenkernel/intern/mesh.cc b/source/blender/blenkernel/intern/mesh.cc index b9c7d0cdcd4..f709328a01d 100644 --- a/source/blender/blenkernel/intern/mesh.cc +++ b/source/blender/blenkernel/intern/mesh.cc @@ -1144,7 +1144,7 @@ BMesh *BKE_mesh_to_bmesh_ex(const Object *ob, const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(me); BMesh *bm = BM_mesh_create(&allocsize, create_params); - BM_mesh_bm_from_me((Object *)ob, bm, me, convert_params); + BM_mesh_bm_from_me(bm, me, convert_params); return bm; } @@ -1170,7 +1170,7 @@ Mesh *BKE_mesh_from_bmesh_nomain(BMesh *bm, BLI_assert(params->calc_object_remap == false); Mesh *mesh = (Mesh *)BKE_id_new_nomain(ID_ME, nullptr); - BM_mesh_bm_to_me(nullptr, nullptr, bm, mesh, params); + BM_mesh_bm_to_me(nullptr, bm, mesh, params); if (me_settings) { BKE_mesh_copy_parameters_for_eval(mesh, me_settings); diff --git a/source/blender/blenkernel/intern/mesh_mirror.cc b/source/blender/blenkernel/intern/mesh_mirror.cc index 8e99d814801..101c2e62306 100644 --- a/source/blender/blenkernel/intern/mesh_mirror.cc +++ b/source/blender/blenkernel/intern/mesh_mirror.cc @@ -109,7 +109,7 @@ void BKE_mesh_mirror_apply_mirror_on_axis(struct Main *bmain, BMeshToMeshParams bmesh_to_mesh_params{}; bmesh_to_mesh_params.calc_object_remap = true; - BM_mesh_bm_to_me(bmain, nullptr, bm, mesh, &bmesh_to_mesh_params); + BM_mesh_bm_to_me(bmain, bm, mesh, &bmesh_to_mesh_params); BM_mesh_free(bm); } diff --git a/source/blender/blenkernel/intern/mesh_remesh_voxel.cc b/source/blender/blenkernel/intern/mesh_remesh_voxel.cc index 7f571a98652..a4932421042 100644 --- a/source/blender/blenkernel/intern/mesh_remesh_voxel.cc +++ b/source/blender/blenkernel/intern/mesh_remesh_voxel.cc @@ -517,7 +517,7 @@ struct Mesh *BKE_mesh_remesh_voxel_fix_poles(const Mesh *mesh) BMeshFromMeshParams bmesh_from_mesh_params{}; bmesh_from_mesh_params.calc_face_normal = true; bmesh_from_mesh_params.calc_vert_normal = true; - BM_mesh_bm_from_me(nullptr, bm, mesh, &bmesh_from_mesh_params); + BM_mesh_bm_from_me(bm, mesh, &bmesh_from_mesh_params); BMVert *v; BMEdge *ed, *ed_next; diff --git a/source/blender/blenkernel/intern/multires.cc b/source/blender/blenkernel/intern/multires.cc index 565d9d2fe5a..8ee83506bd9 100644 --- a/source/blender/blenkernel/intern/multires.cc +++ b/source/blender/blenkernel/intern/multires.cc @@ -17,31 +17,22 @@ #include "BLI_bitmap.h" #include "BLI_blenlib.h" -#include "BLI_edgehash.h" #include "BLI_math.h" #include "BLI_task.h" #include "BLI_utildefines.h" #include "BKE_ccg.h" #include "BKE_cdderivedmesh.h" -#include "BKE_collection.h" -#include "BKE_context.h" #include "BKE_editmesh.h" -#include "BKE_global.h" -#include "BKE_layer.h" -#include "BKE_main.h" #include "BKE_mesh.h" #include "BKE_mesh_mapping.h" #include "BKE_mesh_runtime.h" #include "BKE_modifier.h" #include "BKE_multires.h" -#include "BKE_object.h" #include "BKE_paint.h" #include "BKE_pbvh.h" #include "BKE_scene.h" -#include "BKE_subdiv.h" #include "BKE_subdiv_ccg.h" -#include "BKE_subdiv_eval.h" #include "BKE_subsurf.h" #include "BKE_object.h" @@ -50,8 +41,6 @@ #include "DEG_depsgraph_query.h" -#include "bmesh.h" -#include "multires_inline.h" #include "multires_reshape.hh" #include @@ -863,461 +852,17 @@ static void grid_tangent_matrix(float mat[3][3], const CCGKey *key, int x, int y struct MultiresThreadedData { DispOp op; - MultiResSpace bmop; - BMesh *bm; - int lvl; CCGElem **gridData, **subGridData; CCGKey *key; CCGKey *sub_key; - Subdiv *sd; const MPoly *mpoly; MDisps *mdisps; GridPaintMask *grid_paint_mask; int *gridOffset; - int cd_mdisps_off, cd_mask_off; int gridSize, dGridSize, dSkip; float (*smat)[3]; - bool has_grid_mask; }; -extern "C" Object *multires_dump_grids_bmesh(Object *bmob, BMesh *bm) -{ - if (!CustomData_has_layer(&bm->ldata, CD_MDISPS)) { - printf("multires_dump_grids_bmesh: error: no multires grids\n"); - return nullptr; - } - - bool spaceset = false; - - if (bm->multiresSpace != MULTIRES_SPACE_ABSOLUTE) { - BKE_multires_bmesh_space_set(bmob, bm, MULTIRES_SPACE_ABSOLUTE); - spaceset = true; - } - - Main *bmain = G.main; - const char *name = "multires_dump"; - - bContext *ctx = CTX_create(); - CTX_data_main_set(ctx, bmain); - CTX_wm_manager_set(ctx, (wmWindowManager *)G.main->wm.first); - CTX_data_scene_set(ctx, (Scene *)G.main->scenes.first); - - ViewLayer *view_layer = CTX_data_view_layer(ctx); - Object *ob = BKE_object_add_only_object(bmain, OB_MESH, name); - LayerCollection *layer_collection; - - ob->data = BKE_object_obdata_add_from_type(bmain, OB_MESH, name); - DEG_id_tag_update_ex( - bmain, &ob->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_ANIMATION); - // DEG_id_tag_update_ex( - // bmain, &ob->data, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_ANIMATION); - - layer_collection = BKE_layer_collection_get_active(view_layer); - BKE_collection_object_add(bmain, layer_collection->collection, ob); - - DEG_id_type_tag(bmain, ID_OB); - DEG_relations_tag_update(bmain); - if (ob->data != nullptr) { - DEG_id_tag_update_ex(bmain, (ID *)ob->data, ID_RECALC_EDITORS); - } - - ob->modifiers.first = ob->modifiers.last = nullptr; - zero_v3(ob->loc); - - printf("users: %d\n", ob->id.us); - - Mesh *me = (Mesh *)ob->data; - - BMIter iter; - BMFace *f; - - int cd_mdisp_off = CustomData_get_offset(&bm->ldata, CD_MDISPS); - int dimen = 0; - - BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { - BMLoop *l = f->l_first; - MDisps *md = (MDisps *)BM_ELEM_CD_GET_VOID_P(l, cd_mdisp_off); - dimen = (int)floor(sqrt(md->totdisp) + 0.00001); - break; - } - - if (!dimen) { - printf("multires_dump_grids_bmesh: error: corrupted multires data\n"); - return nullptr; - } - - int totvert = bm->totloop * dimen * dimen; - int totface = bm->totloop * (dimen - 1) * (dimen - 1); - int totloop = totface * 4; - - 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 = totvert; - me->totpoly = totface; - me->totloop = totloop; - me->totedge = totvert + totface; - me->totface = 0; - me->act_face = -1; - - EdgeHash *eh = BLI_edgehash_new_ex("multires_dump_bmesh", me->totedge); - - MVert *mvert = me->totvert ? (MVert *)MEM_callocN(sizeof(MVert) * me->totvert, - "multires_dump_grids_bmesh.vert") : - nullptr; - MEdge *medge = me->totedge ? (MEdge *)MEM_callocN(sizeof(MEdge) * me->totedge, - "multires_dump_grids_bmesh.edge") : - nullptr; - MLoop *mloop = me->totloop ? (MLoop *)MEM_callocN(sizeof(MLoop) * me->totloop, - "multires_dump_grids_bmesh.loop") : - nullptr; - MPoly *mpoly = me->totpoly ? (MPoly *)MEM_callocN(sizeof(MPoly) * me->totpoly, - "multires_dump_grids_bmesh.poly") : - nullptr; - - me->cd_flag = 0; - - me->mvert = (MVert *)CustomData_add_layer(&me->vdata, CD_MVERT, CD_ASSIGN, mvert, me->totvert); - me->medge = (MEdge *)CustomData_add_layer(&me->edata, CD_MEDGE, CD_ASSIGN, medge, me->totedge); - me->mloop = (MLoop *)CustomData_add_layer(&me->ldata, CD_MLOOP, CD_ASSIGN, mloop, me->totloop); - me->mpoly = (MPoly *)CustomData_add_layer(&me->pdata, CD_MPOLY, CD_ASSIGN, mpoly, me->totpoly); - - int loopi = 0; - int outli = 0; - int medi = 0; - -#define VINDEX(i, j) (loopi * dimen * dimen + ((j)*dimen + (i))) - - float(*vert_positions)[3] = BKE_mesh_vert_positions_for_write(me); - - // CustomData_daata_ - BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { - BMLoop *l = f->l_first; - do { - MDisps *md = (MDisps *)BM_ELEM_CD_GET_VOID_P(l, cd_mdisp_off); - - for (int i = 0; i < dimen; i++) { - for (int j = 0; j < dimen; j++) { - int vidx = loopi * dimen * dimen + (j * dimen + i); - int idx = j * dimen + i; - float *co = md->disps[idx]; - - copy_v3_v3(vert_positions[vidx], co); - } - } - - for (int i = 0; i < dimen - 1; i++) { - for (int j = 0; j < dimen - 1; j++) { - // do face - int fidx = loopi * (dimen - 1) * (dimen - 1) + (j * (dimen - 1) + i); - MPoly *mp = mpoly + fidx; - - mp->totloop = 4; - mp->loopstart = outli; - - MLoop *ml = mloop + outli; - - ml[0].v = VINDEX(i, j); - ml[1].v = VINDEX(i, j + 1); - ml[2].v = VINDEX(i + 1, j + 1); - ml[3].v = VINDEX(i + 1, j); - - for (int i2 = 0; i2 < 4; i2++) { - int a = ml[i2].v, b = ml[(i2 + 1) % 4].v; - int e; - - if (!BLI_edgehash_haskey(eh, a, b)) { - BLI_edgehash_insert(eh, a, b, POINTER_FROM_INT(medi)); - e = medi; - - MEdge *med = medge + medi; - - med->v1 = a; - med->v2 = b; - - medi++; - } - else { - e = POINTER_AS_INT(BLI_edgehash_lookup(eh, a, b)); - } - - ml[i2].e = e; - } - - outli += 4; - } - } - - loopi++; - l = l->next; - } while (l != f->l_first); - } - - for (int i = 0; i < me->totpoly; i++) { - if (!mpoly[i].totloop) { - printf("error 1! %d %d\n", i, me->totpoly); - } - if (mpoly[i].loopstart >= me->totloop) { - printf( - "error 2! %d %d l: %d totl: %d\n", i, me->totpoly, mpoly[i].loopstart, mpoly[i].totloop); - } - } - - if (spaceset) { - BKE_multires_bmesh_space_set(bmob, bm, MULTIRES_SPACE_TANGENT); - } - - BKE_mesh_calc_normals(me); - - return ob; -} - -//#define LIMIT_MAX_DISPLACEMENT - -static void multires_bmesh_space_set_cb(void *__restrict userdata, - const int pidx, - const TaskParallelTLS *__restrict) -{ - MultiresThreadedData *tdata = (MultiresThreadedData *)userdata; - - int cd_mdisps_off = tdata->cd_mdisps_off; - BMesh *bm = tdata->bm; - MultiResSpace op = tdata->bmop; - BMFace *f = bm->ftable[pidx]; - int gridSize = tdata->gridSize; - - int S, x, y; - - BMLoop *l; - -#ifdef LIMIT_MAX_DISPLACEMENT - l = f->l_first; - float cent[3]; - int tot = 0; - - // get face center to calculate maximum allowable displacement length - zero_v3(cent); - do { - add_v3_v3(cent, l->v->co); - tot++; - l = l->next; - } while (l != f->l_first); - - mul_v3_fl(cent, 1.0f / (float)tot); -#endif - - l = f->l_first; - S = 0; - do { - MDisps *mdisp = (MDisps *)BM_ELEM_CD_GET_VOID_P(l, cd_mdisps_off); - float(*dispgrid)[3] = nullptr; - - dispgrid = mdisp->disps; - -#ifdef LIMIT_MAX_DISPLACEMENT - /*try to limit numerical instability by clamping max displacement*/ - - float maxlen = len_v3v3(l->v->co, cent) * 15.0f; - maxlen = MAX2(maxlen, 0.00001f); -#endif - - for (y = 0; y < gridSize; y++) { - for (x = 0; x < gridSize; x++) { - float sco[8], udv[3], vdv[3]; - float *data = dispgrid[gridSize * y + x]; - float mat[3][3], disp[3]; - - float grid_u = (float)x / (float)(gridSize - 1); - float grid_v = (float)y / (float)(gridSize - 1); - float u, v; - - int corner = S; - if (f->len == 4) { - BKE_subdiv_rotate_grid_to_quad(corner, grid_u, grid_v, &u, &v); - } - else { - u = 1.0 - grid_v; - v = 1.0 - grid_u; - } - - BKE_subdiv_eval_limit_point_and_derivatives(tdata->sd, l->head.index, u, v, sco, udv, vdv); - BKE_multires_construct_tangent_matrix(mat, udv, vdv, f->len == 4 ? corner : 0); - - copy_v3_v3(disp, data); - - switch (op) { - case MULTIRES_SPACE_ABSOLUTE: - /* Convert displacement to object space - * and add to grid points */ - mul_v3_m3v3(disp, mat, data); - add_v3_v3v3(data, disp, sco); - break; - case MULTIRES_SPACE_TANGENT: - /* Calculate displacement between new and old - * grid points and convert to tangent space */ - invert_m3(mat); - - sub_v3_v3v3(disp, data, sco); - mul_v3_m3v3(data, mat, disp); - - // try to prevent errors - float len = len_v3(data); -#ifdef LIMIT_MAX_DISPLACEMENT - if (len > maxlen) { - mul_v3_fl(data, maxlen / len); - } - else if (isnan(len)) { - zero_v3(data); - } -#else - if (isnan(len)) { - zero_v3(data); - } -#endif - break; - } - } - } - - S++; - l = l->next; - } while (l != f->l_first); -} - -/* The original version of this function was broken (and subsequently removed) - because it didn't properly set the subdivision level; it also used the old - multires system. The new subdiv API is now used instead. - */ -void BKE_multires_bmesh_space_set(Object *ob, BMesh *bm, int mode) -{ - if (!bm->totface || !CustomData_has_layer(&bm->ldata, CD_MDISPS)) { - return; - } - - // get multires settings - MultiresModifierData *mmd = bm->haveMultiResSettings ? &bm->multires : nullptr; - - if (!mmd && ob) { - mmd = get_multires_modifier(nullptr, ob, true); - } - - if (!mmd || !CustomData_has_layer(&bm->ldata, CD_MDISPS)) { - return; - } - - // cache multires settings in bmesh - bm->multiresSpace = mode; - - // create temporary mesh structure - Mesh _me, *me = &_me; - memset(me, 0, sizeof(Mesh)); - CustomData_reset(&me->vdata); - CustomData_reset(&me->edata); - CustomData_reset(&me->ldata); - CustomData_reset(&me->fdata); - CustomData_reset(&me->pdata); - - CustomData_MeshMasks extra = CD_MASK_DERIVEDMESH; - extra.lmask |= CD_MASK_MDISPS; - - BM_mesh_bm_to_me_for_eval(bm, me, &extra); - SubdivSettings settings2; - - // copy the settings and then set subdivision level to max - MultiresModifierData mmdcpy = blender::dna::shallow_copy(*mmd); - mmdcpy.lvl = mmdcpy.sculptlvl = mmdcpy.renderlvl = mmdcpy.totlvl; - - // set up subdivision surface - BKE_multires_subdiv_settings_init(&settings2, &mmdcpy); - Subdiv *sd = BKE_subdiv_new_from_mesh(&settings2, me); - BKE_subdiv_eval_begin_from_mesh(sd, me, nullptr, SUBDIV_EVALUATOR_TYPE_CPU, nullptr); - - // create a fake object with .sculpt set to nullptr - Object fakeob; - if (ob) { - memcpy(static_cast(&fakeob), static_cast(ob), sizeof(*ob)); - fakeob.sculpt = nullptr; - } - else { - memset(&fakeob, 0, sizeof(fakeob)); - fakeob.data = me; - BLI_addtail(&fakeob.modifiers, &mmdcpy); - } - - int i, gridSize; - int totpoly = bm->totface; - - // force paranoia recalc of indices and lookup tables - bm->elem_index_dirty |= BM_FACE; - bm->elem_table_dirty |= BM_FACE; - - BM_mesh_elem_index_ensure(bm, BM_FACE); - BM_mesh_elem_table_ensure(bm, BM_FACE); - - gridSize = multires_side_tot[mmd->totlvl]; - - int cd_disp_off = CustomData_get_offset(&bm->ldata, CD_MDISPS); - - BMFace *f; - BMIter iter; - i = 0; - - /*check that all grids are allocated and also set some indices*/ - BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { - BMIter iter2; - BMLoop *l; - - f->head.index = i; - - BM_ITER_ELEM (l, &iter2, f, BM_LOOPS_OF_FACE) { - MDisps *mdisp = (MDisps *)BM_ELEM_CD_GET_VOID_P(l, cd_disp_off); - - /* allocate new disps, this can happen with newly created faces */ - if (!mdisp->disps) { - multires_reallocate_mdisps(1, mdisp, mmd->totlvl); - } - - l->head.index = i; - - if (f->len != 4) { - i++; - } - } - - if (f->len == 4) { - i++; - } - } - - // do the space conversion - - TaskParallelSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.min_iter_per_thread = CCG_TASK_LIMIT; - - MultiresThreadedData data; - - memset(static_cast(&data), 0, sizeof(data)); - - data.bmop = (MultiResSpace)mode; - data.sd = sd; - data.lvl = mmd->totlvl; - data.bm = bm; - data.cd_mdisps_off = cd_disp_off; - data.gridSize = gridSize; - - BLI_task_parallel_range(0, totpoly, &data, multires_bmesh_space_set_cb, &settings); - - BKE_mesh_free_data_for_undo(me); - BKE_subdiv_free(sd); - - bm->elem_index_dirty |= BM_FACE | BM_LOOP; - bm->elem_table_dirty |= BM_FACE | BM_LOOP; -} - static void multires_disp_run_cb(void *__restrict userdata, const int pidx, const TaskParallelTLS *__restrict /*tls*/) @@ -1687,7 +1232,7 @@ void multires_stitch_grids(Object *ob) int num_faces; BKE_pbvh_get_grid_updates(pbvh, false, (void ***)&faces, &num_faces); if (num_faces) { - // XXX BKE_subdiv_ccg_average_stitch_faces(subdiv_ccg, faces, num_faces); + BKE_subdiv_ccg_average_stitch_faces(subdiv_ccg, faces, num_faces); MEM_freeN(faces); } } @@ -1805,7 +1350,7 @@ void old_mdisps_bilinear(float out[3], float (*disps)[3], const int st, float u, urat = u - x; vrat = v - y; - uopp = 1.0f - urat; + uopp = 1 - urat; mul_v3_v3fl(d[0], disps[y * st + x], uopp); mul_v3_v3fl(d[1], disps[y * st + x2], urat); @@ -1814,7 +1359,7 @@ void old_mdisps_bilinear(float out[3], float (*disps)[3], const int st, float u, add_v3_v3v3(d2[0], d[0], d[1]); add_v3_v3v3(d2[1], d[2], d[3]); - mul_v3_fl(d2[0], 1.0f - vrat); + mul_v3_fl(d2[0], 1 - vrat); mul_v3_fl(d2[1], vrat); add_v3_v3v3(out, d2[0], d2[1]); diff --git a/source/blender/blenkernel/intern/multires_reshape.cc b/source/blender/blenkernel/intern/multires_reshape.cc index ba7f20f38dd..d4e842dc10c 100644 --- a/source/blender/blenkernel/intern/multires_reshape.cc +++ b/source/blender/blenkernel/intern/multires_reshape.cc @@ -269,12 +269,6 @@ void multiresModifier_base_apply(Depsgraph *depsgraph, Object *object, MultiresM multires_reshape_apply_base_update_mesh_coords(&reshape_context); multires_reshape_apply_base_refit_base_mesh(&reshape_context); - /** - * Tag update so deform modifiers (e.g. smooth corrective) - * get updated original coordinates - */ - DEG_id_tag_update((ID *)object, ID_RECALC_GEOMETRY); - /* Reshape to the stored final state. * Not that the base changed, so the subdiv is to be refined to the new positions. Unfortunately, * this can not be done foe entirely cheap: if there were deformation modifiers prior to the diff --git a/source/blender/blenkernel/intern/multires_reshape_apply_base.cc b/source/blender/blenkernel/intern/multires_reshape_apply_base.cc index f32de373529..8b4bc8d5dfe 100644 --- a/source/blender/blenkernel/intern/multires_reshape_apply_base.cc +++ b/source/blender/blenkernel/intern/multires_reshape_apply_base.cc @@ -76,7 +76,7 @@ void multires_reshape_apply_base_refit_base_mesh(MultiresReshapeContext *reshape BKE_mesh_vert_poly_map_create(&pmap, &pmap_mem, reshape_context->base_positions, - reshape_context->base_edges, + +reshape_context->base_edges, reshape_context->base_polys, reshape_context->base_loops, base_mesh->totvert, diff --git a/source/blender/blenkernel/intern/multires_reshape_smooth.cc b/source/blender/blenkernel/intern/multires_reshape_smooth.cc index 081b2b0a107..19cad4e8e71 100644 --- a/source/blender/blenkernel/intern/multires_reshape_smooth.cc +++ b/source/blender/blenkernel/intern/multires_reshape_smooth.cc @@ -32,38 +32,6 @@ #include "atomic_ops.h" #include "subdiv_converter.h" -bool debug_invert_m3_m3(float m1[3][3], const float m2[3][3], const char *func, int line) -{ - float det; - int a, b; - bool success; - - /* calc adjoint */ - adjoint_m3_m3(m1, m2); - - /* then determinant old matrix! */ - det = determinant_m3_array(m2); - - if (det > -0.0001 && det < 0.0001) { - fprintf(stderr, "matrix inverse error %s:%i\n\n", func, line); - } - - success = (det != 0.0f); - - if (LIKELY(det != 0.0f)) { - det = 1.0f / det; - for (a = 0; a < 3; a++) { - for (b = 0; b < 3; b++) { - m1[a][b] *= det; - } - } - } - - return success; -} - -//#define invert_m3_m3(m1, m2) debug_invert_m3_m3(m1, m2, __func__, __LINE__) - /* -------------------------------------------------------------------- */ /** \name Local Structs * \{ */ diff --git a/source/blender/blenkernel/intern/multires_reshape_util.cc b/source/blender/blenkernel/intern/multires_reshape_util.cc index bf51c619ff2..93d1662de14 100644 --- a/source/blender/blenkernel/intern/multires_reshape_util.cc +++ b/source/blender/blenkernel/intern/multires_reshape_util.cc @@ -325,8 +325,6 @@ void multires_reshape_free_original_grids(MultiresReshapeContext *reshape_contex void multires_reshape_context_free(MultiresReshapeContext *reshape_context) { - ModifierData *md; - if (reshape_context->need_free_subdiv) { BKE_subdiv_free(reshape_context->subdiv); } diff --git a/source/blender/blenkernel/intern/multires_unsubdivide.cc b/source/blender/blenkernel/intern/multires_unsubdivide.cc index e7ca4992c32..d6c2e8fd80e 100644 --- a/source/blender/blenkernel/intern/multires_unsubdivide.cc +++ b/source/blender/blenkernel/intern/multires_unsubdivide.cc @@ -865,7 +865,6 @@ static BMesh *get_bmesh_from_mesh(Mesh *mesh) { const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(mesh); - BMeshCreateParams bm_create_params{}; bm_create_params.use_toolflags = true; BMesh *bm = BM_mesh_create(&allocsize, &bm_create_params); @@ -873,7 +872,7 @@ static BMesh *get_bmesh_from_mesh(Mesh *mesh) BMeshFromMeshParams bm_from_me_params{}; bm_from_me_params.calc_face_normal = true; bm_from_me_params.calc_vert_normal = true; - BM_mesh_bm_from_me(nullptr, bm, mesh, &bm_from_me_params); + BM_mesh_bm_from_me(bm, mesh, &bm_from_me_params); return bm; } @@ -1151,7 +1150,7 @@ bool multires_unsubdivide_to_basemesh(MultiresUnsubdivideContext *context) BMeshToMeshParams bm_to_me_params{}; bm_to_me_params.calc_object_remap = true; - BM_mesh_bm_to_me(nullptr, nullptr, bm_base_mesh, context->base_mesh, &bm_to_me_params); + BM_mesh_bm_to_me(nullptr, bm_base_mesh, context->base_mesh, &bm_to_me_params); BM_mesh_free(bm_base_mesh); /* Initialize bmesh and maps for the original mesh and extract the grids. */ diff --git a/source/blender/blenkernel/intern/paint.cc b/source/blender/blenkernel/intern/paint.cc index dec3042666c..0b9799904e4 100644 --- a/source/blender/blenkernel/intern/paint.cc +++ b/source/blender/blenkernel/intern/paint.cc @@ -1480,7 +1480,7 @@ static void sculptsession_bm_to_me_update_data_only(Object *ob, bool reorder) params.cd_mask_extra.emask = CD_MASK_MESH_ID; params.cd_mask_extra.pmask = CD_MASK_MESH_ID; - BM_mesh_bm_to_me(nullptr, nullptr, ss->bm, static_cast(ob->data), ¶ms); + BM_mesh_bm_to_me(nullptr, ss->bm, static_cast(ob->data), ¶ms); } } } @@ -2922,7 +2922,7 @@ PBVH *BKE_sculpt_object_pbvh_ensure(Depsgraph *depsgraph, Object *ob) params.cd_mask_extra.vmask = CD_MASK_DYNTOPO_VERT; - BM_mesh_bm_from_me(nullptr, bm, mesh_orig, ¶ms); + BM_mesh_bm_from_me(bm, mesh_orig, ¶ms); } ob->sculpt->bm = bm; diff --git a/source/blender/blenkernel/intern/pbvh.cc b/source/blender/blenkernel/intern/pbvh.cc index 42ad108eeca..1348ce256e2 100644 --- a/source/blender/blenkernel/intern/pbvh.cc +++ b/source/blender/blenkernel/intern/pbvh.cc @@ -27,6 +27,7 @@ #include "DNA_meshdata_types.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" +#include "DNA_modifier_types.h" #include "BKE_attribute.h" #include "BKE_ccg.h" diff --git a/source/blender/blenkernel/intern/pbvh_bmesh.cc b/source/blender/blenkernel/intern/pbvh_bmesh.cc index 861c3d9512a..47ba9dde08c 100644 --- a/source/blender/blenkernel/intern/pbvh_bmesh.cc +++ b/source/blender/blenkernel/intern/pbvh_bmesh.cc @@ -4359,7 +4359,7 @@ BMesh *BKE_pbvh_reorder_bmesh(PBVH *pbvh) printf("roots: %d\n", (int)roots.size()); - BM_mesh_remap(pbvh->header.bm, vidx, eidx, fidx, lidx); + BM_mesh_remap(pbvh->header.bm, vidx, eidx, lidx, fidx); MEM_SAFE_FREE(vidx); MEM_SAFE_FREE(eidx); @@ -4720,7 +4720,7 @@ BMesh *BKE_pbvh_reorder_bmesh1(PBVH *pbvh) faces[i].elem->head.index = faces[i].index; } - BM_mesh_remap(bm, vs, es, fs, nullptr); + BM_mesh_remap(bm, vs, es, nullptr, fs); // create new mappings BMVert **mapvs = MEM_cnew_array(bm->totvert, __func__); @@ -5471,7 +5471,7 @@ void pbvh_bmesh_cache_test(CacheParams *params, BMesh **r_bm, PBVH **r_pbvh_out) } } - BM_mesh_remap(bm, rands[0], rands[1], rands[3], rands[2]); + BM_mesh_remap(bm, rands[0], rands[1], rands[2], rands[3]); for (int i = 0; i < 4; i++) { MEM_SAFE_FREE(rands[i]); diff --git a/source/blender/blenkernel/intern/scene.cc b/source/blender/blenkernel/intern/scene.cc index 494917c2aaf..09dd527a3da 100644 --- a/source/blender/blenkernel/intern/scene.cc +++ b/source/blender/blenkernel/intern/scene.cc @@ -2618,7 +2618,7 @@ static void prepare_mesh_for_viewport_render(Main *bmain, BMeshToMeshParams params{}; params.calc_object_remap = true; params.update_shapekey_indices = true; - BM_mesh_bm_to_me(bmain, nullptr, bm, mesh, ¶ms); + BM_mesh_bm_to_me(bmain, bm, mesh, ¶ms); DEG_id_tag_update(&mesh->id, 0); } } diff --git a/source/blender/bmesh/bmesh_class.h b/source/blender/bmesh/bmesh_class.h index 7066a8d15f0..11d47962214 100644 --- a/source/blender/bmesh/bmesh_class.h +++ b/source/blender/bmesh/bmesh_class.h @@ -2,8 +2,6 @@ #pragma once -#include "DNA_modifier_types.h" - /** \file * \ingroup bmesh * @@ -379,9 +377,6 @@ typedef struct BMesh { * instead of crashing on invalid memory access. */ void *py_handle; - MultiresModifierData multires; // copy of multires settings - bool haveMultiResSettings; - int multiresSpace; struct { int flag; diff --git a/source/blender/bmesh/intern/bmesh_construct.c b/source/blender/bmesh/intern/bmesh_construct.c index 2ead78d5892..caeebfba173 100644 --- a/source/blender/bmesh/intern/bmesh_construct.c +++ b/source/blender/bmesh/intern/bmesh_construct.c @@ -26,10 +26,6 @@ #include "range_tree.h" -#ifdef USE_BMESH_PAGE_CUSTOMDATA -# include "intern/bmesh_data_attr.h" -#endif - #define SELECT 1 #ifdef WITH_BM_ID_FREELIST @@ -1230,7 +1226,7 @@ BMesh *BM_mesh_copy(BMesh *bm_old) { struct BMeshCreateParams params = {0}; #ifndef USE_NEW_IDMAP - params.create_unique_ids = bm_old->idmap.flag &BM_HAS_IDS; + params.create_unique_ids = bm_old->idmap.flag & BM_HAS_IDS; params.id_elem_mask = bm_old->idmap.flag & (BM_VERT | BM_EDGE | BM_LOOP | BM_FACE); params.id_map = bm_old->idmap.flag & BM_HAS_ID_MAP; params.no_reuse_ids = bm_old->idmap.flag & BM_NO_REUSE_IDS; @@ -1362,3 +1358,28 @@ void bm_rebuild_idmap(BMesh *bm) } } } + +int bm_save_id(BMesh *bm, BMElem *elem) +{ + if (!elem->head.data) { + return -1; + } + + if (bm->idmap.flag & elem->head.htype) { + return BM_ELEM_GET_ID(bm, elem); + } + else { + return -1; + } +} + +void bm_restore_id(BMesh *bm, BMElem *elem, int id) +{ + if (!elem->head.data || id == -1) { + return; + } + + if (bm->idmap.flag & elem->head.htype) { + BM_ELEM_CD_SET_INT(elem, bm->idmap.cd_id_off[elem->head.htype], id); + } +} diff --git a/source/blender/bmesh/intern/bmesh_core.h b/source/blender/bmesh/intern/bmesh_core.h index 27ffd2bcd94..a1a9774f006 100644 --- a/source/blender/bmesh/intern/bmesh_core.h +++ b/source/blender/bmesh/intern/bmesh_core.h @@ -10,6 +10,12 @@ extern "C" { #endif +typedef struct BMCustomLayerReq { + int type; + const char *name; // can be NULL + int flag; +} BMCustomLayerReq; + struct BMTracer; BMFace *BM_face_copy(BMesh *bm_dst, BMesh *bm_src, BMFace *f, bool copy_verts, bool copy_edges); diff --git a/source/blender/bmesh/intern/bmesh_interp.c b/source/blender/bmesh/intern/bmesh_interp.c index d6586f19bed..7798d24c229 100644 --- a/source/blender/bmesh/intern/bmesh_interp.c +++ b/source/blender/bmesh/intern/bmesh_interp.c @@ -12,13 +12,12 @@ #include "DNA_meshdata_types.h" #include "BLI_alloca.h" -#include "BLI_array.h" #include "BLI_linklist.h" #include "BLI_math.h" #include "BLI_memarena.h" #include "BLI_string.h" #include "BLI_task.h" -#include "BLI_utildefines.h" +#include "BLI_array.h" #include "BKE_attribute.h" #include "BKE_customdata.h" @@ -27,37 +26,11 @@ #include "bmesh.h" #include "intern/bmesh_private.h" -int bm_save_id(BMesh *bm, BMElem *elem) -{ - if (!elem->head.data) { - return -1; - } - - if (bm->idmap.flag & elem->head.htype) { - return BM_ELEM_GET_ID(bm, elem); - } - else { - return -1; - } -} - -void bm_restore_id(BMesh *bm, BMElem *elem, int id) -{ - if (!elem->head.data || id == -1) { - return; - } - - if (bm->idmap.flag & elem->head.htype) { - BM_ELEM_CD_SET_INT(elem, bm->idmap.cd_id_off[elem->head.htype], id); - } -} - static void copy_cdata_simple(BMesh *bm, CustomData *data_layer, BMElem *ele_dst, const BMElem *ele_src) { - int id = bm_save_id(bm, ele_dst); int cd_tflags; MToolFlags saved_tflags; @@ -71,7 +44,6 @@ static void copy_cdata_simple(BMesh *bm, if (cd_tflags != -1) { *(MToolFlags *)BM_ELEM_CD_GET_VOID_P(ele_dst, cd_tflags) = saved_tflags; } - bm_restore_id(bm, ele_dst, id); } /* edge and vertex share, currently there's no need to have different logic */ @@ -316,52 +288,17 @@ static bool quad_co(const float v1[3], /* rotate */ poly_rotate_plane(n, projverts, 5); - float projverts2[4][3]; - /* subtract origin */ for (i = 0; i < 4; i++) { sub_v2_v2(projverts[i], projverts[4]); - - copy_v3_v3(projverts2[i], projverts[i]); } - // expand quad a bit -#if 0 - float eps = FLT_EPSILON * 40000; - float c[3]; - - mid_v3_v3v3v3v3(c, projverts[0], projverts[1], projverts[2], projverts[3]); - - sub_v3_v3(projverts2[0], c); - sub_v3_v3(projverts2[1], c); - sub_v3_v3(projverts2[2], c); - sub_v3_v3(projverts2[3], c); - mul_v3_fl(projverts2[0], 1.0f + eps); - mul_v3_fl(projverts2[1], 1.0f + eps); - mul_v3_fl(projverts2[2], 1.0f + eps); - mul_v3_fl(projverts2[3], 1.0f + eps); - add_v3_v3(projverts2[0], c); - add_v3_v3(projverts2[1], c); - add_v3_v3(projverts2[2], c); - add_v3_v3(projverts2[3], c); -#endif - - if (!isect_point_quad_v2(origin, projverts2[0], projverts2[1], projverts2[2], projverts2[3])) { + if (!isect_point_quad_v2(origin, projverts[0], projverts[1], projverts[2], projverts[3])) { return false; } resolve_quad_uv_v2(r_uv, origin, projverts[0], projverts[3], projverts[2], projverts[1]); -#if 0 - float eps2 = FLT_EPSILON * 4000; - if (r_uv[0] < -eps2 || r_uv[1] < -eps2 || r_uv[0] > 1.0 + eps2 || r_uv[1] > 1.0 + eps2) { - return false; - } -#endif - - CLAMP(r_uv[0], 0.0f, 0.99999f); - CLAMP(r_uv[1], 0.0f, 0.99999f); - return true; } @@ -394,7 +331,8 @@ static bool mdisp_in_mdispquad(BMLoop *l_src, float r_axis_y[3], float r_uv[2]) { - float v1[3], v2[3], v3[3], v4[3], e1[3], e2[3]; + float v1[3], v2[3], c[3], v3[3], v4[3], e1[3], e2[3]; + float eps = FLT_EPSILON * 4000; if (is_zero_v3(l_src->v->no)) { BM_vert_normal_update_all(l_src->v); @@ -406,8 +344,6 @@ static bool mdisp_in_mdispquad(BMLoop *l_src, compute_mdisp_quad(l_dst, l_dst_f_center, v1, v2, v3, v4, e1, e2); /* expand quad a bit */ - float c[3]; - float eps = FLT_EPSILON * 400; mid_v3_v3v3v3v3(c, v1, v2, v3, v4); sub_v3_v3(v1, c); @@ -489,10 +425,8 @@ typedef struct BMLoopInterpMultiresData { BMLoop *l_src_first; int cd_loop_mdisp_offset; - int space; MDisps *md_dst; const float *f_src_center; - const float *f_dst_center; float *axis_x, *axis_y; float *v1, *v4; @@ -511,7 +445,6 @@ static void loop_interp_multires_cb(void *__restrict userdata, BMLoop *l_first = data->l_src_first; BMLoop *l_dst = data->l_dst; const int cd_loop_mdisp_offset = data->cd_loop_mdisp_offset; - int space = data->space; MDisps *md_dst = data->md_dst; const float *f_src_center = data->f_src_center; @@ -526,19 +459,6 @@ static void loop_interp_multires_cb(void *__restrict userdata, const int res = data->res; const float d = data->d; - float quad[4][3]; - - float n1[3], n2[3]; - normal_tri_v3(n1, l_dst->v->co, l_dst->next->v->co, data->f_dst_center); - - if (space == MULTIRES_SPACE_ABSOLUTE) { - BMLoop *l = l_dst; - - copy_v3_v3(quad[0], data->f_dst_center); - interp_v3_v3v3(quad[1], l->v->co, l->next->v->co, 0.5); - copy_v3_v3(quad[2], l->v->co); - interp_v3_v3v3(quad[3], l->v->co, l->prev->v->co, 0.5); - } float x = d * ix, y; int iy; @@ -550,71 +470,24 @@ static void loop_interp_multires_cb(void *__restrict userdata, madd_v3_v3v3fl(co2, v4, e2, y); interp_v3_v3v3(co, co1, co2, x); - float sum[3]; - int tot = 0; - zero_v3(sum); - float mindis = 1e17; - - float baseco[3]; - if (space == MULTIRES_SPACE_ABSOLUTE) { - interp_bilinear_quad_v3(quad, x, y, baseco); - } - do { MDisps *md_src; float src_axis_x[3], src_axis_y[3]; float uv[2]; - normal_tri_v3(n2, l_iter->v->co, l_iter->next->v->co, data->f_src_center); - float th = dot_v3v3(n1, n2); - if (th < 0.0f) { - negate_v3(n2); - } - - th = acos(dot_v3v3(n1, n2) * 0.999999f); - if (th > M_PI * 0.1) { - continue; - } - md_src = BM_ELEM_CD_GET_VOID_P(l_iter, cd_loop_mdisp_offset); if (mdisp_in_mdispquad(l_dst, l_iter, f_src_center, co, res, src_axis_x, src_axis_y, uv)) { - float disp[3]; - copy_v3_v3(disp, md_dst->disps[iy * res + ix]); + old_mdisps_bilinear(md_dst->disps[iy * res + ix], md_src->disps, res, uv[0], uv[1]); + bm_loop_flip_disp(src_axis_x, src_axis_y, axis_x, axis_y, md_dst->disps[iy * res + ix]); - old_mdisps_bilinear(disp, md_src->disps, res, uv[0], uv[1]); - - if (space == MULTIRES_SPACE_TANGENT) { - bm_loop_flip_disp(src_axis_x, src_axis_y, axis_x, axis_y, disp); - } - - float l = len_v3v3(disp, baseco); - if (l < mindis) { - mindis = l; - // tot++; - // copy_v3_v3(sum, disp); - } - add_v3_v3(sum, disp); - tot++; - // break; + break; } } while ((l_iter = l_iter->next) != l_first); - - if (tot) { - mul_v3_fl(sum, 1.0 / (float)tot); - copy_v3_v3(md_dst->disps[iy * res + ix], sum); - } - else { - // printf("failed to set disp: %f %f\n", x, y); - if (space == MULTIRES_SPACE_ABSOLUTE) { - // copy_v3_v3(md_dst->disps[iy * res + ix], baseco); - // copy_v3_v3(md_dst->disps[iy * res + ix], baseco); - } - } } } -void BM_loop_interp_multires_ex(BMesh *bm, +void BM_loop_interp_multires_ex(BMesh *UNUSED(bm), BMLoop *l_dst, const BMFace *f_src, const float f_dst_center[3], @@ -656,9 +529,7 @@ void BM_loop_interp_multires_ex(BMesh *bm, .cd_loop_mdisp_offset = cd_loop_mdisp_offset, .md_dst = md_dst, .f_src_center = f_src_center, - .f_dst_center = f_dst_center, .axis_x = axis_x, - .space = bm->multiresSpace, .axis_y = axis_y, .v1 = v1, .v4 = v4, @@ -701,8 +572,6 @@ void BM_face_interp_multires_ex(BMesh *bm, BM_loop_interp_multires_ex( bm, l_iter, f_src, f_dst_center, f_src_center, cd_loop_mdisp_offset); } while ((l_iter = l_iter->next) != l_first); - - BM_face_multires_bounds_smooth(bm, f_dst); } void BM_face_interp_multires(BMesh *bm, BMFace *f_dst, const BMFace *f_src) @@ -720,277 +589,54 @@ void BM_face_interp_multires(BMesh *bm, BMFace *f_dst, const BMFace *f_src) } } -// smooth with weight falloff towards center of grids -static void bm_multires_smooth(BMesh *bm, BMFace *f, bool no_boundary) -{ - const int cd_loop_mdisp_offset = CustomData_get_offset(&bm->ldata, CD_MDISPS); - float(*orig)[3] = NULL; - BLI_array_staticdeclare(orig, 256 * 256); - - if (cd_loop_mdisp_offset < 0) { - return; - } - - float cent[3]; - zero_v3(cent); - - int ctot = 0; - BMLoop *cl = f->l_first; - do { - add_v3_v3(cent, cl->v->co); - cl = cl->next; - ctot++; - } while (cl != f->l_first); - mul_v3_fl(cent, 1.0f / (float)ctot); - - const int offs[][2] = { - {0, 0}, - // {-1, -1}, - {-1, 0}, - // {-1, 1}, - {0, 1}, - // {1, 1}, - {1, 0}, - // {1, -1}, - {0, -1}, - }; - - int totoff = sizeof(offs) / sizeof(*offs); - -#ifndef ABS -# define ABS(a) ((a) < 0 ? -(a) : (a)) -#endif - - // int space = bm->multiresSpace; - BMLoop *l = f->l_first; - do { - MDisps *md = BM_ELEM_CD_GET_VOID_P(l, cd_loop_mdisp_offset); - if (!md->disps) - continue; - - int res = (int)floor(sqrt((double)md->totdisp) + 0.000001); - - int start = no_boundary ? 1 : 0; - int end = no_boundary ? res - 1 : res; - float df = 1.0f / (float)(res - 1); - float u = 0.0; - - BLI_array_clear(orig); - BLI_array_reserve(orig, md->totdisp * 3); - memcpy(orig, md->disps, sizeof(float) * 3 * md->totdisp); - - for (int x = start; x < end; x++, u += df) { - float v = 0.0; - - for (int y = start; y < end; y++, v += df) { - float co[3]; - float tot = 0.0f; - - zero_v3(co); - - int idx1 = y * res + x; - - for (int oi = 0; oi < totoff; oi++) { - int ox = x + offs[oi][0]; - int oy = y + offs[oi][1]; - MDisps *md2 = md; - - if (1 && (ox < 0 || oy < 0 || ox >= res || oy >= res)) { - BMLoop *l2 = NULL; - BMLoop *ls = l; - - if (ox < 0 && oy < 0) { - l2 = ls->next->next; - ox = ABS(ox); - oy = ABS(oy); - } - else if (ox < 0 && oy >= 0 && oy < res) { - l2 = ls->prev; - int t = oy; - - oy = -ox; - ox = t; - } - else if (oy < 0 && ox >= 0 && ox < res) { - l2 = ls->next; - int t = oy; - - oy = ox; - ox = -t; - } - else if (ox >= res && oy >= 0 && oy < res) { - l2 = ls->radial_next->next; - - if (ls->v == l2->v) { - int t = oy; - - oy = 2 * res - ox - 1; - ox = t; - } - else { - l2 = l2->prev; - ox = res - ox; - } - - // XXX disables this branch - // ox = oy = -1; - } - else if (oy >= res && ox >= 0 && ox < res) { - l2 = ls->prev->radial_next; - if (l2->v == ls->v) { - int t = ox; - - ox = 2 * res - oy - 1; - oy = t; - } - else { - l2 = l2->next; - oy = 2 * res - oy - 1; - } - // XXX disables this branch - // ox = oy = -1; - } - else { - printf("ignoring non-4-valence multires corner %d %d %d %d : %d %d %d\t", - ox, - oy, - offs[oi][0], - offs[oi][1], - x, - y, - res); - l2 = NULL; - } - - if (l2) { - // ox = res - ox - 1; - // oy = res - oy - 1; - md2 = BM_ELEM_CD_GET_VOID_P(l2, cd_loop_mdisp_offset); - } - } - - if (!md2->disps || oy < 0 || oy >= res || ox < 0 || ox >= res) { - continue; - } - - int idx2 = oy * res + ox; - float *oco2 = md == md2 ? orig[idx2] : md2->disps[idx2]; - float co2[3]; - - copy_v3_v3(co2, oco2); - - float dx = (float)offs[oi][0]; - float dy = (float)offs[oi][1]; - - float w = 2.0f - dx * dx + dy * dy; - - if (no_boundary && (ox == 0 || oy == 0 || ox == res - 1 || oy == res - 1)) { - // w = 2.0; - } - else if (ox == x && oy == y) { - // blend less away from edges - float au = fabs(u - 0.5) * 2.0, av = fabs(v - 0.5) * 2.0; - float w2 = au * au + av * av; - - w = 4.0 * w2; - } - w = 1.0; - - mul_v3_fl(co2, w); - - tot += w; - add_v3_v3(co, co2); - } - - /* - float vec[3]; - copy_v3_v3(vec, f->no); - mul_v3_fl(vec, 0.4); - - add_v3_v3(md->disps[idx1], vec); - sub_v3_v3(md->disps[idx1], cent); - mul_v3_fl(md->disps[idx1], 1.2); - add_v3_v3(md->disps[idx1], cent); - - continue; - //*/ - - if (tot > 0.0f) { - mul_v3_fl(co, 1.0f / tot); - copy_v3_v3(md->disps[idx1], co); - } - } - } - - l = l->next; - } while (l != f->l_first); - - BLI_array_free(orig); -} - -struct Object *multires_dump_grids_bmesh(struct Object *bmob, BMesh *bm); - -void bmo_test_mres_smooth_exec(BMesh *bm, BMOperator *op) -{ - BMIter iter; - BMFace *f; - - if (!CustomData_has_layer(&bm->ldata, CD_MDISPS)) { - return; - } - - BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { - bool ok = !!BM_elem_flag_test(f, BM_ELEM_SELECT); - ok = ok && !BM_elem_flag_test(f, BM_ELEM_HIDDEN); - - if (!ok) { - continue; - } - - // bm_multires_smooth(bm, f, true); - // BM_multires_smooth(bm, f, false); - // BM_multires_smooth(bm, f, false); - // for (int i=0; i<5; i++) { - BM_face_multires_bounds_smooth(bm, f); - // } - } - - multires_dump_grids_bmesh(NULL, bm); -} - void BM_face_multires_bounds_smooth(BMesh *bm, BMFace *f) { - return; - if (bm->multiresSpace == MULTIRES_SPACE_ABSOLUTE) { - BM_face_multires_stitch(bm, f); - - // for (int i=0; i<5; i++) { - // bm_multires_smooth(bm, f, true); - //} - } -} - -/** - * smooths boundaries between multires grids, - * including some borders in adjacent faces - */ -void BM_face_multires_stitch(BMesh *bm, BMFace *f) -{ + const int cd_loop_mdisp_offset = CustomData_get_offset(&bm->ldata, CD_MDISPS); BMLoop *l; BMIter liter; - float co[3]; - const int cd_loop_mdisp_offset = CustomData_get_offset(&bm->ldata, CD_MDISPS); - int sides = 0; if (cd_loop_mdisp_offset == -1) { return; } + BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) { + MDisps *mdp = BM_ELEM_CD_GET_VOID_P(l->prev, cd_loop_mdisp_offset); + MDisps *mdl = BM_ELEM_CD_GET_VOID_P(l, cd_loop_mdisp_offset); + MDisps *mdn = BM_ELEM_CD_GET_VOID_P(l->next, cd_loop_mdisp_offset); + float co1[3]; + int sides; + int y; + + /** + * mdisps is a grid of displacements, ordered thus: + *
+     *                    v4/next
+     *                      |
+     *  |      v1/cent-----mid2 ---> x
+     *  |         |         |
+     *  |         |         |
+     * v2/prev---mid1-----v3/cur
+     *            |
+     *            V
+     *            y
+     * 
+ */ + + sides = (int)sqrt(mdp->totdisp); + for (y = 0; y < sides; y++) { + mid_v3_v3v3(co1, mdn->disps[y * sides], mdl->disps[y]); + + copy_v3_v3(mdn->disps[y * sides], co1); + copy_v3_v3(mdl->disps[y], co1); + } + } + BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) { MDisps *mdl1 = BM_ELEM_CD_GET_VOID_P(l, cd_loop_mdisp_offset); MDisps *mdl2; - float co1[3], co2[3]; - int x, y; + float co1[3], co2[3], co[3]; + int sides; + int y; /** * mdisps is a grid of displacements, ordered thus: @@ -1018,7 +664,7 @@ void BM_face_multires_stitch(BMesh *bm, BMFace *f) mdl2 = BM_ELEM_CD_GET_VOID_P(l->radial_next->next, cd_loop_mdisp_offset); } - sides = (int)floor(sqrt(mdl1->totdisp) + FLT_EPSILON); + sides = (int)sqrt(mdl1->totdisp); for (y = 0; y < sides; y++) { int a1, a2, o1, o2; @@ -1030,128 +676,24 @@ void BM_face_multires_stitch(BMesh *bm, BMFace *f) o2 = (sides - 1) * sides + y; } else { + a1 = sides * y + sides - 2; + a2 = sides * y + sides - 2; o1 = sides * y + sides - 1; o2 = sides * y + sides - 1; } - mid_v3_v3v3(co, mdl1->disps[o1], mdl2->disps[o2]); + /* magic blending numbers, hardcoded! */ + add_v3_v3v3(co1, mdl1->disps[a1], mdl2->disps[a2]); + mul_v3_fl(co1, 0.18); + + add_v3_v3v3(co2, mdl1->disps[o1], mdl2->disps[o2]); + mul_v3_fl(co2, 0.32); + + add_v3_v3v3(co, co1, co2); copy_v3_v3(mdl1->disps[o1], co); copy_v3_v3(mdl2->disps[o2], co); } - - BMLoop *l2 = l->prev->radial_next; - bool reverse = false; - - if (l2->v != l->v) { - reverse = true; - l2 = l2->next; - } - - mdl2 = BM_ELEM_CD_GET_VOID_P(l2, cd_loop_mdisp_offset); - y = sides - 1; - - if (!mdl2->disps) { - continue; - } - - for (x = 0; x < sides; x++) { - int x2, y2, o1, o2; - - if (!reverse) { - x2 = sides - 1; - y2 = x; - } - else { - x2 = x; - y2 = y; - } - - o1 = y * sides + x; - o2 = y2 * sides + x2; - - mid_v3_v3v3(co, mdl1->disps[o1], mdl2->disps[o2]); - copy_v3_v3(mdl1->disps[o1], co); - copy_v3_v3(mdl2->disps[o2], co); - } - } - - // do exterior corners - BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) { - MDisps *md1 = BM_ELEM_CD_GET_VOID_P(l, cd_loop_mdisp_offset); - BMIter l2iter; - BMLoop *l2; - int x = sides - 1, y = sides - 1; - int idx = y * sides + x; - int tot = 1; - - zero_v3(co); - - if (!md1->disps) { - continue; - } - - add_v3_v3(co, md1->disps[idx]); - - BM_ITER_ELEM (l2, &l2iter, l->v, BM_LOOPS_OF_VERT) { - if (l2->v != l->v) { // winding is flipped - l2 = l2->next; - } - - MDisps *md2 = BM_ELEM_CD_GET_VOID_P(l2, cd_loop_mdisp_offset); - - if (l == l2 || !md2->disps) { - continue; - } - - add_v3_v3(co, md2->disps[idx]); - tot++; - } - - mul_v3_fl(co, 1.0f / (float)tot); - - BM_ITER_ELEM (l2, &l2iter, l->v, BM_LOOPS_OF_VERT) { - if (l2->v != l->v) { // winding is flipped - l2 = l2->next; - } - - MDisps *md2 = BM_ELEM_CD_GET_VOID_P(l2, cd_loop_mdisp_offset); - - if (l == l2 || !md2->disps) { - continue; - } - - copy_v3_v3(md2->disps[idx], co); - } - } - - // do interior corners - int tot = 0; - zero_v3(co); - - BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) { - MDisps *md1 = BM_ELEM_CD_GET_VOID_P(l, cd_loop_mdisp_offset); - - if (!md1->disps) { - continue; - } - - add_v3_v3(co, md1->disps[0]); - tot++; - } - - if (tot) { - mul_v3_fl(co, 1.0f / (float)tot); - } - - BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) { - MDisps *md1 = BM_ELEM_CD_GET_VOID_P(l, cd_loop_mdisp_offset); - - if (!md1->disps) { - continue; - } - - copy_v3_v3(md1->disps[0], co); } } @@ -1470,8 +1012,6 @@ void BM_data_layer_add(BMesh *bm, CustomData *data, int type) if (olddata.layers) { MEM_freeN(olddata.layers); } - - bm_update_idmap_cdlayers(bm); } void BM_data_layer_add_named(BMesh *bm, CustomData *data, int type, const char *name) @@ -1487,8 +1027,6 @@ void BM_data_layer_add_named(BMesh *bm, CustomData *data, int type, const char * if (olddata.layers) { MEM_freeN(olddata.layers); } - - bm_update_idmap_cdlayers(bm); } void BM_data_layer_ensure_named(BMesh *bm, CustomData *data, int type, const char *name) @@ -1562,8 +1100,6 @@ void BM_data_layer_free(BMesh *bm, CustomData *data, int type) if (olddata.layers) { MEM_freeN(olddata.layers); } - - bm_update_idmap_cdlayers(bm); } bool BM_data_layer_free_named(BMesh *bm, CustomData *data, const char *name) @@ -1607,8 +1143,6 @@ void BM_data_layer_free_n(BMesh *bm, CustomData *data, int type, int n) if (olddata.layers) { MEM_freeN(olddata.layers); } - - bm_update_idmap_cdlayers(bm); } void BM_data_layer_copy(BMesh *bm, CustomData *data, int type, int src_n, int dst_n) diff --git a/source/blender/bmesh/intern/bmesh_interp.h b/source/blender/bmesh/intern/bmesh_interp.h index 8ee0b78f0f5..bf11fa45e09 100644 --- a/source/blender/bmesh/intern/bmesh_interp.h +++ b/source/blender/bmesh/intern/bmesh_interp.h @@ -9,18 +9,10 @@ struct LinkNode; struct MemArena; -typedef struct BMCustomLayerReq { - int type; - const char *name; // can be NULL - int flag; -} BMCustomLayerReq; - #ifdef __cplusplus extern "C" { #endif -void BM_face_multires_stitch(BMesh *bm, BMFace *f); - void BM_loop_interp_multires_ex(BMesh *bm, BMLoop *l_dst, const BMFace *f_src, @@ -66,15 +58,9 @@ void BM_data_interp_from_edges( * Walks around the faces of \a e and interpolates * the loop data between two sources. */ -void BM_data_interp_face_vert_edge(BMesh *bm, - const BMVert *v_src_1, - const BMVert *v_src_2, - BMVert *v, - BMEdge *e, - const float fac); - +void BM_data_interp_face_vert_edge( + BMesh *bm, const BMVert *v_src_1, const BMVert *v_src_2, BMVert *v, BMEdge *e, float fac); void BM_data_layers_ensure(BMesh *bm, CustomData *data, BMCustomLayerReq *layers, int totlayer); - void BM_data_layer_add(BMesh *bm, CustomData *data, int type); void BM_data_layer_add_named(BMesh *bm, CustomData *data, int type, const char *name); void BM_data_layer_ensure_named(BMesh *bm, CustomData *data, int type, const char *name); diff --git a/source/blender/bmesh/intern/bmesh_log.cc b/source/blender/bmesh/intern/bmesh_log.cc index e98aea0080e..5f272a3162b 100644 --- a/source/blender/bmesh/intern/bmesh_log.cc +++ b/source/blender/bmesh/intern/bmesh_log.cc @@ -289,8 +289,7 @@ struct BMLogSetFull : public BMLogSetBase { params2.ignore_id_layers = false; BM_mesh_clear(bm); - BM_mesh_bm_from_me(nullptr, - bm, + BM_mesh_bm_from_me(bm, mesh, // note we stored shapekeys as customdata layers, // that's why the shapekey params are false ¶ms2); @@ -712,7 +711,7 @@ struct BMLogEntry { ATTR_NO_OPT void undo(BMesh *bm, BMLogCallbacks *callbacks) { for (int i = sets.size() - 1; i >= 0; i--) { - //printf(" - %d of %d\n", i, (int)(sets.size() - 1)); + // printf(" - %d of %d\n", i, (int)(sets.size() - 1)); sets[i]->undo(bm, callbacks); } } diff --git a/source/blender/bmesh/intern/bmesh_mesh.cc b/source/blender/bmesh/intern/bmesh_mesh.cc index ec60d65400f..96da7a1ae9a 100644 --- a/source/blender/bmesh/intern/bmesh_mesh.cc +++ b/source/blender/bmesh/intern/bmesh_mesh.cc @@ -239,11 +239,6 @@ BMesh *BM_mesh_create(const BMAllocTemplate *allocsize, const struct BMeshCreate BM_mesh_elem_toolflags_ensure(bm); } -#ifdef USE_BMESH_PAGE_CUSTOMDATA - bmesh_update_attr_refs(bm); - BMAttr_init(bm); -#endif - return bm; } @@ -372,11 +367,6 @@ void BM_mesh_data_free(BMesh *bm) } BMO_error_clear(bm); - -#ifdef USE_BMESH_PAGE_CUSTOMDATA - BMAttr_free(bm->attr_list); - bm->attr_list = nullptr; -#endif } void BM_mesh_clear(BMesh *bm) @@ -419,17 +409,6 @@ void BM_mesh_clear(BMesh *bm) #endif bm_init_idmap_cdlayers(bm); } - -#ifdef USE_BMESH_PAGE_CUSTOMDATA - if (!bm->attr_list) { - bm->attr_list = BMAttr_new(); - } - else { - BMAttr_reset(bm->attr_list); - } - - BMAttr_init(bm); -#endif } void BM_mesh_free(BMesh *bm) @@ -445,35 +424,42 @@ void BM_mesh_free(BMesh *bm) MEM_freeN(bm); } - -/** - * \brief BMesh Begin Edit - * - * Functions for setting up a mesh for editing and cleaning up after - * the editing operations are done. These are called by the tools/operator - * API for each time a tool is executed. - */ -void bmesh_edit_begin(BMesh *bm, BMOpTypeFlag type_flag) +void bmesh_edit_begin(BMesh * /*bm*/, BMOpTypeFlag /*type_flag*/) { + /* Most operators seem to be using BMO_OPTYPE_FLAG_UNTAN_MULTIRES to change the MDisps to + * absolute space during mesh edits. With this enabled, changes to the topology + * (loop cuts, edge subdivides, etc) are not reflected in the higher levels of + * the mesh at all, which doesn't seem right. Turning off completely for now, + * until this is shown to be better for certain types of mesh edits. */ +#ifdef BMOP_UNTAN_MULTIRES_ENABLED /* switch multires data out of tangent space */ if ((type_flag & BMO_OPTYPE_FLAG_UNTAN_MULTIRES) && CustomData_has_layer(&bm->ldata, CD_MDISPS)) { - BM_enter_multires_space(nullptr, bm, MULTIRES_SPACE_ABSOLUTE); + bmesh_mdisps_space_set(bm, MULTIRES_SPACE_TANGENT, MULTIRES_SPACE_ABSOLUTE); + /* ensure correct normals, if possible */ - // bmesh_rationalize_normals(bm, 0); - // BM_mesh_normals_update(bm); + bmesh_rationalize_normals(bm, 0); + BM_mesh_normals_update(bm); } +#endif } void bmesh_edit_end(BMesh *bm, BMOpTypeFlag type_flag) { ListBase select_history; + /* BMO_OPTYPE_FLAG_UNTAN_MULTIRES disabled for now, see comment above in bmesh_edit_begin. */ +#ifdef BMOP_UNTAN_MULTIRES_ENABLED /* switch multires data into tangent space */ - if ((type_flag & BMO_OPTYPE_FLAG_UNTAN_MULTIRES) && - CustomData_has_layer(&bm->ldata, CD_MDISPS)) { - BM_enter_multires_space(nullptr, bm, MULTIRES_SPACE_TANGENT); + if ((flag & BMO_OPTYPE_FLAG_UNTAN_MULTIRES) && CustomData_has_layer(&bm->ldata, CD_MDISPS)) { + /* set normals to their previous winding */ + bmesh_rationalize_normals(bm, 1); + bmesh_mdisps_space_set(bm, MULTIRES_SPACE_ABSOLUTE, MULTIRES_SPACE_TANGENT); } + else if (flag & BMO_OP_FLAG_RATIONALIZE_NORMALS) { + bmesh_rationalize_normals(bm, 1); + } +#endif /* compute normals, clear temp flags and flush selections */ if (type_flag & BMO_OPTYPE_FLAG_NORMALS_CALC) { @@ -895,25 +881,11 @@ int BM_mesh_elem_count(BMesh *bm, const char htype) } } -/** - * Remaps the vertices, edges and/or faces of the bmesh as indicated by vert/edge/face_idx arrays - * (xxx_idx[org_index] = new_index). - * - * A nullptr array means no changes. - * - * \note - * - Does not mess with indices, just sets elem_index_dirty flag. - * - For verts/edges/faces only (as loops must remain "ordered" and "aligned" - * on a per-face basis...). - * - * \warning Be careful if you keep pointers to affected BM elements, - * or arrays, when using this func! - */ void BM_mesh_remap(BMesh *bm, const uint *vert_idx, const uint *edge_idx, - const uint *face_idx, - const uint *loop_idx) + const uint *loop_idx, + const uint *face_idx) { /* Mapping old to new pointers. */ GHash *vptr_map = nullptr, *eptr_map = nullptr, *fptr_map = nullptr; diff --git a/source/blender/bmesh/intern/bmesh_mesh.h b/source/blender/bmesh/intern/bmesh_mesh.h index 98816f308a3..58974b27a8d 100644 --- a/source/blender/bmesh/intern/bmesh_mesh.h +++ b/source/blender/bmesh/intern/bmesh_mesh.h @@ -190,8 +190,8 @@ int BM_mesh_elem_count(BMesh *bm, char htype); void BM_mesh_remap(BMesh *bm, const uint *vert_idx, const uint *edge_idx, - const uint *face_idx, - const uint *loop_idx); + const uint *loop_idx, + const uint *face_idx); /** * Use new memory pools for this mesh. diff --git a/source/blender/bmesh/intern/bmesh_mesh_convert.cc b/source/blender/bmesh/intern/bmesh_mesh_convert.cc index 6db9e36bca1..70eed752def 100644 --- a/source/blender/bmesh/intern/bmesh_mesh_convert.cc +++ b/source/blender/bmesh/intern/bmesh_mesh_convert.cc @@ -78,9 +78,7 @@ #include "MEM_guardedalloc.h" #include "BLI_alloca.h" -#include "BLI_array.h" #include "BLI_array.hh" -#include "BLI_bitmap.h" #include "BLI_index_range.hh" #include "BLI_listbase.h" #include "BLI_math_vector.h" @@ -101,68 +99,6 @@ #include "bmesh.h" #include "intern/bmesh_private.h" /* For element checking. */ -#include "range_tree.h" - -static void bm_free_cd_pools(BMesh *bm) -{ - if (bm->vdata.pool) { - BLI_mempool_destroy(bm->vdata.pool); - } - if (bm->edata.pool) { - BLI_mempool_destroy(bm->edata.pool); - } - if (bm->ldata.pool) { - BLI_mempool_destroy(bm->ldata.pool); - } - if (bm->pdata.pool) { - BLI_mempool_destroy(bm->pdata.pool); - } -} - -//#define bm_assign_id(a, b, c, d) -//#define bm_alloc_id(a, b) - -static void bm_unmark_temp_cdlayers(BMesh *bm) -{ - CustomData_unmark_temporary_nocopy(&bm->vdata); - CustomData_unmark_temporary_nocopy(&bm->edata); - CustomData_unmark_temporary_nocopy(&bm->ldata); - CustomData_unmark_temporary_nocopy(&bm->pdata); -} - -static void bm_mark_temp_cdlayers(BMesh *bm) -{ - CustomData_mark_temporary_nocopy(&bm->vdata); - CustomData_mark_temporary_nocopy(&bm->edata); - CustomData_mark_temporary_nocopy(&bm->ldata); - CustomData_mark_temporary_nocopy(&bm->pdata); -} - -#if 0 -# define CustomData_to_bmesh_block(srcdata, destdata, i, block, set_default) \ - { \ - CustomDataLayer *cl = (srcdata)->layers, *cl2 = (destdata)->layers; \ - int size = 0; \ - if (!*block) { \ - *block = BLI_mempool_alloc((destdata)->pool); \ - } \ - for (int j = 0; j < (srcdata)->totlayer; j++, cl++) { \ - if ((destdata)->typemap[cl->type] < 0) { \ - continue; \ - } \ - while (cl2->type != cl->type) { \ - cl2++; \ - } \ - char *ptr = (char *)cl->data; \ - size = cl2 != (destdata)->layers ? cl2->offset - (cl2 - 1)->offset : cl2->offset; \ - ptr += size * i; \ - char *ptr2 = (char *)*block; \ - ptr2 += cl2->offset; \ - memcpy(ptr2, ptr, size); \ - cl2++; \ - } \ - } -#endif #include "CLG_log.h" @@ -228,125 +164,39 @@ static BMFace *bm_face_create_from_mpoly(BMesh &bm, return BM_face_create(&bm, verts.data(), edges.data(), loops.size(), nullptr, BM_CREATE_SKIP_CD); } -void BM_enter_multires_space(Object *ob, BMesh *bm, int space) +static void bm_unmark_temp_cdlayers(BMesh *bm) { - if (!bm->haveMultiResSettings && ob) { - MultiresModifierData *mmd = get_multires_modifier(nullptr, ob, true); - if (mmd) { - memcpy((void *)&bm->multires, (void *)&mmd, sizeof(*mmd)); - bm->haveMultiResSettings = true; - bm->multiresSpace = MULTIRES_SPACE_TANGENT; - } - } - - if (!bm->haveMultiResSettings || !CustomData_has_layer(&bm->ldata, CD_MDISPS) || - space == bm->multiresSpace) { - return; - } - - BKE_multires_bmesh_space_set(ob, bm, space); - bm->multiresSpace = space; + CustomData_unmark_temporary_nocopy(&bm->vdata); + CustomData_unmark_temporary_nocopy(&bm->edata); + CustomData_unmark_temporary_nocopy(&bm->ldata); + CustomData_unmark_temporary_nocopy(&bm->pdata); } -#include "BLI_compiler_attrs.h" - -/** - * \brief Mesh -> BMesh - * \param ob: object that owns bm, may be nullptr (which will disable multires space change) - * \param bm: The mesh to write into, while this is typically a newly created BMesh, - * merging into existing data is supported. - * Note the custom-data layout isn't used. - * If more comprehensive merging is needed we should move this into a separate function - * since this should be kept fast for edit-mode switching and storing undo steps. - * - * \warning This function doesn't calculate face normals. - * - * Mesh IDs will be imported unless requested. If the bmesh was created - * with id map enabled then IDs will be checked for uniqueness, otherwise - * they are imported as is. - */ - -void BM_mesh_bm_from_me(Object *ob, - BMesh *bm, - const Mesh *me, - const struct BMeshFromMeshParams *params) +static void bm_mark_temp_cdlayers(BMesh *bm) { - static int totlayers = 0; - - for (int i = 0; i < 4; i++) { - CustomData *cdata = (&bm->vdata) + i; - - for (int j = 0; j < cdata->totlayer; j++) { - if (cdata->layers[j].type == CD_TOOLFLAGS || cdata->layers[j].type == CD_MESH_ID) { - continue; - } - - totlayers++; - } - } + CustomData_mark_temporary_nocopy(&bm->vdata); + CustomData_mark_temporary_nocopy(&bm->edata); + CustomData_mark_temporary_nocopy(&bm->ldata); + CustomData_mark_temporary_nocopy(&bm->pdata); +} +void BM_mesh_bm_from_me(BMesh *bm, const Mesh *me, const struct BMeshFromMeshParams *params) +{ if (!me) { /* Sanity check. */ return; } - const bool is_new = !(bm->totvert || (bm->vdata.totlayer || bm->edata.totlayer || - bm->pdata.totlayer || bm->ldata.totlayer)); - KeyBlock *actkey; - float(*keyco)[3] = nullptr; - - CustomData_MeshMasks mask = CD_MASK_BMESH; - CustomData_MeshMasks_update(&mask, ¶ms->cd_mask_extra); - - MultiresModifierData *mmd = ob ? get_multires_modifier(nullptr, ob, true) : nullptr; - const CustomData *cdatas[] = {&me->vdata, &me->edata, &me->ldata, &me->pdata}; - // const CustomData *bmdatas[] = {&bm->vdata, &bm->edata, &bm->ldata, &bm->pdata}; - - bool check_id_unqiue = false; - - if (!params->ignore_id_layers) { - for (int i = 0; i < 4; i++) { - int type = 1 << i; - - if (CustomData_has_layer(cdatas[i], CD_MESH_ID)) { - bm->idmap.flag |= type | BM_HAS_IDS; - } - } - - bm_init_idmap_cdlayers(bm); - } - - // check_id_unqiue - if ((bm->idmap.flag & BM_HAS_IDS)) { -#ifndef WITH_BM_ID_FREELIST - if (!bm->idmap.idtree) { - bm->idmap.idtree = range_tree_uint_alloc(0, (uint)-1); - } -#endif - - if (bm->idmap.flag & BM_HAS_ID_MAP) { - check_id_unqiue = true; - } - } if (params->copy_temp_cdlayers) { bm_unmark_temp_cdlayers(bm); } - if (params->copy_temp_cdlayers) { - mask.vmask |= CD_MASK_MESH_ID; - mask.emask |= CD_MASK_MESH_ID; - mask.lmask |= CD_MASK_MESH_ID; - mask.pmask |= CD_MASK_MESH_ID; - } - - if (mmd) { - memcpy((void *)&bm->multires, (void *)&mmd, sizeof(*mmd)); - bm->haveMultiResSettings = true; - bm->multiresSpace = MULTIRES_SPACE_TANGENT; - } - else { - bm->haveMultiResSettings = false; - } + const bool is_new = !(bm->totvert || (bm->vdata.totlayer || bm->edata.totlayer || + bm->pdata.totlayer || bm->ldata.totlayer)); + KeyBlock *actkey; + float(*keyco)[3] = nullptr; + CustomData_MeshMasks mask = CD_MASK_BMESH; + CustomData_MeshMasks_update(&mask, ¶ms->cd_mask_extra); CustomData mesh_vdata = CustomData_shallow_copy_remove_non_bmesh_attributes(&me->vdata, mask.vmask); @@ -404,29 +254,21 @@ void BM_mesh_bm_from_me(Object *ob, CustomData_copy(&mesh_pdata, &bm->pdata, mask.pmask, CD_CONSTRUCT, 0); CustomData_copy(&mesh_ldata, &bm->ldata, mask.lmask, CD_CONSTRUCT, 0); - CustomData_bmesh_init_pool_ex(&bm->vdata, me->totvert, BM_VERT, __func__); - CustomData_bmesh_init_pool_ex(&bm->edata, me->totedge, BM_EDGE, __func__); - CustomData_bmesh_init_pool_ex(&bm->ldata, me->totloop, BM_LOOP, __func__); - CustomData_bmesh_init_pool_ex(&bm->pdata, me->totpoly, BM_FACE, __func__); + CustomData_bmesh_init_pool(&bm->vdata, me->totvert, BM_VERT); + CustomData_bmesh_init_pool(&bm->edata, me->totedge, BM_EDGE); + CustomData_bmesh_init_pool(&bm->ldata, me->totloop, BM_LOOP); + CustomData_bmesh_init_pool(&bm->pdata, me->totpoly, BM_FACE); } -#ifdef USE_BMESH_PAGE_CUSTOMDATA - bmesh_update_attr_refs(bm); -#endif - if (params->copy_temp_cdlayers) { bm_mark_temp_cdlayers(bm); } - if (bm->idmap.flag & BM_HAS_IDS) { - bm_init_idmap_cdlayers(bm); - } - if (bm->use_toolflags) { bm_alloc_toolflags_cdlayers(bm, true); } - return; /* Sanity check. */ + return; } const float(*vert_normals)[3] = nullptr; @@ -435,7 +277,6 @@ void BM_mesh_bm_from_me(Object *ob, } if (is_new) { - bm_free_cd_pools(bm); CustomData_copy(&mesh_vdata, &bm->vdata, mask.vmask, CD_SET_DEFAULT, 0); CustomData_copy(&mesh_edata, &bm->edata, mask.emask, CD_SET_DEFAULT, 0); CustomData_copy(&mesh_pdata, &bm->pdata, mask.pmask, CD_SET_DEFAULT, 0); @@ -554,48 +395,12 @@ void BM_mesh_bm_from_me(Object *ob, } if (is_new) { - CustomData_bmesh_init_pool_ex(&bm->vdata, me->totvert, BM_VERT, __func__); - CustomData_bmesh_init_pool_ex(&bm->edata, me->totedge, BM_EDGE, __func__); - CustomData_bmesh_init_pool_ex(&bm->ldata, me->totloop, BM_LOOP, __func__); - CustomData_bmesh_init_pool_ex(&bm->pdata, me->totpoly, BM_FACE, __func__); + CustomData_bmesh_init_pool(&bm->vdata, me->totvert, BM_VERT); + CustomData_bmesh_init_pool(&bm->edata, me->totedge, BM_EDGE); + CustomData_bmesh_init_pool(&bm->ldata, me->totloop, BM_LOOP); + CustomData_bmesh_init_pool(&bm->pdata, me->totpoly, BM_FACE); } -#define IS_GARBAGE_ID(id) ((id) < 0 || (id) > id_garbage_threshold) - - int *existing_id_layers[4] = {nullptr, nullptr, nullptr, nullptr}; - - /* threshold to detect garbage IDs, number of elements with ids multiplied by 5 */ - int id_garbage_threshold = 0; - - int use_exist_ids = 0; - int has_ids = bm->idmap.flag & BM_HAS_IDS ? - (bm->idmap.flag & (BM_VERT | BM_EDGE | BM_LOOP | BM_FACE)) : - 0; - if (bm->idmap.flag & BM_HAS_IDS) { - int tots[4] = {me->totvert + bm->totvert, - me->totedge + bm->totedge, - me->totloop + bm->totloop, - me->totpoly + bm->totface}; - - if (!params->ignore_id_layers) { - for (int i = 0; i < 4; i++) { - existing_id_layers[i] = (int *)CustomData_get_layer(cdatas[i], CD_MESH_ID); - - if (existing_id_layers[i]) { - id_garbage_threshold += tots[i]; - - use_exist_ids |= 1 << i; - } - } - } - - use_exist_ids &= bm->idmap.flag; - - bm_init_idmap_cdlayers(bm); - } - - id_garbage_threshold *= 5; - int *cd_shape_key_offset = static_cast( tot_shape_keys ? MEM_mallocN(sizeof(int) * tot_shape_keys, "cd_shape_key_offset") : nullptr); @@ -649,16 +454,7 @@ void BM_mesh_bm_from_me(Object *ob, /* Copy Custom Data */ CustomData_to_bmesh_block(&mesh_vdata, &bm->vdata, i, &v->head.data, true); - bm_elem_check_toolflags(bm, (BMElem *)v); - - if (has_ids & BM_VERT) { - if ((use_exist_ids & BM_VERT) && !IS_GARBAGE_ID(existing_id_layers[0][i])) { - bm_assign_id(bm, (BMElem *)v, existing_id_layers[0][i], false); - } - else { - bm_alloc_id(bm, (BMElem *)v); - } - } + bm_elem_check_toolflags(bm, reinterpret_cast(v)); /* Set shape key original index. */ if (cd_shape_keyindex_offset != -1) { @@ -668,9 +464,8 @@ void BM_mesh_bm_from_me(Object *ob, /* Set shape-key data. */ if (tot_shape_keys) { for (int j = 0; j < tot_shape_keys; j++) { - float(*co_dst)[3] = static_cast( - BM_ELEM_CD_GET_VOID_P(v, cd_shape_key_offset[j])); - copy_v3_v3(co_dst[0], shape_key_table[j][i]); + float3 *co_dest = BM_ELEM_CD_PTR(v, cd_shape_key_offset[j]); + copy_v3_v3(*co_dest, shape_key_table[j][i]); } } } @@ -699,17 +494,7 @@ void BM_mesh_bm_from_me(Object *ob, /* Copy Custom Data */ CustomData_to_bmesh_block(&mesh_edata, &bm->edata, i, &e->head.data, true); - - bm_elem_check_toolflags(bm, (BMElem *)e); - - if (has_ids & BM_EDGE) { - if ((use_exist_ids & BM_EDGE) && !IS_GARBAGE_ID(existing_id_layers[1][i])) { - bm_assign_id(bm, (BMElem *)e, existing_id_layers[1][i], false); - } - else { - bm_alloc_id(bm, (BMElem *)e); - } - } + bm_elem_check_toolflags(bm, reinterpret_cast(e)); } if (is_new) { bm->elem_index_dirty &= ~BM_EDGE; /* Added in order, clear dirty flag. */ @@ -769,30 +554,11 @@ void BM_mesh_bm_from_me(Object *ob, /* Save index of corresponding #MLoop. */ CustomData_to_bmesh_block(&mesh_ldata, &bm->ldata, j++, &l_iter->head.data, true); - - if (has_ids & BM_LOOP) { - if ((use_exist_ids & BM_LOOP) && !IS_GARBAGE_ID(existing_id_layers[2][j - 1])) { - bm_assign_id(bm, (BMElem *)l_iter, existing_id_layers[2][j - 1], false); - } - else { - bm_alloc_id(bm, (BMElem *)l_iter); - } - } } while ((l_iter = l_iter->next) != l_first); /* Copy Custom Data */ CustomData_to_bmesh_block(&mesh_pdata, &bm->pdata, i, &f->head.data, true); - - bm_elem_check_toolflags(bm, (BMElem *)f); - - if (has_ids & BM_FACE) { - if ((use_exist_ids & BM_FACE) && !IS_GARBAGE_ID(existing_id_layers[3][i])) { - bm_assign_id(bm, (BMElem *)f, existing_id_layers[3][i], false); - } - else { - bm_alloc_id(bm, (BMElem *)f); - } - } + bm_elem_check_toolflags(bm, reinterpret_cast(f)); if (params->calc_face_normal) { BM_face_normal_update(f); @@ -802,175 +568,6 @@ void BM_mesh_bm_from_me(Object *ob, bm->elem_index_dirty &= ~(BM_FACE | BM_LOOP); /* Added in order, clear dirty flag. */ } - if (check_id_unqiue) { - bm_update_idmap_cdlayers(bm); - - // validate IDs - - // first clear idmap, we want it to have the first elements - // in each id run, not the last - if (bm->idmap.map) { - memset(bm->idmap.map, 0, sizeof(void *) * bm->idmap.map_size); - } - else if (bm->idmap.ghash) { - BLI_ghash_free(bm->idmap.ghash, nullptr, nullptr); - bm->idmap.ghash = BLI_ghash_ptr_new("bm->idmap.ghash"); - } - - int iters[] = {BM_VERTS_OF_MESH, BM_EDGES_OF_MESH, -1, BM_FACES_OF_MESH}; - -#ifdef WITH_BM_ID_FREELIST - uint max_id = 0; -#endif - - // find first element in each id run and assign to map - for (int i = 0; i < 4; i++) { - int type = 1 << i; - int iter = iters[i]; - - if (!(bm->idmap.flag & type)) { - continue; - } - - if (iter == -1) { - iter = BM_FACES_OF_MESH; - } - - BMElem *elem; - BMIter iterstate; - BM_ITER_MESH (elem, &iterstate, bm, iter) { - if (i == 2) { // loops - BMFace *f = (BMFace *)elem; - BMLoop *l = f->l_first; - - do { - uint id = (uint)BM_ELEM_GET_ID(bm, (BMElem *)l); -#ifdef WITH_BM_ID_FREELIST - max_id = MAX2(max_id, i); -#endif - if (!BM_ELEM_FROM_ID(bm, id)) { - bm_assign_id_intern(bm, (BMElem *)l, id); - } - } while ((l = l->next) != f->l_first); - } - else { - uint id = (uint)BM_ELEM_GET_ID(bm, elem); -#ifdef WITH_BM_ID_FREELIST - max_id = MAX2(max_id, i); -#endif - - if (!BM_ELEM_FROM_ID(bm, id)) { - bm_assign_id_intern(bm, elem, id); - } - } - } - } - - // now assign new IDs where necessary - - for (int i = 0; i < 4; i++) { - int type = 1 << i; - int iter = iters[i]; - - if (!(bm->idmap.flag & type)) { - continue; - } - - if (iter == -1) { - iter = BM_FACES_OF_MESH; - } - - BMElem *elem; - BMIter iterstate; - - BM_ITER_MESH (elem, &iterstate, bm, iter) { - if (i == 2) { // loops - BMFace *f = (BMFace *)elem; - BMLoop *l = f->l_first; - - do { - uint id = (uint)BM_ELEM_GET_ID(bm, (BMElem *)l); - - if (BM_ELEM_FROM_ID(bm, id) != (BMElem *)l) { - bm_alloc_id(bm, (BMElem *)l); - } - } while ((l = l->next) != f->l_first); - } - else { - uint id = (uint)BM_ELEM_GET_ID(bm, elem); - - if (BM_ELEM_FROM_ID(bm, id) != elem) { - bm_alloc_id(bm, elem); - - id = (uint)BM_ELEM_GET_ID(bm, elem); -#ifdef WITH_BM_ID_FREELIST - max_id = MAX2(max_id, id); -#endif - } - } - } - } - -#ifdef WITH_BM_ID_FREELIST - max_id = MAX2(bm->idmap.maxid, max_id); - bm->idmap.maxid = max_id; -#endif - } - -#ifdef WITH_BM_ID_FREELIST - /*ensure correct id freelist*/ - if (bm->idmap.flag & BM_HAS_IDS) { - bm_update_idmap_cdlayers(bm); - bm_free_ids_check(bm, bm->idmap.maxid); - - MEM_SAFE_FREE(bm->idmap.freelist); - bm->idmap.freelist_len = 0; - bm->idmap.freelist_size = 0; - bm->idmap.freelist = nullptr; - - memset(bm->idmap.free_ids, 0, bm->idmap.free_ids_size * sizeof(*bm->idmap.free_ids)); - - BLI_mempool_iter miter; - for (int i = 0; i < 4; i++) { - int htype = 1 << i; - - if (!(bm->idmap.flag & htype)) { - continue; - } - - BLI_mempool *pool = (&bm->vpool)[i]; - BLI_mempool_iternew(pool, &miter); - BMElem *elem = (BMElem *)BLI_mempool_iterstep(&miter); - -# if 0 - for (; elem; elem = (BMElem *)BLI_mempool_iterstep(&miter)) { - uint id = (uint)BM_ELEM_GET_ID(bm, elem); - - if (id > bm->idmap.maxid) { - printf("%s: corrupted id: %d > maxid(%d)\n", __func__, (int)id, (int)bm->idmap.maxid); - //BM_ELEM_CD_SET_INT(elem, bm->idmap.cd_id_off[elem->head.htype], 0); - bm_alloc_id(bm, elem); - } - } -# endif - - for (; elem; elem = (BMElem *)BLI_mempool_iterstep(&miter)) { - uint id = (uint)BM_ELEM_GET_ID(bm, elem); - - if ((id >> 2UL) < bm->idmap.free_ids_size) { - BLI_BITMAP_SET(bm->idmap.free_ids, id, true); - } - } - } - - for (uint i = 0; i < bm->idmap.maxid; i++) { - if (!BLI_BITMAP_TEST(bm->idmap.free_ids, i)) { - bm_id_freelist_push(bm, i); - } - } - } -#endif - /* -------------------------------------------------------------------- */ /* MSelect clears the array elements (to avoid adding multiple times). * @@ -1010,26 +607,13 @@ void BM_mesh_bm_from_me(Object *ob, bm_mark_temp_cdlayers(bm); } - if (bm->idmap.flag & BM_HAS_IDS) { - CustomData *cdatas[] = {&bm->vdata, &bm->edata, &bm->ldata, &bm->pdata}; - - for (int i = 0; i < 4; i++) { - int idx = CustomData_get_layer_index(cdatas[i], CD_MESH_ID); - - if (idx >= 0) { - // set layer flags - cdatas[i]->layers[idx].flag |= CD_FLAG_TEMPORARY | CD_FLAG_ELEM_NOCOPY; - } - } - } - MEM_SAFE_FREE(cd_shape_key_offset); } /** * \brief BMesh -> Mesh */ -BMVert **bm_to_mesh_vertex_map(BMesh *bm, int ototvert) +static BMVert **bm_to_mesh_vertex_map(BMesh *bm, int ototvert) { const int cd_shape_keyindex_offset = CustomData_get_offset(&bm->vdata, CD_SHAPE_KEYINDEX); BMVert **vertMap = nullptr; @@ -1152,7 +736,7 @@ BMVert **bm_to_mesh_vertex_map(BMesh *bm, int ototvert) * Returns custom-data shape-key index from a key-block or -1 * \note could split this out into a more generic function. */ -int bm_to_mesh_shape_layer_index_from_kb(BMesh *bm, KeyBlock *currkey) +static int bm_to_mesh_shape_layer_index_from_kb(BMesh *bm, KeyBlock *currkey) { int i; int j = 0; @@ -1482,8 +1066,7 @@ static void convert_bmesh_selection_flags_to_mesh_attributes(BMesh &bm, } } -void BM_mesh_bm_to_me( - Main *bmain, Object *ob, BMesh *bm, Mesh *me, const struct BMeshToMeshParams *params) +void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *me, const struct BMeshToMeshParams *params) { using namespace blender; BMVert *v, *eve; @@ -1492,40 +1075,14 @@ void BM_mesh_bm_to_me( BMIter iter; int i, j; - CustomData *srcdatas[] = {&bm->vdata, &bm->edata, &bm->ldata, &bm->pdata}; - - if (params->copy_temp_cdlayers) { - bm_unmark_temp_cdlayers(bm); - } - - // ensure multires space is correct - if (bm->haveMultiResSettings && bm->multiresSpace != MULTIRES_SPACE_TANGENT) { - BM_enter_multires_space(ob, bm, MULTIRES_SPACE_TANGENT); - } - const int cd_shape_keyindex_offset = CustomData_get_offset(&bm->vdata, CD_SHAPE_KEYINDEX); const int ototvert = me->totvert; - if (me->key && (cd_shape_keyindex_offset != -1)) { - /* Keep the old verts in case we are working on* a key, which is done at the end. */ - - /* Use the array in-place instead of duplicating the array. */ -#if 0 -# if 0 - oldverts = MEM_dupallocN(me->verts().data()); -# else - oldverts = me->mvert; - me->mvert = nullptr; - CustomData_update_typemap(&me->vdata); - CustomData_set_layer(&me->vdata, CD_MVERT, nullptr); -# endif -#endif + if (params->copy_temp_cdlayers) { + bm_unmark_temp_cdlayers(bm); } - // undo mesh? - // bool non_id_mesh = GS(me->id.name) != ID_ME; - /* Free custom data. */ CustomData_free(&me->vdata, me->totvert); CustomData_free(&me->edata, me->totedge); @@ -1545,7 +1102,6 @@ void BM_mesh_bm_to_me( me->totface = 0; me->act_face = -1; - int id_flags[4] = {-1, -1, -1, -1}; /* Mark UV selection layers which are all false as 'nocopy'. */ for (const int layer_index : IndexRange(CustomData_number_of_layers(&bm->ldata, CD_PROP_FLOAT2))) { @@ -1597,29 +1153,14 @@ void BM_mesh_bm_to_me( { CustomData_MeshMasks mask = CD_MASK_MESH; CustomData_MeshMasks_update(&mask, ¶ms->cd_mask_extra); - - /* Copy id layers? temporarily clear cd_temporary and cd_flag_elem_nocopy flags. */ - if (!params->ignore_mesh_id_layers) { - for (int i = 0; i < 4; i++) { - int idx = CustomData_get_layer_index(srcdatas[i], CD_MESH_ID); - - if (idx >= 0) { - id_flags[i] = srcdatas[i]->layers[idx].flag; - srcdatas[i]->layers[idx].flag &= ~(CD_FLAG_TEMPORARY | CD_FLAG_ELEM_NOCOPY); - } - } - } - CustomData_copy(&bm->vdata, &me->vdata, mask.vmask, CD_SET_DEFAULT, me->totvert); CustomData_copy(&bm->edata, &me->edata, mask.emask, CD_SET_DEFAULT, me->totedge); CustomData_copy(&bm->ldata, &me->ldata, mask.lmask, CD_SET_DEFAULT, me->totloop); CustomData_copy(&bm->pdata, &me->pdata, mask.pmask, CD_SET_DEFAULT, me->totpoly); } - if (CustomData_get_named_layer_index(&me->vdata, CD_PROP_FLOAT3, "position") == -1) { - CustomData_add_layer_named( - &me->vdata, CD_PROP_FLOAT3, CD_CONSTRUCT, nullptr, me->totvert, "position"); - } + CustomData_add_layer_named( + &me->vdata, CD_PROP_FLOAT3, CD_CONSTRUCT, nullptr, me->totvert, "position"); CustomData_add_layer(&me->edata, CD_MEDGE, CD_SET_DEFAULT, nullptr, me->totedge); CustomData_add_layer(&me->ldata, CD_MLOOP, CD_SET_DEFAULT, nullptr, me->totloop); CustomData_add_layer(&me->pdata, CD_MPOLY, CD_SET_DEFAULT, nullptr, me->totpoly); @@ -1639,6 +1180,8 @@ void BM_mesh_bm_to_me( i = 0; BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) { + copy_v3_v3(positions[i], v->co); + if (BM_elem_flag_test(v, BM_ELEM_HIDDEN)) { need_hide_vert = true; } @@ -1651,8 +1194,6 @@ void BM_mesh_bm_to_me( /* Copy over custom-data. */ CustomData_from_bmesh_block(&bm->vdata, &me->vdata, v->head.data, i); - copy_v3_v3(positions[i], v->co); - i++; BM_CHECK_ELEMENT(v); @@ -1802,7 +1343,7 @@ void BM_mesh_bm_to_me( } if (vertMap) { - MEM_freeN(static_cast(vertMap)); + MEM_freeN(vertMap); } } @@ -1853,16 +1394,6 @@ void BM_mesh_bm_to_me( /* Topology could be changed, ensure #CD_MDISPS are ok. */ multires_topology_changed(me); - // restore original cd layer flags to bmesh id layers - if (!params->ignore_mesh_id_layers) { - for (int i = 0; i < 4; i++) { - int idx = CustomData_get_layer_index(srcdatas[i], CD_MESH_ID); - if (idx >= 0) { - srcdatas[i]->layers[idx].flag = id_flags[i]; - } - } - } - if (params && params->copy_temp_cdlayers) { bm_mark_temp_cdlayers(bm); } @@ -2083,7 +1614,6 @@ void BM_mesh_bm_to_me_for_eval(BMesh *bm, Mesh *me, const CustomData_MeshMasks * CustomData_MeshMasks_update(&mask, cd_mask_extra); } mask.vmask &= ~CD_MASK_SHAPEKEY; - CustomData_merge(&bm->vdata, &me->vdata, mask.vmask, CD_CONSTRUCT, me->totvert); CustomData_merge(&bm->edata, &me->edata, mask.emask, CD_CONSTRUCT, me->totedge); CustomData_merge(&bm->ldata, &me->ldata, mask.lmask, CD_CONSTRUCT, me->totloop); diff --git a/source/blender/bmesh/intern/bmesh_mesh_convert.h b/source/blender/bmesh/intern/bmesh_mesh_convert.h index c98abf35d58..4ca589279f5 100644 --- a/source/blender/bmesh/intern/bmesh_mesh_convert.h +++ b/source/blender/bmesh/intern/bmesh_mesh_convert.h @@ -56,10 +56,8 @@ struct Object; * * \warning This function doesn't calculate face normals. */ -void BM_mesh_bm_from_me(struct Object *ob, - BMesh *bm, - const struct Mesh *me, - const struct BMeshFromMeshParams *params) ATTR_NONNULL(2, 4); +void BM_mesh_bm_from_me(BMesh *bm, const struct Mesh *me, const struct BMeshFromMeshParams *params) + ATTR_NONNULL(2, 4); struct BMeshToMeshParams { /** Update object hook indices & vertex parents. */ @@ -90,7 +88,6 @@ void BM_enter_multires_space(struct Object *ob, struct BMesh *bm, int space); * \param bmain: May be NULL in case \a calc_object_remap parameter option is not set. */ void BM_mesh_bm_to_me(struct Main *bmain, - struct Object *ob, BMesh *bm, struct Mesh *me, const struct BMeshToMeshParams *params) ATTR_NONNULL(3, 4, 5); diff --git a/source/blender/bmesh/intern/bmesh_opdefines.c b/source/blender/bmesh/intern/bmesh_opdefines.c index 062248ac3cd..89f2d1e1015 100644 --- a/source/blender/bmesh/intern/bmesh_opdefines.c +++ b/source/blender/bmesh/intern/bmesh_opdefines.c @@ -1068,21 +1068,6 @@ static BMOpDefine bmo_extrude_face_region_def = { (BMO_OPTYPE_FLAG_NORMALS_CALC), }; -extern void bmo_test_mres_smooth_exec(BMesh *bm, BMOperator *op); - -static BMOpDefine bmo_test_mres_smooth_def = { - "test_mres_smooth", - /* slots_in */ - {{{'\0'}}}, /* no input */ - {{{'\0'}}}, /* no output */ - bmo_test_mres_smooth_exec, - ( - BMO_OPTYPE_FLAG_UNTAN_MULTIRES | - BMO_OPTYPE_FLAG_NORMALS_CALC | - BMO_OPTYPE_FLAG_SELECT_FLUSH | - BMO_OPTYPE_FLAG_SELECT_VALIDATE) -}; - /* * Dissolve Verts. */ @@ -2201,7 +2186,6 @@ const BMOpDefine *bmo_opdefines[] = { &bmo_unsubdivide_def, &bmo_weld_verts_def, &bmo_wireframe_def, - &bmo_test_mres_smooth_def, }; const int bmo_opdefines_total = ARRAY_SIZE(bmo_opdefines); diff --git a/source/blender/bmesh/operators/bmo_mesh_convert.c b/source/blender/bmesh/operators/bmo_mesh_convert.c index 1e27b9f54d4..7cbbba80bb7 100644 --- a/source/blender/bmesh/operators/bmo_mesh_convert.c +++ b/source/blender/bmesh/operators/bmo_mesh_convert.c @@ -25,8 +25,7 @@ void bmo_mesh_to_bmesh_exec(BMesh *bm, BMOperator *op) Mesh *me = BMO_slot_ptr_get(op->slots_in, "mesh"); bool set_key = BMO_slot_bool_get(op->slots_in, "use_shapekey"); - BM_mesh_bm_from_me(NULL, - bm, + BM_mesh_bm_from_me(bm, me, (&(struct BMeshFromMeshParams){ .use_shapekey = set_key, @@ -53,7 +52,6 @@ void bmo_bmesh_to_mesh_exec(BMesh *bm, BMOperator *op) /* Object *ob = BMO_slot_ptr_get(op, "object"); */ BM_mesh_bm_to_me(G.main, - NULL, bm, me, (&(struct BMeshToMeshParams){ diff --git a/source/blender/editors/mesh/editmesh_mask_extract.cc b/source/blender/editors/mesh/editmesh_mask_extract.cc index 5c236f8bafe..9d72787e4df 100644 --- a/source/blender/editors/mesh/editmesh_mask_extract.cc +++ b/source/blender/editors/mesh/editmesh_mask_extract.cc @@ -108,7 +108,7 @@ static int geometry_extract_apply(bContext *C, BMeshFromMeshParams mesh_to_bm_params{}; mesh_to_bm_params.calc_face_normal = true; mesh_to_bm_params.calc_vert_normal = true; - BM_mesh_bm_from_me(nullptr, bm, new_mesh, &mesh_to_bm_params); + BM_mesh_bm_from_me(bm, new_mesh, &mesh_to_bm_params); BMEditMesh *em = BKE_editmesh_create(bm); @@ -514,7 +514,7 @@ static int paint_mask_slice_exec(bContext *C, wmOperator *op) else { bm = BM_mesh_create(&allocsize, &bm_create_params); - BM_mesh_bm_from_me(nullptr, bm, new_mesh, &mesh_to_bm_params); + BM_mesh_bm_from_me(bm, new_mesh, &mesh_to_bm_params); } slice_paint_mask( @@ -543,7 +543,7 @@ static int paint_mask_slice_exec(bContext *C, wmOperator *op) const BMAllocTemplate allocsize_new_ob = BMALLOC_TEMPLATE_FROM_ME(new_ob_mesh); bm = BM_mesh_create(&allocsize_new_ob, &bm_create_params); - BM_mesh_bm_from_me(nullptr, bm, new_ob_mesh, &mesh_to_bm_params); + BM_mesh_bm_from_me(bm, new_ob_mesh, &mesh_to_bm_params); slice_paint_mask(bm, true, diff --git a/source/blender/editors/mesh/editmesh_tools.c b/source/blender/editors/mesh/editmesh_tools.c index 90776ce004d..63a2aca514e 100644 --- a/source/blender/editors/mesh/editmesh_tools.c +++ b/source/blender/editors/mesh/editmesh_tools.c @@ -4472,8 +4472,7 @@ static Base *mesh_separate_tagged( BM_mesh_normals_update(bm_new); - BM_mesh_bm_to_me( - bmain, base_new->object, bm_new, base_new->object->data, (&(struct BMeshToMeshParams){0})); + BM_mesh_bm_to_me(bmain, bm_new, base_new->object->data, (&(struct BMeshToMeshParams){0})); BM_mesh_free(bm_new); ((Mesh *)base_new->object->data)->edit_mesh = NULL; @@ -4539,8 +4538,7 @@ static Base *mesh_separate_arrays(Main *bmain, BM_vert_kill(bm_old, verts[i]); } - BM_mesh_bm_to_me( - bmain, base_new->object, bm_new, base_new->object->data, (&(struct BMeshToMeshParams){0})); + BM_mesh_bm_to_me(bmain, bm_new, base_new->object->data, (&(struct BMeshToMeshParams){0})); BM_mesh_free(bm_new); ((Mesh *)base_new->object->data)->edit_mesh = NULL; @@ -4723,7 +4721,6 @@ static bool mesh_separate_loose( if (clear_object_data) { BM_mesh_bm_to_me(NULL, - base_old->object, bm_old, me_old, (&(struct BMeshToMeshParams){ @@ -4817,7 +4814,7 @@ static int edbm_separate_exec(bContext *C, wmOperator *op) .use_toolflags = true, })); - BM_mesh_bm_from_me(NULL, bm_old, me, (&(struct BMeshFromMeshParams){0})); + BM_mesh_bm_from_me(bm_old, me, (&(struct BMeshFromMeshParams){0})); switch (type) { case MESH_SEPARATE_MATERIAL: @@ -4833,7 +4830,6 @@ static int edbm_separate_exec(bContext *C, wmOperator *op) if (changed) { BM_mesh_bm_to_me(bmain, - ob, bm_old, me, (&(struct BMeshToMeshParams){ @@ -6031,116 +6027,6 @@ static void edbm_dissolve_prop__use_boundary_tear(wmOperatorType *ot) "Split off face corners instead of merging faces"); } -static int edbm_mres_test_exec(bContext *C, wmOperator *op) -{ - const bool use_face_split = RNA_boolean_get(op->ptr, "use_face_split"); - const bool use_boundary_tear = RNA_boolean_get(op->ptr, "use_boundary_tear"); - - ViewLayer *view_layer = CTX_data_view_layer(C); - uint objects_len = 0; - Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data( - CTX_data_scene(C), view_layer, CTX_wm_view3d(C), &objects_len); - - for (uint ob_index = 0; ob_index < objects_len; ob_index++) { - Object *obedit = objects[ob_index]; - BMEditMesh *em = BKE_editmesh_from_object(obedit); - - if (em->bm->totvertsel == 0) { - continue; - } - - BM_custom_loop_normals_to_vector_layer(em->bm); - - if (!EDBM_op_callf(em, op, "test_mres_smooth")) { - continue; - } - - BM_custom_loop_normals_from_vector_layer(em->bm, false); - - EDBM_update(obedit->data, - &(const struct EDBMUpdate_Params){ - .calc_looptri = true, - .calc_normals = false, - .is_destructive = true, - }); - // EDBM_update_generic(obedit->data, true, true); - } - - MEM_freeN(objects); - return OPERATOR_FINISHED; -} - -extern Object *multires_dump_grids_bmesh(Object *bmob, BMesh *bm); - -static int edbm_dump_mres_grids_exec(bContext *C, wmOperator *op) -{ - const bool use_face_split = RNA_boolean_get(op->ptr, "use_face_split"); - const bool use_boundary_tear = RNA_boolean_get(op->ptr, "use_boundary_tear"); - - ViewLayer *view_layer = CTX_data_view_layer(C); - uint objects_len = 0; - Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data( - CTX_data_scene(C), view_layer, CTX_wm_view3d(C), &objects_len); - - for (uint ob_index = 0; ob_index < objects_len; ob_index++) { - Object *obedit = objects[ob_index]; - BMEditMesh *em = BKE_editmesh_from_object(obedit); - - multires_dump_grids_bmesh(obedit, em->bm); - } - - WM_event_add_notifier(C, NC_SCENE | ND_LAYER_CONTENT, CTX_data_scene(C)); - - MEM_freeN(objects); - return OPERATOR_FINISHED; -} - -static bool mres_test_poll(bContext *C) -{ - Object *obedit = CTX_data_edit_object(C); - if (obedit && obedit->type == OB_MESH) { - BMEditMesh *em = BKE_editmesh_from_object(obedit); - - if (!em || !CustomData_has_layer(&em->bm->ldata, CD_MDISPS)) { - return false; - } - - return true; - } - - return false; -} - -void MESH_OT_dump_mres_grids(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Dump Multires Grids"; - ot->description = "Dump Multires Grids"; - ot->idname = "MESH_OT_dump_mres_grids"; - - /* api callbacks */ - ot->exec = edbm_dump_mres_grids_exec; - ot->poll = mres_test_poll; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; -} - -void MESH_OT_mres_test(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Test Multires Boundary Smooth"; - ot->description = "Test multires boundary smooth"; - ot->idname = "MESH_OT_mres_test"; - - /* api callbacks */ - ot->exec = edbm_mres_test_exec; - ot->poll = mres_test_poll; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; -} - static int edbm_dissolve_verts_exec(bContext *C, wmOperator *op) { const bool use_face_split = RNA_boolean_get(op->ptr, "use_face_split"); @@ -7224,7 +7110,7 @@ static void sort_bmelem_flag(bContext *C, } } - BM_mesh_remap(em->bm, map[0], map[1], map[2], NULL); + BM_mesh_remap(em->bm, map[0], map[1], NULL, map[2]); EDBM_update(ob->data, &(const struct EDBMUpdate_Params){ diff --git a/source/blender/editors/mesh/editmesh_undo.cc b/source/blender/editors/mesh/editmesh_undo.cc index 17993273b89..565ce28a3c1 100644 --- a/source/blender/editors/mesh/editmesh_undo.cc +++ b/source/blender/editors/mesh/editmesh_undo.cc @@ -610,7 +610,7 @@ static void *undomesh_from_editmesh(UndoMesh *um, BMEditMesh *em, Key *key, Undo params.update_shapekey_indices = false; params.cd_mask_extra = cd_mask_extra; params.active_shapekey_to_mvert = true; - BM_mesh_bm_to_me(nullptr, nullptr, em->bm, &um->me, ¶ms); + BM_mesh_bm_to_me(nullptr, em->bm, &um->me, ¶ms); um->selectmode = em->selectmode; um->shapenr = em->bm->shapenr; @@ -678,7 +678,7 @@ static void undomesh_to_editmesh(UndoMesh *um, Object *ob, BMEditMesh *em) convert_params.calc_face_normal = false; convert_params.calc_vert_normal = false; convert_params.active_shapekey = um->shapenr; - BM_mesh_bm_from_me(nullptr, bm, &um->me, &convert_params); + BM_mesh_bm_from_me(bm, &um->me, &convert_params); em_tmp = BKE_editmesh_create(bm); *em = *em_tmp; diff --git a/source/blender/editors/mesh/editmesh_utils.c b/source/blender/editors/mesh/editmesh_utils.c index deee7e29c08..01df41b11a3 100644 --- a/source/blender/editors/mesh/editmesh_utils.c +++ b/source/blender/editors/mesh/editmesh_utils.c @@ -306,7 +306,6 @@ void EDBM_mesh_load_ex(Main *bmain, Object *ob, bool free_data) #endif BM_mesh_bm_to_me(bmain, - ob, bm, me, (&(struct BMeshToMeshParams){ diff --git a/source/blender/editors/mesh/mesh_ops.c b/source/blender/editors/mesh/mesh_ops.c index d3df744b22f..a1bb76b9e1a 100644 --- a/source/blender/editors/mesh/mesh_ops.c +++ b/source/blender/editors/mesh/mesh_ops.c @@ -196,8 +196,6 @@ void ED_operatortypes_mesh(void) WM_operatortype_append(MESH_OT_average_normals); WM_operatortype_append(MESH_OT_smooth_normals); WM_operatortype_append(MESH_OT_mod_weighted_strength); - WM_operatortype_append(MESH_OT_mres_test); - WM_operatortype_append(MESH_OT_dump_mres_grids); WM_operatortype_append(MESH_OT_flip_quad_tessellation); } diff --git a/source/blender/editors/sculpt_paint/paint_mask.cc b/source/blender/editors/sculpt_paint/paint_mask.cc index cc0db5d0827..7eb32480c54 100644 --- a/source/blender/editors/sculpt_paint/paint_mask.cc +++ b/source/blender/editors/sculpt_paint/paint_mask.cc @@ -1024,7 +1024,7 @@ static void sculpt_gesture_trim_normals_update(SculptGestureContext *sgcontext) BMeshFromMeshParams bm_from_me_params{}; bm_from_me_params.calc_face_normal = true; bm_from_me_params.calc_vert_normal = true; - BM_mesh_bm_from_me(nullptr, bm, trim_mesh, &bm_from_me_params); + BM_mesh_bm_from_me(bm, trim_mesh, &bm_from_me_params); BM_mesh_elem_hflag_enable_all(bm, BM_FACE, BM_ELEM_TAG, false); BMO_op_callf(bm, @@ -1391,7 +1391,7 @@ static void sculpt_gesture_apply_trim(SculptGestureContext *sgcontext) bm = BM_mesh_create(&allocsize, ¶ms); - BM_mesh_bm_from_me(nullptr, bm, sculpt_mesh, ¶ms2); + BM_mesh_bm_from_me(bm, sculpt_mesh, ¶ms2); } BMeshCreateParams params1 = {0}; @@ -1404,7 +1404,7 @@ static void sculpt_gesture_apply_trim(SculptGestureContext *sgcontext) BMeshFromMeshParams params3 = {0}; params3.calc_face_normal = params3.calc_vert_normal = true; - BM_mesh_bm_from_me(nullptr, trimbm, trim_mesh, ¶ms3); + BM_mesh_bm_from_me(trimbm, trim_mesh, ¶ms3); BM_mesh_normals_update(bm); @@ -1429,7 +1429,7 @@ static void sculpt_gesture_apply_trim(SculptGestureContext *sgcontext) BMeshFromMeshParams params2 = {0}; params2.calc_face_normal = params2.calc_vert_normal = true; - BM_mesh_bm_from_me(nullptr, bm, sculpt_mesh, ¶ms2); + BM_mesh_bm_from_me(bm, sculpt_mesh, ¶ms2); const int looptris_tot = poly_to_tri_count(bm->totface, bm->totloop); BMLoop *(*looptris)[3] = static_cast( diff --git a/source/blender/editors/sculpt_paint/sculpt_boundary.cc b/source/blender/editors/sculpt_paint/sculpt_boundary.cc index 8acac32495e..97de636eda5 100644 --- a/source/blender/editors/sculpt_paint/sculpt_boundary.cc +++ b/source/blender/editors/sculpt_paint/sculpt_boundary.cc @@ -1222,7 +1222,7 @@ void sculpt_end_vis_object(bContext *C, SculptSession *ss, Object *ob, BMesh *bm params.update_shapekey_indices = false; params.copy_temp_cdlayers = false; - BM_mesh_bm_to_me(bmain, nullptr, bm, me, ¶ms); + BM_mesh_bm_to_me(bmain, bm, me, ¶ms); DEG_id_tag_update(&me->id, ID_RECALC_ALL); } diff --git a/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc b/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc index 7689794efe5..97d2e258206 100644 --- a/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc +++ b/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc @@ -15,12 +15,12 @@ #include "BLI_blenlib.h" #include "BLI_compiler_attrs.h" #include "BLI_hash.h" +#include "BLI_index_range.hh" #include "BLI_linklist.h" #include "BLI_math.h" #include "BLI_memarena.h" #include "BLI_polyfill_2d.h" #include "BLI_task.h" -#include "BLI_index_range.hh" #include "BLT_translation.h" @@ -64,8 +64,8 @@ #include #include -using blender::Vector; using blender::IndexRange; +using blender::Vector; BMesh *SCULPT_dyntopo_empty_bmesh() { @@ -504,11 +504,6 @@ void SCULPT_dynamic_topology_sync_layers(Object *ob, Mesh *me) BKE_sculptsession_sync_attributes(ob, me); } -BMesh *BM_mesh_bm_from_me_threaded(BMesh *bm, - Object *ob, - const Mesh *me, - const struct BMeshFromMeshParams *params); - static void customdata_strip_templayers(CustomData *cdata, int totelem) { for (int i = 0; i < cdata->totlayer; i++) { @@ -605,8 +600,6 @@ void SCULPT_dynamic_topology_enable_ex(Main *bmain, Depsgraph *depsgraph, Scene /* Dynamic topology doesn't ensure selection state is valid, so remove T36280. */ BKE_mesh_mselect_clear(me); -#if 1 - if (!ss->bm) { ss->bm = BKE_sculptsession_empty_bmesh_create(); @@ -616,23 +609,13 @@ void SCULPT_dynamic_topology_enable_ex(Main *bmain, Depsgraph *depsgraph, Scene params.create_shapekey_layers = true; params.active_shapekey = ob->shapenr; - BM_mesh_bm_from_me(nullptr, ss->bm, me, ¶ms); + BM_mesh_bm_from_me(ss->bm, me, ¶ms); if (ss->pbvh) { BKE_sculptsession_update_attr_refs(ob); BKE_pbvh_set_bmesh(ss->pbvh, ss->bm); } } -#else - ss->bm = BM_mesh_bm_from_me_threaded(nullptr, - nullptr, - me, - (&(struct BMeshFromMeshParams){ - .calc_face_normal = true, - .use_shapekey = true, - .active_shapekey = ob->shapenr, - })); -#endif #ifndef DYNTOPO_DYNAMIC_TESS SCULPT_dynamic_topology_triangulate(ss, ss->bm); diff --git a/source/blender/editors/sculpt_paint/sculpt_face_set.cc b/source/blender/editors/sculpt_paint/sculpt_face_set.cc index 3f3818be475..74efb1fe744 100644 --- a/source/blender/editors/sculpt_paint/sculpt_face_set.cc +++ b/source/blender/editors/sculpt_paint/sculpt_face_set.cc @@ -210,7 +210,7 @@ static BMesh *sculpt_faceset_bm_begin(Object *ob, SculptSession *ss, Mesh *mesh) cparams.use_shapekey = true; cparams.create_shapekey_layers = true; - BM_mesh_bm_from_me(NULL, bm, mesh, &cparams); + BM_mesh_bm_from_me(bm, mesh, &cparams); return bm; } @@ -740,7 +740,7 @@ static EnumPropertyItem prop_sculpt_face_set_create_types[] = { "Face Set from Edit Mode Selection", "Create an Face Set corresponding to the Edit Mode face selection", }, - {0, NULL, 0, NULL, NULL}, + {0, nullptr, 0, nullptr, nullptr}, }; static int sculpt_face_set_create_exec(bContext *C, wmOperator *op) @@ -763,7 +763,7 @@ static int sculpt_face_set_create_exec(bContext *C, wmOperator *op) PBVH *pbvh = ob->sculpt->pbvh; PBVHNode **nodes; int totnode; - BKE_pbvh_search_gather(pbvh, NULL, NULL, &nodes, &totnode); + BKE_pbvh_search_gather(pbvh, nullptr, nullptr, &nodes, &totnode); if (!nodes) { return OPERATOR_CANCELLED; @@ -953,7 +953,7 @@ static EnumPropertyItem prop_sculpt_face_sets_init_types[] = { "Face Sets from Face Set Boundaries", "Create a Face Set per isolated Face Set", }, - {0, NULL, 0, NULL, NULL}, + {0, nullptr, 0, nullptr, nullptr}, }; typedef bool (*face_sets_flood_fill_test)( @@ -1115,8 +1115,8 @@ static void sculpt_face_sets_init_loop(Object *ob, const int mode) cd_fmaps_offset = CustomData_get_offset(&ss->bm->pdata, CD_FACEMAP); } - Mesh *me = NULL; - int *fmaps = NULL; + Mesh *me = nullptr; + int *fmaps = nullptr; if (BKE_pbvh_type(ss->pbvh) == PBVH_GRIDS) { me = (Mesh *)ob->data; @@ -1178,7 +1178,7 @@ static int sculpt_face_set_init_exec(bContext *C, wmOperator *op) PBVH *pbvh = ob->sculpt->pbvh; PBVHNode **nodes; int totnode; - BKE_pbvh_search_gather(pbvh, NULL, NULL, &nodes, &totnode); + BKE_pbvh_search_gather(pbvh, nullptr, nullptr, &nodes, &totnode); if (!nodes) { return OPERATOR_CANCELLED; @@ -1535,7 +1535,7 @@ static int sculpt_face_sets_randomize_colors_exec(bContext *C, wmOperator * /*op } BKE_pbvh_face_sets_color_set(pbvh, mesh->face_sets_color_seed, mesh->face_sets_color_default); - BKE_pbvh_search_gather(pbvh, NULL, NULL, &nodes, &totnode); + BKE_pbvh_search_gather(pbvh, nullptr, nullptr, &nodes, &totnode); for (int i = 0; i < totnode; i++) { BKE_pbvh_node_mark_redraw(nodes[i]); } @@ -1642,7 +1642,7 @@ static EnumPropertyItem prop_sculpt_face_sets_edit_types[] = { "All tangency", "Extrude a Face Set along the normals of the faces", }, - {0, NULL, 0, NULL, NULL}, + {0, nullptr, 0, nullptr, nullptr}, }; static void sculpt_face_set_grow_bmesh(Object *ob, @@ -1750,7 +1750,7 @@ static void sculpt_face_set_fill_component(Object *ob, SCULPT_vertex_face_set_set(ss, vertex, active_face_set_id); } - BLI_gset_free(connected_components, NULL); + BLI_gset_free(connected_components, nullptr); } static void sculpt_face_set_shrink_bmesh(Object *ob, @@ -1916,7 +1916,7 @@ static void sculpt_face_set_delete_geometry(Object *ob, cparams.use_shapekey = true; cparams.create_shapekey_layers = true; - BM_mesh_bm_from_me(ob, bm, mesh, &cparams); + BM_mesh_bm_from_me(bm, mesh, &cparams); BM_mesh_elem_table_init(bm, BM_FACE); BM_mesh_elem_table_ensure(bm, BM_FACE); @@ -1934,7 +1934,7 @@ static void sculpt_face_set_delete_geometry(Object *ob, BMeshToMeshParams tparams = {0}; - BM_mesh_bm_to_me(NULL, ob, bm, (Mesh *)ob->data, &tparams); + BM_mesh_bm_to_me(nullptr, bm, (Mesh *)ob->data, &tparams); BM_mesh_free(bm); } @@ -1983,13 +1983,13 @@ static void sculpt_face_set_apply_edit(Object *ob, switch (mode) { case SCULPT_FACE_SET_EDIT_GROW: { - int *prev_face_sets = ss->face_sets ? (int *)MEM_dupallocN(ss->face_sets) : NULL; + int *prev_face_sets = ss->face_sets ? (int *)MEM_dupallocN(ss->face_sets) : nullptr; sculpt_face_set_grow(ob, ss, prev_face_sets, active_face_set_id, modify_hidden); MEM_SAFE_FREE(prev_face_sets); break; } case SCULPT_FACE_SET_EDIT_SHRINK: { - int *prev_face_sets = ss->face_sets ? (int *)MEM_dupallocN(ss->face_sets) : NULL; + int *prev_face_sets = ss->face_sets ? (int *)MEM_dupallocN(ss->face_sets) : nullptr; sculpt_face_set_shrink(ob, ss, prev_face_sets, active_face_set_id, modify_hidden); MEM_SAFE_FREE(prev_face_sets); break; @@ -2019,7 +2019,7 @@ static void sculpt_face_set_apply_edit(Object *ob, sculpt_face_set_edit_fair_face_set(ob, face_set_id, MESH_FAIRING_DEPTH_TANGENCY); } - BLI_gset_free(face_sets_ids, NULL); + BLI_gset_free(face_sets_ids, nullptr); } break; case SCULPT_FACE_SET_EDIT_FAIR_CURVATURE: sculpt_face_set_edit_fair_face_set(ob, active_face_set_id, MESH_FAIRING_DEPTH_CURVATURE); @@ -2097,7 +2097,7 @@ static void sculpt_face_set_edit_modify_face_sets(Object *ob, PBVH *pbvh = ob->sculpt->pbvh; PBVHNode **nodes; int totnode; - BKE_pbvh_search_gather(pbvh, NULL, NULL, &nodes, &totnode); + BKE_pbvh_search_gather(pbvh, nullptr, nullptr, &nodes, &totnode); if (!nodes) { return; @@ -2123,7 +2123,7 @@ static void sculpt_face_set_edit_modify_coordinates(bContext *C, PBVH *pbvh = ss->pbvh; PBVHNode **nodes; int totnode; - BKE_pbvh_search_gather(pbvh, NULL, NULL, &nodes, &totnode); + BKE_pbvh_search_gather(pbvh, nullptr, nullptr, &nodes, &totnode); SCULPT_undo_push_begin(ob, op); for (int i = 0; i < totnode; i++) { BKE_pbvh_node_mark_update(nodes[i]); @@ -2185,7 +2185,7 @@ static void sculpt_face_set_extrude_id(Object *ob, Mesh *mesh = (Mesh *)ob->data; int next_face_set_id = SCULPT_face_set_next_available_get(ss) + 1; - SculptFaceSetIsland *island = NULL; + SculptFaceSetIsland *island = nullptr; if (no_islands && ss->active_face.i != PBVH_REF_NONE) { island = SCULPT_face_set_island_get(ss, ss->active_face, active_face_set_id); @@ -2201,7 +2201,7 @@ static void sculpt_face_set_extrude_id(Object *ob, } } - no_islands = no_islands && island != NULL; + no_islands = no_islands && island != nullptr; BMesh *bm = sculpt_faceset_bm_begin(ob, ss, mesh); if (ss->bm) { @@ -2603,7 +2603,7 @@ static void sculpt_face_set_extrude_id(Object *ob, if (!ss->bm) { BMeshToMeshParams params = {0}; - BM_mesh_bm_to_me(NULL, NULL, bm, (Mesh *)ob->data, ¶ms); + BM_mesh_bm_to_me(nullptr, bm, (Mesh *)ob->data, ¶ms); } sculpt_faceset_bm_end(ss, bm); @@ -2762,7 +2762,7 @@ SculptFaceSetIsland *SCULPT_face_set_island_get(SculptSession *ss, PBVHFaceRef f *ret = *island; // prevent faces from freeing - island->faces = NULL; + island->faces = nullptr; SCULPT_face_set_islands_free(ss, islands); return ret; @@ -2771,7 +2771,7 @@ SculptFaceSetIsland *SCULPT_face_set_island_get(SculptSession *ss, PBVHFaceRef f } SCULPT_face_set_islands_free(ss, islands); - return NULL; + return nullptr; } void SCULPT_face_set_island_free(SculptFaceSetIsland *island) @@ -2858,7 +2858,7 @@ static int sculpt_face_set_edit_modal(bContext *C, wmOperator *op, const wmEvent PBVHNode **nodes; int totnode; - BKE_pbvh_search_gather(ss->pbvh, NULL, NULL, &nodes, &totnode); + BKE_pbvh_search_gather(ss->pbvh, nullptr, nullptr, &nodes, &totnode); for (int i = 0; i < totnode; i++) { BKE_pbvh_node_mark_update(nodes[i]); } @@ -2908,7 +2908,7 @@ static void sculpt_face_set_extrude(bContext *C, } else { SCULPT_undo_push_begin(ob, op); - SCULPT_undo_push_node(ob, NULL, SCULPT_UNDO_COORDS); + SCULPT_undo_push_node(ob, nullptr, SCULPT_UNDO_COORDS); } sculpt_face_set_extrude_id(ob, no_islands, ob->sculpt, active_face_set, fsecd); diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.hh b/source/blender/editors/sculpt_paint/sculpt_intern.hh index dee1d6f5498..1c4a3671e95 100644 --- a/source/blender/editors/sculpt_paint/sculpt_intern.hh +++ b/source/blender/editors/sculpt_paint/sculpt_intern.hh @@ -1686,9 +1686,6 @@ void SCULPT_automasking_cache_settings_update(AutomaskingCache *automasking, const Sculpt *sd, const Brush *brush); -void SCULPT_boundary_automasking_init(Object *ob, - eBoundaryAutomaskMode mode, - int propagation_steps); bool SCULPT_automasking_needs_normal(const SculptSession *ss, const Sculpt *sculpt, const Brush *brush); diff --git a/source/blender/editors/sculpt_paint/sculpt_replay.c b/source/blender/editors/sculpt_paint/sculpt_replay.c index cc647912747..9e5ecbcafee 100644 --- a/source/blender/editors/sculpt_paint/sculpt_replay.c +++ b/source/blender/editors/sculpt_paint/sculpt_replay.c @@ -786,7 +786,7 @@ void SCULPT_replay_make_cube(struct bContext *C, int steps) } } - BM_mesh_remap(bm, rands[0], rands[1], rands[3], rands[2]); + BM_mesh_remap(bm, rands[0], rands[1], rands[2], rands[3]); for (int i = 0; i < 4; i++) { MEM_SAFE_FREE(rands[i]); @@ -798,7 +798,7 @@ void SCULPT_replay_make_cube(struct bContext *C, int steps) BKE_pbvh_free(ss->pbvh); ss->pbvh = NULL; - //XXX call BKE_sculptsession_update_attr_refs here? + // XXX call BKE_sculptsession_update_attr_refs here? /* Redraw. */ DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); diff --git a/source/blender/editors/sculpt_paint/sculpt_undo.cc b/source/blender/editors/sculpt_paint/sculpt_undo.cc index 7c75dd5c29d..37282dd43a1 100644 --- a/source/blender/editors/sculpt_paint/sculpt_undo.cc +++ b/source/blender/editors/sculpt_paint/sculpt_undo.cc @@ -1012,7 +1012,7 @@ static void sculpt_undo_bmesh_enable(Object *ob, SculptUndoNode *unode, bool is_ params.create_shapekey_layers = true; params.active_shapekey = ob->shapenr; - BM_mesh_bm_from_me(nullptr, ss->bm, me, ¶ms); + BM_mesh_bm_from_me(ss->bm, me, ¶ms); BKE_sculptsession_update_attr_refs(ob); @@ -3233,7 +3233,7 @@ void ED_sculpt_fast_save_bmesh(Object *ob) params.cd_mask_extra.pmask = CD_MASK_MESH_ID; // BM_mesh_bm_to_me_threaded(nullptr, ob, bm, (Mesh *)ob->data, ¶ms); - BM_mesh_bm_to_me(nullptr, ob, bm, (Mesh *)ob->data, ¶ms); + BM_mesh_bm_to_me(nullptr, bm, (Mesh *)ob->data, ¶ms); #else SculptUndoStep *last_step = nullptr; @@ -3288,7 +3288,7 @@ void ED_sculpt_fast_save_bmesh(Object *ob) /* Just save everything */ struct BMeshToMeshParams params = {0}; - BM_mesh_bm_to_me(nullptr, ob, bm, (Mesh *)ob->data, ¶ms); + BM_mesh_bm_to_me(nullptr, bm, (Mesh *)ob->data, ¶ms); return; } diff --git a/source/blender/editors/uvedit/uvedit_unwrap_ops.cc b/source/blender/editors/uvedit/uvedit_unwrap_ops.cc index 2770190f534..050362d0c7f 100644 --- a/source/blender/editors/uvedit/uvedit_unwrap_ops.cc +++ b/source/blender/editors/uvedit/uvedit_unwrap_ops.cc @@ -3176,7 +3176,7 @@ void ED_uvedit_add_simple_uvs(Main *bmain, const Scene *scene, Object *ob) BMeshFromMeshParams bm_from_me_params{}; bm_from_me_params.calc_face_normal = true; bm_from_me_params.calc_vert_normal = true; - BM_mesh_bm_from_me(nullptr, bm, me, &bm_from_me_params); + BM_mesh_bm_from_me(bm, me, &bm_from_me_params); /* Select all UVs for cube_project. */ ED_uvedit_select_all(bm); @@ -3197,7 +3197,7 @@ void ED_uvedit_add_simple_uvs(Main *bmain, const Scene *scene, Object *ob) /* Write back from BMesh to Mesh. */ BMeshToMeshParams bm_to_me_params{}; - BM_mesh_bm_to_me(bmain, nullptr, bm, me, &bm_to_me_params); + BM_mesh_bm_to_me(bmain, bm, me, &bm_to_me_params); BM_mesh_free(bm); if (sync_selection) { diff --git a/source/blender/io/collada/collada_utils.cpp b/source/blender/io/collada/collada_utils.cpp index 476872a32cd..a71fbd50a2e 100644 --- a/source/blender/io/collada/collada_utils.cpp +++ b/source/blender/io/collada/collada_utils.cpp @@ -414,12 +414,12 @@ void bc_triangulate_mesh(Mesh *me) BMeshFromMeshParams bm_from_me_params{}; bm_from_me_params.calc_face_normal = true; bm_from_me_params.calc_vert_normal = true; - BM_mesh_bm_from_me(nullptr, bm, me, &bm_from_me_params); + BM_mesh_bm_from_me(bm, me, &bm_from_me_params); BM_mesh_triangulate(bm, quad_method, use_beauty, 4, tag_only, nullptr, nullptr, nullptr); BMeshToMeshParams bm_to_me_params{}; bm_to_me_params.calc_object_remap = false; - BM_mesh_bm_to_me(nullptr, nullptr, bm, me, &bm_to_me_params); + BM_mesh_bm_to_me(nullptr, bm, me, &bm_to_me_params); BM_mesh_free(bm); } diff --git a/source/blender/modifiers/intern/MOD_boolean.cc b/source/blender/modifiers/intern/MOD_boolean.cc index a8ce96f6bb2..453c66c26a6 100644 --- a/source/blender/modifiers/intern/MOD_boolean.cc +++ b/source/blender/modifiers/intern/MOD_boolean.cc @@ -248,7 +248,7 @@ static BMesh *BMD_mesh_bm_create( BMeshFromMeshParams bmesh_from_mesh_params{}; bmesh_from_mesh_params.calc_face_normal = true; bmesh_from_mesh_params.calc_vert_normal = true; - BM_mesh_bm_from_me(nullptr, bm, mesh_operand_ob, &bmesh_from_mesh_params); + BM_mesh_bm_from_me(bm, mesh_operand_ob, &bmesh_from_mesh_params); if (UNLIKELY(*r_is_flip)) { const int cd_loop_mdisp_offset = CustomData_get_offset(&bm->ldata, CD_MDISPS); @@ -259,7 +259,7 @@ static BMesh *BMD_mesh_bm_create( } } - BM_mesh_bm_from_me(nullptr, bm, mesh, &bmesh_from_mesh_params); + BM_mesh_bm_from_me(bm, mesh, &bmesh_from_mesh_params); return bm; } @@ -589,7 +589,7 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * else { BMeshToMeshParams bmesh_to_mesh_params{}; bmesh_to_mesh_params.calc_object_remap = false; - BM_mesh_bm_to_me(nullptr, nullptr, bm, result, &bmesh_to_mesh_params); + BM_mesh_bm_to_me(nullptr, bm, result, &bmesh_to_mesh_params); } BM_mesh_free(bm); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_ico_sphere.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_ico_sphere.cc index faee393f04f..a1795860e4d 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_ico_sphere.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_ico_sphere.cc @@ -58,7 +58,7 @@ static Mesh *create_ico_sphere_mesh(const int subdivisions, params.calc_object_remap = false; Mesh *mesh = reinterpret_cast(BKE_id_new_nomain(ID_ME, nullptr)); BKE_id_material_eval_ensure_default_slot(&mesh->id); - BM_mesh_bm_to_me(nullptr, nullptr, bm, mesh, ¶ms); + BM_mesh_bm_to_me(nullptr, bm, mesh, ¶ms); BM_mesh_free(bm); diff --git a/source/blender/python/bmesh/bmesh_py_types.c b/source/blender/python/bmesh/bmesh_py_types.c index 13568ae9e35..88134a09b49 100644 --- a/source/blender/python/bmesh/bmesh_py_types.c +++ b/source/blender/python/bmesh/bmesh_py_types.c @@ -1043,7 +1043,7 @@ static PyObject *bpy_bmesh_to_mesh(BPy_BMesh *self, PyObject *args) params.calc_object_remap = true; } - BM_mesh_bm_to_me(bmain, NULL, bm, me, ¶ms); + BM_mesh_bm_to_me(bmain, bm, me, ¶ms); /* we could have the user do this but if they forget blender can easy crash * since the references arrays for the objects derived meshes are now invalid */ @@ -1143,8 +1143,7 @@ static PyObject *bpy_bmesh_from_object(BPy_BMesh *self, PyObject *args, PyObject bm = self->bm; - BM_mesh_bm_from_me(NULL, - bm, + BM_mesh_bm_from_me(bm, me_eval, (&(struct BMeshFromMeshParams){ .calc_face_normal = use_fnorm, @@ -1211,8 +1210,7 @@ static PyObject *bpy_bmesh_from_mesh(BPy_BMesh *self, PyObject *args, PyObject * bm = self->bm; - BM_mesh_bm_from_me(NULL, - bm, + BM_mesh_bm_from_me(bm, me, (&(struct BMeshFromMeshParams){ .calc_face_normal = use_fnorm, -- 2.30.2 From b413062a09ed4eb77720ef5fc37b84afb0c1ae9c Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Sat, 11 Feb 2023 14:09:04 -0800 Subject: [PATCH 007/279] temp-sculpt-dyntopo: Fix layer brush and bmesh conversion bug --- source/blender/blenkernel/BKE_paint.h | 3 ++ .../bmesh/intern/bmesh_mesh_convert.cc | 11 +++-- source/blender/editors/sculpt_paint/sculpt.cc | 45 ++++++++----------- .../editors/sculpt_paint/sculpt_api.cc | 7 +-- .../sculpt_paint/sculpt_automasking.cc | 2 +- .../sculpt_paint/sculpt_brush_types.cc | 23 +++++++--- .../editors/sculpt_paint/sculpt_dyntopo.cc | 2 +- .../editors/sculpt_paint/sculpt_intern.hh | 5 +-- .../editors/sculpt_paint/sculpt_undo.cc | 3 -- 9 files changed, 51 insertions(+), 50 deletions(-) diff --git a/source/blender/blenkernel/BKE_paint.h b/source/blender/blenkernel/BKE_paint.h index c97655f9e41..68849e73407 100644 --- a/source/blender/blenkernel/BKE_paint.h +++ b/source/blender/blenkernel/BKE_paint.h @@ -688,6 +688,9 @@ typedef struct SculptAttributePointers { SculptAttribute *persistent_no; SculptAttribute *persistent_disp; + /* Layer brush. */ + SculptAttribute *layer_displayment; + /* Precomputed auto-mask factor indexed by vertex, owned by the auto-masking system and * initialized in #SCULPT_automasking_cache_init when needed. */ SculptAttribute *automasking_factor; diff --git a/source/blender/bmesh/intern/bmesh_mesh_convert.cc b/source/blender/bmesh/intern/bmesh_mesh_convert.cc index 70eed752def..b613a4be13b 100644 --- a/source/blender/bmesh/intern/bmesh_mesh_convert.cc +++ b/source/blender/bmesh/intern/bmesh_mesh_convert.cc @@ -1159,8 +1159,11 @@ void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *me, const struct BMeshToMesh CustomData_copy(&bm->pdata, &me->pdata, mask.pmask, CD_SET_DEFAULT, me->totpoly); } - CustomData_add_layer_named( - &me->vdata, CD_PROP_FLOAT3, CD_CONSTRUCT, nullptr, me->totvert, "position"); + if (!CustomData_get_layer_named(&me->vdata, CD_PROP_FLOAT3, "position")) { + CustomData_add_layer_named( + &me->vdata, CD_PROP_FLOAT3, CD_CONSTRUCT, nullptr, me->totvert, "position"); + } + CustomData_add_layer(&me->edata, CD_MEDGE, CD_SET_DEFAULT, nullptr, me->totedge); CustomData_add_layer(&me->ldata, CD_MLOOP, CD_SET_DEFAULT, nullptr, me->totloop); CustomData_add_layer(&me->pdata, CD_MPOLY, CD_SET_DEFAULT, nullptr, me->totpoly); @@ -1180,8 +1183,6 @@ void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *me, const struct BMeshToMesh i = 0; BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) { - copy_v3_v3(positions[i], v->co); - if (BM_elem_flag_test(v, BM_ELEM_HIDDEN)) { need_hide_vert = true; } @@ -1194,6 +1195,8 @@ void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *me, const struct BMeshToMesh /* Copy over custom-data. */ CustomData_from_bmesh_block(&bm->vdata, &me->vdata, v->head.data, i); + copy_v3_v3(positions[i], v->co); + i++; BM_CHECK_ELEMENT(v); diff --git a/source/blender/editors/sculpt_paint/sculpt.cc b/source/blender/editors/sculpt_paint/sculpt.cc index f9bd9aedc58..e1cccceaaca 100644 --- a/source/blender/editors/sculpt_paint/sculpt.cc +++ b/source/blender/editors/sculpt_paint/sculpt.cc @@ -1856,33 +1856,20 @@ enum StrokeFlags { CLIP_Z = 4, }; -void SCULPT_orig_vert_data_unode_init(SculptOrigVertData *data, Object *ob, SculptUndoNode *unode) -{ - SculptSession *ss = ob->sculpt; - BMesh *bm = ss->bm; - - memset(data, 0, sizeof(*data)); - data->unode = unode; - - if (bm) { - data->bm_log = ss->bm_log; - } - else { - data->coords = data->unode->co; - data->normals = data->unode->no; - data->vmasks = data->unode->mask; - data->colors = data->unode->col; - } -} - void SCULPT_orig_vert_data_init(SculptOrigVertData *data, Object *ob, PBVHNode *node, SculptUndoType type) { - SculptUndoNode *unode; - unode = SCULPT_undo_push_node(ob, node, type); - SCULPT_orig_vert_data_unode_init(data, ob, unode); + SculptSession *ss = ob->sculpt; + BMesh *bm = ss->bm; + + data->ss = ss; + data->datatype = type; + + if (bm) { + data->bm_log = ss->bm_log; + } } /** @@ -4034,6 +4021,7 @@ static void sculpt_topology_update(Sculpt *sd, PBVHTopologyUpdateMode mode = PBVHTopologyUpdateMode(0); float location[3]; +#if 0 int dyntopo_mode = brush->dyntopo.flag; int dyntopo_detail_mode = brush->dyntopo.mode; @@ -4056,6 +4044,14 @@ static void sculpt_topology_update(Sculpt *sd, if (dyntopo_mode & DYNTOPO_CLEANUP) { mode |= PBVH_Cleanup; } +#endif + + if (sd->flags & SCULPT_DYNTOPO_COLLAPSE) { + mode |= PBVH_Collapse; + } + if (sd->flags & SCULPT_DYNTOPO_SUBDIVIDE) { + mode |= PBVH_Subdivide; + } SculptSearchSphereData sdata{}; sdata.ss = ss, sdata.sd = sd, sdata.ob = ob; @@ -5050,7 +5046,6 @@ static const char *sculpt_tool_name(Sculpt *sd) void SCULPT_cache_free(SculptSession *ss, struct Object *ob, StrokeCache *cache) { MEM_SAFE_FREE(cache->dial); - MEM_SAFE_FREE(cache->layer_displacement_factor); MEM_SAFE_FREE(cache->prev_colors); MEM_SAFE_FREE(cache->detail_directions); MEM_SAFE_FREE(cache->prev_displacement); @@ -6141,10 +6136,6 @@ static void sculpt_restore_mesh(Sculpt *sd, Object *ob) (brush->flag & BRUSH_DRAG_DOT)) { paint_mesh_restore_co(sd, ob); - - if (ss->cache) { - MEM_SAFE_FREE(ss->cache->layer_displacement_factor); - } } } diff --git a/source/blender/editors/sculpt_paint/sculpt_api.cc b/source/blender/editors/sculpt_paint/sculpt_api.cc index 432b6962732..6751e2ca241 100644 --- a/source/blender/editors/sculpt_paint/sculpt_api.cc +++ b/source/blender/editors/sculpt_paint/sculpt_api.cc @@ -603,9 +603,9 @@ void SCULPT_stroke_id_ensure(Object *ob) { SculptSession *ss = ob->sculpt; - if (!ss->attrs.automasking_stroke_id) { + if (!ss->attrs.stroke_id) { SculptAttributeParams params = {0}; - ss->attrs.automasking_stroke_id = BKE_sculpt_attribute_ensure( + ss->attrs.stroke_id = BKE_sculpt_attribute_ensure( ob, ATTR_DOMAIN_POINT, CD_PROP_INT32, @@ -625,7 +625,8 @@ int SCULPT_get_tool(const SculptSession *ss, const Brush *br) void SCULPT_ensure_persistent_layers(SculptSession *ss, Object *ob) { - SculptAttributeParams params = {.permanent = true, .simple_array = false}; + SculptAttributeParams params = {}; + params.permanent = true; if (!ss->attrs.persistent_co) { ss->attrs.persistent_co = BKE_sculpt_attribute_ensure( diff --git a/source/blender/editors/sculpt_paint/sculpt_automasking.cc b/source/blender/editors/sculpt_paint/sculpt_automasking.cc index 61b287f2b77..794cc898e91 100644 --- a/source/blender/editors/sculpt_paint/sculpt_automasking.cc +++ b/source/blender/editors/sculpt_paint/sculpt_automasking.cc @@ -791,7 +791,7 @@ static void sculpt_normal_occlusion_automasking_fill(AutomaskingCache *automaski f *= automasking_view_normal_factor(automasking, ss, vertex, &nodedata); } - if (ss->attrs.automasking_stroke_id) { + if (ss->attrs.stroke_id) { SCULPT_stroke_id_test(ss, vertex, STROKEID_USER_AUTOMASKING); } diff --git a/source/blender/editors/sculpt_paint/sculpt_brush_types.cc b/source/blender/editors/sculpt_paint/sculpt_brush_types.cc index afdeef8d93c..fd2d8bef854 100644 --- a/source/blender/editors/sculpt_paint/sculpt_brush_types.cc +++ b/source/blender/editors/sculpt_paint/sculpt_brush_types.cc @@ -1487,8 +1487,7 @@ static void do_layer_brush_task_cb_ex(void *__restrict userdata, Sculpt *sd = data->sd; const Brush *brush = data->brush; - const bool use_persistent_base = !ss->bm && ss->attrs.persistent_co && - brush->flag & BRUSH_PERSISTENT; + const bool use_persistent_base = ss->attrs.persistent_co && brush->flag & BRUSH_PERSISTENT; PBVHVertexIter vd; SculptOrigVertData orig_data; @@ -1529,7 +1528,11 @@ static void do_layer_brush_task_cb_ex(void *__restrict userdata, disp_factor = SCULPT_vertex_attr_get(vd.vertex, ss->attrs.persistent_disp); } else { - disp_factor = &ss->cache->layer_displacement_factor[vi]; + disp_factor = SCULPT_vertex_attr_get(vd.vertex, ss->attrs.layer_displayment); + + if (SCULPT_stroke_id_test(ss, vd.vertex, STROKEID_USER_LAYER_BRUSH)) { + *disp_factor = 0.0f; + } } /* When using persistent base, the layer brush (holding Control) invert mode resets the @@ -1586,10 +1589,7 @@ void SCULPT_do_layer_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode SculptSession *ss = ob->sculpt; Brush *brush = BKE_paint_brush(&sd->paint); - if (ss->cache->layer_displacement_factor == nullptr) { - ss->cache->layer_displacement_factor = MEM_cnew_array(SCULPT_vertex_count_get(ss), - __func__); - } + const bool use_persistent_base = ss->attrs.persistent_co && brush->flag & BRUSH_PERSISTENT; SculptThreadedTaskData data{}; data.sd = sd; @@ -1597,6 +1597,15 @@ void SCULPT_do_layer_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode data.brush = brush; data.nodes = nodes; + if (!use_persistent_base) { + SculptAttributeParams params = {}; + + ss->attrs.layer_displayment = BKE_sculpt_attribute_ensure( + ob, ATTR_DOMAIN_POINT, CD_PROP_FLOAT, SCULPT_ATTRIBUTE_NAME(layer_displayment), ¶ms); + } + + SCULPT_stroke_id_ensure(ob); + TaskParallelSettings settings; BKE_pbvh_parallel_range_settings(&settings, true, totnode); BLI_task_parallel_range(0, totnode, &data, do_layer_brush_task_cb_ex, &settings); diff --git a/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc b/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc index 97d2e258206..e75052b7c23 100644 --- a/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc +++ b/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc @@ -1748,7 +1748,7 @@ void SCULPT_uv_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) BLI_mempool_iternew(solver->verts, &iter); UVSmoothVert *sv = (UVSmoothVert *)BLI_mempool_iterstep(&iter); - AutomaskingNodeData automask_data = {0}; + AutomaskingNodeData automask_data = {}; automask_data.have_orig_data = false; for (; sv; sv = (UVSmoothVert *)BLI_mempool_iterstep(&iter)) { diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.hh b/source/blender/editors/sculpt_paint/sculpt_intern.hh index 1c4a3671e95..ba873b9682c 100644 --- a/source/blender/editors/sculpt_paint/sculpt_intern.hh +++ b/source/blender/editors/sculpt_paint/sculpt_intern.hh @@ -791,10 +791,6 @@ struct StrokeCache { /* Boundary brush */ SculptBoundary *boundaries[PAINT_SYMM_AREAS]; - /* Layer brush */ - float *layer_displacement_factor; - int *layer_stroke_id; - float vertex_rotation; /* amount to rotate the vertices when using rotate brush */ Dial *dial; @@ -2541,6 +2537,7 @@ enum StrokeIDUser { STROKEID_USER_PREV_COLOR = 1 << 3, STROKEID_USER_SMOOTH = 1 << 4, STROKEID_USER_OCCLUSION = 1 << 5, + STROKEID_USER_LAYER_BRUSH = 1 << 6, }; BLI_INLINE bool SCULPT_stroke_id_test(SculptSession *ss, PBVHVertRef vertex, StrokeIDUser user) diff --git a/source/blender/editors/sculpt_paint/sculpt_undo.cc b/source/blender/editors/sculpt_paint/sculpt_undo.cc index 37282dd43a1..2b537c8c672 100644 --- a/source/blender/editors/sculpt_paint/sculpt_undo.cc +++ b/source/blender/editors/sculpt_paint/sculpt_undo.cc @@ -1956,9 +1956,6 @@ static void sculpt_undo_store_coords(Object *ob, SculptUndoNode *unode) SculptSession *ss = ob->sculpt; PBVHVertexIter vd; - SculptOrigVertData orig_data; - SCULPT_orig_vert_data_unode_init(&orig_data, ob, unode); - int totvert, allvert; BKE_pbvh_node_num_verts(ss->pbvh, (PBVHNode *)unode->node, &totvert, &allvert); -- 2.30.2 From b82d08950d6e56e4c71412780a271d7e4f654765 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Sat, 11 Feb 2023 14:14:33 -0800 Subject: [PATCH 008/279] temp-sculpt-dyntopo: Fix compiler error --- .../editors/sculpt_paint/sculpt_dyntopo.cc | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc b/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc index e75052b7c23..5827cee2700 100644 --- a/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc +++ b/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc @@ -1724,14 +1724,13 @@ void SCULPT_uv_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) solver->strength = ss->cache->bstrength; /* Threaded loop over nodes. */ - SculptUVThreadData data = {.solver = solver, - .data = { - .sd = sd, - .ob = ob, - .brush = brush, - .nodes = nodes, - .offset = offset, - }}; + SculptUVThreadData data = {}; + data.solver = solver; + data.data.sd = sd; + data.data.ob = ob; + data.data.brush = brush; + data.data.nodes = nodes; + data.data.offset = offset; TaskParallelSettings settings; -- 2.30.2 From 1b788b5cf87154921db35d5d116be84b19d52f1e Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Sat, 11 Feb 2023 14:14:33 -0800 Subject: [PATCH 009/279] temp-sculpt-dyntopo: Fix compiler error --- source/blender/bmesh/intern/bmesh_mesh_convert.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/blender/bmesh/intern/bmesh_mesh_convert.h b/source/blender/bmesh/intern/bmesh_mesh_convert.h index 4ca589279f5..13b7ac73605 100644 --- a/source/blender/bmesh/intern/bmesh_mesh_convert.h +++ b/source/blender/bmesh/intern/bmesh_mesh_convert.h @@ -57,7 +57,7 @@ struct Object; * \warning This function doesn't calculate face normals. */ void BM_mesh_bm_from_me(BMesh *bm, const struct Mesh *me, const struct BMeshFromMeshParams *params) - ATTR_NONNULL(2, 4); + ATTR_NONNULL(2, 3); struct BMeshToMeshParams { /** Update object hook indices & vertex parents. */ @@ -90,7 +90,7 @@ void BM_enter_multires_space(struct Object *ob, struct BMesh *bm, int space); void BM_mesh_bm_to_me(struct Main *bmain, BMesh *bm, struct Mesh *me, - const struct BMeshToMeshParams *params) ATTR_NONNULL(3, 4, 5); + const struct BMeshToMeshParams *params) ATTR_NONNULL(2, 3, 4); /** * A version of #BM_mesh_bm_to_me intended for getting the mesh -- 2.30.2 From d656a1aa13afe964509d2fc5f9296d3d8a875774 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Sat, 11 Feb 2023 14:18:12 -0800 Subject: [PATCH 010/279] temp-sculpt-dyntopo: Fix another compiler error --- source/blender/editors/sculpt_paint/sculpt_geodesic.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/editors/sculpt_paint/sculpt_geodesic.cc b/source/blender/editors/sculpt_paint/sculpt_geodesic.cc index b291ae3e933..255eb4fd8f9 100644 --- a/source/blender/editors/sculpt_paint/sculpt_geodesic.cc +++ b/source/blender/editors/sculpt_paint/sculpt_geodesic.cc @@ -553,7 +553,7 @@ static float *SCULPT_geodesic_bmesh_create(Object *ob, do { while (BLI_LINKSTACK_SIZE(queue)) { - BMEdge *e = BLI_LINKSTACK_POP(queue); + BMEdge *e = (BMEdge *)BLI_LINKSTACK_POP(queue); BMVert *v1 = e->v1, *v2 = e->v2; int v1_i = BM_elem_index_get(e->v1); -- 2.30.2 From 9188bf38c02431f39883a4cfb1b18320ac1ea768 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Sat, 11 Feb 2023 14:26:27 -0800 Subject: [PATCH 011/279] temp-sculpt-dyntopo: More compiler errors --- .../editors/sculpt_paint/sculpt_dyntopo.cc | 42 ++++++++----------- .../editors/sculpt_paint/sculpt_geodesic.cc | 1 - 2 files changed, 17 insertions(+), 26 deletions(-) diff --git a/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc b/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc index 5827cee2700..3cd38eb3a08 100644 --- a/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc +++ b/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc @@ -405,7 +405,7 @@ void SCULPT_dynamic_topology_triangulate(SculptSession *ss, BMesh *bm) } MemArena *pf_arena = BLI_memarena_new(BLI_POLYFILL_ARENA_SIZE, __func__); - LinkNode *f_double = NULL; + LinkNode *f_double = nullptr; Vector(faces_array); @@ -423,14 +423,14 @@ void SCULPT_dynamic_topology_triangulate(SculptSession *ss, BMesh *bm) f, faces_array.data(), &faces_array_tot, - NULL, - NULL, + nullptr, + nullptr, &f_double, MOD_TRIANGULATE_QUAD_BEAUTY, MOD_TRIANGULATE_NGON_EARCLIP, true, pf_arena, - NULL); + nullptr); for (int i = 0; i < faces_array_tot; i++) { BMFace *f2 = faces_array[i]; @@ -456,14 +456,13 @@ void SCULPT_dynamic_topology_triangulate(SculptSession *ss, BMesh *bm) } BLI_memarena_free(pf_arena); - MEM_SAFE_FREE(faces_array); ss->totfaces = ss->totpoly = ss->bm->totface; ss->totvert = ss->bm->totvert; // BM_mesh_triangulate( - // bm, MOD_TRIANGULATE_QUAD_BEAUTY, MOD_TRIANGULATE_NGON_EARCLIP, 4, false, NULL, NULL, - // NULL); + // bm, MOD_TRIANGULATE_QUAD_BEAUTY, MOD_TRIANGULATE_NGON_EARCLIP, 4, false, nullptr, nullptr, + // nullptr); } void SCULPT_pbvh_clear(Object *ob, bool cache_pbvh) @@ -471,7 +470,7 @@ void SCULPT_pbvh_clear(Object *ob, bool cache_pbvh) SculptSession *ss = ob->sculpt; BKE_pbvh_pmap_release(ss->pmap); - ss->pmap = NULL; + ss->pmap = nullptr; /* Clear out any existing DM and PBVH. */ if (ss->pbvh) { @@ -710,7 +709,7 @@ static void SCULPT_dynamic_topology_disable_ex( ss->bm_log = nullptr; } - /* Typically valid but with global-undo they can be NULL, see: T36234. */ + /* Typically valid but with global-undo they can be nullptr, see: T36234. */ if (ss->bm) { // PBVH now frees this // BM_mesh_free(ss->bm); @@ -1053,8 +1052,8 @@ static void uvsolver_free(UVSolver *solver) BLI_mempool_destroy(solver->tris); BLI_mempool_destroy(solver->constraints); - BLI_ghash_free(solver->vhash, NULL, NULL); - BLI_ghash_free(solver->fhash, NULL, NULL); + BLI_ghash_free(solver->vhash, nullptr, nullptr); + BLI_ghash_free(solver->fhash, nullptr, nullptr); MEM_freeN(solver); } @@ -1088,7 +1087,7 @@ static UVSmoothVert *uvsolver_get_vert(UVSolver *solver, BMLoop *l) float *uv = (float *)BM_ELEM_CD_GET_VOID_P(l, solver->cd_uv); void *pkey = uvsolver_calc_loop_key(solver, l); - void **entry = NULL; + void **entry = nullptr; UVSmoothVert *v; if (!BLI_ghash_ensure_p(solver->vhash, pkey, &entry)) { @@ -1168,7 +1167,7 @@ double area_tri_v3_db(const double v1[3], const double v2[3], const double v3[3] static UVSmoothTri *uvsolver_ensure_face(UVSolver *solver, BMFace *f) { - void **entry = NULL; + void **entry = nullptr; if (BLI_ghash_ensure_p(solver->fhash, (void *)f, &entry)) { return (UVSmoothTri *)*entry; @@ -1623,7 +1622,7 @@ static void sculpt_uv_brush_cb(void *__restrict userdata, int tot2 = 0; float uv[2] = {0}; bool ok = true; - UVSmoothVert *lastv = NULL; + UVSmoothVert *lastv = nullptr; BM_ITER_ELEM (l2, &iter, l->v, BM_LOOPS_OF_VERT) { if (l2->v != l->v) { @@ -1686,7 +1685,7 @@ static void sculpt_uv_brush_cb(void *__restrict userdata, const float fade = bstrength * SCULPT_brush_strength_factor( - ss, brush, vd.co, sqrtf(test.dist), NULL, l->v->no, mask, vertex, thread_id) * + ss, brush, vd.co, sqrtf(test.dist), nullptr, l->v->no, mask, vertex, thread_id) * ss->cache->pressure; } while ((l = l->next) != f->l_first); @@ -1756,16 +1755,9 @@ void SCULPT_uv_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) continue; } - sv->brushfade = SCULPT_brush_strength_factor(ss, - brush, - sv->v->co, - sqrtf(test.dist), - NULL, - sv->v->no, - 0.0f, - (PBVHVertRef){.i = (intptr_t)sv->v}, - 0, - &automask_data); + PBVHVertRef vertex = {(intptr_t)sv->v}; + sv->brushfade = SCULPT_brush_strength_factor( + ss, brush, sv->v->co, sqrtf(test.dist), nullptr, sv->v->no, 0.0f, vertex, 0, &automask_data); } for (int i = 0; i < 5; i++) { diff --git a/source/blender/editors/sculpt_paint/sculpt_geodesic.cc b/source/blender/editors/sculpt_paint/sculpt_geodesic.cc index 255eb4fd8f9..9574efd314e 100644 --- a/source/blender/editors/sculpt_paint/sculpt_geodesic.cc +++ b/source/blender/editors/sculpt_paint/sculpt_geodesic.cc @@ -926,7 +926,6 @@ static float *SCULPT_geodesic_grids_create(Object *ob, BLI_memarena_free(ma); BLI_ghash_free(ehash, nullptr, nullptr); - MEM_SAFE_FREE(edges); MEM_SAFE_FREE(vmap); MEM_SAFE_FREE(e_otherv_map); -- 2.30.2 From 65bdb7d53e362e7db889f5394216dbfcdd88fac5 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Sat, 11 Feb 2023 14:34:07 -0800 Subject: [PATCH 012/279] temp-sculpt-dyntopo: And yet more compiler errors And to think, I went through the effort of compiling this locally with clang --- source/blender/blenkernel/intern/dyntopo.cc | 6 ++--- .../sculpt_paint/sculpt_filter_mask.cc | 25 +++++++++---------- 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/source/blender/blenkernel/intern/dyntopo.cc b/source/blender/blenkernel/intern/dyntopo.cc index 672698cf221..79b9fc05413 100644 --- a/source/blender/blenkernel/intern/dyntopo.cc +++ b/source/blender/blenkernel/intern/dyntopo.cc @@ -1179,8 +1179,8 @@ static void unified_edge_queue_task_cb(void *__restrict userdata, BMEdge edge = *l->e; edge.head.hflag &= ~BM_ELEM_TAG; - intptr_t *t1 = (intptr_t *)&edge.head.index; - intptr_t *t2 = (intptr_t *)&l->e->head.index; + int64_t *t1 = (int64_t *)&edge.head.index; + int64_t *t2 = (int64_t *)&l->e->head.index; atomic_cas_int64(t2, *t2, *t1); @@ -2718,7 +2718,7 @@ extern "C" bool BKE_pbvh_bmesh_update_topology(PBVH *pbvh, } EdgeQueueContext eq_ctx; - memset(static_cast(&eq_ctx), 0, sizeof(eq_ctx)); + memset(static_cast(&eq_ctx), 0, sizeof(eq_ctx)); eq_ctx.pool = nullptr; eq_ctx.bm = pbvh->header.bm; diff --git a/source/blender/editors/sculpt_paint/sculpt_filter_mask.cc b/source/blender/editors/sculpt_paint/sculpt_filter_mask.cc index 8fe3c0ece6c..f80dc22a1d4 100644 --- a/source/blender/editors/sculpt_paint/sculpt_filter_mask.cc +++ b/source/blender/editors/sculpt_paint/sculpt_filter_mask.cc @@ -539,12 +539,12 @@ static float *sculpt_ipmask_step_compute(SculptSession *ss, const int totvert = SCULPT_vertex_count_get(ss); float *next_mask = MEM_cnew_array(totvert, "delta values"); - SculptIPMaskFilterTaskData data = { - .ss = ss, - .next_mask = next_mask, - .current_mask = current_mask, - .direction = direction, - }; + SculptIPMaskFilterTaskData data = {}; + data.ss = ss; + data.next_mask = next_mask; + data.current_mask = current_mask; + data.direction = direction; + TaskParallelSettings settings; memset(&settings, 0, sizeof(TaskParallelSettings)); settings.use_threading = totvert > SCULPT_IPMASK_FILTER_MIN_MULTITHREAD; @@ -635,13 +635,12 @@ static void sculpt_ipmask_apply_mask_data(SculptSession *ss, const float interpolation) { FilterCache *filter_cache = ss->filter_cache; - SculptThreadedTaskData data = { - .ss = ss, - .nodes = filter_cache->nodes, - .new_mask = new_mask, - .next_mask = next_mask, - .mask_interpolation = interpolation, - }; + SculptThreadedTaskData data = {}; + data.ss = ss; + data.nodes = filter_cache->nodes; + data.new_mask = new_mask; + data.next_mask = next_mask; + data.mask_interpolation = interpolation; TaskParallelSettings settings; BKE_pbvh_parallel_range_settings(&settings, true, filter_cache->totnode); -- 2.30.2 From 8d47a57baef2bacde1cdc6aab66f1cdca3b108c1 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Sat, 11 Feb 2023 14:34:07 -0800 Subject: [PATCH 013/279] temp-sculpt-dyntopo: And yet more compiler errors And to think, I went through the effort of compiling this locally with clang --- source/blender/editors/sculpt_paint/sculpt_ops.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/editors/sculpt_paint/sculpt_ops.cc b/source/blender/editors/sculpt_paint/sculpt_ops.cc index a00c3efc7f5..1a13d0663f9 100644 --- a/source/blender/editors/sculpt_paint/sculpt_ops.cc +++ b/source/blender/editors/sculpt_paint/sculpt_ops.cc @@ -1311,7 +1311,7 @@ static int sculpt_set_limit_surface_exec(bContext *C, wmOperator * /* op */) SCULPT_vertex_random_access_ensure(ss); - SculptAttributeParams params = {.permanent = false, .simple_array = false}; + SculptAttributeParams params = {}; if (!ss->attrs.limit_surface) { ss->attrs.limit_surface = BKE_sculpt_attribute_ensure( -- 2.30.2 From ac5c10823415b6b60898004691b65703ada67148 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Sat, 11 Feb 2023 14:43:40 -0800 Subject: [PATCH 014/279] temp-sculpt-dyntopo: And more compiler errors. . . --- source/blender/blenkernel/intern/pbvh_bmesh.cc | 9 +++------ source/blender/editors/sculpt_paint/sculpt_boundary.cc | 2 +- source/blender/editors/sculpt_paint/sculpt_curvature.cc | 8 ++++---- source/blender/editors/sculpt_paint/sculpt_ops.cc | 2 +- 4 files changed, 9 insertions(+), 12 deletions(-) diff --git a/source/blender/blenkernel/intern/pbvh_bmesh.cc b/source/blender/blenkernel/intern/pbvh_bmesh.cc index 47ba9dde08c..66ef1d28cb0 100644 --- a/source/blender/blenkernel/intern/pbvh_bmesh.cc +++ b/source/blender/blenkernel/intern/pbvh_bmesh.cc @@ -362,9 +362,9 @@ static void pbvh_print_mem_size(PBVH *pbvh) int ptrsize = (int)sizeof(void *); - float memsize3[3] = {(float)(ptrsize * bm->idmap.map_size) / 1024.0 / 1024.0, - (float)(ptrsize * bm->idmap.freelist_len) / 1024.0 / 1024.0, - (float)(4 * bm->idmap.free_ids_size) / 1024.0 / 1024.0}; + float memsize3[3] = {(float)(ptrsize * bm->idmap.map_size) / 1024.0f / 1024.0f, + (float)(ptrsize * bm->idmap.freelist_len) / 1024.0f / 1024.0f, + (float)(4 * bm->idmap.free_ids_size) / 1024.0f / 1024.0f}; printf("idmap sizes:\n map_size: %.2fmb freelist_len: %.2fmb free_ids_size: %.2fmb\n", memsize3[0], @@ -4366,10 +4366,7 @@ BMesh *BKE_pbvh_reorder_bmesh(PBVH *pbvh) MEM_SAFE_FREE(lidx); MEM_SAFE_FREE(fidx); - MEM_SAFE_FREE(nstack); - MEM_SAFE_FREE(roots); BLI_mempool_destroy(pool); - MEM_SAFE_FREE(stack); MEM_SAFE_FREE(vnodemap); return pbvh->header.bm; diff --git a/source/blender/editors/sculpt_paint/sculpt_boundary.cc b/source/blender/editors/sculpt_paint/sculpt_boundary.cc index 97de636eda5..4c2f1f66506 100644 --- a/source/blender/editors/sculpt_paint/sculpt_boundary.cc +++ b/source/blender/editors/sculpt_paint/sculpt_boundary.cc @@ -1165,7 +1165,7 @@ static bool sculpt_walk_scalar_field(SculptSession *ss, return true; } -Object *sculpt_get_vis_object(bContext *C, SculptSession *ss, char *name) +Object *sculpt_get_vis_object(bContext *C, SculptSession *ss, const char *name) { if (!C) { C = ss->cache->C; diff --git a/source/blender/editors/sculpt_paint/sculpt_curvature.cc b/source/blender/editors/sculpt_paint/sculpt_curvature.cc index d2c1d8b8584..1b460d032eb 100644 --- a/source/blender/editors/sculpt_paint/sculpt_curvature.cc +++ b/source/blender/editors/sculpt_paint/sculpt_curvature.cc @@ -155,10 +155,10 @@ bool SCULPT_calc_principle_curvatures(SculptSession *ss, if (useAccurateSolver) { int val = SCULPT_vertex_valence_get(ss, vertex); - float *ws = BLI_array_alloca(ws, val); - float *cot1 = BLI_array_alloca(cot1, val); - float *cot2 = BLI_array_alloca(cot2, val); - float *areas = BLI_array_alloca(areas, val); + float *ws = (float *)BLI_array_alloca(ws, val); + float *cot1 = (float *)BLI_array_alloca(cot1, val); + float *cot2 = (float *)BLI_array_alloca(cot2, val); + float *areas = (float *)BLI_array_alloca(areas, val); float totarea = 0.0f; SCULPT_get_cotangents(ss, vertex, ws, cot1, cot2, areas, &totarea); diff --git a/source/blender/editors/sculpt_paint/sculpt_ops.cc b/source/blender/editors/sculpt_paint/sculpt_ops.cc index 1a13d0663f9..26666ed5c04 100644 --- a/source/blender/editors/sculpt_paint/sculpt_ops.cc +++ b/source/blender/editors/sculpt_paint/sculpt_ops.cc @@ -1497,7 +1497,7 @@ static int sculpt_regularize_rake_exec(bContext *C, wmOperator *op) float tanco[3]; add_v3_v3v3(tanco, v2->co, dir2); - SCULPT_dyntopo_check_disk_sort(ss, (PBVHVertRef){.i = (intptr_t)v2}); + SCULPT_dyntopo_check_disk_sort(ss, BKE_pbvh_make_vref((intptr_t)v2)); float lastdir3[3]; float firstdir3[3]; -- 2.30.2 From b51ec6b1fc72a712f01d8f9b0cded1dbf221bcd9 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Sat, 11 Feb 2023 14:48:46 -0800 Subject: [PATCH 015/279] temp-sculpt-dyntopo: Fix compiler error --- source/blender/blenkernel/intern/pbvh_bmesh.cc | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/source/blender/blenkernel/intern/pbvh_bmesh.cc b/source/blender/blenkernel/intern/pbvh_bmesh.cc index 66ef1d28cb0..ab702ea27b9 100644 --- a/source/blender/blenkernel/intern/pbvh_bmesh.cc +++ b/source/blender/blenkernel/intern/pbvh_bmesh.cc @@ -4572,19 +4572,6 @@ BMesh *BKE_pbvh_reorder_bmesh2(PBVH *pbvh) node->flag |= PBVH_UpdateTris | PBVH_UpdateRedraw; } - MEM_SAFE_FREE(fvs); - MEM_SAFE_FREE(fes); - - for (int i = 0; i < pbvh->totnode; i++) { - MEM_SAFE_FREE(nodedata[i].verts); - MEM_SAFE_FREE(nodedata[i].edges); - MEM_SAFE_FREE(nodedata[i].faces); - } - - MEM_SAFE_FREE(verts); - MEM_SAFE_FREE(edges); - MEM_SAFE_FREE(faces); - BM_mesh_free(pbvh->header.bm); pbvh->header.bm = bm2; -- 2.30.2 From 1c0c0d377c6c730bec8fb04f1e0df72170e0350c Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Sat, 11 Feb 2023 15:01:22 -0800 Subject: [PATCH 016/279] temp-sculpt-dyntopo: More compiler fixes --- source/blender/blenkernel/intern/paint.cc | 4 --- source/blender/blenkernel/intern/pbvh.cc | 28 ++++++++++++------- .../blender/blenkernel/intern/pbvh_bmesh.cc | 2 +- source/blender/bmesh/intern/bmesh_log.cc | 1 + .../blender/bmesh/intern/bmesh_log_intern.h | 2 -- .../editors/sculpt_paint/sculpt_dyntopo.cc | 3 -- .../editors/sculpt_paint/sculpt_ops.cc | 2 +- .../editors/sculpt_paint/sculpt_smooth.cc | 10 +++---- 8 files changed, 26 insertions(+), 26 deletions(-) diff --git a/source/blender/blenkernel/intern/paint.cc b/source/blender/blenkernel/intern/paint.cc index 0b9799904e4..8e02b2d9aff 100644 --- a/source/blender/blenkernel/intern/paint.cc +++ b/source/blender/blenkernel/intern/paint.cc @@ -2604,10 +2604,6 @@ static PBVH *build_pbvh_for_dynamic_topology(Object *ob, bool update_sculptverts printf("%s: ss->bm != bm!\n", __func__); ob->sculpt->bm = bm; - - if (ob->sculpt->bm_log) { - BM_log_set_bm(bm, ob->sculpt->bm_log); - } } else if (!bm) { BKE_pbvh_set_bmesh(pbvh, ob->sculpt->bm); diff --git a/source/blender/blenkernel/intern/pbvh.cc b/source/blender/blenkernel/intern/pbvh.cc index 1348ce256e2..a74c6271027 100644 --- a/source/blender/blenkernel/intern/pbvh.cc +++ b/source/blender/blenkernel/intern/pbvh.cc @@ -25,9 +25,9 @@ #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" +#include "DNA_modifier_types.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" -#include "DNA_modifier_types.h" #include "BKE_attribute.h" #include "BKE_ccg.h" @@ -3828,8 +3828,12 @@ bool *BKE_pbvh_get_vert_hide_for_write(PBVH *pbvh) if (pbvh->hide_vert) { return pbvh->hide_vert; } - pbvh->hide_vert = (bool *)CustomData_add_layer_named( - &pbvh->mesh->vdata, CD_PROP_BOOL, CD_SET_DEFAULT, nullptr, pbvh->mesh->totvert, ".hide_vert"); + pbvh->hide_vert = (bool *)CustomData_add_layer_named(&pbvh->mesh->vdata, + CD_PROP_BOOL, + CD_SET_DEFAULT, + nullptr, + pbvh->mesh->totvert, + ".hide_vert"); return pbvh->hide_vert; } @@ -4696,9 +4700,11 @@ void BKE_pbvh_get_vert_face_areas(PBVH *pbvh, PBVHVertRef vertex, float *r_areas const int grid_index = index / key->grid_area; const int vertex_index = index - grid_index * key->grid_area; - SubdivCCGCoord coord = {.grid_index = grid_index, - .x = vertex_index % key->grid_size, - .y = vertex_index / key->grid_size}; + SubdivCCGCoord coord = {}; + + coord.grid_index = grid_index; + coord.x = short(vertex_index % key->grid_size); + coord.y = short(vertex_index / key->grid_size); SubdivCCGNeighbors neighbors; BKE_subdiv_ccg_neighbor_coords_get(pbvh->subdiv_ccg, &coord, false, &neighbors); @@ -4710,7 +4716,7 @@ void BKE_pbvh_get_vert_face_areas(PBVH *pbvh, PBVHVertRef vertex, float *r_areas for (i = 0; i < neighbors.size; i++) { SubdivCCGCoord *coord2 = neighbors.coords + i; - int vertex_index2 = coord2->y * key->grid_size + coord2->x; + int vertex_index2 = int(coord2->y) * key->grid_size + int(coord2->x); float *co2 = CCG_elem_co( key, CCG_elem_offset(key, pbvh->grids[coord2->grid_index], vertex_index2)); @@ -4804,9 +4810,11 @@ void BKE_pbvh_update_vert_boundary_grids(PBVH *pbvh, const int grid_index = index / key->grid_area; const int vertex_index = index - grid_index * key->grid_area; - SubdivCCGCoord coord = {.grid_index = grid_index, - .x = vertex_index % key->grid_size, - .y = vertex_index / key->grid_size}; + SubdivCCGCoord coord = {}; + + coord.grid_index = grid_index; + coord.x = short(vertex_index % key->grid_size); + coord.y = short(vertex_index / key->grid_size); SubdivCCGNeighbors neighbors; BKE_subdiv_ccg_neighbor_coords_get(subdiv_ccg, &coord, false, &neighbors); diff --git a/source/blender/blenkernel/intern/pbvh_bmesh.cc b/source/blender/blenkernel/intern/pbvh_bmesh.cc index ab702ea27b9..f3942830fe7 100644 --- a/source/blender/blenkernel/intern/pbvh_bmesh.cc +++ b/source/blender/blenkernel/intern/pbvh_bmesh.cc @@ -5438,7 +5438,7 @@ void pbvh_bmesh_cache_test(CacheParams *params, BMesh **r_bm, PBVH **r_pbvh_out) // randomize uint *rands[4]; - uint tots[4] = {bm->totvert, bm->totedge, bm->totloop, bm->totface}; + uint tots[4] = {(uint)bm->totvert, (uint)bm->totedge, (uint)bm->totloop, (uint)bm->totface}; RNG *rng = BLI_rng_new(0); diff --git a/source/blender/bmesh/intern/bmesh_log.cc b/source/blender/bmesh/intern/bmesh_log.cc index 5f272a3162b..d5a08626179 100644 --- a/source/blender/bmesh/intern/bmesh_log.cc +++ b/source/blender/bmesh/intern/bmesh_log.cc @@ -1634,3 +1634,4 @@ bool BM_log_entry_drop(BMLogEntry *entry) MEM_delete(entry); return true; } + diff --git a/source/blender/bmesh/intern/bmesh_log_intern.h b/source/blender/bmesh/intern/bmesh_log_intern.h index a571e77e129..45df949cbf8 100644 --- a/source/blender/bmesh/intern/bmesh_log_intern.h +++ b/source/blender/bmesh/intern/bmesh_log_intern.h @@ -107,8 +107,6 @@ bool BM_log_free(BMLog *log, bool safe_mode); BMLog *BM_log_unfreeze(BMesh *bm, BMLogEntry *entry); -void BM_log_set_bm(BMesh *bm, BMLog *log); - /* Get the number of log entries */ /* Get the number of log entries */ int BM_log_length(const BMLog *log); diff --git a/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc b/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc index 3cd38eb3a08..fe7c545f542 100644 --- a/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc +++ b/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc @@ -382,9 +382,6 @@ void SCULPT_reorder_bmesh(SculptSession *ss) BKE_sculptsession_update_attr_refs(ob); - if (ss->bm_log) { - BM_log_set_bm(ss->bm, ss->bm_log); - } #endif } diff --git a/source/blender/editors/sculpt_paint/sculpt_ops.cc b/source/blender/editors/sculpt_paint/sculpt_ops.cc index 26666ed5c04..36c55b349b3 100644 --- a/source/blender/editors/sculpt_paint/sculpt_ops.cc +++ b/source/blender/editors/sculpt_paint/sculpt_ops.cc @@ -1357,7 +1357,7 @@ static int sculpt_regularize_rake_exec(bContext *C, wmOperator *op) Object *ob = CTX_data_active_object(C); SculptSession *ss = ob->sculpt; - Object *sculpt_get_vis_object(bContext * C, SculptSession * ss, char *name); + Object *sculpt_get_vis_object(bContext * C, SculptSession * ss, const char *name); void sculpt_end_vis_object(bContext * C, SculptSession * ss, Object * ob, BMesh * bm); BMeshCreateParams params = {}; diff --git a/source/blender/editors/sculpt_paint/sculpt_smooth.cc b/source/blender/editors/sculpt_paint/sculpt_smooth.cc index ba99ec57a06..c30eef4a575 100644 --- a/source/blender/editors/sculpt_paint/sculpt_smooth.cc +++ b/source/blender/editors/sculpt_paint/sculpt_smooth.cc @@ -760,7 +760,7 @@ void SCULPT_reproject_cdata(SculptSession *ss, /* original (l->prev, l, l->next) projections for each loop ('l' remains unchanged) */ char *_blocks = (char *)alloca(ldata->totsize * totloop); - void **blocks = BLI_array_alloca(blocks, totloop); + void **blocks = (void **)BLI_array_alloca(blocks, totloop); for (int i = 0; i < totloop; i++, _blocks += ldata->totsize) { blocks[i] = (void *)_blocks; @@ -803,8 +803,8 @@ void SCULPT_reproject_cdata(SculptSession *ss, BMLoop *l = ls[i]; float no[3] = {0.0f, 0.0f, 0.0f}; - BMLoop *fakels = BLI_array_alloca(fakels, l->f->len); - BMVert *fakevs = BLI_array_alloca(fakevs, l->f->len); + BMLoop *fakels = (BMLoop *)BLI_array_alloca(fakels, l->f->len); + BMVert *fakevs = (BMVert *)BLI_array_alloca(fakevs, l->f->len); BMLoop *l2 = l->f->l_first; BMLoop *fakel = fakels; BMVert *fakev = fakevs; @@ -818,7 +818,7 @@ void SCULPT_reproject_cdata(SculptSession *ss, *fakev = *l2->v; fakel->v = fakev; - SCULPT_vertex_check_origdata(ss, (PBVHVertRef){.i = (intptr_t)l2->v}); + SCULPT_vertex_check_origdata(ss, BKE_pbvh_make_vref((intptr_t)l2->v)); if (l2->v == v) { copy_v3_v3(fakev->co, origco); @@ -857,7 +857,7 @@ void SCULPT_reproject_cdata(SculptSession *ss, CustomData_bmesh_copy_data(&ss->bm->ldata, &ss->bm->ldata, interpl->head.data, &l->head.data); } - int *tots = BLI_array_alloca(tots, totuv); + int *tots = (int *)BLI_array_alloca(tots, totuv); for (int i = 0; i < totuv; i++) { lastuvs[i * 2] = lastuvs[i * 2 + 1] = 0.0f; -- 2.30.2 From b0ba5dece33e73ec057b0a322a50f553e025605c Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Sat, 11 Feb 2023 15:13:27 -0800 Subject: [PATCH 017/279] temp-sculpt-dyntopo: more compiler error fixes --- source/blender/blenkernel/intern/pbvh.cc | 515 ++---------------- .../blender/blenkernel/intern/pbvh_bmesh.cc | 4 +- source/blender/bmesh/intern/bmesh_log.cc | 3 + 3 files changed, 41 insertions(+), 481 deletions(-) diff --git a/source/blender/blenkernel/intern/pbvh.cc b/source/blender/blenkernel/intern/pbvh.cc index a74c6271027..115813e6924 100644 --- a/source/blender/blenkernel/intern/pbvh.cc +++ b/source/blender/blenkernel/intern/pbvh.cc @@ -1181,9 +1181,6 @@ void BKE_pbvh_free(PBVH *pbvh) BKE_pbvh_bmesh_free_tris(pbvh, node); } -#ifdef PROXY_ADVANCED - BKE_pbvh_free_proxyarray(pbvh, node); -#endif pbvh_node_pixels_free(node); } } @@ -1640,11 +1637,10 @@ static void pbvh_faces_update_normals(PBVH *pbvh, PBVHNode **nodes, int totnode) * can only update vertices marked in the `vert_bitmap`. */ - PBVHUpdateData data = { - .pbvh = pbvh, - .nodes = nodes, - .vert_normals = pbvh->vert_normals, - }; + PBVHUpdateData data = {}; + data.pbvh = pbvh; + data.nodes = nodes; + data.vert_normals = pbvh->vert_normals; TaskParallelSettings settings; BKE_pbvh_parallel_range_settings(&settings, true, totnode); @@ -1693,11 +1689,10 @@ static void pbvh_update_mask_redraw_task_cb(void *__restrict userdata, static void pbvh_update_mask_redraw(PBVH *pbvh, PBVHNode **nodes, int totnode, int flag) { - PBVHUpdateData data = { - .pbvh = pbvh, - .nodes = nodes, - .flag = flag, - }; + PBVHUpdateData data = {}; + data.pbvh = pbvh; + data.nodes = nodes; + data.flag = flag; TaskParallelSettings settings; BKE_pbvh_parallel_range_settings(&settings, true, totnode); @@ -1730,11 +1725,10 @@ static void pbvh_update_visibility_redraw_task_cb(void *__restrict userdata, static void pbvh_update_visibility_redraw(PBVH *pbvh, PBVHNode **nodes, int totnode, int flag) { - PBVHUpdateData data = { - .pbvh = pbvh, - .nodes = nodes, - .flag = flag, - }; + PBVHUpdateData data = {}; + data.pbvh = pbvh; + data.nodes = nodes; + data.flag = flag; TaskParallelSettings settings; BKE_pbvh_parallel_range_settings(&settings, true, totnode); @@ -1760,11 +1754,10 @@ static void pbvh_update_BB_redraw_task_cb(void *__restrict userdata, void pbvh_update_BB_redraw(PBVH *pbvh, PBVHNode **nodes, int totnode, int flag) { /* update BB, redraw flag */ - PBVHUpdateData data = { - .pbvh = pbvh, - .nodes = nodes, - .flag = flag, - }; + PBVHUpdateData data = {}; + data.pbvh = pbvh; + data.nodes = nodes; + data.flag = flag; TaskParallelSettings settings; BKE_pbvh_parallel_range_settings(&settings, true, totnode); @@ -1921,8 +1914,11 @@ static void pbvh_update_draw_buffers( } /* Parallel creation and update of draw buffers. */ - PBVHUpdateData data = { - .pbvh = pbvh, .nodes = nodes, .flat_vcol_shading = pbvh->flat_vcol_shading, .mesh = me}; + PBVHUpdateData data = {}; + data.pbvh = pbvh; + data.nodes = nodes; + data.mesh = me; + data.flat_vcol_shading = pbvh->flat_vcol_shading; TaskParallelSettings settings; BKE_pbvh_parallel_range_settings(&settings, true, totnode); @@ -2126,10 +2122,9 @@ static void pbvh_update_visibility_task_cb(void *__restrict userdata, static void pbvh_update_visibility(PBVH *pbvh, PBVHNode **nodes, int totnode) { - PBVHUpdateData data = { - .pbvh = pbvh, - .nodes = nodes, - }; + PBVHUpdateData data = {}; + data.pbvh = pbvh; + data.nodes = nodes; TaskParallelSettings settings; BKE_pbvh_parallel_range_settings(&settings, true, totnode); @@ -3453,8 +3448,11 @@ void BKE_pbvh_draw_cb(PBVH *pbvh, /* Search for nodes that need updates. */ if (update_only_visible) { /* Get visible nodes with draw updates. */ - PBVHDrawSearchData data = { - .frustum = update_frustum, .accum_update_flag = 0, attrs, attrs_num}; + PBVHDrawSearchData data = {}; + data.frustum = update_frustum; + data.accum_update_flag = 0; + data.attrs = attrs; + data.attrs_num = attrs_num; BKE_pbvh_search_gather(pbvh, pbvh_draw_search_cb, &data, &nodes, &totnode); update_flag = data.accum_update_flag; } @@ -3475,7 +3473,10 @@ void BKE_pbvh_draw_cb(PBVH *pbvh, MEM_SAFE_FREE(nodes); /* Draw visible nodes. */ - PBVHDrawSearchData draw_data = {.frustum = draw_frustum, .accum_update_flag = 0}; + PBVHDrawSearchData draw_data = {}; + draw_data.frustum = draw_frustum; + draw_data.accum_update_flag = 0; + BKE_pbvh_search_gather(pbvh, pbvh_draw_search_cb, &draw_data, &nodes, &totnode); PBVH_GPU_Args args; @@ -3891,450 +3892,6 @@ PBVHNode *BKE_pbvh_node_from_index(PBVH *pbvh, int node_i) return pbvh->nodes + node_i; } -#ifdef PROXY_ADVANCED -// TODO: if this really works, make sure to pull the neighbor iterator out of sculpt.c and put it -// here -/* clang-format off */ -# include "BKE_context.h" -# include "DNA_object_types.h" -# include "DNA_scene_types.h" -# include "../../editors/sculpt_paint/sculpt_intern.h" -/* clang-format on */ - -int checkalloc(void **data, int esize, int oldsize, int newsize, int emask, int umask) -{ - // update channel if it already was allocated once, or is requested by umask - if (newsize != oldsize && (*data || (emask & umask))) { - if (*data) { - *data = MEM_reallocN(*data, newsize * esize); - } - else { - *data = MEM_mallocN(newsize * esize, "pbvh proxy vert arrays"); - } - return emask; - } - - return 0; -} - -void BKE_pbvh_ensure_proxyarray_indexmap(PBVH *pbvh, PBVHNode *node, GHash *vert_node_map) -{ - ProxyVertArray *p = &node->proxyverts; - - int totvert = 0; - BKE_pbvh_node_num_verts(pbvh, node, &totvert, nullptr); - - bool update = !p->indexmap || p->size != totvert; - update = update || (p->indexmap && BLI_ghash_len(p->indexmap) != totvert); - - if (!update) { - return; - } - - if (p->indexmap) { - BLI_ghash_free(p->indexmap, nullptr, nullptr); - } - - GHash *gs = p->indexmap = BLI_ghash_ptr_new("BKE_pbvh_ensure_proxyarray_indexmap"); - - PBVHVertexIter vd; - - int i = 0; - BKE_pbvh_vertex_iter_begin (pbvh, node, vd, PBVH_ITER_UNIQUE) { - BLI_ghash_insert(gs, (void *)vd.vertex.i, (void *)i); - i++; - } - BKE_pbvh_vertex_iter_end; -} - -bool pbvh_proxyarray_needs_update(PBVH *pbvh, PBVHNode *node, int mask) -{ - ProxyVertArray *p = &node->proxyverts; - int totvert = 0; - - if (!(node->flag & PBVH_Leaf) || !node->bm_unique_verts) { - return false; - } - - BKE_pbvh_node_num_verts(pbvh, node, &totvert, nullptr); - - bool bad = p->size != totvert; - bad = bad || ((mask & PV_NEIGHBORS) && p->neighbors_dirty); - bad = bad || (p->datamask & mask) != mask; - - bad = bad && totvert > 0; - - return bad; -} - -GHash *pbvh_build_vert_node_map(PBVH *pbvh, PBVHNode **nodes, int totnode, int mask) -{ - GHash *vert_node_map = BLI_ghash_ptr_new("BKE_pbvh_ensure_proxyarrays"); - - for (int i = 0; i < totnode; i++) { - PBVHVertexIter vd; - PBVHNode *node = nodes[i]; - - if (!(node->flag & PBVH_Leaf)) { - continue; - } - - BKE_pbvh_vertex_iter_begin (pbvh, node, vd, PBVH_ITER_UNIQUE) { - BLI_ghash_insert(vert_node_map, (void *)vd.vertex.i, (void *)(node - pbvh->nodes)); - } - BKE_pbvh_vertex_iter_end; - } - - return vert_node_map; -} - -void BKE_pbvh_ensure_proxyarrays( - SculptSession *ss, PBVH *pbvh, PBVHNode **nodes, int totnode, int mask) -{ - - bool update = false; - - for (int i = 0; i < totnode; i++) { - if (pbvh_proxyarray_needs_update(pbvh, nodes[i], mask)) { - update = true; - break; - } - } - - if (!update) { - return; - } - - GHash *vert_node_map = pbvh_build_vert_node_map(pbvh, nodes, totnode, mask); - - for (int i = 0; i < totnode; i++) { - if (nodes[i]->flag & PBVH_Leaf) { - BKE_pbvh_ensure_proxyarray_indexmap(pbvh, nodes[i], vert_node_map); - } - } - - for (int i = 0; i < totnode; i++) { - if (nodes[i]->flag & PBVH_Leaf) { - BKE_pbvh_ensure_proxyarray(ss, pbvh, nodes[i], mask, vert_node_map, false, false); - } - } - - if (vert_node_map) { - BLI_ghash_free(vert_node_map, nullptr, nullptr); - } -} - -void BKE_pbvh_ensure_proxyarray(SculptSession *ss, - PBVH *pbvh, - PBVHNode *node, - int mask, - GHash *vert_node_map, - bool check_indexmap, - bool force_update) -{ - ProxyVertArray *p = &node->proxyverts; - - if (check_indexmap) { - BKE_pbvh_ensure_proxyarray_indexmap(pbvh, node, vert_node_map); - } - - GHash *gs = p->indexmap; - - int totvert = 0; - BKE_pbvh_node_num_verts(pbvh, node, &totvert, nullptr); - - if (!totvert) { - return; - } - - int updatemask = 0; - -# define UPDATETEST(name, emask, esize) \ - if (mask & emask) { \ - updatemask |= checkalloc((void **)&p->name, esize, p->size, totvert, emask, mask); \ - } - - UPDATETEST(ownerco, PV_OWNERCO, sizeof(void *)) - UPDATETEST(ownerno, PV_OWNERNO, sizeof(void *)) - UPDATETEST(ownermask, PV_OWNERMASK, sizeof(void *)) - UPDATETEST(ownercolor, PV_OWNERCOLOR, sizeof(void *)) - UPDATETEST(co, PV_CO, sizeof(float) * 3) - UPDATETEST(no, PV_NO, sizeof(short) * 3) - UPDATETEST(fno, PV_NO, sizeof(float) * 3) - UPDATETEST(mask, PV_MASK, sizeof(float)) - UPDATETEST(color, PV_COLOR, sizeof(float) * 4) - UPDATETEST(index, PV_INDEX, sizeof(PBVHVertRef)) - UPDATETEST(neighbors, PV_NEIGHBORS, sizeof(ProxyKey) * MAX_PROXY_NEIGHBORS) - - p->size = totvert; - - if (force_update) { - updatemask |= mask; - } - - if ((mask & PV_NEIGHBORS) && p->neighbors_dirty) { - updatemask |= PV_NEIGHBORS; - } - - if (!updatemask) { - return; - } - - p->datamask |= mask; - - PBVHVertexIter vd; - - int i = 0; - -# if 1 - BKE_pbvh_vertex_iter_begin (pbvh, node, vd, PBVH_ITER_UNIQUE) { - void **val; - - if (!BLI_ghash_ensure_p(gs, (void *)vd.vertex.i, &val)) { - *val = (void *)i; - }; - i++; - } - BKE_pbvh_vertex_iter_end; -# endif - - if (updatemask & PV_NEIGHBORS) { - p->neighbors_dirty = false; - } - - i = 0; - BKE_pbvh_vertex_iter_begin (pbvh, node, vd, PBVH_ITER_UNIQUE) { - if (i >= p->size) { - printf("error!! %s\n", __func__); - break; - } - - if (updatemask & PV_OWNERCO) { - p->ownerco[i] = vd.co; - } - if (updatemask & PV_INDEX) { - p->index[i] = vd.vertex; - } - if (updatemask & PV_OWNERNO) { - p->ownerno[i] = vd.no; - } - if (updatemask & PV_NO) { - if (vd.fno) { - if (p->fno) { - copy_v3_v3(p->fno[i], vd.fno); - } - normal_float_to_short_v3(p->no[i], vd.fno); - } - else if (vd.no) { - copy_v3_v3_short(p->no[i], vd.no); - if (p->fno) { - normal_short_to_float_v3(p->fno[i], vd.no); - } - } - else { - p->no[i][0] = p->no[i][1] = p->no[i][2] = 0; - if (p->fno) { - zero_v3(p->fno[i]); - } - } - } - if (updatemask & PV_CO) { - copy_v3_v3(p->co[i], vd.co); - } - if (updatemask & PV_OWNERMASK) { - p->ownermask[i] = vd.mask; - } - if (updatemask & PV_MASK) { - p->mask[i] = vd.mask ? *vd.mask : 0.0f; - } - if (updatemask & PV_COLOR) { - if (vd.vcol) { - copy_v4_v4(p->color[i], vd.vcol->color); - } - } - - if (updatemask & PV_NEIGHBORS) { - int j = 0; - SculptVertexNeighborIter ni; - - SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.vertex, ni) { - if (j >= MAX_PROXY_NEIGHBORS - 1) { - break; - } - - ProxyKey key; - - int *pindex = (int *)BLI_ghash_lookup_p(gs, (void *)ni.vertex.i); - - if (!pindex) { - if (vert_node_map) { - int *nindex = (int *)BLI_ghash_lookup_p(vert_node_map, (void *)ni.vertex.i); - - if (!nindex) { - p->neighbors_dirty = true; - continue; - } - - PBVHNode *node2 = pbvh->nodes + *nindex; - if (node2->proxyverts.indexmap) { - pindex = (int *)BLI_ghash_lookup_p(node2->proxyverts.indexmap, (void *)ni.vertex.i); - } - else { - pindex = nullptr; - } - - if (!pindex) { - p->neighbors_dirty = true; - continue; - } - - key.node = (int)(node2 - pbvh->nodes); - key.pindex = *pindex; - //* - if (node2->proxyverts.size != 0 && - (key.pindex < 0 || key.pindex >= node2->proxyverts.size)) { - printf("error! %s\n", __func__); - fflush(stdout); - p->neighbors_dirty = true; - continue; - } - //*/ - } - else { - p->neighbors_dirty = true; - continue; - } - } - else { - key.node = (int)(node - pbvh->nodes); - key.pindex = *pindex; - } - - p->neighbors[i][j++] = key; - } - SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); - - p->neighbors[i][j].node = -1; - } - - i++; - } - BKE_pbvh_vertex_iter_end; -} - -typedef struct GatherProxyThread { - PBVHNode **nodes; - PBVH *pbvh; - int mask; -} GatherProxyThread; - -static void pbvh_load_proxyarray_exec(void *__restrict userdata, - const int n, - const TaskParallelTLS *__restrict tls) -{ - GatherProxyThread *data = (GatherProxyThread *)userdata; - PBVHNode *node = data->nodes[n]; - PBVHVertexIter vd; - ProxyVertArray *p = &node->proxyverts; - int i = 0; - - int mask = p->datamask; - - BKE_pbvh_ensure_proxyarray(nullptr, data->pbvh, node, data->mask, nullptr, false, true); -} - -void BKE_pbvh_load_proxyarrays(PBVH *pbvh, PBVHNode **nodes, int totnode, int mask) -{ - GatherProxyThread data = {.nodes = nodes, .pbvh = pbvh, .mask = mask}; - - mask = mask & ~PV_NEIGHBORS; // don't update neighbors in threaded code? - - TaskParallelSettings settings; - BKE_pbvh_parallel_range_settings(&settings, true, totnode); - BLI_task_parallel_range(0, totnode, &data, pbvh_load_proxyarray_exec, &settings); -} - -static void pbvh_gather_proxyarray_exec(void *__restrict userdata, - const int n, - const TaskParallelTLS *__restrict tls) -{ - GatherProxyThread *data = (GatherProxyThread *)userdata; - PBVHNode *node = data->nodes[n]; - PBVHVertexIter vd; - ProxyVertArray *p = &node->proxyverts; - int i = 0; - - int mask = p->datamask; - - BKE_pbvh_vertex_iter_begin (data->pbvh, node, vd, PBVH_ITER_UNIQUE) { - if (mask & PV_CO) { - copy_v3_v3(vd.co, p->co[i]); - } - - if (mask & PV_COLOR && vd.col) { - copy_v4_v4(vd.col, p->color[i]); - } - - if (vd.mask && (mask & PV_MASK)) { - *vd.mask = p->mask[i]; - } - - i++; - } - BKE_pbvh_vertex_iter_end; -} - -void BKE_pbvh_gather_proxyarray(PBVH *pbvh, PBVHNode **nodes, int totnode) -{ - GatherProxyThread data = {.nodes = nodes, .pbvh = pbvh}; - - TaskParallelSettings settings; - BKE_pbvh_parallel_range_settings(&settings, true, totnode); - BLI_task_parallel_range(0, totnode, &data, pbvh_gather_proxyarray_exec, &settings); -} - -void BKE_pbvh_free_proxyarray(PBVH *pbvh, PBVHNode *node) -{ - ProxyVertArray *p = &node->proxyverts; - - if (p->indexmap) { - BLI_ghash_free(p->indexmap, nullptr, nullptr); - } - if (p->co) - MEM_freeN(p->co); - if (p->no) - MEM_freeN(p->no); - if (p->index) - MEM_freeN(p->index); - if (p->mask) - MEM_freeN(p->mask); - if (p->ownerco) - MEM_freeN(p->ownerco); - if (p->ownerno) - MEM_freeN(p->ownerno); - if (p->ownermask) - MEM_freeN(p->ownermask); - if (p->ownercolor) - MEM_freeN(p->ownercolor); - if (p->color) - MEM_freeN(p->color); - if (p->neighbors) - MEM_freeN(p->neighbors); - - memset(p, 0, sizeof(*p)); -} - -void BKE_pbvh_update_proxyvert(PBVH *pbvh, PBVHNode *node, ProxyVertUpdateRec *rec) -{ -} - -ProxyVertArray *BKE_pbvh_get_proxyarrays(PBVH *pbvh, PBVHNode *node) -{ - return &node->proxyverts; -} - -#endif - /* checks if pbvh needs to sync its flat vcol shading flag with scene tool settings scene and ob are allowd to be nullptr (in which case nothing is done). */ @@ -4603,8 +4160,8 @@ void BKE_pbvh_get_vert_face_areas(PBVH *pbvh, PBVHVertRef vertex, float *r_areas switch (BKE_pbvh_type(pbvh)) { case PBVH_FACES: { - int *edges = BLI_array_alloca(edges, 16); - int *polys = BLI_array_alloca(polys, 32); + int *edges = (int *)BLI_array_alloca(edges, 16); + int *polys = (int *)BLI_array_alloca(polys, 32); bool heap_alloc = false; int len = 16; @@ -4615,7 +4172,7 @@ void BKE_pbvh_get_vert_face_areas(PBVH *pbvh, PBVHVertRef vertex, float *r_areas /* sort poly references by vemap edge ordering */ MeshElemMap *emap = pbvh->vemap + vertex.i; - int *polys_old = BLI_array_alloca(polys, len * 2); + int *polys_old = (int *)BLI_array_alloca(polys, len * 2); memcpy((void *)polys_old, (void *)polys, sizeof(int) * len * 2); /* note that wire edges will break this, but diff --git a/source/blender/blenkernel/intern/pbvh_bmesh.cc b/source/blender/blenkernel/intern/pbvh_bmesh.cc index f3942830fe7..3df3590d494 100644 --- a/source/blender/blenkernel/intern/pbvh_bmesh.cc +++ b/source/blender/blenkernel/intern/pbvh_bmesh.cc @@ -2999,7 +2999,7 @@ void BKE_pbvh_bmesh_update_all_valence(PBVH *pbvh) BMVert *v; BM_ITER_MESH (v, &iter, pbvh->header.bm, BM_VERTS_OF_MESH) { - BKE_pbvh_bmesh_update_valence(pbvh->cd_sculpt_vert, (PBVHVertRef){(intptr_t)v}); + BKE_pbvh_bmesh_update_valence(pbvh->cd_sculpt_vert, BKE_pbvh_make_vref((intptr_t)v)); } } @@ -3024,7 +3024,7 @@ void BKE_pbvh_bmesh_on_mesh_change(PBVH *pbvh) *flags |= SCULPT_BOUNDARY_NEEDS_UPDATE; MV_ADD_FLAG(mv, SCULPTVERT_NEED_DISK_SORT | SCULPTVERT_NEED_TRIANGULATE); - BKE_pbvh_bmesh_update_valence(pbvh->cd_sculpt_vert, (PBVHVertRef){.i = (intptr_t)v}); + BKE_pbvh_bmesh_update_valence(pbvh->cd_sculpt_vert, BKE_pbvh_make_vref((intptr_t)v)); } } diff --git a/source/blender/bmesh/intern/bmesh_log.cc b/source/blender/bmesh/intern/bmesh_log.cc index d5a08626179..cc33311028c 100644 --- a/source/blender/bmesh/intern/bmesh_log.cc +++ b/source/blender/bmesh/intern/bmesh_log.cc @@ -1635,3 +1635,6 @@ bool BM_log_entry_drop(BMLogEntry *entry) return true; } +void BM_log_print_entry(BMLog *log, BMLogEntry *entry) +{ +} -- 2.30.2 From 555b703afae2bcf57de6f53fc58c2e882a83a7e4 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Sat, 11 Feb 2023 15:18:59 -0800 Subject: [PATCH 018/279] temp-sculpt-dyntopo: ANother compiler error --- source/blender/blenkernel/intern/pbvh.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/source/blender/blenkernel/intern/pbvh.cc b/source/blender/blenkernel/intern/pbvh.cc index 115813e6924..83e7e255dbf 100644 --- a/source/blender/blenkernel/intern/pbvh.cc +++ b/source/blender/blenkernel/intern/pbvh.cc @@ -2876,8 +2876,8 @@ static bool pbvh_faces_node_raycast(PBVH *pbvh, if (j == 0 || len_squared_v3v3(location, co[j]) < len_squared_v3v3(location, nearest_vertex_co)) { copy_v3_v3(nearest_vertex_co, co[j]); - *r_active_vertex_index = (PBVHVertRef){.i = mloop[lt->tri[j]].v}; - *r_active_face_index = (PBVHFaceRef){.i = lt->poly}; + r_active_vertex_index->i = mloop[lt->tri[j]].v; + r_active_face_index->i = lt->poly; } } } @@ -4636,7 +4636,7 @@ bool BKE_pbvh_cache_is_valid(const struct Object *ob, return ok; } -GHash *cached_pbvhs = nullptr; +static GHash *cached_pbvhs = nullptr; static void pbvh_clear_cached_pbvhs(PBVH *exclude) { Vector pbvhs; -- 2.30.2 From 185d06ae74d8ba09db06afc69ec999a54cb73516 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Thu, 16 Feb 2023 12:03:25 -0800 Subject: [PATCH 019/279] temp-sculpt-dyntopo: Remove unrelated multres changes and old ID api --- source/blender/blenkernel/BKE_mesh.h | 7 +-- .../blender/blenkernel/intern/idprop_utils.c | 2 +- source/blender/blenkernel/intern/mesh.cc | 5 +- .../blender/blenkernel/intern/subdiv_ccg.cc | 36 ----------- source/blender/blenlib/BLI_strict_flags.h | 1 - source/blender/blenlib/intern/BLI_mempool.c | 1 - source/blender/editors/mesh/mesh_data.cc | 60 ------------------- source/blender/editors/mesh/mesh_intern.h | 1 - source/blender/editors/mesh/mesh_ops.c | 1 - source/blender/editors/mesh/meshtools.cc | 36 ++++++++--- .../io/alembic/exporter/abc_writer_mesh.cc | 6 +- .../wavefront_obj/exporter/obj_export_mesh.cc | 3 +- source/blender/modifiers/intern/MOD_bevel.c | 3 +- .../blender/modifiers/intern/MOD_decimate.c | 3 +- .../blender/modifiers/intern/MOD_edgesplit.c | 3 +- .../modifiers/intern/MOD_triangulate.cc | 2 +- .../blender/modifiers/intern/MOD_wireframe.c | 3 +- .../geometry/nodes/node_geo_triangulate.cc | 2 +- source/blender/render/RE_bake.h | 1 - source/blender/render/intern/bake.cc | 6 -- 20 files changed, 42 insertions(+), 140 deletions(-) diff --git a/source/blender/blenkernel/BKE_mesh.h b/source/blender/blenkernel/BKE_mesh.h index abc7ae12ff6..6b4553f7246 100644 --- a/source/blender/blenkernel/BKE_mesh.h +++ b/source/blender/blenkernel/BKE_mesh.h @@ -6,10 +6,6 @@ * \ingroup bke */ -#include "BKE_attribute.h" -#include "BKE_customdata.h" -#include "BKE_mesh_types.h" - #include "BLI_compiler_attrs.h" #include "BLI_compiler_compat.h" #include "BLI_utildefines.h" @@ -80,8 +76,7 @@ void BKE_mesh_tag_edges_split(struct Mesh *mesh); /* *** mesh.c *** */ -struct BMesh *BKE_mesh_to_bmesh_ex(const struct Object *ob, - const struct Mesh *me, +struct BMesh *BKE_mesh_to_bmesh_ex(const struct Mesh *me, const struct BMeshCreateParams *create_params, const struct BMeshFromMeshParams *convert_params); struct BMesh *BKE_mesh_to_bmesh(struct Mesh *me, diff --git a/source/blender/blenkernel/intern/idprop_utils.c b/source/blender/blenkernel/intern/idprop_utils.c index e9eeb189ee2..0ade0aa654e 100644 --- a/source/blender/blenkernel/intern/idprop_utils.c +++ b/source/blender/blenkernel/intern/idprop_utils.c @@ -182,7 +182,7 @@ static void idp_repr_fn_recursive(struct ReprState *state, const IDProperty *pro const ID *id = prop->data.pointer; if (id != NULL) { STR_APPEND_STR("bpy.data."); - STR_APPEND_STR(BKE_idtype_idcode_to_name_plural((short)GS(id->name))); + STR_APPEND_STR(BKE_idtype_idcode_to_name_plural(GS(id->name))); STR_APPEND_STR("["); STR_APPEND_STR_QUOTE(id->name + 2); STR_APPEND_STR("]"); diff --git a/source/blender/blenkernel/intern/mesh.cc b/source/blender/blenkernel/intern/mesh.cc index f709328a01d..1fece650a73 100644 --- a/source/blender/blenkernel/intern/mesh.cc +++ b/source/blender/blenkernel/intern/mesh.cc @@ -1136,8 +1136,7 @@ Mesh *BKE_mesh_copy_for_eval(const Mesh *source, bool reference) return result; } -BMesh *BKE_mesh_to_bmesh_ex(const Object *ob, - const Mesh *me, +BMesh *BKE_mesh_to_bmesh_ex(const Mesh *me, const struct BMeshCreateParams *create_params, const struct BMeshFromMeshParams *convert_params) { @@ -1160,7 +1159,7 @@ BMesh *BKE_mesh_to_bmesh(Mesh *me, bmesh_from_mesh_params.add_key_index = add_key_index; bmesh_from_mesh_params.use_shapekey = true; bmesh_from_mesh_params.active_shapekey = ob->shapenr; - return BKE_mesh_to_bmesh_ex(ob, me, params, &bmesh_from_mesh_params); + return BKE_mesh_to_bmesh_ex(me, params, &bmesh_from_mesh_params); } Mesh *BKE_mesh_from_bmesh_nomain(BMesh *bm, diff --git a/source/blender/blenkernel/intern/subdiv_ccg.cc b/source/blender/blenkernel/intern/subdiv_ccg.cc index 93207552ba0..bf09be444b1 100644 --- a/source/blender/blenkernel/intern/subdiv_ccg.cc +++ b/source/blender/blenkernel/intern/subdiv_ccg.cc @@ -17,8 +17,6 @@ #include "BLI_math_vector.h" #include "BLI_task.h" -#include "multires_inline.h" - #include "BKE_DerivedMesh.h" #include "BKE_ccg.h" #include "BKE_global.h" @@ -2102,38 +2100,4 @@ void BKE_subdiv_ccg_eval_limit_point(const SubdivCCG *subdiv_ccg, BKE_subdiv_eval_limit_point(subdiv, ptex_face_index, u, v, r_point); } -void BKE_subdiv_ccg_eval_limit_point_and_derivatives(const SubdivCCG *subdiv_ccg, - const SubdivCCGCoord *coord, - float r_point[3], - float r_dPdu[3], - float r_dPdv[3]) -{ - Subdiv *subdiv = subdiv_ccg->subdiv; - int ptex_face_index; - float u, v; - subdiv_ccg_coord_to_ptex_coord(subdiv_ccg, coord, &ptex_face_index, &u, &v); - BKE_subdiv_eval_limit_point_and_derivatives( - subdiv, ptex_face_index, u, v, r_point, r_dPdu, r_dPdv); -} - -void BKE_subdiv_ccg_get_tangent_matrix(const SubdivCCG *subdiv_ccg, - const SubdivCCGCoord *coord, - float mat[3][3], - float r_point[3]) -{ - int ptex_face_index; - float u, v; - float du[3], dv[3]; - - const int face_index = BKE_subdiv_ccg_grid_to_face_index(subdiv_ccg, coord->grid_index); - const SubdivCCGFace *faces = subdiv_ccg->faces; - const SubdivCCGFace *face = &faces[face_index]; - const float corner = coord->grid_index - face->start_grid_index; - - subdiv_ccg_coord_to_ptex_coord(subdiv_ccg, coord, &ptex_face_index, &u, &v); - - BKE_subdiv_ccg_eval_limit_point_and_derivatives(subdiv_ccg, coord, r_point, du, dv); - BKE_multires_construct_tangent_matrix(mat, du, dv, corner); -} - /** \} */ diff --git a/source/blender/blenlib/BLI_strict_flags.h b/source/blender/blenlib/BLI_strict_flags.h index 5224723f109..eb3fac8ec33 100644 --- a/source/blender/blenlib/BLI_strict_flags.h +++ b/source/blender/blenlib/BLI_strict_flags.h @@ -33,7 +33,6 @@ # pragma clang diagnostic error "-Wsign-conversion" # pragma clang diagnostic error "-Wsign-compare" # pragma clang diagnostic error "-Wimplicit-float-conversion" -# pragma clang diagnostic error "-Wimplicit-int-conversion" # pragma clang diagnostic error "-Wimplicit-int" # pragma clang diagnostic error "-Wshadow" /* Normal MSVC */ diff --git a/source/blender/blenlib/intern/BLI_mempool.c b/source/blender/blenlib/intern/BLI_mempool.c index 48d7175ea42..7deb5379a5c 100644 --- a/source/blender/blenlib/intern/BLI_mempool.c +++ b/source/blender/blenlib/intern/BLI_mempool.c @@ -21,7 +21,6 @@ #include "BLI_string.h" #include "BLI_utildefines.h" -# #include "BLI_asan.h" #include "BLI_mempool.h" /* own include */ #include "BLI_mempool_private.h" /* own include */ diff --git a/source/blender/editors/mesh/mesh_data.cc b/source/blender/editors/mesh/mesh_data.cc index 54cc6518647..b883cdbe60d 100644 --- a/source/blender/editors/mesh/mesh_data.cc +++ b/source/blender/editors/mesh/mesh_data.cc @@ -714,66 +714,6 @@ static int mesh_customdata_add_exec__internal(bContext *C, char htype, int type) return CustomData_has_layer(data, type) ? OPERATOR_FINISHED : OPERATOR_CANCELLED; } -/* Clear Mask */ -static bool mesh_customdata_ids_clear_poll(bContext *C) -{ - Object *ob = ED_object_context(C); - if (ob && ob->type == OB_MESH) { - Mesh *me = (Mesh *)ob->data; - - if (me->edit_mesh) { - return false; - } - - /* special case - can't run this if we're in sculpt mode */ - if (ob->mode & OB_MODE_SCULPT) { - return false; - } - - if (!ID_IS_LINKED(me)) { - bool ret = CustomData_has_layer(GET_CD_DATA(me, vdata), CD_MESH_ID); - ret |= CustomData_has_layer(GET_CD_DATA(me, edata), CD_MESH_ID); - ret |= CustomData_has_layer(GET_CD_DATA(me, ldata), CD_MESH_ID); - ret |= CustomData_has_layer(GET_CD_DATA(me, pdata), CD_MESH_ID); - - return ret; - } - } - return false; -} -static int mesh_customdata_ids_clear_exec(bContext *C, wmOperator * /* op */) -{ - bool ret = false; - - for (int i = 0; i < 4; i++) { - int type = 1 << i; - - ret |= mesh_customdata_clear_exec__internal(C, type, CD_MESH_ID); - } - - if (ret) { - return OPERATOR_FINISHED; - } - - return OPERATOR_CANCELLED; -} - -void MESH_OT_customdata_ids_clear(wmOperatorType *ot) -{ - - /* identifiers */ - ot->name = "Clear Element ID Data"; - ot->idname = "MESH_OT_customdata_ids_clear"; - ot->description = "Clear element ID layers"; - - /* api callbacks */ - ot->exec = mesh_customdata_ids_clear_exec; - ot->poll = mesh_customdata_ids_clear_poll; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; -} - /* Clear Mask */ static bool mesh_customdata_mask_clear_poll(bContext *C) { diff --git a/source/blender/editors/mesh/mesh_intern.h b/source/blender/editors/mesh/mesh_intern.h index 85c3dbc1d22..4d6d720abe6 100644 --- a/source/blender/editors/mesh/mesh_intern.h +++ b/source/blender/editors/mesh/mesh_intern.h @@ -324,7 +324,6 @@ void MESH_OT_customdata_skin_clear(struct wmOperatorType *ot); void MESH_OT_customdata_custom_splitnormals_add(struct wmOperatorType *ot); void MESH_OT_customdata_custom_splitnormals_clear(struct wmOperatorType *ot); void MESH_OT_dump_mres_grids(struct wmOperatorType *ot); -void MESH_OT_customdata_ids_clear(struct wmOperatorType *ot); void MESH_OT_customdata_bevel_weight_vertex_add(struct wmOperatorType *ot); void MESH_OT_customdata_bevel_weight_vertex_clear(struct wmOperatorType *ot); void MESH_OT_customdata_bevel_weight_edge_add(struct wmOperatorType *ot); diff --git a/source/blender/editors/mesh/mesh_ops.c b/source/blender/editors/mesh/mesh_ops.c index a1bb76b9e1a..d92bf2e4f80 100644 --- a/source/blender/editors/mesh/mesh_ops.c +++ b/source/blender/editors/mesh/mesh_ops.c @@ -135,7 +135,6 @@ void ED_operatortypes_mesh(void) WM_operatortype_append(MESH_OT_uv_texture_add); WM_operatortype_append(MESH_OT_uv_texture_remove); WM_operatortype_append(MESH_OT_customdata_mask_clear); - WM_operatortype_append(MESH_OT_customdata_ids_clear); WM_operatortype_append(MESH_OT_customdata_skin_add); WM_operatortype_append(MESH_OT_customdata_skin_clear); WM_operatortype_append(MESH_OT_customdata_custom_splitnormals_add); diff --git a/source/blender/editors/mesh/meshtools.cc b/source/blender/editors/mesh/meshtools.cc index 243845718bb..ca87821e5a3 100644 --- a/source/blender/editors/mesh/meshtools.cc +++ b/source/blender/editors/mesh/meshtools.cc @@ -66,6 +66,22 @@ using blender::Span; /* join selected meshes into the active mesh, context sensitive * return 0 if no join is made (error) and 1 if the join is done */ +static const char *get_id_name(int elemtype) +{ + switch (elemtype) { + case BM_VERT: + return "vertex_id"; + case BM_EDGE: + return "edge_id"; + case BM_LOOP: + return "loop_id"; + case BM_FACE: + return "face_id"; + default: + return nullptr; + } +} + static void get_id_range(Mesh *mesh, CustomData *vdata, CustomData *edata, @@ -83,7 +99,8 @@ static void get_id_range(Mesh *mesh, int min_id = 0, max_id = 0; for (int i = 0; i < 4; i++) { - int *ids = (int *)CustomData_get_layer(datas[i], CD_MESH_ID); + const int *ids = (const int *)CustomData_get_layer_named( + datas[i], CD_PROP_INT32, get_id_name(1 << i)); if (!ids) { continue; } @@ -124,7 +141,7 @@ static void handle_missing_id_layers(Mesh *src, int totpoly) { const CustomData *src_datas[4] = {&src->vdata, &src->edata, &src->ldata, &src->pdata}; - const CustomData *dst_datas[4] = {vdata, edata, ldata, pdata}; + CustomData *dst_datas[4] = {vdata, edata, ldata, pdata}; int srctots[4] = {src->totvert, src->totedge, src->totloop, src->totpoly}; int dsttots[4] = {totvert, totedge, totloop, totpoly}; @@ -157,17 +174,19 @@ static void handle_missing_id_layers(Mesh *src, for (int i = 0; i < 4; i++) { const CustomData *srcdata = src_datas[i]; - const CustomData *dstdata = dst_datas[i]; + CustomData *dstdata = dst_datas[i]; - const bool haveid_src = CustomData_has_layer(srcdata, CD_MESH_ID); - const bool haveid_dst = CustomData_has_layer(dstdata, CD_MESH_ID); + const char *idname = get_id_name(1 << i); + const bool haveid_src = CustomData_get_layer_named(srcdata, CD_PROP_INT32, idname); + const bool haveid_dst = CustomData_get_layer_named(dstdata, CD_PROP_INT32, idname); if (haveid_dst && haveid_src) { // assign ids int offset = dst_range[1] - src_range[0] + 1; - int *srcids = (int *)CustomData_get_layer(srcdata, CD_MESH_ID); - int *dstids = (int *)CustomData_get_layer(dstdata, CD_MESH_ID); + const int *srcids = (const int *)CustomData_get_layer_named(srcdata, CD_PROP_INT32, idname); + int *dstids = (int *)CustomData_get_layer_named_for_write( + dstdata, CD_PROP_INT32, idname, dsttots[i]); int start = dsttots[i]; int end = start + srctots[i]; @@ -180,7 +199,8 @@ static void handle_missing_id_layers(Mesh *src, } else if (haveid_dst) { int curid = dst_range[1] + 1; - int *dstids = (int *)CustomData_get_layer(dstdata, CD_MESH_ID); + int *dstids = (int *)CustomData_get_layer_named_for_write( + dstdata, CD_PROP_INT32, idname, dsttots[i]); int start = dsttots[i]; int end = start + srctots[i]; diff --git a/source/blender/io/alembic/exporter/abc_writer_mesh.cc b/source/blender/io/alembic/exporter/abc_writer_mesh.cc index 6a49fdc8a86..77d42d905c7 100644 --- a/source/blender/io/alembic/exporter/abc_writer_mesh.cc +++ b/source/blender/io/alembic/exporter/abc_writer_mesh.cc @@ -156,11 +156,11 @@ void ABCGenericMeshWriter::do_write(HierarchyContext &context) const int quad_method = args_.export_params->quad_method; const int ngon_method = args_.export_params->ngon_method; - BMeshCreateParams bmesh_create_params = {0}; - BMeshFromMeshParams bmesh_from_mesh_params = {0}; + BMeshCreateParams bmesh_create_params{}; + BMeshFromMeshParams bmesh_from_mesh_params{}; bmesh_from_mesh_params.calc_face_normal = true; bmesh_from_mesh_params.calc_vert_normal = true; - BMesh *bm = BKE_mesh_to_bmesh_ex(object, mesh, &bmesh_create_params, &bmesh_from_mesh_params); + BMesh *bm = BKE_mesh_to_bmesh_ex(mesh, &bmesh_create_params, &bmesh_from_mesh_params); BM_mesh_triangulate(bm, quad_method, ngon_method, 4, tag_only, nullptr, nullptr, nullptr); diff --git a/source/blender/io/wavefront_obj/exporter/obj_export_mesh.cc b/source/blender/io/wavefront_obj/exporter/obj_export_mesh.cc index 679beb07a0d..e2ad7de7291 100644 --- a/source/blender/io/wavefront_obj/exporter/obj_export_mesh.cc +++ b/source/blender/io/wavefront_obj/exporter/obj_export_mesh.cc @@ -112,8 +112,7 @@ void OBJMesh::triangulate_mesh_eval() * triangulated here. */ const int triangulate_min_verts = 4; - BMesh *bmesh = BKE_mesh_to_bmesh_ex( - nullptr, export_mesh_, &bm_create_params, &bm_convert_params); + BMesh *bmesh = BKE_mesh_to_bmesh_ex(export_mesh_, &bm_create_params, &bm_convert_params); BM_mesh_triangulate(bmesh, MOD_TRIANGULATE_NGON_BEAUTY, MOD_TRIANGULATE_QUAD_SHORTEDGE, diff --git a/source/blender/modifiers/intern/MOD_bevel.c b/source/blender/modifiers/intern/MOD_bevel.c index f509077d67a..f49888a17fd 100644 --- a/source/blender/modifiers/intern/MOD_bevel.c +++ b/source/blender/modifiers/intern/MOD_bevel.c @@ -108,8 +108,7 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * const float spread = bmd->spread; const bool invert_vgroup = (bmd->flags & MOD_BEVEL_INVERT_VGROUP) != 0; - bm = BKE_mesh_to_bmesh_ex(NULL, - mesh, + bm = BKE_mesh_to_bmesh_ex(mesh, &(struct BMeshCreateParams){0}, &(struct BMeshFromMeshParams){ .calc_face_normal = true, diff --git a/source/blender/modifiers/intern/MOD_decimate.c b/source/blender/modifiers/intern/MOD_decimate.c index a7174aa4493..cf698669397 100644 --- a/source/blender/modifiers/intern/MOD_decimate.c +++ b/source/blender/modifiers/intern/MOD_decimate.c @@ -158,8 +158,7 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * } } - bm = BKE_mesh_to_bmesh_ex(NULL, - mesh, + bm = BKE_mesh_to_bmesh_ex(mesh, &(struct BMeshCreateParams){0}, &(struct BMeshFromMeshParams){ .calc_face_normal = calc_face_normal, diff --git a/source/blender/modifiers/intern/MOD_edgesplit.c b/source/blender/modifiers/intern/MOD_edgesplit.c index a2bb42f1397..7db6c5e172d 100644 --- a/source/blender/modifiers/intern/MOD_edgesplit.c +++ b/source/blender/modifiers/intern/MOD_edgesplit.c @@ -53,8 +53,7 @@ Mesh *doEdgeSplit(const Mesh *mesh, EdgeSplitModifierData *emd) const bool do_split_all = do_split_angle && emd->split_angle < FLT_EPSILON; const bool calc_face_normals = do_split_angle && !do_split_all; - bm = BKE_mesh_to_bmesh_ex(NULL, - mesh, + bm = BKE_mesh_to_bmesh_ex(mesh, &(struct BMeshCreateParams){0}, &(struct BMeshFromMeshParams){ .calc_face_normal = calc_face_normals, diff --git a/source/blender/modifiers/intern/MOD_triangulate.cc b/source/blender/modifiers/intern/MOD_triangulate.cc index f5735ccc5f9..66ffce98c65 100644 --- a/source/blender/modifiers/intern/MOD_triangulate.cc +++ b/source/blender/modifiers/intern/MOD_triangulate.cc @@ -63,7 +63,7 @@ static Mesh *triangulate_mesh(Mesh *mesh, bmesh_from_mesh_params.calc_vert_normal = false; bmesh_from_mesh_params.cd_mask_extra = cd_mask_extra; - bm = BKE_mesh_to_bmesh_ex(nullptr, mesh, &bmesh_create_params, &bmesh_from_mesh_params); + bm = BKE_mesh_to_bmesh_ex(mesh, &bmesh_create_params, &bmesh_from_mesh_params); BM_mesh_triangulate( bm, quad_method, ngon_method, min_vertices, false, nullptr, nullptr, nullptr); diff --git a/source/blender/modifiers/intern/MOD_wireframe.c b/source/blender/modifiers/intern/MOD_wireframe.c index 5bc621ff65a..8339e1cea76 100644 --- a/source/blender/modifiers/intern/MOD_wireframe.c +++ b/source/blender/modifiers/intern/MOD_wireframe.c @@ -63,8 +63,7 @@ static Mesh *WireframeModifier_do(WireframeModifierData *wmd, Object *ob, Mesh * const int defgrp_index = BKE_id_defgroup_name_index(&mesh->id, wmd->defgrp_name); - bm = BKE_mesh_to_bmesh_ex(NULL, - mesh, + bm = BKE_mesh_to_bmesh_ex(mesh, &(struct BMeshCreateParams){0}, &(struct BMeshFromMeshParams){ .calc_face_normal = true, diff --git a/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc b/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc index 4e6263061d9..52c7dbf0605 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc @@ -48,7 +48,7 @@ static Mesh *triangulate_mesh_selection(const Mesh &mesh, from_mesh_params.calc_face_normal = true; from_mesh_params.calc_vert_normal = true; from_mesh_params.cd_mask_extra = cd_mask_extra; - BMesh *bm = BKE_mesh_to_bmesh_ex(nullptr, &mesh, &create_params, &from_mesh_params); + BMesh *bm = BKE_mesh_to_bmesh_ex(&mesh, &create_params, &from_mesh_params); /* Tag faces to be triangulated from the selection mask. */ BM_mesh_elem_table_ensure(bm, BM_FACE); diff --git a/source/blender/render/RE_bake.h b/source/blender/render/RE_bake.h index 6383e5a15b1..717c5053a38 100644 --- a/source/blender/render/RE_bake.h +++ b/source/blender/render/RE_bake.h @@ -13,7 +13,6 @@ struct Depsgraph; struct ImBuf; struct Mesh; struct Render; -struct MLoopUV; #ifdef __cplusplus extern "C" { diff --git a/source/blender/render/intern/bake.cc b/source/blender/render/intern/bake.cc index 365fc9e658f..dc2eb26a70e 100644 --- a/source/blender/render/intern/bake.cc +++ b/source/blender/render/intern/bake.cc @@ -949,12 +949,6 @@ void RE_bake_normal_world_to_tangent(const BakePixel pixel_array[], /* converts from world space to local space */ mul_transposed_mat3_m4_v3(mat, nor); - normalize_v3(nor); - - if (dot_v3v3(nor, normal) < 0.0f) { - negate_v3(nor); - } - invert_m3_m3(itsm, tsm); mul_m3_v3(itsm, nor); normalize_v3(nor); -- 2.30.2 From 029b2bc02598cee677aebbd2b20f93b5d8be9d66 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Thu, 16 Feb 2023 21:31:44 -0800 Subject: [PATCH 020/279] temp-sculpt-dyntopo: fix compile errors --- source/blender/blenkernel/intern/mesh_mirror.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/blender/blenkernel/intern/mesh_mirror.cc b/source/blender/blenkernel/intern/mesh_mirror.cc index 101c2e62306..4e8095d7d2e 100644 --- a/source/blender/blenkernel/intern/mesh_mirror.cc +++ b/source/blender/blenkernel/intern/mesh_mirror.cc @@ -52,7 +52,7 @@ Mesh *BKE_mesh_mirror_bisect_on_mirror_plane_for_modifier(Object *ob, bmesh_from_mesh_params.cd_mask_extra.emask = CD_MASK_ORIGINDEX; bmesh_from_mesh_params.cd_mask_extra.pmask = CD_MASK_ORIGINDEX; - bm = BKE_mesh_to_bmesh_ex(nullptr, mesh, &bmesh_create_params, &bmesh_from_mesh_params); + bm = BKE_mesh_to_bmesh_ex(mesh, &bmesh_create_params, &bmesh_from_mesh_params); /* Define bisecting plane (aka mirror plane). */ float plane[4]; @@ -97,7 +97,7 @@ void BKE_mesh_mirror_apply_mirror_on_axis(struct Main *bmain, bmesh_from_mesh_params.calc_vert_normal = true; bmesh_from_mesh_params.cd_mask_extra.vmask = CD_MASK_SHAPEKEY; - BMesh *bm = BKE_mesh_to_bmesh_ex(nullptr, mesh, &bmesh_create_params, &bmesh_from_mesh_params); + BMesh *bm = BKE_mesh_to_bmesh_ex(mesh, &bmesh_create_params, &bmesh_from_mesh_params); BMO_op_callf(bm, (BMO_FLAG_DEFAULTS & ~BMO_FLAG_RESPECT_HIDE), -- 2.30.2 From bef4f18cbc685d97180b54b8cf22ab85d57abd34 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Thu, 16 Feb 2023 21:49:32 -0800 Subject: [PATCH 021/279] temp-sculpt-dyntopo: Remove more of the old unique ID API --- source/blender/bmesh/intern/bmesh_construct.c | 487 +----------------- source/blender/bmesh/intern/bmesh_core.c | 106 +--- source/blender/bmesh/intern/bmesh_core.h | 3 - source/blender/bmesh/intern/bmesh_idmap.cc | 2 +- source/blender/bmesh/intern/bmesh_interp.c | 3 - source/blender/bmesh/intern/bmesh_log.cc | 7 +- source/blender/bmesh/intern/bmesh_mesh.cc | 23 - .../bmesh/intern/bmesh_mesh_duplicate.c | 4 - source/blender/bmesh/intern/bmesh_structure.h | 9 - source/blender/bmesh/operators/bmo_dupe.c | 7 - source/blender/bmesh/operators/bmo_inset.c | 6 - .../editors/sculpt_paint/sculpt_ops.cc | 2 +- 12 files changed, 9 insertions(+), 650 deletions(-) diff --git a/source/blender/bmesh/intern/bmesh_construct.c b/source/blender/bmesh/intern/bmesh_construct.c index caeebfba173..453beb179e8 100644 --- a/source/blender/bmesh/intern/bmesh_construct.c +++ b/source/blender/bmesh/intern/bmesh_construct.c @@ -28,301 +28,6 @@ #define SELECT 1 -#ifdef WITH_BM_ID_FREELIST - -/* if freelist is bigger then this, allocate free_idx_map */ -# define FREELIST_HASHMAP_THRESHOLD_HIGH 1024 - -/* if freelist is smaller then this, free free_idx_map*/ -# define FREELIST_HASHMAP_THRESHOLD_LOW 700 - -static void bm_id_freelist_check_hashmap(BMesh *bm) -{ - if (!bm->idmap.free_idx_map && bm->idmap.freelist_len >= FREELIST_HASHMAP_THRESHOLD_HIGH) { - printf("switching on freelist idx map\n"); - bm->idmap.free_idx_map = BLI_ghash_ptr_new("free_idx_map"); - - for (int i = 0; i < bm->idmap.freelist_len; i++) { - BLI_ghash_insert( - bm->idmap.free_idx_map, POINTER_FROM_UINT(bm->idmap.freelist[i]), POINTER_FROM_INT(i)); - } - } - else if (bm->idmap.free_idx_map && bm->idmap.freelist_len <= FREELIST_HASHMAP_THRESHOLD_LOW) { - BLI_ghash_free(bm->idmap.free_idx_map, NULL, NULL); - bm->idmap.free_idx_map = NULL; - - printf("switching off freelist idx map\n"); - } -} - -static uint bm_id_freelist_pop(BMesh *bm) -{ - bm_id_freelist_check_hashmap(bm); - - if (bm->idmap.freelist_len > 0) { - int i = --bm->idmap.freelist_len; - uint id = bm->idmap.freelist[i]; - - if (bm->idmap.free_idx_map) { - BLI_ghash_remove(bm->idmap.free_idx_map, POINTER_FROM_UINT(id), NULL, NULL); - } - - return id; - } - - return 0; -} - -void bm_free_ids_check(BMesh *bm, uint id) -{ - if (!bm->idmap.free_ids || id >> 2UL >= (uint)bm->idmap.free_ids_size) { - size_t size = (size_t)(id >> 2) + 2ULL; - size += size >> 1ULL; - - if (!bm->idmap.free_ids) { - bm->idmap.free_ids = MEM_callocN(sizeof(int) * size, "free_ids"); - } - else { - bm->idmap.free_ids = MEM_recallocN(bm->idmap.free_ids, sizeof(int) * size); - } - - bm->idmap.free_ids_size = (uint)size; - } -} - -void bm_id_freelist_take(BMesh *bm, uint id) -{ - bm_free_ids_check(bm, id); - - if (!bm->idmap.free_ids || !BLI_BITMAP_TEST(bm->idmap.free_ids, id)) { - return; - } - - BLI_BITMAP_DISABLE(bm->idmap.free_ids, id); - - if (bm->idmap.free_idx_map) { - void **val = BLI_ghash_lookup_p(bm->idmap.free_idx_map, POINTER_FROM_UINT(id)); - - if (val) { - uint i = POINTER_AS_UINT(*val); - - uint end = bm->idmap.freelist[bm->idmap.freelist_len - 1]; - - // swap with end - void **endval = BLI_ghash_lookup_p(bm->idmap.free_idx_map, POINTER_FROM_UINT(end)); - if (endval) { - *endval = POINTER_FROM_UINT(i); - } - else { - printf("%s: end id %d was not in free_idx_map; id was %d\n", __func__, end, id); - BLI_ghash_insert(bm->idmap.free_idx_map, POINTER_FROM_UINT(end), POINTER_FROM_UINT(i)); - } - - bm->idmap.freelist[i] = bm->idmap.freelist[bm->idmap.freelist_len - 1]; - bm->idmap.freelist_len--; - } - - BLI_ghash_remove(bm->idmap.free_idx_map, POINTER_FROM_UINT(id), NULL, NULL); - } - else { - for (int i = 0; i < bm->idmap.freelist_len; i++) { - if (bm->idmap.freelist[i] == id) { - // swap with end - bm->idmap.freelist[i] = bm->idmap.freelist[bm->idmap.freelist_len - 1]; - bm->idmap.freelist_len--; - } - } - } -} - -static bool bm_id_freelist_has(BMesh *bm, uint id) -{ - if (!bm->idmap.free_ids) { - return false; - } - - return id < bm->idmap.free_ids_size && BLI_BITMAP_TEST(bm->idmap.free_ids, id); -} - -void bm_id_freelist_push(BMesh *bm, uint id) -{ - bm_id_freelist_check_hashmap(bm); - bm_free_ids_check(bm, id); - - bm->idmap.freelist_len++; - - if (bm->idmap.freelist_len >= bm->idmap.freelist_size) { - int size = 2 + bm->idmap.freelist_size + (bm->idmap.freelist_size >> 1); - - uint *newlist; - - if (bm->idmap.freelist) { - newlist = MEM_reallocN(bm->idmap.freelist, size * sizeof(uint)); - } - else { - newlist = MEM_malloc_arrayN(size, sizeof(uint), "bm->idmap.freelist"); - } - - bm->idmap.freelist_size = size; - bm->idmap.freelist = newlist; - } - - if (bm->idmap.free_idx_map) { - void **val; - - if (!BLI_ghash_ensure_p(bm->idmap.free_idx_map, POINTER_FROM_UINT(id), &val)) { - *val = POINTER_FROM_INT(bm->idmap.freelist_len - 1); - } - } - - bm->idmap.freelist[bm->idmap.freelist_len - 1] = id; - BLI_BITMAP_ENABLE(bm->idmap.free_ids, id); -} -#endif - -// static const int _typemap[] = {0, 0, 1, 0, 2, 0, 0, 0, 3}; - -void bm_assign_id_intern(BMesh *bm, BMElem *elem, uint id) -{ - // CustomData *cdata = &bm->vdata + _typemap[elem->head.htype]; - // int cd_id_off = cdata->layers[cdata->typemap[CD_MESH_ID]].offset; - - BM_ELEM_CD_SET_INT(elem, bm->idmap.cd_id_off[elem->head.htype], id); - bm->idmap.maxid = MAX2(bm->idmap.maxid, id); - - if (bm->idmap.flag & BM_HAS_ID_MAP) { - if (!(bm->idmap.flag & BM_NO_REUSE_IDS)) { - if (!bm->idmap.map || bm->idmap.map_size <= (int)bm->idmap.maxid) { - int size = 2 + bm->idmap.maxid + (bm->idmap.maxid >> 1); - - BMElem **idmap = MEM_callocN(sizeof(void *) * size, "bmesh idmap"); - - if (bm->idmap.map) { - memcpy((void *)idmap, (void *)bm->idmap.map, sizeof(void *) * bm->idmap.map_size); - MEM_freeN(bm->idmap.map); - } - - bm->idmap.map = idmap; - bm->idmap.map_size = size; - } - - bm->idmap.map[id] = elem; - } - else { - void **val = NULL; - - BLI_ghash_ensure_p(bm->idmap.ghash, POINTER_FROM_UINT(id), &val); - *val = (void *)elem; - } - } -} - -static unsigned char *bm_get_htype_str(int htype) -{ - switch (htype) { - case BM_VERT: - return "vertex"; - case BM_EDGE: - return "edge"; - case BM_LOOP: - return "loop"; - case BM_FACE: - return "face"; - default: - return "unknown type"; - } -} - -void bm_assign_id(BMesh *bm, BMElem *elem, uint id, bool check_unqiue) -{ - if (check_unqiue && (bm->idmap.flag & BM_HAS_ID_MAP)) { - BMElem *old; - - if (old = (BMElem *)BM_ELEM_FROM_ID(bm, id)) { - printf("id conflict in bm_assign_id; elem %p (a %s) is being reassinged to id %d.\n", - elem, - bm_get_htype_str((int)elem->head.htype), - (int)id); - printf(" elem %p (a %s) will get a new id\n", old, bm_get_htype_str((int)old->head.htype)); - - bm_free_id(bm, old); - -#ifdef WITH_BM_ID_FREELIST - bm_id_freelist_take(bm, id); -#else - range_tree_uint_retake(bm->idmap.idtree, id); -#endif - - bm_assign_id_intern(bm, elem, id); - - bm_alloc_id(bm, old); - } - } - -#ifdef WITH_BM_ID_FREELIST - bm_id_freelist_take(bm, id); -#else - range_tree_uint_retake(bm->idmap.idtree, id); -#endif - bm_assign_id_intern(bm, elem, id); -} - -void bm_alloc_id(BMesh *bm, BMElem *elem) -{ - if ((bm->idmap.flag & (elem->head.htype | BM_HAS_IDS)) != (elem->head.htype | BM_HAS_IDS)) { - return; - } - - uint id; - -#ifdef WITH_BM_ID_FREELIST - if (bm->idmap.freelist_len > 0 && !(bm->idmap.flag & BM_NO_REUSE_IDS)) { - id = bm_id_freelist_pop(bm); - } - else { - id = bm->idmap.maxid + 1; - } -#else - if (!(bm->idmap.flag & BM_NO_REUSE_IDS)) { - id = range_tree_uint_take_any(bm->idmap.idtree); - } - else { - id = bm->idmap.maxid + 1; - } -#endif - - bm_assign_id_intern(bm, elem, id); -} - -void bm_free_id(BMesh *bm, BMElem *elem) -{ - if ((bm->idmap.flag & (elem->head.htype | BM_HAS_IDS)) != (elem->head.htype | BM_HAS_IDS)) { - return; - } - - uint id = (uint)BM_ELEM_CD_GET_INT(elem, bm->idmap.cd_id_off[elem->head.htype]); - -#ifndef WITH_BM_ID_FREELIST - - if (!(bm->idmap.flag & BM_NO_REUSE_IDS) && !range_tree_uint_has(bm->idmap.idtree, id)) { - range_tree_uint_release(bm->idmap.idtree, id); - } -#else - if (!(bm->idmap.flag & BM_NO_REUSE_IDS)) { - bm_id_freelist_push(bm, id); - } -#endif - - if ((bm->idmap.flag & BM_HAS_ID_MAP)) { - if (!(bm->idmap.flag & BM_NO_REUSE_IDS) && bm->idmap.map && (int)id < bm->idmap.map_size) { - bm->idmap.map[id] = NULL; - } - else if (bm->idmap.flag & BM_NO_REUSE_IDS) { - BLI_ghash_remove(bm->idmap.ghash, POINTER_FROM_UINT(id), NULL, NULL); - } - } -} - /** * Fill in a vertex array from an edge array. * @@ -712,13 +417,9 @@ static void bm_vert_attrs_copy( copy_v3_v3(v_dst->no, v_src->no); } - int id = bm_save_id(bm_dst, (BMElem *)v_dst); - CustomData_bmesh_free_block_data_exclude_by_type(&bm_dst->vdata, v_dst->head.data, mask_exclude); CustomData_bmesh_copy_data_exclude_by_type( &bm_src->vdata, &bm_dst->vdata, v_src->head.data, &v_dst->head.data, mask_exclude); - - bm_restore_id(bm_dst, (BMElem *)v_dst, id); } static void bm_edge_attrs_copy( @@ -729,13 +430,9 @@ static void bm_edge_attrs_copy( return; } - int id = bm_save_id(bm_dst, (BMElem *)e_dst); - CustomData_bmesh_free_block_data_exclude_by_type(&bm_dst->edata, e_dst->head.data, mask_exclude); CustomData_bmesh_copy_data_exclude_by_type( &bm_src->edata, &bm_dst->edata, e_src->head.data, &e_dst->head.data, mask_exclude); - - bm_restore_id(bm_dst, (BMElem *)e_dst, id); } static void bm_loop_attrs_copy( @@ -746,13 +443,9 @@ static void bm_loop_attrs_copy( return; } - int id = bm_save_id(bm_dst, (BMElem *)l_dst); - CustomData_bmesh_free_block_data_exclude_by_type(&bm_dst->ldata, l_dst->head.data, mask_exclude); CustomData_bmesh_copy_data_exclude_by_type( &bm_src->ldata, &bm_dst->ldata, l_src->head.data, &l_dst->head.data, mask_exclude); - - bm_restore_id(bm_dst, (BMElem *)l_dst, id); } static void bm_face_attrs_copy( @@ -766,14 +459,10 @@ static void bm_face_attrs_copy( copy_v3_v3(f_dst->no, f_src->no); } - int id = bm_save_id(bm_dst, (BMElem *)f_dst); - CustomData_bmesh_free_block_data_exclude_by_type(&bm_dst->pdata, f_dst->head.data, mask_exclude); CustomData_bmesh_copy_data_exclude_by_type( &bm_src->pdata, &bm_dst->pdata, f_src->head.data, &f_dst->head.data, mask_exclude); f_dst->mat_nr = f_src->mat_nr; - - bm_restore_id(bm_dst, (BMElem *)f_dst, id); } void BM_elem_attrs_copy_ex(BMesh *bm_src, @@ -885,8 +574,7 @@ static BMFace *bm_mesh_copy_new_face( j++; } while ((l_iter = l_iter->next) != l_first); - f_new = BM_face_create( - bm_new, verts, edges, f->len, NULL, BM_CREATE_SKIP_CD | BM_CREATE_SKIP_ID); + f_new = BM_face_create(bm_new, verts, edges, f->len, NULL, BM_CREATE_SKIP_CD); if (UNLIKELY(f_new == NULL)) { return NULL; @@ -996,19 +684,6 @@ void BM_mesh_copy_init_customdata(BMesh *bm_dst, BMesh *bm_src, const BMAllocTem CustomData_bmesh_init_pool(&bm_dst->edata, allocsize->totedge, BM_EDGE); CustomData_bmesh_init_pool(&bm_dst->ldata, allocsize->totloop, BM_LOOP); CustomData_bmesh_init_pool(&bm_dst->pdata, allocsize->totface, BM_FACE); - - // flag mesh id layer as temporary - if (!(bm_dst->idmap.flag & BM_PERMANENT_IDS)) { - for (int i = 0; i < 4; i++) { - CustomData *cdata = dstdatas[i]; - - if (CustomData_has_layer(cdata, CD_MESH_ID)) { - int idx = CustomData_get_layer_index(cdata, CD_MESH_ID); - - cdata->layers[idx].flag |= CD_FLAG_TEMPORARY | CD_FLAG_ELEM_NOCOPY; - } - } - } } void BM_mesh_copy_init_customdata_all_layers(BMesh *bm_dst, @@ -1042,8 +717,6 @@ void BM_mesh_copy_init_customdata_all_layers(BMesh *bm_dst, } CustomData_bmesh_init_pool(dst, size, htypes[i]); } - - bm_update_idmap_cdlayers(bm_dst); } BMesh *BM_mesh_copy_ex(BMesh *bm_old, struct BMeshCreateParams *params) @@ -1102,8 +775,6 @@ BMesh *BM_mesh_copy_ex(BMesh *bm_old, struct BMeshCreateParams *params) "idmap.ghash", bm_old->totvert + bm_old->totedge + bm_old->totface); } } - - bm_init_idmap_cdlayers(bm_new); } vtable = MEM_mallocN(sizeof(BMVert *) * bm_old->totvert, "BM_mesh_copy vtable"); @@ -1112,11 +783,10 @@ BMesh *BM_mesh_copy_ex(BMesh *bm_old, struct BMeshCreateParams *params) BM_ITER_MESH_INDEX (v, &iter, bm_old, BM_VERTS_OF_MESH, i) { /* copy between meshes so can't use 'example' argument */ - v_new = BM_vert_create(bm_new, v->co, NULL, BM_CREATE_SKIP_CD | BM_CREATE_SKIP_ID); + v_new = BM_vert_create(bm_new, v->co, NULL, BM_CREATE_SKIP_CD); BM_elem_attrs_copy_ex(bm_old, bm_new, v, v_new, 0xff, 0x0); - bm_alloc_id(bm_new, (BMElem *)v_new); bm_elem_check_toolflags(bm_new, (BMElem *)v_new); v_new->head.hflag = v->head.hflag; /* low level! don't do this for normal api use */ @@ -1135,10 +805,9 @@ BMesh *BM_mesh_copy_ex(BMesh *bm_old, struct BMeshCreateParams *params) vtable[BM_elem_index_get(e->v1)], vtable[BM_elem_index_get(e->v2)], e, - BM_CREATE_SKIP_CD | BM_CREATE_SKIP_ID); + BM_CREATE_SKIP_CD); BM_elem_attrs_copy_ex(bm_old, bm_new, e, e_new, 0xff, 0x0); - bm_alloc_id(bm_new, (BMElem *)e_new); bm_elem_check_toolflags(bm_new, (BMElem *)e_new); e_new->head.hflag = e->head.hflag; /* low level! don't do this for normal api use */ @@ -1156,13 +825,11 @@ BMesh *BM_mesh_copy_ex(BMesh *bm_old, struct BMeshCreateParams *params) BM_elem_index_set(f, i); /* set_inline */ f_new = bm_mesh_copy_new_face(bm_new, bm_old, vtable, etable, f); - bm_alloc_id(bm_new, (BMElem *)f_new); if (bm_new->idmap.flag & BM_LOOP) { BMLoop *l_new = f_new->l_first; do { - bm_alloc_id(bm_new, (BMElem *)l_new); l_new = l_new->next; } while (l_new != f_new->l_first); } @@ -1235,151 +902,3 @@ BMesh *BM_mesh_copy(BMesh *bm_old) params.copy_all_layers = true; return BM_mesh_copy_ex(bm_old, ¶ms); } - -void bm_init_idmap_cdlayers(BMesh *bm) -{ - if (!(bm->idmap.flag & BM_HAS_IDS)) { - return; - } - - bool temp_ids = !(bm->idmap.flag & BM_PERMANENT_IDS); - - int types[4] = {BM_VERT, BM_EDGE, BM_LOOP, BM_FACE}; - CustomData *cdatas[4] = {&bm->vdata, &bm->edata, &bm->ldata, &bm->pdata}; - - for (int i = 0; i < 4; i++) { - CustomDataLayer *layer; - - if (!(bm->idmap.flag & types[i])) { - continue; - } - - if (!CustomData_has_layer(cdatas[i], CD_MESH_ID)) { - BM_data_layer_add(bm, cdatas[i], CD_MESH_ID); - } - - layer = cdatas[i]->layers + CustomData_get_layer_index(cdatas[i], CD_MESH_ID); - layer->flag |= CD_FLAG_ELEM_NOCOPY; - - if (temp_ids) { - layer->flag |= CD_FLAG_TEMPORARY; - } - else { - layer->flag &= ~CD_FLAG_TEMPORARY; - } - } - - bm_update_idmap_cdlayers(bm); -} - -void bm_update_idmap_cdlayers(BMesh *bm) -{ - if (!(bm->idmap.flag & BM_HAS_IDS)) { - return; - } - - bm->idmap.cd_id_off[BM_VERT] = CustomData_get_offset(&bm->vdata, CD_MESH_ID); - bm->idmap.cd_id_off[BM_EDGE] = CustomData_get_offset(&bm->edata, CD_MESH_ID); - bm->idmap.cd_id_off[BM_LOOP] = CustomData_get_offset(&bm->ldata, CD_MESH_ID); - bm->idmap.cd_id_off[BM_FACE] = CustomData_get_offset(&bm->pdata, CD_MESH_ID); -} - -void bm_rebuild_idmap(BMesh *bm) -{ - CustomData *cdatas[4] = { - &bm->vdata, - &bm->edata, - &bm->ldata, - &bm->pdata, - }; - - if (bm->idmap.flag & BM_HAS_ID_MAP) { - if (bm->idmap.flag & BM_NO_REUSE_IDS) { - if (bm->idmap.ghash) { - BLI_ghash_clear(bm->idmap.ghash, NULL, NULL); - } - else { - bm->idmap.ghash = BLI_ghash_ptr_new("bm->idmap.ghash"); - } - } - else if (bm->idmap.map) { - memset(bm->idmap.map, 0, sizeof(void *) * bm->idmap.map_size); - } - } - - for (int i = 0; i < 4; i++) { - int type = 1 << i; - - if (!(bm->idmap.flag & type)) { - continue; - } - - int cd_off = bm->idmap.cd_id_off[type]; - cd_off = CustomData_get_offset(cdatas[i], CD_MESH_ID); - - if (bm->idmap.flag & BM_NO_REUSE_IDS) { - BLI_mempool_iter iter; - - BLI_mempool_iternew((&bm->vpool)[i], &iter); - BMElem *elem = (BMElem *)BLI_mempool_iterstep(&iter); - for (; elem; elem = (BMElem *)BLI_mempool_iterstep(&iter)) { - void **val; - - if (!BLI_ghash_ensure_p(bm->idmap.ghash, (void *)elem, &val)) { - *val = POINTER_FROM_INT(BM_ELEM_CD_GET_INT(elem, cd_off)); - } - } - } - else { - BLI_mempool_iter iter; - - BLI_mempool_iternew((&bm->vpool)[i], &iter); - BMElem *elem = (BMElem *)BLI_mempool_iterstep(&iter); - for (; elem; elem = (BMElem *)BLI_mempool_iterstep(&iter)) { - void **val; - int id = BM_ELEM_CD_GET_INT(elem, cd_off); - - if (!bm->idmap.map || bm->idmap.map_size <= id) { - int size = (2 + id); - size += size >> 1; - - if (!bm->idmap.map) { - bm->idmap.map = MEM_calloc_arrayN(size, sizeof(*bm->idmap.map), "bm->idmap.map"); - } - else { - bm->idmap.map = MEM_recallocN(bm->idmap.map, sizeof(void *) * size); - } - - bm->idmap.map_size = size; - } - - bm->idmap.map[BM_ELEM_CD_GET_INT(elem, cd_off)] = elem; - } - } - } -} - -int bm_save_id(BMesh *bm, BMElem *elem) -{ - if (!elem->head.data) { - return -1; - } - - if (bm->idmap.flag & elem->head.htype) { - return BM_ELEM_GET_ID(bm, elem); - } - else { - return -1; - } -} - -void bm_restore_id(BMesh *bm, BMElem *elem, int id) -{ - if (!elem->head.data || id == -1) { - return; - } - - if (bm->idmap.flag & elem->head.htype) { - BM_ELEM_CD_SET_INT(elem, bm->idmap.cd_id_off[elem->head.htype], id); - } -} diff --git a/source/blender/bmesh/intern/bmesh_core.c b/source/blender/bmesh/intern/bmesh_core.c index 939a33b9974..ff8b1b0703a 100644 --- a/source/blender/bmesh/intern/bmesh_core.c +++ b/source/blender/bmesh/intern/bmesh_core.c @@ -150,10 +150,6 @@ BMVert *BM_vert_create(BMesh *bm, zero_v3(v->no); } - - if (!(create_flag & BM_CREATE_SKIP_ID)) { - bm_alloc_id(bm, (BMElem *)v); - } } else { if (v_example) { @@ -229,10 +225,6 @@ BMEdge *BM_edge_create( else { CustomData_bmesh_set_default(&bm->edata, &e->head.data); } - - if (!(create_flag & BM_CREATE_SKIP_ID)) { - bm_alloc_id(bm, (BMElem *)e); - } } /* allocate flags */ @@ -313,10 +305,6 @@ static BMLoop *bm_loop_create(BMesh *bm, else { CustomData_bmesh_set_default(&bm->ldata, &l->head.data); } - - if (!(create_flag & BM_CREATE_SKIP_ID)) { - bm_alloc_id(bm, (BMElem *)l); - } } return l; @@ -390,18 +378,15 @@ BMFace *BM_face_copy( i++; } while ((l_iter = l_iter->next) != l_first); - f_copy = BM_face_create( - bm_dst, verts, edges, f->len, NULL, BM_CREATE_SKIP_CD | BM_CREATE_SKIP_ID); + f_copy = BM_face_create(bm_dst, verts, edges, f->len, NULL, BM_CREATE_SKIP_CD); BM_elem_attrs_copy(bm_src, bm_dst, f, f_copy); - bm_alloc_id(bm_dst, (BMElem *)f_copy); bm_elem_check_toolflags(bm_dst, (BMElem *)f_copy); l_iter = l_first = BM_FACE_FIRST_LOOP(f); l_copy = BM_FACE_FIRST_LOOP(f_copy); do { BM_elem_attrs_copy(bm_src, bm_dst, l_iter, l_copy); - bm_alloc_id(bm_dst, (BMElem *)l_copy); l_copy = l_copy->next; } while ((l_iter = l_iter->next) != l_first); @@ -512,10 +497,6 @@ BMFace *BM_face_create(BMesh *bm, CustomData_bmesh_set_default(&bm->pdata, &f->head.data); zero_v3(f->no); } - - if (!(create_flag & BM_CREATE_SKIP_ID)) { - bm_alloc_id(bm, (BMElem *)f); - } } else { if (f_example) { @@ -792,8 +773,6 @@ void bm_kill_only_vert(BMesh *bm, BMVert *v) bm->elem_table_dirty |= BM_VERT; bm->spacearr_dirty |= BM_SPACEARR_DIRTY_ALL; - bm_free_id(bm, (BMElem *)v); - BM_select_history_remove(bm, v); if (bm->vtoolflagpool) { @@ -811,82 +790,6 @@ void bm_kill_only_vert(BMesh *bm, BMVert *v) BLI_mempool_free(bm->vpool, v); } -#ifdef WITH_BM_ID_FREELIST -void bm_id_freelist_push(BMesh *bm, uint id); -#endif - -// does not modify actual element ids -void BM_clear_ids(BMesh *bm) -{ - if (!(bm->idmap.flag & BM_HAS_IDS)) { - return; - } - - if (bm->idmap.map) { - memset(bm->idmap.map, 0, sizeof(void *) * bm->idmap.map_size); - } - else if (bm->idmap.ghash) { - BLI_ghash_clear(bm->idmap.ghash, NULL, NULL); - } - -#ifndef WITH_BM_ID_FREELIST - if (bm->idmap.idtree) { - range_tree_uint_free(bm->idmap.idtree); - } - - bm->idmap.idtree = range_tree_uint_alloc(0, (uint)-1); -#else - if (bm->idmap.freelist) { - MEM_freeN(bm->idmap.freelist); - bm->idmap.freelist = NULL; - } - - if (bm->idmap.free_ids) { - MEM_SAFE_FREE(bm->idmap.free_ids); - bm->idmap.free_ids = NULL; - } - - bm->idmap.freelist_len = 0; - bm->idmap.freelist_size = 0; -#endif -} - -void BM_reassign_ids(BMesh *bm) -{ - BM_clear_ids(bm); - - int iters[] = {BM_VERTS_OF_MESH, BM_EDGES_OF_MESH, BM_FACES_OF_MESH, BM_FACES_OF_MESH}; - - for (int i = 0; i < 4; i++) { - int htype = 1 << i; - - if (!(bm->idmap.flag & htype)) { - continue; - } - - BMElem *elem; - BMIter iter; - - if (htype == BM_LOOP) { - BMFace *f; - BM_ITER_MESH (f, &iter, bm, iters[i]) { - - BMLoop *l = f->l_first; - do { - l = l->next; - } while (l != f->l_first); - - bm_alloc_id(bm, (BMElem *)l); - } - } - else { - BM_ITER_MESH (elem, &iter, bm, iters[i]) { - bm_alloc_id(bm, elem); - } - } - } -} - /** * low level function, only frees the edge, * doesn't change or adjust surrounding geometry @@ -898,8 +801,6 @@ void bm_kill_only_edge(BMesh *bm, BMEdge *e) bm->elem_table_dirty |= BM_EDGE; bm->spacearr_dirty |= BM_SPACEARR_DIRTY_ALL; - bm_free_id(bm, (BMElem *)e); - BM_select_history_remove(bm, (BMElem *)e); if (bm->etoolflagpool) { @@ -933,8 +834,6 @@ void bm_kill_only_face(BMesh *bm, BMFace *f) bm->elem_table_dirty |= BM_FACE; bm->spacearr_dirty |= BM_SPACEARR_DIRTY_ALL; - bm_free_id(bm, (BMElem *)f); - BM_select_history_remove(bm, (BMElem *)f); if (bm->ftoolflagpool) { @@ -961,8 +860,6 @@ void bm_kill_only_loop(BMesh *bm, BMLoop *l) bm->elem_index_dirty |= BM_LOOP; bm->spacearr_dirty |= BM_SPACEARR_DIRTY_ALL; - bm_free_id(bm, (BMElem *)l); - if (l->head.data) { CustomData_bmesh_free_block(&bm->ldata, &l->head.data); } @@ -1511,7 +1408,6 @@ static BMFace *bm_face_create__sfme(BMesh *bm, BMFace *f_example) #endif BM_elem_attrs_copy(bm, bm, f_example, f); - bm_alloc_id(bm, (BMElem *)f); if (bm->use_toolflags && f->head.data) { int cd_tflags = bm->pdata.layers[bm->pdata.typemap[CD_TOOLFLAGS]].offset; diff --git a/source/blender/bmesh/intern/bmesh_core.h b/source/blender/bmesh/intern/bmesh_core.h index a1a9774f006..6eaa0bb8d54 100644 --- a/source/blender/bmesh/intern/bmesh_core.h +++ b/source/blender/bmesh/intern/bmesh_core.h @@ -31,7 +31,6 @@ typedef enum eBMCreateFlag { */ BM_CREATE_SKIP_CD = (1 << 2), /* if true, you must call bm_elem_check_toolflags(bm, elem) later if toolflags are on */ - BM_CREATE_SKIP_ID = (1 << 3) } eBMCreateFlag; /* if toolflags are enabled, checks that internal pointer to toolflags it not null */ @@ -419,8 +418,6 @@ BMVert *bmesh_kernel_unglue_region_make_vert_multi(BMesh *bm, BMLoop **larr, int */ BMVert *bmesh_kernel_unglue_region_make_vert_multi_isolated(BMesh *bm, BMLoop *l_sep); -void BM_reassign_ids(BMesh *bm); -void BM_clear_ids(BMesh *bm); #ifdef __cplusplus } #endif diff --git a/source/blender/bmesh/intern/bmesh_idmap.cc b/source/blender/bmesh/intern/bmesh_idmap.cc index d9dea1155f0..e71a70eaf3c 100644 --- a/source/blender/bmesh/intern/bmesh_idmap.cc +++ b/source/blender/bmesh/intern/bmesh_idmap.cc @@ -178,7 +178,7 @@ void BM_idmap_check_attributes(BMIdMap *idmap) cdata = &idmap->bm->edata; break; case BM_LOOP: - name = "loop_id"; + name = "corner_id"; cdata = &idmap->bm->ldata; break; case BM_FACE: diff --git a/source/blender/bmesh/intern/bmesh_interp.c b/source/blender/bmesh/intern/bmesh_interp.c index 7798d24c229..5afb784413f 100644 --- a/source/blender/bmesh/intern/bmesh_interp.c +++ b/source/blender/bmesh/intern/bmesh_interp.c @@ -887,8 +887,6 @@ static void update_data_blocks(BMesh *bm, CustomData *olddata, CustomData *data) BLI_mempool_destroy(oldpool); } - - bm_update_idmap_cdlayers(bm); } void BM_data_layers_ensure(BMesh *bm, CustomData *data, BMCustomLayerReq *layers, int totlayer) @@ -989,7 +987,6 @@ void BM_data_layers_ensure(BMesh *bm, CustomData *data, BMCustomLayerReq *layers data->pool = NULL; update_data_blocks(bm, &old, data); - bm_update_idmap_cdlayers(bm); } if (old.layers) { diff --git a/source/blender/bmesh/intern/bmesh_log.cc b/source/blender/bmesh/intern/bmesh_log.cc index cc33311028c..31b5f1b6f95 100644 --- a/source/blender/bmesh/intern/bmesh_log.cc +++ b/source/blender/bmesh/intern/bmesh_log.cc @@ -1119,7 +1119,7 @@ ATTR_NO_OPT void BMLogSetDiff::restore_verts(BMesh *bm, BMLogCallbacks *callbacks) { for (BMLogVert *lv : verts.values()) { - BMVert *v = BM_vert_create(bm, lv->co, nullptr, BM_CREATE_SKIP_ID); + BMVert *v = BM_vert_create(bm, lv->co, nullptr, BM_CREATE_NOP); v->head.hflag = lv->flag; copy_v3_v3(v->no, lv->no); @@ -1180,7 +1180,7 @@ void BMLogSetDiff::restore_edges(BMesh *bm, continue; } - BMEdge *e = BM_edge_create(bm, v1, v2, nullptr, BM_CREATE_SKIP_ID); + BMEdge *e = BM_edge_create(bm, v1, v2, nullptr, BM_CREATE_NOP); CustomData_bmesh_copy_data(&entry->edata, &bm->edata, le->customdata, &e->head.data); entry->assign_elem_id(bm, e, le->id, true); @@ -1270,8 +1270,7 @@ ATTR_NO_OPT void BMLogSetDiff::restore_faces(BMesh *bm, continue; } - BMFace *f = BM_face_create_verts( - bm, verts.data(), verts.size(), nullptr, BM_CREATE_SKIP_ID, true); + BMFace *f = BM_face_create_verts(bm, verts.data(), verts.size(), nullptr, BM_CREATE_NOP, true); f->head.hflag = lf->flag; CustomData_bmesh_copy_data(&entry->pdata, &bm->pdata, lf->customdata, &f->head.data); diff --git a/source/blender/bmesh/intern/bmesh_mesh.cc b/source/blender/bmesh/intern/bmesh_mesh.cc index 96da7a1ae9a..6af871c44b4 100644 --- a/source/blender/bmesh/intern/bmesh_mesh.cc +++ b/source/blender/bmesh/intern/bmesh_mesh.cc @@ -216,7 +216,6 @@ BMesh *BM_mesh_create(const BMAllocTemplate *allocsize, const struct BMeshCreate } if (params->create_unique_ids) { - bm_init_idmap_cdlayers(bm); init_cdata_pools = true; } @@ -389,26 +388,6 @@ void BM_mesh_clear(BMesh *bm) CustomData_reset(&bm->edata); CustomData_reset(&bm->ldata); CustomData_reset(&bm->pdata); - - bm->idmap.flag = idmap_flags; - - if (bm->idmap.flag & BM_HAS_IDS) { - bm->idmap.map = nullptr; - bm->idmap.ghash = nullptr; - bm->idmap.map_size = 0; - -#ifndef WITH_BM_ID_FREELIST - bm->idmap.idtree = range_tree_uint_alloc(0, (uint)-1); -#else - MEM_SAFE_FREE(bm->idmap.free_ids); - MEM_SAFE_FREE(bm->idmap.freelist); - - bm->idmap.freelist_len = bm->idmap.freelist_size = 0; - bm->idmap.free_ids = nullptr; - bm->idmap.freelist = nullptr; -#endif - bm_init_idmap_cdlayers(bm); - } } void BM_mesh_free(BMesh *bm) @@ -1625,8 +1604,6 @@ void BM_mesh_rebuild(BMesh *bm, BLI_mempool_destroy(bm->fpool); bm->fpool = fpool_dst; } - - bm_rebuild_idmap(bm); } void bm_alloc_toolflags_cdlayers(BMesh *bm, bool set_elems) diff --git a/source/blender/bmesh/intern/bmesh_mesh_duplicate.c b/source/blender/bmesh/intern/bmesh_mesh_duplicate.c index 83a84012c36..11ae8236cee 100644 --- a/source/blender/bmesh/intern/bmesh_mesh_duplicate.c +++ b/source/blender/bmesh/intern/bmesh_mesh_duplicate.c @@ -21,7 +21,6 @@ static BMVert *bm_vert_copy(BMesh *bm_src, BMesh *bm_dst, BMVert *v_src) BMVert *v_dst = BM_vert_create(bm_dst, v_src->co, NULL, BM_CREATE_SKIP_CD); BM_elem_attrs_copy(bm_src, bm_dst, v_src, v_dst); - bm_alloc_id(bm_dst, (BMElem *)v_dst); bm_elem_check_toolflags(bm_dst, (BMElem *)v_dst); return v_dst; @@ -38,7 +37,6 @@ static BMEdge *bm_edge_copy_with_arrays(BMesh *bm_src, BM_elem_attrs_copy(bm_src, bm_dst, e_src, e_dst); - bm_alloc_id(bm_dst, (BMElem *)e_dst); bm_elem_check_toolflags(bm_dst, (BMElem *)e_dst); return e_dst; @@ -70,7 +68,6 @@ static BMFace *bm_face_copy_with_arrays( /* Copy attributes. */ BM_elem_attrs_copy(bm_src, bm_dst, f_src, f_dst); - bm_alloc_id(bm_dst, (BMElem *)f_dst); bm_elem_check_toolflags(bm_dst, (BMElem *)f_dst); /* Copy per-loop custom data. */ @@ -78,7 +75,6 @@ static BMFace *bm_face_copy_with_arrays( l_iter_dst = BM_FACE_FIRST_LOOP(f_dst); do { BM_elem_attrs_copy(bm_src, bm_dst, l_iter_src, l_iter_dst); - bm_alloc_id(bm_dst, (BMElem *)l_iter_dst); } while ((void)(l_iter_dst = l_iter_dst->next), (l_iter_src = l_iter_src->next) != l_first_src); return f_dst; diff --git a/source/blender/bmesh/intern/bmesh_structure.h b/source/blender/bmesh/intern/bmesh_structure.h index 54f46901121..ad06355970a 100644 --- a/source/blender/bmesh/intern/bmesh_structure.h +++ b/source/blender/bmesh/intern/bmesh_structure.h @@ -137,12 +137,3 @@ bool bmesh_disk_validate(int len, BMEdge *e, BMVert *v) ATTR_WARN_UNUSED_RESULT #define BM_DEFAULT_IDMAP_SIZE (1 << 12) #include "intern/bmesh_structure_inline.h" - -void bm_assign_id(BMesh *bm, BMElem *elem, uint id, bool check_unique); -void bm_assign_id_intern(BMesh *bm, BMElem *elem, uint id); -void bm_alloc_id(BMesh *bm, BMElem *elem); -void bm_free_id(BMesh *bm, BMElem *elem); -void bm_init_idmap_cdlayers(BMesh *bm); -void bm_update_idmap_cdlayers(BMesh *bm); -void bm_free_ids_check(BMesh *bm, uint id); -void bm_id_freelist_push(BMesh *bm, uint id); diff --git a/source/blender/bmesh/operators/bmo_dupe.c b/source/blender/bmesh/operators/bmo_dupe.c index 766774ad281..7769f792dce 100644 --- a/source/blender/bmesh/operators/bmo_dupe.c +++ b/source/blender/bmesh/operators/bmo_dupe.c @@ -47,8 +47,6 @@ static BMVert *bmo_vert_copy(BMOperator *op, /* Copy attributes */ BM_elem_attrs_copy(bm_src, bm_dst, v_src, v_dst); - /* Handle Ids */ - bm_alloc_id(bm_dst, (BMElem *)v_dst); // short **flags = BM_ELEM_CD_GET_VOID_P( // v_dst, bm_dst->vdata.layers[bm_dst->vdata.typemap[CD_TOOLFLAGS]].offset); @@ -116,8 +114,6 @@ static BMEdge *bmo_edge_copy(BMOperator *op, /* Copy attributes */ BM_elem_attrs_copy(bm_src, bm_dst, e_src, e_dst); - /* Handle Ids */ - bm_alloc_id(bm_dst, (BMElem *)e_dst); bm_elem_check_toolflags(bm_dst, (BMElem *)e_dst); /* Mark the edge for output */ @@ -172,8 +168,6 @@ static BMFace *bmo_face_copy(BMOperator *op, /* Copy attributes */ BM_elem_attrs_copy(bm_src, bm_dst, f_src, f_dst); - /* Handle Ids and toolflags */ - bm_alloc_id(bm_dst, (BMElem *)f_dst); bm_elem_check_toolflags(bm_dst, (BMElem *)f_dst); /* copy per-loop custom data */ @@ -181,7 +175,6 @@ static BMFace *bmo_face_copy(BMOperator *op, l_iter_dst = BM_FACE_FIRST_LOOP(f_dst); do { BM_elem_attrs_copy(bm_src, bm_dst, l_iter_src, l_iter_dst); - bm_alloc_id(bm_dst, (BMElem *)l_iter_dst); } while ((void)(l_iter_dst = l_iter_dst->next), (l_iter_src = l_iter_src->next) != l_first_src); /* Mark the face for output */ diff --git a/source/blender/bmesh/operators/bmo_inset.c b/source/blender/bmesh/operators/bmo_inset.c index 16595f2075d..092cd66ed34 100644 --- a/source/blender/bmesh/operators/bmo_inset.c +++ b/source/blender/bmesh/operators/bmo_inset.c @@ -1242,17 +1242,11 @@ void bmo_inset_region_exec(BMesh *bm, BMOperator *op) const int i_a = BM_elem_index_get(l_a_other); const int i_b = BM_elem_index_get(l_b_other); - int ida = bm_save_id(bm, (BMElem *)l_a); - int idb = bm_save_id(bm, (BMElem *)l_b); - CustomData_bmesh_free_block_data(&bm->ldata, l_b->head.data); CustomData_bmesh_free_block_data(&bm->ldata, l_a->head.data); CustomData_bmesh_copy_data(&bm->ldata, &bm->ldata, iface->blocks_l[i_a], &l_b->head.data); CustomData_bmesh_copy_data(&bm->ldata, &bm->ldata, iface->blocks_l[i_b], &l_a->head.data); - bm_restore_id(bm, (BMElem *)l_a, ida); - bm_restore_id(bm, (BMElem *)l_b, idb); - #ifdef USE_LOOP_CUSTOMDATA_MERGE if (has_math_ldata) { BMEdge *e_connect; diff --git a/source/blender/editors/sculpt_paint/sculpt_ops.cc b/source/blender/editors/sculpt_paint/sculpt_ops.cc index 36c55b349b3..6dfad0c3107 100644 --- a/source/blender/editors/sculpt_paint/sculpt_ops.cc +++ b/source/blender/editors/sculpt_paint/sculpt_ops.cc @@ -292,7 +292,7 @@ static int sculpt_symmetrize_exec(bContext *C, wmOperator *op) SCULT_dyntopo_flag_all_disk_sort(ss); // symmetrize is messing up ids, regenerate them from scratch - BM_reassign_ids(ss->bm); + BM_idmap_check_ids(ss->bm_idmap); BM_mesh_toolflags_set(ss->bm, false); BM_log_full_mesh(ss->bm, ss->bm_log); -- 2.30.2 From 874668f0ad2b969100efd2320b39c3a448a536ba Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Fri, 17 Feb 2023 03:33:56 -0800 Subject: [PATCH 022/279] temp-sculpt-dyntopo: Attempt to fix element id bugs from merge --- source/blender/bmesh/intern/bmesh_idmap.cc | 23 +++++++------ source/blender/bmesh/intern/bmesh_log.cc | 38 +++++++++++++++++++--- 2 files changed, 46 insertions(+), 15 deletions(-) diff --git a/source/blender/bmesh/intern/bmesh_idmap.cc b/source/blender/bmesh/intern/bmesh_idmap.cc index e71a70eaf3c..0c3ae8158be 100644 --- a/source/blender/bmesh/intern/bmesh_idmap.cc +++ b/source/blender/bmesh/intern/bmesh_idmap.cc @@ -16,6 +16,8 @@ #include "bmesh_idmap.h" #include +#define BM_ID_NONE 0 //-1 + using namespace blender; #define FREELIST_HASHMAP_THRESHOLD_HIGH 1024 @@ -31,6 +33,7 @@ BMIdMap *BM_idmap_new(BMesh *bm, int elem_mask) idmap->flag = elem_mask; idmap->bm = bm; + idmap->maxid = 1; BM_idmap_check_attributes(idmap); @@ -71,7 +74,7 @@ void BM_idmap_check_ids(BMIdMap *idmap) idmap->free_idx_map = nullptr; } - int max_id = 0; + int max_id = 1; if (idmap->flag & BM_VERT) { BM_ITER_MESH (v, &iter, idmap->bm, BM_VERTS_OF_MESH) { @@ -204,7 +207,7 @@ void BM_idmap_check_attributes(BMIdMap *idmap) cdata->layers[idx].flag |= CD_FLAG_ELEM_NOINTERP | CD_FLAG_ELEM_NOCOPY; int *default_data = static_cast(cdata->layers[idx].default_data); - *default_data = -1; + *default_data = BM_ID_NONE; idmap->cd_id_off[type] = cdata->layers[idx].offset; }; @@ -242,13 +245,13 @@ static void check_idx_map(BMIdMap *idmap) int BM_idmap_alloc(BMIdMap *idmap, BMElem *elem) { - int id = -1; + int id = BM_ID_NONE; #ifdef USE_NEW_IDMAP while (idmap->freelist.size()) { id = idmap->freelist.pop_last(); - if (id == -1) { + if (id == BM_ID_NONE) { continue; } @@ -259,7 +262,7 @@ int BM_idmap_alloc(BMIdMap *idmap, BMElem *elem) break; } - if (id == -1) { + if (id == BM_ID_NONE) { id = idmap->maxid++; } @@ -279,14 +282,14 @@ void BM_idmap_assign(BMIdMap *idmap, BMElem *elem, int id) const int *val; if ((val = idmap->free_idx_map->lookup_ptr(id))) { - idmap->freelist[*val] = -1; + idmap->freelist[*val] = BM_ID_NONE; idmap->free_idx_map->remove(id); } } else { for (int i : IndexRange(idmap->freelist.size())) { if (idmap->freelist[i] == id) { - idmap->freelist[i] = -1; + idmap->freelist[i] = BM_ID_NONE; } } } @@ -305,7 +308,7 @@ void BM_idmap_release(BMIdMap *idmap, BMElem *elem, bool clear_id) #ifdef USE_NEW_IDMAP int id = BM_ELEM_CD_GET_INT(elem, idmap->cd_id_off[(int)elem->head.htype]); - if (id == -1) { + if (id == BM_ID_NONE) { printf("%s: unassigned id!\n", __func__); return; }; @@ -325,7 +328,7 @@ void BM_idmap_release(BMIdMap *idmap, BMElem *elem, bool clear_id) check_idx_map(idmap); if (clear_id) { - BM_ELEM_CD_SET_INT(elem, idmap->cd_id_off[elem->head.htype], -1); + BM_ELEM_CD_SET_INT(elem, idmap->cd_id_off[elem->head.htype], BM_ID_NONE); } #endif } @@ -334,7 +337,7 @@ int BM_idmap_check_assign(BMIdMap *idmap, BMElem *elem) { int id = BM_ELEM_CD_GET_INT(elem, idmap->cd_id_off[(int)elem->head.htype]); - if (id == -1) { + if (id == BM_ID_NONE) { return BM_idmap_alloc(idmap, elem); } diff --git a/source/blender/bmesh/intern/bmesh_log.cc b/source/blender/bmesh/intern/bmesh_log.cc index 04f52befbae..47d03e2858c 100644 --- a/source/blender/bmesh/intern/bmesh_log.cc +++ b/source/blender/bmesh/intern/bmesh_log.cc @@ -416,7 +416,33 @@ struct BMLogEntry { template T *get_elem_from_id(BMesh *bm, BMID id) { - return reinterpret_cast(BM_idmap_lookup(idmap, id.id)); + T *elem = reinterpret_cast(BM_idmap_lookup(idmap, id.id)); + char htype = 0; + + if (!elem) { + return nullptr; + } + + if constexpr (std::is_same_v) { + htype = BM_VERT; + } + if constexpr (std::is_same_v) { + htype = BM_EDGE; + } + if constexpr (std::is_same_v) { + htype = BM_FACE; + } + + if (elem->head.htype != htype) { + printf("%s: error: expected %s, got %s; id: %d\n", + __func__, + get_elem_htype_str(elem->head.htype), + get_elem_htype_str(htype), + id.id); + return nullptr; + } + + return elem; } template void assign_elem_id(BMesh *bm, T *elem, BMID _id, bool check_unique) @@ -428,10 +454,12 @@ struct BMLogEntry { old = BM_idmap_lookup(idmap, id); if (old && old != (BMElem *)elem) { - printf("id conflict in BMLogEntry::assign_elem_id; elem %p (a %s) is being reassinged to id %d.\n", - elem, - get_elem_htype_str((int)elem->head.htype), - (int)id); + printf( + "id conflict in BMLogEntry::assign_elem_id; elem %p (a %s) is being reassinged to id " + "%d.\n", + elem, + get_elem_htype_str((int)elem->head.htype), + (int)id); printf( " elem %p (a %s) will get a new id\n", old, get_elem_htype_str((int)old->head.htype)); -- 2.30.2 From f52415f82a2d4e4056d1f2ed5b6efddc773e1bc1 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Mon, 20 Feb 2023 13:54:14 -0800 Subject: [PATCH 023/279] temp-sculpt-dyntopo: Add debug code for BMIdMap --- .../startup/bl_ui/properties_paint_common.py | 2 + .../blenkernel/intern/dyntopo_collapse.cc | 2 +- source/blender/blenkernel/intern/paint.cc | 3 + source/blender/blenlib/BLI_mempool.h | 8 +- source/blender/blenlib/intern/BLI_mempool.c | 13 +- source/blender/bmesh/intern/bmesh_construct.c | 8 +- source/blender/bmesh/intern/bmesh_idmap.cc | 280 +++++++++++++++--- source/blender/bmesh/intern/bmesh_idmap.h | 19 +- source/blender/bmesh/intern/bmesh_interp.c | 73 +---- source/blender/bmesh/intern/bmesh_log.cc | 13 +- .../bmesh/intern/bmesh_mesh_convert.cc | 8 +- source/blender/editors/sculpt_paint/sculpt.cc | 4 + source/blender/makesrna/intern/rna_brush.c | 2 +- 13 files changed, 319 insertions(+), 116 deletions(-) diff --git a/release/scripts/startup/bl_ui/properties_paint_common.py b/release/scripts/startup/bl_ui/properties_paint_common.py index 63dd18cb703..08abf33af00 100644 --- a/release/scripts/startup/bl_ui/properties_paint_common.py +++ b/release/scripts/startup/bl_ui/properties_paint_common.py @@ -933,6 +933,8 @@ def brush_settings_advanced(layout, context, brush, popover=False): use_accumulate = capabilities.has_accumulate use_frontface = True + layout.prop(brush.dyntopo, "disabled") + col = layout.column(heading="Auto-Masking", align=True) col = layout.column(align=True) diff --git a/source/blender/blenkernel/intern/dyntopo_collapse.cc b/source/blender/blenkernel/intern/dyntopo_collapse.cc index 146c782d2ff..30c9e75b809 100644 --- a/source/blender/blenkernel/intern/dyntopo_collapse.cc +++ b/source/blender/blenkernel/intern/dyntopo_collapse.cc @@ -300,7 +300,7 @@ static void collapse_ring_callback_pre(BMElem *elem, void *userdata) static void check_new_elem_id(BMElem *elem, TraceData *data) { int id = BM_ELEM_CD_GET_INT(elem, data->pbvh->bm_idmap->cd_id_off[elem->head.htype]); - if (id >= 0) { + if (id != BM_ID_NONE) { BMElem *existing = id < data->pbvh->bm_idmap->map_size ? BM_idmap_lookup(data->pbvh->bm_idmap, id) : nullptr; diff --git a/source/blender/blenkernel/intern/paint.cc b/source/blender/blenkernel/intern/paint.cc index 0426d18d014..6b4efcf6e6b 100644 --- a/source/blender/blenkernel/intern/paint.cc +++ b/source/blender/blenkernel/intern/paint.cc @@ -3602,6 +3602,9 @@ static void sculptsession_bmesh_attr_update_internal(Object *ob) SculptSession *ss = ob->sculpt; sculptsession_bmesh_add_layers(ob); + if (ss->bm_idmap) { + BM_idmap_check_attributes(ss->bm_idmap); + } if (ss->pbvh) { int cd_sculpt_vert = CustomData_get_offset(&ss->bm->vdata, CD_DYNTOPO_VERT); diff --git a/source/blender/blenlib/BLI_mempool.h b/source/blender/blenlib/BLI_mempool.h index 9fa430f9cc6..dc2d70e0439 100644 --- a/source/blender/blenlib/BLI_mempool.h +++ b/source/blender/blenlib/BLI_mempool.h @@ -126,7 +126,8 @@ enum { /* allow random access, implies BLI_MEMPOOL_ALLOW_ITER since we need the freewords to detect free state of elements*/ - BLI_MEMPOOL_RANDOM_ACCESS = (1 << 1) | (1 << 0) + BLI_MEMPOOL_RANDOM_ACCESS = (1 << 1) | (1 << 0), + BLI_MEMPOOL_IGNORE_FREE = (1 << 2), /* Used for debugging. */ }; /** @@ -158,6 +159,11 @@ int BLI_mempool_find_elems_fuzzy( int BLI_mempool_get_size(BLI_mempool *pool); int BLI_mempool_find_real_index(BLI_mempool *pool, void *ptr); +/* Sets BLI_MEMPOOL_IGNORE_FREE flag, ignores call to BLI_mempool_free. + * Used for debugging. + */ +void BLI_mempool_ignore_free(BLI_mempool *pool); + #ifdef __cplusplus } #endif diff --git a/source/blender/blenlib/intern/BLI_mempool.c b/source/blender/blenlib/intern/BLI_mempool.c index 7deb5379a5c..b9fefdbdcf5 100644 --- a/source/blender/blenlib/intern/BLI_mempool.c +++ b/source/blender/blenlib/intern/BLI_mempool.c @@ -19,11 +19,11 @@ #include "atomic_ops.h" -#include "BLI_string.h" -#include "BLI_utildefines.h" #include "BLI_asan.h" #include "BLI_mempool.h" /* own include */ #include "BLI_mempool_private.h" /* own include */ +#include "BLI_string.h" +#include "BLI_utildefines.h" #ifndef WITH_DNA_GHASH # include "BLI_threads.h" @@ -740,6 +740,11 @@ int BLI_mempool_get_size(BLI_mempool *pool) return ret; } +void BLI_mempool_ignore_free(BLI_mempool *pool) +{ + pool->flag |= BLI_MEMPOOL_IGNORE_FREE; +} + /** * Free an element from the mempool. * @@ -747,6 +752,10 @@ int BLI_mempool_get_size(BLI_mempool *pool) */ void BLI_mempool_free(BLI_mempool *pool, void *addr) { + if (pool->flag & BLI_MEMPOOL_IGNORE_FREE) { + return; + } + mempool_unpoison(pool); BLI_freenode *newhead = addr; diff --git a/source/blender/bmesh/intern/bmesh_construct.c b/source/blender/bmesh/intern/bmesh_construct.c index 453beb179e8..611c7627cf0 100644 --- a/source/blender/bmesh/intern/bmesh_construct.c +++ b/source/blender/bmesh/intern/bmesh_construct.c @@ -406,7 +406,7 @@ void BM_sort_disk_cycle(BMVert *v) /*************************************************************/ -static void bm_vert_attrs_copy( +ATTR_NO_OPT static void bm_vert_attrs_copy( BMesh *bm_src, BMesh *bm_dst, const BMVert *v_src, BMVert *v_dst, eCustomDataMask mask_exclude) { if ((bm_src == bm_dst) && (v_src == v_dst)) { @@ -422,7 +422,7 @@ static void bm_vert_attrs_copy( &bm_src->vdata, &bm_dst->vdata, v_src->head.data, &v_dst->head.data, mask_exclude); } -static void bm_edge_attrs_copy( +ATTR_NO_OPT static void bm_edge_attrs_copy( BMesh *bm_src, BMesh *bm_dst, const BMEdge *e_src, BMEdge *e_dst, eCustomDataMask mask_exclude) { if ((bm_src == bm_dst) && (e_src == e_dst)) { @@ -435,7 +435,7 @@ static void bm_edge_attrs_copy( &bm_src->edata, &bm_dst->edata, e_src->head.data, &e_dst->head.data, mask_exclude); } -static void bm_loop_attrs_copy( +ATTR_NO_OPT static void bm_loop_attrs_copy( BMesh *bm_src, BMesh *bm_dst, const BMLoop *l_src, BMLoop *l_dst, eCustomDataMask mask_exclude) { if ((bm_src == bm_dst) && (l_src == l_dst)) { @@ -448,7 +448,7 @@ static void bm_loop_attrs_copy( &bm_src->ldata, &bm_dst->ldata, l_src->head.data, &l_dst->head.data, mask_exclude); } -static void bm_face_attrs_copy( +ATTR_NO_OPT static void bm_face_attrs_copy( BMesh *bm_src, BMesh *bm_dst, const BMFace *f_src, BMFace *f_dst, eCustomDataMask mask_exclude) { if ((bm_src == bm_dst) && (f_src == f_dst)) { diff --git a/source/blender/bmesh/intern/bmesh_idmap.cc b/source/blender/bmesh/intern/bmesh_idmap.cc index 0c3ae8158be..1578f8a3036 100644 --- a/source/blender/bmesh/intern/bmesh_idmap.cc +++ b/source/blender/bmesh/intern/bmesh_idmap.cc @@ -14,32 +14,160 @@ #include "DNA_meshdata_types.h" #include "bmesh_idmap.h" +#include #include -#define BM_ID_NONE 0 //-1 - using namespace blender; #define FREELIST_HASHMAP_THRESHOLD_HIGH 1024 #define FREELIST_HASHMAP_THRESHOLD_LOW 700 +#ifdef DEBUG_BM_IDMAP +static void bm_idmap_debug_check_init(BMesh *bm) +{ + /* Disable mempool allocation so we can use + * element pointers as backup IDs. + */ + BLI_mempool_ignore_free(bm->vpool); + BLI_mempool_ignore_free(bm->epool); + BLI_mempool_ignore_free(bm->lpool); + BLI_mempool_ignore_free(bm->fpool); +} +#endif + +ATTR_NO_OPT static void idmap_log_message(const char *fmt, ...) +{ + char msg[64]; + + va_list args; + va_start(args, fmt); + vprintf(fmt, args); + va_end(args); +} + +#ifdef DEBUG_BM_IDMAP +ATTR_NO_OPT static void _idmap_debug_insert(const char *func, BMIdMap *idmap, BMElem *elem, int id) +{ + if (id == BM_ID_NONE) { + idmap_log_message("%s: Tried to assign a null id\n", func); + } + + idmap->elem2id->add(elem, id); + idmap->id2elem->add(id, elem); +} + +# define idmap_debug_insert(idmap, elem, id) _idmap_debug_insert(__func__, idmap, elem, id) +#endif + BMIdMap *BM_idmap_new(BMesh *bm, int elem_mask) { BMIdMap *idmap = MEM_new("BMIdMap"); +#ifdef DEBUG_BM_IDMAP + bm_idmap_debug_check_init(bm); + idmap->elem2id = new blender::Map; + idmap->id2elem = new blender::Map; +#endif + for (int i = 0; i < ARRAY_SIZE(idmap->cd_id_off); i++) { idmap->cd_id_off[i] = -1; } idmap->flag = elem_mask; idmap->bm = bm; - idmap->maxid = 1; + idmap->maxid = BM_ID_NONE + 1; BM_idmap_check_attributes(idmap); return idmap; } +static const char elem_names[9][16] = { + "corrupted", // 0 + "vertex", // 1 + "edge", // 2 + "corrupted", // 3 + "loop", // 4 + "corrupted", // 5 + "corrupted", // 6 + "corrupted", // 7 + "face", // 8 +}; + +static const char *get_type_name(char htype) +{ + if (htype <= 0 || htype >= 9) { + return "corrupted"; + } + + return elem_names[int(htype)]; +} + +template static constexpr char get_elem_type() +{ + if constexpr (std::is_same_v) { + return BM_VERT; + } + else if constexpr (std::is_same_v) { + return BM_EDGE; + } + else if constexpr (std::is_same_v) { + return BM_LOOP; + } + else if constexpr (std::is_same_v) { + return BM_FACE; + } +} + +#ifdef DEBUG_BM_IDMAP +ATTR_NO_OPT static bool _idmap_check_elem(const char *func, BMIdMap *idmap, BMElem *elem) +{ + int id = BM_idmap_get_id(idmap, elem); + bool exists = idmap->elem2id->contains(elem); + + if (!elem || !ELEM(elem->head.htype, BM_VERT, BM_EDGE, BM_LOOP, BM_FACE)) { + idmap_log_message("%s: bad call to idmap_check_elem; %p\n", func, elem); + return false; + } + + if (id == BM_ID_NONE && !exists) { + return true; + } + + if (id != BM_ID_NONE && !exists) { + idmap_log_message("%s: elem %p(%d, a %s) has an id but isn't in map\n", + func, + elem, + id, + get_type_name(elem->head.htype)); + if (idmap->id2elem->contains(id)) { + BMElem *elem2 = idmap->id2elem->lookup(id); + idmap_log_message( + " another elem %p (a %s) has the id\n", elem2, get_type_name(elem2->head.htype)); + } + return false; + } + + int id2 = idmap->elem2id->contains(elem) ? idmap->elem2id->lookup(elem) : -1; + if (id2 != id) { + idmap_log_message("%s: elem %p (a %s) has id %d; expected %d\n", + func, + elem, + get_type_name(elem->head.htype), + id, + id2); + } + + return true; +} + +# define idmap_check_elem(idmap, elem) _idmap_check_elem(__func__, idmap, elem) +#else +# define idmap_check_elem(idmap, elem) \ + do { \ + } while (0) +#endif + static void idmap_grow_map(BMIdMap *idmap, int newid) { if (idmap->map_size > newid) { @@ -59,13 +187,22 @@ static void idmap_grow_map(BMIdMap *idmap, int newid) idmap->map_size = newsize; } -void BM_idmap_check_ids(BMIdMap *idmap) +ATTR_NO_OPT void BM_idmap_check_ids(BMIdMap *idmap) { BMIter iter; BMVert *v; BMEdge *e; BMFace *f; +#ifdef DEBUG_BM_IDMAP + bm_idmap_debug_check_init(idmap->bm); + delete idmap->id2elem; + delete idmap->elem2id; + + idmap->elem2id = new blender::Map; + idmap->id2elem = new blender::Map; +#endif + BM_idmap_check_attributes(idmap); idmap->freelist.clear(); @@ -119,15 +256,19 @@ void BM_idmap_check_ids(BMIdMap *idmap) } auto check_elem = [&](auto *elem) { - int id = BM_ELEM_CD_GET_INT(elem, idmap->cd_id_off[(int)elem->head.htype]); + int id = BM_ELEM_CD_GET_INT(elem, idmap->cd_id_off[int(elem->head.htype)]); - if (id < 0 || id >= idmap->map_size || idmap->map[id]) { + if (id == BM_ID_NONE || id < 0 || (id < idmap->map_size && idmap->map[id])) { id = max_id++; - BM_ELEM_CD_SET_INT(elem, idmap->cd_id_off[(int)elem->head.htype], id); + BM_ELEM_CD_SET_INT(elem, idmap->cd_id_off[int(elem->head.htype)], id); } idmap_grow_map(idmap, id); idmap->map[id] = reinterpret_cast(elem); + +#ifdef DEBUG_BM_IDMAP + idmap_debug_insert(idmap, reinterpret_cast(elem), id); +#endif }; if (idmap->flag & BM_VERT) { @@ -152,16 +293,11 @@ void BM_idmap_check_ids(BMIdMap *idmap) } } } - if (idmap->flag & BM_VERT) { - BM_ITER_MESH (v, &iter, idmap->bm, BM_VERTS_OF_MESH) { - check_elem(v); - } - } - idmap->maxid = max_id; + idmap->maxid = max_id + 1; } -void BM_idmap_check_attributes(BMIdMap *idmap) +ATTR_NO_OPT void BM_idmap_check_attributes(BMIdMap *idmap) { auto check_attr = [&](int type) { if (!(idmap->flag & type)) { @@ -218,22 +354,27 @@ void BM_idmap_check_attributes(BMIdMap *idmap) check_attr(BM_FACE); } -void BM_idmap_destroy(BMIdMap *idmap) +ATTR_NO_OPT void BM_idmap_destroy(BMIdMap *idmap) { +#ifdef DEBUG_BM_IDMAP + delete idmap->elem2id; + delete idmap->id2elem; +#endif + MEM_SAFE_FREE(idmap->map); MEM_delete(idmap); } -static void check_idx_map(BMIdMap *idmap) +ATTR_NO_OPT static void check_idx_map(BMIdMap *idmap) { if (idmap->free_idx_map && idmap->freelist.size() < FREELIST_HASHMAP_THRESHOLD_LOW) { - // printf("%s: Deleting free_idx_map\n", __func__); + // idmap_log_message("%s: Deleting free_idx_map\n", __func__); MEM_delete(idmap->free_idx_map); idmap->free_idx_map = nullptr; } else if (!idmap->free_idx_map && idmap->freelist.size() < FREELIST_HASHMAP_THRESHOLD_HIGH) { - // printf("%s: Adding free_idx_map\n", __func__); + // idmap_log_message("%s: Adding free_idx_map\n", __func__); idmap->free_idx_map = MEM_new("BMIdMap::FreeIdxMap"); @@ -243,10 +384,36 @@ static void check_idx_map(BMIdMap *idmap) } } -int BM_idmap_alloc(BMIdMap *idmap, BMElem *elem) +ATTR_NO_OPT int BM_idmap_alloc(BMIdMap *idmap, BMElem *elem) { int id = BM_ID_NONE; -#ifdef USE_NEW_IDMAP + +#ifdef DEBUG_BM_IDMAP + int id2 = BM_ELEM_CD_GET_INT(elem, idmap->cd_id_off[int(elem->head.htype)]); + + if (idmap->elem2id->contains(elem)) { + int id3 = idmap->elem2id->lookup(elem); + + if (id2 == id3) { + idmap_log_message("%s: elem %p already had id %d\n", __func__, elem, id3); + } + else { + idmap_log_message( + "%s: elem %p already has an id (%d), but its attribute has the wrong one (%d)\n", + __func__, + elem, + id3, + id2); + } + + idmap->elem2id->remove(elem); + } + + if (idmap->id2elem->contains(id)) { + idmap->id2elem->remove(id); + } + +#endif while (idmap->freelist.size()) { id = idmap->freelist.pop_last(); @@ -269,14 +436,17 @@ int BM_idmap_alloc(BMIdMap *idmap, BMElem *elem) idmap_grow_map(idmap, id); idmap->map[id] = elem; - BM_ELEM_CD_SET_INT(elem, idmap->cd_id_off[elem->head.htype], id); + BM_ELEM_CD_SET_INT(elem, idmap->cd_id_off[int(elem->head.htype)], id); + +#ifdef DEBUG_BM_IDMAP + idmap_debug_insert(idmap, elem, id); #endif + return id; } -void BM_idmap_assign(BMIdMap *idmap, BMElem *elem, int id) +ATTR_NO_OPT void BM_idmap_assign(BMIdMap *idmap, BMElem *elem, int id) { -#ifdef USE_NEW_IDMAP /* Remove id from freelist. */ if (idmap->free_idx_map) { const int *val; @@ -294,26 +464,65 @@ void BM_idmap_assign(BMIdMap *idmap, BMElem *elem, int id) } } - BM_ELEM_CD_SET_INT(elem, idmap->cd_id_off[elem->head.htype], id); + BM_ELEM_CD_SET_INT(elem, idmap->cd_id_off[int(elem->head.htype)], id); idmap_grow_map(idmap, id); idmap->map[id] = elem; check_idx_map(idmap); + +#ifdef DEBUG_BM_IDMAP + if (idmap->elem2id->contains(elem) && idmap->elem2id->lookup(elem) == id) { + return; + } + + if (idmap->elem2id->contains(elem)) { + int id2 = idmap->elem2id->lookup(elem); + + idmap_log_message("%s: elem %p already had id %d, new id: %d\n", __func__, elem, id2, id); + idmap->elem2id->remove(elem); + } + + if (idmap->id2elem->contains(id)) { + BMElem *elem2 = idmap->id2elem->lookup(id); + if (elem2 != elem) { + idmap_log_message("%s: elem %p (a %s) took over id from elem %p (a %s)\n", + __func__, + elem, + get_type_name(elem->head.htype), + elem2, + get_type_name(elem2->head.htype)); + } + } + + idmap_debug_insert(idmap, elem, id); + idmap_check_elem(idmap, elem); #endif } -void BM_idmap_release(BMIdMap *idmap, BMElem *elem, bool clear_id) +ATTR_NO_OPT void BM_idmap_release(BMIdMap *idmap, BMElem *elem, bool clear_id) { -#ifdef USE_NEW_IDMAP - int id = BM_ELEM_CD_GET_INT(elem, idmap->cd_id_off[(int)elem->head.htype]); +#ifdef DEBUG_BM_IDMAP + idmap_check_elem(idmap, elem); + + if (idmap->elem2id->contains(elem)) { + int id2 = idmap->elem2id->lookup(elem); + + if (idmap->id2elem->contains(id2)) { + idmap->id2elem->remove(id2); + } + idmap->elem2id->remove(elem); + } +#endif + + int id = BM_ELEM_CD_GET_INT(elem, idmap->cd_id_off[int(elem->head.htype)]); if (id == BM_ID_NONE) { - printf("%s: unassigned id!\n", __func__); + idmap_log_message("%s: unassigned id!\n", __func__); return; }; if (id < 0 || id >= idmap->map_size || (idmap->map[id] && idmap->map[id] != elem)) { - printf("%s: id corruptions\n", __func__); + idmap_log_message("%s: id corruptions\n", __func__); } else { idmap->map[id] = nullptr; @@ -328,18 +537,21 @@ void BM_idmap_release(BMIdMap *idmap, BMElem *elem, bool clear_id) check_idx_map(idmap); if (clear_id) { - BM_ELEM_CD_SET_INT(elem, idmap->cd_id_off[elem->head.htype], BM_ID_NONE); + BM_ELEM_CD_SET_INT(elem, idmap->cd_id_off[int(elem->head.htype)], BM_ID_NONE); } -#endif } -int BM_idmap_check_assign(BMIdMap *idmap, BMElem *elem) +ATTR_NO_OPT int BM_idmap_check_assign(BMIdMap *idmap, BMElem *elem) { - int id = BM_ELEM_CD_GET_INT(elem, idmap->cd_id_off[(int)elem->head.htype]); + int id = BM_ELEM_CD_GET_INT(elem, idmap->cd_id_off[int(elem->head.htype)]); if (id == BM_ID_NONE) { - return BM_idmap_alloc(idmap, elem); + id = BM_idmap_alloc(idmap, elem); } +#ifdef DEBUG_BM_IDMAP + idmap_check_elem(idmap, elem); +#endif + return id; } diff --git a/source/blender/bmesh/intern/bmesh_idmap.h b/source/blender/bmesh/intern/bmesh_idmap.h index d9543f19005..be6f9148338 100644 --- a/source/blender/bmesh/intern/bmesh_idmap.h +++ b/source/blender/bmesh/intern/bmesh_idmap.h @@ -8,8 +8,12 @@ # define WITH_BM_ID_FREELIST #endif +//#define DEBUG_BM_IDMAP /* Debug idmap; note: disables mempool deallocation */ + #define USE_NEW_IDMAP +#define BM_ID_NONE 0 //-1 + #ifdef __cplusplus # include "BLI_map.hh" # include "BLI_vector.hh" @@ -30,11 +34,20 @@ typedef struct BMIdMap { using FreeIdxMap = blender::Map; -/* maps ids to their position within the freelist - only used if freelist is bigger then a certain size, - see FREELIST_HASHMAP_THRESHOLD_HIGH in bmesh_construct.c.*/ + /* maps ids to their position within the freelist + only used if freelist is bigger then a certain size, + see FREELIST_HASHMAP_THRESHOLD_HIGH in bmesh_construct.c.*/ FreeIdxMap *free_idx_map; #endif + +#ifdef DEBUG_BM_IDMAP +# ifdef __cplusplus + blender::Map *elem2id; + blender::Map *id2elem; +# else + void elem_idmap; +# endif +#endif } BMIdMap; #ifdef __cplusplus diff --git a/source/blender/bmesh/intern/bmesh_interp.c b/source/blender/bmesh/intern/bmesh_interp.c index f5d79315ee4..22da2eb169e 100644 --- a/source/blender/bmesh/intern/bmesh_interp.c +++ b/source/blender/bmesh/intern/bmesh_interp.c @@ -781,7 +781,7 @@ void BM_vert_interp_from_face(BMesh *bm, BMVert *v_dst, const BMFace *f_src) void BPy_bm_new_customdata_layout(BMesh *bm, CustomData *cdata, void *state, char htype); void *BPy_bm_new_customdata_layout_pre(BMesh *bm, CustomData *cdata, char htype); -static void update_data_blocks(BMesh *bm, CustomData *olddata, CustomData *data) +ATTR_NO_OPT static void update_data_blocks(BMesh *bm, CustomData *olddata, CustomData *data) { BMIter iter; BLI_mempool *oldpool = olddata->pool; @@ -893,22 +893,15 @@ void BM_data_layers_ensure(BMesh *bm, CustomData *data, BMCustomLayerReq *layers { bool modified = false; CustomData old = *data; - CustomData temp; - eCustomDataMask mask = 0; if (old.layers) { old.layers = MEM_dupallocN(old.layers); } - memset(&temp, 0, sizeof(temp)); - CustomData_reset(&temp); - for (int i = 0; i < totlayer; i++) { BMCustomLayerReq *req = layers + i; int idx; - mask |= 1ULL << (eCustomDataMask)req->type; - if (req->name) { idx = CustomData_get_named_layer_index(data, req->type, req->name); } @@ -920,68 +913,18 @@ void BM_data_layers_ensure(BMesh *bm, CustomData *data, BMCustomLayerReq *layers modified = true; if (req->name) { - CustomData_add_layer_named(&temp, req->type, CD_ASSIGN, NULL, 0, req->name); + CustomData_add_layer_named(data, req->type, CD_ASSIGN, NULL, 0, req->name); + idx = CustomData_get_named_layer_index(data, req->type, req->name); } else { - CustomData_add_layer(&temp, req->type, CD_ASSIGN, NULL, 0); + CustomData_add_layer(data, req->type, CD_ASSIGN, NULL, 0); + idx = CustomData_get_layer_index(data, req->type); } + + data->layers[idx].flag |= req->flag; } } - int htype; - if (data == &bm->vdata) { - htype = BM_VERT; - } - else if (data == &bm->edata) { - htype = BM_EDGE; - } - else if (data == &bm->ldata) { - htype = BM_LOOP; - } - else if (data == &bm->pdata) { - htype = BM_FACE; - } - else { - printf("error in %s!\n", __func__); - CustomData_free(&temp, 0); - return; - } - - CustomDataLayer **nocopy_layers = NULL; - BLI_array_declare(nocopy_layers); - - for (int i = 0; i < data->totlayer; i++) { - if (data->layers[i].flag & CD_FLAG_NOCOPY) { - BLI_array_append(nocopy_layers, &data->layers[i]); - } - } - - if (modified) { - CustomData_merge(&temp, data, mask, CD_ASSIGN, 0); - } - - for (int i = 0; i < BLI_array_len(nocopy_layers); i++) { - nocopy_layers[i]->flag |= CD_FLAG_NOCOPY; - } - - BLI_array_free(nocopy_layers); - - for (int i = 0; i < totlayer; i++) { - BMCustomLayerReq *req = layers + i; - int idx; - - mask |= 1LL << req->type; - - if (req->name) { - idx = CustomData_get_named_layer_index(data, req->type, req->name); - } - else { - idx = CustomData_get_layer_index(data, req->type); - } - - data->layers[idx].flag |= req->flag; - } - if (modified) { /* the pool is now owned by olddata and must not be shared */ data->pool = NULL; @@ -992,8 +935,6 @@ void BM_data_layers_ensure(BMesh *bm, CustomData *data, BMCustomLayerReq *layers if (old.layers) { MEM_freeN(old.layers); } - - CustomData_free(&temp, 0); } void BM_data_layer_add(BMesh *bm, CustomData *data, int type) diff --git a/source/blender/bmesh/intern/bmesh_log.cc b/source/blender/bmesh/intern/bmesh_log.cc index 47d03e2858c..52291a9596e 100644 --- a/source/blender/bmesh/intern/bmesh_log.cc +++ b/source/blender/bmesh/intern/bmesh_log.cc @@ -921,17 +921,26 @@ void BMLogSetDiff::add_vert(BMesh *bm, BMVert *v) { BMID id = entry->get_elem_id(bm, v); + BMLogVert *lv = nullptr; BMLogVert **modified_lv = modified_verts.lookup_ptr(id); + if (modified_lv) { modified_verts.remove(id); - entry->free_logvert(*modified_lv); + lv = *modified_lv; } if (added_verts.contains(id)) { + if (lv) { + entry->free_logvert(lv); + } return; } - added_verts.add(id, entry->alloc_logvert(bm, v)); + if (!lv) { + lv = entry->alloc_logvert(bm, v); + } + + added_verts.add(id, lv); } void BMLogSetDiff::remove_vert(BMesh *bm, BMVert *v) diff --git a/source/blender/bmesh/intern/bmesh_mesh_convert.cc b/source/blender/bmesh/intern/bmesh_mesh_convert.cc index 501e10136fe..badb71e2db9 100644 --- a/source/blender/bmesh/intern/bmesh_mesh_convert.cc +++ b/source/blender/bmesh/intern/bmesh_mesh_convert.cc @@ -235,7 +235,8 @@ static void mesh_attributes_copy_to_bmesh_block(CustomData &data, POINTER_OFFSET(header.data, info.bmesh_offset)); } else { - CustomData_data_set_default_value(&data, info.type, info.n, POINTER_OFFSET(header.data, info.bmesh_offset)); + CustomData_data_set_default_value( + &data, info.type, info.n, POINTER_OFFSET(header.data, info.bmesh_offset)); } } } @@ -1190,7 +1191,10 @@ static void bmesh_block_copy_to_mesh_attributes(const Span } } -void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *me, const struct BMeshToMeshParams *params) +ATTR_NO_OPT void BM_mesh_bm_to_me(Main *bmain, + BMesh *bm, + Mesh *me, + const struct BMeshToMeshParams *params) { using namespace blender; BMVert *v, *eve; diff --git a/source/blender/editors/sculpt_paint/sculpt.cc b/source/blender/editors/sculpt_paint/sculpt.cc index 426726fee1e..96b56df0f55 100644 --- a/source/blender/editors/sculpt_paint/sculpt.cc +++ b/source/blender/editors/sculpt_paint/sculpt.cc @@ -4100,6 +4100,10 @@ static void sculpt_topology_update(Sculpt *sd, /* build brush radius scale */ float radius_scale = 1.0f; + if (brush->dyntopo.flag & DYNTOPO_DISABLED) { + return; + } + /* Build a list of all nodes that are potentially within the brush's area of influence. */ const bool use_original = sculpt_tool_needs_original(SCULPT_get_tool(ss, brush)) ? true : diff --git a/source/blender/makesrna/intern/rna_brush.c b/source/blender/makesrna/intern/rna_brush.c index 06a133f7d3b..c2b856b658b 100644 --- a/source/blender/makesrna/intern/rna_brush.c +++ b/source/blender/makesrna/intern/rna_brush.c @@ -1308,7 +1308,7 @@ static void rna_def_dyntopo_settings(BlenderRNA *brna) prop = RNA_def_property(srna, "disabled", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", DYNTOPO_DISABLED); RNA_def_property_ui_icon(prop, ICON_NONE, 0); - RNA_def_property_ui_text(prop, "Disable", "Disable Dyntopo for this brush"); + RNA_def_property_ui_text(prop, "No Dyntopo", "Disable Dyntopo for this brush"); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); RNA_def_property_update(prop, 0, "rna_Brush_dyntopo_update"); -- 2.30.2 From 26b3aefb8f3d1dc50f07c94a75607eab3f009176 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Mon, 20 Feb 2023 18:37:00 -0800 Subject: [PATCH 024/279] temp-sculpt-dyntopo: More debugging stuff --- source/blender/blenlib/intern/BLI_mempool.c | 17 +++++++++++++---- source/blender/bmesh/intern/bmesh_construct.c | 2 +- source/blender/bmesh/intern/bmesh_idmap.h | 2 +- source/blender/bmesh/intern/bmesh_interp.c | 4 ++-- source/blender/bmesh/intern/bmesh_mesh.cc | 1 - .../blender/bmesh/intern/bmesh_mesh_convert.cc | 2 +- source/blender/editors/sculpt_paint/sculpt.cc | 17 ++++++++++------- 7 files changed, 28 insertions(+), 17 deletions(-) diff --git a/source/blender/blenlib/intern/BLI_mempool.c b/source/blender/blenlib/intern/BLI_mempool.c index b9fefdbdcf5..6e703ec5490 100644 --- a/source/blender/blenlib/intern/BLI_mempool.c +++ b/source/blender/blenlib/intern/BLI_mempool.c @@ -149,6 +149,7 @@ struct BLI_mempool { char *memtag; char *memtag_chunk; + bool dead; }; #define MEMPOOL_ELEM_SIZE_MIN (sizeof(void *) * 2) @@ -482,7 +483,7 @@ BLI_mempool *BLI_mempool_create_for_tasks(const unsigned int esize, return pool; } -static void mempool_chunk_free(BLI_mempool_chunk *mpchunk, BLI_mempool *pool) +ATTR_NO_OPT static void mempool_chunk_free(BLI_mempool_chunk *mpchunk, BLI_mempool *pool) { BLI_asan_unpoison(mpchunk, sizeof(BLI_mempool_chunk) + pool->esize * pool->csize); BLI_asan_safe_free(mpchunk); @@ -493,7 +494,9 @@ ATTR_NO_OPT static void mempool_chunk_free_all(BLI_mempool_chunk *mpchunk, BLI_m BLI_mempool_chunk *mpchunk_next; for (; mpchunk; mpchunk = mpchunk_next) { + BLI_asan_unpoison(mpchunk, sizeof(BLI_mempool_chunk)); mpchunk_next = mpchunk->next; + mempool_chunk_free(mpchunk, pool); } } @@ -535,6 +538,7 @@ BLI_mempool *BLI_mempool_create_ex( pool->memtag = memtag; pool->memtag_chunk = strdup(buf); + pool->dead = false; pool->totchunk = 0; pool->chunktable = NULL; @@ -742,7 +746,9 @@ int BLI_mempool_get_size(BLI_mempool *pool) void BLI_mempool_ignore_free(BLI_mempool *pool) { + mempool_unpoison(pool); pool->flag |= BLI_MEMPOOL_IGNORE_FREE; + mempool_poison(pool); } /** @@ -750,14 +756,15 @@ void BLI_mempool_ignore_free(BLI_mempool *pool) * * \note doesn't protect against double frees, take care! */ -void BLI_mempool_free(BLI_mempool *pool, void *addr) +ATTR_NO_OPT void BLI_mempool_free(BLI_mempool *pool, void *addr) { + mempool_unpoison(pool); + if (pool->flag & BLI_MEMPOOL_IGNORE_FREE) { + mempool_poison(pool); return; } - mempool_unpoison(pool); - BLI_freenode *newhead = addr; #ifndef NDEBUG @@ -1209,6 +1216,8 @@ void BLI_mempool_destroy(BLI_mempool *pool) mempool_unpoison(pool); mempool_chunk_free_all(pool->chunks, pool); + pool->dead = true; + if (pool->memtag) { free(pool->memtag); } diff --git a/source/blender/bmesh/intern/bmesh_construct.c b/source/blender/bmesh/intern/bmesh_construct.c index 611c7627cf0..89119e3f7bb 100644 --- a/source/blender/bmesh/intern/bmesh_construct.c +++ b/source/blender/bmesh/intern/bmesh_construct.c @@ -659,7 +659,7 @@ void BM_mesh_copy_init_customdata(BMesh *bm_dst, BMesh *bm_src, const BMAllocTem // forcibly copy mesh_id layers CustomData *srcdatas[4] = {&bm_src->vdata, &bm_src->edata, &bm_src->ldata, &bm_src->pdata}; - CustomData *dstdatas[4] = {&bm_dst->vdata, &bm_dst->edata, &bm_dst->ldata, &bm_dst->pdata}; + //CustomData *dstdatas[4] = {&bm_dst->vdata, &bm_dst->edata, &bm_dst->ldata, &bm_dst->pdata}; for (int i = 0; i < 4; i++) { CustomData *cdata = srcdatas[i]; diff --git a/source/blender/bmesh/intern/bmesh_idmap.h b/source/blender/bmesh/intern/bmesh_idmap.h index be6f9148338..9af36a34a57 100644 --- a/source/blender/bmesh/intern/bmesh_idmap.h +++ b/source/blender/bmesh/intern/bmesh_idmap.h @@ -8,7 +8,7 @@ # define WITH_BM_ID_FREELIST #endif -//#define DEBUG_BM_IDMAP /* Debug idmap; note: disables mempool deallocation */ +#define DEBUG_BM_IDMAP /* Debug idmap; note: disables mempool deallocation */ #define USE_NEW_IDMAP diff --git a/source/blender/bmesh/intern/bmesh_interp.c b/source/blender/bmesh/intern/bmesh_interp.c index 22da2eb169e..15fd861cd46 100644 --- a/source/blender/bmesh/intern/bmesh_interp.c +++ b/source/blender/bmesh/intern/bmesh_interp.c @@ -32,7 +32,7 @@ ATTR_NO_OPT static void copy_cdata_simple(BMesh *bm, const BMElem *ele_src) { int cd_tflags; - MToolFlags saved_tflags = {}; + MToolFlags saved_tflags = {0}; if ((cd_tflags = CustomData_get_offset(data_layer, CD_TOOLFLAGS)) != -1) { saved_tflags = *(MToolFlags *)BM_ELEM_CD_GET_VOID_P(ele_dst, cd_tflags); @@ -788,7 +788,7 @@ ATTR_NO_OPT static void update_data_blocks(BMesh *bm, CustomData *olddata, Custo void *block; CustomDataLayer **nocopy_layers = NULL; - BLI_array_staticdeclare(nocopy_layers, 1024); + BLI_array_staticdeclare(nocopy_layers, 32); /* Temporarily clear CD_FLAG_ELEM_NOCOPY flags. */ for (int i = 0; i < data->totlayer; i++) { diff --git a/source/blender/bmesh/intern/bmesh_mesh.cc b/source/blender/bmesh/intern/bmesh_mesh.cc index 6af871c44b4..5dc02aa879e 100644 --- a/source/blender/bmesh/intern/bmesh_mesh.cc +++ b/source/blender/bmesh/intern/bmesh_mesh.cc @@ -371,7 +371,6 @@ void BM_mesh_data_free(BMesh *bm) void BM_mesh_clear(BMesh *bm) { const bool use_toolflags = bm->use_toolflags; - const int idmap_flags = bm->idmap.flag; /* free old mesh */ BM_mesh_data_free(bm); diff --git a/source/blender/bmesh/intern/bmesh_mesh_convert.cc b/source/blender/bmesh/intern/bmesh_mesh_convert.cc index badb71e2db9..eb1d6cc42e8 100644 --- a/source/blender/bmesh/intern/bmesh_mesh_convert.cc +++ b/source/blender/bmesh/intern/bmesh_mesh_convert.cc @@ -1270,7 +1270,7 @@ ATTR_NO_OPT void BM_mesh_bm_to_me(Main *bmain, need_edgesel |= BM_ELEM_CD_GET_BOOL(l, edgesel_offset); } } - if (pin_layer_index) { + if (pin_layer_index >= 0) { BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) { need_pin |= BM_ELEM_CD_GET_BOOL(l, pin_offset); } diff --git a/source/blender/editors/sculpt_paint/sculpt.cc b/source/blender/editors/sculpt_paint/sculpt.cc index 96b56df0f55..6decda688b6 100644 --- a/source/blender/editors/sculpt_paint/sculpt.cc +++ b/source/blender/editors/sculpt_paint/sculpt.cc @@ -76,6 +76,7 @@ #include "RNA_access.h" #include "RNA_define.h" +#include "../../bmesh/intern/bmesh_idmap.h" #include "bmesh.h" using blender::float3; @@ -4166,14 +4167,16 @@ static void sculpt_topology_update(Sculpt *sd, SCULPT_dyntopo_automasking_init(ss, sd, brush, ob, &mask_cb, &mask_cb_data); - int actv = -1, actf = -1; + int actv = BM_ID_NONE, actf = BM_ID_NONE; if (ss->active_vertex.i != PBVH_REF_NONE) { - actv = BM_ELEM_GET_ID(ss->bm, (BMElem *)ss->active_vertex.i); + BM_idmap_check_assign(ss->bm_idmap, (BMElem *)ss->active_vertex.i); + actv = BM_idmap_get_id(ss->bm_idmap, (BMElem *)ss->active_vertex.i); } if (ss->active_face.i != PBVH_REF_NONE) { - actf = BM_ELEM_GET_ID(ss->bm, (BMElem *)ss->active_face.i); + BM_idmap_check_assign(ss->bm_idmap, (BMElem *)ss->active_face.i); + actf = BM_idmap_get_id(ss->bm_idmap, (BMElem *)ss->active_face.i); } /* do nodes under the brush cursor */ @@ -4196,8 +4199,8 @@ static void sculpt_topology_update(Sculpt *sd, SCULPT_dyntopo_automasking_end(mask_cb_data); - if (actv != -1) { - BMVert *v = (BMVert *)BM_ELEM_FROM_ID_SAFE(ss->bm, actv); + if (actv != BM_ID_NONE) { + BMVert *v = (BMVert *)BM_idmap_lookup(ss->bm_idmap, actv); if (v && v->head.htype == BM_VERT) { ss->active_vertex.i = (intptr_t)v; @@ -4207,8 +4210,8 @@ static void sculpt_topology_update(Sculpt *sd, } } - if (actf != -1) { - BMFace *f = (BMFace *)BM_ELEM_FROM_ID_SAFE(ss->bm, actf); + if (actf != BM_ID_NONE) { + BMFace *f = (BMFace *)BM_idmap_lookup(ss->bm_idmap, actf); if (f && f->head.htype == BM_FACE) { ss->active_face.i = (intptr_t)f; -- 2.30.2 From f5748abef854529c8e82f1aad6797e9a6eea6e06 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Tue, 7 Mar 2023 16:30:02 -0800 Subject: [PATCH 025/279] temp-sculpt-dyntopo: Fix compile errors --- source/blender/blenkernel/BKE_paint.h | 2 ++ source/blender/blenkernel/intern/paint.cc | 17 ++++++++++++++++- .../blender/editors/sculpt_paint/paint_mask.cc | 4 ++-- source/blender/editors/sculpt_paint/sculpt.cc | 12 +----------- .../editors/sculpt_paint/sculpt_filter_mesh.cc | 2 +- .../blender/makesrna/intern/rna_sculpt_paint.c | 4 +--- 6 files changed, 23 insertions(+), 18 deletions(-) diff --git a/source/blender/blenkernel/BKE_paint.h b/source/blender/blenkernel/BKE_paint.h index 321cefba20d..c171c7bcc2e 100644 --- a/source/blender/blenkernel/BKE_paint.h +++ b/source/blender/blenkernel/BKE_paint.h @@ -1135,6 +1135,8 @@ bool BKE_sculptsession_use_pbvh_draw(const struct Object *ob, const struct Regio char BKE_get_fset_boundary_symflag(struct Object *object); +bool BKE_sculpt_has_persistent_base(SculptSession *ss); + enum { SCULPT_MASK_LAYER_CALC_VERT = (1 << 0), SCULPT_MASK_LAYER_CALC_LOOP = (1 << 1), diff --git a/source/blender/blenkernel/intern/paint.cc b/source/blender/blenkernel/intern/paint.cc index 1f4f8ef9623..61233604339 100644 --- a/source/blender/blenkernel/intern/paint.cc +++ b/source/blender/blenkernel/intern/paint.cc @@ -76,7 +76,7 @@ #include "bmesh_log.h" // TODO: figure out bad cross module refs -extern "C" void SCULPT_undo_ensure_bmlog(Object *ob); +void SCULPT_undo_ensure_bmlog(Object *ob); static void init_sculptvert_layer(SculptSession *ss, PBVH *pbvh, int totvert); @@ -3885,3 +3885,18 @@ bool BKE_sculpt_attribute_destroy(Object *ob, SculptAttribute *attr) return true; } + +bool BKE_sculpt_has_persistent_base(SculptSession *ss) +{ + if (ss->bm) { + return CustomData_get_named_layer_index( + &ss->bm->vdata, CD_PROP_FLOAT3, SCULPT_ATTRIBUTE_NAME(persistent_co)) != -1; + } + else if (ss->vdata) { + return CustomData_get_named_layer_index( + ss->vdata, CD_PROP_FLOAT3, SCULPT_ATTRIBUTE_NAME(persistent_co)) != -1; + } + + /* Detect multires. */ + return ss->attrs.persistent_co; +} diff --git a/source/blender/editors/sculpt_paint/paint_mask.cc b/source/blender/editors/sculpt_paint/paint_mask.cc index 3e7c846cb95..c0f27b224be 100644 --- a/source/blender/editors/sculpt_paint/paint_mask.cc +++ b/source/blender/editors/sculpt_paint/paint_mask.cc @@ -1342,7 +1342,7 @@ static void sculpt_gesture_trim_geometry_generate(SculptGestureContext *sgcontex BKE_mesh_calc_edges(trim_operation->mesh, false, false); sculpt_gesture_trim_normals_update(sgcontext); - mp = trim_operation->mesh->mpoly + tot_tris_face * 2; + MPoly *mp = &polys[tot_tris_face * 2]; bool *sharp_edge = (bool *)CustomData_get_layer_named_for_write( &trim_operation->mesh->edata, CD_PROP_BOOL, "sharp_edge", trim_operation->mesh->totedge); @@ -1359,7 +1359,7 @@ static void sculpt_gesture_trim_geometry_generate(SculptGestureContext *sgcontex /* flag edges as sharp for dyntopo remesher */ for (int i = 0; i < tot_screen_points * 2; i++, mp++) { - ml = trim_operation->mesh->mloop + mp->loopstart; + MLoop *ml = &loops[mp->loopstart]; sharp_edge[ml[1].e] = true; } diff --git a/source/blender/editors/sculpt_paint/sculpt.cc b/source/blender/editors/sculpt_paint/sculpt.cc index c1138163595..d32af2d0ee1 100644 --- a/source/blender/editors/sculpt_paint/sculpt.cc +++ b/source/blender/editors/sculpt_paint/sculpt.cc @@ -307,17 +307,7 @@ void SCULPT_vertex_normal_get(SculptSession *ss, PBVHVertRef vertex, float no[3] bool SCULPT_has_persistent_base(SculptSession *ss) { - if (ss->bm) { - return CustomData_get_named_layer_index( - &ss->bm->vdata, CD_PROP_FLOAT3, SCULPT_ATTRIBUTE_NAME(persistent_co)) != -1; - } - else if (ss->vdata) { - return CustomData_get_named_layer_index( - ss->vdata, CD_PROP_FLOAT3, SCULPT_ATTRIBUTE_NAME(persistent_co)) != -1; - } - - /* Detect multires. */ - return ss->attrs.persistent_co; + return BKE_sculpt_has_persistent_base(ss); } const float *SCULPT_vertex_persistent_co_get(SculptSession *ss, PBVHVertRef vertex) diff --git a/source/blender/editors/sculpt_paint/sculpt_filter_mesh.cc b/source/blender/editors/sculpt_paint/sculpt_filter_mesh.cc index 53cbaa4175a..d56a65b01df 100644 --- a/source/blender/editors/sculpt_paint/sculpt_filter_mesh.cc +++ b/source/blender/editors/sculpt_paint/sculpt_filter_mesh.cc @@ -826,7 +826,7 @@ static void sculpt_mesh_filter_cancel(bContext *C, wmOperator *op) SCULPT_orig_vert_data_init(&orig_data, ob, nodes[i], SCULPT_UNDO_COORDS); BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) { - SCULPT_orig_vert_data_update(&orig_data, &vd); + SCULPT_orig_vert_data_update(&orig_data, vd.vertex); copy_v3_v3(vd.co, orig_data.co); } diff --git a/source/blender/makesrna/intern/rna_sculpt_paint.c b/source/blender/makesrna/intern/rna_sculpt_paint.c index cbe61c06e39..5002a177589 100644 --- a/source/blender/makesrna/intern/rna_sculpt_paint.c +++ b/source/blender/makesrna/intern/rna_sculpt_paint.c @@ -575,8 +575,6 @@ static char *rna_GPencilSculptGuide_path(const PointerRNA *UNUSED(ptr)) return BLI_strdup("tool_settings.gpencil_sculpt.guide"); } -bool SCULPT_has_persistent_base(SculptSession *ss); - bool rna_Sculpt_has_persistent_base(bContext *C) { ePaintMode mode = BKE_paintmode_get_active_from_context(C); @@ -590,7 +588,7 @@ bool rna_Sculpt_has_persistent_base(bContext *C) return false; } - return SCULPT_has_persistent_base(ob->sculpt); + return BKE_sculpt_has_persistent_base(ob->sculpt); } static void rna_Sculpt_automasking_invert_cavity_set(PointerRNA *ptr, bool val) -- 2.30.2 From a72c470c5ec686d431b054e2d975c5af20f875f3 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Tue, 7 Mar 2023 18:02:18 -0800 Subject: [PATCH 026/279] temp-sculpt-dyntopo: Finish merge --- source/blender/blenkernel/BKE_paint.h | 1 + source/blender/blenkernel/BKE_pbvh.h | 3 +- .../blender/blenkernel/intern/mesh_remap.cc | 4 +- source/blender/blenkernel/intern/paint.cc | 5 +- source/blender/blenkernel/intern/pbvh.cc | 13 ++-- .../blender/blenkernel/intern/subdiv_mesh.cc | 4 +- .../intern/draw_cache_impl_subdivision.cc | 4 +- .../blender/editors/object/object_modifier.cc | 3 +- .../blender/editors/object/object_vgroup.cc | 3 +- .../editors/sculpt_paint/paint_vertex.cc | 44 +++++++------- source/blender/editors/sculpt_paint/sculpt.cc | 60 +------------------ .../editors/sculpt_paint/sculpt_api.cc | 36 +++++------ .../editors/sculpt_paint/sculpt_dyntopo.cc | 31 +++++----- .../editors/sculpt_paint/sculpt_expand.cc | 4 +- .../editors/sculpt_paint/sculpt_geodesic.cc | 6 +- .../blender/modifiers/intern/MOD_multires.cc | 2 +- 16 files changed, 81 insertions(+), 142 deletions(-) diff --git a/source/blender/blenkernel/BKE_paint.h b/source/blender/blenkernel/BKE_paint.h index 0266daa5e5c..93c8afd0c5d 100644 --- a/source/blender/blenkernel/BKE_paint.h +++ b/source/blender/blenkernel/BKE_paint.h @@ -983,6 +983,7 @@ typedef struct SculptSession { int last_automasking_settings_hash; uchar last_automask_stroke_id; bool *sharp_edge; + bool *seam_edge; bool islands_valid; /* Is attrs.topology_island_key valid? */ bool hard_edge_mode; diff --git a/source/blender/blenkernel/BKE_pbvh.h b/source/blender/blenkernel/BKE_pbvh.h index 87594f00757..50a3b9c590b 100644 --- a/source/blender/blenkernel/BKE_pbvh.h +++ b/source/blender/blenkernel/BKE_pbvh.h @@ -1287,7 +1287,8 @@ void BKE_pbvh_update_vert_boundary_faces(int *boundary_flags, struct MSculptVert *msculptverts, const struct MeshElemMap *pmap, PBVHVertRef vertex, - const bool *sharp_edges); + const bool *sharp_edges, + const bool *seam_edges); void BKE_pbvh_update_vert_boundary_grids(PBVH *pbvh, struct SubdivCCG *subdiv_ccg, PBVHVertRef vertex); diff --git a/source/blender/blenkernel/intern/mesh_remap.cc b/source/blender/blenkernel/intern/mesh_remap.cc index eb3c26b84e8..361616b67ab 100644 --- a/source/blender/blenkernel/intern/mesh_remap.cc +++ b/source/blender/blenkernel/intern/mesh_remap.cc @@ -739,11 +739,9 @@ void BKE_mesh_remap_calc_edges_from_mesh(const int mode, BKE_mesh_vert_edge_map_create(&vert_to_edge_src_map, &vert_to_edge_src_map_mem, - nullptr, edges_src.data(), num_verts_src, - int(edges_src.size()), - false); + int(edges_src.size())); BKE_bvhtree_from_mesh_get(&treedata, me_src, BVHTREE_FROM_VERTS, 2); nearest.index = -1; diff --git a/source/blender/blenkernel/intern/paint.cc b/source/blender/blenkernel/intern/paint.cc index 6796f5164bf..9947b0fa7b6 100644 --- a/source/blender/blenkernel/intern/paint.cc +++ b/source/blender/blenkernel/intern/paint.cc @@ -1863,6 +1863,8 @@ static void sculpt_update_object( ss->vert_positions = BKE_mesh_vert_positions_for_write(me); ss->sharp_edge = (bool *)CustomData_get_layer_named_for_write( &me->edata, CD_PROP_BOOL, "sharp_edge", me->totedge); + ss->seam_edge = (bool *)CustomData_get_layer_named_for_write( + &me->edata, CD_PROP_BOOL, ".uv_seam", me->totedge); ss->polys = me->polys().data(); ss->edges = me->edges().data(); ss->loops = me->loops().data(); @@ -2814,7 +2816,8 @@ static void init_sculptvert_layer_faces(SculptSession *ss, PBVH *pbvh, int totve ss->msculptverts, ss->pmap->pmap, vertex, - ss->sharp_edge); + ss->sharp_edge, + ss->seam_edge); // can't fully update boundary here, so still flag for update BKE_sculpt_boundary_flag_update(ss, vertex); diff --git a/source/blender/blenkernel/intern/pbvh.cc b/source/blender/blenkernel/intern/pbvh.cc index a21236869a8..7f38a856f32 100644 --- a/source/blender/blenkernel/intern/pbvh.cc +++ b/source/blender/blenkernel/intern/pbvh.cc @@ -4114,7 +4114,7 @@ void BKE_pbvh_pmap_to_edges(PBVH *pbvh, int len = 0; for (int i = 0; i < map->count; i++) { - const MPoly *mp = pbvh->mpoly + map->indices[i]; + const MPoly *mp = pbvh->polys + map->indices[i]; const MLoop *ml = pbvh->mloop + mp->loopstart; if (pbvh->hide_poly && pbvh->hide_poly[map->indices[i]]) { @@ -4390,7 +4390,8 @@ void BKE_pbvh_update_vert_boundary_faces(int *boundary_flags, MSculptVert *msculptverts, const MeshElemMap *pmap, PBVHVertRef vertex, - const bool *sharp_edges) + const bool *sharp_edges, + const bool *seam_edges) { MSculptVert *mv = msculptverts + vertex.i; const MeshElemMap *vert_map = &pmap[vertex.i]; @@ -4426,7 +4427,7 @@ void BKE_pbvh_update_vert_boundary_faces(int *boundary_flags, totsharp++; } - if (me->flag & ME_SEAM) { + if (seam_edges && seam_edges[ml->e]) { *flags |= SCULPT_BOUNDARY_SEAM; totseam++; } @@ -4709,14 +4710,14 @@ PBVH *BKE_pbvh_get_or_free_cached(Object *ob, Mesh *me, PBVHType pbvh_type) case PBVH_BMESH: break; case PBVH_FACES: - pbvh->vert_normals = BKE_mesh_vertex_normals_for_write(me); + pbvh->vert_normals = BKE_mesh_vert_normals_for_write(me); case PBVH_GRIDS: if (!pbvh->deformed) { pbvh->vert_positions = BKE_mesh_vert_positions_for_write(me); } - pbvh->mloop = me->mloop; - pbvh->mpoly = me->mpoly; + pbvh->mloop = me->loops().data(); + pbvh->polys = me->polys().data(); pbvh->vdata = &me->vdata; pbvh->ldata = &me->ldata; pbvh->pdata = &me->pdata; diff --git a/source/blender/blenkernel/intern/subdiv_mesh.cc b/source/blender/blenkernel/intern/subdiv_mesh.cc index 76eb047dea5..39e10fb03e6 100644 --- a/source/blender/blenkernel/intern/subdiv_mesh.cc +++ b/source/blender/blenkernel/intern/subdiv_mesh.cc @@ -1099,11 +1099,9 @@ static void subdiv_mesh_vertex_of_loose_edge(const SubdivForeachContext *foreach if (ctx->vert_to_edge_map == nullptr) { BKE_mesh_vert_edge_map_create(&ctx->vert_to_edge_map, &ctx->vert_to_edge_buffer, - ctx->coarse_positions, ctx->coarse_edges.data(), coarse_mesh->totvert, - ctx->coarse_mesh->totedge, - false); + ctx->coarse_mesh->totedge); } } diff --git a/source/blender/draw/intern/draw_cache_impl_subdivision.cc b/source/blender/draw/intern/draw_cache_impl_subdivision.cc index 752bfe6277e..a8966d8e31f 100644 --- a/source/blender/draw/intern/draw_cache_impl_subdivision.cc +++ b/source/blender/draw/intern/draw_cache_impl_subdivision.cc @@ -2230,11 +2230,9 @@ void DRW_subdivide_loose_geom(DRWSubdivCache *subdiv_cache, MeshBufferCache *cac MeshElemMap *vert_to_edge_map; BKE_mesh_vert_edge_map_create(&vert_to_edge_map, &vert_to_edge_buffer, - reinterpret_cast(coarse_positions.data()), coarse_edges.data(), coarse_positions.size(), - coarse_edges.size(), - false); + coarse_edges.size()); for (int i = 0; i < coarse_loose_edge_len; i++) { const int coarse_edge_index = cache->loose_geom.edges[i]; diff --git a/source/blender/editors/object/object_modifier.cc b/source/blender/editors/object/object_modifier.cc index f722b146cc1..b1add27403a 100644 --- a/source/blender/editors/object/object_modifier.cc +++ b/source/blender/editors/object/object_modifier.cc @@ -2964,8 +2964,7 @@ static Object *modifier_skin_armature_create(Depsgraph *depsgraph, Main *bmain, CustomData_get_layer_for_write(&me->vdata, CD_MVERT_SKIN, me->totvert)); int *emap_mem; MeshElemMap *emap; - BKE_mesh_vert_edge_map_create( - &emap, &emap_mem, nullptr, me_edges.data(), me->totvert, me->totedge, false); + BKE_mesh_vert_edge_map_create(&emap, &emap_mem, me_edges.data(), me->totvert, me->totedge); BLI_bitmap *edges_visited = BLI_BITMAP_NEW(me->totedge, "edge_visited"); diff --git a/source/blender/editors/object/object_vgroup.cc b/source/blender/editors/object/object_vgroup.cc index 097a92a8418..d8e17ac86b7 100644 --- a/source/blender/editors/object/object_vgroup.cc +++ b/source/blender/editors/object/object_vgroup.cc @@ -1588,8 +1588,7 @@ static void vgroup_smooth_subset(Object *ob, emap_mem = nullptr; } else { - BKE_mesh_vert_edge_map_create( - &emap, &emap_mem, nullptr, me->edges().data(), me->totvert, me->totedge, false); + BKE_mesh_vert_edge_map_create(&emap, &emap_mem, me->edges().data(), me->totvert, me->totedge); } weight_accum_prev = static_cast( diff --git a/source/blender/editors/sculpt_paint/paint_vertex.cc b/source/blender/editors/sculpt_paint/paint_vertex.cc index 5d4c2d3e105..ccac1d9e0ec 100644 --- a/source/blender/editors/sculpt_paint/paint_vertex.cc +++ b/source/blender/editors/sculpt_paint/paint_vertex.cc @@ -1252,15 +1252,15 @@ static void vertex_paint_init_session_data(const ToolSettings *ts, Object *ob) gmap->vert_to_poly = nullptr; BKE_mesh_vert_loop_map_create(&gmap->vert_to_loop, &gmap->vert_map_mem, - me->mpoly, - me->mloop, + me->polys().data(), + me->loops().data(), me->totvert, me->totpoly, me->totloop); BKE_mesh_vert_poly_map_create(&gmap->vert_to_poly, &gmap->poly_map_mem, - me->mpoly, - me->mloop, + me->polys().data(), + me->loops().data(), me->totvert, me->totpoly, me->totloop); @@ -1974,7 +1974,7 @@ static void do_wpaint_brush_blur_task_cb_ex(void *__restrict userdata, if (sculpt_brush_test_sq_fn(&test, vd.co)) { /* For grid based pbvh, take the vert whose loop corresponds to the current grid. * Otherwise, take the current vert. */ - const int v_index = has_grids ? ss->mloop[vd.grid_indices[vd.g]].v : vd.vert_indices[vd.i]; + const int v_index = has_grids ? ss->loops[vd.grid_indices[vd.g]].v : vd.vert_indices[vd.i]; const float grid_alpha = has_grids ? 1.0f / vd.gridsize : 1.0f; /* If the vertex is selected */ if (!(use_face_sel || use_vert_sel) || select_vert[v_index]) { @@ -1988,7 +1988,7 @@ static void do_wpaint_brush_blur_task_cb_ex(void *__restrict userdata, total_hit_loops += poly.totloop; for (int k = 0; k < poly.totloop; k++) { const int l_index = poly.loopstart + k; - const MLoop *ml = &ss->mloop[l_index]; + const MLoop *ml = &ss->loops[l_index]; weight_final += data->wpd->precomputed_weight[ml->v]; } } @@ -2071,7 +2071,7 @@ static void do_wpaint_brush_smear_task_cb_ex(void *__restrict userdata, if (sculpt_brush_test_sq_fn(&test, vd.co)) { /* For grid based pbvh, take the vert whose loop corresponds to the current grid. * Otherwise, take the current vert. */ - const int v_index = has_grids ? ss->mloop[vd.grid_indices[vd.g]].v : vd.vert_indices[vd.i]; + const int v_index = has_grids ? ss->loops[vd.grid_indices[vd.g]].v : vd.vert_indices[vd.i]; const float grid_alpha = has_grids ? 1.0f / vd.gridsize : 1.0f; const float3 &mv_curr = ss->vert_positions[v_index]; @@ -2096,7 +2096,7 @@ static void do_wpaint_brush_smear_task_cb_ex(void *__restrict userdata, for (int j = 0; j < gmap->vert_to_poly[v_index].count; j++) { const int p_index = gmap->vert_to_poly[v_index].indices[j]; const MPoly &poly = ss->polys[p_index]; - const MLoop *ml_other = &ss->mloop[poly.loopstart]; + const MLoop *ml_other = &ss->loops[poly.loopstart]; for (int k = 0; k < poly.totloop; k++, ml_other++) { const uint v_other_index = ml_other->v; if (v_other_index != v_index) { @@ -2180,7 +2180,7 @@ static void do_wpaint_brush_draw_task_cb_ex(void *__restrict userdata, /* NOTE: grids are 1:1 with corners (aka loops). * For multires, take the vert whose loop corresponds to the current grid. * Otherwise, take the current vert. */ - const int v_index = has_grids ? ss->mloop[vd.grid_indices[vd.g]].v : vd.vert_indices[vd.i]; + const int v_index = has_grids ? ss->loops[vd.grid_indices[vd.g]].v : vd.vert_indices[vd.i]; const float grid_alpha = has_grids ? 1.0f / vd.gridsize : 1.0f; /* If the vertex is selected */ @@ -2250,7 +2250,7 @@ static void do_wpaint_brush_calc_average_weight_cb_ex(void *__restrict userdata, 1.0f; if (angle_cos > 0.0 && BKE_brush_curve_strength(data->brush, sqrtf(test.dist), cache->radius) > 0.0) { - const int v_index = has_grids ? ss->mloop[vd.grid_indices[vd.g]].v : vd.vert_indices[vd.i]; + const int v_index = has_grids ? ss->loops[vd.grid_indices[vd.g]].v : vd.vert_indices[vd.i]; /* If the vertex is selected. */ if (!(use_face_sel || use_vert_sel) || select_vert[v_index]) { @@ -3003,7 +3003,7 @@ static void do_vpaint_brush_blur_loops(bContext *C, if (sculpt_brush_test_sq_fn(&test, vd.co)) { /* For grid based pbvh, take the vert whose loop corresponds to the current grid. * Otherwise, take the current vert. */ - const int v_index = has_grids ? ss->mloop[vd.grid_indices[vd.g]].v : + const int v_index = has_grids ? ss->loops[vd.grid_indices[vd.g]].v : vd.vert_indices[vd.i]; const float grid_alpha = has_grids ? 1.0f / vd.gridsize : 1.0f; @@ -3062,7 +3062,7 @@ static void do_vpaint_brush_blur_loops(bContext *C, for (int j = 0; j < gmap->vert_to_poly[v_index].count; j++) { const int p_index = gmap->vert_to_poly[v_index].indices[j]; const int l_index = gmap->vert_to_loop[v_index].indices[j]; - BLI_assert(ss->mloop[l_index].v == v_index); + BLI_assert(ss->loops[l_index].v == v_index); if (!use_face_sel || select_poly[p_index]) { Color color_orig(0, 0, 0, 0); /* unused when array is nullptr */ @@ -3147,7 +3147,7 @@ static void do_vpaint_brush_blur_verts(bContext *C, if (sculpt_brush_test_sq_fn(&test, vd.co)) { /* For grid based pbvh, take the vert whose loop corresponds to the current grid. * Otherwise, take the current vert. */ - const int v_index = has_grids ? ss->mloop[vd.grid_indices[vd.g]].v : + const int v_index = has_grids ? ss->loops[vd.grid_indices[vd.g]].v : vd.vert_indices[vd.i]; const float grid_alpha = has_grids ? 1.0f / vd.gridsize : 1.0f; @@ -3177,7 +3177,7 @@ static void do_vpaint_brush_blur_verts(bContext *C, total_hit_loops += poly.totloop; for (int k = 0; k < poly.totloop; k++) { const uint l_index = poly.loopstart + k; - const uint v_index = ss->mloop[l_index].v; + const uint v_index = ss->loops[l_index].v; Color *col = lcol + v_index; @@ -3208,7 +3208,7 @@ static void do_vpaint_brush_blur_verts(bContext *C, for (int j = 0; j < gmap->vert_to_poly[v_index].count; j++) { const int p_index = gmap->vert_to_poly[v_index].indices[j]; - BLI_assert(ss->mloop[gmap->vert_to_loop[v_index].indices[j]].v == v_index); + BLI_assert(ss->loops[gmap->vert_to_loop[v_index].indices[j]].v == v_index); if (!use_face_sel || select_poly[p_index]) { Color color_orig(0, 0, 0, 0); /* unused when array is nullptr */ @@ -3300,7 +3300,7 @@ static void do_vpaint_brush_smear(bContext *C, if (sculpt_brush_test_sq_fn(&test, vd.co)) { /* For grid based pbvh, take the vert whose loop corresponds to the current grid. * Otherwise, take the current vert. */ - const int v_index = has_grids ? ss->mloop[vd.grid_indices[vd.g]].v : + const int v_index = has_grids ? ss->loops[vd.grid_indices[vd.g]].v : vd.vert_indices[vd.i]; const float grid_alpha = has_grids ? 1.0f / vd.gridsize : 1.0f; const float3 &mv_curr = &ss->vert_positions[v_index]; @@ -3332,11 +3332,11 @@ static void do_vpaint_brush_smear(bContext *C, for (int j = 0; j < gmap->vert_to_poly[v_index].count; j++) { const int p_index = gmap->vert_to_poly[v_index].indices[j]; const int l_index = gmap->vert_to_loop[v_index].indices[j]; - BLI_assert(ss->mloop[l_index].v == v_index); + BLI_assert(ss->loops[l_index].v == v_index); UNUSED_VARS_NDEBUG(l_index); const MPoly &poly = ss->polys[p_index]; if (!use_face_sel || select_poly[p_index]) { - const MLoop *ml_other = &ss->mloop[poly.loopstart]; + const MLoop *ml_other = &ss->loops[poly.loopstart]; for (int k = 0; k < poly.totloop; k++, ml_other++) { const uint v_other_index = ml_other->v; if (v_other_index != v_index) { @@ -3386,7 +3386,7 @@ static void do_vpaint_brush_smear(bContext *C, else { const int l_index = gmap->vert_to_loop[v_index].indices[j]; elem_index = l_index; - BLI_assert(ss->mloop[l_index].v == v_index); + BLI_assert(ss->loops[l_index].v == v_index); } if (!use_face_sel || select_poly[p_index]) { @@ -3463,7 +3463,7 @@ static void calculate_average_color(VPaintData *vpd, BKE_pbvh_vertex_iter_begin (ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) { /* Test to see if the vertex coordinates are within the spherical brush region. */ if (sculpt_brush_test_sq_fn(&test, vd.co)) { - const int v_index = has_grids ? ss->mloop[vd.grid_indices[vd.g]].v : + const int v_index = has_grids ? ss->loops[vd.grid_indices[vd.g]].v : vd.vert_indices[vd.i]; if (BKE_brush_curve_strength(brush, 0.0, cache->radius) > 0.0) { /* If the vertex is selected for painting. */ @@ -3586,7 +3586,7 @@ static void vpaint_do_draw(bContext *C, /* NOTE: Grids are 1:1 with corners (aka loops). * For grid based pbvh, take the vert whose loop corresponds to the current grid. * Otherwise, take the current vert. */ - const int v_index = has_grids ? ss->mloop[vd.grid_indices[vd.g]].v : + const int v_index = has_grids ? ss->loops[vd.grid_indices[vd.g]].v : vd.vert_indices[vd.i]; const float grid_alpha = has_grids ? 1.0f / vd.gridsize : 1.0f; @@ -3642,7 +3642,7 @@ static void vpaint_do_draw(bContext *C, for (int j = 0; j < gmap->vert_to_poly[v_index].count; j++) { const int p_index = gmap->vert_to_poly[v_index].indices[j]; const int l_index = gmap->vert_to_loop[v_index].indices[j]; - BLI_assert(ss->mloop[l_index].v == v_index); + BLI_assert(ss->loops[l_index].v == v_index); if (!use_face_sel || select_poly[p_index]) { Color color_orig = Color(0, 0, 0, 0); /* unused when array is nullptr */ diff --git a/source/blender/editors/sculpt_paint/sculpt.cc b/source/blender/editors/sculpt_paint/sculpt.cc index c8fbe0e0422..7c96ec0c8fd 100644 --- a/source/blender/editors/sculpt_paint/sculpt.cc +++ b/source/blender/editors/sculpt_paint/sculpt.cc @@ -131,7 +131,7 @@ void SCULPT_face_normal_get(SculptSession *ss, PBVHFaceRef face, float no[3]) case PBVH_FACES: case PBVH_GRIDS: { - const MPoly *mp = ss->mpoly + face.i; + const MPoly *mp = ss->polys + face.i; BKE_mesh_calc_poly_normal(mp, ss->loops + mp->loopstart, ss->vert_positions, no); break; } @@ -995,58 +995,6 @@ void SCULPT_visibility_sync_all_from_faces(Object *ob) } } -static bool sculpt_check_unique_face_set_in_base_mesh(const SculptSession *ss, int index) -{ - if (!ss->face_sets) { - return true; - } - const MeshElemMap *vert_map = &ss->pmap->pmap[index]; - int face_set = -1; - for (int i = 0; i < vert_map->count; i++) { - if (face_set == -1) { - face_set = ss->face_sets[vert_map->indices[i]]; - } - else { - if (ss->face_sets[vert_map->indices[i]] != face_set) { - return false; - } - } - } - return true; -} - -/** - * Checks if the face sets of the adjacent faces to the edge between \a v1 and \a v2 - * in the base mesh are equal. - */ -static bool sculpt_check_unique_face_set_for_edge_in_base_mesh(SculptSession *ss, int v1, int v2) -{ - const MeshElemMap *vert_map = &ss->pmap->pmap[v1]; - int p1 = -1, p2 = -1; - for (int i = 0; i < vert_map->count; i++) { - const MPoly &poly = ss->polys[vert_map->indices[i]]; - for (int l = 0; l < poly.totloop; l++) { - const MLoop *loop = &ss->loops[poly.loopstart + l]; - if (loop->v == v2) { - if (p1 == -1) { - p1 = vert_map->indices[i]; - break; - } - - if (p2 == -1) { - p2 = vert_map->indices[i]; - break; - } - } - } - } - - if (p1 != -1 && p2 != -1) { - return abs(ss->face_sets[p1]) == (ss->face_sets[p2]); - } - return true; -} - bool SCULPT_vertex_has_unique_face_set(const SculptSession *ss, PBVHVertRef vertex) { return !SCULPT_vertex_is_boundary(ss, vertex, SCULPT_BOUNDARY_FACE_SET); @@ -4665,11 +4613,6 @@ static void sculpt_combine_proxies_node(Object &object, { SculptSession *ss = object.sculpt; - float(*orco)[3] = nullptr; - if (use_orco && !ss->bm) { - orco = SCULPT_undo_push_node(&object, &node, SCULPT_UNDO_COORDS)->co; - } - int proxy_count; PBVHProxyNode *proxies; BKE_pbvh_node_get_proxies(&node, &proxies, &proxy_count); @@ -6216,7 +6159,6 @@ static void sculpt_brush_stroke_init(bContext *C, wmOperator *op) static void sculpt_restore_mesh(Sculpt *sd, Object *ob) { - SculptSession *ss = ob->sculpt; Brush *brush = BKE_paint_brush(&sd->paint); /* For the cloth brush it makes more sense to not restore the mesh state to keep running the diff --git a/source/blender/editors/sculpt_paint/sculpt_api.cc b/source/blender/editors/sculpt_paint/sculpt_api.cc index 6751e2ca241..af5885ba609 100644 --- a/source/blender/editors/sculpt_paint/sculpt_api.cc +++ b/source/blender/editors/sculpt_paint/sculpt_api.cc @@ -131,9 +131,9 @@ static bool sculpt_check_unique_face_set_for_edge_in_base_mesh(const SculptSessi const MeshElemMap *vert_map = &ss->pmap->pmap[v1]; int p1 = -1, p2 = -1; for (int i = 0; i < vert_map->count; i++) { - const MPoly *p = &ss->mpoly[vert_map->indices[i]]; + const MPoly *p = &ss->polys[vert_map->indices[i]]; for (int l = 0; l < p->totloop; l++) { - const MLoop *loop = &ss->mloop[p->loopstart + l]; + const MLoop *loop = &ss->loops[p->loopstart + l]; if (loop->v == v2) { if (p1 == -1) { p1 = vert_map->indices[i]; @@ -252,7 +252,7 @@ eSculptBoundary SCULPT_edge_is_boundary(const SculptSession *ss, } if (typemask & SCULPT_BOUNDARY_SEAM) { - ret |= ss->medge[edge.i].flag & ME_SEAM ? SCULPT_BOUNDARY_SEAM : 0; + ret |= (ss->seam_edge && ss->seam_edge[edge.i]) ? SCULPT_BOUNDARY_SEAM : 0; } break; @@ -280,8 +280,8 @@ void SCULPT_edge_get_verts(const SculptSession *ss, } case PBVH_FACES: { - r_v1->i = (intptr_t)ss->medge[edge.i].v1; - r_v2->i = (intptr_t)ss->medge[edge.i].v2; + r_v1->i = (intptr_t)ss->edges[edge.i].v1; + r_v2->i = (intptr_t)ss->edges[edge.i].v2; break; } case PBVH_GRIDS: @@ -320,7 +320,7 @@ static void grids_update_boundary_flags(const SculptSession *ss, PBVHVertRef ver int v1, v2; const SubdivCCGAdjacencyType adjacency = BKE_subdiv_ccg_coarse_mesh_adjacency_info_get( - ss->subdiv_ccg, &coord, ss->mloop, ss->mpoly, &v1, &v2); + ss->subdiv_ccg, &coord, ss->loops, ss->polys, &v1, &v2); switch (adjacency) { case SUBDIV_CCG_ADJACENT_VERTEX: @@ -352,13 +352,14 @@ static void faces_update_boundary_flags(const SculptSession *ss, const PBVHVertR ss->face_sets, ss->hide_poly, ss->vert_positions, - ss->medge, - ss->mloop, - ss->mpoly, + ss->edges, + ss->loops, + ss->polys, ss->msculptverts, ss->pmap->pmap, vertex, - ss->sharp_edge); + ss->sharp_edge, + ss->seam_edge); /* We have to handle boundary here seperately. */ @@ -372,7 +373,7 @@ static void faces_update_boundary_flags(const SculptSession *ss, const PBVHVertR bool ok = true; for (int i = 0; i < ss->pmap->pmap[vertex.i].count; i++) { - const MPoly *mp = ss->mpoly + ss->pmap->pmap[vertex.i].indices[i]; + const MPoly *mp = ss->polys + ss->pmap->pmap[vertex.i].indices[i]; if (mp->totloop < 4) { ok = false; } @@ -471,7 +472,7 @@ eSculptBoundary SCULPT_vertex_is_boundary(const SculptSession *ss, coord.y = vertex_index / key->grid_size; int v1, v2; const SubdivCCGAdjacencyType adjacency = BKE_subdiv_ccg_coarse_mesh_adjacency_info_get( - ss->subdiv_ccg, &coord, ss->mloop, ss->mpoly, &v1, &v2); + ss->subdiv_ccg, &coord, ss->loops, ss->polys, &v1, &v2); switch (adjacency) { case SUBDIV_CCG_ADJACENT_VERTEX: @@ -605,12 +606,11 @@ void SCULPT_stroke_id_ensure(Object *ob) if (!ss->attrs.stroke_id) { SculptAttributeParams params = {0}; - ss->attrs.stroke_id = BKE_sculpt_attribute_ensure( - ob, - ATTR_DOMAIN_POINT, - CD_PROP_INT32, - SCULPT_ATTRIBUTE_NAME(automasking_stroke_id), - ¶ms); + ss->attrs.stroke_id = BKE_sculpt_attribute_ensure(ob, + ATTR_DOMAIN_POINT, + CD_PROP_INT32, + SCULPT_ATTRIBUTE_NAME(automasking_stroke_id), + ¶ms); } } diff --git a/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc b/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc index 2d9c51eb52d..9226f1aa6b4 100644 --- a/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc +++ b/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc @@ -217,9 +217,9 @@ void SCULPT_faces_get_cotangents(SculptSession *ss, int i3 = (i + 1) % elem->count; const float *v = ss->vert_positions[vertex.i]; - const MEdge *e1 = ss->medge + elem->indices[i1]; - const MEdge *e2 = ss->medge + elem->indices[i2]; - const MEdge *e3 = ss->medge + elem->indices[i3]; + const MEdge *e1 = ss->edges + elem->indices[i1]; + const MEdge *e2 = ss->edges + elem->indices[i2]; + const MEdge *e3 = ss->edges + elem->indices[i3]; const float *v1 = (unsigned int)vertex.i == e1->v1 ? ss->vert_positions[e1->v2] : ss->vert_positions[e1->v1]; @@ -279,13 +279,8 @@ void SCULPT_cotangents_begin(Object *ob, SculptSession *ss) Mesh *mesh = BKE_object_get_original_mesh(ob); if (!ss->vemap) { - BKE_mesh_vert_edge_map_create(&ss->vemap, - &ss->vemap_mem, - ss->vert_positions, - BKE_mesh_edges(mesh), - mesh->totvert, - mesh->totedge, - true); + BKE_mesh_vert_edge_map_create( + &ss->vemap, &ss->vemap_mem, mesh->edges().data(), mesh->totvert, mesh->totedge); } break; @@ -458,8 +453,8 @@ void SCULPT_dynamic_topology_triangulate(SculptSession *ss, BMesh *bm) ss->totvert = ss->bm->totvert; // BM_mesh_triangulate( - // bm, MOD_TRIANGULATE_QUAD_BEAUTY, MOD_TRIANGULATE_NGON_EARCLIP, 4, false, nullptr, nullptr, - // nullptr); + // bm, MOD_TRIANGULATE_QUAD_BEAUTY, MOD_TRIANGULATE_NGON_EARCLIP, 4, false, nullptr, + // nullptr, nullptr); } void SCULPT_pbvh_clear(Object *ob, bool cache_pbvh) @@ -1753,8 +1748,16 @@ void SCULPT_uv_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) } PBVHVertRef vertex = {(intptr_t)sv->v}; - sv->brushfade = SCULPT_brush_strength_factor( - ss, brush, sv->v->co, sqrtf(test.dist), nullptr, sv->v->no, 0.0f, vertex, 0, &automask_data); + sv->brushfade = SCULPT_brush_strength_factor(ss, + brush, + sv->v->co, + sqrtf(test.dist), + nullptr, + sv->v->no, + 0.0f, + vertex, + 0, + &automask_data); } for (int i = 0; i < 5; i++) { diff --git a/source/blender/editors/sculpt_paint/sculpt_expand.cc b/source/blender/editors/sculpt_paint/sculpt_expand.cc index 2453dcef7d6..1dead0eb201 100644 --- a/source/blender/editors/sculpt_paint/sculpt_expand.cc +++ b/source/blender/editors/sculpt_paint/sculpt_expand.cc @@ -157,7 +157,7 @@ static bool sculpt_expand_is_face_in_active_component(SculptSession *ss, return sculpt_expand_is_vert_in_active_component(ss, expand_cache, v); } else { - const MLoop *loop = &ss->loops[ss->polys[f].loopstart]; + const MLoop *loop = &ss->loops[ss->polys[f.i].loopstart]; return sculpt_expand_is_vert_in_active_component( ss, expand_cache, BKE_pbvh_index_to_vertex(ss->pbvh, loop->v)); } @@ -1197,7 +1197,7 @@ static void sculpt_expand_snap_initialize_from_enabled(SculptSession *ss, const MPoly &poly = ss->polys[p]; bool any_disabled = false; for (int l = 0; l < poly.totloop; l++) { - const MLoop *loop = &ss->mloop[l + poly.loopstart]; + const MLoop *loop = &ss->loops[l + poly.loopstart]; if (!BLI_BITMAP_TEST(enabled_verts, loop->v)) { any_disabled = true; break; diff --git a/source/blender/editors/sculpt_paint/sculpt_geodesic.cc b/source/blender/editors/sculpt_paint/sculpt_geodesic.cc index b9069138ad0..78d80a77de7 100644 --- a/source/blender/editors/sculpt_paint/sculpt_geodesic.cc +++ b/source/blender/editors/sculpt_paint/sculpt_geodesic.cc @@ -309,7 +309,7 @@ static float *SCULPT_geodesic_mesh_create(Object *ob, if (!ss->vemap) { BKE_mesh_vert_edge_map_create( - &ss->vemap, &ss->vemap_mem, cos, mesh->medge, mesh->totvert, mesh->totedge, true); + &ss->vemap, &ss->vemap_mem, mesh->medge, mesh->totvert, mesh->totedge); } /* Both contain edge indices encoded as *void. */ @@ -532,21 +532,17 @@ static float *SCULPT_geodesic_bmesh_create(Object *ob, } BMEdge *e; - int i = 0; BM_ITER_MESH (e, &iter, ss->bm, BM_EDGES_OF_MESH) { const int v1_i = BM_elem_index_get(e->v1); const int v2_i = BM_elem_index_get(e->v2); if (!BLI_BITMAP_TEST(affected_vertex, v1_i) && !BLI_BITMAP_TEST(affected_vertex, v2_i)) { - i++; continue; } if (dists[v1_i] != FLT_MAX || dists[v2_i] != FLT_MAX) { BLI_LINKSTACK_PUSH(queue, e); } - - i++; } do { diff --git a/source/blender/modifiers/intern/MOD_multires.cc b/source/blender/modifiers/intern/MOD_multires.cc index 91a5c5bd102..e7c4f5f9cd0 100644 --- a/source/blender/modifiers/intern/MOD_multires.cc +++ b/source/blender/modifiers/intern/MOD_multires.cc @@ -246,7 +246,7 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * sculpt_session->totpoly = mesh->totpoly; sculpt_session->vert_positions = nullptr; sculpt_session->polys = nullptr; - sculpt_session->mloop = nullptr; + sculpt_session->loops = nullptr; } // BKE_subdiv_stats_print(&subdiv->stats); } -- 2.30.2 From 619c2e1eb9fd2b6e33d215c158199fb8193cbef8 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Wed, 8 Mar 2023 04:16:22 -0800 Subject: [PATCH 027/279] temp-sculpt-dyntopo: Various fixes * Disable idmap debug mode. * Fixed "show original coordinates" draw mode. * Fixed bug with idmap debug mode corrupting meshes. --- scripts/startup/bl_ui/space_view3d_toolbar.py | 1 + source/blender/blenkernel/BKE_pbvh.h | 3 ++ source/blender/blenkernel/intern/dyntopo.cc | 20 ++----------- source/blender/blenkernel/intern/pbvh.cc | 15 +++++++--- .../blender/blenkernel/intern/pbvh_bmesh.cc | 28 ++++++++++--------- .../blender/blenkernel/intern/pbvh_intern.hh | 1 + source/blender/blenlib/intern/BLI_mempool.c | 7 +++++ source/blender/bmesh/intern/bmesh_idmap.h | 2 +- source/blender/draw/intern/draw_pbvh.cc | 26 +++++++++++++---- source/blender/editors/sculpt_paint/sculpt.cc | 22 +++++++++------ 10 files changed, 75 insertions(+), 50 deletions(-) diff --git a/scripts/startup/bl_ui/space_view3d_toolbar.py b/scripts/startup/bl_ui/space_view3d_toolbar.py index c57f0edf5b1..0f97ccea6b5 100644 --- a/scripts/startup/bl_ui/space_view3d_toolbar.py +++ b/scripts/startup/bl_ui/space_view3d_toolbar.py @@ -951,6 +951,7 @@ class VIEW3D_PT_sculpt_options(Panel, View3DPaintPanel): col.prop(sculpt, "show_low_resolution") col.prop(sculpt, "use_sculpt_delay_updates") col.prop(sculpt, "use_deform_only") + col.prop(tool_settings, "show_origco") class VIEW3D_PT_sculpt_options_gravity(Panel, View3DPaintPanel): diff --git a/source/blender/blenkernel/BKE_pbvh.h b/source/blender/blenkernel/BKE_pbvh.h index 50a3b9c590b..1cfa921ac85 100644 --- a/source/blender/blenkernel/BKE_pbvh.h +++ b/source/blender/blenkernel/BKE_pbvh.h @@ -1424,6 +1424,9 @@ void BKE_pbvh_pmap_aquire(SculptPMap *pmap); bool BKE_pbvh_pmap_release(SculptPMap *pmap); void BKE_pbvh_clear_cache(PBVH *preserve); +void BKE_pbvh_show_orig_set(PBVH *pbvh, bool show_orig); +bool BKE_pbvh_show_orig_get(PBVH *pbvh); + #ifdef __cplusplus } #endif diff --git a/source/blender/blenkernel/intern/dyntopo.cc b/source/blender/blenkernel/intern/dyntopo.cc index 79b9fc05413..5e781d81b88 100644 --- a/source/blender/blenkernel/intern/dyntopo.cc +++ b/source/blender/blenkernel/intern/dyntopo.cc @@ -214,9 +214,7 @@ static void pbvh_kill_vert(PBVH *pbvh, BMVert *v, bool log_vert, bool log_edges) BM_log_vert_removed(pbvh->header.bm, pbvh->bm_log, v); } -#ifdef USE_NEW_IDMAP BM_idmap_release(pbvh->bm_idmap, (BMElem *)v, true); -#endif BM_vert_kill(pbvh->header.bm, v); bm_logstack_pop(); } @@ -1255,7 +1253,7 @@ static void unified_edge_queue_task_cb(void *__restrict userdata, BLI_rng_free(rng); } -bool check_face_is_tri(PBVH *pbvh, BMFace *f) +ATTR_NO_OPT bool check_face_is_tri(PBVH *pbvh, BMFace *f) { #if DYNTOPO_DISABLE_FLAG & DYNTOPO_DISABLE_TRIANGULATOR return true; @@ -1290,9 +1288,7 @@ bool check_face_is_tri(PBVH *pbvh, BMFace *f) // BKE_pbvh_bmesh_remove_face(pbvh, f, true); pbvh_bmesh_face_remove(pbvh, f, false, true, true); BM_log_face_removed(pbvh->header.bm, pbvh->bm_log, f); -#ifdef USE_NEW_IDMAP BM_idmap_release(pbvh->bm_idmap, (BMElem *)f, true); -#endif int len = (f->len - 2) * 3; @@ -1304,10 +1300,8 @@ bool check_face_is_tri(PBVH *pbvh, BMFace *f) MemArena *arena = nullptr; struct Heap *heap = nullptr; - if (f->len > 4) { - arena = BLI_memarena_new(512, "ngon arena"); - heap = BLI_heap_new(); - } + arena = BLI_memarena_new(512, "ngon arena"); + heap = BLI_heap_new(); BM_face_triangulate(pbvh->header.bm, f, @@ -1338,9 +1332,7 @@ bool check_face_is_tri(PBVH *pbvh, BMFace *f) f = nullptr; } -#ifdef USE_NEW_IDMAP BM_idmap_release(pbvh->bm_idmap, (BMElem *)dbl->link, true); -#endif BM_face_kill(pbvh->header.bm, (BMFace *)dbl->link); MEM_freeN(dbl); @@ -1535,9 +1527,7 @@ bool destroy_nonmanifold_fins(PBVH *pbvh, BMEdge *e_root) } pbvh_bmesh_face_remove(pbvh, f, true, false, false); -# ifdef USE_NEW_IDMAP BM_idmap_release(pbvh->bm_idmap, (BMElem *)f, true); -# endif BM_face_kill(pbvh->header.bm, f); } @@ -1548,9 +1538,7 @@ bool destroy_nonmanifold_fins(PBVH *pbvh, BMEdge *e_root) if (!e->l) { BM_log_edge_removed(pbvh->header.bm, pbvh->bm_log, e); -# ifdef USE_NEW_IDMAP BM_idmap_release(pbvh->bm_idmap, (BMElem *)e, true); -# endif BM_edge_kill(pbvh->header.bm, e); } } @@ -1562,9 +1550,7 @@ bool destroy_nonmanifold_fins(PBVH *pbvh, BMEdge *e_root) pbvh_bmesh_vert_remove(pbvh, v); BM_log_vert_removed(pbvh->header.bm, pbvh->bm_log, v); -# ifdef USE_NEW_IDMAP BM_idmap_release(pbvh->bm_idmap, (BMElem *)v, true); -# endif BM_vert_kill(pbvh->header.bm, v); } else { diff --git a/source/blender/blenkernel/intern/pbvh.cc b/source/blender/blenkernel/intern/pbvh.cc index 7f38a856f32..d6f967f2f9d 100644 --- a/source/blender/blenkernel/intern/pbvh.cc +++ b/source/blender/blenkernel/intern/pbvh.cc @@ -769,8 +769,15 @@ void BKE_pbvh_set_face_areas(PBVH *pbvh, float *face_areas) pbvh->face_areas = face_areas; } -/* XXX investigate this global. */ -extern "C" bool pbvh_show_orig_co = false; +void BKE_pbvh_show_orig_set(PBVH *pbvh, bool show_orig) +{ + pbvh->show_orig = show_orig; +} + +bool BKE_pbvh_show_orig_get(PBVH *pbvh) +{ + return pbvh->show_orig; +} static void pbvh_draw_args_init(PBVH *pbvh, PBVH_GPU_Args *args, PBVHNode *node) { @@ -791,7 +798,6 @@ static void pbvh_draw_args_init(PBVH *pbvh, PBVH_GPU_Args *args, PBVHNode *node) args->polys = pbvh->polys; args->mlooptri = pbvh->looptri; args->flat_vcol_shading = pbvh->flat_vcol_shading; - args->show_orig = pbvh_show_orig_co; args->updategen = node->updategen; args->msculptverts = pbvh->msculptverts; @@ -861,6 +867,7 @@ static void pbvh_draw_args_init(PBVH *pbvh, PBVH_GPU_Args *args, PBVHNode *node) args->tri_buffers = node->tri_buffers; args->tot_tri_buffers = node->tot_tri_buffers; + args->show_orig = pbvh->show_orig; break; } } @@ -3504,7 +3511,7 @@ void BKE_pbvh_draw_debug_cb(PBVH *pbvh, for (int a = 0; a < pbvh->totnode; a++) { PBVHNode *node = &pbvh->nodes[a]; - if (pbvh_show_orig_co) { + if (pbvh->show_orig) { draw_fn(node, user_data, node->orig_vb.bmin, node->orig_vb.bmax, node->flag); } else { diff --git a/source/blender/blenkernel/intern/pbvh_bmesh.cc b/source/blender/blenkernel/intern/pbvh_bmesh.cc index 3df3590d494..42f1eb87e71 100644 --- a/source/blender/blenkernel/intern/pbvh_bmesh.cc +++ b/source/blender/blenkernel/intern/pbvh_bmesh.cc @@ -2149,19 +2149,19 @@ void BKE_pbvh_set_idmap(PBVH *pbvh, BMIdMap *idmap) } /* Build a PBVH from a BMesh */ -void BKE_pbvh_build_bmesh(PBVH *pbvh, - Mesh *me, - BMesh *bm, - bool smooth_shading, - BMLog *log, - BMIdMap *idmap, - const int cd_vert_node_offset, - const int cd_face_node_offset, - const int cd_sculpt_vert, - const int cd_face_areas, - const int cd_boundary_flag, - bool fast_draw, - bool update_sculptverts) +ATTR_NO_OPT void BKE_pbvh_build_bmesh(PBVH *pbvh, + Mesh *me, + BMesh *bm, + bool smooth_shading, + BMLog *log, + BMIdMap *idmap, + const int cd_vert_node_offset, + const int cd_face_node_offset, + const int cd_sculpt_vert, + const int cd_face_areas, + const int cd_boundary_flag, + bool fast_draw, + bool update_sculptverts) { // coalese_pbvh(pbvh, bm); @@ -2245,6 +2245,8 @@ void BKE_pbvh_build_bmesh(PBVH *pbvh, MSculptVert *mv = BKE_PBVH_SCULPTVERT(pbvh->cd_sculpt_vert, v); mv->flag |= SCULPTVERT_NEED_VALENCE | SCULPTVERT_NEED_TRIANGULATE | SCULPTVERT_NEED_DISK_SORT; + copy_v3_v3(mv->origco, v->co); + copy_v3_v3(mv->origno, v->no); int *flags = BM_ELEM_CD_PTR(v, pbvh->cd_boundary_flag); *flags |= SCULPT_BOUNDARY_NEEDS_UPDATE; diff --git a/source/blender/blenkernel/intern/pbvh_intern.hh b/source/blender/blenkernel/intern/pbvh_intern.hh index bfcaca30b44..69426dd6108 100644 --- a/source/blender/blenkernel/intern/pbvh_intern.hh +++ b/source/blender/blenkernel/intern/pbvh_intern.hh @@ -284,6 +284,7 @@ struct PBVH { PBVHGPUFormat *vbo_id; PBVHPixels pixels; + bool show_orig; }; /* pbvh.cc */ diff --git a/source/blender/blenlib/intern/BLI_mempool.c b/source/blender/blenlib/intern/BLI_mempool.c index 6e703ec5490..37f8e5f434f 100644 --- a/source/blender/blenlib/intern/BLI_mempool.c +++ b/source/blender/blenlib/intern/BLI_mempool.c @@ -761,6 +761,13 @@ ATTR_NO_OPT void BLI_mempool_free(BLI_mempool *pool, void *addr) mempool_unpoison(pool); if (pool->flag & BLI_MEMPOOL_IGNORE_FREE) { + BLI_freenode *newhead = addr; + + if (pool->flag & BLI_MEMPOOL_ALLOW_ITER) { + newhead->freeword = FREEWORD; + } + + BLI_asan_poison(newhead, pool->esize); mempool_poison(pool); return; } diff --git a/source/blender/bmesh/intern/bmesh_idmap.h b/source/blender/bmesh/intern/bmesh_idmap.h index 9af36a34a57..be6f9148338 100644 --- a/source/blender/bmesh/intern/bmesh_idmap.h +++ b/source/blender/bmesh/intern/bmesh_idmap.h @@ -8,7 +8,7 @@ # define WITH_BM_ID_FREELIST #endif -#define DEBUG_BM_IDMAP /* Debug idmap; note: disables mempool deallocation */ +//#define DEBUG_BM_IDMAP /* Debug idmap; note: disables mempool deallocation */ #define USE_NEW_IDMAP diff --git a/source/blender/draw/intern/draw_pbvh.cc b/source/blender/draw/intern/draw_pbvh.cc index d7fdc2f619c..d7d25479209 100644 --- a/source/blender/draw/intern/draw_pbvh.cc +++ b/source/blender/draw/intern/draw_pbvh.cc @@ -1012,13 +1012,27 @@ struct PBVHBatches { break; case CD_PBVH_NO_TYPE: - foreach_bmesh([&](BMLoop *l) { - short no[3]; - bool smooth = BM_elem_flag_test(l->f, BM_ELEM_SMOOTH); + if (args->show_orig) { + int cd_sculpt_vert = CustomData_get_offset(&args->bm->vdata, CD_DYNTOPO_VERT); - normal_float_to_short_v3(no, smooth ? l->v->no : l->f->no); - *static_cast(GPU_vertbuf_raw_step(&access)) = no; - }); + foreach_bmesh([&](BMLoop *l) { + MSculptVert *mv = static_cast( + BM_ELEM_CD_GET_VOID_P(l->v, cd_sculpt_vert)); + short no[3]; + + normal_float_to_short_v3(no, mv->origno); + *static_cast(GPU_vertbuf_raw_step(&access)) = no; + }); + } + else { + foreach_bmesh([&](BMLoop *l) { + short no[3]; + bool smooth = BM_elem_flag_test(l->f, BM_ELEM_SMOOTH); + + normal_float_to_short_v3(no, smooth ? l->v->no : l->f->no); + *static_cast(GPU_vertbuf_raw_step(&access)) = no; + }); + } break; case CD_PBVH_MASK_TYPE: { diff --git a/source/blender/editors/sculpt_paint/sculpt.cc b/source/blender/editors/sculpt_paint/sculpt.cc index 7c96ec0c8fd..51409fe1f84 100644 --- a/source/blender/editors/sculpt_paint/sculpt.cc +++ b/source/blender/editors/sculpt_paint/sculpt.cc @@ -372,7 +372,7 @@ void SCULPT_vertex_persistent_normal_get(SculptSession *ss, PBVHVertRef vertex, SCULPT_vertex_normal_get(ss, vertex, no); } -float SCULPT_vertex_mask_get(SculptSession *ss, PBVHVertRef vertex) +ATTR_NO_OPT float SCULPT_vertex_mask_get(SculptSession *ss, PBVHVertRef vertex) { switch (BKE_pbvh_type(ss->pbvh)) { case PBVH_FACES: @@ -2863,11 +2863,11 @@ void SCULPT_calc_area_normal_and_center( * values pull vertices, negative values push. Uses tablet pressure and a * special multiplier found experimentally to scale the strength factor. */ -static float brush_strength(const Sculpt *sd, - const StrokeCache *cache, - const float feather, - const UnifiedPaintSettings *ups, - const PaintModeSettings * /*paint_mode_settings*/) +ATTR_NO_OPT static float brush_strength(const Sculpt *sd, + const StrokeCache *cache, + const float feather, + const UnifiedPaintSettings *ups, + const PaintModeSettings * /*paint_mode_settings*/) { const Scene *scene = cache->vc->scene; const Brush *brush = BKE_paint_brush((Paint *)&sd->paint); @@ -5355,6 +5355,10 @@ static void sculpt_update_cache_invariants( } #undef PIXEL_INPUT_THRESHHOLD + + if (ss->pbvh) { + BKE_pbvh_show_orig_set(ss->pbvh, tool_settings->show_origco); + } } static float sculpt_brush_dynamic_size_get(Brush *brush, StrokeCache *cache, float initial_size) @@ -6443,14 +6447,14 @@ static bool sculpt_stroke_test_start(bContext *C, wmOperator *op, const float mv sculpt_update_cache_invariants(C, sd, ss, op, mval); + SCULPT_stroke_id_next(ob); + ss->cache->stroke_id = ss->stroke_id; + SculptCursorGeometryInfo sgi; SCULPT_cursor_geometry_info_update(C, &sgi, mval, false, false); sculpt_stroke_undo_begin(C, op); - SCULPT_stroke_id_next(ob); - ss->cache->stroke_id = ss->stroke_id; - return true; } return false; -- 2.30.2 From 656c0758438cc285a44577229ae67e4633408a52 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Thu, 9 Mar 2023 14:57:39 -0800 Subject: [PATCH 028/279] temp-sculpt-dyntopo: Fix symmetrize undo and a few ASAN errors The new BMLog code was failing to call CustomData_bmesh_asan_unpoison prior to directly memcpy'ing a customdata block. --- build_files/cmake/macros.cmake | 2 +- source/blender/bmesh/intern/bmesh_log.cc | 20 +++++++++++++++++++ .../editors/sculpt_paint/sculpt_ops.cc | 9 +++------ 3 files changed, 24 insertions(+), 7 deletions(-) diff --git a/build_files/cmake/macros.cmake b/build_files/cmake/macros.cmake index 958c8205bfd..001963d80e0 100644 --- a/build_files/cmake/macros.cmake +++ b/build_files/cmake/macros.cmake @@ -1090,7 +1090,7 @@ function(msgfmt_simple add_custom_command( OUTPUT ${_file_to} COMMAND ${CMAKE_COMMAND} -E make_directory ${_file_to_path} - COMMAND ${CMAKE_COMMAND} -E env ${PLATFORM_ENV_BUILD} "$" ${_file_from} ${_file_to} + COMMAND ${CMAKE_COMMAND} -E env "$" ${_file_from} ${_file_to} DEPENDS msgfmt ${_file_from}) set_source_files_properties(${_file_to} PROPERTIES GENERATED TRUE) diff --git a/source/blender/bmesh/intern/bmesh_log.cc b/source/blender/bmesh/intern/bmesh_log.cc index 52291a9596e..05a52fc2a62 100644 --- a/source/blender/bmesh/intern/bmesh_log.cc +++ b/source/blender/bmesh/intern/bmesh_log.cc @@ -309,11 +309,13 @@ struct BMLogSetFull : public BMLogSetBase { void undo(BMesh *bm, BMLogCallbacks *callbacks) override { swap(bm); + callbacks->on_full_mesh_load(callbacks->userdata); } void redo(BMesh *bm, BMLogCallbacks *callbacks) override { swap(bm); + callbacks->on_full_mesh_load(callbacks->userdata); } Mesh *mesh = nullptr; @@ -1094,6 +1096,10 @@ ATTR_NO_OPT void BMLogSetDiff::swap_verts(BMesh *bm, const int cd_id = entry->idmap->cd_id_off[BM_VERT]; + if (old_customdata) { + CustomData_bmesh_asan_unpoison(&bm->vdata, old_customdata); + } + for (BMLogVert *lv : verts.values()) { BMVert *v = entry->get_elem_from_id(bm, lv->id); @@ -1103,7 +1109,9 @@ ATTR_NO_OPT void BMLogSetDiff::swap_verts(BMesh *bm, } if (old_customdata) { + CustomData_bmesh_asan_unpoison(&bm->vdata, v->head.data); memcpy(old_customdata, v->head.data, bm->vdata.totsize); + CustomData_bmesh_asan_poison(&bm->vdata, v->head.data); } entry->swap_logvert(bm, lv->id, v, lv); @@ -1223,6 +1231,10 @@ void BMLogSetDiff::swap_edges(BMesh *bm, { void *old_customdata = entry->edata.pool ? BLI_mempool_alloc(bm->edata.pool) : nullptr; + if (old_customdata) { + CustomData_bmesh_asan_unpoison(&bm->edata, old_customdata); + } + for (BMLogEdge *le : edges.values()) { BMEdge *e = entry->get_elem_from_id(bm, le->id); @@ -1232,7 +1244,9 @@ void BMLogSetDiff::swap_edges(BMesh *bm, } if (old_customdata) { + CustomData_bmesh_asan_unpoison(&bm->edata, e->head.data); memcpy(old_customdata, e->head.data, bm->edata.totsize); + CustomData_bmesh_asan_poison(&bm->edata, e->head.data); } entry->swap_logedge(bm, le->id, e, le); @@ -1331,6 +1345,10 @@ ATTR_NO_OPT void BMLogSetDiff::swap_faces(BMesh *bm, const int cd_id = entry->idmap->cd_id_off[BM_FACE]; + if (old_customdata) { + CustomData_bmesh_asan_unpoison(&bm->pdata, old_customdata); + } + for (BMLogFace *lf : faces.values()) { BMFace *f = entry->get_elem_from_id(bm, lf->id); @@ -1340,7 +1358,9 @@ ATTR_NO_OPT void BMLogSetDiff::swap_faces(BMesh *bm, } if (old_customdata) { + CustomData_bmesh_asan_unpoison(&bm->pdata, f->head.data); memcpy(old_customdata, f->head.data, bm->pdata.totsize); + CustomData_bmesh_asan_poison(&bm->pdata, f->head.data); } entry->swap_logface(bm, lf->id, f, lf); diff --git a/source/blender/editors/sculpt_paint/sculpt_ops.cc b/source/blender/editors/sculpt_paint/sculpt_ops.cc index 73d69454610..45f59f30d3b 100644 --- a/source/blender/editors/sculpt_paint/sculpt_ops.cc +++ b/source/blender/editors/sculpt_paint/sculpt_ops.cc @@ -291,12 +291,13 @@ static int sculpt_symmetrize_exec(bContext *C, wmOperator *op) BKE_pbvh_recalc_bmesh_boundary(ss->pbvh); SCULT_dyntopo_flag_all_disk_sort(ss); - // symmetrize is messing up ids, regenerate them from scratch + /* De-duplicate element IDs. */ BM_idmap_check_ids(ss->bm_idmap); + BM_mesh_toolflags_set(ss->bm, false); - BM_log_full_mesh(ss->bm, ss->bm_log); /* Finish undo. */ + BM_log_full_mesh(ss->bm, ss->bm_log); SCULPT_undo_push_end(ob); break; @@ -857,8 +858,6 @@ static int sculpt_sample_color_modal(bContext *C, wmOperator *op, const wmEvent SampleColorCustomData *sccd = (SampleColorCustomData *)op->customdata; - ePaintMode mode = BKE_paintmode_get_active_from_context(C); - /* Finish operation on release. */ if (event->val == KM_RELEASE) { float color_srgb[3]; @@ -1433,7 +1432,6 @@ static int sculpt_regularize_rake_exec(bContext *C, wmOperator *op) BMVert *v = verts[i]; PBVHVertRef vertex = {(intptr_t)v}; - MSculptVert *mv = BKE_PBVH_SCULPTVERT(ss->cd_sculpt_vert, v); int *boundflag = SCULPT_vertex_attr_get(vertex, ss->attrs.boundary_flags); if (*boundflag & (SCULPT_CORNER_MESH | SCULPT_CORNER_FACE_SET | SCULPT_CORNER_SHARP | @@ -1700,7 +1698,6 @@ static void sculpt_bake_cavity_exec_task_cb(void *__restrict userdata, case AUTOMASK_BAKE_DIVIDE: mask = automask > 0.00001f ? *vd.mask / automask : 0.0f; break; - break; case AUTOMASK_BAKE_ADD: mask = *vd.mask + automask; break; -- 2.30.2 From e35f676b828551979469d81dab5e3110a1864120 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Thu, 9 Mar 2023 19:31:24 -0800 Subject: [PATCH 029/279] temp-sculpt-dyntopo: Make enhance details use sculpt attribute api --- source/blender/blenkernel/BKE_paint.h | 11 +++-- source/blender/blenkernel/intern/paint.cc | 44 +++++++++++++++++++ source/blender/editors/sculpt_paint/sculpt.cc | 1 - .../editors/sculpt_paint/sculpt_intern.hh | 3 -- .../editors/sculpt_paint/sculpt_ops.cc | 2 - .../editors/sculpt_paint/sculpt_smooth.cc | 17 +++++-- 6 files changed, 64 insertions(+), 14 deletions(-) diff --git a/source/blender/blenkernel/BKE_paint.h b/source/blender/blenkernel/BKE_paint.h index 93c8afd0c5d..6ac14207145 100644 --- a/source/blender/blenkernel/BKE_paint.h +++ b/source/blender/blenkernel/BKE_paint.h @@ -694,10 +694,10 @@ typedef struct SculptAttributePointers { /* Precomputed auto-mask factor indexed by vertex, owned by the auto-masking system and * initialized in #SCULPT_automasking_cache_init when needed. */ - SculptAttribute *automasking_factor; - SculptAttribute *automasking_occlusion; /* CD_PROP_INT8. */ + SculptAttribute *automasking_factor; /* Stroke only. */ + SculptAttribute *automasking_occlusion; /* CD_PROP_INT8, stroke only */ SculptAttribute *automasking_stroke_id; - SculptAttribute *automasking_cavity; + SculptAttribute *automasking_cavity; /* Stroke only. */ SculptAttribute *topology_island_key; /* CD_PROP_INT8 */ @@ -728,8 +728,11 @@ typedef struct SculptAttributePointers { SculptAttribute *fairing_fade; SculptAttribute *fairing_mask; - /* Stores the displacement produced by the laplacian step of HC smooth. */ + /* Stores the displacement produced by the laplacian step of HC smooth */ SculptAttribute *laplacian_disp; + + /* Enhance Details */ + SculptAttribute *detail_directions; /* Stroke only. */ } SculptAttributePointers; typedef struct SculptSession { diff --git a/source/blender/blenkernel/intern/paint.cc b/source/blender/blenkernel/intern/paint.cc index 9947b0fa7b6..cd59868d900 100644 --- a/source/blender/blenkernel/intern/paint.cc +++ b/source/blender/blenkernel/intern/paint.cc @@ -3689,6 +3689,34 @@ static void sculptsession_bmesh_add_layers(Object *ob) ss->cd_hide_poly = ss->attrs.hide_poly ? ss->attrs.hide_poly->bmesh_cd_offset : -1; } +template static void sculpt_clear_attribute_bmesh(BMesh *bm, SculptAttribute *attr) +{ + BMIter iter; + int itertype; + + switch (attr->domain) { + case ATTR_DOMAIN_POINT: + itertype = BM_VERTS_OF_MESH; + break; + case ATTR_DOMAIN_EDGE: + itertype = BM_EDGES_OF_MESH; + break; + case ATTR_DOMAIN_FACE: + itertype = BM_FACES_OF_MESH; + break; + default: + BLI_assert_unreachable(); + return; + } + + int size = CustomData_getTypeSize(attr->proptype); + + T *elem; + BM_ITER_MESH (elem, &iter, bm, itertype) { + memset(POINTER_OFFSET(elem->head.data, attr->bmesh_cd_offset), 0, size); + } +} + void BKE_sculpt_attributes_destroy_temporary_stroke(Object *ob) { SculptSession *ss = ob->sculpt; @@ -3705,6 +3733,22 @@ void BKE_sculpt_attributes_destroy_temporary_stroke(Object *ob) */ if (!attr->params.simple_array && ss->bm) { + BMIter iter; + int size = CustomData_getTypeSize(attr->proptype); + + /* Zero the attribute in an attempt to emulate the behavior of releasing it. */ + switch (attr->domain) { + case ATTR_DOMAIN_POINT: { + sculpt_clear_attribute_bmesh(ss->bm, attr); + break; + } + case ATTR_DOMAIN_EDGE: + sculpt_clear_attribute_bmesh(ss->bm, attr); + break; + case ATTR_DOMAIN_FACE: + sculpt_clear_attribute_bmesh(ss->bm, attr); + break; + } continue; } diff --git a/source/blender/editors/sculpt_paint/sculpt.cc b/source/blender/editors/sculpt_paint/sculpt.cc index 51409fe1f84..03fd96f5360 100644 --- a/source/blender/editors/sculpt_paint/sculpt.cc +++ b/source/blender/editors/sculpt_paint/sculpt.cc @@ -5074,7 +5074,6 @@ void SCULPT_cache_free(SculptSession *ss, struct Object *ob, StrokeCache *cache) { MEM_SAFE_FREE(cache->dial); MEM_SAFE_FREE(cache->prev_colors); - MEM_SAFE_FREE(cache->detail_directions); MEM_SAFE_FREE(cache->prev_displacement); MEM_SAFE_FREE(cache->limit_surface_co); MEM_SAFE_FREE(cache->prev_colors_vpaint); diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.hh b/source/blender/editors/sculpt_paint/sculpt_intern.hh index 71a13d48925..ee248adc5d5 100644 --- a/source/blender/editors/sculpt_paint/sculpt_intern.hh +++ b/source/blender/editors/sculpt_paint/sculpt_intern.hh @@ -778,9 +778,6 @@ struct StrokeCache { /* Pose brush */ SculptPoseIKChain *pose_ik_chain; - /* Enhance Details. */ - float (*detail_directions)[3]; - /* Clay Thumb brush */ /* Angle of the front tilting plane of the brush to simulate clay accumulation. */ float clay_thumb_front_angle; diff --git a/source/blender/editors/sculpt_paint/sculpt_ops.cc b/source/blender/editors/sculpt_paint/sculpt_ops.cc index 45f59f30d3b..eeb3676f35e 100644 --- a/source/blender/editors/sculpt_paint/sculpt_ops.cc +++ b/source/blender/editors/sculpt_paint/sculpt_ops.cc @@ -1694,7 +1694,6 @@ static void sculpt_bake_cavity_exec_task_cb(void *__restrict userdata, case AUTOMASK_BAKE_MULTIPLY: mask = *vd.mask * automask; break; - break; case AUTOMASK_BAKE_DIVIDE: mask = automask > 0.00001f ? *vd.mask / automask : 0.0f; break; @@ -2000,7 +1999,6 @@ static int sculpt_reveal_all_exec(bContext *C, wmOperator *op) BMIter iter; BMFace *f; BMVert *v; - const int cd_mask = CustomData_get_offset(&ss->bm->vdata, CD_PAINT_MASK); BM_ITER_MESH (v, &iter, ss->bm, BM_VERTS_OF_MESH) { BM_log_vert_before_modified(ss->bm, ss->bm_log, v); diff --git a/source/blender/editors/sculpt_paint/sculpt_smooth.cc b/source/blender/editors/sculpt_paint/sculpt_smooth.cc index c30eef4a575..26833625a6d 100644 --- a/source/blender/editors/sculpt_paint/sculpt_smooth.cc +++ b/source/blender/editors/sculpt_paint/sculpt_smooth.cc @@ -260,7 +260,8 @@ static void do_enhance_details_brush_task_cb_ex(void *__restrict userdata, &automask_data); float disp[3]; - madd_v3_v3v3fl(disp, vd.co, ss->cache->detail_directions[vd.index], fade); + float *detail_dir = SCULPT_vertex_attr_get(vd.vertex, ss->attrs.detail_directions); + madd_v3_v3v3fl(disp, vd.co, detail_dir, fade); SCULPT_clip(sd, ss, vd.co, disp); if (vd.is_mesh) { @@ -287,15 +288,23 @@ static void SCULPT_enhance_details_brush(Sculpt *sd, if (SCULPT_stroke_is_first_brush_step(ss->cache)) { const int totvert = SCULPT_vertex_count_get(ss); - ss->cache->detail_directions = static_cast( - MEM_malloc_arrayN(totvert, sizeof(float[3]), "details directions")); + + if (!ss->attrs.detail_directions) { + SculptAttributeParams params = {}; + params.stroke_only = true; + + ss->attrs.detail_directions = BKE_sculpt_attribute_ensure( + ob, ATTR_DOMAIN_POINT, CD_PROP_FLOAT3, SCULPT_ATTRIBUTE_NAME(laplacian_disp), ¶ms); + } for (int i = 0; i < totvert; i++) { PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); float avg[3]; SCULPT_neighbor_coords_average(ss, avg, vertex, projection, fset_projection, use_weighted); - sub_v3_v3v3(ss->cache->detail_directions[i], avg, SCULPT_vertex_co_get(ss, vertex)); + float *detail_dir = SCULPT_vertex_attr_get(vertex, ss->attrs.detail_directions); + + sub_v3_v3v3(detail_dir, avg, SCULPT_vertex_co_get(ss, vertex)); } } -- 2.30.2 From 681a55bb0124491c840ed7da09fa5bb70de7c1af Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Fri, 10 Mar 2023 01:15:43 -0800 Subject: [PATCH 030/279] temp-sculpt-dyntopo: Fix crash in modifier stack --- source/blender/blenkernel/BKE_mesh_mirror.h | 3 +- .../blenkernel/BKE_mesh_remesh_voxel.h | 10 - source/blender/blenkernel/BKE_mesh_types.h | 3 +- source/blender/blenkernel/BKE_multires.h | 2 - source/blender/blenkernel/BKE_object.h | 2 - source/blender/blenkernel/BKE_subdiv_ccg.h | 11 -- .../blender/blenkernel/intern/DerivedMesh.cc | 2 +- .../blender/blenkernel/intern/crazyspace.cc | 5 +- .../blender/blenkernel/intern/mesh_mirror.cc | 6 +- source/blender/blenkernel/intern/scene.cc | 5 - .../intern/subdiv_displacement_multires.cc | 1 - .../blender/editors/object/object_remesh.cc | 2 +- .../editors/sculpt_paint/sculpt_ops.cc | 5 - source/blender/makesdna/DNA_modifier_types.h | 1 - source/blender/makesrna/intern/rna_modifier.c | 174 ------------------ 15 files changed, 7 insertions(+), 225 deletions(-) diff --git a/source/blender/blenkernel/BKE_mesh_mirror.h b/source/blender/blenkernel/BKE_mesh_mirror.h index 3389a18c18b..0ad6b382d0f 100644 --- a/source/blender/blenkernel/BKE_mesh_mirror.h +++ b/source/blender/blenkernel/BKE_mesh_mirror.h @@ -16,8 +16,7 @@ struct Mesh; struct MirrorModifierData; struct Object; -struct Mesh *BKE_mesh_mirror_bisect_on_mirror_plane_for_modifier(struct Object *ob, - struct MirrorModifierData *mmd, +struct Mesh *BKE_mesh_mirror_bisect_on_mirror_plane_for_modifier(struct MirrorModifierData *mmd, const struct Mesh *mesh, int axis, const float plane_co[3], diff --git a/source/blender/blenkernel/BKE_mesh_remesh_voxel.h b/source/blender/blenkernel/BKE_mesh_remesh_voxel.h index 34ce8d6cebf..d9f5a75ca61 100644 --- a/source/blender/blenkernel/BKE_mesh_remesh_voxel.h +++ b/source/blender/blenkernel/BKE_mesh_remesh_voxel.h @@ -27,20 +27,10 @@ struct Mesh *BKE_mesh_remesh_quadriflow(const struct Mesh *mesh, void (*update_cb)(void *, float progress, int *cancel), void *update_cb_data); -struct Mesh *BKE_mesh_remesh_instant_meshes(const Mesh *input_mesh, - int target_faces, - int iterations, - void (*update_cb)(void *, float progress, int *cancel), - void *update_cb_data); - /* Data reprojection functions */ void BKE_mesh_remesh_reproject_paint_mask(struct Mesh *target, const struct Mesh *source); void BKE_remesh_reproject_vertex_paint(struct Mesh *target, const struct Mesh *source); void BKE_remesh_reproject_sculpt_face_sets(struct Mesh *target, const struct Mesh *source); -void BKE_remesh_reproject_materials(struct Mesh *target, const struct Mesh *source); -void BKE_mesh_remesh_sculpt_array_update(struct Object *ob, - struct Mesh *target, - struct Mesh *source); #ifdef __cplusplus } diff --git a/source/blender/blenkernel/BKE_mesh_types.h b/source/blender/blenkernel/BKE_mesh_types.h index 76c6e846665..b9c62f39c57 100644 --- a/source/blender/blenkernel/BKE_mesh_types.h +++ b/source/blender/blenkernel/BKE_mesh_types.h @@ -30,7 +30,6 @@ struct MLoopTri; struct ShrinkwrapBoundaryData; struct SubdivCCG; struct SubsurfRuntimeData; -struct Mesh; #endif @@ -86,7 +85,7 @@ struct MeshRuntime { /* Evaluated mesh for objects which do not have effective modifiers. * This mesh is used as a result of modifier stack evaluation. * Since modifier stack evaluation is threaded on object level we need some synchronization. */ - struct Mesh *mesh_eval = nullptr; + Mesh *mesh_eval = nullptr; std::mutex eval_mutex; /* A separate mutex is needed for normal calculation, because sometimes diff --git a/source/blender/blenkernel/BKE_multires.h b/source/blender/blenkernel/BKE_multires.h index 13a26a89e4e..02a0e184087 100644 --- a/source/blender/blenkernel/BKE_multires.h +++ b/source/blender/blenkernel/BKE_multires.h @@ -23,7 +23,6 @@ struct MultiresModifierData; struct Object; struct Scene; struct SubdivCCG; -struct BMesh; struct MPoly; @@ -235,7 +234,6 @@ BLI_INLINE void BKE_multires_construct_tangent_matrix(float tangent_matrix[3][3] const float dPdv[3], int corner); -void BKE_multires_bmesh_space_set(struct Object *ob, struct BMesh *bm, int mode); /* Versioning. */ /** diff --git a/source/blender/blenkernel/BKE_object.h b/source/blender/blenkernel/BKE_object.h index 647aed8186a..92ea5bd4986 100644 --- a/source/blender/blenkernel/BKE_object.h +++ b/source/blender/blenkernel/BKE_object.h @@ -10,9 +10,7 @@ #include "BLI_compiler_attrs.h" #include "BLI_sys_types.h" -#include "BKE_lib_id.h" #include "DNA_object_enums.h" -#include "DNA_userdef_types.h" #ifdef __cplusplus extern "C" { diff --git a/source/blender/blenkernel/BKE_subdiv_ccg.h b/source/blender/blenkernel/BKE_subdiv_ccg.h index d8fa552841d..582e0774a58 100644 --- a/source/blender/blenkernel/BKE_subdiv_ccg.h +++ b/source/blender/blenkernel/BKE_subdiv_ccg.h @@ -299,17 +299,6 @@ void BKE_subdiv_ccg_eval_limit_point(const SubdivCCG *subdiv_ccg, const SubdivCCGCoord *coord, float r_point[3]); -void BKE_subdiv_ccg_eval_limit_point_and_derivatives(const SubdivCCG *subdiv_ccg, - const SubdivCCGCoord *coord, - float r_point[3], - float r_dPdu[3], - float r_dPdv[3]); - -void BKE_subdiv_ccg_get_tangent_matrix(const SubdivCCG *subdiv_ccg, - const SubdivCCGCoord *coord, - float mat[3][3], - float r_point[3]); - typedef enum SubdivCCGAdjacencyType { SUBDIV_CCG_ADJACENT_NONE, SUBDIV_CCG_ADJACENT_VERTEX, diff --git a/source/blender/blenkernel/intern/DerivedMesh.cc b/source/blender/blenkernel/intern/DerivedMesh.cc index f673dd4496e..61ca1b81771 100644 --- a/source/blender/blenkernel/intern/DerivedMesh.cc +++ b/source/blender/blenkernel/intern/DerivedMesh.cc @@ -811,7 +811,7 @@ static void mesh_calc_modifiers(struct Depsgraph *depsgraph, unsupported = true; } - if (scene->toolsettings->sculpt && scene->toolsettings->sculpt->flags & SCULPT_ONLY_DEFORM) { + if (scene->toolsettings->sculpt->flags & SCULPT_ONLY_DEFORM) { unsupported |= (mti->type != eModifierTypeType_OnlyDeform); } diff --git a/source/blender/blenkernel/intern/crazyspace.cc b/source/blender/blenkernel/intern/crazyspace.cc index 29c8ceb634a..dc9dfc9f199 100644 --- a/source/blender/blenkernel/intern/crazyspace.cc +++ b/source/blender/blenkernel/intern/crazyspace.cc @@ -340,10 +340,7 @@ static void crazyspace_init_verts_and_matrices(const Mesh *mesh, static bool crazyspace_modifier_supports_deform_matrices(ModifierData *md) { - if (md->type == eModifierType_Multires) { - return true; - } - if (md->type == eModifierType_Subsurf && md->mode & eModifierMode_OnCage) { + if (ELEM(md->type, eModifierType_Subsurf, eModifierType_Multires)) { return true; } const ModifierTypeInfo *mti = BKE_modifier_get_info(static_cast(md->type)); diff --git a/source/blender/blenkernel/intern/mesh_mirror.cc b/source/blender/blenkernel/intern/mesh_mirror.cc index 5f32949868f..1a672d157dd 100644 --- a/source/blender/blenkernel/intern/mesh_mirror.cc +++ b/source/blender/blenkernel/intern/mesh_mirror.cc @@ -25,8 +25,7 @@ #include "MOD_modifiertypes.h" -Mesh *BKE_mesh_mirror_bisect_on_mirror_plane_for_modifier(Object *ob, - MirrorModifierData *mmd, +Mesh *BKE_mesh_mirror_bisect_on_mirror_plane_for_modifier(MirrorModifierData *mmd, const Mesh *mesh, int axis, const float plane_co[3], @@ -98,7 +97,6 @@ void BKE_mesh_mirror_apply_mirror_on_axis(struct Main *bmain, bmesh_from_mesh_params.cd_mask_extra.vmask = CD_MASK_SHAPEKEY; BMesh *bm = BKE_mesh_to_bmesh_ex(mesh, &bmesh_create_params, &bmesh_from_mesh_params); - BMO_op_callf(bm, (BMO_FLAG_DEFAULTS & ~BMO_FLAG_RESPECT_HIDE), "symmetrize input=%avef direction=%i dist=%f use_shapekey=%b", @@ -182,7 +180,7 @@ Mesh *BKE_mesh_mirror_apply_mirror_on_axis_for_modifier(MirrorModifierData *mmd, Mesh *mesh_bisect = nullptr; if (do_bisect) { mesh_bisect = BKE_mesh_mirror_bisect_on_mirror_plane_for_modifier( - ob, mmd, mesh, axis, plane_co, plane_no); + mmd, mesh, axis, plane_co, plane_no); mesh = mesh_bisect; } diff --git a/source/blender/blenkernel/intern/scene.cc b/source/blender/blenkernel/intern/scene.cc index d01842ad9e6..ccf62a6f899 100644 --- a/source/blender/blenkernel/intern/scene.cc +++ b/source/blender/blenkernel/intern/scene.cc @@ -672,7 +672,6 @@ static void scene_foreach_toolsettings(LibraryForeachIDData *data, do_undo_restore, reader, &toolsett_old->sculpt->paint)); - BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS_IDSUPER(data, toolsett->sculpt->gravity_object, do_undo_restore, @@ -681,7 +680,6 @@ static void scene_foreach_toolsettings(LibraryForeachIDData *data, toolsett_old->sculpt->gravity_object, IDWALK_CB_NOP); } - if (toolsett->uvsculpt) { BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS_FUNCTION_CALL( data, @@ -974,7 +972,6 @@ static void scene_blend_write(BlendWriter *writer, ID *id, const void *id_addres } if (tos->sculpt) { BLO_write_struct(writer, Sculpt, tos->sculpt); - if (tos->sculpt->automasking_cavity_curve) { BKE_curvemapping_blend_write(writer, tos->sculpt->automasking_cavity_curve); } @@ -1800,7 +1797,6 @@ ToolSettings *BKE_toolsettings_copy(ToolSettings *toolsettings, const int flag) } if (ts->sculpt) { ts->sculpt = static_cast(MEM_dupallocN(ts->sculpt)); - BKE_paint_copy(&ts->sculpt->paint, &ts->sculpt->paint, flag); if (ts->sculpt->automasking_cavity_curve) { @@ -1879,7 +1875,6 @@ void BKE_toolsettings_free(ToolSettings *toolsettings) } BKE_paint_free(&toolsettings->sculpt->paint); - MEM_freeN(toolsettings->sculpt); } if (toolsettings->uvsculpt) { diff --git a/source/blender/blenkernel/intern/subdiv_displacement_multires.cc b/source/blender/blenkernel/intern/subdiv_displacement_multires.cc index ab5020b6c2d..59309541493 100644 --- a/source/blender/blenkernel/intern/subdiv_displacement_multires.cc +++ b/source/blender/blenkernel/intern/subdiv_displacement_multires.cc @@ -350,7 +350,6 @@ static void eval_displacement(SubdivDisplacement *displacement, BKE_multires_construct_tangent_matrix(tangent_matrix, dPdu, dPdv, corner_of_quad); mul_v3_m3v3(r_D, tangent_matrix, tangent_D); /* For the boundary points of grid average two (or all) neighbor grids. */ - const int corner = displacement_get_face_corner(data, ptex_face_index, u, v); average_displacement(displacement, average_with, ptex_face_index, corner, grid_u, grid_v, r_D); } diff --git a/source/blender/editors/object/object_remesh.cc b/source/blender/editors/object/object_remesh.cc index 1997dfcf808..b61fea64c42 100644 --- a/source/blender/editors/object/object_remesh.cc +++ b/source/blender/editors/object/object_remesh.cc @@ -793,7 +793,7 @@ static Mesh *remesh_symmetry_bisect(Object *ob, Mesh *mesh, eSymmetryAxes symmet mesh_bisect_temp = mesh_bisect; mesh_bisect = BKE_mesh_mirror_bisect_on_mirror_plane_for_modifier( - ob, &mmd, mesh_bisect, axis, plane_co, plane_no); + &mmd, mesh_bisect, axis, plane_co, plane_no); if (mesh_bisect_temp != mesh_bisect) { BKE_id_free(nullptr, mesh_bisect_temp); diff --git a/source/blender/editors/sculpt_paint/sculpt_ops.cc b/source/blender/editors/sculpt_paint/sculpt_ops.cc index eeb3676f35e..7ca0cf277ad 100644 --- a/source/blender/editors/sculpt_paint/sculpt_ops.cc +++ b/source/blender/editors/sculpt_paint/sculpt_ops.cc @@ -9,7 +9,6 @@ #include "MEM_guardedalloc.h" #include "BLI_alloca.h" -#include "BLI_array.h" #include "BLI_ghash.h" #include "BLI_gsqueue.h" #include "BLI_math.h" @@ -1411,9 +1410,6 @@ static int sculpt_regularize_rake_exec(bContext *C, wmOperator *op) MEM_SAFE_FREE(nodes); - BMVert **stack = nullptr; - BLI_array_declare(stack); - bm->elem_index_dirty |= BM_VERT; BM_mesh_elem_index_ensure(bm, BM_VERT); @@ -1608,7 +1604,6 @@ static int sculpt_regularize_rake_exec(bContext *C, wmOperator *op) } MEM_SAFE_FREE(verts); - BLI_array_free(stack); BLI_mempool_destroy(nodepool); MEM_SAFE_FREE(visit); } diff --git a/source/blender/makesdna/DNA_modifier_types.h b/source/blender/makesdna/DNA_modifier_types.h index db353a0609c..5d42cabeb03 100644 --- a/source/blender/makesdna/DNA_modifier_types.h +++ b/source/blender/makesdna/DNA_modifier_types.h @@ -84,7 +84,6 @@ typedef enum ModifierType { eModifierType_MeshToVolume = 58, eModifierType_VolumeDisplace = 59, eModifierType_VolumeToMesh = 60, - eModifierType_BassRelief = 61, NUM_MODIFIER_TYPES, } ModifierType; diff --git a/source/blender/makesrna/intern/rna_modifier.c b/source/blender/makesrna/intern/rna_modifier.c index b01e96fef9b..68a1b74deab 100644 --- a/source/blender/makesrna/intern/rna_modifier.c +++ b/source/blender/makesrna/intern/rna_modifier.c @@ -201,7 +201,6 @@ const EnumPropertyItem rna_enum_object_modifier_type_items[] = { ICON_MOD_ARMATURE, "Armature", "Deform the shape using an armature object"}, - {eModifierType_BassRelief, "BASS_RELIEF", ICON_MOD_SHRINKWRAP, "Bass Relief", ""}, {eModifierType_Cast, "CAST", ICON_MOD_CAST, @@ -1119,18 +1118,6 @@ static void rna_ShrinkwrapModifier_face_cull_set(struct PointerRNA *ptr, int val swm->shrinkOpts = (swm->shrinkOpts & ~MOD_SHRINKWRAP_CULL_TARGET_MASK) | value; } -static int rna_BassReliefModifier_face_cull_get(PointerRNA *ptr) -{ - BassReliefModifierData *swm = (BassReliefModifierData *)ptr->data; - return swm->shrinkOpts & MOD_BASSRELIEF_CULL_TARGET_MASK; -} - -static void rna_BassReliefModifier_face_cull_set(struct PointerRNA *ptr, int value) -{ - BassReliefModifierData *swm = (BassReliefModifierData *)ptr->data; - swm->shrinkOpts = (swm->shrinkOpts & ~MOD_BASSRELIEF_CULL_TARGET_MASK) | value; -} - static bool rna_MeshDeformModifier_is_bound_get(PointerRNA *ptr) { return (((MeshDeformModifierData *)ptr->data)->bindcagecos != NULL); @@ -4461,166 +4448,6 @@ static void rna_def_modifier_shrinkwrap(BlenderRNA *brna) RNA_define_lib_overridable(false); } -static void rna_def_modifier_bassrelief(BlenderRNA *brna) -{ - StructRNA *srna; - PropertyRNA *prop; - - static const EnumPropertyItem bassrelief_face_cull_items[] = { - //{0, "OFF", 0, "Off", "No culling"}, - {MOD_BASSRELIEF_CULL_TARGET_FRONTFACE, - "FRONT", - 0, - "Front", - "No projection when in front of the face"}, - {MOD_BASSRELIEF_CULL_TARGET_BACKFACE, - "BACK", - 0, - "Back", - "No projection when behind the face"}, - {0, NULL, 0, NULL, NULL}, - }; - - srna = RNA_def_struct(brna, "BassReliefModifier", "Modifier"); - RNA_def_struct_ui_text(srna, "Bass Relief Modifier", "Create bass reliefs"); - RNA_def_struct_sdna(srna, "BassReliefModifierData"); - RNA_def_struct_ui_icon(srna, ICON_MOD_SHRINKWRAP); - - RNA_define_lib_overridable(true); - - prop = RNA_def_property(srna, "cull_face", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_sdna(prop, NULL, "shrinkOpts"); - RNA_def_property_enum_items(prop, bassrelief_face_cull_items); - RNA_def_property_enum_funcs( - prop, "rna_BassReliefModifier_face_cull_get", "rna_BassReliefModifier_face_cull_set", NULL); - RNA_def_property_ui_text( - prop, - "Face Cull", - "Stop vertices from projecting to a face on the target when facing towards/away"); - RNA_def_property_update(prop, 0, "rna_Modifier_update"); - - prop = RNA_def_property(srna, "target", PROP_POINTER, PROP_NONE); - RNA_def_property_ui_text(prop, "Target", "Mesh target to shrink to"); - RNA_def_property_pointer_funcs( - prop, NULL, "rna_ShrinkwrapModifier_target_set", NULL, "rna_Mesh_object_poll"); - RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK); - RNA_def_property_update(prop, 0, "rna_Modifier_dependency_update"); - - prop = RNA_def_property(srna, "collection", PROP_POINTER, PROP_NONE); - RNA_def_property_pointer_sdna(prop, NULL, "collection"); - RNA_def_property_ui_text(prop, "Collection Target", "Use meshes in this collection as targets"); - // RNA_def_property_pointer_funcs( - // prop, NULL, "rna_ShrinkwrapModifier_auxTarget_set", NULL, "rna_Mesh_object_poll"); - RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK); - RNA_def_property_update(prop, 0, "rna_Modifier_dependency_update"); - - prop = RNA_def_property(srna, "vertex_group", PROP_STRING, PROP_NONE); - RNA_def_property_string_sdna(prop, NULL, "vgroup_name"); - RNA_def_property_ui_text(prop, "Vertex Group", "Vertex group name"); - // RNA_def_property_string_funcs(prop, NULL, NULL, "rna_BassReliefModifier_vgroup_name_set"); - RNA_def_property_update(prop, 0, "rna_Modifier_update"); - - prop = RNA_def_property(srna, "offset", PROP_FLOAT, PROP_DISTANCE); - RNA_def_property_float_sdna(prop, NULL, "keepDist"); - RNA_def_property_range(prop, -FLT_MAX, FLT_MAX); - RNA_def_property_ui_range(prop, -100, 100, 1, 2); - RNA_def_property_ui_text(prop, "Offset", "Distance to keep from the target"); - RNA_def_property_update(prop, 0, "rna_Modifier_update"); - - prop = RNA_def_property(srna, "project_limit", PROP_FLOAT, PROP_DISTANCE); - RNA_def_property_float_sdna(prop, NULL, "projLimit"); - RNA_def_property_range(prop, 0.0, FLT_MAX); - RNA_def_property_ui_range(prop, 0, 100, 1, 2); - RNA_def_property_ui_text( - prop, "Project Limit", "Limit the distance used for projection (zero disables)"); - RNA_def_property_update(prop, 0, "rna_Modifier_update"); - - prop = RNA_def_property(srna, "use_project_x", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "projAxis", MOD_BASSRELIEF_PROJECT_OVER_X_AXIS); - RNA_def_property_ui_text(prop, "X", ""); - RNA_def_property_update(prop, 0, "rna_Modifier_update"); - - prop = RNA_def_property(srna, "use_project_y", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "projAxis", MOD_BASSRELIEF_PROJECT_OVER_Y_AXIS); - RNA_def_property_ui_text(prop, "Y", ""); - RNA_def_property_update(prop, 0, "rna_Modifier_update"); - - prop = RNA_def_property(srna, "use_project_z", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "projAxis", MOD_BASSRELIEF_PROJECT_OVER_Z_AXIS); - RNA_def_property_ui_text(prop, "Z", ""); - RNA_def_property_update(prop, 0, "rna_Modifier_update"); - - prop = RNA_def_property(srna, "use_negative_direction", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "shrinkOpts", MOD_BASSRELIEF_PROJECT_ALLOW_NEG_DIR); - RNA_def_property_ui_text( - prop, "Negative", "Allow vertices to move in the negative direction of axis"); - RNA_def_property_update(prop, 0, "rna_Modifier_update"); - - prop = RNA_def_property(srna, "use_positive_direction", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "shrinkOpts", MOD_BASSRELIEF_PROJECT_ALLOW_POS_DIR); - RNA_def_property_ui_text( - prop, "Positive", "Allow vertices to move in the positive direction of axis"); - RNA_def_property_update(prop, 0, "rna_Modifier_update"); - - prop = RNA_def_property(srna, "use_invert_cull", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "shrinkOpts", MOD_BASSRELIEF_INVERT_CULL_TARGET); - RNA_def_property_ui_text( - prop, "Invert Cull", "When projecting in the negative direction invert the face cull mode"); - RNA_def_property_update(prop, 0, "rna_Modifier_update"); - - prop = RNA_def_property(srna, "invert_vertex_group", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "shrinkOpts", MOD_BASSRELIEF_INVERT_VGROUP); - RNA_def_property_ui_text(prop, "Invert", "Invert vertex group influence"); - RNA_def_property_update(prop, 0, "rna_Modifier_update"); - - prop = RNA_def_property(srna, "ray_shrink_ratio", PROP_FLOAT, PROP_NONE); - RNA_def_property_float_sdna(prop, NULL, "rayShrinkRatio"); - RNA_def_property_ui_text( - prop, "Shrink Ratio", "Compress shrinkwrap result by this ratio; useful for reliefs"); - RNA_def_property_update(prop, 0, "rna_Modifier_update"); - - prop = RNA_def_property(srna, "use_normal_optimizer", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "shrinkOpts", MOD_BASSRELIEF_OPTIMIZE); - RNA_def_property_ui_text(prop, - "Optimize", - "Try to optimize shading to match original mesh; useful for making " - "reliefs when combined with Shrink Ratio"); - RNA_def_property_update(prop, 0, "rna_Modifier_update"); - - prop = RNA_def_property(srna, "optimizer_steps", PROP_INT, PROP_NONE); - RNA_def_property_int_sdna(prop, NULL, "optimizeSteps"); - RNA_def_property_ui_text(prop, "Exaggeration Filter", ""); - RNA_def_property_range(prop, 0, 255); - RNA_def_property_ui_range(prop, 0, 10, 1, 1); - RNA_def_property_update(prop, 0, "rna_Modifier_update"); - - prop = RNA_def_property(srna, "boundary_smooth_steps", PROP_INT, PROP_NONE); - RNA_def_property_int_sdna(prop, NULL, "boundSmoothSteps"); - RNA_def_property_ui_text(prop, "Boundary Smooth", "Boundary smooth steps"); - RNA_def_property_range(prop, 0, 255); - RNA_def_property_ui_range(prop, 0, 10, 1, 1); - RNA_def_property_update(prop, 0, "rna_Modifier_update"); - - prop = RNA_def_property(srna, "boundary_smooth_falloff", PROP_FLOAT, PROP_NONE); - RNA_def_property_float_sdna(prop, NULL, "boundSmoothFalloff"); - RNA_def_property_ui_range(prop, 0.001, 4.0, 0.01, 3); - RNA_def_property_ui_text(prop, "Boundary Falloff", "Boundary width"); - RNA_def_property_update(prop, 0, "rna_Modifier_update"); - - prop = RNA_def_property(srna, "detail_scale", PROP_FLOAT, PROP_NONE); - RNA_def_property_float_sdna(prop, NULL, "detailScale"); - RNA_def_property_ui_range(prop, 0.001, 4.0, 0.01, 3); - RNA_def_property_ui_text(prop, "Exaggeration Scale", ""); - RNA_def_property_update(prop, 0, "rna_Modifier_update"); - /* - uiItemR(col, ptr, "normal_optimizer_scale", 0, NULL, ICON_NONE); - uiItemR(col, ptr, "normal_optimizer_steps", 0, NULL, ICON_NONE); - uiItemR(col, ptr, "boundary_smooth_scale", 0, NULL, ICON_NONE); - uiItemR(col, ptr, "boundary_smooth_steps", 0, NULL, ICON_NONE); - - */ - RNA_define_lib_overridable(false); -} static void rna_def_modifier_mask(BlenderRNA *brna) { StructRNA *srna; @@ -7653,7 +7480,6 @@ void RNA_def_modifier(BlenderRNA *brna) rna_def_modifier_mesh_to_volume(brna); rna_def_modifier_volume_displace(brna); rna_def_modifier_volume_to_mesh(brna); - rna_def_modifier_bassrelief(brna); } #endif -- 2.30.2 From 0e582f1cc3c0b0ea26d07184ed182fe16fa4dea0 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Fri, 10 Mar 2023 02:11:14 -0800 Subject: [PATCH 031/279] temp-sculpt-dyntopo: Fix "weight by area" for smooth brush Also exposed local brush dyntopo disable property in ui. --- .../startup/bl_ui/properties_paint_common.py | 5 ++ source/blender/blenkernel/intern/brush.cc | 3 + source/blender/blenkernel/intern/pbvh.cc | 5 +- .../blenloader/intern/versioning_300.cc | 19 +++++++ .../bmesh/intern/bmesh_mesh_convert.cc | 6 +- source/blender/editors/sculpt_paint/sculpt.cc | 5 ++ .../editors/sculpt_paint/sculpt_boundary.cc | 4 +- .../sculpt_paint/sculpt_filter_mesh.cc | 5 +- .../editors/sculpt_paint/sculpt_intern.hh | 7 ++- .../editors/sculpt_paint/sculpt_smooth.cc | 57 +++++++++++++------ 10 files changed, 90 insertions(+), 26 deletions(-) diff --git a/scripts/startup/bl_ui/properties_paint_common.py b/scripts/startup/bl_ui/properties_paint_common.py index 08abf33af00..f29884572ea 100644 --- a/scripts/startup/bl_ui/properties_paint_common.py +++ b/scripts/startup/bl_ui/properties_paint_common.py @@ -550,6 +550,8 @@ def brush_settings(layout, context, brush, popover=False): row.prop(brush, "invert_hardness_pressure", text="") row.prop(brush, "use_hardness_pressure", text="") + layout.prop(brush.dyntopo, "disabled") + # auto_smooth_factor and use_inverse_smooth_pressure if capabilities.has_auto_smooth: UnifiedPaintPanel.prop_unified( @@ -561,6 +563,9 @@ def brush_settings(layout, context, brush, popover=False): slider=True, ) + if capabilities.has_auto_smooth or brush.sculpt_tool == "SMOOTH": + layout.prop(brush, "use_weighted_smooth") + # topology_rake_factor if ( capabilities.has_topology_rake and diff --git a/source/blender/blenkernel/intern/brush.cc b/source/blender/blenkernel/intern/brush.cc index 5630c7f5244..a02bd00ef93 100644 --- a/source/blender/blenkernel/intern/brush.cc +++ b/source/blender/blenkernel/intern/brush.cc @@ -1753,6 +1753,7 @@ void BKE_brush_sculpt_reset(Brush *br) br->alpha = 1.0f; br->slide_deform_type = BRUSH_SLIDE_DEFORM_DRAG; disable_dyntopo = true; + br->flag2 |= BRUSH_SMOOTH_USE_AREA_WEIGHT; break; case SCULPT_TOOL_CLAY: br->flag |= BRUSH_SIZE_PRESSURE; @@ -1809,6 +1810,8 @@ void BKE_brush_sculpt_reset(Brush *br) br->surface_smooth_shape_preservation = 0.5f; br->surface_smooth_current_vertex = 0.5f; br->surface_smooth_iterations = 4; + disable_dyntopo = true; + br->flag2 |= BRUSH_SMOOTH_USE_AREA_WEIGHT; break; case SCULPT_TOOL_SNAKE_HOOK: br->alpha = 1.0f; diff --git a/source/blender/blenkernel/intern/pbvh.cc b/source/blender/blenkernel/intern/pbvh.cc index d6f967f2f9d..f9969c6759d 100644 --- a/source/blender/blenkernel/intern/pbvh.cc +++ b/source/blender/blenkernel/intern/pbvh.cc @@ -4161,7 +4161,10 @@ void BKE_pbvh_set_vemap(PBVH *pbvh, MeshElemMap *vemap) pbvh->vemap = vemap; } -void BKE_pbvh_get_vert_face_areas(PBVH *pbvh, PBVHVertRef vertex, float *r_areas, int valence) +ATTR_NO_OPT void BKE_pbvh_get_vert_face_areas(PBVH *pbvh, + PBVHVertRef vertex, + float *r_areas, + int valence) { const int cur_i = pbvh->face_area_i; diff --git a/source/blender/blenloader/intern/versioning_300.cc b/source/blender/blenloader/intern/versioning_300.cc index 7d6eb591923..a4ffc7d00ac 100644 --- a/source/blender/blenloader/intern/versioning_300.cc +++ b/source/blender/blenloader/intern/versioning_300.cc @@ -4014,6 +4014,25 @@ void blo_do_versions_300(FileData *fd, Library * /*lib*/, Main *bmain) } } + if (!DNA_struct_elem_find(fd->filesdna, "Brush", "DynTopoSettings", "dyntopo")) { + LISTBASE_FOREACH (Brush *, brush, &bmain->brushes) { + if (ELEM(brush->sculpt_tool, + SCULPT_TOOL_SMOOTH, + SCULPT_TOOL_DISPLACEMENT_ERASER, + SCULPT_TOOL_SLIDE_RELAX, + SCULPT_TOOL_ROTATE, + SCULPT_TOOL_GRAB, + SCULPT_TOOL_CLOTH, + SCULPT_TOOL_PAINT)) { + brush->dyntopo.flag |= DYNTOPO_DISABLED; + } + + if (ELEM(brush->sculpt_tool, SCULPT_TOOL_SMOOTH, SCULPT_TOOL_SLIDE_RELAX)) { + brush->flag2 |= BRUSH_SMOOTH_USE_AREA_WEIGHT; + } + } + } + /** * Versioning code until next subversion bump goes here. * diff --git a/source/blender/bmesh/intern/bmesh_mesh_convert.cc b/source/blender/bmesh/intern/bmesh_mesh_convert.cc index c151df90199..f2d7ff09c79 100644 --- a/source/blender/bmesh/intern/bmesh_mesh_convert.cc +++ b/source/blender/bmesh/intern/bmesh_mesh_convert.cc @@ -1319,8 +1319,10 @@ ATTR_NO_OPT void BM_mesh_bm_to_me(Main *bmain, bm->ldata.layers[i].flag &= ~CD_FLAG_NOCOPY; } - CustomData_add_layer_named( - &me->vdata, CD_PROP_FLOAT3, CD_CONSTRUCT, nullptr, me->totvert, "position"); + if (!CustomData_get_layer_named(&me->vdata, CD_PROP_FLOAT3, "position")) { + CustomData_add_layer_named( + &me->vdata, CD_PROP_FLOAT3, CD_CONSTRUCT, nullptr, me->totvert, "position"); + } CustomData_add_layer(&me->edata, CD_MEDGE, CD_SET_DEFAULT, nullptr, me->totedge); CustomData_add_layer(&me->ldata, CD_MLOOP, CD_SET_DEFAULT, nullptr, me->totloop); CustomData_add_layer(&me->pdata, CD_MPOLY, CD_SET_DEFAULT, nullptr, me->totpoly); diff --git a/source/blender/editors/sculpt_paint/sculpt.cc b/source/blender/editors/sculpt_paint/sculpt.cc index 03fd96f5360..cfd0ab2e9bc 100644 --- a/source/blender/editors/sculpt_paint/sculpt.cc +++ b/source/blender/editors/sculpt_paint/sculpt.cc @@ -4083,6 +4083,11 @@ static void sculpt_topology_update(Sculpt *sd, mode |= PBVH_Subdivide; } + /* Force both subdivide and collapse for simplify brush. */ + if (brush->sculpt_tool == SCULPT_TOOL_SIMPLIFY) { + mode |= PBVH_Collapse | PBVH_Subdivide; + } + SculptSearchSphereData sdata{}; sdata.ss = ss, sdata.sd = sd, sdata.ob = ob; sdata.radius_squared = square_f(ss->cache->radius * radius_scale * 1.25f); diff --git a/source/blender/editors/sculpt_paint/sculpt_boundary.cc b/source/blender/editors/sculpt_paint/sculpt_boundary.cc index 4c2f1f66506..af49d3b29f9 100644 --- a/source/blender/editors/sculpt_paint/sculpt_boundary.cc +++ b/source/blender/editors/sculpt_paint/sculpt_boundary.cc @@ -1962,7 +1962,7 @@ static void SCULPT_boundary_autosmooth(SculptSession *ss, SculptBoundary *bounda float sco[3]; - SCULPT_neighbor_coords_average_interior(ss, sco, vd.vertex, projection, slide_fset); + SCULPT_neighbor_coords_average_interior(ss, sco, vd.vertex, projection, slide_fset, true); float *co = SCULPT_brush_deform_target_vertex_co_get(ss, boundary->deform_target, &vd); @@ -2001,7 +2001,7 @@ static void SCULPT_boundary_build_smoothco(SculptSession *ss, SculptBoundary *bo float sco[3]; - SCULPT_neighbor_coords_average_interior(ss, sco, vd.vertex, projection, slide_fset); + SCULPT_neighbor_coords_average_interior(ss, sco, vd.vertex, projection, slide_fset, true); float *co = SCULPT_brush_deform_target_vertex_co_get(ss, boundary->deform_target, &vd); diff --git a/source/blender/editors/sculpt_paint/sculpt_filter_mesh.cc b/source/blender/editors/sculpt_paint/sculpt_filter_mesh.cc index 646f1f8950d..380359bfab0 100644 --- a/source/blender/editors/sculpt_paint/sculpt_filter_mesh.cc +++ b/source/blender/editors/sculpt_paint/sculpt_filter_mesh.cc @@ -429,7 +429,8 @@ static void mesh_filter_task_cb(void *__restrict userdata, switch (filter_type) { case MESH_FILTER_SMOOTH: fade = clamp_f(fade, -1.0f, 1.0f); - SCULPT_neighbor_coords_average_interior(ss, avg, vd.vertex, projection, fset_projection); + SCULPT_neighbor_coords_average_interior( + ss, avg, vd.vertex, projection, fset_projection, true); sub_v3_v3v3(val, avg, orig_co); madd_v3_v3v3fl(val, orig_co, val, fade); sub_v3_v3v3(disp, val, orig_co); @@ -492,7 +493,7 @@ static void mesh_filter_task_cb(void *__restrict userdata, break; } case MESH_FILTER_SURFACE_SMOOTH: { - SCULPT_surface_smooth_laplacian_step(ss, disp, vd.co, vd.vertex, orig_data.co, 1.0f); + SCULPT_surface_smooth_laplacian_step(ss, disp, vd.co, vd.vertex, orig_data.co, 1.0f, true); break; } case MESH_FILTER_SHARPEN: { diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.hh b/source/blender/editors/sculpt_paint/sculpt_intern.hh index ee248adc5d5..b26d78a9f89 100644 --- a/source/blender/editors/sculpt_paint/sculpt_intern.hh +++ b/source/blender/editors/sculpt_paint/sculpt_intern.hh @@ -1883,7 +1883,8 @@ void SCULPT_neighbor_coords_average_interior(SculptSession *ss, float result[3], PBVHVertRef vertex, float projection, - float fset_projection); + float fset_projection, + bool use_area_weights); BLI_INLINE bool SCULPT_get_fset_projection(SculptSession *ss, float fset_projection) { if (ss->hard_edge_mode) { @@ -1919,7 +1920,8 @@ void SCULPT_surface_smooth_laplacian_step(SculptSession *ss, const float co[3], const PBVHVertRef vertex, const float origco[3], - const float alpha); + const float alpha, + bool use_area_weights); void SCULPT_surface_smooth_displace_step( SculptSession *ss, float *co, const PBVHVertRef v_index, const float beta, const float fade); @@ -2617,4 +2619,3 @@ BLI_INLINE bool SCULPT_stroke_id_test_no_update(SculptSession *ss, int SCULPT_get_symmetry_pass(const struct SculptSession *ss); #define SCULPT_boundary_flag_update BKE_sculpt_boundary_flag_update - diff --git a/source/blender/editors/sculpt_paint/sculpt_smooth.cc b/source/blender/editors/sculpt_paint/sculpt_smooth.cc index 26833625a6d..8e65f4cbe18 100644 --- a/source/blender/editors/sculpt_paint/sculpt_smooth.cc +++ b/source/blender/editors/sculpt_paint/sculpt_smooth.cc @@ -38,7 +38,6 @@ ATTR_NO_OPT static void SCULPT_neighbor_coords_average_interior_ex(SculptSession eSculptCorner corner_type) { float avg[3] = {0.0f, 0.0f, 0.0f}; - int total = 0; int neighbor_count = 0; const eSculptBoundary is_boundary = SCULPT_vertex_is_boundary(ss, vertex, bound_type); @@ -48,20 +47,32 @@ ATTR_NO_OPT static void SCULPT_neighbor_coords_average_interior_ex(SculptSession projection = max_ff(projection, fset_projection); } + float *areas = nullptr; + + if (weighted) { + const int valence = SCULPT_vertex_valence_get(ss, vertex); + areas = (float *)BLI_array_alloca(areas, valence); + BKE_pbvh_get_vert_face_areas(ss->pbvh, vertex, areas, valence); + } + + float total = 0.0f; + SculptVertexNeighborIter ni; SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vertex, ni) { neighbor_count++; + float w = weighted ? areas[ni.i] : 1.0f; + if (is_boundary) { /* Boundary vertices use only other boundary vertices. */ if (SCULPT_vertex_is_boundary(ss, ni.vertex, bound_type)) { - add_v3_v3(avg, SCULPT_vertex_co_get(ss, ni.vertex)); - total++; + madd_v3_v3fl(avg, SCULPT_vertex_co_get(ss, ni.vertex), w); + total += w; } } else { /* Interior vertices use all neighbors. */ - add_v3_v3(avg, SCULPT_vertex_co_get(ss, ni.vertex)); - total++; + madd_v3_v3fl(avg, SCULPT_vertex_co_get(ss, ni.vertex), w); + total += w; } } SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); @@ -95,14 +106,15 @@ void SCULPT_neighbor_coords_average_interior(SculptSession *ss, float result[3], PBVHVertRef vertex, float projection, - float fset_projection) + float fset_projection, + bool use_area_weights) { eSculptBoundary bound_type = SCULPT_BOUNDARY_MESH | SCULPT_BOUNDARY_FACE_SET | SCULPT_BOUNDARY_SEAM | SCULPT_BOUNDARY_SHARP; eSculptCorner corner_type = SCULPT_CORNER_MESH | SCULPT_CORNER_FACE_SET | SCULPT_CORNER_SEAM | SCULPT_CORNER_SHARP; SCULPT_neighbor_coords_average_interior_ex( - ss, result, vertex, projection, fset_projection, true, bound_type, corner_type); + ss, result, vertex, projection, fset_projection, use_area_weights, bound_type, corner_type); } void SCULPT_bmesh_four_neighbor_average(SculptSession *ss, @@ -284,7 +296,7 @@ static void SCULPT_enhance_details_brush(Sculpt *sd, float projection = brush->autosmooth_projection; float fset_projection = brush->autosmooth_fset_slide; - bool use_weighted = brush->flag2 & BRUSH_SMOOTH_USE_AREA_WEIGHT; + bool use_area_weights = brush->flag2 & BRUSH_SMOOTH_USE_AREA_WEIGHT; if (SCULPT_stroke_is_first_brush_step(ss->cache)) { const int totvert = SCULPT_vertex_count_get(ss); @@ -301,7 +313,8 @@ static void SCULPT_enhance_details_brush(Sculpt *sd, PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); float avg[3]; - SCULPT_neighbor_coords_average(ss, avg, vertex, projection, fset_projection, use_weighted); + SCULPT_neighbor_coords_average( + ss, avg, vertex, projection, fset_projection, use_area_weights); float *detail_dir = SCULPT_vertex_attr_get(vertex, ss->attrs.detail_directions); sub_v3_v3v3(detail_dir, avg, SCULPT_vertex_co_get(ss, vertex)); @@ -319,9 +332,9 @@ static void SCULPT_enhance_details_brush(Sculpt *sd, BLI_task_parallel_range(0, totnode, &data, do_enhance_details_brush_task_cb_ex, &settings); } -static void do_smooth_brush_task_cb_ex(void *__restrict userdata, - const int n, - const TaskParallelTLS *__restrict tls) +ATTR_NO_OPT static void do_smooth_brush_task_cb_ex(void *__restrict userdata, + const int n, + const TaskParallelTLS *__restrict tls) { SculptThreadedTaskData *data = static_cast(userdata); SculptSession *ss = data->ob->sculpt; @@ -338,6 +351,8 @@ static void do_smooth_brush_task_cb_ex(void *__restrict userdata, SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( ss, &test, data->brush->falloff_shape); + BKE_pbvh_check_tri_areas(ss->pbvh, data->nodes[n]); + const int thread_id = BLI_task_parallel_thread_id(tls); AutomaskingNodeData automask_data; SCULPT_automasking_node_begin( @@ -345,6 +360,7 @@ static void do_smooth_brush_task_cb_ex(void *__restrict userdata, float fset_projection = SCULPT_get_fset_projection(ss, brush->autosmooth_fset_slide); float projection = brush->autosmooth_projection; + bool weighted = brush->flag2 & BRUSH_SMOOTH_USE_AREA_WEIGHT; BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { if (!sculpt_brush_test_sq_fn(&test, vd.co)) { @@ -372,7 +388,8 @@ static void do_smooth_brush_task_cb_ex(void *__restrict userdata, } else { float avg[3], val[3]; - SCULPT_neighbor_coords_average_interior(ss, avg, vd.vertex, projection, fset_projection); + SCULPT_neighbor_coords_average_interior( + ss, avg, vd.vertex, projection, fset_projection, weighted); sub_v3_v3v3(val, avg, vd.co); madd_v3_v3v3fl(val, vd.co, val, fade); SCULPT_clip(sd, ss, vd.co, val); @@ -413,6 +430,10 @@ void SCULPT_smooth(Sculpt *sd, SCULPT_vertex_random_access_ensure(ss); SCULPT_boundary_info_ensure(ob); + if (brush->flag2 & BRUSH_SMOOTH_USE_AREA_WEIGHT) { + BKE_pbvh_face_areas_begin(ss->pbvh); + } + for (iteration = 0; iteration <= count; iteration++) { const float strength = (iteration != count) ? 1.0f : last; @@ -466,13 +487,14 @@ void SCULPT_surface_smooth_laplacian_step(SculptSession *ss, const float co[3], const PBVHVertRef vertex, const float origco[3], - const float alpha) + const float alpha, + bool use_area_weights) { float laplacian_smooth_co[3]; float weigthed_o[3], weigthed_q[3], d[3]; int v_index = BKE_pbvh_vertex_to_index(ss->pbvh, vertex); - SCULPT_neighbor_coords_average(ss, laplacian_smooth_co, vertex, 0.0f, 1.0f, true); + SCULPT_neighbor_coords_average(ss, laplacian_smooth_co, vertex, 0.0f, 1.0f, use_area_weights); mul_v3_v3fl(weigthed_o, origco, alpha); mul_v3_v3fl(weigthed_q, co, 1.0f - alpha); @@ -530,6 +552,8 @@ static void SCULPT_do_surface_smooth_brush_laplacian_task_cb_ex( SCULPT_automasking_node_begin( data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]); + bool weighted = brush->flag2 & BRUSH_SMOOTH_USE_AREA_WEIGHT; + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { SCULPT_orig_vert_data_update(&orig_data, vd.vertex); if (!sculpt_brush_test_sq_fn(&test, vd.co)) { @@ -550,7 +574,8 @@ static void SCULPT_do_surface_smooth_brush_laplacian_task_cb_ex( &automask_data); float disp[3]; - SCULPT_surface_smooth_laplacian_step(ss, disp, vd.co, vd.vertex, orig_data.co, alpha); + SCULPT_surface_smooth_laplacian_step( + ss, disp, vd.co, vd.vertex, orig_data.co, alpha, weighted); madd_v3_v3fl(vd.co, disp, clamp_f(fade, 0.0f, 1.0f)); if (vd.is_mesh) { BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); -- 2.30.2 From 29038867a933eade9c9b15179a4ce8a44ef1ed4b Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Fri, 10 Mar 2023 02:15:59 -0800 Subject: [PATCH 032/279] temp-sculpt-dyntopo: Add back global dyntopo disable Also renamed "Dyntopo" panel back to "Dynamic topology." --- scripts/startup/bl_ui/space_view3d_toolbar.py | 4 +++- source/blender/editors/sculpt_paint/sculpt.cc | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/scripts/startup/bl_ui/space_view3d_toolbar.py b/scripts/startup/bl_ui/space_view3d_toolbar.py index 0f97ccea6b5..04b5d6cc866 100644 --- a/scripts/startup/bl_ui/space_view3d_toolbar.py +++ b/scripts/startup/bl_ui/space_view3d_toolbar.py @@ -841,7 +841,7 @@ class VIEW3D_PT_tools_brush_falloff_normal(View3DPaintPanel, Panel): # TODO, move to space_view3d.py class VIEW3D_PT_sculpt_dyntopo(Panel, View3DPaintPanel): bl_context = ".sculpt_mode" # dot on purpose (access from topbar) - bl_label = "Dyntopo" + bl_label = "Dynamic Topology" bl_options = {'DEFAULT_CLOSED'} bl_ui_units_x = 12 @@ -873,6 +873,8 @@ class VIEW3D_PT_sculpt_dyntopo(Panel, View3DPaintPanel): col = layout.column() col.active = context.sculpt_object.use_dynamic_topology_sculpting + col.prop(sculpt, "use_dyntopo") + sub = col.column() sub.active = (brush and brush.sculpt_tool != 'MASK') if sculpt.detail_type_method in {'CONSTANT', 'MANUAL'}: diff --git a/source/blender/editors/sculpt_paint/sculpt.cc b/source/blender/editors/sculpt_paint/sculpt.cc index cfd0ab2e9bc..7060f2ebd20 100644 --- a/source/blender/editors/sculpt_paint/sculpt.cc +++ b/source/blender/editors/sculpt_paint/sculpt.cc @@ -4034,7 +4034,7 @@ static void sculpt_topology_update(Sculpt *sd, /* build brush radius scale */ float radius_scale = 1.0f; - if (brush->dyntopo.flag & DYNTOPO_DISABLED) { + if ((brush->dyntopo.flag & DYNTOPO_DISABLED) || !(sd->flags & SCULPT_DYNTOPO_ENABLED)) { return; } -- 2.30.2 From 27bdf013ec88685f900fcb4ccdcf3ca4e91f508f Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Thu, 16 Mar 2023 16:26:12 -0700 Subject: [PATCH 033/279] temp-sculpt-dyntopo: Rewrite minmax heap in c++ --- source/blender/blenkernel/intern/dyntopo.cc | 30 +- .../blenkernel/intern/dyntopo_collapse.cc | 13 +- .../blenkernel/intern/dyntopo_intern.hh | 17 +- source/blender/blenlib/BLI_heap_minmax.hh | 419 +++++++++++++++++- source/blender/blenlib/CMakeLists.txt | 3 +- 5 files changed, 428 insertions(+), 54 deletions(-) diff --git a/source/blender/blenkernel/intern/dyntopo.cc b/source/blender/blenkernel/intern/dyntopo.cc index 5e781d81b88..517be43bc0f 100644 --- a/source/blender/blenkernel/intern/dyntopo.cc +++ b/source/blender/blenkernel/intern/dyntopo.cc @@ -21,7 +21,7 @@ #include "BLI_compiler_compat.h" #include "BLI_ghash.h" #include "BLI_heap.h" -#include "BLI_heap_minmax.h" +#include "BLI_heap_minmax.hh" #include "BLI_heap_simple.h" #include "BLI_linklist.h" #include "BLI_math.h" @@ -747,7 +747,7 @@ static void edge_queue_insert_unified(EdgeQueueContext *eq_ctx, BMEdge *e) // lensqr += (BLI_thread_frand(0) - 0.5f) * 0.1 * eq_ctx->limit_mid; - BLI_mm_heap_insert(eq_ctx->heap_mm, lensqr, e); + eq_ctx->edge_heap.insert(lensqr, e); e->head.hflag |= BM_ELEM_TAG; } } @@ -2703,8 +2703,7 @@ extern "C" bool BKE_pbvh_bmesh_update_topology(PBVH *pbvh, safe_smooth = DYNTOPO_SAFE_SMOOTH_FAC; } - EdgeQueueContext eq_ctx; - memset(static_cast(&eq_ctx), 0, sizeof(eq_ctx)); + EdgeQueueContext eq_ctx = {}; eq_ctx.pool = nullptr; eq_ctx.bm = pbvh->header.bm; @@ -2724,7 +2723,6 @@ extern "C" bool BKE_pbvh_bmesh_update_topology(PBVH *pbvh, eq_ctx.mode = mode; #ifdef DYNTOPO_USE_MINMAX_HEAP - eq_ctx.heap_mm = BLI_mm_heap_new_ex(max_ii(DYNTOPO_MAX_ITER, custom_max_steps)); // eq_ctx.used_verts = BLI_table_gset_new(__func__); eq_ctx.max_heap_mm = DYNTOPO_MAX_ITER << 8; eq_ctx.limit_min = pbvh->bm_min_edge_len; @@ -2833,7 +2831,7 @@ extern "C" bool BKE_pbvh_bmesh_update_topology(PBVH *pbvh, SmallHash subd_edges; BLI_smallhash_init(&subd_edges); - while (totop > 0 && !BLI_mm_heap_is_empty(eq_ctx.heap_mm) && i < max_steps) { + while (totop > 0 && !eq_ctx.edge_heap.empty() && i < max_steps) { BMEdge *e = nullptr; if (count >= steps[curop]) { @@ -2851,15 +2849,15 @@ extern "C" bool BKE_pbvh_bmesh_update_topology(PBVH *pbvh, switch (ops[curop]) { case PBVH_Subdivide: { - if (BLI_mm_heap_max_value(eq_ctx.heap_mm) < limit_len_subd) { + if (eq_ctx.edge_heap.max_weight() < limit_len_subd) { break; } - e = (BMEdge *)BLI_mm_heap_pop_max(eq_ctx.heap_mm); - while (!BLI_mm_heap_is_empty(eq_ctx.heap_mm) && e && + e = eq_ctx.edge_heap.pop_max(); + while (!eq_ctx.edge_heap.empty() && e && (bm_elem_is_free((BMElem *)e, BM_EDGE) || calc_weighted_length(&eq_ctx, e->v1, e->v2, -1.0) < limit_len_subd)) { - e = (BMEdge *)BLI_mm_heap_pop_max(eq_ctx.heap_mm); + e = eq_ctx.edge_heap.pop_max(); } if (!e || bm_elem_is_free((BMElem *)e, BM_EDGE)) { @@ -2890,15 +2888,15 @@ extern "C" bool BKE_pbvh_bmesh_update_topology(PBVH *pbvh, break; } case PBVH_Collapse: { - if (BLI_mm_heap_min_value(eq_ctx.heap_mm) > limit_len_cold) { + if (eq_ctx.edge_heap.min_weight() > limit_len_cold) { break; } - e = (BMEdge *)BLI_mm_heap_pop_min(eq_ctx.heap_mm); - while (!BLI_mm_heap_is_empty(eq_ctx.heap_mm) && e && + e = eq_ctx.edge_heap.pop_min(); + while (!eq_ctx.edge_heap.empty() && e && (bm_elem_is_free((BMElem *)e, BM_EDGE) || calc_weighted_length(&eq_ctx, e->v1, e->v2, 1.0) > limit_len_cold)) { - e = (BMEdge *)BLI_mm_heap_pop_min(eq_ctx.heap_mm); + e = eq_ctx.edge_heap.pop_min(); } if (!e || bm_elem_is_free((BMElem *)e, BM_EDGE)) { @@ -2984,10 +2982,6 @@ extern "C" bool BKE_pbvh_bmesh_update_topology(PBVH *pbvh, } #ifdef DYNTOPO_USE_MINMAX_HEAP - if (eq_ctx.heap_mm) { - BLI_mm_heap_free(eq_ctx.heap_mm, nullptr); - } - if (eq_ctx.used_verts) { MEM_SAFE_FREE(eq_ctx.used_verts); // BLI_table_gset_free(eq_ctx.used_verts, nullptr); diff --git a/source/blender/blenkernel/intern/dyntopo_collapse.cc b/source/blender/blenkernel/intern/dyntopo_collapse.cc index 30c9e75b809..14fd12cc751 100644 --- a/source/blender/blenkernel/intern/dyntopo_collapse.cc +++ b/source/blender/blenkernel/intern/dyntopo_collapse.cc @@ -701,9 +701,6 @@ BMVert *pbvh_bmesh_collapse_edge(PBVH *pbvh, BLI_ghash_insert(deleted_verts, (void *)v_del, nullptr); } - BMTracer tracer; - BM_empty_tracer(&tracer, &tdata); - vert_ring_do(e->v1, e->v2, collapse_ring_callback_pre, &tdata, tag, facetag, log_rings - 1); if (!uvs_snapped) { @@ -711,8 +708,8 @@ BMVert *pbvh_bmesh_collapse_edge(PBVH *pbvh, copy_v3_v3(co, v_conn->co); - // full non-manifold collapse - BM_edge_collapse(pbvh->header.bm, e, v_del, true, true, true, true, &tracer); + /* Full non-manifold collapse. */ + BM_edge_collapse(pbvh->header.bm, e, v_del, true, true, true, true, nullptr); copy_v3_v3(v_conn->co, co); } else { @@ -721,8 +718,8 @@ BMVert *pbvh_bmesh_collapse_edge(PBVH *pbvh, add_v3_v3v3(co, v_del->co, v_conn->co); mul_v3_fl(co, 0.5f); - // full non-manifold collapse - BM_edge_collapse(pbvh->header.bm, e, v_del, true, true, true, true, &tracer); + /* Full non-manifold collapse. */ + BM_edge_collapse(pbvh->header.bm, e, v_del, true, true, true, true, nullptr); copy_v3_v3(v_conn->co, co); } @@ -741,7 +738,7 @@ BMVert *pbvh_bmesh_collapse_edge(PBVH *pbvh, validate_vert_faces(pbvh, pbvh->header.bm, v_conn, false, true); - /* Flag boundaries for update */ + /* Flag boundaries for update. */ e2 = v_conn->e; do { BMLoop *l = e2->l; diff --git a/source/blender/blenkernel/intern/dyntopo_intern.hh b/source/blender/blenkernel/intern/dyntopo_intern.hh index 25f0490ccbd..554a1bc4e35 100644 --- a/source/blender/blenkernel/intern/dyntopo_intern.hh +++ b/source/blender/blenkernel/intern/dyntopo_intern.hh @@ -2,11 +2,11 @@ #include "BKE_paint.h" #include "BKE_pbvh.h" +#include "BLI_asan.h" +#include "BLI_heap_minmax.hh" #include "bmesh.h" #include "pbvh_intern.hh" -#include "BLI_asan.h" -struct MinMaxHeap; struct GHash; struct BLI_Buffer; @@ -194,9 +194,9 @@ static void pbvh_bmesh_verify(PBVH *pbvh); struct EdgeQueue; typedef struct EdgeQueueContext { - BLI_mempool *pool; - BMesh *bm; - DyntopoMaskCB mask_cb; + BLI_mempool *pool = nullptr; + BMesh *bm = nullptr; + DyntopoMaskCB mask_cb = nullptr; void *mask_cb_data; int cd_sculpt_vert; int cd_vert_mask_offset; @@ -209,10 +209,11 @@ typedef struct EdgeQueueContext { bool local_mode; float surface_smooth_fac; - struct MinMaxHeap *heap_mm; + blender::MinMaxHeap edge_heap; + int max_heap_mm; // TableGSet *used_verts; - BMVert **used_verts; + BMVert **used_verts = nullptr; int used_verts_size; int tot_used_verts; @@ -220,7 +221,7 @@ typedef struct EdgeQueueContext { bool use_view_normal; float limit_min, limit_max, limit_mid; - const float *center; + const float *center = nullptr; float center_proj[3]; /* for when we use projected coords. */ float radius_squared; float limit_len_min; diff --git a/source/blender/blenlib/BLI_heap_minmax.hh b/source/blender/blenlib/BLI_heap_minmax.hh index 1467c675240..0169c6b82a4 100644 --- a/source/blender/blenlib/BLI_heap_minmax.hh +++ b/source/blender/blenlib/BLI_heap_minmax.hh @@ -8,45 +8,233 @@ #include "BLI_index_range.hh" #include "BLI_math.h" +#include "BLI_memory_utils.hh" #include "BLI_vector.hh" using blender::IndexRange; using blender::Vector; -namespace blender { -template class MinMaxHeapNode { - public: - private: - T *value; - float value; - int child1 = -1, child2 = -1, parent = -1; -}; +#include + +namespace blender { +template +class MinMaxHeap { + struct MinMaxHeapNode { + Value value; + float weight; + + int child1 = -1, child2 = -1, parent = -1; + }; -template class MinMaxHeap { public: MinMaxHeap(int reserved = 0) { - nodes.reserve(reserved); + if (reserved > 0) { + nodes.reserve(reserved); + } } ~MinMaxHeap() { } + MinMaxHeapNode *insert(float weight, Value value) + { + MinMaxHeapNode *node = heap_make_node(); + + node->value = value; + node->weight = weight; + + if (nodes.size() == 1) { + return node; + } + + int i = node - nodes.data(); + + node->parent = (i - 1) >> 1; + + MinMaxHeapNode *parent = &nodes[node->parent]; + if (parent->child1 == -1) { + parent->child1 = i; + } + else { + parent->child2 = i; + } + + return heap_push_up(node); + } + + void insert_or_update(MinMaxHeapNode **node_p, float weight, Value value) + { + MinMaxHeapNode *node = *node_p; + + if (!node) { + *node_p = insert(weight, value); + return; + } + + node = heap_push_down(node); + node = heap_push_up(node); + + *node_p = node; + } + + bool empty() const + { + return nodes.size() == 0; + } + + unsigned int len() const + { + return nodes.size(); + } + + float min_weight() + { + return nodes[0].weight; + } + + float max_weight() + { + return max()->weight; + } + + Value pop_min() + { + if (nodes.size() == 1) { + return nodes.pop_last().value; + } + +#ifdef BLI_MINMAX_HEAP_VALIDATE + if (!is_valid()) { + printf("invalid heap!\n"); + } +#endif + + Value ret = nodes[0].value; + MinMaxHeapNode *last = heap_pop_last(); + + std::swap(last->weight, nodes[0].weight); + std::swap(last->value, nodes[0].value); + + heap_push_down(&nodes[0]); + +#ifdef BLI_MINMAX_HEAP_VALIDATE + if (!is_valid()) { + printf("invalid heap!\n"); + } +#endif + + return ret; + } + + Value pop_max() + { + MinMaxHeapNode *node = max(); + + if (nodes.size() == 1) { + return nodes.pop_last().value; + } + +#ifdef BLI_MINMAX_HEAP_VALIDATE + if (!is_valid()) { + printf("invalid heap!\n"); + } +#endif + + Value ret = node->value; + MinMaxHeapNode *last = heap_pop_last(); + + node->weight = last->weight; + node->value = last->value; + + node = heap_push_down(node); + +#ifdef BLI_MINMAX_HEAP_VALIDATE + if (!is_valid()) { + printf("invalid heap!\n"); + } +#endif + + return ret; + } + + MinMaxHeapNode *node_weight_update(MinMaxHeapNode *node, float weight) + { + node->weight = weight; + + node = heap_push_down(node); + node = heap_push_up(node); + + return node; + } + + MinMaxHeapNode *node_weight_update_value(MinMaxHeapNode *node, float weight, Value value) + { + node->weight = weight; + node->value = value; + + node = heap_push_down(node); + node = heap_push_up(node); + + return node; + } + private: + MinMaxHeapNode *min() + { + return &nodes[0]; + } + + MinMaxHeapNode *max() + { + if (nodes.size() == 1) { + return &nodes[0]; + } + + if (nodes[0].child1 != -1) { + return &nodes[nodes[0].child1]; + } + else { + return &nodes[nodes[0].child2]; + } + } + MinMaxHeapNode *heap_make_node() { - nodes.append(MinMaxHeapNode()); + nodes.resize(nodes.size() + 1); return &nodes[nodes.size() - 1]; } + MinMaxHeapNode *heap_descent_min(MinMaxHeapNode *node) + { + if (node->child1 != -1 && node->child2 != -1) { + MinMaxHeapNode *n1 = &nodes[node->child1]; + MinMaxHeapNode *n2 = &nodes[node->child2]; + + n1 = heap_descent_min2(n1); + n2 = heap_descent_min2(n2); + + return n1->value < n2->value ? n1 : n2; + } + else if (node->child1 != -1) { + return heap_descent_min2(&nodes[node->child1]); + } + else if (node->child2 != -1) { + return heap_descent_min2(&nodes[node->child2]); + } + + return nullptr; + } + MinMaxHeapNode *heap_descent_min2(MinMaxHeapNode *n) { if (n->child1 != -1 && n->child2 != -1) { MinMaxHeapNode *n1 = &nodes[n->child1]; MinMaxHeapNode *n2 = &nodes[n->child2]; - return n1->value < n2->value ? n1 : n2; + return n1->weight < n2->weight ? n1 : n2; } else if (n->child1 != -1) { return &nodes[n->child1]; @@ -64,7 +252,7 @@ template class MinMaxHeap { MinMaxHeapNode *n1 = &nodes[n->child1]; MinMaxHeapNode *n2 = &nodes[n->child2]; - return n1->value > n2->value ? n1 : n2; + return n1->weight > n2->weight ? n1 : n2; } else if (n->child1 != -1) { return &nodes[n->child1]; @@ -83,10 +271,10 @@ template class MinMaxHeap { MinMaxHeapNode *n1 = &nodes[node->child1]; MinMaxHeapNode *n2 = &nodes[node->child2]; - n1 = heap_descent_max2(heap, n1); - n2 = heap_descent_max2(heap, n2); + n1 = heap_descent_max2(n1); + n2 = heap_descent_max2(n2); - return n1->value > n2->value ? n1 : n2; + return n1->weight > n2->weight ? n1 : n2; } else if (node->child1 != -1) { return heap_descent_max2(&nodes[node->child1]); @@ -95,9 +283,204 @@ template class MinMaxHeap { return heap_descent_max2(&nodes[node->child2]); } - return NULL; + return nullptr; } - Vector> nodes; + MinMaxHeapNode *heap_push_down_min(MinMaxHeapNode *node) + { + MinMaxHeapNode *ret = nullptr; + + while (node->child1 >= 0 || node->child2 >= 0) { + MinMaxHeapNode *node2 = heap_descent_min(node); + + if (!node2) { + break; + } + + if (node2->weight < node->weight) { + std::swap(node2->weight, node->weight); + std::swap(node2->value, node->value); + + if (node2->parent != node - nodes.data()) { + MinMaxHeapNode *parent = &nodes[node2->parent]; + + if (node2->weight > parent->weight) { + std::swap(node2->weight, parent->weight); + std::swap(node2->value, parent->value); + + /* this is a bit tricky, our return node has now + moved into the other interleaved heap side */ + if (!ret) { + ret = parent; + } + } + } + + node = node2; + } + else { + break; + } + } + + return ret ? ret : node; + } + + MinMaxHeapNode *heap_push_down_max(MinMaxHeapNode *node) + { + MinMaxHeapNode *ret = nullptr; + + while (node->child1 >= 0 || node->child2 >= 0) { + MinMaxHeapNode *node2 = heap_descent_max(node); + + if (node2->weight > node->weight) { + std::swap(node2->weight, node->weight); + std::swap(node2->value, node->value); + + if (node2->parent != node - nodes.data()) { + MinMaxHeapNode *parent = &nodes[node2->parent]; + + if (node2->weight < parent->weight) { + std::swap(node2->weight, parent->weight); + std::swap(node2->value, parent->value); + + /* this is a bit tricky, our return node has now + moved into the other interleaved heap side */ + if (!ret) { + ret = parent; + } + } + } + + node = node2; + } + else { + break; + } + } + + return ret ? ret : node; + } + + int heap_get_level(const MinMaxHeapNode *node) + { + int i = 0; + + while (node->parent != -1) { + node = &nodes[node->parent]; + i++; + } + + return i; + } + + MinMaxHeapNode *heap_push_down(MinMaxHeapNode *node) + { + int i = heap_get_level(node); + + if (i & 1) { + return heap_push_down_max(node); + } + else { + return heap_push_down_min(node); + } + } + + MinMaxHeapNode *heap_push_up_min(MinMaxHeapNode *node) + { + while (node->parent != -1) { + MinMaxHeapNode *parent = &nodes[node->parent]; + + if (parent->parent != -1 && node->weight < nodes[parent->parent].weight) { + parent = &nodes[parent->parent]; + + std::swap(node->weight, parent->weight); + std::swap(node->value, parent->value); + + node = parent; + } + else { + break; + } + } + + return node; + } + + MinMaxHeapNode *heap_push_up_max(MinMaxHeapNode *node) + { + while (node->parent != -1) { + MinMaxHeapNode *parent = &nodes[node->parent]; + + if (parent->parent != -1 && node->weight > nodes[parent->parent].weight) { + parent = &nodes[parent->parent]; + + std::swap(node->weight, parent->weight); + std::swap(node->value, parent->value); + + node = parent; + } + else { + break; + } + } + + return node; + } + + MinMaxHeapNode *heap_push_up(MinMaxHeapNode *node) + { + int i = heap_get_level(node); + + if ((i & 1) == 0) { + MinMaxHeapNode *parent = &nodes[node->parent]; + + if (node->weight > parent->weight) { + std::swap(node->weight, parent->weight); + std::swap(node->value, parent->value); + + return heap_push_up_max(parent); + } + else { + return heap_push_up_min(node); + } + } + else { + MinMaxHeapNode *parent = &nodes[node->parent]; + + if (node->weight < parent->weight) { + std::swap(node->weight, parent->weight); + std::swap(node->value, parent->value); + + return heap_push_up_min(parent); + } + else { + return heap_push_up_max(node); + } + } + + return node; + } + + MinMaxHeapNode *heap_pop_last() + { + MinMaxHeapNode *last = &nodes[nodes.size() - 1]; + + if (last->parent) { + MinMaxHeapNode *parent = &nodes[last->parent]; + if (parent->child1 == nodes.size() - 1) { + parent->child1 = -1; + } + else { + parent->child2 = -1; + } + } + + nodes.pop_last(); + + return last; + } + + Vector nodes; }; } // namespace blender diff --git a/source/blender/blenlib/CMakeLists.txt b/source/blender/blenlib/CMakeLists.txt index 16d6f80cd8d..185b4d2b6e7 100644 --- a/source/blender/blenlib/CMakeLists.txt +++ b/source/blender/blenlib/CMakeLists.txt @@ -82,7 +82,6 @@ set(SRC intern/hash_md5.c intern/hash_mm2a.c intern/hash_mm3.c - intern/heap_minmax.c intern/index_mask.cc intern/jitter_2d.c intern/kdtree_1d.c @@ -243,7 +242,7 @@ set(SRC BLI_hash_tables.hh BLI_heap.h BLI_heap_simple.h - BLI_heap_minmax.h + BLI_heap_minmax.hh BLI_index_mask.hh BLI_index_mask_ops.hh BLI_index_range.hh -- 2.30.2 From f071a4bb04f0a4043fa81fc45e342a9d2edfd0a9 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Thu, 16 Mar 2023 16:51:13 -0700 Subject: [PATCH 034/279] temp-sculpt-dyntopo: Bring back improved topology rake --- .../startup/bl_ui/properties_paint_common.py | 1 + scripts/startup/bl_ui/space_view3d_toolbar.py | 1 + .../sculpt_paint/sculpt_brush_types.cc | 49 +++- .../editors/sculpt_paint/sculpt_intern.hh | 4 +- .../editors/sculpt_paint/sculpt_smooth.cc | 265 ++++++++++++++++-- 5 files changed, 291 insertions(+), 29 deletions(-) diff --git a/scripts/startup/bl_ui/properties_paint_common.py b/scripts/startup/bl_ui/properties_paint_common.py index f29884572ea..adc6fa9e31e 100644 --- a/scripts/startup/bl_ui/properties_paint_common.py +++ b/scripts/startup/bl_ui/properties_paint_common.py @@ -572,6 +572,7 @@ def brush_settings(layout, context, brush, popover=False): context.sculpt_object.use_dynamic_topology_sculpting ): layout.prop(brush, "topology_rake_factor", slider=True) + layout.prop(brush, "use_curvature_rake") # normal_weight if capabilities.has_normal_weight: diff --git a/scripts/startup/bl_ui/space_view3d_toolbar.py b/scripts/startup/bl_ui/space_view3d_toolbar.py index 04b5d6cc866..f0a837d8a5e 100644 --- a/scripts/startup/bl_ui/space_view3d_toolbar.py +++ b/scripts/startup/bl_ui/space_view3d_toolbar.py @@ -954,6 +954,7 @@ class VIEW3D_PT_sculpt_options(Panel, View3DPaintPanel): col.prop(sculpt, "use_sculpt_delay_updates") col.prop(sculpt, "use_deform_only") col.prop(tool_settings, "show_origco") + col.prop(context.object.data, "sculpt_ignore_uvs") class VIEW3D_PT_sculpt_options_gravity(Panel, View3DPaintPanel): diff --git a/source/blender/editors/sculpt_paint/sculpt_brush_types.cc b/source/blender/editors/sculpt_paint/sculpt_brush_types.cc index c0350e2837b..16e29f4d5e1 100644 --- a/source/blender/editors/sculpt_paint/sculpt_brush_types.cc +++ b/source/blender/editors/sculpt_paint/sculpt_brush_types.cc @@ -2822,6 +2822,9 @@ static void do_topology_rake_bmesh_task_cb_ex(void *__restrict userdata, Sculpt *sd = data->sd; const Brush *brush = data->brush; + const bool use_curvature = brush->flag2 & BRUSH_CURVATURE_RAKE; + const bool do_reproject = SCULPT_need_reproject(ss); + float direction[3]; copy_v3_v3(direction, ss->cache->grab_delta_symmetry); @@ -2831,11 +2834,6 @@ static void do_topology_rake_bmesh_task_cb_ex(void *__restrict userdata, sub_v3_v3(direction, tmp); normalize_v3(direction); - /* Cancel if there's no grab data. */ - if (is_zero_v3(direction)) { - return; - } - const float bstrength = clamp_f(data->strength, 0.0f, 1.0f); SculptBrushTest test; @@ -2847,8 +2845,25 @@ static void do_topology_rake_bmesh_task_cb_ex(void *__restrict userdata, SCULPT_automasking_node_begin( data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]); + if (use_curvature) { + SCULPT_curvature_begin(ss, data->nodes[n], false); + } + PBVHVertexIter vd; BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { + float direction2[3]; + + if (use_curvature) { + SCULPT_curvature_dir_get(ss, vd.vertex, direction2, false); + } + else { + copy_v3_v3(direction2, direction); + } + + if (is_zero_v3(direction2)) { + continue; + } + if (!sculpt_brush_test_sq_fn(&test, vd.co)) { continue; } @@ -2867,11 +2882,16 @@ static void do_topology_rake_bmesh_task_cb_ex(void *__restrict userdata, &automask_data) * ss->cache->pressure; + float oldco[3]; + float oldno[3]; + copy_v3_v3(oldco, vd.co); + SCULPT_vertex_normal_get(ss, vd.vertex, oldno); + float avg[3], val[3]; int cd_temp = data->scl->bmesh_cd_offset; SCULPT_bmesh_four_neighbor_average( - ss, avg, direction, vd.bm_vert, 1.0f, true, cd_temp, ss->cd_sculpt_vert, false); + ss, avg, direction2, vd.bm_vert, 1.0f, true, cd_temp, ss->cd_sculpt_vert, false); sub_v3_v3v3(val, avg, vd.co); @@ -2879,6 +2899,10 @@ static void do_topology_rake_bmesh_task_cb_ex(void *__restrict userdata, SCULPT_clip(sd, ss, vd.co, val); + if (do_reproject) { + SCULPT_reproject_cdata(ss, vd.vertex, oldco, oldno); + } + if (vd.is_mesh) { BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); } @@ -2900,9 +2924,16 @@ void SCULPT_bmesh_topology_rake( const int count = iterations * strength + 1; const float factor = iterations * strength / count; - SculptAttributeParams params = {}; - ss->attrs.rake_temp = BKE_sculpt_attribute_ensure( - ob, ATTR_DOMAIN_POINT, CD_PROP_COLOR, SCULPT_ATTRIBUTE_NAME(rake_temp), ¶ms); + if (!ss->attrs.rake_temp) { + SculptAttributeParams params = {}; + ss->attrs.rake_temp = BKE_sculpt_attribute_ensure( + ob, ATTR_DOMAIN_POINT, CD_PROP_COLOR, SCULPT_ATTRIBUTE_NAME(rake_temp), ¶ms); + } + + if (SCULPT_stroke_is_first_brush_step(ss->cache) && + (ss->cache->brush->flag2 & BRUSH_SMOOTH_USE_AREA_WEIGHT)) { + BKE_pbvh_update_all_tri_areas(ss->pbvh); + } for (iteration = 0; iteration <= count; iteration++) { diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.hh b/source/blender/editors/sculpt_paint/sculpt_intern.hh index b26d78a9f89..d8b55ff1aa8 100644 --- a/source/blender/editors/sculpt_paint/sculpt_intern.hh +++ b/source/blender/editors/sculpt_paint/sculpt_intern.hh @@ -1894,9 +1894,9 @@ BLI_INLINE bool SCULPT_get_fset_projection(SculptSession *ss, float fset_project return fset_projection; } -BLI_INLINE bool SCULPT_need_reproject(SculptSession *ss) +BLI_INLINE bool SCULPT_need_reproject(const SculptSession *ss) { - return ss->bm && CustomData_has_layer(&ss->bm->ldata, CD_MLOOPUV); + return !ss->ignore_uvs && ss->bm && CustomData_has_layer(&ss->bm->ldata, CD_MLOOPUV); } void SCULPT_reproject_cdata(SculptSession *ss, diff --git a/source/blender/editors/sculpt_paint/sculpt_smooth.cc b/source/blender/editors/sculpt_paint/sculpt_smooth.cc index 8e65f4cbe18..53c1ef9dafb 100644 --- a/source/blender/editors/sculpt_paint/sculpt_smooth.cc +++ b/source/blender/editors/sculpt_paint/sculpt_smooth.cc @@ -117,40 +117,221 @@ void SCULPT_neighbor_coords_average_interior(SculptSession *ss, ss, result, vertex, projection, fset_projection, use_area_weights, bound_type, corner_type); } +int closest_vec_to_perp(float dir[3], float r_dir2[3], float no[3], float *buckets, float w) +{ + int bits = 0; + + if (dot_v3v3(r_dir2, dir) < 0.0f) { + negate_v3(r_dir2); + bits |= 1; + } + + float dir4[3]; + cross_v3_v3v3(dir4, r_dir2, no); + normalize_v3(dir4); + + if (dot_v3v3(dir4, dir) < 0.0f) { + negate_v3(dir4); + bits |= 2; + } + + if (dot_v3v3(dir4, dir) > dot_v3v3(r_dir2, dir)) { + copy_v3_v3(r_dir2, dir4); + bits |= 4; + } + + buckets[bits] += w; + + return bits; +} + +void vec_transform(float r_dir2[3], float no[3], int bits) +{ + if (bits & 4) { + float dir4[3]; + + copy_v3_v3(dir4, r_dir2); + + if (bits & 2) { + negate_v3(dir4); + } + + float dir5[3]; + + cross_v3_v3v3(dir5, no, dir4); + normalize_v3(dir5); + + copy_v3_v3(r_dir2, dir5); + } + + if (bits & 1) { + negate_v3(r_dir2); + } +} + void SCULPT_bmesh_four_neighbor_average(SculptSession *ss, float avg[3], float direction[3], - struct BMVert *v, + BMVert *v, float projection, bool check_fsets, int cd_temp, int cd_sculpt_vert, bool do_origco) { - float avg_co[3] = {0.0f, 0.0f, 0.0f}; float tot_co = 0.0f; + float buckets[8] = {0}; + + // zero_v3(direction); + + MSculptVert *mv = BKE_PBVH_SCULPTVERT(cd_sculpt_vert, v); + + float *col = BM_ELEM_CD_PTR(v, cd_temp); + float dir[3]; + float dir3[3] = {0.0f, 0.0f, 0.0f}; + + const bool weighted = (ss->cache->brush->flag2 & BRUSH_SMOOTH_USE_AREA_WEIGHT); + float *areas; + + SCULPT_vertex_check_origdata(ss, BKE_pbvh_make_vref(intptr_t(v))); + + if (do_origco) { + // SCULPT_vertex_check_origdata(ss, (PBVHVertRef){.i = (intptr_t)v}); + madd_v3_v3fl(direction, mv->origno, -dot_v3v3(mv->origno, direction)); + normalize_v3(direction); + } + + float *co1 = do_origco ? mv->origco : v->co; + float *no1 = do_origco ? mv->origno : v->no; + + if (weighted) { + PBVHVertRef vertex = {(intptr_t)v}; + + int val = SCULPT_vertex_valence_get(ss, vertex); + areas = BLI_array_alloca(areas, val * 2); + + BKE_pbvh_get_vert_face_areas(ss->pbvh, vertex, areas, val); + float totarea = 0.0f; + + for (int i = 0; i < val; i++) { + totarea += areas[i]; + } + + totarea = totarea != 0.0f ? 1.0f / totarea : 0.0f; + + for (int i = 0; i < val; i++) { + areas[i] *= totarea; + } + } + + copy_v3_v3(dir, col); + + if (dot_v3v3(dir, dir) == 0.0f) { + copy_v3_v3(dir, direction); + } + else { + closest_vec_to_perp(dir, direction, no1, buckets, 1.0f); // col[3]); + } + + float totdir3 = 0.0f; + + const float selfw = (float)mv->valence * 0.0025f; + madd_v3_v3fl(dir3, direction, selfw); + + totdir3 += selfw; + BMIter eiter; BMEdge *e; + bool had_bound = false; + int area_i = 0; - BM_ITER_ELEM (e, &eiter, v, BM_EDGES_OF_VERT) { - if (BM_edge_is_boundary(e)) { - copy_v3_v3(avg, v->co); - return; - } + BM_ITER_ELEM_INDEX (e, &eiter, v, BM_EDGES_OF_VERT, area_i) { BMVert *v_other = (e->v1 == v) ? e->v2 : e->v1; + + float dir2[3]; + float *col2 = BM_ELEM_CD_PTR(v_other, cd_temp); + + float bucketw = 1.0f; + + MSculptVert *mv2 = BKE_PBVH_SCULPTVERT(cd_sculpt_vert, v_other); + float *co2; + + if (!do_origco || mv2->stroke_id != ss->stroke_id) { + co2 = v_other->co; + } + else { + co2 = mv2->origco; + } + + eSculptBoundary bflag = SCULPT_BOUNDARY_FACE_SET | SCULPT_BOUNDARY_MESH | + SCULPT_BOUNDARY_SHARP | SCULPT_BOUNDARY_SEAM | SCULPT_BOUNDARY_UV; + + int bound = SCULPT_edge_is_boundary(ss, BKE_pbvh_make_eref(intptr_t(e)), bflag); + float dirw = 1.0f; + + if (bound) { + had_bound = true; + + sub_v3_v3v3(dir2, co2, co1); + madd_v3_v3fl(dir2, no1, -dot_v3v3(no1, dir2)); + normalize_v3(dir2); + dirw = 100000.0f; + } + else { + dirw = col2[3]; + + copy_v3_v3(dir2, col2); + if (dot_v3v3(dir2, dir2) == 0.0f) { + copy_v3_v3(dir2, dir); + } + } + + closest_vec_to_perp(dir, dir2, no1, buckets, bucketw); // col2[3]); + + madd_v3_v3fl(dir3, dir2, dirw); + totdir3 += dirw; + + if (had_bound) { + tot_co = 0.0f; + continue; + } float vec[3]; - sub_v3_v3v3(vec, v_other->co, v->co); - madd_v3_v3fl(vec, v->no, -dot_v3v3(vec, v->no)); + sub_v3_v3v3(vec, co2, co1); + + madd_v3_v3fl(vec, no1, -dot_v3v3(vec, no1) * projection); normalize_v3(vec); /* fac is a measure of how orthogonal or parallel the edge is * relative to the direction. */ - float fac = dot_v3v3(vec, direction); + float fac = dot_v3v3(vec, dir); +#ifdef SCULPT_DIAGONAL_EDGE_MARKS + float th = fabsf(saacos(fac)) / M_PI + 0.5f; + th -= floorf(th); + + const float limit = 0.045; + + if (fabsf(th - 0.25) < limit || fabsf(th - 0.75) < limit) { + BMEdge enew = *e, eold = *e; + + enew.head.hflag &= ~BM_ELEM_DRAW; + // enew.head.hflag |= BM_ELEM_SEAM; // XXX debug + + atomic_cas_int64((intptr_t *)(&e->head.index), + *(intptr_t *)(&eold.head.index), + *(intptr_t *)(&enew.head.index)); + } +#endif + fac = fac * fac - 0.5f; fac *= fac; - madd_v3_v3fl(avg_co, v_other->co, fac); + + if (weighted) { + fac *= areas[area_i]; + } + + madd_v3_v3fl(avg_co, co2, fac); tot_co += fac; } @@ -160,13 +341,51 @@ void SCULPT_bmesh_four_neighbor_average(SculptSession *ss, /* Preserve volume. */ float vec[3]; - sub_v3_v3(avg, v->co); - mul_v3_v3fl(vec, v->no, dot_v3v3(avg, v->no)); + sub_v3_v3(avg, co1); + mul_v3_v3fl(vec, no1, dot_v3v3(avg, no1) * projection); sub_v3_v3(avg, vec); - add_v3_v3(avg, v->co); + add_v3_v3(avg, co1); } else { - zero_v3(avg); + // zero_v3(avg); + copy_v3_v3(avg, co1); + } + + PBVH_CHECK_NAN(avg); + + // do not update in do_origco + if (do_origco) { + return; + } + + if (totdir3 > 0.0f) { + float outdir = totdir3 / (float)mv->valence; + + // mul_v3_fl(dir3, 1.0 / totdir3); + normalize_v3(dir3); + if (had_bound) { + copy_v3_v3(col, dir3); + col[3] = 1000.0f; + } + else { + + mul_v3_fl(col, col[3]); + madd_v3_v3fl(col, dir3, outdir); + + col[3] = (col[3] + outdir) * 0.4; + normalize_v3(col); + } + + float maxb = 0.0f; + int bi = 0; + for (int i = 0; i < 8; i++) { + if (buckets[i] > maxb) { + maxb = buckets[i]; + bi = i; + } + } + + vec_transform(col, no1, bi); } } @@ -344,6 +563,7 @@ ATTR_NO_OPT static void do_smooth_brush_task_cb_ex(void *__restrict userdata, float bstrength = data->strength; PBVHVertexIter vd; + const bool do_reproject = SCULPT_need_reproject(ss); CLAMP(bstrength, 0.0f, 1.0f); @@ -387,12 +607,22 @@ ATTR_NO_OPT static void do_smooth_brush_task_cb_ex(void *__restrict userdata, CLAMP(*vd.mask, 0.0f, 1.0f); } else { + float oldco[3]; + float oldno[3]; + copy_v3_v3(oldco, vd.co); + SCULPT_vertex_normal_get(ss, vd.vertex, oldno); + float avg[3], val[3]; SCULPT_neighbor_coords_average_interior( ss, avg, vd.vertex, projection, fset_projection, weighted); sub_v3_v3v3(val, avg, vd.co); madd_v3_v3v3fl(val, vd.co, val, fade); SCULPT_clip(sd, ss, vd.co, val); + + if (do_reproject) { + SCULPT_reproject_cdata(ss, vd.vertex, oldco, oldno); + } + if (vd.is_mesh) { BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); } @@ -831,9 +1061,8 @@ void SCULPT_reproject_cdata(SculptSession *ss, } #endif - // build fake f with original coordinates + /* Build fake face with original coordinates. */ for (int i = 0; i < totloop; i++) { - // create fake face BMLoop *l = ls[i]; float no[3] = {0.0f, 0.0f, 0.0f}; @@ -852,7 +1081,7 @@ void SCULPT_reproject_cdata(SculptSession *ss, *fakev = *l2->v; fakel->v = fakev; - SCULPT_vertex_check_origdata(ss, BKE_pbvh_make_vref((intptr_t)l2->v)); + SCULPT_vertex_check_origdata(ss, BKE_pbvh_make_vref(intptr_t(l2->v))); if (l2->v == v) { copy_v3_v3(fakev->co, origco); -- 2.30.2 From 51e6f898d19e1293a75dc2c8331f9d6725805e38 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Thu, 16 Mar 2023 17:14:44 -0700 Subject: [PATCH 035/279] temp-sculpt-dyntopo: Bring back custom dyntopo spacing --- scripts/startup/bl_ui/space_view3d_toolbar.py | 1 + source/blender/editors/sculpt_paint/sculpt.cc | 22 +++++++++++++++++-- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/scripts/startup/bl_ui/space_view3d_toolbar.py b/scripts/startup/bl_ui/space_view3d_toolbar.py index f0a837d8a5e..b425a246111 100644 --- a/scripts/startup/bl_ui/space_view3d_toolbar.py +++ b/scripts/startup/bl_ui/space_view3d_toolbar.py @@ -892,6 +892,7 @@ class VIEW3D_PT_sculpt_dyntopo(Panel, View3DPaintPanel): if sculpt.detail_type_method in {'CONSTANT', 'MANUAL'}: col.operator("sculpt.detail_flood_fill") + col.prop(sculpt, "dyntopo_spacing") col.prop(sculpt, "use_smooth_shading") col.prop(sculpt, "use_flat_vcol_shading") diff --git a/source/blender/editors/sculpt_paint/sculpt.cc b/source/blender/editors/sculpt_paint/sculpt.cc index 7060f2ebd20..fb17177c56e 100644 --- a/source/blender/editors/sculpt_paint/sculpt.cc +++ b/source/blender/editors/sculpt_paint/sculpt.cc @@ -6476,12 +6476,23 @@ static void sculpt_stroke_update_step(bContext *C, const Brush *brush = BKE_paint_brush(&sd->paint); ToolSettings *tool_settings = CTX_data_tool_settings(C); StrokeCache *cache = ss->cache; - cache->stroke_distance = paint_stroke_distance_get(stroke); + + float stroke_distance = paint_stroke_distance_get(stroke); + float stroke_delta = stroke_distance - cache->stroke_distance; + cache->stroke_distance = stroke_distance; SCULPT_stroke_modifiers_check(C, ob, brush); sculpt_update_cache_variants(C, sd, ob, itemptr); sculpt_restore_mesh(sd, ob); + if (brush->flag & BRUSH_SCENE_SPACING) { + stroke_delta /= ss->cache->radius; + } + else { + stroke_delta /= ups->pixel_radius; + } + cache->stroke_distance_t += stroke_delta; + if (sd->flags & (SCULPT_DYNTOPO_DETAIL_CONSTANT | SCULPT_DYNTOPO_DETAIL_MANUAL)) { float object_space_constant_detail = 1.0f / (sd->constant_detail * mat4_to_scale(ob->object_to_world)); @@ -6498,7 +6509,14 @@ static void sculpt_stroke_update_step(bContext *C, 0.4f); } - if (SCULPT_stroke_is_dynamic_topology(ss, brush)) { + float dyntopo_spacing = float(sd->dyntopo_spacing) / 50.0f; + + bool do_dyntopo = SCULPT_stroke_is_dynamic_topology(ss, brush); + do_dyntopo = do_dyntopo && + (ss->cache->stroke_distance_t - ss->cache->last_dyntopo_t) > dyntopo_spacing; + + if (do_dyntopo) { + ss->cache->last_dyntopo_t = ss->cache->stroke_distance_t; do_symmetrical_brush_actions(sd, ob, sculpt_topology_update, ups, &tool_settings->paint_mode); } -- 2.30.2 From 8ee94a85820751d08fc845fdb0836d0573919f14 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Sat, 18 Mar 2023 15:15:39 -0700 Subject: [PATCH 036/279] temp-sculpt-dyntopo: Cleanup code --- source/blender/blenkernel/intern/dyntopo.cc | 502 +---------- .../blenkernel/intern/dyntopo_intern.hh | 9 +- .../blender/blenkernel/intern/pbvh_bmesh.cc | 839 ++++++++++-------- 3 files changed, 491 insertions(+), 859 deletions(-) diff --git a/source/blender/blenkernel/intern/dyntopo.cc b/source/blender/blenkernel/intern/dyntopo.cc index 517be43bc0f..128c09ed246 100644 --- a/source/blender/blenkernel/intern/dyntopo.cc +++ b/source/blender/blenkernel/intern/dyntopo.cc @@ -174,12 +174,12 @@ BLI_INLINE void surface_smooth_v_safe(PBVH *pbvh, BMVert *v, float fac) volatile float x = v->co[0], y = v->co[1], z = v->co[2]; volatile float nx = x + co[0] * fac, ny = y + co[1] * fac, nz = z + co[2] * fac; - // conflicts here should be pretty rare. + /* Conflicts here should be pretty rare. */ atomic_cas_float(&v->co[0], x, nx); atomic_cas_float(&v->co[1], y, ny); atomic_cas_float(&v->co[2], z, nz); - // conflicts here should be pretty rare. + /* Conflicts here should be pretty rare. */ x = mv1->origco[0]; y = mv1->origco[1]; z = mv1->origco[2]; @@ -197,504 +197,6 @@ BLI_INLINE void surface_smooth_v_safe(PBVH *pbvh, BMVert *v, float fac) // atomic_cas_int32(&mv1->stroke_id, stroke_id, pbvh->stroke_id); } -static void pbvh_kill_vert(PBVH *pbvh, BMVert *v, bool log_vert, bool log_edges) -{ - BMEdge *e = v->e; - bm_logstack_push(); - - if (log_edges) { - if (e) { - do { - BM_log_edge_removed(pbvh->header.bm, pbvh->bm_log, e); - } while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e); - } - } - - if (log_vert) { - BM_log_vert_removed(pbvh->header.bm, pbvh->bm_log, v); - } - - BM_idmap_release(pbvh->bm_idmap, (BMElem *)v, true); - BM_vert_kill(pbvh->header.bm, v); - bm_logstack_pop(); -} - -/** - * A version of #BM_face_exists, optimized for triangles - * when we know the loop and the opposite vertex. - * - * Check if any triangle is formed by (l_radial_first->v, l_radial_first->next->v, v_opposite), - * at either winding (since its a triangle no special checks are needed). - * - *
- * l_radial_first->v & l_radial_first->next->v
- * +---+
- * |  /
- * | /
- * + v_opposite
- * 
- * - * Its assumed that \a l_radial_first is never forming the target face. - */ -static BMFace *bm_face_exists_tri_from_loop_vert(BMLoop *l_radial_first, BMVert *v_opposite) -{ - BLI_assert( - !ELEM(v_opposite, l_radial_first->v, l_radial_first->next->v, l_radial_first->prev->v)); - if (l_radial_first->radial_next != l_radial_first) { - BMLoop *l_radial_iter = l_radial_first->radial_next; - do { - BLI_assert(l_radial_iter->f->len == 3); - if (l_radial_iter->prev->v == v_opposite) { - return l_radial_iter->f; - } - } while ((l_radial_iter = l_radial_iter->radial_next) != l_radial_first); - } - return nullptr; -} - -static BMVert *pbvh_bmesh_vert_create(PBVH *pbvh, - int node_index, - const float co[3], - const float no[3], - BMVert *v_example, - const int cd_vert_mask_offset) -{ - PBVHNode *node = &pbvh->nodes[node_index]; - - BLI_assert((pbvh->totnode == 1 || node_index) && node_index <= pbvh->totnode); - - /* avoid initializing customdata because its quite involved */ - BMVert *v = BM_vert_create(pbvh->header.bm, co, nullptr, BM_CREATE_NOP); - MSculptVert *mv = BKE_PBVH_SCULPTVERT(pbvh->cd_sculpt_vert, v); - - pbvh_boundary_update_bmesh(pbvh, v); - MV_ADD_FLAG(mv, SCULPTVERT_NEED_DISK_SORT | SCULPTVERT_NEED_VALENCE); - - if (v_example) { - v->head.hflag = v_example->head.hflag; - - CustomData_bmesh_copy_data( - &pbvh->header.bm->vdata, &pbvh->header.bm->vdata, v_example->head.data, &v->head.data); - - /* This value is logged below */ - copy_v3_v3(v->no, no); - - // keep MSculptVert copied from v_example as-is - } - else { - MSculptVert *mv = BKE_PBVH_SCULPTVERT(pbvh->cd_sculpt_vert, v); - - copy_v3_v3(mv->origco, co); - copy_v3_v3(mv->origno, no); - mv->origmask = 0.0f; - - /* This value is logged below */ - copy_v3_v3(v->no, no); - } - - BLI_table_gset_insert(node->bm_unique_verts, v); - BM_ELEM_CD_SET_INT(v, pbvh->cd_vert_node_offset, node_index); - - node->flag |= PBVH_UpdateDrawBuffers | PBVH_UpdateBB | PBVH_UpdateTris | PBVH_UpdateOtherVerts; - - /* Log the new vertex */ - BM_log_vert_added(pbvh->header.bm, pbvh->bm_log, v); - v->head.index = pbvh->header.bm->totvert; // set provisional index - - return v; -} - -static BMFace *bmesh_face_create_edge_log(PBVH *pbvh, - BMVert *v_tri[3], - BMEdge *e_tri[3], - const BMFace *f_example) -{ - BMFace *f; - - if (!e_tri) { - BMEdge *e_tri2[3]; - - for (int i = 0; i < 3; i++) { - BMVert *v1 = v_tri[i]; - BMVert *v2 = v_tri[(i + 1) % 3]; - - BMEdge *e = BM_edge_exists(v1, v2); - - if (!e) { - e = BM_edge_create(pbvh->header.bm, v1, v2, nullptr, BM_CREATE_NOP); - BM_log_edge_added(pbvh->header.bm, pbvh->bm_log, e); - } - - e_tri2[i] = e; - } - - // f = BM_face_create_verts(pbvh->header.bm, v_tri, 3, f_example, BM_CREATE_NOP, true); - f = BM_face_create(pbvh->header.bm, v_tri, e_tri2, 3, f_example, BM_CREATE_NOP); - } - else { - f = BM_face_create(pbvh->header.bm, v_tri, e_tri, 3, f_example, BM_CREATE_NOP); - } - - if (f_example) { - f->head.hflag = f_example->head.hflag; - } - - return f; -} - -/** - * \note Callers are responsible for checking if the face exists before adding. - */ -static BMFace *pbvh_bmesh_face_create(PBVH *pbvh, - int node_index, - BMVert *v_tri[3], - BMEdge *e_tri[3], - const BMFace *f_example, - bool ensure_verts, - bool log_face) -{ - PBVHNode *node = &pbvh->nodes[node_index]; - - /* ensure we never add existing face */ - BLI_assert(!BM_face_exists(v_tri, 3)); - - BMFace *f = bmesh_face_create_edge_log(pbvh, v_tri, e_tri, f_example); - - BLI_table_gset_insert(node->bm_faces, f); - BM_ELEM_CD_SET_INT(f, pbvh->cd_face_node_offset, node_index); - - /* mark node for update */ - node->flag |= PBVH_UpdateDrawBuffers | PBVH_UpdateNormals | PBVH_UpdateTris | - PBVH_UpdateOtherVerts; - node->flag &= ~PBVH_FullyHidden; - - /* Log the new face */ - if (log_face) { - BM_log_face_added(pbvh->header.bm, pbvh->bm_log, f); - } - - int cd_vert_node = pbvh->cd_vert_node_offset; - - if (ensure_verts) { - BMLoop *l = f->l_first; - do { - int ni = BM_ELEM_CD_GET_INT(l->v, cd_vert_node); - - if (ni == DYNTOPO_NODE_NONE) { - BLI_table_gset_add(node->bm_unique_verts, l->v); - BM_ELEM_CD_SET_INT(l->v, cd_vert_node, node_index); - - node->flag |= PBVH_UpdateDrawBuffers | PBVH_UpdateBB | PBVH_UpdateTris | - PBVH_UpdateOtherVerts; - } - - pbvh_boundary_update_bmesh(pbvh, l->v); - - MSculptVert *mv = BKE_PBVH_SCULPTVERT(pbvh->cd_sculpt_vert, l->v); - MV_ADD_FLAG(mv, SCULPTVERT_NEED_DISK_SORT | SCULPTVERT_NEED_VALENCE); - - l = l->next; - } while (l != f->l_first); - } - else { - BMLoop *l = f->l_first; - do { - pbvh_boundary_update_bmesh(pbvh, l->v); - - MSculptVert *mv = BKE_PBVH_SCULPTVERT(pbvh->cd_sculpt_vert, l->v); - MV_ADD_FLAG(mv, SCULPTVERT_NEED_DISK_SORT | SCULPTVERT_NEED_VALENCE); - } while ((l = l->next) != f->l_first); - } - - return f; -} - -BMVert *BKE_pbvh_vert_create_bmesh( - PBVH *pbvh, float co[3], float no[3], PBVHNode *node, BMVert *v_example) -{ - if (!node) { - for (int i = 0; i < pbvh->totnode; i++) { - PBVHNode *node2 = pbvh->nodes + i; - - if (!(node2->flag & PBVH_Leaf)) { - continue; - } - - // ensure we have at least some node somewhere picked - node = node2; - - bool ok = true; - - for (int j = 0; j < 3; j++) { - if (co[j] < node2->vb.bmin[j] || co[j] >= node2->vb.bmax[j]) { - continue; - } - } - - if (ok) { - break; - } - } - } - - BMVert *v; - - if (!node) { - printf("possible pbvh error\n"); - v = BM_vert_create(pbvh->header.bm, co, v_example, BM_CREATE_NOP); - BM_ELEM_CD_SET_INT(v, pbvh->cd_vert_node_offset, DYNTOPO_NODE_NONE); - - pbvh_boundary_update_bmesh(pbvh, v); - MSculptVert *mv = (MSculptVert *)BM_ELEM_CD_GET_VOID_P(v, pbvh->cd_sculpt_vert); - MV_ADD_FLAG(mv, SCULPTVERT_NEED_VALENCE | SCULPTVERT_NEED_DISK_SORT); - - copy_v3_v3(mv->origco, co); - - return v; - } - - return pbvh_bmesh_vert_create( - pbvh, node - pbvh->nodes, co, no, v_example, pbvh->cd_vert_mask_offset); -} - -PBVHNode *BKE_pbvh_node_from_face_bmesh(PBVH *pbvh, BMFace *f) -{ - return pbvh->nodes + BM_ELEM_CD_GET_INT(f, pbvh->cd_face_node_offset); -} - -BMFace *BKE_pbvh_face_create_bmesh(PBVH *pbvh, - BMVert *v_tri[3], - BMEdge *e_tri[3], - const BMFace *f_example) -{ - int ni = DYNTOPO_NODE_NONE; - - for (int i = 0; i < 3; i++) { - BMVert *v = v_tri[i]; - BMLoop *l; - BMIter iter; - - BM_ITER_ELEM (l, &iter, v, BM_LOOPS_OF_VERT) { - int ni2 = BM_ELEM_CD_GET_INT(l->f, pbvh->cd_face_node_offset); - if (ni2 != DYNTOPO_NODE_NONE) { - ni = ni2; - break; - } - } - } - - if (ni == DYNTOPO_NODE_NONE) { - BMFace *f; - - // no existing nodes? find one - for (int i = 0; i < pbvh->totnode; i++) { - PBVHNode *node = pbvh->nodes + i; - - if (!(node->flag & PBVH_Leaf)) { - continue; - } - - for (int j = 0; j < 3; j++) { - BMVert *v = v_tri[j]; - - bool ok = true; - - for (int k = 0; k < 3; k++) { - if (v->co[k] < node->vb.bmin[k] || v->co[k] >= node->vb.bmax[k]) { - ok = false; - } - } - - if (ok && - (ni == DYNTOPO_NODE_NONE || BLI_table_gset_len(node->bm_faces) < pbvh->leaf_limit)) { - ni = i; - break; - } - } - - if (ni != DYNTOPO_NODE_NONE) { - break; - } - } - - if (ni == DYNTOPO_NODE_NONE) { - // empty pbvh? - printf("possibly pbvh error\n"); - - f = bmesh_face_create_edge_log(pbvh, v_tri, e_tri, f_example); - - BM_ELEM_CD_SET_INT(f, pbvh->cd_face_node_offset, DYNTOPO_NODE_NONE); - - return f; - } - } - - return pbvh_bmesh_face_create(pbvh, ni, v_tri, e_tri, f_example, true, true); -} - -#define pbvh_bmesh_node_vert_use_count_is_equal(pbvh, node, v, n) \ - (pbvh_bmesh_node_vert_use_count_at_most(pbvh, node, v, (n) + 1) == n) - -static int pbvh_bmesh_node_vert_use_count_at_most(PBVH *pbvh, - PBVHNode *node, - BMVert *v, - const int count_max) -{ - int count = 0; - BMFace *f; - - BM_FACES_OF_VERT_ITER_BEGIN (f, v) { - PBVHNode *f_node = pbvh_bmesh_node_from_face(pbvh, f); - if (f_node == node) { - count++; - if (count == count_max) { - return count; - } - } - } - BM_FACES_OF_VERT_ITER_END; - - return count; -} - -/* Return a node that uses vertex 'v' other than its current owner */ -static PBVHNode *pbvh_bmesh_vert_other_node_find(PBVH *pbvh, BMVert *v) -{ - PBVHNode *current_node = pbvh_bmesh_node_from_vert(pbvh, v); - BMFace *f; - - BM_FACES_OF_VERT_ITER_BEGIN (f, v) { - PBVHNode *f_node = pbvh_bmesh_node_from_face(pbvh, f); - - if (f_node != current_node) { - return f_node; - } - } - BM_FACES_OF_VERT_ITER_END; - - return nullptr; -} - -static void pbvh_bmesh_vert_ownership_transfer(PBVH *pbvh, PBVHNode *new_owner, BMVert *v) -{ - PBVHNode *current_owner = pbvh_bmesh_node_from_vert(pbvh, v); - /* mark node for update */ - - if (current_owner) { - current_owner->flag |= PBVH_UpdateDrawBuffers | PBVH_UpdateBB; - - BLI_assert(current_owner != new_owner); - - /* Remove current ownership */ - BLI_table_gset_remove(current_owner->bm_unique_verts, v, nullptr); - } - - /* Set new ownership */ - BM_ELEM_CD_SET_INT(v, pbvh->cd_vert_node_offset, new_owner - pbvh->nodes); - BLI_table_gset_insert(new_owner->bm_unique_verts, v); - - /* mark node for update */ - new_owner->flag |= PBVH_UpdateDrawBuffers | PBVH_UpdateBB | PBVH_UpdateOtherVerts; -} - -void pbvh_bmesh_vert_remove(PBVH *pbvh, BMVert *v) -{ - /* never match for first time */ - int f_node_index_prev = DYNTOPO_NODE_NONE; - const int updateflag = PBVH_UpdateDrawBuffers | PBVH_UpdateBB | PBVH_UpdateTris | - PBVH_UpdateNormals | PBVH_UpdateOtherVerts; - - PBVHNode *v_node = pbvh_bmesh_node_from_vert(pbvh, v); - - if (v_node) { - BLI_table_gset_remove(v_node->bm_unique_verts, v, nullptr); - v_node->flag |= (PBVHNodeFlags)updateflag; - } - - BM_ELEM_CD_SET_INT(v, pbvh->cd_vert_node_offset, DYNTOPO_NODE_NONE); - - /* Have to check each neighboring face's node */ - BMFace *f; - BM_FACES_OF_VERT_ITER_BEGIN (f, v) { - const int f_node_index = pbvh_bmesh_node_index_from_face(pbvh, f); - - if (f_node_index == DYNTOPO_NODE_NONE) { - continue; - } - - /* faces often share the same node, - * quick check to avoid redundant #BLI_table_gset_remove calls */ - if (f_node_index_prev != f_node_index) { - f_node_index_prev = f_node_index; - - PBVHNode *f_node = &pbvh->nodes[f_node_index]; - f_node->flag |= (PBVHNodeFlags)updateflag; // flag update of bm_other_verts - - BLI_assert(!BLI_table_gset_haskey(f_node->bm_unique_verts, v)); - } - } - BM_FACES_OF_VERT_ITER_END; -} - -void pbvh_bmesh_face_remove( - PBVH *pbvh, BMFace *f, bool log_face, bool check_verts, bool ensure_ownership_transfer) -{ - PBVHNode *f_node = pbvh_bmesh_node_from_face(pbvh, f); - - if (!f_node || !(f_node->flag & PBVH_Leaf)) { - printf("pbvh corruption\n"); - fflush(stdout); - return; - } - - bm_logstack_push(); - - /* Check if any of this face's vertices need to be removed - * from the node */ - if (check_verts) { - BMLoop *l_first = BM_FACE_FIRST_LOOP(f); - BMLoop *l_iter = l_first; - do { - BMVert *v = l_iter->v; - if (pbvh_bmesh_node_vert_use_count_is_equal(pbvh, f_node, v, 1)) { - if (BM_ELEM_CD_GET_INT(v, pbvh->cd_vert_node_offset) == f_node - pbvh->nodes) { - // if (BLI_table_gset_haskey(f_node->bm_unique_verts, v)) { - /* Find a different node that uses 'v' */ - PBVHNode *new_node; - - new_node = pbvh_bmesh_vert_other_node_find(pbvh, v); - // BLI_assert(new_node || BM_vert_face_count_is_equal(v, 1)); - - if (new_node) { - pbvh_bmesh_vert_ownership_transfer(pbvh, new_node, v); - } - else if (ensure_ownership_transfer && !BM_vert_face_count_is_equal(v, 1)) { - pbvh_bmesh_vert_remove(pbvh, v); - - f_node->flag |= PBVH_RebuildNodeVerts | PBVH_UpdateOtherVerts; - // printf("failed to find new_node\n"); - } - } - } - } while ((l_iter = l_iter->next) != l_first); - } - - /* Remove face from node and top level */ - BLI_table_gset_remove(f_node->bm_faces, f, nullptr); - BM_ELEM_CD_SET_INT(f, pbvh->cd_face_node_offset, DYNTOPO_NODE_NONE); - - /* Log removed face */ - if (log_face) { - BM_log_face_removed(pbvh->header.bm, pbvh->bm_log, f); - } - - /* mark node for update */ - f_node->flag |= PBVH_UpdateDrawBuffers | PBVH_UpdateNormals | PBVH_UpdateTris | - PBVH_UpdateOtherVerts; - - bm_logstack_pop(); -} - /****************************** EdgeQueue *****************************/ static float maskcb_get(EdgeQueueContext *eq_ctx, BMVert *v1, BMVert *v2) diff --git a/source/blender/blenkernel/intern/dyntopo_intern.hh b/source/blender/blenkernel/intern/dyntopo_intern.hh index 554a1bc4e35..e9dcc031b74 100644 --- a/source/blender/blenkernel/intern/dyntopo_intern.hh +++ b/source/blender/blenkernel/intern/dyntopo_intern.hh @@ -298,7 +298,14 @@ void pbvh_bmesh_face_remove( PBVH *pbvh, BMFace *f, bool log_face, bool check_verts, bool ensure_ownership_transfer); bool check_for_fins(PBVH *pbvh, BMVert *v); - +void pbvh_kill_vert(PBVH *pbvh, BMVert *v, bool log_vert, bool log_edges); +BMFace *pbvh_bmesh_face_create(PBVH *pbvh, + int node_index, + BMVert *v_tri[3], + BMEdge *e_tri[3], + const BMFace *f_example, + bool ensure_verts, + bool log_face); } // namespace blender::dyntopo extern "C" { diff --git a/source/blender/blenkernel/intern/pbvh_bmesh.cc b/source/blender/blenkernel/intern/pbvh_bmesh.cc index 42f1eb87e71..4979ef1d1e1 100644 --- a/source/blender/blenkernel/intern/pbvh_bmesh.cc +++ b/source/blender/blenkernel/intern/pbvh_bmesh.cc @@ -248,6 +248,473 @@ ATTR_NO_OPT void pbvh_bmesh_pbvh_bmesh_check_nodes(PBVH *pbvh) /** \} */ +/****************************** Vertex/Face APIs ******************************/ +namespace blender::dyntopo { + +void pbvh_kill_vert(PBVH *pbvh, BMVert *v, bool log_vert, bool log_edges) +{ + BMEdge *e = v->e; + bm_logstack_push(); + + if (log_edges) { + if (e) { + do { + BM_log_edge_removed(pbvh->header.bm, pbvh->bm_log, e); + } while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e); + } + } + + if (log_vert) { + BM_log_vert_removed(pbvh->header.bm, pbvh->bm_log, v); + } + + BM_idmap_release(pbvh->bm_idmap, (BMElem *)v, true); + BM_vert_kill(pbvh->header.bm, v); + bm_logstack_pop(); +} + +static BMVert *pbvh_bmesh_vert_create(PBVH *pbvh, + int node_index, + const float co[3], + const float no[3], + BMVert *v_example, + const int cd_vert_mask_offset) +{ + PBVHNode *node = &pbvh->nodes[node_index]; + + BLI_assert((pbvh->totnode == 1 || node_index) && node_index <= pbvh->totnode); + + /* avoid initializing customdata because its quite involved */ + BMVert *v = BM_vert_create(pbvh->header.bm, co, nullptr, BM_CREATE_NOP); + MSculptVert *mv = BKE_PBVH_SCULPTVERT(pbvh->cd_sculpt_vert, v); + + pbvh_boundary_update_bmesh(pbvh, v); + MV_ADD_FLAG(mv, SCULPTVERT_NEED_DISK_SORT | SCULPTVERT_NEED_VALENCE); + + if (v_example) { + v->head.hflag = v_example->head.hflag; + + CustomData_bmesh_copy_data( + &pbvh->header.bm->vdata, &pbvh->header.bm->vdata, v_example->head.data, &v->head.data); + + /* This value is logged below */ + copy_v3_v3(v->no, no); + + // keep MSculptVert copied from v_example as-is + } + else { + MSculptVert *mv = BKE_PBVH_SCULPTVERT(pbvh->cd_sculpt_vert, v); + + copy_v3_v3(mv->origco, co); + copy_v3_v3(mv->origno, no); + mv->origmask = 0.0f; + + /* This value is logged below */ + copy_v3_v3(v->no, no); + } + + BLI_table_gset_insert(node->bm_unique_verts, v); + BM_ELEM_CD_SET_INT(v, pbvh->cd_vert_node_offset, node_index); + + node->flag |= PBVH_UpdateDrawBuffers | PBVH_UpdateBB | PBVH_UpdateTris | PBVH_UpdateOtherVerts; + + /* Log the new vertex */ + BM_log_vert_added(pbvh->header.bm, pbvh->bm_log, v); + v->head.index = pbvh->header.bm->totvert; // set provisional index + + return v; +} + +static BMFace *bmesh_face_create_edge_log(PBVH *pbvh, + BMVert *v_tri[3], + BMEdge *e_tri[3], + const BMFace *f_example) +{ + BMFace *f; + + if (!e_tri) { + BMEdge *e_tri2[3]; + + for (int i = 0; i < 3; i++) { + BMVert *v1 = v_tri[i]; + BMVert *v2 = v_tri[(i + 1) % 3]; + + BMEdge *e = BM_edge_exists(v1, v2); + + if (!e) { + e = BM_edge_create(pbvh->header.bm, v1, v2, nullptr, BM_CREATE_NOP); + BM_log_edge_added(pbvh->header.bm, pbvh->bm_log, e); + } + + e_tri2[i] = e; + } + + // f = BM_face_create_verts(pbvh->header.bm, v_tri, 3, f_example, BM_CREATE_NOP, true); + f = BM_face_create(pbvh->header.bm, v_tri, e_tri2, 3, f_example, BM_CREATE_NOP); + } + else { + f = BM_face_create(pbvh->header.bm, v_tri, e_tri, 3, f_example, BM_CREATE_NOP); + } + + if (f_example) { + f->head.hflag = f_example->head.hflag; + } + + return f; +} + +/** + * \note Callers are responsible for checking if the face exists before adding. + */ +BMFace *pbvh_bmesh_face_create(PBVH *pbvh, + int node_index, + BMVert *v_tri[3], + BMEdge *e_tri[3], + const BMFace *f_example, + bool ensure_verts, + bool log_face) +{ + PBVHNode *node = &pbvh->nodes[node_index]; + + /* ensure we never add existing face */ + BLI_assert(!BM_face_exists(v_tri, 3)); + + BMFace *f = bmesh_face_create_edge_log(pbvh, v_tri, e_tri, f_example); + + BLI_table_gset_insert(node->bm_faces, f); + BM_ELEM_CD_SET_INT(f, pbvh->cd_face_node_offset, node_index); + + /* mark node for update */ + node->flag |= PBVH_UpdateDrawBuffers | PBVH_UpdateNormals | PBVH_UpdateTris | + PBVH_UpdateOtherVerts; + node->flag &= ~PBVH_FullyHidden; + + /* Log the new face */ + if (log_face) { + BM_log_face_added(pbvh->header.bm, pbvh->bm_log, f); + } + + int cd_vert_node = pbvh->cd_vert_node_offset; + + if (ensure_verts) { + BMLoop *l = f->l_first; + do { + int ni = BM_ELEM_CD_GET_INT(l->v, cd_vert_node); + + if (ni == DYNTOPO_NODE_NONE) { + BLI_table_gset_add(node->bm_unique_verts, l->v); + BM_ELEM_CD_SET_INT(l->v, cd_vert_node, node_index); + + node->flag |= PBVH_UpdateDrawBuffers | PBVH_UpdateBB | PBVH_UpdateTris | + PBVH_UpdateOtherVerts; + } + + pbvh_boundary_update_bmesh(pbvh, l->v); + + MSculptVert *mv = BKE_PBVH_SCULPTVERT(pbvh->cd_sculpt_vert, l->v); + MV_ADD_FLAG(mv, SCULPTVERT_NEED_DISK_SORT | SCULPTVERT_NEED_VALENCE); + + l = l->next; + } while (l != f->l_first); + } + else { + BMLoop *l = f->l_first; + do { + pbvh_boundary_update_bmesh(pbvh, l->v); + + MSculptVert *mv = BKE_PBVH_SCULPTVERT(pbvh->cd_sculpt_vert, l->v); + MV_ADD_FLAG(mv, SCULPTVERT_NEED_DISK_SORT | SCULPTVERT_NEED_VALENCE); + } while ((l = l->next) != f->l_first); + } + + return f; +} + +BMVert *BKE_pbvh_vert_create_bmesh( + PBVH *pbvh, float co[3], float no[3], PBVHNode *node, BMVert *v_example) +{ + if (!node) { + for (int i = 0; i < pbvh->totnode; i++) { + PBVHNode *node2 = pbvh->nodes + i; + + if (!(node2->flag & PBVH_Leaf)) { + continue; + } + + /* Ensure we have at least some node somewhere picked. */ + node = node2; + + bool ok = true; + + for (int j = 0; j < 3; j++) { + if (co[j] < node2->vb.bmin[j] || co[j] >= node2->vb.bmax[j]) { + continue; + } + } + + if (ok) { + break; + } + } + } + + BMVert *v; + + if (!node) { + printf("possible pbvh error\n"); + v = BM_vert_create(pbvh->header.bm, co, v_example, BM_CREATE_NOP); + BM_ELEM_CD_SET_INT(v, pbvh->cd_vert_node_offset, DYNTOPO_NODE_NONE); + + pbvh_boundary_update_bmesh(pbvh, v); + MSculptVert *mv = (MSculptVert *)BM_ELEM_CD_GET_VOID_P(v, pbvh->cd_sculpt_vert); + MV_ADD_FLAG(mv, SCULPTVERT_NEED_VALENCE | SCULPTVERT_NEED_DISK_SORT); + + copy_v3_v3(mv->origco, co); + + return v; + } + + return pbvh_bmesh_vert_create( + pbvh, node - pbvh->nodes, co, no, v_example, pbvh->cd_vert_mask_offset); +} + +PBVHNode *BKE_pbvh_node_from_face_bmesh(PBVH *pbvh, BMFace *f) +{ + return pbvh->nodes + BM_ELEM_CD_GET_INT(f, pbvh->cd_face_node_offset); +} + +BMFace *BKE_pbvh_face_create_bmesh(PBVH *pbvh, + BMVert *v_tri[3], + BMEdge *e_tri[3], + const BMFace *f_example) +{ + int ni = DYNTOPO_NODE_NONE; + + for (int i = 0; i < 3; i++) { + BMVert *v = v_tri[i]; + BMLoop *l; + BMIter iter; + + BM_ITER_ELEM (l, &iter, v, BM_LOOPS_OF_VERT) { + int ni2 = BM_ELEM_CD_GET_INT(l->f, pbvh->cd_face_node_offset); + if (ni2 != DYNTOPO_NODE_NONE) { + ni = ni2; + break; + } + } + } + + if (ni == DYNTOPO_NODE_NONE) { + BMFace *f; + + /* No existing nodes? Find one. */ + for (int i = 0; i < pbvh->totnode; i++) { + PBVHNode *node = pbvh->nodes + i; + + if (!(node->flag & PBVH_Leaf)) { + continue; + } + + for (int j = 0; j < 3; j++) { + BMVert *v = v_tri[j]; + + bool ok = true; + + for (int k = 0; k < 3; k++) { + if (v->co[k] < node->vb.bmin[k] || v->co[k] >= node->vb.bmax[k]) { + ok = false; + } + } + + if (ok && + (ni == DYNTOPO_NODE_NONE || BLI_table_gset_len(node->bm_faces) < pbvh->leaf_limit)) { + ni = i; + break; + } + } + + if (ni != DYNTOPO_NODE_NONE) { + break; + } + } + + if (ni == DYNTOPO_NODE_NONE) { + /* Empty pbvh? */ + f = bmesh_face_create_edge_log(pbvh, v_tri, e_tri, f_example); + + BM_ELEM_CD_SET_INT(f, pbvh->cd_face_node_offset, DYNTOPO_NODE_NONE); + + return f; + } + } + + return pbvh_bmesh_face_create(pbvh, ni, v_tri, e_tri, f_example, true, true); +} + +#define pbvh_bmesh_node_vert_use_count_is_equal(pbvh, node, v, n) \ + (pbvh_bmesh_node_vert_use_count_at_most(pbvh, node, v, (n) + 1) == n) + +static int pbvh_bmesh_node_vert_use_count_at_most(PBVH *pbvh, + PBVHNode *node, + BMVert *v, + const int count_max) +{ + int count = 0; + BMFace *f; + + BM_FACES_OF_VERT_ITER_BEGIN (f, v) { + PBVHNode *f_node = pbvh_bmesh_node_from_face(pbvh, f); + if (f_node == node) { + count++; + if (count == count_max) { + return count; + } + } + } + BM_FACES_OF_VERT_ITER_END; + + return count; +} + +/* Return a node that uses vertex 'v' other than its current owner */ +static PBVHNode *pbvh_bmesh_vert_other_node_find(PBVH *pbvh, BMVert *v) +{ + PBVHNode *current_node = pbvh_bmesh_node_from_vert(pbvh, v); + BMFace *f; + + BM_FACES_OF_VERT_ITER_BEGIN (f, v) { + PBVHNode *f_node = pbvh_bmesh_node_from_face(pbvh, f); + + if (f_node != current_node) { + return f_node; + } + } + BM_FACES_OF_VERT_ITER_END; + + return nullptr; +} + +static void pbvh_bmesh_vert_ownership_transfer(PBVH *pbvh, PBVHNode *new_owner, BMVert *v) +{ + PBVHNode *current_owner = pbvh_bmesh_node_from_vert(pbvh, v); + /* Mark node for update. */ + + if (current_owner) { + current_owner->flag |= PBVH_UpdateDrawBuffers | PBVH_UpdateBB; + + BLI_assert(current_owner != new_owner); + + /* Remove current ownership. */ + BLI_table_gset_remove(current_owner->bm_unique_verts, v, nullptr); + } + + /* Set new ownership. */ + BM_ELEM_CD_SET_INT(v, pbvh->cd_vert_node_offset, new_owner - pbvh->nodes); + BLI_table_gset_insert(new_owner->bm_unique_verts, v); + + /* Mark node for update. */ + new_owner->flag |= PBVH_UpdateDrawBuffers | PBVH_UpdateBB | PBVH_UpdateOtherVerts; +} + +void pbvh_bmesh_vert_remove(PBVH *pbvh, BMVert *v) +{ + /* never match for first time */ + int f_node_index_prev = DYNTOPO_NODE_NONE; + const int updateflag = PBVH_UpdateDrawBuffers | PBVH_UpdateBB | PBVH_UpdateTris | + PBVH_UpdateNormals | PBVH_UpdateOtherVerts; + + PBVHNode *v_node = pbvh_bmesh_node_from_vert(pbvh, v); + + if (v_node) { + BLI_table_gset_remove(v_node->bm_unique_verts, v, nullptr); + v_node->flag |= (PBVHNodeFlags)updateflag; + } + + BM_ELEM_CD_SET_INT(v, pbvh->cd_vert_node_offset, DYNTOPO_NODE_NONE); + + /* Have to check each neighboring face's node */ + BMFace *f; + BM_FACES_OF_VERT_ITER_BEGIN (f, v) { + const int f_node_index = pbvh_bmesh_node_index_from_face(pbvh, f); + + if (f_node_index == DYNTOPO_NODE_NONE) { + continue; + } + + /* faces often share the same node, + * quick check to avoid redundant #BLI_table_gset_remove calls */ + if (f_node_index_prev != f_node_index) { + f_node_index_prev = f_node_index; + + PBVHNode *f_node = &pbvh->nodes[f_node_index]; + f_node->flag |= (PBVHNodeFlags)updateflag; // flag update of bm_other_verts + + BLI_assert(!BLI_table_gset_haskey(f_node->bm_unique_verts, v)); + } + } + BM_FACES_OF_VERT_ITER_END; +} + +void pbvh_bmesh_face_remove( + PBVH *pbvh, BMFace *f, bool log_face, bool check_verts, bool ensure_ownership_transfer) +{ + PBVHNode *f_node = pbvh_bmesh_node_from_face(pbvh, f); + + if (!f_node || !(f_node->flag & PBVH_Leaf)) { + printf("pbvh corruption\n"); + fflush(stdout); + return; + } + + bm_logstack_push(); + + /* Check if any of this face's vertices need to be removed + * from the node */ + if (check_verts) { + BMLoop *l_first = BM_FACE_FIRST_LOOP(f); + BMLoop *l_iter = l_first; + do { + BMVert *v = l_iter->v; + if (pbvh_bmesh_node_vert_use_count_is_equal(pbvh, f_node, v, 1)) { + if (BM_ELEM_CD_GET_INT(v, pbvh->cd_vert_node_offset) == f_node - pbvh->nodes) { + // if (BLI_table_gset_haskey(f_node->bm_unique_verts, v)) { + /* Find a different node that uses 'v' */ + PBVHNode *new_node; + + new_node = pbvh_bmesh_vert_other_node_find(pbvh, v); + // BLI_assert(new_node || BM_vert_face_count_is_equal(v, 1)); + + if (new_node) { + pbvh_bmesh_vert_ownership_transfer(pbvh, new_node, v); + } + else if (ensure_ownership_transfer && !BM_vert_face_count_is_equal(v, 1)) { + pbvh_bmesh_vert_remove(pbvh, v); + + f_node->flag |= PBVH_RebuildNodeVerts | PBVH_UpdateOtherVerts; + // printf("failed to find new_node\n"); + } + } + } + } while ((l_iter = l_iter->next) != l_first); + } + + /* Remove face from node and top level */ + BLI_table_gset_remove(f_node->bm_faces, f, nullptr); + BM_ELEM_CD_SET_INT(f, pbvh->cd_face_node_offset, DYNTOPO_NODE_NONE); + + /* Log removed face */ + if (log_face) { + BM_log_face_removed(pbvh->header.bm, pbvh->bm_log, f); + } + + /* mark node for update */ + f_node->flag |= PBVH_UpdateDrawBuffers | PBVH_UpdateNormals | PBVH_UpdateTris | + PBVH_UpdateOtherVerts; + + bm_logstack_pop(); +} +} // namespace blender::dyntopo + /****************************** Building ******************************/ /* Update node data after splitting */ @@ -1274,36 +1741,6 @@ void pbvh_bmesh_normals_update(PBVH *pbvh, PBVHNode **nodes, int totnode) #endif } -static void pbvh_bmesh_normals_update_old(PBVHNode **nodes, int totnode) -{ - for (int n = 0; n < totnode; n++) { - PBVHNode *node = nodes[n]; - - if (node->flag & PBVH_UpdateNormals) { - BMVert *v; - BMFace *f; - - TGSET_ITER (f, node->bm_faces) { - BM_face_normal_update(f); - } - TGSET_ITER_END - - TGSET_ITER (v, node->bm_unique_verts) { - BM_vert_normal_update(v); - } - TGSET_ITER_END - - /* This should be unneeded normally */ - TGSET_ITER (v, node->bm_other_verts) { - BM_vert_normal_update(v); - } - TGSET_ITER_END - - node->flag &= ~PBVH_UpdateNormals; - } - } -} - struct FastNodeBuildInfo { int totface; /* number of faces */ int start; /* start of faces in array */ @@ -1569,20 +2006,6 @@ static void pbvh_bmesh_create_nodes_fast_recursive_final(PBVH *pbvh, /***************************** Public API *****************************/ -static float sculpt_corner_angle(float *base, float *co1, float *co2) -{ - float t1[3], t2[3]; - sub_v3_v3v3(t1, co1, base); - sub_v3_v3v3(t2, co2, base); - - normalize_v3(t1); - normalize_v3(t2); - - float th = dot_v3v3(t1, t2); - - return saacos(th); -} - struct FSetTemp { BMVert *v; int fset; @@ -2001,137 +2424,6 @@ void BKE_pbvh_recalc_bmesh_boundary(PBVH *pbvh) } } -struct FaceBinThread { - MemArena *arena; - FastNodeBuildInfo *nodes, **leaves; - int totnode, totleaf; -}; - -static void coalese_pbvh(PBVH *pbvh, BMesh *bm) -{ - BMIter iter; - BMFace *f; - - const char tag = BM_ELEM_TAG_ALT; - - BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { - f->head.hflag &= ~tag; - } - - int leafsize = 1000; - double f1 = pow(2.0, ceil(log2(bm->totface / (double)leafsize))); - leafsize = (int)ceil(bm->totface / f1); - - printf("leafsize: %d\n", leafsize); - - int *fmap = (int *)MEM_calloc_arrayN(bm->totface, sizeof(int), "pbvh face map"); - int totleaf = 0; - - Vector nodes; - - const int qsize = leafsize; - BMFace **queue = MEM_cnew_array(qsize, "pbvh queue"); - - BM_mesh_elem_table_ensure(bm, BM_FACE | BM_VERT); - - BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { - int qhead = 0; - int qtail = 0; - - if (f->head.hflag & tag) { - continue; - } - - int ni = totleaf++; - nodes.resize(nodes.size() + 1); - - FastNodeBuildInfo *node = &nodes.last(0); - copy_v3_v3(node->cent, f->l_first->v->co); - copy_v3_v3(node->no, f->no); - - queue[0] = f; - qhead = 1; - - while (qhead != qtail) { - BMFace *f2 = queue[qtail]; - qtail = (qtail + 1) % qsize; - - fmap[f2->head.index] = ni; - node->totface++; - - BMLoop *l = f2->l_first; - do { - BMLoop *l2 = l; - do { - if (!(l2->f->head.hflag & tag)) { - l2->f->head.hflag |= tag; - - queue[qhead] = l2->f; - qhead = (qhead + 1) % qsize; - } - } while ((l2 = l2->radial_next) != l); - } while ((l = l->next) != f2->l_first); - } - } - - printf("totleafs: %d\n", totleaf); - nodes.resize(nodes.size() << 1); - - const int tag2 = 1; - int pi = totleaf; - int starti = 0; - int endi = totleaf; - - for (int step = 0; step < 55; step++) { - if (endi - starti <= 1) { - break; - } - - for (int i = starti; i < endi; i++) { - FastNodeBuildInfo *n1 = &nodes[i], *n2 = nullptr; - - if (n1->tag & tag2) { - continue; - } - - float mindis = 1e17; - - for (int j = starti; j < endi; j++) { - FastNodeBuildInfo *n3 = &nodes[j]; - - if (j == i || n3->tag) { - continue; - } - - float dis = len_squared_v3v3(n1->cent, n3->cent); - if (dis < mindis) { - mindis = dis; - n2 = n3; - } - } - - if (!n2) { - break; - } - - FastNodeBuildInfo *parent = &nodes[pi]; - pi++; - - n1->tag |= tag2; - n2->tag |= tag2; - parent->tag = 0; - parent->child1 = n1; - parent->child2 = n2; - } - - starti = endi; - endi = pi; - } - - MEM_SAFE_FREE(queue); - MEM_SAFE_FREE(fmap); -} - void BKE_pbvh_update_sculpt_verts(PBVH *pbvh) { int totvert = pbvh->totvert; @@ -2465,120 +2757,6 @@ void BKE_pbvh_bmesh_free_tris(PBVH *pbvh, PBVHNode *node) } } -/* -generate triangle buffers with split uv islands. -currently unused (and untested). -*/ -static bool pbvh_bmesh_split_tris(PBVH *pbvh, PBVHNode *node) -{ - BMFace *f; - - BM_mesh_elem_index_ensure(pbvh->header.bm, BM_VERT | BM_FACE); - - // split by uvs - int layeri = CustomData_get_layer_index(&pbvh->header.bm->ldata, CD_PROP_FLOAT2); - if (layeri < 0) { - return false; - } - - int totlayer = 0; - - while (layeri < pbvh->header.bm->ldata.totlayer && - pbvh->header.bm->ldata.layers[layeri].type == CD_PROP_FLOAT2) { - totlayer++; - layeri++; - } - - const int cd_uv = pbvh->header.bm->ldata.layers[layeri].offset; - const int cd_size = CustomData_sizeof(CD_PROP_FLOAT2); - - Vector verts; - Vector tris; - Vector loops; - - TGSET_ITER (f, node->bm_faces) { - BMLoop *l = f->l_first; - - do { - l->head.index = -1; - l = l->next; - } while (l != f->l_first); - } - TGSET_ITER_END - - int vi = 0; - - TGSET_ITER (f, node->bm_faces) { - BMLoop *l = f->l_first; - - do { - if (l->head.index >= 0) { - continue; - } - - l->head.index = vi++; - loops.append((intptr_t)l); - - PBVHVertRef sv = {(intptr_t)l->v}; - verts.append(sv); - - BMIter iter; - BMLoop *l2; - - BM_ITER_ELEM (l2, &iter, l, BM_LOOPS_OF_VERT) { - bool ok = true; - - for (int i = 0; i < totlayer; i++) { - float *uv1 = BM_ELEM_CD_PTR(l, cd_uv + cd_size * i); - float *uv2 = BM_ELEM_CD_PTR(l2, cd_uv + cd_size * i); - - if (len_v3v3(uv1, uv2) > 0.001) { - ok = false; - break; - } - } - - if (ok) { - l2->head.index = l->head.index; - } - } - } while (l != f->l_first); - } - TGSET_ITER_END - - TGSET_ITER (f, node->bm_faces) { - BMLoop *l1 = f->l_first, *l2 = f->l_first->next, *l3 = f->l_first->prev; - - PBVHTri tri; - tri.f.i = (intptr_t)f; - - tri.v[0] = l1->head.index; - tri.v[1] = l2->head.index; - tri.v[2] = l3->head.index; - - copy_v3_v3(tri.no, f->no); - tris.append(tri); - } - TGSET_ITER_END - - if (node->tribuf) { - pbvh_free_tribuf(node->tribuf); - } - else { - node->tribuf = MEM_cnew("node->tribuf"); - } - - node->tribuf->verts = c_array_from_vector(verts); - node->tribuf->loops = c_array_from_vector(loops); - node->tribuf->tris = c_array_from_vector(tris); - - node->tribuf->tottri = tris.size(); - node->tribuf->totvert = verts.size(); - node->tribuf->totloop = loops.size(); - - return true; -} - BLI_INLINE PBVHTri *pbvh_tribuf_add_tri(PBVHTriBuf *tribuf) { tribuf->tottri++; @@ -3103,14 +3281,14 @@ static void pbvh_bmesh_join_subnodes(PBVH *pbvh, PBVHNode *node, PBVHNode *paren return; } if (node != parent) { - node->flag |= PBVH_Delete; // mark for deletion + node->flag |= PBVH_Delete; /* Mark for deletion. */ } return; } if (node != parent) { - node->flag |= PBVH_Delete; // mark for deletion + node->flag |= PBVH_Delete; /* Mark for deletion. */ } BMVert *v; @@ -3210,7 +3388,7 @@ static void BKE_pbvh_bmesh_correct_tree(PBVH *pbvh, PBVHNode *node, PBVHNode *pa } } -// deletes PBVH_Delete marked nodes +/* Deletes PBVH_Delete marked nodes. */ static void pbvh_bmesh_compact_tree(PBVH *bvh) { // compact nodes @@ -3428,9 +3606,7 @@ static void recursive_delete_nodes(PBVH *pbvh, int ni) } } -// static float bbox_overlap() -/* works by detect overlay of leaf nodes, destroying them - and then re-inserting them*/ +/* Prunes leaf nodes that are too small or degenerate. */ static void pbvh_bmesh_balance_tree(PBVH *pbvh) { float *overlaps = MEM_cnew_array(pbvh->totnode, "overlaps"); @@ -3457,21 +3633,6 @@ static void pbvh_bmesh_balance_tree(PBVH *pbvh) } } -#if 0 - for (int i = 0; i < pbvh->totnode; i++) { - PBVHNode *node = pbvh->nodes + i; - PBVHNode *parent = parentmap[i]; - int depth = 0; - - while (parent) { - parent = parentmap[parent - pbvh->nodes]; - depth++; - } - - depthmap[i] = depth; - } -#endif - const int cd_vert_node = pbvh->cd_vert_node_offset; const int cd_face_node = pbvh->cd_face_node_offset; @@ -3613,47 +3774,8 @@ static void pbvh_bmesh_join_nodes(PBVH *bvh) pbvh_count_subtree_verts(bvh, bvh->nodes); BKE_pbvh_bmesh_correct_tree(bvh, bvh->nodes, nullptr); - // compact nodes + /* Compact nodes. */ int totnode = 0; - for (int i = 0; i < bvh->totnode; i++) { - PBVHNode *n = bvh->nodes + i; - - if (0 && !(n->flag & PBVH_Delete)) { - if (!(n->flag & PBVH_Leaf)) { - PBVHNode *n1 = bvh->nodes + n->children_offset; - PBVHNode *n2 = bvh->nodes + n->children_offset + 1; - - if ((n1->flag & PBVH_Delete) != (n2->flag & PBVH_Delete)) { - printf("%s: Un-deleting an empty node!\n", __func__); - PBVHNode *n3 = n1->flag & PBVH_Delete ? n1 : n2; - - n3->flag = PBVH_Leaf | PBVH_UpdateTris; - n3->bm_unique_verts = BLI_table_gset_new("bm_unique_verts"); - n3->bm_other_verts = BLI_table_gset_new("bm_other_verts"); - n3->bm_faces = BLI_table_gset_new("bm_faces"); - n3->tribuf = nullptr; - n3->draw_batches = nullptr; - } - else if ((n1->flag & PBVH_Delete) && (n2->flag & PBVH_Delete)) { - n->children_offset = 0; - n->flag |= PBVH_Leaf | PBVH_UpdateTris; - - if (!n->bm_unique_verts) { - // should not happen - n->bm_unique_verts = BLI_table_gset_new("bm_unique_verts"); - n->bm_other_verts = BLI_table_gset_new("bm_other_verts"); - n->bm_faces = BLI_table_gset_new("bm_faces"); - } - - n->tribuf = nullptr; - n->draw_batches = nullptr; - } - } - - totnode++; - } - } - int *map = MEM_cnew_array(bvh->totnode, "bmesh map temp"); for (int i = 0; i < bvh->totnode; i++) { @@ -3670,7 +3792,7 @@ static void pbvh_bmesh_join_nodes(PBVH *bvh) } } - // build idx map for child offsets + /* Build index map for child offsets. */ int j = 0; for (int i = 0; i < bvh->totnode; i++) { PBVHNode *n = bvh->nodes + i; @@ -4790,12 +4912,13 @@ struct CacheParamDef { float defvalue, min, max; }; -CacheParamDef pbvh_bmesh_cache_param_def[] = {{"vchunk", 512.0f, 256.0f, 1024.0f * 12.0f}, - {"echunk", 512.0f, 256.0f, 1024.0f * 12.0f}, - {"lchunk", 512.0f, 256.0f, 1024.0f * 12.0f}, - {"pchunk", 512.0f, 256.0f, 1024.0f * 12.0f}, - {"cluster_steps", 512.0f, 1.0f, 256.0f}, - {"cluster_size", 512.0f, 1.0f, 8192.0f * 32.0f}}; +static CacheParamDef pbvh_bmesh_cache_param_def[] = { + {"vchunk", 512.0f, 256.0f, 1024.0f * 12.0f}, + {"echunk", 512.0f, 256.0f, 1024.0f * 12.0f}, + {"lchunk", 512.0f, 256.0f, 1024.0f * 12.0f}, + {"pchunk", 512.0f, 256.0f, 1024.0f * 12.0f}, + {"cluster_steps", 512.0f, 1.0f, 256.0f}, + {"cluster_size", 512.0f, 1.0f, 8192.0f * 32.0f}}; int pbvh_bmesh_cache_test_totparams() { @@ -4857,7 +4980,7 @@ struct ElemHeader { short type, hflag; int index; void *data; -} ElemHeder; +}; struct MeshVert2 { ElemHeader head; -- 2.30.2 From 6a14199f7d34d3f9e9d40eb8f2b655bd10e2ebce Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Sat, 18 Mar 2023 17:01:28 -0700 Subject: [PATCH 037/279] temp-sculpt-dyntopo: Code cleanup and bugfixes * Removed experimental uv solver brush code * Fixed undo bug in draw face sets * Fixed draw face sets not working in extend mode. * Fixed extract ops not working in dyntopo. --- source/blender/blenkernel/BKE_pbvh.h | 2 - source/blender/blenkernel/intern/dyntopo.cc | 4 +- .../blender/blenkernel/intern/pbvh_bmesh.cc | 18 +- source/blender/bmesh/intern/bmesh_construct.c | 18 +- source/blender/bmesh/intern/bmesh_log.cc | 8 + .../editors/mesh/editmesh_mask_extract.cc | 37 +- source/blender/editors/sculpt_paint/sculpt.cc | 12 +- .../sculpt_paint/sculpt_automasking.cc | 3 - .../editors/sculpt_paint/sculpt_curvature.cc | 4 +- .../editors/sculpt_paint/sculpt_dyntopo.cc | 884 +----------------- .../editors/sculpt_paint/sculpt_face_set.cc | 11 +- .../editors/sculpt_paint/sculpt_smooth.cc | 19 +- 12 files changed, 59 insertions(+), 961 deletions(-) diff --git a/source/blender/blenkernel/BKE_pbvh.h b/source/blender/blenkernel/BKE_pbvh.h index 1cfa921ac85..c2398316300 100644 --- a/source/blender/blenkernel/BKE_pbvh.h +++ b/source/blender/blenkernel/BKE_pbvh.h @@ -28,8 +28,6 @@ extern "C" { #endif -/* Experimental feature to detect quad diagonals and mark (but not dissolve) them. */ -//#define SCULPT_DIAGONAL_EDGE_MARKS typedef struct SculptPMap { struct MeshElemMap *pmap; int *pmap_mem; diff --git a/source/blender/blenkernel/intern/dyntopo.cc b/source/blender/blenkernel/intern/dyntopo.cc index 128c09ed246..598653a4196 100644 --- a/source/blender/blenkernel/intern/dyntopo.cc +++ b/source/blender/blenkernel/intern/dyntopo.cc @@ -2894,8 +2894,8 @@ static void pbvh_split_edges(EdgeQueueContext *eq_ctx, if (ni >= pbvh->totnode || !(pbvh->nodes[ni].flag & PBVH_Leaf)) { printf("%s: error\n", __func__); } - /* this should rarely happen */ - // if (ni < 0 || ni >= pbvh->totnode || !(pbvh->nodes[ni].flag & PBVH_Leaf)) { + + /* This should rarely happen. */ if (ni == DYNTOPO_NODE_NONE) { ni = DYNTOPO_NODE_NONE; diff --git a/source/blender/blenkernel/intern/pbvh_bmesh.cc b/source/blender/blenkernel/intern/pbvh_bmesh.cc index 4979ef1d1e1..b4030057da1 100644 --- a/source/blender/blenkernel/intern/pbvh_bmesh.cc +++ b/source/blender/blenkernel/intern/pbvh_bmesh.cc @@ -2952,21 +2952,9 @@ bool BKE_pbvh_bmesh_check_tris(PBVH *pbvh, PBVHNode *node) continue; } -#ifdef SCULPT_DIAGONAL_EDGE_MARKS - int ecount = 0; -#endif - - // clear edgeflag for building edge indices later + /* Clear edgeflag for building edge indices later. */ BMLoop *l = f->l_first; do { -#ifdef SCULPT_DIAGONAL_EDGE_MARKS - BMEdge *e2 = l->v->e; - do { - if (e2->head.hflag & BM_ELEM_DRAW) { - ecount++; - } - } while ((e2 = BM_DISK_EDGE_NEXT(e2, l->v)) != l->v->e); -#endif l->e->head.hflag &= ~edgeflag; } while ((l = l->next) != f->l_first); @@ -3005,11 +2993,7 @@ bool BKE_pbvh_bmesh_check_tris(PBVH *pbvh, PBVHNode *node) void **val = nullptr; BMEdge *e = BM_edge_exists(l->v, l2->v); -# ifdef SCULPT_DIAGONAL_EDGE_MARKS - if (e && (e->head.hflag & BM_ELEM_DRAW)) { -# else if (e) { -# endif tri->eflag |= 1 << j; mat_tri->eflag |= 1 << j; } diff --git a/source/blender/bmesh/intern/bmesh_construct.c b/source/blender/bmesh/intern/bmesh_construct.c index 89119e3f7bb..d29fb20b137 100644 --- a/source/blender/bmesh/intern/bmesh_construct.c +++ b/source/blender/bmesh/intern/bmesh_construct.c @@ -659,7 +659,7 @@ void BM_mesh_copy_init_customdata(BMesh *bm_dst, BMesh *bm_src, const BMAllocTem // forcibly copy mesh_id layers CustomData *srcdatas[4] = {&bm_src->vdata, &bm_src->edata, &bm_src->ldata, &bm_src->pdata}; - //CustomData *dstdatas[4] = {&bm_dst->vdata, &bm_dst->edata, &bm_dst->ldata, &bm_dst->pdata}; + // CustomData *dstdatas[4] = {&bm_dst->vdata, &bm_dst->edata, &bm_dst->ldata, &bm_dst->pdata}; for (int i = 0; i < 4; i++) { CustomData *cdata = srcdatas[i]; @@ -730,16 +730,10 @@ BMesh *BM_mesh_copy_ex(BMesh *bm_old, struct BMeshCreateParams *params) BMIter iter; int i; const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_BM(bm_old); - struct BMeshCreateParams _params; + struct BMeshCreateParams _params = {0}; if (!params) { - _params = ((struct BMeshCreateParams){ - .use_toolflags = bm_old->use_toolflags, - .id_elem_mask = bm_old->idmap.flag & (BM_VERT | BM_EDGE | BM_LOOP | BM_FACE), - .create_unique_ids = !!(bm_old->idmap.flag & BM_HAS_IDS), - .id_map = !!(bm_old->idmap.flag & BM_HAS_ID_MAP), - .temporary_ids = !(bm_old->idmap.flag & BM_PERMANENT_IDS), - .no_reuse_ids = !!(bm_old->idmap.flag & BM_NO_REUSE_IDS)}); + _params.use_toolflags = bm_old->use_toolflags; params = &_params; } @@ -892,12 +886,6 @@ BMesh *BM_mesh_copy_ex(BMesh *bm_old, struct BMeshCreateParams *params) BMesh *BM_mesh_copy(BMesh *bm_old) { struct BMeshCreateParams params = {0}; -#ifndef USE_NEW_IDMAP - params.create_unique_ids = bm_old->idmap.flag & BM_HAS_IDS; - params.id_elem_mask = bm_old->idmap.flag & (BM_VERT | BM_EDGE | BM_LOOP | BM_FACE); - params.id_map = bm_old->idmap.flag & BM_HAS_ID_MAP; - params.no_reuse_ids = bm_old->idmap.flag & BM_NO_REUSE_IDS; -#endif params.copy_all_layers = true; return BM_mesh_copy_ex(bm_old, ¶ms); diff --git a/source/blender/bmesh/intern/bmesh_log.cc b/source/blender/bmesh/intern/bmesh_log.cc index 05a52fc2a62..c67c39275f4 100644 --- a/source/blender/bmesh/intern/bmesh_log.cc +++ b/source/blender/bmesh/intern/bmesh_log.cc @@ -772,6 +772,8 @@ struct BMLog { ATTR_NO_OPT bool free_all_entries() { + printf("Freeing all log entries.\n"); + BMLogEntry *entry = first_entry; if (!entry) { @@ -803,6 +805,8 @@ struct BMLog { } entry->prev = current_entry; + entry->log = this; + entry->idmap = idmap; if (!first_entry) { first_entry = entry; @@ -1621,12 +1625,15 @@ void BM_log_set_current_entry(BMLog *log, BMLogEntry *entry) bool BM_log_entry_drop(BMLogEntry *entry) { + printf("%s: Freeing log entry %p\n", __func__, entry); + if (entry->prev) { entry->prev->next = entry->next; } if (entry->next) { entry->next->prev = entry->prev; } + if (entry->log && entry == entry->log->current_entry) { entry->log->current_entry = entry->prev; } @@ -1637,4 +1644,5 @@ bool BM_log_entry_drop(BMLogEntry *entry) void BM_log_print_entry(BMLog *log, BMLogEntry *entry) { + printf("entry: %p", entry); } diff --git a/source/blender/editors/mesh/editmesh_mask_extract.cc b/source/blender/editors/mesh/editmesh_mask_extract.cc index 22abce063b3..68e4ad14d65 100644 --- a/source/blender/editors/mesh/editmesh_mask_extract.cc +++ b/source/blender/editors/mesh/editmesh_mask_extract.cc @@ -52,10 +52,6 @@ static bool geometry_extract_poll(bContext *C) { Object *ob = CTX_data_active_object(C); if (ob != nullptr && ob->mode == OB_MODE_SCULPT) { - if (ob->sculpt->bm) { - CTX_wm_operator_poll_msg_set(C, "The geometry can not be extracted with dyntopo activated"); - return false; - } return ED_operator_object_active_editable_mesh(C); } return false; @@ -78,16 +74,23 @@ struct GeometryExtractParams { /* Function that tags in BMesh the faces that should be deleted in the extracted object. */ using GeometryExtractTagMeshFunc = void(BMesh *, GeometryExtractParams *); -static int geometry_extract_apply(bContext *C, - wmOperator *op, - GeometryExtractTagMeshFunc *tag_fn, - GeometryExtractParams *params) +ATTR_NO_OPT static int geometry_extract_apply(bContext *C, + wmOperator *op, + GeometryExtractTagMeshFunc *tag_fn, + GeometryExtractParams *params) { Main *bmain = CTX_data_main(C); Object *ob = CTX_data_active_object(C); View3D *v3d = CTX_wm_view3d(C); Scene *scene = CTX_data_scene(C); Depsgraph *depsgraph = CTX_data_depsgraph_on_load(C); + bool use_sculpt_bmesh = ob->mode == OB_MODE_SCULPT && ob->sculpt && ob->sculpt->bm; + BMesh *bm; + + if (use_sculpt_bmesh) { + bm = BM_mesh_copy(ob->sculpt->bm); + BM_mesh_toolflags_set(bm, true); + } ED_object_sculptmode_exit(C, depsgraph); @@ -100,15 +103,17 @@ static int geometry_extract_apply(bContext *C, Mesh *mesh = static_cast(ob->data); Mesh *new_mesh = (Mesh *)BKE_id_copy(bmain, &mesh->id); - const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(new_mesh); - BMeshCreateParams bm_create_params{}; - bm_create_params.use_toolflags = true; - BMesh *bm = BM_mesh_create(&allocsize, &bm_create_params); + if (!use_sculpt_bmesh) { + const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(new_mesh); + BMeshCreateParams bm_create_params{}; + bm_create_params.use_toolflags = true; + bm = BM_mesh_create(&allocsize, &bm_create_params); - BMeshFromMeshParams mesh_to_bm_params{}; - mesh_to_bm_params.calc_face_normal = true; - mesh_to_bm_params.calc_vert_normal = true; - BM_mesh_bm_from_me(bm, new_mesh, &mesh_to_bm_params); + BMeshFromMeshParams mesh_to_bm_params{}; + mesh_to_bm_params.calc_face_normal = true; + mesh_to_bm_params.calc_vert_normal = true; + BM_mesh_bm_from_me(bm, new_mesh, &mesh_to_bm_params); + } BMEditMesh *em = BKE_editmesh_create(bm); diff --git a/source/blender/editors/sculpt_paint/sculpt.cc b/source/blender/editors/sculpt_paint/sculpt.cc index fb17177c56e..33343067eac 100644 --- a/source/blender/editors/sculpt_paint/sculpt.cc +++ b/source/blender/editors/sculpt_paint/sculpt.cc @@ -477,7 +477,7 @@ int SCULPT_active_face_set_get(SculptSession *ss) return ss->face_sets[face_index]; } case PBVH_BMESH: - if (ss->cd_faceset_offset && ss->active_face.i) { + if (ss->cd_faceset_offset && (ss->active_face.i != PBVH_REF_NONE)) { BMFace *f = (BMFace *)ss->active_face.i; return BM_ELEM_CD_GET_INT(f, ss->cd_faceset_offset); } @@ -4360,7 +4360,7 @@ static void do_brush_action(Sculpt *sd, undo_type = SCULPT_UNDO_COLOR; } else if (brush->sculpt_tool == SCULPT_TOOL_DRAW_FACE_SETS) { - if (ss->cache->invert) { + if (ss->cache->alt_smooth) { undo_type = SCULPT_UNDO_COORDS; } else { @@ -5923,6 +5923,8 @@ bool SCULPT_cursor_geometry_info_update(bContext *C, srd.ray_normal = ray_normal; srd.depth = depth; srd.face_normal = face_normal; + srd.active_face.i = PBVH_REF_NONE; + srd.active_vertex.i = PBVH_REF_NONE; isect_ray_tri_watertight_v3_precalc(&srd.isect_precalc, ray_normal); BKE_pbvh_raycast( @@ -5951,7 +5953,7 @@ bool SCULPT_cursor_geometry_info_update(bContext *C, ss->active_grid_index = ss->active_face.i; break; case PBVH_BMESH: - ss->active_face.i = PBVH_REF_NONE; + ss->active_face = srd.active_face; ss->active_grid_index = 0; break; } @@ -6066,6 +6068,8 @@ bool SCULPT_stroke_get_location_ex(bContext *C, srd.depth = depth; srd.original = original; srd.face_normal = face_normal; + srd.active_face.i = PBVH_REF_NONE; + srd.active_vertex.i = PBVH_REF_NONE; isect_ray_tri_watertight_v3_precalc(&srd.isect_precalc, ray_normal); BKE_pbvh_raycast( @@ -7093,6 +7097,8 @@ bool SCULPT_vertex_is_occluded(SculptSession *ss, PBVHVertRef vertex, bool origi srd.ray_normal = ray_normal; srd.depth = depth; srd.face_normal = face_normal; + srd.active_face.i = PBVH_REF_NONE; + srd.active_vertex.i = PBVH_REF_NONE; isect_ray_tri_watertight_v3_precalc(&srd.isect_precalc, ray_normal); BKE_pbvh_raycast( diff --git a/source/blender/editors/sculpt_paint/sculpt_automasking.cc b/source/blender/editors/sculpt_paint/sculpt_automasking.cc index f05825f3594..0568552b484 100644 --- a/source/blender/editors/sculpt_paint/sculpt_automasking.cc +++ b/source/blender/editors/sculpt_paint/sculpt_automasking.cc @@ -83,9 +83,6 @@ bool SCULPT_is_automasking_mode_enabled(const Sculpt *sd, bool SCULPT_is_automasking_enabled(const Sculpt *sd, const SculptSession *ss, const Brush *br) { - if (ss && br && SCULPT_stroke_is_dynamic_topology(ss, br)) { - return false; - } if (SCULPT_is_automasking_mode_enabled(sd, br, BRUSH_AUTOMASKING_TOPOLOGY)) { return true; } diff --git a/source/blender/editors/sculpt_paint/sculpt_curvature.cc b/source/blender/editors/sculpt_paint/sculpt_curvature.cc index 1b460d032eb..5b6977a96fb 100644 --- a/source/blender/editors/sculpt_paint/sculpt_curvature.cc +++ b/source/blender/editors/sculpt_paint/sculpt_curvature.cc @@ -184,7 +184,7 @@ bool SCULPT_calc_principle_curvatures(SculptSession *ss, } if (!useAccurateSolver || !BLI_eigen_solve_selfadjoint_m3(nmat, out->ks, out->principle)) { - // do simple power solve in one direction + /* Do simple power solve in one direction. */ float t[3]; float t2[3]; @@ -238,7 +238,7 @@ void SCULPT_curvature_dir_get(SculptSession *ss, void SCULPT_curvature_begin(SculptSession *ss, struct PBVHNode *node, bool useAccurateSolver) { if (BKE_pbvh_type(ss->pbvh) != PBVH_BMESH) { - // caching only happens for bmesh for now + /* Caching only happens for bmesh for now. */ return; } diff --git a/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc b/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc index 9226f1aa6b4..56e1e9c3a8d 100644 --- a/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc +++ b/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc @@ -105,30 +105,6 @@ static float tri_voronoi_area(const float p[3], const float q[3], const float r[ } } -static float cotangent_tri_weight_v3_proj(const float n[3], - const float v1[3], - const float v2[3], - const float v3[3]) -{ - float a[3], b[3], c[3], c_len; - - sub_v3_v3v3(a, v2, v1); - sub_v3_v3v3(b, v3, v1); - - madd_v3_v3fl(a, n, -dot_v3v3(n, a)); - madd_v3_v3fl(b, n, -dot_v3v3(n, b)); - - cross_v3_v3v3(c, a, b); - - c_len = len_v3(c); - - if (c_len > FLT_EPSILON) { - return dot_v3v3(a, b) / c_len; - } - - return 0.0f; -} - void SCULPT_dyntopo_get_cotangents(SculptSession *ss, PBVHVertRef vertex, float *r_ws, @@ -208,7 +184,6 @@ void SCULPT_faces_get_cotangents(SculptSession *ss, // sculpt vemap should always be sorted in disk cycle order float totarea = 0.0; - float totw = 0.0; MeshElemMap *elem = ss->vemap + vertex.i; for (int i = 0; i < elem->count; i++) { @@ -234,8 +209,7 @@ void SCULPT_faces_get_cotangents(SculptSession *ss, float area = tri_voronoi_area(v, v1, v2); r_ws[i] = (cot1 + cot2); - totw += r_ws[i]; - + totarea += area; if (r_cot1) { @@ -307,7 +281,7 @@ void SCULPT_get_cotangents(SculptSession *ss, break; case PBVH_GRIDS: { { - // not supported, return uniform weights; + /* Not supported, return uniform weights. */ int val = SCULPT_vertex_valence_get(ss, vertex); @@ -325,7 +299,7 @@ void SCULT_dyntopo_flag_all_disk_sort(SculptSession *ss) BKE_pbvh_bmesh_flag_all_disk_sort(ss->pbvh); } -// returns true if edge disk list around vertex was sorted +/* Returns true if edge disk list around vertex was sorted. */ bool SCULPT_dyntopo_check_disk_sort(SculptSession *ss, PBVHVertRef vertex) { BMVert *v = (BMVert *)vertex.i; @@ -676,7 +650,7 @@ static void SCULPT_dynamic_topology_disable_ex( SculptSession *ss = ob->sculpt; Mesh *me = static_cast(ob->data); - /* destroy non-customdata temporary layers (which are rarely (never?) used for PBVH_BMESH) */ + /* Destroy non-customdata temporary layers (which are rarely (never?) used for PBVH_BMESH). */ BKE_sculpt_attribute_destroy_temporary_all(ob); if (ss->attrs.dyntopo_node_id_vertex) { @@ -703,8 +677,7 @@ static void SCULPT_dynamic_topology_disable_ex( /* Typically valid but with global-undo they can be nullptr, see: T36234. */ if (ss->bm) { - // PBVH now frees this - // BM_mesh_free(ss->bm); + /* PBVH now frees bmesh, just null it. */ ss->bm = nullptr; } @@ -845,42 +818,8 @@ static int dyntopo_warning_popup(bContext *C, wmOperatorType *ot, enum eDynTopoW return OPERATOR_INTERFACE; } -static bool dyntopo_supports_customdata_layers(const blender::Span layers, - int totelem) -{ - for (const CustomDataLayer &layer : layers) { - if (CD_TYPE_AS_MASK(layer.type) & CD_MASK_PROP_ALL) { - if (layer.name[0] == '\0') { - return false; - } - - if (STREQ(layer.name, ".sculpt_face_sets") && totelem > 0) { - int *fsets = static_cast(layer.data); - int fset = fsets[0]; - - /* Check if only one face set exists. */ - for (int i : IndexRange(totelem)) { - if (fsets[i] != fset) { - return false; - } - } - - return true; - } - - /* Some data is stored as generic attributes on #Mesh but in flags or field on #BMesh. */ - return BM_attribute_stored_in_bmesh_builtin(layer.name); - } - /* Some layers just encode #Mesh topology or are handled as special cases for dyntopo. */ - return ELEM(layer.type, CD_MEDGE, CD_MFACE, CD_MLOOP, CD_MPOLY, CD_PAINT_MASK, CD_ORIGINDEX); - } - - return true; -} - enum eDynTopoWarnFlag SCULPT_dynamic_topology_check(Scene *scene, Object *ob) { - Mesh *me = static_cast(ob->data); SculptSession *ss = ob->sculpt; enum eDynTopoWarnFlag flag = eDynTopoWarnFlag(0); @@ -954,816 +893,3 @@ void SCULPT_OT_dynamic_topology_toggle(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -#define MAXUVLOOPS 32 -#define MAXUVNEIGHBORS 32 - -struct UVSmoothTri; - -typedef struct UVSmoothVert { - double uv[2]; - float co[3]; // world co - BMVert *v; - double w; - int totw; - bool pinned, boundary; - BMLoop *ls[MAXUVLOOPS]; - - struct UVSmoothVert *neighbors[MAXUVNEIGHBORS]; - struct UVSmoothTri *neighbor_tris[MAXUVNEIGHBORS]; - float neighbor_weights[MAXUVNEIGHBORS]; - - int totloop, totneighbor; - float brushfade; -} UVSmoothVert; - -typedef struct UVSmoothTri { - UVSmoothVert *vs[3]; - float area2d, area3d; -} UVSmoothTri; - -#define CON_MAX_VERTS 16 -typedef struct UVSmoothConstraint { - int type; - double k; - UVSmoothVert *vs[CON_MAX_VERTS]; - UVSmoothTri *tri; - double gs[CON_MAX_VERTS][2]; - int totvert; - double params[8]; -} UVSmoothConstraint; - -enum { CON_ANGLES = 0, CON_AREA = 1 }; - -typedef struct UVSolver { - BLI_mempool *verts; - BLI_mempool *tris; - int totvert, tottri; - float snap_limit; - BLI_mempool *constraints; - GHash *vhash; - GHash *fhash; - int cd_uv; - - double totarea3d; - double totarea2d; - - double strength; - int cd_sculpt_vert; - int cd_boundary_flag; -} UVSolver; - -/*that that currently this tool is *not* threaded*/ - -typedef struct SculptUVThreadData { - SculptThreadedTaskData data; - UVSolver *solver; -} SculptUVThreadData; - -static UVSolver *uvsolver_new(int cd_uv) -{ - UVSolver *solver = (UVSolver *)MEM_callocN(sizeof(*solver), "solver"); - - solver->strength = 1.0; - solver->cd_uv = cd_uv; - solver->snap_limit = 0.0025; - - solver->verts = BLI_mempool_create(sizeof(UVSmoothVert), 0, 512, BLI_MEMPOOL_ALLOW_ITER); - solver->tris = BLI_mempool_create(sizeof(UVSmoothTri), 0, 512, BLI_MEMPOOL_ALLOW_ITER); - solver->constraints = BLI_mempool_create( - sizeof(UVSmoothConstraint), 0, 512, BLI_MEMPOOL_ALLOW_ITER); - - solver->vhash = BLI_ghash_ptr_new("uvsolver"); - solver->fhash = BLI_ghash_ptr_new("uvsolver"); - - return solver; -} - -static void uvsolver_free(UVSolver *solver) -{ - BLI_mempool_destroy(solver->verts); - BLI_mempool_destroy(solver->tris); - BLI_mempool_destroy(solver->constraints); - - BLI_ghash_free(solver->vhash, nullptr, nullptr); - BLI_ghash_free(solver->fhash, nullptr, nullptr); - - MEM_freeN(solver); -} - -void *uvsolver_calc_loop_key(UVSolver *solver, BMLoop *l) -{ - // return (void *)l->v; - float *uv = (float *)BM_ELEM_CD_GET_VOID_P(l, solver->cd_uv); - - // float u = floorf(uv[0] / solver->snap_limit) * solver->snap_limit; - // float v = floorf(uv[1] / solver->snap_limit) * solver->snap_limit; - - intptr_t x = (intptr_t)(uv[0] * 16384.0); - intptr_t y = (intptr_t)(uv[1] * 16384.0); - intptr_t key; - int boundflag = BM_ELEM_CD_GET_INT(l->v, solver->cd_boundary_flag); - - if ((boundflag & (SCULPT_BOUNDARY_SEAM | SCULPT_BOUNDARY_UV)) || - (l->e->head.hflag | l->prev->e->head.hflag) & BM_ELEM_SEAM) { - key = y * 16384LL + x; - } - else { - key = (intptr_t)l->v; - } - - return POINTER_FROM_INT(key); -} - -static UVSmoothVert *uvsolver_get_vert(UVSolver *solver, BMLoop *l) -{ - float *uv = (float *)BM_ELEM_CD_GET_VOID_P(l, solver->cd_uv); - - void *pkey = uvsolver_calc_loop_key(solver, l); - void **entry = nullptr; - UVSmoothVert *v; - - if (!BLI_ghash_ensure_p(solver->vhash, pkey, &entry)) { - v = (UVSmoothVert *)BLI_mempool_alloc(solver->verts); - memset(v, 0, sizeof(*v)); - - MSculptVert *mv = BKE_PBVH_SCULPTVERT(solver->cd_sculpt_vert, l->v); - MSculptVert *mv2 = BKE_PBVH_SCULPTVERT(solver->cd_sculpt_vert, l->prev->v); - MSculptVert *mv3 = BKE_PBVH_SCULPTVERT(solver->cd_sculpt_vert, l->next->v); - - int bound1 = BM_ELEM_CD_GET_INT(l->v, solver->cd_boundary_flag); - int bound2 = BM_ELEM_CD_GET_INT(l->prev->v, solver->cd_boundary_flag); - int bound3 = BM_ELEM_CD_GET_INT(l->next->v, solver->cd_boundary_flag); - - v->boundary = bound1 & SCULPT_BOUNDARY_SEAM; - if ((bound1 | bound2 | bound3) & SCULPT_CORNER_SHARP) { - v->pinned = true; - } - - // copy_v2_v2(v, uv); - v->uv[0] = (double)uv[0]; - v->uv[1] = (double)uv[1]; - - if (isnan(v->uv[0]) || !isfinite(v->uv[0])) { - v->uv[0] = 0.0f; - } - if (isnan(v->uv[1]) || !isfinite(v->uv[1])) { - v->uv[1] = 0.0f; - } - - copy_v3_v3(v->co, l->v->co); - v->v = l->v; - - *entry = (void *)v; - } - - v = (UVSmoothVert *)*entry; - - if (v->totloop < MAXUVLOOPS) { - v->ls[v->totloop++] = l; - } - - return v; -} - -MINLINE double area_tri_signed_v2_db(const double v1[2], const double v2[2], const double v3[2]) -{ - return 0.5 * ((v1[0] - v2[0]) * (v2[1] - v3[1]) + (v1[1] - v2[1]) * (v3[0] - v2[0])); -} - -MINLINE double area_tri_v2_db(const double v1[2], const double v2[2], const double v3[2]) -{ - return fabs(area_tri_signed_v2_db(v1, v2, v3)); -} - -void cross_tri_v3_db(double n[3], const double v1[3], const double v2[3], const double v3[3]) -{ - double n1[3], n2[3]; - - n1[0] = v1[0] - v2[0]; - n2[0] = v2[0] - v3[0]; - n1[1] = v1[1] - v2[1]; - n2[1] = v2[1] - v3[1]; - n1[2] = v1[2] - v2[2]; - n2[2] = v2[2] - v3[2]; - n[0] = n1[1] * n2[2] - n1[2] * n2[1]; - n[1] = n1[2] * n2[0] - n1[0] * n2[2]; - n[2] = n1[0] * n2[1] - n1[1] * n2[0]; -} - -double area_tri_v3_db(const double v1[3], const double v2[3], const double v3[3]) -{ - double n[3]; - cross_tri_v3_db(n, v1, v2, v3); - return len_v3_db(n) * 0.5; -} - -static UVSmoothTri *uvsolver_ensure_face(UVSolver *solver, BMFace *f) -{ - void **entry = nullptr; - - if (BLI_ghash_ensure_p(solver->fhash, (void *)f, &entry)) { - return (UVSmoothTri *)*entry; - } - - UVSmoothTri *tri = (UVSmoothTri *)BLI_mempool_alloc(solver->tris); - memset((void *)tri, 0, sizeof(*tri)); - *entry = (void *)tri; - - BMLoop *l = f->l_first; - - bool nocon = false; - int i = 0; - do { - UVSmoothVert *sv = uvsolver_get_vert(solver, l); - - if (BM_elem_flag_test(l->e, BM_ELEM_SEAM)) { - nocon = true; - } - - tri->vs[i] = sv; - - if (i > 3) { - // bad! - break; - } - - i++; - } while ((l = l->next) != f->l_first); - - double area3d = (double)area_tri_v3(tri->vs[0]->co, tri->vs[1]->co, tri->vs[2]->co); - double area2d = area_tri_signed_v2_db(tri->vs[0]->uv, tri->vs[1]->uv, tri->vs[2]->uv); - - if (fabs(area2d) < 0.0000001) { - tri->vs[0]->uv[0] -= 0.00001; - tri->vs[0]->uv[1] -= 0.00001; - - tri->vs[1]->uv[0] += 0.00001; - tri->vs[2]->uv[1] += 0.00001; - } - - solver->totarea2d += fabs(area2d); - solver->totarea3d += area3d; - - tri->area2d = area2d; - tri->area3d = area3d; - - for (int i = 0; !nocon && i < 3; i++) { - UVSmoothConstraint *con = (UVSmoothConstraint *)BLI_mempool_alloc(solver->constraints); - memset((void *)con, 0, sizeof(*con)); - - con->type = CON_ANGLES; - con->k = 1.0; - - UVSmoothVert *v0 = tri->vs[(i + 2) % 3]; - UVSmoothVert *v1 = tri->vs[i]; - UVSmoothVert *v2 = tri->vs[(i + 1) % 3]; - - con->vs[0] = v0; - con->vs[1] = v1; - con->vs[2] = v2; - con->totvert = 3; - - float t1[3], t2[3]; - - sub_v3_v3v3(t1, v0->co, v1->co); - sub_v3_v3v3(t2, v2->co, v1->co); - - normalize_v3(t1); - normalize_v3(t2); - - float th3d = saacosf(dot_v3v3(t1, t2)); - - con->params[0] = (double)th3d; - - // area constraint - con = (UVSmoothConstraint *)BLI_mempool_alloc(solver->constraints); - memset((void *)con, 0, sizeof(*con)); - - con->type = CON_AREA; - con->k = 1.0; - - con->totvert = 3; - - con->tri = tri; - - con->vs[0] = v0; - con->vs[1] = v1; - con->vs[2] = v2; - } - -#if 1 - for (int i = 0; i < 3; i++) { - UVSmoothVert *v1 = tri->vs[i]; - UVSmoothVert *v2 = tri->vs[(i + 1) % 3]; - - bool ok = true; - - for (int j = 0; j < v1->totneighbor; j++) { - if (v1->neighbors[j] == v2) { - ok = false; - break; - } - } - - ok = ok && v1->totneighbor < MAXUVNEIGHBORS && v2->totneighbor < MAXUVNEIGHBORS; - - if (!ok) { - continue; - } - - v1->neighbor_tris[v1->totneighbor] = tri; - v1->neighbors[v1->totneighbor] = v2; - v1->neighbor_weights[v1->totneighbor] = 1.0; // area3d > 0.0 ? 1.0 / area3d : 100000.0; - v1->totneighbor++; - - v2->neighbor_tris[v2->totneighbor] = tri; - v2->neighbors[v2->totneighbor] = v1; - v2->neighbor_weights[v2->totneighbor] = 1.0; // area3d > 0.0 ? 1.0 / area3d : 100000.0; - v2->totneighbor++; - } -#endif - - return tri; -} - -static double normalize_v2_db(double v[2]) -{ - double len = v[0] * v[0] + v[1] * v[1]; - - if (len < 0.0000001) { - v[0] = v[1] = 0.0; - return 0.0; - } - - len = sqrt(len); - - double mul = 1.0 / len; - - v[0] *= mul; - v[1] *= mul; - - return len; -} - -static double uvsolver_eval_constraint(UVSolver *solver, UVSmoothConstraint *con) -{ - switch (con->type) { - case CON_ANGLES: { - UVSmoothVert *v0 = con->vs[0]; - UVSmoothVert *v1 = con->vs[1]; - UVSmoothVert *v2 = con->vs[2]; - double t1[2], t2[2]; - - sub_v2_v2v2_db(t1, v0->uv, v1->uv); - sub_v2_v2v2_db(t2, v2->uv, v1->uv); - - normalize_v2_db(t1); - normalize_v2_db(t2); - - double th = saacos(dot_v2v2_db(t1, t2)); - - double wind = t1[0] * t2[1] - t1[1] * t2[0]; - - if (wind >= 0.0) { - // th = M_PI - th; - } - - return th - con->params[0]; - } - case CON_AREA: { - UVSmoothVert *v0 = con->vs[0]; - UVSmoothVert *v1 = con->vs[1]; - UVSmoothVert *v2 = con->vs[2]; - - if (con->tri->area3d == 0.0 || solver->totarea3d == 0.0) { - return 0.0; - } - - double area2d = area_tri_signed_v2_db(v0->uv, v1->uv, v2->uv); - double goal = con->tri->area3d * solver->totarea2d / solver->totarea3d; - - con->tri->area2d = area2d; - return (fabs(area2d) - goal) * 1024.0; /* avoid excesively small numbers */ - } - default: - return 0.0; - } -} - -BLI_INLINE float uvsolver_vert_weight(UVSmoothVert *sv) -{ - double w = 1.0; - - if (sv->pinned || sv->boundary || sv->brushfade == 0.0f) { - w = 100000.0; - } - else { - return 1.0 / sv->brushfade; - } - - return w; -} - -static void uvsolver_solve_begin(UVSolver *solver) -{ - UVSmoothVert *sv; - BLI_mempool_iter iter; - - BLI_mempool_iternew(solver->verts, &iter); - sv = (UVSmoothVert *)BLI_mempool_iterstep(&iter); - BMIter liter; - - for (; sv; sv = (UVSmoothVert *)BLI_mempool_iterstep(&iter)) { - BMLoop *l; - sv->pinned = false; - - BM_ITER_ELEM (l, &liter, sv->v, BM_LOOPS_OF_VERT) { - if (!BLI_ghash_haskey(solver->fhash, (void *)l->f)) { - sv->pinned = true; - } - } - } -} - -static void uvsolver_simple_relax(UVSolver *solver, float strength) -{ - BLI_mempool_iter iter; - - UVSmoothVert *sv1; - BLI_mempool_iternew(solver->verts, &iter); - - sv1 = (UVSmoothVert *)BLI_mempool_iterstep(&iter); - for (; sv1; sv1 = (UVSmoothVert *)BLI_mempool_iterstep(&iter)) { - double uv[2] = {0.0, 0.0}; - double tot = 0.0; - int totneighbor = 0; - float strength2 = strength; - - if (!sv1->totneighbor || sv1->pinned) { - continue; - } - - bool lastsign; - - for (int i = 0; i < sv1->totneighbor; i++) { - UVSmoothVert *sv2 = sv1->neighbors[i]; - - if (!sv2 || (sv1->boundary && !sv2->boundary)) { - continue; - } - - bool sign = sv1->neighbor_tris[i]->area2d < 0.0f; - - // try to unfold folded uvs - if (totneighbor > 0 && sign != lastsign) { - strength2 = 1.0; - } - - lastsign = sign; - - double w = (double)sv1->neighbor_weights[i]; - - uv[0] += sv2->uv[0] * w; - uv[1] += sv2->uv[1] * w; - - tot += w; - totneighbor++; - } - - if (totneighbor < 2.0) { - continue; - } - - uv[0] /= tot; - uv[1] /= tot; - - sv1->uv[0] += (uv[0] - sv1->uv[0]) * strength2; - sv1->uv[1] += (uv[1] - sv1->uv[1]) * strength2; - } - - // update real uvs - - const int cd_uv = solver->cd_uv; - - BLI_mempool_iternew(solver->verts, &iter); - UVSmoothVert *sv = (UVSmoothVert *)BLI_mempool_iterstep(&iter); - for (; sv; sv = (UVSmoothVert *)BLI_mempool_iterstep(&iter)) { - for (int i = 0; i < sv->totloop; i++) { - BMLoop *l = sv->ls[i]; - float *uv = (float *)BM_ELEM_CD_GET_VOID_P(l, cd_uv); - - uv[0] = (float)sv->uv[0]; - uv[1] = (float)sv->uv[1]; - } - } -} - -static float uvsolver_solve_step(UVSolver *solver) -{ - BLI_mempool_iter iter; - - if (solver->strength < 0) { - uvsolver_simple_relax(solver, -solver->strength); - return 0.0f; - } - else { - uvsolver_simple_relax(solver, solver->strength * 0.1f); - } - - double error = 0.0; - - const double eval_limit = 0.00001; - const double df = 0.0001; - int totcon = 0; - - BLI_mempool_iternew(solver->constraints, &iter); - UVSmoothConstraint *con = (UVSmoothConstraint *)BLI_mempool_iterstep(&iter); - for (; con; con = (UVSmoothConstraint *)BLI_mempool_iterstep(&iter)) { - double r1 = uvsolver_eval_constraint(solver, con); - - if (fabs(r1) < eval_limit) { - totcon++; - continue; - } - - error += fabs(r1); - totcon++; - - double totg = 0.0; - double totw = 0.0; - - for (int i = 0; i < con->totvert; i++) { - UVSmoothVert *sv = con->vs[i]; - - for (int j = 0; j < 2; j++) { - double orig = sv->uv[j]; - sv->uv[j] += df; - - double r2 = uvsolver_eval_constraint(solver, con); - double g = (r2 - r1) / df; - - con->gs[i][j] = g; - totg += g * g; - - sv->uv[j] = orig; - - totw += uvsolver_vert_weight(sv); - } - } - - if (totg < eval_limit) { - continue; - } - - r1 *= -0.75 * con->k / totg; - - if (totw == 0.0) { - continue; - } - - totw = 1.0 / totw; - - for (int i = 0; i < con->totvert; i++) { - UVSmoothVert *sv = con->vs[i]; - - *(int *)BM_ELEM_CD_GET_VOID_P(sv->v, - solver->cd_boundary_flag) |= SCULPT_BOUNDARY_NEEDS_UPDATE; - - double w = uvsolver_vert_weight(sv) * totw; - w = MIN2(w, 1.0); - w = 1.0; - - for (int j = 0; j < 2; j++) { - double off = r1 * con->gs[i][j] * w; - - CLAMP(off, -0.1, 0.1); - sv->uv[j] += off; - } - } - } - - // update real uvs - - const int cd_uv = solver->cd_uv; - - BLI_mempool_iternew(solver->verts, &iter); - UVSmoothVert *sv = (UVSmoothVert *)BLI_mempool_iterstep(&iter); - for (; sv; sv = (UVSmoothVert *)BLI_mempool_iterstep(&iter)) { - for (int i = 0; i < sv->totloop; i++) { - BMLoop *l = sv->ls[i]; - float *uv = (float *)BM_ELEM_CD_GET_VOID_P(l, cd_uv); - - float fac = solver->strength * sv->brushfade; - - uv[0] += ((float)sv->uv[0] - uv[0]) * fac; - uv[1] += ((float)sv->uv[1] - uv[1]) * fac; - } - } - - return (float)error / (float)totcon; -} - -static void sculpt_uv_brush_cb(void *__restrict userdata, - const int n, - const TaskParallelTLS *__restrict tls) -{ - SculptUVThreadData *data1 = (SculptUVThreadData *)userdata; - SculptThreadedTaskData *data = &data1->data; - SculptSession *ss = data->ob->sculpt; - // const Brush *brush = data->brush; - // const float *offset = data->offset; - - SculptBrushTest test; - SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init(ss, &test); - - PBVHNode *node = data->nodes[n]; - TableGSet *faces = BKE_pbvh_bmesh_node_faces(node); - BMFace *f; - const int cd_uv = CustomData_get_offset(&ss->bm->ldata, CD_MLOOPUV); - - if (cd_uv < 0) { - return; // no uv layers - } - - BKE_pbvh_node_mark_update_color(node); - - TGSET_ITER (f, faces) { - BMLoop *l = f->l_first; - // float mask = 0.0f; - float cent[3] = {0}; - int tot = 0; - - // uvsolver_get_vert - do { - add_v3_v3(cent, l->v->co); - tot++; - } while ((l = l->next) != f->l_first); - - mul_v3_fl(cent, 1.0f / (float)tot); - - if (!sculpt_brush_test_sq_fn(&test, cent)) { - continue; - } - - BM_log_face_modified(ss->bm, ss->bm_log, f); - uvsolver_ensure_face(data1->solver, f); - - do { - BMIter iter; - BMLoop *l2; - int tot2 = 0; - float uv[2] = {0}; - bool ok = true; - UVSmoothVert *lastv = nullptr; - - BM_ITER_ELEM (l2, &iter, l->v, BM_LOOPS_OF_VERT) { - if (l2->v != l->v) { - l2 = l2->next; - } - - UVSmoothVert *sv = uvsolver_get_vert(data1->solver, l2); - - if (lastv && lastv != sv) { - ok = false; - lastv->boundary = true; - sv->boundary = true; - } - - lastv = sv; - - float *luv = (float *)BM_ELEM_CD_GET_VOID_P(l2, cd_uv); - - add_v2_v2(uv, luv); - tot2++; - - if (BM_elem_flag_test(l2->e, BM_ELEM_SEAM)) { - ok = false; - sv->boundary = true; - } - } - - ok = ok && tot2; - - if (ok) { - mul_v2_fl(uv, 1.0f / (float)tot2); - - BM_ITER_ELEM (l2, &iter, l->v, BM_LOOPS_OF_VERT) { - if (l2->v != l->v) { - l2 = l2->next; - } - - float *luv = (float *)BM_ELEM_CD_GET_VOID_P(l2, cd_uv); - - if (len_v2v2(luv, uv) < 0.02) { - copy_v2_v2(luv, uv); - } - } - } - } while ((l = l->next) != f->l_first); - -#if 0 - do { - if (!sculpt_brush_test_sq_fn(&test, l->v->co)) { - continue; - } - - if (cd_mask >= 0) { - mask = BM_ELEM_CD_GET_FLOAT(l->v, cd_mask); - } - - PBVHVertRef vertex = {(intptr_t)l->v}; - - float direction2[3]; - const float fade = - bstrength * - SCULPT_brush_strength_factor( - ss, brush, vd.co, sqrtf(test.dist), nullptr, l->v->no, mask, vertex, thread_id) * - ss->cache->pressure; - - } while ((l = l->next) != f->l_first); -#endif - } - TGSET_ITER_END; -} - -void SCULPT_uv_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) -{ - SculptSession *ss = ob->sculpt; - Brush *brush = BKE_paint_brush(&sd->paint); - float offset[3]; - - if (!ss->bm || BKE_pbvh_type(ss->pbvh) != PBVH_BMESH) { - // dyntopo only - return; - } - - const int cd_uv = CustomData_get_offset(&ss->bm->ldata, CD_MLOOPUV); - if (cd_uv < 0) { - return; // no uv layer? - } - - // add undo log subentry - BM_log_entry_add_ex(ss->bm, ss->bm_log, true); - - BKE_curvemapping_init(brush->curve); - - UVSolver *solver = uvsolver_new(cd_uv); - - solver->cd_boundary_flag = ss->attrs.boundary_flags->bmesh_cd_offset; - solver->cd_sculpt_vert = ss->cd_sculpt_vert; - // solver->strength = powf(fabs(ss->cache->bstrength), 0.25) * signf(ss->cache->bstrength); - solver->strength = ss->cache->bstrength; - - /* Threaded loop over nodes. */ - SculptUVThreadData data = {}; - data.solver = solver; - data.data.sd = sd; - data.data.ob = ob; - data.data.brush = brush; - data.data.nodes = nodes; - data.data.offset = offset; - - TaskParallelSettings settings; - - // for now, be single-threaded - BKE_pbvh_parallel_range_settings(&settings, false, totnode); - BLI_task_parallel_range(0, totnode, &data, sculpt_uv_brush_cb, &settings); - - uvsolver_solve_begin(solver); - - SculptBrushTest test; - SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init(ss, &test); - - BLI_mempool_iter iter; - BLI_mempool_iternew(solver->verts, &iter); - UVSmoothVert *sv = (UVSmoothVert *)BLI_mempool_iterstep(&iter); - - AutomaskingNodeData automask_data = {}; - automask_data.have_orig_data = false; - - for (; sv; sv = (UVSmoothVert *)BLI_mempool_iterstep(&iter)) { - if (!sculpt_brush_test_sq_fn(&test, sv->v->co)) { - sv->brushfade = 0.0f; - continue; - } - - PBVHVertRef vertex = {(intptr_t)sv->v}; - sv->brushfade = SCULPT_brush_strength_factor(ss, - brush, - sv->v->co, - sqrtf(test.dist), - nullptr, - sv->v->no, - 0.0f, - vertex, - 0, - &automask_data); - } - - for (int i = 0; i < 5; i++) { - uvsolver_solve_step(solver); - } - - // tear down solver - uvsolver_free(solver); -} diff --git a/source/blender/editors/sculpt_paint/sculpt_face_set.cc b/source/blender/editors/sculpt_paint/sculpt_face_set.cc index 9e7d0846a1b..e0b4d9efc23 100644 --- a/source/blender/editors/sculpt_paint/sculpt_face_set.cc +++ b/source/blender/editors/sculpt_paint/sculpt_face_set.cc @@ -286,9 +286,9 @@ static int new_fset_apply_curve(SculptSession *ss, return new_fset; } -void do_draw_face_sets_brush_task_cb_ex(void *__restrict userdata, - const int n, - const TaskParallelTLS *__restrict tls) +ATTR_NO_OPT void do_draw_face_sets_brush_task_cb_ex(void *__restrict userdata, + const int n, + const TaskParallelTLS *__restrict tls) { SculptFaceSetDrawData *data = (SculptFaceSetDrawData *)userdata; SculptSession *ss = data->ob->sculpt; @@ -640,7 +640,10 @@ static void do_relax_face_sets_brush_task_cb_ex(void *__restrict userdata, BKE_pbvh_vertex_iter_end; } -void SCULPT_do_draw_face_sets_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) +ATTR_NO_OPT void SCULPT_do_draw_face_sets_brush(Sculpt *sd, + Object *ob, + PBVHNode **nodes, + int totnode) { SculptSession *ss = ob->sculpt; Brush *brush = ss->cache->brush ? ss->cache->brush : BKE_paint_brush(&sd->paint); diff --git a/source/blender/editors/sculpt_paint/sculpt_smooth.cc b/source/blender/editors/sculpt_paint/sculpt_smooth.cc index 53c1ef9dafb..4c60ec650c7 100644 --- a/source/blender/editors/sculpt_paint/sculpt_smooth.cc +++ b/source/blender/editors/sculpt_paint/sculpt_smooth.cc @@ -306,23 +306,6 @@ void SCULPT_bmesh_four_neighbor_average(SculptSession *ss, /* fac is a measure of how orthogonal or parallel the edge is * relative to the direction. */ float fac = dot_v3v3(vec, dir); -#ifdef SCULPT_DIAGONAL_EDGE_MARKS - float th = fabsf(saacos(fac)) / M_PI + 0.5f; - th -= floorf(th); - - const float limit = 0.045; - - if (fabsf(th - 0.25) < limit || fabsf(th - 0.75) < limit) { - BMEdge enew = *e, eold = *e; - - enew.head.hflag &= ~BM_ELEM_DRAW; - // enew.head.hflag |= BM_ELEM_SEAM; // XXX debug - - atomic_cas_int64((intptr_t *)(&e->head.index), - *(intptr_t *)(&eold.head.index), - *(intptr_t *)(&enew.head.index)); - } -#endif fac = fac * fac - 0.5f; fac *= fac; @@ -335,7 +318,7 @@ void SCULPT_bmesh_four_neighbor_average(SculptSession *ss, tot_co += fac; } - /* In case vert has no Edge s. */ + /* In case vertex has no edges. */ if (tot_co > 0.0f) { mul_v3_v3fl(avg, avg_co, 1.0f / tot_co); -- 2.30.2 From 6064c650e283b4e2f91a6dfa483cae80ea6270be Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Sun, 19 Mar 2023 03:25:40 -0700 Subject: [PATCH 038/279] temp-sculpt-dyntopo: Fix bugs in edit face sets operator --- .../editors/sculpt_paint/sculpt_face_set.cc | 29 ++++++++++++++----- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/source/blender/editors/sculpt_paint/sculpt_face_set.cc b/source/blender/editors/sculpt_paint/sculpt_face_set.cc index f2a25e83f89..10e89b46058 100644 --- a/source/blender/editors/sculpt_paint/sculpt_face_set.cc +++ b/source/blender/editors/sculpt_paint/sculpt_face_set.cc @@ -10,6 +10,7 @@ #include "BLI_blenlib.h" #include "BLI_compiler_attrs.h" #include "BLI_hash.h" +#include "BLI_index_range.hh" #include "BLI_math.h" #include "BLI_math_vector.hh" #include "BLI_math_vector_types.hh" @@ -64,6 +65,7 @@ #include #include +using blender::IndexRange; using blender::Vector; static int sculpt_face_material_get(SculptSession *ss, PBVHFaceRef face) @@ -1705,6 +1707,20 @@ static void sculpt_face_set_grow_bmesh(Object *ob, } } +static void rebuild_pbvh_draw_buffers(PBVH *pbvh) +{ + PBVHNode **nodes; + int nodes_num; + + BKE_pbvh_search_gather(pbvh, nullptr, nullptr, &nodes, &nodes_num); + for (int i : IndexRange(nodes_num)) { + BKE_pbvh_node_mark_update(nodes[i]); + BKE_pbvh_node_mark_rebuild_draw(nodes[i]); + } + + MEM_SAFE_FREE(nodes); +} + static void sculpt_face_set_grow(Object *ob, SculptSession *ss, const int *prev_face_sets, @@ -1739,6 +1755,8 @@ static void sculpt_face_set_grow(Object *ob, } } } + + rebuild_pbvh_draw_buffers(ss->pbvh); } static void sculpt_face_set_fill_component(Object *ob, @@ -1869,6 +1887,8 @@ static void sculpt_face_set_shrink(Object *ob, } } } + + rebuild_pbvh_draw_buffers(ss->pbvh); } static bool check_single_face_set(SculptSession *ss, const bool check_visible_only) @@ -2920,14 +2940,7 @@ ATTR_NO_OPT static int sculpt_face_set_edit_modal(bContext *C, BKE_pbvh_vert_tag_update_normal(ss->pbvh, BKE_pbvh_make_vref(idx)); } - PBVHNode **nodes; - int totnode; - BKE_pbvh_search_gather(ss->pbvh, nullptr, nullptr, &nodes, &totnode); - for (int i = 0; i < totnode; i++) { - BKE_pbvh_node_mark_update(nodes[i]); - BKE_pbvh_node_mark_rebuild_draw(nodes[i]); - } - MEM_SAFE_FREE(nodes); + rebuild_pbvh_draw_buffers(ss->pbvh); } else { BM_mesh_elem_index_ensure(ss->bm, BM_VERT | BM_EDGE | BM_FACE); -- 2.30.2 From c2a2c8265c5f64c00721134b3b9b95f2674d9da5 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Wed, 22 Mar 2023 17:53:48 -0700 Subject: [PATCH 039/279] temp-sculpt-dyntopo: Fix face set init operator --- source/blender/editors/sculpt_paint/sculpt.cc | 11 ++++++++--- .../blender/editors/sculpt_paint/sculpt_face_set.cc | 5 ----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/source/blender/editors/sculpt_paint/sculpt.cc b/source/blender/editors/sculpt_paint/sculpt.cc index f2d18cf6afd..7aa23a6fb7a 100644 --- a/source/blender/editors/sculpt_paint/sculpt.cc +++ b/source/blender/editors/sculpt_paint/sculpt.cc @@ -7150,8 +7150,10 @@ void SCULPT_stroke_id_next(Object *ob) int SCULPT_face_set_get(const SculptSession *ss, PBVHFaceRef face) { switch (BKE_pbvh_type(ss->pbvh)) { - case PBVH_BMESH: - return 0; + case PBVH_BMESH: { + BMFace *f = reinterpret_cast(face.i); + return BM_ELEM_CD_GET_INT(f, ss->cd_faceset_offset); + } case PBVH_FACES: case PBVH_GRIDS: return ss->face_sets[face.i]; @@ -7165,8 +7167,11 @@ int SCULPT_face_set_get(const SculptSession *ss, PBVHFaceRef face) void SCULPT_face_set_set(SculptSession *ss, PBVHFaceRef face, int fset) { switch (BKE_pbvh_type(ss->pbvh)) { - case PBVH_BMESH: + case PBVH_BMESH: { + BMFace *f = reinterpret_cast(face.i); + BM_ELEM_CD_SET_INT(f, ss->cd_faceset_offset, fset); break; + } case PBVH_FACES: case PBVH_GRIDS: ss->face_sets[face.i] = fset; diff --git a/source/blender/editors/sculpt_paint/sculpt_face_set.cc b/source/blender/editors/sculpt_paint/sculpt_face_set.cc index 10e89b46058..40e566beb0e 100644 --- a/source/blender/editors/sculpt_paint/sculpt_face_set.cc +++ b/source/blender/editors/sculpt_paint/sculpt_face_set.cc @@ -1183,11 +1183,6 @@ static int sculpt_face_set_init_exec(bContext *C, wmOperator *op) BKE_sculpt_update_object_for_edit(depsgraph, ob, true, false, false); ss->face_sets = BKE_sculpt_face_sets_ensure(ob); - /* Dyntopo not supported. */ - if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) { - return OPERATOR_CANCELLED; - } - PBVH *pbvh = ob->sculpt->pbvh; PBVHNode **nodes; int totnode; -- 2.30.2 From ec76d621f639639e132cb9430b501784669c379e Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Wed, 22 Mar 2023 18:48:32 -0700 Subject: [PATCH 040/279] temp-sculpt-dyntopo: Fix smooth boundary/corner handling * Face set projection now works. * Hard edge mode (which forcibly sets face set projection to 0) * Exposed settings for both in ui. --- .../startup/bl_ui/properties_paint_common.py | 12 +++- scripts/startup/bl_ui/space_view3d_toolbar.py | 4 +- .../blenloader/intern/versioning_300.cc | 6 ++ .../editors/sculpt_paint/sculpt_intern.hh | 2 +- .../editors/sculpt_paint/sculpt_smooth.cc | 72 ++++++++++++------- source/blender/makesrna/intern/rna_brush.c | 2 +- 6 files changed, 66 insertions(+), 32 deletions(-) diff --git a/scripts/startup/bl_ui/properties_paint_common.py b/scripts/startup/bl_ui/properties_paint_common.py index adc6fa9e31e..a435c4be8d8 100644 --- a/scripts/startup/bl_ui/properties_paint_common.py +++ b/scripts/startup/bl_ui/properties_paint_common.py @@ -552,7 +552,15 @@ def brush_settings(layout, context, brush, popover=False): layout.prop(brush.dyntopo, "disabled") - # auto_smooth_factor and use_inverse_smooth_pressure + if capabilities.has_auto_smooth or brush.sculpt_tool == "SMOOTH": + UnifiedPaintPanel.prop_unified( + layout, + context, + brush, + "autosmooth_fset_slide", + slider=True, + ) + if capabilities.has_auto_smooth: UnifiedPaintPanel.prop_unified( layout, @@ -562,7 +570,7 @@ def brush_settings(layout, context, brush, popover=False): pressure_name="use_inverse_smooth_pressure", slider=True, ) - + if capabilities.has_auto_smooth or brush.sculpt_tool == "SMOOTH": layout.prop(brush, "use_weighted_smooth") diff --git a/scripts/startup/bl_ui/space_view3d_toolbar.py b/scripts/startup/bl_ui/space_view3d_toolbar.py index b425a246111..69d4f0d02eb 100644 --- a/scripts/startup/bl_ui/space_view3d_toolbar.py +++ b/scripts/startup/bl_ui/space_view3d_toolbar.py @@ -896,8 +896,6 @@ class VIEW3D_PT_sculpt_dyntopo(Panel, View3DPaintPanel): col.prop(sculpt, "use_smooth_shading") col.prop(sculpt, "use_flat_vcol_shading") - - class VIEW3D_PT_sculpt_voxel_remesh(Panel, View3DPaintPanel): bl_context = ".sculpt_mode" # dot on purpose (access from topbar) bl_label = "Remesh" @@ -956,7 +954,7 @@ class VIEW3D_PT_sculpt_options(Panel, View3DPaintPanel): col.prop(sculpt, "use_deform_only") col.prop(tool_settings, "show_origco") col.prop(context.object.data, "sculpt_ignore_uvs") - + col.prop(tool_settings.unified_paint_settings, "hard_edge_mode") class VIEW3D_PT_sculpt_options_gravity(Panel, View3DPaintPanel): bl_context = ".sculpt_mode" # dot on purpose (access from topbar) diff --git a/source/blender/blenloader/intern/versioning_300.cc b/source/blender/blenloader/intern/versioning_300.cc index 3272cfe3c3c..6dfe9cd6bff 100644 --- a/source/blender/blenloader/intern/versioning_300.cc +++ b/source/blender/blenloader/intern/versioning_300.cc @@ -4148,6 +4148,12 @@ void blo_do_versions_300(FileData *fd, Library * /*lib*/, Main *bmain) } } + if (!DNA_struct_elem_find(fd->filesdna, "Brush", "float", "autosmooth_fset_slide")) { + LISTBASE_FOREACH (Brush *, brush, &bmain->brushes) { + brush->autosmooth_fset_slide = 1.0f; + } + } + if (!DNA_struct_elem_find(fd->filesdna, "Brush", "DynTopoSettings", "dyntopo")) { LISTBASE_FOREACH (Brush *, brush, &bmain->brushes) { if (ELEM(brush->sculpt_tool, diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.hh b/source/blender/editors/sculpt_paint/sculpt_intern.hh index 9f6a872883a..a88d9e31596 100644 --- a/source/blender/editors/sculpt_paint/sculpt_intern.hh +++ b/source/blender/editors/sculpt_paint/sculpt_intern.hh @@ -1889,7 +1889,7 @@ void SCULPT_neighbor_coords_average_interior(SculptSession *ss, BLI_INLINE bool SCULPT_get_fset_projection(SculptSession *ss, float fset_projection) { if (ss->hard_edge_mode) { - return 1.0f; + return 0.0f; } return fset_projection; diff --git a/source/blender/editors/sculpt_paint/sculpt_smooth.cc b/source/blender/editors/sculpt_paint/sculpt_smooth.cc index 4c60ec650c7..ac195e72205 100644 --- a/source/blender/editors/sculpt_paint/sculpt_smooth.cc +++ b/source/blender/editors/sculpt_paint/sculpt_smooth.cc @@ -9,6 +9,7 @@ #include "BLI_alloca.h" #include "BLI_math.h" +#include "BLI_math_vector_types.hh" #include "BLI_task.h" #include "BLI_vector.hh" @@ -26,6 +27,8 @@ #include #include +using blender::float2; +using blender::float3; using blender::Vector; ATTR_NO_OPT static void SCULPT_neighbor_coords_average_interior_ex(SculptSession *ss, @@ -37,7 +40,7 @@ ATTR_NO_OPT static void SCULPT_neighbor_coords_average_interior_ex(SculptSession eSculptBoundary bound_type, eSculptCorner corner_type) { - float avg[3] = {0.0f, 0.0f, 0.0f}; + float3 avg(0.0f, 0.0f, 0.0f); int neighbor_count = 0; const eSculptBoundary is_boundary = SCULPT_vertex_is_boundary(ss, vertex, bound_type); @@ -48,10 +51,13 @@ ATTR_NO_OPT static void SCULPT_neighbor_coords_average_interior_ex(SculptSession } float *areas = nullptr; + float3 no, co; + SCULPT_vertex_normal_get(ss, vertex, no); + co = SCULPT_vertex_co_get(ss, vertex); if (weighted) { const int valence = SCULPT_vertex_valence_get(ss, vertex); - areas = (float *)BLI_array_alloca(areas, valence); + areas = reinterpret_cast(BLI_array_alloca(areas, valence)); BKE_pbvh_get_vert_face_areas(ss->pbvh, vertex, areas, valence); } @@ -62,16 +68,26 @@ ATTR_NO_OPT static void SCULPT_neighbor_coords_average_interior_ex(SculptSession neighbor_count++; float w = weighted ? areas[ni.i] : 1.0f; + const eSculptBoundary is_boundary2 = SCULPT_vertex_is_boundary(ss, ni.vertex, bound_type); + if (is_boundary) { /* Boundary vertices use only other boundary vertices. */ - if (SCULPT_vertex_is_boundary(ss, ni.vertex, bound_type)) { - madd_v3_v3fl(avg, SCULPT_vertex_co_get(ss, ni.vertex), w); + if (is_boundary & is_boundary2) { + avg += float3(SCULPT_vertex_co_get(ss, ni.vertex)) * w; + total += w; + } + else if ((is_boundary2 & SCULPT_BOUNDARY_FACE_SET) != + (is_boundary & SCULPT_BOUNDARY_FACE_SET)) { + /* Project to plane. */ + float3 t1 = float3(SCULPT_vertex_co_get(ss, ni.vertex)) - co; + + avg += (co + no * dot_v3v3(t1, no) * fset_projection) * w; total += w; } } else { /* Interior vertices use all neighbors. */ - madd_v3_v3fl(avg, SCULPT_vertex_co_get(ss, ni.vertex), w); + avg += float3(SCULPT_vertex_co_get(ss, ni.vertex)) * w; total += w; } } @@ -89,16 +105,11 @@ ATTR_NO_OPT static void SCULPT_neighbor_coords_average_interior_ex(SculptSession return; } - float no[3]; - const float *co = SCULPT_vertex_co_get(ss, vertex); - SCULPT_vertex_normal_get(ss, vertex, no); - - mul_v3_fl(avg, 1.0f / (float)total); - sub_v3_v3(avg, co); + /* Project to plane if desired. */ + avg = avg / (float)total - co; float t = dot_v3v3(avg, no); + avg += no * t * projection + co; - madd_v3_v3fl(avg, no, -t * projection); - add_v3_v3(avg, co); copy_v3_v3(result, avg); } @@ -210,7 +221,7 @@ void SCULPT_bmesh_four_neighbor_average(SculptSession *ss, PBVHVertRef vertex = {(intptr_t)v}; int val = SCULPT_vertex_valence_get(ss, vertex); - areas = BLI_array_alloca(areas, val * 2); + areas = (float *)BLI_array_alloca(areas, val * 2); BKE_pbvh_get_vert_face_areas(ss->pbvh, vertex, areas, val); float totarea = 0.0f; @@ -572,17 +583,17 @@ ATTR_NO_OPT static void do_smooth_brush_task_cb_ex(void *__restrict userdata, SCULPT_automasking_node_update(ss, &automask_data, &vd); - const float fade = bstrength * SCULPT_brush_strength_factor( - ss, - brush, - vd.co, - sqrtf(test.dist), - vd.no, - vd.fno, - smooth_mask ? 0.0f : (vd.mask ? *vd.mask : 0.0f), - vd.vertex, - thread_id, - &automask_data); + float fade = bstrength * + SCULPT_brush_strength_factor(ss, + brush, + vd.co, + sqrtf(test.dist), + vd.no, + vd.fno, + smooth_mask ? 0.0f : (vd.mask ? *vd.mask : 0.0f), + vd.vertex, + thread_id, + &automask_data); if (smooth_mask) { float val = SCULPT_neighbor_mask_average(ss, vd.vertex) - *vd.mask; val *= fade * bstrength; @@ -595,6 +606,17 @@ ATTR_NO_OPT static void do_smooth_brush_task_cb_ex(void *__restrict userdata, copy_v3_v3(oldco, vd.co); SCULPT_vertex_normal_get(ss, vd.vertex, oldno); + eSculptCorner is_corner = SCULPT_vertex_is_corner( + ss, vd.vertex, SCULPT_CORNER_MESH | SCULPT_CORNER_FACE_SET | SCULPT_CORNER_SHARP); + + if (is_corner & (SCULPT_CORNER_MESH | SCULPT_CORNER_SHARP)) { + continue; + } + + if (is_corner & SCULPT_CORNER_FACE_SET) { + fade *= fset_projection; + } + float avg[3], val[3]; SCULPT_neighbor_coords_average_interior( ss, avg, vd.vertex, projection, fset_projection, weighted); diff --git a/source/blender/makesrna/intern/rna_brush.c b/source/blender/makesrna/intern/rna_brush.c index 84efd03f2cf..ddef2b6c852 100644 --- a/source/blender/makesrna/intern/rna_brush.c +++ b/source/blender/makesrna/intern/rna_brush.c @@ -3368,7 +3368,7 @@ static void rna_def_brush(BlenderRNA *brna) RNA_def_property_range(prop, -2.0f, 2.0f); RNA_def_property_ui_range(prop, 0.0f, 1.0f, 0.001, 3); RNA_def_property_ui_text( - prop, "Face Set Slide", "Slide face set boundaries instead of sharpening them"); + prop, "Face Set Projection", "Slide face set boundaries instead of sharpening them"); RNA_def_property_update(prop, 0, "rna_Brush_update"); prop = RNA_def_property(srna, "vcol_boundary_exponent", PROP_FLOAT, PROP_FACTOR); -- 2.30.2 From a2f908ec6db8de6a575afeca99188d0b39304353 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Sat, 25 Mar 2023 11:54:48 -0700 Subject: [PATCH 041/279] temp-sculpt-dyntopo: Fix undo bug --- source/blender/bmesh/intern/bmesh_log.cc | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/source/blender/bmesh/intern/bmesh_log.cc b/source/blender/bmesh/intern/bmesh_log.cc index c67c39275f4..8abf3964f15 100644 --- a/source/blender/bmesh/intern/bmesh_log.cc +++ b/source/blender/bmesh/intern/bmesh_log.cc @@ -354,7 +354,7 @@ template constexpr char get_elem_type() } struct BMLogEntry { - struct BMLogEntry *next = nullptr, *prev = nullptr; + BMLogEntry *next = nullptr, *prev = nullptr; Vector sets; LogElemAlloc vpool; @@ -365,6 +365,7 @@ struct BMLogEntry { BMIdMap *idmap = nullptr; BMLog *log = nullptr; + bool dead = false; ATTR_NO_OPT BMLogEntry(BMIdMap *_idmap, CustomData *src_vdata, @@ -386,6 +387,8 @@ struct BMLogEntry { ATTR_NO_OPT ~BMLogEntry() { + dead = true; + for (BMLogSetBase *set : sets) { switch (set->type) { case LOG_SET_DIFF: @@ -1627,15 +1630,25 @@ bool BM_log_entry_drop(BMLogEntry *entry) { printf("%s: Freeing log entry %p\n", __func__, entry); + if (!entry->log) { + printf("%s: error, missing bm log!\n", __func__); + } + if (entry->prev) { entry->prev->next = entry->next; } if (entry->next) { entry->next->prev = entry->prev; } - - if (entry->log && entry == entry->log->current_entry) { - entry->log->current_entry = entry->prev; + + if (entry->log) { + if (entry == entry->log->current_entry) { + entry->log->current_entry = entry->prev; + } + + if (entry == entry->log->first_entry) { + entry->log->first_entry = entry->next; + } } MEM_delete(entry); -- 2.30.2 From 1889224bd05a0c2c76195a8c85b66ea5ad50a55f Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Sat, 25 Mar 2023 13:46:45 -0700 Subject: [PATCH 042/279] temp-sculpt-dyntopo: Fix visibility bug PBVH_BMESH no longer tries to use a .hide_poly layer instead of BM_ELEM_HIDDEN, this is something that will have to happen inside of the BMesh code itself. --- source/blender/blenkernel/BKE_paint.h | 3 +- source/blender/blenkernel/BKE_pbvh.h | 3 +- source/blender/blenkernel/intern/paint.cc | 53 +++++-------------- source/blender/blenkernel/intern/pbvh.c | 7 +-- source/blender/blenkernel/intern/pbvh.cc | 7 +-- .../blender/blenkernel/intern/pbvh_bmesh.cc | 10 +--- .../blender/blenkernel/intern/pbvh_intern.hh | 3 +- .../editors/sculpt_paint/paint_hide.cc | 18 ++++--- source/blender/editors/sculpt_paint/sculpt.cc | 27 +++------- .../editors/sculpt_paint/sculpt_dyntopo.cc | 8 +-- 10 files changed, 44 insertions(+), 95 deletions(-) diff --git a/source/blender/blenkernel/BKE_paint.h b/source/blender/blenkernel/BKE_paint.h index 6ac14207145..c641e8e3baa 100644 --- a/source/blender/blenkernel/BKE_paint.h +++ b/source/blender/blenkernel/BKE_paint.h @@ -233,7 +233,7 @@ bool paint_is_face_hidden(const struct MLoopTri *lt, const bool *hide_poly); */ bool paint_is_grid_face_hidden(const unsigned int *grid_hidden, int gridsize, int x, int y); /** - * Return true if all vertices in the face are visible, false otherwise. + * Return true if face is visible. */ bool paint_is_bmesh_face_hidden(struct BMFace *f); @@ -813,7 +813,6 @@ typedef struct SculptSession { int cd_vert_mask_offset; int cd_faceset_offset; int cd_face_areas; - int cd_hide_poly; int totuv; diff --git a/source/blender/blenkernel/BKE_pbvh.h b/source/blender/blenkernel/BKE_pbvh.h index 60155d990d3..94be40fe2d1 100644 --- a/source/blender/blenkernel/BKE_pbvh.h +++ b/source/blender/blenkernel/BKE_pbvh.h @@ -481,7 +481,6 @@ void BKE_pbvh_update_offsets(PBVH *pbvh, const int cd_face_node_offset, const int cd_sculpt_vert, const int cd_face_areas, - const int cd_hide_poly, const int cd_boudnary_flags); void BKE_pbvh_update_bmesh_offsets(PBVH *pbvh, int cd_vert_node_offset, int cd_face_node_offset); @@ -1002,7 +1001,7 @@ typedef struct PBVHFaceIter { int verts_size_; int bm_faces_iter_; const struct TableGSet *bm_faces_; - int cd_hide_poly_, cd_face_set_; + int cd_face_set_; bool *hide_poly_; int *face_sets_; const struct MPoly *polys_; diff --git a/source/blender/blenkernel/intern/paint.cc b/source/blender/blenkernel/intern/paint.cc index edd387c74f6..ca82c676a06 100644 --- a/source/blender/blenkernel/intern/paint.cc +++ b/source/blender/blenkernel/intern/paint.cc @@ -1295,17 +1295,7 @@ bool paint_is_grid_face_hidden(const uint *grid_hidden, int gridsize, int x, int bool paint_is_bmesh_face_hidden(BMFace *f) { - BMLoop *l_iter; - BMLoop *l_first; - - l_iter = l_first = BM_FACE_FIRST_LOOP(f); - do { - if (BM_elem_flag_test(l_iter->v, BM_ELEM_HIDDEN)) { - return true; - } - } while ((l_iter = l_iter->next) != l_first); - - return false; + return BM_elem_flag_test(f, BM_ELEM_HIDDEN); } float paint_grid_paint_mask(const GridPaintMask *gpm, uint level, uint x, uint y) @@ -1472,19 +1462,15 @@ static void sculptsession_bm_to_me_update_data_only(Object *ob, bool reorder) { SculptSession *ss = ob->sculpt; - if (ss->bm) { - if (ob->data) { - // Mesh *me = BKE_object_get_original_mesh(ob); + if (ss->bm && ob->data) { + BKE_sculptsession_update_attr_refs(ob); - BMeshToMeshParams params = {0}; - params.update_shapekey_indices = true; + BMeshToMeshParams params = {0}; + params.update_shapekey_indices = true; - params.cd_mask_extra.vmask = CD_MASK_MESH_ID | CD_MASK_DYNTOPO_VERT; - params.cd_mask_extra.emask = CD_MASK_MESH_ID; - params.cd_mask_extra.pmask = CD_MASK_MESH_ID; + params.cd_mask_extra.vmask = CD_MASK_DYNTOPO_VERT; - BM_mesh_bm_to_me(nullptr, ss->bm, static_cast(ob->data), ¶ms); - } + BM_mesh_bm_to_me(nullptr, ss->bm, static_cast(ob->data), ¶ms); } } @@ -2346,14 +2332,6 @@ int *BKE_sculpt_face_sets_ensure(Object *ob) bool *BKE_sculpt_hide_poly_ensure(Object *ob) { - Mesh *mesh = BKE_object_get_original_mesh(ob); - - bool *hide_poly = static_cast(CustomData_get_layer_named_for_write( - &mesh->pdata, CD_PROP_BOOL, ".hide_poly", mesh->totpoly)); - if (hide_poly != nullptr) { - return hide_poly; - } - SculptAttributeParams params = {0}; params.permanent = true; @@ -3373,9 +3351,9 @@ static bool sculpt_attribute_create(SculptSession *ss, return false; } - BLI_assert(CustomData_get_named_layer_index(cdata, proptype, name) == -1); - - BM_data_layer_add_named(ss->bm, cdata, proptype, name); + if (CustomData_get_named_layer_index(cdata, proptype, name) == -1) { + BM_data_layer_add_named(ss->bm, cdata, proptype, name); + } int index = CustomData_get_named_layer_index(cdata, proptype, name); if (!permanent && !ss->save_temp_layers) { @@ -3404,9 +3382,9 @@ static bool sculpt_attribute_create(SculptSession *ss, return false; } - BLI_assert(CustomData_get_named_layer_index(cdata, proptype, name) == -1); - - CustomData_add_layer_named(cdata, proptype, CD_SET_DEFAULT, totelem, name); + if (CustomData_get_named_layer_index(cdata, proptype, name) == -1) { + CustomData_add_layer_named(cdata, proptype, CD_SET_DEFAULT, totelem, name); + } int index = CustomData_get_named_layer_index(cdata, proptype, name); if (!permanent && !ss->save_temp_layers) { @@ -3645,7 +3623,6 @@ static void sculptsession_bmesh_attr_update_internal(Object *ob) if (ss->pbvh) { int cd_sculpt_vert = CustomData_get_offset(&ss->bm->vdata, CD_DYNTOPO_VERT); int cd_face_area = ss->attrs.face_areas ? ss->attrs.face_areas->bmesh_cd_offset : -1; - int cd_hide_poly = ss->attrs.hide_poly ? ss->attrs.hide_poly->bmesh_cd_offset : -1; int cd_boundary_flags = ss->attrs.boundary_flags ? ss->attrs.boundary_flags->bmesh_cd_offset : -1; int cd_dyntopo_vert = ss->attrs.dyntopo_node_id_vertex ? @@ -3661,7 +3638,6 @@ static void sculptsession_bmesh_attr_update_internal(Object *ob) cd_dyntopo_face, cd_sculpt_vert, cd_face_area, - cd_hide_poly, cd_boundary_flags); } } @@ -3710,7 +3686,6 @@ static void sculptsession_bmesh_add_layers(Object *ob) ss->cd_face_node_offset = ss->attrs.dyntopo_node_id_face->bmesh_cd_offset; ss->cd_face_areas = ss->attrs.face_areas ? ss->attrs.face_areas->bmesh_cd_offset : -1; ss->cd_sculpt_vert = ss->attrs.sculpt_vert->bmesh_cd_offset; - ss->cd_hide_poly = ss->attrs.hide_poly ? ss->attrs.hide_poly->bmesh_cd_offset : -1; } template static void sculpt_clear_attribute_bmesh(BMesh *bm, SculptAttribute *attr) @@ -3812,7 +3787,6 @@ static void update_bmesh_offsets(Mesh *me, SculptSession *ss) ss->cd_faceset_offset = CustomData_get_offset_named( &ss->bm->pdata, CD_PROP_INT32, ".sculpt_face_set"); ss->cd_face_areas = ss->attrs.face_areas ? ss->attrs.face_areas->bmesh_cd_offset : -1; - ss->cd_hide_poly = ss->attrs.hide_poly ? ss->attrs.hide_poly->bmesh_cd_offset : -1; int cd_boundary_flags = ss->attrs.boundary_flags ? ss->attrs.boundary_flags->bmesh_cd_offset : -1; @@ -3823,7 +3797,6 @@ static void update_bmesh_offsets(Mesh *me, SculptSession *ss) ss->cd_face_node_offset, ss->cd_sculpt_vert, ss->cd_face_areas, - ss->cd_hide_poly, cd_boundary_flags); } } diff --git a/source/blender/blenkernel/intern/pbvh.c b/source/blender/blenkernel/intern/pbvh.c index ff8188fced8..65656b7b0f9 100644 --- a/source/blender/blenkernel/intern/pbvh.c +++ b/source/blender/blenkernel/intern/pbvh.c @@ -5603,9 +5603,8 @@ static void pbvh_face_iter_step(PBVHFaceIter *fd, bool do_step) fd->face_set = (int *)BM_ELEM_CD_GET_VOID_P(f, fd->cd_face_set_); } - if (fd->cd_hide_poly_ != -1) { - fd->hide = (bool *)BM_ELEM_CD_GET_VOID_P(f, fd->cd_hide_poly_); - } + /* TODO: BMesh doesn't yet use .hide_poly. */ + fd->hide = nullptr; pbvh_face_iter_verts_reserve(fd, f->len); int vertex_i = 0; @@ -5705,8 +5704,6 @@ void BKE_pbvh_face_iter_init(PBVH *pbvh, PBVHNode *node, PBVHFaceIter *fd) fd->bm = pbvh->header.bm; fd->cd_face_set_ = CustomData_get_offset_named( &pbvh->header.bm->pdata, CD_PROP_INT32, ".sculpt_face_set"); - fd->cd_hide_poly_ = CustomData_get_offset_named( - &pbvh->header.bm->pdata, CD_PROP_INT32, ".hide_poly"); fd->bm_faces_iter_ = 0; fd->bm_faces_ = node->bm_faces; diff --git a/source/blender/blenkernel/intern/pbvh.cc b/source/blender/blenkernel/intern/pbvh.cc index 8f0a06537ec..17e19c14203 100644 --- a/source/blender/blenkernel/intern/pbvh.cc +++ b/source/blender/blenkernel/intern/pbvh.cc @@ -5198,9 +5198,8 @@ static void pbvh_face_iter_step(PBVHFaceIter *fd, bool do_step) fd->face_set = (int *)BM_ELEM_CD_GET_VOID_P(f, fd->cd_face_set_); } - if (fd->cd_hide_poly_ != -1) { - fd->hide = (bool *)BM_ELEM_CD_GET_VOID_P(f, fd->cd_hide_poly_); - } + /* TODO: BMesh doesn't use .hide_poly yet.*/ + fd->hide = nullptr; pbvh_face_iter_verts_reserve(fd, f->len); int vertex_i = 0; @@ -5300,8 +5299,6 @@ void BKE_pbvh_face_iter_init(PBVH *pbvh, PBVHNode *node, PBVHFaceIter *fd) fd->bm = pbvh->header.bm; fd->cd_face_set_ = CustomData_get_offset_named( &pbvh->header.bm->pdata, CD_PROP_INT32, ".sculpt_face_set"); - fd->cd_hide_poly_ = CustomData_get_offset_named( - &pbvh->header.bm->pdata, CD_PROP_INT32, ".hide_poly"); fd->bm_faces_iter_ = 0; fd->bm_faces_ = node->bm_faces; diff --git a/source/blender/blenkernel/intern/pbvh_bmesh.cc b/source/blender/blenkernel/intern/pbvh_bmesh.cc index b4030057da1..4406e9f7a61 100644 --- a/source/blender/blenkernel/intern/pbvh_bmesh.cc +++ b/source/blender/blenkernel/intern/pbvh_bmesh.cc @@ -739,8 +739,6 @@ static void pbvh_bmesh_node_finalize(PBVH *pbvh, BB_reset(&n->orig_vb); BMFace *f; - int cd_hide_poly = pbvh->cd_hide_poly; - TGSET_ITER (f, n->bm_faces) { /* Update ownership of faces */ BM_ELEM_CD_SET_INT(f, cd_face_node_offset, node_index); @@ -770,7 +768,7 @@ static void pbvh_bmesh_node_finalize(PBVH *pbvh, BB_expand(&n->orig_vb, mv->origco); } while ((l_iter = l_iter->next) != l_first); - if (cd_hide_poly == -1 || !BM_ELEM_CD_GET_BOOL(f, cd_hide_poly)) { + if (!BM_elem_flag_test(f, BM_ELEM_HIDDEN)) { has_visible = true; } } @@ -1370,7 +1368,7 @@ void BKE_pbvh_bmesh_regen_node_verts(PBVH *pbvh) static bool pbvh_poly_hidden(PBVH *pbvh, BMFace *f) { - return pbvh->cd_hide_poly != -1 && BM_ELEM_CD_GET_BOOL(f, pbvh->cd_hide_poly); + return BM_elem_flag_test(f, BM_ELEM_HIDDEN); } bool BKE_pbvh_bmesh_check_origdata(PBVH *pbvh, BMVert *v, int stroke_id) @@ -2459,8 +2457,6 @@ ATTR_NO_OPT void BKE_pbvh_build_bmesh(PBVH *pbvh, pbvh->bm_idmap = idmap; - pbvh->cd_hide_poly = CustomData_get_offset_named( - &bm->pdata, CD_PROP_INT32, ".sculpt_face_areas"); pbvh->cd_face_area = cd_face_areas; pbvh->cd_vert_node_offset = cd_vert_node_offset; pbvh->cd_face_node_offset = cd_face_node_offset; @@ -4006,10 +4002,8 @@ void BKE_pbvh_update_offsets(PBVH *pbvh, const int cd_face_node_offset, const int cd_sculpt_vert, const int cd_face_areas, - const int cd_hide_poly, const int cd_boundary_flag) { - pbvh->cd_hide_poly = cd_hide_poly; pbvh->cd_face_node_offset = cd_face_node_offset; pbvh->cd_vert_node_offset = cd_vert_node_offset; pbvh->cd_face_area = cd_face_areas; diff --git a/source/blender/blenkernel/intern/pbvh_intern.hh b/source/blender/blenkernel/intern/pbvh_intern.hh index 900ab6c9cf6..05dbd3732ea 100644 --- a/source/blender/blenkernel/intern/pbvh_intern.hh +++ b/source/blender/blenkernel/intern/pbvh_intern.hh @@ -240,8 +240,7 @@ struct PBVH { int cd_faceset_offset; int cd_face_area; int cd_vcol_offset; - int cd_hide_poly; - + int totuv; float planes[6][4]; diff --git a/source/blender/editors/sculpt_paint/paint_hide.cc b/source/blender/editors/sculpt_paint/paint_hide.cc index 960065c1d82..9126f46eaea 100644 --- a/source/blender/editors/sculpt_paint/paint_hide.cc +++ b/source/blender/editors/sculpt_paint/paint_hide.cc @@ -223,15 +223,19 @@ static void partialvis_update_bmesh_verts(BMesh *bm, TGSET_ITER_END } -static void partialvis_update_bmesh_faces(TableGSet *faces, int cd_hide_poly) +static void partialvis_update_bmesh_faces(TableGSet *faces) { BMFace *f; - - TGSET_ITER (f, faces) { - bool hidden = paint_is_bmesh_face_hidden(f); - BM_ELEM_CD_SET_BOOL(f, cd_hide_poly, hidden); - if (paint_is_bmesh_face_hidden(f)) { + TGSET_ITER (f, faces) { + bool hidden = false; + + BMLoop *l = f->l_first; + do { + hidden |= BM_elem_flag_test(l->v, BM_ELEM_HIDDEN); + } while ((l = l->next) != f->l_first); + + if (hidden) { BM_elem_flag_enable(f, BM_ELEM_HIDDEN); } else { @@ -264,7 +268,7 @@ static void partialvis_update_bmesh(Object *ob, partialvis_update_bmesh_verts(bm, other, action, area, planes, &any_changed, &any_visible); /* Finally loop over node faces and tag the ones that are fully hidden. */ - partialvis_update_bmesh_faces(faces, ob->sculpt->attrs.hide_poly->bmesh_cd_offset); + partialvis_update_bmesh_faces(faces); if (any_changed) { BKE_pbvh_node_mark_rebuild_draw(node); diff --git a/source/blender/editors/sculpt_paint/sculpt.cc b/source/blender/editors/sculpt_paint/sculpt.cc index 7aa23a6fb7a..880ecfa3708 100644 --- a/source/blender/editors/sculpt_paint/sculpt.cc +++ b/source/blender/editors/sculpt_paint/sculpt.cc @@ -579,8 +579,6 @@ void SCULPT_face_set_visibility_set(SculptSession *ss, int face_set, bool visibl return; } - int cd_hide_poly = ss->attrs.hide_poly->bmesh_cd_offset; - BM_ITER_MESH (f, &iter, ss->bm, BM_FACES_OF_MESH) { int fset = BM_ELEM_CD_GET_INT(f, ss->cd_faceset_offset); int node = BM_ELEM_CD_GET_INT(f, ss->cd_face_node_offset); @@ -589,7 +587,7 @@ void SCULPT_face_set_visibility_set(SculptSession *ss, int face_set, bool visibl continue; } - BM_ELEM_CD_SET_BOOL(f, cd_hide_poly, !visible); + BM_elem_flag_set(f, BM_ELEM_HIDDEN, !visible); if (node != DYNTOPO_NODE_NONE) { BKE_pbvh_vert_tag_update_normal_triangulation(BKE_pbvh_node_from_index(ss->pbvh, node)); @@ -616,13 +614,10 @@ void SCULPT_face_visibility_all_invert(SculptSession *ss) case PBVH_BMESH: { BMIter iter; BMFace *f; - int cd_hide_poly = ss->attrs.hide_poly->bmesh_cd_offset; BM_ITER_MESH (f, &iter, ss->bm, BM_FACES_OF_MESH) { - int state = BM_ELEM_CD_GET_BOOL(f, cd_hide_poly); - state ^= true; - - BM_ELEM_CD_SET_BOOL(f, cd_hide_poly, state); + bool state = BM_elem_flag_test(f, BM_ELEM_HIDDEN); + BM_elem_flag_set(f, BM_ELEM_HIDDEN, state ^ true); } break; } @@ -675,10 +670,8 @@ bool SCULPT_vertex_any_face_visible_get(SculptSession *ss, PBVHVertRef vertex) BMLoop *l; BMVert *v = (BMVert *)vertex.i; - int cd_hide_poly = ss->attrs.hide_poly->bmesh_cd_offset; - BM_ITER_ELEM (l, &iter, v, BM_LOOPS_OF_VERT) { - if (!BM_ELEM_CD_GET_BOOL(l->f, cd_hide_poly)) { + if (!BM_elem_flag_test(l->f, BM_ELEM_HIDDEN)) { return true; } } @@ -709,7 +702,6 @@ bool SCULPT_vertex_all_faces_visible_get(const SculptSession *ss, PBVHVertRef ve case PBVH_BMESH: { BMVert *v = (BMVert *)vertex.i; BMEdge *e = v->e; - int cd_hide_poly = ss->attrs.hide_poly->bmesh_cd_offset; if (!e) { return true; @@ -723,7 +715,7 @@ bool SCULPT_vertex_all_faces_visible_get(const SculptSession *ss, PBVHVertRef ve } do { - if (BM_ELEM_CD_GET_BOOL(l->f, cd_hide_poly)) { + if (BM_elem_flag_test(l->f, BM_ELEM_HIDDEN)) { return false; } } while ((l = l->radial_next) != e->l); @@ -777,11 +769,9 @@ void SCULPT_vertex_face_set_set(SculptSession *ss, PBVHVertRef vertex, int face_ BMLoop *l; BMVert *v = (BMVert *)vertex.i; - int cd_hide_poly = ss->attrs.hide_poly->bmesh_cd_offset; - BM_ITER_ELEM (l, &iter, v, BM_LOOPS_OF_VERT) { int fset = BM_ELEM_CD_GET_INT(l->f, ss->cd_faceset_offset); - if (!BM_ELEM_CD_GET_BOOL(l->f, cd_hide_poly) && fset != face_set) { + if (!BM_elem_flag_test(l->f, BM_ELEM_HIDDEN) && fset != face_set) { BM_ELEM_CD_SET_INT(l->f, ss->cd_faceset_offset, abs(face_set)); } @@ -995,8 +985,6 @@ void SCULPT_visibility_sync_all_from_faces(Object *ob) return; } - int cd_hide_poly = ss->attrs.hide_poly->bmesh_cd_offset; - /* Hide all verts and edges attached to faces. */ BM_ITER_MESH (f, &iter, ss->bm, BM_FACES_OF_MESH) { BMLoop *l = f->l_first; @@ -1008,8 +996,7 @@ void SCULPT_visibility_sync_all_from_faces(Object *ob) /* Unhide verts and edges attached to visible faces. */ BM_ITER_MESH (f, &iter, ss->bm, BM_FACES_OF_MESH) { - if (BM_ELEM_CD_GET_BOOL(f, cd_hide_poly)) { - BM_elem_flag_enable(f, BM_ELEM_HIDDEN); + if (BM_elem_flag_test(f, BM_ELEM_HIDDEN)) { continue; } diff --git a/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc b/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc index 5e8f2d6af3d..bda51dfddad 100644 --- a/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc +++ b/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc @@ -509,9 +509,8 @@ void SCULPT_dynamic_topology_enable_ex(Main *bmain, Depsgraph *depsgraph, Scene ss->bm->totloop == me->totloop && ss->bm->totface == me->totpoly; if (!ok) { - /* Ensure ss->pbvh is in the cache so it can be destroyed in BKE_pbvh_free_bmesh. */ - #ifdef WITH_PBVH_CACHE + /* Ensure ss->pbvh is in the cache so it can be destroyed in BKE_pbvh_free_bmesh. */ if (ss->pbvh) { BKE_pbvh_set_cached(ob, ss->pbvh); } @@ -575,7 +574,7 @@ void SCULPT_dynamic_topology_enable_ex(Main *bmain, Depsgraph *depsgraph, Scene params.active_shapekey = ob->shapenr; BM_mesh_bm_from_me(ss->bm, me, ¶ms); - + if (ss->pbvh) { BKE_sculptsession_update_attr_refs(ob); BKE_pbvh_set_bmesh(ss->pbvh, ss->bm); @@ -650,7 +649,7 @@ static void SCULPT_dynamic_topology_disable_ex( SculptSession *ss = ob->sculpt; Mesh *me = static_cast(ob->data); - /* Destroy non-customdata temporary layers (which are rarely (never?) used for PBVH_BMESH). */ + /* Destroy temporary layers. */ BKE_sculpt_attribute_destroy_temporary_all(ob); if (ss->attrs.dyntopo_node_id_vertex) { @@ -661,6 +660,7 @@ static void SCULPT_dynamic_topology_disable_ex( BKE_sculpt_attribute_destroy(ob, ss->attrs.dyntopo_node_id_face); } + BKE_sculptsession_update_attr_refs(ob); BKE_sculptsession_bm_to_me(ob, true); /* Clear data. */ me->flag &= ~ME_SCULPT_DYNAMIC_TOPOLOGY; -- 2.30.2 From 3dfcd45b0990fb36a0b708a412e8d320570957b8 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Sun, 26 Mar 2023 15:00:36 -0700 Subject: [PATCH 043/279] temp-sculpt-dyntopo: Fix error in merge --- source/blender/blenkernel/intern/paint.cc | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/source/blender/blenkernel/intern/paint.cc b/source/blender/blenkernel/intern/paint.cc index 33482ee109e..d18f00b37d8 100644 --- a/source/blender/blenkernel/intern/paint.cc +++ b/source/blender/blenkernel/intern/paint.cc @@ -3060,18 +3060,7 @@ int BKE_sculptsession_vertex_count(const SculptSession *ss) static bool sculpt_attribute_stored_in_bmesh_builtin(const StringRef name) { - return ELEM(name, - "position", - ".hide_vert", - ".hide_edge", - /* ".hide_poly", sculpt mode uses hide_poly. */ - ".uv_seam", - ".select_vert", - ".select_edge", - ".select_poly", - "material_index", - "sharp_face", - "sharp_edge"); + return BM_attribute_stored_in_bmesh_builtin(name); } /** -- 2.30.2 From b8712aade7ae01ce0c8cb414215a0bc9b08e6787 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Sun, 26 Mar 2023 16:59:13 -0700 Subject: [PATCH 044/279] temp-sculpt-dyntopo: Remove ATTR_NO_OPTs and fix merge errors --- source/blender/blenkernel/BKE_pbvh.h | 1 + .../blender/blenkernel/intern/customdata.cc | 24 +- source/blender/blenkernel/intern/dyntopo.cc | 40 +++- source/blender/blenkernel/intern/paint.cc | 1 + source/blender/blenkernel/intern/pbvh.cc | 19 +- .../blender/blenkernel/intern/pbvh_bmesh.cc | 205 +++++++++++++----- source/blender/bmesh/intern/bmesh_construct.c | 8 +- source/blender/bmesh/intern/bmesh_idmap.cc | 22 +- source/blender/bmesh/intern/bmesh_interp.c | 22 +- source/blender/bmesh/intern/bmesh_log.cc | 106 +++++---- source/blender/draw/intern/draw_pbvh.cc | 4 +- .../editors/mesh/editmesh_mask_extract.cc | 8 +- source/blender/editors/sculpt_paint/sculpt.cc | 12 +- .../editors/sculpt_paint/sculpt_face_set.cc | 25 +-- .../editors/sculpt_paint/sculpt_smooth.cc | 22 +- .../editors/sculpt_paint/sculpt_undo.cc | 4 +- 16 files changed, 310 insertions(+), 213 deletions(-) diff --git a/source/blender/blenkernel/BKE_pbvh.h b/source/blender/blenkernel/BKE_pbvh.h index a04706980b0..3eb303af66c 100644 --- a/source/blender/blenkernel/BKE_pbvh.h +++ b/source/blender/blenkernel/BKE_pbvh.h @@ -432,6 +432,7 @@ void BKE_pbvh_build_mesh(PBVH *pbvh, struct Mesh *mesh, const struct MPoly *polys, const int *corner_verts, + const int *corner_edges, float (*vert_positions)[3], struct MSculptVert *msculptverts, int totvert, diff --git a/source/blender/blenkernel/intern/customdata.cc b/source/blender/blenkernel/intern/customdata.cc index 20ef8b10080..9b6f22097d9 100644 --- a/source/blender/blenkernel/intern/customdata.cc +++ b/source/blender/blenkernel/intern/customdata.cc @@ -4229,7 +4229,7 @@ void CustomData_bmesh_free_block(CustomData *data, void **block) *block = nullptr; } -ATTR_NO_OPT void CustomData_bmesh_free_block_data(CustomData *data, void *block) +void CustomData_bmesh_free_block_data(CustomData *data, void *block) { if (block == nullptr) { return; @@ -4310,10 +4310,7 @@ void CustomData_bmesh_free_block_data_exclude_by_type(CustomData *data, CustomData_bmesh_asan_poison(data, block); } -ATTR_NO_OPT void CustomData_data_set_default_value(const CustomData *data, - const int type, - int n, - void *elem) +void CustomData_data_set_default_value(const CustomData *data, const int type, int n, void *elem) { const LayerTypeInfo *typeInfo = layerType_getInfo(type); int base_idx = data->typemap[type]; @@ -4329,14 +4326,13 @@ ATTR_NO_OPT void CustomData_data_set_default_value(const CustomData *data, } } -ATTR_NO_OPT static void CustomData_bmesh_set_default_n(CustomData *data, - void **block, - const int idx) +static void CustomData_bmesh_set_default_n(CustomData *data, void **block, const int idx) { const int offset = data->layers[idx].offset; int n = idx - data->typemap[data->layers[idx].type]; - CustomData_data_set_default_value(data, data->layers[idx].type, n, POINTER_OFFSET(*block, offset)); + CustomData_data_set_default_value( + data, data->layers[idx].type, n, POINTER_OFFSET(*block, offset)); } void CustomData_bmesh_set_default(CustomData *data, void **block) @@ -4451,11 +4447,11 @@ void CustomData_bmesh_swap_data(CustomData *source, } } -ATTR_NO_OPT void CustomData_bmesh_copy_data_exclude_by_type(const CustomData *source, - CustomData *dest, - void *src_block, - void **dest_block, - const eCustomDataMask mask_exclude) +void CustomData_bmesh_copy_data_exclude_by_type(const CustomData *source, + CustomData *dest, + void *src_block, + void **dest_block, + const eCustomDataMask mask_exclude) { /* Note: having a version of this function without a 'mask_exclude' * would cause too much duplicate code, so add a check instead. */ diff --git a/source/blender/blenkernel/intern/dyntopo.cc b/source/blender/blenkernel/intern/dyntopo.cc index 598653a4196..03851c2fce6 100644 --- a/source/blender/blenkernel/intern/dyntopo.cc +++ b/source/blender/blenkernel/intern/dyntopo.cc @@ -566,6 +566,18 @@ static void long_edge_queue_edge_add_recursive_2(EdgeQueueThreadData *tdata, { BLI_assert(len_sq > square_f(limit_len)); + BMLoop *l = l_edge; + int count = 0; + do { + if (count++ > 5) { + printf("%s: topology error: highly non-manifold edge %p\n", __func__, l_edge->e); + BM_vert_select_set(tdata->pbvh->header.bm, l_edge->e->v1, true); + BM_vert_select_set(tdata->pbvh->header.bm, l_edge->e->v2, true); + BM_edge_select_set(tdata->pbvh->header.bm, l_edge->e, true); + return; + } + } while ((l = l->radial_next) != l_edge); + if (l_edge->e->head.hflag & BM_ELEM_TAG) { // return; } @@ -755,7 +767,7 @@ static void unified_edge_queue_task_cb(void *__restrict userdata, BLI_rng_free(rng); } -ATTR_NO_OPT bool check_face_is_tri(PBVH *pbvh, BMFace *f) +bool check_face_is_tri(PBVH *pbvh, BMFace *f) { #if DYNTOPO_DISABLE_FLAG & DYNTOPO_DISABLE_TRIANGULATOR return true; @@ -964,7 +976,7 @@ bool destroy_nonmanifold_fins(PBVH *pbvh, BMEdge *e_root) return false; } - printf("manifold fin size: %d\n", (int)minfs.size()); + // printf("manifold fin size: %d\n", (int)minfs.size()); const int tag = BM_ELEM_TAG_ALT; for (int i = 0; i < minfs.size(); i++) { @@ -1133,12 +1145,19 @@ bool check_vert_fan_are_tris(PBVH *pbvh, BMVert *v) MV_ADD_FLAG(mv_l, SCULPTVERT_NEED_VALENCE | SCULPTVERT_NEED_DISK_SORT); } while ((l = l->next) != f->l_first); fs.append(f); + + if (BM_elem_is_free((BMElem *)f, BM_FACE)) { + printf("%s: corrupted face error!\n", __func__); + } } mv->flag &= ~SCULPTVERT_NEED_TRIANGULATE; for (int i = 0; i < fs.size(); i++) { - check_face_is_tri(pbvh, fs[i]); + /* Triangulation can sometimes delete a face. */ + if (!BM_elem_is_free((BMElem *)fs[i], BM_FACE)) { + check_face_is_tri(pbvh, fs[i]); + } } return false; @@ -2295,14 +2314,14 @@ extern "C" bool BKE_pbvh_bmesh_update_topology(PBVH *pbvh, int steps[2] = {0, 0}; if ((mode & PBVH_Subdivide) && (mode & PBVH_Collapse)) { - steps[0] = 4096; - steps[1] = 1024; + steps[1] = DYNTOPO_MAX_ITER / 3; + steps[0] = DYNTOPO_MAX_ITER - steps[1]; } else if (mode & PBVH_Subdivide) { - steps[0] = 4096; + steps[0] = DYNTOPO_MAX_ITER; } else if (mode & PBVH_Collapse) { - steps[0] = 4096; + steps[0] = DYNTOPO_MAX_ITER; } int edges_size = steps[0]; @@ -2337,6 +2356,8 @@ extern "C" bool BKE_pbvh_bmesh_update_topology(PBVH *pbvh, BMEdge *e = nullptr; if (count >= steps[curop]) { + BM_log_entry_add_ex(pbvh->header.bm, pbvh->bm_log, true); + if (ops[curop] == PBVH_Subdivide) { modified = true; BLI_smallhash_clear(&subd_edges, 0); @@ -2415,14 +2436,12 @@ extern "C" bool BKE_pbvh_bmesh_update_topology(PBVH *pbvh, break; } - printf("\n"); modified = true; pbvh_bmesh_collapse_edge(pbvh, e, e->v1, e->v2, nullptr, nullptr, &eq_ctx); VALIDATE_LOG(pbvh->bm_log); - printf("\n"); // XXX - BM_log_entry_add_ex(pbvh->header.bm, pbvh->bm_log, true); + // BM_log_entry_add_ex(pbvh->header.bm, pbvh->bm_log, true); break; } default: @@ -2436,6 +2455,7 @@ extern "C" bool BKE_pbvh_bmesh_update_topology(PBVH *pbvh, if (etot > 0) { modified = true; BLI_smallhash_clear(&subd_edges, 0); + BM_log_entry_add_ex(pbvh->header.bm, pbvh->bm_log, true); pbvh_split_edges(&eq_ctx, pbvh, pbvh->header.bm, edges, etot, false); VALIDATE_LOG(pbvh->bm_log); etot = 0; diff --git a/source/blender/blenkernel/intern/paint.cc b/source/blender/blenkernel/intern/paint.cc index d18f00b37d8..a05cc5b5015 100644 --- a/source/blender/blenkernel/intern/paint.cc +++ b/source/blender/blenkernel/intern/paint.cc @@ -2660,6 +2660,7 @@ static PBVH *build_pbvh_from_regular_mesh(Object *ob, Mesh *me_eval_deform, bool me, me->polys().data(), corner_verts.data(), + me->corner_edges().data(), vert_cos, ss->msculptverts, me->totvert, diff --git a/source/blender/blenkernel/intern/pbvh.cc b/source/blender/blenkernel/intern/pbvh.cc index 81b9f5c8444..a83f13b78bc 100644 --- a/source/blender/blenkernel/intern/pbvh.cc +++ b/source/blender/blenkernel/intern/pbvh.cc @@ -957,6 +957,7 @@ void BKE_pbvh_build_mesh(PBVH *pbvh, Mesh *mesh, const MPoly *polys, const int *corner_verts, + const int *corner_edges, float (*vert_positions)[3], MSculptVert *msculptverts, int totvert, @@ -986,6 +987,7 @@ void BKE_pbvh_build_mesh(PBVH *pbvh, pbvh->material_indices = static_cast( CustomData_get_layer_named(&mesh->pdata, CD_PROP_INT32, "material_index")); pbvh->corner_verts = corner_verts; + pbvh->corner_edges = corner_edges; pbvh->looptri = looptri; pbvh->msculptverts = msculptverts; pbvh->vert_positions = vert_positions; @@ -4127,12 +4129,12 @@ static void pbvh_pmap_to_edges_add(PBVH *pbvh, (*len)++; } -void BKE_pbvh_pmap_to_edges(PBVH *pbvh, - PBVHVertRef vertex, - int **r_edges, - int *r_edges_size, - bool *r_heap_alloc, - int **r_polys) +ATTR_NO_OPT void BKE_pbvh_pmap_to_edges(PBVH *pbvh, + PBVHVertRef vertex, + int **r_edges, + int *r_edges_size, + bool *r_heap_alloc, + int **r_polys) { MeshElemMap *map = pbvh->pmap->pmap + vertex.i; int len = 0; @@ -4179,10 +4181,7 @@ void BKE_pbvh_set_vemap(PBVH *pbvh, MeshElemMap *vemap) pbvh->vemap = vemap; } -ATTR_NO_OPT void BKE_pbvh_get_vert_face_areas(PBVH *pbvh, - PBVHVertRef vertex, - float *r_areas, - int valence) +void BKE_pbvh_get_vert_face_areas(PBVH *pbvh, PBVHVertRef vertex, float *r_areas, int valence) { const int cur_i = pbvh->face_area_i; diff --git a/source/blender/blenkernel/intern/pbvh_bmesh.cc b/source/blender/blenkernel/intern/pbvh_bmesh.cc index 4406e9f7a61..79cfcb199a6 100644 --- a/source/blender/blenkernel/intern/pbvh_bmesh.cc +++ b/source/blender/blenkernel/intern/pbvh_bmesh.cc @@ -95,7 +95,7 @@ static void _debugprint(const char *fmt, ...) } #ifdef PBVH_BMESH_DEBUG -ATTR_NO_OPT void pbvh_bmesh_check_nodes_simple(PBVH *pbvh) +void pbvh_bmesh_check_nodes_simple(PBVH *pbvh) { for (int i = 0; i < pbvh->totnode; i++) { PBVHNode *node = pbvh->nodes + i; @@ -2439,19 +2439,19 @@ void BKE_pbvh_set_idmap(PBVH *pbvh, BMIdMap *idmap) } /* Build a PBVH from a BMesh */ -ATTR_NO_OPT void BKE_pbvh_build_bmesh(PBVH *pbvh, - Mesh *me, - BMesh *bm, - bool smooth_shading, - BMLog *log, - BMIdMap *idmap, - const int cd_vert_node_offset, - const int cd_face_node_offset, - const int cd_sculpt_vert, - const int cd_face_areas, - const int cd_boundary_flag, - bool fast_draw, - bool update_sculptverts) +void BKE_pbvh_build_bmesh(PBVH *pbvh, + Mesh *me, + BMesh *bm, + bool smooth_shading, + BMLog *log, + BMIdMap *idmap, + const int cd_vert_node_offset, + const int cd_face_node_offset, + const int cd_sculpt_vert, + const int cd_face_areas, + const int cd_boundary_flag, + bool fast_draw, + bool update_sculptverts) { // coalese_pbvh(pbvh, bm); @@ -2898,7 +2898,7 @@ static uintptr_t tri_loopkey(BMLoop *l, int mat_nr, int cd_fset, int cd_uvs[], i * (currently just raycast), store the node's triangles and vertices. * * Skips triangles that are hidden. */ -bool BKE_pbvh_bmesh_check_tris(PBVH *pbvh, PBVHNode *node) +ATTR_NO_OPT bool BKE_pbvh_bmesh_check_tris(PBVH *pbvh, PBVHNode *node) { BMesh *bm = pbvh->header.bm; @@ -3298,7 +3298,6 @@ static void BKE_pbvh_bmesh_correct_tree(PBVH *pbvh, PBVHNode *node, PBVHNode *pa const int size_lower = pbvh->leaf_limit - (pbvh->leaf_limit >> 1); if (node->flag & PBVH_Leaf) { - // pbvh_bmesh_node_limit_ensure(pbvh, (int)(node - pbvh->nodes)); return; } @@ -3745,40 +3744,132 @@ static void pbvh_bmesh_balance_tree(PBVH *pbvh) MEM_SAFE_FREE(depthmap); } -static void pbvh_bmesh_join_nodes(PBVH *bvh) +/* Fix any orphaned empty leaves that survived other stages of culling.*/ +ATTR_NO_OPT static void pbvh_fix_orphan_leaves(PBVH *pbvh) { - if (bvh->totnode < 2) { + for (int i = 0; i < pbvh->totnode; i++) { + PBVHNode *node = pbvh->nodes + i; + + if (!(node->flag & PBVH_Leaf) || (node->flag & PBVH_Delete) || + BLI_table_gset_len(node->bm_faces) != 0) { + continue; + } + + /* Find parent node. */ + PBVHNode *parent = nullptr; + + for (int j = 0; j < pbvh->totnode; j++) { + if (pbvh->nodes[j].children_offset == i || pbvh->nodes[j].children_offset + 1 == i) { + parent = pbvh->nodes + j; + break; + } + } + + if (!parent) { + printf("%s: node corruption, could not find parent node!\n", __func__); + continue; + } + + PBVHNode *other = pbvh->nodes + (parent->children_offset == i ? i + 1 : i - 1); + + if (other->flag & PBVH_Delete) { + printf("%s: error!\n", __func__); + continue; + } + + while (!(other->flag & PBVH_Leaf)) { + PBVHNode *a = pbvh->nodes + other->children_offset; + PBVHNode *b = pbvh->nodes + other->children_offset + 1; + + if (!(a->flag & PBVH_Delete) && (a->flag & PBVH_Leaf) && + BLI_table_gset_len(a->bm_faces) > 1) { + other = a; + } + else if (!(b->flag & PBVH_Delete) && (b->flag & PBVH_Leaf) && + BLI_table_gset_len(b->bm_faces) > 1) { + other = b; + } + else { + other = nullptr; + break; + } + } + + if (other == nullptr || BLI_table_gset_len(other->bm_faces) < 1) { + printf("%s: other was nullptr\n", __func__); + continue; + } + + /* Steal a single face from other */ + BMFace *f; + PBVHNodeFlags updateflag = PBVH_UpdateOtherVerts | PBVH_UpdateBB | PBVH_UpdateOriginalBB | + PBVH_UpdateTris | PBVH_UpdateTriAreas | PBVH_RebuildDrawBuffers | + PBVH_RebuildNodeVerts | PBVH_RebuildPixels | PBVH_UpdateNormals | + PBVH_UpdateCurvatureDir | PBVH_UpdateRedraw | PBVH_UpdateVisibility; + + TGSET_ITER (f, other->bm_faces) { + BLI_table_gset_remove(other->bm_faces, static_cast(f), nullptr); + BLI_table_gset_add(node->bm_faces, static_cast(f)); + BM_ELEM_CD_SET_INT(f, pbvh->cd_face_node_offset, i); + + BMVert *v = f->l_first->v; + int node_i = BM_ELEM_CD_GET_INT(v, pbvh->cd_vert_node_offset); + if (node_i != DYNTOPO_NODE_NONE) { + PBVHNode *node = pbvh->nodes + node_i; + if (BLI_table_gset_haskey(node->bm_unique_verts, static_cast(v))) { + BLI_table_gset_remove(node->bm_unique_verts, static_cast(v), nullptr); + } + } + + BM_ELEM_CD_SET_INT(v, pbvh->cd_vert_node_offset, i); + BLI_table_gset_add(node->bm_unique_verts, v); + + node->flag |= updateflag; + other->flag |= updateflag; + + printf("%s: Patched empty leaf node.\n", __func__); + break; + } + TGSET_ITER_END; + } +} + +static void pbvh_bmesh_join_nodes(PBVH *pbvh) +{ + if (pbvh->totnode < 2) { return; } - pbvh_count_subtree_verts(bvh, bvh->nodes); - BKE_pbvh_bmesh_correct_tree(bvh, bvh->nodes, nullptr); + pbvh_count_subtree_verts(pbvh, pbvh->nodes); + BKE_pbvh_bmesh_correct_tree(pbvh, pbvh->nodes, nullptr); + pbvh_fix_orphan_leaves(pbvh); /* Compact nodes. */ int totnode = 0; - int *map = MEM_cnew_array(bvh->totnode, "bmesh map temp"); + int *map = MEM_cnew_array(pbvh->totnode, "bmesh map temp"); - for (int i = 0; i < bvh->totnode; i++) { - for (int j = 0; j < bvh->totnode; j++) { - if (i == j || !bvh->nodes[i].draw_batches) { + for (int i = 0; i < pbvh->totnode; i++) { + for (int j = 0; j < pbvh->totnode; j++) { + if (i == j || !pbvh->nodes[i].draw_batches) { continue; } - if (bvh->nodes[i].draw_batches == bvh->nodes[j].draw_batches) { + if (pbvh->nodes[i].draw_batches == pbvh->nodes[j].draw_batches) { printf("%s: error %d %d\n", __func__, i, j); - bvh->nodes[j].draw_batches = nullptr; + pbvh->nodes[j].draw_batches = nullptr; } } } /* Build index map for child offsets. */ int j = 0; - for (int i = 0; i < bvh->totnode; i++) { - PBVHNode *n = bvh->nodes + i; + for (int i = 0; i < pbvh->totnode; i++) { + PBVHNode *n = pbvh->nodes + i; if (!(n->flag & PBVH_Delete)) { map[i] = j++; + totnode++; } else { if (n->layer_disp) { @@ -3786,7 +3877,7 @@ static void pbvh_bmesh_join_nodes(PBVH *bvh) n->layer_disp = nullptr; } - pbvh_free_draw_buffers(bvh, n); + pbvh_free_draw_buffers(pbvh, n); if (n->vert_indices) { MEM_freeN((void *)n->vert_indices); @@ -3798,7 +3889,7 @@ static void pbvh_bmesh_join_nodes(PBVH *bvh) } if (n->tribuf || n->tri_buffers) { - BKE_pbvh_bmesh_free_tris(bvh, n); + BKE_pbvh_bmesh_free_tris(pbvh, n); } if (n->bm_unique_verts) { @@ -3817,57 +3908,57 @@ static void pbvh_bmesh_join_nodes(PBVH *bvh) } #ifdef PROXY_ADVANCED - BKE_pbvh_free_proxyarray(bvh, n); + BKE_pbvh_free_proxyarray(pbvh, n); #endif } } // compact node array j = 0; - for (int i = 0; i < bvh->totnode; i++) { - if (!(bvh->nodes[i].flag & PBVH_Delete)) { - if (bvh->nodes[i].children_offset >= bvh->totnode - 1) { - printf("%s: error %i %i\n", __func__, i, bvh->nodes[i].children_offset); + for (int i = 0; i < pbvh->totnode; i++) { + if (!(pbvh->nodes[i].flag & PBVH_Delete)) { + if (pbvh->nodes[i].children_offset >= pbvh->totnode - 1) { + printf("%s: error %i %i\n", __func__, i, pbvh->nodes[i].children_offset); continue; } - int i1 = map[bvh->nodes[i].children_offset]; - int i2 = map[bvh->nodes[i].children_offset + 1]; + int i1 = map[pbvh->nodes[i].children_offset]; + int i2 = map[pbvh->nodes[i].children_offset + 1]; - if (bvh->nodes[i].children_offset >= bvh->totnode) { + if (pbvh->nodes[i].children_offset >= pbvh->totnode) { printf("%s: Bad child node reference %d->%d, totnode: %d\n", __func__, i, - bvh->nodes[i].children_offset, - bvh->totnode); + pbvh->nodes[i].children_offset, + pbvh->totnode); continue; } - if (bvh->nodes[i].children_offset && i2 != i1 + 1) { + if (pbvh->nodes[i].children_offset && i2 != i1 + 1) { printf(" pbvh corruption during node join %d %d\n", i1, i2); } - bvh->nodes[j] = bvh->nodes[i]; - bvh->nodes[j].children_offset = i1; + pbvh->nodes[j] = pbvh->nodes[i]; + pbvh->nodes[j].children_offset = i1; j++; } } if (j != totnode) { - printf("%s: pbvh error.", __func__); + printf("%s: pbvh error.\n", __func__); } - if (bvh->totnode != j) { - memset(bvh->nodes + j, 0, sizeof(*bvh->nodes) * (bvh->totnode - j)); - bvh->node_mem_count = j; + if (pbvh->totnode != j) { + memset(pbvh->nodes + j, 0, sizeof(*pbvh->nodes) * (pbvh->totnode - j)); + pbvh->node_mem_count = j; } - bvh->totnode = j; + pbvh->totnode = j; // set vert/face node indices again - for (int i = 0; i < bvh->totnode; i++) { - PBVHNode *n = bvh->nodes + i; + for (int i = 0; i < pbvh->totnode; i++) { + PBVHNode *n = pbvh->nodes + i; if (!(n->flag & PBVH_Leaf)) { continue; @@ -3883,22 +3974,22 @@ static void pbvh_bmesh_join_nodes(PBVH *bvh) BMVert *v; TGSET_ITER (v, n->bm_unique_verts) { - BM_ELEM_CD_SET_INT(v, bvh->cd_vert_node_offset, i); + BM_ELEM_CD_SET_INT(v, pbvh->cd_vert_node_offset, i); } TGSET_ITER_END BMFace *f; TGSET_ITER (f, n->bm_faces) { - BM_ELEM_CD_SET_INT(f, bvh->cd_face_node_offset, i); + BM_ELEM_CD_SET_INT(f, pbvh->cd_face_node_offset, i); } TGSET_ITER_END } Vector scratch; - for (int i = 0; i < bvh->totnode; i++) { - PBVHNode *n = bvh->nodes + i; + for (int i = 0; i < pbvh->totnode; i++) { + PBVHNode *n = pbvh->nodes + i; if (!(n->flag & PBVH_Leaf)) { continue; @@ -3908,11 +3999,11 @@ static void pbvh_bmesh_join_nodes(PBVH *bvh) BMVert *v; TGSET_ITER (v, n->bm_other_verts) { - int ni = BM_ELEM_CD_GET_INT(v, bvh->cd_vert_node_offset); + int ni = BM_ELEM_CD_GET_INT(v, pbvh->cd_vert_node_offset); if (ni == DYNTOPO_NODE_NONE) { scratch.append(v); } - // BM_ELEM_CD_SET_INT(v, bvh->cd_vert_node_offset, i); + // BM_ELEM_CD_SET_INT(v, pbvh->cd_vert_node_offset, i); } TGSET_ITER_END @@ -3921,7 +4012,7 @@ static void pbvh_bmesh_join_nodes(PBVH *bvh) BLI_table_gset_remove(n->bm_other_verts, v, nullptr); BLI_table_gset_add(n->bm_unique_verts, v); - BM_ELEM_CD_SET_INT(v, bvh->cd_vert_node_offset, i); + BM_ELEM_CD_SET_INT(v, pbvh->cd_vert_node_offset, i); } } diff --git a/source/blender/bmesh/intern/bmesh_construct.c b/source/blender/bmesh/intern/bmesh_construct.c index df5a6db27fc..113f0a0ab04 100644 --- a/source/blender/bmesh/intern/bmesh_construct.c +++ b/source/blender/bmesh/intern/bmesh_construct.c @@ -406,7 +406,7 @@ void BM_sort_disk_cycle(BMVert *v) /*************************************************************/ -ATTR_NO_OPT static void bm_vert_attrs_copy( +static void bm_vert_attrs_copy( BMesh *bm_src, BMesh *bm_dst, const BMVert *v_src, BMVert *v_dst, eCustomDataMask mask_exclude) { if ((bm_src == bm_dst) && (v_src == v_dst)) { @@ -422,7 +422,7 @@ ATTR_NO_OPT static void bm_vert_attrs_copy( &bm_src->vdata, &bm_dst->vdata, v_src->head.data, &v_dst->head.data, mask_exclude); } -ATTR_NO_OPT static void bm_edge_attrs_copy( +static void bm_edge_attrs_copy( BMesh *bm_src, BMesh *bm_dst, const BMEdge *e_src, BMEdge *e_dst, eCustomDataMask mask_exclude) { if ((bm_src == bm_dst) && (e_src == e_dst)) { @@ -435,7 +435,7 @@ ATTR_NO_OPT static void bm_edge_attrs_copy( &bm_src->edata, &bm_dst->edata, e_src->head.data, &e_dst->head.data, mask_exclude); } -ATTR_NO_OPT static void bm_loop_attrs_copy( +static void bm_loop_attrs_copy( BMesh *bm_src, BMesh *bm_dst, const BMLoop *l_src, BMLoop *l_dst, eCustomDataMask mask_exclude) { if ((bm_src == bm_dst) && (l_src == l_dst)) { @@ -448,7 +448,7 @@ ATTR_NO_OPT static void bm_loop_attrs_copy( &bm_src->ldata, &bm_dst->ldata, l_src->head.data, &l_dst->head.data, mask_exclude); } -ATTR_NO_OPT static void bm_face_attrs_copy( +static void bm_face_attrs_copy( BMesh *bm_src, BMesh *bm_dst, const BMFace *f_src, BMFace *f_dst, eCustomDataMask mask_exclude) { if ((bm_src == bm_dst) && (f_src == f_dst)) { diff --git a/source/blender/bmesh/intern/bmesh_idmap.cc b/source/blender/bmesh/intern/bmesh_idmap.cc index 1578f8a3036..d709cc3e33f 100644 --- a/source/blender/bmesh/intern/bmesh_idmap.cc +++ b/source/blender/bmesh/intern/bmesh_idmap.cc @@ -35,7 +35,7 @@ static void bm_idmap_debug_check_init(BMesh *bm) } #endif -ATTR_NO_OPT static void idmap_log_message(const char *fmt, ...) +static void idmap_log_message(const char *fmt, ...) { char msg[64]; @@ -46,7 +46,7 @@ ATTR_NO_OPT static void idmap_log_message(const char *fmt, ...) } #ifdef DEBUG_BM_IDMAP -ATTR_NO_OPT static void _idmap_debug_insert(const char *func, BMIdMap *idmap, BMElem *elem, int id) +static void _idmap_debug_insert(const char *func, BMIdMap *idmap, BMElem *elem, int id) { if (id == BM_ID_NONE) { idmap_log_message("%s: Tried to assign a null id\n", func); @@ -120,7 +120,7 @@ template static constexpr char get_elem_type() } #ifdef DEBUG_BM_IDMAP -ATTR_NO_OPT static bool _idmap_check_elem(const char *func, BMIdMap *idmap, BMElem *elem) +static bool _idmap_check_elem(const char *func, BMIdMap *idmap, BMElem *elem) { int id = BM_idmap_get_id(idmap, elem); bool exists = idmap->elem2id->contains(elem); @@ -187,7 +187,7 @@ static void idmap_grow_map(BMIdMap *idmap, int newid) idmap->map_size = newsize; } -ATTR_NO_OPT void BM_idmap_check_ids(BMIdMap *idmap) +void BM_idmap_check_ids(BMIdMap *idmap) { BMIter iter; BMVert *v; @@ -297,7 +297,7 @@ ATTR_NO_OPT void BM_idmap_check_ids(BMIdMap *idmap) idmap->maxid = max_id + 1; } -ATTR_NO_OPT void BM_idmap_check_attributes(BMIdMap *idmap) +void BM_idmap_check_attributes(BMIdMap *idmap) { auto check_attr = [&](int type) { if (!(idmap->flag & type)) { @@ -354,7 +354,7 @@ ATTR_NO_OPT void BM_idmap_check_attributes(BMIdMap *idmap) check_attr(BM_FACE); } -ATTR_NO_OPT void BM_idmap_destroy(BMIdMap *idmap) +void BM_idmap_destroy(BMIdMap *idmap) { #ifdef DEBUG_BM_IDMAP delete idmap->elem2id; @@ -365,7 +365,7 @@ ATTR_NO_OPT void BM_idmap_destroy(BMIdMap *idmap) MEM_delete(idmap); } -ATTR_NO_OPT static void check_idx_map(BMIdMap *idmap) +static void check_idx_map(BMIdMap *idmap) { if (idmap->free_idx_map && idmap->freelist.size() < FREELIST_HASHMAP_THRESHOLD_LOW) { // idmap_log_message("%s: Deleting free_idx_map\n", __func__); @@ -384,7 +384,7 @@ ATTR_NO_OPT static void check_idx_map(BMIdMap *idmap) } } -ATTR_NO_OPT int BM_idmap_alloc(BMIdMap *idmap, BMElem *elem) +int BM_idmap_alloc(BMIdMap *idmap, BMElem *elem) { int id = BM_ID_NONE; @@ -445,7 +445,7 @@ ATTR_NO_OPT int BM_idmap_alloc(BMIdMap *idmap, BMElem *elem) return id; } -ATTR_NO_OPT void BM_idmap_assign(BMIdMap *idmap, BMElem *elem, int id) +void BM_idmap_assign(BMIdMap *idmap, BMElem *elem, int id) { /* Remove id from freelist. */ if (idmap->free_idx_map) { @@ -500,7 +500,7 @@ ATTR_NO_OPT void BM_idmap_assign(BMIdMap *idmap, BMElem *elem, int id) #endif } -ATTR_NO_OPT void BM_idmap_release(BMIdMap *idmap, BMElem *elem, bool clear_id) +void BM_idmap_release(BMIdMap *idmap, BMElem *elem, bool clear_id) { #ifdef DEBUG_BM_IDMAP idmap_check_elem(idmap, elem); @@ -541,7 +541,7 @@ ATTR_NO_OPT void BM_idmap_release(BMIdMap *idmap, BMElem *elem, bool clear_id) } } -ATTR_NO_OPT int BM_idmap_check_assign(BMIdMap *idmap, BMElem *elem) +int BM_idmap_check_assign(BMIdMap *idmap, BMElem *elem) { int id = BM_ELEM_CD_GET_INT(elem, idmap->cd_id_off[int(elem->head.htype)]); diff --git a/source/blender/bmesh/intern/bmesh_interp.c b/source/blender/bmesh/intern/bmesh_interp.c index d7a2c9d5604..787ad72dc6a 100644 --- a/source/blender/bmesh/intern/bmesh_interp.c +++ b/source/blender/bmesh/intern/bmesh_interp.c @@ -26,10 +26,10 @@ #include "bmesh.h" #include "intern/bmesh_private.h" -ATTR_NO_OPT static void copy_cdata_simple(BMesh *bm, - CustomData *data_layer, - BMElem *ele_dst, - const BMElem *ele_src) +static void copy_cdata_simple(BMesh *bm, + CustomData *data_layer, + BMElem *ele_dst, + const BMElem *ele_src) { int cd_tflags; MToolFlags saved_tflags = {0}; @@ -47,12 +47,12 @@ ATTR_NO_OPT static void copy_cdata_simple(BMesh *bm, } /* edge and vertex share, currently there's no need to have different logic */ -ATTR_NO_OPT static void bm_data_interp_from_elem(BMesh *bm, - CustomData *data_layer, - const BMElem *ele_src_1, - const BMElem *ele_src_2, - BMElem *ele_dst, - const float fac) +static void bm_data_interp_from_elem(BMesh *bm, + CustomData *data_layer, + const BMElem *ele_src_1, + const BMElem *ele_src_2, + BMElem *ele_dst, + const float fac) { if (ele_src_1->head.data && ele_src_2->head.data) { /* first see if we can avoid interpolation */ @@ -781,7 +781,7 @@ void BM_vert_interp_from_face(BMesh *bm, BMVert *v_dst, const BMFace *f_src) void BPy_bm_new_customdata_layout(BMesh *bm, CustomData *cdata, void *state, char htype); void *BPy_bm_new_customdata_layout_pre(BMesh *bm, CustomData *cdata, char htype); -ATTR_NO_OPT static void update_data_blocks(BMesh *bm, CustomData *olddata, CustomData *data) +static void update_data_blocks(BMesh *bm, CustomData *olddata, CustomData *data) { BMIter iter; BLI_mempool *oldpool = olddata->pool; diff --git a/source/blender/bmesh/intern/bmesh_log.cc b/source/blender/bmesh/intern/bmesh_log.cc index 8abf3964f15..2b623dd8479 100644 --- a/source/blender/bmesh/intern/bmesh_log.cc +++ b/source/blender/bmesh/intern/bmesh_log.cc @@ -269,7 +269,7 @@ struct BMLogSetFull : public BMLogSetBase { mesh = BKE_mesh_from_bmesh_nomain(bm, ¶ms, nullptr); } - void ATTR_NO_OPT swap(BMesh *bm) + void swap(BMesh *bm) { CustomData_MeshMasks cd_mask_extra = {CD_MASK_DYNTOPO_VERT | CD_MASK_SHAPEKEY, 0, 0, 0, 0}; @@ -367,11 +367,11 @@ struct BMLogEntry { BMLog *log = nullptr; bool dead = false; - ATTR_NO_OPT BMLogEntry(BMIdMap *_idmap, - CustomData *src_vdata, - CustomData *src_edata, - CustomData *src_ldata, - CustomData *src_pdata) + BMLogEntry(BMIdMap *_idmap, + CustomData *src_vdata, + CustomData *src_edata, + CustomData *src_ldata, + CustomData *src_pdata) : idmap(_idmap) { CustomData_copy_all_layout(src_vdata, &vdata); @@ -385,7 +385,7 @@ struct BMLogEntry { CustomData_bmesh_init_pool(&pdata, 0, BM_FACE); } - ATTR_NO_OPT ~BMLogEntry() + ~BMLogEntry() { dead = true; @@ -482,7 +482,7 @@ struct BMLogEntry { return BM_idmap_get_id(idmap, reinterpret_cast(elem)); } - ATTR_NO_OPT void push_set(BMesh *bm, BMLogSetType type) + void push_set(BMesh *bm, BMLogSetType type) { switch (type) { case LOG_SET_DIFF: @@ -503,7 +503,7 @@ struct BMLogEntry { return static_cast(sets[sets.size() - 1]); } - ATTR_NO_OPT void update_logvert(BMesh *bm, BMVert *v, BMLogVert *lv) + void update_logvert(BMesh *bm, BMVert *v, BMLogVert *lv) { if (vdata.pool) { CustomData_bmesh_copy_data(&bm->vdata, &vdata, v->head.data, &lv->customdata); @@ -514,7 +514,7 @@ struct BMLogEntry { lv->flag = v->head.hflag; } - ATTR_NO_OPT void swap_logvert(BMesh *bm, BMID id, BMVert *v, BMLogVert *lv) + void swap_logvert(BMesh *bm, BMID id, BMVert *v, BMLogVert *lv) { if (v->head.data && lv->customdata) { CustomData_bmesh_swap_data(&vdata, &bm->vdata, lv->customdata, &v->head.data); @@ -525,14 +525,14 @@ struct BMLogEntry { swap_v3_v3(v->no, lv->no); } - ATTR_NO_OPT void swap_logedge(BMesh *bm, BMID id, BMEdge *e, BMLogEdge *le) + void swap_logedge(BMesh *bm, BMID id, BMEdge *e, BMLogEdge *le) { if (e->head.data && le->customdata) { CustomData_bmesh_swap_data(&edata, &bm->edata, le->customdata, &e->head.data); } } - ATTR_NO_OPT void swap_logface(BMesh *bm, BMID id, BMFace *f, BMLogFace *lf) + void swap_logface(BMesh *bm, BMID id, BMFace *f, BMLogFace *lf) { if (f->head.data && lf->customdata) { CustomData_bmesh_swap_data(&pdata, &bm->pdata, lf->customdata, &f->head.data); @@ -558,7 +558,7 @@ struct BMLogEntry { std::swap(f->head.hflag, lf->flag); } - ATTR_NO_OPT BMLogVert *alloc_logvert(BMesh *bm, BMVert *v) + BMLogVert *alloc_logvert(BMesh *bm, BMVert *v) { BMID id = get_elem_id(bm, v); BMLogVert *lv = vpool.alloc(); @@ -570,7 +570,7 @@ struct BMLogEntry { return lv; } - ATTR_NO_OPT void free_logvert(BMLogVert *lv) + void free_logvert(BMLogVert *lv) { if (lv->customdata) { BLI_mempool_free(vdata.pool, lv->customdata); @@ -579,7 +579,7 @@ struct BMLogEntry { vpool.free(lv); } - ATTR_NO_OPT void load_vert(BMesh *bm, BMVert *v, BMLogVert *lv) + void load_vert(BMesh *bm, BMVert *v, BMLogVert *lv) { if (v->head.data && lv->customdata) { CustomData_bmesh_copy_data(&vdata, &bm->vdata, lv->customdata, &v->head.data); @@ -717,7 +717,7 @@ struct BMLogEntry { current_diff_set(bm)->modify_face(bm, f); } - ATTR_NO_OPT void undo(BMesh *bm, BMLogCallbacks *callbacks) + void undo(BMesh *bm, BMLogCallbacks *callbacks) { for (int i = sets.size() - 1; i >= 0; i--) { // printf(" - %d of %d\n", i, (int)(sets.size() - 1)); @@ -725,7 +725,7 @@ struct BMLogEntry { } } - ATTR_NO_OPT void redo(BMesh *bm, BMLogCallbacks *callbacks) + void redo(BMesh *bm, BMLogCallbacks *callbacks) { for (int i = 0; i < sets.size(); i++) { sets[i]->redo(bm, callbacks); @@ -764,7 +764,7 @@ struct BMLog { { } - ATTR_NO_OPT void set_idmap(BMIdMap *idmap) + void set_idmap(BMIdMap *idmap) { BMLogEntry *entry = first_entry; while (entry) { @@ -773,7 +773,7 @@ struct BMLog { } } - ATTR_NO_OPT bool free_all_entries() + bool free_all_entries() { printf("Freeing all log entries.\n"); @@ -793,7 +793,7 @@ struct BMLog { return true; } - ATTR_NO_OPT BMLogEntry *push_entry(BMesh *bm) + BMLogEntry *push_entry(BMesh *bm) { BMLogEntry *entry = MEM_new( "BMLogEntry", idmap, &bm->vdata, &bm->edata, &bm->ldata, &bm->pdata); @@ -837,7 +837,7 @@ struct BMLog { } } - ATTR_NO_OPT void ensure_entry(BMesh *bm) + void ensure_entry(BMesh *bm) { if (!current_entry) { push_entry(bm); @@ -1057,7 +1057,7 @@ void BMLogSetDiff::add_face(BMesh *bm, BMFace *f) added_faces.add(id, lf); } -ATTR_NO_OPT void BMLogSetDiff::remove_face(BMesh *bm, BMFace *f, bool no_check) +void BMLogSetDiff::remove_face(BMesh *bm, BMFace *f, bool no_check) { BMID id = entry->get_elem_id(bm, f); @@ -1095,9 +1095,9 @@ void BMLogSetDiff::modify_face(BMesh *bm, BMFace *f) } } -ATTR_NO_OPT void BMLogSetDiff::swap_verts(BMesh *bm, - blender::Map, BMLogVert *> verts, - BMLogCallbacks *callbacks) +void BMLogSetDiff::swap_verts(BMesh *bm, + blender::Map, BMLogVert *> verts, + BMLogCallbacks *callbacks) { void *old_customdata = entry->vdata.pool ? BLI_mempool_alloc(bm->vdata.pool) : nullptr; @@ -1136,9 +1136,9 @@ ATTR_NO_OPT void BMLogSetDiff::swap_verts(BMesh *bm, } } -ATTR_NO_OPT void BMLogSetDiff::restore_verts(BMesh *bm, - blender::Map, BMLogVert *> verts, - BMLogCallbacks *callbacks) +void BMLogSetDiff::restore_verts(BMesh *bm, + blender::Map, BMLogVert *> verts, + BMLogCallbacks *callbacks) { for (BMLogVert *lv : verts.values()) { BMVert *v = BM_vert_create(bm, lv->co, nullptr, BM_CREATE_NOP); @@ -1158,9 +1158,9 @@ ATTR_NO_OPT void BMLogSetDiff::restore_verts(BMesh *bm, bm->elem_table_dirty |= BM_VERT | BM_EDGE; } -ATTR_NO_OPT void BMLogSetDiff::remove_verts(BMesh *bm, - blender::Map, BMLogVert *> verts, - BMLogCallbacks *callbacks) +void BMLogSetDiff::remove_verts(BMesh *bm, + blender::Map, BMLogVert *> verts, + BMLogCallbacks *callbacks) { for (BMLogVert *lv : verts.values()) { BMVert *v = entry->get_elem_from_id(bm, lv->id); @@ -1268,9 +1268,9 @@ void BMLogSetDiff::swap_edges(BMesh *bm, } } -ATTR_NO_OPT void BMLogSetDiff::restore_faces(BMesh *bm, - blender::Map, BMLogFace *> faces, - BMLogCallbacks *callbacks) +void BMLogSetDiff::restore_faces(BMesh *bm, + blender::Map, BMLogFace *> faces, + BMLogCallbacks *callbacks) { Vector verts; @@ -1320,9 +1320,9 @@ ATTR_NO_OPT void BMLogSetDiff::restore_faces(BMesh *bm, bm->elem_table_dirty |= BM_FACE; } -ATTR_NO_OPT void BMLogSetDiff::remove_faces(BMesh *bm, - blender::Map, BMLogFace *> faces, - BMLogCallbacks *callbacks) +void BMLogSetDiff::remove_faces(BMesh *bm, + blender::Map, BMLogFace *> faces, + BMLogCallbacks *callbacks) { for (BMLogFace *lf : faces.values()) { BMFace *f = entry->get_elem_from_id(bm, lf->id); @@ -1344,9 +1344,9 @@ ATTR_NO_OPT void BMLogSetDiff::remove_faces(BMesh *bm, bm->elem_table_dirty |= BM_FACE; } -ATTR_NO_OPT void BMLogSetDiff::swap_faces(BMesh *bm, - blender::Map, BMLogFace *> faces, - BMLogCallbacks *callbacks) +void BMLogSetDiff::swap_faces(BMesh *bm, + blender::Map, BMLogFace *> faces, + BMLogCallbacks *callbacks) { void *old_customdata = entry->pdata.pool ? BLI_mempool_alloc(bm->pdata.pool) : nullptr; @@ -1385,7 +1385,7 @@ ATTR_NO_OPT void BMLogSetDiff::swap_faces(BMesh *bm, } } -ATTR_NO_OPT void BMLogSetDiff::undo(BMesh *bm, BMLogCallbacks *callbacks) +void BMLogSetDiff::undo(BMesh *bm, BMLogCallbacks *callbacks) { remove_faces(bm, added_faces, callbacks); remove_edges(bm, added_edges, callbacks); @@ -1399,7 +1399,7 @@ ATTR_NO_OPT void BMLogSetDiff::undo(BMesh *bm, BMLogCallbacks *callbacks) swap_verts(bm, modified_verts, callbacks); } -ATTR_NO_OPT void BMLogSetDiff::redo(BMesh *bm, BMLogCallbacks *callbacks) +void BMLogSetDiff::redo(BMesh *bm, BMLogCallbacks *callbacks) { remove_faces(bm, removed_faces, callbacks); remove_edges(bm, removed_edges, callbacks); @@ -1415,9 +1415,7 @@ ATTR_NO_OPT void BMLogSetDiff::redo(BMesh *bm, BMLogCallbacks *callbacks) } } // namespace blender -ATTR_NO_OPT BMLog *BM_log_from_existing_entries_create(BMesh *bm, - BMIdMap *idmap, - BMLogEntry *entry) +BMLog *BM_log_from_existing_entries_create(BMesh *bm, BMIdMap *idmap, BMLogEntry *entry) { BMLog *log = BM_log_create(bm, idmap); log->load_entries(entry); @@ -1425,14 +1423,14 @@ ATTR_NO_OPT BMLog *BM_log_from_existing_entries_create(BMesh *bm, return log; } -ATTR_NO_OPT BMLog *BM_log_create(BMesh *bm, BMIdMap *idmap) +BMLog *BM_log_create(BMesh *bm, BMIdMap *idmap) { BMLog *log = MEM_new("BMLog", idmap); return log; } -ATTR_NO_OPT void BM_log_set_idmap(BMLog *log, struct BMIdMap *idmap) +void BM_log_set_idmap(BMLog *log, struct BMIdMap *idmap) { log->set_idmap(idmap); } @@ -1440,7 +1438,7 @@ ATTR_NO_OPT void BM_log_set_idmap(BMLog *log, struct BMIdMap *idmap) /* Free all the data in a BMLog including the log itself * safe_mode means log->refcount will be checked, and if nonzero log will not be freed */ -ATTR_NO_OPT static bool bm_log_free_direct(BMLog *log, bool safe_mode) +static bool bm_log_free_direct(BMLog *log, bool safe_mode) { if (safe_mode && log->refcount) { return false; @@ -1453,12 +1451,12 @@ ATTR_NO_OPT static bool bm_log_free_direct(BMLog *log, bool safe_mode) } // if true, make sure to call BM_log_free on the log -ATTR_NO_OPT bool BM_log_is_dead(BMLog *log) +bool BM_log_is_dead(BMLog *log) { return log->dead; } -ATTR_NO_OPT bool BM_log_free(BMLog *log, bool safe_mode) +bool BM_log_free(BMLog *log, bool safe_mode) { if (log->dead) { MEM_delete(log); @@ -1473,7 +1471,7 @@ ATTR_NO_OPT bool BM_log_free(BMLog *log, bool safe_mode) return false; } -ATTR_NO_OPT BMLogEntry *BM_log_entry_add_ex(BMesh *bm, BMLog *log, bool combine_with_last) +BMLogEntry *BM_log_entry_add_ex(BMesh *bm, BMLog *log, bool combine_with_last) { if (combine_with_last && log->current_entry) { log->current_entry->push_set(bm, LOG_SET_DIFF); @@ -1486,7 +1484,7 @@ ATTR_NO_OPT BMLogEntry *BM_log_entry_add_ex(BMesh *bm, BMLog *log, bool combine_ return log->current_entry; } -ATTR_NO_OPT BMLogEntry *BM_log_entry_add(BMesh *bm, BMLog *log) +BMLogEntry *BM_log_entry_add(BMesh *bm, BMLog *log) { log->push_entry(bm)->push_set(bm, LOG_SET_DIFF); return log->current_entry; @@ -1544,11 +1542,11 @@ void BM_log_edge_removed(BMesh *bm, BMLog *log, BMEdge *e) log->remove_edge(bm, e); } -ATTR_NO_OPT void BM_log_face_added(BMesh *bm, BMLog *log, BMFace *f) +void BM_log_face_added(BMesh *bm, BMLog *log, BMFace *f) { log->add_face(bm, f); } -ATTR_NO_OPT void BM_log_face_modified(BMesh *bm, BMLog *log, BMFace *f) +void BM_log_face_modified(BMesh *bm, BMLog *log, BMFace *f) { log->modify_face(bm, f); } diff --git a/source/blender/draw/intern/draw_pbvh.cc b/source/blender/draw/intern/draw_pbvh.cc index 4eddd39f798..5b02e7ed80b 100644 --- a/source/blender/draw/intern/draw_pbvh.cc +++ b/source/blender/draw/intern/draw_pbvh.cc @@ -759,7 +759,7 @@ struct PBVHBatches { } } - ATTR_NO_OPT void fill_vbo_bmesh(PBVHVbo &vbo, PBVH_GPU_Args *args) + void fill_vbo_bmesh(PBVHVbo &vbo, PBVH_GPU_Args *args) { auto foreach_bmesh_normal = [&](std::function callback) { for (int i : IndexRange(args->tribuf->tottri)) { @@ -917,8 +917,6 @@ struct PBVHBatches { const int cd_id = CustomData_get_offset_named( &args->bm->vdata, CD_PROP_INT32, "vertex_id"); - printf("cd_id: %d\n", cd_id); - foreach_bmesh([&](BMLoop *l) { int *id = BM_ELEM_CD_PTR(l->v, cd_id); diff --git a/source/blender/editors/mesh/editmesh_mask_extract.cc b/source/blender/editors/mesh/editmesh_mask_extract.cc index 5695ef1e918..4f5b1225db8 100644 --- a/source/blender/editors/mesh/editmesh_mask_extract.cc +++ b/source/blender/editors/mesh/editmesh_mask_extract.cc @@ -74,10 +74,10 @@ struct GeometryExtractParams { /* Function that tags in BMesh the faces that should be deleted in the extracted object. */ using GeometryExtractTagMeshFunc = void(BMesh *, GeometryExtractParams *); -ATTR_NO_OPT static int geometry_extract_apply(bContext *C, - wmOperator *op, - GeometryExtractTagMeshFunc *tag_fn, - GeometryExtractParams *params) +static int geometry_extract_apply(bContext *C, + wmOperator *op, + GeometryExtractTagMeshFunc *tag_fn, + GeometryExtractParams *params) { Main *bmain = CTX_data_main(C); Object *ob = CTX_data_active_object(C); diff --git a/source/blender/editors/sculpt_paint/sculpt.cc b/source/blender/editors/sculpt_paint/sculpt.cc index 8cca6b92e66..031713b61f5 100644 --- a/source/blender/editors/sculpt_paint/sculpt.cc +++ b/source/blender/editors/sculpt_paint/sculpt.cc @@ -403,7 +403,7 @@ void SCULPT_vertex_persistent_normal_get(SculptSession *ss, PBVHVertRef vertex, SCULPT_vertex_normal_get(ss, vertex, no); } -ATTR_NO_OPT float SCULPT_vertex_mask_get(SculptSession *ss, PBVHVertRef vertex) +float SCULPT_vertex_mask_get(SculptSession *ss, PBVHVertRef vertex) { switch (BKE_pbvh_type(ss->pbvh)) { case PBVH_FACES: @@ -2917,11 +2917,11 @@ void SCULPT_calc_area_normal_and_center( * values pull vertices, negative values push. Uses tablet pressure and a * special multiplier found experimentally to scale the strength factor. */ -ATTR_NO_OPT static float brush_strength(const Sculpt *sd, - const StrokeCache *cache, - const float feather, - const UnifiedPaintSettings *ups, - const PaintModeSettings * /*paint_mode_settings*/) +static float brush_strength(const Sculpt *sd, + const StrokeCache *cache, + const float feather, + const UnifiedPaintSettings *ups, + const PaintModeSettings * /*paint_mode_settings*/) { const Scene *scene = cache->vc->scene; const Brush *brush = BKE_paint_brush((Paint *)&sd->paint); diff --git a/source/blender/editors/sculpt_paint/sculpt_face_set.cc b/source/blender/editors/sculpt_paint/sculpt_face_set.cc index 91acbb6a092..393841c41ac 100644 --- a/source/blender/editors/sculpt_paint/sculpt_face_set.cc +++ b/source/blender/editors/sculpt_paint/sculpt_face_set.cc @@ -296,9 +296,9 @@ static int new_fset_apply_curve(SculptSession *ss, return new_fset; } -ATTR_NO_OPT void do_draw_face_sets_brush_task_cb_ex(void *__restrict userdata, - const int n, - const TaskParallelTLS *__restrict tls) +void do_draw_face_sets_brush_task_cb_ex(void *__restrict userdata, + const int n, + const TaskParallelTLS *__restrict tls) { SculptFaceSetDrawData *data = (SculptFaceSetDrawData *)userdata; SculptSession *ss = data->ob->sculpt; @@ -654,10 +654,7 @@ static void do_relax_face_sets_brush_task_cb_ex(void *__restrict userdata, BKE_pbvh_vertex_iter_end; } -ATTR_NO_OPT void SCULPT_do_draw_face_sets_brush(Sculpt *sd, - Object *ob, - PBVHNode **nodes, - int totnode) +void SCULPT_do_draw_face_sets_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) { SculptSession *ss = ob->sculpt; Brush *brush = ss->cache->brush ? ss->cache->brush : BKE_paint_brush(&sd->paint); @@ -2253,11 +2250,11 @@ static void sculpt_bm_mesh_elem_hflag_disable_all(BMesh *bm, char htype, char hf } } -ATTR_NO_OPT static void sculpt_face_set_extrude_id(Object *ob, - bool no_islands, - SculptSession *ss, - const int active_face_set_id, - FaceSetExtrudeCD *fsecd) +static void sculpt_face_set_extrude_id(Object *ob, + bool no_islands, + SculptSession *ss, + const int active_face_set_id, + FaceSetExtrudeCD *fsecd) { Mesh *mesh = (Mesh *)ob->data; @@ -2858,9 +2855,7 @@ void SCULPT_face_set_island_free(SculptFaceSetIsland *island) } } -ATTR_NO_OPT static int sculpt_face_set_edit_modal(bContext *C, - wmOperator *op, - const wmEvent *event) +static int sculpt_face_set_edit_modal(bContext *C, wmOperator *op, const wmEvent *event) { Object *ob = CTX_data_active_object(C); SculptSession *ss = ob->sculpt; diff --git a/source/blender/editors/sculpt_paint/sculpt_smooth.cc b/source/blender/editors/sculpt_paint/sculpt_smooth.cc index ac195e72205..9f4a978cb81 100644 --- a/source/blender/editors/sculpt_paint/sculpt_smooth.cc +++ b/source/blender/editors/sculpt_paint/sculpt_smooth.cc @@ -31,14 +31,14 @@ using blender::float2; using blender::float3; using blender::Vector; -ATTR_NO_OPT static void SCULPT_neighbor_coords_average_interior_ex(SculptSession *ss, - float result[3], - PBVHVertRef vertex, - float projection, - float fset_projection, - bool weighted, - eSculptBoundary bound_type, - eSculptCorner corner_type) +static void SCULPT_neighbor_coords_average_interior_ex(SculptSession *ss, + float result[3], + PBVHVertRef vertex, + float projection, + float fset_projection, + bool weighted, + eSculptBoundary bound_type, + eSculptCorner corner_type) { float3 avg(0.0f, 0.0f, 0.0f); int neighbor_count = 0; @@ -545,9 +545,9 @@ static void SCULPT_enhance_details_brush(Sculpt *sd, BLI_task_parallel_range(0, totnode, &data, do_enhance_details_brush_task_cb_ex, &settings); } -ATTR_NO_OPT static void do_smooth_brush_task_cb_ex(void *__restrict userdata, - const int n, - const TaskParallelTLS *__restrict tls) +static void do_smooth_brush_task_cb_ex(void *__restrict userdata, + const int n, + const TaskParallelTLS *__restrict tls) { SculptThreadedTaskData *data = static_cast(userdata); SculptSession *ss = data->ob->sculpt; diff --git a/source/blender/editors/sculpt_paint/sculpt_undo.cc b/source/blender/editors/sculpt_paint/sculpt_undo.cc index 973b40fb910..e7aaa7addf4 100644 --- a/source/blender/editors/sculpt_paint/sculpt_undo.cc +++ b/source/blender/editors/sculpt_paint/sculpt_undo.cc @@ -2146,9 +2146,7 @@ void SCULPT_undo_ensure_bmlog(Object *ob) } } -ATTR_NO_OPT static SculptUndoNode *sculpt_undo_bmesh_push(Object *ob, - PBVHNode *node, - SculptUndoType type) +static SculptUndoNode *sculpt_undo_bmesh_push(Object *ob, PBVHNode *node, SculptUndoType type) { UndoSculpt *usculpt = sculpt_undo_get_nodes(); SculptSession *ss = ob->sculpt; -- 2.30.2 From d2ef2d905ce8891efc9cd5ad45e30b07098eb4e2 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Tue, 28 Mar 2023 03:08:05 -0700 Subject: [PATCH 045/279] temp-sculpt-dyntopo: Make use of DynTopoSettings again All DynTopo settings are now stored in a single struct, DynTopoSettings, which is used by both Brush and Sculpt. DynTopo settings can be set to either inherit from Sculpt.dyntopo or Brush.dyntopo; this in controlled with Brush.dyntopo.inherit which is a bitflag. This is the original system I came up prior to the more generic brush property proposals. I decided to bring it back for the same reason I wrote it in the first place: to avoid hardcoding behavior for specific brushes, specifically: * Simplify always has subdivide and collapse on. * Snake hook needs a much smaller dyntopo spacing than other tools. --- .../startup/bl_ui/properties_paint_common.py | 27 +++++++ scripts/startup/bl_ui/space_view3d_toolbar.py | 72 ++++++++++++++++--- source/blender/blenkernel/BKE_paint.h | 3 + source/blender/blenkernel/intern/brush.cc | 20 ++++-- source/blender/blenkernel/intern/paint.cc | 3 + .../blenloader/intern/versioning_300.cc | 67 ++++++++++++++++- source/blender/editors/sculpt_paint/sculpt.cc | 42 ++++++----- .../editors/sculpt_paint/sculpt_api.cc | 35 +++++++++ .../editors/sculpt_paint/sculpt_detail.cc | 6 +- .../editors/sculpt_paint/sculpt_intern.hh | 1 + source/blender/makesdna/DNA_brush_defaults.h | 26 +++---- source/blender/makesdna/DNA_brush_enums.h | 30 ++++++-- source/blender/makesdna/DNA_brush_types.h | 2 +- source/blender/makesdna/DNA_scene_types.h | 32 ++++----- source/blender/makesdna/intern/dna_defaults.c | 3 + source/blender/makesrna/intern/rna_brush.c | 1 - .../makesrna/intern/rna_sculpt_paint.c | 8 +-- 17 files changed, 297 insertions(+), 81 deletions(-) diff --git a/scripts/startup/bl_ui/properties_paint_common.py b/scripts/startup/bl_ui/properties_paint_common.py index a435c4be8d8..54088cbf6b1 100644 --- a/scripts/startup/bl_ui/properties_paint_common.py +++ b/scripts/startup/bl_ui/properties_paint_common.py @@ -93,6 +93,7 @@ class UnifiedPaintPanel: text=None, slider=False, header=False, + dyntopo=False, ): """ Generalized way of adding brush options to the UI, along with their pen pressure setting and global toggle, if they exist. """ @@ -113,6 +114,32 @@ class UnifiedPaintPanel: return row + @staticmethod + def get_dyntopo_prop(context, brush, prop_name): + sculpt = context.tool_settings.sculpt + + if prop_name.upper() in brush.dyntopo.inherit: + return getattr(sculpt.dyntopo, prop_name) + else: + return getattr(brush.dyntopo, prop_name) + + @staticmethod + def prop_unified_dyntopo(layout, context, brush, prop_name, text=None, show_inherit=True, expand=False): + sculpt = context.tool_settings.sculpt + + if prop_name.upper() in brush.dyntopo.inherit: + final_dyntopo = sculpt.dyntopo + else: + final_dyntopo = brush.dyntopo + + if show_inherit: + layout = layout.row(align=True) + + layout.prop(final_dyntopo, prop_name, text=text, expand=expand) + + if show_inherit: + layout.prop_enum(brush.dyntopo, "inherit", prop_name.upper(), text="") + @staticmethod def prop_unified_color(parent, context, brush, prop_name, *, text=None): ups = context.tool_settings.unified_paint_settings diff --git a/scripts/startup/bl_ui/space_view3d_toolbar.py b/scripts/startup/bl_ui/space_view3d_toolbar.py index 69d4f0d02eb..6ae0c565c2f 100644 --- a/scripts/startup/bl_ui/space_view3d_toolbar.py +++ b/scripts/startup/bl_ui/space_view3d_toolbar.py @@ -876,23 +876,77 @@ class VIEW3D_PT_sculpt_dyntopo(Panel, View3DPaintPanel): col.prop(sculpt, "use_dyntopo") sub = col.column() - sub.active = (brush and brush.sculpt_tool != 'MASK') - if sculpt.detail_type_method in {'CONSTANT', 'MANUAL'}: + sub.active = bool(brush) + + detail_mode = UnifiedPaintPanel.get_dyntopo_prop(context, brush, "mode") + + if detail_mode in {'CONSTANT', 'MANUAL'}: row = sub.row(align=True) - row.prop(sculpt, "constant_detail_resolution") + + UnifiedPaintPanel.prop_unified_dyntopo( + row, + context, + brush, + "constant_detail" + ) + props = row.operator("sculpt.sample_detail_size", text="", icon='EYEDROPPER') props.mode = 'DYNTOPO' - elif (sculpt.detail_type_method == 'BRUSH'): - sub.prop(sculpt, "detail_percent") + elif detail_mode == 'BRUSH': + UnifiedPaintPanel.prop_unified_dyntopo( + sub, + context, + brush, + "detail_percent" + ) else: - sub.prop(sculpt, "detail_size") - sub.prop(sculpt, "detail_refine_method", text="Refine Method") - sub.prop(sculpt, "detail_type_method", text="Detailing") + UnifiedPaintPanel.prop_unified_dyntopo( + sub, + context, + brush, + "detail_size" + ) + + UnifiedPaintPanel.prop_unified_dyntopo( + sub, + context, + brush, + "subdivide" + ) + + UnifiedPaintPanel.prop_unified_dyntopo( + sub, + context, + brush, + "collapse" + ) + + UnifiedPaintPanel.prop_unified_dyntopo( + sub, + context, + brush, + "mode", + expand=True + ) + + UnifiedPaintPanel.prop_unified_dyntopo( + sub, + context, + brush, + "detail_range" + ) if sculpt.detail_type_method in {'CONSTANT', 'MANUAL'}: col.operator("sculpt.detail_flood_fill") - col.prop(sculpt, "dyntopo_spacing") + UnifiedPaintPanel.prop_unified_dyntopo( + sub, + context, + brush, + "spacing", + expand=True + ) + col.prop(sculpt, "use_smooth_shading") col.prop(sculpt, "use_flat_vcol_shading") diff --git a/source/blender/blenkernel/BKE_paint.h b/source/blender/blenkernel/BKE_paint.h index fe28f48a59b..d1297a6f381 100644 --- a/source/blender/blenkernel/BKE_paint.h +++ b/source/blender/blenkernel/BKE_paint.h @@ -736,6 +736,8 @@ typedef struct SculptAttributePointers { } SculptAttributePointers; #ifdef __cplusplus +#include "DNA_brush_types.h" + struct SculptSession { /* Mesh data (not copied) can come either directly from a Mesh, or from a MultiresDM */ struct { /* Special handling for multires meshes */ @@ -991,6 +993,7 @@ struct SculptSession { bool islands_valid; /* Is attrs.topology_island_key valid? */ bool hard_edge_mode; + DynTopoSettings cached_dyntopo; }; #else struct SculptSession; diff --git a/source/blender/blenkernel/intern/brush.cc b/source/blender/blenkernel/intern/brush.cc index 2f2d0d7d885..a00e21ed478 100644 --- a/source/blender/blenkernel/intern/brush.cc +++ b/source/blender/blenkernel/intern/brush.cc @@ -1816,11 +1816,13 @@ void BKE_brush_sculpt_reset(Brush *br) case SCULPT_TOOL_SNAKE_HOOK: br->alpha = 1.0f; br->rake_factor = 1.0f; - br->dyntopo.inherit = DYNTOPO_INHERIT_BITMASK & - ~(DYNTOPO_INHERIT_ALL | DYNTOPO_LOCAL_COLLAPSE | + br->dyntopo.inherit = ~(DYNTOPO_INHERIT_SPACING | DYNTOPO_INHERIT_SUBDIVIDE | + DYNTOPO_INHERIT_COLLAPSE | DYNTOPO_INHERIT_DETAIL_RANGE | DYNTOPO_INHERIT_DETAIL_RANGE); - br->dyntopo.flag |= DYNTOPO_LOCAL_COLLAPSE; + br->dyntopo.flag |= DYNTOPO_SUBDIVIDE | DYNTOPO_COLLAPSE; br->dyntopo.detail_range = 0.4f; + br->dyntopo.spacing = 5; + br->dyntopo.radius_scale = 1.15; break; case SCULPT_TOOL_THUMB: br->size = 75; @@ -1906,6 +1908,10 @@ void BKE_brush_sculpt_reset(Brush *br) br->flag &= ~BRUSH_SPACE_ATTEN; br->curve_preset = BRUSH_CURVE_SMOOTHER; break; + case SCULPT_TOOL_SIMPLIFY: + br->dyntopo.inherit = ~(DYNTOPO_INHERIT_COLLAPSE | DYNTOPO_INHERIT_SUBDIVIDE); + br->dyntopo.flag |= DYNTOPO_COLLAPSE | DYNTOPO_SUBDIVIDE; + break; default: break; } @@ -1947,17 +1953,17 @@ void BKE_brush_sculpt_reset(Brush *br) br->sub_col[2] = 0.117f; break; - case SCULPT_TOOL_PINCH: case SCULPT_TOOL_GRAB: - case SCULPT_TOOL_SNAKE_HOOK: - case SCULPT_TOOL_THUMB: - case SCULPT_TOOL_NUDGE: case SCULPT_TOOL_ROTATE: case SCULPT_TOOL_ELASTIC_DEFORM: case SCULPT_TOOL_POSE: case SCULPT_TOOL_BOUNDARY: case SCULPT_TOOL_SLIDE_RELAX: disable_dyntopo = true; + case SCULPT_TOOL_THUMB: + case SCULPT_TOOL_NUDGE: + case SCULPT_TOOL_PINCH: + case SCULPT_TOOL_SNAKE_HOOK: br->add_col[0] = 1.0f; br->add_col[1] = 0.95f; br->add_col[2] = 0.005f; diff --git a/source/blender/blenkernel/intern/paint.cc b/source/blender/blenkernel/intern/paint.cc index a05cc5b5015..275de36e235 100644 --- a/source/blender/blenkernel/intern/paint.cc +++ b/source/blender/blenkernel/intern/paint.cc @@ -11,6 +11,7 @@ #include "MEM_guardedalloc.h" #include "DNA_brush_types.h" +#include "DNA_defaults.h" #include "DNA_gpencil_legacy_types.h" #include "DNA_key_types.h" #include "DNA_mesh_types.h" @@ -2460,6 +2461,8 @@ void BKE_sculpt_toolsettings_data_ensure(Scene *scene) sd->detail_percent = 25; } + sd->dyntopo = *DNA_struct_default_get(DynTopoSettings); + if (!sd->dyntopo_spacing) { sd->dyntopo_spacing = 35; } diff --git a/source/blender/blenloader/intern/versioning_300.cc b/source/blender/blenloader/intern/versioning_300.cc index 6dfe9cd6bff..6931669ff77 100644 --- a/source/blender/blenloader/intern/versioning_300.cc +++ b/source/blender/blenloader/intern/versioning_300.cc @@ -27,6 +27,7 @@ #include "DNA_constraint_types.h" #include "DNA_curve_types.h" #include "DNA_curves_types.h" +#include "DNA_defaults.h" #include "DNA_genfile.h" #include "DNA_gpencil_modifier_types.h" #include "DNA_light_types.h" @@ -49,6 +50,7 @@ #include "BKE_armature.h" #include "BKE_asset.h" #include "BKE_attribute.h" +#include "BKE_brush.h" #include "BKE_collection.h" #include "BKE_colortools.h" #include "BKE_curve.h" @@ -4154,8 +4156,58 @@ void blo_do_versions_300(FileData *fd, Library * /*lib*/, Main *bmain) } } - if (!DNA_struct_elem_find(fd->filesdna, "Brush", "DynTopoSettings", "dyntopo")) { + if (!DNA_struct_elem_find(fd->filesdna, "Sculpt", "DynTopoSettings", "dyntopo")) { + LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) { + if (!scene->toolsettings || !scene->toolsettings->sculpt) { + continue; + } + + Sculpt *sculpt = scene->toolsettings->sculpt; + sculpt->dyntopo = *DNA_struct_default_get(DynTopoSettings); + + DynTopoSettings *ds = &sculpt->dyntopo; + + ds->detail_percent = sculpt->detail_percent; + ds->detail_size = sculpt->detail_size; + ds->detail_range = sculpt->detail_range; + ds->constant_detail = sculpt->constant_detail; + + ds->flag = 0; + if (sculpt->flags & SCULPT_DYNTOPO_SUBDIVIDE) { + ds->flag |= DYNTOPO_SUBDIVIDE; + } + if (sculpt->flags & SCULPT_DYNTOPO_COLLAPSE) { + ds->flag |= DYNTOPO_COLLAPSE; + } + + if (sculpt->flags & SCULPT_DYNTOPO_DETAIL_CONSTANT) { + ds->mode = DYNTOPO_DETAIL_CONSTANT; + } + else if (sculpt->flags & SCULPT_DYNTOPO_DETAIL_BRUSH) { + ds->mode = DYNTOPO_DETAIL_BRUSH; + } + else if (sculpt->flags & SCULPT_DYNTOPO_DETAIL_MANUAL) { + ds->mode = DYNTOPO_DETAIL_MANUAL; + } + else { + ds->mode = DYNTOPO_DETAIL_RELATIVE; + } + + if (sculpt->flags & SCULPT_DYNTOPO_SUBDIVIDE) { + ds->flag |= DYNTOPO_SUBDIVIDE; + } + if (sculpt->flags & SCULPT_DYNTOPO_COLLAPSE) { + ds->flag |= DYNTOPO_COLLAPSE; + } + } + } + + if (!DNA_struct_elem_find(fd->filesdna, "Brush", "DynTopoSettings", "dyntopo") || + !MAIN_VERSION_ATLEAST(bmain, 306, 4)) { LISTBASE_FOREACH (Brush *, brush, &bmain->brushes) { + DynTopoSettings *ds = &brush->dyntopo; + brush->dyntopo = *DNA_struct_default_get(DynTopoSettings); + if (ELEM(brush->sculpt_tool, SCULPT_TOOL_SMOOTH, SCULPT_TOOL_DISPLACEMENT_ERASER, @@ -4170,6 +4222,19 @@ void blo_do_versions_300(FileData *fd, Library * /*lib*/, Main *bmain) if (ELEM(brush->sculpt_tool, SCULPT_TOOL_SMOOTH, SCULPT_TOOL_SLIDE_RELAX)) { brush->flag2 |= BRUSH_SMOOTH_USE_AREA_WEIGHT; } + + /* Some tools need special dyntopo overrides; copy from defaults. */ + if (ELEM(brush->sculpt_tool, SCULPT_TOOL_SNAKE_HOOK, SCULPT_TOOL_SIMPLIFY)) { + Brush dummy = {}; + dummy.sculpt_tool = brush->sculpt_tool; + + BKE_brush_sculpt_reset(&dummy); + if (dummy.curve) { + BKE_curvemapping_free(dummy.curve); + } + + brush->dyntopo = dummy.dyntopo; + } } } diff --git a/source/blender/editors/sculpt_paint/sculpt.cc b/source/blender/editors/sculpt_paint/sculpt.cc index 031713b61f5..720e53ee9b9 100644 --- a/source/blender/editors/sculpt_paint/sculpt.cc +++ b/source/blender/editors/sculpt_paint/sculpt.cc @@ -2760,7 +2760,7 @@ void SCULPT_calc_area_center( { const Brush *brush = BKE_paint_brush(&sd->paint); SculptSession *ss = ob->sculpt; - const bool has_bm_orco = ss->bm && SCULPT_stroke_is_dynamic_topology(ss, brush); + const bool has_bm_orco = ss->bm; int n; /* Intentionally set 'sd' to nullptr since we share logic with vertex paint. */ @@ -2818,7 +2818,7 @@ bool SCULPT_pbvh_calc_area_normal(const Brush *brush, float r_area_no[3]) { SculptSession *ss = ob->sculpt; - const bool has_bm_orco = ss->bm && SCULPT_stroke_is_dynamic_topology(ss, brush); + const bool has_bm_orco = ss->bm; /* Intentionally set 'sd' to nullptr since this is used for vertex paint too. */ SculptThreadedTaskData data{}; @@ -2855,7 +2855,7 @@ void SCULPT_calc_area_normal_and_center( { const Brush *brush = BKE_paint_brush(&sd->paint); SculptSession *ss = ob->sculpt; - const bool has_bm_orco = ss->bm && SCULPT_stroke_is_dynamic_topology(ss, brush); + const bool has_bm_orco = ss->bm; int n; /* Intentionally set 'sd' to nullptr since this is used for vertex paint too. */ @@ -4105,9 +4105,8 @@ static void sculpt_topology_update(Sculpt *sd, PBVHTopologyUpdateMode mode = PBVHTopologyUpdateMode(0); float location[3]; -#if 0 - int dyntopo_mode = brush->dyntopo.flag; - int dyntopo_detail_mode = brush->dyntopo.mode; + int dyntopo_mode = ss->cached_dyntopo.flag; + int dyntopo_detail_mode = ss->cached_dyntopo.mode; if (dyntopo_detail_mode != DYNTOPO_DETAIL_MANUAL) { if (dyntopo_mode & DYNTOPO_SUBDIVIDE) { @@ -4124,22 +4123,19 @@ static void sculpt_topology_update(Sculpt *sd, mode |= PBVH_LocalCollapse | PBVH_Collapse; } } - - if (dyntopo_mode & DYNTOPO_CLEANUP) { - mode |= PBVH_Cleanup; - } -#endif - - if (sd->flags & SCULPT_DYNTOPO_COLLAPSE) { - mode |= PBVH_Collapse; - } - if (sd->flags & SCULPT_DYNTOPO_SUBDIVIDE) { - mode |= PBVH_Subdivide; + else { + if (dyntopo_mode & DYNTOPO_SUBDIVIDE) { + mode |= PBVH_Subdivide; + } + if (dyntopo_mode & DYNTOPO_COLLAPSE) { + mode |= PBVH_Collapse; + } } /* Force both subdivide and collapse for simplify brush. */ + //XXX done with inherit flags now if (brush->sculpt_tool == SCULPT_TOOL_SIMPLIFY) { - mode |= PBVH_Collapse | PBVH_Subdivide; + //mode |= PBVH_Collapse | PBVH_Subdivide; } SculptSearchSphereData sdata{}; @@ -5417,6 +5413,8 @@ static void sculpt_update_cache_invariants( if (ss->pbvh) { BKE_pbvh_show_orig_set(ss->pbvh, tool_settings->show_origco); } + + SCULPT_apply_dyntopo_settings(ss, sd, brush); } static float sculpt_brush_dynamic_size_get(Brush *brush, StrokeCache *cache, float initial_size) @@ -6552,22 +6550,22 @@ static void sculpt_stroke_update_step(bContext *C, cache->stroke_distance_t += stroke_delta; if (sd->flags & (SCULPT_DYNTOPO_DETAIL_CONSTANT | SCULPT_DYNTOPO_DETAIL_MANUAL)) { - float object_space_constant_detail = 1.0f / (sd->constant_detail * + float object_space_constant_detail = 1.0f / (ss->cached_dyntopo.constant_detail * mat4_to_scale(ob->object_to_world)); BKE_pbvh_bmesh_detail_size_set(ss->pbvh, object_space_constant_detail, 0.4f); } else if (sd->flags & SCULPT_DYNTOPO_DETAIL_BRUSH) { BKE_pbvh_bmesh_detail_size_set( - ss->pbvh, ss->cache->radius * sd->detail_percent / 100.0f, 0.4f); + ss->pbvh, ss->cache->radius * ss->cached_dyntopo.detail_percent / 100.0f, 0.4f); } else { BKE_pbvh_bmesh_detail_size_set(ss->pbvh, (ss->cache->radius / ss->cache->dyntopo_pixel_radius) * - (sd->detail_size * U.pixelsize) / 0.4f, + (ss->cached_dyntopo.detail_size * U.pixelsize) / 0.4f, 0.4f); } - float dyntopo_spacing = float(sd->dyntopo_spacing) / 50.0f; + float dyntopo_spacing = float(ss->cached_dyntopo.spacing) / 50.0f; bool do_dyntopo = SCULPT_stroke_is_dynamic_topology(ss, brush); do_dyntopo = do_dyntopo && diff --git a/source/blender/editors/sculpt_paint/sculpt_api.cc b/source/blender/editors/sculpt_paint/sculpt_api.cc index 3039e39c8bd..eb1b027cb80 100644 --- a/source/blender/editors/sculpt_paint/sculpt_api.cc +++ b/source/blender/editors/sculpt_paint/sculpt_api.cc @@ -650,3 +650,38 @@ void SCULPT_ensure_persistent_layers(SculptSession *ss, Object *ob) ob, ATTR_DOMAIN_POINT, CD_PROP_FLOAT, SCULPT_ATTRIBUTE_NAME(persistent_disp), ¶ms); } } + +void SCULPT_apply_dyntopo_settings(SculptSession *ss, Sculpt *sculpt, Brush *brush) +{ + DynTopoSettings *ds1 = &brush->dyntopo; + DynTopoSettings *ds2 = &sculpt->dyntopo; + + DynTopoSettings *ds_final = &ss->cached_dyntopo; + + ds_final->inherit = ds1->inherit; + ds_final->flag = 0; + + for (int i = 0; i < DYNTOPO_MAX_FLAGS; i++) { + if (ds_final->inherit & (1 << i)) { + ds_final->flag |= ds2->flag & (1 << i); + } + else { + ds_final->flag |= ds1->flag & (1 << i); + } + } + + ds_final->constant_detail = ds_final->inherit & DYNTOPO_INHERIT_CONSTANT_DETAIL ? + ds2->constant_detail : + ds1->constant_detail; + ds_final->detail_percent = ds_final->inherit & DYNTOPO_INHERIT_DETAIL_PERCENT ? + ds2->detail_percent : + ds1->detail_percent; + ds_final->detail_range = ds_final->inherit & DYNTOPO_INHERIT_DETAIL_RANGE ? ds2->detail_range : + ds1->detail_range; + ds_final->detail_size = ds_final->inherit & DYNTOPO_INHERIT_DETAIL_SIZE ? ds2->detail_size : + ds1->detail_size; + ds_final->mode = ds_final->inherit & DYNTOPO_INHERIT_MODE ? ds2->mode : ds1->mode; + ds_final->radius_scale = ds_final->inherit & DYNTOPO_INHERIT_RADIUS_SCALE ? ds2->radius_scale : + ds1->radius_scale; + ds_final->spacing = ds_final->inherit & DYNTOPO_INHERIT_SPACING ? ds2->spacing : ds1->spacing; +} diff --git a/source/blender/editors/sculpt_paint/sculpt_detail.cc b/source/blender/editors/sculpt_paint/sculpt_detail.cc index fff3cefeca5..26bf4cea6c6 100644 --- a/source/blender/editors/sculpt_paint/sculpt_detail.cc +++ b/source/blender/editors/sculpt_paint/sculpt_detail.cc @@ -319,8 +319,12 @@ static void sample_detail_dyntopo(bContext *C, ViewContext *vc, const int mval[2 srd.ss->stroke_id); if (srd.hit && srd.edge_length > 0.0f) { + DynTopoSettings *dyntopo = brush->dyntopo.inherit & DYNTOPO_INHERIT_CONSTANT_DETAIL ? + &brush->dyntopo : + &sd->dyntopo; + /* Convert edge length to world space detail resolution. */ - brush->dyntopo.constant_detail = 1.0f / (srd.edge_length * mat4_to_scale(ob->object_to_world)); + dyntopo->constant_detail = 1.0f / (srd.edge_length * mat4_to_scale(ob->object_to_world)); } } diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.hh b/source/blender/editors/sculpt_paint/sculpt_intern.hh index a88d9e31596..f43da770c12 100644 --- a/source/blender/editors/sculpt_paint/sculpt_intern.hh +++ b/source/blender/editors/sculpt_paint/sculpt_intern.hh @@ -2089,6 +2089,7 @@ void SCULPT_OT_detail_flood_fill(wmOperatorType *ot); void SCULPT_OT_sample_detail_size(wmOperatorType *ot); void SCULPT_OT_set_detail_size(wmOperatorType *ot); void SCULPT_OT_dyntopo_detail_size_edit(wmOperatorType *ot); +void SCULPT_apply_dyntopo_settings(SculptSession *ss, Sculpt *sculpt, Brush *brush); /** \} */ /* Dyntopo. */ diff --git a/source/blender/makesdna/DNA_brush_defaults.h b/source/blender/makesdna/DNA_brush_defaults.h index 97ac3eb283e..7fc5cd578fc 100644 --- a/source/blender/makesdna/DNA_brush_defaults.h +++ b/source/blender/makesdna/DNA_brush_defaults.h @@ -15,6 +15,19 @@ /** \name Brush Struct * \{ */ +#define _DNA_DEFAULT_DynTopoSettings \ +{\ + .detail_range = 0.4f,\ + .detail_percent = 25.0f,\ + .detail_size = 12.0f,\ + .constant_detail = 3.0f,\ + .flag = DYNTOPO_COLLAPSE|DYNTOPO_SUBDIVIDE,\ + .mode = DYNTOPO_DETAIL_RELATIVE,\ + .inherit = DYNTOPO_INHERIT_BITMASK,\ + .spacing = 35,\ + .radius_scale = 1.0f\ +} + #define _DNA_DEFAULT_Brush \ { \ .blend = 0, \ @@ -106,20 +119,9 @@ \ .mtex = _DNA_DEFAULT_MTex, \ .mask_mtex = _DNA_DEFAULT_MTex, \ - .dyntopo = {\ - .detail_range = 0.4f,\ - .detail_percent = 25.0f,\ - .detail_size = 12.0f,\ - .constant_detail = 3.0f,\ - .flag = DYNTOPO_COLLAPSE|DYNTOPO_SUBDIVIDE,\ - .mode = DYNTOPO_DETAIL_RELATIVE,\ - .inherit = DYNTOPO_INHERIT_ALL,\ - .spacing = 25,\ - .radius_scale = 1.0f\ - },\ + .dyntopo = _DNA_DEFAULT_DynTopoSettings,\ .concave_mask_factor = 0.75f\ } - /** \} */ /* clang-format on */ diff --git a/source/blender/makesdna/DNA_brush_enums.h b/source/blender/makesdna/DNA_brush_enums.h index 90e27d3dc1a..6eec7f41547 100644 --- a/source/blender/makesdna/DNA_brush_enums.h +++ b/source/blender/makesdna/DNA_brush_enums.h @@ -6,6 +6,8 @@ #pragma once +#include "BLI_utildefines.h" + #ifdef __cplusplus extern "C" { #endif @@ -645,18 +647,30 @@ typedef enum eBrushFalloffShape { // dyntopo flags // synced with PBVHTopologyUpdateMode -enum { +typedef enum eDynTopoFlags { DYNTOPO_SUBDIVIDE = 1 << 0, DYNTOPO_COLLAPSE = 1 << 1, DYNTOPO_DISABLED = 1 << 2, DYNTOPO_CLEANUP = 1 << 3, DYNTOPO_LOCAL_COLLAPSE = 1 << 4, - DYNTOPO_LOCAL_SUBDIVIDE = 1 << 5 -}; + DYNTOPO_LOCAL_SUBDIVIDE = 1 << 5, + DYNTOPO_MAX_FLAGS = 6, +} eDynTopoFlags; +ENUM_OPERATORS(eDynTopoFlags, DYNTOPO_LOCAL_SUBDIVIDE); + +/* Dyntopo inheritance flags. The flags up to DYNTOPO_MAX_FLAGS + * corruspond to eDynTopoFlags + */ +typedef enum eDynTopoInheritFlags { + /* Flags from eDynTopoFlags. */ + DYNTOPO_INHERIT_SUBDIVIDE = 1 << 0, + DYNTOPO_INHERIT_COLLAPSE = 1 << 1, + DYNTOPO_INHERIT_DISABLED = 1 << 2, + DYNTOPO_INHERIT_CLEANUP = 1 << 3, + DYNTOPO_INHERIT_LOCAL_COLLAPSE = 1 << 4, + DYNTOPO_INHERIT_LOCAL_SUBDIVIDE = 1 << 5, + /* End flags from eDynTopoFlags. */ -// dyntopo override flags, copies all flags from dyntopo flags -enum { - DYNTOPO_INHERIT_ALL = 1 << 10, DYNTOPO_INHERIT_DETAIL_RANGE = 1 << 11, DYNTOPO_INHERIT_DETAIL_PERCENT = 1 << 12, DYNTOPO_INHERIT_MODE = 1 << 13, @@ -664,8 +678,10 @@ enum { DYNTOPO_INHERIT_SPACING = 1 << 15, DYNTOPO_INHERIT_DETAIL_SIZE = 1 << 16, DYNTOPO_INHERIT_RADIUS_SCALE = 1 << 17, + DYNTOPO_INHERIT_MAX_FLAGS = 18, // make sure to update DYNTOPO_INHERIT_BITMASK when adding flags here -}; +} eDynTopoInheritFlags; +ENUM_OPERATORS(eDynTopoInheritFlags, DYNTOPO_INHERIT_RADIUS_SCALE); // represents all possible inherit flags #define DYNTOPO_INHERIT_BITMASK ((1 << 18) - 1) diff --git a/source/blender/makesdna/DNA_brush_types.h b/source/blender/makesdna/DNA_brush_types.h index 008d4220c27..f3b405f35b6 100644 --- a/source/blender/makesdna/DNA_brush_types.h +++ b/source/blender/makesdna/DNA_brush_types.h @@ -430,7 +430,7 @@ typedef struct Brush { char _pad2[4]; struct BrushGpencilSettings *gpencil_settings; - DynTopoSettings dyntopo, cached_dyntopo; + DynTopoSettings dyntopo; /* new brush engine stuff */ diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h index ffc776f4361..31a5e8889c4 100644 --- a/source/blender/makesdna/DNA_scene_types.h +++ b/source/blender/makesdna/DNA_scene_types.h @@ -19,6 +19,7 @@ #define USE_SETSCENE_CHECK #include "DNA_ID.h" +#include "DNA_brush_types.h" #include "DNA_color_types.h" /* color management */ #include "DNA_customdata_types.h" /* Scene's runtime custom-data masks. */ #include "DNA_layer_types.h" @@ -1077,8 +1078,8 @@ typedef struct Sculpt { int radial_symm[3]; /** Maximum edge length for dynamic topology sculpting (in pixels). */ - float detail_size; - float detail_range; + float detail_size DNA_DEPRECATED; + float detail_range DNA_DEPRECATED; /** Direction used for `SCULPT_OT_symmetrize` operator. */ int symmetrize_direction; @@ -1088,11 +1089,11 @@ typedef struct Sculpt { /* Scale for constant detail size. */ /** Constant detail resolution (Blender unit / constant_detail). */ - float constant_detail; - float detail_percent; - int dyntopo_spacing; + float constant_detail DNA_DEPRECATED; + float detail_percent DNA_DEPRECATED; + int dyntopo_spacing DNA_DEPRECATED; - float dyntopo_radius_scale; + float dyntopo_radius_scale DNA_DEPRECATED; int automasking_cavity_blur_steps; float automasking_cavity_factor; char _pad[4]; @@ -1104,6 +1105,8 @@ typedef struct Sculpt { /** For use by operators. */ struct CurveMapping *automasking_cavity_curve_op; struct Object *gravity_object; + + DynTopoSettings dyntopo; } Sculpt; typedef struct CurvesSculpt { @@ -2493,27 +2496,24 @@ typedef enum eSculptFlags { SCULPT_DYNTOPO_SMOOTH_SHADING = (1 << 10), /** If set, dynamic-topology brushes will subdivide short edges. */ - SCULPT_DYNTOPO_SUBDIVIDE = (1 << 12), + SCULPT_DYNTOPO_SUBDIVIDE = (1 << 12), /* deprecated. */ /** If set, dynamic-topology brushes will collapse short edges. */ - SCULPT_DYNTOPO_COLLAPSE = (1 << 11), + SCULPT_DYNTOPO_COLLAPSE = (1 << 11), /* deprecated. */ /** If set, dynamic-topology detail size will be constant in object space. */ - SCULPT_DYNTOPO_DETAIL_CONSTANT = (1 << 13), - SCULPT_DYNTOPO_DETAIL_BRUSH = (1 << 14), + SCULPT_DYNTOPO_DETAIL_CONSTANT = (1 << 13), /* deprecated. */ + SCULPT_DYNTOPO_DETAIL_BRUSH = (1 << 14), /* deprecated. */ /* Don't display mask in viewport, but still use it for strokes. */ SCULPT_HIDE_MASK = (1 << 15), - SCULPT_DYNTOPO_DETAIL_MANUAL = (1 << 16), + SCULPT_DYNTOPO_DETAIL_MANUAL = (1 << 16), /* deprecated. */ /* Don't display face sets in viewport. */ SCULPT_HIDE_FACE_SETS = (1 << 17), SCULPT_DYNTOPO_FLAT_VCOL_SHADING = (1 << 18), - SCULPT_DYNTOPO_CLEANUP = (1 << 19), - // hides facesets/masks and forces indexed mode to save GPU bandwidth + /* Hides facesets/masks and forces indexed mode to save GPU bandwidth. */ SCULPT_FAST_DRAW = (1 << 20), - SCULPT_DYNTOPO_LOCAL_SUBDIVIDE = (1 << 21), - SCULPT_DYNTOPO_LOCAL_COLLAPSE = (1 << 22), - SCULPT_DYNTOPO_ENABLED = (1 << 23), + SCULPT_DYNTOPO_ENABLED = (1 << 21), } eSculptFlags; /** #Sculpt.transform_mode */ diff --git a/source/blender/makesdna/intern/dna_defaults.c b/source/blender/makesdna/intern/dna_defaults.c index f4f195310ad..7c092d2f005 100644 --- a/source/blender/makesdna/intern/dna_defaults.c +++ b/source/blender/makesdna/intern/dna_defaults.c @@ -146,6 +146,7 @@ SDNA_DEFAULT_DECL_STRUCT(AssetLibraryReference); SDNA_DEFAULT_DECL_STRUCT(bArmature); /* DNA_brush_defaults.h */ +SDNA_DEFAULT_DECL_STRUCT(DynTopoSettings); SDNA_DEFAULT_DECL_STRUCT(Brush); /* DNA_cachefile_defaults.h */ @@ -357,6 +358,8 @@ const void *DNA_default_table[SDNA_TYPE_MAX] = { /* DNA_brush_defaults.h */ SDNA_DEFAULT_DECL(Brush), + SDNA_DEFAULT_DECL(DynTopoSettings), + SDNA_DEFAULT_DECL_EX(DynTopoSettings, Brush.dyntopo), /* DNA_cachefile_defaults.h */ SDNA_DEFAULT_DECL(CacheFile), diff --git a/source/blender/makesrna/intern/rna_brush.c b/source/blender/makesrna/intern/rna_brush.c index ddef2b6c852..ef2bc13a2ab 100644 --- a/source/blender/makesrna/intern/rna_brush.c +++ b/source/blender/makesrna/intern/rna_brush.c @@ -392,7 +392,6 @@ static EnumPropertyItem rna_enum_brush_dyntopo_inherit[] = { {DYNTOPO_DISABLED, "DISABLED", ICON_NONE, "Disable", ""}, {DYNTOPO_LOCAL_COLLAPSE, "LOCAL_COLLAPSE", ICON_NONE, "Local Collapse", ""}, {DYNTOPO_LOCAL_SUBDIVIDE, "LOCAL_SUBDIVIDE", ICON_NONE, "Local Subdivide", ""}, - {DYNTOPO_INHERIT_ALL, "ALL", ICON_NONE, "All", "Inherit All"}, {DYNTOPO_INHERIT_DETAIL_RANGE, "DETAIL_RANGE", ICON_NONE, "All", ""}, {DYNTOPO_INHERIT_DETAIL_PERCENT, "DETAIL_PERCENT", ICON_NONE, "Percent", ""}, {DYNTOPO_INHERIT_MODE, "MODE", ICON_NONE, "Mode", ""}, diff --git a/source/blender/makesrna/intern/rna_sculpt_paint.c b/source/blender/makesrna/intern/rna_sculpt_paint.c index 205a94eee20..a428f4ea006 100644 --- a/source/blender/makesrna/intern/rna_sculpt_paint.c +++ b/source/blender/makesrna/intern/rna_sculpt_paint.c @@ -880,6 +880,10 @@ static void rna_def_sculpt(BlenderRNA *brna) RNA_def_property_ui_text(prop, "DynTopo", "Enable DynTopo remesher in dynamic topology mode."); RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE); + prop = RNA_def_property(srna, "dyntopo", PROP_POINTER, PROP_NONE); + RNA_def_property_struct_type(prop, "DynTopoSettings"); + RNA_def_property_pointer_sdna(prop, NULL, "dyntopo"); + prop = RNA_def_property(srna, "detail_size", PROP_FLOAT, PROP_PIXEL); RNA_def_property_ui_range(prop, 0.5, 40.0, 0.1, 2); RNA_def_property_ui_scale_type(prop, PROP_SCALE_CUBIC); @@ -940,10 +944,6 @@ static void rna_def_sculpt(BlenderRNA *brna) RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE); RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_Sculpt_update"); - prop = RNA_def_property(srna, "use_dyntopo_cleanup", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flags", SCULPT_DYNTOPO_CLEANUP); - RNA_def_property_ui_text(prop, "Cleanup", "Removes verts surrounded by only 3 or 4 edges"); - prop = RNA_def_property(srna, "use_flat_vcol_shading", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flags", SCULPT_DYNTOPO_FLAT_VCOL_SHADING); RNA_def_property_ui_text( -- 2.30.2 From aabc29b24957140cd4518610f6199e5b972a6023 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Tue, 28 Mar 2023 03:28:20 -0700 Subject: [PATCH 046/279] temp-sculpt-dyntopo: Fix compile error and minor versioning bug --- source/blender/blenkernel/intern/brush.cc | 3 +-- source/blender/blenkernel/intern/paint.cc | 7 +++---- source/blender/blenloader/intern/versioning_300.cc | 2 ++ 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/source/blender/blenkernel/intern/brush.cc b/source/blender/blenkernel/intern/brush.cc index a00e21ed478..334a7a8a897 100644 --- a/source/blender/blenkernel/intern/brush.cc +++ b/source/blender/blenkernel/intern/brush.cc @@ -1817,8 +1817,7 @@ void BKE_brush_sculpt_reset(Brush *br) br->alpha = 1.0f; br->rake_factor = 1.0f; br->dyntopo.inherit = ~(DYNTOPO_INHERIT_SPACING | DYNTOPO_INHERIT_SUBDIVIDE | - DYNTOPO_INHERIT_COLLAPSE | DYNTOPO_INHERIT_DETAIL_RANGE | - DYNTOPO_INHERIT_DETAIL_RANGE); + DYNTOPO_INHERIT_COLLAPSE | DYNTOPO_INHERIT_DETAIL_RANGE); br->dyntopo.flag |= DYNTOPO_SUBDIVIDE | DYNTOPO_COLLAPSE; br->dyntopo.detail_range = 0.4f; br->dyntopo.spacing = 5; diff --git a/source/blender/blenkernel/intern/paint.cc b/source/blender/blenkernel/intern/paint.cc index 275de36e235..62d75fbcbe6 100644 --- a/source/blender/blenkernel/intern/paint.cc +++ b/source/blender/blenkernel/intern/paint.cc @@ -1121,8 +1121,7 @@ bool BKE_paint_ensure(ToolSettings *ts, Paint **r_paint) paint->symmetry_flags |= PAINT_SYMM_X; /* Make sure at least dyntopo subdivision is enabled */ - data->flags |= SCULPT_DYNTOPO_SUBDIVIDE | SCULPT_DYNTOPO_COLLAPSE | SCULPT_DYNTOPO_CLEANUP | - SCULPT_DYNTOPO_ENABLED; + data->flags |= SCULPT_DYNTOPO_SUBDIVIDE | SCULPT_DYNTOPO_COLLAPSE | SCULPT_DYNTOPO_ENABLED; } else if ((GpPaint **)r_paint == &ts->gp_paint) { GpPaint *data = MEM_cnew(__func__); @@ -2448,9 +2447,9 @@ void BKE_sculpt_toolsettings_data_ensure(Scene *scene) sd->dyntopo_radius_scale = 1.0f; } - // we check these flags here in case versioning code fails + /* We check these flags here in case versioning code fails. */ if (!sd->detail_range || !sd->dyntopo_spacing) { - sd->flags |= SCULPT_DYNTOPO_CLEANUP | SCULPT_DYNTOPO_ENABLED; + sd->flags |= SCULPT_DYNTOPO_ENABLED; } if (!sd->detail_range) { diff --git a/source/blender/blenloader/intern/versioning_300.cc b/source/blender/blenloader/intern/versioning_300.cc index 6931669ff77..53da40639b2 100644 --- a/source/blender/blenloader/intern/versioning_300.cc +++ b/source/blender/blenloader/intern/versioning_300.cc @@ -4172,6 +4172,8 @@ void blo_do_versions_300(FileData *fd, Library * /*lib*/, Main *bmain) ds->detail_range = sculpt->detail_range; ds->constant_detail = sculpt->constant_detail; + sculpt->flags |= SCULPT_DYNTOPO_ENABLED; + ds->flag = 0; if (sculpt->flags & SCULPT_DYNTOPO_SUBDIVIDE) { ds->flag |= DYNTOPO_SUBDIVIDE; -- 2.30.2 From ae79a9fe9f8ad12b169529dffe2a8eeca4df4bda Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Tue, 28 Mar 2023 04:52:55 -0700 Subject: [PATCH 047/279] temp-sculpt-dyntopo: Cleanup some warnings --- source/blender/bmesh/intern/bmesh_core.c | 2 +- source/blender/bmesh/intern/bmesh_interp.c | 2 +- source/blender/bmesh/intern/bmesh_log.cc | 38 +++++++++++----------- 3 files changed, 21 insertions(+), 21 deletions(-) diff --git a/source/blender/bmesh/intern/bmesh_core.c b/source/blender/bmesh/intern/bmesh_core.c index ff8b1b0703a..fc2a5a9884e 100644 --- a/source/blender/bmesh/intern/bmesh_core.c +++ b/source/blender/bmesh/intern/bmesh_core.c @@ -1991,7 +1991,7 @@ static char *obj_append_line(const char *line, char *str, char *fixed, int *size } static char *bm_save_local_obj_text( - BMesh *bm, int depth, char buf[LOCAL_OBJ_SIZE], const char *fmt, ...) + BMesh *UNUSED(bm), int depth, char buf[LOCAL_OBJ_SIZE], const char *fmt, ...) { va_list vl; va_start(vl, fmt); diff --git a/source/blender/bmesh/intern/bmesh_interp.c b/source/blender/bmesh/intern/bmesh_interp.c index 787ad72dc6a..426cefc1aef 100644 --- a/source/blender/bmesh/intern/bmesh_interp.c +++ b/source/blender/bmesh/intern/bmesh_interp.c @@ -26,7 +26,7 @@ #include "bmesh.h" #include "intern/bmesh_private.h" -static void copy_cdata_simple(BMesh *bm, +static void copy_cdata_simple(BMesh *UNUSED(bm), CustomData *data_layer, BMElem *ele_dst, const BMElem *ele_src) diff --git a/source/blender/bmesh/intern/bmesh_log.cc b/source/blender/bmesh/intern/bmesh_log.cc index 2b623dd8479..e79af982482 100644 --- a/source/blender/bmesh/intern/bmesh_log.cc +++ b/source/blender/bmesh/intern/bmesh_log.cc @@ -189,10 +189,10 @@ struct BMLogSetBase { { } - virtual void undo(BMesh *bm, BMLogCallbacks *callbacks) + virtual void undo(BMesh */*bm*/, BMLogCallbacks */*callbacks*/) { } - virtual void redo(BMesh *bm, BMLogCallbacks *callbacks) + virtual void redo(BMesh */*bm*/, BMLogCallbacks */*callbacks*/) { } }; @@ -419,7 +419,7 @@ struct BMLogEntry { CustomData_free(&pdata, 0); } - template T *get_elem_from_id(BMesh *bm, BMID id) + template T *get_elem_from_id(BMesh */*bm*/, BMID id) { T *elem = reinterpret_cast(BM_idmap_lookup(idmap, id.id)); char htype = 0; @@ -450,7 +450,7 @@ struct BMLogEntry { return elem; } - template void assign_elem_id(BMesh *bm, T *elem, BMID _id, bool check_unique) + template void assign_elem_id(BMesh */*bm*/, T *elem, BMID _id, bool check_unique) { int id = _id.id; @@ -476,7 +476,7 @@ struct BMLogEntry { BM_idmap_assign(idmap, reinterpret_cast(elem), id); } - template BMID get_elem_id(BMesh *bm, T *elem) + template BMID get_elem_id(BMesh */*bm*/, T *elem) { BM_idmap_check_assign(idmap, reinterpret_cast(elem)); return BM_idmap_get_id(idmap, reinterpret_cast(elem)); @@ -514,7 +514,7 @@ struct BMLogEntry { lv->flag = v->head.hflag; } - void swap_logvert(BMesh *bm, BMID id, BMVert *v, BMLogVert *lv) + void swap_logvert(BMesh *bm, BMID /*id*/, BMVert *v, BMLogVert *lv) { if (v->head.data && lv->customdata) { CustomData_bmesh_swap_data(&vdata, &bm->vdata, lv->customdata, &v->head.data); @@ -525,14 +525,14 @@ struct BMLogEntry { swap_v3_v3(v->no, lv->no); } - void swap_logedge(BMesh *bm, BMID id, BMEdge *e, BMLogEdge *le) + void swap_logedge(BMesh *bm, BMID /*id*/, BMEdge *e, BMLogEdge *le) { if (e->head.data && le->customdata) { CustomData_bmesh_swap_data(&edata, &bm->edata, le->customdata, &e->head.data); } } - void swap_logface(BMesh *bm, BMID id, BMFace *f, BMLogFace *lf) + void swap_logface(BMesh *bm, BMID /*id*/, BMFace *f, BMLogFace *lf) { if (f->head.data && lf->customdata) { CustomData_bmesh_swap_data(&pdata, &bm->pdata, lf->customdata, &f->head.data); @@ -608,7 +608,7 @@ struct BMLogEntry { CustomData_bmesh_copy_data(&bm->edata, &edata, e->head.data, &le->customdata); } - void free_logedge(BMesh *bm, BMLogEdge *e) + void free_logedge(BMesh */*bm*/, BMLogEdge *e) { epool.free(e); } @@ -659,7 +659,7 @@ struct BMLogEntry { } while ((l = l->next) != f->l_first); } - void free_logface(BMesh *bm, BMLogFace *lf) + void free_logface(BMesh */*bm*/, BMLogFace *lf) { if (lf->loop_customdata[0]) { for (int i = 0; i < lf->verts.size(); i++) { @@ -1029,7 +1029,7 @@ void BMLogSetDiff::remove_edge(BMesh *bm, BMEdge *e) removed_edges.add(id, le); } -void BMLogSetDiff::modify_edge(BMesh *bm, BMEdge *e) +void BMLogSetDiff::modify_edge(BMesh */*bm*/, BMEdge */*e*/) { } @@ -1423,7 +1423,7 @@ BMLog *BM_log_from_existing_entries_create(BMesh *bm, BMIdMap *idmap, BMLogEntry return log; } -BMLog *BM_log_create(BMesh *bm, BMIdMap *idmap) +BMLog *BM_log_create(BMesh */*bm*/, BMIdMap *idmap) { BMLog *log = MEM_new("BMLog", idmap); @@ -1564,22 +1564,22 @@ void BM_log_full_mesh(BMesh *bm, BMLog *log) log->full_mesh(bm); } -BMVert *BM_log_id_vert_get(BMesh *bm, BMLog *log, uint id) +BMVert *BM_log_id_vert_get(BMesh */*bm*/, BMLog *log, uint id) { return reinterpret_cast(BM_idmap_lookup(log->idmap, id)); } -uint BM_log_vert_id_get(BMesh *bm, BMLog *log, BMVert *v) +uint BM_log_vert_id_get(BMesh */*bm*/, BMLog *log, BMVert *v) { return BM_idmap_get_id(log->idmap, reinterpret_cast(v)); } -BMFace *BM_log_id_face_get(BMesh *bm, BMLog *log, uint id) +BMFace *BM_log_id_face_get(BMesh */*bm*/, BMLog *log, uint id) { return reinterpret_cast(BM_idmap_lookup(log->idmap, id)); } -uint BM_log_face_id_get(BMesh *bm, BMLog *log, BMFace *f) +uint BM_log_face_id_get(BMesh */*bm*/, BMLog *log, BMFace *f) { return BM_idmap_get_id(log->idmap, reinterpret_cast(f)); } @@ -1599,12 +1599,12 @@ void BM_log_redo(BMesh *bm, BMLog *log, BMLogCallbacks *callbacks) log->redo(bm, callbacks); } -void BM_log_undo_skip(BMesh *bm, BMLog *log) +void BM_log_undo_skip(BMesh */*bm*/, BMLog *log) { log->skip(-1); } -void BM_log_redo_skip(BMesh *bm, BMLog *log) +void BM_log_redo_skip(BMesh */*bm*/, BMLog *log) { log->skip(1); } @@ -1653,7 +1653,7 @@ bool BM_log_entry_drop(BMLogEntry *entry) return true; } -void BM_log_print_entry(BMLog *log, BMLogEntry *entry) +void BM_log_print_entry(BMLog */*log*/, BMLogEntry *entry) { printf("entry: %p", entry); } -- 2.30.2 From 8bbbba537fac565a57b38a288a6c75bd46f030c8 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Tue, 28 Mar 2023 05:07:12 -0700 Subject: [PATCH 048/279] temp-sculpt-dyntopo: Clean up some more warnings --- source/blender/bmesh/intern/bmesh_core.c | 64 +++++++------------ source/blender/bmesh/intern/bmesh_interp.c | 2 +- source/blender/bmesh/intern/bmesh_mesh.cc | 16 ++--- source/blender/bmesh/intern/bmesh_operators.c | 8 +-- 4 files changed, 36 insertions(+), 54 deletions(-) diff --git a/source/blender/bmesh/intern/bmesh_core.c b/source/blender/bmesh/intern/bmesh_core.c index fc2a5a9884e..9c32add671b 100644 --- a/source/blender/bmesh/intern/bmesh_core.c +++ b/source/blender/bmesh/intern/bmesh_core.c @@ -54,7 +54,7 @@ void bm_elem_check_toolflags(BMesh *bm, BMElem *elem) { int cd_off = -1; MToolFlags *flags; - BLI_mempool *flagpool; + BLI_mempool *flagpool = NULL; switch (elem->head.htype) { case BM_VERT: @@ -1747,7 +1747,7 @@ BMEdge *bmesh_kernel_join_edge_kill_vert(BMesh *bm, return NULL; } - BMEdge *e_splice; + BMEdge *e_splice = NULL; BLI_SMALLSTACK_DECLARE(faces_degenerate, BMFace *); BMLoop *l_kill_next; @@ -1877,22 +1877,9 @@ BMEdge *bmesh_kernel_join_edge_kill_vert(BMesh *bm, return NULL; } -static void check_vert_faces(BMVert *v_target) -{ - BMEdge *e = v_target->e; - if (e) { - do { - BM_CHECK_ELEMENT(e); - if (e->l) { - BMLoop *l = e->l; +//#define JVKE_DEBUG - do { - BM_CHECK_ELEMENT(l->f); - } while ((l = l->radial_next) != e->l); - } - } while ((e = BM_DISK_EDGE_NEXT(e, v_target)) != v_target->e); - } -} +#ifdef JVKE_DEBUG #ifdef _ # undef _ @@ -1964,7 +1951,7 @@ static void bm_local_obj_free(char *str, char *fixed) } } -#define LOCAL_OBJ_SIZE 512 +# define LOCAL_OBJ_SIZE 512 static char *obj_append_line(const char *line, char *str, char *fixed, int *size, int *i) { @@ -2298,24 +2285,6 @@ static char *bm_save_local_obj_text( return str; } -/** - * \brief Join Vert Kill Edge (JVKE) - * - * Collapse an edge, merging surrounding data. - * - * Unlike #BM_vert_collapse_edge & #bmesh_kernel_join_edge_kill_vert - * which only handle 2 valence verts, - * this can handle any number of connected edges/faces. - * - *
- * Before: -> After:
- * +-+-+-+    +-+-+-+
- * | | | |    | \ / |
- * +-+-+-+    +--+--+
- * | | | |    | / \ |
- * +-+-+-+    +-+-+-+
- * 
- */ static void trigger_jvke_error(int err, char *obj_text) { @@ -2324,9 +2293,6 @@ static void trigger_jvke_error(int err, char *obj_text) char *_last_local_obj = NULL; -//#define JVKE_DEBUG - -#ifdef JVKE_DEBUG # define JVKE_CHECK_ELEMENT(elem) \ { \ int err = 0; \ @@ -2358,8 +2324,6 @@ static bool cleanup_vert(BMesh *bm, BMVert *v, const BMTracer *tracer) f_example = l->f; - BMLoop *lnext; - do { if (tracer) { tracer->on_face_kill(bm, l->f, tracer->userdata); @@ -2440,6 +2404,24 @@ static void bmesh_kernel_check_val3_vert(BMesh *bm, BMEdge *e, const BMTracer *t bm_logstack_pop(); } +/** + * \brief Join Vert Kill Edge (JVKE) + * + * Collapse an edge, merging surrounding data. + * + * Unlike #BM_vert_collapse_edge & #bmesh_kernel_join_edge_kill_vert + * which only handle 2 valence verts, + * this can handle any number of connected edges/faces. + * + *
+ * Before: -> After:
+ * +-+-+-+    +-+-+-+
+ * | | | |    | \ / |
+ * +-+-+-+    +--+--+
+ * | | | |    | / \ |
+ * +-+-+-+    +-+-+-+
+ * 
+ */ BMVert *bmesh_kernel_join_vert_kill_edge(BMesh *bm, BMEdge *e, BMVert *v_kill, diff --git a/source/blender/bmesh/intern/bmesh_interp.c b/source/blender/bmesh/intern/bmesh_interp.c index 426cefc1aef..0cb2ae68886 100644 --- a/source/blender/bmesh/intern/bmesh_interp.c +++ b/source/blender/bmesh/intern/bmesh_interp.c @@ -33,7 +33,7 @@ static void copy_cdata_simple(BMesh *UNUSED(bm), { int cd_tflags; MToolFlags saved_tflags = {0}; - + ; if ((cd_tflags = CustomData_get_offset(data_layer, CD_TOOLFLAGS)) != -1) { saved_tflags = *(MToolFlags *)BM_ELEM_CD_GET_VOID_P(ele_dst, cd_tflags); } diff --git a/source/blender/bmesh/intern/bmesh_mesh.cc b/source/blender/bmesh/intern/bmesh_mesh.cc index 8a287e0e593..8265c52215a 100644 --- a/source/blender/bmesh/intern/bmesh_mesh.cc +++ b/source/blender/bmesh/intern/bmesh_mesh.cc @@ -136,7 +136,7 @@ void BM_mesh_elem_toolflags_clear(BMesh *bm) // int cdmap[8] = {0, 1, -1, -1, 2, -1, -1, -1, 3}; -static void bm_swap_cd_data(int htype, BMesh *bm, CustomData *cd, void *a, void *b) +static void bm_swap_cd_data(int /*htype*/, BMesh * /*bm*/, CustomData *cd, void *a, void *b) { int tot = cd->totsize; // int cd_id = bm->idmap.cd_id_off[htype]; @@ -1742,7 +1742,7 @@ void BM_mesh_vert_coords_apply_with_mat4(BMesh *bm, void bm_swap_ids(BMesh *bm, BMElem *e1, BMElem *e2) { - int cd_id = bm->idmap.cd_id_off[e1->head.htype]; + int cd_id = bm->idmap.cd_id_off[int(e1->head.htype)]; if (cd_id < 0) { return; @@ -2115,23 +2115,23 @@ bool BM_defragment_vertex(BMesh *bm, return true; } -static void on_vert_kill(BMesh *bm, BMVert *v, void *userdata) +static void on_vert_kill(BMesh * /*bm*/, BMVert * /*v*/, void * /*userdata*/) { } -static void on_edge_kill(BMesh *bm, BMEdge *e, void *userdata) +static void on_edge_kill(BMesh * /*bm*/, BMEdge * /*e*/, void * /*userdata*/) { } -static void on_face_kill(BMesh *bm, BMFace *f, void *userdata) +static void on_face_kill(BMesh * /*bm*/, BMFace * /*f*/, void * /*userdata*/) { } -static void on_vert_create(BMesh *bm, BMVert *v, void *userdata) +static void on_vert_create(BMesh * /*bm*/, BMVert * /*v*/, void * /*userdata*/) { } -static void on_edge_create(BMesh *bm, BMEdge *v, void *userdata) +static void on_edge_create(BMesh * /*bm*/, BMEdge * /*v*/, void * /*userdata*/) { } -static void on_face_create(BMesh *bm, BMFace *v, void *userdata) +static void on_face_create(BMesh * /*bm*/, BMFace * /*v*/, void * /*userdata*/) { } diff --git a/source/blender/bmesh/intern/bmesh_operators.c b/source/blender/bmesh/intern/bmesh_operators.c index c2479f940d4..4cea19ef7d2 100644 --- a/source/blender/bmesh/intern/bmesh_operators.c +++ b/source/blender/bmesh/intern/bmesh_operators.c @@ -1188,7 +1188,7 @@ static void bmo_flag_layer_do(BMesh *bm, static void bmo_flag_layer_alloc_do(BMesh *bm, int cd_tflags, int itertype, - int htype, + int UNUSED(htype), int totelem, int new_totflags, BLI_mempool **pool_ptr) @@ -1246,10 +1246,10 @@ static void bmo_flag_layer_free(BMesh *bm) static void bmo_flag_layer_clear_do(BMesh *bm, int cd_tflags, int itertype, - int htype, - int totelem, + int UNUSED(htype), + int UNUSED(totelem), int totflag, - BLI_mempool **pool_ptr) + BLI_mempool **UNUSED(pool_ptr)) { BMIter iter; BMElem *elem; -- 2.30.2 From 759c5072e07c019c90d19badbc309a871fdc01e8 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Tue, 28 Mar 2023 06:39:29 -0700 Subject: [PATCH 049/279] temp-sculpt-dyntopo: Fix bug where original coords not initialized --- source/blender/blenkernel/intern/paint.cc | 7 ++++++- source/blender/blenkernel/intern/pbvh.cc | 1 + source/blender/editors/sculpt_paint/sculpt.cc | 2 ++ 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/source/blender/blenkernel/intern/paint.cc b/source/blender/blenkernel/intern/paint.cc index 62d75fbcbe6..931d3d69877 100644 --- a/source/blender/blenkernel/intern/paint.cc +++ b/source/blender/blenkernel/intern/paint.cc @@ -2749,7 +2749,12 @@ static PBVH *build_pbvh_from_ccg(Object *ob, SubdivCCG *subdiv_ccg, bool respect BKE_pbvh_set_pmap(pbvh, ss->pmap); - BKE_sculptsession_check_sculptverts(ob, pbvh, BKE_pbvh_get_grid_num_verts(pbvh)); + int totvert = BKE_pbvh_get_grid_num_verts(pbvh); + BKE_sculptsession_check_sculptverts(ob, pbvh, totvert); + + for (int i = 0; i < totvert; i++) { + ss->msculptverts[i].stroke_id = -1; + } #ifdef WITH_PBVH_CACHE BKE_pbvh_set_cached(ob, pbvh); diff --git a/source/blender/blenkernel/intern/pbvh.cc b/source/blender/blenkernel/intern/pbvh.cc index a83f13b78bc..dfe7d12b9b4 100644 --- a/source/blender/blenkernel/intern/pbvh.cc +++ b/source/blender/blenkernel/intern/pbvh.cc @@ -1025,6 +1025,7 @@ void BKE_pbvh_build_mesh(PBVH *pbvh, for (int i = 0; i < mesh->totvert; i++) { msculptverts[i].flag &= ~SCULPTVERT_NEED_VALENCE; msculptverts[i].valence = pmap->pmap[i].count; + msculptverts[i].stroke_id = -1; } for (int i = 0; i < looptri_num; i++) { diff --git a/source/blender/editors/sculpt_paint/sculpt.cc b/source/blender/editors/sculpt_paint/sculpt.cc index 720e53ee9b9..cb3b1d0e07f 100644 --- a/source/blender/editors/sculpt_paint/sculpt.cc +++ b/source/blender/editors/sculpt_paint/sculpt.cc @@ -5415,6 +5415,8 @@ static void sculpt_update_cache_invariants( } SCULPT_apply_dyntopo_settings(ss, sd, brush); + + BKE_pbvh_update_bounds(ss->pbvh, PBVH_UpdateBB | PBVH_UpdateOriginalBB); } static float sculpt_brush_dynamic_size_get(Brush *brush, StrokeCache *cache, float initial_size) -- 2.30.2 From b959acf9f7c094e94251257a677717b7612b6318 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Tue, 28 Mar 2023 08:00:56 -0700 Subject: [PATCH 050/279] temp-sculpt-dyntopo: Fix editmode seperate operator --- source/blender/bmesh/intern/bmesh_construct.c | 26 +++++++++---------- source/blender/editors/mesh/editmesh_tools.cc | 3 ++- 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/source/blender/bmesh/intern/bmesh_construct.c b/source/blender/bmesh/intern/bmesh_construct.c index 113f0a0ab04..0a64a7e34ca 100644 --- a/source/blender/bmesh/intern/bmesh_construct.c +++ b/source/blender/bmesh/intern/bmesh_construct.c @@ -657,20 +657,6 @@ void BM_mesh_copy_init_customdata(BMesh *bm_dst, BMesh *bm_src, const BMAllocTem allocsize = &bm_mesh_allocsize_default; } - // forcibly copy mesh_id layers - CustomData *srcdatas[4] = {&bm_src->vdata, &bm_src->edata, &bm_src->ldata, &bm_src->pdata}; - // CustomData *dstdatas[4] = {&bm_dst->vdata, &bm_dst->edata, &bm_dst->ldata, &bm_dst->pdata}; - - for (int i = 0; i < 4; i++) { - CustomData *cdata = srcdatas[i]; - - if (CustomData_has_layer(cdata, CD_MESH_ID)) { - int idx = CustomData_get_layer_index(cdata, CD_MESH_ID); - - cdata->layers[idx].flag &= ~(CD_FLAG_TEMPORARY | CD_FLAG_ELEM_NOCOPY); - } - } - CustomData_copy( &bm_src->vdata, &bm_dst->vdata, CD_MASK_BMESH.vmask | CD_MASK_MESH_ID, CD_SET_DEFAULT, 0); CustomData_copy( @@ -680,6 +666,18 @@ void BM_mesh_copy_init_customdata(BMesh *bm_dst, BMesh *bm_src, const BMAllocTem CustomData_copy( &bm_src->pdata, &bm_dst->pdata, CD_MASK_BMESH.pmask | CD_MASK_MESH_ID, CD_SET_DEFAULT, 0); + CustomData *srcdatas[4] = {&bm_src->vdata, &bm_src->edata, &bm_src->ldata, &bm_src->pdata}; + CustomData *dstdatas[4] = {&bm_dst->vdata, &bm_dst->edata, &bm_dst->ldata, &bm_dst->pdata}; + + for (int i = 0; i < 4; i++) { + CustomData *srcdata = srcdatas[i]; + CustomData *dstdata = dstdatas[i]; + + if (CustomData_has_layer(srcdata, CD_TOOLFLAGS)) { + CustomData_add_layer(dstdata, CD_TOOLFLAGS, CD_ASSIGN, 0); + } + } + CustomData_bmesh_init_pool(&bm_dst->vdata, allocsize->totvert, BM_VERT); CustomData_bmesh_init_pool(&bm_dst->edata, allocsize->totedge, BM_EDGE); CustomData_bmesh_init_pool(&bm_dst->ldata, allocsize->totloop, BM_LOOP); diff --git a/source/blender/editors/mesh/editmesh_tools.cc b/source/blender/editors/mesh/editmesh_tools.cc index 96bb86f7119..2b5a3cbf684 100644 --- a/source/blender/editors/mesh/editmesh_tools.cc +++ b/source/blender/editors/mesh/editmesh_tools.cc @@ -4403,9 +4403,10 @@ static Base *mesh_separate_tagged( create_params.use_toolflags = true; BMesh *bm_new = BM_mesh_create(&bm_mesh_allocsize_default, &create_params); - BM_mesh_elem_toolflags_ensure(bm_new); /* Needed for 'duplicate' BMO. */ BM_mesh_copy_init_customdata(bm_new, bm_old, &bm_mesh_allocsize_default); + BM_mesh_elem_toolflags_ensure(bm_new); /* Needed for 'duplicate' BMO. */ + /* Take into account user preferences for duplicating actions. */ const eDupli_ID_Flags dupflag = eDupli_ID_Flags(USER_DUP_MESH | (U.dupflag & USER_DUP_ACT)); Base *base_new = ED_object_add_duplicate(bmain, scene, view_layer, base_old, dupflag); -- 2.30.2 From 66b2f57e5e5b739f9ef0d903561357093d0e0c7c Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Thu, 30 Mar 2023 23:28:11 -0700 Subject: [PATCH 051/279] temp-sculpt-dyntopo: Fix merge error --- source/blender/blenkernel/intern/customdata.cc | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/source/blender/blenkernel/intern/customdata.cc b/source/blender/blenkernel/intern/customdata.cc index 696d9c99e64..be6c00c9436 100644 --- a/source/blender/blenkernel/intern/customdata.cc +++ b/source/blender/blenkernel/intern/customdata.cc @@ -4459,11 +4459,11 @@ void CustomData_bmesh_swap_data(CustomData *source, } } -void CustomData_bmesh_copy_data_exclude_by_type(const CustomData *source, - CustomData *dest, - void *src_block, - void **dest_block, - const eCustomDataMask mask_exclude) +ATTR_NO_OPT void CustomData_bmesh_copy_data_exclude_by_type(const CustomData *source, + CustomData *dest, + void *src_block, + void **dest_block, + const eCustomDataMask mask_exclude) { /* Note that having a version of this function without a 'mask_exclude' * would cause too much duplicate code, so add a check instead. */ @@ -4499,7 +4499,9 @@ void CustomData_bmesh_copy_data_exclude_by_type(const CustomData *source, * (this should work because layers are ordered by type) */ while (dest_i < dest->totlayer && dest->layers[dest_i].type < source->layers[src_i].type) { - CustomData_bmesh_set_default_n(dest, dest_block, dest_i); + if (was_new) { + CustomData_bmesh_set_default_n(dest, dest_block, dest_i); + } dest_i++; } -- 2.30.2 From f4e9cf982df864138ca110c1ef1b93971ce264ae Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Sun, 2 Apr 2023 14:15:35 -0700 Subject: [PATCH 052/279] temp-sculpt-dyntopo: Code cleanups * TableGSet now uses blender::Map instead of SmallHash. * Removed a few duplicate CustomData API methods. * Remove last vestiges of the old mesh element ID system. Note that old files from sculpt-dev with CD_MESH_ID layers (which is now CD_DYNTOPO_VERT) are detected and patched. --- source/blender/blenkernel/BKE_attribute.h | 15 - source/blender/blenkernel/BKE_customdata.h | 12 - source/blender/blenkernel/BKE_pbvh.h | 25 +- source/blender/blenkernel/intern/blender.c | 2 - .../blender/blenkernel/intern/customdata.cc | 83 +-- source/blender/blenkernel/intern/paint.cc | 244 +++---- source/blender/blenkernel/intern/pbvh.c | 31 +- source/blender/blenkernel/intern/pbvh.cc | 449 +----------- .../blender/blenkernel/intern/pbvh_bmesh.cc | 6 +- .../blenkernel/intern/pbvh_displacement.c | 266 -------- source/blender/blenlib/BLI_ghash.h | 6 +- source/blender/blenlib/BLI_heap_minmax.h | 118 ---- source/blender/blenlib/CMakeLists.txt | 2 +- .../{BLI_table_gset.c => BLI_table_gset.cc} | 98 ++- source/blender/blenlib/intern/heap_minmax.c | 642 ------------------ .../blenloader/intern/versioning_300.cc | 17 + source/blender/bmesh/CMakeLists.txt | 4 +- source/blender/bmesh/intern/bmesh_construct.c | 20 +- .../blender/bmesh/intern/bmesh_data_attr.cc | 387 ----------- source/blender/bmesh/intern/bmesh_data_attr.h | 275 -------- source/blender/bmesh/intern/bmesh_mesh.cc | 63 -- .../bmesh/intern/bmesh_mesh_convert.cc | 4 +- .../intern/bmesh_mesh_convert_threaded.cc | 2 +- source/blender/bmesh/intern/bmesh_opdefines.c | 4 +- source/blender/editors/mesh/editmesh_fair.c | 9 - source/blender/editors/mesh/editmesh_utils.c | 4 - source/blender/editors/mesh/mesh_data.cc | 5 +- source/blender/editors/object/object_edit.cc | 6 - .../editors/sculpt_paint/sculpt_dyntopo.cc | 43 -- .../editors/sculpt_paint/sculpt_face_set.cc | 2 - .../editors/sculpt_paint/sculpt_undo.cc | 12 +- .../blender/makesdna/DNA_customdata_types.h | 15 +- 32 files changed, 246 insertions(+), 2625 deletions(-) delete mode 100644 source/blender/blenkernel/intern/pbvh_displacement.c delete mode 100644 source/blender/blenlib/BLI_heap_minmax.h rename source/blender/blenlib/intern/{BLI_table_gset.c => BLI_table_gset.cc} (52%) delete mode 100644 source/blender/blenlib/intern/heap_minmax.c delete mode 100644 source/blender/bmesh/intern/bmesh_data_attr.cc delete mode 100644 source/blender/bmesh/intern/bmesh_data_attr.h diff --git a/source/blender/blenkernel/BKE_attribute.h b/source/blender/blenkernel/BKE_attribute.h index 079cc4fb5d2..fb742c1a401 100644 --- a/source/blender/blenkernel/BKE_attribute.h +++ b/source/blender/blenkernel/BKE_attribute.h @@ -109,21 +109,6 @@ int BKE_id_attribute_to_index(const struct ID *id, eAttrDomainMask domain_mask, eCustomDataMask layer_mask); -/** - * Sets up a temporary ID with arbitrary CustomData domains. r_id will - * be zero initialized with ID type id_type and any non-nullptr - * CustomData parameter will be copied into the appropriate struct members. - * - * \param r_id Pointer to storage sufficient for ID typecode id_type. - */ -void BKE_id_attribute_copy_domains_temp(short id_type, - const struct CustomData *vdata, - const struct CustomData *edata, - const struct CustomData *ldata, - const struct CustomData *pdata, - const struct CustomData *cdata, - struct ID *r_id); - const char *BKE_id_attributes_active_color_name(const struct ID *id); const char *BKE_id_attributes_default_color_name(const struct ID *id); void BKE_id_attributes_active_color_set(struct ID *id, const char *name); diff --git a/source/blender/blenkernel/BKE_customdata.h b/source/blender/blenkernel/BKE_customdata.h index dcfe589146a..d7be888dfe3 100644 --- a/source/blender/blenkernel/BKE_customdata.h +++ b/source/blender/blenkernel/BKE_customdata.h @@ -341,7 +341,6 @@ void CustomData_copy_data_named(const struct CustomData *source, int dest_index, int count); -// ignores CD_MESH_ID layer if it exists void CustomData_bmesh_swap_data(struct CustomData *source, struct CustomData *dest, void *src_block, @@ -834,17 +833,6 @@ void CustomData_regen_active_refs(CustomData *data); void CustomData_bmesh_asan_poison(const CustomData *data, void *block); void CustomData_bmesh_asan_unpoison(const CustomData *data, void *block); -void CustomData_setDefaultData(eCustomDataType type, void *block, int totelem); -size_t CustomData_getTypeSize(eCustomDataType type); -void CustomData_freeData(eCustomDataType type, void *block, int totelem); -void CustomData_interpData(eCustomDataType type, - void *block, - int tot, - const void **srcs, - const float *ws, - const float *sub_ws); -size_t CustomData_get_elem_size(const struct CustomDataLayer *layer); - #ifndef NDEBUG struct DynStr; /** Use to inspect mesh data when debugging. */ diff --git a/source/blender/blenkernel/BKE_pbvh.h b/source/blender/blenkernel/BKE_pbvh.h index 3eb303af66c..c88aa95ca49 100644 --- a/source/blender/blenkernel/BKE_pbvh.h +++ b/source/blender/blenkernel/BKE_pbvh.h @@ -18,6 +18,8 @@ #include "BKE_attribute.h" #include "BKE_ccg.h" +#include "BLI_smallhash.h" + #include //#define DEFRAGMENT_MEMORY @@ -44,8 +46,6 @@ typedef struct SculptLoopRef { # include "BLI_smallhash.h" #endif -//#define WITH_PBVH_CACHE - struct BMesh; struct BMVert; struct BMEdge; @@ -1071,7 +1071,13 @@ bool *BKE_pbvh_get_vert_hide_for_write(PBVH *pbvh); const bool *BKE_pbvh_get_poly_hide(const PBVH *pbvh); -bool BKE_pbvh_get_color_layer(const struct Mesh *me, +/* Get active color attribute; if pbvh is non-null + * and is of type PBVH_BMESH the layer inside of + * pbvh->header.bm will be returned, otherwise the + * layer will be looked up inside of me. + */ +bool BKE_pbvh_get_color_layer(const PBVH *pbvh, + const struct Mesh *me, CustomDataLayer **r_layer, eAttrDomain *r_attr); @@ -1399,17 +1405,6 @@ void BKE_pbvh_pmap_to_edges(PBVH *pbvh, void BKE_pbvh_set_vemap(PBVH *pbvh, struct MeshElemMap *vemap); void BKE_pbvh_ignore_uvs_set(PBVH *pbvh, bool value); -bool BKE_pbvh_cache_is_valid(const struct Object *ob, - const struct Mesh *me, - const PBVH *pbvh, - int pbvh_type); - -#ifdef WITH_PBVH_CACHE -bool BKE_pbvh_cache(const struct Mesh *me, PBVH *pbvh); -PBVH *BKE_pbvh_get_or_free_cached(struct Object *ob, struct Mesh *me, PBVHType pbvh_type); -void BKE_pbvh_invalidate_cache(struct Object *ob); -void BKE_pbvh_set_cached(struct Object *ob, PBVH *pbvh); -#endif void BKE_pbvh_set_face_areas(PBVH *pbvh, float *face_areas); void BKE_pbvh_set_sculpt_verts(PBVH *pbvh, struct MSculptVert *sverts); @@ -1418,8 +1413,6 @@ SculptPMap *BKE_pbvh_get_pmap(PBVH *pbvh); void BKE_pbvh_cache_remove(PBVH *pbvh); void BKE_pbvh_set_bmesh(PBVH *pbvh, struct BMesh *bm); void BKE_pbvh_free_bmesh(PBVH *pbvh, struct BMesh *bm); -void BKE_pbvh_system_init(void); -void BKE_pbvh_system_exit(void); SculptPMap *BKE_pbvh_make_pmap(const struct Mesh *me); void BKE_pbvh_pmap_aquire(SculptPMap *pmap); diff --git a/source/blender/blenkernel/intern/blender.c b/source/blender/blenkernel/intern/blender.c index da650217657..3598201d906 100644 --- a/source/blender/blenkernel/intern/blender.c +++ b/source/blender/blenkernel/intern/blender.c @@ -34,7 +34,6 @@ #include "BKE_layer.h" #include "BKE_main.h" #include "BKE_node.h" -#include "BKE_pbvh.h" #include "BKE_report.h" #include "BKE_scene.h" #include "BKE_screen.h" @@ -76,7 +75,6 @@ void BKE_blender_free(void) DEG_free_node_types(); BKE_brush_system_exit(); - BKE_pbvh_system_exit(); RE_texture_rng_exit(); BKE_callback_global_finalize(); diff --git a/source/blender/blenkernel/intern/customdata.cc b/source/blender/blenkernel/intern/customdata.cc index be6c00c9436..5ffc3d0ba9c 100644 --- a/source/blender/blenkernel/intern/customdata.cc +++ b/source/blender/blenkernel/intern/customdata.cc @@ -1607,16 +1607,6 @@ static void layerInterp_noop(const void **, const float *, const float *, int, v // do nothing } -static void layerDefault_mesh_id(void *data, int count) -{ - int *val = (int *)data; - - for (int i = 0; i < count; i++) { - // val[i] = -1; - val[i] = 0; - } -} - static bool layerEqual_propfloat2(const void *data1, const void *data2) { const float2 &a = *static_cast(data1); @@ -2083,17 +2073,7 @@ static const LayerTypeInfo LAYERTYPEINFO[CD_NUMTYPES] = { nullptr}, /* 51: CD_HAIRLENGTH */ {sizeof(float), "float", 1, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr}, - /*52 CD_MESH_ID */ - {sizeof(unsigned int), - "MIntProperty", - 1, - nullptr, // flag singleton layer - nullptr, - nullptr, - layerInterp_noop, - nullptr, - layerDefault_mesh_id}, - /* 53 CD_DYNTOPO_VERT */ + /* 52 CD_DYNTOPO_VERT */ {sizeof(MSculptVert), "MSculptVert", 1, @@ -2101,7 +2081,7 @@ static const LayerTypeInfo LAYERTYPEINFO[CD_NUMTYPES] = { layerDynTopoVert_copy, nullptr, layerDynTopoVert_interp}, - /*54 CD_BMESH_TOOLFLAGS */ + /*53 CD_BMESH_TOOLFLAGS */ {sizeof(MToolFlags), "MToolFlags", 1, @@ -2178,7 +2158,6 @@ static const char *LAYERTYPENAMES[CD_NUMTYPES] = {/* 0-4 */ "CDPropBoolean", /*51-53*/ "CDHairLength", - "CDMeshID", "CDDyntopoVert", "CDPropInt16"}; @@ -5932,64 +5911,6 @@ void CustomData_blend_read(BlendDataReader *reader, CustomData *data, const int CustomData_regen_active_refs(data); // check for corrupted active layer refs } -size_t CustomData_getTypeSize(eCustomDataType type) -{ - const LayerTypeInfo *info = layerType_getInfo(type); - - return info ? info->size : 0ULL; -} - -void CustomData_setDefaultData(eCustomDataType type, void *block, int totelem) -{ - const LayerTypeInfo *info = layerType_getInfo(type); - - if (info->set_default_value) { - info->set_default_value(block, totelem); - } - else { - memset(block, 0, info->size * totelem); - } -} - -void CustomData_freeData(eCustomDataType type, void *block, int totelem) -{ - const LayerTypeInfo *info = layerType_getInfo(type); - - if (info->free) { - info->free(block, totelem, info->size); - } -} - -#include "BLI_alloca.h" - -void CustomData_interpData(eCustomDataType type, - void *block, - int tot, - const void **srcs, - const float *ws, - const float *sub_ws) -{ - const LayerTypeInfo *info = layerType_getInfo(type); - - if (!info->interp) { - return; - } - - if (!ws) { - float *ws2 = static_cast(BLI_array_alloca(ws2, tot)); - float w = 1.0f / (float)tot; - - for (int i = 0; i < tot; i++) { - ws2[i] = w; - } - - info->interp(srcs, ws2, sub_ws, 1, block); - } - else { - info->interp(srcs, ws, sub_ws, 1, block); - } -} - /** \} */ /* -------------------------------------------------------------------- */ diff --git a/source/blender/blenkernel/intern/paint.cc b/source/blender/blenkernel/intern/paint.cc index beb7d2ae25b..a6e32e7529c 100644 --- a/source/blender/blenkernel/intern/paint.cc +++ b/source/blender/blenkernel/intern/paint.cc @@ -1492,27 +1492,13 @@ static void sculptsession_free_pbvh(Object *object) } if (ss->pbvh) { -#ifdef WITH_PBVH_CACHE - if (ss->needs_pbvh_rebuild) { - if (ss->pmap) { - BKE_pbvh_pmap_release(ss->pmap); - ss->pmap = nullptr; - } - - BKE_pbvh_cache_remove(ss->pbvh); - BKE_pbvh_free(ss->pbvh); - } - else { - BKE_pbvh_set_cached(object, ss->pbvh); - } -#else if (ss->pmap) { BKE_pbvh_pmap_release(ss->pmap); ss->pmap = nullptr; } BKE_pbvh_free(ss->pbvh); -#endif + ss->needs_pbvh_rebuild = false; ss->pbvh = nullptr; } @@ -1873,7 +1859,10 @@ static void sculpt_update_object( CustomDataLayer *layer; eAttrDomain domain; - if (BKE_pbvh_get_color_layer(me, &layer, &domain)) { + /* Do not pass ss->pbvh to BKE_pbvh_get_color_layer, + * if it does exist it might not be PBVH_GRIDS. + */ + if (BKE_pbvh_get_color_layer(nullptr, me, &layer, &domain)) { if (layer->type == CD_PROP_COLOR) { ss->vcol = static_cast(layer->data); } @@ -2556,52 +2545,31 @@ void BKE_sculpt_sync_face_visibility_to_grids(Mesh *mesh, SubdivCCG *subdiv_ccg) static PBVH *build_pbvh_for_dynamic_topology(Object *ob, bool update_sculptverts) { Mesh *me = BKE_object_get_original_mesh(ob); + PBVH *pbvh = ob->sculpt->pbvh = BKE_pbvh_new(PBVH_BMESH); + BKE_pbvh_set_bmesh(pbvh, ob->sculpt->bm); -#ifdef WITH_PBVH_CACHE - PBVH *pbvh = BKE_pbvh_get_or_free_cached(ob, me, PBVH_BMESH); -#else - PBVH *pbvh = nullptr; -#endif + BM_mesh_elem_table_ensure(ob->sculpt->bm, BM_VERT | BM_EDGE | BM_FACE); - if (!pbvh) { - pbvh = ob->sculpt->pbvh = BKE_pbvh_new(PBVH_BMESH); - BKE_pbvh_set_bmesh(pbvh, ob->sculpt->bm); + BKE_sculptsession_update_attr_refs(ob); + sculpt_boundary_flags_ensure(ob, pbvh, ob->sculpt->bm->totvert); + sculpt_check_face_areas(ob, pbvh); - BM_mesh_elem_table_ensure(ob->sculpt->bm, BM_VERT | BM_EDGE | BM_FACE); + BKE_sculpt_ensure_idmap(ob); - BKE_sculptsession_update_attr_refs(ob); - sculpt_boundary_flags_ensure(ob, pbvh, ob->sculpt->bm->totvert); - sculpt_check_face_areas(ob, pbvh); - - BKE_sculpt_ensure_idmap(ob); - - sculptsession_bmesh_add_layers(ob); - BKE_pbvh_build_bmesh(pbvh, - BKE_object_get_original_mesh(ob), - ob->sculpt->bm, - ob->sculpt->bm_smooth_shading, - ob->sculpt->bm_log, - ob->sculpt->bm_idmap, - ob->sculpt->attrs.dyntopo_node_id_vertex->bmesh_cd_offset, - ob->sculpt->attrs.dyntopo_node_id_face->bmesh_cd_offset, - ob->sculpt->cd_sculpt_vert, - ob->sculpt->attrs.face_areas->bmesh_cd_offset, - ob->sculpt->attrs.boundary_flags->bmesh_cd_offset, - ob->sculpt->fast_draw, - update_sculptverts); - } - else { - BMesh *bm = BKE_pbvh_get_bmesh(pbvh); - - if (bm && bm != ob->sculpt->bm) { - printf("%s: ss->bm != bm!\n", __func__); - - ob->sculpt->bm = bm; - } - else if (!bm) { - BKE_pbvh_set_bmesh(pbvh, ob->sculpt->bm); - } - } + sculptsession_bmesh_add_layers(ob); + BKE_pbvh_build_bmesh(pbvh, + BKE_object_get_original_mesh(ob), + ob->sculpt->bm, + ob->sculpt->bm_smooth_shading, + ob->sculpt->bm_log, + ob->sculpt->bm_idmap, + ob->sculpt->attrs.dyntopo_node_id_vertex->bmesh_cd_offset, + ob->sculpt->attrs.dyntopo_node_id_face->bmesh_cd_offset, + ob->sculpt->cd_sculpt_vert, + ob->sculpt->attrs.face_areas->bmesh_cd_offset, + ob->sculpt->attrs.boundary_flags->bmesh_cd_offset, + ob->sculpt->fast_draw, + update_sculptverts); if (ob->sculpt->bm_log) { BKE_pbvh_set_bm_log(pbvh, ob->sculpt->bm_log); @@ -2609,10 +2577,6 @@ static PBVH *build_pbvh_for_dynamic_topology(Object *ob, bool update_sculptverts BKE_pbvh_set_symmetry(pbvh, 0, (int)BKE_get_fset_boundary_symflag(ob)); -#ifdef WITH_PBVH_CACHE - BKE_pbvh_set_cached(ob, pbvh); -#endif - return pbvh; } @@ -2622,57 +2586,44 @@ static PBVH *build_pbvh_from_regular_mesh(Object *ob, Mesh *me_eval_deform, bool Mesh *me = BKE_object_get_original_mesh(ob); const int looptris_num = poly_to_tri_count(me->totpoly, me->totloop); -#ifdef WITH_PBVH_CACHE - PBVH *pbvh = BKE_pbvh_get_or_free_cached(ob, me, PBVH_FACES); -#else - PBVH *pbvh = nullptr; -#endif - MutableSpan positions = me->vert_positions_for_write(); const Span polys = me->polys(); const Span corner_verts = me->corner_verts(); - if (!ss->pmap && pbvh) { - ss->pmap = BKE_pbvh_get_pmap(pbvh); - BKE_pbvh_pmap_aquire(ss->pmap); - } - else if (!ss->pmap) { + if (!ss->pmap) { ss->pmap = BKE_pbvh_make_pmap(me); } - if (!pbvh) { - pbvh = ob->sculpt->pbvh = BKE_pbvh_new(PBVH_FACES); + PBVH *pbvh = ob->sculpt->pbvh = BKE_pbvh_new(PBVH_FACES); - BKE_sculptsession_update_attr_refs(ob); - sculpt_check_face_areas(ob, pbvh); - BKE_pbvh_respect_hide_set(pbvh, respect_hide); + BKE_sculptsession_update_attr_refs(ob); + sculpt_check_face_areas(ob, pbvh); + BKE_pbvh_respect_hide_set(pbvh, respect_hide); - float(*vert_cos)[3] = BKE_mesh_vert_positions_for_write(me); - const Span polys = me->polys(); + float(*vert_cos)[3] = BKE_mesh_vert_positions_for_write(me); - MLoopTri *looptri = static_cast( - MEM_malloc_arrayN(looptris_num, sizeof(*looptri), __func__)); + MLoopTri *looptri = static_cast( + MEM_malloc_arrayN(looptris_num, sizeof(*looptri), __func__)); - blender::bke::mesh::looptris_calc(positions, polys, corner_verts, {looptri, looptris_num}); - BKE_sculptsession_check_sculptverts(ob, pbvh, me->totvert); + blender::bke::mesh::looptris_calc(positions, polys, corner_verts, {looptri, looptris_num}); + BKE_sculptsession_check_sculptverts(ob, pbvh, me->totvert); - BKE_pbvh_build_mesh(pbvh, - me, - me->polys().data(), - corner_verts.data(), - me->corner_edges().data(), - vert_cos, - ss->msculptverts, - me->totvert, - &me->vdata, - &me->ldata, - &me->pdata, - looptri, - looptris_num, - ss->fast_draw, - (float *)ss->attrs.face_areas->data, - ss->pmap); - } + BKE_pbvh_build_mesh(pbvh, + me, + me->polys().data(), + corner_verts.data(), + me->corner_edges().data(), + vert_cos, + ss->msculptverts, + me->totvert, + &me->vdata, + &me->ldata, + &me->pdata, + looptri, + looptris_num, + ss->fast_draw, + (float *)ss->attrs.face_areas->data, + ss->pmap); BKE_pbvh_set_pmap(pbvh, ss->pmap); BKE_sculptsession_check_sculptverts(ob, pbvh, me->totvert); @@ -2685,10 +2636,6 @@ static PBVH *build_pbvh_from_regular_mesh(Object *ob, Mesh *me_eval_deform, bool pbvh, BKE_mesh_vert_positions(me_eval_deform), me_eval_deform->totvert); } -#ifdef WITH_PBVH_CACHE - BKE_pbvh_set_cached(ob, pbvh); -#endif - return pbvh; } @@ -2700,37 +2647,27 @@ static PBVH *build_pbvh_from_ccg(Object *ob, SubdivCCG *subdiv_ccg, bool respect BKE_subdiv_ccg_key_top_level(&key, subdiv_ccg); Mesh *base_mesh = BKE_mesh_from_object(ob); -#ifdef WITH_PBVH_CACHE - PBVH *pbvh = BKE_pbvh_get_or_free_cached(ob, base_mesh, PBVH_GRIDS); -#else - PBVH *pbvh = nullptr; -#endif int totgridfaces = base_mesh->totloop * (key.grid_size - 1) * (key.grid_size - 1); BKE_sculpt_sync_face_visibility_to_grids(base_mesh, subdiv_ccg); - if (!pbvh) { - pbvh = ob->sculpt->pbvh = BKE_pbvh_new(PBVH_GRIDS); + PBVH *pbvh = ob->sculpt->pbvh = BKE_pbvh_new(PBVH_GRIDS); - BKE_sculptsession_update_attr_refs(ob); - BKE_pbvh_respect_hide_set(pbvh, respect_hide); - sculpt_check_face_areas(ob, pbvh); + BKE_sculptsession_update_attr_refs(ob); + BKE_pbvh_respect_hide_set(pbvh, respect_hide); + sculpt_check_face_areas(ob, pbvh); - BKE_pbvh_build_grids(pbvh, - subdiv_ccg->grids, - subdiv_ccg->num_grids, - &key, - (void **)subdiv_ccg->grid_faces, - subdiv_ccg->grid_flag_mats, - subdiv_ccg->grid_hidden, - ob->sculpt->fast_draw, - (float *)ss->attrs.face_areas->data, - base_mesh, - subdiv_ccg); - } - else { - BKE_pbvh_subdiv_ccg_set(pbvh, subdiv_ccg); - } + BKE_pbvh_build_grids(pbvh, + subdiv_ccg->grids, + subdiv_ccg->num_grids, + &key, + (void **)subdiv_ccg->grid_faces, + subdiv_ccg->grid_flag_mats, + subdiv_ccg->grid_hidden, + ob->sculpt->fast_draw, + (float *)ss->attrs.face_areas->data, + base_mesh, + subdiv_ccg); BKE_pbvh_respect_hide_set(pbvh, respect_hide); BKE_pbvh_set_sculpt_verts(pbvh, ss->msculptverts); @@ -2754,10 +2691,6 @@ static PBVH *build_pbvh_from_ccg(Object *ob, SubdivCCG *subdiv_ccg, bool respect ss->msculptverts[i].stroke_id = -1; } -#ifdef WITH_PBVH_CACHE - BKE_pbvh_set_cached(ob, pbvh); -#endif - return pbvh; } @@ -2825,34 +2758,19 @@ PBVH *BKE_sculpt_object_pbvh_ensure(Depsgraph *depsgraph, Object *ob) bool is_dyntopo = (mesh_orig->flag & ME_SCULPT_DYNAMIC_TOPOLOGY); if (is_dyntopo) { - /* check if the pbvh cache has a valid bmesh */ -# ifdef WITH_PBVH_CACHE - pbvh = BKE_pbvh_get_or_free_cached(ob, mesh_orig, PBVH_BMESH); -# else - pbvh = nullptr; -# endif + BMesh *bm = BKE_sculptsession_empty_bmesh_create(); - BMesh *bm = nullptr; + BMeshFromMeshParams params = {0}; + params.calc_face_normal = true; + params.use_shapekey = true; + params.active_shapekey = ob->shapenr; + params.create_shapekey_layers = true; + params.ignore_id_layers = false; + params.copy_temp_cdlayers = true; - if (pbvh) { - bm = BKE_pbvh_get_bmesh(pbvh); - } + params.cd_mask_extra.vmask = CD_MASK_DYNTOPO_VERT; - if (!bm) { - bm = BKE_sculptsession_empty_bmesh_create(); - - BMeshFromMeshParams params = {0}; - params.calc_face_normal = true; - params.use_shapekey = true; - params.active_shapekey = ob->shapenr; - params.create_shapekey_layers = true; - params.ignore_id_layers = false; - params.copy_temp_cdlayers = true; - - params.cd_mask_extra.vmask = CD_MASK_DYNTOPO_VERT; - - BM_mesh_bm_from_me(bm, mesh_orig, ¶ms); - } + BM_mesh_bm_from_me(bm, mesh_orig, ¶ms); ob->sculpt->bm = bm; @@ -3361,7 +3279,7 @@ static bool sculpt_attribute_create(SculptSession *ss, out->data = out->layer->data; out->data_for_bmesh = false; out->bmesh_cd_offset = -1; - out->elem_size = CustomData_get_elem_size(out->layer); + out->elem_size = CustomData_sizeof(proptype); break; } @@ -3524,7 +3442,7 @@ SculptAttribute *BKE_sculpt_attribute_get(struct Object *ob, attr->bmesh_cd_offset = cdata->layers[index].offset; attr->elem_num = totelem; attr->layer = cdata->layers + index; - attr->elem_size = CustomData_get_elem_size(attr->layer); + attr->elem_size = CustomData_sizeof(proptype); BLI_strncpy_utf8(attr->name, name, sizeof(attr->name)); return attr; @@ -3673,7 +3591,7 @@ template static void sculpt_clear_attribute_bmesh(BMesh *bm, SculptA return; } - int size = CustomData_getTypeSize(attr->proptype); + int size = CustomData_sizeof(attr->proptype); T *elem; BM_ITER_MESH (elem, &iter, bm, itertype) { @@ -3698,7 +3616,7 @@ void BKE_sculpt_attributes_destroy_temporary_stroke(Object *ob) if (!attr->params.simple_array && ss->bm) { BMIter iter; - int size = CustomData_getTypeSize(attr->proptype); + int size = CustomData_sizeof(attr->proptype); /* Zero the attribute in an attempt to emulate the behavior of releasing it. */ switch (attr->domain) { diff --git a/source/blender/blenkernel/intern/pbvh.c b/source/blender/blenkernel/intern/pbvh.c index 65656b7b0f9..994569479ed 100644 --- a/source/blender/blenkernel/intern/pbvh.c +++ b/source/blender/blenkernel/intern/pbvh.c @@ -1134,10 +1134,6 @@ PBVH *BKE_pbvh_new(PBVHType type) void BKE_pbvh_free(PBVH *pbvh) { -#ifdef WITH_PBVH_CACHE - BKE_pbvh_cache_remove(pbvh); -#endif - for (int i = 0; i < pbvh->totnode; i++) { PBVHNode *node = &pbvh->nodes[i]; @@ -1798,7 +1794,7 @@ static void pbvh_update_draw_buffer_cb(void *__restrict userdata, CustomDataLayer *vcol_layer = NULL; eAttrDomain vcol_domain; - BKE_pbvh_get_color_layer(me, &vcol_layer, &vcol_domain); + BKE_pbvh_get_color_layer(pbvh, me, &vcol_layer, &vcol_domain); CustomData *vdata, *ldata; @@ -4305,9 +4301,7 @@ void BKE_pbvh_free_proxyarray(PBVH *pbvh, PBVHNode *node) memset(p, 0, sizeof(*p)); } -void BKE_pbvh_update_proxyvert(PBVH *pbvh, PBVHNode *node, ProxyVertUpdateRec *rec) -{ -} +void BKE_pbvh_update_proxyvert(PBVH *pbvh, PBVHNode *node, ProxyVertUpdateRec *rec) {} ProxyVertArray *BKE_pbvh_get_proxyarrays(PBVH *pbvh, PBVHNode *node) { @@ -5102,14 +5096,6 @@ void BKE_pbvh_invalidate_cache(Object *ob) char key[PBVH_CACHE_KEY_SIZE]; pbvh_make_cached_key(ob_orig, key); - -#ifdef WITH_PBVH_CACHE - PBVH *pbvh = BLI_ghash_lookup(cached_pbvhs, key); - - if (pbvh) { - BKE_pbvh_cache_remove(pbvh); - } -#endif } PBVH *BKE_pbvh_get_or_free_cached(Object *ob, Mesh *me, PBVHType pbvh_type) @@ -5290,17 +5276,6 @@ BMLog *BKE_pbvh_get_bm_log(PBVH *pbvh) return pbvh->bm_log; } -void BKE_pbvh_system_init() -{ - cached_pbvhs = BLI_ghash_str_new("pbvh cache ghash"); -} - -void BKE_pbvh_system_exit() -{ - pbvh_clear_cached_pbvhs(NULL); - BLI_ghash_free(cached_pbvhs, NULL, NULL); -} - SculptPMap *BKE_pbvh_make_pmap(const struct Mesh *me) { SculptPMap *pmap = MEM_callocN(sizeof(*pmap), "SculptPMap"); @@ -5604,7 +5579,7 @@ static void pbvh_face_iter_step(PBVHFaceIter *fd, bool do_step) } /* TODO: BMesh doesn't yet use .hide_poly. */ - fd->hide = nullptr; + fd->hide = nullptr; pbvh_face_iter_verts_reserve(fd, f->len); int vertex_i = 0; diff --git a/source/blender/blenkernel/intern/pbvh.cc b/source/blender/blenkernel/intern/pbvh.cc index dfe7d12b9b4..4d6323ec250 100644 --- a/source/blender/blenkernel/intern/pbvh.cc +++ b/source/blender/blenkernel/intern/pbvh.cc @@ -1176,10 +1176,6 @@ PBVH *BKE_pbvh_new(PBVHType type) void BKE_pbvh_free(PBVH *pbvh) { -#ifdef WITH_PBVH_CACHE - BKE_pbvh_cache_remove(pbvh); -#endif - for (int i = 0; i < pbvh->totnode; i++) { PBVHNode *node = &pbvh->nodes[i]; @@ -1795,7 +1791,10 @@ void pbvh_update_BB_redraw(PBVH *pbvh, PBVHNode **nodes, int totnode, int flag) BLI_task_parallel_range(0, totnode, &data, pbvh_update_BB_redraw_task_cb, &settings); } -bool BKE_pbvh_get_color_layer(const Mesh *me, CustomDataLayer **r_layer, eAttrDomain *r_attr) +bool BKE_pbvh_get_color_layer(const PBVH *pbvh, + const Mesh *me, + CustomDataLayer **r_layer, + eAttrDomain *r_attr) { CustomDataLayer *layer = BKE_id_attributes_color_find(&me->id, me->active_color_attribute); @@ -1813,6 +1812,29 @@ bool BKE_pbvh_get_color_layer(const Mesh *me, CustomDataLayer **r_layer, eAttrDo return false; } + if (BKE_pbvh_type(pbvh) == PBVH_BMESH) { + CustomData *data; + + if (domain == ATTR_DOMAIN_POINT) { + data = &pbvh->header.bm->vdata; + } + else if (domain == ATTR_DOMAIN_CORNER) { + data = &pbvh->header.bm->ldata; + } + + int layer_i = CustomData_get_named_layer_index( + data, eCustomDataType(layer->type), layer->name); + if (layer_i == -1) { + printf("%s: bmesh lacks color attribute %s\n", __func__, layer->name); + + *r_layer = nullptr; + *r_attr = ATTR_DOMAIN_POINT; + return false; + } + + layer = &data->layers[layer_i]; + } + *r_layer = layer; *r_attr = domain; @@ -1834,31 +1856,18 @@ static void pbvh_update_draw_buffer_cb(void *__restrict userdata, CustomDataLayer *vcol_layer = nullptr; eAttrDomain vcol_domain; - BKE_pbvh_get_color_layer(me, &vcol_layer, &vcol_domain); + BKE_pbvh_get_color_layer(pbvh, me, &vcol_layer, &vcol_domain); CustomData *vdata, *ldata; - if (!pbvh->header.bm) { + if (pbvh->header.bm) { + vdata = &pbvh->header.bm->vdata; + ldata = &pbvh->header.bm->ldata; + } + else { vdata = pbvh->vdata ? pbvh->vdata : &me->vdata; ldata = pbvh->ldata ? pbvh->ldata : &me->ldata; } - else { - vdata = &pbvh->header.bm->vdata; - ldata = &pbvh->header.bm->ldata; - } - - Mesh me_query; - BKE_id_attribute_copy_domains_temp(ID_ME, vdata, nullptr, ldata, nullptr, nullptr, &me_query.id); - me_query.active_color_attribute = me->active_color_attribute; - - if (!pbvh->header.bm) { - vdata = pbvh->vdata; - ldata = pbvh->ldata; - } - else { - vdata = &pbvh->header.bm->vdata; - ldata = &pbvh->header.bm->ldata; - } if (node->flag & PBVH_RebuildDrawBuffers) { PBVH_GPU_Args args; @@ -4515,295 +4524,6 @@ void BKE_pbvh_ignore_uvs_set(PBVH *pbvh, bool value) pbvh_boundaries_flag_update(pbvh); } -bool BKE_pbvh_cache(const struct Mesh *me, PBVH *pbvh) -{ - memset(&pbvh->cached_data, 0, sizeof(pbvh->cached_data)); - - if (pbvh->invalid) { - printf("invalid pbvh!\n"); - return false; - } - - switch (pbvh->header.type) { - case PBVH_BMESH: - if (!pbvh->header.bm) { - return false; - } - - pbvh->cached_data.bm = pbvh->header.bm; - - pbvh->cached_data.vdata = pbvh->header.bm->vdata; - pbvh->cached_data.edata = pbvh->header.bm->edata; - pbvh->cached_data.ldata = pbvh->header.bm->ldata; - pbvh->cached_data.pdata = pbvh->header.bm->pdata; - - pbvh->cached_data.totvert = pbvh->header.bm->totvert; - pbvh->cached_data.totedge = pbvh->header.bm->totedge; - pbvh->cached_data.totloop = pbvh->header.bm->totloop; - pbvh->cached_data.totpoly = pbvh->header.bm->totface; - break; - case PBVH_GRIDS: { - pbvh->cached_data.vdata = me->vdata; - pbvh->cached_data.edata = me->edata; - pbvh->cached_data.ldata = me->ldata; - pbvh->cached_data.pdata = me->pdata; - - int grid_side = pbvh->gridkey.grid_size; - - pbvh->cached_data.totvert = pbvh->totgrid * grid_side * grid_side; - pbvh->cached_data.totedge = me->totedge; - pbvh->cached_data.totloop = me->totloop; - pbvh->cached_data.totpoly = pbvh->totgrid * (grid_side - 1) * (grid_side - 1); - break; - } - case PBVH_FACES: - pbvh->cached_data.vdata = me->vdata; - pbvh->cached_data.edata = me->edata; - pbvh->cached_data.ldata = me->ldata; - pbvh->cached_data.pdata = me->pdata; - - pbvh->cached_data.totvert = me->totvert; - pbvh->cached_data.totedge = me->totedge; - pbvh->cached_data.totloop = me->totloop; - pbvh->cached_data.totpoly = me->totpoly; - break; - } - - return true; -} - -static bool customdata_is_same(const CustomData *a, const CustomData *b) -{ - return memcmp(a, b, sizeof(CustomData)) == 0; -} - -bool BKE_pbvh_cache_is_valid(const struct Object *ob, - const struct Mesh *me, - const PBVH *pbvh, - int pbvh_type) -{ - if (pbvh->invalid) { - printf("pbvh invalid!\n"); - return false; - } - - if (pbvh->header.type != pbvh_type) { - return false; - } - - bool ok = true; - int totvert = 0, totedge = 0, totloop = 0, totpoly = 0; - const CustomData *vdata, *edata, *ldata, *pdata; - - MultiresModifierData *mmd = nullptr; - - LISTBASE_FOREACH (ModifierData *, md, &ob->modifiers) { - if (md->type == eModifierType_Multires) { - mmd = (MultiresModifierData *)md; - break; - } - } - - if (mmd && (mmd->flags & eModifierMode_Realtime)) { - // return false; - } - - switch (pbvh_type) { - case PBVH_BMESH: - if (!pbvh->header.bm || pbvh->header.bm != pbvh->cached_data.bm) { - return false; - } - - totvert = pbvh->header.bm->totvert; - totedge = pbvh->header.bm->totedge; - totloop = pbvh->header.bm->totloop; - totpoly = pbvh->header.bm->totface; - - vdata = &pbvh->header.bm->vdata; - edata = &pbvh->header.bm->edata; - ldata = &pbvh->header.bm->ldata; - pdata = &pbvh->header.bm->pdata; - break; - case PBVH_FACES: - totvert = me->totvert; - totedge = me->totedge; - totloop = me->totloop; - totpoly = me->totpoly; - - vdata = &me->vdata; - edata = &me->edata; - ldata = &me->ldata; - pdata = &me->pdata; - break; - case PBVH_GRIDS: { - if (!mmd) { - return false; - } - - int grid_side = 1 + (1 << (mmd->sculptlvl - 1)); - - totvert = me->totloop * grid_side * grid_side; - totedge = me->totedge; - totloop = me->totloop; - totpoly = me->totloop * (grid_side - 1) * (grid_side - 1); - - vdata = &me->vdata; - edata = &me->edata; - ldata = &me->ldata; - pdata = &me->pdata; - break; - } - } - - ok = ok && totvert == pbvh->cached_data.totvert; - ok = ok && totedge == pbvh->cached_data.totedge; - ok = ok && totloop == pbvh->cached_data.totloop; - ok = ok && totpoly == pbvh->cached_data.totpoly; - - ok = ok && customdata_is_same(vdata, &pbvh->cached_data.vdata); - ok = ok && customdata_is_same(edata, &pbvh->cached_data.edata); - ok = ok && customdata_is_same(ldata, &pbvh->cached_data.ldata); - ok = ok && customdata_is_same(pdata, &pbvh->cached_data.pdata); - - return ok; -} - -static GHash *cached_pbvhs = nullptr; -static void pbvh_clear_cached_pbvhs(PBVH *exclude) -{ - Vector pbvhs; - - GHashIterator iter; - GHASH_ITER (iter, cached_pbvhs) { - PBVH *pbvh = (PBVH *)BLI_ghashIterator_getValue(&iter); - - if (pbvh != exclude) { - pbvhs.append(pbvh); - } - } - - for (int i = 0; i < pbvhs.size(); i++) { - PBVH *pbvh = pbvhs[i]; - - if (pbvh->header.bm) { - BM_mesh_free(pbvh->header.bm); - } - - BKE_pbvh_free(pbvh); - } - - BLI_ghash_clear(cached_pbvhs, MEM_freeN, nullptr); -} - -void BKE_pbvh_clear_cache(PBVH *preserve) -{ - pbvh_clear_cached_pbvhs(nullptr); -} - -#define PBVH_CACHE_KEY_SIZE 1024 - -static void pbvh_make_cached_key(Object *ob, char out[PBVH_CACHE_KEY_SIZE]) -{ - sprintf(out, "%s:%p", ob->id.name, G.main); -} - -void BKE_pbvh_invalidate_cache(Object *ob) -{ - Object *ob_orig = DEG_get_original_object(ob); - - char key[PBVH_CACHE_KEY_SIZE]; - pbvh_make_cached_key(ob_orig, key); - -#ifdef WITH_PBVH_CACHE - PBVH *pbvh = BLI_ghash_lookup(cached_pbvhs, key); - - if (pbvh) { - BKE_pbvh_cache_remove(pbvh); - } -#endif -} - -PBVH *BKE_pbvh_get_or_free_cached(Object *ob, Mesh *me, PBVHType pbvh_type) -{ - Object *ob_orig = DEG_get_original_object(ob); - - char key[PBVH_CACHE_KEY_SIZE]; - pbvh_make_cached_key(ob_orig, key); - - PBVH *pbvh = (PBVH *)BLI_ghash_lookup(cached_pbvhs, key); - - if (!pbvh) { - return nullptr; - } - - if (BKE_pbvh_cache_is_valid(ob, me, pbvh, pbvh_type)) { - switch (pbvh_type) { - case PBVH_BMESH: - break; - case PBVH_FACES: - pbvh->vert_normals = BKE_mesh_vert_normals_for_write(me); - case PBVH_GRIDS: - if (!pbvh->deformed) { - pbvh->vert_positions = BKE_mesh_vert_positions_for_write(me); - } - - pbvh->corner_verts = me->corner_verts().data(); - pbvh->corner_edges = me->corner_edges().data(); - pbvh->polys = me->polys().data(); - pbvh->vdata = &me->vdata; - pbvh->ldata = &me->ldata; - pbvh->pdata = &me->pdata; - - pbvh->face_sets = (int *)CustomData_get_layer_named( - &me->pdata, CD_PROP_INT32, ".sculpt_face_set"); - - break; - } - - BKE_pbvh_update_active_vcol(pbvh, me); - - return pbvh; - } - - pbvh_clear_cached_pbvhs(nullptr); - return nullptr; -} - -void BKE_pbvh_set_cached(Object *ob, PBVH *pbvh) -{ - if (!pbvh) { - return; - } - - Object *ob_orig = DEG_get_original_object(ob); - - char key[PBVH_CACHE_KEY_SIZE]; - pbvh_make_cached_key(ob_orig, key); - - PBVH *exist = (PBVH *)BLI_ghash_lookup(cached_pbvhs, key); - - if (pbvh->invalid) { - printf("pbvh invalid!"); - } - - if (exist && exist->invalid) { - printf("pbvh invalid!"); - } - - if (!exist || exist != pbvh) { - pbvh_clear_cached_pbvhs(pbvh); - - char key[PBVH_CACHE_KEY_SIZE]; - pbvh_make_cached_key(ob_orig, key); - - BLI_ghash_insert(cached_pbvhs, BLI_strdup(key), pbvh); - } - -#ifdef WITH_PBVH_CACHE - BKE_pbvh_cache(BKE_object_get_original_mesh(ob_orig), pbvh); -#endif -} - struct SculptPMap *BKE_pbvh_get_pmap(PBVH *pbvh) { return pbvh->pmap; @@ -4818,92 +4538,16 @@ void BKE_pbvh_set_pmap(PBVH *pbvh, SculptPMap *pmap) pbvh->pmap = pmap; } -/** Does not free pbvh itself. */ -void BKE_pbvh_cache_remove(PBVH *pbvh) -{ - Vector keys; - - GHashIterator iter; - GHASH_ITER (iter, cached_pbvhs) { - PBVH *pbvh2 = (PBVH *)BLI_ghashIterator_getValue(&iter); - - if (pbvh2 == pbvh) { - keys.append((char *)BLI_ghashIterator_getKey(&iter)); - break; - } - } - - for (int i = 0; i < keys.size(); i++) { - BLI_ghash_remove(cached_pbvhs, keys[i], MEM_freeN, nullptr); - } -} - void BKE_pbvh_set_bmesh(PBVH *pbvh, BMesh *bm) { pbvh->header.bm = bm; } -void BKE_pbvh_free_bmesh(PBVH *pbvh, BMesh *bm) -{ - if (pbvh) { - pbvh->header.bm = nullptr; - } - - BM_mesh_free(bm); - - GHashIterator iter; - Vector keys; - Vector pbvhs; - - GHASH_ITER (iter, cached_pbvhs) { - PBVH *pbvh2 = (PBVH *)BLI_ghashIterator_getValue(&iter); - - if (pbvh2->header.bm == bm) { - pbvh2->header.bm = nullptr; - - if (pbvh2 != pbvh) { - bool ok = true; - - for (int i = 0; i < pbvhs.size(); i++) { - if (pbvhs[i] == pbvh2) { - ok = false; - } - } - - if (ok) { - pbvhs.append(pbvh2); - } - } - - keys.append((char *)BLI_ghashIterator_getKey(&iter)); - } - } - - for (int i = 0; i < keys.size(); i++) { - BLI_ghash_remove(cached_pbvhs, keys[i], MEM_freeN, nullptr); - } - - for (int i = 0; i < pbvhs.size(); i++) { - BKE_pbvh_free(pbvhs[i]); - } -} - BMLog *BKE_pbvh_get_bm_log(PBVH *pbvh) { return pbvh->bm_log; } -void BKE_pbvh_system_init() -{ - cached_pbvhs = BLI_ghash_str_new("pbvh cache ghash"); -} - -void BKE_pbvh_system_exit() -{ - pbvh_clear_cached_pbvhs(nullptr); - BLI_ghash_free(cached_pbvhs, nullptr, nullptr); -} - SculptPMap *BKE_pbvh_make_pmap(const struct Mesh *me) { SculptPMap *pmap = (SculptPMap *)MEM_callocN(sizeof(*pmap), "SculptPMap"); @@ -4934,11 +4578,7 @@ bool BKE_pbvh_pmap_release(SculptPMap *pmap) pmap->refcount--; - // if (pmap->refcount < 0) { - // printf("%s: error!\n", __func__); - //} - - if (1 && pmap->refcount == 0) { + if (pmap->refcount <= 0) { MEM_SAFE_FREE(pmap->pmap); MEM_SAFE_FREE(pmap->pmap_mem); MEM_SAFE_FREE(pmap); @@ -4978,24 +4618,9 @@ void BKE_pbvh_update_active_vcol(PBVH *pbvh, const Mesh *mesh) { CustomDataLayer *last_layer = pbvh->color_layer; - Mesh me_query; - const CustomData *vdata, *ldata; + BKE_pbvh_get_color_layer(pbvh, mesh, &pbvh->color_layer, &pbvh->color_domain); - if (pbvh->header.type == PBVH_BMESH && pbvh->header.bm) { - vdata = &pbvh->header.bm->vdata; - ldata = &pbvh->header.bm->ldata; - } - else { - vdata = &mesh->vdata; - ldata = &mesh->ldata; - } - - BKE_id_attribute_copy_domains_temp(ID_ME, vdata, nullptr, ldata, nullptr, nullptr, &me_query.id); - me_query.active_color_attribute = mesh->active_color_attribute; - - BKE_pbvh_get_color_layer(&me_query, &pbvh->color_layer, &pbvh->color_domain); - - if (pbvh->color_layer && pbvh->header.bm) { + if (pbvh->color_layer) { pbvh->cd_vcol_offset = pbvh->color_layer->offset; } else { @@ -5004,7 +4629,7 @@ void BKE_pbvh_update_active_vcol(PBVH *pbvh, const Mesh *mesh) if (pbvh->color_layer != last_layer) { for (int i = 0; i < pbvh->totnode; i++) { - PBVHNode *node = pbvh->nodes + i; + PBVHNode *node = &pbvh->nodes[i]; if (node->flag & PBVH_Leaf) { BKE_pbvh_node_mark_update_color(node); diff --git a/source/blender/blenkernel/intern/pbvh_bmesh.cc b/source/blender/blenkernel/intern/pbvh_bmesh.cc index 79cfcb199a6..0390e9f8fbf 100644 --- a/source/blender/blenkernel/intern/pbvh_bmesh.cc +++ b/source/blender/blenkernel/intern/pbvh_bmesh.cc @@ -2575,8 +2575,6 @@ void BKE_pbvh_build_bmesh(PBVH *pbvh, BKE_pbvh_parallel_range_settings(&settings, true, totleaf); BLI_task_parallel_range(0, totleaf, &tdata, pbvh_bmesh_create_leaf_fast_task_cb, &settings); - // pbvh_bmesh_create_leaf_fast(pbvh, nodeinfo, bbc_array, leaves[i]); - /* take root node and visit and populate children recursively */ pbvh_bmesh_create_nodes_fast_recursive_final(pbvh, nodeinfo, bbc_array, &rootnode); @@ -2584,11 +2582,11 @@ void BKE_pbvh_build_bmesh(PBVH *pbvh, MEM_freeN(bbc_array); MEM_freeN(nodeinfo); - if (me) { // ensure pbvh->vcol_type, vcol_domain and cd_vcol_offset are up to date + if (me) { /* Ensure pbvh->vcol_type, vcol_domain and cd_vcol_offset are up to date. */ CustomDataLayer *cl; eAttrDomain domain; - BKE_pbvh_get_color_layer(me, &cl, &domain); + BKE_pbvh_get_color_layer(pbvh, me, &cl, &domain); } /*final check that nodes are sufficiently subdivided*/ diff --git a/source/blender/blenkernel/intern/pbvh_displacement.c b/source/blender/blenkernel/intern/pbvh_displacement.c deleted file mode 100644 index 61550c2095a..00000000000 --- a/source/blender/blenkernel/intern/pbvh_displacement.c +++ /dev/null @@ -1,266 +0,0 @@ -#if 0 -# include "MEM_guardedalloc.h" - -# include "BLI_alloca.h" -# include "BLI_array.h" -# include "BLI_compiler_attrs.h" -# include "BLI_compiler_compat.h" -# include "BLI_ghash.h" -# include "BLI_linklist.h" -# include "BLI_math.h" -# include "BLI_memarena.h" -# include "BLI_memblock.h" -# include "BLI_mempool.h" -# include "BLI_utildefines.h" - -# include "BLI_hash.h" - -# include "BKE_context.h" -# include "BKE_global.h" -# include "BKE_image.h" -# include "BKE_mesh.h" -# include "BKE_multires.h" -# include "BKE_object.h" -# include "BKE_pbvh.h" -# include "BKE_scene.h" - -# include "BLI_bitmap.h" -# include "DNA_customdata_types.h" -# include "DNA_image_types.h" -# include "DNA_material_types.h" -# include "DNA_mesh_types.h" -# include "DNA_meshdata_types.h" -# include "DNA_object_types.h" -# include "DNA_scene_types.h" - -# include "pbvh_intern.hh" - -# include "bmesh.h" - -void *BKE_pbvh_get_tex_settings(PBVH *pbvh, PBVHNode *node, TexLayerRef vdm) -{ - return NULL; // implement me! -} - -void *BKE_pbvh_get_tex_data(PBVH *pbvh, PBVHNode *node, TexPointRef vdm) -{ - return NULL; // implement me! -} - -typedef union TexelKey { - struct { - int idx; // index in image - int co_key; // used to differentiate same texel used in different 3d points in space - } key; - intptr_t i; -} TexelKey; - -BLI_INLINE int calc_co_key(const float *co) -{ - const int mul = 65535; - const int mask = 65535; - - int x = (int)co[0] + (((int)co[0] * mul) & mask); - int y = (int)co[0] + (((int)co[0] * mul) & mask); - int z = (int)co[0] + (((int)co[0] * mul) & mask); - - return BLI_hash_int_3d(x, y, z); -} - -typedef struct TextureVDMSettings { - ImageUser ima_user; - ID *image; - bool tangent_space; - - char uv_layer[64]; - - // used by texture_vdm_get_points - // BLI_bitmap *texel_used_map; - GSet *texel_used_map; - - int width, height; - bool invalid; -} TextureVDMSettings; - -typedef struct TextureNodeData { - TexPointRef *point_ids; - float (*point_cos)[3]; - float (*point_uvs)[2]; - float **point_cos_ptrs; - int totpoint; -} TextureNodeData; - -void texture_vdm_begin(PBVH *pbvh, PBVHNode *node, TexLayerRef vdm) -{ - TextureVDMSettings *settings = BKE_pbvh_get_tex_settings(pbvh, node, vdm); - - if (!settings->image) { - return; - } - - Image *image = (Image *)settings->image; - - int w = 0, h = 0; - BKE_image_get_size(image, &settings->ima_user, &w, &h); - - // Image *image = settings->image. - settings->width = w; - settings->height = h; - // settings->texel_used_map = BLI_BITMAP_NEW(w * h, "texel_used_map"); - settings->texel_used_map = BLI_gset_ptr_new("texel_used_map"); -} - -void texture_vdm_build_points(PBVH *pbvh, PBVHNode *node, TexLayerRef vdm) -{ - TextureVDMSettings *settings = BKE_pbvh_get_tex_settings(pbvh, node, vdm); - TextureNodeData *data = BKE_pbvh_get_tex_data(pbvh, node, vdm); - - int idx; - - if (!settings->uv_layer[0]) { - idx = CustomData_get_layer_index(&pbvh->bm->ldata, CD_MLOOPUV); - } - else { - idx = CustomData_get_named_layer_index(&pbvh->bm->ldata, CD_MLOOPUV, settings->uv_layer); - } - - if (idx < 0) { - settings->invalid = true; - return; - } - - const int cd_uv = pbvh->bm->ldata.layers[idx].offset; - const int w = settings->width, h = settings->height; - - float **point_cos_ptrs = NULL; - float *uvs = NULL; - float *cos = NULL; - TexPointRef *ids = NULL; - - BLI_array_declare(point_cos_ptrs); - BLI_array_declare(uvs); - BLI_array_declare(cos); - BLI_array_declare(ids); - - for (int i = 0; i < node->tribuf->tottri; i++) { - PBVHTri *tri = node->tribuf->tris + i; - - BMLoop *ls[3] = {(BMLoop *)tri->l[0], (BMLoop *)tri->l[1], (BMLoop *)tri->l[2]}; - float min[2] = {FLT_MAX, FLT_MAX}, max[2] = {FLT_MIN, FLT_MIN}; - - float tricos[3][3]; - - copy_v3_v3(tricos[0], ls[0]->v->co); - copy_v3_v3(tricos[1], ls[1]->v->co); - copy_v3_v3(tricos[2], ls[2]->v->co); - - for (int j = 0; j < 3; j++) { - MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(ls[j], cd_uv); - minmax_v2v2_v2(min, max, luv->uv); - } - - int dw = (int)((max[0] - min[0]) * (float)w + 0.000001f); - int dh = (int)((max[1] - min[1]) * (float)h + 0.000001f); - - dw = MAX2(dw, 1); - dh = MAX2(dh, 1); - - float du = (max[0] - min[0]) / dw; - float dv = (max[1] - min[1]) / dh; - - float u = min[0], v = min[1]; - for (int y = 0; y < dh; y++, v += dv) { - u = min[0]; - - for (int x = 0; x < dw; x++, u += du) { - int idx = y * w + x; - float co[3]; - - interp_barycentric_tri_v3(tricos, u, v, co); - - TexelKey key; - key.key.idx = idx; - key.key.co_key = calc_co_key(co); - - if (BLI_gset_haskey(settings->texel_used_map, (void *)key.i)) { - continue; - } - - BLI_gset_insert(settings->texel_used_map, (void *)key.i); - - BLI_array_append(uvs, u); - BLI_array_append(uvs, v); - - BLI_array_append(cos, co[0]); - BLI_array_append(cos, co[1]); - BLI_array_append(cos, co[2]); - BLI_array_append(ids, (TexPointRef)key.i); - } - } - } - - settings->invalid = false; - MEM_SAFE_FREE(data->point_cos); - MEM_SAFE_FREE(data->point_ids); - MEM_SAFE_FREE(data->point_uvs); - MEM_SAFE_FREE(data->point_cos_ptrs); - - int totpoint = BLI_array_len(ids); - - data->totpoint = totpoint; - - data->point_cos_ptrs = MEM_malloc_arrayN(totpoint, sizeof(void *), "point_cos_ptrs"); - - // dumb casting trick - union { - float *cos; - float (*cos3)[3]; - } castcos; - - union { - float *uvs; - float (*uvs2)[2]; - } castuvs; - - castcos.cos = cos; - castuvs.uvs = uvs; - - data->point_cos = castcos.cos3; - data->point_ids = ids; - data->point_uvs = castuvs.uvs2; - - for (int i = 0; i < totpoint; i++) { - data->point_cos_ptrs[i] = cos + i * 3; - } -} - -void texture_vdm_get_points(PBVH *pbvh, - PBVHNode *node, - TexLayerRef vdm, - TexPointRef **r_ids, - float ***r_cos, - float ***r_nos, - int *r_totpoint) -{ - TextureVDMSettings *settings = BKE_pbvh_get_tex_settings(pbvh, node, vdm); - TextureNodeData *data = BKE_pbvh_get_tex_data(pbvh, node, vdm); - - if (r_totpoint) { - *r_totpoint = data->totpoint; - } - - if (r_cos) { - *r_cos = data->point_cos_ptrs; - } - - if (r_ids) { - *r_ids = data->point_ids; - } -} - -static SculptDisplacementDef texture_vdm = { - .type = SCULPT_TEXTURE_UV, - .settings_size = sizeof(TextureNodeData), - .getPointsFromNode = texture_vdm_get_points, -}; -#endif diff --git a/source/blender/blenlib/BLI_ghash.h b/source/blender/blenlib/BLI_ghash.h index 5b854bc9ec2..064b401f412 100644 --- a/source/blender/blenlib/BLI_ghash.h +++ b/source/blender/blenlib/BLI_ghash.h @@ -354,12 +354,8 @@ BLI_INLINE bool BLI_ghashIterator_done(const GHashIterator *ghi) typedef struct GSet GSet; -struct SmallHash; - -#include "BLI_smallhash.h" - typedef struct TableGSet { - struct SmallHash ptr_to_idx; + void *ptr_to_idx; void **elems; int size, length; int cur; diff --git a/source/blender/blenlib/BLI_heap_minmax.h b/source/blender/blenlib/BLI_heap_minmax.h deleted file mode 100644 index a1d65fcba19..00000000000 --- a/source/blender/blenlib/BLI_heap_minmax.h +++ /dev/null @@ -1,118 +0,0 @@ -/* - * 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 bli - * \brief A min-heap / priority queue ADT - */ - -#include "BLI_math.h" - -#ifdef __cplusplus -extern "C" { -#endif - -struct MinMaxHeap; -struct MinMaxHeapNode; -typedef struct MinMaxHeap MinMaxHeap; -typedef struct MinMaxHeapNode MinMaxHeapNode; - -typedef void (*MinMaxHeapFreeFP)(void *ptr); - -/** - * Creates a new heap. Removed nodes are recycled, so memory usage will not shrink. - * - * \note Use when the size of the heap is known in advance. - */ -MinMaxHeap *BLI_mm_heap_new_ex(unsigned int tot_reserve) ATTR_WARN_UNUSED_RESULT; -MinMaxHeap *BLI_mm_heap_new(void) ATTR_WARN_UNUSED_RESULT; -void BLI_mm_heap_clear(MinMaxHeap *heap, MinMaxHeapFreeFP ptrfreefp) ATTR_NONNULL(1); -void BLI_mm_heap_free(MinMaxHeap *heap, MinMaxHeapFreeFP ptrfreefp) ATTR_NONNULL(1); -/** - * Insert heap node with a value (often a 'cost') and pointer into the heap, - * duplicate values are allowed. - */ -MinMaxHeapNode *BLI_mm_heap_insert(MinMaxHeap *heap, float value, void *ptr) ATTR_NONNULL(1); -/** - * Convenience function since this is a common pattern. - */ -void BLI_mm_heap_insert_or_update(MinMaxHeap *heap, - MinMaxHeapNode **node_p, - float value, - void *ptr) ATTR_NONNULL(1, 2); -bool BLI_mm_heap_is_empty(const MinMaxHeap *heap) ATTR_NONNULL(1); -unsigned int BLI_mm_heap_len(const MinMaxHeap *heap) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1); - -/** - * Return the top node of the heap. - * This is the node with the lowest value. - */ -MinMaxHeapNode *BLI_mm_heap_min(const MinMaxHeap *heap) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1); -/** - * Return the value of top node of the heap. - * This is the node with the lowest value. - */ -float BLI_mm_heap_min_value(const MinMaxHeap *heap) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1); - -/** - * Return the top node of the heap. - * This is the node with the lowest value. - */ -MinMaxHeapNode *BLI_mm_heap_max(const MinMaxHeap *heap) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1); -/** - * Return the value of top node of the heap. - * This is the node with the lowest value. - */ -float BLI_mm_heap_max_value(const MinMaxHeap *heap) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1); - -/** - * Pop the top node off the heap and return its pointer. - */ -void *BLI_mm_heap_pop_min(MinMaxHeap *heap) ATTR_NONNULL(1); - -/** - * Pop the top node off the heap and return its pointer. - */ -void *BLI_mm_heap_pop_max(MinMaxHeap *heap) ATTR_NONNULL(1); - -/** - * Can be used to avoid #BLI_mm_heap_remove, #BLI_mm_heap_insert calls, - * balancing the tree still has a performance cost, - * but is often much less than remove/insert, difference is most noticeable with large heaps. - */ -MinMaxHeapNode *BLI_mm_heap_node_value_update(MinMaxHeap *heap, MinMaxHeapNode *node, float value) - ATTR_NONNULL(1, 2); - -MinMaxHeapNode *BLI_mm_heap_node_value_update_ptr(MinMaxHeap *heap, - MinMaxHeapNode *node, - float value, - void *ptr); - -/** - * Return the value or pointer of a heap node. - */ -float BLI_mm_heap_node_value(const MinMaxHeapNode *heap) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1); -void *BLI_mm_heap_node_ptr(const MinMaxHeapNode *heap) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1); -/** - * Only for checking internal errors (gtest). - */ -bool BLI_mm_heap_is_valid(const MinMaxHeap *heap); - -#ifdef __cplusplus -} -#endif diff --git a/source/blender/blenlib/CMakeLists.txt b/source/blender/blenlib/CMakeLists.txt index 49fb738e537..18d2128b360 100644 --- a/source/blender/blenlib/CMakeLists.txt +++ b/source/blender/blenlib/CMakeLists.txt @@ -160,7 +160,7 @@ set(SRC intern/voxel.c intern/winstuff.c intern/winstuff_dir.c - intern/BLI_table_gset.c + intern/BLI_table_gset.cc # Private headers. intern/BLI_mempool_private.h diff --git a/source/blender/blenlib/intern/BLI_table_gset.c b/source/blender/blenlib/intern/BLI_table_gset.cc similarity index 52% rename from source/blender/blenlib/intern/BLI_table_gset.c rename to source/blender/blenlib/intern/BLI_table_gset.cc index 88e3cc133b1..9c50e6ebf82 100644 --- a/source/blender/blenlib/intern/BLI_table_gset.c +++ b/source/blender/blenlib/intern/BLI_table_gset.cc @@ -8,28 +8,41 @@ #include "BLI_ghash.h" -//#define PTR_TO_IDX(ts) ((GHash *)ts->ptr_to_idx.buckets) -#define PTR_TO_IDX(ts) &(ts)->ptr_to_idx +#ifdef USE_TGSET_SMALLHASH +# include "BLI_smallhash.h" +# define PTR_TO_IDX(ts) static_cast((ts)->ptr_to_idx) +#else +# include "BLI_map.hh" +# define PTR_TO_IDX(ts) static_cast *>((ts)->ptr_to_idx) +#endif TableGSet *BLI_table_gset_new(const char *info) { - TableGSet *ts = MEM_callocN(sizeof(TableGSet), info); + TableGSet *ts = MEM_new(info); - // ts->ptr_to_idx.buckets = (void *)BLI_ghash_ptr_new(info); - BLI_smallhash_init(&ts->ptr_to_idx); +#ifdef USE_TGSET_SMALLHASH + ts->ptr_to_idx = static_cast(MEM_cnew("table gset smallhash")); + BLI_smallhash_init(PTR_TO_IDX(->ptr_to_idx)); +#else + ts->ptr_to_idx = static_cast(MEM_new>("ts->ptr_to_idx")); +#endif return ts; } TableGSet *BLI_table_gset_new_ex(const char *info, int size) { - TableGSet *ts = MEM_callocN(sizeof(TableGSet), info); + TableGSet *ts = MEM_new(info); - // ts->ptr_to_idx.buckets = (void *)BLI_ghash_ptr_new_ex(info, (uint)size); - BLI_smallhash_init_ex(&ts->ptr_to_idx, size); +#ifdef USE_TGSET_SMALLHASH + ts->ptr_to_idx = static_cast(MEM_cnew("table gset smallhash")); + BLI_smallhash_init(PTR_TO_IDX(->ptr_to_idx)); +#else + ts->ptr_to_idx = static_cast(MEM_new>("ts->ptr_to_idx")); +#endif if (size) { - ts->elems = MEM_callocN(sizeof(void *) * (uint)size, info); + ts->elems = static_cast(MEM_callocN(sizeof(void *) * (uint)size, info)); ts->size = size; ts->length = 0; ts->cur = 0; @@ -40,16 +53,14 @@ TableGSet *BLI_table_gset_new_ex(const char *info, int size) void BLI_table_gset_free(TableGSet *ts, GHashKeyFreeFP freefp) { - if (!PTR_TO_IDX(ts)) { - return; - } + MEM_SAFE_FREE(ts->elems); - if (ts->elems) { - MEM_freeN(ts->elems); - } - - // BLI_ghash_free(PTR_TO_IDX(ts), freefp, NULL); - BLI_smallhash_release(&ts->ptr_to_idx); +#ifdef USE_TGSET_SMALLHASH + BLI_smallhash_release(PTR_TO_IDX(ts->ptr_to_idx)); + MEM_freeN(ts->ptr_to_idx); +#else + MEM_delete>(PTR_TO_IDX(ts)); +#endif MEM_freeN(ts); } @@ -62,25 +73,26 @@ static void table_gset_resize(TableGSet *ts) newsize = MAX2(newsize, 8U); if (!ts->elems) { - ts->elems = (void *)MEM_mallocN(sizeof(void *) * newsize, "ts->elems"); + ts->elems = static_cast(MEM_mallocN(sizeof(void *) * newsize, "ts->elems")); } else { - ts->elems = (void *)MEM_reallocN(ts->elems, newsize * sizeof(void *)); + ts->elems = static_cast(MEM_reallocN(ts->elems, newsize * sizeof(void *))); } - // BLI_smallhash_clear(PTR_TO_IDX(ts), 0ULL); - - // compact + /* Compact. */ int i = 0, j = 0; for (i = 0; i < ts->cur; i++) { void *elem2 = ts->elems[i]; if (elem2) { +#ifdef USE_TGSET_SMALLHASH void **val; BLI_smallhash_ensure_p(PTR_TO_IDX(ts), (uintptr_t)elem2, &val); - // BLI_smallhash_insert(PTR_TO_IDX(ts), elem2, (void *)j); *val = POINTER_FROM_INT(j); +#else + PTR_TO_IDX(ts)->add_overwrite(elem2, j); +#endif ts->elems[j++] = elem2; } @@ -93,10 +105,11 @@ static void table_gset_resize(TableGSet *ts) bool BLI_table_gset_add(TableGSet *ts, void *elem) { - void **val; - table_gset_resize(ts); +#ifdef USE_TGSET_SMALLHASH + void **val; + bool ret = BLI_smallhash_ensure_p(PTR_TO_IDX(ts), (uintptr_t)elem, &val); if (!ret) { @@ -107,13 +120,27 @@ bool BLI_table_gset_add(TableGSet *ts, void *elem) } return ret; +#else + auto createfn = [&](int *value) { + *value = ts->cur; + ts->elems[ts->cur++] = elem; + return true; + }; + auto modifyfn = [&](int *value) { return false; }; + + return PTR_TO_IDX(ts)->add_or_modify(elem, createfn, modifyfn); +#endif } void BLI_table_gset_insert(TableGSet *ts, void *elem) { table_gset_resize(ts); +#ifdef USE_TGSET_SMALLHASH BLI_smallhash_insert(PTR_TO_IDX(ts), (uintptr_t)elem, (void *)ts->cur); +#else + PTR_TO_IDX(ts)->add(elem, ts->cur); +#endif ts->elems[ts->cur++] = elem; ts->length++; @@ -125,26 +152,39 @@ void BLI_table_gset_remove(TableGSet *ts, void *elem, GHashKeyFreeFP freefp) return; } +#ifdef USE_TGSET_SMALLHASH int *idx = (int *)BLI_smallhash_lookup_p(PTR_TO_IDX(ts), (uintptr_t)elem); if (!idx) { return; } - int idx2 = *idx; - BLI_smallhash_remove(PTR_TO_IDX(ts), (uintptr_t)elem); +#else + int *idx = PTR_TO_IDX(ts)->lookup_ptr(elem); + if (!idx) { + return; + } + + PTR_TO_IDX(ts)->remove(elem); +#endif + + int idx2 = *idx; if (!ts->elems || ts->elems[idx2] != elem) { return; } ts->length--; - ts->elems[idx2] = NULL; + ts->elems[idx2] = nullptr; } bool BLI_table_gset_haskey(TableGSet *ts, void *elem) { +#ifdef USE_TGSET_SMALLHASH return BLI_smallhash_haskey(PTR_TO_IDX(ts), (uintptr_t)elem); +#else + return PTR_TO_IDX(ts)->contains(elem); +#endif } int BLI_table_gset_len(TableGSet *ts) diff --git a/source/blender/blenlib/intern/heap_minmax.c b/source/blender/blenlib/intern/heap_minmax.c deleted file mode 100644 index 4367ac0dff1..00000000000 --- a/source/blender/blenlib/intern/heap_minmax.c +++ /dev/null @@ -1,642 +0,0 @@ -#include "MEM_guardedalloc.h" - -#include "BLI_heap_minmax.h" -#include "BLI_mempool.h" -#include "BLI_utildefines.h" - -#include "BLI_compiler_attrs.h" -#include "BLI_compiler_compat.h" - -typedef struct MinMaxHeapNode { - void *ptr; - float value; - int child1, child2, parent; -} MinMaxHeapNode; - -typedef struct MinMaxHeap { - MinMaxHeapNode *nodes; - int totnode, reserved; -} MinMaxHeap; - -static void heap_reserve(MinMaxHeap *heap, unsigned int n) -{ - if (heap->reserved >= n) { - return; - } - - n++; - n += n >> 1; - - if (heap->nodes) { - heap->nodes = MEM_recallocN(heap->nodes, sizeof(MinMaxHeapNode) * n); - } - else { - heap->nodes = MEM_calloc_arrayN(n, sizeof(MinMaxHeapNode), "MinMaxHeap nodes"); - } - - heap->reserved = n; -} - -/** - * Creates a new heap. Removed nodes are recycled, so memory usage will not shrink. - * - * \note Use when the size of the heap is known in advance. - */ -MinMaxHeap *BLI_mm_heap_new_ex(unsigned int tot_reserve) -{ - MinMaxHeap *heap = MEM_callocN(sizeof(*heap), __func__); - if (tot_reserve) { - heap_reserve(heap, tot_reserve); - } - - return heap; -} - -MinMaxHeap *BLI_mm_heap_new() -{ - return BLI_mm_heap_new_ex(256); -} - -void BLI_mm_heap_clear(MinMaxHeap *heap, MinMaxHeapFreeFP ptrfreefp) -{ - if (ptrfreefp) { - for (int i = 0; i < heap->totnode; i++) { - ptrfreefp(heap->nodes[i].ptr); - } - } - - MEM_SAFE_FREE(heap->nodes); - heap->totnode = 0; -} - -void BLI_mm_heap_free(MinMaxHeap *heap, MinMaxHeapFreeFP ptrfreefp) -{ - BLI_mm_heap_clear(heap, ptrfreefp); - MEM_freeN(heap); -} - -static MinMaxHeapNode *heap_make_node(MinMaxHeap *heap) -{ - heap_reserve(heap, heap->totnode + 1); - - MinMaxHeapNode *node = heap->nodes + heap->totnode; - node->child1 = node->child2 = node->parent = -1; - - heap->totnode++; - - return node; -} - -static MinMaxHeapNode *heap_descent_min2(MinMaxHeap *heap, MinMaxHeapNode *n) -{ - if (n->child1 != -1 && n->child2 != -1) { - MinMaxHeapNode *n1 = heap->nodes + n->child1; - MinMaxHeapNode *n2 = heap->nodes + n->child2; - - return n1->value < n2->value ? n1 : n2; - } - else if (n->child1 != -1) { - return heap->nodes + n->child1; - } - else if (n->child2 != -1) { - return heap->nodes + n->child2; - } - - return n; -} - -/* find node grandchild */ -static MinMaxHeapNode *heap_descent_min(MinMaxHeap *heap, MinMaxHeapNode *node) -{ - if (node->child1 != -1 && node->child2 != -1) { - MinMaxHeapNode *n1 = heap->nodes + node->child1; - MinMaxHeapNode *n2 = heap->nodes + node->child2; - - n1 = heap_descent_min2(heap, n1); - n2 = heap_descent_min2(heap, n2); - - return n1->value < n2->value ? n1 : n2; - } - else if (node->child1 != -1) { - return heap_descent_min2(heap, heap->nodes + node->child1); - } - else if (node->child2 != -1) { - return heap_descent_min2(heap, heap->nodes + node->child2); - } - - return NULL; -} - -static MinMaxHeapNode *heap_descent_max2(MinMaxHeap *heap, MinMaxHeapNode *n) -{ - if (n->child1 != -1 && n->child2 != -1) { - MinMaxHeapNode *n1 = heap->nodes + n->child1; - MinMaxHeapNode *n2 = heap->nodes + n->child2; - - return n1->value > n2->value ? n1 : n2; - } - else if (n->child1 != -1) { - return heap->nodes + n->child1; - } - else if (n->child2 != -1) { - return heap->nodes + n->child2; - } - - return n; -} - -/* find node grandchild */ -static MinMaxHeapNode *heap_descent_max(MinMaxHeap *heap, MinMaxHeapNode *node) -{ - if (node->child1 != -1 && node->child2 != -1) { - MinMaxHeapNode *n1 = heap->nodes + node->child1; - MinMaxHeapNode *n2 = heap->nodes + node->child2; - - n1 = heap_descent_max2(heap, n1); - n2 = heap_descent_max2(heap, n2); - - return n1->value > n2->value ? n1 : n2; - } - else if (node->child1 != -1) { - return heap_descent_max2(heap, heap->nodes + node->child1); - } - else if (node->child2 != -1) { - return heap_descent_max2(heap, heap->nodes + node->child2); - } - - return NULL; -} - -static MinMaxHeapNode *heap_push_down_min(MinMaxHeap *heap, MinMaxHeapNode *node) -{ - MinMaxHeapNode *ret = NULL; - - while (node->child1 >= 0 || node->child2 >= 0) { - MinMaxHeapNode *node2 = heap_descent_min(heap, node); - - if (!node2) { - break; - } - - if (node2->value < node->value) { - SWAP(float, node2->value, node->value); - SWAP(void *, node2->ptr, node->ptr); - - if (node2->parent != node - heap->nodes) { - MinMaxHeapNode *parent = heap->nodes + node2->parent; - - if (node2->value > parent->value) { - SWAP(float, node2->value, parent->value); - SWAP(void *, node2->ptr, parent->ptr); - - /* this is a bit tricky, our return node has now - moved into the other interleaved heap side */ - if (!ret) { - ret = parent; - } - } - } - - node = node2; - } - else { - break; - } - } - - return ret ? ret : node; -} - -static MinMaxHeapNode *heap_push_down_max(MinMaxHeap *heap, MinMaxHeapNode *node) -{ - MinMaxHeapNode *ret = NULL; - - while (node->child1 >= 0 || node->child2 >= 0) { - MinMaxHeapNode *node2 = heap_descent_max(heap, node); - - if (node2->value > node->value) { - SWAP(float, node2->value, node->value); - SWAP(void *, node2->ptr, node->ptr); - - if (node2->parent != node - heap->nodes) { - MinMaxHeapNode *parent = heap->nodes + node2->parent; - - if (node2->value < parent->value) { - SWAP(float, node2->value, parent->value); - SWAP(void *, node2->ptr, parent->ptr); - - /* this is a bit tricky, our return node has now - moved into the other interleaved heap side */ - if (!ret) { - ret = parent; - } - } - } - - node = node2; - } - else { - break; - } - } - - return ret ? ret : node; -} - -static int heap_get_level(const MinMaxHeap *heap, const MinMaxHeapNode *node) -{ - int i = 0; - - while (node->parent != -1) { - node = heap->nodes + node->parent; - i++; - } - - return i; -} - -static MinMaxHeapNode *heap_push_down(MinMaxHeap *heap, MinMaxHeapNode *node) -{ - int i = heap_get_level(heap, node); - - if (i & 1) { - return heap_push_down_max(heap, node); - } - else { - return heap_push_down_min(heap, node); - } -} - -static MinMaxHeapNode *heap_push_up_min(MinMaxHeap *heap, MinMaxHeapNode *node) -{ - while (node->parent != -1) { - MinMaxHeapNode *parent = heap->nodes + node->parent; - - if (parent->parent != -1 && node->value < heap->nodes[parent->parent].value) { - parent = heap->nodes + parent->parent; - - SWAP(float, node->value, parent->value); - SWAP(void *, node->ptr, parent->ptr); - - node = parent; - } - else { - break; - } - } - - return node; -} - -static MinMaxHeapNode *heap_push_up_max(MinMaxHeap *heap, MinMaxHeapNode *node) -{ - while (node->parent != -1) { - MinMaxHeapNode *parent = heap->nodes + node->parent; - - if (parent->parent != -1 && node->value > heap->nodes[parent->parent].value) { - parent = heap->nodes + parent->parent; - - SWAP(float, node->value, parent->value); - SWAP(void *, node->ptr, parent->ptr); - - node = parent; - } - else { - break; - } - } - - return node; -} - -static MinMaxHeapNode *heap_push_up(MinMaxHeap *heap, MinMaxHeapNode *node) -{ - int i = heap_get_level(heap, node); - - if ((i & 1) == 0) { - MinMaxHeapNode *parent = heap->nodes + node->parent; - - if (node->value > parent->value) { - SWAP(float, node->value, parent->value); - SWAP(void *, node->ptr, parent->ptr); - - return heap_push_up_max(heap, parent); - } - else { - return heap_push_up_min(heap, node); - } - } - else { - MinMaxHeapNode *parent = heap->nodes + node->parent; - - if (node->value < parent->value) { - SWAP(float, node->value, parent->value); - SWAP(void *, node->ptr, parent->ptr); - - return heap_push_up_min(heap, parent); - } - else { - return heap_push_up_max(heap, node); - } - } - - return node; -} - -/** - * Insert heap node with a value (often a 'cost') and pointer into the heap, - * duplicate values are allowed. - */ -MinMaxHeapNode *BLI_mm_heap_insert(MinMaxHeap *heap, float value, void *ptr) -{ - MinMaxHeapNode *node = heap_make_node(heap); - - node->ptr = ptr; - node->value = value; - - if (heap->totnode == 1) { - return node; - } - - int i = node - heap->nodes; - - node->parent = (i - 1) >> 1; - - MinMaxHeapNode *parent = heap->nodes + node->parent; - if (parent->child1 == -1) { - parent->child1 = i; - } - else { - parent->child2 = i; - } - - return heap_push_up(heap, node); -} - -/** - * Convenience function since this is a common pattern. - */ -void BLI_mm_heap_insert_or_update(MinMaxHeap *heap, - MinMaxHeapNode **node_p, - float value, - void *ptr) -{ - MinMaxHeapNode *node = *node_p; - - if (!node) { - *node_p = BLI_mm_heap_insert(heap, value, ptr); - return; - } - - node = heap_push_down(heap, node); - node = heap_push_up(heap, node); - - *node_p = node; -} - -bool BLI_mm_heap_is_empty(const MinMaxHeap *heap) -{ - return heap->totnode == 0; -} - -unsigned int BLI_mm_heap_len(const MinMaxHeap *heap) -{ - return heap->totnode; -} - -MinMaxHeapNode *BLI_mm_heap_min(const MinMaxHeap *heap) -{ - return heap->nodes; -} - -float BLI_mm_heap_min_value(const MinMaxHeap *heap) -{ - return heap->nodes[0].value; -} - -MinMaxHeapNode *BLI_mm_heap_max(const MinMaxHeap *heap) -{ - if (heap->totnode == 1) { - return heap->nodes; - } - - if (heap->nodes[0].child1 != -1) { - return heap->nodes + heap->nodes[0].child1; - } - else { - return heap->nodes + heap->nodes[0].child2; - } -} - -float BLI_mm_heap_max_value(const MinMaxHeap *heap) -{ - return BLI_mm_heap_max(heap)->value; -} - -static MinMaxHeapNode *heap_pop_last(MinMaxHeap *heap) -{ - MinMaxHeapNode *last = heap->nodes + heap->totnode - 1; - - if (last->parent) { - MinMaxHeapNode *parent = heap->nodes + last->parent; - if (parent->child1 == heap->totnode - 1) { - parent->child1 = -1; - } - else { - parent->child2 = -1; - } - } - - heap->totnode--; - - return last; -} - -void *BLI_mm_heap_pop_min(MinMaxHeap *heap) -{ - void *ret = heap->nodes[0].ptr; - - if (heap->totnode == 1) { - heap->totnode--; - return ret; - } - -#ifdef BLI_MINMAX_HEAP_VALIDATE - if (!BLI_mm_heap_is_valid(heap)) { - printf("invalid heap!\n"); - } -#endif - - MinMaxHeapNode *last = heap_pop_last(heap); - - SWAP(float, last->value, heap->nodes->value); - SWAP(void *, last->ptr, heap->nodes->ptr); - - heap_push_down(heap, heap->nodes); - -#ifdef BLI_MINMAX_HEAP_VALIDATE - if (!BLI_mm_heap_is_valid(heap)) { - printf("invalid heap!\n"); - } -#endif - - return ret; -} - -void *BLI_mm_heap_pop_max(MinMaxHeap *heap) -{ - MinMaxHeapNode *node = BLI_mm_heap_max(heap); - - void *ret = node->ptr; - - if (heap->totnode == 1) { - heap->totnode--; - return ret; - } - -#ifdef BLI_MINMAX_HEAP_VALIDATE - if (!BLI_mm_heap_is_valid(heap)) { - printf("invalid heap!\n"); - } -#endif - - MinMaxHeapNode *last = heap_pop_last(heap); - - node->value = last->value; - node->ptr = last->ptr; - - // node = heap_push_up(heap, node); - node = heap_push_down(heap, node); - // heap_push_down(heap, heap->nodes); - -#if 0 - if (heap->nodes[0].child2 != -1 && heap->nodes[0].child1 != 0) { - MinMaxHeapNode *n1 = heap->nodes + heap->nodes[0].child1; - MinMaxHeapNode *n2 = heap->nodes + heap->nodes[0].child2; - - if (n1->value < n2->value) { - SWAP(float, n1->value, n2->value); - SWAP(void *, n1->ptr, n2->ptr); - - heap_push_down(heap, n1); - heap_push_down(heap, n2); - } - } -#endif - -#ifdef BLI_MINMAX_HEAP_VALIDATE - if (!BLI_mm_heap_is_valid(heap)) { - printf("invalid heap!\n"); - } -#endif - - return ret; -} - -MinMaxHeapNode *BLI_mm_heap_node_value_update(MinMaxHeap *heap, MinMaxHeapNode *node, float value) -{ - node->value = value; - - node = heap_push_down(heap, node); - node = heap_push_up(heap, node); - - return node; -} - -MinMaxHeapNode *BLI_mm_heap_node_value_update_ptr(MinMaxHeap *heap, - MinMaxHeapNode *node, - float value, - void *ptr) -{ - node->value = value; - node->ptr = ptr; - - node = heap_push_down(heap, node); - node = heap_push_up(heap, node); - - return node; -} - -/** - * Return the value or pointer of a heap node. - */ -float BLI_mm_heap_node_value(const MinMaxHeapNode *node) -{ - return node->value; -} - -void *BLI_mm_heap_node_ptr(const MinMaxHeapNode *node) -{ - return node->ptr; -} -/** - * Only for checking internal errors (gtest). - */ -bool BLI_mm_heap_is_valid(const MinMaxHeap *heap) -{ - for (int i = 0; i < heap->totnode; i++) { - MinMaxHeapNode *node = heap->nodes + i; - - int level = heap_get_level(heap, node); - - if (i > 0 && (node->parent < 0 || node->parent >= heap->totnode)) { - return false; - } - - if (i < 3) { - continue; - } - - MinMaxHeapNode *parent = heap->nodes + node->parent; - - if (parent->parent < 0 || parent->parent >= heap->totnode) { - return false; - } - - parent = heap->nodes + parent->parent; - bool test = parent->value < node->value; - - if (parent->value == node->value) { - continue; - } - - test = test ^ (level & 1); - - if (!test) { - return false; - } - } - - return true; -} - -#include "BLI_rand.h" - -void test_mm_heap() -{ - MinMaxHeap *heap = BLI_mm_heap_new(); - RNG *rng = BLI_rng_new(0); - const int steps = 1024; - void *ptr = NULL; - - for (int i = 0; i < steps; i++) { - float f = floorf(BLI_rng_get_float(rng) * 10.0); - - BLI_mm_heap_insert(heap, f, ptr); - } - - for (int i = 0; i < steps; i++) { - if (i & 1) { - BLI_mm_heap_pop_max(heap); - } - else { - BLI_mm_heap_pop_min(heap); - } - - BLI_mm_heap_is_valid(heap); - } - - BLI_rng_free(rng); - BLI_mm_heap_is_valid(heap); - BLI_mm_heap_free(heap, NULL); -} diff --git a/source/blender/blenloader/intern/versioning_300.cc b/source/blender/blenloader/intern/versioning_300.cc index 53da40639b2..e4f4a1ad3b2 100644 --- a/source/blender/blenloader/intern/versioning_300.cc +++ b/source/blender/blenloader/intern/versioning_300.cc @@ -4271,6 +4271,23 @@ void blo_do_versions_300(FileData *fd, Library * /*lib*/, Main *bmain) } } + /* Delete any mesh id layers from old versions of sculpt-dev. */ + if (!MAIN_VERSION_ATLEAST(bmain, 306, 1)) { + LISTBASE_FOREACH (Mesh *, mesh, &bmain->meshes) { + CustomData *data = &mesh->vdata; + + for (int i = 0; i < 4; i++, data++) { + for (int j = 0; j < data->totlayer; j++) { + /* CD_DYNTOPO_VERT used to be CD_MESH_ID. */ + if (data->layers[j].type == CD_DYNTOPO_VERT) { + CustomData_free_layer(data, CD_DYNTOPO_VERT, 0, 0); + j--; + } + } + } + } + } + /** * Versioning code until next subversion bump goes here. * diff --git a/source/blender/bmesh/CMakeLists.txt b/source/blender/bmesh/CMakeLists.txt index ae3882e7fd7..696041d96e2 100644 --- a/source/blender/bmesh/CMakeLists.txt +++ b/source/blender/bmesh/CMakeLists.txt @@ -67,8 +67,6 @@ set(SRC intern/bmesh_construct.h intern/bmesh_core.c intern/bmesh_core.h - intern/bmesh_data_attr.h - intern/bmesh_data_attr.cc intern/bmesh_delete.c intern/bmesh_delete.h intern/bmesh_edgeloop.c @@ -180,7 +178,7 @@ set(LIB bf_blenlib extern_rangetree ) - + if(WITH_BULLET) list(APPEND INC_SYS ${BULLET_INCLUDE_DIRS} diff --git a/source/blender/bmesh/intern/bmesh_construct.c b/source/blender/bmesh/intern/bmesh_construct.c index 00fa6696a0f..51c8393096a 100644 --- a/source/blender/bmesh/intern/bmesh_construct.c +++ b/source/blender/bmesh/intern/bmesh_construct.c @@ -506,28 +506,28 @@ void BM_elem_attrs_copy_ex(BMesh *bm_src, bm_dst, (const BMVert *)ele_src, (BMVert *)ele_dst, - cd_mask_exclude | CD_MASK_MESH_ID | CD_MASK_TOOLFLAGS); + cd_mask_exclude | CD_MASK_TOOLFLAGS); break; case BM_EDGE: bm_edge_attrs_copy(bm_src, bm_dst, (const BMEdge *)ele_src, (BMEdge *)ele_dst, - cd_mask_exclude | CD_MASK_MESH_ID | CD_MASK_TOOLFLAGS); + cd_mask_exclude | CD_MASK_TOOLFLAGS); break; case BM_LOOP: bm_loop_attrs_copy(bm_src, bm_dst, (const BMLoop *)ele_src, (BMLoop *)ele_dst, - cd_mask_exclude | CD_MASK_MESH_ID | CD_MASK_TOOLFLAGS); + cd_mask_exclude | CD_MASK_TOOLFLAGS); break; case BM_FACE: bm_face_attrs_copy(bm_src, bm_dst, (const BMFace *)ele_src, (BMFace *)ele_dst, - cd_mask_exclude | CD_MASK_MESH_ID | CD_MASK_TOOLFLAGS); + cd_mask_exclude | CD_MASK_TOOLFLAGS); break; default: BLI_assert(0); @@ -657,14 +657,10 @@ void BM_mesh_copy_init_customdata(BMesh *bm_dst, BMesh *bm_src, const BMAllocTem allocsize = &bm_mesh_allocsize_default; } - CustomData_copy( - &bm_src->vdata, &bm_dst->vdata, CD_MASK_BMESH.vmask | CD_MASK_MESH_ID, CD_SET_DEFAULT, 0); - CustomData_copy( - &bm_src->edata, &bm_dst->edata, CD_MASK_BMESH.emask | CD_MASK_MESH_ID, CD_SET_DEFAULT, 0); - CustomData_copy( - &bm_src->ldata, &bm_dst->ldata, CD_MASK_BMESH.lmask | CD_MASK_MESH_ID, CD_SET_DEFAULT, 0); - CustomData_copy( - &bm_src->pdata, &bm_dst->pdata, CD_MASK_BMESH.pmask | CD_MASK_MESH_ID, CD_SET_DEFAULT, 0); + CustomData_copy(&bm_src->vdata, &bm_dst->vdata, CD_MASK_BMESH.vmask, CD_SET_DEFAULT, 0); + CustomData_copy(&bm_src->edata, &bm_dst->edata, CD_MASK_BMESH.emask, CD_SET_DEFAULT, 0); + CustomData_copy(&bm_src->ldata, &bm_dst->ldata, CD_MASK_BMESH.lmask, CD_SET_DEFAULT, 0); + CustomData_copy(&bm_src->pdata, &bm_dst->pdata, CD_MASK_BMESH.pmask, CD_SET_DEFAULT, 0); CustomData *srcdatas[4] = {&bm_src->vdata, &bm_src->edata, &bm_src->ldata, &bm_src->pdata}; CustomData *dstdatas[4] = {&bm_dst->vdata, &bm_dst->edata, &bm_dst->ldata, &bm_dst->pdata}; diff --git a/source/blender/bmesh/intern/bmesh_data_attr.cc b/source/blender/bmesh/intern/bmesh_data_attr.cc deleted file mode 100644 index ecfdf8dc145..00000000000 --- a/source/blender/bmesh/intern/bmesh_data_attr.cc +++ /dev/null @@ -1,387 +0,0 @@ -#include "DNA_customdata_types.h" - -#include "BLI_index_range.hh" -#include "BLI_mempool.h" - -#include "BKE_customdata.h" - -#include "bmesh.h" -#include "bmesh_data_attr.h" - -#ifdef USE_BMESH_PAGE_CUSTOMDATA - -using blender::IndexRange; -using blender::bmesh::BMAttrDomain; -using blender::bmesh::PageArray; -using blender::bmesh::PageElemRef; - -namespace blender { -namespace bmesh { - -BMeshAttrList *BMAttr_new() -{ - BMeshAttrList *list = static_cast( - MEM_callocN(sizeof(BMeshAttrList), "BMeshAttrList")); - - for (int i : IndexRange(ATTR_DOMAIN_NUM)) { - list->domains[i] = new BMAttrDomain(static_cast(i)); - } - - return list; -} - -void BMAttr_reset(BMeshAttrList *list) -{ - if (list->arrays) { - MEM_freeN(static_cast(list->arrays)); - } - - list->arrays = nullptr; - list->totarray = 0; - - for (int i : IndexRange(ATTR_DOMAIN_NUM)) { - delete list->domains[i]; - } - - for (int i : IndexRange(ATTR_DOMAIN_NUM)) { - list->domains[i] = new BMAttrDomain(static_cast(i)); - } -} - -void BMAttr_free(BMeshAttrList *list) -{ - for (int i : IndexRange(ATTR_DOMAIN_NUM)) { - delete list->domains[i]; - } - - if (list->arrays) { - MEM_freeN(static_cast(list->arrays)); - } - - MEM_freeN(static_cast(list)); -} - -static void bm_update_page_pointers(BMeshAttrList *list) -{ - for (int i : IndexRange(list->totarray)) { - PageArray *page_array = reinterpret_cast *>( - list->arrays[i]); - list->arrays[i]->pages = page_array->pages.data(); - } -} - -int BMAttr_allocElem(BMeshAttrList *list, eAttrDomain domain) -{ - bool hadNewPage; - - int ret = static_cast(list->domains[domain]->alloc(hadNewPage)); - - if (hadNewPage) { - bm_update_page_pointers(list); - } - - return ret; -} - -void BMAttr_freeElem(BMeshAttrList *list, eAttrDomain domain, int elem) -{ - bool pageRemoved; - - list->domains[domain]->free(static_cast(elem), pageRemoved); - - if (pageRemoved) { - bm_update_page_pointers(list); - } -} - -int BMAttr_addLayer(BMeshAttrList *list, eAttrDomain domain, eCustomDataType type) -{ - list->totarray++; - - if (!list->arrays) { - list->arrays = static_cast( - MEM_malloc_arrayN(list->totarray, sizeof(void *), __func__)); - } - else { - list->arrays = static_cast( - MEM_reallocN(static_cast(list->arrays), list->totarray * sizeof(void *))); - } - - BMeshPageArray *bmarray = list->arrays[list->totarray - 1]; - PageArray *page_array = list->domains[domain]->addLayer(type); - - bmarray->cppClass = reinterpret_cast(page_array); - bmarray->pages = page_array->pages.data(); - - for (PageElemRef i : IndexRange(list->domains[domain]->totalloc)) { - page_array->setDefault(i); - } - - return list->totarray - 1; -} - -void BMAttr_init(BMesh *bm) -{ -# ifdef USE_BMESH_PAGE_CUSTOMDATA - CustomData *domains[ATTR_DOMAIN_NUM] = {nullptr}; - - domains[ATTR_DOMAIN_POINT] = &bm->vdata; - domains[ATTR_DOMAIN_EDGE] = &bm->edata; - domains[ATTR_DOMAIN_CORNER] = &bm->ldata; - domains[ATTR_DOMAIN_FACE] = &bm->pdata; - - if (!bm->attr_list) { - bm->attr_list = BMAttr_new(); - } - - BMAttr_fromCData(bm->attr_list, domains); -# endif -} - -void BMAttr_fromCData(BMeshAttrList *list, CustomData *domains[ATTR_DOMAIN_NUM]) -{ - eAttrDomain ds[4] = {ATTR_DOMAIN_POINT, ATTR_DOMAIN_EDGE, ATTR_DOMAIN_CORNER, ATTR_DOMAIN_FACE}; - - for (int i : IndexRange(4)) { - CustomData *cdata = domains[ds[i]]; - - cdata->bm_attrs = static_cast(list); - cdata->_pad[0] = static_cast(domains[ds[i]]); - - for (int j : IndexRange(cdata->totlayer)) { - CustomDataLayer *layer = cdata->layers + j; - - layer->offset = BMAttr_addLayer(list, ds[i], static_cast(layer->type)); - } - } -} - -static eAttrDomain domain_map[] = { - ATTR_DOMAIN_AUTO, // 0 - ATTR_DOMAIN_POINT, // 1 - ATTR_DOMAIN_EDGE, // 2 - ATTR_DOMAIN_AUTO, // 3 - ATTR_DOMAIN_CORNER, // 4 - ATTR_DOMAIN_AUTO, // 5 - ATTR_DOMAIN_AUTO, // 6 - ATTR_DOMAIN_AUTO, // 7 - ATTR_DOMAIN_FACE, // 8 -}; - -void bmesh_update_attr_refs(BMesh *bm) -{ - CustomData *cdata = &bm->vdata; - - for (int i = 0; i < 4; i++, cdata++) { - cdata->_pad[0] = static_cast(domain_map[htype]); - cdata->bm_attrs = bm->attr_list; - } -} - -void CustomData_bmesh_init_pool(CustomData *data, int totelem, const char htype) -{ - CustomData_bmesh_init_pool_ex(data, totelem, htype, __func__); -} - -void CustomData_bmesh_init_pool_ex(CustomData *data, - int totelem, - const char htype, - const char *memtag) -{ - // store domain in _pad[0] for the purposes of this test - data->_pad[0] = static_cast(domain_map[htype]); - - if (data->pool) { - BLI_mempool_destroy(data->pool); - } - - data->pool = BLI_mempool_create(sizeof(BMeshPageRef), 0, 1024, 0); -} - -static void CustomData_bmesh_alloc_block(CustomData *data, void **block) -{ - BMeshAttrList *list = static_cast(data->bm_attrs); - BMeshPageRef *ref = static_cast(BLI_mempool_calloc(data->pool)); - - eAttrDomain domain = static_cast(POINTER_AS_UINT(data->_pad[0])); - - ref->attrs = list; - ref->idx = BMAttr_allocElem(list, domain); - ref->domain = domain; - - *block = static_cast(ref); -} - -static void CustomData_bmesh_set_default_n(CustomData *data, void **block, int n) -{ - if (ELEM(data->layers[n].type, CD_TOOLFLAGS, CD_MESH_ID)) { - /* do not do toolflags or mesh ids */ - return; - } - - BMeshAttrList *list = static_cast(data->bm_attrs); - BMeshPageRef *ref = static_cast(*block); - - list->domains[ref->domain]->arrays[n]->setDefault(static_cast(ref->idx)); -} - -static void CustomData_bmesh_set_default(CustomData *data, void **block) -{ - if (!*block) { - CustomData_bmesh_alloc_block(data, block, n); - } - - BMeshAttrList *list = static_cast(data->bm_attrs); - BMeshPageRef *ref = static_cast(*block); - eAttrDomain domain = static_cast(POINTER_AS_UINT(data->_pad[0])); - - list->domains[domain].setDefault(static_cast(ref->idx)); -} - -static void alloc_block(CustomData *data, BMeshAttrList *list, eAttrDomain domain, void **block) -{ - CustomData_bmesh_alloc_block(data, block); - BMeshPageRef *ref = static_cast * block; - - ref->attrs = list; - ref->idx = BMAttr_allocElem(list, domain); -} - -void CustomData_bmesh_interp(CustomData *data, - const void **src_blocks, - const float *weights, - const float *sub_weights, - int count, - void *dst_block) -{ - BMeshPageRef *ref = static_cast dst_block; - eAttrDomain domain = static_cast(ref->domain); - - PageElemRef *elems = BLI_array_alloca(elems, count); - - for (int i : IndexRange(count)) { - BMeshPageRef *ref2 = static_cast src_blocks[i]; - elems[i] = ref2->idx; - } - - ref->attrs->domains[domain]->interp( - static_cast(ref->idx), count, elems, weights, sub_weights); -} - -void CustomData_to_bmesh_block(const CustomData *source, - CustomData *_dest, - int src_index, - void **dest_block, - bool use_default_init) -{ - if (*dest_block == nullptr) { - CustomData_bmesh_alloc_block(dest, dest_block); - } - - BMeshPageRef *block = static_cast dest_block; - BMeshAttrList *list = static_cast dest->bm_attrs; - auto *dest = list->domains[ref->domain]; - - /* copies a layer at a time */ - int dest_i = 0; - for (int src_i = 0; src_i < source->totlayer; src_i++) { - auto *array = list->domains[ref->domain]->arrays[dest_i]; - - /* find the first dest layer with type >= the source type - * (this should work because layers are ordered by type) - */ - while (dest_i < dest->arrays.size() && - dest->arrays[dest_i].type < source->layers[src_i].type) { - if (use_default_init) { - CustomData_bmesh_set_default_n(dest, dest_block, dest_i); - } - dest_i++; - } - - /* if there are no more dest layers, we're done */ - if (dest_i >= dest->arrays.size()) { - break; - } - - /* if we found a matching layer, copy the data */ - if (dest->arrays[dest_i].type == source->layers[src_i].type) { - const void *src_data = source->layers[src_i].data; - void *dest_data = dest->getElemPtr(static_cast(ref->idx)); - - const LayerTypeInfo *typeInfo = layerType_getInfo(dest->arrays[dest_i].type); - const size_t src_offset = (size_t)src_index * typeInfo->size; - - if (typeInfo->copy) { - typeInfo->copy(POINTER_OFFSET(src_data, src_offset), dest_data, 1); - } - else { - memcpy(dest_data, POINTER_OFFSET(src_data, src_offset), typeInfo->size); - } - - /* 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++; - } - } - - if (use_default_init) { - while (dest_i < dest->arrays.size()) { - CustomData_bmesh_set_default_n(dest, dest_block, dest_i); - dest_i++; - } - } -} - -void CustomData_from_bmesh_block(const CustomData *source, - CustomData *dest, - void *src_block, - int dest_index) -{ - /* copies a layer at a time */ - int dest_i = 0; - for (int src_i = 0; src_i < source->totlayer; src_i++) { - if (source->layers[src_i].flag & CD_FLAG_NOCOPY) { - continue; - } - - /* 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 < source->layers[src_i].type) { - dest_i++; - } - - /* if there are no more dest layers, we're done */ - if (dest_i >= dest->totlayer) { - return; - } - - /* if we found a matching layer, copy the data */ - if (dest->layers[dest_i].type == source->layers[src_i].type) { - const LayerTypeInfo *typeInfo = layerType_getInfo(dest->layers[dest_i].type); - int offset = source->layers[src_i].offset; - const void *src_data = POINTER_OFFSET(src_block, offset); - void *dst_data = POINTER_OFFSET(dest->layers[dest_i].data, - (size_t)dest_index * typeInfo->size); - - if (typeInfo->copy) { - typeInfo->copy(src_data, dst_data, 1); - } - else { - memcpy(dst_data, src_data, typeInfo->size); - } - - /* 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++; - } - } -} - -} // namespace bmesh -} // namespace blender -#endif diff --git a/source/blender/bmesh/intern/bmesh_data_attr.h b/source/blender/bmesh/intern/bmesh_data_attr.h deleted file mode 100644 index f10c1939f94..00000000000 --- a/source/blender/bmesh/intern/bmesh_data_attr.h +++ /dev/null @@ -1,275 +0,0 @@ -#pragma once - -/* - -This is a minimal page-based CustomData backend for bmesh. -It's purpose is to test whether a page-based system would -be faster then the current block-based one. - -The idea is to plug into the existing API in as minimal -a way as possible. - -*/ - -#include "BKE_attribute.h" -#include "bmesh.h" - -#ifdef USE_BMESH_PAGE_CUSTOMDATA - -# define BM_PAGE_SHIFT 10 -# define BM_PAGE_SIZE (1 << BM_PAGE_SHIFT) -# define BM_PAGE_MASK (BM_PAGE_SIZE - 1) - -# ifdef __cplusplus - -# include "BLI_alloca.h" -# include "BLI_array.hh" -# include - -namespace blender { -namespace bmesh { - -using PageElemRef = int; - -template struct PageArray { - std::vector pages; - size_t elemSize; - eCustomDataType type; - - PageArray(const PageArray &b) - { - elemSize = b.elemSize; - pages = b.pages; - } - - PageArray(const PageArray &&b) - { - elemSize = b.elemSize; - pages = std::move(b.pages); - } - - PageArray(eCustomDataType t, size_t size = 0) : type(t) - { - elemSize = CustomData_getTypeSize(type); - - reserve(size); - } - - ~PageArray() - { - } - - void *getElemPtr(PageElemRef elem) - { - size_t page = elem >> PageSizeShift; - size_t elem_i = elem & ((1 << PageSizeShift) - 1); - - char *ptr = static_cast(pages[page]); - ptr += elem_i * elemSize; - - return static_cast(ptr); - } - - void interp(PageElemRef elem, int count, PageElemRef *srcs, float *ws, float *sub_ws) - { - void **blocks = static_cast(BLI_array_alloca(blocks, count)); - - for (int i : IndexRange(count)) { - blocks[i] = getElemPtr(srcs[i]); - } - - CustomData_interpData(type, getElemPtr(elem), count, (const void **)blocks, ws, sub_ws); - } - - void free(PageElemRef elem) - { - CustomData_freeData(type, getElemPtr(elem), 1); - } - - void setDefault(PageElemRef elem) - { - CustomData_setDefaultData(type, getElemPtr(elem), 1); - } - - void reserve(size_t size) - { - size_t totpage = size >> PageSizeShift; - // const size_t pagesize = 1ULL << PageSizeShift; - - int curpage = pages.size(); - pages.resize(totpage); - - for (int i = curpage; i < totpage; i++) { - newPage(); - } - } - - void newPage() - { - const int pagesize = 1 << PageSizeShift; - - pages.resize(pages.size() + 1); - pages[pages.size() - 1] = MEM_malloc_arrayN(pagesize, elemSize, "bmesh attribute page"); - } -}; - -struct BMAttrDomain { - std::vector freelist; - std::vector *> arrays; - - eAttrDomain domain; - int totpage; - int totelem; - int totalloc; - - BMAttrDomain(eAttrDomain d) : domain(d), totpage(0), totelem(0) - { - } - - ~BMAttrDomain() - { - for (PageArray *array : arrays) { - delete array; - } - } - - void interp(PageElemRef elem, int count, PageElemRef *srcs, float *ws, float *sub_ws) - { - for (PageArray *array : arrays) { - array->interp(elem, count, srcs, ws, sub_ws); - } - } - - PageArray *addLayer(eCustomDataType type) - { - PageArray *array = new PageArray(type); - array->reserve(totelem); - - /* keep layers ordered by type */ - - bool state = false; - for (auto iter = arrays.begin(); iter != arrays.end(); ++iter) { - if ((*iter)->type == type) { - state = true; - // now find next layer with wrong type, we - // will insert before it. - } - else if (state) { - arrays.insert(iter, array); - return array; - } - } - - arrays.push_back(array); - return array; - } - - PageElemRef alloc(bool &haveNewPage) - { - if (freelist.size() == 0) { - haveNewPage = true; - newPage(); - } - else { - haveNewPage = false; - } - - totelem++; - PageElemRef r = freelist[freelist.size() - 1]; - freelist.pop_back(); - - for (auto *array : arrays) { - array->setDefault(r); - } - - return r; - } - - void setDefault(PageElemRef ref) - { - for (auto *array : arrays) { - array->setDefault(ref); - } - } - - void free(PageElemRef ref, bool &removedPage) - { - removedPage = false; - - totelem--; - freelist.push_back(ref); - } - - private: - void newPage() - { - totpage++; - totalloc += BM_PAGE_SIZE; - - for (PageArray<> *array : arrays) { - array->newPage(); - } - - size_t count = 1 << BM_PAGE_SHIFT; - for (size_t i = 0; i < count; i++) { - freelist.push_back(i); - } - } -}; - -extern "C" { -# else -struct BMAttrDomain; -# endif //_cplusplus - -# include "BLI_compiler_compat.h" -# include "bmesh_class.h" - -struct BMesh; - -typedef struct BMeshPageArray { - int esize, psize; - void **pages; - void *cppClass; -} BmeshPageArray; - -typedef struct BMeshAttrList { - BMeshPageArray **arrays; - int totarray; - struct BMAttrDomain *domains[ATTR_DOMAIN_NUM]; -} BMeshAttrList; - -typedef struct BMeshPageRef { - // point to arrays, attribute for all domains go into one - // list of arrays - BMeshAttrList *attrs; - int idx; // index into attribute list, NOT element index - int domain; -} BMeshPageRef; - -BLI_INLINE void *BM_ELEM_CD_GET_VOID_P_2(BMElem *elem, int offset) -{ - BMeshPageRef *ref = (BMeshPageRef *)elem->head.data; - BmeshPageArray *array = ref->attrs->arrays[offset]; - size_t page = ref->idx >> BM_PAGE_SHIFT; - size_t off = ref->idx & BM_PAGE_MASK; - - char *ptr = (char *)array->pages[page]; - ptr += off * array->esize; - - return (void *)ptr; -} - -BMeshAttrList *BMAttr_new(); -void BMAttr_reset(BMeshAttrList *list); -void BMAttr_free(BMeshAttrList *list); -void BMAttr_fromCData(BMeshAttrList *list, CustomData *domains[ATTR_DOMAIN_NUM]); -void BMAttr_init(struct BMesh *bm); - -# ifdef __cplusplus -} - -} // namespace bmesh -} // namespace blender -# endif -#endif diff --git a/source/blender/bmesh/intern/bmesh_mesh.cc b/source/blender/bmesh/intern/bmesh_mesh.cc index 8265c52215a..2578dafcb28 100644 --- a/source/blender/bmesh/intern/bmesh_mesh.cc +++ b/source/blender/bmesh/intern/bmesh_mesh.cc @@ -1280,69 +1280,6 @@ void BM_mesh_remap(BMesh *bm, if (fptr_map) { BLI_ghash_free(fptr_map, nullptr, nullptr); } - - // regenerate idmap - if ((bm->idmap.flag & BM_HAS_IDS) && (bm->idmap.flag & BM_HAS_ID_MAP) && bm->idmap.map) { - memset(bm->idmap.map, 0, sizeof(void *) * bm->idmap.map_size); - - char iters[4] = {BM_VERTS_OF_MESH, BM_EDGES_OF_MESH, 0, BM_FACES_OF_MESH}; - const bool have_loop = bm->idmap.flag & BM_LOOP; - - for (int i = 0; i < 4; i++) { - int type = 1 << i; - - if (type == BM_LOOP) { // handle loops with faces - continue; - } - - int cd_id = CustomData_get_offset(cdatas[i], CD_MESH_ID); - int cd_loop_id = CustomData_get_offset(&bm->ldata, CD_MESH_ID); - - BMIter iter; - BMElem *elem; - - if (cd_id < 0 && !(type == BM_FACE && have_loop)) { - continue; - } - - BM_ITER_MESH (elem, &iter, bm, iters[i]) { - if (type == BM_FACE && have_loop) { - BMFace *f = (BMFace *)elem; - BMLoop *l = f->l_first; - - do { - int id_loop = BM_ELEM_CD_GET_INT(l, cd_loop_id); - - if (bm->idmap.ghash) { - void **l_val; - - BLI_ghash_ensure_p(bm->idmap.ghash, POINTER_FROM_INT(id_loop), &l_val); - *l_val = (void *)l; - } - else { - bm->idmap.map[id_loop] = (BMElem *)l; - } - } while ((l = l->next) != f->l_first); - } - - if (cd_id < 0) { - continue; - } - - int id = BM_ELEM_CD_GET_INT(elem, cd_id); - - if (bm->idmap.ghash) { - void **val; - - BLI_ghash_ensure_p(bm->idmap.ghash, POINTER_FROM_INT(id), &val); - *val = (void *)elem; - } - else { - bm->idmap.map[id] = elem; - } - } - } - } } void BM_mesh_rebuild(BMesh *bm, diff --git a/source/blender/bmesh/intern/bmesh_mesh_convert.cc b/source/blender/bmesh/intern/bmesh_mesh_convert.cc index e1260010342..2c4549b34c4 100644 --- a/source/blender/bmesh/intern/bmesh_mesh_convert.cc +++ b/source/blender/bmesh/intern/bmesh_mesh_convert.cc @@ -198,7 +198,7 @@ static Vector mesh_to_bm_copy_info_calc(const CustomData & info.type = type; info.bmesh_offset = bm_layer.offset; info.mesh_data = (mesh_layer_index == -1) ? nullptr : mesh_data.layers[mesh_layer_index].data; - info.elem_size = CustomData_get_elem_size(&bm_layer); + info.elem_size = CustomData_sizeof(type); infos.append(info); per_type_index[type]++; @@ -1154,7 +1154,7 @@ static Vector bm_to_mesh_copy_info_calc(const CustomData & info.n = per_type_index[type]; info.bmesh_offset = bm_layer.offset; info.mesh_data = mesh_layer.data; - info.elem_size = CustomData_get_elem_size(&mesh_layer); + info.elem_size = CustomData_sizeof(type); infos.append(info); per_type_index[type]++; diff --git a/source/blender/bmesh/intern/bmesh_mesh_convert_threaded.cc b/source/blender/bmesh/intern/bmesh_mesh_convert_threaded.cc index 1e42f690c0b..94c2a5b51f4 100644 --- a/source/blender/bmesh/intern/bmesh_mesh_convert_threaded.cc +++ b/source/blender/bmesh/intern/bmesh_mesh_convert_threaded.cc @@ -1033,7 +1033,7 @@ void BM_mesh_bm_to_me_threaded( CustomData_MeshMasks mask = CD_MASK_MESH; CustomData_MeshMasks_update(&mask, ¶ms->cd_mask_extra); - eCustomDataMask extra2 = !params->ignore_mesh_id_layers ? CD_MASK_MESH_ID : 0; + eCustomDataMask extra2 = 0; CustomData *srcdatas[] = {&bm->vdata, &bm->edata, &bm->ldata, &bm->pdata}; int id_flags[4] = {-1, -1, -1, -1}; diff --git a/source/blender/bmesh/intern/bmesh_opdefines.c b/source/blender/bmesh/intern/bmesh_opdefines.c index cea614dbb04..b7060dda400 100644 --- a/source/blender/bmesh/intern/bmesh_opdefines.c +++ b/source/blender/bmesh/intern/bmesh_opdefines.c @@ -1916,7 +1916,7 @@ static BMOpDefine bmo_inset_individual_def = { }, bmo_inset_individual_exec, /* caller needs to handle BMO_OPTYPE_FLAG_SELECT_FLUSH */ - (BMO_OPTYPE_FLAG_NORMALS_CALC | BMO_OPTYPE_FLAG_UNTAN_MULTIRES), + (BMO_OPTYPE_FLAG_NORMALS_CALC), }; /* @@ -1945,7 +1945,7 @@ static BMOpDefine bmo_inset_region_def = { }, bmo_inset_region_exec, (BMO_OPTYPE_FLAG_NORMALS_CALC | - BMO_OPTYPE_FLAG_SELECT_FLUSH | BMO_OPTYPE_FLAG_UNTAN_MULTIRES), + BMO_OPTYPE_FLAG_SELECT_FLUSH), }; /* diff --git a/source/blender/editors/mesh/editmesh_fair.c b/source/blender/editors/mesh/editmesh_fair.c index 267ed14b541..964c8562b24 100644 --- a/source/blender/editors/mesh/editmesh_fair.c +++ b/source/blender/editors/mesh/editmesh_fair.c @@ -86,15 +86,6 @@ static EnumPropertyItem prop_edit_mesh_fair_selection_mode_items[] = { "Tangency", "Fair tangency", }, - /* - { - MESH_FAIRING_DEPTH_CURVATURE, - "CURVATURE", - 0, - "Curvature", - "Fair curvature", - }, - */ {0, NULL, 0, NULL, NULL}, }; diff --git a/source/blender/editors/mesh/editmesh_utils.c b/source/blender/editors/mesh/editmesh_utils.c index 686f8254532..eb69ad7145d 100644 --- a/source/blender/editors/mesh/editmesh_utils.c +++ b/source/blender/editors/mesh/editmesh_utils.c @@ -301,10 +301,6 @@ void EDBM_mesh_load_ex(Main *bmain, Object *ob, bool free_data) bm->shapenr = 1; } -#ifdef WITH_PBVH_CACHE - BKE_pbvh_invalidate_cache(ob); -#endif - BM_mesh_bm_to_me(bmain, bm, me, diff --git a/source/blender/editors/mesh/mesh_data.cc b/source/blender/editors/mesh/mesh_data.cc index 5cfb9b1e446..efe7375b673 100644 --- a/source/blender/editors/mesh/mesh_data.cc +++ b/source/blender/editors/mesh/mesh_data.cc @@ -414,7 +414,8 @@ int ED_mesh_color_add( BM_data_layer_copy(&bm, &bm.ldata, CD_PROP_BYTE_COLOR, src_i, dst_i); } else { - memcpy(layer->data, active_layer->data, CustomData_get_elem_size(layer) * me->totloop); + memcpy( + layer->data, active_layer->data, CustomData_sizeof(CD_PROP_BYTE_COLOR) * me->totloop); } } } @@ -493,7 +494,7 @@ int ED_mesh_sculpt_color_add(Mesh *me, const char *name, const bool do_init, Rep BM_data_layer_copy(&bm, &bm.vdata, CD_PROP_COLOR, src_i, dst_i); } else { - memcpy(layer->data, active_layer->data, CustomData_get_elem_size(layer) * me->totloop); + memcpy(layer->data, active_layer->data, CustomData_sizeof(CD_PROP_COLOR) * me->totloop); } } } diff --git a/source/blender/editors/object/object_edit.cc b/source/blender/editors/object/object_edit.cc index 7c4fe805209..5a0be72c765 100644 --- a/source/blender/editors/object/object_edit.cc +++ b/source/blender/editors/object/object_edit.cc @@ -814,12 +814,6 @@ bool ED_object_editmode_enter_ex(Main *bmain, Scene *scene, Object *ob, int flag return false; } -#ifdef WITH_PBVH_CACHE - if (ob->type == OB_MESH) { - BKE_pbvh_invalidate_cache(ob); - } -#endif - ob->restore_mode = ob->mode; ob->mode = OB_MODE_EDIT; diff --git a/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc b/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc index 83651c0a785..5a31b72cf4a 100644 --- a/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc +++ b/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc @@ -440,18 +440,7 @@ void SCULPT_pbvh_clear(Object *ob, bool /*cache_pbvh*/) /* Clear out any existing DM and PBVH. */ if (ss->pbvh) { -#ifdef WITH_PBVH_CACHE - if (cache_pbvh) { - BKE_pbvh_set_cached(ob, ss->pbvh); - } - else { - BKE_pbvh_cache_remove(ss->pbvh); - BKE_pbvh_free(ss->pbvh); - } -#else BKE_pbvh_free(ss->pbvh); -#endif - ss->pbvh = nullptr; } @@ -497,34 +486,6 @@ void SCULPT_dynamic_topology_enable_ex(Main *bmain, BKE_pbvh_pmap_release(ss->pmap); ss->pmap = nullptr; } - -#ifdef WITH_PBVH_CACHE - /* Remove existing pbvh so we can free it ourselves. */ - BKE_pbvh_cache_remove(ss->pbvh); - - /* Free any other pbvhs */ - BKE_pbvh_clear_cache(nullptr); -#endif - } - - if (ss->bm) { - bool ok = ss->bm->totvert == me->totvert && ss->bm->totedge == me->totedge && - ss->bm->totloop == me->totloop && ss->bm->totface == me->totpoly; - - if (!ok) { -#ifdef WITH_PBVH_CACHE - /* Ensure ss->pbvh is in the cache so it can be destroyed in BKE_pbvh_free_bmesh. */ - if (ss->pbvh) { - BKE_pbvh_set_cached(ob, ss->pbvh); - } -#endif - - /* Destroy all cached PBVHs with this bmesh. */ - BKE_pbvh_free_bmesh(nullptr, ss->bm); - - ss->pbvh = nullptr; - ss->bm = nullptr; - } } if (!ss->bm || !ss->pbvh || BKE_pbvh_type(ss->pbvh) != PBVH_BMESH) { @@ -546,11 +507,7 @@ void SCULPT_dynamic_topology_enable_ex(Main *bmain, } #endif -#ifdef WITH_PBVH_CACHE - PBVH *pbvh = BKE_pbvh_get_or_free_cached(ob, BKE_object_get_original_mesh(ob), PBVH_BMESH); -#else PBVH *pbvh = ss->pbvh; -#endif if (pbvh) { BMesh *bm = BKE_pbvh_get_bmesh(pbvh); diff --git a/source/blender/editors/sculpt_paint/sculpt_face_set.cc b/source/blender/editors/sculpt_paint/sculpt_face_set.cc index b81c671e071..dbf569d2378 100644 --- a/source/blender/editors/sculpt_paint/sculpt_face_set.cc +++ b/source/blender/editors/sculpt_paint/sculpt_face_set.cc @@ -1620,7 +1620,6 @@ static EnumPropertyItem prop_sculpt_face_sets_edit_types[] = { "Creates a smooth as possible geometry patch from the Face Set minimizing changes in " "vertex tangents", }, - /* { SCULPT_FACE_SET_EDIT_FAIR_CURVATURE, "FAIR_CURVATURE", @@ -1629,7 +1628,6 @@ static EnumPropertyItem prop_sculpt_face_sets_edit_types[] = { "Creates a smooth as possible geometry patch from the Face Set minimizing changes in " "surface curvature", }, - */ { SCULPT_FACE_SET_EDIT_FILL_COMPONENT, "FILL_COMPONENT", diff --git a/source/blender/editors/sculpt_paint/sculpt_undo.cc b/source/blender/editors/sculpt_paint/sculpt_undo.cc index a8aff10a98d..04eff63c7ea 100644 --- a/source/blender/editors/sculpt_paint/sculpt_undo.cc +++ b/source/blender/editors/sculpt_paint/sculpt_undo.cc @@ -2636,12 +2636,6 @@ static void sculpt_undo_push_begin_ex(Object *ob, const char *name, bool no_firs BKE_pbvh_set_bm_log(ss->pbvh, ss->bm_log); } } - -#ifdef WITH_PBVH_CACHE - if (ss->pbvh) { - BKE_pbvh_set_cached(ob, ss->pbvh); - } -#endif } void SCULPT_undo_push_begin_ex(Object *ob, const char *name) @@ -3152,15 +3146,13 @@ void ED_sculpt_fast_save_bmesh(Object *ob) } #if 1 - struct BMeshToMeshParams params = {0}; + struct BMeshToMeshParams params = {}; void BM_mesh_bm_to_me_threaded( Main * bmain, Object * ob, BMesh * bm, Mesh * me, const struct BMeshToMeshParams *params); params.update_shapekey_indices = true; - params.cd_mask_extra.vmask = CD_MASK_MESH_ID | CD_MASK_DYNTOPO_VERT; - params.cd_mask_extra.emask = CD_MASK_MESH_ID; - params.cd_mask_extra.pmask = CD_MASK_MESH_ID; + params.cd_mask_extra.vmask = CD_MASK_DYNTOPO_VERT; // BM_mesh_bm_to_me_threaded(nullptr, ob, bm, (Mesh *)ob->data, ¶ms); BM_mesh_bm_to_me(nullptr, bm, (Mesh *)ob->data, ¶ms); diff --git a/source/blender/makesdna/DNA_customdata_types.h b/source/blender/makesdna/DNA_customdata_types.h index de67fe784a5..3a86b8e4de0 100644 --- a/source/blender/makesdna/DNA_customdata_types.h +++ b/source/blender/makesdna/DNA_customdata_types.h @@ -77,19 +77,16 @@ typedef struct CustomData { * MUST be >= CD_NUMTYPES, but we can't use a define here. * Correct size is ensured in CustomData_update_typemap assert(). */ - int typemap[55]; + int typemap[54]; /** Number of layers, size of layers array. */ int totlayer, maxlayer; /** In editmode, total size of all data layers. */ - int totsize; + int totsize, _pad[1]; /** (BMesh Only): Memory pool for allocation of blocks. */ struct BLI_mempool *pool; /** External file storing custom-data layers. */ CustomDataExternal *external; - - /** for use with USE_BMESH_PAGE_CUSTOMDATA test, remove later*/ - void *bm_attrs, *_pad[1]; } CustomData; /** #CustomData.type */ @@ -166,10 +163,9 @@ typedef enum eCustomDataType { CD_PROP_BOOL = 50, CD_HAIRLENGTH = 51, - CD_MESH_ID = 52, - CD_DYNTOPO_VERT = 53, - CD_TOOLFLAGS = 54, - CD_NUMTYPES = 55, + CD_DYNTOPO_VERT = 52, + CD_TOOLFLAGS = 53, + CD_NUMTYPES = 54, } eCustomDataType; /* Bits for eCustomDataMask */ @@ -220,7 +216,6 @@ typedef enum eCustomDataType { #define CD_MASK_PROP_INT8 (1ULL << CD_PROP_INT8) #define CD_MASK_DYNTOPO_VERT (1ULL << CD_DYNTOPO_VERT) -#define CD_MASK_MESH_ID (1ULL << CD_MESH_ID) #define CD_MASK_HAIRLENGTH (1ULL << CD_HAIRLENGTH) #define CD_MASK_TOOLFLAGS (1ULL << CD_TOOLFLAGS) -- 2.30.2 From 212eeabd162200cda2ae1a592e0a908b99fd18c4 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Mon, 3 Apr 2023 00:08:14 -0700 Subject: [PATCH 053/279] temp-sculpt-dyntopo: Various random fixes * Expose dyntopo radius scale in UI; needed for snake hook. * Created a dyntopo "repeat" setting, also used by snake hook. * Fixed nasty bug in CustomData_bmesh_copy_data_exclude_by_type, it may exist in master. * Dyntopo subdivide now adds to unified minmax heap again. * Fixed versioning error in Sculpt.dyntopo. * Fix bug in new blender::Map TableGSet backend. * Vertex IDs are now only uploaded to the GPU when "show ids" overlay is on. * Dyntopo spacing may now be zero, in which case native brush spacing will be used. * Undo nodes of SCULPT_UNDO_COORDS/COLOR/MASK/FACE_SETS can now be pushed multiple times. * Removed last vestiges of PBVH caching code. --- scripts/startup/bl_ui/space_view3d_toolbar.py | 14 ++ source/blender/blenkernel/intern/brush.cc | 8 +- .../blender/blenkernel/intern/customdata.cc | 33 +++- source/blender/blenkernel/intern/dyntopo.cc | 161 ++++++++++++------ .../blenkernel/intern/dyntopo_collapse.cc | 7 +- .../blenkernel/intern/dyntopo_intern.hh | 2 +- source/blender/blenkernel/intern/paint.cc | 16 +- source/blender/blenkernel/intern/pbvh.cc | 2 +- .../blender/blenkernel/intern/pbvh_bmesh.cc | 18 +- .../blender/blenlib/intern/BLI_table_gset.cc | 53 +++--- .../draw/engines/overlay/overlay_sculpt.cc | 3 +- .../blender/draw/intern/draw_manager_data.cc | 2 +- source/blender/draw/intern/draw_pbvh.cc | 2 + .../blender/editors/object/object_relations.c | 2 +- source/blender/editors/sculpt_paint/sculpt.cc | 38 +++-- .../editors/sculpt_paint/sculpt_api.cc | 1 + .../editors/sculpt_paint/sculpt_undo.cc | 29 +++- source/blender/makesdna/DNA_brush_enums.h | 7 +- source/blender/makesdna/DNA_brush_types.h | 1 + source/blender/makesrna/intern/rna_brush.c | 23 ++- source/creator/creator.c | 4 - 21 files changed, 286 insertions(+), 140 deletions(-) diff --git a/scripts/startup/bl_ui/space_view3d_toolbar.py b/scripts/startup/bl_ui/space_view3d_toolbar.py index 6ae0c565c2f..14e9738292b 100644 --- a/scripts/startup/bl_ui/space_view3d_toolbar.py +++ b/scripts/startup/bl_ui/space_view3d_toolbar.py @@ -946,6 +946,20 @@ class VIEW3D_PT_sculpt_dyntopo(Panel, View3DPaintPanel): "spacing", expand=True ) + UnifiedPaintPanel.prop_unified_dyntopo( + sub, + context, + brush, + "radius_scale", + expand=True + ) + UnifiedPaintPanel.prop_unified_dyntopo( + sub, + context, + brush, + "repeat", + expand=True + ) col.prop(sculpt, "use_smooth_shading") col.prop(sculpt, "use_flat_vcol_shading") diff --git a/source/blender/blenkernel/intern/brush.cc b/source/blender/blenkernel/intern/brush.cc index 334a7a8a897..8871e9eb3af 100644 --- a/source/blender/blenkernel/intern/brush.cc +++ b/source/blender/blenkernel/intern/brush.cc @@ -1817,11 +1817,13 @@ void BKE_brush_sculpt_reset(Brush *br) br->alpha = 1.0f; br->rake_factor = 1.0f; br->dyntopo.inherit = ~(DYNTOPO_INHERIT_SPACING | DYNTOPO_INHERIT_SUBDIVIDE | - DYNTOPO_INHERIT_COLLAPSE | DYNTOPO_INHERIT_DETAIL_RANGE); + DYNTOPO_INHERIT_COLLAPSE | DYNTOPO_INHERIT_DETAIL_RANGE | + DYNTOPO_INHERIT_RADIUS_SCALE | DYNTOPO_INHERIT_REPEAT); br->dyntopo.flag |= DYNTOPO_SUBDIVIDE | DYNTOPO_COLLAPSE; br->dyntopo.detail_range = 0.4f; - br->dyntopo.spacing = 5; - br->dyntopo.radius_scale = 1.15; + br->dyntopo.spacing = 0; + br->dyntopo.radius_scale = 1.25; + br->dyntopo.repeat = 1; break; case SCULPT_TOOL_THUMB: br->size = 75; diff --git a/source/blender/blenkernel/intern/customdata.cc b/source/blender/blenkernel/intern/customdata.cc index 5ffc3d0ba9c..ff1e6a2c638 100644 --- a/source/blender/blenkernel/intern/customdata.cc +++ b/source/blender/blenkernel/intern/customdata.cc @@ -1548,6 +1548,16 @@ static bool layerValidate_propfloat2(void *data, const uint totitems, const bool return has_errors; } +static void layerDynTopoVert_default(void *dest, int count) +{ + MSculptVert *mv = static_cast(dest); + + for (int i = 0; i < count; i++, mv++) { + memset(static_cast(mv), 0, sizeof(MSculptVert)); + mv->flag = SCULPTVERT_NEED_TRIANGULATE | SCULPTVERT_NEED_DISK_SORT | SCULPTVERT_NEED_VALENCE; + } +} + static void layerDynTopoVert_copy(const void *source, void *dest, int count) { memcpy(dest, source, count * sizeof(MSculptVert)); @@ -2080,7 +2090,9 @@ static const LayerTypeInfo LAYERTYPEINFO[CD_NUMTYPES] = { nullptr, // flag singleton layer layerDynTopoVert_copy, nullptr, - layerDynTopoVert_interp}, + layerDynTopoVert_interp, + nullptr, + layerDynTopoVert_default}, /*53 CD_BMESH_TOOLFLAGS */ {sizeof(MToolFlags), "MToolFlags", @@ -2159,7 +2171,7 @@ static const char *LAYERTYPENAMES[CD_NUMTYPES] = {/* 0-4 */ /*51-53*/ "CDHairLength", "CDDyntopoVert", - "CDPropInt16"}; + "CDMToolFlags"}; const CustomData_MeshMasks CD_MASK_BAREMESH = { /*vmask*/ CD_MASK_PROP_FLOAT3, @@ -4473,6 +4485,9 @@ ATTR_NO_OPT void CustomData_bmesh_copy_data_exclude_by_type(const CustomData *so /* copies a layer at a time */ int dest_i = 0; for (int src_i = 0; src_i < source->totlayer; src_i++) { + if (source->layers[src_i].flag & CD_FLAG_ELEM_NOCOPY) { + continue; + } /* find the first dest layer with type >= the source type * (this should work because layers are ordered by type) @@ -4484,6 +4499,16 @@ ATTR_NO_OPT void CustomData_bmesh_copy_data_exclude_by_type(const CustomData *so dest_i++; } + while (dest_i < dest->totlayer && dest->layers[dest_i].type == source->layers[src_i].type) { + if (STREQ(dest->layers[dest_i].name, source->layers[src_i].name)) { + break; + } + else if (was_new) { + CustomData_bmesh_set_default_n(dest, dest_block, dest_i); + } + dest_i++; + } + /* if there are no more dest layers, we're done */ if (dest_i >= dest->totlayer) { return; @@ -4493,10 +4518,6 @@ ATTR_NO_OPT void CustomData_bmesh_copy_data_exclude_by_type(const CustomData *so if (dest->layers[dest_i].type == source->layers[src_i].type && STREQ(dest->layers[dest_i].name, source->layers[src_i].name)) { if (no_mask || ((CD_TYPE_AS_MASK(dest->layers[dest_i].type) & mask_exclude) == 0)) { - if (dest->layers[dest_i].flag & CD_FLAG_ELEM_NOCOPY) { - break; - } - const void *src_data = POINTER_OFFSET(src_block, source->layers[src_i].offset); void *dest_data = POINTER_OFFSET(*dest_block, dest->layers[dest_i].offset); const LayerTypeInfo *typeInfo = layerType_getInfo( diff --git a/source/blender/blenkernel/intern/dyntopo.cc b/source/blender/blenkernel/intern/dyntopo.cc index 6311891b4c8..54f0bf9c538 100644 --- a/source/blender/blenkernel/intern/dyntopo.cc +++ b/source/blender/blenkernel/intern/dyntopo.cc @@ -504,7 +504,7 @@ static void long_edge_queue_edge_add_recursive(EdgeQueueContext *eq_ctx, return; } } - + edge_queue_insert_unified(eq_ctx, l_edge->e); if ((l_edge->radial_next != l_edge)) { @@ -517,6 +517,10 @@ static void long_edge_queue_edge_add_recursive(EdgeQueueContext *eq_ctx, do { BMLoop *l_adjacent[2] = {l_iter->next, l_iter->prev}; for (int i = 0; i < (int)ARRAY_SIZE(l_adjacent); i++) { + if (l_adjacent[i]->e->head.hflag & BM_ELEM_TAG) { + continue; + } + float len_sq_other = calc_weighted_length( eq_ctx, l_adjacent[i]->e->v1, l_adjacent[i]->e->v2, -1.0f); @@ -528,7 +532,7 @@ static void long_edge_queue_edge_add_recursive(EdgeQueueContext *eq_ctx, long_edge_queue_edge_add_recursive( eq_ctx, l_adjacent[i]->radial_next, l_adjacent[i], len_sq_other, limit_len, depth + 1); } - } while ((l_iter = l_iter->radial_next) != l_end); + } while ((l_iter = l_iter->radial_next) != l_edge); } } @@ -616,9 +620,9 @@ BLI_INLINE int dyntopo_thread_rand(int seed) return (seed * multiplier + addend) & mask; } -static void unified_edge_queue_task_cb(void *__restrict userdata, - const int n, - const TaskParallelTLS *__restrict /*tls*/) +ATTR_NO_OPT static void unified_edge_queue_task_cb(void *__restrict userdata, + const int n, + const TaskParallelTLS *__restrict /*tls*/) { EdgeQueueThreadData *tdata = ((EdgeQueueThreadData *)userdata) + n; PBVH *pbvh = tdata->pbvh; @@ -829,6 +833,20 @@ bool check_face_is_tri(PBVH *pbvh, BMFace *f) dbl = next; } + for (int i = 0; i < totface; i++) { + BMFace *f2 = fs[i]; + + if (!f2) { + continue; + } + + BMLoop *l = f2->l_first; + do { + MSculptVert *mv = BM_ELEM_CD_PTR(l->v, pbvh->cd_sculpt_vert); + mv->flag |= SCULPTVERT_NEED_DISK_SORT | SCULPTVERT_NEED_VALENCE; + } while ((l = l->next) != f2->l_first); + } + for (int i = 0; i < totface; i++) { BMFace *f2 = fs[i]; @@ -2041,20 +2059,20 @@ float mask_cb_nop(PBVHVertRef /*vertex*/, void * /*userdata*/) } /* Collapse short edges, subdivide long edges */ -extern "C" bool BKE_pbvh_bmesh_update_topology(PBVH *pbvh, - PBVHTopologyUpdateMode mode, - const float center[3], - const float view_normal[3], - float radius, - const bool use_frontface, - const bool use_projected, - int /*sym_axis*/, - bool updatePBVH, - DyntopoMaskCB mask_cb, - void *mask_cb_data, - int custom_max_steps, - bool disable_surface_relax, - bool is_snake_hook) +ATTR_NO_OPT extern "C" bool BKE_pbvh_bmesh_update_topology(PBVH *pbvh, + PBVHTopologyUpdateMode mode, + const float center[3], + const float view_normal[3], + float radius, + const bool use_frontface, + const bool use_projected, + int /*sym_axis*/, + bool updatePBVH, + DyntopoMaskCB mask_cb, + void *mask_cb_data, + int custom_max_steps, + bool disable_surface_relax, + bool is_snake_hook) { /* Disable surface smooth if uv layers are present, to avoid expensive reprojection operation. */ if (!is_snake_hook && CustomData_has_layer(&pbvh->header.bm->ldata, CD_PROP_FLOAT2)) { @@ -2072,7 +2090,6 @@ extern "C" bool BKE_pbvh_bmesh_update_topology(PBVH *pbvh, const int cd_vert_node_offset = pbvh->cd_vert_node_offset; const int cd_face_node_offset = pbvh->cd_face_node_offset; const int cd_sculpt_vert = pbvh->cd_sculpt_vert; - // float ratio = 1.0f; bool modified = false; @@ -2190,6 +2207,8 @@ extern "C" bool BKE_pbvh_bmesh_update_topology(PBVH *pbvh, } #ifdef SKINNY_EDGE_FIX + float ratio = 1.0f; + /* Prevent remesher thrashing by throttling edge splitting in pathological case of skinny * edges. */ @@ -2209,39 +2228,43 @@ extern "C" bool BKE_pbvh_bmesh_update_topology(PBVH *pbvh, } } #else - // ratio = 1.0f; +// const float ratio = 2.0; // 1.0f / 3.0f; #endif int steps[2] = {0, 0}; + PBVHTopologyUpdateMode ops[2]; + int totop = 0; + if ((mode & PBVH_Subdivide) && (mode & PBVH_Collapse)) { - steps[1] = DYNTOPO_MAX_ITER / 3; - steps[0] = DYNTOPO_MAX_ITER - steps[1]; + ops[0] = PBVH_Subdivide; + ops[1] = PBVH_Collapse; + totop = 2; + + steps[1] = DYNTOPO_MAX_ITER / 3.0; + steps[0] = DYNTOPO_MAX_ITER <= steps[1] ? DYNTOPO_MAX_ITER : DYNTOPO_MAX_ITER - steps[1]; } else if (mode & PBVH_Subdivide) { + ops[0] = PBVH_Subdivide; + totop = 1; + steps[0] = DYNTOPO_MAX_ITER; } else if (mode & PBVH_Collapse) { + ops[0] = PBVH_Collapse; + totop = 1; + steps[0] = DYNTOPO_MAX_ITER; } + int max_steps = max_ii(custom_max_steps, DYNTOPO_MAX_ITER) << (totop - 1); + int max_subd = max_steps >> (totop - 1); + int edges_size = steps[0]; BMEdge **edges = (BMEdge **)MEM_malloc_arrayN(edges_size, sizeof(void *), __func__); int etot = 0; - - PBVHTopologyUpdateMode ops[2]; - int count = 0; + int count = 0, count_subd = 0, count_cold = 0; int i = 0; - int max_steps = max_ii(custom_max_steps, DYNTOPO_MAX_ITER) << 1; - - int totop = 0; - - if (mode & PBVH_Subdivide) { - ops[totop++] = PBVH_Subdivide; - } - if (mode & PBVH_Collapse) { - ops[totop++] = PBVH_Collapse; - } int curop = 0; float limit_len_subd = eq_ctx.limit_len_max_sqr; @@ -2259,10 +2282,11 @@ extern "C" bool BKE_pbvh_bmesh_update_topology(PBVH *pbvh, if (count >= steps[curop]) { BM_log_entry_add_ex(pbvh->header.bm, pbvh->bm_log, true); - if (ops[curop] == PBVH_Subdivide) { + if (ops[curop] == PBVH_Subdivide && count_subd < max_subd) { modified = true; BLI_smallhash_clear(&subd_edges, 0); pbvh_split_edges(&eq_ctx, pbvh, pbvh->header.bm, edges, etot, false); + count_subd += etot; VALIDATE_LOG(pbvh->bm_log); etot = 0; } @@ -2271,6 +2295,11 @@ extern "C" bool BKE_pbvh_bmesh_update_topology(PBVH *pbvh, count = 0; } + if (curop == 0 && count_subd >= max_subd && totop > 1 && ops[0] == PBVH_Subdivide && + ops[1] == PBVH_Collapse) { + curop = 1; + } + switch (ops[curop]) { case PBVH_Subdivide: { if (eq_ctx.edge_heap.max_weight() < limit_len_subd) { @@ -2341,6 +2370,7 @@ extern "C" bool BKE_pbvh_bmesh_update_topology(PBVH *pbvh, pbvh_bmesh_collapse_edge(pbvh, e, e->v1, e->v2, nullptr, nullptr, &eq_ctx); VALIDATE_LOG(pbvh->bm_log); + count_cold++; // XXX // BM_log_entry_add_ex(pbvh->header.bm, pbvh->bm_log, true); break; @@ -2356,6 +2386,27 @@ extern "C" bool BKE_pbvh_bmesh_update_topology(PBVH *pbvh, i++; } +#if 0 + { + printf("cd_faceset_offset: %d\n", pbvh->cd_faceset_offset); + CustomData *data = &pbvh->header.bm->pdata; + for (int i = 0; i < data->totlayer; i++) { + CustomDataLayer *layer = &data->layers[i]; + printf(" %s \"%s\" offset=%d flag=%d\n", + CustomData_layertype_name(eCustomDataType(layer->type)), + layer->name, + layer->offset, + layer->flag); + } + printf("\n"); + } +#endif + + printf("subd: %d, cold: %d, ratio: %.3f\n", + count_subd, + count_cold, + count_cold > 0 ? float(count_subd) / float(count_cold) : 0.0f); + if (etot > 0) { modified = true; BLI_smallhash_clear(&subd_edges, 0); @@ -2426,10 +2477,15 @@ extern "C" bool BKE_pbvh_bmesh_update_topology(PBVH *pbvh, PBVHNode *node = pbvh->nodes + i; if (node->flag & PBVH_Leaf) { - // BKE_pbvh_bmesh_check_tris(pbvh, node); + pbvh_bmesh_check_other_verts(node); + BKE_pbvh_bmesh_check_tris(pbvh, node); } } + if (modified) { + BKE_pbvh_update_bounds(pbvh, PBVH_UpdateBB | PBVH_UpdateOriginalBB); + } + /* Push a subentry. */ BM_log_entry_add_ex(pbvh->header.bm, pbvh->bm_log, true); @@ -2947,7 +3003,7 @@ static void pbvh_split_edges(EdgeQueueContext *eq_ctx, } while ((l = l->next) != f->l_first); if (j != n) { - printf("error! %s\n", __func__); + printf("%s: error 1!\n", __func__); continue; } @@ -2975,7 +3031,7 @@ static void pbvh_split_edges(EdgeQueueContext *eq_ctx, } while ((l3 = l3->next) != f2->l_first); if (l1 == l2 || !l1 || !l2) { - printf("errorl!\n"); + printf("%s: error 2!\n", __func__); continue; } @@ -3003,16 +3059,21 @@ static void pbvh_split_edges(EdgeQueueContext *eq_ctx, } if (newf) { - // rl->e->head.hflag &= ~BM_ELEM_TAG; - // edge_queue_insert_unified(eq_ctx, rl->e); - /* - long_edge_queue_edge_add_recursive_3(eq_ctx, - rl->radial_next, - rl, - len_squared_v3v3(rl->e->v1->co, rl->e->v2->co), - pbvh->bm_max_edge_len, - 0); - */ +#if 1 + rl->e->head.hflag &= ~BM_ELEM_TAG; +# if 1 + long_edge_queue_edge_add_recursive( + eq_ctx, + rl, + rl->next, + calc_weighted_length(eq_ctx, rl->e->v1, rl->e->v2, -1.0f), + eq_ctx->limit_len_max, + 0); +# else + edge_queue_insert_unified(eq_ctx, rl->e); + +# endif +#endif check_face_is_manifold(pbvh, bm, newf); check_face_is_manifold(pbvh, bm, f2); diff --git a/source/blender/blenkernel/intern/dyntopo_collapse.cc b/source/blender/blenkernel/intern/dyntopo_collapse.cc index 4b63c835920..ca9b742fe59 100644 --- a/source/blender/blenkernel/intern/dyntopo_collapse.cc +++ b/source/blender/blenkernel/intern/dyntopo_collapse.cc @@ -206,6 +206,9 @@ static void collapse_ring_callback_pre(BMElem *elem, void *userdata) case BM_VERT: { BMVert *v = reinterpret_cast(elem); + MSculptVert *mv = BM_ELEM_CD_PTR(v, data->pbvh->cd_sculpt_vert); + MV_ADD_FLAG(mv, SCULPTVERT_NEED_DISK_SORT | SCULPTVERT_NEED_VALENCE); + BM_log_vert_removed(bm, data->pbvh->bm_log, v); pbvh_bmesh_vert_remove(data->pbvh, v); BM_idmap_release(data->pbvh->bm_idmap, elem, false); @@ -259,8 +262,8 @@ static void collapse_ring_callback_post(BMElem *elem, void *userdata) case BM_VERT: { BMVert *v = reinterpret_cast(elem); - MSculptVert *mv = (MSculptVert *)BM_ELEM_CD_GET_VOID_P(v, data->pbvh->cd_sculpt_vert); - mv->flag |= SCULPTVERT_NEED_VALENCE | SCULPTVERT_NEED_DISK_SORT; + MSculptVert *mv = BM_ELEM_CD_PTR(v, data->pbvh->cd_sculpt_vert); + MV_ADD_FLAG(mv, SCULPTVERT_NEED_DISK_SORT | SCULPTVERT_NEED_VALENCE); check_new_elem_id(elem, data); BM_log_vert_added(bm, data->pbvh->bm_log, v); diff --git a/source/blender/blenkernel/intern/dyntopo_intern.hh b/source/blender/blenkernel/intern/dyntopo_intern.hh index e9dcc031b74..46fd5ee380d 100644 --- a/source/blender/blenkernel/intern/dyntopo_intern.hh +++ b/source/blender/blenkernel/intern/dyntopo_intern.hh @@ -77,7 +77,7 @@ inline bool bm_elem_is_free(BMElem *elem, int htype) (SCULPT_CORNER_MESH | SCULPT_CORNER_FACE_SET | SCULPT_CORNER_SHARP | SCULPT_CORNER_SEAM | \ SCULPT_CORNER_UV) -#define DYNTOPO_MAX_ITER 4096 +#define DYNTOPO_MAX_ITER 256 #define DYNTOPO_USE_HEAP #define DYNTOPO_USE_MINMAX_HEAP diff --git a/source/blender/blenkernel/intern/paint.cc b/source/blender/blenkernel/intern/paint.cc index a6e32e7529c..3ec06e31849 100644 --- a/source/blender/blenkernel/intern/paint.cc +++ b/source/blender/blenkernel/intern/paint.cc @@ -2430,12 +2430,8 @@ void BKE_sculpt_toolsettings_data_ensure(Scene *scene) sd->detail_size = 8.0f; } - if (!sd->dyntopo_radius_scale) { - sd->dyntopo_radius_scale = 1.0f; - } - /* We check these flags here in case versioning code fails. */ - if (!sd->detail_range || !sd->dyntopo_spacing) { + if (!sd->detail_range || !sd->dyntopo.spacing) { sd->flags |= SCULPT_DYNTOPO_ENABLED; } @@ -2447,14 +2443,8 @@ void BKE_sculpt_toolsettings_data_ensure(Scene *scene) sd->detail_percent = 25; } - sd->dyntopo = *DNA_struct_default_get(DynTopoSettings); - - if (!sd->dyntopo_spacing) { - sd->dyntopo_spacing = 35; - } - - if (sd->constant_detail == 0.0f) { - sd->constant_detail = 3.0f; + if (!sd->dyntopo.constant_detail) { + sd->dyntopo = *DNA_struct_default_get(DynTopoSettings); } if (!sd->automasking_start_normal_limit) { diff --git a/source/blender/blenkernel/intern/pbvh.cc b/source/blender/blenkernel/intern/pbvh.cc index 4d6323ec250..d1ecb0b5a5c 100644 --- a/source/blender/blenkernel/intern/pbvh.cc +++ b/source/blender/blenkernel/intern/pbvh.cc @@ -1812,7 +1812,7 @@ bool BKE_pbvh_get_color_layer(const PBVH *pbvh, return false; } - if (BKE_pbvh_type(pbvh) == PBVH_BMESH) { + if (pbvh && BKE_pbvh_type(pbvh) == PBVH_BMESH) { CustomData *data; if (domain == ATTR_DOMAIN_POINT) { diff --git a/source/blender/blenkernel/intern/pbvh_bmesh.cc b/source/blender/blenkernel/intern/pbvh_bmesh.cc index 0390e9f8fbf..49aa46020a9 100644 --- a/source/blender/blenkernel/intern/pbvh_bmesh.cc +++ b/source/blender/blenkernel/intern/pbvh_bmesh.cc @@ -240,10 +240,12 @@ ATTR_NO_OPT void pbvh_bmesh_check_nodes(PBVH *pbvh) } } -ATTR_NO_OPT void pbvh_bmesh_pbvh_bmesh_check_nodes(PBVH *pbvh) +ATTR_NO_OPT extern "C" void BKE_pbvh_bmesh_check_nodes(PBVH *pbvh) { pbvh_bmesh_check_nodes(pbvh); } +#else +ATTR_NO_OPT extern "C" void BKE_pbvh_bmesh_check_nodes(PBVH *pbvh) {} #endif /** \} */ @@ -625,7 +627,7 @@ void pbvh_bmesh_vert_remove(PBVH *pbvh, BMVert *v) PBVHNode *v_node = pbvh_bmesh_node_from_vert(pbvh, v); - if (v_node) { + if (v_node && v_node->bm_unique_verts) { BLI_table_gset_remove(v_node->bm_unique_verts, v, nullptr); v_node->flag |= (PBVHNodeFlags)updateflag; } @@ -1025,8 +1027,6 @@ bool pbvh_bmesh_node_limit_ensure(PBVH *pbvh, int node_index) TableGSet *bm_faces = pbvh->nodes[node_index].bm_faces; const int bm_faces_size = BLI_table_gset_len(bm_faces); - // pbvh_bmesh_check_nodes(pbvh); - if (bm_faces_size <= pbvh->leaf_limit || pbvh->nodes[node_index].depth >= PBVH_STACK_FIXED_DEPTH) { /* Node limit not exceeded */ @@ -1075,8 +1075,6 @@ bool pbvh_bmesh_node_limit_ensure(PBVH *pbvh, int node_index) MEM_freeN(bbc_array); - // pbvh_bmesh_check_nodes(pbvh); - return true; } @@ -2696,7 +2694,7 @@ bool BKE_pbvh_bmesh_update_topology_nodes(PBVH *pbvh, updatePBVH, mask_cb, mask_cb_data, - 0, // is_snake_hook ? 40960 : 0, + 4096, //is_snake_hook ? 4096 : 4096, disable_surface_relax, is_snake_hook); @@ -3323,6 +3321,12 @@ static void BKE_pbvh_bmesh_correct_tree(PBVH *pbvh, PBVHNode *node, PBVHNode *pa TGSET_ITER (f, node->bm_faces) { BMLoop *l = f->l_first; + if (BM_elem_is_free((BMElem *)f, BM_FACE)) { + printf("%s: corrupted face %p.\n", __func__, f); + BLI_table_gset_remove(node->bm_faces, f, nullptr); + continue; + } + BM_ELEM_CD_SET_INT(f, pbvh->cd_face_node_offset, DYNTOPO_NODE_NONE); do { diff --git a/source/blender/blenlib/intern/BLI_table_gset.cc b/source/blender/blenlib/intern/BLI_table_gset.cc index 9c50e6ebf82..53e7b540720 100644 --- a/source/blender/blenlib/intern/BLI_table_gset.cc +++ b/source/blender/blenlib/intern/BLI_table_gset.cc @@ -16,27 +16,13 @@ # define PTR_TO_IDX(ts) static_cast *>((ts)->ptr_to_idx) #endif -TableGSet *BLI_table_gset_new(const char *info) -{ - TableGSet *ts = MEM_new(info); - -#ifdef USE_TGSET_SMALLHASH - ts->ptr_to_idx = static_cast(MEM_cnew("table gset smallhash")); - BLI_smallhash_init(PTR_TO_IDX(->ptr_to_idx)); -#else - ts->ptr_to_idx = static_cast(MEM_new>("ts->ptr_to_idx")); -#endif - - return ts; -} - TableGSet *BLI_table_gset_new_ex(const char *info, int size) { TableGSet *ts = MEM_new(info); #ifdef USE_TGSET_SMALLHASH ts->ptr_to_idx = static_cast(MEM_cnew("table gset smallhash")); - BLI_smallhash_init(PTR_TO_IDX(->ptr_to_idx)); + BLI_smallhash_init_ex(PTR_TO_IDX(->ptr_to_idx), size); #else ts->ptr_to_idx = static_cast(MEM_new>("ts->ptr_to_idx")); #endif @@ -51,6 +37,11 @@ TableGSet *BLI_table_gset_new_ex(const char *info, int size) return ts; } +TableGSet *BLI_table_gset_new(const char *info) +{ + return BLI_table_gset_new_ex(info, 0); +} + void BLI_table_gset_free(TableGSet *ts, GHashKeyFreeFP freefp) { MEM_SAFE_FREE(ts->elems); @@ -62,7 +53,7 @@ void BLI_table_gset_free(TableGSet *ts, GHashKeyFreeFP freefp) MEM_delete>(PTR_TO_IDX(ts)); #endif - MEM_freeN(ts); + MEM_delete(ts); } static void table_gset_resize(TableGSet *ts) @@ -79,31 +70,36 @@ static void table_gset_resize(TableGSet *ts) ts->elems = static_cast(MEM_reallocN(ts->elems, newsize * sizeof(void *))); } +#ifdef USE_TGSET_SMALLHASH + BLI_smallhash_clear(PTR_TO_IDX(ts)); +#else + PTR_TO_IDX(ts)->clear(); +#endif + /* Compact. */ - int i = 0, j = 0; - for (i = 0; i < ts->cur; i++) { + int compact_i = 0; + for (int i = 0; i < ts->cur; i++) { void *elem2 = ts->elems[i]; if (elem2) { #ifdef USE_TGSET_SMALLHASH void **val; BLI_smallhash_ensure_p(PTR_TO_IDX(ts), (uintptr_t)elem2, &val); - // BLI_smallhash_insert(PTR_TO_IDX(ts), elem2, (void *)j); - *val = POINTER_FROM_INT(j); + *val = POINTER_FROM_INT(compact_i); #else - PTR_TO_IDX(ts)->add_overwrite(elem2, j); + PTR_TO_IDX(ts)->add_overwrite(elem2, compact_i); #endif - ts->elems[j++] = elem2; + ts->elems[compact_i++] = elem2; } } + ts->cur = compact_i; ts->size = (int)newsize; - ts->cur = j; } } -bool BLI_table_gset_add(TableGSet *ts, void *elem) +ATTR_NO_OPT bool BLI_table_gset_add(TableGSet *ts, void *elem) { table_gset_resize(ts); @@ -124,6 +120,7 @@ bool BLI_table_gset_add(TableGSet *ts, void *elem) auto createfn = [&](int *value) { *value = ts->cur; ts->elems[ts->cur++] = elem; + ts->length++; return true; }; auto modifyfn = [&](int *value) { return false; }; @@ -132,7 +129,7 @@ bool BLI_table_gset_add(TableGSet *ts, void *elem) #endif } -void BLI_table_gset_insert(TableGSet *ts, void *elem) +ATTR_NO_OPT void BLI_table_gset_insert(TableGSet *ts, void *elem) { table_gset_resize(ts); @@ -148,9 +145,7 @@ void BLI_table_gset_insert(TableGSet *ts, void *elem) void BLI_table_gset_remove(TableGSet *ts, void *elem, GHashKeyFreeFP freefp) { - if (!elem || !ts) { - return; - } + BLI_assert(ts && elem); #ifdef USE_TGSET_SMALLHASH int *idx = (int *)BLI_smallhash_lookup_p(PTR_TO_IDX(ts), (uintptr_t)elem); @@ -178,7 +173,7 @@ void BLI_table_gset_remove(TableGSet *ts, void *elem, GHashKeyFreeFP freefp) ts->elems[idx2] = nullptr; } -bool BLI_table_gset_haskey(TableGSet *ts, void *elem) +ATTR_NO_OPT bool BLI_table_gset_haskey(TableGSet *ts, void *elem) { #ifdef USE_TGSET_SMALLHASH return BLI_smallhash_haskey(PTR_TO_IDX(ts), (uintptr_t)elem); diff --git a/source/blender/draw/engines/overlay/overlay_sculpt.cc b/source/blender/draw/engines/overlay/overlay_sculpt.cc index af6a3299ced..ffeb5c1b94b 100644 --- a/source/blender/draw/engines/overlay/overlay_sculpt.cc +++ b/source/blender/draw/engines/overlay/overlay_sculpt.cc @@ -64,7 +64,8 @@ void OVERLAY_sculpt_cache_populate(OVERLAY_Data *vedata, Object *ob) } if (use_pbvh) { - DRW_shgroup_call_sculpt(pd->sculpt_mask_grp, ob, false, true, true, false, false, false); + bool use_ids = draw_ctx->v3d->overlay.sculpt_flag & V3D_OVERLAY_SCULPT_SHOW_IDS; + DRW_shgroup_call_sculpt(pd->sculpt_mask_grp, ob, false, true, true, false, false, use_ids); } else { sculpt_overlays = DRW_mesh_batch_cache_get_sculpt_overlays(static_cast(ob->data)); diff --git a/source/blender/draw/intern/draw_manager_data.cc b/source/blender/draw/intern/draw_manager_data.cc index bfe6ca90567..1f4a5e64f2d 100644 --- a/source/blender/draw/intern/draw_manager_data.cc +++ b/source/blender/draw/intern/draw_manager_data.cc @@ -1409,7 +1409,7 @@ void DRW_shgroup_call_sculpt(DRWShadingGroup *shgroup, attrs[attrs_num++].type = (eCustomDataType)CD_PBVH_FSET_TYPE; } - if (1||use_id) { + if (use_id) { attrs[attrs_num].type = (eCustomDataType)CD_PBVH_ID_TYPE; attrs[attrs_num].domain = ATTR_DOMAIN_POINT; attrs_num++; diff --git a/source/blender/draw/intern/draw_pbvh.cc b/source/blender/draw/intern/draw_pbvh.cc index 7f23bb4d423..c1b0d85b9fa 100644 --- a/source/blender/draw/intern/draw_pbvh.cc +++ b/source/blender/draw/intern/draw_pbvh.cc @@ -1154,7 +1154,9 @@ struct PBVHBatches { GPU_vertformat_attr_add(&format, "msk", GPU_COMP_U8, 1, GPU_FETCH_INT_TO_FLOAT_UNIT); break; case CD_PBVH_ID_TYPE: + name = "vertex_id"; GPU_vertformat_attr_add(&format, "eid", GPU_COMP_I32, 1, GPU_FETCH_INT); + need_aliases = false; break; case CD_PROP_FLOAT: GPU_vertformat_attr_add(&format, "f", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); diff --git a/source/blender/editors/object/object_relations.c b/source/blender/editors/object/object_relations.c index 7e373d758f9..a6f6ec53bda 100644 --- a/source/blender/editors/object/object_relations.c +++ b/source/blender/editors/object/object_relations.c @@ -2843,7 +2843,7 @@ static void drop_named_material_face_set_slots_update(bContext *C, const int face_set_id = ED_sculpt_face_sets_active_update_and_get(C, ob, mval); int *face_sets = (int *)CustomData_get_layer_named( - &mesh->pdata, CD_PROP_INT32, ".sculpt_face_sets"); + &mesh->pdata, CD_PROP_INT32, ".sculpt_face_set"); int *mat_nr = (int *)CustomData_get_layer_named(&mesh->pdata, CD_PROP_INT32, "material_index"); if (!mat_nr) { diff --git a/source/blender/editors/sculpt_paint/sculpt.cc b/source/blender/editors/sculpt_paint/sculpt.cc index 99db7e910b0..cda216247bc 100644 --- a/source/blender/editors/sculpt_paint/sculpt.cc +++ b/source/blender/editors/sculpt_paint/sculpt.cc @@ -4077,16 +4077,16 @@ bool SCULPT_needs_area_normal(SculptSession *ss, Sculpt *sd, Brush *brush) /* Note: we do the topology update before any brush actions to avoid * issues with the proxies. The size of the proxy can't change, so * topology must be updated first. */ -static void sculpt_topology_update(Sculpt *sd, - Object *ob, - Brush *brush, - UnifiedPaintSettings * /* ups */, - PaintModeSettings * /*paint_mode_settings*/) +ATTR_NO_OPT static void sculpt_topology_update(Sculpt *sd, + Object *ob, + Brush *brush, + UnifiedPaintSettings * /* ups */, + PaintModeSettings * /*paint_mode_settings*/) { SculptSession *ss = ob->sculpt; /* build brush radius scale */ - float radius_scale = 1.0f; + float radius_scale = ss->cached_dyntopo.radius_scale; if ((brush->dyntopo.flag & DYNTOPO_DISABLED) || !(sd->flags & SCULPT_DYNTOPO_ENABLED)) { return; @@ -4133,9 +4133,9 @@ static void sculpt_topology_update(Sculpt *sd, } /* Force both subdivide and collapse for simplify brush. */ - //XXX done with inherit flags now + // XXX done with inherit flags now if (brush->sculpt_tool == SCULPT_TOOL_SIMPLIFY) { - //mode |= PBVH_Collapse | PBVH_Subdivide; + // mode |= PBVH_Collapse | PBVH_Subdivide; } SculptSearchSphereData sdata{}; @@ -6570,17 +6570,35 @@ static void sculpt_stroke_update_step(bContext *C, float dyntopo_spacing = float(ss->cached_dyntopo.spacing) / 50.0f; bool do_dyntopo = SCULPT_stroke_is_dynamic_topology(ss, brush); - do_dyntopo = do_dyntopo && - (ss->cache->stroke_distance_t - ss->cache->last_dyntopo_t) > dyntopo_spacing; + + if (dyntopo_spacing > 0.0f) { + do_dyntopo = do_dyntopo && + (ss->cache->stroke_distance_t - ss->cache->last_dyntopo_t) > dyntopo_spacing; + } if (do_dyntopo) { ss->cache->last_dyntopo_t = ss->cache->stroke_distance_t; + + /* Note: dyntopo repeats happen after the dab. */ do_symmetrical_brush_actions(sd, ob, sculpt_topology_update, ups, &tool_settings->paint_mode); } do_symmetrical_brush_actions(sd, ob, do_brush_action, ups, &tool_settings->paint_mode); sculpt_combine_proxies(sd, ob); + if (do_dyntopo && ss->cached_dyntopo.repeat) { + float3 location = ss->cache->true_location; + + add_v3_v3(cache->true_location, cache->grab_delta); + + for (int i = 0; i < ss->cached_dyntopo.repeat; i++) { + do_symmetrical_brush_actions( + sd, ob, sculpt_topology_update, ups, &tool_settings->paint_mode); + } + + copy_v3_v3(ss->cache->true_location, location); + } + /* Hack to fix noise texture tearing mesh. */ sculpt_fix_noise_tear(sd, ob); diff --git a/source/blender/editors/sculpt_paint/sculpt_api.cc b/source/blender/editors/sculpt_paint/sculpt_api.cc index 0fe8bc614e7..c596a71f92c 100644 --- a/source/blender/editors/sculpt_paint/sculpt_api.cc +++ b/source/blender/editors/sculpt_paint/sculpt_api.cc @@ -631,4 +631,5 @@ void SCULPT_apply_dyntopo_settings(SculptSession *ss, Sculpt *sculpt, Brush *bru ds_final->radius_scale = ds_final->inherit & DYNTOPO_INHERIT_RADIUS_SCALE ? ds2->radius_scale : ds1->radius_scale; ds_final->spacing = ds_final->inherit & DYNTOPO_INHERIT_SPACING ? ds2->spacing : ds1->spacing; + ds_final->repeat = ds_final->inherit & DYNTOPO_INHERIT_REPEAT ? ds2->repeat : ds1->repeat; } diff --git a/source/blender/editors/sculpt_paint/sculpt_undo.cc b/source/blender/editors/sculpt_paint/sculpt_undo.cc index 04eff63c7ea..3d44bd2121d 100644 --- a/source/blender/editors/sculpt_paint/sculpt_undo.cc +++ b/source/blender/editors/sculpt_paint/sculpt_undo.cc @@ -168,6 +168,8 @@ typedef struct SculptUndoStep { #endif } SculptUndoStep; +extern "C" void BKE_pbvh_bmesh_check_nodes(PBVH *pbvh); + static UndoSculpt *sculpt_undo_get_nodes(void); static bool sculpt_attribute_ref_equals(SculptAttrRef *a, SculptAttrRef *b); static void sculpt_save_active_attribute(Object *ob, SculptAttrRef *attr); @@ -714,7 +716,9 @@ static void bmesh_undo_on_vert_add(BMVert *v, void *userdata) data->balance_pbvh = true; - // let face add vert + /* Flag vert as unassigned to a PBVH node; it'll be added to pbvh when + * its owning faces are. + */ BM_ELEM_CD_SET_INT(v, data->cd_vert_node_offset, -1); *(int *)BM_ELEM_CD_GET_VOID_P(v, data->cd_boundary_flag) |= SCULPT_BOUNDARY_NEEDS_UPDATE; @@ -736,6 +740,13 @@ static void bmesh_undo_on_face_kill(BMFace *f, void *userdata) BKE_pbvh_bmesh_mark_node_regen(data->pbvh, node); } + BMLoop *l = f->l_first; + do { + MSculptVert *mv = BKE_PBVH_SCULPTVERT(data->cd_sculpt_vert, l->v); + MV_ADD_FLAG(mv, SCULPTVERT_NEED_DISK_SORT | SCULPTVERT_NEED_VALENCE); + *(int *)BM_ELEM_CD_GET_VOID_P(l->v, data->cd_boundary_flag) |= SCULPT_BOUNDARY_NEEDS_UPDATE; + } while ((l = l->next) != f->l_first); + // data->do_full_recalc = true; data->balance_pbvh = true; } @@ -756,7 +767,11 @@ static void bmesh_undo_on_face_add(BMFace *f, void *userdata) *(int *)BM_ELEM_CD_GET_VOID_P(l->v, data->cd_boundary_flag) |= SCULPT_BOUNDARY_NEEDS_UPDATE; MSculptVert *mv = BKE_PBVH_SCULPTVERT(data->cd_sculpt_vert, l->v); - MV_ADD_FLAG(mv, SCULPTVERT_NEED_DISK_SORT); + MV_ADD_FLAG(mv, SCULPTVERT_NEED_DISK_SORT | SCULPTVERT_NEED_VALENCE); + + if (f->len > 3) { + MV_ADD_FLAG(mv, SCULPTVERT_NEED_TRIANGULATE); + } int ni_l = BM_ELEM_CD_GET_INT(l->v, data->cd_vert_node_offset); @@ -949,6 +964,7 @@ static void sculpt_undo_bmesh_restore_generic(SculptUndoNode *unode, Object *ob, unode->applied = true; } + BKE_pbvh_bmesh_check_nodes(ss->pbvh); update_unode_bmesh_memsize(unode); if (!data.do_full_recalc) { @@ -1062,6 +1078,7 @@ static void sculpt_undo_bmesh_restore_begin( #endif } + BKE_pbvh_bmesh_check_nodes(ss->pbvh); SCULPT_dynamic_topology_disable(C, unode); unode->applied = false; } @@ -1086,6 +1103,7 @@ static void sculpt_undo_bmesh_restore_begin( } if (ss->bm) { + BKE_pbvh_bmesh_check_nodes(ss->pbvh); BM_mesh_elem_index_ensure(ss->bm, BM_VERT | BM_FACE); } } @@ -1112,6 +1130,7 @@ static void sculpt_undo_bmesh_restore_end( #endif unode->applied = false; + BKE_pbvh_bmesh_check_nodes(ss->pbvh); } else { #if 1 @@ -1126,6 +1145,8 @@ static void sculpt_undo_bmesh_restore_end( else { BM_log_redo_skip(ss->bm, ss->bm_log); } + + BKE_pbvh_bmesh_check_nodes(ss->pbvh); } #endif @@ -2343,7 +2364,9 @@ bool SCULPT_ensure_dyntopo_node_undo(Object *ob, unode->nodemap_size = newsize; } - if (unode->nodemap[n] & (1 << type)) { + bool check = !((type | extraType) & (SCULPT_UNDO_COORDS | SCULPT_UNDO_COLOR | SCULPT_UNDO_MASK | + SCULPT_UNDO_FACE_SETS)); + if (check && unode->nodemap[n] & (1 << type)) { return false; } diff --git a/source/blender/makesdna/DNA_brush_enums.h b/source/blender/makesdna/DNA_brush_enums.h index 6eec7f41547..8e36e739cc3 100644 --- a/source/blender/makesdna/DNA_brush_enums.h +++ b/source/blender/makesdna/DNA_brush_enums.h @@ -678,13 +678,14 @@ typedef enum eDynTopoInheritFlags { DYNTOPO_INHERIT_SPACING = 1 << 15, DYNTOPO_INHERIT_DETAIL_SIZE = 1 << 16, DYNTOPO_INHERIT_RADIUS_SCALE = 1 << 17, - DYNTOPO_INHERIT_MAX_FLAGS = 18, + DYNTOPO_INHERIT_REPEAT = 1 << 18, + DYNTOPO_INHERIT_MAX_FLAGS = 19, // make sure to update DYNTOPO_INHERIT_BITMASK when adding flags here } eDynTopoInheritFlags; -ENUM_OPERATORS(eDynTopoInheritFlags, DYNTOPO_INHERIT_RADIUS_SCALE); +ENUM_OPERATORS(eDynTopoInheritFlags, DYNTOPO_INHERIT_REPEAT); // represents all possible inherit flags -#define DYNTOPO_INHERIT_BITMASK ((1 << 18) - 1) +#define DYNTOPO_INHERIT_BITMASK ((1 << DYNTOPO_INHERIT_MAX_FLAGS) - 1) // dyntopo mode enum { diff --git a/source/blender/makesdna/DNA_brush_types.h b/source/blender/makesdna/DNA_brush_types.h index 2d687a20754..6aa06721df9 100644 --- a/source/blender/makesdna/DNA_brush_types.h +++ b/source/blender/makesdna/DNA_brush_types.h @@ -154,6 +154,7 @@ typedef struct DynTopoSettings { int inherit; int spacing; float radius_scale; + int repeat, _pad[1]; } DynTopoSettings; typedef struct BrushCurvesSculptSettings { diff --git a/source/blender/makesrna/intern/rna_brush.c b/source/blender/makesrna/intern/rna_brush.c index ef2bc13a2ab..eddd1f17d83 100644 --- a/source/blender/makesrna/intern/rna_brush.c +++ b/source/blender/makesrna/intern/rna_brush.c @@ -400,6 +400,11 @@ static EnumPropertyItem rna_enum_brush_dyntopo_inherit[] = { {DYNTOPO_CLEANUP, "CLEANUP", ICON_NONE, "Cleanup", ""}, {DYNTOPO_INHERIT_DETAIL_SIZE, "DETAIL_SIZE", ICON_NONE, "Detail Size", ""}, {DYNTOPO_INHERIT_RADIUS_SCALE, "RADIUS_SCALE", ICON_NONE, "Radius Scale", ""}, + {DYNTOPO_INHERIT_REPEAT, + "REPEAT", + ICON_NONE, + "Repeat", + "How many extra times to run the dyntopo remesher."}, {0, NULL, 0, NULL, NULL}, }; @@ -1259,10 +1264,12 @@ static void rna_def_dyntopo_settings(BlenderRNA *brna) prop = RNA_def_property(srna, "spacing", PROP_INT, PROP_PERCENTAGE); RNA_def_property_int_sdna(prop, NULL, "spacing"); - RNA_def_property_range(prop, 1, 1000); - RNA_def_property_ui_range(prop, 1, 500, 5, -1); - RNA_def_property_ui_text( - prop, "Spacing", "Spacing between DynTopo daubs as a percentage of brush diameter"); + RNA_def_property_range(prop, 0, 1000); + RNA_def_property_ui_range(prop, 0, 500, 5, -1); + RNA_def_property_ui_text(prop, + "Spacing", + "Spacing between DynTopo daubs as a percentage of brush diameter; if " + "zero will use brush spacing"); RNA_def_property_update(prop, 0, "rna_Brush_dyntopo_update"); prop = RNA_def_property(srna, "detail_percent", PROP_FLOAT, PROP_PERCENTAGE); @@ -1358,12 +1365,18 @@ static void rna_def_dyntopo_settings(BlenderRNA *brna) RNA_def_property_flag(prop, PROP_ENUM_FLAG); RNA_def_property_ui_text(prop, "Inherit", "Which default dyntopo settings to use"); - prop = RNA_def_property(srna, "radius_scale", PROP_FLOAT, PROP_PERCENTAGE); + prop = RNA_def_property(srna, "radius_scale", PROP_FLOAT, PROP_FACTOR); RNA_def_property_float_sdna(prop, NULL, "radius_scale"); RNA_def_property_range(prop, 0.0f, 15.0f); RNA_def_property_ui_range(prop, 0.0f, 2.0f, 0.001, 4); RNA_def_property_ui_text(prop, "Scale dyntopo radius", ""); RNA_def_property_update(prop, 0, "rna_Brush_dyntopo_update"); + + prop = RNA_def_property(srna, "repeat", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, NULL, "repeat"); + RNA_def_property_range(prop, 0.0f, 15.0f); + RNA_def_property_ui_text(prop, "Repeat", "How many times to run the dyntopo remesher."); + RNA_def_property_update(prop, 0, "rna_Brush_dyntopo_update"); } static void rna_def_sculpt_capabilities(BlenderRNA *brna) diff --git a/source/creator/creator.c b/source/creator/creator.c index 2a9ae119dbf..f10f718fc56 100644 --- a/source/creator/creator.c +++ b/source/creator/creator.c @@ -274,9 +274,6 @@ void gmp_blender_init_allocator() * or exit immediately when running in background-mode. */ -void BKE_pbvh_system_init(void); -void BKE_pbvh_system_exit(void); - int main(int argc, #ifdef USE_WIN32_UNICODE_ARGS const char **UNUSED(argv_c) @@ -444,7 +441,6 @@ int main(int argc, DEG_register_node_types(); BKE_brush_system_init(); - BKE_pbvh_system_init(); RE_texture_rng_init(); BKE_callback_global_init(); -- 2.30.2 From cce5029a59658c75bd1cd4250cbc8731e70502bd Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Thu, 6 Apr 2023 14:55:16 -0700 Subject: [PATCH 054/279] temp-sculpt-dyntopo: Fix versioning bug --- source/blender/blenkernel/intern/customdata.cc | 12 ++++++------ source/blender/blenloader/intern/versioning_300.cc | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/source/blender/blenkernel/intern/customdata.cc b/source/blender/blenkernel/intern/customdata.cc index b7079e6b640..2fcd932ce69 100644 --- a/source/blender/blenkernel/intern/customdata.cc +++ b/source/blender/blenkernel/intern/customdata.cc @@ -5750,12 +5750,12 @@ static void write_grid_paint_mask(BlendWriter *writer, } } -void CustomData_blend_write(BlendWriter *writer, - CustomData *data, - Span layers_to_write, - int count, - eCustomDataMask cddata_mask, - ID *id) +ATTR_NO_OPT void CustomData_blend_write(BlendWriter *writer, + CustomData *data, + Span layers_to_write, + int count, + eCustomDataMask cddata_mask, + ID *id) { /* write external customdata (not for undo) */ if (data->external && !BLO_write_is_undo(writer)) { diff --git a/source/blender/blenloader/intern/versioning_300.cc b/source/blender/blenloader/intern/versioning_300.cc index ea42239827e..5020ba72ba7 100644 --- a/source/blender/blenloader/intern/versioning_300.cc +++ b/source/blender/blenloader/intern/versioning_300.cc @@ -4352,7 +4352,7 @@ void blo_do_versions_300(FileData *fd, Library * /*lib*/, Main *bmain) for (int j = 0; j < data->totlayer; j++) { /* CD_DYNTOPO_VERT used to be CD_MESH_ID. */ if (data->layers[j].type == CD_DYNTOPO_VERT) { - CustomData_free_layer(data, CD_DYNTOPO_VERT, 0, 0); + CustomData_free_layer(data, CD_DYNTOPO_VERT, 0, j); j--; } } -- 2.30.2 From 0acc6c51d6f6f82a9a1ba3f878c6e526f65d5abf Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Sun, 9 Apr 2023 03:06:00 -0700 Subject: [PATCH 055/279] temp-sculpt-dyntopo: Fix bmesh toolflags bug and curvature bug * Fixed a few bugs with handling of CD_TOOLFLAGS layer. * Sculpt curvature API now handles completely flat geometry. --- .../blender/blenkernel/intern/customdata.cc | 22 +++++----- .../blender/blenkernel/intern/pbvh_bmesh.cc | 12 +++--- source/blender/bmesh/intern/bmesh_construct.c | 40 +++++++++++++------ source/blender/bmesh/intern/bmesh_core.c | 19 +++++---- source/blender/bmesh/intern/bmesh_idmap.cc | 2 +- source/blender/bmesh/intern/bmesh_interp.c | 8 ++-- source/blender/bmesh/intern/bmesh_mesh.cc | 39 ------------------ source/blender/bmesh/intern/bmesh_operators.c | 4 +- source/blender/editors/sculpt_paint/sculpt.cc | 10 ++--- .../editors/sculpt_paint/sculpt_curvature.cc | 28 +++++++++++++ 10 files changed, 97 insertions(+), 87 deletions(-) diff --git a/source/blender/blenkernel/intern/customdata.cc b/source/blender/blenkernel/intern/customdata.cc index 72fd3fbdde7..16ada41a39d 100644 --- a/source/blender/blenkernel/intern/customdata.cc +++ b/source/blender/blenkernel/intern/customdata.cc @@ -4433,11 +4433,11 @@ void CustomData_bmesh_swap_data(CustomData *source, } } -ATTR_NO_OPT void CustomData_bmesh_copy_data_exclude_by_type(const CustomData *source, - CustomData *dest, - void *src_block, - void **dest_block, - const eCustomDataMask mask_exclude) +void CustomData_bmesh_copy_data_exclude_by_type(const CustomData *source, + CustomData *dest, + void *src_block, + void **dest_block, + const eCustomDataMask mask_exclude) { /* Note that having a version of this function without a 'mask_exclude' * would cause too much duplicate code, so add a check instead. */ @@ -5744,12 +5744,12 @@ static void write_grid_paint_mask(BlendWriter *writer, } } -ATTR_NO_OPT void CustomData_blend_write(BlendWriter *writer, - CustomData *data, - Span layers_to_write, - int count, - eCustomDataMask cddata_mask, - ID *id) +void CustomData_blend_write(BlendWriter *writer, + CustomData *data, + Span layers_to_write, + int count, + eCustomDataMask cddata_mask, + ID *id) { /* write external customdata (not for undo) */ if (data->external && !BLO_write_is_undo(writer)) { diff --git a/source/blender/blenkernel/intern/pbvh_bmesh.cc b/source/blender/blenkernel/intern/pbvh_bmesh.cc index 49aa46020a9..368bd016d9d 100644 --- a/source/blender/blenkernel/intern/pbvh_bmesh.cc +++ b/source/blender/blenkernel/intern/pbvh_bmesh.cc @@ -119,7 +119,7 @@ void pbvh_bmesh_check_nodes_simple(PBVH *pbvh) } } -ATTR_NO_OPT void pbvh_bmesh_check_nodes(PBVH *pbvh) +void pbvh_bmesh_check_nodes(PBVH *pbvh) { for (int i = 0; i < pbvh->totnode; i++) { PBVHNode *node = pbvh->nodes + i; @@ -240,12 +240,12 @@ ATTR_NO_OPT void pbvh_bmesh_check_nodes(PBVH *pbvh) } } -ATTR_NO_OPT extern "C" void BKE_pbvh_bmesh_check_nodes(PBVH *pbvh) +extern "C" void BKE_pbvh_bmesh_check_nodes(PBVH *pbvh) { pbvh_bmesh_check_nodes(pbvh); } #else -ATTR_NO_OPT extern "C" void BKE_pbvh_bmesh_check_nodes(PBVH *pbvh) {} +extern "C" void BKE_pbvh_bmesh_check_nodes(PBVH *pbvh) {} #endif /** \} */ @@ -2694,7 +2694,7 @@ bool BKE_pbvh_bmesh_update_topology_nodes(PBVH *pbvh, updatePBVH, mask_cb, mask_cb_data, - 4096, //is_snake_hook ? 4096 : 4096, + 4096, // is_snake_hook ? 4096 : 4096, disable_surface_relax, is_snake_hook); @@ -2894,7 +2894,7 @@ static uintptr_t tri_loopkey(BMLoop *l, int mat_nr, int cd_fset, int cd_uvs[], i * (currently just raycast), store the node's triangles and vertices. * * Skips triangles that are hidden. */ -ATTR_NO_OPT bool BKE_pbvh_bmesh_check_tris(PBVH *pbvh, PBVHNode *node) +bool BKE_pbvh_bmesh_check_tris(PBVH *pbvh, PBVHNode *node) { BMesh *bm = pbvh->header.bm; @@ -3747,7 +3747,7 @@ static void pbvh_bmesh_balance_tree(PBVH *pbvh) } /* Fix any orphaned empty leaves that survived other stages of culling.*/ -ATTR_NO_OPT static void pbvh_fix_orphan_leaves(PBVH *pbvh) +static void pbvh_fix_orphan_leaves(PBVH *pbvh) { for (int i = 0; i < pbvh->totnode; i++) { PBVHNode *node = pbvh->nodes + i; diff --git a/source/blender/bmesh/intern/bmesh_construct.c b/source/blender/bmesh/intern/bmesh_construct.c index 51c8393096a..66f3d5d987b 100644 --- a/source/blender/bmesh/intern/bmesh_construct.c +++ b/source/blender/bmesh/intern/bmesh_construct.c @@ -417,9 +417,13 @@ static void bm_vert_attrs_copy( copy_v3_v3(v_dst->no, v_src->no); } - CustomData_bmesh_free_block_data_exclude_by_type(&bm_dst->vdata, v_dst->head.data, mask_exclude); - CustomData_bmesh_copy_data_exclude_by_type( - &bm_src->vdata, &bm_dst->vdata, v_src->head.data, &v_dst->head.data, mask_exclude); + CustomData_bmesh_free_block_data_exclude_by_type( + &bm_dst->vdata, v_dst->head.data, mask_exclude | CD_TOOLFLAGS); + CustomData_bmesh_copy_data_exclude_by_type(&bm_src->vdata, + &bm_dst->vdata, + v_src->head.data, + &v_dst->head.data, + mask_exclude | CD_TOOLFLAGS); } static void bm_edge_attrs_copy( @@ -430,9 +434,13 @@ static void bm_edge_attrs_copy( return; } - CustomData_bmesh_free_block_data_exclude_by_type(&bm_dst->edata, e_dst->head.data, mask_exclude); - CustomData_bmesh_copy_data_exclude_by_type( - &bm_src->edata, &bm_dst->edata, e_src->head.data, &e_dst->head.data, mask_exclude); + CustomData_bmesh_free_block_data_exclude_by_type( + &bm_dst->edata, e_dst->head.data, mask_exclude | CD_TOOLFLAGS); + CustomData_bmesh_copy_data_exclude_by_type(&bm_src->edata, + &bm_dst->edata, + e_src->head.data, + &e_dst->head.data, + mask_exclude | CD_TOOLFLAGS); } static void bm_loop_attrs_copy( @@ -443,9 +451,13 @@ static void bm_loop_attrs_copy( return; } - CustomData_bmesh_free_block_data_exclude_by_type(&bm_dst->ldata, l_dst->head.data, mask_exclude); - CustomData_bmesh_copy_data_exclude_by_type( - &bm_src->ldata, &bm_dst->ldata, l_src->head.data, &l_dst->head.data, mask_exclude); + CustomData_bmesh_free_block_data_exclude_by_type( + &bm_dst->ldata, l_dst->head.data, mask_exclude | CD_TOOLFLAGS); + CustomData_bmesh_copy_data_exclude_by_type(&bm_src->ldata, + &bm_dst->ldata, + l_src->head.data, + &l_dst->head.data, + mask_exclude | CD_TOOLFLAGS); } static void bm_face_attrs_copy( @@ -459,9 +471,13 @@ static void bm_face_attrs_copy( copy_v3_v3(f_dst->no, f_src->no); } - CustomData_bmesh_free_block_data_exclude_by_type(&bm_dst->pdata, f_dst->head.data, mask_exclude); - CustomData_bmesh_copy_data_exclude_by_type( - &bm_src->pdata, &bm_dst->pdata, f_src->head.data, &f_dst->head.data, mask_exclude); + CustomData_bmesh_free_block_data_exclude_by_type( + &bm_dst->pdata, f_dst->head.data, mask_exclude | CD_TOOLFLAGS); + CustomData_bmesh_copy_data_exclude_by_type(&bm_src->pdata, + &bm_dst->pdata, + f_src->head.data, + &f_dst->head.data, + mask_exclude | CD_TOOLFLAGS); f_dst->mat_nr = f_src->mat_nr; } diff --git a/source/blender/bmesh/intern/bmesh_core.c b/source/blender/bmesh/intern/bmesh_core.c index 9c32add671b..bf06543e3bd 100644 --- a/source/blender/bmesh/intern/bmesh_core.c +++ b/source/blender/bmesh/intern/bmesh_core.c @@ -1566,9 +1566,12 @@ BMVert *bmesh_kernel_split_edge_make_vert(BMesh *bm, BMVert *tv, BMEdge *e, BMEd int valence1, valence2; bool edok; int i; + > blender.exe !bmesh_kernel_split_edge_make_vert( + BMesh * bm, BMVert * tv, BMEdge * e, BMEdge * *r_e) Line 1576 C + #endif - BLI_assert(BM_vert_in_edge(e, tv) != false); + BLI_assert(BM_vert_in_edge(e, tv) != false); v_old = BM_edge_other_vert(e, tv); @@ -1881,13 +1884,13 @@ BMEdge *bmesh_kernel_join_edge_kill_vert(BMesh *bm, #ifdef JVKE_DEBUG -#ifdef _ -# undef _ -#endif +# ifdef _ +# undef _ +# endif -#define _(s) \ - case s: \ - return #s; +# define _(s) \ + case s: \ + return #s; static const char *get_err_code_str(BMeshInternalError code) { @@ -1928,7 +1931,7 @@ static const char *get_err_code_str(BMeshInternalError code) return "(unknown-code)"; } -#undef _ +# undef _ static char *get_err_str(int err) { diff --git a/source/blender/bmesh/intern/bmesh_idmap.cc b/source/blender/bmesh/intern/bmesh_idmap.cc index 1699068f4af..d709cc3e33f 100644 --- a/source/blender/bmesh/intern/bmesh_idmap.cc +++ b/source/blender/bmesh/intern/bmesh_idmap.cc @@ -500,7 +500,7 @@ void BM_idmap_assign(BMIdMap *idmap, BMElem *elem, int id) #endif } -ATTR_NO_OPT void BM_idmap_release(BMIdMap *idmap, BMElem *elem, bool clear_id) +void BM_idmap_release(BMIdMap *idmap, BMElem *elem, bool clear_id) { #ifdef DEBUG_BM_IDMAP idmap_check_elem(idmap, elem); diff --git a/source/blender/bmesh/intern/bmesh_interp.c b/source/blender/bmesh/intern/bmesh_interp.c index 851a0bd7abe..bc70626b370 100644 --- a/source/blender/bmesh/intern/bmesh_interp.c +++ b/source/blender/bmesh/intern/bmesh_interp.c @@ -791,10 +791,10 @@ static void update_data_blocks(BMesh *bm, CustomData *olddata, CustomData *data) BLI_array_staticdeclare(nocopy_layers, 32); /* Temporarily clear CD_FLAG_ELEM_NOCOPY flags. */ - for (int i = 0; i < data->totlayer; i++) { - if (data->layers[i].flag & CD_FLAG_ELEM_NOCOPY) { - data->layers[i].flag &= ~CD_FLAG_ELEM_NOCOPY; - BLI_array_append(nocopy_layers, data->layers + i); + for (int i = 0; i < olddata->totlayer; i++) { + if (olddata->layers[i].flag & CD_FLAG_ELEM_NOCOPY) { + olddata->layers[i].flag &= ~CD_FLAG_ELEM_NOCOPY; + BLI_array_append(nocopy_layers, olddata->layers + i); } } diff --git a/source/blender/bmesh/intern/bmesh_mesh.cc b/source/blender/bmesh/intern/bmesh_mesh.cc index 2578dafcb28..9b1866f42d7 100644 --- a/source/blender/bmesh/intern/bmesh_mesh.cc +++ b/source/blender/bmesh/intern/bmesh_mesh.cc @@ -159,45 +159,6 @@ BMesh *BM_mesh_create(const BMAllocTemplate *allocsize, const struct BMeshCreate /* allocate the memory pools for the mesh elements */ bm_mempool_init(bm, allocsize, params->use_toolflags); - bm->idmap.flag = 0; - - if (!params->temporary_ids) { - bm->idmap.flag |= BM_PERMANENT_IDS; - } - - if (params->id_map) { - bm->idmap.flag |= BM_HAS_ID_MAP; - } - - if (params->no_reuse_ids) { - bm->idmap.flag |= BM_NO_REUSE_IDS; - } - - if (params->create_unique_ids) { - bm->idmap.flag |= BM_HAS_IDS; - - bm->idmap.flag |= params->id_elem_mask; - -#ifndef WITH_BM_ID_FREELIST - bm->idmap.idtree = range_tree_uint_alloc(0, (uint)-1); -#endif - } - - if (bm->idmap.flag & BM_HAS_ID_MAP) { - if (bm->idmap.flag & BM_NO_REUSE_IDS) { - bm->idmap.ghash = BLI_ghash_ptr_new("idmap.ghash"); - } - else { - bm->idmap.map_size = BM_DEFAULT_IDMAP_SIZE; - bm->idmap.map = (BMElem **)MEM_callocN(sizeof(void *) * bm->idmap.map_size, "bmesh idmap"); - bm->idmap.ghash = nullptr; - } - } - else { - bm->idmap.map = nullptr; - bm->idmap.ghash = nullptr; - } - /* allocate one flag pool that we don't get rid of. */ bm->use_toolflags = params->use_toolflags; bm->toolflag_index = 0; diff --git a/source/blender/bmesh/intern/bmesh_operators.c b/source/blender/bmesh/intern/bmesh_operators.c index 4cea19ef7d2..a8610193152 100644 --- a/source/blender/bmesh/intern/bmesh_operators.c +++ b/source/blender/bmesh/intern/bmesh_operators.c @@ -1197,7 +1197,9 @@ static void bmo_flag_layer_alloc_do(BMesh *bm, BMElem *elem; int i; - const size_t old_totflags_size = (bm->totflags * sizeof(BMFlagLayer)); + size_t old_totflags_size = (bm->totflags * sizeof(BMFlagLayer)); + old_totflags_size = MIN2(old_totflags_size, sizeof(BMFlagLayer) * new_totflags); + BLI_mempool *oldpool = *pool_ptr; BLI_mempool *newpool = BLI_mempool_create( sizeof(BMFlagLayer) * new_totflags, totelem, 512, BLI_MEMPOOL_NOP); diff --git a/source/blender/editors/sculpt_paint/sculpt.cc b/source/blender/editors/sculpt_paint/sculpt.cc index 046f8da7706..99510b9cab9 100644 --- a/source/blender/editors/sculpt_paint/sculpt.cc +++ b/source/blender/editors/sculpt_paint/sculpt.cc @@ -4065,11 +4065,11 @@ bool SCULPT_needs_area_normal(SculptSession *ss, Sculpt *sd, Brush *brush) /* Note: we do the topology update before any brush actions to avoid * issues with the proxies. The size of the proxy can't change, so * topology must be updated first. */ -ATTR_NO_OPT static void sculpt_topology_update(Sculpt *sd, - Object *ob, - Brush *brush, - UnifiedPaintSettings * /* ups */, - PaintModeSettings * /*paint_mode_settings*/) +static void sculpt_topology_update(Sculpt *sd, + Object *ob, + Brush *brush, + UnifiedPaintSettings * /* ups */, + PaintModeSettings * /*paint_mode_settings*/) { SculptSession *ss = ob->sculpt; diff --git a/source/blender/editors/sculpt_paint/sculpt_curvature.cc b/source/blender/editors/sculpt_paint/sculpt_curvature.cc index 5b6977a96fb..f15113ca1ae 100644 --- a/source/blender/editors/sculpt_paint/sculpt_curvature.cc +++ b/source/blender/editors/sculpt_paint/sculpt_curvature.cc @@ -210,7 +210,35 @@ bool SCULPT_calc_principle_curvatures(SculptSession *ss, copy_v3_v3(out->principle[1], t); cross_v3_v3v3(out->principle[0], out->principle[1], no); + if (dot_v3v3(out->principle[0], out->principle[0]) > FLT_EPSILON * 50.0f) { + normalize_v3(out->principle[0]); + } + else { + zero_v3(out->principle[0]); + } + } + + if (is_zero_v3(out->principle[0])) { + /* Choose an orthoganoal direction. */ + copy_v3_v3(out->principle[0], no); + float axis[3] = {0.0f, 0.0f, 0.0f}; + + if (fabsf(no[0]) > fabs(no[1]) && fabs(no[0]) >= fabs(no[2])) { + axis[1] = 1.0f; + } + else if (fabsf(no[1]) > fabs(no[0]) && fabs(no[1]) >= fabs(no[2])) { + axis[2] = 1.0f; + } + else { + axis[0] = 1.0f; + } + + cross_v3_v3v3(out->principle[0], no, axis); + cross_v3_v3v3(out->principle[1], out->principle[0], no); + copy_v3_v3(out->principle[2], no); + normalize_v3(out->principle[0]); + normalize_v3(out->principle[1]); } return true; -- 2.30.2 From bd182032398c1ca57e99817115ff2cab66701dde Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Fri, 14 Apr 2023 11:14:15 -0700 Subject: [PATCH 056/279] temp-sculpt-dyntopo: Add comment for stroke api and remove ATTR_NO_OPTs --- source/blender/blenkernel/intern/dyntopo.cc | 36 +++++++++---------- .../editors/sculpt_paint/sculpt_intern.hh | 8 +++++ 2 files changed, 26 insertions(+), 18 deletions(-) diff --git a/source/blender/blenkernel/intern/dyntopo.cc b/source/blender/blenkernel/intern/dyntopo.cc index 9e374f8a447..5dc1e206275 100644 --- a/source/blender/blenkernel/intern/dyntopo.cc +++ b/source/blender/blenkernel/intern/dyntopo.cc @@ -504,7 +504,7 @@ static void long_edge_queue_edge_add_recursive(EdgeQueueContext *eq_ctx, return; } } - + edge_queue_insert_unified(eq_ctx, l_edge->e); if ((l_edge->radial_next != l_edge)) { @@ -620,9 +620,9 @@ BLI_INLINE int dyntopo_thread_rand(int seed) return (seed * multiplier + addend) & mask; } -ATTR_NO_OPT static void unified_edge_queue_task_cb(void *__restrict userdata, - const int n, - const TaskParallelTLS *__restrict /*tls*/) +static void unified_edge_queue_task_cb(void *__restrict userdata, + const int n, + const TaskParallelTLS *__restrict /*tls*/) { EdgeQueueThreadData *tdata = ((EdgeQueueThreadData *)userdata) + n; PBVH *pbvh = tdata->pbvh; @@ -2059,20 +2059,20 @@ float mask_cb_nop(PBVHVertRef /*vertex*/, void * /*userdata*/) } /* Collapse short edges, subdivide long edges */ -ATTR_NO_OPT extern "C" bool BKE_pbvh_bmesh_update_topology(PBVH *pbvh, - PBVHTopologyUpdateMode mode, - const float center[3], - const float view_normal[3], - float radius, - const bool use_frontface, - const bool use_projected, - int /*sym_axis*/, - bool updatePBVH, - DyntopoMaskCB mask_cb, - void *mask_cb_data, - int custom_max_steps, - bool disable_surface_relax, - bool is_snake_hook) +extern "C" bool BKE_pbvh_bmesh_update_topology(PBVH *pbvh, + PBVHTopologyUpdateMode mode, + const float center[3], + const float view_normal[3], + float radius, + const bool use_frontface, + const bool use_projected, + int /*sym_axis*/, + bool updatePBVH, + DyntopoMaskCB mask_cb, + void *mask_cb_data, + int custom_max_steps, + bool disable_surface_relax, + bool is_snake_hook) { /* Disable surface smooth if uv layers are present, to avoid expensive reprojection operation. */ if (!is_snake_hook && CustomData_has_layer(&pbvh->header.bm->ldata, CD_PROP_FLOAT2)) { diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.hh b/source/blender/editors/sculpt_paint/sculpt_intern.hh index a5785eb734c..a4c757e029f 100644 --- a/source/blender/editors/sculpt_paint/sculpt_intern.hh +++ b/source/blender/editors/sculpt_paint/sculpt_intern.hh @@ -19,6 +19,7 @@ #include "BLI_compiler_compat.h" #include "BLI_gsqueue.h" #include "BLI_threads.h" +#include "BLI_utildefines.h" #include "ED_view3d.h" @@ -2573,6 +2574,12 @@ int SCULPT_vertex_island_get(SculptSession *ss, PBVHVertRef vertex); #define SCULPT_vertex_attr_get BKE_sculpt_vertex_attr_get #define SCULPT_face_attr_get BKE_sculpt_face_attr_get +/* + * Stroke ID API. This API is used to detect if + * an element has already been processed for some task + * inside a given stroke. + */ + struct StrokeID { short id; short userflag; @@ -2587,6 +2594,7 @@ enum StrokeIDUser { STROKEID_USER_OCCLUSION = 1 << 5, STROKEID_USER_LAYER_BRUSH = 1 << 6, }; +ENUM_OPERATORS(StrokeIDUser, STROKEID_USER_LAYER_BRUSH); BLI_INLINE bool SCULPT_stroke_id_test(SculptSession *ss, PBVHVertRef vertex, StrokeIDUser user) { -- 2.30.2 From 8b35c62c2bac57f6d0848a687e71f99fbc7a0bd7 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Fri, 14 Apr 2023 12:06:56 -0700 Subject: [PATCH 057/279] temp-sculpt-dyntopo: Remove unrelated code --- source/blender/editors/object/CMakeLists.txt | 6 - source/blender/editors/object/object_edit.cc | 1 - source/blender/editors/object/object_intern.h | 1 - source/blender/editors/object/object_modes.cc | 12 -- .../blender/editors/object/object_modifier.cc | 12 +- .../blender/editors/object/object_relations.c | 104 +----------------- .../blender/editors/object/object_remesh.cc | 9 +- 7 files changed, 4 insertions(+), 141 deletions(-) diff --git a/source/blender/editors/object/CMakeLists.txt b/source/blender/editors/object/CMakeLists.txt index 8a70da9964b..9ed1f7046b7 100644 --- a/source/blender/editors/object/CMakeLists.txt +++ b/source/blender/editors/object/CMakeLists.txt @@ -22,7 +22,6 @@ set(INC ../../windowmanager ../../../../intern/clog ../../../../intern/guardedalloc - ../../../../intern/atomic # dna_type_offsets.h in BLO_read_write.h ${CMAKE_BINARY_DIR}/source/blender/makesdna/intern @@ -76,11 +75,6 @@ if(WITH_PYTHON) add_definitions(-DWITH_PYTHON) endif() - -if(WITH_INSTANT_MESHES) - add_definitions(-DWITH_INSTANT_MESHES) -endif() - if(WITH_EXPERIMENTAL_FEATURES) add_definitions(-DWITH_SIMULATION_DATABLOCK) add_definitions(-DWITH_POINT_CLOUD) diff --git a/source/blender/editors/object/object_edit.cc b/source/blender/editors/object/object_edit.cc index e79e407e5f3..9bc7b3a3320 100644 --- a/source/blender/editors/object/object_edit.cc +++ b/source/blender/editors/object/object_edit.cc @@ -61,7 +61,6 @@ #include "BKE_object.h" #include "BKE_paint.h" #include "BKE_particle.h" -#include "BKE_pbvh.h" #include "BKE_pointcache.h" #include "BKE_report.h" #include "BKE_scene.h" diff --git a/source/blender/editors/object/object_intern.h b/source/blender/editors/object/object_intern.h index a093d85ee9b..65b9674c6ee 100644 --- a/source/blender/editors/object/object_intern.h +++ b/source/blender/editors/object/object_intern.h @@ -347,7 +347,6 @@ void TRANSFORM_OT_vertex_random(struct wmOperatorType *ot); void OBJECT_OT_voxel_remesh(struct wmOperatorType *ot); void OBJECT_OT_voxel_size_edit(struct wmOperatorType *ot); void OBJECT_OT_quadriflow_remesh(struct wmOperatorType *ot); -void OBJECT_OT_instant_meshes_remesh(struct wmOperatorType *ot); /* object_transfer_data.c */ diff --git a/source/blender/editors/object/object_modes.cc b/source/blender/editors/object/object_modes.cc index 02c23cdaef7..36ff752e60d 100644 --- a/source/blender/editors/object/object_modes.cc +++ b/source/blender/editors/object/object_modes.cc @@ -7,17 +7,11 @@ * actual mode switching logic is per-object type. */ -#include "MEM_guardedalloc.h" - #include "DNA_gpencil_legacy_types.h" -#include "DNA_mesh_types.h" -#include "DNA_meshdata_types.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" #include "DNA_workspace_types.h" -#include "PIL_time.h" - #include "BLI_kdopbvh.h" #include "BLI_math.h" #include "BLI_utildefines.h" @@ -30,8 +24,6 @@ #include "BKE_gpencil_modifier_legacy.h" #include "BKE_layer.h" #include "BKE_main.h" -#include "BKE_mesh_runtime.h" -#include "BKE_mesh_types.h" #include "BKE_modifier.h" #include "BKE_object.h" #include "BKE_paint.h" @@ -44,7 +36,6 @@ #include "RNA_access.h" #include "RNA_define.h" -#include "RNA_enum_types.h" #include "DEG_depsgraph.h" #include "DEG_depsgraph_query.h" @@ -52,13 +43,10 @@ #include "ED_armature.h" #include "ED_gpencil_legacy.h" #include "ED_screen.h" -#include "ED_space_api.h" #include "ED_transform_snap_object_context.h" #include "ED_undo.h" #include "ED_view3d.h" -#include "UI_resources.h" - #include "WM_toolsystem.h" #include "ED_object.h" /* own include */ diff --git a/source/blender/editors/object/object_modifier.cc b/source/blender/editors/object/object_modifier.cc index 898f2b69c82..93341a0470e 100644 --- a/source/blender/editors/object/object_modifier.cc +++ b/source/blender/editors/object/object_modifier.cc @@ -1142,17 +1142,7 @@ bool ED_object_modifier_apply(Main *bmain, BKE_report(reports, RPT_ERROR, "Modifiers cannot be applied in edit mode"); return false; } - - bool allow_multi_user = mode == MODIFIER_APPLY_SHAPE; - if (md) { - const ModifierTypeInfo *mti = BKE_modifier_get_info((ModifierType)md->type); - - allow_multi_user |= ELEM( - mti->type, eModifierTypeType_NonGeometrical, eModifierTypeType_OnlyDeform); - } - - // bool allow_multi_user = md && md->type == eModifierType_DataTransfer || md->flag & ; - if (!allow_multi_user && ID_REAL_USERS(ob->data) > 1) { + if (mode != MODIFIER_APPLY_SHAPE && ID_REAL_USERS(ob->data) > 1) { BKE_report(reports, RPT_ERROR, "Modifiers cannot be applied to multi-user data"); return false; } diff --git a/source/blender/editors/object/object_relations.c b/source/blender/editors/object/object_relations.c index b5f1b22960c..12f119f68ef 100644 --- a/source/blender/editors/object/object_relations.c +++ b/source/blender/editors/object/object_relations.c @@ -22,7 +22,6 @@ #include "DNA_light_types.h" #include "DNA_material_types.h" #include "DNA_mesh_types.h" -#include "DNA_meshdata_types.h" #include "DNA_meta_types.h" #include "DNA_object_types.h" #include "DNA_particle_types.h" @@ -49,7 +48,6 @@ #include "BKE_context.h" #include "BKE_curve.h" #include "BKE_curves.h" -#include "BKE_customdata.h" #include "BKE_displist.h" #include "BKE_editmesh.h" #include "BKE_fcurve.h" @@ -71,11 +69,9 @@ #include "BKE_modifier.h" #include "BKE_node.h" #include "BKE_object.h" -#include "BKE_paint.h" #include "BKE_pointcloud.h" #include "BKE_report.h" #include "BKE_scene.h" -#include "BKE_screen.h" #include "BKE_speaker.h" #include "BKE_texture.h" #include "BKE_volume.h" @@ -101,7 +97,6 @@ #include "ED_mesh.h" #include "ED_object.h" #include "ED_screen.h" -#include "ED_sculpt.h" #include "ED_view3d.h" #include "MOD_nodes.h" @@ -2829,73 +2824,6 @@ char *ED_object_ot_drop_named_material_tooltip(bContext *C, const char *name, co return result; } -static void drop_named_material_face_set_slots_update(bContext *C, - Object *ob, - const wmEvent *event) -{ - Main *bmain = CTX_data_main(C); - Mesh *mesh = BKE_mesh_from_object(ob); - - bScreen *screen = CTX_wm_screen(C); - ARegion *region = BKE_screen_find_main_region_at_xy(screen, SPACE_VIEW3D, event->xy); - - const float mval[2] = {event->xy[0] - region->winrct.xmin, event->xy[1] - region->winrct.ymin}; - const int face_set_id = ED_sculpt_face_sets_active_update_and_get(C, ob, mval); - - int *face_sets = (int *)CustomData_get_layer_named( - &mesh->pdata, CD_PROP_INT32, ".sculpt_face_set"); - int *mat_nr = (int *)CustomData_get_layer_named(&mesh->pdata, CD_PROP_INT32, "material_index"); - - if (!mat_nr) { - printf("%s: no material index!\n", __func__); - return; - } - - bool create_new_slot = false; - short face_set_nr = -1; - - if (face_sets) { - for (int i = 0; i < mesh->totpoly; i++) { - if (face_sets[i] != face_set_id) { - continue; - } - face_set_nr = mat_nr[i]; - break; - } - - for (int i = 0; i < mesh->totpoly; i++) { - if (face_sets[i] == face_set_id) { - if (mat_nr[i] != face_set_nr) { - create_new_slot = true; - break; - } - } - else { - if (mat_nr[i] == face_set_nr) { - create_new_slot = true; - break; - } - } - } - } - - if (create_new_slot) { - BKE_object_material_slot_add(bmain, ob); - } - else { - ob->actcol = face_set_nr + 1; - } - - const short active_mat_slot = ob->actcol; - const short material_nr = active_mat_slot - 1; - for (int i = 0; i < mesh->totpoly; i++) { - if (face_sets[i] != face_set_id) { - continue; - } - mat_nr[i] = material_nr; - } -} - static int drop_named_material_invoke(bContext *C, wmOperator *op, const wmEvent *event) { Main *bmain = CTX_data_main(C); @@ -2910,10 +2838,6 @@ static int drop_named_material_invoke(bContext *C, wmOperator *op, const wmEvent return OPERATOR_CANCELLED; } - if (ob->mode == OB_MODE_SCULPT) { - drop_named_material_face_set_slots_update(C, ob, event); - } - BKE_object_material_assign(CTX_data_main(C), ob, ma, mat_slot, BKE_MAT_ASSIGN_USERPREF); DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM); @@ -2925,32 +2849,6 @@ static int drop_named_material_invoke(bContext *C, wmOperator *op, const wmEvent return OPERATOR_FINISHED; } -bool ED_operator_drop_material_poll(bContext *C) -{ - Scene *scene = CTX_data_scene(C); - Object *obact = CTX_data_active_object(C); - - if (scene == NULL || ID_IS_LINKED(scene)) { - CTX_wm_operator_poll_msg_set(C, "Missing scene in context"); - return false; - } - if (CTX_data_edit_object(C)) { - CTX_wm_operator_poll_msg_set(C, "Cannot be used in edit mode"); - return false; - } - - if (obact && !ELEM(obact->mode, OB_MODE_OBJECT, OB_MODE_SCULPT)) { - CTX_wm_operator_poll_msg_set(C, "Only supported in object and sculpt modes"); - return false; - } - - return true; -} - -/** - * Used for drop-box. - * Assigns to object under cursor, only first material slot. - */ void OBJECT_OT_drop_named_material(wmOperatorType *ot) { /* identifiers */ @@ -2959,7 +2857,7 @@ void OBJECT_OT_drop_named_material(wmOperatorType *ot) /* api callbacks */ ot->invoke = drop_named_material_invoke; - ot->poll = ED_operator_drop_material_poll; + ot->poll = ED_operator_objectmode_poll_msg; /* flags */ ot->flag = OPTYPE_UNDO | OPTYPE_INTERNAL; diff --git a/source/blender/editors/object/object_remesh.cc b/source/blender/editors/object/object_remesh.cc index baec56d897e..896487ce1e1 100644 --- a/source/blender/editors/object/object_remesh.cc +++ b/source/blender/editors/object/object_remesh.cc @@ -16,7 +16,6 @@ #include "BLI_math.h" #include "BLI_string.h" #include "BLI_string_utf8.h" -#include "BLI_threads.h" #include "BLI_utildefines.h" #include "DNA_mesh_types.h" @@ -36,7 +35,6 @@ #include "BKE_mesh_mirror.h" #include "BKE_mesh_remesh_voxel.h" #include "BKE_mesh_runtime.h" -#include "BKE_mesh_types.h" #include "BKE_modifier.h" #include "BKE_object.h" #include "BKE_paint.h" @@ -156,7 +154,6 @@ static int voxel_remesh_exec(bContext *C, wmOperator *op) if (ob->mode == OB_MODE_SCULPT) { ED_sculpt_undo_geometry_begin(ob, op); - ob->sculpt->needs_pbvh_rebuild = true; } if (mesh->flag & ME_REMESH_FIX_POLES && mesh->remesh_voxel_adaptivity <= 0.0f) { @@ -775,7 +772,7 @@ static void quadriflow_update_job(void *customdata, float progress, int *cancel) *(qj->progress) = progress; } -static Mesh *remesh_symmetry_bisect(Object *ob, Mesh *mesh, eSymmetryAxes symmetry_axes) +static Mesh *remesh_symmetry_bisect(Mesh *mesh, eSymmetryAxes symmetry_axes) { MirrorModifierData mmd = {{nullptr}}; mmd.tolerance = QUADRIFLOW_MIRROR_BISECT_TOLERANCE; @@ -796,10 +793,8 @@ static Mesh *remesh_symmetry_bisect(Object *ob, Mesh *mesh, eSymmetryAxes symmet zero_v3(plane_no); plane_no[axis] = -1.0f; mesh_bisect_temp = mesh_bisect; - mesh_bisect = BKE_mesh_mirror_bisect_on_mirror_plane_for_modifier( &mmd, mesh_bisect, axis, plane_co, plane_no); - if (mesh_bisect_temp != mesh_bisect) { BKE_id_free(nullptr, mesh_bisect_temp); } @@ -868,7 +863,7 @@ static void quadriflow_start_job(void *customdata, bool *stop, bool *do_update, bisect_mesh = BKE_mesh_copy_for_eval(mesh, false); /* Bisect the input mesh using the paint symmetry settings */ - bisect_mesh = remesh_symmetry_bisect(ob, bisect_mesh, qj->symmetry_axes); + bisect_mesh = remesh_symmetry_bisect(bisect_mesh, qj->symmetry_axes); new_mesh = BKE_mesh_remesh_quadriflow(bisect_mesh, qj->target_faces, -- 2.30.2 From c5d6a03dea7e9fd84f60faf21dab689e93841a60 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Fri, 14 Apr 2023 12:32:54 -0700 Subject: [PATCH 058/279] temp-sculpt-dyntopo: Finish merge --- source/blender/blenkernel/intern/brush.cc | 5 ++--- source/blender/editors/object/CMakeLists.txt | 1 + source/creator/CMakeLists.txt | 1 + 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/source/blender/blenkernel/intern/brush.cc b/source/blender/blenkernel/intern/brush.cc index 6ac976c3ad6..28496170846 100644 --- a/source/blender/blenkernel/intern/brush.cc +++ b/source/blender/blenkernel/intern/brush.cc @@ -2607,7 +2607,6 @@ struct ImBuf *BKE_brush_gen_radial_control_imbuf(Brush *br, bool secondary, bool return im; } -<<<<<<< HEAD bool BKE_brush_hard_edge_mode_get(const Scene *scene, const Brush *brush) { UnifiedPaintSettings *ups = &scene->toolsettings->unified_paint_settings; @@ -2637,7 +2636,8 @@ void BKE_brush_hard_edge_mode_set(Scene *scene, Brush *brush, bool val) float BKE_brush_fset_slide_get(const Scene *scene, const Brush *brush) { return BKE_brush_hard_edge_mode_get(scene, brush) ? 0.0f : brush->autosmooth_fset_slide; -======= +} + bool BKE_brush_has_cube_tip(const Brush *brush, ePaintMode paint_mode) { switch (paint_mode) { @@ -2659,5 +2659,4 @@ bool BKE_brush_has_cube_tip(const Brush *brush, ePaintMode paint_mode) } return false; ->>>>>>> main } diff --git a/source/blender/editors/object/CMakeLists.txt b/source/blender/editors/object/CMakeLists.txt index 9ed1f7046b7..19150682c2b 100644 --- a/source/blender/editors/object/CMakeLists.txt +++ b/source/blender/editors/object/CMakeLists.txt @@ -20,6 +20,7 @@ set(INC ../../render ../../shader_fx ../../windowmanager + ../../../../intern/atomic ../../../../intern/clog ../../../../intern/guardedalloc diff --git a/source/creator/CMakeLists.txt b/source/creator/CMakeLists.txt index 3462fcd5b99..f216648bf81 100644 --- a/source/creator/CMakeLists.txt +++ b/source/creator/CMakeLists.txt @@ -2,6 +2,7 @@ # Copyright 2006 Blender Foundation set(INC + ../../intern/atomic ../../intern/clog ../../intern/guardedalloc ../blender/blenkernel -- 2.30.2 From 52dd137e53f40e5c450b7e5e64b1c6510528e8df Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Sat, 15 Apr 2023 00:54:13 -0700 Subject: [PATCH 059/279] temp-sculpt-dyntopo: Fix bug in CustomData_copy_all_layout Also fixed bug in CustomData_layout_is_same. --- .../blender/blenkernel/intern/customdata.cc | 22 ++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/source/blender/blenkernel/intern/customdata.cc b/source/blender/blenkernel/intern/customdata.cc index b307b62dd87..02a2b9474be 100644 --- a/source/blender/blenkernel/intern/customdata.cc +++ b/source/blender/blenkernel/intern/customdata.cc @@ -81,11 +81,15 @@ BLI_STATIC_ASSERT(ARRAY_SIZE(((CustomData *)nullptr)->typemap) == CD_NUMTYPES, " static CLG_LogRef LOG = {"bke.customdata"}; -bool CustomData_layout_is_same(const CustomData *_a, const CustomData *_b) +ATTR_NO_OPT bool CustomData_layout_is_same(const CustomData *_a, const CustomData *_b) { CustomData a = *_a; CustomData b = *_b; + if (a.totlayer != b.totlayer) { + return false; + } + a.layers = b.layers = nullptr; a.pool = b.pool = nullptr; @@ -98,6 +102,7 @@ bool CustomData_layout_is_same(const CustomData *_a, const CustomData *_b) CustomDataLayer clb = _b->layers[i]; cla.data = clb.data = nullptr; + cla.default_data = clb.default_data = nullptr; if (memcmp((void *)&cla, (void *)&clb, sizeof(CustomDataLayer)) != 0) { return false; @@ -2436,7 +2441,8 @@ static bool customdata_typemap_is_valid(const CustomData *data) /* copies all customdata layers without allocating data, * and without respect to type masks or NO_COPY/etc flags*/ -void CustomData_copy_all_layout(const struct CustomData *source, struct CustomData *dest) +ATTR_NO_OPT void CustomData_copy_all_layout(const struct CustomData *source, + struct CustomData *dest) { *dest = *source; @@ -2446,11 +2452,17 @@ void CustomData_copy_all_layout(const struct CustomData *source, struct CustomDa if (source->layers) { dest->layers = static_cast( - MEM_mallocN(sizeof(*dest->layers) * source->totlayer, __func__)); + MEM_mallocN(sizeof(*dest->layers) * source->maxlayer, __func__)); for (int i = 0; i < source->totlayer; i++) { - dest->layers[i] = source->layers[i]; - dest->layers[i].data = nullptr; + CustomDataLayer *layer = &dest->layers[i]; + + *layer = source->layers[i]; + layer->data = nullptr; + + if (layer->default_data) { + layer->default_data = MEM_dupallocN(layer->default_data); + } } } -- 2.30.2 From 59801f80c1e844c815ee8c59a91fb94e1358dac8 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Mon, 17 Apr 2023 16:46:43 -0700 Subject: [PATCH 060/279] temp-sculpt-dyntopo: Fix bug with valence caching in PBVH_GRIDS --- source/blender/blenkernel/intern/paint.cc | 5 +++-- source/blender/blenkernel/intern/pbvh.cc | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/source/blender/blenkernel/intern/paint.cc b/source/blender/blenkernel/intern/paint.cc index 11675aa3ccd..5dbc4a28ae6 100644 --- a/source/blender/blenkernel/intern/paint.cc +++ b/source/blender/blenkernel/intern/paint.cc @@ -2617,8 +2617,6 @@ static PBVH *build_pbvh_from_ccg(Object *ob, SubdivCCG *subdiv_ccg) base_mesh, subdiv_ccg); - BKE_pbvh_set_sculpt_verts(pbvh, ss->msculptverts); - CustomData_reset(&ob->sculpt->temp_vdata); CustomData_reset(&ob->sculpt->temp_pdata); @@ -2636,8 +2634,11 @@ static PBVH *build_pbvh_from_ccg(Object *ob, SubdivCCG *subdiv_ccg) for (int i = 0; i < totvert; i++) { ss->msculptverts[i].stroke_id = -1; + ss->msculptverts[i].flag |= SCULPTVERT_NEED_VALENCE; } + BKE_pbvh_set_sculpt_verts(pbvh, ss->msculptverts); + return pbvh; } diff --git a/source/blender/blenkernel/intern/pbvh.cc b/source/blender/blenkernel/intern/pbvh.cc index cd9cd19885d..49f9b264469 100644 --- a/source/blender/blenkernel/intern/pbvh.cc +++ b/source/blender/blenkernel/intern/pbvh.cc @@ -4240,7 +4240,8 @@ void BKE_pbvh_get_vert_face_areas(PBVH *pbvh, PBVHVertRef vertex, float *r_areas } if (neighbors.size != valence) { - printf("%s: error!\n", __func__); + printf( + "%s: error! neighbors.size was %d expected %d\n", __func__, neighbors.size, valence); } if (totw < 0.000001f) { for (int i = 0; i < neighbors.size; i++) { -- 2.30.2 From 6d53e23e037e81c39f7aba47831f6d06c3bf012e Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Tue, 18 Apr 2023 18:28:25 -0700 Subject: [PATCH 061/279] temp-sculpt-dyntopo: bugfixes * Fixed crash in color sample (SKEY) tool * Fixed bugs in visibility code * Fixed nasty undo bugs when saving full meshes --- .../blender/blenkernel/intern/customdata.cc | 5 +- source/blender/blenkernel/intern/pbvh.cc | 20 ++- .../blender/blenkernel/intern/pbvh_bmesh.cc | 37 +++- source/blender/blenlib/intern/BLI_mempool.c | 6 +- .../blender/blenlib/intern/BLI_table_gset.cc | 6 +- source/blender/bmesh/intern/bmesh_idmap.cc | 1 + source/blender/bmesh/intern/bmesh_log.cc | 166 ++++++++++++------ .../blender/bmesh/intern/bmesh_log_intern.h | 5 +- .../blender/bmesh/intern/bmesh_mesh_convert.h | 1 - source/blender/draw/intern/draw_pbvh.cc | 13 +- source/blender/editors/sculpt_paint/sculpt.cc | 15 +- .../sculpt_paint/sculpt_automasking.cc | 2 +- .../editors/sculpt_paint/sculpt_face_set.cc | 14 +- .../editors/sculpt_paint/sculpt_ops.cc | 5 + .../editors/sculpt_paint/sculpt_undo.cc | 82 +++++++-- 15 files changed, 265 insertions(+), 113 deletions(-) diff --git a/source/blender/blenkernel/intern/customdata.cc b/source/blender/blenkernel/intern/customdata.cc index 02a2b9474be..a8b1f421888 100644 --- a/source/blender/blenkernel/intern/customdata.cc +++ b/source/blender/blenkernel/intern/customdata.cc @@ -81,7 +81,7 @@ BLI_STATIC_ASSERT(ARRAY_SIZE(((CustomData *)nullptr)->typemap) == CD_NUMTYPES, " static CLG_LogRef LOG = {"bke.customdata"}; -ATTR_NO_OPT bool CustomData_layout_is_same(const CustomData *_a, const CustomData *_b) +bool CustomData_layout_is_same(const CustomData *_a, const CustomData *_b) { CustomData a = *_a; CustomData b = *_b; @@ -2441,8 +2441,7 @@ static bool customdata_typemap_is_valid(const CustomData *data) /* copies all customdata layers without allocating data, * and without respect to type masks or NO_COPY/etc flags*/ -ATTR_NO_OPT void CustomData_copy_all_layout(const struct CustomData *source, - struct CustomData *dest) +void CustomData_copy_all_layout(const struct CustomData *source, struct CustomData *dest) { *dest = *source; diff --git a/source/blender/blenkernel/intern/pbvh.cc b/source/blender/blenkernel/intern/pbvh.cc index 49f9b264469..8d8a907acaf 100644 --- a/source/blender/blenkernel/intern/pbvh.cc +++ b/source/blender/blenkernel/intern/pbvh.cc @@ -1755,6 +1755,12 @@ static void pbvh_update_visibility_redraw(PBVH *pbvh, Span nodes, in data.nodes = nodes; data.flag = flag; + if (pbvh->header.type == PBVH_BMESH) { + for (PBVHNode *node : nodes) { + BKE_pbvh_bmesh_check_tris(pbvh, node); + } + } + TaskParallelSettings settings; BKE_pbvh_parallel_range_settings(&settings, true, nodes.size()); BLI_task_parallel_range( @@ -2331,7 +2337,7 @@ void BKE_pbvh_mark_rebuild_pixels(PBVH *pbvh) void BKE_pbvh_node_mark_update_visibility(PBVHNode *node) { node->flag |= PBVH_UpdateVisibility | PBVH_RebuildDrawBuffers | PBVH_UpdateDrawBuffers | - PBVH_UpdateRedraw | PBVH_UpdateTris; + PBVH_UpdateRedraw | PBVH_UpdateTris | PBVH_UpdateTriAreas; } void BKE_pbvh_vert_tag_update_normal_visibility(PBVHNode *node) @@ -4056,12 +4062,12 @@ static void pbvh_pmap_to_edges_add(PBVH *pbvh, (*len)++; } -ATTR_NO_OPT void BKE_pbvh_pmap_to_edges(PBVH *pbvh, - PBVHVertRef vertex, - int **r_edges, - int *r_edges_size, - bool *r_heap_alloc, - int **r_polys) +void BKE_pbvh_pmap_to_edges(PBVH *pbvh, + PBVHVertRef vertex, + int **r_edges, + int *r_edges_size, + bool *r_heap_alloc, + int **r_polys) { MeshElemMap *map = pbvh->pmap->pmap + vertex.i; int len = 0; diff --git a/source/blender/blenkernel/intern/pbvh_bmesh.cc b/source/blender/blenkernel/intern/pbvh_bmesh.cc index c9d2bece4d5..9524b7e5624 100644 --- a/source/blender/blenkernel/intern/pbvh_bmesh.cc +++ b/source/blender/blenkernel/intern/pbvh_bmesh.cc @@ -628,6 +628,7 @@ void pbvh_bmesh_vert_remove(PBVH *pbvh, BMVert *v) const int updateflag = PBVH_UpdateDrawBuffers | PBVH_UpdateBB | PBVH_UpdateTris | PBVH_UpdateNormals | PBVH_UpdateOtherVerts; + int ni = pbvh_bmesh_node_index_from_vert(pbvh, v); PBVHNode *v_node = pbvh_bmesh_node_from_vert(pbvh, v); if (v_node && v_node->bm_unique_verts) { @@ -1239,21 +1240,41 @@ static void pbvh_bmesh_regen_node_verts(PBVH *pbvh, PBVHNode *node) int osize = BLI_table_gset_len(node->bm_other_verts); TableGSet *old_unique_verts = node->bm_unique_verts; + TableGSet *old_other_verts = node->bm_other_verts; - BLI_table_gset_free(node->bm_other_verts, nullptr); + const int cd_vert_node = pbvh->cd_vert_node_offset; + const int ni = (int)(node - pbvh->nodes); + + auto check_vert = [&](BMVert *v) { + if (BM_elem_is_free(reinterpret_cast(v), BM_VERT)) { + printf("%s: corrupted vertex %p\n", __func__, v); + return; + } + int ni2 = BM_ELEM_CD_GET_INT(v, cd_vert_node); + + bool bad = ni2 == ni || ni2 < 0 || ni2 >= pbvh->totnode; + bad = bad || pbvh->nodes[ni2].flag & PBVH_Delete; + bad = bad || !(pbvh->nodes[ni2].flag & PBVH_Leaf); + + if (bad) { + BM_ELEM_CD_SET_INT(v, cd_vert_node, DYNTOPO_NODE_NONE); + } + }; BMVert *v; TGSET_ITER (v, old_unique_verts) { - BM_ELEM_CD_SET_INT(v, pbvh->cd_vert_node_offset, -1); + check_vert(v); + } + TGSET_ITER_END; + + TGSET_ITER (v, old_other_verts) { + check_vert(v); } TGSET_ITER_END; node->bm_unique_verts = BLI_table_gset_new("bm_unique_verts"); node->bm_other_verts = BLI_table_gset_new("bm_other_verts"); - const int cd_vert_node = pbvh->cd_vert_node_offset; - const int ni = (int)(node - pbvh->nodes); - bool update = false; BMFace *f; @@ -1279,6 +1300,11 @@ static void pbvh_bmesh_regen_node_verts(PBVH *pbvh, PBVHNode *node) TGSET_ITER_END; TGSET_ITER (v, old_unique_verts) { + if (BM_elem_is_free(reinterpret_cast(v), BM_VERT)) { + printf("%s: corrupted vertex %p\n", __func__, v); + continue; + } + if (BM_ELEM_CD_GET_INT(v, pbvh->cd_vert_node_offset) == -1) { // try to find node to insert into BMIter iter2; @@ -1333,6 +1359,7 @@ static void pbvh_bmesh_regen_node_verts(PBVH *pbvh, PBVHNode *node) } BLI_table_gset_free(old_unique_verts, nullptr); + BLI_table_gset_free(old_other_verts, nullptr); } void BKE_pbvh_bmesh_mark_node_regen(PBVH *pbvh, PBVHNode *node) diff --git a/source/blender/blenlib/intern/BLI_mempool.c b/source/blender/blenlib/intern/BLI_mempool.c index 2635439801f..bffc7e8adba 100644 --- a/source/blender/blenlib/intern/BLI_mempool.c +++ b/source/blender/blenlib/intern/BLI_mempool.c @@ -483,13 +483,13 @@ BLI_mempool *BLI_mempool_create_for_tasks(const unsigned int esize, return pool; } -ATTR_NO_OPT static void mempool_chunk_free(BLI_mempool_chunk *mpchunk, BLI_mempool *pool) +static void mempool_chunk_free(BLI_mempool_chunk *mpchunk, BLI_mempool *pool) { BLI_asan_unpoison(mpchunk, sizeof(BLI_mempool_chunk) + pool->esize * pool->csize); BLI_asan_safe_free(mpchunk); } -ATTR_NO_OPT static void mempool_chunk_free_all(BLI_mempool_chunk *mpchunk, BLI_mempool *pool) +static void mempool_chunk_free_all(BLI_mempool_chunk *mpchunk, BLI_mempool *pool) { BLI_mempool_chunk *mpchunk_next; @@ -756,7 +756,7 @@ void BLI_mempool_ignore_free(BLI_mempool *pool) * * \note doesn't protect against double frees, take care! */ -ATTR_NO_OPT void BLI_mempool_free(BLI_mempool *pool, void *addr) +void BLI_mempool_free(BLI_mempool *pool, void *addr) { mempool_unpoison(pool); diff --git a/source/blender/blenlib/intern/BLI_table_gset.cc b/source/blender/blenlib/intern/BLI_table_gset.cc index 53e7b540720..88129e45589 100644 --- a/source/blender/blenlib/intern/BLI_table_gset.cc +++ b/source/blender/blenlib/intern/BLI_table_gset.cc @@ -99,7 +99,7 @@ static void table_gset_resize(TableGSet *ts) } } -ATTR_NO_OPT bool BLI_table_gset_add(TableGSet *ts, void *elem) +bool BLI_table_gset_add(TableGSet *ts, void *elem) { table_gset_resize(ts); @@ -129,7 +129,7 @@ ATTR_NO_OPT bool BLI_table_gset_add(TableGSet *ts, void *elem) #endif } -ATTR_NO_OPT void BLI_table_gset_insert(TableGSet *ts, void *elem) +void BLI_table_gset_insert(TableGSet *ts, void *elem) { table_gset_resize(ts); @@ -173,7 +173,7 @@ void BLI_table_gset_remove(TableGSet *ts, void *elem, GHashKeyFreeFP freefp) ts->elems[idx2] = nullptr; } -ATTR_NO_OPT bool BLI_table_gset_haskey(TableGSet *ts, void *elem) +bool BLI_table_gset_haskey(TableGSet *ts, void *elem) { #ifdef USE_TGSET_SMALLHASH return BLI_smallhash_haskey(PTR_TO_IDX(ts), (uintptr_t)elem); diff --git a/source/blender/bmesh/intern/bmesh_idmap.cc b/source/blender/bmesh/intern/bmesh_idmap.cc index d709cc3e33f..a1e64db6211 100644 --- a/source/blender/bmesh/intern/bmesh_idmap.cc +++ b/source/blender/bmesh/intern/bmesh_idmap.cc @@ -259,6 +259,7 @@ void BM_idmap_check_ids(BMIdMap *idmap) int id = BM_ELEM_CD_GET_INT(elem, idmap->cd_id_off[int(elem->head.htype)]); if (id == BM_ID_NONE || id < 0 || (id < idmap->map_size && idmap->map[id])) { + //printf("%s: Allocating new id for %p(%d): %d\n", __func__, elem, id, max_id); id = max_id++; BM_ELEM_CD_SET_INT(elem, idmap->cd_id_off[int(elem->head.htype)], id); } diff --git a/source/blender/bmesh/intern/bmesh_log.cc b/source/blender/bmesh/intern/bmesh_log.cc index e79af982482..80331a298e0 100644 --- a/source/blender/bmesh/intern/bmesh_log.cc +++ b/source/blender/bmesh/intern/bmesh_log.cc @@ -77,9 +77,7 @@ namespace blender { template struct BMID { int id = -1; - BMID(int _id) : id(_id) - { - } + BMID(int _id) : id(_id) {} T *lookup(BMIdMap *idmap) { @@ -177,30 +175,24 @@ struct BMLogFace : public BMLogElem { struct BMLogEntry; +static BMIdMap *entry_get_idmap(BMLogEntry *entry); + struct BMLogSetBase { BMLogSetType type; BMLogEntry *entry = nullptr; /* Parent entry */ - BMLogSetBase(BMLogEntry *_entry, BMLogSetType _type) : type(_type), entry(_entry) - { - } + BMLogSetBase(BMLogEntry *_entry, BMLogSetType _type) : type(_type), entry(_entry) {} - virtual ~BMLogSetBase() - { - } + virtual ~BMLogSetBase() {} - virtual void undo(BMesh */*bm*/, BMLogCallbacks */*callbacks*/) - { - } - virtual void redo(BMesh */*bm*/, BMLogCallbacks */*callbacks*/) - { - } + virtual const char *debug_name() {} + virtual void print_info() {} + virtual void undo(BMesh * /*bm*/, BMLogCallbacks * /*callbacks*/) {} + virtual void redo(BMesh * /*bm*/, BMLogCallbacks * /*callbacks*/) {} }; struct BMLogSetDiff : public BMLogSetBase { - BMLogSetDiff(BMLogEntry *entry) : BMLogSetBase(entry, LOG_SET_DIFF) - { - } + BMLogSetDiff(BMLogEntry *entry) : BMLogSetBase(entry, LOG_SET_DIFF) {} Map, BMLogVert *> modified_verts; Map, BMLogEdge *> modified_edges; @@ -214,6 +206,27 @@ struct BMLogSetDiff : public BMLogSetBase { Map, BMLogEdge *> added_edges; Map, BMLogFace *> added_faces; + const char *debug_name() override + { + return "Diff"; + } + + void print_info() override + { + printf(" modified: v: %d e: %d f: %d\n", + modified_verts.size(), + modified_edges.size(), + modified_faces.size()); + printf(" removed: v: %d e: %d f: %d\n", + removed_verts.size(), + removed_edges.size(), + removed_faces.size()); + printf(" added: v: %d e: %d f: %d\n", + added_verts.size(), + added_edges.size(), + added_faces.size()); + } + void add_vert(BMesh *bm, BMVert *v); void remove_vert(BMesh *bm, BMVert *v); void modify_vert(BMesh *bm, BMVert *v); @@ -264,11 +277,17 @@ struct BMLogSetFull : public BMLogSetBase { params.update_shapekey_indices = false; params.calc_object_remap = false; params.copy_temp_cdlayers = true; - params.ignore_mesh_id_layers = false; mesh = BKE_mesh_from_bmesh_nomain(bm, ¶ms, nullptr); } + const char *debug_name() override + { + return "Full"; + } + + void print_info() {} + void swap(BMesh *bm) { CustomData_MeshMasks cd_mask_extra = {CD_MASK_DYNTOPO_VERT | CD_MASK_SHAPEKEY, 0, 0, 0, 0}; @@ -277,7 +296,6 @@ struct BMLogSetFull : public BMLogSetBase { params.update_shapekey_indices = false; params.calc_object_remap = false; params.copy_temp_cdlayers = true; - params.ignore_mesh_id_layers = false; Mesh *current_mesh = BKE_mesh_from_bmesh_nomain(bm, ¶ms, nullptr); @@ -285,15 +303,19 @@ struct BMLogSetFull : public BMLogSetBase { BMeshFromMeshParams params2 = {}; params2.copy_temp_cdlayers = true; params2.cd_mask_extra = cd_mask_extra; - params2.calc_face_normal = params2.add_key_index = params2.use_shapekey = - params2.ignore_id_layers = false; + params2.calc_face_normal = params2.add_key_index = params2.use_shapekey = false; BM_mesh_clear(bm); BM_mesh_bm_from_me(bm, - mesh, // note we stored shapekeys as customdata layers, - // that's why the shapekey params are false + mesh, /* Note: we stored shapekeys as customdata layers, + * that's why the shapekey params are false. + */ ¶ms2); + /* Regenerate ID map. */ + BMIdMap *idmap = entry_get_idmap(entry); + BM_idmap_check_ids(idmap); + bm->shapenr = shapenr; bm->elem_index_dirty |= BM_VERT | BM_EDGE | BM_FACE; @@ -309,13 +331,18 @@ struct BMLogSetFull : public BMLogSetBase { void undo(BMesh *bm, BMLogCallbacks *callbacks) override { swap(bm); - callbacks->on_full_mesh_load(callbacks->userdata); + + if (callbacks && callbacks->on_full_mesh_load) { + callbacks->on_full_mesh_load(callbacks->userdata); + } } void redo(BMesh *bm, BMLogCallbacks *callbacks) override { swap(bm); - callbacks->on_full_mesh_load(callbacks->userdata); + if (callbacks && callbacks->on_full_mesh_load) { + callbacks->on_full_mesh_load(callbacks->userdata); + } } Mesh *mesh = nullptr; @@ -419,7 +446,7 @@ struct BMLogEntry { CustomData_free(&pdata, 0); } - template T *get_elem_from_id(BMesh */*bm*/, BMID id) + template T *get_elem_from_id(BMesh * /*bm*/, BMID id) { T *elem = reinterpret_cast(BM_idmap_lookup(idmap, id.id)); char htype = 0; @@ -450,7 +477,7 @@ struct BMLogEntry { return elem; } - template void assign_elem_id(BMesh */*bm*/, T *elem, BMID _id, bool check_unique) + template void assign_elem_id(BMesh * /*bm*/, T *elem, BMID _id, bool check_unique) { int id = _id.id; @@ -476,7 +503,7 @@ struct BMLogEntry { BM_idmap_assign(idmap, reinterpret_cast(elem), id); } - template BMID get_elem_id(BMesh */*bm*/, T *elem) + template BMID get_elem_id(BMesh * /*bm*/, T *elem) { BM_idmap_check_assign(idmap, reinterpret_cast(elem)); return BM_idmap_get_id(idmap, reinterpret_cast(elem)); @@ -608,7 +635,7 @@ struct BMLogEntry { CustomData_bmesh_copy_data(&bm->edata, &edata, e->head.data, &le->customdata); } - void free_logedge(BMesh */*bm*/, BMLogEdge *e) + void free_logedge(BMesh * /*bm*/, BMLogEdge *e) { epool.free(e); } @@ -659,7 +686,7 @@ struct BMLogEntry { } while ((l = l->next) != f->l_first); } - void free_logface(BMesh */*bm*/, BMLogFace *lf) + void free_logface(BMesh * /*bm*/, BMLogFace *lf) { if (lf->loop_customdata[0]) { for (int i = 0; i < lf->verts.size(); i++) { @@ -719,15 +746,30 @@ struct BMLogEntry { void undo(BMesh *bm, BMLogCallbacks *callbacks) { +#ifdef BM_LOG_PRINT_DEBUG + printf("\n"); +#endif + for (int i = sets.size() - 1; i >= 0; i--) { - // printf(" - %d of %d\n", i, (int)(sets.size() - 1)); +#ifdef BM_LOG_PRINT_DEBUG + printf("%s: - %d of %d\n", sets[i]->debug_name(), i, (int)(sets.size() - 1)); + sets[i]->print_info(); +#endif sets[i]->undo(bm, callbacks); } } void redo(BMesh *bm, BMLogCallbacks *callbacks) { +#ifdef BM_LOG_PRINT_DEBUG + printf("\n"); +#endif + for (int i = 0; i < sets.size(); i++) { +#ifdef BM_LOG_PRINT_DEBUG + printf("%s: - %d of %d\n", sets[i]->debug_name(), i, (int)(sets.size() - 1)); + sets[i]->print_info(); +#endif sets[i]->redo(bm, callbacks); } } @@ -756,19 +798,17 @@ struct BMLog { int refcount = 1; bool dead = false; - BMLog(BMIdMap *_idmap) : idmap(_idmap) - { - } + BMLog(BMIdMap *_idmap) : idmap(_idmap) {} - ~BMLog() - { - } + ~BMLog() {} - void set_idmap(BMIdMap *idmap) + void set_idmap(BMIdMap *new_idmap) { + idmap = new_idmap; + BMLogEntry *entry = first_entry; while (entry) { - entry->idmap = idmap; + entry->idmap = new_idmap; entry = entry->next; } } @@ -1029,9 +1069,7 @@ void BMLogSetDiff::remove_edge(BMesh *bm, BMEdge *e) removed_edges.add(id, le); } -void BMLogSetDiff::modify_edge(BMesh */*bm*/, BMEdge */*e*/) -{ -} +void BMLogSetDiff::modify_edge(BMesh * /*bm*/, BMEdge * /*e*/) {} void BMLogSetDiff::add_face(BMesh *bm, BMFace *f) { @@ -1099,7 +1137,7 @@ void BMLogSetDiff::swap_verts(BMesh *bm, blender::Map, BMLogVert *> verts, BMLogCallbacks *callbacks) { - void *old_customdata = entry->vdata.pool ? BLI_mempool_alloc(bm->vdata.pool) : nullptr; + void *old_customdata = bm->vdata.pool ? BLI_mempool_alloc(bm->vdata.pool) : nullptr; const int cd_id = entry->idmap->cd_id_off[BM_VERT]; @@ -1360,7 +1398,7 @@ void BMLogSetDiff::swap_faces(BMesh *bm, BMFace *f = entry->get_elem_from_id(bm, lf->id); if (!f) { - printf("modified_verts: invalid vertex %d\n", lf->id.id); + printf("modified_faces: invalid face %d\n", lf->id.id); continue; } @@ -1376,7 +1414,7 @@ void BMLogSetDiff::swap_faces(BMesh *bm, BM_ELEM_CD_SET_INT(f, cd_id, lf->id.id); if (callbacks) { - callbacks->on_face_change(f, callbacks->userdata, old_customdata); + callbacks->on_face_change(f, callbacks->userdata, old_customdata, lf->flag); } } @@ -1387,6 +1425,13 @@ void BMLogSetDiff::swap_faces(BMesh *bm, void BMLogSetDiff::undo(BMesh *bm, BMLogCallbacks *callbacks) { + if (callbacks && callbacks->on_mesh_customdata_change) { + callbacks->on_mesh_customdata_change(&entry->vdata, BM_VERT, callbacks->userdata); + callbacks->on_mesh_customdata_change(&entry->edata, BM_EDGE, callbacks->userdata); + callbacks->on_mesh_customdata_change(&entry->ldata, BM_LOOP, callbacks->userdata); + callbacks->on_mesh_customdata_change(&entry->pdata, BM_FACE, callbacks->userdata); + } + remove_faces(bm, added_faces, callbacks); remove_edges(bm, added_edges, callbacks); remove_verts(bm, added_verts, callbacks); @@ -1401,6 +1446,13 @@ void BMLogSetDiff::undo(BMesh *bm, BMLogCallbacks *callbacks) void BMLogSetDiff::redo(BMesh *bm, BMLogCallbacks *callbacks) { + if (callbacks && callbacks->on_mesh_customdata_change) { + callbacks->on_mesh_customdata_change(&entry->vdata, BM_VERT, callbacks->userdata); + callbacks->on_mesh_customdata_change(&entry->edata, BM_EDGE, callbacks->userdata); + callbacks->on_mesh_customdata_change(&entry->ldata, BM_LOOP, callbacks->userdata); + callbacks->on_mesh_customdata_change(&entry->pdata, BM_FACE, callbacks->userdata); + } + remove_faces(bm, removed_faces, callbacks); remove_edges(bm, removed_edges, callbacks); remove_verts(bm, removed_verts, callbacks); @@ -1413,6 +1465,12 @@ void BMLogSetDiff::redo(BMesh *bm, BMLogCallbacks *callbacks) swap_edges(bm, modified_edges, callbacks); swap_verts(bm, modified_verts, callbacks); } + +static BMIdMap *entry_get_idmap(BMLogEntry *entry) +{ + return entry->idmap; +} + } // namespace blender BMLog *BM_log_from_existing_entries_create(BMesh *bm, BMIdMap *idmap, BMLogEntry *entry) @@ -1423,7 +1481,7 @@ BMLog *BM_log_from_existing_entries_create(BMesh *bm, BMIdMap *idmap, BMLogEntry return log; } -BMLog *BM_log_create(BMesh */*bm*/, BMIdMap *idmap) +BMLog *BM_log_create(BMesh * /*bm*/, BMIdMap *idmap) { BMLog *log = MEM_new("BMLog", idmap); @@ -1564,22 +1622,22 @@ void BM_log_full_mesh(BMesh *bm, BMLog *log) log->full_mesh(bm); } -BMVert *BM_log_id_vert_get(BMesh */*bm*/, BMLog *log, uint id) +BMVert *BM_log_id_vert_get(BMesh * /*bm*/, BMLog *log, uint id) { return reinterpret_cast(BM_idmap_lookup(log->idmap, id)); } -uint BM_log_vert_id_get(BMesh */*bm*/, BMLog *log, BMVert *v) +uint BM_log_vert_id_get(BMesh * /*bm*/, BMLog *log, BMVert *v) { return BM_idmap_get_id(log->idmap, reinterpret_cast(v)); } -BMFace *BM_log_id_face_get(BMesh */*bm*/, BMLog *log, uint id) +BMFace *BM_log_id_face_get(BMesh * /*bm*/, BMLog *log, uint id) { return reinterpret_cast(BM_idmap_lookup(log->idmap, id)); } -uint BM_log_face_id_get(BMesh */*bm*/, BMLog *log, BMFace *f) +uint BM_log_face_id_get(BMesh * /*bm*/, BMLog *log, BMFace *f) { return BM_idmap_get_id(log->idmap, reinterpret_cast(f)); } @@ -1599,12 +1657,12 @@ void BM_log_redo(BMesh *bm, BMLog *log, BMLogCallbacks *callbacks) log->redo(bm, callbacks); } -void BM_log_undo_skip(BMesh */*bm*/, BMLog *log) +void BM_log_undo_skip(BMesh * /*bm*/, BMLog *log) { log->skip(-1); } -void BM_log_redo_skip(BMesh */*bm*/, BMLog *log) +void BM_log_redo_skip(BMesh * /*bm*/, BMLog *log) { log->skip(1); } @@ -1653,7 +1711,7 @@ bool BM_log_entry_drop(BMLogEntry *entry) return true; } -void BM_log_print_entry(BMLog */*log*/, BMLogEntry *entry) +void BM_log_print_entry(BMLog * /*log*/, BMLogEntry *entry) { printf("entry: %p", entry); } diff --git a/source/blender/bmesh/intern/bmesh_log_intern.h b/source/blender/bmesh/intern/bmesh_log_intern.h index 45df949cbf8..00e14f9ac5a 100644 --- a/source/blender/bmesh/intern/bmesh_log_intern.h +++ b/source/blender/bmesh/intern/bmesh_log_intern.h @@ -40,6 +40,7 @@ struct BMVert; struct BMesh; struct RangeTreeUInt; struct BMIdMap; +struct CustomData; #ifdef __cplusplus namespace blender { @@ -65,10 +66,10 @@ typedef struct BMLogCallbacks { void (*on_face_add)(struct BMFace *f, void *userdata); void (*on_face_kill)(struct BMFace *f, void *userdata); - void (*on_face_change)(struct BMFace *f, void *userdata, void *old_customdata); + void (*on_face_change)(struct BMFace *f, void *userdata, void *old_customdata, char old_hflag); void (*on_full_mesh_load)(void *userdata); - void (*on_mesh_id_restore)(void *userdata); + void (*on_mesh_customdata_change)(struct CustomData *domain, char htype, void *userdata); void *userdata; } BMLogCallbacks; diff --git a/source/blender/bmesh/intern/bmesh_mesh_convert.h b/source/blender/bmesh/intern/bmesh_mesh_convert.h index c6469cad855..2f3566e731f 100644 --- a/source/blender/bmesh/intern/bmesh_mesh_convert.h +++ b/source/blender/bmesh/intern/bmesh_mesh_convert.h @@ -79,7 +79,6 @@ struct BMeshToMeshParams { bool active_shapekey_to_mvert; struct CustomData_MeshMasks cd_mask_extra; uint copy_temp_cdlayers : 1; - uint ignore_mesh_id_layers : 1; }; void BM_enter_multires_space(struct Object *ob, struct BMesh *bm, int space); diff --git a/source/blender/draw/intern/draw_pbvh.cc b/source/blender/draw/intern/draw_pbvh.cc index 5497a035482..d0b55c77c65 100644 --- a/source/blender/draw/intern/draw_pbvh.cc +++ b/source/blender/draw/intern/draw_pbvh.cc @@ -761,6 +761,11 @@ struct PBVHBatches { auto foreach_bmesh_normal = [&](std::function callback) { for (int i : IndexRange(args->tribuf->tottri)) { PBVHTri *tri = args->tribuf->tris + i; + BMFace *f = reinterpret_cast(tri->f.i); + + if (BM_elem_flag_test(f, BM_ELEM_HIDDEN)) { + continue; + } for (int j = 0; j < 3; j++) { callback(reinterpret_cast(tri->l[j])); @@ -774,6 +779,12 @@ struct PBVHBatches { for (int i : IndexRange(args->tribuf->tottri)) { PBVHTri *tri = args->tribuf->tris + i; + BMFace *f = reinterpret_cast(tri->f.i); + + if (BM_elem_flag_test(f, BM_ELEM_HIDDEN)) { + continue; + } + BMLoop *la = reinterpret_cast(tri->l[0]); BMLoop *lb = reinterpret_cast(tri->l[1]); BMLoop *lc = reinterpret_cast(tri->l[2]); @@ -1463,7 +1474,7 @@ struct PBVHBatches { bool grid_visible = false; BLI_bitmap *gh = args->grid_hidden[args->grid_indices[i]]; - uint v0, v1, v2, v3; + uint v0, v1 = 0, v2, v3; for (int j = 0; j < gridsize - skip; j += skip) { for (int k = 0; k < gridsize - skip; k += skip) { /* Skip hidden grid face */ diff --git a/source/blender/editors/sculpt_paint/sculpt.cc b/source/blender/editors/sculpt_paint/sculpt.cc index 752ebb0509c..db2ef1b12f5 100644 --- a/source/blender/editors/sculpt_paint/sculpt.cc +++ b/source/blender/editors/sculpt_paint/sculpt.cc @@ -298,7 +298,7 @@ bool SCULPT_has_loop_colors(const Object *ob) bool SCULPT_has_colors(const SculptSession *ss) { - return ss->vcol || ss->mcol; + return ss->bm ? ss->cd_vcol_offset >= 0 : (ss->vcol || ss->mcol); } void SCULPT_vertex_color_get(const SculptSession *ss, PBVHVertRef vertex, float r_color[4]) @@ -643,9 +643,10 @@ void SCULPT_face_visibility_all_set(SculptSession *ss, bool visible) case PBVH_BMESH: for (int i = 0; i < ss->totfaces; i++) { PBVHFaceRef face = BKE_pbvh_index_to_face(ss->pbvh, i); - - *BKE_sculpt_face_attr_get(face, ss->attrs.hide_poly) = !visible; + BMFace *f = reinterpret_cast(face.i); + BM_elem_flag_set(f, BM_ELEM_HIDDEN, !visible); } + break; } } @@ -1829,7 +1830,7 @@ enum StrokeFlags { void SCULPT_orig_vert_data_init(SculptOrigVertData *data, Object *ob, - PBVHNode *node, + PBVHNode * /*node*/, SculptUndoType type) { SculptSession *ss = ob->sculpt; @@ -3962,7 +3963,7 @@ static float sculpt_topology_automasking_mask_cb(PBVHVertRef vertex, void *vdata return 1.0f - SCULPT_vertex_mask_get(state->ss, vertex); } -static float sculpt_null_mask_cb(PBVHVertRef vertex, void *vdata) +static float sculpt_null_mask_cb(PBVHVertRef /*vertex*/, void * /*vdata*/) { return 1.0f; } @@ -4023,7 +4024,7 @@ void SCULPT_dyntopo_automasking_end(void *mask_data) MEM_SAFE_FREE(mask_data); } -bool SCULPT_needs_area_normal(SculptSession *ss, Sculpt *sd, Brush *brush) +bool SCULPT_needs_area_normal(SculptSession * /*ss*/, Sculpt * /*sd*/, Brush *brush) { return brush->tip_roundness != 1.0f || brush->tip_scale_x != 1.0f; } @@ -5058,7 +5059,7 @@ static const char *sculpt_tool_name(Sculpt *sd) /* Operator for applying a stroke (various attributes including mouse path) * using the current brush. */ -void SCULPT_cache_free(SculptSession *ss, struct Object *ob, StrokeCache *cache) +void SCULPT_cache_free(SculptSession * /*ss*/, struct Object * /*ob*/, StrokeCache *cache) { MEM_SAFE_FREE(cache->dial); MEM_SAFE_FREE(cache->prev_colors); diff --git a/source/blender/editors/sculpt_paint/sculpt_automasking.cc b/source/blender/editors/sculpt_paint/sculpt_automasking.cc index 381d3ae03ad..6f0e5e961e3 100644 --- a/source/blender/editors/sculpt_paint/sculpt_automasking.cc +++ b/source/blender/editors/sculpt_paint/sculpt_automasking.cc @@ -148,7 +148,7 @@ static float sculpt_automasking_normal_calc(SculptSession *ss, { float3 normal_v; - if (automask_data->have_orig_data) { + if (automask_data && automask_data->have_orig_data) { normal_v = automask_data->orig_data.no; } else { diff --git a/source/blender/editors/sculpt_paint/sculpt_face_set.cc b/source/blender/editors/sculpt_paint/sculpt_face_set.cc index e8f4b0768f5..1edcec556c6 100644 --- a/source/blender/editors/sculpt_paint/sculpt_face_set.cc +++ b/source/blender/editors/sculpt_paint/sculpt_face_set.cc @@ -683,8 +683,6 @@ void SCULPT_do_draw_face_sets_brush(Sculpt *sd, Object *ob, Span nod data.bstrength = ss->cache->bstrength; data.count = 1; - bool threaded = true; - /*for ctrl invert mode we have to set the automasking initial_face_set to the first non-current faceset that is found*/ int automasking_flags = brush->automasking_flags | (sd ? sd->automasking_flags : 0); @@ -697,6 +695,7 @@ void SCULPT_do_draw_face_sets_brush(Sculpt *sd, Object *ob, Span nod } } + bool threaded = true; if (ss->cache->invert && !ss->cache->alt_smooth && ss->cache->automasking && ss->cache->automasking->settings.initial_face_set == ss->cache->automasking->settings.current_face_set) { @@ -705,7 +704,7 @@ void SCULPT_do_draw_face_sets_brush(Sculpt *sd, Object *ob, Span nod // ctrl-click is single threaded since the tasks will set the initial face set TaskParallelSettings settings; - BKE_pbvh_parallel_range_settings(&settings, true, nodes.size()); + BKE_pbvh_parallel_range_settings(&settings, threaded, nodes.size()); if (ss->cache->alt_smooth) { SCULPT_boundary_info_ensure(ob); for (int i = 0; i < 4; i++) { @@ -1367,6 +1366,7 @@ static int sculpt_face_sets_change_visibility_exec(bContext *C, wmOperator *op) SCULPT_vertex_random_access_ensure(ss); SCULPT_face_random_access_ensure(ss); + BKE_sculpt_hide_poly_ensure(ob); const int active_face_set = SCULPT_active_face_set_get(ss); @@ -1464,7 +1464,6 @@ static int sculpt_face_sets_change_visibility_exec(bContext *C, wmOperator *op) SCULPT_undo_push_end(ob); for (PBVHNode *node : nodes) { BKE_pbvh_node_mark_update_visibility(node); - BKE_pbvh_bmesh_check_tris(ss->pbvh, node); } BKE_pbvh_update_vertex_data(ss->pbvh, PBVH_UpdateVisibility); @@ -1641,9 +1640,9 @@ static EnumPropertyItem prop_sculpt_face_sets_edit_types[] = { {0, nullptr, 0, nullptr, nullptr}, }; -static void sculpt_face_set_grow_bmesh(Object *ob, +static void sculpt_face_set_grow_bmesh(Object * /*ob*/, SculptSession *ss, - const int *prev_face_sets, + const int * /*prev_face_sets*/, const int active_face_set_id, const bool modify_hidden) { @@ -1773,7 +1772,7 @@ static void sculpt_face_set_fill_component(Object *ob, BLI_gset_free(connected_components, nullptr); } -static void sculpt_face_set_shrink_bmesh(Object *ob, +static void sculpt_face_set_shrink_bmesh(Object * /*ob*/, SculptSession *ss, const int *prev_face_sets, const int active_face_set_id, @@ -2131,7 +2130,6 @@ static void face_set_edit_do_post_visibility_updates(Object *ob, Spansculpt; PBVH *pbvh = ss->pbvh; - Mesh *mesh = static_cast(ob->data); /* Sync face sets visibility and vertex visibility as now all Face Sets are visible. */ SCULPT_visibility_sync_all_from_faces(ob); diff --git a/source/blender/editors/sculpt_paint/sculpt_ops.cc b/source/blender/editors/sculpt_paint/sculpt_ops.cc index c557437355c..3ba4da648d0 100644 --- a/source/blender/editors/sculpt_paint/sculpt_ops.cc +++ b/source/blender/editors/sculpt_paint/sculpt_ops.cc @@ -905,6 +905,11 @@ static int sculpt_sample_color_invoke(bContext *C, wmOperator *op, const wmEvent BKE_sculpt_update_object_for_edit(CTX_data_depsgraph_pointer(C), ob, true, false, false); + if (!SCULPT_has_colors(ss)) { + BKE_report(op->reports, RPT_ERROR, "Mesh has no color attributes."); + return OPERATOR_CANCELLED; + } + const PBVHVertRef active_vertex = SCULPT_active_vertex_get(ss); float active_vertex_color[4]; diff --git a/source/blender/editors/sculpt_paint/sculpt_undo.cc b/source/blender/editors/sculpt_paint/sculpt_undo.cc index b47f600de33..f7fbe221e71 100644 --- a/source/blender/editors/sculpt_paint/sculpt_undo.cc +++ b/source/blender/editors/sculpt_paint/sculpt_undo.cc @@ -80,6 +80,7 @@ #include "ED_sculpt.h" #include "ED_undo.h" +#include "../../bmesh/intern/bmesh_idmap.h" #include "bmesh.h" #include "bmesh_log.h" #include "sculpt_intern.hh" @@ -683,6 +684,7 @@ typedef struct BmeshUndoData { bool do_full_recalc; bool balance_pbvh; int cd_face_node_offset, cd_vert_node_offset; + int cd_face_node_offset_old, cd_vert_node_offset_old; int cd_sculpt_vert, cd_boundary_flag; bool regen_all_unique_verts; bool is_redo; @@ -845,14 +847,14 @@ static void bmesh_undo_on_vert_change(BMVert *v, void *userdata, void *old_custo BMElem h; h.head.data = old_customdata; - int ni = BM_ELEM_CD_GET_INT(&h, data->cd_vert_node_offset); + int ni = data->cd_vert_node_offset_old != -1 ? + BM_ELEM_CD_GET_INT(&h, data->cd_vert_node_offset_old) : + DYNTOPO_NODE_NONE; - // int ni2 = BM_ELEM_CD_GET_INT(v, data->cd_vert_node_offset); - - // attempt to find old node + /* Attempt to find old node reference. */ PBVHNode *node = BKE_pbvh_get_node_leaf_safe(data->pbvh, ni); if (node) { - // BKE_pbvh_bmesh_mark_node_regen(data->pbvh, node); + /* Make sure undo customdata didn't override node ref. */ BKE_pbvh_node_mark_update(node); BM_ELEM_CD_SET_INT(v, data->cd_vert_node_offset, ni); } @@ -862,7 +864,7 @@ static void bmesh_undo_on_vert_change(BMVert *v, void *userdata, void *old_custo BM_ELEM_CD_SET_INT(v, data->cd_vert_node_offset, DYNTOPO_NODE_NONE); } - data->regen_all_unique_verts = true; + // data->regen_all_unique_verts = true; } return; @@ -878,7 +880,10 @@ static void bmesh_undo_on_vert_change(BMVert *v, void *userdata, void *old_custo } } -static void bmesh_undo_on_face_change(BMFace *f, void *userdata, void *old_customdata) +static void bmesh_undo_on_face_change(BMFace *f, + void *userdata, + void *old_customdata, + char old_hflag) { BmeshUndoData *data = (BmeshUndoData *)userdata; @@ -897,6 +902,10 @@ static void bmesh_undo_on_face_change(BMFace *f, void *userdata, void *old_custo if (node) { BM_ELEM_CD_SET_INT(f, data->cd_face_node_offset, ni); BKE_pbvh_node_mark_update(node); + + if ((old_hflag & BM_ELEM_HIDDEN) != (f->head.hflag & BM_ELEM_HIDDEN)) { + BKE_pbvh_node_mark_update_visibility(node); + } } else { printf("pbvh face undo error\n"); @@ -925,6 +934,20 @@ static void update_unode_bmesh_memsize(SculptUndoNode *unode) usculpt->undo_size += unode->undo_size; } +static void bmesh_undo_customdata_change(CustomData *domain, char htype, void *userdata) +{ + BmeshUndoData *data = (BmeshUndoData *)userdata; + + if (htype == BM_VERT) { + data->cd_vert_node_offset_old = CustomData_get_offset_named( + domain, CD_PROP_INT32, SCULPT_ATTRIBUTE_NAME(dyntopo_node_id_vertex)); + } + else if (htype == BM_FACE) { + data->cd_face_node_offset_old = CustomData_get_offset_named( + domain, CD_PROP_INT32, SCULPT_ATTRIBUTE_NAME(dyntopo_node_id_face)); + } +} + static void sculpt_undo_bmesh_restore_generic(SculptUndoNode *unode, Object *ob, SculptSession *ss) { BmeshUndoData data = {ss->pbvh, @@ -933,6 +956,8 @@ static void sculpt_undo_bmesh_restore_generic(SculptUndoNode *unode, Object *ob, false, ss->cd_face_node_offset, ss->cd_vert_node_offset, + -1, + -1, ss->cd_sculpt_vert, ss->attrs.boundary_flags->bmesh_cd_offset, false, @@ -948,7 +973,7 @@ static void sculpt_undo_bmesh_restore_generic(SculptUndoNode *unode, Object *ob, bmesh_undo_on_face_kill, bmesh_undo_on_face_change, bmesh_undo_full_mesh, - nullptr, + bmesh_undo_customdata_change, (void *)&data}; BKE_sculptsession_update_attr_refs(ob); @@ -1030,6 +1055,13 @@ static void sculpt_undo_bmesh_enable(Object *ob, SculptUndoNode *unode, bool is_ BKE_pbvh_update_sculpt_verts(ss->pbvh); } + if (ss->bm_idmap) { + BM_idmap_destroy(ss->bm_idmap); + } + + ss->bm_idmap = BM_idmap_new(ss->bm, BM_VERT | BM_EDGE | BM_FACE); + BM_idmap_check_ids(ss->bm_idmap); + if (!ss->bm_log) { /* Restore the BMLog using saved entries. */ ss->bm_log = BM_log_from_existing_entries_create(ss->bm, ss->bm_idmap, unode->bm_entry); @@ -1037,9 +1069,13 @@ static void sculpt_undo_bmesh_enable(Object *ob, SculptUndoNode *unode, bool is_ BM_log_set_current_entry(ss->bm_log, entry); } + else { + BM_log_set_idmap(ss->bm_log, ss->bm_idmap); + } if (!CustomData_has_layer(&ss->bm->vdata, CD_PAINT_MASK)) { BM_data_layer_add(ss->bm, &ss->bm->vdata, CD_PAINT_MASK); + BKE_sculptsession_update_attr_refs(ob); } me->flag |= ME_SCULPT_DYNAMIC_TOPOLOGY; @@ -1096,7 +1132,6 @@ static void sculpt_undo_bmesh_restore_begin( } if (ss->bm) { - BKE_pbvh_bmesh_check_nodes(ss->pbvh); BM_mesh_elem_index_ensure(ss->bm, BM_VERT | BM_FACE); } } @@ -1120,10 +1155,23 @@ static void sculpt_undo_bmesh_restore_end( else { BM_log_redo(ss->bm, ss->bm_log, nullptr); } + + SCULPT_pbvh_clear(ob); + BKE_sculptsession_update_attr_refs(ob); + + BMIter iter; + BMVert *v; + BMFace *f; + + BM_ITER_MESH (v, &iter, ss->bm, BM_VERTS_OF_MESH) { + BM_ELEM_CD_SET_INT(v, ss->cd_vert_node_offset, DYNTOPO_NODE_NONE); + } + BM_ITER_MESH (f, &iter, ss->bm, BM_FACES_OF_MESH) { + BM_ELEM_CD_SET_INT(f, ss->cd_face_node_offset, DYNTOPO_NODE_NONE); + } #endif unode->applied = false; - BKE_pbvh_bmesh_check_nodes(ss->pbvh); } else { #if 1 @@ -1138,8 +1186,6 @@ static void sculpt_undo_bmesh_restore_end( else { BM_log_redo_skip(ss->bm, ss->bm_log); } - - BKE_pbvh_bmesh_check_nodes(ss->pbvh); } #endif @@ -1301,6 +1347,10 @@ static int sculpt_undo_bmesh_restore( if (ss->bm_log) { sculpt_undo_bmesh_restore_generic(unode, ob, ss); SCULPT_vertex_random_access_ensure(ss); + + if (unode->type == SCULPT_UNDO_HIDDEN) { + BKE_pbvh_update_vertex_data(ss->pbvh, PBVH_UpdateVisibility); + } ret = true; } break; @@ -2176,19 +2226,15 @@ static SculptUndoNode *sculpt_undo_bmesh_push(Object *ob, PBVHNode *node, Sculpt if (type == SCULPT_UNDO_DYNTOPO_END) { unode->bm_log = ss->bm_log; - // unode->bm_entry = BM_log_all_ids(ss->bm, ss->bm_log, nullptr); unode->bm_entry = BM_log_entry_add(ss->bm, ss->bm_log); - // BM_log_full_mesh(ss->bm, ss->bm_log); - // BM_log_before_all_removed(ss->bm, ss->bm_log); + BM_log_full_mesh(ss->bm, ss->bm_log); } else if (type == SCULPT_UNDO_DYNTOPO_BEGIN) { unode->bm_log = ss->bm_log; - // unode->bm_entry = BM_log_all_ids(ss->bm, ss->bm_log, nullptr); unode->bm_entry = BM_log_entry_add(ss->bm, ss->bm_log); - // BM_log_all_added(ss->bm, ss->bm_log); - // BM_log_full_mesh(ss->bm, ss->bm_log); + BM_log_full_mesh(ss->bm, ss->bm_log); } else { unode->bm_log = ss->bm_log; -- 2.30.2 From 75eab27366eeddbefe093d1794ebb71456a20044 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Tue, 18 Apr 2023 23:54:05 -0700 Subject: [PATCH 062/279] temp-sculpt-dyntopo: Fix compile error --- source/blender/bmesh/intern/bmesh_log.cc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/source/blender/bmesh/intern/bmesh_log.cc b/source/blender/bmesh/intern/bmesh_log.cc index 80331a298e0..f0d1e08749a 100644 --- a/source/blender/bmesh/intern/bmesh_log.cc +++ b/source/blender/bmesh/intern/bmesh_log.cc @@ -185,7 +185,10 @@ struct BMLogSetBase { virtual ~BMLogSetBase() {} - virtual const char *debug_name() {} + virtual const char *debug_name() + { + return ""; + } virtual void print_info() {} virtual void undo(BMesh * /*bm*/, BMLogCallbacks * /*callbacks*/) {} virtual void redo(BMesh * /*bm*/, BMLogCallbacks * /*callbacks*/) {} -- 2.30.2 From 4dbd79499a8535166ac3328ba1c436da0a449c46 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Tue, 18 Apr 2023 23:55:58 -0700 Subject: [PATCH 063/279] temp-sculpt-dyntopo: fix compile error --- source/blender/bmesh/intern/bmesh_log.cc | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/source/blender/bmesh/intern/bmesh_log.cc b/source/blender/bmesh/intern/bmesh_log.cc index f0d1e08749a..b2431f6134c 100644 --- a/source/blender/bmesh/intern/bmesh_log.cc +++ b/source/blender/bmesh/intern/bmesh_log.cc @@ -217,17 +217,17 @@ struct BMLogSetDiff : public BMLogSetBase { void print_info() override { printf(" modified: v: %d e: %d f: %d\n", - modified_verts.size(), - modified_edges.size(), - modified_faces.size()); + int(modified_verts.size()), + int(modified_edges.size()), + int(modified_faces.size())); printf(" removed: v: %d e: %d f: %d\n", - removed_verts.size(), - removed_edges.size(), - removed_faces.size()); + int(removed_verts.size()), + int(removed_edges.size()), + int(removed_faces.size())); printf(" added: v: %d e: %d f: %d\n", - added_verts.size(), - added_edges.size(), - added_faces.size()); + int(added_verts.size()), + int(added_edges.size()), + int(added_faces.size())); } void add_vert(BMesh *bm, BMVert *v); -- 2.30.2 From a04c6e15a84799b9449cc666dc572664e02d5491 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Fri, 21 Apr 2023 16:15:07 -0700 Subject: [PATCH 064/279] temp-sculpt-dyntopo: Fix crash in box trim --- .../blenkernel/intern/dyntopo_collapse.cc | 2 +- .../blenkernel/intern/dyntopo_intern.hh | 2 +- .../editors/sculpt_paint/paint_mask.cc | 24 +++++++++++++------ 3 files changed, 19 insertions(+), 9 deletions(-) diff --git a/source/blender/blenkernel/intern/dyntopo_collapse.cc b/source/blender/blenkernel/intern/dyntopo_collapse.cc index ca9b742fe59..07c2e23ec1d 100644 --- a/source/blender/blenkernel/intern/dyntopo_collapse.cc +++ b/source/blender/blenkernel/intern/dyntopo_collapse.cc @@ -708,7 +708,7 @@ BMVert *pbvh_bmesh_collapse_edge(PBVH *pbvh, MV_ADD_FLAG(mv3, mupdateflag); if (!v_conn->e) { - // delete isolated vertex + /* Delete isolated vertex. */ if (BM_ELEM_CD_GET_INT(v_conn, pbvh->cd_vert_node_offset) != DYNTOPO_NODE_NONE) { blender::dyntopo::pbvh_bmesh_vert_remove(pbvh, v_conn); } diff --git a/source/blender/blenkernel/intern/dyntopo_intern.hh b/source/blender/blenkernel/intern/dyntopo_intern.hh index 46fd5ee380d..21bef75e9fb 100644 --- a/source/blender/blenkernel/intern/dyntopo_intern.hh +++ b/source/blender/blenkernel/intern/dyntopo_intern.hh @@ -95,7 +95,7 @@ inline bool bm_elem_is_free(BMElem *elem, int htype) #define EVEN_EDGELEN_THRESHOLD 1.2f /* How much the limit increases per recursion * (avoids performing subdivisions too far away). */ -#define EVEN_GENERATION_SCALE 1.1f +#define EVEN_GENERATION_SCALE 1.25f /* recursion depth to start applying front face test */ #define DEPTH_START_LIMIT 5 diff --git a/source/blender/editors/sculpt_paint/paint_mask.cc b/source/blender/editors/sculpt_paint/paint_mask.cc index 7d6c8dcd5f5..38c4965bf67 100644 --- a/source/blender/editors/sculpt_paint/paint_mask.cc +++ b/source/blender/editors/sculpt_paint/paint_mask.cc @@ -59,6 +59,7 @@ /* For undo push. */ #include "sculpt_intern.hh" +using blender::float3; using blender::IndexRange; using blender::OffsetIndices; using blender::Span; @@ -1356,20 +1357,30 @@ static void sculpt_gesture_trim_geometry_generate(SculptGestureContext *sgcontex &trim_operation->mesh->edata, CD_PROP_BOOL, "sharp_edge", trim_operation->mesh->totedge); if (!sharp_edge) { - CustomData_add_layer( - &trim_operation->mesh->edata, CD_PROP_BOOL, CD_CONSTRUCT, trim_operation->mesh->totedge); + CustomData_add_layer_named(&trim_operation->mesh->edata, + CD_PROP_BOOL, + CD_CONSTRUCT, + trim_operation->mesh->totedge, + "sharp_edge"); sharp_edge = (bool *)CustomData_get_layer_named_for_write( &trim_operation->mesh->edata, CD_PROP_BOOL, "sharp_edge", trim_operation->mesh->totedge); } const blender::Span &corner_edges = trim_operation->mesh->corner_edges(); const OffsetIndices &polys = trim_operation->mesh->polys(); + Span poly_normals = trim_operation->mesh->poly_normals(); - // XXX test me - /* flag edges as sharp for dyntopo remesher */ - for (int i = 0; i < tot_screen_points * 2; i++) { - sharp_edge[corner_edges[polys[tot_tris_face + i].start()]] = true; + for (int i = 0; i < trim_operation->mesh->totedge; i++) { + sharp_edge[i] = false; } + const float angle = 80.0f / 180.0f * M_PI; + blender::bke::mesh::edges_sharp_from_angle_set(polys, + corner_verts, + corner_edges, + poly_normals, + nullptr, + angle, + {sharp_edge, trim_operation->mesh->totedge}); } static void sculpt_gesture_trim_geometry_free(SculptGestureContext *sgcontext) { @@ -1543,7 +1554,6 @@ static void sculpt_gesture_trim_begin(bContext *C, SculptGestureContext *sgconte { Object *object = sgcontext->vc.obact; SculptSession *ss = object->sculpt; - Mesh *mesh = (Mesh *)object->data; ss->face_sets = BKE_sculpt_face_sets_ensure(object); Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); -- 2.30.2 From cfc638d4b2690941548c3fd55d5ecb6c3c42f24e Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Fri, 21 Apr 2023 16:38:13 -0700 Subject: [PATCH 065/279] temp-sculpt-dyntopo: Fix trim booleans --- .../editors/sculpt_paint/paint_mask.cc | 56 ++++++++++--------- 1 file changed, 30 insertions(+), 26 deletions(-) diff --git a/source/blender/editors/sculpt_paint/paint_mask.cc b/source/blender/editors/sculpt_paint/paint_mask.cc index 38c4965bf67..c9033960a87 100644 --- a/source/blender/editors/sculpt_paint/paint_mask.cc +++ b/source/blender/editors/sculpt_paint/paint_mask.cc @@ -1420,6 +1420,13 @@ static void sculpt_gesture_apply_trim(SculptGestureContext *sgcontext) BM_mesh_bm_from_me(bm, sculpt_mesh, ¶ms2); } + BMIter iter; + BMFace *efa; + BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) { + BM_elem_flag_enable(efa, BM_ELEM_DRAW); + } + + /* Create trim bmesh. */ BMeshCreateParams params1 = {0}; params1.create_unique_ids = params1.id_map = params1.copy_all_layers = true; params1.id_elem_mask = BM_VERT | BM_EDGE | BM_FACE; @@ -1429,40 +1436,21 @@ static void sculpt_gesture_apply_trim(SculptGestureContext *sgcontext) BMeshFromMeshParams params3 = {0}; params3.calc_face_normal = params3.calc_vert_normal = true; - BM_mesh_bm_from_me(trimbm, trim_mesh, ¶ms3); BM_mesh_normals_update(bm); - -#if 0 - // remesh - DynTopoState *ds = BKE_dyntopo_init(trimbm, NULL); - DynRemeshParams params; - BKE_dyntopo_default_params(¶ms, trim_operation->avg_edge_len * 4.0); - BKE_dyntopo_remesh(ds, ¶ms, 10, PBVH_Collapse | PBVH_Cleanup | PBVH_Subdivide); - - BM_mesh_toolflags_set(bm, true); - - BKE_dyntopo_free(ds); -#endif - BM_mesh_toolflags_set(bm, true); + /* Add trim geometry to bm. */ BMO_op_callf(trimbm, BMO_FLAG_DEFAULTS, "duplicate geom=%avef dest=%p", bm, 3); - - BKE_sculptsession_update_attr_refs(sgcontext->vc.obact); BM_mesh_free(trimbm); - BMeshFromMeshParams params2 = {0}; - params2.calc_face_normal = params2.calc_vert_normal = true; - BM_mesh_bm_from_me(bm, sculpt_mesh, ¶ms2); - + /* Calculate tesselation. */ const int looptris_tot = poly_to_tri_count(bm->totface, bm->totloop); BMLoop *(*looptris)[3] = static_cast( MEM_malloc_arrayN(looptris_tot, sizeof(*looptris), __func__)); BM_mesh_calc_tessellation_beauty(bm, looptris); - BMIter iter; int i; const int i_faces_end = trim_mesh->totpoly; @@ -1472,14 +1460,10 @@ static void sculpt_gesture_apply_trim(SculptGestureContext *sgcontext) const short ob_src_totcol = trim_mesh->totcol; blender::Array material_remap(ob_src_totcol ? ob_src_totcol : 1); - BMFace *efa; i = 0; BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) { normalize_v3(efa->no); - /* Temp tag to test which side split faces are from. */ - BM_elem_flag_enable(efa, BM_ELEM_DRAW); - /* Remap material. */ if (efa->mat_nr < ob_src_totcol) { efa->mat_nr = material_remap[efa->mat_nr]; @@ -1520,8 +1504,28 @@ static void sculpt_gesture_apply_trim(SculptGestureContext *sgcontext) } MEM_freeN(looptris); + BKE_sculptsession_update_attr_refs(sgcontext->vc.obact); - if (sgcontext->ss && sgcontext->ss->bm) { // rebuild pbvh + /* Flag intersection edges as sharp.*/ + BMEdge *e; + BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) { + BMLoop *l = e->l; + + /* At least two surrounding faces? */ + if (!l || l->f == l->radial_next->f) { + continue; + } + + /* Did they come from different meshes? */ + BMFace *f1 = l->f, *f2 = l->radial_next->f; + if (BM_elem_flag_test(f1, BM_ELEM_DRAW) == BM_elem_flag_test(f2, BM_ELEM_DRAW)) { + continue; + } + + BM_elem_flag_disable(e, BM_ELEM_SMOOTH); + } + + if (sgcontext->ss && sgcontext->ss->bm) { /* Rebuild pbvh. */ BKE_pbvh_free(sgcontext->ss->pbvh); sgcontext->ss->pbvh = BKE_pbvh_new(PBVH_BMESH); -- 2.30.2 From 23a048ca585e2b2b3a2b79b7d89ee0b01c44ab12 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Mon, 24 Apr 2023 11:03:08 -0700 Subject: [PATCH 066/279] temp-sculpt-dyntopo: Fix detail flood fill operator --- scripts/startup/bl_ui/space_view3d_toolbar.py | 2 +- .../blenloader/intern/versioning_300.cc | 5 +- source/blender/editors/sculpt_paint/sculpt.cc | 8 ++-- .../editors/sculpt_paint/sculpt_api.cc | 5 ++ .../editors/sculpt_paint/sculpt_detail.cc | 48 ++++++++++--------- 5 files changed, 39 insertions(+), 29 deletions(-) diff --git a/scripts/startup/bl_ui/space_view3d_toolbar.py b/scripts/startup/bl_ui/space_view3d_toolbar.py index 950a5d94ad0..140484ea980 100644 --- a/scripts/startup/bl_ui/space_view3d_toolbar.py +++ b/scripts/startup/bl_ui/space_view3d_toolbar.py @@ -940,7 +940,7 @@ class VIEW3D_PT_sculpt_dyntopo(Panel, View3DPaintPanel): "detail_range" ) - if sculpt.detail_type_method in {'CONSTANT', 'MANUAL'}: + if UnifiedPaintPanel.get_dyntopo_prop(context, brush, "mode") in {'CONSTANT', 'MANUAL'}: col.operator("sculpt.detail_flood_fill") UnifiedPaintPanel.prop_unified_dyntopo( diff --git a/source/blender/blenloader/intern/versioning_300.cc b/source/blender/blenloader/intern/versioning_300.cc index e776e209349..91512847ced 100644 --- a/source/blender/blenloader/intern/versioning_300.cc +++ b/source/blender/blenloader/intern/versioning_300.cc @@ -4242,9 +4242,12 @@ void blo_do_versions_300(FileData *fd, Library * /*lib*/, Main *bmain) ds->detail_percent = sculpt->detail_percent; ds->detail_size = sculpt->detail_size; - ds->detail_range = sculpt->detail_range; ds->constant_detail = sculpt->constant_detail; + if (sculpt->detail_range != 0.0f) { + ds->detail_range = sculpt->detail_range; + } + sculpt->flags |= SCULPT_DYNTOPO_ENABLED; ds->flag = 0; diff --git a/source/blender/editors/sculpt_paint/sculpt.cc b/source/blender/editors/sculpt_paint/sculpt.cc index b5134ee8044..25ee9d79f80 100644 --- a/source/blender/editors/sculpt_paint/sculpt.cc +++ b/source/blender/editors/sculpt_paint/sculpt.cc @@ -81,11 +81,11 @@ using blender::float3; using blender::IndexRange; +using blender::int2; using blender::MutableSpan; using blender::Set; using blender::Span; using blender::Vector; -using blender::int2; static float sculpt_calc_radius(ViewContext *vc, const Brush *brush, @@ -6483,16 +6483,16 @@ static void sculpt_stroke_update_step(bContext *C, } cache->stroke_distance_t += stroke_delta; - if (sd->flags & (SCULPT_DYNTOPO_DETAIL_CONSTANT | SCULPT_DYNTOPO_DETAIL_MANUAL)) { + if (ELEM(ss->cached_dyntopo.mode, DYNTOPO_DETAIL_CONSTANT, DYNTOPO_DETAIL_MANUAL)) { float object_space_constant_detail = 1.0f / (ss->cached_dyntopo.constant_detail * mat4_to_scale(ob->object_to_world)); BKE_pbvh_bmesh_detail_size_set(ss->pbvh, object_space_constant_detail, 0.4f); } - else if (sd->flags & SCULPT_DYNTOPO_DETAIL_BRUSH) { + else if (ss->cached_dyntopo.mode == DYNTOPO_DETAIL_BRUSH) { BKE_pbvh_bmesh_detail_size_set( ss->pbvh, ss->cache->radius * ss->cached_dyntopo.detail_percent / 100.0f, 0.4f); } - else { + else { /* Relative mode. */ BKE_pbvh_bmesh_detail_size_set(ss->pbvh, (ss->cache->radius / ss->cache->dyntopo_pixel_radius) * (ss->cached_dyntopo.detail_size * U.pixelsize) / 0.4f, diff --git a/source/blender/editors/sculpt_paint/sculpt_api.cc b/source/blender/editors/sculpt_paint/sculpt_api.cc index cce0b8a85d8..b3a405f67e9 100644 --- a/source/blender/editors/sculpt_paint/sculpt_api.cc +++ b/source/blender/editors/sculpt_paint/sculpt_api.cc @@ -595,6 +595,11 @@ void SCULPT_ensure_persistent_layers(SculptSession *ss, Object *ob) void SCULPT_apply_dyntopo_settings(SculptSession *ss, Sculpt *sculpt, Brush *brush) { + if (!brush) { + ss->cached_dyntopo = sculpt->dyntopo; + return; + } + DynTopoSettings *ds1 = &brush->dyntopo; DynTopoSettings *ds2 = &sculpt->dyntopo; diff --git a/source/blender/editors/sculpt_paint/sculpt_detail.cc b/source/blender/editors/sculpt_paint/sculpt_detail.cc index 5b51154faa3..07da9f0f1d3 100644 --- a/source/blender/editors/sculpt_paint/sculpt_detail.cc +++ b/source/blender/editors/sculpt_paint/sculpt_detail.cc @@ -40,6 +40,8 @@ #include #include +#include + /* -------------------------------------------------------------------- */ /** \name Internal Utilities * \{ */ @@ -104,12 +106,12 @@ static int sculpt_detail_flood_fill_exec(bContext *C, wmOperator *op) sub_v3_v3v3(dim, bb_max, bb_min); size = max_fff(dim[0], dim[1], dim[2]); - float constant_detail = brush->dyntopo.constant_detail; - float detail_range = brush->dyntopo.detail_range; + SCULPT_apply_dyntopo_settings(ss, sd, brush); + float detail_range = ss->cached_dyntopo.detail_range; /* Update topology size. */ - float object_space_constant_detail = 1.0f / - (sd->constant_detail * mat4_to_scale(ob->object_to_world)); + float object_space_constant_detail = 1.0f / (ss->cached_dyntopo.constant_detail * + mat4_to_scale(ob->object_to_world)); BKE_pbvh_bmesh_detail_size_set(ss->pbvh, object_space_constant_detail, detail_range); BKE_pbvh_set_bm_log(ss->pbvh, ss->bm_log); @@ -317,8 +319,8 @@ static void sample_detail_dyntopo(bContext *C, ViewContext *vc, const int mval[2 if (srd.hit && srd.edge_length > 0.0f) { DynTopoSettings *dyntopo = brush->dyntopo.inherit & DYNTOPO_INHERIT_CONSTANT_DETAIL ? - &brush->dyntopo : - &sd->dyntopo; + &sd->dyntopo : + &brush->dyntopo; /* Convert edge length to world space detail resolution. */ dyntopo->constant_detail = 1.0f / (srd.edge_length * mat4_to_scale(ob->object_to_world)); @@ -476,34 +478,34 @@ void SCULPT_OT_sample_detail_size(wmOperatorType *ot) * falls back to radial control for the remaining methods). * \{ */ -static void set_brush_rc_props(PointerRNA *ptr, const char *prop) -{ - char *path = BLI_sprintfN("tool_settings.sculpt.brush.%s", prop); - RNA_string_set(ptr, "data_path_primary", path); - MEM_freeN(path); -} - static void sculpt_detail_size_set_radial_control(bContext *C) { Sculpt *sd = CTX_data_tool_settings(C)->sculpt; + Brush *brush = BKE_paint_brush(&sd->paint); PointerRNA props_ptr; wmOperatorType *ot = WM_operatortype_find("WM_OT_radial_control", true); WM_operator_properties_create_ptr(&props_ptr, ot); - if (sd->flags & (SCULPT_DYNTOPO_DETAIL_CONSTANT | SCULPT_DYNTOPO_DETAIL_MANUAL)) { - set_brush_rc_props(&props_ptr, "constant_detail_resolution"); - RNA_string_set( - &props_ptr, "data_path_primary", "tool_settings.sculpt.constant_detail_resolution"); + int mode = brush->dyntopo.inherit & DYNTOPO_INHERIT_MODE ? sd->dyntopo.mode : + brush->dyntopo.mode; + std::string base = "tool_settings.sculpt"; + if (!(brush->dyntopo.inherit & DYNTOPO_INHERIT_MODE)) { + base += ".brush"; } - else if (sd->flags & SCULPT_DYNTOPO_DETAIL_BRUSH) { - set_brush_rc_props(&props_ptr, "constant_detail_resolution"); - RNA_string_set(&props_ptr, "data_path_primary", "tool_settings.sculpt.detail_percent"); + + if (ELEM(mode, DYNTOPO_DETAIL_MANUAL, DYNTOPO_DETAIL_CONSTANT)) { + base += ".constant_detail"; + RNA_string_set(&props_ptr, "data_path_primary", base.c_str()); } - else { - set_brush_rc_props(&props_ptr, "detail_size"); - RNA_string_set(&props_ptr, "data_path_primary", "tool_settings.sculpt.detail_size"); + else if (mode == DYNTOPO_DETAIL_BRUSH) { + base += ".detail_percent"; + RNA_string_set(&props_ptr, "data_path_primary", base.c_str()); + } + else { /* Relative mode. */ + base += ".detail_size"; + RNA_string_set(&props_ptr, "data_path_primary", base.c_str()); } WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &props_ptr, nullptr); -- 2.30.2 From d464132d63753428b5a545ad090d8caa03f28aeb Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Mon, 24 Apr 2023 11:45:08 -0700 Subject: [PATCH 067/279] temp-sculpt-dyntopo: Fix detail set operators --- source/blender/bmesh/intern/bmesh_log.cc | 2 +- .../editors/sculpt_paint/sculpt_detail.cc | 31 +++++++++++++++---- 2 files changed, 26 insertions(+), 7 deletions(-) diff --git a/source/blender/bmesh/intern/bmesh_log.cc b/source/blender/bmesh/intern/bmesh_log.cc index b2431f6134c..315a187c6a9 100644 --- a/source/blender/bmesh/intern/bmesh_log.cc +++ b/source/blender/bmesh/intern/bmesh_log.cc @@ -857,7 +857,7 @@ struct BMLog { if (!first_entry) { first_entry = entry; } - else { + else if (current_entry) { current_entry->next = entry; } diff --git a/source/blender/editors/sculpt_paint/sculpt_detail.cc b/source/blender/editors/sculpt_paint/sculpt_detail.cc index 07da9f0f1d3..8cbf279719a 100644 --- a/source/blender/editors/sculpt_paint/sculpt_detail.cc +++ b/source/blender/editors/sculpt_paint/sculpt_detail.cc @@ -495,6 +495,8 @@ static void sculpt_detail_size_set_radial_control(bContext *C) base += ".brush"; } + base += ".dyntopo"; + if (ELEM(mode, DYNTOPO_DETAIL_MANUAL, DYNTOPO_DETAIL_CONSTANT)) { base += ".constant_detail"; RNA_string_set(&props_ptr, "data_path_primary", base.c_str()); @@ -560,6 +562,7 @@ struct DyntopoDetailSizeEditCustomData { float init_detail_size; float accurate_detail_size; float detail_size; + float detail_range; float radius; float preview_tri[3][3]; @@ -581,7 +584,7 @@ static void dyntopo_detail_size_parallel_lines_draw(uint pos3d, * all edges have the exact maximum length, which never happens in practice. As the minimum edge * length for dyntopo is 0.4 * max_edge_length, this adjust the detail size to the average * between max and min edge length so the preview is more accurate. */ - object_space_constant_detail *= 0.7f; + object_space_constant_detail *= 1.0f - cd->detail_range * 0.5f; const float total_len = len_v3v3(cd->preview_tri[0], cd->preview_tri[1]); const int tot_lines = int(total_len / object_space_constant_detail) + 1; @@ -734,6 +737,7 @@ static int dyntopo_detail_size_edit_modal(bContext *C, wmOperator *op, const wmE DyntopoDetailSizeEditCustomData *cd = static_cast( op->customdata); Sculpt *sd = CTX_data_tool_settings(C)->sculpt; + Brush *brush = BKE_paint_brush(&sd->paint); /* Cancel modal operator */ if ((event->type == EVT_ESCKEY && event->val == KM_PRESS) || @@ -748,7 +752,13 @@ static int dyntopo_detail_size_edit_modal(bContext *C, wmOperator *op, const wmE (event->type == EVT_RETKEY && event->val == KM_PRESS) || (event->type == EVT_PADENTER && event->val == KM_PRESS)) { ED_region_draw_cb_exit(region->type, cd->draw_handle); - sd->constant_detail = cd->detail_size; + + if (brush->dyntopo.inherit & DYNTOPO_INHERIT_CONSTANT_DETAIL) { + sd->dyntopo.constant_detail = cd->detail_size; + } + else { + brush->dyntopo.constant_detail = cd->detail_size; + } ss->draw_faded_cursor = false; MEM_freeN(op->customdata); ED_region_tag_redraw(region); @@ -781,10 +791,13 @@ static int dyntopo_detail_size_edit_modal(bContext *C, wmOperator *op, const wmE static int dyntopo_detail_size_edit_invoke(bContext *C, wmOperator *op, const wmEvent *event) { Sculpt *sd = CTX_data_tool_settings(C)->sculpt; + Brush *brush = BKE_paint_brush(&sd->paint); /* Fallback to radial control for modes other than SCULPT_DYNTOPO_DETAIL_CONSTANT [same as in * SCULPT_OT_set_detail_size]. */ - if (!(sd->flags & (SCULPT_DYNTOPO_DETAIL_CONSTANT | SCULPT_DYNTOPO_DETAIL_MANUAL))) { + int mode = !brush || brush->dyntopo.inherit & DYNTOPO_INHERIT_MODE ? sd->dyntopo.mode : + brush->dyntopo.mode; + if (!ELEM(mode, DYNTOPO_DETAIL_MANUAL, DYNTOPO_DETAIL_CONSTANT)) { sculpt_detail_size_set_radial_control(C); return OPERATOR_FINISHED; @@ -793,18 +806,24 @@ static int dyntopo_detail_size_edit_invoke(bContext *C, wmOperator *op, const wm /* Special method for SCULPT_DYNTOPO_DETAIL_CONSTANT. */ ARegion *region = CTX_wm_region(C); Object *active_object = CTX_data_active_object(C); - Brush *brush = BKE_paint_brush(&sd->paint); DyntopoDetailSizeEditCustomData *cd = MEM_cnew(__func__); + int constant_detail = brush->dyntopo.inherit & DYNTOPO_INHERIT_CONSTANT_DETAIL ? + sd->dyntopo.constant_detail : + brush->dyntopo.constant_detail; + /* Initial operator Custom Data setup. */ + cd->detail_range = brush->dyntopo.inherit & DYNTOPO_INHERIT_DETAIL_RANGE ? + sd->dyntopo.detail_range : + brush->dyntopo.detail_range; cd->draw_handle = ED_region_draw_cb_activate( region->type, dyntopo_detail_size_edit_draw, cd, REGION_DRAW_POST_VIEW); cd->active_object = active_object; cd->init_mval[0] = event->mval[0]; cd->init_mval[1] = event->mval[1]; - cd->detail_size = sd->constant_detail; - cd->init_detail_size = sd->constant_detail; + cd->detail_size = constant_detail; + cd->init_detail_size = constant_detail; copy_v4_v4(cd->outline_col, brush->add_col); op->customdata = cd; -- 2.30.2 From b3838ec90d0a32f85d8be4d1f786018ca1d63bb5 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Mon, 24 Apr 2023 12:45:39 -0700 Subject: [PATCH 068/279] temp-sculpt-dyntopo: Remove editmode fair op --- source/blender/editors/mesh/CMakeLists.txt | 1 - source/blender/editors/mesh/editmesh_fair.c | 23 --------------------- source/blender/editors/mesh/mesh_intern.h | 3 --- source/blender/editors/mesh/mesh_ops.c | 2 -- 4 files changed, 29 deletions(-) diff --git a/source/blender/editors/mesh/CMakeLists.txt b/source/blender/editors/mesh/CMakeLists.txt index 83d86a9568b..faf28d594fc 100644 --- a/source/blender/editors/mesh/CMakeLists.txt +++ b/source/blender/editors/mesh/CMakeLists.txt @@ -37,7 +37,6 @@ set(SRC editmesh_extrude_screw.c editmesh_extrude_spin.c editmesh_extrude_spin_gizmo.c - editmesh_fair.c editmesh_inset.c editmesh_intersect.c editmesh_knife.c diff --git a/source/blender/editors/mesh/editmesh_fair.c b/source/blender/editors/mesh/editmesh_fair.c index 964c8562b24..52de21dbb84 100644 --- a/source/blender/editors/mesh/editmesh_fair.c +++ b/source/blender/editors/mesh/editmesh_fair.c @@ -135,26 +135,3 @@ static int edbm_fair_vertices_exec(bContext *C, wmOperator *op) MEM_freeN(objects); return OPERATOR_FINISHED; } - -void MESH_OT_fair_vertices(wmOperatorType *ot) -{ - /* description */ - ot->name = "Fair Vertices"; - ot->idname = "MESH_OT_fair_vertices"; - ot->description = "Create a smooth as possible geometry patch"; - - /* callbacks */ - ot->exec = edbm_fair_vertices_exec; - ot->poll = ED_operator_editmesh; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - /* properties */ - RNA_def_enum(ot->srna, - "mode", - prop_edit_mesh_fair_selection_mode_items, - MESH_FAIRING_DEPTH_POSITION, - "Mode", - ""); -} diff --git a/source/blender/editors/mesh/mesh_intern.h b/source/blender/editors/mesh/mesh_intern.h index 31579318367..9b493eade56 100644 --- a/source/blender/editors/mesh/mesh_intern.h +++ b/source/blender/editors/mesh/mesh_intern.h @@ -307,9 +307,6 @@ void MESH_OT_paint_mask_extract(struct wmOperatorType *ot); void MESH_OT_face_set_extract(struct wmOperatorType *ot); void MESH_OT_paint_mask_slice(struct wmOperatorType *ot); -/* *** editmesh_fair.c *** */ -void MESH_OT_fair_vertices(struct wmOperatorType *ot); - /** Called in transform_ops.c, on each regeneration of key-maps. */ struct wmKeyMap *point_normals_modal_keymap(wmKeyConfig *keyconf); diff --git a/source/blender/editors/mesh/mesh_ops.c b/source/blender/editors/mesh/mesh_ops.c index 9bd415b69a5..68d4437f0f8 100644 --- a/source/blender/editors/mesh/mesh_ops.c +++ b/source/blender/editors/mesh/mesh_ops.c @@ -165,8 +165,6 @@ void ED_operatortypes_mesh(void) WM_operatortype_append(MESH_OT_bevel); - WM_operatortype_append(MESH_OT_fair_vertices); - WM_operatortype_append(MESH_OT_bridge_edge_loops); WM_operatortype_append(MESH_OT_inset); WM_operatortype_append(MESH_OT_offset_edge_loops); -- 2.30.2 From 34c0a7690d1895ac0f2c9fa5b2f5ce17897c216a Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Wed, 26 Apr 2023 01:14:34 -0700 Subject: [PATCH 069/279] temp-sculpt-dyntopo: Revert old mempool ASAN code prior to merge --- source/blender/blenlib/BLI_mempool.h | 48 -- source/blender/blenlib/intern/BLI_mempool.c | 598 ++------------------ 2 files changed, 34 insertions(+), 612 deletions(-) diff --git a/source/blender/blenlib/BLI_mempool.h b/source/blender/blenlib/BLI_mempool.h index c4c2a4de976..c00bc683525 100644 --- a/source/blender/blenlib/BLI_mempool.h +++ b/source/blender/blenlib/BLI_mempool.h @@ -19,29 +19,11 @@ struct BLI_mempool_chunk; typedef struct BLI_mempool BLI_mempool; -BLI_mempool *BLI_mempool_create_ex(unsigned int esize, - unsigned int totelem, - unsigned int pchunk, - unsigned int flag, - const char *tag) - ATTR_MALLOC ATTR_WARN_UNUSED_RESULT ATTR_RETURNS_NONNULL; -; - -//#define DEBUG_MEMPOOL_LEAKS - -#ifdef DEBUG_MEMPOOL_LEAKS -ATTR_MALLOC ATTR_WARN_UNUSED_RESULT ATTR_RETURNS_NONNULL; - -# define BLI_mempool_create(esize, totelem, pchunk, flag) \ - BLI_mempool_create_ex(esize, totelem, pchunk, flag, __func__) -#else BLI_mempool *BLI_mempool_create(unsigned int esize, unsigned int elem_num, unsigned int pchunk, unsigned int flag) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT ATTR_RETURNS_NONNULL; -#endif - void *BLI_mempool_alloc(BLI_mempool *pool) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT ATTR_RETURNS_NONNULL ATTR_NONNULL(1); void *BLI_mempool_calloc(BLI_mempool *pool) @@ -123,11 +105,6 @@ enum { * order of allocation when no chunks have been freed. */ BLI_MEMPOOL_ALLOW_ITER = (1 << 0), - - /* allow random access, implies BLI_MEMPOOL_ALLOW_ITER since we - need the freewords to detect free state of elements*/ - BLI_MEMPOOL_RANDOM_ACCESS = (1 << 1) | (1 << 0), - BLI_MEMPOOL_IGNORE_FREE = (1 << 2), /* Used for debugging. */ }; /** @@ -139,31 +116,6 @@ void BLI_mempool_iternew(BLI_mempool *pool, BLI_mempool_iter *iter) ATTR_NONNULL */ void *BLI_mempool_iterstep(BLI_mempool_iter *iter) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); -/* -This preallocates a mempool suitable for threading. totelem elements are preallocated -in chunks of size pchunk, and returned in r_chunks. -*/ - -BLI_mempool *BLI_mempool_create_for_tasks(const unsigned int esize, - int totelem, - const int pchunk, - void ***r_chunks, - int *r_totchunk, - int *r_esize, - int flag); - -// memory coherence stuff -int BLI_mempool_find_elems_fuzzy( - BLI_mempool *pool, int idx, int range, void **r_elems, int r_elems_size); - -int BLI_mempool_get_size(BLI_mempool *pool); -int BLI_mempool_find_real_index(BLI_mempool *pool, void *ptr); - -/* Sets BLI_MEMPOOL_IGNORE_FREE flag, ignores call to BLI_mempool_free. - * Used for debugging. - */ -void BLI_mempool_ignore_free(BLI_mempool *pool); - #ifdef __cplusplus } #endif diff --git a/source/blender/blenlib/intern/BLI_mempool.c b/source/blender/blenlib/intern/BLI_mempool.c index bffc7e8adba..04670733f56 100644 --- a/source/blender/blenlib/intern/BLI_mempool.c +++ b/source/blender/blenlib/intern/BLI_mempool.c @@ -13,21 +13,15 @@ * (optionally when using the #BLI_MEMPOOL_ALLOW_ITER flag). */ -#include #include #include #include "atomic_ops.h" -#include "BLI_asan.h" -#include "BLI_mempool.h" /* own include */ -#include "BLI_mempool_private.h" /* own include */ -#include "BLI_string.h" #include "BLI_utildefines.h" -#ifndef WITH_DNA_GHASH -# include "BLI_threads.h" -#endif +#include "BLI_mempool.h" /* own include */ +#include "BLI_mempool_private.h" /* own include */ #include "MEM_guardedalloc.h" @@ -37,13 +31,6 @@ # include "valgrind/memcheck.h" #endif -#if (defined(__SANITIZE_ADDRESS__) || __has_feature(address_sanitizer)) -# define POISON_REDZONE_SIZE 32 -# define HAVE_MEMPOOL_ASAN -#else -# define POISON_REDZONE_SIZE 0 -#endif - /* NOTE: copied from BLO_blend_defs.h, don't use here because we're in BLI. */ #ifdef __BIG_ENDIAN__ /* Big Endian */ @@ -108,21 +95,12 @@ typedef struct BLI_mempool_chunk { * The mempool, stores and tracks memory \a chunks and elements within those chunks \a free. */ struct BLI_mempool { -#if !defined(WITH_DNA_GHASH) && (defined(__SANITIZE_ADDRESS__) || __has_feature(address_sanitizer)) - ThreadMutex mutex; -#endif /** Single linked list of allocated chunks. */ BLI_mempool_chunk *chunks; /** Keep a pointer to the last, so we can append new chunks there * this is needed for iteration so we can loop over chunks in the order added. */ BLI_mempool_chunk *chunk_tail; - /* only used if BLI_MEMPOOL_RANDOM_ACCESS is true*/ - BLI_mempool_chunk **chunktable; - - /* only used if BLI_MEMPOOL_RANDOM_ACCESS is true*/ - int totchunk; - /** Element size in bytes. */ uint esize; /** Chunk size in bytes. */ @@ -132,24 +110,16 @@ struct BLI_mempool { uint flag; /* keeps aligned to 16 bits */ -#ifdef HAVE_MEMPOOL_ASAN - char poisoned[256]; -#endif - /** Free element list. Interleaved into chunk data. */ BLI_freenode *free; /** Use to know how many chunks to keep for #BLI_mempool_clear. */ uint maxchunks; /** Number of elements currently in use. */ - int totused; + uint totused; #ifdef USE_TOTALLOC /** Number of elements allocated in total. */ uint totalloc; #endif - - char *memtag; - char *memtag_chunk; - bool dead; }; #define MEMPOOL_ELEM_SIZE_MIN (sizeof(void *) * 2) @@ -162,54 +132,6 @@ struct BLI_mempool { /** Extra bytes implicitly used for every chunk alloc. */ #define CHUNK_OVERHEAD (uint)(MEM_SIZE_OVERHEAD + sizeof(BLI_mempool_chunk)) -static void mempool_poison(BLI_mempool *pool) -{ -#if (defined(__SANITIZE_ADDRESS__) || __has_feature(address_sanitizer)) -# ifndef WITH_DNA_GHASH - BLI_mutex_unlock(&pool->mutex); -# endif - -# ifndef WITH_DNA_GHASH - char *ptr = (char *)pool; - size_t offset = offsetof(BLI_mempool, mutex) + sizeof(ThreadMutex); - BLI_asan_poison(ptr + offset, sizeof(*pool) - offset); -# else - BLI_asan_poison(pool, sizeof(*pool)); -# endif -#endif -} - -static void mempool_poison_no_unlock(BLI_mempool *pool) -{ -#if (defined(__SANITIZE_ADDRESS__) || __has_feature(address_sanitizer)) - -# ifndef WITH_DNA_GHASH - char *ptr = (char *)pool; - size_t offset = offsetof(BLI_mempool, mutex) + sizeof(ThreadMutex); - BLI_asan_poison(ptr + offset, sizeof(*pool) - offset); -# else - BLI_asan_poison(pool, sizeof(*pool)); -# endif -#endif -} - -static void mempool_unpoison(BLI_mempool *pool) -{ -#if (defined(__SANITIZE_ADDRESS__) || __has_feature(address_sanitizer)) -# ifndef WITH_DNA_GHASH - BLI_mutex_lock(&pool->mutex); -# endif - -# ifndef WITH_DNA_GHASH - char *ptr = (char *)pool; - size_t offset = offsetof(BLI_mempool, mutex) + sizeof(ThreadMutex); - BLI_asan_unpoison(ptr + offset, sizeof(*pool) - offset); -# else - BLI_asan_unpoison(pool, sizeof(*pool)); -# endif -#endif -} - #ifdef USE_CHUNK_POW2 static uint power_of_2_max_u(uint x) { @@ -223,33 +145,6 @@ static uint power_of_2_max_u(uint x) } #endif -static void mempool_update_chunktable(BLI_mempool *pool) -{ - if (!(pool->flag & BLI_MEMPOOL_RANDOM_ACCESS)) { - return; - } - - pool->totchunk = 0; - BLI_mempool_chunk *chunk = pool->chunks; - - while (chunk) { - pool->totchunk++; - chunk = chunk->next; - } - - MEM_SAFE_FREE(pool->chunktable); - pool->chunktable = (BLI_mempool_chunk **)MEM_mallocN( - sizeof(pool->chunktable) * (size_t)pool->totchunk, "mempool chunktable"); - - int i = 0; - chunk = pool->chunks; - - while (chunk) { - pool->chunktable[i++] = chunk; - chunk = chunk->next; - } -} - BLI_INLINE BLI_mempool_chunk *mempool_chunk_find(BLI_mempool_chunk *head, uint index) { while (index-- && head) { @@ -271,7 +166,7 @@ BLI_INLINE uint mempool_maxchunks(const uint elem_num, const uint pchunk) static BLI_mempool_chunk *mempool_chunk_alloc(BLI_mempool *pool) { - return BLI_asan_safe_malloc(sizeof(BLI_mempool_chunk) + (size_t)pool->csize, pool->memtag); + return MEM_mallocN(sizeof(BLI_mempool_chunk) + (size_t)pool->csize, "BLI_Mempool Chunk"); } /** @@ -291,26 +186,6 @@ static BLI_freenode *mempool_chunk_add(BLI_mempool *pool, BLI_freenode *curnode = CHUNK_DATA(mpchunk); uint j; - if (pool->flag & BLI_MEMPOOL_RANDOM_ACCESS) { - if (!pool->chunktable || - MEM_allocN_len(pool->chunktable) / sizeof(void *) <= (size_t)pool->totchunk) { - void *old = pool->chunktable; - - int size = (int)pool->totchunk + 2; - size += size >> 1; - - pool->chunktable = MEM_mallocN(sizeof(void *) * (size_t)size, "mempool chunktable"); - - if (old) { - memcpy(pool->chunktable, old, sizeof(void *) * (size_t)pool->totchunk); - } - - MEM_SAFE_FREE(old); - } - - pool->chunktable[pool->totchunk++] = mpchunk; - } - /* append */ if (pool->chunk_tail) { pool->chunk_tail->next = mpchunk; @@ -331,41 +206,22 @@ static BLI_freenode *mempool_chunk_add(BLI_mempool *pool, j = pool->pchunk; if (pool->flag & BLI_MEMPOOL_ALLOW_ITER) { while (j--) { - BLI_freenode *next; - - BLI_asan_unpoison(curnode, pool->esize - POISON_REDZONE_SIZE); - - curnode->next = next = NODE_STEP_NEXT(curnode); + curnode->next = NODE_STEP_NEXT(curnode); curnode->freeword = FREEWORD; - - BLI_asan_poison(curnode, pool->esize); - - curnode = next; + curnode = curnode->next; } } else { while (j--) { - BLI_freenode *next; - - BLI_asan_unpoison(curnode, pool->esize - POISON_REDZONE_SIZE); - curnode->next = next = NODE_STEP_NEXT(curnode); - BLI_asan_poison(curnode, pool->esize); - - curnode = next; + curnode->next = NODE_STEP_NEXT(curnode); + curnode = curnode->next; } } /* terminate the list (rewind one) * will be overwritten if 'curnode' gets passed in again as 'last_tail' */ - - BLI_asan_unpoison(curnode, pool->esize - POISON_REDZONE_SIZE); - BLI_asan_poison(curnode, pool->esize); - curnode = NODE_STEP_PREV(curnode); - - BLI_asan_unpoison(curnode, pool->esize - POISON_REDZONE_SIZE); curnode->next = NULL; - BLI_asan_poison(curnode, pool->esize); #ifdef USE_TOTALLOC pool->totalloc += pool->pchunk; @@ -373,175 +229,35 @@ static BLI_freenode *mempool_chunk_add(BLI_mempool *pool, /* final pointer in the previously allocated chunk is wrong */ if (last_tail) { - BLI_asan_unpoison(last_tail, pool->esize - POISON_REDZONE_SIZE); last_tail->next = CHUNK_DATA(mpchunk); - BLI_asan_poison(last_tail, pool->esize); } return curnode; } -/** - * Preallocates a mempool suitable for threading. elem_num elements are preallocated - * in chunks of size pchunk, and returned in r_chunks. The idea is to pass these - * to tasks. - */ -BLI_mempool *BLI_mempool_create_for_tasks(const unsigned int esize, - int elem_num, - const int pchunk, - void ***r_chunks, - int *r_totchunk, - int *r_esize, - int flag) +static void mempool_chunk_free(BLI_mempool_chunk *mpchunk) { - BLI_mempool *pool = BLI_mempool_create(esize, 0, (uint)pchunk, (uint)flag); - - mempool_unpoison(pool); - - // override pchunk, may not be a power of 2 - pool->pchunk = (uint)pchunk; - pool->csize = (uint)pchunk * pool->esize; - - if (elem_num % pchunk == 0) { - pool->maxchunks = (uint)elem_num / (uint)pchunk; - } - else { - pool->maxchunks = (uint)elem_num / (uint)pchunk + 1; - } - - if (elem_num) { - BLI_freenode *last_tail = NULL; - - /* Allocate the actual chunks. */ - for (uint i = 0; i < pool->maxchunks; i++) { - BLI_mempool_chunk *mpchunk = mempool_chunk_alloc(pool); - last_tail = mempool_chunk_add(pool, mpchunk, last_tail); - } - } - - void **chunks = MEM_callocN(sizeof(void *) * pool->maxchunks, - "BLI_mempool_create_for_tasks r_chunks"); - - unsigned int totalloc = 0; - *r_totchunk = 0; - - BLI_mempool_chunk *chunk = pool->chunks, *lastchunk = NULL; - - while (chunk) { - lastchunk = chunk; - totalloc += pool->pchunk; - chunk = chunk->next; - } - - pool->totused = (int)totalloc; - pool->free = NULL; - - int i = (int)pool->pchunk - 1; - - while (lastchunk && totalloc > (uint)elem_num) { - if (i < 0) { - BLI_mempool_chunk *lastchunk2 = NULL; - - for (chunk = pool->chunks; chunk; chunk = chunk->next) { - if (chunk == lastchunk) { - lastchunk = lastchunk2; - } - } - - if (!lastchunk) { - break; - } - - i = (int)pool->pchunk - 1; - } - - char *elem = CHUNK_DATA(lastchunk); - elem += pool->esize * (unsigned int)i; - - BLI_mempool_free(pool, elem); - - totalloc--; - i--; - } - - int ci = 0; - - chunk = pool->chunks; - while (chunk && chunk != lastchunk) { - chunks[ci++] = CHUNK_DATA(chunk); - chunk = chunk->next; - } - - if (lastchunk && i >= 0) { - chunks[ci++] = CHUNK_DATA(lastchunk); - } - - *r_totchunk = ci; - *r_chunks = (void **)chunks; - *r_esize = (int)pool->esize; - - return pool; + MEM_freeN(mpchunk); } -static void mempool_chunk_free(BLI_mempool_chunk *mpchunk, BLI_mempool *pool) -{ - BLI_asan_unpoison(mpchunk, sizeof(BLI_mempool_chunk) + pool->esize * pool->csize); - BLI_asan_safe_free(mpchunk); -} - -static void mempool_chunk_free_all(BLI_mempool_chunk *mpchunk, BLI_mempool *pool) +static void mempool_chunk_free_all(BLI_mempool_chunk *mpchunk) { BLI_mempool_chunk *mpchunk_next; for (; mpchunk; mpchunk = mpchunk_next) { - BLI_asan_unpoison(mpchunk, sizeof(BLI_mempool_chunk)); mpchunk_next = mpchunk->next; - - mempool_chunk_free(mpchunk, pool); + mempool_chunk_free(mpchunk); } } -#ifdef BLI_mempool_create -# undef BLI_mempool_create -#endif - BLI_mempool *BLI_mempool_create(uint esize, uint elem_num, uint pchunk, uint flag) -{ - return BLI_mempool_create_ex(esize, elem_num, pchunk, flag, ""); -} - -BLI_mempool *BLI_mempool_create_ex( - uint esize, uint elem_num, uint pchunk, uint flag, const char *tag) { BLI_mempool *pool; BLI_freenode *last_tail = NULL; uint i, maxchunks; - char buf[512]; - if (tag) { - sprintf(buf, "Mempool:%s", tag); - } - else { - sprintf(buf, "memory pool"); - } - - char *memtag = strdup(buf); - /* allocate the pool structure */ - pool = MEM_mallocN(sizeof(BLI_mempool), memtag); - - strcat(buf, " chunk"); - -#if !defined(WITH_DNA_GHASH) && (defined(__SANITIZE_ADDRESS__) || __has_feature(address_sanitizer)) - BLI_mutex_init(&pool->mutex); -#endif - - pool->memtag = memtag; - pool->memtag_chunk = strdup(buf); - pool->dead = false; - - pool->totchunk = 0; - pool->chunktable = NULL; + pool = MEM_mallocN(sizeof(BLI_mempool), "memory pool"); /* set the elem size */ if (esize < (int)MEMPOOL_ELEM_SIZE_MIN) { @@ -552,8 +268,6 @@ BLI_mempool *BLI_mempool_create_ex( esize = MAX2(esize, (uint)sizeof(BLI_freenode)); } - esize += POISON_REDZONE_SIZE; - maxchunks = mempool_maxchunks(elem_num, pchunk); pool->chunks = NULL; @@ -599,19 +313,11 @@ BLI_mempool *BLI_mempool_create_ex( VALGRIND_CREATE_MEMPOOL(pool, 0, false); #endif -#ifdef HAVE_MEMPOOL_ASAN - BLI_asan_poison(pool->poisoned, sizeof(pool->poisoned)); -#endif - - mempool_poison_no_unlock(pool); - return pool; } void *BLI_mempool_alloc(BLI_mempool *pool) { - mempool_unpoison(pool); - BLI_freenode *free_pop; if (UNLIKELY(pool->free == NULL)) { @@ -622,8 +328,6 @@ void *BLI_mempool_alloc(BLI_mempool *pool) free_pop = pool->free; - BLI_asan_unpoison(free_pop, pool->esize - POISON_REDZONE_SIZE); - BLI_assert(pool->chunk_tail->next == NULL); if (pool->flag & BLI_MEMPOOL_ALLOW_ITER) { @@ -637,141 +341,18 @@ void *BLI_mempool_alloc(BLI_mempool *pool) VALGRIND_MEMPOOL_ALLOC(pool, free_pop, pool->esize); #endif - mempool_poison(pool); - return (void *)free_pop; } void *BLI_mempool_calloc(BLI_mempool *pool) { void *retval = BLI_mempool_alloc(pool); - - mempool_unpoison(pool); - memset(retval, 0, (size_t)pool->esize - POISON_REDZONE_SIZE); - mempool_poison(pool); - + memset(retval, 0, (size_t)pool->esize); return retval; } -int BLI_mempool_find_real_index(BLI_mempool *pool, void *ptr) -{ - mempool_unpoison(pool); - - BLI_mempool_chunk *chunk = pool->chunks; - uintptr_t uptr = (uintptr_t)ptr; - uintptr_t cptr; - int chunki = 0; - - while (chunk) { - cptr = (uintptr_t)chunk; - - if (uptr >= cptr && uptr < cptr + pool->csize) { - break; - } - - chunk = chunk->next; - chunki++; - } - - if (!chunk) { - mempool_poison(pool); - return -1; // failed - } - - int ret = chunki * (int)pool->pchunk + ((int)(uptr - cptr)) / (int)pool->esize; - - mempool_poison(pool); - - return ret; -} - -/*finds an element in pool that's roughly at idx, idx*/ -int BLI_mempool_find_elems_fuzzy( - BLI_mempool *pool, int idx, int range, void **r_elems, int r_elems_size) -{ - mempool_unpoison(pool); - - int istart = idx - range, iend = idx + range; - istart = MAX2(istart, 0); - - int elem_num = 0; - - for (int i = istart; i < iend; i++) { - int chunki = i / (int)pool->pchunk; - if (chunki >= (int)pool->totchunk) { - break; - } - - int idx2 = i % (int)pool->pchunk; - - BLI_mempool_chunk *chunk = pool->chunktable[chunki]; - char *data = (char *)CHUNK_DATA(chunk); - void *ptr = data + idx2 * (int)pool->esize; - - BLI_asan_unpoison(ptr, pool->esize - POISON_REDZONE_SIZE); - - BLI_freenode *fnode = (BLI_freenode *)ptr; - if (fnode->freeword == FREEWORD) { - BLI_asan_poison(ptr, pool->esize); - continue; - } - - r_elems[elem_num++] = ptr; - - if (elem_num == r_elems_size) { - break; - } - } - - mempool_poison(pool); - return elem_num; -} - -int BLI_mempool_get_size(BLI_mempool *pool) -{ - mempool_unpoison(pool); - - BLI_mempool_chunk *chunk = pool->chunks; - int ret = 0; - - while (chunk) { - chunk = chunk->next; - - ret += (int)pool->pchunk; - } - - mempool_poison(pool); - return ret; -} - -void BLI_mempool_ignore_free(BLI_mempool *pool) -{ - mempool_unpoison(pool); - pool->flag |= BLI_MEMPOOL_IGNORE_FREE; - mempool_poison(pool); -} - -/** - * Free an element from the mempool. - * - * \note doesn't protect against double frees, take care! - */ void BLI_mempool_free(BLI_mempool *pool, void *addr) { - mempool_unpoison(pool); - - if (pool->flag & BLI_MEMPOOL_IGNORE_FREE) { - BLI_freenode *newhead = addr; - - if (pool->flag & BLI_MEMPOOL_ALLOW_ITER) { - newhead->freeword = FREEWORD; - } - - BLI_asan_poison(newhead, pool->esize); - mempool_poison(pool); - return; - } - BLI_freenode *newhead = addr; #ifndef NDEBUG @@ -791,7 +372,7 @@ void BLI_mempool_free(BLI_mempool *pool, void *addr) /* Enable for debugging. */ if (UNLIKELY(mempool_debug_memset)) { - memset(addr, 255, pool->esize - POISON_REDZONE_SIZE); + memset(addr, 255, pool->esize); } #endif @@ -806,16 +387,8 @@ void BLI_mempool_free(BLI_mempool *pool, void *addr) newhead->next = pool->free; pool->free = newhead; - BLI_asan_poison(newhead, pool->esize); - pool->totused--; - if (pool->totused < 0) { - fprintf(stderr, "Corrupted mempool\n"); - fflush(stderr); - abort(); - } - #ifdef WITH_MEM_VALGRIND VALGRIND_MEMPOOL_FREE(pool, addr); #endif @@ -828,12 +401,10 @@ void BLI_mempool_free(BLI_mempool *pool, void *addr) BLI_mempool_chunk *first; first = pool->chunks; - mempool_chunk_free_all(first->next, pool); + mempool_chunk_free_all(first->next); first->next = NULL; pool->chunk_tail = first; - mempool_update_chunktable(pool); - #ifdef USE_TOTALLOC pool->totalloc = pool->pchunk; #endif @@ -848,46 +419,28 @@ void BLI_mempool_free(BLI_mempool *pool, void *addr) j = pool->pchunk; while (j--) { - BLI_asan_unpoison(curnode, pool->esize - POISON_REDZONE_SIZE); - BLI_freenode *next = curnode->next = NODE_STEP_NEXT(curnode); - BLI_asan_poison(curnode, pool->esize); - curnode = next; + curnode->next = NODE_STEP_NEXT(curnode); + curnode = curnode->next; } - - BLI_asan_unpoison(curnode, pool->esize - POISON_REDZONE_SIZE); - BLI_freenode *prev = NODE_STEP_PREV(curnode); - BLI_asan_poison(curnode, pool->esize); - - curnode = prev; - - BLI_asan_unpoison(curnode, pool->esize - POISON_REDZONE_SIZE); + curnode = NODE_STEP_PREV(curnode); curnode->next = NULL; /* terminate the list */ - BLI_asan_poison(curnode, pool->esize); #ifdef WITH_MEM_VALGRIND VALGRIND_MEMPOOL_FREE(pool, CHUNK_DATA(first)); #endif } - - mempool_poison(pool); } int BLI_mempool_len(const BLI_mempool *pool) { - mempool_unpoison((BLI_mempool *)pool); - int ret = pool->totused; - mempool_poison((BLI_mempool *)pool); - - return ret; + return (int)pool->totused; } void *BLI_mempool_findelem(BLI_mempool *pool, uint index) { - mempool_unpoison(pool); - BLI_assert(pool->flag & BLI_MEMPOOL_ALLOW_ITER); - if (index < (uint)pool->totused) { + if (index < pool->totused) { /* We could have some faster mem chunk stepping code inline. */ BLI_mempool_iter iter; void *elem; @@ -895,12 +448,9 @@ void *BLI_mempool_findelem(BLI_mempool *pool, uint index) for (elem = BLI_mempool_iterstep(&iter); index-- != 0; elem = BLI_mempool_iterstep(&iter)) { /* pass */ } - - mempool_poison(pool); return elem; } - mempool_poison(pool); return NULL; } @@ -909,20 +459,12 @@ void BLI_mempool_as_table(BLI_mempool *pool, void **data) BLI_mempool_iter iter; void *elem; void **p = data; - - mempool_unpoison(pool); BLI_assert(pool->flag & BLI_MEMPOOL_ALLOW_ITER); - mempool_poison(pool); - BLI_mempool_iternew(pool, &iter); - while ((elem = BLI_mempool_iterstep(&iter))) { *p++ = elem; } - - mempool_unpoison(pool); - BLI_assert((int)(p - data) == pool->totused); - mempool_poison(pool); + BLI_assert((uint)(p - data) == pool->totused); } void **BLI_mempool_as_tableN(BLI_mempool *pool, const char *allocstr) @@ -934,29 +476,21 @@ void **BLI_mempool_as_tableN(BLI_mempool *pool, const char *allocstr) void BLI_mempool_as_array(BLI_mempool *pool, void *data) { - mempool_unpoison(pool); - - const uint esize = pool->esize - (uint)POISON_REDZONE_SIZE; + const uint esize = pool->esize; BLI_mempool_iter iter; char *elem, *p = data; - BLI_assert(pool->flag & BLI_MEMPOOL_ALLOW_ITER); - - mempool_poison(pool); - BLI_mempool_iternew(pool, &iter); while ((elem = BLI_mempool_iterstep(&iter))) { memcpy(p, elem, (size_t)esize); p = NODE_STEP_NEXT(p); } + BLI_assert((uint)(p - (char *)data) == pool->totused * esize); } void *BLI_mempool_as_arrayN(BLI_mempool *pool, const char *allocstr) { - mempool_unpoison(pool); - char *data = MEM_malloc_arrayN((size_t)pool->totused, pool->esize, allocstr); - mempool_poison(pool); - + char *data = MEM_malloc_arrayN(pool->totused, pool->esize, allocstr); BLI_mempool_as_array(pool, data); return data; } @@ -966,9 +500,7 @@ void BLI_mempool_iternew(BLI_mempool *pool, BLI_mempool_iter *iter) BLI_assert(pool->flag & BLI_MEMPOOL_ALLOW_ITER); iter->pool = pool; - mempool_unpoison(pool); iter->curchunk = pool->chunks; - mempool_poison(pool); iter->curindex = 0; } @@ -1048,16 +580,12 @@ void *BLI_mempool_iterstep(BLI_mempool_iter *iter) return NULL; } - mempool_unpoison(iter->pool); - const uint esize = iter->pool->esize; BLI_freenode *curnode = POINTER_OFFSET(CHUNK_DATA(iter->curchunk), (esize * iter->curindex)); BLI_freenode *ret; do { ret = curnode; - BLI_asan_unpoison(ret, iter->pool->esize - POISON_REDZONE_SIZE); - if (++iter->curindex != iter->pool->pchunk) { curnode = POINTER_OFFSET(curnode, esize); } @@ -1065,21 +593,12 @@ void *BLI_mempool_iterstep(BLI_mempool_iter *iter) iter->curindex = 0; iter->curchunk = iter->curchunk->next; if (UNLIKELY(iter->curchunk == NULL)) { - BLI_asan_unpoison(ret, iter->pool->esize - POISON_REDZONE_SIZE); - void *ret2 = (ret->freeword == FREEWORD) ? NULL : ret; - - if (ret->freeword == FREEWORD) { - BLI_asan_poison(ret, iter->pool->esize); - } - - mempool_poison(iter->pool); - return ret2; + return (ret->freeword == FREEWORD) ? NULL : ret; } curnode = CHUNK_DATA(iter->curchunk); } } while (ret->freeword == FREEWORD); - mempool_poison(iter->pool); return ret; } @@ -1090,16 +609,12 @@ void *mempool_iter_threadsafe_step(BLI_mempool_threadsafe_iter *ts_iter) return NULL; } - mempool_unpoison(iter->pool); - const uint esize = iter->pool->esize; BLI_freenode *curnode = POINTER_OFFSET(CHUNK_DATA(iter->curchunk), (esize * iter->curindex)); BLI_freenode *ret; do { ret = curnode; - BLI_asan_unpoison(ret, esize - POISON_REDZONE_SIZE); - if (++iter->curindex != iter->pool->pchunk) { curnode = POINTER_OFFSET(curnode, esize); } @@ -1115,52 +630,25 @@ void *mempool_iter_threadsafe_step(BLI_mempool_threadsafe_iter *ts_iter) /* pass. */ } if (UNLIKELY(iter->curchunk == NULL)) { - if (ret->freeword == FREEWORD) { - BLI_asan_poison(ret, esize); - mempool_poison(iter->pool); - return NULL; - } - else { - mempool_poison(iter->pool); - return ret; - } + return (ret->freeword == FREEWORD) ? NULL : ret; } /* End `threadsafe` exception. */ iter->curchunk = iter->curchunk->next; if (UNLIKELY(iter->curchunk == NULL)) { - if (ret->freeword == FREEWORD) { - BLI_asan_poison(ret, iter->pool->esize); - mempool_poison(iter->pool); - return NULL; - } - else { - mempool_poison(iter->pool); - return ret; - } + return (ret->freeword == FREEWORD) ? NULL : ret; } - curnode = CHUNK_DATA(iter->curchunk); } + } while (ret->freeword == FREEWORD); - if (ret->freeword == FREEWORD) { - BLI_asan_poison(ret, iter->pool->esize); - } - else { - break; - } - } while (true); - - mempool_poison(iter->pool); return ret; } #endif -void BLI_mempool_clear_ex(BLI_mempool *pool, const int elem_num_reserve) +void BLI_mempool_clear_ex(BLI_mempool *pool, const int totelem_reserve) { - mempool_unpoison(pool); - BLI_mempool_chunk *mpchunk; BLI_mempool_chunk *mpchunk_next; uint maxchunks; @@ -1173,11 +661,11 @@ void BLI_mempool_clear_ex(BLI_mempool *pool, const int elem_num_reserve) VALGRIND_CREATE_MEMPOOL(pool, 0, false); #endif - if (elem_num_reserve == -1) { + if (totelem_reserve == -1) { maxchunks = pool->maxchunks; } else { - maxchunks = mempool_maxchunks((uint)elem_num_reserve, pool->pchunk); + maxchunks = mempool_maxchunks((uint)totelem_reserve, pool->pchunk); } /* Free all after 'pool->maxchunks'. */ @@ -1190,7 +678,7 @@ void BLI_mempool_clear_ex(BLI_mempool *pool, const int elem_num_reserve) do { mpchunk_next = mpchunk->next; - mempool_chunk_free(mpchunk, pool); + mempool_chunk_free(mpchunk); } while ((mpchunk = mpchunk_next)); } @@ -1209,8 +697,6 @@ void BLI_mempool_clear_ex(BLI_mempool *pool, const int elem_num_reserve) chunks_temp = mpchunk->next; last_tail = mempool_chunk_add(pool, mpchunk, last_tail); } - - mempool_poison(pool); } void BLI_mempool_clear(BLI_mempool *pool) @@ -1220,28 +706,12 @@ void BLI_mempool_clear(BLI_mempool *pool) void BLI_mempool_destroy(BLI_mempool *pool) { - mempool_unpoison(pool); - mempool_chunk_free_all(pool->chunks, pool); - - pool->dead = true; - - if (pool->memtag) { - free(pool->memtag); - } - if (pool->memtag_chunk) { - free(pool->memtag_chunk); - } - - MEM_SAFE_FREE(pool->chunktable); + mempool_chunk_free_all(pool->chunks); #ifdef WITH_MEM_VALGRIND VALGRIND_DESTROY_MEMPOOL(pool); #endif -#ifdef HAVE_MEMPOOL_ASAN - BLI_asan_unpoison(pool->poisoned, sizeof(pool->poisoned)); -#endif - MEM_freeN(pool); } -- 2.30.2 From b2afa609685a57648cad6bdc8aa208aaf4617f30 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Mon, 1 May 2023 13:26:18 -0700 Subject: [PATCH 070/279] temp-sculpt-dyntopo: Fix compile errors and remove unused code --- .../blender/blenkernel/intern/customdata.cc | 2 +- source/blender/blenkernel/intern/lib_id.c | 2 +- source/blender/blenlib/BLI_mempool.h | 2 + source/blender/blenlib/intern/BLI_mempool.c | 15 +- source/blender/bmesh/intern/bmesh_log.cc | 10 +- source/blender/bmesh/intern/bmesh_mesh.cc | 375 ------------------ 6 files changed, 23 insertions(+), 383 deletions(-) diff --git a/source/blender/blenkernel/intern/customdata.cc b/source/blender/blenkernel/intern/customdata.cc index b956d5d3b0f..d978cce924c 100644 --- a/source/blender/blenkernel/intern/customdata.cc +++ b/source/blender/blenkernel/intern/customdata.cc @@ -4192,7 +4192,7 @@ void CustomData_bmesh_init_pool_ex(CustomData *data, /* If there are no layers, no pool is needed just yet */ if (data->totlayer) { - data->pool = BLI_mempool_create_ex(data->totsize, totelem, chunksize, BLI_MEMPOOL_NOP, memtag); + data->pool = BLI_mempool_create(data->totsize, totelem, chunksize, BLI_MEMPOOL_NOP); } } diff --git a/source/blender/blenkernel/intern/lib_id.c b/source/blender/blenkernel/intern/lib_id.c index 92a98dd74fb..996eaa39c5f 100644 --- a/source/blender/blenkernel/intern/lib_id.c +++ b/source/blender/blenkernel/intern/lib_id.c @@ -676,7 +676,7 @@ ID *BKE_id_copy(Main *bmain, const ID *id) ID *BKE_id_copy_for_duplicate(Main *bmain, ID *id, - const uint duplicate_flags, + const eDupli_ID_Flags duplicate_flags, const int copy_flags) { if (id == NULL) { diff --git a/source/blender/blenlib/BLI_mempool.h b/source/blender/blenlib/BLI_mempool.h index c00bc683525..6372a792cc7 100644 --- a/source/blender/blenlib/BLI_mempool.h +++ b/source/blender/blenlib/BLI_mempool.h @@ -116,6 +116,8 @@ void BLI_mempool_iternew(BLI_mempool *pool, BLI_mempool_iter *iter) ATTR_NONNULL */ void *BLI_mempool_iterstep(BLI_mempool_iter *iter) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); +size_t BLI_mempool_get_size(BLI_mempool *pool); + #ifdef __cplusplus } #endif diff --git a/source/blender/blenlib/intern/BLI_mempool.c b/source/blender/blenlib/intern/BLI_mempool.c index e4cf1113794..1d6fab9440b 100644 --- a/source/blender/blenlib/intern/BLI_mempool.c +++ b/source/blender/blenlib/intern/BLI_mempool.c @@ -510,7 +510,7 @@ void BLI_mempool_free(BLI_mempool *pool, void *addr) int BLI_mempool_len(const BLI_mempool *pool) { - int ret = pool->totused; + int ret = (int)pool->totused; return ret; } @@ -850,3 +850,16 @@ void BLI_mempool_set_memory_debug(void) mempool_debug_memset = true; } #endif + +size_t BLI_mempool_get_size(BLI_mempool *pool) +{ + unsigned int chunk_count = 0; + BLI_mempool_chunk *chunk = pool->chunks; + + while (chunk) { + chunk = chunk->next; + chunk_count++; + } + + return (size_t)(chunk_count * pool->csize); +} diff --git a/source/blender/bmesh/intern/bmesh_log.cc b/source/blender/bmesh/intern/bmesh_log.cc index 315a187c6a9..e572f39afd1 100644 --- a/source/blender/bmesh/intern/bmesh_log.cc +++ b/source/blender/bmesh/intern/bmesh_log.cc @@ -120,7 +120,7 @@ template struct LogElemAlloc { int calc_size() { - return BLI_mempool_get_size(pool); + return int(BLI_mempool_get_size(pool)); } void operator=(LogElemAlloc &&b) @@ -781,10 +781,10 @@ struct BMLogEntry { { int ret = 0; - ret += vdata.pool ? (int)BLI_mempool_get_size(vdata.pool) : 0; - ret += edata.pool ? (int)BLI_mempool_get_size(edata.pool) : 0; - ret += ldata.pool ? (int)BLI_mempool_get_size(ldata.pool) : 0; - ret += pdata.pool ? (int)BLI_mempool_get_size(pdata.pool) : 0; + ret += vdata.pool ? int(BLI_mempool_get_size(vdata.pool)) : 0; + ret += edata.pool ? int(BLI_mempool_get_size(edata.pool)) : 0; + ret += ldata.pool ? int(BLI_mempool_get_size(ldata.pool)) : 0; + ret += pdata.pool ? int(BLI_mempool_get_size(pdata.pool)) : 0; ret += vpool.calc_size(); ret += epool.calc_size(); diff --git a/source/blender/bmesh/intern/bmesh_mesh.cc b/source/blender/bmesh/intern/bmesh_mesh.cc index 4de9db63491..2ee33730897 100644 --- a/source/blender/bmesh/intern/bmesh_mesh.cc +++ b/source/blender/bmesh/intern/bmesh_mesh.cc @@ -1638,381 +1638,6 @@ void BM_mesh_vert_coords_apply_with_mat4(BMesh *bm, } } -void bm_swap_ids(BMesh *bm, BMElem *e1, BMElem *e2) -{ - int cd_id = bm->idmap.cd_id_off[int(e1->head.htype)]; - - if (cd_id < 0) { - return; - } - - int id1 = BM_ELEM_CD_GET_INT(e1, cd_id); - int id2 = BM_ELEM_CD_GET_INT(e2, cd_id); - - if (bm->idmap.map) { - SWAP(BMElem *, bm->idmap.map[id1], bm->idmap.map[id2]); - } - else if (bm->idmap.ghash) { - void **val1, **val2; - - BLI_ghash_ensure_p(bm->idmap.ghash, POINTER_FROM_INT(id1), &val1); - BLI_ghash_ensure_p(bm->idmap.ghash, POINTER_FROM_INT(id2), &val2); - - *val1 = (void *)e2; - *val2 = (void *)e1; - } -} -static void bm_swap_elements_post(BMesh *bm, CustomData *cdata, BMElem *e1, BMElem *e2) -{ - // unswap customdata pointers - SWAP(void *, e1->head.data, e2->head.data); - - // swap contents of customdata instead - bm_swap_cd_data(e1->head.htype, bm, cdata, e1->head.data, e2->head.data); - - // unswap index - SWAP(int, e1->head.index, e2->head.index); - - bm_swap_ids(bm, e1, e2); -} - -void BM_swap_verts(BMesh *bm, BMVert *v1, BMVert *v2) -{ - if (v1 == v2) { - return; - } - - Vector ls1; - Vector ls2; - Vector es1; - Vector es2; - Vector sides1; - Vector sides2; - - for (int i = 0; i < 2; i++) { - BMVert *v = i ? v2 : v1; - - BMEdge *e = v->e, *starte = e; - - if (!e) { - continue; - } - - // int count = 0; - - do { - // if (count++ > 10000) { - // printf("error!\n"); - // break; - // } - - int side = 0; - if (e->v1 == v) { - side |= 1; - } - - if (e->v2 == v) { - side |= 2; - } - - if (i) { - es2.append(e); - sides2.append(side); - } - else { - es1.append(e); - sides1.append(side); - } - } while ((e = BM_DISK_EDGE_NEXT(e, v)) != starte); - } - - for (int i = 0; i < 2; i++) { - BMVert *v = i ? v2 : v1; - BMVert *v_2 = i ? v1 : v2; - - auto &es = i ? es2 : es1; - auto &sides = i ? sides2 : sides1; - - int elen = i = es.size(); - for (int j = 0; j < elen; j++) { - BMEdge *e = es[j]; - int side = sides[j]; - - // if (side == 3) { - // printf("edge had duplicate verts!\n"); - //} - - if (side & 1) { - e->v1 = v_2; - } - - if (side & 2) { - e->v2 = v_2; - } - -#if 1 - BMLoop *l = e->l; - if (l) { - - do { - BMLoop *l2 = l; - - do { - if (l2->v == v) { - if (i) { - ls2.append(l2); - } - else { - ls1.append(l2); - } - } - } while ((l2 = l2->next) != l); - } while ((l = l->radial_next) != e->l); - } -#endif - // e = enext; - } // while (e != starte); - } - - for (int i = 0; i < 2; i++) { - BMVert *v_2 = i ? v1 : v2; - auto &ls = i ? ls2 : ls1; - - int llen = ls.size(); - for (int j = 0; j < llen; j++) { - ls[j]->v = v_2; - } - } - - SWAP(BMVert, (*v1), (*v2)); - // swap contents of customdata, don't swap pointers - bm_swap_elements_post(bm, &bm->vdata, (BMElem *)v1, (BMElem *)v2); - - bm->elem_table_dirty |= BM_VERT; - bm->elem_index_dirty |= BM_VERT; -} - -void BM_swap_edges(BMesh *bm, BMEdge *e1, BMEdge *e2) -{ - for (int i = 0; i < 2; i++) { - BMEdge *e = i ? e2 : e1; - BMEdge *e_2 = i ? e1 : e2; - - for (int j = 0; j < 2; j++) { - BMVert *v = j ? e->v2 : e->v1; - - if (v->e == e) { - v->e = e_2; - } - } - - BMLoop *l = e->l; - if (l) { - do { - l->e = e_2; - } while ((l = l->radial_next) != e->l); - } - } - - SWAP(BMEdge, *e1, *e2); - // swap contents of customdata, don't swap pointers - bm_swap_elements_post(bm, &bm->edata, (BMElem *)e1, (BMElem *)e2); -} - -void BM_swap_loops(BMesh *bm, BMLoop *l1, BMLoop *l2) -{ - for (int i = 0; i < 2; i++) { - BMLoop *l = i ? l2 : l1; - - l->prev->next = l2; - l->next->prev = l2; - - if (l != l->radial_next) { - l->radial_next->radial_prev = l2; - l->radial_prev->radial_next = l2; - } - - if (l == l->e->l) { - l->e->l = l2; - } - - if (l == l->f->l_first) { - l->f->l_first = l2; - } - } - - // swap contents of customdata, don't swap pointers - SWAP(BMLoop, *l1, *l2); - // swap contents of customdata, don't swap pointers - bm_swap_elements_post(bm, &bm->ldata, (BMElem *)l1, (BMElem *)l2); -} - -// memory coherence defragmentation - -#ifndef ABSLL -# define ABSLL(a) ((a) < 0LL ? -(a) : (a)) -#endif - -#define DEFRAG_FLAG BM_ELEM_TAG_ALT - -bool BM_defragment_vertex(BMesh *bm, - BMVert *v, - RNG *rand, - void (*on_vert_swap)(BMVert *a, BMVert *b, void *userdata), - void *userdata) -{ - BMEdge *e = v->e; - -#if 1 - int cd_vcol = CustomData_get_offset(&bm->vdata, CD_PROP_COLOR); - - if (cd_vcol >= 0) { - float *color = (float *)BM_ELEM_CD_GET_VOID_P(v, cd_vcol); - int idx = BLI_mempool_find_real_index(bm->vpool, (void *)v); - int size = BLI_mempool_get_size(bm->vpool); - - float f = (float)idx / (float)size / 2.0f; - - color[0] = color[1] = color[2] = f; - color[3] = 1.0f; - } -#endif - - // return false; - - // return false; - - // BM_mesh_elem_table_ensure(bm, BM_VERT|BM_EDGE|BM_FACE); - if (!e) { - return false; - } - - bool bad = false; - int limit = 128; - - int vlimit = sizeof(BMVert *) * limit; - int elimit = sizeof(BMEdge *) * limit; - int llimit = sizeof(BMLoop *) * limit; - // int flimit = sizeof(BMFace *) * limit; - - intptr_t iv = (intptr_t)v; - - BMEdge *laste = nullptr; - do { - BMVert *v2 = BM_edge_other_vert(e, v); - intptr_t iv2 = (intptr_t)v2; - intptr_t ie = (intptr_t)e; - - v2->head.hflag &= DEFRAG_FLAG; - e->head.hflag &= ~DEFRAG_FLAG; - - if (ABSLL(iv2 - iv) > vlimit) { - bad = true; - break; - } - - if (laste) { - intptr_t ilaste = (intptr_t)laste; - if (ABSLL(ilaste - ie) > elimit) { - bad = true; - break; - } - } - - BMLoop *l = e->l; - if (l) { - do { - intptr_t il = (intptr_t)l; - intptr_t ilnext = (intptr_t)l->next; - - if (ABSLL(il - ilnext) > llimit) { - bad = true; - break; - } - - BMLoop *l2 = l->f->l_first; - do { - l2->head.hflag &= ~DEFRAG_FLAG; - } while ((l2 = l2->next) != l->f->l_first); - - l2->f->head.hflag &= ~DEFRAG_FLAG; - - l = l->radial_next; - } while (l != e->l); - } - laste = e; - } while (!bad && (e = BM_DISK_EDGE_NEXT(e, v)) != v->e); - - float prob = 1.0; - - if (!bad || BLI_rng_get_float(rand) > prob) { - return false; - } - - // find sort candidates - // BLI_mempool_find_elems_fuzzy - - int vidx = BLI_mempool_find_real_index(bm->vpool, (void *)v); - const int count = 5; - BMVert **elems = static_cast(BLI_array_alloca(elems, count)); - - do { - BMVert *v2 = BM_edge_other_vert(e, v); - int totelem = BLI_mempool_find_elems_fuzzy(bm->vpool, vidx, 4, (void **)elems, count); - - for (int i = 0; i < totelem; i++) { - if (elems[i] == v2 || elems[i] == v) { - continue; - } - - elems[i]->head.hflag &= ~DEFRAG_FLAG; - } - - bool ok = false; - - for (int i = 0; i < totelem; i++) { - if (elems[i] == v2 || elems[i] == v || (elems[i]->head.hflag & DEFRAG_FLAG)) { - continue; - } - - if (elems[i]->head.htype != BM_VERT) { - printf("ERROR!\n"); - } - // found one - v2->head.hflag |= DEFRAG_FLAG; - elems[i]->head.hflag |= DEFRAG_FLAG; - - on_vert_swap(v2, elems[i], userdata); - BM_swap_verts(bm, v2, elems[i]); - -#if 0 - BMIter iter; - BMEdge *et; - int f = 0; - BM_ITER_ELEM (et, &iter, v2, BM_EDGES_OF_VERT) { - printf("an edge %d\n", f++); - } - - f = 0; - BM_ITER_ELEM (et, &iter, v, BM_EDGES_OF_VERT) { - printf("an 1edge %d\n", f++); - } -#endif - - // BM_swap_verts(bm, v2, elems[i]); - - ok = true; - break; - } - - if (ok) { - break; - } - } while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e); - - return true; -} - static void on_vert_kill(BMesh * /*bm*/, BMVert * /*v*/, void * /*userdata*/) { } -- 2.30.2 From d9975b4bf39216b117b91ada67dd28cf1cf314c3 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Mon, 1 May 2023 14:11:23 -0700 Subject: [PATCH 071/279] temp-sculpt-dyntopo: Apply sculpt attribute api cleanup patch --- source/blender/blenkernel/BKE_paint.h | 106 ++++++++++++------ source/blender/editors/sculpt_paint/sculpt.cc | 12 +- .../editors/sculpt_paint/sculpt_api.cc | 15 +-- .../sculpt_paint/sculpt_automasking.cc | 30 ++--- .../sculpt_paint/sculpt_brush_types.cc | 5 +- .../editors/sculpt_paint/sculpt_face_set.cc | 24 ++-- .../editors/sculpt_paint/sculpt_intern.hh | 12 +- .../editors/sculpt_paint/sculpt_ops.cc | 18 +-- .../sculpt_paint/sculpt_paint_color.cc | 11 +- .../editors/sculpt_paint/sculpt_smooth.cc | 12 +- 10 files changed, 147 insertions(+), 98 deletions(-) diff --git a/source/blender/blenkernel/BKE_paint.h b/source/blender/blenkernel/BKE_paint.h index b37bbd251f8..d153d112383 100644 --- a/source/blender/blenkernel/BKE_paint.h +++ b/source/blender/blenkernel/BKE_paint.h @@ -717,8 +717,8 @@ typedef struct SculptAttributePointers { SculptAttribute *boundary_flags; SculptAttribute *sculpt_vert; SculptAttribute *stroke_id; - SculptAttribute *sculpt_flags; /* CD_PROP_INT8, vert */ - SculptAttribute *orig_fsets; /* CD_PROP_INT32, face */ + SculptAttribute *sculpt_flags; /* CD_PROP_INT8, vert */ + SculptAttribute *orig_fsets; /* CD_PROP_INT32, face */ SculptAttribute *smear_previous; SculptAttribute *hide_poly; @@ -796,8 +796,18 @@ struct SculptSession { /* The 0 ID is not used by the tools or the visibility system, it is just used when creating new * geometry (the trim tool, for example) to detect which geometry was just added, so it can be - * assigned a valid Face Set after creation. Tools are not intended to run with Face Sets IDs set - * to 0. */ +<<<<<<< +======= +/* Destroy attributes that were marked as stroke only in SculptAttributeParams. */ + void BKE_sculpt_attributes_destroy_temporary_stroke(struct Object *ob); + + /** + * Create new color layer on object if it doesn't have one and if experimental feature set has + * sculpt vertex color enabled. Returns truth if new layer has been added, false otherwise. + >>>>>>> + * assigned a valid Face Set after creation. Tools are not intended to run with Face Sets IDs + set + * to 0. */ int *face_sets; /** * A reference to the ".hide_poly" attribute, to store whether (base) polygons are hidden. @@ -1195,58 +1205,88 @@ struct CurveMapping *BKE_sculpt_default_cavity_curve(void); #ifdef __cplusplus } +namespace blender::bke::paint { -template -Tptr BKE_sculpt_vertex_attr_get(const PBVHVertRef vertex, const SculptAttribute *attr) +/* Base implementation for vertex_attr_*** and face_attr_*** methods. + * Returns a pointer to the attribute data (as defined by attr) for elem. + */ +template +static T *elem_attr_ptr(const ElemRef elem, const SculptAttribute *attr) { void *ptr = nullptr; if (attr->data) { char *p = (char *)attr->data; - int idx = (int)vertex.i; + int idx = (int)elem.i; if (attr->data_for_bmesh) { - BMElem *v = (BMElem *)vertex.i; - idx = v->head.index; + BMElem *e = (BMElem *)elem.i; + idx = e->head.index; } ptr = p + attr->elem_size * (int)idx; } else { - BMElem *v = (BMElem *)vertex.i; + BMElem *v = (BMElem *)elem.i; ptr = BM_ELEM_CD_GET_VOID_P(v, attr->bmesh_cd_offset); } - return static_cast(ptr); + return static_cast(ptr); } -template -static Tptr BKE_sculpt_face_attr_get(const PBVHFaceRef face, const SculptAttribute *attr) +/* + * Get a pointer to attribute data at vertex. + * + * Example: float *persistent_co = vertex_attr_ptr(vertex, ss->attrs.persistent_co); + */ +template +static T *vertex_attr_ptr(const PBVHVertRef vertex, const SculptAttribute *attr) { - void *ptr = nullptr; - - if (attr->data) { - char *p = (char *)attr->data; - int idx = (int)face.i; - - if (attr->data_for_bmesh) { - BMElem *v = (BMElem *)face.i; - idx = v->head.index; - } - - ptr = p + attr->elem_size * (int)idx; - } - else { - BMElem *v = (BMElem *)face.i; - ptr = BM_ELEM_CD_GET_VOID_P(v, attr->bmesh_cd_offset); - } - - return static_cast(ptr); + return elem_attr_ptr(vertex, attr); } +/* + * Get attribute data at vertex. + * + * Example: float weight = vertex_attr_get(vertex, ss->attrs.automasking_factor); + */ +template +static T vertex_attr_get(const PBVHVertRef vertex, const SculptAttribute *attr) +{ + return *vertex_attr_ptr(vertex, attr); +} + +/* + * Set attribute data at vertex. + * + * vertex_attr_set(vertex, ss->attrs.automasking_factor, 1.0f); + */ +template +static void vertex_attr_set(const PBVHVertRef vertex, const SculptAttribute *attr, T data) +{ + *vertex_attr_ptr(vertex, attr) = data; +} + +template static T *face_attr_ptr(const PBVHFaceRef face, const SculptAttribute *attr) +{ + return elem_attr_ptr(face, attr); +} + +template static T face_attr_get(const PBVHFaceRef face, const SculptAttribute *attr) +{ + return *face_attr_ptr(face, attr); +} + +template +static void face_attr_set(const PBVHFaceRef face, const SculptAttribute *attr, T data) +{ + *face_attr_ptr(face, attr) = data; +} +} // namespace blender::bke::paint + BLI_INLINE void BKE_sculpt_boundary_flag_update(SculptSession *ss, PBVHVertRef vertex) { - int *flags = BKE_sculpt_vertex_attr_get(vertex, ss->attrs.boundary_flags); + int *flags = blender::bke::paint::vertex_attr_ptr(vertex, ss->attrs.boundary_flags); *flags |= SCULPT_BOUNDARY_NEEDS_UPDATE; } diff --git a/source/blender/editors/sculpt_paint/sculpt.cc b/source/blender/editors/sculpt_paint/sculpt.cc index bcaca1881ee..7fc7be886d7 100644 --- a/source/blender/editors/sculpt_paint/sculpt.cc +++ b/source/blender/editors/sculpt_paint/sculpt.cc @@ -87,6 +87,8 @@ using blender::Set; using blender::Span; using blender::Vector; +using namespace blender::bke::paint; + static float sculpt_calc_radius(ViewContext *vc, const Brush *brush, const Scene *scene, @@ -344,7 +346,7 @@ bool SCULPT_has_persistent_base(SculptSession *ss) const float *SCULPT_vertex_persistent_co_get(SculptSession *ss, PBVHVertRef vertex) { if (ss->attrs.persistent_co) { - return SCULPT_vertex_attr_get(vertex, ss->attrs.persistent_co); + return vertex_attr_ptr(vertex, ss->attrs.persistent_co); } return SCULPT_vertex_co_get(ss, vertex); @@ -371,7 +373,7 @@ void SCULPT_vertex_limit_surface_get(SculptSession *ss, PBVHVertRef vertex, floa { if (BKE_pbvh_type(ss->pbvh) != PBVH_GRIDS) { if (ss->attrs.limit_surface) { - float *f = SCULPT_vertex_attr_get(vertex, ss->attrs.limit_surface); + float *f = vertex_attr_ptr(vertex, ss->attrs.limit_surface); copy_v3_v3(r_co, f); } else { @@ -397,7 +399,7 @@ void SCULPT_vertex_limit_surface_get(SculptSession *ss, PBVHVertRef vertex, floa void SCULPT_vertex_persistent_normal_get(SculptSession *ss, PBVHVertRef vertex, float no[3]) { if (ss->attrs.persistent_no) { - copy_v3_v3(no, SCULPT_vertex_attr_get(vertex, ss->attrs.persistent_no)); + copy_v3_v3(no, vertex_attr_ptr(vertex, ss->attrs.persistent_no)); return; } SCULPT_vertex_normal_get(ss, vertex, no); @@ -7146,7 +7148,7 @@ void SCULPT_face_set_set(SculptSession *ss, PBVHFaceRef face, int fset) int SCULPT_vertex_island_get(SculptSession *ss, PBVHVertRef vertex) { if (ss->attrs.topology_island_key) { - return *SCULPT_vertex_attr_get(vertex, ss->attrs.topology_island_key); + return vertex_attr_get(vertex, ss->attrs.topology_island_key); } return -1; @@ -7193,7 +7195,7 @@ void SCULPT_topology_islands_ensure(Object *ob) PBVHVertRef vertex2 = stack.pop_last(); SculptVertexNeighborIter ni; - *SCULPT_vertex_attr_get(vertex2, ss->attrs.topology_island_key) = island_nr; + vertex_attr_set(vertex2, ss->attrs.topology_island_key, island_nr); SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vertex2, ni) { if (visit.add(ni.vertex) && SCULPT_vertex_any_face_visible_get(ss, ni.vertex)) { diff --git a/source/blender/editors/sculpt_paint/sculpt_api.cc b/source/blender/editors/sculpt_paint/sculpt_api.cc index b3a405f67e9..b04740451f1 100644 --- a/source/blender/editors/sculpt_paint/sculpt_api.cc +++ b/source/blender/editors/sculpt_paint/sculpt_api.cc @@ -121,6 +121,8 @@ using blender::IndexRange; using blender::OffsetIndices; +using namespace blender::bke::paint; + /** * Checks if the face sets of the adjacent faces to the edge between \a v1 and \a v2 * in the base mesh are equal. @@ -310,7 +312,7 @@ PBVHVertRef SCULPT_edge_other_vertex(const SculptSession *ss, static void grids_update_boundary_flags(const SculptSession *ss, PBVHVertRef vertex) { - int *flag = SCULPT_vertex_attr_get(vertex, ss->attrs.boundary_flags); + int *flag = vertex_attr_ptr(vertex, ss->attrs.boundary_flags); *flag = 0; @@ -371,7 +373,7 @@ static void faces_update_boundary_flags(const SculptSession *ss, const PBVHVertR /* We have to handle boundary here seperately. */ - int *flag = SCULPT_vertex_attr_get(vertex, ss->attrs.boundary_flags); + int *flag = vertex_attr_ptr(vertex, ss->attrs.boundary_flags); *flag &= ~(SCULPT_CORNER_MESH | SCULPT_BOUNDARY_MESH); if (sculpt_check_boundary_vertex_in_base_mesh(ss, vertex.i)) { @@ -399,7 +401,7 @@ eSculptCorner SCULPT_vertex_is_corner(const SculptSession *ss, const PBVHVertRef vertex, eSculptCorner cornertype) { - eSculptCorner flag = *SCULPT_vertex_attr_get(vertex, ss->attrs.boundary_flags); + eSculptCorner flag = *vertex_attr_ptr(vertex, ss->attrs.boundary_flags); bool needs_update = flag & SCULPT_BOUNDARY_NEEDS_UPDATE; switch (BKE_pbvh_type(ss->pbvh)) { @@ -434,7 +436,7 @@ eSculptCorner SCULPT_vertex_is_corner(const SculptSession *ss, } if (needs_update) { - flag = *SCULPT_vertex_attr_get(vertex, ss->attrs.boundary_flags); + flag = *vertex_attr_ptr(vertex, ss->attrs.boundary_flags); } flag &= cornertype; @@ -447,8 +449,7 @@ eSculptBoundary SCULPT_vertex_is_boundary(const SculptSession *ss, const PBVHVertRef vertex, eSculptBoundary boundary_types) { - eSculptBoundary flag = *SCULPT_vertex_attr_get(vertex, - ss->attrs.boundary_flags); + eSculptBoundary flag = *vertex_attr_ptr(vertex, ss->attrs.boundary_flags); bool needs_update = flag & SCULPT_BOUNDARY_NEEDS_UPDATE; switch (BKE_pbvh_type(ss->pbvh)) { @@ -504,7 +505,7 @@ eSculptBoundary SCULPT_vertex_is_boundary(const SculptSession *ss, } if (needs_update) { - flag = eSculptBoundary(*SCULPT_vertex_attr_get(vertex, ss->attrs.boundary_flags)); + flag = eSculptBoundary(*vertex_attr_ptr(vertex, ss->attrs.boundary_flags)); } flag &= boundary_types; diff --git a/source/blender/editors/sculpt_paint/sculpt_automasking.cc b/source/blender/editors/sculpt_paint/sculpt_automasking.cc index 6f0e5e961e3..8c42b862e5e 100644 --- a/source/blender/editors/sculpt_paint/sculpt_automasking.cc +++ b/source/blender/editors/sculpt_paint/sculpt_automasking.cc @@ -56,6 +56,7 @@ using blender::float3; using blender::IndexRange; using blender::Set; using blender::Vector; +using namespace blender::bke::paint; AutomaskingCache *SCULPT_automasking_active_cache_get(SculptSession *ss) { @@ -261,10 +262,10 @@ static float automasking_view_occlusion_factor(AutomaskingCache *automasking, AutomaskingNodeData * /*automask_data*/, bool force = false) { - char f = *SCULPT_vertex_attr_get(vertex, ss->attrs.automasking_occlusion); + char f = vertex_attr_get(vertex, ss->attrs.automasking_occlusion); if (force || SCULPT_stroke_id_test(ss, vertex, STROKEID_USER_OCCLUSION)) { - f = *SCULPT_vertex_attr_get( + f = *vertex_attr_ptr( vertex, ss->attrs.automasking_occlusion) = SCULPT_vertex_is_occluded(ss, vertex, true) ? 2 : 1; } @@ -439,7 +440,7 @@ static void sculpt_calc_blurred_cavity(SculptSession *ss, factor_sum = sculpt_cavity_calc_factor(automasking, factor_sum); - *SCULPT_vertex_attr_get(vertex, ss->attrs.automasking_cavity) = factor_sum; + vertex_attr_set(vertex, ss->attrs.automasking_cavity, factor_sum); } int SCULPT_automasking_settings_hash(Object *ob, AutomaskingCache *automasking) @@ -497,7 +498,7 @@ static float sculpt_automasking_cavity_factor(AutomaskingCache *automasking, sculpt_calc_blurred_cavity(ss, automasking, automasking->settings.cavity_blur_steps, vertex); } - float factor = *SCULPT_vertex_attr_get(vertex, ss->attrs.automasking_cavity); + float factor = vertex_attr_get(vertex, ss->attrs.automasking_cavity); bool inverted = automasking->settings.flags & BRUSH_AUTOMASKING_CAVITY_INVERTED; if ((automasking->settings.flags & BRUSH_AUTOMASKING_CAVITY_ALL) && @@ -532,7 +533,7 @@ float SCULPT_automasking_factor_get(AutomaskingCache *automasking, * automasking information can't be computed in real time per vertex and needs to be * initialized for the whole mesh when the stroke starts. */ if (ss->attrs.automasking_factor) { - float factor = *SCULPT_vertex_attr_get(vert, ss->attrs.automasking_factor); + float factor = vertex_attr_get(vert, ss->attrs.automasking_factor); if (automasking->settings.flags & BRUSH_AUTOMASKING_CAVITY_ALL) { factor *= sculpt_automasking_cavity_factor(automasking, ss, vert); @@ -609,8 +610,9 @@ static bool automask_floodfill_cb( { AutomaskFloodFillData *data = (AutomaskFloodFillData *)userdata; - *SCULPT_vertex_attr_get(to_v, ss->attrs.automasking_factor) = 1.0f; - *SCULPT_vertex_attr_get(from_v, ss->attrs.automasking_factor) = 1.0f; + vertex_attr_set(to_v, ss->attrs.automasking_factor, 1.0f); + vertex_attr_set(from_v, ss->attrs.automasking_factor, 1.0f); + return (!data->use_radius || SCULPT_is_vertex_inside_brush_radius_symm( SCULPT_vertex_co_get(ss, to_v), data->location, data->radius, data->symm)); @@ -630,7 +632,7 @@ static void SCULPT_topology_automasking_init(Sculpt *sd, Object *ob) for (int i : IndexRange(totvert)) { PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); - (*SCULPT_vertex_attr_get(vertex, ss->attrs.automasking_factor)) = 0.0f; + vertex_attr_set(vertex, ss->attrs.automasking_factor, 0.0f); } /* Flood fill automask to connected vertices. Limited to vertices inside @@ -671,7 +673,7 @@ static void sculpt_face_sets_automasking_init(Sculpt *sd, Object *ob) PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); if (!SCULPT_vertex_has_face_set(ss, vertex, active_face_set)) { - *SCULPT_vertex_attr_get(vertex, ss->attrs.automasking_factor) = 0.0f; + vertex_attr_set(vertex, ss->attrs.automasking_factor, 0.0f); } } } @@ -736,8 +738,8 @@ static void SCULPT_boundary_automasking_init(Object *ob, const float p = 1.0f - (float(edge_distance[i]) / float(propagation_steps)); const float edge_boundary_automask = pow2f(p); - *SCULPT_vertex_attr_get( - vertex, ss->attrs.automasking_factor) *= (1.0f - edge_boundary_automask); + *vertex_attr_ptr(vertex, + ss->attrs.automasking_factor) *= (1.0f - edge_boundary_automask); } MEM_SAFE_FREE(edge_distance); @@ -782,7 +784,7 @@ static void sculpt_normal_occlusion_automasking_fill(AutomaskingCache *automaski for (int i = 0; i < totvert; i++) { PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); - float f = *SCULPT_vertex_attr_get(vertex, ss->attrs.automasking_factor); + float f = vertex_attr_get(vertex, ss->attrs.automasking_factor); if (int(mode) & BRUSH_AUTOMASKING_VIEW_NORMAL) { if (int(mode) & BRUSH_AUTOMASKING_VIEW_OCCLUSION) { @@ -796,7 +798,7 @@ static void sculpt_normal_occlusion_automasking_fill(AutomaskingCache *automaski SCULPT_stroke_id_test(ss, vertex, STROKEID_USER_AUTOMASKING); } - *SCULPT_vertex_attr_get(vertex, ss->attrs.automasking_factor) = f; + vertex_attr_set(vertex, ss->attrs.automasking_factor, f); } } @@ -915,7 +917,7 @@ AutomaskingCache *SCULPT_automasking_cache_init(Sculpt *sd, const Brush *brush, for (int i : IndexRange(totvert)) { PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); - (*SCULPT_vertex_attr_get(vertex, ss->attrs.automasking_factor)) = initial_value; + vertex_attr_set(vertex, ss->attrs.automasking_factor, initial_value); } const int boundary_propagation_steps = brush ? diff --git a/source/blender/editors/sculpt_paint/sculpt_brush_types.cc b/source/blender/editors/sculpt_paint/sculpt_brush_types.cc index 8e66b83b02c..a4e08a9ff98 100644 --- a/source/blender/editors/sculpt_paint/sculpt_brush_types.cc +++ b/source/blender/editors/sculpt_paint/sculpt_brush_types.cc @@ -40,6 +40,7 @@ #include #include +using namespace blender::bke::paint; using blender::Span; /* -------------------------------------------------------------------- */ @@ -1545,10 +1546,10 @@ static void do_layer_brush_task_cb_ex(void *__restrict userdata, const int vi = vd.index; float *disp_factor; if (use_persistent_base) { - disp_factor = SCULPT_vertex_attr_get(vd.vertex, ss->attrs.persistent_disp); + disp_factor = vertex_attr_ptr(vd.vertex, ss->attrs.persistent_disp); } else { - disp_factor = SCULPT_vertex_attr_get(vd.vertex, ss->attrs.layer_displayment); + disp_factor = vertex_attr_ptr(vd.vertex, ss->attrs.layer_displayment); if (SCULPT_stroke_id_test(ss, vd.vertex, STROKEID_USER_LAYER_BRUSH)) { *disp_factor = 0.0f; diff --git a/source/blender/editors/sculpt_paint/sculpt_face_set.cc b/source/blender/editors/sculpt_paint/sculpt_face_set.cc index 1a5e1150396..20bfa18d9bb 100644 --- a/source/blender/editors/sculpt_paint/sculpt_face_set.cc +++ b/source/blender/editors/sculpt_paint/sculpt_face_set.cc @@ -73,6 +73,8 @@ using blender::IndexRange; using blender::Span; using blender::Vector; +using namespace blender::bke::paint; + static int sculpt_face_material_get(SculptSession *ss, PBVHFaceRef face) { switch (BKE_pbvh_type(ss->pbvh)) { @@ -108,7 +110,7 @@ void SCULPT_face_check_origdata(SculptSession *ss, PBVHFaceRef face) return; } - short *s = BKE_sculpt_face_attr_get(face, ss->attrs.orig_fsets); + short *s = face_attr_ptr(face, ss->attrs.orig_fsets); // pack ss->stroke_id in higher 16 bits if (s[1] != ss->stroke_id) { @@ -123,7 +125,7 @@ int SCULPT_face_set_original_get(SculptSession *ss, PBVHFaceRef face) return SCULPT_face_set_get(ss, face); } - short *s = BKE_sculpt_face_attr_get(face, ss->attrs.orig_fsets); + short *s = face_attr_ptr(face, ss->attrs.orig_fsets); if (s[1] != ss->stroke_id) { s[0] = SCULPT_face_set_get(ss, face); @@ -422,9 +424,8 @@ void do_draw_face_sets_brush_task_cb_ex(void *__restrict userdata, const float3 &v = positions[vert_i]; float fno[3]; - *SCULPT_vertex_attr_get( - BKE_pbvh_make_vref(vert_i), - ss->attrs.boundary_flags) |= SCULPT_BOUNDARY_NEEDS_UPDATE; + *vertex_attr_ptr(BKE_pbvh_make_vref(vert_i), + ss->attrs.boundary_flags) |= SCULPT_BOUNDARY_NEEDS_UPDATE; copy_v3_v3(fno, ss->vert_normals[vert_i]); float mask = ss->vmask ? ss->vmask[vert_i] : 0.0f; @@ -532,9 +533,8 @@ void do_draw_face_sets_brush_task_cb_ex(void *__restrict userdata, break; } - *SCULPT_vertex_attr_get( - BKE_pbvh_make_vref((intptr_t)l->v), - ss->attrs.boundary_flags) |= SCULPT_BOUNDARY_NEEDS_UPDATE; + *vertex_attr_ptr(BKE_pbvh_make_vref((intptr_t)l->v), + ss->attrs.boundary_flags) |= SCULPT_BOUNDARY_NEEDS_UPDATE; } while ((l = l->next) != f->l_first); if (ok) { @@ -851,7 +851,7 @@ static int sculpt_face_set_create_exec(bContext *C, wmOperator *op) bool ok = true; if (ss->attrs.hide_poly) { - ok = *BKE_sculpt_face_attr_get(fref, ss->attrs.hide_poly); + ok = *face_attr_ptr(fref, ss->attrs.hide_poly); } ok = ok && SCULPT_face_select_get(ss, fref); @@ -1320,7 +1320,7 @@ void SCULPT_face_sets_visibility_all_set(SculptSession *ss, bool state) for (int i = 0; i < ss->totfaces; i++) { PBVHFaceRef face = BKE_pbvh_index_to_face(ss->pbvh, i); - *BKE_sculpt_face_attr_get(face, ss->attrs.hide_poly) = !state; + *face_attr_ptr(face, ss->attrs.hide_poly) = !state; } } @@ -1329,7 +1329,7 @@ void SCULPT_face_sets_visibility_invert(SculptSession *ss) for (int i = 0; i < ss->totfaces; i++) { PBVHFaceRef face = BKE_pbvh_index_to_face(ss->pbvh, i); - *BKE_sculpt_face_attr_get(face, ss->attrs.hide_poly) ^= true; + *face_attr_ptr(face, ss->attrs.hide_poly) ^= true; } } @@ -1397,7 +1397,7 @@ static int sculpt_face_sets_change_visibility_exec(bContext *C, wmOperator *op) for (int i = 0; i < ss->totfaces; i++) { PBVHFaceRef face = BKE_pbvh_index_to_face(ss->pbvh, i); - if (*BKE_sculpt_face_attr_get(face, ss->attrs.hide_poly)) { + if (*face_attr_ptr(face, ss->attrs.hide_poly)) { hidden_vertex = true; break; } diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.hh b/source/blender/editors/sculpt_paint/sculpt_intern.hh index 858c1425259..575c1a6921f 100644 --- a/source/blender/editors/sculpt_paint/sculpt_intern.hh +++ b/source/blender/editors/sculpt_paint/sculpt_intern.hh @@ -1907,6 +1907,9 @@ BLI_INLINE bool SCULPT_need_reproject(const SculptSession *ss) return !ss->ignore_uvs && ss->bm && CustomData_has_layer(&ss->bm->ldata, CD_PROP_FLOAT2); } +int SCULPT_vertex_island_get(SculptSession *ss, PBVHVertRef vertex); + +/** \} */ void SCULPT_reproject_cdata(SculptSession *ss, PBVHVertRef vertex, float origco[3], @@ -2443,11 +2446,6 @@ int SCULPT_vertex_island_get(SculptSession *ss, PBVHVertRef vertex); /** \} */ -/* Make SCULPT_ alias to a few blenkernel sculpt methods. */ - -#define SCULPT_vertex_attr_get BKE_sculpt_vertex_attr_get -#define SCULPT_face_attr_get BKE_sculpt_face_attr_get - /* * Stroke ID API. This API is used to detect if * an element has already been processed for some task @@ -2472,7 +2470,7 @@ ENUM_OPERATORS(StrokeIDUser, STROKEID_USER_LAYER_BRUSH); BLI_INLINE bool SCULPT_stroke_id_test(SculptSession *ss, PBVHVertRef vertex, StrokeIDUser user) { - StrokeID *id = SCULPT_vertex_attr_get(vertex, ss->attrs.stroke_id); + StrokeID *id = blender::bke::paint::vertex_attr_ptr(vertex, ss->attrs.stroke_id); bool ret; if (id->id != ss->stroke_id) { @@ -2493,7 +2491,7 @@ BLI_INLINE bool SCULPT_stroke_id_test_no_update(SculptSession *ss, PBVHVertRef vertex, StrokeIDUser user) { - StrokeID *id = SCULPT_vertex_attr_get(vertex, ss->attrs.stroke_id); + StrokeID *id = blender::bke::paint::vertex_attr_ptr(vertex, ss->attrs.stroke_id); if (id->id != ss->stroke_id) { return true; diff --git a/source/blender/editors/sculpt_paint/sculpt_ops.cc b/source/blender/editors/sculpt_paint/sculpt_ops.cc index e6677b038ab..fb849415eb2 100644 --- a/source/blender/editors/sculpt_paint/sculpt_ops.cc +++ b/source/blender/editors/sculpt_paint/sculpt_ops.cc @@ -12,6 +12,7 @@ #include "BLI_ghash.h" #include "BLI_gsqueue.h" #include "BLI_math.h" +#include "BLI_math_vector_types.hh" #include "BLI_rand.h" #include "BLI_task.h" #include "BLI_utildefines.h" @@ -89,6 +90,9 @@ #include #include +using namespace blender::bke::paint; +using blender::float3; + /* Reset the copy of the mesh that is being sculpted on (currently just for the layer brush). */ static int sculpt_set_persistent_base_exec(bContext *C, wmOperator * /*op*/) @@ -118,11 +122,9 @@ static int sculpt_set_persistent_base_exec(bContext *C, wmOperator * /*op*/) for (int i = 0; i < totvert; i++) { PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); - copy_v3_v3(SCULPT_vertex_attr_get(vertex, ss->attrs.persistent_co), - SCULPT_vertex_co_get(ss, vertex)); - SCULPT_vertex_normal_get( - ss, vertex, SCULPT_vertex_attr_get(vertex, ss->attrs.persistent_no)); - (*SCULPT_vertex_attr_get(vertex, ss->attrs.persistent_disp)) = 0.0f; + vertex_attr_set(vertex, ss->attrs.persistent_co, SCULPT_vertex_co_get(ss, vertex)); + SCULPT_vertex_normal_get(ss, vertex, vertex_attr_ptr(vertex, ss->attrs.persistent_no)); + vertex_attr_set(vertex, ss->attrs.persistent_disp, 0.0f); } return OPERATOR_FINISHED; @@ -1329,7 +1331,7 @@ static int sculpt_set_limit_surface_exec(bContext *C, wmOperator * /* op */) const bool weighted = false; for (int i = 0; i < totvert; i++) { PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); - float *f = SCULPT_vertex_attr_get(vertex, scl); + float *f = vertex_attr_ptr(vertex, scl); SCULPT_neighbor_coords_average(ss, f, vertex, 0.0, true, weighted); } @@ -1428,7 +1430,7 @@ static int sculpt_regularize_rake_exec(bContext *C, wmOperator *op) BMVert *v = verts[i]; PBVHVertRef vertex = {(intptr_t)v}; - int *boundflag = SCULPT_vertex_attr_get(vertex, ss->attrs.boundary_flags); + int *boundflag = vertex_attr_ptr(vertex, ss->attrs.boundary_flags); if (*boundflag & (SCULPT_CORNER_MESH | SCULPT_CORNER_FACE_SET | SCULPT_CORNER_SHARP | SCULPT_CORNER_SEAM)) { @@ -1583,7 +1585,7 @@ static int sculpt_regularize_rake_exec(bContext *C, wmOperator *op) MSculptVert *mv3 = BKE_PBVH_SCULPTVERT(ss->cd_sculpt_vert, v3); PBVHVertRef vertex3 = {(intptr_t)v3}; - int *boundflag3 = (int *)SCULPT_vertex_attr_get(vertex3, ss->attrs.boundary_flags); + int *boundflag3 = vertex_attr_ptr(vertex3, ss->attrs.boundary_flags); if (*boundflag3 & boundtest) { continue; } diff --git a/source/blender/editors/sculpt_paint/sculpt_paint_color.cc b/source/blender/editors/sculpt_paint/sculpt_paint_color.cc index 90fe0bfcba5..3ca48b20e36 100644 --- a/source/blender/editors/sculpt_paint/sculpt_paint_color.cc +++ b/source/blender/editors/sculpt_paint/sculpt_paint_color.cc @@ -35,6 +35,7 @@ #include using blender::Vector; +using namespace blender::bke::paint; static void do_color_smooth_task_cb_exec(void *__restrict userdata, const int n, @@ -148,8 +149,8 @@ static void do_paint_brush_task_cb_ex(void *__restrict userdata, // our temp layer. do this here before the brush check // to ensure any geomtry dyntopo might subdivide has // valid state. - float *color_buffer = SCULPT_vertex_attr_get(vd.vertex, - buffer_scl); // mv->origcolor; + float *color_buffer = vertex_attr_ptr(vd.vertex, + buffer_scl); // mv->origcolor; if (SCULPT_stroke_id_test(ss, vd.vertex, STROKEID_USER_PREV_COLOR)) { zero_v4(color_buffer); } @@ -474,7 +475,7 @@ static void do_smear_brush_task_cb_exec(void *__restrict userdata, float current_disp[3]; float current_disp_norm[3]; float interp_color[4]; - float *prev_color = SCULPT_vertex_attr_get(vd.vertex, data->scl); + float *prev_color = vertex_attr_ptr(vd.vertex, data->scl); copy_v4_v4(interp_color, prev_color); @@ -552,7 +553,7 @@ static void do_smear_brush_task_cb_exec(void *__restrict userdata, continue; } - const float *neighbor_color = SCULPT_vertex_attr_get(ni.vertex, data->scl); + const float *neighbor_color = vertex_attr_ptr(ni.vertex, data->scl); float color_interp = -dot_v3v3(current_disp_norm, vertex_disp_norm); /* Square directional weight to get a somewhat sharper result. */ @@ -589,7 +590,7 @@ static void do_smear_store_prev_colors_task_cb_exec(void *__restrict userdata, PBVHVertexIter vd; BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { - SCULPT_vertex_color_get(ss, vd.vertex, SCULPT_vertex_attr_get(vd.vertex, data->scl)); + SCULPT_vertex_color_get(ss, vd.vertex, vertex_attr_ptr(vd.vertex, data->scl)); } BKE_pbvh_vertex_iter_end; } diff --git a/source/blender/editors/sculpt_paint/sculpt_smooth.cc b/source/blender/editors/sculpt_paint/sculpt_smooth.cc index a42954f3543..b2a3d15c92e 100644 --- a/source/blender/editors/sculpt_paint/sculpt_smooth.cc +++ b/source/blender/editors/sculpt_paint/sculpt_smooth.cc @@ -31,6 +31,8 @@ using blender::float2; using blender::float3; using blender::Vector; +using namespace blender::bke::paint; + static void SCULPT_neighbor_coords_average_interior_ex(SculptSession *ss, float result[3], PBVHVertRef vertex, @@ -485,7 +487,7 @@ static void do_enhance_details_brush_task_cb_ex(void *__restrict userdata, &automask_data); float disp[3]; - float *detail_dir = SCULPT_vertex_attr_get(vd.vertex, ss->attrs.detail_directions); + float *detail_dir = vertex_attr_ptr(vd.vertex, ss->attrs.detail_directions); madd_v3_v3v3fl(disp, vd.co, detail_dir, fade); SCULPT_clip(sd, ss, vd.co, disp); @@ -525,7 +527,7 @@ static void SCULPT_enhance_details_brush(Sculpt *sd, Object *ob, Span(vertex, ss->attrs.detail_directions); + float *detail_dir = vertex_attr_ptr(vertex, ss->attrs.detail_directions); sub_v3_v3v3(detail_dir, avg, SCULPT_vertex_co_get(ss, vertex)); } @@ -727,7 +729,7 @@ void SCULPT_surface_smooth_laplacian_step(SculptSession *ss, mul_v3_v3fl(weigthed_o, origco, alpha); mul_v3_v3fl(weigthed_q, co, 1.0f - alpha); add_v3_v3v3(d, weigthed_o, weigthed_q); - float *laplacian_disp = SCULPT_vertex_attr_get(vertex, ss->attrs.laplacian_disp); + float *laplacian_disp = vertex_attr_ptr(vertex, ss->attrs.laplacian_disp); sub_v3_v3v3(laplacian_disp, laplacian_smooth_co, d); @@ -742,14 +744,14 @@ void SCULPT_surface_smooth_displace_step( int total = 0; SculptVertexNeighborIter ni; SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vertex, ni) { - float *laplacian_disp = SCULPT_vertex_attr_get(ni.vertex, ss->attrs.laplacian_disp); + float *laplacian_disp = vertex_attr_ptr(ni.vertex, ss->attrs.laplacian_disp); add_v3_v3(b_avg, laplacian_disp); total++; } SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); if (total > 0) { - float *laplacian_disp = SCULPT_vertex_attr_get(vertex, ss->attrs.laplacian_disp); + float *laplacian_disp = vertex_attr_ptr(vertex, ss->attrs.laplacian_disp); mul_v3_v3fl(b_current_vertex, b_avg, (1.0f - beta) / total); madd_v3_v3fl(b_current_vertex, laplacian_disp, beta); -- 2.30.2 From 82ebcc018ea9d68527c6c66198f40b2b9995edee Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Mon, 1 May 2023 16:46:52 -0700 Subject: [PATCH 072/279] temp-sculpt-dyntopo: Begin splitting MSculptVert into attributes Note: not all attributes exist all the time. New attributes: * flags : byte * origco : float3 * origno : float3 * origmask : float * origcolor : float4 * curvature_dir : float3; * valence : int; --- source/blender/blenkernel/BKE_paint.h | 42 +- source/blender/blenkernel/BKE_pbvh.h | 101 +-- source/blender/blenkernel/BKE_sculpt.hh | 88 +++ source/blender/blenkernel/CMakeLists.txt | 1 + source/blender/blenkernel/intern/dyntopo.cc | 167 ++--- .../blenkernel/intern/dyntopo_collapse.cc | 21 +- .../blenkernel/intern/dyntopo_intern.hh | 16 +- source/blender/blenkernel/intern/paint.cc | 239 ++++++- source/blender/blenkernel/intern/pbvh.cc | 92 +-- .../blender/blenkernel/intern/pbvh_bmesh.cc | 625 +++--------------- .../blender/blenkernel/intern/pbvh_intern.hh | 11 +- source/blender/editors/sculpt_paint/sculpt.cc | 27 +- .../editors/sculpt_paint/sculpt_api.cc | 30 +- .../sculpt_paint/sculpt_automasking.cc | 8 +- .../editors/sculpt_paint/sculpt_boundary.cc | 17 +- .../sculpt_paint/sculpt_brush_types.cc | 22 +- .../editors/sculpt_paint/sculpt_curvature.cc | 9 +- .../editors/sculpt_paint/sculpt_dyntopo.cc | 4 +- .../sculpt_paint/sculpt_filter_color.cc | 2 +- .../sculpt_paint/sculpt_filter_mask.cc | 4 +- .../sculpt_paint/sculpt_filter_mesh.cc | 4 +- .../editors/sculpt_paint/sculpt_intern.hh | 61 +- .../sculpt_paint/sculpt_paint_color.cc | 2 +- .../editors/sculpt_paint/sculpt_pose.cc | 2 +- .../editors/sculpt_paint/sculpt_smooth.cc | 2 +- .../editors/sculpt_paint/sculpt_transform.cc | 4 +- .../editors/sculpt_paint/sculpt_undo.cc | 2 +- 27 files changed, 632 insertions(+), 971 deletions(-) create mode 100644 source/blender/blenkernel/BKE_sculpt.hh diff --git a/source/blender/blenkernel/BKE_paint.h b/source/blender/blenkernel/BKE_paint.h index d153d112383..0f4b9b35198 100644 --- a/source/blender/blenkernel/BKE_paint.h +++ b/source/blender/blenkernel/BKE_paint.h @@ -717,8 +717,15 @@ typedef struct SculptAttributePointers { SculptAttribute *boundary_flags; SculptAttribute *sculpt_vert; SculptAttribute *stroke_id; - SculptAttribute *sculpt_flags; /* CD_PROP_INT8, vert */ - SculptAttribute *orig_fsets; /* CD_PROP_INT32, face */ + + SculptAttribute *valence; /* CD_PROP_INT32, vert */ + SculptAttribute *flags; /* CD_PROP_INT8, vert */ + SculptAttribute *orig_co, *orig_no; /* CD_PROP_FLOAT3, vert */ + SculptAttribute *orig_fsets; /* CD_PROP_INT32, face */ + SculptAttribute *orig_color; /* CD_PROP_FLOAT4, vert */ + SculptAttribute *orig_mask; /* CD_PROP_FLOAT vert */ + + SculptAttribute *curvature_dir; /* Curvature direction vectors, CD_PROP_FLOAT3 */ SculptAttribute *smear_previous; SculptAttribute *hide_poly; @@ -796,18 +803,9 @@ struct SculptSession { /* The 0 ID is not used by the tools or the visibility system, it is just used when creating new * geometry (the trim tool, for example) to detect which geometry was just added, so it can be -<<<<<<< -======= -/* Destroy attributes that were marked as stroke only in SculptAttributeParams. */ - void BKE_sculpt_attributes_destroy_temporary_stroke(struct Object *ob); - - /** - * Create new color layer on object if it doesn't have one and if experimental feature set has - * sculpt vertex color enabled. Returns truth if new layer has been added, false otherwise. - >>>>>>> - * assigned a valid Face Set after creation. Tools are not intended to run with Face Sets IDs - set - * to 0. */ + * assigned a valid Face Set after creation. Tools are not intended to run with Face Sets IDs set + * to 0. + */ int *face_sets; /** * A reference to the ".hide_poly" attribute, to store whether (base) polygons are hidden. @@ -1049,6 +1047,15 @@ int BKE_sculptsession_vertex_count(const struct SculptSession *ss); void BKE_sculpt_ensure_idmap(struct Object *ob); +void BKE_sculpt_ensure_origco(struct Object *ob); +void BKE_sculpt_ensure_origmask(struct Object *ob); +void BKE_sculpt_ensure_origcolor(struct Object *ob); +void BKE_sculpt_ensure_origfset(struct Object *ob); +void BKE_sculpt_ensure_curvature_dir(struct Object *ob); + + /* Ensures Sculpt_flags and sculpt_valence layers. */ +void BKE_sculpt_ensure_sculpt_layers(struct Object *ob); + /* Ensure an attribute layer exists. */ SculptAttribute *BKE_sculpt_attribute_ensure(struct Object *ob, eAttrDomain domain, @@ -1282,6 +1289,13 @@ static void face_attr_set(const PBVHFaceRef face, const SculptAttribute *attr, T { *face_attr_ptr(face, attr) = data; } + +bool get_original_vertex(SculptSession *ss, + PBVHVertRef vertex, + const float **r_co, + float **r_no, + float **r_color, + float **r_mask); } // namespace blender::bke::paint BLI_INLINE void BKE_sculpt_boundary_flag_update(SculptSession *ss, PBVHVertRef vertex) diff --git a/source/blender/blenkernel/BKE_pbvh.h b/source/blender/blenkernel/BKE_pbvh.h index 070757f2b53..454a04bdfb5 100644 --- a/source/blender/blenkernel/BKE_pbvh.h +++ b/source/blender/blenkernel/BKE_pbvh.h @@ -82,79 +82,6 @@ struct MeshElemMap; typedef struct PBVH PBVH; typedef struct PBVHNode PBVHNode; -//#define PROXY_ADVANCED - -// experimental performance test of "data-based programming" approach -#ifdef PROXY_ADVANCED -typedef struct ProxyKey { - int node; - int pindex; -} ProxyKey; - -# define MAX_PROXY_NEIGHBORS 12 - -typedef struct ProxyVertArray { - float **ownerco; - short **ownerno; - float (*co)[3]; - float (*fno)[3]; - short (*no)[3]; - float *mask, **ownermask; - PBVHVertRef *index; - float **ownercolor, (*color)[4]; - - ProxyKey (*neighbors)[MAX_PROXY_NEIGHBORS]; - - int size; - int datamask; - bool neighbors_dirty; - - GHash *indexmap; -} ProxyVertArray; - -typedef enum { - PV_OWNERCO = 1, - PV_OWNERNO = 2, - PV_CO = 4, - PV_NO = 8, - PV_MASK = 16, - PV_OWNERMASK = 32, - PV_INDEX = 64, - PV_OWNERCOLOR = 128, - PV_COLOR = 256, - PV_NEIGHBORS = 512 -} ProxyVertField; - -typedef struct ProxyVertUpdateRec { - float *co, *no, *mask, *color; - PBVHVertRef index, newindex; -} ProxyVertUpdateRec; - -# define PBVH_PROXY_DEFAULT CO | INDEX | MASK - -struct SculptSession; - -void BKE_pbvh_ensure_proxyarrays( - struct SculptSession *ss, PBVH *pbvh, PBVHNode **nodes, int totnode, int mask); -void BKE_pbvh_load_proxyarrays(PBVH *pbvh, PBVHNode **nodes, int totnode, int mask); - -void BKE_pbvh_ensure_proxyarray( - struct SculptSession *ss, - struct PBVH *pbvh, - struct PBVHNode *node, - int mask, - struct GHash - *vert_node_map, // vert_node_map maps vertex PBVHVertRefs to PBVHNode indices; optional - bool check_indexmap, - bool force_update); -void BKE_pbvh_gather_proxyarray(PBVH *pbvh, PBVHNode **nodes, int totnode); - -void BKE_pbvh_free_proxyarray(struct PBVH *pbvh, struct PBVHNode *node); -void BKE_pbvh_update_proxyvert(struct PBVH *pbvh, struct PBVHNode *node, ProxyVertUpdateRec *rec); -ProxyVertArray *BKE_pbvh_get_proxyarrays(struct PBVH *pbvh, struct PBVHNode *node); - -#endif - typedef enum { PBVH_FACES, PBVH_GRIDS, @@ -460,8 +387,11 @@ void BKE_pbvh_build_bmesh(PBVH *pbvh, const int cd_sculpt_vert, const int cd_face_areas, const int cd_boundary_flag, - bool fast_draw, - bool update_sculptverts); + const int cd_flag, + const int cd_valence, + const int cd_origco, + const int cd_origno, + bool fast_draw); void BKE_pbvh_fast_draw_set(PBVH *pbvh, bool state); void BKE_pbvh_set_idmap(PBVH *pbvh, struct BMIdMap *idmap); @@ -471,7 +401,12 @@ void BKE_pbvh_update_offsets(PBVH *pbvh, const int cd_face_node_offset, const int cd_sculpt_vert, const int cd_face_areas, - const int cd_boudnary_flags); + const int cd_boudnary_flags, + const int cd_flag, + const int cd_valence, + const int cd_origco, + const int cd_origno, + const int cd_curvature_dir); void BKE_pbvh_update_bmesh_offsets(PBVH *pbvh, int cd_vert_node_offset, int cd_face_node_offset); @@ -497,7 +432,7 @@ checks if original data needs to be updated for v, and if so updates it. Stroke is provided by the sculpt code and is used to detect updates. The reason we do it inside the verts and not in the nodes is to allow splitting of the pbvh during the stroke. */ -bool BKE_pbvh_bmesh_check_origdata(PBVH *pbvh, struct BMVert *v, int stroke_id); +bool BKE_pbvh_bmesh_check_origdata(struct SculptSession *ss, struct BMVert *v, int stroke_id); /** used so pbvh can differentiate between different strokes, see BKE_pbvh_bmesh_check_origdata */ @@ -527,7 +462,8 @@ void BKE_pbvh_raycast(PBVH *pbvh, bool original, int stroke_id); -bool BKE_pbvh_node_raycast(PBVH *pbvh, +bool BKE_pbvh_node_raycast(SculptSession *ss, + PBVH *pbvh, PBVHNode *node, float (*origco)[3], bool use_origco, @@ -563,7 +499,8 @@ void BKE_pbvh_find_nearest_to_ray(PBVH *pbvh, const float ray_normal[3], bool original); -bool BKE_pbvh_node_find_nearest_to_ray(PBVH *pbvh, +bool BKE_pbvh_node_find_nearest_to_ray(SculptSession *ss, + PBVH *pbvh, PBVHNode *node, float (*origco)[3], bool use_origco, @@ -688,7 +625,7 @@ void BKE_pbvh_face_areas_begin(PBVH *pbvh); // updates boundaries and valences for whole mesh void BKE_pbvh_bmesh_on_mesh_change(PBVH *pbvh); bool BKE_pbvh_bmesh_check_valence(PBVH *pbvh, PBVHVertRef vertex); -void BKE_pbvh_bmesh_update_valence(int cd_sculpt_vert, PBVHVertRef vertex); +void BKE_pbvh_bmesh_update_valence(PBVH *pbvh, PBVHVertRef vertex); void BKE_pbvh_bmesh_update_all_valence(PBVH *pbvh); void BKE_pbvh_bmesh_flag_all_disk_sort(PBVH *pbvh); bool BKE_pbvh_bmesh_mark_update_valence(PBVH *pbvh, PBVHVertRef vertex); @@ -1146,6 +1083,8 @@ void BKE_pbvh_update_vert_boundary(int cd_sculpt_vert, int cd_face_node_offset, int cd_vcol, int cd_boundary_flag, + const int cd_flag, + const int cd_valence, struct BMVert *v, int bound_symmetry, const CustomData *ldata, @@ -1385,7 +1324,7 @@ bool BKE_pbvh_show_orig_get(PBVH *pbvh); #ifdef __cplusplus } -#include "BLI_math_vector.hh" +# include "BLI_math_vector.hh" namespace blender::bke::pbvh { void update_vert_boundary_faces(int *boundary_flags, diff --git a/source/blender/blenkernel/BKE_sculpt.hh b/source/blender/blenkernel/BKE_sculpt.hh new file mode 100644 index 00000000000..4a3b8fbc985 --- /dev/null +++ b/source/blender/blenkernel/BKE_sculpt.hh @@ -0,0 +1,88 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2009 by Nicholas Bishop. All rights reserved. */ + +#pragma once + +/** \file + * \ingroup bke + */ + +#include "BKE_attribute.h" +#include "BKE_paint.h" +#include "BKE_pbvh.h" +#include "BLI_compiler_compat.h" + +/* + * Stroke ID API. This API is used to detect if + * an element has already been processed for some task + * inside a given stroke. + */ + +struct StrokeID { + short id; + short userflag; +}; + +enum StrokeIDUser { + STROKEID_USER_AUTOMASKING = 1 << 0, + STROKEID_USER_BOUNDARY = 1 << 1, + STROKEID_USER_SCULPTVERT = 1 << 2, + STROKEID_USER_PREV_COLOR = 1 << 3, + STROKEID_USER_SMOOTH = 1 << 4, + STROKEID_USER_OCCLUSION = 1 << 5, + STROKEID_USER_LAYER_BRUSH = 1 << 6, + STROKEID_USER_ORIGINAL = 1 << 7, +}; +ENUM_OPERATORS(StrokeIDUser, STROKEID_USER_LAYER_BRUSH); + +namespace blender::bke::sculpt { +BLI_INLINE bool stroke_id_clear(SculptSession *ss, PBVHVertRef vertex, StrokeIDUser user) +{ + StrokeID *id = blender::bke::paint::vertex_attr_ptr(vertex, ss->attrs.stroke_id); + + id->userflag &= ~user; +} + +BLI_INLINE bool stroke_id_test(SculptSession *ss, PBVHVertRef vertex, StrokeIDUser user) +{ + StrokeID *id = blender::bke::paint::vertex_attr_ptr(vertex, ss->attrs.stroke_id); + bool ret; + + if (id->id != ss->stroke_id) { + id->id = ss->stroke_id; + id->userflag = 0; + ret = true; + } + else { + ret = !(id->userflag & (int)user); + } + + id->userflag |= (int)user; + + return ret; +} + +BLI_INLINE bool stroke_id_test_no_update(SculptSession *ss, PBVHVertRef vertex, StrokeIDUser user) +{ + StrokeID *id = blender::bke::paint::vertex_attr_ptr(vertex, ss->attrs.stroke_id); + + if (id->id != ss->stroke_id) { + return true; + } + + return !(id->userflag & (int)user); +} + +BLI_INLINE void add_sculpt_flag(SculptSession *ss, PBVHVertRef vertex, uint8_t flag) { + *blender::bke::paint::vertex_attr_ptr(vertex, ss->attrs.flags) |= flag; +} +BLI_INLINE void clear_sculpt_flag(SculptSession *ss, PBVHVertRef vertex, uint8_t flag) +{ + *blender::bke::paint::vertex_attr_ptr(vertex, ss->attrs.flags) &= ~flag; +} +BLI_INLINE bool test_sculpt_flag(SculptSession *ss, PBVHVertRef vertex, uint8_t flag) +{ + return blender::bke::paint::vertex_attr_get(vertex, ss->attrs.flags) & flag; +} + +} // namespace blender::bke::sculpt diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt index ce4d37a4b4f..265c512563f 100644 --- a/source/blender/blenkernel/CMakeLists.txt +++ b/source/blender/blenkernel/CMakeLists.txt @@ -464,6 +464,7 @@ set(SRC BKE_rigidbody.h BKE_scene.h BKE_screen.h + BKE_sculpt.hh BKE_sequencer_offscreen.h BKE_shader_fx.h BKE_shrinkwrap.h diff --git a/source/blender/blenkernel/intern/dyntopo.cc b/source/blender/blenkernel/intern/dyntopo.cc index 5dc1e206275..d797b34b3f3 100644 --- a/source/blender/blenkernel/intern/dyntopo.cc +++ b/source/blender/blenkernel/intern/dyntopo.cc @@ -37,6 +37,7 @@ #include "BKE_dyntopo.h" #include "BKE_paint.h" #include "BKE_pbvh.h" +#include "BKE_sculpt.hh" #include "bmesh.h" #include "bmesh_log.h" @@ -55,6 +56,7 @@ using blender::IndexRange; using blender::Map; using blender::Set; using blender::Vector; +using namespace blender::bke::sculpt; static void pbvh_split_edges(struct EdgeQueueContext *eq_ctx, PBVH *pbvh, @@ -80,7 +82,7 @@ static void edge_queue_create_local(struct EdgeQueueContext *eq_ctx, const bool use_projected, PBVHTopologyUpdateMode local_mode); -BLI_INLINE void surface_smooth_v_safe(PBVH *pbvh, BMVert *v, float fac) +BLI_INLINE void surface_smooth_v_safe(SculptSession *ss, PBVH *pbvh, BMVert *v, float fac) { float co[3]; float origco[3], origco1[3]; @@ -88,15 +90,14 @@ BLI_INLINE void surface_smooth_v_safe(PBVH *pbvh, BMVert *v, float fac) float tan[3]; float tot = 0.0; - MSculptVert *mv1 = BKE_PBVH_SCULPTVERT(pbvh->cd_sculpt_vert, v); - - if (mv1->stroke_id != pbvh->stroke_id) { + PBVHVertRef vertex = {reinterpret_cast(v)}; + if (stroke_id_test_no_update(ss, vertex, STROKEID_USER_ORIGINAL)) { copy_v3_v3(origco1, v->co); copy_v3_v3(origno1, v->no); } else { - copy_v3_v3(origco1, mv1->origco); - copy_v3_v3(origno1, dot_v3v3(mv1->origno, mv1->origno) == 0.0f ? v->no : mv1->origno); + copy_v3_v3(origco1, blender::bke::paint::vertex_attr_ptr(vertex, ss->attrs.orig_co)); + copy_v3_v3(origno1, blender::bke::paint::vertex_attr_ptr(vertex, ss->attrs.orig_no)); } zero_v3(co); @@ -128,12 +129,12 @@ BLI_INLINE void surface_smooth_v_safe(PBVH *pbvh, BMVert *v, float fac) do { BMVert *v2 = e->v1 == v ? e->v2 : e->v1; + PBVHVertRef vertex2 = { reinterpret_cast(v2)}; /* Note: we can't validate the boundary flags from with a thread * so they may not be up to date. */ - MSculptVert *mv2 = BKE_PBVH_SCULPTVERT(cd_sculpt_vert, v2); int boundflag2 = BM_ELEM_CD_GET_INT(v2, pbvh->cd_boundary_flag); const bool bound2 = boundflag2 & SCULPTVERT_SMOOTH_BOUNDARY; @@ -148,8 +149,9 @@ BLI_INLINE void surface_smooth_v_safe(PBVH *pbvh, BMVert *v, float fac) madd_v3_v3fl(tan, v->no, -d * 0.99f); add_v3_v3(co, tan); - if (mv2->stroke_id == pbvh->stroke_id) { - sub_v3_v3v3(tan, mv2->origco, origco1); + if (!stroke_id_test_no_update(ss, vertex2, STROKEID_USER_ORIGINAL)) { + sub_v3_v3v3( + tan, blender::bke::paint::vertex_attr_ptr(vertex2, ss->attrs.orig_co), origco1); } else { sub_v3_v3v3(tan, v2->co, origco1); @@ -178,20 +180,22 @@ BLI_INLINE void surface_smooth_v_safe(PBVH *pbvh, BMVert *v, float fac) atomic_cas_float(&v->co[1], y, ny); atomic_cas_float(&v->co[2], z, nz); + float *start_origco = blender::bke::paint::vertex_attr_ptr(vertex, ss->attrs.orig_co); + /* Conflicts here should be pretty rare. */ - x = mv1->origco[0]; - y = mv1->origco[1]; - z = mv1->origco[2]; + x = start_origco[0]; + y = start_origco[1]; + z = start_origco[2]; nx = x + origco[0] * fac; ny = y + origco[1] * fac; nz = z + origco[2] * fac; - atomic_cas_float(&mv1->origco[0], x, nx); - atomic_cas_float(&mv1->origco[1], y, ny); - atomic_cas_float(&mv1->origco[2], z, nz); + atomic_cas_float(&start_origco[0], x, nx); + atomic_cas_float(&start_origco[1], y, ny); + atomic_cas_float(&start_origco[2], z, nz); - PBVH_CHECK_NAN(mv1->origco); + PBVH_CHECK_NAN(start_origco[0]); PBVH_CHECK_NAN(v->co); // atomic_cas_int32(&mv1->stroke_id, stroke_id, pbvh->stroke_id); } @@ -712,7 +716,7 @@ static void unified_edge_queue_task_cb(void *__restrict userdata, do { /* are we owned by this node? if so, make sure origdata is up to date */ if (BM_ELEM_CD_GET_INT(l_iter->v, pbvh->cd_vert_node_offset) == ni) { - BKE_pbvh_bmesh_check_origdata(pbvh, l_iter->v, pbvh->stroke_id); + BKE_pbvh_bmesh_check_origdata(eq_ctx->ss, l_iter->v, pbvh->stroke_id); } /* try to improve convergence by applying a small amount of smoothing to topology, @@ -722,7 +726,8 @@ static void unified_edge_queue_task_cb(void *__restrict userdata, if (do_smooth && randval > 127) { PBVHVertRef sv = {(intptr_t)l_iter->v}; - surface_smooth_v_safe(tdata->pbvh, + surface_smooth_v_safe(eq_ctx->ss, + tdata->pbvh, l_iter->v, eq_ctx->surface_smooth_fac * eq_ctx->mask_cb(sv, eq_ctx->mask_cb_data)); @@ -842,8 +847,7 @@ bool check_face_is_tri(PBVH *pbvh, BMFace *f) BMLoop *l = f2->l_first; do { - MSculptVert *mv = BM_ELEM_CD_PTR(l->v, pbvh->cd_sculpt_vert); - mv->flag |= SCULPTVERT_NEED_DISK_SORT | SCULPTVERT_NEED_VALENCE; + dyntopo_add_flag(pbvh, l->v, SCULPTVERT_NEED_DISK_SORT | SCULPTVERT_NEED_VALENCE); } while ((l = l->next) != f2->l_first); } @@ -1063,9 +1067,7 @@ bool destroy_nonmanifold_fins(PBVH *pbvh, BMEdge *e_root) } else { pbvh_boundary_update_bmesh(pbvh, v); - - MSculptVert *mv = BKE_PBVH_SCULPTVERT(pbvh->cd_sculpt_vert, v); - mv->flag |= mupdateflag; + dyntopo_add_flag(pbvh, v, mupdateflag); } } @@ -1110,9 +1112,9 @@ bool check_for_fins(PBVH *pbvh, BMVert *v) bool check_vert_fan_are_tris(PBVH *pbvh, BMVert *v) { - MSculptVert *mv = BKE_PBVH_SCULPTVERT(pbvh->cd_sculpt_vert, v); + uint8_t *flag = BM_ELEM_CD_PTR(v, pbvh->cd_flag); - if (!(mv->flag & SCULPTVERT_NEED_TRIANGULATE)) { + if (!(*flag & SCULPTVERT_NEED_TRIANGULATE)) { return true; } @@ -1133,10 +1135,8 @@ bool check_vert_fan_are_tris(PBVH *pbvh, BMVert *v) BMLoop *l = f->l_first; do { - MSculptVert *mv_l = BKE_PBVH_SCULPTVERT(pbvh->cd_sculpt_vert, l->v); pbvh_boundary_update_bmesh(pbvh, l->v); - - MV_ADD_FLAG(mv_l, SCULPTVERT_NEED_VALENCE | SCULPTVERT_NEED_DISK_SORT); + dyntopo_add_flag(pbvh, l->v, SCULPTVERT_NEED_VALENCE | SCULPTVERT_NEED_DISK_SORT); } while ((l = l->next) != f->l_first); fs.append(f); @@ -1145,7 +1145,7 @@ bool check_vert_fan_are_tris(PBVH *pbvh, BMVert *v) } } - mv->flag &= ~SCULPTVERT_NEED_TRIANGULATE; + *flag &= ~SCULPTVERT_NEED_TRIANGULATE; for (int i = 0; i < fs.size(); i++) { /* Triangulation can sometimes delete a face. */ @@ -1326,15 +1326,12 @@ static void unified_edge_queue_create(EdgeQueueContext *eq_ctx, } } - MSculptVert *mv1 = BKE_PBVH_SCULPTVERT(cd_sculpt_vert, e->v1); - MSculptVert *mv2 = BKE_PBVH_SCULPTVERT(cd_sculpt_vert, e->v2); - - if (mv1->flag & SCULPTVERT_NEED_VALENCE) { - BKE_pbvh_bmesh_update_valence(pbvh->cd_sculpt_vert, {(intptr_t)e->v1}); + if (dyntopo_test_flag(pbvh, e->v1, SCULPTVERT_NEED_VALENCE)) { + BKE_pbvh_bmesh_update_valence(pbvh, {(intptr_t)e->v1}); } - if (mv2->flag & SCULPTVERT_NEED_VALENCE) { - BKE_pbvh_bmesh_update_valence(pbvh->cd_sculpt_vert, {(intptr_t)e->v2}); + if (dyntopo_test_flag(pbvh, e->v2, SCULPTVERT_NEED_VALENCE)) { + BKE_pbvh_bmesh_update_valence(pbvh, {(intptr_t)e->v2}); } if (eq_ctx->use_view_normal && (dot_v3v3(e->v1->no, eq_ctx->view_normal) < 0.0f && @@ -1494,10 +1491,9 @@ static void edge_queue_create_local(EdgeQueueContext *eq_ctx, if (!(local_mode & PBVH_LocalCollapse)) { if (!(v->head.hflag & BM_ELEM_TAG)) { v->head.hflag |= BM_ELEM_TAG; - MSculptVert *mv = BKE_PBVH_SCULPTVERT(pbvh->cd_sculpt_vert, v); - if (mv->flag & SCULPTVERT_NEED_VALENCE) { - BKE_pbvh_bmesh_update_valence(pbvh->cd_sculpt_vert, {(intptr_t)v}); + if (dyntopo_test_flag(pbvh, v, SCULPTVERT_NEED_VALENCE)) { + BKE_pbvh_bmesh_update_valence(pbvh, {(intptr_t)v}); } edge_queue_insert_val34_vert(eq_ctx, v); @@ -1685,11 +1681,9 @@ static bool cleanup_valence_3_4(EdgeQueueContext *ectx, continue; } - MSculptVert *mv = BKE_PBVH_SCULPTVERT(pbvh->cd_sculpt_vert, v); - BKE_pbvh_bmesh_check_valence(pbvh, {(intptr_t)v}); - int val = mv->valence; + int val = BM_ELEM_CD_GET_INT(v, pbvh->cd_valence); if (val != 4 && val != 3) { continue; } @@ -1742,19 +1736,15 @@ static bool cleanup_valence_3_4(EdgeQueueContext *ectx, for (int j = 0; j < val; j++) { ls[ls_i++] = l->v == v ? l->next : l; - MSculptVert *mv_l; - if (l->v == v) { - mv_l = BKE_PBVH_SCULPTVERT(pbvh->cd_sculpt_vert, l->next->v); + dyntopo_add_flag(pbvh, l->next->v, updateflag); pbvh_boundary_update_bmesh(pbvh, l->next->v); } else { - mv_l = BKE_PBVH_SCULPTVERT(pbvh->cd_sculpt_vert, l->v); + dyntopo_add_flag(pbvh, l->v, updateflag); pbvh_boundary_update_bmesh(pbvh, l->v); } - MV_ADD_FLAG(mv_l, updateflag); - l = l->prev->radial_next; if (l->v != v) { @@ -1879,17 +1869,13 @@ static bool cleanup_valence_3_4(EdgeQueueContext *ectx, validate_vert(pbvh, pbvh->header.bm, v, false, false); - MSculptVert *mv1 = BKE_PBVH_SCULPTVERT(pbvh->cd_sculpt_vert, vs[0]); - MSculptVert *mv2 = BKE_PBVH_SCULPTVERT(pbvh->cd_sculpt_vert, vs[1]); - MSculptVert *mv3 = BKE_PBVH_SCULPTVERT(pbvh->cd_sculpt_vert, vs[2]); - pbvh_boundary_update_bmesh(pbvh, vs[0]); pbvh_boundary_update_bmesh(pbvh, vs[1]); pbvh_boundary_update_bmesh(pbvh, vs[2]); - MV_ADD_FLAG(mv1, updateflag); - MV_ADD_FLAG(mv2, updateflag); - MV_ADD_FLAG(mv3, updateflag); + dyntopo_add_flag(pbvh, vs[0], updateflag); + dyntopo_add_flag(pbvh, vs[1], updateflag); + dyntopo_add_flag(pbvh, vs[2], updateflag); BMFace *f1 = nullptr; bool ok1 = vs[0] != vs[1] && vs[1] != vs[2] && vs[0] != vs[2]; @@ -1915,17 +1901,13 @@ static bool cleanup_valence_3_4(EdgeQueueContext *ectx, ok2 = ok2 && !BM_face_exists(vs, 3); if (ok2) { - MSculptVert *mv1 = BKE_PBVH_SCULPTVERT(pbvh->cd_sculpt_vert, vs[0]); - MSculptVert *mv2 = BKE_PBVH_SCULPTVERT(pbvh->cd_sculpt_vert, vs[1]); - MSculptVert *mv3 = BKE_PBVH_SCULPTVERT(pbvh->cd_sculpt_vert, vs[2]); - pbvh_boundary_update_bmesh(pbvh, vs[0]); pbvh_boundary_update_bmesh(pbvh, vs[1]); pbvh_boundary_update_bmesh(pbvh, vs[2]); - MV_ADD_FLAG(mv1, updateflag); - MV_ADD_FLAG(mv2, updateflag); - MV_ADD_FLAG(mv3, updateflag); + dyntopo_add_flag(pbvh, vs[0], updateflag); + dyntopo_add_flag(pbvh, vs[1], updateflag); + dyntopo_add_flag(pbvh, vs[2], updateflag); BMFace *example = nullptr; if (v->e && v->e->l) { @@ -2027,13 +2009,11 @@ static bool do_cleanup_3_4(EdgeQueueContext *eq_ctx, continue; } - MSculptVert *mv = BKE_PBVH_SCULPTVERT(pbvh->cd_sculpt_vert, v); - - if (mv->flag & SCULPTVERT_NEED_VALENCE) { - BKE_pbvh_bmesh_update_valence(pbvh->cd_sculpt_vert, {(intptr_t)v}); + if (dyntopo_test_flag(pbvh, v, SCULPTVERT_NEED_VALENCE)) { + BKE_pbvh_bmesh_update_valence(pbvh, {(intptr_t)v}); } - if (mv->valence < 5) { + if (BM_ELEM_CD_GET_INT(v, pbvh->cd_valence) < 5) { edge_queue_insert_val34_vert(eq_ctx, v); } } @@ -2675,9 +2655,10 @@ static void pbvh_split_edges(EdgeQueueContext *eq_ctx, l2->v->head.hflag &= ~SPLIT_TAG; pbvh_boundary_update_bmesh(pbvh, l2->v); - MSculptVert *mv = BKE_PBVH_SCULPTVERT(pbvh->cd_sculpt_vert, l2->v); - MV_ADD_FLAG( - mv, SCULPTVERT_NEED_VALENCE | SCULPTVERT_NEED_DISK_SORT | SCULPTVERT_NEED_TRIANGULATE); + dyntopo_add_flag(pbvh, + l2->v, + SCULPTVERT_NEED_VALENCE | SCULPTVERT_NEED_DISK_SORT | + SCULPTVERT_NEED_TRIANGULATE); } while ((l2 = l2->next) != l->f->l_first); l->f->head.hflag &= ~SPLIT_TAG; @@ -2788,19 +2769,8 @@ static void pbvh_split_edges(EdgeQueueContext *eq_ctx, e->head.hflag &= ~SPLIT_TAG; - MSculptVert *mv1 = BKE_PBVH_SCULPTVERT(pbvh->cd_sculpt_vert, e->v1); - MSculptVert *mv2 = BKE_PBVH_SCULPTVERT(pbvh->cd_sculpt_vert, e->v2); - - if (mv1->stroke_id != pbvh->stroke_id) { - BKE_pbvh_bmesh_check_origdata(pbvh, e->v1, pbvh->stroke_id); - } - if (mv2->stroke_id != pbvh->stroke_id) { - BKE_pbvh_bmesh_check_origdata(pbvh, e->v2, pbvh->stroke_id); - } - - if (mv1->stroke_id != mv2->stroke_id) { - printf("stroke_id error\n"); - } + BKE_pbvh_bmesh_check_origdata(eq_ctx->ss, e->v1, pbvh->stroke_id); + BKE_pbvh_bmesh_check_origdata(eq_ctx->ss, e->v2, pbvh->stroke_id); validate_edge(pbvh, pbvh->header.bm, e, true, true); @@ -2849,19 +2819,20 @@ static void pbvh_split_edges(EdgeQueueContext *eq_ctx, validate_edge(pbvh, pbvh->header.bm, newe, true, true); validate_vert(pbvh, pbvh->header.bm, newv, true, true); - MSculptVert *mv = BKE_PBVH_SCULPTVERT(pbvh->cd_sculpt_vert, newv); - newv->head.hflag |= SPLIT_TAG; pbvh_boundary_update_bmesh(pbvh, newv); - MV_ADD_FLAG(mv, - SCULPTVERT_NEED_VALENCE | SCULPTVERT_NEED_DISK_SORT | SCULPTVERT_NEED_TRIANGULATE); - mv->stroke_id = pbvh->stroke_id; + dyntopo_add_flag(pbvh, + newv, + SCULPTVERT_NEED_VALENCE | SCULPTVERT_NEED_DISK_SORT | + SCULPTVERT_NEED_TRIANGULATE); - mv = BKE_PBVH_SCULPTVERT(pbvh->cd_sculpt_vert, e->v1 != newv ? e->v1 : e->v2); + BMVert *otherv = e->v1 != newv ? e->v1 : e->v2; pbvh_boundary_update_bmesh(pbvh, e->v1 != newv ? e->v1 : e->v2); - MV_ADD_FLAG(mv, - SCULPTVERT_NEED_DISK_SORT | SCULPTVERT_NEED_VALENCE | SCULPTVERT_NEED_TRIANGULATE); + dyntopo_add_flag(pbvh, + otherv, + SCULPTVERT_NEED_DISK_SORT | SCULPTVERT_NEED_VALENCE | + SCULPTVERT_NEED_TRIANGULATE); BM_ELEM_CD_SET_INT(newv, pbvh->cd_vert_node_offset, DYNTOPO_NODE_NONE); @@ -3185,7 +3156,9 @@ DynTopoState *BKE_dyntopo_init(BMesh *bm, PBVH *existing_pbvh) {CD_DYNTOPO_VERT, nullptr, CD_FLAG_TEMPORARY | CD_FLAG_NOCOPY}, {CD_PROP_INT32, SCULPT_ATTRIBUTE_NAME(dyntopo_node_id_vertex), - CD_FLAG_TEMPORARY | CD_FLAG_NOCOPY}}; + CD_FLAG_TEMPORARY | CD_FLAG_NOCOPY}, + {CD_PROP_INT32, SCULPT_ATTRIBUTE_NAME(valence), CD_FLAG_TEMPORARY}, + {CD_PROP_INT8, SCULPT_ATTRIBUTE_NAME(flags), CD_FLAG_TEMPORARY}}; BMCustomLayerReq flayers[] = { {CD_PROP_FLOAT2, SCULPT_ATTRIBUTE_NAME(face_areas), CD_FLAG_TEMPORARY | CD_FLAG_NOCOPY}, @@ -3213,6 +3186,10 @@ DynTopoState *BKE_dyntopo_init(BMesh *bm, PBVH *existing_pbvh) pbvh->cd_faceset_offset = CustomData_get_offset_named( &bm->pdata, CD_PROP_INT32, ".sculpt_face_set"); pbvh->cd_vcol_offset = -1; + pbvh->cd_valence = CustomData_get_offset_named( + &bm->vdata, CD_PROP_INT32, SCULPT_ATTRIBUTE_NAME(valence)); + pbvh->cd_flag = CustomData_get_offset_named( + &bm->vdata, CD_PROP_INT8, SCULPT_ATTRIBUTE_NAME(flags)); if (isfake) { pbvh->bm_log = BM_log_create(bm, pbvh->bm_idmap); @@ -3301,11 +3278,9 @@ void BKE_dyntopo_remesh(DynTopoState *ds, BMVert *v; BM_ITER_MESH (v, &iter, ds->pbvh->header.bm, BM_VERTS_OF_MESH) { - MSculptVert *mv = BKE_PBVH_SCULPTVERT(ds->pbvh->cd_sculpt_vert, v); - pbvh_boundary_update_bmesh(ds->pbvh, v); - mv->flag |= SCULPTVERT_NEED_TRIANGULATE; - mv->valence = BM_vert_edge_count(v); + dyntopo_add_flag(ds->pbvh, v, SCULPTVERT_NEED_TRIANGULATE); + BM_ELEM_CD_SET_INT(v, ds->pbvh->cd_valence, BM_vert_edge_count(v)); pbvh_check_vert_boundary(ds->pbvh, v); diff --git a/source/blender/blenkernel/intern/dyntopo_collapse.cc b/source/blender/blenkernel/intern/dyntopo_collapse.cc index 07c2e23ec1d..9171ff10305 100644 --- a/source/blender/blenkernel/intern/dyntopo_collapse.cc +++ b/source/blender/blenkernel/intern/dyntopo_collapse.cc @@ -206,8 +206,7 @@ static void collapse_ring_callback_pre(BMElem *elem, void *userdata) case BM_VERT: { BMVert *v = reinterpret_cast(elem); - MSculptVert *mv = BM_ELEM_CD_PTR(v, data->pbvh->cd_sculpt_vert); - MV_ADD_FLAG(mv, SCULPTVERT_NEED_DISK_SORT | SCULPTVERT_NEED_VALENCE); + dyntopo_add_flag(data->pbvh, v, SCULPTVERT_NEED_DISK_SORT | SCULPTVERT_NEED_VALENCE); BM_log_vert_removed(bm, data->pbvh->bm_log, v); pbvh_bmesh_vert_remove(data->pbvh, v); @@ -262,8 +261,7 @@ static void collapse_ring_callback_post(BMElem *elem, void *userdata) case BM_VERT: { BMVert *v = reinterpret_cast(elem); - MSculptVert *mv = BM_ELEM_CD_PTR(v, data->pbvh->cd_sculpt_vert); - MV_ADD_FLAG(mv, SCULPTVERT_NEED_DISK_SORT | SCULPTVERT_NEED_VALENCE); + dyntopo_add_flag(data->pbvh, v, SCULPTVERT_NEED_DISK_SORT | SCULPTVERT_NEED_VALENCE); check_new_elem_id(elem, data); BM_log_vert_added(bm, data->pbvh->bm_log, v); @@ -574,8 +572,6 @@ BMVert *pbvh_bmesh_collapse_edge(PBVH *pbvh, check_vert_fan_are_tris(pbvh, e->v1); check_vert_fan_are_tris(pbvh, e->v2); - MSculptVert *mv1 = BKE_PBVH_SCULPTVERT(pbvh->cd_sculpt_vert, v1); - MSculptVert *mv2 = BKE_PBVH_SCULPTVERT(pbvh->cd_sculpt_vert, v2); int boundflag1 = BM_ELEM_CD_GET_INT(v1, pbvh->cd_boundary_flag); int boundflag2 = BM_ELEM_CD_GET_INT(v2, pbvh->cd_boundary_flag); @@ -589,7 +585,6 @@ BMVert *pbvh_bmesh_collapse_edge(PBVH *pbvh, v_del = v2; v_conn = v1; - SWAP(MSculptVert *, mv1, mv2); SWAP(int, boundflag1, boundflag2); } @@ -664,9 +659,7 @@ BMVert *pbvh_bmesh_collapse_edge(PBVH *pbvh, BMLoop *l2 = l->f->l_first; do { pbvh_boundary_update_bmesh(pbvh, l2->v); - - MSculptVert *mv = BKE_PBVH_SCULPTVERT(pbvh->cd_sculpt_vert, l2->v); - mv->flag |= mupdateflag; + dyntopo_add_flag(pbvh, l2->v, mupdateflag); } while ((l2 = l2->next) != l->f->l_first); } while ((l = l->radial_next) != e2->l); } while ((e2 = BM_DISK_EDGE_NEXT(e2, v_conn)) != v_conn->e); @@ -678,10 +671,8 @@ BMVert *pbvh_bmesh_collapse_edge(PBVH *pbvh, return nullptr; } - MSculptVert *mv_conn = BKE_PBVH_SCULPTVERT(pbvh->cd_sculpt_vert, v_conn); pbvh_boundary_update_bmesh(pbvh, v_conn); - - MV_ADD_FLAG(mv_conn, mupdateflag); + dyntopo_add_flag(pbvh, v_conn, mupdateflag); #if 0 e2 = v_conn->e; @@ -702,10 +693,8 @@ BMVert *pbvh_bmesh_collapse_edge(PBVH *pbvh, } while (v_conn->e && (e2 = enext) != v_conn->e); #endif - MSculptVert *mv3 = BKE_PBVH_SCULPTVERT(pbvh->cd_sculpt_vert, v_conn); pbvh_boundary_update_bmesh(pbvh, v_conn); - - MV_ADD_FLAG(mv3, mupdateflag); + dyntopo_add_flag(pbvh, v_conn, mupdateflag); if (!v_conn->e) { /* Delete isolated vertex. */ diff --git a/source/blender/blenkernel/intern/dyntopo_intern.hh b/source/blender/blenkernel/intern/dyntopo_intern.hh index 21bef75e9fb..69730100a4e 100644 --- a/source/blender/blenkernel/intern/dyntopo_intern.hh +++ b/source/blender/blenkernel/intern/dyntopo_intern.hh @@ -9,6 +9,7 @@ struct GHash; struct BLI_Buffer; +struct SculptSession; #define DYNTOPO_DISABLE_SPLIT_EDGES 1 #define DYNTOPO_DISABLE_FIN_REMOVAL 2 @@ -193,7 +194,8 @@ static void pbvh_bmesh_verify(PBVH *pbvh); struct EdgeQueue; -typedef struct EdgeQueueContext { +struct EdgeQueueContext { + SculptSession *ss; BLI_mempool *pool = nullptr; BMesh *bm = nullptr; DyntopoMaskCB mask_cb = nullptr; @@ -233,7 +235,7 @@ typedef struct EdgeQueueContext { bool (*edge_queue_vert_in_range)(const struct EdgeQueueContext *q, BMVert *v); PBVHTopologyUpdateMode mode; -} EdgeQueueContext; +}; bool destroy_nonmanifold_fins(PBVH *pbvh, BMEdge *e_root); bool check_face_is_tri(PBVH *pbvh, BMFace *f); @@ -730,3 +732,13 @@ error: return false; } #endif + +static inline bool dyntopo_test_flag(PBVH *pbvh, BMVert *v, uint8_t flag) +{ + return *BM_ELEM_CD_PTR(v, pbvh->cd_flag) & flag; +} + +static inline void dyntopo_add_flag(PBVH *pbvh, BMVert *v, uint8_t flag) +{ + *BM_ELEM_CD_PTR(v, pbvh->cd_flag) |= flag; +} diff --git a/source/blender/blenkernel/intern/paint.cc b/source/blender/blenkernel/intern/paint.cc index c7ab3593120..a1510de86f4 100644 --- a/source/blender/blenkernel/intern/paint.cc +++ b/source/blender/blenkernel/intern/paint.cc @@ -26,6 +26,7 @@ #include "BLI_array.h" #include "BLI_bitmap.h" #include "BLI_hash.h" +#include "BLI_index_range.hh" #include "BLI_listbase.h" #include "BLI_math_vector.h" #include "BLI_string_ref.hh" @@ -62,6 +63,7 @@ #include "BKE_paint.h" #include "BKE_pbvh.h" #include "BKE_scene.h" +#include "BKE_sculpt.hh" #include "BKE_subdiv_ccg.h" #include "BKE_subsurf.h" #include "BKE_undo_system.h" @@ -81,6 +83,7 @@ void SCULPT_undo_ensure_bmlog(Object *ob); using blender::float3; +using blender::IndexRange; using blender::MutableSpan; using blender::Span; using blender::StringRef; @@ -2370,7 +2373,7 @@ int BKE_sculpt_mask_layers_ensure(Depsgraph *depsgraph, /* If vertices already have mask, copy into multires data. */ if (paint_mask) { for (const int i : polys.index_range()) { - const blender::IndexRange poly = polys[i]; + const IndexRange poly = polys[i]; /* Mask center. */ float avg = 0.0f; @@ -2554,8 +2557,11 @@ static PBVH *build_pbvh_for_dynamic_topology(Object *ob, bool update_sculptverts ob->sculpt->cd_sculpt_vert, ob->sculpt->attrs.face_areas->bmesh_cd_offset, ob->sculpt->attrs.boundary_flags->bmesh_cd_offset, - ob->sculpt->fast_draw, - update_sculptverts); + ob->sculpt->attrs.flags ? ob->sculpt->attrs.flags->bmesh_cd_offset : -1, + ob->sculpt->attrs.valence ? ob->sculpt->attrs.valence->bmesh_cd_offset : -1, + ob->sculpt->attrs.orig_co ? ob->sculpt->attrs.orig_co->bmesh_cd_offset : -1, + ob->sculpt->attrs.orig_no ? ob->sculpt->attrs.orig_no->bmesh_cd_offset : -1, + ob->sculpt->fast_draw); if (ob->sculpt->bm_log) { BKE_pbvh_set_bm_log(pbvh, ob->sculpt->bm_log); @@ -2651,6 +2657,32 @@ extern "C" bool BKE_sculptsession_check_sculptverts(Object *ob, struct PBVH *pbv { SculptSession *ss = ob->sculpt; + if (!ss->attrs.flags) { + BKE_sculpt_ensure_sculpt_layers(ob); + + if (ss->bm) { + int cd_flags = ss->attrs.flags->bmesh_cd_offset; + BMVert *v; + BMIter iter; + + BM_ITER_MESH (v, &iter, ss->bm, BM_VERTS_OF_MESH) { + *BM_ELEM_CD_PTR(v, cd_flags) = SCULPTVERT_NEED_VALENCE | + SCULPTVERT_NEED_TRIANGULATE | + SCULPTVERT_NEED_DISK_SORT; + } + } + else { + uint8_t *flags = static_cast(ss->attrs.flags->data); + + for (int i = 0; i < totvert; i++) { + flags[i] = SCULPTVERT_NEED_VALENCE | SCULPTVERT_NEED_TRIANGULATE | + SCULPTVERT_NEED_DISK_SORT; + } + } + } + + BKE_sculpt_ensure_origco(ob); + sculpt_boundary_flags_ensure(ob, pbvh, totvert); if (!ss->attrs.sculpt_vert || !ss->attrs.sculpt_vert->data) { @@ -3477,6 +3509,8 @@ static void sculptsession_bmesh_attr_update_internal(Object *ob) int cd_dyntopo_face = ss->attrs.dyntopo_node_id_face ? ss->attrs.dyntopo_node_id_face->bmesh_cd_offset : -1; + int cd_flag = ss->attrs.flags ? ss->attrs.flags->bmesh_cd_offset : -1; + int cd_valence = ss->attrs.valence ? ss->attrs.valence->bmesh_cd_offset : -1; BKE_pbvh_set_idmap(ss->pbvh, ss->bm_idmap); BKE_pbvh_update_offsets(ss->pbvh, @@ -3484,7 +3518,13 @@ static void sculptsession_bmesh_attr_update_internal(Object *ob) cd_dyntopo_face, cd_sculpt_vert, cd_face_area, - cd_boundary_flags); + cd_boundary_flags, + cd_flag, + cd_valence, + ss->attrs.orig_co ? ss->attrs.orig_co->bmesh_cd_offset : -1, + ss->attrs.orig_no ? ss->attrs.orig_no->bmesh_cd_offset : -1, + ss->attrs.curvature_dir ? ss->attrs.curvature_dir->bmesh_cd_offset : + -1); } } @@ -3644,7 +3684,13 @@ static void update_bmesh_offsets(Mesh *me, SculptSession *ss) ss->cd_face_node_offset, ss->cd_sculpt_vert, ss->cd_face_areas, - cd_boundary_flags); + cd_boundary_flags, + ss->attrs.flags ? ss->attrs.flags->bmesh_cd_offset : -1, + ss->attrs.valence ? ss->attrs.valence->bmesh_cd_offset : -1, + ss->attrs.orig_co ? ss->attrs.orig_co->bmesh_cd_offset : -1, + ss->attrs.orig_no ? ss->attrs.orig_no->bmesh_cd_offset : -1, + ss->attrs.curvature_dir ? ss->attrs.curvature_dir->bmesh_cd_offset : + -1); } } @@ -3794,3 +3840,186 @@ bool BKE_sculpt_has_persistent_base(SculptSession *ss) /* Detect multires. */ return ss->attrs.persistent_co; } + +void BKE_sculpt_ensure_origco(struct Object *ob) +{ + SculptAttributeParams params = {}; + if (!ob->sculpt->attrs.orig_co) { + ob->sculpt->attrs.orig_co = BKE_sculpt_attribute_ensure( + ob, ATTR_DOMAIN_POINT, CD_PROP_FLOAT3, SCULPT_ATTRIBUTE_NAME(orig_co), ¶ms); + } + if (!ob->sculpt->attrs.orig_no) { + ob->sculpt->attrs.orig_no = BKE_sculpt_attribute_ensure( + ob, ATTR_DOMAIN_POINT, CD_PROP_FLOAT3, SCULPT_ATTRIBUTE_NAME(orig_no), ¶ms); + } +} + +void BKE_sculpt_ensure_curvature_dir(struct Object *ob) +{ + SculptAttributeParams params = {}; + if (!ob->sculpt->attrs.curvature_dir) { + ob->sculpt->attrs.curvature_dir = BKE_sculpt_attribute_ensure( + ob, ATTR_DOMAIN_POINT, CD_PROP_FLOAT3, SCULPT_ATTRIBUTE_NAME(curvature_dir), ¶ms); + } +} + +void BKE_sculpt_ensure_origmask(struct Object *ob) +{ + SculptAttributeParams params = {}; + if (!ob->sculpt->attrs.orig_mask) { + ob->sculpt->attrs.orig_mask = BKE_sculpt_attribute_ensure( + ob, ATTR_DOMAIN_POINT, CD_PROP_FLOAT, SCULPT_ATTRIBUTE_NAME(orig_mask), ¶ms); + } +} +void BKE_sculpt_ensure_origcolor(struct Object *ob) +{ + SculptAttributeParams params = {}; + if (!ob->sculpt->attrs.orig_color) { + ob->sculpt->attrs.orig_color = BKE_sculpt_attribute_ensure( + ob, ATTR_DOMAIN_POINT, CD_PROP_COLOR, SCULPT_ATTRIBUTE_NAME(orig_color), ¶ms); + } +} + +void BKE_sculpt_ensure_origfset(struct Object *ob) +{ + SculptAttributeParams params = {}; + if (!ob->sculpt->attrs.orig_fsets) { + ob->sculpt->attrs.orig_fsets = BKE_sculpt_attribute_ensure( + ob, ATTR_DOMAIN_FACE, CD_PROP_INT32, SCULPT_ATTRIBUTE_NAME(orig_fsets), ¶ms); + } +} + +void BKE_sculpt_ensure_sculpt_layers(struct Object *ob) +{ + SculptAttributeParams params = {}; + + if (!ob->sculpt->attrs.flags) { + ob->sculpt->attrs.flags = BKE_sculpt_attribute_ensure( + ob, ATTR_DOMAIN_POINT, CD_PROP_INT8, SCULPT_ATTRIBUTE_NAME(flags), ¶ms); + } + if (!ob->sculpt->attrs.valence) { + ob->sculpt->attrs.flags = BKE_sculpt_attribute_ensure( + ob, ATTR_DOMAIN_POINT, CD_PROP_INT32, SCULPT_ATTRIBUTE_NAME(valence), ¶ms); + } +} + +namespace blender::bke::paint { +bool get_original_vertex(SculptSession *ss, + PBVHVertRef vertex, + const float **r_co, + float **r_no, + float **r_color, + float **r_mask) +{ + uint8_t flags = vertex_attr_get(vertex, ss->attrs.flags); + bool retval = false; + + if (sculpt::stroke_id_test(ss, vertex, STROKEID_USER_ORIGINAL)) { + if (ss->attrs.orig_co) { + const float *co = nullptr, *no = nullptr; + + switch (BKE_pbvh_type(ss->pbvh)) { + case PBVH_BMESH: + BMVert *v = reinterpret_cast(vertex.i); + co = v->co; + no = v->no; + break; + case PBVH_FACES: + if (ss->shapekey_active || ss->deform_modifiers_active) { + co = BKE_pbvh_get_vert_positions(ss->pbvh)[vertex.i]; + } + else { + co = ss->vert_positions[vertex.i]; + } + + no = BKE_pbvh_get_vert_normals(ss->pbvh)[vertex.i]; + break; + case PBVH_GRIDS: + const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh); + const int grid_index = vertex.i / key->grid_area; + const int vertex_index = vertex.i - grid_index * key->grid_area; + CCGElem *elem = BKE_pbvh_get_grids(ss->pbvh)[grid_index]; + + co = CCG_elem_co(key, CCG_elem_offset(key, elem, vertex_index)); + no = CCG_elem_no(key, CCG_elem_offset(key, elem, vertex_index)); + + break; + } + + copy_v3_v3(vertex_attr_ptr(vertex, ss->attrs.orig_co), co); + copy_v3_v3(vertex_attr_ptr(vertex, ss->attrs.orig_no), no); + } + + bool have_colors = BKE_pbvh_type(ss->pbvh) != PBVH_GRIDS && + ((ss->bm && ss->cd_vcol_offset != -1) || ss->vcol || ss->mcol); + + if (ss->attrs.orig_color && have_colors) { + BKE_pbvh_vertex_color_get( + ss->pbvh, vertex, vertex_attr_ptr(vertex, ss->attrs.orig_color)); + } + + if (ss->attrs.orig_mask) { + float *mask = nullptr; + switch (BKE_pbvh_type(ss->pbvh)) { + case PBVH_FACES: + mask = ss->vmask ? &ss->vmask[vertex.i] : nullptr; + case PBVH_BMESH: { + BMVert *v; + int cd_mask = CustomData_get_offset(&ss->bm->vdata, CD_PAINT_MASK); + + v = (BMVert *)vertex.i; + mask = cd_mask != -1 ? static_cast(BM_ELEM_CD_GET_VOID_P(v, cd_mask)) : nullptr; + } + case PBVH_GRIDS: { + const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh); + + if (key->mask_offset == -1) { + mask = nullptr; + } + else { + const int grid_index = vertex.i / key->grid_area; + const int vertex_index = vertex.i - grid_index * key->grid_area; + CCGElem *elem = BKE_pbvh_get_grids(ss->pbvh)[grid_index]; + mask = CCG_elem_mask(key, CCG_elem_offset(key, elem, vertex_index)); + } + } + } + + if (mask) { + vertex_attr_set(vertex, ss->attrs.orig_mask, *mask); + } + } + + retval = true; + } + + if (r_co && ss->attrs.orig_co) { + *r_co = vertex_attr_ptr(vertex, ss->attrs.orig_co); + } + if (r_no && ss->attrs.orig_no) { + *r_no = vertex_attr_ptr(vertex, ss->attrs.orig_no); + } + if (r_color && ss->attrs.orig_color) { + *r_color = vertex_attr_ptr(vertex, ss->attrs.orig_color); + } + if (r_mask && ss->attrs.orig_mask) { + *r_mask = vertex_attr_ptr(vertex, ss->attrs.orig_mask); + } + + return retval; +} + +void SCULPT_load_all_original(Object *ob) +{ + SculptSession *ss = ob->sculpt; + + int verts_count = BKE_sculptsession_vertex_count(ss); + for (int i : IndexRange(verts_count)) { + PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); + + blender::bke::sculpt::stroke_id_clear(ss, vertex, STROKEID_USER_ORIGINAL); + SCULPT_get_original(ss, vertex); + } +} + +} // namespace blender::bke::paint diff --git a/source/blender/blenkernel/intern/pbvh.cc b/source/blender/blenkernel/intern/pbvh.cc index 2cee9c9f417..2bf5faa639c 100644 --- a/source/blender/blenkernel/intern/pbvh.cc +++ b/source/blender/blenkernel/intern/pbvh.cc @@ -3011,7 +3011,8 @@ static bool pbvh_grids_node_raycast(PBVH *pbvh, return hit; } -bool BKE_pbvh_node_raycast(PBVH *pbvh, +bool BKE_pbvh_node_raycast(SculptSession *ss, + PBVH *pbvh, PBVHNode *node, float (*origco)[3], bool use_origco, @@ -3064,7 +3065,8 @@ bool BKE_pbvh_node_raycast(PBVH *pbvh, face_normal); break; case PBVH_BMESH: - hit = pbvh_bmesh_node_raycast(pbvh, + hit = pbvh_bmesh_node_raycast(ss, + pbvh, node, ray_start, ray_normal, @@ -3284,7 +3286,8 @@ static bool pbvh_grids_node_nearest_to_ray(PBVH *pbvh, return hit; } -bool BKE_pbvh_node_find_nearest_to_ray(PBVH *pbvh, +bool BKE_pbvh_node_find_nearest_to_ray(SculptSession *ss, + PBVH *pbvh, PBVHNode *node, float (*origco)[3], bool use_origco, @@ -3311,7 +3314,7 @@ bool BKE_pbvh_node_find_nearest_to_ray(PBVH *pbvh, break; case PBVH_BMESH: hit = pbvh_bmesh_node_nearest_to_ray( - pbvh, node, ray_start, ray_normal, depth, dist_sq, use_origco, stroke_id); + ss, pbvh, node, ray_start, ray_normal, depth, dist_sq, use_origco, stroke_id); break; } @@ -4613,87 +4616,6 @@ void BKE_pbvh_ensure_node_loops(PBVH *pbvh) MEM_SAFE_FREE(visit); } -bool BKE_pbvh_get_origvert( - PBVH *pbvh, PBVHVertRef vertex, const float **r_co, float **r_no, float **r_color) -{ - MSculptVert *mv; - - switch (pbvh->header.type) { - case PBVH_FACES: - case PBVH_GRIDS: - mv = pbvh->msculptverts + vertex.i; - - if (mv->stroke_id != pbvh->stroke_id) { - mv->stroke_id = pbvh->stroke_id; - float *mask = nullptr; - - if (pbvh->header.type == PBVH_FACES) { - copy_v3_v3(mv->origco, pbvh->vert_positions[vertex.i]); - copy_v3_v3(mv->origno, pbvh->vert_normals[vertex.i]); - mask = (float *)CustomData_get_layer(pbvh->vdata, CD_PAINT_MASK); - - if (mask) { - mask += vertex.i; - } - } - else { - const CCGKey *key = BKE_pbvh_get_grid_key(pbvh); - const int grid_index = vertex.i / key->grid_area; - const int vertex_index = vertex.i - grid_index * key->grid_area; - CCGElem *elem = BKE_pbvh_get_grids(pbvh)[grid_index]; - - copy_v3_v3(mv->origco, CCG_elem_co(key, CCG_elem_offset(key, elem, vertex_index))); - copy_v3_v3(mv->origno, CCG_elem_no(key, CCG_elem_offset(key, elem, vertex_index))); - mask = key->has_mask ? CCG_elem_mask(key, CCG_elem_offset(key, elem, vertex_index)) : - nullptr; - } - - if (mask) { - mv->origmask = (ushort)(*mask * 65535.0f); - } - - if (pbvh->color_layer) { - BKE_pbvh_vertex_color_get(pbvh, vertex, mv->origcolor); - } - } - break; - case PBVH_BMESH: { - BMVert *v = (BMVert *)vertex.i; - mv = BKE_PBVH_SCULPTVERT(pbvh->cd_sculpt_vert, v); - - if (mv->stroke_id != pbvh->stroke_id) { - mv->stroke_id = pbvh->stroke_id; - - copy_v3_v3(mv->origco, v->co); - copy_v3_v3(mv->origno, v->no); - - if (pbvh->cd_vert_mask_offset != -1) { - mv->origmask = (short)(BM_ELEM_CD_GET_FLOAT(v, pbvh->cd_vert_mask_offset) * 65535.0f); - } - - if (pbvh->cd_vcol_offset != -1) { - BKE_pbvh_vertex_color_get(pbvh, vertex, mv->origcolor); - } - } - break; - } - } - - if (r_co) { - *r_co = mv->origco; - } - - if (r_no) { - *r_no = mv->origno; - } - - if (r_color) { - *r_color = mv->origcolor; - } - - return true; -} - int BKE_pbvh_debug_draw_gen_get(PBVHNode *node) { return node->debug_draw_gen; diff --git a/source/blender/blenkernel/intern/pbvh_bmesh.cc b/source/blender/blenkernel/intern/pbvh_bmesh.cc index 9524b7e5624..58259def545 100644 --- a/source/blender/blenkernel/intern/pbvh_bmesh.cc +++ b/source/blender/blenkernel/intern/pbvh_bmesh.cc @@ -291,10 +291,9 @@ static BMVert *pbvh_bmesh_vert_create(PBVH *pbvh, /* avoid initializing customdata because its quite involved */ BMVert *v = BM_vert_create(pbvh->header.bm, co, nullptr, BM_CREATE_NOP); - MSculptVert *mv = BKE_PBVH_SCULPTVERT(pbvh->cd_sculpt_vert, v); pbvh_boundary_update_bmesh(pbvh, v); - MV_ADD_FLAG(mv, SCULPTVERT_NEED_DISK_SORT | SCULPTVERT_NEED_VALENCE); + dyntopo_add_flag(pbvh, v, SCULPTVERT_NEED_DISK_SORT | SCULPTVERT_NEED_VALENCE); if (v_example) { v->head.hflag = v_example->head.hflag; @@ -308,11 +307,13 @@ static BMVert *pbvh_bmesh_vert_create(PBVH *pbvh, // keep MSculptVert copied from v_example as-is } else { +#if 0 /* XXX: do we need to load original data here ? */ MSculptVert *mv = BKE_PBVH_SCULPTVERT(pbvh->cd_sculpt_vert, v); copy_v3_v3(mv->origco, co); copy_v3_v3(mv->origno, no); mv->origmask = 0.0f; +#endif /* This value is logged below */ copy_v3_v3(v->no, no); @@ -415,9 +416,7 @@ BMFace *pbvh_bmesh_face_create(PBVH *pbvh, } pbvh_boundary_update_bmesh(pbvh, l->v); - - MSculptVert *mv = BKE_PBVH_SCULPTVERT(pbvh->cd_sculpt_vert, l->v); - MV_ADD_FLAG(mv, SCULPTVERT_NEED_DISK_SORT | SCULPTVERT_NEED_VALENCE); + dyntopo_add_flag(pbvh, l->v, SCULPTVERT_NEED_DISK_SORT | SCULPTVERT_NEED_VALENCE); l = l->next; } while (l != f->l_first); @@ -426,9 +425,7 @@ BMFace *pbvh_bmesh_face_create(PBVH *pbvh, BMLoop *l = f->l_first; do { pbvh_boundary_update_bmesh(pbvh, l->v); - - MSculptVert *mv = BKE_PBVH_SCULPTVERT(pbvh->cd_sculpt_vert, l->v); - MV_ADD_FLAG(mv, SCULPTVERT_NEED_DISK_SORT | SCULPTVERT_NEED_VALENCE); + dyntopo_add_flag(pbvh, l->v, SCULPTVERT_NEED_DISK_SORT | SCULPTVERT_NEED_VALENCE); } while ((l = l->next) != f->l_first); } @@ -471,10 +468,11 @@ BMVert *BKE_pbvh_vert_create_bmesh( BM_ELEM_CD_SET_INT(v, pbvh->cd_vert_node_offset, DYNTOPO_NODE_NONE); pbvh_boundary_update_bmesh(pbvh, v); - MSculptVert *mv = (MSculptVert *)BM_ELEM_CD_GET_VOID_P(v, pbvh->cd_sculpt_vert); - MV_ADD_FLAG(mv, SCULPTVERT_NEED_VALENCE | SCULPTVERT_NEED_DISK_SORT); + dyntopo_add_flag(pbvh, v, SCULPTVERT_NEED_DISK_SORT | SCULPTVERT_NEED_VALENCE); +#if 0 /* XXX: do we need to load origco here? */ copy_v3_v3(mv->origco, co); +#endif return v; } @@ -755,7 +753,6 @@ static void pbvh_bmesh_node_finalize(PBVH *pbvh, do { BMVert *v = l_iter->v; - MSculptVert *mv = BKE_PBVH_SCULPTVERT(pbvh->cd_sculpt_vert, v); int *flags = BM_ELEM_CD_PTR(v, pbvh->cd_boundary_flag); *flags |= SCULPT_BOUNDARY_NEEDS_UPDATE; @@ -771,7 +768,7 @@ static void pbvh_bmesh_node_finalize(PBVH *pbvh, } /* Update node bounding box */ BB_expand(&n->vb, v->co); - BB_expand(&n->orig_vb, mv->origco); + BB_expand(&n->orig_vb, BM_ELEM_CD_PTR(v, pbvh->cd_origco)); } while ((l_iter = l_iter->next) != l_first); if (!BM_elem_flag_test(f, BM_ELEM_HIDDEN)) { @@ -1113,10 +1110,9 @@ void bke_pbvh_insert_face_finalize(PBVH *pbvh, BMFace *f, const int ni) BMLoop *l = f->l_first; do { const int ni2 = BM_ELEM_CD_GET_INT(l->v, pbvh->cd_vert_node_offset); - MSculptVert *mv = BKE_PBVH_SCULPTVERT(pbvh->cd_sculpt_vert, l->v); BB_expand(&node->vb, l->v->co); - BB_expand(&node->orig_vb, mv->origco); + BB_expand(&node->orig_vb, BM_ELEM_CD_PTR(l->v, pbvh->cd_origco)); if (ni2 == DYNTOPO_NODE_NONE) { BM_ELEM_CD_SET_INT(l->v, pbvh->cd_vert_node_offset, ni); @@ -1399,14 +1395,14 @@ static bool pbvh_poly_hidden(PBVH *pbvh, BMFace *f) return BM_elem_flag_test(f, BM_ELEM_HIDDEN); } -bool BKE_pbvh_bmesh_check_origdata(PBVH *pbvh, BMVert *v, int stroke_id) +bool BKE_pbvh_bmesh_check_origdata(SculptSession *ss, BMVert *v, int stroke_id) { PBVHVertRef vertex = {(intptr_t)v}; - - return BKE_pbvh_get_origvert(pbvh, vertex, nullptr, nullptr, nullptr); + blender::bke::paint::get_original_vertex(ss, vertex, nullptr, nullptr, nullptr, nullptr); } -bool pbvh_bmesh_node_raycast(PBVH *pbvh, +bool pbvh_bmesh_node_raycast(SculptSession *ss, + PBVH *pbvh, PBVHNode *node, const float ray_start[3], const float ray_normal[3], @@ -1439,21 +1435,17 @@ bool pbvh_bmesh_node_raycast(PBVH *pbvh, float *nos[3]; if (use_original) { - BKE_pbvh_bmesh_check_origdata(pbvh, verts[0], stroke_id); - BKE_pbvh_bmesh_check_origdata(pbvh, verts[1], stroke_id); - BKE_pbvh_bmesh_check_origdata(pbvh, verts[2], stroke_id); + BKE_pbvh_bmesh_check_origdata(ss, verts[0], stroke_id); + BKE_pbvh_bmesh_check_origdata(ss, verts[1], stroke_id); + BKE_pbvh_bmesh_check_origdata(ss, verts[2], stroke_id); - MSculptVert *mv1 = BM_ELEM_CD_PTR(verts[0], pbvh->cd_sculpt_vert); - MSculptVert *mv2 = BM_ELEM_CD_PTR(verts[1], pbvh->cd_sculpt_vert); - MSculptVert *mv3 = BM_ELEM_CD_PTR(verts[2], pbvh->cd_sculpt_vert); + cos[0] = BM_ELEM_CD_PTR(verts[0], pbvh->cd_origco); + cos[1] = BM_ELEM_CD_PTR(verts[1], pbvh->cd_origco); + cos[2] = BM_ELEM_CD_PTR(verts[2], pbvh->cd_origco); - cos[0] = mv1->origco; - cos[1] = mv2->origco; - cos[2] = mv3->origco; - - nos[0] = mv1->origno; - nos[1] = mv2->origno; - nos[2] = mv3->origno; + nos[0] = BM_ELEM_CD_PTR(verts[0], pbvh->cd_origno); + nos[1] = BM_ELEM_CD_PTR(verts[1], pbvh->cd_origno); + nos[2] = BM_ELEM_CD_PTR(verts[2], pbvh->cd_origno); } else { for (int j = 0; j < 3; j++) { @@ -1532,7 +1524,8 @@ bool BKE_pbvh_bmesh_node_raycast_detail(PBVH *pbvh, return false; } -bool pbvh_bmesh_node_nearest_to_ray(PBVH *pbvh, +bool pbvh_bmesh_node_nearest_to_ray(SculptSession *ss, + PBVH *pbvh, PBVHNode *node, const float ray_start[3], const float ray_normal[3], @@ -1562,9 +1555,9 @@ bool pbvh_bmesh_node_nearest_to_ray(PBVH *pbvh, float *co1, *co2, *co3; if (use_original) { - BKE_pbvh_bmesh_check_origdata(pbvh, v1, stroke_id); - BKE_pbvh_bmesh_check_origdata(pbvh, v2, stroke_id); - BKE_pbvh_bmesh_check_origdata(pbvh, v3, stroke_id); + BKE_pbvh_bmesh_check_origdata(ss, v1, stroke_id); + BKE_pbvh_bmesh_check_origdata(ss, v2, stroke_id); + BKE_pbvh_bmesh_check_origdata(ss, v3, stroke_id); co1 = BKE_PBVH_SCULPTVERT(cd_sculpt_vert, v1)->origco; co2 = BKE_PBVH_SCULPTVERT(cd_sculpt_vert, v2)->origco; @@ -2104,34 +2097,27 @@ void bke_pbvh_update_vert_boundary(int cd_sculpt_vert, int cd_face_node_offset, int cd_vcol, int cd_boundary_flag, + int cd_flag, + int cd_valence, BMVert *v, int bound_symmetry, const CustomData *ldata, const int totuv) { - MSculptVert *mv = BKE_PBVH_SCULPTVERT(cd_sculpt_vert, v); - - float curv = 0.0f, totcurv = 0.0f; - - int newflag = mv->flag; + int newflag = *BM_ELEM_CD_PTR(v, cd_flag); int oldflag = newflag; - int oldval = mv->valence; + int oldval = BM_ELEM_CD_GET_INT(v, cd_valence); int boundflag = 0; BMEdge *e = v->e; newflag &= ~(SCULPTVERT_VERT_FSET_HIDDEN | SCULPTVERT_PBVH_BOUNDARY); - ushort stroke_id = (ushort)mv->stroke_id; - if (!e) { boundflag |= SCULPT_BOUNDARY_MESH; - int oldboundflag = BM_ELEM_CD_GET_INT(v, cd_boundary_flag); - - atomic_cas_int32(&mv->flag, oldflag, newflag); - atomic_cas_int32(BM_ELEM_CD_PTR(v, cd_boundary_flag), oldboundflag, boundflag); - - atomic_cas_short2(&mv->valence, (ushort)oldval, stroke_id, 0, stroke_id); + BM_ELEM_CD_SET_INT(v, cd_valence, 0); + BM_ELEM_CD_SET_INT(v, cd_boundary_flag, 0); + *BM_ELEM_CD_PTR(v, cd_flag) = newflag; return; } @@ -2174,17 +2160,6 @@ void bke_pbvh_update_vert_boundary(int cd_sculpt_vert, do { BMVert *v2 = v == e->v1 ? e->v2 : e->v1; -#if 0 - float tmp[3]; - sub_v3_v3v3(tmp, v2->co, v->co); - madd_v3_v3fl(avg, v->no, -dot_v3v3(v->no, tmp)); - // madd_v3_v3fl(tmp, v->no, -dot_v3v3(v->no, tmp)); - add_v3_v3(avg, tmp); - - avg_len += len_squared_v3(tmp); - totcurv += 1.0f; -#endif - if (BM_ELEM_CD_GET_INT(v2, cd_vert_node_offset) != ni) { newflag |= SCULPTVERT_PBVH_BOUNDARY; } @@ -2313,9 +2288,6 @@ void bke_pbvh_update_vert_boundary(int cd_sculpt_vert, float th = saacos(dot_v3v3(e->l->f->no, e->l->radial_next->f->no)); th *= M_1_PI * 0.25f; - // th = th * 0.5 + 0.5; - curv += th; - totcurv += 1.0f; if (cd_faceset_offset != -1) { // fset = abs(BM_ELEM_CD_GET_INT(e->l->radial_next->f, cd_faceset_offset)); @@ -2367,38 +2339,12 @@ void bke_pbvh_update_vert_boundary(int cd_sculpt_vert, boundflag |= SCULPT_CORNER_MESH; } -#if 0 - if (totcurv > 0.0f) { - mul_v3_fl(avg, 1.0f / totcurv); - avg_len /= totcurv; - } - - if (avg_len > 0.0f) { - curv = len_squared_v3(avg) / avg_len; - } - else { - curv = 0.0f; - } -#else - if (totcurv > 0.0f) { - curv /= totcurv; - } -#endif - int oldboundflag = BM_ELEM_CD_GET_INT(v, cd_boundary_flag); - atomic_cas_int32(&mv->flag, oldflag, newflag); - atomic_cas_int32(BM_ELEM_CD_PTR(v, cd_boundary_flag), oldboundflag, boundflag); - - atomic_cas_short2(&mv->valence, (ushort)oldval, stroke_id, (ushort)val, stroke_id); - - /* no atomic_cas_int16, so do origmask and curv at once */ - - ushort newcurv = (unsigned short)(min_ff(fabsf(curv), 1.0f) * 65535.0f); - ushort oldcurv = mv->curv; - ushort origmask = mv->origmask; - - atomic_cas_short2(&mv->origmask, origmask, oldcurv, origmask, newcurv); + /* XXX: does this need to be threadsafe? */ + BM_ELEM_CD_SET_INT(v, cd_boundary_flag, boundflag); + *(BM_ELEM_CD_PTR(v, cd_flag)) = newflag; + BM_ELEM_CD_SET_INT(v, cd_valence, val); } bool BKE_pbvh_check_vert_boundary(PBVH *pbvh, BMVert *v) @@ -2412,6 +2358,8 @@ void BKE_pbvh_update_vert_boundary(int cd_sculpt_vert, int cd_face_node_offset, int cd_vcol, int cd_boundary_flag, + int cd_flag, + int cd_valence, BMVert *v, int bound_symmetry, const CustomData *ldata, @@ -2424,6 +2372,8 @@ void BKE_pbvh_update_vert_boundary(int cd_sculpt_vert, cd_face_node_offset, cd_vcol, cd_boundary_flag, + cd_flag, + cd_valence, v, bound_symmetry, ldata, @@ -2443,6 +2393,8 @@ void BKE_pbvh_recalc_bmesh_boundary(PBVH *pbvh) pbvh->cd_face_node_offset, pbvh->cd_vcol_offset, pbvh->cd_boundary_flag, + pbvh->cd_flag, + pbvh->cd_valence, v, pbvh->boundary_symmetry, &pbvh->header.bm->ldata, @@ -2450,17 +2402,6 @@ void BKE_pbvh_recalc_bmesh_boundary(PBVH *pbvh) } } -void BKE_pbvh_update_sculpt_verts(PBVH *pbvh) -{ - int totvert = pbvh->totvert; - - for (int i = 0; i < totvert; i++) { - PBVHVertRef vertex = BKE_pbvh_index_to_vertex(pbvh, i); - - BKE_pbvh_get_origvert(pbvh, vertex, nullptr, nullptr, nullptr); - } -} - void BKE_pbvh_set_idmap(PBVH *pbvh, BMIdMap *idmap) { pbvh->bm_idmap = idmap; @@ -2478,8 +2419,11 @@ void BKE_pbvh_build_bmesh(PBVH *pbvh, const int cd_sculpt_vert, const int cd_face_areas, const int cd_boundary_flag, - bool fast_draw, - bool update_sculptverts) + const int cd_flag_offset, + const int cd_valence_offset, + const int cd_origco, + const int cd_origno, + bool fast_draw) { // coalese_pbvh(pbvh, bm); @@ -2491,6 +2435,10 @@ void BKE_pbvh_build_bmesh(PBVH *pbvh, pbvh->cd_vert_mask_offset = CustomData_get_offset(&bm->vdata, CD_PAINT_MASK); pbvh->cd_sculpt_vert = cd_sculpt_vert; pbvh->cd_boundary_flag = cd_boundary_flag; + pbvh->cd_flag = cd_flag_offset; + pbvh->cd_valence = cd_valence_offset; + pbvh->cd_origco = cd_origco; + pbvh->cd_origno = cd_origno; pbvh->mesh = me; @@ -2558,11 +2506,10 @@ void BKE_pbvh_build_bmesh(PBVH *pbvh, bm->elem_index_dirty |= BM_FACE; BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) { - MSculptVert *mv = BKE_PBVH_SCULPTVERT(pbvh->cd_sculpt_vert, v); - - mv->flag |= SCULPTVERT_NEED_VALENCE | SCULPTVERT_NEED_TRIANGULATE | SCULPTVERT_NEED_DISK_SORT; - copy_v3_v3(mv->origco, v->co); - copy_v3_v3(mv->origno, v->no); + dyntopo_add_flag(pbvh, + v, + SCULPTVERT_NEED_VALENCE | SCULPTVERT_NEED_TRIANGULATE | + SCULPTVERT_NEED_DISK_SORT); int *flags = BM_ELEM_CD_PTR(v, pbvh->cd_boundary_flag); *flags |= SCULPT_BOUNDARY_NEEDS_UPDATE; @@ -2636,10 +2583,6 @@ void BKE_pbvh_build_bmesh(PBVH *pbvh, } } - if (update_sculptverts) { - BKE_pbvh_update_sculpt_verts(pbvh); - } - pbvh_print_mem_size(pbvh); /* update face areas */ @@ -3174,8 +3117,7 @@ void BKE_pbvh_bmesh_flag_all_disk_sort(PBVH *pbvh) BMIter iter; BM_ITER_MESH (v, &iter, pbvh->header.bm, BM_VERTS_OF_MESH) { - MSculptVert *mv = BKE_PBVH_SCULPTVERT(pbvh->cd_sculpt_vert, v); - mv->flag |= SCULPTVERT_NEED_DISK_SORT; + dyntopo_add_flag(pbvh, v, SCULPTVERT_NEED_DISK_SORT); } } @@ -3185,7 +3127,7 @@ void BKE_pbvh_bmesh_update_all_valence(PBVH *pbvh) BMVert *v; BM_ITER_MESH (v, &iter, pbvh->header.bm, BM_VERTS_OF_MESH) { - BKE_pbvh_bmesh_update_valence(pbvh->cd_sculpt_vert, BKE_pbvh_make_vref((intptr_t)v)); + BKE_pbvh_bmesh_update_valence(pbvh, BKE_pbvh_make_vref((intptr_t)v)); } } @@ -3205,23 +3147,20 @@ void BKE_pbvh_bmesh_on_mesh_change(PBVH *pbvh) const int cd_sculpt_vert = pbvh->cd_sculpt_vert; BM_ITER_MESH (v, &iter, pbvh->header.bm, BM_VERTS_OF_MESH) { - MSculptVert *mv = BKE_PBVH_SCULPTVERT(cd_sculpt_vert, v); int *flags = BM_ELEM_CD_PTR(v, pbvh->cd_boundary_flag); *flags |= SCULPT_BOUNDARY_NEEDS_UPDATE; - MV_ADD_FLAG(mv, SCULPTVERT_NEED_DISK_SORT | SCULPTVERT_NEED_TRIANGULATE); - BKE_pbvh_bmesh_update_valence(pbvh->cd_sculpt_vert, BKE_pbvh_make_vref((intptr_t)v)); + dyntopo_add_flag(pbvh, v, SCULPTVERT_NEED_DISK_SORT | SCULPTVERT_NEED_TRIANGULATE | SCULPTVERT_NEED_VALENCE); + BKE_pbvh_bmesh_update_valence(pbvh, BKE_pbvh_make_vref((intptr_t)v)); } } bool BKE_pbvh_bmesh_mark_update_valence(PBVH *pbvh, PBVHVertRef vertex) { BMVert *v = (BMVert *)vertex.i; - MSculptVert *mv = BM_ELEM_CD_PTR(v, pbvh->cd_sculpt_vert); - bool ret = mv->flag & SCULPTVERT_NEED_VALENCE; - - mv->flag |= SCULPTVERT_NEED_VALENCE; + bool ret = (*BM_ELEM_CD_PTR(v, pbvh->cd_flag)) & SCULPTVERT_NEED_VALENCE; + dyntopo_add_flag(pbvh, v, SCULPTVERT_NEED_VALENCE); return ret; } @@ -3229,31 +3168,31 @@ bool BKE_pbvh_bmesh_mark_update_valence(PBVH *pbvh, PBVHVertRef vertex) bool BKE_pbvh_bmesh_check_valence(PBVH *pbvh, PBVHVertRef vertex) { BMVert *v = (BMVert *)vertex.i; - MSculptVert *mv = BM_ELEM_CD_PTR(v, pbvh->cd_sculpt_vert); - if (mv->flag & SCULPTVERT_NEED_VALENCE) { - BKE_pbvh_bmesh_update_valence(pbvh->cd_sculpt_vert, vertex); + if (*BM_ELEM_CD_PTR(v, pbvh->cd_flag) & SCULPTVERT_NEED_VALENCE) { + BKE_pbvh_bmesh_update_valence(pbvh, vertex); return true; } return false; } -void BKE_pbvh_bmesh_update_valence(int cd_sculpt_vert, PBVHVertRef vertex) +void BKE_pbvh_bmesh_update_valence(PBVH *pbvh, PBVHVertRef vertex) { BMVert *v = (BMVert *)vertex.i; BMEdge *e; - MSculptVert *mv = BM_ELEM_CD_PTR(v, cd_sculpt_vert); + uint8_t *flag = BM_ELEM_CD_PTR(v, pbvh->cd_flag); + uint *valence = BM_ELEM_CD_PTR(v, pbvh->cd_valence); - mv->flag &= ~SCULPTVERT_NEED_VALENCE; + *flag &= ~SCULPTVERT_NEED_VALENCE; if (!v->e) { - mv->valence = 0; + *valence = 0; return; } - mv->valence = 0; + *valence = 0; e = v->e; @@ -3262,7 +3201,7 @@ void BKE_pbvh_bmesh_update_valence(int cd_sculpt_vert, PBVHVertRef vertex) } do { - mv->valence++; + (*valence)++; e = v == e->v1 ? e->v1_disk_link.next : e->v2_disk_link.next; @@ -4125,7 +4064,11 @@ void BKE_pbvh_update_offsets(PBVH *pbvh, const int cd_face_node_offset, const int cd_sculpt_vert, const int cd_face_areas, - const int cd_boundary_flag) + const int cd_boundary_flag, + const int cd_flag, + const int cd_valence, + const int cd_origco, + const int cd_origno) { pbvh->cd_face_node_offset = cd_face_node_offset; pbvh->cd_vert_node_offset = cd_vert_node_offset; @@ -4141,6 +4084,11 @@ void BKE_pbvh_update_offsets(PBVH *pbvh, if (pbvh->bm_idmap) { BM_idmap_check_attributes(pbvh->bm_idmap); } + + pbvh->cd_flag = cd_flag; + pbvh->cd_valence = cd_valence; + pbvh->cd_origco = cd_origco; + pbvh->cd_origno = cd_origno; } static void scan_edge_split(BMesh *bm, BMEdge **edges, int totedge) @@ -5013,29 +4961,6 @@ struct CacheParamDef { float defvalue, min, max; }; -static CacheParamDef pbvh_bmesh_cache_param_def[] = { - {"vchunk", 512.0f, 256.0f, 1024.0f * 12.0f}, - {"echunk", 512.0f, 256.0f, 1024.0f * 12.0f}, - {"lchunk", 512.0f, 256.0f, 1024.0f * 12.0f}, - {"pchunk", 512.0f, 256.0f, 1024.0f * 12.0f}, - {"cluster_steps", 512.0f, 1.0f, 256.0f}, - {"cluster_size", 512.0f, 1.0f, 8192.0f * 32.0f}}; - -int pbvh_bmesh_cache_test_totparams() -{ - return sizeof(pbvh_bmesh_cache_param_def) / sizeof(*pbvh_bmesh_cache_param_def); -} - -void pbvh_bmesh_cache_test_default_params(CacheParams *params) -{ - float *fparams = (float *)params; - int totparam = pbvh_bmesh_cache_test_totparams(); - - for (int i = 0; i < totparam; i++) { - fparams[i] = pbvh_bmesh_cache_param_def[i].defvalue; - } -} - static void *hashco(float fx, float fy, float fz, float fdimen) { double x = (double)fx; @@ -5532,390 +5457,6 @@ So looks like the biggest gain is from replacing pointers with indices is a tad bit faster then the index-replacement one, but not by that much. */ -void pbvh_bmesh_cache_test(CacheParams *params, BMesh **r_bm, PBVH **r_pbvh_out) -{ - // build mesh - const int steps = 325; - - printf("== Starting Test ==\n"); - - printf("building test mesh. . .\n"); - - BMAllocTemplate templ = {0, 0, 0, 0}; - BMeshCreateParams bmparams = {}; - - bmparams.id_elem_mask = BM_VERT | BM_EDGE | BM_FACE; - bmparams.id_map = true; - bmparams.create_unique_ids = true; - bmparams.temporary_ids = false; - bmparams.no_reuse_ids = false; - - BMesh *bm = BM_mesh_create(&templ, &bmparams); - - // reinit pools - BLI_mempool_destroy(bm->vpool); - BLI_mempool_destroy(bm->epool); - BLI_mempool_destroy(bm->lpool); - BLI_mempool_destroy(bm->fpool); - - bm->vpool = BLI_mempool_create(sizeof(BMVert), 0, (int)params->vchunk, BLI_MEMPOOL_ALLOW_ITER); - bm->epool = BLI_mempool_create(sizeof(BMEdge), 0, (int)params->echunk, BLI_MEMPOOL_ALLOW_ITER); - bm->lpool = BLI_mempool_create(sizeof(BMLoop), 0, (int)params->lchunk, BLI_MEMPOOL_ALLOW_ITER); - bm->fpool = BLI_mempool_create(sizeof(BMFace), 0, (int)params->pchunk, BLI_MEMPOOL_ALLOW_ITER); - - GHash *vhash = BLI_ghash_ptr_new("vhash"); - - float df = 1.0f / (float)steps; - - int hashdimen = steps * 8; - - BMVert **grid = MEM_cnew_array(steps * steps, "bmvert grid"); - - BM_data_layer_add_named(bm, &bm->vdata, CD_PROP_INT32, "__dyntopo_vert_node"); - BM_data_layer_add_named(bm, &bm->pdata, CD_PROP_INT32, "__dyntopo_face_node"); - BM_data_layer_add_named(bm, &bm->pdata, CD_PROP_INT32, ".sculpt_face_set"); - BM_data_layer_add_named(bm, &bm->pdata, CD_PROP_INT32, ".sculpt_boundary_flags"); - - BM_data_layer_add(bm, &bm->vdata, CD_PAINT_MASK); - BM_data_layer_add(bm, &bm->vdata, CD_DYNTOPO_VERT); - BM_data_layer_add(bm, &bm->vdata, CD_PROP_COLOR); - - BMIdMap *idmap = BM_idmap_new(bm, BM_VERT | BM_EDGE | BM_FACE); - - for (int side = 0; side < 6; side++) { - int axis = side >= 3 ? side - 3 : side; - float sign = side >= 3 ? -1.0f : 1.0f; - - printf("AXIS: %d\n", axis); - - float u = 0.0f; - - for (int i = 0; i < steps; i++, u += df) { - float v = 0.0f; - - for (int j = 0; j < steps; j++, v += df) { - float co[3]; - - co[axis] = u; - co[(axis + 1) % 3] = v; - co[(axis + 2) % 3] = sign; - - // turn into sphere - normalize_v3(co); - - void *key = hashco(co[0], co[1], co[2], hashdimen); - -#if 0 - printf("%.3f %.3f %.3f, key: %p i: %d j: %d df: %f, u: %f v: %f\n", - co[0], - co[1], - co[2], - key, - i, - j, - df, - u, - v); -#endif - - void **val = nullptr; - - if (!BLI_ghash_ensure_p(vhash, key, &val)) { - BMVert *v2 = BM_vert_create(bm, co, nullptr, BM_CREATE_NOP); - - *val = (void *)v2; - } - - BMVert *v2 = (BMVert *)*val; - int idx = j * steps + i; - - grid[idx] = v2; - } - } - - for (int i = 0; i < steps - 1; i++) { - for (int j = 0; j < steps - 1; j++) { - int idx1 = j * steps + i; - int idx2 = (j + 1) * steps + i; - int idx3 = (j + 1) * steps + i + 1; - int idx4 = j * steps + i + 1; - - BMVert *v1 = grid[idx1]; - BMVert *v2 = grid[idx2]; - BMVert *v3 = grid[idx3]; - BMVert *v4 = grid[idx4]; - - if (v1 == v2 || v1 == v3 || v1 == v4 || v2 == v3 || v2 == v4 || v3 == v4) { - printf("ERROR!\n"); - continue; - } - - if (sign < 0) { - BMVert *vs[4] = {v4, v3, v2, v1}; - BM_face_create_verts(bm, vs, 4, nullptr, BM_CREATE_NOP, true); - } - else { - BMVert *vs[4] = {v1, v2, v3, v4}; - BM_face_create_verts(bm, vs, 4, nullptr, BM_CREATE_NOP, true); - } - } - } - } - - // randomize - uint *rands[4]; - uint tots[4] = {(uint)bm->totvert, (uint)bm->totedge, (uint)bm->totloop, (uint)bm->totface}; - - RNG *rng = BLI_rng_new(0); - - for (uint i = 0; i < 4; i++) { - rands[i] = MEM_cnew_array(tots[i], "rands[i]"); - - for (uint j = 0; j < tots[i]; j++) { - rands[i][j] = j; - } - - for (uint j = 0; j < tots[i] >> 1; j++) { - int j2 = BLI_rng_get_int(rng) % tots[i]; - SWAP(uint, rands[i][j], rands[i][j2]); - } - } - - BM_mesh_remap(bm, rands[0], rands[1], rands[2], rands[3]); - - for (int i = 0; i < 4; i++) { - MEM_SAFE_FREE(rands[i]); - } - - BLI_rng_free(rng); - BLI_ghash_free(vhash, nullptr, nullptr); - MEM_SAFE_FREE(grid); - - printf("totvert: %d, totface: %d, tottri: %d\n", bm->totvert, bm->totface, bm->totface * 2); - - int cd_vert_node = CustomData_get_named_layer_index( - &bm->vdata, CD_PROP_INT32, "__dyntopo_vert_node"); - int cd_face_node = CustomData_get_named_layer_index( - &bm->pdata, CD_PROP_INT32, "__dyntopo_face_node"); - int cd_face_area = CustomData_get_named_layer_index( - &bm->pdata, CD_PROP_FLOAT2, "__dyntopo_face_areas"); - int cd_boundary_flag = CustomData_get_named_layer_index( - &bm->vdata, CD_PROP_INT32, ".sculpt_boundary_flags"); - - cd_vert_node = bm->vdata.layers[cd_vert_node].offset; - cd_boundary_flag = bm->vdata.layers[cd_boundary_flag].offset; - cd_face_node = bm->pdata.layers[cd_face_node].offset; - cd_face_area = bm->pdata.layers[cd_face_area].offset; - - const int cd_sculpt_vert = CustomData_get_offset(&bm->vdata, CD_DYNTOPO_VERT); - BMLog *bmlog = BM_log_create(bm, idmap); - - PBVH *pbvh = BKE_pbvh_new(PBVH_BMESH); - - bm->elem_table_dirty |= BM_VERT | BM_EDGE | BM_FACE; - bm->elem_index_dirty |= BM_VERT | BM_EDGE | BM_FACE; - BM_mesh_elem_table_ensure(bm, BM_VERT | BM_FACE); - BM_mesh_elem_index_ensure(bm, BM_VERT | BM_EDGE | BM_FACE); - - BKE_pbvh_build_bmesh(pbvh, - nullptr, - bm, - false, - bmlog, - idmap, - cd_vert_node, - cd_face_node, - cd_sculpt_vert, - cd_face_area, - cd_boundary_flag, - false, - true); - - int loop_size = sizeof(BMLoop) - sizeof(void *) * 4; - - size_t s1 = 0, s2 = 0, s3 = 0; - s1 = sizeof(BMVert) * (size_t)bm->totvert + sizeof(BMEdge) * (size_t)bm->totedge + - sizeof(BMLoop) * (size_t)bm->totloop + sizeof(BMFace) * (size_t)bm->totface; - s2 = sizeof(MeshVert2) * (size_t)bm->totvert + sizeof(MeshEdge2) * (size_t)bm->totedge + - sizeof(MeshLoop2) * (size_t)bm->totloop + sizeof(MeshFace2) * (size_t)bm->totface; - s3 = (size_t)loop_size * (size_t)bm->totvert + sizeof(BMEdge) * (size_t)bm->totedge + - sizeof(BMLoop) * (size_t)bm->totloop + sizeof(BMFace) * (size_t)bm->totface; - - double times[4]; - const char *names[4]; - - int cd_overhead = 0; - CustomData *cdatas[4] = {&bm->vdata, &bm->edata, &bm->ldata, &bm->pdata}; - int ctots[4] = {bm->totvert, bm->totedge, bm->totloop, bm->totface}; - for (int i = 0; i < 4; i++) { - cd_overhead += cdatas[i]->totsize * ctots[i]; - } - - s1 += cd_overhead; - s2 += cd_overhead; - - printf(" bmesh mem size: %.2fmb %.2fmb\n", - (float)s1 / 1024.0f / 1024.0f, - (float)s3 / 1024.0f / 1024.0f); - printf("meshtest2 mem size: %.2fmb\n", (float)s2 / 1024.0f / 1024.0f); - - printf("= BMesh random order\n"); - times[0] = pbvh_bmesh_smooth_test(bm, pbvh); - names[0] = "random order"; - - BMesh *bm2 = BKE_pbvh_reorder_bmesh(pbvh); - - printf("= BMesh vertex cluster order\n"); - - bm2->elem_table_dirty |= BM_VERT | BM_EDGE | BM_FACE; - bm2->elem_index_dirty |= BM_VERT | BM_EDGE | BM_FACE; - BM_mesh_elem_table_ensure(bm2, BM_VERT | BM_FACE); - BM_mesh_elem_index_ensure(bm2, BM_VERT | BM_EDGE | BM_FACE); - - times[1] = pbvh_bmesh_smooth_test(bm2, pbvh); - names[1] = "vertex cluser"; - - printf("= Pure data-oriented (struct of arrays)\n"); - MeshTest *m = meshtest_from_bm(bm2); - - times[2] = pbvh_meshtest_smooth_test(m, pbvh); - names[2] = "data-oriented"; - - free_meshtest(m); - - printf("= Object-oriented but with integer indices instead of pointers\n"); - MeshTest2 *m2 = meshtest2_from_bm(bm2); - - times[3] = pbvh_meshtest2_smooth_test(m2, pbvh); - names[3] = "integer indices"; - - free_meshtest2(m2); - - if (bm2 && bm2 != bm) { - BM_mesh_free(bm2); - } - - if (r_bm) { - *r_bm = bm; - } - else { - BM_mesh_free(bm); - } - - if (r_pbvh_out) { - *r_pbvh_out = pbvh; - } - else { - BKE_pbvh_free(pbvh); - } - - printf("\n== Times ==\n"); - - for (int i = 0; i < ARRAY_SIZE(times); i++) { - if (i > 0) { - double perc = (times[0] / times[i] - 1.0) * 100.0; - printf(" %s : %.2f (%.2f%% improvement)\n", names[i], times[i], perc); - } - else { - printf(" %s : %.2f\n", names[i], times[i]); - } - } - - printf("== Test Finished ==\n"); -} - -#include "BLI_smallhash.h" - -static void hash_test() -{ - const int count = 1024 * 1024 * 4; - - int *data = MEM_cnew_array(count, "test data"); - - TableGSet *gs = BLI_table_gset_new("test"); - GHash *gh = BLI_ghash_ptr_new("test"); - SmallHash sh; - - BLI_smallhash_init(&sh); - RNG *rng = BLI_rng_new(0); - - for (int i = 0; i < count; i++) { - data[i] = i; - } - - printf("== creation: table_gset ==\n"); - double t = PIL_check_seconds_timer(); - - for (int i = 0; i < count; i++) { - int ri = BLI_rng_get_int(rng) % count; - int *ptr = (int *)POINTER_FROM_INT(data[ri]); - - BLI_table_gset_add(gs, ptr); - } - printf(" %.3f\n", PIL_check_seconds_timer() - t); - - printf("== creation: ghash ==\n"); - t = PIL_check_seconds_timer(); - - for (int i = 0; i < count; i++) { - int ri = BLI_rng_get_int(rng) % count; - int *ptr = (int *)POINTER_FROM_INT(data[ri]); - - BLI_ghash_insert(gh, ptr, POINTER_FROM_INT(i)); - } - printf(" %.3f\n", PIL_check_seconds_timer() - t); - - printf("== creation: small hash ==\n"); - t = PIL_check_seconds_timer(); - - for (int i = 0; i < count; i++) { - int ri = BLI_rng_get_int(rng) % count; - int *ptr = (int *)POINTER_FROM_INT(data[ri]); - - BLI_smallhash_insert(&sh, (uintptr_t)ptr, POINTER_FROM_INT(i)); - } - printf(" %.3f\n", PIL_check_seconds_timer() - t); - - printf("== lookup: g hash ==\n"); - t = PIL_check_seconds_timer(); - - for (int i = 0; i < count; i++) { - int ri = BLI_rng_get_int(rng) % count; - int *ptr = (int *)POINTER_FROM_INT(data[ri]); - - BLI_ghash_lookup(gh, ptr); - } - printf(" %.3f\n", PIL_check_seconds_timer() - t); - - printf("== lookup: small hash ==\n"); - t = PIL_check_seconds_timer(); - - for (int i = 0; i < count; i++) { - int ri = BLI_rng_get_int(rng) % count; - int *ptr = (int *)POINTER_FROM_INT(data[ri]); - - BLI_smallhash_lookup(&sh, (uintptr_t)ptr); - } - printf(" %.3f\n", PIL_check_seconds_timer() - t); - - BLI_rng_free(rng); - BLI_ghash_free(gh, nullptr, nullptr); - BLI_smallhash_release(&sh); - BLI_table_gset_free(gs, nullptr); - - MEM_freeN(data); -} - -void pbvh_bmesh_do_cache_test() -{ - for (int i = 0; i < 15; i++) { - printf("\n\n====== %d of %d =====\n", i + 1, 15); - hash_test(); - } - // pbvh_bmesh_cache_test_default_params(¶ms); - // pbvh_bmesh_cache_test(¶ms, &bm, &pbvh); -} - /* saves all bmesh references to internal indices, to be restored later */ void BKE_pbvh_bmesh_save_indices(PBVH *pbvh) { diff --git a/source/blender/blenkernel/intern/pbvh_intern.hh b/source/blender/blenkernel/intern/pbvh_intern.hh index f13108b1138..b0ab2caec16 100644 --- a/source/blender/blenkernel/intern/pbvh_intern.hh +++ b/source/blender/blenkernel/intern/pbvh_intern.hh @@ -233,6 +233,9 @@ struct PBVH { struct BMIdMap *bm_idmap; int cd_sculpt_vert; + int cd_flag; + int cd_valence; + int cd_origco, cd_origno; int cd_vert_node_offset; int cd_face_node_offset; int cd_vert_mask_offset; @@ -348,7 +351,8 @@ bool ray_face_intersection_depth_tri(const float ray_start[3], /* pbvh_bmesh.cc */ /* pbvh_bmesh.c */ -bool pbvh_bmesh_node_raycast(PBVH *pbvh, +bool pbvh_bmesh_node_raycast(SculptSession *ss, + PBVH *pbvh, PBVHNode *node, const float ray_start[3], const float ray_normal[3], @@ -362,7 +366,8 @@ bool pbvh_bmesh_node_raycast(PBVH *pbvh, float *r_face_normal, int stroke_id); -bool pbvh_bmesh_node_nearest_to_ray(PBVH *pbvh, +bool pbvh_bmesh_node_nearest_to_ray(SculptSession *ss, + PBVH *pbvh, PBVHNode *node, const float ray_start[3], const float ray_normal[3], @@ -441,6 +446,8 @@ BLI_INLINE bool pbvh_check_vert_boundary(PBVH *pbvh, struct BMVert *v) pbvh->cd_face_node_offset, pbvh->cd_vcol_offset, pbvh->cd_boundary_flag, + pbvh->cd_flag, + pbvh->cd_valence, v, pbvh->boundary_symmetry, &pbvh->header.bm->ldata, diff --git a/source/blender/editors/sculpt_paint/sculpt.cc b/source/blender/editors/sculpt_paint/sculpt.cc index 7fc7be886d7..b13d172f4ac 100644 --- a/source/blender/editors/sculpt_paint/sculpt.cc +++ b/source/blender/editors/sculpt_paint/sculpt.cc @@ -1850,24 +1850,23 @@ void SCULPT_orig_vert_data_init(SculptOrigVertData *data, /** * DEPRECATED use Update a #SculptOrigVertData for a particular vertex from the PBVH iterator. */ -void SCULPT_orig_vert_data_update(SculptOrigVertData *orig_data, PBVHVertRef vertex) +void SCULPT_orig_vert_data_update(SculptSession *ss, + SculptOrigVertData *orig_data, + PBVHVertRef vertex) { - // check if we need to update original data for current stroke - MSculptVert *mv = SCULPT_vertex_get_sculptvert(orig_data->ss, vertex); + float *co = nullptr, *no = nullptr, *color = nullptr, *mask = nullptr; - SCULPT_vertex_check_origdata(orig_data->ss, vertex); + get_original_vertex(ss, vertex, &co, &no, &color, &mask); if (orig_data->datatype == SCULPT_UNDO_COORDS) { - float *no = mv->origno; - + orig_data->co = co; orig_data->no = no; - orig_data->co = mv->origco; } else if (orig_data->datatype == SCULPT_UNDO_COLOR) { - orig_data->col = mv->origcolor; + orig_data->col = color; } else if (orig_data->datatype == SCULPT_UNDO_MASK) { - orig_data->mask = (float)mv->origmask / 65535.0f; + orig_data->mask = *mask; } } @@ -5778,7 +5777,8 @@ static void sculpt_raycast_cb(PBVHNode *node, void *data_v, float *tmin) float back_depth = 0.0f; int hit_count = 0; - if (BKE_pbvh_node_raycast(srd->ss->pbvh, + if (BKE_pbvh_node_raycast(srd->ss, + srd->ss->pbvh, node, origco, use_origco, @@ -5818,7 +5818,8 @@ static void sculpt_find_nearest_to_ray_cb(PBVHNode *node, void *data_v, float *t } } - if (BKE_pbvh_node_find_nearest_to_ray(srd->ss->pbvh, + if (BKE_pbvh_node_find_nearest_to_ray(srd->ss, + srd->ss->pbvh, node, origco, use_origco, @@ -7058,12 +7059,12 @@ void SCULPT_automasking_node_begin(Object *ob, } } -void SCULPT_automasking_node_update(SculptSession * /*ss*/, +void SCULPT_automasking_node_update(SculptSession * ss, AutomaskingNodeData *automask_data, PBVHVertexIter *vd) { if (automask_data->have_orig_data) { - SCULPT_orig_vert_data_update(&automask_data->orig_data, vd->vertex); + SCULPT_orig_vert_data_update(ss, &automask_data->orig_data, vd->vertex); } } diff --git a/source/blender/editors/sculpt_paint/sculpt_api.cc b/source/blender/editors/sculpt_paint/sculpt_api.cc index b04740451f1..04fe6a8dfbf 100644 --- a/source/blender/editors/sculpt_paint/sculpt_api.cc +++ b/source/blender/editors/sculpt_paint/sculpt_api.cc @@ -413,6 +413,8 @@ eSculptCorner SCULPT_vertex_is_corner(const SculptSession *ss, ss->cd_face_node_offset, ss->cd_vcol_offset, ss->attrs.boundary_flags->bmesh_cd_offset, + ss->attrs.flags->bmesh_cd_offset, + ss->attrs.valence->bmesh_cd_offset, (BMVert *)vertex.i, ss->boundary_symmetry, &ss->bm->ldata, @@ -516,16 +518,16 @@ eSculptBoundary SCULPT_vertex_is_boundary(const SculptSession *ss, bool SCULPT_vertex_check_origdata(SculptSession *ss, PBVHVertRef vertex) { - return BKE_pbvh_get_origvert(ss->pbvh, vertex, nullptr, nullptr, nullptr); + return blender::bke::paint::get_original_vertex(ss, vertex, nullptr, nullptr, nullptr, nullptr); } int SCULPT_vertex_valence_get(const struct SculptSession *ss, PBVHVertRef vertex) { SculptVertexNeighborIter ni; - MSculptVert *mv = SCULPT_vertex_get_sculptvert(ss, vertex); + uint8_t flag = vertex_attr_get(vertex, ss->attrs.flags); - if (mv->flag & SCULPTVERT_NEED_VALENCE) { - mv->flag &= ~SCULPTVERT_NEED_VALENCE; + if (flag & SCULPTVERT_NEED_VALENCE) { + vertex_attr_set(vertex, ss->attrs.flags, flag & ~SCULPTVERT_NEED_VALENCE); int tot = 0; @@ -534,25 +536,11 @@ int SCULPT_vertex_valence_get(const struct SculptSession *ss, PBVHVertRef vertex } SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); - mv->valence = tot; + vertex_attr_set(vertex, ss->attrs.valence, tot); + return tot; } -#if 0 - else { - int tot = 0; - - SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vertex, ni) { - tot++; - } - SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); - - if (tot != mv->valence) { - printf("%s: error: valence error!\n", __func__); - } - } -#endif - - return mv->valence; + return vertex_attr_get(vertex, ss->attrs.valence); } /* See SCULPT_stroke_id_test. */ diff --git a/source/blender/editors/sculpt_paint/sculpt_automasking.cc b/source/blender/editors/sculpt_paint/sculpt_automasking.cc index 8c42b862e5e..2ccc876cea4 100644 --- a/source/blender/editors/sculpt_paint/sculpt_automasking.cc +++ b/source/blender/editors/sculpt_paint/sculpt_automasking.cc @@ -264,7 +264,7 @@ static float automasking_view_occlusion_factor(AutomaskingCache *automasking, { char f = vertex_attr_get(vertex, ss->attrs.automasking_occlusion); - if (force || SCULPT_stroke_id_test(ss, vertex, STROKEID_USER_OCCLUSION)) { + if (force || blender::bke::sculpt::stroke_id_test(ss, vertex, STROKEID_USER_OCCLUSION)) { f = *vertex_attr_ptr( vertex, ss->attrs.automasking_occlusion) = SCULPT_vertex_is_occluded(ss, vertex, true) ? 2 : 1; @@ -280,7 +280,7 @@ static float automasking_factor_end(SculptSession *ss, float value) { if (ss->attrs.stroke_id) { - SCULPT_stroke_id_test(ss, vertex, STROKEID_USER_AUTOMASKING); + blender::bke::sculpt::stroke_id_test(ss, vertex, STROKEID_USER_AUTOMASKING); } return value; @@ -494,7 +494,7 @@ static float sculpt_automasking_cavity_factor(AutomaskingCache *automasking, SculptSession *ss, PBVHVertRef vertex) { - if (SCULPT_stroke_id_test_no_update(ss, vertex, STROKEID_USER_AUTOMASKING)) { + if (blender::bke::sculpt::stroke_id_test_no_update(ss, vertex, STROKEID_USER_AUTOMASKING)) { sculpt_calc_blurred_cavity(ss, automasking, automasking->settings.cavity_blur_steps, vertex); } @@ -795,7 +795,7 @@ static void sculpt_normal_occlusion_automasking_fill(AutomaskingCache *automaski } if (ss->attrs.stroke_id) { - SCULPT_stroke_id_test(ss, vertex, STROKEID_USER_AUTOMASKING); + blender::bke::sculpt::stroke_id_test(ss, vertex, STROKEID_USER_AUTOMASKING); } vertex_attr_set(vertex, ss->attrs.automasking_factor, f); diff --git a/source/blender/editors/sculpt_paint/sculpt_boundary.cc b/source/blender/editors/sculpt_paint/sculpt_boundary.cc index 28b776b6a57..a26d3bee76f 100644 --- a/source/blender/editors/sculpt_paint/sculpt_boundary.cc +++ b/source/blender/editors/sculpt_paint/sculpt_boundary.cc @@ -1470,7 +1470,7 @@ static void do_boundary_brush_circle_task_cb_ex(void *__restrict userdata, continue; } - SCULPT_orig_vert_data_update(&orig_data, vd.vertex); + SCULPT_orig_vert_data_update(ss, &orig_data, vd.vertex); if (!SCULPT_check_vertex_pivot_symmetry( orig_data.co, boundary->initial_vertex_position, symm)) { continue; @@ -1619,7 +1619,7 @@ static void do_boundary_brush_bend_task_cb_ex(void *__restrict userdata, } SCULPT_automasking_node_update(ss, &automask_data, &vd); - SCULPT_orig_vert_data_update(&orig_data, vd.vertex); + SCULPT_orig_vert_data_update(ss, &orig_data, vd.vertex); if (!SCULPT_check_vertex_pivot_symmetry( orig_data.co, boundary->initial_vertex_position, symm)) { continue; @@ -1672,7 +1672,7 @@ static void do_boundary_brush_slide_task_cb_ex(void *__restrict userdata, } SCULPT_automasking_node_update(ss, &automask_data, &vd); - SCULPT_orig_vert_data_update(&orig_data, vd.vertex); + SCULPT_orig_vert_data_update(ss, &orig_data, vd.vertex); if (!SCULPT_check_vertex_pivot_symmetry( orig_data.co, boundary->initial_vertex_position, symm)) { continue; @@ -1722,7 +1722,7 @@ static void do_boundary_brush_inflate_task_cb_ex(void *__restrict userdata, } SCULPT_automasking_node_update(ss, &automask_data, &vd); - SCULPT_orig_vert_data_update(&orig_data, vd.vertex); + SCULPT_orig_vert_data_update(ss, &orig_data, vd.vertex); if (!SCULPT_check_vertex_pivot_symmetry( orig_data.co, boundary->initial_vertex_position, symm)) { continue; @@ -1773,7 +1773,7 @@ static void do_boundary_brush_grab_task_cb_ex(void *__restrict userdata, } SCULPT_automasking_node_update(ss, &automask_data, &vd); - SCULPT_orig_vert_data_update(&orig_data, vd.vertex); + SCULPT_orig_vert_data_update(ss, &orig_data, vd.vertex); if (!SCULPT_check_vertex_pivot_symmetry( orig_data.co, boundary->initial_vertex_position, symm)) { continue; @@ -1828,7 +1828,7 @@ static void do_boundary_brush_twist_task_cb_ex(void *__restrict userdata, } SCULPT_automasking_node_update(ss, &automask_data, &vd); - SCULPT_orig_vert_data_update(&orig_data, vd.vertex); + SCULPT_orig_vert_data_update(ss, &orig_data, vd.vertex); if (!SCULPT_check_vertex_pivot_symmetry( orig_data.co, boundary->initial_vertex_position, symm)) { continue; @@ -1874,7 +1874,7 @@ static void do_boundary_brush_smooth_task_cb_ex(void *__restrict userdata, continue; } - SCULPT_orig_vert_data_update(&orig_data, vd.vertex); + SCULPT_orig_vert_data_update(ss, &orig_data, vd.vertex); if (!SCULPT_check_vertex_pivot_symmetry( orig_data.co, boundary->initial_vertex_position, symm)) { continue; @@ -2125,7 +2125,8 @@ void SCULPT_do_boundary_brush(Sculpt *sd, Object *ob, Span nodes) 0, nodes.size(), &data, do_boundary_brush_smooth_task_cb_ex, &settings); break; case BRUSH_BOUNDARY_DEFORM_CIRCLE: - BLI_task_parallel_range(0, nodes.size(), &data, do_boundary_brush_circle_task_cb_ex, &settings); + BLI_task_parallel_range( + 0, nodes.size(), &data, do_boundary_brush_circle_task_cb_ex, &settings); break; } diff --git a/source/blender/editors/sculpt_paint/sculpt_brush_types.cc b/source/blender/editors/sculpt_paint/sculpt_brush_types.cc index a4e08a9ff98..23817c26a25 100644 --- a/source/blender/editors/sculpt_paint/sculpt_brush_types.cc +++ b/source/blender/editors/sculpt_paint/sculpt_brush_types.cc @@ -1367,7 +1367,7 @@ static void do_thumb_brush_task_cb_ex(void *__restrict userdata, data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]); BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { - SCULPT_orig_vert_data_update(&orig_data, vd.vertex); + SCULPT_orig_vert_data_update(ss, &orig_data, vd.vertex); if (!sculpt_brush_test_sq_fn(&test, orig_data.co)) { continue; @@ -1446,7 +1446,7 @@ static void do_rotate_brush_task_cb_ex(void *__restrict userdata, data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]); BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { - SCULPT_orig_vert_data_update(&orig_data, vd.vertex); + SCULPT_orig_vert_data_update(ss, &orig_data, vd.vertex); if (!sculpt_brush_test_sq_fn(&test, orig_data.co)) { continue; @@ -1525,7 +1525,7 @@ static void do_layer_brush_task_cb_ex(void *__restrict userdata, data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]); BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { - SCULPT_orig_vert_data_update(&orig_data, vd.vertex); + SCULPT_orig_vert_data_update(ss, &orig_data, vd.vertex); if (!sculpt_brush_test_sq_fn(&test, orig_data.co)) { continue; @@ -1551,7 +1551,7 @@ static void do_layer_brush_task_cb_ex(void *__restrict userdata, else { disp_factor = vertex_attr_ptr(vd.vertex, ss->attrs.layer_displayment); - if (SCULPT_stroke_id_test(ss, vd.vertex, STROKEID_USER_LAYER_BRUSH)) { + if (blender::bke::sculpt::stroke_id_test(ss, vd.vertex, STROKEID_USER_LAYER_BRUSH)) { *disp_factor = 0.0f; } } @@ -2057,7 +2057,7 @@ static void do_grab_brush_task_cb_ex(void *__restrict userdata, data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]); BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { - SCULPT_orig_vert_data_update(&orig_data, vd.vertex); + SCULPT_orig_vert_data_update(ss, &orig_data, vd.vertex); if (!sculpt_brush_test_sq_fn(&test, orig_data.co)) { continue; @@ -2163,7 +2163,7 @@ static void do_elastic_deform_brush_task_cb_ex(void *__restrict userdata, ¶ms, ss->cache->radius, force, 1.0f, brush->elastic_deform_volume_preservation); BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { - SCULPT_orig_vert_data_update(&orig_data, vd.vertex); + SCULPT_orig_vert_data_update(ss, &orig_data, vd.vertex); SCULPT_automasking_node_update(ss, &automask_data, &vd); float final_disp[3]; @@ -2266,7 +2266,7 @@ static void do_draw_sharp_brush_task_cb_ex(void *__restrict userdata, data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]); BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { - SCULPT_orig_vert_data_update(&orig_data, vd.vertex); + SCULPT_orig_vert_data_update(ss, &orig_data, vd.vertex); if (!sculpt_brush_test_sq_fn(&test, orig_data.co)) { continue; } @@ -2356,7 +2356,7 @@ static void do_topology_slide_task_cb_ex(void *__restrict userdata, data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]); BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { - SCULPT_orig_vert_data_update(&orig_data, vd.vertex); + SCULPT_orig_vert_data_update(ss, &orig_data, vd.vertex); if (!sculpt_brush_test_sq_fn(&test, orig_data.co)) { continue; } @@ -2530,7 +2530,7 @@ static void do_topology_relax_task_cb_ex(void *__restrict userdata, data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]); BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { - SCULPT_orig_vert_data_update(&orig_data, vd.vertex); + SCULPT_orig_vert_data_update(ss, &orig_data, vd.vertex); if (!sculpt_brush_test_sq_fn(&test, orig_data.co)) { continue; } @@ -2940,6 +2940,10 @@ void SCULPT_bmesh_topology_rake(Sculpt *sd, Object *ob, Span nodes, BKE_pbvh_update_all_tri_areas(ss->pbvh); } + if (brush->flag2 & BRUSH_CURVATURE_RAKE) { + BKE_sculpt_ensure_curvature_dir(ob); + } + for (iteration = 0; iteration <= count; iteration++) { SculptThreadedTaskData data{}; diff --git a/source/blender/editors/sculpt_paint/sculpt_curvature.cc b/source/blender/editors/sculpt_paint/sculpt_curvature.cc index f15113ca1ae..fca02fc84a2 100644 --- a/source/blender/editors/sculpt_paint/sculpt_curvature.cc +++ b/source/blender/editors/sculpt_paint/sculpt_curvature.cc @@ -110,6 +110,8 @@ #include #include +using namespace blender::bke::paint; + /* If you're working with uniform triangle tesselations, the math for calculating principle curvatures reduces to doing an eigen decomposition @@ -257,10 +259,7 @@ void SCULPT_curvature_dir_get(SculptSession *ss, return; } - BMVert *bv = (BMVert *)v.i; - MSculptVert *mv = BKE_PBVH_SCULPTVERT(ss->cd_sculpt_vert, bv); - - copy_v3_v3(dir, mv->curvature_dir); + copy_v3_v3(dir, vertex_attr_ptr(v, ss->attrs.curvature_dir)); } void SCULPT_curvature_begin(SculptSession *ss, struct PBVHNode *node, bool useAccurateSolver) @@ -282,7 +281,7 @@ void SCULPT_curvature_begin(SculptSession *ss, struct PBVHNode *node, bool useAc SculptCurvatureData curv; SCULPT_calc_principle_curvatures(ss, vi.vertex, &curv, useAccurateSolver); - copy_v3_v3(mv->curvature_dir, curv.principle[0]); + copy_v3_v3(vertex_attr_ptr(vi.vertex, ss->attrs.curvature_dir), curv.principle[0]); } BKE_pbvh_vertex_iter_end; } diff --git a/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc b/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc index 76c09d1bc22..eaa6eec18c7 100644 --- a/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc +++ b/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc @@ -529,7 +529,7 @@ void SCULPT_dynamic_topology_enable_ex(Main *bmain, if (ss->pbvh) { BKE_sculptsession_update_attr_refs(ob); - BKE_pbvh_update_sculpt_verts(ss->pbvh); + SCULPT_load_all_original(ob); } if (ss->pbvh && SCULPT_has_persistent_base(ss)) { @@ -552,7 +552,7 @@ void SCULPT_dynamic_topology_enable_ex(Main *bmain, } if (ss->pbvh) { - BKE_pbvh_update_sculpt_verts(ss->pbvh); + SCULPT_load_all_original(ob); } /* Make sure the data for existing faces are initialized. */ diff --git a/source/blender/editors/sculpt_paint/sculpt_filter_color.cc b/source/blender/editors/sculpt_paint/sculpt_filter_color.cc index eaa09a30f3c..08a190d76af 100644 --- a/source/blender/editors/sculpt_paint/sculpt_filter_color.cc +++ b/source/blender/editors/sculpt_paint/sculpt_filter_color.cc @@ -87,7 +87,7 @@ static void color_filter_task_cb(void *__restrict userdata, PBVHVertexIter vd; BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { - SCULPT_orig_vert_data_update(&orig_data, vd.vertex); + SCULPT_orig_vert_data_update(ss, &orig_data, vd.vertex); SCULPT_automasking_node_update(ss, &automask_data, &vd); float orig_color[3], final_color[4], hsv_color[3]; diff --git a/source/blender/editors/sculpt_paint/sculpt_filter_mask.cc b/source/blender/editors/sculpt_paint/sculpt_filter_mask.cc index df669b357e1..79f251e20f1 100644 --- a/source/blender/editors/sculpt_paint/sculpt_filter_mask.cc +++ b/source/blender/editors/sculpt_paint/sculpt_filter_mask.cc @@ -812,7 +812,7 @@ static void ipmask_filter_apply_from_original_task_cb(void *__restrict userdata, 0.5f) { continue; } - SCULPT_orig_vert_data_update(&orig_data, vd.vertex); + SCULPT_orig_vert_data_update(ss, &orig_data, vd.vertex); float new_mask = orig_data.mask; switch (filter_type) { case IPMASK_FILTER_ADD_SUBSTRACT: @@ -889,7 +889,7 @@ static void ipmask_filter_restore_original_mask_task_cb( SCULPT_orig_vert_data_init(&orig_data, data->ob, node, SCULPT_UNDO_COORDS); PBVHVertexIter vd; BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) { - SCULPT_orig_vert_data_update(&orig_data, vd.vertex); + SCULPT_orig_vert_data_update(ss, &orig_data, vd.vertex); *vd.mask = orig_data.mask; update = true; if (vd.is_mesh) { diff --git a/source/blender/editors/sculpt_paint/sculpt_filter_mesh.cc b/source/blender/editors/sculpt_paint/sculpt_filter_mesh.cc index e52835f6e0c..1e9c8743705 100644 --- a/source/blender/editors/sculpt_paint/sculpt_filter_mesh.cc +++ b/source/blender/editors/sculpt_paint/sculpt_filter_mesh.cc @@ -387,7 +387,7 @@ static void mesh_filter_task_cb(void *__restrict userdata, PBVHVertexIter vd; BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) { - SCULPT_orig_vert_data_update(&orig_data, vd.vertex); + SCULPT_orig_vert_data_update(ss, &orig_data, vd.vertex); SCULPT_automasking_node_update(ss, &automask_data, &vd); float orig_co[3], val[3], avg[3], disp[3], disp2[3], transform[3][3], final_pos[3]; @@ -890,7 +890,7 @@ static void sculpt_mesh_filter_cancel(bContext *C, wmOperator * /*op*/) SCULPT_orig_vert_data_init(&orig_data, ob, node, SCULPT_UNDO_COORDS); BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) { - SCULPT_orig_vert_data_update(&orig_data, vd.vertex); + SCULPT_orig_vert_data_update(ss, &orig_data, vd.vertex); copy_v3_v3(vd.co, orig_data.co); } diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.hh b/source/blender/editors/sculpt_paint/sculpt_intern.hh index 575c1a6921f..d7e53d14610 100644 --- a/source/blender/editors/sculpt_paint/sculpt_intern.hh +++ b/source/blender/editors/sculpt_paint/sculpt_intern.hh @@ -17,6 +17,7 @@ #include "BKE_attribute.h" #include "BKE_paint.h" #include "BKE_pbvh.h" +#include "BKE_sculpt.hh" #include "BLI_bitmap.h" #include "BLI_compiler_attrs.h" @@ -1400,7 +1401,9 @@ void SCULPT_orig_vert_data_init(SculptOrigVertData *data, * DEPRECATED: use SCULPT_vertex_check_origdata and SCULPT_vertex_get_sculptvert * Update a #SculptOrigVertData for a particular vertex from the PBVH iterator. */ -void SCULPT_orig_vert_data_update(SculptOrigVertData *orig_data, PBVHVertRef vertex); +void SCULPT_orig_vert_data_update(SculptSession *ss, + SculptOrigVertData *orig_data, + PBVHVertRef vertex); /** * DEPRECATED: use SCULPT_vertex_check_origdata and SCULPT_vertex_get_sculptvert @@ -2330,6 +2333,8 @@ float SCULPT_calc_concavity(SculptSession *ss, PBVHVertRef vref); If useAccurateSolver is false, a faster but less accurate power solver will be used. If true then BLI_eigen_solve_selfadjoint_m3 will be called. + +Must call BKE_sculpt_ensure_curvature_dir to ensure ss->attrs.curvature_dir exists. */ bool SCULPT_calc_principle_curvatures(SculptSession *ss, PBVHVertRef vertex, @@ -2446,60 +2451,6 @@ int SCULPT_vertex_island_get(SculptSession *ss, PBVHVertRef vertex); /** \} */ -/* - * Stroke ID API. This API is used to detect if - * an element has already been processed for some task - * inside a given stroke. - */ - -struct StrokeID { - short id; - short userflag; -}; - -enum StrokeIDUser { - STROKEID_USER_AUTOMASKING = 1 << 0, - STROKEID_USER_BOUNDARY = 1 << 1, - STROKEID_USER_SCULPTVERT = 1 << 2, - STROKEID_USER_PREV_COLOR = 1 << 3, - STROKEID_USER_SMOOTH = 1 << 4, - STROKEID_USER_OCCLUSION = 1 << 5, - STROKEID_USER_LAYER_BRUSH = 1 << 6, -}; -ENUM_OPERATORS(StrokeIDUser, STROKEID_USER_LAYER_BRUSH); - -BLI_INLINE bool SCULPT_stroke_id_test(SculptSession *ss, PBVHVertRef vertex, StrokeIDUser user) -{ - StrokeID *id = blender::bke::paint::vertex_attr_ptr(vertex, ss->attrs.stroke_id); - bool ret; - - if (id->id != ss->stroke_id) { - id->id = ss->stroke_id; - id->userflag = 0; - ret = true; - } - else { - ret = !(id->userflag & (int)user); - } - - id->userflag |= (int)user; - - return ret; -} - -BLI_INLINE bool SCULPT_stroke_id_test_no_update(SculptSession *ss, - PBVHVertRef vertex, - StrokeIDUser user) -{ - StrokeID *id = blender::bke::paint::vertex_attr_ptr(vertex, ss->attrs.stroke_id); - - if (id->id != ss->stroke_id) { - return true; - } - - return !(id->userflag & (int)user); -} - int SCULPT_get_symmetry_pass(const struct SculptSession *ss); #define SCULPT_boundary_flag_update BKE_sculpt_boundary_flag_update diff --git a/source/blender/editors/sculpt_paint/sculpt_paint_color.cc b/source/blender/editors/sculpt_paint/sculpt_paint_color.cc index 3ca48b20e36..8d80746b384 100644 --- a/source/blender/editors/sculpt_paint/sculpt_paint_color.cc +++ b/source/blender/editors/sculpt_paint/sculpt_paint_color.cc @@ -151,7 +151,7 @@ static void do_paint_brush_task_cb_ex(void *__restrict userdata, // valid state. float *color_buffer = vertex_attr_ptr(vd.vertex, buffer_scl); // mv->origcolor; - if (SCULPT_stroke_id_test(ss, vd.vertex, STROKEID_USER_PREV_COLOR)) { + if (blender::bke::sculpt::stroke_id_test(ss, vd.vertex, STROKEID_USER_PREV_COLOR)) { zero_v4(color_buffer); } diff --git a/source/blender/editors/sculpt_paint/sculpt_pose.cc b/source/blender/editors/sculpt_paint/sculpt_pose.cc index 492be7cb528..fb66eeb3257 100644 --- a/source/blender/editors/sculpt_paint/sculpt_pose.cc +++ b/source/blender/editors/sculpt_paint/sculpt_pose.cc @@ -155,7 +155,7 @@ static void do_pose_brush_task_cb_ex(void *__restrict userdata, data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]); BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { - SCULPT_orig_vert_data_update(&orig_data, vd.vertex); + SCULPT_orig_vert_data_update(ss, &orig_data, vd.vertex); SCULPT_automasking_node_update(ss, &automask_data, &vd); float total_disp[3]; diff --git a/source/blender/editors/sculpt_paint/sculpt_smooth.cc b/source/blender/editors/sculpt_paint/sculpt_smooth.cc index b2a3d15c92e..e931749e810 100644 --- a/source/blender/editors/sculpt_paint/sculpt_smooth.cc +++ b/source/blender/editors/sculpt_paint/sculpt_smooth.cc @@ -785,7 +785,7 @@ static void SCULPT_do_surface_smooth_brush_laplacian_task_cb_ex( bool weighted = brush->flag2 & BRUSH_SMOOTH_USE_AREA_WEIGHT; BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { - SCULPT_orig_vert_data_update(&orig_data, vd.vertex); + SCULPT_orig_vert_data_update(ss, &orig_data, vd.vertex); if (!sculpt_brush_test_sq_fn(&test, vd.co)) { continue; } diff --git a/source/blender/editors/sculpt_paint/sculpt_transform.cc b/source/blender/editors/sculpt_paint/sculpt_transform.cc index bceb48753d3..b99210e15b5 100644 --- a/source/blender/editors/sculpt_paint/sculpt_transform.cc +++ b/source/blender/editors/sculpt_paint/sculpt_transform.cc @@ -169,7 +169,7 @@ static void sculpt_transform_task_cb(void *__restrict userdata, SCULPT_undo_push_node(data->ob, node, SCULPT_UNDO_COORDS); BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) { - SCULPT_orig_vert_data_update(&orig_data, vd.vertex); + SCULPT_orig_vert_data_update(ss, &orig_data, vd.vertex); float transformed_co[3], orig_co[3], disp[3]; float *start_co; @@ -258,7 +258,7 @@ static void sculpt_elastic_transform_task_cb(void *__restrict userdata, PBVHVertexIter vd; BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) { - SCULPT_orig_vert_data_update(&orig_data, vd.vertex); + SCULPT_orig_vert_data_update(ss, &orig_data, vd.vertex); float transformed_co[3], orig_co[3], disp[3]; const float fade = vd.mask ? *vd.mask : 0.0f; copy_v3_v3(orig_co, orig_data.co); diff --git a/source/blender/editors/sculpt_paint/sculpt_undo.cc b/source/blender/editors/sculpt_paint/sculpt_undo.cc index eda7503df60..099713f4401 100644 --- a/source/blender/editors/sculpt_paint/sculpt_undo.cc +++ b/source/blender/editors/sculpt_paint/sculpt_undo.cc @@ -1052,7 +1052,7 @@ static void sculpt_undo_bmesh_enable(Object *ob, SculptUndoNode *unode, bool is_ BKE_sculptsession_update_attr_refs(ob); if (ss->pbvh) { - BKE_pbvh_update_sculpt_verts(ss->pbvh); + SCULPT_load_all_original(ob); } if (ss->bm_idmap) { -- 2.30.2 From 3c40e6c5d9939f6bf62dd1e2168db23063ddec08 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Tue, 2 May 2023 15:43:05 -0700 Subject: [PATCH 073/279] temp-sculpt-dyntopo: Continue splitting up MSculptVert Mostly done, just need to remove MSculptVert struct itself --- source/blender/blenkernel/BKE_paint.h | 14 +- source/blender/blenkernel/BKE_pbvh.h | 24 +- source/blender/blenkernel/BKE_sculpt.hh | 3 + .../blender/blenkernel/intern/customdata.cc | 17 +- source/blender/blenkernel/intern/dyntopo.cc | 37 +-- .../blenkernel/intern/dyntopo_intern.hh | 15 - source/blender/blenkernel/intern/paint.cc | 286 ++++++++++-------- source/blender/blenkernel/intern/pbvh.cc | 108 ++++--- .../blender/blenkernel/intern/pbvh_bmesh.cc | 237 +++------------ .../blender/blenkernel/intern/pbvh_intern.hh | 13 + source/blender/bmesh/intern/bmesh_log.cc | 21 +- source/blender/bmesh/intern/bmesh_mesh.cc | 40 ++- source/blender/draw/DRW_pbvh.hh | 3 +- source/blender/draw/intern/draw_pbvh.cc | 20 +- .../editors/sculpt_paint/paint_mask.cc | 7 +- source/blender/editors/sculpt_paint/sculpt.cc | 101 ++----- .../editors/sculpt_paint/sculpt_api.cc | 71 +++-- .../editors/sculpt_paint/sculpt_curvature.cc | 1 - .../editors/sculpt_paint/sculpt_detail.cc | 9 +- .../editors/sculpt_paint/sculpt_dyntopo.cc | 13 +- .../editors/sculpt_paint/sculpt_face_set.cc | 13 +- .../sculpt_paint/sculpt_paint_color.cc | 6 +- .../editors/sculpt_paint/sculpt_smooth.cc | 59 ++-- .../editors/sculpt_paint/sculpt_undo.cc | 93 +++--- 24 files changed, 524 insertions(+), 687 deletions(-) diff --git a/source/blender/blenkernel/BKE_paint.h b/source/blender/blenkernel/BKE_paint.h index 0f4b9b35198..3996cfbceaa 100644 --- a/source/blender/blenkernel/BKE_paint.h +++ b/source/blender/blenkernel/BKE_paint.h @@ -687,6 +687,8 @@ typedef struct SculptAttribute { /* Convenience pointers for standard sculpt attributes. */ typedef struct SculptAttributePointers { + SculptAttribute *face_set; + /* Persistent base. */ SculptAttribute *persistent_co; SculptAttribute *persistent_no; @@ -725,7 +727,7 @@ typedef struct SculptAttributePointers { SculptAttribute *orig_color; /* CD_PROP_FLOAT4, vert */ SculptAttribute *orig_mask; /* CD_PROP_FLOAT vert */ - SculptAttribute *curvature_dir; /* Curvature direction vectors, CD_PROP_FLOAT3 */ + SculptAttribute *curvature_dir; /* Curvature direction vectors, CD_PROP_FLOAT3 */ SculptAttribute *smear_previous; SculptAttribute *hide_poly; @@ -1053,7 +1055,7 @@ void BKE_sculpt_ensure_origcolor(struct Object *ob); void BKE_sculpt_ensure_origfset(struct Object *ob); void BKE_sculpt_ensure_curvature_dir(struct Object *ob); - /* Ensures Sculpt_flags and sculpt_valence layers. */ +/* Ensures Sculpt_flags and sculpt_valence layers. */ void BKE_sculpt_ensure_sculpt_layers(struct Object *ob); /* Ensure an attribute layer exists. */ @@ -1082,7 +1084,10 @@ void BKE_sculpt_attribute_destroy_temporary_all(struct Object *ob); /* Destroy attributes that were marked as stroke only in SculptAttributeParams. */ void BKE_sculpt_attributes_destroy_temporary_stroke(struct Object *ob); -bool BKE_sculptsession_check_sculptverts(struct Object *ob, struct PBVH *pbvh, int totvert); +bool BKE_sculptsession_check_sculptverts(struct Object *ob, + struct PBVH *pbvh, + int totvert, + bool reset_flags); struct BMesh *BKE_sculptsession_empty_bmesh_create(void); void BKE_sculptsession_bmesh_attr_update_internal(struct Object *ob); @@ -1292,10 +1297,11 @@ static void face_attr_set(const PBVHFaceRef face, const SculptAttribute *attr, T bool get_original_vertex(SculptSession *ss, PBVHVertRef vertex, - const float **r_co, + float **r_co, float **r_no, float **r_color, float **r_mask); +void load_all_original(Object *ob); } // namespace blender::bke::paint BLI_INLINE void BKE_sculpt_boundary_flag_update(SculptSession *ss, PBVHVertRef vertex) diff --git a/source/blender/blenkernel/BKE_pbvh.h b/source/blender/blenkernel/BKE_pbvh.h index 454a04bdfb5..aab6c09adab 100644 --- a/source/blender/blenkernel/BKE_pbvh.h +++ b/source/blender/blenkernel/BKE_pbvh.h @@ -423,10 +423,6 @@ BMLog *BKE_pbvh_get_bm_log(PBVH *pbvh); */ void BKE_pbvh_update_sculpt_verts(PBVH *pbvh); -/** update original data, only data whose r_** parameters are passed in will be updated*/ -bool BKE_pbvh_get_origvert( - PBVH *pbvh, PBVHVertRef vertex, const float **r_co, float **r_no, float **r_color); - /** checks if original data needs to be updated for v, and if so updates it. Stroke_id is provided by the sculpt code and is used to detect updates. The reason we do it @@ -462,7 +458,7 @@ void BKE_pbvh_raycast(PBVH *pbvh, bool original, int stroke_id); -bool BKE_pbvh_node_raycast(SculptSession *ss, +bool BKE_pbvh_node_raycast(struct SculptSession *ss, PBVH *pbvh, PBVHNode *node, float (*origco)[3], @@ -499,7 +495,7 @@ void BKE_pbvh_find_nearest_to_ray(PBVH *pbvh, const float ray_normal[3], bool original); -bool BKE_pbvh_node_find_nearest_to_ray(SculptSession *ss, +bool BKE_pbvh_node_find_nearest_to_ray(struct SculptSession *ss, PBVH *pbvh, PBVHNode *node, float (*origco)[3], @@ -586,6 +582,7 @@ ENUM_OPERATORS(PBVHTopologyUpdateMode, PBVH_LocalCollapse); typedef float (*DyntopoMaskCB)(PBVHVertRef vertex, void *userdata); bool BKE_pbvh_bmesh_update_topology( + struct SculptSession *ss, PBVH *pbvh, PBVHTopologyUpdateMode mode, const float center[3], @@ -601,7 +598,8 @@ bool BKE_pbvh_bmesh_update_topology( bool disable_surface_relax, bool is_snake_hook); -bool BKE_pbvh_bmesh_update_topology_nodes(PBVH *pbvh, +bool BKE_pbvh_bmesh_update_topology_nodes(struct SculptSession *ss, + PBVH *pbvh, bool (*searchcb)(PBVHNode *node, void *data), void (*undopush)(PBVHNode *node, void *data), void *searchdata, @@ -1196,9 +1194,7 @@ typedef struct SculptLayerEntry { int BKE_pbvh_do_fset_symmetry(int fset, const int symflag, const float *co); bool BKE_pbvh_check_vert_boundary(PBVH *pbvh, struct BMVert *v); -void BKE_pbvh_update_vert_boundary_grids(PBVH *pbvh, - struct SubdivCCG *subdiv_ccg, - PBVHVertRef vertex); +void BKE_pbvh_update_vert_boundary_grids(PBVH *pbvh, PBVHVertRef vertex); #if 0 # include "DNA_meshdata_types.h" @@ -1327,6 +1323,9 @@ bool BKE_pbvh_show_orig_get(PBVH *pbvh); # include "BLI_math_vector.hh" namespace blender::bke::pbvh { +void set_flags_valence(PBVH *pbvh, uint8_t *flags, int *valence); +void set_original(PBVH *pbvh, Span origco, Span origno); + void update_vert_boundary_faces(int *boundary_flags, const int *face_sets, const bool *hide_poly, @@ -1336,11 +1335,12 @@ void update_vert_boundary_faces(int *boundary_flags, const int *corner_edges, blender::OffsetIndices polys, int totpoly, - MSculptVert *msculptverts, const MeshElemMap *pmap, PBVHVertRef vertex, const bool *sharp_edges, - const bool *seam_edges); + const bool *seam_edges, + uint8_t *flags, + int *valence); Vector search_gather(PBVH *pbvh, BKE_pbvh_SearchCallback scb, diff --git a/source/blender/blenkernel/BKE_sculpt.hh b/source/blender/blenkernel/BKE_sculpt.hh index 4a3b8fbc985..600323015c0 100644 --- a/source/blender/blenkernel/BKE_sculpt.hh +++ b/source/blender/blenkernel/BKE_sculpt.hh @@ -40,7 +40,10 @@ BLI_INLINE bool stroke_id_clear(SculptSession *ss, PBVHVertRef vertex, StrokeIDU { StrokeID *id = blender::bke::paint::vertex_attr_ptr(vertex, ss->attrs.stroke_id); + bool retval = id->userflag & user; id->userflag &= ~user; + + return retval; } BLI_INLINE bool stroke_id_test(SculptSession *ss, PBVHVertRef vertex, StrokeIDUser user) diff --git a/source/blender/blenkernel/intern/customdata.cc b/source/blender/blenkernel/intern/customdata.cc index ff3ba4fd91b..158e8600d71 100644 --- a/source/blender/blenkernel/intern/customdata.cc +++ b/source/blender/blenkernel/intern/customdata.cc @@ -72,9 +72,6 @@ using blender::Vector; #define CUSTOMDATA_GROW 5 #define BM_ASAN_PAD 32 -#if defined(__SANITIZE_ADDRESS__) || __has_feature(address_sanitizer) -# define CD_HAVE_ASAN -#endif /* ensure typemap size is ok */ BLI_STATIC_ASSERT(ARRAY_SIZE(((CustomData *)nullptr)->typemap) == CD_NUMTYPES, "size mismatch"); @@ -2858,7 +2855,7 @@ static void customData_update_offsets(CustomData *data) int size = (int)typeInfo->size; /* Float vectors get 4-byte alignment. */ - if (ELEM(layer->type, CD_PROP_COLOR, CD_PROP_FLOAT2, CD_PROP_FLOAT3)) { + if (ELEM(layer->type, CD_PROP_COLOR, CD_PROP_FLOAT2, CD_PROP_FLOAT3, CD_PROP_INT32_2D)) { alignment = max_ii(alignment, 4); } else if (size > 4) { @@ -2883,14 +2880,14 @@ static void customData_update_offsets(CustomData *data) size += 8 - (size & 7); } -#ifdef CD_HAVE_ASAN +#ifdef WITH_ASAN offset += BM_ASAN_PAD; #endif layer->offset = offset; offset += size; -#ifdef CD_HAVE_ASAN +#ifdef WITH_ASAN offset += BM_ASAN_PAD; #endif } @@ -2911,7 +2908,7 @@ static void customData_update_offsets(CustomData *data) continue; } -#ifdef CD_HAVE_ASAN +#ifdef WITH_ASAN offset += BM_ASAN_PAD; #endif @@ -2925,7 +2922,7 @@ static void customData_update_offsets(CustomData *data) layer->offset = offset; offset += size; -#ifdef CD_HAVE_ASAN +#ifdef WITH_ASAN offset += BM_ASAN_PAD; #endif } @@ -2942,7 +2939,7 @@ static void customData_update_offsets(CustomData *data) void CustomData_bmesh_asan_poison(const CustomData *data, void *block) { -#ifdef CD_HAVE_ASAN +#ifdef WITH_ASAN if (!block) { return; } @@ -2953,7 +2950,7 @@ void CustomData_bmesh_asan_poison(const CustomData *data, void *block) for (int i = 0; i < data->totlayer; i++) { CustomDataLayer *layer = data->layers + i; - const LayerTypeInfo *typeInfo = layerType_getInfo(layer->type); + const LayerTypeInfo *typeInfo = layerType_getInfo(eCustomDataType(layer->type)); BLI_asan_poison((ptr + layer->offset - BM_ASAN_PAD), BM_ASAN_PAD); BLI_asan_poison((ptr + layer->offset + typeInfo->size), BM_ASAN_PAD); diff --git a/source/blender/blenkernel/intern/dyntopo.cc b/source/blender/blenkernel/intern/dyntopo.cc index d797b34b3f3..95b69f9def0 100644 --- a/source/blender/blenkernel/intern/dyntopo.cc +++ b/source/blender/blenkernel/intern/dyntopo.cc @@ -114,7 +114,6 @@ BLI_INLINE void surface_smooth_v_safe(SculptSession *ss, PBVH *pbvh, BMVert *v, pbvh_check_vert_boundary(pbvh, v); } - const int cd_sculpt_vert = pbvh->cd_sculpt_vert; const int boundflag = BM_ELEM_CD_GET_INT(v, pbvh->cd_boundary_flag); const bool bound1 = boundflag & SCULPTVERT_SMOOTH_BOUNDARY; @@ -129,7 +128,7 @@ BLI_INLINE void surface_smooth_v_safe(SculptSession *ss, PBVH *pbvh, BMVert *v, do { BMVert *v2 = e->v1 == v ? e->v2 : e->v1; - PBVHVertRef vertex2 = { reinterpret_cast(v2)}; + PBVHVertRef vertex2 = {reinterpret_cast(v2)}; /* Note: we can't validate the boundary flags from with a thread * so they may not be up to date. @@ -195,7 +194,7 @@ BLI_INLINE void surface_smooth_v_safe(SculptSession *ss, PBVH *pbvh, BMVert *v, atomic_cas_float(&start_origco[1], y, ny); atomic_cas_float(&start_origco[2], z, nz); - PBVH_CHECK_NAN(start_origco[0]); + PBVH_CHECK_NAN(start_origco); PBVH_CHECK_NAN(v->co); // atomic_cas_int32(&mv1->stroke_id, stroke_id, pbvh->stroke_id); } @@ -1288,8 +1287,6 @@ static void unified_edge_queue_create(EdgeQueueContext *eq_ctx, BLI_task_parallel_range(0, count, (void *)tdata.data(), unified_edge_queue_task_cb, &settings); - const int cd_sculpt_vert = pbvh->cd_sculpt_vert; - for (int i = 0; i < count; i++) { EdgeQueueThreadData *td = &tdata[i]; BMEdge **edges = td->edges; @@ -2039,7 +2036,8 @@ float mask_cb_nop(PBVHVertRef /*vertex*/, void * /*userdata*/) } /* Collapse short edges, subdivide long edges */ -extern "C" bool BKE_pbvh_bmesh_update_topology(PBVH *pbvh, +extern "C" bool BKE_pbvh_bmesh_update_topology(SculptSession *ss, + PBVH *pbvh, PBVHTopologyUpdateMode mode, const float center[3], const float view_normal[3], @@ -2123,6 +2121,8 @@ extern "C" bool BKE_pbvh_bmesh_update_topology(PBVH *pbvh, EdgeQueueContext eq_ctx = {}; + eq_ctx.ss = ss; + eq_ctx.pool = nullptr; eq_ctx.bm = pbvh->header.bm; eq_ctx.mask_cb = mask_cb; @@ -3156,9 +3156,7 @@ DynTopoState *BKE_dyntopo_init(BMesh *bm, PBVH *existing_pbvh) {CD_DYNTOPO_VERT, nullptr, CD_FLAG_TEMPORARY | CD_FLAG_NOCOPY}, {CD_PROP_INT32, SCULPT_ATTRIBUTE_NAME(dyntopo_node_id_vertex), - CD_FLAG_TEMPORARY | CD_FLAG_NOCOPY}, - {CD_PROP_INT32, SCULPT_ATTRIBUTE_NAME(valence), CD_FLAG_TEMPORARY}, - {CD_PROP_INT8, SCULPT_ATTRIBUTE_NAME(flags), CD_FLAG_TEMPORARY}}; + CD_FLAG_TEMPORARY | CD_FLAG_NOCOPY}}; BMCustomLayerReq flayers[] = { {CD_PROP_FLOAT2, SCULPT_ATTRIBUTE_NAME(face_areas), CD_FLAG_TEMPORARY | CD_FLAG_NOCOPY}, @@ -3250,21 +3248,6 @@ void BKE_dyntopo_free(DynTopoState *ds) MEM_freeN(ds); } -/* -bool BKE_pbvh_bmesh_update_topology(PBVH *pbvh, - PBVHTopologyUpdateMode mode, - const float center[3], - const float view_normal[3], - float radius, - const bool use_frontface, - const bool use_projected, - int sym_axis, - bool updatePBVH, - DyntopoMaskCB mask_cb, - void *mask_cb_data, - int custom_max_steps, - bool disable_surface_relax) -*/ void BKE_dyntopo_remesh(DynTopoState *ds, DynRemeshParams *params, int steps, @@ -3298,7 +3281,8 @@ void BKE_dyntopo_remesh(DynTopoState *ds, /* subdivide once */ if (mode & PBVH_Subdivide) { - BKE_pbvh_bmesh_update_topology(ds->pbvh, + BKE_pbvh_bmesh_update_topology(nullptr, + ds->pbvh, PBVH_Subdivide, cent, view, @@ -3323,7 +3307,8 @@ void BKE_dyntopo_remesh(DynTopoState *ds, } } - BKE_pbvh_bmesh_update_topology(ds->pbvh, + BKE_pbvh_bmesh_update_topology(nullptr, + ds->pbvh, mode, cent, view, diff --git a/source/blender/blenkernel/intern/dyntopo_intern.hh b/source/blender/blenkernel/intern/dyntopo_intern.hh index 69730100a4e..e5c122cf429 100644 --- a/source/blender/blenkernel/intern/dyntopo_intern.hh +++ b/source/blender/blenkernel/intern/dyntopo_intern.hh @@ -311,21 +311,6 @@ BMFace *pbvh_bmesh_face_create(PBVH *pbvh, } // namespace blender::dyntopo extern "C" { -bool BKE_pbvh_bmesh_update_topology(PBVH *pbvh, - PBVHTopologyUpdateMode mode, - const float center[3], - const float view_normal[3], - float radius, - const bool use_frontface, - const bool use_projected, - int sym_axis, - bool updatePBVH, - DyntopoMaskCB mask_cb, - void *mask_cb_data, - int custom_max_steps, - bool disable_surface_relax, - bool is_snake_hook); - void BKE_pbvh_bmesh_remove_face(PBVH *pbvh, BMFace *f, bool log_face); void BKE_pbvh_bmesh_remove_edge(PBVH *pbvh, BMEdge *e, bool log_edge); void BKE_pbvh_bmesh_remove_vertex(PBVH *pbvh, BMVert *v, bool log_vert); diff --git a/source/blender/blenkernel/intern/paint.cc b/source/blender/blenkernel/intern/paint.cc index a1510de86f4..5dfa8edeb42 100644 --- a/source/blender/blenkernel/intern/paint.cc +++ b/source/blender/blenkernel/intern/paint.cc @@ -1457,7 +1457,7 @@ void BKE_sculptsession_free_vwpaint_data(SculptSession *ss) /** * Write out the sculpt dynamic-topology #BMesh to the #Mesh. */ -static void sculptsession_bm_to_me_update_data_only(Object *ob, bool reorder) +static void sculptsession_bm_to_me_update_data_only(Object *ob, bool /*reorder*/) { SculptSession *ss = ob->sculpt; @@ -1888,15 +1888,6 @@ static void sculpt_update_object( } } - /* Sculpt Face Sets. */ - if (use_face_sets) { - ss->face_sets = static_cast(CustomData_get_layer_named_for_write( - &me->pdata, CD_PROP_INT32, ".sculpt_face_set", me->totpoly)); - } - else { - ss->face_sets = nullptr; - } - ss->hide_poly = (bool *)CustomData_get_layer_named_for_write( &me->pdata, CD_PROP_BOOL, ".hide_poly", me->totpoly); @@ -1906,6 +1897,29 @@ static void sculpt_update_object( PBVH *pbvh = BKE_sculpt_object_pbvh_ensure(depsgraph, ob); sculpt_check_face_areas(ob, pbvh); + /* Sculpt Face Sets. */ + if (use_face_sets) { + int *face_sets = static_cast(CustomData_get_layer_named_for_write( + &me->pdata, CD_PROP_INT32, ".sculpt_face_set", me->totpoly)); + + if (face_sets) { + /* Load into sculpt attribute system. */ + ss->face_sets = BKE_sculpt_face_sets_ensure(ob); + } + else { + ss->face_sets = nullptr; + if (ss->pbvh && !ss->bm) { + BKE_pbvh_face_sets_set(ss->pbvh, nullptr); + } + } + } + else { + ss->face_sets = nullptr; + if (ss->pbvh && !ss->bm) { + BKE_pbvh_face_sets_set(ss->pbvh, nullptr); + } + } + sculpt_boundary_flags_ensure(ob, pbvh, BKE_sculptsession_vertex_count(ss)); BKE_pbvh_update_active_vcol(pbvh, me); @@ -1923,7 +1937,7 @@ static void sculpt_update_object( if (ss->subdiv_ccg) { BKE_pbvh_subdiv_ccg_set(ss->pbvh, ss->subdiv_ccg); } - BKE_pbvh_face_sets_set(ss->pbvh, ss->face_sets); + BKE_pbvh_update_hide_attributes_from_mesh(ss->pbvh); BKE_pbvh_face_sets_color_set(ss->pbvh, me->face_sets_color_seed, me->face_sets_color_default); @@ -2045,7 +2059,7 @@ static void sculpt_update_object( break; } - BKE_sculptsession_check_sculptverts(ob, pbvh, totvert); + BKE_sculptsession_check_sculptverts(ob, pbvh, totvert, false); if (ss->bm && me->key && ob->shapenr != ss->bm->shapenr) { KeyBlock *actkey = static_cast(BLI_findlink(&me->key->block, ss->bm->shapenr - 1)); @@ -2203,6 +2217,9 @@ static void sculpt_update_object( } if (ss->pbvh) { + blender::bke::pbvh::set_flags_valence(ss->pbvh, + static_cast(ss->attrs.flags->data), + static_cast(ss->attrs.valence->data)); BKE_pbvh_set_sculpt_verts(ss->pbvh, ss->msculptverts); } } @@ -2278,53 +2295,55 @@ void BKE_sculpt_update_object_for_edit( { /* Update from sculpt operators and undo, to update sculpt session * and PBVH after edits. */ - Scene *scene_eval = DEG_get_evaluated_scene(depsgraph); Object *ob_eval = DEG_get_evaluated_object(depsgraph, ob_orig); sculpt_update_object(depsgraph, ob_orig, ob_eval, need_pmap, is_paint_tool); } -int *BKE_sculpt_face_sets_ensure(Object *ob) +ATTR_NO_OPT int *BKE_sculpt_face_sets_ensure(Object *ob) { - Mesh *mesh = BKE_object_get_original_mesh(ob); SculptSession *ss = ob->sculpt; - if (ss && ss->bm) { - if (CustomData_get_named_layer_index(&ss->bm->pdata, CD_PROP_INT32, ".sculpt_face_set") != - -1) { - return nullptr; + if (!ss->attrs.face_set) { + Mesh *mesh = static_cast(ob->data); + SculptAttributeParams params = {}; + params.permanent = true; + + CustomData *cdata = ss->bm ? &ss->bm->pdata : &mesh->pdata; + bool clear = CustomData_get_named_layer_index( + cdata, CD_PROP_INT32, SCULPT_ATTRIBUTE_NAME(face_set)) == -1; + + ss->attrs.face_set = BKE_sculpt_attribute_ensure( + ob, ATTR_DOMAIN_FACE, CD_PROP_INT32, SCULPT_ATTRIBUTE_NAME(face_set), ¶ms); + + if (clear) { + if (ss->bm) { + BMFace *f; + BMIter iter; + int cd_faceset = ss->attrs.face_set->bmesh_cd_offset; + + BM_ITER_MESH (f, &iter, ss->bm, BM_FACES_OF_MESH) { + BM_ELEM_CD_SET_INT(f, cd_faceset, 1); + } + } + else { + int *face_sets = static_cast(ss->attrs.face_set->data); + + for (int i : IndexRange(ss->totfaces)) { + face_sets[i] = 1; + } + } + + Mesh *mesh = static_cast(ob->data); + mesh->face_sets_color_default = 1; } - - BM_data_layer_add_named(ss->bm, &ss->bm->pdata, CD_PROP_INT32, ".sculpt_face_set"); - - const int cd_faceset_offset = CustomData_get_offset_named( - &ss->bm->pdata, CD_PROP_INT32, ".sculpt_face_set"); - - BMIter iter; - BMFace *f; - BM_ITER_MESH (f, &iter, ss->bm, BM_FACES_OF_MESH) { - BM_ELEM_CD_SET_INT(f, cd_faceset_offset, 1); - } - - BKE_sculptsession_update_attr_refs(ob); - - return nullptr; } - using namespace blender; - using namespace blender::bke; - - MutableAttributeAccessor attributes = mesh->attributes_for_write(); - if (!attributes.contains(".sculpt_face_set")) { - SpanAttributeWriter face_sets = attributes.lookup_or_add_for_write_only_span( - ".sculpt_face_set", ATTR_DOMAIN_FACE); - face_sets.span.fill(1); - mesh->face_sets_color_default = 1; - face_sets.finish(); + if (ss->pbvh && !ss->bm) { + BKE_pbvh_face_sets_set(ss->pbvh, static_cast(ss->attrs.face_set->data)); } - return static_cast(CustomData_get_layer_named_for_write( - &mesh->pdata, CD_PROP_INT32, ".sculpt_face_set", mesh->totpoly)); + return static_cast(ss->attrs.face_set->data); } bool *BKE_sculpt_hide_poly_ensure(Object *ob) @@ -2531,9 +2550,8 @@ void BKE_sculpt_sync_face_visibility_to_grids(Mesh *mesh, SubdivCCG *subdiv_ccg) } } -static PBVH *build_pbvh_for_dynamic_topology(Object *ob, bool update_sculptverts) +static PBVH *build_pbvh_for_dynamic_topology(Object *ob, bool /*update_sculptverts*/) { - Mesh *me = BKE_object_get_original_mesh(ob); PBVH *pbvh = ob->sculpt->pbvh = BKE_pbvh_new(PBVH_BMESH); BKE_pbvh_set_bmesh(pbvh, ob->sculpt->bm); @@ -2583,7 +2601,7 @@ static PBVH *build_pbvh_from_regular_mesh(Object *ob, Mesh *me_eval_deform) PBVH *pbvh = ob->sculpt->pbvh = BKE_pbvh_new(PBVH_FACES); - BKE_sculptsession_check_sculptverts(ob, pbvh, me->totvert); + BKE_sculptsession_check_sculptverts(ob, pbvh, me->totvert, true); sculpt_check_face_areas(ob, pbvh); BKE_sculptsession_update_attr_refs(ob); BKE_pbvh_set_pmap(pbvh, ss->pmap); @@ -2610,7 +2628,6 @@ static PBVH *build_pbvh_from_ccg(Object *ob, SubdivCCG *subdiv_ccg) Mesh *base_mesh = BKE_mesh_from_object(ob); - int totgridfaces = base_mesh->totloop * (key.grid_size - 1) * (key.grid_size - 1); BKE_sculpt_sync_face_visibility_to_grids(base_mesh, subdiv_ccg); BKE_sculptsession_update_attr_refs(ob); @@ -2641,25 +2658,28 @@ static PBVH *build_pbvh_from_ccg(Object *ob, SubdivCCG *subdiv_ccg) BKE_pbvh_set_pmap(pbvh, ss->pmap); int totvert = BKE_pbvh_get_grid_num_verts(pbvh); - BKE_sculptsession_check_sculptverts(ob, pbvh, totvert); - - for (int i = 0; i < totvert; i++) { - ss->msculptverts[i].stroke_id = -1; - ss->msculptverts[i].flag |= SCULPTVERT_NEED_VALENCE; - } - - BKE_pbvh_set_sculpt_verts(pbvh, ss->msculptverts); + BKE_sculptsession_check_sculptverts(ob, pbvh, totvert, true); return pbvh; } -extern "C" bool BKE_sculptsession_check_sculptverts(Object *ob, struct PBVH *pbvh, int totvert) +extern "C" bool BKE_sculptsession_check_sculptverts(Object *ob, + struct PBVH *pbvh, + int totvert, + bool reset_flags) { SculptSession *ss = ob->sculpt; if (!ss->attrs.flags) { BKE_sculpt_ensure_sculpt_layers(ob); + reset_flags = true; + } + + BKE_sculpt_ensure_origco(ob); + sculpt_boundary_flags_ensure(ob, pbvh, totvert); + + if (reset_flags) { if (ss->bm) { int cd_flags = ss->attrs.flags->bmesh_cd_offset; BMVert *v; @@ -2681,10 +2701,6 @@ extern "C" bool BKE_sculptsession_check_sculptverts(Object *ob, struct PBVH *pbv } } - BKE_sculpt_ensure_origco(ob); - - sculpt_boundary_flags_ensure(ob, pbvh, totvert); - if (!ss->attrs.sculpt_vert || !ss->attrs.sculpt_vert->data) { SculptAttributeParams params = {0}; @@ -2695,6 +2711,10 @@ extern "C" bool BKE_sculptsession_check_sculptverts(Object *ob, struct PBVH *pbv ss->msculptverts = static_cast(ss->attrs.sculpt_vert->data); BKE_pbvh_set_sculpt_verts(pbvh, ss->msculptverts); + blender::bke::pbvh::set_flags_valence(ss->pbvh, + static_cast(ss->attrs.flags->data), + static_cast(ss->attrs.valence->data)); + return false; } @@ -2745,8 +2765,7 @@ PBVH *BKE_sculpt_object_pbvh_ensure(Depsgraph *depsgraph, Object *ob) ob->sculpt->pbvh = pbvh; } else { -#if 1 // def WHEN_GLOBAL_UNDO_WORKS - /*detect if we are loading from an undo memfile step*/ + /* Detect if we are loading from an undo memfile step. */ Mesh *mesh_orig = BKE_object_get_original_mesh(ob); bool is_dyntopo = (mesh_orig->flag & ME_SCULPT_DYNAMIC_TOPOLOGY); @@ -2785,20 +2804,6 @@ PBVH *BKE_sculpt_object_pbvh_ensure(Depsgraph *depsgraph, Object *ob) pbvh = build_pbvh_from_regular_mesh(ob, me_eval_deform); } } -#else - Object *object_eval = DEG_get_evaluated_object(depsgraph, ob); - Mesh *mesh_eval = static_cast(object_eval->data); - if (mesh_eval->runtime->subdiv_ccg != nullptr) { - pbvh = build_pbvh_from_ccg(ob, mesh_eval->runtime->subdiv_ccg); - } - else if (ob->type == OB_MESH) { - Mesh *me_eval_deform = object_eval->runtime.mesh_deform_eval; - - BKE_sculptsession_check_sculptverts(ob, me_eval_deform->totvert); - - pbvh = build_pbvh_from_regular_mesh(ob, me_eval_deform); - } -#endif } if (!ob->sculpt->pmap) { @@ -3156,14 +3161,14 @@ static int sculpt_attr_elem_count_get(Object *ob, eAttrDomain domain) } } -static bool sculpt_attribute_create(SculptSession *ss, - Object *ob, - eAttrDomain domain, - eCustomDataType proptype, - const char *name, - SculptAttribute *out, - const SculptAttributeParams *params, - PBVHType pbvhtype) +ATTR_NO_OPT static bool sculpt_attribute_create(SculptSession *ss, + Object *ob, + eAttrDomain domain, + eCustomDataType proptype, + const char *name, + SculptAttribute *out, + const SculptAttributeParams *params, + PBVHType pbvhtype) { Mesh *me = BKE_object_get_original_mesh(ob); @@ -3176,7 +3181,7 @@ static bool sculpt_attribute_create(SculptSession *ss, BLI_strncpy_utf8(out->name, name, sizeof(out->name)); /* Force non-CustomData simple_array mode if not PBVH_FACES. */ - if (pbvhtype == PBVH_GRIDS) { + if (pbvhtype == PBVH_GRIDS && domain == ATTR_DOMAIN_POINT) { if (permanent) { printf( "%s: error: tried to make permanent customdata in multires; will make " @@ -3236,7 +3241,6 @@ static bool sculpt_attribute_create(SculptSession *ss, if (!permanent && !ss->save_temp_layers) { cdata->layers[index].flag |= CD_FLAG_TEMPORARY | CD_FLAG_NOCOPY; - out->params.permanent = true; } out->data = nullptr; @@ -3245,6 +3249,7 @@ static bool sculpt_attribute_create(SculptSession *ss, out->elem_size = CustomData_sizeof(proptype); break; } + case PBVH_GRIDS: case PBVH_FACES: { CustomData *cdata = nullptr; @@ -3266,7 +3271,6 @@ static bool sculpt_attribute_create(SculptSession *ss, int index = CustomData_get_named_layer_index(cdata, proptype, name); if (!permanent && !ss->save_temp_layers) { - out->params.permanent = true; cdata->layers[index].flag |= CD_FLAG_TEMPORARY | CD_FLAG_NOCOPY; } @@ -3278,11 +3282,6 @@ static bool sculpt_attribute_create(SculptSession *ss, break; } - case PBVH_GRIDS: { - /* GRIDS should have been handled as simple arrays. */ - BLI_assert_unreachable(); - break; - } default: BLI_assert_unreachable(); break; @@ -3294,7 +3293,7 @@ static bool sculpt_attribute_create(SculptSession *ss, return true; } -static bool sculpt_attr_update(Object *ob, SculptAttribute *attr) +ATTR_NO_OPT static bool sculpt_attr_update(Object *ob, SculptAttribute *attr) { SculptSession *ss = ob->sculpt; int elem_num = sculpt_attr_elem_count_get(ob, attr->domain); @@ -3306,8 +3305,8 @@ static bool sculpt_attr_update(Object *ob, SculptAttribute *attr) } /* Check if we are a coerced simple array and shouldn't be. */ - bad |= attr->simple_array && !attr->params.simple_array && - !ELEM(BKE_pbvh_type(ss->pbvh), PBVH_GRIDS, PBVH_BMESH); + bad |= (attr->simple_array && !attr->params.simple_array) && + !(BKE_pbvh_type(ss->pbvh) == PBVH_GRIDS && attr->domain == ATTR_DOMAIN_POINT); CustomData *cdata = sculpt_get_cdata(ob, attr->domain); if (cdata && !attr->simple_array) { @@ -3346,6 +3345,10 @@ static bool sculpt_attr_update(Object *ob, SculptAttribute *attr) MEM_SAFE_FREE(attr->data); } + if (pbvhtype != PBVH_GRIDS && (attr->simple_array && !attr->params.simple_array)) { + attr->simple_array = false; + } + sculpt_attribute_create( ss, ob, attr->domain, attr->proptype, attr->name, attr, &attr->params, pbvhtype); } @@ -3420,16 +3423,29 @@ SculptAttribute *BKE_sculpt_attribute_get(struct Object *ob, return attr; } + if (BKE_pbvh_type(ss->pbvh) == PBVH_GRIDS && domain == ATTR_DOMAIN_POINT) { + /* Don't pull from customdata for PBVH_GRIDS and vertex domain. + * Multires vertex attributes don't go through CustomData. + */ + return nullptr; + } + /* Does attribute exist in CustomData layout? */ CustomData *cdata = sculpt_get_cdata(ob, domain); if (cdata) { int index = CustomData_get_named_layer_index(cdata, proptype, name); if (index != -1) { - int totelem = sculpt_attr_elem_count_get(ob, ATTR_DOMAIN_POINT); + int totelem = sculpt_attr_elem_count_get(ob, domain); attr = sculpt_alloc_attr(ss); + if (BKE_pbvh_type(ss->pbvh) == PBVH_FACES || + (BKE_pbvh_type(ss->pbvh) == PBVH_GRIDS && domain == ATTR_DOMAIN_FACE)) { + attr->data = cdata->layers[index].data; + } + + attr->params.permanent = !(cdata->layers[index].flag & CD_FLAG_TEMPORARY); attr->used = true; attr->domain = domain; attr->proptype = proptype; @@ -3438,6 +3454,7 @@ SculptAttribute *BKE_sculpt_attribute_get(struct Object *ob, attr->elem_num = totelem; attr->layer = cdata->layers + index; attr->elem_size = CustomData_sizeof(proptype); + attr->data_for_bmesh = ss->bm && attr->bmesh_cd_offset != -1; BLI_strncpy_utf8(attr->name, name, sizeof(attr->name)); return attr; @@ -3618,21 +3635,15 @@ void BKE_sculpt_attributes_destroy_temporary_stroke(Object *ob) */ if (!attr->params.simple_array && ss->bm) { - BMIter iter; - int size = CustomData_sizeof(attr->proptype); - /* Zero the attribute in an attempt to emulate the behavior of releasing it. */ - switch (attr->domain) { - case ATTR_DOMAIN_POINT: { - sculpt_clear_attribute_bmesh(ss->bm, attr); - break; - } - case ATTR_DOMAIN_EDGE: - sculpt_clear_attribute_bmesh(ss->bm, attr); - break; - case ATTR_DOMAIN_FACE: - sculpt_clear_attribute_bmesh(ss->bm, attr); - break; + if (attr->domain == ATTR_DOMAIN_POINT) { + sculpt_clear_attribute_bmesh(ss->bm, attr); + } + else if (attr->domain == ATTR_DOMAIN_EDGE) { + sculpt_clear_attribute_bmesh(ss->bm, attr); + } + else if (attr->domain == ATTR_DOMAIN_FACE) { + sculpt_clear_attribute_bmesh(ss->bm, attr); } continue; } @@ -3726,6 +3737,15 @@ static void sculpt_attribute_update_refs(Object *ob) if (ss->bm) { update_bmesh_offsets(me, ss); } + else if (ss->pbvh) { + if (ss->attrs.orig_co && ss->attrs.orig_no) { + const int verts_count = BKE_sculptsession_vertex_count(ss); + blender::bke::pbvh::set_original( + ob->sculpt->pbvh, + {static_cast(ss->attrs.orig_co->data), verts_count}, + {static_cast(ss->attrs.orig_no->data), verts_count}); + } + } } void BKE_sculptsession_update_attr_refs(Object *ob) @@ -3843,15 +3863,25 @@ bool BKE_sculpt_has_persistent_base(SculptSession *ss) void BKE_sculpt_ensure_origco(struct Object *ob) { + SculptSession *ss = ob->sculpt; SculptAttributeParams params = {}; - if (!ob->sculpt->attrs.orig_co) { - ob->sculpt->attrs.orig_co = BKE_sculpt_attribute_ensure( + + if (!ss->attrs.orig_co) { + ss->attrs.orig_co = BKE_sculpt_attribute_ensure( ob, ATTR_DOMAIN_POINT, CD_PROP_FLOAT3, SCULPT_ATTRIBUTE_NAME(orig_co), ¶ms); } - if (!ob->sculpt->attrs.orig_no) { - ob->sculpt->attrs.orig_no = BKE_sculpt_attribute_ensure( + if (!ss->attrs.orig_no) { + ss->attrs.orig_no = BKE_sculpt_attribute_ensure( ob, ATTR_DOMAIN_POINT, CD_PROP_FLOAT3, SCULPT_ATTRIBUTE_NAME(orig_no), ¶ms); } + + if (ss->pbvh) { + const int verts_count = BKE_sculptsession_vertex_count(ss); + blender::bke::pbvh::set_original( + ob->sculpt->pbvh, + {static_cast(ss->attrs.orig_co->data), verts_count}, + {static_cast(ss->attrs.orig_no->data), verts_count}); + } } void BKE_sculpt_ensure_curvature_dir(struct Object *ob) @@ -3898,20 +3928,29 @@ void BKE_sculpt_ensure_sculpt_layers(struct Object *ob) ob, ATTR_DOMAIN_POINT, CD_PROP_INT8, SCULPT_ATTRIBUTE_NAME(flags), ¶ms); } if (!ob->sculpt->attrs.valence) { - ob->sculpt->attrs.flags = BKE_sculpt_attribute_ensure( + ob->sculpt->attrs.valence = BKE_sculpt_attribute_ensure( ob, ATTR_DOMAIN_POINT, CD_PROP_INT32, SCULPT_ATTRIBUTE_NAME(valence), ¶ms); } + if (!ob->sculpt->attrs.stroke_id) { + ob->sculpt->attrs.stroke_id = BKE_sculpt_attribute_ensure( + ob, ATTR_DOMAIN_POINT, CD_PROP_INT32, SCULPT_ATTRIBUTE_NAME(stroke_id), ¶ms); + } + + if (ob->sculpt->pbvh) { + blender::bke::pbvh::set_flags_valence(ob->sculpt->pbvh, + static_cast(ob->sculpt->attrs.flags->data), + static_cast(ob->sculpt->attrs.valence->data)); + } } namespace blender::bke::paint { bool get_original_vertex(SculptSession *ss, PBVHVertRef vertex, - const float **r_co, + float **r_co, float **r_no, float **r_color, float **r_mask) { - uint8_t flags = vertex_attr_get(vertex, ss->attrs.flags); bool retval = false; if (sculpt::stroke_id_test(ss, vertex, STROKEID_USER_ORIGINAL)) { @@ -3919,11 +3958,12 @@ bool get_original_vertex(SculptSession *ss, const float *co = nullptr, *no = nullptr; switch (BKE_pbvh_type(ss->pbvh)) { - case PBVH_BMESH: + case PBVH_BMESH: { BMVert *v = reinterpret_cast(vertex.i); co = v->co; no = v->no; break; + } case PBVH_FACES: if (ss->shapekey_active || ss->deform_modifiers_active) { co = BKE_pbvh_get_vert_positions(ss->pbvh)[vertex.i]; @@ -4009,7 +4049,7 @@ bool get_original_vertex(SculptSession *ss, return retval; } -void SCULPT_load_all_original(Object *ob) +void load_all_original(Object *ob) { SculptSession *ss = ob->sculpt; @@ -4018,7 +4058,7 @@ void SCULPT_load_all_original(Object *ob) PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); blender::bke::sculpt::stroke_id_clear(ss, vertex, STROKEID_USER_ORIGINAL); - SCULPT_get_original(ss, vertex); + get_original_vertex(ss, vertex, nullptr, nullptr, nullptr, nullptr); } } diff --git a/source/blender/blenkernel/intern/pbvh.cc b/source/blender/blenkernel/intern/pbvh.cc index 2bf5faa639c..a174faf4eb8 100644 --- a/source/blender/blenkernel/intern/pbvh.cc +++ b/source/blender/blenkernel/intern/pbvh.cc @@ -40,6 +40,7 @@ #include "BKE_object.h" #include "BKE_paint.h" #include "BKE_pbvh.h" +#include "BKE_sculpt.hh" #include "BKE_subdiv_ccg.h" #include "DEG_depsgraph_query.h" @@ -196,16 +197,12 @@ static void update_node_vb(PBVH *pbvh, PBVHNode *node, int updateflag) } if (do_orig) { - MSculptVert *mv = pbvh->header.type == PBVH_BMESH ? (MSculptVert *)BM_ELEM_CD_GET_VOID_P( - vd.bm_vert, pbvh->cd_sculpt_vert) : - pbvh->msculptverts + vd.index; + const float *origco = pbvh->header.type == PBVH_BMESH ? + BM_ELEM_CD_PTR(vd.bm_vert, pbvh->cd_origco) : + reinterpret_cast(&pbvh->origco[vd.index]); - if (mv->stroke_id != pbvh->stroke_id) { - BB_expand(&orig_vb, vd.co); - } - else { - BB_expand(&orig_vb, mv->origco); - } + /* XXX check stroke id here and use v->co? */ + BB_expand(&orig_vb, origco); } } BKE_pbvh_vertex_iter_end; @@ -814,6 +811,8 @@ static void pbvh_draw_args_init(PBVH *pbvh, PBVH_GPU_Args *args, PBVHNode *node) args->mesh_verts_num = pbvh->totvert; args->mesh_grids_num = pbvh->totgrid; args->node = node; + args->origco = pbvh->origco; + args->origno = pbvh->origno; BKE_pbvh_node_num_verts(pbvh, node, nullptr, &args->node_verts_num); @@ -829,7 +828,6 @@ static void pbvh_draw_args_init(PBVH *pbvh, PBVH_GPU_Args *args, PBVHNode *node) args->mlooptri = pbvh->looptri; args->flat_vcol_shading = pbvh->flat_vcol_shading; args->updategen = node->updategen; - args->msculptverts = pbvh->msculptverts; if (ELEM(pbvh->header.type, PBVH_FACES, PBVH_GRIDS)) { args->hide_poly = (const bool *)(pbvh->pdata ? CustomData_get_layer_named( @@ -1060,13 +1058,6 @@ void BKE_pbvh_build_mesh(PBVH *pbvh, Mesh *mesh) prim_bbc = (BBC *)MEM_mallocN(sizeof(BBC) * looptri_num, "prim_bbc"); SculptPMap *pmap = pbvh->pmap; - MSculptVert *msculptverts = pbvh->msculptverts; - - for (int i = 0; i < mesh->totvert; i++) { - msculptverts[i].flag &= ~SCULPTVERT_NEED_VALENCE; - msculptverts[i].valence = pmap->pmap[i].count; - msculptverts[i].stroke_id = -1; - } for (int i = 0; i < looptri_num; i++) { const MLoopTri *lt = &looptri[i]; @@ -4313,19 +4304,13 @@ void BKE_pbvh_set_sculpt_verts(PBVH *pbvh, struct MSculptVert *msculptverts) pbvh->msculptverts = msculptverts; } -void BKE_pbvh_update_vert_boundary_grids(PBVH *pbvh, - struct SubdivCCG *subdiv_ccg, - PBVHVertRef vertex) +ATTR_NO_OPT void BKE_pbvh_update_vert_boundary_grids(PBVH *pbvh, PBVHVertRef vertex) { - MSculptVert *mv = pbvh->msculptverts + vertex.i; - - int *flags = pbvh->boundary_flags + vertex.i; - *flags = 0; - - /* TODO: finish this function. */ - + SubdivCCG *subdiv_ccg = pbvh->subdiv_ccg; int index = (int)vertex.i; + /* Update valence. */ + /* TODO: optimize this. We could fill #SculptVertexNeighborIter directly, * maybe provide coordinate and mask pointers directly rather than converting * back and forth between #CCGElem and global index. */ @@ -4342,36 +4327,71 @@ void BKE_pbvh_update_vert_boundary_grids(PBVH *pbvh, SubdivCCGNeighbors neighbors; BKE_subdiv_ccg_neighbor_coords_get(subdiv_ccg, &coord, false, &neighbors); - mv->valence = neighbors.size; - mv->flag &= ~SCULPTVERT_NEED_VALENCE; + pbvh->valence[vertex.i] = neighbors.size; + pbvh->sculpt_flags[vertex.i] &= ~SCULPTVERT_NEED_VALENCE; + + /* Update boundary */ + int *boundary_flag = pbvh->boundary_flags + vertex.i; + *boundary_flag = 0; + + int face = BKE_subdiv_ccg_grid_to_face_index(subdiv_ccg, grid_index); + int fset = pbvh->face_sets ? pbvh->face_sets[face] : 0; + + for (int i : IndexRange(neighbors.size)) { + int grid_index2 = neighbors.coords[i].grid_index; + + if (grid_index == grid_index2) { + continue; + } + + int face2 = BKE_subdiv_ccg_grid_to_face_index(subdiv_ccg, grid_index2); + int fset2 = pbvh->face_sets ? pbvh->face_sets[face2] : 0; + + if (fset2 != fset) { + *boundary_flag |= SCULPT_BOUNDARY_FACE_SET; + } + } } namespace blender::bke::pbvh { +void set_flags_valence(PBVH *pbvh, uint8_t *flags, int *valence) +{ + pbvh->sculpt_flags = flags; + pbvh->valence = valence; +} + +void set_original(PBVH *pbvh, Span origco, Span origno) +{ + pbvh->origco = origco; + pbvh->origno = origno; +} + void update_vert_boundary_faces(int *boundary_flags, const int *face_sets, const bool *hide_poly, const float (*vert_positions)[3], - const int2 *medge, + const int2 * /*medge*/, const int *corner_verts, const int *corner_edges, OffsetIndices polys, - int totpoly, - MSculptVert *msculptverts, + int /*totpoly*/, const MeshElemMap *pmap, PBVHVertRef vertex, const bool *sharp_edges, - const bool *seam_edges) + const bool *seam_edges, + uint8_t *flags, + int * /*valence*/) { - MSculptVert *mv = msculptverts + vertex.i; const MeshElemMap *vert_map = &pmap[vertex.i]; + uint8_t *flag = flags + vertex.i; - mv->flag &= ~SCULPTVERT_VERT_FSET_HIDDEN; + *flag &= ~SCULPTVERT_VERT_FSET_HIDDEN; int last_fset = -1; int last_fset2 = -1; - int *flags = boundary_flags + vertex.i; - *flags = 0; + int *boundary_flag = boundary_flags + vertex.i; + *boundary_flag = 0; int totsharp = 0, totseam = 0; int visible = false; @@ -4396,12 +4416,12 @@ void update_vert_boundary_faces(int *boundary_flags, int e_index = corner_edges[loopstart + j]; if (sharp_edges && sharp_edges[e_index]) { - *flags |= SCULPT_BOUNDARY_SHARP; + *boundary_flag |= SCULPT_BOUNDARY_SHARP; totsharp++; } if (seam_edges && seam_edges[e_index]) { - *flags |= SCULPT_BOUNDARY_SEAM; + *boundary_flag |= SCULPT_BOUNDARY_SEAM; totseam++; } } @@ -4413,11 +4433,11 @@ void update_vert_boundary_faces(int *boundary_flags, } if (i > 0 && fset != last_fset) { - *flags |= SCULPT_BOUNDARY_FACE_SET; + *boundary_flag |= SCULPT_BOUNDARY_FACE_SET; if (i > 1 && last_fset2 != last_fset && last_fset != -1 && last_fset2 != -1 && fset != -1 && last_fset2 != fset) { - *flags |= SCULPT_CORNER_FACE_SET; + *boundary_flag |= SCULPT_CORNER_FACE_SET; } } @@ -4429,15 +4449,15 @@ void update_vert_boundary_faces(int *boundary_flags, } if (!visible) { - mv->flag |= SCULPTVERT_VERT_FSET_HIDDEN; + *flag |= SCULPTVERT_VERT_FSET_HIDDEN; } if (totsharp > 2) { - *flags |= SCULPT_CORNER_SHARP; + *boundary_flag |= SCULPT_CORNER_SHARP; } if (totseam > 2) { - *flags |= SCULPT_CORNER_SEAM; + *boundary_flag |= SCULPT_CORNER_SEAM; } } } // namespace blender::bke::pbvh diff --git a/source/blender/blenkernel/intern/pbvh_bmesh.cc b/source/blender/blenkernel/intern/pbvh_bmesh.cc index 58259def545..f9213a7ae66 100644 --- a/source/blender/blenkernel/intern/pbvh_bmesh.cc +++ b/source/blender/blenkernel/intern/pbvh_bmesh.cc @@ -248,7 +248,7 @@ extern "C" void BKE_pbvh_bmesh_check_nodes(PBVH *pbvh) pbvh_bmesh_check_nodes(pbvh); } #else -extern "C" void BKE_pbvh_bmesh_check_nodes(PBVH *pbvh) {} +extern "C" void BKE_pbvh_bmesh_check_nodes(PBVH * /*pbvh*/) {} #endif /** \} */ @@ -283,7 +283,7 @@ static BMVert *pbvh_bmesh_vert_create(PBVH *pbvh, const float co[3], const float no[3], BMVert *v_example, - const int cd_vert_mask_offset) + const int /*cd_vert_mask_offset*/) { PBVHNode *node = &pbvh->nodes[node_index]; @@ -626,7 +626,6 @@ void pbvh_bmesh_vert_remove(PBVH *pbvh, BMVert *v) const int updateflag = PBVH_UpdateDrawBuffers | PBVH_UpdateBB | PBVH_UpdateTris | PBVH_UpdateNormals | PBVH_UpdateOtherVerts; - int ni = pbvh_bmesh_node_index_from_vert(pbvh, v); PBVHNode *v_node = pbvh_bmesh_node_from_vert(pbvh, v); if (v_node && v_node->bm_unique_verts) { @@ -1127,8 +1126,11 @@ void bke_pbvh_insert_face_finalize(PBVH *pbvh, BMFace *f, const int ni) node2->flag |= updateflag; + float *origco = pbvh->cd_origco != -1 ? BM_ELEM_CD_PTR(l->v, pbvh->cd_origco) : + l->v->co; + BB_expand(&node2->vb, l->v->co); - BB_expand(&node2->orig_vb, mv->origco); + BB_expand(&node2->orig_vb, origco); } l = l->next; } while (l != f->l_first); @@ -1358,7 +1360,7 @@ static void pbvh_bmesh_regen_node_verts(PBVH *pbvh, PBVHNode *node) BLI_table_gset_free(old_other_verts, nullptr); } -void BKE_pbvh_bmesh_mark_node_regen(PBVH *pbvh, PBVHNode *node) +void BKE_pbvh_bmesh_mark_node_regen(PBVH * /*pbvh*/, PBVHNode *node) { node->flag |= PBVH_RebuildNodeVerts; } @@ -1390,15 +1392,15 @@ void BKE_pbvh_bmesh_regen_node_verts(PBVH *pbvh) /************************* Called from pbvh.c *************************/ -static bool pbvh_poly_hidden(PBVH *pbvh, BMFace *f) +static bool pbvh_poly_hidden(PBVH * /*pbvh*/, BMFace *f) { return BM_elem_flag_test(f, BM_ELEM_HIDDEN); } -bool BKE_pbvh_bmesh_check_origdata(SculptSession *ss, BMVert *v, int stroke_id) +bool BKE_pbvh_bmesh_check_origdata(SculptSession *ss, BMVert *v, int /*stroke_id*/) { PBVHVertRef vertex = {(intptr_t)v}; - blender::bke::paint::get_original_vertex(ss, vertex, nullptr, nullptr, nullptr, nullptr); + return blender::bke::paint::get_original_vertex(ss, vertex, nullptr, nullptr, nullptr, nullptr); } bool pbvh_bmesh_node_raycast(SculptSession *ss, @@ -1419,8 +1421,6 @@ bool pbvh_bmesh_node_raycast(SculptSession *ss, bool hit = false; float nearest_vertex_co[3] = {0.0f}; - GSetIterator gs_iter; - BKE_pbvh_bmesh_check_tris(pbvh, node); for (int i = 0; i < node->tribuf->tottri; i++) { @@ -2077,25 +2077,11 @@ static int color_boundary_key(float col[4]) } #endif -/* calls atomic_cas_uint32 on two adjacent (and int aligned) shorts */ -BLI_INLINE void atomic_cas_short2(ushort *base, ushort olda, ushort oldb, ushort newa, ushort newb) -{ - uint oldi, newi; - - ((ushort *)&oldi)[0] = olda; - ((ushort *)&oldi)[1] = oldb; - - ((ushort *)&newi)[0] = newa; - ((ushort *)&newi)[1] = newb; - - atomic_cas_uint32((uint32_t *)base, oldi, newi); -} - -void bke_pbvh_update_vert_boundary(int cd_sculpt_vert, +void bke_pbvh_update_vert_boundary(int /*cd_sculpt_vert*/, int cd_faceset_offset, int cd_vert_node_offset, int cd_face_node_offset, - int cd_vcol, + int /*cd_vcol*/, int cd_boundary_flag, int cd_flag, int cd_valence, @@ -2105,8 +2091,6 @@ void bke_pbvh_update_vert_boundary(int cd_sculpt_vert, const int totuv) { int newflag = *BM_ELEM_CD_PTR(v, cd_flag); - int oldflag = newflag; - int oldval = BM_ELEM_CD_GET_INT(v, cd_valence); int boundflag = 0; BMEdge *e = v->e; @@ -2285,10 +2269,6 @@ void bke_pbvh_update_vert_boundary(int cd_sculpt_vert, // also check e->l->radial_next, in case we are not manifold // which can mess up the loop order if (e->l->radial_next != e->l) { - float th = saacos(dot_v3v3(e->l->f->no, e->l->radial_next->f->no)); - - th *= M_1_PI * 0.25f; - if (cd_faceset_offset != -1) { // fset = abs(BM_ELEM_CD_GET_INT(e->l->radial_next->f, cd_faceset_offset)); int fset2 = BKE_pbvh_do_fset_symmetry( @@ -2339,8 +2319,6 @@ void bke_pbvh_update_vert_boundary(int cd_sculpt_vert, boundflag |= SCULPT_CORNER_MESH; } - int oldboundflag = BM_ELEM_CD_GET_INT(v, cd_boundary_flag); - /* XXX: does this need to be threadsafe? */ BM_ELEM_CD_SET_INT(v, cd_boundary_flag, boundflag); *(BM_ELEM_CD_PTR(v, cd_flag)) = newflag; @@ -2435,11 +2413,16 @@ void BKE_pbvh_build_bmesh(PBVH *pbvh, pbvh->cd_vert_mask_offset = CustomData_get_offset(&bm->vdata, CD_PAINT_MASK); pbvh->cd_sculpt_vert = cd_sculpt_vert; pbvh->cd_boundary_flag = cd_boundary_flag; - pbvh->cd_flag = cd_flag_offset; - pbvh->cd_valence = cd_valence_offset; pbvh->cd_origco = cd_origco; pbvh->cd_origno = cd_origno; + pbvh->cd_flag = CustomData_get_offset_named( + &pbvh->header.bm->vdata, CD_PROP_INT8, ".sculpt_flags"); + pbvh->cd_valence = CustomData_get_offset_named( + &pbvh->header.bm->vdata, CD_PROP_INT8, ".sculpt_valence"); + pbvh->cd_boundary_flag = CustomData_get_offset_named( + &pbvh->header.bm->vdata, CD_PROP_INT8, ".sculpt_boundary_flags"); + pbvh->mesh = me; smooth_shading |= fast_draw; @@ -2506,14 +2489,6 @@ void BKE_pbvh_build_bmesh(PBVH *pbvh, bm->elem_index_dirty |= BM_FACE; BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) { - dyntopo_add_flag(pbvh, - v, - SCULPTVERT_NEED_VALENCE | SCULPTVERT_NEED_TRIANGULATE | - SCULPTVERT_NEED_DISK_SORT); - - int *flags = BM_ELEM_CD_PTR(v, pbvh->cd_boundary_flag); - *flags |= SCULPT_BOUNDARY_NEEDS_UPDATE; - BM_ELEM_CD_SET_INT(v, cd_vert_node_offset, DYNTOPO_NODE_NONE); } @@ -2617,7 +2592,8 @@ void BKE_pbvh_set_bm_log(PBVH *pbvh, BMLog *log) BM_log_set_idmap(log, pbvh->bm_idmap); } -bool BKE_pbvh_bmesh_update_topology_nodes(PBVH *pbvh, +bool BKE_pbvh_bmesh_update_topology_nodes(SculptSession *ss, + PBVH *pbvh, bool (*searchcb)(PBVHNode *node, void *data), void (*undopush)(PBVHNode *node, void *data), void *searchdata, @@ -2656,7 +2632,8 @@ bool BKE_pbvh_bmesh_update_topology_nodes(PBVH *pbvh, BKE_pbvh_node_mark_topology_update(node); } - modified = BKE_pbvh_bmesh_update_topology(pbvh, + modified = BKE_pbvh_bmesh_update_topology(ss, + pbvh, mode, center, view_normal, @@ -2702,7 +2679,7 @@ PBVHTriBuf *BKE_pbvh_bmesh_get_tris(PBVH *pbvh, PBVHNode *node) return node->tribuf; } -void BKE_pbvh_bmesh_free_tris(PBVH *pbvh, PBVHNode *node) +void BKE_pbvh_bmesh_free_tris(PBVH * /*pbvh*/, PBVHNode *node) { if (node->tribuf) { pbvh_free_tribuf(node->tribuf); @@ -3144,13 +3121,14 @@ void BKE_pbvh_bmesh_on_mesh_change(PBVH *pbvh) } } - const int cd_sculpt_vert = pbvh->cd_sculpt_vert; - BM_ITER_MESH (v, &iter, pbvh->header.bm, BM_VERTS_OF_MESH) { int *flags = BM_ELEM_CD_PTR(v, pbvh->cd_boundary_flag); *flags |= SCULPT_BOUNDARY_NEEDS_UPDATE; - dyntopo_add_flag(pbvh, v, SCULPTVERT_NEED_DISK_SORT | SCULPTVERT_NEED_TRIANGULATE | SCULPTVERT_NEED_VALENCE); + dyntopo_add_flag(pbvh, + v, + SCULPTVERT_NEED_DISK_SORT | SCULPTVERT_NEED_TRIANGULATE | + SCULPTVERT_NEED_VALENCE); BKE_pbvh_bmesh_update_valence(pbvh, BKE_pbvh_make_vref((intptr_t)v)); } } @@ -3258,7 +3236,7 @@ static void pbvh_bmesh_join_subnodes(PBVH *pbvh, PBVHNode *node, PBVHNode *paren TGSET_ITER_END } -static void BKE_pbvh_bmesh_correct_tree(PBVH *pbvh, PBVHNode *node, PBVHNode *parent) +static void BKE_pbvh_bmesh_correct_tree(PBVH *pbvh, PBVHNode *node, PBVHNode * /*parent*/) { const int size_lower = pbvh->leaf_limit - (pbvh->leaf_limit >> 1); @@ -4068,7 +4046,8 @@ void BKE_pbvh_update_offsets(PBVH *pbvh, const int cd_flag, const int cd_valence, const int cd_origco, - const int cd_origno) + const int cd_origno, + const int cd_curvature_dir) { pbvh->cd_face_node_offset = cd_face_node_offset; pbvh->cd_vert_node_offset = cd_vert_node_offset; @@ -4080,6 +4059,7 @@ void BKE_pbvh_update_offsets(PBVH *pbvh, pbvh->totuv = CustomData_number_of_layers(&pbvh->header.bm->ldata, CD_PROP_FLOAT2); pbvh->cd_boundary_flag = cd_boundary_flag; + pbvh->cd_curvature_dir = cd_curvature_dir; if (pbvh->bm_idmap) { BM_idmap_check_attributes(pbvh->bm_idmap); @@ -4091,153 +4071,6 @@ void BKE_pbvh_update_offsets(PBVH *pbvh, pbvh->cd_origno = cd_origno; } -static void scan_edge_split(BMesh *bm, BMEdge **edges, int totedge) -{ - Vector faces; - Vector newedges; - Vector newverts; - Vector fmap; - Vector emap; - - // remove e from radial list of e->v2 - for (int i = 0; i < totedge; i++) { - BMEdge *e = edges[i]; - - BMDiskLink *prev; - BMDiskLink *next; - - if (e->v2_disk_link.prev->v1 == e->v2) { - prev = &e->v2_disk_link.prev->v1_disk_link; - } - else { - prev = &e->v2_disk_link.prev->v2_disk_link; - } - - if (e->v2_disk_link.next->v1 == e->v2) { - next = &e->v2_disk_link.next->v1_disk_link; - } - else { - next = &e->v2_disk_link.next->v2_disk_link; - } - - prev->next = e->v2_disk_link.next; - next->prev = e->v2_disk_link.prev; - } - - for (int i = 0; i < totedge; i++) { - BMEdge *e = edges[i]; - - BMVert *v2 = static_cast(BLI_mempool_alloc(bm->vpool)); - memset(v2, 0, sizeof(*v2)); - v2->head.data = BLI_mempool_alloc(bm->vdata.pool); - - newverts.append(v2); - - BMEdge *e2 = static_cast(BLI_mempool_alloc(bm->epool)); - newedges.append(e2); - - memset(e2, 0, sizeof(*e2)); - if (bm->edata.pool) { - e2->head.data = BLI_mempool_alloc(bm->edata.pool); - } - - BMLoop *l = e->l; - - if (!l) { - continue; - } - - do { - faces.append(l->f); - BMFace *f2 = static_cast(BLI_mempool_alloc(bm->fpool)); - - faces.append(l->f); - fmap.append(v2); - emap.append(i); - - faces.append(f2); - fmap.append(v2); - emap.append(i); - - memset(f2, 0, sizeof(*f2)); - f2->head.data = BLI_mempool_alloc(bm->ldata.pool); - - BMLoop *prev = nullptr; - BMLoop *l2 = nullptr; - - for (int j = 0; j < 3; j++) { - l2 = (BMLoop *)BLI_mempool_alloc(bm->lpool); - memset(l2, 0, sizeof(*l2)); - l2->head.data = BLI_mempool_alloc(bm->ldata.pool); - - l2->prev = prev; - - if (prev) { - prev->next = l2; - } - else { - f2->l_first = l2; - } - } - - f2->l_first->prev = l2; - l2->next = f2->l_first; - - faces.append(f2); - l = l->radial_next; - } while (l != e->l); - } - - for (int i : IndexRange(newedges.size())) { - BMEdge *e1 = edges[i]; - BMEdge *e2 = newedges[i]; - BMVert *v = newverts[i]; - - add_v3_v3v3(v->co, e1->v1->co, e1->v2->co); - mul_v3_fl(v->co, 0.5f); - - e2->v1 = v; - e2->v2 = e1->v2; - e1->v2 = v; - - v->e = e1; - - e1->v2_disk_link.next = e1->v2_disk_link.prev = e2; - e2->v1_disk_link.next = e2->v1_disk_link.prev = e1; - } - - for (int i : IndexRange(faces.size())) { - BMFace *f1 = faces[i], *f2 = faces[i + 1]; - BMEdge *e1 = edges[emap[i]]; - BMEdge *e2 = newedges[emap[i]]; - BMVert *nv = fmap[i]; - - // make sure first loop points to e1->v1 - BMLoop *l = f1->l_first; - do { - if (l->v == e1->v1) { - break; - } - l = l->next; - } while (l != f1->l_first); - - f1->l_first = l; - - BMLoop *l2 = f2->l_first; - - l2->f = l2->next->f = l2->prev->f = f2; - l2->v = nv; - l2->next->v = l->next->v; - l2->prev->v = l->prev->v; - l2->e = e2; - l2->next->e = l->next->e; - l2->prev->e = l->prev->e; - - l->next->v = nv; - l->next->e = e2; - } -} - #define MAX_RE_CHILD 3 struct ReVertNode { int totvert, totchild; @@ -5225,7 +5058,7 @@ static void free_meshtest(MeshTest *m) #define SMOOTH_TEST_STEPS 20 -double pbvh_bmesh_smooth_test(BMesh *bm, PBVH *pbvh) +double pbvh_bmesh_smooth_test(BMesh *bm, PBVH * /*pbvh*/) { double average = 0.0f; double average_tot = 0.0f; @@ -5290,7 +5123,7 @@ double pbvh_bmesh_smooth_test(BMesh *bm, PBVH *pbvh) return average / average_tot; } -double pbvh_meshtest2_smooth_test(MeshTest2 *m2, PBVH *pbvh) +double pbvh_meshtest2_smooth_test(MeshTest2 *m2, PBVH * /*pbvh*/) { double average = 0.0f; double average_tot = 0.0f; @@ -5358,7 +5191,7 @@ double pbvh_meshtest2_smooth_test(MeshTest2 *m2, PBVH *pbvh) return average / average_tot; } -double pbvh_meshtest_smooth_test(MeshTest *m, PBVH *pbvh) +double pbvh_meshtest_smooth_test(MeshTest *m, PBVH * /*pbvh*/) { double average = 0.0f; double average_tot = 0.0f; diff --git a/source/blender/blenkernel/intern/pbvh_intern.hh b/source/blender/blenkernel/intern/pbvh_intern.hh index b0ab2caec16..80c5ce44a7a 100644 --- a/source/blender/blenkernel/intern/pbvh_intern.hh +++ b/source/blender/blenkernel/intern/pbvh_intern.hh @@ -4,6 +4,7 @@ #include "BLI_compiler_compat.h" #include "BLI_ghash.h" +#include "BLI_math_vector_types.hh" #include "BLI_vector.hh" #include "DNA_customdata_types.h" @@ -196,7 +197,15 @@ struct PBVH { const int *corner_edges; /* Owned by the #PBVH, because after deformations they have to be recomputed. */ const MLoopTri *looptri; + blender::Span origco, origno; + struct MSculptVert *msculptverts; + /* Sculpt flags*/ + uint8_t *sculpt_flags; + + /* Cached vertex valences */ + int *valence; + CustomData *vdata; CustomData *ldata; CustomData *pdata; @@ -282,6 +291,8 @@ struct PBVH { int *boundary_flags; int cd_boundary_flag; + int cd_curvature_dir; + PBVHGPUFormat *vbo_id; PBVHPixels pixels; @@ -430,6 +441,8 @@ void bke_pbvh_update_vert_boundary(int cd_sculpt_vert, int cd_face_node_offset, int cd_vcol_offset, int cd_boundary_flags, + int cd_flags, + int cd_valence, BMVert *v, int bound_symmetry, const struct CustomData *ldata, diff --git a/source/blender/bmesh/intern/bmesh_log.cc b/source/blender/bmesh/intern/bmesh_log.cc index e572f39afd1..8805d51498c 100644 --- a/source/blender/bmesh/intern/bmesh_log.cc +++ b/source/blender/bmesh/intern/bmesh_log.cc @@ -7,6 +7,7 @@ #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" +#include "BLI_asan.h" #include "BLI_hash.hh" #include "BLI_listbase_wrapper.hh" #include "BLI_map.hh" @@ -99,6 +100,14 @@ template struct BMLogElem { BMID id = BMID(-1); void *customdata = nullptr; char flag = 0; + +#ifdef WITH_ASAN + bool dead = false; + ~BMLogElem() + { + dead = true; + } +#endif }; template struct LogElemAlloc { @@ -603,6 +612,7 @@ struct BMLogEntry { void free_logvert(BMLogVert *lv) { if (lv->customdata) { + CustomData_bmesh_asan_unpoison(&vdata, lv->customdata); BLI_mempool_free(vdata.pool, lv->customdata); } @@ -638,9 +648,14 @@ struct BMLogEntry { CustomData_bmesh_copy_data(&bm->edata, &edata, e->head.data, &le->customdata); } - void free_logedge(BMesh * /*bm*/, BMLogEdge *e) + void free_logedge(BMesh * /*bm*/, BMLogEdge *le) { - epool.free(e); + if (le->customdata) { + CustomData_bmesh_asan_unpoison(&edata, le->customdata); + BLI_mempool_free(edata.pool, le->customdata); + } + + epool.free(le); } BMLogFace *alloc_logface(BMesh *bm, BMFace *f) @@ -693,11 +708,13 @@ struct BMLogEntry { { if (lf->loop_customdata[0]) { for (int i = 0; i < lf->verts.size(); i++) { + CustomData_bmesh_asan_unpoison(&ldata, lf->loop_customdata[i]); BLI_mempool_free(ldata.pool, lf->loop_customdata[i]); } } if (lf->customdata) { + CustomData_bmesh_asan_unpoison(&pdata, lf->customdata); BLI_mempool_free(pdata.pool, lf->customdata); } diff --git a/source/blender/bmesh/intern/bmesh_mesh.cc b/source/blender/bmesh/intern/bmesh_mesh.cc index 2ee33730897..be489af4a89 100644 --- a/source/blender/bmesh/intern/bmesh_mesh.cc +++ b/source/blender/bmesh/intern/bmesh_mesh.cc @@ -202,6 +202,14 @@ BMesh *BM_mesh_create(const BMAllocTemplate *allocsize, const struct BMeshCreate return bm; } +ATTR_NO_OPT static void customdata_pool_destroy(BMesh */*bm*/, CustomData *cdata) +{ + if (cdata->pool) { + BLI_mempool_destroy(cdata->pool); + cdata->pool = nullptr; + } +} + void BM_mesh_data_free(BMesh *bm) { BMVert *v; @@ -266,16 +274,16 @@ void BM_mesh_data_free(BMesh *bm) /* Free custom data pools, This should probably go in CustomData_free? */ if (bm->vdata.totlayer) { - BLI_mempool_destroy(bm->vdata.pool); + customdata_pool_destroy(bm, &bm->vdata); } if (bm->edata.totlayer) { - BLI_mempool_destroy(bm->edata.pool); + customdata_pool_destroy(bm, &bm->edata); } if (bm->ldata.totlayer) { - BLI_mempool_destroy(bm->ldata.pool); + customdata_pool_destroy(bm, &bm->ldata); } if (bm->pdata.totlayer) { - BLI_mempool_destroy(bm->pdata.pool); + customdata_pool_destroy(bm, &bm->pdata); } /* free custom data */ @@ -1638,25 +1646,13 @@ void BM_mesh_vert_coords_apply_with_mat4(BMesh *bm, } } -static void on_vert_kill(BMesh * /*bm*/, BMVert * /*v*/, void * /*userdata*/) -{ -} -static void on_edge_kill(BMesh * /*bm*/, BMEdge * /*e*/, void * /*userdata*/) -{ -} -static void on_face_kill(BMesh * /*bm*/, BMFace * /*f*/, void * /*userdata*/) -{ -} +static void on_vert_kill(BMesh * /*bm*/, BMVert * /*v*/, void * /*userdata*/) {} +static void on_edge_kill(BMesh * /*bm*/, BMEdge * /*e*/, void * /*userdata*/) {} +static void on_face_kill(BMesh * /*bm*/, BMFace * /*f*/, void * /*userdata*/) {} -static void on_vert_create(BMesh * /*bm*/, BMVert * /*v*/, void * /*userdata*/) -{ -} -static void on_edge_create(BMesh * /*bm*/, BMEdge * /*v*/, void * /*userdata*/) -{ -} -static void on_face_create(BMesh * /*bm*/, BMFace * /*v*/, void * /*userdata*/) -{ -} +static void on_vert_create(BMesh * /*bm*/, BMVert * /*v*/, void * /*userdata*/) {} +static void on_edge_create(BMesh * /*bm*/, BMEdge * /*v*/, void * /*userdata*/) {} +static void on_face_create(BMesh * /*bm*/, BMFace * /*v*/, void * /*userdata*/) {} void BM_empty_tracer(BMTracer *tracer, void *userdata) { diff --git a/source/blender/draw/DRW_pbvh.hh b/source/blender/draw/DRW_pbvh.hh index 9b985644e5e..97b3919639b 100644 --- a/source/blender/draw/DRW_pbvh.hh +++ b/source/blender/draw/DRW_pbvh.hh @@ -12,6 +12,7 @@ #include "BLI_bitmap.h" #include "BLI_offset_indices.hh" #include "BLI_span.hh" +#include "BLI_math_vector_types.hh" #include "BKE_ccg.h" @@ -73,7 +74,7 @@ struct PBVH_GPU_Args { int cd_mask_layer; struct PBVHTriBuf *tribuf, *tri_buffers; int tot_tri_buffers, updategen; - struct MSculptVert *msculptverts; + blender::Span origco, origno; }; void DRW_pbvh_node_update(PBVHBatches *batches, PBVH_GPU_Args *args); diff --git a/source/blender/draw/intern/draw_pbvh.cc b/source/blender/draw/intern/draw_pbvh.cc index 9679409a7a2..a5906316b37 100644 --- a/source/blender/draw/intern/draw_pbvh.cc +++ b/source/blender/draw/intern/draw_pbvh.cc @@ -582,9 +582,7 @@ struct PBVHBatches { if (args->show_orig) { foreach_faces( [&](int /*buffer_i*/, int /*tri_i*/, int vertex_i, const MLoopTri * /*tri*/) { - MSculptVert *mv = args->msculptverts + vertex_i; - - *static_cast(GPU_vertbuf_raw_step(&access)) = mv->origco; + *static_cast(GPU_vertbuf_raw_step(&access)) = args->origco[vertex_i]; }); } else { @@ -999,13 +997,12 @@ struct PBVHBatches { } case CD_PBVH_CO_TYPE: if (args->show_orig) { - int cd_sculpt_vert = CustomData_get_offset(&args->bm->vdata, CD_DYNTOPO_VERT); + int cd_origco = CustomData_get_offset_named( + &args->bm->vdata, CD_PROP_INT32, ".sculpt_orig_co"); foreach_bmesh([&](BMLoop *l) { - MSculptVert *mv = static_cast( - BM_ELEM_CD_GET_VOID_P(l->v, cd_sculpt_vert)); - - *static_cast(GPU_vertbuf_raw_step(&access)) = mv->origco; + *static_cast(GPU_vertbuf_raw_step(&access)) = BM_ELEM_CD_PTR( + l->v, cd_origco); }); } else { @@ -1017,14 +1014,13 @@ struct PBVHBatches { case CD_PBVH_NO_TYPE: if (args->show_orig) { - int cd_sculpt_vert = CustomData_get_offset(&args->bm->vdata, CD_DYNTOPO_VERT); + int cd_origco = CustomData_get_offset_named( + &args->bm->vdata, CD_PROP_INT32, ".sculpt_orig_co"); foreach_bmesh([&](BMLoop *l) { - MSculptVert *mv = static_cast( - BM_ELEM_CD_GET_VOID_P(l->v, cd_sculpt_vert)); short no[3]; - normal_float_to_short_v3(no, mv->origno); + normal_float_to_short_v3(no, BM_ELEM_CD_PTR(l->v, cd_origco)); *static_cast(GPU_vertbuf_raw_step(&access)) = no; }); } diff --git a/source/blender/editors/sculpt_paint/paint_mask.cc b/source/blender/editors/sculpt_paint/paint_mask.cc index c9033960a87..91828df6f58 100644 --- a/source/blender/editors/sculpt_paint/paint_mask.cc +++ b/source/blender/editors/sculpt_paint/paint_mask.cc @@ -1540,8 +1540,11 @@ static void sculpt_gesture_apply_trim(SculptGestureContext *sgcontext) sgcontext->ss->cd_sculpt_vert, sgcontext->ss->cd_face_areas, sgcontext->ss->attrs.boundary_flags->bmesh_cd_offset, - sgcontext->ss->fast_draw, - true); + sgcontext->ss->attrs.flags->bmesh_cd_offset, + sgcontext->ss->attrs.valence->bmesh_cd_offset, + sgcontext->ss->attrs.orig_co->bmesh_cd_offset, + sgcontext->ss->attrs.orig_no->bmesh_cd_offset, + sgcontext->ss->fast_draw); } else { // save result to mesh BMeshToMeshParams params = {0}; diff --git a/source/blender/editors/sculpt_paint/sculpt.cc b/source/blender/editors/sculpt_paint/sculpt.cc index b13d172f4ac..9eedf50c447 100644 --- a/source/blender/editors/sculpt_paint/sculpt.cc +++ b/source/blender/editors/sculpt_paint/sculpt.cc @@ -184,36 +184,12 @@ MSculptVert *SCULPT_vertex_get_sculptvert(const SculptSession *ss, PBVHVertRef v const float *SCULPT_vertex_origco_get(SculptSession *ss, PBVHVertRef vertex) { - switch (BKE_pbvh_type(ss->pbvh)) { - case PBVH_BMESH: { - BMVert *v = (BMVert *)vertex.i; - return BKE_PBVH_SCULPTVERT(ss->cd_sculpt_vert, v)->origco; - } - - case PBVH_GRIDS: - case PBVH_FACES: { - return ss->msculptverts[vertex.i].origco; - } - } - - return nullptr; + return vertex_attr_ptr(vertex, ss->attrs.orig_co); } const float *SCULPT_vertex_origno_get(SculptSession *ss, PBVHVertRef vertex) { - switch (BKE_pbvh_type(ss->pbvh)) { - case PBVH_BMESH: { - BMVert *v = (BMVert *)vertex.i; - return BKE_PBVH_SCULPTVERT(ss->cd_sculpt_vert, v)->origno; - } - - case PBVH_GRIDS: - case PBVH_FACES: { - return ss->msculptverts[vertex.i].origno; - } - } - - return nullptr; + return vertex_attr_ptr(vertex, ss->attrs.orig_no); } int SCULPT_vertex_count_get(const SculptSession *ss) @@ -1204,9 +1180,9 @@ static void sculpt_vertex_neighbors_get_bmesh(const SculptSession *ss, e2 = BM_DISK_EDGE_NEXT(e, v); v2 = v == e->v1 ? e->v2 : e->v1; - MSculptVert *mv = BKE_PBVH_SCULPTVERT(ss->cd_sculpt_vert, v2); + uint8_t flag = *BM_ELEM_CD_PTR(v2, ss->attrs.flags->bmesh_cd_offset); - if (!(mv->flag & SCULPTVERT_VERT_FSET_HIDDEN)) { // && (e->head.hflag & BM_ELEM_DRAW)) { + if (!(flag & SCULPTVERT_VERT_FSET_HIDDEN)) { sculpt_vertex_neighbor_add_nocheck(iter, BKE_pbvh_make_vref((intptr_t)v2), BKE_pbvh_make_eref((intptr_t)e), @@ -1308,9 +1284,9 @@ static void sculpt_vertex_neighbors_get_faces_vemap(const SculptSession *ss, const int2 &e = ss->edges[vert_map->indices[i]]; unsigned int v = e[0] == (unsigned int)vertex.i ? e[1] : e[0]; - MSculptVert *mv = ss->msculptverts + v; + int8_t flag = vertex_attr_get(vertex, ss->attrs.flags); - if (mv->flag & SCULPTVERT_VERT_FSET_HIDDEN) { + if (flag & SCULPTVERT_VERT_FSET_HIDDEN) { /* Skip connectivity from hidden faces. */ continue; } @@ -1417,46 +1393,6 @@ static bool sculpt_check_boundary_vertex_in_base_mesh(const SculptSession *ss, c return BLI_BITMAP_TEST(ss->vertex_info.boundary, index); } -bool SCULPT_vertex_is_boundary(const SculptSession *ss, const PBVHVertRef vertex) -{ - switch (BKE_pbvh_type(ss->pbvh)) { - case PBVH_FACES: { - if (!SCULPT_vertex_all_faces_visible_get(ss, vertex)) { - return true; - } - return sculpt_check_boundary_vertex_in_base_mesh(ss, vertex.i); - } - case PBVH_BMESH: { - BMVert *v = (BMVert *)vertex.i; - return BM_vert_is_boundary(v); - } - - case PBVH_GRIDS: { - const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh); - const int grid_index = vertex.i / key->grid_area; - const int vertex_index = vertex.i - grid_index * key->grid_area; - SubdivCCGCoord coord{}; - coord.grid_index = grid_index; - coord.x = vertex_index % key->grid_size; - coord.y = vertex_index / key->grid_size; - int v1, v2; - const SubdivCCGAdjacencyType adjacency = BKE_subdiv_ccg_coarse_mesh_adjacency_info_get( - ss->subdiv_ccg, &coord, ss->corner_verts, ss->polys, &v1, &v2); - switch (adjacency) { - case SUBDIV_CCG_ADJACENT_VERTEX: - return sculpt_check_boundary_vertex_in_base_mesh(ss, v1); - case SUBDIV_CCG_ADJACENT_EDGE: - return sculpt_check_boundary_vertex_in_base_mesh(ss, v1) && - sculpt_check_boundary_vertex_in_base_mesh(ss, v2); - case SUBDIV_CCG_ADJACENT_NONE: - return false; - } - } - } - - return false; -} - /* Utilities */ bool SCULPT_stroke_is_main_symmetry_pass(StrokeCache *cache) @@ -2645,17 +2581,8 @@ static void calc_area_normal_and_center_task_cb(void *__restrict userdata, float no_s[3]; if (use_original) { - if (unode->bm_entry) { - BMVert *v = vd.bm_vert; - MSculptVert *mv = BKE_PBVH_SCULPTVERT(vd.cd_sculpt_vert, v); - - copy_v3_v3(no_s, mv->origno); - copy_v3_v3(co, mv->origco); - } - else { - copy_v3_v3(co, unode->co[vd.i]); - copy_v3_v3(no_s, unode->no[vd.i]); - } + copy_v3_v3(co, vertex_attr_ptr(vd.vertex, ss->attrs.orig_co)); + copy_v3_v3(no_s, vertex_attr_ptr(vd.vertex, ss->attrs.orig_no)); } else { copy_v3_v3(co, vd.co); @@ -4125,7 +4052,8 @@ static void sculpt_topology_update(Sculpt *sd, } /* do nodes under the brush cursor */ - BKE_pbvh_bmesh_update_topology_nodes(ss->pbvh, + BKE_pbvh_bmesh_update_topology_nodes(ss, + ss->pbvh, SCULPT_search_sphere_cb, topology_undopush_cb, &sdata, @@ -4400,6 +4328,13 @@ static void do_brush_action(Sculpt *sd, case SCULPT_UNDO_COORDS: BKE_pbvh_node_mark_update(nodes[i]); break; + case SCULPT_UNDO_HIDDEN: + case SCULPT_UNDO_DYNTOPO_BEGIN: + case SCULPT_UNDO_DYNTOPO_END: + case SCULPT_UNDO_DYNTOPO_SYMMETRIZE: + case SCULPT_UNDO_GEOMETRY: + case SCULPT_UNDO_NO_TYPE: + break; } if (extra_type == int(SCULPT_UNDO_COORDS)) { @@ -7059,7 +6994,7 @@ void SCULPT_automasking_node_begin(Object *ob, } } -void SCULPT_automasking_node_update(SculptSession * ss, +void SCULPT_automasking_node_update(SculptSession *ss, AutomaskingNodeData *automask_data, PBVHVertexIter *vd) { diff --git a/source/blender/editors/sculpt_paint/sculpt_api.cc b/source/blender/editors/sculpt_paint/sculpt_api.cc index 04fe6a8dfbf..152d1e2a2d4 100644 --- a/source/blender/editors/sculpt_paint/sculpt_api.cc +++ b/source/blender/editors/sculpt_paint/sculpt_api.cc @@ -365,11 +365,12 @@ static void faces_update_boundary_flags(const SculptSession *ss, const PBVHVertR ss->corner_edges.data(), ss->polys, ss->totfaces, - ss->msculptverts, ss->pmap->pmap, vertex, ss->sharp_edge, - ss->seam_edge); + ss->seam_edge, + static_cast(ss->attrs.flags->data), + static_cast(ss->attrs.valence->data)); /* We have to handle boundary here seperately. */ @@ -463,6 +464,8 @@ eSculptBoundary SCULPT_vertex_is_boundary(const SculptSession *ss, ss->cd_face_node_offset, ss->cd_vcol_offset, ss->attrs.boundary_flags->bmesh_cd_offset, + ss->attrs.flags->bmesh_cd_offset, + ss->attrs.valence->bmesh_cd_offset, (BMVert *)vertex.i, ss->boundary_symmetry, &ss->bm->ldata, @@ -480,28 +483,39 @@ eSculptBoundary SCULPT_vertex_is_boundary(const SculptSession *ss, } case PBVH_GRIDS: { - const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh); - const int grid_index = vertex.i / key->grid_area; - const int vertex_index = vertex.i - grid_index * key->grid_area; - SubdivCCGCoord coord{}; - coord.grid_index = grid_index; - coord.x = vertex_index % key->grid_size; - coord.y = vertex_index / key->grid_size; - int v1, v2; - const SubdivCCGAdjacencyType adjacency = BKE_subdiv_ccg_coarse_mesh_adjacency_info_get( - ss->subdiv_ccg, &coord, ss->corner_verts, ss->polys, &v1, &v2); + if (needs_update) { + BKE_pbvh_update_vert_boundary_grids(ss->pbvh, vertex); + needs_update = false; + } - switch (adjacency) { - case SUBDIV_CCG_ADJACENT_VERTEX: - return sculpt_check_boundary_vertex_in_base_mesh(ss, v1) ? SCULPT_BOUNDARY_MESH : - (eSculptBoundary)0; - case SUBDIV_CCG_ADJACENT_EDGE: - if (sculpt_check_boundary_vertex_in_base_mesh(ss, v1) && - sculpt_check_boundary_vertex_in_base_mesh(ss, v2)) { - return SCULPT_BOUNDARY_MESH; - } - case SUBDIV_CCG_ADJACENT_NONE: - return (eSculptBoundary)0; + flag = eSculptBoundary(vertex_attr_get(vertex, ss->attrs.boundary_flags)); + + /* BKE_pbvh_update_vert_boundary_grids does not yet support mesh boundaries for PBVH_GRIDS. + */ + if (boundary_types & SCULPT_BOUNDARY_MESH) { + const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh); + const int grid_index = vertex.i / key->grid_area; + const int vertex_index = vertex.i - grid_index * key->grid_area; + SubdivCCGCoord coord{}; + coord.grid_index = grid_index; + coord.x = vertex_index % key->grid_size; + coord.y = vertex_index / key->grid_size; + int v1, v2; + const SubdivCCGAdjacencyType adjacency = BKE_subdiv_ccg_coarse_mesh_adjacency_info_get( + ss->subdiv_ccg, &coord, ss->corner_verts, ss->polys, &v1, &v2); + + switch (adjacency) { + case SUBDIV_CCG_ADJACENT_VERTEX: + flag |= sculpt_check_boundary_vertex_in_base_mesh(ss, v1) ? SCULPT_BOUNDARY_MESH : + (eSculptBoundary)0; + case SUBDIV_CCG_ADJACENT_EDGE: + if (sculpt_check_boundary_vertex_in_base_mesh(ss, v1) && + sculpt_check_boundary_vertex_in_base_mesh(ss, v2)) { + flag |= SCULPT_BOUNDARY_MESH; + } + case SUBDIV_CCG_ADJACENT_NONE: + break; + } } } } @@ -546,16 +560,7 @@ int SCULPT_vertex_valence_get(const struct SculptSession *ss, PBVHVertRef vertex /* See SCULPT_stroke_id_test. */ void SCULPT_stroke_id_ensure(Object *ob) { - SculptSession *ss = ob->sculpt; - - if (!ss->attrs.stroke_id) { - SculptAttributeParams params = {0}; - ss->attrs.stroke_id = BKE_sculpt_attribute_ensure(ob, - ATTR_DOMAIN_POINT, - CD_PROP_INT32, - SCULPT_ATTRIBUTE_NAME(automasking_stroke_id), - ¶ms); - } + BKE_sculpt_ensure_sculpt_layers(ob); } int SCULPT_get_tool(const SculptSession *ss, const Brush *br) diff --git a/source/blender/editors/sculpt_paint/sculpt_curvature.cc b/source/blender/editors/sculpt_paint/sculpt_curvature.cc index fca02fc84a2..84d36c993c4 100644 --- a/source/blender/editors/sculpt_paint/sculpt_curvature.cc +++ b/source/blender/editors/sculpt_paint/sculpt_curvature.cc @@ -276,7 +276,6 @@ void SCULPT_curvature_begin(SculptSession *ss, struct PBVHNode *node, bool useAc BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vi, PBVH_ITER_UNIQUE) { BMVert *v = (BMVert *)vi.vertex.i; - MSculptVert *mv = BKE_PBVH_SCULPTVERT(ss->cd_sculpt_vert, v); SculptCurvatureData curv; SCULPT_calc_principle_curvatures(ss, vi.vertex, &curv, useAccurateSolver); diff --git a/source/blender/editors/sculpt_paint/sculpt_detail.cc b/source/blender/editors/sculpt_paint/sculpt_detail.cc index 8cbf279719a..afb91956496 100644 --- a/source/blender/editors/sculpt_paint/sculpt_detail.cc +++ b/source/blender/editors/sculpt_paint/sculpt_detail.cc @@ -133,7 +133,8 @@ static int sculpt_detail_flood_fill_exec(bContext *C, wmOperator *op) bool modified = true; while (modified) { - modified = BKE_pbvh_bmesh_update_topology(ss->pbvh, + modified = BKE_pbvh_bmesh_update_topology(ss, + ss->pbvh, PBVH_Collapse, center, nullptr, @@ -152,7 +153,8 @@ static int sculpt_detail_flood_fill_exec(bContext *C, wmOperator *op) BKE_pbvh_node_mark_topology_update(nodes[j]); } - modified |= BKE_pbvh_bmesh_update_topology(ss->pbvh, + modified |= BKE_pbvh_bmesh_update_topology(ss, + ss->pbvh, PBVH_Subdivide, center, nullptr, @@ -181,7 +183,8 @@ static int sculpt_detail_flood_fill_exec(bContext *C, wmOperator *op) BKE_pbvh_node_mark_topology_update(nodes[j]); } - BKE_pbvh_bmesh_update_topology(ss->pbvh, + BKE_pbvh_bmesh_update_topology(ss, + ss->pbvh, PBVH_Cleanup, center, nullptr, diff --git a/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc b/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc index eaa6eec18c7..2e576005829 100644 --- a/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc +++ b/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc @@ -304,10 +304,10 @@ void SCULT_dyntopo_flag_all_disk_sort(SculptSession *ss) bool SCULPT_dyntopo_check_disk_sort(SculptSession *ss, PBVHVertRef vertex) { BMVert *v = (BMVert *)vertex.i; - MSculptVert *mv = BKE_PBVH_SCULPTVERT(ss->cd_sculpt_vert, v); + uint8_t *flag = blender::bke::paint::vertex_attr_ptr(vertex, ss->attrs.flags); - if (mv->flag & SCULPTVERT_NEED_DISK_SORT) { - mv->flag &= ~SCULPTVERT_NEED_DISK_SORT; + if (*flag & SCULPTVERT_NEED_DISK_SORT) { + *flag &= ~SCULPTVERT_NEED_DISK_SORT; BM_sort_disk_cycle(v); @@ -528,8 +528,9 @@ void SCULPT_dynamic_topology_enable_ex(Main *bmain, #endif if (ss->pbvh) { - BKE_sculptsession_update_attr_refs(ob); - SCULPT_load_all_original(ob); + BKE_sculpt_ensure_sculpt_layers(ob); + BKE_sculpt_ensure_origco(ob); + blender::bke::paint::load_all_original(ob); } if (ss->pbvh && SCULPT_has_persistent_base(ss)) { @@ -552,7 +553,7 @@ void SCULPT_dynamic_topology_enable_ex(Main *bmain, } if (ss->pbvh) { - SCULPT_load_all_original(ob); + blender::bke::paint::load_all_original(ob); } /* Make sure the data for existing faces are initialized. */ diff --git a/source/blender/editors/sculpt_paint/sculpt_face_set.cc b/source/blender/editors/sculpt_paint/sculpt_face_set.cc index 20bfa18d9bb..a178b6606b5 100644 --- a/source/blender/editors/sculpt_paint/sculpt_face_set.cc +++ b/source/blender/editors/sculpt_paint/sculpt_face_set.cc @@ -2521,8 +2521,8 @@ static void sculpt_face_set_extrude_id(Object *ob, do { *(int *)BM_ELEM_CD_GET_VOID_P(l->v, cd_boundary_flag) |= SCULPT_BOUNDARY_NEEDS_UPDATE; - MSculptVert *mv = BKE_PBVH_SCULPTVERT(cd_sculpt_vert, l->v); - MV_ADD_FLAG(mv, mupdateflag); + uint8_t *flag = BM_ELEM_CD_PTR(l->v, ss->attrs.flags->bmesh_cd_offset); + *flag |= mupdateflag; } while ((l = l->next) != f->l_first); } @@ -2563,8 +2563,8 @@ static void sculpt_face_set_extrude_id(Object *ob, do { *(int *)BM_ELEM_CD_GET_VOID_P(l->v, cd_boundary_flag) |= SCULPT_BOUNDARY_NEEDS_UPDATE; - MSculptVert *mv = BKE_PBVH_SCULPTVERT(cd_sculpt_vert, l->v); - MV_ADD_FLAG(mv, mupdateflag); + uint8_t *flag = BM_ELEM_CD_PTR(l->v, ss->attrs.flags->bmesh_cd_offset); + *flag |= mupdateflag; } while ((l = l->next) != f->l_first); } @@ -2600,9 +2600,8 @@ static void sculpt_face_set_extrude_id(Object *ob, } if (cd_sculpt_vert != -1) { - MSculptVert *mv = (MSculptVert *)BM_ELEM_CD_GET_VOID_P(l->v, cd_sculpt_vert); - - MV_ADD_FLAG(mv, mupdateflag); + uint8_t *flag = BM_ELEM_CD_PTR(l->v, ss->attrs.flags->bmesh_cd_offset); + *flag |= mupdateflag; } } while ((l = l->next) != f->l_first); } diff --git a/source/blender/editors/sculpt_paint/sculpt_paint_color.cc b/source/blender/editors/sculpt_paint/sculpt_paint_color.cc index 8d80746b384..1ba79fc7c54 100644 --- a/source/blender/editors/sculpt_paint/sculpt_paint_color.cc +++ b/source/blender/editors/sculpt_paint/sculpt_paint_color.cc @@ -227,8 +227,10 @@ static void do_paint_brush_task_cb_ex(void *__restrict userdata, vcolor[3] = 1.0f; } else { - MSculptVert *mv = SCULPT_vertex_get_sculptvert(ss, vd.vertex); - IMB_blend_color_float(vcolor, mv->origcolor, buffer_color, IMB_BlendMode(brush->blend)); + IMB_blend_color_float(vcolor, + vertex_attr_ptr(vd.vertex, ss->attrs.orig_color), + buffer_color, + IMB_BlendMode(brush->blend)); } CLAMP4(vcolor, 0.0f, 1.0f); diff --git a/source/blender/editors/sculpt_paint/sculpt_smooth.cc b/source/blender/editors/sculpt_paint/sculpt_smooth.cc index e931749e810..f97670624a7 100644 --- a/source/blender/editors/sculpt_paint/sculpt_smooth.cc +++ b/source/blender/editors/sculpt_paint/sculpt_smooth.cc @@ -196,11 +196,10 @@ void SCULPT_bmesh_four_neighbor_average(SculptSession *ss, float tot_co = 0.0f; float buckets[8] = {0}; + PBVHVertRef vertex = {(intptr_t)v}; // zero_v3(direction); - MSculptVert *mv = BKE_PBVH_SCULPTVERT(cd_sculpt_vert, v); - float *col = BM_ELEM_CD_PTR(v, cd_temp); float dir[3]; float dir3[3] = {0.0f, 0.0f, 0.0f}; @@ -210,31 +209,33 @@ void SCULPT_bmesh_four_neighbor_average(SculptSession *ss, SCULPT_vertex_check_origdata(ss, BKE_pbvh_make_vref(intptr_t(v))); + float *origco = BM_ELEM_CD_PTR(v, ss->attrs.orig_co->bmesh_cd_offset); + float *origno = BM_ELEM_CD_PTR(v, ss->attrs.orig_no->bmesh_cd_offset); + if (do_origco) { // SCULPT_vertex_check_origdata(ss, (PBVHVertRef){.i = (intptr_t)v}); - madd_v3_v3fl(direction, mv->origno, -dot_v3v3(mv->origno, direction)); + madd_v3_v3fl(direction, origno, -dot_v3v3(origno, direction)); normalize_v3(direction); } - float *co1 = do_origco ? mv->origco : v->co; - float *no1 = do_origco ? mv->origno : v->no; + float *co1 = do_origco ? origco : v->co; + float *no1 = do_origco ? origno : v->no; + + int valence = SCULPT_vertex_valence_get(ss, vertex); if (weighted) { - PBVHVertRef vertex = {(intptr_t)v}; + areas = (float *)BLI_array_alloca(areas, valence * 2); - int val = SCULPT_vertex_valence_get(ss, vertex); - areas = (float *)BLI_array_alloca(areas, val * 2); - - BKE_pbvh_get_vert_face_areas(ss->pbvh, vertex, areas, val); + BKE_pbvh_get_vert_face_areas(ss->pbvh, vertex, areas, valence); float totarea = 0.0f; - for (int i = 0; i < val; i++) { + for (int i = 0; i < valence; i++) { totarea += areas[i]; } totarea = totarea != 0.0f ? 1.0f / totarea : 0.0f; - for (int i = 0; i < val; i++) { + for (int i = 0; i < valence; i++) { areas[i] *= totarea; } } @@ -245,12 +246,12 @@ void SCULPT_bmesh_four_neighbor_average(SculptSession *ss, copy_v3_v3(dir, direction); } else { - closest_vec_to_perp(dir, direction, no1, buckets, 1.0f); // col[3]); + closest_vec_to_perp(dir, direction, no1, buckets, 1.0f); } float totdir3 = 0.0f; - const float selfw = (float)mv->valence * 0.0025f; + const float selfw = (float)valence * 0.0025f; madd_v3_v3fl(dir3, direction, selfw); totdir3 += selfw; @@ -262,20 +263,21 @@ void SCULPT_bmesh_four_neighbor_average(SculptSession *ss, BM_ITER_ELEM_INDEX (e, &eiter, v, BM_EDGES_OF_VERT, area_i) { BMVert *v_other = (e->v1 == v) ? e->v2 : e->v1; + PBVHVertRef vertex_other = {reinterpret_cast(v_other)}; float dir2[3]; float *col2 = BM_ELEM_CD_PTR(v_other, cd_temp); float bucketw = 1.0f; - MSculptVert *mv2 = BKE_PBVH_SCULPTVERT(cd_sculpt_vert, v_other); float *co2; - if (!do_origco || mv2->stroke_id != ss->stroke_id) { + if (!do_origco || + blender::bke::sculpt::stroke_id_test_no_update(ss, vertex_other, STROKEID_USER_ORIGINAL)) { co2 = v_other->co; } else { - co2 = mv2->origco; + co2 = BM_ELEM_CD_PTR(v_other, ss->attrs.orig_co->bmesh_cd_offset); } eSculptBoundary bflag = SCULPT_BOUNDARY_FACE_SET | SCULPT_BOUNDARY_MESH | @@ -355,7 +357,7 @@ void SCULPT_bmesh_four_neighbor_average(SculptSession *ss, } if (totdir3 > 0.0f) { - float outdir = totdir3 / (float)mv->valence; + float outdir = totdir3 / (float)valence; // mul_v3_fl(dir3, 1.0 / totdir3); normalize_v3(dir3); @@ -887,8 +889,6 @@ void SCULPT_reproject_cdata(SculptSession *ss, return; } - MSculptVert *mv = BKE_PBVH_SCULPTVERT(ss->cd_sculpt_vert, v); - // int totuv = CustomData_number_of_layers(&ss->bm->ldata, CD_PROP_FLOAT2); CustomData *ldata = &ss->bm->ldata; @@ -1019,7 +1019,8 @@ void SCULPT_reproject_cdata(SculptSession *ss, const float *v_proj_axis = v->no; float v_proj[3][3]; - project_plane_normalized_v3_v3v3(v_proj[1], mv->origco, v_proj_axis); + float *old_origco = BM_ELEM_CD_PTR(v, ss->attrs.orig_co->bmesh_cd_offset); + project_plane_normalized_v3_v3v3(v_proj[1], old_origco, v_proj_axis); /* original (l->prev, l, l->next) projections for each loop ('l' remains unchanged) */ @@ -1100,22 +1101,22 @@ void SCULPT_reproject_cdata(SculptSession *ss, *fakef = *l->f; fakef->l_first = fakels; - // set original face normal - // normalize_v3(no); - // copy_v3_v3(fakef->no, no); - - // interpolate + /* Interpolate. */ BMLoop _interpl, *interpl = &_interpl; - MSculptVert saved = *mv; + uint8_t *flag = BM_ELEM_CD_PTR(v, ss->attrs.flags->bmesh_cd_offset); + uint8_t *stroke_id = BM_ELEM_CD_PTR(v, ss->attrs.stroke_id->bmesh_cd_offset); + + int flag_saved = *flag; + int stroke_id_saved = *stroke_id; *interpl = *l; interpl->head.data = blocks[i]; - // memcpy(interpl->head.data, l2->head.data, ldata->totsize); BM_loop_interp_from_face(ss->bm, interpl, fakef, false, false); - *mv = saved; + *stroke_id = stroke_id_saved; + *flag = flag_saved; CustomData_bmesh_copy_data(&ss->bm->ldata, &ss->bm->ldata, interpl->head.data, &l->head.data); } diff --git a/source/blender/editors/sculpt_paint/sculpt_undo.cc b/source/blender/editors/sculpt_paint/sculpt_undo.cc index 099713f4401..6d341dfdb90 100644 --- a/source/blender/editors/sculpt_paint/sculpt_undo.cc +++ b/source/blender/editors/sculpt_paint/sculpt_undo.cc @@ -685,7 +685,7 @@ typedef struct BmeshUndoData { bool balance_pbvh; int cd_face_node_offset, cd_vert_node_offset; int cd_face_node_offset_old, cd_vert_node_offset_old; - int cd_sculpt_vert, cd_boundary_flag; + int cd_sculpt_vert, cd_boundary_flag, cd_flags; bool regen_all_unique_verts; bool is_redo; } BmeshUndoData; @@ -724,10 +724,9 @@ static void bmesh_undo_on_vert_add(BMVert *v, void *userdata) BM_ELEM_CD_SET_INT(v, data->cd_vert_node_offset, -1); *(int *)BM_ELEM_CD_GET_VOID_P(v, data->cd_boundary_flag) |= SCULPT_BOUNDARY_NEEDS_UPDATE; - - MSculptVert *mv = BKE_PBVH_SCULPTVERT(data->cd_sculpt_vert, v); - MV_ADD_FLAG(mv, - SCULPTVERT_NEED_DISK_SORT | SCULPTVERT_NEED_VALENCE | SCULPTVERT_NEED_TRIANGULATE); + *BM_ELEM_CD_PTR(v, data->cd_flags) |= SCULPTVERT_NEED_DISK_SORT | + SCULPTVERT_NEED_VALENCE | + SCULPTVERT_NEED_TRIANGULATE; } static void bmesh_undo_on_face_kill(BMFace *f, void *userdata) @@ -744,9 +743,9 @@ static void bmesh_undo_on_face_kill(BMFace *f, void *userdata) BMLoop *l = f->l_first; do { - MSculptVert *mv = BKE_PBVH_SCULPTVERT(data->cd_sculpt_vert, l->v); - MV_ADD_FLAG(mv, SCULPTVERT_NEED_DISK_SORT | SCULPTVERT_NEED_VALENCE); - *(int *)BM_ELEM_CD_GET_VOID_P(l->v, data->cd_boundary_flag) |= SCULPT_BOUNDARY_NEEDS_UPDATE; + *BM_ELEM_CD_PTR(l->v, data->cd_flags) |= SCULPTVERT_NEED_DISK_SORT | + SCULPTVERT_NEED_VALENCE; + *BM_ELEM_CD_PTR(l->v, data->cd_boundary_flag) |= SCULPT_BOUNDARY_NEEDS_UPDATE; } while ((l = l->next) != f->l_first); // data->do_full_recalc = true; @@ -768,11 +767,11 @@ static void bmesh_undo_on_face_add(BMFace *f, void *userdata) do { *(int *)BM_ELEM_CD_GET_VOID_P(l->v, data->cd_boundary_flag) |= SCULPT_BOUNDARY_NEEDS_UPDATE; - MSculptVert *mv = BKE_PBVH_SCULPTVERT(data->cd_sculpt_vert, l->v); - MV_ADD_FLAG(mv, SCULPTVERT_NEED_DISK_SORT | SCULPTVERT_NEED_VALENCE); + *BM_ELEM_CD_PTR(l->v, data->cd_flags) |= SCULPTVERT_NEED_DISK_SORT | + SCULPTVERT_NEED_VALENCE; if (f->len > 3) { - MV_ADD_FLAG(mv, SCULPTVERT_NEED_TRIANGULATE); + *BM_ELEM_CD_PTR(l->v, data->cd_flags) |= SCULPTVERT_NEED_TRIANGULATE; } int ni_l = BM_ELEM_CD_GET_INT(l->v, data->cd_vert_node_offset); @@ -808,30 +807,30 @@ static void bmesh_undo_on_edge_kill(BMEdge *e, void *userdata) { BmeshUndoData *data = (BmeshUndoData *)userdata; - MSculptVert *mv1 = BKE_PBVH_SCULPTVERT(data->cd_sculpt_vert, e->v1); - MSculptVert *mv2 = BKE_PBVH_SCULPTVERT(data->cd_sculpt_vert, e->v2); - *(int *)BM_ELEM_CD_GET_VOID_P(e->v1, data->cd_boundary_flag) |= SCULPT_BOUNDARY_NEEDS_UPDATE; *(int *)BM_ELEM_CD_GET_VOID_P(e->v2, data->cd_boundary_flag) |= SCULPT_BOUNDARY_NEEDS_UPDATE; - MV_ADD_FLAG(mv1, - SCULPTVERT_NEED_TRIANGULATE | SCULPTVERT_NEED_DISK_SORT | SCULPTVERT_NEED_VALENCE); - MV_ADD_FLAG(mv2, - SCULPTVERT_NEED_TRIANGULATE | SCULPTVERT_NEED_DISK_SORT | SCULPTVERT_NEED_VALENCE); -} - + *BM_ELEM_CD_PTR(e->v1, data->cd_flags) |= SCULPTVERT_NEED_DISK_SORT | + SCULPTVERT_NEED_VALENCE | + SCULPTVERT_NEED_TRIANGULATE; + *BM_ELEM_CD_PTR(e->v2, data->cd_flags) |= SCULPTVERT_NEED_DISK_SORT | + SCULPTVERT_NEED_VALENCE | + SCULPTVERT_NEED_TRIANGULATE; +}; +; static void bmesh_undo_on_edge_add(BMEdge *e, void *userdata) { BmeshUndoData *data = (BmeshUndoData *)userdata; - MSculptVert *mv1 = BKE_PBVH_SCULPTVERT(data->cd_sculpt_vert, e->v1); - MSculptVert *mv2 = BKE_PBVH_SCULPTVERT(data->cd_sculpt_vert, e->v2); - *(int *)BM_ELEM_CD_GET_VOID_P(e->v1, data->cd_boundary_flag) |= SCULPT_BOUNDARY_NEEDS_UPDATE; *(int *)BM_ELEM_CD_GET_VOID_P(e->v2, data->cd_boundary_flag) |= SCULPT_BOUNDARY_NEEDS_UPDATE; - mv1->flag |= SCULPTVERT_NEED_TRIANGULATE | SCULPTVERT_NEED_DISK_SORT | SCULPTVERT_NEED_VALENCE; - mv2->flag |= SCULPTVERT_NEED_TRIANGULATE | SCULPTVERT_NEED_DISK_SORT | SCULPTVERT_NEED_VALENCE; + *BM_ELEM_CD_PTR(e->v1, data->cd_flags) |= SCULPTVERT_NEED_DISK_SORT | + SCULPTVERT_NEED_VALENCE | + SCULPTVERT_NEED_TRIANGULATE; + *BM_ELEM_CD_PTR(e->v2, data->cd_flags) |= SCULPTVERT_NEED_DISK_SORT | + SCULPTVERT_NEED_VALENCE | + SCULPTVERT_NEED_TRIANGULATE; } static void bmesh_undo_on_vert_change(BMVert *v, void *userdata, void *old_customdata) @@ -960,6 +959,7 @@ static void sculpt_undo_bmesh_restore_generic(SculptUndoNode *unode, Object *ob, -1, ss->cd_sculpt_vert, ss->attrs.boundary_flags->bmesh_cd_offset, + ss->attrs.flags->bmesh_cd_offset, false, !unode->applied}; @@ -1052,7 +1052,7 @@ static void sculpt_undo_bmesh_enable(Object *ob, SculptUndoNode *unode, bool is_ BKE_sculptsession_update_attr_refs(ob); if (ss->pbvh) { - SCULPT_load_all_original(ob); + blender::bke::paint::load_all_original(ob); } if (ss->bm_idmap) { @@ -2032,8 +2032,8 @@ static void sculpt_undo_store_coords(Object *ob, SculptUndoNode *unode) copy_v3_v3(unode->orig_co[vd.i], cos[vd.index]); } else { - MSculptVert *mv = SCULPT_vertex_get_sculptvert(ss, vd.vertex); - copy_v3_v3(unode->orig_co[vd.i], mv->origco); + copy_v3_v3(unode->orig_co[vd.i], + blender::bke::paint::vertex_attr_ptr(vd.vertex, ss->attrs.orig_co)); } } } @@ -2293,28 +2293,24 @@ static SculptUndoNode *sculpt_undo_bmesh_push(Object *ob, PBVHNode *node, Sculpt } eAttrDomain domain = BKE_id_attribute_domain(&mesh->id, color_layer); - switch (domain) { - case ATTR_DOMAIN_POINT: { - BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) { - bm_logstack_push(); - BM_log_vert_before_modified(ss->bm, ss->bm_log, vd.bm_vert); - bm_logstack_pop(); - } - BKE_pbvh_vertex_iter_end; - break; - } - case ATTR_DOMAIN_CORNER: { - TableGSet *faces = BKE_pbvh_bmesh_node_faces(node); - BMFace *f; - - TGSET_ITER (f, faces) { - BM_log_face_modified(ss->bm, ss->bm_log, f); - } - TGSET_ITER_END - - break; + if (domain == ATTR_DOMAIN_POINT) { + BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) { + bm_logstack_push(); + BM_log_vert_before_modified(ss->bm, ss->bm_log, vd.bm_vert); + bm_logstack_pop(); } + BKE_pbvh_vertex_iter_end; } + else if (domain == ATTR_DOMAIN_CORNER) { + TableGSet *faces = BKE_pbvh_bmesh_node_faces(node); + BMFace *f; + + TGSET_ITER (f, faces) { + BM_log_face_modified(ss->bm, ss->bm_log, f); + } + TGSET_ITER_END + } + break; } case SCULPT_UNDO_FACE_SETS: { @@ -2332,6 +2328,7 @@ static SculptUndoNode *sculpt_undo_bmesh_push(Object *ob, PBVHNode *node, Sculpt case SCULPT_UNDO_DYNTOPO_END: case SCULPT_UNDO_DYNTOPO_SYMMETRIZE: case SCULPT_UNDO_GEOMETRY: + case SCULPT_UNDO_NO_TYPE: break; } } -- 2.30.2 From 79b3b8e0933a14f681ce769dc88c24eb79360f7b Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Tue, 2 May 2023 16:33:09 -0700 Subject: [PATCH 074/279] temp-sculpt-dyntopo: Finish getting rid of MSculptVert --- source/blender/blenkernel/BKE_paint.h | 12 +-- source/blender/blenkernel/BKE_pbvh.h | 32 +------- .../blender/blenkernel/intern/customdata.cc | 78 +------------------ source/blender/blenkernel/intern/dyntopo.cc | 40 +++++----- .../blenkernel/intern/dyntopo_collapse.cc | 8 +- .../blenkernel/intern/dyntopo_intern.hh | 31 ++++---- source/blender/blenkernel/intern/paint.cc | 56 ++++--------- source/blender/blenkernel/intern/pbvh.c | 22 ++++-- source/blender/blenkernel/intern/pbvh.cc | 26 ++----- .../blender/blenkernel/intern/pbvh_bmesh.cc | 67 +++++++--------- .../blender/blenkernel/intern/pbvh_intern.hh | 9 +-- .../blenloader/intern/versioning_300.cc | 6 +- source/blender/bmesh/intern/bmesh_log.cc | 2 +- .../editors/mesh/editmesh_mask_extract.cc | 14 ++-- .../editors/sculpt_paint/paint_mask.cc | 1 - source/blender/editors/sculpt_paint/sculpt.cc | 68 ++++++++-------- .../editors/sculpt_paint/sculpt_api.cc | 10 +-- .../sculpt_paint/sculpt_brush_types.cc | 5 +- .../editors/sculpt_paint/sculpt_dyntopo.cc | 4 +- .../editors/sculpt_paint/sculpt_face_set.cc | 42 ++++------ .../sculpt_paint/sculpt_filter_color.cc | 2 + .../sculpt_paint/sculpt_filter_mask.cc | 3 + .../editors/sculpt_paint/sculpt_intern.hh | 5 +- .../sculpt_paint/sculpt_paint_color.cc | 4 + .../editors/sculpt_paint/sculpt_smooth.cc | 1 - .../editors/sculpt_paint/sculpt_undo.cc | 45 +++++------ .../blender/makesdna/DNA_customdata_types.h | 12 ++- source/blender/makesdna/DNA_meshdata_types.h | 43 ++-------- 28 files changed, 225 insertions(+), 423 deletions(-) diff --git a/source/blender/blenkernel/BKE_paint.h b/source/blender/blenkernel/BKE_paint.h index 3996cfbceaa..c3caf4f7ef8 100644 --- a/source/blender/blenkernel/BKE_paint.h +++ b/source/blender/blenkernel/BKE_paint.h @@ -28,7 +28,6 @@ extern "C" { #endif struct SculptAttribute; -struct MSculptVert; struct BMFace; struct BMesh; struct BMIdMap; @@ -716,12 +715,12 @@ typedef struct SculptAttributePointers { SculptAttribute *smooth_bdist; SculptAttribute *smooth_vel; - SculptAttribute *boundary_flags; - SculptAttribute *sculpt_vert; + /* Sculpt utility attributes. */ SculptAttribute *stroke_id; - + SculptAttribute *boundary_flags; SculptAttribute *valence; /* CD_PROP_INT32, vert */ SculptAttribute *flags; /* CD_PROP_INT8, vert */ + SculptAttribute *orig_co, *orig_no; /* CD_PROP_FLOAT3, vert */ SculptAttribute *orig_fsets; /* CD_PROP_INT32, face */ SculptAttribute *orig_color; /* CD_PROP_FLOAT4, vert */ @@ -824,7 +823,6 @@ struct SculptSession { /* TODO: get rid of these cd_ members and use * .attrs.XXX.bmesh_cd_offset directly. */ - int cd_sculpt_vert; int cd_vert_node_offset; int cd_face_node_offset; int cd_vcol_offset; @@ -967,8 +965,6 @@ struct SculptSession { int boundary_symmetry; bool fast_draw; // hides facesets/masks and forces smooth to save GPU bandwidth - struct MSculptVert *msculptverts; // for non-bmesh - int last_msculptverts_count; /* This is a fixed-size array so we can pass pointers to its elements * to client code. This is important to keep bmesh offsets up to date. @@ -1084,7 +1080,7 @@ void BKE_sculpt_attribute_destroy_temporary_all(struct Object *ob); /* Destroy attributes that were marked as stroke only in SculptAttributeParams. */ void BKE_sculpt_attributes_destroy_temporary_stroke(struct Object *ob); -bool BKE_sculptsession_check_sculptverts(struct Object *ob, +bool BKE_sculpt_init_flags_valence(struct Object *ob, struct PBVH *pbvh, int totvert, bool reset_flags); diff --git a/source/blender/blenkernel/BKE_pbvh.h b/source/blender/blenkernel/BKE_pbvh.h index aab6c09adab..9ca5649c549 100644 --- a/source/blender/blenkernel/BKE_pbvh.h +++ b/source/blender/blenkernel/BKE_pbvh.h @@ -64,7 +64,6 @@ struct TableGSet; struct DMFlagMat; struct IsectRayPrecalc; struct MLoopTri; -struct MSculptVert; struct Mesh; struct MeshElemMap; struct PBVH; @@ -384,7 +383,6 @@ void BKE_pbvh_build_bmesh(PBVH *pbvh, struct BMIdMap *idmap, const int cd_vert_node_offset, const int cd_face_node_offset, - const int cd_sculpt_vert, const int cd_face_areas, const int cd_boundary_flag, const int cd_flag, @@ -399,7 +397,6 @@ void BKE_pbvh_set_idmap(PBVH *pbvh, struct BMIdMap *idmap); void BKE_pbvh_update_offsets(PBVH *pbvh, const int cd_vert_node_offset, const int cd_face_node_offset, - const int cd_sculpt_vert, const int cd_face_areas, const int cd_boudnary_flags, const int cd_flag, @@ -419,9 +416,6 @@ void BKE_pbvh_free(PBVH *pbvh); void BKE_pbvh_set_bm_log(PBVH *pbvh, BMLog *log); BMLog *BKE_pbvh_get_bm_log(PBVH *pbvh); -/* update MSculptVerts, doesn't take pbvh argument to allow usage if pbvh doesn't currently exist - */ -void BKE_pbvh_update_sculpt_verts(PBVH *pbvh); /** checks if original data needs to be updated for v, and if so updates it. Stroke_id @@ -776,7 +770,6 @@ typedef struct PBVHVertexIter { struct TableGSet *bm_unique_verts, *bm_other_verts; struct CustomData *bm_vdata; - int cd_sculpt_vert; int cd_vert_mask_offset; int cd_vcol_offset; @@ -790,9 +783,6 @@ typedef struct PBVHVertexIter { bool visible; } PBVHVertexIter; -#define BKE_PBVH_SCULPTVERT(cd_sculpt_vert, v) \ - ((MSculptVert *)BM_ELEM_CD_GET_VOID_P(v, cd_sculpt_vert)) - void pbvh_vertex_iter_init(PBVH *pbvh, PBVHNode *node, PBVHVertexIter *vi, int mode); #define BKE_pbvh_vertex_iter_begin(pbvh, node, vi, mode) \ @@ -1075,8 +1065,7 @@ PBVHNode *BKE_pbvh_node_from_face_bmesh(PBVH *pbvh, struct BMFace *f); PBVHNode *BKE_pbvh_node_from_index(PBVH *pbvh, int node_i); struct BMesh *BKE_pbvh_reorder_bmesh(PBVH *pbvh); -void BKE_pbvh_update_vert_boundary(int cd_sculpt_vert, - int cd_faceset_offset, +void BKE_pbvh_update_vert_boundary(int cd_faceset_offset, int cd_vert_node_offset, int cd_face_node_offset, int cd_vcol, @@ -1196,26 +1185,12 @@ bool BKE_pbvh_check_vert_boundary(PBVH *pbvh, struct BMVert *v); void BKE_pbvh_update_vert_boundary_grids(PBVH *pbvh, PBVHVertRef vertex); -#if 0 -# include "DNA_meshdata_types.h" -ATTR_NO_OPT static void MV_ADD_FLAG(MSculptVert *mv, int flag) -{ - if (flag & SCULPTVERT_NEED_BOUNDARY) { - flag |= flag; - } - - mv->flag |= flag; -} -#else -# define MV_ADD_FLAG(mv, flag1) (mv)->flag |= (flag1) -#endif - #if 1 # include "atomic_ops.h" # include # include -/*why is atomic_ops defining near & far macros?*/ +/* Why is atomic_ops defining near & far macros? */ # ifdef near # undef near # endif @@ -1272,7 +1247,7 @@ existing_pbvh can be NULL. Note that all the sculpt customdata layers will be created if they don't exist, so cd_vert/face_node_offset, cd_mask_offset, -cd_sculpt_vert, etc*/ +etc*/ DynTopoState *BKE_dyntopo_init(struct BMesh *bm, PBVH *existing_pbvh); void BKE_dyntopo_free(DynTopoState *ds); void BKE_dyntopo_default_params(DynRemeshParams *params, float edge_size); @@ -1302,7 +1277,6 @@ void BKE_pbvh_set_vemap(PBVH *pbvh, struct MeshElemMap *vemap); void BKE_pbvh_ignore_uvs_set(PBVH *pbvh, bool value); void BKE_pbvh_set_face_areas(PBVH *pbvh, float *face_areas); -void BKE_pbvh_set_sculpt_verts(PBVH *pbvh, struct MSculptVert *sverts); void BKE_pbvh_set_pmap(PBVH *pbvh, SculptPMap *pmap); SculptPMap *BKE_pbvh_get_pmap(PBVH *pbvh); void BKE_pbvh_cache_remove(PBVH *pbvh); diff --git a/source/blender/blenkernel/intern/customdata.cc b/source/blender/blenkernel/intern/customdata.cc index 158e8600d71..74b953e899f 100644 --- a/source/blender/blenkernel/intern/customdata.cc +++ b/source/blender/blenkernel/intern/customdata.cc @@ -1551,70 +1551,6 @@ static bool layerValidate_propfloat2(void *data, const uint totitems, const bool return has_errors; } -static void layerDynTopoVert_default(void *dest, int count) -{ - MSculptVert *mv = static_cast(dest); - - for (int i = 0; i < count; i++, mv++) { - memset(static_cast(mv), 0, sizeof(MSculptVert)); - mv->flag = SCULPTVERT_NEED_TRIANGULATE | SCULPTVERT_NEED_DISK_SORT | SCULPTVERT_NEED_VALENCE; - } -} - -static void layerDynTopoVert_copy(const void *source, void *dest, int count) -{ - memcpy(dest, source, count * sizeof(MSculptVert)); -} - -static void layerDynTopoVert_interp( - const void **sources, const float *weights, const float *sub_weights, int count, void *dest) -{ - float co[3], no[3], origmask, color[4], curv; - MSculptVert *mv = (MSculptVert *)dest; - - // float totweight = 0.0f; - - if (count == 0) { - memset(mv, 0, sizeof(*mv)); - return; - } - - zero_v3(co); - zero_v3(no); - origmask = 0.0f; - curv = 0.0f; - zero_v4(color); - - for (int i = 0; i < count; i++) { - MSculptVert *mv2 = (MSculptVert *)sources[i]; - float w; - - if (i == 0) { // copy flag from first source - mv->flag = mv2->flag; - mv->stroke_id = mv2->stroke_id; - } - - w = weights[i]; - - madd_v3_v3fl(co, mv2->origco, w); - madd_v3_v3fl(no, mv2->origno, w); - madd_v4_v4fl(color, mv2->origcolor, w); - origmask += (float)mv2->origmask * w; - curv += (float)mv2->curv * w; - - // totweight += w; - } - - normalize_v3(no); - - copy_v3_v3(mv->origco, co); - copy_v3_v3(mv->origno, no); - copy_v4_v4(mv->origcolor, color); - - mv->curv = (short)curv; - mv->origmask = (short)origmask; -} - static void layerInterp_noop(const void **, const float *, const float *, int, void *) { // do nothing @@ -2086,17 +2022,7 @@ static const LayerTypeInfo LAYERTYPEINFO[CD_NUMTYPES] = { nullptr}, /* 51: CD_HAIRLENGTH */ {sizeof(float), "float", 1, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr}, - /* 52 CD_DYNTOPO_VERT */ - {sizeof(MSculptVert), - "MSculptVert", - 1, - nullptr, // flag singleton layer - layerDynTopoVert_copy, - nullptr, - layerDynTopoVert_interp, - nullptr, - layerDynTopoVert_default}, - /*53 CD_BMESH_TOOLFLAGS */ + /*52 CD_BMESH_TOOLFLAGS */ {sizeof(MToolFlags), "MToolFlags", 1, @@ -2173,7 +2099,6 @@ static const char *LAYERTYPENAMES[CD_NUMTYPES] = {/* 0-4 */ "CDPropBoolean", /*51-53*/ "CDHairLength", - "CDDyntopoVert", "CDMToolFlags"}; const CustomData_MeshMasks CD_MASK_BAREMESH = { @@ -4315,6 +4240,7 @@ void CustomData_bmesh_free_block(CustomData *data, void **block) } if (data->totsize) { + CustomData_bmesh_asan_unpoison(data, *block); BLI_mempool_free(data->pool, *block); } diff --git a/source/blender/blenkernel/intern/dyntopo.cc b/source/blender/blenkernel/intern/dyntopo.cc index 95b69f9def0..18222c4b5eb 100644 --- a/source/blender/blenkernel/intern/dyntopo.cc +++ b/source/blender/blenkernel/intern/dyntopo.cc @@ -846,7 +846,7 @@ bool check_face_is_tri(PBVH *pbvh, BMFace *f) BMLoop *l = f2->l_first; do { - dyntopo_add_flag(pbvh, l->v, SCULPTVERT_NEED_DISK_SORT | SCULPTVERT_NEED_VALENCE); + dyntopo_add_flag(pbvh, l->v, SCULPTFLAG_NEED_DISK_SORT | SCULPTFLAG_NEED_VALENCE); } while ((l = l->next) != f2->l_first); } @@ -1042,7 +1042,7 @@ bool destroy_nonmanifold_fins(PBVH *pbvh, BMEdge *e_root) BM_face_kill(pbvh->header.bm, f); } - const int mupdateflag = SCULPTVERT_NEED_DISK_SORT | SCULPTVERT_NEED_VALENCE; + const int mupdateflag = SCULPTFLAG_NEED_DISK_SORT | SCULPTFLAG_NEED_VALENCE; for (int i = 0; i < es.size(); i++) { BMEdge *e = es[i]; @@ -1113,7 +1113,7 @@ bool check_vert_fan_are_tris(PBVH *pbvh, BMVert *v) { uint8_t *flag = BM_ELEM_CD_PTR(v, pbvh->cd_flag); - if (!(*flag & SCULPTVERT_NEED_TRIANGULATE)) { + if (!(*flag & SCULPTFLAG_NEED_TRIANGULATE)) { return true; } @@ -1135,7 +1135,7 @@ bool check_vert_fan_are_tris(PBVH *pbvh, BMVert *v) do { pbvh_boundary_update_bmesh(pbvh, l->v); - dyntopo_add_flag(pbvh, l->v, SCULPTVERT_NEED_VALENCE | SCULPTVERT_NEED_DISK_SORT); + dyntopo_add_flag(pbvh, l->v, SCULPTFLAG_NEED_VALENCE | SCULPTFLAG_NEED_DISK_SORT); } while ((l = l->next) != f->l_first); fs.append(f); @@ -1144,7 +1144,7 @@ bool check_vert_fan_are_tris(PBVH *pbvh, BMVert *v) } } - *flag &= ~SCULPTVERT_NEED_TRIANGULATE; + *flag &= ~SCULPTFLAG_NEED_TRIANGULATE; for (int i = 0; i < fs.size(); i++) { /* Triangulation can sometimes delete a face. */ @@ -1323,11 +1323,11 @@ static void unified_edge_queue_create(EdgeQueueContext *eq_ctx, } } - if (dyntopo_test_flag(pbvh, e->v1, SCULPTVERT_NEED_VALENCE)) { + if (dyntopo_test_flag(pbvh, e->v1, SCULPTFLAG_NEED_VALENCE)) { BKE_pbvh_bmesh_update_valence(pbvh, {(intptr_t)e->v1}); } - if (dyntopo_test_flag(pbvh, e->v2, SCULPTVERT_NEED_VALENCE)) { + if (dyntopo_test_flag(pbvh, e->v2, SCULPTFLAG_NEED_VALENCE)) { BKE_pbvh_bmesh_update_valence(pbvh, {(intptr_t)e->v2}); } @@ -1489,7 +1489,7 @@ static void edge_queue_create_local(EdgeQueueContext *eq_ctx, if (!(v->head.hflag & BM_ELEM_TAG)) { v->head.hflag |= BM_ELEM_TAG; - if (dyntopo_test_flag(pbvh, v, SCULPTVERT_NEED_VALENCE)) { + if (dyntopo_test_flag(pbvh, v, SCULPTFLAG_NEED_VALENCE)) { BKE_pbvh_bmesh_update_valence(pbvh, {(intptr_t)v}); } @@ -1663,7 +1663,7 @@ static bool cleanup_valence_3_4(EdgeQueueContext *ectx, const int cd_vert_node = pbvh->cd_vert_node_offset; - int updateflag = SCULPTVERT_NEED_DISK_SORT | SCULPTVERT_NEED_VALENCE; + int updateflag = SCULPTFLAG_NEED_DISK_SORT | SCULPTFLAG_NEED_VALENCE; for (int i = 0; i < ectx->tot_used_verts; i++) { BMVert *v = ectx->used_verts[i]; @@ -2006,7 +2006,7 @@ static bool do_cleanup_3_4(EdgeQueueContext *eq_ctx, continue; } - if (dyntopo_test_flag(pbvh, v, SCULPTVERT_NEED_VALENCE)) { + if (dyntopo_test_flag(pbvh, v, SCULPTFLAG_NEED_VALENCE)) { BKE_pbvh_bmesh_update_valence(pbvh, {(intptr_t)v}); } @@ -2067,7 +2067,6 @@ extern "C" bool BKE_pbvh_bmesh_update_topology(SculptSession *ss, const int cd_vert_mask_offset = CustomData_get_offset(&pbvh->header.bm->vdata, CD_PAINT_MASK); const int cd_vert_node_offset = pbvh->cd_vert_node_offset; const int cd_face_node_offset = pbvh->cd_face_node_offset; - const int cd_sculpt_vert = pbvh->cd_sculpt_vert; bool modified = false; @@ -2128,7 +2127,6 @@ extern "C" bool BKE_pbvh_bmesh_update_topology(SculptSession *ss, eq_ctx.mask_cb = mask_cb; eq_ctx.mask_cb_data = mask_cb_data; - eq_ctx.cd_sculpt_vert = cd_sculpt_vert; eq_ctx.cd_vert_mask_offset = cd_vert_mask_offset; eq_ctx.cd_vert_node_offset = cd_vert_node_offset; eq_ctx.cd_face_node_offset = cd_face_node_offset; @@ -2657,8 +2655,8 @@ static void pbvh_split_edges(EdgeQueueContext *eq_ctx, pbvh_boundary_update_bmesh(pbvh, l2->v); dyntopo_add_flag(pbvh, l2->v, - SCULPTVERT_NEED_VALENCE | SCULPTVERT_NEED_DISK_SORT | - SCULPTVERT_NEED_TRIANGULATE); + SCULPTFLAG_NEED_VALENCE | SCULPTFLAG_NEED_DISK_SORT | + SCULPTFLAG_NEED_TRIANGULATE); } while ((l2 = l2->next) != l->f->l_first); l->f->head.hflag &= ~SPLIT_TAG; @@ -2824,15 +2822,15 @@ static void pbvh_split_edges(EdgeQueueContext *eq_ctx, pbvh_boundary_update_bmesh(pbvh, newv); dyntopo_add_flag(pbvh, newv, - SCULPTVERT_NEED_VALENCE | SCULPTVERT_NEED_DISK_SORT | - SCULPTVERT_NEED_TRIANGULATE); + SCULPTFLAG_NEED_VALENCE | SCULPTFLAG_NEED_DISK_SORT | + SCULPTFLAG_NEED_TRIANGULATE); BMVert *otherv = e->v1 != newv ? e->v1 : e->v2; pbvh_boundary_update_bmesh(pbvh, e->v1 != newv ? e->v1 : e->v2); dyntopo_add_flag(pbvh, otherv, - SCULPTVERT_NEED_DISK_SORT | SCULPTVERT_NEED_VALENCE | - SCULPTVERT_NEED_TRIANGULATE); + SCULPTFLAG_NEED_DISK_SORT | SCULPTFLAG_NEED_VALENCE | + SCULPTFLAG_NEED_TRIANGULATE); BM_ELEM_CD_SET_INT(newv, pbvh->cd_vert_node_offset, DYNTOPO_NODE_NONE); @@ -3124,7 +3122,7 @@ typedef struct DynTopoState { /* existing_pbvh may be nullptr, if so a fake one will be created. Note that all the sculpt customdata layers will be created if they don't exist, so cd_vert/face_node_offset, cd_mask_offset, -cd_sculpt_vert, etc*/ +etc*/ DynTopoState *BKE_dyntopo_init(BMesh *bm, PBVH *existing_pbvh) { PBVH *pbvh; @@ -3153,7 +3151,6 @@ DynTopoState *BKE_dyntopo_init(BMesh *bm, PBVH *existing_pbvh) const bool isfake = pbvh != existing_pbvh; BMCustomLayerReq vlayers[] = {{CD_PAINT_MASK, nullptr, 0}, - {CD_DYNTOPO_VERT, nullptr, CD_FLAG_TEMPORARY | CD_FLAG_NOCOPY}, {CD_PROP_INT32, SCULPT_ATTRIBUTE_NAME(dyntopo_node_id_vertex), CD_FLAG_TEMPORARY | CD_FLAG_NOCOPY}}; @@ -3179,7 +3176,6 @@ DynTopoState *BKE_dyntopo_init(BMesh *bm, PBVH *existing_pbvh) pbvh->cd_face_area = CustomData_get_offset_named( &bm->pdata, CD_PROP_FLOAT2, SCULPT_ATTRIBUTE_NAME(face_areas)); - pbvh->cd_sculpt_vert = CustomData_get_offset(&bm->vdata, CD_DYNTOPO_VERT); pbvh->cd_vert_mask_offset = CustomData_get_offset(&bm->vdata, CD_PAINT_MASK); pbvh->cd_faceset_offset = CustomData_get_offset_named( &bm->pdata, CD_PROP_INT32, ".sculpt_face_set"); @@ -3262,7 +3258,7 @@ void BKE_dyntopo_remesh(DynTopoState *ds, BM_ITER_MESH (v, &iter, ds->pbvh->header.bm, BM_VERTS_OF_MESH) { pbvh_boundary_update_bmesh(ds->pbvh, v); - dyntopo_add_flag(ds->pbvh, v, SCULPTVERT_NEED_TRIANGULATE); + dyntopo_add_flag(ds->pbvh, v, SCULPTFLAG_NEED_TRIANGULATE); BM_ELEM_CD_SET_INT(v, ds->pbvh->cd_valence, BM_vert_edge_count(v)); pbvh_check_vert_boundary(ds->pbvh, v); diff --git a/source/blender/blenkernel/intern/dyntopo_collapse.cc b/source/blender/blenkernel/intern/dyntopo_collapse.cc index 9171ff10305..4b14437eaf3 100644 --- a/source/blender/blenkernel/intern/dyntopo_collapse.cc +++ b/source/blender/blenkernel/intern/dyntopo_collapse.cc @@ -206,7 +206,7 @@ static void collapse_ring_callback_pre(BMElem *elem, void *userdata) case BM_VERT: { BMVert *v = reinterpret_cast(elem); - dyntopo_add_flag(data->pbvh, v, SCULPTVERT_NEED_DISK_SORT | SCULPTVERT_NEED_VALENCE); + dyntopo_add_flag(data->pbvh, v, SCULPTFLAG_NEED_DISK_SORT | SCULPTFLAG_NEED_VALENCE); BM_log_vert_removed(bm, data->pbvh->bm_log, v); pbvh_bmesh_vert_remove(data->pbvh, v); @@ -261,7 +261,7 @@ static void collapse_ring_callback_post(BMElem *elem, void *userdata) case BM_VERT: { BMVert *v = reinterpret_cast(elem); - dyntopo_add_flag(data->pbvh, v, SCULPTVERT_NEED_DISK_SORT | SCULPTVERT_NEED_VALENCE); + dyntopo_add_flag(data->pbvh, v, SCULPTFLAG_NEED_DISK_SORT | SCULPTFLAG_NEED_VALENCE); check_new_elem_id(elem, data); BM_log_vert_added(bm, data->pbvh->bm_log, v); @@ -564,8 +564,8 @@ BMVert *pbvh_bmesh_collapse_edge(PBVH *pbvh, tdata.pbvh = pbvh; tdata.e = e; - const int mupdateflag = SCULPTVERT_NEED_VALENCE | SCULPTVERT_NEED_DISK_SORT; - // updateflag |= SCULPTVERT_NEED_TRIANGULATE; // to check for non-manifold flaps + const int mupdateflag = SCULPTFLAG_NEED_VALENCE | SCULPTFLAG_NEED_DISK_SORT; + // updateflag |= SCULPTFLAG_NEED_TRIANGULATE; // to check for non-manifold flaps validate_edge(pbvh, pbvh->header.bm, e, true, true); diff --git a/source/blender/blenkernel/intern/dyntopo_intern.hh b/source/blender/blenkernel/intern/dyntopo_intern.hh index e5c122cf429..1b03f9e933e 100644 --- a/source/blender/blenkernel/intern/dyntopo_intern.hh +++ b/source/blender/blenkernel/intern/dyntopo_intern.hh @@ -28,6 +28,16 @@ void bm_kill_only_loop(BMesh *bm, BMLoop *l); void bm_kill_only_face(BMesh *bm, BMFace *f); } +static inline bool dyntopo_test_flag(PBVH *pbvh, BMVert *v, uint8_t flag) +{ + return *BM_ELEM_CD_PTR(v, pbvh->cd_flag) & flag; +} + +static inline void dyntopo_add_flag(PBVH *pbvh, BMVert *v, uint8_t flag) +{ + *BM_ELEM_CD_PTR(v, pbvh->cd_flag) |= flag; +} + namespace blender::dyntopo { static int elem_sizes[] = {-1, @@ -63,7 +73,7 @@ inline bool bm_elem_is_free(BMElem *elem, int htype) //#define WITH_ADAPTIVE_CURVATURE //#define DYNTOPO_NO_THREADING -#define SCULPTVERT_VALENCE_TEMP SCULPTVERT_SPLIT_TEMP +#define SCULPTVERT_VALENCE_TEMP SCULPTFLAG_SPLIT_TEMP #define SCULPTVERT_SMOOTH_BOUNDARY \ (SCULPT_BOUNDARY_MESH | SCULPT_BOUNDARY_FACE_SET | SCULPT_BOUNDARY_SHARP | \ @@ -200,7 +210,6 @@ struct EdgeQueueContext { BMesh *bm = nullptr; DyntopoMaskCB mask_cb = nullptr; void *mask_cb_data; - int cd_sculpt_vert; int cd_vert_mask_offset; int cd_vert_node_offset; int cd_face_node_offset; @@ -332,10 +341,10 @@ inline void fix_mesh(PBVH *pbvh, BMesh *bm) BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) { v->e = nullptr; - MSculptVert *mv = BKE_PBVH_SCULPTVERT(pbvh->cd_sculpt_vert, v); - - MV_ADD_FLAG(mv, - SCULPTVERT_NEED_VALENCE | SCULPTVERT_NEED_DISK_SORT | SCULPTVERT_NEED_TRIANGULATE); + dyntopo_add_flag(pbvh, + v, + SCULPTFLAG_NEED_VALENCE | SCULPTFLAG_NEED_DISK_SORT | + SCULPTFLAG_NEED_TRIANGULATE); } BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) { @@ -717,13 +726,3 @@ error: return false; } #endif - -static inline bool dyntopo_test_flag(PBVH *pbvh, BMVert *v, uint8_t flag) -{ - return *BM_ELEM_CD_PTR(v, pbvh->cd_flag) & flag; -} - -static inline void dyntopo_add_flag(PBVH *pbvh, BMVert *v, uint8_t flag) -{ - *BM_ELEM_CD_PTR(v, pbvh->cd_flag) |= flag; -} diff --git a/source/blender/blenkernel/intern/paint.cc b/source/blender/blenkernel/intern/paint.cc index 5dfa8edeb42..791630898c0 100644 --- a/source/blender/blenkernel/intern/paint.cc +++ b/source/blender/blenkernel/intern/paint.cc @@ -1467,8 +1467,6 @@ static void sculptsession_bm_to_me_update_data_only(Object *ob, bool /*reorder*/ BMeshToMeshParams params = {0}; params.update_shapekey_indices = true; - params.cd_mask_extra.vmask = CD_MASK_DYNTOPO_VERT; - BM_mesh_bm_to_me(nullptr, ss->bm, static_cast(ob->data), ¶ms); } } @@ -1546,10 +1544,6 @@ void BKE_sculptsession_free(Object *ob) if (ob && ob->sculpt) { SculptSession *ss = ob->sculpt; - if (ss->msculptverts) { - ss->msculptverts = nullptr; - } - if (ss->bm_idmap) { BM_idmap_destroy(ss->bm_idmap); ss->bm_idmap = nullptr; @@ -2059,7 +2053,7 @@ static void sculpt_update_object( break; } - BKE_sculptsession_check_sculptverts(ob, pbvh, totvert, false); + BKE_sculpt_init_flags_valence(ob, pbvh, totvert, false); if (ss->bm && me->key && ob->shapenr != ss->bm->shapenr) { KeyBlock *actkey = static_cast(BLI_findlink(&me->key->block, ss->bm->shapenr - 1)); @@ -2220,7 +2214,6 @@ static void sculpt_update_object( blender::bke::pbvh::set_flags_valence(ss->pbvh, static_cast(ss->attrs.flags->data), static_cast(ss->attrs.valence->data)); - BKE_pbvh_set_sculpt_verts(ss->pbvh, ss->msculptverts); } } @@ -2572,7 +2565,6 @@ static PBVH *build_pbvh_for_dynamic_topology(Object *ob, bool /*update_sculptver ob->sculpt->bm_idmap, ob->sculpt->attrs.dyntopo_node_id_vertex->bmesh_cd_offset, ob->sculpt->attrs.dyntopo_node_id_face->bmesh_cd_offset, - ob->sculpt->cd_sculpt_vert, ob->sculpt->attrs.face_areas->bmesh_cd_offset, ob->sculpt->attrs.boundary_flags->bmesh_cd_offset, ob->sculpt->attrs.flags ? ob->sculpt->attrs.flags->bmesh_cd_offset : -1, @@ -2601,7 +2593,7 @@ static PBVH *build_pbvh_from_regular_mesh(Object *ob, Mesh *me_eval_deform) PBVH *pbvh = ob->sculpt->pbvh = BKE_pbvh_new(PBVH_FACES); - BKE_sculptsession_check_sculptverts(ob, pbvh, me->totvert, true); + BKE_sculpt_init_flags_valence(ob, pbvh, me->totvert, true); sculpt_check_face_areas(ob, pbvh); BKE_sculptsession_update_attr_refs(ob); BKE_pbvh_set_pmap(pbvh, ss->pmap); @@ -2658,15 +2650,15 @@ static PBVH *build_pbvh_from_ccg(Object *ob, SubdivCCG *subdiv_ccg) BKE_pbvh_set_pmap(pbvh, ss->pmap); int totvert = BKE_pbvh_get_grid_num_verts(pbvh); - BKE_sculptsession_check_sculptverts(ob, pbvh, totvert, true); + BKE_sculpt_init_flags_valence(ob, pbvh, totvert, true); return pbvh; } -extern "C" bool BKE_sculptsession_check_sculptverts(Object *ob, - struct PBVH *pbvh, - int totvert, - bool reset_flags) +extern "C" bool BKE_sculpt_init_flags_valence(Object *ob, + struct PBVH *pbvh, + int totvert, + bool reset_flags) { SculptSession *ss = ob->sculpt; @@ -2686,31 +2678,21 @@ extern "C" bool BKE_sculptsession_check_sculptverts(Object *ob, BMIter iter; BM_ITER_MESH (v, &iter, ss->bm, BM_VERTS_OF_MESH) { - *BM_ELEM_CD_PTR(v, cd_flags) = SCULPTVERT_NEED_VALENCE | - SCULPTVERT_NEED_TRIANGULATE | - SCULPTVERT_NEED_DISK_SORT; + *BM_ELEM_CD_PTR(v, cd_flags) = SCULPTFLAG_NEED_VALENCE | + SCULPTFLAG_NEED_TRIANGULATE | + SCULPTFLAG_NEED_DISK_SORT; } } else { uint8_t *flags = static_cast(ss->attrs.flags->data); for (int i = 0; i < totvert; i++) { - flags[i] = SCULPTVERT_NEED_VALENCE | SCULPTVERT_NEED_TRIANGULATE | - SCULPTVERT_NEED_DISK_SORT; + flags[i] = SCULPTFLAG_NEED_VALENCE | SCULPTFLAG_NEED_TRIANGULATE | + SCULPTFLAG_NEED_DISK_SORT; } } } - if (!ss->attrs.sculpt_vert || !ss->attrs.sculpt_vert->data) { - SculptAttributeParams params = {0}; - - ss->attrs.sculpt_vert = sculpt_attribute_ensure_ex( - ob, ATTR_DOMAIN_POINT, CD_DYNTOPO_VERT, "", ¶ms, BKE_pbvh_type(pbvh)); - } - - ss->msculptverts = static_cast(ss->attrs.sculpt_vert->data); - BKE_pbvh_set_sculpt_verts(pbvh, ss->msculptverts); - blender::bke::pbvh::set_flags_valence(ss->pbvh, static_cast(ss->attrs.flags->data), static_cast(ss->attrs.valence->data)); @@ -2780,8 +2762,6 @@ PBVH *BKE_sculpt_object_pbvh_ensure(Depsgraph *depsgraph, Object *ob) params.ignore_id_layers = false; params.copy_temp_cdlayers = true; - params.cd_mask_extra.vmask = CD_MASK_DYNTOPO_VERT; - BM_mesh_bm_from_me(bm, mesh_orig, ¶ms); ob->sculpt->bm = bm; @@ -3423,7 +3403,7 @@ SculptAttribute *BKE_sculpt_attribute_get(struct Object *ob, return attr; } - if (BKE_pbvh_type(ss->pbvh) == PBVH_GRIDS && domain == ATTR_DOMAIN_POINT) { + if (!ss->pbvh || (BKE_pbvh_type(ss->pbvh) == PBVH_GRIDS && domain == ATTR_DOMAIN_POINT)) { /* Don't pull from customdata for PBVH_GRIDS and vertex domain. * Multires vertex attributes don't go through CustomData. */ @@ -3516,7 +3496,6 @@ static void sculptsession_bmesh_attr_update_internal(Object *ob) } if (ss->pbvh) { - int cd_sculpt_vert = CustomData_get_offset(&ss->bm->vdata, CD_DYNTOPO_VERT); int cd_face_area = ss->attrs.face_areas ? ss->attrs.face_areas->bmesh_cd_offset : -1; int cd_boundary_flags = ss->attrs.boundary_flags ? ss->attrs.boundary_flags->bmesh_cd_offset : -1; @@ -3533,7 +3512,6 @@ static void sculptsession_bmesh_attr_update_internal(Object *ob) BKE_pbvh_update_offsets(ss->pbvh, cd_dyntopo_vert, cd_dyntopo_face, - cd_sculpt_vert, cd_face_area, cd_boundary_flags, cd_flag, @@ -3550,11 +3528,6 @@ static void sculptsession_bmesh_add_layers(Object *ob) SculptSession *ss = ob->sculpt; SculptAttributeParams params = {0}; - if (!ss->attrs.sculpt_vert) { - ss->attrs.sculpt_vert = sculpt_attribute_ensure_ex( - ob, ATTR_DOMAIN_POINT, CD_DYNTOPO_VERT, "", ¶ms, PBVH_BMESH); - } - if (!ss->attrs.face_areas) { SculptAttributeParams params = {0}; ss->attrs.face_areas = sculpt_attribute_ensure_ex(ob, @@ -3588,7 +3561,6 @@ static void sculptsession_bmesh_add_layers(Object *ob) ss->cd_vert_node_offset = ss->attrs.dyntopo_node_id_vertex->bmesh_cd_offset; ss->cd_face_node_offset = ss->attrs.dyntopo_node_id_face->bmesh_cd_offset; ss->cd_face_areas = ss->attrs.face_areas ? ss->attrs.face_areas->bmesh_cd_offset : -1; - ss->cd_sculpt_vert = ss->attrs.sculpt_vert->bmesh_cd_offset; } template static void sculpt_clear_attribute_bmesh(BMesh *bm, SculptAttribute *attr) @@ -3655,7 +3627,6 @@ void BKE_sculpt_attributes_destroy_temporary_stroke(Object *ob) static void update_bmesh_offsets(Mesh *me, SculptSession *ss) { - ss->cd_sculpt_vert = ss->attrs.sculpt_vert ? ss->attrs.sculpt_vert->bmesh_cd_offset : -1; ss->cd_vert_node_offset = ss->attrs.dyntopo_node_id_vertex ? ss->attrs.dyntopo_node_id_vertex->bmesh_cd_offset : -1; @@ -3693,7 +3664,6 @@ static void update_bmesh_offsets(Mesh *me, SculptSession *ss) BKE_pbvh_update_offsets(ss->pbvh, ss->cd_vert_node_offset, ss->cd_face_node_offset, - ss->cd_sculpt_vert, ss->cd_face_areas, cd_boundary_flags, ss->attrs.flags ? ss->attrs.flags->bmesh_cd_offset : -1, diff --git a/source/blender/blenkernel/intern/pbvh.c b/source/blender/blenkernel/intern/pbvh.c index 994569479ed..fa23936afdc 100644 --- a/source/blender/blenkernel/intern/pbvh.c +++ b/source/blender/blenkernel/intern/pbvh.c @@ -179,11 +179,17 @@ static void update_node_vb(PBVH *pbvh, PBVHNode *node, int updateflag) } if (do_orig) { - MSculptVert *mv = pbvh->header.type == PBVH_BMESH ? - BM_ELEM_CD_GET_VOID_P(vd.bm_vert, pbvh->cd_sculpt_vert) : - pbvh->msculptverts + vd.index; + float *origco = nullptr; - if (mv->stroke_id != pbvh->stroke_id) { + if (pbvh->header.type == PBVH_BMESH) { + origco = pbvh->cd_origco != -1 ? BM_ELEM_CD_PTR(vd.bm_vert, pbvh->cd_origco) : + nullptr; + } + else { + origco = pbvh->origco + } + + if (origco) { BB_expand(&orig_vb, vd.co); } else { @@ -985,7 +991,7 @@ void BKE_pbvh_build_mesh(PBVH *pbvh, prim_bbc = MEM_mallocN(sizeof(BBC) * looptri_num, "prim_bbc"); for (int i = 0; i < mesh->totvert; i++) { - msculptverts[i].flag &= ~SCULPTVERT_NEED_VALENCE; + msculptverts[i].flag &= ~SCULPTFLAG_NEED_VALENCE; msculptverts[i].valence = pmap->pmap[i].count; } @@ -4791,7 +4797,7 @@ void BKE_pbvh_update_vert_boundary_grids(PBVH *pbvh, BKE_subdiv_ccg_neighbor_coords_get(subdiv_ccg, &coord, false, &neighbors); mv->valence = neighbors.size; - mv->flag &= ~SCULPTVERT_NEED_VALENCE; + mv->flag &= ~SCULPTFLAG_NEED_VALENCE; } void BKE_pbvh_update_vert_boundary_faces(int *boundary_flags, @@ -4809,7 +4815,7 @@ void BKE_pbvh_update_vert_boundary_faces(int *boundary_flags, MSculptVert *mv = msculptverts + vertex.i; const MeshElemMap *vert_map = &pmap[vertex.i]; - mv->flag &= ~SCULPTVERT_VERT_FSET_HIDDEN; + mv->flag &= ~SCULPTFLAG_VERT_FSET_HIDDEN; int last_fset = -1; int last_fset2 = -1; @@ -4869,7 +4875,7 @@ void BKE_pbvh_update_vert_boundary_faces(int *boundary_flags, } if (!visible) { - mv->flag |= SCULPTVERT_VERT_FSET_HIDDEN; + mv->flag |= SCULPTFLAG_VERT_FSET_HIDDEN; } if (totsharp > 2) { diff --git a/source/blender/blenkernel/intern/pbvh.cc b/source/blender/blenkernel/intern/pbvh.cc index a174faf4eb8..80cef309a7d 100644 --- a/source/blender/blenkernel/intern/pbvh.cc +++ b/source/blender/blenkernel/intern/pbvh.cc @@ -985,8 +985,6 @@ void BKE_pbvh_update_mesh_pointers(PBVH *pbvh, Mesh *mesh) &mesh->vdata, CD_PROP_BOOL, ".hide_vert", mesh->totvert)); pbvh->face_areas = static_cast(CustomData_get_layer_named_for_write( &mesh->pdata, CD_PROP_FLOAT2, SCULPT_ATTRIBUTE_NAME(face_areas), mesh->totpoly)); - pbvh->msculptverts = static_cast( - CustomData_get_layer_for_write(&mesh->vdata, CD_DYNTOPO_VERT, mesh->totvert)); /* Make sure cached normals start out calculated. */ mesh->vert_normals(); @@ -1057,8 +1055,6 @@ void BKE_pbvh_build_mesh(PBVH *pbvh, Mesh *mesh) /* For each face, store the AABB and the AABB centroid */ prim_bbc = (BBC *)MEM_mallocN(sizeof(BBC) * looptri_num, "prim_bbc"); - SculptPMap *pmap = pbvh->pmap; - for (int i = 0; i < looptri_num; i++) { const MLoopTri *lt = &looptri[i]; const int sides = 3; @@ -1525,7 +1521,7 @@ static bool update_search_cb(PBVHNode *node, void *data_v) return true; } -typedef struct PBVHUpdateData { +struct PBVHUpdateData { PBVH *pbvh; Mesh *mesh; Span nodes; @@ -2834,7 +2830,7 @@ static bool pbvh_faces_node_raycast(PBVH *pbvh, PBVHVertRef *r_active_vertex, PBVHFaceRef *r_active_face, float *r_face_normal, - int stroke_id) + int /*stroke_id*/) { const float(*positions)[3] = pbvh->vert_positions; const int *corner_verts = pbvh->corner_verts; @@ -3677,7 +3673,6 @@ void pbvh_vertex_iter_init(PBVH *pbvh, PBVHNode *node, PBVHVertexIter *vi, int m vi->bm_vdata = &pbvh->header.bm->vdata; vi->bm_vert = nullptr; - vi->cd_sculpt_vert = CustomData_get_offset(vi->bm_vdata, CD_DYNTOPO_VERT); vi->cd_vert_mask_offset = CustomData_get_offset(vi->bm_vdata, CD_PAINT_MASK); } @@ -3835,7 +3830,7 @@ int BKE_pbvh_get_totnodes(PBVH *pbvh) return pbvh->totnode; } -int BKE_pbvh_get_node_id(PBVH *pbvh, PBVHNode *node) +int BKE_pbvh_get_node_id(PBVH */*pbvh*/, PBVHNode *node) { return node->id; } @@ -4008,8 +4003,8 @@ void BKE_pbvh_check_tri_areas(PBVH *pbvh, PBVHNode *node) } } -static void pbvh_pmap_to_edges_add(PBVH *pbvh, - PBVHVertRef vertex, +static void pbvh_pmap_to_edges_add(PBVH */*pbvh*/, + PBVHVertRef /*vertex*/, int **r_edges, int *r_edges_size, bool *heap_alloc, @@ -4299,11 +4294,6 @@ void BKE_pbvh_set_symmetry(PBVH *pbvh, int symmetry, int boundary_symmetry) pbvh_boundaries_flag_update(pbvh); } -void BKE_pbvh_set_sculpt_verts(PBVH *pbvh, struct MSculptVert *msculptverts) -{ - pbvh->msculptverts = msculptverts; -} - ATTR_NO_OPT void BKE_pbvh_update_vert_boundary_grids(PBVH *pbvh, PBVHVertRef vertex) { SubdivCCG *subdiv_ccg = pbvh->subdiv_ccg; @@ -4328,7 +4318,7 @@ ATTR_NO_OPT void BKE_pbvh_update_vert_boundary_grids(PBVH *pbvh, PBVHVertRef ver BKE_subdiv_ccg_neighbor_coords_get(subdiv_ccg, &coord, false, &neighbors); pbvh->valence[vertex.i] = neighbors.size; - pbvh->sculpt_flags[vertex.i] &= ~SCULPTVERT_NEED_VALENCE; + pbvh->sculpt_flags[vertex.i] &= ~SCULPTFLAG_NEED_VALENCE; /* Update boundary */ int *boundary_flag = pbvh->boundary_flags + vertex.i; @@ -4385,7 +4375,7 @@ void update_vert_boundary_faces(int *boundary_flags, const MeshElemMap *vert_map = &pmap[vertex.i]; uint8_t *flag = flags + vertex.i; - *flag &= ~SCULPTVERT_VERT_FSET_HIDDEN; + *flag &= ~SCULPTFLAG_VERT_FSET_HIDDEN; int last_fset = -1; int last_fset2 = -1; @@ -4449,7 +4439,7 @@ void update_vert_boundary_faces(int *boundary_flags, } if (!visible) { - *flag |= SCULPTVERT_VERT_FSET_HIDDEN; + *flag |= SCULPTFLAG_VERT_FSET_HIDDEN; } if (totsharp > 2) { diff --git a/source/blender/blenkernel/intern/pbvh_bmesh.cc b/source/blender/blenkernel/intern/pbvh_bmesh.cc index f9213a7ae66..88c4c764e17 100644 --- a/source/blender/blenkernel/intern/pbvh_bmesh.cc +++ b/source/blender/blenkernel/intern/pbvh_bmesh.cc @@ -166,10 +166,9 @@ void pbvh_bmesh_check_nodes(PBVH *pbvh) _debugprint("vert in node->bm_other_verts"); } - MSculptVert *mv = BKE_PBVH_SCULPTVERT(pbvh->cd_sculpt_vert, v); BKE_pbvh_bmesh_check_valence(pbvh, (PBVHVertRef){.i = (intptr_t)v}); - if (BM_vert_edge_count(v) != mv->valence) { + if (BM_vert_edge_count(v) != BM_ELEM_CD_GET_INT(v, pbvh->cd_valence)) { _debugprint("cached vertex valence mismatch; old: %d, should be: %d\n", mv->valence, BM_vert_edge_count(v)); @@ -293,7 +292,7 @@ static BMVert *pbvh_bmesh_vert_create(PBVH *pbvh, BMVert *v = BM_vert_create(pbvh->header.bm, co, nullptr, BM_CREATE_NOP); pbvh_boundary_update_bmesh(pbvh, v); - dyntopo_add_flag(pbvh, v, SCULPTVERT_NEED_DISK_SORT | SCULPTVERT_NEED_VALENCE); + dyntopo_add_flag(pbvh, v, SCULPTFLAG_NEED_DISK_SORT | SCULPTFLAG_NEED_VALENCE); if (v_example) { v->head.hflag = v_example->head.hflag; @@ -416,7 +415,7 @@ BMFace *pbvh_bmesh_face_create(PBVH *pbvh, } pbvh_boundary_update_bmesh(pbvh, l->v); - dyntopo_add_flag(pbvh, l->v, SCULPTVERT_NEED_DISK_SORT | SCULPTVERT_NEED_VALENCE); + dyntopo_add_flag(pbvh, l->v, SCULPTFLAG_NEED_DISK_SORT | SCULPTFLAG_NEED_VALENCE); l = l->next; } while (l != f->l_first); @@ -425,7 +424,7 @@ BMFace *pbvh_bmesh_face_create(PBVH *pbvh, BMLoop *l = f->l_first; do { pbvh_boundary_update_bmesh(pbvh, l->v); - dyntopo_add_flag(pbvh, l->v, SCULPTVERT_NEED_DISK_SORT | SCULPTVERT_NEED_VALENCE); + dyntopo_add_flag(pbvh, l->v, SCULPTFLAG_NEED_DISK_SORT | SCULPTFLAG_NEED_VALENCE); } while ((l = l->next) != f->l_first); } @@ -468,7 +467,7 @@ BMVert *BKE_pbvh_vert_create_bmesh( BM_ELEM_CD_SET_INT(v, pbvh->cd_vert_node_offset, DYNTOPO_NODE_NONE); pbvh_boundary_update_bmesh(pbvh, v); - dyntopo_add_flag(pbvh, v, SCULPTVERT_NEED_DISK_SORT | SCULPTVERT_NEED_VALENCE); + dyntopo_add_flag(pbvh, v, SCULPTFLAG_NEED_DISK_SORT | SCULPTFLAG_NEED_VALENCE); #if 0 /* XXX: do we need to load origco here? */ copy_v3_v3(mv->origco, co); @@ -1538,7 +1537,6 @@ bool pbvh_bmesh_node_nearest_to_ray(SculptSession *ss, BKE_pbvh_bmesh_check_tris(pbvh, node); PBVHTriBuf *tribuf = node->tribuf; - const int cd_sculpt_vert = pbvh->cd_sculpt_vert; for (int i = 0; i < tribuf->tottri; i++) { PBVHTri *tri = tribuf->tris + i; @@ -1559,9 +1557,9 @@ bool pbvh_bmesh_node_nearest_to_ray(SculptSession *ss, BKE_pbvh_bmesh_check_origdata(ss, v2, stroke_id); BKE_pbvh_bmesh_check_origdata(ss, v3, stroke_id); - co1 = BKE_PBVH_SCULPTVERT(cd_sculpt_vert, v1)->origco; - co2 = BKE_PBVH_SCULPTVERT(cd_sculpt_vert, v2)->origco; - co3 = BKE_PBVH_SCULPTVERT(cd_sculpt_vert, v3)->origco; + co1 = BM_ELEM_CD_PTR(v1, pbvh->cd_origco); + co2 = BM_ELEM_CD_PTR(v2, pbvh->cd_origco); + co3 = BM_ELEM_CD_PTR(v3, pbvh->cd_origco); } else { co1 = v1->co; @@ -1578,7 +1576,7 @@ bool pbvh_bmesh_node_nearest_to_ray(SculptSession *ss, struct UpdateNormalsTaskData { PBVHNode *node; Vector border_verts; - int cd_sculpt_vert; + int cd_flag; int cd_vert_node_offset; int cd_face_node_offset; int node_nr; @@ -1604,7 +1602,7 @@ static void pbvh_update_normals_task_cb(void *__restrict userdata, #endif #define NORMAL_VERT_BAD(v) \ (!v->e || BM_ELEM_CD_GET_INT((v), cd_vert_node_offset) != node_nr || \ - (BKE_PBVH_SCULPTVERT(data->cd_sculpt_vert, (v))->flag & SCULPTVERT_PBVH_BOUNDARY)) + ((*BM_ELEM_CD_PTR(v, data->cd_flag)) & SCULPTFLAG_PBVH_BOUNDARY)) const char tag = BM_ELEM_TAG_ALT; @@ -1687,7 +1685,7 @@ void pbvh_bmesh_normals_update(PBVH *pbvh, Span nodes) for (int i : nodes.index_range()) { datas[i].node = nodes[i]; - datas[i].cd_sculpt_vert = pbvh->cd_sculpt_vert; + datas[i].cd_flag = pbvh->cd_flag; datas[i].cd_vert_node_offset = pbvh->cd_vert_node_offset; datas[i].cd_face_node_offset = pbvh->cd_face_node_offset; datas[i].node_nr = nodes[i] - pbvh->nodes; @@ -2077,8 +2075,7 @@ static int color_boundary_key(float col[4]) } #endif -void bke_pbvh_update_vert_boundary(int /*cd_sculpt_vert*/, - int cd_faceset_offset, +void bke_pbvh_update_vert_boundary(int cd_faceset_offset, int cd_vert_node_offset, int cd_face_node_offset, int /*cd_vcol*/, @@ -2094,7 +2091,7 @@ void bke_pbvh_update_vert_boundary(int /*cd_sculpt_vert*/, int boundflag = 0; BMEdge *e = v->e; - newflag &= ~(SCULPTVERT_VERT_FSET_HIDDEN | SCULPTVERT_PBVH_BOUNDARY); + newflag &= ~(SCULPTFLAG_VERT_FSET_HIDDEN | SCULPTFLAG_PBVH_BOUNDARY); if (!e) { boundflag |= SCULPT_BOUNDARY_MESH; @@ -2145,7 +2142,7 @@ void bke_pbvh_update_vert_boundary(int /*cd_sculpt_vert*/, BMVert *v2 = v == e->v1 ? e->v2 : e->v1; if (BM_ELEM_CD_GET_INT(v2, cd_vert_node_offset) != ni) { - newflag |= SCULPTVERT_PBVH_BOUNDARY; + newflag |= SCULPTFLAG_PBVH_BOUNDARY; } if (e->head.hflag & BM_ELEM_SEAM) { @@ -2229,7 +2226,7 @@ void bke_pbvh_update_vert_boundary(int /*cd_sculpt_vert*/, } if (BM_ELEM_CD_GET_INT(e->l->f, cd_face_node_offset) != ni) { - newflag |= SCULPTVERT_PBVH_BOUNDARY; + newflag |= SCULPTFLAG_PBVH_BOUNDARY; } if (e->l != e->l->radial_next) { @@ -2242,12 +2239,12 @@ void bke_pbvh_update_vert_boundary(int /*cd_sculpt_vert*/, } if (BM_ELEM_CD_GET_INT(e->l->radial_next->f, cd_face_node_offset) != ni) { - newflag |= SCULPTVERT_PBVH_BOUNDARY; + newflag |= SCULPTFLAG_PBVH_BOUNDARY; } } if (e->l->f->len > 3) { - newflag |= SCULPTVERT_NEED_TRIANGULATE; + newflag |= SCULPTFLAG_NEED_TRIANGULATE; } if (cd_faceset_offset != -1) { @@ -2287,7 +2284,7 @@ void bke_pbvh_update_vert_boundary(int /*cd_sculpt_vert*/, } if (e->l->radial_next->f->len > 3) { - newflag |= SCULPTVERT_NEED_TRIANGULATE; + newflag |= SCULPTFLAG_NEED_TRIANGULATE; } } } @@ -2330,8 +2327,7 @@ bool BKE_pbvh_check_vert_boundary(PBVH *pbvh, BMVert *v) return pbvh_check_vert_boundary(pbvh, v); } -void BKE_pbvh_update_vert_boundary(int cd_sculpt_vert, - int cd_faceset_offset, +void BKE_pbvh_update_vert_boundary(int cd_faceset_offset, int cd_vert_node_offset, int cd_face_node_offset, int cd_vcol, @@ -2344,8 +2340,7 @@ void BKE_pbvh_update_vert_boundary(int cd_sculpt_vert, const int totuv, const bool do_uvs) { - bke_pbvh_update_vert_boundary(cd_sculpt_vert, - cd_faceset_offset, + bke_pbvh_update_vert_boundary(cd_faceset_offset, cd_vert_node_offset, cd_face_node_offset, cd_vcol, @@ -2365,8 +2360,7 @@ void BKE_pbvh_recalc_bmesh_boundary(PBVH *pbvh) BMIter iter; BM_ITER_MESH (v, &iter, pbvh->header.bm, BM_VERTS_OF_MESH) { - bke_pbvh_update_vert_boundary(pbvh->cd_sculpt_vert, - pbvh->cd_faceset_offset, + bke_pbvh_update_vert_boundary(pbvh->cd_faceset_offset, pbvh->cd_vert_node_offset, pbvh->cd_face_node_offset, pbvh->cd_vcol_offset, @@ -2394,7 +2388,6 @@ void BKE_pbvh_build_bmesh(PBVH *pbvh, BMIdMap *idmap, const int cd_vert_node_offset, const int cd_face_node_offset, - const int cd_sculpt_vert, const int cd_face_areas, const int cd_boundary_flag, const int cd_flag_offset, @@ -2411,7 +2404,6 @@ void BKE_pbvh_build_bmesh(PBVH *pbvh, pbvh->cd_vert_node_offset = cd_vert_node_offset; pbvh->cd_face_node_offset = cd_face_node_offset; pbvh->cd_vert_mask_offset = CustomData_get_offset(&bm->vdata, CD_PAINT_MASK); - pbvh->cd_sculpt_vert = cd_sculpt_vert; pbvh->cd_boundary_flag = cd_boundary_flag; pbvh->cd_origco = cd_origco; pbvh->cd_origno = cd_origno; @@ -3094,7 +3086,7 @@ void BKE_pbvh_bmesh_flag_all_disk_sort(PBVH *pbvh) BMIter iter; BM_ITER_MESH (v, &iter, pbvh->header.bm, BM_VERTS_OF_MESH) { - dyntopo_add_flag(pbvh, v, SCULPTVERT_NEED_DISK_SORT); + dyntopo_add_flag(pbvh, v, SCULPTFLAG_NEED_DISK_SORT); } } @@ -3127,8 +3119,8 @@ void BKE_pbvh_bmesh_on_mesh_change(PBVH *pbvh) dyntopo_add_flag(pbvh, v, - SCULPTVERT_NEED_DISK_SORT | SCULPTVERT_NEED_TRIANGULATE | - SCULPTVERT_NEED_VALENCE); + SCULPTFLAG_NEED_DISK_SORT | SCULPTFLAG_NEED_TRIANGULATE | + SCULPTFLAG_NEED_VALENCE); BKE_pbvh_bmesh_update_valence(pbvh, BKE_pbvh_make_vref((intptr_t)v)); } } @@ -3137,8 +3129,8 @@ bool BKE_pbvh_bmesh_mark_update_valence(PBVH *pbvh, PBVHVertRef vertex) { BMVert *v = (BMVert *)vertex.i; - bool ret = (*BM_ELEM_CD_PTR(v, pbvh->cd_flag)) & SCULPTVERT_NEED_VALENCE; - dyntopo_add_flag(pbvh, v, SCULPTVERT_NEED_VALENCE); + bool ret = (*BM_ELEM_CD_PTR(v, pbvh->cd_flag)) & SCULPTFLAG_NEED_VALENCE; + dyntopo_add_flag(pbvh, v, SCULPTFLAG_NEED_VALENCE); return ret; } @@ -3147,7 +3139,7 @@ bool BKE_pbvh_bmesh_check_valence(PBVH *pbvh, PBVHVertRef vertex) { BMVert *v = (BMVert *)vertex.i; - if (*BM_ELEM_CD_PTR(v, pbvh->cd_flag) & SCULPTVERT_NEED_VALENCE) { + if (*BM_ELEM_CD_PTR(v, pbvh->cd_flag) & SCULPTFLAG_NEED_VALENCE) { BKE_pbvh_bmesh_update_valence(pbvh, vertex); return true; } @@ -3163,7 +3155,7 @@ void BKE_pbvh_bmesh_update_valence(PBVH *pbvh, PBVHVertRef vertex) uint8_t *flag = BM_ELEM_CD_PTR(v, pbvh->cd_flag); uint *valence = BM_ELEM_CD_PTR(v, pbvh->cd_valence); - *flag &= ~SCULPTVERT_NEED_VALENCE; + *flag &= ~SCULPTFLAG_NEED_VALENCE; if (!v->e) { *valence = 0; @@ -4040,7 +4032,6 @@ struct TableGSet *BKE_pbvh_bmesh_node_faces(PBVHNode *node) void BKE_pbvh_update_offsets(PBVH *pbvh, const int cd_vert_node_offset, const int cd_face_node_offset, - const int cd_sculpt_vert, const int cd_face_areas, const int cd_boundary_flag, const int cd_flag, @@ -4053,7 +4044,6 @@ void BKE_pbvh_update_offsets(PBVH *pbvh, pbvh->cd_vert_node_offset = cd_vert_node_offset; pbvh->cd_face_area = cd_face_areas; pbvh->cd_vert_mask_offset = CustomData_get_offset(&pbvh->header.bm->vdata, CD_PAINT_MASK); - pbvh->cd_sculpt_vert = cd_sculpt_vert; pbvh->cd_faceset_offset = CustomData_get_offset_named( &pbvh->header.bm->pdata, CD_PROP_INT32, ".sculpt_face_set"); @@ -5500,7 +5490,6 @@ static void pbvh_bmesh_fetch_cdrefs(PBVH *pbvh) pbvh->cd_vert_mask_offset = CustomData_get_offset(&bm->vdata, CD_PAINT_MASK); pbvh->cd_faceset_offset = CustomData_get_offset_named( &pbvh->header.bm->pdata, CD_PROP_INT32, ".sculpt_face_set"); - pbvh->cd_sculpt_vert = CustomData_get_offset(&bm->vdata, CD_DYNTOPO_VERT); } void BKE_pbvh_bmesh_set_toolflags(PBVH *pbvh, bool use_toolflags) diff --git a/source/blender/blenkernel/intern/pbvh_intern.hh b/source/blender/blenkernel/intern/pbvh_intern.hh index 80c5ce44a7a..9be911fc4d8 100644 --- a/source/blender/blenkernel/intern/pbvh_intern.hh +++ b/source/blender/blenkernel/intern/pbvh_intern.hh @@ -24,7 +24,6 @@ struct PBVHGPUFormat; /** \file * \ingroup bke */ -struct MSculptVert; struct CustomData; struct PBVHTriBuf; @@ -199,7 +198,6 @@ struct PBVH { const MLoopTri *looptri; blender::Span origco, origno; - struct MSculptVert *msculptverts; /* Sculpt flags*/ uint8_t *sculpt_flags; @@ -241,7 +239,6 @@ struct PBVH { float bm_detail_range; struct BMIdMap *bm_idmap; - int cd_sculpt_vert; int cd_flag; int cd_valence; int cd_origco, cd_origno; @@ -435,8 +432,7 @@ void pbvh_bmesh_check_nodes_simple(PBVH *pbvh); void bke_pbvh_insert_face_finalize(PBVH *pbvh, BMFace *f, const int ni); void bke_pbvh_insert_face(PBVH *pbvh, struct BMFace *f); -void bke_pbvh_update_vert_boundary(int cd_sculpt_vert, - int cd_faceset_offset, +void bke_pbvh_update_vert_boundary(int cd_faceset_offset, int cd_vert_node_offset, int cd_face_node_offset, int cd_vcol_offset, @@ -453,8 +449,7 @@ BLI_INLINE bool pbvh_check_vert_boundary(PBVH *pbvh, struct BMVert *v) int *flag = (int *)BM_ELEM_CD_GET_VOID_P(v, pbvh->cd_boundary_flag); if (*flag & SCULPT_BOUNDARY_NEEDS_UPDATE) { - bke_pbvh_update_vert_boundary(pbvh->cd_sculpt_vert, - pbvh->cd_faceset_offset, + bke_pbvh_update_vert_boundary(pbvh->cd_faceset_offset, pbvh->cd_vert_node_offset, pbvh->cd_face_node_offset, pbvh->cd_vcol_offset, diff --git a/source/blender/blenloader/intern/versioning_300.cc b/source/blender/blenloader/intern/versioning_300.cc index f194200f872..2f6129a3272 100644 --- a/source/blender/blenloader/intern/versioning_300.cc +++ b/source/blender/blenloader/intern/versioning_300.cc @@ -4355,9 +4355,9 @@ void blo_do_versions_300(FileData *fd, Library * /*lib*/, Main *bmain) for (int i = 0; i < 4; i++, data++) { for (int j = 0; j < data->totlayer; j++) { - /* CD_DYNTOPO_VERT used to be CD_MESH_ID. */ - if (data->layers[j].type == CD_DYNTOPO_VERT) { - CustomData_free_layer(data, CD_DYNTOPO_VERT, 0, j); + /* CD_TOOLFLAGS used to be CD_MESH_ID. */ + if (data->layers[j].type == 52) { + CustomData_free_layer(data, eCustomDataType(52), 0, j); j--; } } diff --git a/source/blender/bmesh/intern/bmesh_log.cc b/source/blender/bmesh/intern/bmesh_log.cc index 8805d51498c..efeb90e27ff 100644 --- a/source/blender/bmesh/intern/bmesh_log.cc +++ b/source/blender/bmesh/intern/bmesh_log.cc @@ -302,7 +302,7 @@ struct BMLogSetFull : public BMLogSetBase { void swap(BMesh *bm) { - CustomData_MeshMasks cd_mask_extra = {CD_MASK_DYNTOPO_VERT | CD_MASK_SHAPEKEY, 0, 0, 0, 0}; + CustomData_MeshMasks cd_mask_extra = {0, 0, 0, 0, 0}; BMeshToMeshParams params = {}; params.update_shapekey_indices = false; diff --git a/source/blender/editors/mesh/editmesh_mask_extract.cc b/source/blender/editors/mesh/editmesh_mask_extract.cc index 7990dc47723..b23220053de 100644 --- a/source/blender/editors/mesh/editmesh_mask_extract.cc +++ b/source/blender/editors/mesh/editmesh_mask_extract.cc @@ -595,28 +595,28 @@ static int paint_mask_slice_exec(bContext *C, wmOperator *op) &ss->bm->pdata, CD_PROP_INT32, ".sculpt_face_set"); const int cd_boundary_flag = CustomData_get_offset_named( &ss->bm->vdata, CD_PROP_INT32, SCULPT_ATTRIBUTE_NAME(boundary_flags)); + const int cd_flag = CustomData_get_offset_named( + &ss->bm->vdata, CD_PROP_INT8, SCULPT_ATTRIBUTE_NAME(flags)); if (ss->bm && cd_fset != -1) { - const int cd_sculptvert = CustomData_get_offset(&ss->bm->vdata, CD_DYNTOPO_VERT); - BMFace *f; BMVert *v; BMIter iter; const int next_face_set_id = SCULPT_face_set_next_available_get(ss); - const int updateflag = SCULPTVERT_NEED_VALENCE | SCULPTVERT_NEED_TRIANGULATE | - SCULPTVERT_NEED_DISK_SORT; + const int updateflag = SCULPTFLAG_NEED_VALENCE | SCULPTFLAG_NEED_TRIANGULATE | + SCULPTFLAG_NEED_DISK_SORT; BM_ITER_MESH (v, &iter, ss->bm, BM_VERTS_OF_MESH) { - MSculptVert *mv = (MSculptVert *)BM_ELEM_CD_GET_VOID_P(v, cd_sculptvert); - if (cd_boundary_flag != -1) { int *flag = (int *)BM_ELEM_CD_GET_VOID_P(v, cd_boundary_flag); *flag |= SCULPT_BOUNDARY_NEEDS_UPDATE; } - mv->flag |= updateflag; + if (cd_flag != -1) { + *BM_ELEM_CD_PTR(v, cd_flag) |= updateflag; + } } BM_ITER_MESH (f, &iter, ss->bm, BM_FACES_OF_MESH) { diff --git a/source/blender/editors/sculpt_paint/paint_mask.cc b/source/blender/editors/sculpt_paint/paint_mask.cc index 91828df6f58..abcfd667c4b 100644 --- a/source/blender/editors/sculpt_paint/paint_mask.cc +++ b/source/blender/editors/sculpt_paint/paint_mask.cc @@ -1537,7 +1537,6 @@ static void sculpt_gesture_apply_trim(SculptGestureContext *sgcontext) sgcontext->ss->bm_idmap, sgcontext->ss->cd_vert_node_offset, sgcontext->ss->cd_face_node_offset, - sgcontext->ss->cd_sculpt_vert, sgcontext->ss->cd_face_areas, sgcontext->ss->attrs.boundary_flags->bmesh_cd_offset, sgcontext->ss->attrs.flags->bmesh_cd_offset, diff --git a/source/blender/editors/sculpt_paint/sculpt.cc b/source/blender/editors/sculpt_paint/sculpt.cc index 9eedf50c447..bac2a55e891 100644 --- a/source/blender/editors/sculpt_paint/sculpt.cc +++ b/source/blender/editors/sculpt_paint/sculpt.cc @@ -89,6 +89,14 @@ using blender::Vector; using namespace blender::bke::paint; +static bool is_realtime_restored(Brush *brush) +{ + return ((brush->flag & BRUSH_ANCHORED) || + (ELEM(brush->sculpt_tool, SCULPT_TOOL_GRAB, SCULPT_TOOL_ELASTIC_DEFORM) && + BKE_brush_use_size_pressure(brush)) || + (brush->flag & BRUSH_DRAG_DOT)); +} + static float sculpt_calc_radius(ViewContext *vc, const Brush *brush, const Scene *scene, @@ -165,23 +173,6 @@ void SCULPT_face_random_access_ensure(SculptSession *ss) } } -MSculptVert *SCULPT_vertex_get_sculptvert(const SculptSession *ss, PBVHVertRef vertex) -{ - switch (BKE_pbvh_type(ss->pbvh)) { - case PBVH_BMESH: { - BMVert *v = (BMVert *)vertex.i; - return BKE_PBVH_SCULPTVERT(ss->cd_sculpt_vert, v); - } - - case PBVH_GRIDS: - case PBVH_FACES: { - return ss->msculptverts + vertex.i; - } - } - - return nullptr; -} - const float *SCULPT_vertex_origco_get(SculptSession *ss, PBVHVertRef vertex) { return vertex_attr_ptr(vertex, ss->attrs.orig_co); @@ -1182,7 +1173,7 @@ static void sculpt_vertex_neighbors_get_bmesh(const SculptSession *ss, uint8_t flag = *BM_ELEM_CD_PTR(v2, ss->attrs.flags->bmesh_cd_offset); - if (!(flag & SCULPTVERT_VERT_FSET_HIDDEN)) { + if (!(flag & SCULPTFLAG_VERT_FSET_HIDDEN)) { sculpt_vertex_neighbor_add_nocheck(iter, BKE_pbvh_make_vref((intptr_t)v2), BKE_pbvh_make_eref((intptr_t)e), @@ -1286,7 +1277,7 @@ static void sculpt_vertex_neighbors_get_faces_vemap(const SculptSession *ss, unsigned int v = e[0] == (unsigned int)vertex.i ? e[1] : e[0]; int8_t flag = vertex_attr_get(vertex, ss->attrs.flags); - if (flag & SCULPTVERT_VERT_FSET_HIDDEN) { + if (flag & SCULPTFLAG_VERT_FSET_HIDDEN) { /* Skip connectivity from hidden faces. */ continue; } @@ -1950,45 +1941,49 @@ static void paint_mesh_restore_co_task_cb(void *__restrict userdata, BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { SCULPT_vertex_check_origdata(ss, vd.vertex); - MSculptVert *mv = SCULPT_vertex_get_sculptvert(ss, vd.vertex); + float *origco = vertex_attr_ptr(vd.vertex, ss->attrs.orig_co); + float *origno = vertex_attr_ptr(vd.vertex, ss->attrs.orig_no); if (type & SCULPT_UNDO_COORDS) { - if (len_squared_v3v3(vd.co, mv->origco) > FLT_EPSILON) { + if (len_squared_v3v3(vd.co, origco) > FLT_EPSILON) { modified = true; } - copy_v3_v3(vd.co, mv->origco); + copy_v3_v3(vd.co, origco); if (vd.no) { - copy_v3_v3(vd.no, mv->origno); + copy_v3_v3(vd.no, origno); } else { - copy_v3_v3(vd.fno, mv->origno); + copy_v3_v3(vd.fno, origno); } if (vd.is_mesh) { BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); } } - if (type & SCULPT_UNDO_MASK) { - if ((*vd.mask - mv->origmask) * (*vd.mask - mv->origmask) > FLT_EPSILON) { + if ((type & SCULPT_UNDO_MASK) && ss->attrs.orig_mask) { + float origmask = vertex_attr_get(vd.vertex, ss->attrs.orig_mask); + + if ((*vd.mask - origmask) * (*vd.mask - origmask) > FLT_EPSILON) { modified = true; } - *vd.mask = mv->origmask; + *vd.mask = origmask; } - if (type & SCULPT_UNDO_COLOR) { + if ((type & SCULPT_UNDO_COLOR) && ss->attrs.orig_color) { + float *origcolor = vertex_attr_ptr(vd.vertex, ss->attrs.orig_color); float color[4]; if (SCULPT_has_colors(ss)) { SCULPT_vertex_color_get(ss, vd.vertex, color); - if (len_squared_v4v4(color, mv->origcolor) > FLT_EPSILON) { + if (len_squared_v4v4(color, origcolor) > FLT_EPSILON) { modified = true; } - SCULPT_vertex_color_set(ss, vd.vertex, mv->origcolor); + SCULPT_vertex_color_set(ss, vd.vertex, origcolor); } } } @@ -5283,6 +5278,13 @@ static void sculpt_update_cache_invariants( BKE_pbvh_show_orig_set(ss->pbvh, tool_settings->show_origco); } + if (SCULPT_tool_is_paint(brush->sculpt_tool)) { + BKE_sculpt_ensure_origcolor(ob); + } + else if (SCULPT_tool_is_mask(brush->sculpt_tool)) { + BKE_sculpt_ensure_origmask(ob); + } + SCULPT_apply_dyntopo_settings(ss, sd, brush); BKE_pbvh_update_bounds(ss->pbvh, PBVH_UpdateBB | PBVH_UpdateOriginalBB); @@ -6102,11 +6104,7 @@ static void sculpt_restore_mesh(Sculpt *sd, Object *ob) } /* Restore the mesh before continuing with anchored stroke. */ - if ((brush->flag & BRUSH_ANCHORED) || - (ELEM(brush->sculpt_tool, SCULPT_TOOL_GRAB, SCULPT_TOOL_ELASTIC_DEFORM) && - BKE_brush_use_size_pressure(brush)) || - (brush->flag & BRUSH_DRAG_DOT)) { - + if (is_realtime_restored(brush)) { paint_mesh_restore_co(sd, ob); } } diff --git a/source/blender/editors/sculpt_paint/sculpt_api.cc b/source/blender/editors/sculpt_paint/sculpt_api.cc index 152d1e2a2d4..e7374bb7a2a 100644 --- a/source/blender/editors/sculpt_paint/sculpt_api.cc +++ b/source/blender/editors/sculpt_paint/sculpt_api.cc @@ -408,8 +408,7 @@ eSculptCorner SCULPT_vertex_is_corner(const SculptSession *ss, switch (BKE_pbvh_type(ss->pbvh)) { case PBVH_BMESH: { if (needs_update) { - BKE_pbvh_update_vert_boundary(ss->cd_sculpt_vert, - ss->cd_faceset_offset, + BKE_pbvh_update_vert_boundary(ss->cd_faceset_offset, ss->cd_vert_node_offset, ss->cd_face_node_offset, ss->cd_vcol_offset, @@ -458,8 +457,7 @@ eSculptBoundary SCULPT_vertex_is_boundary(const SculptSession *ss, switch (BKE_pbvh_type(ss->pbvh)) { case PBVH_BMESH: { if (needs_update) { - BKE_pbvh_update_vert_boundary(ss->cd_sculpt_vert, - ss->cd_faceset_offset, + BKE_pbvh_update_vert_boundary(ss->cd_faceset_offset, ss->cd_vert_node_offset, ss->cd_face_node_offset, ss->cd_vcol_offset, @@ -540,8 +538,8 @@ int SCULPT_vertex_valence_get(const struct SculptSession *ss, PBVHVertRef vertex SculptVertexNeighborIter ni; uint8_t flag = vertex_attr_get(vertex, ss->attrs.flags); - if (flag & SCULPTVERT_NEED_VALENCE) { - vertex_attr_set(vertex, ss->attrs.flags, flag & ~SCULPTVERT_NEED_VALENCE); + if (flag & SCULPTFLAG_NEED_VALENCE) { + vertex_attr_set(vertex, ss->attrs.flags, flag & ~SCULPTFLAG_NEED_VALENCE); int tot = 0; diff --git a/source/blender/editors/sculpt_paint/sculpt_brush_types.cc b/source/blender/editors/sculpt_paint/sculpt_brush_types.cc index 23817c26a25..6adbb097eee 100644 --- a/source/blender/editors/sculpt_paint/sculpt_brush_types.cc +++ b/source/blender/editors/sculpt_paint/sculpt_brush_types.cc @@ -2897,7 +2897,7 @@ static void do_topology_rake_bmesh_task_cb_ex(void *__restrict userdata, int cd_temp = data->scl->bmesh_cd_offset; SCULPT_bmesh_four_neighbor_average( - ss, avg, direction2, vd.bm_vert, 1.0f, true, cd_temp, ss->cd_sculpt_vert, false); + ss, avg, direction2, vd.bm_vert, 1.0f, true, cd_temp, false); sub_v3_v3v3(val, avg, vd.co); @@ -3019,6 +3019,8 @@ void SCULPT_do_mask_brush_draw(Sculpt *sd, Object *ob, Span nodes) { Brush *brush = BKE_paint_brush(&sd->paint); + BKE_sculpt_ensure_origmask(ob); + /* Threaded loop over nodes. */ SculptThreadedTaskData data{}; data.sd = sd; @@ -3041,6 +3043,7 @@ void SCULPT_do_mask_brush(Sculpt *sd, Object *ob, Span nodes) SCULPT_do_mask_brush_draw(sd, ob, nodes); break; case BRUSH_MASK_SMOOTH: + BKE_sculpt_ensure_origmask(ob); SCULPT_smooth(sd, ob, nodes, ss->cache->bstrength, true); break; } diff --git a/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc b/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc index 2e576005829..2c3de21f996 100644 --- a/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc +++ b/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc @@ -306,8 +306,8 @@ bool SCULPT_dyntopo_check_disk_sort(SculptSession *ss, PBVHVertRef vertex) BMVert *v = (BMVert *)vertex.i; uint8_t *flag = blender::bke::paint::vertex_attr_ptr(vertex, ss->attrs.flags); - if (*flag & SCULPTVERT_NEED_DISK_SORT) { - *flag &= ~SCULPTVERT_NEED_DISK_SORT; + if (*flag & SCULPTFLAG_NEED_DISK_SORT) { + *flag &= ~SCULPTFLAG_NEED_DISK_SORT; BM_sort_disk_cycle(v); diff --git a/source/blender/editors/sculpt_paint/sculpt_face_set.cc b/source/blender/editors/sculpt_paint/sculpt_face_set.cc index a178b6606b5..6e7d5d7ad04 100644 --- a/source/blender/editors/sculpt_paint/sculpt_face_set.cc +++ b/source/blender/editors/sculpt_paint/sculpt_face_set.cc @@ -2279,8 +2279,8 @@ static void sculpt_face_set_extrude_id(Object *ob, BM_mesh_select_mode_set(bm, SCE_SELECT_FACE); - int mupdateflag = SCULPTVERT_NEED_DISK_SORT | SCULPTVERT_NEED_TRIANGULATE | - SCULPTVERT_NEED_VALENCE; + int mupdateflag = SCULPTFLAG_NEED_DISK_SORT | SCULPTFLAG_NEED_TRIANGULATE | + SCULPTFLAG_NEED_VALENCE; Vector retvs, vs; Vector es; @@ -2427,7 +2427,6 @@ static void sculpt_face_set_extrude_id(Object *ob, sculpt_bm_mesh_elem_hflag_disable_all( bm, BM_ALL_NOLOOP, BM_ELEM_SELECT | BM_ELEM_TAG_ALT | BM_ELEM_TAG); - int cd_sculpt_vert = CustomData_get_offset(&bm->vdata, CD_DYNTOPO_VERT); cd_faceset_offset = CustomData_get_offset_named( &bm->pdata, CD_PROP_INT32, ".sculpt_face_set"); // recalc in case bmop changed it @@ -2516,15 +2515,12 @@ static void sculpt_face_set_extrude_id(Object *ob, case BM_FACE: { BMFace *f = (BMFace *)ele; - if (cd_sculpt_vert != -1) { - BMLoop *l = f->l_first; - do { - *(int *)BM_ELEM_CD_GET_VOID_P(l->v, - cd_boundary_flag) |= SCULPT_BOUNDARY_NEEDS_UPDATE; - uint8_t *flag = BM_ELEM_CD_PTR(l->v, ss->attrs.flags->bmesh_cd_offset); - *flag |= mupdateflag; - } while ((l = l->next) != f->l_first); - } + BMLoop *l = f->l_first; + do { + *(int *)BM_ELEM_CD_GET_VOID_P(l->v, cd_boundary_flag) |= SCULPT_BOUNDARY_NEEDS_UPDATE; + uint8_t *flag = BM_ELEM_CD_PTR(l->v, ss->attrs.flags->bmesh_cd_offset); + *flag |= mupdateflag; + } while ((l = l->next) != f->l_first); if (ss->bm) { BKE_pbvh_bmesh_add_face(ss->pbvh, f, true, false); @@ -2558,15 +2554,13 @@ static void sculpt_face_set_extrude_id(Object *ob, continue; } - if (cd_sculpt_vert >= 0) { - BMLoop *l = f->l_first; - do { - *(int *)BM_ELEM_CD_GET_VOID_P(l->v, cd_boundary_flag) |= SCULPT_BOUNDARY_NEEDS_UPDATE; + BMLoop *l = f->l_first; + do { + *(int *)BM_ELEM_CD_GET_VOID_P(l->v, cd_boundary_flag) |= SCULPT_BOUNDARY_NEEDS_UPDATE; - uint8_t *flag = BM_ELEM_CD_PTR(l->v, ss->attrs.flags->bmesh_cd_offset); - *flag |= mupdateflag; - } while ((l = l->next) != f->l_first); - } + uint8_t *flag = BM_ELEM_CD_PTR(l->v, ss->attrs.flags->bmesh_cd_offset); + *flag |= mupdateflag; + } while ((l = l->next) != f->l_first); if (ss->bm && !BM_elem_flag_test(f, tag1)) { BKE_pbvh_bmesh_add_face(ss->pbvh, f, true, false); @@ -2590,8 +2584,6 @@ static void sculpt_face_set_extrude_id(Object *ob, continue; } - const int cd_sculpt_vert = CustomData_get_offset(&bm->vdata, CD_DYNTOPO_VERT); - BMLoop *l = f->l_first; do { @@ -2599,10 +2591,8 @@ static void sculpt_face_set_extrude_id(Object *ob, *(int *)BM_ELEM_CD_GET_VOID_P(l->v, cd_boundary_flag) |= SCULPT_BOUNDARY_NEEDS_UPDATE; } - if (cd_sculpt_vert != -1) { - uint8_t *flag = BM_ELEM_CD_PTR(l->v, ss->attrs.flags->bmesh_cd_offset); - *flag |= mupdateflag; - } + uint8_t *flag = BM_ELEM_CD_PTR(l->v, ss->attrs.flags->bmesh_cd_offset); + *flag |= mupdateflag; } while ((l = l->next) != f->l_first); } diff --git a/source/blender/editors/sculpt_paint/sculpt_filter_color.cc b/source/blender/editors/sculpt_paint/sculpt_filter_color.cc index 08a190d76af..3351a66dc58 100644 --- a/source/blender/editors/sculpt_paint/sculpt_filter_color.cc +++ b/source/blender/editors/sculpt_paint/sculpt_filter_color.cc @@ -363,6 +363,8 @@ static int sculpt_color_filter_init(bContext *C, wmOperator *op) SCULPT_undo_push_begin(ob, op); BKE_sculpt_color_layer_create_if_needed(ob); + BKE_sculpt_ensure_origcolor(ob); + /* CTX_data_ensure_evaluated_depsgraph should be used at the end to include the updates of * earlier steps modifying the data. */ Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); diff --git a/source/blender/editors/sculpt_paint/sculpt_filter_mask.cc b/source/blender/editors/sculpt_paint/sculpt_filter_mask.cc index 79f251e20f1..f766c3fac26 100644 --- a/source/blender/editors/sculpt_paint/sculpt_filter_mask.cc +++ b/source/blender/editors/sculpt_paint/sculpt_filter_mask.cc @@ -1048,6 +1048,7 @@ static int sculpt_ipmask_filter_invoke(bContext *C, wmOperator *op, const wmEven SCULPT_undo_push_begin(ob, op); BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true, false); + BKE_sculpt_ensure_origmask(ob); const eSculptIPMaskFilterType filter_type = (eSculptIPMaskFilterType)RNA_enum_get(op->ptr, "filter_type"); @@ -1073,6 +1074,8 @@ static int sculpt_ipmask_filter_exec(bContext *C, wmOperator *op) SCULPT_undo_push_begin(ob, op); BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true, false); + BKE_sculpt_ensure_origmask(ob); + ss->filter_cache = sculpt_ipmask_filter_cache_init(ob, sd, filter_type, false); sculpt_ipmask_store_initial_undo_step(ob); sculpt_ipmask_store_reference_step(ss); diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.hh b/source/blender/editors/sculpt_paint/sculpt_intern.hh index d7e53d14610..f4690775586 100644 --- a/source/blender/editors/sculpt_paint/sculpt_intern.hh +++ b/source/blender/editors/sculpt_paint/sculpt_intern.hh @@ -410,7 +410,7 @@ struct SculptThreadedTaskData { ThreadMutex mutex; // Layer brush - int cd_temp, cd_temp2, cd_temp3, cd_sculpt_vert; + int cd_temp, cd_temp2, cd_temp3; float smooth_projection; float rake_projection; @@ -1386,8 +1386,6 @@ bool SCULPT_face_select_get(SculptSession *ss, PBVHFaceRef face); /** \name Original Data API * \{ */ -MSculptVert *SCULPT_vertex_get_sculptvert(const SculptSession *ss, PBVHVertRef vertex); - /** * DEPRECATED: use SCULPT_vertex_check_origdata and SCULPT_vertex_get_sculptvert * Initialize a #SculptOrigVertData for accessing original vertex data; @@ -1876,7 +1874,6 @@ void SCULPT_bmesh_four_neighbor_average(SculptSession *ss, float projection, bool check_fsets, int cd_temp, - int cd_sculpt_vert, bool do_origco); void SCULPT_neighbor_coords_average(SculptSession *ss, diff --git a/source/blender/editors/sculpt_paint/sculpt_paint_color.cc b/source/blender/editors/sculpt_paint/sculpt_paint_color.cc index 1ba79fc7c54..ecd0e9f2e20 100644 --- a/source/blender/editors/sculpt_paint/sculpt_paint_color.cc +++ b/source/blender/editors/sculpt_paint/sculpt_paint_color.cc @@ -307,6 +307,8 @@ void SCULPT_do_paint_brush(PaintModeSettings *paint_mode_settings, return; } + BKE_sculpt_ensure_origcolor(ob); + if (SCULPT_stroke_is_first_brush_step_of_symmetry_pass(ss->cache)) { if (SCULPT_stroke_is_first_brush_step(ss->cache)) { ss->cache->density_seed = float(BLI_hash_int_01(ss->cache->location[0] * 1000)); @@ -402,6 +404,8 @@ void SCULPT_do_paint_brush(PaintModeSettings *paint_mode_settings, SculptAttributeParams params_id = {}; params_id.nointerp = params.stroke_only = true; + BKE_sculpt_ensure_origcolor(ob); + if (!ss->attrs.smear_previous) { ss->attrs.smear_previous = BKE_sculpt_attribute_ensure( ob, ATTR_DOMAIN_POINT, CD_PROP_COLOR, SCULPT_ATTRIBUTE_NAME(smear_previous), ¶ms); diff --git a/source/blender/editors/sculpt_paint/sculpt_smooth.cc b/source/blender/editors/sculpt_paint/sculpt_smooth.cc index f97670624a7..4ec13e6cc87 100644 --- a/source/blender/editors/sculpt_paint/sculpt_smooth.cc +++ b/source/blender/editors/sculpt_paint/sculpt_smooth.cc @@ -189,7 +189,6 @@ void SCULPT_bmesh_four_neighbor_average(SculptSession *ss, float projection, bool check_fsets, int cd_temp, - int cd_sculpt_vert, bool do_origco) { float avg_co[3] = {0.0f, 0.0f, 0.0f}; diff --git a/source/blender/editors/sculpt_paint/sculpt_undo.cc b/source/blender/editors/sculpt_paint/sculpt_undo.cc index 6d341dfdb90..7140a9e41db 100644 --- a/source/blender/editors/sculpt_paint/sculpt_undo.cc +++ b/source/blender/editors/sculpt_paint/sculpt_undo.cc @@ -685,7 +685,7 @@ typedef struct BmeshUndoData { bool balance_pbvh; int cd_face_node_offset, cd_vert_node_offset; int cd_face_node_offset_old, cd_vert_node_offset_old; - int cd_sculpt_vert, cd_boundary_flag, cd_flags; + int cd_boundary_flag, cd_flags; bool regen_all_unique_verts; bool is_redo; } BmeshUndoData; @@ -724,9 +724,9 @@ static void bmesh_undo_on_vert_add(BMVert *v, void *userdata) BM_ELEM_CD_SET_INT(v, data->cd_vert_node_offset, -1); *(int *)BM_ELEM_CD_GET_VOID_P(v, data->cd_boundary_flag) |= SCULPT_BOUNDARY_NEEDS_UPDATE; - *BM_ELEM_CD_PTR(v, data->cd_flags) |= SCULPTVERT_NEED_DISK_SORT | - SCULPTVERT_NEED_VALENCE | - SCULPTVERT_NEED_TRIANGULATE; + *BM_ELEM_CD_PTR(v, data->cd_flags) |= SCULPTFLAG_NEED_DISK_SORT | + SCULPTFLAG_NEED_VALENCE | + SCULPTFLAG_NEED_TRIANGULATE; } static void bmesh_undo_on_face_kill(BMFace *f, void *userdata) @@ -743,8 +743,8 @@ static void bmesh_undo_on_face_kill(BMFace *f, void *userdata) BMLoop *l = f->l_first; do { - *BM_ELEM_CD_PTR(l->v, data->cd_flags) |= SCULPTVERT_NEED_DISK_SORT | - SCULPTVERT_NEED_VALENCE; + *BM_ELEM_CD_PTR(l->v, data->cd_flags) |= SCULPTFLAG_NEED_DISK_SORT | + SCULPTFLAG_NEED_VALENCE; *BM_ELEM_CD_PTR(l->v, data->cd_boundary_flag) |= SCULPT_BOUNDARY_NEEDS_UPDATE; } while ((l = l->next) != f->l_first); @@ -767,11 +767,11 @@ static void bmesh_undo_on_face_add(BMFace *f, void *userdata) do { *(int *)BM_ELEM_CD_GET_VOID_P(l->v, data->cd_boundary_flag) |= SCULPT_BOUNDARY_NEEDS_UPDATE; - *BM_ELEM_CD_PTR(l->v, data->cd_flags) |= SCULPTVERT_NEED_DISK_SORT | - SCULPTVERT_NEED_VALENCE; + *BM_ELEM_CD_PTR(l->v, data->cd_flags) |= SCULPTFLAG_NEED_DISK_SORT | + SCULPTFLAG_NEED_VALENCE; if (f->len > 3) { - *BM_ELEM_CD_PTR(l->v, data->cd_flags) |= SCULPTVERT_NEED_TRIANGULATE; + *BM_ELEM_CD_PTR(l->v, data->cd_flags) |= SCULPTFLAG_NEED_TRIANGULATE; } int ni_l = BM_ELEM_CD_GET_INT(l->v, data->cd_vert_node_offset); @@ -810,12 +810,12 @@ static void bmesh_undo_on_edge_kill(BMEdge *e, void *userdata) *(int *)BM_ELEM_CD_GET_VOID_P(e->v1, data->cd_boundary_flag) |= SCULPT_BOUNDARY_NEEDS_UPDATE; *(int *)BM_ELEM_CD_GET_VOID_P(e->v2, data->cd_boundary_flag) |= SCULPT_BOUNDARY_NEEDS_UPDATE; - *BM_ELEM_CD_PTR(e->v1, data->cd_flags) |= SCULPTVERT_NEED_DISK_SORT | - SCULPTVERT_NEED_VALENCE | - SCULPTVERT_NEED_TRIANGULATE; - *BM_ELEM_CD_PTR(e->v2, data->cd_flags) |= SCULPTVERT_NEED_DISK_SORT | - SCULPTVERT_NEED_VALENCE | - SCULPTVERT_NEED_TRIANGULATE; + *BM_ELEM_CD_PTR(e->v1, data->cd_flags) |= SCULPTFLAG_NEED_DISK_SORT | + SCULPTFLAG_NEED_VALENCE | + SCULPTFLAG_NEED_TRIANGULATE; + *BM_ELEM_CD_PTR(e->v2, data->cd_flags) |= SCULPTFLAG_NEED_DISK_SORT | + SCULPTFLAG_NEED_VALENCE | + SCULPTFLAG_NEED_TRIANGULATE; }; ; static void bmesh_undo_on_edge_add(BMEdge *e, void *userdata) @@ -825,12 +825,12 @@ static void bmesh_undo_on_edge_add(BMEdge *e, void *userdata) *(int *)BM_ELEM_CD_GET_VOID_P(e->v1, data->cd_boundary_flag) |= SCULPT_BOUNDARY_NEEDS_UPDATE; *(int *)BM_ELEM_CD_GET_VOID_P(e->v2, data->cd_boundary_flag) |= SCULPT_BOUNDARY_NEEDS_UPDATE; - *BM_ELEM_CD_PTR(e->v1, data->cd_flags) |= SCULPTVERT_NEED_DISK_SORT | - SCULPTVERT_NEED_VALENCE | - SCULPTVERT_NEED_TRIANGULATE; - *BM_ELEM_CD_PTR(e->v2, data->cd_flags) |= SCULPTVERT_NEED_DISK_SORT | - SCULPTVERT_NEED_VALENCE | - SCULPTVERT_NEED_TRIANGULATE; + *BM_ELEM_CD_PTR(e->v1, data->cd_flags) |= SCULPTFLAG_NEED_DISK_SORT | + SCULPTFLAG_NEED_VALENCE | + SCULPTFLAG_NEED_TRIANGULATE; + *BM_ELEM_CD_PTR(e->v2, data->cd_flags) |= SCULPTFLAG_NEED_DISK_SORT | + SCULPTFLAG_NEED_VALENCE | + SCULPTFLAG_NEED_TRIANGULATE; } static void bmesh_undo_on_vert_change(BMVert *v, void *userdata, void *old_customdata) @@ -957,7 +957,6 @@ static void sculpt_undo_bmesh_restore_generic(SculptUndoNode *unode, Object *ob, ss->cd_vert_node_offset, -1, -1, - ss->cd_sculpt_vert, ss->attrs.boundary_flags->bmesh_cd_offset, ss->attrs.flags->bmesh_cd_offset, false, @@ -3162,7 +3161,6 @@ extern "C" void SCULPT_substep_undo(bContext * /*C*/, int /*dir*/) false, ss->cd_face_node_offset, ss->cd_vert_node_offset, - ss->cd_sculpt_vert, ss->attrs.boundary_flags->bmesh_cd_offset, false, false}; @@ -3205,7 +3203,6 @@ void ED_sculpt_fast_save_bmesh(Object *ob) Main * bmain, Object * ob, BMesh * bm, Mesh * me, const struct BMeshToMeshParams *params); params.update_shapekey_indices = true; - params.cd_mask_extra.vmask = CD_MASK_DYNTOPO_VERT; // BM_mesh_bm_to_me_threaded(nullptr, ob, bm, (Mesh *)ob->data, ¶ms); BM_mesh_bm_to_me(nullptr, bm, (Mesh *)ob->data, ¶ms); diff --git a/source/blender/makesdna/DNA_customdata_types.h b/source/blender/makesdna/DNA_customdata_types.h index 88277d767f2..42c26a52094 100644 --- a/source/blender/makesdna/DNA_customdata_types.h +++ b/source/blender/makesdna/DNA_customdata_types.h @@ -87,12 +87,12 @@ typedef struct CustomData { * MUST be >= CD_NUMTYPES, but we can't use a define here. * Correct size is ensured in CustomData_update_typemap assert(). */ - int typemap[54]; + int typemap[53]; /** Number of layers, size of layers array. */ int totlayer, maxlayer; /** In editmode, total size of all data layers. */ - int totsize, _pad[1]; + int totsize; /** (BMesh Only): Memory pool for allocation of blocks. */ struct BLI_mempool *pool; /** External file storing custom-data layers. */ @@ -180,9 +180,8 @@ typedef enum eCustomDataType { CD_PROP_BOOL = 50, CD_HAIRLENGTH = 51, - CD_DYNTOPO_VERT = 52, - CD_TOOLFLAGS = 53, - CD_NUMTYPES = 54, + CD_TOOLFLAGS = 52, + CD_NUMTYPES = 53, } eCustomDataType; /* Bits for eCustomDataMask */ @@ -227,9 +226,8 @@ typedef enum eCustomDataType { #define CD_MASK_PROP_INT8 (1ULL << CD_PROP_INT8) #define CD_MASK_PROP_INT32_2D (1ULL << CD_PROP_INT32_2D) -#define CD_MASK_DYNTOPO_VERT (1ULL << CD_DYNTOPO_VERT) -#define CD_MASK_HAIRLENGTH (1ULL << CD_HAIRLENGTH) #define CD_MASK_TOOLFLAGS (1ULL << CD_TOOLFLAGS) +#define CD_MASK_HAIRLENGTH (1ULL << CD_HAIRLENGTH) /** Multi-resolution loop data. */ #define CD_MASK_MULTIRES_GRIDS (CD_MASK_MDISPS | CD_GRID_PAINT_MASK) diff --git a/source/blender/makesdna/DNA_meshdata_types.h b/source/blender/makesdna/DNA_meshdata_types.h index 5ca29b4b631..056e349b3c1 100644 --- a/source/blender/makesdna/DNA_meshdata_types.h +++ b/source/blender/makesdna/DNA_meshdata_types.h @@ -528,41 +528,14 @@ typedef struct MRecast { /** \} */ -typedef struct MSculptVert { - unsigned short valence; - - /* id of current stroke, used to detect - if vertex original data needs to be updated. - */ - short stroke_id; - - int flag; - - /**original coordinates*/ - float origco[3], origno[3]; - - /**original color*/ - float origcolor[4]; - - unsigned short origmask; - - /* curv is a fast curvature approximation used by dyntopo - adaptive curvature. */ - unsigned short curv; - - /* curvature_dir parallels a principle curvature direction */ - float curvature_dir[3]; -} MSculptVert; - -/* MSculptVert->flag */ -enum { - SCULPTVERT_VERT_FSET_HIDDEN = (1 << 1), - SCULPTVERT_NEED_TRIANGULATE = (1 << 4), - SCULPTVERT_NEED_DISK_SORT = (1 << 5), - SCULPTVERT_NEED_VALENCE = (1 << 6), - SCULPTVERT_SPLIT_TEMP = (1 << 13), - SCULPTVERT_PBVH_BOUNDARY = (1 << 14), -}; +typedef enum eSculptFlag { + SCULPTFLAG_VERT_FSET_HIDDEN = (1 << 1), + SCULPTFLAG_NEED_TRIANGULATE = (1 << 2), + SCULPTFLAG_NEED_DISK_SORT = (1 << 3), + SCULPTFLAG_NEED_VALENCE = (1 << 4), + SCULPTFLAG_SPLIT_TEMP = (1 << 5), + SCULPTFLAG_PBVH_BOUNDARY = (1 << 6), +} eSculptFlag; /* for internal bmesh toolflags api */ typedef struct MToolFlags { -- 2.30.2 From fce8273d08c97068f73a3822a704a02af91407fd Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Tue, 2 May 2023 22:52:22 -0700 Subject: [PATCH 075/279] temp-sculpt-dyntopo: Fix dyntopo temp attribute bug --- .../editors/sculpt_paint/sculpt_dyntopo.cc | 61 +++++++++---------- 1 file changed, 30 insertions(+), 31 deletions(-) diff --git a/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc b/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc index 2c3de21f996..9a0fef7bd1a 100644 --- a/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc +++ b/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc @@ -467,10 +467,10 @@ static void customdata_strip_templayers(CustomData *cdata, int totelem) } } -void SCULPT_dynamic_topology_enable_ex(Main *bmain, - Depsgraph *depsgraph, - Scene * /*scene*/, - Object *ob) +ATTR_NO_OPT void SCULPT_dynamic_topology_enable_ex(Main *bmain, + Depsgraph *depsgraph, + Scene * /*scene*/, + Object *ob) { SculptSession *ss = ob->sculpt; Mesh *me = BKE_object_get_original_mesh(ob); @@ -505,6 +505,7 @@ void SCULPT_dynamic_topology_enable_ex(Main *bmain, /* Dynamic topology doesn't ensure selection state is valid, so remove #36280. */ BKE_mesh_mselect_clear(me); + bool tag_update = false; if (!ss->bm) { ss->bm = BKE_sculptsession_empty_bmesh_create(); @@ -516,46 +517,24 @@ void SCULPT_dynamic_topology_enable_ex(Main *bmain, params.active_shapekey = ob->shapenr; BM_mesh_bm_from_me(ss->bm, me, ¶ms); - - if (ss->pbvh) { - BKE_sculptsession_update_attr_refs(ob); - BKE_pbvh_set_bmesh(ss->pbvh, ss->bm); - } + tag_update = true; } #ifndef DYNTOPO_DYNAMIC_TESS SCULPT_dynamic_topology_triangulate(ss, ss->bm); #endif - if (ss->pbvh) { - BKE_sculpt_ensure_sculpt_layers(ob); - BKE_sculpt_ensure_origco(ob); - blender::bke::paint::load_all_original(ob); - } - - if (ss->pbvh && SCULPT_has_persistent_base(ss)) { - SCULPT_ensure_persistent_layers(ss, ob); - } - - if (!CustomData_has_layer(&ss->bm->vdata, CD_PAINT_MASK)) { - BM_data_layer_add(ss->bm, &ss->bm->vdata, CD_PAINT_MASK); - } - if (ss->pbvh) { BKE_sculptsession_update_attr_refs(ob); } + /* XXX Delete this block of code? Might be old fake quadrangulation edge hiding. */ BMIter iter; BMEdge *e; - BM_ITER_MESH (e, &iter, ss->bm, BM_EDGES_OF_MESH) { e->head.hflag |= BM_ELEM_DRAW; } - if (ss->pbvh) { - blender::bke::paint::load_all_original(ob); - } - /* Make sure the data for existing faces are initialized. */ if (me->totpoly != ss->bm->totface) { BM_mesh_normals_update(ss->bm); @@ -574,14 +553,34 @@ void SCULPT_dynamic_topology_enable_ex(Main *bmain, ss->bm_log = BM_log_create(ss->bm, ss->bm_idmap); } + tag_update |= !ss->pbvh || BKE_pbvh_type(ss->pbvh) != PBVH_BMESH; + /* Update dependency graph, so modifiers that depend on dyntopo being enabled * are re-evaluated and the PBVH is re-created. */ - if (!ss->pbvh || BKE_pbvh_type(ss->pbvh) != PBVH_BMESH) { + if (tag_update) { DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); + BKE_scene_graph_update_tagged(depsgraph, bmain); } - // TODO: this line here is being slow, do we need it? - joeedh - BKE_scene_graph_update_tagged(depsgraph, bmain); + /* ss->pbvh should exist by this point. */ + + if (ss->pbvh) { + BKE_sculpt_ensure_sculpt_layers(ob); + BKE_sculpt_ensure_origco(ob); + blender::bke::paint::load_all_original(ob); + } + + if (ss->pbvh && SCULPT_has_persistent_base(ss)) { + SCULPT_ensure_persistent_layers(ss, ob); + } + + if (!CustomData_has_layer(&ss->bm->vdata, CD_PAINT_MASK)) { + BM_data_layer_add(ss->bm, &ss->bm->vdata, CD_PAINT_MASK); + } + + if (ss->pbvh) { + blender::bke::paint::load_all_original(ob); + } } /* Free the sculpt BMesh and BMLog -- 2.30.2 From e741fba3c60b9f22506baf100f54df8fdac394f1 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Tue, 2 May 2023 23:09:14 -0700 Subject: [PATCH 076/279] temp-sculpt-dyntopo: Cleanup a comment --- source/blender/editors/sculpt_paint/sculpt_dyntopo.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc b/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc index 9a0fef7bd1a..2ca2f4d0c7c 100644 --- a/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc +++ b/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc @@ -448,8 +448,8 @@ void SCULPT_pbvh_clear(Object *ob) } /** - Syncs customdata layers with internal bmesh, but ignores deleted layers. -*/ + * Syncs customdata layers with internal bmesh, but ignores deleted layers. + */ void SCULPT_dynamic_topology_sync_layers(Object *ob, Mesh *me) { BKE_sculptsession_sync_attributes(ob, me); -- 2.30.2 From 18de7e52817b3aa8443a854575dc1390c7dfac28 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Tue, 2 May 2023 23:34:24 -0700 Subject: [PATCH 077/279] temp-sculpt-dyntopo: Run clang-format --- source/blender/blenkernel/BKE_paint.h | 10 +++--- source/blender/blenkernel/BKE_pbvh.h | 1 - source/blender/blenkernel/BKE_sculpt.hh | 3 +- source/blender/blenkernel/intern/attribute.cc | 1 - .../blender/blenkernel/intern/customdata.cc | 6 ++-- source/blender/blenkernel/intern/dyntopo.cc | 32 ++++++++++++------- .../blenkernel/intern/dyntopo_collapse.cc | 9 ++++-- source/blender/blenkernel/intern/paint.cc | 3 +- source/blender/blenkernel/intern/pbvh.c | 30 +++++++++++------ source/blender/blenkernel/intern/pbvh.cc | 20 +++++++----- .../blender/blenkernel/intern/pbvh_bmesh.cc | 17 ++++++---- .../blender/blenkernel/intern/pbvh_colors.cc | 2 +- source/blender/blenlib/BLI_heap_minmax.hh | 4 +-- .../blenloader/intern/versioning_300.cc | 6 ++-- source/blender/bmesh/intern/bmesh_core.c | 4 +-- source/blender/bmesh/intern/bmesh_idmap.cc | 2 +- source/blender/bmesh/intern/bmesh_mesh.cc | 2 +- source/blender/draw/DRW_pbvh.hh | 2 +- .../engines/overlay/overlay_mode_transfer.cc | 3 +- .../editors/sculpt_paint/paint_intern.h | 3 +- .../editors/sculpt_paint/sculpt_api.cc | 6 ++-- .../editors/sculpt_paint/sculpt_face_set.cc | 17 ++++++---- .../sculpt_paint/sculpt_face_set_topology.c | 5 +-- .../sculpt_paint/sculpt_filter_mask.cc | 3 +- .../editors/sculpt_paint/sculpt_geodesic.cc | 6 ++-- .../editors/sculpt_paint/sculpt_ops.cc | 5 +-- .../editors/sculpt_paint/sculpt_poly_loop.c | 12 ++++--- .../editors/sculpt_paint/sculpt_replay.c | 4 +-- .../editors/sculpt_paint/sculpt_smooth.cc | 6 ++-- .../editors/sculpt_paint/sculpt_transform.cc | 3 +- .../editors/sculpt_paint/sculpt_undo.cc | 6 ++-- source/blender/makesrna/intern/rna_scene.c | 1 - 32 files changed, 140 insertions(+), 94 deletions(-) diff --git a/source/blender/blenkernel/BKE_paint.h b/source/blender/blenkernel/BKE_paint.h index c3caf4f7ef8..16390b98609 100644 --- a/source/blender/blenkernel/BKE_paint.h +++ b/source/blender/blenkernel/BKE_paint.h @@ -718,8 +718,8 @@ typedef struct SculptAttributePointers { /* Sculpt utility attributes. */ SculptAttribute *stroke_id; SculptAttribute *boundary_flags; - SculptAttribute *valence; /* CD_PROP_INT32, vert */ - SculptAttribute *flags; /* CD_PROP_INT8, vert */ + SculptAttribute *valence; /* CD_PROP_INT32, vert */ + SculptAttribute *flags; /* CD_PROP_INT8, vert */ SculptAttribute *orig_co, *orig_no; /* CD_PROP_FLOAT3, vert */ SculptAttribute *orig_fsets; /* CD_PROP_INT32, face */ @@ -1081,9 +1081,9 @@ void BKE_sculpt_attribute_destroy_temporary_all(struct Object *ob); void BKE_sculpt_attributes_destroy_temporary_stroke(struct Object *ob); bool BKE_sculpt_init_flags_valence(struct Object *ob, - struct PBVH *pbvh, - int totvert, - bool reset_flags); + struct PBVH *pbvh, + int totvert, + bool reset_flags); struct BMesh *BKE_sculptsession_empty_bmesh_create(void); void BKE_sculptsession_bmesh_attr_update_internal(struct Object *ob); diff --git a/source/blender/blenkernel/BKE_pbvh.h b/source/blender/blenkernel/BKE_pbvh.h index 9ca5649c549..28f1036715a 100644 --- a/source/blender/blenkernel/BKE_pbvh.h +++ b/source/blender/blenkernel/BKE_pbvh.h @@ -416,7 +416,6 @@ void BKE_pbvh_free(PBVH *pbvh); void BKE_pbvh_set_bm_log(PBVH *pbvh, BMLog *log); BMLog *BKE_pbvh_get_bm_log(PBVH *pbvh); - /** checks if original data needs to be updated for v, and if so updates it. Stroke_id is provided by the sculpt code and is used to detect updates. The reason we do it diff --git a/source/blender/blenkernel/BKE_sculpt.hh b/source/blender/blenkernel/BKE_sculpt.hh index 600323015c0..069860a2b83 100644 --- a/source/blender/blenkernel/BKE_sculpt.hh +++ b/source/blender/blenkernel/BKE_sculpt.hh @@ -76,7 +76,8 @@ BLI_INLINE bool stroke_id_test_no_update(SculptSession *ss, PBVHVertRef vertex, return !(id->userflag & (int)user); } -BLI_INLINE void add_sculpt_flag(SculptSession *ss, PBVHVertRef vertex, uint8_t flag) { +BLI_INLINE void add_sculpt_flag(SculptSession *ss, PBVHVertRef vertex, uint8_t flag) +{ *blender::bke::paint::vertex_attr_ptr(vertex, ss->attrs.flags) |= flag; } BLI_INLINE void clear_sculpt_flag(SculptSession *ss, PBVHVertRef vertex, uint8_t flag) diff --git a/source/blender/blenkernel/intern/attribute.cc b/source/blender/blenkernel/intern/attribute.cc index cfe11d10d89..0b1b70b564c 100644 --- a/source/blender/blenkernel/intern/attribute.cc +++ b/source/blender/blenkernel/intern/attribute.cc @@ -969,4 +969,3 @@ void BKE_id_attribute_copy_domains_temp(short id_type, *((short *)r_id->name) = id_type; } - diff --git a/source/blender/blenkernel/intern/customdata.cc b/source/blender/blenkernel/intern/customdata.cc index 05f62a0b7c4..e7c7d152228 100644 --- a/source/blender/blenkernel/intern/customdata.cc +++ b/source/blender/blenkernel/intern/customdata.cc @@ -4421,7 +4421,8 @@ void CustomData_bmesh_swap_data(CustomData *source, * (this should work because layers are ordered by type) */ while (dest_i_start < dest->totlayer && - dest->layers[dest_i_start].type < source->layers[src_i].type) { + dest->layers[dest_i_start].type < source->layers[src_i].type) + { dest_i_start++; } @@ -4440,7 +4441,8 @@ void CustomData_bmesh_swap_data(CustomData *source, while (dest_i < dest->totlayer && dest->layers[dest_i].type == source->layers[src_i].type) { /* if we found a matching layer, copy the data */ if (dest->layers[dest_i].type == source->layers[src_i].type && - STREQ(dest->layers[dest_i].name, source->layers[src_i].name)) { + STREQ(dest->layers[dest_i].name, source->layers[src_i].name)) + { void *src_data = POINTER_OFFSET(src_block, source->layers[src_i].offset); void *dest_data = POINTER_OFFSET(*dest_block, dest->layers[dest_i].offset); const LayerTypeInfo *typeInfo = layerType_getInfo( diff --git a/source/blender/blenkernel/intern/dyntopo.cc b/source/blender/blenkernel/intern/dyntopo.cc index 18222c4b5eb..3ea79336a76 100644 --- a/source/blender/blenkernel/intern/dyntopo.cc +++ b/source/blender/blenkernel/intern/dyntopo.cc @@ -233,8 +233,8 @@ BLI_INLINE float calc_weighted_length(EdgeQueueContext *eq_ctx, BMVert *v1, BMVe static void edge_queue_insert_unified(EdgeQueueContext *eq_ctx, BMEdge *e) { - if (/*BLI_mm_heap_len(eq_ctx->heap_mm) < eq_ctx->max_heap_mm &&*/ !(e->head.hflag & - BM_ELEM_TAG)) { + if (/*BLI_mm_heap_len(eq_ctx->heap_mm) < eq_ctx->max_heap_mm &&*/ !(e->head.hflag & BM_ELEM_TAG)) + { float lensqr = len_squared_v3v3(e->v1->co, e->v2->co); float len = sqrtf(lensqr); @@ -1332,7 +1332,8 @@ static void unified_edge_queue_create(EdgeQueueContext *eq_ctx, } if (eq_ctx->use_view_normal && (dot_v3v3(e->v1->no, eq_ctx->view_normal) < 0.0f && - dot_v3v3(e->v2->no, eq_ctx->view_normal) < 0.0f)) { + dot_v3v3(e->v2->no, eq_ctx->view_normal) < 0.0f)) + { return; } @@ -1421,7 +1422,8 @@ static void edge_queue_create_local(EdgeQueueContext *eq_ctx, EdgeQueueThreadData td; if ((node->flag & PBVH_Leaf) && (node->flag & PBVH_UpdateTopology) && - !(node->flag & PBVH_FullyHidden)) { + !(node->flag & PBVH_FullyHidden)) + { memset(&td, 0, sizeof(td)); td.pbvh = pbvh; td.node = node; @@ -1688,7 +1690,8 @@ static bool cleanup_valence_3_4(EdgeQueueContext *ectx, PBVHVertRef sv = {(intptr_t)v}; if (len_squared_v3v3(v->co, center) >= rsqr || !v->e || - ectx->mask_cb(sv, ectx->mask_cb_data) < 0.5f) { + ectx->mask_cb(sv, ectx->mask_cb_data) < 0.5f) + { continue; } @@ -1750,7 +1753,8 @@ static bool cleanup_valence_3_4(EdgeQueueContext *ectx, /*ignore non-manifold edges along with ones flagged as sharp*/ if (l->radial_next == l || l->radial_next->radial_next != l || - !(l->e->head.hflag & BM_ELEM_SMOOTH)) { + !(l->e->head.hflag & BM_ELEM_SMOOTH)) + { bad = true; break; } @@ -2274,7 +2278,8 @@ extern "C" bool BKE_pbvh_bmesh_update_topology(SculptSession *ss, } if (curop == 0 && count_subd >= max_subd && totop > 1 && ops[0] == PBVH_Subdivide && - ops[1] == PBVH_Collapse) { + ops[1] == PBVH_Collapse) + { curop = 1; } @@ -2287,7 +2292,8 @@ extern "C" bool BKE_pbvh_bmesh_update_topology(SculptSession *ss, e = eq_ctx.edge_heap.pop_max(); while (!eq_ctx.edge_heap.empty() && e && (bm_elem_is_free((BMElem *)e, BM_EDGE) || - calc_weighted_length(&eq_ctx, e->v1, e->v2, -1.0) < limit_len_subd)) { + calc_weighted_length(&eq_ctx, e->v1, e->v2, -1.0) < limit_len_subd)) + { e = eq_ctx.edge_heap.pop_max(); } @@ -2326,7 +2332,8 @@ extern "C" bool BKE_pbvh_bmesh_update_topology(SculptSession *ss, e = eq_ctx.edge_heap.pop_min(); while (!eq_ctx.edge_heap.empty() && e && (bm_elem_is_free((BMElem *)e, BM_EDGE) || - calc_weighted_length(&eq_ctx, e->v1, e->v2, 1.0) > limit_len_cold)) { + calc_weighted_length(&eq_ctx, e->v1, e->v2, 1.0) > limit_len_cold)) + { e = eq_ctx.edge_heap.pop_min(); } @@ -2334,8 +2341,8 @@ extern "C" bool BKE_pbvh_bmesh_update_topology(SculptSession *ss, break; } - if (bm_elem_is_free((BMElem *)e->v1, BM_VERT) || - bm_elem_is_free((BMElem *)e->v2, BM_VERT)) { + if (bm_elem_is_free((BMElem *)e->v1, BM_VERT) || bm_elem_is_free((BMElem *)e->v2, BM_VERT)) + { printf("%s: error! operated on freed bmesh elements! e: %p, e->v1: %p, e->v2: %p\n", __func__, e, @@ -2412,7 +2419,8 @@ extern "C" bool BKE_pbvh_bmesh_update_topology(SculptSession *ss, PBVHNode *node = pbvh->nodes + i; if ((node->flag & PBVH_Leaf) && (node->flag & PBVH_UpdateTopology) && - !(node->flag & PBVH_FullyHidden)) { + !(node->flag & PBVH_FullyHidden)) + { /* do not clear PBVH_UpdateTopology here in case split messes with it */ diff --git a/source/blender/blenkernel/intern/dyntopo_collapse.cc b/source/blender/blenkernel/intern/dyntopo_collapse.cc index 4b14437eaf3..5b532393e76 100644 --- a/source/blender/blenkernel/intern/dyntopo_collapse.cc +++ b/source/blender/blenkernel/intern/dyntopo_collapse.cc @@ -398,7 +398,8 @@ bool pbvh_bmesh_collapse_edge_uvs( totuv = 0; while (uvidx < pbvh->header.bm->ldata.totlayer && - pbvh->header.bm->ldata.layers[uvidx].type == CD_PROP_FLOAT2) { + pbvh->header.bm->ldata.layers[uvidx].type == CD_PROP_FLOAT2) + { uvidx++; totuv++; } @@ -577,7 +578,8 @@ BMVert *pbvh_bmesh_collapse_edge(PBVH *pbvh, /* one of the two vertices may be masked, select the correct one for deletion */ if (!(boundflag1 & SCULPTVERT_ALL_CORNER) || DYNTOPO_MASK(eq_ctx->cd_vert_mask_offset, v1) < - DYNTOPO_MASK(eq_ctx->cd_vert_mask_offset, v2)) { + DYNTOPO_MASK(eq_ctx->cd_vert_mask_offset, v2)) + { v_del = v1; v_conn = v2; } @@ -590,7 +592,8 @@ BMVert *pbvh_bmesh_collapse_edge(PBVH *pbvh, /* Don't collapse across boundaries. */ if ((boundflag1 & SCULPTVERT_ALL_CORNER) || - (boundflag1 & SCULPTVERT_ALL_BOUNDARY) != (boundflag2 & SCULPTVERT_ALL_BOUNDARY)) { + (boundflag1 & SCULPTVERT_ALL_BOUNDARY) != (boundflag2 & SCULPTVERT_ALL_BOUNDARY)) + { bm_logstack_pop(); return nullptr; } diff --git a/source/blender/blenkernel/intern/paint.cc b/source/blender/blenkernel/intern/paint.cc index 7ee826d679a..fad30deead0 100644 --- a/source/blender/blenkernel/intern/paint.cc +++ b/source/blender/blenkernel/intern/paint.cc @@ -3423,7 +3423,8 @@ SculptAttribute *BKE_sculpt_attribute_get(struct Object *ob, attr = sculpt_alloc_attr(ss); if (BKE_pbvh_type(ss->pbvh) == PBVH_FACES || - (BKE_pbvh_type(ss->pbvh) == PBVH_GRIDS && domain == ATTR_DOMAIN_FACE)) { + (BKE_pbvh_type(ss->pbvh) == PBVH_GRIDS && domain == ATTR_DOMAIN_FACE)) + { attr->data = cdata->layers[index].data; } diff --git a/source/blender/blenkernel/intern/pbvh.c b/source/blender/blenkernel/intern/pbvh.c index fa23936afdc..9909c821f4a 100644 --- a/source/blender/blenkernel/intern/pbvh.c +++ b/source/blender/blenkernel/intern/pbvh.c @@ -2655,7 +2655,8 @@ bool ray_face_intersection_quad(const float ray_start[3], if ((isect_ray_tri_watertight_v3(ray_start, isect_precalc, t0, t1, t2, &depth_test, NULL) && (depth_test < *depth)) || (isect_ray_tri_watertight_v3(ray_start, isect_precalc, t0, t2, t3, &depth_test, NULL) && - (depth_test < *depth))) { + (depth_test < *depth))) + { *depth = depth_test; return true; } @@ -2672,7 +2673,8 @@ bool ray_face_intersection_tri(const float ray_start[3], { float depth_test; if (isect_ray_tri_watertight_v3(ray_start, isect_precalc, t0, t1, t2, &depth_test, NULL) && - (depth_test < *depth)) { + (depth_test < *depth)) + { *depth = depth_test; return true; } @@ -2711,7 +2713,8 @@ bool ray_face_intersection_depth_quad(const float ray_start[3], { float depth_test; if (!(isect_ray_tri_watertight_v3(ray_start, isect_precalc, t0, t1, t2, &depth_test, NULL) || - isect_ray_tri_watertight_v3(ray_start, isect_precalc, t0, t2, t3, &depth_test, NULL))) { + isect_ray_tri_watertight_v3(ray_start, isect_precalc, t0, t2, t3, &depth_test, NULL))) + { return false; } return ray_update_depth_and_hit_count(depth_test, r_depth, r_back_depth, hit_count); @@ -2772,11 +2775,13 @@ bool ray_face_nearest_quad(const float ray_start[3], float co[3], depth_test; if ((dist_sq_test = dist_squared_ray_to_tri_v3_fast( - ray_start, ray_normal, t0, t1, t2, co, &depth_test)) < *dist_sq) { + ray_start, ray_normal, t0, t1, t2, co, &depth_test)) < *dist_sq) + { *dist_sq = dist_sq_test; *depth = depth_test; if ((dist_sq_test = dist_squared_ray_to_tri_v3_fast( - ray_start, ray_normal, t0, t2, t3, co, &depth_test)) < *dist_sq) { + ray_start, ray_normal, t0, t2, t3, co, &depth_test)) < *dist_sq) + { *dist_sq = dist_sq_test; *depth = depth_test; } @@ -2798,7 +2803,8 @@ bool ray_face_nearest_tri(const float ray_start[3], float co[3], depth_test; if ((dist_sq_test = dist_squared_ray_to_tri_v3_fast( - ray_start, ray_normal, t0, t1, t2, co, &depth_test)) < *dist_sq) { + ray_start, ray_normal, t0, t1, t2, co, &depth_test)) < *dist_sq) + { *dist_sq = dist_sq_test; *depth = depth_test; return true; @@ -2851,7 +2857,8 @@ static bool pbvh_faces_node_raycast(PBVH *pbvh, } if (!ray_face_intersection_depth_tri( - ray_start, isect_precalc, co[0], co[1], co[2], depth, depth_back, hit_count)) { + ray_start, isect_precalc, co[0], co[1], co[2], depth, depth_back, hit_count)) + { continue; } @@ -2943,7 +2950,8 @@ static bool pbvh_grids_node_raycast(PBVH *pbvh, co[3], depth, back_depth, - hit_count)) { + hit_count)) + { continue; } hit = true; @@ -4862,7 +4870,8 @@ void BKE_pbvh_update_vert_boundary_faces(int *boundary_flags, *flags |= SCULPT_BOUNDARY_FACE_SET; if (i > 1 && last_fset2 != last_fset && last_fset != -1 && last_fset2 != -1 && fset != -1 && - last_fset2 != fset) { + last_fset2 != fset) + { *flags |= SCULPT_CORNER_FACE_SET; } } @@ -5768,7 +5777,8 @@ void BKE_pbvh_sync_visibility_from_verts(PBVH *pbvh, Mesh *mesh) int grid_index = mp->loopstart + loop_index; if (pbvh->grid_hidden[grid_index] && - BLI_BITMAP_TEST(pbvh->grid_hidden[grid_index], key.grid_area - 1)) { + BLI_BITMAP_TEST(pbvh->grid_hidden[grid_index], key.grid_area - 1)) + { hidden = true; break; diff --git a/source/blender/blenkernel/intern/pbvh.cc b/source/blender/blenkernel/intern/pbvh.cc index 6335d04ae53..17bca45e98f 100644 --- a/source/blender/blenkernel/intern/pbvh.cc +++ b/source/blender/blenkernel/intern/pbvh.cc @@ -2724,7 +2724,8 @@ bool ray_face_intersection_depth_quad(const float ray_start[3], { float depth_test; if (!(isect_ray_tri_watertight_v3(ray_start, isect_precalc, t0, t1, t2, &depth_test, nullptr) || - isect_ray_tri_watertight_v3(ray_start, isect_precalc, t0, t2, t3, &depth_test, nullptr))) { + isect_ray_tri_watertight_v3(ray_start, isect_precalc, t0, t2, t3, &depth_test, nullptr))) + { return false; } return ray_update_depth_and_hit_count(depth_test, r_depth, r_back_depth, hit_count); @@ -2867,7 +2868,8 @@ static bool pbvh_faces_node_raycast(PBVH *pbvh, } if (!ray_face_intersection_depth_tri( - ray_start, isect_precalc, co[0], co[1], co[2], depth, depth_back, hit_count)) { + ray_start, isect_precalc, co[0], co[1], co[2], depth, depth_back, hit_count)) + { continue; } @@ -2959,7 +2961,8 @@ static bool pbvh_grids_node_raycast(PBVH *pbvh, co[3], depth, back_depth, - hit_count)) { + hit_count)) + { continue; } hit = true; @@ -3835,7 +3838,7 @@ int BKE_pbvh_get_totnodes(PBVH *pbvh) return pbvh->totnode; } -int BKE_pbvh_get_node_id(PBVH */*pbvh*/, PBVHNode *node) +int BKE_pbvh_get_node_id(PBVH * /*pbvh*/, PBVHNode *node) { return node->id; } @@ -4008,7 +4011,7 @@ void BKE_pbvh_check_tri_areas(PBVH *pbvh, PBVHNode *node) } } -static void pbvh_pmap_to_edges_add(PBVH */*pbvh*/, +static void pbvh_pmap_to_edges_add(PBVH * /*pbvh*/, PBVHVertRef /*vertex*/, int **r_edges, int *r_edges_size, @@ -4431,7 +4434,8 @@ void update_vert_boundary_faces(int *boundary_flags, *boundary_flag |= SCULPT_BOUNDARY_FACE_SET; if (i > 1 && last_fset2 != last_fset && last_fset != -1 && last_fset2 != -1 && fset != -1 && - last_fset2 != fset) { + last_fset2 != fset) + { *boundary_flag |= SCULPT_CORNER_FACE_SET; } } @@ -4677,8 +4681,8 @@ static void pbvh_face_iter_step(PBVHFaceIter *fd, bool do_step) fd->bm_faces_iter_++; } - while (fd->bm_faces_iter_ < fd->bm_faces_->cur && - !fd->bm_faces_->elems[fd->bm_faces_iter_]) { + while (fd->bm_faces_iter_ < fd->bm_faces_->cur && !fd->bm_faces_->elems[fd->bm_faces_iter_]) + { fd->bm_faces_iter_++; } diff --git a/source/blender/blenkernel/intern/pbvh_bmesh.cc b/source/blender/blenkernel/intern/pbvh_bmesh.cc index 88c4c764e17..ddbfd8eb20d 100644 --- a/source/blender/blenkernel/intern/pbvh_bmesh.cc +++ b/source/blender/blenkernel/intern/pbvh_bmesh.cc @@ -1026,8 +1026,8 @@ bool pbvh_bmesh_node_limit_ensure(PBVH *pbvh, int node_index) TableGSet *bm_faces = pbvh->nodes[node_index].bm_faces; const int bm_faces_size = BLI_table_gset_len(bm_faces); - if (bm_faces_size <= pbvh->leaf_limit || - pbvh->nodes[node_index].depth >= PBVH_STACK_FIXED_DEPTH) { + if (bm_faces_size <= pbvh->leaf_limit || pbvh->nodes[node_index].depth >= PBVH_STACK_FIXED_DEPTH) + { /* Node limit not exceeded */ return false; } @@ -1454,7 +1454,8 @@ bool pbvh_bmesh_node_raycast(SculptSession *ss, } if (ray_face_intersection_depth_tri( - ray_start, isect_precalc, cos[0], cos[1], cos[2], depth, back_depth, hit_count)) { + ray_start, isect_precalc, cos[0], cos[1], cos[2], depth, back_depth, hit_count)) + { hit = true; if (r_face_normal) { @@ -1951,7 +1952,8 @@ static void pbvh_bmesh_create_leaf_fast_task_cb(void *__restrict userdata, ptr += pbvh->cd_vert_node_offset; if (old == DYNTOPO_NODE_NONE && - atomic_cas_int32((int32_t *)ptr, DYNTOPO_NODE_NONE, node_index) == DYNTOPO_NODE_NONE) { + atomic_cas_int32((int32_t *)ptr, DYNTOPO_NODE_NONE, node_index) == DYNTOPO_NODE_NONE) + { BLI_table_gset_insert(n->bm_unique_verts, v); } else { @@ -3692,7 +3694,8 @@ static void pbvh_fix_orphan_leaves(PBVH *pbvh) PBVHNode *node = pbvh->nodes + i; if (!(node->flag & PBVH_Leaf) || (node->flag & PBVH_Delete) || - BLI_table_gset_len(node->bm_faces) != 0) { + BLI_table_gset_len(node->bm_faces) != 0) + { continue; } @@ -3722,8 +3725,8 @@ static void pbvh_fix_orphan_leaves(PBVH *pbvh) PBVHNode *a = pbvh->nodes + other->children_offset; PBVHNode *b = pbvh->nodes + other->children_offset + 1; - if (!(a->flag & PBVH_Delete) && (a->flag & PBVH_Leaf) && - BLI_table_gset_len(a->bm_faces) > 1) { + if (!(a->flag & PBVH_Delete) && (a->flag & PBVH_Leaf) && BLI_table_gset_len(a->bm_faces) > 1) + { other = a; } else if (!(b->flag & PBVH_Delete) && (b->flag & PBVH_Leaf) && diff --git a/source/blender/blenkernel/intern/pbvh_colors.cc b/source/blender/blenkernel/intern/pbvh_colors.cc index 699a947bab5..36488e42b6c 100644 --- a/source/blender/blenkernel/intern/pbvh_colors.cc +++ b/source/blender/blenkernel/intern/pbvh_colors.cc @@ -215,7 +215,7 @@ static void pbvh_vertex_color_set_bmesh(PBVH &pbvh, PBVHVertRef vertex, const fl } while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e); } else { - from_float(color, *reinterpret_cast(BM_ELEM_CD_GET_VOID_P(v, pbvh.cd_vcol_offset))); + from_float(color, *reinterpret_cast(BM_ELEM_CD_GET_VOID_P(v, pbvh.cd_vcol_offset))); } } diff --git a/source/blender/blenlib/BLI_heap_minmax.hh b/source/blender/blenlib/BLI_heap_minmax.hh index 0169c6b82a4..a3fa2c78177 100644 --- a/source/blender/blenlib/BLI_heap_minmax.hh +++ b/source/blender/blenlib/BLI_heap_minmax.hh @@ -35,9 +35,7 @@ class MinMaxHeap { } } - ~MinMaxHeap() - { - } + ~MinMaxHeap() {} MinMaxHeapNode *insert(float weight, Value value) { diff --git a/source/blender/blenloader/intern/versioning_300.cc b/source/blender/blenloader/intern/versioning_300.cc index 345770eac8f..895b7fb9eb2 100644 --- a/source/blender/blenloader/intern/versioning_300.cc +++ b/source/blender/blenloader/intern/versioning_300.cc @@ -4302,7 +4302,8 @@ void blo_do_versions_300(FileData *fd, Library * /*lib*/, Main *bmain) } if (!DNA_struct_elem_find(fd->filesdna, "Brush", "DynTopoSettings", "dyntopo") || - !MAIN_VERSION_ATLEAST(bmain, 306, 4)) { + !MAIN_VERSION_ATLEAST(bmain, 306, 4)) + { LISTBASE_FOREACH (Brush *, brush, &bmain->brushes) { DynTopoSettings *ds = &brush->dyntopo; brush->dyntopo = *DNA_struct_default_get(DynTopoSettings); @@ -4314,7 +4315,8 @@ void blo_do_versions_300(FileData *fd, Library * /*lib*/, Main *bmain) SCULPT_TOOL_ROTATE, SCULPT_TOOL_GRAB, SCULPT_TOOL_CLOTH, - SCULPT_TOOL_PAINT)) { + SCULPT_TOOL_PAINT)) + { brush->dyntopo.flag |= DYNTOPO_DISABLED; } diff --git a/source/blender/bmesh/intern/bmesh_core.c b/source/blender/bmesh/intern/bmesh_core.c index c340eff2b1d..f53aaa39146 100644 --- a/source/blender/bmesh/intern/bmesh_core.c +++ b/source/blender/bmesh/intern/bmesh_core.c @@ -3387,8 +3387,8 @@ static void bmesh_kernel_vert_separate__cleanup(BMesh *bm, LinkNode *edges_separ do { BMEdge *e = n_step->link; BLI_assert(e != e_orig); - if ((e->v1 == e_orig->v1) && (e->v2 == e_orig->v2) && - BM_edge_splice(bm, e_orig, e, false)) { + if ((e->v1 == e_orig->v1) && (e->v2 == e_orig->v2) && BM_edge_splice(bm, e_orig, e, false)) + { /* don't visit again */ n_prev->next = n_step->next; } diff --git a/source/blender/bmesh/intern/bmesh_idmap.cc b/source/blender/bmesh/intern/bmesh_idmap.cc index a1e64db6211..a4ae795a396 100644 --- a/source/blender/bmesh/intern/bmesh_idmap.cc +++ b/source/blender/bmesh/intern/bmesh_idmap.cc @@ -259,7 +259,7 @@ void BM_idmap_check_ids(BMIdMap *idmap) int id = BM_ELEM_CD_GET_INT(elem, idmap->cd_id_off[int(elem->head.htype)]); if (id == BM_ID_NONE || id < 0 || (id < idmap->map_size && idmap->map[id])) { - //printf("%s: Allocating new id for %p(%d): %d\n", __func__, elem, id, max_id); + // printf("%s: Allocating new id for %p(%d): %d\n", __func__, elem, id, max_id); id = max_id++; BM_ELEM_CD_SET_INT(elem, idmap->cd_id_off[int(elem->head.htype)], id); } diff --git a/source/blender/bmesh/intern/bmesh_mesh.cc b/source/blender/bmesh/intern/bmesh_mesh.cc index 2d3fd3ffe0c..394001de96d 100644 --- a/source/blender/bmesh/intern/bmesh_mesh.cc +++ b/source/blender/bmesh/intern/bmesh_mesh.cc @@ -202,7 +202,7 @@ BMesh *BM_mesh_create(const BMAllocTemplate *allocsize, const struct BMeshCreate return bm; } -ATTR_NO_OPT static void customdata_pool_destroy(BMesh */*bm*/, CustomData *cdata) +ATTR_NO_OPT static void customdata_pool_destroy(BMesh * /*bm*/, CustomData *cdata) { if (cdata->pool) { BLI_mempool_destroy(cdata->pool); diff --git a/source/blender/draw/DRW_pbvh.hh b/source/blender/draw/DRW_pbvh.hh index 97b3919639b..77b0486a835 100644 --- a/source/blender/draw/DRW_pbvh.hh +++ b/source/blender/draw/DRW_pbvh.hh @@ -10,9 +10,9 @@ /* Needed for BKE_ccg.h. */ #include "BLI_assert.h" #include "BLI_bitmap.h" +#include "BLI_math_vector_types.hh" #include "BLI_offset_indices.hh" #include "BLI_span.hh" -#include "BLI_math_vector_types.hh" #include "BKE_ccg.h" diff --git a/source/blender/draw/engines/overlay/overlay_mode_transfer.cc b/source/blender/draw/engines/overlay/overlay_mode_transfer.cc index 7d798f60e62..95c11e3b4ea 100644 --- a/source/blender/draw/engines/overlay/overlay_mode_transfer.cc +++ b/source/blender/draw/engines/overlay/overlay_mode_transfer.cc @@ -110,7 +110,8 @@ void OVERLAY_mode_transfer_cache_populate(OVERLAY_Data *vedata, Object *ob) pd->mode_transfer.any_animated = true; if (use_sculpt_pbvh) { - DRW_shgroup_call_sculpt(mode_transfer_grp[is_xray], ob, false, false, false, false, false, false); + DRW_shgroup_call_sculpt( + mode_transfer_grp[is_xray], ob, false, false, false, false, false, false); } else { struct GPUBatch *geom = DRW_cache_object_surface_get(ob); diff --git a/source/blender/editors/sculpt_paint/paint_intern.h b/source/blender/editors/sculpt_paint/paint_intern.h index 10f7bb0649a..9411731d888 100644 --- a/source/blender/editors/sculpt_paint/paint_intern.h +++ b/source/blender/editors/sculpt_paint/paint_intern.h @@ -71,7 +71,8 @@ typedef void (*StrokeUpdateStep)(struct bContext *C, struct PaintStroke *stroke, struct PointerRNA *itemptr); typedef void (*StrokeRedraw)(const struct bContext *C, struct PaintStroke *stroke, bool final); -typedef void (*StrokeDone)(const struct bContext *C, struct PaintStroke *stroke);; +typedef void (*StrokeDone)(const struct bContext *C, struct PaintStroke *stroke); +; struct PaintStroke *paint_stroke_new(struct bContext *C, struct wmOperator *op, diff --git a/source/blender/editors/sculpt_paint/sculpt_api.cc b/source/blender/editors/sculpt_paint/sculpt_api.cc index e7374bb7a2a..d915449fe4b 100644 --- a/source/blender/editors/sculpt_paint/sculpt_api.cc +++ b/source/blender/editors/sculpt_paint/sculpt_api.cc @@ -345,7 +345,8 @@ static void grids_update_boundary_flags(const SculptSession *ss, PBVHVertRef ver } if (sculpt_check_boundary_vertex_in_base_mesh(ss, v1) && - sculpt_check_boundary_vertex_in_base_mesh(ss, v2)) { + sculpt_check_boundary_vertex_in_base_mesh(ss, v2)) + { *flag |= SCULPT_BOUNDARY_MESH; } break; @@ -508,7 +509,8 @@ eSculptBoundary SCULPT_vertex_is_boundary(const SculptSession *ss, (eSculptBoundary)0; case SUBDIV_CCG_ADJACENT_EDGE: if (sculpt_check_boundary_vertex_in_base_mesh(ss, v1) && - sculpt_check_boundary_vertex_in_base_mesh(ss, v2)) { + sculpt_check_boundary_vertex_in_base_mesh(ss, v2)) + { flag |= SCULPT_BOUNDARY_MESH; } case SUBDIV_CCG_ADJACENT_NONE: diff --git a/source/blender/editors/sculpt_paint/sculpt_face_set.cc b/source/blender/editors/sculpt_paint/sculpt_face_set.cc index 067862885fe..bc5be2c3b84 100644 --- a/source/blender/editors/sculpt_paint/sculpt_face_set.cc +++ b/source/blender/editors/sculpt_paint/sculpt_face_set.cc @@ -403,8 +403,8 @@ void do_draw_face_sets_brush_task_cb_ex(void *__restrict userdata, /* Sample faces that are within * 8 pixels of the center of the brush. */ - if (set_active_faceset && - fset != abs(ss->cache->automasking->settings.initial_face_set)) { + if (set_active_faceset && fset != abs(ss->cache->automasking->settings.initial_face_set)) + { float radius = ss->cache->radius; float pixels = 8; // TODO: multiply with DPI @@ -690,7 +690,8 @@ void SCULPT_do_draw_face_sets_brush(Sculpt *sd, Object *ob, Span nod if (SCULPT_stroke_is_first_brush_step(ss->cache)) { if (ss->cache->invert && ss->cache->automasking && - (automasking_flags & BRUSH_AUTOMASKING_FACE_SETS)) { + (automasking_flags & BRUSH_AUTOMASKING_FACE_SETS)) + { ss->cache->automasking->settings.current_face_set = ss->cache->automasking->settings.initial_face_set; } @@ -699,7 +700,8 @@ void SCULPT_do_draw_face_sets_brush(Sculpt *sd, Object *ob, Span nod bool threaded = true; if (ss->cache->invert && !ss->cache->alt_smooth && ss->cache->automasking && ss->cache->automasking->settings.initial_face_set == - ss->cache->automasking->settings.current_face_set) { + ss->cache->automasking->settings.current_face_set) + { threaded = false; } @@ -1820,7 +1822,8 @@ static void sculpt_face_set_shrink_bmesh(Object * /*ob*/, if (l->radial_next != l && abs(BM_ELEM_CD_GET_INT(l->radial_next->f, ss->cd_faceset_offset)) != - abs(active_face_set_id)) { + abs(active_face_set_id)) + { int fset = BM_ELEM_CD_GET_INT(l->radial_next->f, ss->cd_faceset_offset); BM_ELEM_CD_SET_INT(f, ss->cd_faceset_offset, fset); break; @@ -2887,8 +2890,8 @@ static int sculpt_face_set_edit_modal(bContext *C, wmOperator *op, const wmEvent float grabtan[3]; sub_v3_v3v3(grabtan, new_pos, depth_world_space); - if (dot_v3v3(fsecd->start_no, fsecd->start_no) > 0.0f && - dot_v3v3(grabtan, fsecd->start_no) < 0) { + if (dot_v3v3(fsecd->start_no, fsecd->start_no) > 0.0f && dot_v3v3(grabtan, fsecd->start_no) < 0) + { extrude_disp *= -1.0f; } diff --git a/source/blender/editors/sculpt_paint/sculpt_face_set_topology.c b/source/blender/editors/sculpt_paint/sculpt_face_set_topology.c index d73d9efe02e..72d2bdb01eb 100644 --- a/source/blender/editors/sculpt_paint/sculpt_face_set_topology.c +++ b/source/blender/editors/sculpt_paint/sculpt_face_set_topology.c @@ -140,7 +140,8 @@ static int sculpt_face_set_by_topology_invoke(bContext *C, wmOperator *op, const int new_face_set = SCULPT_FACE_SET_NONE; if (repeat_previous && ss->face_set_last_created != SCULPT_FACE_SET_NONE && - initial_poly.i != ss->face_set_last_poly.i && initial_edge.i != ss->face_set_last_edge.i) { + initial_poly.i != ss->face_set_last_poly.i && initial_edge.i != ss->face_set_last_edge.i) + { new_face_set = ss->face_set_last_created; } else { @@ -160,7 +161,7 @@ static int sculpt_face_set_by_topology_invoke(bContext *C, wmOperator *op, const ss->face_set_last_poly = initial_poly; /* Sync face sets visibility and vertex visibility as now all Face Sets are visible. */ - //SCULPT_visibility_sync_all_face_sets_to_vertices(ob); + // SCULPT_visibility_sync_all_face_sets_to_vertices(ob); for (int i = 0; i < totnode; i++) { BKE_pbvh_vert_tag_update_normal_visibility(nodes[i]); diff --git a/source/blender/editors/sculpt_paint/sculpt_filter_mask.cc b/source/blender/editors/sculpt_paint/sculpt_filter_mask.cc index f766c3fac26..9549e3cd9f2 100644 --- a/source/blender/editors/sculpt_paint/sculpt_filter_mask.cc +++ b/source/blender/editors/sculpt_paint/sculpt_filter_mask.cc @@ -946,7 +946,8 @@ static int sculpt_ipmask_filter_modal(bContext *C, wmOperator *op, const wmEvent const int iteration_count = RNA_int_get(op->ptr, "iterations"); if ((event->type == EVT_ESCKEY && event->val == KM_PRESS) || - (event->type == RIGHTMOUSE && event->val == KM_PRESS)) { + (event->type == RIGHTMOUSE && event->val == KM_PRESS)) + { sculpt_ipmask_filter_cancel(C, op); return OPERATOR_FINISHED; } diff --git a/source/blender/editors/sculpt_paint/sculpt_geodesic.cc b/source/blender/editors/sculpt_paint/sculpt_geodesic.cc index 7d83926876a..bf3ef74d10c 100644 --- a/source/blender/editors/sculpt_paint/sculpt_geodesic.cc +++ b/source/blender/editors/sculpt_paint/sculpt_geodesic.cc @@ -578,7 +578,8 @@ static float *SCULPT_geodesic_bmesh_create(Object *ob, const int v_other_i = BM_elem_index_get(v_other); if (sculpt_geodesic_mesh_test_dist_add_bmesh( - v_other, v1, v2, dists, initial_verts, r_closest_verts, cos)) { + v_other, v1, v2, dists, initial_verts, r_closest_verts, cos)) + { BMIter eiter; BMEdge *e_other; @@ -874,7 +875,8 @@ static float *SCULPT_geodesic_grids_create(Object *ob, //} if (sculpt_geodesic_grids_test_dist_add( - ss, v_other, v1, v2, dists, initial_verts, r_closest_verts, cos)) { + ss, v_other, v1, v2, dists, initial_verts, r_closest_verts, cos)) + { for (int edge_map_index = 0; edge_map_index < vmap[v_other].count; edge_map_index++) { const int e_other = vmap[v_other].indices[edge_map_index]; int ev_other; diff --git a/source/blender/editors/sculpt_paint/sculpt_ops.cc b/source/blender/editors/sculpt_paint/sculpt_ops.cc index 128135d5aeb..c44309a9ee3 100644 --- a/source/blender/editors/sculpt_paint/sculpt_ops.cc +++ b/source/blender/editors/sculpt_paint/sculpt_ops.cc @@ -1432,8 +1432,9 @@ static int sculpt_regularize_rake_exec(bContext *C, wmOperator *op) int *boundflag = vertex_attr_ptr(vertex, ss->attrs.boundary_flags); - if (*boundflag & (SCULPT_CORNER_MESH | SCULPT_CORNER_FACE_SET | SCULPT_CORNER_SHARP | - SCULPT_CORNER_SEAM)) { + if (*boundflag & + (SCULPT_CORNER_MESH | SCULPT_CORNER_FACE_SET | SCULPT_CORNER_SHARP | SCULPT_CORNER_SEAM)) + { continue; } diff --git a/source/blender/editors/sculpt_paint/sculpt_poly_loop.c b/source/blender/editors/sculpt_paint/sculpt_poly_loop.c index 631dbb04231..b2f3613e160 100644 --- a/source/blender/editors/sculpt_paint/sculpt_poly_loop.c +++ b/source/blender/editors/sculpt_paint/sculpt_poly_loop.c @@ -164,7 +164,8 @@ PBVHEdgeRef sculpt_poly_loop_initial_edge_from_cursor(Object *ob) int closest_vert = mesh->mloop[initial_poly->loopstart].v; for (int i = 0; i < initial_poly->totloop; i++) { if (len_squared_v3v3(vert_positions[ss->mloop[initial_poly->loopstart + i].v], location) < - len_squared_v3v3(vert_positions[closest_vert], location)) { + len_squared_v3v3(vert_positions[closest_vert], location)) + { closest_vert = ss->mloop[initial_poly->loopstart + i].v; } } @@ -180,9 +181,9 @@ PBVHEdgeRef sculpt_poly_loop_initial_edge_from_cursor(Object *ob) mesh->medge[edge_index].v1; if (dist_to_line_segment_v3( location, vert_positions[closest_vert], vert_positions[other_vert]) < - dist_to_line_segment_v3(location, - vert_positions[closest_vert], - vert_positions[closest_vert_on_initial_edge])) { + dist_to_line_segment_v3( + location, vert_positions[closest_vert], vert_positions[closest_vert_on_initial_edge])) + { initial_edge = edge_index; closest_vert_on_initial_edge = other_vert; } @@ -264,7 +265,8 @@ static void sculpt_poly_loop_symm_poly_find(Object *ob, float original_edge_center[3]; MEdge *original_edge = &mesh->medge[edge_index]; - mid_v3_v3v3(original_edge_center, vert_positions[original_edge->v1], vert_positions[original_edge->v2]); + mid_v3_v3v3( + original_edge_center, vert_positions[original_edge->v1], vert_positions[original_edge->v2]); float symm_edge_center[3]; flip_v3_v3(symm_edge_center, original_edge_center, symm_it); diff --git a/source/blender/editors/sculpt_paint/sculpt_replay.c b/source/blender/editors/sculpt_paint/sculpt_replay.c index 9e5ecbcafee..4038be046aa 100644 --- a/source/blender/editors/sculpt_paint/sculpt_replay.c +++ b/source/blender/editors/sculpt_paint/sculpt_replay.c @@ -1115,9 +1115,7 @@ char *SCULPT_replay_serialize() return ret; } -static void SCULPT_replay_deserialize(SculptReplayLog *log) -{ -} +static void SCULPT_replay_deserialize(SculptReplayLog *log) {} static void replay_samples_ensure_size(SculptReplayLog *log) { diff --git a/source/blender/editors/sculpt_paint/sculpt_smooth.cc b/source/blender/editors/sculpt_paint/sculpt_smooth.cc index 4ec13e6cc87..75f9b9c7781 100644 --- a/source/blender/editors/sculpt_paint/sculpt_smooth.cc +++ b/source/blender/editors/sculpt_paint/sculpt_smooth.cc @@ -272,7 +272,8 @@ void SCULPT_bmesh_four_neighbor_average(SculptSession *ss, float *co2; if (!do_origco || - blender::bke::sculpt::stroke_id_test_no_update(ss, vertex_other, STROKEID_USER_ORIGINAL)) { + blender::bke::sculpt::stroke_id_test_no_update(ss, vertex_other, STROKEID_USER_ORIGINAL)) + { co2 = v_other->co; } else { @@ -897,7 +898,8 @@ void SCULPT_reproject_cdata(SculptSession *ss, if (ldata->typemap[CD_PROP_FLOAT2] != -1) { for (int i = ldata->typemap[CD_PROP_FLOAT2]; i < ldata->totlayer && ldata->layers[i].type == CD_PROP_FLOAT2; - i++) { + i++) + { totuv++; } diff --git a/source/blender/editors/sculpt_paint/sculpt_transform.cc b/source/blender/editors/sculpt_paint/sculpt_transform.cc index b99210e15b5..8f9a67f0a53 100644 --- a/source/blender/editors/sculpt_paint/sculpt_transform.cc +++ b/source/blender/editors/sculpt_paint/sculpt_transform.cc @@ -80,7 +80,8 @@ void ED_sculpt_init_transform(bContext *C, Object *ob, const int mval[2], const } if (sd->transform_mode == SCULPT_TRANSFORM_MODE_RADIUS_ELASTIC || - sd->transform_deform_target == SCULPT_TRANSFORM_DEFORM_TARGET_CLOTH_SIM) { + sd->transform_deform_target == SCULPT_TRANSFORM_DEFORM_TARGET_CLOTH_SIM) + { ss->filter_cache->transform_displacement_mode = SCULPT_TRANSFORM_DISPLACEMENT_INCREMENTAL; } else { diff --git a/source/blender/editors/sculpt_paint/sculpt_undo.cc b/source/blender/editors/sculpt_paint/sculpt_undo.cc index 35038ca504c..5a715a122e9 100644 --- a/source/blender/editors/sculpt_paint/sculpt_undo.cc +++ b/source/blender/editors/sculpt_paint/sculpt_undo.cc @@ -2155,7 +2155,8 @@ void SCULPT_undo_ensure_bmlog(Object *ob) if (!us) { // check next step if (ustack->step_active && ustack->step_active->next && - ustack->step_active->next->type == BKE_UNDOSYS_TYPE_SCULPT) { + ustack->step_active->next->type == BKE_UNDOSYS_TYPE_SCULPT) + { us = ustack->step_active->next; } } @@ -2603,7 +2604,8 @@ static void sculpt_save_active_attribute_color(Object *ob, SculptAttrRef *attr) (layer = BKE_id_attribute_search(&me->id, BKE_id_attributes_active_color_name(&me->id), CD_MASK_COLOR_ALL, - ATTR_DOMAIN_MASK_POINT | ATTR_DOMAIN_MASK_CORNER))) { + ATTR_DOMAIN_MASK_POINT | ATTR_DOMAIN_MASK_CORNER))) + { attr->domain = BKE_id_attribute_domain((ID *)me, layer); BLI_strncpy(attr->name, layer->name, sizeof(attr->name)); attr->type = eCustomDataType(layer->type); diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c index f8abcbdbac3..b2493e408c9 100644 --- a/source/blender/makesrna/intern/rna_scene.c +++ b/source/blender/makesrna/intern/rna_scene.c @@ -3063,7 +3063,6 @@ static void rna_def_tool_settings(BlenderRNA *brna) RNA_def_property_boolean_sdna(prop, NULL, "show_origco", 1); RNA_def_property_ui_text(prop, "Show Original", ""); - prop = RNA_def_property(srna, "sculpt", PROP_POINTER, PROP_NONE); RNA_def_property_struct_type(prop, "Sculpt"); RNA_def_property_ui_text(prop, "Sculpt", ""); -- 2.30.2 From dcec104fbd202f5ba9797b89d351645b00122bb3 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Wed, 3 May 2023 00:21:23 -0700 Subject: [PATCH 078/279] temp-sculpt-dyntopo: fix odd merge error --- source/blender/blenlib/intern/path_util.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/source/blender/blenlib/intern/path_util.c b/source/blender/blenlib/intern/path_util.c index f07cb901fa8..d9e77379272 100644 --- a/source/blender/blenlib/intern/path_util.c +++ b/source/blender/blenlib/intern/path_util.c @@ -1145,19 +1145,19 @@ bool BLI_path_program_extensions_add_win32(char *program_name, const size_t maxl bool retval = false; int type; - type = BLI_exists(name); + type = BLI_exists(program_name); if ((type == 0) || S_ISDIR(type)) { /* Typically 3-5, ".EXE", ".BAT"... etc. */ const int ext_max = 12; const char *ext = BLI_getenv("PATHEXT"); if (ext) { - const int name_len = strlen(name); + const int name_len = strlen(program_name); char *filename = alloca(name_len + ext_max); char *filename_ext; const char *ext_next; /* Null terminated in the loop. */ - memcpy(filename, name, name_len); + memcpy(filename, program_name, name_len); filename_ext = filename + name_len; do { @@ -1172,7 +1172,7 @@ bool BLI_path_program_extensions_add_win32(char *program_name, const size_t maxl type = BLI_exists(filename); if (type && (!S_ISDIR(type))) { retval = true; - BLI_strncpy(name, filename, maxlen); + BLI_strncpy(program_name, filename, maxlen); break; } } -- 2.30.2 From c2bd9849d079613c302b597fedf978ba120d9a41 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Mon, 8 May 2023 21:25:35 -0700 Subject: [PATCH 079/279] temp-sculpt-dyntopo: Fix various original coordinates bugs --- source/blender/draw/intern/draw_pbvh.cc | 11 ++++++----- .../editors/sculpt_paint/sculpt_automasking.cc | 3 +++ source/blender/editors/sculpt_paint/sculpt_dyntopo.cc | 9 +++------ source/blender/editors/sculpt_paint/sculpt_undo.cc | 4 +++- 4 files changed, 15 insertions(+), 12 deletions(-) diff --git a/source/blender/draw/intern/draw_pbvh.cc b/source/blender/draw/intern/draw_pbvh.cc index 2f6736dcfb9..f8e4de9044f 100644 --- a/source/blender/draw/intern/draw_pbvh.cc +++ b/source/blender/draw/intern/draw_pbvh.cc @@ -989,10 +989,11 @@ struct PBVHBatches { case CD_PBVH_CO_TYPE: if (args->show_orig) { int cd_origco = CustomData_get_offset_named( - &args->bm->vdata, CD_PROP_INT32, ".sculpt_orig_co"); + &args->bm->vdata, CD_PROP_FLOAT3, ".sculpt_orig_co"); + printf("cd_orig_co: %d\n", cd_origco); foreach_bmesh([&](BMLoop *l) { - *static_cast(GPU_vertbuf_raw_step(&access)) = BM_ELEM_CD_PTR( + *static_cast(GPU_vertbuf_raw_step(&access)) = *BM_ELEM_CD_PTR( l->v, cd_origco); }); } @@ -1005,13 +1006,13 @@ struct PBVHBatches { case CD_PBVH_NO_TYPE: if (args->show_orig) { - int cd_origco = CustomData_get_offset_named( - &args->bm->vdata, CD_PROP_INT32, ".sculpt_orig_co"); + int cd_origno = CustomData_get_offset_named( + &args->bm->vdata, CD_PROP_FLOAT3, ".sculpt_orig_no"); foreach_bmesh([&](BMLoop *l) { short no[3]; - normal_float_to_short_v3(no, BM_ELEM_CD_PTR(l->v, cd_origco)); + normal_float_to_short_v3(no, BM_ELEM_CD_PTR(l->v, cd_origno)); *static_cast(GPU_vertbuf_raw_step(&access)) = no; }); } diff --git a/source/blender/editors/sculpt_paint/sculpt_automasking.cc b/source/blender/editors/sculpt_paint/sculpt_automasking.cc index 503bf586e89..02f2e046438 100644 --- a/source/blender/editors/sculpt_paint/sculpt_automasking.cc +++ b/source/blender/editors/sculpt_paint/sculpt_automasking.cc @@ -898,6 +898,9 @@ AutomaskingCache *SCULPT_automasking_cache_init(Sculpt *sd, const Brush *brush, } if (!SCULPT_automasking_needs_factors_cache(sd, brush)) { + if (ss->attrs.automasking_factor) { + BKE_sculpt_attribute_destroy(ob, ss->attrs.automasking_factor); + } return automasking; } diff --git a/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc b/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc index 2ca2f4d0c7c..10046c4262e 100644 --- a/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc +++ b/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc @@ -510,8 +510,7 @@ ATTR_NO_OPT void SCULPT_dynamic_topology_enable_ex(Main *bmain, if (!ss->bm) { ss->bm = BKE_sculptsession_empty_bmesh_create(); - BMeshFromMeshParams params = {0}; - params.calc_face_normal = true; + BMeshFromMeshParams params = {}; params.use_shapekey = true; params.create_shapekey_layers = true; params.active_shapekey = ob->shapenr; @@ -535,10 +534,8 @@ ATTR_NO_OPT void SCULPT_dynamic_topology_enable_ex(Main *bmain, e->head.hflag |= BM_ELEM_DRAW; } - /* Make sure the data for existing faces are initialized. */ - if (me->totpoly != ss->bm->totface) { - BM_mesh_normals_update(ss->bm); - } + /* Calculate normals. */ + BM_mesh_normals_update(ss->bm); /* Enable dynamic topology. */ me->flag |= ME_SCULPT_DYNAMIC_TOPOLOGY; diff --git a/source/blender/editors/sculpt_paint/sculpt_undo.cc b/source/blender/editors/sculpt_paint/sculpt_undo.cc index 5a715a122e9..6c297680019 100644 --- a/source/blender/editors/sculpt_paint/sculpt_undo.cc +++ b/source/blender/editors/sculpt_paint/sculpt_undo.cc @@ -1041,13 +1041,15 @@ static void sculpt_undo_bmesh_enable(Object *ob, SculptUndoNode *unode, bool is_ #endif BMeshFromMeshParams params = {0}; - params.calc_face_normal = true; params.use_shapekey = true; params.create_shapekey_layers = true; params.active_shapekey = ob->shapenr; BM_mesh_bm_from_me(ss->bm, me, ¶ms); + /* Calculate normals. */ + BM_mesh_normals_update(ss->bm); + BKE_sculptsession_update_attr_refs(ob); if (ss->pbvh) { -- 2.30.2 From 2a219a1167b09b4f2384c432d0e8d34558836aab Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Mon, 8 May 2023 21:33:09 -0700 Subject: [PATCH 080/279] temp-sculpt-dyntopo: Fix topology automasking not working with boundary --- source/blender/editors/sculpt_paint/sculpt_automasking.cc | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/source/blender/editors/sculpt_paint/sculpt_automasking.cc b/source/blender/editors/sculpt_paint/sculpt_automasking.cc index 02f2e046438..c8f1ca49875 100644 --- a/source/blender/editors/sculpt_paint/sculpt_automasking.cc +++ b/source/blender/editors/sculpt_paint/sculpt_automasking.cc @@ -937,10 +937,8 @@ AutomaskingCache *SCULPT_automasking_cache_init(Sculpt *sd, const Brush *brush, if (SCULPT_is_automasking_mode_enabled(sd, brush, BRUSH_AUTOMASKING_TOPOLOGY)) { SCULPT_vertex_random_access_ensure(ss); - if (sculpt_automasking_is_constrained_by_radius(brush)) { - automasking->settings.topology_use_brush_limit = true; - SCULPT_topology_automasking_init(sd, ob); - } + automasking->settings.topology_use_brush_limit = true; + SCULPT_topology_automasking_init(sd, ob); } if (SCULPT_is_automasking_mode_enabled(sd, brush, BRUSH_AUTOMASKING_FACE_SETS)) { -- 2.30.2 From 58a41db224cd431452deef7a3cc7dba344b5fb76 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Mon, 8 May 2023 21:35:57 -0700 Subject: [PATCH 081/279] temp-sculpt-dyntopo: Fix incorrect member initialization in last commit --- source/blender/editors/sculpt_paint/sculpt_automasking.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source/blender/editors/sculpt_paint/sculpt_automasking.cc b/source/blender/editors/sculpt_paint/sculpt_automasking.cc index c8f1ca49875..21b14496268 100644 --- a/source/blender/editors/sculpt_paint/sculpt_automasking.cc +++ b/source/blender/editors/sculpt_paint/sculpt_automasking.cc @@ -937,7 +937,8 @@ AutomaskingCache *SCULPT_automasking_cache_init(Sculpt *sd, const Brush *brush, if (SCULPT_is_automasking_mode_enabled(sd, brush, BRUSH_AUTOMASKING_TOPOLOGY)) { SCULPT_vertex_random_access_ensure(ss); - automasking->settings.topology_use_brush_limit = true; + automasking->settings.topology_use_brush_limit = sculpt_automasking_is_constrained_by_radius( + brush); SCULPT_topology_automasking_init(sd, ob); } -- 2.30.2 From 489106675469c38a9c962bab377bee725f0043d9 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Tue, 9 May 2023 11:19:45 -0700 Subject: [PATCH 082/279] temp-sculpt-dyntopo: Fix bugs in smoothing code and draw face sets Draw face sets' code has been rewritten to use new sculpt face API. --- .../startup/bl_ui/properties_paint_common.py | 2 +- source/blender/editors/sculpt_paint/sculpt.cc | 2 +- .../editors/sculpt_paint/sculpt_api.cc | 10 ++ .../sculpt_paint/sculpt_automasking.cc | 21 +-- .../sculpt_paint/sculpt_brush_types.cc | 6 +- .../editors/sculpt_paint/sculpt_curvature.cc | 16 +- .../editors/sculpt_paint/sculpt_dyntopo.cc | 9 +- .../editors/sculpt_paint/sculpt_face_set.cc | 149 +++++++++++------- .../editors/sculpt_paint/sculpt_intern.hh | 1 + .../editors/sculpt_paint/sculpt_smooth.cc | 3 +- 10 files changed, 126 insertions(+), 93 deletions(-) diff --git a/scripts/startup/bl_ui/properties_paint_common.py b/scripts/startup/bl_ui/properties_paint_common.py index 8dcaabee161..211eceff288 100644 --- a/scripts/startup/bl_ui/properties_paint_common.py +++ b/scripts/startup/bl_ui/properties_paint_common.py @@ -586,7 +586,7 @@ def brush_settings(layout, context, brush, popover=False): layout, context, brush, - "autosmooth_fset_slide", + "auto_smooth_projection", slider=True, ) diff --git a/source/blender/editors/sculpt_paint/sculpt.cc b/source/blender/editors/sculpt_paint/sculpt.cc index 914645bee03..087f1002b49 100644 --- a/source/blender/editors/sculpt_paint/sculpt.cc +++ b/source/blender/editors/sculpt_paint/sculpt.cc @@ -145,7 +145,7 @@ void SCULPT_face_normal_get(SculptSession *ss, PBVHFaceRef face, float no[3]) case PBVH_GRIDS: { no = blender::bke::mesh::poly_normal_calc( {reinterpret_cast(ss->vert_positions), ss->totvert}, - ss->corner_edges.slice(ss->polys[face.i])); + ss->corner_verts.slice(ss->polys[face.i])); break; } default: /* Failed. */ diff --git a/source/blender/editors/sculpt_paint/sculpt_api.cc b/source/blender/editors/sculpt_paint/sculpt_api.cc index d915449fe4b..75364f9b970 100644 --- a/source/blender/editors/sculpt_paint/sculpt_api.cc +++ b/source/blender/editors/sculpt_paint/sculpt_api.cc @@ -627,3 +627,13 @@ void SCULPT_apply_dyntopo_settings(SculptSession *ss, Sculpt *sculpt, Brush *bru ds_final->spacing = ds_final->inherit & DYNTOPO_INHERIT_SPACING ? ds2->spacing : ds1->spacing; ds_final->repeat = ds_final->inherit & DYNTOPO_INHERIT_REPEAT ? ds2->repeat : ds1->repeat; } + +bool SCULPT_face_is_hidden(const SculptSession *ss, PBVHFaceRef face) { + if (ss->bm) { + BMFace *f = reinterpret_cast(face.i); + return BM_elem_flag_test(f, BM_ELEM_HIDDEN); + } + else { + return ss->hide_poly ? ss->hide_poly[face.i] : false; + } +} diff --git a/source/blender/editors/sculpt_paint/sculpt_automasking.cc b/source/blender/editors/sculpt_paint/sculpt_automasking.cc index 21b14496268..365b0b04d09 100644 --- a/source/blender/editors/sculpt_paint/sculpt_automasking.cc +++ b/source/blender/editors/sculpt_paint/sculpt_automasking.cc @@ -629,11 +629,6 @@ static void SCULPT_topology_automasking_init(Sculpt *sd, Object *ob) SculptSession *ss = ob->sculpt; Brush *brush = BKE_paint_brush(&sd->paint); - if (BKE_pbvh_type(ss->pbvh) == PBVH_FACES && !ss->pmap) { - BLI_assert_unreachable(); - return; - } - const int totvert = SCULPT_vertex_count_get(ss); for (int i : IndexRange(totvert)) { PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); @@ -668,11 +663,6 @@ static void sculpt_face_sets_automasking_init(Sculpt *sd, Object *ob) return; } - if (BKE_pbvh_type(ss->pbvh) == PBVH_FACES && !ss->pmap) { - BLI_assert_msg(0, "Face Sets automasking: pmap missing"); - return; - } - int tot_vert = SCULPT_vertex_count_get(ss); int active_face_set = SCULPT_active_face_set_get(ss); for (int i : IndexRange(tot_vert)) { @@ -692,11 +682,6 @@ static void SCULPT_boundary_automasking_init(Object *ob, { SculptSession *ss = ob->sculpt; - if (!ss->pmap) { - BLI_assert_msg(0, "Boundary Edges masking: pmap missing"); - return; - } - const int totvert = SCULPT_vertex_count_get(ss); int *edge_distance = (int *)MEM_callocN(sizeof(int) * totvert, "automask_factor"); @@ -942,8 +927,10 @@ AutomaskingCache *SCULPT_automasking_cache_init(Sculpt *sd, const Brush *brush, SCULPT_topology_automasking_init(sd, ob); } - if (SCULPT_is_automasking_mode_enabled(sd, brush, BRUSH_AUTOMASKING_FACE_SETS)) { - SCULPT_vertex_random_access_ensure(ss); + /* Draw face sets brush handles face set automasking on its own. */ + if (SCULPT_is_automasking_mode_enabled(sd, brush, BRUSH_AUTOMASKING_FACE_SETS) && + !(brush && brush->sculpt_tool == SCULPT_TOOL_DRAW_FACE_SETS && !ss->cache->alt_smooth)) + { sculpt_face_sets_automasking_init(sd, ob); } diff --git a/source/blender/editors/sculpt_paint/sculpt_brush_types.cc b/source/blender/editors/sculpt_paint/sculpt_brush_types.cc index 46e34a6dd3e..80562a4dd5d 100644 --- a/source/blender/editors/sculpt_paint/sculpt_brush_types.cc +++ b/source/blender/editors/sculpt_paint/sculpt_brush_types.cc @@ -2823,9 +2823,9 @@ void SCULPT_do_displacement_smear_brush(Sculpt *sd, Object *ob, Span /** \name Sculpt Topology Rake (Shared Utility) * \{ */ -static void do_topology_rake_bmesh_task_cb_ex(void *__restrict userdata, - const int n, - const TaskParallelTLS *__restrict tls) +ATTR_NO_OPT static void do_topology_rake_bmesh_task_cb_ex(void *__restrict userdata, + const int n, + const TaskParallelTLS *__restrict tls) { SculptThreadedTaskData *data = static_cast(userdata); SculptSession *ss = data->ob->sculpt; diff --git a/source/blender/editors/sculpt_paint/sculpt_curvature.cc b/source/blender/editors/sculpt_paint/sculpt_curvature.cc index 84d36c993c4..7f25064924b 100644 --- a/source/blender/editors/sculpt_paint/sculpt_curvature.cc +++ b/source/blender/editors/sculpt_paint/sculpt_curvature.cc @@ -141,10 +141,10 @@ BLI_INLINE void normal_covariance(float mat[3][3], float no[3]) mat[2][2] = no[2] * no[2]; } -bool SCULPT_calc_principle_curvatures(SculptSession *ss, - PBVHVertRef vertex, - SculptCurvatureData *out, - bool useAccurateSolver) +ATTR_NO_OPT bool SCULPT_calc_principle_curvatures(SculptSession *ss, + PBVHVertRef vertex, + SculptCurvatureData *out, + bool useAccurateSolver) { SculptVertexNeighborIter ni; float nmat[3][3], nmat2[3][3]; @@ -246,10 +246,10 @@ bool SCULPT_calc_principle_curvatures(SculptSession *ss, return true; } -void SCULPT_curvature_dir_get(SculptSession *ss, - PBVHVertRef v, - float dir[3], - bool useAccurateSolver) +ATTR_NO_OPT void SCULPT_curvature_dir_get(SculptSession *ss, + PBVHVertRef v, + float dir[3], + bool useAccurateSolver) { if (BKE_pbvh_type(ss->pbvh) != PBVH_BMESH) { SculptCurvatureData curv; diff --git a/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc b/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc index 10046c4262e..51096b90b06 100644 --- a/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc +++ b/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc @@ -467,10 +467,10 @@ static void customdata_strip_templayers(CustomData *cdata, int totelem) } } -ATTR_NO_OPT void SCULPT_dynamic_topology_enable_ex(Main *bmain, - Depsgraph *depsgraph, - Scene * /*scene*/, - Object *ob) +void SCULPT_dynamic_topology_enable_ex(Main *bmain, + Depsgraph *depsgraph, + Scene * /*scene*/, + Object *ob) { SculptSession *ss = ob->sculpt; Mesh *me = BKE_object_get_original_mesh(ob); @@ -542,6 +542,7 @@ ATTR_NO_OPT void SCULPT_dynamic_topology_enable_ex(Main *bmain, if (!ss->bm_idmap) { ss->bm_idmap = BM_idmap_new(ss->bm, BM_VERT | BM_EDGE | BM_FACE); + BM_idmap_check_ids(ss->bm_idmap); BKE_sculptsession_update_attr_refs(ob); } diff --git a/source/blender/editors/sculpt_paint/sculpt_face_set.cc b/source/blender/editors/sculpt_paint/sculpt_face_set.cc index 17fb47104cb..dcea9d86e96 100644 --- a/source/blender/editors/sculpt_paint/sculpt_face_set.cc +++ b/source/blender/editors/sculpt_paint/sculpt_face_set.cc @@ -110,19 +110,21 @@ int ED_sculpt_face_sets_active_update_and_get(bContext *C, Object *ob, const flo } /* Draw Face Sets Brush. */ +struct SculptFaceSetTaskData : public SculptThreadedTaskData { + bool set_active_faceset; + bool have_fset_automasking; +}; static void do_draw_face_sets_brush_task_cb_ex(void *__restrict userdata, const int n, const TaskParallelTLS *__restrict tls) { using namespace blender; - SculptThreadedTaskData *data = static_cast(userdata); + SculptFaceSetTaskData *data = static_cast(userdata); SculptSession *ss = data->ob->sculpt; const Brush *brush = data->brush; const float bstrength = ss->cache->bstrength; - PBVHVertexIter vd; - SculptBrushTest test; SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( ss, &test, data->brush->falloff_shape); @@ -135,75 +137,95 @@ static void do_draw_face_sets_brush_task_cb_ex(void *__restrict userdata, SCULPT_automasking_node_begin( data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]); - bool changed = false; - BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { - SCULPT_automasking_node_update(ss, &automask_data, &vd); + const bool have_fset_automasking = data->have_fset_automasking; + bool set_active_faceset = data->set_active_faceset; - if (BKE_pbvh_type(ss->pbvh) == PBVH_FACES) { - MeshElemMap *vert_map = &ss->pmap->pmap[vd.index]; - for (int j = 0; j < ss->pmap->pmap[vd.index].count; j++) { - const blender::IndexRange poly = ss->polys[vert_map->indices[j]]; + /* Ensure automasking data is up to date. */ + if (ss->cache->automasking) { + PBVHVertexIter vd; - const float3 poly_center = bke::mesh::poly_center_calc(positions, - ss->corner_verts.slice(poly)); - - if (!sculpt_brush_test_sq_fn(&test, poly_center)) { - continue; - } - const bool face_hidden = ss->hide_poly && ss->hide_poly[vert_map->indices[j]]; - if (face_hidden) { - continue; - } - const float fade = bstrength * SCULPT_brush_strength_factor(ss, - brush, - vd.co, - sqrtf(test.dist), - vd.no, - vd.fno, - vd.mask ? *vd.mask : 0.0f, - vd.vertex, - thread_id, - &automask_data); - - if (fade > 0.05f) { - ss->face_sets[vert_map->indices[j]] = ss->cache->paint_face_set; - changed = true; - } - } + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_ALL) { + SCULPT_automasking_node_update(ss, &automask_data, &vd); } - else if (BKE_pbvh_type(ss->pbvh) == PBVH_GRIDS) { - if (!sculpt_brush_test_sq_fn(&test, vd.co)) { + BKE_pbvh_vertex_iter_end; + } + + bool changed = false; + + PBVHFaceIter fd; + BKE_pbvh_face_iter_begin (ss->pbvh, data->nodes[n], fd) { + if (SCULPT_face_is_hidden(ss, fd.face)) { + continue; + } + + float3 poly_center = {}; + float mask = 0.0; + + for (int i = 0; i < fd.verts_num; i++) { + poly_center += SCULPT_vertex_co_get(ss, fd.verts[i]); + mask += SCULPT_vertex_mask_get(ss, fd.verts[i]); + } + + poly_center /= (float)fd.verts_num; + mask /= (float)fd.verts_num; + + if (!sculpt_brush_test_sq_fn(&test, poly_center)) { + continue; + } + + /* Face set automasking in inverted draw mode is tricky,, we have + * to sample the automasking face set after the stroke has started. + */ + if (set_active_faceset && + *fd.face_set != abs(ss->cache->automasking->settings.initial_face_set)) { + + float radius = ss->cache->radius; + float pixels = 8; /* TODO: multiply with DPI? */ + radius = pixels * (radius / (float)ss->cache->dyntopo_pixel_radius); + + if (sqrtf(test.dist) < radius) { + ss->cache->automasking->settings.initial_face_set = *fd.face_set; + set_active_faceset = data->set_active_faceset = false; + ss->cache->automasking->settings.flags |= BRUSH_AUTOMASKING_FACE_SETS; + } + else { continue; } - const float fade = bstrength * SCULPT_brush_strength_factor(ss, - brush, - vd.co, - sqrtf(test.dist), - vd.no, - vd.fno, - vd.mask ? *vd.mask : 0.0f, - vd.vertex, - thread_id, - &automask_data); + } - if (fade > 0.05f) { - SCULPT_vertex_face_set_set(ss, vd.vertex, ss->cache->paint_face_set); - changed = true; + if (have_fset_automasking) { + if (*fd.face_set != ss->cache->automasking->settings.initial_face_set) { + continue; } } - } - BKE_pbvh_vertex_iter_end; - if (changed) { - SCULPT_undo_push_node(data->ob, data->nodes[n], SCULPT_UNDO_FACE_SETS); + float fno[3]; + SCULPT_face_normal_get(ss, fd.face, fno); + + const float fade = bstrength * SCULPT_brush_strength_factor(ss, + brush, + poly_center, + sqrtf(test.dist), + fno, + fno, + mask, + fd.verts[0], + thread_id, + &automask_data); + + if (fade > 0.05f) { + *fd.face_set = ss->cache->paint_face_set; + changed = true; + } } + BKE_pbvh_face_iter_end(fd); } static void do_relax_face_sets_brush_task_cb_ex(void *__restrict userdata, const int n, const TaskParallelTLS *__restrict tls) { - SculptThreadedTaskData *data = static_cast(userdata); + SculptFaceSetTaskData *data = static_cast(userdata); SculptSession *ss = data->ob->sculpt; const Brush *brush = data->brush; float bstrength = ss->cache->bstrength; @@ -268,15 +290,25 @@ void SCULPT_do_draw_face_sets_brush(Sculpt *sd, Object *ob, Span nod BKE_curvemapping_init(brush->curve); /* Threaded loop over nodes. */ - SculptThreadedTaskData data{}; + SculptFaceSetTaskData data{}; data.sd = sd; data.ob = ob; data.brush = brush; data.nodes = nodes; + /* Note: face set automasking is fairly involved in this brush. */ + data.have_fset_automasking = ss->cache->automasking && ss->cache->automasking->settings.flags & + BRUSH_AUTOMASKING_FACE_SETS; + + /* In invert mode we have to set the automasking face set ourselves. */ + data.set_active_faceset = data.have_fset_automasking && ss->cache->invert && + ss->cache->automasking->settings.initial_face_set == + ss->cache->paint_face_set; + TaskParallelSettings settings; - BKE_pbvh_parallel_range_settings(&settings, true, nodes.size()); if (ss->cache->alt_smooth) { + BKE_pbvh_parallel_range_settings(&settings, true, nodes.size()); + SCULPT_boundary_info_ensure(ob); for (int i = 0; i < 4; i++) { data.iteration = i; @@ -285,6 +317,7 @@ void SCULPT_do_draw_face_sets_brush(Sculpt *sd, Object *ob, Span nod } } else { + BKE_pbvh_parallel_range_settings(&settings, !data.set_active_faceset, nodes.size()); BLI_task_parallel_range(0, nodes.size(), &data, do_draw_face_sets_brush_task_cb_ex, &settings); } } diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.hh b/source/blender/editors/sculpt_paint/sculpt_intern.hh index 898600cae91..d7dcdc11928 100644 --- a/source/blender/editors/sculpt_paint/sculpt_intern.hh +++ b/source/blender/editors/sculpt_paint/sculpt_intern.hh @@ -1381,6 +1381,7 @@ void SCULPT_face_set_visibility_set(SculptSession *ss, int face_set, bool visibl int SCULPT_face_set_original_get(SculptSession *ss, PBVHFaceRef face); bool SCULPT_face_select_get(SculptSession *ss, PBVHFaceRef face); +bool SCULPT_face_is_hidden(const SculptSession *ss, PBVHFaceRef face); /** \} */ diff --git a/source/blender/editors/sculpt_paint/sculpt_smooth.cc b/source/blender/editors/sculpt_paint/sculpt_smooth.cc index 75f9b9c7781..213cb21a9db 100644 --- a/source/blender/editors/sculpt_paint/sculpt_smooth.cc +++ b/source/blender/editors/sculpt_paint/sculpt_smooth.cc @@ -110,7 +110,8 @@ static void SCULPT_neighbor_coords_average_interior_ex(SculptSession *ss, /* Project to plane if desired. */ avg = avg / (float)total - co; float t = dot_v3v3(avg, no); - avg += no * t * projection + co; + + avg += -no * t * projection + co; copy_v3_v3(result, avg); } -- 2.30.2 From 7aa38adaabd5e371740a6912f84929db57682223 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Tue, 9 May 2023 11:51:09 -0700 Subject: [PATCH 083/279] temp-sculpt-dyntopo: Fix face set boundary smoothing --- .../startup/bl_ui/properties_paint_common.py | 8 +++++++ .../editors/sculpt_paint/sculpt_smooth.cc | 24 +++++++++---------- 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/scripts/startup/bl_ui/properties_paint_common.py b/scripts/startup/bl_ui/properties_paint_common.py index 211eceff288..0485185b31d 100644 --- a/scripts/startup/bl_ui/properties_paint_common.py +++ b/scripts/startup/bl_ui/properties_paint_common.py @@ -589,6 +589,14 @@ def brush_settings(layout, context, brush, popover=False): "auto_smooth_projection", slider=True, ) + if capabilities.has_auto_smooth or brush.sculpt_tool == "SMOOTH": + UnifiedPaintPanel.prop_unified( + layout, + context, + brush, + "autosmooth_fset_slide", + slider=True, + ) if capabilities.has_auto_smooth: UnifiedPaintPanel.prop_unified( diff --git a/source/blender/editors/sculpt_paint/sculpt_smooth.cc b/source/blender/editors/sculpt_paint/sculpt_smooth.cc index 213cb21a9db..93d2aa3dbe8 100644 --- a/source/blender/editors/sculpt_paint/sculpt_smooth.cc +++ b/source/blender/editors/sculpt_paint/sculpt_smooth.cc @@ -48,10 +48,6 @@ static void SCULPT_neighbor_coords_average_interior_ex(SculptSession *ss, const eSculptBoundary is_boundary = SCULPT_vertex_is_boundary(ss, vertex, bound_type); const eSculptCorner is_corner = SCULPT_vertex_is_corner(ss, vertex, corner_type); - if ((is_boundary & SCULPT_BOUNDARY_FACE_SET) || (is_corner & SCULPT_CORNER_FACE_SET)) { - projection = max_ff(projection, fset_projection); - } - float *areas = nullptr; float3 no, co; SCULPT_vertex_normal_get(ss, vertex, no); @@ -71,19 +67,23 @@ static void SCULPT_neighbor_coords_average_interior_ex(SculptSession *ss, float w = weighted ? areas[ni.i] : 1.0f; const eSculptBoundary is_boundary2 = SCULPT_vertex_is_boundary(ss, ni.vertex, bound_type); + const eSculptBoundary smooth_types = !ss->hard_edge_mode ? + SCULPT_BOUNDARY_FACE_SET | SCULPT_BOUNDARY_SEAM | + SCULPT_BOUNDARY_UV : + SCULPT_BOUNDARY_UV; + /* Boundary vertices use only other boundary vertices. */ if (is_boundary) { - /* Boundary vertices use only other boundary vertices. */ - if (is_boundary & is_boundary2) { - avg += float3(SCULPT_vertex_co_get(ss, ni.vertex)) * w; - total += w; - } - else if ((is_boundary2 & SCULPT_BOUNDARY_FACE_SET) != - (is_boundary & SCULPT_BOUNDARY_FACE_SET)) { + /* Handle smooth boundaries. */ + if (bool(is_boundary2 & smooth_types) != bool(is_boundary & smooth_types)) { /* Project to plane. */ float3 t1 = float3(SCULPT_vertex_co_get(ss, ni.vertex)) - co; - avg += (co + no * dot_v3v3(t1, no) * fset_projection) * w; + avg += (co + no * dot_v3v3(t1, no)) * w; + total += w; + } + else if (is_boundary & is_boundary2) { + avg += float3(SCULPT_vertex_co_get(ss, ni.vertex)) * w; total += w; } } -- 2.30.2 From d6fc5552f1abe64ec5079d5cfbe5104b456cf360 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Tue, 9 May 2023 11:59:25 -0700 Subject: [PATCH 084/279] temp-sculpt-automasking: Remove brush->autosmooth_fset_slide --- .../editors/sculpt_paint/sculpt_boundary.cc | 6 ++-- .../sculpt_paint/sculpt_brush_types.cc | 3 +- .../sculpt_paint/sculpt_filter_mesh.cc | 18 ++++-------- .../editors/sculpt_paint/sculpt_intern.hh | 11 ------- .../editors/sculpt_paint/sculpt_ops.cc | 2 +- .../editors/sculpt_paint/sculpt_smooth.cc | 29 ++++++------------- 6 files changed, 19 insertions(+), 50 deletions(-) diff --git a/source/blender/editors/sculpt_paint/sculpt_boundary.cc b/source/blender/editors/sculpt_paint/sculpt_boundary.cc index 8fed8882e36..5eec4eeb1d2 100644 --- a/source/blender/editors/sculpt_paint/sculpt_boundary.cc +++ b/source/blender/editors/sculpt_paint/sculpt_boundary.cc @@ -1935,7 +1935,6 @@ static void SCULPT_boundary_autosmooth(SculptSession *ss, SculptBoundary *bounda Vector nodes = blender::bke::pbvh::search_gather(ss->pbvh, nullptr, nullptr); float projection = ss->cache->brush->autosmooth_projection; - float slide_fset = ss->cache->brush->autosmooth_fset_slide; for (int iteration = 0; iteration <= count; iteration++) { for (int i = 0; i < nodes.size(); i++) { @@ -1963,7 +1962,7 @@ static void SCULPT_boundary_autosmooth(SculptSession *ss, SculptBoundary *bounda float sco[3]; - SCULPT_neighbor_coords_average_interior(ss, sco, vd.vertex, projection, slide_fset, true); + SCULPT_neighbor_coords_average_interior(ss, sco, vd.vertex, projection, true); float *co = SCULPT_brush_deform_target_vertex_co_get(ss, boundary->deform_target, &vd); @@ -1982,7 +1981,6 @@ static void SCULPT_boundary_build_smoothco(SculptSession *ss, SculptBoundary *bo boundary->smoothco = MEM_cnew_array(totvert, "boundary->smoothco"); float projection = ss->cache->brush->autosmooth_projection; - float slide_fset = ss->cache->brush->autosmooth_fset_slide; Vector nodes = blender::bke::pbvh::search_gather(ss->pbvh, nullptr, nullptr); @@ -1998,7 +1996,7 @@ static void SCULPT_boundary_build_smoothco(SculptSession *ss, SculptBoundary *bo float sco[3]; - SCULPT_neighbor_coords_average_interior(ss, sco, vd.vertex, projection, slide_fset, true); + SCULPT_neighbor_coords_average_interior(ss, sco, vd.vertex, projection, true); float *co = SCULPT_brush_deform_target_vertex_co_get(ss, boundary->deform_target, &vd); diff --git a/source/blender/editors/sculpt_paint/sculpt_brush_types.cc b/source/blender/editors/sculpt_paint/sculpt_brush_types.cc index 80562a4dd5d..dbfb17c7dfc 100644 --- a/source/blender/editors/sculpt_paint/sculpt_brush_types.cc +++ b/source/blender/editors/sculpt_paint/sculpt_brush_types.cc @@ -2900,8 +2900,7 @@ ATTR_NO_OPT static void do_topology_rake_bmesh_task_cb_ex(void *__restrict userd float avg[3], val[3]; int cd_temp = data->scl->bmesh_cd_offset; - SCULPT_bmesh_four_neighbor_average( - ss, avg, direction2, vd.bm_vert, 1.0f, true, cd_temp, false); + SCULPT_bmesh_four_neighbor_average(ss, avg, direction2, vd.bm_vert, 1.0f, cd_temp, false); sub_v3_v3v3(val, avg, vd.co); diff --git a/source/blender/editors/sculpt_paint/sculpt_filter_mesh.cc b/source/blender/editors/sculpt_paint/sculpt_filter_mesh.cc index 12743b43fec..8df145d2504 100644 --- a/source/blender/editors/sculpt_paint/sculpt_filter_mesh.cc +++ b/source/blender/editors/sculpt_paint/sculpt_filter_mesh.cc @@ -382,8 +382,6 @@ static void mesh_filter_task_cb(void *__restrict userdata, SCULPT_automasking_node_begin(data->ob, ss, ss->filter_cache->automasking, &automask_data, node); /* Smooth parameters. */ - float fset_projection = SCULPT_get_fset_projection( - ss, ss->filter_cache->preserve_fset_boundaries ? 1.0f : 0.0f); float projection = 0.0f; PBVHVertexIter vd; @@ -424,8 +422,7 @@ static void mesh_filter_task_cb(void *__restrict userdata, switch (filter_type) { case MESH_FILTER_SMOOTH: fade = clamp_f(fade, -1.0f, 1.0f); - SCULPT_neighbor_coords_average_interior( - ss, avg, vd.vertex, projection, fset_projection, true); + SCULPT_neighbor_coords_average_interior(ss, avg, vd.vertex, projection, true); sub_v3_v3v3(val, avg, orig_co); madd_v3_v3v3fl(val, orig_co, val, fade); sub_v3_v3v3(disp, val, orig_co); @@ -513,12 +510,7 @@ static void mesh_filter_task_cb(void *__restrict userdata, float disp_avg[3]; float avg_co[3]; - SCULPT_neighbor_coords_average(ss, - avg_co, - vd.vertex, - projection, - ss->filter_cache->preserve_fset_boundaries ? 0.0f : 1.0f, - true); + SCULPT_neighbor_coords_average(ss, avg_co, vd.vertex, projection, true); sub_v3_v3v3(disp_avg, avg_co, vd.co); mul_v3_v3fl( disp_avg, disp_avg, smooth_ratio * pow2f(ss->filter_cache->sharpen_factor[vd.index])); @@ -582,7 +574,7 @@ static void mesh_filter_enhance_details_init_directions(SculptSession *ss) PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); float avg[3]; - SCULPT_neighbor_coords_average(ss, avg, vertex, 0.0f, 1.0f, true); + SCULPT_neighbor_coords_average(ss, avg, vertex, 0.0f, true); sub_v3_v3v3(filter_cache->detail_directions[i], avg, SCULPT_vertex_co_get(ss, vertex)); } } @@ -632,7 +624,7 @@ static void mesh_filter_sharpen_init(SculptSession *ss, PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); float avg[3]; - SCULPT_neighbor_coords_average(ss, avg, vertex, 0.0f, 1.0f, true); + SCULPT_neighbor_coords_average(ss, avg, vertex, 0.0f, true); sub_v3_v3v3(filter_cache->detail_directions[i], avg, SCULPT_vertex_co_get(ss, vertex)); filter_cache->sharpen_factor[i] = len_v3(filter_cache->detail_directions[i]); } @@ -782,6 +774,8 @@ static void sculpt_mesh_filter_apply(bContext *C, wmOperator *op) data.filter_type = filter_type; data.filter_strength = filter_strength; + ss->filter_cache->preserve_fset_boundaries = !ss->hard_edge_mode; + TaskParallelSettings settings; BKE_pbvh_parallel_range_settings(&settings, true, ss->filter_cache->nodes.size()); BLI_task_parallel_range( diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.hh b/source/blender/editors/sculpt_paint/sculpt_intern.hh index d7dcdc11928..a74ef763817 100644 --- a/source/blender/editors/sculpt_paint/sculpt_intern.hh +++ b/source/blender/editors/sculpt_paint/sculpt_intern.hh @@ -1876,7 +1876,6 @@ void SCULPT_bmesh_four_neighbor_average(SculptSession *ss, float direction[3], struct BMVert *v, float projection, - bool check_fsets, int cd_temp, bool do_origco); @@ -1884,7 +1883,6 @@ void SCULPT_neighbor_coords_average(SculptSession *ss, float result[3], PBVHVertRef index, float projection, - float fset_projection, bool weighted); float SCULPT_neighbor_mask_average(SculptSession *ss, PBVHVertRef index); void SCULPT_neighbor_color_average(SculptSession *ss, float result[4], PBVHVertRef index); @@ -1895,16 +1893,7 @@ void SCULPT_neighbor_coords_average_interior(SculptSession *ss, float result[3], PBVHVertRef vertex, float projection, - float fset_projection, bool use_area_weights); -BLI_INLINE bool SCULPT_get_fset_projection(SculptSession *ss, float fset_projection) -{ - if (ss->hard_edge_mode) { - return 0.0f; - } - - return fset_projection; -} BLI_INLINE bool SCULPT_need_reproject(const SculptSession *ss) { diff --git a/source/blender/editors/sculpt_paint/sculpt_ops.cc b/source/blender/editors/sculpt_paint/sculpt_ops.cc index 9c0151a7532..f67ed4bb655 100644 --- a/source/blender/editors/sculpt_paint/sculpt_ops.cc +++ b/source/blender/editors/sculpt_paint/sculpt_ops.cc @@ -1333,7 +1333,7 @@ static int sculpt_set_limit_surface_exec(bContext *C, wmOperator * /* op */) PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); float *f = vertex_attr_ptr(vertex, scl); - SCULPT_neighbor_coords_average(ss, f, vertex, 0.0, true, weighted); + SCULPT_neighbor_coords_average(ss, f, vertex, 0.0, weighted); } return OPERATOR_FINISHED; diff --git a/source/blender/editors/sculpt_paint/sculpt_smooth.cc b/source/blender/editors/sculpt_paint/sculpt_smooth.cc index 93d2aa3dbe8..3e33d67e386 100644 --- a/source/blender/editors/sculpt_paint/sculpt_smooth.cc +++ b/source/blender/editors/sculpt_paint/sculpt_smooth.cc @@ -37,7 +37,6 @@ static void SCULPT_neighbor_coords_average_interior_ex(SculptSession *ss, float result[3], PBVHVertRef vertex, float projection, - float fset_projection, bool weighted, eSculptBoundary bound_type, eSculptCorner corner_type) @@ -120,7 +119,6 @@ void SCULPT_neighbor_coords_average_interior(SculptSession *ss, float result[3], PBVHVertRef vertex, float projection, - float fset_projection, bool use_area_weights) { eSculptBoundary bound_type = SCULPT_BOUNDARY_MESH | SCULPT_BOUNDARY_FACE_SET | @@ -128,7 +126,7 @@ void SCULPT_neighbor_coords_average_interior(SculptSession *ss, eSculptCorner corner_type = SCULPT_CORNER_MESH | SCULPT_CORNER_FACE_SET | SCULPT_CORNER_SEAM | SCULPT_CORNER_SHARP; SCULPT_neighbor_coords_average_interior_ex( - ss, result, vertex, projection, fset_projection, use_area_weights, bound_type, corner_type); + ss, result, vertex, projection, use_area_weights, bound_type, corner_type); } int closest_vec_to_perp(float dir[3], float r_dir2[3], float no[3], float *buckets, float w) @@ -188,7 +186,6 @@ void SCULPT_bmesh_four_neighbor_average(SculptSession *ss, float direction[3], BMVert *v, float projection, - bool check_fsets, int cd_temp, bool do_origco) { @@ -391,19 +388,15 @@ void SCULPT_bmesh_four_neighbor_average(SculptSession *ss, /* Generic functions for laplacian smoothing. These functions do not take boundary vertices into * account. */ -void SCULPT_neighbor_coords_average(SculptSession *ss, - float result[3], - PBVHVertRef vertex, - float projection, - float fset_projection, - bool weighted) +void SCULPT_neighbor_coords_average( + SculptSession *ss, float result[3], PBVHVertRef vertex, float projection, bool weighted) { eSculptCorner corner_type = SCULPT_CORNER_SHARP | SCULPT_CORNER_FACE_SET; eSculptBoundary bound_type = SCULPT_BOUNDARY_SHARP | SCULPT_BOUNDARY_SEAM | SCULPT_BOUNDARY_UV | SCULPT_BOUNDARY_FACE_SET; SCULPT_neighbor_coords_average_interior_ex( - ss, result, vertex, projection, fset_projection, weighted, bound_type, corner_type); + ss, result, vertex, projection, weighted, bound_type, corner_type); } float SCULPT_neighbor_mask_average(SculptSession *ss, PBVHVertRef vertex) @@ -510,7 +503,6 @@ static void SCULPT_enhance_details_brush(Sculpt *sd, Object *ob, Spanautosmooth_projection; - float fset_projection = brush->autosmooth_fset_slide; bool use_area_weights = brush->flag2 & BRUSH_SMOOTH_USE_AREA_WEIGHT; if (SCULPT_stroke_is_first_brush_step(ss->cache)) { @@ -528,8 +520,7 @@ static void SCULPT_enhance_details_brush(Sculpt *sd, Object *ob, Spanpbvh, i); float avg[3]; - SCULPT_neighbor_coords_average( - ss, avg, vertex, projection, fset_projection, use_area_weights); + SCULPT_neighbor_coords_average(ss, avg, vertex, projection, use_area_weights); float *detail_dir = vertex_attr_ptr(vertex, ss->attrs.detail_directions); sub_v3_v3v3(detail_dir, avg, SCULPT_vertex_co_get(ss, vertex)); @@ -574,7 +565,6 @@ static void do_smooth_brush_task_cb_ex(void *__restrict userdata, SCULPT_automasking_node_begin( data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]); - float fset_projection = SCULPT_get_fset_projection(ss, brush->autosmooth_fset_slide); float projection = brush->autosmooth_projection; bool weighted = brush->flag2 & BRUSH_SMOOTH_USE_AREA_WEIGHT; @@ -615,13 +605,12 @@ static void do_smooth_brush_task_cb_ex(void *__restrict userdata, continue; } - if (is_corner & SCULPT_CORNER_FACE_SET) { - fade *= fset_projection; + if ((is_corner & SCULPT_CORNER_FACE_SET) && ss->hard_edge_mode) { + continue; } float avg[3], val[3]; - SCULPT_neighbor_coords_average_interior( - ss, avg, vd.vertex, projection, fset_projection, weighted); + SCULPT_neighbor_coords_average_interior(ss, avg, vd.vertex, projection, weighted); sub_v3_v3v3(val, avg, vd.co); madd_v3_v3v3fl(val, vd.co, val, fade); SCULPT_clip(sd, ss, vd.co, val); @@ -727,7 +716,7 @@ void SCULPT_surface_smooth_laplacian_step(SculptSession *ss, float weigthed_o[3], weigthed_q[3], d[3]; int v_index = BKE_pbvh_vertex_to_index(ss->pbvh, vertex); - SCULPT_neighbor_coords_average(ss, laplacian_smooth_co, vertex, 0.0f, 1.0f, use_area_weights); + SCULPT_neighbor_coords_average(ss, laplacian_smooth_co, vertex, 0.0f, use_area_weights); mul_v3_v3fl(weigthed_o, origco, alpha); mul_v3_v3fl(weigthed_q, co, 1.0f - alpha); -- 2.30.2 From 5c19910e71a4fb3a46c9c6bedad4a2560db6fcd8 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Tue, 9 May 2023 12:16:39 -0700 Subject: [PATCH 085/279] temp-sculpt-dyntopo: Fix crash in boundary brush --- source/blender/blenkernel/BKE_paint.h | 1 - .../editors/sculpt_paint/sculpt_boundary.cc | 276 ++---------------- 2 files changed, 22 insertions(+), 255 deletions(-) diff --git a/source/blender/blenkernel/BKE_paint.h b/source/blender/blenkernel/BKE_paint.h index cfc6cc0351a..ff789e4d204 100644 --- a/source/blender/blenkernel/BKE_paint.h +++ b/source/blender/blenkernel/BKE_paint.h @@ -465,7 +465,6 @@ typedef struct SculptVertexInfo { typedef struct SculptBoundaryEditInfo { /* Vertex index from where the topology propagation reached this vertex. */ - PBVHVertRef original_vertex; int original_vertex_i; /* How many steps were needed to reach this vertex from the boundary. */ diff --git a/source/blender/editors/sculpt_paint/sculpt_boundary.cc b/source/blender/editors/sculpt_paint/sculpt_boundary.cc index 5eec4eeb1d2..61621fb8cf3 100644 --- a/source/blender/editors/sculpt_paint/sculpt_boundary.cc +++ b/source/blender/editors/sculpt_paint/sculpt_boundary.cc @@ -136,7 +136,7 @@ static PBVHVertRef sculpt_boundary_get_closest_boundary_vertex(SculptSession *ss fdata.boundary_initial_vertex_steps = INT_MAX; fdata.radius_sq = radius * radius; - fdata.floodfill_steps = MEM_cnew_array(TSTN, __func__); + fdata.floodfill_steps = MEM_cnew_array(SCULPT_vertex_count_get(ss) * TSTN, __func__); SCULPT_floodfill_execute(ss, &flood, boundary_initial_vertex_floodfill_cb, &fdata); SCULPT_floodfill_free(&flood); @@ -459,19 +459,7 @@ static void sculpt_boundary_indices_init(Object *ob, GSet *boundary_verts; - if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) { - boundary_verts = BLI_gset_int_new_ex("included vertices", BOUNDARY_INDICES_BLOCK_SIZE); - - GSetIterator gi; - - GSET_ITER (gi, included_verts) { - BMVert *v = (BMVert *)BLI_gsetIterator_getKey(&gi); - BLI_gset_add(boundary_verts, POINTER_FROM_INT(v->head.index)); - } - } - else { - boundary_verts = included_verts; - } + boundary_verts = included_verts; boundary->boundary_closest = MEM_cnew_array(totvert, "boundary_closest"); boundary->boundary_dist = SCULPT_geodesic_distances_create( @@ -938,237 +926,6 @@ void SCULPT_boundary_data_free(SculptBoundary *boundary) MEM_SAFE_FREE(boundary); } -typedef struct ScalarFieldWalkData { - PBVHVertRef v; - float co[3]; - - struct { - PBVHVertRef v1, v2; - } edge; - - float t, f; - bool has_edge; -} ScalarFieldWalkData; - -static void sculpt_walk_scalar_field_init(SculptSession *ss, - PBVHVertRef v, - ScalarFieldWalkData *wd, - float *field) -{ - wd->v = v; - copy_v3_v3(wd->co, SCULPT_vertex_co_get(ss, v)); - wd->has_edge = false; - wd->t = 0.0f; - - int i = BKE_pbvh_vertex_to_index(ss->pbvh, v); - wd->f = field[i]; -} - -/*walk in decreasing direction of scalar field*/ -static bool sculpt_walk_scalar_field(SculptSession *ss, - ScalarFieldWalkData *wd, - float *field, - float (*dfield)[3]) -{ - SculptVertexNeighborIter ni; - SculptVertexNeighborIter ni2; - PBVHVertRef v = wd->v, minv1 = {-1LL}, minv2 = {-1LL}; - float mindis1 = FLT_MAX, mindis2 = FLT_MAX; - float minf1 = 0.0, minf2 = 0.0; - - SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, v, ni) { - const float *co2 = SCULPT_vertex_co_get(ss, ni.vertex); - float f2 = field[ni.index]; - - if (ni.vertex.i == v.i) { - continue; - } - - if (f2 > wd->f) { - continue; - } - - float len = len_v3v3(co2, wd->co); - float dist = f2 * len; - - if (dist >= mindis1) { - continue; - } - - mindis1 = dist; - minf1 = f2; - minv1 = ni.vertex; - - mindis2 = FLT_MAX; - - SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, ni.vertex, ni2) { - if (ni2.vertex.i == ni.vertex.i) { - continue; - } - - const float *co3 = SCULPT_vertex_co_get(ss, ni2.vertex); - float f3 = field[ni2.index]; - - float dist2 = f3 * len; - - if (dist2 < mindis2) { - mindis2 = dist2; - minf2 = f3; - minv2 = ni2.vertex; - } - } - SCULPT_VERTEX_NEIGHBORS_ITER_END(ni2); - } - SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); - - if (minv1.i == -1LL) { - // didn't find anything - return false; - } - - if (minv2.i == -1LL) { - wd->v = minv1; - copy_v3_v3(wd->co, SCULPT_vertex_co_get(ss, minv1)); - wd->has_edge = false; - wd->f = minf1; - - return true; - } - - wd->has_edge = true; - wd->edge.v1 = minv1; - wd->edge.v2 = minv2; - - /* - on factor - load_package "avector"; - - comment: relative to wd.co; - a := avec(ax, ay, az); - b := avec(bx, by, bz); - - dva := avec(dvax, dvay, dvaz); - dvb := avec(dvbx, dvby, dvbz); - - la := a dot a; - lb := b dot b; - - f2 := a + (b - a) * t; - df2 := dva + (dvb - dva)*t; - - ll := f2 dot f2; - f1 := (minf1 + (minf2 - minf1)*t) * ll; - - ff := solve(df(f1, t, 2), t); - f := part(ff, 1, 2); - - - */ - - const float *a = SCULPT_vertex_co_get(ss, minv1); - const float *b = SCULPT_vertex_co_get(ss, minv2); - - float ax = a[0] - wd->co[0]; - float ay = a[1] - wd->co[1]; - float az = a[2] - wd->co[2]; - - float bx = b[0] - wd->co[0]; - float by = b[1] - wd->co[1]; - float bz = b[2] - wd->co[2]; - - float div = (by * by + bz * bz + bx * bx + (az - 2.0 * bz) * az + (ay - 2.0 * by) * ay + - (ax - 2.0 * bx) * ax); - - float t = ((ay - by) * ay + (az - bz) * az + (ax - bx) * ax) / div; - - float m1m2 = minf1 + minf2; - - float ans4 = -2.0 * (by * by + bz * bz + bx * bx) * m1m2 * az * bz * minf1 - - (2.0 * (minf1 + minf2) * bz - az * minf2) * az * az * az * minf2; - - float sqr2 = (by * by + bz * bz + bx * bx); - sqr2 = sqr2 * sqr2; - - float ans3 = - (2.0 * - ((4.0 * (minf1 * minf1 - minf1 * minf2 + minf2 * minf2) * by - - (minf1 + minf2) * ay * minf2) * - ay + - (4.0 * (minf1 * minf1 - minf1 * minf2 + minf2 * minf2) * bz - - (minf1 + minf2) * az * minf2) * - az - - (by * by + bz * bz + bx * bx) * m1m2 * minf1) * - bx - - (2.0 * m1m2 * bx - ax * minf2) * ax * ax * minf2 - - ((by * by + bz * bz) * (3.0 * minf1 * minf1 - 8.0 * minf1 * minf2 + 3.0 * minf2 * minf2) - - (minf1 * minf1 + 4.0 * minf1 * minf2 + minf2 * minf2) * bx * bx + - 2.0 * (m1m2 * bz - az * minf2) * az * minf2 + - 2.0 * (m1m2 * by - ay * minf2) * ay * minf2) * - ax) * - ax - - (2.0 * - ((by * by + bz * bz + bx * bx) * m1m2 * minf1 - - (4.0 * (minf1 * minf1 - minf1 * minf2 + minf2 * minf2) * bz - m1m2 * az * minf2) * - az) * - by + - (2.0 * (minf1 + minf2) * by - ay * minf2) * ay * ay * minf2 + - ((bx * bx + bz * bz) * (3.0 * minf1 * minf1 - 8.0 * minf1 * minf2 + 3.0 * minf2 * minf2) - - (minf1 * minf1 + 4.0 * minf1 * minf2 + minf2 * minf2) * by * by + - 2.0 * (m1m2 * bz - az * minf2) * az * minf2) * - ay) * - ay - - ((bx * bx + by * by) * (3.0 * minf1 * minf1 - 8.0 * minf1 * minf2 + 3.0 * minf2 * minf2) - - (minf1 * minf1 + 4.0 * minf1 * minf2 + minf2 * minf2) * bz * bz) * - az * az + - sqr2 * minf1 * minf1 + ans4; - - float ans2 = sqrtf(ans3); - - float ans1 = (by * by + bz * bz + bx * bx) * minf1 + - ((3.0 * minf1 - 2.0 * minf2) * az - 2.0 * (2.0 * minf1 - minf2) * bz) * az + - ((3.0 * minf1 - 2.0 * minf2) * ay - 2.0 * (2.0 * minf1 - minf2) * by) * ay + - ((3.0 * minf1 - 2.0 * minf2) * ax - 2.0 * (2.0 * minf1 - minf2) * bx) * ax + ans2; - - t = ans1 / (3.0 * - (by * by + bz * bz + bx * bx + (az - 2.0 * bz) * az + (ay - 2.0 * by) * ay + - (ax - 2.0 * bx) * ax) * - (minf1 - minf2)); - -#if 1 - t = (((3.0 * minf1 - 2.0 * minf2) * ay - 2.0 * (2.0 * minf1 - minf2) * by) * ay + - ((3.0 * minf1 - 2.0 * minf2) * az - 2.0 * (2.0 * minf1 - minf2) * bz) * az + - ((3.0 * minf1 - 2.0 * minf2) * ax - 2.0 * (2.0 * minf1 - minf2) * bx) * ax + - (by * by + bz * bz + bx * bx) * minf1) / - (3.0 * - (by * by + bz * bz + bx * bx + (az - 2.0 * bz) * az + (ay - 2.0 * by) * ay + - (ax - 2.0 * bx) * ax) * - (minf1 - minf2)); -#endif - - t = t < 0.0f ? 0.0f : t; - t = t > 1.0f ? 1.0f : t; - - t = 0.5f; - wd->t = t; - - wd->v = minv1; - wd->f = minf1 + (minf2 - minf1) * wd->t; - float co[3]; - - interp_v3_v3v3(co, SCULPT_vertex_co_get(ss, minv1), SCULPT_vertex_co_get(ss, minv2), wd->t); - - float f3 = wd->f * len_v3v3(wd->co, co); - if (f3 > mindis1 || f3 > mindis2) { - wd->f = minf1; - t = 0.0f; - interp_v3_v3v3(co, SCULPT_vertex_co_get(ss, minv1), SCULPT_vertex_co_get(ss, minv2), wd->t); - } - - copy_v3_v3(wd->co, co); - - return true; -} - Object *sculpt_get_vis_object(bContext *C, SculptSession *ss, const char *name) { if (!C) { @@ -1274,7 +1031,7 @@ static void sculpt_boundary_bend_data_init(SculptSession *ss, continue; } - if (boundary->edit_info[i].original_vertex.i == -1) { + if (boundary->edit_info[i].original_vertex_i == -1) { continue; } @@ -1283,7 +1040,11 @@ static void sculpt_boundary_bend_data_init(SculptSession *ss, float dir[3]; float normal[3]; SCULPT_vertex_normal_get(ss, vertex, normal); - sub_v3_v3v3(dir, SCULPT_vertex_co_get(ss, boundary->edit_info[i].original_vertex), co1); + sub_v3_v3v3( + dir, + SCULPT_vertex_co_get( + ss, BKE_pbvh_index_to_vertex(ss->pbvh, boundary->edit_info[i].original_vertex_i)), + co1); normalize_v3(dir); @@ -1295,9 +1056,11 @@ static void sculpt_boundary_bend_data_init(SculptSession *ss, copy_v3_v3(dir, boundary->boundary_tangents[i]); if (dot_v3v3(dir, dir) < 0.00001f) { - sub_v3_v3v3(dir, - SCULPT_vertex_co_get(ss, boundary->edit_info[i].original_vertex), - SCULPT_vertex_co_get(ss, vertex)); + sub_v3_v3v3( + dir, + SCULPT_vertex_co_get( + ss, BKE_pbvh_index_to_vertex(ss->pbvh, boundary->edit_info[i].original_vertex_i)), + SCULPT_vertex_co_get(ss, vertex)); } } else { @@ -1364,7 +1127,10 @@ static void sculpt_boundary_bend_data_init(SculptSession *ss, } float pos[3], oco[3]; copy_v3_v3(pos, boundary->bend.pivot_positions[boundary->edit_info[i].original_vertex_i]); - copy_v3_v3(oco, SCULPT_vertex_co_get(ss, boundary->edit_info[i].original_vertex)); + copy_v3_v3( + oco, + SCULPT_vertex_co_get( + ss, BKE_pbvh_index_to_vertex(ss->pbvh, boundary->edit_info[i].original_vertex_i))); if (boundary->boundary_dist[i] != FLT_MAX) { float no[3]; @@ -1434,9 +1200,11 @@ static void sculpt_boundary_slide_data_init(SculptSession *ss, SculptBoundary *b continue; } - sub_v3_v3v3(boundary->slide.directions[boundary->edit_info[i].original_vertex_i], - SCULPT_vertex_co_get(ss, boundary->edit_info[i].original_vertex), - SCULPT_vertex_co_get(ss, vertex)); + sub_v3_v3v3( + boundary->slide.directions[boundary->edit_info[i].original_vertex_i], + SCULPT_vertex_co_get( + ss, BKE_pbvh_index_to_vertex(ss->pbvh, boundary->edit_info[i].original_vertex_i)), + SCULPT_vertex_co_get(ss, vertex)); normalize_v3(boundary->slide.directions[boundary->edit_info[i].original_vertex_i]); } -- 2.30.2 From 3d4d32c56870dc5b55da02d16da0762248285302 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Sun, 14 May 2023 16:45:27 -0700 Subject: [PATCH 086/279] temp-sculpt-dyntopo: Remove SculptPMap struct SculptPMap was a referenced counted wrapper around a vertex to poly map. It was originally created to solve a bug that no longer exists. --- source/blender/blenkernel/BKE_paint.h | 3 +- source/blender/blenkernel/BKE_pbvh.h | 17 +----- source/blender/blenkernel/intern/multires.cc | 3 +- source/blender/blenkernel/intern/paint.cc | 48 ++++++++-------- source/blender/blenkernel/intern/pbvh.c | 2 +- source/blender/blenkernel/intern/pbvh.cc | 57 +++---------------- .../blender/blenkernel/intern/pbvh_colors.cc | 4 +- .../blender/blenkernel/intern/pbvh_intern.hh | 3 +- source/blender/editors/sculpt_paint/sculpt.cc | 18 +++--- .../editors/sculpt_paint/sculpt_api.cc | 12 ++-- .../editors/sculpt_paint/sculpt_dyntopo.cc | 10 ++-- .../editors/sculpt_paint/sculpt_expand.cc | 7 +-- .../editors/sculpt_paint/sculpt_face_set.cc | 4 +- 13 files changed, 68 insertions(+), 120 deletions(-) diff --git a/source/blender/blenkernel/BKE_paint.h b/source/blender/blenkernel/BKE_paint.h index ff789e4d204..cf4dcb88b6d 100644 --- a/source/blender/blenkernel/BKE_paint.h +++ b/source/blender/blenkernel/BKE_paint.h @@ -787,7 +787,8 @@ struct SculptSession { /* Mesh connectivity maps. */ /* Vertices to adjacent polys. */ - SculptPMap *pmap; + MeshElemMap *pmap; + int *pmap_mem; /* Edges to adjacent polys. */ struct MeshElemMap *epmap; diff --git a/source/blender/blenkernel/BKE_pbvh.h b/source/blender/blenkernel/BKE_pbvh.h index c277455c788..c67b0d5655a 100644 --- a/source/blender/blenkernel/BKE_pbvh.h +++ b/source/blender/blenkernel/BKE_pbvh.h @@ -34,12 +34,6 @@ extern "C" { #endif -typedef struct SculptPMap { - struct MeshElemMap *pmap; - int *pmap_mem; - int refcount; -} SculptPMap; - #if 0 typedef struct SculptLoopRef { intptr_t i; @@ -57,7 +51,6 @@ struct BMFace; struct BMIdMap; struct Scene; struct CCGElem; -struct MeshElemMap; struct CCGKey; struct CustomData; struct TableGSet; @@ -65,7 +58,6 @@ struct DMFlagMat; struct IsectRayPrecalc; struct MLoopTri; struct Mesh; -struct MeshElemMap; struct PBVH; struct MEdge; struct PBVHBatches; @@ -1276,17 +1268,12 @@ void BKE_pbvh_set_vemap(PBVH *pbvh, struct MeshElemMap *vemap); void BKE_pbvh_ignore_uvs_set(PBVH *pbvh, bool value); void BKE_pbvh_set_face_areas(PBVH *pbvh, float *face_areas); -void BKE_pbvh_set_pmap(PBVH *pbvh, SculptPMap *pmap); -SculptPMap *BKE_pbvh_get_pmap(PBVH *pbvh); +void BKE_pbvh_set_pmap(PBVH *pbvh, struct MeshElemMap *pmap, int *mem); +struct MeshElemMap *BKE_pbvh_get_pmap(PBVH *pbvh, int **r_mem); void BKE_pbvh_cache_remove(PBVH *pbvh); void BKE_pbvh_set_bmesh(PBVH *pbvh, struct BMesh *bm); void BKE_pbvh_free_bmesh(PBVH *pbvh, struct BMesh *bm); -SculptPMap *BKE_pbvh_make_pmap(const struct Mesh *me); -void BKE_pbvh_pmap_aquire(SculptPMap *pmap); -bool BKE_pbvh_pmap_release(SculptPMap *pmap); -void BKE_pbvh_clear_cache(PBVH *preserve); - void BKE_pbvh_show_orig_set(PBVH *pbvh, bool show_orig); bool BKE_pbvh_show_orig_get(PBVH *pbvh); diff --git a/source/blender/blenkernel/intern/multires.cc b/source/blender/blenkernel/intern/multires.cc index f1d879871da..e649a3e87ca 100644 --- a/source/blender/blenkernel/intern/multires.cc +++ b/source/blender/blenkernel/intern/multires.cc @@ -471,7 +471,8 @@ void multires_force_sculpt_rebuild(Object *object) object->sculpt->pbvh = nullptr; } - BKE_pbvh_pmap_release(ss->pmap); + MEM_SAFE_FREE(ss->pmap); + MEM_SAFE_FREE(ss->pmap_mem); ss->pmap = nullptr; } diff --git a/source/blender/blenkernel/intern/paint.cc b/source/blender/blenkernel/intern/paint.cc index 1ed02b03e8d..dc4f6bd7ce7 100644 --- a/source/blender/blenkernel/intern/paint.cc +++ b/source/blender/blenkernel/intern/paint.cc @@ -1491,17 +1491,14 @@ static void sculptsession_free_pbvh(Object *object) } if (ss->pbvh) { - if (ss->pmap) { - BKE_pbvh_pmap_release(ss->pmap); - ss->pmap = nullptr; - } - BKE_pbvh_free(ss->pbvh); ss->needs_pbvh_rebuild = false; ss->pbvh = nullptr; } + MEM_SAFE_FREE(ss->pmap); + MEM_SAFE_FREE(ss->pmap_mem); MEM_SAFE_FREE(ss->epmap); MEM_SAFE_FREE(ss->epmap_mem); @@ -1565,13 +1562,11 @@ void BKE_sculptsession_free(Object *ob) CustomData_free(&ss->temp_vdata, ss->temp_vdata_elems); CustomData_free(&ss->temp_pdata, ss->temp_pdata_elems); - if (!ss->pbvh) { - BKE_pbvh_pmap_release(ss->pmap); - ss->pmap = nullptr; - } - sculptsession_free_pbvh(ob); + MEM_SAFE_FREE(ss->pmap); + MEM_SAFE_FREE(ss->pmap_mem); + MEM_SAFE_FREE(ss->epmap); MEM_SAFE_FREE(ss->epmap_mem); @@ -1941,18 +1936,15 @@ static void sculpt_update_object( if (ob->type == OB_MESH && !ss->pmap) { if (!ss->pmap && ss->pbvh) { - ss->pmap = BKE_pbvh_get_pmap(ss->pbvh); - - if (ss->pmap) { - BKE_pbvh_pmap_aquire(ss->pmap); - } + ss->pmap = BKE_pbvh_get_pmap(ss->pbvh, &ss->pmap_mem); } if (!ss->pmap) { - ss->pmap = BKE_pbvh_make_pmap(me); + BKE_mesh_vert_poly_map_create( + &ss->pmap, &ss->pmap_mem, me->polys(), me->corner_verts().data(), me->totvert); if (ss->pbvh) { - BKE_pbvh_set_pmap(ss->pbvh, ss->pmap); + BKE_pbvh_set_pmap(ss->pbvh, ss->pmap, ss->pmap_mem); } } } @@ -2590,7 +2582,8 @@ static PBVH *build_pbvh_from_regular_mesh(Object *ob, Mesh *me_eval_deform) Mesh *me = BKE_object_get_original_mesh(ob); if (!ss->pmap) { - ss->pmap = BKE_pbvh_make_pmap(me); + BKE_mesh_vert_poly_map_create( + &ss->pmap, &ss->pmap_mem, me->polys(), me->corner_verts().data(), me->totvert); } PBVH *pbvh = ob->sculpt->pbvh = BKE_pbvh_new(PBVH_FACES); @@ -2598,7 +2591,7 @@ static PBVH *build_pbvh_from_regular_mesh(Object *ob, Mesh *me_eval_deform) BKE_sculpt_init_flags_valence(ob, pbvh, me->totvert, true); sculpt_check_face_areas(ob, pbvh); BKE_sculptsession_update_attr_refs(ob); - BKE_pbvh_set_pmap(pbvh, ss->pmap); + BKE_pbvh_set_pmap(pbvh, ss->pmap, ss->pmap_mem); BKE_pbvh_build_mesh(pbvh, me); BKE_pbvh_fast_draw_set(pbvh, ss->fast_draw); @@ -2646,10 +2639,14 @@ static PBVH *build_pbvh_from_ccg(Object *ob, SubdivCCG *subdiv_ccg) ss->temp_pdata_elems = ss->totfaces; if (!ss->pmap) { - ss->pmap = BKE_pbvh_make_pmap(BKE_object_get_original_mesh(ob)); + BKE_mesh_vert_poly_map_create(&ss->pmap, + &ss->pmap_mem, + base_mesh->polys(), + base_mesh->corner_verts().data(), + base_mesh->totvert); } - BKE_pbvh_set_pmap(pbvh, ss->pmap); + BKE_pbvh_set_pmap(pbvh, ss->pmap, ss->pmap_mem); int totvert = BKE_pbvh_get_grid_num_verts(pbvh); BKE_sculpt_init_flags_valence(ob, pbvh, totvert, true); @@ -2789,10 +2786,15 @@ PBVH *BKE_sculpt_object_pbvh_ensure(Depsgraph *depsgraph, Object *ob) } if (!ob->sculpt->pmap) { - ob->sculpt->pmap = BKE_pbvh_make_pmap(BKE_object_get_original_mesh(ob)); + Mesh *me = BKE_object_get_original_mesh(ob); + BKE_mesh_vert_poly_map_create(&ob->sculpt->pmap, + &ob->sculpt->pmap_mem, + me->polys(), + me->corner_verts().data(), + me->totvert); } - BKE_pbvh_set_pmap(pbvh, ob->sculpt->pmap); + BKE_pbvh_set_pmap(pbvh, ob->sculpt->pmap, ob->sculpt->pmap_mem); ob->sculpt->pbvh = pbvh; sculpt_attribute_update_refs(ob); diff --git a/source/blender/blenkernel/intern/pbvh.c b/source/blender/blenkernel/intern/pbvh.c index 9909c821f4a..1c215b9bf39 100644 --- a/source/blender/blenkernel/intern/pbvh.c +++ b/source/blender/blenkernel/intern/pbvh.c @@ -4542,7 +4542,7 @@ void BKE_pbvh_pmap_to_edges(PBVH *pbvh, bool *r_heap_alloc, int **r_polys) { - MeshElemMap *map = pbvh->pmap->pmap + vertex.i; + MeshElemMap *map = pbvh->pmap + vertex.i; int len = 0; for (int i = 0; i < map->count; i++) { diff --git a/source/blender/blenkernel/intern/pbvh.cc b/source/blender/blenkernel/intern/pbvh.cc index b5ed4f7a934..f54720771ed 100644 --- a/source/blender/blenkernel/intern/pbvh.cc +++ b/source/blender/blenkernel/intern/pbvh.cc @@ -1269,9 +1269,6 @@ void BKE_pbvh_free(PBVH *pbvh) MEM_SAFE_FREE(pbvh->vert_bitmap); - BKE_pbvh_pmap_release(pbvh->pmap); - pbvh->pmap = nullptr; - pbvh->invalid = true; pbvh_pixels_free(pbvh); @@ -1559,8 +1556,7 @@ static void pbvh_faces_update_normals(PBVH *pbvh, Span nodes) for (const PBVHNode *node : nodes) { for (const int vert : Span(node->vert_indices, node->uniq_verts)) { if (update_tags[vert]) { - polys_to_update.add_multiple( - {pbvh->pmap->pmap[vert].indices, pbvh->pmap->pmap[vert].count}); + polys_to_update.add_multiple({pbvh->pmap[vert].indices, pbvh->pmap[vert].count}); } } } @@ -1599,7 +1595,7 @@ static void pbvh_faces_update_normals(PBVH *pbvh, Span nodes) threading::parallel_for(verts_to_update.index_range(), 1024, [&](const IndexRange range) { for (const int vert : verts_to_update.as_span().slice(range)) { float3 normal(0.0f); - for (const int poly : Span(pbvh->pmap->pmap[vert].indices, pbvh->pmap->pmap[vert].count)) { + for (const int poly : Span(pbvh->pmap[vert].indices, pbvh->pmap[vert].count)) { normal += poly_normals[poly]; } vert_normals[vert] = math::normalize(normal); @@ -4014,7 +4010,7 @@ void BKE_pbvh_pmap_to_edges(PBVH *pbvh, bool *r_heap_alloc, int **r_polys) { - MeshElemMap *map = pbvh->pmap->pmap + vertex.i; + MeshElemMap *map = pbvh->pmap + vertex.i; int len = 0; for (int i = 0; i < map->count; i++) { @@ -4425,18 +4421,17 @@ void BKE_pbvh_ignore_uvs_set(PBVH *pbvh, bool value) pbvh_boundaries_flag_update(pbvh); } -struct SculptPMap *BKE_pbvh_get_pmap(PBVH *pbvh) +struct MeshElemMap *BKE_pbvh_get_pmap(PBVH *pbvh, int **r_pmap_mem) { + *r_pmap_mem = pbvh->pmap_mem; + return pbvh->pmap; } -void BKE_pbvh_set_pmap(PBVH *pbvh, SculptPMap *pmap) +void BKE_pbvh_set_pmap(PBVH *pbvh, MeshElemMap *pmap, int *mem) { - if (pbvh->pmap != pmap) { - BKE_pbvh_pmap_aquire(pmap); - } - pbvh->pmap = pmap; + pbvh->pmap_mem = mem; } void BKE_pbvh_set_bmesh(PBVH *pbvh, BMesh *bm) @@ -4449,42 +4444,6 @@ BMLog *BKE_pbvh_get_bm_log(PBVH *pbvh) return pbvh->bm_log; } -SculptPMap *BKE_pbvh_make_pmap(const struct Mesh *me) -{ - SculptPMap *pmap = (SculptPMap *)MEM_callocN(sizeof(*pmap), "SculptPMap"); - - BKE_mesh_vert_poly_map_create( - &pmap->pmap, &pmap->pmap_mem, me->polys(), me->corner_verts().data(), me->totvert); - - pmap->refcount = 1; - - return pmap; -} - -void BKE_pbvh_pmap_aquire(SculptPMap *pmap) -{ - pmap->refcount++; -} - -bool BKE_pbvh_pmap_release(SculptPMap *pmap) -{ - if (!pmap) { - return false; - } - - pmap->refcount--; - - if (pmap->refcount <= 0) { - MEM_SAFE_FREE(pmap->pmap); - MEM_SAFE_FREE(pmap->pmap_mem); - MEM_SAFE_FREE(pmap); - - return true; - } - - return false; -} - bool BKE_pbvh_is_drawing(const PBVH *pbvh) { return pbvh->is_drawing; diff --git a/source/blender/blenkernel/intern/pbvh_colors.cc b/source/blender/blenkernel/intern/pbvh_colors.cc index 36488e42b6c..2f6b739b1ab 100644 --- a/source/blender/blenkernel/intern/pbvh_colors.cc +++ b/source/blender/blenkernel/intern/pbvh_colors.cc @@ -89,7 +89,7 @@ static void pbvh_vertex_color_get(const PBVH &pbvh, PBVHVertRef vertex, float r_ int index = vertex.i; if (pbvh.color_domain == ATTR_DOMAIN_CORNER) { - const MeshElemMap &melem = pbvh.pmap->pmap[index]; + const MeshElemMap &melem = pbvh.pmap[index]; int count = 0; zero_v4(r_color); @@ -124,7 +124,7 @@ static void pbvh_vertex_color_set(PBVH &pbvh, PBVHVertRef vertex, const float co int index = vertex.i; if (pbvh.color_domain == ATTR_DOMAIN_CORNER) { - const MeshElemMap &melem = pbvh.pmap->pmap[index]; + const MeshElemMap &melem = pbvh.pmap[index]; for (const int i_poly : Span(melem.indices, melem.count)) { const IndexRange poly = pbvh.polys[i_poly]; diff --git a/source/blender/blenkernel/intern/pbvh_intern.hh b/source/blender/blenkernel/intern/pbvh_intern.hh index 2d741fe9e4c..c12c552ba80 100644 --- a/source/blender/blenkernel/intern/pbvh_intern.hh +++ b/source/blender/blenkernel/intern/pbvh_intern.hh @@ -182,7 +182,8 @@ struct PBVH { /* Mesh data */ struct MeshElemMap *vemap; - SculptPMap *pmap; + MeshElemMap *pmap; + int *pmap_mem; Mesh *mesh; diff --git a/source/blender/editors/sculpt_paint/sculpt.cc b/source/blender/editors/sculpt_paint/sculpt.cc index e6efb88aded..c8fb7f1e934 100644 --- a/source/blender/editors/sculpt_paint/sculpt.cc +++ b/source/blender/editors/sculpt_paint/sculpt.cc @@ -627,8 +627,8 @@ bool SCULPT_vertex_any_face_visible_get(SculptSession *ss, PBVHVertRef vertex) if (!ss->hide_poly) { return true; } - const MeshElemMap *vert_map = &ss->pmap->pmap[vertex.i]; - for (int j = 0; j < ss->pmap->pmap[vertex.i].count; j++) { + const MeshElemMap *vert_map = &ss->pmap[vertex.i]; + for (int j = 0; j < ss->pmap[vertex.i].count; j++) { if (!ss->hide_poly[vert_map->indices[j]]) { return true; } @@ -661,7 +661,7 @@ bool SCULPT_vertex_all_faces_visible_get(const SculptSession *ss, PBVHVertRef ve if (!ss->hide_poly) { return true; } - const MeshElemMap *vert_map = &ss->pmap->pmap[vertex.i]; + const MeshElemMap *vert_map = &ss->pmap[vertex.i]; for (int j = 0; j < vert_map->count; j++) { if (ss->hide_poly[vert_map->indices[j]]) { return false; @@ -713,7 +713,7 @@ void SCULPT_vertex_face_set_set(SculptSession *ss, PBVHVertRef vertex, int face_ switch (BKE_pbvh_type(ss->pbvh)) { case PBVH_FACES: { BLI_assert(ss->face_sets != nullptr); - const MeshElemMap *vert_map = &ss->pmap->pmap[vertex.i]; + const MeshElemMap *vert_map = &ss->pmap[vertex.i]; for (int j = 0; j < vert_map->count; j++) { const int poly_index = vert_map->indices[j]; if (ss->hide_poly && ss->hide_poly[poly_index]) { @@ -767,8 +767,8 @@ void SCULPT_vertex_face_set_increase(SculptSession *ss, PBVHVertRef vertex, cons switch (BKE_pbvh_type(ss->pbvh)) { case PBVH_FACES: { int index = (int)vertex.i; - MeshElemMap *vert_map = &ss->pmap->pmap[index]; - for (int j = 0; j < ss->pmap->pmap[index].count; j++) { + MeshElemMap *vert_map = &ss->pmap[index]; + for (int j = 0; j < ss->pmap[index].count; j++) { if (ss->face_sets[vert_map->indices[j]] > 0) { ss->face_sets[vert_map->indices[j]] += increase; } @@ -812,7 +812,7 @@ int SCULPT_vertex_face_set_get(SculptSession *ss, PBVHVertRef vertex) if (!ss->face_sets) { return SCULPT_FACE_SET_NONE; } - const MeshElemMap *vert_map = &ss->pmap->pmap[vertex.i]; + const MeshElemMap *vert_map = &ss->pmap[vertex.i]; int face_set = 0; for (int i = 0; i < vert_map->count; i++) { if (ss->face_sets[vert_map->indices[i]] > face_set) { @@ -858,7 +858,7 @@ bool SCULPT_vertex_has_face_set(SculptSession *ss, PBVHVertRef vertex, int face_ if (!ss->face_sets) { return face_set == SCULPT_FACE_SET_NONE; } - const MeshElemMap *vert_map = &ss->pmap->pmap[vertex.i]; + const MeshElemMap *vert_map = &ss->pmap[vertex.i]; for (int i = 0; i < vert_map->count; i++) { if (ss->face_sets[vert_map->indices[i]] == face_set) { return true; @@ -990,7 +990,7 @@ bool SCULPT_vertex_has_unique_face_set(const SculptSession *ss, PBVHVertRef vert */ static bool sculpt_check_unique_face_set_for_edge_in_base_mesh(SculptSession *ss, int v1, int v2) { - const MeshElemMap *vert_map = &ss->pmap->pmap[v1]; + const MeshElemMap *vert_map = &ss->pmap[v1]; int p1 = -1, p2 = -1; for (int i = 0; i < vert_map->count; i++) { const int poly_i = vert_map->indices[i]; diff --git a/source/blender/editors/sculpt_paint/sculpt_api.cc b/source/blender/editors/sculpt_paint/sculpt_api.cc index 75364f9b970..e113c6e2af6 100644 --- a/source/blender/editors/sculpt_paint/sculpt_api.cc +++ b/source/blender/editors/sculpt_paint/sculpt_api.cc @@ -135,7 +135,7 @@ static bool sculpt_check_unique_face_set_for_edge_in_base_mesh(const SculptSessi return true; } - const MeshElemMap *vert_map = &ss->pmap->pmap[v1]; + const MeshElemMap *vert_map = &ss->pmap[v1]; int p1 = -1, p2 = -1; for (int i = 0; i < vert_map->count; i++) { const IndexRange p = ss->polys[vert_map->indices[i]]; @@ -174,7 +174,7 @@ static bool sculpt_check_unique_face_set_in_base_mesh(const SculptSession *ss, i if (!ss->face_sets) { return true; } - const MeshElemMap *vert_map = &ss->pmap->pmap[index]; + const MeshElemMap *vert_map = &ss->pmap[index]; int face_set = -1; for (int i = 0; i < vert_map->count; i++) { if (face_set == -1) { @@ -366,7 +366,7 @@ static void faces_update_boundary_flags(const SculptSession *ss, const PBVHVertR ss->corner_edges.data(), ss->polys, ss->totfaces, - ss->pmap->pmap, + ss->pmap, vertex, ss->sharp_edge, ss->seam_edge, @@ -381,11 +381,11 @@ static void faces_update_boundary_flags(const SculptSession *ss, const PBVHVertR if (sculpt_check_boundary_vertex_in_base_mesh(ss, vertex.i)) { *flag |= SCULPT_BOUNDARY_MESH; - if (ss->pmap->pmap[vertex.i].count < 4) { + if (ss->pmap[vertex.i].count < 4) { bool ok = true; - for (int i = 0; i < ss->pmap->pmap[vertex.i].count; i++) { - const IndexRange mp = ss->polys[ss->pmap->pmap[vertex.i].indices[i]]; + for (int i = 0; i < ss->pmap[vertex.i].count; i++) { + const IndexRange mp = ss->polys[ss->pmap[vertex.i].indices[i]]; if (mp.size() < 4) { ok = false; } diff --git a/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc b/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc index 51096b90b06..d0d55dc7344 100644 --- a/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc +++ b/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc @@ -432,8 +432,8 @@ void SCULPT_pbvh_clear(Object *ob) { SculptSession *ss = ob->sculpt; - BKE_pbvh_pmap_release(ss->pmap); - ss->pmap = nullptr; + MEM_SAFE_FREE(ss->pmap); + MEM_SAFE_FREE(ss->pmap_mem); /* Clear out any existing DM and PBVH. */ if (ss->pbvh) { @@ -479,10 +479,8 @@ void SCULPT_dynamic_topology_enable_ex(Main *bmain, customdata_strip_templayers(&me->pdata, me->totpoly); if (ss->pbvh) { - if (ss->pmap) { - BKE_pbvh_pmap_release(ss->pmap); - ss->pmap = nullptr; - } + MEM_SAFE_FREE(ss->pmap); + MEM_SAFE_FREE(ss->pmap_mem); } if (!ss->bm || !ss->pbvh || BKE_pbvh_type(ss->pbvh) != PBVH_BMESH) { diff --git a/source/blender/editors/sculpt_paint/sculpt_expand.cc b/source/blender/editors/sculpt_paint/sculpt_expand.cc index 8b6133a548d..ba81b74323f 100644 --- a/source/blender/editors/sculpt_paint/sculpt_expand.cc +++ b/source/blender/editors/sculpt_paint/sculpt_expand.cc @@ -767,9 +767,8 @@ static float *sculpt_expand_diagonals_falloff_create(Object *ob, const PBVHVertR } } else { - for (int j = 0; j < ss->pmap->pmap[v_next_i].count; j++) { - for (const int vert : - ss->corner_verts.slice(ss->polys[ss->pmap->pmap[v_next_i].indices[j]])) { + for (int j = 0; j < ss->pmap[v_next_i].count; j++) { + for (const int vert : ss->corner_verts.slice(ss->polys[ss->pmap[v_next_i].indices[j]])) { const PBVHVertRef neighbor_v = BKE_pbvh_make_vref(vert); if (BLI_BITMAP_TEST(visited_verts, neighbor_v.i)) { continue; @@ -2185,7 +2184,7 @@ static void sculpt_expand_delete_face_set_id(int *r_face_sets, } const int totface = ss->totfaces; - MeshElemMap *pmap = ss->pmap->pmap; + MeshElemMap *pmap = ss->pmap; const blender::OffsetIndices polys = mesh->polys(); const blender::Span corner_verts = mesh->corner_verts(); diff --git a/source/blender/editors/sculpt_paint/sculpt_face_set.cc b/source/blender/editors/sculpt_paint/sculpt_face_set.cc index dcea9d86e96..f804bd38d23 100644 --- a/source/blender/editors/sculpt_paint/sculpt_face_set.cc +++ b/source/blender/editors/sculpt_paint/sculpt_face_set.cc @@ -1118,7 +1118,7 @@ static void sculpt_face_set_grow(Object *ob, continue; } for (const int vert : corner_verts.slice(polys[p])) { - const MeshElemMap *vert_map = &ss->pmap->pmap[vert]; + const MeshElemMap *vert_map = &ss->pmap[vert]; for (int i = 0; i < vert_map->count; i++) { const int neighbor_face_index = vert_map->indices[i]; if (neighbor_face_index == p) { @@ -1148,7 +1148,7 @@ static void sculpt_face_set_shrink(Object *ob, } if (abs(prev_face_sets[p]) == active_face_set_id) { for (const int vert_i : corner_verts.slice(polys[p])) { - const MeshElemMap *vert_map = &ss->pmap->pmap[vert_i]; + const MeshElemMap *vert_map = &ss->pmap[vert_i]; for (int i = 0; i < vert_map->count; i++) { const int neighbor_face_index = vert_map->indices[i]; if (neighbor_face_index == p) { -- 2.30.2 From f4951c51e5cdeda5d85f0e6f0b5007954dc46c91 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Sun, 14 May 2023 17:20:07 -0700 Subject: [PATCH 087/279] temp-sculpt-dyntopo: Fix broken boundary flags after draw face sets --- source/blender/blenkernel/intern/paint.cc | 3 ++- .../blender/editors/sculpt_paint/sculpt_face_set.cc | 11 ++++++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/source/blender/blenkernel/intern/paint.cc b/source/blender/blenkernel/intern/paint.cc index dc4f6bd7ce7..4eb249d7047 100644 --- a/source/blender/blenkernel/intern/paint.cc +++ b/source/blender/blenkernel/intern/paint.cc @@ -1499,6 +1499,7 @@ static void sculptsession_free_pbvh(Object *object) MEM_SAFE_FREE(ss->pmap); MEM_SAFE_FREE(ss->pmap_mem); + MEM_SAFE_FREE(ss->epmap); MEM_SAFE_FREE(ss->epmap_mem); @@ -1936,7 +1937,7 @@ static void sculpt_update_object( if (ob->type == OB_MESH && !ss->pmap) { if (!ss->pmap && ss->pbvh) { - ss->pmap = BKE_pbvh_get_pmap(ss->pbvh, &ss->pmap_mem); + //ss->pmap = BKE_pbvh_get_pmap(ss->pbvh, &ss->pmap_mem); } if (!ss->pmap) { diff --git a/source/blender/editors/sculpt_paint/sculpt_face_set.cc b/source/blender/editors/sculpt_paint/sculpt_face_set.cc index f804bd38d23..5fce262b9dc 100644 --- a/source/blender/editors/sculpt_paint/sculpt_face_set.cc +++ b/source/blender/editors/sculpt_paint/sculpt_face_set.cc @@ -214,6 +214,10 @@ static void do_draw_face_sets_brush_task_cb_ex(void *__restrict userdata, &automask_data); if (fade > 0.05f) { + for (int i = 0; i < fd.verts_num; i++) { + BKE_sculpt_boundary_flag_update(ss, fd.verts[i]); + } + *fd.face_set = ss->cache->paint_face_set; changed = true; } @@ -317,7 +321,7 @@ void SCULPT_do_draw_face_sets_brush(Sculpt *sd, Object *ob, Span nod } } else { - BKE_pbvh_parallel_range_settings(&settings, !data.set_active_faceset, nodes.size()); + BKE_pbvh_parallel_range_settings(&settings, false, nodes.size()); BLI_task_parallel_range(0, nodes.size(), &data, do_draw_face_sets_brush_task_cb_ex, &settings); } } @@ -761,6 +765,11 @@ static int sculpt_face_set_init_exec(bContext *C, wmOperator *op) SCULPT_undo_push_end(ob); + int verts_num = SCULPT_vertex_count_get(ob->sculpt); + for (int i : IndexRange(verts_num)) { + BKE_sculpt_boundary_flag_update(ob->sculpt, BKE_pbvh_index_to_vertex(ss->pbvh, i)); + } + /* Sync face sets visibility and vertex visibility as now all Face Sets are visible. */ SCULPT_visibility_sync_all_from_faces(ob); -- 2.30.2 From ea48ba1f8bcb4e27415d29bae44f9be3eb107f4e Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Mon, 15 May 2023 01:04:13 -0700 Subject: [PATCH 088/279] temp-sculpt-dyntopo: Fix various bugs related to area weights and dyntopo --- source/blender/blenkernel/BKE_pbvh.h | 2 +- source/blender/blenkernel/intern/dyntopo.cc | 30 +++++++++++-------- source/blender/blenkernel/intern/pbvh.cc | 5 ++-- .../blender/blenkernel/intern/pbvh_bmesh.cc | 14 +++++---- .../sculpt_paint/sculpt_brush_types.cc | 7 +++++ .../editors/sculpt_paint/sculpt_smooth.cc | 12 ++++---- .../editors/sculpt_paint/sculpt_undo.cc | 2 +- 7 files changed, 46 insertions(+), 26 deletions(-) diff --git a/source/blender/blenkernel/BKE_pbvh.h b/source/blender/blenkernel/BKE_pbvh.h index c67b0d5655a..e6296462b76 100644 --- a/source/blender/blenkernel/BKE_pbvh.h +++ b/source/blender/blenkernel/BKE_pbvh.h @@ -679,7 +679,7 @@ struct TableGSet *BKE_pbvh_bmesh_node_unique_verts(PBVHNode *node); struct TableGSet *BKE_pbvh_bmesh_node_other_verts(PBVHNode *node); struct TableGSet *BKE_pbvh_bmesh_node_faces(PBVHNode *node); -void BKE_pbvh_bmesh_regen_node_verts(PBVH *pbvh); +void BKE_pbvh_bmesh_regen_node_verts(PBVH *pbvh, bool report); void BKE_pbvh_bmesh_mark_node_regen(PBVH *pbvh, PBVHNode *node); void BKE_pbvh_bmesh_after_stroke(PBVH *pbvh, bool force_balance); diff --git a/source/blender/blenkernel/intern/dyntopo.cc b/source/blender/blenkernel/intern/dyntopo.cc index 3ea79336a76..2330492b5f9 100644 --- a/source/blender/blenkernel/intern/dyntopo.cc +++ b/source/blender/blenkernel/intern/dyntopo.cc @@ -3036,20 +3036,26 @@ static void pbvh_split_edges(EdgeQueueContext *eq_ctx, } if (newf) { -#if 1 rl->e->head.hflag &= ~BM_ELEM_TAG; -# if 1 - long_edge_queue_edge_add_recursive( - eq_ctx, - rl, - rl->next, - calc_weighted_length(eq_ctx, rl->e->v1, rl->e->v2, -1.0f), - eq_ctx->limit_len_max, - 0); -# else - edge_queue_insert_unified(eq_ctx, rl->e); +#if 1 + /* Try to improve quality by inserting new edge into queue. + * This is a bit tricky since we don't want to expand outside + * the brush radius too much, but we can't stay strictly inside + * either. + */ -# endif + float3 co = rl->v->co; + co = (co + rl->next->v->co) * 0.5f; + + float len = len_v3v3(co, eq_ctx->center); + float radius = sqrtf(eq_ctx->radius_squared); + float w = calc_weighted_length(eq_ctx, rl->e->v1, rl->e->v2, -1.0f); + float fac = 1.0f + 2.0 * (len / radius); + + if (w / fac > eq_ctx->limit_len_max) { + + long_edge_queue_edge_add_recursive(eq_ctx, rl, rl->next, w, eq_ctx->limit_len_max, 0); + } #endif check_face_is_manifold(pbvh, bm, newf); diff --git a/source/blender/blenkernel/intern/pbvh.cc b/source/blender/blenkernel/intern/pbvh.cc index f54720771ed..d5217e5bd8a 100644 --- a/source/blender/blenkernel/intern/pbvh.cc +++ b/source/blender/blenkernel/intern/pbvh.cc @@ -2270,13 +2270,14 @@ void BKE_pbvh_node_mark_update_visibility(PBVHNode *node) void BKE_pbvh_vert_tag_update_normal_visibility(PBVHNode *node) { node->flag |= PBVH_UpdateVisibility | PBVH_RebuildDrawBuffers | PBVH_UpdateDrawBuffers | - PBVH_UpdateRedraw | PBVH_UpdateCurvatureDir | PBVH_UpdateTris; + PBVH_UpdateRedraw | PBVH_UpdateCurvatureDir | PBVH_UpdateTris | + PBVH_UpdateTriAreas; } void BKE_pbvh_node_mark_rebuild_draw(PBVHNode *node) { node->flag |= PBVH_RebuildDrawBuffers | PBVH_UpdateDrawBuffers | PBVH_UpdateRedraw | - PBVH_UpdateCurvatureDir; + PBVH_UpdateCurvatureDir | PBVH_UpdateTriAreas; } void BKE_pbvh_node_mark_redraw(PBVHNode *node) diff --git a/source/blender/blenkernel/intern/pbvh_bmesh.cc b/source/blender/blenkernel/intern/pbvh_bmesh.cc index ddbfd8eb20d..b6ffee8b974 100644 --- a/source/blender/blenkernel/intern/pbvh_bmesh.cc +++ b/source/blender/blenkernel/intern/pbvh_bmesh.cc @@ -1229,7 +1229,7 @@ void bke_pbvh_insert_face(PBVH *pbvh, struct BMFace *f) bke_pbvh_insert_face_finalize(pbvh, f, ni); } -static void pbvh_bmesh_regen_node_verts(PBVH *pbvh, PBVHNode *node) +static void pbvh_bmesh_regen_node_verts(PBVH *pbvh, PBVHNode *node, bool report) { node->flag &= ~PBVH_RebuildNodeVerts; @@ -1244,7 +1244,9 @@ static void pbvh_bmesh_regen_node_verts(PBVH *pbvh, PBVHNode *node) auto check_vert = [&](BMVert *v) { if (BM_elem_is_free(reinterpret_cast(v), BM_VERT)) { - printf("%s: corrupted vertex %p\n", __func__, v); + if (report) { + printf("%s: corrupted vertex %p\n", __func__, v); + } return; } int ni2 = BM_ELEM_CD_GET_INT(v, cd_vert_node); @@ -1298,7 +1300,9 @@ static void pbvh_bmesh_regen_node_verts(PBVH *pbvh, PBVHNode *node) TGSET_ITER (v, old_unique_verts) { if (BM_elem_is_free(reinterpret_cast(v), BM_VERT)) { - printf("%s: corrupted vertex %p\n", __func__, v); + if (report) { + printf("%s: corrupted vertex %p\n", __func__, v); + } continue; } @@ -1376,7 +1380,7 @@ PBVHNode *BKE_pbvh_get_node_leaf_safe(PBVH *pbvh, int i) return nullptr; } -void BKE_pbvh_bmesh_regen_node_verts(PBVH *pbvh) +void BKE_pbvh_bmesh_regen_node_verts(PBVH *pbvh, bool report) { for (int i = 0; i < pbvh->totnode; i++) { PBVHNode *node = pbvh->nodes + i; @@ -1385,7 +1389,7 @@ void BKE_pbvh_bmesh_regen_node_verts(PBVH *pbvh) continue; } - pbvh_bmesh_regen_node_verts(pbvh, node); + pbvh_bmesh_regen_node_verts(pbvh, node, report); } } diff --git a/source/blender/editors/sculpt_paint/sculpt_brush_types.cc b/source/blender/editors/sculpt_paint/sculpt_brush_types.cc index 5dbf7c5b666..11acbfbb9c0 100644 --- a/source/blender/editors/sculpt_paint/sculpt_brush_types.cc +++ b/source/blender/editors/sculpt_paint/sculpt_brush_types.cc @@ -2859,6 +2859,10 @@ ATTR_NO_OPT static void do_topology_rake_bmesh_task_cb_ex(void *__restrict userd SCULPT_curvature_begin(ss, data->nodes[n], false); } + if (brush->flag2 & BRUSH_SMOOTH_USE_AREA_WEIGHT) { + BKE_pbvh_check_tri_areas(ss->pbvh, data->nodes[n]); + } + PBVHVertexIter vd; BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { float direction2[3]; @@ -2943,6 +2947,9 @@ void SCULPT_bmesh_topology_rake(Sculpt *sd, Object *ob, Span nodes, { BKE_pbvh_update_all_tri_areas(ss->pbvh); } + else if (brush->flag2 & BRUSH_SMOOTH_USE_AREA_WEIGHT) { + BKE_pbvh_face_areas_begin(ss->pbvh); + } if (brush->flag2 & BRUSH_CURVATURE_RAKE) { BKE_sculpt_ensure_curvature_dir(ob); diff --git a/source/blender/editors/sculpt_paint/sculpt_smooth.cc b/source/blender/editors/sculpt_paint/sculpt_smooth.cc index 3e33d67e386..fa9fa2d0036 100644 --- a/source/blender/editors/sculpt_paint/sculpt_smooth.cc +++ b/source/blender/editors/sculpt_paint/sculpt_smooth.cc @@ -558,7 +558,9 @@ static void do_smooth_brush_task_cb_ex(void *__restrict userdata, SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( ss, &test, data->brush->falloff_shape); - BKE_pbvh_check_tri_areas(ss->pbvh, data->nodes[n]); + if (brush->flag2 & BRUSH_SMOOTH_USE_AREA_WEIGHT) { + BKE_pbvh_check_tri_areas(ss->pbvh, data->nodes[n]); + } const int thread_id = BLI_task_parallel_thread_id(tls); AutomaskingNodeData automask_data; @@ -652,13 +654,13 @@ void SCULPT_smooth( SCULPT_vertex_random_access_ensure(ss); SCULPT_boundary_info_ensure(ob); - if (brush->flag2 & BRUSH_SMOOTH_USE_AREA_WEIGHT) { - BKE_pbvh_face_areas_begin(ss->pbvh); - } - for (iteration = 0; iteration <= count; iteration++) { const float strength = (iteration != count) ? 1.0f : last; + if (brush->flag2 & BRUSH_SMOOTH_USE_AREA_WEIGHT) { + BKE_pbvh_face_areas_begin(ss->pbvh); + } + SculptThreadedTaskData data{}; data.sd = sd; data.ob = ob; diff --git a/source/blender/editors/sculpt_paint/sculpt_undo.cc b/source/blender/editors/sculpt_paint/sculpt_undo.cc index 10121f3d1cb..f0724baddcb 100644 --- a/source/blender/editors/sculpt_paint/sculpt_undo.cc +++ b/source/blender/editors/sculpt_paint/sculpt_undo.cc @@ -1001,7 +1001,7 @@ static void sculpt_undo_bmesh_restore_generic(SculptUndoNode *unode, Object *ob, } // pbvh_bmesh_check_nodes(ss->pbvh); - BKE_pbvh_bmesh_regen_node_verts(ss->pbvh); + BKE_pbvh_bmesh_regen_node_verts(ss->pbvh, false); // pbvh_bmesh_check_nodes(ss->pbvh); BKE_pbvh_update_bounds(ss->pbvh, PBVH_UpdateBB | PBVH_UpdateOriginalBB | PBVH_UpdateRedraw); -- 2.30.2 From 45e7875b70f1558b5309a1576a4c1678178c85a1 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Mon, 15 May 2023 15:32:13 -0700 Subject: [PATCH 089/279] temp-sculpt-dyntopo: Bring back 3/4 pole cleanup option This will probably not be user-configurable. It makes nicer topology and, more importantly, seems to improve convergence of subdivide only mode. --- scripts/startup/bl_ui/space_view3d_toolbar.py | 7 + source/blender/blenkernel/intern/dyntopo.cc | 211 ++++++------------ .../blenkernel/intern/dyntopo_intern.hh | 4 +- source/blender/blenkernel/intern/pbvh.cc | 2 +- .../blender/blenkernel/intern/pbvh_bmesh.cc | 27 ++- source/blender/editors/sculpt_paint/sculpt.cc | 4 + .../editors/sculpt_paint/sculpt_api.cc | 46 ++-- .../sculpt_paint/sculpt_brush_types.cc | 14 +- .../editors/sculpt_paint/sculpt_smooth.cc | 39 ++-- source/blender/makesrna/intern/rna_brush.c | 5 +- 10 files changed, 180 insertions(+), 179 deletions(-) diff --git a/scripts/startup/bl_ui/space_view3d_toolbar.py b/scripts/startup/bl_ui/space_view3d_toolbar.py index bef50143ba7..3f8156c5d57 100644 --- a/scripts/startup/bl_ui/space_view3d_toolbar.py +++ b/scripts/startup/bl_ui/space_view3d_toolbar.py @@ -925,6 +925,13 @@ class VIEW3D_PT_sculpt_dyntopo(Panel, View3DPaintPanel): "collapse" ) + UnifiedPaintPanel.prop_unified_dyntopo( + sub, + context, + brush, + "cleanup" + ) + UnifiedPaintPanel.prop_unified_dyntopo( sub, context, diff --git a/source/blender/blenkernel/intern/dyntopo.cc b/source/blender/blenkernel/intern/dyntopo.cc index 2330492b5f9..09837000f83 100644 --- a/source/blender/blenkernel/intern/dyntopo.cc +++ b/source/blender/blenkernel/intern/dyntopo.cc @@ -253,23 +253,7 @@ static void edge_queue_insert_unified(EdgeQueueContext *eq_ctx, BMEdge *e) static void edge_queue_insert_val34_vert(EdgeQueueContext *eq_ctx, BMVert *v) { - eq_ctx->tot_used_verts++; - - if (!eq_ctx->used_verts || eq_ctx->tot_used_verts >= eq_ctx->used_verts_size) { - int newlen = (eq_ctx->tot_used_verts + 1); - newlen += newlen >> 1; - - eq_ctx->used_verts_size = newlen; - if (eq_ctx->used_verts) { - eq_ctx->used_verts = (BMVert **)MEM_reallocN(eq_ctx->used_verts, newlen * sizeof(BMVert *)); - } - else { - eq_ctx->used_verts = (BMVert **)MEM_malloc_arrayN( - newlen, sizeof(BMVert *), "eq_ctx->used_verts"); - } - } - - eq_ctx->used_verts[eq_ctx->tot_used_verts - 1] = v; + eq_ctx->used_verts.append(v); } static bool edge_queue_vert_in_sphere(const EdgeQueueContext *eq_ctx, BMVert *v) @@ -623,9 +607,9 @@ BLI_INLINE int dyntopo_thread_rand(int seed) return (seed * multiplier + addend) & mask; } -static void unified_edge_queue_task_cb(void *__restrict userdata, - const int n, - const TaskParallelTLS *__restrict /*tls*/) +ATTR_NO_OPT static void unified_edge_queue_task_cb(void *__restrict userdata, + const int n, + const TaskParallelTLS *__restrict /*tls*/) { EdgeQueueThreadData *tdata = ((EdgeQueueThreadData *)userdata) + n; PBVH *pbvh = tdata->pbvh; @@ -643,34 +627,23 @@ static void unified_edge_queue_task_cb(void *__restrict userdata, const char facetag = BM_ELEM_TAG_ALT; -#if 1 -# if 0 - /* Try to be nice to branch predictor. */ - int off = (seed = dyntopo_thread_rand(seed)); - int stepi = off & 65535; -# endif /* - we care more about convergence to accurate results - then accuracy in any individual runs. profiling - has shown this loop overwhelms the L3 cache, - so randomly skip bits of it. - */ + * We care more about convergence to accurate results + * then accuracy in any individual runs. profiling + * has shown this loop overwhelms the L3 cache, + * so randomly skip bits of it. + */ TGSET_ITER (f, node->bm_faces) { BMLoop *l = f->l_first; f->head.hflag &= ~facetag; -# if 0 - if ((stepi++) & 3) { - continue; - } -# else + /* Stochastically skip faces. */ if ((seed = dyntopo_thread_rand(seed)) & 3) { continue; } -# endif do { - /* kind of tricky to atomicly update flags here. . . */ + /* Kind of tricky to atomicly update flags here. . . */ BMEdge edge = *l->e; edge.head.hflag &= ~BM_ELEM_TAG; @@ -684,7 +657,6 @@ static void unified_edge_queue_task_cb(void *__restrict userdata, } while (l != f->l_first); } TGSET_ITER_END -#endif PBVHTriBuf *tribuf = node->tribuf; for (int i = 0; i < node->tribuf->tottri; i++) { @@ -709,17 +681,18 @@ static void unified_edge_queue_task_cb(void *__restrict userdata, if (eq_ctx->edge_queue_tri_in_range(eq_ctx, vs, f->no)) { f->head.hflag |= facetag; - /* Check each edge of the face */ + /* Check each edge of the face. */ BMLoop *l_first = BM_FACE_FIRST_LOOP(f); BMLoop *l_iter = l_first; do { - /* are we owned by this node? if so, make sure origdata is up to date */ + /* Are we owned by this node? if so, make sure origdata is up to date. */ if (BM_ELEM_CD_GET_INT(l_iter->v, pbvh->cd_vert_node_offset) == ni) { BKE_pbvh_bmesh_check_origdata(eq_ctx->ss, l_iter->v, pbvh->stroke_id); } - /* try to improve convergence by applying a small amount of smoothing to topology, - but tangentially to surface. + /* Try to improve convergence by applying a small amount of smoothing to topology, + * but tangentially to surface. We can stochastically skip this and still get the + * benefit to convergence. */ int randval = (seed = dyntopo_thread_rand(seed)) & 255; @@ -734,8 +707,7 @@ static void unified_edge_queue_task_cb(void *__restrict userdata, float len_sq = calc_edge_length(eq_ctx, l_iter->e->v1, l_iter->e->v2); - /* subdivide walks the mesh a bit for better transitions in the topology */ - + /* Subdivide walks the mesh a bit for better transitions in the topology. */ if ((eq_ctx->mode & PBVH_Subdivide) && (len_sq > eq_ctx->limit_len_max_sqr)) { long_edge_queue_edge_add_recursive_2( tdata, l_iter->radial_next, l_iter, len_sq, eq_ctx->limit_len_max, 0, true); @@ -1642,24 +1614,20 @@ static void edge_queue_create_local(EdgeQueueContext *eq_ctx, } } -static bool cleanup_valence_3_4(EdgeQueueContext *ectx, - PBVH *pbvh, - const float center[3], - const float /*view_normal*/[3], - float radius, - const bool /*use_frontface*/, - const bool /*use_projected*/) +ATTR_NO_OPT static bool cleanup_valence_3_4(EdgeQueueContext *ectx, + PBVH *pbvh, + const float center[3], + const float /*view_normal*/[3], + float radius, + const bool /*use_frontface*/, + const bool /*use_projected*/) { - return false; /* XXX not working with local collapse/subdivide mode. */ bool modified = false; bm_logstack_push(); bm_log_message(" == cleanup_valence_3_4 == "); - /* Push log subentry. */ - BM_log_entry_add_ex(pbvh->header.bm, pbvh->bm_log, true); - float radius2 = radius * 1.25; float rsqr = radius2 * radius2; @@ -1667,28 +1635,17 @@ static bool cleanup_valence_3_4(EdgeQueueContext *ectx, int updateflag = SCULPTFLAG_NEED_DISK_SORT | SCULPTFLAG_NEED_VALENCE; - for (int i = 0; i < ectx->tot_used_verts; i++) { - BMVert *v = ectx->used_verts[i]; - + for (BMVert *v : ectx->used_verts) { if (bm_elem_is_free((BMElem *)v, BM_VERT)) { continue; } const int n = BM_ELEM_CD_GET_INT(v, cd_vert_node); - if (n == DYNTOPO_NODE_NONE) { continue; } - BKE_pbvh_bmesh_check_valence(pbvh, {(intptr_t)v}); - - int val = BM_ELEM_CD_GET_INT(v, pbvh->cd_valence); - if (val != 4 && val != 3) { - continue; - } - PBVHVertRef sv = {(intptr_t)v}; - if (len_squared_v3v3(v->co, center) >= rsqr || !v->e || ectx->mask_cb(sv, ectx->mask_cb_data) < 0.5f) { @@ -1697,18 +1654,16 @@ static bool cleanup_valence_3_4(EdgeQueueContext *ectx, validate_vert(pbvh, pbvh->header.bm, v, false, true); check_vert_fan_are_tris(pbvh, v); + pbvh_check_vert_boundary(pbvh, v); validate_vert(pbvh, pbvh->header.bm, v, true, true); -#if 0 - /* Check valence again. */ - val = BM_vert_edge_count(v); + BKE_pbvh_bmesh_check_valence(pbvh, {(intptr_t)v}); + + int val = BM_ELEM_CD_GET_INT(v, pbvh->cd_valence); if (val != 4 && val != 3) { - printf("pbvh valence error\n"); continue; } -#endif - pbvh_check_vert_boundary(pbvh, v); int boundflag = BM_ELEM_CD_GET_INT(v, pbvh->cd_boundary_flag); if (boundflag & SCULPTVERT_ALL_BOUNDARY) { @@ -1795,20 +1750,7 @@ static bool cleanup_valence_3_4(EdgeQueueContext *ectx, int ni = BM_ELEM_CD_GET_INT(v, pbvh->cd_vert_node_offset); if (ni < 0) { - printf("cleanup_valence_3_4 error!\n"); - - /* Attempt to recover. */ - - BMFace *f; - BM_ITER_ELEM (f, &iter, v, BM_FACES_OF_VERT) { - int ni2 = BM_ELEM_CD_GET_INT(f, pbvh->cd_face_node_offset); - - if (ni2 != DYNTOPO_NODE_NONE) { - PBVHNode *node2 = pbvh->nodes + ni2; - - BLI_table_gset_remove(node2->bm_unique_verts, v, nullptr); - } - } + continue; } pbvh_bmesh_vert_remove(pbvh, v); @@ -1818,9 +1760,6 @@ static bool cleanup_valence_3_4(EdgeQueueContext *ectx, int ni2 = BM_ELEM_CD_GET_INT(f, pbvh->cd_face_node_offset); if (ni2 != DYNTOPO_NODE_NONE) { - // PBVHNode *node2 = pbvh->nodes + ni2; - // BLI_table_gset_remove(node2->bm_unique_verts, v, nullptr); - pbvh_bmesh_face_remove(pbvh, f, true, true, true); } else { @@ -1862,6 +1801,13 @@ static bool cleanup_valence_3_4(EdgeQueueContext *ectx, ls[j] = ls2[(j + 1) % 4]; } } + + if (!BM_edge_exists(ls[0]->v, ls[2]->v)) { + BMEdge *e_diag = BM_edge_create( + pbvh->header.bm, ls[0]->v, ls[2]->v, nullptr, BM_CREATE_NOP); + BM_idmap_check_assign(pbvh->bm_idmap, reinterpret_cast(e_diag)); + BM_log_edge_added(pbvh->header.bm, pbvh->bm_log, e_diag); + } } vs[0] = ls[0]->v; @@ -1884,6 +1830,8 @@ static bool cleanup_valence_3_4(EdgeQueueContext *ectx, if (ok1) { f1 = pbvh_bmesh_face_create(pbvh, n, vs, nullptr, l->f, true, false); + BM_idmap_check_assign(pbvh->bm_idmap, reinterpret_cast(f1)); + normal_tri_v3( f1->no, f1->l_first->v->co, f1->l_first->next->v->co, f1->l_first->prev->v->co); @@ -1916,6 +1864,7 @@ static bool cleanup_valence_3_4(EdgeQueueContext *ectx, } f2 = pbvh_bmesh_face_create(pbvh, n, vs, nullptr, example, true, false); + BM_idmap_check_assign(pbvh->bm_idmap, reinterpret_cast(f2)); CustomData_bmesh_swap_data_simple(&pbvh->header.bm->ldata, &f2->l_first->prev->head.data, @@ -1958,15 +1907,11 @@ static bool cleanup_valence_3_4(EdgeQueueContext *ectx, pbvh_kill_vert(pbvh, v, true, true); if (f1 && !bm_elem_is_free((BMElem *)f1, BM_FACE)) { - if (!bm_elem_is_free((BMElem *)f1, BM_FACE)) { - check_face_is_manifold(pbvh, pbvh->header.bm, f1); - } + check_face_is_manifold(pbvh, pbvh->header.bm, f1); } if (f2 && !bm_elem_is_free((BMElem *)f2, BM_FACE)) { - if (!bm_elem_is_free((BMElem *)f2, BM_FACE)) { - check_face_is_manifold(pbvh, pbvh->header.bm, f2); - } + check_face_is_manifold(pbvh, pbvh->header.bm, f2); } } @@ -1980,47 +1925,41 @@ static bool cleanup_valence_3_4(EdgeQueueContext *ectx, return modified; } -static bool do_cleanup_3_4(EdgeQueueContext *eq_ctx, - PBVH *pbvh, - const float center[3], - const float view_normal[3], - float radius, - bool use_frontface, - bool use_projected) +ATTR_NO_OPT static bool do_cleanup_3_4(EdgeQueueContext *eq_ctx, + PBVH *pbvh, + const float center[3], + const float view_normal[3], + float radius, + bool use_frontface, + bool use_projected) { bool modified = false; #if 1 - if (!(eq_ctx->mode & - (PBVH_Subdivide | PBVH_Collapse | PBVH_LocalCollapse | PBVH_LocalSubdivide))) { - for (int n = 0; n < pbvh->totnode; n++) { - PBVHNode *node = pbvh->nodes + n; - BMVert *v; + eq_ctx->used_verts.clear(); - if (!(node->flag & PBVH_Leaf) || !(node->flag & PBVH_UpdateTopology)) { - continue; - } + for (const PBVHNode &node : Span(pbvh->nodes, pbvh->totnode)) { + BMVert *v; - TGSET_ITER (v, node->bm_unique_verts) { - if (!eq_ctx->edge_queue_vert_in_range(eq_ctx, v)) { - continue; - } - - if (use_frontface && dot_v3v3(v->no, view_normal) < 0.0f) { - continue; - } - - if (dyntopo_test_flag(pbvh, v, SCULPTFLAG_NEED_VALENCE)) { - BKE_pbvh_bmesh_update_valence(pbvh, {(intptr_t)v}); - } - - if (BM_ELEM_CD_GET_INT(v, pbvh->cd_valence) < 5) { - edge_queue_insert_val34_vert(eq_ctx, v); - } - } - TGSET_ITER_END; + if (!(node.flag & PBVH_Leaf) || !(node.flag & PBVH_UpdateTopology)) { + continue; } + + TGSET_ITER (v, node.bm_unique_verts) { + if (dyntopo_test_flag(pbvh, v, SCULPTFLAG_NEED_VALENCE)) { + BKE_pbvh_bmesh_update_valence(pbvh, {(intptr_t)v}); + } + + bool ok = BM_ELEM_CD_GET_INT(v, pbvh->cd_valence) < 5; + ok = ok && eq_ctx->edge_queue_vert_in_range(eq_ctx, v); + + if (ok) { + edge_queue_insert_val34_vert(eq_ctx, v); + } + } + TGSET_ITER_END; } + #endif BM_log_entry_add_ex(pbvh->header.bm, pbvh->bm_log, true); @@ -2122,6 +2061,10 @@ extern "C" bool BKE_pbvh_bmesh_update_topology(SculptSession *ss, safe_smooth = DYNTOPO_SAFE_SMOOTH_FAC; } + // XXX + disable_surface_relax = false; + safe_smooth = DYNTOPO_SAFE_SMOOTH_FAC; + EdgeQueueContext eq_ctx = {}; eq_ctx.ss = ss; @@ -2404,6 +2347,7 @@ extern "C" bool BKE_pbvh_bmesh_update_topology(SculptSession *ss, MEM_SAFE_FREE(edges); BLI_smallhash_release(&subd_edges); + // XXX if (mode & PBVH_Cleanup) { modified |= do_cleanup_3_4( &eq_ctx, pbvh, center, eq_ctx.view_normal, radius, use_frontface, use_projected); @@ -2444,13 +2388,6 @@ extern "C" bool BKE_pbvh_bmesh_update_topology(SculptSession *ss, node->flag &= ~PBVH_UpdateTopology; } -#ifdef DYNTOPO_USE_MINMAX_HEAP - if (eq_ctx.used_verts) { - MEM_SAFE_FREE(eq_ctx.used_verts); - // BLI_table_gset_free(eq_ctx.used_verts, nullptr); - } -#endif - BLI_buffer_free(&edge_loops); BLI_buffer_free(&deleted_faces); diff --git a/source/blender/blenkernel/intern/dyntopo_intern.hh b/source/blender/blenkernel/intern/dyntopo_intern.hh index 1b03f9e933e..6d8e4fbad2b 100644 --- a/source/blender/blenkernel/intern/dyntopo_intern.hh +++ b/source/blender/blenkernel/intern/dyntopo_intern.hh @@ -224,9 +224,7 @@ struct EdgeQueueContext { int max_heap_mm; // TableGSet *used_verts; - BMVert **used_verts = nullptr; - int used_verts_size; - int tot_used_verts; + blender::Vector used_verts; float view_normal[3]; bool use_view_normal; diff --git a/source/blender/blenkernel/intern/pbvh.cc b/source/blender/blenkernel/intern/pbvh.cc index d5217e5bd8a..77aab874da1 100644 --- a/source/blender/blenkernel/intern/pbvh.cc +++ b/source/blender/blenkernel/intern/pbvh.cc @@ -4138,7 +4138,7 @@ void BKE_pbvh_get_vert_face_areas(PBVH *pbvh, PBVHVertRef vertex, float *r_areas } if (j >= valence) { - printf("%s: error, corrupt edge cycle\n", __func__); + printf("%s: error, corrupt edge cycle, valence was %d expected %d\n", __func__, j, valence); break; } diff --git a/source/blender/blenkernel/intern/pbvh_bmesh.cc b/source/blender/blenkernel/intern/pbvh_bmesh.cc index b6ffee8b974..fe560c0235f 100644 --- a/source/blender/blenkernel/intern/pbvh_bmesh.cc +++ b/source/blender/blenkernel/intern/pbvh_bmesh.cc @@ -260,12 +260,27 @@ void pbvh_kill_vert(PBVH *pbvh, BMVert *v, bool log_vert, bool log_edges) BMEdge *e = v->e; bm_logstack_push(); - if (log_edges) { - if (e) { - do { - BM_log_edge_removed(pbvh->header.bm, pbvh->bm_log, e); - } while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e); - } + if (e && log_edges) { + do { + BM_log_edge_removed(pbvh->header.bm, pbvh->bm_log, e); + } while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e); + } + + /* Release IDs. */ + if (e) { + do { + BMLoop *l = e->l; + if (l) { + do { + int id = BM_idmap_get_id(pbvh->bm_idmap, reinterpret_cast(l->f)); + if (id != BM_ID_NONE) { + BM_idmap_release(pbvh->bm_idmap, (BMElem *)l->f, true); + } + } while ((l = l->radial_next) != e->l); + } + + BM_idmap_release(pbvh->bm_idmap, (BMElem *)e, true); + } while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e); } if (log_vert) { diff --git a/source/blender/editors/sculpt_paint/sculpt.cc b/source/blender/editors/sculpt_paint/sculpt.cc index c8fb7f1e934..c44ca6501c4 100644 --- a/source/blender/editors/sculpt_paint/sculpt.cc +++ b/source/blender/editors/sculpt_paint/sculpt.cc @@ -4020,6 +4020,10 @@ static void sculpt_topology_update(Sculpt *sd, } } + if (dyntopo_mode & DYNTOPO_CLEANUP) { + mode |= PBVH_Cleanup; + } + /* Force both subdivide and collapse for simplify brush. */ // XXX done with inherit flags now if (brush->sculpt_tool == SCULPT_TOOL_SIMPLIFY) { diff --git a/source/blender/editors/sculpt_paint/sculpt_api.cc b/source/blender/editors/sculpt_paint/sculpt_api.cc index e113c6e2af6..a91d9dd0e10 100644 --- a/source/blender/editors/sculpt_paint/sculpt_api.cc +++ b/source/blender/editors/sculpt_paint/sculpt_api.cc @@ -204,7 +204,9 @@ eSculptBoundary SCULPT_edge_is_boundary(const SculptSession *ss, ret |= (!e->l || e->l == e->l->radial_next) ? SCULPT_BOUNDARY_MESH : 0; } - if ((typemask & SCULPT_BOUNDARY_FACE_SET) && e->l && e->l != e->l->radial_next) { + if ((typemask & SCULPT_BOUNDARY_FACE_SET) && e->l && e->l != e->l->radial_next && + ss->cd_faceset_offset != -1) + { if (ss->boundary_symmetry) { // TODO: calc and cache this properly @@ -217,11 +219,7 @@ eSculptBoundary SCULPT_edge_is_boundary(const SculptSession *ss, int fset1 = BM_ELEM_CD_GET_INT(e->l->f, ss->cd_faceset_offset); int fset2 = BM_ELEM_CD_GET_INT(e->l->radial_next->f, ss->cd_faceset_offset); - bool ok = (fset1 < 0) != (fset2 < 0); - - ok = ok || fset1 != fset2; - - ret |= ok ? SCULPT_BOUNDARY_FACE_SET : 0; + ret |= fset1 != fset2 ? SCULPT_BOUNDARY_FACE_SET : 0; } } @@ -243,14 +241,35 @@ eSculptBoundary SCULPT_edge_is_boundary(const SculptSession *ss, break; } case PBVH_FACES: { - eSculptBoundary mask = typemask & (SCULPT_BOUNDARY_MESH | SCULPT_BOUNDARY_FACE_SET); - PBVHVertRef v1, v2; + eSculptBoundary epmap_mask = typemask & (SCULPT_BOUNDARY_MESH | SCULPT_BOUNDARY_FACE_SET); - SCULPT_edge_get_verts(ss, edge, &v1, &v2); + /* Some boundary types require an edge->poly map to be fully accurate. */ + if (epmap_mask && ss->epmap) { + if (epmap_mask & SCULPT_BOUNDARY_FACE_SET) { + MeshElemMap *polys = &ss->epmap[edge.i]; + int fset = -1; - if (mask) { // use less accurate approximation for now - eSculptBoundary a = SCULPT_vertex_is_boundary(ss, v1, mask); - eSculptBoundary b = SCULPT_vertex_is_boundary(ss, v2, mask); + for (int i : IndexRange(polys->count)) { + if (fset == -1) { + fset = ss->face_sets[polys->indices[i]]; + } + else if (ss->face_sets[polys->indices[i]] != fset) { + ret |= SCULPT_BOUNDARY_FACE_SET; + break; + } + } + } + if (epmap_mask & SCULPT_BOUNDARY_MESH) { + ret |= ss->epmap[edge.i].count == 1 ? SCULPT_BOUNDARY_MESH : 0; + } + } + else if (epmap_mask) + { /* No edge->poly map; approximate from vertices (will give artifacts on corners). */ + PBVHVertRef v1, v2; + + SCULPT_edge_get_verts(ss, edge, &v1, &v2); + eSculptBoundary a = SCULPT_vertex_is_boundary(ss, v1, epmap_mask); + eSculptBoundary b = SCULPT_vertex_is_boundary(ss, v2, epmap_mask); ret |= a & b; } @@ -628,7 +647,8 @@ void SCULPT_apply_dyntopo_settings(SculptSession *ss, Sculpt *sculpt, Brush *bru ds_final->repeat = ds_final->inherit & DYNTOPO_INHERIT_REPEAT ? ds2->repeat : ds1->repeat; } -bool SCULPT_face_is_hidden(const SculptSession *ss, PBVHFaceRef face) { +bool SCULPT_face_is_hidden(const SculptSession *ss, PBVHFaceRef face) +{ if (ss->bm) { BMFace *f = reinterpret_cast(face.i); return BM_elem_flag_test(f, BM_ELEM_HIDDEN); diff --git a/source/blender/editors/sculpt_paint/sculpt_brush_types.cc b/source/blender/editors/sculpt_paint/sculpt_brush_types.cc index 11acbfbb9c0..14ae590a613 100644 --- a/source/blender/editors/sculpt_paint/sculpt_brush_types.cc +++ b/source/blender/editors/sculpt_paint/sculpt_brush_types.cc @@ -2823,9 +2823,9 @@ void SCULPT_do_displacement_smear_brush(Sculpt *sd, Object *ob, Span /** \name Sculpt Topology Rake (Shared Utility) * \{ */ -ATTR_NO_OPT static void do_topology_rake_bmesh_task_cb_ex(void *__restrict userdata, - const int n, - const TaskParallelTLS *__restrict tls) +static void do_topology_rake_bmesh_task_cb_ex(void *__restrict userdata, + const int n, + const TaskParallelTLS *__restrict tls) { SculptThreadedTaskData *data = static_cast(userdata); SculptSession *ss = data->ob->sculpt; @@ -2864,6 +2864,8 @@ ATTR_NO_OPT static void do_topology_rake_bmesh_task_cb_ex(void *__restrict userd } PBVHVertexIter vd; + bool modified = false; + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { float direction2[3]; @@ -2883,6 +2885,8 @@ ATTR_NO_OPT static void do_topology_rake_bmesh_task_cb_ex(void *__restrict userd } SCULPT_automasking_node_update(ss, &automask_data, &vd); + modified = true; + const float fade = bstrength * SCULPT_brush_strength_factor(ss, brush, @@ -2921,6 +2925,10 @@ ATTR_NO_OPT static void do_topology_rake_bmesh_task_cb_ex(void *__restrict userd } } BKE_pbvh_vertex_iter_end; + + if (modified) { + BKE_pbvh_node_mark_update(data->nodes[n]); + } } void SCULPT_bmesh_topology_rake(Sculpt *sd, Object *ob, Span nodes, float bstrength) diff --git a/source/blender/editors/sculpt_paint/sculpt_smooth.cc b/source/blender/editors/sculpt_paint/sculpt_smooth.cc index fa9fa2d0036..0e0fdabff15 100644 --- a/source/blender/editors/sculpt_paint/sculpt_smooth.cc +++ b/source/blender/editors/sculpt_paint/sculpt_smooth.cc @@ -129,6 +129,9 @@ void SCULPT_neighbor_coords_average_interior(SculptSession *ss, ss, result, vertex, projection, use_area_weights, bound_type, corner_type); } +/* Compares four vectors seperated by 90 degrees around normal and picks the one closest + * to perpindicular to dir. Used for building a cross field. + */ int closest_vec_to_perp(float dir[3], float r_dir2[3], float no[3], float *buckets, float w) { int bits = 0; @@ -197,7 +200,7 @@ void SCULPT_bmesh_four_neighbor_average(SculptSession *ss, // zero_v3(direction); - float *col = BM_ELEM_CD_PTR(v, cd_temp); + float *field = BM_ELEM_CD_PTR(v, cd_temp); float dir[3]; float dir3[3] = {0.0f, 0.0f, 0.0f}; @@ -210,7 +213,6 @@ void SCULPT_bmesh_four_neighbor_average(SculptSession *ss, float *origno = BM_ELEM_CD_PTR(v, ss->attrs.orig_no->bmesh_cd_offset); if (do_origco) { - // SCULPT_vertex_check_origdata(ss, (PBVHVertRef){.i = (intptr_t)v}); madd_v3_v3fl(direction, origno, -dot_v3v3(origno, direction)); normalize_v3(direction); } @@ -237,7 +239,8 @@ void SCULPT_bmesh_four_neighbor_average(SculptSession *ss, } } - copy_v3_v3(dir, col); + /* Build final direction from input direction and the cross field. */ + copy_v3_v3(dir, field); if (dot_v3v3(dir, dir) == 0.0f) { copy_v3_v3(dir, direction); @@ -263,7 +266,7 @@ void SCULPT_bmesh_four_neighbor_average(SculptSession *ss, PBVHVertRef vertex_other = {reinterpret_cast(v_other)}; float dir2[3]; - float *col2 = BM_ELEM_CD_PTR(v_other, cd_temp); + float *field2 = BM_ELEM_CD_PTR(v_other, cd_temp); float bucketw = 1.0f; @@ -284,6 +287,7 @@ void SCULPT_bmesh_four_neighbor_average(SculptSession *ss, int bound = SCULPT_edge_is_boundary(ss, BKE_pbvh_make_eref(intptr_t(e)), bflag); float dirw = 1.0f; + /* Add to cross field. */ if (bound) { had_bound = true; @@ -293,15 +297,15 @@ void SCULPT_bmesh_four_neighbor_average(SculptSession *ss, dirw = 100000.0f; } else { - dirw = col2[3]; + dirw = field2[3]; - copy_v3_v3(dir2, col2); + copy_v3_v3(dir2, field2); if (dot_v3v3(dir2, dir2) == 0.0f) { copy_v3_v3(dir2, dir); } } - closest_vec_to_perp(dir, dir2, no1, buckets, bucketw); // col2[3]); + closest_vec_to_perp(dir, dir2, no1, buckets, bucketw); // field2[3]); madd_v3_v3fl(dir3, dir2, dirw); totdir3 += dirw; @@ -360,16 +364,16 @@ void SCULPT_bmesh_four_neighbor_average(SculptSession *ss, // mul_v3_fl(dir3, 1.0 / totdir3); normalize_v3(dir3); if (had_bound) { - copy_v3_v3(col, dir3); - col[3] = 1000.0f; + copy_v3_v3(field, dir3); + field[3] = 1000.0f; } else { - mul_v3_fl(col, col[3]); - madd_v3_v3fl(col, dir3, outdir); + mul_v3_fl(field, field[3]); + madd_v3_v3fl(field, dir3, outdir); - col[3] = (col[3] + outdir) * 0.4; - normalize_v3(col); + field[3] = (field[3] + outdir) * 0.4; + normalize_v3(field); } float maxb = 0.0f; @@ -381,7 +385,7 @@ void SCULPT_bmesh_four_neighbor_average(SculptSession *ss, } } - vec_transform(col, no1, bi); + vec_transform(field, no1, bi); } } @@ -569,12 +573,15 @@ static void do_smooth_brush_task_cb_ex(void *__restrict userdata, float projection = brush->autosmooth_projection; bool weighted = brush->flag2 & BRUSH_SMOOTH_USE_AREA_WEIGHT; + bool modified = false; BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { if (!sculpt_brush_test_sq_fn(&test, vd.co)) { continue; } + modified = true; + SCULPT_automasking_node_update(ss, &automask_data, &vd); float fade = bstrength * @@ -627,6 +634,10 @@ static void do_smooth_brush_task_cb_ex(void *__restrict userdata, } } BKE_pbvh_vertex_iter_end; + + if (modified) { + BKE_pbvh_node_mark_update(data->nodes[n]); + } } void SCULPT_smooth( diff --git a/source/blender/makesrna/intern/rna_brush.c b/source/blender/makesrna/intern/rna_brush.c index b94cba2f8a0..375f0e58601 100644 --- a/source/blender/makesrna/intern/rna_brush.c +++ b/source/blender/makesrna/intern/rna_brush.c @@ -441,21 +441,22 @@ static EnumPropertyItem rna_enum_brush_dyntopo_flag[] = { {DYNTOPO_DISABLED, "DISABLED", ICON_NONE, "Disable", ""}, {DYNTOPO_LOCAL_COLLAPSE, "LOCAL_COLLAPSE", ICON_NONE, "Local Collapse", ""}, {DYNTOPO_LOCAL_SUBDIVIDE, "LOCAL_SUBDIVIDE", ICON_NONE, "Local Subdivide", ""}, + {DYNTOPO_CLEANUP, "CLEANUP", ICON_NONE, "Cleanup", "Cleanup 3 and 4 pole vertices (development option)"}, {0, NULL, 0, NULL, NULL}, }; static EnumPropertyItem rna_enum_brush_dyntopo_inherit[] = { {DYNTOPO_SUBDIVIDE, "SUBDIVIDE", ICON_NONE, "Subdivide", ""}, {DYNTOPO_COLLAPSE, "COLLAPSE", ICON_NONE, "Collapse", ""}, - {DYNTOPO_DISABLED, "DISABLED", ICON_NONE, "Disable", ""}, + {DYNTOPO_CLEANUP, "CLEANUP", ICON_NONE, "Cleanup", ""}, {DYNTOPO_LOCAL_COLLAPSE, "LOCAL_COLLAPSE", ICON_NONE, "Local Collapse", ""}, {DYNTOPO_LOCAL_SUBDIVIDE, "LOCAL_SUBDIVIDE", ICON_NONE, "Local Subdivide", ""}, + {DYNTOPO_DISABLED, "DISABLED", ICON_NONE, "Disable", ""}, {DYNTOPO_INHERIT_DETAIL_RANGE, "DETAIL_RANGE", ICON_NONE, "All", ""}, {DYNTOPO_INHERIT_DETAIL_PERCENT, "DETAIL_PERCENT", ICON_NONE, "Percent", ""}, {DYNTOPO_INHERIT_MODE, "MODE", ICON_NONE, "Mode", ""}, {DYNTOPO_INHERIT_CONSTANT_DETAIL, "CONSTANT_DETAIL", ICON_NONE, "Constant Detail", ""}, {DYNTOPO_INHERIT_SPACING, "SPACING", ICON_NONE, "Spacing", ""}, - {DYNTOPO_CLEANUP, "CLEANUP", ICON_NONE, "Cleanup", ""}, {DYNTOPO_INHERIT_DETAIL_SIZE, "DETAIL_SIZE", ICON_NONE, "Detail Size", ""}, {DYNTOPO_INHERIT_RADIUS_SCALE, "RADIUS_SCALE", ICON_NONE, "Radius Scale", ""}, {DYNTOPO_INHERIT_REPEAT, -- 2.30.2 From 5e2ee83131672374635d3cab5ac904f99a75773c Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Mon, 15 May 2023 15:59:22 -0700 Subject: [PATCH 090/279] temp-sculpt-dyntopo: Fix cached flags and vert valences not being set --- .../editors/sculpt_paint/sculpt_dyntopo.cc | 58 ++++++++++++++++++- .../editors/sculpt_paint/sculpt_intern.hh | 10 ++-- .../editors/sculpt_paint/sculpt_undo.cc | 2 + 3 files changed, 63 insertions(+), 7 deletions(-) diff --git a/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc b/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc index d0d55dc7344..bf5e06f08ef 100644 --- a/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc +++ b/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc @@ -566,16 +566,70 @@ void SCULPT_dynamic_topology_enable_ex(Main *bmain, blender::bke::paint::load_all_original(ob); } + SCULPT_update_all_valence_boundary(ob); + if (ss->pbvh && SCULPT_has_persistent_base(ss)) { SCULPT_ensure_persistent_layers(ss, ob); } if (!CustomData_has_layer(&ss->bm->vdata, CD_PAINT_MASK)) { BM_data_layer_add(ss->bm, &ss->bm->vdata, CD_PAINT_MASK); + BKE_sculptsession_update_attr_refs(ob); + } +} + +void SCULPT_update_all_valence_boundary(Object *ob) +{ + SculptSession *ss = ob->sculpt; + + /* Do bmesh seperately to avoid needing the PBVH, which we might not + * have inside the undo code. + */ + + if (ss->bm) { + SCULPT_vertex_random_access_ensure(ss); + BMIter iter; + BMVert *v; + + int cd_flag = CustomData_get_offset_named( + &ss->bm->vdata, CD_PROP_INT8, SCULPT_ATTRIBUTE_NAME(flags)); + int cd_boundary = CustomData_get_offset_named( + &ss->bm->vdata, CD_PROP_INT32, SCULPT_ATTRIBUTE_NAME(boundary_flags)); + int cd_valence = CustomData_get_offset_named( + &ss->bm->vdata, CD_PROP_INT32, SCULPT_ATTRIBUTE_NAME(valence)); + + BLI_assert(cd_flag != -1 && cd_boundary != -1 && cd_valence != -1); + + BM_ITER_MESH (v, &iter, ss->bm, BM_VERTS_OF_MESH) { + *BM_ELEM_CD_PTR(v, cd_flag) = SCULPTFLAG_NEED_DISK_SORT | + SCULPTFLAG_NEED_TRIANGULATE; + BM_ELEM_CD_SET_INT(v, cd_valence, BM_vert_edge_count(v)); + BM_ELEM_CD_SET_INT(v, cd_boundary, SCULPT_BOUNDARY_NEEDS_UPDATE); + + /* Update boundary if we have a pbvh. */ + if (ss->pbvh) { + PBVHVertRef vertex = {reinterpret_cast(v)}; + SCULPT_vertex_is_boundary(ss, vertex, SCULPT_BOUNDARY_ALL); + } + } + + return; + } + if (!ss->pbvh) { + return; } - if (ss->pbvh) { - blender::bke::paint::load_all_original(ob); + int verts_count = SCULPT_vertex_count_get(ss); + for (int i = 0; i < verts_count; i++) { + PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); + + blender::bke::paint::vertex_attr_set( + vertex, + ss->attrs.flags, + SCULPTFLAG_NEED_VALENCE | SCULPTFLAG_NEED_TRIANGULATE | SCULPTFLAG_NEED_DISK_SORT); + BKE_sculpt_boundary_flag_update(ss, vertex); + SCULPT_vertex_valence_get(ss, vertex); + SCULPT_vertex_is_boundary(ss, vertex, SCULPT_BOUNDARY_ALL); } } diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.hh b/source/blender/editors/sculpt_paint/sculpt_intern.hh index a74ef763817..ec90827a23c 100644 --- a/source/blender/editors/sculpt_paint/sculpt_intern.hh +++ b/source/blender/editors/sculpt_paint/sculpt_intern.hh @@ -1315,6 +1315,9 @@ void SCULPT_fake_neighbors_free(Object *ob); /* Vertex Info. */ void SCULPT_boundary_info_ensure(Object *object); +/* Update all boundary and valence info in the mesh. */ +void SCULPT_update_all_valence_boundary(Object *ob); + /* Boundary Info needs to be initialized in order to use this function. */ eSculptCorner SCULPT_vertex_is_corner(const SculptSession *ss, const PBVHVertRef index, @@ -1879,11 +1882,8 @@ void SCULPT_bmesh_four_neighbor_average(SculptSession *ss, int cd_temp, bool do_origco); -void SCULPT_neighbor_coords_average(SculptSession *ss, - float result[3], - PBVHVertRef index, - float projection, - bool weighted); +void SCULPT_neighbor_coords_average( + SculptSession *ss, float result[3], PBVHVertRef index, float projection, bool weighted); float SCULPT_neighbor_mask_average(SculptSession *ss, PBVHVertRef index); void SCULPT_neighbor_color_average(SculptSession *ss, float result[4], PBVHVertRef index); diff --git a/source/blender/editors/sculpt_paint/sculpt_undo.cc b/source/blender/editors/sculpt_paint/sculpt_undo.cc index f0724baddcb..bc1c067a788 100644 --- a/source/blender/editors/sculpt_paint/sculpt_undo.cc +++ b/source/blender/editors/sculpt_paint/sculpt_undo.cc @@ -1079,6 +1079,8 @@ static void sculpt_undo_bmesh_enable(Object *ob, SculptUndoNode *unode, bool is_ BKE_sculptsession_update_attr_refs(ob); } + SCULPT_update_all_valence_boundary(ob); + me->flag |= ME_SCULPT_DYNAMIC_TOPOLOGY; } -- 2.30.2 From 3e29e32c0af639b6001789f648bcb318409df995 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Mon, 15 May 2023 17:37:43 -0700 Subject: [PATCH 091/279] temp-sculpt-dyntopo: Fix sculpt boundary corner handling --- source/blender/blenkernel/intern/dyntopo.cc | 34 ++++++------ source/blender/blenkernel/intern/paint.cc | 23 ++++---- source/blender/blenkernel/intern/pbvh.cc | 49 ----------------- .../editors/sculpt_paint/sculpt_api.cc | 54 ++++++++++++------- .../editors/sculpt_paint/sculpt_curvature.cc | 16 +++--- .../editors/sculpt_paint/sculpt_smooth.cc | 40 +++++++++++--- 6 files changed, 104 insertions(+), 112 deletions(-) diff --git a/source/blender/blenkernel/intern/dyntopo.cc b/source/blender/blenkernel/intern/dyntopo.cc index 09837000f83..b00ea6bae2c 100644 --- a/source/blender/blenkernel/intern/dyntopo.cc +++ b/source/blender/blenkernel/intern/dyntopo.cc @@ -607,9 +607,9 @@ BLI_INLINE int dyntopo_thread_rand(int seed) return (seed * multiplier + addend) & mask; } -ATTR_NO_OPT static void unified_edge_queue_task_cb(void *__restrict userdata, - const int n, - const TaskParallelTLS *__restrict /*tls*/) +static void unified_edge_queue_task_cb(void *__restrict userdata, + const int n, + const TaskParallelTLS *__restrict /*tls*/) { EdgeQueueThreadData *tdata = ((EdgeQueueThreadData *)userdata) + n; PBVH *pbvh = tdata->pbvh; @@ -1614,13 +1614,13 @@ static void edge_queue_create_local(EdgeQueueContext *eq_ctx, } } -ATTR_NO_OPT static bool cleanup_valence_3_4(EdgeQueueContext *ectx, - PBVH *pbvh, - const float center[3], - const float /*view_normal*/[3], - float radius, - const bool /*use_frontface*/, - const bool /*use_projected*/) +static bool cleanup_valence_3_4(EdgeQueueContext *ectx, + PBVH *pbvh, + const float center[3], + const float /*view_normal*/[3], + float radius, + const bool /*use_frontface*/, + const bool /*use_projected*/) { bool modified = false; @@ -1925,13 +1925,13 @@ ATTR_NO_OPT static bool cleanup_valence_3_4(EdgeQueueContext *ectx, return modified; } -ATTR_NO_OPT static bool do_cleanup_3_4(EdgeQueueContext *eq_ctx, - PBVH *pbvh, - const float center[3], - const float view_normal[3], - float radius, - bool use_frontface, - bool use_projected) +static bool do_cleanup_3_4(EdgeQueueContext *eq_ctx, + PBVH *pbvh, + const float center[3], + const float view_normal[3], + float radius, + bool use_frontface, + bool use_projected) { bool modified = false; diff --git a/source/blender/blenkernel/intern/paint.cc b/source/blender/blenkernel/intern/paint.cc index 4eb249d7047..5e77d480bd1 100644 --- a/source/blender/blenkernel/intern/paint.cc +++ b/source/blender/blenkernel/intern/paint.cc @@ -1937,7 +1937,7 @@ static void sculpt_update_object( if (ob->type == OB_MESH && !ss->pmap) { if (!ss->pmap && ss->pbvh) { - //ss->pmap = BKE_pbvh_get_pmap(ss->pbvh, &ss->pmap_mem); + // ss->pmap = BKE_pbvh_get_pmap(ss->pbvh, &ss->pmap_mem); } if (!ss->pmap) { @@ -2288,7 +2288,7 @@ void BKE_sculpt_update_object_for_edit( sculpt_update_object(depsgraph, ob_orig, ob_eval, need_pmap, is_paint_tool); } -ATTR_NO_OPT int *BKE_sculpt_face_sets_ensure(Object *ob) +int *BKE_sculpt_face_sets_ensure(Object *ob) { SculptSession *ss = ob->sculpt; @@ -2670,6 +2670,7 @@ extern "C" bool BKE_sculpt_init_flags_valence(Object *ob, BKE_sculpt_ensure_origco(ob); sculpt_boundary_flags_ensure(ob, pbvh, totvert); + BKE_sculptsession_update_attr_refs(ob); if (reset_flags) { if (ss->bm) { @@ -3146,14 +3147,14 @@ static int sculpt_attr_elem_count_get(Object *ob, eAttrDomain domain) } } -ATTR_NO_OPT static bool sculpt_attribute_create(SculptSession *ss, - Object *ob, - eAttrDomain domain, - eCustomDataType proptype, - const char *name, - SculptAttribute *out, - const SculptAttributeParams *params, - PBVHType pbvhtype) +static bool sculpt_attribute_create(SculptSession *ss, + Object *ob, + eAttrDomain domain, + eCustomDataType proptype, + const char *name, + SculptAttribute *out, + const SculptAttributeParams *params, + PBVHType pbvhtype) { Mesh *me = BKE_object_get_original_mesh(ob); @@ -3278,7 +3279,7 @@ ATTR_NO_OPT static bool sculpt_attribute_create(SculptSession *ss, return true; } -ATTR_NO_OPT static bool sculpt_attr_update(Object *ob, SculptAttribute *attr) +static bool sculpt_attr_update(Object *ob, SculptAttribute *attr) { SculptSession *ss = ob->sculpt; int elem_num = sculpt_attr_elem_count_get(ob, attr->domain); diff --git a/source/blender/blenkernel/intern/pbvh.cc b/source/blender/blenkernel/intern/pbvh.cc index 77aab874da1..2319d34bb3c 100644 --- a/source/blender/blenkernel/intern/pbvh.cc +++ b/source/blender/blenkernel/intern/pbvh.cc @@ -4247,55 +4247,6 @@ void BKE_pbvh_set_symmetry(PBVH *pbvh, int symmetry, int boundary_symmetry) pbvh_boundaries_flag_update(pbvh); } -ATTR_NO_OPT void BKE_pbvh_update_vert_boundary_grids(PBVH *pbvh, PBVHVertRef vertex) -{ - SubdivCCG *subdiv_ccg = pbvh->subdiv_ccg; - int index = (int)vertex.i; - - /* Update valence. */ - - /* TODO: optimize this. We could fill #SculptVertexNeighborIter directly, - * maybe provide coordinate and mask pointers directly rather than converting - * back and forth between #CCGElem and global index. */ - const CCGKey *key = BKE_pbvh_get_grid_key(pbvh); - const int grid_index = index / key->grid_area; - const int vertex_index = index - grid_index * key->grid_area; - - SubdivCCGCoord coord = {}; - - coord.grid_index = grid_index; - coord.x = short(vertex_index % key->grid_size); - coord.y = short(vertex_index / key->grid_size); - - SubdivCCGNeighbors neighbors; - BKE_subdiv_ccg_neighbor_coords_get(subdiv_ccg, &coord, false, &neighbors); - - pbvh->valence[vertex.i] = neighbors.size; - pbvh->sculpt_flags[vertex.i] &= ~SCULPTFLAG_NEED_VALENCE; - - /* Update boundary */ - int *boundary_flag = pbvh->boundary_flags + vertex.i; - *boundary_flag = 0; - - int face = BKE_subdiv_ccg_grid_to_face_index(subdiv_ccg, grid_index); - int fset = pbvh->face_sets ? pbvh->face_sets[face] : 0; - - for (int i : IndexRange(neighbors.size)) { - int grid_index2 = neighbors.coords[i].grid_index; - - if (grid_index == grid_index2) { - continue; - } - - int face2 = BKE_subdiv_ccg_grid_to_face_index(subdiv_ccg, grid_index2); - int fset2 = pbvh->face_sets ? pbvh->face_sets[face2] : 0; - - if (fset2 != fset) { - *boundary_flag |= SCULPT_BOUNDARY_FACE_SET; - } - } -} - namespace blender::bke::pbvh { void set_flags_valence(PBVH *pbvh, uint8_t *flags, int *valence) { diff --git a/source/blender/editors/sculpt_paint/sculpt_api.cc b/source/blender/editors/sculpt_paint/sculpt_api.cc index a91d9dd0e10..0686c50f82a 100644 --- a/source/blender/editors/sculpt_paint/sculpt_api.cc +++ b/source/blender/editors/sculpt_paint/sculpt_api.cc @@ -169,24 +169,33 @@ static bool sculpt_check_boundary_vertex_in_base_mesh(const SculptSession *ss, i return BLI_BITMAP_TEST(ss->vertex_info.boundary, index); } -static bool sculpt_check_unique_face_set_in_base_mesh(const SculptSession *ss, int index) +static bool sculpt_check_unique_face_set_in_base_mesh(const SculptSession *ss, + int vertex, + bool *r_corner) { - if (!ss->face_sets) { + if (!ss->attrs.face_set) { return true; } - const MeshElemMap *vert_map = &ss->pmap[index]; - int face_set = -1; - for (int i = 0; i < vert_map->count; i++) { - if (face_set == -1) { - face_set = ss->face_sets[vert_map->indices[i]]; + const MeshElemMap *vert_map = &ss->pmap[vertex]; + int fset1 = -1, fset2 = -1, fset3 = -1; + + for (int i : IndexRange(vert_map->count)) { + PBVHFaceRef face = {vert_map->indices[i]}; + int fset = face_attr_get(face, ss->attrs.face_set); + + if (fset1 == -1) { + fset1 = fset; } - else { - if (ss->face_sets[vert_map->indices[i]] != face_set) { - return false; - } + else if (fset2 == -1 && fset != fset1) { + fset2 = fset; + } + else if (fset3 == -1 && fset != fset1 && fset != fset2) { + fset3 = fset; } } - return true; + + *r_corner = fset3 != -1; + return fset2 == -1; } eSculptBoundary SCULPT_edge_is_boundary(const SculptSession *ss, @@ -349,17 +358,18 @@ static void grids_update_boundary_flags(const SculptSession *ss, PBVHVertRef ver const SubdivCCGAdjacencyType adjacency = BKE_subdiv_ccg_coarse_mesh_adjacency_info_get( ss->subdiv_ccg, &coord, ss->corner_verts, ss->polys, &v1, &v2); + bool fset_corner = false; switch (adjacency) { case SUBDIV_CCG_ADJACENT_VERTEX: - if (sculpt_check_unique_face_set_in_base_mesh(ss, v1)) { + if (!sculpt_check_unique_face_set_in_base_mesh(ss, v1, &fset_corner)) { *flag |= SCULPT_BOUNDARY_FACE_SET; } - if (sculpt_check_boundary_vertex_in_base_mesh(ss, v2)) { + if (sculpt_check_boundary_vertex_in_base_mesh(ss, v1)) { *flag |= SCULPT_BOUNDARY_MESH; } break; - case SUBDIV_CCG_ADJACENT_EDGE: - if (sculpt_check_unique_face_set_for_edge_in_base_mesh(ss, v1, v2)) { + case SUBDIV_CCG_ADJACENT_EDGE: { + if (!sculpt_check_unique_face_set_for_edge_in_base_mesh(ss, v1, v2)) { *flag |= SCULPT_BOUNDARY_FACE_SET; } @@ -369,9 +379,14 @@ static void grids_update_boundary_flags(const SculptSession *ss, PBVHVertRef ver *flag |= SCULPT_BOUNDARY_MESH; } break; + } case SUBDIV_CCG_ADJACENT_NONE: break; } + + if (fset_corner) { + *flag |= SCULPT_CORNER_FACE_SET | SCULPT_BOUNDARY_FACE_SET; + } } static void faces_update_boundary_flags(const SculptSession *ss, const PBVHVertRef vertex) @@ -422,7 +437,7 @@ eSculptCorner SCULPT_vertex_is_corner(const SculptSession *ss, const PBVHVertRef vertex, eSculptCorner cornertype) { - eSculptCorner flag = *vertex_attr_ptr(vertex, ss->attrs.boundary_flags); + eSculptCorner flag = eSculptCorner(vertex_attr_get(vertex, ss->attrs.boundary_flags)); bool needs_update = flag & SCULPT_BOUNDARY_NEEDS_UPDATE; switch (BKE_pbvh_type(ss->pbvh)) { @@ -453,12 +468,13 @@ eSculptCorner SCULPT_vertex_is_corner(const SculptSession *ss, if (needs_update) { grids_update_boundary_flags(ss, vertex); } + break; } } if (needs_update) { - flag = *vertex_attr_ptr(vertex, ss->attrs.boundary_flags); + flag = eSculptCorner(vertex_attr_get(vertex, ss->attrs.boundary_flags)); } flag &= cornertype; @@ -502,7 +518,7 @@ eSculptBoundary SCULPT_vertex_is_boundary(const SculptSession *ss, case PBVH_GRIDS: { if (needs_update) { - BKE_pbvh_update_vert_boundary_grids(ss->pbvh, vertex); + grids_update_boundary_flags(ss, vertex); needs_update = false; } diff --git a/source/blender/editors/sculpt_paint/sculpt_curvature.cc b/source/blender/editors/sculpt_paint/sculpt_curvature.cc index 7f25064924b..84d36c993c4 100644 --- a/source/blender/editors/sculpt_paint/sculpt_curvature.cc +++ b/source/blender/editors/sculpt_paint/sculpt_curvature.cc @@ -141,10 +141,10 @@ BLI_INLINE void normal_covariance(float mat[3][3], float no[3]) mat[2][2] = no[2] * no[2]; } -ATTR_NO_OPT bool SCULPT_calc_principle_curvatures(SculptSession *ss, - PBVHVertRef vertex, - SculptCurvatureData *out, - bool useAccurateSolver) +bool SCULPT_calc_principle_curvatures(SculptSession *ss, + PBVHVertRef vertex, + SculptCurvatureData *out, + bool useAccurateSolver) { SculptVertexNeighborIter ni; float nmat[3][3], nmat2[3][3]; @@ -246,10 +246,10 @@ ATTR_NO_OPT bool SCULPT_calc_principle_curvatures(SculptSession *ss, return true; } -ATTR_NO_OPT void SCULPT_curvature_dir_get(SculptSession *ss, - PBVHVertRef v, - float dir[3], - bool useAccurateSolver) +void SCULPT_curvature_dir_get(SculptSession *ss, + PBVHVertRef v, + float dir[3], + bool useAccurateSolver) { if (BKE_pbvh_type(ss->pbvh) != PBVH_BMESH) { SculptCurvatureData curv; diff --git a/source/blender/editors/sculpt_paint/sculpt_smooth.cc b/source/blender/editors/sculpt_paint/sculpt_smooth.cc index 0e0fdabff15..f763fd55ff8 100644 --- a/source/blender/editors/sculpt_paint/sculpt_smooth.cc +++ b/source/blender/editors/sculpt_paint/sculpt_smooth.cc @@ -14,9 +14,12 @@ #include "BLI_vector.hh" #include "DNA_brush_types.h" +#include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" #include "BKE_context.h" +#include "BKE_mesh.hh" +#include "BKE_mesh_mapping.h" #include "BKE_paint.h" #include "BKE_pbvh.h" @@ -31,8 +34,6 @@ using blender::float2; using blender::float3; using blender::Vector; -using namespace blender::bke::paint; - static void SCULPT_neighbor_coords_average_interior_ex(SculptSession *ss, float result[3], PBVHVertRef vertex, @@ -65,7 +66,14 @@ static void SCULPT_neighbor_coords_average_interior_ex(SculptSession *ss, neighbor_count++; float w = weighted ? areas[ni.i] : 1.0f; - const eSculptBoundary is_boundary2 = SCULPT_vertex_is_boundary(ss, ni.vertex, bound_type); + eSculptBoundary is_boundary2; + + if (ni.has_edge) { + is_boundary2 = SCULPT_edge_is_boundary(ss, ni.edge, bound_type); + } + else { + is_boundary2 = SCULPT_vertex_is_boundary(ss, ni.vertex, bound_type); + } const eSculptBoundary smooth_types = !ss->hard_edge_mode ? SCULPT_BOUNDARY_FACE_SET | SCULPT_BOUNDARY_SEAM | SCULPT_BOUNDARY_UV : @@ -487,7 +495,8 @@ static void do_enhance_details_brush_task_cb_ex(void *__restrict userdata, &automask_data); float disp[3]; - float *detail_dir = vertex_attr_ptr(vd.vertex, ss->attrs.detail_directions); + float *detail_dir = blender::bke::paint::vertex_attr_ptr(vd.vertex, + ss->attrs.detail_directions); madd_v3_v3v3fl(disp, vd.co, detail_dir, fade); SCULPT_clip(sd, ss, vd.co, disp); @@ -525,7 +534,8 @@ static void SCULPT_enhance_details_brush(Sculpt *sd, Object *ob, Span(vertex, ss->attrs.detail_directions); + float *detail_dir = blender::bke::paint::vertex_attr_ptr(vertex, + ss->attrs.detail_directions); sub_v3_v3v3(detail_dir, avg, SCULPT_vertex_co_get(ss, vertex)); } @@ -652,6 +662,18 @@ void SCULPT_smooth( int iteration, count; float last; + /* PBVH_FACES needs ss->epmap. */ + if (BKE_pbvh_type(ss->pbvh) == PBVH_FACES && !ss->epmap) { + Mesh *mesh = static_cast(ob->data); + + BKE_mesh_edge_poly_map_create(&ss->epmap, + &ss->epmap_mem, + mesh->edges().size(), + mesh->polys(), + mesh->corner_edges().data(), + mesh->corner_edges().size()); + } + CLAMP(bstrength, 0.0f, 1.0f); count = int(bstrength * max_iterations); @@ -734,7 +756,7 @@ void SCULPT_surface_smooth_laplacian_step(SculptSession *ss, mul_v3_v3fl(weigthed_o, origco, alpha); mul_v3_v3fl(weigthed_q, co, 1.0f - alpha); add_v3_v3v3(d, weigthed_o, weigthed_q); - float *laplacian_disp = vertex_attr_ptr(vertex, ss->attrs.laplacian_disp); + float *laplacian_disp = blender::bke::paint::vertex_attr_ptr(vertex, ss->attrs.laplacian_disp); sub_v3_v3v3(laplacian_disp, laplacian_smooth_co, d); @@ -749,14 +771,16 @@ void SCULPT_surface_smooth_displace_step( int total = 0; SculptVertexNeighborIter ni; SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vertex, ni) { - float *laplacian_disp = vertex_attr_ptr(ni.vertex, ss->attrs.laplacian_disp); + float *laplacian_disp = blender::bke::paint::vertex_attr_ptr(ni.vertex, + ss->attrs.laplacian_disp); add_v3_v3(b_avg, laplacian_disp); total++; } SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); if (total > 0) { - float *laplacian_disp = vertex_attr_ptr(vertex, ss->attrs.laplacian_disp); + float *laplacian_disp = blender::bke::paint::vertex_attr_ptr(vertex, + ss->attrs.laplacian_disp); mul_v3_v3fl(b_current_vertex, b_avg, (1.0f - beta) / total); madd_v3_v3fl(b_current_vertex, laplacian_disp, beta); -- 2.30.2 From f8a77a6265c566315ed404e27f5fbe657c2fdab7 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Tue, 16 May 2023 01:35:48 -0700 Subject: [PATCH 092/279] temp-sculpt-dyntopo: Add an option to control hard edge corner pinning --- .../startup/bl_ui/properties_paint_common.py | 592 +++++++++++------- .../startup/bl_ui/space_toolsystem_toolbar.py | 506 ++++++++------- source/blender/blenkernel/BKE_brush.h | 1 + source/blender/blenkernel/intern/brush.cc | 14 +- .../blenloader/intern/versioning_300.cc | 11 +- .../editors/sculpt_paint/sculpt_boundary.cc | 8 +- .../sculpt_paint/sculpt_brush_types.cc | 7 +- .../sculpt_paint/sculpt_filter_color.cc | 5 +- .../sculpt_paint/sculpt_filter_mesh.cc | 79 ++- .../editors/sculpt_paint/sculpt_intern.hh | 12 +- .../editors/sculpt_paint/sculpt_ops.cc | 2 +- .../editors/sculpt_paint/sculpt_replay.c | 2 +- .../editors/sculpt_paint/sculpt_smooth.cc | 78 ++- source/blender/makesdna/DNA_brush_defaults.h | 1 + source/blender/makesdna/DNA_brush_types.h | 2 +- source/blender/makesdna/DNA_scene_defaults.h | 2 +- source/blender/makesdna/DNA_scene_types.h | 8 +- source/blender/makesrna/intern/rna_brush.c | 16 +- source/blender/makesrna/intern/rna_scene.c | 23 +- 19 files changed, 828 insertions(+), 541 deletions(-) diff --git a/scripts/startup/bl_ui/properties_paint_common.py b/scripts/startup/bl_ui/properties_paint_common.py index 0485185b31d..f57e86276a3 100644 --- a/scripts/startup/bl_ui/properties_paint_common.py +++ b/scripts/startup/bl_ui/properties_paint_common.py @@ -9,15 +9,16 @@ class UnifiedPaintPanel: @staticmethod def get_brush_mode(context): - """ Get the correct mode for this context. For any context where this returns None, - no brush options should be displayed.""" + """Get the correct mode for this context. For any context where this returns None, + no brush options should be displayed.""" mode = context.mode - if mode == 'PARTICLE': + if mode == "PARTICLE": # Particle brush settings currently completely do their own thing. return None from bl_ui.space_toolsystem_common import ToolSelectPanelHelper + tool = ToolSelectPanelHelper.tool_active_from_context(context) if not tool: @@ -33,12 +34,12 @@ class UnifiedPaintPanel: if space_data: space_type = space_data.type - if space_type == 'IMAGE_EDITOR': + if space_type == "IMAGE_EDITOR": if space_data.show_uvedit: - return 'UV_SCULPT' - return 'PAINT_2D' - elif space_type in {'VIEW_3D', 'PROPERTIES'}: - if mode == 'PAINT_TEXTURE': + return "UV_SCULPT" + return "PAINT_2D" + elif space_type in {"VIEW_3D", "PROPERTIES"}: + if mode == "PAINT_TEXTURE": if tool_settings.image_paint: return mode else: @@ -53,50 +54,50 @@ class UnifiedPaintPanel: mode = UnifiedPaintPanel.get_brush_mode(context) # 3D paint settings - if mode == 'SCULPT': + if mode == "SCULPT": return tool_settings.sculpt - elif mode == 'PAINT_VERTEX': + elif mode == "PAINT_VERTEX": return tool_settings.vertex_paint - elif mode == 'PAINT_WEIGHT': + elif mode == "PAINT_WEIGHT": return tool_settings.weight_paint - elif mode == 'PAINT_TEXTURE': + elif mode == "PAINT_TEXTURE": return tool_settings.image_paint - elif mode == 'PARTICLE': + elif mode == "PARTICLE": return tool_settings.particle_edit # 2D paint settings - elif mode == 'PAINT_2D': + elif mode == "PAINT_2D": return tool_settings.image_paint - elif mode == 'UV_SCULPT': + elif mode == "UV_SCULPT": return tool_settings.uv_sculpt # Grease Pencil settings - elif mode == 'PAINT_GPENCIL': + elif mode == "PAINT_GPENCIL": return tool_settings.gpencil_paint - elif mode == 'SCULPT_GPENCIL': + elif mode == "SCULPT_GPENCIL": return tool_settings.gpencil_sculpt_paint - elif mode == 'WEIGHT_GPENCIL': + elif mode == "WEIGHT_GPENCIL": return tool_settings.gpencil_weight_paint - elif mode == 'VERTEX_GPENCIL': + elif mode == "VERTEX_GPENCIL": return tool_settings.gpencil_vertex_paint - elif mode == 'SCULPT_CURVES': + elif mode == "SCULPT_CURVES": return tool_settings.curves_sculpt return None @staticmethod def prop_unified( - layout, - context, - brush, - prop_name, - unified_name=None, - pressure_name=None, - icon='NONE', - text=None, - slider=False, - header=False, - dyntopo=False, + layout, + context, + brush, + prop_name, + unified_name=None, + pressure_name=None, + icon="NONE", + text=None, + slider=False, + header=False, + dyntopo=False, ): - """ Generalized way of adding brush options to the UI, - along with their pen pressure setting and global toggle, if they exist. """ + """Generalized way of adding brush options to the UI, + along with their pen pressure setting and global toggle, if they exist.""" row = layout.row(align=True) ups = context.tool_settings.unified_paint_settings prop_owner = brush @@ -110,7 +111,7 @@ class UnifiedPaintPanel: if unified_name and not header: # NOTE: We don't draw UnifiedPaintSettings in the header to reduce clutter. D5928#136281 - row.prop(ups, unified_name, text="", icon='BRUSHES_ALL') + row.prop(ups, unified_name, text="", icon="BRUSHES_ALL") return row @@ -124,7 +125,9 @@ class UnifiedPaintPanel: return getattr(brush.dyntopo, prop_name) @staticmethod - def prop_unified_dyntopo(layout, context, brush, prop_name, text=None, show_inherit=True, expand=False): + def prop_unified_dyntopo( + layout, context, brush, prop_name, text=None, show_inherit=True, expand=False + ): sculpt = context.tool_settings.sculpt if prop_name.upper() in brush.dyntopo.inherit: @@ -171,14 +174,16 @@ class BrushSelectPanel(BrushPanel): row = layout.row() large_preview = True if large_preview: - row.column().template_ID_preview(settings, "brush", new="brush.add", rows=3, cols=8, hide_buttons=False) + row.column().template_ID_preview( + settings, "brush", new="brush.add", rows=3, cols=8, hide_buttons=False + ) else: row.column().template_ID(settings, "brush", new="brush.add") col = row.column() - col.menu("VIEW3D_MT_brush_context_menu", icon='DOWNARROW_HLT', text="") + col.menu("VIEW3D_MT_brush_context_menu", icon="DOWNARROW_HLT", text="") if brush is not None: - col.prop(brush, "use_custom_icon", toggle=True, icon='FILE_IMAGE', text="") + col.prop(brush, "use_custom_icon", toggle=True, icon="FILE_IMAGE", text="") if brush.use_custom_icon: layout.prop(brush, "icon_filepath", text="") @@ -186,7 +191,7 @@ class BrushSelectPanel(BrushPanel): class ColorPalettePanel(BrushPanel): bl_label = "Color Palette" - bl_options = {'DEFAULT_CLOSED'} + bl_options = {"DEFAULT_CLOSED"} @classmethod def poll(cls, context): @@ -196,7 +201,7 @@ class ColorPalettePanel(BrushPanel): settings = cls.paint_settings(context) brush = settings.brush - if context.space_data.type == 'IMAGE_EDITOR' or context.image_paint_object: + if context.space_data.type == "IMAGE_EDITOR" or context.image_paint_object: capabilities = brush.image_paint_capabilities return capabilities.has_color @@ -220,7 +225,7 @@ class ColorPalettePanel(BrushPanel): class ClonePanel(BrushPanel): bl_label = "Clone" - bl_options = {'DEFAULT_CLOSED'} + bl_options = {"DEFAULT_CLOSED"} @classmethod def poll(cls, context): @@ -230,9 +235,9 @@ class ClonePanel(BrushPanel): settings = cls.paint_settings(context) mode = cls.get_brush_mode(context) - if mode == 'PAINT_TEXTURE': + if mode == "PAINT_TEXTURE": brush = settings.brush - return brush.image_tool == 'CLONE' + return brush.image_tool == "CLONE" return False def draw_header(self, context): @@ -248,13 +253,16 @@ class ClonePanel(BrushPanel): ob = context.active_object col = layout.column() - if settings.mode == 'MATERIAL': + if settings.mode == "MATERIAL": if len(ob.material_slots) > 1: col.label(text="Materials") col.template_list( - "MATERIAL_UL_matslots", "", - ob, "material_slots", - ob, "active_material_index", + "MATERIAL_UL_matslots", + "", + ob, + "material_slots", + ob, + "active_material_index", rows=2, ) @@ -262,25 +270,30 @@ class ClonePanel(BrushPanel): if mat: col.label(text="Source Clone Slot") col.template_list( - "TEXTURE_UL_texpaintslots", "", - mat, "texture_paint_slots", - mat, "paint_clone_slot", + "TEXTURE_UL_texpaintslots", + "", + mat, + "texture_paint_slots", + mat, + "paint_clone_slot", rows=2, ) - elif settings.mode == 'IMAGE': + elif settings.mode == "IMAGE": mesh = ob.data clone_text = mesh.uv_layer_clone.name if mesh.uv_layer_clone else "" col.label(text="Source Clone Image") col.template_ID(settings, "clone_image") col.label(text="Source Clone UV Map") - col.menu("VIEW3D_MT_tools_projectpaint_clone", text=clone_text, translate=False) + col.menu( + "VIEW3D_MT_tools_projectpaint_clone", text=clone_text, translate=False + ) class TextureMaskPanel(BrushPanel): bl_label = "Texture Mask" - bl_options = {'DEFAULT_CLOSED'} + bl_options = {"DEFAULT_CLOSED"} def draw(self, context): layout = self.layout @@ -291,13 +304,15 @@ class TextureMaskPanel(BrushPanel): mask_tex_slot = brush.mask_texture_slot col = layout.column() - col.template_ID_preview(mask_tex_slot, "texture", new="texture.new", rows=3, cols=8) + col.template_ID_preview( + mask_tex_slot, "texture", new="texture.new", rows=3, cols=8 + ) # map_mode layout.row().prop(mask_tex_slot, "mask_map_mode", text="Mask Mapping") - if mask_tex_slot.map_mode == 'STENCIL': - if brush.mask_texture and brush.mask_texture.type == 'IMAGE': + if mask_tex_slot.map_mode == "STENCIL": + if brush.mask_texture and brush.mask_texture.type == "IMAGE": layout.operator("brush.stencil_fit_image_aspect").mask = True layout.operator("brush.stencil_reset_transform").mask = True @@ -310,7 +325,10 @@ class TextureMaskPanel(BrushPanel): if mask_tex_slot.has_texture_angle_source: col.prop(mask_tex_slot, "use_rake", text="Rake") - if brush.brush_capabilities.has_random_texture_angle and mask_tex_slot.has_random_texture_angle: + if ( + brush.brush_capabilities.has_random_texture_angle + and mask_tex_slot.has_random_texture_angle + ): col.prop(mask_tex_slot, "use_random", text="Random") if mask_tex_slot.use_random: col.prop(mask_tex_slot, "random_angle", text="Random Angle") @@ -322,7 +340,7 @@ class TextureMaskPanel(BrushPanel): class StrokePanel(BrushPanel): bl_label = "Stroke" - bl_options = {'DEFAULT_CLOSED'} + bl_options = {"DEFAULT_CLOSED"} bl_ui_units_x = 13 def draw(self, context): @@ -354,11 +372,16 @@ class StrokePanel(BrushPanel): row = col.row(align=True) row.prop(brush, "spacing", text="Spacing") - if mode == 'SCULPT': - col.row().prop(brush, "use_scene_spacing", text="Spacing Distance", expand=True) + if mode == "SCULPT": + col.row().prop( + brush, "use_scene_spacing", text="Spacing Distance", expand=True + ) - if mode in {'PAINT_TEXTURE', 'PAINT_2D', 'SCULPT'}: - if brush.image_paint_capabilities.has_space_attenuation or brush.sculpt_capabilities.has_space_attenuation: + if mode in {"PAINT_TEXTURE", "PAINT_2D", "SCULPT"}: + if ( + brush.image_paint_capabilities.has_space_attenuation + or brush.sculpt_capabilities.has_space_attenuation + ): col.prop(brush, "use_space_attenuation") if brush.use_curve: @@ -373,10 +396,12 @@ class StrokePanel(BrushPanel): col.prop(brush, "dash_ratio", text="Dash Ratio") col.prop(brush, "dash_samples", text="Dash Length") - if (mode == 'SCULPT' and brush.sculpt_capabilities.has_jitter) or mode != 'SCULPT': + if ( + mode == "SCULPT" and brush.sculpt_capabilities.has_jitter + ) or mode != "SCULPT": col.separator() row = col.row(align=True) - if brush.jitter_unit == 'BRUSH': + if brush.jitter_unit == "BRUSH": row.prop(brush, "jitter", slider=True) else: row.prop(brush, "jitter_absolute") @@ -389,7 +414,7 @@ class StrokePanel(BrushPanel): class SmoothStrokePanel(BrushPanel): bl_label = "Stabilize Stroke" - bl_options = {'DEFAULT_CLOSED'} + bl_options = {"DEFAULT_CLOSED"} @classmethod def poll(cls, context): @@ -406,8 +431,9 @@ class SmoothStrokePanel(BrushPanel): brush = settings.brush self.layout.use_property_split = False - self.layout.prop(brush, "use_smooth_stroke", - text=self.bl_label if self.is_popover else "") + self.layout.prop( + brush, "use_smooth_stroke", text=self.bl_label if self.is_popover else "" + ) def draw(self, context): layout = self.layout @@ -425,7 +451,7 @@ class SmoothStrokePanel(BrushPanel): class FalloffPanel(BrushPanel): bl_label = "Falloff" - bl_options = {'DEFAULT_CLOSED'} + bl_options = {"DEFAULT_CLOSED"} @classmethod def poll(cls, context): @@ -434,9 +460,9 @@ class FalloffPanel(BrushPanel): settings = cls.paint_settings(context) if not (settings and settings.brush and settings.brush.curve): return False - if cls.get_brush_mode(context) == 'SCULPT_CURVES': + if cls.get_brush_mode(context) == "SCULPT_CURVES": brush = settings.brush - if brush.curves_sculpt_tool in {'ADD', 'DELETE'}: + if brush.curves_sculpt_tool in {"ADD", "DELETE"}: return False return True @@ -453,22 +479,35 @@ class FalloffPanel(BrushPanel): row = col.row(align=True) row.prop(brush, "curve_preset", text="") - if brush.curve_preset == 'CUSTOM': + if brush.curve_preset == "CUSTOM": layout.template_curve_mapping(brush, "curve", brush=True) col = layout.column(align=True) row = col.row(align=True) - row.operator("brush.curve_preset", icon='SMOOTHCURVE', text="").shape = 'SMOOTH' - row.operator("brush.curve_preset", icon='SPHERECURVE', text="").shape = 'ROUND' - row.operator("brush.curve_preset", icon='ROOTCURVE', text="").shape = 'ROOT' - row.operator("brush.curve_preset", icon='SHARPCURVE', text="").shape = 'SHARP' - row.operator("brush.curve_preset", icon='LINCURVE', text="").shape = 'LINE' - row.operator("brush.curve_preset", icon='NOCURVE', text="").shape = 'MAX' + row.operator( + "brush.curve_preset", icon="SMOOTHCURVE", text="" + ).shape = "SMOOTH" + row.operator( + "brush.curve_preset", icon="SPHERECURVE", text="" + ).shape = "ROUND" + row.operator("brush.curve_preset", icon="ROOTCURVE", text="").shape = "ROOT" + row.operator( + "brush.curve_preset", icon="SHARPCURVE", text="" + ).shape = "SHARP" + row.operator("brush.curve_preset", icon="LINCURVE", text="").shape = "LINE" + row.operator("brush.curve_preset", icon="NOCURVE", text="").shape = "MAX" show_fallof_shape = False - if mode in {'SCULPT', 'PAINT_VERTEX', 'PAINT_WEIGHT'} and brush.sculpt_tool != 'POSE': + if ( + mode in {"SCULPT", "PAINT_VERTEX", "PAINT_WEIGHT"} + and brush.sculpt_tool != "POSE" + ): show_fallof_shape = True - if not show_fallof_shape and mode == 'SCULPT_CURVES' and context.space_data.type == 'PROPERTIES': + if ( + not show_fallof_shape + and mode == "SCULPT_CURVES" + and context.space_data.type == "PROPERTIES" + ): show_fallof_shape = True if show_fallof_shape: @@ -481,7 +520,7 @@ class FalloffPanel(BrushPanel): class DisplayPanel(BrushPanel): bl_label = "Brush Cursor" - bl_options = {'DEFAULT_CLOSED'} + bl_options = {"DEFAULT_CLOSED"} def draw_header(self, context): settings = self.paint_settings(context) @@ -508,37 +547,64 @@ class DisplayPanel(BrushPanel): col.active = brush.brush_capabilities.has_overlay and settings.show_brush col.prop(brush, "cursor_color_add", text="Cursor Color") - if mode == 'SCULPT' and brush.sculpt_capabilities.has_secondary_color: + if mode == "SCULPT" and brush.sculpt_capabilities.has_secondary_color: col.prop(brush, "cursor_color_subtract", text="Inverse Color") col.separator() row = col.row(align=True) row.prop(brush, "cursor_overlay_alpha", text="Falloff Opacity") - row.prop(brush, "use_cursor_overlay_override", toggle=True, text="", icon='BRUSH_DATA') row.prop( - brush, "use_cursor_overlay", text="", toggle=True, - icon='HIDE_OFF' if brush.use_cursor_overlay else 'HIDE_ON', + brush, + "use_cursor_overlay_override", + toggle=True, + text="", + icon="BRUSH_DATA", + ) + row.prop( + brush, + "use_cursor_overlay", + text="", + toggle=True, + icon="HIDE_OFF" if brush.use_cursor_overlay else "HIDE_ON", ) - if mode in {'PAINT_2D', 'PAINT_TEXTURE', 'PAINT_VERTEX', 'SCULPT'}: + if mode in {"PAINT_2D", "PAINT_TEXTURE", "PAINT_VERTEX", "SCULPT"}: row = col.row(align=True) row.prop(brush, "texture_overlay_alpha", text="Texture Opacity") - row.prop(brush, "use_primary_overlay_override", toggle=True, text="", icon='BRUSH_DATA') - if tex_slot.map_mode != 'STENCIL': + row.prop( + brush, + "use_primary_overlay_override", + toggle=True, + text="", + icon="BRUSH_DATA", + ) + if tex_slot.map_mode != "STENCIL": row.prop( - brush, "use_primary_overlay", text="", toggle=True, - icon='HIDE_OFF' if brush.use_primary_overlay else 'HIDE_ON', + brush, + "use_primary_overlay", + text="", + toggle=True, + icon="HIDE_OFF" if brush.use_primary_overlay else "HIDE_ON", ) - if mode in {'PAINT_TEXTURE', 'PAINT_2D'}: + if mode in {"PAINT_TEXTURE", "PAINT_2D"}: row = col.row(align=True) row.prop(brush, "mask_overlay_alpha", text="Mask Texture Opacity") - row.prop(brush, "use_secondary_overlay_override", toggle=True, text="", icon='BRUSH_DATA') - if tex_slot_mask.map_mode != 'STENCIL': + row.prop( + brush, + "use_secondary_overlay_override", + toggle=True, + text="", + icon="BRUSH_DATA", + ) + if tex_slot_mask.map_mode != "STENCIL": row.prop( - brush, "use_secondary_overlay", text="", toggle=True, - icon='HIDE_OFF' if brush.use_secondary_overlay else 'HIDE_ON', + brush, + "use_secondary_overlay", + text="", + toggle=True, + icon="HIDE_OFF" if brush.use_secondary_overlay else "HIDE_ON", ) @@ -549,14 +615,16 @@ class VIEW3D_MT_tools_projectpaint_clone(Menu): layout = self.layout for i, uv_layer in enumerate(context.active_object.data.uv_layers): - props = layout.operator("wm.context_set_int", text=uv_layer.name, translate=False) + props = layout.operator( + "wm.context_set_int", text=uv_layer.name, translate=False + ) props.data_path = "active_object.data.uv_layer_clone_index" props.value = i def brush_settings(layout, context, brush, popover=False): - """ Draw simple brush settings for Sculpt, - Texture/Vertex/Weight Paint modes, or skip certain settings for the popover """ + """Draw simple brush settings for Sculpt, + Texture/Vertex/Weight Paint modes, or skip certain settings for the popover""" mode = UnifiedPaintPanel.get_brush_mode(context) @@ -564,14 +632,17 @@ def brush_settings(layout, context, brush, popover=False): brush_shared_settings(layout, context, brush, popover) # Sculpt Mode # - if mode == 'SCULPT': + if mode == "SCULPT": capabilities = brush.sculpt_capabilities sculpt_tool = brush.sculpt_tool # normal_radius_factor layout.prop(brush, "normal_radius_factor", slider=True) - if context.preferences.experimental.use_sculpt_tools_tilt and capabilities.has_tilt: + if ( + context.preferences.experimental.use_sculpt_tools_tilt + and capabilities.has_tilt + ): layout.prop(brush, "tilt_strength_factor", slider=True) row = layout.row(align=True) @@ -589,13 +660,20 @@ def brush_settings(layout, context, brush, popover=False): "auto_smooth_projection", slider=True, ) - if capabilities.has_auto_smooth or brush.sculpt_tool == "SMOOTH": + + is_smooth = capabilities.has_auto_smooth + is_smooth = is_smooth or brush.sculpt_tool in {"SMOOTH", "SIMPLIFY"} + hard_edge = context.tool_settings.unified_paint_settings.hard_edge_mode + + if is_smooth: UnifiedPaintPanel.prop_unified( layout, context, brush, - "autosmooth_fset_slide", + "hard_corner_pin", slider=True, + unified_name = "use_unified_hard_corner_pin", + text="Corner Pin" ) if capabilities.has_auto_smooth: @@ -607,14 +685,14 @@ def brush_settings(layout, context, brush, popover=False): pressure_name="use_inverse_smooth_pressure", slider=True, ) - + if capabilities.has_auto_smooth or brush.sculpt_tool == "SMOOTH": layout.prop(brush, "use_weighted_smooth") - + # topology_rake_factor if ( - capabilities.has_topology_rake and - context.sculpt_object.use_dynamic_topology_sculpting + capabilities.has_topology_rake + and context.sculpt_object.use_dynamic_topology_sculpting ): layout.prop(brush, "topology_rake_factor", slider=True) layout.prop(brush, "use_curvature_rake") @@ -626,7 +704,7 @@ def brush_settings(layout, context, brush, popover=False): # crease_pinch_factor if capabilities.has_pinch_factor: text = "Pinch" - if sculpt_tool in {'BLOB', 'SNAKE_HOOK'}: + if sculpt_tool in {"BLOB", "SNAKE_HOOK"}: text = "Magnify" layout.prop(brush, "crease_pinch_factor", slider=True, text=text) @@ -669,30 +747,34 @@ def brush_settings(layout, context, brush, popover=False): ups = context.scene.tool_settings.unified_paint_settings row = layout.row(align=True) UnifiedPaintPanel.prop_unified_color(row, context, brush, "color", text="") - UnifiedPaintPanel.prop_unified_color(row, context, brush, "secondary_color", text="") + UnifiedPaintPanel.prop_unified_color( + row, context, brush, "secondary_color", text="" + ) row.separator() - row.operator("paint.brush_colors_flip", icon='FILE_REFRESH', text="", emboss=False) - row.prop(ups, "use_unified_color", text="", icon='BRUSHES_ALL') + row.operator( + "paint.brush_colors_flip", icon="FILE_REFRESH", text="", emboss=False + ) + row.prop(ups, "use_unified_color", text="", icon="BRUSHES_ALL") layout.prop(brush, "blend", text="Blend Mode") # Per sculpt tool options. - if sculpt_tool == 'CLAY_STRIPS': + if sculpt_tool == "CLAY_STRIPS": row = layout.row() row.prop(brush, "tip_roundness") - elif sculpt_tool == 'ELASTIC_DEFORM': + elif sculpt_tool == "ELASTIC_DEFORM": layout.separator() layout.prop(brush, "elastic_deform_type") layout.prop(brush, "elastic_deform_volume_preservation", slider=True) layout.separator() - elif sculpt_tool == 'SNAKE_HOOK': + elif sculpt_tool == "SNAKE_HOOK": layout.separator() layout.prop(brush, "snake_hook_deform_type") layout.separator() - elif sculpt_tool == 'POSE': + elif sculpt_tool == "POSE": layout.separator() layout.prop(brush, "deform_target") layout.separator() @@ -700,9 +782,12 @@ def brush_settings(layout, context, brush, popover=False): layout.prop(brush, "pose_origin_type") layout.prop(brush, "pose_offset") layout.prop(brush, "pose_smooth_iterations") - if brush.pose_deform_type == 'ROTATE_TWIST' and brush.pose_origin_type in {'TOPOLOGY', 'FACE_SETS'}: + if brush.pose_deform_type == "ROTATE_TWIST" and brush.pose_origin_type in { + "TOPOLOGY", + "FACE_SETS", + }: layout.prop(brush, "pose_ik_segments") - if brush.pose_deform_type == 'SCALE_TRANSLATE': + if brush.pose_deform_type == "SCALE_TRANSLATE": layout.prop(brush, "use_pose_lock_rotation") layout.prop(brush, "use_pose_ik_anchored") layout.prop(brush, "use_connected_only") @@ -710,14 +795,14 @@ def brush_settings(layout, context, brush, popover=False): layout.separator() - elif sculpt_tool == 'CLOTH': + elif sculpt_tool == "CLOTH": layout.separator() layout.prop(brush, "cloth_simulation_area_type") - if brush.cloth_simulation_area_type != 'GLOBAL': + if brush.cloth_simulation_area_type != "GLOBAL": layout.prop(brush, "cloth_sim_limit") layout.prop(brush, "cloth_sim_falloff") - if brush.cloth_simulation_area_type == 'LOCAL': + if brush.cloth_simulation_area_type == "LOCAL": layout.prop(brush, "use_cloth_pin_simulation_boundary") layout.separator() @@ -731,25 +816,25 @@ def brush_settings(layout, context, brush, popover=False): layout.prop(brush, "use_cloth_collision") layout.separator() - elif sculpt_tool == 'SCRAPE': + elif sculpt_tool == "SCRAPE": row = layout.row(align=True) row.prop(brush, "area_radius_factor") row.prop(brush, "use_pressure_area_radius", text="") row = layout.row() row.prop(brush, "invert_to_scrape_fill", text="Invert to Fill") - elif sculpt_tool == 'FILL': + elif sculpt_tool == "FILL": row = layout.row(align=True) row.prop(brush, "area_radius_factor") row.prop(brush, "use_pressure_area_radius", text="") row = layout.row() row.prop(brush, "invert_to_scrape_fill", text="Invert to Scrape") - elif sculpt_tool == 'GRAB': + elif sculpt_tool == "GRAB": layout.prop(brush, "use_grab_active_vertex") layout.prop(brush, "use_grab_silhouette") - elif sculpt_tool == 'PAINT': + elif sculpt_tool == "PAINT": row = layout.row(align=True) row.prop(brush, "flow") row.prop(brush, "invert_flow_pressure", text="") @@ -779,11 +864,11 @@ def brush_settings(layout, context, brush, popover=False): row = layout.row() row.prop(brush, "tip_scale_x") - elif sculpt_tool == 'SMEAR': + elif sculpt_tool == "SMEAR": col = layout.column() col.prop(brush, "smear_deform_type") - elif sculpt_tool == 'BOUNDARY': + elif sculpt_tool == "BOUNDARY": layout.prop(brush, "deform_target") layout.separator() col = layout.column() @@ -791,52 +876,58 @@ def brush_settings(layout, context, brush, popover=False): col.prop(brush, "boundary_falloff_type") col.prop(brush, "boundary_offset") - elif sculpt_tool == 'TOPOLOGY': + elif sculpt_tool == "TOPOLOGY": col = layout.column() col.prop(brush, "slide_deform_type") - elif sculpt_tool == 'MULTIPLANE_SCRAPE': + elif sculpt_tool == "MULTIPLANE_SCRAPE": col = layout.column() col.prop(brush, "multiplane_scrape_angle") col.prop(brush, "use_multiplane_scrape_dynamic") col.prop(brush, "show_multiplane_scrape_planes_preview") - elif sculpt_tool == 'SMOOTH': + elif sculpt_tool == "SMOOTH": col = layout.column() col.prop(brush, "smooth_deform_type") - if brush.smooth_deform_type == 'SURFACE': + if brush.smooth_deform_type == "SURFACE": col.prop(brush, "surface_smooth_shape_preservation") col.prop(brush, "surface_smooth_current_vertex") col.prop(brush, "surface_smooth_iterations") - elif sculpt_tool == 'DISPLACEMENT_SMEAR': + elif sculpt_tool == "DISPLACEMENT_SMEAR": col = layout.column() col.prop(brush, "smear_deform_type") - elif sculpt_tool == 'MASK': + elif sculpt_tool == "MASK": layout.row().prop(brush, "mask_tool", expand=True) # End sculpt_tool interface. # 3D and 2D Texture Paint Mode. - elif mode in {'PAINT_TEXTURE', 'PAINT_2D'}: + elif mode in {"PAINT_TEXTURE", "PAINT_2D"}: capabilities = brush.image_paint_capabilities - if brush.image_tool == 'FILL': + if brush.image_tool == "FILL": # For some reason fill threshold only appears to be implemented in 2D paint. - if brush.color_type == 'COLOR': - if mode == 'PAINT_2D': - layout.prop(brush, "fill_threshold", text="Fill Threshold", slider=True) - elif brush.color_type == 'GRADIENT': + if brush.color_type == "COLOR": + if mode == "PAINT_2D": + layout.prop( + brush, "fill_threshold", text="Fill Threshold", slider=True + ) + elif brush.color_type == "GRADIENT": layout.row().prop(brush, "gradient_fill_mode", expand=True) - elif mode == 'SCULPT_CURVES': - if brush.curves_sculpt_tool == 'ADD': + elif mode == "SCULPT_CURVES": + if brush.curves_sculpt_tool == "ADD": layout.prop(brush.curves_sculpt_settings, "add_amount") col = layout.column(heading="Interpolate", align=True) col.prop(brush.curves_sculpt_settings, "interpolate_length", text="Length") col.prop(brush.curves_sculpt_settings, "interpolate_shape", text="Shape") - col.prop(brush.curves_sculpt_settings, "interpolate_point_count", text="Point Count") + col.prop( + brush.curves_sculpt_settings, + "interpolate_point_count", + text="Point Count", + ) col = layout.column() col.active = not brush.curves_sculpt_settings.interpolate_length @@ -845,13 +936,13 @@ def brush_settings(layout, context, brush, popover=False): col = layout.column() col.active = not brush.curves_sculpt_settings.interpolate_point_count col.prop(brush.curves_sculpt_settings, "points_per_curve") - elif brush.curves_sculpt_tool == 'GROW_SHRINK': + elif brush.curves_sculpt_tool == "GROW_SHRINK": layout.prop(brush.curves_sculpt_settings, "scale_uniform") layout.prop(brush.curves_sculpt_settings, "minimum_length") def brush_shared_settings(layout, context, brush, popover=False): - """ Draw simple brush settings that are shared between different paint modes. """ + """Draw simple brush settings that are shared between different paint modes.""" mode = UnifiedPaintPanel.get_brush_mode(context) @@ -865,14 +956,14 @@ def brush_shared_settings(layout, context, brush, popover=False): direction = False # 3D and 2D Texture Paint # - if mode in {'PAINT_TEXTURE', 'PAINT_2D'}: + if mode in {"PAINT_TEXTURE", "PAINT_2D"}: if not popover: blend_mode = brush.image_paint_capabilities.has_color size = brush.image_paint_capabilities.has_radius strength = strength_pressure = True # Sculpt # - if mode == 'SCULPT': + if mode == "SCULPT": size_mode = True if not popover: size = True @@ -881,7 +972,7 @@ def brush_shared_settings(layout, context, brush, popover=False): direction = not brush.sculpt_capabilities.has_direction # Vertex Paint # - if mode == 'PAINT_VERTEX': + if mode == "PAINT_VERTEX": if not popover: blend_mode = True size = True @@ -889,25 +980,25 @@ def brush_shared_settings(layout, context, brush, popover=False): strength_pressure = True # Weight Paint # - if mode == 'PAINT_WEIGHT': + if mode == "PAINT_WEIGHT": if not popover: size = True weight = brush.weight_paint_capabilities.has_weight strength = strength_pressure = True # Only draw blend mode for the Draw tool, because for other tools it is pointless. D5928#137944 - if brush.weight_tool == 'DRAW': + if brush.weight_tool == "DRAW": blend_mode = True # UV Sculpt # - if mode == 'UV_SCULPT': + if mode == "UV_SCULPT": size = True strength = True # Sculpt Curves # - if mode == 'SCULPT_CURVES': + if mode == "SCULPT_CURVES": size = True strength = True - direction = brush.curves_sculpt_tool in {'GROW_SHRINK', 'SELECTION_PAINT'} + direction = brush.curves_sculpt_tool in {"GROW_SHRINK", "SELECTION_PAINT"} ### Draw settings. ### ups = context.scene.tool_settings.unified_paint_settings @@ -928,7 +1019,7 @@ def brush_shared_settings(layout, context, brush, popover=False): size_owner = ups if ups.use_unified_size else brush size_prop = "size" - if size_mode and (size_owner.use_locked_size == 'SCENE'): + if size_mode and (size_owner.use_locked_size == "SCENE"): size_prop = "unprojected_radius" if size or size_mode: if size: @@ -978,7 +1069,7 @@ def brush_settings_advanced(layout, context, brush, popover=False): use_accumulate = False use_frontface = False - if mode == 'SCULPT': + if mode == "SCULPT": sculpt = context.tool_settings.sculpt capabilities = brush.sculpt_capabilities use_accumulate = capabilities.has_accumulate @@ -998,7 +1089,10 @@ def brush_settings_advanced(layout, context, brush, popover=False): col.prop(brush, "use_automasking_boundary_edges", text="Mesh Boundary") col.prop(brush, "use_automasking_boundary_face_sets", text="Face Sets Boundary") - if brush.use_automasking_boundary_edges or brush.use_automasking_boundary_face_sets: + if ( + brush.use_automasking_boundary_edges + or brush.use_automasking_boundary_face_sets + ): col = layout.column() col.use_property_split = False split = col.split(factor=0.4) @@ -1011,7 +1105,9 @@ def brush_settings_advanced(layout, context, brush, popover=False): row = col.row() row.prop(brush, "use_automasking_cavity", text="Cavity") - is_cavity_active = brush.use_automasking_cavity or brush.use_automasking_cavity_inverted + is_cavity_active = ( + brush.use_automasking_cavity or brush.use_automasking_cavity_inverted + ) if is_cavity_active: props = row.operator("sculpt.mask_from_cavity", text="Create Mask") @@ -1061,42 +1157,42 @@ def brush_settings_advanced(layout, context, brush, popover=False): layout.separator() # 3D and 2D Texture Paint. - elif mode in {'PAINT_TEXTURE', 'PAINT_2D'}: + elif mode in {"PAINT_TEXTURE", "PAINT_2D"}: capabilities = brush.image_paint_capabilities use_accumulate = capabilities.has_accumulate - if mode == 'PAINT_2D': + if mode == "PAINT_2D": layout.prop(brush, "use_paint_antialiasing") else: layout.prop(brush, "use_alpha") # Tool specific settings - if brush.image_tool == 'SOFTEN': + if brush.image_tool == "SOFTEN": layout.separator() layout.row().prop(brush, "direction", expand=True) layout.prop(brush, "sharp_threshold") - if mode == 'PAINT_2D': + if mode == "PAINT_2D": layout.prop(brush, "blur_kernel_radius") layout.prop(brush, "blur_mode") - elif brush.image_tool == 'MASK': + elif brush.image_tool == "MASK": layout.prop(brush, "weight", text="Mask Value", slider=True) - elif brush.image_tool == 'CLONE': - if mode == 'PAINT_2D': + elif brush.image_tool == "CLONE": + if mode == "PAINT_2D": layout.prop(brush, "clone_image", text="Image") layout.prop(brush, "clone_alpha", text="Alpha") # Vertex Paint # - elif mode == 'PAINT_VERTEX': + elif mode == "PAINT_VERTEX": layout.prop(brush, "use_alpha") - if brush.vertex_tool != 'SMEAR': + if brush.vertex_tool != "SMEAR": use_accumulate = True use_frontface = True # Weight Paint - elif mode == 'PAINT_WEIGHT': - if brush.weight_tool != 'SMEAR': + elif mode == "PAINT_WEIGHT": + if brush.weight_tool != "SMEAR": use_accumulate = True use_frontface = True @@ -1118,24 +1214,30 @@ def draw_color_settings(context, layout, brush, color_type=False): row.prop(brush, "color_type", expand=True) # Color wheel - if brush.color_type == 'COLOR': - UnifiedPaintPanel.prop_unified_color_picker(layout, context, brush, "color", value_slider=True) + if brush.color_type == "COLOR": + UnifiedPaintPanel.prop_unified_color_picker( + layout, context, brush, "color", value_slider=True + ) row = layout.row(align=True) UnifiedPaintPanel.prop_unified_color(row, context, brush, "color", text="") - UnifiedPaintPanel.prop_unified_color(row, context, brush, "secondary_color", text="") + UnifiedPaintPanel.prop_unified_color( + row, context, brush, "secondary_color", text="" + ) row.separator() - row.operator("paint.brush_colors_flip", icon='FILE_REFRESH', text="", emboss=False) - row.prop(ups, "use_unified_color", text="", icon='BRUSHES_ALL') + row.operator( + "paint.brush_colors_flip", icon="FILE_REFRESH", text="", emboss=False + ) + row.prop(ups, "use_unified_color", text="", icon="BRUSHES_ALL") # Gradient - elif brush.color_type == 'GRADIENT': + elif brush.color_type == "GRADIENT": layout.template_color_ramp(brush, "gradient", expand=True) layout.use_property_split = True col = layout.column() - if brush.image_tool == 'DRAW': + if brush.image_tool == "DRAW": UnifiedPaintPanel.prop_unified( col, context, @@ -1147,7 +1249,7 @@ def draw_color_settings(context, layout, brush, color_type=False): ) col.prop(brush, "gradient_stroke_mode", text="Gradient Mapping") - if brush.gradient_stroke_mode in {'SPACING_REPEAT', 'SPACING_CLAMP'}: + if brush.gradient_stroke_mode in {"SPACING_REPEAT", "SPACING_CLAMP"}: col.prop(brush, "grad_spacing") @@ -1163,8 +1265,8 @@ def brush_texture_settings(layout, brush, sculpt): layout.separator() - if tex_slot.map_mode == 'STENCIL': - if brush.texture and brush.texture.type == 'IMAGE': + if tex_slot.map_mode == "STENCIL": + if brush.texture and brush.texture.type == "IMAGE": layout.operator("brush.stencil_fit_image_aspect") layout.operator("brush.stencil_reset_transform") @@ -1175,7 +1277,10 @@ def brush_texture_settings(layout, brush, sculpt): if tex_slot.has_texture_angle_source: col.prop(tex_slot, "use_rake", text="Rake") - if brush.brush_capabilities.has_random_texture_angle and tex_slot.has_random_texture_angle: + if ( + brush.brush_capabilities.has_random_texture_angle + and tex_slot.has_random_texture_angle + ): if sculpt: if brush.sculpt_capabilities.has_random_texture_angle: col.prop(tex_slot, "use_random", text="Random") @@ -1194,9 +1299,9 @@ def brush_texture_settings(layout, brush, sculpt): # texture_sample_bias layout.prop(brush, "texture_sample_bias", slider=True, text="Sample Bias") - if brush.sculpt_tool == 'DRAW': + if brush.sculpt_tool == "DRAW": col = layout.column() - col.active = tex_slot.map_mode == 'AREA_PLANE' + col.active = tex_slot.map_mode == "AREA_PLANE" col.prop(brush, "use_color_as_displacement", text="Vector Displacement") @@ -1209,8 +1314,8 @@ def brush_mask_texture_settings(layout, brush): # map_mode layout.row().prop(mask_tex_slot, "mask_map_mode", text="Mask Mapping") - if mask_tex_slot.map_mode == 'STENCIL': - if brush.mask_texture and brush.mask_texture.type == 'IMAGE': + if mask_tex_slot.map_mode == "STENCIL": + if brush.mask_texture and brush.mask_texture.type == "IMAGE": layout.operator("brush.stencil_fit_image_aspect").mask = True layout.operator("brush.stencil_reset_transform").mask = True @@ -1223,7 +1328,10 @@ def brush_mask_texture_settings(layout, brush): if mask_tex_slot.has_texture_angle_source: col.prop(mask_tex_slot, "use_rake", text="Rake") - if brush.brush_capabilities.has_random_texture_angle and mask_tex_slot.has_random_texture_angle: + if ( + brush.brush_capabilities.has_random_texture_angle + and mask_tex_slot.has_random_texture_angle + ): col.prop(mask_tex_slot, "use_random", text="Random") if mask_tex_slot.use_random: col.prop(mask_tex_slot, "random_angle", text="Random Angle") @@ -1241,7 +1349,9 @@ def brush_basic_texpaint_settings(layout, context, brush, *, compact=False): row = layout.row(align=True) row.ui_units_x = 4 UnifiedPaintPanel.prop_unified_color(row, context, brush, "color", text="") - UnifiedPaintPanel.prop_unified_color(row, context, brush, "secondary_color", text="") + UnifiedPaintPanel.prop_unified_color( + row, context, brush, "secondary_color", text="" + ) row.separator() layout.prop(brush, "blend", text="" if compact else "Blend") @@ -1284,7 +1394,7 @@ def brush_basic__draw_color_selector(context, layout, brush, gp_settings, props) txt_ma = ma.name maxw = 25 if len(txt_ma) > maxw: - txt_ma = txt_ma[:maxw - 5] + '..' + txt_ma[-3:] + txt_ma = txt_ma[: maxw - 5] + ".." + txt_ma[-3:] sub = row.row(align=True) sub.enabled = not gp_settings.use_material_pin @@ -1297,20 +1407,37 @@ def brush_basic__draw_color_selector(context, layout, brush, gp_settings, props) row.prop(gp_settings, "use_material_pin", text="") - if brush.gpencil_tool in {'DRAW', 'FILL'}: + if brush.gpencil_tool in {"DRAW", "FILL"}: row.separator(factor=1.0) sub_row = row.row(align=True) sub_row.enabled = not gp_settings.pin_draw_mode if gp_settings.pin_draw_mode: - sub_row.prop_enum(gp_settings, "brush_draw_mode", 'MATERIAL', text="", icon='MATERIAL') - sub_row.prop_enum(gp_settings, "brush_draw_mode", 'VERTEXCOLOR', text="", icon='VPAINT_HLT') + sub_row.prop_enum( + gp_settings, "brush_draw_mode", "MATERIAL", text="", icon="MATERIAL" + ) + sub_row.prop_enum( + gp_settings, + "brush_draw_mode", + "VERTEXCOLOR", + text="", + icon="VPAINT_HLT", + ) else: - sub_row.prop_enum(settings, "color_mode", 'MATERIAL', text="", icon='MATERIAL') - sub_row.prop_enum(settings, "color_mode", 'VERTEXCOLOR', text="", icon='VPAINT_HLT') + sub_row.prop_enum( + settings, "color_mode", "MATERIAL", text="", icon="MATERIAL" + ) + sub_row.prop_enum( + settings, "color_mode", "VERTEXCOLOR", text="", icon="VPAINT_HLT" + ) sub_row = row.row(align=True) - sub_row.enabled = settings.color_mode == 'VERTEXCOLOR' or gp_settings.brush_draw_mode == 'VERTEXCOLOR' - sub_row.prop_with_popover(brush, "color", text="", panel="TOPBAR_PT_gpencil_vertexcolor") + sub_row.enabled = ( + settings.color_mode == "VERTEXCOLOR" + or gp_settings.brush_draw_mode == "VERTEXCOLOR" + ) + sub_row.prop_with_popover( + brush, "color", text="", panel="TOPBAR_PT_gpencil_vertexcolor" + ) row.prop(gp_settings, "pin_draw_mode", text="") if props: @@ -1327,26 +1454,28 @@ def brush_basic_gpencil_paint_settings(layout, context, brush, *, compact=False) return # Brush details - if brush.gpencil_tool == 'ERASE': + if brush.gpencil_tool == "ERASE": row = layout.row(align=True) row.prop(brush, "size", text="Radius") - row.prop(gp_settings, "use_pressure", text="", icon='STYLUS_PRESSURE') - row.prop(gp_settings, "use_occlude_eraser", text="", icon='XRAY') + row.prop(gp_settings, "use_pressure", text="", icon="STYLUS_PRESSURE") + row.prop(gp_settings, "use_occlude_eraser", text="", icon="XRAY") row.prop(gp_settings, "use_default_eraser", text="") row = layout.row(align=True) row.prop(gp_settings, "eraser_mode", expand=True) - if gp_settings.eraser_mode == 'SOFT': + if gp_settings.eraser_mode == "SOFT": row = layout.row(align=True) row.prop(gp_settings, "pen_strength", slider=True) - row.prop(gp_settings, "use_strength_pressure", text="", icon='STYLUS_PRESSURE') + row.prop( + gp_settings, "use_strength_pressure", text="", icon="STYLUS_PRESSURE" + ) row = layout.row(align=True) row.prop(gp_settings, "eraser_strength_factor") row = layout.row(align=True) row.prop(gp_settings, "eraser_thickness_factor") # FIXME: tools must use their own UI drawing! - elif brush.gpencil_tool == 'FILL': + elif brush.gpencil_tool == "FILL": use_property_split_prev = layout.use_property_split if compact: row = layout.row(align=True) @@ -1367,45 +1496,47 @@ def brush_basic_gpencil_paint_settings(layout, context, brush, *, compact=False) else: # brush.gpencil_tool == 'DRAW/TINT': row = layout.row(align=True) row.prop(brush, "size", text="Radius") - row.prop(gp_settings, "use_pressure", text="", icon='STYLUS_PRESSURE') + row.prop(gp_settings, "use_pressure", text="", icon="STYLUS_PRESSURE") if gp_settings.use_pressure and not compact: col = layout.column() - col.template_curve_mapping(gp_settings, "curve_sensitivity", brush=True, - use_negative_slope=True) + col.template_curve_mapping( + gp_settings, "curve_sensitivity", brush=True, use_negative_slope=True + ) row = layout.row(align=True) row.prop(gp_settings, "pen_strength", slider=True) - row.prop(gp_settings, "use_strength_pressure", text="", icon='STYLUS_PRESSURE') + row.prop(gp_settings, "use_strength_pressure", text="", icon="STYLUS_PRESSURE") if gp_settings.use_strength_pressure and not compact: col = layout.column() - col.template_curve_mapping(gp_settings, "curve_strength", brush=True, - use_negative_slope=True) + col.template_curve_mapping( + gp_settings, "curve_strength", brush=True, use_negative_slope=True + ) - if brush.gpencil_tool == 'TINT': + if brush.gpencil_tool == "TINT": row = layout.row(align=True) row.prop(gp_settings, "vertex_mode", text="Mode") else: row = layout.row(align=True) - if context.region.type == 'TOOL_HEADER': + if context.region.type == "TOOL_HEADER": row.prop(gp_settings, "caps_type", text="", expand=True) else: row.prop(gp_settings, "caps_type", text="Caps Type") # FIXME: tools must use their own UI drawing! if tool.idname in { - "builtin.arc", - "builtin.curve", - "builtin.line", - "builtin.box", - "builtin.circle", - "builtin.polyline", + "builtin.arc", + "builtin.curve", + "builtin.line", + "builtin.box", + "builtin.circle", + "builtin.polyline", }: settings = context.tool_settings.gpencil_sculpt if compact: row = layout.row(align=True) - row.prop(settings, "use_thickness_curve", text="", icon='SPHERECURVE') + row.prop(settings, "use_thickness_curve", text="", icon="SPHERECURVE") sub = row.row(align=True) sub.active = settings.use_thickness_curve sub.popover( @@ -1418,7 +1549,9 @@ def brush_basic_gpencil_paint_settings(layout, context, brush, *, compact=False) sub = row.row(align=True) if settings.use_thickness_curve: # Curve - layout.template_curve_mapping(settings, "thickness_primitive_curve", brush=True) + layout.template_curve_mapping( + settings, "thickness_primitive_curve", brush=True + ) def brush_basic_gpencil_sculpt_settings(layout, _context, brush, *, compact=False): @@ -1432,7 +1565,7 @@ def brush_basic_gpencil_sculpt_settings(layout, _context, brush, *, compact=Fals row = layout.row(align=True) row.prop(brush, "size", slider=True) sub = row.row(align=True) - sub.enabled = tool not in {'GRAB', 'CLONE'} + sub.enabled = tool not in {"GRAB", "CLONE"} sub.prop(gp_settings, "use_pressure", text="") row = layout.row(align=True) @@ -1440,22 +1573,22 @@ def brush_basic_gpencil_sculpt_settings(layout, _context, brush, *, compact=Fals row.prop(brush, "use_pressure_strength", text="") if compact: - if tool in {'THICKNESS', 'STRENGTH', 'PINCH', 'TWIST'}: + if tool in {"THICKNESS", "STRENGTH", "PINCH", "TWIST"}: row.separator() row.prop(gp_settings, "direction", expand=True, text="") else: use_property_split_prev = layout.use_property_split layout.use_property_split = False - if tool in {'THICKNESS', 'STRENGTH'}: + if tool in {"THICKNESS", "STRENGTH"}: layout.row().prop(gp_settings, "direction", expand=True) - elif tool == 'PINCH': + elif tool == "PINCH": row = layout.row(align=True) - row.prop_enum(gp_settings, "direction", value='ADD', text="Pinch") - row.prop_enum(gp_settings, "direction", value='SUBTRACT', text="Inflate") - elif tool == 'TWIST': + row.prop_enum(gp_settings, "direction", value="ADD", text="Pinch") + row.prop_enum(gp_settings, "direction", value="SUBTRACT", text="Inflate") + elif tool == "TWIST": row = layout.row(align=True) - row.prop_enum(gp_settings, "direction", value='ADD', text="CCW") - row.prop_enum(gp_settings, "direction", value='SUBTRACT', text="CW") + row.prop_enum(gp_settings, "direction", value="ADD", text="CCW") + row.prop_enum(gp_settings, "direction", value="SUBTRACT", text="CW") layout.use_property_split = use_property_split_prev @@ -1466,11 +1599,13 @@ def brush_basic_gpencil_weight_settings(layout, _context, brush, *, compact=Fals row.prop(brush, "strength", slider=True) row.prop(brush, "use_pressure_strength", text="") - if brush.gpencil_weight_tool in {'WEIGHT'}: + if brush.gpencil_weight_tool in {"WEIGHT"}: layout.prop(brush, "weight", slider=True) gp_settings = brush.gpencil_settings - layout.prop(gp_settings, "direction", expand=True, text="" if compact else "Direction") + layout.prop( + gp_settings, "direction", expand=True, text="" if compact else "Direction" + ) def brush_basic_gpencil_vertex_settings(layout, _context, brush, *, compact=False): @@ -1479,23 +1614,22 @@ def brush_basic_gpencil_vertex_settings(layout, _context, brush, *, compact=Fals # Brush details row = layout.row(align=True) row.prop(brush, "size", text="Radius") - row.prop(gp_settings, "use_pressure", text="", icon='STYLUS_PRESSURE') + row.prop(gp_settings, "use_pressure", text="", icon="STYLUS_PRESSURE") - if brush.gpencil_vertex_tool in {'DRAW', 'BLUR', 'SMEAR'}: + if brush.gpencil_vertex_tool in {"DRAW", "BLUR", "SMEAR"}: row = layout.row(align=True) row.prop(gp_settings, "pen_strength", slider=True) - row.prop(gp_settings, "use_strength_pressure", text="", icon='STYLUS_PRESSURE') + row.prop(gp_settings, "use_strength_pressure", text="", icon="STYLUS_PRESSURE") - if brush.gpencil_vertex_tool in {'DRAW', 'REPLACE'}: + if brush.gpencil_vertex_tool in {"DRAW", "REPLACE"}: row = layout.row(align=True) row.prop(gp_settings, "vertex_mode", text="Mode") -classes = ( - VIEW3D_MT_tools_projectpaint_clone, -) +classes = (VIEW3D_MT_tools_projectpaint_clone,) if __name__ == "__main__": # only for live edit. from bpy.utils import register_class + for cls in classes: register_class(cls) diff --git a/scripts/startup/bl_ui/space_toolsystem_toolbar.py b/scripts/startup/bl_ui/space_toolsystem_toolbar.py index 8864d654b52..42334c3878d 100644 --- a/scripts/startup/bl_ui/space_toolsystem_toolbar.py +++ b/scripts/startup/bl_ui/space_toolsystem_toolbar.py @@ -25,15 +25,16 @@ def kmi_to_string_or_none(kmi): def generate_from_enum_ex( - _context, *, - idname_prefix, - icon_prefix, - type, - attr, - cursor='DEFAULT', - tooldef_keywords=None, - icon_map=None, - use_separators=True, + _context, + *, + idname_prefix, + icon_prefix, + type, + attr, + cursor="DEFAULT", + tooldef_keywords=None, + icon_map=None, + use_separators=True, ): if tooldef_keywords is None: tooldef_keywords = {} @@ -42,8 +43,7 @@ def generate_from_enum_ex( enum_items = getattr( type.bl_rna.properties[attr], - "enum_items_static_ui" if use_separators else - "enum_items_static", + "enum_items_static_ui" if use_separators else "enum_items_static", ) for enum in enum_items: @@ -102,12 +102,11 @@ class _defs_view3d_generic: props = tool.operator_properties("view3d.cursor3d") layout.prop(props, "use_depth") layout.prop(props, "orientation") + return dict( idname="builtin.cursor", label="Cursor", - description=( - "Set the cursor location, drag to transform" - ), + description=("Set the cursor location, drag to transform"), icon="ops.generic.cursor", keymap="3D View Tool: Cursor", draw_settings=draw_settings, @@ -142,6 +141,7 @@ class _defs_view3d_generic: kmi_to_string_or_none(kmi_add), kmi_to_string_or_none(kmi_remove), ) + return dict( idname="builtin.measure", label="Measure", @@ -153,7 +153,6 @@ class _defs_view3d_generic: class _defs_annotate: - def draw_settings_common(context, layout, tool): gpd = context.annotation_data region_type = context.region.type @@ -163,15 +162,15 @@ class _defs_annotate: text = gpd.layers.active_note maxw = 25 if len(text) > maxw: - text = text[:maxw - 5] + '..' + text[-3:] + text = text[: maxw - 5] + ".." + text[-3:] else: text = "" gpl = context.active_annotation_layer if gpl is not None: layout.label(text="Annotation:") - if context.space_data.type in {'VIEW_3D', 'SEQUENCE_EDITOR'}: - if region_type == 'TOOL_HEADER': + if context.space_data.type in {"VIEW_3D", "SEQUENCE_EDITOR"}: + if region_type == "TOOL_HEADER": sub = layout.split(align=True, factor=0.5) sub.ui_units_x = 6.5 sub.prop(gpl, "color", text="") @@ -188,21 +187,30 @@ class _defs_annotate: space_type = tool.space_type tool_settings = context.tool_settings - if space_type == 'VIEW_3D': + if space_type == "VIEW_3D": row = layout.row(align=True) - row.prop(tool_settings, "annotation_stroke_placement_view3d", text="Placement") - if tool_settings.gpencil_stroke_placement_view3d == 'CURSOR': + row.prop( + tool_settings, "annotation_stroke_placement_view3d", text="Placement" + ) + if tool_settings.gpencil_stroke_placement_view3d == "CURSOR": row.prop(tool_settings.gpencil_sculpt, "lockaxis") - elif tool_settings.gpencil_stroke_placement_view3d in {'SURFACE', 'STROKE'}: + elif tool_settings.gpencil_stroke_placement_view3d in {"SURFACE", "STROKE"}: row.prop(tool_settings, "use_gpencil_stroke_endpoints") - elif space_type in {'IMAGE_EDITOR', 'NODE_EDITOR', 'SEQUENCE_EDITOR', 'CLIP_EDITOR'}: + elif space_type in { + "IMAGE_EDITOR", + "NODE_EDITOR", + "SEQUENCE_EDITOR", + "CLIP_EDITOR", + }: row = layout.row(align=True) - row.prop(tool_settings, "annotation_stroke_placement_view2d", text="Placement") + row.prop( + tool_settings, "annotation_stroke_placement_view2d", text="Placement" + ) if tool.idname == "builtin.annotate_line": props = tool.operator_properties("gpencil.annotate") - if region_type == 'TOOL_HEADER': + if region_type == "TOOL_HEADER": row = layout.row() row.ui_units_x = 15 row.prop(props, "arrowstyle_start", text="Start") @@ -214,7 +222,7 @@ class _defs_annotate: col.prop(props, "arrowstyle_end", text="End") elif tool.idname == "builtin.annotate": props = tool.operator_properties("gpencil.annotate") - if region_type == 'TOOL_HEADER': + if region_type == "TOOL_HEADER": row = layout.row() row.prop(props, "use_stabilizer", text="Stabilize Stroke") subrow = layout.row(align=False) @@ -234,10 +242,10 @@ class _defs_annotate: idname="builtin.annotate", label="Annotate", icon="ops.gpencil.draw", - cursor='PAINT_BRUSH', + cursor="PAINT_BRUSH", keymap="Generic Tool: Annotate", draw_settings=draw_settings, - options={'KEYMAP_FALLBACK'}, + options={"KEYMAP_FALLBACK"}, ) @ToolDef.from_fn.with_args(draw_settings=draw_settings_common) @@ -246,10 +254,10 @@ class _defs_annotate: idname="builtin.annotate_line", label="Annotate Line", icon="ops.gpencil.draw.line", - cursor='PAINT_BRUSH', + cursor="PAINT_BRUSH", keymap="Generic Tool: Annotate Line", draw_settings=draw_settings, - options={'KEYMAP_FALLBACK'}, + options={"KEYMAP_FALLBACK"}, ) @ToolDef.from_fn.with_args(draw_settings=draw_settings_common) @@ -258,10 +266,10 @@ class _defs_annotate: idname="builtin.annotate_polygon", label="Annotate Polygon", icon="ops.gpencil.draw.poly", - cursor='PAINT_BRUSH', + cursor="PAINT_BRUSH", keymap="Generic Tool: Annotate Polygon", draw_settings=draw_settings, - options={'KEYMAP_FALLBACK'}, + options={"KEYMAP_FALLBACK"}, ) @ToolDef.from_fn @@ -270,21 +278,21 @@ class _defs_annotate: # TODO: Move this setting to tool_settings prefs = context.preferences layout.prop(prefs.edit, "grease_pencil_eraser_radius", text="Radius") + return dict( idname="builtin.annotate_eraser", label="Annotate Eraser", icon="ops.gpencil.draw.eraser", - cursor='ERASER', + cursor="ERASER", keymap="Generic Tool: Annotate Eraser", draw_settings=draw_settings, - options={'KEYMAP_FALLBACK'}, + options={"KEYMAP_FALLBACK"}, ) class _defs_transform: - def draw_transform_sculpt_tool_settings(context, layout): - if context.mode != 'SCULPT': + if context.mode != "SCULPT": return layout.prop(context.tool_settings.sculpt, "transform_mode") @@ -292,7 +300,10 @@ class _defs_transform: def translate(): def draw_settings(context, layout, _tool): _defs_transform.draw_transform_sculpt_tool_settings(context, layout) - _template_widget.VIEW3D_GGT_xform_gizmo.draw_settings_with_index(context, layout, 1) + _template_widget.VIEW3D_GGT_xform_gizmo.draw_settings_with_index( + context, layout, 1 + ) + return dict( idname="builtin.move", label="Move", @@ -308,7 +319,10 @@ class _defs_transform: def rotate(): def draw_settings(context, layout, _tool): _defs_transform.draw_transform_sculpt_tool_settings(context, layout) - _template_widget.VIEW3D_GGT_xform_gizmo.draw_settings_with_index(context, layout, 2) + _template_widget.VIEW3D_GGT_xform_gizmo.draw_settings_with_index( + context, layout, 2 + ) + return dict( idname="builtin.rotate", label="Rotate", @@ -324,7 +338,10 @@ class _defs_transform: def scale(): def draw_settings(context, layout, _tool): _defs_transform.draw_transform_sculpt_tool_settings(context, layout) - _template_widget.VIEW3D_GGT_xform_gizmo.draw_settings_with_index(context, layout, 3) + _template_widget.VIEW3D_GGT_xform_gizmo.draw_settings_with_index( + context, layout, 3 + ) + return dict( idname="builtin.scale", label="Scale", @@ -339,7 +356,10 @@ class _defs_transform: @ToolDef.from_fn def scale_cage(): def draw_settings(context, layout, _tool): - _template_widget.VIEW3D_GGT_xform_gizmo.draw_settings_with_index(context, layout, 3) + _template_widget.VIEW3D_GGT_xform_gizmo.draw_settings_with_index( + context, layout, 3 + ) + return dict( idname="builtin.scale_cage", label="Scale Cage", @@ -354,7 +374,10 @@ class _defs_transform: def shear(): def draw_settings(context, layout, _tool): # props = tool.operator_properties("transform.shear") - _template_widget.VIEW3D_GGT_xform_gizmo.draw_settings_with_index(context, layout, 2) + _template_widget.VIEW3D_GGT_xform_gizmo.draw_settings_with_index( + context, layout, 2 + ) + return dict( idname="builtin.shear", label="Shear", @@ -372,7 +395,7 @@ class _defs_transform: show_drag = True tool_settings = context.tool_settings - if tool_settings.workspace_tool_type == 'FALLBACK': + if tool_settings.workspace_tool_type == "FALLBACK": show_drag = False if show_drag: @@ -380,14 +403,14 @@ class _defs_transform: layout.prop(props, "drag_action") _defs_transform.draw_transform_sculpt_tool_settings(context, layout) - _template_widget.VIEW3D_GGT_xform_gizmo.draw_settings_with_index(context, layout, 1) + _template_widget.VIEW3D_GGT_xform_gizmo.draw_settings_with_index( + context, layout, 1 + ) return dict( idname="builtin.transform", label="Transform", - description=( - "Supports any combination of grab, rotate, and scale at once" - ), + description=("Supports any combination of grab, rotate, and scale at once"), icon="ops.transform.transform", widget="VIEW3D_GGT_xform_gizmo", keymap="3D View Tool: Transform", @@ -396,7 +419,6 @@ class _defs_transform: class _defs_view3d_select: - @ToolDef.from_fn def select(): return dict( @@ -414,6 +436,7 @@ class _defs_view3d_select: row = layout.row() row.use_property_split = False row.prop(props, "mode", text="", expand=True, icon_only=True) + return dict( idname="builtin.select_box", label="Select Box", @@ -430,6 +453,7 @@ class _defs_view3d_select: row = layout.row() row.use_property_split = False row.prop(props, "mode", text="", expand=True, icon_only=True) + return dict( idname="builtin.select_lasso", label="Select Lasso", @@ -450,6 +474,7 @@ class _defs_view3d_select: def draw_cursor(_context, tool, xy): from gpu_extras.presets import draw_circle_2d + props = tool.operator_properties("view3d.select_circle") radius = props.radius draw_circle_2d(xy, (1.0,) * 4, radius, segments=32) @@ -466,7 +491,6 @@ class _defs_view3d_select: class _defs_view3d_add: - @staticmethod def description_interactive_add(context, _item, _km, *, prefix): km = context.window_manager.keyconfigs.user.keymaps["View3D Placement Modal"] @@ -478,9 +502,9 @@ class _defs_view3d_add: return None if km is not None: - kmi_snap = keymap_item_from_propvalue('SNAP_ON') - kmi_center = keymap_item_from_propvalue('PIVOT_CENTER_ON') - kmi_fixed_aspect = keymap_item_from_propvalue('FIXED_ASPECT_ON') + kmi_snap = keymap_item_from_propvalue("SNAP_ON") + kmi_center = keymap_item_from_propvalue("PIVOT_CENTER_ON") + kmi_fixed_aspect = keymap_item_from_propvalue("FIXED_ASPECT_ON") else: kmi_snap = None kmi_center = None @@ -515,7 +539,7 @@ class _defs_view3d_add: row = layout.row() row.prop(props, "snap_target") - region_is_header = bpy.context.region.type == 'TOOL_HEADER' + region_is_header = bpy.context.region.type == "TOOL_HEADER" if region_is_header: # Don't draw the "extra" popover here as we might have other settings & this should be last. @@ -539,7 +563,9 @@ class _defs_view3d_add: @ToolDef.from_fn def cube_add(): def draw_settings(_context, layout, tool, *, extra=False): - show_extra = _defs_view3d_add.draw_settings_interactive_add(layout, tool, extra) + show_extra = _defs_view3d_add.draw_settings_interactive_add( + layout, tool, extra + ) if show_extra: layout.popover("TOPBAR_PT_tool_settings_extra", text="...") @@ -548,7 +574,8 @@ class _defs_view3d_add: label="Add Cube", icon="ops.mesh.primitive_cube_add_gizmo", description=lambda *args: _defs_view3d_add.description_interactive_add( - *args, prefix=tip_("Add cube to mesh interactively"), + *args, + prefix=tip_("Add cube to mesh interactively"), ), widget="VIEW3D_GGT_placement", keymap="3D View Tool: Object, Add Primitive", @@ -558,7 +585,9 @@ class _defs_view3d_add: @ToolDef.from_fn def cone_add(): def draw_settings(_context, layout, tool, *, extra=False): - show_extra = _defs_view3d_add.draw_settings_interactive_add(layout, tool, extra) + show_extra = _defs_view3d_add.draw_settings_interactive_add( + layout, tool, extra + ) if extra: return @@ -574,7 +603,8 @@ class _defs_view3d_add: label="Add Cone", icon="ops.mesh.primitive_cone_add_gizmo", description=lambda *args: _defs_view3d_add.description_interactive_add( - *args, prefix=tip_("Add cone to mesh interactively"), + *args, + prefix=tip_("Add cone to mesh interactively"), ), widget="VIEW3D_GGT_placement", keymap="3D View Tool: Object, Add Primitive", @@ -584,7 +614,9 @@ class _defs_view3d_add: @ToolDef.from_fn def cylinder_add(): def draw_settings(_context, layout, tool, *, extra=False): - show_extra = _defs_view3d_add.draw_settings_interactive_add(layout, tool, extra) + show_extra = _defs_view3d_add.draw_settings_interactive_add( + layout, tool, extra + ) if extra: return @@ -594,12 +626,14 @@ class _defs_view3d_add: if show_extra: layout.popover("TOPBAR_PT_tool_settings_extra", text="...") + return dict( idname="builtin.primitive_cylinder_add", label="Add Cylinder", icon="ops.mesh.primitive_cylinder_add_gizmo", description=lambda *args: _defs_view3d_add.description_interactive_add( - *args, prefix=tip_("Add cylinder to mesh interactively"), + *args, + prefix=tip_("Add cylinder to mesh interactively"), ), widget="VIEW3D_GGT_placement", keymap="3D View Tool: Object, Add Primitive", @@ -609,7 +643,9 @@ class _defs_view3d_add: @ToolDef.from_fn def uv_sphere_add(): def draw_settings(_context, layout, tool, *, extra=False): - show_extra = _defs_view3d_add.draw_settings_interactive_add(layout, tool, extra) + show_extra = _defs_view3d_add.draw_settings_interactive_add( + layout, tool, extra + ) if extra: return @@ -619,12 +655,14 @@ class _defs_view3d_add: if show_extra: layout.popover("TOPBAR_PT_tool_settings_extra", text="...") + return dict( idname="builtin.primitive_uv_sphere_add", label="Add UV Sphere", icon="ops.mesh.primitive_sphere_add_gizmo", description=lambda *args: _defs_view3d_add.description_interactive_add( - *args, prefix=tip_("Add sphere to mesh interactively"), + *args, + prefix=tip_("Add sphere to mesh interactively"), ), widget="VIEW3D_GGT_placement", keymap="3D View Tool: Object, Add Primitive", @@ -634,7 +672,9 @@ class _defs_view3d_add: @ToolDef.from_fn def ico_sphere_add(): def draw_settings(_context, layout, tool, *, extra=False): - show_extra = _defs_view3d_add.draw_settings_interactive_add(layout, tool, extra) + show_extra = _defs_view3d_add.draw_settings_interactive_add( + layout, tool, extra + ) if extra: return @@ -643,12 +683,14 @@ class _defs_view3d_add: if show_extra: layout.popover("TOPBAR_PT_tool_settings_extra", text="...") + return dict( idname="builtin.primitive_ico_sphere_add", label="Add Ico Sphere", icon="ops.mesh.primitive_sphere_add_gizmo", description=lambda *args: _defs_view3d_add.description_interactive_add( - *args, prefix=tip_("Add sphere to mesh interactively"), + *args, + prefix=tip_("Add sphere to mesh interactively"), ), widget="VIEW3D_GGT_placement", keymap="3D View Tool: Object, Add Primitive", @@ -659,8 +701,8 @@ class _defs_view3d_add: # ----------------------------------------------------------------------------- # Object Modes (named based on context.mode) -class _defs_edit_armature: +class _defs_edit_armature: @ToolDef.from_fn def roll(): return dict( @@ -707,7 +749,7 @@ class _defs_edit_armature: return dict( idname="builtin.extrude_to_cursor", label="Extrude to Cursor", - cursor='CROSSHAIR', + cursor="CROSSHAIR", icon="ops.armature.extrude_cursor", widget=None, keymap=(), @@ -715,7 +757,6 @@ class _defs_edit_armature: class _defs_edit_mesh: - @ToolDef.from_fn def rip_region(): def draw_settings(_context, layout, tool): @@ -748,6 +789,7 @@ class _defs_edit_mesh: props = tool.operator_properties("mesh.polybuild_face_at_cursor_move") props_macro = props.MESH_OT_polybuild_face_at_cursor layout.prop(props_macro, "create_quads") + return dict( idname="builtin.poly_build", label="Poly Build", @@ -848,9 +890,9 @@ class _defs_edit_mesh: def draw_settings(context, layout, tool, *, extra=False): props = tool.operator_properties("mesh.bevel") - region_is_header = context.region.type == 'TOOL_HEADER' + region_is_header = context.region.type == "TOOL_HEADER" - edge_bevel = props.affect == 'EDGES' + edge_bevel = props.affect == "EDGES" if not extra: if region_is_header: @@ -892,7 +934,7 @@ class _defs_edit_mesh: col.active = edge_bevel col.prop(props, "miter_outer", text="Miter Outer") col.prop(props, "miter_inner", text="Inner") - if props.miter_inner == 'ARC': + if props.miter_inner == "ARC": col.prop(props, "spread") layout.separator() @@ -905,9 +947,11 @@ class _defs_edit_mesh: layout.prop(props, "profile_type") - if props.profile_type == 'CUSTOM': + if props.profile_type == "CUSTOM": tool_settings = context.tool_settings - layout.template_curveprofile(tool_settings, "custom_bevel_profile_preset") + layout.template_curveprofile( + tool_settings, "custom_bevel_profile_preset" + ) return dict( idname="builtin.bevel", @@ -924,9 +968,7 @@ class _defs_edit_mesh: idname="builtin.extrude_region", label="Extrude Region", # The operator description isn't useful in this case, give our own. - description=( - "Extrude freely or along an axis" - ), + description=("Extrude freely or along an axis"), icon="ops.mesh.extrude_region_move", widget="VIEW3D_GGT_xform_extrude", # Important to use same operator as 'E' key. @@ -954,6 +996,7 @@ class _defs_edit_mesh: props = tool.operator_properties("mesh.extrude_region_shrink_fatten") props_macro = props.TRANSFORM_OT_shrink_fatten layout.prop(props_macro, "use_even_offset") + return dict( idname="builtin.extrude_along_normals", label="Extrude Along Normals", @@ -983,7 +1026,7 @@ class _defs_edit_mesh: return dict( idname="builtin.extrude_to_cursor", label="Extrude to Cursor", - cursor='CROSSHAIR', + cursor="CROSSHAIR", icon="ops.mesh.dupli_extrude_cursor", widget=None, keymap=(), @@ -992,7 +1035,6 @@ class _defs_edit_mesh: @ToolDef.from_fn def loopcut_slide(): - def draw_settings(_context, layout, tool): props = tool.operator_properties("mesh.loopcut_slide") props_macro = props.MESH_OT_loopcut @@ -1024,6 +1066,7 @@ class _defs_edit_mesh: def draw_settings(_context, layout, tool): props = tool.operator_properties("mesh.vertices_smooth") layout.prop(props, "repeat") + return dict( idname="builtin.smooth", label="Smooth", @@ -1040,6 +1083,7 @@ class _defs_edit_mesh: layout.prop(props, "uniform") layout.prop(props, "normal") layout.prop(props, "seed") + return dict( idname="builtin.randomize", label="Randomize", @@ -1093,7 +1137,7 @@ class _defs_edit_mesh: layout.prop(props, "use_occlude_geometry") layout.prop(props, "only_selected") layout.prop(props, "xray") - region_is_header = bpy.context.region.type == 'TOOL_HEADER' + region_is_header = bpy.context.region.type == "TOOL_HEADER" if region_is_header: show_extra = True else: @@ -1108,15 +1152,16 @@ class _defs_edit_mesh: layout.prop(props, "angle_snapping_increment", text="") if show_extra: layout.popover("TOPBAR_PT_tool_settings_extra", text="...") + return dict( idname="builtin.knife", label="Knife", - cursor='KNIFE', + cursor="KNIFE", icon="ops.mesh.knife_tool", widget=None, keymap=(), draw_settings=draw_settings, - options={'KEYMAP_FALLBACK'}, + options={"KEYMAP_FALLBACK"}, ) @ToolDef.from_fn @@ -1127,6 +1172,7 @@ class _defs_edit_mesh: layout.prop(props, "clear_inner") layout.prop(props, "clear_outer") layout.prop(props, "threshold") + return dict( idname="builtin.bisect", label="Bisect", @@ -1138,7 +1184,6 @@ class _defs_edit_mesh: class _defs_edit_curve: - @ToolDef.from_fn def draw(): def draw_settings(context, layout, _tool, *, extra=False): @@ -1147,7 +1192,7 @@ class _defs_edit_curve: cps = tool_settings.curve_paint_settings region_type = context.region.type - if region_type == 'TOOL_HEADER': + if region_type == "TOOL_HEADER": if not extra: layout.prop(cps, "curve_type", text="") layout.prop(cps, "depth_mode", expand=True) @@ -1157,13 +1202,13 @@ class _defs_edit_curve: layout.use_property_split = True layout.use_property_decorate = False - if region_type != 'TOOL_HEADER': + if region_type != "TOOL_HEADER": layout.prop(cps, "curve_type") layout.separator() - if cps.curve_type == 'BEZIER': + if cps.curve_type == "BEZIER": layout.prop(cps, "fit_method") layout.prop(cps, "error_threshold") - if region_type != 'TOOL_HEADER': + if region_type != "TOOL_HEADER": row = layout.row(heading="Detect Corners", align=True) else: row = layout.row(heading="Corners", align=True) @@ -1181,13 +1226,13 @@ class _defs_edit_curve: col.prop(cps, "radius_max", text="Max") col.prop(cps, "use_pressure_radius") - if region_type != 'TOOL_HEADER' or cps.depth_mode == 'SURFACE': + if region_type != "TOOL_HEADER" or cps.depth_mode == "SURFACE": layout.separator() - if region_type != 'TOOL_HEADER': + if region_type != "TOOL_HEADER": row = layout.row() row.prop(cps, "depth_mode", expand=True) - if cps.depth_mode == 'SURFACE': + if cps.depth_mode == "SURFACE": col = layout.column() col.prop(cps, "surface_offset") col.prop(cps, "use_offset_absolute") @@ -1199,7 +1244,7 @@ class _defs_edit_curve: return dict( idname="builtin.draw", label="Draw", - cursor='PAINT_BRUSH', + cursor="PAINT_BRUSH", icon="ops.curve.draw", widget=None, keymap=(), @@ -1222,7 +1267,7 @@ class _defs_edit_curve: return dict( idname="builtin.extrude_cursor", label="Extrude to Cursor", - cursor='CROSSHAIR', + cursor="CROSSHAIR", icon="ops.curve.extrude_cursor", widget=None, keymap=(), @@ -1234,10 +1279,11 @@ class _defs_edit_curve: props = tool.operator_properties("curve.pen") layout.prop(props, "close_spline") layout.prop(props, "extrude_handle") + return dict( idname="builtin.pen", label="Curve Pen", - cursor='CROSSHAIR', + cursor="CROSSHAIR", icon="ops.curve.pen", widget=None, keymap=(), @@ -1259,9 +1305,7 @@ class _defs_edit_curve: return dict( idname="builtin.radius", label="Radius", - description=( - "Expand or contract the radius of the selected curve points" - ), + description=("Expand or contract the radius of the selected curve points"), icon="ops.curve.radius", widget="VIEW3D_GGT_tool_generic_handle_free", keymap=(), @@ -1274,6 +1318,7 @@ class _defs_edit_curve: layout.prop(props, "uniform") layout.prop(props, "normal") layout.prop(props, "seed") + return dict( idname="builtin.randomize", label="Randomize", @@ -1285,13 +1330,12 @@ class _defs_edit_curve: class _defs_edit_text: - @ToolDef.from_fn def select_text(): return dict( idname="builtin.select_text", label="Select Text", - cursor='TEXT', + cursor="TEXT", icon="ops.generic.select_box", widget=None, keymap=(), @@ -1299,7 +1343,6 @@ class _defs_edit_text: class _defs_pose: - @ToolDef.from_fn def breakdown(): return dict( @@ -1332,7 +1375,6 @@ class _defs_pose: class _defs_particle: - @staticmethod def generate_from_brushes(context): return generate_from_enum_ex( @@ -1345,7 +1387,6 @@ class _defs_particle: class _defs_sculpt: - @staticmethod def generate_from_brushes(context): return generate_from_enum_ex( @@ -1453,6 +1494,7 @@ class _defs_sculpt: layout.prop(props, "trim_orientation", expand=False) layout.prop(props, "trim_extrude_mode", expand=False) layout.prop(props, "use_cursor_depth", expand=False) + return dict( idname="builtin.box_trim", label="Box Trim", @@ -1470,6 +1512,7 @@ class _defs_sculpt: layout.prop(props, "trim_orientation", expand=False) layout.prop(props, "trim_extrude_mode", expand=False) layout.prop(props, "use_cursor_depth", expand=False) + return dict( idname="builtin.lasso_trim", label="Lasso Trim", @@ -1503,10 +1546,14 @@ class _defs_sculpt: row = layout.row(align=True) row.prop(props, "deform_axis") layout.prop(props, "orientation", expand=False) - if props.type == 'SURFACE_SMOOTH': + + if props.type in {"SMOOTH", "SURFACE_SMOOTH", "ENHANCE_DETAILS", "SHARPEN"}: + layout.prop(props, "hard_corner_pin", expand=False) + + if props.type == "SURFACE_SMOOTH": layout.prop(props, "surface_smooth_shape_preservation", expand=False) layout.prop(props, "surface_smooth_current_vertex", expand=False) - elif props.type == 'SHARPEN': + elif props.type == "SHARPEN": layout.prop(props, "sharpen_smooth_ratio", expand=False) layout.prop(props, "sharpen_intensify_detail_strength", expand=False) layout.prop(props, "sharpen_curvature_smooth_iterations", expand=False) @@ -1548,7 +1595,7 @@ class _defs_sculpt: def draw_settings(_context, layout, tool): props = tool.operator_properties("sculpt.color_filter") layout.prop(props, "type", expand=False) - if props.type == 'FILL': + if props.type == "FILL": layout.prop(props, "fill_color", expand=False) layout.prop(props, "strength") @@ -1597,15 +1644,16 @@ class _defs_sculpt: class _defs_vertex_paint: - @staticmethod def poll_select_mask(context): if context is None: return True ob = context.active_object - return (ob and ob.type == 'MESH' and - (ob.data.use_paint_mask or - ob.data.use_paint_mask_vertex)) + return ( + ob + and ob.type == "MESH" + and (ob.data.use_paint_mask or ob.data.use_paint_mask_vertex) + ) @staticmethod def generate_from_brushes(context): @@ -1619,14 +1667,12 @@ class _defs_vertex_paint: class _defs_texture_paint: - @staticmethod def poll_select_mask(context): if context is None: return True ob = context.active_object - return (ob and ob.type == 'MESH' and - (ob.data.use_paint_mask)) + return ob and ob.type == "MESH" and (ob.data.use_paint_mask) @staticmethod def generate_from_brushes(context): @@ -1636,20 +1682,21 @@ class _defs_texture_paint: icon_prefix="brush.paint_texture.", type=bpy.types.Brush, attr="image_tool", - cursor='PAINT_CROSS', + cursor="PAINT_CROSS", ) class _defs_weight_paint: - @staticmethod def poll_select_mask(context): if context is None: return True ob = context.active_object - return (ob and ob.type == 'MESH' and - (ob.data.use_paint_mask or - ob.data.use_paint_mask_vertex)) + return ( + ob + and ob.type == "MESH" + and (ob.data.use_paint_mask or ob.data.use_paint_mask_vertex) + ) @staticmethod def generate_from_brushes(context): @@ -1671,11 +1718,12 @@ class _defs_weight_paint: else: return layout.label(text="Weight: %.3f" % weight) + return dict( idname="builtin.sample_weight", label="Sample Weight", icon="ops.paint.weight_sample", - cursor='EYEDROPPER', + cursor="EYEDROPPER", widget=None, keymap=(), draw_settings=draw_settings, @@ -1687,7 +1735,7 @@ class _defs_weight_paint: idname="builtin.sample_vertex_group", label="Sample Vertex Group", icon="ops.paint.weight_sample_group", - cursor='EYEDROPPER', + cursor="EYEDROPPER", widget=None, keymap=(), ) @@ -1698,6 +1746,7 @@ class _defs_weight_paint: brush = context.tool_settings.weight_paint.brush if brush is not None: from bl_ui.properties_paint_common import UnifiedPaintPanel + UnifiedPaintPanel.prop_unified( layout, context, @@ -1731,7 +1780,6 @@ class _defs_weight_paint: class _defs_image_generic: - @staticmethod def poll_uvedit(context): if context is None: @@ -1748,9 +1796,7 @@ class _defs_image_generic: return dict( idname="builtin.cursor", label="Cursor", - description=( - "Set the cursor location, drag to transform" - ), + description=("Set the cursor location, drag to transform"), icon="ops.generic.cursor", keymap=(), ) @@ -1762,12 +1808,11 @@ class _defs_image_generic: def draw_settings(_context, layout, tool): props = tool.operator_properties("image.sample") layout.prop(props, "size") + return dict( idname="builtin.sample", label="Sample", - description=( - "Sample pixel values under the cursor" - ), + description=("Sample pixel values under the cursor"), icon="ops.paint.weight_sample", # XXX, needs own icon. keymap="Image Editor Tool: Sample", draw_settings=draw_settings, @@ -1775,7 +1820,6 @@ class _defs_image_generic: class _defs_image_uv_transform: - @ToolDef.from_fn def translate(): return dict( @@ -1814,9 +1858,7 @@ class _defs_image_uv_transform: return dict( idname="builtin.transform", label="Transform", - description=( - "Supports any combination of grab, rotate, and scale at once" - ), + description=("Supports any combination of grab, rotate, and scale at once"), icon="ops.transform.transform", widget="IMAGE_GGT_gizmo2d", # No keymap default action, only for gizmo! @@ -1824,7 +1866,6 @@ class _defs_image_uv_transform: class _defs_image_uv_select: - @ToolDef.from_fn def select(): return dict( @@ -1842,6 +1883,7 @@ class _defs_image_uv_select: row = layout.row() row.use_property_split = False row.prop(props, "mode", text="", expand=True, icon_only=True) + return dict( idname="builtin.select_box", label="Select Box", @@ -1858,6 +1900,7 @@ class _defs_image_uv_select: row = layout.row() row.use_property_split = False row.prop(props, "mode", text="", expand=True, icon_only=True) + return dict( idname="builtin.select_lasso", label="Select Lasso", @@ -1878,6 +1921,7 @@ class _defs_image_uv_select: def draw_cursor(_context, tool, xy): from gpu_extras.presets import draw_circle_2d + props = tool.operator_properties("uv.select_circle") radius = props.radius draw_circle_2d(xy, (1.0,) * 4, radius, segments=32) @@ -1894,7 +1938,6 @@ class _defs_image_uv_select: class _defs_image_uv_edit: - @ToolDef.from_fn def rip_region(): return dict( @@ -1904,16 +1947,16 @@ class _defs_image_uv_edit: # TODO: generic operator (UV version of `VIEW3D_GGT_tool_generic_handle_free`). widget=None, keymap=(), - options={'KEYMAP_FALLBACK'}, + options={"KEYMAP_FALLBACK"}, ) class _defs_image_uv_sculpt: - @staticmethod def generate_from_brushes(context): def draw_cursor(context, _tool, xy): from gpu_extras.presets import draw_circle_2d + tool_settings = context.tool_settings uv_sculpt = tool_settings.uv_sculpt if not uv_sculpt.show_brush: @@ -1938,13 +1981,12 @@ class _defs_image_uv_sculpt: operator="sculpt.uv_sculpt_stroke", keymap="Image Editor Tool: Uv, Sculpt Stroke", draw_cursor=draw_cursor, - options={'KEYMAP_FALLBACK'}, + options={"KEYMAP_FALLBACK"}, ), ) class _defs_gpencil_paint: - @staticmethod def gpencil_primitive_toolbar(context, layout, _tool, props): paint = context.tool_settings.gpencil_paint @@ -1977,7 +2019,7 @@ class _defs_gpencil_paint: icon_prefix="brush.gpencil_draw.", type=bpy.types.Brush, attr="gpencil_tool", - cursor='DOT', + cursor="DOT", tooldef_keywords=dict( operator="gpencil.draw", ), @@ -1990,11 +2032,12 @@ class _defs_gpencil_paint: row = layout.row() row.use_property_split = False row.prop(props, "flat_caps") + return dict( idname="builtin.cutter", label="Cutter", icon="ops.gpencil.stroke_cutter", - cursor='KNIFE', + cursor="KNIFE", widget=None, keymap=(), draw_settings=draw_settings, @@ -2010,7 +2053,7 @@ class _defs_gpencil_paint: idname="builtin.line", label="Line", icon="ops.gpencil.primitive_line", - cursor='CROSSHAIR', + cursor="CROSSHAIR", widget=None, keymap=(), draw_settings=draw_settings, @@ -2026,7 +2069,7 @@ class _defs_gpencil_paint: idname="builtin.polyline", label="Polyline", icon="ops.gpencil.primitive_polyline", - cursor='CROSSHAIR', + cursor="CROSSHAIR", widget=None, keymap=(), draw_settings=draw_settings, @@ -2042,7 +2085,7 @@ class _defs_gpencil_paint: idname="builtin.box", label="Box", icon="ops.gpencil.primitive_box", - cursor='CROSSHAIR', + cursor="CROSSHAIR", widget=None, keymap=(), draw_settings=draw_settings, @@ -2058,7 +2101,7 @@ class _defs_gpencil_paint: idname="builtin.circle", label="Circle", icon="ops.gpencil.primitive_circle", - cursor='CROSSHAIR', + cursor="CROSSHAIR", widget=None, keymap=(), draw_settings=draw_settings, @@ -2074,7 +2117,7 @@ class _defs_gpencil_paint: idname="builtin.arc", label="Arc", icon="ops.gpencil.primitive_arc", - cursor='CROSSHAIR', + cursor="CROSSHAIR", widget=None, keymap=(), draw_settings=draw_settings, @@ -2090,7 +2133,7 @@ class _defs_gpencil_paint: idname="builtin.curve", label="Curve", icon="ops.gpencil.primitive_curve", - cursor='CROSSHAIR', + cursor="CROSSHAIR", widget=None, keymap=(), draw_settings=draw_settings, @@ -2103,11 +2146,12 @@ class _defs_gpencil_paint: row = layout.row() row.use_property_split = False row.prop(props, "mode", expand=True) + return dict( idname="builtin.eyedropper", label="Eyedropper", icon="ops.paint.eyedropper_add", - cursor='EYEDROPPER', + cursor="EYEDROPPER", widget=None, keymap=(), draw_settings=draw_settings, @@ -2127,7 +2171,7 @@ class _defs_gpencil_paint: idname="builtin.interpolate", label="Interpolate", icon="ops.pose.breakdowner", - cursor='DEFAULT', + cursor="DEFAULT", widget=None, keymap=(), draw_settings=draw_settings, @@ -2137,11 +2181,11 @@ class _defs_gpencil_paint: class _defs_gpencil_edit: def is_segment(context): tool_settings = context.scene.tool_settings - if context.mode == 'EDIT_GPENCIL': - return tool_settings.gpencil_selectmode_edit == 'SEGMENT' - elif context.mode == 'SCULPT_GPENCIL': + if context.mode == "EDIT_GPENCIL": + return tool_settings.gpencil_selectmode_edit == "SEGMENT" + elif context.mode == "SCULPT_GPENCIL": return tool_settings.use_gpencil_select_mask_segment - elif context.mode == 'VERTEX_GPENCIL': + elif context.mode == "VERTEX_GPENCIL": return tool_settings.use_gpencil_vertex_select_mask_segment else: return False @@ -2160,7 +2204,10 @@ class _defs_gpencil_edit: def select(): def draw_settings(context, layout, _tool): if _defs_gpencil_edit.is_segment(context): - layout.prop(context.tool_settings.gpencil_sculpt, "intersection_threshold") + layout.prop( + context.tool_settings.gpencil_sculpt, "intersection_threshold" + ) + return dict( idname="builtin.select", label="Tweak", @@ -2178,7 +2225,10 @@ class _defs_gpencil_edit: row.use_property_split = False row.prop(props, "mode", text="", expand=True, icon_only=True) if _defs_gpencil_edit.is_segment(context): - layout.prop(context.tool_settings.gpencil_sculpt, "intersection_threshold") + layout.prop( + context.tool_settings.gpencil_sculpt, "intersection_threshold" + ) + return dict( idname="builtin.select_box", label="Select Box", @@ -2196,7 +2246,10 @@ class _defs_gpencil_edit: row.use_property_split = False row.prop(props, "mode", text="", expand=True, icon_only=True) if _defs_gpencil_edit.is_segment(context): - layout.prop(context.tool_settings.gpencil_sculpt, "intersection_threshold") + layout.prop( + context.tool_settings.gpencil_sculpt, "intersection_threshold" + ) + return dict( idname="builtin.select_lasso", label="Select Lasso", @@ -2215,10 +2268,13 @@ class _defs_gpencil_edit: row.prop(props, "mode", text="", expand=True, icon_only=True) layout.prop(props, "radius") if _defs_gpencil_edit.is_segment(context): - layout.prop(context.tool_settings.gpencil_sculpt, "intersection_threshold") + layout.prop( + context.tool_settings.gpencil_sculpt, "intersection_threshold" + ) def draw_cursor(_context, tool, xy): from gpu_extras.presets import draw_circle_2d + props = tool.operator_properties("gpencil.select_circle") radius = props.radius draw_circle_2d(xy, (1.0,) * 4, radius, segments=32) @@ -2238,11 +2294,8 @@ class _defs_gpencil_edit: return dict( idname="builtin.radius", label="Radius", - description=( - "Expand or contract the radius of the selected points" - ), + description=("Expand or contract the radius of the selected points"), icon="ops.gpencil.radius", - widget=None, keymap=(), ) @@ -2290,7 +2343,7 @@ class _defs_gpencil_edit: idname="builtin.transform_fill", label="Transform Fill", icon="ops.gpencil.transform_fill", - cursor='DEFAULT', + cursor="DEFAULT", widget=None, keymap=(), draw_settings=draw_settings, @@ -2311,7 +2364,7 @@ class _defs_gpencil_edit: idname="builtin.interpolate", label="Interpolate", icon="ops.pose.breakdowner", - cursor='DEFAULT', + cursor="DEFAULT", widget=None, keymap=(), draw_settings=draw_settings, @@ -2319,7 +2372,6 @@ class _defs_gpencil_edit: class _defs_gpencil_sculpt: - @staticmethod def poll_select_mask(context): if context is None: @@ -2327,11 +2379,12 @@ class _defs_gpencil_sculpt: ob = context.active_object tool_settings = context.scene.tool_settings return ( - ob is not None and - ob.type == 'GPENCIL' and ( - tool_settings.use_gpencil_select_mask_point or - tool_settings.use_gpencil_select_mask_stroke or - tool_settings.use_gpencil_select_mask_segment + ob is not None + and ob.type == "GPENCIL" + and ( + tool_settings.use_gpencil_select_mask_point + or tool_settings.use_gpencil_select_mask_stroke + or tool_settings.use_gpencil_select_mask_segment ) ) @@ -2350,7 +2403,6 @@ class _defs_gpencil_sculpt: class _defs_gpencil_weight: - @staticmethod def generate_from_brushes(context): return generate_from_enum_ex( @@ -2366,7 +2418,6 @@ class _defs_gpencil_weight: class _defs_curves_sculpt: - @staticmethod def generate_from_brushes(context): return generate_from_enum_ex( @@ -2383,7 +2434,6 @@ class _defs_curves_sculpt: class _defs_gpencil_vertex: - @staticmethod def poll_select_mask(context): if context is None: @@ -2391,11 +2441,12 @@ class _defs_gpencil_vertex: ob = context.active_object tool_settings = context.scene.tool_settings return ( - ob is not None and - ob.type == 'GPENCIL' and ( - tool_settings.use_gpencil_vertex_select_mask_point or - tool_settings.use_gpencil_vertex_select_mask_stroke or - tool_settings.use_gpencil_vertex_select_mask_segment + ob is not None + and ob.type == "GPENCIL" + and ( + tool_settings.use_gpencil_vertex_select_mask_point + or tool_settings.use_gpencil_vertex_select_mask_stroke + or tool_settings.use_gpencil_vertex_select_mask_segment ) ) @@ -2407,7 +2458,7 @@ class _defs_gpencil_vertex: icon_prefix="brush.paint_vertex.", type=bpy.types.Brush, attr="gpencil_vertex_tool", - cursor='DOT', + cursor="DOT", tooldef_keywords=dict( operator="gpencil.vertex_paint", ), @@ -2415,7 +2466,6 @@ class _defs_gpencil_vertex: class _defs_node_select: - @ToolDef.from_fn def select(): return dict( @@ -2433,6 +2483,7 @@ class _defs_node_select: row = layout.row() row.use_property_split = False row.prop(props, "mode", text="", expand=True, icon_only=True) + return dict( idname="builtin.select_box", label="Select Box", @@ -2449,6 +2500,7 @@ class _defs_node_select: row = layout.row() row.use_property_split = False row.prop(props, "mode", text="", expand=True, icon_only=True) + return dict( idname="builtin.select_lasso", label="Select Lasso", @@ -2469,6 +2521,7 @@ class _defs_node_select: def draw_cursor(_context, tool, xy): from gpu_extras.presets import draw_circle_2d + props = tool.operator_properties("node.select_circle") radius = props.radius draw_circle_2d(xy, (1.0,) * 4, radius, segments=32) @@ -2485,7 +2538,6 @@ class _defs_node_select: class _defs_node_edit: - @ToolDef.from_fn def links_cut(): return dict( @@ -2494,20 +2546,17 @@ class _defs_node_edit: icon="ops.node.links_cut", widget=None, keymap="Node Tool: Links Cut", - options={'KEYMAP_FALLBACK'}, + options={"KEYMAP_FALLBACK"}, ) class _defs_sequencer_generic: - @ToolDef.from_fn def cursor(): return dict( idname="builtin.cursor", label="Cursor", - description=( - "Set the cursor location, drag to transform" - ), + description=("Set the cursor location, drag to transform"), icon="ops.generic.cursor", keymap="Sequencer Tool: Cursor", ) @@ -2519,15 +2568,16 @@ class _defs_sequencer_generic: row = layout.row() row.use_property_split = False row.prop(props, "type", expand=True) + return dict( idname="builtin.blade", label="Blade", icon="ops.sequencer.blade", - cursor='CROSSHAIR', + cursor="CROSSHAIR", widget=None, keymap="Sequencer Tool: Blade", draw_settings=draw_settings, - options={'KEYMAP_FALLBACK'}, + options={"KEYMAP_FALLBACK"}, ) @ToolDef.from_fn @@ -2539,7 +2589,7 @@ class _defs_sequencer_generic: widget="SEQUENCER_GGT_gizmo_retime", operator=None, keymap=None, - options={'KEYMAP_FALLBACK'}, + options={"KEYMAP_FALLBACK"}, ) @ToolDef.from_fn @@ -2547,9 +2597,7 @@ class _defs_sequencer_generic: return dict( idname="builtin.sample", label="Sample", - description=( - "Sample pixel values under the cursor" - ), + description=("Sample pixel values under the cursor"), icon="ops.paint.weight_sample", # XXX, needs own icon. keymap="Sequencer Tool: Sample", ) @@ -2592,9 +2640,7 @@ class _defs_sequencer_generic: return dict( idname="builtin.transform", label="Transform", - description=( - "Supports any combination of grab, rotate, and scale at once" - ), + description=("Supports any combination of grab, rotate, and scale at once"), icon="ops.transform.transform", widget="SEQUENCER_GGT_gizmo2d", # No keymap default action, only for gizmo! @@ -2619,6 +2665,7 @@ class _defs_sequencer_select: row = layout.row() row.use_property_split = False row.prop(props, "mode", text="", expand=True, icon_only=True) + return dict( idname="builtin.select_box", label="Select Box", @@ -2630,10 +2677,10 @@ class _defs_sequencer_select: class IMAGE_PT_tools_active(ToolSelectPanelHelper, Panel): - bl_space_type = 'IMAGE_EDITOR' - bl_region_type = 'TOOLS' + bl_space_type = "IMAGE_EDITOR" + bl_region_type = "TOOLS" bl_label = "Tools" # not visible - bl_options = {'HIDE_HEADER'} + bl_options = {"HIDE_HEADER"} # Satisfy the `ToolSelectPanelHelper` API. keymap_prefix = "Image Editor Tool:" @@ -2645,7 +2692,7 @@ class IMAGE_PT_tools_active(ToolSelectPanelHelper, Panel): def tools_from_context(cls, context, mode=None): if mode is None: if context.space_data is None: - mode = 'VIEW' + mode = "VIEW" else: mode = context.space_data.mode for tools in (cls._tools[None], cls._tools.get(mode, ())): @@ -2693,11 +2740,11 @@ class IMAGE_PT_tools_active(ToolSelectPanelHelper, Panel): None: [ # for all modes ], - 'VIEW': [ + "VIEW": [ _defs_image_generic.sample, *_tools_annotate, ], - 'UV': [ + "UV": [ *_tools_select, _defs_image_generic.cursor, None, @@ -2713,10 +2760,10 @@ class IMAGE_PT_tools_active(ToolSelectPanelHelper, Panel): else () ), ], - 'MASK': [ + "MASK": [ None, ], - 'PAINT': [ + "PAINT": [ _defs_texture_paint.generate_from_brushes, None, *_tools_annotate, @@ -2725,10 +2772,10 @@ class IMAGE_PT_tools_active(ToolSelectPanelHelper, Panel): class NODE_PT_tools_active(ToolSelectPanelHelper, Panel): - bl_space_type = 'NODE_EDITOR' - bl_region_type = 'TOOLS' + bl_space_type = "NODE_EDITOR" + bl_region_type = "TOOLS" bl_label = "Tools" # not visible - bl_options = {'HIDE_HEADER'} + bl_options = {"HIDE_HEADER"} # Satisfy the `ToolSelectPanelHelper` API. keymap_prefix = "Node Editor Tool:" @@ -2789,10 +2836,10 @@ class NODE_PT_tools_active(ToolSelectPanelHelper, Panel): class VIEW3D_PT_tools_active(ToolSelectPanelHelper, Panel): - bl_space_type = 'VIEW_3D' - bl_region_type = 'TOOLS' + bl_space_type = "VIEW_3D" + bl_region_type = "TOOLS" bl_label = "Tools" # not visible - bl_options = {'HIDE_HEADER'} + bl_options = {"HIDE_HEADER"} # Satisfy the `ToolSelectPanelHelper` API. keymap_prefix = "3D View Tool:" @@ -2881,12 +2928,12 @@ class VIEW3D_PT_tools_active(ToolSelectPanelHelper, Panel): # _defs_view3d_generic.cursor, # End group. ], - 'OBJECT': [ + "OBJECT": [ *_tools_default, None, _tools_view3d_add, ], - 'POSE': [ + "POSE": [ *_tools_default, None, ( @@ -2895,7 +2942,7 @@ class VIEW3D_PT_tools_active(ToolSelectPanelHelper, Panel): _defs_pose.relax, ), ], - 'EDIT_ARMATURE': [ + "EDIT_ARMATURE": [ *_tools_default, None, _defs_edit_armature.roll, @@ -2910,9 +2957,8 @@ class VIEW3D_PT_tools_active(ToolSelectPanelHelper, Panel): ), _defs_transform.shear, ], - 'EDIT_MESH': [ + "EDIT_MESH": [ *_tools_default, - None, _tools_view3d_add, None, @@ -2959,7 +3005,7 @@ class VIEW3D_PT_tools_active(ToolSelectPanelHelper, Panel): _defs_edit_mesh.rip_edge, ), ], - 'EDIT_CURVE': [ + "EDIT_CURVE": [ *_tools_default, None, _defs_edit_curve.draw, @@ -2975,38 +3021,38 @@ class VIEW3D_PT_tools_active(ToolSelectPanelHelper, Panel): _defs_transform.shear, _defs_edit_curve.curve_vertex_randomize, ], - 'EDIT_CURVES': [ + "EDIT_CURVES": [ *_tools_default, ], - 'EDIT_SURFACE': [ + "EDIT_SURFACE": [ *_tools_default, None, _defs_transform.shear, ], - 'EDIT_METABALL': [ + "EDIT_METABALL": [ *_tools_default, None, _defs_transform.shear, ], - 'EDIT_LATTICE': [ + "EDIT_LATTICE": [ *_tools_default, None, _defs_transform.shear, ], - 'EDIT_TEXT': [ + "EDIT_TEXT": [ _defs_edit_text.select_text, _defs_view3d_generic.cursor, None, *_tools_annotate, _defs_view3d_generic.ruler, ], - 'PARTICLE': [ + "PARTICLE": [ *_tools_select, _defs_view3d_generic.cursor, None, _defs_particle.generate_from_brushes, ], - 'SCULPT': [ + "SCULPT": [ _defs_sculpt.generate_from_brushes, None, ( @@ -3039,7 +3085,7 @@ class VIEW3D_PT_tools_active(ToolSelectPanelHelper, Panel): None, *_tools_annotate, ], - 'PAINT_TEXTURE': [ + "PAINT_TEXTURE": [ _defs_texture_paint.generate_from_brushes, None, lambda context: ( @@ -3049,7 +3095,7 @@ class VIEW3D_PT_tools_active(ToolSelectPanelHelper, Panel): ), *_tools_annotate, ], - 'PAINT_VERTEX': [ + "PAINT_VERTEX": [ _defs_vertex_paint.generate_from_brushes, None, lambda context: ( @@ -3059,7 +3105,7 @@ class VIEW3D_PT_tools_active(ToolSelectPanelHelper, Panel): ), *_tools_annotate, ], - 'PAINT_WEIGHT': [ + "PAINT_WEIGHT": [ _defs_weight_paint.generate_from_brushes, _defs_weight_paint.gradient, None, @@ -3085,7 +3131,7 @@ class VIEW3D_PT_tools_active(ToolSelectPanelHelper, Panel): ), *_tools_annotate, ], - 'PAINT_GPENCIL': [ + "PAINT_GPENCIL": [ _defs_view3d_generic.cursor, None, _defs_gpencil_paint.generate_from_brushes, @@ -3104,7 +3150,7 @@ class VIEW3D_PT_tools_active(ToolSelectPanelHelper, Panel): None, *_tools_annotate, ], - 'EDIT_GPENCIL': [ + "EDIT_GPENCIL": [ *_tools_gpencil_select, _defs_view3d_generic.cursor, None, @@ -3123,7 +3169,7 @@ class VIEW3D_PT_tools_active(ToolSelectPanelHelper, Panel): None, *_tools_annotate, ], - 'SCULPT_GPENCIL': [ + "SCULPT_GPENCIL": [ _defs_gpencil_sculpt.generate_from_brushes, None, *_tools_annotate, @@ -3133,12 +3179,12 @@ class VIEW3D_PT_tools_active(ToolSelectPanelHelper, Panel): else () ), ], - 'WEIGHT_GPENCIL': [ + "WEIGHT_GPENCIL": [ _defs_gpencil_weight.generate_from_brushes, None, *_tools_annotate, ], - 'VERTEX_GPENCIL': [ + "VERTEX_GPENCIL": [ _defs_gpencil_vertex.generate_from_brushes, None, *_tools_annotate, @@ -3149,7 +3195,7 @@ class VIEW3D_PT_tools_active(ToolSelectPanelHelper, Panel): else () ), ], - 'SCULPT_CURVES': [ + "SCULPT_CURVES": [ _defs_curves_sculpt.generate_from_brushes, None, *_tools_annotate, @@ -3158,10 +3204,10 @@ class VIEW3D_PT_tools_active(ToolSelectPanelHelper, Panel): class SEQUENCER_PT_tools_active(ToolSelectPanelHelper, Panel): - bl_space_type = 'SEQUENCE_EDITOR' - bl_region_type = 'TOOLS' + bl_space_type = "SEQUENCE_EDITOR" + bl_region_type = "TOOLS" bl_label = "Tools" # not visible - bl_options = {'HIDE_HEADER'} + bl_options = {"HIDE_HEADER"} # Satisfy the `ToolSelectPanelHelper` API. keymap_prefix = "Sequence Editor Tool:" @@ -3206,9 +3252,8 @@ class SEQUENCER_PT_tools_active(ToolSelectPanelHelper, Panel): # The keys match sequence editors view type: `context.space_data.view_type`. # The values represent the tools, see `ToolSelectPanelHelper` for details. _tools = { - None: [ - ], - 'PREVIEW': [ + None: [], + "PREVIEW": [ *_tools_select, _defs_sequencer_generic.cursor, None, @@ -3220,12 +3265,12 @@ class SEQUENCER_PT_tools_active(ToolSelectPanelHelper, Panel): _defs_sequencer_generic.sample, *_tools_annotate, ], - 'SEQUENCER': [ + "SEQUENCER": [ *_tools_select, _defs_sequencer_generic.blade, _defs_sequencer_generic.retime, ], - 'SEQUENCER_PREVIEW': [ + "SEQUENCER_PREVIEW": [ *_tools_select, None, *_tools_annotate, @@ -3244,5 +3289,6 @@ classes = ( if __name__ == "__main__": # only for live edit. from bpy.utils import register_class + for cls in classes: register_class(cls) diff --git a/source/blender/blenkernel/BKE_brush.h b/source/blender/blenkernel/BKE_brush.h index 41c67bd81a0..8414d0f51e6 100644 --- a/source/blender/blenkernel/BKE_brush.h +++ b/source/blender/blenkernel/BKE_brush.h @@ -220,6 +220,7 @@ void BKE_brush_get_dyntopo(struct Brush *brush, struct Sculpt *sd, struct DynTop bool BKE_brush_hard_edge_mode_get(const struct Scene *scene, const struct Brush *brush); void BKE_brush_hard_edge_mode_set(struct Scene *scene, struct Brush *brush, bool val); +float BKE_brush_hard_corner_pin_get(const struct Scene *scene, const struct Brush *brush); float BKE_brush_fset_slide_get(const struct Scene *scene, const struct Brush *brush); float BKE_brush_curve_strength_ex(int curve_preset, diff --git a/source/blender/blenkernel/intern/brush.cc b/source/blender/blenkernel/intern/brush.cc index 5b0bd054988..e2bbfa491c9 100644 --- a/source/blender/blenkernel/intern/brush.cc +++ b/source/blender/blenkernel/intern/brush.cc @@ -2697,11 +2697,6 @@ void BKE_brush_hard_edge_mode_set(Scene *scene, Brush *brush, bool val) } } -float BKE_brush_fset_slide_get(const Scene *scene, const Brush *brush) -{ - return BKE_brush_hard_edge_mode_get(scene, brush) ? 0.0f : brush->autosmooth_fset_slide; -} - bool BKE_brush_has_cube_tip(const Brush *brush, ePaintMode paint_mode) { switch (paint_mode) { @@ -2725,3 +2720,12 @@ bool BKE_brush_has_cube_tip(const Brush *brush, ePaintMode paint_mode) return false; } + +float BKE_brush_hard_corner_pin_get(const Scene *scene, const Brush *brush) +{ + if (scene && scene->toolsettings->unified_paint_settings.flag & UNIFIED_PAINT_HARD_CORNER_PIN) { + return scene->toolsettings->unified_paint_settings.hard_corner_pin; + } + + return brush->hard_corner_pin; +} diff --git a/source/blender/blenloader/intern/versioning_300.cc b/source/blender/blenloader/intern/versioning_300.cc index 81900a11140..66d63b3383a 100644 --- a/source/blender/blenloader/intern/versioning_300.cc +++ b/source/blender/blenloader/intern/versioning_300.cc @@ -4248,9 +4248,16 @@ void blo_do_versions_300(FileData *fd, Library * /*lib*/, Main *bmain) } } - if (!DNA_struct_elem_find(fd->filesdna, "Brush", "float", "autosmooth_fset_slide")) { + if (!DNA_struct_elem_find(fd->filesdna, "Brush", "float", "hard_corner_pin")) { LISTBASE_FOREACH (Brush *, brush, &bmain->brushes) { - brush->autosmooth_fset_slide = 1.0f; + brush->hard_corner_pin = 1.0f; + } + LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) { + if (scene->toolsettings) { + scene->toolsettings->unified_paint_settings.hard_corner_pin = 1.0f; + scene->toolsettings->unified_paint_settings.flag |= UNIFIED_PAINT_HARD_CORNER_PIN | + UNIFIED_PAINT_FLAG_HARD_EDGE_MODE; + } } } diff --git a/source/blender/editors/sculpt_paint/sculpt_boundary.cc b/source/blender/editors/sculpt_paint/sculpt_boundary.cc index da932fb5ba5..1e48f3af9e9 100644 --- a/source/blender/editors/sculpt_paint/sculpt_boundary.cc +++ b/source/blender/editors/sculpt_paint/sculpt_boundary.cc @@ -1703,6 +1703,7 @@ static void SCULPT_boundary_autosmooth(SculptSession *ss, SculptBoundary *bounda Vector nodes = blender::bke::pbvh::search_gather(ss->pbvh, nullptr, nullptr); float projection = ss->cache->brush->autosmooth_projection; + float hard_corner_pin = BKE_brush_hard_corner_pin_get(ss->scene, ss->cache->brush); for (int iteration = 0; iteration <= count; iteration++) { for (int i = 0; i < nodes.size(); i++) { @@ -1730,7 +1731,8 @@ static void SCULPT_boundary_autosmooth(SculptSession *ss, SculptBoundary *bounda float sco[3]; - SCULPT_neighbor_coords_average_interior(ss, sco, vd.vertex, projection, true); + SCULPT_neighbor_coords_average_interior( + ss, sco, vd.vertex, projection, hard_corner_pin, true); float *co = SCULPT_brush_deform_target_vertex_co_get(ss, boundary->deform_target, &vd); @@ -1749,6 +1751,7 @@ static void SCULPT_boundary_build_smoothco(SculptSession *ss, SculptBoundary *bo boundary->smoothco = MEM_cnew_array(totvert, "boundary->smoothco"); float projection = ss->cache->brush->autosmooth_projection; + float hard_corner_pin = BKE_brush_hard_corner_pin_get(ss->scene, ss->cache->brush); Vector nodes = blender::bke::pbvh::search_gather(ss->pbvh, nullptr, nullptr); @@ -1764,7 +1767,8 @@ static void SCULPT_boundary_build_smoothco(SculptSession *ss, SculptBoundary *bo float sco[3]; - SCULPT_neighbor_coords_average_interior(ss, sco, vd.vertex, projection, true); + SCULPT_neighbor_coords_average_interior( + ss, sco, vd.vertex, projection, hard_corner_pin, true); float *co = SCULPT_brush_deform_target_vertex_co_get(ss, boundary->deform_target, &vd); diff --git a/source/blender/editors/sculpt_paint/sculpt_brush_types.cc b/source/blender/editors/sculpt_paint/sculpt_brush_types.cc index 14ae590a613..b4de901cf08 100644 --- a/source/blender/editors/sculpt_paint/sculpt_brush_types.cc +++ b/source/blender/editors/sculpt_paint/sculpt_brush_types.cc @@ -2834,6 +2834,7 @@ static void do_topology_rake_bmesh_task_cb_ex(void *__restrict userdata, const bool use_curvature = brush->flag2 & BRUSH_CURVATURE_RAKE; const bool do_reproject = SCULPT_need_reproject(ss); + float hard_corner_pin = BKE_brush_hard_corner_pin_get(ss->scene, brush); float direction[3]; copy_v3_v3(direction, ss->cache->grab_delta_symmetry); @@ -2859,7 +2860,8 @@ static void do_topology_rake_bmesh_task_cb_ex(void *__restrict userdata, SCULPT_curvature_begin(ss, data->nodes[n], false); } - if (brush->flag2 & BRUSH_SMOOTH_USE_AREA_WEIGHT) { + bool weighted = brush->flag2 & BRUSH_SMOOTH_USE_AREA_WEIGHT; + if (weighted) { BKE_pbvh_check_tri_areas(ss->pbvh, data->nodes[n]); } @@ -2908,7 +2910,8 @@ static void do_topology_rake_bmesh_task_cb_ex(void *__restrict userdata, float avg[3], val[3]; int cd_temp = data->scl->bmesh_cd_offset; - SCULPT_bmesh_four_neighbor_average(ss, avg, direction2, vd.bm_vert, 1.0f, cd_temp, false); + SCULPT_bmesh_four_neighbor_average( + ss, avg, direction2, vd.bm_vert, 1.0f, hard_corner_pin, cd_temp, weighted, false); sub_v3_v3v3(val, avg, vd.co); diff --git a/source/blender/editors/sculpt_paint/sculpt_filter_color.cc b/source/blender/editors/sculpt_paint/sculpt_filter_color.cc index 3351a66dc58..3c147541d9a 100644 --- a/source/blender/editors/sculpt_paint/sculpt_filter_color.cc +++ b/source/blender/editors/sculpt_paint/sculpt_filter_color.cc @@ -340,11 +340,10 @@ static int sculpt_color_filter_init(bContext *C, wmOperator *op) int mval[2]; RNA_int_get_array(op->ptr, "start_mouse", mval); + SCULPT_stroke_id_next(ob); + const bool use_automasking = SCULPT_is_automasking_enabled(sd, ss, nullptr); if (use_automasking) { - /* Increment stroke id for auto-masking system. */ - SCULPT_stroke_id_next(ob); - if (v3d) { /* Update the active face set manually as the paint cursor is not enabled when using the Mesh * Filter Tool. */ diff --git a/source/blender/editors/sculpt_paint/sculpt_filter_mesh.cc b/source/blender/editors/sculpt_paint/sculpt_filter_mesh.cc index 8aa69becb9b..a6b5c1f7ad2 100644 --- a/source/blender/editors/sculpt_paint/sculpt_filter_mesh.cc +++ b/source/blender/editors/sculpt_paint/sculpt_filter_mesh.cc @@ -360,6 +360,25 @@ static bool sculpt_mesh_filter_is_continuous(eSculptMeshFilterType type) MESH_FILTER_RELAX_FACE_SETS); } +static void mesh_filter_task_update_boundaries_cb(void *__restrict userdata, + const int i, + const TaskParallelTLS *__restrict /*tls*/) +{ + SculptThreadedTaskData *data = static_cast(userdata); + SculptSession *ss = data->ob->sculpt; + PBVHNode *node = data->nodes[i]; + + BKE_pbvh_check_tri_areas(ss->pbvh, node); + + /* Ensure boundaries and valences are up to date. */ + PBVHVertexIter vd; + BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) { + SCULPT_vertex_is_boundary(ss, vd.vertex, SCULPT_BOUNDARY_ALL); + SCULPT_vertex_valence_get(ss, vd.vertex); + } + BKE_pbvh_vertex_iter_end; +} + static void mesh_filter_task_cb(void *__restrict userdata, const int i, const TaskParallelTLS *__restrict /*tls*/) @@ -422,7 +441,8 @@ static void mesh_filter_task_cb(void *__restrict userdata, switch (filter_type) { case MESH_FILTER_SMOOTH: fade = clamp_f(fade, -1.0f, 1.0f); - SCULPT_neighbor_coords_average_interior(ss, avg, vd.vertex, projection, true); + SCULPT_neighbor_coords_average_interior( + ss, avg, vd.vertex, projection, ss->filter_cache->hard_corner_pin, true); sub_v3_v3v3(val, avg, orig_co); madd_v3_v3v3fl(val, orig_co, val, fade); sub_v3_v3v3(disp, val, orig_co); @@ -510,7 +530,8 @@ static void mesh_filter_task_cb(void *__restrict userdata, float disp_avg[3]; float avg_co[3]; - SCULPT_neighbor_coords_average(ss, avg_co, vd.vertex, projection, true); + SCULPT_neighbor_coords_average( + ss, avg_co, vd.vertex, projection, ss->filter_cache->hard_corner_pin, true); sub_v3_v3v3(disp_avg, avg_co, vd.co); mul_v3_v3fl( disp_avg, disp_avg, smooth_ratio * pow2f(ss->filter_cache->sharpen_factor[vd.index])); @@ -574,7 +595,7 @@ static void mesh_filter_enhance_details_init_directions(SculptSession *ss) PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); float avg[3]; - SCULPT_neighbor_coords_average(ss, avg, vertex, 0.0f, true); + SCULPT_neighbor_coords_average(ss, avg, vertex, 0.0f, filter_cache->hard_corner_pin, true); sub_v3_v3v3(filter_cache->detail_directions[i], avg, SCULPT_vertex_co_get(ss, vertex)); } } @@ -624,7 +645,7 @@ static void mesh_filter_sharpen_init(SculptSession *ss, PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); float avg[3]; - SCULPT_neighbor_coords_average(ss, avg, vertex, 0.0f, true); + SCULPT_neighbor_coords_average(ss, avg, vertex, 0.0f, filter_cache->hard_corner_pin, true); sub_v3_v3v3(filter_cache->detail_directions[i], avg, SCULPT_vertex_co_get(ss, vertex)); filter_cache->sharpen_factor[i] = len_v3(filter_cache->detail_directions[i]); } @@ -776,7 +797,22 @@ static void sculpt_mesh_filter_apply(bContext *C, wmOperator *op) ss->filter_cache->preserve_fset_boundaries = !ss->hard_edge_mode; TaskParallelSettings settings; - BKE_pbvh_parallel_range_settings(&settings, true, ss->filter_cache->nodes.size()); + BKE_pbvh_parallel_range_settings(&settings, false, ss->filter_cache->nodes.size()); + + if (ELEM(filter_type, + MESH_FILTER_SMOOTH, + MESH_FILTER_SURFACE_SMOOTH, + MESH_FILTER_ENHANCE_DETAILS, + MESH_FILTER_SHARPEN)) + { + BKE_pbvh_face_areas_begin(ss->pbvh); + BLI_task_parallel_range(0, + ss->filter_cache->nodes.size(), + &data, + mesh_filter_task_update_boundaries_cb, + &settings); + } + BLI_task_parallel_range( 0, ss->filter_cache->nodes.size(), &data, mesh_filter_task_cb, &settings); @@ -794,7 +830,8 @@ static void sculpt_mesh_filter_apply(bContext *C, wmOperator *op) SCULPT_flush_stroke_deform(sd, ob, true); } - /* The relax mesh filter needs the updated normals of the modified mesh after each iteration. */ + /* The relax mesh filter needs the updated normals of the modified mesh after each iteration. + */ if (ELEM(MESH_FILTER_RELAX, MESH_FILTER_RELAX_FACE_SETS)) { BKE_pbvh_update_normals(ss->pbvh, ss->subdiv_ccg); } @@ -1009,7 +1046,8 @@ static int sculpt_mesh_filter_start(bContext *C, wmOperator *op) { Object *ob = CTX_data_active_object(C); Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); - Sculpt *sd = CTX_data_tool_settings(C)->sculpt; + ToolSettings *tool_settings = CTX_data_tool_settings(C); + Sculpt *sd = tool_settings->sculpt; int mval[2]; RNA_int_get_array(op->ptr, "start_mouse", mval); @@ -1028,10 +1066,9 @@ static int sculpt_mesh_filter_start(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - if (use_automasking) { - /* Increment stroke id for automasking system. */ - SCULPT_stroke_id_next(ob); + SCULPT_stroke_id_next(ob); + if (use_automasking) { /* Update the active face set manually as the paint cursor is not enabled when using the Mesh * Filter Tool. */ float mval_fl[2] = {float(mval[0]), float(mval[1])}; @@ -1068,6 +1105,9 @@ static int sculpt_mesh_filter_start(bContext *C, wmOperator *op) RNA_enum_get(op->ptr, "orientation")); ss->filter_cache->orientation = orientation; + ss->filter_cache->hard_corner_pin = RNA_float_get(op->ptr, "hard_corner_pin"); + ss->hard_edge_mode = tool_settings->unified_paint_settings.hard_edge_mode; + return OPERATOR_PASS_THROUGH; } @@ -1144,6 +1184,16 @@ void SCULPT_mesh_filter_properties(wmOperatorType *ot) PropertyRNA *prop = RNA_def_collection_runtime( ot->srna, "event_history", &RNA_OperatorStrokeElement, "", ""); RNA_def_property_flag(prop, PropertyFlag(int(PROP_HIDDEN) | int(PROP_SKIP_SAVE))); + + RNA_def_float(ot->srna, + "hard_corner_pin", + 1.0f, + 0.0, + 1.0f, + "Corner Pin", + "How much to pin corners in hard edge mode", + 0.0f, + 1.0f); } static void sculpt_mesh_ui_exec(bContext * /*C*/, wmOperator *op) @@ -1151,6 +1201,15 @@ static void sculpt_mesh_ui_exec(bContext * /*C*/, wmOperator *op) uiLayout *layout = op->layout; uiItemR(layout, op->ptr, "strength", 0, nullptr, ICON_NONE); + if (ELEM(RNA_enum_get(op->ptr, "type"), + MESH_FILTER_SMOOTH, + MESH_FILTER_SURFACE_SMOOTH, + MESH_FILTER_ENHANCE_DETAILS, + MESH_FILTER_SHARPEN)) + { + uiItemR(layout, op->ptr, "hard_corner_pin", 0, nullptr, ICON_NONE); + } + uiItemR(layout, op->ptr, "iteration_count", 0, nullptr, ICON_NONE); uiItemR(layout, op->ptr, "orientation", 0, nullptr, ICON_NONE); layout = uiLayoutRow(layout, true); diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.hh b/source/blender/editors/sculpt_paint/sculpt_intern.hh index ec90827a23c..d6cf39115bb 100644 --- a/source/blender/editors/sculpt_paint/sculpt_intern.hh +++ b/source/blender/editors/sculpt_paint/sculpt_intern.hh @@ -641,6 +641,7 @@ struct FilterCache { bool weighted_smooth; float hard_edge_fac; bool hard_edge_mode; + float hard_corner_pin; float bound_smooth_radius; float bevel_smooth_fac; @@ -1879,11 +1880,17 @@ void SCULPT_bmesh_four_neighbor_average(SculptSession *ss, float direction[3], struct BMVert *v, float projection, + float hard_corner_pin, int cd_temp, + bool weighted, bool do_origco); -void SCULPT_neighbor_coords_average( - SculptSession *ss, float result[3], PBVHVertRef index, float projection, bool weighted); +void SCULPT_neighbor_coords_average(SculptSession *ss, + float result[3], + PBVHVertRef index, + float projection, + float hard_corner_pin, + bool weighted); float SCULPT_neighbor_mask_average(SculptSession *ss, PBVHVertRef index); void SCULPT_neighbor_color_average(SculptSession *ss, float result[4], PBVHVertRef index); @@ -1893,6 +1900,7 @@ void SCULPT_neighbor_coords_average_interior(SculptSession *ss, float result[3], PBVHVertRef vertex, float projection, + float hard_corner_pin, bool use_area_weights); BLI_INLINE bool SCULPT_need_reproject(const SculptSession *ss) diff --git a/source/blender/editors/sculpt_paint/sculpt_ops.cc b/source/blender/editors/sculpt_paint/sculpt_ops.cc index f9f41b8681a..30e90d9c5dc 100644 --- a/source/blender/editors/sculpt_paint/sculpt_ops.cc +++ b/source/blender/editors/sculpt_paint/sculpt_ops.cc @@ -1333,7 +1333,7 @@ static int sculpt_set_limit_surface_exec(bContext *C, wmOperator * /* op */) PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); float *f = vertex_attr_ptr(vertex, scl); - SCULPT_neighbor_coords_average(ss, f, vertex, 0.0, weighted); + SCULPT_neighbor_coords_average(ss, f, vertex, 0.0, 0.0f, weighted); } return OPERATOR_FINISHED; diff --git a/source/blender/editors/sculpt_paint/sculpt_replay.c b/source/blender/editors/sculpt_paint/sculpt_replay.c index 4038be046aa..e185359ddee 100644 --- a/source/blender/editors/sculpt_paint/sculpt_replay.c +++ b/source/blender/editors/sculpt_paint/sculpt_replay.c @@ -238,7 +238,7 @@ static ReplaySerialDef brush_def[] = { DEF(autosmooth_projection, REPLAY_FLOAT, Brush), DEF(autosmooth_spacing, REPLAY_FLOAT, Brush), DEF(boundary_smooth_factor, REPLAY_FLOAT, Brush), - DEF(autosmooth_fset_slide, REPLAY_FLOAT, Brush), + DEF(hard_corner_pin, REPLAY_FLOAT, Brush), DEF(sculpt_tool, REPLAY_BYTE, Brush), DEF(falloff_shape, REPLAY_BYTE, Brush), DEF(falloff_angle, REPLAY_FLOAT, Brush), diff --git a/source/blender/editors/sculpt_paint/sculpt_smooth.cc b/source/blender/editors/sculpt_paint/sculpt_smooth.cc index f763fd55ff8..8cfe473f888 100644 --- a/source/blender/editors/sculpt_paint/sculpt_smooth.cc +++ b/source/blender/editors/sculpt_paint/sculpt_smooth.cc @@ -17,6 +17,7 @@ #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" +#include "BKE_brush.h" #include "BKE_context.h" #include "BKE_mesh.hh" #include "BKE_mesh_mapping.h" @@ -38,6 +39,7 @@ static void SCULPT_neighbor_coords_average_interior_ex(SculptSession *ss, float result[3], PBVHVertRef vertex, float projection, + float hard_corner_pin, bool weighted, eSculptBoundary bound_type, eSculptCorner corner_type) @@ -102,12 +104,6 @@ static void SCULPT_neighbor_coords_average_interior_ex(SculptSession *ss, } SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); - /* Do not modify corner vertices. */ - if (is_corner & (SCULPT_CORNER_MESH | SCULPT_CORNER_SHARP)) { - copy_v3_v3(result, SCULPT_vertex_co_get(ss, vertex)); - return; - } - /* Avoid division by 0 when there are no neighbors. */ if (total == 0) { copy_v3_v3(result, SCULPT_vertex_co_get(ss, vertex)); @@ -120,21 +116,31 @@ static void SCULPT_neighbor_coords_average_interior_ex(SculptSession *ss, avg += -no * t * projection + co; - copy_v3_v3(result, avg); + if (is_corner) { + interp_v3_v3v3(result, co, avg, 1.0f - hard_corner_pin); + } + else { + copy_v3_v3(result, avg); + } } void SCULPT_neighbor_coords_average_interior(SculptSession *ss, float result[3], PBVHVertRef vertex, float projection, + float hard_corner_pin, bool use_area_weights) { eSculptBoundary bound_type = SCULPT_BOUNDARY_MESH | SCULPT_BOUNDARY_FACE_SET | SCULPT_BOUNDARY_SEAM | SCULPT_BOUNDARY_SHARP; - eSculptCorner corner_type = SCULPT_CORNER_MESH | SCULPT_CORNER_FACE_SET | SCULPT_CORNER_SEAM | - SCULPT_CORNER_SHARP; + eSculptCorner corner_type = SCULPT_CORNER_MESH | SCULPT_CORNER_SHARP; + + if (ss->hard_edge_mode) { + corner_type |= SCULPT_CORNER_FACE_SET | SCULPT_CORNER_SEAM; + } + SCULPT_neighbor_coords_average_interior_ex( - ss, result, vertex, projection, use_area_weights, bound_type, corner_type); + ss, result, vertex, projection, hard_corner_pin, use_area_weights, bound_type, corner_type); } /* Compares four vectors seperated by 90 degrees around normal and picks the one closest @@ -197,7 +203,9 @@ void SCULPT_bmesh_four_neighbor_average(SculptSession *ss, float direction[3], BMVert *v, float projection, + float hard_corner_pin, int cd_temp, + bool weighted, bool do_origco) { float avg_co[3] = {0.0f, 0.0f, 0.0f}; @@ -212,7 +220,6 @@ void SCULPT_bmesh_four_neighbor_average(SculptSession *ss, float dir[3]; float dir3[3] = {0.0f, 0.0f, 0.0f}; - const bool weighted = (ss->cache->brush->flag2 & BRUSH_SMOOTH_USE_AREA_WEIGHT); float *areas; SCULPT_vertex_check_origdata(ss, BKE_pbvh_make_vref(intptr_t(v))); @@ -343,7 +350,6 @@ void SCULPT_bmesh_four_neighbor_average(SculptSession *ss, tot_co += fac; } - /* In case vertex has no edges. */ if (tot_co > 0.0f) { mul_v3_v3fl(avg, avg_co, 1.0f / tot_co); @@ -359,6 +365,15 @@ void SCULPT_bmesh_four_neighbor_average(SculptSession *ss, copy_v3_v3(avg, co1); } + eSculptCorner corner_type = SCULPT_CORNER_MESH | SCULPT_CORNER_SHARP; + if (ss->hard_edge_mode) { + corner_type |= SCULPT_CORNER_FACE_SET; + } + + if (SCULPT_vertex_is_corner(ss, vertex, corner_type)) { + interp_v3_v3v3(avg, avg, SCULPT_vertex_co_get(ss, vertex), hard_corner_pin); + } + PBVH_CHECK_NAN(avg); // do not update in do_origco @@ -400,15 +415,19 @@ void SCULPT_bmesh_four_neighbor_average(SculptSession *ss, /* Generic functions for laplacian smoothing. These functions do not take boundary vertices into * account. */ -void SCULPT_neighbor_coords_average( - SculptSession *ss, float result[3], PBVHVertRef vertex, float projection, bool weighted) +void SCULPT_neighbor_coords_average(SculptSession *ss, + float result[3], + PBVHVertRef vertex, + float projection, + float hard_corner_pin, + bool weighted) { eSculptCorner corner_type = SCULPT_CORNER_SHARP | SCULPT_CORNER_FACE_SET; eSculptBoundary bound_type = SCULPT_BOUNDARY_SHARP | SCULPT_BOUNDARY_SEAM | SCULPT_BOUNDARY_UV | SCULPT_BOUNDARY_FACE_SET; SCULPT_neighbor_coords_average_interior_ex( - ss, result, vertex, projection, weighted, bound_type, corner_type); + ss, result, vertex, projection, hard_corner_pin, weighted, bound_type, corner_type); } float SCULPT_neighbor_mask_average(SculptSession *ss, PBVHVertRef vertex) @@ -496,7 +515,7 @@ static void do_enhance_details_brush_task_cb_ex(void *__restrict userdata, float disp[3]; float *detail_dir = blender::bke::paint::vertex_attr_ptr(vd.vertex, - ss->attrs.detail_directions); + ss->attrs.detail_directions); madd_v3_v3v3fl(disp, vd.co, detail_dir, fade); SCULPT_clip(sd, ss, vd.co, disp); @@ -517,6 +536,7 @@ static void SCULPT_enhance_details_brush(Sculpt *sd, Object *ob, Spanautosmooth_projection; bool use_area_weights = brush->flag2 & BRUSH_SMOOTH_USE_AREA_WEIGHT; + float hard_corner_pin = BKE_brush_hard_corner_pin_get(ss->scene, brush); if (SCULPT_stroke_is_first_brush_step(ss->cache)) { const int totvert = SCULPT_vertex_count_get(ss); @@ -533,9 +553,10 @@ static void SCULPT_enhance_details_brush(Sculpt *sd, Object *ob, Spanpbvh, i); float avg[3]; - SCULPT_neighbor_coords_average(ss, avg, vertex, projection, use_area_weights); + SCULPT_neighbor_coords_average( + ss, avg, vertex, projection, hard_corner_pin, use_area_weights); float *detail_dir = blender::bke::paint::vertex_attr_ptr(vertex, - ss->attrs.detail_directions); + ss->attrs.detail_directions); sub_v3_v3v3(detail_dir, avg, SCULPT_vertex_co_get(ss, vertex)); } @@ -585,6 +606,8 @@ static void do_smooth_brush_task_cb_ex(void *__restrict userdata, bool weighted = brush->flag2 & BRUSH_SMOOTH_USE_AREA_WEIGHT; bool modified = false; + float hard_corner_pin = BKE_brush_hard_corner_pin_get(ss->scene, brush); + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { if (!sculpt_brush_test_sq_fn(&test, vd.co)) { continue; @@ -617,19 +640,9 @@ static void do_smooth_brush_task_cb_ex(void *__restrict userdata, copy_v3_v3(oldco, vd.co); SCULPT_vertex_normal_get(ss, vd.vertex, oldno); - eSculptCorner is_corner = SCULPT_vertex_is_corner( - ss, vd.vertex, SCULPT_CORNER_MESH | SCULPT_CORNER_FACE_SET | SCULPT_CORNER_SHARP); - - if (is_corner & (SCULPT_CORNER_MESH | SCULPT_CORNER_SHARP)) { - continue; - } - - if ((is_corner & SCULPT_CORNER_FACE_SET) && ss->hard_edge_mode) { - continue; - } - float avg[3], val[3]; - SCULPT_neighbor_coords_average_interior(ss, avg, vd.vertex, projection, weighted); + SCULPT_neighbor_coords_average_interior( + ss, avg, vd.vertex, projection, hard_corner_pin, weighted); sub_v3_v3v3(val, avg, vd.co); madd_v3_v3v3fl(val, vd.co, val, fade); SCULPT_clip(sd, ss, vd.co, val); @@ -751,12 +764,13 @@ void SCULPT_surface_smooth_laplacian_step(SculptSession *ss, float weigthed_o[3], weigthed_q[3], d[3]; int v_index = BKE_pbvh_vertex_to_index(ss->pbvh, vertex); - SCULPT_neighbor_coords_average(ss, laplacian_smooth_co, vertex, 0.0f, use_area_weights); + SCULPT_neighbor_coords_average(ss, laplacian_smooth_co, vertex, 0.0f, 0.0f, use_area_weights); mul_v3_v3fl(weigthed_o, origco, alpha); mul_v3_v3fl(weigthed_q, co, 1.0f - alpha); add_v3_v3v3(d, weigthed_o, weigthed_q); - float *laplacian_disp = blender::bke::paint::vertex_attr_ptr(vertex, ss->attrs.laplacian_disp); + float *laplacian_disp = blender::bke::paint::vertex_attr_ptr(vertex, + ss->attrs.laplacian_disp); sub_v3_v3v3(laplacian_disp, laplacian_smooth_co, d); diff --git a/source/blender/makesdna/DNA_brush_defaults.h b/source/blender/makesdna/DNA_brush_defaults.h index 3e737cecdc3..53afc3c9bac 100644 --- a/source/blender/makesdna/DNA_brush_defaults.h +++ b/source/blender/makesdna/DNA_brush_defaults.h @@ -122,6 +122,7 @@ .dyntopo = _DNA_DEFAULT_DynTopoSettings,\ .concave_mask_factor = 0.75f,\ .falloff_shape = 0,\ + .hard_corner_pin = 1.0f,\ } /** \} */ diff --git a/source/blender/makesdna/DNA_brush_types.h b/source/blender/makesdna/DNA_brush_types.h index 6aa06721df9..846ad17b964 100644 --- a/source/blender/makesdna/DNA_brush_types.h +++ b/source/blender/makesdna/DNA_brush_types.h @@ -312,7 +312,7 @@ typedef struct Brush { float autosmooth_projection; int autosmooth_spacing; // spacing for BRUSH_CUSTOM_AUTOSMOOTH_SPACING float boundary_smooth_factor; - float autosmooth_fset_slide; + float hard_corner_pin; float tilt_strength_factor; diff --git a/source/blender/makesdna/DNA_scene_defaults.h b/source/blender/makesdna/DNA_scene_defaults.h index f8fb9dbed1c..563c64d51b6 100644 --- a/source/blender/makesdna/DNA_scene_defaults.h +++ b/source/blender/makesdna/DNA_scene_defaults.h @@ -276,7 +276,7 @@ .unprojected_radius = 0.29, \ .alpha = 0.5f, \ .weight = 0.5f, \ - .flag = UNIFIED_PAINT_SIZE | UNIFIED_PAINT_ALPHA | UNIFIED_PAINT_FLAG_HARD_EDGE_MODE, \ + .flag = UNIFIED_PAINT_SIZE | UNIFIED_PAINT_ALPHA | UNIFIED_PAINT_FLAG_HARD_EDGE_MODE | UNIFIED_PAINT_HARD_CORNER_PIN, \ } #define _DNA_DEFAULTS_ParticleEditSettings \ diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h index ba447546263..458a4756954 100644 --- a/source/blender/makesdna/DNA_scene_types.h +++ b/source/blender/makesdna/DNA_scene_types.h @@ -1379,7 +1379,8 @@ typedef struct UnifiedPaintSettings { float pixel_radius; float initial_pixel_radius; - char _pad[7]; + float hard_corner_pin; + char _pad[3]; char hard_edge_mode; float start_pixel_radius; @@ -1403,12 +1404,11 @@ typedef enum { UNIFIED_PAINT_ALPHA = (1 << 1), UNIFIED_PAINT_WEIGHT = (1 << 5), UNIFIED_PAINT_COLOR = (1 << 6), + UNIFIED_PAINT_HARD_CORNER_PIN = (1 << 7), + UNIFIED_PAINT_FLAG_HARD_EDGE_MODE = (1 << 8), /** Only used if unified size is enabled, mirrors the brush flag #BRUSH_LOCK_SIZE. */ UNIFIED_PAINT_BRUSH_LOCK_SIZE = (1 << 2), - UNIFIED_PAINT_FLAG_HARD_EDGE_MODE = (1 << 3), - - UNIFIED_PAINT_FLAG_UI_ADVANCED = (1 << 4), } eUnifiedPaintSettingsFlags; typedef struct CurvePaintSettings { diff --git a/source/blender/makesrna/intern/rna_brush.c b/source/blender/makesrna/intern/rna_brush.c index 375f0e58601..39643509877 100644 --- a/source/blender/makesrna/intern/rna_brush.c +++ b/source/blender/makesrna/intern/rna_brush.c @@ -441,7 +441,11 @@ static EnumPropertyItem rna_enum_brush_dyntopo_flag[] = { {DYNTOPO_DISABLED, "DISABLED", ICON_NONE, "Disable", ""}, {DYNTOPO_LOCAL_COLLAPSE, "LOCAL_COLLAPSE", ICON_NONE, "Local Collapse", ""}, {DYNTOPO_LOCAL_SUBDIVIDE, "LOCAL_SUBDIVIDE", ICON_NONE, "Local Subdivide", ""}, - {DYNTOPO_CLEANUP, "CLEANUP", ICON_NONE, "Cleanup", "Cleanup 3 and 4 pole vertices (development option)"}, + {DYNTOPO_CLEANUP, + "CLEANUP", + ICON_NONE, + "Cleanup", + "Cleanup 3 and 4 pole vertices (development option)"}, {0, NULL, 0, NULL, NULL}, }; @@ -3361,7 +3365,8 @@ static void rna_def_brush(BlenderRNA *brna) RNA_def_property_float_sdna(prop, NULL, "autosmooth_projection"); RNA_def_property_range(prop, 0.0f, 1.0f); RNA_def_property_ui_range(prop, 0.0f, 1.0f, 0.001, 3); - RNA_def_property_ui_text(prop, "Projection", "How much autosmooth should stick to surface"); + RNA_def_property_ui_text( + prop, "Projection", "How much autosmooth should stick to surface\n(prevents shrinking)."); RNA_def_property_update(prop, 0, "rna_Brush_update"); RNA_def_property_update(prop, 0, "rna_Brush_update"); @@ -3434,13 +3439,12 @@ static void rna_def_brush(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Boundary Smoothing", "How much to smooth sharp boundaries "); RNA_def_property_update(prop, 0, "rna_Brush_update"); - prop = RNA_def_property(srna, "autosmooth_fset_slide", PROP_FLOAT, PROP_FACTOR); - RNA_def_property_float_sdna(prop, NULL, "autosmooth_fset_slide"); + prop = RNA_def_property(srna, "hard_corner_pin", PROP_FLOAT, PROP_FACTOR); + RNA_def_property_float_sdna(prop, NULL, "hard_corner_pin"); RNA_def_property_float_default(prop, 0); RNA_def_property_range(prop, -2.0f, 2.0f); RNA_def_property_ui_range(prop, 0.0f, 1.0f, 0.001, 3); - RNA_def_property_ui_text( - prop, "Face Set Projection", "Slide face set boundaries instead of sharpening them"); + RNA_def_property_ui_text(prop, "Corner Pin", "How much to pin corners in hard edge mode."); RNA_def_property_update(prop, 0, "rna_Brush_update"); prop = RNA_def_property(srna, "vcol_boundary_exponent", PROP_FLOAT, PROP_FACTOR); diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c index 93651932f83..cd6480af452 100644 --- a/source/blender/makesrna/intern/rna_scene.c +++ b/source/blender/makesrna/intern/rna_scene.c @@ -3900,6 +3900,19 @@ static void rna_def_unified_paint_settings(BlenderRNA *brna) RNA_def_property_ui_text( prop, "Use Unified Hard Edge Mode", "Use global setting for hard edge mode"); + prop = RNA_def_property(srna, "use_unified_hard_corner_pin", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", UNIFIED_PAINT_HARD_CORNER_PIN); + RNA_def_property_ui_text( + prop, "Use Unified Hard Corner Pin", "Use global setting for hard corner pin"); + + prop = RNA_def_property(srna, "hard_corner_pin", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "hard_corner_pin"); + RNA_def_property_range(prop, 0.0f, 1.0f); + RNA_def_property_ui_text( + prop, "Use Unified Hard Corner Pin", "Use global setting for hard corner pin"); + RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE); + RNA_def_property_update(prop, 0, "rna_UnifiedPaintSettings_update"); + prop = RNA_def_property(srna, "use_unified_strength", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", UNIFIED_PAINT_ALPHA); RNA_def_property_ui_text(prop, @@ -3979,16 +3992,6 @@ static void rna_def_unified_paint_settings(BlenderRNA *brna) RNA_def_property_ui_text( prop, "Hard Edge Mode", "Hard edge mode; treat all face set boundaries as hard edges"); RNA_def_property_update(prop, 0, "rna_UnifiedPaintSettings_update"); - - prop = RNA_def_property(srna, "brush_editor_mode", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", UNIFIED_PAINT_FLAG_UI_ADVANCED); - RNA_def_property_ui_text(prop, "Workspace Edit Mode", "Brush editor mode"); - RNA_def_property_update(prop, NC_BRUSH | NA_EDITED, NULL); - - prop = RNA_def_property(srna, "brush_editor_advanced", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", 1 << 7); - RNA_def_property_ui_text(prop, "Show Advanced", "Show Advanced Settings"); - RNA_def_property_update(prop, NC_BRUSH | NA_EDITED, NULL); } static void rna_def_curve_paint_settings(BlenderRNA *brna) -- 2.30.2 From 972df91bc5b5047dc25d8c7388573e92e54a4de1 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Tue, 16 May 2023 13:11:59 -0700 Subject: [PATCH 093/279] temp-sculpt-dyntopo: UV reprojection improvements * UVs are now reprojected in dyntopo's topology smoother (dyntopo relaxes the topology a small amount to improve convergence). * Moved SCULPT_reproject_cdata to BKE_sculpt_reproject_cdata in blenkernel. * Fixed numerical instability issue that was causing corrupted UVs. --- source/blender/blenkernel/BKE_sculpt.hh | 6 +- .../blender/blenkernel/intern/customdata.cc | 47 ++- source/blender/blenkernel/intern/dyntopo.cc | 387 ++++++++++++++++-- .../blenkernel/intern/dyntopo_intern.hh | 3 +- source/blender/blenkernel/intern/pbvh.cc | 19 +- source/blender/bmesh/intern/bmesh_interp.c | 24 ++ source/blender/bmesh/intern/bmesh_mesh.cc | 2 +- source/blender/editors/sculpt_paint/sculpt.cc | 33 +- .../sculpt_paint/sculpt_brush_types.cc | 4 +- .../editors/sculpt_paint/sculpt_intern.hh | 7 +- .../editors/sculpt_paint/sculpt_smooth.cc | 328 ++------------- 11 files changed, 445 insertions(+), 415 deletions(-) diff --git a/source/blender/blenkernel/BKE_sculpt.hh b/source/blender/blenkernel/BKE_sculpt.hh index 069860a2b83..6f36adc6f5e 100644 --- a/source/blender/blenkernel/BKE_sculpt.hh +++ b/source/blender/blenkernel/BKE_sculpt.hh @@ -35,6 +35,11 @@ enum StrokeIDUser { }; ENUM_OPERATORS(StrokeIDUser, STROKEID_USER_LAYER_BRUSH); +void BKE_sculpt_reproject_cdata(SculptSession *ss, + PBVHVertRef vertex, + float startco[3], + float startno[3]); + namespace blender::bke::sculpt { BLI_INLINE bool stroke_id_clear(SculptSession *ss, PBVHVertRef vertex, StrokeIDUser user) { @@ -88,5 +93,4 @@ BLI_INLINE bool test_sculpt_flag(SculptSession *ss, PBVHVertRef vertex, uint8_t { return blender::bke::paint::vertex_attr_get(vertex, ss->attrs.flags) & flag; } - } // namespace blender::bke::sculpt diff --git a/source/blender/blenkernel/intern/customdata.cc b/source/blender/blenkernel/intern/customdata.cc index 2a08ba729dd..24cd552dfb6 100644 --- a/source/blender/blenkernel/intern/customdata.cc +++ b/source/blender/blenkernel/intern/customdata.cc @@ -17,6 +17,7 @@ #include "DNA_customdata_types.h" #include "DNA_meshdata_types.h" +#include "BLI_array.hh" #include "BLI_asan.h" #include "BLI_bitmap.h" #include "BLI_color.hh" @@ -35,6 +36,7 @@ #include "BLI_string_utf8.h" #include "BLI_string_utils.h" #include "BLI_utildefines.h" +#include "BLI_vector.hh" #ifndef NDEBUG # include "BLI_dynstr.h" @@ -61,6 +63,7 @@ /* only for customdata_data_transfer_interp_normal_normals */ #include "data_transfer_intern.h" +using blender::Array; using blender::float2; using blender::ImplicitSharingInfo; using blender::IndexRange; @@ -2770,9 +2773,11 @@ static void customData_update_offsets(CustomData *data) int offset = 0; /* Sort by alignment. */ - int aligns[] = {16, 8, 4, 2, 1}; - BLI_bitmap *donemap = BLI_BITMAP_NEW_ALLOCA(data->totlayer); - int alignment = 1; + int aligns[] = {8, 4, 2, 1}; + Array donemap(data->totlayer, false); + Array alignmap(data->totlayer); + + int max_alignment = 1; /* Do large structs first. */ for (int j = 0; j < data->totlayer; j++) { @@ -2781,26 +2786,38 @@ static void customData_update_offsets(CustomData *data) typeInfo = layerType_getInfo(eCustomDataType(layer->type)); int size = (int)typeInfo->size; + int alignment; + /* Float vectors get 4-byte alignment. */ - if (ELEM(layer->type, CD_PROP_COLOR, CD_PROP_FLOAT2, CD_PROP_FLOAT3, CD_PROP_INT32_2D)) { - alignment = max_ii(alignment, 4); + if (ELEM(layer->type, + CD_PROP_COLOR, + CD_PROP_FLOAT2, + CD_PROP_FLOAT3, + CD_PROP_INT32_2D, + CD_CUSTOMLOOPNORMAL, + CD_NORMAL)) + { + alignment = 4; } else if (size > 4) { - alignment = max_ii(alignment, 8); + alignment = 8; } else if (size > 2) { - alignment = max_ii(alignment, 4); + alignment = 4; } else if (size > 1) { - alignment = max_ii(alignment, 2); + alignment = 2; } else { - alignment = max_ii(alignment, 1); + alignment = 1; } + max_alignment = max_ii(max_alignment, alignment); + alignmap[j] = alignment; + /* Detect large structures */ if (size > 8) { - BLI_BITMAP_SET(donemap, j, true); + donemap[j] = true; /* Align to 8-byte boundary. */ if (size & 7) { @@ -2824,14 +2841,14 @@ static void customData_update_offsets(CustomData *data) for (int j = 0; j < data->totlayer; j++) { CustomDataLayer *layer = data->layers + j; - if (BLI_BITMAP_TEST(donemap, j)) { + if (donemap[j]) { continue; } typeInfo = layerType_getInfo(eCustomDataType(layer->type)); int size = (int)typeInfo->size; - if (i < ARRAY_SIZE(aligns) && (size % aligns[i]) != 0) { + if (i < ARRAY_SIZE(aligns) && aligns[i] != alignmap[j]) { continue; } @@ -2839,7 +2856,7 @@ static void customData_update_offsets(CustomData *data) offset += BM_ASAN_PAD; #endif - BLI_BITMAP_SET(donemap, j, true); + donemap[j] = true; int align2 = aligns[i] - (offset % aligns[i]); if (align2 != aligns[i]) { @@ -2855,8 +2872,8 @@ static void customData_update_offsets(CustomData *data) } } - if (offset % alignment != 0) { - offset += alignment - (offset % alignment); + if (offset % max_alignment != 0) { + offset += max_alignment - (offset % max_alignment); } data->totsize = offset; diff --git a/source/blender/blenkernel/intern/dyntopo.cc b/source/blender/blenkernel/intern/dyntopo.cc index b00ea6bae2c..47851d88089 100644 --- a/source/blender/blenkernel/intern/dyntopo.cc +++ b/source/blender/blenkernel/intern/dyntopo.cc @@ -82,7 +82,8 @@ static void edge_queue_create_local(struct EdgeQueueContext *eq_ctx, const bool use_projected, PBVHTopologyUpdateMode local_mode); -BLI_INLINE void surface_smooth_v_safe(SculptSession *ss, PBVH *pbvh, BMVert *v, float fac) +static void surface_smooth_v_safe( + SculptSession *ss, PBVH *pbvh, BMVert *v, float fac, bool reproject_cdata) { float co[3]; float origco[3], origco1[3]; @@ -168,6 +169,13 @@ BLI_INLINE void surface_smooth_v_safe(SculptSession *ss, PBVH *pbvh, BMVert *v, return; } + float startco[3]; + float startno[3]; + if (reproject_cdata) { + copy_v3_v3(startco, v->co); + copy_v3_v3(startno, v->no); + } + mul_v3_fl(co, 1.0f / tot); mul_v3_fl(origco, 1.0f / tot); @@ -179,6 +187,11 @@ BLI_INLINE void surface_smooth_v_safe(SculptSession *ss, PBVH *pbvh, BMVert *v, atomic_cas_float(&v->co[1], y, ny); atomic_cas_float(&v->co[2], z, nz); + /* Note: threading is disabled if reproject_cdata is on. */ + if (reproject_cdata) { + BKE_sculpt_reproject_cdata(ss, vertex, startco, startno); + } + float *start_origco = blender::bke::paint::vertex_attr_ptr(vertex, ss->attrs.orig_co); /* Conflicts here should be pretty rare. */ @@ -477,12 +490,8 @@ static bool edge_queue_vert_in_circle(const EdgeQueueContext *eq_ctx, BMVert *v) return len_squared_v3v3(eq_ctx->center_proj, c) <= eq_ctx->radius_squared; } -static void long_edge_queue_edge_add_recursive(EdgeQueueContext *eq_ctx, - BMLoop *l_edge, - BMLoop *l_end, - const float len_sq, - float limit_len, - int depth) +static void long_edge_queue_edge_add_recursive( + EdgeQueueContext *eq_ctx, BMLoop *l_edge, const float len_sq, float limit_len, int depth) { BLI_assert(len_sq > square_f(limit_len)); @@ -517,7 +526,7 @@ static void long_edge_queue_edge_add_recursive(EdgeQueueContext *eq_ctx, } long_edge_queue_edge_add_recursive( - eq_ctx, l_adjacent[i]->radial_next, l_adjacent[i], len_sq_other, limit_len, depth + 1); + eq_ctx, l_adjacent[i]->radial_next, len_sq_other, limit_len, depth + 1); } } while ((l_iter = l_iter->radial_next) != l_edge); } @@ -627,6 +636,9 @@ static void unified_edge_queue_task_cb(void *__restrict userdata, const char facetag = BM_ELEM_TAG_ALT; + /* Only do reprojection if UVs exist. */ + bool reproject_cdata = eq_ctx->reproject_cdata; + /* * We care more about convergence to accurate results * then accuracy in any individual runs. profiling @@ -702,7 +714,8 @@ static void unified_edge_queue_task_cb(void *__restrict userdata, tdata->pbvh, l_iter->v, eq_ctx->surface_smooth_fac * - eq_ctx->mask_cb(sv, eq_ctx->mask_cb_data)); + eq_ctx->mask_cb(sv, eq_ctx->mask_cb_data), + reproject_cdata); } float len_sq = calc_edge_length(eq_ctx, l_iter->e->v1, l_iter->e->v2); @@ -1253,6 +1266,8 @@ static void unified_edge_queue_create(EdgeQueueContext *eq_ctx, TaskParallelSettings settings; BLI_parallel_range_settings_defaults(&settings); + settings.use_threading = !eq_ctx->reproject_cdata; + #ifdef DYNTOPO_NO_THREADING settings.use_threading = false; #endif @@ -1993,13 +2008,8 @@ extern "C" bool BKE_pbvh_bmesh_update_topology(SculptSession *ss, void *mask_cb_data, int custom_max_steps, bool disable_surface_relax, - bool is_snake_hook) + bool /*is_snake_hook*/) { - /* Disable surface smooth if uv layers are present, to avoid expensive reprojection operation. */ - if (!is_snake_hook && CustomData_has_layer(&pbvh->header.bm->ldata, CD_PROP_FLOAT2)) { - disable_surface_relax = true; - } - /* Push a subentry. */ BM_log_entry_add_ex(pbvh->header.bm, pbvh->bm_log, true); @@ -2049,22 +2059,6 @@ extern "C" bool BKE_pbvh_bmesh_update_topology(SculptSession *ss, } #endif - float safe_smooth; - - if (disable_surface_relax) { - safe_smooth = 0.0f; - } - else if ((mode & PBVH_Subdivide) && (!(mode & PBVH_Collapse) || (mode & PBVH_LocalCollapse))) { - safe_smooth = DYNTOPO_SAFE_SMOOTH_SUBD_ONLY_FAC; - } - else { - safe_smooth = DYNTOPO_SAFE_SMOOTH_FAC; - } - - // XXX - disable_surface_relax = false; - safe_smooth = DYNTOPO_SAFE_SMOOTH_FAC; - EdgeQueueContext eq_ctx = {}; eq_ctx.ss = ss; @@ -2082,8 +2076,10 @@ extern "C" bool BKE_pbvh_bmesh_update_topology(SculptSession *ss, eq_ctx.min_elen = 1e17; eq_ctx.totedge = 0.0f; eq_ctx.local_mode = false; - eq_ctx.surface_smooth_fac = safe_smooth; + eq_ctx.surface_smooth_fac = disable_surface_relax ? 0.0f : DYNTOPO_SAFE_SMOOTH_FAC; eq_ctx.mode = mode; + eq_ctx.reproject_cdata = CustomData_has_layer(&pbvh->header.bm->ldata, CD_PROP_FLOAT2) && + !ss->ignore_uvs; #ifdef DYNTOPO_USE_MINMAX_HEAP // eq_ctx.used_verts = BLI_table_gset_new(__func__); @@ -2330,7 +2326,8 @@ extern "C" bool BKE_pbvh_bmesh_update_topology(SculptSession *ss, } #endif - printf("subd: %d, cold: %d, ratio: %.3f\n", + printf("%s: subd: %d, cold: %d, ratio: %.3f\n", + __func__, count_subd, count_cold, count_cold > 0 ? float(count_subd) / float(count_cold) : 0.0f); @@ -2733,7 +2730,7 @@ static void pbvh_split_edges(EdgeQueueContext *eq_ctx, edge_queue_insert_val34_vert(eq_ctx, newv); -#ifdef DYNTOPO_USE_MINMAX_HEAP +#if 0 const float elimit = eq_ctx->limit_len_max; if (0 && e->l) { @@ -2741,19 +2738,13 @@ static void pbvh_split_edges(EdgeQueueContext *eq_ctx, newe->head.hflag &= ~BM_ELEM_TAG; long_edge_queue_edge_add_recursive( - eq_ctx, e->l->radial_next, e->l, len_squared_v3v3(e->v1->co, e->v2->co), elimit, 0); + eq_ctx, e->l->radial_next, len_squared_v3v3(e->v1->co, e->v2->co), elimit, 0); long_edge_queue_edge_add_recursive(eq_ctx, newe->l->radial_next, - newe->l, len_squared_v3v3(newe->v1->co, newe->v2->co), elimit, 0); } - - // edge_queue_insert_unified(eq_ctx, e); - // edge_queue_insert_unified(eq_ctx, newe); - - // BLI_table_gset_add(eq_ctx->used_verts, newv); #endif PBVH_CHECK_NAN(newv->co); @@ -2991,7 +2982,7 @@ static void pbvh_split_edges(EdgeQueueContext *eq_ctx, if (w / fac > eq_ctx->limit_len_max) { - long_edge_queue_edge_add_recursive(eq_ctx, rl, rl->next, w, eq_ctx->limit_len_max, 0); + long_edge_queue_edge_add_recursive(eq_ctx, rl, w, eq_ctx->limit_len_max, 0); } #endif @@ -3267,7 +3258,7 @@ void BKE_dyntopo_remesh(DynTopoState *ds, mask_cb_nop, nullptr, ds->pbvh->header.bm->totedge * 5, - true, + false, false); BKE_pbvh_update_normals(ds->pbvh, nullptr); @@ -3399,3 +3390,313 @@ void BKE_pbvh_bmesh_add_face(PBVH *pbvh, struct BMFace *f, bool log_face, bool f bm_logstack_pop(); } + +void BKE_sculpt_reproject_cdata(SculptSession *ss, + PBVHVertRef vertex, + float startco[3], + float startno[3]) +{ + BMVert *v = (BMVert *)vertex.i; + BMEdge *e; + + if (!v->e) { + return; + } + + CustomData *ldata = &ss->bm->ldata; + + int totuv = 0; + CustomDataLayer *uvlayer = NULL; + + /* Optimized substitute for CustomData_number_of_layers. */ + if (ldata->typemap[CD_PROP_FLOAT2] != -1) { + for (int i = ldata->typemap[CD_PROP_FLOAT2]; + i < ldata->totlayer && ldata->layers[i].type == CD_PROP_FLOAT2; + i++) + { + totuv++; + } + + uvlayer = ldata->layers + ldata->typemap[CD_PROP_FLOAT2]; + } + + int tag = BM_ELEM_TAG_ALT; + + float origin[3]; + float ray[3]; + + copy_v3_v3(origin, v->co); + copy_v3_v3(ray, v->no); + negate_v3(ray); + + struct IsectRayPrecalc precalc; + isect_ray_tri_watertight_v3_precalc(&precalc, ray); + + float *lastuvs = (float *)BLI_array_alloca(lastuvs, totuv * 2); + bool *snapuvs = (bool *)BLI_array_alloca(snapuvs, totuv); + + e = v->e; + + /* first clear some flags */ + do { + e->head.api_flag &= ~tag; + + if (!e->l) { + continue; + } + + BMLoop *l = e->l; + do { + l->head.hflag &= ~tag; + l->next->head.hflag &= ~tag; + l->prev->head.hflag &= ~tag; + } while ((l = l->radial_next) != e->l); + } while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e); + + Vector ls; + + bool first = true; + bool bad = false; + + for (int i = 0; i < totuv; i++) { + snapuvs[i] = true; + } + + do { + BMLoop *l = e->l; + + if (!l) { + continue; + } +#if 0 + bool bound = l == l->radial_next; + + // check for faceset boundaries + bound = bound || (BM_ELEM_CD_GET_INT(l->f,ss->cd_faceset_offset) != + BM_ELEM_CD_GET_INT(l->radial_next->f,ss->cd_faceset_offset)); + + // check for seam and sharp edges + bound = bound || (e->head.hflag & BM_ELEM_SEAM) || !(e->head.hflag & BM_ELEM_SMOOTH); + + if (bound) { + continue; + } +#endif + do { + BMLoop *l2 = l->v != v ? l->next : l; + + if (l2->head.hflag & tag) { + continue; + } + + l2->head.hflag |= tag; + ls.append(l2); + + for (int i = 0; i < totuv; i++) { + const int cd_uv = uvlayer[i].offset; + float *luv = BM_ELEM_CD_PTR(l2, cd_uv); + + // check that we are not part of a uv seam + if (!first) { + const float dx = lastuvs[i * 2] - luv[0]; + const float dy = lastuvs[i * 2 + 1] - luv[1]; + const float eps = 0.00001f; + + if (dx * dx + dy * dy > eps) { + bad = true; + snapuvs[i] = false; + } + } + + lastuvs[i * 2] = luv[0]; + lastuvs[i * 2 + 1] = luv[1]; + } + + first = false; + + if (bad) { + break; + } + } while ((l = l->radial_next) != e->l); + + if (bad) { + break; + } + } while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e); + + if (bad || !ls.size()) { + return; + } + + int totloop = ls.size(); + + /* original (l->prev, l, l->next) projections for each loop ('l' remains unchanged) */ + + char *_blocks = (char *)alloca(ldata->totsize * totloop); + void **blocks = (void **)BLI_array_alloca(blocks, totloop); + + for (int i = 0; i < totloop; i++, _blocks += ldata->totsize) { + blocks[i] = (void *)_blocks; + } + + float vco[3], vno[3]; + + copy_v3_v3(vco, v->co); + copy_v3_v3(vno, v->no); + + BMFace _fakef, *fakef = &_fakef; + +#if 0 + BMFace *projf = NULL; + // find face vertex projects into + for (int i = 0; i < totloop; i++) { + BMLoop *l = ls[i]; + + copy_v3_v3(ray,l->f->no); + negate_v3(ray); + + float t,uv[2]; + + //* + bool hit = isect_ray_tri_v3(origin,ray,l->prev->v->co,origco,l->next->v->co,&t,uv); + if (hit) { + projf = l->f; + break; + } //*/ + } + + if (!projf) { + return; + } +#endif + + /* Build fake face with original coordinates. */ + for (int i = 0; i < totloop; i++) { + BMLoop *l = ls[i]; + float no[3] = {0.0f, 0.0f, 0.0f}; + + BMLoop *fakels = (BMLoop *)BLI_array_alloca(fakels, l->f->len); + BMVert *fakevs = (BMVert *)BLI_array_alloca(fakevs, l->f->len); + BMLoop *l2 = l->f->l_first; + BMLoop *fakel = fakels; + BMVert *fakev = fakevs; + int j = 0; + + do { + *fakel = *l2; + fakel->next = fakels + ((j + 1) % l->f->len); + fakel->prev = fakels + ((j + l->f->len - 1) % l->f->len); + + *fakev = *l2->v; + fakel->v = fakev; + + /* Make sure original coordinate is up to date. */ + blender::bke::paint::get_original_vertex(ss, vertex, nullptr, nullptr, nullptr, nullptr); + + if (l2->v == v) { + copy_v3_v3(fakev->co, startco); + copy_v3_v3(fakev->no, startno); + add_v3_v3(no, startno); + } + else { + add_v3_v3(no, l2->v->no); + } + + fakel++; + fakev++; + j++; + } while ((l2 = l2->next) != l->f->l_first); + + *fakef = *l->f; + fakef->l_first = fakels; + + normalize_v3(no); + + if (len_squared_v3(no) > 0.0f) { + copy_v3_v3(fakef->no, no); + } + else if (fakef->len == 4) { + normal_quad_v3( + fakef->no, l->v->co, l->next->v->co, l->next->next->v->co, l->next->next->next->v->co); + } + else { + normal_tri_v3(fakef->no, l->v->co, l->next->v->co, l->next->next->v->co); + } + + /* Interpolate. */ + BMLoop _interpl, *interpl = &_interpl; + + uint8_t *flag = BM_ELEM_CD_PTR(v, ss->attrs.flags->bmesh_cd_offset); + uint8_t *stroke_id = BM_ELEM_CD_PTR(v, ss->attrs.stroke_id->bmesh_cd_offset); + + int flag_saved = *flag; + int stroke_id_saved = *stroke_id; + + *interpl = *l; + interpl->head.data = blocks[i]; + + BM_loop_interp_from_face(ss->bm, interpl, fakef, false, false); + + *stroke_id = stroke_id_saved; + *flag = flag_saved; + + CustomData_bmesh_copy_data(&ss->bm->ldata, &ss->bm->ldata, interpl->head.data, &l->head.data); + } + + int *tots = (int *)BLI_array_alloca(tots, totuv); + + for (int i = 0; i < totuv; i++) { + lastuvs[i * 2] = lastuvs[i * 2 + 1] = 0.0f; + tots[i] = 0; + } + + // re-snap uvs + v = (BMVert *)vertex.i; + + e = v->e; + do { + if (!e->l) { + continue; + } + + BMLoop *l_iter = e->l; + do { + BMLoop *l = l_iter->v != v ? l_iter->next : l_iter; + + for (int i = 0; i < totuv; i++) { + const int cd_uv = uvlayer[i].offset; + float *luv = BM_ELEM_CD_PTR(l, cd_uv); + + add_v2_v2(lastuvs + i * 2, luv); + tots[i]++; + } + } while ((l_iter = l_iter->radial_next) != e->l); + } while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e); + + for (int i = 0; i < totuv; i++) { + if (tots[i]) { + mul_v2_fl(lastuvs + i * 2, 1.0f / (float)tots[i]); + } + } + + e = v->e; + do { + if (!e->l) { + continue; + } + + BMLoop *l_iter = e->l; + do { + BMLoop *l = l_iter->v != v ? l_iter->next : l_iter; + + for (int i = 0; i < totuv; i++) { + const int cd_uv = uvlayer[i].offset; + float *luv = BM_ELEM_CD_PTR(l, cd_uv); + + if (snapuvs[i]) { + copy_v2_v2(luv, lastuvs + i * 2); + } + } + } while ((l_iter = l_iter->radial_next) != e->l); + } while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e); +} diff --git a/source/blender/blenkernel/intern/dyntopo_intern.hh b/source/blender/blenkernel/intern/dyntopo_intern.hh index 6d8e4fbad2b..4b1cb0f7718 100644 --- a/source/blender/blenkernel/intern/dyntopo_intern.hh +++ b/source/blender/blenkernel/intern/dyntopo_intern.hh @@ -118,8 +118,6 @@ inline bool bm_elem_is_free(BMElem *elem, int htype) to improve convergence of remesher */ #define DYNTOPO_SAFE_SMOOTH_FAC 0.05f -#define DYNTOPO_SAFE_SMOOTH_SUBD_ONLY_FAC 0.075f - #ifdef USE_EDGEQUEUE_EVEN_SUBDIV # include "BKE_global.h" #endif @@ -242,6 +240,7 @@ struct EdgeQueueContext { bool (*edge_queue_vert_in_range)(const struct EdgeQueueContext *q, BMVert *v); PBVHTopologyUpdateMode mode; + bool reproject_cdata; }; bool destroy_nonmanifold_fins(PBVH *pbvh, BMEdge *e_root); diff --git a/source/blender/blenkernel/intern/pbvh.cc b/source/blender/blenkernel/intern/pbvh.cc index 2319d34bb3c..f8cefeb8239 100644 --- a/source/blender/blenkernel/intern/pbvh.cc +++ b/source/blender/blenkernel/intern/pbvh.cc @@ -1751,6 +1751,13 @@ bool BKE_pbvh_get_color_layer(const PBVH *pbvh, else if (domain == ATTR_DOMAIN_CORNER) { data = &pbvh->header.bm->ldata; } + else { + *r_layer = nullptr; + *r_attr = ATTR_DOMAIN_POINT; + + BLI_assert_unreachable(); + return false; + } int layer_i = CustomData_get_named_layer_index( data, eCustomDataType(layer->type), layer->name); @@ -1788,17 +1795,6 @@ static void pbvh_update_draw_buffer_cb(void *__restrict userdata, BKE_pbvh_get_color_layer(pbvh, me, &vcol_layer, &vcol_domain); - CustomData *vdata, *ldata; - - if (pbvh->header.bm) { - vdata = &pbvh->header.bm->vdata; - ldata = &pbvh->header.bm->ldata; - } - else { - vdata = pbvh->vdata ? pbvh->vdata : &me->vdata; - ldata = pbvh->ldata ? pbvh->ldata : &me->ldata; - } - if (node->flag & PBVH_RebuildDrawBuffers) { PBVH_GPU_Args args; pbvh_draw_args_init(pbvh, &args, node); @@ -3882,7 +3878,6 @@ void BKE_pbvh_check_tri_areas(PBVH *pbvh, PBVHNode *node) switch (BKE_pbvh_type(pbvh)) { case PBVH_FACES: { for (int i = 0; i < (int)node->totprim; i++) { - const MLoopTri *lt = &pbvh->looptri[node->prim_indices[i]]; const int poly = pbvh->looptri_polys[node->prim_indices[i]]; if (pbvh->hide_poly && pbvh->hide_poly[poly]) { diff --git a/source/blender/bmesh/intern/bmesh_interp.c b/source/blender/bmesh/intern/bmesh_interp.c index b678c787d85..8402c5332c2 100644 --- a/source/blender/bmesh/intern/bmesh_interp.c +++ b/source/blender/bmesh/intern/bmesh_interp.c @@ -740,6 +740,30 @@ void BM_loop_interp_from_face( /* interpolate */ interp_weights_poly_v2(w, cos_2d, f_src->len, co); + + /* Clamp negative weights to zero. Prevents numerical + * instability when interpolating UVs. + */ + float totw = 0.0f; + for (int i = 0; i < f_src->len; i++) { + if (w[i] < 0.0f) { + w[i] = 0.0f; + } + + totw += w[i]; + } + + if (fabsf(totw - 1.0f) > FLT_EPSILON * 100) { + for (int i = 0; i < f_src->len; i++) { + w[i] /= totw; + } + } + else if (totw == 0.0f) { /* Use uniform weights in this case.*/ + for (int i = 0; i < f_src->len; i++) { + w[i] = 1.0f / (float)f_src->len; + } + } + CustomData_bmesh_interp(&bm->ldata, blocks, w, NULL, f_src->len, l_dst->head.data); if (do_vertex) { CustomData_bmesh_interp(&bm->vdata, vblocks, w, NULL, f_src->len, l_dst->v->head.data); diff --git a/source/blender/bmesh/intern/bmesh_mesh.cc b/source/blender/bmesh/intern/bmesh_mesh.cc index 394001de96d..5f2ec51f02d 100644 --- a/source/blender/bmesh/intern/bmesh_mesh.cc +++ b/source/blender/bmesh/intern/bmesh_mesh.cc @@ -202,7 +202,7 @@ BMesh *BM_mesh_create(const BMAllocTemplate *allocsize, const struct BMeshCreate return bm; } -ATTR_NO_OPT static void customdata_pool_destroy(BMesh * /*bm*/, CustomData *cdata) +static void customdata_pool_destroy(BMesh * /*bm*/, CustomData *cdata) { if (cdata->pool) { BLI_mempool_destroy(cdata->pool); diff --git a/source/blender/editors/sculpt_paint/sculpt.cc b/source/blender/editors/sculpt_paint/sculpt.cc index c44ca6501c4..182513291c3 100644 --- a/source/blender/editors/sculpt_paint/sculpt.cc +++ b/source/blender/editors/sculpt_paint/sculpt.cc @@ -984,37 +984,6 @@ bool SCULPT_vertex_has_unique_face_set(const SculptSession *ss, PBVHVertRef vert return !SCULPT_vertex_is_boundary(ss, vertex, SCULPT_BOUNDARY_FACE_SET); } -/** - * Checks if the face sets of the adjacent faces to the edge between \a v1 and \a v2 - * in the base mesh are equal. - */ -static bool sculpt_check_unique_face_set_for_edge_in_base_mesh(SculptSession *ss, int v1, int v2) -{ - const MeshElemMap *vert_map = &ss->pmap[v1]; - int p1 = -1, p2 = -1; - for (int i = 0; i < vert_map->count; i++) { - const int poly_i = vert_map->indices[i]; - for (const int corner : ss->polys[poly_i]) { - if (ss->corner_verts[corner] == v2) { - if (p1 == -1) { - p1 = vert_map->indices[i]; - break; - } - - if (p2 == -1) { - p2 = vert_map->indices[i]; - break; - } - } - } - } - - if (p1 != -1 && p2 != -1) { - return abs(ss->face_sets[p1]) == (ss->face_sets[p2]); - } - return true; -} - int SCULPT_face_set_next_available_get(SculptSession *ss) { if (ss->cd_faceset_offset == -1) { @@ -4075,7 +4044,7 @@ static void sculpt_topology_update(Sculpt *sd, true, mask_cb, mask_cb_data, - /*XXX implement me: disable dyntopo smooth setting*/ false, + false, brush->sculpt_tool == SCULPT_TOOL_SNAKE_HOOK); SCULPT_dyntopo_automasking_end(mask_cb_data); diff --git a/source/blender/editors/sculpt_paint/sculpt_brush_types.cc b/source/blender/editors/sculpt_paint/sculpt_brush_types.cc index b4de901cf08..d2735e6d4cd 100644 --- a/source/blender/editors/sculpt_paint/sculpt_brush_types.cc +++ b/source/blender/editors/sculpt_paint/sculpt_brush_types.cc @@ -2920,7 +2920,7 @@ static void do_topology_rake_bmesh_task_cb_ex(void *__restrict userdata, SCULPT_clip(sd, ss, vd.co, val); if (do_reproject) { - SCULPT_reproject_cdata(ss, vd.vertex, oldco, oldno); + BKE_sculpt_reproject_cdata(ss, vd.vertex, oldco, oldno); } if (vd.is_mesh) { @@ -2940,6 +2940,8 @@ void SCULPT_bmesh_topology_rake(Sculpt *sd, Object *ob, Span nodes, SculptSession *ss = ob->sculpt; const float strength = clamp_f(bstrength, 0.0f, 1.0f); + SCULPT_smooth_undo_push(ob, nodes); + /* Interactions increase both strength and quality. */ const int iterations = 3; diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.hh b/source/blender/editors/sculpt_paint/sculpt_intern.hh index d6cf39115bb..3730ed8883d 100644 --- a/source/blender/editors/sculpt_paint/sculpt_intern.hh +++ b/source/blender/editors/sculpt_paint/sculpt_intern.hh @@ -1905,16 +1905,13 @@ void SCULPT_neighbor_coords_average_interior(SculptSession *ss, BLI_INLINE bool SCULPT_need_reproject(const SculptSession *ss) { - return !ss->ignore_uvs && ss->bm && CustomData_has_layer(&ss->bm->ldata, CD_PROP_FLOAT2); + return !ss->ignore_uvs && ss->bm; // && CustomData_has_layer(&ss->bm->ldata, CD_PROP_FLOAT2); } int SCULPT_vertex_island_get(SculptSession *ss, PBVHVertRef vertex); /** \} */ -void SCULPT_reproject_cdata(SculptSession *ss, - PBVHVertRef vertex, - float origco[3], - float origno[3]); +void SCULPT_smooth_undo_push(Object *ob, Span nodes); void SCULPT_smooth( Sculpt *sd, Object *ob, Span nodes, float bstrength, const bool smooth_mask); diff --git a/source/blender/editors/sculpt_paint/sculpt_smooth.cc b/source/blender/editors/sculpt_paint/sculpt_smooth.cc index 8cfe473f888..d90c9edf27a 100644 --- a/source/blender/editors/sculpt_paint/sculpt_smooth.cc +++ b/source/blender/editors/sculpt_paint/sculpt_smooth.cc @@ -531,6 +531,8 @@ static void SCULPT_enhance_details_brush(Sculpt *sd, Object *ob, Spansculpt; Brush *brush = BKE_paint_brush(&sd->paint); + SCULPT_smooth_undo_push(ob, nodes); + SCULPT_vertex_random_access_ensure(ss); SCULPT_boundary_info_ensure(ob); @@ -648,7 +650,7 @@ static void do_smooth_brush_task_cb_ex(void *__restrict userdata, SCULPT_clip(sd, ss, vd.co, val); if (do_reproject) { - SCULPT_reproject_cdata(ss, vd.vertex, oldco, oldno); + BKE_sculpt_reproject_cdata(ss, vd.vertex, oldco, oldno); } if (vd.is_mesh) { @@ -663,6 +665,24 @@ static void do_smooth_brush_task_cb_ex(void *__restrict userdata, } } +void SCULPT_smooth_undo_push(Object *ob, Span nodes) +{ + SculptSession *ss = ob->sculpt; + + if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH && SCULPT_need_reproject(ss)) { + BM_log_entry_add_ex(ss->bm, ss->bm_log, true); + + for (PBVHNode *node : nodes) { + PBVHVertexIter vd; + PBVHFaceIter fd; + BKE_pbvh_face_iter_begin (ss->pbvh, node, fd) { + BM_log_face_modified(ss->bm, ss->bm_log, reinterpret_cast(fd.face.i)); + } + BKE_pbvh_face_iter_end(fd); + } + } +} + void SCULPT_smooth( Sculpt *sd, Object *ob, Span nodes, float bstrength, const bool smooth_mask) { @@ -675,6 +695,8 @@ void SCULPT_smooth( int iteration, count; float last; + SCULPT_smooth_undo_push(ob, nodes); + /* PBVH_FACES needs ss->epmap. */ if (BKE_pbvh_type(ss->pbvh) == PBVH_FACES && !ss->epmap) { Mesh *mesh = static_cast(ob->data); @@ -902,6 +924,8 @@ void SCULPT_do_surface_smooth_brush(Sculpt *sd, Object *ob, Span nod { Brush *brush = BKE_paint_brush(&sd->paint); + SCULPT_smooth_undo_push(ob, nodes); + /* Threaded loop over nodes. */ SculptThreadedTaskData data{}; data.sd = sd; @@ -918,305 +942,3 @@ void SCULPT_do_surface_smooth_brush(Sculpt *sd, Object *ob, Span nod 0, nodes.size(), &data, SCULPT_do_surface_smooth_brush_displace_task_cb_ex, &settings); } } - -void SCULPT_reproject_cdata(SculptSession *ss, - PBVHVertRef vertex, - float origco[3], - float origno[3]) -{ - BMVert *v = (BMVert *)vertex.i; - - if (!ss->bm || !v->e) { - return; - } - - // int totuv = CustomData_number_of_layers(&ss->bm->ldata, CD_PROP_FLOAT2); - CustomData *ldata = &ss->bm->ldata; - - int totuv = 0; - CustomDataLayer *uvlayer = NULL; - - if (ldata->typemap[CD_PROP_FLOAT2] != -1) { - for (int i = ldata->typemap[CD_PROP_FLOAT2]; - i < ldata->totlayer && ldata->layers[i].type == CD_PROP_FLOAT2; - i++) - { - totuv++; - } - - uvlayer = ldata->layers + ldata->typemap[CD_PROP_FLOAT2]; - } - - BMEdge *e; - int tag = BM_ELEM_TAG_ALT; - - float origin[3]; - float ray[3]; - - copy_v3_v3(origin, v->co); - copy_v3_v3(ray, v->no); - negate_v3(ray); - - struct IsectRayPrecalc precalc; - isect_ray_tri_watertight_v3_precalc(&precalc, ray); - - float *lastuvs = (float *)BLI_array_alloca(lastuvs, totuv * 2); - bool *snapuvs = (bool *)BLI_array_alloca(snapuvs, totuv); - - e = v->e; - - /* first clear some flags */ - do { - e->head.api_flag &= ~tag; - - if (!e->l) { - continue; - } - - BMLoop *l = e->l; - do { - l->head.hflag &= ~tag; - l->next->head.hflag &= ~tag; - l->prev->head.hflag &= ~tag; - } while ((l = l->radial_next) != e->l); - } while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e); - - Vector ls; - - bool first = true; - bool bad = false; - - for (int i = 0; i < totuv; i++) { - snapuvs[i] = true; //!(mv->flag & SCULPTVERT_UV_BOUNDARY); - } - - do { - BMLoop *l = e->l; - - if (!l) { - continue; - } -#if 0 - bool bound = l == l->radial_next; - - // check for faceset boundaries - bound = bound || (BM_ELEM_CD_GET_INT(l->f,ss->cd_faceset_offset) != - BM_ELEM_CD_GET_INT(l->radial_next->f,ss->cd_faceset_offset)); - - // check for seam and sharp edges - bound = bound || (e->head.hflag & BM_ELEM_SEAM) || !(e->head.hflag & BM_ELEM_SMOOTH); - - if (bound) { - continue; - } -#endif - do { - BMLoop *l2 = l->v != v ? l->next : l; - - if (l2->head.hflag & tag) { - continue; - } - - l2->head.hflag |= tag; - ls.append(l2); - - for (int i = 0; i < totuv; i++) { - const int cd_uv = uvlayer[i].offset; - float *luv = BM_ELEM_CD_PTR(l2, cd_uv); - - // check that we are not part of a uv seam - if (!first) { - const float dx = lastuvs[i * 2] - luv[0]; - const float dy = lastuvs[i * 2 + 1] - luv[1]; - const float eps = 0.00001f; - - if (dx * dx + dy * dy > eps) { - bad = true; - snapuvs[i] = false; - } - } - - lastuvs[i * 2] = luv[0]; - lastuvs[i * 2 + 1] = luv[1]; - } - - first = false; - - if (bad) { - break; - } - } while ((l = l->radial_next) != e->l); - - if (bad) { - break; - } - } while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e); - - if (bad || !ls.size()) { - return; - } - - int totloop = ls.size(); - - const float *v_proj_axis = v->no; - float v_proj[3][3]; - - float *old_origco = BM_ELEM_CD_PTR(v, ss->attrs.orig_co->bmesh_cd_offset); - project_plane_normalized_v3_v3v3(v_proj[1], old_origco, v_proj_axis); - - /* original (l->prev, l, l->next) projections for each loop ('l' remains unchanged) */ - - char *_blocks = (char *)alloca(ldata->totsize * totloop); - void **blocks = (void **)BLI_array_alloca(blocks, totloop); - - for (int i = 0; i < totloop; i++, _blocks += ldata->totsize) { - blocks[i] = (void *)_blocks; - } - - float vco[3], vno[3]; - - copy_v3_v3(vco, v->co); - copy_v3_v3(vno, v->no); - - BMFace _fakef, *fakef = &_fakef; - -#if 0 - BMFace *projf = NULL; - // find face vertex projects into - for (int i = 0; i < totloop; i++) { - BMLoop *l = ls[i]; - - copy_v3_v3(ray,l->f->no); - negate_v3(ray); - - float t,uv[2]; - - //* - bool hit = isect_ray_tri_v3(origin,ray,l->prev->v->co,origco,l->next->v->co,&t,uv); - if (hit) { - projf = l->f; - break; - } //*/ - } - - if (!projf) { - return; - } -#endif - - /* Build fake face with original coordinates. */ - for (int i = 0; i < totloop; i++) { - BMLoop *l = ls[i]; - float no[3] = {0.0f, 0.0f, 0.0f}; - - BMLoop *fakels = (BMLoop *)BLI_array_alloca(fakels, l->f->len); - BMVert *fakevs = (BMVert *)BLI_array_alloca(fakevs, l->f->len); - BMLoop *l2 = l->f->l_first; - BMLoop *fakel = fakels; - BMVert *fakev = fakevs; - int j = 0; - - do { - *fakel = *l2; - fakel->next = fakels + ((j + 1) % l->f->len); - fakel->prev = fakels + ((j + l->f->len - 1) % l->f->len); - - *fakev = *l2->v; - fakel->v = fakev; - - SCULPT_vertex_check_origdata(ss, BKE_pbvh_make_vref(intptr_t(l2->v))); - - if (l2->v == v) { - copy_v3_v3(fakev->co, origco); - copy_v3_v3(fakev->no, origno); - add_v3_v3(no, origno); - } - else { - add_v3_v3(no, l2->v->no); - } - - fakel++; - fakev++; - j++; - } while ((l2 = l2->next) != l->f->l_first); - - *fakef = *l->f; - fakef->l_first = fakels; - - /* Interpolate. */ - BMLoop _interpl, *interpl = &_interpl; - - uint8_t *flag = BM_ELEM_CD_PTR(v, ss->attrs.flags->bmesh_cd_offset); - uint8_t *stroke_id = BM_ELEM_CD_PTR(v, ss->attrs.stroke_id->bmesh_cd_offset); - - int flag_saved = *flag; - int stroke_id_saved = *stroke_id; - - *interpl = *l; - interpl->head.data = blocks[i]; - - BM_loop_interp_from_face(ss->bm, interpl, fakef, false, false); - - *stroke_id = stroke_id_saved; - *flag = flag_saved; - - CustomData_bmesh_copy_data(&ss->bm->ldata, &ss->bm->ldata, interpl->head.data, &l->head.data); - } - - int *tots = (int *)BLI_array_alloca(tots, totuv); - - for (int i = 0; i < totuv; i++) { - lastuvs[i * 2] = lastuvs[i * 2 + 1] = 0.0f; - tots[i] = 0; - } - - // re-snap uvs - v = (BMVert *)vertex.i; - - e = v->e; - do { - if (!e->l) { - continue; - } - - BMLoop *l_iter = e->l; - do { - BMLoop *l = l_iter->v != v ? l_iter->next : l_iter; - - for (int i = 0; i < totuv; i++) { - const int cd_uv = uvlayer[i].offset; - float *luv = BM_ELEM_CD_PTR(l, cd_uv); - - add_v2_v2(lastuvs + i * 2, luv); - tots[i]++; - } - } while ((l_iter = l_iter->radial_next) != e->l); - } while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e); - - for (int i = 0; i < totuv; i++) { - if (tots[i]) { - mul_v2_fl(lastuvs + i * 2, 1.0f / (float)tots[i]); - } - } - - e = v->e; - do { - if (!e->l) { - continue; - } - - BMLoop *l_iter = e->l; - do { - BMLoop *l = l_iter->v != v ? l_iter->next : l_iter; - - for (int i = 0; i < totuv; i++) { - const int cd_uv = uvlayer[i].offset; - float *luv = BM_ELEM_CD_PTR(l, cd_uv); - - if (snapuvs[i]) { - copy_v2_v2(luv, lastuvs + i * 2); - } - } - } while ((l_iter = l_iter->radial_next) != e->l); - } while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e); -} -- 2.30.2 From 64b21b4d1f6eca680ad30bcd7c15600112f43eeb Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Wed, 17 May 2023 02:06:15 -0700 Subject: [PATCH 094/279] temp-sculpt-dyntopo: Minor smoothing fixes and code cleanups * Fixed a few minor smoothing code issues * Removed a few unused functions --- .../blender/blenkernel/intern/pbvh_bmesh.cc | 610 +----------------- source/blender/editors/sculpt_paint/sculpt.cc | 1 - .../sculpt_paint/sculpt_brush_types.cc | 42 +- .../editors/sculpt_paint/sculpt_intern.hh | 2 +- .../editors/sculpt_paint/sculpt_smooth.cc | 13 +- 5 files changed, 34 insertions(+), 634 deletions(-) diff --git a/source/blender/blenkernel/intern/pbvh_bmesh.cc b/source/blender/blenkernel/intern/pbvh_bmesh.cc index fe560c0235f..c8b3294321a 100644 --- a/source/blender/blenkernel/intern/pbvh_bmesh.cc +++ b/source/blender/blenkernel/intern/pbvh_bmesh.cc @@ -406,7 +406,7 @@ BMFace *pbvh_bmesh_face_create(PBVH *pbvh, /* mark node for update */ node->flag |= PBVH_UpdateDrawBuffers | PBVH_UpdateNormals | PBVH_UpdateTris | - PBVH_UpdateOtherVerts; + PBVH_UpdateOtherVerts | PBVH_UpdateCurvatureDir | PBVH_UpdateTriAreas; node->flag &= ~PBVH_FullyHidden; /* Log the new face */ @@ -726,7 +726,7 @@ void pbvh_bmesh_face_remove( /* mark node for update */ f_node->flag |= PBVH_UpdateDrawBuffers | PBVH_UpdateNormals | PBVH_UpdateTris | - PBVH_UpdateOtherVerts; + PBVH_UpdateOtherVerts | PBVH_UpdateTriAreas | PBVH_UpdateCurvatureDir; bm_logstack_pop(); } @@ -1115,7 +1115,8 @@ void bke_pbvh_insert_face_finalize(PBVH *pbvh, BMFace *f, const int ni) PBVHNodeFlags updateflag = PBVH_UpdateTris | PBVH_UpdateBB | PBVH_UpdateDrawBuffers | PBVH_UpdateCurvatureDir | PBVH_UpdateOtherVerts; updateflag |= PBVH_UpdateColor | PBVH_UpdateMask | PBVH_UpdateNormals | PBVH_UpdateOriginalBB; - updateflag |= PBVH_UpdateVisibility | PBVH_UpdateRedraw | PBVH_RebuildDrawBuffers; + updateflag |= PBVH_UpdateVisibility | PBVH_UpdateRedraw | PBVH_RebuildDrawBuffers | + PBVH_UpdateTriAreas; node->flag |= updateflag; @@ -1939,7 +1940,7 @@ static void pbvh_bmesh_create_leaf_fast_task_cb(void *__restrict userdata, n->flag = PBVH_Leaf | PBVH_UpdateTris | PBVH_UpdateBB | PBVH_UpdateOriginalBB | PBVH_UpdateTriAreas | PBVH_UpdateColor | PBVH_UpdateVisibility | PBVH_UpdateDrawBuffers | PBVH_RebuildDrawBuffers | PBVH_UpdateCurvatureDir | - PBVH_UpdateTriAreas | PBVH_UpdateMask | PBVH_UpdateRedraw; + PBVH_UpdateMask | PBVH_UpdateRedraw; n->bm_faces = BLI_table_gset_new_ex("bm_faces", node->totface); @@ -3130,7 +3131,7 @@ void BKE_pbvh_bmesh_on_mesh_change(PBVH *pbvh) PBVHNode *node = pbvh->nodes + i; if (node->flag & PBVH_Leaf) { - node->flag |= PBVH_UpdateTriAreas; + node->flag |= PBVH_UpdateTriAreas | PBVH_UpdateCurvatureDir; } } @@ -4795,600 +4796,6 @@ BMesh *BKE_pbvh_reorder_bmesh1(PBVH *pbvh) return pbvh->header.bm; } -// only floats! and 8 byte aligned! -struct CacheParams { - float vchunk, echunk, lchunk, pchunk; - int cluster_steps, cluster_size; -}; - -struct CacheParamDef { - char name[32]; - float defvalue, min, max; -}; - -static void *hashco(float fx, float fy, float fz, float fdimen) -{ - double x = (double)fx; - double y = (double)fy; - double z = (double)fz; - double dimen = (double)fdimen; - - return (void *)((intptr_t)(z * dimen * dimen * dimen + y * dimen * dimen + x * dimen)); -} - -struct MeshTest { - float (*v_co)[3]; - float (*v_no)[3]; - int *v_e; - int *v_index; - int *v_flag; - - int *e_v1; - int *e_v2; - int *e_v1_next; - int *e_v2_next; - int *e_l; - int *e_flag; - int *e_index; - - int *l_v; - int *l_e; - int *l_f; - int *l_next; - int *l_prev; - int *l_radial_next; - int *l_radial_prev; - - int *f_l; - int *f_index; - int *f_flag; - - int totvert, totedge, totloop, totface; - MemArena *arena; -}; - -struct ElemHeader { - short type, hflag; - int index; - void *data; -}; - -struct MeshVert2 { - ElemHeader head; - float co[3]; - float no[3]; - int e; -}; - -struct MeshEdge2 { - ElemHeader head; - int v1, v2; - int v1_next, v2_next; - int l; -}; - -struct MeshLoop2 { - ElemHeader head; - int v, e, f, next, prev; - int radial_next, radial_prev; -}; - -struct MeshFace2 { - ElemHeader head; - int l, len; - float no[3]; -}; - -struct MeshTest2 { - MeshVert2 *verts; - MeshEdge2 *edges; - MeshLoop2 *loops; - MeshFace2 *faces; - - int totvert, totedge, totloop, totface; - MemArena *arena; -}; - -static MeshTest2 *meshtest2_from_bm(BMesh *bm) -{ - MeshTest2 *m2 = (MeshTest2 *)MEM_callocN(sizeof(MeshTest2), "MeshTest2"); - m2->arena = BLI_memarena_new(1024 * 32, "MeshTest2 arena"); - - m2->totvert = bm->totvert; - m2->totedge = bm->totedge; - m2->totloop = bm->totloop; - m2->totface = bm->totface; - - BMVert *v; - BMEdge *e; - BMFace *f; - BMIter iter; - - int lindex = 0; - - BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { - BMLoop *l = f->l_first; - do { - l->head.index = lindex++; - } while ((l = l->next) != f->l_first); - } - - m2->totloop = lindex; - - m2->verts = (MeshVert2 *)MEM_calloc_arrayN(bm->totvert, sizeof(MeshVert2), "MeshVert2s"); - m2->edges = (MeshEdge2 *)MEM_calloc_arrayN(bm->totedge, sizeof(MeshEdge2), "MeshEdge2s"); - m2->loops = (MeshLoop2 *)MEM_calloc_arrayN(m2->totloop, sizeof(MeshLoop2), "MeshLoop2s"); - m2->faces = (MeshFace2 *)MEM_calloc_arrayN(bm->totface, sizeof(MeshFace2), "MeshFace2s"); - - bm->elem_index_dirty |= BM_VERT | BM_EDGE | BM_FACE; - BM_mesh_elem_index_ensure(bm, BM_VERT | BM_EDGE | BM_FACE); - - BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) { - const int vi = v->head.index; - - copy_v3_v3(m2->verts[vi].co, v->co); - copy_v3_v3(m2->verts[vi].no, v->no); - - m2->verts[vi].e = v->e ? v->e->head.index : -1; - } - - BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) { - const int ei = e->head.index; - - m2->edges[ei].v1 = e->v1->head.index; - m2->edges[ei].v2 = e->v2->head.index; - m2->edges[ei].l = e->l ? e->l->head.index : -1; - - m2->edges[ei].v1_next = e->v1_disk_link.next->head.index; - m2->edges[ei].v2_next = e->v2_disk_link.next->head.index; - } - - BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { - const int fi = f->head.index; - - m2->faces[fi].len = f->len; - copy_v3_v3(m2->faces[fi].no, f->no); - - BMLoop *l = f->l_first; - do { - int li = l->head.index; - - m2->loops[li].v = l->v->head.index; - m2->loops[li].e = l->e->head.index; - m2->loops[li].f = l->f->head.index; - - m2->loops[li].radial_next = l->radial_next->head.index; - m2->loops[li].radial_prev = l->radial_prev->head.index; - - m2->loops[li].next = l->next->head.index; - m2->loops[li].prev = l->prev->head.index; - } while ((l = l->next) != f->l_first); - } - - return m2; -} - -static void free_meshtest2(MeshTest2 *m2) -{ - BLI_memarena_free(m2->arena); - MEM_freeN(m2); -} - -static MeshTest *meshtest_from_bm(BMesh *bm) -{ - MeshTest *m = MEM_cnew("MeshTest"); - m->arena = BLI_memarena_new(1024 * 32, "m->arena"); - - m->v_co = (float(*)[3])BLI_memarena_alloc(m->arena, bm->totvert * sizeof(*m->v_co)); - m->v_no = (float(*)[3])BLI_memarena_alloc(m->arena, bm->totvert * sizeof(*m->v_no)); - m->v_e = (int *)BLI_memarena_alloc(m->arena, bm->totvert * sizeof(*m->v_e)); - m->v_flag = (int *)BLI_memarena_alloc(m->arena, bm->totvert * sizeof(*m->v_flag)); - m->v_index = (int *)BLI_memarena_alloc(m->arena, bm->totvert * sizeof(*m->v_index)); - - m->e_v1 = (int *)BLI_memarena_alloc(m->arena, bm->totedge * sizeof(*m->e_v1)); - m->e_v1_next = (int *)BLI_memarena_alloc(m->arena, bm->totedge * sizeof(*m->e_v1)); - m->e_v2 = (int *)BLI_memarena_alloc(m->arena, bm->totedge * sizeof(*m->e_v1)); - m->e_v2_next = (int *)BLI_memarena_alloc(m->arena, bm->totedge * sizeof(*m->e_v1)); - m->e_l = (int *)BLI_memarena_alloc(m->arena, bm->totedge * sizeof(*m->e_v1)); - m->e_index = (int *)BLI_memarena_alloc(m->arena, bm->totedge * sizeof(*m->e_v1)); - m->e_flag = (int *)BLI_memarena_alloc(m->arena, bm->totedge * sizeof(*m->e_v1)); - - m->l_v = (int *)BLI_memarena_alloc(m->arena, bm->totloop * sizeof(*m->l_e)); - m->l_e = (int *)BLI_memarena_alloc(m->arena, bm->totloop * sizeof(*m->l_e)); - m->l_f = (int *)BLI_memarena_alloc(m->arena, bm->totloop * sizeof(*m->l_e)); - m->l_next = (int *)BLI_memarena_alloc(m->arena, bm->totloop * sizeof(*m->l_e)); - m->l_prev = (int *)BLI_memarena_alloc(m->arena, bm->totloop * sizeof(*m->l_e)); - m->l_radial_next = (int *)BLI_memarena_alloc(m->arena, bm->totloop * sizeof(*m->l_e)); - m->l_radial_prev = (int *)BLI_memarena_alloc(m->arena, bm->totloop * sizeof(*m->l_e)); - - m->f_l = (int *)BLI_memarena_alloc(m->arena, bm->totface * sizeof(*m->f_l)); - - m->totvert = bm->totvert; - m->totedge = bm->totedge; - m->totface = bm->totface; - m->totloop = bm->totloop; - - BMVert *v; - BMEdge *e; - BMFace *f; - BMIter iter; - - int lindex = 0; - - BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { - BMLoop *l = f->l_first; - do { - l->head.index = lindex++; - } while ((l = l->next) != f->l_first); - } - - bm->elem_index_dirty |= BM_VERT | BM_EDGE | BM_FACE; - BM_mesh_elem_index_ensure(bm, BM_VERT | BM_EDGE | BM_FACE); - - BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) { - copy_v3_v3(m->v_co[v->head.index], v->co); - copy_v3_v3(m->v_no[v->head.index], v->no); - - m->v_e[v->head.index] = v->e ? v->e->head.index : -1; - } - - BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) { - m->e_v1[e->head.index] = e->v1->head.index; - m->e_v2[e->head.index] = e->v2->head.index; - - m->e_v1_next[e->head.index] = e->v1_disk_link.next->head.index; - m->e_v2_next[e->head.index] = e->v2_disk_link.next->head.index; - - m->e_l[e->head.index] = e->l ? e->l->head.index : -1; - } - - BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { - m->f_l[f->head.index] = f->l_first->head.index; - - BMLoop *l = f->l_first; - do { - const int li = l->head.index; - - m->l_e[li] = l->e->head.index; - m->l_v[li] = l->v->head.index; - m->l_f[li] = f->head.index; - m->l_next[li] = l->next->head.index; - m->l_prev[li] = l->prev->head.index; - m->l_radial_next[li] = l->radial_next->head.index; - m->l_radial_prev[li] = l->radial_prev->head.index; - } while ((l = l->next) != f->l_first); - } - - return m; -} - -static void free_meshtest(MeshTest *m) -{ - BLI_memarena_free(m->arena); - MEM_freeN(m); -} - -#define SMOOTH_TEST_STEPS 20 - -double pbvh_bmesh_smooth_test(BMesh *bm, PBVH * /*pbvh*/) -{ - double average = 0.0f; - double average_tot = 0.0f; - - for (int iter = 0; iter < SMOOTH_TEST_STEPS; iter++) { - RNG *rng = BLI_rng_new(0); - - double time1 = PIL_check_seconds_timer(); - - for (int step = 0; step < 5; step++) { - for (int i = 0; i < bm->totvert; i++) { - int vi = BLI_rng_get_int(rng) % bm->totvert; - BMVert *v = bm->vtable[vi]; - BMEdge *e = v->e; - float co[3]; - - zero_v3(co); - int tot = 0.0; - - if (!e) { - continue; - } - - do { - BMVert *v2 = BM_edge_other_vert(e, v); - float co2[3]; - - sub_v3_v3v3(co2, v2->co, v->co); - madd_v3_v3fl(co2, v->no, -dot_v3v3(v->no, co2) * 0.9f); - add_v3_v3(co, co2); - - tot++; - - e = e->v1 == v ? e->v1_disk_link.next : e->v2_disk_link.next; - } while (e != v->e); - - if (tot == 0.0) { - continue; - } - - mul_v3_fl(co, 1.0f / (float)tot); - madd_v3_v3fl(v->co, co, 0.5f); - } - } - - double time2 = PIL_check_seconds_timer(); - - double time = time2 - time1; - - printf(" time: %.5f, %d of %d\n", time, iter, SMOOTH_TEST_STEPS); - - // skip first five - if (iter >= 5) { - average += time; - average_tot += 1.0f; - } - - BLI_rng_free(rng); - } - - printf("time: %.5f\n", average / average_tot); - return average / average_tot; -} - -double pbvh_meshtest2_smooth_test(MeshTest2 *m2, PBVH * /*pbvh*/) -{ - double average = 0.0f; - double average_tot = 0.0f; - - for (int iter = 0; iter < SMOOTH_TEST_STEPS; iter++) { - RNG *rng = BLI_rng_new(0); - - double time1 = PIL_check_seconds_timer(); - - for (int step = 0; step < 5; step++) { - for (int i = 0; i < m2->totvert; i++) { - int vi = BLI_rng_get_int(rng) % m2->totvert; - MeshVert2 *v = m2->verts + vi; - MeshEdge2 *e = v->e != -1 ? m2->edges + v->e : nullptr; - float co[3]; - - zero_v3(co); - int tot = 0.0; - - if (!e) { - continue; - } - - int enext = -1; - - do { - MeshVert2 *v2 = vi == e->v1 ? m2->verts + e->v2 : m2->verts + e->v1; - float co2[3]; - - sub_v3_v3v3(co2, v2->co, v->co); - madd_v3_v3fl(co2, v->no, -dot_v3v3(v->no, co2) * 0.9f); - add_v3_v3(co, co2); - - tot++; - - enext = e->v1 == vi ? e->v1_next : e->v2_next; - e = m2->edges + enext; - } while (enext != v->e); - - if (tot == 0.0) { - continue; - } - - mul_v3_fl(co, 1.0f / (float)tot); - madd_v3_v3fl(v->co, co, 0.5f); - } - } - - double time2 = PIL_check_seconds_timer(); - - double time = time2 - time1; - - printf(" time: %.5f, %d of %d\n", time, iter, SMOOTH_TEST_STEPS); - - // skip first five - if (iter >= 5) { - average += time; - average_tot += 1.0f; - } - - BLI_rng_free(rng); - } - - printf("time: %.5f\n", average / average_tot); - return average / average_tot; -} - -double pbvh_meshtest_smooth_test(MeshTest *m, PBVH * /*pbvh*/) -{ - double average = 0.0f; - double average_tot = 0.0f; - - for (int iter = 0; iter < SMOOTH_TEST_STEPS; iter++) { - RNG *rng = BLI_rng_new(0); - - double time1 = PIL_check_seconds_timer(); - - for (int step = 0; step < 5; step++) { - for (int i = 0; i < m->totvert; i++) { - int vi = BLI_rng_get_int(rng) % m->totvert; - // BMVert *v = bm->vtable[vi]; - const int startei = m->v_e[vi]; - int ei = startei; - - float co[3]; - - zero_v3(co); - int tot = 0.0; - - if (ei == -1) { - continue; - } - - const float *no = m->v_no[vi]; - const float *vco = m->v_co[vi]; - - do { - int ev1 = m->e_v1[ei]; - int ev2 = m->e_v2[ei]; - - int v2i = ev1 == vi ? ev2 : ev1; - - float co2[3]; - - sub_v3_v3v3(co2, m->v_co[v2i], vco); - madd_v3_v3fl(co2, no, -dot_v3v3(no, co2) * 0.9f); - add_v3_v3(co, co2); - - tot++; - - ei = ev1 == vi ? m->e_v1_next[ei] : m->e_v2_next[ei]; - } while (ei != startei); - - if (tot == 0.0) { - continue; - } - - mul_v3_fl(co, 1.0f / (float)tot); - madd_v3_v3fl(m->v_co[vi], co, 0.5f); - } - } - - double time2 = PIL_check_seconds_timer(); - - double time = time2 - time1; - - printf(" time: %.5f, %d of %d\n", time, iter + 1, SMOOTH_TEST_STEPS); - - // skip first five - if (iter >= 5) { - average += time; - average_tot += 1.0f; - } - - BLI_rng_free(rng); - } - - printf("time: %.5f\n", average / average_tot); - return average / average_tot; -} - -/* -test results from blenderartists thread: - -random, cluster, percent, data, data_perc, indices, ind_perc, mem (gb) - [1.22, 1.04, 14.42, 0.73, 67, 0.94, 29, 0], - [1.49, 1.46, 2.35, 1.10, 36, 1.17, 27, 0], - [1.29, 1.13, 14, 0.75, 71.54, 0.89, 45.08 , 0], - [1.58, 1.40, 12.3, 1.09, 44.7, 1.11, 42.42, 16], - [1.53, 1.36, 12.77, 1.08, 41.6, 1.07, 42.91, 0], - [1.56, 1.39, 12.47, 1.09, 42.65, 1.10, 42.15, 16], - [1.22, 1.06, 15.05, 0.75, 63.85, 0.82, 49.67, 32] - -[random] average: 1.41 variange: 0.15 median: 1.49 -[cluster] average: 1.26 variange: 0.17 median: 1.36 -[cluster-percent] average: 11.91 variange: 4.02 median: 12.77 -[data] average: 0.94 variange: 0.17 median: 1.08 -[data-percent] average: 52.48 variange: 13.37 median: 44.70 -[indices] average: 1.01 variange: 0.12 median: 1.07 -[indices-percent] average: 39.75 variange: 7.82 median: 42.42 - -So looks like the biggest gain is from replacing pointers with indices -(which lessens total memory bandwidth). The pure data-oriented version -is a tad bit faster then the index-replacement one, but not by that much. -*/ - -/* saves all bmesh references to internal indices, to be restored later */ -void BKE_pbvh_bmesh_save_indices(PBVH *pbvh) -{ - BM_mesh_elem_index_ensure(pbvh->header.bm, BM_VERT | BM_EDGE | BM_FACE); - - BMFace *f; - BMVert *v; - BMIter iter; - - int j = 0; - - BM_ITER_MESH (f, &iter, pbvh->header.bm, BM_FACES_OF_MESH) { - BMLoop *l = f->l_first; - - do { - l->head.index = j++; - } while ((l = l->next) != f->l_first); - } - - for (int i = 0; i < pbvh->totnode; i++) { - PBVHNode *node = pbvh->nodes + i; - - if (!(node->flag & PBVH_Leaf)) { - continue; - } - - node->prim_indices = (int *)MEM_calloc_arrayN(1 + node->bm_faces->length + - node->bm_unique_verts->length, - sizeof(int), - "saved bmesh indices"); - - int j = 0; - - TGSET_ITER (f, node->bm_faces) { - node->prim_indices[j++] = f->head.index; - } - TGSET_ITER_END; - - // flag start of vertex array - node->prim_indices[j++] = -1; - - TGSET_ITER (v, node->bm_unique_verts) { - node->prim_indices[j++] = v->head.index; - } - TGSET_ITER_END; - - node->totprim = j; - - // don't try to save invalid triangulation - if (node->flag & PBVH_UpdateTris) { - continue; - } - - // now do tribufs - for (j = 0; j < node->tot_tri_buffers + 1; j++) { - PBVHTriBuf *tribuf = j == node->tot_tri_buffers ? node->tribuf : node->tri_buffers + j; - - if (!tribuf) { - break; - } - - for (int k = 0; k < tribuf->totvert; k++) { - if (i == 35 && k == 12) { - printf("eek!"); - } - - tribuf->verts[k].i = ((BMVert *)tribuf->verts[k].i)->head.index; - } - - for (int k = 0; k < tribuf->totloop; k++) { - tribuf->loops[k] = ((BMLoop *)tribuf->loops[k])->head.index; - } - - for (int k = 0; k < tribuf->tottri; k++) { - PBVHTri *tri = tribuf->tris + k; - BMFace *f = (BMFace *)tri->f.i; - - tri->f.i = f->head.index; - - for (int l = 0; l < 3; l++) { - tri->l[l] = ((BMLoop *)tri->l[l])->head.index; - } - } - } - } -} - /* restore bmesh references from previously indices saved by BKE_pbvh_bmesh_save_indices */ void BKE_pbvh_bmesh_from_saved_indices(PBVH *pbvh) { @@ -5520,11 +4927,8 @@ void BKE_pbvh_bmesh_set_toolflags(PBVH *pbvh, bool use_toolflags) return; } - // BKE_pbvh_bmesh_save_indices(pbvh); BM_mesh_toolflags_set(pbvh->header.bm, use_toolflags); - // customdata layout might've changed + /* Customdata layout might've changed. */ pbvh_bmesh_fetch_cdrefs(pbvh); - - // BKE_pbvh_bmesh_from_saved_indices(pbvh); } diff --git a/source/blender/editors/sculpt_paint/sculpt.cc b/source/blender/editors/sculpt_paint/sculpt.cc index 182513291c3..04f8e3e9e97 100644 --- a/source/blender/editors/sculpt_paint/sculpt.cc +++ b/source/blender/editors/sculpt_paint/sculpt.cc @@ -5865,7 +5865,6 @@ bool SCULPT_cursor_geometry_info_update(bContext *C, /* Update the active vertex of the SculptSession. */ ss->active_vertex = srd.active_vertex; - SCULPT_vertex_random_access_ensure(ss); copy_v3_v3(out->active_vertex_co, SCULPT_active_vertex_co_get(ss)); switch (BKE_pbvh_type(ss->pbvh)) { diff --git a/source/blender/editors/sculpt_paint/sculpt_brush_types.cc b/source/blender/editors/sculpt_paint/sculpt_brush_types.cc index d2735e6d4cd..795eee008de 100644 --- a/source/blender/editors/sculpt_paint/sculpt_brush_types.cc +++ b/source/blender/editors/sculpt_paint/sculpt_brush_types.cc @@ -2425,7 +2425,6 @@ void SCULPT_relax_vertex(SculptSession *ss, float smooth_pos[3]; float final_disp[3]; int avg_count = 0; - int neighbor_count = 0; zero_v3(smooth_pos); eSculptBoundary bset = boundary_mask; @@ -2444,8 +2443,6 @@ void SCULPT_relax_vertex(SculptSession *ss, SculptVertexNeighborIter ni; SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd->vertex, ni) { - neighbor_count++; - /* When the vertex to relax is boundary, use only connected boundary vertices for the * average position. */ if (is_boundary) { @@ -2819,6 +2816,23 @@ void SCULPT_do_displacement_smear_brush(Sculpt *sd, Object *ob, Span /** \} */ +static void update_curvatures_task_cb_ex(void *__restrict userdata, + const int n, + const TaskParallelTLS *__restrict tls) +{ + SculptThreadedTaskData *data = static_cast(userdata); + SculptSession *ss = data->ob->sculpt; + const Brush *brush = data->brush; + + if (brush->flag2 & BRUSH_SMOOTH_USE_AREA_WEIGHT) { + BKE_pbvh_check_tri_areas(ss->pbvh, data->nodes[n]); + } + + if (brush->flag2 & BRUSH_CURVATURE_RAKE) { + SCULPT_curvature_begin(ss, data->nodes[n], false); + } +} + /* -------------------------------------------------------------------- */ /** \name Sculpt Topology Rake (Shared Utility) * \{ */ @@ -2856,14 +2870,7 @@ static void do_topology_rake_bmesh_task_cb_ex(void *__restrict userdata, SCULPT_automasking_node_begin( data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]); - if (use_curvature) { - SCULPT_curvature_begin(ss, data->nodes[n], false); - } - bool weighted = brush->flag2 & BRUSH_SMOOTH_USE_AREA_WEIGHT; - if (weighted) { - BKE_pbvh_check_tri_areas(ss->pbvh, data->nodes[n]); - } PBVHVertexIter vd; bool modified = false; @@ -2910,6 +2917,7 @@ static void do_topology_rake_bmesh_task_cb_ex(void *__restrict userdata, float avg[3], val[3]; int cd_temp = data->scl->bmesh_cd_offset; + SCULPT_bmesh_four_neighbor_average( ss, avg, direction2, vd.bm_vert, 1.0f, hard_corner_pin, cd_temp, weighted, false); @@ -2955,15 +2963,6 @@ void SCULPT_bmesh_topology_rake(Sculpt *sd, Object *ob, Span nodes, ob, ATTR_DOMAIN_POINT, CD_PROP_COLOR, SCULPT_ATTRIBUTE_NAME(rake_temp), ¶ms); } - if (SCULPT_stroke_is_first_brush_step(ss->cache) && - (ss->cache->brush->flag2 & BRUSH_SMOOTH_USE_AREA_WEIGHT)) - { - BKE_pbvh_update_all_tri_areas(ss->pbvh); - } - else if (brush->flag2 & BRUSH_SMOOTH_USE_AREA_WEIGHT) { - BKE_pbvh_face_areas_begin(ss->pbvh); - } - if (brush->flag2 & BRUSH_CURVATURE_RAKE) { BKE_sculpt_ensure_curvature_dir(ob); } @@ -2978,9 +2977,14 @@ void SCULPT_bmesh_topology_rake(Sculpt *sd, Object *ob, Span nodes, data.strength = factor; data.scl = ss->attrs.rake_temp; + if (brush->flag2 & BRUSH_SMOOTH_USE_AREA_WEIGHT) { + BKE_pbvh_face_areas_begin(ss->pbvh); + } + TaskParallelSettings settings; BKE_pbvh_parallel_range_settings(&settings, true, nodes.size()); + BLI_task_parallel_range(0, nodes.size(), &data, update_curvatures_task_cb_ex, &settings); BLI_task_parallel_range(0, nodes.size(), &data, do_topology_rake_bmesh_task_cb_ex, &settings); } } diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.hh b/source/blender/editors/sculpt_paint/sculpt_intern.hh index 3730ed8883d..fbb5db95e8c 100644 --- a/source/blender/editors/sculpt_paint/sculpt_intern.hh +++ b/source/blender/editors/sculpt_paint/sculpt_intern.hh @@ -1905,7 +1905,7 @@ void SCULPT_neighbor_coords_average_interior(SculptSession *ss, BLI_INLINE bool SCULPT_need_reproject(const SculptSession *ss) { - return !ss->ignore_uvs && ss->bm; // && CustomData_has_layer(&ss->bm->ldata, CD_PROP_FLOAT2); + return !ss->ignore_uvs && ss->bm && CustomData_has_layer(&ss->bm->ldata, CD_PROP_FLOAT2); } int SCULPT_vertex_island_get(SculptSession *ss, PBVHVertRef vertex); diff --git a/source/blender/editors/sculpt_paint/sculpt_smooth.cc b/source/blender/editors/sculpt_paint/sculpt_smooth.cc index d90c9edf27a..51c698e8cdf 100644 --- a/source/blender/editors/sculpt_paint/sculpt_smooth.cc +++ b/source/blender/editors/sculpt_paint/sculpt_smooth.cc @@ -45,7 +45,6 @@ static void SCULPT_neighbor_coords_average_interior_ex(SculptSession *ss, eSculptCorner corner_type) { float3 avg(0.0f, 0.0f, 0.0f); - int neighbor_count = 0; const eSculptBoundary is_boundary = SCULPT_vertex_is_boundary(ss, vertex, bound_type); const eSculptCorner is_corner = SCULPT_vertex_is_corner(ss, vertex, corner_type); @@ -65,7 +64,6 @@ static void SCULPT_neighbor_coords_average_interior_ex(SculptSession *ss, SculptVertexNeighborIter ni; SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vertex, ni) { - neighbor_count++; float w = weighted ? areas[ni.i] : 1.0f; eSculptBoundary is_boundary2; @@ -214,8 +212,6 @@ void SCULPT_bmesh_four_neighbor_average(SculptSession *ss, float buckets[8] = {0}; PBVHVertRef vertex = {(intptr_t)v}; - // zero_v3(direction); - float *field = BM_ELEM_CD_PTR(v, cd_temp); float dir[3]; float dir3[3] = {0.0f, 0.0f, 0.0f}; @@ -669,11 +665,12 @@ void SCULPT_smooth_undo_push(Object *ob, Span nodes) { SculptSession *ss = ob->sculpt; - if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH && SCULPT_need_reproject(ss)) { + if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH && SCULPT_need_reproject(ss) && + CustomData_has_layer(&ss->bm->ldata, CD_PROP_FLOAT2)) + { BM_log_entry_add_ex(ss->bm, ss->bm_log, true); for (PBVHNode *node : nodes) { - PBVHVertexIter vd; PBVHFaceIter fd; BKE_pbvh_face_iter_begin (ss->pbvh, node, fd) { BM_log_face_modified(ss->bm, ss->bm_log, reinterpret_cast(fd.face.i)); @@ -719,9 +716,6 @@ void SCULPT_smooth( return; } - SCULPT_vertex_random_access_ensure(ss); - SCULPT_boundary_info_ensure(ob); - for (iteration = 0; iteration <= count; iteration++) { const float strength = (iteration != count) ? 1.0f : last; @@ -784,7 +778,6 @@ void SCULPT_surface_smooth_laplacian_step(SculptSession *ss, { float laplacian_smooth_co[3]; float weigthed_o[3], weigthed_q[3], d[3]; - int v_index = BKE_pbvh_vertex_to_index(ss->pbvh, vertex); SCULPT_neighbor_coords_average(ss, laplacian_smooth_co, vertex, 0.0f, 0.0f, use_area_weights); -- 2.30.2 From 380ad68d84cca0a40ddcc8f8d1c0d72afdf3f488 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Wed, 17 May 2023 02:57:24 -0700 Subject: [PATCH 095/279] temp-sculpt-dyntopo: Fix smoothing bug --- source/blender/blenkernel/BKE_pbvh.h | 2 ++ source/blender/blenkernel/intern/pbvh.cc | 29 ++++++++++++++++++- .../editors/sculpt_paint/sculpt_undo.cc | 2 ++ 3 files changed, 32 insertions(+), 1 deletion(-) diff --git a/source/blender/blenkernel/BKE_pbvh.h b/source/blender/blenkernel/BKE_pbvh.h index e6296462b76..caa34f5dbfa 100644 --- a/source/blender/blenkernel/BKE_pbvh.h +++ b/source/blender/blenkernel/BKE_pbvh.h @@ -1277,6 +1277,8 @@ void BKE_pbvh_free_bmesh(PBVH *pbvh, struct BMesh *bm); void BKE_pbvh_show_orig_set(PBVH *pbvh, bool show_orig); bool BKE_pbvh_show_orig_get(PBVH *pbvh); +void BKE_pbvh_flush_tri_areas(PBVH *pbvh); + #ifdef __cplusplus } diff --git a/source/blender/blenkernel/intern/pbvh.cc b/source/blender/blenkernel/intern/pbvh.cc index f8cefeb8239..97e53b6e31d 100644 --- a/source/blender/blenkernel/intern/pbvh.cc +++ b/source/blender/blenkernel/intern/pbvh.cc @@ -4133,7 +4133,8 @@ void BKE_pbvh_get_vert_face_areas(PBVH *pbvh, PBVHVertRef vertex, float *r_areas } if (j >= valence) { - printf("%s: error, corrupt edge cycle, valence was %d expected %d\n", __func__, j, valence); + printf( + "%s: error, corrupt edge cycle, valence was %d expected %d\n", __func__, j, valence); break; } @@ -4773,6 +4774,32 @@ void BKE_pbvh_sync_visibility_from_verts(PBVH *pbvh, Mesh *mesh) } } +void BKE_pbvh_flush_tri_areas(PBVH *pbvh) +{ + for (int i : IndexRange(pbvh->totnode)) { + PBVHNode *node = pbvh->nodes + i; + + if (!(node->flag & PBVH_Leaf) || !(node->flag & PBVH_UpdateTriAreas)) { + continue; + } + + BKE_pbvh_check_tri_areas(pbvh, node); + node->flag |= PBVH_UpdateTriAreas; + } + + BKE_pbvh_face_areas_begin(pbvh); + + for (int i : IndexRange(pbvh->totnode)) { + PBVHNode *node = pbvh->nodes + i; + + if (!(node->flag & PBVH_Leaf)) { + continue; + } + + BKE_pbvh_check_tri_areas(pbvh, node); + } +} + namespace blender::bke::pbvh { Vector search_gather(PBVH *pbvh, BKE_pbvh_SearchCallback scb, diff --git a/source/blender/editors/sculpt_paint/sculpt_undo.cc b/source/blender/editors/sculpt_paint/sculpt_undo.cc index bc1c067a788..9ea34e9031a 100644 --- a/source/blender/editors/sculpt_paint/sculpt_undo.cc +++ b/source/blender/editors/sculpt_paint/sculpt_undo.cc @@ -1359,6 +1359,8 @@ static int sculpt_undo_bmesh_restore( break; } + BKE_pbvh_flush_tri_areas(ss->pbvh); + if (set_active_vertex && ss->bm_log && ss->bm) { if (ss->active_face.i != -1) { BMFace *f = BM_log_id_face_get(ss->bm, ss->bm_log, (uint)ss->active_face.i); -- 2.30.2 From 5243408f68dffc7684966610a47963db90043b82 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Wed, 17 May 2023 09:10:48 -0700 Subject: [PATCH 096/279] temp-sculpt-dyntopo: Fix various face set bugs * Face set undo now updates boundary flags properly. * Fixed bug with draw face set brush sometimes only drawing one face set --- source/blender/editors/sculpt_paint/sculpt.cc | 4 +-- .../editors/sculpt_paint/sculpt_undo.cc | 25 ++++++++++++++++--- 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/source/blender/editors/sculpt_paint/sculpt.cc b/source/blender/editors/sculpt_paint/sculpt.cc index 04f8e3e9e97..daddcd01233 100644 --- a/source/blender/editors/sculpt_paint/sculpt.cc +++ b/source/blender/editors/sculpt_paint/sculpt.cc @@ -477,7 +477,7 @@ int SCULPT_active_face_set_get(SculptSession *ss) return ss->face_sets[face_index]; } case PBVH_BMESH: - if (ss->cd_faceset_offset && (ss->active_face.i != PBVH_REF_NONE)) { + if (ss->cd_faceset_offset != -1 && (ss->active_face.i != PBVH_REF_NONE)) { BMFace *f = (BMFace *)ss->active_face.i; return BM_ELEM_CD_GET_INT(f, ss->cd_faceset_offset); } @@ -1009,7 +1009,7 @@ int SCULPT_face_set_next_available_get(SculptSession *ss) int next_face_set = 0; BMIter iter; BMFace *f; - if (!ss->cd_faceset_offset) { + if (ss->cd_faceset_offset == -1) { return 0; } diff --git a/source/blender/editors/sculpt_paint/sculpt_undo.cc b/source/blender/editors/sculpt_paint/sculpt_undo.cc index 9ea34e9031a..cfe8a277b0c 100644 --- a/source/blender/editors/sculpt_paint/sculpt_undo.cc +++ b/source/blender/editors/sculpt_paint/sculpt_undo.cc @@ -308,6 +308,7 @@ static void update_cb(PBVHNode *node, void *rebuild) struct PartialUpdateData { PBVH *pbvh; + SculptSession *ss; bool rebuild; char *modified_grids; bool *modified_hidden_verts; @@ -365,6 +366,8 @@ static void update_cb_partial(PBVHNode *node, void *userdata) if (data->modified_hidden_verts != nullptr) { for (int i = 0; i < verts_num; i++) { if (data->modified_hidden_verts[vert_indices[i]]) { + BKE_sculpt_boundary_flag_update(data->ss, BKE_pbvh_make_vref(vert_indices[i])); + if (data->rebuild) { BKE_pbvh_vert_tag_update_normal_visibility(node); } @@ -374,12 +377,21 @@ static void update_cb_partial(PBVHNode *node, void *userdata) } } } + if (data->modified_face_set_faces) { PBVHFaceIter fd; + bool updated = false; + BKE_pbvh_face_iter_begin (data->pbvh, node, fd) { if (data->modified_face_set_faces[fd.index]) { - BKE_pbvh_node_mark_update_face_sets(node); - break; + for (int i = 0; i < fd.verts_num; i++) { + BKE_sculpt_boundary_flag_update(data->ss, fd.verts[i]); + } + + if (!updated) { + BKE_pbvh_node_mark_update_face_sets(node); + updated = true; + } } } BKE_pbvh_face_iter_end(fd); @@ -671,7 +683,7 @@ static bool sculpt_undo_restore_face_sets(bContext *C, SWAP(int, unode->face_sets[i], ss->face_sets[face_index]); - modified_face_set_faces[face_index] = unode->face_sets[i] != ss->face_sets[face_index]; + modified_face_set_faces[face_index] |= unode->face_sets[i] != ss->face_sets[face_index]; modified |= modified_face_set_faces[face_index]; } @@ -896,6 +908,12 @@ static void bmesh_undo_on_face_change(BMFace *f, int ni = BM_ELEM_CD_GET_INT(&h, data->cd_face_node_offset); + BMLoop *l = f->l_first; + do { + int flag = BM_ELEM_CD_GET_INT(l->v, data->cd_boundary_flag); + BM_ELEM_CD_SET_INT(l->v, data->cd_boundary_flag, flag | SCULPT_BOUNDARY_NEEDS_UPDATE); + } while ((l = l->next) != f->l_first); + // attempt to find old node in old_customdata PBVHNode *node = BKE_pbvh_get_node_leaf_safe(data->pbvh, ni); if (node) { @@ -1609,6 +1627,7 @@ static void sculpt_undo_restore_list(bContext *C, Depsgraph *depsgraph, ListBase * the nodes get recreated, though in that case it could do all. */ PartialUpdateData data{}; data.rebuild = rebuild; + data.ss = ss; data.pbvh = ss->pbvh; data.modified_grids = undo_modified_grids; data.modified_hidden_verts = modified_hidden_verts; -- 2.30.2 From 6d9faebd0f086cea4db0436ef555119100f4ced9 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Wed, 17 May 2023 10:27:05 -0700 Subject: [PATCH 097/279] temp-sculpt-dyntopo: Fix crash with original mask code --- source/blender/blenkernel/intern/paint.cc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/source/blender/blenkernel/intern/paint.cc b/source/blender/blenkernel/intern/paint.cc index 5e77d480bd1..8562858b16e 100644 --- a/source/blender/blenkernel/intern/paint.cc +++ b/source/blender/blenkernel/intern/paint.cc @@ -3981,12 +3981,14 @@ bool get_original_vertex(SculptSession *ss, switch (BKE_pbvh_type(ss->pbvh)) { case PBVH_FACES: mask = ss->vmask ? &ss->vmask[vertex.i] : nullptr; + break; case PBVH_BMESH: { BMVert *v; int cd_mask = CustomData_get_offset(&ss->bm->vdata, CD_PAINT_MASK); v = (BMVert *)vertex.i; mask = cd_mask != -1 ? static_cast(BM_ELEM_CD_GET_VOID_P(v, cd_mask)) : nullptr; + break; } case PBVH_GRIDS: { const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh); @@ -4000,6 +4002,7 @@ bool get_original_vertex(SculptSession *ss, CCGElem *elem = BKE_pbvh_get_grids(ss->pbvh)[grid_index]; mask = CCG_elem_mask(key, CCG_elem_offset(key, elem, vertex_index)); } + break; } } -- 2.30.2 From 62404b38788b703977f8cbde3e06a468e4a183cd Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Wed, 17 May 2023 13:24:06 -0700 Subject: [PATCH 098/279] temp-sculpt-dyntopo: Improve dyntopo support for face sets * Face set edit operator is now supported. * Fixed various bugs in the visibility operators. --- source/blender/blenkernel/intern/paint.cc | 8 +- source/blender/editors/sculpt_paint/sculpt.cc | 45 +- .../sculpt_paint/sculpt_brush_types.cc | 5 +- .../editors/sculpt_paint/sculpt_face_set.cc | 483 +++++++++++++----- .../editors/sculpt_paint/sculpt_intern.hh | 7 +- .../editors/sculpt_paint/sculpt_ops.cc | 2 +- .../editors/sculpt_paint/sculpt_smooth.cc | 2 + .../editors/sculpt_paint/sculpt_undo.cc | 24 +- 8 files changed, 397 insertions(+), 179 deletions(-) diff --git a/source/blender/blenkernel/intern/paint.cc b/source/blender/blenkernel/intern/paint.cc index 8562858b16e..c410f42322e 100644 --- a/source/blender/blenkernel/intern/paint.cc +++ b/source/blender/blenkernel/intern/paint.cc @@ -2154,6 +2154,11 @@ static void sculpt_update_object( } } + if (ss->bm_log && ss->pbvh) { + BKE_pbvh_set_idmap(ss->pbvh, ss->bm_idmap); + BKE_pbvh_set_bm_log(ss->pbvh, ss->bm_log); + } + if (is_paint_tool) { if (ss->vcol_domain == ATTR_DOMAIN_CORNER) { /* Ensure pbvh nodes have loop indices; the sculpt undo system @@ -2652,6 +2657,8 @@ static PBVH *build_pbvh_from_ccg(Object *ob, SubdivCCG *subdiv_ccg) int totvert = BKE_pbvh_get_grid_num_verts(pbvh); BKE_sculpt_init_flags_valence(ob, pbvh, totvert, true); + BKE_subdiv_ccg_start_face_grid_index_ensure(ss->subdiv_ccg); + return pbvh; } @@ -3140,7 +3147,6 @@ static int sculpt_attr_elem_count_get(Object *ob, eAttrDomain domain) break; case ATTR_DOMAIN_FACE: return ss->totfaces; - break; default: BLI_assert_unreachable(); return 0; diff --git a/source/blender/editors/sculpt_paint/sculpt.cc b/source/blender/editors/sculpt_paint/sculpt.cc index daddcd01233..dbb6fda1758 100644 --- a/source/blender/editors/sculpt_paint/sculpt.cc +++ b/source/blender/editors/sculpt_paint/sculpt.cc @@ -544,10 +544,6 @@ void SCULPT_face_set_visibility_set(SculptSession *ss, int face_set, bool visibl BMIter iter; BMFace *f; - if (!ss->attrs.hide_poly) { - return; - } - BM_ITER_MESH (f, &iter, ss->bm, BM_FACES_OF_MESH) { int fset = BM_ELEM_CD_GET_INT(f, ss->cd_faceset_offset); int node = BM_ELEM_CD_GET_INT(f, ss->cd_face_node_offset); @@ -571,11 +567,11 @@ void SCULPT_face_visibility_all_invert(SculptSession *ss) { SCULPT_topology_islands_invalidate(ss); - BLI_assert(ss->face_sets != nullptr); - BLI_assert(ss->hide_poly != nullptr); switch (BKE_pbvh_type(ss->pbvh)) { case PBVH_FACES: case PBVH_GRIDS: + BLI_assert(ss->hide_poly != nullptr); + for (int i = 0; i < ss->totfaces; i++) { ss->hide_poly[i] = !ss->hide_poly[i]; } @@ -593,13 +589,17 @@ void SCULPT_face_visibility_all_invert(SculptSession *ss) } } -void SCULPT_face_visibility_all_set(SculptSession *ss, bool visible) +void SCULPT_face_visibility_all_set(Object *ob, bool visible) { - if (visible && !ss->attrs.hide_poly) { - /* This case is allowed. */ + SculptSession *ss = ob->sculpt; + + if (!ss->bm && visible && !ss->attrs.hide_poly) { + /* Nothing is hidden. */ return; } + SCULPT_topology_islands_invalidate(ss); + SCULPT_face_random_access_ensure(ss); switch (BKE_pbvh_type(ss->pbvh)) { case PBVH_FACES: @@ -608,7 +608,18 @@ void SCULPT_face_visibility_all_set(SculptSession *ss, bool visible) BLI_assert(ss->hide_poly != nullptr); - memset(ss->hide_poly, !visible, sizeof(bool) * ss->totfaces); + if (visible) { + if (ss->attrs.hide_poly) { + BKE_sculpt_attribute_destroy(ob, ss->attrs.hide_poly); + } + ss->hide_poly = nullptr; + } + else { + if (!ss->hide_poly) { + ss->hide_poly = BKE_sculpt_hide_poly_ensure(ob); + } + memset(ss->hide_poly, !visible, sizeof(bool) * ss->totfaces); + } break; case PBVH_BMESH: for (int i = 0; i < ss->totfaces; i++) { @@ -937,20 +948,6 @@ void SCULPT_visibility_sync_all_from_faces(Object *ob) BMIter iter; BMFace *f; - if (!ss->attrs.hide_poly) { - BM_ITER_MESH (f, &iter, ss->bm, BM_FACES_OF_MESH) { - BM_elem_flag_disable(f, BM_ELEM_HIDDEN); - - BMLoop *l = f->l_first; - do { - BM_elem_flag_disable(l->v, BM_ELEM_HIDDEN); - BM_elem_flag_disable(l->e, BM_ELEM_HIDDEN); - } while ((l = l->next) != f->l_first); - } - - return; - } - /* Hide all verts and edges attached to faces. */ BM_ITER_MESH (f, &iter, ss->bm, BM_FACES_OF_MESH) { BMLoop *l = f->l_first; diff --git a/source/blender/editors/sculpt_paint/sculpt_brush_types.cc b/source/blender/editors/sculpt_paint/sculpt_brush_types.cc index 795eee008de..35738b4bc93 100644 --- a/source/blender/editors/sculpt_paint/sculpt_brush_types.cc +++ b/source/blender/editors/sculpt_paint/sculpt_brush_types.cc @@ -1547,7 +1547,6 @@ static void do_layer_brush_task_cb_ex(void *__restrict userdata, thread_id, &automask_data); - const int vi = vd.index; float *disp_factor; if (use_persistent_base) { disp_factor = vertex_attr_ptr(vd.vertex, ss->attrs.persistent_disp); @@ -2565,6 +2564,8 @@ void SCULPT_do_slide_relax_brush(Sculpt *sd, Object *ob, Span nodes) return; } + SCULPT_boundary_info_ensure(ob); + BKE_curvemapping_init(brush->curve); SculptThreadedTaskData data{}; @@ -2818,7 +2819,7 @@ void SCULPT_do_displacement_smear_brush(Sculpt *sd, Object *ob, Span static void update_curvatures_task_cb_ex(void *__restrict userdata, const int n, - const TaskParallelTLS *__restrict tls) + const TaskParallelTLS *__restrict /*tls*/) { SculptThreadedTaskData *data = static_cast(userdata); SculptSession *ss = data->ob->sculpt; diff --git a/source/blender/editors/sculpt_paint/sculpt_face_set.cc b/source/blender/editors/sculpt_paint/sculpt_face_set.cc index 5fce262b9dc..c4d16895145 100644 --- a/source/blender/editors/sculpt_paint/sculpt_face_set.cc +++ b/source/blender/editors/sculpt_paint/sculpt_face_set.cc @@ -41,6 +41,7 @@ #include "BKE_object.h" #include "BKE_paint.h" #include "BKE_pbvh.h" +#include "BKE_subdiv_ccg.h" #include "DEG_depsgraph.h" @@ -54,10 +55,13 @@ #include "RNA_access.h" #include "RNA_define.h" +#include "../../bmesh/intern/bmesh_idmap.h" #include "bmesh.h" using blender::Array; using blender::float3; +using blender::IndexRange; +using blender::Span; using blender::Vector; /* Utils. */ @@ -852,14 +856,9 @@ static int sculpt_face_sets_change_visibility_exec(bContext *C, wmOperator *op) SculptSession *ss = ob->sculpt; Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); - Mesh *mesh = BKE_object_get_original_mesh(ob); - BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true, false); - - /* Not supported for dyntopo. */ - if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) { - return OPERATOR_CANCELLED; - } + SCULPT_vertex_random_access_ensure(ss); + SCULPT_face_random_access_ensure(ss); const int mode = RNA_enum_get(op->ptr, "mode"); const int tot_vert = SCULPT_vertex_count_get(ss); @@ -895,36 +894,37 @@ static int sculpt_face_sets_change_visibility_exec(bContext *C, wmOperator *op) } } - if (ss->hide_poly) { + if (ss->attrs.hide_poly) { for (int i = 0; i < ss->totfaces; i++) { - if (ss->hide_poly[i]) { + PBVHFaceRef face = BKE_pbvh_index_to_face(ss->pbvh, i); + if (SCULPT_face_is_hidden(ss, face)) { hidden_vertex = true; break; } } } - ss->hide_poly = BKE_sculpt_hide_poly_ensure(ob); + BKE_sculpt_hide_poly_ensure(ob); if (hidden_vertex) { - SCULPT_face_visibility_all_set(ss, true); + SCULPT_face_visibility_all_set(ob, true); } else { - if (ss->face_sets) { - SCULPT_face_visibility_all_set(ss, false); + if (ss->attrs.face_set) { + SCULPT_face_visibility_all_set(ob, false); SCULPT_face_set_visibility_set(ss, active_face_set, true); } else { - SCULPT_face_visibility_all_set(ss, true); + SCULPT_face_visibility_all_set(ob, true); } } break; } case SCULPT_FACE_SET_VISIBILITY_SHOW_ACTIVE: - ss->hide_poly = BKE_sculpt_hide_poly_ensure(ob); + BKE_sculpt_hide_poly_ensure(ob); - if (ss->face_sets) { - SCULPT_face_visibility_all_set(ss, false); + if (ss->attrs.face_set) { + SCULPT_face_visibility_all_set(ob, false); SCULPT_face_set_visibility_set(ss, active_face_set, true); } else { @@ -932,13 +932,13 @@ static int sculpt_face_sets_change_visibility_exec(bContext *C, wmOperator *op) } break; case SCULPT_FACE_SET_VISIBILITY_HIDE_ACTIVE: - ss->hide_poly = BKE_sculpt_hide_poly_ensure(ob); + BKE_sculpt_hide_poly_ensure(ob); - if (ss->face_sets) { + if (ss->attrs.face_set) { SCULPT_face_set_visibility_set(ss, active_face_set, false); } else { - SCULPT_face_visibility_all_set(ss, false); + SCULPT_face_visibility_all_set(ob, false); } break; @@ -1017,24 +1017,24 @@ static int sculpt_face_sets_randomize_colors_exec(bContext *C, wmOperator * /*op Object *ob = CTX_data_active_object(C); SculptSession *ss = ob->sculpt; - /* Dyntopo not supported. */ - if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) { + if (!ss->attrs.face_set) { return OPERATOR_CANCELLED; } - if (!ss->face_sets) { - return OPERATOR_CANCELLED; - } + SCULPT_face_random_access_ensure(ss); PBVH *pbvh = ob->sculpt->pbvh; Mesh *mesh = static_cast(ob->data); mesh->face_sets_color_seed += 1; - if (ss->face_sets) { + if (ss->attrs.face_set) { const int random_index = clamp_i(ss->totfaces * BLI_hash_int_01(mesh->face_sets_color_seed), 0, max_ii(0, ss->totfaces - 1)); - mesh->face_sets_color_default = ss->face_sets[random_index]; + PBVHFaceRef face = BKE_pbvh_index_to_face(ss->pbvh, random_index); + + mesh->face_sets_color_default = blender::bke::paint::face_attr_get(face, + ss->attrs.face_set); } BKE_pbvh_face_sets_color_set(pbvh, mesh->face_sets_color_seed, mesh->face_sets_color_default); @@ -1111,86 +1111,138 @@ static EnumPropertyItem prop_sculpt_face_sets_edit_types[] = { {0, nullptr, 0, nullptr, nullptr}, }; -static void sculpt_face_set_grow(Object *ob, - SculptSession *ss, - const int *prev_face_sets, - const int active_face_set_id, - const bool modify_hidden) +void SCULPT_face_mark_boundary_update(SculptSession *ss, PBVHFaceRef face) { - using namespace blender; - Mesh *mesh = BKE_mesh_from_object(ob); - const OffsetIndices polys = mesh->polys(); - const Span corner_verts = mesh->corner_verts(); + switch (BKE_pbvh_type(ss->pbvh)) { + case PBVH_BMESH: { + BMFace *f = reinterpret_cast(face.i); + BMLoop *l = f->l_first; + do { + PBVHVertRef vertex = {reinterpret_cast(l->v)}; + BKE_sculpt_boundary_flag_update(ss, vertex); + } while ((l = l->next) != f->l_first); - for (const int p : polys.index_range()) { - if (!modify_hidden && prev_face_sets[p] <= 0) { - continue; + break; } - for (const int vert : corner_verts.slice(polys[p])) { - const MeshElemMap *vert_map = &ss->pmap[vert]; - for (int i = 0; i < vert_map->count; i++) { - const int neighbor_face_index = vert_map->indices[i]; - if (neighbor_face_index == p) { - continue; - } - if (abs(prev_face_sets[neighbor_face_index]) == active_face_set_id) { - ss->face_sets[p] = active_face_set_id; - } + case PBVH_FACES: { + for (int vert_i : ss->corner_verts.slice(ss->polys[face.i])) { + PBVHVertRef vertex = {vert_i}; + BKE_sculpt_boundary_flag_update(ss, vertex); } + break; } - } -} + case PBVH_GRIDS: { + const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh); + int grid_index = BKE_subdiv_ccg_start_face_grid_index_get(ss->subdiv_ccg)[face.i]; + int vertex_i = grid_index * key->grid_area; + int verts_num = ss->polys[face.i].size() * key->grid_area; -static void sculpt_face_set_shrink(Object *ob, - SculptSession *ss, - const int *prev_face_sets, - const int active_face_set_id, - const bool modify_hidden) -{ - using namespace blender; - Mesh *mesh = BKE_mesh_from_object(ob); - const OffsetIndices polys = mesh->polys(); - const Span corner_verts = mesh->corner_verts(); - for (const int p : polys.index_range()) { - if (!modify_hidden && prev_face_sets[p] <= 0) { - continue; - } - if (abs(prev_face_sets[p]) == active_face_set_id) { - for (const int vert_i : corner_verts.slice(polys[p])) { - const MeshElemMap *vert_map = &ss->pmap[vert_i]; - for (int i = 0; i < vert_map->count; i++) { - const int neighbor_face_index = vert_map->indices[i]; - if (neighbor_face_index == p) { - continue; - } - if (abs(prev_face_sets[neighbor_face_index]) != active_face_set_id) { - ss->face_sets[p] = prev_face_sets[neighbor_face_index]; - } - } + for (int i = 0; i < verts_num; i++, vertex_i++) { + BKE_sculpt_boundary_flag_update(ss, {vertex_i}); } - } - } -} -static bool check_single_face_set(SculptSession *ss, - const int *face_sets, - const bool check_visible_only) -{ - if (face_sets == nullptr) { - return true; - } - int first_face_set = SCULPT_FACE_SET_NONE; - if (check_visible_only) { - for (int f = 0; f < ss->totfaces; f++) { - if (ss->hide_poly && ss->hide_poly[f]) { - continue; - } - first_face_set = face_sets[f]; break; } } +} + +static void sculpt_face_set_grow_shrink(Object *ob, + SculptSession *ss, + const Array prev_face_sets, + const int active_face_set_id, + const bool modify_hidden, + bool grow) +{ + using namespace blender; + + Mesh *mesh = BKE_mesh_from_object(ob); + const OffsetIndices polys = mesh->polys(); + const Span corner_verts = mesh->corner_verts(); + + Vector modified_faces; + + for (int face_i = 0; face_i < ss->totfaces; face_i++) { + PBVHFaceRef face = BKE_pbvh_index_to_face(ss->pbvh, face_i); + + if ((!modify_hidden && SCULPT_face_is_hidden(ss, face)) || + prev_face_sets[face_i] != active_face_set_id) + { + continue; + } + + if (ss->bm) { + BMFace *f = reinterpret_cast(face.i); + BMLoop *l = f->l_first; + BMIter iter; + BMFace *f2; + + do { + BM_ITER_ELEM (f2, &iter, l->v, BM_FACES_OF_VERT) { + if (f2 == f || (!modify_hidden && BM_elem_flag_test(f2, BM_ELEM_HIDDEN))) { + continue; + } + + PBVHFaceRef face2 = {reinterpret_cast(f2)}; + int face2_i = BKE_pbvh_face_to_index(ss->pbvh, face2); + + if (grow) { + SCULPT_face_set_set(ss, face2, active_face_set_id); + modified_faces.append(face2); + } + else if (prev_face_sets[face2_i] != active_face_set_id) { + SCULPT_face_set_set(ss, face, prev_face_sets[face2_i]); + modified_faces.append(face); + } + } + } while ((l = l->next) != f->l_first); + } + else { // + for (const int vert_i : corner_verts.slice(polys[face_i])) { + const MeshElemMap *vert_map = &ss->pmap[vert_i]; + for (int i = 0; i < vert_map->count; i++) { + const int neighbor_face_index = vert_map->indices[i]; + if (neighbor_face_index == face_i) { + continue; + } + + if (grow) { + ss->face_sets[neighbor_face_index] = active_face_set_id; + modified_faces.append(BKE_pbvh_index_to_face(ss->pbvh, neighbor_face_index)); + } + else if (prev_face_sets[neighbor_face_index] != active_face_set_id) { + ss->face_sets[face_i] = prev_face_sets[neighbor_face_index]; + modified_faces.append(face); + } + } + } + } + } + + for (PBVHFaceRef face : modified_faces) { + SCULPT_face_mark_boundary_update(ss, face); + } +} + +static bool check_single_face_set(SculptSession *ss, const bool check_visible_only) +{ + int first_face_set = SCULPT_FACE_SET_NONE; + if (check_visible_only) { + for (int f = 0; f < ss->totfaces; f++) { + PBVHFaceRef face = BKE_pbvh_index_to_face(ss->pbvh, f); + if (SCULPT_face_is_hidden(ss, face)) { + continue; + } + + first_face_set = SCULPT_face_set_get(ss, face); + break; + } + } + else if (ss->totfaces > 0) { + PBVHFaceRef face = BKE_pbvh_index_to_face(ss->pbvh, 0); + first_face_set = SCULPT_face_set_get(ss, face); + } else { - first_face_set = face_sets[0]; + first_face_set = SCULPT_FACE_SET_NONE; } if (first_face_set == SCULPT_FACE_SET_NONE) { @@ -1198,16 +1250,102 @@ static bool check_single_face_set(SculptSession *ss, } for (int f = 0; f < ss->totfaces; f++) { - if (check_visible_only && ss->hide_poly && ss->hide_poly[f]) { + PBVHFaceRef face = BKE_pbvh_index_to_face(ss->pbvh, f); + + if (check_visible_only && SCULPT_face_is_hidden(ss, face)) { continue; } - if (face_sets[f] != first_face_set) { + if (SCULPT_face_set_get(ss, face) != first_face_set) { return false; } } return true; } +/* Deletes geometry without destroying the underlying PBVH. */ +static void sculpt_face_set_delete_geometry_bmesh(Object *ob, BMesh *bm) +{ + SculptSession *ss = ob->sculpt; + BMIter iter; + int cd_face_node = ss->cd_face_node_offset; + + Vector nodes = blender::bke::pbvh::search_gather(ss->pbvh, nullptr, nullptr); + for (PBVHNode *node : nodes) { + /* Only need to do this once. */ + SCULPT_ensure_dyntopo_node_undo(ob, node, SCULPT_UNDO_NO_TYPE, 0); + break; + } + + /* Tag verts/edges for deletion. */ + BMFace *f; + BM_ITER_MESH (f, &iter, ss->bm, BM_FACES_OF_MESH) { + if (!BM_elem_flag_test(f, BM_ELEM_TAG)) { + continue; + } + + BMLoop *l = f->l_first; + do { + BM_elem_flag_enable(l->v, BM_ELEM_TAG); + BM_elem_flag_enable(l->e, BM_ELEM_TAG); + } while ((l = l->next) != f->l_first); + } + + /* Untag any shared verts/edges we want to keep. */ + BM_ITER_MESH (f, &iter, ss->bm, BM_FACES_OF_MESH) { + if (BM_elem_flag_test(f, BM_ELEM_TAG)) { + continue; + } + + BMLoop *l = f->l_first; + do { + BM_elem_flag_disable(l->v, BM_ELEM_TAG); + BM_elem_flag_disable(l->e, BM_ELEM_TAG); + } while ((l = l->next) != f->l_first); + } + + BMVert *v; + BM_ITER_MESH (v, &iter, ss->bm, BM_VERTS_OF_MESH) { + if (BM_elem_flag_test(v, BM_ELEM_TAG)) { + BKE_pbvh_bmesh_remove_vertex(ss->pbvh, v, false); + } + } + + BM_ITER_MESH (f, &iter, ss->bm, BM_FACES_OF_MESH) { + if (!BM_elem_flag_test(f, BM_ELEM_TAG)) { + continue; + } + + int ni = BM_ELEM_CD_GET_INT(f, cd_face_node); + BKE_pbvh_bmesh_remove_face(ss->pbvh, f, true); + + BM_idmap_release(ss->bm_idmap, reinterpret_cast(f), true); + BM_face_kill(bm, f); + } + + BMEdge *e; + BM_ITER_MESH (e, &iter, ss->bm, BM_EDGES_OF_MESH) { + if (BM_elem_flag_test(e, BM_ELEM_TAG)) { + BM_log_edge_removed(ss->bm, ss->bm_log, e); + BM_idmap_release(ss->bm_idmap, reinterpret_cast(e), true); + BM_edge_kill(bm, e); + } + } + + BM_ITER_MESH (v, &iter, ss->bm, BM_VERTS_OF_MESH) { + if (BM_elem_flag_test(v, BM_ELEM_TAG)) { + BM_log_vert_removed(ss->bm, ss->bm_log, v); + BM_idmap_release(ss->bm_idmap, reinterpret_cast(v), true); + BM_vert_kill(bm, v); + } + } + + ss->totfaces = bm->totface; + ss->totvert = bm->totvert; + + BKE_pbvh_bmesh_after_stroke(ss->pbvh, true); + BKE_pbvh_update_bounds(ss->pbvh, PBVH_UpdateBB | PBVH_UpdateOriginalBB); +} + static void sculpt_face_set_delete_geometry(Object *ob, SculptSession *ss, const int active_face_set_id, @@ -1215,15 +1353,34 @@ static void sculpt_face_set_delete_geometry(Object *ob, { Mesh *mesh = static_cast(ob->data); - const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(mesh); - BMeshCreateParams create_params{}; - create_params.use_toolflags = true; - BMesh *bm = BM_mesh_create(&allocsize, &create_params); + BMesh *bm; - BMeshFromMeshParams convert_params{}; - convert_params.calc_vert_normal = true; - convert_params.calc_face_normal = true; - BM_mesh_bm_from_me(bm, mesh, &convert_params); + if (ss->bm) { + bm = ss->bm; + BM_mesh_toolflags_set(bm, true); + BM_idmap_check_attributes(ss->bm_idmap); + BKE_sculptsession_update_attr_refs(ob); + } + else { + const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(mesh); + BMeshCreateParams create_params{}; + create_params.use_toolflags = true; + + bm = BM_mesh_create(&allocsize, &create_params); + + BMeshFromMeshParams convert_params{}; + convert_params.calc_vert_normal = true; + convert_params.calc_face_normal = true; + + BM_mesh_bm_from_me(bm, mesh, &convert_params); + } + + int cd_fset_offset = CustomData_get_offset_named( + &bm->pdata, CD_PROP_INT32, SCULPT_ATTRIBUTE_NAME(face_set)); + + if (cd_fset_offset == -1) { + return; + } BM_mesh_elem_table_init(bm, BM_FACE); BM_mesh_elem_table_ensure(bm, BM_FACE); @@ -1231,20 +1388,35 @@ static void sculpt_face_set_delete_geometry(Object *ob, BMIter iter; BMFace *f; BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { - const int face_index = BM_elem_index_get(f); - if (!modify_hidden && ss->hide_poly && ss->hide_poly[face_index]) { + if (!modify_hidden && BM_elem_flag_test(f, BM_ELEM_HIDDEN)) { continue; } - BM_elem_flag_set(f, BM_ELEM_TAG, ss->face_sets[face_index] == active_face_set_id); + + int fset = BM_ELEM_CD_GET_INT(f, cd_fset_offset); + BM_elem_flag_set(f, BM_ELEM_TAG, fset == active_face_set_id); } - BM_mesh_delete_hflag_context(bm, BM_ELEM_TAG, DEL_FACES); + + if (ss->bm) { + sculpt_face_set_delete_geometry_bmesh(ob, bm); + } + else { + BM_mesh_delete_hflag_context(bm, BM_ELEM_TAG, DEL_FACES); + } + BM_mesh_elem_hflag_disable_all(bm, BM_VERT | BM_EDGE | BM_FACE, BM_ELEM_TAG, false); - BMeshToMeshParams bmesh_to_mesh_params{}; - bmesh_to_mesh_params.calc_object_remap = false; - BM_mesh_bm_to_me(nullptr, bm, mesh, &bmesh_to_mesh_params); + if (ss->bm) { + BM_mesh_toolflags_set(bm, false); + BM_idmap_check_attributes(ss->bm_idmap); + BKE_sculptsession_update_attr_refs(ob); + } + else { + BMeshToMeshParams bmesh_to_mesh_params{}; + bmesh_to_mesh_params.calc_object_remap = false; + BM_mesh_bm_to_me(nullptr, bm, mesh, &bmesh_to_mesh_params); - BM_mesh_free(bm); + BM_mesh_free(bm); + } } static void sculpt_face_set_edit_fair_face_set(Object *ob, @@ -1263,6 +1435,7 @@ static void sculpt_face_set_edit_fair_face_set(Object *ob, fair_verts.resize(totvert); SCULPT_boundary_info_ensure(ob); + SCULPT_vertex_random_access_ensure(ss); for (int i = 0; i < totvert; i++) { PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); @@ -1273,16 +1446,39 @@ static void sculpt_face_set_edit_fair_face_set(Object *ob, SCULPT_vertex_has_unique_face_set(ss, vertex); } - float(*positions)[3] = SCULPT_mesh_deformed_positions_get(ss); - BKE_mesh_prefair_and_fair_verts(mesh, positions, fair_verts.data(), fair_order); + float(*positions)[3] = nullptr; + + if (ss->bm) { + BKE_bmesh_prefair_and_fair_verts(ss->bm, fair_verts.data(), fair_order); + } + else { + positions = SCULPT_mesh_deformed_positions_get(ss); + BKE_mesh_prefair_and_fair_verts(mesh, positions, fair_verts.data(), fair_order); + } for (int i = 0; i < totvert; i++) { + PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); + float *co = ss->bm ? reinterpret_cast(vertex.i)->co : positions[i]; + if (fair_verts[i]) { - interp_v3_v3v3(positions[i], orig_positions[i], positions[i], strength); + interp_v3_v3v3(co, orig_positions[i], co, strength); } } } +static Array save_face_sets(SculptSession *ss) +{ + Array prev_face_sets(ss->totfaces); + + for (int i = 0; i < ss->totfaces; i++) { + PBVHFaceRef face = BKE_pbvh_index_to_face(ss->pbvh, i); + + prev_face_sets[i] = ss->attrs.face_set ? SCULPT_face_set_get(ss, face) : 0; + } + + return prev_face_sets; +} + static void sculpt_face_set_apply_edit(Object *ob, const int active_face_set_id, const int mode, @@ -1293,15 +1489,13 @@ static void sculpt_face_set_apply_edit(Object *ob, switch (mode) { case SCULPT_FACE_SET_EDIT_GROW: { - int *prev_face_sets = static_cast(MEM_dupallocN(ss->face_sets)); - sculpt_face_set_grow(ob, ss, prev_face_sets, active_face_set_id, modify_hidden); - MEM_SAFE_FREE(prev_face_sets); + sculpt_face_set_grow_shrink( + ob, ss, save_face_sets(ss), active_face_set_id, modify_hidden, true); break; } case SCULPT_FACE_SET_EDIT_SHRINK: { - int *prev_face_sets = static_cast(MEM_dupallocN(ss->face_sets)); - sculpt_face_set_shrink(ob, ss, prev_face_sets, active_face_set_id, modify_hidden); - MEM_SAFE_FREE(prev_face_sets); + sculpt_face_set_grow_shrink( + ob, ss, save_face_sets(ss), active_face_set_id, modify_hidden, false); break; } case SCULPT_FACE_SET_EDIT_DELETE_GEOMETRY: @@ -1322,20 +1516,15 @@ static bool sculpt_face_set_edit_is_operation_valid(SculptSession *ss, const eSculptFaceSetEditMode mode, const bool modify_hidden) { - if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) { - /* Dyntopo is not supported. */ - return false; - } - if (mode == SCULPT_FACE_SET_EDIT_DELETE_GEOMETRY) { if (BKE_pbvh_type(ss->pbvh) == PBVH_GRIDS) { /* Modification of base mesh geometry requires special remapping of multi-resolution * displacement, which does not happen here. - * Disable delete operation. It can be supported in the future by doing similar displacement - * data remapping as what happens in the mesh edit mode. */ + * Disable delete operation. It can be supported in the future by doing similar + * displacement data remapping as what happens in the mesh edit mode. */ return false; } - if (check_single_face_set(ss, ss->face_sets, !modify_hidden)) { + if (check_single_face_set(ss, !modify_hidden)) { /* Cancel the operator if the mesh only contains one Face Set to avoid deleting the * entire object. */ return false; @@ -1361,13 +1550,30 @@ static void sculpt_face_set_edit_modify_geometry(bContext *C, const bool modify_hidden, wmOperator *op) { + SculptSession *ss = ob->sculpt; Mesh *mesh = static_cast(ob->data); - ED_sculpt_undo_geometry_begin(ob, op); - sculpt_face_set_apply_edit(ob, abs(active_face_set), mode, modify_hidden); - ED_sculpt_undo_geometry_end(ob); - BKE_mesh_batch_cache_dirty_tag(mesh, BKE_MESH_BATCH_DIRTY_ALL); - DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); - WM_event_add_notifier(C, NC_GEOM | ND_DATA, mesh); + + if (!ss->bm) { + ED_sculpt_undo_geometry_begin(ob, op); + } + else { + SCULPT_undo_push_begin(ob, op); + } + + sculpt_face_set_apply_edit(ob, active_face_set, mode, modify_hidden); + + if (!ss->bm) { + ED_sculpt_undo_geometry_end(ob); + } + else { + SCULPT_undo_push_end(ob); + } + + if (BKE_pbvh_type(ob->sculpt->pbvh) != PBVH_BMESH) { + BKE_mesh_batch_cache_dirty_tag(mesh, BKE_MESH_BATCH_DIRTY_ALL); + DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_GEOM | ND_DATA, mesh); + } } static void face_set_edit_do_post_visibility_updates(Object *ob, Span nodes) @@ -1453,8 +1659,8 @@ static bool sculpt_face_set_edit_init(bContext *C, wmOperator *op) return false; } - ss->face_sets = BKE_sculpt_face_sets_ensure(ob); BKE_sculpt_update_object_for_edit(depsgraph, ob, true, false, false); + BKE_sculpt_face_sets_ensure(ob); return true; } @@ -1467,6 +1673,9 @@ static int sculpt_face_set_edit_exec(bContext *C, wmOperator *op) Object *ob = CTX_data_active_object(C); + SCULPT_vertex_random_access_ensure(ob->sculpt); + SCULPT_face_random_access_ensure(ob->sculpt); + const int active_face_set = RNA_int_get(op->ptr, "active_face_set"); const eSculptFaceSetEditMode mode = static_cast( RNA_enum_get(op->ptr, "mode")); diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.hh b/source/blender/editors/sculpt_paint/sculpt_intern.hh index fbb5db95e8c..7fcc487df46 100644 --- a/source/blender/editors/sculpt_paint/sculpt_intern.hh +++ b/source/blender/editors/sculpt_paint/sculpt_intern.hh @@ -1341,7 +1341,12 @@ bool SCULPT_vertex_all_faces_visible_get(const SculptSession *ss, PBVHVertRef ve bool SCULPT_vertex_any_face_visible_get(SculptSession *ss, PBVHVertRef vertex); void SCULPT_face_visibility_all_invert(SculptSession *ss); -void SCULPT_face_visibility_all_set(SculptSession *ss, bool visible); +void SCULPT_face_visibility_all_set(Object *ob, bool visible); + +/* Flags all the vertices of face for boundary update. For PBVH_GRIDS + * this includes all the verts in all the grids belonging to that face. + */ +void SCULPT_face_mark_boundary_update(SculptSession *ss, PBVHFaceRef face); void SCULPT_visibility_sync_all_from_faces(Object *ob); diff --git a/source/blender/editors/sculpt_paint/sculpt_ops.cc b/source/blender/editors/sculpt_paint/sculpt_ops.cc index 30e90d9c5dc..9951d17d969 100644 --- a/source/blender/editors/sculpt_paint/sculpt_ops.cc +++ b/source/blender/editors/sculpt_paint/sculpt_ops.cc @@ -1999,7 +1999,7 @@ static int sculpt_reveal_all_exec(bContext *C, wmOperator *op) BM_log_face_modified(ss->bm, ss->bm_log, f); } - SCULPT_face_visibility_all_set(ss, true); + SCULPT_face_visibility_all_set(ob, true); } SCULPT_visibility_sync_all_from_faces(ob); diff --git a/source/blender/editors/sculpt_paint/sculpt_smooth.cc b/source/blender/editors/sculpt_paint/sculpt_smooth.cc index 51c698e8cdf..30230ca4ec3 100644 --- a/source/blender/editors/sculpt_paint/sculpt_smooth.cc +++ b/source/blender/editors/sculpt_paint/sculpt_smooth.cc @@ -692,6 +692,7 @@ void SCULPT_smooth( int iteration, count; float last; + SCULPT_boundary_info_ensure(ob); SCULPT_smooth_undo_push(ob, nodes); /* PBVH_FACES needs ss->epmap. */ @@ -917,6 +918,7 @@ void SCULPT_do_surface_smooth_brush(Sculpt *sd, Object *ob, Span nod { Brush *brush = BKE_paint_brush(&sd->paint); + SCULPT_boundary_info_ensure(ob); SCULPT_smooth_undo_push(ob, nodes); /* Threaded loop over nodes. */ diff --git a/source/blender/editors/sculpt_paint/sculpt_undo.cc b/source/blender/editors/sculpt_paint/sculpt_undo.cc index cfe8a277b0c..00895aee128 100644 --- a/source/blender/editors/sculpt_paint/sculpt_undo.cc +++ b/source/blender/editors/sculpt_paint/sculpt_undo.cc @@ -384,9 +384,7 @@ static void update_cb_partial(PBVHNode *node, void *userdata) BKE_pbvh_face_iter_begin (data->pbvh, node, fd) { if (data->modified_face_set_faces[fd.index]) { - for (int i = 0; i < fd.verts_num; i++) { - BKE_sculpt_boundary_flag_update(data->ss, fd.verts[i]); - } + SCULPT_face_mark_boundary_update(data->ss, fd.face); if (!updated) { BKE_pbvh_node_mark_update_face_sets(node); @@ -1377,7 +1375,9 @@ static int sculpt_undo_bmesh_restore( break; } - BKE_pbvh_flush_tri_areas(ss->pbvh); + if (ss->pbvh && BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) { + BKE_pbvh_flush_tri_areas(ss->pbvh); + } if (set_active_vertex && ss->bm_log && ss->bm) { if (ss->active_face.i != -1) { @@ -2162,13 +2162,12 @@ void SCULPT_undo_ensure_bmlog(Object *ob) SculptSession *ss = ob->sculpt; Mesh *me = BKE_object_get_original_mesh(ob); - /*log exists or not in sculpt mode? good then*/ - if (ss->bm_log || !ob->sculpt) { + /* Log exists or object is not in sculpt mode? */ + if (!ss || ss->bm_log) { return; } - /*try to find log from entries in the undo stack*/ - + /* Try to find log from entries in the undo stack. */ UndoStack *ustack = ED_undo_stack_get(); if (!ustack) { @@ -2704,13 +2703,12 @@ static void sculpt_undo_push_begin_ex(Object *ob, const char *name, bool no_firs SculptSession *ss = ob->sculpt; - /*when pusing an undo node after - undoing to the start of the stack - the log ref count hits zero, we have to check it, - do cleanup and recreate it*/ + /* When pushing an undo node after undoing to the start of the stack + * the log ref count hits zero; we must detect this and handle it. + */ if (ss && ss->bm && ss->bm_log && BM_log_is_dead(ss->bm_log)) { - // forcibly destroy all entries? the 'true' parameter + /* Forcibly destroy all entries (the 'true' parameter). */ BM_log_free(ss->bm_log, true); BKE_sculpt_ensure_idmap(ob); -- 2.30.2 From 61fe3ae8d2728e3bdcf9bbe2b3d9c50b9081e233 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Wed, 17 May 2023 14:06:06 -0700 Subject: [PATCH 099/279] temp-sculpt-dyntopo: Fix undoing past first undo step locking the mesh --- source/blender/blenkernel/intern/paint.cc | 50 ++++++++++++------- .../sculpt_paint/sculpt_brush_types.cc | 2 +- .../editors/sculpt_paint/sculpt_undo.cc | 2 +- 3 files changed, 35 insertions(+), 19 deletions(-) diff --git a/source/blender/blenkernel/intern/paint.cc b/source/blender/blenkernel/intern/paint.cc index c410f42322e..fb2a22d3c34 100644 --- a/source/blender/blenkernel/intern/paint.cc +++ b/source/blender/blenkernel/intern/paint.cc @@ -1385,7 +1385,10 @@ bool paint_calculate_rake_rotation(UnifiedPaintSettings *ups, return ok; } -static bool sculpt_boundary_flags_ensure(Object *ob, PBVH *pbvh, int totvert) +static bool sculpt_boundary_flags_ensure(Object *ob, + PBVH *pbvh, + int totvert, + bool force_update = false) { SculptSession *ss = ob->sculpt; bool ret = false; @@ -1403,12 +1406,15 @@ static bool sculpt_boundary_flags_ensure(Object *ob, PBVH *pbvh, int totvert) ¶ms, BKE_pbvh_type(pbvh)); + force_update = true; + ret = true; + } + + if (force_update) { for (int i = 0; i < totvert; i++) { PBVHVertRef vertex = BKE_pbvh_index_to_vertex(pbvh, i); BKE_sculpt_boundary_flag_update(ss, vertex); } - - ret = true; } BKE_pbvh_set_boundary_flags(pbvh, reinterpret_cast(ss->attrs.boundary_flags->data)); @@ -2714,9 +2720,10 @@ PBVH *BKE_sculpt_object_pbvh_ensure(Depsgraph *depsgraph, Object *ob) return nullptr; } + SculptSession *ss = ob->sculpt; Scene *scene = DEG_get_input_scene(depsgraph); - PBVH *pbvh = ob->sculpt->pbvh; + PBVH *pbvh = ss->pbvh; if (pbvh != nullptr) { /* NOTE: It is possible that pointers to grids or other geometry data changed. Need to update * those pointers. */ @@ -2746,13 +2753,13 @@ PBVH *BKE_sculpt_object_pbvh_ensure(Depsgraph *depsgraph, Object *ob) return pbvh; } - ob->sculpt->islands_valid = false; + ss->islands_valid = false; - if (ob->sculpt->bm != nullptr) { + if (ss->bm != nullptr) { /* Sculpting on a BMesh (dynamic-topology) gets a special PBVH. */ pbvh = build_pbvh_for_dynamic_topology(ob, false); - ob->sculpt->pbvh = pbvh; + ss->pbvh = pbvh; } else { /* Detect if we are loading from an undo memfile step. */ @@ -2771,15 +2778,27 @@ PBVH *BKE_sculpt_object_pbvh_ensure(Depsgraph *depsgraph, Object *ob) params.copy_temp_cdlayers = true; BM_mesh_bm_from_me(bm, mesh_orig, ¶ms); + BM_mesh_elem_table_ensure(bm, BM_VERT | BM_EDGE | BM_FACE); - ob->sculpt->bm = bm; + ss->bm = bm; SCULPT_undo_ensure_bmlog(ob); /* Note build_pbvh_for_dynamic_topology respects the pbvh cache. */ - pbvh = build_pbvh_for_dynamic_topology(ob, true); + pbvh = ss->pbvh = build_pbvh_for_dynamic_topology(ob, true); + + if (!CustomData_has_layer(&ss->bm->vdata, CD_PAINT_MASK)) { + BM_data_layer_add(ss->bm, &ss->bm->vdata, CD_PAINT_MASK); + } BKE_sculptsession_update_attr_refs(ob); + BKE_sculpt_ensure_origco(ob); + BKE_sculpt_ensure_sculpt_layers(ob); + + BKE_sculpt_init_flags_valence(ob, pbvh, bm->totvert, true); + blender::bke::paint::load_all_original(ob); + + sculpt_boundary_flags_ensure(ob, pbvh, ss->bm->totvert); } else { Object *object_eval = DEG_get_evaluated_object(depsgraph, ob); @@ -2794,17 +2813,14 @@ PBVH *BKE_sculpt_object_pbvh_ensure(Depsgraph *depsgraph, Object *ob) } } - if (!ob->sculpt->pmap) { + if (!ss->pmap) { Mesh *me = BKE_object_get_original_mesh(ob); - BKE_mesh_vert_poly_map_create(&ob->sculpt->pmap, - &ob->sculpt->pmap_mem, - me->polys(), - me->corner_verts().data(), - me->totvert); + BKE_mesh_vert_poly_map_create( + &ss->pmap, &ss->pmap_mem, me->polys(), me->corner_verts().data(), me->totvert); } - BKE_pbvh_set_pmap(pbvh, ob->sculpt->pmap, ob->sculpt->pmap_mem); - ob->sculpt->pbvh = pbvh; + BKE_pbvh_set_pmap(pbvh, ss->pmap, ss->pmap_mem); + ss->pbvh = pbvh; sculpt_attribute_update_refs(ob); diff --git a/source/blender/editors/sculpt_paint/sculpt_brush_types.cc b/source/blender/editors/sculpt_paint/sculpt_brush_types.cc index 35738b4bc93..678423666a3 100644 --- a/source/blender/editors/sculpt_paint/sculpt_brush_types.cc +++ b/source/blender/editors/sculpt_paint/sculpt_brush_types.cc @@ -1621,7 +1621,7 @@ void SCULPT_do_layer_brush(Sculpt *sd, Object *ob, Span nodes) data.brush = brush; data.nodes = nodes; - if (!use_persistent_base) { + if (!use_persistent_base && !ss->attrs.layer_displayment) { SculptAttributeParams params = {}; ss->attrs.layer_displayment = BKE_sculpt_attribute_ensure( diff --git a/source/blender/editors/sculpt_paint/sculpt_undo.cc b/source/blender/editors/sculpt_paint/sculpt_undo.cc index 00895aee128..9a8d9e8a37b 100644 --- a/source/blender/editors/sculpt_paint/sculpt_undo.cc +++ b/source/blender/editors/sculpt_paint/sculpt_undo.cc @@ -1334,7 +1334,7 @@ static int sculpt_undo_bmesh_restore( ss->active_vertex.i = -1; } #endif - ss->active_face.i = ss->active_vertex.i = 0; + ss->active_face.i = ss->active_vertex.i = -1; } else { ss->active_face.i = ss->active_vertex.i = -1; -- 2.30.2 From d25e597adbda521ba8b5b00f382dc42ddfbbf8d3 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Wed, 17 May 2023 14:12:22 -0700 Subject: [PATCH 100/279] temp-sculpt-dyntopo: remove dyntopo smooth shading from UI --- scripts/startup/bl_ui/space_view3d_toolbar.py | 1 - 1 file changed, 1 deletion(-) diff --git a/scripts/startup/bl_ui/space_view3d_toolbar.py b/scripts/startup/bl_ui/space_view3d_toolbar.py index 3f8156c5d57..2b5614a7385 100644 --- a/scripts/startup/bl_ui/space_view3d_toolbar.py +++ b/scripts/startup/bl_ui/space_view3d_toolbar.py @@ -972,7 +972,6 @@ class VIEW3D_PT_sculpt_dyntopo(Panel, View3DPaintPanel): expand=True ) - col.prop(sculpt, "use_smooth_shading") col.prop(sculpt, "use_flat_vcol_shading") class VIEW3D_PT_sculpt_voxel_remesh(Panel, View3DPaintPanel): -- 2.30.2 From 83362d46118d069c2b29b0f6808bd39b5964a845 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Thu, 18 May 2023 02:11:12 -0700 Subject: [PATCH 101/279] temp-sculpt-dyntopo: Fix BMLog not saving edge flags properly --- source/blender/bmesh/intern/bmesh_log.cc | 21 ++++++++++++-- .../blender/bmesh/intern/bmesh_log_intern.h | 29 ++----------------- 2 files changed, 20 insertions(+), 30 deletions(-) diff --git a/source/blender/bmesh/intern/bmesh_log.cc b/source/blender/bmesh/intern/bmesh_log.cc index efeb90e27ff..4f031c6b7e0 100644 --- a/source/blender/bmesh/intern/bmesh_log.cc +++ b/source/blender/bmesh/intern/bmesh_log.cc @@ -569,6 +569,8 @@ struct BMLogEntry { if (e->head.data && le->customdata) { CustomData_bmesh_swap_data(&edata, &bm->edata, le->customdata, &e->head.data); } + + std::swap(e->head.hflag, le->flag); } void swap_logface(BMesh *bm, BMID /*id*/, BMFace *f, BMLogFace *lf) @@ -634,11 +636,12 @@ struct BMLogEntry { { BMLogEdge *le = epool.alloc(); - CustomData_bmesh_copy_data(&bm->edata, &edata, e->head.data, &le->customdata); le->id = get_elem_id(bm, e); le->v1 = get_elem_id(bm, e->v1); le->v2 = get_elem_id(bm, e->v2); + update_logedge(bm, e, le); + return le; } @@ -1089,7 +1092,16 @@ void BMLogSetDiff::remove_edge(BMesh *bm, BMEdge *e) removed_edges.add(id, le); } -void BMLogSetDiff::modify_edge(BMesh * /*bm*/, BMEdge * /*e*/) {} +void BMLogSetDiff::modify_edge(BMesh *bm, BMEdge *e) +{ + BMID id = entry->get_elem_id(bm, e); + + if (modified_edges.contains(id)) { + return; + } + + modified_edges.add(id, entry->alloc_logedge(bm, e)); +} void BMLogSetDiff::add_face(BMesh *bm, BMFace *f) { @@ -1259,6 +1271,8 @@ void BMLogSetDiff::restore_edges(BMesh *bm, } BMEdge *e = BM_edge_create(bm, v1, v2, nullptr, BM_CREATE_NOP); + e->head.hflag = le->flag; + CustomData_bmesh_copy_data(&entry->edata, &bm->edata, le->customdata, &e->head.data); entry->assign_elem_id(bm, e, le->id, true); @@ -1353,8 +1367,8 @@ void BMLogSetDiff::restore_faces(BMesh *bm, } BMFace *f = BM_face_create_verts(bm, verts.data(), verts.size(), nullptr, BM_CREATE_NOP, true); - f->head.hflag = lf->flag; + CustomData_bmesh_copy_data(&entry->pdata, &bm->pdata, lf->customdata, &f->head.data); entry->assign_elem_id(bm, f, lf->id, true); @@ -1461,6 +1475,7 @@ void BMLogSetDiff::undo(BMesh *bm, BMLogCallbacks *callbacks) restore_faces(bm, removed_faces, callbacks); swap_faces(bm, modified_faces, callbacks); + swap_edges(bm, modified_edges, callbacks); swap_verts(bm, modified_verts, callbacks); } diff --git a/source/blender/bmesh/intern/bmesh_log_intern.h b/source/blender/bmesh/intern/bmesh_log_intern.h index 00e14f9ac5a..999f286aa71 100644 --- a/source/blender/bmesh/intern/bmesh_log_intern.h +++ b/source/blender/bmesh/intern/bmesh_log_intern.h @@ -141,32 +141,6 @@ void BM_log_redo(BMesh *bm, BMLog *log, BMLogCallbacks *callbacks); /* Log a vertex before it is modified */ void BM_log_vert_before_modified(BMesh *bm, BMLog *log, BMVert *v); -/* Log a vertex before it is modified - * - * Before modifying vertex coordinates, masks, or hflags, call this - * function to log its current values. This is better than logging - * after the coordinates have been modified, because only those - * vertices that are modified need to have their original values - * stored. - * - * Handles two separate cases: - * - * If the vertex was added in the current log entry, update the - * vertex in the map of added vertices. - * - * If the vertex already existed prior to the current log entry, a - * separate key/value map of modified vertices is used (using the - * vertex's ID as the key). The values stored in that case are - * the vertex's original state so that an undo can restore the - * previous state. - * - * On undo, the current vertex state will be swapped with the stored - * state so that a subsequent redo operation will restore the newer - * vertex state. - */ -void BM_log_edge_before_modified(BMLog *log, BMEdge *v, bool log_customdata); - -/* Log a new vertex as added to the BMesh */ /* Log a new vertex as added to the BMesh * * The new vertex gets a unique ID assigned. It is then added to a map @@ -178,7 +152,8 @@ void BM_log_vert_added(BMesh *bm, BMLog *log, struct BMVert *v); /* Log a new edge as added to the BMesh */ void BM_log_edge_added(BMesh *bm, BMLog *log, BMEdge *e); -/* Log a face before it is modified */ +void BM_log_edge_modified(BMesh *bm, BMLog *log, BMEdge *e); + /* Log a face before it is modified * * This is intended to handle only header flags and we always -- 2.30.2 From ab46277a5d28845159b640dac79b6001238b3293 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Thu, 18 May 2023 13:55:41 -0700 Subject: [PATCH 102/279] temp-sculpt-boundary: New boundary type: sharp-angle edges * Detects sharp edges by face angles. * Sharp-angle boundaries are respected by dyntopo only for now. --- scripts/startup/bl_ui/space_view3d_toolbar.py | 9 ++ source/blender/blenkernel/BKE_paint.h | 27 ++-- source/blender/blenkernel/BKE_pbvh.h | 6 +- source/blender/blenkernel/intern/brush.cc | 3 - source/blender/blenkernel/intern/dyntopo.cc | 47 +++++- .../blenkernel/intern/dyntopo_collapse.cc | 2 +- .../blenkernel/intern/dyntopo_intern.hh | 18 +-- source/blender/blenkernel/intern/paint.cc | 40 +++++ source/blender/blenkernel/intern/pbvh.c | 4 +- source/blender/blenkernel/intern/pbvh.cc | 6 +- .../blender/blenkernel/intern/pbvh_bmesh.cc | 137 ++++++++++-------- .../blender/blenkernel/intern/pbvh_intern.hh | 26 ++-- .../blenloader/intern/versioning_300.cc | 17 +++ source/blender/editors/sculpt_paint/sculpt.cc | 20 ++- .../editors/sculpt_paint/sculpt_api.cc | 34 +++-- .../sculpt_paint/sculpt_brush_types.cc | 2 + .../sculpt_paint/sculpt_filter_mesh.cc | 1 + .../editors/sculpt_paint/sculpt_ops.cc | 4 +- .../editors/sculpt_paint/sculpt_smooth.cc | 69 ++++++++- source/blender/makesdna/DNA_brush_defaults.h | 1 + source/blender/makesdna/DNA_brush_types.h | 2 +- source/blender/makesdna/DNA_scene_defaults.h | 4 +- source/blender/makesdna/DNA_scene_types.h | 9 +- source/blender/makesrna/intern/rna_brush.c | 7 + source/blender/makesrna/intern/rna_scene.c | 12 ++ 25 files changed, 366 insertions(+), 141 deletions(-) diff --git a/scripts/startup/bl_ui/space_view3d_toolbar.py b/scripts/startup/bl_ui/space_view3d_toolbar.py index 2b5614a7385..f759312af21 100644 --- a/scripts/startup/bl_ui/space_view3d_toolbar.py +++ b/scripts/startup/bl_ui/space_view3d_toolbar.py @@ -1025,6 +1025,7 @@ class VIEW3D_PT_sculpt_options(Panel, View3DPaintPanel): tool_settings = context.tool_settings sculpt = tool_settings.sculpt + brush = sculpt.brush col = layout.column(heading="Display", align=True) col.prop(sculpt, "show_low_resolution") @@ -1034,6 +1035,14 @@ class VIEW3D_PT_sculpt_options(Panel, View3DPaintPanel): col.prop(context.object.data, "sculpt_ignore_uvs") col.prop(tool_settings.unified_paint_settings, "hard_edge_mode") + UnifiedPaintPanel.prop_unified( + layout, + context, + tool_settings.unified_paint_settings, + "sharp_angle_limit", + unified_name = "use_unified_sharp_angle_limit" + ) + class VIEW3D_PT_sculpt_options_gravity(Panel, View3DPaintPanel): bl_context = ".sculpt_mode" # dot on purpose (access from topbar) bl_parent_id = "VIEW3D_PT_sculpt_options" diff --git a/source/blender/blenkernel/BKE_paint.h b/source/blender/blenkernel/BKE_paint.h index cf4dcb88b6d..0632c41136a 100644 --- a/source/blender/blenkernel/BKE_paint.h +++ b/source/blender/blenkernel/BKE_paint.h @@ -1004,6 +1004,7 @@ struct SculptSession { bool hard_edge_mode; DynTopoSettings cached_dyntopo; + float sharp_angle_limit; }; #else struct SculptSession; @@ -1014,9 +1015,12 @@ typedef enum eSculptBoundary { SCULPT_BOUNDARY_MESH = 1 << 0, SCULPT_BOUNDARY_FACE_SET = 1 << 1, SCULPT_BOUNDARY_SEAM = 1 << 2, - SCULPT_BOUNDARY_SHARP = 1 << 3, - SCULPT_BOUNDARY_UV = 1 << 4, - SCULPT_BOUNDARY_NEEDS_UPDATE = 1 << 5, + SCULPT_BOUNDARY_SHARP_MARK = 1 << 3, /* Edges marked as sharp. */ + SCULPT_BOUNDARY_SHARP_ANGLE = 1 << 4, /* Edges whose face angle is above a limit */ + SCULPT_BOUNDARY_UV = 1 << 5, + SCULPT_BOUNDARY_NEEDS_UPDATE = 1 << 6, + SCULPT_BOUNDARY_UPDATE_SHARP_ANGLE = 1 << 7, + SCULPT_BOUNDARY_ALL = (1 << 0) | (1 << 1) | (1 << 2) | (1 << 3) | (1 << 4), SCULPT_BOUNDARY_DEFAULT = (1 << 0) | (1 << 3) | (1 << 4) // mesh and sharp } eSculptBoundary; @@ -1026,11 +1030,12 @@ ENUM_OPERATORS(eSculptBoundary, SCULPT_BOUNDARY_NEEDS_UPDATE); /* Note: This is stored in a single attribute with boundary flags */ typedef enum eSculptCorner { SCULPT_CORNER_NONE = 0, - SCULPT_CORNER_MESH = 1 << 6, - SCULPT_CORNER_FACE_SET = 1 << 7, - SCULPT_CORNER_SEAM = 1 << 8, - SCULPT_CORNER_SHARP = 1 << 9, - SCULPT_CORNER_UV = 1 << 10, + SCULPT_CORNER_MESH = 1 << 10, + SCULPT_CORNER_FACE_SET = 1 << 11, + SCULPT_CORNER_SEAM = 1 << 12, + SCULPT_CORNER_SHARP_MARK = 1 << 13, + SCULPT_CORNER_SHARP_ANGLE = 1 << 14, + SCULPT_CORNER_UV = 1 << 15, } eSculptCorner; ENUM_OPERATORS(eSculptCorner, SCULPT_CORNER_UV); @@ -1303,7 +1308,11 @@ void load_all_original(Object *ob); BLI_INLINE void BKE_sculpt_boundary_flag_update(SculptSession *ss, PBVHVertRef vertex) { int *flags = blender::bke::paint::vertex_attr_ptr(vertex, ss->attrs.boundary_flags); - *flags |= SCULPT_BOUNDARY_NEEDS_UPDATE; + *flags |= SCULPT_BOUNDARY_NEEDS_UPDATE | SCULPT_BOUNDARY_UPDATE_SHARP_ANGLE; } +void BKE_sculpt_sharp_boundary_flag_update(SculptSession *ss, + PBVHVertRef vertex, + bool update_ring = false); + #endif diff --git a/source/blender/blenkernel/BKE_pbvh.h b/source/blender/blenkernel/BKE_pbvh.h index caa34f5dbfa..a0bf5d29d23 100644 --- a/source/blender/blenkernel/BKE_pbvh.h +++ b/source/blender/blenkernel/BKE_pbvh.h @@ -1056,6 +1056,7 @@ PBVHNode *BKE_pbvh_node_from_face_bmesh(PBVH *pbvh, struct BMFace *f); PBVHNode *BKE_pbvh_node_from_index(PBVH *pbvh, int node_i); struct BMesh *BKE_pbvh_reorder_bmesh(PBVH *pbvh); +void BKE_pbvh_sharp_limit_set(PBVH *pbvh, float limit); void BKE_pbvh_update_vert_boundary(int cd_faceset_offset, int cd_vert_node_offset, int cd_face_node_offset, @@ -1067,7 +1068,8 @@ void BKE_pbvh_update_vert_boundary(int cd_faceset_offset, int bound_symmetry, const CustomData *ldata, const int totuv, - const bool do_uvs); + const bool do_uvs, + float sharp_angle_limit); #define DYNTOPO_DYNAMIC_TESS @@ -1287,7 +1289,7 @@ void BKE_pbvh_flush_tri_areas(PBVH *pbvh); namespace blender::bke::pbvh { void set_flags_valence(PBVH *pbvh, uint8_t *flags, int *valence); void set_original(PBVH *pbvh, Span origco, Span origno); - +void update_sharp_boundary_bmesh(BMVert *v, int cd_boundary_flag, const float sharp_angle_limit); void update_vert_boundary_faces(int *boundary_flags, const int *face_sets, const bool *hide_poly, diff --git a/source/blender/blenkernel/intern/brush.cc b/source/blender/blenkernel/intern/brush.cc index e2bbfa491c9..472a0f2262d 100644 --- a/source/blender/blenkernel/intern/brush.cc +++ b/source/blender/blenkernel/intern/brush.cc @@ -1796,7 +1796,6 @@ void BKE_brush_sculpt_reset(Brush *br) * assign this so logic below can remain the same. */ br->alpha = 0.5f; - br->flag2 |= BRUSH_SMOOTH_USE_AREA_WEIGHT; bool disable_dyntopo = false; /* Brush settings */ @@ -1817,7 +1816,6 @@ void BKE_brush_sculpt_reset(Brush *br) br->alpha = 1.0f; br->slide_deform_type = BRUSH_SLIDE_DEFORM_DRAG; disable_dyntopo = true; - br->flag2 |= BRUSH_SMOOTH_USE_AREA_WEIGHT; break; case SCULPT_TOOL_CLAY: br->flag |= BRUSH_SIZE_PRESSURE; @@ -1875,7 +1873,6 @@ void BKE_brush_sculpt_reset(Brush *br) br->surface_smooth_current_vertex = 0.5f; br->surface_smooth_iterations = 4; disable_dyntopo = true; - br->flag2 |= BRUSH_SMOOTH_USE_AREA_WEIGHT; break; case SCULPT_TOOL_SNAKE_HOOK: br->alpha = 1.0f; diff --git a/source/blender/blenkernel/intern/dyntopo.cc b/source/blender/blenkernel/intern/dyntopo.cc index 47851d88089..93e286d4a7e 100644 --- a/source/blender/blenkernel/intern/dyntopo.cc +++ b/source/blender/blenkernel/intern/dyntopo.cc @@ -115,11 +115,13 @@ static void surface_smooth_v_safe( pbvh_check_vert_boundary(pbvh, v); } - const int boundflag = BM_ELEM_CD_GET_INT(v, pbvh->cd_boundary_flag); + int boundmask = SCULPTVERT_SMOOTH_BOUNDARY; + int cornermask = SCULPTVERT_SMOOTH_CORNER; - const bool bound1 = boundflag & SCULPTVERT_SMOOTH_BOUNDARY; + int boundflag = BM_ELEM_CD_GET_INT(v, pbvh->cd_boundary_flag); + int bound1 = boundflag & boundmask; - if (boundflag & SCULPTVERT_SMOOTH_CORNER) { + if (boundflag & (cornermask | SCULPT_BOUNDARY_SHARP_ANGLE)) { return; } @@ -127,6 +129,28 @@ static void surface_smooth_v_safe( fac *= 0.1; } + const int sharp_tag = 1 << 13; + float limit = 22.0 / 180.0 * M_PI; + + do { + if (!e->l) { + continue; + } + + BMLoop *l1 = e->l; + BMLoop *l2 = l1->radial_next; + + bool sharp = l1 == l2; + sharp = sharp || saacos(dot_v3v3(l1->f->no, l2->f->no)) > limit; + + if (sharp && (bound1 & sharp_tag)) { + return; /* corner */ + } + else if (sharp) { + bound1 |= sharp_tag; + } + } while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e); + do { BMVert *v2 = e->v1 == v ? e->v2 : e->v1; PBVHVertRef vertex2 = {reinterpret_cast(v2)}; @@ -136,8 +160,15 @@ static void surface_smooth_v_safe( */ int boundflag2 = BM_ELEM_CD_GET_INT(v2, pbvh->cd_boundary_flag); + int bound2 = boundflag2 & boundmask; - const bool bound2 = boundflag2 & SCULPTVERT_SMOOTH_BOUNDARY; + if (e->l) { + bool sharp = e->l == e->l->radial_next; + sharp = sharp || saacos(dot_v3v3(e->l->f->no, e->l->radial_next->f->no)) > limit; + if (sharp) { + bound2 |= sharp_tag; + } + } if (bound1 && !bound2) { continue; @@ -146,7 +177,7 @@ static void surface_smooth_v_safe( sub_v3_v3v3(tan, v2->co, v->co); float d = dot_v3v3(tan, v->no); - madd_v3_v3fl(tan, v->no, -d * 0.99f); + madd_v3_v3fl(tan, v->no, -d * 0.95f); add_v3_v3(co, tan); if (!stroke_id_test_no_update(ss, vertex2, STROKEID_USER_ORIGINAL)) { @@ -158,7 +189,7 @@ static void surface_smooth_v_safe( } d = dot_v3v3(tan, origno1); - madd_v3_v3fl(tan, origno1, -d * 0.99f); + madd_v3_v3fl(tan, origno1, -d * 0.95f); add_v3_v3(origco, tan); tot += 1.0f; @@ -2326,11 +2357,13 @@ extern "C" bool BKE_pbvh_bmesh_update_topology(SculptSession *ss, } #endif +#if 0 printf("%s: subd: %d, cold: %d, ratio: %.3f\n", __func__, count_subd, count_cold, count_cold > 0 ? float(count_subd) / float(count_cold) : 0.0f); +#endif if (etot > 0) { modified = true; @@ -3358,7 +3391,7 @@ void BKE_pbvh_bmesh_add_face(PBVH *pbvh, struct BMFace *f, bool log_face, bool f return; } - /* Look for node in surrounding geometry. */ + /* Look for node in srounding geometry. */ BMLoop *l = f->l_first; do { ni = BM_ELEM_CD_GET_INT(l->radial_next->f, pbvh->cd_face_node_offset); diff --git a/source/blender/blenkernel/intern/dyntopo_collapse.cc b/source/blender/blenkernel/intern/dyntopo_collapse.cc index 5b532393e76..b165171179b 100644 --- a/source/blender/blenkernel/intern/dyntopo_collapse.cc +++ b/source/blender/blenkernel/intern/dyntopo_collapse.cc @@ -407,7 +407,7 @@ bool pbvh_bmesh_collapse_edge_uvs( /*have to check edge flags directly, vertex flag test above isn't specific enough and can sometimes let bad edges through*/ - if ((boundflag1 & SCULPT_BOUNDARY_SHARP) && (e->head.hflag & BM_ELEM_SMOOTH)) { + if ((boundflag1 & SCULPT_BOUNDARY_SHARP_MARK) && (e->head.hflag & BM_ELEM_SMOOTH)) { bm_logstack_pop(); return false; } diff --git a/source/blender/blenkernel/intern/dyntopo_intern.hh b/source/blender/blenkernel/intern/dyntopo_intern.hh index 4b1cb0f7718..2d0163d040c 100644 --- a/source/blender/blenkernel/intern/dyntopo_intern.hh +++ b/source/blender/blenkernel/intern/dyntopo_intern.hh @@ -76,17 +76,17 @@ inline bool bm_elem_is_free(BMElem *elem, int htype) #define SCULPTVERT_VALENCE_TEMP SCULPTFLAG_SPLIT_TEMP #define SCULPTVERT_SMOOTH_BOUNDARY \ - (SCULPT_BOUNDARY_MESH | SCULPT_BOUNDARY_FACE_SET | SCULPT_BOUNDARY_SHARP | \ - SCULPT_BOUNDARY_SEAM | SCULPT_BOUNDARY_UV) -#define SCULPTVERT_ALL_BOUNDARY \ - (SCULPT_BOUNDARY_MESH | SCULPT_BOUNDARY_FACE_SET | SCULPT_BOUNDARY_SHARP | \ - SCULPT_BOUNDARY_SEAM | SCULPT_BOUNDARY_UV) + (SCULPT_BOUNDARY_MESH | SCULPT_BOUNDARY_FACE_SET | SCULPT_BOUNDARY_SHARP_MARK | \ + SCULPT_BOUNDARY_SEAM | SCULPT_BOUNDARY_UV | SCULPT_BOUNDARY_SHARP_ANGLE) #define SCULPTVERT_SMOOTH_CORNER \ - (SCULPT_CORNER_MESH | SCULPT_CORNER_FACE_SET | SCULPT_CORNER_SHARP | SCULPT_CORNER_SEAM | \ - SCULPT_CORNER_UV) + (SCULPT_CORNER_MESH | SCULPT_CORNER_FACE_SET | SCULPT_CORNER_SHARP_MARK | SCULPT_CORNER_SEAM | \ + SCULPT_CORNER_UV | SCULPT_CORNER_SHARP_ANGLE) +#define SCULPTVERT_ALL_BOUNDARY \ + (SCULPT_BOUNDARY_MESH | SCULPT_BOUNDARY_FACE_SET | SCULPT_BOUNDARY_SHARP_MARK | \ + SCULPT_BOUNDARY_SEAM | SCULPT_BOUNDARY_UV | SCULPT_BOUNDARY_SHARP_ANGLE) #define SCULPTVERT_ALL_CORNER \ - (SCULPT_CORNER_MESH | SCULPT_CORNER_FACE_SET | SCULPT_CORNER_SHARP | SCULPT_CORNER_SEAM | \ - SCULPT_CORNER_UV) + (SCULPT_CORNER_MESH | SCULPT_CORNER_FACE_SET | SCULPT_CORNER_SHARP_MARK | SCULPT_CORNER_SEAM | \ + SCULPT_CORNER_UV | SCULPT_CORNER_SHARP_ANGLE) #define DYNTOPO_MAX_ITER 256 diff --git a/source/blender/blenkernel/intern/paint.cc b/source/blender/blenkernel/intern/paint.cc index fb2a22d3c34..47567ae92eb 100644 --- a/source/blender/blenkernel/intern/paint.cc +++ b/source/blender/blenkernel/intern/paint.cc @@ -1776,6 +1776,7 @@ static void sculpt_update_object( { Scene *scene = DEG_get_input_scene(depsgraph); Sculpt *sd = scene->toolsettings->sculpt; + UnifiedPaintSettings &ups = scene->toolsettings->unified_paint_settings; SculptSession *ss = ob->sculpt; Mesh *me = BKE_object_get_original_mesh(ob); Mesh *me_eval = BKE_object_get_evaluated_mesh(ob_eval); @@ -1789,6 +1790,10 @@ static void sculpt_update_object( return; } + Brush *brush = sd->paint.brush; + ss->sharp_angle_limit = (!brush || ups.flag & UNIFIED_PAINT_FLAG_SHARP_ANGLE_LIMIT) ? + ups.sharp_angle_limit : + brush->sharp_angle_limit; ss->depsgraph = depsgraph; ss->bm_smooth_shading = scene->toolsettings->sculpt->flags & SCULPT_DYNTOPO_SMOOTH_SHADING; @@ -1893,6 +1898,10 @@ static void sculpt_update_object( PBVH *pbvh = BKE_sculpt_object_pbvh_ensure(depsgraph, ob); sculpt_check_face_areas(ob, pbvh); + if (pbvh) { + BKE_pbvh_sharp_limit_set(pbvh, ss->sharp_angle_limit); + } + /* Sculpt Face Sets. */ if (use_face_sets) { int *face_sets = static_cast(CustomData_get_layer_named_for_write( @@ -2582,6 +2591,7 @@ static PBVH *build_pbvh_for_dynamic_topology(Object *ob, bool /*update_sculptver if (ob->sculpt->bm_log) { BKE_pbvh_set_bm_log(pbvh, ob->sculpt->bm_log); } + BKE_pbvh_sharp_limit_set(pbvh, ob->sculpt->sharp_angle_limit); BKE_pbvh_set_symmetry(pbvh, 0, (int)BKE_get_fset_boundary_symflag(ob)); @@ -2606,6 +2616,7 @@ static PBVH *build_pbvh_from_regular_mesh(Object *ob, Mesh *me_eval_deform) BKE_pbvh_set_pmap(pbvh, ss->pmap, ss->pmap_mem); BKE_pbvh_build_mesh(pbvh, me); + BKE_pbvh_sharp_limit_set(pbvh, ss->sharp_angle_limit); BKE_pbvh_fast_draw_set(pbvh, ss->fast_draw); const bool is_deformed = check_sculpt_object_deformed(ob, true); @@ -2644,6 +2655,8 @@ static PBVH *build_pbvh_from_ccg(Object *ob, SubdivCCG *subdiv_ccg) base_mesh, subdiv_ccg); + BKE_pbvh_sharp_limit_set(pbvh, ss->sharp_angle_limit); + CustomData_reset(&ob->sculpt->temp_vdata); CustomData_reset(&ob->sculpt->temp_pdata); @@ -2725,6 +2738,8 @@ PBVH *BKE_sculpt_object_pbvh_ensure(Depsgraph *depsgraph, Object *ob) PBVH *pbvh = ss->pbvh; if (pbvh != nullptr) { + BKE_pbvh_sharp_limit_set(pbvh, ss->sharp_angle_limit); + /* NOTE: It is possible that pointers to grids or other geometry data changed. Need to update * those pointers. */ const PBVHType pbvh_type = BKE_pbvh_type(pbvh); @@ -4066,3 +4081,28 @@ void load_all_original(Object *ob) } } // namespace blender::bke::paint + +ATTR_NO_OPT void BKE_sculpt_sharp_boundary_flag_update(SculptSession *ss, + PBVHVertRef vertex, + bool update_ring) +{ + int *flags = blender::bke::paint::vertex_attr_ptr(vertex, ss->attrs.boundary_flags); + *flags |= SCULPT_BOUNDARY_UPDATE_SHARP_ANGLE; + +#if 1 + if (update_ring && ss->bm) { + BMVert *v = reinterpret_cast(vertex.i); + if (!v->e) { + return; + } + + BMEdge *e = v->e; + do { + PBVHVertRef vertex2 = {reinterpret_cast(BM_edge_other_vert(e, v))}; + + int *flags2 = blender::bke::paint::vertex_attr_ptr(vertex2, ss->attrs.boundary_flags); + *flags2 |= SCULPT_BOUNDARY_UPDATE_SHARP_ANGLE; + } while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e); + } +#endif +} diff --git a/source/blender/blenkernel/intern/pbvh.c b/source/blender/blenkernel/intern/pbvh.c index 1c215b9bf39..42686e734b7 100644 --- a/source/blender/blenkernel/intern/pbvh.c +++ b/source/blender/blenkernel/intern/pbvh.c @@ -4850,7 +4850,7 @@ void BKE_pbvh_update_vert_boundary_faces(int *boundary_flags, if (j < mp->totloop) { const MEdge *me = medge + ml->e; if (sharp_edges && sharp_edges[ml->e]) { - *flags |= SCULPT_BOUNDARY_SHARP; + *flags |= SCULPT_BOUNDARY_SHARP_MARK; totsharp++; } @@ -4888,7 +4888,7 @@ void BKE_pbvh_update_vert_boundary_faces(int *boundary_flags, } if (totsharp > 2) { - *flags |= SCULPT_CORNER_SHARP; + *flags |= SCULPT_CORNER_SHARP_MARK; } if (totseam > 2) { diff --git a/source/blender/blenkernel/intern/pbvh.cc b/source/blender/blenkernel/intern/pbvh.cc index 97e53b6e31d..7a5e46fdae7 100644 --- a/source/blender/blenkernel/intern/pbvh.cc +++ b/source/blender/blenkernel/intern/pbvh.cc @@ -4306,7 +4306,7 @@ void update_vert_boundary_faces(int *boundary_flags, int e_index = corner_edges[loopstart + j]; if (sharp_edges && sharp_edges[e_index]) { - *boundary_flag |= SCULPT_BOUNDARY_SHARP; + *boundary_flag |= SCULPT_BOUNDARY_SHARP_MARK; totsharp++; } @@ -4343,8 +4343,8 @@ void update_vert_boundary_faces(int *boundary_flags, *flag |= SCULPTFLAG_VERT_FSET_HIDDEN; } - if (totsharp > 2) { - *boundary_flag |= SCULPT_CORNER_SHARP; + if (!ELEM(totsharp, 0, 2)) { + *boundary_flag |= SCULPT_CORNER_SHARP_MARK; } if (totseam > 2) { diff --git a/source/blender/blenkernel/intern/pbvh_bmesh.cc b/source/blender/blenkernel/intern/pbvh_bmesh.cc index c8b3294321a..f270ede125f 100644 --- a/source/blender/blenkernel/intern/pbvh_bmesh.cc +++ b/source/blender/blenkernel/intern/pbvh_bmesh.cc @@ -2097,17 +2097,19 @@ static int color_boundary_key(float col[4]) } #endif -void bke_pbvh_update_vert_boundary(int cd_faceset_offset, +void BKE_pbvh_update_vert_boundary(int cd_faceset_offset, int cd_vert_node_offset, int cd_face_node_offset, int /*cd_vcol*/, int cd_boundary_flag, - int cd_flag, - int cd_valence, + const int cd_flag, + const int cd_valence, BMVert *v, int bound_symmetry, const CustomData *ldata, - const int totuv) + const int totuv, + const bool do_uvs, + float sharp_angle_limit) { int newflag = *BM_ELEM_CD_PTR(v, cd_flag); int boundflag = 0; @@ -2146,20 +2148,23 @@ void bke_pbvh_update_vert_boundary(int cd_faceset_offset, #endif Vector fsets; - float(*lastuv)[2] = (float(*)[2])BLI_array_alloca(lastuv, totuv); - float(*lastuv2)[2] = (float(*)[2])BLI_array_alloca(lastuv2, totuv); + float(*lastuv)[2] = do_uvs ? (float(*)[2])BLI_array_alloca(lastuv, totuv) : nullptr; + float(*lastuv2)[2] = do_uvs ? (float(*)[2])BLI_array_alloca(lastuv2, totuv) : nullptr; - int *disjount_uv_count = (int *)BLI_array_alloca(disjount_uv_count, totuv); + int *disjount_uv_count = do_uvs ? (int *)BLI_array_alloca(disjount_uv_count, totuv) : nullptr; int *cd_uvs = (int *)BLI_array_alloca(cd_uvs, totuv); int base_uv_idx = ldata->typemap[CD_PROP_FLOAT2]; bool uv_first = true; - for (int i = 0; i < totuv; i++) { - CustomDataLayer *layer = ldata->layers + base_uv_idx + i; - cd_uvs[i] = layer->offset; - disjount_uv_count[i] = 0; + if (do_uvs) { + for (int i = 0; i < totuv; i++) { + CustomDataLayer *layer = ldata->layers + base_uv_idx + i; + cd_uvs[i] = layer->offset; + disjount_uv_count[i] = 0; + } } + int sharp_angle_num = 0; do { BMVert *v2 = v == e->v1 ? e->v2 : e->v1; @@ -2167,6 +2172,12 @@ void bke_pbvh_update_vert_boundary(int cd_faceset_offset, newflag |= SCULPTFLAG_PBVH_BOUNDARY; } + if (e->l) { + if (saacos(dot_v3v3(e->l->f->no, e->l->radial_next->f->no)) > sharp_angle_limit) { + boundflag |= SCULPT_BOUNDARY_SHARP_ANGLE; + sharp_angle_num++; + } + } if (e->head.hflag & BM_ELEM_SEAM) { boundflag |= SCULPT_BOUNDARY_SEAM; seamcount++; @@ -2195,17 +2206,17 @@ void bke_pbvh_update_vert_boundary(int cd_faceset_offset, #endif if (!(e->head.hflag & BM_ELEM_SMOOTH)) { - boundflag |= SCULPT_BOUNDARY_SHARP; + boundflag |= SCULPT_BOUNDARY_SHARP_MARK; sharpcount++; if (sharpcount > 2) { - boundflag |= SCULPT_CORNER_SHARP; + boundflag |= SCULPT_CORNER_SHARP_MARK; } } if (e->l) { /* detect uv island boundaries */ - if (totuv) { + if (do_uvs && totuv) { BMLoop *l_iter = e->l; do { BMLoop *l = l_iter->v != v ? l_iter->next : l_iter; @@ -2326,8 +2337,12 @@ void bke_pbvh_update_vert_boundary(int cd_faceset_offset, boundflag |= SCULPT_CORNER_FACE_SET; } - if (sharpcount == 1) { - boundflag |= SCULPT_CORNER_SHARP; + if (sharp_angle_num > 2) { + boundflag |= SCULPT_CORNER_SHARP_ANGLE; + } + + if (!ELEM(sharpcount, 0, 2)) { + boundflag |= SCULPT_CORNER_SHARP_MARK; } if (seamcount == 1) { @@ -2349,30 +2364,9 @@ bool BKE_pbvh_check_vert_boundary(PBVH *pbvh, BMVert *v) return pbvh_check_vert_boundary(pbvh, v); } -void BKE_pbvh_update_vert_boundary(int cd_faceset_offset, - int cd_vert_node_offset, - int cd_face_node_offset, - int cd_vcol, - int cd_boundary_flag, - int cd_flag, - int cd_valence, - BMVert *v, - int bound_symmetry, - const CustomData *ldata, - const int totuv, - const bool do_uvs) +void BKE_pbvh_sharp_limit_set(PBVH *pbvh, float limit) { - bke_pbvh_update_vert_boundary(cd_faceset_offset, - cd_vert_node_offset, - cd_face_node_offset, - cd_vcol, - cd_boundary_flag, - cd_flag, - cd_valence, - v, - bound_symmetry, - ldata, - do_uvs ? totuv : 0); + pbvh->sharp_angle_limit = limit; } /*Used by symmetrize to update boundary flags*/ @@ -2382,7 +2376,7 @@ void BKE_pbvh_recalc_bmesh_boundary(PBVH *pbvh) BMIter iter; BM_ITER_MESH (v, &iter, pbvh->header.bm, BM_VERTS_OF_MESH) { - bke_pbvh_update_vert_boundary(pbvh->cd_faceset_offset, + BKE_pbvh_update_vert_boundary(pbvh->cd_faceset_offset, pbvh->cd_vert_node_offset, pbvh->cd_face_node_offset, pbvh->cd_vcol_offset, @@ -2392,7 +2386,9 @@ void BKE_pbvh_recalc_bmesh_boundary(PBVH *pbvh) v, pbvh->boundary_symmetry, &pbvh->header.bm->ldata, - pbvh->flags & PBVH_IGNORE_UVS ? 0 : pbvh->totuv); + pbvh->flags & PBVH_IGNORE_UVS ? 0 : pbvh->totuv, + pbvh->flags & PBVH_IGNORE_UVS, + pbvh->sharp_angle_limit); } } @@ -2412,8 +2408,8 @@ void BKE_pbvh_build_bmesh(PBVH *pbvh, const int cd_face_node_offset, const int cd_face_areas, const int cd_boundary_flag, - const int cd_flag_offset, - const int cd_valence_offset, + const int /*cd_flag_offset*/, + const int /*cd_valence_offset*/, const int cd_origco, const int cd_origno, bool fast_draw) @@ -3531,23 +3527,6 @@ static void pbvh_bmesh_compact_tree(PBVH *bvh) MEM_freeN(map); } -static void recursive_delete_nodes(PBVH *pbvh, int ni) -{ - PBVHNode *node = pbvh->nodes + ni; - - node->flag |= PBVH_Delete; - - if (!(node->flag & PBVH_Leaf) && node->children_offset) { - if (node->children_offset < pbvh->totnode) { - recursive_delete_nodes(pbvh, node->children_offset); - } - - if (node->children_offset + 1 < pbvh->totnode) { - recursive_delete_nodes(pbvh, node->children_offset + 1); - } - } -} - /* Prunes leaf nodes that are too small or degenerate. */ static void pbvh_bmesh_balance_tree(PBVH *pbvh) { @@ -4932,3 +4911,41 @@ void BKE_pbvh_bmesh_set_toolflags(PBVH *pbvh, bool use_toolflags) /* Customdata layout might've changed. */ pbvh_bmesh_fetch_cdrefs(pbvh); } + +float BKE_pbvh_bmesh_detail_size_avg_get(PBVH *pbvh) +{ + return (pbvh->bm_max_edge_len + pbvh->bm_min_edge_len) * 0.5f; +} + +namespace blender::bke::pbvh { +void update_sharp_boundary_bmesh(BMVert *v, int cd_boundary_flag, const float sharp_angle_limit) +{ + int flag = BM_ELEM_CD_GET_INT(v, cd_boundary_flag); + flag &= ~(SCULPT_BOUNDARY_UPDATE_SHARP_ANGLE | SCULPT_BOUNDARY_SHARP_ANGLE | + SCULPT_CORNER_SHARP_ANGLE); + + if (!v->e) { + return; + } + + int sharp_num = 0; + + BMEdge *e = v->e; + do { + if (!e->l || e->l == e->l->radial_next) { + continue; + } + + if (saacos(dot_v3v3(e->l->f->no, e->l->radial_next->f->no)) > sharp_angle_limit) { + flag |= SCULPT_BOUNDARY_SHARP_ANGLE; + sharp_num++; + } + } while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e); + + if (sharp_num > 2) { + flag |= SCULPT_CORNER_SHARP_ANGLE; + } + + BM_ELEM_CD_SET_INT(v, cd_boundary_flag, flag); +} +} // namespace blender::bke::pbvh diff --git a/source/blender/blenkernel/intern/pbvh_intern.hh b/source/blender/blenkernel/intern/pbvh_intern.hh index c12c552ba80..ec249a97bce 100644 --- a/source/blender/blenkernel/intern/pbvh_intern.hh +++ b/source/blender/blenkernel/intern/pbvh_intern.hh @@ -299,6 +299,7 @@ struct PBVH { PBVHPixels pixels; bool show_orig; + float sharp_angle_limit; }; /* pbvh.cc */ @@ -437,24 +438,13 @@ void pbvh_bmesh_check_nodes_simple(PBVH *pbvh); void bke_pbvh_insert_face_finalize(PBVH *pbvh, BMFace *f, const int ni); void bke_pbvh_insert_face(PBVH *pbvh, struct BMFace *f); -void bke_pbvh_update_vert_boundary(int cd_faceset_offset, - int cd_vert_node_offset, - int cd_face_node_offset, - int cd_vcol_offset, - int cd_boundary_flags, - int cd_flags, - int cd_valence, - BMVert *v, - int bound_symmetry, - const struct CustomData *ldata, - const int totuv); BLI_INLINE bool pbvh_check_vert_boundary(PBVH *pbvh, struct BMVert *v) { - int *flag = (int *)BM_ELEM_CD_GET_VOID_P(v, pbvh->cd_boundary_flag); + int flag = BM_ELEM_CD_GET_INT(v, pbvh->cd_boundary_flag); - if (*flag & SCULPT_BOUNDARY_NEEDS_UPDATE) { - bke_pbvh_update_vert_boundary(pbvh->cd_faceset_offset, + if (flag & SCULPT_BOUNDARY_NEEDS_UPDATE) { + BKE_pbvh_update_vert_boundary(pbvh->cd_faceset_offset, pbvh->cd_vert_node_offset, pbvh->cd_face_node_offset, pbvh->cd_vcol_offset, @@ -464,9 +454,15 @@ BLI_INLINE bool pbvh_check_vert_boundary(PBVH *pbvh, struct BMVert *v) v, pbvh->boundary_symmetry, &pbvh->header.bm->ldata, - pbvh->flags & PBVH_IGNORE_UVS ? 0 : pbvh->totuv); + pbvh->flags & PBVH_IGNORE_UVS ? 0 : pbvh->totuv, + pbvh->flags & PBVH_IGNORE_UVS, + pbvh->sharp_angle_limit); return true; } + else if (flag & SCULPT_BOUNDARY_UPDATE_SHARP_ANGLE) { + blender::bke::pbvh::update_sharp_boundary_bmesh( + v, pbvh->cd_boundary_flag, pbvh->sharp_angle_limit); + } return false; } diff --git a/source/blender/blenloader/intern/versioning_300.cc b/source/blender/blenloader/intern/versioning_300.cc index 66d63b3383a..bf0c2eec12a 100644 --- a/source/blender/blenloader/intern/versioning_300.cc +++ b/source/blender/blenloader/intern/versioning_300.cc @@ -4260,6 +4260,23 @@ void blo_do_versions_300(FileData *fd, Library * /*lib*/, Main *bmain) } } } + if (!DNA_struct_elem_find(fd->filesdna, "UnifiedPaintSettings", "float", "sharp_angle_limit")) { + LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) { + if (!scene->toolsettings) { + continue; + } + + scene->toolsettings->unified_paint_settings.flag |= UNIFIED_PAINT_FLAG_HARD_EDGE_MODE | + UNIFIED_PAINT_HARD_CORNER_PIN | + UNIFIED_PAINT_FLAG_SHARP_ANGLE_LIMIT; + scene->toolsettings->unified_paint_settings.sharp_angle_limit = + (DNA_struct_default_get(UnifiedPaintSettings))->sharp_angle_limit; + } + + LISTBASE_FOREACH (Brush *, brush, &bmain->brushes) { + brush->sharp_angle_limit = (DNA_struct_default_get(Brush))->sharp_angle_limit; + } + } if (!DNA_struct_elem_find(fd->filesdna, "Sculpt", "DynTopoSettings", "dyntopo")) { LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) { diff --git a/source/blender/editors/sculpt_paint/sculpt.cc b/source/blender/editors/sculpt_paint/sculpt.cc index dbb6fda1758..56e93b3482e 100644 --- a/source/blender/editors/sculpt_paint/sculpt.cc +++ b/source/blender/editors/sculpt_paint/sculpt.cc @@ -4543,22 +4543,30 @@ static void sculpt_combine_proxies_node(Object &object, BKE_pbvh_vertex_iter_begin (ss->pbvh, &node, vd, PBVH_ITER_UNIQUE) { float val[3]; - if (use_orco) { - copy_v3_v3(val, SCULPT_vertex_origco_get(ss, vd.vertex)); - } - else { - copy_v3_v3(val, vd.co); - } + zero_v3(val); for (int p = 0; p < proxy_count; p++) { add_v3_v3(val, proxies[p].co[vd.i]); } + bool modified = len_squared_v3(val) > 0.0f; + + if (use_orco) { + add_v3_v3(val, SCULPT_vertex_origco_get(ss, vd.vertex)); + } + else { + add_v3_v3(val, vd.co); + } + SCULPT_clip(&sd, ss, vd.co, val); if (ss->deform_modifiers_active) { sculpt_flush_pbvhvert_deform(*ss, vd, positions); } + + if (modified) { + BKE_sculpt_sharp_boundary_flag_update(ss, vd.vertex); + } } BKE_pbvh_vertex_iter_end; diff --git a/source/blender/editors/sculpt_paint/sculpt_api.cc b/source/blender/editors/sculpt_paint/sculpt_api.cc index 0686c50f82a..a12c728731f 100644 --- a/source/blender/editors/sculpt_paint/sculpt_api.cc +++ b/source/blender/editors/sculpt_paint/sculpt_api.cc @@ -239,8 +239,8 @@ eSculptBoundary SCULPT_edge_is_boundary(const SculptSession *ss, ret |= (boundflag1 | boundflag2) & SCULPT_BOUNDARY_UV; } - if (typemask & SCULPT_BOUNDARY_SHARP) { - ret |= !BM_elem_flag_test(e, BM_ELEM_SMOOTH) ? SCULPT_BOUNDARY_SHARP : 0; + if (typemask & SCULPT_BOUNDARY_SHARP_MARK) { + ret |= !BM_elem_flag_test(e, BM_ELEM_SMOOTH) ? SCULPT_BOUNDARY_SHARP_MARK : 0; } if (typemask & SCULPT_BOUNDARY_SEAM) { @@ -283,8 +283,8 @@ eSculptBoundary SCULPT_edge_is_boundary(const SculptSession *ss, ret |= a & b; } - if (typemask & SCULPT_BOUNDARY_SHARP) { - ret |= (ss->sharp_edge && ss->sharp_edge[edge.i]) ? SCULPT_BOUNDARY_SHARP : 0; + if (typemask & SCULPT_BOUNDARY_SHARP_MARK) { + ret |= (ss->sharp_edge && ss->sharp_edge[edge.i]) ? SCULPT_BOUNDARY_SHARP_MARK : 0; } if (typemask & SCULPT_BOUNDARY_SEAM) { @@ -442,6 +442,8 @@ eSculptCorner SCULPT_vertex_is_corner(const SculptSession *ss, switch (BKE_pbvh_type(ss->pbvh)) { case PBVH_BMESH: { + BMVert *v = reinterpret_cast(vertex.i); + if (needs_update) { BKE_pbvh_update_vert_boundary(ss->cd_faceset_offset, ss->cd_vert_node_offset, @@ -450,11 +452,17 @@ eSculptCorner SCULPT_vertex_is_corner(const SculptSession *ss, ss->attrs.boundary_flags->bmesh_cd_offset, ss->attrs.flags->bmesh_cd_offset, ss->attrs.valence->bmesh_cd_offset, - (BMVert *)vertex.i, + v, ss->boundary_symmetry, &ss->bm->ldata, ss->totuv, - !ss->ignore_uvs); + !ss->ignore_uvs, + ss->sharp_angle_limit); + } + else if (flag & SCULPT_BOUNDARY_UPDATE_SHARP_ANGLE) { + blender::bke::pbvh::update_sharp_boundary_bmesh( + v, ss->attrs.boundary_flags->bmesh_cd_offset, ss->sharp_angle_limit); + flag = eSculptCorner(vertex_attr_get(vertex, ss->attrs.boundary_flags)); } break; @@ -480,7 +488,7 @@ eSculptCorner SCULPT_vertex_is_corner(const SculptSession *ss, flag &= cornertype; return flag & (SCULPT_CORNER_MESH | SCULPT_CORNER_FACE_SET | SCULPT_CORNER_SEAM | - SCULPT_CORNER_SHARP | SCULPT_CORNER_UV); + SCULPT_CORNER_SHARP_MARK | SCULPT_CORNER_UV); } eSculptBoundary SCULPT_vertex_is_boundary(const SculptSession *ss, @@ -492,6 +500,8 @@ eSculptBoundary SCULPT_vertex_is_boundary(const SculptSession *ss, switch (BKE_pbvh_type(ss->pbvh)) { case PBVH_BMESH: { + BMVert *v = reinterpret_cast(vertex.i); + if (needs_update) { BKE_pbvh_update_vert_boundary(ss->cd_faceset_offset, ss->cd_vert_node_offset, @@ -504,7 +514,13 @@ eSculptBoundary SCULPT_vertex_is_boundary(const SculptSession *ss, ss->boundary_symmetry, &ss->bm->ldata, ss->totuv, - !ss->ignore_uvs); + !ss->ignore_uvs, + ss->sharp_angle_limit); + } + else if (flag & SCULPT_BOUNDARY_UPDATE_SHARP_ANGLE) { + blender::bke::pbvh::update_sharp_boundary_bmesh( + v, ss->attrs.boundary_flags->bmesh_cd_offset, ss->sharp_angle_limit); + flag = *vertex_attr_ptr(vertex, ss->attrs.boundary_flags); } break; @@ -562,7 +578,7 @@ eSculptBoundary SCULPT_vertex_is_boundary(const SculptSession *ss, flag &= boundary_types; return flag & (SCULPT_BOUNDARY_MESH | SCULPT_BOUNDARY_FACE_SET | SCULPT_BOUNDARY_SEAM | - SCULPT_BOUNDARY_SHARP | SCULPT_BOUNDARY_UV); + SCULPT_BOUNDARY_SHARP_MARK | SCULPT_BOUNDARY_UV); } bool SCULPT_vertex_check_origdata(SculptSession *ss, PBVHVertRef vertex) diff --git a/source/blender/editors/sculpt_paint/sculpt_brush_types.cc b/source/blender/editors/sculpt_paint/sculpt_brush_types.cc index 678423666a3..03f5d22cc3e 100644 --- a/source/blender/editors/sculpt_paint/sculpt_brush_types.cc +++ b/source/blender/editors/sculpt_paint/sculpt_brush_types.cc @@ -1302,6 +1302,7 @@ static void do_snake_hook_brush_task_cb_ex(void *__restrict userdata, if (vd.is_mesh) { BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); } + BKE_sculpt_sharp_boundary_flag_update(ss, vd.vertex); } BKE_pbvh_vertex_iter_end; } @@ -2094,6 +2095,7 @@ static void do_grab_brush_task_cb_ex(void *__restrict userdata, if (vd.is_mesh) { BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); } + BKE_sculpt_sharp_boundary_flag_update(ss, vd.vertex); } BKE_pbvh_vertex_iter_end; } diff --git a/source/blender/editors/sculpt_paint/sculpt_filter_mesh.cc b/source/blender/editors/sculpt_paint/sculpt_filter_mesh.cc index a6b5c1f7ad2..d43e80c17c6 100644 --- a/source/blender/editors/sculpt_paint/sculpt_filter_mesh.cc +++ b/source/blender/editors/sculpt_paint/sculpt_filter_mesh.cc @@ -578,6 +578,7 @@ static void mesh_filter_task_cb(void *__restrict userdata, if (vd.is_mesh) { BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); } + BKE_sculpt_sharp_boundary_flag_update(ss, vd.vertex); } BKE_pbvh_vertex_iter_end; diff --git a/source/blender/editors/sculpt_paint/sculpt_ops.cc b/source/blender/editors/sculpt_paint/sculpt_ops.cc index 9951d17d969..8bcb513f64a 100644 --- a/source/blender/editors/sculpt_paint/sculpt_ops.cc +++ b/source/blender/editors/sculpt_paint/sculpt_ops.cc @@ -1433,7 +1433,7 @@ static int sculpt_regularize_rake_exec(bContext *C, wmOperator *op) int *boundflag = vertex_attr_ptr(vertex, ss->attrs.boundary_flags); if (*boundflag & - (SCULPT_CORNER_MESH | SCULPT_CORNER_FACE_SET | SCULPT_CORNER_SHARP | SCULPT_CORNER_SEAM)) + (SCULPT_CORNER_MESH | SCULPT_CORNER_FACE_SET | SCULPT_CORNER_SHARP_MARK | SCULPT_CORNER_SEAM)) { continue; } @@ -1456,7 +1456,7 @@ static int sculpt_regularize_rake_exec(bContext *C, wmOperator *op) ListBase queue = {node, node}; const int boundtest = SCULPT_BOUNDARY_MESH | SCULPT_BOUNDARY_FACE_SET | - SCULPT_BOUNDARY_SEAM | SCULPT_BOUNDARY_SHARP; + SCULPT_BOUNDARY_SEAM | SCULPT_BOUNDARY_SHARP_MARK; while (queue.first) { BMLinkItem *node2 = static_cast(BLI_poptail(&queue)); BMVert *v2 = node2->item; diff --git a/source/blender/editors/sculpt_paint/sculpt_smooth.cc b/source/blender/editors/sculpt_paint/sculpt_smooth.cc index 30230ca4ec3..7a05adc4bfc 100644 --- a/source/blender/editors/sculpt_paint/sculpt_smooth.cc +++ b/source/blender/editors/sculpt_paint/sculpt_smooth.cc @@ -34,6 +34,51 @@ using blender::float2; using blender::float3; using blender::Vector; +float BKE_pbvh_bmesh_detail_size_avg_get(PBVH *pbvh); + +/* */ +static void SCULPT_neighbor_coors_average_for_detail(SculptSession *ss, + float result[3], + PBVHVertRef vertex) +{ + float detail = BKE_pbvh_bmesh_detail_size_avg_get(ss->pbvh); + + float original_vertex_co[3]; + copy_v3_v3(original_vertex_co, SCULPT_vertex_co_get(ss, vertex)); + + float edge_length_accum = 0; + int neighbor_count = 0; + SculptVertexNeighborIter ni; + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vertex, ni) { + edge_length_accum = len_v3v3(original_vertex_co, SCULPT_vertex_co_get(ss, ni.vertex)); + neighbor_count++; + } + SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); + + if (neighbor_count == 0) { + copy_v3_v3(result, original_vertex_co); + return; + } + + const float edge_length_avg = edge_length_accum / neighbor_count; + /* This ensures a common length average for all vertices. The smaller this factor is, the more + * uniform smoothing is going to be across different mesh detail areas, but it will make the + * smooth brush effect weaker. It can be exposed as a parameter in the future. */ + const float detail_factor = detail * 0.1f; + + float pos_accum[3] = {0.0f}; + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vertex, ni) { + float disp[3]; + sub_v3_v3v3(disp, SCULPT_vertex_co_get(ss, ni.vertex), original_vertex_co); + const float original_length = normalize_v3(disp); + float new_length = min_ff(original_length, detail_factor * original_length / edge_length_avg); + float new_co[3]; + madd_v3_v3v3fl(new_co, original_vertex_co, disp, new_length); + add_v3_v3(pos_accum, new_co); + } + SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); + mul_v3_v3fl(result, pos_accum, 1.0f / neighbor_count); +} static void SCULPT_neighbor_coords_average_interior_ex(SculptSession *ss, float result[3], @@ -46,6 +91,13 @@ static void SCULPT_neighbor_coords_average_interior_ex(SculptSession *ss, { float3 avg(0.0f, 0.0f, 0.0f); +#if 0 + if (weighted) { + SCULPT_neighbor_coors_average_for_detail(ss, result, vertex); + return; + } +#endif + const eSculptBoundary is_boundary = SCULPT_vertex_is_boundary(ss, vertex, bound_type); const eSculptCorner is_corner = SCULPT_vertex_is_corner(ss, vertex, corner_type); @@ -130,8 +182,8 @@ void SCULPT_neighbor_coords_average_interior(SculptSession *ss, bool use_area_weights) { eSculptBoundary bound_type = SCULPT_BOUNDARY_MESH | SCULPT_BOUNDARY_FACE_SET | - SCULPT_BOUNDARY_SEAM | SCULPT_BOUNDARY_SHARP; - eSculptCorner corner_type = SCULPT_CORNER_MESH | SCULPT_CORNER_SHARP; + SCULPT_BOUNDARY_SEAM | SCULPT_BOUNDARY_SHARP_MARK; + eSculptCorner corner_type = SCULPT_CORNER_MESH | SCULPT_CORNER_SHARP_MARK; if (ss->hard_edge_mode) { corner_type |= SCULPT_CORNER_FACE_SET | SCULPT_CORNER_SEAM; @@ -293,7 +345,7 @@ void SCULPT_bmesh_four_neighbor_average(SculptSession *ss, } eSculptBoundary bflag = SCULPT_BOUNDARY_FACE_SET | SCULPT_BOUNDARY_MESH | - SCULPT_BOUNDARY_SHARP | SCULPT_BOUNDARY_SEAM | SCULPT_BOUNDARY_UV; + SCULPT_BOUNDARY_SHARP_MARK | SCULPT_BOUNDARY_SEAM | SCULPT_BOUNDARY_UV; int bound = SCULPT_edge_is_boundary(ss, BKE_pbvh_make_eref(intptr_t(e)), bflag); float dirw = 1.0f; @@ -361,7 +413,7 @@ void SCULPT_bmesh_four_neighbor_average(SculptSession *ss, copy_v3_v3(avg, co1); } - eSculptCorner corner_type = SCULPT_CORNER_MESH | SCULPT_CORNER_SHARP; + eSculptCorner corner_type = SCULPT_CORNER_MESH | SCULPT_CORNER_SHARP_MARK; if (ss->hard_edge_mode) { corner_type |= SCULPT_CORNER_FACE_SET; } @@ -418,9 +470,9 @@ void SCULPT_neighbor_coords_average(SculptSession *ss, float hard_corner_pin, bool weighted) { - eSculptCorner corner_type = SCULPT_CORNER_SHARP | SCULPT_CORNER_FACE_SET; - eSculptBoundary bound_type = SCULPT_BOUNDARY_SHARP | SCULPT_BOUNDARY_SEAM | SCULPT_BOUNDARY_UV | - SCULPT_BOUNDARY_FACE_SET; + eSculptCorner corner_type = SCULPT_CORNER_SHARP_MARK | SCULPT_CORNER_FACE_SET; + eSculptBoundary bound_type = SCULPT_BOUNDARY_SHARP_MARK | SCULPT_BOUNDARY_SEAM | + SCULPT_BOUNDARY_UV | SCULPT_BOUNDARY_FACE_SET; SCULPT_neighbor_coords_average_interior_ex( ss, result, vertex, projection, hard_corner_pin, weighted, bound_type, corner_type); @@ -518,6 +570,7 @@ static void do_enhance_details_brush_task_cb_ex(void *__restrict userdata, if (vd.is_mesh) { BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); } + BKE_sculpt_sharp_boundary_flag_update(ss, vd.vertex); } BKE_pbvh_vertex_iter_end; } @@ -652,6 +705,7 @@ static void do_smooth_brush_task_cb_ex(void *__restrict userdata, if (vd.is_mesh) { BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); } + BKE_sculpt_sharp_boundary_flag_update(ss, vd.vertex); } } BKE_pbvh_vertex_iter_end; @@ -910,6 +964,7 @@ static void SCULPT_do_surface_smooth_brush_displace_task_cb_ex( thread_id, &automask_data); SCULPT_surface_smooth_displace_step(ss, vd.co, vd.vertex, beta, fade); + BKE_sculpt_sharp_boundary_flag_update(ss, vd.vertex); } BKE_pbvh_vertex_iter_end; } diff --git a/source/blender/makesdna/DNA_brush_defaults.h b/source/blender/makesdna/DNA_brush_defaults.h index 53afc3c9bac..a0e7f963564 100644 --- a/source/blender/makesdna/DNA_brush_defaults.h +++ b/source/blender/makesdna/DNA_brush_defaults.h @@ -123,6 +123,7 @@ .concave_mask_factor = 0.75f,\ .falloff_shape = 0,\ .hard_corner_pin = 1.0f,\ + .sharp_angle_limit = 0.38f,\ } /** \} */ diff --git a/source/blender/makesdna/DNA_brush_types.h b/source/blender/makesdna/DNA_brush_types.h index 846ad17b964..7785cebda28 100644 --- a/source/blender/makesdna/DNA_brush_types.h +++ b/source/blender/makesdna/DNA_brush_types.h @@ -428,7 +428,7 @@ typedef struct Brush { float concave_mask_factor; - char _pad2[4]; + float sharp_angle_limit; struct BrushGpencilSettings *gpencil_settings; DynTopoSettings dyntopo; diff --git a/source/blender/makesdna/DNA_scene_defaults.h b/source/blender/makesdna/DNA_scene_defaults.h index 563c64d51b6..d6d76400a06 100644 --- a/source/blender/makesdna/DNA_scene_defaults.h +++ b/source/blender/makesdna/DNA_scene_defaults.h @@ -276,7 +276,9 @@ .unprojected_radius = 0.29, \ .alpha = 0.5f, \ .weight = 0.5f, \ - .flag = UNIFIED_PAINT_SIZE | UNIFIED_PAINT_ALPHA | UNIFIED_PAINT_FLAG_HARD_EDGE_MODE | UNIFIED_PAINT_HARD_CORNER_PIN, \ + .flag = UNIFIED_PAINT_SIZE | UNIFIED_PAINT_ALPHA | UNIFIED_PAINT_FLAG_HARD_EDGE_MODE | UNIFIED_PAINT_HARD_CORNER_PIN | UNIFIED_PAINT_FLAG_SHARP_ANGLE_LIMIT, \ + .hard_corner_pin = 1.0f,\ + .sharp_angle_limit = 0.38f,\ } #define _DNA_DEFAULTS_ParticleEditSettings \ diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h index 458a4756954..c9286d3fce8 100644 --- a/source/blender/makesdna/DNA_scene_types.h +++ b/source/blender/makesdna/DNA_scene_types.h @@ -1380,7 +1380,8 @@ typedef struct UnifiedPaintSettings { float initial_pixel_radius; float hard_corner_pin; - char _pad[3]; + float sharp_angle_limit; + char _pad[7]; char hard_edge_mode; float start_pixel_radius; @@ -1402,13 +1403,13 @@ typedef struct UnifiedPaintSettings { typedef enum { UNIFIED_PAINT_SIZE = (1 << 0), UNIFIED_PAINT_ALPHA = (1 << 1), + /** Only used if unified size is enabled, mirrors the brush flag #BRUSH_LOCK_SIZE. */ + UNIFIED_PAINT_BRUSH_LOCK_SIZE = (1 << 2), UNIFIED_PAINT_WEIGHT = (1 << 5), UNIFIED_PAINT_COLOR = (1 << 6), UNIFIED_PAINT_HARD_CORNER_PIN = (1 << 7), UNIFIED_PAINT_FLAG_HARD_EDGE_MODE = (1 << 8), - - /** Only used if unified size is enabled, mirrors the brush flag #BRUSH_LOCK_SIZE. */ - UNIFIED_PAINT_BRUSH_LOCK_SIZE = (1 << 2), + UNIFIED_PAINT_FLAG_SHARP_ANGLE_LIMIT = (1 << 9), } eUnifiedPaintSettingsFlags; typedef struct CurvePaintSettings { diff --git a/source/blender/makesrna/intern/rna_brush.c b/source/blender/makesrna/intern/rna_brush.c index 39643509877..fdcdbc5f3d7 100644 --- a/source/blender/makesrna/intern/rna_brush.c +++ b/source/blender/makesrna/intern/rna_brush.c @@ -3369,6 +3369,13 @@ static void rna_def_brush(BlenderRNA *brna) prop, "Projection", "How much autosmooth should stick to surface\n(prevents shrinking)."); RNA_def_property_update(prop, 0, "rna_Brush_update"); + prop = RNA_def_property(srna, "sharp_angle_limit", PROP_FLOAT, PROP_ANGLE); + RNA_def_property_float_sdna(prop, NULL, "sharp_angle_limit"); + RNA_def_property_range(prop, 0.0f, M_PI); + RNA_def_property_ui_text( + prop, "Sharp Limit", ""); + RNA_def_property_update(prop, 0, "rna_Brush_update"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); prop = RNA_def_property(srna, "auto_smooth_radius_factor", PROP_FLOAT, PROP_FACTOR); RNA_def_property_float_sdna(prop, NULL, "autosmooth_radius_factor"); diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c index cd6480af452..6e9c010314d 100644 --- a/source/blender/makesrna/intern/rna_scene.c +++ b/source/blender/makesrna/intern/rna_scene.c @@ -3913,6 +3913,18 @@ static void rna_def_unified_paint_settings(BlenderRNA *brna) RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE); RNA_def_property_update(prop, 0, "rna_UnifiedPaintSettings_update"); + prop = RNA_def_property(srna, "use_unified_sharp_angle_limit", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", UNIFIED_PAINT_FLAG_SHARP_ANGLE_LIMIT); + RNA_def_property_ui_text( + prop, "Use Unified Sharp Angle Limit", "Use global setting for sharp angle limit"); + + prop = RNA_def_property(srna, "sharp_angle_limit", PROP_FLOAT, PROP_ANGLE); + RNA_def_property_float_sdna(prop, NULL, "sharp_angle_limit"); + RNA_def_property_range(prop, 0.0f, M_PI); + RNA_def_property_ui_text(prop, "Sharp Limit", ""); + RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE); + RNA_def_property_update(prop, 0, "rna_UnifiedPaintSettings_update"); + prop = RNA_def_property(srna, "use_unified_strength", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", UNIFIED_PAINT_ALPHA); RNA_def_property_ui_text(prop, -- 2.30.2 From 15d46217394b5b297b8b93eeb98c4e8ff5b08a20 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Thu, 18 May 2023 15:18:51 -0700 Subject: [PATCH 103/279] temp-sculpt-dyntopo: Expose smooth brush boundary settings to UI --- scripts/startup/bl_ui/space_view3d_toolbar.py | 33 +++- source/blender/blenkernel/BKE_paint.h | 32 +--- source/blender/blenkernel/intern/paint.cc | 6 +- source/blender/blenkernel/intern/pbvh.cc | 2 +- .../blenloader/intern/versioning_300.cc | 23 ++- source/blender/editors/sculpt_paint/sculpt.cc | 1 + .../editors/sculpt_paint/sculpt_api.cc | 155 +++++++----------- .../sculpt_paint/sculpt_brush_types.cc | 6 +- .../editors/sculpt_paint/sculpt_smooth.cc | 23 ++- source/blender/makesdna/DNA_scene_defaults.h | 2 + source/blender/makesdna/DNA_scene_enums.h | 32 ++++ source/blender/makesdna/DNA_scene_types.h | 3 +- source/blender/makesrna/intern/rna_scene.c | 37 +++++ 13 files changed, 206 insertions(+), 149 deletions(-) diff --git a/scripts/startup/bl_ui/space_view3d_toolbar.py b/scripts/startup/bl_ui/space_view3d_toolbar.py index f759312af21..f765c052cdc 100644 --- a/scripts/startup/bl_ui/space_view3d_toolbar.py +++ b/scripts/startup/bl_ui/space_view3d_toolbar.py @@ -1026,6 +1026,7 @@ class VIEW3D_PT_sculpt_options(Panel, View3DPaintPanel): tool_settings = context.tool_settings sculpt = tool_settings.sculpt brush = sculpt.brush + ups = tool_settings.unified_paint_settings col = layout.column(heading="Display", align=True) col.prop(sculpt, "show_low_resolution") @@ -1033,16 +1034,44 @@ class VIEW3D_PT_sculpt_options(Panel, View3DPaintPanel): col.prop(sculpt, "use_deform_only") col.prop(tool_settings, "show_origco") col.prop(context.object.data, "sculpt_ignore_uvs") - col.prop(tool_settings.unified_paint_settings, "hard_edge_mode") + col.label(text="Smooth Boundaries") + col = layout.column(align=True) + col.prop(ups, "smooth_boundary_mesh") + col.prop(ups, "smooth_boundary_face_set") + + row = col.row() + row.enabled = ups.smooth_boundary_face_set + row.prop(ups, "hard_edge_mode", text="Sharp Face Sets") + + col.prop(ups, "smooth_boundary_seam") + col.prop(ups, "smooth_boundary_sharp_mark") + col.prop(ups, "smooth_boundary_sharp_angle") + + row = col.row() + row.enabled = ups.smooth_boundary_sharp_angle UnifiedPaintPanel.prop_unified( - layout, + row, context, tool_settings.unified_paint_settings, "sharp_angle_limit", unified_name = "use_unified_sharp_angle_limit" ) + col.prop(ups, "smooth_boundary_uv") + col.separator(); + + UnifiedPaintPanel.prop_unified( + layout, + context, + brush, + "hard_corner_pin", + slider=True, + unified_name = "use_unified_hard_corner_pin", + text="Corner Pin" + ) + + class VIEW3D_PT_sculpt_options_gravity(Panel, View3DPaintPanel): bl_context = ".sculpt_mode" # dot on purpose (access from topbar) bl_parent_id = "VIEW3D_PT_sculpt_options" diff --git a/source/blender/blenkernel/BKE_paint.h b/source/blender/blenkernel/BKE_paint.h index 0632c41136a..5479e5f7a9c 100644 --- a/source/blender/blenkernel/BKE_paint.h +++ b/source/blender/blenkernel/BKE_paint.h @@ -17,6 +17,7 @@ #include "DNA_brush_enums.h" #include "DNA_customdata_types.h" #include "DNA_object_enums.h" +#include "DNA_scene_enums.h" #include "BKE_attribute.h" #include "BKE_pbvh.h" @@ -1005,41 +1006,12 @@ struct SculptSession { bool hard_edge_mode; DynTopoSettings cached_dyntopo; float sharp_angle_limit; + eSculptBoundary smooth_boundary_flag; }; #else struct SculptSession; #endif -typedef enum eSculptBoundary { - SCULPT_BOUNDARY_NONE = 0, - SCULPT_BOUNDARY_MESH = 1 << 0, - SCULPT_BOUNDARY_FACE_SET = 1 << 1, - SCULPT_BOUNDARY_SEAM = 1 << 2, - SCULPT_BOUNDARY_SHARP_MARK = 1 << 3, /* Edges marked as sharp. */ - SCULPT_BOUNDARY_SHARP_ANGLE = 1 << 4, /* Edges whose face angle is above a limit */ - SCULPT_BOUNDARY_UV = 1 << 5, - SCULPT_BOUNDARY_NEEDS_UPDATE = 1 << 6, - SCULPT_BOUNDARY_UPDATE_SHARP_ANGLE = 1 << 7, - - SCULPT_BOUNDARY_ALL = (1 << 0) | (1 << 1) | (1 << 2) | (1 << 3) | (1 << 4), - SCULPT_BOUNDARY_DEFAULT = (1 << 0) | (1 << 3) | (1 << 4) // mesh and sharp -} eSculptBoundary; - -ENUM_OPERATORS(eSculptBoundary, SCULPT_BOUNDARY_NEEDS_UPDATE); - -/* Note: This is stored in a single attribute with boundary flags */ -typedef enum eSculptCorner { - SCULPT_CORNER_NONE = 0, - SCULPT_CORNER_MESH = 1 << 10, - SCULPT_CORNER_FACE_SET = 1 << 11, - SCULPT_CORNER_SEAM = 1 << 12, - SCULPT_CORNER_SHARP_MARK = 1 << 13, - SCULPT_CORNER_SHARP_ANGLE = 1 << 14, - SCULPT_CORNER_UV = 1 << 15, -} eSculptCorner; - -ENUM_OPERATORS(eSculptCorner, SCULPT_CORNER_UV); - void BKE_sculptsession_free(struct Object *ob); void BKE_sculptsession_free_deformMats(struct SculptSession *ss); void BKE_sculptsession_free_vwpaint_data(struct SculptSession *ss); diff --git a/source/blender/blenkernel/intern/paint.cc b/source/blender/blenkernel/intern/paint.cc index 47567ae92eb..b885459b7da 100644 --- a/source/blender/blenkernel/intern/paint.cc +++ b/source/blender/blenkernel/intern/paint.cc @@ -1794,6 +1794,8 @@ static void sculpt_update_object( ss->sharp_angle_limit = (!brush || ups.flag & UNIFIED_PAINT_FLAG_SHARP_ANGLE_LIMIT) ? ups.sharp_angle_limit : brush->sharp_angle_limit; + ss->smooth_boundary_flag = eSculptBoundary(ups.smooth_boundary_flag); + ss->depsgraph = depsgraph; ss->bm_smooth_shading = scene->toolsettings->sculpt->flags & SCULPT_DYNTOPO_SMOOTH_SHADING; @@ -4082,9 +4084,7 @@ void load_all_original(Object *ob) } // namespace blender::bke::paint -ATTR_NO_OPT void BKE_sculpt_sharp_boundary_flag_update(SculptSession *ss, - PBVHVertRef vertex, - bool update_ring) +void BKE_sculpt_sharp_boundary_flag_update(SculptSession *ss, PBVHVertRef vertex, bool update_ring) { int *flags = blender::bke::paint::vertex_attr_ptr(vertex, ss->attrs.boundary_flags); *flags |= SCULPT_BOUNDARY_UPDATE_SHARP_ANGLE; diff --git a/source/blender/blenkernel/intern/pbvh.cc b/source/blender/blenkernel/intern/pbvh.cc index 7a5e46fdae7..f84b29201b3 100644 --- a/source/blender/blenkernel/intern/pbvh.cc +++ b/source/blender/blenkernel/intern/pbvh.cc @@ -4283,7 +4283,7 @@ void update_vert_boundary_faces(int *boundary_flags, int *boundary_flag = boundary_flags + vertex.i; *boundary_flag = 0; - int totsharp = 0, totseam = 0; + int totsharp = 0, totseam = 0, totsharp_angle = 0; int visible = false; for (int i = 0; i < vert_map->count; i++) { diff --git a/source/blender/blenloader/intern/versioning_300.cc b/source/blender/blenloader/intern/versioning_300.cc index bf0c2eec12a..f1257dda345 100644 --- a/source/blender/blenloader/intern/versioning_300.cc +++ b/source/blender/blenloader/intern/versioning_300.cc @@ -4260,6 +4260,7 @@ void blo_do_versions_300(FileData *fd, Library * /*lib*/, Main *bmain) } } } + if (!DNA_struct_elem_find(fd->filesdna, "UnifiedPaintSettings", "float", "sharp_angle_limit")) { LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) { if (!scene->toolsettings) { @@ -4278,6 +4279,17 @@ void blo_do_versions_300(FileData *fd, Library * /*lib*/, Main *bmain) } } + if (!DNA_struct_elem_find(fd->filesdna, "UnifiedPaintSettings", "int", "smooth_boundary_flag")) { + LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) { + if (!scene->toolsettings) { + continue; + } + + scene->toolsettings->unified_paint_settings.smooth_boundary_flag = + (DNA_struct_default_get(UnifiedPaintSettings))->smooth_boundary_flag; + } + } + if (!DNA_struct_elem_find(fd->filesdna, "Sculpt", "DynTopoSettings", "dyntopo")) { LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) { if (!scene->toolsettings || !scene->toolsettings->sculpt) { @@ -4382,8 +4394,8 @@ void blo_do_versions_300(FileData *fd, Library * /*lib*/, Main *bmain) } } - /* Use `SEQ_SINGLE_FRAME_CONTENT` flag instead of weird function to check if strip has multiple - * frames. */ + /* Use `SEQ_SINGLE_FRAME_CONTENT` flag instead of weird function to check if strip has + * multiple frames. */ LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) { Editing *ed = SEQ_editing_get(scene); if (ed != nullptr) { @@ -4416,14 +4428,15 @@ void blo_do_versions_300(FileData *fd, Library * /*lib*/, Main *bmain) } if (!MAIN_VERSION_ATLEAST(bmain, 306, 5)) { - /* Some regions used to be added/removed dynamically. Ensure they are always there, there is a - * `ARegionType.poll()` now. */ + /* Some regions used to be added/removed dynamically. Ensure they are always there, there is + * a `ARegionType.poll()` now. */ LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) { LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) { version_ensure_missing_regions(area, sl); - /* Ensure expected region state. Previously this was modified to hide/unhide regions. */ + /* Ensure expected region state. Previously this was modified to hide/unhide regions. + */ const ListBase *regionbase = (sl == area->spacedata.first) ? &area->regionbase : &sl->regionbase; diff --git a/source/blender/editors/sculpt_paint/sculpt.cc b/source/blender/editors/sculpt_paint/sculpt.cc index 56e93b3482e..11651084878 100644 --- a/source/blender/editors/sculpt_paint/sculpt.cc +++ b/source/blender/editors/sculpt_paint/sculpt.cc @@ -5125,6 +5125,7 @@ static void sculpt_update_cache_invariants( int mode; ss->hard_edge_mode = ups->hard_edge_mode; + ss->smooth_boundary_flag = eSculptBoundary(ups->smooth_boundary_flag); Mesh *me = BKE_object_get_original_mesh(ob); BKE_sculptsession_ignore_uvs_set(ob, me->flag & ME_SCULPT_IGNORE_UVS); diff --git a/source/blender/editors/sculpt_paint/sculpt_api.cc b/source/blender/editors/sculpt_paint/sculpt_api.cc index a12c728731f..67b4eafc676 100644 --- a/source/blender/editors/sculpt_paint/sculpt_api.cc +++ b/source/blender/editors/sculpt_paint/sculpt_api.cc @@ -209,6 +209,12 @@ eSculptBoundary SCULPT_edge_is_boundary(const SculptSession *ss, case PBVH_BMESH: { BMEdge *e = (BMEdge *)edge.i; + if (e->l && e->l != e->l->radial_next && typemask & SCULPT_BOUNDARY_SHARP_ANGLE) { + if (saacos(dot_v3v3(e->l->f->no, e->l->radial_next->f->no)) > ss->sharp_angle_limit) { + ret |= SCULPT_BOUNDARY_SHARP_ANGLE; + } + } + if (typemask & SCULPT_BOUNDARY_MESH) { ret |= (!e->l || e->l == e->l->radial_next) ? SCULPT_BOUNDARY_MESH : 0; } @@ -433,67 +439,8 @@ static void faces_update_boundary_flags(const SculptSession *ss, const PBVHVertR } } } -eSculptCorner SCULPT_vertex_is_corner(const SculptSession *ss, - const PBVHVertRef vertex, - eSculptCorner cornertype) -{ - eSculptCorner flag = eSculptCorner(vertex_attr_get(vertex, ss->attrs.boundary_flags)); - bool needs_update = flag & SCULPT_BOUNDARY_NEEDS_UPDATE; - switch (BKE_pbvh_type(ss->pbvh)) { - case PBVH_BMESH: { - BMVert *v = reinterpret_cast(vertex.i); - - if (needs_update) { - BKE_pbvh_update_vert_boundary(ss->cd_faceset_offset, - ss->cd_vert_node_offset, - ss->cd_face_node_offset, - ss->cd_vcol_offset, - ss->attrs.boundary_flags->bmesh_cd_offset, - ss->attrs.flags->bmesh_cd_offset, - ss->attrs.valence->bmesh_cd_offset, - v, - ss->boundary_symmetry, - &ss->bm->ldata, - ss->totuv, - !ss->ignore_uvs, - ss->sharp_angle_limit); - } - else if (flag & SCULPT_BOUNDARY_UPDATE_SHARP_ANGLE) { - blender::bke::pbvh::update_sharp_boundary_bmesh( - v, ss->attrs.boundary_flags->bmesh_cd_offset, ss->sharp_angle_limit); - flag = eSculptCorner(vertex_attr_get(vertex, ss->attrs.boundary_flags)); - } - - break; - } - case PBVH_FACES: - if (needs_update) { - faces_update_boundary_flags(ss, vertex); - } - break; - case PBVH_GRIDS: { - if (needs_update) { - grids_update_boundary_flags(ss, vertex); - } - - break; - } - } - - if (needs_update) { - flag = eSculptCorner(vertex_attr_get(vertex, ss->attrs.boundary_flags)); - } - - flag &= cornertype; - - return flag & (SCULPT_CORNER_MESH | SCULPT_CORNER_FACE_SET | SCULPT_CORNER_SEAM | - SCULPT_CORNER_SHARP_MARK | SCULPT_CORNER_UV); -} - -eSculptBoundary SCULPT_vertex_is_boundary(const SculptSession *ss, - const PBVHVertRef vertex, - eSculptBoundary boundary_types) +static bool sculpt_vertex_ensure_boundary(const SculptSession *ss, const PBVHVertRef vertex) { eSculptBoundary flag = *vertex_attr_ptr(vertex, ss->attrs.boundary_flags); bool needs_update = flag & SCULPT_BOUNDARY_NEEDS_UPDATE; @@ -520,7 +467,6 @@ eSculptBoundary SCULPT_vertex_is_boundary(const SculptSession *ss, else if (flag & SCULPT_BOUNDARY_UPDATE_SHARP_ANGLE) { blender::bke::pbvh::update_sharp_boundary_bmesh( v, ss->attrs.boundary_flags->bmesh_cd_offset, ss->sharp_angle_limit); - flag = *vertex_attr_ptr(vertex, ss->attrs.boundary_flags); } break; @@ -537,48 +483,61 @@ eSculptBoundary SCULPT_vertex_is_boundary(const SculptSession *ss, grids_update_boundary_flags(ss, vertex); needs_update = false; } - - flag = eSculptBoundary(vertex_attr_get(vertex, ss->attrs.boundary_flags)); - - /* BKE_pbvh_update_vert_boundary_grids does not yet support mesh boundaries for PBVH_GRIDS. - */ - if (boundary_types & SCULPT_BOUNDARY_MESH) { - const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh); - const int grid_index = vertex.i / key->grid_area; - const int vertex_index = vertex.i - grid_index * key->grid_area; - SubdivCCGCoord coord{}; - coord.grid_index = grid_index; - coord.x = vertex_index % key->grid_size; - coord.y = vertex_index / key->grid_size; - int v1, v2; - const SubdivCCGAdjacencyType adjacency = BKE_subdiv_ccg_coarse_mesh_adjacency_info_get( - ss->subdiv_ccg, &coord, ss->corner_verts, ss->polys, &v1, &v2); - - switch (adjacency) { - case SUBDIV_CCG_ADJACENT_VERTEX: - flag |= sculpt_check_boundary_vertex_in_base_mesh(ss, v1) ? SCULPT_BOUNDARY_MESH : - (eSculptBoundary)0; - case SUBDIV_CCG_ADJACENT_EDGE: - if (sculpt_check_boundary_vertex_in_base_mesh(ss, v1) && - sculpt_check_boundary_vertex_in_base_mesh(ss, v2)) - { - flag |= SCULPT_BOUNDARY_MESH; - } - case SUBDIV_CCG_ADJACENT_NONE: - break; - } - } + break; } } - if (needs_update) { - flag = eSculptBoundary(*vertex_attr_ptr(vertex, ss->attrs.boundary_flags)); + return needs_update; +} + +eSculptCorner SCULPT_vertex_is_corner(const SculptSession *ss, + const PBVHVertRef vertex, + eSculptCorner cornertype) +{ + sculpt_vertex_ensure_boundary(ss, vertex); + eSculptCorner flag = eSculptCorner(vertex_attr_get(vertex, ss->attrs.boundary_flags)); + + return flag & cornertype; +} + +eSculptBoundary SCULPT_vertex_is_boundary(const SculptSession *ss, + const PBVHVertRef vertex, + eSculptBoundary boundary_types) +{ + eSculptBoundary flag = vertex_attr_get(vertex, ss->attrs.boundary_flags); + + sculpt_vertex_ensure_boundary(ss, vertex); + + if (BKE_pbvh_type(ss->pbvh) == PBVH_GRIDS && boundary_types & SCULPT_BOUNDARY_MESH) { + /* TODO: BKE_pbvh_update_vert_boundary_grids does not yet support mesh boundaries for + * PBVH_GRIDS.*/ + const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh); + const int grid_index = vertex.i / key->grid_area; + const int vertex_index = vertex.i - grid_index * key->grid_area; + SubdivCCGCoord coord{}; + coord.grid_index = grid_index; + coord.x = vertex_index % key->grid_size; + coord.y = vertex_index / key->grid_size; + int v1, v2; + const SubdivCCGAdjacencyType adjacency = BKE_subdiv_ccg_coarse_mesh_adjacency_info_get( + ss->subdiv_ccg, &coord, ss->corner_verts, ss->polys, &v1, &v2); + + switch (adjacency) { + case SUBDIV_CCG_ADJACENT_VERTEX: + flag |= sculpt_check_boundary_vertex_in_base_mesh(ss, v1) ? SCULPT_BOUNDARY_MESH : + (eSculptBoundary)0; + case SUBDIV_CCG_ADJACENT_EDGE: + if (sculpt_check_boundary_vertex_in_base_mesh(ss, v1) && + sculpt_check_boundary_vertex_in_base_mesh(ss, v2)) + { + flag |= SCULPT_BOUNDARY_MESH; + } + case SUBDIV_CCG_ADJACENT_NONE: + break; + } } - flag &= boundary_types; - - return flag & (SCULPT_BOUNDARY_MESH | SCULPT_BOUNDARY_FACE_SET | SCULPT_BOUNDARY_SEAM | - SCULPT_BOUNDARY_SHARP_MARK | SCULPT_BOUNDARY_UV); + return flag & boundary_types; } bool SCULPT_vertex_check_origdata(SculptSession *ss, PBVHVertRef vertex) diff --git a/source/blender/editors/sculpt_paint/sculpt_brush_types.cc b/source/blender/editors/sculpt_paint/sculpt_brush_types.cc index 03f5d22cc3e..c4e2cc42ab9 100644 --- a/source/blender/editors/sculpt_paint/sculpt_brush_types.cc +++ b/source/blender/editors/sculpt_paint/sculpt_brush_types.cc @@ -2431,7 +2431,11 @@ void SCULPT_relax_vertex(SculptSession *ss, eSculptBoundary bset = boundary_mask; bset |= SCULPT_BOUNDARY_FACE_SET; - if (SCULPT_vertex_is_corner(ss, vd->vertex, (eSculptCorner)bset)) { + eSculptCorner corner_mask = eSculptCorner( + int(bset & (SCULPT_BOUNDARY_MESH | SCULPT_BOUNDARY_SHARP_MARK | SCULPT_BOUNDARY_SHARP_ANGLE)) + << SCULPT_CORNER_BIT_SHIFT); + + if (SCULPT_vertex_is_corner(ss, vd->vertex, corner_mask)) { copy_v3_v3(r_final_pos, vd->co); return; } diff --git a/source/blender/editors/sculpt_paint/sculpt_smooth.cc b/source/blender/editors/sculpt_paint/sculpt_smooth.cc index 7a05adc4bfc..39fc5a27dee 100644 --- a/source/blender/editors/sculpt_paint/sculpt_smooth.cc +++ b/source/blender/editors/sculpt_paint/sculpt_smooth.cc @@ -101,6 +101,11 @@ static void SCULPT_neighbor_coords_average_interior_ex(SculptSession *ss, const eSculptBoundary is_boundary = SCULPT_vertex_is_boundary(ss, vertex, bound_type); const eSculptCorner is_corner = SCULPT_vertex_is_corner(ss, vertex, corner_type); + if (ss->bm) { + BMVert *v = reinterpret_cast(vertex.i); + BM_vert_select_set(ss->bm, v, is_corner & SCULPT_CORNER_SHARP_ANGLE); + } + float *areas = nullptr; float3 no, co; SCULPT_vertex_normal_get(ss, vertex, no); @@ -127,9 +132,8 @@ static void SCULPT_neighbor_coords_average_interior_ex(SculptSession *ss, is_boundary2 = SCULPT_vertex_is_boundary(ss, ni.vertex, bound_type); } const eSculptBoundary smooth_types = !ss->hard_edge_mode ? - SCULPT_BOUNDARY_FACE_SET | SCULPT_BOUNDARY_SEAM | - SCULPT_BOUNDARY_UV : - SCULPT_BOUNDARY_UV; + SCULPT_BOUNDARY_FACE_SET | SCULPT_BOUNDARY_UV : + SCULPT_BOUNDARY_UV | SCULPT_BOUNDARY_SEAM; /* Boundary vertices use only other boundary vertices. */ if (is_boundary) { @@ -181,12 +185,15 @@ void SCULPT_neighbor_coords_average_interior(SculptSession *ss, float hard_corner_pin, bool use_area_weights) { - eSculptBoundary bound_type = SCULPT_BOUNDARY_MESH | SCULPT_BOUNDARY_FACE_SET | - SCULPT_BOUNDARY_SEAM | SCULPT_BOUNDARY_SHARP_MARK; - eSculptCorner corner_type = SCULPT_CORNER_MESH | SCULPT_CORNER_SHARP_MARK; + eSculptBoundary bound_type = ss->smooth_boundary_flag; + eSculptCorner corner_type; - if (ss->hard_edge_mode) { - corner_type |= SCULPT_CORNER_FACE_SET | SCULPT_CORNER_SEAM; + corner_type = eSculptCorner(int(bound_type & (SCULPT_BOUNDARY_MESH | SCULPT_BOUNDARY_SHARP_MARK | + SCULPT_BOUNDARY_SHARP_ANGLE)) + << SCULPT_CORNER_BIT_SHIFT); + + if (ss->hard_edge_mode && ss->smooth_boundary_flag & SCULPT_BOUNDARY_FACE_SET) { + corner_type |= SCULPT_CORNER_FACE_SET; } SCULPT_neighbor_coords_average_interior_ex( diff --git a/source/blender/makesdna/DNA_scene_defaults.h b/source/blender/makesdna/DNA_scene_defaults.h index d6d76400a06..7d7171ebbfb 100644 --- a/source/blender/makesdna/DNA_scene_defaults.h +++ b/source/blender/makesdna/DNA_scene_defaults.h @@ -6,6 +6,7 @@ #pragma once +#include "DNA_scene_enums.h" #include "DNA_view3d_defaults.h" /* Struct members on own line. */ @@ -279,6 +280,7 @@ .flag = UNIFIED_PAINT_SIZE | UNIFIED_PAINT_ALPHA | UNIFIED_PAINT_FLAG_HARD_EDGE_MODE | UNIFIED_PAINT_HARD_CORNER_PIN | UNIFIED_PAINT_FLAG_SHARP_ANGLE_LIMIT, \ .hard_corner_pin = 1.0f,\ .sharp_angle_limit = 0.38f,\ + .smooth_boundary_flag = SCULPT_BOUNDARY_MESH|SCULPT_BOUNDARY_FACE_SET|SCULPT_BOUNDARY_SEAM|SCULPT_BOUNDARY_SHARP_MARK,\ } #define _DNA_DEFAULTS_ParticleEditSettings \ diff --git a/source/blender/makesdna/DNA_scene_enums.h b/source/blender/makesdna/DNA_scene_enums.h index 7d645a7ebe4..622444e5857 100644 --- a/source/blender/makesdna/DNA_scene_enums.h +++ b/source/blender/makesdna/DNA_scene_enums.h @@ -7,6 +7,8 @@ #pragma once +#include "BLI_utildefines.h" + /** #ToolSettings.vgroupsubset */ typedef enum eVGroupSelect { WT_VGROUP_ALL = 0, @@ -15,3 +17,33 @@ typedef enum eVGroupSelect { WT_VGROUP_BONE_DEFORM = 3, WT_VGROUP_BONE_DEFORM_OFF = 4, } eVGroupSelect; + +typedef enum eSculptBoundary { + SCULPT_BOUNDARY_NONE = 0, + SCULPT_BOUNDARY_MESH = 1 << 0, + SCULPT_BOUNDARY_FACE_SET = 1 << 1, + SCULPT_BOUNDARY_SEAM = 1 << 2, + SCULPT_BOUNDARY_SHARP_MARK = 1 << 3, /* Edges marked as sharp. */ + SCULPT_BOUNDARY_SHARP_ANGLE = 1 << 4, /* Edges whose face angle is above a limit */ + SCULPT_BOUNDARY_UV = 1 << 5, + SCULPT_BOUNDARY_NEEDS_UPDATE = 1 << 6, + SCULPT_BOUNDARY_UPDATE_SHARP_ANGLE = 1 << 7, + + SCULPT_BOUNDARY_ALL = (1 << 0) | (1 << 1) | (1 << 2) | (1 << 3) | (1 << 4), + SCULPT_BOUNDARY_DEFAULT = (1 << 0) | (1 << 3) | (1 << 4) // mesh and sharp +} eSculptBoundary; +ENUM_OPERATORS(eSculptBoundary, SCULPT_BOUNDARY_UPDATE_SHARP_ANGLE); + +/* Note: This is stored in a single attribute with boundary flags */ +typedef enum eSculptCorner { + SCULPT_CORNER_NONE = 0, + SCULPT_CORNER_BIT_SHIFT = + 16, /* Shift boundary flags by this much to get matching corner flags. */ + SCULPT_CORNER_MESH = 1 << 16, + SCULPT_CORNER_FACE_SET = 1 << 17, + SCULPT_CORNER_SEAM = 1 << 18, + SCULPT_CORNER_SHARP_MARK = 1 << 19, + SCULPT_CORNER_SHARP_ANGLE = 1 << 20, + SCULPT_CORNER_UV = 1 << 21, +} eSculptCorner; +ENUM_OPERATORS(eSculptCorner, SCULPT_CORNER_UV); diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h index c9286d3fce8..26194a0ee7b 100644 --- a/source/blender/makesdna/DNA_scene_types.h +++ b/source/blender/makesdna/DNA_scene_types.h @@ -1381,8 +1381,9 @@ typedef struct UnifiedPaintSettings { float hard_corner_pin; float sharp_angle_limit; - char _pad[7]; + char _pad[3]; char hard_edge_mode; + int smooth_boundary_flag; float start_pixel_radius; diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c index 6e9c010314d..233f513af2e 100644 --- a/source/blender/makesrna/intern/rna_scene.c +++ b/source/blender/makesrna/intern/rna_scene.c @@ -14,6 +14,7 @@ #include "DNA_modifier_types.h" #include "DNA_particle_types.h" #include "DNA_rigidbody_types.h" +#include "DNA_scene_enums.h" #include "DNA_scene_types.h" #include "DNA_screen_types.h" /* TransformOrientation */ #include "DNA_userdef_types.h" @@ -4004,6 +4005,42 @@ static void rna_def_unified_paint_settings(BlenderRNA *brna) RNA_def_property_ui_text( prop, "Hard Edge Mode", "Hard edge mode; treat all face set boundaries as hard edges"); RNA_def_property_update(prop, 0, "rna_UnifiedPaintSettings_update"); + + prop = RNA_def_property(srna, "smooth_boundary_mesh", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "smooth_boundary_flag", SCULPT_BOUNDARY_MESH); + RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE); + RNA_def_property_ui_text(prop, "Mesh", "Open mesh boundaries (holes in the mesh)."); + RNA_def_property_update(prop, 0, "rna_UnifiedPaintSettings_update"); + + prop = RNA_def_property(srna, "smooth_boundary_seam", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "smooth_boundary_flag", SCULPT_BOUNDARY_SEAM); + RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE); + RNA_def_property_ui_text(prop, "Seam", "Seam edges"); + RNA_def_property_update(prop, 0, "rna_UnifiedPaintSettings_update"); + + prop = RNA_def_property(srna, "smooth_boundary_sharp_mark", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "smooth_boundary_flag", SCULPT_BOUNDARY_SHARP_MARK); + RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE); + RNA_def_property_ui_text(prop, "Marked Sharp", "Edges marked as sharp"); + RNA_def_property_update(prop, 0, "rna_UnifiedPaintSettings_update"); + + prop = RNA_def_property(srna, "smooth_boundary_sharp_angle", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "smooth_boundary_flag", SCULPT_BOUNDARY_SHARP_ANGLE); + RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE); + RNA_def_property_ui_text(prop, "Sharp By Angle", "Edges whose face angle exceeds a limit"); + RNA_def_property_update(prop, 0, "rna_UnifiedPaintSettings_update"); + + prop = RNA_def_property(srna, "smooth_boundary_face_set", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "smooth_boundary_flag", SCULPT_BOUNDARY_FACE_SET); + RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE); + RNA_def_property_ui_text(prop, "Face Sets", "Face set boundaries"); + RNA_def_property_update(prop, 0, "rna_UnifiedPaintSettings_update"); + + prop = RNA_def_property(srna, "smooth_boundary_uv", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "smooth_boundary_flag", SCULPT_BOUNDARY_UV); + RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE); + RNA_def_property_ui_text(prop, "UV", "UV island boundaries"); + RNA_def_property_update(prop, 0, "rna_UnifiedPaintSettings_update"); } static void rna_def_curve_paint_settings(BlenderRNA *brna) -- 2.30.2 From 71dda6300b97b1b41176d2b15dd66359954ae9ed Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Thu, 18 May 2023 15:33:14 -0700 Subject: [PATCH 104/279] temp-sculpt-dyntopo: Fix strings of boundary edges shrinking during smooth --- .../editors/sculpt_paint/sculpt_smooth.cc | 42 ++++++++++++++----- 1 file changed, 31 insertions(+), 11 deletions(-) diff --git a/source/blender/editors/sculpt_paint/sculpt_smooth.cc b/source/blender/editors/sculpt_paint/sculpt_smooth.cc index 39fc5a27dee..59e67f37936 100644 --- a/source/blender/editors/sculpt_paint/sculpt_smooth.cc +++ b/source/blender/editors/sculpt_paint/sculpt_smooth.cc @@ -80,14 +80,14 @@ static void SCULPT_neighbor_coors_average_for_detail(SculptSession *ss, mul_v3_v3fl(result, pos_accum, 1.0f / neighbor_count); } -static void SCULPT_neighbor_coords_average_interior_ex(SculptSession *ss, - float result[3], - PBVHVertRef vertex, - float projection, - float hard_corner_pin, - bool weighted, - eSculptBoundary bound_type, - eSculptCorner corner_type) +ATTR_NO_OPT static void SCULPT_neighbor_coords_average_interior_ex(SculptSession *ss, + float result[3], + PBVHVertRef vertex, + float projection, + float hard_corner_pin, + bool weighted, + eSculptBoundary bound_type, + eSculptCorner corner_type) { float3 avg(0.0f, 0.0f, 0.0f); @@ -118,6 +118,7 @@ static void SCULPT_neighbor_coords_average_interior_ex(SculptSession *ss, } float total = 0.0f; + int totboundary = 0; SculptVertexNeighborIter ni; SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vertex, ni) { @@ -132,9 +133,14 @@ static void SCULPT_neighbor_coords_average_interior_ex(SculptSession *ss, is_boundary2 = SCULPT_vertex_is_boundary(ss, ni.vertex, bound_type); } const eSculptBoundary smooth_types = !ss->hard_edge_mode ? - SCULPT_BOUNDARY_FACE_SET | SCULPT_BOUNDARY_UV : + SCULPT_BOUNDARY_FACE_SET | SCULPT_BOUNDARY_UV | + SCULPT_BOUNDARY_SEAM : SCULPT_BOUNDARY_UV | SCULPT_BOUNDARY_SEAM; + if (is_boundary2) { + totboundary++; + } + /* Boundary vertices use only other boundary vertices. */ if (is_boundary) { /* Handle smooth boundaries. */ @@ -158,6 +164,20 @@ static void SCULPT_neighbor_coords_average_interior_ex(SculptSession *ss, } SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); + /* Ensure open strings of boundary edges don't shrink at the endpoints. */ + if (totboundary == 1) { + total = 0.0; + zero_v3(avg); + + SculptVertexNeighborIter ni; + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vertex, ni) { + float w = weighted ? areas[ni.i] : 1.0f; + avg += float3(SCULPT_vertex_co_get(ss, ni.vertex)) * w; + total += w; + } + SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); + } + /* Avoid division by 0 when there are no neighbors. */ if (total == 0) { copy_v3_v3(result, SCULPT_vertex_co_get(ss, vertex)); @@ -805,8 +825,8 @@ void SCULPT_do_smooth_brush(Sculpt *sd, Object *ob, Span nodes) /* NOTE: The enhance brush needs to initialize its state on the first brush step. The stroke * strength can become 0 during the stroke, but it can not change sign (the sign is determined - * in the beginning of the stroke. So here it is important to not switch to enhance brush in the - * middle of the stroke. */ + * in the beginning of the stroke. So here it is important to not switch to enhance brush in + * the middle of the stroke. */ if (ss->cache->bstrength < 0.0f) { /* Invert mode, intensify details. */ SCULPT_enhance_details_brush(sd, ob, nodes); -- 2.30.2 From f704e272fcda76a2fc77c5726c414ac5ea9fdeba Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Thu, 18 May 2023 15:34:20 -0700 Subject: [PATCH 105/279] temp-sculpt-dyntopo: Remove debug code from last 2 commits --- .../editors/sculpt_paint/sculpt_smooth.cc | 21 +++++++------------ 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/source/blender/editors/sculpt_paint/sculpt_smooth.cc b/source/blender/editors/sculpt_paint/sculpt_smooth.cc index 59e67f37936..65257f201fb 100644 --- a/source/blender/editors/sculpt_paint/sculpt_smooth.cc +++ b/source/blender/editors/sculpt_paint/sculpt_smooth.cc @@ -80,14 +80,14 @@ static void SCULPT_neighbor_coors_average_for_detail(SculptSession *ss, mul_v3_v3fl(result, pos_accum, 1.0f / neighbor_count); } -ATTR_NO_OPT static void SCULPT_neighbor_coords_average_interior_ex(SculptSession *ss, - float result[3], - PBVHVertRef vertex, - float projection, - float hard_corner_pin, - bool weighted, - eSculptBoundary bound_type, - eSculptCorner corner_type) +static void SCULPT_neighbor_coords_average_interior_ex(SculptSession *ss, + float result[3], + PBVHVertRef vertex, + float projection, + float hard_corner_pin, + bool weighted, + eSculptBoundary bound_type, + eSculptCorner corner_type) { float3 avg(0.0f, 0.0f, 0.0f); @@ -101,11 +101,6 @@ ATTR_NO_OPT static void SCULPT_neighbor_coords_average_interior_ex(SculptSession const eSculptBoundary is_boundary = SCULPT_vertex_is_boundary(ss, vertex, bound_type); const eSculptCorner is_corner = SCULPT_vertex_is_corner(ss, vertex, corner_type); - if (ss->bm) { - BMVert *v = reinterpret_cast(vertex.i); - BM_vert_select_set(ss->bm, v, is_corner & SCULPT_CORNER_SHARP_ANGLE); - } - float *areas = nullptr; float3 no, co; SCULPT_vertex_normal_get(ss, vertex, no); -- 2.30.2 From 225df16d81f1d52f8815f263631475e2e42e5cbc Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Fri, 19 May 2023 00:20:42 -0700 Subject: [PATCH 106/279] temp-sculpt-dyntopo: Fix a large number of memory leaks * ss->bm wasn't being freed at all, a holdover from the old PBVH caching experiment I ended up ditching. * Fixed a few memory leaks in BMLog. --- .../blender/blenkernel/intern/customdata.cc | 17 ++- source/blender/blenkernel/intern/paint.cc | 13 +- .../blender/blenkernel/intern/pbvh_bmesh.cc | 6 +- source/blender/bmesh/intern/bmesh_log.cc | 117 ++++++++++++++++-- .../editors/sculpt_paint/sculpt_dyntopo.cc | 2 +- .../editors/sculpt_paint/sculpt_undo.cc | 10 +- 6 files changed, 130 insertions(+), 35 deletions(-) diff --git a/source/blender/blenkernel/intern/customdata.cc b/source/blender/blenkernel/intern/customdata.cc index 24cd552dfb6..8b5ef49719f 100644 --- a/source/blender/blenkernel/intern/customdata.cc +++ b/source/blender/blenkernel/intern/customdata.cc @@ -2370,10 +2370,8 @@ static bool customdata_typemap_is_valid(const CustomData *data) void CustomData_copy_all_layout(const struct CustomData *source, struct CustomData *dest) { *dest = *source; - - if (dest->pool) { - dest->pool = nullptr; - } + dest->external = nullptr; + dest->pool = nullptr; if (source->layers) { dest->layers = static_cast( @@ -2384,6 +2382,7 @@ void CustomData_copy_all_layout(const struct CustomData *source, struct CustomDa *layer = source->layers[i]; layer->data = nullptr; + layer->sharing_info = nullptr; if (layer->default_data) { layer->default_data = MEM_dupallocN(layer->default_data); @@ -2699,7 +2698,7 @@ void CustomData_copy_layout(const struct CustomData *source, CustomData_merge_layout(source, dest, mask, alloctype, totelem); } -static void customData_free_layer__internal(CustomDataLayer *layer, const int totelem) +ATTR_NO_OPT static void customData_free_layer__internal(CustomDataLayer *layer, const int totelem) { if (layer->anonymous_id != nullptr) { layer->anonymous_id->remove_user_and_delete_if_last(); @@ -2710,15 +2709,15 @@ static void customData_free_layer__internal(CustomDataLayer *layer, const int to if (layer->data) { free_layer_data(type, layer->data, totelem); } - - if (layer->default_data) { - MEM_freeN(layer->default_data); - } } else { layer->sharing_info->remove_user_and_delete_if_last(); layer->sharing_info = nullptr; } + + if (layer->default_data) { + MEM_freeN(layer->default_data); + } } static void CustomData_external_free(CustomData *data) diff --git a/source/blender/blenkernel/intern/paint.cc b/source/blender/blenkernel/intern/paint.cc index b885459b7da..f25d5b4b263 100644 --- a/source/blender/blenkernel/intern/paint.cc +++ b/source/blender/blenkernel/intern/paint.cc @@ -1553,17 +1553,19 @@ void BKE_sculptsession_free(Object *ob) ss->bm_idmap = nullptr; } - if (ss->bm_log && BM_log_free(ss->bm_log, true)) { + if (ss->bm_log) { + /* Does not free the actual entries, the undo system does that */ + BM_log_free(ss->bm_log, true); ss->bm_log = nullptr; } - /*try to save current mesh*/ + /* Destroy temporary attributes. */ BKE_sculpt_attribute_destroy_temporary_all(ob); if (ss->bm) { BKE_sculptsession_bm_to_me(ob, true); + BM_mesh_free(ss->bm); ss->bm = nullptr; - // BM_mesh_free(ss->bm); } CustomData_free(&ss->temp_vdata, ss->temp_vdata_elems); @@ -2564,6 +2566,7 @@ static PBVH *build_pbvh_for_dynamic_topology(Object *ob, bool /*update_sculptver { PBVH *pbvh = ob->sculpt->pbvh = BKE_pbvh_new(PBVH_BMESH); BKE_pbvh_set_bmesh(pbvh, ob->sculpt->bm); + sculptsession_bmesh_add_layers(ob); BM_mesh_elem_table_ensure(ob->sculpt->bm, BM_VERT | BM_EDGE | BM_FACE); @@ -2573,7 +2576,6 @@ static PBVH *build_pbvh_for_dynamic_topology(Object *ob, bool /*update_sculptver BKE_sculpt_ensure_idmap(ob); - sculptsession_bmesh_add_layers(ob); BKE_pbvh_build_bmesh(pbvh, BKE_object_get_original_mesh(ob), ob->sculpt->bm, @@ -2799,6 +2801,7 @@ PBVH *BKE_sculpt_object_pbvh_ensure(Depsgraph *depsgraph, Object *ob) ss->bm = bm; + BKE_sculpt_ensure_idmap(ob); SCULPT_undo_ensure_bmlog(ob); /* Note build_pbvh_for_dynamic_topology respects the pbvh cache. */ @@ -2806,9 +2809,9 @@ PBVH *BKE_sculpt_object_pbvh_ensure(Depsgraph *depsgraph, Object *ob) if (!CustomData_has_layer(&ss->bm->vdata, CD_PAINT_MASK)) { BM_data_layer_add(ss->bm, &ss->bm->vdata, CD_PAINT_MASK); + BKE_sculptsession_update_attr_refs(ob); } - BKE_sculptsession_update_attr_refs(ob); BKE_sculpt_ensure_origco(ob); BKE_sculpt_ensure_sculpt_layers(ob); diff --git a/source/blender/blenkernel/intern/pbvh_bmesh.cc b/source/blender/blenkernel/intern/pbvh_bmesh.cc index f270ede125f..3892e0017a6 100644 --- a/source/blender/blenkernel/intern/pbvh_bmesh.cc +++ b/source/blender/blenkernel/intern/pbvh_bmesh.cc @@ -843,9 +843,9 @@ static void pbvh_print_mem_size(PBVH *pbvh) int ptrsize = (int)sizeof(void *); - float memsize3[3] = {(float)(ptrsize * bm->idmap.map_size) / 1024.0f / 1024.0f, - (float)(ptrsize * bm->idmap.freelist_len) / 1024.0f / 1024.0f, - (float)(4 * bm->idmap.free_ids_size) / 1024.0f / 1024.0f}; + float memsize3[3] = {(float)(ptrsize * pbvh->bm_idmap->map_size) / 1024.0f / 1024.0f, + (float)(ptrsize * pbvh->bm_idmap->freelist.capacity()) / 1024.0f / 1024.0f, + pbvh->bm_idmap->free_idx_map ? (float)(4 * pbvh->bm_idmap->free_idx_map->capacity()) / 1024.0f / 1024.0f : 0.0f}; printf("idmap sizes:\n map_size: %.2fmb freelist_len: %.2fmb free_ids_size: %.2fmb\n", memsize3[0], diff --git a/source/blender/bmesh/intern/bmesh_log.cc b/source/blender/bmesh/intern/bmesh_log.cc index 4f031c6b7e0..21a208f63f6 100644 --- a/source/blender/bmesh/intern/bmesh_log.cc +++ b/source/blender/bmesh/intern/bmesh_log.cc @@ -108,11 +108,78 @@ template struct BMLogElem { dead = true; } #endif + + void free(CustomData *domain) + { + if (customdata) { + CustomData_bmesh_free_block_data(domain, customdata); + } + } }; template struct LogElemAlloc { BLI_mempool *pool; + class iterator { + LogElemAlloc *alloc; + BLI_mempool_iter iter; + void *elem = nullptr, *first; + + public: + iterator(LogElemAlloc *_alloc) : alloc(_alloc) + { + BLI_mempool_iternew(_alloc->pool, &iter); + elem = first = BLI_mempool_iterstep(&iter); + } + + iterator(const iterator &b) : alloc(b.alloc), elem(b.elem), first(b.first) + { + iter = b.iter; + } + + iterator &operator++() + { + elem = BLI_mempool_iterstep(&iter); + + return *this; + } + + T &operator*() + { + return *reinterpret_cast(elem); + } + + iterator begin() + { + iterator start(*this); + start.elem = first; + return start; + } + + iterator end() + { + iterator end = iterator(*this); + end.elem = nullptr; + + return end; + } + + bool operator==(const iterator &b) + { + return elem == b.elem; + } + + bool operator!=(const iterator &b) + { + return elem != b.elem; + } + }; + + iterator elements() + { + return iterator(this); + } + LogElemAlloc() { /* We need an iterable pool to call individual destructors in ~LogElemAlloc(). */ @@ -180,6 +247,17 @@ struct BMLogFace : public BMLogElem { Vector, 5> verts; Vector loop_customdata; // int material_index; + + void free(CustomData *domain, CustomData *loop_domain) + { + BMLogElem::free(domain); + + if (loop_customdata[0]) { + for (void *data : loop_customdata) { + CustomData_bmesh_free_block_data(loop_domain, data); + } + } + } }; struct BMLogEntry; @@ -293,6 +371,14 @@ struct BMLogSetFull : public BMLogSetBase { mesh = BKE_mesh_from_bmesh_nomain(bm, ¶ms, nullptr); } + ~BMLogSetFull() + { + if (mesh) { + BKE_mesh_free_data_for_undo(mesh); + MEM_SAFE_FREE(mesh); + } + } + const char *debug_name() override { return "Full"; @@ -337,6 +423,7 @@ struct BMLogSetFull : public BMLogSetBase { BM_mesh_elem_index_ensure(bm, BM_VERT | BM_EDGE | BM_FACE); BKE_mesh_free_data_for_undo(mesh); + MEM_SAFE_FREE(mesh); mesh = current_mesh; } @@ -439,6 +526,16 @@ struct BMLogEntry { } } + for (BMLogVert &vert : vpool.elements()) { + vert.free(&vdata); + } + for (BMLogEdge &edge : epool.elements()) { + edge.free(&edata); + } + for (BMLogFace &face : fpool.elements()) { + face.free(&pdata, &ldata); + } + if (vdata.pool) { BLI_mempool_destroy(vdata.pool); } @@ -1551,17 +1648,15 @@ bool BM_log_is_dead(BMLog *log) bool BM_log_free(BMLog *log, bool safe_mode) { - if (log->dead) { - MEM_delete(log); - return true; + BMLogEntry *entry = log->first_entry; + + while (entry) { + entry->log = nullptr; + entry = entry->next; } - if (bm_log_free_direct(log, safe_mode)) { - MEM_delete(log); - return true; - } - - return false; + MEM_delete(log); + return true; } BMLogEntry *BM_log_entry_add_ex(BMesh *bm, BMLog *log, bool combine_with_last) @@ -1721,10 +1816,6 @@ bool BM_log_entry_drop(BMLogEntry *entry) { printf("%s: Freeing log entry %p\n", __func__, entry); - if (!entry->log) { - printf("%s: error, missing bm log!\n", __func__); - } - if (entry->prev) { entry->prev->next = entry->next; } diff --git a/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc b/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc index bf5e06f08ef..988c6fcfe40 100644 --- a/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc +++ b/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc @@ -680,7 +680,7 @@ static void SCULPT_dynamic_topology_disable_ex( /* Typically valid but with global-undo they can be nullptr, see: T36234. */ if (ss->bm) { - /* PBVH now frees bmesh, just null it. */ + BM_mesh_free(ss->bm); ss->bm = nullptr; } diff --git a/source/blender/editors/sculpt_paint/sculpt_undo.cc b/source/blender/editors/sculpt_paint/sculpt_undo.cc index 9a8d9e8a37b..b451c6b25e1 100644 --- a/source/blender/editors/sculpt_paint/sculpt_undo.cc +++ b/source/blender/editors/sculpt_paint/sculpt_undo.cc @@ -856,9 +856,7 @@ static void bmesh_undo_on_vert_change(BMVert *v, void *userdata, void *old_custo BMElem h; h.head.data = old_customdata; - int ni = data->cd_vert_node_offset_old != -1 ? - BM_ELEM_CD_GET_INT(&h, data->cd_vert_node_offset_old) : - DYNTOPO_NODE_NONE; + int ni = BM_ELEM_CD_GET_INT(&h, data->cd_vert_node_offset); /* Attempt to find old node reference. */ PBVHNode *node = BKE_pbvh_get_node_leaf_safe(data->pbvh, ni); @@ -869,7 +867,10 @@ static void bmesh_undo_on_vert_change(BMVert *v, void *userdata, void *old_custo } else { if (ni != DYNTOPO_NODE_NONE) { - printf("%s: error: corrupted vertex. ni: %d\n", __func__, ni); + printf("%s: error: corrupted vertex. ni: %d, cd_node_offset: %d\n", + __func__, + ni, + data->cd_vert_node_offset_old); BM_ELEM_CD_SET_INT(v, data->cd_vert_node_offset, DYNTOPO_NODE_NONE); } @@ -944,6 +945,7 @@ static void update_unode_bmesh_memsize(SculptUndoNode *unode) } unode->undo_size = BM_log_entry_size(unode->bm_entry); + //printf("unode->unode_size: size: %.4fmb\n", __func__, float(unode->undo_size) / 1024.0f / 1024.0f); // add new size usculpt->undo_size += unode->undo_size; -- 2.30.2 From c40d7cc2783c5d613e5f51f6e1ab22f593c2f32f Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Fri, 19 May 2023 01:09:27 -0700 Subject: [PATCH 107/279] temp-sculpt-dyntopo: Improve dyntopo remesher quality * Sharp-by-angle boundaries now test for degenerate triangles. * Increases maximum number of edges that can be processed at once. --- source/blender/blenkernel/intern/dyntopo.cc | 5 +- .../blenkernel/intern/dyntopo_intern.hh | 8 ++- .../blender/blenkernel/intern/pbvh_bmesh.cc | 51 +++++++++++++++++-- .../editors/sculpt_paint/sculpt_api.cc | 2 +- 4 files changed, 58 insertions(+), 8 deletions(-) diff --git a/source/blender/blenkernel/intern/dyntopo.cc b/source/blender/blenkernel/intern/dyntopo.cc index 93e286d4a7e..7b0f482f8ae 100644 --- a/source/blender/blenkernel/intern/dyntopo.cc +++ b/source/blender/blenkernel/intern/dyntopo.cc @@ -2194,13 +2194,14 @@ extern "C" bool BKE_pbvh_bmesh_update_topology(SculptSession *ss, totop = 2; steps[1] = DYNTOPO_MAX_ITER / 3.0; - steps[0] = DYNTOPO_MAX_ITER <= steps[1] ? DYNTOPO_MAX_ITER : DYNTOPO_MAX_ITER - steps[1]; + steps[0] = DYNTOPO_MAX_ITER_SUBD; // DYNTOPO_MAX_ITER <= steps[1] ? DYNTOPO_MAX_ITER : + // DYNTOPO_MAX_ITER - steps[1]; } else if (mode & PBVH_Subdivide) { ops[0] = PBVH_Subdivide; totop = 1; - steps[0] = DYNTOPO_MAX_ITER; + steps[0] = DYNTOPO_MAX_ITER_SUBD; } else if (mode & PBVH_Collapse) { ops[0] = PBVH_Collapse; diff --git a/source/blender/blenkernel/intern/dyntopo_intern.hh b/source/blender/blenkernel/intern/dyntopo_intern.hh index 2d0163d040c..59abcef28a0 100644 --- a/source/blender/blenkernel/intern/dyntopo_intern.hh +++ b/source/blender/blenkernel/intern/dyntopo_intern.hh @@ -75,12 +75,17 @@ inline bool bm_elem_is_free(BMElem *elem, int htype) #define SCULPTVERT_VALENCE_TEMP SCULPTFLAG_SPLIT_TEMP +/* Which boundary types dyntopo should respect. */ + +/* Smooth boundaries */ #define SCULPTVERT_SMOOTH_BOUNDARY \ (SCULPT_BOUNDARY_MESH | SCULPT_BOUNDARY_FACE_SET | SCULPT_BOUNDARY_SHARP_MARK | \ SCULPT_BOUNDARY_SEAM | SCULPT_BOUNDARY_UV | SCULPT_BOUNDARY_SHARP_ANGLE) #define SCULPTVERT_SMOOTH_CORNER \ (SCULPT_CORNER_MESH | SCULPT_CORNER_FACE_SET | SCULPT_CORNER_SHARP_MARK | SCULPT_CORNER_SEAM | \ SCULPT_CORNER_UV | SCULPT_CORNER_SHARP_ANGLE) + +/* All boundaries */ #define SCULPTVERT_ALL_BOUNDARY \ (SCULPT_BOUNDARY_MESH | SCULPT_BOUNDARY_FACE_SET | SCULPT_BOUNDARY_SHARP_MARK | \ SCULPT_BOUNDARY_SEAM | SCULPT_BOUNDARY_UV | SCULPT_BOUNDARY_SHARP_ANGLE) @@ -88,7 +93,8 @@ inline bool bm_elem_is_free(BMElem *elem, int htype) (SCULPT_CORNER_MESH | SCULPT_CORNER_FACE_SET | SCULPT_CORNER_SHARP_MARK | SCULPT_CORNER_SEAM | \ SCULPT_CORNER_UV | SCULPT_CORNER_SHARP_ANGLE) -#define DYNTOPO_MAX_ITER 256 +#define DYNTOPO_MAX_ITER 1024 +#define DYNTOPO_MAX_ITER_SUBD 4096 #define DYNTOPO_USE_HEAP #define DYNTOPO_USE_MINMAX_HEAP diff --git a/source/blender/blenkernel/intern/pbvh_bmesh.cc b/source/blender/blenkernel/intern/pbvh_bmesh.cc index 3892e0017a6..9d552e5b187 100644 --- a/source/blender/blenkernel/intern/pbvh_bmesh.cc +++ b/source/blender/blenkernel/intern/pbvh_bmesh.cc @@ -845,7 +845,10 @@ static void pbvh_print_mem_size(PBVH *pbvh) float memsize3[3] = {(float)(ptrsize * pbvh->bm_idmap->map_size) / 1024.0f / 1024.0f, (float)(ptrsize * pbvh->bm_idmap->freelist.capacity()) / 1024.0f / 1024.0f, - pbvh->bm_idmap->free_idx_map ? (float)(4 * pbvh->bm_idmap->free_idx_map->capacity()) / 1024.0f / 1024.0f : 0.0f}; + pbvh->bm_idmap->free_idx_map ? + (float)(4 * pbvh->bm_idmap->free_idx_map->capacity()) / 1024.0f / + 1024.0f : + 0.0f}; printf("idmap sizes:\n map_size: %.2fmb freelist_len: %.2fmb free_ids_size: %.2fmb\n", memsize3[0], @@ -2097,6 +2100,44 @@ static int color_boundary_key(float col[4]) } #endif +static bool test_colinear_tri(BMFace *f) +{ + BMLoop *l = f->l_first; + + float area_limit = 0.00001f; + area_limit = len_squared_v3v3(l->v->co, l->next->v->co) * 0.001; + + return area_tri_v3(l->v->co, l->next->v->co, l->prev->v->co) <= area_limit; +} + +static float test_sharp_faces(BMFace *f1, BMFace *f2, float limit) +{ + float angle = saacos(dot_v3v3(f1->no, f2->no)); + +#if 0 // XXX + float no1[3], no2[3]; + normal_tri_v3(no1, f1->l_first->v->co, f1->l_first->next->v->co, f1->l_first->next->next->v->co); + normal_tri_v3(no2, f2->l_first->v->co, f2->l_first->next->v->co, f2->l_first->next->next->v->co); + + angle = saacos(dot_v3v3(no1, no2)); +#endif + + /* Detect coincident triangles. */ + if (f1->len == 3 && test_colinear_tri(f1)) { + return false; + } + if (f2->len == 3 && test_colinear_tri(f2)) { + return false; + } + + /* Try to ignore folded over edges. */ + if (angle > M_PI * 0.6) { + return false; + } + + return angle > limit; +} + void BKE_pbvh_update_vert_boundary(int cd_faceset_offset, int cd_vert_node_offset, int cd_face_node_offset, @@ -2172,12 +2213,13 @@ void BKE_pbvh_update_vert_boundary(int cd_faceset_offset, newflag |= SCULPTFLAG_PBVH_BOUNDARY; } - if (e->l) { - if (saacos(dot_v3v3(e->l->f->no, e->l->radial_next->f->no)) > sharp_angle_limit) { + if (e->l && e->l != e->l->radial_next) { + if (test_sharp_faces(e->l->f, e->l->radial_next->f, sharp_angle_limit)) { boundflag |= SCULPT_BOUNDARY_SHARP_ANGLE; sharp_angle_num++; } } + if (e->head.hflag & BM_ELEM_SEAM) { boundflag |= SCULPT_BOUNDARY_SEAM; seamcount++; @@ -4918,6 +4960,7 @@ float BKE_pbvh_bmesh_detail_size_avg_get(PBVH *pbvh) } namespace blender::bke::pbvh { + void update_sharp_boundary_bmesh(BMVert *v, int cd_boundary_flag, const float sharp_angle_limit) { int flag = BM_ELEM_CD_GET_INT(v, cd_boundary_flag); @@ -4936,7 +4979,7 @@ void update_sharp_boundary_bmesh(BMVert *v, int cd_boundary_flag, const float sh continue; } - if (saacos(dot_v3v3(e->l->f->no, e->l->radial_next->f->no)) > sharp_angle_limit) { + if (test_sharp_faces(e->l->f, e->l->radial_next->f, sharp_angle_limit)) { flag |= SCULPT_BOUNDARY_SHARP_ANGLE; sharp_num++; } diff --git a/source/blender/editors/sculpt_paint/sculpt_api.cc b/source/blender/editors/sculpt_paint/sculpt_api.cc index 67b4eafc676..927307738e7 100644 --- a/source/blender/editors/sculpt_paint/sculpt_api.cc +++ b/source/blender/editors/sculpt_paint/sculpt_api.cc @@ -260,7 +260,7 @@ eSculptBoundary SCULPT_edge_is_boundary(const SculptSession *ss, /* Some boundary types require an edge->poly map to be fully accurate. */ if (epmap_mask && ss->epmap) { - if (epmap_mask & SCULPT_BOUNDARY_FACE_SET) { + if (ss->face_sets && epmap_mask & SCULPT_BOUNDARY_FACE_SET) { MeshElemMap *polys = &ss->epmap[edge.i]; int fset = -1; -- 2.30.2 From 78a0e81d9a17afecee636d8b4d990f7c8d2f9f4b Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Fri, 19 May 2023 02:02:04 -0700 Subject: [PATCH 108/279] temp-sculpt-dyntopo: Fix uv reprojection bug and cleanup code --- source/blender/blenkernel/BKE_pbvh.h | 1 + source/blender/blenkernel/intern/dyntopo.cc | 56 ++++--------------- .../blenkernel/intern/dyntopo_intern.hh | 7 ++- source/blender/blenkernel/intern/paint.cc | 2 +- .../blender/blenkernel/intern/pbvh_bmesh.cc | 6 +- source/blender/bmesh/intern/bmesh_interp.c | 4 +- source/blender/bmesh/intern/bmesh_log.cc | 19 +------ .../blender/bmesh/intern/bmesh_log_intern.h | 4 +- .../editors/sculpt_paint/sculpt_api.cc | 3 +- .../editors/sculpt_paint/sculpt_dyntopo.cc | 2 +- .../editors/sculpt_paint/sculpt_ops.cc | 6 +- .../editors/sculpt_paint/sculpt_undo.cc | 22 +------- 12 files changed, 35 insertions(+), 97 deletions(-) diff --git a/source/blender/blenkernel/BKE_pbvh.h b/source/blender/blenkernel/BKE_pbvh.h index a0bf5d29d23..565137a2f4f 100644 --- a/source/blender/blenkernel/BKE_pbvh.h +++ b/source/blender/blenkernel/BKE_pbvh.h @@ -1057,6 +1057,7 @@ PBVHNode *BKE_pbvh_node_from_index(PBVH *pbvh, int node_i); struct BMesh *BKE_pbvh_reorder_bmesh(PBVH *pbvh); void BKE_pbvh_sharp_limit_set(PBVH *pbvh, float limit); +float BKE_pbvh_test_sharp_faces_bmesh(BMFace *f1, BMFace *f2, float limit); void BKE_pbvh_update_vert_boundary(int cd_faceset_offset, int cd_vert_node_offset, int cd_face_node_offset, diff --git a/source/blender/blenkernel/intern/dyntopo.cc b/source/blender/blenkernel/intern/dyntopo.cc index 7b0f482f8ae..816a2caab38 100644 --- a/source/blender/blenkernel/intern/dyntopo.cc +++ b/source/blender/blenkernel/intern/dyntopo.cc @@ -82,7 +82,7 @@ static void edge_queue_create_local(struct EdgeQueueContext *eq_ctx, const bool use_projected, PBVHTopologyUpdateMode local_mode); -static void surface_smooth_v_safe( +ATTR_NO_OPT static void surface_smooth_v_safe( SculptSession *ss, PBVH *pbvh, BMVert *v, float fac, bool reproject_cdata) { float co[3]; @@ -129,28 +129,6 @@ static void surface_smooth_v_safe( fac *= 0.1; } - const int sharp_tag = 1 << 13; - float limit = 22.0 / 180.0 * M_PI; - - do { - if (!e->l) { - continue; - } - - BMLoop *l1 = e->l; - BMLoop *l2 = l1->radial_next; - - bool sharp = l1 == l2; - sharp = sharp || saacos(dot_v3v3(l1->f->no, l2->f->no)) > limit; - - if (sharp && (bound1 & sharp_tag)) { - return; /* corner */ - } - else if (sharp) { - bound1 |= sharp_tag; - } - } while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e); - do { BMVert *v2 = e->v1 == v ? e->v2 : e->v1; PBVHVertRef vertex2 = {reinterpret_cast(v2)}; @@ -162,14 +140,6 @@ static void surface_smooth_v_safe( int boundflag2 = BM_ELEM_CD_GET_INT(v2, pbvh->cd_boundary_flag); int bound2 = boundflag2 & boundmask; - if (e->l) { - bool sharp = e->l == e->l->radial_next; - sharp = sharp || saacos(dot_v3v3(e->l->f->no, e->l->radial_next->f->no)) > limit; - if (sharp) { - bound2 |= sharp_tag; - } - } - if (bound1 && !bound2) { continue; } @@ -2107,7 +2077,6 @@ extern "C" bool BKE_pbvh_bmesh_update_topology(SculptSession *ss, eq_ctx.min_elen = 1e17; eq_ctx.totedge = 0.0f; eq_ctx.local_mode = false; - eq_ctx.surface_smooth_fac = disable_surface_relax ? 0.0f : DYNTOPO_SAFE_SMOOTH_FAC; eq_ctx.mode = mode; eq_ctx.reproject_cdata = CustomData_has_layer(&pbvh->header.bm->ldata, CD_PROP_FLOAT2) && !ss->ignore_uvs; @@ -2130,6 +2099,8 @@ extern "C" bool BKE_pbvh_bmesh_update_topology(SculptSession *ss, #endif + eq_ctx.surface_smooth_fac = disable_surface_relax ? 0.0f : DYNTOPO_SAFE_SMOOTH_FAC; + if (mode & PBVH_LocalSubdivide) { mode |= PBVH_Subdivide; } @@ -2327,8 +2298,6 @@ extern "C" bool BKE_pbvh_bmesh_update_topology(SculptSession *ss, VALIDATE_LOG(pbvh->bm_log); count_cold++; - // XXX - // BM_log_entry_add_ex(pbvh->header.bm, pbvh->bm_log, true); break; } case PBVH_LocalSubdivide: @@ -2378,7 +2347,6 @@ extern "C" bool BKE_pbvh_bmesh_update_topology(SculptSession *ss, MEM_SAFE_FREE(edges); BLI_smallhash_release(&subd_edges); - // XXX if (mode & PBVH_Cleanup) { modified |= do_cleanup_3_4( &eq_ctx, pbvh, center, eq_ctx.view_normal, radius, use_frontface, use_projected); @@ -3201,7 +3169,7 @@ void BKE_dyntopo_default_params(DynRemeshParams *params, float edge_size) void BKE_dyntopo_free(DynTopoState *ds) { if (ds->is_fake_pbvh) { - BM_log_free(ds->pbvh->bm_log, false); + BM_log_free(ds->pbvh->bm_log); BM_idmap_destroy(ds->pbvh->bm_idmap); PBVHNode *node = ds->pbvh->nodes; @@ -3425,10 +3393,10 @@ void BKE_pbvh_bmesh_add_face(PBVH *pbvh, struct BMFace *f, bool log_face, bool f bm_logstack_pop(); } -void BKE_sculpt_reproject_cdata(SculptSession *ss, - PBVHVertRef vertex, - float startco[3], - float startno[3]) +ATTR_NO_OPT void BKE_sculpt_reproject_cdata(SculptSession *ss, + PBVHVertRef vertex, + float startco[3], + float startno[3]) { BMVert *v = (BMVert *)vertex.i; BMEdge *e; @@ -3471,7 +3439,7 @@ void BKE_sculpt_reproject_cdata(SculptSession *ss, e = v->e; - /* first clear some flags */ + /* First clear some flags. */ do { e->head.api_flag &= ~tag; @@ -3530,7 +3498,7 @@ void BKE_sculpt_reproject_cdata(SculptSession *ss, const int cd_uv = uvlayer[i].offset; float *luv = BM_ELEM_CD_PTR(l2, cd_uv); - // check that we are not part of a uv seam + /* Check that we are not part of a uv seam. */ if (!first) { const float dx = lastuvs[i * 2] - luv[0]; const float dy = lastuvs[i * 2 + 1] - luv[1]; @@ -3564,7 +3532,7 @@ void BKE_sculpt_reproject_cdata(SculptSession *ss, int totloop = ls.size(); - /* original (l->prev, l, l->next) projections for each loop ('l' remains unchanged) */ + /* Original (l->prev, l, l->next) projections for each loop ('l' remains unchanged). */ char *_blocks = (char *)alloca(ldata->totsize * totloop); void **blocks = (void **)BLI_array_alloca(blocks, totloop); @@ -3684,7 +3652,7 @@ void BKE_sculpt_reproject_cdata(SculptSession *ss, tots[i] = 0; } - // re-snap uvs + /* Re-snap uvs. */ v = (BMVert *)vertex.i; e = v->e; diff --git a/source/blender/blenkernel/intern/dyntopo_intern.hh b/source/blender/blenkernel/intern/dyntopo_intern.hh index 59abcef28a0..9994db02add 100644 --- a/source/blender/blenkernel/intern/dyntopo_intern.hh +++ b/source/blender/blenkernel/intern/dyntopo_intern.hh @@ -120,9 +120,10 @@ inline bool bm_elem_is_free(BMElem *elem, int htype) //#define FANCY_EDGE_WEIGHTS <= too slow //#define SKINNY_EDGE_FIX -/* slightly relax geometry by this factor along surface tangents - to improve convergence of remesher */ -#define DYNTOPO_SAFE_SMOOTH_FAC 0.05f +/* Slightly relax geometry by this factor along surface tangents + * to improve convergence of dyntopo remesher. + */ +#define DYNTOPO_SAFE_SMOOTH_FAC 0.015f #ifdef USE_EDGEQUEUE_EVEN_SUBDIV # include "BKE_global.h" diff --git a/source/blender/blenkernel/intern/paint.cc b/source/blender/blenkernel/intern/paint.cc index f25d5b4b263..90e340fded3 100644 --- a/source/blender/blenkernel/intern/paint.cc +++ b/source/blender/blenkernel/intern/paint.cc @@ -1555,7 +1555,7 @@ void BKE_sculptsession_free(Object *ob) if (ss->bm_log) { /* Does not free the actual entries, the undo system does that */ - BM_log_free(ss->bm_log, true); + BM_log_free(ss->bm_log); ss->bm_log = nullptr; } diff --git a/source/blender/blenkernel/intern/pbvh_bmesh.cc b/source/blender/blenkernel/intern/pbvh_bmesh.cc index 9d552e5b187..fc0bdaeaf27 100644 --- a/source/blender/blenkernel/intern/pbvh_bmesh.cc +++ b/source/blender/blenkernel/intern/pbvh_bmesh.cc @@ -2110,7 +2110,7 @@ static bool test_colinear_tri(BMFace *f) return area_tri_v3(l->v->co, l->next->v->co, l->prev->v->co) <= area_limit; } -static float test_sharp_faces(BMFace *f1, BMFace *f2, float limit) +float BKE_pbvh_test_sharp_faces_bmesh(BMFace *f1, BMFace *f2, float limit) { float angle = saacos(dot_v3v3(f1->no, f2->no)); @@ -2214,7 +2214,7 @@ void BKE_pbvh_update_vert_boundary(int cd_faceset_offset, } if (e->l && e->l != e->l->radial_next) { - if (test_sharp_faces(e->l->f, e->l->radial_next->f, sharp_angle_limit)) { + if (BKE_pbvh_test_sharp_faces_bmesh(e->l->f, e->l->radial_next->f, sharp_angle_limit)) { boundflag |= SCULPT_BOUNDARY_SHARP_ANGLE; sharp_angle_num++; } @@ -4979,7 +4979,7 @@ void update_sharp_boundary_bmesh(BMVert *v, int cd_boundary_flag, const float sh continue; } - if (test_sharp_faces(e->l->f, e->l->radial_next->f, sharp_angle_limit)) { + if (BKE_pbvh_test_sharp_faces_bmesh(e->l->f, e->l->radial_next->f, sharp_angle_limit)) { flag |= SCULPT_BOUNDARY_SHARP_ANGLE; sharp_num++; } diff --git a/source/blender/bmesh/intern/bmesh_interp.c b/source/blender/bmesh/intern/bmesh_interp.c index 8402c5332c2..1f8c25129fc 100644 --- a/source/blender/bmesh/intern/bmesh_interp.c +++ b/source/blender/bmesh/intern/bmesh_interp.c @@ -741,12 +741,12 @@ void BM_loop_interp_from_face( /* interpolate */ interp_weights_poly_v2(w, cos_2d, f_src->len, co); - /* Clamp negative weights to zero. Prevents numerical + /* Clamp excessively negative weights to zero. Prevents numerical * instability when interpolating UVs. */ float totw = 0.0f; for (int i = 0; i < f_src->len; i++) { - if (w[i] < 0.0f) { + if (w[i] < -5.0) { w[i] = 0.0f; } diff --git a/source/blender/bmesh/intern/bmesh_log.cc b/source/blender/bmesh/intern/bmesh_log.cc index 21a208f63f6..752507df094 100644 --- a/source/blender/bmesh/intern/bmesh_log.cc +++ b/source/blender/bmesh/intern/bmesh_log.cc @@ -1625,28 +1625,13 @@ void BM_log_set_idmap(BMLog *log, struct BMIdMap *idmap) log->set_idmap(idmap); } -/* Free all the data in a BMLog including the log itself - * safe_mode means log->refcount will be checked, and if nonzero log will not be freed - */ -static bool bm_log_free_direct(BMLog *log, bool safe_mode) -{ - if (safe_mode && log->refcount) { - return false; - } - - log->dead = true; - log->free_all_entries(); - - return true; -} - -// if true, make sure to call BM_log_free on the log +/* XXX get rid of bmlog refcounting. */ bool BM_log_is_dead(BMLog *log) { return log->dead; } -bool BM_log_free(BMLog *log, bool safe_mode) +bool BM_log_free(BMLog *log) { BMLogEntry *entry = log->first_entry; diff --git a/source/blender/bmesh/intern/bmesh_log_intern.h b/source/blender/bmesh/intern/bmesh_log_intern.h index 999f286aa71..f3d6635399e 100644 --- a/source/blender/bmesh/intern/bmesh_log_intern.h +++ b/source/blender/bmesh/intern/bmesh_log_intern.h @@ -103,8 +103,8 @@ BMLog *BM_log_create(BMesh *bm, struct BMIdMap *idmap); */ BMLog *BM_log_from_existing_entries_create(BMesh *bm, struct BMIdMap *idmap, BMLogEntry *entry); -/* Free all the data in a BMLog including the log itself */ -bool BM_log_free(BMLog *log, bool safe_mode); +/* Does not free the log's entries, just the BMLog itself. */ +bool BM_log_free(BMLog *log); BMLog *BM_log_unfreeze(BMesh *bm, BMLogEntry *entry); diff --git a/source/blender/editors/sculpt_paint/sculpt_api.cc b/source/blender/editors/sculpt_paint/sculpt_api.cc index 927307738e7..2fc95850f7d 100644 --- a/source/blender/editors/sculpt_paint/sculpt_api.cc +++ b/source/blender/editors/sculpt_paint/sculpt_api.cc @@ -210,7 +210,8 @@ eSculptBoundary SCULPT_edge_is_boundary(const SculptSession *ss, BMEdge *e = (BMEdge *)edge.i; if (e->l && e->l != e->l->radial_next && typemask & SCULPT_BOUNDARY_SHARP_ANGLE) { - if (saacos(dot_v3v3(e->l->f->no, e->l->radial_next->f->no)) > ss->sharp_angle_limit) { + if (BKE_pbvh_test_sharp_faces_bmesh(e->l->f, e->l->radial_next->f, ss->sharp_angle_limit)) + { ret |= SCULPT_BOUNDARY_SHARP_ANGLE; } } diff --git a/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc b/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc index 988c6fcfe40..e2f45f37cbc 100644 --- a/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc +++ b/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc @@ -674,7 +674,7 @@ static void SCULPT_dynamic_topology_disable_ex( } if (ss->bm_log) { - BM_log_free(ss->bm_log, true); + BM_log_free(ss->bm_log); ss->bm_log = nullptr; } diff --git a/source/blender/editors/sculpt_paint/sculpt_ops.cc b/source/blender/editors/sculpt_paint/sculpt_ops.cc index 8bcb513f64a..5f70ec58e51 100644 --- a/source/blender/editors/sculpt_paint/sculpt_ops.cc +++ b/source/blender/editors/sculpt_paint/sculpt_ops.cc @@ -516,7 +516,7 @@ void ED_object_sculptmode_enter_ex(Main *bmain, } else if (need_bmlog) { if (ob->sculpt->bm_log) { - BM_log_free(ob->sculpt->bm_log, true); + BM_log_free(ob->sculpt->bm_log); ob->sculpt->bm_log = nullptr; } @@ -1432,8 +1432,8 @@ static int sculpt_regularize_rake_exec(bContext *C, wmOperator *op) int *boundflag = vertex_attr_ptr(vertex, ss->attrs.boundary_flags); - if (*boundflag & - (SCULPT_CORNER_MESH | SCULPT_CORNER_FACE_SET | SCULPT_CORNER_SHARP_MARK | SCULPT_CORNER_SEAM)) + if (*boundflag & (SCULPT_CORNER_MESH | SCULPT_CORNER_FACE_SET | SCULPT_CORNER_SHARP_MARK | + SCULPT_CORNER_SEAM)) { continue; } diff --git a/source/blender/editors/sculpt_paint/sculpt_undo.cc b/source/blender/editors/sculpt_paint/sculpt_undo.cc index b451c6b25e1..d6de0c2e82a 100644 --- a/source/blender/editors/sculpt_paint/sculpt_undo.cc +++ b/source/blender/editors/sculpt_paint/sculpt_undo.cc @@ -945,7 +945,8 @@ static void update_unode_bmesh_memsize(SculptUndoNode *unode) } unode->undo_size = BM_log_entry_size(unode->bm_entry); - //printf("unode->unode_size: size: %.4fmb\n", __func__, float(unode->undo_size) / 1024.0f / 1024.0f); + // printf("unode->unode_size: size: %.4fmb\n", __func__, float(unode->undo_size) / 1024.0f / + // 1024.0f); // add new size usculpt->undo_size += unode->undo_size; @@ -2702,25 +2703,6 @@ static void sculpt_undo_push_begin_ex(Object *ob, const char *name, bool no_firs sculpt_save_active_attribute_color(ob, &us->active_color_end); us->active_color_end.was_set = false; } - - SculptSession *ss = ob->sculpt; - - /* When pushing an undo node after undoing to the start of the stack - * the log ref count hits zero; we must detect this and handle it. - */ - - if (ss && ss->bm && ss->bm_log && BM_log_is_dead(ss->bm_log)) { - /* Forcibly destroy all entries (the 'true' parameter). */ - BM_log_free(ss->bm_log, true); - - BKE_sculpt_ensure_idmap(ob); - - ss->bm_log = BM_log_create(ss->bm, ss->bm_idmap); - - if (ss->pbvh) { - BKE_pbvh_set_bm_log(ss->pbvh, ss->bm_log); - } - } } void SCULPT_undo_push_begin_ex(Object *ob, const char *name) -- 2.30.2 From 88bab95b8c3e24c4a87e38da4bfe88b2429499cd Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Fri, 19 May 2023 02:57:55 -0700 Subject: [PATCH 109/279] temp-sculpt-dyntopo: Tweak UI for new smooth boundary settings --- scripts/startup/bl_ui/space_view3d_toolbar.py | 42 ++++++++++--------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/scripts/startup/bl_ui/space_view3d_toolbar.py b/scripts/startup/bl_ui/space_view3d_toolbar.py index f765c052cdc..a5060fbc5cd 100644 --- a/scripts/startup/bl_ui/space_view3d_toolbar.py +++ b/scripts/startup/bl_ui/space_view3d_toolbar.py @@ -1037,16 +1037,32 @@ class VIEW3D_PT_sculpt_options(Panel, View3DPaintPanel): col.label(text="Smooth Boundaries") col = layout.column(align=True) - col.prop(ups, "smooth_boundary_mesh") - col.prop(ups, "smooth_boundary_face_set") + + col.prop(ups, "smooth_boundary_seam", text="Relax Marked Seams") + col.prop(ups, "smooth_boundary_uv", text="Relax UV Seams") + col.prop(ups, "smooth_boundary_face_set", text="Relax Face Sets") + + col.separator(); row = col.row() row.enabled = ups.smooth_boundary_face_set - row.prop(ups, "hard_edge_mode", text="Sharp Face Sets") + row.prop(ups, "hard_edge_mode", text="Crease Face Sets") - col.prop(ups, "smooth_boundary_seam") - col.prop(ups, "smooth_boundary_sharp_mark") - col.prop(ups, "smooth_boundary_sharp_angle") + UnifiedPaintPanel.prop_unified( + col, + context, + brush, + "hard_corner_pin", + slider=True, + unified_name = "use_unified_hard_corner_pin", + text="Corner Pin" + ) + + col.separator(); + + col.prop(ups, "smooth_boundary_mesh", text="Crease Boundaries") + col.prop(ups, "smooth_boundary_sharp_mark", text="Crease Marked Sharp") + col.prop(ups, "smooth_boundary_sharp_angle", text="Crease By Angle") row = col.row() row.enabled = ups.smooth_boundary_sharp_angle @@ -1058,20 +1074,6 @@ class VIEW3D_PT_sculpt_options(Panel, View3DPaintPanel): unified_name = "use_unified_sharp_angle_limit" ) - col.prop(ups, "smooth_boundary_uv") - col.separator(); - - UnifiedPaintPanel.prop_unified( - layout, - context, - brush, - "hard_corner_pin", - slider=True, - unified_name = "use_unified_hard_corner_pin", - text="Corner Pin" - ) - - class VIEW3D_PT_sculpt_options_gravity(Panel, View3DPaintPanel): bl_context = ".sculpt_mode" # dot on purpose (access from topbar) bl_parent_id = "VIEW3D_PT_sculpt_options" -- 2.30.2 From bd35096a79ff5405eb081abf8468bbc71c6425cb Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Sat, 20 May 2023 02:08:01 -0700 Subject: [PATCH 110/279] temp-sculpt-dyntopo: Cleanup: Move code around to avoid long rebuild times * Move some of the SCULPT_TOOL_DOES_XXX macros to a new file, BKE_sculpt.h from DNA_brush_enums.h (they were there because rna needs them). * Fix lazy updating of sharp angle boundaries. --- source/blender/blenkernel/BKE_sculpt.h | 47 +++++++++++++++++++ source/blender/blenkernel/CMakeLists.txt | 1 + source/blender/blenkernel/intern/pbvh.cc | 1 + source/blender/editors/sculpt_paint/sculpt.cc | 10 +++- .../editors/sculpt_paint/sculpt_api.cc | 12 +++-- .../editors/sculpt_paint/sculpt_intern.hh | 4 +- source/blender/makesdna/DNA_brush_enums.h | 46 ------------------ source/blender/makesrna/intern/rna_brush.c | 1 + 8 files changed, 70 insertions(+), 52 deletions(-) create mode 100644 source/blender/blenkernel/BKE_sculpt.h diff --git a/source/blender/blenkernel/BKE_sculpt.h b/source/blender/blenkernel/BKE_sculpt.h new file mode 100644 index 00000000000..abea5a8a3ab --- /dev/null +++ b/source/blender/blenkernel/BKE_sculpt.h @@ -0,0 +1,47 @@ +#pragma once + +#define SCULPT_TOOL_HAS_VCOL_BOUNDARY_SMOOTH(t) ELEM(t, SCULPT_TOOL_PAINT, SCULPT_TOOL_SMEAR) + +/** When #BRUSH_ACCUMULATE is used */ +#define SCULPT_TOOL_HAS_ACCUMULATE(t) \ + ELEM(t, \ + SCULPT_TOOL_DRAW, \ + SCULPT_TOOL_DRAW_SHARP, \ + SCULPT_TOOL_SLIDE_RELAX, \ + SCULPT_TOOL_CREASE, \ + SCULPT_TOOL_BLOB, \ + SCULPT_TOOL_INFLATE, \ + SCULPT_TOOL_CLAY, \ + SCULPT_TOOL_CLAY_STRIPS, \ + SCULPT_TOOL_CLAY_THUMB, \ + SCULPT_TOOL_ROTATE, \ + SCULPT_TOOL_SCRAPE, \ + SCULPT_TOOL_FLATTEN) + +#define SCULPT_TOOL_HAS_NORMAL_WEIGHT(t) \ + ELEM(t, SCULPT_TOOL_GRAB, SCULPT_TOOL_SNAKE_HOOK, SCULPT_TOOL_ELASTIC_DEFORM) + +#define SCULPT_TOOL_HAS_RAKE(t) ELEM(t, SCULPT_TOOL_SNAKE_HOOK) + +#define SCULPT_TOOL_HAS_DYNTOPO(t) \ + (ELEM(t, /* These brushes, as currently coded, cannot support dynamic topology */ \ + SCULPT_TOOL_GRAB, \ + SCULPT_TOOL_CLOTH, \ + SCULPT_TOOL_DISPLACEMENT_ERASER, \ + SCULPT_TOOL_ELASTIC_DEFORM, \ + SCULPT_TOOL_BOUNDARY, \ + SCULPT_TOOL_POSE, \ + SCULPT_TOOL_DRAW_FACE_SETS, \ +\ + /* These brushes could handle dynamic topology, \ \ + * but user feedback indicates it's better not to */ \ + SCULPT_TOOL_MASK) == 0) + +#define SCULPT_TOOL_HAS_TOPOLOGY_RAKE(t) \ + (ELEM(t, /* These brushes, as currently coded, cannot support topology rake. */ \ + SCULPT_TOOL_GRAB, \ + SCULPT_TOOL_ELASTIC_DEFORM, \ + SCULPT_TOOL_ROTATE, \ + SCULPT_TOOL_DISPLACEMENT_ERASER, \ + SCULPT_TOOL_SLIDE_RELAX, \ + SCULPT_TOOL_MASK) == 0) diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt index 35c648cc7d7..4cd03b281a7 100644 --- a/source/blender/blenkernel/CMakeLists.txt +++ b/source/blender/blenkernel/CMakeLists.txt @@ -468,6 +468,7 @@ set(SRC BKE_rigidbody.h BKE_scene.h BKE_screen.h + BKE_sculpt.h BKE_sculpt.hh BKE_sequencer_offscreen.h BKE_shader_fx.h diff --git a/source/blender/blenkernel/intern/pbvh.cc b/source/blender/blenkernel/intern/pbvh.cc index f84b29201b3..16d3f2ae9f7 100644 --- a/source/blender/blenkernel/intern/pbvh.cc +++ b/source/blender/blenkernel/intern/pbvh.cc @@ -59,6 +59,7 @@ #include "pbvh_intern.hh" #include +#include using blender::float3; using blender::IndexRange; diff --git a/source/blender/editors/sculpt_paint/sculpt.cc b/source/blender/editors/sculpt_paint/sculpt.cc index 11651084878..1ddc219824b 100644 --- a/source/blender/editors/sculpt_paint/sculpt.cc +++ b/source/blender/editors/sculpt_paint/sculpt.cc @@ -1248,7 +1248,7 @@ static void sculpt_vertex_neighbors_get_faces_vemap(const SculptSession *ss, continue; } - sculpt_vertex_neighbor_add( + sculpt_vertex_neighbor_add_nocheck( iter, BKE_pbvh_make_vref(v), BKE_pbvh_make_eref(vert_map->indices[i]), v); } @@ -6917,6 +6917,14 @@ void SCULPT_boundary_info_ensure(Object *object) MEM_freeN(adjacent_faces_edge_count); } +void SCULPT_ensure_vemap(SculptSession *ss) +{ + if (BKE_pbvh_type(ss->pbvh) != PBVH_BMESH && !ss->vemap) { + BKE_mesh_vert_edge_map_create( + &ss->vemap, &ss->vemap_mem, ss->edges.data(), ss->totvert, ss->totedges); + } +} + void SCULPT_ensure_epmap(SculptSession *ss) { if (BKE_pbvh_type(ss->pbvh) != PBVH_BMESH && !ss->epmap) { diff --git a/source/blender/editors/sculpt_paint/sculpt_api.cc b/source/blender/editors/sculpt_paint/sculpt_api.cc index 2fc95850f7d..64ef1366f6d 100644 --- a/source/blender/editors/sculpt_paint/sculpt_api.cc +++ b/source/blender/editors/sculpt_paint/sculpt_api.cc @@ -441,7 +441,9 @@ static void faces_update_boundary_flags(const SculptSession *ss, const PBVHVertR } } -static bool sculpt_vertex_ensure_boundary(const SculptSession *ss, const PBVHVertRef vertex) +static bool sculpt_vertex_ensure_boundary(const SculptSession *ss, + const PBVHVertRef vertex, + int mask) { eSculptBoundary flag = *vertex_attr_ptr(vertex, ss->attrs.boundary_flags); bool needs_update = flag & SCULPT_BOUNDARY_NEEDS_UPDATE; @@ -465,7 +467,9 @@ static bool sculpt_vertex_ensure_boundary(const SculptSession *ss, const PBVHVer !ss->ignore_uvs, ss->sharp_angle_limit); } - else if (flag & SCULPT_BOUNDARY_UPDATE_SHARP_ANGLE) { + else if ((mask & (SCULPT_BOUNDARY_SHARP_ANGLE | SCULPT_CORNER_SHARP_ANGLE)) && + flag & SCULPT_BOUNDARY_UPDATE_SHARP_ANGLE) + { blender::bke::pbvh::update_sharp_boundary_bmesh( v, ss->attrs.boundary_flags->bmesh_cd_offset, ss->sharp_angle_limit); } @@ -495,7 +499,7 @@ eSculptCorner SCULPT_vertex_is_corner(const SculptSession *ss, const PBVHVertRef vertex, eSculptCorner cornertype) { - sculpt_vertex_ensure_boundary(ss, vertex); + sculpt_vertex_ensure_boundary(ss, vertex, int(cornertype)); eSculptCorner flag = eSculptCorner(vertex_attr_get(vertex, ss->attrs.boundary_flags)); return flag & cornertype; @@ -507,7 +511,7 @@ eSculptBoundary SCULPT_vertex_is_boundary(const SculptSession *ss, { eSculptBoundary flag = vertex_attr_get(vertex, ss->attrs.boundary_flags); - sculpt_vertex_ensure_boundary(ss, vertex); + sculpt_vertex_ensure_boundary(ss, vertex, int(boundary_types)); if (BKE_pbvh_type(ss->pbvh) == PBVH_GRIDS && boundary_types & SCULPT_BOUNDARY_MESH) { /* TODO: BKE_pbvh_update_vert_boundary_grids does not yet support mesh boundaries for diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.hh b/source/blender/editors/sculpt_paint/sculpt_intern.hh index 7fcc487df46..e12c99df31d 100644 --- a/source/blender/editors/sculpt_paint/sculpt_intern.hh +++ b/source/blender/editors/sculpt_paint/sculpt_intern.hh @@ -17,6 +17,7 @@ #include "BKE_attribute.h" #include "BKE_paint.h" #include "BKE_pbvh.h" +#include "BKE_sculpt.h" #include "BKE_sculpt.hh" #include "BLI_bitmap.h" @@ -1344,7 +1345,7 @@ void SCULPT_face_visibility_all_invert(SculptSession *ss); void SCULPT_face_visibility_all_set(Object *ob, bool visible); /* Flags all the vertices of face for boundary update. For PBVH_GRIDS - * this includes all the verts in all the grids belonging to that face. + * this includes all the verts in all the grids belonging to that face. */ void SCULPT_face_mark_boundary_update(SculptSession *ss, PBVHFaceRef face); @@ -2379,6 +2380,7 @@ void SCULPT_cotangents_begin(struct Object *ob, SculptSession *ss); void SCULPT_ensure_persistent_layers(SculptSession *ss, struct Object *ob); void SCULPT_ensure_epmap(SculptSession *ss); +void SCULPT_ensure_vemap(SculptSession *ss); bool SCULPT_dyntopo_automasking_init(const SculptSession *ss, Sculpt *sd, const Brush *br, diff --git a/source/blender/makesdna/DNA_brush_enums.h b/source/blender/makesdna/DNA_brush_enums.h index 719a69d33b1..06fb92c6ae4 100644 --- a/source/blender/makesdna/DNA_brush_enums.h +++ b/source/blender/makesdna/DNA_brush_enums.h @@ -514,52 +514,6 @@ typedef enum eBrushCurvesSculptTool { CURVES_SCULPT_TOOL_SLIDE = 10, } eBrushCurvesSculptTool; -#define SCULPT_TOOL_HAS_VCOL_BOUNDARY_SMOOTH(t) ELEM(t, SCULPT_TOOL_PAINT, SCULPT_TOOL_SMEAR) - -/** When #BRUSH_ACCUMULATE is used */ -#define SCULPT_TOOL_HAS_ACCUMULATE(t) \ - ELEM(t, \ - SCULPT_TOOL_DRAW, \ - SCULPT_TOOL_DRAW_SHARP, \ - SCULPT_TOOL_SLIDE_RELAX, \ - SCULPT_TOOL_CREASE, \ - SCULPT_TOOL_BLOB, \ - SCULPT_TOOL_INFLATE, \ - SCULPT_TOOL_CLAY, \ - SCULPT_TOOL_CLAY_STRIPS, \ - SCULPT_TOOL_CLAY_THUMB, \ - SCULPT_TOOL_ROTATE, \ - SCULPT_TOOL_SCRAPE, \ - SCULPT_TOOL_FLATTEN) - -#define SCULPT_TOOL_HAS_NORMAL_WEIGHT(t) \ - ELEM(t, SCULPT_TOOL_GRAB, SCULPT_TOOL_SNAKE_HOOK, SCULPT_TOOL_ELASTIC_DEFORM) - -#define SCULPT_TOOL_HAS_RAKE(t) ELEM(t, SCULPT_TOOL_SNAKE_HOOK) - -#define SCULPT_TOOL_HAS_DYNTOPO(t) \ - (ELEM(t, /* These brushes, as currently coded, cannot support dynamic topology */ \ - SCULPT_TOOL_GRAB, \ - SCULPT_TOOL_CLOTH, \ - SCULPT_TOOL_DISPLACEMENT_ERASER, \ - SCULPT_TOOL_ELASTIC_DEFORM, \ - SCULPT_TOOL_BOUNDARY, \ - SCULPT_TOOL_POSE, \ - SCULPT_TOOL_DRAW_FACE_SETS, \ -\ - /* These brushes could handle dynamic topology, \ \ - * but user feedback indicates it's better not to */ \ - SCULPT_TOOL_MASK) == 0) - -#define SCULPT_TOOL_HAS_TOPOLOGY_RAKE(t) \ - (ELEM(t, /* These brushes, as currently coded, cannot support topology rake. */ \ - SCULPT_TOOL_GRAB, \ - SCULPT_TOOL_ELASTIC_DEFORM, \ - SCULPT_TOOL_ROTATE, \ - SCULPT_TOOL_DISPLACEMENT_ERASER, \ - SCULPT_TOOL_SLIDE_RELAX, \ - SCULPT_TOOL_MASK) == 0) - /** #ImagePaintSettings.tool */ typedef enum eBrushImagePaintTool { PAINT_TOOL_DRAW = 0, diff --git a/source/blender/makesrna/intern/rna_brush.c b/source/blender/makesrna/intern/rna_brush.c index fdcdbc5f3d7..353aa9ba993 100644 --- a/source/blender/makesrna/intern/rna_brush.c +++ b/source/blender/makesrna/intern/rna_brush.c @@ -15,6 +15,7 @@ #include "DNA_workspace_types.h" #include "BKE_layer.h" +#include "BKE_sculpt.h" #include "BLI_math.h" -- 2.30.2 From 3ab1999b4134b9d0fcc6f4e61ffac05ac5b1503d Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Sun, 21 May 2023 13:10:28 -0700 Subject: [PATCH 111/279] temp-sculpt-dyntopo: Make detail flood fill dynamic, add quality slider * Detail flood fill now updates the mesh in real time via a modal mode and the jobs API. * Added a quality slider to control dyntopo quality. --- scripts/startup/bl_ui/space_view3d_toolbar.py | 11 + source/blender/blenkernel/BKE_pbvh.h | 7 +- source/blender/blenkernel/BKE_sculpt.h | 5 +- source/blender/blenkernel/intern/brush.cc | 4 + source/blender/blenkernel/intern/dyntopo.cc | 129 +++++--- .../blenkernel/intern/dyntopo_intern.hh | 2 +- source/blender/blenkernel/intern/paint.cc | 3 +- .../blender/blenkernel/intern/pbvh_bmesh.cc | 11 +- .../blenloader/intern/versioning_300.cc | 22 ++ source/blender/editors/sculpt_paint/sculpt.cc | 5 +- .../editors/sculpt_paint/sculpt_api.cc | 1 + .../editors/sculpt_paint/sculpt_detail.cc | 276 +++++++++++++----- .../editors/sculpt_paint/sculpt_intern.hh | 2 +- source/blender/makesdna/DNA_brush_defaults.h | 3 +- source/blender/makesdna/DNA_brush_enums.h | 5 +- source/blender/makesdna/DNA_brush_types.h | 3 +- source/blender/makesrna/intern/rna_brush.c | 12 +- 17 files changed, 368 insertions(+), 133 deletions(-) diff --git a/scripts/startup/bl_ui/space_view3d_toolbar.py b/scripts/startup/bl_ui/space_view3d_toolbar.py index a5060fbc5cd..0864436b582 100644 --- a/scripts/startup/bl_ui/space_view3d_toolbar.py +++ b/scripts/startup/bl_ui/space_view3d_toolbar.py @@ -949,6 +949,10 @@ class VIEW3D_PT_sculpt_dyntopo(Panel, View3DPaintPanel): if UnifiedPaintPanel.get_dyntopo_prop(context, brush, "mode") in {'CONSTANT', 'MANUAL'}: col.operator("sculpt.detail_flood_fill") + col.prop(WindowManager.operator_properties_last("sculpt.detail_flood_fill"), "interactive") + row = col.row() + row.enabled = not WindowManager.operator_properties_last("sculpt.detail_flood_fill").interactive + row.prop(WindowManager.operator_properties_last("sculpt.detail_flood_fill"), "fully_converge") UnifiedPaintPanel.prop_unified_dyntopo( sub, @@ -971,6 +975,13 @@ class VIEW3D_PT_sculpt_dyntopo(Panel, View3DPaintPanel): "repeat", expand=True ) + UnifiedPaintPanel.prop_unified_dyntopo( + sub, + context, + brush, + "quality", + expand=True + ) col.prop(sculpt, "use_flat_vcol_shading") diff --git a/source/blender/blenkernel/BKE_pbvh.h b/source/blender/blenkernel/BKE_pbvh.h index 565137a2f4f..1cf702fffb0 100644 --- a/source/blender/blenkernel/BKE_pbvh.h +++ b/source/blender/blenkernel/BKE_pbvh.h @@ -581,7 +581,9 @@ bool BKE_pbvh_bmesh_update_topology( void *mask_cb_data, int custom_max_steps, // if 0, will use defaul hueristics for max steps bool disable_surface_relax, - bool is_snake_hook); + bool is_snake_hook, + bool no_radius_test, + int edge_limit_multiply); bool BKE_pbvh_bmesh_update_topology_nodes(struct SculptSession *ss, PBVH *pbvh, @@ -599,7 +601,8 @@ bool BKE_pbvh_bmesh_update_topology_nodes(struct SculptSession *ss, DyntopoMaskCB mask_cb, void *mask_cb_data, bool disable_surface_relax, - bool is_snake_hook); + bool is_snake_hook, + int edge_limit_multiply); /* Node Access */ void BKE_pbvh_check_tri_areas(PBVH *pbvh, PBVHNode *node); diff --git a/source/blender/blenkernel/BKE_sculpt.h b/source/blender/blenkernel/BKE_sculpt.h index abea5a8a3ab..710251fc2f4 100644 --- a/source/blender/blenkernel/BKE_sculpt.h +++ b/source/blender/blenkernel/BKE_sculpt.h @@ -30,12 +30,11 @@ SCULPT_TOOL_DISPLACEMENT_ERASER, \ SCULPT_TOOL_ELASTIC_DEFORM, \ SCULPT_TOOL_BOUNDARY, \ - SCULPT_TOOL_POSE, \ - SCULPT_TOOL_DRAW_FACE_SETS, \ + SCULPT_TOOL_POSE /*SCULPT_TOOL_DRAW_FACE_SETS,*/ \ \ /* These brushes could handle dynamic topology, \ \ * but user feedback indicates it's better not to */ \ - SCULPT_TOOL_MASK) == 0) + /*SCULPT_TOOL_MASK*/) == 0) #define SCULPT_TOOL_HAS_TOPOLOGY_RAKE(t) \ (ELEM(t, /* These brushes, as currently coded, cannot support topology rake. */ \ diff --git a/source/blender/blenkernel/intern/brush.cc b/source/blender/blenkernel/intern/brush.cc index 472a0f2262d..ad86ec3c9b7 100644 --- a/source/blender/blenkernel/intern/brush.cc +++ b/source/blender/blenkernel/intern/brush.cc @@ -1918,6 +1918,7 @@ void BKE_brush_sculpt_reset(Brush *br) br->flag &= ~BRUSH_ALPHA_PRESSURE; br->flag &= ~BRUSH_SPACE; br->flag &= ~BRUSH_SPACE_ATTEN; + disable_dyntopo = true; break; case SCULPT_TOOL_GRAB: br->alpha = 0.4f; @@ -1974,6 +1975,9 @@ void BKE_brush_sculpt_reset(Brush *br) br->dyntopo.inherit = ~(DYNTOPO_INHERIT_COLLAPSE | DYNTOPO_INHERIT_SUBDIVIDE); br->dyntopo.flag |= DYNTOPO_COLLAPSE | DYNTOPO_SUBDIVIDE; break; + case SCULPT_TOOL_MASK: + disable_dyntopo = true; + break; default: break; } diff --git a/source/blender/blenkernel/intern/dyntopo.cc b/source/blender/blenkernel/intern/dyntopo.cc index 816a2caab38..3c83c040df1 100644 --- a/source/blender/blenkernel/intern/dyntopo.cc +++ b/source/blender/blenkernel/intern/dyntopo.cc @@ -80,7 +80,8 @@ static void edge_queue_create_local(struct EdgeQueueContext *eq_ctx, float radius, const bool use_frontface, const bool use_projected, - PBVHTopologyUpdateMode local_mode); + PBVHTopologyUpdateMode local_mode, + bool no_radius_test); ATTR_NO_OPT static void surface_smooth_v_safe( SculptSession *ss, PBVH *pbvh, BMVert *v, float fac, bool reproject_cdata) @@ -133,6 +134,8 @@ ATTR_NO_OPT static void surface_smooth_v_safe( BMVert *v2 = e->v1 == v ? e->v2 : e->v1; PBVHVertRef vertex2 = {reinterpret_cast(v2)}; + float w = len_squared_v3v3(e->v1->co, e->v2->co); + /* Note: we can't validate the boundary flags from with a thread * so they may not be up to date. */ @@ -148,7 +151,7 @@ ATTR_NO_OPT static void surface_smooth_v_safe( float d = dot_v3v3(tan, v->no); madd_v3_v3fl(tan, v->no, -d * 0.95f); - add_v3_v3(co, tan); + madd_v3_v3fl(co, tan, w); if (!stroke_id_test_no_update(ss, vertex2, STROKEID_USER_ORIGINAL)) { sub_v3_v3v3( @@ -160,9 +163,9 @@ ATTR_NO_OPT static void surface_smooth_v_safe( d = dot_v3v3(tan, origno1); madd_v3_v3fl(tan, origno1, -d * 0.95f); - add_v3_v3(origco, tan); + madd_v3_v3fl(origco, tan, w); - tot += 1.0f; + tot += w; } while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e); @@ -405,7 +408,9 @@ static float dist_to_tri_sphere_simple( return dis; } -static bool edge_queue_tri_in_sphere(const EdgeQueueContext *q, BMVert *vs[3], float no[3]) +ATTR_NO_OPT static bool edge_queue_tri_in_sphere(const EdgeQueueContext *q, + BMVert *vs[3], + float no[3]) { #if 0 float cent[3]; @@ -617,9 +622,9 @@ BLI_INLINE int dyntopo_thread_rand(int seed) return (seed * multiplier + addend) & mask; } -static void unified_edge_queue_task_cb(void *__restrict userdata, - const int n, - const TaskParallelTLS *__restrict /*tls*/) +ATTR_NO_OPT static void unified_edge_queue_task_cb(void *__restrict userdata, + const int n, + const TaskParallelTLS *__restrict /*tls*/) { EdgeQueueThreadData *tdata = ((EdgeQueueThreadData *)userdata) + n; PBVH *pbvh = tdata->pbvh; @@ -1142,14 +1147,31 @@ bool check_vert_fan_are_tris(PBVH *pbvh, BMVert *v) return false; } +static bool edge_queue_tri_null_test(const EdgeQueueContext * /*eq_ctx*/, + BMVert * /*v_tri*/[3], + float /*no*/[3]) +{ + return true; +} + +static bool edge_queue_vert_null_test(const EdgeQueueContext * /*eq_ctx*/, BMVert * /*v*/) +{ + return true; +} + static void edge_queue_init(EdgeQueueContext *eq_ctx, bool use_projected, bool use_frontface, const float center[3], const float view_normal[3], - const float radius) + const float radius, + bool no_radius_test) { - if (use_projected) { + if (no_radius_test) { + eq_ctx->edge_queue_tri_in_range = edge_queue_tri_null_test; + eq_ctx->edge_queue_vert_in_range = edge_queue_vert_null_test; + } + else if (use_projected) { eq_ctx->edge_queue_tri_in_range = edge_queue_tri_in_circle; eq_ctx->edge_queue_vert_in_range = edge_queue_vert_in_circle; project_plane_normalized_v3_v3v3(eq_ctx->center_proj, center, view_normal); @@ -1172,7 +1194,7 @@ static void edge_queue_init(EdgeQueueContext *eq_ctx, #endif } -static bool edge_queue_test(EdgeQueueContext *eq_ctx, PBVH * /*pbvh*/, BMEdge *e) +ATTR_NO_OPT static bool edge_queue_test(EdgeQueueContext *eq_ctx, PBVH * /*pbvh*/, BMEdge *e) { float len = len_squared_v3v3(e->v1->co, e->v2->co); float min = eq_ctx->limit_len_min_sqr; @@ -1207,11 +1229,19 @@ static void unified_edge_queue_create(EdgeQueueContext *eq_ctx, float radius, const bool use_frontface, const bool use_projected, - PBVHTopologyUpdateMode local_mode) + PBVHTopologyUpdateMode local_mode, + bool no_radius_test) { if (local_mode) { - edge_queue_create_local( - eq_ctx, pbvh, center, view_normal, radius, use_frontface, use_projected, local_mode); + edge_queue_create_local(eq_ctx, + pbvh, + center, + view_normal, + radius, + use_frontface, + use_projected, + local_mode, + no_radius_test); return; } @@ -1220,7 +1250,8 @@ static void unified_edge_queue_create(EdgeQueueContext *eq_ctx, eq_ctx->limit_len_min = pbvh->bm_min_edge_len; eq_ctx->limit_len_max = pbvh->bm_max_edge_len; - edge_queue_init(eq_ctx, use_projected, use_frontface, center, eq_ctx->view_normal, radius); + edge_queue_init( + eq_ctx, use_projected, use_frontface, center, eq_ctx->view_normal, radius, no_radius_test); #ifdef USE_EDGEQUEUE_TAG_VERIFY pbvh_bmesh_edge_tag_verify(pbvh); @@ -1395,13 +1426,15 @@ static void edge_queue_create_local(EdgeQueueContext *eq_ctx, float radius, const bool use_frontface, const bool use_projected, - PBVHTopologyUpdateMode local_mode) + PBVHTopologyUpdateMode local_mode, + bool no_radius_test) { eq_ctx->limit_len_min = pbvh->bm_min_edge_len; eq_ctx->limit_len_max = pbvh->bm_max_edge_len; eq_ctx->local_mode = true; - edge_queue_init(eq_ctx, use_projected, use_frontface, center, eq_ctx->view_normal, radius); + edge_queue_init( + eq_ctx, use_projected, use_frontface, center, eq_ctx->view_normal, radius, no_radius_test); Vector tdata; @@ -1630,13 +1663,13 @@ static void edge_queue_create_local(EdgeQueueContext *eq_ctx, } } -static bool cleanup_valence_3_4(EdgeQueueContext *ectx, - PBVH *pbvh, - const float center[3], - const float /*view_normal*/[3], - float radius, - const bool /*use_frontface*/, - const bool /*use_projected*/) +ATTR_NO_OPT static bool cleanup_valence_3_4(EdgeQueueContext *ectx, + PBVH *pbvh, + const float center[3], + const float /*view_normal*/[3], + float radius, + const bool /*use_frontface*/, + const bool /*use_projected*/) { bool modified = false; @@ -1704,6 +1737,22 @@ static bool cleanup_valence_3_4(EdgeQueueContext *ectx, bool bad = false; int ls_i = 0; + /* Don't dissolve verts if attached to long edges, to avoid + * preventing subdivision convergence. + */ + BMEdge *e = v->e; + do { + float len = calc_weighted_length(ectx, e->v1, e->v2, 1); + if (sqrtf(len) > ectx->limit_len_max * 1.2f) { + bad = true; + break; + } + } while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e); + + if (bad) { + continue; + } + for (int j = 0; j < val; j++) { ls[ls_i++] = l->v == v ? l->next : l; @@ -2009,7 +2058,9 @@ extern "C" bool BKE_pbvh_bmesh_update_topology(SculptSession *ss, void *mask_cb_data, int custom_max_steps, bool disable_surface_relax, - bool /*is_snake_hook*/) + bool /*is_snake_hook*/, + bool no_radius_test, + int edge_limit_multiply) { /* Push a subentry. */ BM_log_entry_add_ex(pbvh->header.bm, pbvh->bm_log, true); @@ -2123,10 +2174,12 @@ extern "C" bool BKE_pbvh_bmesh_update_topology(SculptSession *ss, radius, use_frontface, use_projected, - mode & (PBVH_LocalSubdivide | PBVH_LocalCollapse)); + mode & (PBVH_LocalSubdivide | PBVH_LocalCollapse), + no_radius_test); } else { - edge_queue_init(&eq_ctx, use_projected, use_frontface, center, eq_ctx.view_normal, radius); + edge_queue_init( + &eq_ctx, use_projected, use_frontface, center, eq_ctx.view_normal, radius, no_radius_test); } #ifdef SKINNY_EDGE_FIX @@ -2181,7 +2234,11 @@ extern "C" bool BKE_pbvh_bmesh_update_topology(SculptSession *ss, steps[0] = DYNTOPO_MAX_ITER; } - int max_steps = max_ii(custom_max_steps, DYNTOPO_MAX_ITER) << (totop - 1); + steps[0] *= edge_limit_multiply; + steps[1] *= edge_limit_multiply; + + int max_steps = (max_ii(custom_max_steps, DYNTOPO_MAX_ITER) * edge_limit_multiply) + << (totop - 1); int max_subd = max_steps >> (totop - 1); int edges_size = steps[0]; @@ -2204,7 +2261,7 @@ extern "C" bool BKE_pbvh_bmesh_update_topology(SculptSession *ss, BMEdge *e = nullptr; if (count >= steps[curop]) { - BM_log_entry_add_ex(pbvh->header.bm, pbvh->bm_log, true); + // BM_log_entry_add_ex(pbvh->header.bm, pbvh->bm_log, true); if (ops[curop] == PBVH_Subdivide && count_subd < max_subd) { modified = true; @@ -2338,7 +2395,7 @@ extern "C" bool BKE_pbvh_bmesh_update_topology(SculptSession *ss, if (etot > 0) { modified = true; BLI_smallhash_clear(&subd_edges, 0); - BM_log_entry_add_ex(pbvh->header.bm, pbvh->bm_log, true); + // BM_log_entry_add_ex(pbvh->header.bm, pbvh->bm_log, true); pbvh_split_edges(&eq_ctx, pbvh, pbvh->header.bm, edges, etot, false); VALIDATE_LOG(pbvh->bm_log); etot = 0; @@ -3216,8 +3273,8 @@ void BKE_dyntopo_remesh(DynTopoState *ds, } ds->pbvh->bm_max_edge_len = params->edge_size; - ds->pbvh->bm_min_edge_len = params->edge_size * params->detail_range; - ds->pbvh->bm_detail_range = params->detail_range; + ds->pbvh->bm_detail_range = max_ff(params->detail_range, 0.1f); + ds->pbvh->bm_min_edge_len = params->edge_size * ds->pbvh->bm_detail_range; /* subdivide once */ if (mode & PBVH_Subdivide) { @@ -3235,7 +3292,9 @@ void BKE_dyntopo_remesh(DynTopoState *ds, nullptr, ds->pbvh->header.bm->totedge, false, - false); + false, + false, + 1); } for (int i = 0; i < steps; i++) { @@ -3261,7 +3320,9 @@ void BKE_dyntopo_remesh(DynTopoState *ds, nullptr, ds->pbvh->header.bm->totedge * 5, false, - false); + false, + false, + 1); BKE_pbvh_update_normals(ds->pbvh, nullptr); diff --git a/source/blender/blenkernel/intern/dyntopo_intern.hh b/source/blender/blenkernel/intern/dyntopo_intern.hh index 9994db02add..60ee5bf21c8 100644 --- a/source/blender/blenkernel/intern/dyntopo_intern.hh +++ b/source/blender/blenkernel/intern/dyntopo_intern.hh @@ -93,7 +93,7 @@ inline bool bm_elem_is_free(BMElem *elem, int htype) (SCULPT_CORNER_MESH | SCULPT_CORNER_FACE_SET | SCULPT_CORNER_SHARP_MARK | SCULPT_CORNER_SEAM | \ SCULPT_CORNER_UV | SCULPT_CORNER_SHARP_ANGLE) -#define DYNTOPO_MAX_ITER 1024 +#define DYNTOPO_MAX_ITER 4096 #define DYNTOPO_MAX_ITER_SUBD 4096 #define DYNTOPO_USE_HEAP diff --git a/source/blender/blenkernel/intern/paint.cc b/source/blender/blenkernel/intern/paint.cc index 90e340fded3..6fb29e87c98 100644 --- a/source/blender/blenkernel/intern/paint.cc +++ b/source/blender/blenkernel/intern/paint.cc @@ -1122,8 +1122,7 @@ bool BKE_paint_ensure(ToolSettings *ts, Paint **r_paint) /* Turn on X plane mirror symmetry by default */ paint->symmetry_flags |= PAINT_SYMM_X; - /* Make sure at least dyntopo subdivision is enabled */ - data->flags |= SCULPT_DYNTOPO_SUBDIVIDE | SCULPT_DYNTOPO_COLLAPSE | SCULPT_DYNTOPO_ENABLED; + data->dyntopo = *DNA_struct_default_get(DynTopoSettings); } else if ((GpPaint **)r_paint == &ts->gp_paint) { GpPaint *data = MEM_cnew(__func__); diff --git a/source/blender/blenkernel/intern/pbvh_bmesh.cc b/source/blender/blenkernel/intern/pbvh_bmesh.cc index fc0bdaeaf27..3e212a1b8ba 100644 --- a/source/blender/blenkernel/intern/pbvh_bmesh.cc +++ b/source/blender/blenkernel/intern/pbvh_bmesh.cc @@ -2660,7 +2660,8 @@ bool BKE_pbvh_bmesh_update_topology_nodes(SculptSession *ss, DyntopoMaskCB mask_cb, void *mask_cb_data, bool disable_surface_relax, - bool is_snake_hook) + bool is_snake_hook, + int edge_limit_multiply) { bool modified = false; Vector nodes; @@ -2698,7 +2699,9 @@ bool BKE_pbvh_bmesh_update_topology_nodes(SculptSession *ss, mask_cb_data, 4096, // is_snake_hook ? 4096 : 4096, disable_surface_relax, - is_snake_hook); + is_snake_hook, + false, + 1); return modified; } @@ -4045,9 +4048,9 @@ void BKE_pbvh_bmesh_after_stroke(PBVH *pbvh, bool force_balance) void BKE_pbvh_bmesh_detail_size_set(PBVH *pbvh, float detail_size, float detail_range) { + pbvh->bm_detail_range = max_ff(detail_range, 0.1f); pbvh->bm_max_edge_len = detail_size; - pbvh->bm_min_edge_len = pbvh->bm_max_edge_len * detail_range; - pbvh->bm_detail_range = detail_range; + pbvh->bm_min_edge_len = pbvh->bm_max_edge_len * pbvh->bm_detail_range; } void BKE_pbvh_node_mark_topology_update(PBVHNode *node) diff --git a/source/blender/blenloader/intern/versioning_300.cc b/source/blender/blenloader/intern/versioning_300.cc index f1257dda345..2468b0fde55 100644 --- a/source/blender/blenloader/intern/versioning_300.cc +++ b/source/blender/blenloader/intern/versioning_300.cc @@ -4290,6 +4290,20 @@ void blo_do_versions_300(FileData *fd, Library * /*lib*/, Main *bmain) } } + if (!DNA_struct_elem_find(fd->filesdna, "DynTopoSettings", "float", "quality")) { + LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) { + if (!scene->toolsettings || !scene->toolsettings->sculpt) { + continue; + } + + scene->toolsettings->sculpt->dyntopo.quality = 1.0f; + } + + LISTBASE_FOREACH (Brush *, brush, &bmain->brushes) { + brush->dyntopo.quality = 1.0f; + } + } + if (!DNA_struct_elem_find(fd->filesdna, "Sculpt", "DynTopoSettings", "dyntopo")) { LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) { if (!scene->toolsettings || !scene->toolsettings->sculpt) { @@ -4379,6 +4393,14 @@ void blo_do_versions_300(FileData *fd, Library * /*lib*/, Main *bmain) } } + if (!DNA_struct_elem_find(fd->filesdna, "UnifiedPaintSettings", "int", "smooth_boundary_flag")) { + LISTBASE_FOREACH (Brush *, brush, &bmain->brushes) { + if (ELEM(brush->sculpt_tool, SCULPT_TOOL_MASK, SCULPT_TOOL_DRAW_FACE_SETS)) { + brush->dyntopo.flag |= DYNTOPO_DISABLED; + } + } + } + if (!MAIN_VERSION_ATLEAST(bmain, 306, 3)) { /* Z bias for retopology overlay. */ if (!DNA_struct_elem_find(fd->filesdna, "View3DOverlay", "float", "retopology_offset")) { diff --git a/source/blender/editors/sculpt_paint/sculpt.cc b/source/blender/editors/sculpt_paint/sculpt.cc index 1ddc219824b..e5dcd1850ff 100644 --- a/source/blender/editors/sculpt_paint/sculpt.cc +++ b/source/blender/editors/sculpt_paint/sculpt.cc @@ -3996,6 +3996,8 @@ static void sculpt_topology_update(Sculpt *sd, // mode |= PBVH_Collapse | PBVH_Subdivide; } + int edge_multiply = 1 + int(ss->cached_dyntopo.quality * 10.0f); + SculptSearchSphereData sdata{}; sdata.ss = ss, sdata.sd = sd, sdata.ob = ob; sdata.radius_squared = square_f(ss->cache->radius * radius_scale * 1.25f); @@ -4042,7 +4044,8 @@ static void sculpt_topology_update(Sculpt *sd, mask_cb, mask_cb_data, false, - brush->sculpt_tool == SCULPT_TOOL_SNAKE_HOOK); + brush->sculpt_tool == SCULPT_TOOL_SNAKE_HOOK, + edge_multiply); SCULPT_dyntopo_automasking_end(mask_cb_data); diff --git a/source/blender/editors/sculpt_paint/sculpt_api.cc b/source/blender/editors/sculpt_paint/sculpt_api.cc index 64ef1366f6d..77e92331cd7 100644 --- a/source/blender/editors/sculpt_paint/sculpt_api.cc +++ b/source/blender/editors/sculpt_paint/sculpt_api.cc @@ -641,6 +641,7 @@ void SCULPT_apply_dyntopo_settings(SculptSession *ss, Sculpt *sculpt, Brush *bru ds1->radius_scale; ds_final->spacing = ds_final->inherit & DYNTOPO_INHERIT_SPACING ? ds2->spacing : ds1->spacing; ds_final->repeat = ds_final->inherit & DYNTOPO_INHERIT_REPEAT ? ds2->repeat : ds1->repeat; + ds_final->quality = ds_final->inherit & DYNTOPO_INHERIT_QUALITY ? ds2->quality : ds1->quality; } bool SCULPT_face_is_hidden(const SculptSession *ss, PBVHFaceRef face) diff --git a/source/blender/editors/sculpt_paint/sculpt_detail.cc b/source/blender/editors/sculpt_paint/sculpt_detail.cc index 8bb5478b0b5..87f829da350 100644 --- a/source/blender/editors/sculpt_paint/sculpt_detail.cc +++ b/source/blender/editors/sculpt_paint/sculpt_detail.cc @@ -37,6 +37,8 @@ #include "RNA_access.h" #include "RNA_define.h" +#include "PIL_time.h" + #include #include @@ -79,13 +81,8 @@ static bool sculpt_and_dynamic_topology_poll(bContext *C) /* -------------------------------------------------------------------- */ /** \name Detail Flood Fill * \{ */ - -static int sculpt_detail_flood_fill_exec(bContext *C, wmOperator *op) +static int sculpt_detail_flood_fill_run(Object *ob, Sculpt *sd, Brush *brush, wmOperator *op) { - Sculpt *sd = CTX_data_tool_settings(C)->sculpt; - Object *ob = CTX_data_active_object(C); - Brush *brush = BKE_paint_brush(&sd->paint); - SculptSession *ss = ob->sculpt; float size; float bb_min[3], bb_max[3], center[3], dim[3]; @@ -96,15 +93,12 @@ static int sculpt_detail_flood_fill_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - for (PBVHNode *node : nodes) { - BKE_pbvh_node_mark_topology_update(node); - } /* Get the bounding box, its center and size. */ BKE_pbvh_bounding_box(ob->sculpt->pbvh, bb_min, bb_max); add_v3_v3v3(center, bb_min, bb_max); mul_v3_fl(center, 0.5f); sub_v3_v3v3(dim, bb_max, bb_min); - size = max_fff(dim[0], dim[1], dim[2]); + size = max_fff(dim[0], dim[1], dim[2]) * 1.1f; SCULPT_apply_dyntopo_settings(ss, sd, brush); float detail_range = ss->cached_dyntopo.detail_range; @@ -115,10 +109,7 @@ static int sculpt_detail_flood_fill_exec(bContext *C, wmOperator *op) BKE_pbvh_bmesh_detail_size_set(ss->pbvh, object_space_constant_detail, detail_range); BKE_pbvh_set_bm_log(ss->pbvh, ss->bm_log); - bool enable_surface_relax = true; /* XXX should be user-configurable. */ - - SCULPT_undo_push_begin(ob, op); - SCULPT_undo_push_node(ob, nullptr, SCULPT_UNDO_COORDS); + bool enable_surface_relax = true; DyntopoMaskCB mask_cb; void *mask_cb_data; @@ -132,10 +123,29 @@ static int sculpt_detail_flood_fill_exec(bContext *C, wmOperator *op) int i = 0; bool modified = true; - while (modified) { + PBVHTopologyUpdateMode mode = PBVHTopologyUpdateMode(0); + if (ss->cached_dyntopo.flag & DYNTOPO_SUBDIVIDE) { + mode |= PBVH_Subdivide; + } + if (ss->cached_dyntopo.flag & DYNTOPO_COLLAPSE) { + mode |= PBVH_Collapse; + } + if (ss->cached_dyntopo.flag & DYNTOPO_CLEANUP) { + mode |= PBVH_Cleanup; + } + + double time = PIL_check_seconds_timer(); + + for (int i = 0; i < 1 + ss->cached_dyntopo.repeat; i++) { + nodes = blender::bke::pbvh::search_gather(ss->pbvh, nullptr, nullptr); + + for (int j = 0; j < nodes.size(); j++) { + BKE_pbvh_node_mark_topology_update(nodes[j]); + } + modified = BKE_pbvh_bmesh_update_topology(ss, ss->pbvh, - PBVH_Collapse, + mode, center, nullptr, size, @@ -146,72 +156,174 @@ static int sculpt_detail_flood_fill_exec(bContext *C, wmOperator *op) mask_cb, mask_cb_data, max_dyntopo_steps_coll, - enable_surface_relax, - false); - - for (int j = 0; j < nodes.size(); j++) { - BKE_pbvh_node_mark_topology_update(nodes[j]); - } - - modified |= BKE_pbvh_bmesh_update_topology(ss, - ss->pbvh, - PBVH_Subdivide, - center, - nullptr, - size, - false, - false, - -1, - false, - mask_cb, - mask_cb_data, - max_dyntopo_steps_subd, - enable_surface_relax, - false); - for (int j = 0; j < nodes.size(); j++) { - BKE_pbvh_node_mark_topology_update(nodes[j]); - } - - if (i++ > max_steps) { - break; - } + !enable_surface_relax, + false, + true, + 10); } - /* one more time, but with cleanup valence 3/4 verts enabled */ - for (i = 0; i < 2; i++) { - for (int j = 0; j < nodes.size(); j++) { - BKE_pbvh_node_mark_topology_update(nodes[j]); - } - - BKE_pbvh_bmesh_update_topology(ss, - ss->pbvh, - PBVH_Cleanup, - center, - nullptr, - size, - false, - false, - -1, - false, - mask_cb, - mask_cb_data, - max_dyntopo_steps_coll, - enable_surface_relax, - false); - } + time = (PIL_check_seconds_timer() - time) * 1000.0; + printf(" Time: %.3fms\n", float(time)); SCULPT_dyntopo_automasking_end(mask_cb_data); - SCULPT_undo_push_end(ob); - - /* Force rebuild of PBVH for better BB placement. */ - SCULPT_pbvh_clear(ob); - /* Redraw. */ - WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob); + BKE_pbvh_bmesh_after_stroke(ss->pbvh, true); + DEG_id_tag_update(&ob->id, ID_RECALC_SHADING); return OPERATOR_FINISHED; } +static int sculpt_detail_flood_fill_exec(bContext *C, wmOperator *op) +{ + Object *ob = CTX_data_active_object(C); + Sculpt *sd = CTX_data_tool_settings(C)->sculpt; + Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); + + BKE_sculpt_update_object_for_edit(depsgraph, ob, true, false, false); + + SCULPT_undo_push_begin(ob, op); + SCULPT_undo_push_node(ob, nullptr, SCULPT_UNDO_COORDS); + + int ret = sculpt_detail_flood_fill_run( + CTX_data_active_object(C), sd, BKE_paint_brush(&sd->paint), op); + WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob); + + SCULPT_undo_push_end(ob); + + return ret; +} + +struct FloodFillJob { + wmJob *job; + bool *stop; + bool *do_update; + float *progress; + Object *ob; + Depsgraph *depsgraph; + Scene *scene; + wmOperator *op; + bContext *C; + Brush *brush; + Sculpt *sd; +}; +FloodFillJob flood_fill_job; + +ATTR_NO_OPT static void start_fill_job(void *custom_data, + bool *stop, + bool *do_update, + float *progress) +{ + flood_fill_job.stop = stop; + flood_fill_job.do_update = do_update; + flood_fill_job.progress = progress; + + printf("Start fill job.\n"); + + *progress = 0.0f; + + while (true) { + if (*stop) { + break; + } + + WM_job_main_thread_lock_acquire(flood_fill_job.job); + + if (sculpt_detail_flood_fill_run( + flood_fill_job.ob, flood_fill_job.sd, flood_fill_job.brush, flood_fill_job.op) == + OPERATOR_CANCELLED) + { + WM_job_main_thread_lock_release(flood_fill_job.job); + break; + } + + WM_job_main_thread_lock_release(flood_fill_job.job); + + *do_update = true; + + PIL_sleep_ms(50); + } + + printf("\nJob finished\n\n"); +} + +static void init_fill_job(void *owner) +{ + printf("Init fill job\n"); +} + +static void update_fill_job(void *owner) +{ + printf("Update fill job\n"); +} + +static void end_fill_job(void *owner) +{ + SCULPT_undo_push_end(flood_fill_job.ob); + + printf("End fill job\n"); +} + +static void flood_fill_free(void *customdata) +{ + printf("%s: detail flood fill free.\n", __func__); +} + +int sculpt_detail_flood_fill_invoke(bContext *C, wmOperator *op, const wmEvent * /*event*/) +{ + ED_workspace_status_text(C, TIP_("")); + + if (RNA_boolean_get(op->ptr, "interactive")) { + Object *ob = CTX_data_active_object(C); + + BKE_sculpt_update_object_for_edit( + CTX_data_ensure_evaluated_depsgraph(C), ob, true, false, false); + + SCULPT_undo_push_begin(ob, op); + SCULPT_undo_push_node(ob, nullptr, SCULPT_UNDO_COORDS); + + flood_fill_job.sd = CTX_data_tool_settings(C)->sculpt; + flood_fill_job.brush = BKE_paint_brush(&flood_fill_job.sd->paint); + flood_fill_job.ob = CTX_data_active_object(C); + flood_fill_job.op = op; + flood_fill_job.C = C; + flood_fill_job.scene = CTX_data_scene(C); + flood_fill_job.depsgraph = CTX_data_ensure_evaluated_depsgraph(C); + + flood_fill_job.job = WM_jobs_get(CTX_wm_manager(C), + CTX_wm_window(C), + static_cast(&flood_fill_job), + "Dyntopo Flood Fill", + WM_JOB_PROGRESS, + WM_JOB_TYPE_ANY); + + WM_jobs_callbacks( + flood_fill_job.job, start_fill_job, init_fill_job, update_fill_job, end_fill_job); + WM_jobs_timer(flood_fill_job.job, 0.5, NC_OBJECT | ND_DRAW, NC_OBJECT | ND_DRAW); + WM_jobs_customdata_set(flood_fill_job.job, &flood_fill_job, flood_fill_free); + + WM_jobs_start(CTX_wm_manager(C), flood_fill_job.job); + + WM_event_add_modal_handler(C, op); + return OPERATOR_RUNNING_MODAL; + } + else { + sculpt_detail_flood_fill_exec(C, op); + return OPERATOR_FINISHED; + } +} + +static int sculpt_sample_flood_fill_modal(bContext *C, wmOperator *op, const wmEvent *event) +{ + switch (event->type) { + case EVT_ESCKEY: + case EVT_RETKEY: + WM_jobs_kill(CTX_wm_manager(C), &flood_fill_job, start_fill_job); + return OPERATOR_FINISHED; + } + + return OPERATOR_RUNNING_MODAL; +} + void SCULPT_OT_detail_flood_fill(wmOperatorType *ot) { /* Identifiers. */ @@ -222,8 +334,14 @@ void SCULPT_OT_detail_flood_fill(wmOperatorType *ot) /* API callbacks. */ ot->exec = sculpt_detail_flood_fill_exec; ot->poll = sculpt_and_constant_or_manual_detail_poll; + ot->invoke = sculpt_detail_flood_fill_invoke; + ot->modal = sculpt_sample_flood_fill_modal; ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + RNA_def_boolean( + ot->srna, "fully_converge", false, "Full Convergence", "Run until fully converged."); + RNA_def_boolean(ot->srna, "interactive", true, "Interactive", "Interactive mode"); } /** \} */ @@ -584,10 +702,10 @@ static void dyntopo_detail_size_parallel_lines_draw(uint pos3d, mat4_to_scale(cd->active_object->object_to_world)); /* The constant detail represents the maximum edge length allowed before subdividing it. If the - * triangle grid preview is created with this value it will represent an ideal mesh density where - * all edges have the exact maximum length, which never happens in practice. As the minimum edge - * length for dyntopo is 0.4 * max_edge_length, this adjust the detail size to the average - * between max and min edge length so the preview is more accurate. */ + * triangle grid preview is created with this value it will represent an ideal mesh density + * where all edges have the exact maximum length, which never happens in practice. As the + * minimum edge length for dyntopo is 0.4 * max_edge_length, this adjust the detail size to the + * average between max and min edge length so the preview is more accurate. */ object_space_constant_detail *= 1.0f - cd->detail_range * 0.5f; const float total_len = len_v3v3(cd->preview_tri[0], cd->preview_tri[1]); @@ -836,8 +954,8 @@ static int dyntopo_detail_size_edit_invoke(bContext *C, wmOperator *op, const wm SculptSession *ss = active_object->sculpt; cd->radius = ss->cursor_radius; - /* Generates the matrix to position the gizmo in the surface of the mesh using the same location - * and orientation as the brush cursor. */ + /* Generates the matrix to position the gizmo in the surface of the mesh using the same + * location and orientation as the brush cursor. */ float cursor_trans[4][4], cursor_rot[4][4]; const float z_axis[4] = {0.0f, 0.0f, 1.0f, 0.0f}; float quat[4]; diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.hh b/source/blender/editors/sculpt_paint/sculpt_intern.hh index e12c99df31d..134b9778edc 100644 --- a/source/blender/editors/sculpt_paint/sculpt_intern.hh +++ b/source/blender/editors/sculpt_paint/sculpt_intern.hh @@ -97,7 +97,7 @@ struct _SculptNeighborRef { PBVHEdgeRef edge; }; -#define SCULPT_VERTEX_NEIGHBOR_FIXED_CAPACITY 12 +#define SCULPT_VERTEX_NEIGHBOR_FIXED_CAPACITY 16 struct SculptVertexNeighborIter { /* Storage */ diff --git a/source/blender/makesdna/DNA_brush_defaults.h b/source/blender/makesdna/DNA_brush_defaults.h index a0e7f963564..cad294b9229 100644 --- a/source/blender/makesdna/DNA_brush_defaults.h +++ b/source/blender/makesdna/DNA_brush_defaults.h @@ -25,7 +25,8 @@ .mode = DYNTOPO_DETAIL_RELATIVE,\ .inherit = DYNTOPO_INHERIT_BITMASK,\ .spacing = 35,\ - .radius_scale = 1.0f\ + .radius_scale = 1.0f,\ + .quality = 1.0f,\ } #define _DNA_DEFAULT_Brush \ diff --git a/source/blender/makesdna/DNA_brush_enums.h b/source/blender/makesdna/DNA_brush_enums.h index 06fb92c6ae4..f660cc70be4 100644 --- a/source/blender/makesdna/DNA_brush_enums.h +++ b/source/blender/makesdna/DNA_brush_enums.h @@ -642,10 +642,11 @@ typedef enum eDynTopoInheritFlags { DYNTOPO_INHERIT_DETAIL_SIZE = 1 << 16, DYNTOPO_INHERIT_RADIUS_SCALE = 1 << 17, DYNTOPO_INHERIT_REPEAT = 1 << 18, - DYNTOPO_INHERIT_MAX_FLAGS = 19, + DYNTOPO_INHERIT_QUALITY = 1 << 19, + DYNTOPO_INHERIT_MAX_FLAGS = 20, // make sure to update DYNTOPO_INHERIT_BITMASK when adding flags here } eDynTopoInheritFlags; -ENUM_OPERATORS(eDynTopoInheritFlags, DYNTOPO_INHERIT_REPEAT); +ENUM_OPERATORS(eDynTopoInheritFlags, DYNTOPO_INHERIT_QUALITY); // represents all possible inherit flags #define DYNTOPO_INHERIT_BITMASK ((1 << DYNTOPO_INHERIT_MAX_FLAGS) - 1) diff --git a/source/blender/makesdna/DNA_brush_types.h b/source/blender/makesdna/DNA_brush_types.h index 7785cebda28..24197304a60 100644 --- a/source/blender/makesdna/DNA_brush_types.h +++ b/source/blender/makesdna/DNA_brush_types.h @@ -154,7 +154,8 @@ typedef struct DynTopoSettings { int inherit; int spacing; float radius_scale; - int repeat, _pad[1]; + int repeat; + float quality; } DynTopoSettings; typedef struct BrushCurvesSculptSettings { diff --git a/source/blender/makesrna/intern/rna_brush.c b/source/blender/makesrna/intern/rna_brush.c index 353aa9ba993..e71dcb1fdfc 100644 --- a/source/blender/makesrna/intern/rna_brush.c +++ b/source/blender/makesrna/intern/rna_brush.c @@ -469,6 +469,7 @@ static EnumPropertyItem rna_enum_brush_dyntopo_inherit[] = { ICON_NONE, "Repeat", "How many extra times to run the dyntopo remesher."}, + {DYNTOPO_INHERIT_QUALITY, "QUALITY", ICON_NONE, "Quality", ""}, {0, NULL, 0, NULL, NULL}, }; @@ -1442,6 +1443,14 @@ static void rna_def_dyntopo_settings(BlenderRNA *brna) RNA_def_property_range(prop, 0.0f, 15.0f); RNA_def_property_ui_text(prop, "Repeat", "How many times to run the dyntopo remesher."); RNA_def_property_update(prop, 0, "rna_Brush_dyntopo_update"); + + prop = RNA_def_property(srna, "quality", PROP_FLOAT, PROP_PERCENTAGE); + RNA_def_property_float_sdna(prop, NULL, "quality"); + RNA_def_property_range(prop, 0.0f, 10.0f); + RNA_def_property_ui_range(prop, 0.0f, 1.0f, 0.01, 3); + RNA_def_property_ui_text( + prop, "Quality", "Lower values are faster but make lower-quality geometry"); + RNA_def_property_update(prop, 0, "rna_Brush_dyntopo_update"); } static void rna_def_sculpt_capabilities(BlenderRNA *brna) @@ -3373,8 +3382,7 @@ static void rna_def_brush(BlenderRNA *brna) prop = RNA_def_property(srna, "sharp_angle_limit", PROP_FLOAT, PROP_ANGLE); RNA_def_property_float_sdna(prop, NULL, "sharp_angle_limit"); RNA_def_property_range(prop, 0.0f, M_PI); - RNA_def_property_ui_text( - prop, "Sharp Limit", ""); + RNA_def_property_ui_text(prop, "Sharp Limit", ""); RNA_def_property_update(prop, 0, "rna_Brush_update"); RNA_def_property_update(prop, 0, "rna_Brush_update"); -- 2.30.2 From 4de7f2b06feb18bb02b996edabf84128d5d0ce25 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Sun, 21 May 2023 17:12:25 -0700 Subject: [PATCH 112/279] temp-sculpt-dyntopo: Cleanup dyntopo code * New header BKE_dyntopo.hh * blender::dyntopo -> blender::bke::dyntopo * PBVH_pbvh_bmesh_update_topology -> blender::bke::dyntopo::remesh_topology. * Vertex/tri brush test callbacks are now handled via C++ classes. --- source/blender/blenkernel/BKE_dyntopo.h | 24 - source/blender/blenkernel/BKE_dyntopo.hh | 161 +++++ source/blender/blenkernel/BKE_pbvh.h | 86 +-- source/blender/blenkernel/CMakeLists.txt | 1 + .../blender/blenkernel/intern/customdata.cc | 2 +- source/blender/blenkernel/intern/dyntopo.cc | 583 +++--------------- .../blenkernel/intern/dyntopo_collapse.cc | 8 +- .../blenkernel/intern/dyntopo_intern.hh | 24 +- .../blender/blenkernel/intern/pbvh_bmesh.cc | 77 +-- source/blender/editors/sculpt_paint/sculpt.cc | 63 +- .../editors/sculpt_paint/sculpt_detail.cc | 97 +-- .../editors/sculpt_paint/sculpt_face_set.cc | 2 +- .../editors/sculpt_paint/sculpt_intern.hh | 3 +- .../editors/sculpt_paint/sculpt_undo.cc | 2 +- 14 files changed, 351 insertions(+), 782 deletions(-) delete mode 100644 source/blender/blenkernel/BKE_dyntopo.h create mode 100644 source/blender/blenkernel/BKE_dyntopo.hh diff --git a/source/blender/blenkernel/BKE_dyntopo.h b/source/blender/blenkernel/BKE_dyntopo.h deleted file mode 100644 index 7944567a141..00000000000 --- a/source/blender/blenkernel/BKE_dyntopo.h +++ /dev/null @@ -1,24 +0,0 @@ -/* - * 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 bke - * \brief Dynamic topology remeshing API - */ - -typedef struct DynTopo DynTopo; diff --git a/source/blender/blenkernel/BKE_dyntopo.hh b/source/blender/blenkernel/BKE_dyntopo.hh new file mode 100644 index 00000000000..af480ce3206 --- /dev/null +++ b/source/blender/blenkernel/BKE_dyntopo.hh @@ -0,0 +1,161 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +/** \file + * \ingroup bke + * + * Dynamic remesher for PBVH + */ + +#include "BKE_paint.h" +#include "BKE_pbvh.h" + +#include "BLI_math.h" +#include "BLI_math_vector_types.hh" + +#define DYNTOPO_CD_INTERP +#define DYNTOPO_DYNAMIC_TESS + +namespace blender::bke::dyntopo { + +float dist_to_tri_sphere_simple(float p[3], float v1[3], float v2[3], float v3[3], float n[3]); + +struct BrushTester { + bool is_sphere_or_tube; + + virtual ~BrushTester() {} + + virtual bool vert_in_range(BMVert * /*v*/) + { + return true; + } + virtual bool tri_in_range(BMVert * /*tri*/[3], float * /*no*/) + { + return true; + } +}; + +struct BrushSphere : public BrushTester { + BrushSphere(float3 center, float radius) + : center_(center), radius_(radius), radius_squared_(radius * radius) + { + is_sphere_or_tube = true; + } + + bool vert_in_range(BMVert *v) override + { + return len_squared_v3v3(center_, v->co) <= radius_squared_; + } + bool tri_in_range(BMVert *tri[3], float *no) override + { + /* Check if triangle intersects the sphere */ + float dis = dist_to_tri_sphere_simple((float *)center_, + (float *)tri[0]->co, + (float *)tri[1]->co, + (float *)tri[2]->co, + (float *)no); + return dis <= radius_squared_; + } + + inline const float3 ¢er() + { + return center_; + } + inline float radius_squared() + { + return radius_squared_; + } + + inline float radius() + { + return radius_; + } + + protected: + float3 center_; + float radius_, radius_squared_; +}; + +struct BrushTube : public BrushSphere { + BrushTube(float3 center, float3 view_normal, float radius) + : BrushSphere(center, radius), view_normal_(view_normal) + { + project_plane_normalized_v3_v3v3(center_proj_, center_, view_normal_); + } + + bool vert_in_range(BMVert *v) override + { + float c[3]; + + project_plane_normalized_v3_v3v3(c, v->co, view_normal_); + + return len_squared_v3v3(center_proj_, c) <= radius_squared_; + } + + bool tri_in_range(BMVert *tri[3], float * /*no*/) override + { + float c[3]; + float tri_proj[3][3]; + + project_plane_normalized_v3_v3v3(tri_proj[0], tri[0]->co, view_normal_); + project_plane_normalized_v3_v3v3(tri_proj[1], tri[1]->co, view_normal_); + project_plane_normalized_v3_v3v3(tri_proj[2], tri[2]->co, view_normal_); + + closest_on_tri_to_point_v3(c, center_proj_, tri_proj[0], tri_proj[1], tri_proj[2]); + + /* Check if triangle intersects the sphere */ + return len_squared_v3v3(center_proj_, c) <= radius_squared_; + } + + private: + float3 center_proj_, view_normal_; +}; + +struct BrushNoRadius : public BrushTester { + BrushNoRadius() + { + is_sphere_or_tube = false; + } +}; + +typedef float (*DyntopoMaskCB)(PBVHVertRef vertex, void *userdata); + +enum PBVHTopologyUpdateMode { + PBVH_Subdivide = 1 << 0, + PBVH_Collapse = 1 << 1, + PBVH_Cleanup = 1 << 2, // dissolve verts surrounded by either 3 or 4 triangles then triangulate + PBVH_LocalSubdivide = 1 << 3, + PBVH_LocalCollapse = 1 << 4 +}; +ENUM_OPERATORS(PBVHTopologyUpdateMode, PBVH_LocalCollapse); + +void detail_size_set(PBVH *pbvh, float detail_size, float detail_range); + +bool remesh_topology(blender::bke::dyntopo::BrushTester *brush_tester, + struct SculptSession *ss, + PBVH *pbvh, + PBVHTopologyUpdateMode mode, + bool use_frontface, + blender::float3 view_normal, + bool updatePBVH, + DyntopoMaskCB mask_cb, + void *mask_cb_data, + int edge_limit_multiply); + +bool remesh_topology_nodes(blender::bke::dyntopo::BrushTester *tester, + struct SculptSession *ss, + PBVH *pbvh, + bool (*searchcb)(PBVHNode *node, void *data), + void (*undopush)(PBVHNode *node, void *data), + void *searchdata, + PBVHTopologyUpdateMode mode, + bool use_frontface, + blender::float3 view_normal, + bool updatePBVH, + DyntopoMaskCB mask_cb, + void *mask_cb_data, + int edge_limit_multiply); + +void after_stroke(PBVH *pbvh, bool force_balance); +} // namespace blender::bke::dyntopo diff --git a/source/blender/blenkernel/BKE_pbvh.h b/source/blender/blenkernel/BKE_pbvh.h index 1cf702fffb0..073884931eb 100644 --- a/source/blender/blenkernel/BKE_pbvh.h +++ b/source/blender/blenkernel/BKE_pbvh.h @@ -549,60 +549,7 @@ BLI_bitmap **BKE_pbvh_get_grid_visibility(const PBVH *pbvh); int BKE_pbvh_get_grid_num_verts(const PBVH *pbvh); int BKE_pbvh_get_grid_num_faces(const PBVH *pbvh); -/** - * Only valid for type == #PBVH_BMESH. - */ -void BKE_pbvh_bmesh_detail_size_set(PBVH *pbvh, float detail_size, float detail_range); -typedef enum { - PBVH_Subdivide = 1 << 0, - PBVH_Collapse = 1 << 1, - PBVH_Cleanup = 1 << 2, // dissolve verts surrounded by either 3 or 4 triangles then triangulate - PBVH_LocalSubdivide = 1 << 3, - PBVH_LocalCollapse = 1 << 4 -} PBVHTopologyUpdateMode; - -ENUM_OPERATORS(PBVHTopologyUpdateMode, PBVH_LocalCollapse); - -typedef float (*DyntopoMaskCB)(PBVHVertRef vertex, void *userdata); - -bool BKE_pbvh_bmesh_update_topology( - struct SculptSession *ss, - PBVH *pbvh, - PBVHTopologyUpdateMode mode, - const float center[3], - const float view_normal[3], - float radius, - const bool use_frontface, - const bool use_projected, - int symaxis, - bool updatePBVH, - DyntopoMaskCB mask_cb, - void *mask_cb_data, - int custom_max_steps, // if 0, will use defaul hueristics for max steps - bool disable_surface_relax, - bool is_snake_hook, - bool no_radius_test, - int edge_limit_multiply); - -bool BKE_pbvh_bmesh_update_topology_nodes(struct SculptSession *ss, - PBVH *pbvh, - bool (*searchcb)(PBVHNode *node, void *data), - void (*undopush)(PBVHNode *node, void *data), - void *searchdata, - PBVHTopologyUpdateMode mode, - const float center[3], - const float view_normal[3], - float radius, - const bool use_frontface, - const bool use_projected, - int sym_axis, - bool updatePBVH, - DyntopoMaskCB mask_cb, - void *mask_cb_data, - bool disable_surface_relax, - bool is_snake_hook, - int edge_limit_multiply); /* Node Access */ void BKE_pbvh_check_tri_areas(PBVH *pbvh, PBVHNode *node); @@ -685,8 +632,6 @@ struct TableGSet *BKE_pbvh_bmesh_node_faces(PBVHNode *node); void BKE_pbvh_bmesh_regen_node_verts(PBVH *pbvh, bool report); void BKE_pbvh_bmesh_mark_node_regen(PBVH *pbvh, PBVHNode *node); -void BKE_pbvh_bmesh_after_stroke(PBVH *pbvh, bool force_balance); - /* Update Bounding Box/Redraw and clear flags. */ void BKE_pbvh_update_bounds(PBVH *pbvh, int flags); @@ -1012,8 +957,6 @@ int BKE_pbvh_get_node_index(PBVH *pbvh, PBVHNode *node); int BKE_pbvh_get_node_id(PBVH *pbvh, PBVHNode *node); void BKE_pbvh_set_flat_vcol_shading(PBVH *pbvh, bool value); -#define DYNTOPO_CD_INTERP - void SCULPT_update_flat_vcol_shading(struct Object *ob, struct Scene *scene); void BKE_pbvh_curvature_update_set(PBVHNode *node, bool state); @@ -1075,8 +1018,6 @@ void BKE_pbvh_update_vert_boundary(int cd_faceset_offset, const bool do_uvs, float sharp_angle_limit); -#define DYNTOPO_DYNAMIC_TESS - PBVHNode *BKE_pbvh_get_node_leaf_safe(PBVH *pbvh, int i); void BKE_pbvh_get_vert_face_areas(PBVH *pbvh, PBVHVertRef vertex, float *r_areas, int valence); @@ -1228,33 +1169,8 @@ BLI_INLINE bool _pbvh_nan_check(const float *co, const char *func, const char *f # define PBVH_CHECK_NAN(co) #endif -typedef struct DynTopoState DynTopoState; - -typedef struct DynRemeshParams { - float edge_size; - float detail_range; - float relax_strength; -} DynRemeshParams; - /* -Simple wrapper api to use the dyntopo remesher in -non-sculpt contexts. - -existing_pbvh can be NULL. - -Note that all the sculpt customdata layers will be created -if they don't exist, so cd_vert/face_node_offset, cd_mask_offset, -etc*/ -DynTopoState *BKE_dyntopo_init(struct BMesh *bm, PBVH *existing_pbvh); -void BKE_dyntopo_free(DynTopoState *ds); -void BKE_dyntopo_default_params(DynRemeshParams *params, float edge_size); -void BKE_dyntopo_remesh(DynTopoState *ds, - DynRemeshParams *params, - int steps, - PBVHTopologyUpdateMode mode); -/* - -use pmap to build an array of edge indices surrounding vertex +Uses pmap to build an array of edge indices surrounding vertex r_edges, r_edges_size, heap_alloc define an existing array to put data in. final array is similarly put in these pointers. note that calling code diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt index 4cd03b281a7..e079131cf51 100644 --- a/source/blender/blenkernel/CMakeLists.txt +++ b/source/blender/blenkernel/CMakeLists.txt @@ -376,6 +376,7 @@ set(SRC BKE_displist.h BKE_duplilist.h BKE_dynamicpaint.h + BKE_dyntopo.hh BKE_editlattice.h BKE_editmesh.h BKE_editmesh_bvh.h diff --git a/source/blender/blenkernel/intern/customdata.cc b/source/blender/blenkernel/intern/customdata.cc index 8b5ef49719f..a139763cc91 100644 --- a/source/blender/blenkernel/intern/customdata.cc +++ b/source/blender/blenkernel/intern/customdata.cc @@ -2698,7 +2698,7 @@ void CustomData_copy_layout(const struct CustomData *source, CustomData_merge_layout(source, dest, mask, alloctype, totelem); } -ATTR_NO_OPT static void customData_free_layer__internal(CustomDataLayer *layer, const int totelem) +static void customData_free_layer__internal(CustomDataLayer *layer, const int totelem) { if (layer->anonymous_id != nullptr) { layer->anonymous_id->remove_user_and_delete_if_last(); diff --git a/source/blender/blenkernel/intern/dyntopo.cc b/source/blender/blenkernel/intern/dyntopo.cc index 3c83c040df1..70defef5ed9 100644 --- a/source/blender/blenkernel/intern/dyntopo.cc +++ b/source/blender/blenkernel/intern/dyntopo.cc @@ -25,16 +25,18 @@ #include "BLI_heap_simple.h" #include "BLI_linklist.h" #include "BLI_math.h" +#include "BLI_math_vector_types.hh" #include "BLI_memarena.h" #include "BLI_rand.h" #include "BLI_smallhash.h" #include "BLI_task.h" #include "BLI_utildefines.h" + #include "PIL_time.h" #include "atomic_ops.h" #include "BKE_customdata.h" -#include "BKE_dyntopo.h" +#include "BKE_dyntopo.hh" #include "BKE_paint.h" #include "BKE_pbvh.h" #include "BKE_sculpt.hh" @@ -47,7 +49,7 @@ #include -namespace blender::dyntopo { +namespace blender::bke::dyntopo { using blender::float2; using blender::float3; @@ -73,17 +75,11 @@ void bm_kill_only_face(BMesh *bm, BMFace *f); } static bool edge_queue_test(struct EdgeQueueContext *eq_ctx, PBVH *pbvh, BMEdge *e); -static void edge_queue_create_local(struct EdgeQueueContext *eq_ctx, +static void edge_queue_create_local(EdgeQueueContext *eq_ctx, PBVH *pbvh, - const float center[3], - const float view_normal[3], - float radius, - const bool use_frontface, - const bool use_projected, - PBVHTopologyUpdateMode local_mode, - bool no_radius_test); + PBVHTopologyUpdateMode local_mode); -ATTR_NO_OPT static void surface_smooth_v_safe( +static void surface_smooth_v_safe( SculptSession *ss, PBVH *pbvh, BMVert *v, float fac, bool reproject_cdata) { float co[3]; @@ -273,12 +269,6 @@ static void edge_queue_insert_val34_vert(EdgeQueueContext *eq_ctx, BMVert *v) eq_ctx->used_verts.append(v); } -static bool edge_queue_vert_in_sphere(const EdgeQueueContext *eq_ctx, BMVert *v) -{ - /* Check if triangle intersects the sphere */ - return len_squared_v3v3(eq_ctx->center, v->co) <= eq_ctx->radius_squared; -} - /* Profiling revealed the accurate distance to tri in blenlib was too slow, so we use a simpler version here @@ -360,8 +350,7 @@ static bool point_in_tri_v3(float p[3], float v1[3], float v2[3], float v3[3], f #endif } -static float dist_to_tri_sphere_simple( - float p[3], float v1[3], float v2[3], float v3[3], float n[3]) +float dist_to_tri_sphere_simple(float p[3], float v1[3], float v2[3], float v3[3], float n[3]) { float co[3]; float t1[3], t2[3], t3[3]; @@ -408,48 +397,6 @@ static float dist_to_tri_sphere_simple( return dis; } -ATTR_NO_OPT static bool edge_queue_tri_in_sphere(const EdgeQueueContext *q, - BMVert *vs[3], - float no[3]) -{ -#if 0 - float cent[3]; - - zero_v3(cent); - add_v3_v3(cent, l->v->co); - add_v3_v3(cent, l->next->v->co); - add_v3_v3(cent, l->prev->v->co); - - mul_v3_fl(cent, 1.0f / 3.0f); - return len_squared_v3v3(cent, q->center) < q->radius_squared; -#endif - - /* Check if triangle intersects the sphere */ -#if 1 - float dis = dist_to_tri_sphere_simple( - (float *)q->center, (float *)vs[0]->co, (float *)vs[1]->co, (float *)vs[2]->co, (float *)no); -#else - float dis = len_squared_v3v3(q->center, l->v->co); -#endif - - return dis <= q->radius_squared; -} - -static bool edge_queue_tri_in_circle(const EdgeQueueContext *q, BMVert *v_tri[3], float /*no*/[3]) -{ - float c[3]; - float tri_proj[3][3]; - - project_plane_normalized_v3_v3v3(tri_proj[0], v_tri[0]->co, q->view_normal); - project_plane_normalized_v3_v3v3(tri_proj[1], v_tri[1]->co, q->view_normal); - project_plane_normalized_v3_v3v3(tri_proj[2], v_tri[2]->co, q->view_normal); - - closest_on_tri_to_point_v3(c, q->center_proj, tri_proj[0], tri_proj[1], tri_proj[2]); - - /* Check if triangle intersects the sphere */ - return len_squared_v3v3(q->center_proj, c) <= q->radius_squared; -} - typedef struct EdgeQueueThreadData { PBVH *pbvh; PBVHNode *node; @@ -487,15 +434,6 @@ static void edge_thread_data_insert(EdgeQueueThreadData *tdata, BMEdge *e) tdata->totedge++; } -static bool edge_queue_vert_in_circle(const EdgeQueueContext *eq_ctx, BMVert *v) -{ - float c[3]; - - project_plane_normalized_v3_v3v3(c, v->co, eq_ctx->view_normal); - - return len_squared_v3v3(eq_ctx->center_proj, c) <= eq_ctx->radius_squared; -} - static void long_edge_queue_edge_add_recursive( EdgeQueueContext *eq_ctx, BMLoop *l_edge, const float len_sq, float limit_len, int depth) { @@ -622,9 +560,9 @@ BLI_INLINE int dyntopo_thread_rand(int seed) return (seed * multiplier + addend) & mask; } -ATTR_NO_OPT static void unified_edge_queue_task_cb(void *__restrict userdata, - const int n, - const TaskParallelTLS *__restrict /*tls*/) +static void unified_edge_queue_task_cb(void *__restrict userdata, + const int n, + const TaskParallelTLS *__restrict /*tls*/) { EdgeQueueThreadData *tdata = ((EdgeQueueThreadData *)userdata) + n; PBVH *pbvh = tdata->pbvh; @@ -696,7 +634,7 @@ ATTR_NO_OPT static void unified_edge_queue_task_cb(void *__restrict userdata, BMVert *vs[3] = {(BMVert *)tribuf->verts[tri->v[0]].i, (BMVert *)tribuf->verts[tri->v[1]].i, (BMVert *)tribuf->verts[tri->v[2]].i}; - if (eq_ctx->edge_queue_tri_in_range(eq_ctx, vs, f->no)) { + if (eq_ctx->brush_tester->tri_in_range(vs, f->no)) { f->head.hflag |= facetag; /* Check each edge of the face. */ @@ -1147,54 +1085,7 @@ bool check_vert_fan_are_tris(PBVH *pbvh, BMVert *v) return false; } -static bool edge_queue_tri_null_test(const EdgeQueueContext * /*eq_ctx*/, - BMVert * /*v_tri*/[3], - float /*no*/[3]) -{ - return true; -} - -static bool edge_queue_vert_null_test(const EdgeQueueContext * /*eq_ctx*/, BMVert * /*v*/) -{ - return true; -} - -static void edge_queue_init(EdgeQueueContext *eq_ctx, - bool use_projected, - bool use_frontface, - const float center[3], - const float view_normal[3], - const float radius, - bool no_radius_test) -{ - if (no_radius_test) { - eq_ctx->edge_queue_tri_in_range = edge_queue_tri_null_test; - eq_ctx->edge_queue_vert_in_range = edge_queue_vert_null_test; - } - else if (use_projected) { - eq_ctx->edge_queue_tri_in_range = edge_queue_tri_in_circle; - eq_ctx->edge_queue_vert_in_range = edge_queue_vert_in_circle; - project_plane_normalized_v3_v3v3(eq_ctx->center_proj, center, view_normal); - } - else { - eq_ctx->edge_queue_tri_in_range = edge_queue_tri_in_sphere; - eq_ctx->edge_queue_vert_in_range = edge_queue_vert_in_sphere; - } - - eq_ctx->center = center; - copy_v3_v3(eq_ctx->view_normal, view_normal); - eq_ctx->radius_squared = radius * radius; - eq_ctx->limit_len_min_sqr = eq_ctx->limit_len_min * eq_ctx->limit_len_min; - eq_ctx->limit_len_max_sqr = eq_ctx->limit_len_max * eq_ctx->limit_len_max; - -#ifdef USE_EDGEQUEUE_FRONTFACE - eq_ctx->use_view_normal = use_frontface; -#else - UNUSED_VARS(use_frontface); -#endif -} - -ATTR_NO_OPT static bool edge_queue_test(EdgeQueueContext *eq_ctx, PBVH * /*pbvh*/, BMEdge *e) +static bool edge_queue_test(EdgeQueueContext *eq_ctx, PBVH * /*pbvh*/, BMEdge *e) { float len = len_squared_v3v3(e->v1->co, e->v2->co); float min = eq_ctx->limit_len_min_sqr; @@ -1224,35 +1115,13 @@ ATTR_NO_OPT static bool edge_queue_test(EdgeQueueContext *eq_ctx, PBVH * /*pbvh* */ static void unified_edge_queue_create(EdgeQueueContext *eq_ctx, PBVH *pbvh, - const float center[3], - const float view_normal[3], - float radius, - const bool use_frontface, - const bool use_projected, - PBVHTopologyUpdateMode local_mode, - bool no_radius_test) + PBVHTopologyUpdateMode local_mode) { if (local_mode) { - edge_queue_create_local(eq_ctx, - pbvh, - center, - view_normal, - radius, - use_frontface, - use_projected, - local_mode, - no_radius_test); + edge_queue_create_local(eq_ctx, pbvh, local_mode); return; } - eq_ctx->radius_squared = radius * radius; - - eq_ctx->limit_len_min = pbvh->bm_min_edge_len; - eq_ctx->limit_len_max = pbvh->bm_max_edge_len; - - edge_queue_init( - eq_ctx, use_projected, use_frontface, center, eq_ctx->view_normal, radius, no_radius_test); - #ifdef USE_EDGEQUEUE_TAG_VERIFY pbvh_bmesh_edge_tag_verify(pbvh); #endif @@ -1407,7 +1276,7 @@ static void short_edge_queue_task_cb_local(void *__restrict userdata, #endif BMVert *vs[3] = {f->l_first->v, f->l_first->next->v, f->l_first->next->next->v}; - if (eq_ctx->edge_queue_tri_in_range(eq_ctx, vs, f->no)) { + if (eq_ctx->brush_tester->tri_in_range(vs, f->no)) { BMLoop *l = f->l_first; do { @@ -1421,21 +1290,10 @@ static void short_edge_queue_task_cb_local(void *__restrict userdata, static void edge_queue_create_local(EdgeQueueContext *eq_ctx, PBVH *pbvh, - const float center[3], - const float /*view_normal*/[3], - float radius, - const bool use_frontface, - const bool use_projected, - PBVHTopologyUpdateMode local_mode, - bool no_radius_test) + PBVHTopologyUpdateMode local_mode) { - eq_ctx->limit_len_min = pbvh->bm_min_edge_len; - eq_ctx->limit_len_max = pbvh->bm_max_edge_len; eq_ctx->local_mode = true; - edge_queue_init( - eq_ctx, use_projected, use_frontface, center, eq_ctx->view_normal, radius, no_radius_test); - Vector tdata; for (int n = 0; n < pbvh->totnode; n++) { @@ -1663,13 +1521,7 @@ static void edge_queue_create_local(EdgeQueueContext *eq_ctx, } } -ATTR_NO_OPT static bool cleanup_valence_3_4(EdgeQueueContext *ectx, - PBVH *pbvh, - const float center[3], - const float /*view_normal*/[3], - float radius, - const bool /*use_frontface*/, - const bool /*use_projected*/) +static bool cleanup_valence_3_4(EdgeQueueContext *ectx, PBVH *pbvh) { bool modified = false; @@ -1677,9 +1529,6 @@ ATTR_NO_OPT static bool cleanup_valence_3_4(EdgeQueueContext *ectx, bm_log_message(" == cleanup_valence_3_4 == "); - float radius2 = radius * 1.25; - float rsqr = radius2 * radius2; - const int cd_vert_node = pbvh->cd_vert_node_offset; int updateflag = SCULPTFLAG_NEED_DISK_SORT | SCULPTFLAG_NEED_VALENCE; @@ -1695,9 +1544,8 @@ ATTR_NO_OPT static bool cleanup_valence_3_4(EdgeQueueContext *ectx, } PBVHVertRef sv = {(intptr_t)v}; - if (len_squared_v3v3(v->co, center) >= rsqr || !v->e || - ectx->mask_cb(sv, ectx->mask_cb_data) < 0.5f) - { + if (!ectx->brush_tester->vert_in_range(v) || !v->e || + ectx->mask_cb(sv, ectx->mask_cb_data) < 0.5f) { continue; } @@ -1771,7 +1619,7 @@ ATTR_NO_OPT static bool cleanup_valence_3_4(EdgeQueueContext *ectx, l = l->next; } - /*ignore non-manifold edges along with ones flagged as sharp*/ + /* Ignore non-manifold edges along with ones flagged as sharp. */ if (l->radial_next == l || l->radial_next->radial_next != l || !(l->e->head.hflag & BM_ELEM_SMOOTH)) { @@ -1990,13 +1838,7 @@ ATTR_NO_OPT static bool cleanup_valence_3_4(EdgeQueueContext *ectx, return modified; } -static bool do_cleanup_3_4(EdgeQueueContext *eq_ctx, - PBVH *pbvh, - const float center[3], - const float view_normal[3], - float radius, - bool use_frontface, - bool use_projected) +static bool do_cleanup_3_4(EdgeQueueContext *eq_ctx, PBVH *pbvh) { bool modified = false; @@ -2016,7 +1858,7 @@ static bool do_cleanup_3_4(EdgeQueueContext *eq_ctx, } bool ok = BM_ELEM_CD_GET_INT(v, pbvh->cd_valence) < 5; - ok = ok && eq_ctx->edge_queue_vert_in_range(eq_ctx, v); + ok = ok && eq_ctx->brush_tester->vert_in_range(v); if (ok) { edge_queue_insert_val34_vert(eq_ctx, v); @@ -2031,8 +1873,7 @@ static bool do_cleanup_3_4(EdgeQueueContext *eq_ctx, pbvh_bmesh_check_nodes(pbvh); - modified |= cleanup_valence_3_4( - eq_ctx, pbvh, center, view_normal, radius, use_frontface, use_projected); + modified |= cleanup_valence_3_4(eq_ctx, pbvh); pbvh_bmesh_check_nodes(pbvh); return modified; @@ -2044,23 +1885,16 @@ float mask_cb_nop(PBVHVertRef /*vertex*/, void * /*userdata*/) } /* Collapse short edges, subdivide long edges */ -extern "C" bool BKE_pbvh_bmesh_update_topology(SculptSession *ss, - PBVH *pbvh, - PBVHTopologyUpdateMode mode, - const float center[3], - const float view_normal[3], - float radius, - const bool use_frontface, - const bool use_projected, - int /*sym_axis*/, - bool updatePBVH, - DyntopoMaskCB mask_cb, - void *mask_cb_data, - int custom_max_steps, - bool disable_surface_relax, - bool /*is_snake_hook*/, - bool no_radius_test, - int edge_limit_multiply) +bool remesh_topology(BrushTester *brush_tester, + struct SculptSession *ss, + PBVH *pbvh, + PBVHTopologyUpdateMode mode, + bool use_frontface, + float3 view_normal, + bool updatePBVH, + DyntopoMaskCB mask_cb, + void *mask_cb_data, + int edge_limit_multiply) { /* Push a subentry. */ BM_log_entry_add_ex(pbvh->header.bm, pbvh->bm_log, true); @@ -2114,11 +1948,15 @@ extern "C" bool BKE_pbvh_bmesh_update_topology(SculptSession *ss, EdgeQueueContext eq_ctx = {}; eq_ctx.ss = ss; + eq_ctx.brush_tester = brush_tester; + eq_ctx.use_view_normal = use_frontface; + eq_ctx.view_normal = view_normal; eq_ctx.pool = nullptr; eq_ctx.bm = pbvh->header.bm; eq_ctx.mask_cb = mask_cb; eq_ctx.mask_cb_data = mask_cb_data; + eq_ctx.view_normal = view_normal; eq_ctx.cd_vert_mask_offset = cd_vert_mask_offset; eq_ctx.cd_vert_node_offset = cd_vert_node_offset; @@ -2134,23 +1972,16 @@ extern "C" bool BKE_pbvh_bmesh_update_topology(SculptSession *ss, #ifdef DYNTOPO_USE_MINMAX_HEAP // eq_ctx.used_verts = BLI_table_gset_new(__func__); - eq_ctx.max_heap_mm = DYNTOPO_MAX_ITER << 8; - eq_ctx.limit_min = pbvh->bm_min_edge_len; - eq_ctx.limit_max = pbvh->bm_max_edge_len; - eq_ctx.limit_mid = eq_ctx.limit_max * 0.5f + eq_ctx.limit_min * 0.5f; - - eq_ctx.use_view_normal = use_frontface; - if (view_normal) { - copy_v3_v3(eq_ctx.view_normal, view_normal); - } - else { - zero_v3(eq_ctx.view_normal); - eq_ctx.view_normal[2] = 1.0f; - } + eq_ctx.max_heap_mm = (DYNTOPO_MAX_ITER * edge_limit_multiply) << 8; + eq_ctx.limit_len_min = pbvh->bm_min_edge_len; + eq_ctx.limit_len_max = pbvh->bm_max_edge_len; + eq_ctx.limit_len_min_sqr = eq_ctx.limit_len_min * eq_ctx.limit_len_min; + eq_ctx.limit_len_max_sqr = eq_ctx.limit_len_max * eq_ctx.limit_len_max; + eq_ctx.limit_mid = eq_ctx.limit_len_max * 0.5f + eq_ctx.limit_len_min * 0.5f; #endif - eq_ctx.surface_smooth_fac = disable_surface_relax ? 0.0f : DYNTOPO_SAFE_SMOOTH_FAC; + eq_ctx.surface_smooth_fac = DYNTOPO_SAFE_SMOOTH_FAC; if (mode & PBVH_LocalSubdivide) { mode |= PBVH_Subdivide; @@ -2167,19 +1998,7 @@ extern "C" bool BKE_pbvh_bmesh_update_topology(SculptSession *ss, #endif if (mode & (PBVH_Subdivide | PBVH_Collapse)) { - unified_edge_queue_create(&eq_ctx, - pbvh, - center, - view_normal, - radius, - use_frontface, - use_projected, - mode & (PBVH_LocalSubdivide | PBVH_LocalCollapse), - no_radius_test); - } - else { - edge_queue_init( - &eq_ctx, use_projected, use_frontface, center, eq_ctx.view_normal, radius, no_radius_test); + unified_edge_queue_create(&eq_ctx, pbvh, mode & (PBVH_LocalSubdivide | PBVH_LocalCollapse)); } #ifdef SKINNY_EDGE_FIX @@ -2234,25 +2053,23 @@ extern "C" bool BKE_pbvh_bmesh_update_topology(SculptSession *ss, steps[0] = DYNTOPO_MAX_ITER; } + printf("%s: edge_limit_multiply: %d\n", __func__, edge_limit_multiply); + steps[0] *= edge_limit_multiply; steps[1] *= edge_limit_multiply; - int max_steps = (max_ii(custom_max_steps, DYNTOPO_MAX_ITER) * edge_limit_multiply) - << (totop - 1); + int max_steps = (DYNTOPO_MAX_ITER * edge_limit_multiply) << (totop - 1); int max_subd = max_steps >> (totop - 1); int edges_size = steps[0]; BMEdge **edges = (BMEdge **)MEM_malloc_arrayN(edges_size, sizeof(void *), __func__); int etot = 0; - int count = 0, count_subd = 0, count_cold = 0; + int count = 0, count_subd = 0; //, count_cold = 0; int i = 0; int curop = 0; float limit_len_subd = eq_ctx.limit_len_max_sqr; float limit_len_cold = eq_ctx.limit_len_min_sqr; - // limit_len_cold = limit_len_cold * limit_len_cold; - - // printf(" minmax queue size: %d\n", BLI_mm_heap_len(eq_ctx.heap_mm)); SmallHash subd_edges; BLI_smallhash_init(&subd_edges); @@ -2354,7 +2171,7 @@ extern "C" bool BKE_pbvh_bmesh_update_topology(SculptSession *ss, pbvh_bmesh_collapse_edge(pbvh, e, e->v1, e->v2, nullptr, nullptr, &eq_ctx); VALIDATE_LOG(pbvh->bm_log); - count_cold++; + // count_cold++; break; } case PBVH_LocalSubdivide: @@ -2405,8 +2222,7 @@ extern "C" bool BKE_pbvh_bmesh_update_topology(SculptSession *ss, BLI_smallhash_release(&subd_edges); if (mode & PBVH_Cleanup) { - modified |= do_cleanup_3_4( - &eq_ctx, pbvh, center, eq_ctx.view_normal, radius, use_frontface, use_projected); + modified |= do_cleanup_3_4(&eq_ctx, pbvh); VALIDATE_LOG(pbvh->bm_log); } @@ -3034,13 +2850,19 @@ static void pbvh_split_edges(EdgeQueueContext *eq_ctx, float3 co = rl->v->co; co = (co + rl->next->v->co) * 0.5f; - float len = len_v3v3(co, eq_ctx->center); - float radius = sqrtf(eq_ctx->radius_squared); float w = calc_weighted_length(eq_ctx, rl->e->v1, rl->e->v2, -1.0f); - float fac = 1.0f + 2.0 * (len / radius); + if (eq_ctx->brush_tester->is_sphere_or_tube) { + BrushSphere *sphere_test = static_cast(eq_ctx->brush_tester); - if (w / fac > eq_ctx->limit_len_max) { + float len = len_v3v3(co, sphere_test->center()); + float fac = 1.0f + 2.0 * (len / sphere_test->radius()); + if (w / fac > eq_ctx->limit_len_max) { + + long_edge_queue_edge_add_recursive(eq_ctx, rl, w, eq_ctx->limit_len_max, 0); + } + } + else { long_edge_queue_edge_add_recursive(eq_ctx, rl, w, eq_ctx->limit_len_max, 0); } #endif @@ -3114,276 +2936,17 @@ static void pbvh_split_edges(EdgeQueueContext *eq_ctx, bm_logstack_pop(); } - -typedef struct DynTopoState { - PBVH *pbvh; - bool is_fake_pbvh; -} DynTopoState; - -/* existing_pbvh may be nullptr, if so a fake one will be created. -Note that all the sculpt customdata layers will be created -if they don't exist, so cd_vert/face_node_offset, cd_mask_offset, -etc*/ -DynTopoState *BKE_dyntopo_init(BMesh *bm, PBVH *existing_pbvh) +void detail_size_set(PBVH *pbvh, float detail_size, float detail_range) { - PBVH *pbvh; - - if (!existing_pbvh) { - pbvh = MEM_new("pbvh"); - - pbvh->nodes = static_cast(MEM_callocN(sizeof(PBVHNode), "PBVHNode")); - pbvh->header.type = PBVH_BMESH; - pbvh->totnode = 1; - - PBVHNode *node = pbvh->nodes; - - node->flag = PBVH_Leaf | PBVH_UpdateTris | PBVH_UpdateTriAreas; - node->bm_faces = BLI_table_gset_new_ex("node->bm_faces", bm->totface); - node->bm_unique_verts = BLI_table_gset_new_ex("node->bm_unique_verts", bm->totvert); - } - else { - pbvh = existing_pbvh; - } - - if (!pbvh->bm_idmap) { - pbvh->bm_idmap = BM_idmap_new(bm, BM_VERT | BM_EDGE | BM_FACE); - } - - const bool isfake = pbvh != existing_pbvh; - - BMCustomLayerReq vlayers[] = {{CD_PAINT_MASK, nullptr, 0}, - {CD_PROP_INT32, - SCULPT_ATTRIBUTE_NAME(dyntopo_node_id_vertex), - CD_FLAG_TEMPORARY | CD_FLAG_NOCOPY}}; - - BMCustomLayerReq flayers[] = { - {CD_PROP_FLOAT2, SCULPT_ATTRIBUTE_NAME(face_areas), CD_FLAG_TEMPORARY | CD_FLAG_NOCOPY}, - {CD_PROP_INT32, ".sculpt_face_set", 0}, - {CD_PROP_INT32, - SCULPT_ATTRIBUTE_NAME(dyntopo_node_id_face), - CD_FLAG_TEMPORARY | CD_FLAG_NOCOPY}}; - - BM_data_layers_ensure(bm, &bm->vdata, vlayers, 3); - BM_data_layers_ensure(bm, &bm->pdata, flayers, 3); - - pbvh->header.bm = bm; - - pbvh->cd_vert_node_offset = CustomData_get_offset_named( - &bm->vdata, CD_PROP_INT32, SCULPT_ATTRIBUTE_NAME(dyntopo_node_id_vertex)); - - pbvh->cd_face_node_offset = CustomData_get_offset_named( - &bm->pdata, CD_PROP_INT32, SCULPT_ATTRIBUTE_NAME(dyntopo_node_id_face)); - - pbvh->cd_face_area = CustomData_get_offset_named( - &bm->pdata, CD_PROP_FLOAT2, SCULPT_ATTRIBUTE_NAME(face_areas)); - - pbvh->cd_vert_mask_offset = CustomData_get_offset(&bm->vdata, CD_PAINT_MASK); - pbvh->cd_faceset_offset = CustomData_get_offset_named( - &bm->pdata, CD_PROP_INT32, ".sculpt_face_set"); - pbvh->cd_vcol_offset = -1; - pbvh->cd_valence = CustomData_get_offset_named( - &bm->vdata, CD_PROP_INT32, SCULPT_ATTRIBUTE_NAME(valence)); - pbvh->cd_flag = CustomData_get_offset_named( - &bm->vdata, CD_PROP_INT8, SCULPT_ATTRIBUTE_NAME(flags)); - - if (isfake) { - pbvh->bm_log = BM_log_create(bm, pbvh->bm_idmap); - } - - BMVert *v; - BMFace *f; - BMIter iter; - - if (isfake) { - BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) { - BM_ELEM_CD_SET_INT(v, pbvh->cd_vert_node_offset, 0); - BLI_table_gset_add(pbvh->nodes->bm_unique_verts, v); - } - - BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { - BM_ELEM_CD_SET_INT(f, pbvh->cd_face_node_offset, 0); - BLI_table_gset_add(pbvh->nodes->bm_faces, f); - } - - BKE_pbvh_bmesh_check_tris(pbvh, pbvh->nodes); - } - - DynTopoState *ds = MEM_cnew("DynTopoState"); - - ds->pbvh = pbvh; - ds->is_fake_pbvh = isfake; - - return ds; + pbvh->bm_detail_range = max_ff(detail_range, 0.1f); + pbvh->bm_max_edge_len = detail_size; + pbvh->bm_min_edge_len = pbvh->bm_max_edge_len * pbvh->bm_detail_range; } - -void BKE_dyntopo_default_params(DynRemeshParams *params, float edge_size) -{ - memset(params, 0, sizeof(*params)); - params->detail_range = 0.45f; - params->edge_size = edge_size; -} - -void BKE_dyntopo_free(DynTopoState *ds) -{ - if (ds->is_fake_pbvh) { - BM_log_free(ds->pbvh->bm_log); - BM_idmap_destroy(ds->pbvh->bm_idmap); - - PBVHNode *node = ds->pbvh->nodes; - - if (node->tribuf || node->tri_buffers) { - BKE_pbvh_bmesh_free_tris(ds->pbvh, node); - } - - BLI_table_gset_free(node->bm_faces, nullptr); - BLI_table_gset_free(node->bm_unique_verts, nullptr); - - MEM_freeN(ds->pbvh->nodes); - MEM_delete(ds->pbvh); - } - - MEM_freeN(ds); -} - -void BKE_dyntopo_remesh(DynTopoState *ds, - DynRemeshParams *params, - int steps, - PBVHTopologyUpdateMode mode) -{ - float cent[3] = {0.0f, 0.0f, 0.0f}; - int totcent = 0; - float view[3] = {0.0f, 0.0f, 1.0f}; - - BMIter iter; - BMVert *v; - - BM_ITER_MESH (v, &iter, ds->pbvh->header.bm, BM_VERTS_OF_MESH) { - pbvh_boundary_update_bmesh(ds->pbvh, v); - dyntopo_add_flag(ds->pbvh, v, SCULPTFLAG_NEED_TRIANGULATE); - BM_ELEM_CD_SET_INT(v, ds->pbvh->cd_valence, BM_vert_edge_count(v)); - - pbvh_check_vert_boundary(ds->pbvh, v); - - add_v3_v3(cent, v->co); - totcent++; - } - - if (totcent) { - mul_v3_fl(cent, 1.0f / (float)totcent); - } - - ds->pbvh->bm_max_edge_len = params->edge_size; - ds->pbvh->bm_detail_range = max_ff(params->detail_range, 0.1f); - ds->pbvh->bm_min_edge_len = params->edge_size * ds->pbvh->bm_detail_range; - - /* subdivide once */ - if (mode & PBVH_Subdivide) { - BKE_pbvh_bmesh_update_topology(nullptr, - ds->pbvh, - PBVH_Subdivide, - cent, - view, - 1e17, - false, - false, - 0, - false, - mask_cb_nop, - nullptr, - ds->pbvh->header.bm->totedge, - false, - false, - false, - 1); - } - - for (int i = 0; i < steps; i++) { - for (int j = 0; j < ds->pbvh->totnode; j++) { - PBVHNode *node = ds->pbvh->nodes + j; - - if (node->flag & PBVH_Leaf) { - node->flag |= PBVH_UpdateTopology; - } - } - - BKE_pbvh_bmesh_update_topology(nullptr, - ds->pbvh, - mode, - cent, - view, - 1e17, - false, - false, - 0, - false, - mask_cb_nop, - nullptr, - ds->pbvh->header.bm->totedge * 5, - false, - false, - false, - 1); - - BKE_pbvh_update_normals(ds->pbvh, nullptr); - - BM_ITER_MESH (v, &iter, ds->pbvh->header.bm, BM_VERTS_OF_MESH) { - pbvh_check_vert_boundary(ds->pbvh, v); - int boundflag = BM_ELEM_CD_GET_INT(v, ds->pbvh->cd_boundary_flag); - - float avg[3] = {0.0f, 0.0f, 0.0f}; - float totw = 0.0f; - - bool bound1 = boundflag & SCULPTVERT_ALL_BOUNDARY; - if (bound1) { - continue; - } - - if (boundflag & SCULPTVERT_ALL_CORNER) { - continue; - } - - if (!v->e) { - continue; - } - - BMEdge *e = v->e; - do { - BMVert *v2 = BM_edge_other_vert(e, v); - - pbvh_check_vert_boundary(ds->pbvh, v2); - int boundflag2 = BM_ELEM_CD_GET_INT(v2, ds->pbvh->cd_boundary_flag); - - bool bound2 = boundflag2 & SCULPTVERT_ALL_BOUNDARY; - - if (bound1 && !bound2) { - continue; - } - - float tmp[3]; - float w = 1.0f; - - sub_v3_v3v3(tmp, v2->co, v->co); - madd_v3_v3fl(tmp, v->no, -dot_v3v3(v->no, tmp) * 0.75); - add_v3_v3(tmp, v->co); - madd_v3_v3fl(avg, tmp, w); - - totw += w; - } while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e); - - if (totw == 0.0f) { - continue; - } - - mul_v3_fl(avg, 1.0f / totw); - interp_v3_v3v3(v->co, v->co, avg, 0.5f); - } - } -} -} // namespace blender::dyntopo +} // namespace blender::bke::dyntopo void BKE_pbvh_bmesh_remove_face(PBVH *pbvh, BMFace *f, bool log_face) { - blender::dyntopo::pbvh_bmesh_face_remove(pbvh, f, log_face, true, true); + blender::bke::dyntopo::pbvh_bmesh_face_remove(pbvh, f, log_face, true, true); } void BKE_pbvh_bmesh_remove_edge(PBVH *pbvh, BMEdge *e, bool log_edge) @@ -3397,7 +2960,7 @@ void BKE_pbvh_bmesh_remove_edge(PBVH *pbvh, BMEdge *e, bool log_edge) void BKE_pbvh_bmesh_remove_vertex(PBVH *pbvh, BMVert *v, bool log_vert) { - blender::dyntopo::pbvh_bmesh_vert_remove(pbvh, v); + blender::bke::dyntopo::pbvh_bmesh_vert_remove(pbvh, v); if (log_vert) { BM_log_vert_removed(pbvh->header.bm, pbvh->bm_log, v); @@ -3454,10 +3017,10 @@ void BKE_pbvh_bmesh_add_face(PBVH *pbvh, struct BMFace *f, bool log_face, bool f bm_logstack_pop(); } -ATTR_NO_OPT void BKE_sculpt_reproject_cdata(SculptSession *ss, - PBVHVertRef vertex, - float startco[3], - float startno[3]) +void BKE_sculpt_reproject_cdata(SculptSession *ss, + PBVHVertRef vertex, + float startco[3], + float startno[3]) { BMVert *v = (BMVert *)vertex.i; BMEdge *e; diff --git a/source/blender/blenkernel/intern/dyntopo_collapse.cc b/source/blender/blenkernel/intern/dyntopo_collapse.cc index b165171179b..966c6b3aa28 100644 --- a/source/blender/blenkernel/intern/dyntopo_collapse.cc +++ b/source/blender/blenkernel/intern/dyntopo_collapse.cc @@ -24,7 +24,7 @@ #include "atomic_ops.h" #include "BKE_customdata.h" -#include "BKE_dyntopo.h" +#include "BKE_dyntopo.hh" #include "BKE_paint.h" #include "BKE_pbvh.h" @@ -45,7 +45,7 @@ using blender::Map; using blender::Set; using blender::Vector; -namespace blender::dyntopo { +namespace blender::bke::dyntopo { typedef struct TraceData { PBVH *pbvh; @@ -702,7 +702,7 @@ BMVert *pbvh_bmesh_collapse_edge(PBVH *pbvh, if (!v_conn->e) { /* Delete isolated vertex. */ if (BM_ELEM_CD_GET_INT(v_conn, pbvh->cd_vert_node_offset) != DYNTOPO_NODE_NONE) { - blender::dyntopo::pbvh_bmesh_vert_remove(pbvh, v_conn); + blender::bke::dyntopo::pbvh_bmesh_vert_remove(pbvh, v_conn); } BM_log_vert_removed(pbvh->header.bm, pbvh->bm_log, v_conn); @@ -727,4 +727,4 @@ BMVert *pbvh_bmesh_collapse_edge(PBVH *pbvh, return v_conn; } -} // namespace blender::dyntopo +} // namespace blender::bke::dyntopo diff --git a/source/blender/blenkernel/intern/dyntopo_intern.hh b/source/blender/blenkernel/intern/dyntopo_intern.hh index 60ee5bf21c8..0ebd605839f 100644 --- a/source/blender/blenkernel/intern/dyntopo_intern.hh +++ b/source/blender/blenkernel/intern/dyntopo_intern.hh @@ -1,12 +1,18 @@ //#define DYNTOPO_VALIDATE_LOG +#include "BKE_dyntopo.hh" #include "BKE_paint.h" #include "BKE_pbvh.h" + #include "BLI_asan.h" #include "BLI_heap_minmax.hh" +#include "BLI_math_vector_types.hh" + #include "bmesh.h" #include "pbvh_intern.hh" +using blender::float3; + struct GHash; struct BLI_Buffer; struct SculptSession; @@ -38,7 +44,7 @@ static inline void dyntopo_add_flag(PBVH *pbvh, BMVert *v, uint8_t flag) *BM_ELEM_CD_PTR(v, pbvh->cd_flag) |= flag; } -namespace blender::dyntopo { +namespace blender::bke::dyntopo { static int elem_sizes[] = {-1, (int)sizeof(BMVert), @@ -93,8 +99,8 @@ inline bool bm_elem_is_free(BMElem *elem, int htype) (SCULPT_CORNER_MESH | SCULPT_CORNER_FACE_SET | SCULPT_CORNER_SHARP_MARK | SCULPT_CORNER_SEAM | \ SCULPT_CORNER_UV | SCULPT_CORNER_SHARP_ANGLE) -#define DYNTOPO_MAX_ITER 4096 -#define DYNTOPO_MAX_ITER_SUBD 4096 +#define DYNTOPO_MAX_ITER 256 +#define DYNTOPO_MAX_ITER_SUBD 256 #define DYNTOPO_USE_HEAP #define DYNTOPO_USE_MINMAX_HEAP @@ -210,6 +216,7 @@ static void pbvh_bmesh_verify(PBVH *pbvh); struct EdgeQueue; struct EdgeQueueContext { + blender::bke::dyntopo::BrushTester *brush_tester; SculptSession *ss; BLI_mempool *pool = nullptr; BMesh *bm = nullptr; @@ -231,21 +238,16 @@ struct EdgeQueueContext { // TableGSet *used_verts; blender::Vector used_verts; - float view_normal[3]; + float3 view_normal; bool use_view_normal; - float limit_min, limit_max, limit_mid; - const float *center = nullptr; - float center_proj[3]; /* for when we use projected coords. */ float radius_squared; float limit_len_min; + float limit_mid; float limit_len_max; float limit_len_min_sqr; float limit_len_max_sqr; - bool (*edge_queue_tri_in_range)(const struct EdgeQueueContext *q, BMVert *vs[3], float no[3]); - bool (*edge_queue_vert_in_range)(const struct EdgeQueueContext *q, BMVert *v); - PBVHTopologyUpdateMode mode; bool reproject_cdata; }; @@ -321,7 +323,7 @@ BMFace *pbvh_bmesh_face_create(PBVH *pbvh, const BMFace *f_example, bool ensure_verts, bool log_face); -} // namespace blender::dyntopo +} // namespace blender::bke::dyntopo extern "C" { void BKE_pbvh_bmesh_remove_face(PBVH *pbvh, BMFace *f, bool log_face); diff --git a/source/blender/blenkernel/intern/pbvh_bmesh.cc b/source/blender/blenkernel/intern/pbvh_bmesh.cc index 3e212a1b8ba..3dd4029dca6 100644 --- a/source/blender/blenkernel/intern/pbvh_bmesh.cc +++ b/source/blender/blenkernel/intern/pbvh_bmesh.cc @@ -253,7 +253,7 @@ extern "C" void BKE_pbvh_bmesh_check_nodes(PBVH * /*pbvh*/) {} /** \} */ /****************************** Vertex/Face APIs ******************************/ -namespace blender::dyntopo { +namespace blender::bke::dyntopo { void pbvh_kill_vert(PBVH *pbvh, BMVert *v, bool log_vert, bool log_edges) { @@ -730,7 +730,7 @@ void pbvh_bmesh_face_remove( bm_logstack_pop(); } -} // namespace blender::dyntopo +} // namespace blender::bke::dyntopo /****************************** Building ******************************/ @@ -2481,7 +2481,7 @@ void BKE_pbvh_build_bmesh(PBVH *pbvh, pbvh->header.bm = bm; - BKE_pbvh_bmesh_detail_size_set(pbvh, 0.75f, 0.4f); + blender::bke::dyntopo::detail_size_set(pbvh, 0.75f, 0.4f); pbvh->header.type = PBVH_BMESH; pbvh->bm_log = log; @@ -2644,24 +2644,20 @@ void BKE_pbvh_set_bm_log(PBVH *pbvh, BMLog *log) BM_log_set_idmap(log, pbvh->bm_idmap); } -bool BKE_pbvh_bmesh_update_topology_nodes(SculptSession *ss, - PBVH *pbvh, - bool (*searchcb)(PBVHNode *node, void *data), - void (*undopush)(PBVHNode *node, void *data), - void *searchdata, - PBVHTopologyUpdateMode mode, - const float center[3], - const float view_normal[3], - float radius, - const bool use_frontface, - const bool use_projected, - int sym_axis, - bool updatePBVH, - DyntopoMaskCB mask_cb, - void *mask_cb_data, - bool disable_surface_relax, - bool is_snake_hook, - int edge_limit_multiply) +namespace blender::bke::dyntopo { +bool remesh_topology_nodes(blender::bke::dyntopo::BrushTester *brush_tester, + SculptSession *ss, + PBVH *pbvh, + bool (*searchcb)(PBVHNode *node, void *data), + void (*undopush)(PBVHNode *node, void *data), + void *searchdata, + PBVHTopologyUpdateMode mode, + const bool use_frontface, + float3 view_normal, + bool updatePBVH, + DyntopoMaskCB mask_cb, + void *mask_cb_data, + int edge_limit_multiply) { bool modified = false; Vector nodes; @@ -2685,26 +2681,20 @@ bool BKE_pbvh_bmesh_update_topology_nodes(SculptSession *ss, BKE_pbvh_node_mark_topology_update(node); } - modified = BKE_pbvh_bmesh_update_topology(ss, - pbvh, - mode, - center, - view_normal, - radius, - use_frontface, - use_projected, - sym_axis, - updatePBVH, - mask_cb, - mask_cb_data, - 4096, // is_snake_hook ? 4096 : 4096, - disable_surface_relax, - is_snake_hook, - false, - 1); + modified = remesh_topology(brush_tester, + ss, + pbvh, + mode, + use_frontface, + view_normal, + updatePBVH, + mask_cb, + mask_cb_data, + edge_limit_multiply); return modified; } +} // namespace blender::bke::dyntopo static void pbvh_free_tribuf(PBVHTriBuf *tribuf) { @@ -4007,7 +3997,8 @@ static void pbvh_bmesh_join_nodes(PBVH *pbvh) MEM_freeN(map); } -void BKE_pbvh_bmesh_after_stroke(PBVH *pbvh, bool force_balance) +namespace blender::bke::dyntopo { +void after_stroke(PBVH *pbvh, bool force_balance) { int totnode = pbvh->totnode; @@ -4045,13 +4036,7 @@ void BKE_pbvh_bmesh_after_stroke(PBVH *pbvh, bool force_balance) pbvh_print_mem_size(pbvh); } - -void BKE_pbvh_bmesh_detail_size_set(PBVH *pbvh, float detail_size, float detail_range) -{ - pbvh->bm_detail_range = max_ff(detail_range, 0.1f); - pbvh->bm_max_edge_len = detail_size; - pbvh->bm_min_edge_len = pbvh->bm_max_edge_len * pbvh->bm_detail_range; -} +} // namespace blender::bke::dyntopo void BKE_pbvh_node_mark_topology_update(PBVHNode *node) { diff --git a/source/blender/editors/sculpt_paint/sculpt.cc b/source/blender/editors/sculpt_paint/sculpt.cc index e5dcd1850ff..da9a266bc3c 100644 --- a/source/blender/editors/sculpt_paint/sculpt.cc +++ b/source/blender/editors/sculpt_paint/sculpt.cc @@ -19,6 +19,7 @@ #include "BLI_gsqueue.h" #include "BLI_index_range.hh" #include "BLI_math.h" +#include "BLI_math_vector.hh" #include "BLI_set.hh" #include "BLI_task.h" #include "BLI_task.hh" @@ -40,6 +41,7 @@ #include "BKE_ccg.h" #include "BKE_colortools.h" #include "BKE_context.h" +#include "BKE_dyntopo.hh" #include "BKE_image.h" #include "BKE_key.h" #include "BKE_lib_id.h" @@ -56,7 +58,6 @@ #include "BKE_scene.h" #include "BKE_subdiv_ccg.h" #include "BKE_subsurf.h" -#include "BLI_math_vector.hh" #include "NOD_texture.h" @@ -3871,7 +3872,7 @@ bool SCULPT_dyntopo_automasking_init(const SculptSession *ss, Sculpt *sd, const Brush *br, Object *ob, - DyntopoMaskCB *r_mask_cb, + blender::bke::dyntopo::DyntopoMaskCB *r_mask_cb, void **r_mask_cb_data) { if (!SCULPT_is_automasking_enabled(sd, ss, br)) { @@ -3937,6 +3938,7 @@ static void sculpt_topology_update(Sculpt *sd, UnifiedPaintSettings * /* ups */, PaintModeSettings * /*paint_mode_settings*/) { + using namespace blender::bke::dyntopo; SculptSession *ss = ob->sculpt; /* build brush radius scale */ @@ -3996,7 +3998,7 @@ static void sculpt_topology_update(Sculpt *sd, // mode |= PBVH_Collapse | PBVH_Subdivide; } - int edge_multiply = 1 + int(ss->cached_dyntopo.quality * 10.0f); + int edge_multiply = 1 + int(ss->cached_dyntopo.quality * 50.0f); SculptSearchSphereData sdata{}; sdata.ss = ss, sdata.sd = sd, sdata.ob = ob; @@ -4006,10 +4008,8 @@ static void sculpt_topology_update(Sculpt *sd, sdata.center = nullptr; sdata.brush = brush; - int symidx = SCULPT_get_symmetry_pass(ss); - void *mask_cb_data; - DyntopoMaskCB mask_cb; + blender::bke::dyntopo::DyntopoMaskCB mask_cb; BKE_pbvh_set_bm_log(ss->pbvh, ss->bm_log); @@ -4027,25 +4027,25 @@ static void sculpt_topology_update(Sculpt *sd, actf = BM_idmap_get_id(ss->bm_idmap, (BMElem *)ss->active_face.i); } + blender::bke::dyntopo::BrushSphere sphere_tester(ss->cache->location, ss->cache->radius); + blender::bke::dyntopo::BrushTube tube_tester( + ss->cache->location, ss->cache->view_normal, ss->cache->radius); + /* do nodes under the brush cursor */ - BKE_pbvh_bmesh_update_topology_nodes(ss, - ss->pbvh, - SCULPT_search_sphere_cb, - topology_undopush_cb, - &sdata, - mode, - ss->cache->location, - ss->cache->view_normal, - ss->cache->radius * radius_scale, - (brush->flag & BRUSH_FRONTFACE) != 0, - (brush->falloff_shape != PAINT_FALLOFF_SHAPE_SPHERE), - symidx, - true, - mask_cb, - mask_cb_data, - false, - brush->sculpt_tool == SCULPT_TOOL_SNAKE_HOOK, - edge_multiply); + blender::bke::dyntopo::remesh_topology_nodes( + brush->falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE ? &sphere_tester : &tube_tester, + ss, + ss->pbvh, + SCULPT_search_sphere_cb, + topology_undopush_cb, + &sdata, + mode, + (brush->flag & BRUSH_FRONTFACE) != 0, + ss->cache->view_normal, + true, + mask_cb, + mask_cb_data, + edge_multiply); SCULPT_dyntopo_automasking_end(mask_cb_data); @@ -6275,7 +6275,7 @@ void SCULPT_flush_update_done(const bContext *C, Object *ob, SculptUpdateType up } if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) { - BKE_pbvh_bmesh_after_stroke(ss->pbvh, false); + blender::bke::dyntopo::after_stroke(ss->pbvh, false); } BKE_sculpt_attributes_destroy_temporary_stroke(ob); @@ -6431,17 +6431,18 @@ static void sculpt_stroke_update_step(bContext *C, if (ELEM(ss->cached_dyntopo.mode, DYNTOPO_DETAIL_CONSTANT, DYNTOPO_DETAIL_MANUAL)) { float object_space_constant_detail = 1.0f / (ss->cached_dyntopo.constant_detail * mat4_to_scale(ob->object_to_world)); - BKE_pbvh_bmesh_detail_size_set(ss->pbvh, object_space_constant_detail, 0.4f); + blender::bke::dyntopo::detail_size_set(ss->pbvh, object_space_constant_detail, 0.4f); } else if (ss->cached_dyntopo.mode == DYNTOPO_DETAIL_BRUSH) { - BKE_pbvh_bmesh_detail_size_set( + blender::bke::dyntopo::detail_size_set( ss->pbvh, ss->cache->radius * ss->cached_dyntopo.detail_percent / 100.0f, 0.4f); } else { /* Relative mode. */ - BKE_pbvh_bmesh_detail_size_set(ss->pbvh, - (ss->cache->radius / ss->cache->dyntopo_pixel_radius) * - (ss->cached_dyntopo.detail_size * U.pixelsize) / 0.4f, - 0.4f); + blender::bke::dyntopo::detail_size_set(ss->pbvh, + (ss->cache->radius / ss->cache->dyntopo_pixel_radius) * + (ss->cached_dyntopo.detail_size * U.pixelsize) / + 0.4f, + 0.4f); } float dyntopo_spacing = float(ss->cached_dyntopo.spacing) / 50.0f; diff --git a/source/blender/editors/sculpt_paint/sculpt_detail.cc b/source/blender/editors/sculpt_paint/sculpt_detail.cc index 87f829da350..98c2447f870 100644 --- a/source/blender/editors/sculpt_paint/sculpt_detail.cc +++ b/source/blender/editors/sculpt_paint/sculpt_detail.cc @@ -15,6 +15,7 @@ #include "DNA_mesh_types.h" #include "BKE_context.h" +#include "BKE_dyntopo.hh" #include "BKE_paint.h" #include "BKE_pbvh.h" #include "BKE_screen.h" @@ -44,6 +45,8 @@ #include +using blender::float3; + /* -------------------------------------------------------------------- */ /** \name Internal Utilities * \{ */ @@ -81,11 +84,11 @@ static bool sculpt_and_dynamic_topology_poll(bContext *C) /* -------------------------------------------------------------------- */ /** \name Detail Flood Fill * \{ */ -static int sculpt_detail_flood_fill_run(Object *ob, Sculpt *sd, Brush *brush, wmOperator *op) +static int sculpt_detail_flood_fill_run(Object *ob, Sculpt *sd, Brush *brush, wmOperator * /*op*/) { + using namespace blender::bke::dyntopo; + SculptSession *ss = ob->sculpt; - float size; - float bb_min[3], bb_max[3], center[3], dim[3]; Vector nodes = blender::bke::pbvh::search_gather(ss->pbvh, nullptr, nullptr); @@ -93,36 +96,20 @@ static int sculpt_detail_flood_fill_run(Object *ob, Sculpt *sd, Brush *brush, wm return OPERATOR_CANCELLED; } - /* Get the bounding box, its center and size. */ - BKE_pbvh_bounding_box(ob->sculpt->pbvh, bb_min, bb_max); - add_v3_v3v3(center, bb_min, bb_max); - mul_v3_fl(center, 0.5f); - sub_v3_v3v3(dim, bb_max, bb_min); - size = max_fff(dim[0], dim[1], dim[2]) * 1.1f; - SCULPT_apply_dyntopo_settings(ss, sd, brush); float detail_range = ss->cached_dyntopo.detail_range; /* Update topology size. */ float object_space_constant_detail = 1.0f / (ss->cached_dyntopo.constant_detail * mat4_to_scale(ob->object_to_world)); - BKE_pbvh_bmesh_detail_size_set(ss->pbvh, object_space_constant_detail, detail_range); + blender::bke::dyntopo::detail_size_set(ss->pbvh, object_space_constant_detail, detail_range); BKE_pbvh_set_bm_log(ss->pbvh, ss->bm_log); - bool enable_surface_relax = true; - DyntopoMaskCB mask_cb; void *mask_cb_data; SCULPT_dyntopo_automasking_init(ss, sd, nullptr, ob, &mask_cb, &mask_cb_data); - const int max_steps = 2; - const int max_dyntopo_steps_coll = 1 << 13; - const int max_dyntopo_steps_subd = 1 << 15; - - int i = 0; - bool modified = true; - PBVHTopologyUpdateMode mode = PBVHTopologyUpdateMode(0); if (ss->cached_dyntopo.flag & DYNTOPO_SUBDIVIDE) { mode |= PBVH_Subdivide; @@ -135,6 +122,7 @@ static int sculpt_detail_flood_fill_run(Object *ob, Sculpt *sd, Brush *brush, wm } double time = PIL_check_seconds_timer(); + int edge_step_mul = 1 + int(ss->cached_dyntopo.quality * 100.0f); for (int i = 0; i < 1 + ss->cached_dyntopo.repeat; i++) { nodes = blender::bke::pbvh::search_gather(ss->pbvh, nullptr, nullptr); @@ -143,23 +131,17 @@ static int sculpt_detail_flood_fill_run(Object *ob, Sculpt *sd, Brush *brush, wm BKE_pbvh_node_mark_topology_update(nodes[j]); } - modified = BKE_pbvh_bmesh_update_topology(ss, - ss->pbvh, - mode, - center, - nullptr, - size, - false, - false, - -1, - false, - mask_cb, - mask_cb_data, - max_dyntopo_steps_coll, - !enable_surface_relax, - false, - true, - 10); + blender::bke::dyntopo::BrushNoRadius brush_tester; + remesh_topology(&brush_tester, + ss, + ss->pbvh, + mode, + false, + float3(0.0f, 0.0f, 1.0f), + false, + mask_cb, + mask_cb_data, + edge_step_mul); } time = (PIL_check_seconds_timer() - time) * 1000.0; @@ -167,7 +149,7 @@ static int sculpt_detail_flood_fill_run(Object *ob, Sculpt *sd, Brush *brush, wm SCULPT_dyntopo_automasking_end(mask_cb_data); - BKE_pbvh_bmesh_after_stroke(ss->pbvh, true); + after_stroke(ss->pbvh, true); DEG_id_tag_update(&ob->id, ID_RECALC_SHADING); return OPERATOR_FINISHED; @@ -195,9 +177,6 @@ static int sculpt_detail_flood_fill_exec(bContext *C, wmOperator *op) struct FloodFillJob { wmJob *job; - bool *stop; - bool *do_update; - float *progress; Object *ob; Depsgraph *depsgraph; Scene *scene; @@ -206,20 +185,15 @@ struct FloodFillJob { Brush *brush; Sculpt *sd; }; -FloodFillJob flood_fill_job; -ATTR_NO_OPT static void start_fill_job(void *custom_data, - bool *stop, - bool *do_update, - float *progress) +static FloodFillJob flood_fill_job; + +static void start_fill_job(void * /*custom_data*/, + bool *stop, + bool *do_update, + float * /*progress*/) { - flood_fill_job.stop = stop; - flood_fill_job.do_update = do_update; - flood_fill_job.progress = progress; - - printf("Start fill job.\n"); - - *progress = 0.0f; + printf("Start detail fill job.\n"); while (true) { if (*stop) { @@ -246,17 +220,7 @@ ATTR_NO_OPT static void start_fill_job(void *custom_data, printf("\nJob finished\n\n"); } -static void init_fill_job(void *owner) -{ - printf("Init fill job\n"); -} - -static void update_fill_job(void *owner) -{ - printf("Update fill job\n"); -} - -static void end_fill_job(void *owner) +static void end_fill_job(void *) { SCULPT_undo_push_end(flood_fill_job.ob); @@ -296,8 +260,7 @@ int sculpt_detail_flood_fill_invoke(bContext *C, wmOperator *op, const wmEvent * WM_JOB_PROGRESS, WM_JOB_TYPE_ANY); - WM_jobs_callbacks( - flood_fill_job.job, start_fill_job, init_fill_job, update_fill_job, end_fill_job); + WM_jobs_callbacks(flood_fill_job.job, start_fill_job, nullptr, nullptr, end_fill_job); WM_jobs_timer(flood_fill_job.job, 0.5, NC_OBJECT | ND_DRAW, NC_OBJECT | ND_DRAW); WM_jobs_customdata_set(flood_fill_job.job, &flood_fill_job, flood_fill_free); @@ -312,7 +275,7 @@ int sculpt_detail_flood_fill_invoke(bContext *C, wmOperator *op, const wmEvent * } } -static int sculpt_sample_flood_fill_modal(bContext *C, wmOperator *op, const wmEvent *event) +static int sculpt_sample_flood_fill_modal(bContext *C, wmOperator * /*op*/, const wmEvent *event) { switch (event->type) { case EVT_ESCKEY: diff --git a/source/blender/editors/sculpt_paint/sculpt_face_set.cc b/source/blender/editors/sculpt_paint/sculpt_face_set.cc index c4d16895145..19389bb81da 100644 --- a/source/blender/editors/sculpt_paint/sculpt_face_set.cc +++ b/source/blender/editors/sculpt_paint/sculpt_face_set.cc @@ -1342,7 +1342,7 @@ static void sculpt_face_set_delete_geometry_bmesh(Object *ob, BMesh *bm) ss->totfaces = bm->totface; ss->totvert = bm->totvert; - BKE_pbvh_bmesh_after_stroke(ss->pbvh, true); + blender::bke::dyntopo::after_stroke(ss->pbvh, true); BKE_pbvh_update_bounds(ss->pbvh, PBVH_UpdateBB | PBVH_UpdateOriginalBB); } diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.hh b/source/blender/editors/sculpt_paint/sculpt_intern.hh index 134b9778edc..3f3c7cc1aee 100644 --- a/source/blender/editors/sculpt_paint/sculpt_intern.hh +++ b/source/blender/editors/sculpt_paint/sculpt_intern.hh @@ -15,6 +15,7 @@ #include "DNA_vec_types.h" #include "BKE_attribute.h" +#include "BKE_dyntopo.hh" #include "BKE_paint.h" #include "BKE_pbvh.h" #include "BKE_sculpt.h" @@ -2385,7 +2386,7 @@ bool SCULPT_dyntopo_automasking_init(const SculptSession *ss, Sculpt *sd, const Brush *br, Object *ob, - DyntopoMaskCB *r_mask_cb, + blender::bke::dyntopo::DyntopoMaskCB *r_mask_cb, void **r_mask_cb_data); void SCULPT_dyntopo_automasking_end(void *mask_data); diff --git a/source/blender/editors/sculpt_paint/sculpt_undo.cc b/source/blender/editors/sculpt_paint/sculpt_undo.cc index d6de0c2e82a..0a35a5302ba 100644 --- a/source/blender/editors/sculpt_paint/sculpt_undo.cc +++ b/source/blender/editors/sculpt_paint/sculpt_undo.cc @@ -1026,7 +1026,7 @@ static void sculpt_undo_bmesh_restore_generic(SculptUndoNode *unode, Object *ob, BKE_pbvh_update_bounds(ss->pbvh, PBVH_UpdateBB | PBVH_UpdateOriginalBB | PBVH_UpdateRedraw); if (data.balance_pbvh) { - BKE_pbvh_bmesh_after_stroke(ss->pbvh, true); + blender::bke::dyntopo::after_stroke(ss->pbvh, true); } // pbvh_bmesh_check_nodes(ss->pbvh); -- 2.30.2 From 1d572e238bbf98824b93ac389588bd2d62937fbe Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Sun, 21 May 2023 18:34:49 -0700 Subject: [PATCH 113/279] temp-sculpt-dyntopo: Remove unused enum --- source/blender/makesdna/DNA_brush_enums.h | 1 - source/blender/makesrna/intern/rna_brush.c | 1 - 2 files changed, 2 deletions(-) diff --git a/source/blender/makesdna/DNA_brush_enums.h b/source/blender/makesdna/DNA_brush_enums.h index f660cc70be4..4c8b672b8a7 100644 --- a/source/blender/makesdna/DNA_brush_enums.h +++ b/source/blender/makesdna/DNA_brush_enums.h @@ -254,7 +254,6 @@ typedef enum eBrushPoseDeformType { BRUSH_POSE_DEFORM_ROTATE_TWIST = 0, BRUSH_POSE_DEFORM_SCALE_TRASLATE = 1, BRUSH_POSE_DEFORM_SQUASH_STRETCH = 2, - BRUSH_POSE_DEFORM_BEND = 3, } eBrushPoseDeformType; typedef enum eBrushPoseOriginType { diff --git a/source/blender/makesrna/intern/rna_brush.c b/source/blender/makesrna/intern/rna_brush.c index 7c0255bd371..80f9dcf8640 100644 --- a/source/blender/makesrna/intern/rna_brush.c +++ b/source/blender/makesrna/intern/rna_brush.c @@ -2663,7 +2663,6 @@ static void rna_def_brush(BlenderRNA *brna) {BRUSH_POSE_DEFORM_ROTATE_TWIST, "ROTATE_TWIST", 0, "Rotate/Twist", ""}, {BRUSH_POSE_DEFORM_SCALE_TRASLATE, "SCALE_TRANSLATE", 0, "Scale/Translate", ""}, {BRUSH_POSE_DEFORM_SQUASH_STRETCH, "SQUASH_STRETCH", 0, "Squash & Stretch", ""}, - {BRUSH_POSE_DEFORM_BEND, "BEND", 0, "Bend", ""}, {0, NULL, 0, NULL, NULL}, }; -- 2.30.2 From 70acf884dd2e5475240353b3a9ee3966cc22661d Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Mon, 22 May 2023 12:03:38 -0700 Subject: [PATCH 114/279] temp-sculpt-dyntopo: Use eigen decomposer from Eigen for curvature rake Pun not intended. --- .../sculpt_paint/sculpt_brush_types.cc | 4 +- .../editors/sculpt_paint/sculpt_curvature.cc | 43 ++++++++++++------- 2 files changed, 30 insertions(+), 17 deletions(-) diff --git a/source/blender/editors/sculpt_paint/sculpt_brush_types.cc b/source/blender/editors/sculpt_paint/sculpt_brush_types.cc index c4e2cc42ab9..b61d7ea2f90 100644 --- a/source/blender/editors/sculpt_paint/sculpt_brush_types.cc +++ b/source/blender/editors/sculpt_paint/sculpt_brush_types.cc @@ -2836,7 +2836,7 @@ static void update_curvatures_task_cb_ex(void *__restrict userdata, } if (brush->flag2 & BRUSH_CURVATURE_RAKE) { - SCULPT_curvature_begin(ss, data->nodes[n], false); + SCULPT_curvature_begin(ss, data->nodes[n], true); } } @@ -2886,7 +2886,7 @@ static void do_topology_rake_bmesh_task_cb_ex(void *__restrict userdata, float direction2[3]; if (use_curvature) { - SCULPT_curvature_dir_get(ss, vd.vertex, direction2, false); + SCULPT_curvature_dir_get(ss, vd.vertex, direction2, true); } else { copy_v3_v3(direction2, direction); diff --git a/source/blender/editors/sculpt_paint/sculpt_curvature.cc b/source/blender/editors/sculpt_paint/sculpt_curvature.cc index 84d36c993c4..9fc7763aff8 100644 --- a/source/blender/editors/sculpt_paint/sculpt_curvature.cc +++ b/source/blender/editors/sculpt_paint/sculpt_curvature.cc @@ -141,10 +141,10 @@ BLI_INLINE void normal_covariance(float mat[3][3], float no[3]) mat[2][2] = no[2] * no[2]; } -bool SCULPT_calc_principle_curvatures(SculptSession *ss, - PBVHVertRef vertex, - SculptCurvatureData *out, - bool useAccurateSolver) +ATTR_NO_OPT bool SCULPT_calc_principle_curvatures(SculptSession *ss, + PBVHVertRef vertex, + SculptCurvatureData *out, + bool useAccurateSolver) { SculptVertexNeighborIter ni; float nmat[3][3], nmat2[3][3]; @@ -155,7 +155,7 @@ bool SCULPT_calc_principle_curvatures(SculptSession *ss, SCULPT_vertex_normal_get(ss, vertex, no); normal_covariance(nmat, no); - if (useAccurateSolver) { +#if 0 int val = SCULPT_vertex_valence_get(ss, vertex); float *ws = (float *)BLI_array_alloca(ws, val); float *cot1 = (float *)BLI_array_alloca(cot1, val); @@ -173,17 +173,30 @@ bool SCULPT_calc_principle_curvatures(SculptSession *ss, madd_m3_m3m3fl(nmat, nmat, nmat2, ws[ni.i]); } SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); - } - else { - SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vertex, ni) { - SCULPT_vertex_normal_get(ss, ni.vertex, no2); - sub_v3_v3(no2, no); +#else + /* TODO: review the math here. We're deriving the curvature + * via an eigen decomposition of the weighted summed + * normal covariance matrices of the surrounding topology. + */ + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vertex, ni) { + SCULPT_vertex_normal_get(ss, ni.vertex, no2); + sub_v3_v3(no2, no); - normal_covariance(nmat2, no2); - add_m3_m3m3(nmat, nmat, nmat2); + SculptVertexNeighborIter ni2; + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, ni.vertex, ni2) { + float no3[3]; + SCULPT_vertex_normal_get(ss, ni2.vertex, no3); + + normal_covariance(nmat2, no3); + madd_m3_m3m3fl(nmat, nmat, nmat2, 1.0f / ni2.size); } - SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); + SCULPT_VERTEX_NEIGHBORS_ITER_END(ni2); + + normal_covariance(nmat2, no2); + madd_m3_m3m3fl(nmat, nmat, nmat2, 1.0f / ni.size); } + SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); +#endif if (!useAccurateSolver || !BLI_eigen_solve_selfadjoint_m3(nmat, out->ks, out->principle)) { /* Do simple power solve in one direction. */ @@ -194,11 +207,11 @@ bool SCULPT_calc_principle_curvatures(SculptSession *ss, SCULPT_vertex_normal_get(ss, vertex, no); copy_v3_v3(t, no); - for (int i = 0; i < 15; i++) { + for (int i = 0; i < 25; i++) { if (i > 0) { normalize_v3(t); - if (i > 1 && len_squared_v3v3(t, t2) < 0.0001) { + if (i > 5 && len_squared_v3v3(t, t2) < 0.000001f) { break; } -- 2.30.2 From fe647b9e330b845df700af216fa9cb7ea2ba848c Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Mon, 22 May 2023 12:10:24 -0700 Subject: [PATCH 115/279] temp-sculpt-dyntopo: Fix brush resetting not affecting dyntopo settings --- source/blender/blenkernel/intern/brush.cc | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/source/blender/blenkernel/intern/brush.cc b/source/blender/blenkernel/intern/brush.cc index 1b9caee44c7..6c2120b957b 100644 --- a/source/blender/blenkernel/intern/brush.cc +++ b/source/blender/blenkernel/intern/brush.cc @@ -1791,6 +1791,8 @@ void BKE_brush_sculpt_reset(Brush *br) brush_defaults(br); BKE_brush_curve_preset(br, CURVE_PRESET_SMOOTH); + br->dyntopo = *DNA_struct_default_get(DynTopoSettings); + /* Use the curve presets by default */ br->curve_preset = BRUSH_CURVE_SMOOTH; @@ -1974,8 +1976,8 @@ void BKE_brush_sculpt_reset(Brush *br) br->curve_preset = BRUSH_CURVE_SMOOTHER; break; case SCULPT_TOOL_SIMPLIFY: - br->dyntopo.inherit = ~(DYNTOPO_INHERIT_COLLAPSE | DYNTOPO_INHERIT_SUBDIVIDE); - br->dyntopo.flag |= DYNTOPO_COLLAPSE | DYNTOPO_SUBDIVIDE; + br->dyntopo.inherit = ~(DYNTOPO_INHERIT_COLLAPSE | DYNTOPO_INHERIT_SUBDIVIDE | DYNTOPO_INHERIT_CLEANUP); + br->dyntopo.flag |= DYNTOPO_COLLAPSE | DYNTOPO_SUBDIVIDE | DYNTOPO_CLEANUP; break; case SCULPT_TOOL_MASK: disable_dyntopo = true; @@ -2067,7 +2069,6 @@ void BKE_brush_sculpt_reset(Brush *br) } if (disable_dyntopo) { - // disabled flag is never inherited br->dyntopo.flag |= DYNTOPO_DISABLED; } } -- 2.30.2 From 57b1ad98099897898dc5d016a4bf09e2755aa519 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Mon, 22 May 2023 14:09:04 -0700 Subject: [PATCH 116/279] temp-sculpt-dyntopo: Improve dyntopo convergence --- source/blender/blenkernel/BKE_dyntopo.hh | 1 + source/blender/blenkernel/intern/brush.cc | 8 +- source/blender/blenkernel/intern/dyntopo.cc | 282 ++++++++++-------- .../blenkernel/intern/dyntopo_intern.hh | 9 +- 4 files changed, 161 insertions(+), 139 deletions(-) diff --git a/source/blender/blenkernel/BKE_dyntopo.hh b/source/blender/blenkernel/BKE_dyntopo.hh index af480ce3206..ac7cca012dd 100644 --- a/source/blender/blenkernel/BKE_dyntopo.hh +++ b/source/blender/blenkernel/BKE_dyntopo.hh @@ -122,6 +122,7 @@ struct BrushNoRadius : public BrushTester { typedef float (*DyntopoMaskCB)(PBVHVertRef vertex, void *userdata); enum PBVHTopologyUpdateMode { + PBVH_None = 0, PBVH_Subdivide = 1 << 0, PBVH_Collapse = 1 << 1, PBVH_Cleanup = 1 << 2, // dissolve verts surrounded by either 3 or 4 triangles then triangulate diff --git a/source/blender/blenkernel/intern/brush.cc b/source/blender/blenkernel/intern/brush.cc index 6c2120b957b..1ad11c05e3b 100644 --- a/source/blender/blenkernel/intern/brush.cc +++ b/source/blender/blenkernel/intern/brush.cc @@ -1883,8 +1883,9 @@ void BKE_brush_sculpt_reset(Brush *br) br->rake_factor = 1.0f; br->dyntopo.inherit = ~(DYNTOPO_INHERIT_SPACING | DYNTOPO_INHERIT_SUBDIVIDE | DYNTOPO_INHERIT_COLLAPSE | DYNTOPO_INHERIT_DETAIL_RANGE | - DYNTOPO_INHERIT_RADIUS_SCALE | DYNTOPO_INHERIT_REPEAT); - br->dyntopo.flag |= DYNTOPO_SUBDIVIDE | DYNTOPO_COLLAPSE; + DYNTOPO_INHERIT_RADIUS_SCALE | DYNTOPO_INHERIT_REPEAT | + DYNTOPO_INHERIT_CLEANUP); + br->dyntopo.flag |= DYNTOPO_SUBDIVIDE | DYNTOPO_COLLAPSE | DYNTOPO_CLEANUP; br->dyntopo.detail_range = 0.4f; br->dyntopo.spacing = 0; br->dyntopo.radius_scale = 1.25; @@ -1976,7 +1977,8 @@ void BKE_brush_sculpt_reset(Brush *br) br->curve_preset = BRUSH_CURVE_SMOOTHER; break; case SCULPT_TOOL_SIMPLIFY: - br->dyntopo.inherit = ~(DYNTOPO_INHERIT_COLLAPSE | DYNTOPO_INHERIT_SUBDIVIDE | DYNTOPO_INHERIT_CLEANUP); + br->dyntopo.inherit = ~(DYNTOPO_INHERIT_COLLAPSE | DYNTOPO_INHERIT_SUBDIVIDE | + DYNTOPO_INHERIT_CLEANUP); br->dyntopo.flag |= DYNTOPO_COLLAPSE | DYNTOPO_SUBDIVIDE | DYNTOPO_CLEANUP; break; case SCULPT_TOOL_MASK: diff --git a/source/blender/blenkernel/intern/dyntopo.cc b/source/blender/blenkernel/intern/dyntopo.cc index 70defef5ed9..436de26ac4e 100644 --- a/source/blender/blenkernel/intern/dyntopo.cc +++ b/source/blender/blenkernel/intern/dyntopo.cc @@ -74,7 +74,6 @@ void bm_kill_only_loop(BMesh *bm, BMLoop *l); void bm_kill_only_face(BMesh *bm, BMFace *f); } -static bool edge_queue_test(struct EdgeQueueContext *eq_ctx, PBVH *pbvh, BMEdge *e); static void edge_queue_create_local(EdgeQueueContext *eq_ctx, PBVH *pbvh, PBVHTopologyUpdateMode local_mode); @@ -229,27 +228,57 @@ static float maskcb_get(EdgeQueueContext *eq_ctx, BMVert *v1, BMVert *v2) return 1.0f; } -BLI_INLINE float calc_edge_length(EdgeQueueContext * /*eq_ctx*/, BMVert *v1, BMVert *v2) -{ - return len_squared_v3v3(v1->co, v2->co); -} +enum WeightMode { + SPLIT = -1, + COLLAPSE = 1, +}; -BLI_INLINE float calc_weighted_length(EdgeQueueContext *eq_ctx, BMVert *v1, BMVert *v2, float sign) +BLI_INLINE float calc_weighted_length(EdgeQueueContext *eq_ctx, + BMVert *v1, + BMVert *v2, + WeightMode mode) { float w = 1.0 - maskcb_get(eq_ctx, v1, v2); float len = len_squared_v3v3(v1->co, v2->co); - w = 1.0 + w * sign; + w = 1.0 + w * float(mode); return len * w; } -static void edge_queue_insert_unified(EdgeQueueContext *eq_ctx, BMEdge *e) +static PBVHTopologyUpdateMode edge_queue_test(EdgeQueueContext *eq_ctx, + PBVH * /*pbvh*/, + BMEdge *e, + float *r_w) +{ + float len1 = calc_weighted_length(eq_ctx, e->v1, e->v2, SPLIT); + float max = eq_ctx->limit_len_max_sqr; + + if ((eq_ctx->mode & PBVH_Subdivide) && len1 > max) { + if (r_w) { + *r_w = len1; + } + return PBVH_Subdivide; + } + + float len2 = calc_weighted_length(eq_ctx, e->v1, e->v2, COLLAPSE); + float min = eq_ctx->limit_len_min_sqr; + + if ((eq_ctx->mode & PBVH_Collapse) && len2 < min) { + if (r_w) { + *r_w = len2; + } + return PBVH_Collapse; + } + + return PBVH_None; +} + +static void edge_queue_insert_unified(EdgeQueueContext *eq_ctx, BMEdge *e, float w) { if (/*BLI_mm_heap_len(eq_ctx->heap_mm) < eq_ctx->max_heap_mm &&*/ !(e->head.hflag & BM_ELEM_TAG)) { float lensqr = len_squared_v3v3(e->v1->co, e->v2->co); - float len = sqrtf(lensqr); eq_ctx->avg_elen += len; @@ -259,7 +288,7 @@ static void edge_queue_insert_unified(EdgeQueueContext *eq_ctx, BMEdge *e) // lensqr += (BLI_thread_frand(0) - 0.5f) * 0.1 * eq_ctx->limit_mid; - eq_ctx->edge_heap.insert(lensqr, e); + eq_ctx->edge_heap.insert(w, e); e->head.hflag |= BM_ELEM_TAG; } } @@ -445,7 +474,7 @@ static void long_edge_queue_edge_add_recursive( } } - edge_queue_insert_unified(eq_ctx, l_edge->e); + edge_queue_insert_unified(eq_ctx, l_edge->e, len_sq); if ((l_edge->radial_next != l_edge)) { const float len_sq_cmp = len_sq * EVEN_EDGELEN_THRESHOLD; @@ -462,7 +491,7 @@ static void long_edge_queue_edge_add_recursive( } float len_sq_other = calc_weighted_length( - eq_ctx, l_adjacent[i]->e->v1, l_adjacent[i]->e->v2, -1.0f); + eq_ctx, l_adjacent[i]->e->v1, l_adjacent[i]->e->v2, SPLIT); bool insert_ok = len_sq_other > max_ff(len_sq_cmp, limit_len_sq); if (!insert_ok) { @@ -524,8 +553,8 @@ static void long_edge_queue_edge_add_recursive_2(EdgeQueueThreadData *tdata, do { BMLoop *l_adjacent[2] = {l_iter->next, l_iter->prev}; for (int i = 0; i < (int)ARRAY_SIZE(l_adjacent); i++) { - float len_sq_other = calc_edge_length( - tdata->eq_ctx, l_adjacent[i]->e->v1, l_adjacent[i]->e->v2); + float len_sq_other = calc_weighted_length( + tdata->eq_ctx, l_adjacent[i]->e->v1, l_adjacent[i]->e->v2, SPLIT); bool insert_ok = len_sq_other > max_ff(len_sq_cmp, limit_len_sq); #ifdef EVEN_NO_TEST_DEPTH_LIMIT @@ -662,15 +691,15 @@ static void unified_edge_queue_task_cb(void *__restrict userdata, reproject_cdata); } - float len_sq = calc_edge_length(eq_ctx, l_iter->e->v1, l_iter->e->v2); + float w = 0.0f; + PBVHTopologyUpdateMode mode = edge_queue_test(eq_ctx, pbvh, l_iter->e, &w); /* Subdivide walks the mesh a bit for better transitions in the topology. */ - if ((eq_ctx->mode & PBVH_Subdivide) && (len_sq > eq_ctx->limit_len_max_sqr)) { + if (mode == PBVH_Subdivide) { long_edge_queue_edge_add_recursive_2( - tdata, l_iter->radial_next, l_iter, len_sq, eq_ctx->limit_len_max, 0, true); + tdata, l_iter->radial_next, l_iter, w, eq_ctx->limit_len_max, 0, true); } - - if (edge_queue_test(eq_ctx, pbvh, l_iter->e)) { + else if (mode == PBVH_Collapse) { edge_thread_data_insert(tdata, l_iter->e); } } while ((l_iter = l_iter->next) != l_first); @@ -1085,25 +1114,6 @@ bool check_vert_fan_are_tris(PBVH *pbvh, BMVert *v) return false; } -static bool edge_queue_test(EdgeQueueContext *eq_ctx, PBVH * /*pbvh*/, BMEdge *e) -{ - float len = len_squared_v3v3(e->v1->co, e->v2->co); - float min = eq_ctx->limit_len_min_sqr; - float max = eq_ctx->limit_len_max_sqr; - - bool ret = false; - - if (eq_ctx->mode & PBVH_Subdivide) { - ret |= len > max; - } - - if (eq_ctx->mode & PBVH_Collapse) { - ret |= len < min; - } - - return ret; -} - /* Create a priority queue containing vertex pairs connected by a long * edge as defined by PBVH.bm_max_edge_len. * @@ -1231,11 +1241,10 @@ static void unified_edge_queue_create(EdgeQueueContext *eq_ctx, e->v1->head.hflag |= BM_ELEM_TAG; e->v2->head.hflag |= BM_ELEM_TAG; - if (edge_queue_test(eq_ctx, pbvh, e)) { - edge_queue_insert_unified(eq_ctx, e); + float w; + if (edge_queue_test(eq_ctx, pbvh, e, &w)) { + edge_queue_insert_unified(eq_ctx, e, w); } - - // edge_queue_insert(eq_ctx, e, w, eq_ctx->limit_len); } MEM_SAFE_FREE(td->edges); @@ -1500,24 +1509,29 @@ static void edge_queue_create_local(EdgeQueueContext *eq_ctx, bool a = eq_ctx->mode & (PBVH_Subdivide | PBVH_LocalSubdivide); bool b = eq_ctx->mode & (PBVH_Collapse | PBVH_LocalCollapse); - float len = len_squared_v3v3(e->v1->co, e->v2->co); + float len1 = calc_weighted_length(eq_ctx, e->v1, e->v2, COLLAPSE); + float len2 = calc_weighted_length(eq_ctx, e->v1, e->v2, SPLIT); + float w = 0.0f; if (a && b) { - ok = len < eq_ctx->limit_len_min_sqr || len > eq_ctx->limit_len_max_sqr; - ok = ok || (len < pbvh->bm_min_edge_len || len > pbvh->bm_max_edge_len); + ok = len1 < eq_ctx->limit_len_min_sqr || len1 > eq_ctx->limit_len_max_sqr; + ok = ok || (len2 < pbvh->bm_min_edge_len || len2 > pbvh->bm_max_edge_len); + w = (len1 + len2) * 0.5; } else if (a) { - ok = len > eq_ctx->limit_len_max || len > pbvh->bm_max_edge_len; + ok = len1 > eq_ctx->limit_len_max || len1 > pbvh->bm_max_edge_len; + w = len1; } else if (b) { - ok = len < eq_ctx->limit_len_min || len < pbvh->bm_min_edge_len; + ok = len2 < eq_ctx->limit_len_min || len2 < pbvh->bm_min_edge_len; + w = len2; } if (!ok) { continue; } - edge_queue_insert_unified(eq_ctx, e); + edge_queue_insert_unified(eq_ctx, e, w); } } @@ -1590,7 +1604,7 @@ static bool cleanup_valence_3_4(EdgeQueueContext *ectx, PBVH *pbvh) */ BMEdge *e = v->e; do { - float len = calc_weighted_length(ectx, e->v1, e->v2, 1); + float len = calc_weighted_length(ectx, e->v1, e->v2, COLLAPSE); if (sqrtf(len) > ectx->limit_len_max * 1.2f) { bad = true; break; @@ -2036,9 +2050,8 @@ bool remesh_topology(BrushTester *brush_tester, ops[1] = PBVH_Collapse; totop = 2; - steps[1] = DYNTOPO_MAX_ITER / 3.0; - steps[0] = DYNTOPO_MAX_ITER_SUBD; // DYNTOPO_MAX_ITER <= steps[1] ? DYNTOPO_MAX_ITER : - // DYNTOPO_MAX_ITER - steps[1]; + steps[1] = DYNTOPO_MAX_ITER_COLLAPSE; + steps[0] = DYNTOPO_MAX_ITER_SUBD; } else if (mode & PBVH_Subdivide) { ops[0] = PBVH_Subdivide; @@ -2050,21 +2063,16 @@ bool remesh_topology(BrushTester *brush_tester, ops[0] = PBVH_Collapse; totop = 1; - steps[0] = DYNTOPO_MAX_ITER; + steps[0] = DYNTOPO_MAX_ITER_COLLAPSE; } - printf("%s: edge_limit_multiply: %d\n", __func__, edge_limit_multiply); - - steps[0] *= edge_limit_multiply; - steps[1] *= edge_limit_multiply; - int max_steps = (DYNTOPO_MAX_ITER * edge_limit_multiply) << (totop - 1); int max_subd = max_steps >> (totop - 1); int edges_size = steps[0]; BMEdge **edges = (BMEdge **)MEM_malloc_arrayN(edges_size, sizeof(void *), __func__); int etot = 0; - int count = 0, count_subd = 0; //, count_cold = 0; + int count = 0, count_subd = 0; int i = 0; int curop = 0; @@ -2074,6 +2082,19 @@ bool remesh_topology(BrushTester *brush_tester, SmallHash subd_edges; BLI_smallhash_init(&subd_edges); + auto flush_subdivision = [&]() { + if (etot == 0) { + return; + } + + modified = true; + BLI_smallhash_clear(&subd_edges, 0); + pbvh_split_edges(&eq_ctx, pbvh, pbvh->header.bm, edges, etot, false); + count_subd += etot; + VALIDATE_LOG(pbvh->bm_log); + etot = 0; + }; + while (totop > 0 && !eq_ctx.edge_heap.empty() && i < max_steps) { BMEdge *e = nullptr; @@ -2081,12 +2102,7 @@ bool remesh_topology(BrushTester *brush_tester, // BM_log_entry_add_ex(pbvh->header.bm, pbvh->bm_log, true); if (ops[curop] == PBVH_Subdivide && count_subd < max_subd) { - modified = true; - BLI_smallhash_clear(&subd_edges, 0); - pbvh_split_edges(&eq_ctx, pbvh, pbvh->header.bm, edges, etot, false); - count_subd += etot; - VALIDATE_LOG(pbvh->bm_log); - etot = 0; + flush_subdivision(); } curop = (curop + 1) % totop; @@ -2108,7 +2124,7 @@ bool remesh_topology(BrushTester *brush_tester, e = eq_ctx.edge_heap.pop_max(); while (!eq_ctx.edge_heap.empty() && e && (bm_elem_is_free((BMElem *)e, BM_EDGE) || - calc_weighted_length(&eq_ctx, e->v1, e->v2, -1.0) < limit_len_subd)) + calc_weighted_length(&eq_ctx, e->v1, e->v2, SPLIT) < limit_len_subd)) { e = eq_ctx.edge_heap.pop_max(); } @@ -2148,7 +2164,7 @@ bool remesh_topology(BrushTester *brush_tester, e = eq_ctx.edge_heap.pop_min(); while (!eq_ctx.edge_heap.empty() && e && (bm_elem_is_free((BMElem *)e, BM_EDGE) || - calc_weighted_length(&eq_ctx, e->v1, e->v2, 1.0) > limit_len_cold)) + calc_weighted_length(&eq_ctx, e->v1, e->v2, COLLAPSE) > limit_len_cold)) { e = eq_ctx.edge_heap.pop_min(); } @@ -2170,8 +2186,6 @@ bool remesh_topology(BrushTester *brush_tester, modified = true; pbvh_bmesh_collapse_edge(pbvh, e, e->v1, e->v2, nullptr, nullptr, &eq_ctx); VALIDATE_LOG(pbvh->bm_log); - - // count_cold++; break; } case PBVH_LocalSubdivide: @@ -2181,41 +2195,17 @@ bool remesh_topology(BrushTester *brush_tester, break; } + if (eq_ctx.edge_heap.empty() && etot > 0) { + /* Flush subdivision, it may add more to queue.*/ + flush_subdivision(); + } + count++; i++; } -#if 0 - { - printf("cd_faceset_offset: %d\n", pbvh->cd_faceset_offset); - CustomData *data = &pbvh->header.bm->pdata; - for (int i = 0; i < data->totlayer; i++) { - CustomDataLayer *layer = &data->layers[i]; - printf(" %s \"%s\" offset=%d flag=%d\n", - CustomData_layertype_name(eCustomDataType(layer->type)), - layer->name, - layer->offset, - layer->flag); - } - printf("\n"); - } -#endif - -#if 0 - printf("%s: subd: %d, cold: %d, ratio: %.3f\n", - __func__, - count_subd, - count_cold, - count_cold > 0 ? float(count_subd) / float(count_cold) : 0.0f); -#endif - if (etot > 0) { - modified = true; - BLI_smallhash_clear(&subd_edges, 0); - // BM_log_entry_add_ex(pbvh->header.bm, pbvh->bm_log, true); - pbvh_split_edges(&eq_ctx, pbvh, pbvh->header.bm, edges, etot, false); - VALIDATE_LOG(pbvh->bm_log); - etot = 0; + flush_subdivision(); } MEM_SAFE_FREE(edges); @@ -2433,9 +2423,39 @@ static void pbvh_split_edges(EdgeQueueContext *eq_ctx, bm_logstack_push(); bm_log_message(" == split edges == "); + /* Try to improve quality by inserting new edge into queue. + * This is a bit tricky since we don't want to expand outside + * the brush radius too much, but we can't stay strictly inside + * either. + */ + + auto test_near_brush = [&](BMEdge *e, float *co, float *r_w = nullptr) { + float w = calc_weighted_length(eq_ctx, e->v1, e->v2, SPLIT); + if (r_w) { + *r_w = w; + } + + if (eq_ctx->brush_tester->is_sphere_or_tube) { + BrushSphere *sphere_test = static_cast(eq_ctx->brush_tester); + + float len = len_v3v3(co, sphere_test->center()); + float fac = 1.0f + powf((len / sphere_test->radius()), 5.0); + + if (len < sphere_test->radius() && w > eq_ctx->limit_len_max_sqr) { + return true; + } + + return (w * 2.0) / fac > eq_ctx->limit_len_max_sqr; + } + + return true; + }; + BMEdge **edges = edges1; Vector faces; +#define SUBD_ADD_TO_QUEUE + const int node_updateflag = PBVH_UpdateBB | PBVH_UpdateOriginalBB | PBVH_UpdateNormals | PBVH_UpdateOtherVerts | PBVH_UpdateCurvatureDir | PBVH_UpdateTriAreas | PBVH_UpdateDrawBuffers | @@ -2595,7 +2615,6 @@ static void pbvh_split_edges(EdgeQueueContext *eq_ctx, BM_idmap_release(pbvh->bm_idmap, (BMElem *)e, true); - // BMVert *newv = BM_log_edge_split_do(pbvh->bm_log, e, e->v1, &newe, 0.5f); BMVert *newv = BM_edge_split(pbvh->header.bm, e, e->v1, &newe, 0.5f); newe->head.hflag &= ~SPLIT_TAG; @@ -2605,20 +2624,36 @@ static void pbvh_split_edges(EdgeQueueContext *eq_ctx, edge_queue_insert_val34_vert(eq_ctx, newv); -#if 0 - const float elimit = eq_ctx->limit_len_max; +#ifdef SUBD_ADD_TO_QUEUE + if (e->l) { + float w1 = 0.0f, w2 = 0.0f; + PBVHTopologyUpdateMode mode; - if (0 && e->l) { - e->head.hflag &= ~BM_ELEM_TAG; - newe->head.hflag &= ~BM_ELEM_TAG; + bool ok = false; +# if 0 + ok = eq_ctx->brush_tester->vert_in_range(e->v1) || + eq_ctx->brush_tester->vert_in_range(e->v2) || + eq_ctx->brush_tester->vert_in_range(newe->v1) || + eq_ctx->brush_tester->vert_in_range(newe->v2); +# else + ok = test_near_brush(e, v1->co); + ok |= test_near_brush(e, v2->co); + ok |= test_near_brush(newe, newv->co); +# endif - long_edge_queue_edge_add_recursive( - eq_ctx, e->l->radial_next, len_squared_v3v3(e->v1->co, e->v2->co), elimit, 0); - long_edge_queue_edge_add_recursive(eq_ctx, - newe->l->radial_next, - len_squared_v3v3(newe->v1->co, newe->v2->co), - elimit, - 0); + if (ok) { + float w1 = 0.0f, w2 = 0.0f; + + if ((mode = edge_queue_test(eq_ctx, pbvh, e, &w1)) != PBVH_None) { + e->head.hflag &= ~BM_ELEM_TAG; + edge_queue_insert_unified(eq_ctx, e, w1); + } + + if ((mode = edge_queue_test(eq_ctx, pbvh, newe, &w2)) != PBVH_None) { + newe->head.hflag &= ~BM_ELEM_TAG; + edge_queue_insert_unified(eq_ctx, newe, w2); + } + } } #endif @@ -2840,29 +2875,12 @@ static void pbvh_split_edges(EdgeQueueContext *eq_ctx, if (newf) { rl->e->head.hflag &= ~BM_ELEM_TAG; -#if 1 - /* Try to improve quality by inserting new edge into queue. - * This is a bit tricky since we don't want to expand outside - * the brush radius too much, but we can't stay strictly inside - * either. - */ - +#ifdef SUBD_ADD_TO_QUEUE float3 co = rl->v->co; co = (co + rl->next->v->co) * 0.5f; - float w = calc_weighted_length(eq_ctx, rl->e->v1, rl->e->v2, -1.0f); - if (eq_ctx->brush_tester->is_sphere_or_tube) { - BrushSphere *sphere_test = static_cast(eq_ctx->brush_tester); - - float len = len_v3v3(co, sphere_test->center()); - float fac = 1.0f + 2.0 * (len / sphere_test->radius()); - - if (w / fac > eq_ctx->limit_len_max) { - - long_edge_queue_edge_add_recursive(eq_ctx, rl, w, eq_ctx->limit_len_max, 0); - } - } - else { + float w = 0.0f; + if (test_near_brush(rl->e, co, &w)) { long_edge_queue_edge_add_recursive(eq_ctx, rl, w, eq_ctx->limit_len_max, 0); } #endif diff --git a/source/blender/blenkernel/intern/dyntopo_intern.hh b/source/blender/blenkernel/intern/dyntopo_intern.hh index 0ebd605839f..6a14de2e4b0 100644 --- a/source/blender/blenkernel/intern/dyntopo_intern.hh +++ b/source/blender/blenkernel/intern/dyntopo_intern.hh @@ -99,8 +99,9 @@ inline bool bm_elem_is_free(BMElem *elem, int htype) (SCULPT_CORNER_MESH | SCULPT_CORNER_FACE_SET | SCULPT_CORNER_SHARP_MARK | SCULPT_CORNER_SEAM | \ SCULPT_CORNER_UV | SCULPT_CORNER_SHARP_ANGLE) -#define DYNTOPO_MAX_ITER 256 -#define DYNTOPO_MAX_ITER_SUBD 256 +#define DYNTOPO_MAX_ITER 512 +#define DYNTOPO_MAX_ITER_COLLAPSE 512 +#define DYNTOPO_MAX_ITER_SUBD 512 #define DYNTOPO_USE_HEAP #define DYNTOPO_USE_MINMAX_HEAP @@ -118,10 +119,10 @@ inline bool bm_elem_is_free(BMElem *elem, int htype) #define EVEN_EDGELEN_THRESHOLD 1.2f /* How much the limit increases per recursion * (avoids performing subdivisions too far away). */ -#define EVEN_GENERATION_SCALE 1.25f +#define EVEN_GENERATION_SCALE 1.05f /* recursion depth to start applying front face test */ -#define DEPTH_START_LIMIT 5 +#define DEPTH_START_LIMIT 4 //#define FANCY_EDGE_WEIGHTS <= too slow //#define SKINNY_EDGE_FIX -- 2.30.2 From 3e9493937a5ca851dac7bc4edd984d988c40ab2e Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Mon, 22 May 2023 14:22:58 -0700 Subject: [PATCH 117/279] temp-sculpt-dyntopo: Fix memory leak --- source/blender/bmesh/intern/bmesh_idmap.cc | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/source/blender/bmesh/intern/bmesh_idmap.cc b/source/blender/bmesh/intern/bmesh_idmap.cc index a4ae795a396..63de78b1243 100644 --- a/source/blender/bmesh/intern/bmesh_idmap.cc +++ b/source/blender/bmesh/intern/bmesh_idmap.cc @@ -207,7 +207,7 @@ void BM_idmap_check_ids(BMIdMap *idmap) idmap->freelist.clear(); if (idmap->free_idx_map) { - MEM_delete(idmap->free_idx_map); + MEM_delete(idmap->free_idx_map); idmap->free_idx_map = nullptr; } @@ -362,8 +362,12 @@ void BM_idmap_destroy(BMIdMap *idmap) delete idmap->id2elem; #endif + if (idmap->free_idx_map) { + MEM_delete(idmap->free_idx_map); + } + MEM_SAFE_FREE(idmap->map); - MEM_delete(idmap); + MEM_delete(idmap); } static void check_idx_map(BMIdMap *idmap) @@ -371,7 +375,7 @@ static void check_idx_map(BMIdMap *idmap) if (idmap->free_idx_map && idmap->freelist.size() < FREELIST_HASHMAP_THRESHOLD_LOW) { // idmap_log_message("%s: Deleting free_idx_map\n", __func__); - MEM_delete(idmap->free_idx_map); + MEM_delete(idmap->free_idx_map); idmap->free_idx_map = nullptr; } else if (!idmap->free_idx_map && idmap->freelist.size() < FREELIST_HASHMAP_THRESHOLD_HIGH) { -- 2.30.2 From bd6069248e64d39e001e42ed1b4449c9231a44dd Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Tue, 23 May 2023 03:40:32 -0700 Subject: [PATCH 118/279] temp-sculpt-dyntopo Move core dyntopo loop into EdgeQueueContext * remesh_topology has been split into methods in EdgeQueueContext. This allows us to run detail flood fill in a special "developer" mode to watch the queue update in real time. --- scripts/startup/bl_ui/space_view3d_toolbar.py | 1 + source/blender/blenkernel/intern/dyntopo.cc | 732 +++++++++++++----- .../blenkernel/intern/dyntopo_intern.hh | 53 ++ source/blender/blenlib/BLI_heap_minmax.hh | 151 +++- source/blender/bmesh/intern/bmesh_log.cc | 3 +- .../editors/sculpt_paint/sculpt_detail.cc | 104 ++- 6 files changed, 810 insertions(+), 234 deletions(-) diff --git a/scripts/startup/bl_ui/space_view3d_toolbar.py b/scripts/startup/bl_ui/space_view3d_toolbar.py index af1d5e9a3d3..be100e02cd4 100644 --- a/scripts/startup/bl_ui/space_view3d_toolbar.py +++ b/scripts/startup/bl_ui/space_view3d_toolbar.py @@ -952,6 +952,7 @@ class VIEW3D_PT_sculpt_dyntopo(Panel, View3DPaintPanel): if UnifiedPaintPanel.get_dyntopo_prop(context, brush, "mode") in {'CONSTANT', 'MANUAL'}: col.operator("sculpt.detail_flood_fill") col.prop(WindowManager.operator_properties_last("sculpt.detail_flood_fill"), "interactive") + col.prop(WindowManager.operator_properties_last("sculpt.detail_flood_fill"), "developer") UnifiedPaintPanel.prop_unified_dyntopo( sub, diff --git a/source/blender/blenkernel/intern/dyntopo.cc b/source/blender/blenkernel/intern/dyntopo.cc index 436de26ac4e..475c2f5a620 100644 --- a/source/blender/blenkernel/intern/dyntopo.cc +++ b/source/blender/blenkernel/intern/dyntopo.cc @@ -5,15 +5,8 @@ #include "DNA_meshdata_types.h" #include "DNA_modifier_types.h" -#include "BLI_array.hh" -#include "BLI_index_range.hh" -#include "BLI_map.hh" -#include "BLI_set.hh" -#include "BLI_task.hh" -#include "BLI_vector.hh" - #include "BLI_alloca.h" -#include "BLI_array.h" +#include "BLI_array.hh" #include "BLI_asan.h" #include "BLI_bitmap.h" #include "BLI_buffer.h" @@ -23,14 +16,20 @@ #include "BLI_heap.h" #include "BLI_heap_minmax.hh" #include "BLI_heap_simple.h" +#include "BLI_index_range.hh" #include "BLI_linklist.h" +#include "BLI_map.hh" #include "BLI_math.h" #include "BLI_math_vector_types.hh" #include "BLI_memarena.h" #include "BLI_rand.h" +#include "BLI_rand.hh" +#include "BLI_set.hh" #include "BLI_smallhash.h" #include "BLI_task.h" +#include "BLI_task.hh" #include "BLI_utildefines.h" +#include "BLI_vector.hh" #include "PIL_time.h" #include "atomic_ops.h" @@ -60,12 +59,8 @@ using blender::Set; using blender::Vector; using namespace blender::bke::sculpt; -static void pbvh_split_edges(struct EdgeQueueContext *eq_ctx, - PBVH *pbvh, - BMesh *bm, - BMEdge **edges, - int totedge, - bool ignore_isolated_edges); +static void pbvh_split_edges( + struct EdgeQueueContext *eq_ctx, PBVH *pbvh, BMesh *bm, BMEdge **edges, int totedge); extern "C" { void bmesh_disk_edge_append(BMEdge *e, BMVert *v); void bmesh_radial_loop_append(BMEdge *e, BMLoop *l); @@ -242,7 +237,6 @@ BLI_INLINE float calc_weighted_length(EdgeQueueContext *eq_ctx, float len = len_squared_v3v3(v1->co, v2->co); w = 1.0 + w * float(mode); - return len * w; } @@ -252,9 +246,7 @@ static PBVHTopologyUpdateMode edge_queue_test(EdgeQueueContext *eq_ctx, float *r_w) { float len1 = calc_weighted_length(eq_ctx, e->v1, e->v2, SPLIT); - float max = eq_ctx->limit_len_max_sqr; - - if ((eq_ctx->mode & PBVH_Subdivide) && len1 > max) { + if ((eq_ctx->mode & PBVH_Subdivide) && len1 > eq_ctx->limit_len_max_sqr) { if (r_w) { *r_w = len1; } @@ -262,9 +254,7 @@ static PBVHTopologyUpdateMode edge_queue_test(EdgeQueueContext *eq_ctx, } float len2 = calc_weighted_length(eq_ctx, e->v1, e->v2, COLLAPSE); - float min = eq_ctx->limit_len_min_sqr; - - if ((eq_ctx->mode & PBVH_Collapse) && len2 < min) { + if ((eq_ctx->mode & PBVH_Collapse) && len2 < eq_ctx->limit_len_min_sqr) { if (r_w) { *r_w = len2; } @@ -276,18 +266,14 @@ static PBVHTopologyUpdateMode edge_queue_test(EdgeQueueContext *eq_ctx, static void edge_queue_insert_unified(EdgeQueueContext *eq_ctx, BMEdge *e, float w) { - if (/*BLI_mm_heap_len(eq_ctx->heap_mm) < eq_ctx->max_heap_mm &&*/ !(e->head.hflag & BM_ELEM_TAG)) - { - float lensqr = len_squared_v3v3(e->v1->co, e->v2->co); - float len = sqrtf(lensqr); + if (!(e->head.hflag & BM_ELEM_TAG)) { + float len = len_v3v3(e->v1->co, e->v2->co); eq_ctx->avg_elen += len; eq_ctx->min_elen = min_ff(eq_ctx->min_elen, len); eq_ctx->max_elen = max_ff(eq_ctx->max_elen, len); eq_ctx->totedge++; - // lensqr += (BLI_thread_frand(0) - 0.5f) * 0.1 * eq_ctx->limit_mid; - eq_ctx->edge_heap.insert(w, e); e->head.hflag |= BM_ELEM_TAG; } @@ -466,8 +452,6 @@ static void edge_thread_data_insert(EdgeQueueThreadData *tdata, BMEdge *e) static void long_edge_queue_edge_add_recursive( EdgeQueueContext *eq_ctx, BMLoop *l_edge, const float len_sq, float limit_len, int depth) { - BLI_assert(len_sq > square_f(limit_len)); - if (depth > DEPTH_START_LIMIT && eq_ctx->use_view_normal) { if (dot_v3v3(l_edge->f->no, eq_ctx->view_normal) < 0.0f) { return; @@ -709,7 +693,7 @@ static void unified_edge_queue_task_cb(void *__restrict userdata, BLI_rng_free(rng); } -bool check_face_is_tri(PBVH *pbvh, BMFace *f) +ATTR_NO_OPT bool check_face_is_tri(PBVH *pbvh, BMFace *f) { #if DYNTOPO_DISABLE_FLAG & DYNTOPO_DISABLE_TRIANGULATOR return true; @@ -1067,7 +1051,7 @@ bool check_for_fins(PBVH *pbvh, BMVert *v) return false; } -bool check_vert_fan_are_tris(PBVH *pbvh, BMVert *v) +ATTR_NO_OPT bool check_vert_fan_are_tris(PBVH *pbvh, BMVert *v) { uint8_t *flag = BM_ELEM_CD_PTR(v, pbvh->cd_flag); @@ -1898,104 +1882,50 @@ float mask_cb_nop(PBVHVertRef /*vertex*/, void * /*userdata*/) return 1.0f; } -/* Collapse short edges, subdivide long edges */ -bool remesh_topology(BrushTester *brush_tester, - struct SculptSession *ss, - PBVH *pbvh, - PBVHTopologyUpdateMode mode, - bool use_frontface, - float3 view_normal, - bool updatePBVH, - DyntopoMaskCB mask_cb, - void *mask_cb_data, - int edge_limit_multiply) +ATTR_NO_OPT EdgeQueueContext::EdgeQueueContext(BrushTester *brush_tester_, + struct SculptSession *ss_, + PBVH *pbvh_, + PBVHTopologyUpdateMode mode_, + bool use_frontface_, + float3 view_normal_, + bool updatePBVH_, + DyntopoMaskCB mask_cb_, + void *mask_cb_data_, + int edge_limit_multiply) { - /* Push a subentry. */ - BM_log_entry_add_ex(pbvh->header.bm, pbvh->bm_log, true); + ss = ss_; + pbvh = pbvh_; + brush_tester = brush_tester_; + use_view_normal = use_frontface_; + view_normal = view_normal_; - /* 2 is enough for edge faces - manifold edge */ - BLI_buffer_declare_static(BMLoop *, edge_loops, BLI_BUFFER_NOP, 2); - BLI_buffer_declare_static(BMFace *, deleted_faces, BLI_BUFFER_NOP, 32); + pool = nullptr; + bm = pbvh->header.bm; + mask_cb = mask_cb_; + mask_cb_data = mask_cb_data_; + view_normal = view_normal_; - const int cd_vert_mask_offset = CustomData_get_offset(&pbvh->header.bm->vdata, CD_PAINT_MASK); - const int cd_vert_node_offset = pbvh->cd_vert_node_offset; - const int cd_face_node_offset = pbvh->cd_face_node_offset; + updatePBVH = updatePBVH_; + cd_vert_mask_offset = pbvh->cd_vert_mask_offset; + cd_vert_node_offset = pbvh->cd_vert_node_offset; + cd_face_node_offset = pbvh->cd_face_node_offset; + avg_elen = 0.0f; + max_elen = -1e17; + min_elen = 1e17; + totedge = 0.0f; + local_mode = false; + mode = mode_; + reproject_cdata = CustomData_has_layer(&pbvh->header.bm->ldata, CD_PROP_FLOAT2) && + !ss->ignore_uvs; - bool modified = false; + max_heap_mm = (DYNTOPO_MAX_ITER * edge_limit_multiply) << 8; + limit_len_min = pbvh->bm_min_edge_len; + limit_len_max = pbvh->bm_max_edge_len; + limit_len_min_sqr = limit_len_min * limit_len_min; + limit_len_max_sqr = limit_len_max * limit_len_max; + limit_mid = limit_len_max * 0.5f + limit_len_min * 0.5f; - if (view_normal) { - BLI_assert(len_squared_v3(view_normal) != 0.0f); - } - -#ifdef DYNTOPO_REPORT - { - BMesh *bm = pbvh->header.bm; - - int vmem = (int)((size_t)bm->totvert * (sizeof(BMVert) + bm->vdata.totsize)); - int emem = (int)((size_t)bm->totedge * (sizeof(BMEdge) + bm->edata.totsize)); - int lmem = (int)((size_t)bm->totloop * (sizeof(BMLoop) + bm->ldata.totsize)); - int fmem = (int)((size_t)bm->totface * (sizeof(BMFace) + bm->pdata.totsize)); - - double fvmem = (double)vmem / 1024.0 / 1024.0; - double femem = (double)emem / 1024.0 / 1024.0; - double flmem = (double)lmem / 1024.0 / 1024.0; - double ffmem = (double)fmem / 1024.0 / 1024.0; - - printf("totmem: %.2fmb\n", fvmem + femem + flmem + ffmem); - printf("v: %.2f e: %.2f l: %.2f f: %.2f\n", fvmem, femem, flmem, ffmem); - - printf("custom attributes only:\n"); - vmem = (int)((size_t)bm->totvert * (bm->vdata.totsize)); - emem = (int)((size_t)bm->totedge * (bm->edata.totsize)); - lmem = (int)((size_t)bm->totloop * (bm->ldata.totsize)); - fmem = (int)((size_t)bm->totface * (bm->pdata.totsize)); - - fvmem = (double)vmem / 1024.0 / 1024.0; - femem = (double)emem / 1024.0 / 1024.0; - flmem = (double)lmem / 1024.0 / 1024.0; - ffmem = (double)fmem / 1024.0 / 1024.0; - - printf("v: %.2f e: %.2f l: %.2f f: %.2f\n", fvmem, femem, flmem, ffmem); - } -#endif - - EdgeQueueContext eq_ctx = {}; - - eq_ctx.ss = ss; - eq_ctx.brush_tester = brush_tester; - eq_ctx.use_view_normal = use_frontface; - eq_ctx.view_normal = view_normal; - - eq_ctx.pool = nullptr; - eq_ctx.bm = pbvh->header.bm; - eq_ctx.mask_cb = mask_cb; - eq_ctx.mask_cb_data = mask_cb_data; - eq_ctx.view_normal = view_normal; - - eq_ctx.cd_vert_mask_offset = cd_vert_mask_offset; - eq_ctx.cd_vert_node_offset = cd_vert_node_offset; - eq_ctx.cd_face_node_offset = cd_face_node_offset; - eq_ctx.avg_elen = 0.0f; - eq_ctx.max_elen = -1e17; - eq_ctx.min_elen = 1e17; - eq_ctx.totedge = 0.0f; - eq_ctx.local_mode = false; - eq_ctx.mode = mode; - eq_ctx.reproject_cdata = CustomData_has_layer(&pbvh->header.bm->ldata, CD_PROP_FLOAT2) && - !ss->ignore_uvs; - -#ifdef DYNTOPO_USE_MINMAX_HEAP - // eq_ctx.used_verts = BLI_table_gset_new(__func__); - eq_ctx.max_heap_mm = (DYNTOPO_MAX_ITER * edge_limit_multiply) << 8; - eq_ctx.limit_len_min = pbvh->bm_min_edge_len; - eq_ctx.limit_len_max = pbvh->bm_max_edge_len; - eq_ctx.limit_len_min_sqr = eq_ctx.limit_len_min * eq_ctx.limit_len_min; - eq_ctx.limit_len_max_sqr = eq_ctx.limit_len_max * eq_ctx.limit_len_max; - eq_ctx.limit_mid = eq_ctx.limit_len_max * 0.5f + eq_ctx.limit_len_min * 0.5f; - -#endif - - eq_ctx.surface_smooth_fac = DYNTOPO_SAFE_SMOOTH_FAC; + surface_smooth_fac = DYNTOPO_SAFE_SMOOTH_FAC; if (mode & PBVH_LocalSubdivide) { mode |= PBVH_Subdivide; @@ -2004,6 +1934,10 @@ bool remesh_topology(BrushTester *brush_tester, mode |= PBVH_Collapse; } +#ifdef DYNTOPO_REPORT + report(); +#endif + #if DYNTOPO_DISABLE_FLAG & DYNTOPO_DISABLE_COLLAPSE mode &= ~PBVH_Collapse; #endif @@ -2012,7 +1946,7 @@ bool remesh_topology(BrushTester *brush_tester, #endif if (mode & (PBVH_Subdivide | PBVH_Collapse)) { - unified_edge_queue_create(&eq_ctx, pbvh, mode & (PBVH_LocalSubdivide | PBVH_LocalCollapse)); + unified_edge_queue_create(this, pbvh, mode & (PBVH_LocalSubdivide | PBVH_LocalCollapse)); } #ifdef SKINNY_EDGE_FIX @@ -2040,6 +1974,437 @@ bool remesh_topology(BrushTester *brush_tester, // const float ratio = 2.0; // 1.0f / 3.0f; #endif + if ((mode & PBVH_Subdivide) && (mode & PBVH_Collapse)) { + ops[0] = PBVH_Subdivide; + ops[1] = PBVH_Collapse; + totop = 2; + + steps[1] = DYNTOPO_MAX_ITER_COLLAPSE; + steps[0] = DYNTOPO_MAX_ITER_SUBD; + } + else if (mode & PBVH_Subdivide) { + ops[0] = PBVH_Subdivide; + totop = 1; + + steps[0] = DYNTOPO_MAX_ITER_SUBD; + } + else if (mode & PBVH_Collapse) { + ops[0] = PBVH_Collapse; + totop = 1; + + steps[0] = DYNTOPO_MAX_ITER_COLLAPSE; + } + + max_steps = (DYNTOPO_MAX_ITER * edge_limit_multiply) << (totop - 1); + max_subd = max_steps >> (totop - 1); + + edges_size = steps[0]; + edges = (BMEdge **)MEM_malloc_arrayN(edges_size, sizeof(void *), __func__); + etot = 0; + + BLI_smallhash_init(&subd_edges); +} + +ATTR_NO_OPT void EdgeQueueContext::flush_subdivision() +{ + if (etot == 0) { + return; + } + + modified = true; + BLI_smallhash_clear(&subd_edges, 0); + pbvh_split_edges(this, pbvh, pbvh->header.bm, edges, etot); + count_subd += etot; + VALIDATE_LOG(pbvh->bm_log); + etot = 0; +} + +ATTR_NO_OPT EdgeQueueContext::~EdgeQueueContext() +{ + MEM_SAFE_FREE(edges); + BLI_smallhash_release(&subd_edges); +} + +ATTR_NO_OPT void EdgeQueueContext::start() +{ + current_i = 0; +} + +ATTR_NO_OPT bool EdgeQueueContext::done() +{ + return !(totop > 0 && !edge_heap.empty() && current_i < max_steps); +} + +ATTR_NO_OPT void EdgeQueueContext::finish() +{ + if (mode & PBVH_Cleanup) { + modified |= do_cleanup_3_4(this, pbvh); + + VALIDATE_LOG(pbvh->bm_log); + } + + if (modified) { + /* Avoid potential infinite loops. */ + const int totnode = pbvh->totnode; + + for (int i = 0; i < totnode; i++) { + PBVHNode *node = pbvh->nodes + i; + + if ((node->flag & PBVH_Leaf) && (node->flag & PBVH_UpdateTopology) && + !(node->flag & PBVH_FullyHidden)) + { + + /* do not clear PBVH_UpdateTopology here in case split messes with it */ + + /* Recursively split nodes that have gotten too many + * elements */ + if (updatePBVH) { + pbvh_bmesh_node_limit_ensure(pbvh, i); + } + } + } + } + + /* clear PBVH_UpdateTopology flags */ + for (int i = 0; i < pbvh->totnode; i++) { + PBVHNode *node = pbvh->nodes + i; + + if (!(node->flag & PBVH_Leaf)) { + continue; + } + + node->flag &= ~PBVH_UpdateTopology; + } + +#ifdef USE_VERIFY + pbvh_bmesh_verify(pbvh); +#endif + + /* Ensure triangulations are all up to date. */ + for (int i = 0; i < pbvh->totnode; i++) { + PBVHNode *node = pbvh->nodes + i; + + if (node->flag & PBVH_Leaf) { + pbvh_bmesh_check_other_verts(node); + BKE_pbvh_bmesh_check_tris(pbvh, node); + } + } + + if (modified) { + BKE_pbvh_update_bounds(pbvh, PBVH_UpdateBB | PBVH_UpdateOriginalBB); + } + + /* Push a subentry. */ + BM_log_entry_add_ex(pbvh->header.bm, pbvh->bm_log, true); +} + +ATTR_NO_OPT void EdgeQueueContext::step() +{ + if (done()) { + return; + } + + BMEdge *e = nullptr; + + if (count >= steps[curop]) { + if (ops[curop] == PBVH_Subdivide) { // && count_subd < max_subd) { + flush_subdivision(); + flushed_ = true; + } + + curop = (curop + 1) % totop; + count = 0; + } + +#if 0 + if (curop == 0 && count_subd >= max_subd && totop > 1 && ops[0] == PBVH_Subdivide && + ops[1] == PBVH_Collapse) + { + if (etot > 0) { + flush_subdivision(); + } + curop = 1; + } +#endif + + switch (ops[curop]) { + case PBVH_None: + break; + case PBVH_Subdivide: { + if (edge_heap.max_weight() < limit_len_max_sqr) { + break; + } + + float w = 0.0f; + e = edge_heap.pop_max(&w); + + while (!edge_heap.empty() && e && + (bm_elem_is_free((BMElem *)e, BM_EDGE) || + fabs(calc_weighted_length(this, e->v1, e->v2, SPLIT) - w) > w * 0.1)) + { + if (e && !bm_elem_is_free((BMElem *)e, BM_EDGE)) { + e->head.hflag &= ~BM_ELEM_TAG; + edge_heap.insert(calc_weighted_length(this, e->v1, e->v2, SPLIT), e); + } + + e = edge_heap.pop_max(&w); + } + + // printf("w: %.5f\n", calc_weighted_length(this, e->v1, e->v2, SPLIT)); + + if (!e || bm_elem_is_free((BMElem *)e, BM_EDGE) || w < limit_len_max_sqr) { + break; + } + + e->head.hflag &= ~BM_ELEM_TAG; + +#if 0 + void **val = nullptr; + if (etot < edges_size && !BLI_smallhash_ensure_p(&subd_edges, (uintptr_t)e, &val)) { + *val = nullptr; + edges[etot++] = e; + } +#else + /* add complete triangles */ + BMLoop *l = e->l; + if (l) { + do { + BMLoop *l2 = l; + do { + if (etot >= edges_size) { + break; + } + + if (calc_weighted_length(this, l->e->v1, l->e->v2, SPLIT) < limit_len_max_sqr) { + continue; + } + void **val = nullptr; + + if (!BLI_smallhash_ensure_p(&subd_edges, (uintptr_t)l->e, &val)) { + *val = nullptr; + edges[etot++] = e; + } + } while ((l2 = l2->next) != l); + } while ((l = l->radial_next) != e->l); + } +#endif + break; + } + case PBVH_Collapse: { + if (edge_heap.min_weight() > limit_len_min_sqr) { + break; + } + + e = edge_heap.pop_min(); + while (!edge_heap.empty() && e && + (bm_elem_is_free((BMElem *)e, BM_EDGE) || + calc_weighted_length(this, e->v1, e->v2, COLLAPSE) > limit_len_min_sqr)) + { + e = edge_heap.pop_min(); + } + + if (!e || bm_elem_is_free((BMElem *)e, BM_EDGE)) { + break; + } + + if (bm_elem_is_free((BMElem *)e->v1, BM_VERT) || bm_elem_is_free((BMElem *)e->v2, BM_VERT)) { + printf("%s: error! operated on freed bmesh elements! e: %p, e->v1: %p, e->v2: %p\n", + __func__, + e, + e->v1, + e->v2); + break; + } + + modified = true; + pbvh_bmesh_collapse_edge(pbvh, e, e->v1, e->v2, nullptr, nullptr, this); + flushed_ = true; + VALIDATE_LOG(pbvh->bm_log); + break; + } + case PBVH_LocalSubdivide: + case PBVH_LocalCollapse: + case PBVH_Cleanup: + BLI_assert_unreachable(); + break; + } + + if (edge_heap.empty() && etot > 0) { + /* Flush subdivision, it may add more to queue.*/ + flush_subdivision(); + flushed_ = true; + } + + count++; + current_i++; +} + +ATTR_NO_OPT void EdgeQueueContext::report() +{ + BMesh *bm = pbvh->header.bm; + + int vmem = (int)((size_t)bm->totvert * (sizeof(BMVert) + bm->vdata.totsize)); + int emem = (int)((size_t)bm->totedge * (sizeof(BMEdge) + bm->edata.totsize)); + int lmem = (int)((size_t)bm->totloop * (sizeof(BMLoop) + bm->ldata.totsize)); + int fmem = (int)((size_t)bm->totface * (sizeof(BMFace) + bm->pdata.totsize)); + + double fvmem = (double)vmem / 1024.0 / 1024.0; + double femem = (double)emem / 1024.0 / 1024.0; + double flmem = (double)lmem / 1024.0 / 1024.0; + double ffmem = (double)fmem / 1024.0 / 1024.0; + + printf("totmem: %.2fmb\n", fvmem + femem + flmem + ffmem); + printf("v: %.2f e: %.2f l: %.2f f: %.2f\n", fvmem, femem, flmem, ffmem); + + printf("custom attributes only:\n"); + vmem = (int)((size_t)bm->totvert * (bm->vdata.totsize)); + emem = (int)((size_t)bm->totedge * (bm->edata.totsize)); + lmem = (int)((size_t)bm->totloop * (bm->ldata.totsize)); + fmem = (int)((size_t)bm->totface * (bm->pdata.totsize)); + + fvmem = (double)vmem / 1024.0 / 1024.0; + femem = (double)emem / 1024.0 / 1024.0; + flmem = (double)lmem / 1024.0 / 1024.0; + ffmem = (double)fmem / 1024.0 / 1024.0; + + printf("v: %.2f e: %.2f l: %.2f f: %.2f\n", fvmem, femem, flmem, ffmem); +} + +// EdgeQueueContext +ATTR_NO_OPT bool remesh_topology(BrushTester *brush_tester, + struct SculptSession *ss, + PBVH *pbvh, + PBVHTopologyUpdateMode mode, + bool use_frontface, + float3 view_normal, + bool updatePBVH, + DyntopoMaskCB mask_cb, + void *mask_cb_data, + int edge_limit_multiply) +{ + EdgeQueueContext eq_ctx(brush_tester, + ss, + pbvh, + mode, + use_frontface, + view_normal, + updatePBVH, + mask_cb, + mask_cb_data, + edge_limit_multiply); + eq_ctx.start(); + + while (!eq_ctx.done()) { + eq_ctx.step(); + } + + eq_ctx.finish(); + return eq_ctx.modified; +} + +#if 0 +/* Collapse short edges, subdivide long edges */ +bool remesh_topology(BrushTester *brush_tester, + struct SculptSession *ss, + PBVH *pbvh, + PBVHTopologyUpdateMode mode, + bool use_frontface, + float3 view_normal, + bool updatePBVH, + DyntopoMaskCB mask_cb, + void *mask_cb_data, + int edge_limit_multiply) +{ + /* Push a subentry. */ + BM_log_entry_add_ex(pbvh->header.bm, pbvh->bm_log, true); + + const int cd_vert_mask_offset = CustomData_get_offset(&pbvh->header.bm->vdata, CD_PAINT_MASK); + const int cd_vert_node_offset = pbvh->cd_vert_node_offset; + const int cd_face_node_offset = pbvh->cd_face_node_offset; + + bool modified = false; + + if (view_normal) { + BLI_assert(len_squared_v3(view_normal) != 0.0f); + } + + EdgeQueueContext eq_ctx = {}; + + eq_ctx.ss = ss; + eq_ctx.brush_tester = brush_tester; + eq_ctx.use_view_normal = use_frontface; + eq_ctx.view_normal = view_normal; + + eq_ctx.pool = nullptr; + eq_ctx.bm = pbvh->header.bm; + eq_ctx.mask_cb = mask_cb; + eq_ctx.mask_cb_data = mask_cb_data; + eq_ctx.view_normal = view_normal; + + eq_ctx.cd_vert_mask_offset = cd_vert_mask_offset; + eq_ctx.cd_vert_node_offset = cd_vert_node_offset; + eq_ctx.cd_face_node_offset = cd_face_node_offset; + eq_ctx.avg_elen = 0.0f; + eq_ctx.max_elen = -1e17; + eq_ctx.min_elen = 1e17; + eq_ctx.totedge = 0.0f; + eq_ctx.local_mode = false; + eq_ctx.mode = mode; + eq_ctx.reproject_cdata = CustomData_has_layer(&pbvh->header.bm->ldata, CD_PROP_FLOAT2) && + !ss->ignore_uvs; + + eq_ctx.max_heap_mm = (DYNTOPO_MAX_ITER * edge_limit_multiply) << 8; + eq_ctx.limit_len_min = pbvh->bm_min_edge_len; + eq_ctx.limit_len_max = pbvh->bm_max_edge_len; + eq_ctx.limit_len_min_sqr = eq_ctx.limit_len_min * eq_ctx.limit_len_min; + eq_ctx.limit_len_max_sqr = eq_ctx.limit_len_max * eq_ctx.limit_len_max; + eq_ctx.limit_mid = eq_ctx.limit_len_max * 0.5f + eq_ctx.limit_len_min * 0.5f; + + eq_ctx.surface_smooth_fac = DYNTOPO_SAFE_SMOOTH_FAC; + + if (mode & PBVH_LocalSubdivide) { + mode |= PBVH_Subdivide; + } + if (mode & PBVH_LocalSubdivide) { + mode |= PBVH_Collapse; + } + +# if DYNTOPO_DISABLE_FLAG & DYNTOPO_DISABLE_COLLAPSE + mode &= ~PBVH_Collapse; +# endif +# if DYNTOPO_DISABLE_FLAG & DYNTOPO_DISABLE_SPLIT_EDGES + mode &= ~PBVH_Subdivide; +# endif + + if (mode & (PBVH_Subdivide | PBVH_Collapse)) { + unified_edge_queue_create(&eq_ctx, pbvh, mode & (PBVH_LocalSubdivide | PBVH_LocalCollapse)); + } + +# ifdef SKINNY_EDGE_FIX + float ratio = 1.0f; + + /* Prevent remesher thrashing by throttling edge splitting in pathological case of skinny + * edges. + */ + float avg_elen = eq_ctx.avg_elen; + if (eq_ctx.totedge > 0.0f) { + avg_elen /= eq_ctx.totedge; + + float emax = eq_ctx.max_elen; + if (emax == 0.0f) { + emax = 0.0001f; + } + + if (pbvh->bm_min_edge_len > 0.0f && avg_elen > 0.0f) { + ratio = avg_elen / (pbvh->bm_min_edge_len * 0.5 + emax * 0.5); + ratio = MAX2(ratio, 0.25f); + ratio = MIN2(ratio, 5.0f); + } + } +# else +// const float ratio = 2.0; // 1.0f / 3.0f; +# endif + int steps[2] = {0, 0}; PBVHTopologyUpdateMode ops[2]; @@ -2250,12 +2615,9 @@ bool remesh_topology(BrushTester *brush_tester, node->flag &= ~PBVH_UpdateTopology; } - BLI_buffer_free(&edge_loops); - BLI_buffer_free(&deleted_faces); - -#ifdef USE_VERIFY +# ifdef USE_VERIFY pbvh_bmesh_verify(pbvh); -#endif +# endif /* Ensure triangulations are all up to date. */ for (int i = 0; i < pbvh->totnode; i++) { @@ -2276,8 +2638,9 @@ bool remesh_topology(BrushTester *brush_tester, return modified; } +#endif -#define SPLIT_TAG BM_ELEM_TAG +#define SPLIT_TAG BM_ELEM_TAG_ALT /* @@ -2413,12 +2776,8 @@ static const int splitmap[43][16] = { {6, -1, 3, -1, 5, -1, 1}, // 42 0 1 0 1 0 1 }; -static void pbvh_split_edges(EdgeQueueContext *eq_ctx, - PBVH *pbvh, - BMesh *bm, - BMEdge **edges1, - int totedge, - bool ignore_isolated_edges) +ATTR_NO_OPT static void pbvh_split_edges( + EdgeQueueContext *eq_ctx, PBVH *pbvh, BMesh *bm, BMEdge **edges1, int totedge) { bm_logstack_push(); bm_log_message(" == split edges == "); @@ -2464,8 +2823,9 @@ static void pbvh_split_edges(EdgeQueueContext *eq_ctx, for (int i = 0; i < totedge; i++) { BMEdge *e = edges[i]; - check_vert_fan_are_tris(pbvh, e->v1); - check_vert_fan_are_tris(pbvh, e->v2); + // XXX + // check_vert_fan_are_tris(pbvh, e->v1); + // check_vert_fan_are_tris(pbvh, e->v2); } for (int i = 0; i < totedge; i++) { @@ -2516,7 +2876,7 @@ static void pbvh_split_edges(EdgeQueueContext *eq_ctx, if (!(l->f->head.hflag & SPLIT_TAG)) { BMLoop *l2 = l; do { - l2->v->head.hflag &= ~BM_ELEM_TAG; + l2->v->head.hflag &= ~SPLIT_TAG; } while ((l2 = l2->next) != l); l->f->head.hflag |= SPLIT_TAG; @@ -2534,19 +2894,14 @@ static void pbvh_split_edges(EdgeQueueContext *eq_ctx, BMFace *f = faces[i]; BMLoop *l = f->l_first; - if (!ignore_isolated_edges) { - f->head.hflag |= SPLIT_TAG; - BM_log_face_removed(pbvh->header.bm, pbvh->bm_log, f); - BM_idmap_release(pbvh->bm_idmap, (BMElem *)f, true); - } - else { - f->head.hflag &= ~SPLIT_TAG; - } + f->head.hflag |= SPLIT_TAG; + BM_log_face_removed(pbvh->header.bm, pbvh->bm_log, f); + BM_idmap_release(pbvh->bm_idmap, (BMElem *)f, true); + /* Build pattern mask and store in f->head.index. */ int mask = 0; int j = 0; int count = 0; - do { if (l->e->head.hflag & SPLIT_TAG) { mask |= 1 << j; @@ -2556,12 +2911,6 @@ static void pbvh_split_edges(EdgeQueueContext *eq_ctx, j++; } while ((l = l->next) != f->l_first); - if (ignore_isolated_edges) { - do { - l->e->head.index = MAX2(l->e->head.index, count); - } while ((l = l->next) != f->l_first); - } - f->head.index = mask; } @@ -2570,38 +2919,14 @@ static void pbvh_split_edges(EdgeQueueContext *eq_ctx, for (int i = 0; i < totedge; i++) { BMEdge *e = edges[i]; - if (ignore_isolated_edges && e->head.index < 2) { - BMLoop *l = e->l; - - do { - l->f->head.hflag &= ~SPLIT_TAG; - } while ((l = l->radial_next) != e->l); - - edges[i] = nullptr; - continue; - } - - if (ignore_isolated_edges) { - BMLoop *l = e->l; - do { - if (!(l->f->head.hflag & SPLIT_TAG)) { - l->f->head.hflag |= SPLIT_TAG; - BM_log_face_removed(pbvh->header.bm, pbvh->bm_log, l->f); - } - } while ((l = l->radial_next) != e->l); - } - } - - for (int i = 0; i < totedge; i++) { - BMEdge *e = edges[i]; - BMVert *v1 = e->v1; - BMVert *v2 = e->v2; - BMEdge *newe = nullptr; - if (!e || !(e->head.hflag & SPLIT_TAG)) { continue; } + BMVert *v1 = e->v1; + BMVert *v2 = e->v2; + BMEdge *newe = nullptr; + e->head.hflag &= ~SPLIT_TAG; BKE_pbvh_bmesh_check_origdata(eq_ctx->ss, e->v1, pbvh->stroke_id); @@ -2616,7 +2941,8 @@ static void pbvh_split_edges(EdgeQueueContext *eq_ctx, BM_idmap_release(pbvh->bm_idmap, (BMElem *)e, true); BMVert *newv = BM_edge_split(pbvh->header.bm, e, e->v1, &newe, 0.5f); - newe->head.hflag &= ~SPLIT_TAG; + newe->head.hflag &= ~(SPLIT_TAG | BM_ELEM_TAG); + e->head.hflag &= ~(SPLIT_TAG | BM_ELEM_TAG); BM_log_vert_added(bm, pbvh->bm_log, newv); BM_log_edge_added(bm, pbvh->bm_log, e); @@ -2626,7 +2952,6 @@ static void pbvh_split_edges(EdgeQueueContext *eq_ctx, #ifdef SUBD_ADD_TO_QUEUE if (e->l) { - float w1 = 0.0f, w2 = 0.0f; PBVHTopologyUpdateMode mode; bool ok = false; @@ -2646,12 +2971,22 @@ static void pbvh_split_edges(EdgeQueueContext *eq_ctx, if ((mode = edge_queue_test(eq_ctx, pbvh, e, &w1)) != PBVH_None) { e->head.hflag &= ~BM_ELEM_TAG; - edge_queue_insert_unified(eq_ctx, e, w1); + if (e->l && mode == PBVH_Subdivide) { + long_edge_queue_edge_add_recursive(eq_ctx, e->l, w1, eq_ctx->limit_len_max, 0); + } + else { + edge_queue_insert_unified(eq_ctx, e, w1); + } } if ((mode = edge_queue_test(eq_ctx, pbvh, newe, &w2)) != PBVH_None) { newe->head.hflag &= ~BM_ELEM_TAG; - edge_queue_insert_unified(eq_ctx, newe, w2); + if (newe->l && mode == PBVH_Subdivide) { + long_edge_queue_edge_add_recursive(eq_ctx, newe->l, w2, eq_ctx->limit_len_max, 0); + } + else { + edge_queue_insert_unified(eq_ctx, newe, w2); + } } } } @@ -2763,7 +3098,6 @@ static void pbvh_split_edges(EdgeQueueContext *eq_ctx, for (int i = 0; i < totface; i++) { BMFace *f = faces[i]; - int mask = 0; if (!(f->head.hflag & SPLIT_TAG)) { continue; @@ -2777,8 +3111,8 @@ static void pbvh_split_edges(EdgeQueueContext *eq_ctx, } BMLoop *l = f->l_first; + int totmask = 0, mask = 0; int j = 0; - int totmask = 0; do { if (l->v->head.hflag & SPLIT_TAG) { @@ -2799,11 +3133,12 @@ static void pbvh_split_edges(EdgeQueueContext *eq_ctx, int n = pat[0]; if (n < 0) { + printf("%s: error 1! %d %d\n", __func__, n, flen); continue; } if (n != f->len || n != flen) { - printf("%s: error! %d %d\n", __func__, n, flen); + printf("%s: error 2! %d %d\n", __func__, n, flen); continue; } @@ -2871,17 +3206,22 @@ static void pbvh_split_edges(EdgeQueueContext *eq_ctx, } else { newf = BM_face_split(bm, f2, l1, l2, &rl, nullptr, false); + exist_e = newf ? rl->e : nullptr; } - if (newf) { - rl->e->head.hflag &= ~BM_ELEM_TAG; + if (exist_e && exist_e->l) { + exist_e->head.hflag &= ~BM_ELEM_TAG; + #ifdef SUBD_ADD_TO_QUEUE - float3 co = rl->v->co; - co = (co + rl->next->v->co) * 0.5f; + float3 co = exist_e->v1->co; + co = (co + exist_e->v2->co) * 0.5f; float w = 0.0f; - if (test_near_brush(rl->e, co, &w)) { - long_edge_queue_edge_add_recursive(eq_ctx, rl, w, eq_ctx->limit_len_max, 0); + if (test_near_brush(exist_e, co, &w)) { + if (w >= eq_ctx->limit_len_max_sqr) { + // edge_queue_insert_unified(eq_ctx, exist_e, w); + long_edge_queue_edge_add_recursive(eq_ctx, exist_e->l, w, eq_ctx->limit_len_max, 0); + } } #endif diff --git a/source/blender/blenkernel/intern/dyntopo_intern.hh b/source/blender/blenkernel/intern/dyntopo_intern.hh index 6a14de2e4b0..84dba0aadec 100644 --- a/source/blender/blenkernel/intern/dyntopo_intern.hh +++ b/source/blender/blenkernel/intern/dyntopo_intern.hh @@ -3,10 +3,12 @@ #include "BKE_dyntopo.hh" #include "BKE_paint.h" #include "BKE_pbvh.h" +#include "BKE_sculpt.hh" #include "BLI_asan.h" #include "BLI_heap_minmax.hh" #include "BLI_math_vector_types.hh" +#include "BLI_rand.hh" #include "bmesh.h" #include "pbvh_intern.hh" @@ -251,6 +253,57 @@ struct EdgeQueueContext { PBVHTopologyUpdateMode mode; bool reproject_cdata; + + bool updatePBVH = false; + int steps[2]; + PBVHTopologyUpdateMode ops[2]; + int totop = 0; + int current_i = 0; + int max_steps; + PBVH *pbvh; + BMEdge **edges; + int etot; + int edges_size; + SmallHash subd_edges; + bool modified = false; + int count_subd = 0; + int count = 0; + int curop = 0; + int max_subd; + + ~EdgeQueueContext(); + EdgeQueueContext(BrushTester *brush_tester, + struct SculptSession *ss, + PBVH *pbvh, + PBVHTopologyUpdateMode mode, + bool use_frontface, + float3 view_normal, + bool updatePBVH, + DyntopoMaskCB mask_cb, + void *mask_cb_data, + int edge_limit_multiply); + + void flush_subdivision(); + void start(); + bool done(); + void step(); + void finish(); + + void report(); + + bool was_flushed() + { + if (flushed_) { + flushed_ = false; + return true; + } + + return false; + } + + blender::RandomNumberGenerator rand; + private: + bool flushed_ = false; }; bool destroy_nonmanifold_fins(PBVH *pbvh, BMEdge *e_root); diff --git a/source/blender/blenlib/BLI_heap_minmax.hh b/source/blender/blenlib/BLI_heap_minmax.hh index a3fa2c78177..75bafd44ba3 100644 --- a/source/blender/blenlib/BLI_heap_minmax.hh +++ b/source/blender/blenlib/BLI_heap_minmax.hh @@ -19,6 +19,117 @@ using blender::Vector; namespace blender { template + +#if 1 +class MinMaxHeap { + struct MinMaxHeapNode { + Value value; + float weight; + + int child1 = -1, child2 = -1, parent = -1; + }; + + public: + MinMaxHeap(int reserved = 0) {} + ATTR_NO_OPT MinMaxHeapNode *insert(float weight, Value value) + { + nodes.resize(nodes.size() + 1); + MinMaxHeapNode *node = &nodes.last(); + + node->weight = weight; + node->value = value; + + return node; + } + + MinMaxHeapNode *max() + { + MinMaxHeapNode *max_node = nullptr; + float max = FLT_MIN; + + for (MinMaxHeapNode &node : nodes) { + if (node.weight > max) { + max_node = &node; + max = node.weight; + } + } + + return max_node; + } + MinMaxHeapNode *min() + { + MinMaxHeapNode *min_node = nullptr; + float min = FLT_MAX; + + for (MinMaxHeapNode &node : nodes) { + if (node.weight < min) { + min_node = &node; + min = node.weight; + } + } + + return min_node; + } + + float min_weight() + { + return min()->weight; + } + + float max_weight() + { + return max()->weight; + } + + ATTR_NO_OPT void pop_node(MinMaxHeapNode *node) + { + int i = node - nodes.data(); + + nodes[i] = nodes[nodes.size() - 1]; + nodes.pop_last(); + } + + Value pop_min(float *r_w = nullptr) + { + MinMaxHeapNode *node = min(); + if (r_w) { + *r_w = node->weight; + } + + Value ret = node->value; + pop_node(node); + + return ret; + } + + Value pop_max(float *r_w = nullptr) + { + MinMaxHeapNode *node = max(); + if (r_w) { + *r_w = node->weight; + } + + Value ret = node->value; + pop_node(node); + + return ret; + } + + int len() + { + return nodes.size(); + } + + bool empty() + { + return nodes.size() == 0; + } + + private: + Vector nodes; +}; + +#else class MinMaxHeap { struct MinMaxHeapNode { Value value; @@ -104,11 +215,11 @@ class MinMaxHeap { return nodes.pop_last().value; } -#ifdef BLI_MINMAX_HEAP_VALIDATE +# ifdef BLI_MINMAX_HEAP_VALIDATE if (!is_valid()) { printf("invalid heap!\n"); } -#endif +# endif Value ret = nodes[0].value; MinMaxHeapNode *last = heap_pop_last(); @@ -118,30 +229,37 @@ class MinMaxHeap { heap_push_down(&nodes[0]); -#ifdef BLI_MINMAX_HEAP_VALIDATE +# ifdef BLI_MINMAX_HEAP_VALIDATE if (!is_valid()) { printf("invalid heap!\n"); } -#endif +# endif return ret; } - Value pop_max() + Value pop_max(float *r_w = nullptr) { - MinMaxHeapNode *node = max(); - if (nodes.size() == 1) { + if (r_w) { + *r_w = nodes[0].weight; + } return nodes.pop_last().value; } -#ifdef BLI_MINMAX_HEAP_VALIDATE + MinMaxHeapNode *node = max(); + +# ifdef BLI_MINMAX_HEAP_VALIDATE if (!is_valid()) { printf("invalid heap!\n"); } -#endif +# endif Value ret = node->value; + if (r_w) { + *r_w = node->weight; + } + MinMaxHeapNode *last = heap_pop_last(); node->weight = last->weight; @@ -149,11 +267,11 @@ class MinMaxHeap { node = heap_push_down(node); -#ifdef BLI_MINMAX_HEAP_VALIDATE +# ifdef BLI_MINMAX_HEAP_VALIDATE if (!is_valid()) { printf("invalid heap!\n"); } -#endif +# endif return ret; } @@ -191,7 +309,15 @@ class MinMaxHeap { return &nodes[0]; } - if (nodes[0].child1 != -1) { + MinMaxHeapNode &root = nodes[0]; + if (root.child1 != -1 && root.child2 != -1) { + if (nodes[nodes[0].child1].weight > nodes[nodes[0].child2].weight) { + return &nodes[nodes[0].child1]; + } + + return &nodes[nodes[0].child2]; + } + else if (root.child1 != -1) { return &nodes[nodes[0].child1]; } else { @@ -481,4 +607,5 @@ class MinMaxHeap { Vector nodes; }; +#endif } // namespace blender diff --git a/source/blender/bmesh/intern/bmesh_log.cc b/source/blender/bmesh/intern/bmesh_log.cc index 752507df094..d40c3a50f8a 100644 --- a/source/blender/bmesh/intern/bmesh_log.cc +++ b/source/blender/bmesh/intern/bmesh_log.cc @@ -1799,7 +1799,8 @@ void BM_log_set_current_entry(BMLog *log, BMLogEntry *entry) bool BM_log_entry_drop(BMLogEntry *entry) { - printf("%s: Freeing log entry %p\n", __func__, entry); + float size = entry->calc_size() / 1024.0f / 1024.0f; + printf("%s: Freeing log entry %p, size: %.3fmb\n", __func__, entry, size); if (entry->prev) { entry->prev->next = entry->next; diff --git a/source/blender/editors/sculpt_paint/sculpt_detail.cc b/source/blender/editors/sculpt_paint/sculpt_detail.cc index e78f0c4ab6f..9a3ed089613 100644 --- a/source/blender/editors/sculpt_paint/sculpt_detail.cc +++ b/source/blender/editors/sculpt_paint/sculpt_detail.cc @@ -45,6 +45,9 @@ #include +/* For detail flood fill developer mode */ +#include "../../blenkernel/intern/dyntopo_intern.hh" + using blender::float3; /* -------------------------------------------------------------------- */ @@ -84,15 +87,25 @@ static bool sculpt_and_dynamic_topology_poll(bContext *C) /* -------------------------------------------------------------------- */ /** \name Detail Flood Fill * \{ */ -static int sculpt_detail_flood_fill_run(Object *ob, Sculpt *sd, Brush *brush, wmOperator * /*op*/) +static int sculpt_detail_flood_fill_run(Object *ob, + Sculpt *sd, + Brush *brush, + wmOperator *op, + std::function lock_main_thread, + std::function unlock_main_thread, + std::function update_main_thread, + std::function should_stop) { using namespace blender::bke::dyntopo; - SculptSession *ss = ob->sculpt; + lock_main_thread(); + bool developer_mode = RNA_boolean_get(op->ptr, "developer"); + SculptSession *ss = ob->sculpt; Vector nodes = blender::bke::pbvh::search_gather(ss->pbvh, nullptr, nullptr); if (nodes.is_empty()) { + unlock_main_thread(); return OPERATOR_CANCELLED; } @@ -124,26 +137,53 @@ static int sculpt_detail_flood_fill_run(Object *ob, Sculpt *sd, Brush *brush, wm double time = PIL_check_seconds_timer(); int edge_step_mul = 1 + int(ss->cached_dyntopo.quality * 100.0f); - for (int i = 0; i < 1 + ss->cached_dyntopo.repeat; i++) { - nodes = blender::bke::pbvh::search_gather(ss->pbvh, nullptr, nullptr); + unlock_main_thread(); + int repeat = ss->cached_dyntopo.repeat; + for (int i = 0; i < 1 + repeat; i++) { + lock_main_thread(); + + nodes = blender::bke::pbvh::search_gather(ss->pbvh, nullptr, nullptr); for (int j = 0; j < nodes.size(); j++) { BKE_pbvh_node_mark_topology_update(nodes[j]); } blender::bke::dyntopo::BrushNoRadius brush_tester; - remesh_topology(&brush_tester, - ss, - ss->pbvh, - mode, - false, - float3(0.0f, 0.0f, 1.0f), - false, - mask_cb, - mask_cb_data, - edge_step_mul); + EdgeQueueContext remesher(&brush_tester, + ss, + ss->pbvh, + mode, + false, + float3(0.0f, 0.0f, 1.0f), + false, + mask_cb, + mask_cb_data, + edge_step_mul); + + remesher.start(); + + while (!remesher.done()) { + remesher.step(); + + if (developer_mode && remesher.was_flushed()) { + DEG_id_tag_update(&ob->id, ID_RECALC_SHADING); + unlock_main_thread(); + update_main_thread(); + PIL_sleep_ms(1); + lock_main_thread(); + } + + if (should_stop()) { + break; + } + } + + remesher.finish(); + unlock_main_thread(); } + lock_main_thread(); + time = (PIL_check_seconds_timer() - time) * 1000.0; printf(" Time: %.3fms\n", float(time)); @@ -152,6 +192,8 @@ static int sculpt_detail_flood_fill_run(Object *ob, Sculpt *sd, Brush *brush, wm after_stroke(ss->pbvh, true); DEG_id_tag_update(&ob->id, ID_RECALC_SHADING); + unlock_main_thread(); + return OPERATOR_FINISHED; } @@ -167,7 +209,14 @@ static int sculpt_detail_flood_fill_exec(bContext *C, wmOperator *op) SCULPT_undo_push_node(ob, nullptr, SCULPT_UNDO_COORDS); int ret = sculpt_detail_flood_fill_run( - CTX_data_active_object(C), sd, BKE_paint_brush(&sd->paint), op); + CTX_data_active_object(C), + sd, + BKE_paint_brush(&sd->paint), + op, + []() {}, + []() {}, + []() {}, + []() { return false; }); WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob); SCULPT_undo_push_end(ob); @@ -195,26 +244,30 @@ static void start_fill_job(void * /*custom_data*/, { printf("Start detail fill job.\n"); + auto lock_main_thread = [&]() { WM_job_main_thread_lock_acquire(flood_fill_job.job); }; + auto unlock_main_thread = [&]() { WM_job_main_thread_lock_release(flood_fill_job.job); }; + auto update_main_thread = [&]() { *do_update = true; }; + auto should_stop = [&]() { return *stop; }; + while (true) { if (*stop) { break; } - WM_job_main_thread_lock_acquire(flood_fill_job.job); - - if (sculpt_detail_flood_fill_run( - flood_fill_job.ob, flood_fill_job.sd, flood_fill_job.brush, flood_fill_job.op) == - OPERATOR_CANCELLED) + if (sculpt_detail_flood_fill_run(flood_fill_job.ob, + flood_fill_job.sd, + flood_fill_job.brush, + flood_fill_job.op, + lock_main_thread, + unlock_main_thread, + update_main_thread, + should_stop) == OPERATOR_CANCELLED) { - WM_job_main_thread_lock_release(flood_fill_job.job); break; } - WM_job_main_thread_lock_release(flood_fill_job.job); - *do_update = true; - - PIL_sleep_ms(50); + PIL_sleep_ms(15); } printf("\nJob finished\n\n"); @@ -303,6 +356,7 @@ void SCULPT_OT_detail_flood_fill(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; RNA_def_boolean(ot->srna, "interactive", true, "Interactive", "Interactive mode"); + RNA_def_boolean(ot->srna, "developer", false, "Developer", "Developer mode"); } /** \} */ -- 2.30.2 From 774d08bfd4f0089c6d1b60dc2e04db733e825700 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Thu, 25 May 2023 03:28:53 -0700 Subject: [PATCH 119/279] temp-sculpt-dyntopo: Improve dyntopo remeshing quality Also cleaned up the code some more. --- scripts/startup/bl_ui/space_view3d_toolbar.py | 16 + source/blender/blenkernel/intern/dyntopo.cc | 907 ++++++------------ .../blenkernel/intern/dyntopo_intern.hh | 15 +- source/blender/blenlib/BLI_heap_minmax.hh | 115 ++- source/blender/editors/sculpt_paint/sculpt.cc | 14 +- .../editors/sculpt_paint/sculpt_api.cc | 39 +- .../editors/sculpt_paint/sculpt_curvature.cc | 8 +- .../editors/sculpt_paint/sculpt_detail.cc | 94 +- .../editors/sculpt_paint/sculpt_intern.hh | 9 +- 9 files changed, 527 insertions(+), 690 deletions(-) diff --git a/scripts/startup/bl_ui/space_view3d_toolbar.py b/scripts/startup/bl_ui/space_view3d_toolbar.py index be100e02cd4..84a27490824 100644 --- a/scripts/startup/bl_ui/space_view3d_toolbar.py +++ b/scripts/startup/bl_ui/space_view3d_toolbar.py @@ -949,10 +949,26 @@ class VIEW3D_PT_sculpt_dyntopo(Panel, View3DPaintPanel): "detail_range" ) + if 0: + scene = context.scene + def do_prop(name, text=None): + if text is None: text = name + + if name in scene: + sub.prop(scene, "[\"%s\"]" % name, text=text) + + do_prop("dparam1", text="p0") + do_prop("dparam2", text="p1") + do_prop("dparam3", text="p2") + do_prop("dparam4", text="p3") + do_prop("dparam5", text="p4") + if UnifiedPaintPanel.get_dyntopo_prop(context, brush, "mode") in {'CONSTANT', 'MANUAL'}: col.operator("sculpt.detail_flood_fill") col.prop(WindowManager.operator_properties_last("sculpt.detail_flood_fill"), "interactive") col.prop(WindowManager.operator_properties_last("sculpt.detail_flood_fill"), "developer") + if WindowManager.operator_properties_last("sculpt.detail_flood_fill").developer: + col.prop(WindowManager.operator_properties_last("sculpt.detail_flood_fill"), "emulate_brush") UnifiedPaintPanel.prop_unified_dyntopo( sub, diff --git a/source/blender/blenkernel/intern/dyntopo.cc b/source/blender/blenkernel/intern/dyntopo.cc index 475c2f5a620..6ee928051ff 100644 --- a/source/blender/blenkernel/intern/dyntopo.cc +++ b/source/blender/blenkernel/intern/dyntopo.cc @@ -48,6 +48,8 @@ #include +//#define CLEAR_TAGS_IN_THREAD + namespace blender::bke::dyntopo { using blender::float2; @@ -61,13 +63,6 @@ using namespace blender::bke::sculpt; static void pbvh_split_edges( struct EdgeQueueContext *eq_ctx, PBVH *pbvh, BMesh *bm, BMEdge **edges, int totedge); -extern "C" { -void bmesh_disk_edge_append(BMEdge *e, BMVert *v); -void bmesh_radial_loop_append(BMEdge *e, BMLoop *l); -void bm_kill_only_edge(BMesh *bm, BMEdge *e); -void bm_kill_only_loop(BMesh *bm, BMLoop *l); -void bm_kill_only_face(BMesh *bm, BMFace *f); -} static void edge_queue_create_local(EdgeQueueContext *eq_ctx, PBVH *pbvh, @@ -267,13 +262,6 @@ static PBVHTopologyUpdateMode edge_queue_test(EdgeQueueContext *eq_ctx, static void edge_queue_insert_unified(EdgeQueueContext *eq_ctx, BMEdge *e, float w) { if (!(e->head.hflag & BM_ELEM_TAG)) { - float len = len_v3v3(e->v1->co, e->v2->co); - - eq_ctx->avg_elen += len; - eq_ctx->min_elen = min_ff(eq_ctx->min_elen, len); - eq_ctx->max_elen = max_ff(eq_ctx->max_elen, len); - eq_ctx->totedge++; - eq_ctx->edge_heap.insert(w, e); e->head.hflag |= BM_ELEM_TAG; } @@ -412,44 +400,97 @@ float dist_to_tri_sphere_simple(float p[3], float v1[3], float v2[3], float v3[3 return dis; } -typedef struct EdgeQueueThreadData { - PBVH *pbvh; - PBVHNode *node; - BMEdge **edges; - EdgeQueueContext *eq_ctx; - int totedge; - int size; - bool is_collapse; - int seed; -} EdgeQueueThreadData; - -static void edge_thread_data_insert(EdgeQueueThreadData *tdata, BMEdge *e) +static bool skinny_bad_edge(BMEdge *e, const float limit = 3.0f) { - if (tdata->size <= tdata->totedge) { - tdata->size = (tdata->totedge + 1) << 1; - if (!tdata->edges) { - tdata->edges = (BMEdge **)MEM_mallocN(sizeof(void *) * tdata->size, - "edge_thread_data_insert"); + float len1 = len_v3v3(e->v1->co, e->v2->co); + + BMLoop *l = e->l; + do { + float len2 = len_v3v3(l->next->v->co, l->next->next->v->co); + if (len1 > 0.0f && len2 / len1 > limit) { + return true; } - else { - tdata->edges = (BMEdge **)MEM_reallocN(tdata->edges, sizeof(void *) * tdata->size); + + len2 = len_v3v3(l->v->co, l->prev->v->co); + if (len1 > 0.0f && len2 / len1 > limit) { + return true; } - } + } while ((l = l->radial_next) != e->l); - BMElem elem; - memcpy(&elem, (BMElem *)e, sizeof(BMElem)); - - elem.head.hflag = e->head.hflag | BM_ELEM_TAG; - int64_t iold = *((int64_t *)&e->head.index); - int64_t inew = *((int64_t *)&elem.head.index); - - atomic_cas_int64((int64_t *)&e->head.index, iold, inew); - - tdata->edges[tdata->totedge] = e; - tdata->totedge++; + return false; } -static void long_edge_queue_edge_add_recursive( +#if 0 +static void add_split_edge_recursive( + EdgeQueueContext *eq_ctx, BMLoop *l_edge, const float len_sq, float limit_len, int depth) +{ + struct StackItem { + BMLoop *l_edge; + float len_sq; + float limit_len; + int depth; + + StackItem(BMLoop *l, float len, float limit, int d) + : l_edge(l), len_sq(len), limit_len(limit), depth(d) + { + } + }; + + Vector stack; + stack.append(StackItem(l_edge, len_sq, limit_len, depth)); + + while (stack.size() > 0) { +# if 0 // stack + StackItem item = stack.pop_last(); +# else // queue + StackItem item = stack[0]; + stack.remove_and_reorder(0); +# endif + + int depth = item.depth; + float len_sq = item.len_sq, limit_len = item.limit_len; + BMLoop *l_edge = item.l_edge; + + if (depth > DEPTH_START_LIMIT && eq_ctx->use_view_normal) { + if (dot_v3v3(l_edge->f->no, eq_ctx->view_normal) < 0.0f) { + continue; + } + } + + if (!skinny_bad_edge(l_edge->e)) { + edge_queue_insert_unified(eq_ctx, l_edge->e, len_sq); + } + + if ((l_edge->radial_next != l_edge)) { + const float len_sq_cmp = len_sq * EVEN_EDGELEN_THRESHOLD; + + limit_len *= EVEN_GENERATION_SCALE; + const float limit_len_sq = square_f(limit_len); + + BMLoop *l_iter = l_edge; + do { + BMLoop *l_adjacent[2] = {l_iter->next, l_iter->prev}; + for (int i = 0; i < (int)ARRAY_SIZE(l_adjacent); i++) { + if (l_adjacent[i]->e->head.hflag & BM_ELEM_TAG) { + continue; + } + + float len_sq_other = calc_weighted_length( + eq_ctx, l_adjacent[i]->e->v1, l_adjacent[i]->e->v2, SPLIT); + + bool insert_ok = len_sq_other > max_ff(len_sq_cmp, limit_len_sq); + if (!insert_ok) { + continue; + } + + stack.append(StackItem(l_adjacent[i]->radial_next, len_sq_other, limit_len, depth + 1)); + } + } while ((l_iter = l_iter->radial_next) != l_edge); + } + } +} +#else +static void add_split_edge_recursive( EdgeQueueContext *eq_ctx, BMLoop *l_edge, const float len_sq, float limit_len, int depth) { if (depth > DEPTH_START_LIMIT && eq_ctx->use_view_normal) { @@ -458,7 +499,9 @@ static void long_edge_queue_edge_add_recursive( } } - edge_queue_insert_unified(eq_ctx, l_edge->e, len_sq); + if (!skinny_bad_edge(l_edge->e)) { + edge_queue_insert_unified(eq_ctx, l_edge->e, len_sq); + } if ((l_edge->radial_next != l_edge)) { const float len_sq_cmp = len_sq * EVEN_EDGELEN_THRESHOLD; @@ -482,20 +525,45 @@ static void long_edge_queue_edge_add_recursive( continue; } - long_edge_queue_edge_add_recursive( + add_split_edge_recursive( eq_ctx, l_adjacent[i]->radial_next, len_sq_other, limit_len, depth + 1); } } while ((l_iter = l_iter->radial_next) != l_edge); } } +#endif -static void long_edge_queue_edge_add_recursive_2(EdgeQueueThreadData *tdata, - BMLoop *l_edge, - BMLoop *l_end, - const float len_sq, - float limit_len, - int depth, - bool insert) +typedef struct EdgeQueueThreadData { + PBVH *pbvh; + PBVHNode *node; + Vector edges; + EdgeQueueContext *eq_ctx; + int size; + bool is_collapse; + int seed; +} EdgeQueueThreadData; + +static void edge_thread_data_insert(EdgeQueueThreadData *tdata, BMEdge *e) +{ + tdata->edges.append(e); + + BMElem elem; + memcpy(&elem, (BMElem *)e, sizeof(BMElem)); + + elem.head.hflag = e->head.hflag | BM_ELEM_TAG; + int64_t iold = *((int64_t *)&e->head.index); + int64_t inew = *((int64_t *)&elem.head.index); + + atomic_cas_int64((int64_t *)&e->head.index, iold, inew); +} + +static void add_split_edge_recursive_threaded(EdgeQueueThreadData *tdata, + BMLoop *l_edge, + BMLoop *l_end, + const float len_sq, + float limit_len, + int depth, + bool insert) { BLI_assert(len_sq > square_f(limit_len)); @@ -551,41 +619,28 @@ static void long_edge_queue_edge_add_recursive_2(EdgeQueueThreadData *tdata, } #endif - long_edge_queue_edge_add_recursive_2(tdata, - l_adjacent[i]->radial_next, - l_adjacent[i], - len_sq_other, - limit_len, - depth + 1, - insert_ok); + add_split_edge_recursive_threaded(tdata, + l_adjacent[i]->radial_next, + l_adjacent[i], + len_sq_other, + limit_len, + depth + 1, + insert_ok); } } while ((l_iter = l_iter->radial_next) != l_end); } } -BLI_INLINE int dyntopo_thread_rand(int seed) -{ - /* glibc linear congruel random number generator. */ - const uint32_t multiplier = 1103515245; - const uint32_t addend = 12345; - const uint32_t mask = (1 << 30) - 1; - - return (seed * multiplier + addend) & mask; -} - static void unified_edge_queue_task_cb(void *__restrict userdata, const int n, const TaskParallelTLS *__restrict /*tls*/) { + blender::RandomNumberGenerator rand(uint32_t(n + PIL_check_seconds_timer() * 100000.0f)); EdgeQueueThreadData *tdata = ((EdgeQueueThreadData *)userdata) + n; PBVH *pbvh = tdata->pbvh; PBVHNode *node = tdata->node; EdgeQueueContext *eq_ctx = tdata->eq_ctx; - RNG *rng = BLI_rng_new(POINTER_AS_UINT(tdata)); - int seed = tdata->seed + n; - - BMFace *f; bool do_smooth = eq_ctx->surface_smooth_fac > 0.0f; BKE_pbvh_bmesh_check_tris(pbvh, node); @@ -596,23 +651,31 @@ static void unified_edge_queue_task_cb(void *__restrict userdata, /* Only do reprojection if UVs exist. */ bool reproject_cdata = eq_ctx->reproject_cdata; - /* - * We care more about convergence to accurate results - * then accuracy in any individual runs. profiling - * has shown this loop overwhelms the L3 cache, - * so randomly skip bits of it. - */ +/* + * Clear edge flags. + * + * We care more about convergence to accurate results + * then accuracy in any individual runs. Profiling + * has shown this loop overwhelms the L3 cache, + * so randomly skip bits of it. + */ +#ifdef CLEAR_TAGS_IN_THREAD + BMFace *f; TGSET_ITER (f, node->bm_faces) { BMLoop *l = f->l_first; + /* Note that f itself is owned by this node. */ f->head.hflag &= ~facetag; /* Stochastically skip faces. */ - if ((seed = dyntopo_thread_rand(seed)) & 3) { + if (rand.get_uint32() > (1 << 16)) { continue; } + do { - /* Kind of tricky to atomicly update flags here. . . */ + /* Kind of tricky to atomicly update flags here. We probably + * don't need to do this on x86, but I'm not sure about ARM. + */ BMEdge edge = *l->e; edge.head.hflag &= ~BM_ELEM_TAG; @@ -621,11 +684,11 @@ static void unified_edge_queue_task_cb(void *__restrict userdata, atomic_cas_int64(t2, *t2, *t1); - l->e->head.hflag &= ~BM_ELEM_TAG; l = l->next; } while (l != f->l_first); } TGSET_ITER_END +#endif PBVHTriBuf *tribuf = node->tribuf; for (int i = 0; i < node->tribuf->tottri; i++) { @@ -663,9 +726,9 @@ static void unified_edge_queue_task_cb(void *__restrict userdata, * but tangentially to surface. We can stochastically skip this and still get the * benefit to convergence. */ - int randval = (seed = dyntopo_thread_rand(seed)) & 255; + int randval = rand.get_uint32(); - if (do_smooth && randval > 127) { + if (do_smooth && randval > (1 << 16)) { PBVHVertRef sv = {(intptr_t)l_iter->v}; surface_smooth_v_safe(eq_ctx->ss, tdata->pbvh, @@ -680,7 +743,7 @@ static void unified_edge_queue_task_cb(void *__restrict userdata, /* Subdivide walks the mesh a bit for better transitions in the topology. */ if (mode == PBVH_Subdivide) { - long_edge_queue_edge_add_recursive_2( + add_split_edge_recursive_threaded( tdata, l_iter->radial_next, l_iter, w, eq_ctx->limit_len_max, 0, true); } else if (mode == PBVH_Collapse) { @@ -689,11 +752,9 @@ static void unified_edge_queue_task_cb(void *__restrict userdata, } while ((l_iter = l_iter->next) != l_first); } } - - BLI_rng_free(rng); } -ATTR_NO_OPT bool check_face_is_tri(PBVH *pbvh, BMFace *f) +bool check_face_is_tri(PBVH *pbvh, BMFace *f) { #if DYNTOPO_DISABLE_FLAG & DYNTOPO_DISABLE_TRIANGULATOR return true; @@ -1051,7 +1112,7 @@ bool check_for_fins(PBVH *pbvh, BMVert *v) return false; } -ATTR_NO_OPT bool check_vert_fan_are_tris(PBVH *pbvh, BMVert *v) +bool check_vert_fan_are_tris(PBVH *pbvh, BMVert *v) { uint8_t *flag = BM_ELEM_CD_PTR(v, pbvh->cd_flag); @@ -1135,9 +1196,7 @@ static void unified_edge_queue_create(EdgeQueueContext *eq_ctx, continue; } - EdgeQueueThreadData td; - - memset(&td, 0, sizeof(td)); + EdgeQueueThreadData td = {}; td.seed = BLI_thread_rand(0); td.pbvh = pbvh; @@ -1145,15 +1204,6 @@ static void unified_edge_queue_create(EdgeQueueContext *eq_ctx, td.eq_ctx = eq_ctx; tdata.append(td); - - /* Check each face */ - /* - BMFace *f; - TGSET_ITER (f, node->bm_faces) { - long_edge_queue_face_add(eq_ctx, f); - } - TGSET_ITER_END - */ } int count = tdata.size(); @@ -1167,28 +1217,39 @@ static void unified_edge_queue_create(EdgeQueueContext *eq_ctx, settings.use_threading = false; #endif +#ifndef CLEAR_TAGS_IN_THREAD + for (int i : IndexRange(pbvh->totnode)) { + PBVHNode *node = &pbvh->nodes[i]; + + if (!(node->flag & PBVH_Leaf) || !(node->flag & PBVH_UpdateTopology)) { + continue; + } + + BMFace *f; + + TGSET_ITER (f, node->bm_faces) { + BMLoop *l = f->l_first; + do { + l->e->head.hflag &= ~BM_ELEM_TAG; + l->v->head.hflag &= ~BM_ELEM_TAG; + l->f->head.hflag &= ~(BM_ELEM_TAG | BM_ELEM_TAG_ALT); + } while ((l = l->next) != f->l_first); + } + TGSET_ITER_END; + } +#endif + BLI_task_parallel_range(0, count, (void *)tdata.data(), unified_edge_queue_task_cb, &settings); for (int i = 0; i < count; i++) { - EdgeQueueThreadData *td = &tdata[i]; - BMEdge **edges = td->edges; - - for (int j = 0; j < td->totedge; j++) { - BMEdge *e = edges[j]; - + for (BMEdge *e : tdata[i].edges) { e->head.hflag &= ~BM_ELEM_TAG; } } Vector verts; - for (int i = 0; i < count; i++) { - EdgeQueueThreadData *td = &tdata[i]; - - BMEdge **edges = td->edges; - for (int j = 0; j < td->totedge; j++) { - BMEdge *e = edges[j]; - + for (BMEdge *e : tdata[i].edges) { if (bm_elem_is_free((BMElem *)e, BM_EDGE)) { continue; } @@ -1196,7 +1257,7 @@ static void unified_edge_queue_create(EdgeQueueContext *eq_ctx, e->head.hflag &= ~BM_ELEM_TAG; if (e->l && e->l != e->l->radial_next->radial_next) { - /* Deal with non-manifold iffyness. */ + /* Fix non-manifold "fins". */ destroy_nonmanifold_fins(pbvh, e); push_subentry = true; @@ -1216,7 +1277,7 @@ static void unified_edge_queue_create(EdgeQueueContext *eq_ctx, if (eq_ctx->use_view_normal && (dot_v3v3(e->v1->no, eq_ctx->view_normal) < 0.0f && dot_v3v3(e->v2->no, eq_ctx->view_normal) < 0.0f)) { - return; + continue; } verts.append(e->v1); @@ -1230,13 +1291,9 @@ static void unified_edge_queue_create(EdgeQueueContext *eq_ctx, edge_queue_insert_unified(eq_ctx, e, w); } } - - MEM_SAFE_FREE(td->edges); } - for (int i = 0; i < verts.size(); i++) { - BMVert *v = verts[i]; - + for (BMVert *v : verts) { if (v->head.hflag & BM_ELEM_TAG) { v->head.hflag &= ~BM_ELEM_TAG; @@ -1244,6 +1301,10 @@ static void unified_edge_queue_create(EdgeQueueContext *eq_ctx, } } + for (BMEdge *e : eq_ctx->edge_heap.values()) { + e->head.hflag |= BM_ELEM_TAG; + } + if (push_subentry) { BM_log_entry_add_ex(pbvh->header.bm, pbvh->bm_log, true); } @@ -1322,21 +1383,13 @@ static void edge_queue_create_local(EdgeQueueContext *eq_ctx, Vector edges; for (int i = 0; i < count; i++) { - EdgeQueueThreadData *td = &tdata[i]; - - BMEdge **edges2 = td->edges; - for (int j = 0; j < td->totedge; j++) { - edges2[j]->head.hflag &= ~BM_ELEM_TAG; + for (BMEdge *e : tdata[i].edges) { + e->head.hflag &= ~BM_ELEM_TAG; } } for (int i = 0; i < count; i++) { - EdgeQueueThreadData *td = &tdata[i]; - - BMEdge **edges2 = td->edges; - for (int j = 0; j < td->totedge; j++) { - BMEdge *e = edges2[j]; - + for (BMEdge *e : tdata[i].edges) { e->v1->head.hflag &= ~BM_ELEM_TAG; e->v2->head.hflag &= ~BM_ELEM_TAG; @@ -1347,11 +1400,6 @@ static void edge_queue_create_local(EdgeQueueContext *eq_ctx, } } - for (int i = 0; i < count; i++) { - EdgeQueueThreadData *td = &tdata[i]; - MEM_SAFE_FREE(td->edges); - } - for (int i = 0; i < edges.size(); i++) { BMEdge *e = edges[i]; float len = len_v3v3(e->v1->co, e->v2->co); @@ -1371,6 +1419,7 @@ static void edge_queue_create_local(EdgeQueueContext *eq_ctx, } } } + e->head.index = i; lens.append(len); } @@ -1451,11 +1500,6 @@ static void edge_queue_create_local(EdgeQueueContext *eq_ctx, continue; } - /* Check seam/sharp flags here. */ - // if (!(e->head.hflag & BM_ELEM_SMOOTH) || e->head.hflag & BM_ELEM_SEAM) { - // continue; - // } - limit += lens[i]; tot += 1.0f; } @@ -1840,7 +1884,6 @@ static bool do_cleanup_3_4(EdgeQueueContext *eq_ctx, PBVH *pbvh) { bool modified = false; -#if 1 eq_ctx->used_verts.clear(); for (const PBVHNode &node : Span(pbvh->nodes, pbvh->totnode)) { @@ -1865,8 +1908,6 @@ static bool do_cleanup_3_4(EdgeQueueContext *eq_ctx, PBVH *pbvh) TGSET_ITER_END; } -#endif - BM_log_entry_add_ex(pbvh->header.bm, pbvh->bm_log, true); pbvh_bmesh_check_nodes(pbvh); @@ -1882,16 +1923,16 @@ float mask_cb_nop(PBVHVertRef /*vertex*/, void * /*userdata*/) return 1.0f; } -ATTR_NO_OPT EdgeQueueContext::EdgeQueueContext(BrushTester *brush_tester_, - struct SculptSession *ss_, - PBVH *pbvh_, - PBVHTopologyUpdateMode mode_, - bool use_frontface_, - float3 view_normal_, - bool updatePBVH_, - DyntopoMaskCB mask_cb_, - void *mask_cb_data_, - int edge_limit_multiply) +EdgeQueueContext::EdgeQueueContext(BrushTester *brush_tester_, + struct SculptSession *ss_, + PBVH *pbvh_, + PBVHTopologyUpdateMode mode_, + bool use_frontface_, + float3 view_normal_, + bool updatePBVH_, + DyntopoMaskCB mask_cb_, + void *mask_cb_data_, + int edge_limit_multiply) { ss = ss_; pbvh = pbvh_; @@ -1909,10 +1950,6 @@ ATTR_NO_OPT EdgeQueueContext::EdgeQueueContext(BrushTester *brush_tester_, cd_vert_mask_offset = pbvh->cd_vert_mask_offset; cd_vert_node_offset = pbvh->cd_vert_node_offset; cd_face_node_offset = pbvh->cd_face_node_offset; - avg_elen = 0.0f; - max_elen = -1e17; - min_elen = 1e17; - totedge = 0.0f; local_mode = false; mode = mode_; reproject_cdata = CustomData_has_layer(&pbvh->header.bm->ldata, CD_PROP_FLOAT2) && @@ -1949,31 +1986,6 @@ ATTR_NO_OPT EdgeQueueContext::EdgeQueueContext(BrushTester *brush_tester_, unified_edge_queue_create(this, pbvh, mode & (PBVH_LocalSubdivide | PBVH_LocalCollapse)); } -#ifdef SKINNY_EDGE_FIX - float ratio = 1.0f; - - /* Prevent remesher thrashing by throttling edge splitting in pathological case of skinny - * edges. - */ - float avg_elen = eq_ctx.avg_elen; - if (eq_ctx.totedge > 0.0f) { - avg_elen /= eq_ctx.totedge; - - float emax = eq_ctx.max_elen; - if (emax == 0.0f) { - emax = 0.0001f; - } - - if (pbvh->bm_min_edge_len > 0.0f && avg_elen > 0.0f) { - ratio = avg_elen / (pbvh->bm_min_edge_len * 0.5 + emax * 0.5); - ratio = MAX2(ratio, 0.25f); - ratio = MIN2(ratio, 5.0f); - } - } -#else -// const float ratio = 2.0; // 1.0f / 3.0f; -#endif - if ((mode & PBVH_Subdivide) && (mode & PBVH_Collapse)) { ops[0] = PBVH_Subdivide; ops[1] = PBVH_Collapse; @@ -1998,44 +2010,45 @@ ATTR_NO_OPT EdgeQueueContext::EdgeQueueContext(BrushTester *brush_tester_, max_steps = (DYNTOPO_MAX_ITER * edge_limit_multiply) << (totop - 1); max_subd = max_steps >> (totop - 1); - edges_size = steps[0]; - edges = (BMEdge **)MEM_malloc_arrayN(edges_size, sizeof(void *), __func__); + split_edges_size = steps[0]; + split_edges = (BMEdge **)MEM_malloc_arrayN(split_edges_size, sizeof(void *), __func__); etot = 0; - BLI_smallhash_init(&subd_edges); + subd_edges.clear(); } -ATTR_NO_OPT void EdgeQueueContext::flush_subdivision() +void EdgeQueueContext::flush_subdivision() { if (etot == 0) { return; } modified = true; - BLI_smallhash_clear(&subd_edges, 0); - pbvh_split_edges(this, pbvh, pbvh->header.bm, edges, etot); + subd_edges.clear(); + + pbvh_split_edges(this, pbvh, pbvh->header.bm, split_edges, etot); + count_subd += etot; VALIDATE_LOG(pbvh->bm_log); etot = 0; } -ATTR_NO_OPT EdgeQueueContext::~EdgeQueueContext() +EdgeQueueContext::~EdgeQueueContext() { - MEM_SAFE_FREE(edges); - BLI_smallhash_release(&subd_edges); + MEM_SAFE_FREE(split_edges); } -ATTR_NO_OPT void EdgeQueueContext::start() +void EdgeQueueContext::start() { current_i = 0; } -ATTR_NO_OPT bool EdgeQueueContext::done() +bool EdgeQueueContext::done() { return !(totop > 0 && !edge_heap.empty() && current_i < max_steps); } -ATTR_NO_OPT void EdgeQueueContext::finish() +void EdgeQueueContext::finish() { if (mode & PBVH_Cleanup) { modified |= do_cleanup_3_4(this, pbvh); @@ -2098,7 +2111,7 @@ ATTR_NO_OPT void EdgeQueueContext::finish() BM_log_entry_add_ex(pbvh->header.bm, pbvh->bm_log, true); } -ATTR_NO_OPT void EdgeQueueContext::step() +void EdgeQueueContext::step() { if (done()) { return; @@ -2150,8 +2163,6 @@ ATTR_NO_OPT void EdgeQueueContext::step() e = edge_heap.pop_max(&w); } - // printf("w: %.5f\n", calc_weighted_length(this, e->v1, e->v2, SPLIT)); - if (!e || bm_elem_is_free((BMElem *)e, BM_EDGE) || w < limit_len_max_sqr) { break; } @@ -2159,30 +2170,27 @@ ATTR_NO_OPT void EdgeQueueContext::step() e->head.hflag &= ~BM_ELEM_TAG; #if 0 - void **val = nullptr; - if (etot < edges_size && !BLI_smallhash_ensure_p(&subd_edges, (uintptr_t)e, &val)) { - *val = nullptr; - edges[etot++] = e; + if (subd_edges.add(e)) { + split_edges[etot++] = e; } #else - /* add complete triangles */ + /* Add complete faces. */ BMLoop *l = e->l; if (l) { do { BMLoop *l2 = l; do { - if (etot >= edges_size) { + if (etot >= split_edges_size) { break; } if (calc_weighted_length(this, l->e->v1, l->e->v2, SPLIT) < limit_len_max_sqr) { continue; } - void **val = nullptr; - if (!BLI_smallhash_ensure_p(&subd_edges, (uintptr_t)l->e, &val)) { - *val = nullptr; - edges[etot++] = e; + if (subd_edges.add(l->e)) { + l->e->head.hflag &= ~BM_ELEM_TAG; + split_edges[etot++] = l->e; } } while ((l2 = l2->next) != l); } while ((l = l->radial_next) != e->l); @@ -2239,7 +2247,7 @@ ATTR_NO_OPT void EdgeQueueContext::step() current_i++; } -ATTR_NO_OPT void EdgeQueueContext::report() +void EdgeQueueContext::report() { BMesh *bm = pbvh->header.bm; @@ -2271,16 +2279,16 @@ ATTR_NO_OPT void EdgeQueueContext::report() } // EdgeQueueContext -ATTR_NO_OPT bool remesh_topology(BrushTester *brush_tester, - struct SculptSession *ss, - PBVH *pbvh, - PBVHTopologyUpdateMode mode, - bool use_frontface, - float3 view_normal, - bool updatePBVH, - DyntopoMaskCB mask_cb, - void *mask_cb_data, - int edge_limit_multiply) +bool remesh_topology(BrushTester *brush_tester, + struct SculptSession *ss, + PBVH *pbvh, + PBVHTopologyUpdateMode mode, + bool use_frontface, + float3 view_normal, + bool updatePBVH, + DyntopoMaskCB mask_cb, + void *mask_cb_data, + int edge_limit_multiply) { EdgeQueueContext eq_ctx(brush_tester, ss, @@ -2302,344 +2310,6 @@ ATTR_NO_OPT bool remesh_topology(BrushTester *brush_tester, return eq_ctx.modified; } -#if 0 -/* Collapse short edges, subdivide long edges */ -bool remesh_topology(BrushTester *brush_tester, - struct SculptSession *ss, - PBVH *pbvh, - PBVHTopologyUpdateMode mode, - bool use_frontface, - float3 view_normal, - bool updatePBVH, - DyntopoMaskCB mask_cb, - void *mask_cb_data, - int edge_limit_multiply) -{ - /* Push a subentry. */ - BM_log_entry_add_ex(pbvh->header.bm, pbvh->bm_log, true); - - const int cd_vert_mask_offset = CustomData_get_offset(&pbvh->header.bm->vdata, CD_PAINT_MASK); - const int cd_vert_node_offset = pbvh->cd_vert_node_offset; - const int cd_face_node_offset = pbvh->cd_face_node_offset; - - bool modified = false; - - if (view_normal) { - BLI_assert(len_squared_v3(view_normal) != 0.0f); - } - - EdgeQueueContext eq_ctx = {}; - - eq_ctx.ss = ss; - eq_ctx.brush_tester = brush_tester; - eq_ctx.use_view_normal = use_frontface; - eq_ctx.view_normal = view_normal; - - eq_ctx.pool = nullptr; - eq_ctx.bm = pbvh->header.bm; - eq_ctx.mask_cb = mask_cb; - eq_ctx.mask_cb_data = mask_cb_data; - eq_ctx.view_normal = view_normal; - - eq_ctx.cd_vert_mask_offset = cd_vert_mask_offset; - eq_ctx.cd_vert_node_offset = cd_vert_node_offset; - eq_ctx.cd_face_node_offset = cd_face_node_offset; - eq_ctx.avg_elen = 0.0f; - eq_ctx.max_elen = -1e17; - eq_ctx.min_elen = 1e17; - eq_ctx.totedge = 0.0f; - eq_ctx.local_mode = false; - eq_ctx.mode = mode; - eq_ctx.reproject_cdata = CustomData_has_layer(&pbvh->header.bm->ldata, CD_PROP_FLOAT2) && - !ss->ignore_uvs; - - eq_ctx.max_heap_mm = (DYNTOPO_MAX_ITER * edge_limit_multiply) << 8; - eq_ctx.limit_len_min = pbvh->bm_min_edge_len; - eq_ctx.limit_len_max = pbvh->bm_max_edge_len; - eq_ctx.limit_len_min_sqr = eq_ctx.limit_len_min * eq_ctx.limit_len_min; - eq_ctx.limit_len_max_sqr = eq_ctx.limit_len_max * eq_ctx.limit_len_max; - eq_ctx.limit_mid = eq_ctx.limit_len_max * 0.5f + eq_ctx.limit_len_min * 0.5f; - - eq_ctx.surface_smooth_fac = DYNTOPO_SAFE_SMOOTH_FAC; - - if (mode & PBVH_LocalSubdivide) { - mode |= PBVH_Subdivide; - } - if (mode & PBVH_LocalSubdivide) { - mode |= PBVH_Collapse; - } - -# if DYNTOPO_DISABLE_FLAG & DYNTOPO_DISABLE_COLLAPSE - mode &= ~PBVH_Collapse; -# endif -# if DYNTOPO_DISABLE_FLAG & DYNTOPO_DISABLE_SPLIT_EDGES - mode &= ~PBVH_Subdivide; -# endif - - if (mode & (PBVH_Subdivide | PBVH_Collapse)) { - unified_edge_queue_create(&eq_ctx, pbvh, mode & (PBVH_LocalSubdivide | PBVH_LocalCollapse)); - } - -# ifdef SKINNY_EDGE_FIX - float ratio = 1.0f; - - /* Prevent remesher thrashing by throttling edge splitting in pathological case of skinny - * edges. - */ - float avg_elen = eq_ctx.avg_elen; - if (eq_ctx.totedge > 0.0f) { - avg_elen /= eq_ctx.totedge; - - float emax = eq_ctx.max_elen; - if (emax == 0.0f) { - emax = 0.0001f; - } - - if (pbvh->bm_min_edge_len > 0.0f && avg_elen > 0.0f) { - ratio = avg_elen / (pbvh->bm_min_edge_len * 0.5 + emax * 0.5); - ratio = MAX2(ratio, 0.25f); - ratio = MIN2(ratio, 5.0f); - } - } -# else -// const float ratio = 2.0; // 1.0f / 3.0f; -# endif - - int steps[2] = {0, 0}; - - PBVHTopologyUpdateMode ops[2]; - int totop = 0; - - if ((mode & PBVH_Subdivide) && (mode & PBVH_Collapse)) { - ops[0] = PBVH_Subdivide; - ops[1] = PBVH_Collapse; - totop = 2; - - steps[1] = DYNTOPO_MAX_ITER_COLLAPSE; - steps[0] = DYNTOPO_MAX_ITER_SUBD; - } - else if (mode & PBVH_Subdivide) { - ops[0] = PBVH_Subdivide; - totop = 1; - - steps[0] = DYNTOPO_MAX_ITER_SUBD; - } - else if (mode & PBVH_Collapse) { - ops[0] = PBVH_Collapse; - totop = 1; - - steps[0] = DYNTOPO_MAX_ITER_COLLAPSE; - } - - int max_steps = (DYNTOPO_MAX_ITER * edge_limit_multiply) << (totop - 1); - int max_subd = max_steps >> (totop - 1); - - int edges_size = steps[0]; - BMEdge **edges = (BMEdge **)MEM_malloc_arrayN(edges_size, sizeof(void *), __func__); - int etot = 0; - int count = 0, count_subd = 0; - int i = 0; - - int curop = 0; - float limit_len_subd = eq_ctx.limit_len_max_sqr; - float limit_len_cold = eq_ctx.limit_len_min_sqr; - - SmallHash subd_edges; - BLI_smallhash_init(&subd_edges); - - auto flush_subdivision = [&]() { - if (etot == 0) { - return; - } - - modified = true; - BLI_smallhash_clear(&subd_edges, 0); - pbvh_split_edges(&eq_ctx, pbvh, pbvh->header.bm, edges, etot, false); - count_subd += etot; - VALIDATE_LOG(pbvh->bm_log); - etot = 0; - }; - - while (totop > 0 && !eq_ctx.edge_heap.empty() && i < max_steps) { - BMEdge *e = nullptr; - - if (count >= steps[curop]) { - // BM_log_entry_add_ex(pbvh->header.bm, pbvh->bm_log, true); - - if (ops[curop] == PBVH_Subdivide && count_subd < max_subd) { - flush_subdivision(); - } - - curop = (curop + 1) % totop; - count = 0; - } - - if (curop == 0 && count_subd >= max_subd && totop > 1 && ops[0] == PBVH_Subdivide && - ops[1] == PBVH_Collapse) - { - curop = 1; - } - - switch (ops[curop]) { - case PBVH_Subdivide: { - if (eq_ctx.edge_heap.max_weight() < limit_len_subd) { - break; - } - - e = eq_ctx.edge_heap.pop_max(); - while (!eq_ctx.edge_heap.empty() && e && - (bm_elem_is_free((BMElem *)e, BM_EDGE) || - calc_weighted_length(&eq_ctx, e->v1, e->v2, SPLIT) < limit_len_subd)) - { - e = eq_ctx.edge_heap.pop_max(); - } - - if (!e || bm_elem_is_free((BMElem *)e, BM_EDGE)) { - break; - } - - /* add complete triangles */ - BMLoop *l = e->l; - if (l) { - do { - BMLoop *l2 = l; - do { - if (etot >= edges_size) { - break; - } - - void **val = nullptr; - - if (!BLI_smallhash_ensure_p(&subd_edges, (uintptr_t)l->e, &val)) { - *val = nullptr; - edges[etot++] = e; - } - } while ((l2 = l2->next) != l); - } while ((l = l->radial_next) != e->l); - } - - // edges[etot++] = e; - break; - } - case PBVH_Collapse: { - if (eq_ctx.edge_heap.min_weight() > limit_len_cold) { - break; - } - - e = eq_ctx.edge_heap.pop_min(); - while (!eq_ctx.edge_heap.empty() && e && - (bm_elem_is_free((BMElem *)e, BM_EDGE) || - calc_weighted_length(&eq_ctx, e->v1, e->v2, COLLAPSE) > limit_len_cold)) - { - e = eq_ctx.edge_heap.pop_min(); - } - - if (!e || bm_elem_is_free((BMElem *)e, BM_EDGE)) { - break; - } - - if (bm_elem_is_free((BMElem *)e->v1, BM_VERT) || bm_elem_is_free((BMElem *)e->v2, BM_VERT)) - { - printf("%s: error! operated on freed bmesh elements! e: %p, e->v1: %p, e->v2: %p\n", - __func__, - e, - e->v1, - e->v2); - break; - } - - modified = true; - pbvh_bmesh_collapse_edge(pbvh, e, e->v1, e->v2, nullptr, nullptr, &eq_ctx); - VALIDATE_LOG(pbvh->bm_log); - break; - } - case PBVH_LocalSubdivide: - case PBVH_LocalCollapse: - case PBVH_Cleanup: - BLI_assert_unreachable(); - break; - } - - if (eq_ctx.edge_heap.empty() && etot > 0) { - /* Flush subdivision, it may add more to queue.*/ - flush_subdivision(); - } - - count++; - i++; - } - - if (etot > 0) { - flush_subdivision(); - } - - MEM_SAFE_FREE(edges); - BLI_smallhash_release(&subd_edges); - - if (mode & PBVH_Cleanup) { - modified |= do_cleanup_3_4(&eq_ctx, pbvh); - - VALIDATE_LOG(pbvh->bm_log); - } - - if (modified) { - /* Avoid potential infinite loops. */ - const int totnode = pbvh->totnode; - - for (int i = 0; i < totnode; i++) { - PBVHNode *node = pbvh->nodes + i; - - if ((node->flag & PBVH_Leaf) && (node->flag & PBVH_UpdateTopology) && - !(node->flag & PBVH_FullyHidden)) - { - - /* do not clear PBVH_UpdateTopology here in case split messes with it */ - - /* Recursively split nodes that have gotten too many - * elements */ - if (updatePBVH) { - pbvh_bmesh_node_limit_ensure(pbvh, i); - } - } - } - } - - /* clear PBVH_UpdateTopology flags */ - for (int i = 0; i < pbvh->totnode; i++) { - PBVHNode *node = pbvh->nodes + i; - - if (!(node->flag & PBVH_Leaf)) { - continue; - } - - node->flag &= ~PBVH_UpdateTopology; - } - -# ifdef USE_VERIFY - pbvh_bmesh_verify(pbvh); -# endif - - /* Ensure triangulations are all up to date. */ - for (int i = 0; i < pbvh->totnode; i++) { - PBVHNode *node = pbvh->nodes + i; - - if (node->flag & PBVH_Leaf) { - pbvh_bmesh_check_other_verts(node); - BKE_pbvh_bmesh_check_tris(pbvh, node); - } - } - - if (modified) { - BKE_pbvh_update_bounds(pbvh, PBVH_UpdateBB | PBVH_UpdateOriginalBB); - } - - /* Push a subentry. */ - BM_log_entry_add_ex(pbvh->header.bm, pbvh->bm_log, true); - - return modified; -} -#endif - #define SPLIT_TAG BM_ELEM_TAG_ALT /* @@ -2776,7 +2446,9 @@ static const int splitmap[43][16] = { {6, -1, 3, -1, 5, -1, 1}, // 42 0 1 0 1 0 1 }; -ATTR_NO_OPT static void pbvh_split_edges( +float dyntopo_params[5] = {5.0f, 1.0f, 4.0f}; + +static void pbvh_split_edges( EdgeQueueContext *eq_ctx, PBVH *pbvh, BMesh *bm, BMEdge **edges1, int totedge) { bm_logstack_push(); @@ -2785,7 +2457,7 @@ ATTR_NO_OPT static void pbvh_split_edges( /* Try to improve quality by inserting new edge into queue. * This is a bit tricky since we don't want to expand outside * the brush radius too much, but we can't stay strictly inside - * either. + * either. We use tri_in_range for this. */ auto test_near_brush = [&](BMEdge *e, float *co, float *r_w = nullptr) { @@ -2794,20 +2466,23 @@ ATTR_NO_OPT static void pbvh_split_edges( *r_w = w; } - if (eq_ctx->brush_tester->is_sphere_or_tube) { - BrushSphere *sphere_test = static_cast(eq_ctx->brush_tester); - - float len = len_v3v3(co, sphere_test->center()); - float fac = 1.0f + powf((len / sphere_test->radius()), 5.0); - - if (len < sphere_test->radius() && w > eq_ctx->limit_len_max_sqr) { - return true; - } - - return (w * 2.0) / fac > eq_ctx->limit_len_max_sqr; + if (w == 0.0f || !e->l) { + return false; } - return true; + if (skinny_bad_edge(e)) { + return false; + } + + BMLoop *l = e->l; + do { + BMVert *vs[3] = {l->v, l->next->v, l->next->next->v}; + if (eq_ctx->brush_tester->tri_in_range(vs, l->f->no)) { + return true; + } + } while ((l = l->radial_next) != e->l); + + return false; }; BMEdge **edges = edges1; @@ -2823,9 +2498,8 @@ ATTR_NO_OPT static void pbvh_split_edges( for (int i = 0; i < totedge; i++) { BMEdge *e = edges[i]; - // XXX - // check_vert_fan_are_tris(pbvh, e->v1); - // check_vert_fan_are_tris(pbvh, e->v2); + check_vert_fan_are_tris(pbvh, e->v1); + check_vert_fan_are_tris(pbvh, e->v2); } for (int i = 0; i < totedge; i++) { @@ -2901,11 +2575,9 @@ ATTR_NO_OPT static void pbvh_split_edges( /* Build pattern mask and store in f->head.index. */ int mask = 0; int j = 0; - int count = 0; do { if (l->e->head.hflag & SPLIT_TAG) { mask |= 1 << j; - count++; } j++; @@ -2916,6 +2588,10 @@ ATTR_NO_OPT static void pbvh_split_edges( bm_log_message(" == split edges (edge split) == "); +#ifdef SUBD_ADD_TO_QUEUE + Vector new_edges; +#endif + for (int i = 0; i < totedge; i++) { BMEdge *e = edges[i]; @@ -2951,45 +2627,8 @@ ATTR_NO_OPT static void pbvh_split_edges( edge_queue_insert_val34_vert(eq_ctx, newv); #ifdef SUBD_ADD_TO_QUEUE - if (e->l) { - PBVHTopologyUpdateMode mode; - - bool ok = false; -# if 0 - ok = eq_ctx->brush_tester->vert_in_range(e->v1) || - eq_ctx->brush_tester->vert_in_range(e->v2) || - eq_ctx->brush_tester->vert_in_range(newe->v1) || - eq_ctx->brush_tester->vert_in_range(newe->v2); -# else - ok = test_near_brush(e, v1->co); - ok |= test_near_brush(e, v2->co); - ok |= test_near_brush(newe, newv->co); -# endif - - if (ok) { - float w1 = 0.0f, w2 = 0.0f; - - if ((mode = edge_queue_test(eq_ctx, pbvh, e, &w1)) != PBVH_None) { - e->head.hflag &= ~BM_ELEM_TAG; - if (e->l && mode == PBVH_Subdivide) { - long_edge_queue_edge_add_recursive(eq_ctx, e->l, w1, eq_ctx->limit_len_max, 0); - } - else { - edge_queue_insert_unified(eq_ctx, e, w1); - } - } - - if ((mode = edge_queue_test(eq_ctx, pbvh, newe, &w2)) != PBVH_None) { - newe->head.hflag &= ~BM_ELEM_TAG; - if (newe->l && mode == PBVH_Subdivide) { - long_edge_queue_edge_add_recursive(eq_ctx, newe->l, w2, eq_ctx->limit_len_max, 0); - } - else { - edge_queue_insert_unified(eq_ctx, newe, w2); - } - } - } - } + new_edges.append(e); + new_edges.append(newe); #endif PBVH_CHECK_NAN(newv->co); @@ -3213,16 +2852,7 @@ ATTR_NO_OPT static void pbvh_split_edges( exist_e->head.hflag &= ~BM_ELEM_TAG; #ifdef SUBD_ADD_TO_QUEUE - float3 co = exist_e->v1->co; - co = (co + exist_e->v2->co) * 0.5f; - - float w = 0.0f; - if (test_near_brush(exist_e, co, &w)) { - if (w >= eq_ctx->limit_len_max_sqr) { - // edge_queue_insert_unified(eq_ctx, exist_e, w); - long_edge_queue_edge_add_recursive(eq_ctx, exist_e->l, w, eq_ctx->limit_len_max, 0); - } - } + new_edges.append(exist_e); #endif check_face_is_manifold(pbvh, bm, newf); @@ -3292,6 +2922,25 @@ ATTR_NO_OPT static void pbvh_split_edges( BM_log_face_added(bm, pbvh->bm_log, f); } +#ifdef SUBD_ADD_TO_QUEUE + for (BMEdge *e : new_edges) { + float3 co = e->v1->co; + co = (co + e->v2->co) * 0.5f; + + float w = 0.0f; + w = calc_weighted_length(eq_ctx, e->v1, e->v2, SPLIT); + + if (test_near_brush(e, co)) { + if (w > eq_ctx->limit_len_max_sqr) { + add_split_edge_recursive(eq_ctx, e->l, w, eq_ctx->limit_len_max, 0); + } + else if (w < eq_ctx->limit_len_min_sqr) { + edge_queue_insert_unified(eq_ctx, e, w); + } + } + } +#endif + bm_logstack_pop(); } void detail_size_set(PBVH *pbvh, float detail_size, float detail_range) diff --git a/source/blender/blenkernel/intern/dyntopo_intern.hh b/source/blender/blenkernel/intern/dyntopo_intern.hh index 84dba0aadec..7d10fc6300a 100644 --- a/source/blender/blenkernel/intern/dyntopo_intern.hh +++ b/source/blender/blenkernel/intern/dyntopo_intern.hh @@ -9,6 +9,7 @@ #include "BLI_heap_minmax.hh" #include "BLI_math_vector_types.hh" #include "BLI_rand.hh" +#include "BLI_set.hh" #include "bmesh.h" #include "pbvh_intern.hh" @@ -121,7 +122,7 @@ inline bool bm_elem_is_free(BMElem *elem, int htype) #define EVEN_EDGELEN_THRESHOLD 1.2f /* How much the limit increases per recursion * (avoids performing subdivisions too far away). */ -#define EVEN_GENERATION_SCALE 1.05f +#define EVEN_GENERATION_SCALE 1.25f /* recursion depth to start applying front face test */ #define DEPTH_START_LIMIT 4 @@ -132,7 +133,7 @@ inline bool bm_elem_is_free(BMElem *elem, int htype) /* Slightly relax geometry by this factor along surface tangents * to improve convergence of dyntopo remesher. */ -#define DYNTOPO_SAFE_SMOOTH_FAC 0.015f +#define DYNTOPO_SAFE_SMOOTH_FAC 0.01f #ifdef USE_EDGEQUEUE_EVEN_SUBDIV # include "BKE_global.h" @@ -218,6 +219,8 @@ static void pbvh_bmesh_verify(PBVH *pbvh); struct EdgeQueue; +extern float dyntopo_params[5]; + struct EdgeQueueContext { blender::bke::dyntopo::BrushTester *brush_tester; SculptSession *ss; @@ -261,10 +264,11 @@ struct EdgeQueueContext { int current_i = 0; int max_steps; PBVH *pbvh; - BMEdge **edges; + BMEdge **split_edges; + int split_edges_size; int etot; - int edges_size; - SmallHash subd_edges; + blender::Set subd_edges; + bool modified = false; int count_subd = 0; int count = 0; @@ -302,6 +306,7 @@ struct EdgeQueueContext { } blender::RandomNumberGenerator rand; + private: bool flushed_ = false; }; diff --git a/source/blender/blenlib/BLI_heap_minmax.hh b/source/blender/blenlib/BLI_heap_minmax.hh index 75bafd44ba3..1476455aa6e 100644 --- a/source/blender/blenlib/BLI_heap_minmax.hh +++ b/source/blender/blenlib/BLI_heap_minmax.hh @@ -17,10 +17,55 @@ using blender::Vector; #include namespace blender { +template class HeapValueIter { + Span nodes_; + int i_ = 0; + + public: + HeapValueIter(Vector &nodes) : nodes_(nodes) {} + HeapValueIter(Span &nodes) : nodes_(nodes) {} + HeapValueIter(const HeapValueIter &b) : nodes_(b.nodes_), i_(b.i_) {} + + bool operator==(const HeapValueIter &b) + { + return b.i_ == i_; + } + + bool operator!=(const HeapValueIter &b) + { + return b.i_ != i_; + } + + Value operator*() + { + return nodes_[i_].value; + } + + HeapValueIter &operator++() + { + i_++; + + return *this; + } + + HeapValueIter begin() + { + return HeapValueIter(nodes_); + } + + HeapValueIter end() + { + HeapValueIter ret(nodes_); + ret.i_ = std::max(int(nodes_.size()) - 1, 0); + + return ret; + } +}; + template -#if 1 +#if 0 class MinMaxHeap { struct MinMaxHeapNode { Value value; @@ -31,7 +76,7 @@ class MinMaxHeap { public: MinMaxHeap(int reserved = 0) {} - ATTR_NO_OPT MinMaxHeapNode *insert(float weight, Value value) + MinMaxHeapNode *insert(float weight, Value value) { nodes.resize(nodes.size() + 1); MinMaxHeapNode *node = &nodes.last(); @@ -81,7 +126,7 @@ class MinMaxHeap { return max()->weight; } - ATTR_NO_OPT void pop_node(MinMaxHeapNode *node) + void pop_node(MinMaxHeapNode *node) { int i = node - nodes.data(); @@ -125,6 +170,11 @@ class MinMaxHeap { return nodes.size() == 0; } + HeapValueIter values() + { + return HeapValueIter(nodes); + } + private: Vector nodes; }; @@ -148,6 +198,11 @@ class MinMaxHeap { ~MinMaxHeap() {} + HeapValueIter values() + { + return HeapValueIter(nodes); + } + MinMaxHeapNode *insert(float weight, Value value) { MinMaxHeapNode *node = heap_make_node(); @@ -206,7 +261,7 @@ class MinMaxHeap { float max_weight() { - return max()->weight; + return max().weight; } Value pop_min() @@ -222,10 +277,10 @@ class MinMaxHeap { # endif Value ret = nodes[0].value; - MinMaxHeapNode *last = heap_pop_last(); + MinMaxHeapNode last = heap_pop_last(); - std::swap(last->weight, nodes[0].weight); - std::swap(last->value, nodes[0].value); + nodes[0].weight = last.weight; + nodes[1].value = last.value; heap_push_down(&nodes[0]); @@ -247,7 +302,7 @@ class MinMaxHeap { return nodes.pop_last().value; } - MinMaxHeapNode *node = max(); + MinMaxHeapNode &node = max(); # ifdef BLI_MINMAX_HEAP_VALIDATE if (!is_valid()) { @@ -255,17 +310,17 @@ class MinMaxHeap { } # endif - Value ret = node->value; + Value ret = node.value; if (r_w) { - *r_w = node->weight; + *r_w = node.weight; } - MinMaxHeapNode *last = heap_pop_last(); + MinMaxHeapNode last = heap_pop_last(); - node->weight = last->weight; - node->value = last->value; + node.weight = last.weight; + node.value = last.value; - node = heap_push_down(node); + heap_push_down(&node); # ifdef BLI_MINMAX_HEAP_VALIDATE if (!is_valid()) { @@ -298,30 +353,30 @@ class MinMaxHeap { } private: - MinMaxHeapNode *min() + MinMaxHeapNode &min() { - return &nodes[0]; + return nodes[0]; } - MinMaxHeapNode *max() + MinMaxHeapNode &max() { if (nodes.size() == 1) { - return &nodes[0]; + return nodes[0]; } MinMaxHeapNode &root = nodes[0]; if (root.child1 != -1 && root.child2 != -1) { if (nodes[nodes[0].child1].weight > nodes[nodes[0].child2].weight) { - return &nodes[nodes[0].child1]; + return nodes[nodes[0].child1]; } - return &nodes[nodes[0].child2]; + return nodes[nodes[0].child2]; } else if (root.child1 != -1) { - return &nodes[nodes[0].child1]; + return nodes[nodes[0].child1]; } else { - return &nodes[nodes[0].child2]; + return nodes[nodes[0].child2]; } } @@ -586,22 +641,22 @@ class MinMaxHeap { return node; } - MinMaxHeapNode *heap_pop_last() + MinMaxHeapNode heap_pop_last() { - MinMaxHeapNode *last = &nodes[nodes.size() - 1]; + int index = nodes.size() - 1; - if (last->parent) { - MinMaxHeapNode *parent = &nodes[last->parent]; - if (parent->child1 == nodes.size() - 1) { - parent->child1 = -1; + MinMaxHeapNode last = nodes[index]; + if (last.parent != -1) { + MinMaxHeapNode &parent = nodes[last.parent]; + if (parent.child1 == index) { + parent.child1 = -1; } else { - parent->child2 = -1; + parent.child2 = -1; } } nodes.pop_last(); - return last; } diff --git a/source/blender/editors/sculpt_paint/sculpt.cc b/source/blender/editors/sculpt_paint/sculpt.cc index da9a266bc3c..7288426a0b2 100644 --- a/source/blender/editors/sculpt_paint/sculpt.cc +++ b/source/blender/editors/sculpt_paint/sculpt.cc @@ -98,10 +98,10 @@ static bool is_realtime_restored(Brush *brush) (brush->flag & BRUSH_DRAG_DOT)); } -static float sculpt_calc_radius(ViewContext *vc, - const Brush *brush, - const Scene *scene, - const float3 location) +float SCULPT_calc_radius(ViewContext *vc, + const Brush *brush, + const Scene *scene, + const float3 location) { if (!BKE_brush_use_locked_size(scene, brush)) { return paint_calc_object_space_radius(vc, location, BKE_brush_size_get(scene, brush)); @@ -5285,7 +5285,7 @@ static void sculpt_update_cache_invariants( BKE_sculpt_ensure_origmask(ob); } - SCULPT_apply_dyntopo_settings(ss, sd, brush); + SCULPT_apply_dyntopo_settings(CTX_data_scene(C), ss, sd, brush); BKE_pbvh_update_bounds(ss->pbvh, PBVH_UpdateBB | PBVH_UpdateOriginalBB); } @@ -5583,7 +5583,7 @@ static void sculpt_update_cache_variants(bContext *C, Sculpt *sd, Object *ob, Po /* Truly temporary data that isn't stored in properties. */ if (SCULPT_stroke_is_first_brush_step_of_symmetry_pass(ss->cache)) { - cache->initial_radius = sculpt_calc_radius(cache->vc, brush, scene, cache->true_location); + cache->initial_radius = SCULPT_calc_radius(cache->vc, brush, scene, cache->true_location); if (!BKE_brush_use_locked_size(scene, brush)) { BKE_brush_unprojected_radius_set(scene, brush, cache->initial_radius); @@ -6037,7 +6037,7 @@ bool SCULPT_stroke_get_location_ex(bContext *C, float closest_radius_sq = FLT_MAX; if (limit_closest_radius) { - closest_radius_sq = sculpt_calc_radius(&vc, brush, CTX_data_scene(C), out); + closest_radius_sq = SCULPT_calc_radius(&vc, brush, CTX_data_scene(C), out); closest_radius_sq *= closest_radius_sq; } diff --git a/source/blender/editors/sculpt_paint/sculpt_api.cc b/source/blender/editors/sculpt_paint/sculpt_api.cc index 77e92331cd7..31a927892c3 100644 --- a/source/blender/editors/sculpt_paint/sculpt_api.cc +++ b/source/blender/editors/sculpt_paint/sculpt_api.cc @@ -56,6 +56,7 @@ #include "BKE_ccg.h" #include "BKE_colortools.h" #include "BKE_context.h" +#include "BKE_idprop.h" #include "BKE_image.h" #include "BKE_key.h" #include "BKE_lib_id.h" @@ -75,7 +76,6 @@ #include "BKE_scene.h" #include "BKE_subdiv_ccg.h" #include "BKE_subsurf.h" - #include "NOD_texture.h" #include "DEG_depsgraph.h" @@ -602,8 +602,43 @@ void SCULPT_ensure_persistent_layers(SculptSession *ss, Object *ob) } } -void SCULPT_apply_dyntopo_settings(SculptSession *ss, Sculpt *sculpt, Brush *brush) +namespace blender::bke::dyntopo { +extern float dyntopo_params[5]; +} + +void SCULPT_apply_dyntopo_settings(Scene *scene, SculptSession *ss, Sculpt *sculpt, Brush *brush) { + using namespace blender::bke::dyntopo; + if (scene->id.properties) { + auto load_prop = [&](const char *name, int index) { + IDProperty *prop = IDP_GetPropertyFromGroup(scene->id.properties, name); + if (prop) { + float val = 0.0f; + if (prop->type == IDP_FLOAT) { + val = IDP_Float(prop); + } + else if (prop->type == IDP_DOUBLE) { + val = float(IDP_Double(prop)); + } + else { + return prop; + } + + printf("%s: Found %s (%.5f)\n", __func__, name, val); + + dyntopo_params[index] = val; + } + + return prop; + }; + + IDProperty *prop1 = load_prop("dparam1", 0); + load_prop("dparam2", 1); + load_prop("dparam3", 2); + load_prop("dparam4", 3); + load_prop("dparam5", 4); + } + if (!brush) { ss->cached_dyntopo = sculpt->dyntopo; return; diff --git a/source/blender/editors/sculpt_paint/sculpt_curvature.cc b/source/blender/editors/sculpt_paint/sculpt_curvature.cc index 9fc7763aff8..6d4b6297547 100644 --- a/source/blender/editors/sculpt_paint/sculpt_curvature.cc +++ b/source/blender/editors/sculpt_paint/sculpt_curvature.cc @@ -141,10 +141,10 @@ BLI_INLINE void normal_covariance(float mat[3][3], float no[3]) mat[2][2] = no[2] * no[2]; } -ATTR_NO_OPT bool SCULPT_calc_principle_curvatures(SculptSession *ss, - PBVHVertRef vertex, - SculptCurvatureData *out, - bool useAccurateSolver) +bool SCULPT_calc_principle_curvatures(SculptSession *ss, + PBVHVertRef vertex, + SculptCurvatureData *out, + bool useAccurateSolver) { SculptVertexNeighborIter ni; float nmat[3][3], nmat2[3][3]; diff --git a/source/blender/editors/sculpt_paint/sculpt_detail.cc b/source/blender/editors/sculpt_paint/sculpt_detail.cc index 9a3ed089613..3fe64b9ddfa 100644 --- a/source/blender/editors/sculpt_paint/sculpt_detail.cc +++ b/source/blender/editors/sculpt_paint/sculpt_detail.cc @@ -87,10 +87,12 @@ static bool sculpt_and_dynamic_topology_poll(bContext *C) /* -------------------------------------------------------------------- */ /** \name Detail Flood Fill * \{ */ -static int sculpt_detail_flood_fill_run(Object *ob, - Sculpt *sd, +static int sculpt_detail_flood_fill_run(Scene *scene, + Object *ob, + ToolSettings *tool_settings, Brush *brush, wmOperator *op, + ViewContext *vc, std::function lock_main_thread, std::function unlock_main_thread, std::function update_main_thread, @@ -102,6 +104,8 @@ static int sculpt_detail_flood_fill_run(Object *ob, bool developer_mode = RNA_boolean_get(op->ptr, "developer"); SculptSession *ss = ob->sculpt; + Sculpt *sd = tool_settings->sculpt; + Vector nodes = blender::bke::pbvh::search_gather(ss->pbvh, nullptr, nullptr); if (nodes.is_empty()) { @@ -109,7 +113,7 @@ static int sculpt_detail_flood_fill_run(Object *ob, return OPERATOR_CANCELLED; } - SCULPT_apply_dyntopo_settings(ss, sd, brush); + SCULPT_apply_dyntopo_settings(scene, ss, sd, brush); float detail_range = ss->cached_dyntopo.detail_range; /* Update topology size. */ @@ -139,17 +143,57 @@ static int sculpt_detail_flood_fill_run(Object *ob, unlock_main_thread(); + float radius; + float3 center; + bool emulate_brush = RNA_boolean_get(op->ptr, "emulate_brush") && developer_mode && vc; + + if (emulate_brush && vc) { + // ViewContext + + if (tool_settings->unified_paint_settings.average_stroke_counter) { + center = float3(tool_settings->unified_paint_settings.average_stroke_accum) / + float(tool_settings->unified_paint_settings.average_stroke_counter); + } + else { + zero_v3(center); + } + + radius = SCULPT_calc_radius(vc, brush, vc->scene, center); + printf("radius: %.5f", radius); + } + int repeat = ss->cached_dyntopo.repeat; for (int i = 0; i < 1 + repeat; i++) { lock_main_thread(); + BrushNoRadius null_brush_tester; + BrushSphere sphere_brush_tester(center, radius); + BrushTester *tester; + nodes = blender::bke::pbvh::search_gather(ss->pbvh, nullptr, nullptr); + + if (!emulate_brush) { + tester = &null_brush_tester; + } + else { + tester = &sphere_brush_tester; + +#if 0 + SculptSearchSphereData data{}; + data.sd = sd; + data.radius_squared = radius*radius; + data.original = false; + data.center = center; + + nodes = blender::bke::pbvh::search_gather(ss->pbvh, SCULPT_search_sphere_cb, &data); +#endif + } + for (int j = 0; j < nodes.size(); j++) { BKE_pbvh_node_mark_topology_update(nodes[j]); } - blender::bke::dyntopo::BrushNoRadius brush_tester; - EdgeQueueContext remesher(&brush_tester, + EdgeQueueContext remesher(tester, ss, ss->pbvh, mode, @@ -209,10 +253,12 @@ static int sculpt_detail_flood_fill_exec(bContext *C, wmOperator *op) SCULPT_undo_push_node(ob, nullptr, SCULPT_UNDO_COORDS); int ret = sculpt_detail_flood_fill_run( + CTX_data_scene(C), CTX_data_active_object(C), - sd, + CTX_data_tool_settings(C), BKE_paint_brush(&sd->paint), op, + nullptr, []() {}, []() {}, []() {}, @@ -232,7 +278,8 @@ struct FloodFillJob { wmOperator *op; bContext *C; Brush *brush; - Sculpt *sd; + ToolSettings *tool_settings; + ViewContext vc; }; static FloodFillJob flood_fill_job; @@ -254,10 +301,12 @@ static void start_fill_job(void * /*custom_data*/, break; } - if (sculpt_detail_flood_fill_run(flood_fill_job.ob, - flood_fill_job.sd, + if (sculpt_detail_flood_fill_run(flood_fill_job.scene, + flood_fill_job.ob, + flood_fill_job.tool_settings, flood_fill_job.brush, flood_fill_job.op, + &flood_fill_job.vc, lock_main_thread, unlock_main_thread, update_main_thread, @@ -280,7 +329,7 @@ static void end_fill_job(void *) printf("End fill job\n"); } -static void flood_fill_free(void *customdata) +static void flood_fill_free(void *) { printf("%s: detail flood fill free.\n", __func__); } @@ -298,14 +347,33 @@ int sculpt_detail_flood_fill_invoke(bContext *C, wmOperator *op, const wmEvent * SCULPT_undo_push_begin(ob, op); SCULPT_undo_push_node(ob, nullptr, SCULPT_UNDO_COORDS); - flood_fill_job.sd = CTX_data_tool_settings(C)->sculpt; - flood_fill_job.brush = BKE_paint_brush(&flood_fill_job.sd->paint); + flood_fill_job.tool_settings = CTX_data_tool_settings(C); + flood_fill_job.brush = BKE_paint_brush(&flood_fill_job.tool_settings->sculpt->paint); flood_fill_job.ob = CTX_data_active_object(C); flood_fill_job.op = op; flood_fill_job.C = C; flood_fill_job.scene = CTX_data_scene(C); flood_fill_job.depsgraph = CTX_data_ensure_evaluated_depsgraph(C); + ED_view3d_viewcontext_init(C, &flood_fill_job.vc, flood_fill_job.depsgraph); + if (!flood_fill_job.vc.rv3d) { + LISTBASE_FOREACH (ScrArea *, area, &CTX_wm_screen(C)->areabase) { + if (area->spacetype == SPACE_VIEW3D) { + LISTBASE_FOREACH (ARegion *, region, &area->regionbase) { + if (region->regiontype == RGN_TYPE_WINDOW) { + flood_fill_job.vc.region = region; + flood_fill_job.vc.rv3d = static_cast(region->regiondata); + break; + } + } + break; + } + + if (flood_fill_job.vc.rv3d) { + break; + } + } + } flood_fill_job.job = WM_jobs_get(CTX_wm_manager(C), CTX_wm_window(C), static_cast(&flood_fill_job), @@ -357,6 +425,8 @@ void SCULPT_OT_detail_flood_fill(wmOperatorType *ot) RNA_def_boolean(ot->srna, "interactive", true, "Interactive", "Interactive mode"); RNA_def_boolean(ot->srna, "developer", false, "Developer", "Developer mode"); + RNA_def_boolean( + ot->srna, "emulate_brush", false, "Emulate Brush", "Use last brush position and radius"); } /** \} */ diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.hh b/source/blender/editors/sculpt_paint/sculpt_intern.hh index 3f3c7cc1aee..278214acce9 100644 --- a/source/blender/editors/sculpt_paint/sculpt_intern.hh +++ b/source/blender/editors/sculpt_paint/sculpt_intern.hh @@ -26,6 +26,7 @@ #include "BLI_compiler_compat.h" #include "BLI_gsqueue.h" #include "BLI_implicit_sharing.hh" +#include "BLI_math_vector_types.hh" #include "BLI_span.hh" #include "BLI_threads.h" #include "BLI_utildefines.h" @@ -67,6 +68,7 @@ used by various code that needs to statically store per-pass state. */ #define SCULPT_MAX_SYMMETRY_PASSES 255 +using blender::float3; using blender::Span; using blender::Vector; @@ -1541,6 +1543,11 @@ void SCULPT_flip_quat_by_symm_area(float quat[4], * #PAINT_FALLOFF_SHAPE_NOOP */ +float SCULPT_calc_radius(ViewContext *vc, + const Brush *brush, + const Scene *scene, + const float3 location); + SculptBrushTestFn SCULPT_brush_test_init(const SculptSession *ss, SculptBrushTest *test); SculptBrushTestFn SCULPT_brush_test_init_ex(const SculptSession *ss, SculptBrushTest *test, @@ -2099,7 +2106,7 @@ void SCULPT_OT_detail_flood_fill(wmOperatorType *ot); void SCULPT_OT_sample_detail_size(wmOperatorType *ot); void SCULPT_OT_set_detail_size(wmOperatorType *ot); void SCULPT_OT_dyntopo_detail_size_edit(wmOperatorType *ot); -void SCULPT_apply_dyntopo_settings(SculptSession *ss, Sculpt *sculpt, Brush *brush); +void SCULPT_apply_dyntopo_settings(Scene *scene, SculptSession *ss, Sculpt *sculpt, Brush *brush); /** \} */ /* Dyntopo. */ -- 2.30.2 From d69a80cf5ba2468080a922a26816172bff935504 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Thu, 25 May 2023 16:10:44 -0700 Subject: [PATCH 120/279] temp-sculpt-dyntopo: Fix automasking bug --- source/blender/blenkernel/intern/dyntopo.cc | 21 ++++++++++++++++--- .../blenkernel/intern/dyntopo_intern.hh | 2 +- source/blender/blenlib/BLI_heap_minmax.hh | 2 +- 3 files changed, 20 insertions(+), 5 deletions(-) diff --git a/source/blender/blenkernel/intern/dyntopo.cc b/source/blender/blenkernel/intern/dyntopo.cc index 6ee928051ff..cd628611026 100644 --- a/source/blender/blenkernel/intern/dyntopo.cc +++ b/source/blender/blenkernel/intern/dyntopo.cc @@ -231,8 +231,15 @@ BLI_INLINE float calc_weighted_length(EdgeQueueContext *eq_ctx, float w = 1.0 - maskcb_get(eq_ctx, v1, v2); float len = len_squared_v3v3(v1->co, v2->co); - w = 1.0 + w * float(mode); - return len * w; + switch (mode) { + case SPLIT: + w = 1.0 + w * float(mode); + break; + case COLLAPSE: + w = 1.0 + 2.0 * w * float(mode); + break; + } + return len * w * w; } static PBVHTopologyUpdateMode edge_queue_test(EdgeQueueContext *eq_ctx, @@ -400,7 +407,7 @@ float dist_to_tri_sphere_simple(float p[3], float v1[3], float v2[3], float v3[3 return dis; } -static bool skinny_bad_edge(BMEdge *e, const float limit = 3.0f) +static bool skinny_bad_edge(BMEdge *e, const float limit = 4.0f) { float len1 = len_v3v3(e->v1->co, e->v2->co); @@ -2461,6 +2468,14 @@ static void pbvh_split_edges( */ auto test_near_brush = [&](BMEdge *e, float *co, float *r_w = nullptr) { +#if 0 + const int vlimit = 8; + if (BM_ELEM_CD_GET_INT(e->v1, pbvh->cd_valence) > vlimit || + BM_ELEM_CD_GET_INT(e->v2, pbvh->cd_valence) > vlimit) + { + return true; + } +#endif float w = calc_weighted_length(eq_ctx, e->v1, e->v2, SPLIT); if (r_w) { *r_w = w; diff --git a/source/blender/blenkernel/intern/dyntopo_intern.hh b/source/blender/blenkernel/intern/dyntopo_intern.hh index 7d10fc6300a..6437ec9a7bb 100644 --- a/source/blender/blenkernel/intern/dyntopo_intern.hh +++ b/source/blender/blenkernel/intern/dyntopo_intern.hh @@ -122,7 +122,7 @@ inline bool bm_elem_is_free(BMElem *elem, int htype) #define EVEN_EDGELEN_THRESHOLD 1.2f /* How much the limit increases per recursion * (avoids performing subdivisions too far away). */ -#define EVEN_GENERATION_SCALE 1.25f +#define EVEN_GENERATION_SCALE 1.15f /* recursion depth to start applying front face test */ #define DEPTH_START_LIMIT 4 diff --git a/source/blender/blenlib/BLI_heap_minmax.hh b/source/blender/blenlib/BLI_heap_minmax.hh index 1476455aa6e..366b66a71e2 100644 --- a/source/blender/blenlib/BLI_heap_minmax.hh +++ b/source/blender/blenlib/BLI_heap_minmax.hh @@ -65,7 +65,7 @@ template class HeapValueIter { template -#if 0 +#if 1 class MinMaxHeap { struct MinMaxHeapNode { Value value; -- 2.30.2 From b1fd3ad48fcbde9c3eba00477b7d37e0b175f8d4 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Fri, 26 May 2023 03:41:43 -0700 Subject: [PATCH 121/279] temp-sculpt-dyntopo: Dyntopo cleanup and bugfixes * Remove BMTracer callback system for BM_edge_collapse * Fix mask/automasking weighting for dyntopo. * Fix bug in minmax heap --- source/blender/blenkernel/intern/dyntopo.cc | 103 +++++++++++- .../blenkernel/intern/dyntopo_collapse.cc | 42 ++--- .../blenkernel/intern/mesh_remesh_voxel.cc | 2 +- source/blender/blenkernel/intern/pbvh.cc | 61 ++++++- .../blender/blenkernel/intern/pbvh_bmesh.cc | 26 +-- .../blender/blenkernel/intern/pbvh_intern.hh | 4 +- source/blender/blenlib/BLI_heap_minmax.hh | 151 ++++++++++++------ source/blender/bmesh/intern/bmesh_core.c | 130 ++------------- source/blender/bmesh/intern/bmesh_core.h | 3 +- source/blender/bmesh/intern/bmesh_mods.c | 5 +- source/blender/bmesh/intern/bmesh_mods.h | 3 +- source/blender/bmesh/operators/bmo_extrude.c | 10 +- .../blender/bmesh/operators/bmo_fill_grid.c | 2 +- .../blender/bmesh/operators/bmo_primitive.c | 4 +- .../bmesh/operators/bmo_removedoubles.c | 2 +- .../blender/editors/mesh/editmesh_polybuild.c | 4 +- 16 files changed, 302 insertions(+), 250 deletions(-) diff --git a/source/blender/blenkernel/intern/dyntopo.cc b/source/blender/blenkernel/intern/dyntopo.cc index cd628611026..d94102fb1e9 100644 --- a/source/blender/blenkernel/intern/dyntopo.cc +++ b/source/blender/blenkernel/intern/dyntopo.cc @@ -77,6 +77,10 @@ static void surface_smooth_v_safe( float tan[3]; float tot = 0.0; + if (BM_ELEM_CD_GET_INT(v, pbvh->cd_valence) > 12) { + return; + } + PBVHVertRef vertex = {reinterpret_cast(v)}; if (stroke_id_test_no_update(ss, vertex, STROKEID_USER_ORIGINAL)) { copy_v3_v3(origco1, v->co); @@ -234,12 +238,26 @@ BLI_INLINE float calc_weighted_length(EdgeQueueContext *eq_ctx, switch (mode) { case SPLIT: w = 1.0 + w * float(mode); - break; - case COLLAPSE: - w = 1.0 + 2.0 * w * float(mode); - break; + return len > eq_ctx->limit_len_max_sqr ? len * w * w : len; + case COLLAPSE: { +#if 0 + if (eq_ctx->brush_tester->is_sphere_or_tube) { + BrushSphere *sphere = static_cast(eq_ctx->brush_tester); + + float l1 = len_v3v3(v1->co, sphere->center()); + float l2 = len_v3v3(v2->co, sphere->center()); + float l = min_ff(min_ff(l1, l2) / sphere->radius(), 1.0f); + } +#endif + + return len < eq_ctx->limit_len_min_sqr ? + len + eq_ctx->limit_len_min_sqr * 1.0f * powf(w, 5.0) : + len; + } } - return len * w * w; + + BLI_assert_unreachable(); + return 0.0f; } static PBVHTopologyUpdateMode edge_queue_test(EdgeQueueContext *eq_ctx, @@ -1120,6 +1138,68 @@ bool check_for_fins(PBVH *pbvh, BMVert *v) } bool check_vert_fan_are_tris(PBVH *pbvh, BMVert *v) +{ + static Vector fs; + + /* Prevent pathological allocation thrashing on topology with + * vertices with lots of edges around them by reusing the same + * static local vector, instead of allocating on the stack. + */ + fs.clear(); + + uint8_t *flag = BM_ELEM_CD_PTR(v, pbvh->cd_flag); + if (!(*flag & SCULPTFLAG_NEED_TRIANGULATE)) { + return true; + } + + if (!v->e) { + *flag &= ~SCULPTFLAG_NEED_TRIANGULATE; + return true; + } + + const int tag = BM_ELEM_TAG_ALT; + + BMEdge *e = v->e; + do { + BMLoop *l = e->l; + + if (!l) { + continue; + } + + do { + l->f->head.hflag |= tag; + } while ((l = l->radial_next) != e->l); + } while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e); + + e = v->e; + do { + BMLoop *l = e->l; + + if (!l) { + continue; + } + + do { + if (l->f->head.hflag & tag) { + l->f->head.hflag &= ~tag; + fs.append(l->f); + } + } while ((l = l->radial_next) != e->l); + } while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e); + + for (int i = 0; i < fs.size(); i++) { + /* Triangulation can sometimes delete a face. */ + if (!BM_elem_is_free((BMElem *)fs[i], BM_FACE)) { + check_face_is_tri(pbvh, fs[i]); + } + } + + *flag &= ~SCULPTFLAG_NEED_TRIANGULATE; + return false; +} + +bool _check_vert_fan_are_tris(PBVH *pbvh, BMVert *v) { uint8_t *flag = BM_ELEM_CD_PTR(v, pbvh->cd_flag); @@ -2128,6 +2208,7 @@ void EdgeQueueContext::step() if (count >= steps[curop]) { if (ops[curop] == PBVH_Subdivide) { // && count_subd < max_subd) { + BM_log_entry_add_ex(pbvh->header.bm, pbvh->bm_log, true); flush_subdivision(); flushed_ = true; } @@ -2174,6 +2255,12 @@ void EdgeQueueContext::step() break; } + /*XXX*/ + if (BM_vert_edge_count(e->v1) > 100 || BM_vert_edge_count(e->v2) > 100) { + printf("Pathological vertex for subdivide.\n"); + break; + } + e->head.hflag &= ~BM_ELEM_TAG; #if 0 @@ -2222,6 +2309,12 @@ void EdgeQueueContext::step() break; } + /*XXX*/ + if (BM_vert_edge_count(e->v1) > 100 || BM_vert_edge_count(e->v2) > 100) { + printf("Pathological vertex for collapse.\n"); + break; + } + if (bm_elem_is_free((BMElem *)e->v1, BM_VERT) || bm_elem_is_free((BMElem *)e->v2, BM_VERT)) { printf("%s: error! operated on freed bmesh elements! e: %p, e->v1: %p, e->v2: %p\n", __func__, diff --git a/source/blender/blenkernel/intern/dyntopo_collapse.cc b/source/blender/blenkernel/intern/dyntopo_collapse.cc index 966c6b3aa28..d288cf7c133 100644 --- a/source/blender/blenkernel/intern/dyntopo_collapse.cc +++ b/source/blender/blenkernel/intern/dyntopo_collapse.cc @@ -620,7 +620,7 @@ BMVert *pbvh_bmesh_collapse_edge(PBVH *pbvh, copy_v3_v3(co, v_conn->co); /* Full non-manifold collapse. */ - BM_edge_collapse(pbvh->header.bm, e, v_del, true, true, true, true, nullptr); + BM_edge_collapse(pbvh->header.bm, e, v_del, true, true, true, true); copy_v3_v3(v_conn->co, co); } else { @@ -630,7 +630,7 @@ BMVert *pbvh_bmesh_collapse_edge(PBVH *pbvh, mul_v3_fl(co, 0.5f); /* Full non-manifold collapse. */ - BM_edge_collapse(pbvh->header.bm, e, v_del, true, true, true, true, nullptr); + BM_edge_collapse(pbvh->header.bm, e, v_del, true, true, true, true); copy_v3_v3(v_conn->co, co); } @@ -674,30 +674,13 @@ BMVert *pbvh_bmesh_collapse_edge(PBVH *pbvh, return nullptr; } - pbvh_boundary_update_bmesh(pbvh, v_conn); - dyntopo_add_flag(pbvh, v_conn, mupdateflag); - -#if 0 - e2 = v_conn->e; - BMEdge *enext; - do { - if (!e2) { - break; + if (v_conn->e && !v_conn->e->l) { + BM_log_edge_removed(pbvh->header.bm, pbvh->bm_log, v_conn->e); + if (BM_idmap_get_id(pbvh->bm_idmap, reinterpret_cast(v_conn->e)) != BM_ID_NONE) { + BM_idmap_release(pbvh->bm_idmap, reinterpret_cast(v_conn->e), true); } - - enext = BM_DISK_EDGE_NEXT(e2, v_conn); - - // kill wire edge - if (!e2->l) { - BM_log_edge_pre(pbvh->header.bm, pbvh->bm_log, e2); - BM_idmap_release(pbvh->bm_idmap, (BMElem *)e2, true); - BM_edge_kill(pbvh->header.bm, e2); - } - } while (v_conn->e && (e2 = enext) != v_conn->e); -#endif - - pbvh_boundary_update_bmesh(pbvh, v_conn); - dyntopo_add_flag(pbvh, v_conn, mupdateflag); + BM_edge_kill(pbvh->header.bm, v_conn->e); + } if (!v_conn->e) { /* Delete isolated vertex. */ @@ -706,14 +689,21 @@ BMVert *pbvh_bmesh_collapse_edge(PBVH *pbvh, } BM_log_vert_removed(pbvh->header.bm, pbvh->bm_log, v_conn); + BM_idmap_release(pbvh->bm_idmap, reinterpret_cast(v_conn), true); BM_vert_kill(pbvh->header.bm, v_conn); bm_logstack_pop(); return nullptr; } + pbvh_boundary_update_bmesh(pbvh, v_conn); + dyntopo_add_flag(pbvh, v_conn, mupdateflag); + if (BM_ELEM_CD_GET_INT(v_conn, pbvh->cd_vert_node_offset) == DYNTOPO_NODE_NONE) { - printf("%s: error: failed to remove vert from pbvh?\n", __func__); + printf("%s: error: failed to remove vert from pbvh? v_conn->e: %p v_conn->e->l\n", + __func__, + v_conn->e, + v_conn->e ? v_conn->e->l : nullptr); } if (v_conn) { diff --git a/source/blender/blenkernel/intern/mesh_remesh_voxel.cc b/source/blender/blenkernel/intern/mesh_remesh_voxel.cc index ee09cb8e8ec..fded6f929b5 100644 --- a/source/blender/blenkernel/intern/mesh_remesh_voxel.cc +++ b/source/blender/blenkernel/intern/mesh_remesh_voxel.cc @@ -533,7 +533,7 @@ struct Mesh *BKE_mesh_remesh_voxel_fix_poles(const Mesh *mesh) if (BM_elem_flag_test(ed, BM_ELEM_TAG)) { float co[3]; mid_v3_v3v3(co, ed->v1->co, ed->v2->co); - BMVert *vc = BM_edge_collapse(bm, ed, ed->v1, true, true, true, true, nullptr); + BMVert *vc = BM_edge_collapse(bm, ed, ed->v1, true, true, true, true); copy_v3_v3(vc->co, co); } } diff --git a/source/blender/blenkernel/intern/pbvh.cc b/source/blender/blenkernel/intern/pbvh.cc index 16d3f2ae9f7..bd0f65bb204 100644 --- a/source/blender/blenkernel/intern/pbvh.cc +++ b/source/blender/blenkernel/intern/pbvh.cc @@ -99,6 +99,12 @@ typedef struct PBVHIter { int stackspace; } PBVHIter; +void BB_zero(BB *bb) +{ + bb->bmin[0] = bb->bmin[1] = bb->bmin[2] = 0.0f; + bb->bmax[0] = bb->bmax[1] = bb->bmax[2] = 0.0f; +} + void BB_reset(BB *bb) { bb->bmin[0] = bb->bmin[1] = bb->bmin[2] = FLT_MAX; @@ -176,6 +182,14 @@ void BBC_update_centroid(BBC *bbc) /* Not recursive */ static void update_node_vb(PBVH *pbvh, PBVHNode *node, int updateflag) { + auto not_leaf_or_has_faces = [&](PBVHNode *node) { + if (!(node->flag & PBVH_Leaf)) { + return true; + } + + return bool(node->bm_faces ? node->bm_faces->length : node->totprim); + }; + if (!(updateflag & (PBVH_UpdateBB | PBVH_UpdateOriginalBB))) { return; } @@ -210,16 +224,42 @@ static void update_node_vb(PBVH *pbvh, PBVHNode *node, int updateflag) } } BKE_pbvh_vertex_iter_end; + + if (!not_leaf_or_has_faces(node)) { + zero_v3(vb.bmin); + zero_v3(vb.bmax); + zero_v3(orig_vb.bmin); + zero_v3(orig_vb.bmax); + } } else { - if (do_normal) { - BB_expand_with_bb(&vb, &pbvh->nodes[node->children_offset].vb); - BB_expand_with_bb(&vb, &pbvh->nodes[node->children_offset + 1].vb); + bool ok = false; + + if (not_leaf_or_has_faces(&pbvh->nodes[node->children_offset])) { + if (do_normal) { + BB_expand_with_bb(&vb, &pbvh->nodes[node->children_offset].vb); + } + if (do_orig) { + BB_expand_with_bb(&orig_vb, &pbvh->nodes[node->children_offset].orig_vb); + } + + ok = true; } - if (do_orig) { - BB_expand_with_bb(&orig_vb, &pbvh->nodes[node->children_offset].orig_vb); - BB_expand_with_bb(&orig_vb, &pbvh->nodes[node->children_offset + 1].orig_vb); + if (not_leaf_or_has_faces(&pbvh->nodes[node->children_offset + 1])) { + if (do_normal) { + BB_expand_with_bb(&vb, &pbvh->nodes[node->children_offset + 1].vb); + } + if (do_orig) { + BB_expand_with_bb(&orig_vb, &pbvh->nodes[node->children_offset + 1].orig_vb); + } + + ok = true; + } + + if (!ok) { + BB_zero(&vb); + BB_zero(&orig_vb); } } @@ -1376,7 +1416,10 @@ static PBVHNode *pbvh_iter_next_occluded(PBVHIter *iter) float ff = dot_v3v3(node->vb.bmin, node->vb.bmax); if (isnan(ff) || !isfinite(ff)) { - printf("%s: nan!\n", __func__); + printf("%s: nan! totf: %d totv: %d\n", + __func__, + node->bm_faces ? node->bm_faces->length : 0, + node->bm_unique_verts ? node->bm_unique_verts->length : 0); } if (iter->scb && !iter->scb(node, iter->search_data)) { @@ -4344,6 +4387,10 @@ void update_vert_boundary_faces(int *boundary_flags, *flag |= SCULPTFLAG_VERT_FSET_HIDDEN; } + if (totsharp_angle > 2) { + *boundary_flag |= SCULPT_CORNER_SHARP_ANGLE; + } + if (!ELEM(totsharp, 0, 2)) { *boundary_flag |= SCULPT_CORNER_SHARP_MARK; } diff --git a/source/blender/blenkernel/intern/pbvh_bmesh.cc b/source/blender/blenkernel/intern/pbvh_bmesh.cc index 3dd4029dca6..7537c4e2cf2 100644 --- a/source/blender/blenkernel/intern/pbvh_bmesh.cc +++ b/source/blender/blenkernel/intern/pbvh_bmesh.cc @@ -678,7 +678,7 @@ void pbvh_bmesh_face_remove( PBVHNode *f_node = pbvh_bmesh_node_from_face(pbvh, f); if (!f_node || !(f_node->flag & PBVH_Leaf)) { - printf("pbvh corruption\n"); + printf("%s: pbvh corruption\n", __func__); fflush(stdout); return; } @@ -3563,7 +3563,7 @@ static void pbvh_bmesh_compact_tree(PBVH *bvh) } /* Prunes leaf nodes that are too small or degenerate. */ -static void pbvh_bmesh_balance_tree(PBVH *pbvh) +ATTR_NO_OPT static void pbvh_bmesh_balance_tree(PBVH *pbvh) { float *overlaps = MEM_cnew_array(pbvh->totnode, "overlaps"); PBVHNode **parentmap = MEM_cnew_array(pbvh->totnode, "parentmap"); @@ -3614,27 +3614,7 @@ static void pbvh_bmesh_balance_tree(PBVH *pbvh) float overlap = BB_volume(&clip); float factor; - /* use higher threshold for the root node and its immediate children */ - switch (stack.size()) { - case 0: - factor = 0.5; - break; - case 1: - case 2: - factor = 0.2; - break; - default: - factor = 0.2; - break; - } - -#if 0 - for (int k = 0; k < stack.size(); k++) { - printf(" "); - } - - printf("factor: %.3f\n", factor); -#endif + factor = 0.2f; bool bad = overlap > volume * factor; diff --git a/source/blender/blenkernel/intern/pbvh_intern.hh b/source/blender/blenkernel/intern/pbvh_intern.hh index ec249a97bce..96989155436 100644 --- a/source/blender/blenkernel/intern/pbvh_intern.hh +++ b/source/blender/blenkernel/intern/pbvh_intern.hh @@ -305,7 +305,9 @@ struct PBVH { /* pbvh.cc */ void BB_reset(BB *bb); -/** +void BB_zero(BB *bb); + + /** * Expand the bounding box to include a new coordinate. */ void BB_expand(BB *bb, const float co[3]); diff --git a/source/blender/blenlib/BLI_heap_minmax.hh b/source/blender/blenlib/BLI_heap_minmax.hh index 366b66a71e2..699fa51e063 100644 --- a/source/blender/blenlib/BLI_heap_minmax.hh +++ b/source/blender/blenlib/BLI_heap_minmax.hh @@ -16,6 +16,8 @@ using blender::Vector; #include +//#define BLI_MINMAX_HEAP_VALIDATE + namespace blender { template class HeapValueIter { Span nodes_; @@ -65,7 +67,7 @@ template class HeapValueIter { template -#if 1 +#if 0 class MinMaxHeap { struct MinMaxHeapNode { Value value; @@ -198,12 +200,59 @@ class MinMaxHeap { ~MinMaxHeap() {} + ATTR_NO_OPT bool valid_recurse(int i) + { + bool ret = true; + + MinMaxHeapNode &node = nodes[i]; + int level = i & 1; + + if (node.child1 != -1) { + MinMaxHeapNode &child = nodes[node.child1]; + if (child.weight != node.weight && (child.weight < node.weight) ^ level) { + printf("error: node %d[%f] is less than node %d[%f]\n", + i, + node.weight, + node.child1, + child.weight); + } + + ret = ret && valid_recurse(node.child1); + } + + if (node.child2 != -1) { + MinMaxHeapNode &child = nodes[node.child2]; + + if (child.weight != node.weight && (child.weight < node.weight) ^ level) { + printf("error: node %d[%f] is less than node %d[%f]\n", + i, + node.weight, + node.child2, + child.weight); + } + + ret = ret && valid_recurse(node.child2); + } + + return ret; + } + + ATTR_NO_OPT bool is_valid() + { + + if (nodes.size() == 0) { + return true; + } + + return valid_recurse(0); + } + HeapValueIter values() { return HeapValueIter(nodes); } - MinMaxHeapNode *insert(float weight, Value value) + ATTR_NO_OPT MinMaxHeapNode *insert(float weight, Value value) { MinMaxHeapNode *node = heap_make_node(); @@ -226,7 +275,15 @@ class MinMaxHeap { parent->child2 = i; } - return heap_push_up(node); + MinMaxHeapNode *ret = heap_push_up(node); + +# ifdef BLI_MINMAX_HEAP_VALIDATE + if (!is_valid()) { + printf("invalid heap!\n"); + } +# endif + + return ret; } void insert_or_update(MinMaxHeapNode **node_p, float weight, Value value) @@ -280,7 +337,7 @@ class MinMaxHeap { MinMaxHeapNode last = heap_pop_last(); nodes[0].weight = last.weight; - nodes[1].value = last.value; + nodes[0].value = last.value; heap_push_down(&nodes[0]); @@ -467,78 +524,74 @@ class MinMaxHeap { MinMaxHeapNode *heap_push_down_min(MinMaxHeapNode *node) { - MinMaxHeapNode *ret = nullptr; + MinMaxHeapNode *ret = node; + MinMaxHeapNode *node2 = heap_descent_min(node); - while (node->child1 >= 0 || node->child2 >= 0) { - MinMaxHeapNode *node2 = heap_descent_min(node); + if (!node2) { + return node; + } - if (!node2) { - break; - } + /* Is node2 a grandchild? */ + if (node2->parent != node - nodes.data()) { + MinMaxHeapNode *parent = &nodes[node2->parent]; if (node2->weight < node->weight) { std::swap(node2->weight, node->weight); std::swap(node2->value, node->value); + ret = node2; - if (node2->parent != node - nodes.data()) { - MinMaxHeapNode *parent = &nodes[node2->parent]; - - if (node2->weight > parent->weight) { - std::swap(node2->weight, parent->weight); - std::swap(node2->value, parent->value); - - /* this is a bit tricky, our return node has now - moved into the other interleaved heap side */ - if (!ret) { - ret = parent; - } - } + if (node2->weight > parent->weight) { + std::swap(node2->weight, parent->weight); + std::swap(node2->value, parent->value); + ret = parent; } - node = node2; - } - else { - break; + ret = heap_push_down(node2); } } + else if (node2->weight < node->weight) { + std::swap(node2->weight, node->weight); + std::swap(node2->value, node->value); + ret = node2; + } - return ret ? ret : node; + return ret; } MinMaxHeapNode *heap_push_down_max(MinMaxHeapNode *node) { - MinMaxHeapNode *ret = nullptr; + MinMaxHeapNode *ret = node; + MinMaxHeapNode *node2 = heap_descent_max(node); - while (node->child1 >= 0 || node->child2 >= 0) { - MinMaxHeapNode *node2 = heap_descent_max(node); + if (!node2) { + return node; + } + + /* Is node2 a grandchild? */ + if (node2->parent != node - nodes.data()) { + MinMaxHeapNode *parent = &nodes[node2->parent]; if (node2->weight > node->weight) { std::swap(node2->weight, node->weight); std::swap(node2->value, node->value); + ret = node2; - if (node2->parent != node - nodes.data()) { - MinMaxHeapNode *parent = &nodes[node2->parent]; - - if (node2->weight < parent->weight) { - std::swap(node2->weight, parent->weight); - std::swap(node2->value, parent->value); - - /* this is a bit tricky, our return node has now - moved into the other interleaved heap side */ - if (!ret) { - ret = parent; - } - } + if (node2->weight < parent->weight) { + std::swap(node2->weight, parent->weight); + std::swap(node2->value, parent->value); + ret = parent; } - node = node2; - } - else { - break; + ret = heap_push_down(node2); } } + else if (node2->weight > node->weight) { + std::swap(node2->weight, node->weight); + std::swap(node2->value, node->value); + ret = node2; + } - return ret ? ret : node; + return ret; } int heap_get_level(const MinMaxHeapNode *node) diff --git a/source/blender/bmesh/intern/bmesh_core.c b/source/blender/bmesh/intern/bmesh_core.c index f53aaa39146..c813b7654f4 100644 --- a/source/blender/bmesh/intern/bmesh_core.c +++ b/source/blender/bmesh/intern/bmesh_core.c @@ -6,9 +6,10 @@ * Core BMesh functions for adding, removing BMesh elements. */ +//#define JVKE_DEBUG //#define FORCE_BMESH_CHECK -#if !defined(NDEBUG) || defined(FORCE_BMESH_CHECK) +#if !defined(NDEBUG) || defined(FORCE_BMESH_CHECK) || defined(JVKE_DEBUG) # define BMESH_DEBUG #endif @@ -1567,12 +1568,9 @@ BMVert *bmesh_kernel_split_edge_make_vert(BMesh *bm, BMVert *tv, BMEdge *e, BMEd int valence1, valence2; bool edok; int i; - > blender.exe !bmesh_kernel_split_edge_make_vert( - BMesh * bm, BMVert * tv, BMEdge * e, BMEdge * *r_e) Line 1576 C - #endif - BLI_assert(BM_vert_in_edge(e, tv) != false); + BLI_assert(BM_vert_in_edge(e, tv) != false); v_old = BM_edge_other_vert(e, tv); @@ -1881,8 +1879,6 @@ BMEdge *bmesh_kernel_join_edge_kill_vert(BMesh *bm, return NULL; } -//#define JVKE_DEBUG - #ifdef JVKE_DEBUG # ifdef _ @@ -2308,7 +2304,7 @@ char *_last_local_obj = NULL; # define JVKE_CHECK_ELEMENT(elem) #endif -static bool cleanup_vert(BMesh *bm, BMVert *v, const BMTracer *tracer) +static bool cleanup_vert(BMesh *bm, BMVert *v) { BMEdge *e = v->e; @@ -2327,24 +2323,9 @@ static bool cleanup_vert(BMesh *bm, BMVert *v, const BMTracer *tracer) } f_example = l->f; - - do { - if (tracer) { - tracer->on_face_kill(bm, l->f, tracer->userdata); - } - } while ((l = l->radial_next) != e->l); + break; } while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e); - if (tracer) { - do { - tracer->on_edge_kill(bm, e, tracer->userdata); - } while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e); - } - - if (tracer) { - tracer->on_vert_kill(bm, v, tracer->userdata); - } - BMVert *v1 = BM_edge_other_vert(v->e, v); BMVert *v2 = BM_edge_other_vert(BM_DISK_EDGE_NEXT(v->e, v), v); BMVert *v3 = BM_edge_other_vert(BM_DISK_EDGE_NEXT(BM_DISK_EDGE_NEXT(v->e, v), v), v); @@ -2362,15 +2343,11 @@ static bool cleanup_vert(BMesh *bm, BMVert *v, const BMTracer *tracer) BM_vert_kill(bm, v); - if (tracer) { - tracer->on_face_create(bm, f, tracer->userdata); - } - bm_logstack_pop(); return true; } -static void bmesh_kernel_check_val3_vert(BMesh *bm, BMEdge *e, const BMTracer *tracer) +static void bmesh_kernel_check_val3_vert(BMesh *bm, BMEdge *e) { if (!e->l) { return; @@ -2397,7 +2374,7 @@ static void bmesh_kernel_check_val3_vert(BMesh *bm, BMEdge *e, const BMTracer *t } if (BM_vert_edge_count(l2->v) == 3) { - if (cleanup_vert(bm, l2->v, tracer)) { + if (cleanup_vert(bm, l2->v)) { stop = false; break; } @@ -2426,12 +2403,8 @@ static void bmesh_kernel_check_val3_vert(BMesh *bm, BMEdge *e, const BMTracer *t * +-+-+-+ +-+-+-+ * */ -BMVert *bmesh_kernel_join_vert_kill_edge(BMesh *bm, - BMEdge *e, - BMVert *v_kill, - const bool do_del, - const bool combine_flags, - const BMTracer *tracer) +ATTR_NO_OPT BMVert *bmesh_kernel_join_vert_kill_edge( + BMesh *bm, BMEdge *e, BMVert *v_kill, const bool do_del, const bool combine_flags) { BMVert *v_conn = BM_edge_other_vert(e, v_kill); @@ -2449,7 +2422,7 @@ BMVert *bmesh_kernel_join_vert_kill_edge(BMesh *bm, #endif /* Free any surrounding valence-3 rings disconnected from the edge. */ - bmesh_kernel_check_val3_vert(bm, e, tracer); + bmesh_kernel_check_val3_vert(bm, e); BMFace **fs = NULL; BMEdge **deles = NULL; @@ -2467,8 +2440,6 @@ BMVert *bmesh_kernel_join_vert_kill_edge(BMesh *bm, JVKE_CHECK_ELEMENT(v_conn); JVKE_CHECK_ELEMENT(v_del); -#define _OTHER_TRACES // paranoia (and likely duplicate) calls to tracer callbacks - /* first clear tags */ for (int i = 0; i < 2; i++) { BMVert *v = i ? v_del : v_conn; @@ -2479,22 +2450,6 @@ BMVert *bmesh_kernel_join_vert_kill_edge(BMesh *bm, BMEdge *e2 = v->e; do { - /* build list of edges if needed for tracing */ - if (tracer) { - bool ok = true; - - for (int j = 0; j < BLI_array_len(es); j++) { - if (es[j] == e2) { - ok = false; - break; - } - } - - if (ok) { - BLI_array_append(es, e2); - } - } - if (!e2->l) { continue; } @@ -2547,18 +2502,6 @@ BMVert *bmesh_kernel_join_vert_kill_edge(BMesh *bm, } while ((e2 = BM_DISK_EDGE_NEXT(e2, v)) != v->e); } - if (tracer) { - for (int i = 0; i < BLI_array_len(fs); i++) { - tracer->on_face_kill(bm, fs[i], tracer->userdata); - } - } - - if (tracer) { - for (int i = 0; i < BLI_array_len(es); i++) { - tracer->on_edge_kill(bm, es[i], tracer->userdata); - } - } - /* unlink loops */ for (int i = 0; i < BLI_array_len(fs); i++) { BMFace *f = fs[i]; @@ -2653,22 +2596,6 @@ BMVert *bmesh_kernel_join_vert_kill_edge(BMesh *bm, e2->l = NULL; -#ifdef _OTHER_TRACES - if (tracer) { - bool found = false; - for (int j = 0; j < BLI_array_len(es); j++) { - if (es[j] == deles[i]) { - found = true; - break; - } - } - - if (!found) { - tracer->on_edge_kill(bm, deles[i], tracer->userdata); - } - } -#endif - if (deles[i]->l) { printf("%s: edge is not cleared\n", __func__); } @@ -2704,12 +2631,6 @@ BMVert *bmesh_kernel_join_vert_kill_edge(BMesh *bm, } while (lnext && (l = lnext) != f->l_first); if (f->len <= 2) { -#if 0 // def _OTHER_TRACES - if (tracer) { - tracer->on_face_kill(bm, f, tracer->userdata); - } -#endif - /* kill face */ while (f->l_first) { BMLoop *l2 = f->l_first; @@ -2753,6 +2674,7 @@ BMVert *bmesh_kernel_join_vert_kill_edge(BMesh *bm, } bmesh_radial_loop_append(l->e, l); + JVKE_CHECK_ELEMENT(l->e); BM_ELEM_API_FLAG_DISABLE(l->e, dup_tag); BM_ELEM_API_FLAG_DISABLE(l->v, dup_tag); @@ -2874,9 +2796,6 @@ BMVert *bmesh_kernel_join_vert_kill_edge(BMesh *bm, BMFace *f; if ((f = BM_face_find_double(l->f))) { - if (tracer) { - tracer->on_face_kill(bm, l->f, tracer->userdata); - } BM_face_kill(bm, l->f); } } while (e2->l && (l = l_next) != e2->l); @@ -2886,9 +2805,6 @@ BMVert *bmesh_kernel_join_vert_kill_edge(BMesh *bm, // printf("v_del: %p, v_conn: %p\n", v_del->e, v_conn->e); if (do_del) { JVKE_CHECK_ELEMENT(v_del); - if (tracer) { - tracer->on_vert_kill(bm, v_del, tracer->userdata); - } if (v_del->e && v_del->e->l) { printf("%s: vert is not cleared\n", __func__); @@ -2899,29 +2815,8 @@ BMVert *bmesh_kernel_join_vert_kill_edge(BMesh *bm, } } - if (tracer && v_conn->e) { - e = v_conn->e; - do { - tracer->on_edge_create(bm, e, tracer->userdata); - } while ((e = BM_DISK_EDGE_NEXT(e, v_conn)) != v_conn->e); - - do { - BMLoop *l = e->l; - if (!l) { - continue; - } - - do { - if (BM_ELEM_API_FLAG_TEST(l->f, final_tag)) { - BM_ELEM_API_FLAG_DISABLE(l->f, final_tag); - - tracer->on_face_create(bm, l->f, tracer->userdata); - } - } while ((l = l->radial_next) != e->l); - } while ((e = BM_DISK_EDGE_NEXT(e, v_conn)) != v_conn->e); - } - #ifdef JVKE_DEBUG +# if 0 /* "Inverted pyramid" situations will trigger this check */ if (v_conn && v_conn->e) { BMEdge *e = v_conn->e; do { @@ -2937,6 +2832,7 @@ BMVert *bmesh_kernel_join_vert_kill_edge(BMesh *bm, } } while ((e = BM_DISK_EDGE_NEXT(e, v_conn)) != v_conn->e); } +# endif bm_local_obj_free(saved_obj, buf); #endif diff --git a/source/blender/bmesh/intern/bmesh_core.h b/source/blender/bmesh/intern/bmesh_core.h index 6eaa0bb8d54..0317cc21beb 100644 --- a/source/blender/bmesh/intern/bmesh_core.h +++ b/source/blender/bmesh/intern/bmesh_core.h @@ -350,8 +350,7 @@ BMVert *bmesh_kernel_join_vert_kill_edge(BMesh *bm, BMEdge *e_kill, BMVert *v_kill, const bool do_del, - const bool combine_flags, - const struct BMTracer *tracer); + const bool combine_flags); BMVert *bmesh_kernel_join_vert_kill_edge_fast(BMesh *bm, BMEdge *e_kill, diff --git a/source/blender/bmesh/intern/bmesh_mods.c b/source/blender/bmesh/intern/bmesh_mods.c index b14abe5c4b9..7057fa4ff07 100644 --- a/source/blender/bmesh/intern/bmesh_mods.c +++ b/source/blender/bmesh/intern/bmesh_mods.c @@ -448,11 +448,10 @@ BMVert *BM_edge_collapse(BMesh *bm, const bool do_del, const bool kill_degenerate_faces, const bool combine_flags, - const bool full_non_manifold_collapse, - const BMTracer *tracer) + const bool full_non_manifold_collapse) { if (full_non_manifold_collapse || true) { - return bmesh_kernel_join_vert_kill_edge(bm, e_kill, v_kill, do_del, combine_flags, tracer); + return bmesh_kernel_join_vert_kill_edge(bm, e_kill, v_kill, do_del, combine_flags); } else { return bmesh_kernel_join_vert_kill_edge_fast( diff --git a/source/blender/bmesh/intern/bmesh_mods.h b/source/blender/bmesh/intern/bmesh_mods.h index 4396da83b3b..19547c0a766 100644 --- a/source/blender/bmesh/intern/bmesh_mods.h +++ b/source/blender/bmesh/intern/bmesh_mods.h @@ -163,8 +163,7 @@ BMVert *BM_edge_collapse(BMesh *bm, const bool do_del, const bool kill_degenerate_faces, const bool combine_flags, - const bool full_non_manifold_collapse, - const struct BMTracer *tracer); + const bool full_non_manifold_collapse); /** * \brief Edge Split diff --git a/source/blender/bmesh/operators/bmo_extrude.c b/source/blender/bmesh/operators/bmo_extrude.c index 04e726087f7..5972cc6ee85 100644 --- a/source/blender/bmesh/operators/bmo_extrude.c +++ b/source/blender/bmesh/operators/bmo_extrude.c @@ -612,14 +612,8 @@ void bmo_extrude_face_region_exec(BMesh *bm, BMOperator *op) BMEdge *e_other = BM_DISK_EDGE_NEXT(e, v); if ((e_other == e) || (BM_DISK_EDGE_NEXT(e_other, v) == e)) { /* Loose edge or BMVert is edge pair. */ - BM_edge_collapse(bm, - BMO_elem_flag_test(bm, e, EXT_TAG) ? e : e_other, - v, - true, - true, - false, - false, - NULL); + BM_edge_collapse( + bm, BMO_elem_flag_test(bm, e, EXT_TAG) ? e : e_other, v, true, true, false, false); } else { BLI_assert(!BM_vert_is_edge_pair(v)); diff --git a/source/blender/bmesh/operators/bmo_fill_grid.c b/source/blender/bmesh/operators/bmo_fill_grid.c index a3e9ae52fb0..6367ca861f9 100644 --- a/source/blender/bmesh/operators/bmo_fill_grid.c +++ b/source/blender/bmesh/operators/bmo_fill_grid.c @@ -707,7 +707,7 @@ void bmo_grid_fill_exec(BMesh *bm, BMOperator *op) GSetIterator gs_iter; GSET_ITER (gs_iter, split_edges) { BMEdge *e = BLI_gsetIterator_getKey(&gs_iter); - BM_edge_collapse(bm, e, e->v2, true, true, false, false, NULL); + BM_edge_collapse(bm, e, e->v2, true, true, false, false); } BLI_gset_free(split_edges, NULL); } diff --git a/source/blender/bmesh/operators/bmo_primitive.c b/source/blender/bmesh/operators/bmo_primitive.c index 7ba103407b7..3cb49d7ab38 100644 --- a/source/blender/bmesh/operators/bmo_primitive.c +++ b/source/blender/bmesh/operators/bmo_primitive.c @@ -1471,7 +1471,7 @@ void bmo_create_cone_exec(BMesh *bm, BMOperator *op) for (int i = 0; i < side_faces_len; i++) { f = side_faces[i]; BMLoop *l = BM_FACE_FIRST_LOOP(f); - BM_edge_collapse(bm, l->prev->e, l->prev->v, true, true, false, false, NULL); + BM_edge_collapse(bm, l->prev->e, l->prev->v, true, true, false, false); } } @@ -1483,7 +1483,7 @@ void bmo_create_cone_exec(BMesh *bm, BMOperator *op) for (int i = 0; i < side_faces_len; i++) { f = side_faces[i]; BMLoop *l = BM_FACE_FIRST_LOOP(f); - BM_edge_collapse(bm, l->next->e, l->next->v, true, true, false, false, NULL); + BM_edge_collapse(bm, l->next->e, l->next->v, true, true, false, false); } } diff --git a/source/blender/bmesh/operators/bmo_removedoubles.c b/source/blender/bmesh/operators/bmo_removedoubles.c index b588b4a2c1e..101b1da1506 100644 --- a/source/blender/bmesh/operators/bmo_removedoubles.c +++ b/source/blender/bmesh/operators/bmo_removedoubles.c @@ -524,7 +524,7 @@ void bmo_collapse_exec(BMesh *bm, BMOperator *op) BMO_slot_map_elem_insert(&weldop, slot_targetmap, v_src, v_tar); #ifdef USE_BM_EDGE_COLLAPSE - BM_edge_collapse(bm, e, v_src, true, true, true, true, NULL); + BM_edge_collapse(bm, e, v_src, true, true, true, true); break; #endif } diff --git a/source/blender/editors/mesh/editmesh_polybuild.c b/source/blender/editors/mesh/editmesh_polybuild.c index 8c2b8e758e8..1e38b901533 100644 --- a/source/blender/editors/mesh/editmesh_polybuild.c +++ b/source/blender/editors/mesh/editmesh_polybuild.c @@ -213,7 +213,7 @@ static int edbm_polybuild_delete_at_cursor_invoke(bContext *C, if (ele_act->head.htype == BM_VERT) { BMVert *v_act = (BMVert *)ele_act; if (BM_vert_is_edge_pair(v_act) && !BM_vert_is_wire(v_act)) { - BM_edge_collapse(bm, v_act->e, v_act, true, true, false, false, NULL); + BM_edge_collapse(bm, v_act->e, v_act, true, true, false, false); changed = true; } else { @@ -567,7 +567,7 @@ static int edbm_polybuild_dissolve_at_cursor_invoke(bContext *C, else if (ele_act->head.htype == BM_VERT) { BMVert *v_act = (BMVert *)ele_act; if (BM_vert_is_edge_pair(v_act)) { - BM_edge_collapse(bm, v_act->e, v_act, true, true, false, false, NULL); + BM_edge_collapse(bm, v_act->e, v_act, true, true, false, false); } else { /* too involved to do inline */ -- 2.30.2 From 79c28f1ee27e4c5c92ec01056ce4e56e6a7d102d Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Mon, 29 May 2023 17:06:42 -0700 Subject: [PATCH 122/279] temp-sculpt-dyntopo: Remove unneccasry bmesh table building --- source/blender/editors/sculpt_paint/sculpt.cc | 5 ----- 1 file changed, 5 deletions(-) diff --git a/source/blender/editors/sculpt_paint/sculpt.cc b/source/blender/editors/sculpt_paint/sculpt.cc index c5a64d8cb04..d528e90b2a2 100644 --- a/source/blender/editors/sculpt_paint/sculpt.cc +++ b/source/blender/editors/sculpt_paint/sculpt.cc @@ -5955,11 +5955,6 @@ bool SCULPT_stroke_get_location_ex(bContext *C, depth = SCULPT_raycast_init(&vc, mval, ray_start, ray_end, ray_normal, original); - if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) { - BM_mesh_elem_table_ensure(ss->bm, BM_VERT); - BM_mesh_elem_index_ensure(ss->bm, BM_VERT); - } - bool hit = false; { SculptRaycastData srd; -- 2.30.2 From c606bce67e7d800b059669a30bfc2d474d87134d Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Mon, 29 May 2023 17:41:31 -0700 Subject: [PATCH 123/279] temp-sculpt-dyntopo: Don't ignore detail_range setting --- source/blender/blenkernel/intern/dyntopo.cc | 2 +- source/blender/editors/sculpt_paint/sculpt.cc | 13 ++++++++----- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/source/blender/blenkernel/intern/dyntopo.cc b/source/blender/blenkernel/intern/dyntopo.cc index d94102fb1e9..9105b75c390 100644 --- a/source/blender/blenkernel/intern/dyntopo.cc +++ b/source/blender/blenkernel/intern/dyntopo.cc @@ -3053,7 +3053,7 @@ static void pbvh_split_edges( } void detail_size_set(PBVH *pbvh, float detail_size, float detail_range) { - pbvh->bm_detail_range = max_ff(detail_range, 0.1f); + pbvh->bm_detail_range = detail_range == 0.0f ? 0.4f : max_ff(detail_range, 0.1f); pbvh->bm_max_edge_len = detail_size; pbvh->bm_min_edge_len = pbvh->bm_max_edge_len * pbvh->bm_detail_range; } diff --git a/source/blender/editors/sculpt_paint/sculpt.cc b/source/blender/editors/sculpt_paint/sculpt.cc index d528e90b2a2..64569ac5b6a 100644 --- a/source/blender/editors/sculpt_paint/sculpt.cc +++ b/source/blender/editors/sculpt_paint/sculpt.cc @@ -6397,18 +6397,21 @@ static void sculpt_stroke_update_step(bContext *C, if (ELEM(ss->cached_dyntopo.mode, DYNTOPO_DETAIL_CONSTANT, DYNTOPO_DETAIL_MANUAL)) { float object_space_constant_detail = 1.0f / (ss->cached_dyntopo.constant_detail * mat4_to_scale(ob->object_to_world)); - blender::bke::dyntopo::detail_size_set(ss->pbvh, object_space_constant_detail, 0.4f); + blender::bke::dyntopo::detail_size_set( + ss->pbvh, object_space_constant_detail, ss->cached_dyntopo.detail_range); } else if (ss->cached_dyntopo.mode == DYNTOPO_DETAIL_BRUSH) { - blender::bke::dyntopo::detail_size_set( - ss->pbvh, ss->cache->radius * ss->cached_dyntopo.detail_percent / 100.0f, 0.4f); + blender::bke::dyntopo::detail_size_set(ss->pbvh, + ss->cache->radius * ss->cached_dyntopo.detail_percent / + 100.0f, + ss->cached_dyntopo.detail_range); } else { /* Relative mode. */ blender::bke::dyntopo::detail_size_set(ss->pbvh, (ss->cache->radius / ss->cache->dyntopo_pixel_radius) * (ss->cached_dyntopo.detail_size * U.pixelsize) / - 0.4f, - 0.4f); + ss->cached_dyntopo.detail_range, + ss->cached_dyntopo.detail_range); } float dyntopo_spacing = float(ss->cached_dyntopo.spacing) / 50.0f; -- 2.30.2 From ac719086802690ddded037b31f232d2bec4642c0 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Mon, 29 May 2023 22:01:32 -0700 Subject: [PATCH 124/279] temp-sculpt-dyntopo: Smooth and topology rake fixes * Certain tools need to have their original coordinates attribute processed during autosmoothing and topology raking, otherwise they get noise. * For now it's just draw sharp, other brushes will be tested (the layer brush might need it). * Tried to improve the behavior of topology rake a bit more. --- source/blender/editors/sculpt_paint/sculpt.cc | 4 +- .../sculpt_paint/sculpt_brush_types.cc | 50 +++++-- .../editors/sculpt_paint/sculpt_intern.hh | 11 +- .../editors/sculpt_paint/sculpt_smooth.cc | 140 +++++++++++++++--- 4 files changed, 164 insertions(+), 41 deletions(-) diff --git a/source/blender/editors/sculpt_paint/sculpt.cc b/source/blender/editors/sculpt_paint/sculpt.cc index 64569ac5b6a..39c4bc78bd3 100644 --- a/source/blender/editors/sculpt_paint/sculpt.cc +++ b/source/blender/editors/sculpt_paint/sculpt.cc @@ -179,9 +179,9 @@ const float *SCULPT_vertex_origco_get(SculptSession *ss, PBVHVertRef vertex) return vertex_attr_ptr(vertex, ss->attrs.orig_co); } -const float *SCULPT_vertex_origno_get(SculptSession *ss, PBVHVertRef vertex) +void SCULPT_vertex_origno_get(SculptSession *ss, PBVHVertRef vertex, float r_no[3]) { - return vertex_attr_ptr(vertex, ss->attrs.orig_no); + copy_v3_v3(r_no, vertex_attr_ptr(vertex, ss->attrs.orig_no)); } int SCULPT_vertex_count_get(const SculptSession *ss) diff --git a/source/blender/editors/sculpt_paint/sculpt_brush_types.cc b/source/blender/editors/sculpt_paint/sculpt_brush_types.cc index b61d7ea2f90..0c61240109b 100644 --- a/source/blender/editors/sculpt_paint/sculpt_brush_types.cc +++ b/source/blender/editors/sculpt_paint/sculpt_brush_types.cc @@ -2882,6 +2882,8 @@ static void do_topology_rake_bmesh_task_cb_ex(void *__restrict userdata, PBVHVertexIter vd; bool modified = false; + const float projection = brush->autosmooth_projection; + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { float direction2[3]; @@ -2903,18 +2905,20 @@ static void do_topology_rake_bmesh_task_cb_ex(void *__restrict userdata, modified = true; - const float fade = bstrength * - SCULPT_brush_strength_factor(ss, - brush, - vd.co, - sqrtf(test.dist), - vd.no, - vd.fno, - *vd.mask, - vd.vertex, - thread_id, - &automask_data) * - ss->cache->pressure; + float fade = SCULPT_brush_strength_factor(ss, + brush, + vd.co, + sqrtf(test.dist), + vd.no, + vd.fno, + *vd.mask, + vd.vertex, + thread_id, + &automask_data); + + /* Make brush falloff less sharp. */ + fade = sqrtf(fade); + fade *= bstrength; float oldco[3]; float oldno[3]; @@ -2926,14 +2930,29 @@ static void do_topology_rake_bmesh_task_cb_ex(void *__restrict userdata, int cd_temp = data->scl->bmesh_cd_offset; SCULPT_bmesh_four_neighbor_average( - ss, avg, direction2, vd.bm_vert, 1.0f, hard_corner_pin, cd_temp, weighted, false); + ss, avg, direction2, vd.bm_vert, projection, hard_corner_pin, cd_temp, weighted, false); sub_v3_v3v3(val, avg, vd.co); - madd_v3_v3v3fl(val, vd.co, val, fade); - SCULPT_clip(sd, ss, vd.co, val); + if (data->smooth_origco) { + float origco_avg[3]; + + SCULPT_vertex_check_origdata(ss, vd.vertex); + SCULPT_bmesh_four_neighbor_average(ss, + origco_avg, + direction2, + vd.bm_vert, + projection, + hard_corner_pin, + cd_temp, + weighted, + true); + float *origco = blender::bke::paint::vertex_attr_ptr(vd.vertex, ss->attrs.orig_co); + interp_v3_v3v3(origco, origco, origco_avg, fade); + } + if (do_reproject) { BKE_sculpt_reproject_cdata(ss, vd.vertex, oldco, oldno); } @@ -2983,6 +3002,7 @@ void SCULPT_bmesh_topology_rake(Sculpt *sd, Object *ob, Span nodes, data.nodes = nodes; data.strength = factor; data.scl = ss->attrs.rake_temp; + data.smooth_origco = SCULPT_tool_needs_smooth_origco(brush->sculpt_tool); if (brush->flag2 & BRUSH_SMOOTH_USE_AREA_WEIGHT) { BKE_pbvh_face_areas_begin(ss->pbvh); diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.hh b/source/blender/editors/sculpt_paint/sculpt_intern.hh index cc9c758318a..fa6839dbad3 100644 --- a/source/blender/editors/sculpt_paint/sculpt_intern.hh +++ b/source/blender/editors/sculpt_paint/sculpt_intern.hh @@ -319,6 +319,7 @@ struct SculptThreadedTaskData { float angle; float strength; bool smooth_mask; + bool smooth_origco; bool has_bm_orco; SculptProjectVector *spvc; @@ -1212,7 +1213,7 @@ const float *SCULPT_vertex_co_get(SculptSession *ss, PBVHVertRef vertex); void SCULPT_vertex_co_set(SculptSession *ss, PBVHVertRef vertex, const float *co); void SCULPT_vertex_normal_get(SculptSession *ss, PBVHVertRef vertex, float no[3]); const float *SCULPT_vertex_origco_get(SculptSession *ss, PBVHVertRef vertex); -const float *SCULPT_vertex_origno_get(SculptSession *ss, PBVHVertRef vertex); +void SCULPT_vertex_origno_get(SculptSession *ss, PBVHVertRef vertex, float no[3]); const float *SCULPT_vertex_persistent_co_get(SculptSession *ss, PBVHVertRef vertex); void SCULPT_vertex_persistent_normal_get(SculptSession *ss, PBVHVertRef vertex, float no[3]); @@ -1915,7 +1916,8 @@ void SCULPT_neighbor_coords_average_interior(SculptSession *ss, PBVHVertRef vertex, float projection, float hard_corner_pin, - bool use_area_weights); + bool use_area_weights, + bool smooth_origco = false); BLI_INLINE bool SCULPT_need_reproject(const SculptSession *ss) { @@ -2465,3 +2467,8 @@ int SCULPT_vertex_island_get(SculptSession *ss, PBVHVertRef vertex); int SCULPT_get_symmetry_pass(const struct SculptSession *ss); #define SCULPT_boundary_flag_update BKE_sculpt_boundary_flag_update + +/* Some tools need original coordinates to be smoothed during + * autosmooth. + */ +#define SCULPT_tool_needs_smooth_origco(tool) ELEM(tool, SCULPT_TOOL_DRAW_SHARP) diff --git a/source/blender/editors/sculpt_paint/sculpt_smooth.cc b/source/blender/editors/sculpt_paint/sculpt_smooth.cc index d39891fd00d..67ec9f4fa80 100644 --- a/source/blender/editors/sculpt_paint/sculpt_smooth.cc +++ b/source/blender/editors/sculpt_paint/sculpt_smooth.cc @@ -19,6 +19,7 @@ #include "BKE_brush.h" #include "BKE_context.h" +#include "BKE_global.h" #include "BKE_mesh.hh" #include "BKE_mesh_mapping.h" #include "BKE_paint.h" @@ -87,7 +88,8 @@ static void SCULPT_neighbor_coords_average_interior_ex(SculptSession *ss, float hard_corner_pin, bool weighted, eSculptBoundary bound_type, - eSculptCorner corner_type) + eSculptCorner corner_type, + bool smooth_origco) { float3 avg(0.0f, 0.0f, 0.0f); @@ -101,10 +103,18 @@ static void SCULPT_neighbor_coords_average_interior_ex(SculptSession *ss, const eSculptBoundary is_boundary = SCULPT_vertex_is_boundary(ss, vertex, bound_type); const eSculptCorner is_corner = SCULPT_vertex_is_corner(ss, vertex, corner_type); + const float *(*vertex_co_get)(SculptSession * ss, + PBVHVertRef vertex) = smooth_origco ? SCULPT_vertex_origco_get : + SCULPT_vertex_co_get; + void (*vertex_no_get)(SculptSession * ss, + PBVHVertRef vertex, + float r_no[3]) = smooth_origco ? SCULPT_vertex_origno_get : + SCULPT_vertex_normal_get; + float *areas = nullptr; float3 no, co; - SCULPT_vertex_normal_get(ss, vertex, no); - co = SCULPT_vertex_co_get(ss, vertex); + vertex_no_get(ss, vertex, no); + co = vertex_co_get(ss, vertex); if (weighted) { const int valence = SCULPT_vertex_valence_get(ss, vertex); @@ -141,19 +151,19 @@ static void SCULPT_neighbor_coords_average_interior_ex(SculptSession *ss, /* Handle smooth boundaries. */ if (bool(is_boundary2 & smooth_types) != bool(is_boundary & smooth_types)) { /* Project to plane. */ - float3 t1 = float3(SCULPT_vertex_co_get(ss, ni.vertex)) - co; + float3 t1 = float3(vertex_co_get(ss, ni.vertex)) - co; avg += (co + no * dot_v3v3(t1, no)) * w; total += w; } else if (is_boundary & is_boundary2) { - avg += float3(SCULPT_vertex_co_get(ss, ni.vertex)) * w; + avg += float3(vertex_co_get(ss, ni.vertex)) * w; total += w; } } else { /* Interior vertices use all neighbors. */ - avg += float3(SCULPT_vertex_co_get(ss, ni.vertex)) * w; + avg += float3(vertex_co_get(ss, ni.vertex)) * w; total += w; } } @@ -167,7 +177,7 @@ static void SCULPT_neighbor_coords_average_interior_ex(SculptSession *ss, SculptVertexNeighborIter ni; SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vertex, ni) { float w = weighted ? areas[ni.i] : 1.0f; - avg += float3(SCULPT_vertex_co_get(ss, ni.vertex)) * w; + avg += float3(vertex_co_get(ss, ni.vertex)) * w; total += w; } SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); @@ -175,7 +185,7 @@ static void SCULPT_neighbor_coords_average_interior_ex(SculptSession *ss, /* Avoid division by 0 when there are no neighbors. */ if (total == 0) { - copy_v3_v3(result, SCULPT_vertex_co_get(ss, vertex)); + copy_v3_v3(result, vertex_co_get(ss, vertex)); return; } @@ -198,7 +208,8 @@ void SCULPT_neighbor_coords_average_interior(SculptSession *ss, PBVHVertRef vertex, float projection, float hard_corner_pin, - bool use_area_weights) + bool use_area_weights, + bool smooth_origco) { eSculptBoundary bound_type = ss->smooth_boundary_flag; eSculptCorner corner_type; @@ -211,8 +222,15 @@ void SCULPT_neighbor_coords_average_interior(SculptSession *ss, corner_type |= SCULPT_CORNER_FACE_SET; } - SCULPT_neighbor_coords_average_interior_ex( - ss, result, vertex, projection, hard_corner_pin, use_area_weights, bound_type, corner_type); + SCULPT_neighbor_coords_average_interior_ex(ss, + result, + vertex, + projection, + hard_corner_pin, + use_area_weights, + bound_type, + corner_type, + smooth_origco); } /* Compares four vectors seperated by 90 degrees around normal and picks the one closest @@ -270,9 +288,56 @@ void vec_transform(float r_dir2[3], float no[3], int bits) } } +void SCULPT_get_normal_average( + SculptSession *ss, float avg[3], PBVHVertRef vertex, bool weighted, bool use_original) +{ + float tot = 0.0f; + int valence = SCULPT_vertex_valence_get(ss, vertex); + + float *areas = nullptr; + + if (weighted) { + areas = (float *)BLI_array_alloca(areas, valence * 2); + + BKE_pbvh_get_vert_face_areas(ss->pbvh, vertex, areas, valence); + } + + int total = 0; + zero_v3(avg); + + SculptVertexNeighborIter ni; + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vertex, ni) { + float w = weighted ? areas[ni.i] : 1.0f; + float no2[3]; + + if (!use_original) { + SCULPT_vertex_normal_get(ss, ni.vertex, no2); + } + else { + SCULPT_vertex_origno_get(ss, ni.vertex, no2); + } + + madd_v3_v3fl(avg, no2, w); + total++; + } + SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); + + if (total) { + normalize_v3(avg); + } + else { + if (!use_original) { + SCULPT_vertex_normal_get(ss, ni.vertex, avg); + } + else { + SCULPT_vertex_origno_get(ss, ni.vertex, avg); + } + } +} + void SCULPT_bmesh_four_neighbor_average(SculptSession *ss, float avg[3], - float direction[3], + float direction_in[3], BMVert *v, float projection, float hard_corner_pin, @@ -297,7 +362,11 @@ void SCULPT_bmesh_four_neighbor_average(SculptSession *ss, float *origco = BM_ELEM_CD_PTR(v, ss->attrs.orig_co->bmesh_cd_offset); float *origno = BM_ELEM_CD_PTR(v, ss->attrs.orig_no->bmesh_cd_offset); + float direction[3]; + copy_v3_v3(direction, direction_in); + if (do_origco) { + /* Project direction into original normal's plane. */ madd_v3_v3fl(direction, origno, -dot_v3v3(origno, direction)); normalize_v3(direction); } @@ -305,6 +374,22 @@ void SCULPT_bmesh_four_neighbor_average(SculptSession *ss, float *co1 = do_origco ? origco : v->co; float *no1 = do_origco ? origno : v->no; +#if 0 + float no1[3]; + + float avg_no1[3]; + copy_v3_v3(no1, do_origco ? origno : v->no); + + if (G.debug_value != 893) { + SCULPT_get_normal_average(ss, avg_no1, vertex, weighted, do_origco); + interp_v3_v3v3(no1, no1, avg_no1, 1.0f); + } + + /* Project direction into normal's plane. */ + madd_v3_v3fl(direction, no1, -dot_v3v3(no1, direction)); + normalize_v3(direction); +#endif + int valence = SCULPT_vertex_valence_get(ss, vertex); if (weighted) { @@ -390,7 +475,7 @@ void SCULPT_bmesh_four_neighbor_average(SculptSession *ss, } } - closest_vec_to_perp(dir, dir2, no1, buckets, bucketw); // field2[3]); + closest_vec_to_perp(dir, dir2, no1, buckets, bucketw); madd_v3_v3fl(dir3, dir2, dirw); totdir3 += dirw; @@ -402,10 +487,11 @@ void SCULPT_bmesh_four_neighbor_average(SculptSession *ss, float vec[3]; sub_v3_v3v3(vec, co2, co1); - madd_v3_v3fl(vec, no1, -dot_v3v3(vec, no1) * projection); + /* Project into no1's plane. */ + madd_v3_v3fl(vec, no1, -dot_v3v3(vec, no1) * 1.0f); normalize_v3(vec); - /* fac is a measure of how orthogonal or parallel the edge is + /* Fac is a measure of how orthogonal or parallel the edge is * relative to the direction. */ float fac = dot_v3v3(vec, dir); @@ -431,7 +517,6 @@ void SCULPT_bmesh_four_neighbor_average(SculptSession *ss, add_v3_v3(avg, co1); } else { - // zero_v3(avg); copy_v3_v3(avg, co1); } @@ -446,7 +531,7 @@ void SCULPT_bmesh_four_neighbor_average(SculptSession *ss, PBVH_CHECK_NAN(avg); - // do not update in do_origco + /* Do not update field when doing original coordinates. */ if (do_origco) { return; } @@ -497,7 +582,7 @@ void SCULPT_neighbor_coords_average(SculptSession *ss, SCULPT_BOUNDARY_UV | SCULPT_BOUNDARY_FACE_SET; SCULPT_neighbor_coords_average_interior_ex( - ss, result, vertex, projection, hard_corner_pin, weighted, bound_type, corner_type); + ss, result, vertex, projection, hard_corner_pin, weighted, bound_type, corner_type, false); } float SCULPT_neighbor_mask_average(SculptSession *ss, PBVHVertRef vertex) @@ -646,9 +731,9 @@ static void SCULPT_enhance_details_brush(Sculpt *sd, Object *ob, Span(userdata); SculptSession *ss = data->ob->sculpt; @@ -715,7 +800,17 @@ static void do_smooth_brush_task_cb_ex(void *__restrict userdata, float avg[3], val[3]; SCULPT_neighbor_coords_average_interior( - ss, avg, vd.vertex, projection, hard_corner_pin, weighted); + ss, avg, vd.vertex, projection, hard_corner_pin, weighted, false); + + if (data->smooth_origco) { + float origco_avg[3]; + + SCULPT_neighbor_coords_average_interior( + ss, origco_avg, vd.vertex, projection, hard_corner_pin, weighted, true); + float *origco = blender::bke::paint::vertex_attr_ptr(vd.vertex, ss->attrs.orig_co); + interp_v3_v3v3(origco, origco, origco_avg, fade); + } + sub_v3_v3v3(val, avg, vd.co); madd_v3_v3v3fl(val, vd.co, val, fade); SCULPT_clip(sd, ss, vd.co, val); @@ -798,6 +893,7 @@ void SCULPT_smooth( data.brush = brush; data.nodes = nodes; data.smooth_mask = smooth_mask; + data.smooth_origco = SCULPT_tool_needs_smooth_origco(brush->sculpt_tool); data.strength = strength; TaskParallelSettings settings; -- 2.30.2 From 2a0244cf1a73e66f0f11ac68c1ec7f30e31059a4 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Mon, 29 May 2023 22:29:14 -0700 Subject: [PATCH 125/279] temp-sculpt-dyntopo: Fix bug with layer brush exploding the geometry --- source/blender/blenkernel/intern/dyntopo.cc | 5 ++++ .../blenkernel/intern/dyntopo_collapse.cc | 28 +++++++++++++++++-- 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/source/blender/blenkernel/intern/dyntopo.cc b/source/blender/blenkernel/intern/dyntopo.cc index 9105b75c390..029eeaca0fa 100644 --- a/source/blender/blenkernel/intern/dyntopo.cc +++ b/source/blender/blenkernel/intern/dyntopo.cc @@ -2725,6 +2725,11 @@ static void pbvh_split_edges( BM_idmap_release(pbvh->bm_idmap, (BMElem *)e, true); BMVert *newv = BM_edge_split(pbvh->header.bm, e, e->v1, &newe, 0.5f); + int cd_stroke_id = eq_ctx->ss->attrs.stroke_id->bmesh_cd_offset; + + /* Flag new vertex as not needing original data update, since we interpolated it. */ + sculpt::stroke_id_test(eq_ctx->ss, {reinterpret_cast(newv)}, STROKEID_USER_ORIGINAL); + newe->head.hflag &= ~(SPLIT_TAG | BM_ELEM_TAG); e->head.hflag &= ~(SPLIT_TAG | BM_ELEM_TAG); diff --git a/source/blender/blenkernel/intern/dyntopo_collapse.cc b/source/blender/blenkernel/intern/dyntopo_collapse.cc index d288cf7c133..921912ea7bf 100644 --- a/source/blender/blenkernel/intern/dyntopo_collapse.cc +++ b/source/blender/blenkernel/intern/dyntopo_collapse.cc @@ -50,6 +50,7 @@ namespace blender::bke::dyntopo { typedef struct TraceData { PBVH *pbvh; BMEdge *e; + SculptSession *ss; } TraceData; // copied from decimate modifier code @@ -382,7 +383,7 @@ static void vert_ring_do(BMVert *v, } bool pbvh_bmesh_collapse_edge_uvs( - PBVH *pbvh, BMEdge *e, BMVert *v_conn, BMVert *v_del, EdgeQueueContext * /*eq_ctx*/) + PBVH *pbvh, BMEdge *e, BMVert *v_conn, BMVert *v_del, EdgeQueueContext *eq_ctx) { bm_logstack_push(); @@ -418,19 +419,35 @@ bool pbvh_bmesh_collapse_edge_uvs( bool snap = !(boundflag2 & SCULPTVERT_ALL_CORNER); - /* snap customdata */ + /* Snap non-UV attributes. */ if (snap) { + /* Save a few attributes we don't want to snap. */ int ni_conn = BM_ELEM_CD_GET_INT(v_conn, pbvh->cd_vert_node_offset); + StrokeID stroke_id; + if (eq_ctx->ss->attrs.stroke_id) { + stroke_id = blender::bke::paint::vertex_attr_get({(intptr_t)v_conn}, + eq_ctx->ss->attrs.stroke_id); + } const float v_ws[2] = {0.5f, 0.5f}; const void *v_blocks[2] = {v_del->head.data, v_conn->head.data}; CustomData_bmesh_interp( &pbvh->header.bm->vdata, v_blocks, v_ws, nullptr, 2, v_conn->head.data); + + /* Restore node index. */ BM_ELEM_CD_SET_INT(v_conn, pbvh->cd_vert_node_offset, ni_conn); + + /* Restore v_conn's stroke id. This is needed to avoid a nasty + * bug in the layer brush that leads to an exploding mesh. + */ + if (eq_ctx->ss->attrs.stroke_id) { + blender::bke::paint::vertex_attr_set( + {(intptr_t)v_conn}, eq_ctx->ss->attrs.stroke_id, stroke_id); + } } - // deal with UVs + /* Deal with UVs. */ if (e->l) { BMLoop *l = e->l; @@ -562,6 +579,7 @@ BMVert *pbvh_bmesh_collapse_edge(PBVH *pbvh, TraceData tdata; + tdata.ss = eq_ctx->ss; tdata.pbvh = pbvh; tdata.e = e; @@ -598,6 +616,10 @@ BMVert *pbvh_bmesh_collapse_edge(PBVH *pbvh, return nullptr; } + /* Make sure original data is initialized before we snap it. */ + BKE_pbvh_bmesh_check_origdata(eq_ctx->ss, v_conn, pbvh->stroke_id); + BKE_pbvh_bmesh_check_origdata(eq_ctx->ss, v_del, pbvh->stroke_id); + bool uvs_snapped = pbvh_bmesh_collapse_edge_uvs(pbvh, e, v_conn, v_del, eq_ctx); validate_vert_faces(pbvh, pbvh->header.bm, v_conn, false, true); -- 2.30.2 From 02d4bab58d5b7b6ee11f2df6f68ec4a81d5393e2 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Tue, 30 May 2023 12:16:22 -0700 Subject: [PATCH 126/279] temp-sculpt-dyntopo: Remove extraneous code: * Cotangent API * BMesh vertex disk sorting method. * Lots of small changes to BMesh. * Remnants of the original bmesh idmap API. --- source/blender/blenkernel/BKE_customdata.h | 5 - source/blender/blenkernel/BKE_paint.h | 9 - source/blender/blenkernel/BKE_pbvh.h | 1 - .../blender/blenkernel/intern/customdata.cc | 8 - source/blender/blenkernel/intern/dyntopo.cc | 23 +- .../blenkernel/intern/dyntopo_collapse.cc | 6 +- .../blenkernel/intern/dyntopo_intern.hh | 5 +- source/blender/blenkernel/intern/paint.cc | 12 +- .../blender/blenkernel/intern/pbvh_bmesh.cc | 745 +-------- source/blender/bmesh/CMakeLists.txt | 5 +- source/blender/bmesh/bmesh_class.h | 49 - source/blender/bmesh/intern/bmesh_construct.c | 141 +- source/blender/bmesh/intern/bmesh_construct.h | 1 - source/blender/bmesh/intern/bmesh_core.h | 8 - source/blender/bmesh/intern/bmesh_idmap.h | 6 - source/blender/bmesh/intern/bmesh_inline.h | 2 +- source/blender/bmesh/intern/bmesh_interp.c | 27 +- source/blender/bmesh/intern/bmesh_interp.h | 7 + .../blender/bmesh/intern/bmesh_iterators.cc | 1 - .../bmesh/intern/bmesh_iterators_inline.h | 3 +- source/blender/bmesh/intern/bmesh_mesh.cc | 232 +-- source/blender/bmesh/intern/bmesh_mesh.h | 40 - .../intern/bmesh_mesh_convert_threaded.cc | 1374 ----------------- source/blender/bmesh/intern/bmesh_mods.h | 2 - source/blender/bmesh/intern/bmesh_opdefines.c | 3 +- .../bmesh/intern/bmesh_operator_api_inline.h | 1 - source/blender/bmesh/intern/bmesh_polygon.c | 30 +- source/blender/bmesh/intern/bmesh_polygon.h | 40 +- source/blender/bmesh/intern/bmesh_structure.h | 2 - source/blender/bmesh/operators/bmo_dupe.c | 5 +- source/blender/bmesh/operators/bmo_extrude.c | 27 +- source/blender/bmesh/operators/bmo_inset.c | 1 - .../blender/bmesh/operators/bmo_subdivide.c | 5 - source/blender/bmesh/tests/bmesh_core_test.cc | 4 +- .../editors/mesh/editmesh_mask_extract.cc | 3 +- source/blender/editors/mesh/editmesh_tools.cc | 2 +- .../editors/sculpt_paint/paint_mask.cc | 2 - .../editors/sculpt_paint/sculpt_boundary.cc | 149 -- .../editors/sculpt_paint/sculpt_curvature.cc | 20 - .../editors/sculpt_paint/sculpt_dyntopo.cc | 359 +---- .../editors/sculpt_paint/sculpt_intern.hh | 25 - .../editors/sculpt_paint/sculpt_ops.cc | 65 +- .../editors/sculpt_paint/sculpt_smooth.cc | 4 +- .../editors/sculpt_paint/sculpt_undo.cc | 29 +- source/blender/makesdna/DNA_meshdata_types.h | 7 +- source/blender/python/bmesh/bmesh_py_types.c | 132 +- .../python/bmesh/bmesh_py_types_customdata.c | 2 - 47 files changed, 117 insertions(+), 3512 deletions(-) delete mode 100644 source/blender/bmesh/intern/bmesh_mesh_convert_threaded.cc diff --git a/source/blender/blenkernel/BKE_customdata.h b/source/blender/blenkernel/BKE_customdata.h index a59abd677cf..5f17fe18728 100644 --- a/source/blender/blenkernel/BKE_customdata.h +++ b/source/blender/blenkernel/BKE_customdata.h @@ -680,11 +680,6 @@ void CustomData_bmesh_update_active_layers(struct CustomData *fdata, struct Cust void CustomData_bmesh_do_versions_update_active_layers(struct CustomData *fdata, struct CustomData *ldata); -void CustomData_bmesh_init_pool_ex(CustomData *data, - int totelem, - const char htype, - const char *memtag); - void CustomData_bmesh_init_pool(struct CustomData *data, int totelem, char htype); /** diff --git a/source/blender/blenkernel/BKE_paint.h b/source/blender/blenkernel/BKE_paint.h index 78421cb9349..ec23aa550ab 100644 --- a/source/blender/blenkernel/BKE_paint.h +++ b/source/blender/blenkernel/BKE_paint.h @@ -489,14 +489,6 @@ typedef struct SculptBoundaryPreviewEdge { PBVHVertRef v2; } SculptBoundaryPreviewEdge; -#define MAX_STORED_COTANGENTW_EDGES 7 - -typedef struct StoredCotangentW { - float static_weights[MAX_STORED_COTANGENTW_EDGES]; - float *weights; - int length; -} StoredCotangentW; - typedef struct SculptBoundary { /* Vertex indices of the active boundary. */ PBVHVertRef *verts; @@ -512,7 +504,6 @@ typedef struct SculptBoundary { float *boundary_dist; // distances from verts to boundary float (*boundary_tangents)[3]; - StoredCotangentW *boundary_cotangents; PBVHVertRef *boundary_closest; int sculpt_totvert; diff --git a/source/blender/blenkernel/BKE_pbvh.h b/source/blender/blenkernel/BKE_pbvh.h index 7f26acb9563..02dd83ce1c9 100644 --- a/source/blender/blenkernel/BKE_pbvh.h +++ b/source/blender/blenkernel/BKE_pbvh.h @@ -558,7 +558,6 @@ void BKE_pbvh_bmesh_on_mesh_change(PBVH *pbvh); bool BKE_pbvh_bmesh_check_valence(PBVH *pbvh, PBVHVertRef vertex); void BKE_pbvh_bmesh_update_valence(PBVH *pbvh, PBVHVertRef vertex); void BKE_pbvh_bmesh_update_all_valence(PBVH *pbvh); -void BKE_pbvh_bmesh_flag_all_disk_sort(PBVH *pbvh); bool BKE_pbvh_bmesh_mark_update_valence(PBVH *pbvh, PBVHVertRef vertex); /* if pbvh uses a split index buffer, will call BKE_pbvh_vert_tag_update_normal_triangulation; diff --git a/source/blender/blenkernel/intern/customdata.cc b/source/blender/blenkernel/intern/customdata.cc index 3d77a209a17..eed98928a60 100644 --- a/source/blender/blenkernel/intern/customdata.cc +++ b/source/blender/blenkernel/intern/customdata.cc @@ -4092,14 +4092,6 @@ void CustomData_bmesh_update_active_layers(CustomData *fdata, CustomData *ldata) } void CustomData_bmesh_init_pool(CustomData *data, const int totelem, const char htype) -{ - CustomData_bmesh_init_pool_ex(data, totelem, htype, __func__); -} - -void CustomData_bmesh_init_pool_ex(CustomData *data, - int totelem, - const char htype, - const char *memtag) { int chunksize; diff --git a/source/blender/blenkernel/intern/dyntopo.cc b/source/blender/blenkernel/intern/dyntopo.cc index 029eeaca0fa..41b4421e84e 100644 --- a/source/blender/blenkernel/intern/dyntopo.cc +++ b/source/blender/blenkernel/intern/dyntopo.cc @@ -874,7 +874,7 @@ bool check_face_is_tri(PBVH *pbvh, BMFace *f) BMLoop *l = f2->l_first; do { - dyntopo_add_flag(pbvh, l->v, SCULPTFLAG_NEED_DISK_SORT | SCULPTFLAG_NEED_VALENCE); + dyntopo_add_flag(pbvh, l->v, SCULPTFLAG_NEED_VALENCE); } while ((l = l->next) != f2->l_first); } @@ -1070,7 +1070,7 @@ bool destroy_nonmanifold_fins(PBVH *pbvh, BMEdge *e_root) BM_face_kill(pbvh->header.bm, f); } - const int mupdateflag = SCULPTFLAG_NEED_DISK_SORT | SCULPTFLAG_NEED_VALENCE; + const int mupdateflag = SCULPTFLAG_NEED_VALENCE; for (int i = 0; i < es.size(); i++) { BMEdge *e = es[i]; @@ -1225,7 +1225,7 @@ bool _check_vert_fan_are_tris(PBVH *pbvh, BMVert *v) do { pbvh_boundary_update_bmesh(pbvh, l->v); - dyntopo_add_flag(pbvh, l->v, SCULPTFLAG_NEED_VALENCE | SCULPTFLAG_NEED_DISK_SORT); + dyntopo_add_flag(pbvh, l->v, SCULPTFLAG_NEED_VALENCE); } while ((l = l->next) != f->l_first); fs.append(f); @@ -1660,7 +1660,7 @@ static bool cleanup_valence_3_4(EdgeQueueContext *ectx, PBVH *pbvh) const int cd_vert_node = pbvh->cd_vert_node_offset; - int updateflag = SCULPTFLAG_NEED_DISK_SORT | SCULPTFLAG_NEED_VALENCE; + int updateflag = SCULPTFLAG_NEED_VALENCE; for (BMVert *v : ectx->used_verts) { if (bm_elem_is_free((BMElem *)v, BM_VERT)) { @@ -2632,10 +2632,7 @@ static void pbvh_split_edges( l2->v->head.hflag &= ~SPLIT_TAG; pbvh_boundary_update_bmesh(pbvh, l2->v); - dyntopo_add_flag(pbvh, - l2->v, - SCULPTFLAG_NEED_VALENCE | SCULPTFLAG_NEED_DISK_SORT | - SCULPTFLAG_NEED_TRIANGULATE); + dyntopo_add_flag(pbvh, l2->v, SCULPTFLAG_NEED_VALENCE | SCULPTFLAG_NEED_TRIANGULATE); } while ((l2 = l2->next) != l->f->l_first); l->f->head.hflag &= ~SPLIT_TAG; @@ -2753,17 +2750,11 @@ static void pbvh_split_edges( newv->head.hflag |= SPLIT_TAG; pbvh_boundary_update_bmesh(pbvh, newv); - dyntopo_add_flag(pbvh, - newv, - SCULPTFLAG_NEED_VALENCE | SCULPTFLAG_NEED_DISK_SORT | - SCULPTFLAG_NEED_TRIANGULATE); + dyntopo_add_flag(pbvh, newv, SCULPTFLAG_NEED_VALENCE | SCULPTFLAG_NEED_TRIANGULATE); BMVert *otherv = e->v1 != newv ? e->v1 : e->v2; pbvh_boundary_update_bmesh(pbvh, e->v1 != newv ? e->v1 : e->v2); - dyntopo_add_flag(pbvh, - otherv, - SCULPTFLAG_NEED_DISK_SORT | SCULPTFLAG_NEED_VALENCE | - SCULPTFLAG_NEED_TRIANGULATE); + dyntopo_add_flag(pbvh, otherv, SCULPTFLAG_NEED_VALENCE | SCULPTFLAG_NEED_TRIANGULATE); BM_ELEM_CD_SET_INT(newv, pbvh->cd_vert_node_offset, DYNTOPO_NODE_NONE); diff --git a/source/blender/blenkernel/intern/dyntopo_collapse.cc b/source/blender/blenkernel/intern/dyntopo_collapse.cc index 921912ea7bf..fa562843a24 100644 --- a/source/blender/blenkernel/intern/dyntopo_collapse.cc +++ b/source/blender/blenkernel/intern/dyntopo_collapse.cc @@ -207,7 +207,7 @@ static void collapse_ring_callback_pre(BMElem *elem, void *userdata) case BM_VERT: { BMVert *v = reinterpret_cast(elem); - dyntopo_add_flag(data->pbvh, v, SCULPTFLAG_NEED_DISK_SORT | SCULPTFLAG_NEED_VALENCE); + dyntopo_add_flag(data->pbvh, v, SCULPTFLAG_NEED_VALENCE); BM_log_vert_removed(bm, data->pbvh->bm_log, v); pbvh_bmesh_vert_remove(data->pbvh, v); @@ -262,7 +262,7 @@ static void collapse_ring_callback_post(BMElem *elem, void *userdata) case BM_VERT: { BMVert *v = reinterpret_cast(elem); - dyntopo_add_flag(data->pbvh, v, SCULPTFLAG_NEED_DISK_SORT | SCULPTFLAG_NEED_VALENCE); + dyntopo_add_flag(data->pbvh, v, SCULPTFLAG_NEED_VALENCE); check_new_elem_id(elem, data); BM_log_vert_added(bm, data->pbvh->bm_log, v); @@ -583,7 +583,7 @@ BMVert *pbvh_bmesh_collapse_edge(PBVH *pbvh, tdata.pbvh = pbvh; tdata.e = e; - const int mupdateflag = SCULPTFLAG_NEED_VALENCE | SCULPTFLAG_NEED_DISK_SORT; + const int mupdateflag = SCULPTFLAG_NEED_VALENCE; // updateflag |= SCULPTFLAG_NEED_TRIANGULATE; // to check for non-manifold flaps validate_edge(pbvh, pbvh->header.bm, e, true, true); diff --git a/source/blender/blenkernel/intern/dyntopo_intern.hh b/source/blender/blenkernel/intern/dyntopo_intern.hh index 6437ec9a7bb..768c5091bdd 100644 --- a/source/blender/blenkernel/intern/dyntopo_intern.hh +++ b/source/blender/blenkernel/intern/dyntopo_intern.hh @@ -406,10 +406,7 @@ inline void fix_mesh(PBVH *pbvh, BMesh *bm) BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) { v->e = nullptr; - dyntopo_add_flag(pbvh, - v, - SCULPTFLAG_NEED_VALENCE | SCULPTFLAG_NEED_DISK_SORT | - SCULPTFLAG_NEED_TRIANGULATE); + dyntopo_add_flag(pbvh, v, SCULPTFLAG_NEED_VALENCE | SCULPTFLAG_NEED_TRIANGULATE); } BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) { diff --git a/source/blender/blenkernel/intern/paint.cc b/source/blender/blenkernel/intern/paint.cc index 6ec757d12e2..0a9b4a7abb8 100644 --- a/source/blender/blenkernel/intern/paint.cc +++ b/source/blender/blenkernel/intern/paint.cc @@ -2696,16 +2696,14 @@ extern "C" bool BKE_sculpt_init_flags_valence(Object *ob, BM_ITER_MESH (v, &iter, ss->bm, BM_VERTS_OF_MESH) { *BM_ELEM_CD_PTR(v, cd_flags) = SCULPTFLAG_NEED_VALENCE | - SCULPTFLAG_NEED_TRIANGULATE | - SCULPTFLAG_NEED_DISK_SORT; + SCULPTFLAG_NEED_TRIANGULATE; } } else { uint8_t *flags = static_cast(ss->attrs.flags->data); for (int i = 0; i < totvert; i++) { - flags[i] = SCULPTFLAG_NEED_VALENCE | SCULPTFLAG_NEED_TRIANGULATE | - SCULPTFLAG_NEED_DISK_SORT; + flags[i] = SCULPTFLAG_NEED_VALENCE | SCULPTFLAG_NEED_TRIANGULATE; } } } @@ -3087,12 +3085,6 @@ BMesh *BKE_sculptsession_empty_bmesh_create() BMeshCreateParams params = {0}; params.use_toolflags = false; - params.create_unique_ids = true; - params.id_elem_mask = BM_VERT | BM_EDGE | BM_FACE; - params.id_map = true; - params.temporary_ids = false; - - params.no_reuse_ids = false; BMesh *bm = BM_mesh_create(&allocsize, ¶ms); diff --git a/source/blender/blenkernel/intern/pbvh_bmesh.cc b/source/blender/blenkernel/intern/pbvh_bmesh.cc index a66f3932d81..89b7d106b11 100644 --- a/source/blender/blenkernel/intern/pbvh_bmesh.cc +++ b/source/blender/blenkernel/intern/pbvh_bmesh.cc @@ -307,7 +307,7 @@ static BMVert *pbvh_bmesh_vert_create(PBVH *pbvh, BMVert *v = BM_vert_create(pbvh->header.bm, co, nullptr, BM_CREATE_NOP); pbvh_boundary_update_bmesh(pbvh, v); - dyntopo_add_flag(pbvh, v, SCULPTFLAG_NEED_DISK_SORT | SCULPTFLAG_NEED_VALENCE); + dyntopo_add_flag(pbvh, v, SCULPTFLAG_NEED_VALENCE); if (v_example) { v->head.hflag = v_example->head.hflag; @@ -430,7 +430,7 @@ BMFace *pbvh_bmesh_face_create(PBVH *pbvh, } pbvh_boundary_update_bmesh(pbvh, l->v); - dyntopo_add_flag(pbvh, l->v, SCULPTFLAG_NEED_DISK_SORT | SCULPTFLAG_NEED_VALENCE); + dyntopo_add_flag(pbvh, l->v, SCULPTFLAG_NEED_VALENCE); l = l->next; } while (l != f->l_first); @@ -439,7 +439,7 @@ BMFace *pbvh_bmesh_face_create(PBVH *pbvh, BMLoop *l = f->l_first; do { pbvh_boundary_update_bmesh(pbvh, l->v); - dyntopo_add_flag(pbvh, l->v, SCULPTFLAG_NEED_DISK_SORT | SCULPTFLAG_NEED_VALENCE); + dyntopo_add_flag(pbvh, l->v, SCULPTFLAG_NEED_VALENCE); } while ((l = l->next) != f->l_first); } @@ -482,7 +482,7 @@ BMVert *BKE_pbvh_vert_create_bmesh( BM_ELEM_CD_SET_INT(v, pbvh->cd_vert_node_offset, DYNTOPO_NODE_NONE); pbvh_boundary_update_bmesh(pbvh, v); - dyntopo_add_flag(pbvh, v, SCULPTFLAG_NEED_DISK_SORT | SCULPTFLAG_NEED_VALENCE); + dyntopo_add_flag(pbvh, v, SCULPTFLAG_NEED_VALENCE); #if 0 /* XXX: do we need to load origco here? */ copy_v3_v3(mv->origco, co); @@ -858,16 +858,6 @@ static void pbvh_print_mem_size(PBVH *pbvh) tot += memsize3[0] + memsize3[1] + memsize3[2]; printf("total: %.2f\n", tot); - -#ifdef WITH_BM_ID_FREELIST - if (bm->idmap.free_idx_map) { - printf("freelist length: %d\n", bm->idmap.freelist_len); - /* printf("free_idx_map: nentries %d, size %d: nfreecells: %d\n", - bm->idmap.free_idx_map->nentries, - bm->idmap.free_idx_map->nbuckets, - bm->idmap.free_idx_map->nfreecells);*/ - } -#endif } /* Recursively split the node if it exceeds the leaf_limit */ @@ -3095,16 +3085,6 @@ static int pbvh_count_subtree_verts(PBVH *pbvh, PBVHNode *n) return ret; } -void BKE_pbvh_bmesh_flag_all_disk_sort(PBVH *pbvh) -{ - BMVert *v; - BMIter iter; - - BM_ITER_MESH (v, &iter, pbvh->header.bm, BM_VERTS_OF_MESH) { - dyntopo_add_flag(pbvh, v, SCULPTFLAG_NEED_DISK_SORT); - } -} - void BKE_pbvh_bmesh_update_all_valence(PBVH *pbvh) { BMIter iter; @@ -3132,10 +3112,7 @@ void BKE_pbvh_bmesh_on_mesh_change(PBVH *pbvh) int *flags = BM_ELEM_CD_PTR(v, pbvh->cd_boundary_flag); *flags |= SCULPT_BOUNDARY_NEEDS_UPDATE; - dyntopo_add_flag(pbvh, - v, - SCULPTFLAG_NEED_DISK_SORT | SCULPTFLAG_NEED_TRIANGULATE | - SCULPTFLAG_NEED_VALENCE); + dyntopo_add_flag(pbvh, v, SCULPTFLAG_NEED_TRIANGULATE | SCULPTFLAG_NEED_VALENCE); BKE_pbvh_bmesh_update_valence(pbvh, BKE_pbvh_make_vref((intptr_t)v)); } } @@ -4035,718 +4012,6 @@ void BKE_pbvh_update_offsets(PBVH *pbvh, pbvh->cd_origno = cd_origno; } -#define MAX_RE_CHILD 3 -struct ReVertNode { - int totvert, totchild; - struct ReVertNode *parent; - struct ReVertNode *children[MAX_RE_CHILD]; - BMVert *verts[]; -}; - -BMesh *BKE_pbvh_reorder_bmesh(PBVH *pbvh) -{ - /*try to compute size of verts per node*/ - int vsize = sizeof(BMVert); - vsize += pbvh->header.bm->vdata.totsize; - - // perhaps aim for l2 cache? - const int limit = 1024; - int leaf_limit = MAX2(limit / vsize, 4); - - BLI_mempool *pool = BLI_mempool_create(sizeof(ReVertNode) + sizeof(void *) * vsize, 0, 8192, 0); - ReVertNode **vnodemap = (ReVertNode **)MEM_calloc_arrayN( - pbvh->header.bm->totvert, sizeof(void *), "vnodemap"); - - printf("leaf_limit: %d\n", leaf_limit); - - BMIter iter; - BMVert *v; - const char flag = BM_ELEM_TAG_ALT; - int i = 0; - - BM_ITER_MESH (v, &iter, pbvh->header.bm, BM_VERTS_OF_MESH) { - v->head.hflag &= ~flag; - v->head.index = i++; - } - - Vector stack; - - BM_ITER_MESH (v, &iter, pbvh->header.bm, BM_VERTS_OF_MESH) { - if (v->head.hflag & flag) { - continue; - } - - ReVertNode *node = (ReVertNode *)BLI_mempool_calloc(pool); - - stack.clear(); - stack.append(v); - - v->head.hflag |= flag; - - vnodemap[v->head.index] = node; - node->verts[node->totvert++] = v; - - while (stack.size() > 0) { - BMVert *v2 = stack.pop_last(); - BMEdge *e; - - if (node->totvert >= leaf_limit) { - break; - } - - if (!v2->e) { - continue; - } - - int len = node->totvert; - - e = v2->e; - do { - BMVert *v3 = BM_edge_other_vert(e, v2); - - if (!BM_elem_flag_test(v3, flag) && len < leaf_limit) { - v3->head.hflag |= flag; - - vnodemap[v3->head.index] = node; - node->verts[node->totvert++] = v3; - - len++; - - stack.append(v3); - } - - e = e->v1 == v2 ? e->v1_disk_link.next : e->v2_disk_link.next; - } while (e != v2->e); - } - } - - const int steps = 4; - Vector roots; - - for (int step = 0; step < steps; step++) { - const bool last_step = step == steps - 1; - - BM_ITER_MESH_INDEX (v, &iter, pbvh->header.bm, BM_VERTS_OF_MESH, i) { - BMEdge *e = v->e; - - if (!e) { - continue; - } - - ReVertNode *node = vnodemap[v->head.index]; - if (node->parent) { - continue; - } - - ReVertNode *parent = (ReVertNode *)BLI_mempool_calloc(pool); - parent->children[0] = node; - parent->totchild = 1; - - do { - BMVert *v2 = BM_edge_other_vert(e, v); - - ReVertNode *node2 = vnodemap[v2->head.index]; - - bool ok = node != node2 && !node2->parent; - ok = ok && parent->totchild < MAX_RE_CHILD; - - for (int j = 0; j < parent->totchild; j++) { - if (parent->children[j] == node2) { - ok = false; - break; - } - } - - if (ok) { - parent->children[parent->totchild++] = node2; - node2->parent = parent; - break; - } - - e = e->v1 == v ? e->v1_disk_link.next : e->v2_disk_link.next; - } while (e != v->e); - - if (last_step) { - roots.append(parent); - } - - for (int j = 0; j < parent->totchild; j++) { - parent->children[j]->parent = parent; - } - } - - BM_ITER_MESH_INDEX (v, &iter, pbvh->header.bm, BM_VERTS_OF_MESH, i) { - while (vnodemap[i]->parent) { - vnodemap[i] = vnodemap[i]->parent; - } - } - } - - BLI_mempool_iter loopiter; - BLI_mempool_iternew(pbvh->header.bm->lpool, &loopiter); - BMLoop *l = (BMLoop *)BLI_mempool_iterstep(&loopiter); - BMEdge *e; - BMFace *f; - - for (i = 0; l; l = (BMLoop *)BLI_mempool_iterstep(&loopiter), i++) { - l->head.hflag &= ~flag; - } - BM_ITER_MESH (e, &iter, pbvh->header.bm, BM_EDGES_OF_MESH) { - e->head.hflag &= ~flag; - } - - BM_ITER_MESH (f, &iter, pbvh->header.bm, BM_FACES_OF_MESH) { - f->head.hflag &= ~flag; - } - - int totroot = roots.size(); - Vector nstack; - int vorder = 0, eorder = 0, lorder = 0, forder = 0; - - for (i = 0; i < totroot; i++) { - nstack.clear(); - - ReVertNode *node = roots[i]; - nstack.append(node); - - while (nstack.size() > 0) { - ReVertNode *node2 = nstack.pop_last(); - - if (node2->totchild == 0) { - for (int j = 0; j < node2->totvert; j++) { - v = node2->verts[j]; - -#if 0 - const int cd_vcol = CustomData_get_offset(&pbvh->header.bm->vdata,CD_PROP_COLOR); - - if (cd_vcol >= 0) { - MPropCol *col = BM_ELEM_CD_PTR(node2->verts[j],cd_vcol); - - float r = 0.0f,g = 0.0f,b = 0.0f; - - ReVertNode *parent = node2->parent; - for (int j = 0; parent->parent && j < 2; j++) { - parent = parent->parent; - } - - unsigned int p = (unsigned int)node2->parent; - p = p % 65535; - - unsigned int p2 = (unsigned int)parent; - p2 = p2 % 65535; - - r = ((float)vorder) * 0.01; - g = ((float)p2) / 65535.0f; - b = ((float)p2) / 65535.0f; - - r = cosf(r * 17.2343) * 0.5 + 0.5; - g = cosf(g * 11.2343) * 0.5 + 0.5; - b = cosf(b * 19.2343) * 0.5 + 0.5; - - col->color[0] = r; - col->color[1] = g; - col->color[2] = b; - col->color[3] = 1.0f; - } -#endif - v->head.index = vorder++; - - BMEdge *e = v->e; - if (!e) { - continue; - } - - do { - if (!(e->head.hflag & flag)) { - e->head.hflag |= flag; - e->head.index = eorder++; - } - - if (e->l) { - BMLoop *l = e->l; - - do { - if (!(l->head.hflag & flag)) { - l->head.hflag |= flag; - l->head.index = lorder++; - } - - if (!(l->f->head.hflag & flag)) { - l->f->head.hflag |= flag; - l->f->head.index = forder++; - } - - l = l->radial_next; - } while (l != e->l); - } - e = e->v1 == v ? e->v1_disk_link.next : e->v2_disk_link.next; - } while (e != v->e); - } - } - else { - for (int j = 0; j < node2->totchild; j++) { - nstack.append(node2->children[j]); - } - } - } - } - - uint *vidx, *eidx, *lidx, *fidx; - - vidx = MEM_cnew_array(pbvh->header.bm->totvert, "vorder"); - eidx = MEM_cnew_array(pbvh->header.bm->totedge, "eorder"); - lidx = MEM_cnew_array(pbvh->header.bm->totloop, "lorder"); - fidx = MEM_cnew_array(pbvh->header.bm->totface, "forder"); - - printf("v %d %d\n", vorder, pbvh->header.bm->totvert); - printf("e %d %d\n", eorder, pbvh->header.bm->totedge); - printf("l %d %d\n", lorder, pbvh->header.bm->totloop); - printf("f %d %d\n", forder, pbvh->header.bm->totface); - - BM_ITER_MESH_INDEX (v, &iter, pbvh->header.bm, BM_VERTS_OF_MESH, i) { - vidx[i] = (uint)v->head.index; - } - - BM_ITER_MESH_INDEX (e, &iter, pbvh->header.bm, BM_EDGES_OF_MESH, i) { - eidx[i] = (uint)e->head.index; - } - BM_ITER_MESH_INDEX (f, &iter, pbvh->header.bm, BM_FACES_OF_MESH, i) { - fidx[i] = (uint)f->head.index; - } - - BLI_mempool_iternew(pbvh->header.bm->lpool, &loopiter); - l = (BMLoop *)BLI_mempool_iterstep(&loopiter); - - for (i = 0; l; l = (BMLoop *)BLI_mempool_iterstep(&loopiter), i++) { - // handle orphaned loops - if (!(l->head.hflag & flag)) { - printf("warning in %s: orphaned loop!\n", __func__); - l->head.index = lorder++; - } - - lidx[i] = (uint)l->head.index; - } - - printf("roots: %d\n", (int)roots.size()); - - BM_mesh_remap(pbvh->header.bm, vidx, eidx, lidx, fidx); - - MEM_SAFE_FREE(vidx); - MEM_SAFE_FREE(eidx); - MEM_SAFE_FREE(lidx); - MEM_SAFE_FREE(fidx); - - BLI_mempool_destroy(pool); - MEM_SAFE_FREE(vnodemap); - - return pbvh->header.bm; -} - -BMesh *BKE_pbvh_reorder_bmesh2(PBVH *pbvh) -{ - if (BKE_pbvh_type(pbvh) != PBVH_BMESH || pbvh->totnode == 0) { - return pbvh->header.bm; - } - - // try to group memory allocations by node - struct TempNodeData { - Vector verts; - Vector edges; - Vector faces; - }; - - Vector nodedata; - nodedata.resize(pbvh->totnode); - - BMIter iter; - int types[3] = {BM_VERTS_OF_MESH, BM_EDGES_OF_MESH, BM_FACES_OF_MESH}; - -#define VISIT_TAG BM_ELEM_TAG - - BM_mesh_elem_index_ensure(pbvh->header.bm, BM_VERT | BM_EDGE | BM_FACE); - BM_mesh_elem_table_ensure(pbvh->header.bm, BM_VERT | BM_EDGE | BM_FACE); - - for (int i = 0; i < 3; i++) { - BMHeader *elem; - - BM_ITER_MESH (elem, &iter, pbvh->header.bm, types[i]) { - elem->hflag &= ~VISIT_TAG; - } - } - - for (int i = 0; i < pbvh->totnode; i++) { - PBVHNode *node = pbvh->nodes + i; - - if (!(node->flag & PBVH_Leaf)) { - continue; - } - - Vector &verts = nodedata[i].verts; - Vector &edges = nodedata[i].edges; - Vector &faces = nodedata[i].faces; - - BMVert *v; - BMFace *f; - - TGSET_ITER (v, node->bm_unique_verts) { - if (v->head.hflag & VISIT_TAG) { - continue; - } - - v->head.hflag |= VISIT_TAG; - verts.append(v); - - BMEdge *e = v->e; - do { - if (!(e->head.hflag & VISIT_TAG)) { - e->head.hflag |= VISIT_TAG; - edges.append(e); - } - e = v == e->v1 ? e->v1_disk_link.next : e->v2_disk_link.next; - } while (e != v->e); - } - TGSET_ITER_END; - - TGSET_ITER (f, node->bm_faces) { - if (f->head.hflag & VISIT_TAG) { - continue; - } - - faces.append(f); - f->head.hflag |= VISIT_TAG; - } - TGSET_ITER_END; - } - - BMAllocTemplate templ = {pbvh->header.bm->totvert, - pbvh->header.bm->totedge, - pbvh->header.bm->totloop, - pbvh->header.bm->totface}; - struct BMeshCreateParams params = {0}; - - BMesh *bm2 = BM_mesh_create(&templ, ¶ms); - - CustomData_copy_all_layout(&pbvh->header.bm->vdata, &bm2->vdata); - CustomData_copy_all_layout(&pbvh->header.bm->edata, &bm2->edata); - CustomData_copy_all_layout(&pbvh->header.bm->ldata, &bm2->ldata); - CustomData_copy_all_layout(&pbvh->header.bm->pdata, &bm2->pdata); - - CustomData_bmesh_init_pool(&bm2->vdata, pbvh->header.bm->totvert, BM_VERT); - CustomData_bmesh_init_pool(&bm2->edata, pbvh->header.bm->totedge, BM_EDGE); - CustomData_bmesh_init_pool(&bm2->ldata, pbvh->header.bm->totloop, BM_LOOP); - CustomData_bmesh_init_pool(&bm2->pdata, pbvh->header.bm->totface, BM_FACE); - - Vector verts; - Vector edges; - Vector faces; - - for (int i = 0; i < pbvh->totnode; i++) { - for (int j = 0; j < nodedata[i].verts.size(); j++) { - BMVert *v1 = nodedata[i].verts[j]; - BMVert *v2 = BM_vert_create(bm2, v1->co, nullptr, BM_CREATE_NOP); - BM_elem_attrs_copy_ex(pbvh->header.bm, bm2, v1, v2, 0, 0L); - - v2->head.index = v1->head.index = verts.size(); - verts.append(v2); - } - } - - for (int i = 0; i < pbvh->totnode; i++) { - for (int j = 0; j < nodedata[i].edges.size(); j++) { - BMEdge *e1 = nodedata[i].edges[j]; - BMEdge *e2 = BM_edge_create( - bm2, verts[e1->v1->head.index], verts[e1->v2->head.index], nullptr, BM_CREATE_NOP); - BM_elem_attrs_copy_ex(pbvh->header.bm, bm2, e1, e2, 0, 0L); - - e2->head.index = e1->head.index = edges.size(); - edges.append(e2); - } - } - - Vector fvs; - Vector fes; - - for (int i = 0; i < pbvh->totnode; i++) { - for (int j = 0; j < nodedata[i].faces.size(); j++) { - BMFace *f1 = nodedata[i].faces[j]; - - fvs.clear(); - fes.clear(); - - int totloop = 0; - BMLoop *l1 = f1->l_first; - do { - fvs.append(verts[l1->v->head.index]); - fes.append(edges[l1->e->head.index]); - l1 = l1->next; - totloop++; - } while (l1 != f1->l_first); - - BMFace *f2 = BM_face_create(bm2, fvs.data(), fes.data(), totloop, nullptr, BM_CREATE_NOP); - f1->head.index = f2->head.index = faces.size(); - faces.append(f2); - - // CustomData_bmesh_copy_data(&pbvh->header.bm->pdata, &bm2->pdata, f1->head.data, - // &f2->head.data); - BM_elem_attrs_copy_ex(pbvh->header.bm, bm2, f1, f2, 0, 0L); - - BMLoop *l2 = f2->l_first; - do { - BM_elem_attrs_copy_ex(pbvh->header.bm, bm2, l1, l2, 0, 0L); - - l1 = l1->next; - l2 = l2->next; - } while (l2 != f2->l_first); - } - } - - for (int i = 0; i < pbvh->totnode; i++) { - PBVHNode *node = pbvh->nodes + i; - - if (!(node->flag & PBVH_Leaf)) { - continue; - } - - int totunique = node->bm_unique_verts->length; - int totother = node->bm_other_verts->length; - int totface = node->bm_faces->length; - - TableGSet *bm_faces = BLI_table_gset_new_ex("bm_faces", totface); - TableGSet *bm_other_verts = BLI_table_gset_new_ex("bm_other_verts", totunique); - TableGSet *bm_unique_verts = BLI_table_gset_new_ex("bm_unique_verts", totother); - - BMVert *v; - BMFace *f; - - TGSET_ITER (v, node->bm_unique_verts) { - BLI_table_gset_insert(bm_unique_verts, verts[v->head.index]); - } - TGSET_ITER_END; - TGSET_ITER (v, node->bm_other_verts) { - BLI_table_gset_insert(bm_other_verts, verts[v->head.index]); - } - TGSET_ITER_END; - TGSET_ITER (f, node->bm_faces) { - BLI_table_gset_insert(bm_faces, faces[f->head.index]); - } - TGSET_ITER_END; - - BLI_table_gset_free(node->bm_faces, nullptr); - BLI_table_gset_free(node->bm_other_verts, nullptr); - BLI_table_gset_free(node->bm_unique_verts, nullptr); - - node->bm_faces = bm_faces; - node->bm_other_verts = bm_other_verts; - node->bm_unique_verts = bm_unique_verts; - - node->flag |= PBVH_UpdateTris | PBVH_UpdateRedraw; - } - - BM_mesh_free(pbvh->header.bm); - pbvh->header.bm = bm2; - - return bm2; -} - -struct SortElem { - BMElem *elem; - int index; - int cd_node_off; -}; - -static int sort_verts_faces(const void *va, const void *vb) -{ - SortElem *a = (SortElem *)va; - SortElem *b = (SortElem *)vb; - int ni1 = BM_ELEM_CD_GET_INT(a->elem, a->cd_node_off); - int ni2 = BM_ELEM_CD_GET_INT(b->elem, b->cd_node_off); - - return ni1 - ni2; -} - -static int sort_edges(const void *va, const void *vb) -{ - SortElem *a = (SortElem *)va; - SortElem *b = (SortElem *)vb; - - BMEdge *e1 = (BMEdge *)a->elem; - BMEdge *e2 = (BMEdge *)b->elem; - - int ni1 = BM_ELEM_CD_GET_INT(e1->v1, a->cd_node_off); - int ni2 = BM_ELEM_CD_GET_INT(e1->v2, a->cd_node_off); - int ni3 = BM_ELEM_CD_GET_INT(e2->v1, b->cd_node_off); - int ni4 = BM_ELEM_CD_GET_INT(e2->v2, b->cd_node_off); - - return (ni1 + ni2) - (ni3 + ni4); -} - -BMesh *BKE_pbvh_reorder_bmesh1(PBVH *pbvh) -{ - BMesh *bm = pbvh->header.bm; - - Vector> save_other_vs; - Vector> save_unique_vs; - Vector> save_fs; - - save_other_vs.resize(pbvh->totnode); - save_unique_vs.resize(pbvh->totnode); - save_fs.resize(pbvh->totnode); - - SortElem *verts = MEM_cnew_array(bm->totvert, __func__); - SortElem *edges = MEM_cnew_array(bm->totedge, __func__); - SortElem *faces = MEM_cnew_array(bm->totface, __func__); - - BMIter iter; - BMVert *v; - BMEdge *e; - BMFace *f; - - int i = 0; - - BM_ITER_MESH_INDEX (v, &iter, bm, BM_VERTS_OF_MESH, i) { - verts[i].elem = (BMElem *)v; - verts[i].cd_node_off = pbvh->cd_vert_node_offset; - verts[i].index = i; - v->head.index = i; - } - BM_ITER_MESH_INDEX (e, &iter, bm, BM_EDGES_OF_MESH, i) { - edges[i].elem = (BMElem *)e; - edges[i].cd_node_off = pbvh->cd_vert_node_offset; - edges[i].index = i; - e->head.index = i; - } - BM_ITER_MESH_INDEX (f, &iter, bm, BM_FACES_OF_MESH, i) { - faces[i].elem = (BMElem *)f; - faces[i].cd_node_off = pbvh->cd_face_node_offset; - faces[i].index = i; - f->head.index = i; - } - - for (i = 0; i < pbvh->totnode; i++) { - Vector other_vs; - Vector unique_vs; - Vector fs; - - PBVHNode *node = pbvh->nodes + i; - if (!(node->flag & PBVH_Leaf)) { - continue; - } - - BMVert *v; - BMFace *f; - - TGSET_ITER (v, node->bm_unique_verts) { - unique_vs.append(v->head.index); - } - TGSET_ITER_END; - TGSET_ITER (v, node->bm_other_verts) { - other_vs.append(v->head.index); - } - TGSET_ITER_END; - TGSET_ITER (f, node->bm_faces) { - fs.append(f->head.index); - } - TGSET_ITER_END; - - save_unique_vs[i] = unique_vs; - save_other_vs[i] = other_vs; - save_fs[i] = fs; - } - - qsort(verts, bm->totvert, sizeof(SortElem), sort_verts_faces); - qsort(edges, bm->totedge, sizeof(SortElem), sort_edges); - qsort(faces, bm->totface, sizeof(SortElem), sort_verts_faces); - - uint *vs = MEM_cnew_array(bm->totvert, __func__); - uint *es = MEM_cnew_array(bm->totedge, __func__); - uint *fs = MEM_cnew_array(bm->totface, __func__); - - for (i = 0; i < bm->totvert; i++) { - vs[i] = (uint)verts[i].index; - verts[i].elem->head.index = verts[i].index; - } - for (i = 0; i < bm->totedge; i++) { - es[i] = (uint)edges[i].index; - edges[i].elem->head.index = edges[i].index; - } - for (i = 0; i < bm->totface; i++) { - fs[i] = (uint)faces[i].index; - faces[i].elem->head.index = faces[i].index; - } - - BM_mesh_remap(bm, vs, es, nullptr, fs); - - // create new mappings - BMVert **mapvs = MEM_cnew_array(bm->totvert, __func__); - BMEdge **mapes = MEM_cnew_array(bm->totedge, __func__); - BMFace **mapfs = MEM_cnew_array(bm->totface, __func__); - - BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) { - mapvs[v->head.index] = v; - } - BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) { - mapes[e->head.index] = e; - } - BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { - mapfs[f->head.index] = f; - } - - // rebuild bm_unique_verts bm_other_verts and bm_faces in pbvh nodes - for (i = 0; i < pbvh->totnode; i++) { - PBVHNode *node = pbvh->nodes + i; - - if (!(node->flag & PBVH_Leaf)) { - continue; - } - - int tot_unique_vs = BLI_table_gset_len(node->bm_unique_verts); - int tot_other_vs = BLI_table_gset_len(node->bm_other_verts); - int tot_fs = BLI_table_gset_len(node->bm_faces); - - BLI_table_gset_free(node->bm_unique_verts, nullptr); - BLI_table_gset_free(node->bm_other_verts, nullptr); - BLI_table_gset_free(node->bm_faces, nullptr); - - node->bm_unique_verts = BLI_table_gset_new("bm_unique_verts"); - node->bm_other_verts = BLI_table_gset_new("bm_other_verts"); - node->bm_faces = BLI_table_gset_new("bm_faces"); - - Vector &unique_vs = save_unique_vs[i]; - Vector &other_vs = save_other_vs[i]; - Vector &fs = save_fs[i]; - - for (int j = 0; j < tot_unique_vs; j++) { - BLI_table_gset_add(node->bm_unique_verts, mapvs[unique_vs[j]]); - } - for (int j = 0; j < tot_other_vs; j++) { - BLI_table_gset_add(node->bm_other_verts, mapvs[other_vs[j]]); - } - - for (int j = 0; j < tot_fs; j++) { - BLI_table_gset_add(node->bm_faces, mapfs[fs[j]]); - } - - node->flag |= PBVH_UpdateTris; - } - - MEM_SAFE_FREE(mapvs); - MEM_SAFE_FREE(mapes); - MEM_SAFE_FREE(mapfs); - - bm->elem_index_dirty |= BM_VERT | BM_EDGE | BM_FACE; - bm->elem_table_dirty |= BM_VERT | BM_EDGE | BM_FACE; - - MEM_SAFE_FREE(vs); - MEM_SAFE_FREE(es); - MEM_SAFE_FREE(fs); - - MEM_SAFE_FREE(verts); - MEM_SAFE_FREE(edges); - MEM_SAFE_FREE(faces); - - return pbvh->header.bm; -} - /* restore bmesh references from previously indices saved by BKE_pbvh_bmesh_save_indices */ void BKE_pbvh_bmesh_from_saved_indices(PBVH *pbvh) { diff --git a/source/blender/bmesh/CMakeLists.txt b/source/blender/bmesh/CMakeLists.txt index 696041d96e2..39f9c776b43 100644 --- a/source/blender/bmesh/CMakeLists.txt +++ b/source/blender/bmesh/CMakeLists.txt @@ -13,7 +13,6 @@ set(INC ../../../intern/eigen ../../../intern/guardedalloc ../../../extern/rangetree - ../python/bmesh ) set(INC_SYS @@ -85,7 +84,6 @@ set(SRC intern/bmesh_marking.h intern/bmesh_mesh.cc intern/bmesh_mesh.h - intern/bmesh_mesh_convert_threaded.cc intern/bmesh_mesh_convert.cc intern/bmesh_mesh_convert.h intern/bmesh_mesh_debug.c @@ -176,9 +174,8 @@ set(SRC set(LIB bf_blenkernel bf_blenlib - extern_rangetree ) - + if(WITH_BULLET) list(APPEND INC_SYS ${BULLET_INCLUDE_DIRS} diff --git a/source/blender/bmesh/bmesh_class.h b/source/blender/bmesh/bmesh_class.h index 664f4663134..947f8fd10a1 100644 --- a/source/blender/bmesh/bmesh_class.h +++ b/source/blender/bmesh/bmesh_class.h @@ -10,9 +10,6 @@ */ #include "BLI_assert.h" -#include "BLI_compiler_compat.h" -#include "BLI_compiler_typecheck.h" -#include "BLI_utildefines.h" #ifdef __cplusplus extern "C" { @@ -27,7 +24,6 @@ struct BMFace; struct BMLoop; struct BMVert; struct BMesh; -struct GSet; struct MLoopNorSpaceArray; @@ -105,10 +101,6 @@ typedef struct BMVert { struct BMEdge *e; } BMVert; -#define BMVert_OFlag BMVert -#define BMEdge_OFlag BMEdge -#define BMFace_OFlag BMFace - /* disk link structure, only used by edges */ typedef struct BMDiskLink { struct BMEdge *next, *prev; @@ -290,10 +282,6 @@ typedef struct BMFlagLayer { // #pragma GCC diagnostic ignored "-Wpadded" -struct RangeTreeUInt; - -#define WITH_BM_ID_FREELIST - typedef struct BMesh { int totvert, totedge, totloop, totface; int totvertsel, totedgesel, totfacesel; @@ -379,41 +367,8 @@ typedef struct BMesh { * Doesn't hold a #PyObject reference, cleared when the last object is de-referenced. */ void *py_handle; - - struct { - int flag; -#ifdef WITH_BM_ID_FREELIST - uint *freelist; - int freelist_len, freelist_size; - uint *free_ids, free_ids_size; - - /* maps ids to their position within the freelist - only used if freelist is bigger then a certain size, - see FREELIST_HASHMAP_THRESHOLD_HIGH in bmesh_construct.c.*/ - struct GHash *free_idx_map; -#else - struct RangeTreeUInt *idtree; -#endif - uint maxid; - struct BMElem **map; // used if BM_NO_REUSE_IDS is false - struct GHash *ghash; // used if BM_NO_REUSE_IDS is true - int map_size; - int cd_id_off[15]; - } idmap; - -#ifdef USE_BMESH_PAGE_CUSTOMDATA - struct BMeshAttrList *attr_list; -#endif } BMesh; -enum { - // firsst four bits are reserved for BM_VERT/EDGE/LOOP/FACE - BM_HAS_IDS = 1 << 4, - BM_HAS_ID_MAP = 1 << 5, - BM_NO_REUSE_IDS = 1 << 6, - BM_PERMANENT_IDS = 1 << 7 -}; - /** #BMHeader.htype (char) */ enum { BM_VERT = 1, @@ -712,10 +667,6 @@ extern "C" { # define BM_OMP_LIMIT 10000 #endif -/* note does not check if ids are enabled for a given element type */ -#define BM_ELEM_GET_ID(bm, elem) \ - BM_ELEM_CD_GET_INT(elem, bm->idmap.cd_id_off[(int)(elem)->head.htype]) - #ifdef __cplusplus } #endif diff --git a/source/blender/bmesh/intern/bmesh_construct.c b/source/blender/bmesh/intern/bmesh_construct.c index 93792f997b6..acf5cb8b5f2 100644 --- a/source/blender/bmesh/intern/bmesh_construct.c +++ b/source/blender/bmesh/intern/bmesh_construct.c @@ -24,15 +24,6 @@ #include "bmesh.h" #include "intern/bmesh_private.h" -#include "range_tree.h" - -#define SELECT 1 - -/** - * Fill in a vertex array from an edge array. - * - * \returns false if any verts aren't found. - */ bool BM_verts_from_edges(BMVert **vert_arr, BMEdge **edge_arr, const int len) { int i, i_prev = len - 1; @@ -331,79 +322,6 @@ void BM_verts_sort_radial_plane(BMVert **vert_arr, int len) } } -void BM_sort_disk_cycle(BMVert *v) -{ - BMVert **vs = NULL; - BLI_array_staticdeclare(vs, 64); - BMEdge **es = NULL; - BLI_array_staticdeclare(es, 64); - - if (!v->e) { - return; - } - - BMEdge *e = v->e; - do { - BMVert *v2 = BM_edge_other_vert(e, v); - - BLI_array_append(es, e); - BLI_array_append(vs, v2); - - e = v == e->v1 ? e->v1_disk_link.next : e->v2_disk_link.next; - } while (e != v->e); - - if (BLI_array_len(vs) < 2) { - return; - } - - int totvert = BLI_array_len(vs); - - struct SortIntByFloat *vang = BLI_array_alloca(vang, totvert); - BMVert **vert_arr_map = BLI_array_alloca(vert_arr_map, totvert); - - float nor[3], cent[3]; - int index_tangent = 0; - BM_verts_calc_normal_from_cloud_ex(vs, totvert, nor, cent, &index_tangent); - const float *far = vs[index_tangent]->co; - - /* Now calculate every points angle around the normal (signed). */ - for (int i = 0; i < totvert; i++) { - vang[i].sort_value = angle_signed_on_axis_v3v3v3_v3(far, cent, vs[i]->co, nor); - vang[i].data = i; - vert_arr_map[i] = vs[i]; - } - - /* sort by angle and magic! - we have our ngon */ - qsort(vang, totvert, sizeof(*vang), BLI_sortutil_cmp_float); - - BMEdge **es2 = BLI_array_alloca(es2, totvert); - - /* --- */ - - for (int i = 0; i < totvert; i++) { - es2[i] = es[vang[i].data]; - } - - // rebuild disk cycle - for (int i = 0; i < totvert; i++) { - int prev = (i + totvert - 1) % totvert; - int next = (i + 1) % totvert; - BMEdge *e = es2[i]; - - if (e->v1 == v) { - e->v1_disk_link.prev = es2[prev]; - e->v1_disk_link.next = es2[next]; - } - else { - e->v2_disk_link.prev = es2[prev]; - e->v2_disk_link.next = es2[next]; - } - } - - BLI_array_free(es); - BLI_array_free(vs); -} - /*************************************************************/ static void bm_vert_attrs_copy( @@ -728,7 +646,7 @@ void BM_mesh_copy_init_customdata_all_layers(BMesh *bm_dst, } } -BMesh *BM_mesh_copy_ex(BMesh *bm_old, struct BMeshCreateParams *params) +BMesh *BM_mesh_copy(BMesh *bm_old) { BMesh *bm_new; BMVert *v, *v_new, **vtable = NULL; @@ -739,46 +657,15 @@ BMesh *BM_mesh_copy_ex(BMesh *bm_old, struct BMeshCreateParams *params) BMIter iter; int i; const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_BM(bm_old); - struct BMeshCreateParams _params = {0}; + struct BMeshCreateParams params = {}; - if (!params) { - _params.use_toolflags = bm_old->use_toolflags; - params = &_params; - } + params.use_toolflags = bm_old->use_toolflags; /* allocate a bmesh */ - bm_new = BM_mesh_create(&allocsize, params); + bm_new = BM_mesh_create(&allocsize, ¶ms); - if (params->copy_all_layers) { - BM_mesh_copy_init_customdata_all_layers( - bm_new, bm_old, BM_VERT | BM_EDGE | BM_LOOP | BM_FACE, &allocsize); - } - else { - BM_mesh_copy_init_customdata(bm_new, bm_old, &allocsize); - } - - if (bm_old->idmap.flag & BM_HAS_IDS) { - MEM_SAFE_FREE(bm_new->idmap.map); - - if ((bm_old->idmap.flag & BM_HAS_ID_MAP)) { - if (!(bm_old->idmap.flag & BM_NO_REUSE_IDS)) { - bm_new->idmap.map_size = bm_old->idmap.map_size; - bm_new->idmap.flag = bm_old->idmap.flag; - - if (bm_new->idmap.map_size) { - bm_new->idmap.map = MEM_callocN(sizeof(void *) * bm_old->idmap.map_size, "bm idmap"); - } - else { - bm_new->idmap.map = NULL; - } - } - else { - BLI_ghash_free(bm_new->idmap.ghash, NULL, NULL); - bm_new->idmap.ghash = BLI_ghash_ptr_new_ex( - "idmap.ghash", bm_old->totvert + bm_old->totedge + bm_old->totface); - } - } - } + BM_mesh_copy_init_customdata_all_layers( + bm_new, bm_old, BM_VERT | BM_EDGE | BM_LOOP | BM_FACE, &allocsize); vtable = MEM_mallocN(sizeof(BMVert *) * bm_old->totvert, "BM_mesh_copy vtable"); etable = MEM_mallocN(sizeof(BMEdge *) * bm_old->totedge, "BM_mesh_copy etable"); @@ -829,14 +716,6 @@ BMesh *BM_mesh_copy_ex(BMesh *bm_old, struct BMeshCreateParams *params) f_new = bm_mesh_copy_new_face(bm_new, bm_old, vtable, etable, f); - if (bm_new->idmap.flag & BM_LOOP) { - BMLoop *l_new = f_new->l_first; - - do { - l_new = l_new->next; - } while (l_new != f_new->l_first); - } - ftable[i] = f_new; if (f == bm_old->act_face) { @@ -891,11 +770,3 @@ BMesh *BM_mesh_copy_ex(BMesh *bm_old, struct BMeshCreateParams *params) return bm_new; } - -BMesh *BM_mesh_copy(BMesh *bm_old) -{ - struct BMeshCreateParams params = {0}; - - params.copy_all_layers = true; - return BM_mesh_copy_ex(bm_old, ¶ms); -} diff --git a/source/blender/bmesh/intern/bmesh_construct.h b/source/blender/bmesh/intern/bmesh_construct.h index 75ce48becdb..c5020a7a88d 100644 --- a/source/blender/bmesh/intern/bmesh_construct.h +++ b/source/blender/bmesh/intern/bmesh_construct.h @@ -171,7 +171,6 @@ void BM_mesh_copy_init_customdata_all_layers(BMesh *bm_dst, BMesh *BM_mesh_copy(BMesh *bm_old); /* BM -> ME */ -void BM_sort_disk_cycle(BMVert *v); #ifdef __cplusplus } diff --git a/source/blender/bmesh/intern/bmesh_core.h b/source/blender/bmesh/intern/bmesh_core.h index 0317cc21beb..019501a35eb 100644 --- a/source/blender/bmesh/intern/bmesh_core.h +++ b/source/blender/bmesh/intern/bmesh_core.h @@ -10,14 +10,6 @@ extern "C" { #endif -typedef struct BMCustomLayerReq { - int type; - const char *name; // can be NULL - int flag; -} BMCustomLayerReq; - -struct BMTracer; - BMFace *BM_face_copy(BMesh *bm_dst, BMesh *bm_src, BMFace *f, bool copy_verts, bool copy_edges); typedef enum eBMCreateFlag { diff --git a/source/blender/bmesh/intern/bmesh_idmap.h b/source/blender/bmesh/intern/bmesh_idmap.h index be6f9148338..d3d90bf7887 100644 --- a/source/blender/bmesh/intern/bmesh_idmap.h +++ b/source/blender/bmesh/intern/bmesh_idmap.h @@ -4,14 +4,8 @@ #include "bmesh.h" -#ifndef WITH_BM_ID_FREELIST -# define WITH_BM_ID_FREELIST -#endif - //#define DEBUG_BM_IDMAP /* Debug idmap; note: disables mempool deallocation */ -#define USE_NEW_IDMAP - #define BM_ID_NONE 0 //-1 #ifdef __cplusplus diff --git a/source/blender/bmesh/intern/bmesh_inline.h b/source/blender/bmesh/intern/bmesh_inline.h index f897ec73af7..ac76c6bb9ca 100644 --- a/source/blender/bmesh/intern/bmesh_inline.h +++ b/source/blender/bmesh/intern/bmesh_inline.h @@ -65,7 +65,7 @@ BLI_INLINE void _bm_elem_flag_merge(BMHeader *head_a, BMHeader *head_b) BLI_INLINE void _bm_elem_flag_merge_ex(BMHeader *head_a, BMHeader *head_b, const char hflag_and) { - if (((head_a->hflag & head_b->hflag) & hflag_and) == (char)0) { + if (((head_a->hflag & head_b->hflag) & hflag_and) == 0) { head_a->hflag &= (char)(~hflag_and); head_b->hflag &= (char)(~hflag_and); } diff --git a/source/blender/bmesh/intern/bmesh_interp.c b/source/blender/bmesh/intern/bmesh_interp.c index 1f8c25129fc..0a6a1152b02 100644 --- a/source/blender/bmesh/intern/bmesh_interp.c +++ b/source/blender/bmesh/intern/bmesh_interp.c @@ -802,9 +802,6 @@ void BM_vert_interp_from_face(BMesh *bm, BMVert *v_dst, const BMFace *f_src) CustomData_bmesh_interp(&bm->vdata, blocks, w, NULL, f_src->len, v_dst->head.data); } -void BPy_bm_new_customdata_layout(BMesh *bm, CustomData *cdata, void *state, char htype); -void *BPy_bm_new_customdata_layout_pre(BMesh *bm, CustomData *cdata, char htype); - static void update_data_blocks(BMesh *bm, CustomData *olddata, CustomData *data) { BMIter iter; @@ -825,8 +822,7 @@ static void update_data_blocks(BMesh *bm, CustomData *olddata, CustomData *data) if (data == &bm->vdata) { BMVert *eve; - CustomData_bmesh_init_pool_ex(data, bm->totvert, BM_VERT, __func__); - void *state = BPy_bm_new_customdata_layout_pre(bm, olddata, BM_VERT); + CustomData_bmesh_init_pool(data, bm->totvert, BM_VERT); BM_ITER_MESH (eve, &iter, bm, BM_VERTS_OF_MESH) { block = NULL; @@ -835,15 +831,11 @@ static void update_data_blocks(BMesh *bm, CustomData *olddata, CustomData *data) CustomData_bmesh_free_block(olddata, &eve->head.data); eve->head.data = block; } - - BPy_bm_new_customdata_layout(bm, &bm->vdata, state, BM_VERT); - MEM_SAFE_FREE(state); } else if (data == &bm->edata) { BMEdge *eed; - CustomData_bmesh_init_pool_ex(data, bm->totedge, BM_EDGE, __func__); - void *state = BPy_bm_new_customdata_layout_pre(bm, olddata, BM_EDGE); + CustomData_bmesh_init_pool(data, bm->totedge, BM_EDGE); BM_ITER_MESH (eed, &iter, bm, BM_EDGES_OF_MESH) { block = NULL; @@ -852,17 +844,13 @@ static void update_data_blocks(BMesh *bm, CustomData *olddata, CustomData *data) CustomData_bmesh_free_block(olddata, &eed->head.data); eed->head.data = block; } - - BPy_bm_new_customdata_layout(bm, &bm->edata, state, BM_EDGE); - MEM_SAFE_FREE(state); } else if (data == &bm->ldata) { BMIter liter; BMFace *efa; BMLoop *l; - CustomData_bmesh_init_pool_ex(data, bm->totloop, BM_LOOP, __func__); - void *state = BPy_bm_new_customdata_layout_pre(bm, olddata, BM_LOOP); + CustomData_bmesh_init_pool(data, bm->totloop, BM_LOOP); BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) { BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { @@ -873,15 +861,11 @@ static void update_data_blocks(BMesh *bm, CustomData *olddata, CustomData *data) l->head.data = block; } } - - BPy_bm_new_customdata_layout(bm, &bm->ldata, state, BM_LOOP); - MEM_SAFE_FREE(state); } else if (data == &bm->pdata) { BMFace *efa; - CustomData_bmesh_init_pool_ex(data, bm->totface, BM_FACE, __func__); - void *state = BPy_bm_new_customdata_layout_pre(bm, olddata, BM_FACE); + CustomData_bmesh_init_pool(data, bm->totface, BM_FACE); BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) { block = NULL; @@ -890,9 +874,6 @@ static void update_data_blocks(BMesh *bm, CustomData *olddata, CustomData *data) CustomData_bmesh_free_block(olddata, &efa->head.data); efa->head.data = block; } - - BPy_bm_new_customdata_layout(bm, &bm->pdata, state, BM_FACE); - MEM_SAFE_FREE(state); } else { /* should never reach this! */ diff --git a/source/blender/bmesh/intern/bmesh_interp.h b/source/blender/bmesh/intern/bmesh_interp.h index bf11fa45e09..9dfc0cedd0f 100644 --- a/source/blender/bmesh/intern/bmesh_interp.h +++ b/source/blender/bmesh/intern/bmesh_interp.h @@ -60,7 +60,14 @@ void BM_data_interp_from_edges( */ void BM_data_interp_face_vert_edge( BMesh *bm, const BMVert *v_src_1, const BMVert *v_src_2, BMVert *v, BMEdge *e, float fac); + +typedef struct BMCustomLayerReq { + int type; + const char *name; /* Can be NULL. */ + int flag; +} BMCustomLayerReq; void BM_data_layers_ensure(BMesh *bm, CustomData *data, BMCustomLayerReq *layers, int totlayer); + void BM_data_layer_add(BMesh *bm, CustomData *data, int type); void BM_data_layer_add_named(BMesh *bm, CustomData *data, int type, const char *name); void BM_data_layer_ensure_named(BMesh *bm, CustomData *data, int type, const char *name); diff --git a/source/blender/bmesh/intern/bmesh_iterators.cc b/source/blender/bmesh/intern/bmesh_iterators.cc index ba90cc012e5..ba8e71f934a 100644 --- a/source/blender/bmesh/intern/bmesh_iterators.cc +++ b/source/blender/bmesh/intern/bmesh_iterators.cc @@ -11,7 +11,6 @@ #include "MEM_guardedalloc.h" #include "BLI_bitmap.h" -#include "BLI_compiler_attrs.h" #include "BLI_utildefines.h" #include "bmesh.h" diff --git a/source/blender/bmesh/intern/bmesh_iterators_inline.h b/source/blender/bmesh/intern/bmesh_iterators_inline.h index 43a86082706..0fdc2f39ee8 100644 --- a/source/blender/bmesh/intern/bmesh_iterators_inline.h +++ b/source/blender/bmesh/intern/bmesh_iterators_inline.h @@ -28,7 +28,8 @@ ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1) BLI_INLINE void *BM_iter_step(BMIter *it * it with the appropriate function pointers based * upon its type. */ -ATTR_NONNULL(1) BLI_INLINE bool BM_iter_init(BMIter *iter, BMesh *bm, const char itype, void *data) +ATTR_NONNULL(1) +BLI_INLINE bool BM_iter_init(BMIter *iter, BMesh *bm, const char itype, void *data) { /* int argtype; */ iter->itype = itype; diff --git a/source/blender/bmesh/intern/bmesh_mesh.cc b/source/blender/bmesh/intern/bmesh_mesh.cc index 5f2ec51f02d..ccb9812989a 100644 --- a/source/blender/bmesh/intern/bmesh_mesh.cc +++ b/source/blender/bmesh/intern/bmesh_mesh.cc @@ -43,18 +43,10 @@ static void bm_mempool_init_ex(const BMAllocTemplate *allocsize, { size_t vert_size, edge_size, loop_size, face_size; - if (use_toolflags == true) { - vert_size = sizeof(BMVert_OFlag); - edge_size = sizeof(BMEdge_OFlag); - loop_size = sizeof(BMLoop); - face_size = sizeof(BMFace_OFlag); - } - else { - vert_size = sizeof(BMVert); - edge_size = sizeof(BMEdge); - loop_size = sizeof(BMLoop); - face_size = sizeof(BMFace); - } + vert_size = sizeof(BMVert); + edge_size = sizeof(BMEdge); + loop_size = sizeof(BMLoop); + face_size = sizeof(BMFace); if (r_vpool) { *r_vpool = BLI_mempool_create( @@ -134,23 +126,6 @@ void BM_mesh_elem_toolflags_clear(BMesh *bm) } } -// int cdmap[8] = {0, 1, -1, -1, 2, -1, -1, -1, 3}; - -static void bm_swap_cd_data(int /*htype*/, BMesh * /*bm*/, CustomData *cd, void *a, void *b) -{ - int tot = cd->totsize; - // int cd_id = bm->idmap.cd_id_off[htype]; - - char *sa = (char *)a; - char *sb = (char *)b; - - for (int i = 0; i < tot; i++, sa++, sb++) { - char tmp = *sa; - *sa = *sb; - *sb = tmp; - } -} - BMesh *BM_mesh_create(const BMAllocTemplate *allocsize, const struct BMeshCreateParams *params) { /* allocate the structure */ @@ -176,22 +151,18 @@ BMesh *BM_mesh_create(const BMAllocTemplate *allocsize, const struct BMeshCreate bm_alloc_toolflags_cdlayers(bm, false); } - if (params->create_unique_ids) { - init_cdata_pools = true; - } - if (init_cdata_pools) { if (bm->vdata.totlayer) { - CustomData_bmesh_init_pool_ex(&bm->vdata, 0, BM_VERT, __func__); + CustomData_bmesh_init_pool(&bm->vdata, 0, BM_VERT); } if (bm->edata.totlayer) { - CustomData_bmesh_init_pool_ex(&bm->edata, 0, BM_EDGE, __func__); + CustomData_bmesh_init_pool(&bm->edata, 0, BM_EDGE); } if (bm->ldata.totlayer) { - CustomData_bmesh_init_pool_ex(&bm->ldata, 0, BM_LOOP, __func__); + CustomData_bmesh_init_pool(&bm->ldata, 0, BM_LOOP); } if (bm->pdata.totlayer) { - CustomData_bmesh_init_pool_ex(&bm->pdata, 0, BM_FACE, __func__); + CustomData_bmesh_init_pool(&bm->pdata, 0, BM_FACE); } } @@ -202,14 +173,6 @@ BMesh *BM_mesh_create(const BMAllocTemplate *allocsize, const struct BMeshCreate return bm; } -static void customdata_pool_destroy(BMesh * /*bm*/, CustomData *cdata) -{ - if (cdata->pool) { - BLI_mempool_destroy(cdata->pool); - cdata->pool = nullptr; - } -} - void BM_mesh_data_free(BMesh *bm) { BMVert *v; @@ -220,30 +183,6 @@ void BM_mesh_data_free(BMesh *bm) BMIter iter; BMIter itersub; -#ifndef WITH_BM_ID_FREELIST - if (bm->idmap.idtree) { - range_tree_uint_free(bm->idmap.idtree); - } -#else - MEM_SAFE_FREE(bm->idmap.free_ids); - MEM_SAFE_FREE(bm->idmap.freelist); - bm->idmap.freelist = nullptr; - bm->idmap.free_ids = nullptr; -#endif - - MEM_SAFE_FREE(bm->idmap.map); - - if (bm->idmap.ghash) { - BLI_ghash_free(bm->idmap.ghash, nullptr, nullptr); - } - -#ifdef WITH_BM_ID_FREELIST - if (bm->idmap.free_idx_map) { - BLI_ghash_free(bm->idmap.free_idx_map, nullptr, nullptr); - bm->idmap.free_idx_map = nullptr; - } -#endif - const bool is_ldata_free = CustomData_bmesh_has_free(&bm->ldata); const bool is_pdata_free = CustomData_bmesh_has_free(&bm->pdata); @@ -274,16 +213,16 @@ void BM_mesh_data_free(BMesh *bm) /* Free custom data pools, This should probably go in CustomData_free? */ if (bm->vdata.totlayer) { - customdata_pool_destroy(bm, &bm->vdata); + BLI_mempool_destroy(bm->vdata.pool); } if (bm->edata.totlayer) { - customdata_pool_destroy(bm, &bm->edata); + BLI_mempool_destroy(bm->edata.pool); } if (bm->ldata.totlayer) { - customdata_pool_destroy(bm, &bm->ldata); + BLI_mempool_destroy(bm->ldata.pool); } if (bm->pdata.totlayer) { - customdata_pool_destroy(bm, &bm->pdata); + BLI_mempool_destroy(bm->pdata.pool); } /* free custom data */ @@ -829,11 +768,7 @@ int BM_mesh_elem_count(BMesh *bm, const char htype) } } -void BM_mesh_remap(BMesh *bm, - const uint *vert_idx, - const uint *edge_idx, - const uint *loop_idx, - const uint *face_idx) +void BM_mesh_remap(BMesh *bm, const uint *vert_idx, const uint *edge_idx, const uint *face_idx) { /* Mapping old to new pointers. */ GHash *vptr_map = nullptr, *eptr_map = nullptr, *fptr_map = nullptr; @@ -850,21 +785,6 @@ void BM_mesh_remap(BMesh *bm, BM_mesh_elem_table_ensure( bm, (vert_idx ? BM_VERT : 0) | (edge_idx ? BM_EDGE : 0) | (face_idx ? BM_FACE : 0)); - CustomData *cdatas[4] = {&bm->vdata, &bm->edata, &bm->ldata, &bm->pdata}; - -#define DO_SWAP(ci, cdata, v, vp) *(v) = *(vp); - -// NOT WORKING -/* unswaps customdata blocks*/ -#define DO_SWAP2(ci, cdata, v, vp) \ - void *cdold = (v)->head.data; \ - void *cdnew = (vp)->head.data; \ - *(v) = *(vp); \ - if (cdold) { \ - (v)->head.data = cdold; \ - memcpy(cdold, cdnew, bm->cdata.totsize); \ - } - /* Remap Verts */ if (vert_idx) { BMVert **verts_pool, *verts_copy, **vep; @@ -886,9 +806,7 @@ void BM_mesh_remap(BMesh *bm, nullptr; for (i = totvert, ve = verts_copy + totvert - 1, vep = verts_pool + totvert - 1; i--; ve--, vep--) { - *ve = **vep; - // printf("*vep: %p, verts_pool[%d]: %p\n", *vep, i, verts_pool[i]); if (cd_vert_pyptr != -1) { void **pyptr = static_cast(BM_ELEM_CD_GET_VOID_P(((BMElem *)ve), cd_vert_pyptr)); @@ -902,15 +820,12 @@ void BM_mesh_remap(BMesh *bm, vep = verts_pool + totvert - 1; /* old, org pointer */ for (i = totvert; i--; new_idx--, ve--, vep--) { BMVert *new_vep = verts_pool[*new_idx]; - - DO_SWAP(0, vdata, new_vep, ve); - - BLI_ghash_insert(vptr_map, *vep, new_vep); + *new_vep = *ve; #if 0 printf( "mapping vert from %d to %d (%p/%p to %p)\n", i, *new_idx, *vep, verts_pool[i], new_vep); #endif - + BLI_ghash_insert(vptr_map, *vep, new_vep); if (cd_vert_pyptr != -1) { void **pyptr = static_cast( BM_ELEM_CD_GET_VOID_P(((BMElem *)new_vep), cd_vert_pyptr)); @@ -926,77 +841,6 @@ void BM_mesh_remap(BMesh *bm, } } - GHash *lptr_map = nullptr; - - /* Remap Loops */ - if (loop_idx) { - BMLoop **ltable = (BMLoop **)MEM_malloc_arrayN(bm->totloop, sizeof(*ltable), "ltable"); - - BMLoop *ed; - BLI_mempool_iter liter; - BLI_mempool_iternew(bm->lpool, &liter); - BMLoop *l = (BMLoop *)BLI_mempool_iterstep(&liter); - - int i = 0; - for (; l; l = (BMLoop *)BLI_mempool_iterstep(&liter), i++) { - l->head.index = i; - ltable[i] = l; - } - - BMLoop **loops_pool, *loops_copy, **edl; - int totloop = bm->totloop; - const uint *new_idx; - /* Special case: Python uses custom data layers to hold PyObject references. - * These have to be kept in place, else the PyObjects we point to, won't point back to us. */ - const int cd_loop_pyptr = CustomData_get_offset(&bm->ldata, CD_BM_ELEM_PYPTR); - - /* Init the old-to-new vert pointers mapping */ - lptr_map = BLI_ghash_ptr_new_ex("BM_mesh_remap loop pointers mapping", bm->totloop); - - /* Make a copy of all vertices. */ - loops_pool = ltable; - loops_copy = (BMLoop *)MEM_mallocN(sizeof(BMLoop) * totloop, "BM_mesh_remap loops copy"); - - void **pyptrs = (cd_loop_pyptr != -1) ? - (void **)MEM_mallocN(sizeof(void *) * totloop, __func__) : - nullptr; - for (i = totloop, ed = loops_copy + totloop - 1, edl = loops_pool + totloop - 1; i--; - ed--, edl--) { - - *ed = **edl; - - if (cd_loop_pyptr != -1) { - void **pyptr = (void **)BM_ELEM_CD_GET_VOID_P(((BMElem *)ed), cd_loop_pyptr); - pyptrs[i] = *pyptr; - } - } - - /* Copy back verts to their new place, and update old2new pointers mapping. */ - new_idx = loop_idx + totloop - 1; - ed = loops_copy + totloop - 1; - edl = loops_pool + totloop - 1; /* old, org pointer */ - for (i = totloop; i--; new_idx--, ed--, edl--) { - BMLoop *new_edl = loops_pool[*new_idx]; - *new_edl = *ed; - - DO_SWAP(2, ldata, new_edl, ed); - - BLI_ghash_insert(lptr_map, *edl, new_edl); -#if 0 - printf( - "mapping loop from %d to %d (%p/%p to %p)\n", i, *new_idx, *edl, loops_pool[i], new_edl); -#endif - if (cd_loop_pyptr != -1) { - void **pyptr = (void **)BM_ELEM_CD_GET_VOID_P(((BMElem *)new_edl), cd_loop_pyptr); - *pyptr = pyptrs[*new_idx]; - } - } - - MEM_SAFE_FREE(ltable); - MEM_SAFE_FREE(loops_copy); - MEM_SAFE_FREE(pyptrs); - } - /* Remap Edges */ if (edge_idx) { BMEdge **edges_pool, *edges_copy, **edp; @@ -1031,13 +875,7 @@ void BM_mesh_remap(BMesh *bm, edp = edges_pool + totedge - 1; /* old, org pointer */ for (i = totedge; i--; new_idx--, ed--, edp--) { BMEdge *new_edp = edges_pool[*new_idx]; - - DO_SWAP(1, edata, new_edp, ed); - - if (new_edp->l && lptr_map) { - new_edp->l = (BMLoop *)BLI_ghash_lookup(lptr_map, (BMLoop *)new_edp->l); - } - + *new_edp = *ed; BLI_ghash_insert(eptr_map, *edp, new_edp); #if 0 printf( @@ -1094,24 +932,6 @@ void BM_mesh_remap(BMesh *bm, BMFace *new_fap = faces_pool[*new_idx]; *new_fap = *fa; BLI_ghash_insert(fptr_map, *fap, new_fap); - - DO_SWAP(3, pdata, new_fap, fa); - - if (lptr_map) { - new_fap->l_first = (BMLoop *)BLI_ghash_lookup(lptr_map, (void *)new_fap->l_first); - - BMLoop *l = new_fap->l_first; - - do { - l->next = (BMLoop *)BLI_ghash_lookup(lptr_map, (void *)l->next); - l->prev = (BMLoop *)BLI_ghash_lookup(lptr_map, (void *)l->prev); - l->radial_next = (BMLoop *)BLI_ghash_lookup(lptr_map, (void *)l->radial_next); - l->radial_prev = (BMLoop *)BLI_ghash_lookup(lptr_map, (void *)l->radial_prev); - - l = l->next; - } while (l != new_fap->l_first); - } - if (cd_poly_pyptr != -1) { void **pyptr = static_cast( BM_ELEM_CD_GET_VOID_P(((BMElem *)new_fap), cd_poly_pyptr)); @@ -1647,24 +1467,4 @@ void BM_mesh_vert_coords_apply_with_mat4(BMesh *bm, } } -static void on_vert_kill(BMesh * /*bm*/, BMVert * /*v*/, void * /*userdata*/) {} -static void on_edge_kill(BMesh * /*bm*/, BMEdge * /*e*/, void * /*userdata*/) {} -static void on_face_kill(BMesh * /*bm*/, BMFace * /*f*/, void * /*userdata*/) {} - -static void on_vert_create(BMesh * /*bm*/, BMVert * /*v*/, void * /*userdata*/) {} -static void on_edge_create(BMesh * /*bm*/, BMEdge * /*v*/, void * /*userdata*/) {} -static void on_face_create(BMesh * /*bm*/, BMFace * /*v*/, void * /*userdata*/) {} - -void BM_empty_tracer(BMTracer *tracer, void *userdata) -{ - tracer->userdata = userdata; - - tracer->on_vert_create = on_vert_create; - tracer->on_edge_create = on_edge_create; - tracer->on_face_create = on_face_create; - - tracer->on_vert_kill = on_vert_kill; - tracer->on_edge_kill = on_edge_kill; - tracer->on_face_kill = on_face_kill; -} /** \} */ diff --git a/source/blender/bmesh/intern/bmesh_mesh.h b/source/blender/bmesh/intern/bmesh_mesh.h index 58974b27a8d..2eca0e560a8 100644 --- a/source/blender/bmesh/intern/bmesh_mesh.h +++ b/source/blender/bmesh/intern/bmesh_mesh.h @@ -9,50 +9,19 @@ #include "BLI_compiler_compat.h" #include "bmesh_class.h" -typedef struct BMTracer { - void (*on_vert_kill)(BMesh *bm, BMVert *v, void *userdata); - void (*on_edge_kill)(BMesh *bm, BMEdge *e, void *userdata); - void (*on_face_kill)(BMesh *bm, BMFace *f, void *userdata); - - void (*on_vert_create)(BMesh *bm, BMVert *v, void *userdata); - void (*on_edge_create)(BMesh *bm, BMEdge *v, void *userdata); - void (*on_face_create)(BMesh *bm, BMFace *v, void *userdata); - - void *userdata; -} BMTracer; - -typedef enum { - MULTIRES_SPACE_TANGENT, // convert absolute to tangent - MULTIRES_SPACE_ABSOLUTE // convert tangent to absolute -} MultiResSpace; - #ifdef __cplusplus extern "C" { #endif struct BMAllocTemplate; -void BM_empty_tracer(BMTracer *tracer, void *userdata); - void BM_mesh_elem_toolflags_ensure(BMesh *bm); void BM_mesh_elem_toolflags_clear(BMesh *bm); struct BMeshCreateParams { - bool create_unique_ids : 1; - int id_elem_mask : 8; // which element types to make unique ids for - bool id_map : 1; // maintain an id to element lookup table bool use_toolflags : 1; - bool no_reuse_ids : 1; // do not reuse IDs; a GHash will be used internally instead of a lookup - // array - bool temporary_ids : 1; - bool copy_all_layers : 1; // used by BM_mesh_copy_ex }; -// used to temporary save/restore element IDs -// when changing out customdata -int bm_save_id(BMesh *bm, BMElem *elem); -void bm_restore_id(BMesh *bm, BMElem *elem, int id); - /** * \brief BMesh Make Mesh * @@ -190,7 +159,6 @@ int BM_mesh_elem_count(BMesh *bm, char htype); void BM_mesh_remap(BMesh *bm, const uint *vert_idx, const uint *edge_idx, - const uint *loop_idx, const uint *face_idx); /** @@ -242,14 +210,6 @@ void BM_mesh_vert_coords_apply_with_mat4(BMesh *bm, const float (*vert_coords)[3], const float mat[4][4]); -#define BM_ELEM_FROM_ID(bm, id) \ - ((bm->idmap.flag & BM_NO_REUSE_IDS) ? \ - BLI_ghash_lookup(bm->idmap.ghash, POINTER_FROM_UINT(id)) : \ - bm->idmap.map[id]) - -#define BM_ELEM_FROM_ID_SAFE(bm, id) \ - (((id) >= 0 && (id) < (bm)->idmap.maxid) ? (BM_ELEM_FROM_ID(bm, id)) : NULL) - bool BM_elem_is_free(BMElem *elem, int htype); #ifdef __cplusplus diff --git a/source/blender/bmesh/intern/bmesh_mesh_convert_threaded.cc b/source/blender/bmesh/intern/bmesh_mesh_convert_threaded.cc deleted file mode 100644 index 74cae2fd4a8..00000000000 --- a/source/blender/bmesh/intern/bmesh_mesh_convert_threaded.cc +++ /dev/null @@ -1,1374 +0,0 @@ -#if 0 -# include "MEM_guardedalloc.h" - -# include "BLI_compiler_attrs.h" -# include "BLI_compiler_compat.h" -# include "BLI_index_range.hh" -# include "BLI_map.hh" -# include "BLI_math_vec_types.hh" -# include "BLI_set.hh" -# include "BLI_task.h" -# include "BLI_task.hh" -# include "BLI_vector.hh" - -# include "DNA_key_types.h" -# include "DNA_mesh_types.h" -# include "DNA_meshdata_types.h" -# include "DNA_modifier_types.h" -# include "DNA_object_types.h" - -# include "BKE_customdata.h" -# include "BKE_mesh.h" -# include "BKE_mesh_runtime.h" -# include "BKE_multires.h" - -# include "BKE_key.h" -# include "BKE_main.h" - -# include "DEG_depsgraph_query.h" - -# include "atomic_ops.h" -# include "bmesh.h" -# include "intern/bmesh_private.h" /* For element checking. */ - -using blender::float2; -using blender::float3; -using blender::IndexRange; -using blender::Map; -using blender::Set; -using blender::Vector; -using namespace blender::threading; - -extern "C" void BM_mesh_bm_to_me_threaded( - Main *bmain, Object *ob, BMesh *bm, Mesh *me, const struct BMeshToMeshParams *params) -{ - BM_mesh_elem_index_ensure(bm, BM_VERT | BM_EDGE | BM_FACE); - BM_mesh_elem_table_ensure(bm, BM_VERT | BM_EDGE | BM_FACE); - - CustomData_reset(&me->vdata); - CustomData_reset(&me->edata); - CustomData_reset(&me->ldata); - CustomData_reset(&me->pdata); - - me->totvert = bm->totvert; - me->totedge = bm->totedge; - me->totloop = bm->totloop; - me->totpoly = bm->totface; - me->totface = 0; - - CustomData_merge(&bm->vdata, - &me->vdata, - params->cd_mask_extra.vmask | CD_MASK_MESH.vmask, - CD_CONSTRUCT, - bm->totvert); - CustomData_merge(&bm->edata, - &me->edata, - params->cd_mask_extra.emask | CD_MASK_MESH.emask, - CD_CONSTRUCT, - bm->totedge); - CustomData_merge(&bm->ldata, - &me->ldata, - params->cd_mask_extra.lmask | CD_MASK_MESH.lmask, - CD_CONSTRUCT, - bm->totloop); - CustomData_merge(&bm->pdata, - &me->pdata, - params->cd_mask_extra.pmask | CD_MASK_MESH.pmask, - CD_CONSTRUCT, - bm->totface); - - MVert *mvert = me->mvert = static_cast( - CustomData_add_layer(&me->vdata, CD_MVERT, CD_CONSTRUCT, nullptr, bm->totvert)); - MEdge *medge = static_cast( - CustomData_add_layer(&me->vdata, CD_MEDGE, CD_CONSTRUCT, nullptr, bm->totedge)); - MLoop *mloop = static_cast( - CustomData_add_layer(&me->vdata, CD_MLOOP, CD_CONSTRUCT, nullptr, bm->totloop)); - MPoly *mpoly = static_cast( - CustomData_add_layer(&me->vdata, CD_MPOLY, CD_CONSTRUCT, nullptr, bm->totface)); - float(*normals)[3] = BKE_mesh_vertex_normals_for_write(me); - - bool *hide_vert = static_cast( - CustomData_get_layer_named(&me->vdata, CD_PROP_BOOL, ".hide_vert")); - bool *hide_edge = static_cast( - CustomData_get_layer_named(&me->vdata, CD_PROP_BOOL, ".hide_edge")); - bool *hide_poly = static_cast( - CustomData_get_layer_named(&me->pdata, CD_PROP_BOOL, ".hide_poly")); - - parallel_for(IndexRange(bm->totvert), 512, [&](IndexRange range) { - for (int i : range) { - MVert *mv = mvert + i; - BMVert *v = bm->vtable[i]; - - copy_v3_v3(mv->co, v->co); - copy_v3_v3(normals[i], v->no); - - CustomData_from_bmesh_block(&bm->vdata, &me->vdata, v->head.data, i); - - if (hide_vert) { - hide_vert[i] = v->head.hflag & BM_ELEM_HIDDEN; - } - } - }); - - parallel_for(IndexRange(bm->totedge), 512, [&](IndexRange range) { - for (int i : range) { - MEdge *med = medge + i; - BMEdge *e = bm->etable[i]; - - med->v1 = e->v1->head.index; - med->v2 = e->v2->head.index; - - CustomData_from_bmesh_block(&bm->edata, &me->edata, e->head.data, i); - - if (hide_edge) { - hide_edge[i] = e->head.hflag & BM_ELEM_HIDDEN; - } - } - }); - - BMIter iter; - BMFace *f; - int loop_i = 0; - - BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { - BMLoop *l = f->l_first; - do { - l->head.index = loop_i++; - } while ((l = l->next) != f->l_first); - } - - int *material_index = static_cast( - CustomData_get_layer_named(&me->pdata, CD_PROP_INT32, "material_index")); - - parallel_for(IndexRange(bm->totface), 512, [&](IndexRange range) { - for (int i : range) { - MPoly *mp = mpoly + i; - BMFace *f = bm->ftable[i]; - - mp->loopstart = f->l_first->head.index; - mp->totloop = 0; - mp->flag = 0; - - if (material_index) { - material_index[i] = f->mat_nr; - } - - CustomData_from_bmesh_block(&bm->pdata, &me->pdata, f->head.data, i); - if (hide_poly) { - hide_poly[i] = f->head.hflag & BM_ELEM_HIDDEN; - } - - BMLoop *l = f->l_first; - do { - int l_i = l->head.index; - MLoop *ml = mloop + l_i; - - ml->v = l->v->head.index; - ml->e = l->e->head.index; - - CustomData_from_bmesh_block(&bm->ldata, &me->ldata, l->head.data, l_i); - mp->totloop++; - } while ((l = l->next) != f->l_first); - } - }); -} -#endif - -#if 0 -# include "DNA_key_types.h" -# include "DNA_mesh_types.h" -# include "DNA_meshdata_types.h" -# include "DNA_modifier_types.h" -# include "DNA_object_types.h" - -# include "MEM_guardedalloc.h" - -# include "BLI_alloca.h" -# include "BLI_compiler_attrs.h" -# include "BLI_listbase.h" -# include "BLI_math_vector.h" -# include "BLI_task.h" -# include "BLI_threads.h" - -# include "BKE_customdata.h" -# include "BKE_mesh.h" -# include "BKE_mesh_runtime.h" -# include "BKE_multires.h" - -# include "BKE_key.h" -# include "BKE_main.h" - -# include "DEG_depsgraph_query.h" - -# include "bmesh.h" -# include "intern/bmesh_private.h" /* For element checking. */ - -# include "BLI_task.h" - -# include "atomic_ops.h" - -# define ECHUNK 512 -# define VCHUNK 512 -# define FCHUNK 512 -# define LCHUNK 1024 - -typedef struct BMThreadData { - BMesh *bm; - Object *ob; - const Mesh *me; - - struct BMeshFromMeshParams *params; - - void **vdata, **edata, **ldata, **fdata; - int totdv, totde, totdl, totdf; - int vsize, esize, lsize, fsize; - - int vchunk, echunk, lchunk, fchunk; - - BMVert **verts; - BMEdge **edges; - BMLoop **loops; - BMFace **faces; - - float (**shape_key_table)[3]; - int tot_shape_keys; - - int cd_vert_bweight; - int cd_edge_bweight; - int cd_crease; - - int cdvsize, cdesize, cdlsize, cdfsize; - - // chunk sizes - int totcv, totce, totcl, totcf; -} BMThreadData; - -# define ELEM_NEXT(type, ptr, size) ((type *)(((char *)ptr) + size)) - -static void bm_vert_task(void *__restrict userdata, - const int n, - const TaskParallelTLS *__restrict tls) -{ - BMThreadData *data = userdata; - BMesh *bm = data->bm; - const Mesh *me = data->me; - - int starti = n * VCHUNK; - - int ilen = starti + VCHUNK > bm->totvert ? bm->totvert - starti : VCHUNK; - MVert *mv = me->mvert + starti; - BMVert *v = data->verts[n]; - char *cdblock = data->vdata ? (char *)data->vdata[n] : NULL; - - for (int i = 0; i < ilen; i++, mv++) { - if (cdblock) { - v->head.data = (void *)cdblock; - cdblock += data->cdvsize; - } - else { - v->head.data = NULL; - } - - v->head.htype = BM_VERT; - v->head.hflag = BM_vert_flag_from_mflag(mv->flag); - v->head.api_flag = 0; - - copy_v3_v3(v->co, mv->co); - // XXX normal_short_to_float_v3(v->no, mv->no); - - v->e = NULL; - v->head.index = i + starti; - v = ELEM_NEXT(BMVert, v, data->vsize); - } - - if (data->vdata) { - v = data->verts[n]; - for (int i = 0; i < ilen; i++) { - CustomData_to_bmesh_block(&me->vdata, &bm->vdata, i + starti, &v->head.data, true); - v = ELEM_NEXT(BMVert, v, data->vsize); - } - } -} - -static void bm_edge_task(void *__restrict userdata, - const int n, - const TaskParallelTLS *__restrict tls) -{ - BMThreadData *data = userdata; - BMesh *bm = data->bm; - const Mesh *me = data->me; - - int starti = n * ECHUNK; - - int ilen = starti + ECHUNK > bm->totedge ? bm->totedge - starti : ECHUNK; - MEdge *med = me->medge + starti; - BMEdge *e = data->edges[n]; - char *cdblock = data->edata ? (char *)data->edata[n] : NULL; - - for (int i = 0; i < ilen; i++, med++) { - if (cdblock) { - e->head.data = (void *)cdblock; - cdblock += data->cdesize; - } - else { - e->head.data = NULL; - } - - e->head.htype = BM_EDGE; - e->head.hflag = BM_edge_flag_from_mflag(med->flag); - e->head.api_flag = 0; - - e->v1 = &data->verts[med->v1 / VCHUNK][med->v1 % VCHUNK]; - e->v2 = &data->verts[med->v2 / VCHUNK][med->v2 % VCHUNK]; - - e->l = NULL; - e->v1_disk_link.next = e->v1_disk_link.prev = NULL; - e->v2_disk_link.next = e->v2_disk_link.prev = NULL; - - e = ELEM_NEXT(BMEdge, e, data->esize); - } - - if (data->edata) { - e = data->edges[n]; - for (int i = 0; i < ilen; i++) { - CustomData_to_bmesh_block(&me->edata, &bm->edata, i + starti, &e->head.data, true); - e = ELEM_NEXT(BMEdge, e, data->esize); - } - } -} - -static void bm_loop_task(void *__restrict userdata, - const int n, - const TaskParallelTLS *__restrict tls) -{ - BMThreadData *data = userdata; - BMesh *bm = data->bm; - const Mesh *me = data->me; - - int starti = n * LCHUNK; - - int ilen = starti + LCHUNK > bm->totloop ? bm->totloop - starti : LCHUNK; - MLoop *ml = me->mloop + starti; - BMLoop *l = data->loops[n]; - char *cdblock = data->ldata ? (char *)data->ldata[n] : NULL; - - for (int i = 0; i < ilen; i++, ml++) { - if (cdblock) { - l->head.data = (void *)cdblock; - cdblock += data->cdlsize; - } - else { - l->head.data = NULL; - } - - l->head.htype = BM_LOOP; - l->head.hflag = 0; - l->head.api_flag = 0; - - l->v = data->verts[ml->v / VCHUNK] + (ml->v % VCHUNK); - l->e = data->edges[ml->e / ECHUNK] + (ml->e % ECHUNK); - l->radial_next = l->radial_prev = l->next = l->prev = NULL; - l->f = NULL; - - l = ELEM_NEXT(BMLoop, l, data->lsize); - } - - if (data->ldata) { - l = data->loops[n]; - for (int i = 0; i < ilen; i++) { - CustomData_to_bmesh_block(&me->ldata, &bm->ldata, i + starti, &l->head.data, true); - l = ELEM_NEXT(BMLoop, l, data->lsize); - } - } -} - -static void bm_face_task(void *__restrict userdata, - const int n, - const TaskParallelTLS *__restrict tls) -{ - BMThreadData *data = userdata; - BMesh *bm = data->bm; - const Mesh *me = data->me; - - int starti = n * FCHUNK; - - int ilen = starti + FCHUNK > bm->totface ? bm->totface - starti : FCHUNK; - MPoly *mp = me->mpoly + starti; - BMFace *f = data->faces[n]; - char *cdblock = data->fdata ? (char *)data->fdata[n] : NULL; - - for (int i = 0; i < ilen; i++, mp++) { - if (cdblock) { - f->head.data = (void *)cdblock; - cdblock += data->cdfsize; - } - else { - f->head.data = NULL; - } - - f->head.htype = BM_FACE; - f->head.hflag = BM_face_flag_from_mflag(mp->flag); - f->head.api_flag = 0; - - f->len = mp->totloop; - f->mat_nr = mp->mat_nr; - zero_v3(f->no); - - int li = mp->loopstart; - BMLoop *lastl = NULL; - - for (int j = 0; j < mp->totloop; j++, li++) { - BMLoop *l = data->loops[li / LCHUNK] + (li % LCHUNK); - - l->f = f; - - if (j == 0) { - f->l_first = l; - } - else { - lastl->next = l; - l->prev = lastl; - } - - lastl = l; - } - - lastl->next = f->l_first; - f->l_first->prev = lastl; - - f = ELEM_NEXT(BMFace, f, data->fsize); - } - - if (data->fdata) { - f = data->faces[n]; - for (int i = 0; i < ilen; i++) { - CustomData_to_bmesh_block(&me->pdata, &bm->pdata, i + starti, &f->head.data, true); - f = ELEM_NEXT(BMFace, f, data->fsize); - } - } -} - -static void bm_mesh_cd_flag_apply(BMesh *bm, const char cd_flag) -{ - /* CustomData_bmesh_init_pool() must run first */ - BLI_assert(bm->vdata.totlayer == 0 || bm->vdata.pool != NULL); - BLI_assert(bm->edata.totlayer == 0 || bm->edata.pool != NULL); - BLI_assert(bm->pdata.totlayer == 0 || bm->pdata.pool != NULL); - - if (cd_flag & ME_CDFLAG_VERT_BWEIGHT) { - if (!CustomData_has_layer(&bm->vdata, CD_BWEIGHT)) { - CustomData_add_layer(&bm->vdata, CD_BWEIGHT, CD_SET_DEFAULT, NULL, 0); - } - } - else { - if (CustomData_has_layer(&bm->vdata, CD_BWEIGHT)) { - CustomData_free_layer_active(&bm->vdata, CD_BWEIGHT, 0); - } - } - - if (cd_flag & ME_CDFLAG_EDGE_BWEIGHT) { - if (!CustomData_has_layer(&bm->edata, CD_BWEIGHT)) { - CustomData_add_layer(&bm->edata, CD_BWEIGHT, CD_SET_DEFAULT, NULL, 0); - } - } - else { - if (CustomData_has_layer(&bm->edata, CD_BWEIGHT)) { - CustomData_free_layer_active(&bm->edata, CD_BWEIGHT, 0); - } - } - - if (cd_flag & ME_CDFLAG_EDGE_CREASE) { - if (!CustomData_has_layer(&bm->edata, CD_CREASE)) { - CustomData_add_layer(&bm->edata, CD_CREASE, CD_SET_DEFAULT, NULL, 0); - } - } - else { - if (CustomData_has_layer(&bm->edata, CD_CREASE)) { - CustomData_free_layer_active(&bm->edata, CD_CREASE, 0); - } - } -} - -BMesh *BM_mesh_bm_from_me_threaded(BMesh *bm, - Object *ob, - const Mesh *me, - const struct BMeshFromMeshParams *params) -{ - if (!bm) { - bm = MEM_callocN(sizeof(BMesh), "BM_mesh_bm_from_me_threaded bm"); - } - else { - BM_mesh_data_free(bm); - memset((void *)bm, 0, sizeof(*bm)); - } - const bool is_new = true; - - bm->totvert = me->totvert; - bm->totedge = me->totedge; - bm->totface = me->totpoly; - bm->totloop = me->totloop; - - bm->elem_index_dirty = bm->elem_table_dirty = BM_VERT | BM_EDGE | BM_LOOP | BM_FACE; - bm->spacearr_dirty = BM_SPACEARR_DIRTY_ALL; - - BMVert **verts; - BMEdge **edges; - BMLoop **loops; - BMFace **faces; - - void **vdata = NULL, **edata = NULL, **ldata = NULL, **fdata = NULL; - int totdv = 0, totde = 0, totdl = 0, totdf = 0; - - int totcv = 0, totce = 0, totcl = 0, totcf = 0; - - BMThreadData data = {0}; - - int vsize, esize, lsize, fsize; - - bm->vpool = BLI_mempool_create_for_tasks(sizeof(BMVert), - bm->totvert, - VCHUNK, - (void ***)&verts, - &totcv, - &vsize, - BLI_MEMPOOL_ALLOW_ITER); - bm->epool = BLI_mempool_create_for_tasks(sizeof(BMEdge), - bm->totedge, - ECHUNK, - (void ***)&edges, - &totce, - &esize, - BLI_MEMPOOL_ALLOW_ITER); - bm->lpool = BLI_mempool_create_for_tasks(sizeof(BMLoop), - bm->totloop, - LCHUNK, - (void ***)&loops, - &totcl, - &lsize, - BLI_MEMPOOL_ALLOW_ITER); - bm->fpool = BLI_mempool_create_for_tasks(sizeof(BMFace), - bm->totface, - FCHUNK, - (void ***)&faces, - &totcf, - &fsize, - BLI_MEMPOOL_ALLOW_ITER); - - data.verts = verts; - data.edges = edges; - data.loops = loops; - data.faces = faces; - - data.vsize = vsize; - data.esize = esize; - data.lsize = lsize; - data.fsize = fsize; - - data.totcv = totcv; - data.totce = totce; - data.totcl = totcl; - data.totcf = totcf; - - data.bm = bm; - data.me = me; - - // bm->vpool = BLI_mem - - KeyBlock *actkey, *block; - BMEdge *e; - BMFace *f; - float(*keyco)[3] = NULL; - int i; - CustomData_MeshMasks mask = CD_MASK_BMESH; - CustomData_MeshMasks_update(&mask, ¶ms->cd_mask_extra); - - MultiresModifierData *mmd = ob ? get_multires_modifier(NULL, ob, true) : NULL; - - if (mmd) { - bm->multires = *mmd; - bm->haveMultiResSettings = true; - bm->multiresSpace = MULTIRES_SPACE_TANGENT; - } - else { - bm->haveMultiResSettings = false; - } - - CustomData_copy(&me->vdata, &bm->vdata, mask.vmask, CD_SET_DEFAULT, CD_FLAG_NOCOPY); - CustomData_copy(&me->edata, &bm->edata, mask.emask, CD_SET_DEFAULT, CD_FLAG_NOCOPY); - CustomData_copy(&me->ldata, &bm->ldata, mask.lmask, CD_SET_DEFAULT, CD_FLAG_NOCOPY); - CustomData_copy(&me->pdata, &bm->pdata, mask.pmask, CD_SET_DEFAULT, CD_FLAG_NOCOPY); - - CustomData *cds[] = {&bm->vdata, &bm->edata, &bm->ldata, &bm->pdata}; - - // clear customdata->layers[X].data pointers - for (int i = 0; i < 4; i++) { - CustomData *cd = cds[i]; - for (int j = 0; j < cd->totlayer; j++) { - cd->layers[j].data = NULL; - } - } - bm_mesh_cd_flag_apply(bm, me->cd_flag); - - data.cd_vert_bweight = CustomData_get_offset(&bm->vdata, CD_BWEIGHT); - data.cd_edge_bweight = CustomData_get_offset(&bm->edata, CD_BWEIGHT); - data.cd_crease = CustomData_get_offset(&bm->edata, CD_CREASE); - - if (bm->vdata.totlayer) { - bm->vdata.pool = BLI_mempool_create_for_tasks( - bm->vdata.totsize, bm->totvert, VCHUNK, &vdata, &totdv, &data.cdvsize, BLI_MEMPOOL_NOP); - } - if (bm->edata.totlayer) { - bm->edata.pool = BLI_mempool_create_for_tasks( - bm->edata.totsize, bm->totedge, ECHUNK, &edata, &totde, &data.cdesize, BLI_MEMPOOL_NOP); - } - if (bm->ldata.totlayer) { - bm->ldata.pool = BLI_mempool_create_for_tasks( - bm->ldata.totsize, bm->totloop, LCHUNK, &ldata, &totdl, &data.cdlsize, BLI_MEMPOOL_NOP); - } - if (bm->pdata.totlayer) { - bm->pdata.pool = BLI_mempool_create_for_tasks( - bm->pdata.totsize, bm->totface, FCHUNK, &fdata, &totdf, &data.cdfsize, BLI_MEMPOOL_NOP); - } - - data.vdata = vdata; - data.edata = edata; - data.ldata = ldata; - data.fdata = fdata; - - data.totdv = totdv; - data.totde = totde; - data.totdl = totdl; - data.totdf = totdf; - - /* -------------------------------------------------------------------- */ - /* Shape Key */ - int tot_shape_keys = 0; - if (me->key != NULL && DEG_is_original_id(&me->id)) { - /* Evaluated meshes can be topologically inconsistent with their shape keys. - * Shape keys are also already integrated into the state of the evaluated - * mesh, so considering them here would kind of apply them twice. */ - tot_shape_keys = BLI_listbase_count(&me->key->block); - - /* Original meshes must never contain a shape-key custom-data layers. - * - * This may happen if and object's mesh data is accidentally - * set to the output from the modifier stack, causing it to be an "original" ID, - * even though the data isn't fully compatible (hence this assert). - * - * This results in: - * - The newly created #BMesh having twice the number of custom-data layers. - * - When converting the #BMesh back to a regular mesh, - * At least one of the extra shape-key blocks will be created in #Mesh.key - * depending on the value of #CustomDataLayer.uid. - * - * We could support mixing both kinds of data if there is a compelling use-case for it. - * At the moment it's simplest to assume all original meshes use the key-block and meshes - * that are evaluated (through the modifier stack for example) use custom-data layers. - */ - BLI_assert(!CustomData_has_layer(&me->vdata, CD_SHAPEKEY)); - } - if (is_new == false) { - tot_shape_keys = min_ii(tot_shape_keys, CustomData_number_of_layers(&bm->vdata, CD_SHAPEKEY)); - } - - float(**shape_key_table)[3] = tot_shape_keys ? - BLI_array_alloca(shape_key_table, tot_shape_keys) : - NULL; - - if ((params->active_shapekey != 0) && tot_shape_keys > 0) { - actkey = BLI_findlink(&me->key->block, params->active_shapekey - 1); - } - else { - actkey = NULL; - } - - if (is_new) { - if (tot_shape_keys || params->add_key_index) { - CustomData_add_layer(&bm->vdata, CD_SHAPE_KEYINDEX, CD_SET_DEFAULT, NULL, 0); - } - } - - if (tot_shape_keys) { - if (is_new) { - /* Check if we need to generate unique ids for the shape-keys. - * This also exists in the file reading code, but is here for a sanity check. */ - if (!me->key->uidgen) { - fprintf(stderr, - "%s had to generate shape key uid's in a situation we shouldn't need to! " - "(bmesh internal error)\n", - __func__); - - me->key->uidgen = 1; - for (block = me->key->block.first; block; block = block->next) { - block->uid = me->key->uidgen++; - } - } - } - - if (actkey && actkey->totelem == me->totvert) { - keyco = params->use_shapekey ? actkey->data : NULL; - if (is_new) { - bm->shapenr = params->active_shapekey; - } - } - - for (i = 0, block = me->key->block.first; i < tot_shape_keys; block = block->next, i++) { - if (is_new) { - CustomData_add_layer_named(&bm->vdata, CD_SHAPEKEY, CD_SET_DEFAULT, NULL, 0, block->name); - int j = CustomData_get_layer_index_n(&bm->vdata, CD_SHAPEKEY, i); - bm->vdata.layers[j].uid = block->uid; - } - shape_key_table[i] = (float(*)[3])block->data; - } - } - - data.tot_shape_keys = tot_shape_keys; - data.shape_key_table = shape_key_table; - - TaskParallelSettings settings; - - BLI_parallel_range_settings_defaults(&settings); - BLI_task_parallel_range(0, data.totcv, &data, bm_vert_task, &settings); - BLI_task_parallel_range(0, data.totce, &data, bm_edge_task, &settings); - BLI_task_parallel_range(0, data.totcl, &data, bm_loop_task, &settings); - BLI_task_parallel_range(0, data.totcf, &data, bm_face_task, &settings); - - BMIter iter; - - // link edges - BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) { - bmesh_disk_edge_append(e, e->v1); - bmesh_disk_edge_append(e, e->v2); - } - - // link radial lists - i = 0; - BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { - BMLoop *l = f->l_first; - - do { - bmesh_radial_loop_append(l->e, l); - - l = l->next; - } while (l != f->l_first); - - i++; - } - - printf("totface: %d\n", i); - - bm->elem_index_dirty = BM_VERT | BM_EDGE | BM_FACE; - bm->elem_table_dirty = BM_VERT | BM_EDGE | BM_FACE; - - return bm; -} - -static void bm_unmark_temp_cdlayers(BMesh *bm) -{ - CustomData_unmark_temporary_nocopy(&bm->vdata); - CustomData_unmark_temporary_nocopy(&bm->edata); - CustomData_unmark_temporary_nocopy(&bm->ldata); - CustomData_unmark_temporary_nocopy(&bm->pdata); -} - -static void bm_mark_temp_cdlayers(BMesh *bm) -{ - CustomData_mark_temporary_nocopy(&bm->vdata); - CustomData_mark_temporary_nocopy(&bm->edata); - CustomData_mark_temporary_nocopy(&bm->ldata); - CustomData_mark_temporary_nocopy(&bm->pdata); -} - -typedef struct BMToMeTask { - Mesh *me; - BMesh *bm; - Object *ob; - Main *bmain; - const struct BMeshToMeshParams *params; - struct CustomData_MeshMasks mask; - uint64_t extra2; -} BMToMeTask; - -static void me_vert_task(void *__restrict userdata) -{ - BMToMeTask *data = (BMToMeTask *)userdata; - Mesh *me = data->me; - BMesh *bm = data->bm; - - CustomData_free(&me->vdata, me->totvert); - me->totvert = bm->totvert; - - CustomData_copy( - &bm->vdata, &me->vdata, data->mask.vmask | data->extra2, CD_SET_DEFAULT, me->totvert); - - MVert *mvert = bm->totvert ? MEM_callocN(sizeof(MVert) * bm->totvert, "bm_to_me.vert") : NULL; - CustomData_add_layer(&me->vdata, CD_MVERT, CD_SET_DEFAULT, mvert, me->totvert); - - BMVert *v; - BMIter iter; - int i = 0; - - const int cd_vert_bweight_offset = CustomData_get_offset(&bm->vdata, CD_BWEIGHT); - - BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) { - copy_v3_v3(mvert->co, v->co); - // XXX normal_float_to_short_v3(mvert->no, v->no); - - mvert->flag = BM_vert_flag_to_mflag(v); - - /* Copy over custom-data. */ - CustomData_from_bmesh_block(&bm->vdata, &me->vdata, v->head.data, i); - - if (cd_vert_bweight_offset != -1) { - mvert->bweight = BM_ELEM_CD_GET_FLOAT_AS_UCHAR(v, cd_vert_bweight_offset); - } - - mvert++; - i++; - - BM_CHECK_ELEMENT(v); - } -} - -BLI_INLINE void bmesh_quick_edgedraw_flag(MEdge *med, BMEdge *e) -{ - /* This is a cheap way to set the edge draw, its not precise and will - * pick the first 2 faces an edge uses. - * The dot comparison is a little arbitrary, but set so that a 5 subd - * IcoSphere won't vanish but subd 6 will (as with pre-bmesh Blender). */ - - if (/* (med->flag & ME_EDGEDRAW) && */ /* Assume to be true. */ - (e->l && (e->l != e->l->radial_next)) && - (dot_v3v3(e->l->f->no, e->l->radial_next->f->no) > 0.9995f)) { - med->flag &= ~ME_EDGEDRAW; - } - else { - med->flag |= ME_EDGEDRAW; - } -} - -static void me_edge_task(void *__restrict userdata) -{ - BMToMeTask *data = (BMToMeTask *)userdata; - Mesh *me = data->me; - BMesh *bm = data->bm; - MEdge *med; - - CustomData_free(&me->edata, me->totedge); - me->totedge = bm->totedge; - - CustomData_copy( - &bm->edata, &me->edata, data->mask.emask | data->extra2, CD_SET_DEFAULT, me->totvert); - - MEdge *medge = bm->totedge ? MEM_callocN(sizeof(MEdge) * bm->totedge, "bm_to_me.edge") : NULL; - CustomData_add_layer(&me->edata, CD_MEDGE, CD_SET_DEFAULT, medge, me->totedge); - const int cd_edge_bweight_offset = CustomData_get_offset(&bm->edata, CD_BWEIGHT); - - BMEdge *e; - BMIter iter; - int i = 0; - - const int cd_edge_crease_offset = CustomData_get_offset(&bm->edata, CD_CREASE); - - med = medge; - i = 0; - BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) { - med->v1 = BM_elem_index_get(e->v1); - med->v2 = BM_elem_index_get(e->v2); - - med->flag = BM_edge_flag_to_mflag(e); - - /* Copy over custom-data. */ - CustomData_from_bmesh_block(&bm->edata, &me->edata, e->head.data, i); - - bmesh_quick_edgedraw_flag(med, e); - - if (cd_edge_crease_offset != -1) { - med->crease = BM_ELEM_CD_GET_FLOAT_AS_UCHAR(e, cd_edge_crease_offset); - } - if (cd_edge_bweight_offset != -1) { - med->bweight = BM_ELEM_CD_GET_FLOAT_AS_UCHAR(e, cd_edge_bweight_offset); - } - - i++; - med++; - BM_CHECK_ELEMENT(e); - } -} - -static void me_face_task(void *__restrict userdata) -{ - BMToMeTask *data = (BMToMeTask *)userdata; - Mesh *me = data->me; - BMesh *bm = data->bm; - MPoly *mpoly; - MLoop *mloop; - - // set up polys - CustomData_free(&me->pdata, me->totpoly); - me->totpoly = bm->totface; - - CustomData_copy( - &bm->pdata, &me->pdata, data->mask.pmask | data->extra2, CD_SET_DEFAULT, me->totpoly); - - mpoly = bm->totface ? MEM_callocN(sizeof(MPoly) * bm->totface, "bm_to_me.poly") : NULL; - CustomData_add_layer(&me->pdata, CD_MPOLY, CD_SET_DEFAULT, mpoly, me->totpoly); - - // set up loops - CustomData_free(&me->ldata, me->totloop); - me->totloop = bm->totloop; - - CustomData_copy( - &bm->ldata, &me->ldata, data->mask.lmask | data->extra2, CD_SET_DEFAULT, me->totloop); - - mloop = bm->totloop ? MEM_callocN(sizeof(MLoop) * bm->totloop, "bm_to_me.loop") : NULL; - CustomData_add_layer(&me->ldata, CD_MLOOP, CD_SET_DEFAULT, mloop, me->totloop); - - // convert - - BMIter iter; - BMFace *f; - - int i, j; - i = 0; - j = 0; - BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { - BMLoop *l_iter, *l_first; - mpoly->loopstart = j; - mpoly->totloop = f->len; - mpoly->mat_nr = f->mat_nr; - mpoly->flag = BM_face_flag_to_mflag(f); - - l_iter = l_first = BM_FACE_FIRST_LOOP(f); - do { - mloop->e = BM_elem_index_get(l_iter->e); - mloop->v = BM_elem_index_get(l_iter->v); - - /* Copy over custom-data. */ - CustomData_from_bmesh_block(&bm->ldata, &me->ldata, l_iter->head.data, j); - - j++; - mloop++; - BM_CHECK_ELEMENT(l_iter); - BM_CHECK_ELEMENT(l_iter->e); - BM_CHECK_ELEMENT(l_iter->v); - } while ((l_iter = l_iter->next) != l_first); - - if (f == bm->act_face) { - me->act_face = i; - } - - /* Copy over custom-data. */ - CustomData_from_bmesh_block(&bm->pdata, &me->pdata, f->head.data, i); - - i++; - mpoly++; - BM_CHECK_ELEMENT(f); - } -} - -BMVert **bm_to_mesh_vertex_map(BMesh *bm, int ototvert); -int bm_to_mesh_shape_layer_index_from_kb(BMesh *bm, KeyBlock *currkey); - -typedef struct Test { - BMToMeTask *data; - int n; -} Test; -static void *test(void *userdata) -{ - Test *test = (Test *)userdata; - switch (test->n) { - case 0: - me_vert_task(test->data); - break; - case 1: - me_edge_task(test->data); - break; - case 2: - me_face_task(test->data); - break; - } - - return NULL; -} - -void BM_mesh_bm_to_me_threaded( - Main *bmain, Object *ob, BMesh *bm, Mesh *me, const struct BMeshToMeshParams *params) -{ - BMVert *eve; - BMIter iter; - - MVert *oldverts = NULL, *mvert = NULL; - const int ototvert = me->totvert; - const int cd_shape_keyindex_offset = CustomData_get_offset(&bm->vdata, CD_SHAPE_KEYINDEX); - - int i, j; - - BM_mesh_elem_index_ensure(bm, BM_VERT | BM_EDGE | BM_FACE); - - if (me->key && (cd_shape_keyindex_offset != -1)) { - /* Keep the old verts in case we are working on* a key, which is done at the end. */ - - /* Use the array in-place instead of duplicating the array. */ -# if 0 - oldverts = MEM_dupallocN(me->mvert); -# else - oldverts = me->mvert; - me->mvert = NULL; - CustomData_update_typemap(&me->vdata); - CustomData_set_layer(&me->vdata, CD_MVERT, NULL); -# endif - } - - BMToMeTask taskdata = {.params = params, .bm = bm, .me = me, .ob = ob, .bmain = bmain}; - - if (params->copy_temp_cdlayers) { - bm_unmark_temp_cdlayers(bm); - } - - // ensure multires space is correct - if (bm->haveMultiResSettings && bm->multiresSpace != MULTIRES_SPACE_TANGENT) { - BM_enter_multires_space(ob, bm, MULTIRES_SPACE_TANGENT); - } - - CustomData_MeshMasks mask = CD_MASK_MESH; - CustomData_MeshMasks_update(&mask, ¶ms->cd_mask_extra); - eCustomDataMask extra2 = 0; - CustomData *srcdatas[] = {&bm->vdata, &bm->edata, &bm->ldata, &bm->pdata}; - int id_flags[4] = {-1, -1, -1, -1}; - - taskdata.mask = mask; - taskdata.extra2 = extra2; - - // copy id layers? temporarily clear cd_temporary and cd_flag_elem_nocopy flags - if (!params->ignore_mesh_id_layers) { - - for (int i = 0; i < 4; i++) { - int idx = CustomData_get_layer_index(srcdatas[i], CD_MESH_ID); - if (idx >= 0) { - id_flags[i] = srcdatas[i]->layers[idx].flag; - srcdatas[i]->layers[idx].flag &= ~(CD_FLAG_TEMPORARY | CD_FLAG_ELEM_NOCOPY); - } - } - } - - me->cd_flag = BM_mesh_cd_flag_from_bmesh(bm); - -# if 0 - struct TaskGraph *taskgraph = BLI_task_graph_create(); - struct TaskNode *node; - - node = BLI_task_graph_node_create(taskgraph, me_vert_task, &taskdata, NULL); - BLI_task_graph_node_push_work(node); - - node = BLI_task_graph_node_create(taskgraph, me_edge_task, &taskdata, NULL); - BLI_task_graph_node_push_work(node); - - node = BLI_task_graph_node_create(taskgraph, me_face_task, &taskdata, NULL); - BLI_task_graph_node_push_work(node); - - BLI_task_graph_work_and_wait(taskgraph); - BLI_task_graph_free(taskgraph); -# else - ListBase threadpool; - Test datas[3] = {{&taskdata, 0}, {&taskdata, 1}, {&taskdata, 2}}; - - BLI_threadpool_init(&threadpool, test, 3); - BLI_threadpool_insert(&threadpool, &datas[0]); - BLI_threadpool_insert(&threadpool, &datas[1]); - BLI_threadpool_insert(&threadpool, &datas[2]); - BLI_threadpool_end(&threadpool); - -// BLI_threadpool_ -# endif - // undo changes to source bmesh's id layers' flags - if (!params->ignore_mesh_id_layers) { - for (int i = 0; i < 4; i++) { - int idx = CustomData_get_layer_index(srcdatas[i], CD_MESH_ID); - - if (id_flags[i] >= 0 && idx >= 0) { - srcdatas[i]->layers[idx].flag = id_flags[i]; - } - } - } - - if (me->fdata.layers) { - CustomData_free(&me->fdata, me->totface); - } - - CustomData_reset(&me->fdata); - - /* Will be overwritten with a valid value if 'dotess' is set, otherwise we - * end up with 'me->totface' and me->mface == NULL which can crash T28625. */ - me->totface = 0; - me->act_face = -1; - - BKE_mesh_update_customdata_pointers(me, 0); - - /* Patch hook indices and vertex parents. */ - if (params->calc_object_remap && (ototvert > 0)) { - BLI_assert(bmain != NULL); - Object *ob; - ModifierData *md; - BMVert **vertMap = NULL; - - for (ob = bmain->objects.first; ob; ob = ob->id.next) { - if ((ob->parent) && (ob->parent->data == me) && ELEM(ob->partype, PARVERT1, PARVERT3)) { - - if (vertMap == NULL) { - vertMap = bm_to_mesh_vertex_map(bm, ototvert); - } - - if (ob->par1 < ototvert) { - eve = vertMap[ob->par1]; - if (eve) { - ob->par1 = BM_elem_index_get(eve); - } - } - if (ob->par2 < ototvert) { - eve = vertMap[ob->par2]; - if (eve) { - ob->par2 = BM_elem_index_get(eve); - } - } - if (ob->par3 < ototvert) { - eve = vertMap[ob->par3]; - if (eve) { - ob->par3 = BM_elem_index_get(eve); - } - } - } - if (ob->data == me) { - for (md = ob->modifiers.first; md; md = md->next) { - if (md->type == eModifierType_Hook) { - HookModifierData *hmd = (HookModifierData *)md; - - if (vertMap == NULL) { - vertMap = bm_to_mesh_vertex_map(bm, ototvert); - } - - for (i = j = 0; i < hmd->indexar_num; i++) { - if (hmd->indexar[i] < ototvert) { - eve = vertMap[hmd->indexar[i]]; - - if (eve) { - hmd->indexar[j++] = BM_elem_index_get(eve); - } - } - else { - j++; - } - } - - hmd->indexar_num = j; - } - } - } - } - - if (vertMap) { - MEM_freeN(vertMap); - } - } - - /* This is called again, 'dotess' arg is used there. */ - BKE_mesh_update_customdata_pointers(me, false); - - { - BMEditSelection *selected; - me->totselect = BLI_listbase_count(&(bm->selected)); - - MEM_SAFE_FREE(me->mselect); - if (me->totselect != 0) { - me->mselect = MEM_mallocN(sizeof(MSelect) * me->totselect, "Mesh selection history"); - } - - for (i = 0, selected = bm->selected.first; selected; i++, selected = selected->next) { - if (selected->htype == BM_VERT) { - me->mselect[i].type = ME_VSEL; - } - else if (selected->htype == BM_EDGE) { - me->mselect[i].type = ME_ESEL; - } - else if (selected->htype == BM_FACE) { - me->mselect[i].type = ME_FSEL; - } - - me->mselect[i].index = BM_elem_index_get(selected->ele); - } - } - - /* See comment below, this logic is in twice. */ - - if (me->key) { - KeyBlock *currkey; - KeyBlock *actkey = BLI_findlink(&me->key->block, bm->shapenr - 1); - - float(*ofs)[3] = NULL; - - /* Go through and find any shape-key custom-data layers - * that might not have corresponding KeyBlocks, and add them if necessary. */ - for (i = 0; i < bm->vdata.totlayer; i++) { - if (bm->vdata.layers[i].type != CD_SHAPEKEY) { - continue; - } - - for (currkey = me->key->block.first; currkey; currkey = currkey->next) { - if (currkey->uid == bm->vdata.layers[i].uid) { - break; - } - } - - if (!currkey) { - currkey = BKE_keyblock_add(me->key, bm->vdata.layers[i].name); - currkey->uid = bm->vdata.layers[i].uid; - } - } - - /* Editing the base key should update others. */ - if (/* Only need offsets for relative shape keys. */ - (me->key->type == KEY_RELATIVE) && - - /* Unlikely, but the active key may not be valid if the - * BMesh and the mesh are out of sync. */ - (actkey != NULL) && - - /* Not used here, but 'oldverts' is used later for applying 'ofs'. */ - (oldverts != NULL) && - - /* Needed for referencing oldverts. */ - (cd_shape_keyindex_offset != -1)) { - - const bool act_is_basis = BKE_keyblock_is_basis(me->key, bm->shapenr - 1); - - /* Active key is a base. */ - if (act_is_basis) { - const float(*fp)[3] = actkey->data; - - ofs = MEM_callocN(sizeof(float[3]) * bm->totvert, "currkey->data"); - mvert = me->mvert; - BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, i) { - const int keyi = BM_ELEM_CD_GET_INT(eve, cd_shape_keyindex_offset); - - /* Could use 'eve->co' or 'mvert->co', they're the same at this point. */ - if (keyi != ORIGINDEX_NONE && keyi < actkey->totelem) { - sub_v3_v3v3(ofs[i], mvert->co, fp[keyi]); - } - else { - /* If there are new vertices in the mesh, we can't propagate the offset - * because it will only work for the existing vertices and not the new - * ones, creating a mess when doing e.g. subdivide + translate. */ - MEM_freeN(ofs); - ofs = NULL; - break; - } - - mvert++; - } - } - } - - for (currkey = me->key->block.first; currkey; currkey = currkey->next) { - int keyi; - const float(*ofs_pt)[3] = ofs; - float *newkey, (*oldkey)[3], *fp; - - const int currkey_uuid = bm_to_mesh_shape_layer_index_from_kb(bm, currkey); - const int cd_shape_offset = (currkey_uuid == -1) ? -1 : - CustomData_get_n_offset(&bm->vdata, - CD_SHAPEKEY, - currkey_uuid); - const bool apply_offset = (cd_shape_offset != -1) && (ofs != NULL) && (currkey != actkey) && - (bm->shapenr - 1 == currkey->relative); - - fp = newkey = MEM_callocN(me->key->elemsize * bm->totvert, "currkey->data"); - oldkey = currkey->data; - - mvert = me->mvert; - BM_ITER_MESH (eve, &iter, bm, BM_VERTS_OF_MESH) { - - if (currkey == actkey) { - copy_v3_v3(fp, eve->co); - - if (actkey != me->key->refkey) { /* Important see bug T30771. */ - if (cd_shape_keyindex_offset != -1) { - if (oldverts) { - keyi = BM_ELEM_CD_GET_INT(eve, cd_shape_keyindex_offset); - if (keyi != ORIGINDEX_NONE && keyi < currkey->totelem) { /* Valid old vertex. */ - copy_v3_v3(mvert->co, oldverts[keyi].co); - } - } - } - } - } - else if (cd_shape_offset != -1) { - /* In most cases this runs. */ - copy_v3_v3(fp, BM_ELEM_CD_GET_VOID_P(eve, cd_shape_offset)); - } - else if ((oldkey != NULL) && (cd_shape_keyindex_offset != -1) && - ((keyi = BM_ELEM_CD_GET_INT(eve, cd_shape_keyindex_offset)) != ORIGINDEX_NONE) && - (keyi < currkey->totelem)) { - /* Old method of reconstructing keys via vertices original key indices, - * currently used if the new method above fails - * (which is theoretically possible in certain cases of undo). */ - copy_v3_v3(fp, oldkey[keyi]); - } - else { - /* Fail! fill in with dummy value. */ - copy_v3_v3(fp, mvert->co); - } - - /* Propagate edited basis offsets to other shapes. */ - if (apply_offset) { - add_v3_v3(fp, *ofs_pt++); - /* Apply back new coordinates shape-keys that have offset into BMesh. - * Otherwise, in case we call again #BM_mesh_bm_to_me on same BMesh, - * we'll apply diff from previous call to #BM_mesh_bm_to_me, - * to shape-key values from *original creation of the BMesh*. See T50524. */ - copy_v3_v3(BM_ELEM_CD_GET_VOID_P(eve, cd_shape_offset), fp); - } - - fp += 3; - mvert++; - } - - currkey->totelem = bm->totvert; - if (currkey->data) { - MEM_freeN(currkey->data); - } - currkey->data = newkey; - } - - if (ofs) { - MEM_freeN(ofs); - } - } - - /* Run this even when shape keys aren't used since it may be used for hooks or vertex parents. */ - if (params->update_shapekey_indices) { - /* We have written a new shape key, if this mesh is _not_ going to be freed, - * update the shape key indices to match the newly updated. */ - if (cd_shape_keyindex_offset != -1) { - BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, i) { - BM_ELEM_CD_SET_INT(eve, cd_shape_keyindex_offset, i); - } - } - } - - me->cd_flag = BM_mesh_cd_flag_from_bmesh(bm); - - if (oldverts != NULL) { - MEM_freeN(oldverts); - } - - /* Topology could be changed, ensure #CD_MDISPS are ok. */ - multires_topology_changed(me); - - /* To be removed as soon as COW is enabled by default. */ - BKE_mesh_runtime_clear_geometry(me); - - if (params->copy_temp_cdlayers) { - bm_mark_temp_cdlayers(bm); - } -} -#endif diff --git a/source/blender/bmesh/intern/bmesh_mods.h b/source/blender/bmesh/intern/bmesh_mods.h index 19547c0a766..38e8a585cc9 100644 --- a/source/blender/bmesh/intern/bmesh_mods.h +++ b/source/blender/bmesh/intern/bmesh_mods.h @@ -2,8 +2,6 @@ #pragma once -struct BMTracer; - /** \file * \ingroup bmesh */ diff --git a/source/blender/bmesh/intern/bmesh_opdefines.c b/source/blender/bmesh/intern/bmesh_opdefines.c index 601c6087cc1..0d44d35b17e 100644 --- a/source/blender/bmesh/intern/bmesh_opdefines.c +++ b/source/blender/bmesh/intern/bmesh_opdefines.c @@ -1061,7 +1061,6 @@ static BMOpDefine bmo_extrude_face_region_def = { }, /* slots_out */ {{"geom.out", BMO_OP_SLOT_ELEMENT_BUF, {BM_VERT | BM_EDGE | BM_FACE}}, - {"side_geom.out", BMO_OP_SLOT_ELEMENT_BUF, {BM_VERT | BM_EDGE | BM_FACE}}, {{'\0'}}, }, bmo_extrude_face_region_exec, @@ -1966,7 +1965,7 @@ static BMOpDefine bmo_offset_edgeloops_def = { }, bmo_offset_edgeloops_exec, (BMO_OPTYPE_FLAG_NORMALS_CALC | - BMO_OPTYPE_FLAG_SELECT_FLUSH | BMO_OPTYPE_FLAG_UNTAN_MULTIRES), + BMO_OPTYPE_FLAG_SELECT_FLUSH), }; /* diff --git a/source/blender/bmesh/intern/bmesh_operator_api_inline.h b/source/blender/bmesh/intern/bmesh_operator_api_inline.h index ceb821186ac..3cb4923eb78 100644 --- a/source/blender/bmesh/intern/bmesh_operator_api_inline.h +++ b/source/blender/bmesh/intern/bmesh_operator_api_inline.h @@ -16,7 +16,6 @@ ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1, 2) BLI_INLINE short _bmo_elem_flag_test(BMesh *bm, const BMFlagLayer *oflags, const short oflag) { BLI_assert(bm->use_toolflags); - return oflags[bm->toolflag_index].f & oflag; } diff --git a/source/blender/bmesh/intern/bmesh_polygon.c b/source/blender/bmesh/intern/bmesh_polygon.c index 0c630098b20..77fbfe9a335 100644 --- a/source/blender/bmesh/intern/bmesh_polygon.c +++ b/source/blender/bmesh/intern/bmesh_polygon.c @@ -14,7 +14,6 @@ #include "MEM_guardedalloc.h" #include "BLI_alloca.h" -#include "BLI_compiler_attrs.h" #include "BLI_heap.h" #include "BLI_linklist.h" #include "BLI_math.h" @@ -1282,10 +1281,18 @@ void BM_face_splits_check_optimal(BMFace *f, BMLoop *(*loops)[2], int len) } } -/** - * faster alternative to: - * BM_iter_as_array(bm, BM_VERTS_OF_FACE, f, (void **)v, 4); - */ +void BM_face_as_array_vert_tri(BMFace *f, BMVert *r_verts[3]) +{ + BMLoop *l = BM_FACE_FIRST_LOOP(f); + + BLI_assert(f->len == 3); + + r_verts[0] = l->v; + l = l->next; + r_verts[1] = l->v; + l = l->next; + r_verts[2] = l->v; +} void BM_face_as_array_vert_quad(BMFace *f, BMVert *r_verts[4]) { @@ -1302,6 +1309,19 @@ void BM_face_as_array_vert_quad(BMFace *f, BMVert *r_verts[4]) r_verts[3] = l->v; } +void BM_face_as_array_loop_tri(BMFace *f, BMLoop *r_loops[3]) +{ + BMLoop *l = BM_FACE_FIRST_LOOP(f); + + BLI_assert(f->len == 3); + + r_loops[0] = l; + l = l->next; + r_loops[1] = l; + l = l->next; + r_loops[2] = l; +} + void BM_face_as_array_loop_quad(BMFace *f, BMLoop *r_loops[4]) { BMLoop *l = BM_FACE_FIRST_LOOP(f); diff --git a/source/blender/bmesh/intern/bmesh_polygon.h b/source/blender/bmesh/intern/bmesh_polygon.h index 8ca878e69bc..5ca7c3bafaf 100644 --- a/source/blender/bmesh/intern/bmesh_polygon.h +++ b/source/blender/bmesh/intern/bmesh_polygon.h @@ -9,7 +9,6 @@ struct Heap; #include "BLI_compiler_attrs.h" -#include "BLI_compiler_compat.h" #ifdef __cplusplus extern "C" { @@ -249,18 +248,12 @@ void BM_face_splits_check_optimal(BMFace *f, BMLoop *(*loops)[2], int len) ATTR_ * faster alternative to: * BM_iter_as_array(bm, BM_VERTS_OF_FACE, f, (void **)v, 3); */ -BLI_INLINE void BM_face_as_array_vert_tri(BMFace *f, BMVert *r_verts[3]) -{ - BMLoop *l = BM_FACE_FIRST_LOOP(f); - - BLI_assert(f->len == 3); - - r_verts[0] = l->v; - l = l->next; - r_verts[1] = l->v; - l = l->next; - r_verts[2] = l->v; -} +void BM_face_as_array_vert_tri(BMFace *f, BMVert *r_verts[3]) ATTR_NONNULL(); +/** + * faster alternative to: + * BM_iter_as_array(bm, BM_VERTS_OF_FACE, f, (void **)v, 4); + */ +void BM_face_as_array_vert_quad(BMFace *f, BMVert *r_verts[4]) ATTR_NONNULL(); /** * Small utility functions for fast access @@ -268,24 +261,7 @@ BLI_INLINE void BM_face_as_array_vert_tri(BMFace *f, BMVert *r_verts[3]) * faster alternative to: * BM_iter_as_array(bm, BM_LOOPS_OF_FACE, f, (void **)l, 3); */ -BLI_INLINE void BM_face_as_array_loop_tri(BMFace *f, BMLoop *r_loops[3]) -{ - BMLoop *l = BM_FACE_FIRST_LOOP(f); - - BLI_assert(f->len == 3); - - r_loops[0] = l; - l = l->next; - r_loops[1] = l; - l = l->next; - r_loops[2] = l; -} - -/** - * faster alternative to: - * BM_iter_as_array(bm, BM_VERTS_OF_FACE, f, (void **)v, 4); - */ -void BM_face_as_array_vert_quad(BMFace *f, BMVert *r_verts[4]) ATTR_NONNULL(); +void BM_face_as_array_loop_tri(BMFace *f, BMLoop *r_loops[3]) ATTR_NONNULL(); /** * faster alternative to: * BM_iter_as_array(bm, BM_LOOPS_OF_FACE, f, (void **)l, 4); @@ -311,8 +287,6 @@ void BM_vert_tri_calc_tangent_edge(BMVert *verts[3], float r_tangent[3]); */ void BM_vert_tri_calc_tangent_edge_pair(BMVert *verts[3], float r_tangent[3]); -void BM_face_as_array_vert_quad(BMFace *f, BMVert *r_verts[4]); - #ifdef __cplusplus } #endif diff --git a/source/blender/bmesh/intern/bmesh_structure.h b/source/blender/bmesh/intern/bmesh_structure.h index 16cc0eb394a..0af95d2b981 100644 --- a/source/blender/bmesh/intern/bmesh_structure.h +++ b/source/blender/bmesh/intern/bmesh_structure.h @@ -134,6 +134,4 @@ BMEdge *bmesh_disk_edge_exists(const BMVert *v1, const BMVert *v2) ATTR_WARN_UNU ATTR_NONNULL(); bool bmesh_disk_validate(int len, BMEdge *e, BMVert *v) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); -#define BM_DEFAULT_IDMAP_SIZE (1 << 12) - #include "intern/bmesh_structure_inline.h" diff --git a/source/blender/bmesh/operators/bmo_dupe.c b/source/blender/bmesh/operators/bmo_dupe.c index f51702ba385..d666d0cc2f5 100644 --- a/source/blender/bmesh/operators/bmo_dupe.c +++ b/source/blender/bmesh/operators/bmo_dupe.c @@ -47,11 +47,8 @@ static BMVert *bmo_vert_copy(BMOperator *op, /* Copy attributes */ BM_elem_attrs_copy(bm_src, bm_dst, v_src, v_dst); - // short **flags = BM_ELEM_CD_GET_VOID_P( - // v_dst, bm_dst->vdata.layers[bm_dst->vdata.typemap[CD_TOOLFLAGS]].offset); - bm_elem_check_toolflags(bm_dst, (BMElem *)v_dst); - // printf("%p\n", flags); + /* Mark the vert for output */ BMO_vert_flag_enable(bm_dst, v_dst, DUPE_NEW); diff --git a/source/blender/bmesh/operators/bmo_extrude.c b/source/blender/bmesh/operators/bmo_extrude.c index 5972cc6ee85..87f1c682cbc 100644 --- a/source/blender/bmesh/operators/bmo_extrude.c +++ b/source/blender/bmesh/operators/bmo_extrude.c @@ -21,7 +21,12 @@ #define USE_EDGE_REGION_FLAGS -enum { EXT_INPUT = 1, EXT_KEEP = 2, EXT_DEL = 4, EXT_TAG = 8, EXT_ALT = 16 }; +enum { + EXT_INPUT = 1, + EXT_KEEP = 2, + EXT_DEL = 4, + EXT_TAG = 8, +}; #define VERT_MARK 1 #define EDGE_MARK 1 @@ -314,17 +319,13 @@ void bmo_extrude_face_region_exec(BMesh *bm, BMOperator *op) BMFace *f; bool found, delorig = false; BMOpSlot *slot_facemap_out; - BMOpSlot *slot_sidemap_out; BMOpSlot *slot_edges_exclude; - const bool use_normal_flip = BMO_slot_bool_get(op->slots_in, "use_normal_flip"); const bool use_normal_from_adjacent = BMO_slot_bool_get(op->slots_in, "use_normal_from_adjacent"); const bool use_dissolve_ortho_edges = BMO_slot_bool_get(op->slots_in, "use_dissolve_ortho_edges"); - bool side_tag = EXT_ALT; - /* initialize our sub-operators */ BMO_op_initf(bm, &dupeop, @@ -539,37 +540,29 @@ void bmo_extrude_face_region_exec(BMesh *bm, BMOperator *op) char e_hflag[2]; bool e_hflag_ok = bm_extrude_region_edge_flag(f_verts[2], e_hflag); f_edges[1] = BM_edge_create(bm, f_verts[1], f_verts[2], NULL, BM_CREATE_NOP); - if (e_hflag_ok) { BM_elem_flag_enable(f_edges[1], e_hflag[0]); BM_elem_flag_disable(f_edges[1], e_hflag[1]); } } - BMO_elem_flag_set(bm, f_edges[1], side_tag, true); f_edges[3] = BM_edge_exists(f_verts[3], f_verts[0]); - if (f_edges[3] == NULL) { char e_hflag[2]; bool e_hflag_ok = bm_extrude_region_edge_flag(f_verts[3], e_hflag); f_edges[3] = BM_edge_create(bm, f_verts[3], f_verts[0], NULL, BM_CREATE_NOP); - if (e_hflag_ok) { BM_elem_flag_enable(f_edges[3], e_hflag[0]); BM_elem_flag_disable(f_edges[3], e_hflag[1]); } } - BMO_elem_flag_set(bm, f_edges[3], side_tag, true); f = BM_face_create(bm, f_verts, f_edges, 4, NULL, BM_CREATE_NOP); - #else f = BM_face_create_verts(bm, f_verts, 4, NULL, BM_CREATE_NOP, true); #endif bm_extrude_copy_face_loop_attributes(bm, f); - BMO_elem_flag_set(bm, (BMElem *)f, side_tag, true); - if (join_face) { BMVert *v1 = e->v1; BMVert *v2 = e->v2; @@ -601,7 +594,7 @@ void bmo_extrude_face_region_exec(BMesh *bm, BMOperator *op) } } - BMO_elem_flag_set(bm, BM_edge_create(bm, v, v2, NULL, BM_CREATE_NO_DOUBLE), side_tag, true); + BM_edge_create(bm, v, v2, NULL, BM_CREATE_NO_DOUBLE); } if (dissolve_verts) { @@ -612,8 +605,7 @@ void bmo_extrude_face_region_exec(BMesh *bm, BMOperator *op) BMEdge *e_other = BM_DISK_EDGE_NEXT(e, v); if ((e_other == e) || (BM_DISK_EDGE_NEXT(e_other, v) == e)) { /* Loose edge or BMVert is edge pair. */ - BM_edge_collapse( - bm, BMO_elem_flag_test(bm, e, EXT_TAG) ? e : e_other, v, true, true, false, false); + BM_edge_collapse(bm, BMO_elem_flag_test(bm, e, EXT_TAG) ? e : e_other, v, true, true, true, true); } else { BLI_assert(!BM_vert_is_edge_pair(v)); @@ -622,9 +614,6 @@ void bmo_extrude_face_region_exec(BMesh *bm, BMOperator *op) MEM_freeN(dissolve_verts); } - BMO_slot_buffer_from_enabled_flag( - bm, op, op->slots_out, "side_geom.out", BM_FACE | BM_EDGE, side_tag); - /* cleanup */ if (delorig) { BMO_op_finish(bm, &delop); diff --git a/source/blender/bmesh/operators/bmo_inset.c b/source/blender/bmesh/operators/bmo_inset.c index 50c62edf0bb..26156ff8867 100644 --- a/source/blender/bmesh/operators/bmo_inset.c +++ b/source/blender/bmesh/operators/bmo_inset.c @@ -1245,7 +1245,6 @@ void bmo_inset_region_exec(BMesh *bm, BMOperator *op) InterpFace *iface = iface_array[BM_elem_index_get(es->l->f)]; const int i_a = BM_elem_index_get(l_a_other); const int i_b = BM_elem_index_get(l_b_other); - CustomData_bmesh_free_block_data(&bm->ldata, l_b->head.data); CustomData_bmesh_free_block_data(&bm->ldata, l_a->head.data); CustomData_bmesh_copy_data(&bm->ldata, &bm->ldata, iface->blocks_l[i_a], &l_b->head.data); diff --git a/source/blender/bmesh/operators/bmo_subdivide.c b/source/blender/bmesh/operators/bmo_subdivide.c index 08056dc6b6f..f2af5d19db4 100644 --- a/source/blender/bmesh/operators/bmo_subdivide.c +++ b/source/blender/bmesh/operators/bmo_subdivide.c @@ -848,11 +848,6 @@ static void tri_3edge_subdivide(BMesh *bm, for (j = 0; j < i; j++) { e = connect_smallest_face(bm, lines[i][j], lines[i + 1][j + 1], &f_new); - if (!e) { - printf("%s: subdivide error;\n", __func__); - continue; - } - BMO_edge_flag_enable(bm, e, ELE_INNER); BMO_face_flag_enable(bm, f_new, ELE_INNER); diff --git a/source/blender/bmesh/tests/bmesh_core_test.cc b/source/blender/bmesh/tests/bmesh_core_test.cc index 9faa1448221..a0f6ea2706b 100644 --- a/source/blender/bmesh/tests/bmesh_core_test.cc +++ b/source/blender/bmesh/tests/bmesh_core_test.cc @@ -12,10 +12,8 @@ TEST(bmesh_core, BMVertCreate) BMVert *bv1, *bv2, *bv3; const float co1[3] = {1.0f, 2.0f, 0.0f}; - BMeshCreateParams bmesh_create_params = {0}; - + BMeshCreateParams bmesh_create_params{}; bmesh_create_params.use_toolflags = true; - bm = BM_mesh_create(&bm_mesh_allocsize_default, &bmesh_create_params); EXPECT_EQ(bm->totvert, 0); /* make a custom layer so we can see if it is copied properly */ diff --git a/source/blender/editors/mesh/editmesh_mask_extract.cc b/source/blender/editors/mesh/editmesh_mask_extract.cc index 8f3b7f579c3..1b5837dce65 100644 --- a/source/blender/editors/mesh/editmesh_mask_extract.cc +++ b/source/blender/editors/mesh/editmesh_mask_extract.cc @@ -607,8 +607,7 @@ static int paint_mask_slice_exec(bContext *C, wmOperator *op) const int next_face_set_id = SCULPT_face_set_next_available_get(ss); - const int updateflag = SCULPTFLAG_NEED_VALENCE | SCULPTFLAG_NEED_TRIANGULATE | - SCULPTFLAG_NEED_DISK_SORT; + const int updateflag = SCULPTFLAG_NEED_VALENCE | SCULPTFLAG_NEED_TRIANGULATE; BM_ITER_MESH (v, &iter, ss->bm, BM_VERTS_OF_MESH) { if (cd_boundary_flag != -1) { diff --git a/source/blender/editors/mesh/editmesh_tools.cc b/source/blender/editors/mesh/editmesh_tools.cc index 2d37e6c4eba..a3d774d5016 100644 --- a/source/blender/editors/mesh/editmesh_tools.cc +++ b/source/blender/editors/mesh/editmesh_tools.cc @@ -7091,7 +7091,7 @@ static void sort_bmelem_flag(bContext *C, } } - BM_mesh_remap(em->bm, map[0], map[1], NULL, map[2]); + BM_mesh_remap(em->bm, map[0], map[1], map[2]); EDBMUpdate_Params params{}; params.calc_looptri = (totelem[2] != 0); diff --git a/source/blender/editors/sculpt_paint/paint_mask.cc b/source/blender/editors/sculpt_paint/paint_mask.cc index 5d29bf0163c..4d6a39bedfe 100644 --- a/source/blender/editors/sculpt_paint/paint_mask.cc +++ b/source/blender/editors/sculpt_paint/paint_mask.cc @@ -1431,8 +1431,6 @@ static void sculpt_gesture_apply_trim(SculptGestureContext *sgcontext) /* Create trim bmesh. */ BMeshCreateParams params1 = {0}; - params1.create_unique_ids = params1.id_map = params1.copy_all_layers = true; - params1.id_elem_mask = BM_VERT | BM_EDGE | BM_FACE; const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(sculpt_mesh, trim_mesh); BMesh *trimbm = BM_mesh_create(&allocsize, ¶ms1); diff --git a/source/blender/editors/sculpt_paint/sculpt_boundary.cc b/source/blender/editors/sculpt_paint/sculpt_boundary.cc index 1e48f3af9e9..05bc1c80c22 100644 --- a/source/blender/editors/sculpt_paint/sculpt_boundary.cc +++ b/source/blender/editors/sculpt_paint/sculpt_boundary.cc @@ -290,62 +290,6 @@ static float *calc_boundary_tangent(SculptSession *ss, SculptBoundary *boundary) float no1[3]; SCULPT_vertex_normal_get(ss, vertex, no1); -#if 0 - volatile int val = SCULPT_vertex_valence_get(ss, vertex); - float *ws = BLI_array_alloca(ws, val); - float *cot1 = BLI_array_alloca(cot1, val); - float *cot2 = BLI_array_alloca(cot2, val); - float *areas = BLI_array_alloca(areas, val); - float totarea; - - SCULPT_get_cotangents(ss, vertex, ws, cot1, cot2, areas, &totarea); - - float(*cos)[3] = BLI_array_alloca(cos, val); - float *scalars = BLI_array_alloca(scalars, val); - - SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vertex, ni) { - scalars[ni.i] = boundary->boundary_dist[ni.index]; - copy_v3_v3(cos[ni.i], SCULPT_vertex_co_get(ss, ni.vertex)); - } - SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); - - for (int j1 = 0; j1 < val; j1++) { - int j2 = (j1 + 1) % val; - - float *co2 = cos[j1]; - float *co3 = cos[j2]; - float dir2[3]; - float dir3[3]; - - float f2 = scalars[j1]; - float f3 = scalars[j2]; - - if (f2 == FLT_MAX || f1 == FLT_MAX) { - continue; - } - - float du = f2 - f1; - float dv = f3 - f1; - - sub_v3_v3v3(dir2, co2, co1); - sub_v3_v3v3(dir3, co3, co1); - - mul_v3_fl(dir2, du); - mul_v3_fl(dir3, dv); - - add_v3_v3(dir2, dir3); - // normalize_v3(dir2); - - float w = 1.0; // ws[j1]; - - madd_v3_v3v3fl(dir, dir, dir2, w); - } - - normalize_v3(dir); - copy_v3_v3(tangents[i], dir); - -#else - SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vertex, ni) { const float *co2 = SCULPT_vertex_co_get(ss, ni.vertex); float no2[3]; @@ -380,41 +324,11 @@ static float *calc_boundary_tangent(SculptSession *ss, SculptBoundary *boundary) negate_v3(dir); copy_v3_v3(tangents[i], dir); -#endif } return (float *)tangents; } -static void sculpt_boundary_cotan_init(SculptSession *ss, SculptBoundary *boundary) -{ - const int totvert = SCULPT_vertex_count_get(ss); - boundary->boundary_cotangents = MEM_cnew_array(totvert, "StoredCotangentW"); - StoredCotangentW *cotw = boundary->boundary_cotangents; - - for (int i = 0; i < totvert; i++, cotw++) { - if (boundary->boundary_dist[i] == FLT_MAX) { - cotw->length = 0; - cotw->weights = nullptr; - continue; - } - - PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); - const int val = SCULPT_vertex_valence_get(ss, vertex); - - cotw->length = val; - - if (val < MAX_STORED_COTANGENTW_EDGES) { - cotw->weights = cotw->static_weights; - } - else { - cotw->weights = (float *)MEM_malloc_arrayN(val, sizeof(*cotw->weights), "cotw->weights"); - } - - SCULPT_get_cotangents(ss, vertex, cotw->weights, nullptr, nullptr, nullptr, nullptr); - } -} - static void sculpt_boundary_indices_init(Object *ob, SculptSession *ss, SculptBoundary *boundary, @@ -465,54 +379,6 @@ static void sculpt_boundary_indices_init(Object *ob, boundary->boundary_dist = SCULPT_geodesic_distances_create( ob, boundary_verts, radius, boundary->boundary_closest, nullptr); - sculpt_boundary_cotan_init(ss, boundary); - -#if 0 // smooth geodesic scalar field - float *boundary_dist = MEM_calloc_arrayN(totvert, sizeof(float), "boundary_dist"); - - for (int iteration = 0; iteration < 4; iteration++) { - for (int i = 0; i < totvert; i++) { - if (boundary->boundary_dist[i] == FLT_MAX) { - boundary_dist[i] = FLT_MAX; - continue; - } - - PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); - float tot = 0.0f; - - StoredCotangentW *cotw = boundary->boundary_cotangents + i; - - SculptVertexNeighborIter ni; - int j = 0; - SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vertex, ni) { - if (boundary->boundary_dist[ni.index] == FLT_MAX) { - j++; - continue; - } - - const float w = cotw->weights[j]; - - boundary_dist[i] += boundary->boundary_dist[ni.index] * w; - - tot += w; - j++; - } - SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); - - if (tot == 0.0f) { - boundary_dist[i] = FLT_MAX; - } - else { - boundary_dist[i] /= tot; - } - } - - SWAP(float *, boundary_dist, boundary->boundary_dist); - } - - MEM_SAFE_FREE(boundary_dist); -#endif - boundary->boundary_tangents = (float(*)[3])calc_boundary_tangent(ss, boundary); #if 1 // smooth geodesic tangent field @@ -529,7 +395,6 @@ static void sculpt_boundary_indices_init(Object *ob, PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); float tot = 0.0f; - // StoredCotangentW *cotw = boundary->boundary_cotangents + i; float tan[3] = {0.0f, 0.0f, 0.0f}; SculptVertexNeighborIter ni; @@ -911,18 +776,6 @@ void SCULPT_boundary_data_free(SculptBoundary *boundary) MEM_SAFE_FREE(boundary->slide.directions); MEM_SAFE_FREE(boundary->circle.origin); MEM_SAFE_FREE(boundary->circle.radius); - - StoredCotangentW *cotw = boundary->boundary_cotangents; - - if (cotw) { - for (int i = 0; i < boundary->sculpt_totvert; i++, cotw++) { - if (cotw->weights != cotw->static_weights) { - MEM_SAFE_FREE(cotw->weights); - } - } - } - - MEM_SAFE_FREE(boundary->boundary_cotangents); MEM_SAFE_FREE(boundary); } @@ -1788,8 +1641,6 @@ void SCULPT_do_boundary_brush(Sculpt *sd, Object *ob, Span nodes) SculptSession *ss = ob->sculpt; Brush *brush = BKE_paint_brush(&sd->paint); - SCULPT_cotangents_begin(ob, ss); - const float radius = ss->cache->radius; const float boundary_radius = brush ? radius * brush->boundary_offset : radius; diff --git a/source/blender/editors/sculpt_paint/sculpt_curvature.cc b/source/blender/editors/sculpt_paint/sculpt_curvature.cc index 6d4b6297547..ece3c9eb82d 100644 --- a/source/blender/editors/sculpt_paint/sculpt_curvature.cc +++ b/source/blender/editors/sculpt_paint/sculpt_curvature.cc @@ -155,25 +155,6 @@ bool SCULPT_calc_principle_curvatures(SculptSession *ss, SCULPT_vertex_normal_get(ss, vertex, no); normal_covariance(nmat, no); -#if 0 - int val = SCULPT_vertex_valence_get(ss, vertex); - float *ws = (float *)BLI_array_alloca(ws, val); - float *cot1 = (float *)BLI_array_alloca(cot1, val); - float *cot2 = (float *)BLI_array_alloca(cot2, val); - float *areas = (float *)BLI_array_alloca(areas, val); - float totarea = 0.0f; - - SCULPT_get_cotangents(ss, vertex, ws, cot1, cot2, areas, &totarea); - - SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vertex, ni) { - SCULPT_vertex_normal_get(ss, ni.vertex, no2); - sub_v3_v3(no2, no); - - normal_covariance(nmat2, no2); - madd_m3_m3m3fl(nmat, nmat, nmat2, ws[ni.i]); - } - SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); -#else /* TODO: review the math here. We're deriving the curvature * via an eigen decomposition of the weighted summed * normal covariance matrices of the surrounding topology. @@ -196,7 +177,6 @@ bool SCULPT_calc_principle_curvatures(SculptSession *ss, madd_m3_m3m3fl(nmat, nmat, nmat2, 1.0f / ni.size); } SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); -#endif if (!useAccurateSolver || !BLI_eigen_solve_selfadjoint_m3(nmat, out->ks, out->principle)) { /* Do simple power solve in one direction. */ diff --git a/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc b/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc index 95f2f5860fc..418d097cedb 100644 --- a/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc +++ b/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc @@ -72,360 +72,6 @@ BMesh *SCULPT_dyntopo_empty_bmesh() { return BKE_sculptsession_empty_bmesh_create(); } -// TODO: check if (mathematically speaking) is it really necassary -// to sort the edge lists around verts - -// from http://rodolphe-vaillant.fr/?e=20 -static float tri_voronoi_area(const float p[3], const float q[3], const float r[3]) -{ - float pr[3]; - float pq[3]; - - sub_v3_v3v3(pr, p, r); - sub_v3_v3v3(pq, p, q); - - float angles[3]; - - angle_tri_v3(angles, p, q, r); - - if (angles[0] > (float)M_PI * 0.5f) { - return area_tri_v3(p, q, r) / 2.0f; - } - else if (angles[1] > (float)M_PI * 0.5f || angles[2] > (float)M_PI * 0.5f) { - return area_tri_v3(p, q, r) / 4.0f; - } - else { - - float dpr = dot_v3v3(pr, pr); - float dpq = dot_v3v3(pq, pq); - - float area = (1.0f / 8.0f) * - (dpr * cotangent_tri_weight_v3(q, p, r) + dpq * cotangent_tri_weight_v3(r, q, p)); - - return area; - } -} - -void SCULPT_dyntopo_get_cotangents(SculptSession *ss, - PBVHVertRef vertex, - float *r_ws, - float *r_cot1, - float *r_cot2, - float *r_area, - float *r_totarea) -{ - SCULPT_dyntopo_check_disk_sort(ss, vertex); - - BMVert *v = (BMVert *)vertex.i; - BMEdge *e = v->e; - - if (!e) { - return; - } - - int i = 0; - float totarea = 0.0f; - // float totw = 0.0f; - - do { - BMEdge *eprev = v == e->v1 ? e->v1_disk_link.prev : e->v2_disk_link.prev; - BMEdge *enext = v == e->v1 ? e->v1_disk_link.next : e->v2_disk_link.next; - - BMVert *v1 = BM_edge_other_vert(eprev, v); - BMVert *v2 = BM_edge_other_vert(e, v); - BMVert *v3 = BM_edge_other_vert(enext, v); - - float cot1 = cotangent_tri_weight_v3(v1->co, v->co, v2->co); - float cot2 = cotangent_tri_weight_v3(v3->co, v2->co, v->co); - - float area = tri_voronoi_area(v->co, v1->co, v2->co); - - r_ws[i] = (cot1 + cot2); - // totw += r_ws[i]; - - totarea += area; - - if (r_cot1) { - r_cot1[i] = cot1; - } - - if (r_cot2) { - r_cot2[i] = cot2; - } - - if (r_area) { - r_area[i] = area; - } - - i++; - e = enext; - } while (e != v->e); - - if (r_totarea) { - *r_totarea = totarea; - } - - int count = i; - - float mul = 1.0f / (totarea * 2.0); - - for (i = 0; i < count; i++) { - r_ws[i] *= mul; - } -} - -void SCULPT_faces_get_cotangents(SculptSession *ss, - PBVHVertRef vertex, - float *r_ws, - float *r_cot1, - float *r_cot2, - float *r_area, - float *r_totarea) -{ - // sculpt vemap should always be sorted in disk cycle order - - float totarea = 0.0; - - Span elem = ss->vemap[vertex.i]; - const int count = elem.size(); - - for (int i = 0; i < count; i++) { - int i1 = (i + count - 1) % count; - int i2 = i; - int i3 = (i + 1) % count; - - const float *v = ss->vert_positions[vertex.i]; - const int2 &e1 = ss->edges[elem[i1]]; - const int2 &e2 = ss->edges[elem[i2]]; - const int2 &e3 = ss->edges[elem[i3]]; - - const float *v1 = (unsigned int)vertex.i == e1[0] ? ss->vert_positions[e1[1]] : - ss->vert_positions[e1[0]]; - const float *v2 = (unsigned int)vertex.i == e2[0] ? ss->vert_positions[e2[1]] : - ss->vert_positions[e2[0]]; - const float *v3 = (unsigned int)vertex.i == e3[0] ? ss->vert_positions[e3[1]] : - ss->vert_positions[e3[0]]; - - float cot1 = cotangent_tri_weight_v3(v1, v, v2); - float cot2 = cotangent_tri_weight_v3(v3, v2, v); - - float area = tri_voronoi_area(v, v1, v2); - - r_ws[i] = (cot1 + cot2); - - totarea += area; - - if (r_cot1) { - r_cot1[i] = cot1; - } - - if (r_cot2) { - r_cot2[i] = cot2; - } - - if (r_area) { - r_area[i] = area; - } - } - - if (r_totarea) { - *r_totarea = totarea; - } - - float mul = 1.0f / (totarea * 2.0); - - for (int i = 0; i < count; i++) { - r_ws[i] *= mul; - } -} - -void SCULPT_cotangents_begin(Object *ob, SculptSession *ss) -{ - SCULPT_vertex_random_access_ensure(ss); - int totvert = SCULPT_vertex_count_get(ss); - - switch (BKE_pbvh_type(ss->pbvh)) { - case PBVH_BMESH: { - for (int i = 0; i < totvert; i++) { - PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); - SCULPT_dyntopo_check_disk_sort(ss, vertex); - } - break; - } - case PBVH_FACES: { - Mesh *mesh = BKE_object_get_original_mesh(ob); - - SCULPT_ensure_vemap(ss); - - break; - } - case PBVH_GRIDS: // not supported yet - break; - } -} - -void SCULPT_get_cotangents(SculptSession *ss, - PBVHVertRef vertex, - float *r_ws, - float *r_cot1, - float *r_cot2, - float *r_area, - float *r_totarea) -{ - switch (BKE_pbvh_type(ss->pbvh)) { - case PBVH_BMESH: - SCULPT_dyntopo_get_cotangents(ss, vertex, r_ws, r_cot1, r_cot2, r_area, r_totarea); - break; - case PBVH_FACES: - SCULPT_faces_get_cotangents(ss, vertex, r_ws, r_cot1, r_cot2, r_area, r_totarea); - break; - case PBVH_GRIDS: { - { - /* Not supported, return uniform weights. */ - - int val = SCULPT_vertex_valence_get(ss, vertex); - - for (int i = 0; i < val; i++) { - r_ws[i] = 1.0f; - } - } - break; - } - } -} - -void SCULT_dyntopo_flag_all_disk_sort(SculptSession *ss) -{ - BKE_pbvh_bmesh_flag_all_disk_sort(ss->pbvh); -} - -/* Returns true if edge disk list around vertex was sorted. */ -bool SCULPT_dyntopo_check_disk_sort(SculptSession *ss, PBVHVertRef vertex) -{ - BMVert *v = (BMVert *)vertex.i; - uint8_t *flag = blender::bke::paint::vertex_attr_ptr(vertex, ss->attrs.flags); - - if (*flag & SCULPTFLAG_NEED_DISK_SORT) { - *flag &= ~SCULPTFLAG_NEED_DISK_SORT; - - BM_sort_disk_cycle(v); - - return true; - } - - return false; -} - -/* -Copies the bmesh, but orders the elements -according to PBVH node to improve memory locality -*/ -void SCULPT_reorder_bmesh(SculptSession * /*ss*/) -{ -#if 0 - SCULPT_face_random_access_ensure(ss); - SCULPT_vertex_random_access_ensure(ss); - - int actv = ss->active_vertex.i ? - BKE_pbvh_vertex_to_index(ss->pbvh, ss->active_vertex) : - -1; - int actf = ss->active_face.i ? - BKE_pbvh_face_to_index(ss->pbvh, ss->active_face) : - -1; - - if (ss->bm_log) { - BM_log_full_mesh(ss->bm, ss->bm_log); - } - - ss->bm = BKE_pbvh_reorder_bmesh(ss->pbvh); - - SCULPT_face_random_access_ensure(ss); - SCULPT_vertex_random_access_ensure(ss); - - if (actv >= 0) { - ss->active_vertex = BKE_pbvh_index_to_vertex(ss->pbvh, actv); - } - if (actf >= 0) { - ss->active_face = BKE_pbvh_index_to_face(ss->pbvh, actf); - } - - BKE_sculptsession_update_attr_refs(ob); - -#endif -} - -void SCULPT_dynamic_topology_triangulate(SculptSession *ss, BMesh *bm) -{ - if (bm->totloop == bm->totface * 3) { - ss->totfaces = ss->totpoly = ss->bm->totface; - ss->totvert = ss->bm->totvert; - - return; - } - - BMIter iter; - BMFace *f; - - BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { - BM_elem_flag_enable(f, BM_ELEM_TAG); - } - - MemArena *pf_arena = BLI_memarena_new(BLI_POLYFILL_ARENA_SIZE, __func__); - LinkNode *f_double = nullptr; - - Vector(faces_array); - - BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { - if (f->len <= 3) { - continue; - } - - bool sel = BM_elem_flag_test(f, BM_ELEM_SELECT); - - int faces_array_tot = f->len; - faces_array.resize(faces_array_tot); - - BM_face_triangulate(bm, - f, - faces_array.data(), - &faces_array_tot, - nullptr, - nullptr, - &f_double, - MOD_TRIANGULATE_QUAD_BEAUTY, - MOD_TRIANGULATE_NGON_EARCLIP, - true, - pf_arena, - nullptr); - - for (int i = 0; i < faces_array_tot; i++) { - BMFace *f2 = faces_array[i]; - - // forcibly copy selection state - if (sel) { - BM_face_select_set(bm, f2, true); - - // restore original face selection state too, triangulate code unset it - BM_face_select_set(bm, f, true); - } - - // paranoia check that tag flag wasn't copied over - BM_elem_flag_disable(f2, BM_ELEM_TAG); - } - } - - while (f_double) { - LinkNode *next = f_double->next; - BM_face_kill(bm, (BMFace *)f_double->link); - MEM_freeN(f_double); - f_double = next; - } - - BLI_memarena_free(pf_arena); - - ss->totfaces = ss->totpoly = ss->bm->totface; - ss->totvert = ss->bm->totvert; -} void SCULPT_pbvh_clear(Object *ob) { @@ -614,8 +260,7 @@ void SCULPT_update_all_valence_boundary(Object *ob) BLI_assert(cd_flag != -1 && cd_boundary != -1 && cd_valence != -1); BM_ITER_MESH (v, &iter, ss->bm, BM_VERTS_OF_MESH) { - *BM_ELEM_CD_PTR(v, cd_flag) = SCULPTFLAG_NEED_DISK_SORT | - SCULPTFLAG_NEED_TRIANGULATE; + *BM_ELEM_CD_PTR(v, cd_flag) = SCULPTFLAG_NEED_TRIANGULATE; BM_ELEM_CD_SET_INT(v, cd_valence, BM_vert_edge_count(v)); BM_ELEM_CD_SET_INT(v, cd_boundary, SCULPT_BOUNDARY_NEEDS_UPDATE); @@ -639,7 +284,7 @@ void SCULPT_update_all_valence_boundary(Object *ob) blender::bke::paint::vertex_attr_set( vertex, ss->attrs.flags, - SCULPTFLAG_NEED_VALENCE | SCULPTFLAG_NEED_TRIANGULATE | SCULPTFLAG_NEED_DISK_SORT); + SCULPTFLAG_NEED_VALENCE | SCULPTFLAG_NEED_TRIANGULATE ); BKE_sculpt_boundary_flag_update(ss, vertex); SCULPT_vertex_valence_get(ss, vertex); SCULPT_vertex_is_boundary(ss, vertex, SCULPT_BOUNDARY_ALL); diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.hh b/source/blender/editors/sculpt_paint/sculpt_intern.hh index fa6839dbad3..ce036b0ce88 100644 --- a/source/blender/editors/sculpt_paint/sculpt_intern.hh +++ b/source/blender/editors/sculpt_paint/sculpt_intern.hh @@ -2363,31 +2363,6 @@ void SCULPT_curvature_dir_get(SculptSession *ss, /** \name Cotangent API * \{ */ -bool SCULPT_dyntopo_check_disk_sort(SculptSession *ss, PBVHVertRef vertex); -void SCULT_dyntopo_flag_all_disk_sort(SculptSession *ss); - -// call SCULPT_cotangents_begin in the main thread before any calls to this function -void SCULPT_dyntopo_get_cotangents(SculptSession *ss, - PBVHVertRef vertex, - float *r_ws, - float *r_cot1, - float *r_cot2, - float *r_area, - float *r_totarea); - -/** call SCULPT_cotangents_begin in the main thread before any calls to this function */ -void SCULPT_get_cotangents(SculptSession *ss, - PBVHVertRef vertex, - float *r_ws, - float *r_cot1, - float *r_cot2, - float *r_area, - float *r_totarea); - -/** call this in the main thread before any calls to SCULPT_get_cotangents */ -void SCULPT_cotangents_begin(struct Object *ob, SculptSession *ss); -/** \} */ - void SCULPT_ensure_persistent_layers(SculptSession *ss, struct Object *ob); void SCULPT_ensure_epmap(SculptSession *ss); void SCULPT_ensure_vemap(SculptSession *ss); diff --git a/source/blender/editors/sculpt_paint/sculpt_ops.cc b/source/blender/editors/sculpt_paint/sculpt_ops.cc index 0d521e33785..85bc27b7f08 100644 --- a/source/blender/editors/sculpt_paint/sculpt_ops.cc +++ b/source/blender/editors/sculpt_paint/sculpt_ops.cc @@ -194,60 +194,6 @@ static bool sculpt_only_bmesh_poll(bContext *C) return false; } -static int sculpt_spatial_sort_exec(bContext *C, wmOperator *op) -{ - Object *ob = CTX_data_active_object(C); - SculptSession *ss = ob->sculpt; - PBVH *pbvh = ss->pbvh; - - if (!pbvh) { - return OPERATOR_CANCELLED; - } - - switch (BKE_pbvh_type(pbvh)) { - case PBVH_BMESH: - SCULPT_undo_push_begin(ob, op); - SCULPT_undo_push_node(ob, nullptr, SCULPT_UNDO_GEOMETRY); - - BKE_pbvh_reorder_bmesh(ss->pbvh); - - BKE_pbvh_bmesh_on_mesh_change(ss->pbvh); - BM_log_full_mesh(ss->bm, ss->bm_log); - - ss->active_vertex.i = 0; - ss->active_face.i = 0; - - BKE_pbvh_free(ss->pbvh); - ss->pbvh = nullptr; - - /* Finish undo. */ - SCULPT_undo_push_end(ob); - - break; - case PBVH_FACES: - return OPERATOR_CANCELLED; - case PBVH_GRIDS: - return OPERATOR_CANCELLED; - } - - /* Redraw. */ - DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); - WM_event_add_notifier(C, ND_DATA | NC_OBJECT | ND_DRAW, ob); - - return OPERATOR_FINISHED; -} -static void SCULPT_OT_spatial_sort_mesh(wmOperatorType *ot) -{ - /* Identifiers. */ - ot->name = "Spatially Sort Mesh"; - ot->idname = "SCULPT_OT_spatial_sort_mesh"; - ot->description = "Spatially sort mesh to improve memory coherency"; - - /* API callbacks. */ - ot->exec = sculpt_spatial_sort_exec; - ot->poll = sculpt_only_bmesh_poll; -} - static int sculpt_symmetrize_exec(bContext *C, wmOperator *op) { Main *bmain = CTX_data_main(C); @@ -281,16 +227,13 @@ static int sculpt_symmetrize_exec(bContext *C, wmOperator *op) sd->symmetrize_direction, dist, true); -#ifndef DYNTOPO_DYNAMIC_TESS - SCULPT_dynamic_topology_triangulate(ss, ss->bm); -#endif + /* Bisect operator flags edges (keep tags clean for edge queue). */ BM_mesh_elem_hflag_disable_all(ss->bm, BM_EDGE, BM_ELEM_TAG, false); BM_mesh_toolflags_set(ss->bm, false); BKE_pbvh_recalc_bmesh_boundary(ss->pbvh); - SCULT_dyntopo_flag_all_disk_sort(ss); /* De-duplicate element IDs. */ BM_idmap_check_ids(ss->bm_idmap); @@ -1364,7 +1307,7 @@ static int sculpt_regularize_rake_exec(bContext *C, wmOperator *op) void sculpt_end_vis_object(bContext * C, SculptSession * ss, Object * ob, BMesh * bm); BMeshCreateParams params = {}; - params.create_unique_ids = params.use_toolflags = false; + params.use_toolflags = false; Object *visob = sculpt_get_vis_object(C, ss, "rakevis"); BMesh *visbm = BM_mesh_create(&bm_mesh_allocsize_default, ¶ms); @@ -1492,8 +1435,6 @@ static int sculpt_regularize_rake_exec(bContext *C, wmOperator *op) float tanco[3]; add_v3_v3v3(tanco, v2->co, dir2); - SCULPT_dyntopo_check_disk_sort(ss, BKE_pbvh_make_vref((intptr_t)v2)); - float lastdir3[3]; float firstdir3[3]; bool first = true; @@ -2071,8 +2012,6 @@ void ED_operatortypes_sculpt(void) WM_operatortype_append(SCULPT_OT_reset_brushes); WM_operatortype_append(SCULPT_OT_ipmask_filter); - WM_operatortype_append(SCULPT_OT_spatial_sort_mesh); - WM_operatortype_append(SCULPT_OT_expand); WM_operatortype_append(SCULPT_OT_mask_from_cavity); WM_operatortype_append(SCULPT_OT_regularize_rake_directions); diff --git a/source/blender/editors/sculpt_paint/sculpt_smooth.cc b/source/blender/editors/sculpt_paint/sculpt_smooth.cc index 67ec9f4fa80..54e1fc2f291 100644 --- a/source/blender/editors/sculpt_paint/sculpt_smooth.cc +++ b/source/blender/editors/sculpt_paint/sculpt_smooth.cc @@ -37,7 +37,7 @@ using blender::float3; using blender::Vector; float BKE_pbvh_bmesh_detail_size_avg_get(PBVH *pbvh); -/* */ +#if 0 static void SCULPT_neighbor_coors_average_for_detail(SculptSession *ss, float result[3], PBVHVertRef vertex) @@ -80,6 +80,7 @@ static void SCULPT_neighbor_coors_average_for_detail(SculptSession *ss, SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); mul_v3_v3fl(result, pos_accum, 1.0f / neighbor_count); } +#endif static void SCULPT_neighbor_coords_average_interior_ex(SculptSession *ss, float result[3], @@ -291,7 +292,6 @@ void vec_transform(float r_dir2[3], float no[3], int bits) void SCULPT_get_normal_average( SculptSession *ss, float avg[3], PBVHVertRef vertex, bool weighted, bool use_original) { - float tot = 0.0f; int valence = SCULPT_vertex_valence_get(ss, vertex); float *areas = nullptr; diff --git a/source/blender/editors/sculpt_paint/sculpt_undo.cc b/source/blender/editors/sculpt_paint/sculpt_undo.cc index 0a35a5302ba..583b82ebab7 100644 --- a/source/blender/editors/sculpt_paint/sculpt_undo.cc +++ b/source/blender/editors/sculpt_paint/sculpt_undo.cc @@ -85,7 +85,6 @@ #include "bmesh_log.h" #include "sculpt_intern.hh" -#define WHEN_GLOBAL_UNDO_WORKS /* Uncomment to print the undo stack in the console on push/undo/redo. */ //#define SCULPT_UNDO_DEBUG @@ -734,8 +733,7 @@ static void bmesh_undo_on_vert_add(BMVert *v, void *userdata) BM_ELEM_CD_SET_INT(v, data->cd_vert_node_offset, -1); *(int *)BM_ELEM_CD_GET_VOID_P(v, data->cd_boundary_flag) |= SCULPT_BOUNDARY_NEEDS_UPDATE; - *BM_ELEM_CD_PTR(v, data->cd_flags) |= SCULPTFLAG_NEED_DISK_SORT | - SCULPTFLAG_NEED_VALENCE | + *BM_ELEM_CD_PTR(v, data->cd_flags) |= SCULPTFLAG_NEED_VALENCE | SCULPTFLAG_NEED_TRIANGULATE; } @@ -753,8 +751,7 @@ static void bmesh_undo_on_face_kill(BMFace *f, void *userdata) BMLoop *l = f->l_first; do { - *BM_ELEM_CD_PTR(l->v, data->cd_flags) |= SCULPTFLAG_NEED_DISK_SORT | - SCULPTFLAG_NEED_VALENCE; + *BM_ELEM_CD_PTR(l->v, data->cd_flags) |= SCULPTFLAG_NEED_VALENCE; *BM_ELEM_CD_PTR(l->v, data->cd_boundary_flag) |= SCULPT_BOUNDARY_NEEDS_UPDATE; } while ((l = l->next) != f->l_first); @@ -777,8 +774,7 @@ static void bmesh_undo_on_face_add(BMFace *f, void *userdata) do { *(int *)BM_ELEM_CD_GET_VOID_P(l->v, data->cd_boundary_flag) |= SCULPT_BOUNDARY_NEEDS_UPDATE; - *BM_ELEM_CD_PTR(l->v, data->cd_flags) |= SCULPTFLAG_NEED_DISK_SORT | - SCULPTFLAG_NEED_VALENCE; + *BM_ELEM_CD_PTR(l->v, data->cd_flags) |= SCULPTFLAG_NEED_VALENCE; if (f->len > 3) { *BM_ELEM_CD_PTR(l->v, data->cd_flags) |= SCULPTFLAG_NEED_TRIANGULATE; @@ -820,11 +816,9 @@ static void bmesh_undo_on_edge_kill(BMEdge *e, void *userdata) *(int *)BM_ELEM_CD_GET_VOID_P(e->v1, data->cd_boundary_flag) |= SCULPT_BOUNDARY_NEEDS_UPDATE; *(int *)BM_ELEM_CD_GET_VOID_P(e->v2, data->cd_boundary_flag) |= SCULPT_BOUNDARY_NEEDS_UPDATE; - *BM_ELEM_CD_PTR(e->v1, data->cd_flags) |= SCULPTFLAG_NEED_DISK_SORT | - SCULPTFLAG_NEED_VALENCE | + *BM_ELEM_CD_PTR(e->v1, data->cd_flags) |= SCULPTFLAG_NEED_VALENCE | SCULPTFLAG_NEED_TRIANGULATE; - *BM_ELEM_CD_PTR(e->v2, data->cd_flags) |= SCULPTFLAG_NEED_DISK_SORT | - SCULPTFLAG_NEED_VALENCE | + *BM_ELEM_CD_PTR(e->v2, data->cd_flags) |= SCULPTFLAG_NEED_VALENCE | SCULPTFLAG_NEED_TRIANGULATE; }; ; @@ -835,11 +829,9 @@ static void bmesh_undo_on_edge_add(BMEdge *e, void *userdata) *(int *)BM_ELEM_CD_GET_VOID_P(e->v1, data->cd_boundary_flag) |= SCULPT_BOUNDARY_NEEDS_UPDATE; *(int *)BM_ELEM_CD_GET_VOID_P(e->v2, data->cd_boundary_flag) |= SCULPT_BOUNDARY_NEEDS_UPDATE; - *BM_ELEM_CD_PTR(e->v1, data->cd_flags) |= SCULPTFLAG_NEED_DISK_SORT | - SCULPTFLAG_NEED_VALENCE | + *BM_ELEM_CD_PTR(e->v1, data->cd_flags) |= SCULPTFLAG_NEED_VALENCE | SCULPTFLAG_NEED_TRIANGULATE; - *BM_ELEM_CD_PTR(e->v2, data->cd_flags) |= SCULPTFLAG_NEED_DISK_SORT | - SCULPTFLAG_NEED_VALENCE | + *BM_ELEM_CD_PTR(e->v2, data->cd_flags) |= SCULPTFLAG_NEED_VALENCE | SCULPTFLAG_NEED_TRIANGULATE; } @@ -1310,11 +1302,9 @@ static int sculpt_undo_bmesh_restore( { // handle transition from another undo type -#ifdef WHEN_GLOBAL_UNDO_WORKS if (!ss->bm_log && ss->bm && unode->bm_entry) { // && BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) { ss->bm_log = BM_log_from_existing_entries_create(ss->bm, ss->bm_idmap, unode->bm_entry); } -#endif if (ss->bm_log && ss->bm && !ELEM(unode->type, SCULPT_UNDO_DYNTOPO_BEGIN, SCULPT_UNDO_DYNTOPO_END)) { @@ -2965,11 +2955,6 @@ static void sculpt_undosys_step_decode( * (some) evaluated data. */ BKE_scene_graph_evaluated_ensure(depsgraph, bmain); -#ifndef WHEN_GLOBAL_UNDO_WORKS - /* Don't add sculpt topology undo steps when reading back undo state. - * The undo steps must enter/exit for us. */ - me->flag &= ~ME_SCULPT_DYNAMIC_TOPOLOGY; -#endif ED_object_sculptmode_enter_ex(bmain, depsgraph, scene, ob, true, nullptr, false); } diff --git a/source/blender/makesdna/DNA_meshdata_types.h b/source/blender/makesdna/DNA_meshdata_types.h index 48df94010f5..c5b098d84ea 100644 --- a/source/blender/makesdna/DNA_meshdata_types.h +++ b/source/blender/makesdna/DNA_meshdata_types.h @@ -529,10 +529,9 @@ typedef struct MRecast { typedef enum eSculptFlag { SCULPTFLAG_VERT_FSET_HIDDEN = (1 << 1), SCULPTFLAG_NEED_TRIANGULATE = (1 << 2), - SCULPTFLAG_NEED_DISK_SORT = (1 << 3), - SCULPTFLAG_NEED_VALENCE = (1 << 4), - SCULPTFLAG_SPLIT_TEMP = (1 << 5), - SCULPTFLAG_PBVH_BOUNDARY = (1 << 6), + SCULPTFLAG_NEED_VALENCE = (1 << 3), + SCULPTFLAG_SPLIT_TEMP = (1 << 4), + SCULPTFLAG_PBVH_BOUNDARY = (1 << 5), } eSculptFlag; /* for internal bmesh toolflags api */ diff --git a/source/blender/python/bmesh/bmesh_py_types.c b/source/blender/python/bmesh/bmesh_py_types.c index 8fa141cae3b..ea723bd592a 100644 --- a/source/blender/python/bmesh/bmesh_py_types.c +++ b/source/blender/python/bmesh/bmesh_py_types.c @@ -5,8 +5,6 @@ * \ingroup pybmesh */ -#include "MEM_guardedalloc.h" - #include "BLI_math.h" #include "BLI_sort.h" #include "BLI_string.h" @@ -2788,7 +2786,7 @@ static PyObject *bpy_bmelemseq_sort(BPy_BMElemSeq *self, PyObject *args, PyObjec return NULL; } - BM_mesh_remap(bm, vert_idx, edge_idx, face_idx, NULL); + BM_mesh_remap(bm, vert_idx, edge_idx, face_idx); PyMem_FREE(elem_map_idx); PyMem_FREE(elem_idx); @@ -4055,134 +4053,6 @@ int bpy_bm_generic_valid_check_source(BMesh *bm_source, return ret; } -void *BPy_bm_new_customdata_layout_pre(BMesh *bm, CustomData *cdata, char htype) -{ - if (!CustomData_has_layer(cdata, CD_BM_ELEM_PYPTR)) { - return NULL; - } - - BLI_mempool *pool = NULL; - int num = 0; - - switch (htype) { - case BM_VERT: - pool = bm->vpool; - num = bm->totvert; - break; - case BM_EDGE: - pool = bm->epool; - num = bm->totedge; - break; - case BM_LOOP: - pool = bm->fpool; - num = bm->totloop; - break; - case BM_FACE: - pool = bm->fpool; - num = bm->totface; - break; - } - - void **ptrs = MEM_malloc_arrayN(num, sizeof(void *), __func__); - - int cd_py = CustomData_get_offset(cdata, CD_BM_ELEM_PYPTR); - - BMElem *elem; - BLI_mempool_iter iter; - - int i = 0; - - if (htype != BM_LOOP) { - BLI_mempool_iternew(pool, &iter); - while ((elem = BLI_mempool_iterstep(&iter))) { - ptrs[i++] = *((void **)BM_ELEM_CD_GET_VOID_P(elem, cd_py)); - } - } - else { - BMFace *f; - - BLI_mempool_iternew(pool, &iter); - while ((f = BLI_mempool_iterstep(&iter))) { - BMLoop *l = f->l_first; - - do { - ptrs[i++] = *((void **)BM_ELEM_CD_GET_VOID_P(l, cd_py)); - } while ((l = l->next) != f->l_first); - } - } - - return ptrs; -} - -void BPy_bm_new_customdata_layout(BMesh *bm, CustomData *cdata, void *state, char htype) -{ - /* - * Un-invalidate python pointers, which got invalidated when the customdata layout - * changed. - */ - - if (!state || !CustomData_has_layer(cdata, CD_BM_ELEM_PYPTR)) { - return; - } - - BLI_mempool *pool = NULL; - void **ptrs = (void **)state; - - switch (htype) { - case BM_VERT: - pool = bm->vpool; - break; - case BM_EDGE: - pool = bm->epool; - break; - case BM_LOOP: - pool = bm->fpool; - break; - case BM_FACE: - pool = bm->fpool; - break; - } - - int cd_py = CustomData_get_offset(cdata, CD_BM_ELEM_PYPTR); - - BMElem *elem; - BLI_mempool_iter iter; - int i = 0; - - if (htype != BM_LOOP) { - BLI_mempool_iternew(pool, &iter); - while ((elem = BLI_mempool_iterstep(&iter))) { - void **ptr = BM_ELEM_CD_GET_VOID_P(elem, cd_py); - - *ptr = ptrs[i++]; - BPy_BMGeneric *bpy_ptr = (BPy_BMGeneric *)*ptr; - - if (bpy_ptr) { - bpy_ptr->bm = bm; - } - } - } - else { - BMFace *f; - - BLI_mempool_iternew(pool, &iter); - while ((f = BLI_mempool_iterstep(&iter))) { - BMLoop *l = f->l_first; - - do { - void **ptr = BM_ELEM_CD_GET_VOID_P(l, cd_py); - - *ptr = ptrs[i++]; - BPy_BMGeneric *bpy_ptr = (BPy_BMGeneric *)*ptr; - - if (bpy_ptr) { - bpy_ptr->bm = bm; - } - } while ((l = l->next) != f->l_first); - } - } -} - void bpy_bm_generic_invalidate(BPy_BMGeneric *self) { self->bm = NULL; diff --git a/source/blender/python/bmesh/bmesh_py_types_customdata.c b/source/blender/python/bmesh/bmesh_py_types_customdata.c index 72a21706240..51d3775a345 100644 --- a/source/blender/python/bmesh/bmesh_py_types_customdata.c +++ b/source/blender/python/bmesh/bmesh_py_types_customdata.c @@ -68,8 +68,6 @@ PyDoc_STRVAR(bpy_bmlayeraccess_collection__float_doc, "Generic float custom-data layer.\n\ntype: :class:`BMLayerCollection`"); PyDoc_STRVAR(bpy_bmlayeraccess_collection__int_doc, "Generic int custom-data layer.\n\ntype: :class:`BMLayerCollection`"); -PyDoc_STRVAR(bpy_bmlayeraccess_collection__face_set_doc, - "Sculpt face set custom-data layer.\n\ntype: :class:`BMLayerCollection`"); PyDoc_STRVAR(bpy_bmlayeraccess_collection__float_vector_doc, "Generic 3D vector with float precision custom-data layer.\n\ntype: " ":class:`BMLayerCollection`"); -- 2.30.2 From aee491cb5981fcc9261603ddd99aef75e923c977 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Tue, 30 May 2023 13:08:18 -0700 Subject: [PATCH 127/279] temp-sculpt-dyntopo: more reversions --- build_files/cmake/macros.cmake | 2 +- intern/atomic/intern/atomic_ops_msvc.h | 4 +- source/blender/blenkernel/BKE_attribute.h | 4 +- source/blender/blenkernel/BKE_brush.h | 6 - source/blender/blenkernel/BKE_customdata.h | 19 --- source/blender/blenkernel/BKE_paint.h | 113 +----------------- source/blender/blenkernel/BKE_sculpt.h | 2 - .../blender/blenkernel/intern/customdata.cc | 16 +-- source/blender/blenkernel/intern/paint.cc | 1 - source/blender/editors/sculpt_paint/sculpt.cc | 1 - source/blender/makesdna/DNA_brush_types.h | 3 - source/blender/makesrna/intern/rna_brush.c | 26 ---- 12 files changed, 15 insertions(+), 182 deletions(-) diff --git a/build_files/cmake/macros.cmake b/build_files/cmake/macros.cmake index 96b3712b5a2..42a0dd58660 100644 --- a/build_files/cmake/macros.cmake +++ b/build_files/cmake/macros.cmake @@ -1091,7 +1091,7 @@ function(msgfmt_simple add_custom_command( OUTPUT ${_file_to} COMMAND ${CMAKE_COMMAND} -E make_directory ${_file_to_path} - COMMAND ${CMAKE_COMMAND} -E env "$" ${_file_from} ${_file_to} + COMMAND ${CMAKE_COMMAND} -E env ${PLATFORM_ENV_BUILD} "$" ${_file_from} ${_file_to} DEPENDS msgfmt ${_file_from}) set_source_files_properties(${_file_to} PROPERTIES GENERATED TRUE) diff --git a/intern/atomic/intern/atomic_ops_msvc.h b/intern/atomic/intern/atomic_ops_msvc.h index 4d0dd1586fe..6fbcaaad346 100644 --- a/intern/atomic/intern/atomic_ops_msvc.h +++ b/intern/atomic/intern/atomic_ops_msvc.h @@ -88,12 +88,12 @@ ATOMIC_INLINE void atomic_store_uint64(uint64_t *p, uint64_t v) ATOMIC_INLINE uint64_t atomic_fetch_and_add_uint64(uint64_t *p, uint64_t x) { - return (uint32_t)InterlockedExchangeAdd64((int64_t *)p, (int64_t)x); + return (uint64_t)InterlockedExchangeAdd64((int64_t *)p, (int64_t)x); } ATOMIC_INLINE uint64_t atomic_fetch_and_sub_uint64(uint64_t *p, uint64_t x) { - return (uint32_t)InterlockedExchangeAdd64((int64_t *)p, -((int64_t)x)); + return (uint64_t)InterlockedExchangeAdd64((int64_t *)p, -((int64_t)x)); } /* Signed */ diff --git a/source/blender/blenkernel/BKE_attribute.h b/source/blender/blenkernel/BKE_attribute.h index fb742c1a401..2f1eee54070 100644 --- a/source/blender/blenkernel/BKE_attribute.h +++ b/source/blender/blenkernel/BKE_attribute.h @@ -54,7 +54,8 @@ ENUM_OPERATORS(eAttrDomainMask, ATTR_DOMAIN_MASK_ALL); bool BKE_id_attributes_supported(const struct ID *id); bool BKE_attribute_allow_procedural_access(const char *attribute_name); -/** Create a new attribute layer. +/** + * Create a new attribute layer. */ struct CustomDataLayer *BKE_id_attribute_new(struct ID *id, const char *name, @@ -117,7 +118,6 @@ void BKE_id_attributes_default_color_set(struct ID *id, const char *name); struct CustomDataLayer *BKE_id_attributes_color_find(const struct ID *id, const char *name); bool BKE_id_attribute_calc_unique_name(struct ID *id, const char *name, char *outname); -struct CustomDataLayer *BKE_id_attributes_color_find(const struct ID *id, const char *name); const char *BKE_uv_map_vert_select_name_get(const char *uv_map_name, char *buffer); const char *BKE_uv_map_edge_select_name_get(const char *uv_map_name, char *buffer); diff --git a/source/blender/blenkernel/BKE_brush.h b/source/blender/blenkernel/BKE_brush.h index 8414d0f51e6..8869937f87f 100644 --- a/source/blender/blenkernel/BKE_brush.h +++ b/source/blender/blenkernel/BKE_brush.h @@ -203,13 +203,7 @@ bool BKE_brush_has_cube_tip(const struct Brush *brush, ePaintMode paint_mode); #define BKE_brush_tool_set(brush, p, tool) \ { \ CHECK_TYPE_ANY(brush, struct Brush *); \ - char _old = *(char *)POINTER_OFFSET(brush, (p)->runtime.tool_offset); \ *(char *)POINTER_OFFSET(brush, (p)->runtime.tool_offset) = tool; \ - if ((p)->runtime.ob_mode == OB_MODE_SCULPT) { \ - if (_old != tool) { \ - BKE_brush_sculpt_reset(brush); \ - } \ - } \ } \ ((void)0) diff --git a/source/blender/blenkernel/BKE_customdata.h b/source/blender/blenkernel/BKE_customdata.h index 5f17fe18728..e33082859bb 100644 --- a/source/blender/blenkernel/BKE_customdata.h +++ b/source/blender/blenkernel/BKE_customdata.h @@ -648,14 +648,6 @@ int CustomData_name_max_length_calc(blender::StringRef name); */ void CustomData_set_layer_unique_name(struct CustomData *data, int index); -/* get unique layer name for a layer that doesn't currently exist */ -void CustomData_find_unique_layer_name(CustomData *data, - int type, - const char *name, - char *outname); - -/* try to find layer with name name; if it does not exist, - load the active layer name into outname*/ void CustomData_validate_layer_name(const struct CustomData *data, eCustomDataType type, const char *name, @@ -669,17 +661,6 @@ bool CustomData_verify_versions(struct CustomData *data, int index); /* BMesh specific custom-data stuff. */ -void CustomData_bmesh_update_active_layers(struct CustomData *fdata, struct CustomData *ldata); - -/** - * Update active indices for active/render/clone/stencil custom data layers - * based on indices from fdata layers - * used by do_versions in `readfile.c` when creating pdata and ldata for pre-bmesh - * meshes and needed to preserve active/render/clone/stencil flags set in pre-bmesh files. - */ -void CustomData_bmesh_do_versions_update_active_layers(struct CustomData *fdata, - struct CustomData *ldata); - void CustomData_bmesh_init_pool(struct CustomData *data, int totelem, char htype); /** diff --git a/source/blender/blenkernel/BKE_paint.h b/source/blender/blenkernel/BKE_paint.h index ec23aa550ab..e1990ac1466 100644 --- a/source/blender/blenkernel/BKE_paint.h +++ b/source/blender/blenkernel/BKE_paint.h @@ -321,11 +321,6 @@ typedef struct SculptPoseIKChain { SculptPoseIKChainSegment *segments; int tot_segments; float grab_delta_offset[3]; - float bend_mat[4][4]; - float bend_mat_inv[4][4]; - float bend_factor; - float bend_limit; - float bend_upper_limit; } SculptPoseIKChain; /* Cloth Brush */ @@ -355,59 +350,6 @@ typedef enum eSculptClothConstraintType { SCULPT_CLOTH_CONSTRAINT_PIN = 3, } eSculptClothConstraintType; -#define CLOTH_NO_POS_PTR - -typedef struct SculptClothConstraint { - signed char ctype, thread_nr; - - /* Index in #SculptClothSimulation.node_state of the node from where this constraint was - * created. This constraints will only be used by the solver if the state is active. */ - short node; - - float strength; - - /* Elements that are affected by the constraint. */ - /* Element a should always be a mesh vertex - * with the index stored in elem_index_a as - * it is \ - * always deformed. Element b could be - * another vertex of the same mesh or any - * other position \ - * (arbitrary point, position for a previous - * state). In that case, elem_index_a and \ - * elem_index_b should be the same to avoid - * affecting two different vertices when - * solving the \ - * constraints. *elem_position points to the - * position which is owned by the element. */ - - struct { - int index; -#ifndef CLOTH_NO_POS_PTR - float *position; -#endif - } elems[]; -} SculptClothConstraint; - -#ifndef CLOTH_NO_POS_PTR -# define MAKE_CONSTRAINT_STRUCT(totelem) \ - signed char ctype, thread_nr; \ - short node; \ - float strength; \ - struct { \ - int index; \ - float *position; \ - } elems[totelem] -#else -# define MAKE_CONSTRAINT_STRUCT(totelem) \ - signed char ctype, thread_nr; \ - short node; \ - float strength; \ - struct { \ - int index; \ - } elems[totelem] -#endif - typedef struct SculptClothLengthConstraint { /* Elements that are affected by the constraint. */ /* Element a should always be a mesh vertex with the index stored in elem_index_a as it is always @@ -467,9 +409,6 @@ typedef struct SculptClothSimulation { typedef struct SculptVertexInfo { /* Indexed by base mesh vertex index, stores if that vertex is a boundary. */ BLI_bitmap *boundary; - - /* Indexed by vertex, stores the symmetrical topology vertex index found by symmetrize. */ - int *symmetrize_map; } SculptVertexInfo; typedef struct SculptBoundaryEditInfo { @@ -563,53 +502,6 @@ typedef struct SculptBoundary { int deform_target; } SculptBoundary; -/* Array Brush. */ -typedef struct SculptArrayCopy { - int index; - int symm_pass; - float mat[4][4]; - float imat[4][4]; - float origin[3]; -} SculptArrayCopy; - -typedef struct ScultpArrayPathPoint { - float length; - float strength; - float co[3]; - float orco[3]; - float direction[3]; -} ScultpArrayPathPoint; - -typedef struct SculptArray { - SculptArrayCopy *copies[PAINT_SYMM_AREAS]; - int num_copies; - - struct { - ScultpArrayPathPoint *points; - int tot_points; - int capacity; - float total_length; - } path; - - int mode; - float normal[3]; - float direction[3]; - float radial_angle; - float initial_radial_angle; - - bool source_mat_valid; - float source_origin[3]; - float source_mat[4][4]; - float source_imat[4][4]; - float (*orco)[3]; - - int *copy_index; - int *symmetry_pass; - - float *smooth_strength; - struct SculptAttribute *scl_inst, *scl_sym; -} SculptArray; - typedef struct SculptFakeNeighbors { bool use_fake_neighbors; @@ -919,9 +811,6 @@ struct SculptSession { SculptVertexInfo vertex_info; SculptFakeNeighbors fake_neighbors; - /* Array. */ - SculptArray *array; - /* Transform operator */ float pivot_pos[3]; float pivot_rot[4]; @@ -964,7 +853,7 @@ struct SculptSession { */ char needs_flush_to_id; - bool fast_draw; /* Hides facesets/masks and forces smooth to save GPU bandwidth. */ + bool fast_draw; /* Hides facesets/masks and forces smooth to save GPU bandwidth. */ /* This is a fixed-size array so we can pass pointers to its elements * to client code. This is important to keep bmesh offsets up to date. diff --git a/source/blender/blenkernel/BKE_sculpt.h b/source/blender/blenkernel/BKE_sculpt.h index 710251fc2f4..dd7c057109d 100644 --- a/source/blender/blenkernel/BKE_sculpt.h +++ b/source/blender/blenkernel/BKE_sculpt.h @@ -1,7 +1,5 @@ #pragma once -#define SCULPT_TOOL_HAS_VCOL_BOUNDARY_SMOOTH(t) ELEM(t, SCULPT_TOOL_PAINT, SCULPT_TOOL_SMEAR) - /** When #BRUSH_ACCUMULATE is used */ #define SCULPT_TOOL_HAS_ACCUMULATE(t) \ ELEM(t, \ diff --git a/source/blender/blenkernel/intern/customdata.cc b/source/blender/blenkernel/intern/customdata.cc index eed98928a60..a88c06d4ea2 100644 --- a/source/blender/blenkernel/intern/customdata.cc +++ b/source/blender/blenkernel/intern/customdata.cc @@ -2258,7 +2258,7 @@ void CustomData_regen_active_refs(CustomData *data) } } - /* explicitly flag active layers */ + /* Explicitly flag active layers. */ for (i = 0, j = 0; i < data->totlayer; i++) { CustomDataLayer *layer = &data->layers[i]; CustomDataLayer *base = data->layers + data->typemap[layer->type]; @@ -2274,7 +2274,7 @@ void CustomData_regen_active_refs(CustomData *data) layer->active_rnd = n == base->active_rnd; } - /* handle case of base layers being active */ + /* Handle case of base layers being active. */ for (int i = 0; i < CD_NUMTYPES; i++) { if (data->typemap[i] == -1) { continue; @@ -2288,8 +2288,9 @@ void CustomData_regen_active_refs(CustomData *data) base->active_rnd = !base->active_rnd; } - /* regenerate active refs */ - /* set active n in base layer for all types */ + /* Regenerate active refs, + * set active n in base layer for all types. + */ for (i = 0; i < data->totlayer; i++) { CustomDataLayer *layer = data->layers + i; CustomDataLayer *base = data->layers + data->typemap[layer->type]; @@ -2331,7 +2332,7 @@ void CustomData_regen_active_refs(CustomData *data) } } -/* currently only used in BLI_assert */ +/* Currently only used in BLI_assert. */ #ifndef NDEBUG static bool customdata_typemap_is_valid(const CustomData *data) { @@ -2341,8 +2342,9 @@ static bool customdata_typemap_is_valid(const CustomData *data) } #endif -/* copies all customdata layers without allocating data, - * and without respect to type masks or NO_COPY/etc flags*/ +/* Copies all customdata layers without allocating data, + * and without respect to type masks or NO_COPY/etc flags. + */ void CustomData_copy_all_layout(const struct CustomData *source, struct CustomData *dest) { *dest = *source; diff --git a/source/blender/blenkernel/intern/paint.cc b/source/blender/blenkernel/intern/paint.cc index 0a9b4a7abb8..fcca87d4097 100644 --- a/source/blender/blenkernel/intern/paint.cc +++ b/source/blender/blenkernel/intern/paint.cc @@ -1518,7 +1518,6 @@ static void sculptsession_free_pbvh(Object *object) ss->preview_vert_count = 0; MEM_SAFE_FREE(ss->vertex_info.boundary); - MEM_SAFE_FREE(ss->vertex_info.symmetrize_map); MEM_SAFE_FREE(ss->fake_neighbors.fake_neighbor_index); } diff --git a/source/blender/editors/sculpt_paint/sculpt.cc b/source/blender/editors/sculpt_paint/sculpt.cc index 39c4bc78bd3..084b066b653 100644 --- a/source/blender/editors/sculpt_paint/sculpt.cc +++ b/source/blender/editors/sculpt_paint/sculpt.cc @@ -3927,7 +3927,6 @@ static void sculpt_topology_update(Sculpt *sd, /* Free index based vertex info as it will become invalid after modifying the topology during * the stroke. */ MEM_SAFE_FREE(ss->vertex_info.boundary); - MEM_SAFE_FREE(ss->vertex_info.symmetrize_map); PBVHTopologyUpdateMode mode = PBVHTopologyUpdateMode(0); float location[3]; diff --git a/source/blender/makesdna/DNA_brush_types.h b/source/blender/makesdna/DNA_brush_types.h index 24197304a60..638d78e59b4 100644 --- a/source/blender/makesdna/DNA_brush_types.h +++ b/source/blender/makesdna/DNA_brush_types.h @@ -322,9 +322,6 @@ typedef struct Brush { float topology_rake_projection; int topology_rake_spacing; // spacing for BRUSH_CUSTOM_TOPOLOGY_RAKE_SPACING - float vcol_boundary_factor; - float vcol_boundary_exponent; - float crease_pinch_factor; float normal_radius_factor; diff --git a/source/blender/makesrna/intern/rna_brush.c b/source/blender/makesrna/intern/rna_brush.c index 80f9dcf8640..08a18715f39 100644 --- a/source/blender/makesrna/intern/rna_brush.c +++ b/source/blender/makesrna/intern/rna_brush.c @@ -502,12 +502,6 @@ static bool rna_BrushCapabilitiesSculpt_has_topology_rake_get(PointerRNA *ptr) return SCULPT_TOOL_HAS_TOPOLOGY_RAKE(br->sculpt_tool); } -static bool rna_BrushCapabilitiesSculpt_has_vcol_boundary_smooth_get(PointerRNA *ptr) -{ - Brush *br = (Brush *)ptr->data; - return SCULPT_TOOL_HAS_VCOL_BOUNDARY_SMOOTH(br->sculpt_tool); -} - static bool rna_BrushCapabilitiesSculpt_has_auto_smooth_get(PointerRNA *ptr) { Brush *br = (Brush *)ptr->data; @@ -1476,7 +1470,6 @@ static void rna_def_sculpt_capabilities(BlenderRNA *brna) SCULPT_TOOL_CAPABILITY(has_accumulate, "Has Accumulate"); SCULPT_TOOL_CAPABILITY(has_auto_smooth, "Has Auto Smooth"); SCULPT_TOOL_CAPABILITY(has_topology_rake, "Has Topology Rake"); - SCULPT_TOOL_CAPABILITY(has_vcol_boundary_smooth, "Has VCol Boundary Smooth"); SCULPT_TOOL_CAPABILITY(has_height, "Has Height"); SCULPT_TOOL_CAPABILITY(has_jitter, "Has Jitter"); SCULPT_TOOL_CAPABILITY(has_normal_weight, "Has Crease/Pinch Factor"); @@ -3439,17 +3432,6 @@ static void rna_def_brush(BlenderRNA *brna) RNA_def_property_update(prop, 0, "rna_Brush_update"); - prop = RNA_def_property(srna, "vcol_boundary_factor", PROP_FLOAT, PROP_FACTOR); - RNA_def_property_float_sdna(prop, NULL, "vcol_boundary_factor"); - RNA_def_property_float_default(prop, 0); - RNA_def_property_range(prop, 0.0f, 1.0f); - RNA_def_property_ui_range(prop, 0.0f, 1.0f, 0.001, 3); - RNA_def_property_ui_text(prop, - "Boundary Hardening", - "Automatically align edges on color boundaries" - "to generate sharper features. "); - RNA_def_property_update(prop, 0, "rna_Brush_update"); - prop = RNA_def_property(srna, "boundary_smooth_factor", PROP_FLOAT, PROP_FACTOR); RNA_def_property_float_sdna(prop, NULL, "boundary_smooth_factor"); RNA_def_property_float_default(prop, 0); @@ -3466,14 +3448,6 @@ static void rna_def_brush(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Corner Pin", "How much to pin corners in hard edge mode."); RNA_def_property_update(prop, 0, "rna_Brush_update"); - prop = RNA_def_property(srna, "vcol_boundary_exponent", PROP_FLOAT, PROP_FACTOR); - RNA_def_property_float_sdna(prop, NULL, "vcol_boundary_exponent"); - RNA_def_property_float_default(prop, 0); - RNA_def_property_range(prop, 0.0f, 6.0f); - RNA_def_property_ui_range(prop, 0.1f, 3.0f, 0.001, 3); - RNA_def_property_ui_text(prop, "Exponent", "Hardening exponent (smaller value smoother edges)"); - RNA_def_property_update(prop, 0, "rna_Brush_update"); - prop = RNA_def_property(srna, "tilt_strength_factor", PROP_FLOAT, PROP_FACTOR); RNA_def_property_float_sdna(prop, NULL, "tilt_strength_factor"); RNA_def_property_float_default(prop, 0); -- 2.30.2 From 7ffab5395ef2ea61ddabb661da20a4a569fb49c7 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Tue, 30 May 2023 13:49:42 -0700 Subject: [PATCH 128/279] temp-sculpt-dyntopo: revert more unrelated code --- source/blender/blenkernel/BKE_pbvh.h | 18 +- .../blender/blenkernel/intern/curve_bevel.c | 2 +- source/blender/blenkernel/intern/dyntopo.cc | 1 - .../blenkernel/intern/mesh_legacy_convert.cc | 2 +- .../blender/blenkernel/intern/mesh_tangent.cc | 2 +- .../blenkernel/intern/mesh_validate.cc | 6 +- .../blenkernel/intern/object_update.cc | 2 - source/blender/blenkernel/intern/pbvh.cc | 4 +- .../blender/blenkernel/intern/pbvh_bmesh.cc | 45 ++- .../blenkernel/intern/pbvh_cache_test_main.c | 31 -- .../blender/blenkernel/intern/pbvh_intern.hh | 11 +- source/blender/blenlib/BLI_asan.h | 56 ---- source/blender/blenlib/BLI_linklist_stack.h | 2 +- source/blender/blenlib/BLI_memarena.h | 4 +- source/blender/blenlib/BLI_smallhash.h | 26 +- source/blender/blenlib/BLI_utildefines.h | 6 - source/blender/blenlib/intern/BLI_memarena.c | 11 - source/blender/blenlib/intern/path_util.c | 1 - source/blender/blenlib/intern/smallhash.c | 269 +++--------------- source/blender/blenlib/intern/task_pool.cc | 3 +- .../editors/sculpt_paint/sculpt_undo.cc | 145 ---------- .../makesrna/intern/rna_sculpt_paint.c | 5 - 22 files changed, 80 insertions(+), 572 deletions(-) delete mode 100644 source/blender/blenkernel/intern/pbvh_cache_test_main.c diff --git a/source/blender/blenkernel/BKE_pbvh.h b/source/blender/blenkernel/BKE_pbvh.h index 02dd83ce1c9..bb5af8d0a48 100644 --- a/source/blender/blenkernel/BKE_pbvh.h +++ b/source/blender/blenkernel/BKE_pbvh.h @@ -26,24 +26,12 @@ #include -//#define DEFRAGMENT_MEMORY - #include "DNA_customdata_types.h" #ifdef __cplusplus extern "C" { #endif -#if 0 -typedef struct SculptLoopRef { - intptr_t i; -} SculptLoopRef; -#endif - -#ifdef DEFRAGMENT_MEMORY -# include "BLI_smallhash.h" -#endif - struct BMesh; struct BMVert; struct BMEdge; @@ -153,7 +141,11 @@ typedef struct PBVHTriBuf { int totvert, totedge, tottri; int verts_size, edges_size, tris_size; - SmallHash vertmap; // maps vertex ptrs to indices within verts +#ifdef __cplusplus + blender::Map vertmap; +#else + void *vertmap; +#endif // private field intptr_t *loops; diff --git a/source/blender/blenkernel/intern/curve_bevel.c b/source/blender/blenkernel/intern/curve_bevel.c index 5a350de9512..6f32f0f5e6f 100644 --- a/source/blender/blenkernel/intern/curve_bevel.c +++ b/source/blender/blenkernel/intern/curve_bevel.c @@ -86,7 +86,7 @@ static void curve_bevel_make_extrude_and_fill(const Curve *cu, * for #Curve.bevresol is 32. */ float *quarter_coords_x = alloca(sizeof(float) * (cu->bevresol + 1)); float *quarter_coords_y = alloca(sizeof(float) * (cu->bevresol + 1)); - bevel_quarter_fill((Curve *)cu, quarter_coords_x, quarter_coords_y); + bevel_quarter_fill(cu, quarter_coords_x, quarter_coords_y); int nr; if (fill_type == FULL) { diff --git a/source/blender/blenkernel/intern/dyntopo.cc b/source/blender/blenkernel/intern/dyntopo.cc index 41b4421e84e..b6b3855b526 100644 --- a/source/blender/blenkernel/intern/dyntopo.cc +++ b/source/blender/blenkernel/intern/dyntopo.cc @@ -25,7 +25,6 @@ #include "BLI_rand.h" #include "BLI_rand.hh" #include "BLI_set.hh" -#include "BLI_smallhash.h" #include "BLI_task.h" #include "BLI_task.hh" #include "BLI_utildefines.h" diff --git a/source/blender/blenkernel/intern/mesh_legacy_convert.cc b/source/blender/blenkernel/intern/mesh_legacy_convert.cc index b2823cdf738..73ea753061f 100644 --- a/source/blender/blenkernel/intern/mesh_legacy_convert.cc +++ b/source/blender/blenkernel/intern/mesh_legacy_convert.cc @@ -726,7 +726,7 @@ void BKE_mesh_convert_mfaces_to_mpolys(Mesh *mesh) * used when creating pdata and ldata for pre-bmesh * meshes and needed to preserve active/render/clone/stencil flags set in pre-bmesh files. */ -void CustomData_bmesh_do_versions_update_active_layers(CustomData *fdata, CustomData *ldata) +static void CustomData_bmesh_do_versions_update_active_layers(CustomData *fdata, CustomData *ldata) { int act; diff --git a/source/blender/blenkernel/intern/mesh_tangent.cc b/source/blender/blenkernel/intern/mesh_tangent.cc index c6d62c1cbe1..025ab04ac15 100644 --- a/source/blender/blenkernel/intern/mesh_tangent.cc +++ b/source/blender/blenkernel/intern/mesh_tangent.cc @@ -25,7 +25,7 @@ #include "BKE_mesh_tangent.h" #include "BKE_report.h" -//#include "BLI_strict_flags.h" +#include "BLI_strict_flags.h" #include "atomic_ops.h" #include "mikktspace.hh" diff --git a/source/blender/blenkernel/intern/mesh_validate.cc b/source/blender/blenkernel/intern/mesh_validate.cc index e5a8afd4381..a3fc11f3441 100644 --- a/source/blender/blenkernel/intern/mesh_validate.cc +++ b/source/blender/blenkernel/intern/mesh_validate.cc @@ -696,11 +696,7 @@ bool BKE_mesh_validate_arrays(Mesh *mesh, if ((p1_nv == p2_nv) && (memcmp(p1_v, p2_v, p1_nv * sizeof(*p1_v)) == 0)) { if (do_verbose) { /* TODO: convert list to string */ - PRINT_ERR("\tPolys %u(len=%d) and %u use same vertices (%d", - prev_sp->index, - p1_nv, - sp->index, - *p1_v); + PRINT_ERR("\tPolys %u and %u use same vertices (%d", prev_sp->index, sp->index, *p1_v); for (j = 1; j < p1_nv; j++) { PRINT_ERR(", %d", p1_v[j]); } diff --git a/source/blender/blenkernel/intern/object_update.cc b/source/blender/blenkernel/intern/object_update.cc index aa7fd83afe6..e4a14b9afe4 100644 --- a/source/blender/blenkernel/intern/object_update.cc +++ b/source/blender/blenkernel/intern/object_update.cc @@ -265,8 +265,6 @@ void BKE_object_sync_to_original(Depsgraph *depsgraph, Object *object) object_orig->transflag = object->transflag; object_orig->flag = object->flag; - // object_orig->cached_pbvh2 = object->cached_pbvh2; - /* Copy back error messages from modifiers. */ for (ModifierData *md = static_cast(object->modifiers.first), *md_orig = static_cast(object_orig->modifiers.first); diff --git a/source/blender/blenkernel/intern/pbvh.cc b/source/blender/blenkernel/intern/pbvh.cc index eac66dcecb7..7d18d7172f3 100644 --- a/source/blender/blenkernel/intern/pbvh.cc +++ b/source/blender/blenkernel/intern/pbvh.cc @@ -938,8 +938,8 @@ static void pbvh_draw_args_init(PBVH *pbvh, PBVH_GPU_Args *args, PBVHNode *node) args->cd_mask_layer = CustomData_get_offset(&pbvh->header.bm->vdata, CD_PAINT_MASK); args->tribuf = node->tribuf; - args->tri_buffers = node->tri_buffers; - args->tot_tri_buffers = node->tot_tri_buffers; + args->tri_buffers = node->tri_buffers->data(); + args->tot_tri_buffers = node->tri_buffers->size(); args->show_orig = pbvh->show_orig; break; diff --git a/source/blender/blenkernel/intern/pbvh_bmesh.cc b/source/blender/blenkernel/intern/pbvh_bmesh.cc index 89b7d106b11..fb54b84f373 100644 --- a/source/blender/blenkernel/intern/pbvh_bmesh.cc +++ b/source/blender/blenkernel/intern/pbvh_bmesh.cc @@ -2655,8 +2655,6 @@ static void pbvh_free_tribuf(PBVHTriBuf *tribuf) MEM_SAFE_FREE(tribuf->loops); MEM_SAFE_FREE(tribuf->edges); - BLI_smallhash_release(&tribuf->vertmap); - tribuf->verts = nullptr; tribuf->tris = nullptr; tribuf->loops = nullptr; @@ -2680,19 +2678,17 @@ void BKE_pbvh_bmesh_free_tris(PBVH * /*pbvh*/, PBVHNode *node) { if (node->tribuf) { pbvh_free_tribuf(node->tribuf); - MEM_freeN(node->tribuf); + MEM_delete(node->tribuf); node->tribuf = nullptr; } if (node->tri_buffers) { - for (int i = 0; i < node->tot_tri_buffers; i++) { - pbvh_free_tribuf(node->tri_buffers + i); + for (PBVHTriBuf &tri : *node->tri_buffers) { + pbvh_free_tribuf(&tri); } - MEM_SAFE_FREE(node->tri_buffers); - + MEM_delete>(node->tri_buffers); node->tri_buffers = nullptr; - node->tot_tri_buffers = 0; } } @@ -2809,8 +2805,6 @@ static void pbvh_init_tribuf(PBVHNode *node, PBVHTriBuf *tribuf) tribuf->verts = nullptr; tribuf->tris = nullptr; tribuf->loops = nullptr; - - BLI_smallhash_init_ex(&tribuf->vertmap, node->bm_unique_verts->length); } static uintptr_t tri_loopkey(BMLoop *l, int mat_nr, int cd_fset, int cd_uvs[], int totuv) @@ -2869,7 +2863,7 @@ bool BKE_pbvh_bmesh_check_tris(PBVH *pbvh, PBVHNode *node) BKE_pbvh_bmesh_free_tris(pbvh, node); } - node->tribuf = MEM_cnew("node->tribuf"); + node->tribuf = MEM_new("node->tribuf"); pbvh_init_tribuf(node, node->tribuf); Vector loops; @@ -2939,25 +2933,20 @@ bool BKE_pbvh_bmesh_check_tris(PBVH *pbvh, PBVHNode *node) uintptr_t loopkey = tri_loopkey(l, mat_nr, pbvh->cd_faceset_offset, cd_uvs, totuv); - if (!BLI_smallhash_ensure_p(&node->tribuf->vertmap, loopkey, &val)) { + bool existed = node->tribuf->vertmap.add(loopkey, node->tribuf->totvert); + if (!existed) { PBVHVertRef sv = {(intptr_t)l->v}; - minmax_v3v3_v3(min, max, l->v->co); - - *val = POINTER_FROM_INT(node->tribuf->totvert); pbvh_tribuf_add_vert(node->tribuf, sv, l); } tri->v[j] = (intptr_t)val[0]; tri->l[j] = (intptr_t)l; - val = nullptr; - if (!BLI_smallhash_ensure_p(&mat_tribuf->vertmap, loopkey, &val)) { + existed = mat_tribuf->vertmap.add(loopkey, mat_tribuf->totvert); + if (!existed) { PBVHVertRef sv = {(intptr_t)l->v}; - minmax_v3v3_v3(min, max, l->v->co); - - *val = POINTER_FROM_INT(mat_tribuf->totvert); pbvh_tribuf_add_vert(mat_tribuf, sv, l); } @@ -3037,13 +3026,13 @@ bool BKE_pbvh_bmesh_check_tris(PBVH *pbvh, PBVHNode *node) l->e->head.hflag |= edgeflag; - int v1 = POINTER_AS_INT(BLI_smallhash_lookup(&node->tribuf->vertmap, (uintptr_t)l->e->v1)); - int v2 = POINTER_AS_INT(BLI_smallhash_lookup(&node->tribuf->vertmap, (uintptr_t)l->e->v2)); + int v1 = node->tribuf->vertmap.lookup_default((uintptr_t)l->e->v1, 0); + int v2 = node->tribuf->vertmap.lookup_default((uintptr_t)l->e->v2, 0); pbvh_tribuf_add_edge(node->tribuf, v1, v2); - v1 = POINTER_AS_INT(BLI_smallhash_lookup(&mat_tribuf->vertmap, (uintptr_t)l->e->v1)); - v2 = POINTER_AS_INT(BLI_smallhash_lookup(&mat_tribuf->vertmap, (uintptr_t)l->e->v2)); + v1 = mat_tribuf->vertmap.lookup_default((uintptr_t)l->e->v1, 0); + v2 = mat_tribuf->vertmap.lookup_default((uintptr_t)l->e->v2, 0); pbvh_tribuf_add_edge(mat_tribuf, v1, v2); } while ((l = l->next) != f->l_first); @@ -3052,8 +3041,7 @@ bool BKE_pbvh_bmesh_check_tris(PBVH *pbvh, PBVHNode *node) bm->elem_index_dirty |= BM_VERT; - node->tri_buffers = c_array_from_vector(tribufs); - node->tot_tri_buffers = tribufs.size(); + node->tri_buffers = new Vector(tribufs); if (node->tribuf->totvert) { copy_v3_v3(node->tribuf->min, min); @@ -4085,8 +4073,9 @@ void BKE_pbvh_bmesh_from_saved_indices(PBVH *pbvh) continue; } - for (j = 0; j < node->tot_tri_buffers + 1; j++) { - PBVHTriBuf *tribuf = j == node->tot_tri_buffers ? node->tribuf : node->tri_buffers + j; + for (j = 0; j < node->tri_buffers->size() + 1; j++) { + PBVHTriBuf *tribuf = j == node->tri_buffers->size() ? node->tribuf : + &((*node->tri_buffers)[j]); if (!tribuf) { break; diff --git a/source/blender/blenkernel/intern/pbvh_cache_test_main.c b/source/blender/blenkernel/intern/pbvh_cache_test_main.c deleted file mode 100644 index a01ab8a4453..00000000000 --- a/source/blender/blenkernel/intern/pbvh_cache_test_main.c +++ /dev/null @@ -1,31 +0,0 @@ -#include "MEM_guardedalloc.h" - -#include "BLI_compiler_compat.h" -#include "BLI_math.h" -#include "BLI_utildefines.h" - -#include "BKE_customdata.h" -#include "BKE_pbvh.h" - -#include "DNA_customdata_types.h" -#include "DNA_mesh_types.h" -#include "DNA_meshdata_types.h" - -#include "bmesh.h" -#include "pbvh_intern.h" - -#include -#include - -#include - -// void pbvh_bmesh_do_cache_test(void); - -int main(int argc, char **argv) -{ - printf("argc: %d\n", argc); - - // pbvh_bmesh_do_cache_test(); - - return 0; -} diff --git a/source/blender/blenkernel/intern/pbvh_intern.hh b/source/blender/blenkernel/intern/pbvh_intern.hh index 0b40c0addeb..f98aba37ead 100644 --- a/source/blender/blenkernel/intern/pbvh_intern.hh +++ b/source/blender/blenkernel/intern/pbvh_intern.hh @@ -2,12 +2,13 @@ #pragma once +#include "BLI_array.hh" #include "BLI_compiler_compat.h" #include "BLI_ghash.h" #include "BLI_math_vector_types.hh" +#include "BLI_offset_indices.hh" #include "BLI_span.hh" #include "BLI_vector.hh" -#include "BLI_offset_indices.hh" #include "DNA_customdata_types.h" #include "DNA_material_types.h" @@ -131,9 +132,8 @@ struct PBVHNode { TableGSet *bm_unique_verts; TableGSet *bm_other_verts; - PBVHTriBuf *tribuf; // all triangles - PBVHTriBuf *tri_buffers; // tribuffers, one per material used - int tot_tri_buffers; + PBVHTriBuf *tribuf; // all triangles + blender::Vector *tri_buffers; int updategen; @@ -304,7 +304,7 @@ struct PBVH { void BB_reset(BB *bb); void BB_zero(BB *bb); - /** +/** * Expand the bounding box to include a new coordinate. */ void BB_expand(BB *bb, const float co[3]); @@ -466,7 +466,6 @@ BLI_INLINE bool pbvh_check_vert_boundary(PBVH *pbvh, struct BMVert *v) } void pbvh_bmesh_check_other_verts(PBVHNode *node); -//#define DEFRAGMENT_MEMORY void pbvh_bmesh_normals_update(PBVH *pbvh, blender::Span nodes); /* pbvh_pixels.hh */ diff --git a/source/blender/blenlib/BLI_asan.h b/source/blender/blenlib/BLI_asan.h index ca18d14b225..a8d04a84625 100644 --- a/source/blender/blenlib/BLI_asan.h +++ b/source/blender/blenlib/BLI_asan.h @@ -28,59 +28,3 @@ * Mark a region of memory as usable again. */ #define BLI_asan_unpoison(addr, size) ASAN_UNPOISON_MEMORY_REGION(addr, size) - -#if (defined(__SANITIZE_ADDRESS__) || __has_feature(address_sanitizer)) -# include "MEM_guardedalloc.h" - -static void *BLI_asan_safe_malloc(size_t size, const char *tag) -{ - // align size at 16 bytes - size += 15 - (size & 15); - - // add safe padding - size += 32; - - void *ret = MEM_mallocN(size, tag); - - int *iptr = (int *)ret; - *iptr = (int)size; - - char *ptr = (char *)ret; - - ptr[4] = 't'; - ptr[5] = 'a'; - ptr[6] = 'g'; - ptr[7] = '1'; - - BLI_asan_poison(ptr, 16); - BLI_asan_poison(ptr + size - 16, 16); - - ret = (void *)(ptr + 16); - - return ret; -} - -static void BLI_asan_safe_free(void *mem) -{ - if (!mem) { - return; - } - - mem = (void *)(((char *)mem) - 16); - - BLI_asan_unpoison(mem, 16); - int *iptr = (int *)mem; - volatile char *ptr = (char *)mem; - - if (ptr[4] != 't' || ptr[5] != 'a' || ptr[6] != 'g' || ptr[7] != '1') { - BLI_asan_poison(mem, 16); - *ptr = 1; // deliberately trigger asan fault - } - - BLI_asan_unpoison(ptr + iptr[0] - 16, 16); - MEM_freeN((void *)ptr); -} -#else -# define BLI_asan_safe_malloc(size, tag) MEM_mallocN(size, tag) -# define BLI_asan_safe_free(mem) MEM_SAFE_FREE(mem) -#endif diff --git a/source/blender/blenlib/BLI_linklist_stack.h b/source/blender/blenlib/BLI_linklist_stack.h index 27464d0058a..63878d5a61f 100644 --- a/source/blender/blenlib/BLI_linklist_stack.h +++ b/source/blender/blenlib/BLI_linklist_stack.h @@ -36,7 +36,7 @@ #define BLI_LINKSTACK_SIZE(var) BLI_mempool_len(var##_pool_) /* check for typeof() */ -#if defined(__GNUC__) || defined(__clang__) +#ifdef __GNUC__ # define BLI_LINKSTACK_PUSH(var, ptr) \ (CHECK_TYPE_INLINE_NONCONST(ptr, typeof(var##_type_)), \ BLI_linklist_prepend_pool(&(var), ptr, var##_pool_)) diff --git a/source/blender/blenlib/BLI_memarena.h b/source/blender/blenlib/BLI_memarena.h index 388128ccdf6..2c9e4c4700c 100644 --- a/source/blender/blenlib/BLI_memarena.h +++ b/source/blender/blenlib/BLI_memarena.h @@ -22,9 +22,7 @@ extern "C" { struct MemArena; typedef struct MemArena MemArena; -int BLI_memarena_size(const struct MemArena *ma); - -struct MemArena *BLI_memarena_new(const size_t bufsize, +struct MemArena *BLI_memarena_new(size_t bufsize, const char *name) ATTR_WARN_UNUSED_RESULT ATTR_RETURNS_NONNULL ATTR_NONNULL(2) ATTR_MALLOC; void BLI_memarena_free(struct MemArena *ma) ATTR_NONNULL(1); diff --git a/source/blender/blenlib/BLI_smallhash.h b/source/blender/blenlib/BLI_smallhash.h index d545053a0fc..e7022784d31 100644 --- a/source/blender/blenlib/BLI_smallhash.h +++ b/source/blender/blenlib/BLI_smallhash.h @@ -25,14 +25,11 @@ typedef struct { #define SMSTACKSIZE 131 typedef struct SmallHash { unsigned int nbuckets; - unsigned int nentries, nfreecells; + unsigned int nentries; unsigned int cursize; SmallHashEntry *buckets; SmallHashEntry buckets_stack[SMSTACKSIZE]; - - bool use_pointer_hash; - bool using_stack; } SmallHash; typedef struct { @@ -40,9 +37,7 @@ typedef struct { unsigned int i; } SmallHashIter; -int BLI_smallhash_memuse(SmallHash *sh); - -void BLI_smallhash_init_ex(SmallHash *sh, const unsigned int nentries_reserve) ATTR_NONNULL(1); +void BLI_smallhash_init_ex(SmallHash *sh, unsigned int nentries_reserve) ATTR_NONNULL(1); void BLI_smallhash_init(SmallHash *sh) ATTR_NONNULL(1); /** * \note does *not* free *sh itself! only the direct data! @@ -58,25 +53,22 @@ void BLI_smallhash_insert(SmallHash *sh, uintptr_t key, void *item) ATTR_NONNULL */ bool BLI_smallhash_reinsert(SmallHash *sh, uintptr_t key, void *item) ATTR_NONNULL(1); bool BLI_smallhash_remove(SmallHash *sh, uintptr_t key) ATTR_NONNULL(1); -void *BLI_smallhash_lookup(SmallHash *sh, uintptr_t key) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT; -void **BLI_smallhash_lookup_p(SmallHash *sh, uintptr_t key) +void *BLI_smallhash_lookup(const SmallHash *sh, uintptr_t key) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT; -bool BLI_smallhash_haskey(SmallHash *sh, uintptr_t key) ATTR_NONNULL(1); -int BLI_smallhash_len(SmallHash *sh) ATTR_NONNULL(1); +void **BLI_smallhash_lookup_p(const SmallHash *sh, uintptr_t key) + ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT; +bool BLI_smallhash_haskey(const SmallHash *sh, uintptr_t key) ATTR_NONNULL(1); +int BLI_smallhash_len(const SmallHash *sh) ATTR_NONNULL(1); void *BLI_smallhash_iternext(SmallHashIter *iter, uintptr_t *key) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT; void **BLI_smallhash_iternext_p(SmallHashIter *iter, uintptr_t *key) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT; -void *BLI_smallhash_iternew(SmallHash *sh, SmallHashIter *iter, uintptr_t *key) +void *BLI_smallhash_iternew(const SmallHash *sh, SmallHashIter *iter, uintptr_t *key) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT; -void **BLI_smallhash_iternew_p(SmallHash *sh, SmallHashIter *iter, uintptr_t *key) +void **BLI_smallhash_iternew_p(const SmallHash *sh, SmallHashIter *iter, uintptr_t *key) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT; /* void BLI_smallhash_print(SmallHash *sh); */ /* UNUSED */ -void BLI_smallhash_clear(SmallHash *sh, uintptr_t key); -bool BLI_smallhash_ensure_p(SmallHash *sh, uintptr_t key, void ***item); -bool BLI_smallhash_remove_p(SmallHash *sh, uintptr_t key, void **val); - #ifdef DEBUG /** * Measure how well the hash function performs diff --git a/source/blender/blenlib/BLI_utildefines.h b/source/blender/blenlib/BLI_utildefines.h index d1370a8db2b..ee5740107d8 100644 --- a/source/blender/blenlib/BLI_utildefines.h +++ b/source/blender/blenlib/BLI_utildefines.h @@ -286,12 +286,6 @@ extern "C" { (_VA_ELEM15(v, a, b, c, d, e, f, g, h, i, j, k, l, m, n) || _VA_ELEM2(v, o)) #define _VA_ELEM17(v, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p) \ (_VA_ELEM16(v, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o) || _VA_ELEM2(v, p)) -#define _VA_ELEM18(v, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q) \ - (_VA_ELEM17(v, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p) || _VA_ELEM2(v, q)) -#define _VA_ELEM19(v, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r) \ - (_VA_ELEM18(v, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q) || _VA_ELEM2(v, r)) -#define _VA_ELEM20(v, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s) \ - (_VA_ELEM19(v, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r) || _VA_ELEM2(v, s)) /* clang-format on */ /* reusable ELEM macro */ diff --git a/source/blender/blenlib/intern/BLI_memarena.c b/source/blender/blenlib/intern/BLI_memarena.c index aee24173fa1..3c353a1c8c8 100644 --- a/source/blender/blenlib/intern/BLI_memarena.c +++ b/source/blender/blenlib/intern/BLI_memarena.c @@ -48,17 +48,6 @@ struct MemArena { bool use_calloc; }; -int BLI_memarena_size(const struct MemArena *ma) -{ - int ret = 0; - - for (struct MemBuf *buf = ma->bufs; buf; buf = buf->next) { - ret += (int)ma->bufsize; - } - - return ret; -} - static void memarena_buf_free_all(struct MemBuf *mb) { while (mb != NULL) { diff --git a/source/blender/blenlib/intern/path_util.c b/source/blender/blenlib/intern/path_util.c index b84cc51d6a5..14fa20573f1 100644 --- a/source/blender/blenlib/intern/path_util.c +++ b/source/blender/blenlib/intern/path_util.c @@ -1275,7 +1275,6 @@ bool BLI_path_program_extensions_add_win32(char *program_name, const size_t prog if (type && (!S_ISDIR(type))) { retval = true; BLI_strncpy(program_name, filename, program_name_maxncpy); - break; } } diff --git a/source/blender/blenlib/intern/smallhash.c b/source/blender/blenlib/intern/smallhash.c index d54b905268a..64e9f0183ed 100644 --- a/source/blender/blenlib/intern/smallhash.c +++ b/source/blender/blenlib/intern/smallhash.c @@ -1,16 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-or-later * Copyright 2008 Blender Foundation */ -#ifdef __GNUC__ -/* I can't even *cast* signed ints in gcc's sign-conversion warning? gcc 10.3.0 -joeedh */ -# pragma GCC diagnostic ignored "-Wsign-conversion" -#endif - -#ifdef __GNUC__ -/* I can't even *cast* signed ints in gcc's sign-conversion warning? gcc 10.3.0 -joeedh */ -# pragma GCC diagnostic ignored "-Wsign-conversion" -#endif - /** \file * \ingroup bli * @@ -49,46 +39,20 @@ #include "BLI_smallhash.h" -#include "BLI_asan.h" #include "BLI_strict_flags.h" -/* NOTE: copied from BLO_blend_defs.h, don't use here because we're in BLI. */ -#ifdef __BIG_ENDIAN__ -/* Big Endian */ -# define MAKE_ID(a, b, c, d) ((int)(a) << 24 | (int)(b) << 16 | (c) << 8 | (d)) -# define MAKE_ID_8(a, b, c, d, e, f, g, h) \ - ((int64_t)(a) << 56 | (int64_t)(b) << 48 | (int64_t)(c) << 40 | (int64_t)(d) << 32 | \ - (int64_t)(e) << 24 | (int64_t)(f) << 16 | (int64_t)(g) << 8 | (h)) -#else -/* Little Endian */ -# define MAKE_ID(a, b, c, d) ((int)(d) << 24 | (int)(c) << 16 | (b) << 8 | (a)) -# define MAKE_ID_8(a, b, c, d, e, f, g, h) \ - ((int64_t)(h) << 56 | (int64_t)(g) << 48 | (int64_t)(f) << 40 | (int64_t)(e) << 32 | \ - (int64_t)(d) << 24 | (int64_t)(c) << 16 | (int64_t)(b) << 8 | (a)) -#endif - -#define SMHASH_KEY_UNUSED (uintptr_t)(MAKE_ID_8('s', 'm', 'h', 'k', 'u', 'n', 'u', 's')) -#define SMHASH_CELL_FREE (void *)(MAKE_ID_8('s', 'm', 'h', 'c', 'f', 'r', 'e', 'e')) -#define SMHASH_CELL_UNUSED (void *)(MAKE_ID_8('s', 'm', 'h', 'c', 'u', 'n', 'u', 's')) - -#define USE_REMOVE +#define SMHASH_KEY_UNUSED ((uintptr_t)(UINTPTR_MAX - 0)) +#define SMHASH_CELL_FREE ((void *)(UINTPTR_MAX - 1)) +#define SMHASH_CELL_UNUSED ((void *)(UINTPTR_MAX - 2)) /* typically this re-assigns 'h' */ #define SMHASH_NEXT(h, hoff) \ - (CHECK_TYPE_INLINE(&(h), uintptr_t *), \ - CHECK_TYPE_INLINE(&(hoff), uintptr_t *), \ - ((h) + (((hoff) = ((hoff)*3) + 1), (hoff)))) + (CHECK_TYPE_INLINE(&(h), uint *), \ + CHECK_TYPE_INLINE(&(hoff), uint *), \ + ((h) + (((hoff) = ((hoff)*2) + 1), (hoff)))) -BLI_INLINE bool check_stack_move(SmallHash *sh) -{ - if (sh->using_stack && sh->buckets != sh->buckets_stack) { - sh->buckets = sh->buckets_stack; - - return true; - } - - return false; -} +/* nothing uses BLI_smallhash_remove yet */ +// #define USE_REMOVE BLI_INLINE bool smallhash_val_is_used(const void *val) { @@ -102,45 +66,18 @@ BLI_INLINE bool smallhash_val_is_used(const void *val) extern const uint BLI_ghash_hash_sizes[]; #define hashsizes BLI_ghash_hash_sizes -int BLI_smallhash_memuse(SmallHash *sh) +BLI_INLINE uint smallhash_key(const uintptr_t key) { - return (int)sh->nbuckets * (int)sizeof(SmallHashEntry) + (int)sizeof(SmallHash); + return (uint)key; } -#if 0 -BLI_INLINE uintptr_t smallhash_key(const uintptr_t key) -{ -# if 1 - return key; -# else - uintptr_t y = (size_t)key; - /* bottom 3 or 4 bits are likely to be 0; rotate y by 4 to avoid - * excessive hash collisions for dicts and sets */ - - return (uintptr_t)(y >> 4) | ((uintptr_t)y << (sizeof(uintptr_t[8]) - 4)); -# endif -} -#endif - -#ifdef _keyrot -# undef _keyrot -#endif - -#define _keyrot(y) ((uintptr_t)(y) >> 4) | ((uintptr_t)(y) << (sizeof(uintptr_t[8]) - 4)) -#define smallhash_key(key) sh->use_pointer_hash ? _keyrot(key) : (key) /** * Check if the number of items in the smallhash is large enough to require more buckets. */ -BLI_INLINE bool smallhash_test_expand_buckets(const uint nentries, - const uint nbuckets, - const uint nfreecells) +BLI_INLINE bool smallhash_test_expand_buckets(const uint nentries, const uint nbuckets) { - if (nfreecells < 3) { - return true; - } - /* (approx * 1.5) */ - return (nentries + (nentries >> 1)) > nbuckets || nfreecells < 3; + return (nentries + (nentries >> 1)) > nbuckets; } BLI_INLINE void smallhash_init_empty(SmallHash *sh) @@ -158,26 +95,23 @@ BLI_INLINE void smallhash_init_empty(SmallHash *sh) */ BLI_INLINE void smallhash_buckets_reserve(SmallHash *sh, const uint nentries_reserve) { - while (smallhash_test_expand_buckets(nentries_reserve, sh->nbuckets, sh->nbuckets + 5)) { + while (smallhash_test_expand_buckets(nentries_reserve, sh->nbuckets)) { sh->nbuckets = hashsizes[++sh->cursize]; - sh->nfreecells = sh->nbuckets; } } -BLI_INLINE SmallHashEntry *smallhash_lookup(SmallHash *sh, const uintptr_t key) +BLI_INLINE SmallHashEntry *smallhash_lookup(const SmallHash *sh, const uintptr_t key) { - check_stack_move(sh); - SmallHashEntry *e; - uintptr_t h = smallhash_key(key); - uintptr_t hoff = 1; + uint h = smallhash_key(key); + uint hoff = 1; BLI_assert(key != SMHASH_KEY_UNUSED); /* NOTE: there are always more buckets than entries, * so we know there will always be a free bucket if the key isn't found. */ - for (e = &sh->buckets[h % (uintptr_t)sh->nbuckets]; e->val != SMHASH_CELL_FREE; - h = SMHASH_NEXT(h, hoff), e = &sh->buckets[h % (uintptr_t)sh->nbuckets]) + for (e = &sh->buckets[h % sh->nbuckets]; e->val != SMHASH_CELL_FREE; + h = SMHASH_NEXT(h, hoff), e = &sh->buckets[h % sh->nbuckets]) { if (e->key == key) { /* should never happen because unused keys are zero'd */ @@ -191,14 +125,12 @@ BLI_INLINE SmallHashEntry *smallhash_lookup(SmallHash *sh, const uintptr_t key) BLI_INLINE SmallHashEntry *smallhash_lookup_first_free(SmallHash *sh, const uintptr_t key) { - check_stack_move(sh); - SmallHashEntry *e; - uintptr_t h = smallhash_key(key); - uintptr_t hoff = 1; + uint h = smallhash_key(key); + uint hoff = 1; - for (e = &sh->buckets[h % (uintptr_t)sh->nbuckets]; smallhash_val_is_used(e->val); - h = SMHASH_NEXT(h, hoff), e = &sh->buckets[h % (uintptr_t)sh->nbuckets]) + for (e = &sh->buckets[h % sh->nbuckets]; smallhash_val_is_used(e->val); + h = SMHASH_NEXT(h, hoff), e = &sh->buckets[h % sh->nbuckets]) { /* pass */ } @@ -208,8 +140,6 @@ BLI_INLINE SmallHashEntry *smallhash_lookup_first_free(SmallHash *sh, const uint BLI_INLINE void smallhash_resize_buckets(SmallHash *sh, const uint nbuckets) { - check_stack_move(sh); - SmallHashEntry *buckets_old = sh->buckets; const uint nbuckets_old = sh->nbuckets; const bool was_alloc = (buckets_old != sh->buckets_stack); @@ -225,28 +155,21 @@ BLI_INLINE void smallhash_resize_buckets(SmallHash *sh, const uint nbuckets) } else { sh->buckets = MEM_mallocN(sizeof(*sh->buckets) * nbuckets, __func__); - sh->using_stack = false; } sh->nbuckets = nbuckets; - sh->nfreecells = nbuckets; - sh->nentries = 0; smallhash_init_empty(sh); for (i = 0; i < nbuckets_old; i++) { if (smallhash_val_is_used(buckets_old[i].val)) { SmallHashEntry *e = smallhash_lookup_first_free(sh, buckets_old[i].key); - e->key = buckets_old[i].key; e->val = buckets_old[i].val; - - sh->nfreecells--; - sh->nentries++; } } - if (was_alloc && buckets_old) { + if (was_alloc) { MEM_freeN(buckets_old); } } @@ -257,9 +180,7 @@ void BLI_smallhash_init_ex(SmallHash *sh, const uint nentries_reserve) sh->nentries = 0; sh->cursize = 2; - sh->using_stack = true; sh->nbuckets = hashsizes[sh->cursize]; - sh->nfreecells = sh->nbuckets; sh->buckets = sh->buckets_stack; @@ -268,8 +189,6 @@ void BLI_smallhash_init_ex(SmallHash *sh, const uint nentries_reserve) if (sh->nbuckets > SMSTACKSIZE) { sh->buckets = MEM_mallocN(sizeof(*sh->buckets) * sh->nbuckets, __func__); - - sh->using_stack = false; } } @@ -283,86 +202,24 @@ void BLI_smallhash_init(SmallHash *sh) void BLI_smallhash_release(SmallHash *sh) { - check_stack_move(sh); - - if (sh->buckets && sh->buckets != sh->buckets_stack) { + if (sh->buckets != sh->buckets_stack) { MEM_freeN(sh->buckets); } } -bool BLI_smallhash_ensure_p(SmallHash *sh, uintptr_t key, void ***item) -{ - check_stack_move(sh); - - SmallHashEntry *e = NULL; - uintptr_t h = smallhash_key(key); - uintptr_t hoff = 1; - - if (UNLIKELY(smallhash_test_expand_buckets(sh->nentries, sh->nbuckets, sh->nfreecells))) { - smallhash_resize_buckets(sh, hashsizes[++sh->cursize]); - } - - BLI_assert(key != SMHASH_KEY_UNUSED); - - /* NOTE: there are always more buckets than entries, - * so we know there will always be a free bucket if the key isn't found. */ - for (e = &sh->buckets[h % (uintptr_t)sh->nbuckets]; e->val != SMHASH_CELL_FREE; - h = SMHASH_NEXT(h, hoff), e = &sh->buckets[h % (uintptr_t)sh->nbuckets]) - { - if (e->key == key) { - /* should never happen because unused keys are zero'd */ - BLI_assert(e->val != SMHASH_CELL_UNUSED); - break; - } - } - - bool ret; - - if (e->val == SMHASH_CELL_FREE || e->val == SMHASH_CELL_UNUSED) { - sh->nentries++; - - if (e->val == SMHASH_CELL_FREE) { - sh->nfreecells--; - } - - ret = false; - e->val = NULL; - } - else { - ret = true; - } - - e->key = key; - - *item = &e->val; - - return ret; -} - void BLI_smallhash_insert(SmallHash *sh, uintptr_t key, void *item) { - check_stack_move(sh); - SmallHashEntry *e; BLI_assert(key != SMHASH_KEY_UNUSED); BLI_assert(smallhash_val_is_used(item)); BLI_assert(BLI_smallhash_haskey(sh, key) == false); - if (UNLIKELY(smallhash_test_expand_buckets(sh->nentries, sh->nbuckets, sh->nfreecells))) { + if (UNLIKELY(smallhash_test_expand_buckets(++sh->nentries, sh->nbuckets))) { smallhash_resize_buckets(sh, hashsizes[++sh->cursize]); } e = smallhash_lookup_first_free(sh, key); - - if (e->val == SMHASH_CELL_FREE) { - sh->nentries++; - sh->nfreecells--; - } - else if (e->val == SMHASH_CELL_UNUSED) { - sh->nentries++; - } - e->key = key; e->val = item; } @@ -381,48 +238,10 @@ bool BLI_smallhash_reinsert(SmallHash *sh, uintptr_t key, void *item) #ifdef USE_REMOVE bool BLI_smallhash_remove(SmallHash *sh, uintptr_t key) -{ - check_stack_move(sh); - - // SmallHashEntry *e = smallhash_lookup(sh, key); - - SmallHashEntry *e; - uintptr_t h = smallhash_key(key); - uintptr_t hoff = 1; - - for (e = &sh->buckets[h % (uintptr_t)sh->nbuckets]; e->val != SMHASH_CELL_FREE; - h = SMHASH_NEXT(h, hoff), e = &sh->buckets[h % (uintptr_t)sh->nbuckets]) - { - if (e->key == key) { - /* should never happen because unused keys are zero'd */ - BLI_assert(e->val != SMHASH_CELL_UNUSED); - break; - } - } - - if (e && e->key == key) { - h = SMHASH_NEXT(h, hoff); - SmallHashEntry *e2 = &sh->buckets[h & sh->nbuckets]; - - e->key = SMHASH_KEY_UNUSED; - e->val = SMHASH_CELL_UNUSED; - - sh->nentries--; - - return true; - } - else { - return false; - } -} - -bool BLI_smallhash_remove_p(SmallHash *sh, uintptr_t key, void **val) { SmallHashEntry *e = smallhash_lookup(sh, key); if (e) { - *val = e->val; - e->key = SMHASH_KEY_UNUSED; e->val = SMHASH_CELL_UNUSED; sh->nentries--; @@ -435,42 +254,28 @@ bool BLI_smallhash_remove_p(SmallHash *sh, uintptr_t key, void **val) } #endif -void *BLI_smallhash_lookup(SmallHash *sh, uintptr_t key) +void *BLI_smallhash_lookup(const SmallHash *sh, uintptr_t key) { SmallHashEntry *e = smallhash_lookup(sh, key); return e ? e->val : NULL; } -void **BLI_smallhash_lookup_p(SmallHash *sh, uintptr_t key) +void **BLI_smallhash_lookup_p(const SmallHash *sh, uintptr_t key) { SmallHashEntry *e = smallhash_lookup(sh, key); return e ? &e->val : NULL; } -void BLI_smallhash_clear(SmallHash *sh, uintptr_t key) -{ - check_stack_move(sh); - - SmallHashEntry *e = sh->buckets; - - for (uint i = 0; i < sh->nbuckets; i++, e++) { - e->key = SMHASH_KEY_UNUSED; - e->val = SMHASH_CELL_FREE; - } - - sh->nentries = 0; -} - -bool BLI_smallhash_haskey(SmallHash *sh, uintptr_t key) +bool BLI_smallhash_haskey(const SmallHash *sh, uintptr_t key) { SmallHashEntry *e = smallhash_lookup(sh, key); return (e != NULL); } -int BLI_smallhash_len(SmallHash *sh) +int BLI_smallhash_len(const SmallHash *sh) { return (int)sh->nentries; } @@ -506,20 +311,16 @@ void **BLI_smallhash_iternext_p(SmallHashIter *iter, uintptr_t *key) return e ? &e->val : NULL; } -void *BLI_smallhash_iternew(SmallHash *sh, SmallHashIter *iter, uintptr_t *key) +void *BLI_smallhash_iternew(const SmallHash *sh, SmallHashIter *iter, uintptr_t *key) { - check_stack_move(sh); - iter->sh = sh; iter->i = 0; return BLI_smallhash_iternext(iter, key); } -void **BLI_smallhash_iternew_p(SmallHash *sh, SmallHashIter *iter, uintptr_t *key) +void **BLI_smallhash_iternew_p(const SmallHash *sh, SmallHashIter *iter, uintptr_t *key) { - check_stack_move(sh); - iter->sh = sh; iter->i = 0; @@ -578,11 +379,11 @@ double BLI_smallhash_calc_quality(SmallHash *sh) if (sh->buckets[i].key != SMHASH_KEY_UNUSED) { uint64_t count = 0; SmallHashEntry *e, *e_final = &sh->buckets[i]; - uintptr_t h = smallhash_key(e_final->key); - uintptr_t hoff = 1; + uint h = smallhash_key(e_final->key); + uint hoff = 1; - for (e = &sh->buckets[h % (uintptr_t)sh->nbuckets]; e != e_final; - h = SMHASH_NEXT(h, hoff), e = &sh->buckets[h % (uintptr_t)sh->nbuckets]) + for (e = &sh->buckets[h % sh->nbuckets]; e != e_final; + h = SMHASH_NEXT(h, hoff), e = &sh->buckets[h % sh->nbuckets]) { count += 1; } diff --git a/source/blender/blenlib/intern/task_pool.cc b/source/blender/blenlib/intern/task_pool.cc index 5e73f411c80..80fc4f44ab9 100644 --- a/source/blender/blenlib/intern/task_pool.cc +++ b/source/blender/blenlib/intern/task_pool.cc @@ -76,8 +76,7 @@ class Task { other.freedata = nullptr; } -#if (defined(WITH_TBB) && TBB_INTERFACE_VERSION_MAJOR < 10) || \ - (defined(__clang__) && defined(WIN32)) +#if defined(WITH_TBB) && TBB_INTERFACE_VERSION_MAJOR < 10 Task(const Task &other) : pool(other.pool), run(other.run), diff --git a/source/blender/editors/sculpt_paint/sculpt_undo.cc b/source/blender/editors/sculpt_paint/sculpt_undo.cc index 583b82ebab7..1e9add1c4ba 100644 --- a/source/blender/editors/sculpt_paint/sculpt_undo.cc +++ b/source/blender/editors/sculpt_paint/sculpt_undo.cc @@ -3130,58 +3130,6 @@ void ED_sculpt_undo_push_multires_mesh_end(bContext *C, const char *str) } /** \} */ -extern "C" void SCULPT_substep_undo(bContext * /*C*/, int /*dir*/) -{ - printf("%s: not working!\n", __func__); -#if 0 // XXX - Scene *scene = CTX_data_scene(C); - Object *ob = CTX_data_active_object(C); - - Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); - - if (!scene || !ob || !ob->sculpt) { - printf("not in sculpt mode\n"); - return; - } - - SculptSession *ss = ob->sculpt; - - if (!ss->bm) { - printf("not in dyntopo mode\n"); - return; - } - - BmeshUndoData data = {ss->pbvh, - ss->bm, - false, - false, - ss->cd_face_node_offset, - ss->cd_vert_node_offset, - ss->attrs.boundary_flags->bmesh_cd_offset, - false, - false}; - - BMLogCallbacks callbacks = {bmesh_undo_on_vert_add, - bmesh_undo_on_vert_kill, - bmesh_undo_on_vert_change, - bmesh_undo_on_edge_add, - bmesh_undo_on_edge_kill, - bmesh_undo_on_edge_change, - bmesh_undo_on_face_add, - bmesh_undo_on_face_kill, - bmesh_undo_on_face_change, - bmesh_undo_full_mesh, - nullptr, - (void *)&data}; - - BM_log_undo_single(ss->bm, ss->bm_log, &callbacks, nullptr); - - BKE_sculpt_update_object_for_edit(depsgraph, ob, true, false, false); - DEG_id_tag_update(&ob->id, ID_RECALC_SHADING); -#endif -} - -void BM_log_get_changed(BMesh *bm, struct BMIdMap *idmap, BMLogEntry *_entry, SmallHash *sh); void ED_sculpt_fast_save_bmesh(Object *ob) { @@ -3192,100 +3140,7 @@ void ED_sculpt_fast_save_bmesh(Object *ob) return; } -#if 1 struct BMeshToMeshParams params = {}; - - void BM_mesh_bm_to_me_threaded( - Main * bmain, Object * ob, BMesh * bm, Mesh * me, const struct BMeshToMeshParams *params); - params.update_shapekey_indices = true; - - // BM_mesh_bm_to_me_threaded(nullptr, ob, bm, (Mesh *)ob->data, ¶ms); BM_mesh_bm_to_me(nullptr, bm, (Mesh *)ob->data, ¶ms); -#else - SculptUndoStep *last_step = nullptr; - - UndoStack *ustack = ED_undo_stack_get(); - UndoStep *us = ustack->step_active; - - SmallHash elems; - BLI_smallhash_init(&elems); - - bool bad = false; - - if (!us) { - printf("no active undo step!"); - bad = true; - } - else { - while (us) { - us = us->prev; - - if (us->type == BKE_UNDOSYS_TYPE_SCULPT) { - SculptUndoStep *usculpt = (SculptUndoStep *)us; - - LISTBASE_FOREACH (SculptUndoNode *, unode, &usculpt->data.nodes) { - if (unode->bm_entry) { - BM_log_get_changed(bm, ss->bm_idmap, unode->bm_entry, &elems); - } - } - - if (usculpt->auto_saved) { - last_step = usculpt; - break; - } - - if (!last_step) { - usculpt->auto_saved = true; - } - - last_step = usculpt; - } - } - } - - if (!last_step) { - bad = true; - } - else { - last_step->auto_saved = true; - } - - if (bad) { - printf("%s: Failed to find sculpt undo stack entries\n", __func__); - - /* Just save everything */ - struct BMeshToMeshParams params = {0}; - BM_mesh_bm_to_me(nullptr, bm, (Mesh *)ob->data, ¶ms); - return; - } - - int totv = 0, tote = 0, totl = 0, totf = 0; - - BM_mesh_elem_table_ensure(bm, BM_VERT | BM_EDGE | BM_LOOP | BM_FACE); - - SmallHashIter iter; - uintptr_t key; - void *val = BLI_smallhash_iternew(&elems, &iter, &key); - for (; val; val = BLI_smallhash_iternext(&iter, &key)) { - BMElem *elem = (BMElem *)key; - - switch (elem->head.htype) { - case BM_VERT: - totv++; - break; - case BM_EDGE: - tote++; - break; - case BM_LOOP: - totl++; - break; - case BM_FACE: - totf++; - break; - } - } - - BLI_smallhash_release(&elems); -#endif } diff --git a/source/blender/makesrna/intern/rna_sculpt_paint.c b/source/blender/makesrna/intern/rna_sculpt_paint.c index 01db781e218..af46bbf0eaf 100644 --- a/source/blender/makesrna/intern/rna_sculpt_paint.c +++ b/source/blender/makesrna/intern/rna_sculpt_paint.c @@ -1106,11 +1106,6 @@ static void rna_def_sculpt(BlenderRNA *brna) RNA_def_function_ui_description(func, "Test if sculpt has persistent base (sculpt mode only)"); RNA_def_function_flag(func, FUNC_USE_CONTEXT | FUNC_NO_SELF); RNA_def_function_return(func, RNA_def_boolean(func, "has", 1, "Has persistent Base", "")); - - func = RNA_def_function(srna, "debug_substep_undo", "rna_SCULPT_substep_undo"); - RNA_def_function_ui_description(func, "Test function"); - RNA_def_function_flag(func, FUNC_NO_SELF | FUNC_USE_CONTEXT); - RNA_def_int(func, "dir", -1, -1, 1, "dir", "dir", -1, 1); } static void rna_def_uv_sculpt(BlenderRNA *brna) -- 2.30.2 From 309cf9fb8248b2999fe3f852ddc00f2c9a1d971d Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Tue, 30 May 2023 15:15:01 -0700 Subject: [PATCH 129/279] temp-sculpt-dyntopo: fix compile error --- source/blender/bmesh/intern/bmesh_construct.c | 2 +- source/blender/bmesh/intern/bmesh_core.c | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/source/blender/bmesh/intern/bmesh_construct.c b/source/blender/bmesh/intern/bmesh_construct.c index acf5cb8b5f2..9d3b3155cfb 100644 --- a/source/blender/bmesh/intern/bmesh_construct.c +++ b/source/blender/bmesh/intern/bmesh_construct.c @@ -657,7 +657,7 @@ BMesh *BM_mesh_copy(BMesh *bm_old) BMIter iter; int i; const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_BM(bm_old); - struct BMeshCreateParams params = {}; + struct BMeshCreateParams params = {0}; params.use_toolflags = bm_old->use_toolflags; diff --git a/source/blender/bmesh/intern/bmesh_core.c b/source/blender/bmesh/intern/bmesh_core.c index c813b7654f4..7575b35743f 100644 --- a/source/blender/bmesh/intern/bmesh_core.c +++ b/source/blender/bmesh/intern/bmesh_core.c @@ -2429,9 +2429,6 @@ ATTR_NO_OPT BMVert *bmesh_kernel_join_vert_kill_edge( BLI_array_staticdeclare(fs, 32); BLI_array_staticdeclare(deles, 32); - BMEdge **es = NULL; - BLI_array_staticdeclare(es, 32); - BMVert *v_del = BM_edge_other_vert(e, v_conn); const int tag = _FLAG_WALK_ALT; // using bmhead.api_flag here const int dup_tag = _FLAG_OVERLAP; -- 2.30.2 From dcfb042cbab056e6817cf4b8638cd929c7d3ee81 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Tue, 30 May 2023 15:21:36 -0700 Subject: [PATCH 130/279] temp-sculpt-dyntopo: fix msvc error --- source/blender/bmesh/intern/bmesh_construct.c | 2 +- source/blender/bmesh/intern/bmesh_mesh.cc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/source/blender/bmesh/intern/bmesh_construct.c b/source/blender/bmesh/intern/bmesh_construct.c index 9d3b3155cfb..17ccf0b741c 100644 --- a/source/blender/bmesh/intern/bmesh_construct.c +++ b/source/blender/bmesh/intern/bmesh_construct.c @@ -658,7 +658,7 @@ BMesh *BM_mesh_copy(BMesh *bm_old) int i; const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_BM(bm_old); struct BMeshCreateParams params = {0}; - + params.use_toolflags = bm_old->use_toolflags; /* allocate a bmesh */ diff --git a/source/blender/bmesh/intern/bmesh_mesh.cc b/source/blender/bmesh/intern/bmesh_mesh.cc index ccb9812989a..f46751a7b5e 100644 --- a/source/blender/bmesh/intern/bmesh_mesh.cc +++ b/source/blender/bmesh/intern/bmesh_mesh.cc @@ -35,7 +35,7 @@ const BMAllocTemplate bm_mesh_chunksize_default = {512, 1024, 2048, 512}; static void bm_alloc_toolflags(BMesh *bm); static void bm_mempool_init_ex(const BMAllocTemplate *allocsize, - const bool use_toolflags, + const bool /*use_toolflags*/, BLI_mempool **r_vpool, BLI_mempool **r_epool, BLI_mempool **r_lpool, -- 2.30.2 From 64963b6556d8ed04df0a6c1f3be44dc09c80b4ac Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Tue, 30 May 2023 15:48:31 -0700 Subject: [PATCH 131/279] temp-sculpt-dyntopo: Fix crash in recent cleanup commits --- .../blender/blenkernel/intern/pbvh_bmesh.cc | 27 ++++++++++--------- 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/source/blender/blenkernel/intern/pbvh_bmesh.cc b/source/blender/blenkernel/intern/pbvh_bmesh.cc index fb54b84f373..80ce44af635 100644 --- a/source/blender/blenkernel/intern/pbvh_bmesh.cc +++ b/source/blender/blenkernel/intern/pbvh_bmesh.cc @@ -2868,7 +2868,7 @@ bool BKE_pbvh_bmesh_check_tris(PBVH *pbvh, PBVHNode *node) Vector loops; Vector loops_idx; - Vector tribufs; + Vector *tribufs = MEM_new>("PBVHTriBuf tribufs"); node->flag &= ~PBVH_UpdateTris; @@ -2896,11 +2896,11 @@ bool BKE_pbvh_bmesh_check_tris(PBVH *pbvh, PBVHNode *node) if (mat_map[mat_nr] == -1) { PBVHTriBuf _tribuf = {0}; - mat_map[mat_nr] = tribufs.size(); + mat_map[mat_nr] = tribufs->size(); pbvh_init_tribuf(node, &_tribuf); _tribuf.mat_nr = mat_nr; - tribufs.append(_tribuf); + tribufs->append(_tribuf); } #ifdef DYNTOPO_DYNAMIC_TESS @@ -2913,7 +2913,7 @@ bool BKE_pbvh_bmesh_check_tris(PBVH *pbvh, PBVHNode *node) for (int i = 0; i < tottri; i++) { PBVHTri *tri = pbvh_tribuf_add_tri(node->tribuf); - PBVHTriBuf *mat_tribuf = &tribufs[mat_map[mat_nr]]; + PBVHTriBuf *mat_tribuf = &(*tribufs)[mat_map[mat_nr]]; PBVHTri *mat_tri = pbvh_tribuf_add_tri(mat_tribuf); tri->eflag = mat_tri->eflag = 0; @@ -2932,25 +2932,28 @@ bool BKE_pbvh_bmesh_check_tris(PBVH *pbvh, PBVHNode *node) } uintptr_t loopkey = tri_loopkey(l, mat_nr, pbvh->cd_faceset_offset, cd_uvs, totuv); + int &tri_v = node->tribuf->vertmap.lookup_or_add(loopkey, node->tribuf->totvert); - bool existed = node->tribuf->vertmap.add(loopkey, node->tribuf->totvert); - if (!existed) { + /* Newly added to the set? */ + if (tri_v == node->tribuf->totvert) { PBVHVertRef sv = {(intptr_t)l->v}; minmax_v3v3_v3(min, max, l->v->co); pbvh_tribuf_add_vert(node->tribuf, sv, l); } - tri->v[j] = (intptr_t)val[0]; + tri->v[j] = (intptr_t)tri_v; tri->l[j] = (intptr_t)l; - existed = mat_tribuf->vertmap.add(loopkey, mat_tribuf->totvert); - if (!existed) { + int &mattri_v = mat_tribuf->vertmap.lookup_or_add(loopkey, mat_tribuf->totvert); + + /* Newly added to the set? */ + if (mattri_v == mat_tribuf->totvert) { PBVHVertRef sv = {(intptr_t)l->v}; minmax_v3v3_v3(min, max, l->v->co); pbvh_tribuf_add_vert(mat_tribuf, sv, l); } - mat_tri->v[j] = (intptr_t)val[0]; + mat_tri->v[j] = (intptr_t)mattri_v; mat_tri->l[j] = (intptr_t)l; } @@ -3016,7 +3019,7 @@ bool BKE_pbvh_bmesh_check_tris(PBVH *pbvh, PBVHNode *node) } int mat_nr = f->mat_nr; - PBVHTriBuf *mat_tribuf = &tribufs[mat_map[mat_nr]]; + PBVHTriBuf *mat_tribuf = &(*tribufs)[mat_map[mat_nr]]; BMLoop *l = f->l_first; do { @@ -3041,7 +3044,7 @@ bool BKE_pbvh_bmesh_check_tris(PBVH *pbvh, PBVHNode *node) bm->elem_index_dirty |= BM_VERT; - node->tri_buffers = new Vector(tribufs); + node->tri_buffers = tribufs; if (node->tribuf->totvert) { copy_v3_v3(node->tribuf->min, min); -- 2.30.2 From 7235d33e571fff5c9fd268a50ebc95b342ff836c Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Wed, 31 May 2023 03:29:25 -0700 Subject: [PATCH 132/279] temp-sculpt-dyntopo: Topology rake fixes * Properly handle smooth boundaries. --- .../sculpt_paint/sculpt_brush_types.cc | 16 +++---- .../editors/sculpt_paint/sculpt_smooth.cc | 43 +++++++++++++++---- 2 files changed, 40 insertions(+), 19 deletions(-) diff --git a/source/blender/editors/sculpt_paint/sculpt_brush_types.cc b/source/blender/editors/sculpt_paint/sculpt_brush_types.cc index 0c61240109b..4e3fd6559b4 100644 --- a/source/blender/editors/sculpt_paint/sculpt_brush_types.cc +++ b/source/blender/editors/sculpt_paint/sculpt_brush_types.cc @@ -2831,9 +2831,7 @@ static void update_curvatures_task_cb_ex(void *__restrict userdata, SculptSession *ss = data->ob->sculpt; const Brush *brush = data->brush; - if (brush->flag2 & BRUSH_SMOOTH_USE_AREA_WEIGHT) { - BKE_pbvh_check_tri_areas(ss->pbvh, data->nodes[n]); - } + BKE_pbvh_check_tri_areas(ss->pbvh, data->nodes[n]); if (brush->flag2 & BRUSH_CURVATURE_RAKE) { SCULPT_curvature_begin(ss, data->nodes[n], true); @@ -2877,8 +2875,6 @@ static void do_topology_rake_bmesh_task_cb_ex(void *__restrict userdata, SCULPT_automasking_node_begin( data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]); - bool weighted = brush->flag2 & BRUSH_SMOOTH_USE_AREA_WEIGHT; - PBVHVertexIter vd; bool modified = false; @@ -2917,7 +2913,7 @@ static void do_topology_rake_bmesh_task_cb_ex(void *__restrict userdata, &automask_data); /* Make brush falloff less sharp. */ - fade = sqrtf(fade); + fade = powf(fade, 1.0f / 3.0f); fade *= bstrength; float oldco[3]; @@ -2930,7 +2926,7 @@ static void do_topology_rake_bmesh_task_cb_ex(void *__restrict userdata, int cd_temp = data->scl->bmesh_cd_offset; SCULPT_bmesh_four_neighbor_average( - ss, avg, direction2, vd.bm_vert, projection, hard_corner_pin, cd_temp, weighted, false); + ss, avg, direction2, vd.bm_vert, projection, hard_corner_pin, cd_temp, true, false); sub_v3_v3v3(val, avg, vd.co); madd_v3_v3v3fl(val, vd.co, val, fade); @@ -2947,7 +2943,7 @@ static void do_topology_rake_bmesh_task_cb_ex(void *__restrict userdata, projection, hard_corner_pin, cd_temp, - weighted, + true, true); float *origco = blender::bke::paint::vertex_attr_ptr(vd.vertex, ss->attrs.orig_co); interp_v3_v3v3(origco, origco, origco_avg, fade); @@ -3004,9 +3000,7 @@ void SCULPT_bmesh_topology_rake(Sculpt *sd, Object *ob, Span nodes, data.scl = ss->attrs.rake_temp; data.smooth_origco = SCULPT_tool_needs_smooth_origco(brush->sculpt_tool); - if (brush->flag2 & BRUSH_SMOOTH_USE_AREA_WEIGHT) { - BKE_pbvh_face_areas_begin(ss->pbvh); - } + BKE_pbvh_face_areas_begin(ss->pbvh); TaskParallelSettings settings; BKE_pbvh_parallel_range_settings(&settings, true, nodes.size()); diff --git a/source/blender/editors/sculpt_paint/sculpt_smooth.cc b/source/blender/editors/sculpt_paint/sculpt_smooth.cc index 54e1fc2f291..9519e79f979 100644 --- a/source/blender/editors/sculpt_paint/sculpt_smooth.cc +++ b/source/blender/editors/sculpt_paint/sculpt_smooth.cc @@ -371,6 +371,19 @@ void SCULPT_bmesh_four_neighbor_average(SculptSession *ss, normalize_v3(direction); } + eSculptBoundary boundary_mask = SCULPT_BOUNDARY_FACE_SET | SCULPT_BOUNDARY_MESH | + SCULPT_BOUNDARY_SHARP_MARK | SCULPT_BOUNDARY_SEAM | + SCULPT_BOUNDARY_UV; + eSculptBoundary boundary = SCULPT_vertex_is_boundary(ss, vertex, boundary_mask); + + eSculptCorner corner_mask = eSculptCorner(int(boundary) << SCULPT_CORNER_BIT_SHIFT); + eSculptCorner corner = SCULPT_vertex_is_corner(ss, vertex, corner_mask); + + eSculptBoundary smooth_mask = SCULPT_BOUNDARY_SEAM | SCULPT_BOUNDARY_UV; + if (!ss->hard_edge_mode) { + smooth_mask |= SCULPT_BOUNDARY_FACE_SET; + } + float *co1 = do_origco ? origco : v->co; float *no1 = do_origco ? origno : v->no; @@ -451,14 +464,12 @@ void SCULPT_bmesh_four_neighbor_average(SculptSession *ss, co2 = BM_ELEM_CD_PTR(v_other, ss->attrs.orig_co->bmesh_cd_offset); } - eSculptBoundary bflag = SCULPT_BOUNDARY_FACE_SET | SCULPT_BOUNDARY_MESH | - SCULPT_BOUNDARY_SHARP_MARK | SCULPT_BOUNDARY_SEAM | SCULPT_BOUNDARY_UV; - - int bound = SCULPT_edge_is_boundary(ss, BKE_pbvh_make_eref(intptr_t(e)), bflag); + eSculptBoundary boundary2 = SCULPT_edge_is_boundary( + ss, BKE_pbvh_make_eref(intptr_t(e)), boundary_mask); float dirw = 1.0f; /* Add to cross field. */ - if (bound) { + if (boundary2 != SCULPT_BOUNDARY_NONE) { had_bound = true; sub_v3_v3v3(dir2, co2, co1); @@ -480,10 +491,26 @@ void SCULPT_bmesh_four_neighbor_average(SculptSession *ss, madd_v3_v3fl(dir3, dir2, dirw); totdir3 += dirw; - if (had_bound) { - tot_co = 0.0f; + if (boundary2) { + float fac = weighted ? areas[area_i] : 1.0f; + + madd_v3_v3fl(avg_co, co2, fac); + tot_co += fac; + continue; + } else if (boundary != SCULPT_BOUNDARY_NONE) { + if (boundary & smooth_mask) { + float fac = weighted ? areas[area_i] : 1.0f; + float vec[3], co3[3]; + + sub_v3_v3v3(vec, co2, co1); + copy_v3_v3(co3, co1); + madd_v3_v3fl(co3, no1, dot_v3v3(vec, no1)); + madd_v3_v3fl(avg_co, co3, fac); + tot_co += fac; + } continue; } + float vec[3]; sub_v3_v3v3(vec, co2, co1); @@ -525,7 +552,7 @@ void SCULPT_bmesh_four_neighbor_average(SculptSession *ss, corner_type |= SCULPT_CORNER_FACE_SET; } - if (SCULPT_vertex_is_corner(ss, vertex, corner_type)) { + if (corner != SCULPT_CORNER_NONE) { interp_v3_v3v3(avg, avg, SCULPT_vertex_co_get(ss, vertex), hard_corner_pin); } -- 2.30.2 From 51e5b7a33b6d5a817ee7bfebb5a036b883c5d8b0 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Wed, 31 May 2023 03:50:20 -0700 Subject: [PATCH 133/279] temp-sculpt-dyntopo: Fix arm compiler error --- source/blender/blenkernel/BKE_pbvh.h | 2 +- source/blender/blenkernel/intern/pbvh_bmesh.cc | 13 +++++++------ source/blender/bmesh/intern/bmesh_idmap.cc | 4 ++-- .../blender/editors/sculpt_paint/sculpt_smooth.cc | 1 + 4 files changed, 11 insertions(+), 9 deletions(-) diff --git a/source/blender/blenkernel/BKE_pbvh.h b/source/blender/blenkernel/BKE_pbvh.h index bb5af8d0a48..5260b047c49 100644 --- a/source/blender/blenkernel/BKE_pbvh.h +++ b/source/blender/blenkernel/BKE_pbvh.h @@ -142,7 +142,7 @@ typedef struct PBVHTriBuf { int verts_size, edges_size, tris_size; #ifdef __cplusplus - blender::Map vertmap; + blender::Map vertmap; #else void *vertmap; #endif diff --git a/source/blender/blenkernel/intern/pbvh_bmesh.cc b/source/blender/blenkernel/intern/pbvh_bmesh.cc index 80ce44af635..802952e7093 100644 --- a/source/blender/blenkernel/intern/pbvh_bmesh.cc +++ b/source/blender/blenkernel/intern/pbvh_bmesh.cc @@ -2923,7 +2923,6 @@ bool BKE_pbvh_bmesh_check_tris(PBVH *pbvh, PBVHNode *node) BMLoop *l = loops[loops_idx[i][j]]; BMLoop *l2 = loops[loops_idx[i][(j + 1) % 3]]; - void **val = nullptr; BMEdge *e = BM_edge_exists(l->v, l2->v); if (e) { @@ -2931,7 +2930,9 @@ bool BKE_pbvh_bmesh_check_tris(PBVH *pbvh, PBVHNode *node) mat_tri->eflag |= 1 << j; } - uintptr_t loopkey = tri_loopkey(l, mat_nr, pbvh->cd_faceset_offset, cd_uvs, totuv); + void *loopkey = reinterpret_cast( + tri_loopkey(l, mat_nr, pbvh->cd_faceset_offset, cd_uvs, totuv)); + int &tri_v = node->tribuf->vertmap.lookup_or_add(loopkey, node->tribuf->totvert); /* Newly added to the set? */ @@ -3029,13 +3030,13 @@ bool BKE_pbvh_bmesh_check_tris(PBVH *pbvh, PBVHNode *node) l->e->head.hflag |= edgeflag; - int v1 = node->tribuf->vertmap.lookup_default((uintptr_t)l->e->v1, 0); - int v2 = node->tribuf->vertmap.lookup_default((uintptr_t)l->e->v2, 0); + int v1 = node->tribuf->vertmap.lookup_default((void *)l->e->v1, 0); + int v2 = node->tribuf->vertmap.lookup_default((void *)l->e->v2, 0); pbvh_tribuf_add_edge(node->tribuf, v1, v2); - v1 = mat_tribuf->vertmap.lookup_default((uintptr_t)l->e->v1, 0); - v2 = mat_tribuf->vertmap.lookup_default((uintptr_t)l->e->v2, 0); + v1 = mat_tribuf->vertmap.lookup_default((void *)l->e->v1, 0); + v2 = mat_tribuf->vertmap.lookup_default((void *)l->e->v2, 0); pbvh_tribuf_add_edge(mat_tribuf, v1, v2); } while ((l = l->next) != f->l_first); diff --git a/source/blender/bmesh/intern/bmesh_idmap.cc b/source/blender/bmesh/intern/bmesh_idmap.cc index 63de78b1243..20629b1c008 100644 --- a/source/blender/bmesh/intern/bmesh_idmap.cc +++ b/source/blender/bmesh/intern/bmesh_idmap.cc @@ -37,8 +37,6 @@ static void bm_idmap_debug_check_init(BMesh *bm) static void idmap_log_message(const char *fmt, ...) { - char msg[64]; - va_list args; va_start(args, fmt); vprintf(fmt, args); @@ -94,6 +92,7 @@ static const char elem_names[9][16] = { "face", // 8 }; +#ifdef DEBUG_BM_IDMAP static const char *get_type_name(char htype) { if (htype <= 0 || htype >= 9) { @@ -102,6 +101,7 @@ static const char *get_type_name(char htype) return elem_names[int(htype)]; } +#endif template static constexpr char get_elem_type() { diff --git a/source/blender/editors/sculpt_paint/sculpt_smooth.cc b/source/blender/editors/sculpt_paint/sculpt_smooth.cc index 9519e79f979..9bd91ed3d48 100644 --- a/source/blender/editors/sculpt_paint/sculpt_smooth.cc +++ b/source/blender/editors/sculpt_paint/sculpt_smooth.cc @@ -505,6 +505,7 @@ void SCULPT_bmesh_four_neighbor_average(SculptSession *ss, sub_v3_v3v3(vec, co2, co1); copy_v3_v3(co3, co1); madd_v3_v3fl(co3, no1, dot_v3v3(vec, no1)); + madd_v3_v3fl(avg_co, co3, fac); tot_co += fac; } -- 2.30.2 From cfbe3b64f7e6bdf44082a893ff2299c9f19fb3ca Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Wed, 31 May 2023 04:31:30 -0700 Subject: [PATCH 134/279] temp-sculpt-dyntopo: Remove atomics changes * Removed the root cause of why they were needed: I had forgotten to revert changes to BLI_strict_flags.h I apparently made in sculpt-dev. * Also removed a few other unrelated changes. --- intern/atomic/intern/atomic_ops_msvc.h | 30 +++++++++---------- source/blender/blenkernel/CMakeLists.txt | 10 ------- .../blenkernel/intern/attribute_access.cc | 4 --- source/blender/blenlib/BLI_assert.h | 4 +-- source/blender/blenlib/BLI_strict_flags.h | 23 ++++---------- 5 files changed, 23 insertions(+), 48 deletions(-) diff --git a/intern/atomic/intern/atomic_ops_msvc.h b/intern/atomic/intern/atomic_ops_msvc.h index 6fbcaaad346..e65691d3970 100644 --- a/intern/atomic/intern/atomic_ops_msvc.h +++ b/intern/atomic/intern/atomic_ops_msvc.h @@ -63,17 +63,17 @@ /* Unsigned */ ATOMIC_INLINE uint64_t atomic_add_and_fetch_uint64(uint64_t *p, uint64_t x) { - return (uint64_t)InterlockedExchangeAdd64((int64_t *)p, (int64_t)x) + x; + return InterlockedExchangeAdd64((int64_t *)p, (int64_t)x) + x; } ATOMIC_INLINE uint64_t atomic_sub_and_fetch_uint64(uint64_t *p, uint64_t x) { - return (uint64_t)InterlockedExchangeAdd64((int64_t *)p, -((int64_t)x)) - x; + return InterlockedExchangeAdd64((int64_t *)p, -((int64_t)x)) - x; } ATOMIC_INLINE uint64_t atomic_cas_uint64(uint64_t *v, uint64_t old, uint64_t _new) { - return (uint64_t)InterlockedCompareExchange64((int64_t *)v, _new, old); + return InterlockedCompareExchange64((int64_t *)v, _new, old); } ATOMIC_INLINE uint64_t atomic_load_uint64(const uint64_t *v) @@ -88,12 +88,12 @@ ATOMIC_INLINE void atomic_store_uint64(uint64_t *p, uint64_t v) ATOMIC_INLINE uint64_t atomic_fetch_and_add_uint64(uint64_t *p, uint64_t x) { - return (uint64_t)InterlockedExchangeAdd64((int64_t *)p, (int64_t)x); + return InterlockedExchangeAdd64((int64_t *)p, (int64_t)x); } ATOMIC_INLINE uint64_t atomic_fetch_and_sub_uint64(uint64_t *p, uint64_t x) { - return (uint64_t)InterlockedExchangeAdd64((int64_t *)p, -((int64_t)x)); + return InterlockedExchangeAdd64((int64_t *)p, -((int64_t)x)); } /* Signed */ @@ -137,17 +137,17 @@ ATOMIC_INLINE int64_t atomic_fetch_and_sub_int64(int64_t *p, int64_t x) /* Unsigned */ ATOMIC_INLINE uint32_t atomic_add_and_fetch_uint32(uint32_t *p, uint32_t x) { - return (uint32_t)InterlockedExchangeAdd(p, x) + x; + return InterlockedExchangeAdd(p, x) + x; } ATOMIC_INLINE uint32_t atomic_sub_and_fetch_uint32(uint32_t *p, uint32_t x) { - return (uint32_t)InterlockedExchangeAdd(p, -((int32_t)x)) - x; + return InterlockedExchangeAdd(p, -((int32_t)x)) - x; } ATOMIC_INLINE uint32_t atomic_cas_uint32(uint32_t *v, uint32_t old, uint32_t _new) { - return (uint32_t)InterlockedCompareExchange((long *)v, _new, old); + return InterlockedCompareExchange((long *)v, _new, old); } ATOMIC_INLINE uint32_t atomic_load_uint32(const uint32_t *v) @@ -162,17 +162,17 @@ ATOMIC_INLINE void atomic_store_uint32(uint32_t *p, uint32_t v) ATOMIC_INLINE uint32_t atomic_fetch_and_add_uint32(uint32_t *p, uint32_t x) { - return (uint32_t)InterlockedExchangeAdd(p, x); + return InterlockedExchangeAdd(p, x); } ATOMIC_INLINE uint32_t atomic_fetch_and_or_uint32(uint32_t *p, uint32_t x) { - return (uint32_t)InterlockedOr((long *)p, x); + return InterlockedOr((long *)p, x); } ATOMIC_INLINE uint32_t atomic_fetch_and_and_uint32(uint32_t *p, uint32_t x) { - return (uint32_t)InterlockedAnd((long *)p, x); + return InterlockedAnd((long *)p, x); } /* Signed */ @@ -259,9 +259,9 @@ ATOMIC_INLINE uint8_t atomic_fetch_and_or_uint8(uint8_t *p, uint8_t b) ATOMIC_INLINE int8_t atomic_fetch_and_and_int8(int8_t *p, int8_t b) { #if (LG_SIZEOF_PTR == 8 || LG_SIZEOF_INT == 8) - return (int8_t)InterlockedAnd8((char *)p, (char)b); + return InterlockedAnd8((char *)p, (char)b); #else - return (int8_t)_InterlockedAnd8((char *)p, (char)b); + return _InterlockedAnd8((char *)p, (char)b); #endif } @@ -269,9 +269,9 @@ ATOMIC_INLINE int8_t atomic_fetch_and_and_int8(int8_t *p, int8_t b) ATOMIC_INLINE int8_t atomic_fetch_and_or_int8(int8_t *p, int8_t b) { #if (LG_SIZEOF_PTR == 8 || LG_SIZEOF_INT == 8) - return (int8_t)InterlockedOr8((char *)p, (char)b); + return InterlockedOr8((char *)p, (char)b); #else - return (int8_t)_InterlockedOr8((char *)p, (char)b); + return _InterlockedOr8((char *)p, (char)b); #endif } diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt index 32ebe383ba8..70846185140 100644 --- a/source/blender/blenkernel/CMakeLists.txt +++ b/source/blender/blenkernel/CMakeLists.txt @@ -782,16 +782,6 @@ if(WITH_OPENVDB) add_definitions(-DWITH_OPENVDB ${OPENVDB_DEFINITIONS}) endif() -if(WITH_INSTANT_MESHES) - list(APPEND INC - ../../../intern/instant-meshes - ) - list(APPEND LIB - bf_intern_instant_meshes - ) - add_definitions(-DWITH_INSTANT_MESHES) -endif() - if(WITH_QUADRIFLOW) list(APPEND INC ../../../intern/quadriflow diff --git a/source/blender/blenkernel/intern/attribute_access.cc b/source/blender/blenkernel/intern/attribute_access.cc index 155370230c4..3d52b6421b4 100644 --- a/source/blender/blenkernel/intern/attribute_access.cc +++ b/source/blender/blenkernel/intern/attribute_access.cc @@ -6,7 +6,6 @@ #include "BKE_customdata.h" #include "BKE_deform.h" #include "BKE_geometry_set.hh" -#include "BKE_global.h" #include "BKE_mesh.hh" #include "BKE_pointcloud.h" #include "BKE_type_conversions.hh" @@ -57,9 +56,6 @@ const char *no_procedural_access_message = N_( bool allow_procedural_attribute_access(StringRef attribute_name) { - if (G.debug_value == 892) { - return true; - } if (attribute_name.startswith(".corner")) { return false; } diff --git a/source/blender/blenlib/BLI_assert.h b/source/blender/blenlib/BLI_assert.h index c0cd31155f0..4292620e462 100644 --- a/source/blender/blenlib/BLI_assert.h +++ b/source/blender/blenlib/BLI_assert.h @@ -58,8 +58,8 @@ void _BLI_assert_unreachable_print(const char *file, int line, const char *funct NULL)) : \ NULL) #else -# define BLI_assert(a) ((void *)0) -# define BLI_assert_msg(a, msg) ((void *)0) +# define BLI_assert(a) ((void)0) +# define BLI_assert_msg(a, msg) ((void)0) #endif #if defined(__cplusplus) diff --git a/source/blender/blenlib/BLI_strict_flags.h b/source/blender/blenlib/BLI_strict_flags.h index 9b065f53a07..c8131fcf819 100644 --- a/source/blender/blenlib/BLI_strict_flags.h +++ b/source/blender/blenlib/BLI_strict_flags.h @@ -27,21 +27,10 @@ #endif #ifdef _MSC_VER -/* While regular clang defines __GNUC__ and is handled by the code above, clang-cl does not and - * needs to be handled separately. */ -# ifdef __clang__ -# pragma clang diagnostic error "-Wsign-conversion" -# pragma clang diagnostic error "-Wsign-compare" -# pragma clang diagnostic error "-Wimplicit-float-conversion" -# pragma clang diagnostic error "-Wimplicit-int" -# pragma clang diagnostic error "-Wshadow" -/* Normal MSVC */ -# else -# pragma warning(error : 4018) /* signed/unsigned mismatch */ -# pragma warning(error : 4244) /* conversion from 'type1' to 'type2', possible loss of data */ -# pragma warning(error : 4245) /* conversion from 'int' to 'unsigned int' */ -# pragma warning(error : 4267) /* conversion from 'size_t' to 'type', possible loss of data */ -# pragma warning(error : 4305) /* truncation from 'type1' to 'type2' */ -# pragma warning(error : 4389) /* signed/unsigned mismatch */ -# endif +# pragma warning(error : 4018) /* signed/unsigned mismatch */ +# pragma warning(error : 4244) /* conversion from 'type1' to 'type2', possible loss of data */ +# pragma warning(error : 4245) /* conversion from 'int' to 'unsigned int' */ +# pragma warning(error : 4267) /* conversion from 'size_t' to 'type', possible loss of data */ +# pragma warning(error : 4305) /* truncation from 'type1' to 'type2' */ +# pragma warning(error : 4389) /* signed/unsigned mismatch */ #endif -- 2.30.2 From 8c9939959756477407b8fc02d4fae21f43d02d40 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Wed, 31 May 2023 04:57:02 -0700 Subject: [PATCH 135/279] temp-sculpt-dyntopo: Remove unrelated drawing code changes * Vcol cell drawing was in this PR for debugging purposes. As it is (hopefully) no longer needed for that purpose the time has come to remove it. * Also remove PBVH_BMESH's "smooth shading" and PBVH's "fast draw" modes. --- scripts/startup/bl_ui/space_view3d_toolbar.py | 2 - source/blender/blenkernel/BKE_paint.h | 5 - source/blender/blenkernel/BKE_pbvh.h | 110 +---------------- source/blender/blenkernel/intern/paint.cc | 26 +--- source/blender/blenkernel/intern/pbvh.c | 39 +----- source/blender/blenkernel/intern/pbvh.cc | 70 +---------- .../blender/blenkernel/intern/pbvh_bmesh.cc | 14 +-- .../blender/blenkernel/intern/pbvh_intern.hh | 11 +- source/blender/draw/DRW_pbvh.hh | 2 +- source/blender/draw/intern/draw_pbvh.cc | 114 +----------------- .../sculpt_paint/grease_pencil_draw_ops.cc | 4 +- .../editors/sculpt_paint/paint_mask.cc | 4 +- source/blender/makesdna/DNA_scene_types.h | 6 +- .../makesrna/intern/rna_sculpt_paint.c | 41 ------- 14 files changed, 23 insertions(+), 425 deletions(-) diff --git a/scripts/startup/bl_ui/space_view3d_toolbar.py b/scripts/startup/bl_ui/space_view3d_toolbar.py index 84a27490824..ed27c5d333f 100644 --- a/scripts/startup/bl_ui/space_view3d_toolbar.py +++ b/scripts/startup/bl_ui/space_view3d_toolbar.py @@ -999,8 +999,6 @@ class VIEW3D_PT_sculpt_dyntopo(Panel, View3DPaintPanel): expand=True ) - col.prop(sculpt, "use_flat_vcol_shading") - class VIEW3D_PT_sculpt_voxel_remesh(Panel, View3DPaintPanel): bl_context = ".sculpt_mode" # dot on purpose (access from topbar) bl_label = "Remesh" diff --git a/source/blender/blenkernel/BKE_paint.h b/source/blender/blenkernel/BKE_paint.h index e1990ac1466..0ef519a8dea 100644 --- a/source/blender/blenkernel/BKE_paint.h +++ b/source/blender/blenkernel/BKE_paint.h @@ -728,7 +728,6 @@ struct SculptSession { int totuv; - bool bm_smooth_shading; bool ignore_uvs; /* Undo/redo log for dynamic topology sculpting */ @@ -853,8 +852,6 @@ struct SculptSession { */ char needs_flush_to_id; - bool fast_draw; /* Hides facesets/masks and forces smooth to save GPU bandwidth. */ - /* This is a fixed-size array so we can pass pointers to its elements * to client code. This is important to keep bmesh offsets up to date. */ @@ -1056,8 +1053,6 @@ bool BKE_object_attributes_active_color_fill(struct Object *ob, /** C accessor for #Object::sculpt::pbvh. */ struct PBVH *BKE_object_sculpt_pbvh_get(struct Object *object); bool BKE_object_sculpt_use_dyntopo(const struct Object *object); -void BKE_object_sculpt_dyntopo_smooth_shading_set(struct Object *object, bool value); -void BKE_object_sculpt_fast_draw_set(struct Object *object, bool value); /* paint_canvas.cc */ diff --git a/source/blender/blenkernel/BKE_pbvh.h b/source/blender/blenkernel/BKE_pbvh.h index 5260b047c49..d4d57b9cebb 100644 --- a/source/blender/blenkernel/BKE_pbvh.h +++ b/source/blender/blenkernel/BKE_pbvh.h @@ -351,7 +351,6 @@ void BKE_pbvh_build_grids(PBVH *pbvh, void **gridfaces, struct DMFlagMat *flagmats, unsigned int **grid_hidden, - bool fast_draw, float *face_areas, struct Mesh *me, struct SubdivCCG *subdiv_ccg); @@ -361,7 +360,6 @@ void BKE_pbvh_build_grids(PBVH *pbvh, void BKE_pbvh_build_bmesh(PBVH *pbvh, struct Mesh *me, struct BMesh *bm, - bool smooth_shading, BMLog *log, struct BMIdMap *idmap, const int cd_vert_node_offset, @@ -371,10 +369,8 @@ void BKE_pbvh_build_bmesh(PBVH *pbvh, const int cd_flag, const int cd_valence, const int cd_origco, - const int cd_origno, - bool fast_draw); + const int cd_origno); -void BKE_pbvh_fast_draw_set(PBVH *pbvh, bool state); void BKE_pbvh_set_idmap(PBVH *pbvh, struct BMIdMap *idmap); void BKE_pbvh_update_offsets(PBVH *pbvh, @@ -555,7 +551,6 @@ bool BKE_pbvh_bmesh_mark_update_valence(PBVH *pbvh, PBVHVertRef vertex); /* if pbvh uses a split index buffer, will call BKE_pbvh_vert_tag_update_normal_triangulation; otherwise does nothing. returns true if BKE_pbvh_vert_tag_update_normal_triangulation was called.*/ -bool BKE_pbvh_node_mark_update_index_buffer(PBVH *pbvh, PBVHNode *node); void BKE_pbvh_vert_tag_update_normal_triangulation(PBVHNode *node); void BKE_pbvh_node_mark_original_update(PBVHNode *node); void BKE_pbvh_vert_tag_update_normal_tri_area(PBVHNode *node); @@ -945,9 +940,6 @@ int BKE_pbvh_debug_draw_gen_get(PBVHNode *node); int BKE_pbvh_get_node_index(PBVH *pbvh, PBVHNode *node); int BKE_pbvh_get_node_id(PBVH *pbvh, PBVHNode *node); -void BKE_pbvh_set_flat_vcol_shading(PBVH *pbvh, bool value); - -void SCULPT_update_flat_vcol_shading(struct Object *ob, struct Scene *scene); void BKE_pbvh_curvature_update_set(PBVHNode *node, bool state); bool BKE_pbvh_curvature_update_get(PBVHNode *node); @@ -1012,107 +1004,15 @@ PBVHNode *BKE_pbvh_get_node_leaf_safe(PBVH *pbvh, int i); void BKE_pbvh_get_vert_face_areas(PBVH *pbvh, PBVHVertRef vertex, float *r_areas, int valence); void BKE_pbvh_set_symmetry(PBVH *pbvh, int symmetry, int boundary_symmetry); -#if 0 -typedef enum { - SCULPT_TEXTURE_UV = 1 << 0, // per-uv - SCULPT_TEXTURE_GRIDS = 1<<1 -} SculptTextureType; - -typedef int TexLayerRef; - -/* -Texture points are texels projected into 3d. -*/ -typedef intptr_t TexPointRef; - -void *BKE_pbvh_get_tex_settings(PBVH *pbvh, PBVHNode *node, TexLayerRef vdm); -void *BKE_pbvh_get_tex_data(PBVH *pbvh, PBVHNode *node, TexPointRef vdm); - -typedef struct SculptTextureDef { - SculptTextureType type; - int settings_size; - - void (*build_begin)(PBVH *pbvh, PBVHNode *node, TexLayerRef vdm); - - void (*calc_bounds)(PBVH *pbvh, PBVHNode *node, float r_min[3], float r_max[3], TexLayerRef vdm); - - /*vdms can cache data per node, which is freed to maintain memory limit. - they store cache in the same structure they return in buildNodeData.*/ - void (*freeCachedData)(PBVH *pbvh, PBVHNode *node, TexLayerRef vdm); - void (*ensuredCachedData)(PBVH *pbvh, PBVHNode *node, TexLayerRef vdm); - - /*builds all data that isn't cached.*/ - void *(*buildNodeData)(PBVH *pbvh, PBVHNode *node); - bool (*validate)(PBVH *pbvh, TexLayerRef vdm); - - void (*setVertexCos)(PBVH *pbvh, PBVHNode *node, PBVHVertRef *verts, int totvert, TexLayerRef vdm); - - void (*getPointsFromNode)(PBVH *pbvh, - PBVHNode *node, - TexLayerRef vdm, - TexPointRef **r_ids, - float ***r_cos, - float ***r_nos, - int *r_totpoint); - void (*releaseNodePoints)( - PBVH *pbvh, PBVHNode *node, TexLayerRef vdm, TexPointRef *ids, float **cos, float **nos); - -# if 0 - int (*getTrisFromNode)(PBVH *pbvh, - PBVHNode *node, - TexLayerRef vdm, - TexPointRef *((*r_tris)[3]), - TexPointRef **r_ids, - int tottri, - int totid); - void (*getTriInterpWeightsFromNode)(PBVH *pbvh, - PBVHNode *node, - TexLayerRef vdm, - float *((*r_tris)[3]), - SculptLoopRef ***r_src_loops, - int tottri, - int totloop); - int (*getTriCount)(PBVH *pbvh, PBVHNode *node, TexLayerRef vdm); -# endif - - void (*getPointNeighbors)(PBVH *pbvh, - PBVHNode *node, - TexLayerRef vdm, - TexPointRef id, - TexPointRef **r_neighbor_ids, - int *r_totneighbor, - int maxneighbors, - TexPointRef **r_duplicates_id, - int r_totduplicate, - int maxduplicates); - void (*getPointValence)(PBVH *pbvh, PBVHNode *node, TexLayerRef vdm, TexPointRef id); - void (*freeNodeData)(PBVH *pbvh, PBVHNode *node, TexLayerRef vdm, void *settings); - - void (*getPointsFromIds)( - PBVH *pbvh, PBVHNode *node, TexLayerRef vdm, TexPointRef *ids, int totid); - - /*displacement texture stuff*/ - // can be tangent, object space displacement - void (*worldToDelta)(PBVH *pbvh, PBVHNode *node, TexLayerRef vdm, TexPointRef *ids, int totid); - void (*deltaToWorld)(PBVH *pbvh, PBVHNode *node, TexLayerRef vdm, TexPointRef *ids, int totid); -} SculptDisplacementDef; - -typedef struct SculptLayerEntry { - char name[64]; - int type; - void *settings; - float factor; - struct SculptLayerEntry *parent; -} SculptLayerEntry; - -#endif - int BKE_pbvh_do_fset_symmetry(int fset, const int symflag, const float *co); bool BKE_pbvh_check_vert_boundary(PBVH *pbvh, struct BMVert *v); void BKE_pbvh_update_vert_boundary_grids(PBVH *pbvh, PBVHVertRef vertex); -#if 1 +/* Uncomment to enable PBVH NaN debugging. */ +//#define PBVH_CHECK_NANS + +#ifdef PBVH_CHECK_NANS # include "atomic_ops.h" # include # include diff --git a/source/blender/blenkernel/intern/paint.cc b/source/blender/blenkernel/intern/paint.cc index fcca87d4097..0e34e625b1e 100644 --- a/source/blender/blenkernel/intern/paint.cc +++ b/source/blender/blenkernel/intern/paint.cc @@ -1790,7 +1790,6 @@ static void sculpt_update_object( ss->depsgraph = depsgraph; - ss->bm_smooth_shading = scene->toolsettings->sculpt->flags & SCULPT_DYNTOPO_SMOOTH_SHADING; ss->ignore_uvs = me->flag & ME_SCULPT_IGNORE_UVS; ss->deform_modifiers_active = sculpt_modifiers_active(scene, sd, ob); @@ -1886,7 +1885,6 @@ static void sculpt_update_object( &me->pdata, CD_PROP_BOOL, ".hide_poly", me->totpoly); ss->subdiv_ccg = me_eval->runtime->subdiv_ccg; - ss->fast_draw = (scene->toolsettings->sculpt->flags & SCULPT_FAST_DRAW) != 0; PBVH *pbvh = BKE_sculpt_object_pbvh_ensure(depsgraph, ob); sculpt_check_face_areas(ob, pbvh); @@ -2564,7 +2562,6 @@ static PBVH *build_pbvh_for_dynamic_topology(Object *ob, bool /*update_sculptver BKE_pbvh_build_bmesh(pbvh, BKE_object_get_original_mesh(ob), ob->sculpt->bm, - ob->sculpt->bm_smooth_shading, ob->sculpt->bm_log, ob->sculpt->bm_idmap, ob->sculpt->attrs.dyntopo_node_id_vertex->bmesh_cd_offset, @@ -2574,8 +2571,8 @@ static PBVH *build_pbvh_for_dynamic_topology(Object *ob, bool /*update_sculptver ob->sculpt->attrs.flags ? ob->sculpt->attrs.flags->bmesh_cd_offset : -1, ob->sculpt->attrs.valence ? ob->sculpt->attrs.valence->bmesh_cd_offset : -1, ob->sculpt->attrs.orig_co ? ob->sculpt->attrs.orig_co->bmesh_cd_offset : -1, - ob->sculpt->attrs.orig_no ? ob->sculpt->attrs.orig_no->bmesh_cd_offset : -1, - ob->sculpt->fast_draw); + ob->sculpt->attrs.orig_no ? ob->sculpt->attrs.orig_no->bmesh_cd_offset : + -1); if (ob->sculpt->bm_log) { BKE_pbvh_set_bm_log(pbvh, ob->sculpt->bm_log); @@ -2607,7 +2604,6 @@ static PBVH *build_pbvh_from_regular_mesh(Object *ob, Mesh *me_eval_deform) BKE_pbvh_build_mesh(pbvh, me); BKE_pbvh_sharp_limit_set(pbvh, ss->sharp_angle_limit); - BKE_pbvh_fast_draw_set(pbvh, ss->fast_draw); const bool is_deformed = check_sculpt_object_deformed(ob, true); if (is_deformed && me_eval_deform != nullptr) { @@ -2640,7 +2636,6 @@ static PBVH *build_pbvh_from_ccg(Object *ob, SubdivCCG *subdiv_ccg) (void **)subdiv_ccg->grid_faces, subdiv_ccg->grid_flag_mats, subdiv_ccg->grid_hidden, - ob->sculpt->fast_draw, (float *)ss->attrs.face_areas->data, base_mesh, subdiv_ccg); @@ -2820,10 +2815,6 @@ PBVH *BKE_sculpt_object_pbvh_ensure(Depsgraph *depsgraph, Object *ob) sculpt_attribute_update_refs(ob); - if (pbvh) { - SCULPT_update_flat_vcol_shading(ob, scene); - } - return pbvh; } @@ -2840,19 +2831,6 @@ bool BKE_object_sculpt_use_dyntopo(const Object *object) return object->sculpt && object->sculpt->bm; } -void BKE_object_sculpt_dyntopo_smooth_shading_set(Object *object, const bool value) -{ - object->sculpt->bm_smooth_shading = value; -} - -void BKE_object_sculpt_fast_draw_set(Object *object, const bool value) -{ - object->sculpt->fast_draw = value; - if (object->sculpt->pbvh) { - BKE_pbvh_fast_draw_set(object->sculpt->pbvh, value); - } -} - void BKE_sculpt_bvh_update_from_ccg(PBVH *pbvh, SubdivCCG *subdiv_ccg) { CCGKey key; diff --git a/source/blender/blenkernel/intern/pbvh.c b/source/blender/blenkernel/intern/pbvh.c index 71bc373b406..014a04290be 100644 --- a/source/blender/blenkernel/intern/pbvh.c +++ b/source/blender/blenkernel/intern/pbvh.c @@ -1019,10 +1019,6 @@ void BKE_pbvh_build_mesh(PBVH *pbvh, #endif } - if (fast_draw) { - pbvh->flags |= PBVH_FAST_DRAW; - } - MEM_freeN(prim_bbc); /* Clear the bitmap so it can be used as an update tag later on. */ @@ -3725,7 +3721,7 @@ void pbvh_vertex_iter_init(PBVH *pbvh, PBVHNode *node, PBVHVertexIter *vi, int m bool BKE_pbvh_draw_mask(const PBVH *pbvh) { - return BKE_pbvh_has_mask(pbvh) && !(pbvh->flags & PBVH_FAST_DRAW); + return BKE_pbvh_has_mask(pbvh); } bool BKE_pbvh_has_mask(const PBVH *pbvh) @@ -3745,10 +3741,6 @@ bool BKE_pbvh_has_mask(const PBVH *pbvh) bool BKE_pbvh_draw_face_sets(PBVH *pbvh) { - if (pbvh->flags & PBVH_FAST_DRAW) { - return false; - } - switch (pbvh->header.type) { case PBVH_GRIDS: case PBVH_FACES: @@ -4324,40 +4316,11 @@ ProxyVertArray *BKE_pbvh_get_proxyarrays(PBVH *pbvh, PBVHNode *node) #endif -/* checks if pbvh needs to sync its flat vcol shading flag with scene tool settings - scene and ob are allowd to be NULL (in which case nothing is done). -*/ -void SCULPT_update_flat_vcol_shading(Object *ob, Scene *scene) -{ - if (!scene || !ob || !ob->sculpt || !ob->sculpt->pbvh) { - return; - } - - if (ob->sculpt->pbvh) { - bool flat_vcol_shading = ((scene->toolsettings->sculpt->flags & - SCULPT_DYNTOPO_FLAT_VCOL_SHADING) != 0); - - BKE_pbvh_set_flat_vcol_shading(ob->sculpt->pbvh, flat_vcol_shading); - } -} - PBVHNode *BKE_pbvh_get_node(PBVH *pbvh, int node) { return pbvh->nodes + node; } -bool BKE_pbvh_node_mark_update_index_buffer(PBVH *pbvh, PBVHNode *node) -{ - bool split_indexed = pbvh->header.bm && - (pbvh->flags & (PBVH_DYNTOPO_SMOOTH_SHADING | PBVH_FAST_DRAW)); - - if (split_indexed) { - BKE_pbvh_vert_tag_update_normal_triangulation(node); - } - - return split_indexed; -} - void BKE_pbvh_vert_tag_update_normal_triangulation(PBVHNode *node) { node->flag |= PBVH_UpdateTris; diff --git a/source/blender/blenkernel/intern/pbvh.cc b/source/blender/blenkernel/intern/pbvh.cc index 7d18d7172f3..46a45cdd627 100644 --- a/source/blender/blenkernel/intern/pbvh.cc +++ b/source/blender/blenkernel/intern/pbvh.cc @@ -870,7 +870,6 @@ static void pbvh_draw_args_init(PBVH *pbvh, PBVH_GPU_Args *args, PBVHNode *node) } args->polys = pbvh->polys; args->mlooptri = pbvh->looptri; - args->flat_vcol_shading = pbvh->flat_vcol_shading; args->updategen = node->updategen; if (ELEM(pbvh->header.type, PBVH_FACES, PBVH_GRIDS)) { @@ -1046,16 +1045,6 @@ void BKE_pbvh_update_mesh_pointers(PBVH *pbvh, Mesh *mesh) pbvh->pdata = &mesh->pdata; } -void BKE_pbvh_fast_draw_set(PBVH *pbvh, bool state) -{ - if (state) { - pbvh->flags |= PBVH_FAST_DRAW; - } - else { - pbvh->flags &= ~PBVH_FAST_DRAW; - } -} - void BKE_pbvh_build_mesh(PBVH *pbvh, Mesh *mesh) { BBC *prim_bbc = nullptr; @@ -1152,7 +1141,6 @@ void BKE_pbvh_build_grids(PBVH *pbvh, void **gridfaces, DMFlagMat *flagmats, BLI_bitmap **grid_hidden, - bool fast_draw, float *face_areas, Mesh *me, SubdivCCG *subdiv_ccg) @@ -1227,10 +1215,6 @@ void BKE_pbvh_build_grids(PBVH *pbvh, #endif } - if (fast_draw) { - pbvh->flags |= PBVH_FAST_DRAW; - } - MEM_freeN(prim_bbc); #ifdef VALIDATE_UNIQUE_NODE_FACES pbvh_validate_node_prims(pbvh); @@ -1578,7 +1562,6 @@ struct PBVHUpdateData { int flag = 0; bool show_sculpt_face_sets = false; - bool flat_vcol_shading = false; PBVHAttrReq *attrs = nullptr; int attrs_num = 0; @@ -1859,23 +1842,6 @@ static void pbvh_update_draw_buffer_cb(void *__restrict userdata, } } -void BKE_pbvh_set_flat_vcol_shading(PBVH *pbvh, bool value) -{ - if (value != pbvh->flat_vcol_shading) { - for (int i = 0; i < pbvh->totnode; i++) { - PBVHNode *node = pbvh->nodes + i; - - if (!(node->flag & PBVH_Leaf)) { - continue; - } - - BKE_pbvh_node_mark_rebuild_draw(node); - } - } - - pbvh->flat_vcol_shading = value; -} - void pbvh_free_draw_buffers(PBVH * /* pbvh */, PBVHNode *node) { if (node->draw_batches) { @@ -1924,7 +1890,6 @@ static void pbvh_update_draw_buffers(PBVH *pbvh, Mesh *me, Span node /* Parallel creation and update of draw buffers. */ PBVHUpdateData data(pbvh, nodes); data.mesh = me; - data.flat_vcol_shading = pbvh->flat_vcol_shading; TaskParallelSettings settings; BKE_pbvh_parallel_range_settings(&settings, true, nodes.size()); @@ -3684,7 +3649,7 @@ void pbvh_vertex_iter_init(PBVH *pbvh, PBVHNode *node, PBVHVertexIter *vi, int m bool BKE_pbvh_draw_mask(const PBVH *pbvh) { - return BKE_pbvh_has_mask(pbvh) && !(pbvh->flags & PBVH_FAST_DRAW); + return BKE_pbvh_has_mask(pbvh); } bool BKE_pbvh_has_mask(const PBVH *pbvh) @@ -3704,10 +3669,6 @@ bool BKE_pbvh_has_mask(const PBVH *pbvh) bool BKE_pbvh_draw_face_sets(PBVH *pbvh) { - if (pbvh->flags & PBVH_FAST_DRAW) { - return false; - } - switch (pbvh->header.type) { case PBVH_GRIDS: case PBVH_FACES: @@ -3831,40 +3792,11 @@ PBVHNode *BKE_pbvh_node_from_index(PBVH *pbvh, int node_i) return pbvh->nodes + node_i; } -/* checks if pbvh needs to sync its flat vcol shading flag with scene tool settings - scene and ob are allowd to be nullptr (in which case nothing is done). -*/ -void SCULPT_update_flat_vcol_shading(Object *ob, Scene *scene) -{ - if (!scene || !ob || !ob->sculpt || !ob->sculpt->pbvh) { - return; - } - - if (ob->sculpt->pbvh) { - bool flat_vcol_shading = ((scene->toolsettings->sculpt->flags & - SCULPT_DYNTOPO_FLAT_VCOL_SHADING) != 0); - - BKE_pbvh_set_flat_vcol_shading(ob->sculpt->pbvh, flat_vcol_shading); - } -} - PBVHNode *BKE_pbvh_get_node(PBVH *pbvh, int node) { return pbvh->nodes + node; } -bool BKE_pbvh_node_mark_update_index_buffer(PBVH *pbvh, PBVHNode *node) -{ - bool split_indexed = pbvh->header.bm && - (pbvh->flags & (PBVH_DYNTOPO_SMOOTH_SHADING | PBVH_FAST_DRAW)); - - if (split_indexed) { - BKE_pbvh_vert_tag_update_normal_triangulation(node); - } - - return split_indexed; -} - void BKE_pbvh_vert_tag_update_normal_triangulation(PBVHNode *node) { node->flag |= PBVH_UpdateTris; diff --git a/source/blender/blenkernel/intern/pbvh_bmesh.cc b/source/blender/blenkernel/intern/pbvh_bmesh.cc index 802952e7093..5f7c00c29f4 100644 --- a/source/blender/blenkernel/intern/pbvh_bmesh.cc +++ b/source/blender/blenkernel/intern/pbvh_bmesh.cc @@ -2395,7 +2395,6 @@ void BKE_pbvh_set_idmap(PBVH *pbvh, BMIdMap *idmap) void BKE_pbvh_build_bmesh(PBVH *pbvh, Mesh *me, BMesh *bm, - bool smooth_shading, BMLog *log, BMIdMap *idmap, const int cd_vert_node_offset, @@ -2405,8 +2404,7 @@ void BKE_pbvh_build_bmesh(PBVH *pbvh, const int /*cd_flag_offset*/, const int /*cd_valence_offset*/, const int cd_origco, - const int cd_origno, - bool fast_draw) + const int cd_origno) { // coalese_pbvh(pbvh, bm); @@ -2429,8 +2427,6 @@ void BKE_pbvh_build_bmesh(PBVH *pbvh, pbvh->mesh = me; - smooth_shading |= fast_draw; - pbvh->header.bm = bm; blender::bke::dyntopo::detail_size_set(pbvh, 0.75f, 0.4f); @@ -2453,14 +2449,6 @@ void BKE_pbvh_build_bmesh(PBVH *pbvh, BMIter iter; BMVert *v; - if (smooth_shading) { - pbvh->flags |= PBVH_DYNTOPO_SMOOTH_SHADING; - } - - if (fast_draw) { - pbvh->flags |= PBVH_FAST_DRAW; - } - /* bounding box array of all faces, no need to recalculate every time */ BBC *bbc_array = MEM_cnew_array(bm->totface, "BBC"); BMFace **nodeinfo = MEM_cnew_array(bm->totface, "nodeinfo"); diff --git a/source/blender/blenkernel/intern/pbvh_intern.hh b/source/blender/blenkernel/intern/pbvh_intern.hh index f98aba37ead..1c4c2721688 100644 --- a/source/blender/blenkernel/intern/pbvh_intern.hh +++ b/source/blender/blenkernel/intern/pbvh_intern.hh @@ -149,11 +149,7 @@ struct PBVHNode { int id; }; -typedef enum { - PBVH_DYNTOPO_SMOOTH_SHADING = 1, - PBVH_FAST_DRAW = 2, // hides facesets/masks and forces smooth to save GPU bandwidth - PBVH_IGNORE_UVS = 4 -} PBVHFlags; +typedef enum { PBVH_IGNORE_UVS = 1 } PBVHFlags; ENUM_OPERATORS(PBVHFlags, PBVH_IGNORE_UVS); typedef struct PBVHBMeshLog PBVHBMeshLog; @@ -260,11 +256,10 @@ struct PBVH { BMLog *bm_log; struct SubdivCCG *subdiv_ccg; - bool flat_vcol_shading; - bool need_full_render; // used by pbvh drawing for PBVH_BMESH + bool need_full_render; /* Set by pbvh drawing for PBVH_BMESH. */ int balance_counter; - int stroke_id; // used to keep origdata up to date in PBVH_BMESH + int stroke_id; bool is_cached; diff --git a/source/blender/draw/DRW_pbvh.hh b/source/blender/draw/DRW_pbvh.hh index 842cd0eb857..51da2d960fa 100644 --- a/source/blender/draw/DRW_pbvh.hh +++ b/source/blender/draw/DRW_pbvh.hh @@ -67,7 +67,7 @@ struct PBVH_GPU_Args { const int *looptri_polys; struct PBVHNode *node; - bool flat_vcol_shading; + /* Debug mode to show original coordinates instead of vertex positions. */ bool show_orig; /* BMesh. */ diff --git a/source/blender/draw/intern/draw_pbvh.cc b/source/blender/draw/intern/draw_pbvh.cc index 40d43b14aeb..08eb34f9efe 100644 --- a/source/blender/draw/intern/draw_pbvh.cc +++ b/source/blender/draw/intern/draw_pbvh.cc @@ -214,7 +214,7 @@ struct PBVHBatches { break; } case PBVH_BMESH: { - count = args->flat_vcol_shading ? args->tribuf->tottri * 6 : args->tribuf->tottri; + count = args->tribuf->tottri; } } @@ -759,7 +759,7 @@ struct PBVHBatches { void fill_vbo_bmesh(PBVHVbo &vbo, PBVH_GPU_Args *args) { - auto foreach_bmesh_normal = [&](std::function callback) { + auto foreach_bmesh = [&](std::function callback) { for (int i : IndexRange(args->tribuf->tottri)) { PBVHTri *tri = args->tribuf->tris + i; BMFace *f = reinterpret_cast(tri->f.i); @@ -774,115 +774,7 @@ struct PBVHBatches { } }; - BMVert v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12; - - auto foreach_bmesh_flat_vcol = [&](std::function callback) { - for (int i : IndexRange(args->tribuf->tottri)) { - PBVHTri *tri = args->tribuf->tris + i; - - BMFace *f = reinterpret_cast(tri->f.i); - - if (BM_elem_flag_test(f, BM_ELEM_HIDDEN)) { - continue; - } - - BMLoop *la = reinterpret_cast(tri->l[0]); - BMLoop *lb = reinterpret_cast(tri->l[1]); - BMLoop *lc = reinterpret_cast(tri->l[2]); - - BMLoop l9 = *la, l1 = *la, l2 = *la; - BMLoop l3 = *lb, l4 = *lb, l5 = *lb; - BMLoop l6 = *lc, l7 = *lc, l8 = *lc; - BMLoop l10 = *la, l11 = *lb, l12 = *lc; - - v9 = *la->v, v1 = *la->v, v2 = *la->v; - v3 = *lb->v, v4 = *lb->v, v5 = *lb->v; - v6 = *lc->v, v7 = *lc->v, v8 = *lc->v; - v10 = *la->v, v11 = *lb->v, v12 = *lc->v; - - if (vbo.type == CD_PBVH_CO_TYPE) { - l1.v = &v1; - l2.v = &v2; - l3.v = &v3; - l4.v = &v4; - l5.v = &v5; - l6.v = &v6; - l7.v = &v7; - l8.v = &v8; - l9.v = &v9; - l10.v = &v10; - l11.v = &v11; - l12.v = &v12; - - float3 cent = la->v->co; - cent += lb->v->co; - cent += lc->v->co; - cent *= 1.0f / 3.0f; - - copy_v3_v3(v10.co, cent); - copy_v3_v3(v11.co, cent); - copy_v3_v3(v12.co, cent); - - float3 cent1 = la->v->co; - cent1 += lb->v->co; - cent1 *= 0.5f; - copy_v3_v3(v2.co, cent1); - copy_v3_v3(v3.co, cent1); - - float3 cent2 = lb->v->co; - cent2 += lc->v->co; - cent2 *= 0.5f; - copy_v3_v3(v5.co, cent2); - copy_v3_v3(v6.co, cent2); - - float3 cent3 = lc->v->co; - cent3 += la->v->co; - cent3 *= 0.5f; - copy_v3_v3(v8.co, cent3); - copy_v3_v3(v9.co, cent3); - } - - /* v4 - b - v3 v5 - v2 cents v6 - a c - v1 v9 v8 v7 - */ - callback(&l7); - callback(&l8); - callback(&l6); - callback(&l8); - callback(&l12); - callback(&l6); - - callback(&l1); - callback(&l2); - callback(&l9); - callback(&l2); - callback(&l10); - callback(&l9); - - callback(&l4); - callback(&l5); - callback(&l3); - callback(&l5); - callback(&l11); - callback(&l3); - } - }; - - std::function)> foreach_bmesh; - - if (args->flat_vcol_shading) { - foreach_bmesh = foreach_bmesh_flat_vcol; - } - else { - foreach_bmesh = foreach_bmesh_normal; - } - - faces_count = args->flat_vcol_shading ? args->tribuf->tottri * 6 : args->tribuf->tottri; - tris_count = faces_count; + faces_count = tris_count = args->tribuf->tottri; int existing_num = GPU_vertbuf_get_vertex_len(vbo.vert_buf); void *existing_data = GPU_vertbuf_get_data(vbo.vert_buf); diff --git a/source/blender/editors/sculpt_paint/grease_pencil_draw_ops.cc b/source/blender/editors/sculpt_paint/grease_pencil_draw_ops.cc index aa4bf3bb3d3..5a6e1b681bc 100644 --- a/source/blender/editors/sculpt_paint/grease_pencil_draw_ops.cc +++ b/source/blender/editors/sculpt_paint/grease_pencil_draw_ops.cc @@ -160,7 +160,7 @@ static void GREASE_PENCIL_OT_brush_stroke(struct wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - paint_stroke_operator_properties(ot); + paint_stroke_operator_properties(ot, false); } /** \} */ @@ -262,4 +262,4 @@ void ED_operatortypes_grease_pencil_draw() WM_operatortype_append(GREASE_PENCIL_OT_draw_mode_toggle); } -/** \} */ \ No newline at end of file +/** \} */ diff --git a/source/blender/editors/sculpt_paint/paint_mask.cc b/source/blender/editors/sculpt_paint/paint_mask.cc index 4d6a39bedfe..b68550ee6fc 100644 --- a/source/blender/editors/sculpt_paint/paint_mask.cc +++ b/source/blender/editors/sculpt_paint/paint_mask.cc @@ -1533,7 +1533,6 @@ static void sculpt_gesture_apply_trim(SculptGestureContext *sgcontext) BKE_pbvh_build_bmesh(sgcontext->ss->pbvh, sculpt_mesh, sgcontext->ss->bm, - sgcontext->ss->bm_smooth_shading, sgcontext->ss->bm_log, sgcontext->ss->bm_idmap, sgcontext->ss->cd_vert_node_offset, @@ -1543,8 +1542,7 @@ static void sculpt_gesture_apply_trim(SculptGestureContext *sgcontext) sgcontext->ss->attrs.flags->bmesh_cd_offset, sgcontext->ss->attrs.valence->bmesh_cd_offset, sgcontext->ss->attrs.orig_co->bmesh_cd_offset, - sgcontext->ss->attrs.orig_no->bmesh_cd_offset, - sgcontext->ss->fast_draw); + sgcontext->ss->attrs.orig_no->bmesh_cd_offset); } else { // save result to mesh BMeshToMeshParams params = {0}; diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h index 01c044be4e5..fdada73be8c 100644 --- a/source/blender/makesdna/DNA_scene_types.h +++ b/source/blender/makesdna/DNA_scene_types.h @@ -2497,7 +2497,7 @@ typedef enum eSculptFlags { // SCULPT_SHOW_DIFFUSE = (1 << 9), /* deprecated */ /** If set, the mesh will be drawn with smooth-shading in dynamic-topology mode. */ - SCULPT_DYNTOPO_SMOOTH_SHADING = (1 << 10), + SCULPT_FLAG_UNUSED_10 = (1 << 10), /** If set, dynamic-topology brushes will subdivide short edges. */ SCULPT_DYNTOPO_SUBDIVIDE = (1 << 12), /* deprecated. */ @@ -2513,10 +2513,10 @@ typedef enum eSculptFlags { /* Don't display face sets in viewport. */ SCULPT_HIDE_FACE_SETS = (1 << 17), - SCULPT_DYNTOPO_FLAT_VCOL_SHADING = (1 << 18), + SCULPT_FLAG_UNUSED_8 = (1 << 18), /* Hides facesets/masks and forces indexed mode to save GPU bandwidth. */ - SCULPT_FAST_DRAW = (1 << 20), + SCULPT_FLAG_UNUSED_20 = (1 << 20), SCULPT_DYNTOPO_ENABLED = (1 << 21), } eSculptFlags; diff --git a/source/blender/makesrna/intern/rna_sculpt_paint.c b/source/blender/makesrna/intern/rna_sculpt_paint.c index af46bbf0eaf..893fdd98a77 100644 --- a/source/blender/makesrna/intern/rna_sculpt_paint.c +++ b/source/blender/makesrna/intern/rna_sculpt_paint.c @@ -373,8 +373,6 @@ static bool rna_Brush_mode_with_tool_poll(PointerRNA *ptr, PointerRNA value) return brush->ob_mode & mode; } -void SCULPT_update_flat_vcol_shading(Object *ob, Scene *scene); - static void rna_Sculpt_update(bContext *C, PointerRNA *UNUSED(ptr)) { Scene *scene = CTX_data_scene(C); @@ -383,15 +381,6 @@ static void rna_Sculpt_update(bContext *C, PointerRNA *UNUSED(ptr)) Object *ob = BKE_view_layer_active_object_get(view_layer); if (ob) { - if (ob->sculpt) { - SCULPT_update_flat_vcol_shading(ob, scene); - - BKE_object_sculpt_fast_draw_set( - ob, ((scene->toolsettings->sculpt->flags & SCULPT_FAST_DRAW) != 0)); - BKE_object_sculpt_dyntopo_smooth_shading_set( - ob, ((scene->toolsettings->sculpt->flags & SCULPT_DYNTOPO_SMOOTH_SHADING) != 0)); - } - DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); WM_main_add_notifier(NC_OBJECT | ND_MODIFIER | ND_DRAW, ob); } @@ -924,36 +913,6 @@ static void rna_def_sculpt(BlenderRNA *brna) "of Blender unit - higher value means smaller edge length)"); RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); - prop = RNA_def_property(srna, "use_smooth_shading", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flags", SCULPT_DYNTOPO_SMOOTH_SHADING); - RNA_def_property_ui_text(prop, - "Smooth Shading", - "Show faces in dynamic-topology mode with smooth " - "shading rather than flat shaded"); - RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE); - RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_Sculpt_update"); - - prop = RNA_def_property(srna, "use_fast_draw", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flags", SCULPT_FAST_DRAW); - RNA_def_property_ui_text(prop, - "Fast Draw Mode", - "Forces smooth shading" - "and disables drawing of masks and face sets" - "to speed up drawing. Useful for posing" - "high-poly meshes."); - RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE); - RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_Sculpt_update"); - - prop = RNA_def_property(srna, "use_flat_vcol_shading", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flags", SCULPT_DYNTOPO_FLAT_VCOL_SHADING); - RNA_def_property_ui_text( - prop, - "Draw Color Cells", - "Draw vertex colors in flat cells instead of smoothly interpolating." - "For debugging purposes only, does not effect rendering in eevee or cycles"); - RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE); - RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_Sculpt_update"); - const EnumPropertyItem *entry = rna_enum_brush_automasking_flag_items; do { prop = RNA_def_property(srna, entry->identifier, PROP_BOOLEAN, PROP_NONE); -- 2.30.2 From 8ad27d180e87a04503e23705740a00059e8671bd Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Wed, 31 May 2023 05:21:01 -0700 Subject: [PATCH 136/279] Add files deleted by merge error --- .../icons/ops.armature.extrude.cursor.dat | Bin 0 -> 1502 bytes release/datafiles/icons/ops.armature.extrude.dat | Bin 0 -> 1250 bytes .../icons/ops.curve.dupli_extrude_cursor.dat | Bin 0 -> 4202 bytes 3 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 release/datafiles/icons/ops.armature.extrude.cursor.dat create mode 100644 release/datafiles/icons/ops.armature.extrude.dat create mode 100644 release/datafiles/icons/ops.curve.dupli_extrude_cursor.dat diff --git a/release/datafiles/icons/ops.armature.extrude.cursor.dat b/release/datafiles/icons/ops.armature.extrude.cursor.dat new file mode 100644 index 0000000000000000000000000000000000000000..ec8f2432052d155cdedc2d4650857d7e88d87db1 GIT binary patch literal 1502 zcmeH`-)fss9LIf}95pr7q+aD}HyJekORQs-j*P(8-tQH1xzJh@sjVi1r zl~Xz8e~FPtu(J$x%T_JZ?|1B;?RdvM*9{g|_a5=c;C3*9Tn~Nh++GleIQN&)U>W|r zi)Zm{@i33((T_NU1dGC-4E>?+yV$|tMgwq1%WxUSGiru0b?9Cg7@g1I1lDQ$i_tZ@ zT1Pv9eAF%7YPC(nG(Kpy`n7dtS~RDHb3&bM8_3A~6kQ*G}U^1FQ?g|P7<~X44pWo?YmaI4HB-^Z?iYv*I zEP39*dh?LLoDx`NPr#Ek$$&ot2MS!#n{Ad8lfNE;L$A3Hnx3SELFm z?;)P&W%dA7QMDwM_8|f)qcY2JEXS779;8wdk%+2njjM4o==6@l-=`+WQb$BJj^|+t fJJRl39_MfMy59lAF!1mHpL(fpZMWOyueJUGq^9P# literal 0 HcmV?d00001 diff --git a/release/datafiles/icons/ops.armature.extrude.dat b/release/datafiles/icons/ops.armature.extrude.dat new file mode 100644 index 0000000000000000000000000000000000000000..2194c5bf55674e9945e3309e4779aa334ff4cb0a GIT binary patch literal 1250 zcmeH_%}&BV5P*3bpxCgX)@^F=;>A;iiwRm-EB-%G>a6?rAZ*vt6aody-F!iXV%Cee6 zh+&I)F(+hso)HQnE3&&q7Gf_i=EW?ZLpfB~LJVSfMr40j>Od7nliq5kG)zS|2Cmg| zT919x@%(8rPKNO?e)Am+p{Wy%nufcM(9Ic*Lv(;?*mV88u z*JMg#7>5yfV3A>0Rwg?Wmplg>1=Iy{R;mhN_>P zn#y^-t+bU()z)n7+R!1oK*|;{K$!I1)^tPdz&ubntvj5{zjv_dF8{sF|KVTl?i(cqc!dA} literal 0 HcmV?d00001 diff --git a/release/datafiles/icons/ops.curve.dupli_extrude_cursor.dat b/release/datafiles/icons/ops.curve.dupli_extrude_cursor.dat new file mode 100644 index 0000000000000000000000000000000000000000..509ca7c94408599c4062d72d186a6c029510038a GIT binary patch literal 4202 zcmeHKT~8Wm6!t#|1PP^7FSik8U|?Vv1`wnu#cI2XBPi%B!cHkwKBG1?^uyS%*lGxC zLFA*uP5~toTJO8zqSyKpdetA{p6PGcbaxZx%^S}9oO7P%naKe%zuG(lS62fA#Hi*b zc9S%s22yV!bSBbBjA>QGs2S0!i7^YQBQ1p0rng$iY0BoG^16K!E7O$Kj@hj?n-gEvN`S3M)!ovv+M}EeKRYQv}f5hPkNpAro5IV*ZgeoR%m8r zZgu9jsjwNa9A`YQyY$>ohg7V)MIwj^M-!cynTQo73RJoRf3rdE&0~9_cmDPq>V3gUddR zdyH<+@+9pD&aB)DP13G;%M$6G^4k2CrP<(A_*Q5tJR21JxqlZNqB^Yx92!ua>WOPxBuomh;Dl*-SRK z{rJgN?rAnx*vlWh+poMmKFlBN?mXYkJ$;#dvG;tpoPS?9KHSe8K6~@(@L6H+-F__n za1$8g4|k$liFi7Zc$7|Tr_$N&R5r8yxbn7kP|jyFPqwnTCtJHaxu=D_((C*|{-E?) zV1Hi__+Mt9@4n3L=U(iU^Tj{j7wRYFYUP;c&e_jRwp2N;AMvL=_qoXx*i!xIw9{$w zr|kk)oG7Iv>uu?0NN^ z?N;0E8e2c9)yh?#1B?aU6Hq(_W{v}9wsZ=doo2hxE^%-Z3wF^3p*2gy2>4L&JIIGp_C-oDy1Wt?XVy6VvKs&qBcZZW5^8u($TnI6&K0{=M5-DZli&(cPFzPsh;&S1R1(+mVN{HW&_Qg( zsKke{K~#(ms)p2Z)irbkQ(_|q2{wp})beqq>KZP^MTBHrsZk=sCK(|y4{4O>2r^8{ zO)~S4Nroxaazv_@sfKWcNlwZUkw$_@fmcCXN5v3}FyJ&Qu|d5U6@!XQEk{St8<^4{ z(TlM`=vIs?aj8Lqi^i3RLZdXva48`Hl^f^?sDO@Iu9hKEM5>X1Q?XubkU+K6B!eZ# z6}SS}VWBrPKfDMv28ocx6#_1xpeZ)VsOUn>d!LT4Z-$aogo=9auO>rDI_+nuh(8?6 ztU??J`x$?DA-1@&u<6ePQh+jn@LFVX!<$^#T#K$nLX0=*OHye%7Gl;P_>%q%m0sVZ zW9tv-SRl2yK}8p13o$xQZ>(=pktK$T0&hH&^fM4gg21_&41@!Wp8*xn@n@D(fH48k zf$q3BxfWez)*{{{7563S*jhBi_>#*he+E=GXy{msQBf+o1PpW>xS)GEUtS>3xP#tU3TPK}rr~2jVpKvwUhA@yP0tTnLa1!FLe|`CS(d%9IdKZ_y z?>|1zE_xTe@0W1CJnI22WZ-o19rC^2*#+bw6ta;2_U+qMfA^ok&zgb6)#+7#_n$$3 j2L2l}@Uy=6eVzV)On(Oce={&JFaYo7|Lp#^e*6Chrm2=& literal 0 HcmV?d00001 -- 2.30.2 From 93a92fa0fde116ccc32d4a0f6ac1cc6def1540db Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Wed, 31 May 2023 05:22:56 -0700 Subject: [PATCH 137/279] temp-sculpt-dyntopo: Remove blf_font.c change --- source/blender/blenfont/intern/blf_font.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/blenfont/intern/blf_font.c b/source/blender/blenfont/intern/blf_font.c index b122a3f8143..f794dc42a14 100644 --- a/source/blender/blenfont/intern/blf_font.c +++ b/source/blender/blenfont/intern/blf_font.c @@ -1690,7 +1690,7 @@ bool blf_font_size(FontBLF *font, float size) font->ft_size->generic.finalizer = blf_size_finalizer; } else { - if (FT_Set_Char_Size(font->face, 0, (FT_F26Dot6)ft_size, BLF_DPI, BLF_DPI) != FT_Err_Ok) { + if (FT_Set_Char_Size(font->face, 0, ft_size, BLF_DPI, BLF_DPI) != FT_Err_Ok) { return false; } font->ft_size = font->face->size; -- 2.30.2 From f498ec60f500113d2f4ccd82cb2382e3219c7665 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Wed, 31 May 2023 17:08:29 -0700 Subject: [PATCH 138/279] temp-sculpt-dyntopo: Remove more code, and cleanup dyntopo debug code * Removed show_ids option * Cleaned up dyntopo mesh validation code. It now uses BMesh's element checkers. --- scripts/startup/bl_ui/space_view3d.py | 3 - scripts/startup/bl_ui/space_view3d_toolbar.py | 4 +- source/blender/blenkernel/BKE_paint.h | 1 - source/blender/blenkernel/BKE_pbvh.h | 10 +- source/blender/blenkernel/intern/dyntopo.cc | 46 +- .../blenkernel/intern/dyntopo_collapse.cc | 8 +- .../blenkernel/intern/dyntopo_intern.hh | 578 +++++++----------- source/blender/blenkernel/intern/paint.cc | 7 +- source/blender/blenkernel/intern/pbvh.cc | 14 +- source/blender/blenlib/BLI_compiler_attrs.h | 5 +- source/blender/bmesh/intern/bmesh_core.c | 5 +- .../blender/draw/engines/basic/basic_engine.c | 2 +- .../draw/engines/overlay/overlay_facing.cc | 2 +- .../draw/engines/overlay/overlay_fade.cc | 2 +- .../engines/overlay/overlay_mode_transfer.cc | 3 +- .../draw/engines/overlay/overlay_sculpt.cc | 16 +- .../draw/engines/overlay/overlay_wireframe.cc | 2 +- .../shaders/infos/overlay_sculpt_info.hh | 6 +- .../shaders/overlay_sculpt_mask_frag.glsl | 48 +- .../shaders/overlay_sculpt_mask_vert.glsl | 6 +- .../draw/engines/workbench/workbench_engine.c | 2 +- source/blender/draw/intern/DRW_render.h | 3 +- .../blender/draw/intern/draw_manager_data.cc | 9 +- source/blender/draw/intern/draw_pbvh.cc | 18 - source/blender/draw/intern/draw_pbvh.h | 1 - source/blender/editors/sculpt_paint/sculpt.cc | 1 + .../editors/sculpt_paint/sculpt_detail.cc | 3 + .../editors/sculpt_paint/sculpt_undo.cc | 2 +- source/blender/makesdna/DNA_scene_types.h | 6 +- source/blender/makesdna/DNA_view3d_types.h | 6 - source/blender/makesrna/intern/rna_scene.c | 5 +- source/blender/makesrna/intern/rna_space.c | 21 - .../blender/windowmanager/intern/wm_files.cc | 5 +- 33 files changed, 301 insertions(+), 549 deletions(-) diff --git a/scripts/startup/bl_ui/space_view3d.py b/scripts/startup/bl_ui/space_view3d.py index 85777d03386..d196f5a0ba8 100644 --- a/scripts/startup/bl_ui/space_view3d.py +++ b/scripts/startup/bl_ui/space_view3d.py @@ -6857,9 +6857,6 @@ class VIEW3D_PT_overlay_sculpt(Panel): sub.active = overlay.show_sculpt_face_sets row.prop(overlay, "sculpt_mode_face_sets_opacity", text="Face Sets") - row = layout.row(align=True) - row.prop(overlay, "show_sculpt_ids") - class VIEW3D_PT_overlay_sculpt_curves(Panel): bl_space_type = 'VIEW_3D' bl_context = ".curves_sculpt" diff --git a/scripts/startup/bl_ui/space_view3d_toolbar.py b/scripts/startup/bl_ui/space_view3d_toolbar.py index ed27c5d333f..91fa9094489 100644 --- a/scripts/startup/bl_ui/space_view3d_toolbar.py +++ b/scripts/startup/bl_ui/space_view3d_toolbar.py @@ -1056,8 +1056,8 @@ class VIEW3D_PT_sculpt_options(Panel, View3DPaintPanel): col = layout.column(heading="Display", align=True) col.prop(sculpt, "show_low_resolution") col.prop(sculpt, "use_sculpt_delay_updates") - col.prop(sculpt, "use_deform_only") - col.prop(tool_settings, "show_origco") + col.prop(sculpt, "use_deform_only") + col.prop(tool_settings, "show_origco") #NotForPR col.prop(context.object.data, "sculpt_ignore_uvs") col.label(text="Smooth Boundaries") diff --git a/source/blender/blenkernel/BKE_paint.h b/source/blender/blenkernel/BKE_paint.h index 0ef519a8dea..7186a38e732 100644 --- a/source/blender/blenkernel/BKE_paint.h +++ b/source/blender/blenkernel/BKE_paint.h @@ -879,7 +879,6 @@ struct SculptSession { /* Used to derive initial tip rotation. */ float last_grab_delta[3]; - bool save_temp_layers; const float (*vert_normals)[3]; int last_automasking_settings_hash; diff --git a/source/blender/blenkernel/BKE_pbvh.h b/source/blender/blenkernel/BKE_pbvh.h index d4d57b9cebb..11b1c1bc934 100644 --- a/source/blender/blenkernel/BKE_pbvh.h +++ b/source/blender/blenkernel/BKE_pbvh.h @@ -869,17 +869,13 @@ bool BKE_pbvh_node_has_vert_with_normal_update_tag(PBVH *pbvh, PBVHNode *node); // void BKE_pbvh_node_BB_reset(PBVHNode *node); // void BKE_pbvh_node_BB_expand(PBVHNode *node, float co[3]); -bool BKE_pbvh_draw_mask(const PBVH *pbvh); -bool BKE_pbvh_has_mask(const PBVH *pbvh); - -void pbvh_show_mask_set(PBVH *pbvh, bool show_mask); - -bool BKE_pbvh_draw_face_sets(PBVH *pbvh); -void pbvh_show_face_sets_set(PBVH *pbvh, bool show_face_sets); bool pbvh_has_mask(const PBVH *pbvh); bool pbvh_has_face_sets(PBVH *pbvh); +void pbvh_show_mask_set(PBVH *pbvh, bool show_mask); +void pbvh_show_face_sets_set(PBVH *pbvh, bool show_face_sets); + /* Parallelization. */ void BKE_pbvh_parallel_range_settings(struct TaskParallelSettings *settings, diff --git a/source/blender/blenkernel/intern/dyntopo.cc b/source/blender/blenkernel/intern/dyntopo.cc index b6b3855b526..c3b66718502 100644 --- a/source/blender/blenkernel/intern/dyntopo.cc +++ b/source/blender/blenkernel/intern/dyntopo.cc @@ -803,7 +803,7 @@ bool check_face_is_tri(PBVH *pbvh, BMFace *f) BMLoop *l = f->l_first; do { - validate_vert(pbvh, pbvh->header.bm, l->v, true, true); + validate_vert(pbvh, l->v, CHECK_VERT_ALL); if (l->e->head.index == -1) { l->e->head.index = 0; @@ -898,7 +898,7 @@ bool check_face_is_tri(PBVH *pbvh, BMFace *f) } } while ((l = l->next) != f2->l_first); - validate_face(pbvh, pbvh->header.bm, f2, false, true); + validate_face(pbvh, f2, CHECK_FACE_MANIFOLD); BKE_pbvh_bmesh_add_face(pbvh, f2, false, true); // BM_log_face_post(pbvh->bm_log, f2); @@ -1210,7 +1210,7 @@ bool _check_vert_fan_are_tris(PBVH *pbvh, BMVert *v) Vector fs; - validate_vert(pbvh, pbvh->header.bm, v, true, true); + validate_vert(pbvh, v, CHECK_VERT_ALL); if (v->head.htype != BM_VERT) { printf("non-vert %p fed to %s\n", v, __func__); @@ -1677,10 +1677,12 @@ static bool cleanup_valence_3_4(EdgeQueueContext *ectx, PBVH *pbvh) continue; } - validate_vert(pbvh, pbvh->header.bm, v, false, true); + validate_vert(pbvh, v, CHECK_VERT_ALL); + check_vert_fan_are_tris(pbvh, v); pbvh_check_vert_boundary(pbvh, v); - validate_vert(pbvh, pbvh->header.bm, v, true, true); + + validate_vert(pbvh, v, CHECK_VERT_ALL); BKE_pbvh_bmesh_check_valence(pbvh, {(intptr_t)v}); @@ -1815,7 +1817,7 @@ static bool cleanup_valence_3_4(EdgeQueueContext *ectx, PBVH *pbvh) continue; } - validate_vert(pbvh, pbvh->header.bm, v, false, true); + validate_vert(pbvh, v, CHECK_VERT_ALL); l = v->e->l; @@ -1855,7 +1857,7 @@ static bool cleanup_valence_3_4(EdgeQueueContext *ectx, PBVH *pbvh) vs[1] = ls[1]->v; vs[2] = ls[2]->v; - validate_vert(pbvh, pbvh->header.bm, v, false, false); + validate_vert(pbvh, v, CHECK_VERT_ALL); pbvh_boundary_update_bmesh(pbvh, vs[0]); pbvh_boundary_update_bmesh(pbvh, vs[1]); @@ -1876,7 +1878,7 @@ static bool cleanup_valence_3_4(EdgeQueueContext *ectx, PBVH *pbvh) normal_tri_v3( f1->no, f1->l_first->v->co, f1->l_first->next->v->co, f1->l_first->prev->v->co); - validate_face(pbvh, pbvh->header.bm, f1, false, false); + validate_face(pbvh, f1, CHECK_FACE_NONE); } BMFace *f2 = nullptr; @@ -1924,7 +1926,7 @@ static bool cleanup_valence_3_4(EdgeQueueContext *ectx, PBVH *pbvh) f2->no, f2->l_first->v->co, f2->l_first->next->v->co, f2->l_first->prev->v->co); BM_log_face_added(pbvh->header.bm, pbvh->bm_log, f2); - validate_face(pbvh, pbvh->header.bm, f2, false, false); + validate_face(pbvh, f2, CHECK_FACE_NONE); } if (f1) { @@ -1944,15 +1946,15 @@ static bool cleanup_valence_3_4(EdgeQueueContext *ectx, PBVH *pbvh) BM_log_face_added(pbvh->header.bm, pbvh->bm_log, f1); } - validate_vert(pbvh, pbvh->header.bm, v, false, false); + validate_vert(pbvh, v, CHECK_VERT_ALL); pbvh_kill_vert(pbvh, v, true, true); if (f1 && !bm_elem_is_free((BMElem *)f1, BM_FACE)) { - check_face_is_manifold(pbvh, pbvh->header.bm, f1); + check_face_is_manifold(pbvh, f1); } if (f2 && !bm_elem_is_free((BMElem *)f2, BM_FACE)) { - check_face_is_manifold(pbvh, pbvh->header.bm, f2); + check_face_is_manifold(pbvh, f2); } } @@ -2712,7 +2714,7 @@ static void pbvh_split_edges( BKE_pbvh_bmesh_check_origdata(eq_ctx->ss, e->v1, pbvh->stroke_id); BKE_pbvh_bmesh_check_origdata(eq_ctx->ss, e->v2, pbvh->stroke_id); - validate_edge(pbvh, pbvh->header.bm, e, true, true); + validate_edge(pbvh, e); BM_idmap_check_assign(pbvh->bm_idmap, (BMElem *)e->v1); BM_idmap_check_assign(pbvh->bm_idmap, (BMElem *)e->v2); @@ -2742,9 +2744,9 @@ static void pbvh_split_edges( PBVH_CHECK_NAN(newv->co); - validate_edge(pbvh, pbvh->header.bm, e, true, true); - validate_edge(pbvh, pbvh->header.bm, newe, true, true); - validate_vert(pbvh, pbvh->header.bm, newv, true, true); + validate_edge(pbvh, e); + validate_edge(pbvh, newe); + validate_vert(pbvh, newv, CHECK_VERT_ALL); newv->head.hflag |= SPLIT_TAG; @@ -2927,7 +2929,7 @@ static void pbvh_split_edges( continue; } - validate_face(pbvh, bm, f2, false, true); + validate_face(pbvh, f2, CHECK_FACE_MANIFOLD); bool log_edge = true; BMFace *newf = nullptr; @@ -2958,12 +2960,12 @@ static void pbvh_split_edges( new_edges.append(exist_e); #endif - check_face_is_manifold(pbvh, bm, newf); - check_face_is_manifold(pbvh, bm, f2); - check_face_is_manifold(pbvh, bm, f); + check_face_is_manifold(pbvh, newf); + check_face_is_manifold(pbvh, f2); + check_face_is_manifold(pbvh, f); - validate_face(pbvh, bm, f2, false, true); - validate_face(pbvh, bm, newf, false, true); + validate_face(pbvh, f2, CHECK_FACE_MANIFOLD); + validate_face(pbvh, newf, CHECK_FACE_MANIFOLD); if (log_edge) { BM_log_edge_added(bm, pbvh->bm_log, rl->e); diff --git a/source/blender/blenkernel/intern/dyntopo_collapse.cc b/source/blender/blenkernel/intern/dyntopo_collapse.cc index fa562843a24..4a890bad730 100644 --- a/source/blender/blenkernel/intern/dyntopo_collapse.cc +++ b/source/blender/blenkernel/intern/dyntopo_collapse.cc @@ -586,7 +586,7 @@ BMVert *pbvh_bmesh_collapse_edge(PBVH *pbvh, const int mupdateflag = SCULPTFLAG_NEED_VALENCE; // updateflag |= SCULPTFLAG_NEED_TRIANGULATE; // to check for non-manifold flaps - validate_edge(pbvh, pbvh->header.bm, e, true, true); + validate_edge(pbvh, e); check_vert_fan_are_tris(pbvh, e->v1); check_vert_fan_are_tris(pbvh, e->v2); @@ -622,7 +622,7 @@ BMVert *pbvh_bmesh_collapse_edge(PBVH *pbvh, bool uvs_snapped = pbvh_bmesh_collapse_edge_uvs(pbvh, e, v_conn, v_del, eq_ctx); - validate_vert_faces(pbvh, pbvh->header.bm, v_conn, false, true); + validate_vert(pbvh, v_conn, CHECK_VERT_FACES | CHECK_VERT_NODE_ASSIGNED); BMEdge *e2; @@ -669,7 +669,7 @@ BMVert *pbvh_bmesh_collapse_edge(PBVH *pbvh, return v_conn; } - validate_vert_faces(pbvh, pbvh->header.bm, v_conn, false, true); + validate_vert(pbvh, v_conn, CHECK_VERT_FACES | CHECK_VERT_NODE_ASSIGNED); /* Flag boundaries for update. */ e2 = v_conn->e; @@ -732,7 +732,7 @@ BMVert *pbvh_bmesh_collapse_edge(PBVH *pbvh, check_for_fins(pbvh, v_conn); } - validate_vert_faces(pbvh, pbvh->header.bm, v_conn, false, true); + validate_vert(pbvh, v_conn, CHECK_VERT_FACES | CHECK_VERT_NODE_ASSIGNED); bm_logstack_pop(); PBVH_CHECK_NAN(v_conn->co); diff --git a/source/blender/blenkernel/intern/dyntopo_intern.hh b/source/blender/blenkernel/intern/dyntopo_intern.hh index 768c5091bdd..dd6d2f2a221 100644 --- a/source/blender/blenkernel/intern/dyntopo_intern.hh +++ b/source/blender/blenkernel/intern/dyntopo_intern.hh @@ -10,6 +10,7 @@ #include "BLI_math_vector_types.hh" #include "BLI_rand.hh" #include "BLI_set.hh" +#include "BLI_utildefines.h" #include "bmesh.h" #include "pbvh_intern.hh" @@ -395,113 +396,58 @@ void BKE_pbvh_bmesh_add_face(PBVH *pbvh, struct BMFace *f, bool log_face, bool f /**** Debugging Tools ********/ -inline void fix_mesh(PBVH *pbvh, BMesh *bm) -{ - BMIter iter; - BMVert *v; - BMEdge *e; - BMFace *f; - - printf("fixing mesh. . .\n"); - - BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) { - v->e = nullptr; - dyntopo_add_flag(pbvh, v, SCULPTFLAG_NEED_VALENCE | SCULPTFLAG_NEED_TRIANGULATE); - } - - BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) { - e->v1_disk_link.next = e->v1_disk_link.prev = nullptr; - e->v2_disk_link.next = e->v2_disk_link.prev = nullptr; - e->l = nullptr; - - if (e->v1 == e->v2) { - bm_kill_only_edge(bm, e); - } - } - - // rebuild disk cycles - BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) { - if (BM_edge_exists(e->v1, e->v2)) { - printf("duplicate edge %p!\n", e); - bm_kill_only_edge(bm, e); - - continue; - } - - bmesh_disk_edge_append(e, e->v1); - bmesh_disk_edge_append(e, e->v2); - } - - BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { - BMLoop *l = f->l_first; - - do { - if (f->len < 3) { - break; - } - - if (l->next->v == l->v) { - BMLoop *l_del = l->next; - - l->next = l_del->next; - l_del->next->prev = l; - - f->len--; - - if (f->l_first == l_del) { - f->l_first = l; - } - - bm_kill_only_loop(bm, l_del); - - if (f->len < 3) { - break; - } - } - } while ((l = l->next) != f->l_first); - - if (f->len < 3) { - int ni = BM_ELEM_CD_GET_INT(f, pbvh->cd_face_node_offset); - - if (ni >= 0 && ni < pbvh->totnode && (pbvh->nodes[ni].flag & PBVH_Leaf)) { - BLI_table_gset_remove(pbvh->nodes[ni].bm_faces, f, nullptr); - } - - bm_kill_only_face(bm, f); - continue; - } - - do { - l->e = BM_edge_exists(l->v, l->next->v); - - if (!l->e) { - l->e = BM_edge_create(bm, l->v, l->next->v, nullptr, BM_CREATE_NOP); - } - - bmesh_radial_loop_append(l->e, l); - } while ((l = l->next) != f->l_first); - } - - bm->elem_table_dirty |= BM_VERT | BM_EDGE | BM_FACE; - bm->elem_index_dirty |= BM_VERT | BM_EDGE | BM_FACE; - - printf("done fixing mesh.\n"); -} - +/* Note: you will have to uncomment FORCE_BMESH_CHECK in bmesh_core.cc for this + * to work in RelWithDebInfo builds. + */ //#define CHECKMESH -//#define TEST_INVALID_NORMALS + +enum eValidateVertFlags { + CHECK_VERT_NONE = 0, + CHECK_VERT_MANIFOLD = (1 << 0), + CHECK_VERT_NODE_ASSIGNED = (1 << 1), + CHECK_VERT_FACES = (1 << 2), + CHECK_VERT_ALL = (1 << 0) | (1 << 1) /* Don't include CHECK_VERT_FACES */ +}; +ENUM_OPERATORS(eValidateVertFlags, CHECK_VERT_FACES); + +enum eValidateFaceFlags { + CHECK_FACE_NONE = 0, + CHECK_FACE_NODE_ASSIGNED = (1 << 1), + CHECK_FACE_ALL = (1 << 1), + CHECK_FACE_MANIFOLD = (1 << 2), +}; +ENUM_OPERATORS(eValidateFaceFlags, CHECK_FACE_MANIFOLD); #ifndef CHECKMESH -# define validate_vert(pbvh, bm, v, autofix, check_manifold) true -# define validate_edge(pbvh, bm, e, autofix, check_manifold) true -# define validate_face(pbvh, bm, f, autofix, check_manifold) true -# define validate_vert_faces(pbvh, bm, v, autofix, check_manifold) true -# define check_face_is_manifold(pbvh, bm, f) true + +template inline bool validate_elem(PBVH *pbvh, T *elem){return true}; +inline bool validate_vert(PBVH *pbvh, BMVert *v, eValidateVertFlags flags = CHECK_VERT_NONE) +{ + return true; +} +inline bool validate_edge(PBVH *pbvh, BMEdge *e) +{ + return true; +} +inline bool validate_loop(PBVH *pbvh, BMLoop *l) +{ + return true; +} +inline bool validate_face(PBVH *pbvh, BMFace *f, eValidateFaceFlags flags = CHECK_FACE_NONE) +{ + return true; +} +inline bool check_face_is_manifold(PBVH *pbvh, BMFace *f) +{ + return true; +} #else +# include "../bmesh/intern/bmesh_private.h" +# include +# include -# define CHECKMESH_ATTR ATTR_NO_OPT - -CHECKMESH_ATTR static void _debugprint(const char *fmt, ...) +namespace blender::bke::dyntopo::debug { +static void debug_printf(const char *fmt, ...) { va_list args; va_start(args, fmt); @@ -509,282 +455,212 @@ CHECKMESH_ATTR static void _debugprint(const char *fmt, ...) va_end(args); } -CHECKMESH_ATTR static bool check_face_is_manifold(PBVH *pbvh, BMesh *bm, BMFace *f) +template static char bm_get_htype() { - BMLoop *l = f->l_first; + if constexpr (std::is_same_v) { + return BM_VERT; + } + if constexpr (std::is_same_v) { + return BM_EDGE; + } + if constexpr (std::is_same_v) { + return BM_LOOP; + } + if constexpr (std::is_same_v) { + return BM_FACE; + } + return 0; +} + +template static const char *get_type_name() +{ + if constexpr (std::is_same_v) { + return "vertex"; + } + if constexpr (std::is_same_v) { + return "edge"; + } + if constexpr (std::is_same_v) { + return "loop"; + } + if constexpr (std::is_same_v) { + return "face"; + } + + return "(invalid element)"; +} +static const char *get_type_name(char htype) +{ + switch (htype) { + case BM_VERT: + return "vertex"; + case BM_EDGE: + return "edge"; + case BM_LOOP: + return "loop"; + case BM_FACE: + return "face"; + } + + return "(invalid element)"; +} +} // namespace blender::bke::dyntopo::debug + +template ATTR_NO_OPT static bool validate_elem(PBVH *pbvh, T *elem) +{ + using namespace blender::bke::dyntopo::debug; + + if (!v) { + debug_printf("%s was null\n", get_type_name); + return false; + } + + if (elem->head.htype != get_type_htype()) { + debug_printf("%p had wrong type: expected a %s but got %s (type %d).\n", + elem, + get_type_name(), + get_type_name(elem->head.htype)); + return false; + } + + int ret = bmesh_elem_check(static_cast(elem), bm_get_htype()); + + if (ret) { + debug_printf("%s (%p) failed integrity checks with code %d\n", get_type_name(), elem, ret); + return false; + } + + return true; +} + +ATTR_NO_OPT static bool check_face_is_manifold(PBVH *pbvh, BMFace *f) +{ + using namespace blender::bke::dyntopo::debug; + + BMLoop *l = f->l_first; do { if (l->radial_next != l && l->radial_next->radial_next != l) { - //_debugprint("non-manifold edge in loop\n"); - - BMVert *v1 = l->e->v1, *v2 = l->e->v2; - - for (int i = 0; i < 2; i++) { - BMVert *v = i ? v2 : v1; - BMEdge *e = v->e; - - if (!e) { - continue; - } - - int i = 0; - - do { - if (!e) { - break; - } - - bool same = e->v1 == v1 && e->v2 == v2; - same = same || (e->v1 == v2 && e->v2 == v1); - - if (same && e != l->e) { - // printf("duplicate edges in face!\n"); - } - - if (i++ > 5000) { - printf("infinite loop in edge disk cycle! v: %p, e: %p\n", v, e); - break; - } - } while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e); - } - l->e->head.hflag |= BM_ELEM_SELECT; - l->f->head.hflag |= BM_ELEM_SELECT; - l->v->head.hflag |= BM_ELEM_SELECT; - - // pbvh->dyntopo_stop = true; + debug_printf("Face %p has non-manifold edge %p\n", f, l->e); return false; } - -# ifdef TEST_INVALID_NORMALS - if (l != l->radial_next && l->v == l->radial_next->v) { - _debugprint("invalid normals\n"); - return false; - } -# endif } while ((l = l->next) != f->l_first); return true; } -CHECKMESH_ATTR -static bool validate_vert(PBVH *pbvh, BMesh *bm, BMVert *v, bool autofix, bool check_manifold) +ATTR_NO_OPT static bool validate_face(PBVH *pbvh, + BMFace *f, + eValidateFaceFlags flags = CHECK_FACE_NONE) { - if (v->head.htype != BM_VERT) { - _debugprint("bad vertex\n"); + if (!validate_elem(pbvh, f)) { return false; } - BMEdge *e = v->e; - int i = 0; + bool ok = true; - if (!e) { - return true; + if (flags & CHECK_FACE_MANIFOLD) { + ok = ok && check_face_is_manifold(pbvh, f); } - do { - if (e->v1 != v && e->v2 != v) { - _debugprint("edge does not contain v\n"); - goto error; - } - - if (e->l) { - int j = 0; - - BMLoop *l = e->l; - do { - if (l->e->v1 != v && l->e->v2 != v) { - _debugprint("loop's edges doesn't contain v\n"); - goto error; - } - - if (l->v != v && l->next->v != v) { - _debugprint("loop and loop->next don't contain v\n"); - goto error; - } - - j++; - if (j > 1000) { - _debugprint("corrupted radial cycle\n"); - goto error; - } - - if (check_manifold) { - check_face_is_manifold(pbvh, bm, l->f); - } - } while ((l = l->radial_next) != e->l); - } - if (i > 10000) { - _debugprint("corrupted disk cycle\n"); - goto error; - } - - e = BM_DISK_EDGE_NEXT(e, v); - i++; - } while (e != v->e); - - return true; - -error: - - if (autofix) { - fix_mesh(pbvh, bm); - } - - return false; -} - -CHECKMESH_ATTR -static bool validate_edge(PBVH *pbvh, BMesh *bm, BMEdge *e, bool autofix, bool check_manifold) -{ - if (e->head.htype != BM_EDGE) { - _debugprint("corrupted edge!\n"); - return false; - } - - bool ret = validate_vert(pbvh, bm, e->v1, false, check_manifold) && - validate_vert(pbvh, bm, e->v2, false, check_manifold); - - if (!ret && autofix) { - fix_mesh(pbvh, bm); - } - - return ret; -} - -CHECKMESH_ATTR bool face_verts_are_same(PBVH *pbvh, BMesh *bm, BMFace *f1, BMFace *f2) -{ - BMLoop *l1 = f1->l_first; - BMLoop *l2 = f2->l_first; - - int count1 = 0; - - do { - count1++; - } while ((l1 = l1->next) != f1->l_first); - - do { - bool ok = false; - - do { - if (l2->v == l1->v) { - ok = true; - break; - } - } while ((l2 = l2->next) != f2->l_first); - - if (!ok) { + int ni = BM_ELEM_CD_GET_INT(f, pbvh->cd_face_node_offset); + if (ni < 0 || ni >= pbvh->totnode) { + if (ni != DYNTOPO_NODE_NONE || flags & CHECK_FACE_NODE_ASSIGNED) { + debug_printf("face %p has corrupted node index %d\n", f, ni); return false; } - } while ((l1 = l1->next) != f1->l_first); + else { + return ok; + } + } - return true; -} - -CHECKMESH_ATTR -static bool validate_face(PBVH *pbvh, BMesh *bm, BMFace *f, bool autofix, bool check_manifold) -{ - if (f->head.htype != BM_FACE) { - _debugprint("corrupted edge!\n"); + PBVHNode *node = &pbvh->nodes[ni]; + if (!(node->flag & PBVH_Leaf)) { + printf("face %p has corrupted node index.", f); return false; } - BMLoop **ls = nullptr; - BLI_array_staticdeclare(ls, 32); - - BMLoop *l = f->l_first; - int i = 0; - do { - i++; - - if (i > 100000) { - _debugprint("Very corrupted face!\n"); - goto error; - } - - if (!validate_edge(pbvh, bm, l->e, false, check_manifold)) { - goto error; - } - - BMLoop *l2 = l->radial_next; - do { - if (l2->f != f && face_verts_are_same(pbvh, bm, l2->f, f)) { - _debugprint("Duplicate faces!\n"); - goto error; - } - } while ((l2 = l2->radial_next) != l); - - BLI_array_append(ls, l); - } while ((l = l->next) != f->l_first); - - for (int i = 0; i < BLI_array_len(ls); i++) { - BMLoop *l1 = ls[i]; - for (int j = 0; j < BLI_array_len(ls); j++) { - BMLoop *l2 = ls[j]; - - if (i != j && l1->v == l2->v) { - _debugprint("duplicate verts in face!\n"); - goto error; - } - - if (BM_edge_exists(l->v, l->next->v) != l->e) { - _debugprint("loop has wrong edge!\n"); - goto error; - } - } - } - - BLI_array_free(ls); - return true; - -error: - BLI_array_free(ls); - - if (autofix) { - fix_mesh(pbvh, bm); - } - - return false; -} - -CHECKMESH_ATTR bool validate_vert_faces( - PBVH *pbvh, BMesh *bm, BMVert *v, int autofix, bool check_manifold) -{ - if (!validate_vert(pbvh, bm, v, autofix, check_manifold)) { + if (!BLI_table_gset_has(node->bm_faces, static_cast(f))) { + printf("face is not in node->bm_faces.\n"); return false; } - if (!v->e) { - return true; - } - - BMEdge *e = v->e; - do { - BMLoop *l = e->l; - - if (!l) { - continue; - } - - do { - if (!validate_edge(pbvh, bm, l->e, false, false)) { - goto error; - } - - if (!validate_face(pbvh, bm, l->f, false, check_manifold)) { - goto error; - } - } while ((l = l->radial_next) != e->l); - } while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e); - - return true; - -error: - - if (autofix) { - fix_mesh(pbvh, bm); - } - - return false; + return ok; } + +ATTR_NO_OPT static bool validate_vert(PBVH *pbvh, + BMVert *v, + eValidateVertFlags flags = CHECK_VERT_NONE) +{ + using namespace blender::bke::dyntopo::debug; + + if (!validate_elem(pbvh, v)) { + return false; + } + + if (flags & CHECK_VERT_FACES) { + BMIter iter; + BMFace *f; + BM_ITER_ELEM (f, &iter, v, BM_FACES_OF_VERT) { + if (!validate_face(pbvh, + f, + flags & CHECK_VERT_NODE_ASSIGNED ? CHECK_FACE_NODE_ASSIGNED : + CHECK_FACE_NONE)) + { + return false; + } + } + } + + bool ok = true; + + if (flags & CHECK_VERT_MANIFOLD) { + BMIter iter; + BMFace *f; + + BM_ITER_ELEM (f, &iter, v, BM_FACES_OF_VERT) { + ok = ok && check_face_is_manifold(pbvh, f); + } + } + + int ni = BM_ELEM_CD_GET_INT(v, pbvh->cd_vert_node_offset); + + if (ni < 0 || ni >= pbvh->totnode) { + if (ni != DYNTOPO_NODE_NONE || flags & CHECK_VERT_NODE_ASSIGNED) { + debug_printf("vertex %p has corrupted node index %d\n", v, ni); + return false; + } + else { + return ok; + } + } + + PBVHNode *node = &pbvh->nodes[ni]; + if (!(node->flag & PBVH_Leaf)) { + printf("Vertex %p has corrupted node index.", v); + return false; + } + + if (!BLI_table_gset_haskey(node->bm_unique_verts, static_cast(v))) { + printf("Vertex %p is not in node->bm_unique_verts\n"); + return false; + } + if (BLI_table_gset_haskey(node->bm_other_verts, static_cast(v))) { + printf("Vertex %p is inside of node->bm_other_verts\n"); + return false; + } + + return ok; +} + +ATTR_NO_OPT static bool validate_edge(PBVH *pbvh, BMEdge *e) +{ + return validate_elem(pbvh, e); +} +ATTR_NO_OPT static bool validate_loop(PBVH *pbvh, BMLoop *l) +{ + return validate_elem(pbvh, l); +} + #endif diff --git a/source/blender/blenkernel/intern/paint.cc b/source/blender/blenkernel/intern/paint.cc index 0e34e625b1e..3ad30fdf48a 100644 --- a/source/blender/blenkernel/intern/paint.cc +++ b/source/blender/blenkernel/intern/paint.cc @@ -1797,9 +1797,6 @@ static void sculpt_update_object( ss->building_vp_handle = false; ss->scene = scene; - if (scene->toolsettings) { - ss->save_temp_layers = scene->toolsettings->save_temp_layers; - } ss->shapekey_active = (mmd == nullptr) ? BKE_keyblock_from_object(ob) : nullptr; @@ -3216,7 +3213,7 @@ static bool sculpt_attribute_create(SculptSession *ss, } int index = CustomData_get_named_layer_index(cdata, proptype, name); - if (!permanent && !ss->save_temp_layers) { + if (!permanent) { cdata->layers[index].flag |= CD_FLAG_TEMPORARY | CD_FLAG_NOCOPY; } @@ -3247,7 +3244,7 @@ static bool sculpt_attribute_create(SculptSession *ss, } int index = CustomData_get_named_layer_index(cdata, proptype, name); - if (!permanent && !ss->save_temp_layers) { + if (!permanent) { cdata->layers[index].flag |= CD_FLAG_TEMPORARY | CD_FLAG_NOCOPY; } diff --git a/source/blender/blenkernel/intern/pbvh.cc b/source/blender/blenkernel/intern/pbvh.cc index 46a45cdd627..5f52ec3a04e 100644 --- a/source/blender/blenkernel/intern/pbvh.cc +++ b/source/blender/blenkernel/intern/pbvh.cc @@ -3647,12 +3647,7 @@ void pbvh_vertex_iter_init(PBVH *pbvh, PBVHNode *node, PBVHVertexIter *vi, int m } } -bool BKE_pbvh_draw_mask(const PBVH *pbvh) -{ - return BKE_pbvh_has_mask(pbvh); -} - -bool BKE_pbvh_has_mask(const PBVH *pbvh) +bool pbvh_has_mask(const PBVH *pbvh) { switch (pbvh->header.type) { case PBVH_GRIDS: @@ -3667,7 +3662,7 @@ bool BKE_pbvh_has_mask(const PBVH *pbvh) return false; } -bool BKE_pbvh_draw_face_sets(PBVH *pbvh) +bool pbvh_has_face_sets(PBVH *pbvh) { switch (pbvh->header.type) { case PBVH_GRIDS: @@ -3675,9 +3670,8 @@ bool BKE_pbvh_draw_face_sets(PBVH *pbvh) return pbvh->pdata && CustomData_get_layer_named(pbvh->pdata, CD_PROP_INT32, ".sculpt_face_set") != nullptr; case PBVH_BMESH: - return (pbvh->header.bm && CustomData_get_named_layer_index(&pbvh->header.bm->pdata, - CD_PROP_INT32, - ".sculpt_face_set") != -1); + return CustomData_get_offset_named( + &pbvh->header.bm->pdata, CD_PROP_INT32, ".sculpt_face_set") != -1; } return false; diff --git a/source/blender/blenlib/BLI_compiler_attrs.h b/source/blender/blenlib/BLI_compiler_attrs.h index 837d9a39653..e2f0609567f 100644 --- a/source/blender/blenlib/BLI_compiler_attrs.h +++ b/source/blender/blenlib/BLI_compiler_attrs.h @@ -89,7 +89,10 @@ # define ATTR_ALIGN(x) __attribute__((aligned(x))) #endif -/* Disable optimization for a function (for debugging use only!)*/ +/* NotForPR: Needed for debugging but already been rejected in + * a standalone PR. + * Disable optimization for a function (for debugging use only!) + */ #ifdef __clang__ # define ATTR_NO_OPT __attribute__((optnone)) #elif defined(_MSC_VER) diff --git a/source/blender/bmesh/intern/bmesh_core.c b/source/blender/bmesh/intern/bmesh_core.c index 7575b35743f..a48df915730 100644 --- a/source/blender/bmesh/intern/bmesh_core.c +++ b/source/blender/bmesh/intern/bmesh_core.c @@ -7,7 +7,7 @@ */ //#define JVKE_DEBUG -//#define FORCE_BMESH_CHECK +#define FORCE_BMESH_CHECK #if !defined(NDEBUG) || defined(FORCE_BMESH_CHECK) || defined(JVKE_DEBUG) # define BMESH_DEBUG @@ -1977,6 +1977,9 @@ static char *obj_append_line(const char *line, char *str, char *fixed, int *size return str; } +/* NotForPr: saves an obj of the neighborhood around an edge prior to collapse + * into a buffer that can be read from a debugger. + */ static char *bm_save_local_obj_text( BMesh *UNUSED(bm), int depth, char buf[LOCAL_OBJ_SIZE], const char *fmt, ...) { diff --git a/source/blender/draw/engines/basic/basic_engine.c b/source/blender/draw/engines/basic/basic_engine.c index 9a78013e560..01c4d4c7757 100644 --- a/source/blender/draw/engines/basic/basic_engine.c +++ b/source/blender/draw/engines/basic/basic_engine.c @@ -199,7 +199,7 @@ static void basic_cache_populate(void *vedata, Object *ob) stl->g_data->depth_shgrp[do_in_front]; if (use_sculpt_pbvh) { - DRW_shgroup_call_sculpt(shgrp, ob, false, false, false, false, false, false); + DRW_shgroup_call_sculpt(shgrp, ob, false, false, false, false, false); } else { if (stl->g_data->use_material_slot_selection && BKE_object_supports_material_slots(ob)) { diff --git a/source/blender/draw/engines/overlay/overlay_facing.cc b/source/blender/draw/engines/overlay/overlay_facing.cc index 4a58b113460..62b9947233b 100644 --- a/source/blender/draw/engines/overlay/overlay_facing.cc +++ b/source/blender/draw/engines/overlay/overlay_facing.cc @@ -46,7 +46,7 @@ void OVERLAY_facing_cache_populate(OVERLAY_Data *vedata, Object *ob) const bool is_xray = (ob->dtx & OB_DRAW_IN_FRONT) != 0; if (use_sculpt_pbvh) { - DRW_shgroup_call_sculpt(pd->facing_grp[is_xray], ob, false, false, false, false, false, false); + DRW_shgroup_call_sculpt(pd->facing_grp[is_xray], ob, false, false, false, false, false); } else { struct GPUBatch *geom = DRW_cache_object_surface_get(ob); diff --git a/source/blender/draw/engines/overlay/overlay_fade.cc b/source/blender/draw/engines/overlay/overlay_fade.cc index 27f107243fe..8aef21ad95d 100644 --- a/source/blender/draw/engines/overlay/overlay_fade.cc +++ b/source/blender/draw/engines/overlay/overlay_fade.cc @@ -56,7 +56,7 @@ void OVERLAY_fade_cache_populate(OVERLAY_Data *vedata, Object *ob) const bool is_xray = (ob->dtx & OB_DRAW_IN_FRONT) != 0; if (use_sculpt_pbvh) { - DRW_shgroup_call_sculpt(pd->fade_grp[is_xray], ob, false, false, false, false, false, false); + DRW_shgroup_call_sculpt(pd->fade_grp[is_xray], ob, false, false, false, false, false); } else { struct GPUBatch *geom = DRW_cache_object_surface_get(ob); diff --git a/source/blender/draw/engines/overlay/overlay_mode_transfer.cc b/source/blender/draw/engines/overlay/overlay_mode_transfer.cc index 95c11e3b4ea..79ac92a187c 100644 --- a/source/blender/draw/engines/overlay/overlay_mode_transfer.cc +++ b/source/blender/draw/engines/overlay/overlay_mode_transfer.cc @@ -110,8 +110,7 @@ void OVERLAY_mode_transfer_cache_populate(OVERLAY_Data *vedata, Object *ob) pd->mode_transfer.any_animated = true; if (use_sculpt_pbvh) { - DRW_shgroup_call_sculpt( - mode_transfer_grp[is_xray], ob, false, false, false, false, false, false); + DRW_shgroup_call_sculpt(mode_transfer_grp[is_xray], ob, false, false, false, false, false); } else { struct GPUBatch *geom = DRW_cache_object_surface_get(ob); diff --git a/source/blender/draw/engines/overlay/overlay_sculpt.cc b/source/blender/draw/engines/overlay/overlay_sculpt.cc index ffeb5c1b94b..3377aa5394b 100644 --- a/source/blender/draw/engines/overlay/overlay_sculpt.cc +++ b/source/blender/draw/engines/overlay/overlay_sculpt.cc @@ -25,20 +25,9 @@ void OVERLAY_sculpt_cache_init(OVERLAY_Data *vedata) GPUShader *sh = OVERLAY_shader_sculpt_mask(); pd->sculpt_mask_grp = grp = DRW_shgroup_create(sh, psl->sculpt_mask_ps); - DRW_shgroup_uniform_float_copy(grp, "maskOpacity", pd->overlay.sculpt_mode_mask_opacity); - DRW_shgroup_uniform_float_copy( grp, "faceSetsOpacity", pd->overlay.sculpt_mode_face_sets_opacity); - DRW_shgroup_uniform_float_copy( - grp, "faceSetsPatSeed", pd->overlay.sculpt_mode_face_sets_moire_seed); - DRW_shgroup_uniform_bool_copy( - grp, "useMoire", pd->overlay.sculpt_flag & V3D_OVERLAY_SCULPT_FSET_MOIRE); - DRW_shgroup_uniform_bool_copy( - grp, "showIds", pd->overlay.sculpt_flag & V3D_OVERLAY_SCULPT_SHOW_IDS); - - DRW_shgroup_uniform_float_copy( - grp, "faceSetsPatScale", 0.5f * (1.1f - pd->overlay.sculpt_mode_face_sets_moire_scale)); } void OVERLAY_sculpt_cache_populate(OVERLAY_Data *vedata, Object *ob) @@ -56,7 +45,7 @@ void OVERLAY_sculpt_cache_populate(OVERLAY_Data *vedata, Object *ob) return; } - if (!BKE_pbvh_draw_mask(pbvh) && !BKE_pbvh_draw_face_sets(pbvh)) { + if (!pbvh_has_mask(pbvh) && !pbvh_has_face_sets(pbvh)) { /* The SculptSession and the PBVH can be created without a Mask data-layer or Face Set * data-layer. (masks data-layers are created after using a mask tool), so in these cases there * is nothing to draw. */ @@ -64,8 +53,7 @@ void OVERLAY_sculpt_cache_populate(OVERLAY_Data *vedata, Object *ob) } if (use_pbvh) { - bool use_ids = draw_ctx->v3d->overlay.sculpt_flag & V3D_OVERLAY_SCULPT_SHOW_IDS; - DRW_shgroup_call_sculpt(pd->sculpt_mask_grp, ob, false, true, true, false, false, use_ids); + DRW_shgroup_call_sculpt(pd->sculpt_mask_grp, ob, false, true, true, false, false); } else { sculpt_overlays = DRW_mesh_batch_cache_get_sculpt_overlays(static_cast(ob->data)); diff --git a/source/blender/draw/engines/overlay/overlay_wireframe.cc b/source/blender/draw/engines/overlay/overlay_wireframe.cc index c8f96028d69..67f80e5844b 100644 --- a/source/blender/draw/engines/overlay/overlay_wireframe.cc +++ b/source/blender/draw/engines/overlay/overlay_wireframe.cc @@ -312,7 +312,7 @@ void OVERLAY_wireframe_cache_populate(OVERLAY_Data *vedata, DRW_shgroup_call_no_cull(shgrp, geom, ob); } else if (use_sculpt_pbvh) { - DRW_shgroup_call_sculpt(shgrp, ob, true, false, false, false, false, true); + DRW_shgroup_call_sculpt(shgrp, ob, true, false, false, false, false); } else { DRW_shgroup_call(shgrp, geom, ob); diff --git a/source/blender/draw/engines/overlay/shaders/infos/overlay_sculpt_info.hh b/source/blender/draw/engines/overlay/shaders/infos/overlay_sculpt_info.hh index 366d4b115b2..3a53bd388a6 100644 --- a/source/blender/draw/engines/overlay/shaders/infos/overlay_sculpt_info.hh +++ b/source/blender/draw/engines/overlay/shaders/infos/overlay_sculpt_info.hh @@ -5,19 +5,15 @@ GPU_SHADER_INTERFACE_INFO(overlay_sculpt_mask_iface, "") .flat(Type::VEC3, "faceset_color") .smooth(Type::FLOAT, "mask_color") - .smooth(Type::VEC4, "finalColor") - .flat(Type::INT, "sculpt_id"); + .smooth(Type::VEC4, "finalColor"); GPU_SHADER_CREATE_INFO(overlay_sculpt_mask) .do_static_compilation(true) .push_constant(Type::FLOAT, "maskOpacity") .push_constant(Type::FLOAT, "faceSetsOpacity") - .push_constant(Type::BOOL, "useMoire") - .push_constant(Type::BOOL, "showIds") .vertex_in(0, Type::VEC3, "pos") .vertex_in(1, Type::VEC3, "fset") .vertex_in(2, Type::FLOAT, "msk") - .vertex_in(3, Type::INT, "eid") .vertex_out(overlay_sculpt_mask_iface) .vertex_source("overlay_sculpt_mask_vert.glsl") .fragment_source("overlay_sculpt_mask_frag.glsl") diff --git a/source/blender/draw/engines/overlay/shaders/overlay_sculpt_mask_frag.glsl b/source/blender/draw/engines/overlay/shaders/overlay_sculpt_mask_frag.glsl index aff5718e7e7..9650af755c5 100644 --- a/source/blender/draw/engines/overlay/shaders/overlay_sculpt_mask_frag.glsl +++ b/source/blender/draw/engines/overlay/shaders/overlay_sculpt_mask_frag.glsl @@ -1,51 +1,5 @@ -uniform float faceSetsPatSeed; -uniform float faceSetsPatScale; - -float tent(float f) -{ - return 1.0 - abs(fract(f) - 0.5) * 2.0; -} void main() { - vec3 final_color = faceset_color; - vec3 white = vec3(1.0, 1.0, 1.0); - - if (showIds) { - float id = float(sculpt_id); - - if (sculpt_id == -1) { - final_color *= vec3(0.0, 0.0, 0.0); - } - else { - id *= 0.1; - - vec3 id_color = vec3(fract(id), fract(id * 0.1 + 0.1), fract(id * 0.01 + 0.2)); - // final_color = mix(final_color, id_color, 0.5); - final_color *= id_color; - } - } - - if (!useMoire) { - fragColor = vec4(final_color * vec3(mask_color), 1.0); - return; - } - - vec2 xy = gl_FragCoord.xy * faceSetsPatScale; - - /* Basic moire pattern */ - - float seed = 1.0 / 3.0 + faceSetsPatSeed; - - float dx1 = tent(xy.x); - float dy1 = tent(xy.y); - float dx2 = tent(tent(seed) * xy.x + tent(seed * seed + 0.5) * xy.y); - float dy2 = tent(tent(seed) * xy.y - tent(seed * seed + 0.5) * xy.x); - - float fac = (dx1 + dy1 + dx2 + dy2) / 4.0; - fac = step(fac, 1.0 - faceSetsOpacity); - - final_color += (white - final_color) * fac; - - fragColor = vec4(final_color * vec3(mask_color), 1.0); + fragColor = vec4(faceset_color * vec3(mask_color), 1.0); } diff --git a/source/blender/draw/engines/overlay/shaders/overlay_sculpt_mask_vert.glsl b/source/blender/draw/engines/overlay/shaders/overlay_sculpt_mask_vert.glsl index 349405b7e53..36c0e6a0acf 100644 --- a/source/blender/draw/engines/overlay/shaders/overlay_sculpt_mask_vert.glsl +++ b/source/blender/draw/engines/overlay/shaders/overlay_sculpt_mask_vert.glsl @@ -6,12 +6,8 @@ void main() vec3 world_pos = point_object_to_world(pos); gl_Position = point_world_to_ndc(world_pos); - faceset_color = !useMoire ? mix(vec3(1.0), fset, sqrt(faceSetsOpacity)) : fset; + faceset_color = mix(vec3(1.0), fset, faceSetsOpacity); mask_color = 1.0 - (msk * maskOpacity); - if (showIds) { - sculpt_id = eid; - } - view_clipping_distances(world_pos); } diff --git a/source/blender/draw/engines/workbench/workbench_engine.c b/source/blender/draw/engines/workbench/workbench_engine.c index d9ef8d66407..7eeea7eba66 100644 --- a/source/blender/draw/engines/workbench/workbench_engine.c +++ b/source/blender/draw/engines/workbench/workbench_engine.c @@ -110,7 +110,7 @@ static void workbench_cache_sculpt_populate(WORKBENCH_PrivateData *wpd, bool use_color = color_type == V3D_SHADING_VERTEX_COLOR; bool use_uv = color_type == V3D_SHADING_TEXTURE_COLOR; - DRW_shgroup_call_sculpt(grp, ob, false, false, false, use_color, use_uv, false); + DRW_shgroup_call_sculpt(grp, ob, false, false, false, use_color, use_uv); } else { const int materials_len = DRW_cache_object_material_count_get(ob); diff --git a/source/blender/draw/intern/DRW_render.h b/source/blender/draw/intern/DRW_render.h index 45ea35b474d..f4406dbdca0 100644 --- a/source/blender/draw/intern/DRW_render.h +++ b/source/blender/draw/intern/DRW_render.h @@ -464,8 +464,7 @@ void DRW_shgroup_call_sculpt(DRWShadingGroup *shgroup, bool use_mask, bool use_fset, bool use_color, - bool use_uv, - bool use_ids); + bool use_uv); void DRW_shgroup_call_sculpt_with_materials(DRWShadingGroup **shgroups, struct GPUMaterial **gpumats, diff --git a/source/blender/draw/intern/draw_manager_data.cc b/source/blender/draw/intern/draw_manager_data.cc index 57a88ac166d..ba82609b848 100644 --- a/source/blender/draw/intern/draw_manager_data.cc +++ b/source/blender/draw/intern/draw_manager_data.cc @@ -1388,8 +1388,7 @@ void DRW_shgroup_call_sculpt(DRWShadingGroup *shgroup, bool use_mask, bool use_fset, bool use_color, - bool use_uv, - bool use_id) + bool use_uv) { DRWSculptCallbackData scd{}; scd.ob = ob; @@ -1416,12 +1415,6 @@ void DRW_shgroup_call_sculpt(DRWShadingGroup *shgroup, attrs[attrs_num++].type = (eCustomDataType)CD_PBVH_FSET_TYPE; } - if (use_id) { - attrs[attrs_num].type = (eCustomDataType)CD_PBVH_ID_TYPE; - attrs[attrs_num].domain = ATTR_DOMAIN_POINT; - attrs_num++; - } - Mesh *me = BKE_object_get_original_mesh(ob); if (use_color) { diff --git a/source/blender/draw/intern/draw_pbvh.cc b/source/blender/draw/intern/draw_pbvh.cc index 08eb34f9efe..9de33eeaeb2 100644 --- a/source/blender/draw/intern/draw_pbvh.cc +++ b/source/blender/draw/intern/draw_pbvh.cc @@ -77,7 +77,6 @@ static bool valid_pbvh_attr(int type) case CD_PROP_COLOR: case CD_PROP_BYTE_COLOR: case CD_PROP_FLOAT2: - case CD_PBVH_ID_TYPE: return true; } @@ -814,18 +813,6 @@ struct PBVHBatches { #endif switch (vbo.type) { - case CD_PBVH_ID_TYPE: { - const int cd_id = CustomData_get_offset_named( - &args->bm->vdata, CD_PROP_INT32, "vertex_id"); - - foreach_bmesh([&](BMLoop *l) { - int *id = BM_ELEM_CD_PTR(l->v, cd_id); - - *static_cast(GPU_vertbuf_raw_step(&access)) = *id; - }); - break; - } - case CD_PROP_FLOAT2: { const int cd_uv = CustomData_get_offset_named( &args->bm->ldata, CD_PROP_FLOAT2, vbo.name.c_str()); @@ -1055,11 +1042,6 @@ struct PBVHBatches { case CD_PBVH_MASK_TYPE: GPU_vertformat_attr_add(&format, "msk", GPU_COMP_U8, 1, GPU_FETCH_INT_TO_FLOAT_UNIT); break; - case CD_PBVH_ID_TYPE: - name = "vertex_id"; - GPU_vertformat_attr_add(&format, "eid", GPU_COMP_I32, 1, GPU_FETCH_INT); - need_aliases = false; - break; case CD_PROP_FLOAT: GPU_vertformat_attr_add(&format, "f", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); need_aliases = true; diff --git a/source/blender/draw/intern/draw_pbvh.h b/source/blender/draw/intern/draw_pbvh.h index fec81a8f67a..ea7dfeee6f0 100644 --- a/source/blender/draw/intern/draw_pbvh.h +++ b/source/blender/draw/intern/draw_pbvh.h @@ -15,7 +15,6 @@ enum { CD_PBVH_NO_TYPE = CD_NUMTYPES + 1, CD_PBVH_FSET_TYPE = CD_NUMTYPES + 2, CD_PBVH_MASK_TYPE = CD_NUMTYPES + 3, - CD_PBVH_ID_TYPE = CD_NUMTYPES + 4 }; int drw_pbvh_material_index_get(struct PBVHBatches *batches); diff --git a/source/blender/editors/sculpt_paint/sculpt.cc b/source/blender/editors/sculpt_paint/sculpt.cc index 084b066b653..0d578d546fd 100644 --- a/source/blender/editors/sculpt_paint/sculpt.cc +++ b/source/blender/editors/sculpt_paint/sculpt.cc @@ -5245,6 +5245,7 @@ static void sculpt_update_cache_invariants( #undef PIXEL_INPUT_THRESHHOLD if (ss->pbvh) { + /* NotForPR: draw original coordinates for debugging. */ BKE_pbvh_show_orig_set(ss->pbvh, tool_settings->show_origco); } diff --git a/source/blender/editors/sculpt_paint/sculpt_detail.cc b/source/blender/editors/sculpt_paint/sculpt_detail.cc index 3fe64b9ddfa..9310fc20f7c 100644 --- a/source/blender/editors/sculpt_paint/sculpt_detail.cc +++ b/source/blender/editors/sculpt_paint/sculpt_detail.cc @@ -102,6 +102,7 @@ static int sculpt_detail_flood_fill_run(Scene *scene, lock_main_thread(); + /* NotForPR: used to debug dyntopo edge queue. */ bool developer_mode = RNA_boolean_get(op->ptr, "developer"); SculptSession *ss = ob->sculpt; Sculpt *sd = tool_settings->sculpt; @@ -424,6 +425,8 @@ void SCULPT_OT_detail_flood_fill(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; RNA_def_boolean(ot->srna, "interactive", true, "Interactive", "Interactive mode"); + + /* NotForPR: used to debug dyntopo edge queue. */ RNA_def_boolean(ot->srna, "developer", false, "Developer", "Developer mode"); RNA_def_boolean( ot->srna, "emulate_brush", false, "Emulate Brush", "Use last brush position and radius"); diff --git a/source/blender/editors/sculpt_paint/sculpt_undo.cc b/source/blender/editors/sculpt_paint/sculpt_undo.cc index 1e9add1c4ba..40bd776a194 100644 --- a/source/blender/editors/sculpt_paint/sculpt_undo.cc +++ b/source/blender/editors/sculpt_paint/sculpt_undo.cc @@ -2552,7 +2552,7 @@ SculptUndoNode *SCULPT_undo_push_node(Object *ob, PBVHNode *node, SculptUndoType sculpt_undo_store_hidden(ob, unode); break; case SCULPT_UNDO_MASK: - if (BKE_pbvh_has_mask(ss->pbvh)) { + if (pbvh_has_mask(ss->pbvh)) { sculpt_undo_store_mask(ob, unode); } break; diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h index fdada73be8c..2b56fbf278a 100644 --- a/source/blender/makesdna/DNA_scene_types.h +++ b/source/blender/makesdna/DNA_scene_types.h @@ -1704,9 +1704,11 @@ typedef struct ToolSettings { /** Normal Editing. */ float normal_vector[3]; - char save_temp_layers, show_origco; - char _pad6[2]; + /* NotForPR: Show original coordinates from start of sculpt stroke.*/ + char show_origco; + + char _pad6[3]; /** * Custom Curve Profile for bevel tool: diff --git a/source/blender/makesdna/DNA_view3d_types.h b/source/blender/makesdna/DNA_view3d_types.h index c0fa7a1acd1..55d56c27fc4 100644 --- a/source/blender/makesdna/DNA_view3d_types.h +++ b/source/blender/makesdna/DNA_view3d_types.h @@ -606,12 +606,6 @@ enum { V3D_OVERLAY_WPAINT_CONTOURS = (1 << 0), }; -/** #View3DOverlay.sculpt_flag */ -enum { - V3D_OVERLAY_SCULPT_FSET_MOIRE = (1 << 0), - V3D_OVERLAY_SCULPT_SHOW_IDS = (1 << 1), -}; - /** #View3D.around */ enum { /* center of the bounding box */ diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c index 27c2bebdeea..0f6df590f5e 100644 --- a/source/blender/makesrna/intern/rna_scene.c +++ b/source/blender/makesrna/intern/rna_scene.c @@ -3101,10 +3101,7 @@ static void rna_def_tool_settings(BlenderRNA *brna) RNA_def_struct_path_func(srna, "rna_ToolSettings_path"); RNA_def_struct_ui_text(srna, "Tool Settings", ""); - prop = RNA_def_property(srna, "save_temp_layers", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "save_temp_layers", 1); - RNA_def_property_ui_text(prop, "Save Temp Layers", ""); - + /* NotForPR: used to debug interpolation of original coordinates in dyntopo. */ prop = RNA_def_property(srna, "show_origco", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "show_origco", 1); RNA_def_property_ui_text(prop, "Show Original", ""); diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c index 4eeb133a83d..c4862befdf5 100644 --- a/source/blender/makesrna/intern/rna_space.c +++ b/source/blender/makesrna/intern/rna_space.c @@ -4773,27 +4773,6 @@ static void rna_def_space_view3d_overlay(BlenderRNA *brna) RNA_def_property_range(prop, 0.0f, 1.0f); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); - prop = RNA_def_property(srna, "sculpt_mode_face_sets_moire_seed", PROP_FLOAT, PROP_FACTOR); - RNA_def_property_float_sdna(prop, NULL, "overlay.sculpt_mode_face_sets_moire_seed"); - RNA_def_property_ui_text(prop, "Pattern Seed", "Sculpt Face Sets Pattern Seed"); - RNA_def_property_range(prop, 0.0f, 1.0f); - RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); - - prop = RNA_def_property(srna, "sculpt_mode_use_face_set_moire", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "overlay.sculpt_flag", V3D_OVERLAY_SCULPT_FSET_MOIRE); - RNA_def_property_ui_text(prop, "Moire", "Draw face sets with moire pattern"); - RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); - - prop = RNA_def_property(srna, "sculpt_mode_face_sets_moire_scale", PROP_FLOAT, PROP_FACTOR); - RNA_def_property_float_sdna(prop, NULL, "overlay.sculpt_mode_face_sets_moire_scale"); - RNA_def_property_ui_text(prop, "Pattern Scale", "Sculpt Face Sets Pattern Scale"); - RNA_def_property_range(prop, 0.0f, 1.0f); - - prop = RNA_def_property(srna, "show_sculpt_ids", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "overlay.sculpt_flag", V3D_OVERLAY_SCULPT_SHOW_IDS); - RNA_def_property_ui_text(prop, "Sculpt IDs", ""); - RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); - prop = RNA_def_property(srna, "show_sculpt_mask", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "overlay.flag", V3D_OVERLAY_SCULPT_SHOW_MASK); RNA_def_property_ui_text(prop, "Sculpt Show Mask", ""); diff --git a/source/blender/windowmanager/intern/wm_files.cc b/source/blender/windowmanager/intern/wm_files.cc index 412214f4695..3e01bbf6765 100644 --- a/source/blender/windowmanager/intern/wm_files.cc +++ b/source/blender/windowmanager/intern/wm_files.cc @@ -2052,7 +2052,10 @@ static void wm_autosave_location(char filepath[FILE_MAX]) BLI_path_join(filepath, FILE_MAX, tempdir_base, filename); } -/* TODO: Move to appropriate headers */ +/* NotForPR: In order to make testing less painful, + * flush the sculpt mesh prior to autosave. Since this comes with a + * performance cost it must be removed prior to the final merge. + */ void ED_sculpt_fast_save_bmesh(Object *ob); struct MemFileUndoStep; MemFileUndoData *memfile_get_step_data(struct MemFileUndoStep *us); -- 2.30.2 From ea8c4dd15b054ba65a8f8606e21ad9dd914c8943 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Wed, 31 May 2023 17:13:24 -0700 Subject: [PATCH 139/279] temp-sculpt-dyntopo: Remove ASAN code for CustomData bmesh blocks --- source/blender/blenkernel/BKE_customdata.h | 3 - .../blender/blenkernel/intern/customdata.cc | 71 ------------------- source/blender/bmesh/intern/bmesh_log.cc | 22 ------ 3 files changed, 96 deletions(-) diff --git a/source/blender/blenkernel/BKE_customdata.h b/source/blender/blenkernel/BKE_customdata.h index e33082859bb..47d5242b320 100644 --- a/source/blender/blenkernel/BKE_customdata.h +++ b/source/blender/blenkernel/BKE_customdata.h @@ -832,9 +832,6 @@ void CustomData_mark_temporary_nocopy(struct CustomData *data); void CustomData_regen_active_refs(CustomData *data); -void CustomData_bmesh_asan_poison(const CustomData *data, void *block); -void CustomData_bmesh_asan_unpoison(const CustomData *data, void *block); - #ifndef NDEBUG struct DynStr; /** Use to inspect mesh data when debugging. */ diff --git a/source/blender/blenkernel/intern/customdata.cc b/source/blender/blenkernel/intern/customdata.cc index a88c06d4ea2..8988a497724 100644 --- a/source/blender/blenkernel/intern/customdata.cc +++ b/source/blender/blenkernel/intern/customdata.cc @@ -18,7 +18,6 @@ #include "DNA_meshdata_types.h" #include "BLI_array.hh" -#include "BLI_asan.h" #include "BLI_bitmap.h" #include "BLI_color.hh" #include "BLI_compiler_attrs.h" @@ -75,8 +74,6 @@ using blender::Vector; /* number of layers to add when growing a CustomData object */ #define CUSTOMDATA_GROW 5 -#define BM_ASAN_PAD 32 - /* ensure typemap size is ok */ BLI_STATIC_ASSERT(BOUNDED_ARRAY_TYPE_SIZE() == CD_NUMTYPES, "size mismatch"); @@ -2801,16 +2798,8 @@ static void customData_update_offsets(CustomData *data) size += 8 - (size & 7); } -#ifdef WITH_ASAN - offset += BM_ASAN_PAD; -#endif - layer->offset = offset; offset += size; - -#ifdef WITH_ASAN - offset += BM_ASAN_PAD; -#endif } } @@ -2829,10 +2818,6 @@ static void customData_update_offsets(CustomData *data) continue; } -#ifdef WITH_ASAN - offset += BM_ASAN_PAD; -#endif - donemap[j] = true; int align2 = aligns[i] - (offset % aligns[i]); @@ -2842,10 +2827,6 @@ static void customData_update_offsets(CustomData *data) layer->offset = offset; offset += size; - -#ifdef WITH_ASAN - offset += BM_ASAN_PAD; -#endif } } @@ -2858,35 +2839,6 @@ static void customData_update_offsets(CustomData *data) CustomData_update_typemap(data); } -void CustomData_bmesh_asan_poison(const CustomData *data, void *block) -{ -#ifdef WITH_ASAN - if (!block) { - return; - } - - char *ptr = (char *)block; - - BLI_asan_unpoison(block, data->totsize); - - for (int i = 0; i < data->totlayer; i++) { - CustomDataLayer *layer = data->layers + i; - const LayerTypeInfo *typeInfo = layerType_getInfo(eCustomDataType(layer->type)); - - BLI_asan_poison((ptr + layer->offset - BM_ASAN_PAD), BM_ASAN_PAD); - BLI_asan_poison((ptr + layer->offset + typeInfo->size), BM_ASAN_PAD); - } -#endif -} - -void CustomData_bmesh_asan_unpoison(const CustomData *data, void *block) -{ - if (!block) { - return; - } - BLI_asan_unpoison(block, data->totsize); -} - /* to use when we're in the middle of modifying layers */ static int CustomData_get_layer_index__notypemap(const CustomData *data, const eCustomDataType type) @@ -4223,8 +4175,6 @@ void CustomData_bmesh_free_block(CustomData *data, void **block) return; } - CustomData_bmesh_asan_unpoison(data, *block); - for (int i = 0; i < data->totlayer; i++) { const LayerTypeInfo *typeInfo = layerType_getInfo(eCustomDataType(data->layers[i].type)); @@ -4235,7 +4185,6 @@ void CustomData_bmesh_free_block(CustomData *data, void **block) } if (data->totsize) { - CustomData_bmesh_asan_unpoison(data, *block); BLI_mempool_free(data->pool, *block); } @@ -4248,8 +4197,6 @@ void CustomData_bmesh_free_block_data(CustomData *data, void *block) return; } - CustomData_bmesh_asan_unpoison(data, block); - for (int i = 0; i < data->totlayer; i++) { const LayerTypeInfo *typeInfo = layerType_getInfo(eCustomDataType(data->layers[i].type)); @@ -4263,8 +4210,6 @@ void CustomData_bmesh_free_block_data(CustomData *data, void *block) memset(POINTER_OFFSET(block, data->layers[i].offset), 0, typeInfo->size); } } - - CustomData_bmesh_asan_poison(data, block); } void CustomData_bmesh_alloc_block(CustomData *data, void **block) @@ -4276,8 +4221,6 @@ void CustomData_bmesh_alloc_block(CustomData *data, void **block) if (data->totsize > 0) { *block = BLI_mempool_alloc(data->pool); - CustomData_bmesh_asan_poison(data, *block); - /*clear toolflags pointer when created for the first time*/ int cd_tflags = data->typemap[CD_TOOLFLAGS]; if (cd_tflags != -1) { @@ -4303,8 +4246,6 @@ void CustomData_bmesh_free_block_data_exclude_by_type(CustomData *data, return; } - CustomData_bmesh_asan_unpoison(data, block); - for (int i = 0; i < data->totlayer; i++) { if ((CD_TYPE_AS_MASK(data->layers[i].type) & mask_exclude) == 0) { const LayerTypeInfo *typeInfo = layerType_getInfo(eCustomDataType(data->layers[i].type)); @@ -4315,8 +4256,6 @@ void CustomData_bmesh_free_block_data_exclude_by_type(CustomData *data, memset(POINTER_OFFSET(block, offset), 0, typeInfo->size); } } - - CustomData_bmesh_asan_poison(data, block); } void CustomData_data_set_default_value(const CustomData *data, @@ -4360,9 +4299,6 @@ void CustomData_bmesh_set_default(CustomData *data, void **block) void CustomData_bmesh_swap_data_simple(CustomData *data, void **block1, void **block2, int cd_id) { - CustomData_bmesh_asan_unpoison(data, *block1); - CustomData_bmesh_asan_unpoison(data, *block2); - std::swap(*block1, *block2); int cd_toolflags = data->typemap[CD_TOOLFLAGS]; @@ -4384,9 +4320,6 @@ void CustomData_bmesh_swap_data_simple(CustomData *data, void **block1, void **b std::swap(*id1, *id2); } } - - CustomData_bmesh_asan_poison(data, *block1); - CustomData_bmesh_asan_poison(data, *block2); } void CustomData_bmesh_swap_data(CustomData *source, @@ -4402,9 +4335,7 @@ void CustomData_bmesh_swap_data(CustomData *source, CustomData_bmesh_alloc_block(dest, dest_block); if (*dest_block) { - CustomData_bmesh_asan_unpoison(dest, *dest_block); memset(*dest_block, 0, dest->totsize); - CustomData_bmesh_asan_poison(dest, *dest_block); CustomData_bmesh_set_default(dest, dest_block); } @@ -4488,9 +4419,7 @@ void CustomData_bmesh_copy_data_exclude_by_type(const CustomData *source, CustomData_bmesh_alloc_block(dest, dest_block); if (*dest_block) { - CustomData_bmesh_asan_unpoison(dest, *dest_block); memset(*dest_block, 0, dest->totsize); - CustomData_bmesh_asan_poison(dest, *dest_block); was_new = true; } diff --git a/source/blender/bmesh/intern/bmesh_log.cc b/source/blender/bmesh/intern/bmesh_log.cc index d40c3a50f8a..52ce773c0e9 100644 --- a/source/blender/bmesh/intern/bmesh_log.cc +++ b/source/blender/bmesh/intern/bmesh_log.cc @@ -711,7 +711,6 @@ struct BMLogEntry { void free_logvert(BMLogVert *lv) { if (lv->customdata) { - CustomData_bmesh_asan_unpoison(&vdata, lv->customdata); BLI_mempool_free(vdata.pool, lv->customdata); } @@ -751,7 +750,6 @@ struct BMLogEntry { void free_logedge(BMesh * /*bm*/, BMLogEdge *le) { if (le->customdata) { - CustomData_bmesh_asan_unpoison(&edata, le->customdata); BLI_mempool_free(edata.pool, le->customdata); } @@ -808,13 +806,11 @@ struct BMLogEntry { { if (lf->loop_customdata[0]) { for (int i = 0; i < lf->verts.size(); i++) { - CustomData_bmesh_asan_unpoison(&ldata, lf->loop_customdata[i]); BLI_mempool_free(ldata.pool, lf->loop_customdata[i]); } } if (lf->customdata) { - CustomData_bmesh_asan_unpoison(&pdata, lf->customdata); BLI_mempool_free(pdata.pool, lf->customdata); } @@ -1270,10 +1266,6 @@ void BMLogSetDiff::swap_verts(BMesh *bm, const int cd_id = entry->idmap->cd_id_off[BM_VERT]; - if (old_customdata) { - CustomData_bmesh_asan_unpoison(&bm->vdata, old_customdata); - } - for (BMLogVert *lv : verts.values()) { BMVert *v = entry->get_elem_from_id(bm, lv->id); @@ -1283,9 +1275,7 @@ void BMLogSetDiff::swap_verts(BMesh *bm, } if (old_customdata) { - CustomData_bmesh_asan_unpoison(&bm->vdata, v->head.data); memcpy(old_customdata, v->head.data, bm->vdata.totsize); - CustomData_bmesh_asan_poison(&bm->vdata, v->head.data); } entry->swap_logvert(bm, lv->id, v, lv); @@ -1407,10 +1397,6 @@ void BMLogSetDiff::swap_edges(BMesh *bm, { void *old_customdata = entry->edata.pool ? BLI_mempool_alloc(bm->edata.pool) : nullptr; - if (old_customdata) { - CustomData_bmesh_asan_unpoison(&bm->edata, old_customdata); - } - for (BMLogEdge *le : edges.values()) { BMEdge *e = entry->get_elem_from_id(bm, le->id); @@ -1420,9 +1406,7 @@ void BMLogSetDiff::swap_edges(BMesh *bm, } if (old_customdata) { - CustomData_bmesh_asan_unpoison(&bm->edata, e->head.data); memcpy(old_customdata, e->head.data, bm->edata.totsize); - CustomData_bmesh_asan_poison(&bm->edata, e->head.data); } entry->swap_logedge(bm, le->id, e, le); @@ -1521,10 +1505,6 @@ void BMLogSetDiff::swap_faces(BMesh *bm, const int cd_id = entry->idmap->cd_id_off[BM_FACE]; - if (old_customdata) { - CustomData_bmesh_asan_unpoison(&bm->pdata, old_customdata); - } - for (BMLogFace *lf : faces.values()) { BMFace *f = entry->get_elem_from_id(bm, lf->id); @@ -1534,9 +1514,7 @@ void BMLogSetDiff::swap_faces(BMesh *bm, } if (old_customdata) { - CustomData_bmesh_asan_unpoison(&bm->pdata, f->head.data); memcpy(old_customdata, f->head.data, bm->pdata.totsize); - CustomData_bmesh_asan_poison(&bm->pdata, f->head.data); } entry->swap_logface(bm, lf->id, f, lf); -- 2.30.2 From 5f6125a3010ea8a5639440da6b08d6a40e6374a2 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Wed, 31 May 2023 17:41:35 -0700 Subject: [PATCH 140/279] temp-sculpt-dyntopo: Remove changes to customdata * No more per-customdata-layer default data, BMIdMap no longer needs it. * Fixed formatting differences. --- source/blender/blenkernel/BKE_customdata.h | 11 +- .../blender/blenkernel/intern/customdata.cc | 252 +++++------------- source/blender/bmesh/intern/bmesh_idmap.cc | 7 - .../bmesh/intern/bmesh_mesh_convert.cc | 3 +- .../blender/makesdna/DNA_customdata_types.h | 3 - 5 files changed, 79 insertions(+), 197 deletions(-) diff --git a/source/blender/blenkernel/BKE_customdata.h b/source/blender/blenkernel/BKE_customdata.h index 47d5242b320..fdd66a11476 100644 --- a/source/blender/blenkernel/BKE_customdata.h +++ b/source/blender/blenkernel/BKE_customdata.h @@ -134,10 +134,7 @@ bool CustomData_has_referenced(const struct CustomData *data); * implemented for `mloopuv/mloopcol`, for now. */ void CustomData_data_copy_value(eCustomDataType type, const void *source, void *dest); -void CustomData_data_set_default_value(const struct CustomData *data, - const eCustomDataType type, - int n, - void *elem); +void CustomData_data_set_default_value(const eCustomDataType type, void *elem); /** * Mixes the "value" (e.g. `mloopuv` UV or `mloopcol` colors) from one block into @@ -179,8 +176,10 @@ void CustomData_copy_layout(const struct CustomData *source, /* BMESH_TODO, not really a public function but readfile.c needs it */ void CustomData_update_typemap(struct CustomData *data); -/* copies all customdata layers without allocating data, - * and without respect to type masks or NO_COPY/etc flags*/ +/* Copies all customdata layers without allocating data + * and without respect to type masks or CD_FLAG_NO_COPY + * or CD_FLAG_TEMPORARY flags. + */ void CustomData_copy_all_layout(const struct CustomData *source, struct CustomData *dest); /** diff --git a/source/blender/blenkernel/intern/customdata.cc b/source/blender/blenkernel/intern/customdata.cc index 8988a497724..0261d5bb3c7 100644 --- a/source/blender/blenkernel/intern/customdata.cc +++ b/source/blender/blenkernel/intern/customdata.cc @@ -101,7 +101,6 @@ bool CustomData_layout_is_same(const CustomData *_a, const CustomData *_b) CustomDataLayer clb = _b->layers[i]; cla.data = clb.data = nullptr; - cla.default_data = clb.default_data = nullptr; if (memcmp((void *)&cla, (void *)&clb, sizeof(CustomDataLayer)) != 0) { return false; @@ -226,7 +225,6 @@ struct LayerTypeInfo { /** a function to determine max allowed number of layers, * should be null or return -1 if no limit */ int (*layers_max)(); - bool use_default_data; }; /** \} */ @@ -1530,10 +1528,7 @@ static bool layerValidate_propfloat2(void *data, const uint totitems, const bool return has_errors; } -static void layerInterp_noop(const void **, const float *, const float *, int, void *) -{ - // do nothing -} +static void layerInterp_noop(const void **, const float *, const float *, int, void *) {} static bool layerEqual_propfloat2(const void *data1, const void *data2) { @@ -1690,21 +1685,7 @@ static const LayerTypeInfo LAYERTYPEINFO[CD_NUMTYPES] = { nullptr, nullptr, layerInterp_propInt, - nullptr, - nullptr, - nullptr, - nullptr, - nullptr, - nullptr, - nullptr, - nullptr, - nullptr, - nullptr, - nullptr, - nullptr, - nullptr, - nullptr, - true}, + nullptr}, /* 12: CD_PROP_STRING */ {sizeof(MStringProperty), "MStringProperty", @@ -2011,74 +1992,62 @@ static const LayerTypeInfo LAYERTYPEINFO[CD_NUMTYPES] = { layerInterp_noop}, }; -static const char *LAYERTYPENAMES[CD_NUMTYPES] = {/* 0-4 */ - "CDMVert", - "CDMSticky", - "CDMDeformVert", - "CDMEdge", - "CDMFace", - /* 5-9 */ - "CDMTFace", - "CDMCol", - "CDOrigIndex", - "CDNormal", - "CDFaceMap", - /* 10-14 */ - "CDMFloatProperty", - "CDMIntProperty", - "CDMStringProperty", - "CDOrigSpace", - "CDOrco", - /* 15-19 */ - "CDMTexPoly", - "CDMLoopUV", - "CDMloopCol", - "CDTangent", - "CDMDisps", - /* 20-24 */ - "CDPreviewMCol", - "CDIDMCol", - "CDTextureMCol", - "CDClothOrco", - "CDMRecast", +static const char *LAYERTYPENAMES[CD_NUMTYPES] = { + /* 0-4 */ "CDMVert", + "CDMSticky", + "CDMDeformVert", + "CDMEdge", + "CDMFace", + /* 5-9 */ "CDMTFace", + "CDMCol", + "CDOrigIndex", + "CDNormal", + "CDFaceMap", + /* 10-14 */ "CDMFloatProperty", + "CDMIntProperty", + "CDMStringProperty", + "CDOrigSpace", + "CDOrco", + /* 15-19 */ "CDMTexPoly", + "CDMLoopUV", + "CDMloopCol", + "CDTangent", + "CDMDisps", + /* 20-24 */ "CDPreviewMCol", + "CDIDMCol", + "CDTextureMCol", + "CDClothOrco", + "CDMRecast", - /* BMESH ONLY */ - /* 25-29 */ - "CDMPoly", - "CDMLoop", - "CDShapeKeyIndex", - "CDShapeKey", - "CDBevelWeight", - /* 30-34 */ - "CDSubSurfCrease", - "CDOrigSpaceLoop", - "CDPreviewLoopCol", - "CDBMElemPyPtr", - "CDPaintMask", - /* 35-36 */ - "CDGridPaintMask", - "CDMVertSkin", - /* 37-38 */ - "CDFreestyleEdge", - "CDFreestyleFace", - /* 39-42 */ - "CDMLoopTangent", - "CDTessLoopNormal", - "CDCustomLoopNormal", - "CDSculptFaceGroups", - /* 43-46 */ - "CDHairPoint", - "CDHairMapping", - "CDPropInt8", - "CDPoint", - /* 47-50 */ - "CDPropCol", - "CDPropFloat3", - "CDPropFloat2", - "CDPropBoolean", - /*51-53*/ - "CDHairLength", - "CDMToolFlags"}; + /* BMESH ONLY */ + /* 25-29 */ "CDMPoly", + "CDMLoop", + "CDShapeKeyIndex", + "CDShapeKey", + "CDBevelWeight", + /* 30-34 */ "CDSubSurfCrease", + "CDOrigSpaceLoop", + "CDPreviewLoopCol", + "CDBMElemPyPtr", + "CDPaintMask", + /* 35-36 */ "CDGridPaintMask", + "CDMVertSkin", + /* 37-38 */ "CDFreestyleEdge", + "CDFreestyleFace", + /* 39-42 */ "CDMLoopTangent", + "CDTessLoopNormal", + "CDCustomLoopNormal", + "CDSculptFaceGroups", + /* 43-46 */ "CDHairPoint", + "CDPropInt8", + "CDHairMapping", + "CDPoint", + "CDPropCol", + "CDPropFloat3", + "CDPropFloat2", + "CDPropBoolean", + /*51-53*/ "CDHairLength", + "CDMToolFlags"}; const CustomData_MeshMasks CD_MASK_BAREMESH = { /*vmask*/ CD_MASK_PROP_FLOAT3, @@ -2358,10 +2327,6 @@ void CustomData_copy_all_layout(const struct CustomData *source, struct CustomDa *layer = source->layers[i]; layer->data = nullptr; layer->sharing_info = nullptr; - - if (layer->default_data) { - layer->default_data = MEM_dupallocN(layer->default_data); - } } } @@ -2479,10 +2444,6 @@ static bool customdata_merge_internal(const CustomData *source, new_layer->active_mask = last_mask; changed = true; - if (src_layer.default_data) { - new_layer->default_data = MEM_dupallocN(src_layer.default_data); - } - if (src_layer.anonymous_id != nullptr) { new_layer->anonymous_id = src_layer.anonymous_id; new_layer->anonymous_id->add_user(); @@ -2490,7 +2451,6 @@ static bool customdata_merge_internal(const CustomData *source, } CustomData_update_typemap(dest); - CustomData_regen_active_refs(dest); customData_update_offsets(dest); return changed; @@ -2689,10 +2649,6 @@ static void customData_free_layer__internal(CustomDataLayer *layer, const int to layer->sharing_info->remove_user_and_delete_if_last(); layer->sharing_info = nullptr; } - - if (layer->default_data) { - MEM_freeN(layer->default_data); - } } static void CustomData_external_free(CustomData *data) @@ -3537,8 +3493,6 @@ void CustomData_free_temporary(CustomData *data, const int totelem) if ((layer->flag & CD_FLAG_TEMPORARY) == CD_FLAG_TEMPORARY) { customData_free_layer__internal(layer, totelem); changed = true; - - // compact data->layers by not incrementing j here } else { j++; @@ -4012,39 +3966,6 @@ const char *CustomData_get_layer_name(const CustomData *data, /* BMesh functions */ -void CustomData_bmesh_update_active_layers(CustomData *fdata, CustomData *ldata) -{ - int act; - - if (CustomData_has_layer(ldata, CD_MLOOPUV)) { - act = CustomData_get_active_layer(ldata, CD_MLOOPUV); - CustomData_set_layer_active(fdata, CD_MTFACE, act); - - act = CustomData_get_render_layer(ldata, CD_MLOOPUV); - CustomData_set_layer_render(fdata, CD_MTFACE, act); - - act = CustomData_get_clone_layer(ldata, CD_MLOOPUV); - CustomData_set_layer_clone(fdata, CD_MTFACE, act); - - act = CustomData_get_stencil_layer(ldata, CD_MLOOPUV); - CustomData_set_layer_stencil(fdata, CD_MTFACE, act); - } - - if (CustomData_has_layer(ldata, CD_PROP_BYTE_COLOR)) { - act = CustomData_get_active_layer(ldata, CD_PROP_BYTE_COLOR); - CustomData_set_layer_active(fdata, CD_MCOL, act); - - act = CustomData_get_render_layer(ldata, CD_PROP_BYTE_COLOR); - CustomData_set_layer_render(fdata, CD_MCOL, act); - - act = CustomData_get_clone_layer(ldata, CD_PROP_BYTE_COLOR); - CustomData_set_layer_clone(fdata, CD_MCOL, act); - - act = CustomData_get_stencil_layer(ldata, CD_PROP_BYTE_COLOR); - CustomData_set_layer_stencil(fdata, CD_MCOL, act); - } -} - void CustomData_bmesh_init_pool(CustomData *data, const int totelem, const char htype) { int chunksize; @@ -4221,7 +4142,7 @@ void CustomData_bmesh_alloc_block(CustomData *data, void **block) if (data->totsize > 0) { *block = BLI_mempool_alloc(data->pool); - /*clear toolflags pointer when created for the first time*/ + /* Clear toolflags pointer when created for the first time. */ int cd_tflags = data->typemap[CD_TOOLFLAGS]; if (cd_tflags != -1) { cd_tflags = data->layers[cd_tflags].offset; @@ -4258,32 +4179,22 @@ void CustomData_bmesh_free_block_data_exclude_by_type(CustomData *data, } } -void CustomData_data_set_default_value(const CustomData *data, - const eCustomDataType type, - int n, - void *elem) +void CustomData_data_set_default_value(const eCustomDataType type, void *elem) { const LayerTypeInfo *typeInfo = layerType_getInfo(type); - int base_idx = data->typemap[type]; - if (typeInfo->set_default_value) { typeInfo->set_default_value(elem, 1); } - else if (typeInfo->use_default_data && data->layers[base_idx + n].default_data) { - memcpy(elem, data->layers[base_idx + n].default_data, typeInfo->size); - } else { memset(elem, 0, typeInfo->size); } } -static void CustomData_bmesh_set_default_n(CustomData *data, void **block, const int idx) +static void CustomData_bmesh_set_default_n(CustomData *data, void **block, const int n) { - const int offset = data->layers[idx].offset; - int n = idx - data->typemap[data->layers[idx].type]; - - CustomData_data_set_default_value( - data, eCustomDataType(data->layers[idx].type), n, POINTER_OFFSET(*block, offset)); + const int offset = data->layers[n].offset; + CustomData_data_set_default_value(eCustomDataType(data->layers[n].type), + POINTER_OFFSET(*block, offset)); } void CustomData_bmesh_set_default(CustomData *data, void **block) @@ -4342,8 +4253,8 @@ void CustomData_bmesh_swap_data(CustomData *source, } for (src_i = 0; src_i < source->totlayer; src_i++) { - /* find the first dest layer with type >= the source type - * (this should work because layers are ordered by type) + /* Find the first dest layer with type >= the source type + * (this should work because layers are ordered by type). */ while (dest_i_start < dest->totlayer && dest->layers[dest_i_start].type < source->layers[src_i].type) @@ -4352,11 +4263,11 @@ void CustomData_bmesh_swap_data(CustomData *source, } if (source->layers[src_i].type == CD_TOOLFLAGS) { - // do not swap toolflags + /* Do not swap toolflags. */ continue; } - /* if there are no more dest layers, we're done */ + /* If there are no more dest layers, we're done. */ if (dest_i_start >= dest->totlayer) { return; } @@ -4364,7 +4275,7 @@ void CustomData_bmesh_swap_data(CustomData *source, dest_i = dest_i_start; while (dest_i < dest->totlayer && dest->layers[dest_i].type == source->layers[src_i].type) { - /* if we found a matching layer, copy the data */ + /* If we found a matching layer, copy the data. */ if (dest->layers[dest_i].type == source->layers[src_i].type && STREQ(dest->layers[dest_i].name, source->layers[src_i].name)) { @@ -4374,7 +4285,7 @@ void CustomData_bmesh_swap_data(CustomData *source, eCustomDataType(source->layers[src_i].type)); const uint size = typeInfo->size; - // swap data + /* Swap data. */ char *bsrc = (char *)src_data; char *bdst = (char *)dest_data; @@ -4406,7 +4317,7 @@ void CustomData_bmesh_copy_data_exclude_by_type(const CustomData *source, const bool no_mask = (mask_exclude == 0); /* - Note: we don't handle id/toolflag layers as a special case, + Note: we don't handle toolflag layers as a special case, instead relying on CD_ELEM_NO_COPY semantics. This is so BM_data_layer_add can reallocate customdata blocks without @@ -4779,14 +4690,9 @@ void CustomData_bmesh_interp(CustomData *data, 1); } else { - if (layer->default_data && typeInfo->use_default_data) { - memcpy(POINTER_OFFSET(dst_block, layer->offset), layer->default_data, typeInfo->size); - } - else { - memcpy(POINTER_OFFSET(dst_block, layer->offset), - POINTER_OFFSET(src_blocks[0], layer->offset), - typeInfo->size); - } + memcpy(POINTER_OFFSET(dst_block, layer->offset), + POINTER_OFFSET(src_blocks[0], layer->offset), + typeInfo->size); } continue; @@ -5716,10 +5622,6 @@ void CustomData_blend_write(BlendWriter *writer, for (const CustomDataLayer &layer : layers_to_write) { const LayerTypeInfo *typeInfo = layerType_getInfo(eCustomDataType(layer.type)); - if (typeInfo->use_default_data && layer.default_data) { - BLO_write_struct_by_name(writer, typeInfo->structname, layer.default_data); - } - switch (layer.type) { case CD_MDEFORMVERT: BKE_defvert_blend_write(writer, count, static_cast(layer.data)); @@ -5829,13 +5731,6 @@ void CustomData_blend_read(BlendDataReader *reader, CustomData *data, const int CustomDataLayer *layer = &data->layers[i]; const LayerTypeInfo *typeInfo = layerType_getInfo(eCustomDataType(layer->type)); - if (layer->default_data && typeInfo->use_default_data) { - BLO_read_data_address(reader, &layer->default_data); - } - else { - layer->default_data = nullptr; - } - if (layer->flag & CD_FLAG_EXTERNAL) { layer->flag &= ~CD_FLAG_IN_MEMORY; } @@ -5877,7 +5772,6 @@ void CustomData_blend_read(BlendDataReader *reader, CustomData *data, const int data->maxlayer = data->totlayer; CustomData_update_typemap(data); - CustomData_regen_active_refs(data); // check for corrupted active layer refs } /** \} */ diff --git a/source/blender/bmesh/intern/bmesh_idmap.cc b/source/blender/bmesh/intern/bmesh_idmap.cc index 20629b1c008..cec89f073aa 100644 --- a/source/blender/bmesh/intern/bmesh_idmap.cc +++ b/source/blender/bmesh/intern/bmesh_idmap.cc @@ -337,15 +337,8 @@ void BM_idmap_check_attributes(BMIdMap *idmap) idx = CustomData_get_named_layer_index(cdata, CD_PROP_INT32, name); } - if (!cdata->layers[idx].default_data) { - cdata->layers[idx].default_data = MEM_cnew("MIntProperty"); - } - cdata->layers[idx].flag |= CD_FLAG_ELEM_NOINTERP | CD_FLAG_ELEM_NOCOPY; - int *default_data = static_cast(cdata->layers[idx].default_data); - *default_data = BM_ID_NONE; - idmap->cd_id_off[type] = cdata->layers[idx].offset; }; diff --git a/source/blender/bmesh/intern/bmesh_mesh_convert.cc b/source/blender/bmesh/intern/bmesh_mesh_convert.cc index 31441a5968f..efe3afe76ec 100644 --- a/source/blender/bmesh/intern/bmesh_mesh_convert.cc +++ b/source/blender/bmesh/intern/bmesh_mesh_convert.cc @@ -220,8 +220,7 @@ static void mesh_attributes_copy_to_bmesh_block(CustomData &data, POINTER_OFFSET(header.data, info.bmesh_offset)); } else { - CustomData_data_set_default_value( - &data, info.type, info.n, POINTER_OFFSET(header.data, info.bmesh_offset)); + CustomData_data_set_default_value(info.type, POINTER_OFFSET(header.data, info.bmesh_offset)); } } } diff --git a/source/blender/makesdna/DNA_customdata_types.h b/source/blender/makesdna/DNA_customdata_types.h index 67017441351..e9225e54aab 100644 --- a/source/blender/makesdna/DNA_customdata_types.h +++ b/source/blender/makesdna/DNA_customdata_types.h @@ -56,9 +56,6 @@ typedef struct CustomDataLayer { */ const AnonymousAttributeIDHandle *anonymous_id; - /** Default data for layer elements, heap-allocated. */ - void *default_data; - /** * Run-time data that allows sharing `data` with other entities (mostly custom data layers on * other geometries). -- 2.30.2 From dcc6302acf99e8460339bbf788207ba16efd0942 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Wed, 31 May 2023 20:28:46 -0700 Subject: [PATCH 141/279] temp-sculpt-dyntopo: Rewrite TableGSet in c++ * Rewrote TableGSet to be fully C++. Since I'm not sure this is generally useful beyond dyntopo I've situated it in blenkernel, `BKE_dyntopo_set.hh`. TODO: Make PBVHNode.bm_unique_verts/bm_other_verts/bm_faces members instead of pointers after PBVH->nodes becomes a Vector. --- source/blender/blenkernel/BKE_pbvh.h | 73 +-- source/blender/blenkernel/CMakeLists.txt | 1 + source/blender/blenkernel/intern/dyntopo.cc | 23 +- .../blenkernel/intern/dyntopo_intern.hh | 24 +- source/blender/blenkernel/intern/pbvh.cc | 66 +-- .../blender/blenkernel/intern/pbvh_bmesh.cc | 490 ++++++------------ .../blender/blenkernel/intern/pbvh_intern.hh | 5 +- source/blender/blenlib/BLI_ghash.h | 84 +-- source/blender/blenlib/CMakeLists.txt | 1 - source/blender/blenlib/intern/BLI_ghash.c | 39 -- .../blender/blenlib/intern/BLI_table_gset.cc | 188 ------- source/blender/draw/DRW_pbvh.hh | 2 - .../editors/sculpt_paint/paint_hide.cc | 18 +- .../editors/sculpt_paint/sculpt_undo.cc | 24 +- 14 files changed, 271 insertions(+), 767 deletions(-) delete mode 100644 source/blender/blenlib/intern/BLI_table_gset.cc diff --git a/source/blender/blenkernel/BKE_pbvh.h b/source/blender/blenkernel/BKE_pbvh.h index 11b1c1bc934..320d8f9703e 100644 --- a/source/blender/blenkernel/BKE_pbvh.h +++ b/source/blender/blenkernel/BKE_pbvh.h @@ -7,6 +7,10 @@ * \brief A BVH for high poly meshes. */ +#ifdef __cplusplus +# include "BKE_dyntopo_set.hh" +#endif + #include "BLI_bitmap.h" #include "BLI_compiler_compat.h" #include "BLI_ghash.h" @@ -41,7 +45,6 @@ struct Scene; struct CCGElem; struct CCGKey; struct CustomData; -struct TableGSet; struct DMFlagMat; struct IsectRayPrecalc; struct MLoopTri; @@ -125,6 +128,7 @@ typedef struct PBVHFaceRef { #define PBVH_REF_NONE -1LL +#ifdef __cplusplus typedef struct PBVHTri { int v[3]; // references into PBVHTriBuf->verts int eflag; // bitmask of which edges in the tri are real edges in the mesh @@ -141,17 +145,21 @@ typedef struct PBVHTriBuf { int totvert, totedge, tottri; int verts_size, edges_size, tris_size; -#ifdef __cplusplus +# ifdef __cplusplus blender::Map vertmap; -#else +# else void *vertmap; -#endif +# endif // private field intptr_t *loops; int totloop, mat_nr; float min[3], max[3]; } PBVHTriBuf; +#else +struct PBVHTri; +struct PBVHTriBuf; +#endif typedef struct { float (*co)[3]; @@ -609,9 +617,11 @@ bool BKE_pbvh_node_frustum_contain_AABB(PBVHNode *node, void *frustum); */ bool BKE_pbvh_node_frustum_exclude_AABB(PBVHNode *node, void *frustum); -struct TableGSet *BKE_pbvh_bmesh_node_unique_verts(PBVHNode *node); -struct TableGSet *BKE_pbvh_bmesh_node_other_verts(PBVHNode *node); -struct TableGSet *BKE_pbvh_bmesh_node_faces(PBVHNode *node); +#ifdef __cplusplus +blender::bke::dyntopo::DyntopoSet *BKE_pbvh_bmesh_node_unique_verts(PBVHNode *node); +blender::bke::dyntopo::DyntopoSet *BKE_pbvh_bmesh_node_other_verts(PBVHNode *node); +blender::bke::dyntopo::DyntopoSet *BKE_pbvh_bmesh_node_faces(PBVHNode *node); +#endif void BKE_pbvh_bmesh_regen_node_verts(PBVH *pbvh, bool report); void BKE_pbvh_bmesh_mark_node_regen(PBVH *pbvh, PBVHNode *node); @@ -658,6 +668,7 @@ bool BKE_pbvh_is_deformed(PBVH *pbvh); #define PBVH_ITER_ALL 0 #define PBVH_ITER_UNIQUE 1 +#ifdef __cplusplus typedef struct PBVHVertexIter { /* iteration */ int g; @@ -689,8 +700,9 @@ typedef struct PBVHVertexIter { /* bmesh */ int bi; - struct TableGSet *bm_cur_set; - struct TableGSet *bm_unique_verts, *bm_other_verts; + int bm_cur_set; + blender::bke::dyntopo::DyntopoSet *bm_unique_verts, *bm_other_verts; + blender::bke::dyntopo::DyntopoSet::iterator bm_iter, bm_iter_end; struct CustomData *bm_vdata; int cd_vert_mask_offset; @@ -705,8 +717,11 @@ typedef struct PBVHVertexIter { float *mask; bool visible; } PBVHVertexIter; +#else +struct PBVHVertexIter; +#endif -void pbvh_vertex_iter_init(PBVH *pbvh, PBVHNode *node, PBVHVertexIter *vi, int mode); +void pbvh_vertex_iter_init(PBVH *pbvh, PBVHNode *node, struct PBVHVertexIter *vi, int mode); #define BKE_pbvh_vertex_iter_begin(pbvh, node, vi, mode) \ pbvh_vertex_iter_init(pbvh, node, &vi, mode); \ @@ -755,27 +770,21 @@ void pbvh_vertex_iter_init(PBVH *pbvh, PBVHNode *node, PBVHVertexIter *vi, int m } \ } \ else { \ - BMVert *bv = NULL; \ - while (!bv) { \ - if (!vi.bm_cur_set->elems || vi.bi >= vi.bm_cur_set->cur) { \ - if (vi.bm_cur_set != vi.bm_other_verts && mode != PBVH_ITER_UNIQUE) { \ - vi.bm_cur_set = vi.bm_other_verts; \ - vi.bi = 0; \ - if (!vi.bm_cur_set->elems || vi.bi >= vi.bm_other_verts->cur) { \ - break; \ - } \ - } \ - else { \ - break; \ + if (vi.bm_iter == vi.bm_iter_end) { \ + if (vi.bm_cur_set == 0 && mode == PBVH_ITER_ALL) { \ + vi.bm_cur_set = 1; \ + vi.bm_iter = vi.bm_other_verts->begin(); \ + vi.bm_iter_end = vi.bm_other_verts->end(); \ + if (vi.bm_iter == vi.bm_iter_end) { \ + continue; \ } \ } \ else { \ - bv = (BMVert *)vi.bm_cur_set->elems[vi.bi++]; \ + continue; \ } \ } \ - if (!bv) { \ - continue; \ - } \ + BMVert *bv = *vi.bm_iter; \ + ++vi.bm_iter; \ vi.bm_vert = bv; \ vi.vertex.i = (intptr_t)bv; \ vi.index = BM_elem_index_get(vi.bm_vert); \ @@ -808,7 +817,8 @@ PBVHFaceRef BKE_pbvh_index_to_face(PBVH *pbvh, int idx); #define PBVH_FACE_ITER_VERTS_RESERVED 8 -typedef struct PBVHFaceIter { +#ifdef __cplusplus +struct PBVHFaceIter { PBVHFaceRef face; int index; bool *hide; @@ -822,8 +832,7 @@ typedef struct PBVHFaceIter { const PBVHNode *node_; PBVHType pbvh_type_; int verts_size_; - int bm_faces_iter_; - const struct TableGSet *bm_faces_; + blender::bke::dyntopo::DyntopoSet::iterator bm_iter_, bm_iter_end_; int cd_face_set_; bool *hide_poly_; int *face_sets_; @@ -836,13 +845,15 @@ typedef struct PBVHFaceIter { CCGKey subdiv_key_; int last_poly_index_; -} PBVHFaceIter; +}; void BKE_pbvh_face_iter_init(PBVH *pbvh, PBVHNode *node, PBVHFaceIter *fd); void BKE_pbvh_face_iter_step(PBVHFaceIter *fd); bool BKE_pbvh_face_iter_done(PBVHFaceIter *fd); void BKE_pbvh_face_iter_finish(PBVHFaceIter *fd); +#endif + /** * Iterate over faces inside a #PBVHNode. These are either base mesh faces * (for PBVH_FACES and PBVH_GRIDS) or BMesh faces (for PBVH_BMESH). @@ -943,7 +954,7 @@ bool BKE_pbvh_curvature_update_get(PBVHNode *node); int BKE_pbvh_get_totnodes(PBVH *pbvh); bool BKE_pbvh_bmesh_check_tris(PBVH *pbvh, PBVHNode *node); -PBVHTriBuf *BKE_pbvh_bmesh_get_tris(PBVH *pbvh, PBVHNode *node); +struct PBVHTriBuf *BKE_pbvh_bmesh_get_tris(PBVH *pbvh, PBVHNode *node); void BKE_pbvh_bmesh_free_tris(PBVH *pbvh, PBVHNode *node); /*recalculates boundary flags for *all* vertices. used by diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt index 70846185140..309704f74aa 100644 --- a/source/blender/blenkernel/CMakeLists.txt +++ b/source/blender/blenkernel/CMakeLists.txt @@ -380,6 +380,7 @@ set(SRC BKE_duplilist.h BKE_dynamicpaint.h BKE_dyntopo.hh + BKE_dyntopo_set.hh BKE_editlattice.h BKE_editmesh.h BKE_editmesh_bvh.h diff --git a/source/blender/blenkernel/intern/dyntopo.cc b/source/blender/blenkernel/intern/dyntopo.cc index c3b66718502..23b6e3bf60d 100644 --- a/source/blender/blenkernel/intern/dyntopo.cc +++ b/source/blender/blenkernel/intern/dyntopo.cc @@ -684,8 +684,7 @@ static void unified_edge_queue_task_cb(void *__restrict userdata, * so randomly skip bits of it. */ #ifdef CLEAR_TAGS_IN_THREAD - BMFace *f; - TGSET_ITER (f, node->bm_faces) { + for (BMFace *f : *node->bm_faces) { BMLoop *l = f->l_first; /* Note that f itself is owned by this node. */ @@ -711,7 +710,6 @@ static void unified_edge_queue_task_cb(void *__restrict userdata, l = l->next; } while (l != f->l_first); } - TGSET_ITER_END #endif PBVHTriBuf *tribuf = node->tribuf; @@ -1311,9 +1309,7 @@ static void unified_edge_queue_create(EdgeQueueContext *eq_ctx, continue; } - BMFace *f; - - TGSET_ITER (f, node->bm_faces) { + for (BMFace *f : *node->bm_faces) { BMLoop *l = f->l_first; do { l->e->head.hflag &= ~BM_ELEM_TAG; @@ -1321,7 +1317,6 @@ static void unified_edge_queue_create(EdgeQueueContext *eq_ctx, l->f->head.hflag &= ~(BM_ELEM_TAG | BM_ELEM_TAG_ALT); } while ((l = l->next) != f->l_first); } - TGSET_ITER_END; } #endif @@ -1404,9 +1399,7 @@ static void short_edge_queue_task_cb_local(void *__restrict userdata, PBVHNode *node = tdata->node; EdgeQueueContext *eq_ctx = tdata->eq_ctx; - BMFace *f; - - TGSET_ITER (f, node->bm_faces) { + for (BMFace *f : *node->bm_faces) { #ifdef USE_EDGEQUEUE_FRONTFACE if (eq_ctx->use_view_normal) { if (dot_v3v3(f->no, eq_ctx->view_normal) < 0.0f) { @@ -1425,7 +1418,6 @@ static void short_edge_queue_task_cb_local(void *__restrict userdata, } while ((l = l->next) != f->l_first); } } - TGSET_ITER_END } static void edge_queue_create_local(EdgeQueueContext *eq_ctx, @@ -1975,13 +1967,11 @@ static bool do_cleanup_3_4(EdgeQueueContext *eq_ctx, PBVH *pbvh) eq_ctx->used_verts.clear(); for (const PBVHNode &node : Span(pbvh->nodes, pbvh->totnode)) { - BMVert *v; - if (!(node.flag & PBVH_Leaf) || !(node.flag & PBVH_UpdateTopology)) { continue; } - TGSET_ITER (v, node.bm_unique_verts) { + for (BMVert *v : *node.bm_unique_verts) { if (dyntopo_test_flag(pbvh, v, SCULPTFLAG_NEED_VALENCE)) { BKE_pbvh_bmesh_update_valence(pbvh, {(intptr_t)v}); } @@ -1993,7 +1983,6 @@ static bool do_cleanup_3_4(EdgeQueueContext *eq_ctx, PBVH *pbvh) edge_queue_insert_val34_vert(eq_ctx, v); } } - TGSET_ITER_END; } BM_log_entry_add_ex(pbvh->header.bm, pbvh->bm_log, true); @@ -2822,7 +2811,7 @@ static void pbvh_split_edges( node->flag |= (PBVHNodeFlags)node_updateflag; - BLI_table_gset_add(node->bm_unique_verts, newv); + node->bm_unique_verts->add(newv); BM_ELEM_CD_SET_INT(newv, pbvh->cd_vert_node_offset, ni); // BM_ELEM_CD_SET_INT(newv, pbvh->cd_vert_node_offset, -1); @@ -2980,7 +2969,7 @@ static void pbvh_split_edges( node->flag |= (PBVHNodeFlags)node_updateflag; - BLI_table_gset_add(node->bm_faces, newf); + node->bm_faces->add(newf); BM_ELEM_CD_SET_INT(newf, pbvh->cd_face_node_offset, ni); } else { diff --git a/source/blender/blenkernel/intern/dyntopo_intern.hh b/source/blender/blenkernel/intern/dyntopo_intern.hh index dd6d2f2a221..9b14c9a4ea1 100644 --- a/source/blender/blenkernel/intern/dyntopo_intern.hh +++ b/source/blender/blenkernel/intern/dyntopo_intern.hh @@ -421,23 +421,23 @@ ENUM_OPERATORS(eValidateFaceFlags, CHECK_FACE_MANIFOLD); #ifndef CHECKMESH template inline bool validate_elem(PBVH *pbvh, T *elem){return true}; -inline bool validate_vert(PBVH *pbvh, BMVert *v, eValidateVertFlags flags = CHECK_VERT_NONE) +inline bool validate_vert(PBVH *, BMVert *, eValidateVertFlags) { return true; } -inline bool validate_edge(PBVH *pbvh, BMEdge *e) +inline bool validate_edge(PBVH *, BMEdge *) { return true; } -inline bool validate_loop(PBVH *pbvh, BMLoop *l) +inline bool validate_loop(PBVH *, BMLoop *) { return true; } -inline bool validate_face(PBVH *pbvh, BMFace *f, eValidateFaceFlags flags = CHECK_FACE_NONE) +inline bool validate_face(PBVH *, BMFace *, eValidateFaceFlags) { return true; } -inline bool check_face_is_manifold(PBVH *pbvh, BMFace *f) +inline bool check_face_is_manifold(PBVH *, BMFace *) { return true; } @@ -550,9 +550,7 @@ ATTR_NO_OPT static bool check_face_is_manifold(PBVH *pbvh, BMFace *f) return true; } -ATTR_NO_OPT static bool validate_face(PBVH *pbvh, - BMFace *f, - eValidateFaceFlags flags = CHECK_FACE_NONE) +ATTR_NO_OPT static bool validate_face(PBVH *pbvh, BMFace *f, eValidateFaceFlags flags) { if (!validate_elem(pbvh, f)) { return false; @@ -581,7 +579,7 @@ ATTR_NO_OPT static bool validate_face(PBVH *pbvh, return false; } - if (!BLI_table_gset_has(node->bm_faces, static_cast(f))) { + if (!node->bm_faces->contains(f)) { printf("face is not in node->bm_faces.\n"); return false; } @@ -589,9 +587,7 @@ ATTR_NO_OPT static bool validate_face(PBVH *pbvh, return ok; } -ATTR_NO_OPT static bool validate_vert(PBVH *pbvh, - BMVert *v, - eValidateVertFlags flags = CHECK_VERT_NONE) +ATTR_NO_OPT static bool validate_vert(PBVH *pbvh, BMVert *v, eValidateVertFlags flags) { using namespace blender::bke::dyntopo::debug; @@ -642,11 +638,11 @@ ATTR_NO_OPT static bool validate_vert(PBVH *pbvh, return false; } - if (!BLI_table_gset_haskey(node->bm_unique_verts, static_cast(v))) { + if (!node->bm_unique_verts->contains(v)) { printf("Vertex %p is not in node->bm_unique_verts\n"); return false; } - if (BLI_table_gset_haskey(node->bm_other_verts, static_cast(v))) { + if (node->bm_other_verts->contains(v)) { printf("Vertex %p is inside of node->bm_other_verts\n"); return false; } diff --git a/source/blender/blenkernel/intern/pbvh.cc b/source/blender/blenkernel/intern/pbvh.cc index 5f52ec3a04e..08c629e436c 100644 --- a/source/blender/blenkernel/intern/pbvh.cc +++ b/source/blender/blenkernel/intern/pbvh.cc @@ -69,6 +69,7 @@ using blender::OffsetIndices; using blender::Set; using blender::Span; using blender::Vector; +using blender::bke::dyntopo::DyntopoSet; #define LEAF_LIMIT 10000 @@ -187,7 +188,7 @@ static void update_node_vb(PBVH *pbvh, PBVHNode *node, int updateflag) return true; } - return bool(node->bm_faces ? node->bm_faces->length : node->totprim); + return bool(node->bm_faces ? node->bm_faces->size() : node->totprim); }; if (!(updateflag & (PBVH_UpdateBB | PBVH_UpdateOriginalBB))) { @@ -930,10 +931,7 @@ static void pbvh_draw_args_init(PBVH *pbvh, PBVH_GPU_Args *args, PBVHNode *node) args->vdata = &args->bm->vdata; args->ldata = &args->bm->ldata; args->pdata = &args->bm->pdata; - args->bm_faces = node->bm_faces; - args->bm_other_verts = node->bm_other_verts; - args->bm_unique_vert = node->bm_unique_verts; - args->totprim = BLI_table_gset_len(node->bm_faces); + args->totprim = node->bm_faces->size(); args->cd_mask_layer = CustomData_get_offset(&pbvh->header.bm->vdata, CD_PAINT_MASK); args->tribuf = node->tribuf; @@ -1253,13 +1251,13 @@ void BKE_pbvh_free(PBVH *pbvh) MEM_freeN((void *)node->face_vert_indices); } if (node->bm_faces) { - BLI_table_gset_free(node->bm_faces, nullptr); + MEM_delete>(node->bm_faces); } if (node->bm_unique_verts) { - BLI_table_gset_free(node->bm_unique_verts, nullptr); + MEM_delete>(node->bm_unique_verts); } if (node->bm_other_verts) { - BLI_table_gset_free(node->bm_other_verts, nullptr); + MEM_delete>(node->bm_other_verts); } if (node->tribuf || node->tri_buffers) { @@ -1402,8 +1400,8 @@ static PBVHNode *pbvh_iter_next_occluded(PBVHIter *iter) if (isnan(ff) || !isfinite(ff)) { printf("%s: nan! totf: %d totv: %d\n", __func__, - node->bm_faces ? node->bm_faces->length : 0, - node->bm_unique_verts ? node->bm_unique_verts->length : 0); + node->bm_faces ? node->bm_faces->size() : 0, + node->bm_unique_verts ? node->bm_unique_verts->size() : 0); } if (iter->scb && !iter->scb(node, iter->search_data)) { @@ -2029,28 +2027,19 @@ static void pbvh_grids_node_visibility_update(PBVH *pbvh, PBVHNode *node) static void pbvh_bmesh_node_visibility_update(PBVHNode *node) { - TableGSet *unique, *other; - - unique = BKE_pbvh_bmesh_node_unique_verts(node); - other = BKE_pbvh_bmesh_node_other_verts(node); - - BMVert *v; - - TGSET_ITER (v, unique) { + for (BMVert *v : *node->bm_unique_verts) { if (!BM_elem_flag_test(v, BM_ELEM_HIDDEN)) { BKE_pbvh_node_fully_hidden_set(node, false); return; } } - TGSET_ITER_END - TGSET_ITER (v, other) { + for (BMVert *v : *node->bm_other_verts) { if (!BM_elem_flag_test(v, BM_ELEM_HIDDEN)) { BKE_pbvh_node_fully_hidden_set(node, false); return; } } - TGSET_ITER_END BKE_pbvh_node_fully_hidden_set(node, true); } @@ -2446,9 +2435,9 @@ void BKE_pbvh_node_num_verts(PBVH *pbvh, PBVHNode *node, int *r_uniquevert, int pbvh_bmesh_check_other_verts(node); - tot = BLI_table_gset_len(node->bm_unique_verts); + tot = node->bm_unique_verts->size(); if (r_totvert) { - *r_totvert = tot + BLI_table_gset_len(node->bm_other_verts); + *r_totvert = tot + node->bm_other_verts->size(); } if (r_uniquevert) { *r_uniquevert = tot; @@ -3623,9 +3612,12 @@ void pbvh_vertex_iter_init(PBVH *pbvh, PBVHNode *node, PBVHVertexIter *vi, int m vi->vert_positions = nullptr; vi->bi = 0; - vi->bm_cur_set = node->bm_unique_verts; + vi->bm_cur_set = 0; vi->bm_unique_verts = node->bm_unique_verts; vi->bm_other_verts = node->bm_other_verts; + vi->bm_iter = node->bm_unique_verts->begin(); + vi->bm_iter_end = node->bm_unique_verts->end(); + vi->bm_vdata = &pbvh->header.bm->vdata; vi->bm_vert = nullptr; @@ -3883,14 +3875,12 @@ void BKE_pbvh_check_tri_areas(PBVH *pbvh, PBVHNode *node) case PBVH_GRIDS: break; case PBVH_BMESH: { - BMFace *f; const int cd_face_area = pbvh->cd_face_area; - TGSET_ITER (f, node->bm_faces) { + for (BMFace *f : *node->bm_faces) { float *areabuf = (float *)BM_ELEM_CD_GET_VOID_P(f, cd_face_area); areabuf[cur_i] = 0.0f; } - TGSET_ITER_END; for (int i = 0; i < node->tribuf->tottri; i++) { PBVHTri *tri = node->tribuf->tris + i; @@ -3904,7 +3894,7 @@ void BKE_pbvh_check_tri_areas(PBVH *pbvh, PBVHNode *node) areabuf[cur_i] += area_tri_v3(v1->co, v2->co, v3->co); } - TGSET_ITER (f, node->bm_faces) { + for (BMFace *f : *node->bm_faces) { float *areabuf = (float *)BM_ELEM_CD_GET_VOID_P(f, cd_face_area); /* sanity check on read side of read write buffer */ @@ -3912,7 +3902,6 @@ void BKE_pbvh_check_tri_areas(PBVH *pbvh, PBVHNode *node) areabuf[cur_i ^ 1] = areabuf[cur_i]; } } - TGSET_ITER_END; break; } @@ -4221,7 +4210,7 @@ void set_original(PBVH *pbvh, Span origco, Span origno) void update_vert_boundary_faces(int *boundary_flags, const int *face_sets, const bool *hide_poly, - const float (*vert_positions)[3], + const float (*/*vert_positions*/)[3], const int2 * /*medge*/, const int *corner_verts, const int *corner_edges, @@ -4486,19 +4475,14 @@ static void pbvh_face_iter_step(PBVHFaceIter *fd, bool do_step) switch (fd->pbvh_type_) { case PBVH_BMESH: { if (do_step) { - fd->bm_faces_iter_++; + ++fd->bm_iter_; } - while (fd->bm_faces_iter_ < fd->bm_faces_->cur && !fd->bm_faces_->elems[fd->bm_faces_iter_]) - { - fd->bm_faces_iter_++; - } - - if (fd->bm_faces_iter_ >= fd->bm_faces_->cur) { + if (fd->bm_iter_ == fd->bm_iter_end_) { return; } - BMFace *f = (BMFace *)fd->bm_faces_->elems[fd->bm_faces_iter_]; + BMFace *f = *fd->bm_iter_; fd->face.i = (intptr_t)f; fd->index = f->head.index; @@ -4609,8 +4593,8 @@ void BKE_pbvh_face_iter_init(PBVH *pbvh, PBVHNode *node, PBVHFaceIter *fd) fd->cd_face_set_ = CustomData_get_offset_named( &pbvh->header.bm->pdata, CD_PROP_INT32, ".sculpt_face_set"); - fd->bm_faces_iter_ = 0; - fd->bm_faces_ = node->bm_faces; + fd->bm_iter_ = node->bm_faces->begin(); + fd->bm_iter_end_ = node->bm_faces->end(); break; } @@ -4633,7 +4617,7 @@ bool BKE_pbvh_face_iter_done(PBVHFaceIter *fd) case PBVH_GRIDS: return fd->prim_index_ >= fd->node_->totprim; case PBVH_BMESH: - return fd->bm_faces_iter_ >= fd->bm_faces_->cur; + return fd->bm_iter_ == fd->bm_iter_end_; default: BLI_assert_unreachable(); return true; diff --git a/source/blender/blenkernel/intern/pbvh_bmesh.cc b/source/blender/blenkernel/intern/pbvh_bmesh.cc index 5f7c00c29f4..5f70f0c3649 100644 --- a/source/blender/blenkernel/intern/pbvh_bmesh.cc +++ b/source/blender/blenkernel/intern/pbvh_bmesh.cc @@ -81,6 +81,7 @@ using blender::IndexRange; using blender::Map; using blender::Set; using blender::Vector; +using blender::bke::dyntopo::DyntopoSet; template T *c_array_from_vector(Vector &array) { @@ -108,7 +109,7 @@ void pbvh_bmesh_check_nodes_simple(PBVH *pbvh) continue; } - TGSET_ITER (f, node->bm_faces) { + for (BMFace *f : *node->bm_faces) { if (!f || f->head.htype != BM_FACE) { _debugprint("Corrupted (freed?) face in node->bm_faces\n"); continue; @@ -118,7 +119,6 @@ void pbvh_bmesh_check_nodes_simple(PBVH *pbvh) _debugprint("Face in more then one node\n"); } } - TGSET_ITER_END; } } @@ -158,11 +158,11 @@ void pbvh_bmesh_check_nodes(PBVH *pbvh) continue; } - if (!BLI_table_gset_haskey(node->bm_unique_verts, v)) { + if (!node->bm_unique_verts->contains(v)) { _debugprint("vert not in node->bm_unique_verts\n"); } - if (BLI_table_gset_haskey(node->bm_other_verts, v)) { + if (node->bm_other_verts->contains(v)) { _debugprint("vert in node->bm_other_verts"); } @@ -193,15 +193,14 @@ void pbvh_bmesh_check_nodes(PBVH *pbvh) continue; } - TGSET_ITER (v, node->bm_unique_verts) { + for (BMVert *v : *node->bm_unique_verts) { int ni = BM_ELEM_CD_GET_INT(v, pbvh->cd_vert_node_offset); if (ni != i) { if (ni >= 0 && ni < pbvh->totnode) { PBVHNode *node2 = pbvh->nodes + ni; _debugprint("v node offset is wrong, %d\n", - !node2->bm_unique_verts ? 0 : - BLI_table_gset_haskey(node2->bm_unique_verts, v)); + !node2->bm_unique_verts ? 0 : node2->bm_unique_verts->contains(v)); } else { _debugprint("v node offset is wrong\n"); @@ -211,13 +210,12 @@ void pbvh_bmesh_check_nodes(PBVH *pbvh) if (!v || v->head.htype != BM_VERT) { _debugprint("corruption in pbvh! bm_unique_verts\n"); } - else if (BLI_table_gset_haskey(node->bm_other_verts, v)) { + else if (node->bm_other_verts->contains(v)) { _debugprint("v in both unique and other verts\n"); } } - TGSET_ITER_END; - TGSET_ITER (f, node->bm_faces) { + for (BMFace *f : *node->bm_faces) { if (!f || f->head.htype != BM_FACE) { _debugprint("corruption in pbvh! bm_faces\n"); continue; @@ -228,17 +226,15 @@ void pbvh_bmesh_check_nodes(PBVH *pbvh) _debugprint("face in multiple nodes!\n"); } } - TGSET_ITER_END; - TGSET_ITER (v, node->bm_other_verts) { + for (BMVert *v : node->bm_other_verts) { if (!v || v->head.htype != BM_VERT) { _debugprint("corruption in pbvh! bm_other_verts\n"); } - else if (BLI_table_gset_haskey(node->bm_unique_verts, v)) { + else if (node->bm_unique_verts->contains(v)) { _debugprint("v in both unique and other verts\n"); } } - TGSET_ITER_END; } } @@ -333,7 +329,7 @@ static BMVert *pbvh_bmesh_vert_create(PBVH *pbvh, copy_v3_v3(v->no, no); } - BLI_table_gset_insert(node->bm_unique_verts, v); + node->bm_unique_verts->add(v); BM_ELEM_CD_SET_INT(v, pbvh->cd_vert_node_offset, node_index); node->flag |= PBVH_UpdateDrawBuffers | PBVH_UpdateBB | PBVH_UpdateTris | PBVH_UpdateOtherVerts; @@ -401,7 +397,7 @@ BMFace *pbvh_bmesh_face_create(PBVH *pbvh, BMFace *f = bmesh_face_create_edge_log(pbvh, v_tri, e_tri, f_example); - BLI_table_gset_insert(node->bm_faces, f); + node->bm_faces->add(f); BM_ELEM_CD_SET_INT(f, pbvh->cd_face_node_offset, node_index); /* mark node for update */ @@ -422,7 +418,7 @@ BMFace *pbvh_bmesh_face_create(PBVH *pbvh, int ni = BM_ELEM_CD_GET_INT(l->v, cd_vert_node); if (ni == DYNTOPO_NODE_NONE) { - BLI_table_gset_add(node->bm_unique_verts, l->v); + node->bm_unique_verts->add(l->v); BM_ELEM_CD_SET_INT(l->v, cd_vert_node, node_index); node->flag |= PBVH_UpdateDrawBuffers | PBVH_UpdateBB | PBVH_UpdateTris | @@ -543,8 +539,7 @@ BMFace *BKE_pbvh_face_create_bmesh(PBVH *pbvh, } } - if (ok && - (ni == DYNTOPO_NODE_NONE || BLI_table_gset_len(node->bm_faces) < pbvh->leaf_limit)) { + if (ok && (ni == DYNTOPO_NODE_NONE || node->bm_faces->size() < pbvh->leaf_limit)) { ni = i; break; } @@ -622,12 +617,12 @@ static void pbvh_bmesh_vert_ownership_transfer(PBVH *pbvh, PBVHNode *new_owner, BLI_assert(current_owner != new_owner); /* Remove current ownership. */ - BLI_table_gset_remove(current_owner->bm_unique_verts, v, nullptr); + current_owner->bm_unique_verts->remove(v); } /* Set new ownership. */ BM_ELEM_CD_SET_INT(v, pbvh->cd_vert_node_offset, new_owner - pbvh->nodes); - BLI_table_gset_insert(new_owner->bm_unique_verts, v); + new_owner->bm_unique_verts->add(v); /* Mark node for update. */ new_owner->flag |= PBVH_UpdateDrawBuffers | PBVH_UpdateBB | PBVH_UpdateOtherVerts; @@ -643,7 +638,7 @@ void pbvh_bmesh_vert_remove(PBVH *pbvh, BMVert *v) PBVHNode *v_node = pbvh_bmesh_node_from_vert(pbvh, v); if (v_node && v_node->bm_unique_verts) { - BLI_table_gset_remove(v_node->bm_unique_verts, v, nullptr); + v_node->bm_unique_verts->remove(v); v_node->flag |= (PBVHNodeFlags)updateflag; } @@ -658,21 +653,22 @@ void pbvh_bmesh_vert_remove(PBVH *pbvh, BMVert *v) continue; } - /* faces often share the same node, - * quick check to avoid redundant #BLI_table_gset_remove calls */ + /* Faces often share the same node, + * quick check to avoid redundant set removal calls. + */ if (f_node_index_prev != f_node_index) { f_node_index_prev = f_node_index; PBVHNode *f_node = &pbvh->nodes[f_node_index]; f_node->flag |= (PBVHNodeFlags)updateflag; // flag update of bm_other_verts - BLI_assert(!BLI_table_gset_haskey(f_node->bm_unique_verts, v)); + BLI_assert(!f_node->bm_unique_verts->contains(v)); } } BM_FACES_OF_VERT_ITER_END; } -void pbvh_bmesh_face_remove( +ATTR_NO_OPT void pbvh_bmesh_face_remove( PBVH *pbvh, BMFace *f, bool log_face, bool check_verts, bool ensure_ownership_transfer) { PBVHNode *f_node = pbvh_bmesh_node_from_face(pbvh, f); @@ -694,12 +690,8 @@ void pbvh_bmesh_face_remove( BMVert *v = l_iter->v; if (pbvh_bmesh_node_vert_use_count_is_equal(pbvh, f_node, v, 1)) { if (BM_ELEM_CD_GET_INT(v, pbvh->cd_vert_node_offset) == f_node - pbvh->nodes) { - // if (BLI_table_gset_haskey(f_node->bm_unique_verts, v)) { /* Find a different node that uses 'v' */ - PBVHNode *new_node; - - new_node = pbvh_bmesh_vert_other_node_find(pbvh, v); - // BLI_assert(new_node || BM_vert_face_count_is_equal(v, 1)); + PBVHNode *new_node = pbvh_bmesh_vert_other_node_find(pbvh, v); if (new_node) { pbvh_bmesh_vert_ownership_transfer(pbvh, new_node, v); @@ -716,7 +708,7 @@ void pbvh_bmesh_face_remove( } /* Remove face from node and top level */ - BLI_table_gset_remove(f_node->bm_faces, f, nullptr); + f_node->bm_faces->remove(f); BM_ELEM_CD_SET_INT(f, pbvh->cd_face_node_offset, DYNTOPO_NODE_NONE); /* Log removed face */ @@ -748,15 +740,14 @@ static void pbvh_bmesh_node_finalize(PBVH *pbvh, /* Create vert hash sets */ if (!n->bm_unique_verts) { - n->bm_unique_verts = BLI_table_gset_new("bm_unique_verts"); + n->bm_unique_verts = MEM_new>("bm_unique_verts"); } - n->bm_other_verts = BLI_table_gset_new("bm_other_verts"); + n->bm_other_verts = MEM_new>("bm_other_verts"); BB_reset(&n->vb); BB_reset(&n->orig_vb); - BMFace *f; - TGSET_ITER (f, n->bm_faces) { + for (BMFace *f : *n->bm_faces) { /* Update ownership of faces */ BM_ELEM_CD_SET_INT(f, cd_face_node_offset, node_index); @@ -770,12 +761,12 @@ static void pbvh_bmesh_node_finalize(PBVH *pbvh, int *flags = BM_ELEM_CD_PTR(v, pbvh->cd_boundary_flag); *flags |= SCULPT_BOUNDARY_NEEDS_UPDATE; - if (!BLI_table_gset_haskey(n->bm_unique_verts, v)) { + if (!n->bm_unique_verts->contains(v)) { if (BM_ELEM_CD_GET_INT(v, cd_vert_node_offset) != DYNTOPO_NODE_NONE) { - BLI_table_gset_add(n->bm_other_verts, v); + n->bm_other_verts->add(v); } else { - BLI_table_gset_insert(n->bm_unique_verts, v); + n->bm_unique_verts->add(v); BM_ELEM_CD_SET_INT(v, cd_vert_node_offset, node_index); } } @@ -788,7 +779,6 @@ static void pbvh_bmesh_node_finalize(PBVH *pbvh, has_visible = true; } } - TGSET_ITER_END BLI_assert(n->vb.bmin[0] <= n->vb.bmax[0] && n->vb.bmin[1] <= n->vb.bmax[1] && n->vb.bmin[2] <= n->vb.bmax[2]); @@ -872,7 +862,7 @@ static void pbvh_bmesh_node_split( BKE_pbvh_free_proxyarray(pbvh, n); #endif - if (n->depth >= PBVH_STACK_FIXED_DEPTH || BLI_table_gset_len(n->bm_faces) <= pbvh->leaf_limit) { + if (n->depth >= PBVH_STACK_FIXED_DEPTH || n->bm_faces->size() <= pbvh->leaf_limit) { /* Node limit not exceeded */ pbvh_bmesh_node_finalize(pbvh, node_index, cd_vert_node_offset, cd_face_node_offset, add_orco); return; @@ -881,14 +871,12 @@ static void pbvh_bmesh_node_split( /* Calculate bounding box around primitive centroids */ BB cb; BB_reset(&cb); - BMFace *f; - TGSET_ITER (f, n->bm_faces) { + for (BMFace *f : *n->bm_faces) { const BBC *bbc = &bbc_array[BM_elem_index_get(f)]; BB_expand(&cb, bbc->bcentroid); } - TGSET_ITER_END /* Find widest axis and its midpoint */ const int axis = BB_widest_axis(&cb); @@ -915,84 +903,59 @@ static void pbvh_bmesh_node_split( c1->flag |= PBVH_Leaf; c2->flag |= PBVH_Leaf; - c1->bm_faces = BLI_table_gset_new_ex("bm_faces", BLI_table_gset_len(n->bm_faces) / 2); - c2->bm_faces = BLI_table_gset_new_ex("bm_faces", BLI_table_gset_len(n->bm_faces) / 2); + c1->bm_faces = MEM_new>("bm_faces", int64_t(n->bm_faces->size() >> 1)); + c2->bm_faces = MEM_new>("bm_faces", int64_t(n->bm_faces->size() >> 1)); - c1->bm_unique_verts = BLI_table_gset_new("bm_unique_verts"); - c2->bm_unique_verts = BLI_table_gset_new("bm_unique_verts"); + c1->bm_unique_verts = MEM_new>("bm_unique_verts"); + c2->bm_unique_verts = MEM_new>("bm_unique_verts"); c1->bm_other_verts = c2->bm_other_verts = nullptr; /* Partition the parent node's faces between the two children */ - TGSET_ITER (f, n->bm_faces) { + for (BMFace *f : *n->bm_faces) { const BBC *bbc = &bbc_array[BM_elem_index_get(f)]; if (bbc->bcentroid[axis] < mid) { - BLI_table_gset_insert(c1->bm_faces, f); + c1->bm_faces->add(f); } else { - BLI_table_gset_insert(c2->bm_faces, f); + c2->bm_faces->add(f); } } - TGSET_ITER_END -#if 0 - /* Enforce at least one primitive in each node */ - TableGSet *empty = nullptr,*other; - if (BLI_table_gset_len(c1->bm_faces) == 0) { - empty = c1->bm_faces; - other = c2->bm_faces; - } else if (BLI_table_gset_len(c2->bm_faces) == 0) { - empty = c2->bm_faces; - other = c1->bm_faces; - } - if (empty) { - void *key; - TGSET_ITER (key,other) { - BLI_table_gset_insert(empty,key); - BLI_table_gset_remove(other,key,nullptr); - break; - } - TGSET_ITER_END - } -#endif /* Clear this node */ - BMVert *v; - /* Assign verts to c1 and c2. Note that the previous method of simply marking them as untaken and rebuilding unique verts later doesn't work, as it assumes that dyntopo never assigns verts to nodes that don't contain their faces.*/ if (n->bm_unique_verts) { - TGSET_ITER (v, n->bm_unique_verts) { + for (BMVert *v : *n->bm_unique_verts) { if (v->co[axis] < mid) { BM_ELEM_CD_SET_INT(v, cd_vert_node_offset, (c1 - pbvh->nodes)); - BLI_table_gset_add(c1->bm_unique_verts, v); + c1->bm_unique_verts->add(v); } else { BM_ELEM_CD_SET_INT(v, cd_vert_node_offset, (c2 - pbvh->nodes)); - BLI_table_gset_add(c2->bm_unique_verts, v); + c2->bm_unique_verts->add(v); } } - TGSET_ITER_END - BLI_table_gset_free(n->bm_unique_verts, nullptr); + MEM_delete(n->bm_unique_verts); } if (n->bm_faces) { /* Unclaim faces */ - TGSET_ITER (f, n->bm_faces) { + for (BMFace *f : *n->bm_faces) { BM_ELEM_CD_SET_INT(f, cd_face_node_offset, DYNTOPO_NODE_NONE); } - TGSET_ITER_END - BLI_table_gset_free(n->bm_faces, nullptr); + MEM_delete(n->bm_faces); } if (n->bm_other_verts) { - BLI_table_gset_free(n->bm_other_verts, nullptr); + MEM_delete(n->bm_other_verts); } if (n->layer_disp) { @@ -1031,8 +994,8 @@ static void pbvh_bmesh_node_split( /* Recursively split the node if it exceeds the leaf_limit */ bool pbvh_bmesh_node_limit_ensure(PBVH *pbvh, int node_index) { - TableGSet *bm_faces = pbvh->nodes[node_index].bm_faces; - const int bm_faces_size = BLI_table_gset_len(bm_faces); + DyntopoSet *bm_faces = pbvh->nodes[node_index].bm_faces; + const int bm_faces_size = bm_faces->size(); if (bm_faces_size <= pbvh->leaf_limit || pbvh->nodes[node_index].depth >= PBVH_STACK_FIXED_DEPTH) { @@ -1046,20 +1009,9 @@ bool pbvh_bmesh_node_limit_ensure(PBVH *pbvh, int node_index) /* For each BMFace, store the AABB and AABB centroid */ BBC *bbc_array = MEM_cnew_array(bm_faces_size, "BBC"); - BMFace *f; + int i = 0; + for (BMFace *f : *bm_faces) { - int i; - - /* - TGSET_ITER_INDEX(f, bm_faces, i) - { - } - TGSET_ITER_INDEX_END - printf("size: %d %d\n", i + 1, bm_faces_size); - */ - - TGSET_ITER_INDEX(f, bm_faces, i) - { BBC *bbc = &bbc_array[i]; BB_reset((BB *)bbc); @@ -1072,8 +1024,8 @@ bool pbvh_bmesh_node_limit_ensure(PBVH *pbvh, int node_index) /* so we can do direct lookups on 'bbc_array' */ BM_elem_index_set(f, i); /* set_dirty! */ + i++; } - TGSET_ITER_INDEX_END /* Likely this is already dirty. */ pbvh->header.bm->elem_index_dirty |= BM_FACE; @@ -1103,7 +1055,7 @@ void bke_pbvh_insert_face_finalize(PBVH *pbvh, BMFace *f, const int ni) return; } - BLI_table_gset_add(node->bm_faces, f); + node->bm_faces->add(f); PBVHNodeFlags updateflag = PBVH_UpdateTris | PBVH_UpdateBB | PBVH_UpdateDrawBuffers | PBVH_UpdateCurvatureDir | PBVH_UpdateOtherVerts; @@ -1123,13 +1075,13 @@ void bke_pbvh_insert_face_finalize(PBVH *pbvh, BMFace *f, const int ni) if (ni2 == DYNTOPO_NODE_NONE) { BM_ELEM_CD_SET_INT(l->v, pbvh->cd_vert_node_offset, ni); - BLI_table_gset_add(node->bm_unique_verts, l->v); + node->bm_unique_verts->add(l->v); } else { PBVHNode *node2 = pbvh->nodes + ni2; if (ni != ni2) { - BLI_table_gset_add(node->bm_other_verts, l->v); + node->bm_other_verts->add(l->v); } node2->flag |= updateflag; @@ -1242,11 +1194,11 @@ static void pbvh_bmesh_regen_node_verts(PBVH *pbvh, PBVHNode *node, bool report) { node->flag &= ~PBVH_RebuildNodeVerts; - int usize = BLI_table_gset_len(node->bm_unique_verts); - int osize = BLI_table_gset_len(node->bm_other_verts); + int usize = node->bm_unique_verts->size(); + int osize = node->bm_other_verts->size(); - TableGSet *old_unique_verts = node->bm_unique_verts; - TableGSet *old_other_verts = node->bm_other_verts; + DyntopoSet *old_unique_verts = node->bm_unique_verts; + DyntopoSet *old_other_verts = node->bm_other_verts; const int cd_vert_node = pbvh->cd_vert_node_offset; const int ni = (int)(node - pbvh->nodes); @@ -1269,24 +1221,20 @@ static void pbvh_bmesh_regen_node_verts(PBVH *pbvh, PBVHNode *node, bool report) } }; - BMVert *v; - TGSET_ITER (v, old_unique_verts) { + for (BMVert *v : *old_unique_verts) { check_vert(v); } - TGSET_ITER_END; - TGSET_ITER (v, old_other_verts) { + for (BMVert *v : *old_other_verts) { check_vert(v); } - TGSET_ITER_END; - node->bm_unique_verts = BLI_table_gset_new("bm_unique_verts"); - node->bm_other_verts = BLI_table_gset_new("bm_other_verts"); + node->bm_unique_verts = MEM_new>("bm_unique_verts"); + node->bm_other_verts = MEM_new>("bm_other_verts"); bool update = false; - BMFace *f; - TGSET_ITER (f, node->bm_faces) { + for (BMFace *f : *node->bm_faces) { BMLoop *l = f->l_first; do { int ni2 = BM_ELEM_CD_GET_INT(l->v, cd_vert_node); @@ -1298,16 +1246,15 @@ static void pbvh_bmesh_regen_node_verts(PBVH *pbvh, PBVHNode *node, bool report) } if (ni2 == ni) { - BLI_table_gset_add(node->bm_unique_verts, l->v); + node->bm_unique_verts->add(l->v); } else { - BLI_table_gset_add(node->bm_other_verts, l->v); + node->bm_other_verts->add(l->v); } } while ((l = l->next) != f->l_first); } - TGSET_ITER_END; - TGSET_ITER (v, old_unique_verts) { + for (BMVert *v : *old_unique_verts) { if (BM_elem_is_free(reinterpret_cast(v), BM_VERT)) { if (report) { printf("%s: corrupted vertex %p\n", __func__, v); @@ -1328,8 +1275,8 @@ static void pbvh_bmesh_regen_node_verts(PBVH *pbvh, PBVHNode *node, bool report) BM_ELEM_CD_SET_INT(v, pbvh->cd_vert_node_offset, ni2); PBVHNode *node = pbvh->nodes + ni2; - BLI_table_gset_add(node->bm_unique_verts, v); - BLI_table_gset_remove(node->bm_other_verts, v, nullptr); + node->bm_unique_verts->add(v); + node->bm_other_verts->remove(v); ok = true; break; @@ -1341,23 +1288,22 @@ static void pbvh_bmesh_regen_node_verts(PBVH *pbvh, PBVHNode *node, bool report) } } } - TGSET_ITER_END; - if (usize != BLI_table_gset_len(node->bm_unique_verts)) { + if (usize != node->bm_unique_verts->size()) { update = true; #if 0 printf("possible pbvh error: bm_unique_verts might have had bad data. old: %d, new: %d\n", usize, - BLI_table_gset_len(node->bm_unique_verts)); + node->bm_unique_verts->size()); #endif } - if (osize != BLI_table_gset_len(node->bm_other_verts)) { + if (osize != node->bm_other_verts->size()) { update = true; #if 0 printf("possible pbvh error: bm_other_verts might have had bad data. old: %d, new: %d\n", osize, - BLI_table_gset_len(node->bm_other_verts)); + node->bm_other_verts->size()); #endif } @@ -1368,8 +1314,8 @@ static void pbvh_bmesh_regen_node_verts(PBVH *pbvh, PBVHNode *node, bool report) PBVH_UpdateVisibility; } - BLI_table_gset_free(old_unique_verts, nullptr); - BLI_table_gset_free(old_other_verts, nullptr); + MEM_delete(old_unique_verts); + MEM_delete(old_other_verts); } void BKE_pbvh_bmesh_mark_node_regen(PBVH * /*pbvh*/, PBVHNode *node) @@ -1600,8 +1546,6 @@ static void pbvh_update_normals_task_cb(void *__restrict userdata, const int n, const TaskParallelTLS *__restrict /* tls */) { - BMVert *v; - BMFace *f; UpdateNormalsTaskData *data = ((UpdateNormalsTaskData *)userdata) + n; PBVHNode *node = data->node; const int node_nr = data->node_nr; @@ -1620,7 +1564,7 @@ static void pbvh_update_normals_task_cb(void *__restrict userdata, const char tag = BM_ELEM_TAG_ALT; - TGSET_ITER (v, node->bm_unique_verts) { + for (BMVert *v : *node->bm_other_verts) { PBVH_CHECK_NAN(v->no); if (NORMAL_VERT_BAD(v)) { @@ -1655,9 +1599,8 @@ static void pbvh_update_normals_task_cb(void *__restrict userdata, zero_v3(v->no); } - TGSET_ITER_END - TGSET_ITER (f, node->bm_faces) { + for (BMFace *f : *node->bm_faces) { BM_face_normal_update(f); PBVH_CHECK_NAN(f->no); @@ -1671,9 +1614,8 @@ static void pbvh_update_normals_task_cb(void *__restrict userdata, } } while ((l = l->next) != f->l_first); } - TGSET_ITER_END - TGSET_ITER (v, node->bm_unique_verts) { + for (BMVert *v : *node->bm_unique_verts) { PBVH_CHECK_NAN(v->no); if (dot_v3v3(v->no, v->no) == 0.0f) { @@ -1686,7 +1628,6 @@ static void pbvh_update_normals_task_cb(void *__restrict userdata, normalize_v3(v->no); } } - TGSET_ITER_END node->flag &= ~PBVH_UpdateNormals; } @@ -1935,11 +1876,11 @@ static void pbvh_bmesh_create_leaf_fast_task_cb(void *__restrict userdata, PBVH_UpdateDrawBuffers | PBVH_RebuildDrawBuffers | PBVH_UpdateCurvatureDir | PBVH_UpdateMask | PBVH_UpdateRedraw; - n->bm_faces = BLI_table_gset_new_ex("bm_faces", node->totface); + n->bm_faces = MEM_new>("bm_faces", node->totface); /* Create vert hash sets */ - n->bm_unique_verts = BLI_table_gset_new_ex("bm_unique_verts", node->totface * 3); - n->bm_other_verts = BLI_table_gset_new_ex("bm_other_verts", node->totface * 3); + n->bm_unique_verts = MEM_new>("bm_unique_verts", node->totface * 3); + n->bm_other_verts = MEM_new>("bm_other_verts", node->totface * 3); BB_reset(&n->vb); @@ -1950,7 +1891,7 @@ static void pbvh_bmesh_create_leaf_fast_task_cb(void *__restrict userdata, BBC *bbc = &bbc_array[BM_elem_index_get(f)]; /* Update ownership of faces */ - BLI_table_gset_insert(n->bm_faces, f); + n->bm_faces->add(f); BM_ELEM_CD_SET_INT(f, cd_face_node_offset, node_index); /* Update vertices */ @@ -1967,10 +1908,10 @@ static void pbvh_bmesh_create_leaf_fast_task_cb(void *__restrict userdata, if (old == DYNTOPO_NODE_NONE && atomic_cas_int32((int32_t *)ptr, DYNTOPO_NODE_NONE, node_index) == DYNTOPO_NODE_NONE) { - BLI_table_gset_insert(n->bm_unique_verts, v); + n->bm_unique_verts->add(v); } else { - BLI_table_gset_add(n->bm_other_verts, v); + n->bm_other_verts->add(v); } } while ((l_iter = l_iter->next) != l_first); @@ -2570,11 +2511,10 @@ void BKE_pbvh_build_bmesh(PBVH *pbvh, int area_dst_i = pbvh->face_area_i; /* make sure read side of double buffer is set too */ - TGSET_ITER (f, node->bm_faces) { + for (BMFace *f : *node->bm_faces) { float *areabuf = BM_ELEM_CD_PTR(f, cd_face_area); areabuf[area_dst_i] = areabuf[area_src_i]; } - TGSET_ITER_END; } } @@ -2760,22 +2700,20 @@ void pbvh_bmesh_check_other_verts(PBVHNode *node) node->flag &= ~PBVH_UpdateOtherVerts; if (node->bm_other_verts) { - BLI_table_gset_free(node->bm_other_verts, nullptr); + MEM_delete(node->bm_other_verts); } - node->bm_other_verts = BLI_table_gset_new("bm_other_verts"); - BMFace *f; + node->bm_other_verts = MEM_new>("bm_other_verts"); - TGSET_ITER (f, node->bm_faces) { + for (BMFace *f : *node->bm_faces) { BMLoop *l = f->l_first; do { - if (!BLI_table_gset_haskey(node->bm_unique_verts, l->v)) { - BLI_table_gset_add(node->bm_other_verts, l->v); + if (!node->bm_unique_verts->contains(l->v)) { + node->bm_other_verts->add(l->v); } } while ((l = l->next) != f->l_first); } - TGSET_ITER_END; } static void pbvh_init_tribuf(PBVHNode *node, PBVHTriBuf *tribuf) @@ -2862,13 +2800,11 @@ bool BKE_pbvh_bmesh_check_tris(PBVH *pbvh, PBVHNode *node) const int edgeflag = BM_ELEM_TAG_ALT; - BMFace *f; - float min[3], max[3]; INIT_MINMAX(min, max); - TGSET_ITER (f, node->bm_faces) { + for (BMFace *f : *node->bm_faces) { if (pbvh_poly_hidden(pbvh, f)) { continue; } @@ -2891,7 +2827,6 @@ bool BKE_pbvh_bmesh_check_tris(PBVH *pbvh, PBVHNode *node) tribufs->append(_tribuf); } -#ifdef DYNTOPO_DYNAMIC_TESS int tottri = (f->len - 2); loops.resize(f->len); @@ -2951,58 +2886,9 @@ bool BKE_pbvh_bmesh_check_tris(PBVH *pbvh, PBVHNode *node) tri->f.i = (intptr_t)f; mat_tri->f.i = (intptr_t)f; } -#else - PBVHTri *tri = pbvh_tribuf_add_tri(node->tribuf); - PBVHTriBuf *mat_tribuf = tribufs + mat_map[mat_nr]; - PBVHTri *mat_tri = pbvh_tribuf_add_tri(mat_tribuf); - - BMLoop *l = f->l_first; - int j = 0; - - do { - void **val = nullptr; - - if (!BLI_ghash_ensure_p(vmap, l->v, &val)) { - PBVHVertRef sv = {(intptr_t)l->v}; - - minmax_v3v3_v3(min, max, l->v->co); - - *val = (void *)node->tribuf->totvert; - pbvh_tribuf_add_vert(node->tribuf, sv); - } - - tri->v[j] = (intptr_t)val[0]; - tri->l[j] = (intptr_t)l; - - val = nullptr; - if (!BLI_ghash_ensure_p(mat_vmaps[mat_nr], l->v, &val)) { - PBVHVertRef sv = {(intptr_t)l->v}; - - minmax_v3v3_v3(min, max, l->v->co); - - *val = (void *)mat_tribuf->totvert; - pbvh_tribuf_add_vert(mat_tribuf, sv); - } - - mat_tri->v[j] = (intptr_t)val[0]; - mat_tri->l[j] = (intptr_t)l; - - j++; - - if (j >= 3) { - break; - } - - l = l->next; - } while (l != f->l_first); - - copy_v3_v3(tri->no, f->no); - tri->f.i = (intptr_t)f; -#endif } - TGSET_ITER_END - TGSET_ITER (f, node->bm_faces) { + for (BMFace *f : *node->bm_faces) { if (pbvh_poly_hidden(pbvh, f)) { continue; } @@ -3029,7 +2915,6 @@ bool BKE_pbvh_bmesh_check_tris(PBVH *pbvh, PBVHNode *node) pbvh_tribuf_add_edge(mat_tribuf, v1, v2); } while ((l = l->next) != f->l_first); } - TGSET_ITER_END bm->elem_index_dirty |= BM_VERT; @@ -3050,8 +2935,7 @@ bool BKE_pbvh_bmesh_check_tris(PBVH *pbvh, PBVHNode *node) static int pbvh_count_subtree_verts(PBVH *pbvh, PBVHNode *n) { if (n->flag & PBVH_Leaf) { - n->subtree_tottri = BLI_table_gset_len( - n->bm_faces); // n->tm_unique_verts->length + n->tm_other_verts->length; + n->subtree_tottri = n->bm_faces->size(); return n->subtree_tottri; } @@ -3178,26 +3062,19 @@ static void pbvh_bmesh_join_subnodes(PBVH *pbvh, PBVHNode *node, PBVHNode *paren node->flag |= PBVH_Delete; /* Mark for deletion. */ } - BMVert *v; - - TGSET_ITER (v, node->bm_unique_verts) { - BLI_table_gset_add(parent->bm_unique_verts, v); + for (BMVert *v : *node->bm_unique_verts) { + parent->bm_unique_verts->add(v); int *flags = BM_ELEM_CD_PTR(v, pbvh->cd_boundary_flag); *flags |= SCULPT_BOUNDARY_NEEDS_UPDATE; BM_ELEM_CD_SET_INT(v, pbvh->cd_vert_node_offset, DYNTOPO_NODE_NONE); } - TGSET_ITER_END - // printf(" subtotface: %d\n", BLI_table_gset_len(node->bm_faces)); - - BMFace *f; - TGSET_ITER (f, node->bm_faces) { - BLI_table_gset_add(parent->bm_faces, f); + for (BMFace *f : *node->bm_faces) { + parent->bm_faces->add(f); BM_ELEM_CD_SET_INT(f, pbvh->cd_face_node_offset, DYNTOPO_NODE_NONE); } - TGSET_ITER_END } static void BKE_pbvh_bmesh_correct_tree(PBVH *pbvh, PBVHNode *node, PBVHNode * /*parent*/) @@ -3209,9 +3086,9 @@ static void BKE_pbvh_bmesh_correct_tree(PBVH *pbvh, PBVHNode *node, PBVHNode * / } if (node->subtree_tottri < size_lower && node != pbvh->nodes) { - node->bm_unique_verts = BLI_table_gset_new("bm_unique_verts"); - node->bm_other_verts = BLI_table_gset_new("bm_other_verts"); - node->bm_faces = BLI_table_gset_new("bm_faces"); + node->bm_unique_verts = MEM_new>("bm_unique_verts"); + node->bm_other_verts = MEM_new>("bm_other_verts"); + node->bm_faces = MEM_new>("bm_faces"); pbvh_bmesh_join_subnodes(pbvh, pbvh->nodes + node->children_offset, node); pbvh_bmesh_join_subnodes(pbvh, pbvh->nodes + node->children_offset + 1, node); @@ -3221,52 +3098,44 @@ static void BKE_pbvh_bmesh_correct_tree(PBVH *pbvh, PBVHNode *node, PBVHNode * / PBVH_RebuildDrawBuffers | PBVH_UpdateOriginalBB | PBVH_UpdateMask | PBVH_UpdateVisibility | PBVH_UpdateColor | PBVH_UpdateNormals | PBVH_UpdateTris; - TableGSet *other = BLI_table_gset_new(__func__); - BMVert *v; + DyntopoSet *other = MEM_new>("bm_other_verts"); node->children_offset = 0; node->draw_batches = nullptr; - // rebuild bm_other_verts - BMFace *f; - TGSET_ITER (f, node->bm_faces) { + /* Rebuild bm_other_verts. */ + for (BMFace *f : *node->bm_faces) { BMLoop *l = f->l_first; if (BM_elem_is_free((BMElem *)f, BM_FACE)) { printf("%s: corrupted face %p.\n", __func__, f); - BLI_table_gset_remove(node->bm_faces, f, nullptr); + node->bm_faces->remove(f); continue; } BM_ELEM_CD_SET_INT(f, pbvh->cd_face_node_offset, DYNTOPO_NODE_NONE); do { - if (!BLI_table_gset_haskey(node->bm_unique_verts, l->v)) { - BLI_table_gset_add(other, l->v); + if (!node->bm_unique_verts->contains(l->v)) { + other->add(l->v); } l = l->next; } while (l != f->l_first); } - TGSET_ITER_END - BLI_table_gset_free(node->bm_other_verts, nullptr); + MEM_delete(node->bm_other_verts); node->bm_other_verts = other; BB_reset(&node->vb); -#if 1 - TGSET_ITER (v, node->bm_unique_verts) { + for (BMVert *v : *node->bm_unique_verts) { BB_expand(&node->vb, v->co); } - TGSET_ITER_END - TGSET_ITER (v, node->bm_other_verts) { + for (BMVert *v : *node->bm_other_verts) { BB_expand(&node->vb, v->co); } - TGSET_ITER_END -#endif - // printf("totface: %d\n", BLI_table_gset_len(node->bm_faces)); node->orig_vb = node->vb; return; @@ -3298,9 +3167,9 @@ static void pbvh_bmesh_compact_tree(PBVH *bvh) PBVHNode *n3 = n1->flag & PBVH_Delete ? n1 : n2; n3->flag = PBVH_Leaf | PBVH_UpdateTris; - n3->bm_unique_verts = BLI_table_gset_new("bm_unique_verts"); - n3->bm_other_verts = BLI_table_gset_new("bm_other_verts"); - n3->bm_faces = BLI_table_gset_new("bm_faces"); + n3->bm_unique_verts = MEM_new>("bm_unique_verts"); + n3->bm_other_verts = MEM_new>("bm_other_verts"); + n3->bm_faces = MEM_new>("bm_faces"); n3->tribuf = nullptr; n3->draw_batches = nullptr; } @@ -3310,9 +3179,9 @@ static void pbvh_bmesh_compact_tree(PBVH *bvh) if (!n->bm_unique_verts) { // should not happen - n->bm_unique_verts = BLI_table_gset_new("bm_unique_verts"); - n->bm_other_verts = BLI_table_gset_new("bm_other_verts"); - n->bm_faces = BLI_table_gset_new("bm_faces"); + n->bm_unique_verts = MEM_new>("bm_unique_verts"); + n->bm_other_verts = MEM_new>("bm_other_verts"); + n->bm_faces = MEM_new>("bm_faces"); n->tribuf = nullptr; n->draw_batches = nullptr; } @@ -3355,17 +3224,17 @@ static void pbvh_bmesh_compact_tree(PBVH *bvh) } if (n->bm_unique_verts) { - BLI_table_gset_free(n->bm_unique_verts, nullptr); + MEM_delete(n->bm_unique_verts); n->bm_unique_verts = nullptr; } if (n->bm_other_verts) { - BLI_table_gset_free(n->bm_other_verts, nullptr); + MEM_delete(n->bm_other_verts); n->bm_other_verts = nullptr; } if (n->bm_faces) { - BLI_table_gset_free(n->bm_faces, nullptr); + MEM_delete(n->bm_faces); n->bm_faces = nullptr; } @@ -3426,25 +3295,20 @@ static void pbvh_bmesh_compact_tree(PBVH *bvh) } if (!n->bm_unique_verts) { - printf("ERROR!\n"); - n->bm_unique_verts = BLI_table_gset_new("bleh"); - n->bm_other_verts = BLI_table_gset_new("bleh"); - n->bm_faces = BLI_table_gset_new("bleh"); + printf("%s: pbvh error\n", __func__); + + n->bm_unique_verts = MEM_new>("bm_unique_verts"); + n->bm_other_verts = MEM_new>("bm_other_verts"); + n->bm_faces = MEM_new>("bm_faces"); } - BMVert *v; - - TGSET_ITER (v, n->bm_unique_verts) { + for (BMVert *v : *n->bm_unique_verts) { BM_ELEM_CD_SET_INT(v, bvh->cd_vert_node_offset, i); } - TGSET_ITER_END - BMFace *f; - - TGSET_ITER (f, n->bm_faces) { + for (BMFace *f : *n->bm_faces) { BM_ELEM_CD_SET_INT(f, bvh->cd_face_node_offset, i); } - TGSET_ITER_END } Vector scratch; @@ -3457,23 +3321,20 @@ static void pbvh_bmesh_compact_tree(PBVH *bvh) } scratch.clear(); - BMVert *v; - TGSET_ITER (v, n->bm_other_verts) { + for (BMVert *v : *n->bm_other_verts) { int ni = BM_ELEM_CD_GET_INT(v, bvh->cd_vert_node_offset); if (ni == DYNTOPO_NODE_NONE) { scratch.append(v); } - // BM_ELEM_CD_SET_INT(v, bvh->cd_vert_node_offset, i); } - TGSET_ITER_END int slen = scratch.size(); for (int j = 0; j < slen; j++) { BMVert *v = scratch[j]; - BLI_table_gset_remove(n->bm_other_verts, v, nullptr); - BLI_table_gset_add(n->bm_unique_verts, v); + n->bm_other_verts->remove(v); + n->bm_unique_verts->add(v); BM_ELEM_CD_SET_INT(v, bvh->cd_vert_node_offset, i); } } @@ -3537,8 +3398,8 @@ ATTR_NO_OPT static void pbvh_bmesh_balance_tree(PBVH *pbvh) bool bad = overlap > volume * factor; - bad |= child1->bm_faces && !BLI_table_gset_len(child1->bm_faces); - bad |= child2->bm_faces && !BLI_table_gset_len(child2->bm_faces); + bad |= child1->bm_faces && !child1->bm_faces->size(); + bad |= child2->bm_faces && !child2->bm_faces->size(); if (bad) { modified = true; @@ -3553,10 +3414,7 @@ ATTR_NO_OPT static void pbvh_bmesh_balance_tree(PBVH *pbvh) node2->flag |= PBVH_Delete; if (node2->flag & PBVH_Leaf) { - BMFace *f; - BMVert *v; - - TGSET_ITER (f, node2->bm_faces) { + for (BMFace *f : *node2->bm_faces) { if (BM_ELEM_CD_GET_INT(f, cd_face_node) == -1) { // eek! continue; @@ -3565,15 +3423,13 @@ ATTR_NO_OPT static void pbvh_bmesh_balance_tree(PBVH *pbvh) BM_ELEM_CD_SET_INT(f, cd_face_node, DYNTOPO_NODE_NONE); faces.append(f); } - TGSET_ITER_END; - TGSET_ITER (v, node2->bm_unique_verts) { + for (BMVert *v : *node2->bm_unique_verts) { int *flags = BM_ELEM_CD_PTR(v, pbvh->cd_boundary_flag); *flags |= SCULPT_BOUNDARY_NEEDS_UPDATE; BM_ELEM_CD_SET_INT(v, cd_vert_node, DYNTOPO_NODE_NONE); } - TGSET_ITER_END; } else if (node2->children_offset > 0 && node2->children_offset < pbvh->totnode) { substack.append(pbvh->nodes + node2->children_offset); @@ -3626,9 +3482,7 @@ static void pbvh_fix_orphan_leaves(PBVH *pbvh) for (int i = 0; i < pbvh->totnode; i++) { PBVHNode *node = pbvh->nodes + i; - if (!(node->flag & PBVH_Leaf) || (node->flag & PBVH_Delete) || - BLI_table_gset_len(node->bm_faces) != 0) - { + if (!(node->flag & PBVH_Leaf) || (node->flag & PBVH_Delete) || node->bm_faces->size() != 0) { continue; } @@ -3658,12 +3512,10 @@ static void pbvh_fix_orphan_leaves(PBVH *pbvh) PBVHNode *a = pbvh->nodes + other->children_offset; PBVHNode *b = pbvh->nodes + other->children_offset + 1; - if (!(a->flag & PBVH_Delete) && (a->flag & PBVH_Leaf) && BLI_table_gset_len(a->bm_faces) > 1) - { + if (!(a->flag & PBVH_Delete) && (a->flag & PBVH_Leaf) && a->bm_faces->size() > 1) { other = a; } - else if (!(b->flag & PBVH_Delete) && (b->flag & PBVH_Leaf) && - BLI_table_gset_len(b->bm_faces) > 1) { + else if (!(b->flag & PBVH_Delete) && (b->flag & PBVH_Leaf) && b->bm_faces->size() > 1) { other = b; } else { @@ -3672,34 +3524,33 @@ static void pbvh_fix_orphan_leaves(PBVH *pbvh) } } - if (other == nullptr || BLI_table_gset_len(other->bm_faces) < 1) { + if (other == nullptr || other->bm_faces->size() < 1) { printf("%s: other was nullptr\n", __func__); continue; } /* Steal a single face from other */ - BMFace *f; PBVHNodeFlags updateflag = PBVH_UpdateOtherVerts | PBVH_UpdateBB | PBVH_UpdateOriginalBB | PBVH_UpdateTris | PBVH_UpdateTriAreas | PBVH_RebuildDrawBuffers | PBVH_RebuildNodeVerts | PBVH_RebuildPixels | PBVH_UpdateNormals | PBVH_UpdateCurvatureDir | PBVH_UpdateRedraw | PBVH_UpdateVisibility; - TGSET_ITER (f, other->bm_faces) { - BLI_table_gset_remove(other->bm_faces, static_cast(f), nullptr); - BLI_table_gset_add(node->bm_faces, static_cast(f)); + for (BMFace *f : *other->bm_faces) { + other->bm_faces->remove(f); + node->bm_faces->add(f); BM_ELEM_CD_SET_INT(f, pbvh->cd_face_node_offset, i); BMVert *v = f->l_first->v; int node_i = BM_ELEM_CD_GET_INT(v, pbvh->cd_vert_node_offset); if (node_i != DYNTOPO_NODE_NONE) { - PBVHNode *node = pbvh->nodes + node_i; - if (BLI_table_gset_haskey(node->bm_unique_verts, static_cast(v))) { - BLI_table_gset_remove(node->bm_unique_verts, static_cast(v), nullptr); + PBVHNode *node2 = pbvh->nodes + node_i; + if (node2->bm_unique_verts->contains(v)) { + node2->bm_unique_verts->remove(v); } } BM_ELEM_CD_SET_INT(v, pbvh->cd_vert_node_offset, i); - BLI_table_gset_add(node->bm_unique_verts, v); + node->bm_unique_verts->add(v); node->flag |= updateflag; other->flag |= updateflag; @@ -3707,7 +3558,6 @@ static void pbvh_fix_orphan_leaves(PBVH *pbvh) printf("%s: Patched empty leaf node.\n", __func__); break; } - TGSET_ITER_END; } } @@ -3770,17 +3620,17 @@ static void pbvh_bmesh_join_nodes(PBVH *pbvh) } if (n->bm_unique_verts) { - BLI_table_gset_free(n->bm_unique_verts, nullptr); + MEM_delete(n->bm_unique_verts); n->bm_unique_verts = nullptr; } if (n->bm_other_verts) { - BLI_table_gset_free(n->bm_other_verts, nullptr); + MEM_delete(n->bm_other_verts); n->bm_other_verts = nullptr; } if (n->bm_faces) { - BLI_table_gset_free(n->bm_faces, nullptr); + MEM_delete(n->bm_faces); n->bm_faces = nullptr; } @@ -3842,25 +3692,19 @@ static void pbvh_bmesh_join_nodes(PBVH *pbvh) } if (!n->bm_unique_verts) { - printf("%s: ERROR!\n", __func__); - n->bm_unique_verts = BLI_table_gset_new("bleh"); - n->bm_other_verts = BLI_table_gset_new("bleh"); - n->bm_faces = BLI_table_gset_new("bleh"); + printf("%s: pbvh error.\n", __func__); + n->bm_unique_verts = MEM_new>("bm_unique_verts"); + n->bm_other_verts = MEM_new>("bm_other_verts"); + n->bm_faces = MEM_new>("bm_faces"); } - BMVert *v; - - TGSET_ITER (v, n->bm_unique_verts) { + for (BMVert *v : *n->bm_unique_verts) { BM_ELEM_CD_SET_INT(v, pbvh->cd_vert_node_offset, i); } - TGSET_ITER_END - BMFace *f; - - TGSET_ITER (f, n->bm_faces) { + for (BMFace *f : *n->bm_faces) { BM_ELEM_CD_SET_INT(f, pbvh->cd_face_node_offset, i); } - TGSET_ITER_END } Vector scratch; @@ -3873,22 +3717,19 @@ static void pbvh_bmesh_join_nodes(PBVH *pbvh) } scratch.clear(); - BMVert *v; - TGSET_ITER (v, n->bm_other_verts) { + for (BMVert *v : *n->bm_other_verts) { int ni = BM_ELEM_CD_GET_INT(v, pbvh->cd_vert_node_offset); if (ni == DYNTOPO_NODE_NONE) { scratch.append(v); } - // BM_ELEM_CD_SET_INT(v, pbvh->cd_vert_node_offset, i); } - TGSET_ITER_END for (int j : IndexRange(scratch.size())) { BMVert *v = scratch[j]; - BLI_table_gset_remove(n->bm_other_verts, v, nullptr); - BLI_table_gset_add(n->bm_unique_verts, v); + n->bm_other_verts->remove(v); + n->bm_unique_verts->add(v); BM_ELEM_CD_SET_INT(v, pbvh->cd_vert_node_offset, i); } } @@ -3942,18 +3783,18 @@ void BKE_pbvh_node_mark_topology_update(PBVHNode *node) node->flag |= PBVH_UpdateTopology; } -TableGSet *BKE_pbvh_bmesh_node_unique_verts(PBVHNode *node) +DyntopoSet *BKE_pbvh_bmesh_node_unique_verts(PBVHNode *node) { return node->bm_unique_verts; } -TableGSet *BKE_pbvh_bmesh_node_other_verts(PBVHNode *node) +DyntopoSet *BKE_pbvh_bmesh_node_other_verts(PBVHNode *node) { pbvh_bmesh_check_other_verts(node); return node->bm_other_verts; } -struct TableGSet *BKE_pbvh_bmesh_node_faces(PBVHNode *node) +DyntopoSet *BKE_pbvh_bmesh_node_faces(PBVHNode *node) { return node->bm_faces; } @@ -4020,18 +3861,19 @@ void BKE_pbvh_bmesh_from_saved_indices(PBVH *pbvh) continue; } - BLI_table_gset_free(node->bm_unique_verts, nullptr); - BLI_table_gset_free(node->bm_faces, nullptr); + // MEM_delete< + MEM_delete(node->bm_unique_verts); + MEM_delete(node->bm_faces); if (node->bm_other_verts) { - BLI_table_gset_free(node->bm_other_verts, nullptr); + MEM_delete(node->bm_other_verts); } - node->bm_other_verts = BLI_table_gset_new("bm_other_verts"); + node->bm_other_verts = MEM_new>("bm_other_verts"); node->flag |= PBVH_UpdateOtherVerts; - node->bm_faces = BLI_table_gset_new("bm_faces"); - node->bm_unique_verts = BLI_table_gset_new("bm_verts"); + node->bm_faces = MEM_new>("bm_faces"); + node->bm_unique_verts = MEM_new>("bm_verts"); int j = 0; int *data = node->prim_indices; @@ -4040,7 +3882,7 @@ void BKE_pbvh_bmesh_from_saved_indices(PBVH *pbvh) BMFace *f = pbvh->header.bm->ftable[data[j]]; BM_ELEM_CD_SET_INT(f, pbvh->cd_face_node_offset, i); - BLI_table_gset_insert(node->bm_faces, f); + node->bm_faces->add(f); j++; } @@ -4054,7 +3896,7 @@ void BKE_pbvh_bmesh_from_saved_indices(PBVH *pbvh) BMVert *v = pbvh->header.bm->vtable[data[j]]; BM_ELEM_CD_SET_INT(v, pbvh->cd_vert_node_offset, i); - BLI_table_gset_insert(node->bm_unique_verts, v); + node->bm_unique_verts->add(v); j++; } diff --git a/source/blender/blenkernel/intern/pbvh_intern.hh b/source/blender/blenkernel/intern/pbvh_intern.hh index 1c4c2721688..245654b2416 100644 --- a/source/blender/blenkernel/intern/pbvh_intern.hh +++ b/source/blender/blenkernel/intern/pbvh_intern.hh @@ -128,9 +128,8 @@ struct PBVHNode { /* GSet of pointers to the BMFaces used by this node. * NOTE: PBVH_BMESH only. */ - TableGSet *bm_faces; - TableGSet *bm_unique_verts; - TableGSet *bm_other_verts; + blender::bke::dyntopo::DyntopoSet *bm_unique_verts, *bm_other_verts; + blender::bke::dyntopo::DyntopoSet *bm_faces; PBVHTriBuf *tribuf; // all triangles blender::Vector *tri_buffers; diff --git a/source/blender/blenlib/BLI_ghash.h b/source/blender/blenlib/BLI_ghash.h index 064b401f412..dee93e5c062 100644 --- a/source/blender/blenlib/BLI_ghash.h +++ b/source/blender/blenlib/BLI_ghash.h @@ -162,22 +162,7 @@ void *BLI_ghash_lookup_default(const GHash *gh, */ void **BLI_ghash_lookup_p(GHash *gh, const void *key) ATTR_WARN_UNUSED_RESULT; /** - * Lookup a pointer to the value of \a key in \a gh. - * - * \param key: The key to lookup. - * \param r_key: Pointer to variable to store the key as stored in the ghash - * \param r_val: Similar to BLI_ghash_ensure_p, pointer to the ghash pointer - * that stores the value - * - * \returns true if the value was found - * - * \note This has 2 main benefits over #BLI_ghash_lookup. - * - The value can be modified in-place without further function calls (faster). - * - The key as stored in the ghash is returned in r_key - */ -bool BLI_ghash_lookup_p_ex(GHash *gh, const void *key, void **r_key, void ***r_val); - -/** Ensure \a key is exists in \a gh. + * Ensure \a key is exists in \a gh. * * This handles the common situation where the caller needs ensure a key is added to \a gh, * constructing a new value in the case the key isn't found. @@ -354,73 +339,6 @@ BLI_INLINE bool BLI_ghashIterator_done(const GHashIterator *ghi) typedef struct GSet GSet; -typedef struct TableGSet { - void *ptr_to_idx; - void **elems; - int size, length; - int cur; -} TableGSet; - -TableGSet *BLI_table_gset_new(const char *info); -TableGSet *BLI_table_gset_new_ex(const char *info, int size); -void BLI_table_gset_free(TableGSet *ts, GHashKeyFreeFP freefp); -void BLI_table_gset_insert(TableGSet *ts, void *elem); -bool BLI_table_gset_add(TableGSet *ts, void *elem); -void BLI_table_gset_remove(TableGSet *ts, void *elem, GHashKeyFreeFP freefp); -bool BLI_table_gset_haskey(TableGSet *ts, void *elem); - -int BLI_table_gset_len(TableGSet *ts); - -#ifdef __cplusplus -# define TGSET_ITER(v, ts) \ - { \ - int _i1; \ - for (_i1 = 0; _i1 < (ts)->cur; _i1++) { \ - if (!(ts)->elems[_i1]) \ - continue; \ - v = static_cast((ts)->elems[_i1]); -#else -# define TGSET_ITER(v, ts) \ - { \ - int _i1; \ - for (_i1 = 0; _i1 < (ts)->cur; _i1++) { \ - if (!(ts)->elems[_i1]) \ - continue; \ - v = (ts)->elems[_i1]; -#endif - -#define TGSET_ITER_END \ - } \ - } - -#define TGSET_ITER_INDEX(v, ts, index) \ - { \ - int _i1; \ - index = -1; \ - for (_i1 = 0; _i1 < (ts)->cur; _i1++) { \ - if (!(ts)->elems[_i1]) \ - continue; \ - v = static_cast((ts)->elems[_i1]); \ - index++; - -#define TGSET_ITER_INDEX_END \ - } \ - } - -#define TGSET_FOREACH_BEGIN(type, v, ts) \ - { \ - type v; \ - int _i1; \ - for (_i1 = 0; _i1 < (ts)->cur; _i1++) { \ - if (!(ts)->elems[_i1]) \ - continue; \ - v = (type)(ts)->elems[_i1]; - -#define TGSET_FOREACH_END() \ - } \ - } \ - while (0) - typedef GHashHashFP GSetHashFP; typedef GHashCmpFP GSetCmpFP; typedef GHashKeyFreeFP GSetKeyFreeFP; diff --git a/source/blender/blenlib/CMakeLists.txt b/source/blender/blenlib/CMakeLists.txt index 1b780470858..4ee87202b78 100644 --- a/source/blender/blenlib/CMakeLists.txt +++ b/source/blender/blenlib/CMakeLists.txt @@ -161,7 +161,6 @@ set(SRC intern/voxel.c intern/winstuff.c intern/winstuff_dir.c - intern/BLI_table_gset.cc # Private headers. intern/BLI_mempool_private.h diff --git a/source/blender/blenlib/intern/BLI_ghash.c b/source/blender/blenlib/intern/BLI_ghash.c index d6f142a4495..e6ff5bab8a1 100644 --- a/source/blender/blenlib/intern/BLI_ghash.c +++ b/source/blender/blenlib/intern/BLI_ghash.c @@ -752,45 +752,6 @@ void **BLI_ghash_lookup_p(GHash *gh, const void *key) return e ? &e->val : NULL; } -/** - * Lookup a pointer to the value of \a key in \a gh. - * - * \param key: The key to lookup. - * \returns true if the value was found - * - * \note This has 2 main benefits over #BLI_ghash_lookup. - * - The value can be modified in-place without further function calls (faster). - * - The key as stored in the ghash in returned - */ -bool BLI_ghash_lookup_p_ex(GHash *gh, const void *key, void **r_key, void ***r_val) -{ - GHashEntry *e = (GHashEntry *)ghash_lookup_entry(gh, key); - BLI_assert(!(gh->flag & GHASH_FLAG_IS_GSET)); - - if (e) { - *r_key = e->e.key; - *r_val = &e->val; - return true; - } - else { - return false; - } -} - -/** - * Ensure \a key is exists in \a gh. - * - * This handles the common situation where the caller needs ensure a key is added to \a gh, - * constructing a new value in the case the key isn't found. - * Otherwise use the existing value. - * - * Such situations typically incur multiple lookups, however this function - * avoids them by ensuring the key is added, - * returning a pointer to the value so it can be used or initialized by the caller. - * - * \returns true when the value didn't need to be added. - * (when false, the caller _must_ initialize the value). - */ bool BLI_ghash_ensure_p(GHash *gh, void *key, void ***r_val) { const uint hash = ghash_keyhash(gh, key); diff --git a/source/blender/blenlib/intern/BLI_table_gset.cc b/source/blender/blenlib/intern/BLI_table_gset.cc deleted file mode 100644 index 88129e45589..00000000000 --- a/source/blender/blenlib/intern/BLI_table_gset.cc +++ /dev/null @@ -1,188 +0,0 @@ -#include "MEM_guardedalloc.h" - -#include "BLI_compiler_attrs.h" -#include "BLI_compiler_compat.h" -# -#include "BLI_smallhash.h" -#include "BLI_utildefines.h" - -#include "BLI_ghash.h" - -#ifdef USE_TGSET_SMALLHASH -# include "BLI_smallhash.h" -# define PTR_TO_IDX(ts) static_cast((ts)->ptr_to_idx) -#else -# include "BLI_map.hh" -# define PTR_TO_IDX(ts) static_cast *>((ts)->ptr_to_idx) -#endif - -TableGSet *BLI_table_gset_new_ex(const char *info, int size) -{ - TableGSet *ts = MEM_new(info); - -#ifdef USE_TGSET_SMALLHASH - ts->ptr_to_idx = static_cast(MEM_cnew("table gset smallhash")); - BLI_smallhash_init_ex(PTR_TO_IDX(->ptr_to_idx), size); -#else - ts->ptr_to_idx = static_cast(MEM_new>("ts->ptr_to_idx")); -#endif - - if (size) { - ts->elems = static_cast(MEM_callocN(sizeof(void *) * (uint)size, info)); - ts->size = size; - ts->length = 0; - ts->cur = 0; - } - - return ts; -} - -TableGSet *BLI_table_gset_new(const char *info) -{ - return BLI_table_gset_new_ex(info, 0); -} - -void BLI_table_gset_free(TableGSet *ts, GHashKeyFreeFP freefp) -{ - MEM_SAFE_FREE(ts->elems); - -#ifdef USE_TGSET_SMALLHASH - BLI_smallhash_release(PTR_TO_IDX(ts->ptr_to_idx)); - MEM_freeN(ts->ptr_to_idx); -#else - MEM_delete>(PTR_TO_IDX(ts)); -#endif - - MEM_delete(ts); -} - -static void table_gset_resize(TableGSet *ts) -{ - if (ts->cur >= ts->size) { - uint newsize = (uint)(ts->cur + 1); - newsize = (newsize << 1U) - (newsize >> 1U); - newsize = MAX2(newsize, 8U); - - if (!ts->elems) { - ts->elems = static_cast(MEM_mallocN(sizeof(void *) * newsize, "ts->elems")); - } - else { - ts->elems = static_cast(MEM_reallocN(ts->elems, newsize * sizeof(void *))); - } - -#ifdef USE_TGSET_SMALLHASH - BLI_smallhash_clear(PTR_TO_IDX(ts)); -#else - PTR_TO_IDX(ts)->clear(); -#endif - - /* Compact. */ - int compact_i = 0; - for (int i = 0; i < ts->cur; i++) { - void *elem2 = ts->elems[i]; - - if (elem2) { -#ifdef USE_TGSET_SMALLHASH - void **val; - BLI_smallhash_ensure_p(PTR_TO_IDX(ts), (uintptr_t)elem2, &val); - *val = POINTER_FROM_INT(compact_i); -#else - PTR_TO_IDX(ts)->add_overwrite(elem2, compact_i); -#endif - - ts->elems[compact_i++] = elem2; - } - } - - ts->cur = compact_i; - ts->size = (int)newsize; - } -} - -bool BLI_table_gset_add(TableGSet *ts, void *elem) -{ - table_gset_resize(ts); - -#ifdef USE_TGSET_SMALLHASH - void **val; - - bool ret = BLI_smallhash_ensure_p(PTR_TO_IDX(ts), (uintptr_t)elem, &val); - - if (!ret) { - *val = POINTER_FROM_INT(ts->cur); - - ts->elems[ts->cur++] = elem; - ts->length++; - } - - return ret; -#else - auto createfn = [&](int *value) { - *value = ts->cur; - ts->elems[ts->cur++] = elem; - ts->length++; - return true; - }; - auto modifyfn = [&](int *value) { return false; }; - - return PTR_TO_IDX(ts)->add_or_modify(elem, createfn, modifyfn); -#endif -} - -void BLI_table_gset_insert(TableGSet *ts, void *elem) -{ - table_gset_resize(ts); - -#ifdef USE_TGSET_SMALLHASH - BLI_smallhash_insert(PTR_TO_IDX(ts), (uintptr_t)elem, (void *)ts->cur); -#else - PTR_TO_IDX(ts)->add(elem, ts->cur); -#endif - - ts->elems[ts->cur++] = elem; - ts->length++; -} - -void BLI_table_gset_remove(TableGSet *ts, void *elem, GHashKeyFreeFP freefp) -{ - BLI_assert(ts && elem); - -#ifdef USE_TGSET_SMALLHASH - int *idx = (int *)BLI_smallhash_lookup_p(PTR_TO_IDX(ts), (uintptr_t)elem); - if (!idx) { - return; - } - - BLI_smallhash_remove(PTR_TO_IDX(ts), (uintptr_t)elem); -#else - int *idx = PTR_TO_IDX(ts)->lookup_ptr(elem); - if (!idx) { - return; - } - - PTR_TO_IDX(ts)->remove(elem); -#endif - - int idx2 = *idx; - - if (!ts->elems || ts->elems[idx2] != elem) { - return; - } - - ts->length--; - ts->elems[idx2] = nullptr; -} - -bool BLI_table_gset_haskey(TableGSet *ts, void *elem) -{ -#ifdef USE_TGSET_SMALLHASH - return BLI_smallhash_haskey(PTR_TO_IDX(ts), (uintptr_t)elem); -#else - return PTR_TO_IDX(ts)->contains(elem); -#endif -} - -int BLI_table_gset_len(TableGSet *ts) -{ - return ts->length; -} diff --git a/source/blender/draw/DRW_pbvh.hh b/source/blender/draw/DRW_pbvh.hh index 51da2d960fa..c9b7b82c290 100644 --- a/source/blender/draw/DRW_pbvh.hh +++ b/source/blender/draw/DRW_pbvh.hh @@ -70,8 +70,6 @@ struct PBVH_GPU_Args { /* Debug mode to show original coordinates instead of vertex positions. */ bool show_orig; - /* BMesh. */ - struct TableGSet *bm_unique_vert, *bm_other_verts, *bm_faces; int cd_mask_layer; struct PBVHTriBuf *tribuf, *tri_buffers; int tot_tri_buffers, updategen; diff --git a/source/blender/editors/sculpt_paint/paint_hide.cc b/source/blender/editors/sculpt_paint/paint_hide.cc index 889db1ac70c..dbdac06faf0 100644 --- a/source/blender/editors/sculpt_paint/paint_hide.cc +++ b/source/blender/editors/sculpt_paint/paint_hide.cc @@ -45,6 +45,7 @@ #include "sculpt_intern.hh" using blender::Vector; +using blender::bke::dyntopo::DyntopoSet; /* Return true if the element should be hidden/shown. */ static bool is_effected(PartialVisArea area, @@ -196,15 +197,14 @@ static void partialvis_update_grids(Depsgraph *depsgraph, } static void partialvis_update_bmesh_verts(BMesh *bm, - TableGSet *verts, + DyntopoSet *verts, PartialVisAction action, PartialVisArea area, float planes[4][4], bool *any_changed, bool *any_visible) { - BMVert *v; - TGSET_ITER (v, verts) { + for (BMVert *v : *verts) { float *vmask = static_cast( CustomData_bmesh_get(&bm->vdata, v->head.data, CD_PAINT_MASK)); @@ -223,14 +223,11 @@ static void partialvis_update_bmesh_verts(BMesh *bm, (*any_visible) = true; } } - TGSET_ITER_END } -static void partialvis_update_bmesh_faces(TableGSet *faces) +static void partialvis_update_bmesh_faces(DyntopoSet *faces) { - BMFace *f; - - TGSET_ITER (f, faces) { + for (BMFace *f : *faces) { bool hidden = false; BMLoop *l = f->l_first; @@ -245,7 +242,6 @@ static void partialvis_update_bmesh_faces(TableGSet *faces) BM_elem_flag_disable(f, BM_ELEM_HIDDEN); } } - TGSET_ITER_END } static void partialvis_update_bmesh(Object *ob, @@ -256,7 +252,9 @@ static void partialvis_update_bmesh(Object *ob, float planes[4][4]) { BMesh *bm; - TableGSet *unique, *other, *faces; + DyntopoSet *unique, *other; + DyntopoSet *faces; + bool any_changed = false, any_visible = false; bm = BKE_pbvh_get_bmesh(pbvh); diff --git a/source/blender/editors/sculpt_paint/sculpt_undo.cc b/source/blender/editors/sculpt_paint/sculpt_undo.cc index 40bd776a194..b702d136680 100644 --- a/source/blender/editors/sculpt_paint/sculpt_undo.cc +++ b/source/blender/editors/sculpt_paint/sculpt_undo.cc @@ -85,6 +85,8 @@ #include "bmesh_log.h" #include "sculpt_intern.hh" +using blender::bke::dyntopo::DyntopoSet; + /* Uncomment to print the undo stack in the console on push/undo/redo. */ //#define SCULPT_UNDO_DEBUG @@ -784,9 +786,9 @@ static void bmesh_undo_on_face_add(BMFace *f, void *userdata) if (ni_l < 0 && ni >= 0) { BM_ELEM_CD_SET_INT(l->v, ni_l, ni); - TableGSet *bm_unique_verts = BKE_pbvh_bmesh_node_unique_verts(node); + DyntopoSet *bm_unique_verts = BKE_pbvh_bmesh_node_unique_verts(node); - BLI_table_gset_add(bm_unique_verts, l->v); + bm_unique_verts->add(l->v); } } while ((l = l->next) != f->l_first); @@ -2279,8 +2281,7 @@ static SculptUndoNode *sculpt_undo_bmesh_push(Object *ob, PBVHNode *node, Sculpt break; case SCULPT_UNDO_HIDDEN: { - TableGSet *faces = BKE_pbvh_bmesh_node_faces(node); - BMFace *f; + DyntopoSet *faces = BKE_pbvh_bmesh_node_faces(node); BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) { bm_logstack_push(); @@ -2289,10 +2290,9 @@ static SculptUndoNode *sculpt_undo_bmesh_push(Object *ob, PBVHNode *node, Sculpt } BKE_pbvh_vertex_iter_end; - TGSET_ITER (f, faces) { + for (BMFace *f : *faces) { BM_log_face_modified(ss->bm, ss->bm_log, f); } - TGSET_ITER_END break; } @@ -2319,25 +2319,21 @@ static SculptUndoNode *sculpt_undo_bmesh_push(Object *ob, PBVHNode *node, Sculpt BKE_pbvh_vertex_iter_end; } else if (domain == ATTR_DOMAIN_CORNER) { - TableGSet *faces = BKE_pbvh_bmesh_node_faces(node); - BMFace *f; + DyntopoSet *faces = BKE_pbvh_bmesh_node_faces(node); - TGSET_ITER (f, faces) { + for (BMFace *f : *faces) { BM_log_face_modified(ss->bm, ss->bm_log, f); } - TGSET_ITER_END } break; } case SCULPT_UNDO_FACE_SETS: { - TableGSet *faces = BKE_pbvh_bmesh_node_faces(node); - BMFace *f; + DyntopoSet *faces = BKE_pbvh_bmesh_node_faces(node); - TGSET_ITER (f, faces) { + for (BMFace *f : *faces) { BM_log_face_modified(ss->bm, ss->bm_log, f); } - TGSET_ITER_END break; } -- 2.30.2 From 057f6b48ede9aa1be5aa2825b942aa9f9940995f Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Wed, 31 May 2023 20:32:52 -0700 Subject: [PATCH 142/279] temp-sculpt-dyntopo: Remove old TableGSet iter macro from clang-format --- .clang-format | 1 - 1 file changed, 1 deletion(-) diff --git a/.clang-format b/.clang-format index f08332ce18f..17196f7ddab 100644 --- a/.clang-format +++ b/.clang-format @@ -268,7 +268,6 @@ ForEachMacros: - SET_SLOT_PROBING_BEGIN - MAP_SLOT_PROBING_BEGIN - VECTOR_SET_SLOT_PROBING_BEGIN - - TGSET_ITER - WL_ARRAY_FOR_EACH - FOREACH_SPECTRUM_CHANNEL -- 2.30.2 From 329770b622a724f82d55e5b0dbc136f43f4b9a2f Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Wed, 31 May 2023 20:33:42 -0700 Subject: [PATCH 143/279] temp-sculpt-dyntopo: Forgot to add this file --- source/blender/blenkernel/BKE_dyntopo_set.hh | 133 +++++++++++++++++++ 1 file changed, 133 insertions(+) create mode 100644 source/blender/blenkernel/BKE_dyntopo_set.hh diff --git a/source/blender/blenkernel/BKE_dyntopo_set.hh b/source/blender/blenkernel/BKE_dyntopo_set.hh new file mode 100644 index 00000000000..b46c26337be --- /dev/null +++ b/source/blender/blenkernel/BKE_dyntopo_set.hh @@ -0,0 +1,133 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +/** \file + * \ingroup bli + * + * A simple set class that's optimized for iteration. + * Elements are stored in both a blender::Map and a flat array. + */ + +#include "BLI_compiler_attrs.h" +#include "BLI_map.hh" +#include "BLI_set.hh" +#include "BLI_vector.hh" + +#include + +namespace blender::bke::dyntopo { +template class DyntopoSet { + public: + DyntopoSet(int64_t reserve) + { + elem_to_index_.reserve(reserve); + index_to_elem_.reserve(reserve); + } + DyntopoSet() {} + DyntopoSet(const DyntopoSet &) = delete; + + struct iterator { + iterator() : set_(nullptr), i_(-1) {} + iterator(DyntopoSet *set, int i) : set_(set), i_(i) {} + iterator(const iterator &b) : set_(b.set_), i_(b.i_) {} + + inline T *operator*() + { + return set_->index_to_elem_[i_]; + } + + inline iterator &operator++() + { + i_++; + + while (i_ < set_->index_to_elem_.size() && set_->index_to_elem_[i_] == nullptr) { + i_++; + } + + return *this; + } + + inline bool operator==(const iterator &b) + { + return b.i_ == i_; + } + + inline bool operator!=(const iterator &b) + { + return b.i_ != i_; + } + + private: + DyntopoSet *set_; + int i_; + }; + + bool contains(T *key) + { + return elem_to_index_.contains(key); + } + + void remove(T *key) + { + if (!elem_to_index_.contains(key)) { + printf("DyntopoSet::remove: key not in set\n"); + return; + } + + int i = elem_to_index_.pop(key); + index_to_elem_[i] = nullptr; + freelist_.append(i); + } + + /* Add key, returns true if key was already in set. */ + bool add(T *key) + { + int i; + if (freelist_.size() > 0) { + i = freelist_.last(); + } + else { + i = index_to_elem_.size(); + } + + bool was_added = elem_to_index_.add(key, i); + if (was_added) { + if (i == index_to_elem_.size()) { + index_to_elem_.append(key); + } + else { + freelist_.pop_last(); + index_to_elem_[i] = key; + } + } + + return was_added; + } + + int size() + { + return elem_to_index_.size(); + } + + iterator begin() + { + int i = 0; + while (i < index_to_elem_.size() && index_to_elem_[i] == nullptr) { + i++; + } + + return iterator(this, i); + } + + iterator end() + { + return iterator(this, index_to_elem_.size()); + } + + private: + blender::Map elem_to_index_; + blender::Vector index_to_elem_; + blender::Vector freelist_; +}; +} // namespace blender::bke::dyntopo -- 2.30.2 From 71a0ca63b2b79edc8b95f6215ee82fd88bf18afd Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Wed, 31 May 2023 20:39:09 -0700 Subject: [PATCH 144/279] temp-sculpt-dyntopo: Revert more unrelated stuff --- source/blender/draw/DRW_engine.h | 6 --- source/blender/draw/intern/draw_cache.c | 40 ------------------- .../draw/intern/draw_cache_impl_mesh.cc | 2 +- source/blender/draw/intern/draw_debug.cc | 2 - source/blender/draw/intern/draw_manager.c | 1 - 5 files changed, 1 insertion(+), 50 deletions(-) diff --git a/source/blender/draw/DRW_engine.h b/source/blender/draw/DRW_engine.h index 586a72a850c..6423bb8dd4f 100644 --- a/source/blender/draw/DRW_engine.h +++ b/source/blender/draw/DRW_engine.h @@ -77,12 +77,6 @@ void DRW_draw_view(const struct bContext *C); * Draw render engine info. */ void DRW_draw_region_engine_info(int xoffset, int *yoffset, int line_height); -void DRW_make_cdlayer_attr_aliases(struct GPUVertFormat *format, - char *base_name, - const struct CustomData *data, - const struct CustomDataLayer *cl, - bool is_active_render, - bool is_active_layer); /** * Used for both regular and off-screen drawing. diff --git a/source/blender/draw/intern/draw_cache.c b/source/blender/draw/intern/draw_cache.c index d0a129e859f..504172cb2f0 100644 --- a/source/blender/draw/intern/draw_cache.c +++ b/source/blender/draw/intern/draw_cache.c @@ -500,46 +500,6 @@ static void sphere_lat_lon_vert(GPUVertBuf *vbo, int *v_ofs, float lat, float lo (*v_ofs)++; } -void DRW_make_cdlayer_attr_aliases(GPUVertFormat *format, - char *base_name, - const CustomData *data, - const CustomDataLayer *cl, - bool is_active_render, - bool is_active_layer) -{ - char attr_name[32], attr_safe_name[GPU_MAX_SAFE_ATTR_NAME]; - const char *layer_name = cl->name; - - int i = (int)(cl - data->typemap[cl->type]); - - GPU_vertformat_safe_attr_name(layer_name, attr_safe_name, GPU_MAX_SAFE_ATTR_NAME); - - /* Attribute layer name. */ - BLI_snprintf(attr_name, sizeof(attr_name), "%s%s", base_name, attr_safe_name); - GPU_vertformat_alias_add(format, attr_name); - - /* Auto layer name. */ - BLI_snprintf(attr_name, sizeof(attr_name), "a%s", attr_safe_name); - GPU_vertformat_alias_add(format, attr_name); - - /* Active render layer name. */ - if (is_active_render) { - GPU_vertformat_alias_add(format, base_name); - } - - /* Active display layer name. */ - if (is_active_layer) { - BLI_snprintf(attr_name, sizeof(attr_name), "a%s", base_name); - GPU_vertformat_alias_add(format, attr_name); - } - - /* Stencil mask layer name. */ - if (i == CustomData_get_stencil_layer(data, cl->type)) { - BLI_snprintf(attr_name, sizeof(attr_name), "m%s", base_name); - GPU_vertformat_alias_add(format, attr_name); - } -} - GPUBatch *DRW_cache_sphere_get(const eDRWLevelOfDetail level_of_detail) { BLI_assert(level_of_detail >= DRW_LOD_LOW && level_of_detail < DRW_LOD_MAX); diff --git a/source/blender/draw/intern/draw_cache_impl_mesh.cc b/source/blender/draw/intern/draw_cache_impl_mesh.cc index 593b20fed3e..10ab77e2dab 100644 --- a/source/blender/draw/intern/draw_cache_impl_mesh.cc +++ b/source/blender/draw/intern/draw_cache_impl_mesh.cc @@ -1351,7 +1351,7 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, bool cd_uv_update = false; /* Early out */ - if (!cache || cache->batch_requested == 0) { + if (cache->batch_requested == 0) { #ifdef DEBUG drw_mesh_batch_cache_check_available(task_graph, me); #endif diff --git a/source/blender/draw/intern/draw_debug.cc b/source/blender/draw/intern/draw_debug.cc index ccbea73847a..face8ccd2f7 100644 --- a/source/blender/draw/intern/draw_debug.cc +++ b/source/blender/draw/intern/draw_debug.cc @@ -7,8 +7,6 @@ * \brief Simple API to draw debug shapes in the viewport. */ -#define DRAW_DEBUG - #include "BKE_object.h" #include "BLI_link_utils.h" #include "BLI_math_matrix.hh" diff --git a/source/blender/draw/intern/draw_manager.c b/source/blender/draw/intern/draw_manager.c index 7f24ec83f42..8851e166661 100644 --- a/source/blender/draw/intern/draw_manager.c +++ b/source/blender/draw/intern/draw_manager.c @@ -14,7 +14,6 @@ #include "BLI_string.h" #include "BLI_task.h" #include "BLI_threads.h" -#include "BLI_utildefines.h" #include "BLF_api.h" -- 2.30.2 From a3650b90080ed19b2dcb6d1ab313058bfd84fcf2 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Wed, 31 May 2023 20:41:26 -0700 Subject: [PATCH 145/279] temp-sculpt-dyntopo: Revert extraneous change --- .../draw/intern/mesh_extractors/extract_mesh_vbo_uv.cc | 4 ---- 1 file changed, 4 deletions(-) diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_uv.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_uv.cc index 34effedf19b..9358c0d1e25 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_uv.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_uv.cc @@ -43,10 +43,6 @@ static bool mesh_extract_uv_format_init(GPUVertFormat *format, char attr_name[32], attr_safe_name[GPU_MAX_SAFE_ATTR_NAME]; const char *layer_name = CustomData_get_layer_name(cd_ldata, CD_PROP_FLOAT2, i); - if (!layer_name) { - continue; - } - GPU_vertformat_safe_attr_name(layer_name, attr_safe_name, GPU_MAX_SAFE_ATTR_NAME); /* UV layer name. */ SNPRINTF(attr_name, "a%s", attr_safe_name); -- 2.30.2 From 1944b90d02abaa7b3707adc38c7afd7327843aab Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Fri, 2 Jun 2023 04:22:41 -0700 Subject: [PATCH 146/279] temp-sculpt-dyntopo: Fix misc. issues related to UVs * BKE_sculpt_reproject_cdata is in somewhat of a mess, lots of debug code. It is starting to become more reliable though. * Rewrote the API by which changes in the attribute layout of the base mesh is propagated to the sculpt bmesh and vice versa. * The add/remove operators for UV maps, color attributes, and normal attributes now properly invoke sculpt undo and calls the above API to push the new layer into the sculpt mesh. * Fixed UV island boundary detection. --- scripts/startup/bl_ui/space_view3d_toolbar.py | 2 + source/blender/blenkernel/BKE_customdata.h | 7 + source/blender/blenkernel/BKE_dyntopo.hh | 4 +- source/blender/blenkernel/BKE_paint.h | 10 +- source/blender/blenkernel/BKE_pbvh.h | 2 +- .../blenkernel/intern/attribute_access.cc | 5 + .../blender/blenkernel/intern/customdata.cc | 28 +- source/blender/blenkernel/intern/dyntopo.cc | 1159 +++++++++++++++-- .../blenkernel/intern/dyntopo_collapse.cc | 30 +- .../blenkernel/intern/dyntopo_intern.hh | 5 +- source/blender/blenkernel/intern/paint.cc | 381 ++++-- source/blender/blenkernel/intern/pbvh.cc | 6 +- .../blender/blenkernel/intern/pbvh_bmesh.cc | 144 +- .../blender/blenkernel/intern/pbvh_intern.hh | 2 +- source/blender/blenlib/BLI_heap_minmax.hh | 6 +- source/blender/bmesh/intern/bmesh_core.c | 2 +- source/blender/bmesh/intern/bmesh_interp.c | 23 - source/blender/bmesh/intern/bmesh_log.c | 1033 --------------- source/blender/bmesh/intern/bmesh_log.cc | 5 +- .../bmesh/intern/bmesh_mesh_convert.cc | 89 +- .../blender/draw/intern/draw_manager_data.cc | 18 +- .../editors/geometry/geometry_attributes.cc | 32 + source/blender/editors/mesh/mesh_data.cc | 23 + .../editors/sculpt_paint/paint_vertex.cc | 29 +- source/blender/editors/sculpt_paint/sculpt.cc | 4 +- .../editors/sculpt_paint/sculpt_api.cc | 48 +- .../editors/sculpt_paint/sculpt_detail.cc | 2 +- .../editors/sculpt_paint/sculpt_dyntopo.cc | 14 +- .../editors/sculpt_paint/sculpt_intern.hh | 3 +- .../sculpt_paint/sculpt_paint_color.cc | 2 +- .../editors/sculpt_paint/sculpt_smooth.cc | 14 +- .../editors/sculpt_paint/sculpt_undo.cc | 89 +- source/blender/makesrna/intern/rna_mesh.c | 4 + 33 files changed, 1657 insertions(+), 1568 deletions(-) delete mode 100644 source/blender/bmesh/intern/bmesh_log.c diff --git a/scripts/startup/bl_ui/space_view3d_toolbar.py b/scripts/startup/bl_ui/space_view3d_toolbar.py index 91fa9094489..91feb003b17 100644 --- a/scripts/startup/bl_ui/space_view3d_toolbar.py +++ b/scripts/startup/bl_ui/space_view3d_toolbar.py @@ -949,6 +949,8 @@ class VIEW3D_PT_sculpt_dyntopo(Panel, View3DPaintPanel): "detail_range" ) + sub.prop(context.object.data, "sculpt_dyntopo_relax") + if 0: scene = context.scene def do_prop(name, text=None): diff --git a/source/blender/blenkernel/BKE_customdata.h b/source/blender/blenkernel/BKE_customdata.h index fdd66a11476..e9df4676b23 100644 --- a/source/blender/blenkernel/BKE_customdata.h +++ b/source/blender/blenkernel/BKE_customdata.h @@ -444,6 +444,13 @@ void CustomData_bmesh_interp(struct CustomData *data, const float *sub_weights, int count, void *dst_block); +void CustomData_bmesh_interp_ex(struct CustomData *data, + const void **src_blocks, + const float *weights, + const float *sub_weights, + int count, + void *dst_block, + eCustomDataMask typemask); /** * Swap data inside each item, for all layers. diff --git a/source/blender/blenkernel/BKE_dyntopo.hh b/source/blender/blenkernel/BKE_dyntopo.hh index ac7cca012dd..249ae9ab6fc 100644 --- a/source/blender/blenkernel/BKE_dyntopo.hh +++ b/source/blender/blenkernel/BKE_dyntopo.hh @@ -134,7 +134,7 @@ ENUM_OPERATORS(PBVHTopologyUpdateMode, PBVH_LocalCollapse); void detail_size_set(PBVH *pbvh, float detail_size, float detail_range); bool remesh_topology(blender::bke::dyntopo::BrushTester *brush_tester, - struct SculptSession *ss, + struct Object *ob, PBVH *pbvh, PBVHTopologyUpdateMode mode, bool use_frontface, @@ -145,7 +145,7 @@ bool remesh_topology(blender::bke::dyntopo::BrushTester *brush_tester, int edge_limit_multiply); bool remesh_topology_nodes(blender::bke::dyntopo::BrushTester *tester, - struct SculptSession *ss, + struct Object *ob, PBVH *pbvh, bool (*searchcb)(PBVHNode *node, void *data), void (*undopush)(PBVHNode *node, void *data), diff --git a/source/blender/blenkernel/BKE_paint.h b/source/blender/blenkernel/BKE_paint.h index 7186a38e732..631112f6dab 100644 --- a/source/blender/blenkernel/BKE_paint.h +++ b/source/blender/blenkernel/BKE_paint.h @@ -728,7 +728,8 @@ struct SculptSession { int totuv; - bool ignore_uvs; + /* Reproject customdata during smooth. */ + bool reproject_smooth; /* Undo/redo log for dynamic topology sculpting */ BMLog *bm_log; @@ -949,7 +950,10 @@ bool BKE_sculpt_init_flags_valence(struct Object *ob, struct BMesh *BKE_sculptsession_empty_bmesh_create(void); void BKE_sculptsession_bmesh_attr_update_internal(struct Object *ob); -void BKE_sculptsession_sync_attributes(struct Object *ob, struct Mesh *me); +/* Ensures non-temporary attributes in me exist in the sculpt mesh, or vice + * versa if load_to_mesh is true. + */ +void BKE_sculptsession_sync_attributes(struct Object *ob, struct Mesh *me, bool load_to_mesh); void BKE_sculptsession_bmesh_add_layers(struct Object *ob); SculptAttribute *BKE_sculptsession_attr_layer_get(struct Object *ob, @@ -963,7 +967,7 @@ void BKE_sculptsession_update_attr_refs(struct Object *ob); int BKE_sculptsession_get_totvert(const struct SculptSession *ss); -void BKE_sculptsession_ignore_uvs_set(struct Object *ob, bool value); +void BKE_sculptsession_reproject_smooth_set(struct Object *ob, bool value); void BKE_sculptsession_free_attribute_refs(struct Object *ob); /** diff --git a/source/blender/blenkernel/BKE_pbvh.h b/source/blender/blenkernel/BKE_pbvh.h index 320d8f9703e..9332c25d5c9 100644 --- a/source/blender/blenkernel/BKE_pbvh.h +++ b/source/blender/blenkernel/BKE_pbvh.h @@ -1082,7 +1082,7 @@ void BKE_pbvh_pmap_to_edges(PBVH *pbvh, bool *heap_alloc, int **r_polys); -void BKE_pbvh_ignore_uvs_set(PBVH *pbvh, bool value); +void BKE_pbvh_reproject_smooth_set(PBVH *pbvh, bool value); void BKE_pbvh_set_face_areas(PBVH *pbvh, float *face_areas); void BKE_pbvh_set_bmesh(PBVH *pbvh, struct BMesh *bm); diff --git a/source/blender/blenkernel/intern/attribute_access.cc b/source/blender/blenkernel/intern/attribute_access.cc index 3d52b6421b4..94d85f23d71 100644 --- a/source/blender/blenkernel/intern/attribute_access.cc +++ b/source/blender/blenkernel/intern/attribute_access.cc @@ -9,6 +9,7 @@ #include "BKE_mesh.hh" #include "BKE_pointcloud.h" #include "BKE_type_conversions.hh" +#include "BKE_global.h" #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" @@ -56,6 +57,10 @@ const char *no_procedural_access_message = N_( bool allow_procedural_attribute_access(StringRef attribute_name) { + if (G.debug_value == 892) { + return true; + } + if (attribute_name.startswith(".corner")) { return false; } diff --git a/source/blender/blenkernel/intern/customdata.cc b/source/blender/blenkernel/intern/customdata.cc index 0261d5bb3c7..aade96b82ea 100644 --- a/source/blender/blenkernel/intern/customdata.cc +++ b/source/blender/blenkernel/intern/customdata.cc @@ -4638,12 +4638,13 @@ void CustomData_bmesh_interp_n(CustomData *data, typeInfo->interp(src_blocks_ofs, weights, sub_weights, count, dst_block_ofs); } -void CustomData_bmesh_interp(CustomData *data, - const void **src_blocks, - const float *weights, - const float *sub_weights, - int count, - void *dst_block) +void CustomData_bmesh_interp_ex(CustomData *data, + const void **src_blocks, + const float *weights, + const float *sub_weights, + int count, + void *dst_block, + eCustomDataMask typemask) { if (count <= 0) { return; @@ -4679,6 +4680,10 @@ void CustomData_bmesh_interp(CustomData *data, const LayerTypeInfo *typeInfo = layerType_getInfo(eCustomDataType(layer->type)); + if (!(CD_TYPE_AS_MASK(layer->type) & typemask)) { + continue; + } + if (layer->flag & CD_FLAG_ELEM_NOINTERP) { if (layer->flag & CD_FLAG_ELEM_NOCOPY) { continue; @@ -4714,6 +4719,17 @@ void CustomData_bmesh_interp(CustomData *data, MEM_freeN(default_weights); } } +void CustomData_bmesh_interp(CustomData *data, + const void **src_blocks, + const float *weights, + const float *sub_weights, + int count, + void *dst_block) + +{ + eCustomDataMask typemask = eCustomDataMask((1ULL << CD_NUMTYPES) - 1ULL); + CustomData_bmesh_interp_ex(data, src_blocks, weights, sub_weights, count, dst_block, typemask); +} void CustomData_file_write_info(const eCustomDataType type, const char **r_struct_name, diff --git a/source/blender/blenkernel/intern/dyntopo.cc b/source/blender/blenkernel/intern/dyntopo.cc index 23b6e3bf60d..db532ebffd9 100644 --- a/source/blender/blenkernel/intern/dyntopo.cc +++ b/source/blender/blenkernel/intern/dyntopo.cc @@ -4,6 +4,7 @@ #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" #include "DNA_modifier_types.h" +#include "DNA_object_types.h" #include "BLI_alloca.h" #include "BLI_array.hh" @@ -49,8 +50,6 @@ //#define CLEAR_TAGS_IN_THREAD -namespace blender::bke::dyntopo { - using blender::float2; using blender::float3; using blender::float4; @@ -58,10 +57,15 @@ using blender::IndexRange; using blender::Map; using blender::Set; using blender::Vector; + +namespace blender::bke::dyntopo { + using namespace blender::bke::sculpt; -static void pbvh_split_edges( - struct EdgeQueueContext *eq_ctx, PBVH *pbvh, BMesh *bm, BMEdge **edges, int totedge); +static void pbvh_split_edges(struct EdgeQueueContext *eq_ctx, + PBVH *pbvh, + BMesh *bm, + Span edges); static void edge_queue_create_local(EdgeQueueContext *eq_ctx, PBVH *pbvh, @@ -665,7 +669,7 @@ static void unified_edge_queue_task_cb(void *__restrict userdata, PBVHNode *node = tdata->node; EdgeQueueContext *eq_ctx = tdata->eq_ctx; - bool do_smooth = eq_ctx->surface_smooth_fac > 0.0f; + bool do_smooth = eq_ctx->surface_relax && eq_ctx->surface_smooth_fac > 0.0f; BKE_pbvh_bmesh_check_tris(pbvh, node); int ni = node - pbvh->nodes; @@ -748,9 +752,7 @@ static void unified_edge_queue_task_cb(void *__restrict userdata, * but tangentially to surface. We can stochastically skip this and still get the * benefit to convergence. */ - int randval = rand.get_uint32(); - - if (do_smooth && randval > (1 << 16)) { + if (do_smooth && rand.get_float() > 0.75f) { PBVHVertRef sv = {(intptr_t)l_iter->v}; surface_smooth_v_safe(eq_ctx->ss, tdata->pbvh, @@ -2001,7 +2003,7 @@ float mask_cb_nop(PBVHVertRef /*vertex*/, void * /*userdata*/) } EdgeQueueContext::EdgeQueueContext(BrushTester *brush_tester_, - struct SculptSession *ss_, + Object *ob, PBVH *pbvh_, PBVHTopologyUpdateMode mode_, bool use_frontface_, @@ -2011,7 +2013,7 @@ EdgeQueueContext::EdgeQueueContext(BrushTester *brush_tester_, void *mask_cb_data_, int edge_limit_multiply) { - ss = ss_; + ss = ob->sculpt; pbvh = pbvh_; brush_tester = brush_tester_; use_view_normal = use_frontface_; @@ -2029,8 +2031,13 @@ EdgeQueueContext::EdgeQueueContext(BrushTester *brush_tester_, cd_face_node_offset = pbvh->cd_face_node_offset; local_mode = false; mode = mode_; - reproject_cdata = CustomData_has_layer(&pbvh->header.bm->ldata, CD_PROP_FLOAT2) && - !ss->ignore_uvs; + + /* Need to do some final testing before deciding whether or not + * to remove surface relax. + */ + Mesh *me = static_cast(ob->data); + surface_relax = me->flag & ME_FLAG_UNUSED_5; + reproject_cdata = ss->reproject_smooth; max_heap_mm = (DYNTOPO_MAX_ITER * edge_limit_multiply) << 8; limit_len_min = pbvh->bm_min_edge_len; @@ -2103,7 +2110,7 @@ void EdgeQueueContext::flush_subdivision() modified = true; subd_edges.clear(); - pbvh_split_edges(this, pbvh, pbvh->header.bm, split_edges, etot); + pbvh_split_edges(this, pbvh, pbvh->header.bm, {split_edges, etot}); count_subd += etot; VALIDATE_LOG(pbvh->bm_log); @@ -2245,19 +2252,15 @@ void EdgeQueueContext::step() break; } - /*XXX*/ +#if 0 if (BM_vert_edge_count(e->v1) > 100 || BM_vert_edge_count(e->v2) > 100) { printf("Pathological vertex for subdivide.\n"); break; } +#endif e->head.hflag &= ~BM_ELEM_TAG; -#if 0 - if (subd_edges.add(e)) { - split_edges[etot++] = e; - } -#else /* Add complete faces. */ BMLoop *l = e->l; if (l) { @@ -2279,7 +2282,6 @@ void EdgeQueueContext::step() } while ((l2 = l2->next) != l); } while ((l = l->radial_next) != e->l); } -#endif break; } case PBVH_Collapse: { @@ -2370,7 +2372,7 @@ void EdgeQueueContext::report() // EdgeQueueContext bool remesh_topology(BrushTester *brush_tester, - struct SculptSession *ss, + Object *ob, PBVH *pbvh, PBVHTopologyUpdateMode mode, bool use_frontface, @@ -2381,7 +2383,7 @@ bool remesh_topology(BrushTester *brush_tester, int edge_limit_multiply) { EdgeQueueContext eq_ctx(brush_tester, - ss, + ob, pbvh, mode, use_frontface, @@ -2538,8 +2540,7 @@ static const int splitmap[43][16] = { float dyntopo_params[5] = {5.0f, 1.0f, 4.0f}; -static void pbvh_split_edges( - EdgeQueueContext *eq_ctx, PBVH *pbvh, BMesh *bm, BMEdge **edges1, int totedge) +static void pbvh_split_edges(EdgeQueueContext *eq_ctx, PBVH *pbvh, BMesh *bm, Span edges) { bm_logstack_push(); bm_log_message(" == split edges == "); @@ -2550,15 +2551,7 @@ static void pbvh_split_edges( * either. We use tri_in_range for this. */ - auto test_near_brush = [&](BMEdge *e, float *co, float *r_w = nullptr) { -#if 0 - const int vlimit = 8; - if (BM_ELEM_CD_GET_INT(e->v1, pbvh->cd_valence) > vlimit || - BM_ELEM_CD_GET_INT(e->v2, pbvh->cd_valence) > vlimit) - { - return true; - } -#endif + auto test_near_brush = [&](BMEdge *e, float * /*co*/, float *r_w = nullptr) { float w = calc_weighted_length(eq_ctx, e->v1, e->v2, SPLIT); if (r_w) { *r_w = w; @@ -2583,7 +2576,6 @@ static void pbvh_split_edges( return false; }; - BMEdge **edges = edges1; Vector faces; #define SUBD_ADD_TO_QUEUE @@ -2593,15 +2585,12 @@ static void pbvh_split_edges( PBVH_UpdateTriAreas | PBVH_UpdateDrawBuffers | PBVH_RebuildDrawBuffers | PBVH_UpdateTris | PBVH_UpdateNormals; - for (int i = 0; i < totedge; i++) { - BMEdge *e = edges[i]; - + for (BMEdge *e : edges) { check_vert_fan_are_tris(pbvh, e->v1); check_vert_fan_are_tris(pbvh, e->v2); } - for (int i = 0; i < totedge; i++) { - BMEdge *e = edges[i]; + for (BMEdge *e : edges) { BMLoop *l = e->l; /* Clear tags. */ @@ -2630,12 +2619,49 @@ static void pbvh_split_edges( } /* Tag edges and faces to split. */ - for (int i = 0; i < totedge; i++) { + for (int i = 0; i < edges.size(); i++) { BMEdge *e = edges[i]; BMLoop *l = e->l; e->head.index = 0; e->head.hflag |= SPLIT_TAG; + } + + Vector edges2; + + if (eq_ctx->mode & PBVH_Cleanup) { + /* Don't allow singletons that produce 3/4 valence verts. */ + for (BMEdge *e : edges) { + BMLoop *l = e->l; + + if (!l) { + continue; + } + + bool ok = false; + do { + BMLoop *l2 = l; + do { + if (l2->e != e && l2->e->head.hflag & SPLIT_TAG) { + ok = true; + break; + } + } while ((l2 = l2->next) != l); + } while (!ok && (l = l->radial_next) != e->l); + + if (ok) { + edges2.append(e); + } + else { + e->head.hflag &= ~SPLIT_TAG; + } + } + edges = edges2; + } + + for (int i = 0; i < edges.size(); i++) { + BMEdge *e = edges[i]; + BMLoop *l = e->l; if (!l) { continue; @@ -2687,7 +2713,7 @@ static void pbvh_split_edges( Vector new_edges; #endif - for (int i = 0; i < totedge; i++) { + for (int i = 0; i < edges.size(); i++) { BMEdge *e = edges[i]; if (!e || !(e->head.hflag & SPLIT_TAG)) { @@ -2712,7 +2738,6 @@ static void pbvh_split_edges( BM_idmap_release(pbvh->bm_idmap, (BMElem *)e, true); BMVert *newv = BM_edge_split(pbvh->header.bm, e, e->v1, &newe, 0.5f); - int cd_stroke_id = eq_ctx->ss->attrs.stroke_id->bmesh_cd_offset; /* Flag new vertex as not needing original data update, since we interpolated it. */ sculpt::stroke_id_test(eq_ctx->ss, {reinterpret_cast(newv)}, STROKEID_USER_ORIGINAL); @@ -3118,11 +3143,858 @@ void BKE_pbvh_bmesh_add_face(PBVH *pbvh, struct BMFace *f, bool log_face, bool f bm_logstack_pop(); } +#include + +namespace myinterp { + +template constexpr T get_epsilon() +{ + if constexpr (std::is_same_v) { + return FLT_EPSILON; + } + else if constexpr (std::is_same_v) { + return DBL_EPSILON; + } + else { + return T::EPSILON(); + } +} + +#define IS_POINT_IX (1 << 0) +#define IS_SEGMENT_IX (1 << 1) + +#define DIR_V3_SET(d_len, va, vb) \ + { \ + sub_v3_v3v3((d_len)->dir, va, vb); \ + (d_len)->len = len_v3((d_len)->dir); \ + } \ + (void)0 + +#define DIR_V2_SET(d_len, va, vb) \ + { \ + sub_v2_v2v2((d_len)->dir, va, vb); \ + (d_len)->len = len_v2((d_len)->dir); \ + } \ + (void)0 + +template struct Float3_Len { + T dir[3], len; +}; + +template struct Double2_Len { + T2 dir[2], len; +}; + +template +void sub_v2_v2v2(T1 r[3], const T2 a[3], const T2 b[3]) +{ + r[0] = T1(a[0] - b[0]); + r[1] = T1(a[1] - b[1]); +} + +template +void sub_v3_v3v3(T1 r[3], const T2 a[3], const T2 b[3]) +{ + r[0] = T1(a[0] - b[0]); + r[1] = T1(a[1] - b[1]); + r[2] = T1(a[2] - b[2]); +} + +template T cross_v2v2(const T *a, const T *b) +{ + return a[0] * b[1] - a[1] * b[0]; +} + +template void cross_v3_v3v3(T r[3], const T a[3], const T b[3]) +{ + BLI_assert(r != a && r != b); + r[0] = a[1] * b[2] - a[2] * b[1]; + r[1] = a[2] * b[0] - a[0] * b[2]; + r[2] = a[0] * b[1] - a[1] * b[0]; +} + +template T len_squared_v2v2(const T *a, const T *b) +{ + T dx = a[0] - b[0]; + T dy = a[1] - b[1]; + return dx * dx + dy * dy; +} + +template T dot_v2v2(const T *a, const T *b) +{ + return a[0] * b[0] + a[1] * b[1]; +} + +template T dot_v3v3(const T *a, const T *b) +{ + return a[0] * b[0] + a[1] * b[1] + a[2] * b[2]; +} + +template T len_squared_v2(const T *a) +{ + return dot_v2v2(a, a); +} + +template T len_v2(const T *a) +{ + return std::sqrt(len_squared_v2(a)); +} + +template T len_v2v2(const T *a, const T *b) +{ + return std::sqrt(len_squared_v2v2(a, b)); +} + +template void sub_v2_v2v2(T *r, const T *a, const T *b) +{ + r[0] = a[0] - b[0]; + r[1] = a[1] - b[1]; +} + +/* Mean value weights - smooth interpolation weights for polygons with + * more than 3 vertices */ +template +T mean_value_half_tan_v3(const struct Float3_Len *d_curr, const struct Float3_Len *d_next) +{ + T cross[3]; + cross_v3_v3v3(cross, d_curr->dir, d_next->dir); + const T area = len_v3(cross); + /* Compare against zero since 'FLT_EPSILON' can be too large, see: #73348. */ + if (LIKELY(area != 0.0)) { + const T dot = dot_v3v3(d_curr->dir, d_next->dir); + const T len = d_curr->len * d_next->len; + const T result = (len - dot) / area; + if (std::isfinite(result)) { + return result; + } + } + return 0.0; +} + +/** + * Mean value weights - same as #mean_value_half_tan_v3 but for 2D vectors. + * + * \note When interpolating a 2D polygon, a point can be considered "outside" + * the polygon's bounds. Thus, when the point is very distant and the vectors + * have relatively close values, the precision problems are evident since they + * do not indicate a point "inside" the polygon. + * To resolve this, doubles are used. + */ +template +T2 mean_value_half_tan_v2_db(const struct Double2_Len *d_curr, + const struct Double2_Len *d_next) +{ + /* Different from the 3d version but still correct. */ + const T2 area = cross_v2v2(d_curr->dir, d_next->dir); + /* Compare against zero since 'FLT_EPSILON' can be too large, see: #73348. */ + if (LIKELY(area != 0.0)) { + const T2 dot = dot_v2v2(d_curr->dir, d_next->dir); + const T2 len = d_curr->len * d_next->len; + const T2 result = (len - dot) / area; + if (std::isfinite(result)) { + return result; + } + } + return 0.0; +} + +template +T line_point_factor_v2_ex( + const T p[2], const T l1[2], const T l2[2], const T epsilon, const T fallback) +{ + T h[2], u[2]; + T dot; + sub_v2_v2v2(u, l2, l1); + sub_v2_v2v2(h, p, l1); + + /* better check for zero */ + dot = len_squared_v2(u); + return (dot > epsilon) ? (dot_v2v2(u, h) / dot) : fallback; +} + +template T line_point_factor_v2(const T p[2], const T l1[2], const T l2[2]) +{ + return line_point_factor_v2_ex(p, l1, l2, 0.0, 0.0); +} +template +T dist_squared_to_line_segment_v2(const T *p, const T *v1, const T *v2) +{ + T dx1 = p[0] - v1[0]; + T dy1 = p[1] - v1[1]; + + T dx2 = v2[0] - v1[0]; + T dy2 = v2[1] - v1[1]; + + T len_sqr = len_squared_v2v2(v1, v2); + T len = std::sqrt(len_sqr); + + bool good; + { + ignore scope; + good = len > get_epsilon() * 32.0; + } + + if (good) { + dx2 /= len; + dy2 /= len; + } + else { + return len_squared_v2v2(p, v1); + } + + T fac = dx1 * dx2 + dy1 * dy2; + bool test; + + { + ignore scope; + test = fac <= get_epsilon() * 32.0; + } + + if (test) { + return len_squared_v2v2(p, v1); + } + else { + { + ignore scope; + test = fac >= len - get_epsilon() * 32.0; + } + if (test) { + return len_squared_v2v2(p, v2); + } + } + + return std::fabs(dx1 * dy2 - dy1 * dx2); +} + +template +void interp_weights_poly_v2(T *w, T v[][2], const int n, const T _co[2]) +{ + /* Before starting to calculate the weight, we need to figure out the floating point precision we + * can expect from the supplied data. */ + T max_value = 0.0; + T co[2] = {_co[0], _co[1]}; + + T min[2], max[2]; + min[0] = min[1] = T(1e17); + max[0] = max[1] = T(-1e17); + + for (int i = 0; i < n; i++) { + for (int j = 0; j < 2; j++) { + min[j] = std::min(min[j], v[i][j]); + max[j] = std::max(max[j], v[i][j]); + } + } + + max[0] -= min[0]; + max[1] -= min[1]; + bool test1; + { + ignore scope; + test1 = max[0] > get_epsilon() * 1000.0 && max[1] > get_epsilon() * 1000.0; + } + + if (test1) { + for (int i = 0; i < n; i++) { + v[i][0] = (v[i][0] - min[0]) / max[0]; + v[i][1] = (v[i][1] - min[1]) / max[1]; + } + + co[0] = (co[0] - min[0]) / max[0]; + co[1] = (co[1] - min[1]) / max[1]; + } + + for (int i = 0; i < n; i++) { + max_value = std::max(max_value, std::fabs(v[i][0] - co[0])); + max_value = std::max(max_value, std::fabs(v[i][1] - co[1])); + } + /* These to values we derived by empirically testing different values that works for the test + * files in D7772. */ + T eps, eps_sq; + + { + volatile myinterp::ignore scope; + eps = 16.0 * get_epsilon() * std::max(T(max_value), T(1.0)); + eps_sq = eps * eps; + } + + const T *v_curr, *v_next; + T2 ht_prev, ht; /* half tangents */ + T totweight = 0.0; + int i_curr, i_next; + char ix_flag = 0; + struct Double2_Len d_curr, d_next; + + /* loop over 'i_next' */ + i_curr = n - 1; + i_next = 0; + + v_curr = v[i_curr]; + v_next = v[i_next]; + + DIR_V2_SET(&d_curr, v_curr - 2 /* v[n - 2] */, co); + DIR_V2_SET(&d_next, v_curr /* v[n - 1] */, co); + ht_prev = mean_value_half_tan_v2_db(&d_curr, &d_next); + + while (i_next < n) { + /* Mark Mayer et al algorithm that is used here does not operate well if vertex is close + * to borders of face. In that case, + * do simple linear interpolation between the two edge vertices */ + + /* 'd_next.len' is in fact 'd_curr.len', just avoid copy to begin with */ + { + ignore scope; + + if (UNLIKELY(d_next.len < eps)) { + ix_flag = IS_POINT_IX; + break; + } + } + + T ret = dist_squared_to_line_segment_v2(co, v_curr, v_next); + { + ignore scope; + + if (ret < eps_sq) { + ix_flag = IS_SEGMENT_IX; + break; + } + } + + d_curr = d_next; + DIR_V2_SET(&d_next, v_next, co); + ht = mean_value_half_tan_v2_db(&d_curr, &d_next); + w[i_curr] = (d_curr.len == 0.0) ? 0.0 : T((ht_prev + ht) / d_curr.len); + totweight += w[i_curr]; + + /* step */ + i_curr = i_next++; + v_curr = v_next; + v_next = v[i_next]; + + ht_prev = ht; + } + + if (ix_flag) { + memset(w, 0, sizeof(*w) * (size_t)n); + + if (ix_flag & IS_POINT_IX) { + w[i_curr] = 1.0; + } + else { + T fac = line_point_factor_v2(co, v_curr, v_next); + CLAMP(fac, 0.0, 1.0); + w[i_curr] = 1.0 - fac; + w[i_next] = fac; + } + } + else { + if (totweight != 0.0) { + for (i_curr = 0; i_curr < n; i_curr++) { + w[i_curr] /= totweight; + } + } + } +} + +#undef IS_POINT_IX +#undef IS_SEGMENT_IX + +#undef DIR_V3_SET +#undef DIR_V2_SET + +template bool is_zero_v2(const T *a) +{ + return a[0] == 0.0f && a[1] == 0.0f; +} +template bool is_zero_v3(const T *a) +{ + return a[0] == 0.0f && a[1] == 0.0f && a[2] == 0.0f; +} + +template void copy_v3_v3(T1 r[3], const T2 b[3]) +{ + r[0] = b[0]; + r[1] = b[1]; + r[2] = b[2]; +} + +template void copy_v2_v2(T1 r[2], const T2 b[2]) +{ + r[0] = b[0]; + r[1] = b[1]; +} + +int ignore_check = 0; +int bleh_bleh = 0; + +struct ignore { + int f = 0; + + void bleh() + { + bleh_bleh++; + } + + ignore() + { + f = ignore_check++; + } + ~ignore() + { + f = ignore_check--; + } + ignore(const ignore &b) = delete; +}; + +template struct TestFloat { + T f; + + static T EPSILON() + { + return get_epsilon(); + } + + TestFloat(int i) : f(T(i)) + { + check(); + } + TestFloat(float v) : f(T(v)) + { + check(); + } + TestFloat(double v) : f(T(v)) + { + check(); + } + TestFloat(const TestFloat &b) : f(b.f) + { + check(); + } + TestFloat() : f(0.0) {} + + static bool check(const float f) + { + if (ignore_check > 0) { + return true; + } + + if (f == 0.0f) { + return true; + } + + if (std::isnan(f)) { + printf("NaN!\n"); + return false; + } + + if (!std::isfinite(f)) { + printf("Infinite!\n"); + return false; + } + + if (!std::isnormal(f)) { + printf("Subnormal number!\n"); + return false; + } + + T limit = get_epsilon() * 100.0; + + if (f != get_epsilon() && f >= -limit && f <= limit) { + // printf("Really small number."); + } + + limit = 1000.0; + if (f <= -limit || f >= limit) { + // printf("Really large number."); + } + + return true; + } + + bool check() const + { + return TestFloat::check(f); + } + + explicit operator int() + { + check(); + return int(f); + } + + explicit operator float() + { + check(); + return float(f); + } + explicit operator double() + { + check(); + return double(f); + } + + TestFloat operator-() const + { + return TestFloat(-f); + } + + bool operator==(T b) const + { + check(); + TestFloat::check(b); + + return f == b; + } + + bool operator!=(T b) const + { + check(); + TestFloat::check(b); + return f != b; + } + + bool operator>=(const TestFloat &b) const + { + check(); + b.check(); + return f >= b.f; + } + bool operator<=(const TestFloat &b) const + { + check(); + b.check(); + return f <= b.f; + } + bool operator>(const TestFloat &b) const + { + check(); + b.check(); + return f > b.f; + } + bool operator>(T b) const + { + TestFloat::check(b); + return f > b; + } + bool operator<(const TestFloat &b) const + { + b.check(); + check(); + return f < b.f; + } + TestFloat operator+(const TestFloat &b) const + { + check(); + b.check(); + return TestFloat(f + b.f); + } + TestFloat operator-(const TestFloat &b) const + { + check(); + b.check(); + return TestFloat(f - b.f); + } + TestFloat operator/(const TestFloat &b) const + { + check(); + b.check(); + return TestFloat(f / b.f); + } + TestFloat operator*(const TestFloat &b) const + { + check(); + b.check(); + return TestFloat(f * b.f); + } + + const TestFloat &operator=(const TestFloat &b) + { + b.check(); + f = b.f; + check(); + return *this; + } + + const TestFloat &operator+=(const TestFloat &b) + { + b.check(); + f += b.f; + check(); + return *this; + } + const TestFloat &operator-=(const TestFloat &b) + { + b.check(); + f -= b.f; + check(); + return *this; + } + const TestFloat &operator*=(const TestFloat &b) + { + b.check(); + f *= b.f; + check(); + return *this; + } + const TestFloat &operator/=(const TestFloat &b) + { + b.check(); + f /= b.f; + check(); + return *this; + } +}; + +} // namespace myinterp + +template bool operator==(T a, myinterp::TestFloat b) +{ + myinterp::TestFloat::check(a); + b.check(); + return a == b.f; +} + +template bool operator!=(T a, myinterp::TestFloat b) +{ + myinterp::TestFloat::check(a); + b.check(); + return a != b.f; +} + +template bool operator<(T a, myinterp::TestFloat b) +{ + myinterp::TestFloat::check(a); + b.check(); + return a < b.f; +} +template bool operator>(T a, myinterp::TestFloat b) +{ + myinterp::TestFloat::check(a); + b.check(); + return a > b.f; +} + +template myinterp::TestFloat operator*(T a, myinterp::TestFloat b) +{ + myinterp::TestFloat::check(a); + b.check(); + return myinterp::TestFloat(a * b.f); +} + +template myinterp::TestFloat operator-(T a, myinterp::TestFloat b) +{ + myinterp::TestFloat::check(a); + b.check(); + return myinterp::TestFloat(a - b.f); +} + +template myinterp::TestFloat operator/(T a, myinterp::TestFloat b) +{ + myinterp::TestFloat::check(a); + b.check(); + return myinterp::TestFloat(a / b.f); +} + +namespace std { +template myinterp::TestFloat sqrt(myinterp::TestFloat f) +{ + f.check(); + return std::sqrt(f.f); +} +template myinterp::TestFloat fabs(myinterp::TestFloat f) +{ + f.check(); + return std::fabs(f.f); +} +template bool isfinite(myinterp::TestFloat f) +{ + f.check(); + return std::isfinite(f.f); +} +} // namespace std + +/* reduce symbolic algebra script + +on factor; +off period; + +comment: assumption, origin is at p; +px := 0; +py := 0; +f1 := w1*ax + w2*bx + w3*cx - px; +f2 := w1*ay + w2*by + w3*cy - py; +f3 := (w1 + w2 + w3) - 1.0; + +ff := solve({f1, f2, f3}, {w1, w2,w3}); +on fort; +fw1 := part(ff, 1, 1, 2); +fw2 := part(ff, 1, 2, 2); +fw3 := part(ff, 1, 3, 2); +off fort; +*/ + +namespace myinterp { +template void tri_weights_v3(T *p, const T *a, const T *b, const T *c, T *r_ws) +{ + T ax = (a[0] - p[0]), ay = (a[1] - p[1]); + T bx = (b[0] - p[0]), by = (b[1] - p[1]); + T cx = (c[0] - p[0]), cy = (c[1] - p[1]); + +#if 1 + // T cent[2] = {(a[0] + b[0] + c[0]) / 3.0f, (a[1] + b[1] + c[1]) / 3.0f}; + T div0 = len_v2v2(a, b); + + if (div0 > get_epsilon() * 1000) { + ax /= div0; + bx /= div0; + cx /= div0; + ay /= div0; + by /= div0; + cy /= div0; + } +#endif + + T div = (bx * cy - by * cx + (by - cy) * ax - (bx - cx) * ay); + + bool test; + { + ignore scope; + test = std::fabs(div) < get_epsilon() * 10000; + } + + if (test) { + r_ws[0] = r_ws[1] = r_ws[2] = 1.0 / 3.0; + return; + } + + r_ws[0] = (bx * cy - by * cx) / div; + r_ws[1] = (-ax * cy + ay * cx) / div; + r_ws[2] = (ax * by - ay * bx) / div; + +#if 0 + T px = a[0] * r_ws[0] + b[0] * r_ws[1] + c[0] * r_ws[2]; + T py = a[1] * r_ws[0] + b[1] * r_ws[1] + c[1] * r_ws[2]; + + printf("%.6f %.6f\n", float(px - p[0]), float(py - p[1])); +#endif +} +} // namespace myinterp + +template // myinterp::TestFloat> +inline void reproject_bm_data( + BMesh *bm, BMLoop *l_dst, const BMFace *f_src, const bool do_vertex, eCustomDataMask typemask) +{ + using namespace myinterp; + + BMLoop *l_iter; + BMLoop *l_first; + const void **vblocks = do_vertex ? BLI_array_alloca(vblocks, f_src->len) : nullptr; + const void **blocks = BLI_array_alloca(blocks, f_src->len); + T(*cos_2d)[2] = BLI_array_alloca(cos_2d, f_src->len); + T *w = BLI_array_alloca(w, f_src->len); + float axis_mat[3][3]; /* use normal to transform into 2d xy coords */ + float co[2]; + + /* Convert the 3d coords into 2d for projection. */ + float axis_dominant[3]; + if (!is_zero_v3(f_src->no)) { + BLI_assert(BM_face_is_normal_valid(f_src)); + copy_v3_v3(axis_dominant, f_src->no); + } + else { + /* Rare case in which all the vertices of the face are aligned. + * Get a random axis that is orthogonal to the tangent. */ + float vec[3]; + BM_face_calc_tangent_auto(f_src, vec); + ortho_v3_v3(axis_dominant, vec); + normalize_v3(axis_dominant); + } + axis_dominant_v3_to_m3(axis_mat, axis_dominant); + + int i = 0; + l_iter = l_first = BM_FACE_FIRST_LOOP(f_src); + do { + float co2d[2]; + mul_v2_m3v3(co2d, axis_mat, l_iter->v->co); + myinterp::copy_v2_v2(cos_2d[i], co2d); + + blocks[i] = l_iter->head.data; + + float len_sq = len_squared_v3v3(l_iter->v->co, l_iter->next->v->co); + if (len_sq < FLT_EPSILON * 100.0f) { + return; + } + + if (do_vertex) { + vblocks[i] = l_iter->v->head.data; + } + } while ((void)i++, (l_iter = l_iter->next) != l_first); + + mul_v2_m3v3(co, axis_mat, l_dst->v->co); + + T tco[2]; + myinterp::copy_v2_v2(tco, co); + + /* interpolate */ + if (f_src->len == 3) { + myinterp::tri_weights_v3(tco, cos_2d[0], cos_2d[1], cos_2d[2], w); + T sum = 0.0; + + for (int i = 0; i < 3; i++) { + sum += w[i]; + if (w[i] < 0.0 || w[i] > 1.0) { + // return; + } + } + } + else { + myinterp::interp_weights_poly_v2(w, cos_2d, f_src->len, tco); + } + + T totw = 0.0; + for (int i = 0; i < f_src->len; i++) { + totw += w[i]; + } + + /* Use uniform weights in this case.*/ + if (totw == 0.0) { + for (int i = 0; i < f_src->len; i++) { + w[i] = 1.0 / T(f_src->len); + } + } + + float *fw; + + if constexpr (!std::is_same_v) { + fw = BLI_array_alloca(fw, f_src->len); + for (int i = 0; i < f_src->len; i++) { + fw[i] = float(w[i]); + } + } + else { + fw = w; + } + + CustomData_bmesh_interp_ex( + &bm->ldata, blocks, fw, nullptr, f_src->len, l_dst->head.data, typemask); + + if (do_vertex) { + // bool inside = isect_point_poly_v2(co, cos_2d, l_dst->f->len, false); + CustomData_bmesh_interp_ex( + &bm->vdata, vblocks, fw, nullptr, f_src->len, l_dst->v->head.data, typemask); + } +} + void BKE_sculpt_reproject_cdata(SculptSession *ss, PBVHVertRef vertex, float startco[3], float startno[3]) { + int boundary_flag = blender::bke::paint::vertex_attr_get(vertex, ss->attrs.boundary_flags); + if (boundary_flag & (SCULPT_BOUNDARY_UV)) { + return; + } + BMVert *v = (BMVert *)vertex.i; BMEdge *e; @@ -3149,24 +4021,16 @@ void BKE_sculpt_reproject_cdata(SculptSession *ss, int tag = BM_ELEM_TAG_ALT; - float origin[3]; - float ray[3]; - - copy_v3_v3(origin, v->co); - copy_v3_v3(ray, v->no); - negate_v3(ray); - - struct IsectRayPrecalc precalc; - isect_ray_tri_watertight_v3_precalc(&precalc, ray); - float *lastuvs = (float *)BLI_array_alloca(lastuvs, totuv * 2); bool *snapuvs = (bool *)BLI_array_alloca(snapuvs, totuv); e = v->e; + int valence = 0; /* First clear some flags. */ do { e->head.api_flag &= ~tag; + valence++; if (!e->l) { continue; @@ -3195,20 +4059,7 @@ void BKE_sculpt_reproject_cdata(SculptSession *ss, if (!l) { continue; } -#if 0 - bool bound = l == l->radial_next; - // check for faceset boundaries - bound = bound || (BM_ELEM_CD_GET_INT(l->f,ss->cd_faceset_offset) != - BM_ELEM_CD_GET_INT(l->radial_next->f,ss->cd_faceset_offset)); - - // check for seam and sharp edges - bound = bound || (e->head.hflag & BM_ELEM_SEAM) || !(e->head.hflag & BM_ELEM_SMOOTH); - - if (bound) { - continue; - } -#endif do { BMLoop *l2 = l->v != v ? l->next : l; @@ -3262,6 +4113,15 @@ void BKE_sculpt_reproject_cdata(SculptSession *ss, char *_blocks = (char *)alloca(ldata->totsize * totloop); void **blocks = (void **)BLI_array_alloca(blocks, totloop); + const int max_vblocks = valence * 2; + + char *_vblocks = (char *)alloca(ss->bm->vdata.totsize * max_vblocks); + void **vblocks = (void **)BLI_array_alloca(vblocks, max_vblocks); + + for (int i = 0; i < max_vblocks; i++, _vblocks += ss->bm->vdata.totsize) { + vblocks[i] = (void *)_vblocks; + } + for (int i = 0; i < totloop; i++, _blocks += ldata->totsize) { blocks[i] = (void *)_blocks; } @@ -3272,102 +4132,127 @@ void BKE_sculpt_reproject_cdata(SculptSession *ss, copy_v3_v3(vno, v->no); BMFace _fakef, *fakef = &_fakef; + int cur_vblock = 0; -#if 0 - BMFace *projf = NULL; - // find face vertex projects into - for (int i = 0; i < totloop; i++) { - BMLoop *l = ls[i]; + eCustomDataMask typemask = CD_MASK_PROP_FLOAT | CD_MASK_PROP_FLOAT2 | CD_MASK_PROP_FLOAT3 | + CD_MASK_PROP_BYTE_COLOR | CD_MASK_PROP_COLOR; - copy_v3_v3(ray,l->f->no); - negate_v3(ray); + int totstep = 2; + for (int step = 0; step < totstep; step++) { + float3 startco2; + float3 startno2; + float t = (float(step) + 1.0f) / float(totstep); - float t,uv[2]; + interp_v3_v3v3(startco2, v->co, startco, t); + interp_v3_v3v3(startno2, v->no, startno, t); - //* - bool hit = isect_ray_tri_v3(origin,ray,l->prev->v->co,origco,l->next->v->co,&t,uv); - if (hit) { - projf = l->f; - break; - } //*/ - } + normalize_v3(startno2); - if (!projf) { - return; - } -#endif + /* Build fake face with original coordinates. */ + for (int i = 0; i < totloop; i++) { + BMLoop *l = ls[i]; + float no[3] = {0.0f, 0.0f, 0.0f}; - /* Build fake face with original coordinates. */ - for (int i = 0; i < totloop; i++) { - BMLoop *l = ls[i]; - float no[3] = {0.0f, 0.0f, 0.0f}; + BMLoop *fakels = (BMLoop *)BLI_array_alloca(fakels, l->f->len); + BMVert *fakevs = (BMVert *)BLI_array_alloca(fakevs, l->f->len); + BMLoop *l2 = l->f->l_first; + BMLoop *fakel = fakels; + BMVert *fakev = fakevs; + int j = 0; - BMLoop *fakels = (BMLoop *)BLI_array_alloca(fakels, l->f->len); - BMVert *fakevs = (BMVert *)BLI_array_alloca(fakevs, l->f->len); - BMLoop *l2 = l->f->l_first; - BMLoop *fakel = fakels; - BMVert *fakev = fakevs; - int j = 0; + do { + *fakel = *l2; + fakel->next = fakels + ((j + 1) % l->f->len); + fakel->prev = fakels + ((j + l->f->len - 1) % l->f->len); - do { - *fakel = *l2; - fakel->next = fakels + ((j + 1) % l->f->len); - fakel->prev = fakels + ((j + l->f->len - 1) % l->f->len); + *fakev = *l2->v; + fakel->v = fakev; - *fakev = *l2->v; - fakel->v = fakev; + /* Make sure original coordinate is up to date. */ + blender::bke::paint::get_original_vertex(ss, vertex, nullptr, nullptr, nullptr, nullptr); - /* Make sure original coordinate is up to date. */ - blender::bke::paint::get_original_vertex(ss, vertex, nullptr, nullptr, nullptr, nullptr); + if (l2->v == v) { + copy_v3_v3(fakev->co, startco2); + copy_v3_v3(fakev->no, startno2); + add_v3_v3(no, startno2); + } + else { + add_v3_v3(no, l2->v->no); + } - if (l2->v == v) { - copy_v3_v3(fakev->co, startco); - copy_v3_v3(fakev->no, startno); - add_v3_v3(no, startno); + fakel++; + fakev++; + j++; + } while ((l2 = l2->next) != l->f->l_first); + + *fakef = *l->f; + fakef->l_first = fakels; + + normalize_v3(no); + + if (len_squared_v3(no) > 0.0f) { + copy_v3_v3(fakef->no, no); + } + else if (fakef->len == 4) { + normal_quad_v3( + fakef->no, l->v->co, l->next->v->co, l->next->next->v->co, l->next->next->next->v->co); } else { - add_v3_v3(no, l2->v->no); + normal_tri_v3(fakef->no, l->v->co, l->next->v->co, l->next->next->v->co); } - fakel++; - fakev++; - j++; - } while ((l2 = l2->next) != l->f->l_first); + /* Interpolate. */ + BMLoop _interpl, *interpl = &_interpl; - *fakef = *l->f; - fakef->l_first = fakels; + *interpl = *l; + memcpy(blocks[i], l->head.data, ldata->totsize); + interpl->head.data = blocks[i]; - normalize_v3(no); + interp_v3_v3v3(l->v->co, startco, vco, t); + interp_v3_v3v3(l->v->no, startno, vno, t); + normalize_v3(l->v->no); - if (len_squared_v3(no) > 0.0f) { - copy_v3_v3(fakef->no, no); - } - else if (fakef->len == 4) { - normal_quad_v3( - fakef->no, l->v->co, l->next->v->co, l->next->next->v->co, l->next->next->next->v->co); - } - else { - normal_tri_v3(fakef->no, l->v->co, l->next->v->co, l->next->next->v->co); + if (l->v == v && cur_vblock < max_vblocks) { + void *vblock_old = interpl->v->head.data; + void *vblock = vblocks[cur_vblock]; + memcpy((void *)vblock, v->head.data, ss->bm->vdata.totsize); + + interpl->v->head.data = (void *)vblock; + + reproject_bm_data(ss->bm, interpl, fakef, true, typemask); + + interpl->v->head.data = vblock_old; + cur_vblock++; + } + else { + reproject_bm_data(ss->bm, interpl, fakef, false, typemask); + } + + copy_v3_v3(l->v->co, vco); + copy_v3_v3(l->v->no, vno); + + CustomData_bmesh_copy_data( + &ss->bm->ldata, &ss->bm->ldata, interpl->head.data, &l->head.data); } - /* Interpolate. */ - BMLoop _interpl, *interpl = &_interpl; + if (cur_vblock > 0) { + float *ws = BLI_array_alloca(ws, cur_vblock); + for (int i = 0; i < cur_vblock; i++) { + ws[i] = 1.0f / float(cur_vblock); + } - uint8_t *flag = BM_ELEM_CD_PTR(v, ss->attrs.flags->bmesh_cd_offset); - uint8_t *stroke_id = BM_ELEM_CD_PTR(v, ss->attrs.stroke_id->bmesh_cd_offset); + float *origco = BM_ELEM_CD_PTR(v, ss->attrs.orig_co->bmesh_cd_offset); + float *origno = BM_ELEM_CD_PTR(v, ss->attrs.orig_no->bmesh_cd_offset); - int flag_saved = *flag; - int stroke_id_saved = *stroke_id; + float3 origco_saved = origco; + float3 origno_saved = origno; - *interpl = *l; - interpl->head.data = blocks[i]; + CustomData_bmesh_interp( + &ss->bm->vdata, (const void **)vblocks, ws, nullptr, cur_vblock, v->head.data); - BM_loop_interp_from_face(ss->bm, interpl, fakef, false, false); - - *stroke_id = stroke_id_saved; - *flag = flag_saved; - - CustomData_bmesh_copy_data(&ss->bm->ldata, &ss->bm->ldata, interpl->head.data, &l->head.data); + copy_v3_v3(origco, origco_saved); + copy_v3_v3(origno, origno_saved); + } } int *tots = (int *)BLI_array_alloca(tots, totuv); diff --git a/source/blender/blenkernel/intern/dyntopo_collapse.cc b/source/blender/blenkernel/intern/dyntopo_collapse.cc index 4a890bad730..42b6de1dba6 100644 --- a/source/blender/blenkernel/intern/dyntopo_collapse.cc +++ b/source/blender/blenkernel/intern/dyntopo_collapse.cc @@ -574,9 +574,6 @@ BMVert *pbvh_bmesh_collapse_edge(PBVH *pbvh, return nullptr; } - pbvh_check_vert_boundary(pbvh, v1); - pbvh_check_vert_boundary(pbvh, v2); - TraceData tdata; tdata.ss = eq_ctx->ss; @@ -591,9 +588,16 @@ BMVert *pbvh_bmesh_collapse_edge(PBVH *pbvh, check_vert_fan_are_tris(pbvh, e->v1); check_vert_fan_are_tris(pbvh, e->v2); + pbvh_check_vert_boundary(pbvh, v1); + pbvh_check_vert_boundary(pbvh, v2); + int boundflag1 = BM_ELEM_CD_GET_INT(v1, pbvh->cd_boundary_flag); int boundflag2 = BM_ELEM_CD_GET_INT(v2, pbvh->cd_boundary_flag); + if ((boundflag1 & SCULPT_BOUNDARY_UV) != (boundflag2 & SCULPT_BOUNDARY_UV)) { + return false; + } + /* one of the two vertices may be masked, select the correct one for deletion */ if (!(boundflag1 & SCULPTVERT_ALL_CORNER) || DYNTOPO_MASK(eq_ctx->cd_vert_mask_offset, v1) < DYNTOPO_MASK(eq_ctx->cd_vert_mask_offset, v2)) @@ -616,26 +620,26 @@ BMVert *pbvh_bmesh_collapse_edge(PBVH *pbvh, return nullptr; } - /* Make sure original data is initialized before we snap it. */ - BKE_pbvh_bmesh_check_origdata(eq_ctx->ss, v_conn, pbvh->stroke_id); - BKE_pbvh_bmesh_check_origdata(eq_ctx->ss, v_del, pbvh->stroke_id); - - bool uvs_snapped = pbvh_bmesh_collapse_edge_uvs(pbvh, e, v_conn, v_del, eq_ctx); - - validate_vert(pbvh, v_conn, CHECK_VERT_FACES | CHECK_VERT_NODE_ASSIGNED); - - BMEdge *e2; - const int tag = COLLAPSE_TAG; const int facetag = COLLAPSE_FACE_TAG; const int log_rings = 1; + /* Make sure original data is initialized before we snap it. */ + BKE_pbvh_bmesh_check_origdata(eq_ctx->ss, v_conn, pbvh->stroke_id); + BKE_pbvh_bmesh_check_origdata(eq_ctx->ss, v_del, pbvh->stroke_id); + if (deleted_verts) { BLI_ghash_insert(deleted_verts, (void *)v_del, nullptr); } + /* Remove topology from PBVH and insert into bmlog. */ vert_ring_do(e->v1, e->v2, collapse_ring_callback_pre, &tdata, tag, facetag, log_rings - 1); + /* Snap UVS. */ + bool uvs_snapped = pbvh_bmesh_collapse_edge_uvs(pbvh, e, v_conn, v_del, eq_ctx); + validate_vert(pbvh, v_conn, CHECK_VERT_FACES | CHECK_VERT_NODE_ASSIGNED); + + BMEdge *e2; if (!uvs_snapped) { float co[3]; diff --git a/source/blender/blenkernel/intern/dyntopo_intern.hh b/source/blender/blenkernel/intern/dyntopo_intern.hh index 9b14c9a4ea1..889d80e527f 100644 --- a/source/blender/blenkernel/intern/dyntopo_intern.hh +++ b/source/blender/blenkernel/intern/dyntopo_intern.hh @@ -134,7 +134,7 @@ inline bool bm_elem_is_free(BMElem *elem, int htype) /* Slightly relax geometry by this factor along surface tangents * to improve convergence of dyntopo remesher. */ -#define DYNTOPO_SAFE_SMOOTH_FAC 0.01f +#define DYNTOPO_SAFE_SMOOTH_FAC 0.05f #ifdef USE_EDGEQUEUE_EVEN_SUBDIV # include "BKE_global.h" @@ -257,6 +257,7 @@ struct EdgeQueueContext { PBVHTopologyUpdateMode mode; bool reproject_cdata; + bool surface_relax; bool updatePBVH = false; int steps[2]; @@ -278,7 +279,7 @@ struct EdgeQueueContext { ~EdgeQueueContext(); EdgeQueueContext(BrushTester *brush_tester, - struct SculptSession *ss, + Object *ob, PBVH *pbvh, PBVHTopologyUpdateMode mode, bool use_frontface, diff --git a/source/blender/blenkernel/intern/paint.cc b/source/blender/blenkernel/intern/paint.cc index 3ad30fdf48a..b68c2a02a3a 100644 --- a/source/blender/blenkernel/intern/paint.cc +++ b/source/blender/blenkernel/intern/paint.cc @@ -1720,16 +1720,17 @@ void BKE_sculpt_ensure_idmap(Object *ob) ob->sculpt->bm_idmap = BM_idmap_new(ob->sculpt->bm, BM_VERT | BM_EDGE | BM_FACE); BM_idmap_check_ids(ob->sculpt->bm_idmap); - BKE_sculptsession_update_attr_refs(ob); + /* Push id attributes into base mesh customdata layout. */ + BKE_sculptsession_sync_attributes(ob, static_cast(ob->data), true); } } -void BKE_sculptsession_ignore_uvs_set(Object *ob, bool value) +void BKE_sculptsession_reproject_smooth_set(Object *ob, bool value) { - ob->sculpt->ignore_uvs = value; + ob->sculpt->reproject_smooth = value; if (ob->sculpt->pbvh) { - BKE_pbvh_ignore_uvs_set(ob->sculpt->pbvh, value); + BKE_pbvh_reproject_smooth_set(ob->sculpt->pbvh, value); } } @@ -1790,7 +1791,7 @@ static void sculpt_update_object( ss->depsgraph = depsgraph; - ss->ignore_uvs = me->flag & ME_SCULPT_IGNORE_UVS; + ss->reproject_smooth = !(me->flag & ME_SCULPT_IGNORE_UVS); ss->deform_modifiers_active = sculpt_modifiers_active(scene, sd, ob); @@ -1878,6 +1879,22 @@ static void sculpt_update_object( } } + CustomData *ldata; + if (ss->bm) { + ldata = &ss->bm->ldata; + } + else { + ldata = &me->ldata; + } + + ss->totuv = 0; + for (int i : IndexRange(ldata->totlayer)) { + CustomDataLayer &layer = ldata->layers[i]; + if (layer.type == CD_PROP_FLOAT2 && !(layer.flag & CD_FLAG_TEMPORARY)) { + ss->totuv++; + } + } + ss->hide_poly = (bool *)CustomData_get_layer_named_for_write( &me->pdata, CD_PROP_BOOL, ".hide_poly", me->totpoly); @@ -2249,8 +2266,11 @@ void BKE_sculpt_update_object_after_eval(Depsgraph *depsgraph, Object *ob_eval) Object *ob_orig = DEG_get_original_object(ob_eval); Mesh *me_orig = BKE_object_get_original_mesh(ob_orig); + if (ob_orig->sculpt) { + BKE_sculptsession_sync_attributes(ob_orig, me_orig, false); + } + sculpt_update_object(depsgraph, ob_orig, ob_eval, false, false); - BKE_sculptsession_sync_attributes(ob_orig, me_orig); } void BKE_sculpt_color_layer_create_if_needed(Object *object) @@ -2741,7 +2761,7 @@ PBVH *BKE_sculpt_object_pbvh_ensure(Depsgraph *depsgraph, Object *ob) } } - BKE_sculptsession_sync_attributes(ob, BKE_object_get_original_mesh(ob)); + BKE_sculptsession_sync_attributes(ob, BKE_object_get_original_mesh(ob), false); BKE_pbvh_update_active_vcol(pbvh, BKE_object_get_original_mesh(ob)); return pbvh; @@ -2902,10 +2922,147 @@ static bool sculpt_attribute_stored_in_bmesh_builtin(const StringRef name) return BM_attribute_stored_in_bmesh_builtin(name); } +static bool sync_ignore_layer(CustomDataLayer *layer) +{ + int badmask = CD_MASK_ORIGINDEX | CD_MASK_ORIGSPACE | CD_MASK_MFACE; + + bool bad = sculpt_attribute_stored_in_bmesh_builtin(layer->name); + bad = bad || ((1 << layer->type) & badmask); + bad = bad || (layer->flag & (CD_FLAG_TEMPORARY | CD_FLAG_NOCOPY)); + + return bad; +} /** Syncs customdata layers with internal bmesh, but ignores deleted layers. */ -void BKE_sculptsession_sync_attributes(struct Object *ob, struct Mesh *me) +static void get_synced_attributes(CustomData *src_data, + CustomData *dst_data, + Vector &r_new, + Vector &r_kill) +{ + int badmask = CD_MASK_ORIGINDEX | CD_MASK_ORIGSPACE | CD_MASK_MFACE; + + for (int i : IndexRange(src_data->totlayer)) { + CustomDataLayer *src_layer = src_data->layers + i; + + if (sync_ignore_layer(src_layer)) { + continue; + } + + if (CustomData_get_named_layer_index( + dst_data, eCustomDataType(src_layer->type), src_layer->name) == -1) + { + r_new.append(*src_layer); + } + } + + for (int i : IndexRange(dst_data->totlayer)) { + CustomDataLayer *dst_layer = dst_data->layers + i; + + if (sync_ignore_layer(dst_layer)) { + continue; + } + + if (CustomData_get_named_layer_index( + src_data, eCustomDataType(dst_layer->type), dst_layer->name) == -1) + { + r_kill.append(*dst_layer); + } + } +} + +static bool sync_attribute_actives(CustomData *src_data, CustomData *dst_data) +{ + bool modified = false; + + bool donemap[CD_NUMTYPES] = {0}; + + for (int i : IndexRange(src_data->totlayer)) { + CustomDataLayer *src_layer = src_data->layers + i; + eCustomDataType type = eCustomDataType(src_layer->type); + + if (sync_ignore_layer(src_layer) || donemap[int(type)]) { + continue; + } + + /* Only do first layers of each type, active refs will be propagated to + * the other ones later. + */ + donemap[src_layer->type] = true; + + /* Find first layer of type. */ + int baseidx = CustomData_get_layer_index(dst_data, type); + + if (baseidx < 0) { + modified |= true; + continue; + } + + CustomDataLayer *dst_layer = dst_data->layers + baseidx; + + int idx = CustomData_get_named_layer_index(dst_data, type, src_layer[src_layer->active].name); + if (idx >= 0) { + modified |= idx - baseidx != dst_layer->active; + dst_layer->active = idx - baseidx; + } + else { + modified |= dst_layer->active != 0; + dst_layer->active = 0; + } + + idx = CustomData_get_named_layer_index(dst_data, type, src_layer[src_layer->active_rnd].name); + if (idx >= 0) { + modified |= idx - baseidx != dst_layer->active_rnd; + dst_layer->active_rnd = idx - baseidx; + } + else { + modified |= dst_layer->active_rnd != 0; + dst_layer->active_rnd = 0; + } + + idx = CustomData_get_named_layer_index(dst_data, type, src_layer[src_layer->active_mask].name); + if (idx >= 0) { + modified |= idx - baseidx != dst_layer->active_mask; + dst_layer->active_mask = idx - baseidx; + } + else { + modified |= dst_layer->active_mask != 0; + dst_layer->active_mask = 0; + } + + idx = CustomData_get_named_layer_index( + dst_data, type, src_layer[src_layer->active_clone].name); + if (idx >= 0) { + modified |= idx - baseidx != dst_layer->active_clone; + dst_layer->active_clone = idx - baseidx; + } + else { + modified |= dst_layer->active_clone != 0; + dst_layer->active_clone = 0; + } + } + + if (modified) { + CustomDataLayer *base_layer = dst_data->layers; + + for (int i = 0; i < dst_data->totlayer; i++) { + CustomDataLayer *dst_layer = dst_data->layers; + + if (dst_layer->type != base_layer->type) { + base_layer = dst_layer; + } + + dst_layer->active = base_layer->active; + dst_layer->active_clone = base_layer->active_clone; + dst_layer->active_mask = base_layer->active_mask; + dst_layer->active_rnd = base_layer->active_rnd; + } + } + + return modified; +} + +void BKE_sculptsession_sync_attributes(struct Object *ob, struct Mesh *me, bool load_to_mesh) { SculptSession *ss = ob->sculpt; @@ -2913,139 +3070,99 @@ void BKE_sculptsession_sync_attributes(struct Object *ob, struct Mesh *me) return; } else if (!ss->bm) { - BKE_sculptsession_update_attr_refs(ob); + if (!load_to_mesh) { + BKE_sculptsession_update_attr_refs(ob); + } return; } bool modified = false; + BMesh *bm = ss->bm; - CustomData *cd1[4] = {&me->vdata, &me->edata, &me->ldata, &me->pdata}; - CustomData *cd2[4] = {&bm->vdata, &bm->edata, &bm->ldata, &bm->pdata}; - int badmask = CD_MASK_ORIGINDEX | CD_MASK_ORIGSPACE | CD_MASK_MFACE; + CustomData *cdme[4] = {&me->vdata, &me->edata, &me->ldata, &me->pdata}; + CustomData *cdbm[4] = {&bm->vdata, &bm->edata, &bm->ldata, &bm->pdata}; - for (int i = 0; i < 4; i++) { - Vector newlayers; + if (!load_to_mesh) { + for (int i = 0; i < 4; i++) { + Vector new_layers, kill_layers; - CustomData *data1 = cd1[i]; - CustomData *data2 = cd2[i]; + get_synced_attributes(cdme[i], cdbm[i], new_layers, kill_layers); - if (!data1->layers) { - modified |= data2->layers != nullptr; - continue; + for (CustomDataLayer &layer : kill_layers) { + BM_data_layer_free_named(bm, cdbm[i], layer.name); + modified = true; + } + + Vector new_bm_layers; + for (CustomDataLayer &layer : new_layers) { + new_bm_layers.append({layer.type, layer.name, layer.flag}); + } + + BM_data_layers_ensure(bm, cdbm[i], new_bm_layers.data(), new_bm_layers.size()); + + modified |= new_bm_layers.size() > 0; + modified |= sync_attribute_actives(cdme[i], cdbm[i]); + } + } + else { + for (int i = 0; i < 4; i++) { + Vector new_layers, kill_layers; + + get_synced_attributes(cdbm[i], cdme[i], new_layers, kill_layers); + + int totelem; + switch (i) { + case 0: + totelem = me->totvert; + break; + case 1: + totelem = me->totedge; + break; + case 2: + totelem = me->totloop; + break; + case 3: + totelem = me->totpoly; + break; + } + + for (CustomDataLayer &layer : new_layers) { + CustomData_add_layer_named( + cdme[i], eCustomDataType(layer.type), CD_CONSTRUCT, totelem, layer.name); + modified = true; + } + + for (CustomDataLayer &layer : kill_layers) { + CustomData_free_layer_named(cdme[i], layer.name, totelem); + modified = true; + } + + modified |= sync_attribute_actives(cdbm[i], cdme[i]); } - for (int j = 0; j < data1->totlayer; j++) { - CustomDataLayer *cl1 = data1->layers + j; - - if (sculpt_attribute_stored_in_bmesh_builtin(cl1->name)) { - continue; - } - if ((1 << cl1->type) & badmask) { - continue; - } - - int idx = CustomData_get_named_layer_index(data2, eCustomDataType(cl1->type), cl1->name); - if (idx < 0) { - newlayers.append(cl1); - } + if (me->default_color_attribute && + !BKE_id_attributes_color_find(&me->id, me->active_color_attribute)) + { + MEM_SAFE_FREE(me->active_color_attribute); } - - for (CustomDataLayer *layer : newlayers) { - BM_data_layer_add_named(bm, data2, layer->type, layer->name); - modified = true; - } - - /* sync various ids */ - for (int j = 0; j < data1->totlayer; j++) { - CustomDataLayer *cl1 = data1->layers + j; - - if (sculpt_attribute_stored_in_bmesh_builtin(cl1->name)) { - continue; - } - if ((1 << cl1->type) & badmask) { - continue; - } - - int idx = CustomData_get_named_layer_index(data2, eCustomDataType(cl1->type), cl1->name); - - if (idx == -1) { - continue; - } - - CustomDataLayer *cl2 = data2->layers + idx; - - cl2->anonymous_id = cl1->anonymous_id; - cl2->uid = cl1->uid; - } - - bool typemap[CD_NUMTYPES] = {0}; - - for (int j = 0; j < data1->totlayer; j++) { - CustomDataLayer *cl1 = data1->layers + j; - - if (sculpt_attribute_stored_in_bmesh_builtin(cl1->name)) { - continue; - } - if ((1 << cl1->type) & badmask) { - continue; - } - - if (typemap[cl1->type]) { - continue; - } - - typemap[cl1->type] = true; - - // find first layer - int baseidx = CustomData_get_layer_index(data2, eCustomDataType(cl1->type)); - - if (baseidx < 0) { - modified |= true; - continue; - } - - CustomDataLayer *cl2 = data2->layers + baseidx; - - int idx = CustomData_get_named_layer_index( - data2, eCustomDataType(cl1->type), cl1[cl1->active].name); - if (idx >= 0) { - modified |= idx - baseidx != cl2->active; - cl2->active = idx - baseidx; - } - - idx = CustomData_get_named_layer_index( - data2, eCustomDataType(cl1->type), cl1[cl1->active_rnd].name); - if (idx >= 0) { - modified |= idx - baseidx != cl2->active_rnd; - cl2->active_rnd = idx - baseidx; - } - - idx = CustomData_get_named_layer_index( - data2, eCustomDataType(cl1->type), cl1[cl1->active_mask].name); - if (idx >= 0) { - modified |= idx - baseidx != cl2->active_mask; - cl2->active_mask = idx - baseidx; - } - - idx = CustomData_get_named_layer_index( - data2, eCustomDataType(cl1->type), cl1[cl1->active_clone].name); - if (idx >= 0) { - modified |= idx - baseidx != cl2->active_clone; - cl2->active_clone = idx - baseidx; - } + if (me->default_color_attribute && + !BKE_id_attributes_color_find(&me->id, me->default_color_attribute)) + { + MEM_SAFE_FREE(me->default_color_attribute); } } - if (modified && ss->bm) { - CustomData_regen_active_refs(&ss->bm->vdata); - CustomData_regen_active_refs(&ss->bm->edata); - CustomData_regen_active_refs(&ss->bm->ldata); - CustomData_regen_active_refs(&ss->bm->pdata); + if (modified) { + printf("%s: Attribute layout changed! %s\n", + __func__, + load_to_mesh ? "Loading to mesh" : "Loading from mesh"); } - BKE_sculptsession_update_attr_refs(ob); -} + if (!load_to_mesh) { + BKE_sculptsession_update_attr_refs(ob); + } +}; BMesh *BKE_sculptsession_empty_bmesh_create() { @@ -3216,6 +3333,10 @@ static bool sculpt_attribute_create(SculptSession *ss, if (!permanent) { cdata->layers[index].flag |= CD_FLAG_TEMPORARY | CD_FLAG_NOCOPY; } + else { + /* Push attribute into the base mesh. */ + BKE_sculptsession_sync_attributes(ob, static_cast(ob->data), true); + } out->data = nullptr; out->layer = cdata->layers + index; @@ -3298,6 +3419,15 @@ static bool sculpt_attr_update(Object *ob, SculptAttribute *attr) attr->data = cdata->layers[layer_index].data; } } + + if (layer_index != -1) { + if (attr->params.nocopy) { + cdata->layers[layer_index].flag |= CD_FLAG_ELEM_NOCOPY; + } + if (attr->params.nointerp) { + cdata->layers[layer_index].flag |= CD_FLAG_ELEM_NOINTERP; + } + } } PBVHType pbvhtype; @@ -3523,6 +3653,9 @@ static void sculptsession_bmesh_add_layers(Object *ob) SculptSession *ss = ob->sculpt; SculptAttributeParams params = {0}; + params.nocopy = true; + params.nointerp = true; + if (!ss->attrs.face_areas) { SculptAttributeParams params = {0}; ss->attrs.face_areas = sculpt_attribute_ensure_ex(ob, diff --git a/source/blender/blenkernel/intern/pbvh.cc b/source/blender/blenkernel/intern/pbvh.cc index 08c629e436c..893d558c4d6 100644 --- a/source/blender/blenkernel/intern/pbvh.cc +++ b/source/blender/blenkernel/intern/pbvh.cc @@ -4308,13 +4308,13 @@ void update_vert_boundary_faces(int *boundary_flags, } } // namespace blender::bke::pbvh -void BKE_pbvh_ignore_uvs_set(PBVH *pbvh, bool value) +void BKE_pbvh_reproject_smooth_set(PBVH *pbvh, bool value) { - if (!!(pbvh->flags & PBVH_IGNORE_UVS) == value) { + if (!!(pbvh->flags & PBVH_IGNORE_UVS) == !value) { return; // no change } - if (value) { + if (!value) { pbvh->flags |= PBVH_IGNORE_UVS; } else { diff --git a/source/blender/blenkernel/intern/pbvh_bmesh.cc b/source/blender/blenkernel/intern/pbvh_bmesh.cc index 5f70f0c3649..65fe70b90c9 100644 --- a/source/blender/blenkernel/intern/pbvh_bmesh.cc +++ b/source/blender/blenkernel/intern/pbvh_bmesh.cc @@ -668,7 +668,7 @@ void pbvh_bmesh_vert_remove(PBVH *pbvh, BMVert *v) BM_FACES_OF_VERT_ITER_END; } -ATTR_NO_OPT void pbvh_bmesh_face_remove( +void pbvh_bmesh_face_remove( PBVH *pbvh, BMFace *f, bool log_face, bool check_verts, bool ensure_ownership_transfer) { PBVHNode *f_node = pbvh_bmesh_node_from_face(pbvh, f); @@ -1559,42 +1559,24 @@ static void pbvh_update_normals_task_cb(void *__restrict userdata, # undef NORMAL_VERT_BAD #endif #define NORMAL_VERT_BAD(v) \ - (!v->e || BM_ELEM_CD_GET_INT((v), cd_vert_node_offset) != node_nr || \ - ((*BM_ELEM_CD_PTR(v, data->cd_flag)) & SCULPTFLAG_PBVH_BOUNDARY)) + (!(v)->e || BM_ELEM_CD_GET_INT((v), cd_vert_node_offset) != node_nr || \ + ((*BM_ELEM_CD_PTR((v), data->cd_flag)) & SCULPTFLAG_PBVH_BOUNDARY)) - const char tag = BM_ELEM_TAG_ALT; + for (BMVert *v : *node->bm_unique_verts) { + PBVH_CHECK_NAN(v->no); + + if (NORMAL_VERT_BAD(v)) { + data->border_verts.append(v); + } + + zero_v3(v->no); + } for (BMVert *v : *node->bm_other_verts) { PBVH_CHECK_NAN(v->no); if (NORMAL_VERT_BAD(v)) { - v->head.hflag |= tag; data->border_verts.append(v); - continue; - } - - v->head.hflag &= ~tag; - - BMEdge *e = v->e; - do { - BMLoop *l = e->l; - - if (!l) { - continue; - } - - do { - if (BM_ELEM_CD_GET_INT(l->f, cd_face_node_offset) != node_nr) { - v->head.hflag |= tag; - goto loop_exit; - } - } while ((l = l->radial_next) != e->l); - } while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e); - loop_exit: - - if (v->head.hflag & tag) { - data->border_verts.append(v); - continue; } zero_v3(v->no); @@ -1609,7 +1591,7 @@ static void pbvh_update_normals_task_cb(void *__restrict userdata, do { PBVH_CHECK_NAN(l->v->no); - if (BM_ELEM_CD_GET_INT(l->v, cd_vert_node_offset) == node_nr && !(l->v->head.hflag & tag)) { + if (!NORMAL_VERT_BAD(l->v)) { add_v3_v3(l->v->no, f->no); } } while ((l = l->next) != f->l_first); @@ -1618,13 +1600,7 @@ static void pbvh_update_normals_task_cb(void *__restrict userdata, for (BMVert *v : *node->bm_unique_verts) { PBVH_CHECK_NAN(v->no); - if (dot_v3v3(v->no, v->no) == 0.0f) { - data->border_verts.append(v); - - continue; - } - - if (!(v->head.hflag & tag)) { + if (!NORMAL_VERT_BAD(v)) { normalize_v3(v->no); } } @@ -1640,10 +1616,10 @@ void pbvh_bmesh_normals_update(PBVH *pbvh, Span nodes) for (int i : nodes.index_range()) { datas[i].node = nodes[i]; + datas[i].node_nr = nodes[i] - pbvh->nodes; datas[i].cd_flag = pbvh->cd_flag; datas[i].cd_vert_node_offset = pbvh->cd_vert_node_offset; datas[i].cd_face_node_offset = pbvh->cd_face_node_offset; - datas[i].node_nr = nodes[i] - pbvh->nodes; BKE_pbvh_bmesh_check_tris(pbvh, nodes[i]); } @@ -1651,66 +1627,34 @@ void pbvh_bmesh_normals_update(PBVH *pbvh, Span nodes) BKE_pbvh_parallel_range_settings(&settings, true, nodes.size()); BLI_task_parallel_range(0, nodes.size(), datas.data(), pbvh_update_normals_task_cb, &settings); - /* not sure it's worth calling BM_mesh_elem_index_ensure here */ -#if 0 - BLI_bitmap *visit = BLI_BITMAP_NEW(bm->totvert, "visit"); - BM_mesh_elem_index_ensure(bm, BM_VERT); -#endif - - for (int i = 0; i < datas.size(); i++) { - UpdateNormalsTaskData *data = &datas[i]; - -#if 0 - printf("%.2f%% : %d %d\n", - 100.0f * (float)data->tot_border_verts / (float)data->node->bm_unique_verts->length, - data->tot_border_verts, - data->node->bm_unique_verts->length); -#endif - - for (int j = 0; j < data->border_verts.size(); j++) { - BMVert *v = data->border_verts[j]; - + for (UpdateNormalsTaskData &data : datas) { + for (BMVert *v : data.border_verts) { if (BM_elem_is_free((BMElem *)v, BM_VERT)) { printf("%s: error, v was freed!\n", __func__); continue; } -#if 0 - if (v->head.index < 0 || v->head.index >= bm->totvert) { - printf("%s: error, v->head.index was out of bounds!\n", __func__); - continue; - } - - if (BLI_BITMAP_TEST(visit, v->head.index)) { - continue; - } - - BLI_BITMAP_ENABLE(visit, v->head.index); -#endif - - // manual iteration - BMEdge *e = v->e; - - if (!e) { + if (!v->e || !v->e->l) { continue; } zero_v3(v->no); + BMEdge *e = v->e; do { - if (e->l) { - add_v3_v3(v->no, e->l->f->no); + BMLoop *l = e->l; + if (!l) { + continue; } - e = BM_DISK_EDGE_NEXT(e, v); - } while (e != v->e); + + do { + add_v3_v3(v->no, l->f->no); + } while ((l = l->radial_next) != e->l); + } while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e); normalize_v3(v->no); } } - -#if 0 - MEM_SAFE_FREE(visit); -#endif } struct FastNodeBuildInfo { @@ -2094,10 +2038,20 @@ void BKE_pbvh_update_vert_boundary(int cd_faceset_offset, bool uv_first = true; if (do_uvs) { - for (int i = 0; i < totuv; i++) { - CustomDataLayer *layer = ldata->layers + base_uv_idx + i; - cd_uvs[i] = layer->offset; - disjount_uv_count[i] = 0; + int base = ldata->typemap[CD_PROP_FLOAT2]; + int uv_i = 0; + + for (int i = base; i < ldata->totlayer; i++) { + CustomDataLayer *layer = ldata->layers + i; + if (layer->type != CD_PROP_FLOAT2) { + break; + } + + if (!(layer->flag & CD_FLAG_TEMPORARY)) { + cd_uvs[uv_i] = layer->offset; + disjount_uv_count[uv_i] = 0; + uv_i++; + } } } @@ -2526,7 +2480,7 @@ void BKE_pbvh_set_bm_log(PBVH *pbvh, BMLog *log) namespace blender::bke::dyntopo { bool remesh_topology_nodes(blender::bke::dyntopo::BrushTester *brush_tester, - SculptSession *ss, + Object *ob, PBVH *pbvh, bool (*searchcb)(PBVHNode *node, void *data), void (*undopush)(PBVHNode *node, void *data), @@ -2562,7 +2516,7 @@ bool remesh_topology_nodes(blender::bke::dyntopo::BrushTester *brush_tester, } modified = remesh_topology(brush_tester, - ss, + ob, pbvh, mode, use_frontface, @@ -3343,7 +3297,7 @@ static void pbvh_bmesh_compact_tree(PBVH *bvh) } /* Prunes leaf nodes that are too small or degenerate. */ -ATTR_NO_OPT static void pbvh_bmesh_balance_tree(PBVH *pbvh) +static void pbvh_bmesh_balance_tree(PBVH *pbvh) { float *overlaps = MEM_cnew_array(pbvh->totnode, "overlaps"); PBVHNode **parentmap = MEM_cnew_array(pbvh->totnode, "parentmap"); @@ -3819,7 +3773,15 @@ void BKE_pbvh_update_offsets(PBVH *pbvh, pbvh->cd_faceset_offset = CustomData_get_offset_named( &pbvh->header.bm->pdata, CD_PROP_INT32, ".sculpt_face_set"); - pbvh->totuv = CustomData_number_of_layers(&pbvh->header.bm->ldata, CD_PROP_FLOAT2); + CustomData *ldata = &pbvh->header.bm->ldata; + pbvh->totuv = 0; + for (int i : IndexRange(ldata->totlayer)) { + CustomDataLayer &layer = ldata->layers[i]; + if (layer.type == CD_PROP_FLOAT2 && !(layer.flag & CD_FLAG_TEMPORARY)) { + pbvh->totuv++; + } + } + pbvh->cd_boundary_flag = cd_boundary_flag; pbvh->cd_curvature_dir = cd_curvature_dir; diff --git a/source/blender/blenkernel/intern/pbvh_intern.hh b/source/blender/blenkernel/intern/pbvh_intern.hh index 245654b2416..8fe426bf6d5 100644 --- a/source/blender/blenkernel/intern/pbvh_intern.hh +++ b/source/blender/blenkernel/intern/pbvh_intern.hh @@ -447,7 +447,7 @@ BLI_INLINE bool pbvh_check_vert_boundary(PBVH *pbvh, struct BMVert *v) v, &pbvh->header.bm->ldata, pbvh->flags & PBVH_IGNORE_UVS ? 0 : pbvh->totuv, - pbvh->flags & PBVH_IGNORE_UVS, + !(pbvh->flags & PBVH_IGNORE_UVS), pbvh->sharp_angle_limit); return true; } diff --git a/source/blender/blenlib/BLI_heap_minmax.hh b/source/blender/blenlib/BLI_heap_minmax.hh index 699fa51e063..b8e96925d4a 100644 --- a/source/blender/blenlib/BLI_heap_minmax.hh +++ b/source/blender/blenlib/BLI_heap_minmax.hh @@ -200,7 +200,7 @@ class MinMaxHeap { ~MinMaxHeap() {} - ATTR_NO_OPT bool valid_recurse(int i) + bool valid_recurse(int i) { bool ret = true; @@ -237,7 +237,7 @@ class MinMaxHeap { return ret; } - ATTR_NO_OPT bool is_valid() + bool is_valid() { if (nodes.size() == 0) { @@ -252,7 +252,7 @@ class MinMaxHeap { return HeapValueIter(nodes); } - ATTR_NO_OPT MinMaxHeapNode *insert(float weight, Value value) + MinMaxHeapNode *insert(float weight, Value value) { MinMaxHeapNode *node = heap_make_node(); diff --git a/source/blender/bmesh/intern/bmesh_core.c b/source/blender/bmesh/intern/bmesh_core.c index a48df915730..18992a197a2 100644 --- a/source/blender/bmesh/intern/bmesh_core.c +++ b/source/blender/bmesh/intern/bmesh_core.c @@ -2406,7 +2406,7 @@ static void bmesh_kernel_check_val3_vert(BMesh *bm, BMEdge *e) * +-+-+-+ +-+-+-+ * */ -ATTR_NO_OPT BMVert *bmesh_kernel_join_vert_kill_edge( +BMVert *bmesh_kernel_join_vert_kill_edge( BMesh *bm, BMEdge *e, BMVert *v_kill, const bool do_del, const bool combine_flags) { BMVert *v_conn = BM_edge_other_vert(e, v_kill); diff --git a/source/blender/bmesh/intern/bmesh_interp.c b/source/blender/bmesh/intern/bmesh_interp.c index 0a6a1152b02..c7d43cfb4bb 100644 --- a/source/blender/bmesh/intern/bmesh_interp.c +++ b/source/blender/bmesh/intern/bmesh_interp.c @@ -741,29 +741,6 @@ void BM_loop_interp_from_face( /* interpolate */ interp_weights_poly_v2(w, cos_2d, f_src->len, co); - /* Clamp excessively negative weights to zero. Prevents numerical - * instability when interpolating UVs. - */ - float totw = 0.0f; - for (int i = 0; i < f_src->len; i++) { - if (w[i] < -5.0) { - w[i] = 0.0f; - } - - totw += w[i]; - } - - if (fabsf(totw - 1.0f) > FLT_EPSILON * 100) { - for (int i = 0; i < f_src->len; i++) { - w[i] /= totw; - } - } - else if (totw == 0.0f) { /* Use uniform weights in this case.*/ - for (int i = 0; i < f_src->len; i++) { - w[i] = 1.0f / (float)f_src->len; - } - } - CustomData_bmesh_interp(&bm->ldata, blocks, w, NULL, f_src->len, l_dst->head.data); if (do_vertex) { CustomData_bmesh_interp(&bm->vdata, vblocks, w, NULL, f_src->len, l_dst->v->head.data); diff --git a/source/blender/bmesh/intern/bmesh_log.c b/source/blender/bmesh/intern/bmesh_log.c deleted file mode 100644 index e44bf2d4b09..00000000000 --- a/source/blender/bmesh/intern/bmesh_log.c +++ /dev/null @@ -1,1033 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ - -/** \file - * \ingroup bmesh - * - * The BMLog is an interface for storing undo/redo steps as a BMesh is - * modified. It only stores changes to the BMesh, not full copies. - * - * Currently it supports the following types of changes: - * - * - Adding and removing vertices - * - Adding and removing faces - * - Moving vertices - * - Setting vertex paint-mask values - * - Setting vertex hflags - */ - -#include "MEM_guardedalloc.h" - -#include "BLI_ghash.h" -#include "BLI_listbase.h" -#include "BLI_math.h" -#include "BLI_mempool.h" -#include "BLI_utildefines.h" - -#include "BKE_customdata.h" - -#include "bmesh.h" -#include "bmesh_log.h" -#include "range_tree.h" - -#include "BLI_strict_flags.h" - -struct BMLogEntry { - struct BMLogEntry *next, *prev; - - /* The following #GHash members map from an element ID to one of the log types above. */ - - /** Elements that were in the previous entry, but have been deleted. */ - GHash *deleted_verts; - GHash *deleted_faces; - /** Elements that were not in the previous entry, but are in the result of this entry. */ - GHash *added_verts; - GHash *added_faces; - - /** Vertices whose coordinates, mask value, or hflag have changed. */ - GHash *modified_verts; - GHash *modified_faces; - - BLI_mempool *pool_verts; - BLI_mempool *pool_faces; - - /** - * This is only needed for dropping BMLogEntries while still in - * dynamic-topology mode, as that should release vert/face IDs - * back to the BMLog but no BMLog pointer is available at that time. - * - * This field is not guaranteed to be valid, any use of it should - * check for NULL. - */ - BMLog *log; -}; - -struct BMLog { - /** Tree of free IDs */ - struct RangeTreeUInt *unused_ids; - - /** - * Mapping from unique IDs to vertices and faces - * - * Each vertex and face in the log gets a unique `uint` - * assigned. That ID is taken from the set managed by the - * unused_ids range tree. - * - * The ID is needed because element pointers will change as they - * are created and deleted. - */ - GHash *id_to_elem; - GHash *elem_to_id; - - /** All #BMLogEntrys, ordered from earliest to most recent. */ - ListBase entries; - - /** - * The current log entry from entries list - * - * If null, then the original mesh from before any of the log - * entries is current (i.e. there is nothing left to undo.) - * - * If equal to the last entry in the entries list, then all log - * entries have been applied (i.e. there is nothing left to redo.) - */ - BMLogEntry *current_entry; -}; - -typedef struct { - float co[3]; - float no[3]; - char hflag; - float mask; -} BMLogVert; - -typedef struct { - uint v_ids[3]; - char hflag; -} BMLogFace; - -/************************* Get/set element IDs ************************/ - -/* bypass actual hashing, the keys don't overlap */ -#define logkey_hash BLI_ghashutil_inthash_p_simple -#define logkey_cmp BLI_ghashutil_intcmp - -/* Get the vertex's unique ID from the log */ -static uint bm_log_vert_id_get(BMLog *log, BMVert *v) -{ - BLI_assert(BLI_ghash_haskey(log->elem_to_id, v)); - return POINTER_AS_UINT(BLI_ghash_lookup(log->elem_to_id, v)); -} - -/* Set the vertex's unique ID in the log */ -static void bm_log_vert_id_set(BMLog *log, BMVert *v, uint id) -{ - void *vid = POINTER_FROM_UINT(id); - - BLI_ghash_reinsert(log->id_to_elem, vid, v, NULL, NULL); - BLI_ghash_reinsert(log->elem_to_id, v, vid, NULL, NULL); -} - -/* Get a vertex from its unique ID */ -static BMVert *bm_log_vert_from_id(BMLog *log, uint id) -{ - void *key = POINTER_FROM_UINT(id); - BLI_assert(BLI_ghash_haskey(log->id_to_elem, key)); - return BLI_ghash_lookup(log->id_to_elem, key); -} - -/* Get the face's unique ID from the log */ -static uint bm_log_face_id_get(BMLog *log, BMFace *f) -{ - BLI_assert(BLI_ghash_haskey(log->elem_to_id, f)); - return POINTER_AS_UINT(BLI_ghash_lookup(log->elem_to_id, f)); -} - -/* Set the face's unique ID in the log */ -static void bm_log_face_id_set(BMLog *log, BMFace *f, uint id) -{ - void *fid = POINTER_FROM_UINT(id); - - BLI_ghash_reinsert(log->id_to_elem, fid, f, NULL, NULL); - BLI_ghash_reinsert(log->elem_to_id, f, fid, NULL, NULL); -} - -/* Get a face from its unique ID */ -static BMFace *bm_log_face_from_id(BMLog *log, uint id) -{ - void *key = POINTER_FROM_UINT(id); - BLI_assert(BLI_ghash_haskey(log->id_to_elem, key)); - return BLI_ghash_lookup(log->id_to_elem, key); -} - -/************************ BMLogVert / BMLogFace ***********************/ - -/* Get a vertex's paint-mask value - * - * Returns zero if no paint-mask layer is present */ -static float vert_mask_get(BMVert *v, const int cd_vert_mask_offset) -{ - if (cd_vert_mask_offset != -1) { - return BM_ELEM_CD_GET_FLOAT(v, cd_vert_mask_offset); - } - return 0.0f; -} - -/* Set a vertex's paint-mask value - * - * Has no effect is no paint-mask layer is present */ -static void vert_mask_set(BMVert *v, const float new_mask, const int cd_vert_mask_offset) -{ - if (cd_vert_mask_offset != -1) { - BM_ELEM_CD_SET_FLOAT(v, cd_vert_mask_offset, new_mask); - } -} - -/* Update a BMLogVert with data from a BMVert */ -static void bm_log_vert_bmvert_copy(BMLogVert *lv, BMVert *v, const int cd_vert_mask_offset) -{ - copy_v3_v3(lv->co, v->co); - copy_v3_v3(lv->no, v->no); - lv->mask = vert_mask_get(v, cd_vert_mask_offset); - lv->hflag = v->head.hflag; -} - -/* Allocate and initialize a BMLogVert */ -static BMLogVert *bm_log_vert_alloc(BMLog *log, BMVert *v, const int cd_vert_mask_offset) -{ - BMLogEntry *entry = log->current_entry; - BMLogVert *lv = BLI_mempool_alloc(entry->pool_verts); - - bm_log_vert_bmvert_copy(lv, v, cd_vert_mask_offset); - - return lv; -} - -/* Allocate and initialize a BMLogFace */ -static BMLogFace *bm_log_face_alloc(BMLog *log, BMFace *f) -{ - BMLogEntry *entry = log->current_entry; - BMLogFace *lf = BLI_mempool_alloc(entry->pool_faces); - BMVert *v[3]; - - BLI_assert(f->len == 3); - - // BM_iter_as_array(NULL, BM_VERTS_OF_FACE, f, (void **)v, 3); - BM_face_as_array_vert_tri(f, v); - - lf->v_ids[0] = bm_log_vert_id_get(log, v[0]); - lf->v_ids[1] = bm_log_vert_id_get(log, v[1]); - lf->v_ids[2] = bm_log_vert_id_get(log, v[2]); - - lf->hflag = f->head.hflag; - return lf; -} - -/************************ Helpers for undo/redo ***********************/ - -static void bm_log_verts_unmake(BMesh *bm, BMLog *log, GHash *verts) -{ - const int cd_vert_mask_offset = CustomData_get_offset(&bm->vdata, CD_PAINT_MASK); - - GHashIterator gh_iter; - GHASH_ITER (gh_iter, verts) { - void *key = BLI_ghashIterator_getKey(&gh_iter); - BMLogVert *lv = BLI_ghashIterator_getValue(&gh_iter); - uint id = POINTER_AS_UINT(key); - BMVert *v = bm_log_vert_from_id(log, id); - - /* Ensure the log has the final values of the vertex before - * deleting it */ - bm_log_vert_bmvert_copy(lv, v, cd_vert_mask_offset); - - BM_vert_kill(bm, v); - } -} - -static void bm_log_faces_unmake(BMesh *bm, BMLog *log, GHash *faces) -{ - GHashIterator gh_iter; - GHASH_ITER (gh_iter, faces) { - void *key = BLI_ghashIterator_getKey(&gh_iter); - uint id = POINTER_AS_UINT(key); - BMFace *f = bm_log_face_from_id(log, id); - BMEdge *e_tri[3]; - BMLoop *l_iter; - int i; - - l_iter = BM_FACE_FIRST_LOOP(f); - for (i = 0; i < 3; i++, l_iter = l_iter->next) { - e_tri[i] = l_iter->e; - } - - /* Remove any unused edges */ - BM_face_kill(bm, f); - for (i = 0; i < 3; i++) { - if (BM_edge_is_wire(e_tri[i])) { - BM_edge_kill(bm, e_tri[i]); - } - } - } -} - -static void bm_log_verts_restore(BMesh *bm, BMLog *log, GHash *verts) -{ - const int cd_vert_mask_offset = CustomData_get_offset(&bm->vdata, CD_PAINT_MASK); - - GHashIterator gh_iter; - GHASH_ITER (gh_iter, verts) { - void *key = BLI_ghashIterator_getKey(&gh_iter); - BMLogVert *lv = BLI_ghashIterator_getValue(&gh_iter); - BMVert *v = BM_vert_create(bm, lv->co, NULL, BM_CREATE_NOP); - vert_mask_set(v, lv->mask, cd_vert_mask_offset); - v->head.hflag = lv->hflag; - copy_v3_v3(v->no, lv->no); - bm_log_vert_id_set(log, v, POINTER_AS_UINT(key)); - } -} - -static void bm_log_faces_restore(BMesh *bm, BMLog *log, GHash *faces) -{ - GHashIterator gh_iter; - const int cd_face_sets = CustomData_get_offset_named( - &bm->pdata, CD_PROP_INT32, ".sculpt_face_set"); - - GHASH_ITER (gh_iter, faces) { - void *key = BLI_ghashIterator_getKey(&gh_iter); - BMLogFace *lf = BLI_ghashIterator_getValue(&gh_iter); - BMVert *v[3] = { - bm_log_vert_from_id(log, lf->v_ids[0]), - bm_log_vert_from_id(log, lf->v_ids[1]), - bm_log_vert_from_id(log, lf->v_ids[2]), - }; - BMFace *f; - - f = BM_face_create_verts(bm, v, 3, NULL, BM_CREATE_NOP, true); - f->head.hflag = lf->hflag; - bm_log_face_id_set(log, f, POINTER_AS_UINT(key)); - - /* Ensure face sets have valid values. Fixes #80174. */ - if (cd_face_sets != -1) { - BM_ELEM_CD_SET_INT(f, cd_face_sets, 1); - } - } -} - -static void bm_log_vert_values_swap(BMesh *bm, BMLog *log, GHash *verts) -{ - const int cd_vert_mask_offset = CustomData_get_offset(&bm->vdata, CD_PAINT_MASK); - - GHashIterator gh_iter; - GHASH_ITER (gh_iter, verts) { - void *key = BLI_ghashIterator_getKey(&gh_iter); - BMLogVert *lv = BLI_ghashIterator_getValue(&gh_iter); - uint id = POINTER_AS_UINT(key); - BMVert *v = bm_log_vert_from_id(log, id); - float mask; - - swap_v3_v3(v->co, lv->co); - swap_v3_v3(v->no, lv->no); - SWAP(char, v->head.hflag, lv->hflag); - mask = lv->mask; - lv->mask = vert_mask_get(v, cd_vert_mask_offset); - vert_mask_set(v, mask, cd_vert_mask_offset); - } -} - -static void bm_log_face_values_swap(BMLog *log, GHash *faces) -{ - GHashIterator gh_iter; - GHASH_ITER (gh_iter, faces) { - void *key = BLI_ghashIterator_getKey(&gh_iter); - BMLogFace *lf = BLI_ghashIterator_getValue(&gh_iter); - uint id = POINTER_AS_UINT(key); - BMFace *f = bm_log_face_from_id(log, id); - - SWAP(char, f->head.hflag, lf->hflag); - } -} - -/**********************************************************************/ - -/* Assign unique IDs to all vertices and faces already in the BMesh */ -static void bm_log_assign_ids(BMesh *bm, BMLog *log) -{ - BMIter iter; - BMVert *v; - BMFace *f; - - /* Generate vertex IDs */ - BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) { - uint id = range_tree_uint_take_any(log->unused_ids); - bm_log_vert_id_set(log, v, id); - } - - /* Generate face IDs */ - BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { - uint id = range_tree_uint_take_any(log->unused_ids); - bm_log_face_id_set(log, f, id); - } -} - -/* Allocate an empty log entry */ -static BMLogEntry *bm_log_entry_create(void) -{ - BMLogEntry *entry = MEM_callocN(sizeof(BMLogEntry), __func__); - - entry->deleted_verts = BLI_ghash_new(logkey_hash, logkey_cmp, __func__); - entry->deleted_faces = BLI_ghash_new(logkey_hash, logkey_cmp, __func__); - entry->added_verts = BLI_ghash_new(logkey_hash, logkey_cmp, __func__); - entry->added_faces = BLI_ghash_new(logkey_hash, logkey_cmp, __func__); - entry->modified_verts = BLI_ghash_new(logkey_hash, logkey_cmp, __func__); - entry->modified_faces = BLI_ghash_new(logkey_hash, logkey_cmp, __func__); - - entry->pool_verts = BLI_mempool_create(sizeof(BMLogVert), 0, 64, BLI_MEMPOOL_NOP); - entry->pool_faces = BLI_mempool_create(sizeof(BMLogFace), 0, 64, BLI_MEMPOOL_NOP); - - return entry; -} - -/* Free the data in a log entry - * - * NOTE: does not free the log entry itself. */ -static void bm_log_entry_free(BMLogEntry *entry) -{ - BLI_ghash_free(entry->deleted_verts, NULL, NULL); - BLI_ghash_free(entry->deleted_faces, NULL, NULL); - BLI_ghash_free(entry->added_verts, NULL, NULL); - BLI_ghash_free(entry->added_faces, NULL, NULL); - BLI_ghash_free(entry->modified_verts, NULL, NULL); - BLI_ghash_free(entry->modified_faces, NULL, NULL); - - BLI_mempool_destroy(entry->pool_verts); - BLI_mempool_destroy(entry->pool_faces); -} - -static void bm_log_id_ghash_retake(RangeTreeUInt *unused_ids, GHash *id_ghash) -{ - GHashIterator gh_iter; - - GHASH_ITER (gh_iter, id_ghash) { - void *key = BLI_ghashIterator_getKey(&gh_iter); - uint id = POINTER_AS_UINT(key); - - range_tree_uint_retake(unused_ids, id); - } -} - -static int uint_compare(const void *a_v, const void *b_v) -{ - const uint *a = a_v; - const uint *b = b_v; - return (*a) < (*b); -} - -/* Remap IDs to contiguous indices - * - * E.g. if the vertex IDs are (4, 1, 10, 3), the mapping will be: - * 4 -> 2 - * 1 -> 0 - * 10 -> 3 - * 3 -> 1 - */ -static GHash *bm_log_compress_ids_to_indices(uint *ids, uint totid) -{ - GHash *map = BLI_ghash_int_new_ex(__func__, totid); - uint i; - - qsort(ids, totid, sizeof(*ids), uint_compare); - - for (i = 0; i < totid; i++) { - void *key = POINTER_FROM_UINT(ids[i]); - void *val = POINTER_FROM_UINT(i); - BLI_ghash_insert(map, key, val); - } - - return map; -} - -/* Release all ID keys in id_ghash */ -static void bm_log_id_ghash_release(BMLog *log, GHash *id_ghash) -{ - GHashIterator gh_iter; - - GHASH_ITER (gh_iter, id_ghash) { - void *key = BLI_ghashIterator_getKey(&gh_iter); - uint id = POINTER_AS_UINT(key); - range_tree_uint_release(log->unused_ids, id); - } -} - -/***************************** Public API *****************************/ - -BMLog *BM_log_create(BMesh *bm) -{ - BMLog *log = MEM_callocN(sizeof(*log), __func__); - const uint reserve_num = (uint)(bm->totvert + bm->totface); - - log->unused_ids = range_tree_uint_alloc(0, (uint)-1); - log->id_to_elem = BLI_ghash_new_ex(logkey_hash, logkey_cmp, __func__, reserve_num); - log->elem_to_id = BLI_ghash_ptr_new_ex(__func__, reserve_num); - - /* Assign IDs to all existing vertices and faces */ - bm_log_assign_ids(bm, log); - - return log; -} - -void BM_log_cleanup_entry(BMLogEntry *entry) -{ - BMLog *log = entry->log; - - if (log) { - /* Take all used IDs */ - bm_log_id_ghash_retake(log->unused_ids, entry->deleted_verts); - bm_log_id_ghash_retake(log->unused_ids, entry->deleted_faces); - bm_log_id_ghash_retake(log->unused_ids, entry->added_verts); - bm_log_id_ghash_retake(log->unused_ids, entry->added_faces); - bm_log_id_ghash_retake(log->unused_ids, entry->modified_verts); - bm_log_id_ghash_retake(log->unused_ids, entry->modified_faces); - - /* delete entries to avoid releasing ids in node cleanup */ - BLI_ghash_clear(entry->deleted_verts, NULL, NULL); - BLI_ghash_clear(entry->deleted_faces, NULL, NULL); - BLI_ghash_clear(entry->added_verts, NULL, NULL); - BLI_ghash_clear(entry->added_faces, NULL, NULL); - BLI_ghash_clear(entry->modified_verts, NULL, NULL); - } -} - -BMLog *BM_log_from_existing_entries_create(BMesh *bm, BMLogEntry *entry) -{ - BMLog *log = BM_log_create(bm); - - if (entry->prev) { - log->current_entry = entry; - } - else { - log->current_entry = NULL; - } - - /* Let BMLog manage the entry list again */ - log->entries.first = log->entries.last = entry; - - { - while (entry->prev) { - entry = entry->prev; - log->entries.first = entry; - } - entry = log->entries.last; - while (entry->next) { - entry = entry->next; - log->entries.last = entry; - } - } - - for (entry = log->entries.first; entry; entry = entry->next) { - entry->log = log; - - /* Take all used IDs */ - bm_log_id_ghash_retake(log->unused_ids, entry->deleted_verts); - bm_log_id_ghash_retake(log->unused_ids, entry->deleted_faces); - bm_log_id_ghash_retake(log->unused_ids, entry->added_verts); - bm_log_id_ghash_retake(log->unused_ids, entry->added_faces); - bm_log_id_ghash_retake(log->unused_ids, entry->modified_verts); - bm_log_id_ghash_retake(log->unused_ids, entry->modified_faces); - } - - return log; -} - -void BM_log_free(BMLog *log) -{ - BMLogEntry *entry; - - if (log->unused_ids) { - range_tree_uint_free(log->unused_ids); - } - - if (log->id_to_elem) { - BLI_ghash_free(log->id_to_elem, NULL, NULL); - } - - if (log->elem_to_id) { - BLI_ghash_free(log->elem_to_id, NULL, NULL); - } - - /* Clear the BMLog references within each entry, but do not free - * the entries themselves */ - for (entry = log->entries.first; entry; entry = entry->next) { - entry->log = NULL; - } - - MEM_freeN(log); -} - -int BM_log_length(const BMLog *log) -{ - return BLI_listbase_count(&log->entries); -} - -void BM_log_mesh_elems_reorder(BMesh *bm, BMLog *log) -{ - uint *varr; - uint *farr; - - GHash *id_to_idx; - - BMIter bm_iter; - BMVert *v; - BMFace *f; - - uint i; - - /* Put all vertex IDs into an array */ - varr = MEM_mallocN(sizeof(int) * (size_t)bm->totvert, __func__); - BM_ITER_MESH_INDEX (v, &bm_iter, bm, BM_VERTS_OF_MESH, i) { - varr[i] = bm_log_vert_id_get(log, v); - } - - /* Put all face IDs into an array */ - farr = MEM_mallocN(sizeof(int) * (size_t)bm->totface, __func__); - BM_ITER_MESH_INDEX (f, &bm_iter, bm, BM_FACES_OF_MESH, i) { - farr[i] = bm_log_face_id_get(log, f); - } - - /* Create BMVert index remap array */ - id_to_idx = bm_log_compress_ids_to_indices(varr, (uint)bm->totvert); - BM_ITER_MESH_INDEX (v, &bm_iter, bm, BM_VERTS_OF_MESH, i) { - const uint id = bm_log_vert_id_get(log, v); - const void *key = POINTER_FROM_UINT(id); - const void *val = BLI_ghash_lookup(id_to_idx, key); - varr[i] = POINTER_AS_UINT(val); - } - BLI_ghash_free(id_to_idx, NULL, NULL); - - /* Create BMFace index remap array */ - id_to_idx = bm_log_compress_ids_to_indices(farr, (uint)bm->totface); - BM_ITER_MESH_INDEX (f, &bm_iter, bm, BM_FACES_OF_MESH, i) { - const uint id = bm_log_face_id_get(log, f); - const void *key = POINTER_FROM_UINT(id); - const void *val = BLI_ghash_lookup(id_to_idx, key); - farr[i] = POINTER_AS_UINT(val); - } - BLI_ghash_free(id_to_idx, NULL, NULL); - - BM_mesh_remap(bm, varr, NULL, farr); - - MEM_freeN(varr); - MEM_freeN(farr); -} - -BMLogEntry *BM_log_entry_add(BMLog *log) -{ - /* WARNING: this is now handled by the UndoSystem: BKE_UNDOSYS_TYPE_SCULPT - * freeing here causes unnecessary complications. */ - BMLogEntry *entry; -#if 0 - /* Delete any entries after the current one */ - entry = log->current_entry; - if (entry) { - BMLogEntry *next; - for (entry = entry->next; entry; entry = next) { - next = entry->next; - bm_log_entry_free(entry); - BLI_freelinkN(&log->entries, entry); - } - } -#endif - - /* Create and append the new entry */ - entry = bm_log_entry_create(); - BLI_addtail(&log->entries, entry); - entry->log = log; - log->current_entry = entry; - - return entry; -} - -void BM_log_entry_drop(BMLogEntry *entry) -{ - BMLog *log = entry->log; - - if (!log) { - /* Unlink */ - BLI_assert(!(entry->prev && entry->next)); - if (entry->prev) { - entry->prev->next = NULL; - } - else if (entry->next) { - entry->next->prev = NULL; - } - - bm_log_entry_free(entry); - MEM_freeN(entry); - return; - } - - if (!entry->prev) { - /* Release IDs of elements that are deleted by this - * entry. Since the entry is at the beginning of the undo - * stack, and it's being deleted, those elements can never be - * restored. Their IDs can go back into the pool. */ - - /* This would never happen usually since first entry of log is - * usually dyntopo enable, which, when reverted will free the log - * completely. However, it is possible have a stroke instead of - * dyntopo enable as first entry if nodes have been cleaned up - * after sculpting on a different object than A, B. - * - * The steps are: - * A dyntopo enable - sculpt - * B dyntopo enable - sculpt - undo (A objects operators get cleaned up) - * A sculpt (now A's log has a sculpt operator as first entry) - * - * Causing a cleanup at this point will call the code below, however - * this will invalidate the state of the log since the deleted vertices - * have been reclaimed already on step 2 (see BM_log_cleanup_entry) - * - * Also, design wise, a first entry should not have any deleted vertices since it - * should not have anything to delete them -from- - */ - // bm_log_id_ghash_release(log, entry->deleted_faces); - // bm_log_id_ghash_release(log, entry->deleted_verts); - } - else if (!entry->next) { - /* Release IDs of elements that are added by this entry. Since - * the entry is at the end of the undo stack, and it's being - * deleted, those elements can never be restored. Their IDs - * can go back into the pool. */ - bm_log_id_ghash_release(log, entry->added_faces); - bm_log_id_ghash_release(log, entry->added_verts); - } - else { - BLI_assert_msg(0, "Cannot drop BMLogEntry from middle"); - } - - if (log->current_entry == entry) { - log->current_entry = entry->prev; - } - - bm_log_entry_free(entry); - BLI_freelinkN(&log->entries, entry); -} - -void BM_log_undo(BMesh *bm, BMLog *log) -{ - BMLogEntry *entry = log->current_entry; - - if (entry) { - log->current_entry = entry->prev; - - /* Delete added faces and verts */ - bm_log_faces_unmake(bm, log, entry->added_faces); - bm_log_verts_unmake(bm, log, entry->added_verts); - - /* Restore deleted verts and faces */ - bm_log_verts_restore(bm, log, entry->deleted_verts); - bm_log_faces_restore(bm, log, entry->deleted_faces); - - /* Restore vertex coordinates, mask, and hflag */ - bm_log_vert_values_swap(bm, log, entry->modified_verts); - bm_log_face_values_swap(log, entry->modified_faces); - } -} - -void BM_log_redo(BMesh *bm, BMLog *log) -{ - BMLogEntry *entry = log->current_entry; - - if (!entry) { - /* Currently at the beginning of the undo stack, move to first entry */ - entry = log->entries.first; - } - else if (entry->next) { - /* Move to next undo entry */ - entry = entry->next; - } - else { - /* Currently at the end of the undo stack, nothing left to redo */ - return; - } - - log->current_entry = entry; - - if (entry) { - /* Re-delete previously deleted faces and verts */ - bm_log_faces_unmake(bm, log, entry->deleted_faces); - bm_log_verts_unmake(bm, log, entry->deleted_verts); - - /* Restore previously added verts and faces */ - bm_log_verts_restore(bm, log, entry->added_verts); - bm_log_faces_restore(bm, log, entry->added_faces); - - /* Restore vertex coordinates, mask, and hflag */ - bm_log_vert_values_swap(bm, log, entry->modified_verts); - bm_log_face_values_swap(log, entry->modified_faces); - } -} - -void BM_log_vert_before_modified(BMLog *log, BMVert *v, const int cd_vert_mask_offset) -{ - BMLogEntry *entry = log->current_entry; - BMLogVert *lv; - uint v_id = bm_log_vert_id_get(log, v); - void *key = POINTER_FROM_UINT(v_id); - void **val_p; - - /* Find or create the BMLogVert entry */ - if ((lv = BLI_ghash_lookup(entry->added_verts, key))) { - bm_log_vert_bmvert_copy(lv, v, cd_vert_mask_offset); - } - else if (!BLI_ghash_ensure_p(entry->modified_verts, key, &val_p)) { - lv = bm_log_vert_alloc(log, v, cd_vert_mask_offset); - *val_p = lv; - } -} - -void BM_log_vert_added(BMLog *log, BMVert *v, const int cd_vert_mask_offset) -{ - BMLogVert *lv; - uint v_id = range_tree_uint_take_any(log->unused_ids); - void *key = POINTER_FROM_UINT(v_id); - - bm_log_vert_id_set(log, v, v_id); - lv = bm_log_vert_alloc(log, v, cd_vert_mask_offset); - BLI_ghash_insert(log->current_entry->added_verts, key, lv); -} - -void BM_log_face_modified(BMLog *log, BMFace *f) -{ - BMLogFace *lf; - uint f_id = bm_log_face_id_get(log, f); - void *key = POINTER_FROM_UINT(f_id); - - lf = bm_log_face_alloc(log, f); - BLI_ghash_insert(log->current_entry->modified_faces, key, lf); -} - -void BM_log_face_added(BMLog *log, BMFace *f) -{ - BMLogFace *lf; - uint f_id = range_tree_uint_take_any(log->unused_ids); - void *key = POINTER_FROM_UINT(f_id); - - /* Only triangles are supported for now */ - BLI_assert(f->len == 3); - - bm_log_face_id_set(log, f, f_id); - lf = bm_log_face_alloc(log, f); - BLI_ghash_insert(log->current_entry->added_faces, key, lf); -} - -void BM_log_vert_removed(BMLog *log, BMVert *v, const int cd_vert_mask_offset) -{ - BMLogEntry *entry = log->current_entry; - uint v_id = bm_log_vert_id_get(log, v); - void *key = POINTER_FROM_UINT(v_id); - - /* if it has a key, it shouldn't be NULL */ - BLI_assert(!!BLI_ghash_lookup(entry->added_verts, key) == - !!BLI_ghash_haskey(entry->added_verts, key)); - - if (BLI_ghash_remove(entry->added_verts, key, NULL, NULL)) { - range_tree_uint_release(log->unused_ids, v_id); - } - else { - BMLogVert *lv, *lv_mod; - - lv = bm_log_vert_alloc(log, v, cd_vert_mask_offset); - BLI_ghash_insert(entry->deleted_verts, key, lv); - - /* If the vertex was modified before deletion, ensure that the - * original vertex values are stored */ - if ((lv_mod = BLI_ghash_lookup(entry->modified_verts, key))) { - (*lv) = (*lv_mod); - BLI_ghash_remove(entry->modified_verts, key, NULL, NULL); - } - } -} - -void BM_log_face_removed(BMLog *log, BMFace *f) -{ - BMLogEntry *entry = log->current_entry; - uint f_id = bm_log_face_id_get(log, f); - void *key = POINTER_FROM_UINT(f_id); - - /* if it has a key, it shouldn't be NULL */ - BLI_assert(!!BLI_ghash_lookup(entry->added_faces, key) == - !!BLI_ghash_haskey(entry->added_faces, key)); - - if (BLI_ghash_remove(entry->added_faces, key, NULL, NULL)) { - range_tree_uint_release(log->unused_ids, f_id); - } - else { - BMLogFace *lf; - - lf = bm_log_face_alloc(log, f); - BLI_ghash_insert(entry->deleted_faces, key, lf); - } -} - -void BM_log_all_added(BMesh *bm, BMLog *log) -{ - const int cd_vert_mask_offset = CustomData_get_offset(&bm->vdata, CD_PAINT_MASK); - BMIter bm_iter; - BMVert *v; - BMFace *f; - - /* avoid unnecessary resizing on initialization */ - if (BLI_ghash_len(log->current_entry->added_verts) == 0) { - BLI_ghash_reserve(log->current_entry->added_verts, (uint)bm->totvert); - } - - if (BLI_ghash_len(log->current_entry->added_faces) == 0) { - BLI_ghash_reserve(log->current_entry->added_faces, (uint)bm->totface); - } - - /* Log all vertices as newly created */ - BM_ITER_MESH (v, &bm_iter, bm, BM_VERTS_OF_MESH) { - BM_log_vert_added(log, v, cd_vert_mask_offset); - } - - /* Log all faces as newly created */ - BM_ITER_MESH (f, &bm_iter, bm, BM_FACES_OF_MESH) { - BM_log_face_added(log, f); - } -} - -void BM_log_before_all_removed(BMesh *bm, BMLog *log) -{ - const int cd_vert_mask_offset = CustomData_get_offset(&bm->vdata, CD_PAINT_MASK); - BMIter bm_iter; - BMVert *v; - BMFace *f; - - /* Log deletion of all faces */ - BM_ITER_MESH (f, &bm_iter, bm, BM_FACES_OF_MESH) { - BM_log_face_removed(log, f); - } - - /* Log deletion of all vertices */ - BM_ITER_MESH (v, &bm_iter, bm, BM_VERTS_OF_MESH) { - BM_log_vert_removed(log, v, cd_vert_mask_offset); - } -} - -const float *BM_log_original_vert_co(BMLog *log, BMVert *v) -{ - BMLogEntry *entry = log->current_entry; - const BMLogVert *lv; - uint v_id = bm_log_vert_id_get(log, v); - void *key = POINTER_FROM_UINT(v_id); - - BLI_assert(entry); - - BLI_assert(BLI_ghash_haskey(entry->modified_verts, key)); - - lv = BLI_ghash_lookup(entry->modified_verts, key); - return lv->co; -} - -const float *BM_log_original_vert_no(BMLog *log, BMVert *v) -{ - BMLogEntry *entry = log->current_entry; - const BMLogVert *lv; - uint v_id = bm_log_vert_id_get(log, v); - void *key = POINTER_FROM_UINT(v_id); - - BLI_assert(entry); - - BLI_assert(BLI_ghash_haskey(entry->modified_verts, key)); - - lv = BLI_ghash_lookup(entry->modified_verts, key); - return lv->no; -} - -float BM_log_original_mask(BMLog *log, BMVert *v) -{ - BMLogEntry *entry = log->current_entry; - const BMLogVert *lv; - uint v_id = bm_log_vert_id_get(log, v); - void *key = POINTER_FROM_UINT(v_id); - - BLI_assert(entry); - - BLI_assert(BLI_ghash_haskey(entry->modified_verts, key)); - - lv = BLI_ghash_lookup(entry->modified_verts, key); - return lv->mask; -} - -void BM_log_original_vert_data(BMLog *log, BMVert *v, const float **r_co, const float **r_no) -{ - BMLogEntry *entry = log->current_entry; - const BMLogVert *lv; - uint v_id = bm_log_vert_id_get(log, v); - void *key = POINTER_FROM_UINT(v_id); - - BLI_assert(entry); - - BLI_assert(BLI_ghash_haskey(entry->modified_verts, key)); - - lv = BLI_ghash_lookup(entry->modified_verts, key); - *r_co = lv->co; - *r_no = lv->no; -} - -/************************ Debugging and Testing ***********************/ - -BMLogEntry *BM_log_current_entry(BMLog *log) -{ - return log->current_entry; -} - -RangeTreeUInt *BM_log_unused_ids(BMLog *log) -{ - return log->unused_ids; -} - -#if 0 -/* Print the list of entries, marking the current one - * - * Keep around for debugging */ -void bm_log_print(const BMLog *log, const char *description) -{ - const BMLogEntry *entry; - const char *current = " <-- current"; - int i; - - printf("%s:\n", description); - printf(" % 2d: [ initial ]%s\n", 0, (!log->current_entry) ? current : ""); - for (entry = log->entries.first, i = 1; entry; entry = entry->next, i++) { - printf(" % 2d: [%p]%s\n", i, entry, (entry == log->current_entry) ? current : ""); - } -} -#endif - -void BM_log_print_entry(BMesh *bm, BMLogEntry *entry) -{ - if (bm) { - printf("BM { totvert=%d totedge=%d totloop=%d totpoly=%d\n", - bm->totvert, - bm->totedge, - bm->totloop, - bm->totface); - - if (!bm->totvert) { - printf("%s: Warning: empty bmesh\n", __func__); - } - } - else { - printf("BM { totvert=unknown totedge=unknown totloop=unknown totpoly=unknown\n"); - } - - printf("v | added: %d, removed: %d, modified: %d\n", - (int)BLI_ghash_len(entry->added_verts), - (int)BLI_ghash_len(entry->deleted_verts), - (int)BLI_ghash_len(entry->modified_verts)); - printf("f | added: %d, removed: %d, modified: %d\n", - (int)BLI_ghash_len(entry->added_faces), - (int)BLI_ghash_len(entry->deleted_faces), - (int)BLI_ghash_len(entry->modified_faces)); - printf("}\n"); -} diff --git a/source/blender/bmesh/intern/bmesh_log.cc b/source/blender/bmesh/intern/bmesh_log.cc index 52ce773c0e9..56e4c4ade4f 100644 --- a/source/blender/bmesh/intern/bmesh_log.cc +++ b/source/blender/bmesh/intern/bmesh_log.cc @@ -1233,7 +1233,9 @@ void BMLogSetDiff::remove_face(BMesh *bm, BMFace *f, bool no_check) if (BMLogFace **ptr = modified_faces.lookup_ptr(id)) { lf = *ptr; modified_faces.remove(id); - entry->update_logface(bm, lf, f); + if (lf->verts.size() != f->len) { + entry->update_logface(bm, lf, f); + } } else { lf = entry->alloc_logface(bm, f); @@ -1603,7 +1605,6 @@ void BM_log_set_idmap(BMLog *log, struct BMIdMap *idmap) log->set_idmap(idmap); } -/* XXX get rid of bmlog refcounting. */ bool BM_log_is_dead(BMLog *log) { return log->dead; diff --git a/source/blender/bmesh/intern/bmesh_mesh_convert.cc b/source/blender/bmesh/intern/bmesh_mesh_convert.cc index efe3afe76ec..319f61aa026 100644 --- a/source/blender/bmesh/intern/bmesh_mesh_convert.cc +++ b/source/blender/bmesh/intern/bmesh_mesh_convert.cc @@ -103,6 +103,7 @@ #include "intern/bmesh_private.h" /* For element checking. */ #include "CLG_log.h" +#include static CLG_LogRef LOG = {"bmesh.mesh.convert"}; @@ -151,20 +152,58 @@ static BMFace *bm_face_create_from_mpoly(BMesh &bm, return BM_face_create(&bm, verts.data(), edges.data(), size, nullptr, BM_CREATE_SKIP_CD); } -static void bm_unmark_temp_cdlayers(BMesh *bm) +using NoCopyLayerVector = blender::Vector>; + +static NoCopyLayerVector unmark_temp_cdlayers(CustomData *domains[4]) { - CustomData_unmark_temporary_nocopy(&bm->vdata); - CustomData_unmark_temporary_nocopy(&bm->edata); - CustomData_unmark_temporary_nocopy(&bm->ldata); - CustomData_unmark_temporary_nocopy(&bm->pdata); + NoCopyLayerVector nocopy_list; + + for (int i = 0; i < 4; i++) { + CustomData *data = domains[i]; + + for (const CustomDataLayer &layer : + blender::Span(data->layers, data->totlayer)) { + if ((layer.flag & CD_FLAG_TEMPORARY) && (layer.flag & CD_FLAG_NOCOPY)) { + nocopy_list.append(std::make_pair(layer, int(1 << i))); + } + } + + CustomData_unmark_temporary_nocopy(data); + } + + return nocopy_list; } -static void bm_mark_temp_cdlayers(BMesh *bm) +static void restore_cd_copy_flags(CustomData *domains[4], NoCopyLayerVector &nocopy_list) { - CustomData_mark_temporary_nocopy(&bm->vdata); - CustomData_mark_temporary_nocopy(&bm->edata); - CustomData_mark_temporary_nocopy(&bm->ldata); - CustomData_mark_temporary_nocopy(&bm->pdata); + for (std::pair &pair : nocopy_list) { + CustomData *data = nullptr; + + switch (pair.second) { + case BM_VERT: + data = domains[0]; + break; + case BM_EDGE: + data = domains[1]; + break; + case BM_LOOP: + data = domains[2]; + break; + case BM_FACE: + data = domains[3]; + break; + } + + CustomDataLayer &layer = pair.first; + int idx = CustomData_get_named_layer_index(data, eCustomDataType(layer.type), layer.name); + + if (idx == -1) { + printf("Error: missing temporary attribute %s\n", layer.name); + continue; + } + + data->layers[idx].flag |= CD_FLAG_NOCOPY; + } } struct MeshToBMeshLayerInfo { @@ -232,10 +271,6 @@ void BM_mesh_bm_from_me(BMesh *bm, const Mesh *me, const struct BMeshFromMeshPar return; } - if (params->copy_temp_cdlayers) { - bm_unmark_temp_cdlayers(bm); - } - const bool is_new = !(bm->totvert || (bm->vdata.totlayer || bm->edata.totlayer || bm->pdata.totlayer || bm->ldata.totlayer)); KeyBlock *actkey; @@ -252,6 +287,14 @@ void BM_mesh_bm_from_me(BMesh *bm, const Mesh *me, const struct BMeshFromMeshPar CustomData mesh_ldata = CustomData_shallow_copy_remove_non_bmesh_attributes(&me->ldata, mask.lmask); + CustomData *mesh_domains[4] = {&mesh_vdata, &mesh_edata, &mesh_ldata, &mesh_pdata}; + CustomData *bmesh_domains[4] = {&bm->vdata, &bm->edata, &bm->ldata, &bm->pdata}; + NoCopyLayerVector nocopy_layers; + + if (params && params->copy_temp_cdlayers) { + nocopy_layers = unmark_temp_cdlayers(mesh_domains); + } + blender::Vector temporary_layers_to_delete; for (const int layer_index : @@ -302,8 +345,8 @@ void BM_mesh_bm_from_me(BMesh *bm, const Mesh *me, const struct BMeshFromMeshPar CustomData_bmesh_init_pool(&bm->pdata, me->totpoly, BM_FACE); } - if (params->copy_temp_cdlayers) { - bm_mark_temp_cdlayers(bm); + if (params && params->copy_temp_cdlayers) { + restore_cd_copy_flags(bmesh_domains, nocopy_layers); } if (bm->use_toolflags) { @@ -664,8 +707,9 @@ void BM_mesh_bm_from_me(BMesh *bm, const Mesh *me, const struct BMeshFromMeshPar BM_select_history_clear(bm); } - if (params->copy_temp_cdlayers) { - bm_mark_temp_cdlayers(bm); + if (params && params->copy_temp_cdlayers) { + restore_cd_copy_flags(mesh_domains, nocopy_layers); + restore_cd_copy_flags(bmesh_domains, nocopy_layers); } MEM_SAFE_FREE(cd_shape_key_offset); @@ -1486,8 +1530,10 @@ void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *me, const struct BMeshToMesh using namespace blender; const int old_verts_num = me->totvert; + CustomData *bmesh_domains[4] = {&bm->vdata, &bm->edata, &bm->ldata, &bm->pdata}; + NoCopyLayerVector nocopy_layers; if (params->copy_temp_cdlayers) { - bm_unmark_temp_cdlayers(bm); + nocopy_layers = unmark_temp_cdlayers(bmesh_domains); } BKE_mesh_clear_geometry(me); @@ -1692,7 +1738,10 @@ void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *me, const struct BMeshToMesh material_index.finish(); if (params && params->copy_temp_cdlayers) { - bm_mark_temp_cdlayers(bm); + CustomData *mesh_domains[4] = {&me->vdata, &me->edata, &me->ldata, &me->pdata}; + + restore_cd_copy_flags(bmesh_domains, nocopy_layers); + restore_cd_copy_flags(mesh_domains, nocopy_layers); } } diff --git a/source/blender/draw/intern/draw_manager_data.cc b/source/blender/draw/intern/draw_manager_data.cc index ba82609b848..2632c086942 100644 --- a/source/blender/draw/intern/draw_manager_data.cc +++ b/source/blender/draw/intern/draw_manager_data.cc @@ -1420,7 +1420,7 @@ void DRW_shgroup_call_sculpt(DRWShadingGroup *shgroup, if (use_color) { const CustomDataLayer *layer = BKE_id_attributes_color_find(&me->id, me->active_color_attribute); - if (layer) { + if (layer && !(layer->flag & CD_FLAG_TEMPORARY)) { eAttrDomain domain = BKE_id_attribute_domain(&me->id, layer); attrs[attrs_num].type = eCustomDataType(layer->type); @@ -1436,11 +1436,13 @@ void DRW_shgroup_call_sculpt(DRWShadingGroup *shgroup, if (layer_i != -1) { CustomDataLayer *layer = me->ldata.layers + layer_i; - attrs[attrs_num].type = CD_PROP_FLOAT2; - attrs[attrs_num].domain = ATTR_DOMAIN_CORNER; - STRNCPY(attrs[attrs_num].name, layer->name); + if (!(layer->flag & CD_FLAG_TEMPORARY)) { + attrs[attrs_num].type = CD_PROP_FLOAT2; + attrs[attrs_num].domain = ATTR_DOMAIN_CORNER; + STRNCPY(attrs[attrs_num].name, layer->name); - attrs_num++; + attrs_num++; + } } } @@ -1474,10 +1476,12 @@ void DRW_shgroup_call_sculpt_with_materials(DRWShadingGroup **shgroups, blender::Array attrs(attrs_num, PBVHAttrReq{}); int attrs_i = 0; + int real_attrs_num = 0; /* Requests minus and CD_FLAG_TEMPORARY layers. */ /* NOTE: these are NOT #eCustomDataType, they are extended values, ASAN may warn about this. */ attrs[attrs_i++].type = (eCustomDataType)CD_PBVH_CO_TYPE; attrs[attrs_i++].type = (eCustomDataType)CD_PBVH_NO_TYPE; + real_attrs_num += 2; for (int i = 0; i < draw_attrs.num_requests; i++) { DRW_AttributeRequest *req = draw_attrs.requests + i; @@ -1486,6 +1490,7 @@ void DRW_shgroup_call_sculpt_with_materials(DRWShadingGroup **shgroups, attrs[attrs_i].domain = req->domain; STRNCPY(attrs[attrs_i].name, req->attribute_name); attrs_i++; + real_attrs_num++; } /* UV maps are not in attribute requests. */ @@ -1496,11 +1501,12 @@ void DRW_shgroup_call_sculpt_with_materials(DRWShadingGroup **shgroups, int layer_i = CustomData_get_layer_index_n(&me->ldata, CD_PROP_FLOAT2, i); CustomDataLayer *layer = layer_i != -1 ? me->ldata.layers + layer_i : nullptr; - if (layer) { + if (layer && !(layer->flag & CD_FLAG_TEMPORARY)) { attrs[attrs_i].type = CD_PROP_FLOAT2; attrs[attrs_i].domain = ATTR_DOMAIN_CORNER; STRNCPY(attrs[attrs_i].name, layer->name); attrs_i++; + real_attrs_num++; } } } diff --git a/source/blender/editors/geometry/geometry_attributes.cc b/source/blender/editors/geometry/geometry_attributes.cc index 65685ff394d..b697f53c3f3 100644 --- a/source/blender/editors/geometry/geometry_attributes.cc +++ b/source/blender/editors/geometry/geometry_attributes.cc @@ -36,6 +36,7 @@ #include "ED_geometry.h" #include "ED_mesh.h" #include "ED_object.h" +#include "ED_sculpt.h" #include "geometry_intern.hh" @@ -89,6 +90,10 @@ static int geometry_attribute_add_exec(bContext *C, wmOperator *op) Object *ob = ED_object_context(C); ID *id = static_cast(ob->data); + if (ob->type == OB_MESH && ob->mode & OB_MODE_SCULPT && ob->sculpt && ob->sculpt->pbvh) { + ED_sculpt_undo_geometry_begin(ob, op); + } + char name[MAX_NAME]; RNA_string_get(op->ptr, "name", name); eCustomDataType type = (eCustomDataType)RNA_enum_get(op->ptr, "data_type"); @@ -96,11 +101,18 @@ static int geometry_attribute_add_exec(bContext *C, wmOperator *op) CustomDataLayer *layer = BKE_id_attribute_new(id, name, type, domain, op->reports); if (layer == nullptr) { + ED_sculpt_undo_geometry_end(ob); return OPERATOR_CANCELLED; } BKE_id_attributes_active_set(id, layer->name); + if (ob->type == OB_MESH && ob->mode == OB_MODE_SCULPT && ob->sculpt && ob->sculpt->pbvh) { + /* Push attribute into sculpt mesh. */ + BKE_sculptsession_sync_attributes(ob, static_cast(ob->data), false); + ED_sculpt_undo_geometry_end(ob); + } + DEG_id_tag_update(id, ID_RECALC_GEOMETRY); WM_main_add_notifier(NC_GEOM | ND_DATA, id); @@ -152,6 +164,10 @@ static int geometry_attribute_remove_exec(bContext *C, wmOperator *op) ID *id = static_cast(ob->data); CustomDataLayer *layer = BKE_id_attributes_active_get(id); + if (ob->type == OB_MESH && ob->mode & OB_MODE_SCULPT && ob->sculpt && ob->sculpt->pbvh) { + ED_sculpt_undo_geometry_end(ob); + } + if (!BKE_id_attribute_remove(id, layer->name, op->reports)) { return OPERATOR_CANCELLED; } @@ -161,6 +177,12 @@ static int geometry_attribute_remove_exec(bContext *C, wmOperator *op) *active_index -= 1; } + if (ob->type == OB_MESH && ob->mode == OB_MODE_SCULPT && ob->sculpt && ob->sculpt->pbvh) { + /* Push attribute into sculpt mesh. */ + BKE_sculptsession_sync_attributes(ob, static_cast(ob->data), false); + ED_sculpt_undo_geometry_end(ob); + } + DEG_id_tag_update(id, ID_RECALC_GEOMETRY); WM_main_add_notifier(NC_GEOM | ND_DATA, id); @@ -186,6 +208,11 @@ static int geometry_color_attribute_add_exec(bContext *C, wmOperator *op) { Object *ob = ED_object_context(C); ID *id = static_cast(ob->data); + bool is_sculpt = ob->sculpt && ob->mode == OB_MODE_SCULPT && ob->sculpt->pbvh; + + if (is_sculpt) { + ED_sculpt_undo_geometry_begin(ob, op); + } char name[MAX_NAME]; RNA_string_get(op->ptr, "name", name); @@ -197,6 +224,7 @@ static int geometry_color_attribute_add_exec(bContext *C, wmOperator *op) RNA_float_get_array(op->ptr, "color", color); if (layer == nullptr) { + ED_sculpt_undo_geometry_end(ob); return OPERATOR_CANCELLED; } @@ -208,6 +236,10 @@ static int geometry_color_attribute_add_exec(bContext *C, wmOperator *op) BKE_object_attributes_active_color_fill(ob, color, false); + if (is_sculpt) { + ED_sculpt_undo_geometry_end(ob); + } + DEG_id_tag_update(id, ID_RECALC_GEOMETRY); WM_main_add_notifier(NC_GEOM | ND_DATA, id); diff --git a/source/blender/editors/mesh/mesh_data.cc b/source/blender/editors/mesh/mesh_data.cc index 49323de0ef6..92cc315061d 100644 --- a/source/blender/editors/mesh/mesh_data.cc +++ b/source/blender/editors/mesh/mesh_data.cc @@ -45,6 +45,7 @@ #include "ED_screen.h" #include "ED_uvedit.h" #include "ED_view3d.h" +#include "ED_sculpt.h" #include "GEO_mesh_split_edges.hh" @@ -491,7 +492,12 @@ static int mesh_uv_texture_add_exec(bContext *C, wmOperator *op) Object *ob = ED_object_context(C); Mesh *me = static_cast(ob->data); + if (ob->mode & OB_MODE_SCULPT && ob->sculpt && ob->sculpt->pbvh) { + ED_sculpt_undo_geometry_begin(ob, op); + } + if (ED_mesh_uv_add(me, nullptr, true, true, op->reports) == -1) { + ED_sculpt_undo_geometry_end(ob); return OPERATOR_CANCELLED; } @@ -501,6 +507,12 @@ static int mesh_uv_texture_add_exec(bContext *C, wmOperator *op) WM_event_add_notifier(C, NC_SCENE | ND_TOOLSETTINGS, nullptr); } + if (ob->mode == OB_MODE_SCULPT && ob->sculpt) { + /* Push attribute into the sculpt mesh. */ + BKE_sculptsession_sync_attributes(ob, me, false); + ED_sculpt_undo_geometry_end(ob); + } + return OPERATOR_FINISHED; } @@ -524,9 +536,14 @@ static int mesh_uv_texture_remove_exec(bContext *C, wmOperator *op) Object *ob = ED_object_context(C); Mesh *me = static_cast(ob->data); + if (ob->mode & OB_MODE_SCULPT && ob->sculpt && ob->sculpt->pbvh) { + ED_sculpt_undo_geometry_begin(ob, op); + } + CustomData *ldata = GET_CD_DATA(me, ldata); const char *name = CustomData_get_active_layer_name(ldata, CD_PROP_FLOAT2); if (!BKE_id_attribute_remove(&me->id, name, op->reports)) { + ED_sculpt_undo_geometry_end(ob); return OPERATOR_CANCELLED; } @@ -536,6 +553,12 @@ static int mesh_uv_texture_remove_exec(bContext *C, wmOperator *op) WM_event_add_notifier(C, NC_SCENE | ND_TOOLSETTINGS, nullptr); } + if (ob->mode == OB_MODE_SCULPT && ob->sculpt) { + /* Delete attribute from the sculpt mesh. */ + BKE_sculptsession_sync_attributes(ob, me, false); + ED_sculpt_undo_geometry_end(ob); + } + DEG_id_tag_update(&me->id, ID_RECALC_GEOMETRY); WM_main_add_notifier(NC_GEOM | ND_DATA, me); diff --git a/source/blender/editors/sculpt_paint/paint_vertex.cc b/source/blender/editors/sculpt_paint/paint_vertex.cc index f6bdddd441f..4cc188ef5a4 100644 --- a/source/blender/editors/sculpt_paint/paint_vertex.cc +++ b/source/blender/editors/sculpt_paint/paint_vertex.cc @@ -4181,7 +4181,34 @@ bool BKE_object_attributes_active_color_fill(Object *ob, const float fill_color[4], bool only_selected) { - return paint_object_attributes_active_color_fill_ex(ob, ColorPaint4f(fill_color), only_selected); + if (ob->sculpt && ob->mode == OB_MODE_SCULPT && ob->sculpt->pbvh && + ELEM(BKE_pbvh_type(ob->sculpt->pbvh), PBVH_FACES, PBVH_BMESH)) + { + SculptSession *ss = ob->sculpt; + + /* Ensure active attribute is set inside ss->bm properly if + * in PBVH_BMESH mode. + */ + BKE_sculptsession_sync_attributes(ob, static_cast(ob->data), false); + BKE_pbvh_update_active_vcol(ss->pbvh, static_cast(ob->data)); + + Vector nodes = blender::bke::pbvh::search_gather(ss->pbvh, nullptr, nullptr); + for (PBVHNode *node : nodes) { + PBVHVertexIter vd; + BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) { + BKE_pbvh_vertex_color_set(ss->pbvh, vd.vertex, fill_color); + } + BKE_pbvh_vertex_iter_end; + + BKE_pbvh_node_mark_update_color(node); + } + + return true; + } + else { + return paint_object_attributes_active_color_fill_ex( + ob, ColorPaint4f(fill_color), only_selected); + } } static int vertex_color_set_exec(bContext *C, wmOperator * /*op*/) diff --git a/source/blender/editors/sculpt_paint/sculpt.cc b/source/blender/editors/sculpt_paint/sculpt.cc index 0d578d546fd..306569170cb 100644 --- a/source/blender/editors/sculpt_paint/sculpt.cc +++ b/source/blender/editors/sculpt_paint/sculpt.cc @@ -4004,7 +4004,7 @@ static void sculpt_topology_update(Sculpt *sd, /* do nodes under the brush cursor */ blender::bke::dyntopo::remesh_topology_nodes( brush->falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE ? &sphere_tester : &tube_tester, - ss, + ob, ss->pbvh, SCULPT_search_sphere_cb, topology_undopush_cb, @@ -5101,7 +5101,7 @@ static void sculpt_update_cache_invariants( ss->smooth_boundary_flag = eSculptBoundary(ups->smooth_boundary_flag); Mesh *me = BKE_object_get_original_mesh(ob); - BKE_sculptsession_ignore_uvs_set(ob, me->flag & ME_SCULPT_IGNORE_UVS); + BKE_sculptsession_reproject_smooth_set(ob, !(me->flag & ME_SCULPT_IGNORE_UVS)); ss->cache = cache; diff --git a/source/blender/editors/sculpt_paint/sculpt_api.cc b/source/blender/editors/sculpt_paint/sculpt_api.cc index d59088171d5..e5794c24696 100644 --- a/source/blender/editors/sculpt_paint/sculpt_api.cc +++ b/source/blender/editors/sculpt_paint/sculpt_api.cc @@ -118,6 +118,8 @@ #include #include +using blender::float2; +using blender::float3; using blender::IndexRange; using blender::OffsetIndices; @@ -227,12 +229,48 @@ eSculptBoundary SCULPT_edge_is_boundary(const SculptSession *ss, ret |= fset1 != fset2 ? SCULPT_BOUNDARY_FACE_SET : 0; } - if (typemask & SCULPT_BOUNDARY_UV) { - int boundflag1 = BM_ELEM_CD_GET_INT(e->v1, ss->attrs.boundary_flags->bmesh_cd_offset); - int boundflag2 = BM_ELEM_CD_GET_INT(e->v2, ss->attrs.boundary_flags->bmesh_cd_offset); + if (e->l && (typemask & SCULPT_BOUNDARY_UV)) { +#if 1 + CustomData *ldata = &ss->bm->ldata; + int base = ldata->typemap[CD_PROP_FLOAT2]; - ret |= (boundflag1 | boundflag2) & SCULPT_BOUNDARY_UV; + for (int i = base; i < ldata->totlayer; i++) { + CustomDataLayer &layer = ldata->layers[i]; + if (layer.type != CD_PROP_FLOAT2) { + break; + } + if (layer.flag & CD_FLAG_TEMPORARY) { + continue; + } + + BMLoop *l = e->l; + + int cd_uv = layer.offset; + float limit = 0.0005; + + float2 a1 = BM_ELEM_CD_PTR((l->v == e->v1 ? l : l->next), cd_uv); + float2 a2 = BM_ELEM_CD_PTR((l->v == e->v2 ? l : l->next), cd_uv); + + do { + float *b1 = BM_ELEM_CD_PTR((l->v == e->v1 ? l : l->next), cd_uv); + float *b2 = BM_ELEM_CD_PTR((l->v == e->v2 ? l : l->next), cd_uv); + + if (len_v2v2(a1, b1) > limit || len_v2v2(a2, b2) > limit) { + ret |= SCULPT_BOUNDARY_UV; + goto uv_outer; + } + } while ((l = l->radial_next) != e->l); + } +#else + int b1 = BM_ELEM_CD_GET_INT(e->v1, ss->attrs.boundary_flags->bmesh_cd_offset); + int b2 = BM_ELEM_CD_GET_INT(e->v2, ss->attrs.boundary_flags->bmesh_cd_offset); + + if ((b1 & SCULPT_BOUNDARY_UV) && (b2 & SCULPT_BOUNDARY_UV)) { + ret |= SCULPT_BOUNDARY_UV; + } +#endif } + uv_outer: if (typemask & SCULPT_BOUNDARY_SHARP_MARK) { ret |= !BM_elem_flag_test(e, BM_ELEM_SMOOTH) ? SCULPT_BOUNDARY_SHARP_MARK : 0; @@ -452,7 +490,7 @@ static bool sculpt_vertex_ensure_boundary(const SculptSession *ss, (BMVert *)vertex.i, &ss->bm->ldata, ss->totuv, - !ss->ignore_uvs, + ss->reproject_smooth, ss->sharp_angle_limit); } else if ((mask & (SCULPT_BOUNDARY_SHARP_ANGLE | SCULPT_CORNER_SHARP_ANGLE)) && diff --git a/source/blender/editors/sculpt_paint/sculpt_detail.cc b/source/blender/editors/sculpt_paint/sculpt_detail.cc index 9310fc20f7c..9a702233fc0 100644 --- a/source/blender/editors/sculpt_paint/sculpt_detail.cc +++ b/source/blender/editors/sculpt_paint/sculpt_detail.cc @@ -195,7 +195,7 @@ static int sculpt_detail_flood_fill_run(Scene *scene, } EdgeQueueContext remesher(tester, - ss, + ob, ss->pbvh, mode, false, diff --git a/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc b/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc index 418d097cedb..042d30458bb 100644 --- a/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc +++ b/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc @@ -99,14 +99,6 @@ void SCULPT_pbvh_clear(Object *ob) DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); } -/** - * Syncs customdata layers with internal bmesh, but ignores deleted layers. - */ -void SCULPT_dynamic_topology_sync_layers(Object *ob, Mesh *me) -{ - BKE_sculptsession_sync_attributes(ob, me); -} - static void customdata_strip_templayers(CustomData *cdata, int totelem) { for (int i = 0; i < cdata->totlayer; i++) { @@ -197,11 +189,7 @@ void SCULPT_dynamic_topology_enable_ex(Main *bmain, /* Enable dynamic topology. */ me->flag |= ME_SCULPT_DYNAMIC_TOPOLOGY; - if (!ss->bm_idmap) { - ss->bm_idmap = BM_idmap_new(ss->bm, BM_VERT | BM_EDGE | BM_FACE); - BM_idmap_check_ids(ss->bm_idmap); - BKE_sculptsession_update_attr_refs(ob); - } + BKE_sculpt_ensure_idmap(ob); /* Enable logging for undo/redo. */ if (!ss->bm_log) { diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.hh b/source/blender/editors/sculpt_paint/sculpt_intern.hh index ce036b0ce88..e5f859496a6 100644 --- a/source/blender/editors/sculpt_paint/sculpt_intern.hh +++ b/source/blender/editors/sculpt_paint/sculpt_intern.hh @@ -1696,7 +1696,6 @@ void sculpt_dynamic_topology_disable_with_undo(Main *bmain, bool SCULPT_stroke_is_dynamic_topology(const SculptSession *ss, const Brush *brush); void SCULPT_dynamic_topology_triangulate(struct SculptSession *ss, struct BMesh *bm); -void SCULPT_dynamic_topology_sync_layers(Object *ob, struct Mesh *me); enum eDynTopoWarnFlag SCULPT_dynamic_topology_check(Scene *scene, Object *ob); @@ -1921,7 +1920,7 @@ void SCULPT_neighbor_coords_average_interior(SculptSession *ss, BLI_INLINE bool SCULPT_need_reproject(const SculptSession *ss) { - return !ss->ignore_uvs && ss->bm && CustomData_has_layer(&ss->bm->ldata, CD_PROP_FLOAT2); + return ss->reproject_smooth && ss->bm; // && CustomData_has_layer(&ss->bm->ldata, CD_PROP_FLOAT2); } int SCULPT_vertex_island_get(SculptSession *ss, PBVHVertRef vertex); diff --git a/source/blender/editors/sculpt_paint/sculpt_paint_color.cc b/source/blender/editors/sculpt_paint/sculpt_paint_color.cc index ecd0e9f2e20..a930b26c348 100644 --- a/source/blender/editors/sculpt_paint/sculpt_paint_color.cc +++ b/source/blender/editors/sculpt_paint/sculpt_paint_color.cc @@ -215,7 +215,7 @@ static void do_paint_brush_task_cb_ex(void *__restrict userdata, * at this point to avoid washing out non-binary masking modes like cavity masking. */ float automasking = SCULPT_automasking_factor_get( ss->cache->automasking, ss, vd.vertex, &automask_data); - mul_v4_v4fl(buffer_color, color_buffer, brush->alpha * automasking); + mul_v4_v4fl(buffer_color, color_buffer, alpha * automasking); float vcolor[4]; SCULPT_vertex_color_get(ss, vd.vertex, vcolor); diff --git a/source/blender/editors/sculpt_paint/sculpt_smooth.cc b/source/blender/editors/sculpt_paint/sculpt_smooth.cc index 9bd91ed3d48..5fb79f40152 100644 --- a/source/blender/editors/sculpt_paint/sculpt_smooth.cc +++ b/source/blender/editors/sculpt_paint/sculpt_smooth.cc @@ -497,7 +497,8 @@ void SCULPT_bmesh_four_neighbor_average(SculptSession *ss, madd_v3_v3fl(avg_co, co2, fac); tot_co += fac; continue; - } else if (boundary != SCULPT_BOUNDARY_NONE) { + } + else if (boundary != SCULPT_BOUNDARY_NONE) { if (boundary & smooth_mask) { float fac = weighted ? areas[area_i] : 1.0f; float vec[3], co3[3]; @@ -505,7 +506,7 @@ void SCULPT_bmesh_four_neighbor_average(SculptSession *ss, sub_v3_v3v3(vec, co2, co1); copy_v3_v3(co3, co1); madd_v3_v3fl(co3, no1, dot_v3v3(vec, no1)); - + madd_v3_v3fl(avg_co, co3, fac); tot_co += fac; } @@ -759,9 +760,9 @@ static void SCULPT_enhance_details_brush(Sculpt *sd, Object *ob, Span(userdata); SculptSession *ss = data->ob->sculpt; @@ -925,7 +926,8 @@ void SCULPT_smooth( data.strength = strength; TaskParallelSettings settings; - BKE_pbvh_parallel_range_settings(&settings, true, nodes.size()); + //XXX + BKE_pbvh_parallel_range_settings(&settings, false, nodes.size()); BLI_task_parallel_range(0, nodes.size(), &data, do_smooth_brush_task_cb_ex, &settings); } } diff --git a/source/blender/editors/sculpt_paint/sculpt_undo.cc b/source/blender/editors/sculpt_paint/sculpt_undo.cc index b702d136680..d0db226b24d 100644 --- a/source/blender/editors/sculpt_paint/sculpt_undo.cc +++ b/source/blender/editors/sculpt_paint/sculpt_undo.cc @@ -690,6 +690,7 @@ static bool sculpt_undo_restore_face_sets(bContext *C, } typedef struct BmeshUndoData { + Object *ob; PBVH *pbvh; BMesh *bm; bool do_full_recalc; @@ -794,12 +795,22 @@ static void bmesh_undo_on_face_add(BMFace *f, void *userdata) data->balance_pbvh = true; } + static void bmesh_undo_full_mesh(void *userdata) { BmeshUndoData *data = (BmeshUndoData *)userdata; + BKE_sculptsession_update_attr_refs(data->ob); + if (data->pbvh) { - BKE_pbvh_bmesh_update_all_valence(data->pbvh); + BMIter iter; + BMVert *v; + + BM_ITER_MESH (v, &iter, data->bm, BM_VERTS_OF_MESH) { + BKE_pbvh_bmesh_update_valence(data->pbvh, BKE_pbvh_make_vref((intptr_t)v)); + } + + data->pbvh = nullptr; } data->do_full_recalc = true; @@ -962,7 +973,8 @@ static void bmesh_undo_customdata_change(CustomData *domain, char htype, void *u static void sculpt_undo_bmesh_restore_generic(SculptUndoNode *unode, Object *ob, SculptSession *ss) { - BmeshUndoData data = {ss->pbvh, + BmeshUndoData data = {ob, + ss->pbvh, ss->bm, false, false, @@ -990,6 +1002,7 @@ static void sculpt_undo_bmesh_restore_generic(SculptUndoNode *unode, Object *ob, BKE_sculptsession_update_attr_refs(ob); + BKE_sculpt_ensure_idmap(ob); // pbvh_bmesh_check_nodes(ss->pbvh); if (unode->applied) { @@ -1001,10 +1014,11 @@ static void sculpt_undo_bmesh_restore_generic(SculptUndoNode *unode, Object *ob, unode->applied = true; } - BKE_pbvh_bmesh_check_nodes(ss->pbvh); update_unode_bmesh_memsize(unode); if (!data.do_full_recalc) { + BKE_pbvh_bmesh_check_nodes(ss->pbvh); + Vector nodes = blender::bke::pbvh::search_gather(ss->pbvh, nullptr, nullptr); if (data.regen_all_unique_verts) { @@ -1071,10 +1085,10 @@ static void sculpt_undo_bmesh_enable(Object *ob, SculptUndoNode *unode, bool is_ if (ss->bm_idmap) { BM_idmap_destroy(ss->bm_idmap); + ss->bm_idmap = nullptr; } - ss->bm_idmap = BM_idmap_new(ss->bm, BM_VERT | BM_EDGE | BM_FACE); - BM_idmap_check_ids(ss->bm_idmap); + BKE_sculpt_ensure_idmap(ob); if (!ss->bm_log) { /* Restore the BMLog using saved entries. */ @@ -1308,35 +1322,7 @@ static int sculpt_undo_bmesh_restore( ss->bm_log = BM_log_from_existing_entries_create(ss->bm, ss->bm_idmap, unode->bm_entry); } - if (ss->bm_log && ss->bm && - !ELEM(unode->type, SCULPT_UNDO_DYNTOPO_BEGIN, SCULPT_UNDO_DYNTOPO_END)) { - BKE_sculptsession_update_attr_refs(ob); - -#if 0 - if (ss->active_face.i && ss->active_face.i != -1LL) { - ss->active_face.i = (intptr_t)BM_log_face_id_get(ss->bm_log, - (BMFace *)ss->active_face.i); - } - else { - ss->active_face.i = -1; - } - - if (ss->active_vertex.i && ss->active_vertex.i != -1LL) { - ss->active_vertex.i = (intptr_t)BM_log_vert_id_get( - ss->bm_log, (BMVert *)ss->active_vertex.i); - } - else { - ss->active_vertex.i = -1; - } -#endif - ss->active_face.i = ss->active_vertex.i = -1; - } - else { - ss->active_face.i = ss->active_vertex.i = -1; - } - bool ret = false; - bool set_active_vertex = true; switch (unode->type) { case SCULPT_UNDO_DYNTOPO_BEGIN: @@ -1344,13 +1330,11 @@ static int sculpt_undo_bmesh_restore( SCULPT_vertex_random_access_ensure(ss); ss->active_face.i = ss->active_vertex.i = 0; - set_active_vertex = false; ret = true; break; case SCULPT_UNDO_DYNTOPO_END: - ss->active_face.i = ss->active_vertex.i = 0; - set_active_vertex = false; + ss->active_face.i = ss->active_vertex.i = PBVH_REF_NONE; sculpt_undo_bmesh_restore_end(C, unode, ob, ss, dir); SCULPT_vertex_random_access_ensure(ss); @@ -1374,37 +1358,10 @@ static int sculpt_undo_bmesh_restore( BKE_pbvh_flush_tri_areas(ss->pbvh); } - if (set_active_vertex && ss->bm_log && ss->bm) { - if (ss->active_face.i != -1) { - BMFace *f = BM_log_id_face_get(ss->bm, ss->bm_log, (uint)ss->active_face.i); - if (f && f->head.htype == BM_FACE) { - ss->active_face.i = (intptr_t)f; - } - else { - ss->active_face.i = 0LL; - } - } - else { - ss->active_face.i = 0LL; - } + ss->active_face.i = ss->active_vertex.i = PBVH_REF_NONE; - if (ss->active_vertex.i != -1) { - BMVert *v = BM_log_id_vert_get(ss->bm, ss->bm_log, (uint)ss->active_vertex.i); - - if (v && v->head.htype == BM_VERT) { - ss->active_vertex.i = (intptr_t)v; - } - else { - ss->active_vertex.i = 0LL; - } - } - else { - ss->active_vertex.i = 0LL; - } - } - else { - ss->active_face.i = ss->active_vertex.i = 0; - } + /* Load attribute layout from bmesh to ob. */ + BKE_sculptsession_sync_attributes(ob, static_cast(ob->data), true); return ret; } diff --git a/source/blender/makesrna/intern/rna_mesh.c b/source/blender/makesrna/intern/rna_mesh.c index 75142be0195..ed0dcb1b9e2 100644 --- a/source/blender/makesrna/intern/rna_mesh.c +++ b/source/blender/makesrna/intern/rna_mesh.c @@ -3744,6 +3744,10 @@ static void rna_def_mesh(BlenderRNA *brna) RNA_def_property_boolean_sdna(prop, NULL, "flag", ME_SCULPT_IGNORE_UVS); RNA_def_property_ui_text(prop, "Ignore UVs", ""); + prop = RNA_def_property(srna, "sculpt_dyntopo_relax", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", ME_FLAG_UNUSED_5); + RNA_def_property_ui_text(prop, "Relax During Remeshing", "Relax geometry during remeshing"); + /* End Symmetry */ prop = RNA_def_property(srna, "use_auto_smooth", PROP_BOOLEAN, PROP_NONE); -- 2.30.2 From 65251574be33ec8d7366acf29fa21d441018b495 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Fri, 2 Jun 2023 04:38:46 -0700 Subject: [PATCH 147/279] temp-sculpt-dyntopo: Fix compiler error Clang is sometimes excessively agressive in pruning unused template code, and allows syntax errors. --- source/blender/blenkernel/intern/dyntopo_intern.hh | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/source/blender/blenkernel/intern/dyntopo_intern.hh b/source/blender/blenkernel/intern/dyntopo_intern.hh index 889d80e527f..73423958f03 100644 --- a/source/blender/blenkernel/intern/dyntopo_intern.hh +++ b/source/blender/blenkernel/intern/dyntopo_intern.hh @@ -421,7 +421,10 @@ ENUM_OPERATORS(eValidateFaceFlags, CHECK_FACE_MANIFOLD); #ifndef CHECKMESH -template inline bool validate_elem(PBVH *pbvh, T *elem){return true}; +template inline bool validate_elem(PBVH *pbvh, T *elem) +{ + return true +} inline bool validate_vert(PBVH *, BMVert *, eValidateVertFlags) { return true; -- 2.30.2 From c49db523494af20149538cdd4ef1499c574590c6 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Fri, 2 Jun 2023 04:44:43 -0700 Subject: [PATCH 148/279] temp-sculpt-dyntopo: Fix compile error again --- source/blender/blenkernel/intern/dyntopo_intern.hh | 4 ++-- source/blender/bmesh/intern/bmesh_core.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/source/blender/blenkernel/intern/dyntopo_intern.hh b/source/blender/blenkernel/intern/dyntopo_intern.hh index 73423958f03..5e398e86fe9 100644 --- a/source/blender/blenkernel/intern/dyntopo_intern.hh +++ b/source/blender/blenkernel/intern/dyntopo_intern.hh @@ -421,9 +421,9 @@ ENUM_OPERATORS(eValidateFaceFlags, CHECK_FACE_MANIFOLD); #ifndef CHECKMESH -template inline bool validate_elem(PBVH *pbvh, T *elem) +template inline bool validate_elem(PBVH *, T *) { - return true + return true; } inline bool validate_vert(PBVH *, BMVert *, eValidateVertFlags) { diff --git a/source/blender/bmesh/intern/bmesh_core.c b/source/blender/bmesh/intern/bmesh_core.c index 18992a197a2..ea1b9a87605 100644 --- a/source/blender/bmesh/intern/bmesh_core.c +++ b/source/blender/bmesh/intern/bmesh_core.c @@ -7,7 +7,7 @@ */ //#define JVKE_DEBUG -#define FORCE_BMESH_CHECK +//#define FORCE_BMESH_CHECK #if !defined(NDEBUG) || defined(FORCE_BMESH_CHECK) || defined(JVKE_DEBUG) # define BMESH_DEBUG -- 2.30.2 From dbebea634396451233d0411aef38bda717e69843 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Fri, 2 Jun 2023 04:49:07 -0700 Subject: [PATCH 149/279] temp-sculpt-dyntopo: More clang-related compile errors --- source/blender/blenkernel/intern/dyntopo.cc | 37 +++++++++------------ 1 file changed, 15 insertions(+), 22 deletions(-) diff --git a/source/blender/blenkernel/intern/dyntopo.cc b/source/blender/blenkernel/intern/dyntopo.cc index db532ebffd9..d5aa88efa0e 100644 --- a/source/blender/blenkernel/intern/dyntopo.cc +++ b/source/blender/blenkernel/intern/dyntopo.cc @@ -3147,6 +3147,21 @@ void BKE_pbvh_bmesh_add_face(PBVH *pbvh, struct BMFace *f, bool log_face, bool f namespace myinterp { +int ignore_check = 0; +struct ignore { + int f = 0; + + ignore() + { + f = ignore_check++; + } + ~ignore() + { + f = ignore_check--; + } + ignore(const ignore &b) = delete; +}; + template constexpr T get_epsilon() { if constexpr (std::is_same_v) { @@ -3524,28 +3539,6 @@ template void copy_v2_v2(T1 r[2], cons r[1] = b[1]; } -int ignore_check = 0; -int bleh_bleh = 0; - -struct ignore { - int f = 0; - - void bleh() - { - bleh_bleh++; - } - - ignore() - { - f = ignore_check++; - } - ~ignore() - { - f = ignore_check--; - } - ignore(const ignore &b) = delete; -}; - template struct TestFloat { T f; -- 2.30.2 From 0b5cb2945f9c216ea87d663af211e17ba40f002a Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Fri, 2 Jun 2023 04:51:57 -0700 Subject: [PATCH 150/279] temp-sculpt-dyntopo: another compiler error --- source/blender/blenkernel/intern/dyntopo_collapse.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/blenkernel/intern/dyntopo_collapse.cc b/source/blender/blenkernel/intern/dyntopo_collapse.cc index 42b6de1dba6..9328d1b959b 100644 --- a/source/blender/blenkernel/intern/dyntopo_collapse.cc +++ b/source/blender/blenkernel/intern/dyntopo_collapse.cc @@ -595,7 +595,7 @@ BMVert *pbvh_bmesh_collapse_edge(PBVH *pbvh, int boundflag2 = BM_ELEM_CD_GET_INT(v2, pbvh->cd_boundary_flag); if ((boundflag1 & SCULPT_BOUNDARY_UV) != (boundflag2 & SCULPT_BOUNDARY_UV)) { - return false; + return nullptr; } /* one of the two vertices may be masked, select the correct one for deletion */ -- 2.30.2 From 21aeab573d4eb8e01814bb9b90fa0b58902f57fe Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Fri, 2 Jun 2023 05:23:01 -0700 Subject: [PATCH 151/279] temp-sculpt-dyntopo: Cleanup warnings --- source/blender/blenkernel/BKE_pbvh.h | 25 ++--- source/blender/blenkernel/intern/dyntopo.cc | 96 +++++-------------- .../blenkernel/intern/dyntopo_intern.hh | 7 -- source/blender/blenkernel/intern/pbvh.cc | 26 ++--- .../editors/sculpt_paint/sculpt_expand.cc | 2 +- .../editors/sculpt_paint/sculpt_undo.cc | 2 - .../makesrna/intern/rna_sculpt_paint.c | 6 -- 7 files changed, 45 insertions(+), 119 deletions(-) diff --git a/source/blender/blenkernel/BKE_pbvh.h b/source/blender/blenkernel/BKE_pbvh.h index 9332c25d5c9..771cd5636fb 100644 --- a/source/blender/blenkernel/BKE_pbvh.h +++ b/source/blender/blenkernel/BKE_pbvh.h @@ -803,18 +803,6 @@ void pbvh_vertex_iter_init(PBVH *pbvh, PBVHNode *node, struct PBVHVertexIter *vi } \ ((void)0) -#define BKE_pbvh_vertex_to_index(pbvh, v) \ - (BKE_pbvh_type(pbvh) == PBVH_BMESH && v.i != -1 ? BM_elem_index_get((BMVert *)(v.i)) : (v.i)) -PBVHVertRef BKE_pbvh_index_to_vertex(PBVH *pbvh, int idx); - -#define BKE_pbvh_edge_to_index(pbvh, v) \ - (BKE_pbvh_type(pbvh) == PBVH_BMESH && v.i != -1 ? BM_elem_index_get((BMEdge *)(v.i)) : (v.i)) -PBVHEdgeRef BKE_pbvh_index_to_edge(PBVH *pbvh, int idx); - -#define BKE_pbvh_face_to_index(pbvh, v) \ - (BKE_pbvh_type(pbvh) == PBVH_BMESH && v.i != -1 ? BM_elem_index_get((BMFace *)(v.i)) : (v.i)) -PBVHFaceRef BKE_pbvh_index_to_face(PBVH *pbvh, int idx); - #define PBVH_FACE_ITER_VERTS_RESERVED 8 #ifdef __cplusplus @@ -972,25 +960,23 @@ void BKE_pbvh_bmesh_from_saved_indices(PBVH *pbvh); * BKE_pbvh_bmesh_from_saved_indices */ void BKE_pbvh_bmesh_set_toolflags(PBVH *pbvh, bool use_toolflags); -void BKE_pbvh_bmesh_remove_face(PBVH *pbvh, struct BMFace *f, bool log_face); -void BKE_pbvh_bmesh_remove_edge(PBVH *pbvh, struct BMEdge *e, bool log_vert); -void BKE_pbvh_bmesh_remove_vertex(PBVH *pbvh, struct BMVert *v, bool log_vert); - +void BKE_pbvh_bmesh_remove_face(PBVH *pbvh, BMFace *f, bool log_face); +void BKE_pbvh_bmesh_remove_edge(PBVH *pbvh, BMEdge *e, bool log_edge); +void BKE_pbvh_bmesh_remove_vertex(PBVH *pbvh, BMVert *v, bool log_vert); void BKE_pbvh_bmesh_add_face(PBVH *pbvh, struct BMFace *f, bool log_face, bool force_tree_walk); -// note that e_tri and f_example are allowed to be NULL +/* e_tri and f_example are allowed to be nullptr. */ struct BMFace *BKE_pbvh_face_create_bmesh(PBVH *pbvh, struct BMVert *v_tri[3], struct BMEdge *e_tri[3], const struct BMFace *f_example); -// if node is NULL, one will be foudn in the pbvh, which potentially can be slow +/* If node is nullptr then one will be found in the pbvh. */ struct BMVert *BKE_pbvh_vert_create_bmesh( PBVH *pbvh, float co[3], float no[3], PBVHNode *node, struct BMVert *v_example); PBVHNode *BKE_pbvh_node_from_face_bmesh(PBVH *pbvh, struct BMFace *f); PBVHNode *BKE_pbvh_node_from_index(PBVH *pbvh, int node_i); -struct BMesh *BKE_pbvh_reorder_bmesh(PBVH *pbvh); void BKE_pbvh_sharp_limit_set(PBVH *pbvh, float limit); float BKE_pbvh_test_sharp_faces_bmesh(BMFace *f1, BMFace *f2, float limit); void BKE_pbvh_update_vert_boundary(int cd_faceset_offset, @@ -1092,6 +1078,7 @@ void BKE_pbvh_show_orig_set(PBVH *pbvh, bool show_orig); bool BKE_pbvh_show_orig_get(PBVH *pbvh); void BKE_pbvh_flush_tri_areas(PBVH *pbvh); +void BKE_pbvh_bmesh_check_nodes(PBVH *pbvh); #ifdef __cplusplus void BKE_pbvh_pmap_set(PBVH *pbvh, blender::GroupedSpan pmap); diff --git a/source/blender/blenkernel/intern/dyntopo.cc b/source/blender/blenkernel/intern/dyntopo.cc index d5aa88efa0e..b590f57da02 100644 --- a/source/blender/blenkernel/intern/dyntopo.cc +++ b/source/blender/blenkernel/intern/dyntopo.cc @@ -562,13 +562,13 @@ static void add_split_edge_recursive( #endif typedef struct EdgeQueueThreadData { - PBVH *pbvh; - PBVHNode *node; + PBVH *pbvh = nullptr; + PBVHNode *node = nullptr; Vector edges; - EdgeQueueContext *eq_ctx; - int size; - bool is_collapse; - int seed; + EdgeQueueContext *eq_ctx = nullptr; + int size = 0; + bool is_collapse = false; + int seed = 0; } EdgeQueueThreadData; static void edge_thread_data_insert(EdgeQueueThreadData *tdata, BMEdge *e) @@ -1198,53 +1198,6 @@ bool check_vert_fan_are_tris(PBVH *pbvh, BMVert *v) return false; } -bool _check_vert_fan_are_tris(PBVH *pbvh, BMVert *v) -{ - uint8_t *flag = BM_ELEM_CD_PTR(v, pbvh->cd_flag); - - if (!(*flag & SCULPTFLAG_NEED_TRIANGULATE)) { - return true; - } - - bm_log_message(" == triangulate == "); - - Vector fs; - - validate_vert(pbvh, v, CHECK_VERT_ALL); - - if (v->head.htype != BM_VERT) { - printf("non-vert %p fed to %s\n", v, __func__); - return false; - } - - BMIter iter; - BMFace *f; - BM_ITER_ELEM (f, &iter, v, BM_FACES_OF_VERT) { - BMLoop *l = f->l_first; - - do { - pbvh_boundary_update_bmesh(pbvh, l->v); - dyntopo_add_flag(pbvh, l->v, SCULPTFLAG_NEED_VALENCE); - } while ((l = l->next) != f->l_first); - fs.append(f); - - if (BM_elem_is_free((BMElem *)f, BM_FACE)) { - printf("%s: corrupted face error!\n", __func__); - } - } - - *flag &= ~SCULPTFLAG_NEED_TRIANGULATE; - - for (int i = 0; i < fs.size(); i++) { - /* Triangulation can sometimes delete a face. */ - if (!BM_elem_is_free((BMElem *)fs[i], BM_FACE)) { - check_face_is_tri(pbvh, fs[i]); - } - } - - return false; -} - /* Create a priority queue containing vertex pairs connected by a long * edge as defined by PBVH.bm_max_edge_len. * @@ -1437,7 +1390,6 @@ static void edge_queue_create_local(EdgeQueueContext *eq_ctx, if ((node->flag & PBVH_Leaf) && (node->flag & PBVH_UpdateTopology) && !(node->flag & PBVH_FullyHidden)) { - memset(&td, 0, sizeof(td)); td.pbvh = pbvh; td.node = node; td.is_collapse = local_mode & PBVH_LocalCollapse; @@ -3881,10 +3833,12 @@ inline void reproject_bm_data( BMLoop *l_iter; BMLoop *l_first; - const void **vblocks = do_vertex ? BLI_array_alloca(vblocks, f_src->len) : nullptr; - const void **blocks = BLI_array_alloca(blocks, f_src->len); - T(*cos_2d)[2] = BLI_array_alloca(cos_2d, f_src->len); - T *w = BLI_array_alloca(w, f_src->len); + const void **vblocks = do_vertex ? + static_cast(BLI_array_alloca(vblocks, f_src->len)) : + nullptr; + const void **blocks = static_cast(BLI_array_alloca(blocks, f_src->len)); + T(*cos_2d)[2] = static_cast(BLI_array_alloca(cos_2d, f_src->len)); + T *w = static_cast(BLI_array_alloca(w, f_src->len)); float axis_mat[3][3]; /* use normal to transform into 2d xy coords */ float co[2]; @@ -3959,7 +3913,7 @@ inline void reproject_bm_data( float *fw; if constexpr (!std::is_same_v) { - fw = BLI_array_alloca(fw, f_src->len); + fw = static_cast(BLI_array_alloca(fw, f_src->len)); for (int i = 0; i < f_src->len; i++) { fw[i] = float(w[i]); } @@ -4014,8 +3968,8 @@ void BKE_sculpt_reproject_cdata(SculptSession *ss, int tag = BM_ELEM_TAG_ALT; - float *lastuvs = (float *)BLI_array_alloca(lastuvs, totuv * 2); - bool *snapuvs = (bool *)BLI_array_alloca(snapuvs, totuv); + float *lastuvs = static_cast(BLI_array_alloca(lastuvs, totuv * 2)); + bool *snapuvs = static_cast(BLI_array_alloca(snapuvs, totuv)); e = v->e; int valence = 0; @@ -4103,20 +4057,20 @@ void BKE_sculpt_reproject_cdata(SculptSession *ss, /* Original (l->prev, l, l->next) projections for each loop ('l' remains unchanged). */ - char *_blocks = (char *)alloca(ldata->totsize * totloop); - void **blocks = (void **)BLI_array_alloca(blocks, totloop); + char *_blocks = static_cast(alloca(ldata->totsize * totloop)); + void **blocks = static_cast(BLI_array_alloca(blocks, totloop)); const int max_vblocks = valence * 2; - char *_vblocks = (char *)alloca(ss->bm->vdata.totsize * max_vblocks); - void **vblocks = (void **)BLI_array_alloca(vblocks, max_vblocks); + char *_vblocks = static_cast(alloca(ss->bm->vdata.totsize * max_vblocks)); + void **vblocks = static_cast(BLI_array_alloca(vblocks, max_vblocks)); for (int i = 0; i < max_vblocks; i++, _vblocks += ss->bm->vdata.totsize) { - vblocks[i] = (void *)_vblocks; + vblocks[i] = static_cast(_vblocks); } for (int i = 0; i < totloop; i++, _blocks += ldata->totsize) { - blocks[i] = (void *)_blocks; + blocks[i] = static_cast(_blocks); } float vco[3], vno[3]; @@ -4146,8 +4100,8 @@ void BKE_sculpt_reproject_cdata(SculptSession *ss, BMLoop *l = ls[i]; float no[3] = {0.0f, 0.0f, 0.0f}; - BMLoop *fakels = (BMLoop *)BLI_array_alloca(fakels, l->f->len); - BMVert *fakevs = (BMVert *)BLI_array_alloca(fakevs, l->f->len); + BMLoop *fakels = static_cast(BLI_array_alloca(fakels, l->f->len)); + BMVert *fakevs = static_cast(BLI_array_alloca(fakevs, l->f->len)); BMLoop *l2 = l->f->l_first; BMLoop *fakel = fakels; BMVert *fakev = fakevs; @@ -4229,7 +4183,7 @@ void BKE_sculpt_reproject_cdata(SculptSession *ss, } if (cur_vblock > 0) { - float *ws = BLI_array_alloca(ws, cur_vblock); + float *ws = static_cast(BLI_array_alloca(ws, cur_vblock)); for (int i = 0; i < cur_vblock; i++) { ws[i] = 1.0f / float(cur_vblock); } @@ -4248,7 +4202,7 @@ void BKE_sculpt_reproject_cdata(SculptSession *ss, } } - int *tots = (int *)BLI_array_alloca(tots, totuv); + int *tots = static_cast(BLI_array_alloca(tots, totuv)); for (int i = 0; i < totuv; i++) { lastuvs[i * 2] = lastuvs[i * 2 + 1] = 0.0f; diff --git a/source/blender/blenkernel/intern/dyntopo_intern.hh b/source/blender/blenkernel/intern/dyntopo_intern.hh index 5e398e86fe9..eee4fb72c2c 100644 --- a/source/blender/blenkernel/intern/dyntopo_intern.hh +++ b/source/blender/blenkernel/intern/dyntopo_intern.hh @@ -386,13 +386,6 @@ BMFace *pbvh_bmesh_face_create(PBVH *pbvh, bool log_face); } // namespace blender::bke::dyntopo -extern "C" { -void BKE_pbvh_bmesh_remove_face(PBVH *pbvh, BMFace *f, bool log_face); -void BKE_pbvh_bmesh_remove_edge(PBVH *pbvh, BMEdge *e, bool log_edge); -void BKE_pbvh_bmesh_remove_vertex(PBVH *pbvh, BMVert *v, bool log_vert); -void BKE_pbvh_bmesh_add_face(PBVH *pbvh, struct BMFace *f, bool log_face, bool force_tree_walk); -} - /*************************** Topology update **************************/ /**** Debugging Tools ********/ diff --git a/source/blender/blenkernel/intern/pbvh.cc b/source/blender/blenkernel/intern/pbvh.cc index 893d558c4d6..2f3bd55a111 100644 --- a/source/blender/blenkernel/intern/pbvh.cc +++ b/source/blender/blenkernel/intern/pbvh.cc @@ -2628,10 +2628,10 @@ bool ray_face_intersection_tri(const float ray_start[3], return false; } -bool ray_update_depth_and_hit_count(const float depth_test, - float *r_depth, - float *r_back_depth, - int *hit_count) +static bool ray_update_depth_and_hit_count(const float depth_test, + float *r_depth, + float *r_back_depth, + int *hit_count) { (*hit_count)++; if (depth_test < *r_depth) { @@ -2647,15 +2647,15 @@ bool ray_update_depth_and_hit_count(const float depth_test, return false; } -bool ray_face_intersection_depth_quad(const float ray_start[3], - struct IsectRayPrecalc *isect_precalc, - const float t0[3], - const float t1[3], - const float t2[3], - const float t3[3], - float *r_depth, - float *r_back_depth, - int *hit_count) +static bool ray_face_intersection_depth_quad(const float ray_start[3], + struct IsectRayPrecalc *isect_precalc, + const float t0[3], + const float t1[3], + const float t2[3], + const float t3[3], + float *r_depth, + float *r_back_depth, + int *hit_count) { float depth_test; if (!(isect_ray_tri_watertight_v3(ray_start, isect_precalc, t0, t1, t2, &depth_test, nullptr) || diff --git a/source/blender/editors/sculpt_paint/sculpt_expand.cc b/source/blender/editors/sculpt_paint/sculpt_expand.cc index bf544976cfb..6e1cdbf44aa 100644 --- a/source/blender/editors/sculpt_paint/sculpt_expand.cc +++ b/source/blender/editors/sculpt_paint/sculpt_expand.cc @@ -1838,7 +1838,7 @@ static int sculpt_expand_active_face_set_id_get(SculptSession *ss, ExpandCache * switch (BKE_pbvh_type(ss->pbvh)) { case PBVH_BMESH: case PBVH_FACES: - return expand_cache->original_face_sets[BKE_pbvh_vertex_to_index(ss->pbvh, ss->active_face)]; + return expand_cache->original_face_sets[BKE_pbvh_face_to_index(ss->pbvh, ss->active_face)]; case PBVH_GRIDS: { const int face_index = BKE_subdiv_ccg_grid_to_face_index(ss->subdiv_ccg, ss->active_grid_index); diff --git a/source/blender/editors/sculpt_paint/sculpt_undo.cc b/source/blender/editors/sculpt_paint/sculpt_undo.cc index d0db226b24d..9f8be14107a 100644 --- a/source/blender/editors/sculpt_paint/sculpt_undo.cc +++ b/source/blender/editors/sculpt_paint/sculpt_undo.cc @@ -170,8 +170,6 @@ typedef struct SculptUndoStep { #endif } SculptUndoStep; -extern "C" void BKE_pbvh_bmesh_check_nodes(PBVH *pbvh); - static UndoSculpt *sculpt_undo_get_nodes(void); static bool sculpt_attribute_ref_equals(SculptAttrRef *a, SculptAttrRef *b); static void sculpt_save_active_attribute(Object *ob, SculptAttrRef *attr); diff --git a/source/blender/makesrna/intern/rna_sculpt_paint.c b/source/blender/makesrna/intern/rna_sculpt_paint.c index 893fdd98a77..a88c59ccccf 100644 --- a/source/blender/makesrna/intern/rna_sculpt_paint.c +++ b/source/blender/makesrna/intern/rna_sculpt_paint.c @@ -549,12 +549,6 @@ static bool rna_ImaPaint_detect_data(ImagePaintSettings *imapaint) return imapaint->missing_data == 0; } -void SCULPT_substep_undo(bContext *ctx, int dir); -static void rna_SCULPT_substep_undo(bContext *ctx, int dir) -{ - SCULPT_substep_undo(ctx, dir); -} - static char *rna_GPencilSculptSettings_path(const PointerRNA *UNUSED(ptr)) { return BLI_strdup("tool_settings.gpencil_sculpt"); -- 2.30.2 From 78d338b2081dd1876fd542701fa2cc489e2b1484 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Fri, 2 Jun 2023 05:25:10 -0700 Subject: [PATCH 152/279] temp-sculpt-dyntopo: Remove redundant declaration --- source/blender/editors/sculpt_paint/sculpt_intern.hh | 2 -- 1 file changed, 2 deletions(-) diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.hh b/source/blender/editors/sculpt_paint/sculpt_intern.hh index e5f859496a6..8f9a41d4563 100644 --- a/source/blender/editors/sculpt_paint/sculpt_intern.hh +++ b/source/blender/editors/sculpt_paint/sculpt_intern.hh @@ -1701,8 +1701,6 @@ enum eDynTopoWarnFlag SCULPT_dynamic_topology_check(Scene *scene, Object *ob); /** \} */ -void SCULPT_combine_transform_proxies(Sculpt *sd, Object *ob); - /* -------------------------------------------------------------------- */ /** \name Auto-masking. * \{ */ -- 2.30.2 From 1763f9fb6353fef4c09a36e9f478dc3cf5c35a0a Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Sat, 3 Jun 2023 01:11:02 -0700 Subject: [PATCH 153/279] temp-sculpt-dyntopo: Commit various bugfixes * Fixed various corruptions of PBVHNode.bm_other_verts. --- source/blender/blenkernel/BKE_dyntopo_set.hh | 1 - source/blender/blenkernel/intern/dyntopo.cc | 16 +- .../blenkernel/intern/dyntopo_collapse.cc | 456 ++++++++++-------- .../blenkernel/intern/dyntopo_intern.hh | 2 - .../blender/blenkernel/intern/pbvh_bmesh.cc | 374 +++++++------- source/blender/bmesh/intern/bmesh_core.c | 18 - source/blender/editors/sculpt_paint/sculpt.cc | 2 +- 7 files changed, 482 insertions(+), 387 deletions(-) diff --git a/source/blender/blenkernel/BKE_dyntopo_set.hh b/source/blender/blenkernel/BKE_dyntopo_set.hh index b46c26337be..d57430ea9ba 100644 --- a/source/blender/blenkernel/BKE_dyntopo_set.hh +++ b/source/blender/blenkernel/BKE_dyntopo_set.hh @@ -71,7 +71,6 @@ template class DyntopoSet { void remove(T *key) { if (!elem_to_index_.contains(key)) { - printf("DyntopoSet::remove: key not in set\n"); return; } diff --git a/source/blender/blenkernel/intern/dyntopo.cc b/source/blender/blenkernel/intern/dyntopo.cc index b590f57da02..e7b7d974e85 100644 --- a/source/blender/blenkernel/intern/dyntopo.cc +++ b/source/blender/blenkernel/intern/dyntopo.cc @@ -918,6 +918,7 @@ bool check_face_is_tri(PBVH *pbvh, BMFace *f) BLI_heap_free(heap, nullptr); } + pbvh_bmesh_check_nodes(pbvh); bm_logstack_pop(); return false; @@ -991,8 +992,7 @@ bool destroy_nonmanifold_fins(PBVH *pbvh, BMEdge *e_root) } } - int nupdateflag = PBVH_UpdateOtherVerts | PBVH_UpdateDrawBuffers | PBVH_UpdateBB | - PBVH_UpdateTriAreas; + int nupdateflag = PBVH_UpdateDrawBuffers | PBVH_UpdateBB | PBVH_UpdateTriAreas; nupdateflag = nupdateflag | PBVH_UpdateNormals | PBVH_UpdateTris | PBVH_RebuildDrawBuffers; if (!minfs.size()) { @@ -1064,7 +1064,7 @@ bool destroy_nonmanifold_fins(PBVH *pbvh, BMEdge *e_root) pbvh->nodes[ni].flag |= (PBVHNodeFlags)nupdateflag; } - pbvh_bmesh_face_remove(pbvh, f, true, false, false); + pbvh_bmesh_face_remove(pbvh, f, true, true, false); BM_idmap_release(pbvh->bm_idmap, (BMElem *)f, true); BM_face_kill(pbvh->header.bm, f); } @@ -1097,6 +1097,8 @@ bool destroy_nonmanifold_fins(PBVH *pbvh, BMEdge *e_root) } } + pbvh_bmesh_check_nodes(pbvh); + bm_logstack_pop(); return true; #else @@ -2269,7 +2271,7 @@ void EdgeQueueContext::step() } modified = true; - pbvh_bmesh_collapse_edge(pbvh, e, e->v1, e->v2, nullptr, nullptr, this); + pbvh_bmesh_collapse_edge(pbvh, e, e->v1, e->v2, this); flushed_ = true; VALIDATE_LOG(pbvh->bm_log); break; @@ -2533,9 +2535,9 @@ static void pbvh_split_edges(EdgeQueueContext *eq_ctx, PBVH *pbvh, BMesh *bm, Sp #define SUBD_ADD_TO_QUEUE const int node_updateflag = PBVH_UpdateBB | PBVH_UpdateOriginalBB | PBVH_UpdateNormals | - PBVH_UpdateOtherVerts | PBVH_UpdateCurvatureDir | - PBVH_UpdateTriAreas | PBVH_UpdateDrawBuffers | - PBVH_RebuildDrawBuffers | PBVH_UpdateTris | PBVH_UpdateNormals; + PBVH_UpdateCurvatureDir | PBVH_UpdateTriAreas | + PBVH_UpdateDrawBuffers | PBVH_RebuildDrawBuffers | PBVH_UpdateTris | + PBVH_UpdateNormals; for (BMEdge *e : edges) { check_vert_fan_are_tris(pbvh, e->v1); diff --git a/source/blender/blenkernel/intern/dyntopo_collapse.cc b/source/blender/blenkernel/intern/dyntopo_collapse.cc index 9328d1b959b..1a0292979e4 100644 --- a/source/blender/blenkernel/intern/dyntopo_collapse.cc +++ b/source/blender/blenkernel/intern/dyntopo_collapse.cc @@ -12,6 +12,7 @@ #include "BLI_map.hh" #include "BLI_set.hh" #include "BLI_task.hh" +#include "BLI_timeit.hh" #include "BLI_vector.hh" #include "BLI_alloca.h" @@ -53,148 +54,8 @@ typedef struct TraceData { SculptSession *ss; } TraceData; -// copied from decimate modifier code -inline bool bm_edge_collapse_is_degenerate_topology(BMEdge *e_first) -{ - /* simply check that there is no overlap between faces and edges of each vert, - * (excluding the 2 faces attached to 'e' and 'e' its self) */ - - BMEdge *e_iter; - - /* clear flags on both disks */ - e_iter = e_first; - do { - if (!bm_edge_is_manifold_or_boundary(e_iter->l)) { - return true; - } - bm_edge_tag_disable(e_iter); - } while ((e_iter = BM_DISK_EDGE_NEXT(e_iter, e_first->v1)) != e_first); - - e_iter = e_first; - do { - if (!bm_edge_is_manifold_or_boundary(e_iter->l)) { - return true; - } - bm_edge_tag_disable(e_iter); - } while ((e_iter = BM_DISK_EDGE_NEXT(e_iter, e_first->v2)) != e_first); - - /* now enable one side... */ - e_iter = e_first; - do { - bm_edge_tag_enable(e_iter); - } while ((e_iter = BM_DISK_EDGE_NEXT(e_iter, e_first->v1)) != e_first); - - /* ... except for the edge we will collapse, we know that's shared, - * disable this to avoid false positive. We could be smart and never enable these - * face/edge tags in the first place but easier to do this */ - // bm_edge_tag_disable(e_first); - /* do inline... */ - { -#if 0 - BMIter iter; - BMIter liter; - BMLoop *l; - BMVert *v; - BM_ITER_ELEM (l, &liter, e_first, BM_LOOPS_OF_EDGE) { - BM_elem_flag_disable(l->f, BM_ELEM_TAG); - BM_ITER_ELEM (v, &iter, l->f, BM_VERTS_OF_FACE) { - BM_elem_flag_disable(v, BM_ELEM_TAG); - } - } -#else - /* we know each face is a triangle, no looping/iterators needed here */ - - BMLoop *l_radial; - BMLoop *l_face; - - l_radial = e_first->l; - l_face = l_radial; - BLI_assert(l_face->f->len == 3); - BM_elem_flag_disable(l_face->f, BM_ELEM_TAG); - BM_elem_flag_disable((l_face = l_radial)->v, BM_ELEM_TAG); - BM_elem_flag_disable((l_face = l_face->next)->v, BM_ELEM_TAG); - BM_elem_flag_disable((l_face->next)->v, BM_ELEM_TAG); - l_face = l_radial->radial_next; - if (l_radial != l_face) { - BLI_assert(l_face->f->len == 3); - BM_elem_flag_disable(l_face->f, BM_ELEM_TAG); - BM_elem_flag_disable((l_face = l_radial->radial_next)->v, BM_ELEM_TAG); - BM_elem_flag_disable((l_face = l_face->next)->v, BM_ELEM_TAG); - BM_elem_flag_disable((l_face->next)->v, BM_ELEM_TAG); - } -#endif - } - - /* and check for overlap */ - e_iter = e_first; - do { - if (bm_edge_tag_test(e_iter)) { - return true; - } - } while ((e_iter = BM_DISK_EDGE_NEXT(e_iter, e_first->v2)) != e_first); - - return false; -} - -void vert_ring_do_apply(BMVert *v, - std::function callback, - void *userdata, - int tag, - int facetag, - int depth) -{ - BMEdge *e = v->e; - - callback((BMElem *)v, userdata); - v->head.hflag &= ~tag; - - e = v->e; - do { - BMVert *v2 = BM_edge_other_vert(e, v); - - if (depth > 0) { - vert_ring_do_apply(v2, callback, userdata, tag, facetag, depth - 1); - } - - if (v2->head.hflag & tag) { - v2->head.hflag &= ~tag; - callback((BMElem *)v2, userdata); - } - if (e->head.hflag & tag) { - e->head.hflag &= ~tag; - callback((BMElem *)e, userdata); - } - - if (!e->l) { - continue; - } - - BMLoop *l = e->l; - do { - BMLoop *l2 = l; - - do { - if (l2->v->head.hflag & tag) { - l2->v->head.hflag &= ~tag; - callback((BMElem *)l2->v, userdata); - } - - if (l2->e->head.hflag & tag) { - l2->e->head.hflag &= ~tag; - callback((BMElem *)l2->e, userdata); - } - - if (l2->f->head.hflag & tag) { - l2->f->head.hflag &= ~tag; - callback((BMElem *)l2->f, userdata); - } - } while ((l2 = l2->next) != l); - } while ((l = l->radial_next) != e->l); - } while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e); -} - -const int COLLAPSE_TAG = BM_ELEM_INTERNAL_TAG; -const int COLLAPSE_FACE_TAG = BM_ELEM_TAG_ALT; +const int COLLAPSE_TAG = 32; +const int COLLAPSE_FACE_TAG = 64; static void collapse_ring_callback_pre(BMElem *elem, void *userdata) { @@ -223,7 +84,7 @@ static void collapse_ring_callback_pre(BMElem *elem, void *userdata) case BM_FACE: { BMFace *f = reinterpret_cast(elem); BM_log_face_removed(bm, data->pbvh->bm_log, f); - pbvh_bmesh_face_remove(data->pbvh, f, false, false, false); + pbvh_bmesh_face_remove(data->pbvh, f, false, true, true); BM_idmap_release(data->pbvh->bm_idmap, elem, false); break; } @@ -286,47 +147,247 @@ static void collapse_ring_callback_post(BMElem *elem, void *userdata) } } -static void vert_ring_do(BMVert *v, +static void tag_vert_ring(BMVert *v, const int tag, const int facetag) +{ + v->head.api_flag |= tag; + + BMEdge *e = v->e; + do { + BMVert *v_other = v == e->v1 ? e->v2 : e->v1; + + e->head.api_flag |= tag; + v_other->head.api_flag |= tag; + + BMLoop *l = e->l; + if (!l) { + continue; + } + do { + l->f->head.api_flag |= tag | facetag; + BMLoop *l2 = l; + do { + l2->v->head.api_flag |= tag; + l2->e->head.api_flag |= tag; + } while ((l2 = l2->next) != l); + } while ((l = l->radial_next) != e->l); + } while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e); +} + +template> +static void add_elem(T *elem, Vec &vec, int tag) +{ + if (elem->head.api_flag & tag) { + elem->head.api_flag &= ~tag; + vec.append(elem); + } +} + +template, + typename EV = Vector, + typename FV = Vector> + +static void add_vert_to_ring( + BMVert *v, const int tag, const int facetag, VV &verts, EV &edges, FV &faces) +{ + if (v->head.api_flag & tag) { + return; + } + + v->head.api_flag &= ~tag; + + if (!v->e) { + return; + } + + BMEdge *e = v->e; + do { + BMLoop *l = e->l; + if (!l) { + continue; + } + + do { + if (!(l->f->head.api_flag & facetag)) { + return; + } + } while ((l = l->radial_next) != e->l); + } while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e); + + verts.append(v); +} + +template, + typename EV = Vector, + typename FV = Vector> + +static void build_vert_ring( + BMVert *v, const int tag, const int facetag, VV &verts, EV &edges, FV &faces) +{ + add_elem(v, verts, tag); + + BMEdge *e = v->e; + do { + BMVert *v_other = v == e->v1 ? e->v2 : e->v1; + add_vert_to_ring(v_other, tag, facetag, verts, edges, faces); + + BMLoop *l = e->l; + bool bad = false; + + if (!l) { + continue; + } + do { + if (!(l->f->head.api_flag & facetag)) { + bad = true; + break; + } + + add_elem(l->f, faces, tag); + BMLoop *l2 = l; + do { + add_vert_to_ring(l2->v, tag, facetag, verts, edges, faces); + } while ((l2 = l2->next) != l); + } while ((l = l->radial_next) != e->l); + + if (!bad) { + add_elem(e, edges, tag); + } + else { + e->head.api_flag &= ~tag; + } + } while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e); + + e = v->e; + do { + BMLoop *l = e->l; + } while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e); +} + +static void vert_ring_do_(BMesh *bm, + BMVert *v, + BMVert *v_extra, + void (*callback)(BMElem *elem, void *userdata), + void *userdata, + const int tag, + const int facetag, + const int depth = 0) +{ + tag_vert_ring(v, tag, facetag); + if (v_extra) { + tag_vert_ring(v_extra, tag, facetag); + } + + Vector verts; + Vector edges; + Vector faces; + + build_vert_ring(v, tag, facetag, verts, edges, faces); + if (v_extra) { + build_vert_ring(v_extra, tag, facetag, verts, edges, faces); + } + + for (BMFace *f : faces) { + f->head.api_flag &= ~facetag; + } + + // printf("%d %d %d\n", verts.size(), edges.size(), faces.size()); + +#if 0 + BMIter iter; + BMVert *vi; + BMEdge *ei; + BMFace *fi; + BM_ITER_MESH (vi, &iter, bm, BM_VERTS_OF_MESH) { + vi->head.hflag &= ~BM_ELEM_SELECT; + } + BM_ITER_MESH (ei, &iter, bm, BM_EDGES_OF_MESH) { + ei->head.hflag &= ~BM_ELEM_SELECT; + } + BM_ITER_MESH (fi, &iter, bm, BM_FACES_OF_MESH) { + fi->head.hflag &= ~BM_ELEM_SELECT; + } +#endif + + for (BMFace *f : faces) { + f->head.hflag |= BM_ELEM_SELECT; + callback(reinterpret_cast(f), userdata); + } + + BMEdge *exist_e = v_extra ? BM_edge_exists(v, v_extra) : nullptr; + for (BMEdge *e : edges) { + if (exist_e == e) { + e->head.hflag &= ~BM_ELEM_SELECT; + } + else { + e->head.hflag |= BM_ELEM_SELECT; + } + callback(reinterpret_cast(e), userdata); + } + for (BMVert *v2 : verts) { + v2->head.hflag |= BM_ELEM_SELECT; + callback(reinterpret_cast(v2), userdata); + } +} + +template> +static void vert_ring_recurse(BMVert *v, FaceSet &faces, int depth) +{ + if (!v->e) { + return; + } + + BMEdge *e = v->e; + do { + BMVert *v2 = BM_edge_other_vert(e, v); + + if (!e->l) { + if (depth > 0) { + vert_ring_recurse(v2, faces, depth - 1); + } + continue; + } + + BMLoop *l = e->l; + do { + faces.add(l->f); + } while ((l = l->radial_next) != e->l); + + if (depth > 0) { + vert_ring_recurse(v2, faces, depth - 1); + } + } while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e); +} + +static bool vert_is_nonmanifold(BMVert *v) +{ + if (!v->e) { + return false; + } + + BMEdge *e = v->e; + do { + if (e->l->radial_next != e->l && e->l->radial_next->radial_next != e->l) { + return true; + } + } while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e); + + return false; +} + +static void vert_ring_do(BMesh *bm, + BMVert *v, BMVert *v_extra, void (*callback)(BMElem *elem, void *userdata), void *userdata, int /*tag*/, int /*facetag*/, - int /*depth*/) + int max_depth = 0) { blender::Set faces; - std::function recurse = [&](BMVert *v, int depth) { - if (!v->e) { - return; - } - - const int max_depth = 1; - BMEdge *e = v->e; - do { - BMVert *v2 = BM_edge_other_vert(e, v); - - if (!e->l) { - if (depth < max_depth) { - recurse(v2, depth + 1); - } - continue; - } - - BMLoop *l = e->l; - do { - faces.add(l->f); - } while ((l = l->radial_next) != e->l); - - if (depth < max_depth) { - recurse(v2, depth + 1); - } - } while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e); - }; - - recurse(v, 0); + vert_ring_recurse(v, faces, max_depth); if (v_extra) { - recurse(v_extra, 0); + vert_ring_recurse(v_extra, faces, max_depth); } blender::Set verts; @@ -371,6 +432,8 @@ static void vert_ring_do(BMVert *v, } while ((l = l->next) != f->l_first); } + // printf("%d %d %d\n", verts.size(), edges.size(), faces.size()); + for (BMFace *f : faces) { callback(reinterpret_cast(f), userdata); } @@ -557,13 +620,8 @@ bool pbvh_bmesh_collapse_edge_uvs( * This function is rather complicated. It has to * snap UVs, log geometry and free ids. */ -BMVert *pbvh_bmesh_collapse_edge(PBVH *pbvh, - BMEdge *e, - BMVert *v1, - BMVert *v2, - GHash *deleted_verts, - BLI_Buffer * /*deleted_faces*/, - EdgeQueueContext *eq_ctx) +BMVert *pbvh_bmesh_collapse_edge( + PBVH *pbvh, BMEdge *e, BMVert *v1, BMVert *v2, EdgeQueueContext *eq_ctx) { bm_logstack_push(); @@ -580,6 +638,8 @@ BMVert *pbvh_bmesh_collapse_edge(PBVH *pbvh, tdata.pbvh = pbvh; tdata.e = e; + pbvh_bmesh_check_nodes(pbvh); + const int mupdateflag = SCULPTFLAG_NEED_VALENCE; // updateflag |= SCULPTFLAG_NEED_TRIANGULATE; // to check for non-manifold flaps @@ -588,6 +648,8 @@ BMVert *pbvh_bmesh_collapse_edge(PBVH *pbvh, check_vert_fan_are_tris(pbvh, e->v1); check_vert_fan_are_tris(pbvh, e->v2); + pbvh_bmesh_check_nodes(pbvh); + pbvh_check_vert_boundary(pbvh, v1); pbvh_check_vert_boundary(pbvh, v2); @@ -620,20 +682,33 @@ BMVert *pbvh_bmesh_collapse_edge(PBVH *pbvh, return nullptr; } + /* Needed for vert_ring_do. */ const int tag = COLLAPSE_TAG; const int facetag = COLLAPSE_FACE_TAG; - const int log_rings = 1; + int vert_ring_maxdepth = 0; + + bool non_manifold = vert_is_nonmanifold(e->v1); + non_manifold |= vert_is_nonmanifold(e->v2); + + if (non_manifold) { + vert_ring_maxdepth++; + } /* Make sure original data is initialized before we snap it. */ BKE_pbvh_bmesh_check_origdata(eq_ctx->ss, v_conn, pbvh->stroke_id); BKE_pbvh_bmesh_check_origdata(eq_ctx->ss, v_del, pbvh->stroke_id); - if (deleted_verts) { - BLI_ghash_insert(deleted_verts, (void *)v_del, nullptr); - } - /* Remove topology from PBVH and insert into bmlog. */ - vert_ring_do(e->v1, e->v2, collapse_ring_callback_pre, &tdata, tag, facetag, log_rings - 1); + vert_ring_do(pbvh->header.bm, + e->v1, + e->v2, + collapse_ring_callback_pre, + &tdata, + tag, + facetag, + vert_ring_maxdepth); + + pbvh_bmesh_check_nodes(pbvh); /* Snap UVS. */ bool uvs_snapped = pbvh_bmesh_collapse_edge_uvs(pbvh, e, v_conn, v_del, eq_ctx); @@ -660,13 +735,14 @@ BMVert *pbvh_bmesh_collapse_edge(PBVH *pbvh, copy_v3_v3(v_conn->co, co); } - vert_ring_do(v_conn, + vert_ring_do(pbvh->header.bm, + v_conn, nullptr, collapse_ring_callback_post, static_cast(&tdata), tag, facetag, - log_rings - 1); + vert_ring_maxdepth); if (!v_conn->e) { printf("%s: pbvh error, v_conn->e was null\n", __func__); diff --git a/source/blender/blenkernel/intern/dyntopo_intern.hh b/source/blender/blenkernel/intern/dyntopo_intern.hh index eee4fb72c2c..c04719fc42b 100644 --- a/source/blender/blenkernel/intern/dyntopo_intern.hh +++ b/source/blender/blenkernel/intern/dyntopo_intern.hh @@ -321,8 +321,6 @@ BMVert *pbvh_bmesh_collapse_edge(PBVH *pbvh, BMEdge *e, BMVert *v1, BMVert *v2, - struct GHash *deleted_verts, - struct BLI_Buffer *deleted_faces, struct EdgeQueueContext *eq_ctx); extern "C" void bm_log_message(const char *fmt, ...); diff --git a/source/blender/blenkernel/intern/pbvh_bmesh.cc b/source/blender/blenkernel/intern/pbvh_bmesh.cc index 65fe70b90c9..3c447177c6d 100644 --- a/source/blender/blenkernel/intern/pbvh_bmesh.cc +++ b/source/blender/blenkernel/intern/pbvh_bmesh.cc @@ -124,14 +124,6 @@ void pbvh_bmesh_check_nodes_simple(PBVH *pbvh) void pbvh_bmesh_check_nodes(PBVH *pbvh) { - for (int i = 0; i < pbvh->totnode; i++) { - PBVHNode *node = pbvh->nodes + i; - - if (node->flag & PBVH_Leaf) { - pbvh_bmesh_check_other_verts(node); - } - } - BMVert *v; BMIter iter; @@ -139,7 +131,7 @@ void pbvh_bmesh_check_nodes(PBVH *pbvh) int ni = BM_ELEM_CD_GET_INT(v, pbvh->cd_vert_node_offset); if (ni >= 0 && (!v->e || !v->e->l)) { - _debugprint("wire vert had node reference: %p (type %d)\n", v, v->head.htype); + //_debugprint("wire vert had node reference: %p (type %d)\n", v, v->head.htype); // BM_ELEM_CD_SET_INT(v, pbvh->cd_vert_node_offset, DYNTOPO_NODE_NONE); } @@ -166,12 +158,15 @@ void pbvh_bmesh_check_nodes(PBVH *pbvh) _debugprint("vert in node->bm_other_verts"); } - BKE_pbvh_bmesh_check_valence(pbvh, (PBVHVertRef){.i = (intptr_t)v}); + if (pbvh->cd_valence != -1) { + BKE_pbvh_bmesh_check_valence(pbvh, (PBVHVertRef){.i = (intptr_t)v}); + int valence = BM_ELEM_CD_GET_INT(v, pbvh->cd_valence); - if (BM_vert_edge_count(v) != BM_ELEM_CD_GET_INT(v, pbvh->cd_valence)) { - _debugprint("cached vertex valence mismatch; old: %d, should be: %d\n", - mv->valence, - BM_vert_edge_count(v)); + if (BM_vert_edge_count(v) != valence) { + _debugprint("cached vertex valence mismatch; old: %d, should be: %d\n", + valence, + BM_vert_edge_count(v)); + } } } @@ -194,6 +189,11 @@ void pbvh_bmesh_check_nodes(PBVH *pbvh) } for (BMVert *v : *node->bm_unique_verts) { + if (BM_elem_is_free((BMElem *)v, BM_VERT)) { + printf("bm_unique_verts has freed vertex.\n"); + continue; + } + int ni = BM_ELEM_CD_GET_INT(v, pbvh->cd_vert_node_offset); if (ni != i) { @@ -227,7 +227,38 @@ void pbvh_bmesh_check_nodes(PBVH *pbvh) } } - for (BMVert *v : node->bm_other_verts) { + for (BMVert *v : *node->bm_other_verts) { + BMIter iter; + BMFace *f = nullptr; + + if (BM_elem_is_free((BMElem *)v, BM_VERT)) { + printf("bm_other_verts has freed vertex.\n"); + continue; + } + + int ni = int(node - pbvh->nodes); + + bool ok = false; + BM_ITER_ELEM (f, &iter, v, BM_FACES_OF_VERT) { + if (BM_ELEM_CD_GET_INT(f, pbvh->cd_face_node_offset) == ni) { + if (!node->bm_faces->contains(f)) { + _debugprint("Node does not contain f, but f has a node index to it\n"); + continue; + } + + ok = true; + break; + } + } + + if (!ok) { + int ni = BM_ELEM_CD_GET_INT(v, pbvh->cd_vert_node_offset); + _debugprint( + "v is in node.bm_other_verts but none of its faces are in node.bm_faces. owning node " + "(not this one): %d\n", + ni); + } + if (!v || v->head.htype != BM_VERT) { _debugprint("corruption in pbvh! bm_other_verts\n"); } @@ -332,7 +363,7 @@ static BMVert *pbvh_bmesh_vert_create(PBVH *pbvh, node->bm_unique_verts->add(v); BM_ELEM_CD_SET_INT(v, pbvh->cd_vert_node_offset, node_index); - node->flag |= PBVH_UpdateDrawBuffers | PBVH_UpdateBB | PBVH_UpdateTris | PBVH_UpdateOtherVerts; + node->flag |= PBVH_UpdateDrawBuffers | PBVH_UpdateBB | PBVH_UpdateTris; /* Log the new vertex */ BM_log_vert_added(pbvh->header.bm, pbvh->bm_log, v); @@ -402,7 +433,7 @@ BMFace *pbvh_bmesh_face_create(PBVH *pbvh, /* mark node for update */ node->flag |= PBVH_UpdateDrawBuffers | PBVH_UpdateNormals | PBVH_UpdateTris | - PBVH_UpdateOtherVerts | PBVH_UpdateCurvatureDir | PBVH_UpdateTriAreas; + PBVH_UpdateCurvatureDir | PBVH_UpdateTriAreas; node->flag &= ~PBVH_FullyHidden; /* Log the new face */ @@ -421,8 +452,7 @@ BMFace *pbvh_bmesh_face_create(PBVH *pbvh, node->bm_unique_verts->add(l->v); BM_ELEM_CD_SET_INT(l->v, cd_vert_node, node_index); - node->flag |= PBVH_UpdateDrawBuffers | PBVH_UpdateBB | PBVH_UpdateTris | - PBVH_UpdateOtherVerts; + node->flag |= PBVH_UpdateDrawBuffers | PBVH_UpdateBB | PBVH_UpdateTris; } pbvh_boundary_update_bmesh(pbvh, l->v); @@ -563,109 +593,62 @@ BMFace *BKE_pbvh_face_create_bmesh(PBVH *pbvh, return pbvh_bmesh_face_create(pbvh, ni, v_tri, e_tri, f_example, true, true); } -#define pbvh_bmesh_node_vert_use_count_is_equal(pbvh, node, v, n) \ - (pbvh_bmesh_node_vert_use_count_at_most(pbvh, node, v, (n) + 1) == n) - -static int pbvh_bmesh_node_vert_use_count_at_most(PBVH *pbvh, - PBVHNode *node, - BMVert *v, - const int count_max) -{ - int count = 0; - BMFace *f; - - BM_FACES_OF_VERT_ITER_BEGIN (f, v) { - PBVHNode *f_node = pbvh_bmesh_node_from_face(pbvh, f); - if (f_node == node) { - count++; - if (count == count_max) { - return count; - } - } - } - BM_FACES_OF_VERT_ITER_END; - - return count; -} - -/* Return a node that uses vertex 'v' other than its current owner */ -static PBVHNode *pbvh_bmesh_vert_other_node_find(PBVH *pbvh, BMVert *v) -{ - PBVHNode *current_node = pbvh_bmesh_node_from_vert(pbvh, v); - BMFace *f; - - BM_FACES_OF_VERT_ITER_BEGIN (f, v) { - PBVHNode *f_node = pbvh_bmesh_node_from_face(pbvh, f); - - if (f_node != current_node) { - return f_node; - } - } - BM_FACES_OF_VERT_ITER_END; - - return nullptr; -} - -static void pbvh_bmesh_vert_ownership_transfer(PBVH *pbvh, PBVHNode *new_owner, BMVert *v) -{ - PBVHNode *current_owner = pbvh_bmesh_node_from_vert(pbvh, v); - /* Mark node for update. */ - - if (current_owner) { - current_owner->flag |= PBVH_UpdateDrawBuffers | PBVH_UpdateBB; - - BLI_assert(current_owner != new_owner); - - /* Remove current ownership. */ - current_owner->bm_unique_verts->remove(v); - } - - /* Set new ownership. */ - BM_ELEM_CD_SET_INT(v, pbvh->cd_vert_node_offset, new_owner - pbvh->nodes); - new_owner->bm_unique_verts->add(v); - - /* Mark node for update. */ - new_owner->flag |= PBVH_UpdateDrawBuffers | PBVH_UpdateBB | PBVH_UpdateOtherVerts; -} - void pbvh_bmesh_vert_remove(PBVH *pbvh, BMVert *v) { /* never match for first time */ - int f_node_index_prev = DYNTOPO_NODE_NONE; const int updateflag = PBVH_UpdateDrawBuffers | PBVH_UpdateBB | PBVH_UpdateTris | - PBVH_UpdateNormals | PBVH_UpdateOtherVerts; + PBVH_UpdateNormals; - PBVHNode *v_node = pbvh_bmesh_node_from_vert(pbvh, v); - - if (v_node && v_node->bm_unique_verts) { - v_node->bm_unique_verts->remove(v); - v_node->flag |= (PBVHNodeFlags)updateflag; + int ni = BM_ELEM_CD_GET_INT(v, pbvh->cd_vert_node_offset); + if (ni != DYNTOPO_NODE_NONE) { + PBVHNode *node = pbvh->nodes + ni; + node->bm_unique_verts->remove(v); + node->flag |= (PBVHNodeFlags)updateflag; } BM_ELEM_CD_SET_INT(v, pbvh->cd_vert_node_offset, DYNTOPO_NODE_NONE); - /* Have to check each neighboring face's node */ - BMFace *f; - BM_FACES_OF_VERT_ITER_BEGIN (f, v) { - const int f_node_index = pbvh_bmesh_node_index_from_face(pbvh, f); + if (!v->e) { + return; + } - if (f_node_index == DYNTOPO_NODE_NONE) { + const int tag = 2; + + BMEdge *e = v->e; + do { + BMLoop *l = e->l; + if (!l) { continue; } - /* Faces often share the same node, - * quick check to avoid redundant set removal calls. - */ - if (f_node_index_prev != f_node_index) { - f_node_index_prev = f_node_index; + do { + l->f->head.api_flag |= tag; + } while ((l = l->radial_next) != e->l); + } while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e); - PBVHNode *f_node = &pbvh->nodes[f_node_index]; - f_node->flag |= (PBVHNodeFlags)updateflag; // flag update of bm_other_verts - - BLI_assert(!f_node->bm_unique_verts->contains(v)); + e = v->e; + do { + BMLoop *l = e->l; + if (!l) { + continue; } - } - BM_FACES_OF_VERT_ITER_END; + + do { + if (!(l->f->head.api_flag & tag)) { + continue; + } + + l->f->head.api_flag &= ~tag; + + int ni2 = BM_ELEM_CD_GET_INT(l->f, pbvh->cd_face_node_offset); + if (ni2 != DYNTOPO_NODE_NONE) { + PBVHNode *node = pbvh->nodes + ni2; + + node->flag |= PBVHNodeFlags(updateflag); + node->bm_other_verts->remove(v); + } + } while ((l = l->radial_next) != e->l); + } while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e); } void pbvh_bmesh_face_remove( @@ -674,8 +657,7 @@ void pbvh_bmesh_face_remove( PBVHNode *f_node = pbvh_bmesh_node_from_face(pbvh, f); if (!f_node || !(f_node->flag & PBVH_Leaf)) { - printf("%s: pbvh corruption\n", __func__); - fflush(stdout); + printf("%s: pbvh corruption.\n", __func__); return; } @@ -684,27 +666,47 @@ void pbvh_bmesh_face_remove( /* Check if any of this face's vertices need to be removed * from the node */ if (check_verts) { - BMLoop *l_first = BM_FACE_FIRST_LOOP(f); - BMLoop *l_iter = l_first; + int ni = int(f_node - pbvh->nodes); + + BMLoop *l = f->l_first; do { - BMVert *v = l_iter->v; - if (pbvh_bmesh_node_vert_use_count_is_equal(pbvh, f_node, v, 1)) { - if (BM_ELEM_CD_GET_INT(v, pbvh->cd_vert_node_offset) == f_node - pbvh->nodes) { - /* Find a different node that uses 'v' */ - PBVHNode *new_node = pbvh_bmesh_vert_other_node_find(pbvh, v); + bool owns_vert = BM_ELEM_CD_GET_INT(l->v, pbvh->cd_vert_node_offset) == ni; + bool ok = false; + int new_ni = DYNTOPO_NODE_NONE; - if (new_node) { - pbvh_bmesh_vert_ownership_transfer(pbvh, new_node, v); - } - else if (ensure_ownership_transfer && !BM_vert_face_count_is_equal(v, 1)) { - pbvh_bmesh_vert_remove(pbvh, v); + BMIter iter; + BMLoop *l2; + BM_ITER_ELEM (l2, &iter, l->v, BM_LOOPS_OF_VERT) { + int ni2 = BM_ELEM_CD_GET_INT(l2->f, pbvh->cd_face_node_offset); + if (l2->f != f && ni2 == ni) { + ok = true; + } - f_node->flag |= PBVH_RebuildNodeVerts | PBVH_UpdateOtherVerts; - // printf("failed to find new_node\n"); - } + if (ni2 != DYNTOPO_NODE_NONE && ni2 != ni) { + new_ni = ni2; } } - } while ((l_iter = l_iter->next) != l_first); + + if (!ok) { + if (owns_vert) { + f_node->bm_unique_verts->remove(l->v); + + if (new_ni != DYNTOPO_NODE_NONE) { + PBVHNode *new_node = &pbvh->nodes[new_ni]; + + new_node->bm_other_verts->remove(l->v); + new_node->bm_unique_verts->add(l->v); + BM_ELEM_CD_SET_INT(l->v, pbvh->cd_vert_node_offset, new_ni); + } + else { + BM_ELEM_CD_SET_INT(l->v, pbvh->cd_vert_node_offset, DYNTOPO_NODE_NONE); + } + } + else { + f_node->bm_other_verts->remove(l->v); + } + } + } while ((l = l->next) != f->l_first); } /* Remove face from node and top level */ @@ -718,7 +720,7 @@ void pbvh_bmesh_face_remove( /* mark node for update */ f_node->flag |= PBVH_UpdateDrawBuffers | PBVH_UpdateNormals | PBVH_UpdateTris | - PBVH_UpdateOtherVerts | PBVH_UpdateTriAreas | PBVH_UpdateCurvatureDir; + PBVH_UpdateTriAreas | PBVH_UpdateCurvatureDir; bm_logstack_pop(); } @@ -1033,6 +1035,7 @@ bool pbvh_bmesh_node_limit_ensure(PBVH *pbvh, int node_index) pbvh_bmesh_node_split(pbvh, bbc_array, node_index, false, 0); MEM_freeN(bbc_array); + pbvh_bmesh_check_nodes(pbvh); return true; } @@ -1058,10 +1061,9 @@ void bke_pbvh_insert_face_finalize(PBVH *pbvh, BMFace *f, const int ni) node->bm_faces->add(f); PBVHNodeFlags updateflag = PBVH_UpdateTris | PBVH_UpdateBB | PBVH_UpdateDrawBuffers | - PBVH_UpdateCurvatureDir | PBVH_UpdateOtherVerts; - updateflag |= PBVH_UpdateColor | PBVH_UpdateMask | PBVH_UpdateNormals | PBVH_UpdateOriginalBB; - updateflag |= PBVH_UpdateVisibility | PBVH_UpdateRedraw | PBVH_RebuildDrawBuffers | - PBVH_UpdateTriAreas; + PBVH_UpdateCurvatureDir | PBVH_UpdateColor | PBVH_UpdateMask | + PBVH_UpdateNormals | PBVH_UpdateOriginalBB | PBVH_UpdateVisibility | + PBVH_UpdateRedraw | PBVH_RebuildDrawBuffers | PBVH_UpdateTriAreas; node->flag |= updateflag; @@ -1074,26 +1076,22 @@ void bke_pbvh_insert_face_finalize(PBVH *pbvh, BMFace *f, const int ni) BB_expand(&node->orig_vb, BM_ELEM_CD_PTR(l->v, pbvh->cd_origco)); if (ni2 == DYNTOPO_NODE_NONE) { - BM_ELEM_CD_SET_INT(l->v, pbvh->cd_vert_node_offset, ni); node->bm_unique_verts->add(l->v); + BM_ELEM_CD_SET_INT(l->v, pbvh->cd_vert_node_offset, ni); + continue; } - else { - PBVHNode *node2 = pbvh->nodes + ni2; - if (ni != ni2) { - node->bm_other_verts->add(l->v); - } + PBVHNode *node2 = pbvh->nodes + ni2; - node2->flag |= updateflag; - - float *origco = pbvh->cd_origco != -1 ? BM_ELEM_CD_PTR(l->v, pbvh->cd_origco) : - l->v->co; + if (ni2 != ni) { + node->bm_other_verts->add(l->v); BB_expand(&node2->vb, l->v->co); - BB_expand(&node2->orig_vb, origco); + BB_expand(&node2->orig_vb, BM_ELEM_CD_PTR(l->v, pbvh->cd_origco)); + + node2->flag |= updateflag; } - l = l->next; - } while (l != f->l_first); + } while ((l = l->next) != f->l_first); } void bke_pbvh_insert_face(PBVH *pbvh, struct BMFace *f) @@ -1247,6 +1245,7 @@ static void pbvh_bmesh_regen_node_verts(PBVH *pbvh, PBVHNode *node, bool report) if (ni2 == ni) { node->bm_unique_verts->add(l->v); + BM_ELEM_CD_SET_INT(l->v, cd_vert_node, ni); } else { node->bm_other_verts->add(l->v); @@ -1272,11 +1271,12 @@ static void pbvh_bmesh_regen_node_verts(PBVH *pbvh, PBVHNode *node, bool report) int ni2 = BM_ELEM_CD_GET_INT(f2, pbvh->cd_face_node_offset); if (ni2 >= 0) { - BM_ELEM_CD_SET_INT(v, pbvh->cd_vert_node_offset, ni2); - PBVHNode *node = pbvh->nodes + ni2; + PBVHNode *node2 = pbvh->nodes + ni2; - node->bm_unique_verts->add(v); - node->bm_other_verts->remove(v); + node2->bm_unique_verts->add(v); + node2->bm_other_verts->remove(v); + + BM_ELEM_CD_SET_INT(v, pbvh->cd_vert_node_offset, ni2); ok = true; break; @@ -1713,8 +1713,8 @@ static void pbvh_bmesh_node_limit_ensure_fast(PBVH *pbvh, if (bbc->bcentroid[axis] > mid) { int i_iter = end - num_child2 - 1; int candidate = -1; - /* found a face that should be part of another node, look for a face to substitute with */ + /* Found a face that should be part of another node, look for a face to substitute with. */ for (; i_iter > i; i_iter--) { BMFace *f_iter = nodeinfo[i_iter]; const BBC *bbc_iter = &bbc_array[BM_elem_index_get(f_iter)]; @@ -1730,13 +1730,15 @@ static void pbvh_bmesh_node_limit_ensure_fast(PBVH *pbvh, BMFace *tmp = nodeinfo[i]; nodeinfo[i] = nodeinfo[candidate]; nodeinfo[candidate] = tmp; - /* increase both counts */ + + /* Increase both counts. */ num_child1++; num_child2++; } else { - /* not finding candidate means second half of array part is full of - * second node parts, just increase the number of child nodes for it */ + /* Not finding candidate means second half of array part is full of + * second node parts, just increase the number of child nodes for it. + */ num_child2++; } } @@ -1745,7 +1747,7 @@ static void pbvh_bmesh_node_limit_ensure_fast(PBVH *pbvh, } } - /* ensure at least one child in each node */ + /* Ensure at least one child in each node. */ if (num_child2 == 0) { num_child2++; num_child1--; @@ -1755,8 +1757,9 @@ static void pbvh_bmesh_node_limit_ensure_fast(PBVH *pbvh, num_child2--; } - /* at this point, faces should have been split along the array range sequentially, - * each sequential part belonging to one node only */ + /* At this point, faces should have been split along the array range sequentially, + * each sequential part belonging to one node only. + */ BLI_assert((num_child1 + num_child2) == node->totface); node->child1 = child1 = (FastNodeBuildInfo *)BLI_memarena_alloc(arena, @@ -1835,6 +1838,7 @@ static void pbvh_bmesh_create_leaf_fast_task_cb(void *__restrict userdata, BBC *bbc = &bbc_array[BM_elem_index_get(f)]; /* Update ownership of faces */ + n->bm_faces->add(f); BM_ELEM_CD_SET_INT(f, cd_face_node_offset, node_index); @@ -1845,16 +1849,21 @@ static void pbvh_bmesh_create_leaf_fast_task_cb(void *__restrict userdata, BMVert *v = l_iter->v; int old = BM_ELEM_CD_GET_INT(v, pbvh->cd_vert_node_offset); + ; + int32_t *ptr = static_cast( + POINTER_OFFSET(v->head.data, pbvh->cd_vert_node_offset)); - char *ptr = (char *)v->head.data; - ptr += pbvh->cd_vert_node_offset; - - if (old == DYNTOPO_NODE_NONE && - atomic_cas_int32((int32_t *)ptr, DYNTOPO_NODE_NONE, node_index) == DYNTOPO_NODE_NONE) - { - n->bm_unique_verts->add(v); + if (BM_ELEM_CD_GET_INT(v, pbvh->cd_vert_node_offset) == DYNTOPO_NODE_NONE) { + if (old == DYNTOPO_NODE_NONE && + atomic_cas_int32(ptr, DYNTOPO_NODE_NONE, node_index) == DYNTOPO_NODE_NONE) + { + n->bm_unique_verts->add(v); + } + else { + n->bm_other_verts->add(v); + } } - else { + else if (old != node->node_index) { n->bm_other_verts->add(v); } } while ((l_iter = l_iter->next) != l_first); @@ -1881,7 +1890,6 @@ static void pbvh_bmesh_create_nodes_fast_recursive_create(PBVH *pbvh, BBC *bbc_array, struct FastNodeBuildInfo *node) { - /* two cases, node does not have children or does have children */ if (node->child1) { int children_offset = pbvh->totnode; pbvh_grow_nodes(pbvh, pbvh->totnode + 2); @@ -2286,6 +2294,38 @@ void BKE_pbvh_set_idmap(PBVH *pbvh, BMIdMap *idmap) pbvh->bm_idmap = idmap; } +#if 0 +PBVH *global_debug_pbvh = nullptr; + +extern "C" void debug_pbvh_on_vert_kill(BMVert *v) +{ + PBVH *pbvh = global_debug_pbvh; + + if (!pbvh) { + return; + } + + for (int i = 0; i < pbvh->totnode; i++) { + PBVHNode &node = pbvh->nodes[i]; + + if (!(node.flag & PBVH_Leaf)) { + continue; + } + + for (BMVert *v2 : *node.bm_unique_verts) { + if (v2 == v) { + printf("Error! Vertex %p is still in bm_unique_verts\n", v); + } + } + for (BMVert *v2 : *node.bm_other_verts) { + if (v2 == v) { + printf("Error! Vertex still in bm_other_verts\n"); + } + } + } +} +#endif + /* Build a PBVH from a BMesh */ void BKE_pbvh_build_bmesh(PBVH *pbvh, Mesh *me, @@ -2301,8 +2341,6 @@ void BKE_pbvh_build_bmesh(PBVH *pbvh, const int cd_origco, const int cd_origno) { - // coalese_pbvh(pbvh, bm); - pbvh->bm_idmap = idmap; pbvh->cd_face_area = cd_face_areas; @@ -2470,6 +2508,8 @@ void BKE_pbvh_build_bmesh(PBVH *pbvh, areabuf[area_dst_i] = areabuf[area_src_i]; } } + + pbvh_bmesh_check_nodes(pbvh); } void BKE_pbvh_set_bm_log(PBVH *pbvh, BMLog *log) @@ -2670,7 +2710,7 @@ void pbvh_bmesh_check_other_verts(PBVHNode *node) } } -static void pbvh_init_tribuf(PBVHNode *node, PBVHTriBuf *tribuf) +static void pbvh_init_tribuf(PBVHNode * /*node*/, PBVHTriBuf *tribuf) { tribuf->tottri = 0; tribuf->tris_size = 0; @@ -2731,8 +2771,6 @@ bool BKE_pbvh_bmesh_check_tris(PBVH *pbvh, PBVHNode *node) cd_uvs[i] = bm->ldata.layers[idx].offset; } - node->flag |= PBVH_UpdateOtherVerts; - int mat_map[MAXMAT]; for (int i = 0; i < MAXMAT; i++) { diff --git a/source/blender/bmesh/intern/bmesh_core.c b/source/blender/bmesh/intern/bmesh_core.c index ea1b9a87605..d9bae4e6822 100644 --- a/source/blender/bmesh/intern/bmesh_core.c +++ b/source/blender/bmesh/intern/bmesh_core.c @@ -2816,24 +2816,6 @@ BMVert *bmesh_kernel_join_vert_kill_edge( } #ifdef JVKE_DEBUG -# if 0 /* "Inverted pyramid" situations will trigger this check */ - if (v_conn && v_conn->e) { - BMEdge *e = v_conn->e; - do { - BMLoop *l = e->l; - - if (!l) { - continue; - } - - /* boundary? */ - if (l == l->radial_next && !have_boundary) { - trigger_jvke_error(IS_LOOP_WRONG_RADIAL_LENGTH, saved_obj); - } - } while ((e = BM_DISK_EDGE_NEXT(e, v_conn)) != v_conn->e); - } -# endif - bm_local_obj_free(saved_obj, buf); #endif diff --git a/source/blender/editors/sculpt_paint/sculpt.cc b/source/blender/editors/sculpt_paint/sculpt.cc index 306569170cb..07ceae80551 100644 --- a/source/blender/editors/sculpt_paint/sculpt.cc +++ b/source/blender/editors/sculpt_paint/sculpt.cc @@ -3968,7 +3968,7 @@ static void sculpt_topology_update(Sculpt *sd, // mode |= PBVH_Collapse | PBVH_Subdivide; } - int edge_multiply = 1 + int(ss->cached_dyntopo.quality * 50.0f); + int edge_multiply = 1 + int(powf(ss->cached_dyntopo.quality, 3.0f) * 50.0f); SculptSearchSphereData sdata{}; sdata.ss = ss, sdata.sd = sd, sdata.ob = ob; -- 2.30.2 From 6c45d0b0d01aef3f49280bcbe1efc06312c789f2 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Sat, 3 Jun 2023 02:38:56 -0700 Subject: [PATCH 154/279] temp-sculpt-dyntopo: Fix a few bugs with cleanup mode --- source/blender/blenkernel/intern/dyntopo.cc | 102 +++++++----------- .../blenkernel/intern/dyntopo_collapse.cc | 2 +- source/blender/editors/sculpt_paint/sculpt.cc | 3 +- 3 files changed, 42 insertions(+), 65 deletions(-) diff --git a/source/blender/blenkernel/intern/dyntopo.cc b/source/blender/blenkernel/intern/dyntopo.cc index e7b7d974e85..ef8a6498bc1 100644 --- a/source/blender/blenkernel/intern/dyntopo.cc +++ b/source/blender/blenkernel/intern/dyntopo.cc @@ -804,6 +804,7 @@ bool check_face_is_tri(PBVH *pbvh, BMFace *f) BMLoop *l = f->l_first; do { validate_vert(pbvh, l->v, CHECK_VERT_ALL); + dyntopo_add_flag(pbvh, l->v, SCULPTFLAG_NEED_VALENCE); if (l->e->head.index == -1) { l->e->head.index = 0; @@ -857,8 +858,13 @@ bool check_face_is_tri(PBVH *pbvh, BMFace *f) f = nullptr; } + BMLoop *l = f2->l_first; + do { + dyntopo_add_flag(pbvh, l->v, SCULPTFLAG_NEED_VALENCE); + } while ((l = l->next) != f2->l_first); + BM_idmap_release(pbvh->bm_idmap, (BMElem *)dbl->link, true); - BM_face_kill(pbvh->header.bm, (BMFace *)dbl->link); + BM_face_kill(pbvh->header.bm, f2); MEM_freeN(dbl); dbl = next; @@ -894,6 +900,8 @@ bool check_face_is_tri(PBVH *pbvh, BMFace *f) do { if (l->e->head.index == -1) { BM_log_edge_added(pbvh->header.bm, pbvh->bm_log, l->e); + dyntopo_add_flag(pbvh, l->v, SCULPTFLAG_NEED_VALENCE); + dyntopo_add_flag(pbvh, l->next->v, SCULPTFLAG_NEED_VALENCE); l->e->head.index = 0; } } while ((l = l->next) != f2->l_first); @@ -1633,8 +1641,8 @@ static bool cleanup_valence_3_4(EdgeQueueContext *ectx, PBVH *pbvh) validate_vert(pbvh, v, CHECK_VERT_ALL); BKE_pbvh_bmesh_check_valence(pbvh, {(intptr_t)v}); - int val = BM_ELEM_CD_GET_INT(v, pbvh->cd_valence); + if (val != 4 && val != 3) { continue; } @@ -1830,15 +1838,16 @@ static bool cleanup_valence_3_4(EdgeQueueContext *ectx, PBVH *pbvh) } BMFace *f2 = nullptr; + bool ok2 = false; if (val == 4) { vs[0] = ls[0]->v; vs[1] = ls[2]->v; vs[2] = ls[3]->v; - } - bool ok2 = val == 4 && vs[0] != vs[2] && vs[2] != vs[3] && vs[0] != vs[3]; - ok2 = ok2 && !BM_face_exists(vs, 3); + ok2 = vs[0] != vs[1] && vs[1] != vs[2] && vs[2] != vs[0]; + ok2 = ok2 && !BM_face_exists(vs, 3); + } if (ok2) { pbvh_boundary_update_bmesh(pbvh, vs[0]); @@ -2024,6 +2033,8 @@ EdgeQueueContext::EdgeQueueContext(BrushTester *brush_tester_, unified_edge_queue_create(this, pbvh, mode & (PBVH_LocalSubdivide | PBVH_LocalCollapse)); } + totop = 0; + if ((mode & PBVH_Subdivide) && (mode & PBVH_Collapse)) { ops[0] = PBVH_Subdivide; ops[1] = PBVH_Collapse; @@ -2048,11 +2059,17 @@ EdgeQueueContext::EdgeQueueContext(BrushTester *brush_tester_, max_steps = (DYNTOPO_MAX_ITER * edge_limit_multiply) << (totop - 1); max_subd = max_steps >> (totop - 1); - split_edges_size = steps[0]; - split_edges = (BMEdge **)MEM_malloc_arrayN(split_edges_size, sizeof(void *), __func__); - etot = 0; + if (totop > 0) { + split_edges_size = steps[0]; + split_edges = (BMEdge **)MEM_malloc_arrayN(split_edges_size, sizeof(void *), __func__); + etot = 0; - subd_edges.clear(); + subd_edges.clear(); + } + else { + split_edges = nullptr; + split_edges_size = 0; + } } void EdgeQueueContext::flush_subdivision() @@ -2575,44 +2592,11 @@ static void pbvh_split_edges(EdgeQueueContext *eq_ctx, PBVH *pbvh, BMesh *bm, Sp /* Tag edges and faces to split. */ for (int i = 0; i < edges.size(); i++) { BMEdge *e = edges[i]; - BMLoop *l = e->l; e->head.index = 0; e->head.hflag |= SPLIT_TAG; } - Vector edges2; - - if (eq_ctx->mode & PBVH_Cleanup) { - /* Don't allow singletons that produce 3/4 valence verts. */ - for (BMEdge *e : edges) { - BMLoop *l = e->l; - - if (!l) { - continue; - } - - bool ok = false; - do { - BMLoop *l2 = l; - do { - if (l2->e != e && l2->e->head.hflag & SPLIT_TAG) { - ok = true; - break; - } - } while ((l2 = l2->next) != l); - } while (!ok && (l = l->radial_next) != e->l); - - if (ok) { - edges2.append(e); - } - else { - e->head.hflag &= ~SPLIT_TAG; - } - } - edges = edges2; - } - for (int i = 0; i < edges.size(); i++) { BMEdge *e = edges[i]; BMLoop *l = e->l; @@ -2901,20 +2885,10 @@ static void pbvh_split_edges(EdgeQueueContext *eq_ctx, PBVH *pbvh, BMesh *bm, Sp bool log_edge = true; BMFace *newf = nullptr; - BMEdge *exist_e; + BMEdge *exist_e = BM_edge_exists(v1, v2); - if ((exist_e = BM_edge_exists(v1, v2))) { + if (exist_e) { log_edge = false; - - BMLoop *l1 = exist_e->l; - - if (l1 && l1->f == f2) { - l1 = l1->radial_next; - } - - if (l1 && l1->f != f2) { - // newf = l1->f; - } } else { newf = BM_face_split(bm, f2, l1, l2, &rl, nullptr, false); @@ -3007,7 +2981,7 @@ static void pbvh_split_edges(EdgeQueueContext *eq_ctx, PBVH *pbvh, BMesh *bm, Sp if (w > eq_ctx->limit_len_max_sqr) { add_split_edge_recursive(eq_ctx, e->l, w, eq_ctx->limit_len_max, 0); } - else if (w < eq_ctx->limit_len_min_sqr) { + else if (w < eq_ctx->limit_len_min_sqr && (eq_ctx->mode & PBVH_Collapse)) { edge_queue_insert_unified(eq_ctx, e, w); } } @@ -3018,9 +2992,13 @@ static void pbvh_split_edges(EdgeQueueContext *eq_ctx, PBVH *pbvh, BMesh *bm, Sp } void detail_size_set(PBVH *pbvh, float detail_size, float detail_range) { - pbvh->bm_detail_range = detail_range == 0.0f ? 0.4f : max_ff(detail_range, 0.1f); + detail_range = max_ff(detail_range, 0.1f); + + detail_size /= detail_range; + + pbvh->bm_detail_range = detail_range; pbvh->bm_max_edge_len = detail_size; - pbvh->bm_min_edge_len = pbvh->bm_max_edge_len * pbvh->bm_detail_range; + pbvh->bm_min_edge_len = detail_size * detail_range; } } // namespace blender::bke::dyntopo @@ -3860,14 +3838,14 @@ inline void reproject_bm_data( } axis_dominant_v3_to_m3(axis_mat, axis_dominant); - int i = 0; + int l_i = 0; l_iter = l_first = BM_FACE_FIRST_LOOP(f_src); do { float co2d[2]; mul_v2_m3v3(co2d, axis_mat, l_iter->v->co); - myinterp::copy_v2_v2(cos_2d[i], co2d); + myinterp::copy_v2_v2(cos_2d[l_i], co2d); - blocks[i] = l_iter->head.data; + blocks[l_i] = l_iter->head.data; float len_sq = len_squared_v3v3(l_iter->v->co, l_iter->next->v->co); if (len_sq < FLT_EPSILON * 100.0f) { @@ -3875,9 +3853,9 @@ inline void reproject_bm_data( } if (do_vertex) { - vblocks[i] = l_iter->v->head.data; + vblocks[l_i] = l_iter->v->head.data; } - } while ((void)i++, (l_iter = l_iter->next) != l_first); + } while ((void)l_i++, (l_iter = l_iter->next) != l_first); mul_v2_m3v3(co, axis_mat, l_dst->v->co); diff --git a/source/blender/blenkernel/intern/dyntopo_collapse.cc b/source/blender/blenkernel/intern/dyntopo_collapse.cc index 1a0292979e4..82bf9629886 100644 --- a/source/blender/blenkernel/intern/dyntopo_collapse.cc +++ b/source/blender/blenkernel/intern/dyntopo_collapse.cc @@ -366,7 +366,7 @@ static bool vert_is_nonmanifold(BMVert *v) BMEdge *e = v->e; do { - if (e->l->radial_next != e->l && e->l->radial_next->radial_next != e->l) { + if (e->l && e->l->radial_next != e->l && e->l->radial_next->radial_next != e->l) { return true; } } while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e); diff --git a/source/blender/editors/sculpt_paint/sculpt.cc b/source/blender/editors/sculpt_paint/sculpt.cc index 07ceae80551..bc157cb4d86 100644 --- a/source/blender/editors/sculpt_paint/sculpt.cc +++ b/source/blender/editors/sculpt_paint/sculpt.cc @@ -6409,8 +6409,7 @@ static void sculpt_stroke_update_step(bContext *C, else { /* Relative mode. */ blender::bke::dyntopo::detail_size_set(ss->pbvh, (ss->cache->radius / ss->cache->dyntopo_pixel_radius) * - (ss->cached_dyntopo.detail_size * U.pixelsize) / - ss->cached_dyntopo.detail_range, + (ss->cached_dyntopo.detail_size * U.pixelsize), ss->cached_dyntopo.detail_range); } -- 2.30.2 From 45a68a54f63872e7bcc8524877066f03af88c430 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Sat, 3 Jun 2023 03:00:13 -0700 Subject: [PATCH 155/279] temp-sculpt-dyntopo: Fix crash when no uv layers --- source/blender/blenkernel/intern/pbvh_bmesh.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/source/blender/blenkernel/intern/pbvh_bmesh.cc b/source/blender/blenkernel/intern/pbvh_bmesh.cc index 3c447177c6d..a403dd6d8cd 100644 --- a/source/blender/blenkernel/intern/pbvh_bmesh.cc +++ b/source/blender/blenkernel/intern/pbvh_bmesh.cc @@ -1997,12 +1997,14 @@ void BKE_pbvh_update_vert_boundary(int cd_faceset_offset, BMVert *v, const CustomData *ldata, const int totuv, - const bool do_uvs, + const bool /*do_uvs*/, float sharp_angle_limit) { int newflag = *BM_ELEM_CD_PTR(v, cd_flag); int boundflag = 0; + const bool do_uvs = totuv > 0; + BMEdge *e = v->e; newflag &= ~(SCULPTFLAG_VERT_FSET_HIDDEN | SCULPTFLAG_PBVH_BOUNDARY); -- 2.30.2 From fa8959a8817c87863fc205ef6819a8dfb085aa63 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Sat, 3 Jun 2023 03:24:30 -0700 Subject: [PATCH 156/279] temp-sculpt-dyntopo: Fix another crash related to uvs --- source/blender/editors/sculpt_paint/sculpt_api.cc | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/source/blender/editors/sculpt_paint/sculpt_api.cc b/source/blender/editors/sculpt_paint/sculpt_api.cc index e5794c24696..168bf89becd 100644 --- a/source/blender/editors/sculpt_paint/sculpt_api.cc +++ b/source/blender/editors/sculpt_paint/sculpt_api.cc @@ -229,8 +229,7 @@ eSculptBoundary SCULPT_edge_is_boundary(const SculptSession *ss, ret |= fset1 != fset2 ? SCULPT_BOUNDARY_FACE_SET : 0; } - if (e->l && (typemask & SCULPT_BOUNDARY_UV)) { -#if 1 + if (e->l && (typemask & SCULPT_BOUNDARY_UV) && ss->bm->ldata.typemap[CD_PROP_FLOAT2] != -1) { CustomData *ldata = &ss->bm->ldata; int base = ldata->typemap[CD_PROP_FLOAT2]; @@ -261,14 +260,6 @@ eSculptBoundary SCULPT_edge_is_boundary(const SculptSession *ss, } } while ((l = l->radial_next) != e->l); } -#else - int b1 = BM_ELEM_CD_GET_INT(e->v1, ss->attrs.boundary_flags->bmesh_cd_offset); - int b2 = BM_ELEM_CD_GET_INT(e->v2, ss->attrs.boundary_flags->bmesh_cd_offset); - - if ((b1 & SCULPT_BOUNDARY_UV) && (b2 & SCULPT_BOUNDARY_UV)) { - ret |= SCULPT_BOUNDARY_UV; - } -#endif } uv_outer: -- 2.30.2 From 9cd4f0d4d30a51f1a82a50dcb6a38be415939d13 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Sun, 4 Jun 2023 06:22:44 -0700 Subject: [PATCH 157/279] temp-sculpt-dyntopo: Move edge collapse code to C++. * The new code is a return to the original tracing-based callback system for informing PBVH of changes to the mesh. This ended up saving quite a lot of memory in undo steps and is much faster too. * Mercifully C++ templates make this much less painful than the original C version of tracer callbacks. * New code is still in somewhat of a wip state. * Also fixed some bugs in attribute reprojection --- source/blender/blenkernel/intern/dyntopo.cc | 133 ++- .../blenkernel/intern/dyntopo_collapse.cc | 148 ++- .../blenkernel/intern/dyntopo_intern.hh | 62 +- .../blender/blenkernel/intern/pbvh_bmesh.cc | 12 +- .../blender/blenkernel/intern/pbvh_intern.hh | 14 +- source/blender/bmesh/CMakeLists.txt | 2 + source/blender/bmesh/intern/bmesh_collapse.cc | 15 + source/blender/bmesh/intern/bmesh_collapse.hh | 1012 +++++++++++++++++ source/blender/bmesh/intern/bmesh_core.c | 941 +-------------- source/blender/bmesh/intern/bmesh_log.cc | 8 +- source/blender/bmesh/intern/bmesh_private.h | 40 + .../bmesh/operators/bmo_removedoubles.c | 4 +- .../editors/sculpt_paint/sculpt_undo.cc | 4 +- 13 files changed, 1354 insertions(+), 1041 deletions(-) create mode 100644 source/blender/bmesh/intern/bmesh_collapse.cc create mode 100644 source/blender/bmesh/intern/bmesh_collapse.hh diff --git a/source/blender/blenkernel/intern/dyntopo.cc b/source/blender/blenkernel/intern/dyntopo.cc index ef8a6498bc1..f9c59643670 100644 --- a/source/blender/blenkernel/intern/dyntopo.cc +++ b/source/blender/blenkernel/intern/dyntopo.cc @@ -85,7 +85,7 @@ static void surface_smooth_v_safe( } PBVHVertRef vertex = {reinterpret_cast(v)}; - if (stroke_id_test_no_update(ss, vertex, STROKEID_USER_ORIGINAL)) { + if (stroke_id_test(ss, vertex, STROKEID_USER_ORIGINAL)) { copy_v3_v3(origco1, v->co); copy_v3_v3(origno1, v->no); } @@ -126,8 +126,24 @@ static void surface_smooth_v_safe( BMVert *v2 = e->v1 == v ? e->v2 : e->v1; PBVHVertRef vertex2 = {reinterpret_cast(v2)}; - float w = len_squared_v3v3(e->v1->co, e->v2->co); + float w; + if (e->l && e->l->f->len == 3) { + BMLoop *l = e->l; + float w1 = area_tri_v3(l->v->co, l->next->v->co, l->prev->v->co); + + if (l->radial_next != l) { + l = l->radial_next; + float w2 = area_tri_v3(l->v->co, l->next->v->co, l->prev->v->co); + w = (w1 + w2) * 0.5f; + } + else { + w = len_squared_v3v3(e->v1->co, e->v2->co); + } + } + else { + w = len_squared_v3v3(e->v1->co, e->v2->co); + } /* Note: we can't validate the boundary flags from with a thread * so they may not be up to date. */ @@ -140,19 +156,20 @@ static void surface_smooth_v_safe( } sub_v3_v3v3(tan, v2->co, v->co); - float d = dot_v3v3(tan, v->no); + float d = dot_v3v3(tan, v->no); madd_v3_v3fl(tan, v->no, -d * 0.95f); madd_v3_v3fl(co, tan, w); + float *origco2; if (!stroke_id_test_no_update(ss, vertex2, STROKEID_USER_ORIGINAL)) { - sub_v3_v3v3( - tan, blender::bke::paint::vertex_attr_ptr(vertex2, ss->attrs.orig_co), origco1); + origco2 = blender::bke::paint::vertex_attr_ptr(vertex2, ss->attrs.orig_co); } else { - sub_v3_v3v3(tan, v2->co, origco1); + origco2 = v2->co; } + sub_v3_v3v3(tan, origco2, origco1); d = dot_v3v3(tan, origno1); madd_v3_v3fl(tan, origno1, -d * 0.95f); madd_v3_v3fl(origco, tan, w); @@ -752,7 +769,9 @@ static void unified_edge_queue_task_cb(void *__restrict userdata, * but tangentially to surface. We can stochastically skip this and still get the * benefit to convergence. */ - if (do_smooth && rand.get_float() > 0.75f) { + if (do_smooth && rand.get_float() > 0.75f && + BM_ELEM_CD_GET_INT(l_iter->v, pbvh->cd_vert_node_offset) == ni) + { PBVHVertRef sv = {(intptr_t)l_iter->v}; surface_smooth_v_safe(eq_ctx->ss, tdata->pbvh, @@ -880,6 +899,7 @@ bool check_face_is_tri(PBVH *pbvh, BMFace *f) BMLoop *l = f2->l_first; do { dyntopo_add_flag(pbvh, l->v, SCULPTFLAG_NEED_VALENCE); + validate_edge(pbvh, l->e); } while ((l = l->next) != f2->l_first); } @@ -902,6 +922,7 @@ bool check_face_is_tri(PBVH *pbvh, BMFace *f) BM_log_edge_added(pbvh->header.bm, pbvh->bm_log, l->e); dyntopo_add_flag(pbvh, l->v, SCULPTFLAG_NEED_VALENCE); dyntopo_add_flag(pbvh, l->next->v, SCULPTFLAG_NEED_VALENCE); + validate_edge(pbvh, l->e); l->e->head.index = 0; } } while ((l = l->next) != f2->l_first); @@ -915,6 +936,7 @@ bool check_face_is_tri(PBVH *pbvh, BMFace *f) if (f) { BKE_pbvh_bmesh_add_face(pbvh, f, false, true); + validate_face(pbvh, f, CHECK_FACE_MANIFOLD); BM_log_face_added(pbvh->header.bm, pbvh->bm_log, f); } @@ -1260,7 +1282,7 @@ static void unified_edge_queue_create(EdgeQueueContext *eq_ctx, TaskParallelSettings settings; BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = !eq_ctx->reproject_cdata; + settings.use_threading = true; //! eq_ctx->reproject_cdata; #ifdef DYNTOPO_NO_THREADING settings.use_threading = false; @@ -1883,7 +1905,7 @@ static bool cleanup_valence_3_4(EdgeQueueContext *ectx, PBVH *pbvh) f2->no, f2->l_first->v->co, f2->l_first->next->v->co, f2->l_first->prev->v->co); BM_log_face_added(pbvh->header.bm, pbvh->bm_log, f2); - validate_face(pbvh, f2, CHECK_FACE_NONE); + validate_face(pbvh, f2, CHECK_FACE_MANIFOLD); } if (f1) { @@ -1901,6 +1923,7 @@ static bool cleanup_valence_3_4(EdgeQueueContext *ectx, PBVH *pbvh) pbvh->bm_idmap->cd_id_off[BM_LOOP]); BM_log_face_added(pbvh->header.bm, pbvh->bm_log, f1); + validate_face(pbvh, f1, CHECK_FACE_MANIFOLD); } validate_vert(pbvh, v, CHECK_VERT_ALL); @@ -2100,7 +2123,7 @@ void EdgeQueueContext::start() bool EdgeQueueContext::done() { - return !(totop > 0 && !edge_heap.empty() && current_i < max_steps); + return totop == 0 || edge_heap.empty() || current_i >= max_steps; } void EdgeQueueContext::finish() @@ -2196,6 +2219,14 @@ void EdgeQueueContext::step() } #endif + RandomNumberGenerator srand(PIL_check_seconds_timer() * 10000); + + auto do_smooth = [&](BMVert *v) { + // if (srand.get_float() > 0.75) { + // surface_smooth_v_safe(ss, pbvh, v, surface_smooth_fac, true); + //} + }; + switch (ops[curop]) { case PBVH_None: break; @@ -2230,6 +2261,9 @@ void EdgeQueueContext::step() } #endif + do_smooth(e->v1); + do_smooth(e->v2); + e->head.hflag &= ~BM_ELEM_TAG; /* Add complete faces. */ @@ -2287,6 +2321,9 @@ void EdgeQueueContext::step() break; } + do_smooth(e->v1); + do_smooth(e->v2); + modified = true; pbvh_bmesh_collapse_edge(pbvh, e, e->v1, e->v2, this); flushed_ = true; @@ -2365,8 +2402,14 @@ bool remesh_topology(BrushTester *brush_tester, edge_limit_multiply); eq_ctx.start(); + double time = PIL_check_seconds_timer(); + while (!eq_ctx.done()) { eq_ctx.step(); + + if (PIL_check_seconds_timer() - time > 350.0 / 1000.0) { + // XXX break; + } } eq_ctx.finish(); @@ -2676,6 +2719,7 @@ static void pbvh_split_edges(EdgeQueueContext *eq_ctx, PBVH *pbvh, BMesh *bm, Sp BM_idmap_release(pbvh->bm_idmap, (BMElem *)e, true); BMVert *newv = BM_edge_split(pbvh->header.bm, e, e->v1, &newe, 0.5f); + validate_edge(pbvh, newe); /* Flag new vertex as not needing original data update, since we interpolated it. */ sculpt::stroke_id_test(eq_ctx->ss, {reinterpret_cast(newv)}, STROKEID_USER_ORIGINAL); @@ -2895,7 +2939,7 @@ static void pbvh_split_edges(EdgeQueueContext *eq_ctx, PBVH *pbvh, BMesh *bm, Sp exist_e = newf ? rl->e : nullptr; } - if (exist_e && exist_e->l) { + if (newf && exist_e && exist_e->l) { exist_e->head.hflag &= ~BM_ELEM_TAG; #ifdef SUBD_ADD_TO_QUEUE @@ -4064,6 +4108,50 @@ void BKE_sculpt_reproject_cdata(SculptSession *ss, eCustomDataMask typemask = CD_MASK_PROP_FLOAT | CD_MASK_PROP_FLOAT2 | CD_MASK_PROP_FLOAT3 | CD_MASK_PROP_BYTE_COLOR | CD_MASK_PROP_COLOR; + CustomData *cdatas[2] = {&ss->bm->vdata, &ss->bm->ldata}; + bool ok = false; + + int cd_originals[4]; + cd_originals[0] = ss->attrs.orig_co->bmesh_cd_offset; + cd_originals[1] = ss->attrs.orig_no->bmesh_cd_offset; + cd_originals[2] = ss->attrs.orig_color ? ss->attrs.orig_color->bmesh_cd_offset : -1; + cd_originals[3] = ss->attrs.orig_mask ? ss->attrs.orig_mask->bmesh_cd_offset : -1; + + for (int i = 0; i < 2; i++) { + CustomData *data = cdatas[i]; + + for (int j = 0; j < data->totlayer; j++) { + if (data->layers[j].flag & (CD_FLAG_ELEM_NOINTERP)) { + continue; + } + + /* We don't reproject origco/origno. */ + if (i == 0) { + bool bad = false; + + for (int k = 0; k < ARRAY_SIZE(cd_originals); k++) { + if (data->layers[j].offset == cd_originals[k]) { + bad = true; + break; + } + } + + if (bad) { + continue; + } + } + + if (CD_TYPE_AS_MASK(data->layers[j].type) & typemask) { + ok = true; + } + } + } + + /* No attributes to reproject. */ + if (!ok) { + return; + } + int totstep = 2; for (int step = 0; step < totstep; step++) { float3 startco2; @@ -4075,7 +4163,7 @@ void BKE_sculpt_reproject_cdata(SculptSession *ss, normalize_v3(startno2); - /* Build fake face with original coordinates. */ + /* Build fake face with starting coordinates. */ for (int i = 0; i < totloop; i++) { BMLoop *l = ls[i]; float no[3] = {0.0f, 0.0f, 0.0f}; @@ -4095,9 +4183,6 @@ void BKE_sculpt_reproject_cdata(SculptSession *ss, *fakev = *l2->v; fakel->v = fakev; - /* Make sure original coordinate is up to date. */ - blender::bke::paint::get_original_vertex(ss, vertex, nullptr, nullptr, nullptr, nullptr); - if (l2->v == v) { copy_v3_v3(fakev->co, startco2); copy_v3_v3(fakev->no, startno2); @@ -4142,8 +4227,8 @@ void BKE_sculpt_reproject_cdata(SculptSession *ss, if (l->v == v && cur_vblock < max_vblocks) { void *vblock_old = interpl->v->head.data; void *vblock = vblocks[cur_vblock]; - memcpy((void *)vblock, v->head.data, ss->bm->vdata.totsize); + memcpy((void *)vblock, v->head.data, ss->bm->vdata.totsize); interpl->v->head.data = (void *)vblock; reproject_bm_data(ss->bm, interpl, fakef, true, typemask); @@ -4168,17 +4253,17 @@ void BKE_sculpt_reproject_cdata(SculptSession *ss, ws[i] = 1.0f / float(cur_vblock); } - float *origco = BM_ELEM_CD_PTR(v, ss->attrs.orig_co->bmesh_cd_offset); - float *origno = BM_ELEM_CD_PTR(v, ss->attrs.orig_no->bmesh_cd_offset); + float3 *origco = BM_ELEM_CD_PTR(v, ss->attrs.orig_co->bmesh_cd_offset); + float3 *origno = BM_ELEM_CD_PTR(v, ss->attrs.orig_no->bmesh_cd_offset); - float3 origco_saved = origco; - float3 origno_saved = origno; + float3 origco_saved = *origco; + float3 origno_saved = *origno; - CustomData_bmesh_interp( - &ss->bm->vdata, (const void **)vblocks, ws, nullptr, cur_vblock, v->head.data); + CustomData_bmesh_interp_ex( + &ss->bm->vdata, (const void **)vblocks, ws, nullptr, cur_vblock, v->head.data, typemask); - copy_v3_v3(origco, origco_saved); - copy_v3_v3(origno, origno_saved); + *origco = origco_saved; + *origno = origno_saved; } } diff --git a/source/blender/blenkernel/intern/dyntopo_collapse.cc b/source/blender/blenkernel/intern/dyntopo_collapse.cc index 82bf9629886..3724d18701b 100644 --- a/source/blender/blenkernel/intern/dyntopo_collapse.cc +++ b/source/blender/blenkernel/intern/dyntopo_collapse.cc @@ -29,6 +29,7 @@ #include "BKE_paint.h" #include "BKE_pbvh.h" +#include "../../bmesh/intern/bmesh_collapse.hh" #include "bmesh.h" #include "bmesh_log.h" @@ -57,6 +58,33 @@ typedef struct TraceData { const int COLLAPSE_TAG = 32; const int COLLAPSE_FACE_TAG = 64; +template static void check_new_elem_id(T *elem, PBVH *pbvh) +{ +#if 1 + int id = BM_ELEM_CD_GET_INT(elem, pbvh->bm_idmap->cd_id_off[int(elem->head.htype)]); + if (id != BM_ID_NONE) { + BMElem *existing = id < pbvh->bm_idmap->map_size ? BM_idmap_lookup(pbvh->bm_idmap, id) : + nullptr; + + if (existing) { + BM_idmap_check_assign(pbvh->bm_idmap, reinterpret_cast(elem)); + BM_idmap_release(pbvh->bm_idmap, existing, true); + } + + BM_idmap_assign(pbvh->bm_idmap, reinterpret_cast(elem), id); + + if (existing) { + BM_idmap_check_assign(pbvh->bm_idmap, existing); + } + } + else { + BM_idmap_check_assign(pbvh->bm_idmap, reinterpret_cast(elem)); + } +#else + BM_idmap_check_assign(pbvh->bm_idmap, reinterpret_cast(elem)); +#endif +} + static void collapse_ring_callback_pre(BMElem *elem, void *userdata) { TraceData *data = static_cast(userdata); @@ -91,29 +119,6 @@ static void collapse_ring_callback_pre(BMElem *elem, void *userdata) } } -static void check_new_elem_id(BMElem *elem, TraceData *data) -{ - int id = BM_ELEM_CD_GET_INT(elem, data->pbvh->bm_idmap->cd_id_off[int(elem->head.htype)]); - if (id != BM_ID_NONE) { - BMElem *existing = id < data->pbvh->bm_idmap->map_size ? - BM_idmap_lookup(data->pbvh->bm_idmap, id) : - nullptr; - - if (existing) { - BM_idmap_release(data->pbvh->bm_idmap, existing, true); - } - - BM_idmap_assign(data->pbvh->bm_idmap, elem, id); - - if (existing) { - BM_idmap_check_assign(data->pbvh->bm_idmap, existing); - } - } - else { - BM_idmap_check_assign(data->pbvh->bm_idmap, elem); - } -} - static void collapse_ring_callback_post(BMElem *elem, void *userdata) { TraceData *data = static_cast(userdata); @@ -125,20 +130,20 @@ static void collapse_ring_callback_post(BMElem *elem, void *userdata) dyntopo_add_flag(data->pbvh, v, SCULPTFLAG_NEED_VALENCE); - check_new_elem_id(elem, data); + check_new_elem_id(v, data->pbvh); BM_log_vert_added(bm, data->pbvh->bm_log, v); break; } case BM_EDGE: { BMEdge *e = reinterpret_cast(elem); - check_new_elem_id(elem, data); + check_new_elem_id(e, data->pbvh); BM_log_edge_added(bm, data->pbvh->bm_log, e); break; } case BM_FACE: { BMFace *f = reinterpret_cast(elem); - check_new_elem_id(elem, data); + check_new_elem_id(f, data->pbvh); BM_log_face_added(bm, data->pbvh->bm_log, f); BKE_pbvh_bmesh_add_face(data->pbvh, f, false, false); @@ -616,6 +621,54 @@ bool pbvh_bmesh_collapse_edge_uvs( return snap; } +class DyntopoCollapseCallbacks { + PBVH *pbvh; + BMesh *bm; + + public: + DyntopoCollapseCallbacks(PBVH *pbvh_) : pbvh(pbvh_), bm(pbvh_->header.bm) {} + + inline void on_vert_kill(BMVert *v) + { + dyntopo_add_flag(pbvh, v, SCULPTFLAG_NEED_VALENCE); + + BM_log_vert_removed(bm, pbvh->bm_log, v); + pbvh_bmesh_vert_remove(pbvh, v); + BM_idmap_release(pbvh->bm_idmap, reinterpret_cast(v), false); + } + inline void on_edge_kill(BMEdge *e) + { + BM_log_edge_removed(bm, pbvh->bm_log, e); + BM_idmap_release(pbvh->bm_idmap, reinterpret_cast(e), false); + } + inline void on_face_kill(BMFace *f) + { + BM_log_face_removed(bm, pbvh->bm_log, f); + pbvh_bmesh_face_remove(pbvh, f, false, true, true); + BM_idmap_release(pbvh->bm_idmap, reinterpret_cast(f), false); + } + + inline void on_vert_create(BMVert *v) + { + dyntopo_add_flag(pbvh, v, SCULPTFLAG_NEED_VALENCE); + + check_new_elem_id(v, pbvh); + BM_log_vert_added(bm, pbvh->bm_log, v); + } + inline void on_edge_create(BMEdge *e) + { + check_new_elem_id(e, pbvh); + BM_log_edge_added(bm, pbvh->bm_log, e); + } + inline void on_face_create(BMFace *f) + { + check_new_elem_id(f, pbvh); + BM_log_face_added(bm, pbvh->bm_log, f); + BM_ELEM_CD_SET_INT(f, pbvh->cd_face_node_offset, DYNTOPO_NODE_NONE); + BKE_pbvh_bmesh_add_face(pbvh, f, false, false); + } +}; + /* * This function is rather complicated. It has to * snap UVs, log geometry and free ids. @@ -687,17 +740,28 @@ BMVert *pbvh_bmesh_collapse_edge( const int facetag = COLLAPSE_FACE_TAG; int vert_ring_maxdepth = 0; - bool non_manifold = vert_is_nonmanifold(e->v1); - non_manifold |= vert_is_nonmanifold(e->v2); + bool non_manifold_v1 = vert_is_nonmanifold(e->v1); + bool non_manifold_v2 = vert_is_nonmanifold(e->v2); - if (non_manifold) { + if (non_manifold_v1 && non_manifold_v2) { vert_ring_maxdepth++; } + /* Do not collapse non-manifold verts into manifold ones. */ + else if (non_manifold_v1 != non_manifold_v2) { + return nullptr; + } + +#define USE_COLLAPSE_CALLBACKS + +#ifdef USE_COLLAPSE_CALLBACKS + DyntopoCollapseCallbacks callbacks(pbvh); +#endif /* Make sure original data is initialized before we snap it. */ BKE_pbvh_bmesh_check_origdata(eq_ctx->ss, v_conn, pbvh->stroke_id); BKE_pbvh_bmesh_check_origdata(eq_ctx->ss, v_del, pbvh->stroke_id); +#ifndef USE_COLLAPSE_CALLBACKS /* Remove topology from PBVH and insert into bmlog. */ vert_ring_do(pbvh->header.bm, e->v1, @@ -707,7 +771,7 @@ BMVert *pbvh_bmesh_collapse_edge( tag, facetag, vert_ring_maxdepth); - +#endif pbvh_bmesh_check_nodes(pbvh); /* Snap UVS. */ @@ -719,9 +783,6 @@ BMVert *pbvh_bmesh_collapse_edge( float co[3]; copy_v3_v3(co, v_conn->co); - - /* Full non-manifold collapse. */ - BM_edge_collapse(pbvh->header.bm, e, v_del, true, true, true, true); copy_v3_v3(v_conn->co, co); } else { @@ -730,11 +791,17 @@ BMVert *pbvh_bmesh_collapse_edge( add_v3_v3v3(co, v_del->co, v_conn->co); mul_v3_fl(co, 0.5f); - /* Full non-manifold collapse. */ - BM_edge_collapse(pbvh->header.bm, e, v_del, true, true, true, true); copy_v3_v3(v_conn->co, co); } + /* Full non-manifold collapse. */ +#ifdef USE_COLLAPSE_CALLBACKS + blender::bmesh::join_vert_kill_edge(pbvh->header.bm, e, v_del, true, true, callbacks); +#else + BM_edge_collapse(pbvh->header.bm, e, v_del, true, true, true, true); +#endif + +#ifndef USE_COLLAPSE_CALLBACKS vert_ring_do(pbvh->header.bm, v_conn, nullptr, @@ -743,12 +810,19 @@ BMVert *pbvh_bmesh_collapse_edge( tag, facetag, vert_ring_maxdepth); +#endif if (!v_conn->e) { printf("%s: pbvh error, v_conn->e was null\n", __func__); return v_conn; } + if (v_conn->e) { + BMEdge *e2 = v_conn->e; + do { + validate_edge(pbvh, e2); + } while ((e2 = BM_DISK_EDGE_NEXT(e2, v_conn)) != v_conn->e); + } validate_vert(pbvh, v_conn, CHECK_VERT_FACES | CHECK_VERT_NODE_ASSIGNED); /* Flag boundaries for update. */ @@ -776,7 +850,7 @@ BMVert *pbvh_bmesh_collapse_edge( return nullptr; } - if (v_conn->e && !v_conn->e->l) { + if (0) { // XXX v_conn->e && !v_conn->e->l) { BM_log_edge_removed(pbvh->header.bm, pbvh->bm_log, v_conn->e); if (BM_idmap_get_id(pbvh->bm_idmap, reinterpret_cast(v_conn->e)) != BM_ID_NONE) { BM_idmap_release(pbvh->bm_idmap, reinterpret_cast(v_conn->e), true); @@ -784,7 +858,7 @@ BMVert *pbvh_bmesh_collapse_edge( BM_edge_kill(pbvh->header.bm, v_conn->e); } - if (!v_conn->e) { + if (0) { // XXX !v_conn->e) { /* Delete isolated vertex. */ if (BM_ELEM_CD_GET_INT(v_conn, pbvh->cd_vert_node_offset) != DYNTOPO_NODE_NONE) { blender::bke::dyntopo::pbvh_bmesh_vert_remove(pbvh, v_conn); diff --git a/source/blender/blenkernel/intern/dyntopo_intern.hh b/source/blender/blenkernel/intern/dyntopo_intern.hh index c04719fc42b..4dd5205702e 100644 --- a/source/blender/blenkernel/intern/dyntopo_intern.hh +++ b/source/blender/blenkernel/intern/dyntopo_intern.hh @@ -317,11 +317,8 @@ bool destroy_nonmanifold_fins(PBVH *pbvh, BMEdge *e_root); bool check_face_is_tri(PBVH *pbvh, BMFace *f); bool check_vert_fan_are_tris(PBVH *pbvh, BMVert *v); -BMVert *pbvh_bmesh_collapse_edge(PBVH *pbvh, - BMEdge *e, - BMVert *v1, - BMVert *v2, - struct EdgeQueueContext *eq_ctx); +BMVert *pbvh_bmesh_collapse_edge( + PBVH *pbvh, BMEdge *e, BMVert *v1, BMVert *v2, struct EdgeQueueContext *eq_ctx); extern "C" void bm_log_message(const char *fmt, ...); void pbvh_bmesh_vert_remove(PBVH *pbvh, BMVert *v); @@ -437,7 +434,7 @@ inline bool check_face_is_manifold(PBVH *, BMFace *) return true; } #else -# include "../bmesh/intern/bmesh_private.h" +extern "C" int bmesh_elem_check_all(void *element, char htype); # include # include @@ -450,7 +447,7 @@ static void debug_printf(const char *fmt, ...) va_end(args); } -template static char bm_get_htype() +template static char get_type_htype() { if constexpr (std::is_same_v) { return BM_VERT; @@ -502,27 +499,33 @@ static const char *get_type_name(char htype) } } // namespace blender::bke::dyntopo::debug +extern "C" char *bm_get_err_str(int err); + template ATTR_NO_OPT static bool validate_elem(PBVH *pbvh, T *elem) { using namespace blender::bke::dyntopo::debug; - if (!v) { - debug_printf("%s was null\n", get_type_name); + if (!elem) { + blender::bke::dyntopo::debug::debug_printf("%s was null\n", get_type_name); return false; } if (elem->head.htype != get_type_htype()) { - debug_printf("%p had wrong type: expected a %s but got %s (type %d).\n", - elem, - get_type_name(), - get_type_name(elem->head.htype)); + blender::bke::dyntopo::debug::debug_printf( + "%p had wrong type: expected a %s but got %s (type %d).\n", + elem, + get_type_name(), + get_type_name(elem->head.htype)); return false; } - int ret = bmesh_elem_check(static_cast(elem), bm_get_htype()); + int ret = bmesh_elem_check_all(static_cast(elem), get_type_htype()); if (ret) { - debug_printf("%s (%p) failed integrity checks with code %d\n", get_type_name(), elem, ret); + blender::bke::dyntopo::debug::debug_printf("%s (%p) failed integrity checks with code %s\n", + get_type_name(), + elem, + bm_get_err_str(ret)); return false; } @@ -535,11 +538,14 @@ ATTR_NO_OPT static bool check_face_is_manifold(PBVH *pbvh, BMFace *f) BMLoop *l = f->l_first; do { - if (l->radial_next != l && l->radial_next->radial_next != l) { - debug_printf("Face %p has non-manifold edge %p\n", f, l->e); - - return false; - } + int count = 0; + BMLoop *l2 = l; + do { + if (count++ > 10) { + blender::bke::dyntopo::debug::debug_printf("Face %p has highly non-manifold edge %p\n", f, l->e); + return false; + } + } while ((l2 = l2->radial_next) != l); } while ((l = l->next) != f->l_first); return true; @@ -560,7 +566,7 @@ ATTR_NO_OPT static bool validate_face(PBVH *pbvh, BMFace *f, eValidateFaceFlags int ni = BM_ELEM_CD_GET_INT(f, pbvh->cd_face_node_offset); if (ni < 0 || ni >= pbvh->totnode) { if (ni != DYNTOPO_NODE_NONE || flags & CHECK_FACE_NODE_ASSIGNED) { - debug_printf("face %p has corrupted node index %d\n", f, ni); + //blender::bke::dyntopo::debug::debug_printf("face %p has corrupted node index %d\n", f, ni); return false; } else { @@ -570,7 +576,7 @@ ATTR_NO_OPT static bool validate_face(PBVH *pbvh, BMFace *f, eValidateFaceFlags PBVHNode *node = &pbvh->nodes[ni]; if (!(node->flag & PBVH_Leaf)) { - printf("face %p has corrupted node index.", f); + //printf("face %p has corrupted node index.", f); return false; } @@ -594,10 +600,10 @@ ATTR_NO_OPT static bool validate_vert(PBVH *pbvh, BMVert *v, eValidateVertFlags BMIter iter; BMFace *f; BM_ITER_ELEM (f, &iter, v, BM_FACES_OF_VERT) { - if (!validate_face(pbvh, - f, - flags & CHECK_VERT_NODE_ASSIGNED ? CHECK_FACE_NODE_ASSIGNED : - CHECK_FACE_NONE)) + if (!validate_face(pbvh, + f, + flags & CHECK_VERT_NODE_ASSIGNED ? CHECK_FACE_NODE_ASSIGNED : + CHECK_FACE_NONE)) { return false; } @@ -619,7 +625,7 @@ ATTR_NO_OPT static bool validate_vert(PBVH *pbvh, BMVert *v, eValidateVertFlags if (ni < 0 || ni >= pbvh->totnode) { if (ni != DYNTOPO_NODE_NONE || flags & CHECK_VERT_NODE_ASSIGNED) { - debug_printf("vertex %p has corrupted node index %d\n", v, ni); + //blender::bke::dyntopo::debug::debug_printf("vertex %p has corrupted node index %d\n", v, ni); return false; } else { @@ -629,7 +635,7 @@ ATTR_NO_OPT static bool validate_vert(PBVH *pbvh, BMVert *v, eValidateVertFlags PBVHNode *node = &pbvh->nodes[ni]; if (!(node->flag & PBVH_Leaf)) { - printf("Vertex %p has corrupted node index.", v); + //printf("Vertex %p has corrupted node index.", v); return false; } diff --git a/source/blender/blenkernel/intern/pbvh_bmesh.cc b/source/blender/blenkernel/intern/pbvh_bmesh.cc index a403dd6d8cd..eaa97f75fa9 100644 --- a/source/blender/blenkernel/intern/pbvh_bmesh.cc +++ b/source/blender/blenkernel/intern/pbvh_bmesh.cc @@ -657,7 +657,8 @@ void pbvh_bmesh_face_remove( PBVHNode *f_node = pbvh_bmesh_node_from_face(pbvh, f); if (!f_node || !(f_node->flag & PBVH_Leaf)) { - printf("%s: pbvh corruption.\n", __func__); + printf( + "%s: pbvh corruption. %d\n", __func__, BM_ELEM_CD_GET_INT(f, pbvh->cd_face_node_offset)); return; } @@ -1070,7 +1071,14 @@ void bke_pbvh_insert_face_finalize(PBVH *pbvh, BMFace *f, const int ni) // ensure verts are in pbvh BMLoop *l = f->l_first; do { - const int ni2 = BM_ELEM_CD_GET_INT(l->v, pbvh->cd_vert_node_offset); + int ni2 = BM_ELEM_CD_GET_INT(l->v, pbvh->cd_vert_node_offset); + + if (ni2 < 0 || ni2 >= pbvh->totnode || !(pbvh->nodes[ni2].flag & PBVH_Leaf) || + !(pbvh->nodes[ni2].bm_unique_verts->contains(l->v))) + { + printf("%s: pbvh corruption\n", __func__); + ni2 = DYNTOPO_NODE_NONE; + } BB_expand(&node->vb, l->v->co); BB_expand(&node->orig_vb, BM_ELEM_CD_PTR(l->v, pbvh->cd_origco)); diff --git a/source/blender/blenkernel/intern/pbvh_intern.hh b/source/blender/blenkernel/intern/pbvh_intern.hh index 8fe426bf6d5..8ec049dbc64 100644 --- a/source/blender/blenkernel/intern/pbvh_intern.hh +++ b/source/blender/blenkernel/intern/pbvh_intern.hh @@ -393,14 +393,6 @@ BLI_INLINE int pbvh_bmesh_node_index_from_vert(PBVH *pbvh, const BMVert *key) return node_index; } -BLI_INLINE int pbvh_bmesh_node_index_from_face(PBVH *pbvh, const BMFace *key) -{ - const int node_index = BM_ELEM_CD_GET_INT((const BMElem *)key, pbvh->cd_face_node_offset); - BLI_assert(node_index != DYNTOPO_NODE_NONE); - BLI_assert(node_index < pbvh->totnode); - return node_index; -} - BLI_INLINE PBVHNode *pbvh_bmesh_node_from_vert(PBVH *pbvh, const BMVert *key) { int ni = pbvh_bmesh_node_index_from_vert(pbvh, key); @@ -411,10 +403,8 @@ BLI_INLINE PBVHNode *pbvh_bmesh_node_from_vert(PBVH *pbvh, const BMVert *key) BLI_INLINE PBVHNode *pbvh_bmesh_node_from_face(PBVH *pbvh, const BMFace *key) { - int ni = pbvh_bmesh_node_index_from_face(pbvh, key); - - return ni >= 0 ? pbvh->nodes + ni : NULL; - // return &pbvh->nodes[pbvh_bmesh_node_index_from_face(pbvh, key)]; + int ni = BM_ELEM_CD_GET_INT(key, pbvh->cd_face_node_offset); + return ni >= 0 && ni < pbvh->totnode ? pbvh->nodes + ni : NULL; } bool pbvh_bmesh_node_limit_ensure(PBVH *pbvh, int node_index); diff --git a/source/blender/bmesh/CMakeLists.txt b/source/blender/bmesh/CMakeLists.txt index 39f9c776b43..18e0b3339fd 100644 --- a/source/blender/bmesh/CMakeLists.txt +++ b/source/blender/bmesh/CMakeLists.txt @@ -62,6 +62,8 @@ set(SRC intern/bmesh_callback_generic.c intern/bmesh_callback_generic.h + intern/bmesh_collapse.cc + intern/bmesh_collapse.hh intern/bmesh_construct.c intern/bmesh_construct.h intern/bmesh_core.c diff --git a/source/blender/bmesh/intern/bmesh_collapse.cc b/source/blender/bmesh/intern/bmesh_collapse.cc new file mode 100644 index 00000000000..98ff51ef01a --- /dev/null +++ b/source/blender/bmesh/intern/bmesh_collapse.cc @@ -0,0 +1,15 @@ +#include "bmesh_collapse.hh" + +using blender::bmesh::NullCollapseCallbacks; + +namespace blender::bmesh { +char *_last_local_obj = nullptr; +} + +extern "C" BMVert *bmesh_kernel_join_vert_kill_edge( + BMesh *bm, BMEdge *e, BMVert *v_kill, const bool do_del, const bool combine_flags) +{ + NullCollapseCallbacks callbacks; + return blender::bmesh::join_vert_kill_edge( + bm, e, v_kill, do_del, combine_flags, callbacks); +} diff --git a/source/blender/bmesh/intern/bmesh_collapse.hh b/source/blender/bmesh/intern/bmesh_collapse.hh new file mode 100644 index 00000000000..e3171ccb0a8 --- /dev/null +++ b/source/blender/bmesh/intern/bmesh_collapse.hh @@ -0,0 +1,1012 @@ +#pragma once + +#include +#include +#include +#include + +#include "MEM_guardedalloc.h" + +#include "DNA_meshdata_types.h" + +#include "BLI_compiler_attrs.h" +#include "BLI_compiler_compat.h" +#include "BLI_map.hh" +#include "BLI_math_vector_types.hh" +#include "BLI_set.hh" +#include "BLI_span.hh" +#include "BLI_utildefines.h" +#include "BLI_vector.hh" + +using blender::float2; +using blender::float3; +using blender::IndexRange; +using blender::Map; +using blender::MutableSpan; +using blender::Set; +using blender::Span; +using blender::Vector; + +#include +#include +#include +#include + +#include "bmesh.h" +#include "bmesh_private.h" +extern "C" { +#include "bmesh_structure.h" +} + +//#define JVKE_DEBUG + +namespace blender::bmesh { + +struct NullCollapseCallbacks { + inline void on_vert_kill(BMVert *) {} + inline void on_edge_kill(BMEdge *) {} + inline void on_face_kill(BMFace *) {} + inline void on_vert_create(BMVert *) {} + inline void on_edge_create(BMEdge *) {} + inline void on_face_create(BMFace *) {} +}; + +#ifdef JVKE_DEBUG + +static void bm_local_obj_free(char *str, char *fixed) +{ + if (str != fixed) { + MEM_freeN(str); + } +} + +# define LOCAL_OBJ_SIZE 512 + +static char *obj_append_line(const char *line, char *str, char *fixed, int *size, int *i) +{ + int len = (int)strlen(line); + + if (*i + len + 1 >= *size) { + *size += len + ((*size) >> 1); + + if (str == fixed) { + str = static_cast(MEM_mallocN(*size, "buf")); + memcpy(static_cast(str), fixed, LOCAL_OBJ_SIZE); + } + else { + str = static_cast(MEM_reallocN(str, *size)); + } + } + + memcpy(str + *i, line, len); + str[*i + len] = 0; + + *i += len; + + return str; +} + +/* NotForPr: saves an obj of the neighborhood around an edge prior to collapse + * into a buffer that can be read from a debugger. + */ +static char *bm_save_local_obj_text( + BMesh *, int depth, char buf[LOCAL_OBJ_SIZE], const char *fmt, ...) +{ + va_list vl; + va_start(vl, fmt); + + buf[0] = 0; + + Vector vs; + Vector initial_vs; + Vector es; + Vector initial_es; + Vector fs; + Vector initial_fs; + + Set visit; + + const char *c = fmt; + while (*c) { + if (*c == ' ' || *c == '\t') { + c++; + continue; + } + + void *ptr = va_arg(vl, void *); + + switch (*c) { + case 'v': + vs.append(static_cast(ptr)); + initial_vs.append(static_cast(ptr)); + break; + case 'e': + es.append(static_cast(ptr)); + initial_es.append(static_cast(ptr)); + break; + case 'f': + fs.append(static_cast(ptr)); + initial_fs.append(static_cast(ptr)); + break; + } + + c++; + } + + va_end(vl); + + int tag = 4; + for (BMFace *f : fs) { + BMLoop *l = f->l_first; + + do { + l->v->head.api_flag &= ~tag; + l->e->head.api_flag &= ~tag; + } while ((l = l->next) != f->l_first); + } + + for (BMEdge *e : es) { + e->v1->head.api_flag &= ~tag; + e->v2->head.api_flag &= ~tag; + } + + for (BMVert *v : vs) { + v->head.api_flag |= tag; + } + + for (BMEdge *e : es) { + if (!(e->v1->head.api_flag & tag)) { + vs.append(e->v1); + e->v1->head.api_flag |= tag; + } + + if (!(e->v2->head.api_flag & tag)) { + vs.append(e->v2); + e->v2->head.api_flag |= tag; + } + + e->head.api_flag |= tag; + } + + for (BMFace *f : fs) { + BMLoop *l = f->l_first; + + do { + if (!(l->v->head.api_flag & tag)) { + vs.append(l->v); + l->v->head.api_flag |= tag; + } + + if (!(l->e->head.api_flag & tag)) { + es.append(l->e); + l->e->head.api_flag |= tag; + } + } while ((l = l->next) != f->l_first); + } + + struct StackItem { + BMVert *v; + int depth; + }; + + Vector stack; + Set elemset; + + for (BMVert *v : vs) { + elemset.add(static_cast(v)); + } + for (BMEdge *e : es) { + elemset.add(static_cast(e)); + } + for (BMFace *f : fs) { + elemset.add(static_cast(f)); + } + + stack.clear(); + stack.append({vs[0], 0}); + while (stack.size() > 0) { + StackItem item = stack.pop_last(); + BMVert *v = item.v; + int startdepth = item.depth; + + if (elemset.add(static_cast(v))) { + vs.append(v); + } + + if (!v->e || item.depth > depth) { + continue; + } + + BMEdge *e = v->e; + do { + if (visit.add(static_cast(e))) { + stack.append({e->v1, startdepth + 1}); + stack.append({e->v2, startdepth + 1}); + } + + if (!e->l) { + continue; + } + + BMLoop *l = e->l; + do { + if (visit.add(static_cast(l->f))) { + if (elemset.add(static_cast(l->f))) { + fs.append(l->f); + } + + BMLoop *l2 = l; + do { + if (visit.add(static_cast(l->v))) { + stack.append({l->v, startdepth + 1}); + } + } while ((l2 = l2->next) != l); + } + } while ((l = l->radial_next) != e->l); + } while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e); + } + + char *str = buf; + int size = LOCAL_OBJ_SIZE - 1; + int stri = 0; + + char line[128]; + line[0] = 0; + + for (BMVert *v : vs) { + v->head.api_flag &= ~tag; + } + for (BMEdge *e : es) { + e->head.api_flag &= ~tag; + } + + for (BMFace *f : fs) { + f->head.api_flag &= ~tag; + } + for (BMVert *v : initial_vs) { + v->head.api_flag |= tag; + } + + for (BMEdge *e : initial_es) { + e->head.api_flag |= tag; + e->v1->head.api_flag |= tag; + e->v2->head.api_flag |= tag; + } + + for (BMFace *f : initial_fs) { + f->head.api_flag |= tag; + BMLoop *l = f->l_first; + + do { + l->v->head.api_flag |= tag; + } while ((l = l->next) != f->l_first); + } + + for (int i : vs.index_range()) { + BMVert *v = vs[i]; + + if (v->head.api_flag & tag) { + sprintf(line, "#select\n"); + str = obj_append_line(line, str, buf, &size, &stri); + } + + v->head.index = i + 1; + sprintf(line, "v %.4f %.4f %.4f\n", v->co[0], v->co[1], v->co[2]); + + str = obj_append_line(line, str, buf, &size, &stri); + } + + /* save wire edges */ + for (BMEdge *e : es) { + if (e->l) { + continue; + } + + sprintf(line, "l %d %d\n", e->v1->head.index, e->v2->head.index); + str = obj_append_line(line, str, buf, &size, &stri); + } + + for (BMFace *f : fs) { + BMLoop *l = f->l_first; + + sprintf(line, "f"); + str = obj_append_line(line, str, buf, &size, &stri); + + do { + sprintf(line, " %d", l->v->head.index); + + str = obj_append_line(line, str, buf, &size, &stri); + } while ((l = l->next) != f->l_first); + + str = obj_append_line("\n", str, buf, &size, &stri); + } + + return str; +} + +static void check_mesh_radial(BMesh *bm) +{ + return; + + BMIter iter; + BMEdge *e; + BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) { + int count = 0; + BMLoop *l = e->l; + + if (!l) { + continue; + } + + do { + if (BM_elem_is_free((BMElem *)l, BM_LOOP)) { + printf("Freed loop in edge %p radial cycle\n", e); + } + + if (count++ > 100) { + printf("Corrupted radial cycle for %p\n", e); + break; + } + } while ((l = l->radial_next) != e->l); + } +} + +static void trigger_jvke_error(int err, char *obj_text) +{ + printf("========= ERROR %s============\n\n%s\n\n", bm_get_error_str(err), obj_text); +} + +extern char *_last_local_obj; + +# define JVKE_CHECK_ELEMENT(elem) \ + { \ + int err = 0; \ + if ((err = bmesh_elem_check(elem, (elem)->head.htype))) { \ + trigger_jvke_error(err, saved_obj); \ + } \ + } +#else +# define JVKE_CHECK_ELEMENT(elem) +#endif + +template +static bool cleanup_vert(BMesh *bm, BMVert *v, Callbacks &callbacks) +{ + BMEdge *e = v->e; + + if (!e->l || e->l->f == e->l->radial_next->f) { + return false; + } + + BMFace *f_example = nullptr; + + BMIter iter; + BMFace *f; + BM_ITER_ELEM (f, &iter, v, BM_FACES_OF_VERT) { + callbacks.on_face_kill(f); + } + + do { + BMLoop *l = e->l; + if (!l) { + continue; + } + + callbacks.on_edge_kill(e); + + if (!f_example) { + f_example = l->f; + } + } while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e); + + BMVert *v1 = BM_edge_other_vert(v->e, v); + BMVert *v2 = BM_edge_other_vert(BM_DISK_EDGE_NEXT(v->e, v), v); + BMVert *v3 = BM_edge_other_vert(BM_DISK_EDGE_NEXT(BM_DISK_EDGE_NEXT(v->e, v), v), v); + + f = BM_face_create_quad_tri(bm, v1, v2, v3, nullptr, f_example, BM_CREATE_NOP); + BMLoop *l = f->l_first; + + // ensure correct winding + do { + if (l->radial_next != l && l->radial_next->v == l->v) { + BM_face_normal_flip(bm, f); + break; + } + } while ((l = l->next) != f->l_first); + + callbacks.on_face_create(f); + callbacks.on_vert_kill(v); + + BM_vert_kill(bm, v); + + return true; +} + +template +static void bmesh_kernel_check_val3_vert(BMesh *bm, BMEdge *e, Callbacks &callbacks) +{ + if (!e->l) { + return; + } + + bool stop; + + do { + stop = true; + + BMLoop *l = e->l; + + if (!l) { + break; + } + + do { + BMLoop *l2 = l->prev; + + if (l2 == l2->radial_next) { + continue; + } + + if (BM_vert_edge_count(l2->v) == 3) { + if (cleanup_vert(bm, l2->v, callbacks)) { + stop = false; + break; + } + } + } while ((l = l->radial_next) != e->l); + } while (!stop); +} + +/** + * \brief Join Vert Kill Edge (JVKE) + * + * Collapse an edge, merging surrounding data. + * + * Unlike #BM_vert_collapse_edge & #bmesh_kernel_join_edge_kill_vert + * which only handle 2 valence verts, + * this can handle any number of connected edges/faces. + * + *
+ * Before: -> After:
+ * +-+-+-+    +-+-+-+
+ * | | | |    | \ / |
+ * +-+-+-+    +--+--+
+ * | | | |    | / \ |
+ * +-+-+-+    +-+-+-+
+ * 
+ */ + +template +BMVert *join_vert_kill_edge(BMesh *bm, + BMEdge *e, + BMVert *v_kill, + const bool do_del, + const bool combine_flags, + Callbacks &callbacks) +{ + BMVert *v_conn = BM_edge_other_vert(e, v_kill); + +#ifdef JVKE_DEBUG + char buf[LOCAL_OBJ_SIZE]; + + bool have_boundary = false; + + if (_last_local_obj) { + MEM_freeN(static_cast(_last_local_obj)); + } + + char *saved_obj = bm_save_local_obj_text(bm, 2, buf, "e", e); + + _last_local_obj = static_cast(MEM_mallocN(strlen(saved_obj) + 1, "_last_local_obj")); + BLI_strncpy(_last_local_obj, saved_obj, strlen(saved_obj) + 1); +#endif + + /* Free any surrounding valence-3 rings disconnected from the edge. */ + bmesh_kernel_check_val3_vert(bm, e, callbacks); + + Vector fs; + Vector deles; + + BMVert *v_del = BM_edge_other_vert(e, v_conn); + const int tag = _FLAG_WALK_ALT; /* Using bmhead.api_flag. */ + const int dup_tag = _FLAG_OVERLAP; + const int final_tag = _FLAG_JF; + + JVKE_CHECK_ELEMENT(v_conn); + JVKE_CHECK_ELEMENT(v_del); + + /* Clear tags. */ + for (int i = 0; i < 2; i++) { + BMVert *v = i ? v_del : v_conn; + + if (!v->e) { + continue; + } + + BMEdge *e2 = v->e; + do { + if (!e2->l) { + continue; + } + + BMLoop *l = e2->l; + do { + BM_ELEM_API_FLAG_DISABLE(l->f, tag); + + BMLoop *l2 = l; + do { + BMLoop *l3 = l2; + do { + BM_ELEM_API_FLAG_DISABLE(l3->f, tag); + } while ((l3 = l3->radial_next) != l2); + } while ((l2 = l2->next) != l); + +#ifdef JVKE_DEBUG + if (l->radial_next == l) { + have_boundary = true; + } +#endif + + BM_ELEM_API_FLAG_DISABLE(l->f, dup_tag); + BM_ELEM_API_FLAG_DISABLE(l->e, dup_tag); + BM_ELEM_API_FLAG_DISABLE(l->v, dup_tag); + } while ((l = l->radial_next) != e2->l); + } while ((e2 = BM_DISK_EDGE_NEXT(e2, v)) != v->e); + } + + /* Build face list. */ + for (int i = 0; i < 2; i++) { + BMVert *v = i ? v_del : v_conn; + BMEdge *e2 = v->e; + + if (!e2 || !e2->l) { + continue; + } + + do { + BMLoop *l = e2->l; + + if (!l) { + continue; + } + + do { + if (!BM_ELEM_API_FLAG_TEST(l->f, tag)) { + BM_ELEM_API_FLAG_ENABLE(l->f, tag); + + BMLoop *l2 = l; + do { + BM_ELEM_API_FLAG_DISABLE(l2->e, tag); + } while ((l2 = l2->next) != l); + + if (fs.contains(l->f)) { + printf("%s: error!\n", __func__); + } + else { + fs.append(l->f); + } + } + + BMLoop *l2 = l; + do { + BMLoop *l3 = l2; + do { + if (l3->f != l->f && !BM_ELEM_API_FLAG_TEST(l3->f, tag)) { + BM_ELEM_API_FLAG_ENABLE(l3->f, tag); + if (fs.contains(l3->f)) { + printf("%s: error!\n", __func__); + } + else { + fs.append(l3->f); + } + } + } while ((l3 = l3->radial_next) != l2); + } while ((l2 = l2->next) != l); + + } while ((l = l->radial_next) != e2->l); + } while ((e2 = BM_DISK_EDGE_NEXT(e2, v)) != v->e); + } + + // printf("fs len: %d\n", int(fs.size())); + + for (BMFace *f : fs) { + callbacks.on_face_kill(f); + } + + /* Unlink loops. */ + for (BMFace *f : fs) { + if (f->head.htype != BM_FACE) { + printf("f was freed!\n"); + continue; + } + BMLoop *l = f->l_first; + + do { + BMEdge *e2 = l->e; + + l->radial_next->radial_prev = l->radial_prev; + l->radial_prev->radial_next = l->radial_next; + + if (l == e2->l) { + e2->l = l->radial_next; + } + + if (l == e2->l) { + e2->l = nullptr; + } + + l->radial_next = l->radial_prev = l; + if (e2->l) { + BMLoop *l2 = e2->l; + int count = 0; + + do { + if (count++ > 10) { + printf("%s: Corrupted radial list\n", __func__); + break; + } + + if (l2 == l) { + printf("%s: Radial list still has deleted loop\n", __func__); + } + } while ((l2 = l2->next) != e2->l); + } + } while ((l = l->next) != f->l_first); + } + + /* Swap verts. */ + for (BMFace *f : fs) { + BMLoop *l = f->l_first, *lnext = nullptr; + + do { + lnext = l->next; + + if (l->v == v_del) { + l->v = v_conn; + } + + BM_ELEM_API_FLAG_DISABLE(l->v, tag); + + for (int step = 0; step < 2; step++) { + BMVert *v_edge = step ? l->e->v2 : l->e->v1; + BMVert *v_other = BM_edge_other_vert(l->e, v_edge); + + if (v_edge != v_del) { + continue; + } + + if (v_other == v_conn) { + /* Flag for later deletion. */ + if (!BM_ELEM_API_FLAG_TEST(l->e, tag)) { + // printf("%p l:%p 1\n", l->e->l, l); + deles.append(l->e); + } + + BM_ELEM_API_FLAG_ENABLE(l->e, tag); + } + else { + BMEdge *e3; + + if ((e3 = BM_edge_exists(v_conn, v_other))) { + if (combine_flags) { + bool remove_smooth = !BM_elem_flag_test(l->e, BM_ELEM_SMOOTH); + remove_smooth = remove_smooth || !BM_elem_flag_test(e3, BM_ELEM_SMOOTH); + + e3->head.hflag |= l->e->head.hflag; + + if (remove_smooth) { + BM_elem_flag_disable(e3, BM_ELEM_SMOOTH); + } + } + + /* Flag for later deletion. */ + if (!BM_ELEM_API_FLAG_TEST(l->e, tag)) { + // printf("%p l:%p 2\n", l->e->l, l); + deles.append(l->e); + } + + BM_ELEM_API_FLAG_ENABLE(l->e, tag); + + l->e = e3; + } + else { + callbacks.on_edge_kill(l->e); + bmesh_disk_vert_replace(l->e, v_conn, v_del); + callbacks.on_edge_create(l->e); + } + } + } + } while ((l = lnext) != f->l_first); + } + + for (BMEdge *e2 : deles) { + if (e2->l != nullptr) { + printf("%s: e2->l was not null!\n", __func__); + + while (e2->l) { + BMFace *f = e2->l->f; + + if (fs.contains(f)) { + printf("double entry for f! f->len: %d\n", f->len); + } + + if (!fs.contains(f)) { + fs.append(f); + callbacks.on_face_kill(f); + + /* Unlink face. */ + BMLoop *l = f->l_first; + do { + if (l->v == v_del) { + /* Duplicate vertex; it will be filtered out later. */ + l->v = l->next->v; + } + + if (l->e != e2) { + bmesh_radial_loop_remove(l->e, l); + } + } while ((l = l->next) != f->l_first); + } + + bmesh_radial_loop_remove(e2, e2->l); + } + } + + callbacks.on_edge_kill(e2); + BM_edge_kill(bm, e2); + } + + for (int i : fs.index_range()) { + BMFace *f = fs[i]; + BMLoop *l, *lnext; + + /* Validate. */ + l = f->l_first; + do { + lnext = l == l->next ? nullptr : l->next; + + if (l->v == l->next->v) { + l->prev->next = l->next; + l->next->prev = l->prev; + + if (l == l->f->l_first) { + l->f->l_first = l->next; + } + + l->f->len--; + + if (l == l->f->l_first) { + l->f->l_first = nullptr; + } + + bm_kill_only_loop(bm, l); + } + } while (lnext && (l = lnext) != f->l_first); + + if (f->len <= 2) { + /* Kill face. */ + while (f->l_first) { + BMLoop *l2 = f->l_first; + + l2->prev->next = l2->next; + l2->next->prev = l2->prev; + f->l_first = l2->next; + + bm_kill_only_loop(bm, l2); + + if (f->l_first == l2) { + f->l_first = nullptr; + } + } + + bm_kill_only_face(bm, f); + fs[i] = nullptr; + } + } + + bm->elem_index_dirty |= BM_VERT | BM_EDGE | BM_FACE; + bm->elem_table_dirty |= BM_VERT | BM_EDGE | BM_FACE; + + /* Relink. */ + for (BMFace *f : fs) { + if (!f) { + continue; + } + + BM_ELEM_API_FLAG_ENABLE(f, final_tag); + + BMLoop *l = f->l_first; + do { + l->e = BM_edge_exists(l->v, l->next->v); + + if (!l->e) { + printf("%s: missing edge! %p %p\n", __func__, l->v, l->next->v); + + l->e = BM_edge_create(bm, l->v, l->next->v, nullptr, BM_CREATE_NOP); + callbacks.on_edge_create(l->e); + } + + bmesh_radial_loop_append(l->e, l); + JVKE_CHECK_ELEMENT(l->e); + + BM_ELEM_API_FLAG_DISABLE(l->e, dup_tag); + BM_ELEM_API_FLAG_DISABLE(l->v, dup_tag); + BM_ELEM_API_FLAG_DISABLE(l->f, dup_tag); + } while ((l = l->next) != f->l_first); + + if (f->head.htype != BM_FACE) { + printf("%s: error!! f was freed!\n", __func__); + continue; + } + + callbacks.on_face_create(f); + } + + JVKE_CHECK_ELEMENT(v_conn); + +#ifdef JVKE_DEBUG + for (int step = 0; step < 2; step++) { + BMVert *v = step ? v_conn : v_del; + BMEdge *e1 = v->e; + + if (e1) { + do { + JVKE_CHECK_ELEMENT(e1); + + BMLoop *l = e1->l; + + if (!l) { + continue; + } + + /* boundary? */ + if (l == l->radial_next && !have_boundary) { + trigger_jvke_error(IS_LOOP_WRONG_RADIAL_LENGTH, saved_obj); + } + + if (!l) { + continue; + } + + do { + JVKE_CHECK_ELEMENT(l); + JVKE_CHECK_ELEMENT(l->v); + JVKE_CHECK_ELEMENT(l->e); + JVKE_CHECK_ELEMENT(l->f); + } while ((l = l->radial_next) != e1->l); + } while ((e1 = BM_DISK_EDGE_NEXT(e1, v)) != v->e); + } + } +#endif + + /* Use euler criteria to check for duplicate faces. */ + if (0 && do_del && v_conn->e) { +#if 0 + int tote = 0, totv = 0, totf = 0; + + BMVert *v = v_conn; + BMEdge *e2 = v->e; + + if (!BM_ELEM_API_FLAG_TEST(v, dup_tag)) { + BM_ELEM_API_FLAG_ENABLE(v, dup_tag); + totv++; + } + + do { + BMVert *v2 = BM_edge_other_vert(e2, v); + + if (!BM_ELEM_API_FLAG_TEST(e2, dup_tag)) { + BM_ELEM_API_FLAG_ENABLE(e2, dup_tag); + tote++; + } + if (!BM_ELEM_API_FLAG_TEST(v2, dup_tag)) { + BM_ELEM_API_FLAG_ENABLE(v2, dup_tag); + totv++; + } + + if (e2->l) { + BMLoop *l_radial = e2->l; + do { + if (BM_ELEM_API_FLAG_TEST(l_radial->f, dup_tag)) { + continue; + } + + totf++; + + BM_ELEM_API_FLAG_ENABLE(l_radial->f, dup_tag); + BMLoop *l = l_radial; + + do { + if (!BM_ELEM_API_FLAG_TEST(l->v, dup_tag)) { + BM_ELEM_API_FLAG_ENABLE(l->v, dup_tag); + totv++; + } + + if (!BM_ELEM_API_FLAG_TEST(l->e, dup_tag)) { + BM_ELEM_API_FLAG_ENABLE(l->e, dup_tag); + tote++; + } + } while ((l = l->next) != l_radial); + } while ((l_radial = l_radial->radial_next) != e2->l); + } + } while ((e2 = BM_DISK_EDGE_NEXT(e2, v)) != v->e); + + int eul = totv - tote + totf; + if (eul != 1) { +#else + { + BMEdge *e2; + BMVert *v = v_conn; +#endif + e2 = v->e; + + do { + BMLoop *l = e2->l; + + if (!l) { + continue; + } + + BMLoop *l_next = l; + + do { + /* No guarantee each face has only one loop in radial + * list. + */ + l_next = l->radial_next; + + while (l_next != l && l_next->f == l->f) { + l_next = l->radial_next; + } + + BMFace *f; + + if ((f = BM_face_find_double(l->f))) { + callbacks.on_face_kill(l->f); + BM_face_kill(bm, l->f); + } + } while (e2->l && (l = l_next) != e2->l); + } while ((e2 = BM_DISK_EDGE_NEXT(e2, v)) != v->e); + } +} + +if (do_del) { + JVKE_CHECK_ELEMENT(v_del); + + if (v_del->e && v_del->e->l) { + printf("%s: vert is not cleared\n", __func__); + } + + bool ok = !v_del->e; + ok = ok || (!v_del->e->l && v_del->e == BM_DISK_EDGE_NEXT(v_del->e, v_del)); + + if (v_del->e) { + BMIter iter; + + BMEdge *e2; + BM_ITER_ELEM (e2, &iter, v_del, BM_EDGES_OF_VERT) { + callbacks.on_edge_kill(e2); + } + + BMFace *f2; + BM_ITER_ELEM (f2, &iter, v_del, BM_FACES_OF_VERT) { + callbacks.on_face_kill(f2); + } + } + + if (1 || ok) { + callbacks.on_vert_kill(v_del); + BM_vert_kill(bm, v_del); + } +} + +#ifdef JVKE_DEBUG +bm_local_obj_free(saved_obj, buf); +#endif + +return v_conn; +} +#ifdef _OTHER_TRACES +# undef _OTHER_TRACES +#endif +} // namespace blender::bmesh diff --git a/source/blender/bmesh/intern/bmesh_core.c b/source/blender/bmesh/intern/bmesh_core.c index d9bae4e6822..cab19d164b3 100644 --- a/source/blender/bmesh/intern/bmesh_core.c +++ b/source/blender/bmesh/intern/bmesh_core.c @@ -6,10 +6,9 @@ * Core BMesh functions for adding, removing BMesh elements. */ -//#define JVKE_DEBUG //#define FORCE_BMESH_CHECK -#if !defined(NDEBUG) || defined(FORCE_BMESH_CHECK) || defined(JVKE_DEBUG) +#if !defined(NDEBUG) || defined(FORCE_BMESH_CHECK) # define BMESH_DEBUG #endif @@ -541,41 +540,6 @@ BMFace *BM_face_create_verts(BMesh *bm, return BM_face_create(bm, vert_arr, edge_arr, len, f_example, create_flag); } -typedef enum { - IS_OK = 0, - IS_NULL = (1 << 0), - IS_WRONG_TYPE = (1 << 1), - - IS_VERT_WRONG_EDGE_TYPE = (1 << 2), - - IS_EDGE_NULL_DISK_LINK = (1 << 3), - IS_EDGE_WRONG_LOOP_TYPE = (1 << 4), - IS_EDGE_WRONG_FACE_TYPE = (1 << 5), - IS_EDGE_NULL_RADIAL_LINK = (1 << 6), - IS_EDGE_ZERO_FACE_LENGTH = (1 << 7), - - IS_LOOP_WRONG_FACE_TYPE = (1 << 8), - IS_LOOP_WRONG_EDGE_TYPE = (1 << 9), - IS_LOOP_WRONG_VERT_TYPE = (1 << 10), - IS_LOOP_VERT_NOT_IN_EDGE = (1 << 11), - IS_LOOP_NULL_CYCLE_LINK = (1 << 12), - IS_LOOP_ZERO_FACE_LENGTH = (1 << 13), - IS_LOOP_WRONG_FACE_LENGTH = (1 << 14), - IS_LOOP_WRONG_RADIAL_LENGTH = (1 << 15), - - IS_FACE_NULL_LOOP = (1 << 16), - IS_FACE_WRONG_LOOP_FACE = (1 << 17), - IS_FACE_NULL_EDGE = (1 << 18), - IS_FACE_NULL_VERT = (1 << 19), - IS_FACE_LOOP_VERT_NOT_IN_EDGE = (1 << 20), - IS_FACE_LOOP_WRONG_RADIAL_LENGTH = (1 << 21), - IS_FACE_LOOP_WRONG_DISK_LENGTH = (1 << 22), - IS_FACE_LOOP_DUPE_LOOP = (1 << 23), - IS_FACE_LOOP_DUPE_VERT = (1 << 24), - IS_FACE_LOOP_DUPE_EDGE = (1 << 25), - IS_FACE_WRONG_LENGTH = (1 << 26), -} BMeshInternalError; - #ifdef BMESH_DEBUG int bmesh_elem_check(void *element, const char htype) @@ -1879,15 +1843,13 @@ BMEdge *bmesh_kernel_join_edge_kill_vert(BMesh *bm, return NULL; } -#ifdef JVKE_DEBUG +#ifdef _ +# undef _ +#endif -# ifdef _ -# undef _ -# endif - -# define _(s) \ - case s: \ - return #s; +#define _(s) \ + case s: \ + return #s; static const char *get_err_code_str(BMeshInternalError code) { @@ -1928,9 +1890,9 @@ static const char *get_err_code_str(BMeshInternalError code) return "(unknown-code)"; } -# undef _ +#undef _ -static char *get_err_str(int err) +const char *bm_get_error_str(int err) { static char buf[1024]; buf[0] = 0; @@ -1938,896 +1900,13 @@ static char *get_err_str(int err) for (int i = 0; i < 27; i++) { if (err & (1 << i)) { strcat(buf, get_err_code_str(1 << i)); + strcat(buf, "|"); } } return buf; } -static void bm_local_obj_free(char *str, char *fixed) -{ - if (str != fixed) { - MEM_freeN(str); - } -} - -# define LOCAL_OBJ_SIZE 512 - -static char *obj_append_line(const char *line, char *str, char *fixed, int *size, int *i) -{ - int len = (int)strlen(line); - - if (*i + len + 1 >= *size) { - *size += len + ((*size) >> 1); - - if (str == fixed) { - str = MEM_mallocN(*size, "buf"); - memcpy(str, fixed, LOCAL_OBJ_SIZE); - } - else { - str = MEM_reallocN(str, *size); - } - } - - memcpy(str + *i, line, len); - str[*i + len] = 0; - - *i += len; - - return str; -} - -/* NotForPr: saves an obj of the neighborhood around an edge prior to collapse - * into a buffer that can be read from a debugger. - */ -static char *bm_save_local_obj_text( - BMesh *UNUSED(bm), int depth, char buf[LOCAL_OBJ_SIZE], const char *fmt, ...) -{ - va_list vl; - va_start(vl, fmt); - - buf[0] = 0; - - BMVert **vs = NULL, **initial_vs = NULL; - BMEdge **es = NULL, **initial_es = NULL; - BMFace **fs = NULL, **initial_fs = NULL; - - BLI_array_staticdeclare(vs, 64); - BLI_array_staticdeclare(es, 64); - BLI_array_staticdeclare(fs, 64); - - BLI_array_staticdeclare(initial_vs, 8); - BLI_array_staticdeclare(initial_es, 8); - BLI_array_staticdeclare(initial_fs, 8); - - SmallHash visit; - BLI_smallhash_init(&visit); - - const char *c = fmt; - while (*c) { - if (*c == ' ' || *c == '\t') { - c++; - continue; - } - - void *ptr = va_arg(vl, void *); - - switch (*c) { - case 'v': - BLI_array_append(vs, (BMVert *)ptr); - BLI_array_append(initial_vs, (BMVert *)ptr); - break; - case 'e': - BLI_array_append(es, (BMEdge *)ptr); - BLI_array_append(initial_es, (BMEdge *)ptr); - break; - case 'f': - BLI_array_append(fs, (BMFace *)ptr); - BLI_array_append(initial_fs, (BMFace *)ptr); - break; - } - - c++; - } - - va_end(vl); - - int tag = 4; - for (int i = 0; i < BLI_array_len(fs); i++) { - BMFace *f = fs[i]; - BMLoop *l = f->l_first; - - do { - l->v->head.api_flag &= ~tag; - l->e->head.api_flag &= ~tag; - } while ((l = l->next) != f->l_first); - } - - for (int i = 0; i < BLI_array_len(es); i++) { - BMEdge *e = es[i]; - - e->v1->head.api_flag &= ~tag; - e->v2->head.api_flag &= ~tag; - } - - for (int i = 0; i < BLI_array_len(vs); i++) { - vs[i]->head.api_flag |= tag; - } - for (int i = 0; i < BLI_array_len(es); i++) { - BMEdge *e = es[i]; - - if (!(e->v1->head.api_flag & tag)) { - BLI_array_append(vs, e->v1); - e->v1->head.api_flag |= tag; - } - - if (!(e->v2->head.api_flag & tag)) { - BLI_array_append(vs, e->v2); - e->v2->head.api_flag |= tag; - } - - e->head.api_flag |= tag; - } - - for (int i = 0; i < BLI_array_len(fs); i++) { - BMFace *f = fs[i]; - BMLoop *l = f->l_first; - - do { - if (!(l->v->head.api_flag & tag)) { - BLI_array_append(vs, l->v); - l->v->head.api_flag |= tag; - } - - if (!(l->e->head.api_flag & tag)) { - BLI_array_append(es, l->e); - l->e->head.api_flag |= tag; - } - } while ((l = l->next) != f->l_first); - } - - struct stack { - BMVert *v; - int depth; - } *stack = NULL; - BLI_array_staticdeclare(stack, 256); - - SmallHash elemset; - BLI_smallhash_init(&elemset); - - for (int i = 0; i < BLI_array_len(vs); i++) { - BLI_smallhash_insert(&elemset, (uintptr_t)vs[i], NULL); - } - for (int i = 0; i < BLI_array_len(es); i++) { - BLI_smallhash_insert(&elemset, (uintptr_t)es[i], NULL); - } - for (int i = 0; i < BLI_array_len(fs); i++) { - BLI_smallhash_insert(&elemset, (uintptr_t)fs[i], NULL); - } - - for (int i = 0; i < BLI_array_len(vs); i++) { - int si = 0; - - BLI_array_clear(stack); - - // connected islands only - if (i > 0) { - break; - } - - BLI_array_grow_one(stack); - - stack[si].v = vs[i]; - stack[si].depth = 0; - si++; - - while (si > 0) { - BLI_array_len_set(stack, BLI_array_len(stack) - 1); - - if (si >= 8192) { - printf("%s: stack error\n", __func__); - } - - si--; - - BMVert *v = stack[si].v; - int startdepth = stack[si].depth; - - void **val; - - if (!BLI_smallhash_ensure_p(&elemset, (uintptr_t)v, &val)) { - *val = NULL; - BLI_array_append(vs, v); - } - - if (!v->e || stack[si].depth > depth) { - continue; - } - - BMEdge *e = v->e; - do { - if (!BLI_smallhash_ensure_p(&visit, (uintptr_t)e, &val)) { - BLI_array_grow_one(stack); - - *val = NULL; - stack[si].v = e->v1; - stack[si].depth = startdepth + 1; - si++; - - BLI_array_grow_one(stack); - - stack[si].v = e->v2; - stack[si].depth = startdepth + 1; - si++; - } - - if (!e->l) { - continue; - } - - BMLoop *l = e->l; - do { - if (!BLI_smallhash_ensure_p(&visit, (uintptr_t)l->f, &val)) { - if (!BLI_smallhash_ensure_p(&elemset, (uintptr_t)l->f, &val)) { - *val = NULL; - BLI_array_append(fs, l->f); - } - - BMLoop *l2 = l; - do { - if (!BLI_smallhash_ensure_p(&visit, (uintptr_t)l->v, &val)) { - BLI_array_grow_one(stack); - - *val = NULL; - stack[si].v = l->v; - stack[si].depth = startdepth + 1; - si++; - } - } while ((l2 = l2->next) != l); - } - } while ((l = l->radial_next) != e->l); - } while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e); - } - } - - char *str = buf; - int size = LOCAL_OBJ_SIZE - 1; - int stri = 0; - - for (int i = 0; i < BLI_array_len(es); i++) { - es[i]->head.api_flag &= ~tag; - } - - char line[128]; - line[0] = 0; - - for (int i = 0; i < BLI_array_len(vs); i++) { - vs[i]->head.api_flag &= ~tag; - } - for (int i = 0; i < BLI_array_len(es); i++) { - es[i]->head.api_flag &= ~tag; - } - for (int i = 0; i < BLI_array_len(fs); i++) { - fs[i]->head.api_flag &= ~tag; - } - - for (int i = 0; i < BLI_array_len(initial_vs); i++) { - initial_vs[i]->head.api_flag |= tag; - } - - for (int i = 0; i < BLI_array_len(initial_es); i++) { - initial_es[i]->head.api_flag |= tag; - initial_es[i]->v1->head.api_flag |= tag; - initial_es[i]->v2->head.api_flag |= tag; - } - - for (int i = 0; i < BLI_array_len(initial_fs); i++) { - BMFace *f = initial_fs[i]; - - f->head.api_flag |= tag; - BMLoop *l = f->l_first; - - do { - l->v->head.api_flag |= tag; - } while ((l = l->next) != f->l_first); - } - - for (int i = 0; i < BLI_array_len(vs); i++) { - BMVert *v = vs[i]; - - if (v->head.api_flag & tag) { - sprintf(line, "#select\n"); - str = obj_append_line(line, str, buf, &size, &stri); - } - - v->head.index = i + 1; - sprintf(line, "v %.4f %.4f %.4f\n", v->co[0], v->co[1], v->co[2]); - - str = obj_append_line(line, str, buf, &size, &stri); - } - - /* save wire edges */ - for (int i = 0; i < BLI_array_len(es); i++) { - BMEdge *e = es[i]; - - if (e->l) { - continue; - } - - sprintf(line, "l %d %d\n", e->v1->head.index, e->v2->head.index); - str = obj_append_line(line, str, buf, &size, &stri); - } - - for (int i = 0; i < BLI_array_len(fs); i++) { - BMFace *f = fs[i]; - BMLoop *l = f->l_first; - - sprintf(line, "f"); - str = obj_append_line(line, str, buf, &size, &stri); - - do { - sprintf(line, " %d", l->v->head.index); - - str = obj_append_line(line, str, buf, &size, &stri); - } while ((l = l->next) != f->l_first); - - str = obj_append_line("\n", str, buf, &size, &stri); - } - - BLI_smallhash_release(&visit); - BLI_smallhash_release(&elemset); - - BLI_array_free(vs); - BLI_array_free(es); - BLI_array_free(fs); - BLI_array_free(stack); - - BLI_array_free(initial_vs); - BLI_array_free(initial_es); - BLI_array_free(initial_fs); - - return str; -} - -static void trigger_jvke_error(int err, char *obj_text) -{ - printf("========= ERROR %s============\n\n%s\n\n", get_err_str(err), obj_text); -} - -char *_last_local_obj = NULL; - -# define JVKE_CHECK_ELEMENT(elem) \ - { \ - int err = 0; \ - if ((err = bmesh_elem_check(elem, (elem)->head.htype))) { \ - trigger_jvke_error(err, saved_obj); \ - } \ - } -#else -# define JVKE_CHECK_ELEMENT(elem) -#endif - -static bool cleanup_vert(BMesh *bm, BMVert *v) -{ - BMEdge *e = v->e; - - if (!e->l || e->l->f == e->l->radial_next->f) { - return false; - } - - bm_logstack_push(); - - BMFace *f_example = NULL; - - do { - BMLoop *l = e->l; - if (!l) { - continue; - } - - f_example = l->f; - break; - } while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e); - - BMVert *v1 = BM_edge_other_vert(v->e, v); - BMVert *v2 = BM_edge_other_vert(BM_DISK_EDGE_NEXT(v->e, v), v); - BMVert *v3 = BM_edge_other_vert(BM_DISK_EDGE_NEXT(BM_DISK_EDGE_NEXT(v->e, v), v), v); - - BMFace *f = BM_face_create_quad_tri(bm, v1, v2, v3, NULL, f_example, BM_CREATE_NOP); - BMLoop *l = f->l_first; - - // ensure correct winding - do { - if (l->radial_next != l && l->radial_next->v == l->v) { - BM_face_normal_flip(bm, f); - break; - } - } while ((l = l->next) != f->l_first); - - BM_vert_kill(bm, v); - - bm_logstack_pop(); - return true; -} - -static void bmesh_kernel_check_val3_vert(BMesh *bm, BMEdge *e) -{ - if (!e->l) { - return; - } - - bm_logstack_push(); - - bool stop; - - do { - stop = true; - - BMLoop *l = e->l; - - if (!l) { - break; - } - - do { - BMLoop *l2 = l->prev; - - if (l2 == l2->radial_next) { - continue; - } - - if (BM_vert_edge_count(l2->v) == 3) { - if (cleanup_vert(bm, l2->v)) { - stop = false; - break; - } - } - } while ((l = l->radial_next) != e->l); - } while (!stop); - - bm_logstack_pop(); -} - -/** - * \brief Join Vert Kill Edge (JVKE) - * - * Collapse an edge, merging surrounding data. - * - * Unlike #BM_vert_collapse_edge & #bmesh_kernel_join_edge_kill_vert - * which only handle 2 valence verts, - * this can handle any number of connected edges/faces. - * - *
- * Before: -> After:
- * +-+-+-+    +-+-+-+
- * | | | |    | \ / |
- * +-+-+-+    +--+--+
- * | | | |    | / \ |
- * +-+-+-+    +-+-+-+
- * 
- */ -BMVert *bmesh_kernel_join_vert_kill_edge( - BMesh *bm, BMEdge *e, BMVert *v_kill, const bool do_del, const bool combine_flags) -{ - BMVert *v_conn = BM_edge_other_vert(e, v_kill); - -#ifdef JVKE_DEBUG - char buf[LOCAL_OBJ_SIZE]; - - bool have_boundary = false; - - if (_last_local_obj) { - free(_last_local_obj); - } - - char *saved_obj = bm_save_local_obj_text(bm, 2, buf, "e", e); - _last_local_obj = strdup(saved_obj); -#endif - - /* Free any surrounding valence-3 rings disconnected from the edge. */ - bmesh_kernel_check_val3_vert(bm, e); - - BMFace **fs = NULL; - BMEdge **deles = NULL; - BLI_array_staticdeclare(fs, 32); - BLI_array_staticdeclare(deles, 32); - - BMVert *v_del = BM_edge_other_vert(e, v_conn); - const int tag = _FLAG_WALK_ALT; // using bmhead.api_flag here - const int dup_tag = _FLAG_OVERLAP; - const int final_tag = _FLAG_JF; - - JVKE_CHECK_ELEMENT(v_conn); - JVKE_CHECK_ELEMENT(v_del); - - /* first clear tags */ - for (int i = 0; i < 2; i++) { - BMVert *v = i ? v_del : v_conn; - - if (!v->e) { - continue; - } - - BMEdge *e2 = v->e; - do { - if (!e2->l) { - continue; - } - - BMLoop *l = e2->l; - do { - BM_ELEM_API_FLAG_DISABLE(l->f, tag); - -#ifdef JVKE_DEBUG - if (l->radial_next == l) { - have_boundary = true; - } -#endif - - BM_ELEM_API_FLAG_DISABLE(l->f, dup_tag); - BM_ELEM_API_FLAG_DISABLE(l->e, dup_tag); - BM_ELEM_API_FLAG_DISABLE(l->v, dup_tag); - } while ((l = l->radial_next) != e2->l); - } while ((e2 = BM_DISK_EDGE_NEXT(e2, v)) != v->e); - } - - /* now build face list */ - for (int i = 0; i < 2; i++) { - BMVert *v = i ? v_del : v_conn; - BMEdge *e2 = v->e; - - if (!e2 || !e2->l) { - continue; - } - - do { - BMLoop *l = e2->l; - - if (!l) { - continue; - } - - do { - if (!BM_ELEM_API_FLAG_TEST(l->f, tag)) { - BM_ELEM_API_FLAG_ENABLE(l->f, tag); - - BMLoop *l2 = l; - do { - BM_ELEM_API_FLAG_DISABLE(l2->e, tag); - } while ((l2 = l2->next) != l); - - BLI_array_append(fs, l->f); - } - } while ((l = l->radial_next) != e2->l); - } while ((e2 = BM_DISK_EDGE_NEXT(e2, v)) != v->e); - } - - /* unlink loops */ - for (int i = 0; i < BLI_array_len(fs); i++) { - BMFace *f = fs[i]; - BMLoop *l = f->l_first; - - do { - BMEdge *e2 = l->e; - - l->radial_next->radial_prev = l->radial_prev; - l->radial_prev->radial_next = l->radial_next; - - if (l == e2->l) { - e2->l = l->radial_next; - } - - if (l == e2->l) { - e2->l = NULL; - } - } while ((l = l->next) != f->l_first); - } - - /* swap verts */ - for (int i = 0; i < BLI_array_len(fs); i++) { - BMFace *f = fs[i]; - BMLoop *l = f->l_first, *lnext = NULL; - - do { - lnext = l->next; - - if (l->v == v_del) { - l->v = v_conn; - } - - BM_ELEM_API_FLAG_DISABLE(l->v, tag); - - for (int step = 0; step < 2; step++) { - BMVert *v_edge = step ? l->e->v2 : l->e->v1; - BMVert *v_other = BM_edge_other_vert(l->e, v_edge); - - if (v_edge != v_del) { - continue; - } - - if (v_other == v_conn) { - /* flag for later selection */ - if (!BM_ELEM_API_FLAG_TEST(l->e, tag)) { - BLI_array_append(deles, l->e); - } - - BM_ELEM_API_FLAG_ENABLE(l->e, tag); - } - else { - BMEdge *e3; - - if ((e3 = BM_edge_exists(v_conn, v_other))) { - if (combine_flags) { - /* TODO: stop flagging sharp edges by the abscene of the BM_ELEM_SMOOTH flag*/ - bool remove_smooth = !BM_elem_flag_test(l->e, BM_ELEM_SMOOTH); - remove_smooth = remove_smooth || !BM_elem_flag_test(e3, BM_ELEM_SMOOTH); - - e3->head.hflag |= l->e->head.hflag; - - if (remove_smooth) { - BM_elem_flag_disable(e3, BM_ELEM_SMOOTH); - } - } - - /* flag for later deletion */ - if (!BM_ELEM_API_FLAG_TEST(l->e, tag)) { - BLI_array_append(deles, l->e); - } - - BM_ELEM_API_FLAG_ENABLE(l->e, tag); - - l->e = e3; - } - else { - bmesh_disk_vert_replace(l->e, v_conn, v_del); - } - } - } - } while ((l = lnext) != f->l_first); - } - - for (int i = 0; i < BLI_array_len(deles); i++) { - BMEdge *e2 = deles[i]; - - if (e2->l != NULL) { - printf("%s: e2->l was not null!\n", __func__); - continue; - } - - e2->l = NULL; - - if (deles[i]->l) { - printf("%s: edge is not cleared\n", __func__); - } - - BM_edge_kill(bm, deles[i]); - } - - for (int i = 0; i < BLI_array_len(fs); i++) { - BMFace *f = fs[i]; - BMLoop *l, *lnext; - - /* validate */ - l = f->l_first; - do { - lnext = l == l->next ? NULL : l->next; - - if (l->v == l->next->v) { - l->prev->next = l->next; - l->next->prev = l->prev; - - if (l == l->f->l_first) { - l->f->l_first = l->next; - } - - l->f->len--; - - if (l == l->f->l_first) { - l->f->l_first = NULL; - } - - bm_kill_only_loop(bm, l); - } - } while (lnext && (l = lnext) != f->l_first); - - if (f->len <= 2) { - /* kill face */ - while (f->l_first) { - BMLoop *l2 = f->l_first; - - l2->prev->next = l2->next; - l2->next->prev = l2->prev; - f->l_first = l2->next; - - bm_kill_only_loop(bm, l2); - - if (f->l_first == l2) { - f->l_first = NULL; - } - } - - bm_kill_only_face(bm, f); - fs[i] = NULL; - } - } - - bm->elem_index_dirty |= BM_VERT | BM_EDGE | BM_FACE; - bm->elem_table_dirty |= BM_VERT | BM_EDGE | BM_FACE; - - /* relink */ - for (int i = 0; i < BLI_array_len(fs); i++) { - BMFace *f = fs[i]; - - if (!f) { - continue; - } - - BM_ELEM_API_FLAG_ENABLE(f, final_tag); - - BMLoop *l = f->l_first; - do { - l->e = BM_edge_exists(l->v, l->next->v); - - if (!l->e) { - printf("warning: missing edge! %p %p\n", l->v, l->next->v); - l->e = BM_edge_create(bm, l->v, l->next->v, NULL, BM_CREATE_NOP); - } - - bmesh_radial_loop_append(l->e, l); - JVKE_CHECK_ELEMENT(l->e); - - BM_ELEM_API_FLAG_DISABLE(l->e, dup_tag); - BM_ELEM_API_FLAG_DISABLE(l->v, dup_tag); - BM_ELEM_API_FLAG_DISABLE(l->f, dup_tag); - } while ((l = l->next) != f->l_first); - } - - JVKE_CHECK_ELEMENT(v_conn); - -#ifdef JVKE_DEBUG - for (int step = 0; step < 2; step++) { - BMVert *v = step ? v_conn : v_del; - BMEdge *e1 = v->e; - - if (e1) { - do { - JVKE_CHECK_ELEMENT(e1); - - BMLoop *l = e1->l; - - if (!l) { - continue; - } - - /* boundary? */ - if (l == l->radial_next && !have_boundary) { - trigger_jvke_error(IS_LOOP_WRONG_RADIAL_LENGTH, saved_obj); - } - - if (!l) { - continue; - } - - do { - JVKE_CHECK_ELEMENT(l); - JVKE_CHECK_ELEMENT(l->v); - JVKE_CHECK_ELEMENT(l->e); - JVKE_CHECK_ELEMENT(l->f); - } while ((l = l->radial_next) != e1->l); - } while ((e1 = BM_DISK_EDGE_NEXT(e1, v)) != v->e); - } - } -#endif - - /* use euler criteria to check for duplicate faces */ - if (do_del && v_conn->e) { - int tote = 0, totv = 0, totf = 0; - - BMVert *v = v_conn; - BMEdge *e2 = v->e; - - if (!BM_ELEM_API_FLAG_TEST(v, dup_tag)) { - BM_ELEM_API_FLAG_ENABLE(v, dup_tag); - totv++; - } - - do { - BMVert *v2 = BM_edge_other_vert(e2, v); - - if (!BM_ELEM_API_FLAG_TEST(e2, dup_tag)) { - BM_ELEM_API_FLAG_ENABLE(e2, dup_tag); - tote++; - } - if (!BM_ELEM_API_FLAG_TEST(v2, dup_tag)) { - BM_ELEM_API_FLAG_ENABLE(v2, dup_tag); - totv++; - } - - if (e2->l) { - BMLoop *l_radial = e2->l; - do { - if (BM_ELEM_API_FLAG_TEST(l_radial->f, dup_tag)) { - continue; - } - - totf++; - - BM_ELEM_API_FLAG_ENABLE(l_radial->f, dup_tag); - BMLoop *l = l_radial; - - do { - if (!BM_ELEM_API_FLAG_TEST(l->v, dup_tag)) { - BM_ELEM_API_FLAG_ENABLE(l->v, dup_tag); - totv++; - } - - if (!BM_ELEM_API_FLAG_TEST(l->e, dup_tag)) { - BM_ELEM_API_FLAG_ENABLE(l->e, dup_tag); - tote++; - } - } while ((l = l->next) != l_radial); - } while ((l_radial = l_radial->radial_next) != e2->l); - } - } while ((e2 = BM_DISK_EDGE_NEXT(e2, v)) != v->e); - - int eul = totv - tote + totf; - if (eul != 1) { - // printf("%s: possible duplicate geometry! %d\n", __func__, eul); - e2 = v->e; - - do { - BMLoop *l = e2->l; - - if (!l) { - continue; - } - - BMLoop *l_next = l; - - do { - /* no guarantee each face has only one loop in radial - list */ - l_next = l->radial_next; - - while (l_next != l && l_next->f == l->f) { - l_next = l->radial_next; - } - - BMFace *f; - - if ((f = BM_face_find_double(l->f))) { - BM_face_kill(bm, l->f); - } - } while (e2->l && (l = l_next) != e2->l); - } while ((e2 = BM_DISK_EDGE_NEXT(e2, v)) != v->e); - } - } - // printf("v_del: %p, v_conn: %p\n", v_del->e, v_conn->e); - if (do_del) { - JVKE_CHECK_ELEMENT(v_del); - - if (v_del->e && v_del->e->l) { - printf("%s: vert is not cleared\n", __func__); - } - - if (!(v_del->e && v_del->e->l)) { - BM_vert_kill(bm, v_del); - } - } - -#ifdef JVKE_DEBUG - bm_local_obj_free(saved_obj, buf); -#endif - - BLI_array_free(deles); - BLI_array_free(fs); - - return v_conn; -} -#ifdef _OTHER_TRACES -# undef _OTHER_TRACES -#endif - /*original version of bmesh_kernel_join_vert_kill_edge*/ BMVert *bmesh_kernel_join_vert_kill_edge_fast(BMesh *bm, BMEdge *e_kill, diff --git a/source/blender/bmesh/intern/bmesh_log.cc b/source/blender/bmesh/intern/bmesh_log.cc index 56e4c4ade4f..6a5efff2068 100644 --- a/source/blender/bmesh/intern/bmesh_log.cc +++ b/source/blender/bmesh/intern/bmesh_log.cc @@ -1285,7 +1285,7 @@ void BMLogSetDiff::swap_verts(BMesh *bm, /* Ensure id wasn't mangled in customdata swap. */ BM_ELEM_CD_SET_INT(v, cd_id, lv->id.id); - if (callbacks) { + if (callbacks && callbacks->on_vert_change) { callbacks->on_vert_change(v, callbacks->userdata, old_customdata); } } @@ -1366,7 +1366,7 @@ void BMLogSetDiff::restore_edges(BMesh *bm, entry->assign_elem_id(bm, e, le->id, true); - if (callbacks->on_edge_add) { + if (callbacks && callbacks->on_edge_add) { callbacks->on_edge_add(e, callbacks->userdata); } } @@ -1413,7 +1413,7 @@ void BMLogSetDiff::swap_edges(BMesh *bm, entry->swap_logedge(bm, le->id, e, le); - if (callbacks->on_edge_change) { + if (callbacks && callbacks->on_edge_change) { callbacks->on_edge_change(e, callbacks->userdata, old_customdata); } } @@ -1524,7 +1524,7 @@ void BMLogSetDiff::swap_faces(BMesh *bm, /* Ensure id wasn't mangled in customdata swap. */ BM_ELEM_CD_SET_INT(f, cd_id, lf->id.id); - if (callbacks) { + if (callbacks && callbacks->on_face_change) { callbacks->on_face_change(f, callbacks->userdata, old_customdata, lf->flag); } } diff --git a/source/blender/bmesh/intern/bmesh_private.h b/source/blender/bmesh/intern/bmesh_private.h index dfc3dcbcfc4..b782d9b9ec7 100644 --- a/source/blender/bmesh/intern/bmesh_private.h +++ b/source/blender/bmesh/intern/bmesh_private.h @@ -11,6 +11,41 @@ * parts of the bmesh internals. */ +typedef enum { + IS_OK = 0, + IS_NULL = (1 << 0), + IS_WRONG_TYPE = (1 << 1), + + IS_VERT_WRONG_EDGE_TYPE = (1 << 2), + + IS_EDGE_NULL_DISK_LINK = (1 << 3), + IS_EDGE_WRONG_LOOP_TYPE = (1 << 4), + IS_EDGE_WRONG_FACE_TYPE = (1 << 5), + IS_EDGE_NULL_RADIAL_LINK = (1 << 6), + IS_EDGE_ZERO_FACE_LENGTH = (1 << 7), + + IS_LOOP_WRONG_FACE_TYPE = (1 << 8), + IS_LOOP_WRONG_EDGE_TYPE = (1 << 9), + IS_LOOP_WRONG_VERT_TYPE = (1 << 10), + IS_LOOP_VERT_NOT_IN_EDGE = (1 << 11), + IS_LOOP_NULL_CYCLE_LINK = (1 << 12), + IS_LOOP_ZERO_FACE_LENGTH = (1 << 13), + IS_LOOP_WRONG_FACE_LENGTH = (1 << 14), + IS_LOOP_WRONG_RADIAL_LENGTH = (1 << 15), + + IS_FACE_NULL_LOOP = (1 << 16), + IS_FACE_WRONG_LOOP_FACE = (1 << 17), + IS_FACE_NULL_EDGE = (1 << 18), + IS_FACE_NULL_VERT = (1 << 19), + IS_FACE_LOOP_VERT_NOT_IN_EDGE = (1 << 20), + IS_FACE_LOOP_WRONG_RADIAL_LENGTH = (1 << 21), + IS_FACE_LOOP_WRONG_DISK_LENGTH = (1 << 22), + IS_FACE_LOOP_DUPE_LOOP = (1 << 23), + IS_FACE_LOOP_DUPE_VERT = (1 << 24), + IS_FACE_LOOP_DUPE_EDGE = (1 << 25), + IS_FACE_WRONG_LENGTH = (1 << 26), +} BMeshInternalError; + #ifdef __cplusplus extern "C" { #endif @@ -90,6 +125,11 @@ enum { */ void poly_rotate_plane(const float normal[3], float (*verts)[3], uint nverts); +void bm_kill_only_face(BMesh *bm, BMFace *f); +void bm_kill_only_loop(BMesh *bm, BMLoop *l); +const char *bm_get_error_str(int err); +int bmesh_elem_check(void *element, const char htype); + /* include the rest of our private declarations */ #include "bmesh_structure.h" diff --git a/source/blender/bmesh/operators/bmo_removedoubles.c b/source/blender/bmesh/operators/bmo_removedoubles.c index 101b1da1506..9a0257d28d7 100644 --- a/source/blender/bmesh/operators/bmo_removedoubles.c +++ b/source/blender/bmesh/operators/bmo_removedoubles.c @@ -443,7 +443,7 @@ void bmo_pointmerge_exec(BMesh *bm, BMOperator *op) #define USE_BM_EDGE_COLLAPSE -void bmo_collapse_exec(BMesh *bm, BMOperator *op) +ATTR_NO_OPT void bmo_collapse_exec(BMesh *bm, BMOperator *op) { BMOperator weldop; BMWalker walker; @@ -510,7 +510,7 @@ void bmo_collapse_exec(BMesh *bm, BMOperator *op) BLI_stack_pop(edge_stack, &e); #ifdef USE_BM_EDGE_COLLAPSE - if (e->head.htype != BM_EDGE) { + if (BM_elem_is_free((BMElem*)e, BM_EDGE)) { continue; } #endif diff --git a/source/blender/editors/sculpt_paint/sculpt_undo.cc b/source/blender/editors/sculpt_paint/sculpt_undo.cc index 9f8be14107a..0d1565f9f9c 100644 --- a/source/blender/editors/sculpt_paint/sculpt_undo.cc +++ b/source/blender/editors/sculpt_paint/sculpt_undo.cc @@ -706,7 +706,9 @@ static void bmesh_undo_on_vert_kill(BMVert *v, void *userdata) int ni = BM_ELEM_CD_GET_INT(v, data->cd_vert_node_offset); // data->do_full_recalc = true; - if (ni < 0) { + bool bad = ni == -1 || !BKE_pbvh_get_node_leaf_safe(data->pbvh, ni); + + if (bad) { #if 0 // not sure this is really an error // something went wrong printf("%s: error, vertex %d is not in pbvh; ni was: %d\n", -- 2.30.2 From 029b6ab9a26bc9b16e866f2cddae80c9586c1023 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Sun, 4 Jun 2023 17:47:43 -0700 Subject: [PATCH 158/279] temp-sculpt-dyntopo: Suppress false positive warning --- source/blender/blenkernel/intern/pbvh_bmesh.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/blender/blenkernel/intern/pbvh_bmesh.cc b/source/blender/blenkernel/intern/pbvh_bmesh.cc index eaa97f75fa9..006d993e9a8 100644 --- a/source/blender/blenkernel/intern/pbvh_bmesh.cc +++ b/source/blender/blenkernel/intern/pbvh_bmesh.cc @@ -1073,8 +1073,8 @@ void bke_pbvh_insert_face_finalize(PBVH *pbvh, BMFace *f, const int ni) do { int ni2 = BM_ELEM_CD_GET_INT(l->v, pbvh->cd_vert_node_offset); - if (ni2 < 0 || ni2 >= pbvh->totnode || !(pbvh->nodes[ni2].flag & PBVH_Leaf) || - !(pbvh->nodes[ni2].bm_unique_verts->contains(l->v))) + if (ni2 != DYNTOPO_NODE_NONE && + (ni2 < 0 || ni2 >= pbvh->totnode || !(pbvh->nodes[ni2].flag & PBVH_Leaf))) { printf("%s: pbvh corruption\n", __func__); ni2 = DYNTOPO_NODE_NONE; -- 2.30.2 From 370be3d365c68059a37f576166a3b567b748f996 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Sun, 4 Jun 2023 20:44:58 -0700 Subject: [PATCH 159/279] temp-sculpt-dyntopo: Remove pattern based triangle subdivider This turned out to be a dead end. The original idea of using a pattern based subdivider was to preserve quads. But that turned out to be more trouble than it was worth and just converged to triangle in the end anyway, except for little floating quads that messed up smoothing. So I removed support for quads (quite a while ago) and recent tests have shown a simple edge splitter is faster and produces higher quality results. --- source/blender/blenkernel/intern/dyntopo.cc | 897 ++++-------------- .../blenkernel/intern/dyntopo_intern.hh | 12 +- .../blender/blenkernel/intern/pbvh_bmesh.cc | 17 +- source/blender/bmesh/intern/bmesh_collapse.hh | 21 +- 4 files changed, 208 insertions(+), 739 deletions(-) diff --git a/source/blender/blenkernel/intern/dyntopo.cc b/source/blender/blenkernel/intern/dyntopo.cc index f9c59643670..914a0ecffca 100644 --- a/source/blender/blenkernel/intern/dyntopo.cc +++ b/source/blender/blenkernel/intern/dyntopo.cc @@ -50,6 +50,8 @@ //#define CLEAR_TAGS_IN_THREAD +#define EDGE_QUEUE_FLAG BM_ELEM_TAG + using blender::float2; using blender::float3; using blender::float4; @@ -62,10 +64,7 @@ namespace blender::bke::dyntopo { using namespace blender::bke::sculpt; -static void pbvh_split_edges(struct EdgeQueueContext *eq_ctx, - PBVH *pbvh, - BMesh *bm, - Span edges); +static void pbvh_split_edge(struct EdgeQueueContext *eq_ctx, BMEdge *e); static void edge_queue_create_local(EdgeQueueContext *eq_ctx, PBVH *pbvh, @@ -306,9 +305,9 @@ static PBVHTopologyUpdateMode edge_queue_test(EdgeQueueContext *eq_ctx, static void edge_queue_insert_unified(EdgeQueueContext *eq_ctx, BMEdge *e, float w) { - if (!(e->head.hflag & BM_ELEM_TAG)) { + if (!(e->head.hflag & EDGE_QUEUE_FLAG)) { eq_ctx->edge_heap.insert(w, e); - e->head.hflag |= BM_ELEM_TAG; + e->head.hflag |= EDGE_QUEUE_FLAG; } } @@ -516,7 +515,7 @@ static void add_split_edge_recursive( do { BMLoop *l_adjacent[2] = {l_iter->next, l_iter->prev}; for (int i = 0; i < (int)ARRAY_SIZE(l_adjacent); i++) { - if (l_adjacent[i]->e->head.hflag & BM_ELEM_TAG) { + if (l_adjacent[i]->e->head.hflag & EDGE_QUEUE_FLAG) { continue; } @@ -544,9 +543,9 @@ static void add_split_edge_recursive( } } - if (!skinny_bad_edge(l_edge->e)) { - edge_queue_insert_unified(eq_ctx, l_edge->e, len_sq); - } + // if (!skinny_bad_edge(l_edge->e)) { + edge_queue_insert_unified(eq_ctx, l_edge->e, len_sq); + //} if ((l_edge->radial_next != l_edge)) { const float len_sq_cmp = len_sq * EVEN_EDGELEN_THRESHOLD; @@ -558,7 +557,7 @@ static void add_split_edge_recursive( do { BMLoop *l_adjacent[2] = {l_iter->next, l_iter->prev}; for (int i = 0; i < (int)ARRAY_SIZE(l_adjacent); i++) { - if (l_adjacent[i]->e->head.hflag & BM_ELEM_TAG) { + if (l_adjacent[i]->e->head.hflag & EDGE_QUEUE_FLAG) { continue; } @@ -595,7 +594,7 @@ static void edge_thread_data_insert(EdgeQueueThreadData *tdata, BMEdge *e) BMElem elem; memcpy(&elem, (BMElem *)e, sizeof(BMElem)); - elem.head.hflag = e->head.hflag | BM_ELEM_TAG; + elem.head.hflag = e->head.hflag | EDGE_QUEUE_FLAG; int64_t iold = *((int64_t *)&e->head.index); int64_t inew = *((int64_t *)&elem.head.index); @@ -624,8 +623,8 @@ static void add_split_edge_recursive_threaded(EdgeQueueThreadData *tdata, } } while ((l = l->radial_next) != l_edge); - if (l_edge->e->head.hflag & BM_ELEM_TAG) { - // return; + if (l_edge->e->head.hflag & EDGE_QUEUE_FLAG) { + return; } #ifdef USE_EDGEQUEUE_FRONTFACE @@ -721,7 +720,7 @@ static void unified_edge_queue_task_cb(void *__restrict userdata, * don't need to do this on x86, but I'm not sure about ARM. */ BMEdge edge = *l->e; - edge.head.hflag &= ~BM_ELEM_TAG; + edge.head.hflag &= ~EDGE_QUEUE_FLAG; int64_t *t1 = (int64_t *)&edge.head.index; int64_t *t2 = (int64_t *)&l->e->head.index; @@ -1299,7 +1298,7 @@ static void unified_edge_queue_create(EdgeQueueContext *eq_ctx, for (BMFace *f : *node->bm_faces) { BMLoop *l = f->l_first; do { - l->e->head.hflag &= ~BM_ELEM_TAG; + l->e->head.hflag &= ~EDGE_QUEUE_FLAG; l->v->head.hflag &= ~BM_ELEM_TAG; l->f->head.hflag &= ~(BM_ELEM_TAG | BM_ELEM_TAG_ALT); } while ((l = l->next) != f->l_first); @@ -1311,7 +1310,7 @@ static void unified_edge_queue_create(EdgeQueueContext *eq_ctx, for (int i = 0; i < count; i++) { for (BMEdge *e : tdata[i].edges) { - e->head.hflag &= ~BM_ELEM_TAG; + e->head.hflag &= ~EDGE_QUEUE_FLAG; } } @@ -1322,7 +1321,7 @@ static void unified_edge_queue_create(EdgeQueueContext *eq_ctx, continue; } - e->head.hflag &= ~BM_ELEM_TAG; + e->head.hflag &= ~EDGE_QUEUE_FLAG; if (e->l && e->l != e->l->radial_next->radial_next) { /* Fix non-manifold "fins". */ @@ -1351,8 +1350,8 @@ static void unified_edge_queue_create(EdgeQueueContext *eq_ctx, verts.append(e->v1); verts.append(e->v2); - e->v1->head.hflag |= BM_ELEM_TAG; - e->v2->head.hflag |= BM_ELEM_TAG; + e->v1->head.hflag |= EDGE_QUEUE_FLAG; + e->v2->head.hflag |= EDGE_QUEUE_FLAG; float w; if (edge_queue_test(eq_ctx, pbvh, e, &w)) { @@ -1370,7 +1369,7 @@ static void unified_edge_queue_create(EdgeQueueContext *eq_ctx, } for (BMEdge *e : eq_ctx->edge_heap.values()) { - e->head.hflag |= BM_ELEM_TAG; + e->head.hflag |= EDGE_QUEUE_FLAG; } if (push_subentry) { @@ -1448,7 +1447,7 @@ static void edge_queue_create_local(EdgeQueueContext *eq_ctx, for (int i = 0; i < count; i++) { for (BMEdge *e : tdata[i].edges) { - e->head.hflag &= ~BM_ELEM_TAG; + e->head.hflag &= ~EDGE_QUEUE_FLAG; } } @@ -1457,9 +1456,9 @@ static void edge_queue_create_local(EdgeQueueContext *eq_ctx, e->v1->head.hflag &= ~BM_ELEM_TAG; e->v2->head.hflag &= ~BM_ELEM_TAG; - if (!(e->head.hflag & BM_ELEM_TAG)) { + if (!(e->head.hflag & EDGE_QUEUE_FLAG)) { edges.append(e); - e->head.hflag |= BM_ELEM_TAG; + e->head.hflag |= EDGE_QUEUE_FLAG; } } } @@ -1497,7 +1496,7 @@ static void edge_queue_create_local(EdgeQueueContext *eq_ctx, BMEdge *e1 = v1->e; do { - e1->head.hflag &= ~BM_ELEM_TAG; + e1->head.hflag &= ~EDGE_QUEUE_FLAG; e1 = BM_DISK_EDGE_NEXT(e1, v1); } while (e1 != v1->e); @@ -1506,7 +1505,7 @@ static void edge_queue_create_local(EdgeQueueContext *eq_ctx, /* Re-tag edge list. */ for (int i = 0; i < edges.size(); i++) { - edges[i]->head.hflag |= BM_ELEM_TAG; + edges[i]->head.hflag |= EDGE_QUEUE_FLAG; } int totstep = 3; @@ -1524,7 +1523,7 @@ static void edge_queue_create_local(EdgeQueueContext *eq_ctx, BMEdge *e1 = v1->e; do { - if (e1->head.hflag & BM_ELEM_TAG) { + if (e1->head.hflag & EDGE_QUEUE_FLAG) { len += lens[e1->head.index]; totlen += 1.0f; } @@ -1548,7 +1547,7 @@ static void edge_queue_create_local(EdgeQueueContext *eq_ctx, for (int i = 0; i < edges.size(); i++) { BMEdge *e = edges[i]; - e->head.hflag &= ~BM_ELEM_TAG; + e->head.hflag &= ~EDGE_QUEUE_FLAG; pbvh_check_vert_boundary(pbvh, e->v1); pbvh_check_vert_boundary(pbvh, e->v2); @@ -2080,41 +2079,9 @@ EdgeQueueContext::EdgeQueueContext(BrushTester *brush_tester_, } max_steps = (DYNTOPO_MAX_ITER * edge_limit_multiply) << (totop - 1); - max_subd = max_steps >> (totop - 1); - - if (totop > 0) { - split_edges_size = steps[0]; - split_edges = (BMEdge **)MEM_malloc_arrayN(split_edges_size, sizeof(void *), __func__); - etot = 0; - - subd_edges.clear(); - } - else { - split_edges = nullptr; - split_edges_size = 0; - } } -void EdgeQueueContext::flush_subdivision() -{ - if (etot == 0) { - return; - } - - modified = true; - subd_edges.clear(); - - pbvh_split_edges(this, pbvh, pbvh->header.bm, {split_edges, etot}); - - count_subd += etot; - VALIDATE_LOG(pbvh->bm_log); - etot = 0; -} - -EdgeQueueContext::~EdgeQueueContext() -{ - MEM_SAFE_FREE(split_edges); -} +EdgeQueueContext::~EdgeQueueContext() {} void EdgeQueueContext::start() { @@ -2126,8 +2093,16 @@ bool EdgeQueueContext::done() return totop == 0 || edge_heap.empty() || current_i >= max_steps; } -void EdgeQueueContext::finish() +ATTR_NO_OPT void EdgeQueueContext::finish() { + while (!edge_heap.empty()) { + BMEdge *e = edge_heap.pop_max(); + + if (!BM_elem_is_free(reinterpret_cast(e), BM_EDGE)) { + e->head.hflag &= ~EDGE_QUEUE_FLAG; + } + } + if (mode & PBVH_Cleanup) { modified |= do_cleanup_3_4(this, pbvh); @@ -2198,33 +2173,19 @@ void EdgeQueueContext::step() BMEdge *e = nullptr; if (count >= steps[curop]) { - if (ops[curop] == PBVH_Subdivide) { // && count_subd < max_subd) { - BM_log_entry_add_ex(pbvh->header.bm, pbvh->bm_log, true); - flush_subdivision(); - flushed_ = true; - } - curop = (curop + 1) % totop; count = 0; - } -#if 0 - if (curop == 0 && count_subd >= max_subd && totop > 1 && ops[0] == PBVH_Subdivide && - ops[1] == PBVH_Collapse) - { - if (etot > 0) { - flush_subdivision(); - } - curop = 1; + flushed_ = true; } -#endif RandomNumberGenerator srand(PIL_check_seconds_timer() * 10000); auto do_smooth = [&](BMVert *v) { - // if (srand.get_float() > 0.75) { - // surface_smooth_v_safe(ss, pbvh, v, surface_smooth_fac, true); - //} + float prob = ops[curop] == PBVH_Subdivide ? 0.25 : 0.75; + if (srand.get_float() > prob) { + surface_smooth_v_safe(ss, pbvh, v, surface_smooth_fac, true); + } }; switch (ops[curop]) { @@ -2243,7 +2204,7 @@ void EdgeQueueContext::step() fabs(calc_weighted_length(this, e->v1, e->v2, SPLIT) - w) > w * 0.1)) { if (e && !bm_elem_is_free((BMElem *)e, BM_EDGE)) { - e->head.hflag &= ~BM_ELEM_TAG; + e->head.hflag &= ~EDGE_QUEUE_FLAG; edge_heap.insert(calc_weighted_length(this, e->v1, e->v2, SPLIT), e); } @@ -2264,8 +2225,10 @@ void EdgeQueueContext::step() do_smooth(e->v1); do_smooth(e->v2); - e->head.hflag &= ~BM_ELEM_TAG; + e->head.hflag &= ~EDGE_QUEUE_FLAG; + pbvh_split_edge(this, e); +#if 0 /* Add complete faces. */ BMLoop *l = e->l; if (l) { @@ -2281,12 +2244,13 @@ void EdgeQueueContext::step() } if (subd_edges.add(l->e)) { - l->e->head.hflag &= ~BM_ELEM_TAG; + l->e->head.hflag &= ~EDGE_QUEUE_FLAG; split_edges[etot++] = l->e; } } while ((l2 = l2->next) != l); } while ((l = l->radial_next) != e->l); } +#endif break; } case PBVH_Collapse: { @@ -2326,7 +2290,6 @@ void EdgeQueueContext::step() modified = true; pbvh_bmesh_collapse_edge(pbvh, e, e->v1, e->v2, this); - flushed_ = true; VALIDATE_LOG(pbvh->bm_log); break; } @@ -2337,12 +2300,6 @@ void EdgeQueueContext::step() break; } - if (edge_heap.empty() && etot > 0) { - /* Flush subdivision, it may add more to queue.*/ - flush_subdivision(); - flushed_ = true; - } - count++; current_i++; } @@ -2408,7 +2365,7 @@ bool remesh_topology(BrushTester *brush_tester, eq_ctx.step(); if (PIL_check_seconds_timer() - time > 350.0 / 1000.0) { - // XXX break; + break; } } @@ -2416,624 +2373,8 @@ bool remesh_topology(BrushTester *brush_tester, return eq_ctx.modified; } -#define SPLIT_TAG BM_ELEM_TAG_ALT - -/* - -#generate shifted and mirrored patterns -# [number of verts, vert_connections... ] -table = [ - [4, -1, 3, -1, -1], - [5, -1, 3, -1, 0, -1], - [6, -1, 3, -1, 5, -1, 1] -] - -table2 = {} - -def getmask(row): - mask = 0 - for i in range(len(row)): - if row[i] >= 0: - mask |= 1 << i - return mask - -ii = 0 -for row in table: - n = row[0] - row = row[1:] - - mask = getmask(row) - table2[mask] = [n] + row - - for step in range(2): - for i in range(n): - row2 = [] - for j in range(n): - j2 = row[(j + i) % n] - - if j2 != -1: - j2 = (j2 - i + n) % n - - row2.append(j2) - - if row2[0] != -1: - continue - - mask2 = getmask(row2) - if mask2 not in table2: - table2[mask2] = [n] + row2 - - #reverse row - for i in range(n): - if row[i] != -1: - row[i] = n - row[i] - - row.reverse() - -maxk = 0 -for k in table2: - maxk = max(maxk, k) - -buf = 'static const int splitmap[%i][16] = {\n' % (maxk+1) -buf += ' //{numverts, vert_connections...}\n' - -for k in range(maxk+1): - line = "" - - if k not in table2: - line += ' {-1},' - else: - line += ' {' - row = table2[k] - for j in range(len(row)): - if j > 0: - line += ", " - line += str(row[j]) - line += '},' - - while len(line) < 35: - line += " " - line += "//" + str(k) + " " - - if k in table2: - for i in range(table2[k][0]): - ch = "1" if k & (1 << i) else "0" - line += str(ch) + " " - - buf += line + "\n" -buf += '};\n' -print(buf) - -*/ -static const int splitmap[43][16] = { - //{numverts, vert_connections...} - {-1}, // 0 - {-1}, // 1 - {4, -1, 3, -1, -1}, // 2 0 1 0 0 - {-1}, // 3 - {4, -1, -1, 0, -1}, // 4 0 0 1 0 - {-1}, // 5 - {-1}, // 6 - {-1}, // 7 - {4, -1, -1, -1, 1}, // 8 0 0 0 1 - {-1}, // 9 - {5, -1, 3, -1, 0, -1}, // 10 0 1 0 1 0 - {-1}, // 11 - {-1}, // 12 - {-1}, // 13 - {-1}, // 14 - {-1}, // 15 - {-1}, // 16 - {-1}, // 17 - {5, -1, 3, -1, -1, 1}, // 18 0 1 0 0 1 - {-1}, // 19 - {5, -1, -1, 4, -1, 1}, // 20 0 0 1 0 1 - {-1}, // 21 - {-1}, // 22 - {-1}, // 23 - {-1}, // 24 - {-1}, // 25 - {-1}, // 26 - {-1}, // 27 - {-1}, // 28 - {-1}, // 29 - {-1}, // 30 - {-1}, // 31 - {-1}, // 32 - {-1}, // 33 - {-1}, // 34 - {-1}, // 35 - {-1}, // 36 - {-1}, // 37 - {-1}, // 38 - {-1}, // 39 - {-1}, // 40 - {-1}, // 41 - {6, -1, 3, -1, 5, -1, 1}, // 42 0 1 0 1 0 1 -}; - float dyntopo_params[5] = {5.0f, 1.0f, 4.0f}; -static void pbvh_split_edges(EdgeQueueContext *eq_ctx, PBVH *pbvh, BMesh *bm, Span edges) -{ - bm_logstack_push(); - bm_log_message(" == split edges == "); - - /* Try to improve quality by inserting new edge into queue. - * This is a bit tricky since we don't want to expand outside - * the brush radius too much, but we can't stay strictly inside - * either. We use tri_in_range for this. - */ - - auto test_near_brush = [&](BMEdge *e, float * /*co*/, float *r_w = nullptr) { - float w = calc_weighted_length(eq_ctx, e->v1, e->v2, SPLIT); - if (r_w) { - *r_w = w; - } - - if (w == 0.0f || !e->l) { - return false; - } - - if (skinny_bad_edge(e)) { - return false; - } - - BMLoop *l = e->l; - do { - BMVert *vs[3] = {l->v, l->next->v, l->next->next->v}; - if (eq_ctx->brush_tester->tri_in_range(vs, l->f->no)) { - return true; - } - } while ((l = l->radial_next) != e->l); - - return false; - }; - - Vector faces; - -#define SUBD_ADD_TO_QUEUE - - const int node_updateflag = PBVH_UpdateBB | PBVH_UpdateOriginalBB | PBVH_UpdateNormals | - PBVH_UpdateCurvatureDir | PBVH_UpdateTriAreas | - PBVH_UpdateDrawBuffers | PBVH_RebuildDrawBuffers | PBVH_UpdateTris | - PBVH_UpdateNormals; - - for (BMEdge *e : edges) { - check_vert_fan_are_tris(pbvh, e->v1); - check_vert_fan_are_tris(pbvh, e->v2); - } - - for (BMEdge *e : edges) { - BMLoop *l = e->l; - - /* Clear tags. */ - e->head.hflag &= ~SPLIT_TAG; - e->v1->head.hflag &= ~SPLIT_TAG; - e->v2->head.hflag &= ~SPLIT_TAG; - - if (!l) { - continue; - } - - /* Clear tags in wider neighborhood and flag valence/boundary for update. */ - do { - BMLoop *l2 = l->f->l_first; - - do { - l2->e->head.hflag &= ~SPLIT_TAG; - l2->v->head.hflag &= ~SPLIT_TAG; - - pbvh_boundary_update_bmesh(pbvh, l2->v); - dyntopo_add_flag(pbvh, l2->v, SCULPTFLAG_NEED_VALENCE | SCULPTFLAG_NEED_TRIANGULATE); - } while ((l2 = l2->next) != l->f->l_first); - - l->f->head.hflag &= ~SPLIT_TAG; - } while ((l = l->radial_next) != e->l); - } - - /* Tag edges and faces to split. */ - for (int i = 0; i < edges.size(); i++) { - BMEdge *e = edges[i]; - - e->head.index = 0; - e->head.hflag |= SPLIT_TAG; - } - - for (int i = 0; i < edges.size(); i++) { - BMEdge *e = edges[i]; - BMLoop *l = e->l; - - if (!l) { - continue; - } - - do { - if (!(l->f->head.hflag & SPLIT_TAG)) { - BMLoop *l2 = l; - do { - l2->v->head.hflag &= ~SPLIT_TAG; - } while ((l2 = l2->next) != l); - l->f->head.hflag |= SPLIT_TAG; - - if (l->f->len == 3) { - l->f->head.index = l->f->len; - faces.append(l->f); - } - } - - } while ((l = l->radial_next) != e->l); - } - - int totface = faces.size(); - for (int i = 0; i < totface; i++) { - BMFace *f = faces[i]; - BMLoop *l = f->l_first; - - f->head.hflag |= SPLIT_TAG; - BM_log_face_removed(pbvh->header.bm, pbvh->bm_log, f); - BM_idmap_release(pbvh->bm_idmap, (BMElem *)f, true); - - /* Build pattern mask and store in f->head.index. */ - int mask = 0; - int j = 0; - do { - if (l->e->head.hflag & SPLIT_TAG) { - mask |= 1 << j; - } - - j++; - } while ((l = l->next) != f->l_first); - - f->head.index = mask; - } - - bm_log_message(" == split edges (edge split) == "); - -#ifdef SUBD_ADD_TO_QUEUE - Vector new_edges; -#endif - - for (int i = 0; i < edges.size(); i++) { - BMEdge *e = edges[i]; - - if (!e || !(e->head.hflag & SPLIT_TAG)) { - continue; - } - - BMVert *v1 = e->v1; - BMVert *v2 = e->v2; - BMEdge *newe = nullptr; - - e->head.hflag &= ~SPLIT_TAG; - - BKE_pbvh_bmesh_check_origdata(eq_ctx->ss, e->v1, pbvh->stroke_id); - BKE_pbvh_bmesh_check_origdata(eq_ctx->ss, e->v2, pbvh->stroke_id); - - validate_edge(pbvh, e); - - BM_idmap_check_assign(pbvh->bm_idmap, (BMElem *)e->v1); - BM_idmap_check_assign(pbvh->bm_idmap, (BMElem *)e->v2); - BM_log_edge_removed(pbvh->header.bm, pbvh->bm_log, e); - - BM_idmap_release(pbvh->bm_idmap, (BMElem *)e, true); - - BMVert *newv = BM_edge_split(pbvh->header.bm, e, e->v1, &newe, 0.5f); - validate_edge(pbvh, newe); - - /* Flag new vertex as not needing original data update, since we interpolated it. */ - sculpt::stroke_id_test(eq_ctx->ss, {reinterpret_cast(newv)}, STROKEID_USER_ORIGINAL); - - newe->head.hflag &= ~(SPLIT_TAG | BM_ELEM_TAG); - e->head.hflag &= ~(SPLIT_TAG | BM_ELEM_TAG); - - BM_log_vert_added(bm, pbvh->bm_log, newv); - BM_log_edge_added(bm, pbvh->bm_log, e); - BM_log_edge_added(bm, pbvh->bm_log, newe); - - edge_queue_insert_val34_vert(eq_ctx, newv); - -#ifdef SUBD_ADD_TO_QUEUE - new_edges.append(e); - new_edges.append(newe); -#endif - - PBVH_CHECK_NAN(newv->co); - - validate_edge(pbvh, e); - validate_edge(pbvh, newe); - validate_vert(pbvh, newv, CHECK_VERT_ALL); - - newv->head.hflag |= SPLIT_TAG; - - pbvh_boundary_update_bmesh(pbvh, newv); - dyntopo_add_flag(pbvh, newv, SCULPTFLAG_NEED_VALENCE | SCULPTFLAG_NEED_TRIANGULATE); - - BMVert *otherv = e->v1 != newv ? e->v1 : e->v2; - pbvh_boundary_update_bmesh(pbvh, e->v1 != newv ? e->v1 : e->v2); - dyntopo_add_flag(pbvh, otherv, SCULPTFLAG_NEED_VALENCE | SCULPTFLAG_NEED_TRIANGULATE); - - BM_ELEM_CD_SET_INT(newv, pbvh->cd_vert_node_offset, DYNTOPO_NODE_NONE); - - int ni = BM_ELEM_CD_GET_INT(v1, pbvh->cd_vert_node_offset); - - if (ni == DYNTOPO_NODE_NONE) { - ni = BM_ELEM_CD_GET_INT(v2, pbvh->cd_vert_node_offset); - } - - if (ni >= pbvh->totnode || !(pbvh->nodes[ni].flag & PBVH_Leaf)) { - printf("%s: error\n", __func__); - } - - /* This should rarely happen. */ - if (ni == DYNTOPO_NODE_NONE) { - ni = DYNTOPO_NODE_NONE; - - for (int j = 0; j < 2; j++) { - BMVert *v = nullptr; - - switch (j) { - case 0: - v = v1; - break; - case 1: - v = v2; - break; - } - - if (!v->e) { - continue; - } - - BMEdge *e2 = v->e; - do { - if (!e2->l) { - break; - } - - BMLoop *l = e2->l; - do { - int ni2 = BM_ELEM_CD_GET_INT(l->f, pbvh->cd_face_node_offset); - - if (ni2 >= 0 && ni2 < pbvh->totnode && (pbvh->nodes[ni2].flag & PBVH_Leaf)) { - ni = ni2; - goto outerbreak; - } - } while ((l = l->radial_next) != e2->l); - } while ((e2 = BM_DISK_EDGE_NEXT(e2, v)) != v->e); - } - outerbreak:; - } - - if (ni != DYNTOPO_NODE_NONE) { - PBVHNode *node = pbvh->nodes + ni; - - if (!(node->flag & PBVH_Leaf)) { - printf("pbvh error in pbvh_split_edges!\n"); - - BM_ELEM_CD_SET_INT(newv, pbvh->cd_vert_node_offset, DYNTOPO_NODE_NONE); - - continue; - } - - node->flag |= (PBVHNodeFlags)node_updateflag; - - node->bm_unique_verts->add(newv); - - BM_ELEM_CD_SET_INT(newv, pbvh->cd_vert_node_offset, ni); - // BM_ELEM_CD_SET_INT(newv, pbvh->cd_vert_node_offset, -1); - } - else { - BM_ELEM_CD_SET_INT(newv, pbvh->cd_vert_node_offset, DYNTOPO_NODE_NONE); - printf("%s: error!\n", __func__); - } - } - - bm_log_message(" == split edges (triangulate) == "); - - /* Subdivide from template. */ - - Vector vs; - Vector newfaces; - - for (int i = 0; i < totface; i++) { - BMFace *f = faces[i]; - - if (!(f->head.hflag & SPLIT_TAG)) { - continue; - } - - int ni = BM_ELEM_CD_GET_INT(f, pbvh->cd_face_node_offset); - - if (ni < 0 || ni >= pbvh->totnode || !(pbvh->nodes[ni].flag & PBVH_Leaf)) { - printf("%s: error!\n", __func__); - ni = DYNTOPO_NODE_NONE; - } - - BMLoop *l = f->l_first; - int totmask = 0, mask = 0; - int j = 0; - - do { - if (l->v->head.hflag & SPLIT_TAG) { - mask |= 1 << j; - totmask++; - } - j++; - } while ((l = l->next) != f->l_first); - - int flen = j; - - if (mask < 0 || mask >= (int)ARRAY_SIZE(splitmap)) { - printf("splitmap error! flen: %d totmask: %d mask: %d\n", flen, totmask, mask); - continue; - } - - const int *pat = splitmap[mask]; - int n = pat[0]; - - if (n < 0) { - printf("%s: error 1! %d %d\n", __func__, n, flen); - continue; - } - - if (n != f->len || n != flen) { - printf("%s: error 2! %d %d\n", __func__, n, flen); - continue; - } - - BMFace *f2 = f; - - vs.resize(n); - - l = f->l_first; - j = 0; - do { - vs[j++] = l->v; - } while ((l = l->next) != f->l_first); - - if (j != n) { - printf("%s: error 1!\n", __func__); - continue; - } - - newfaces.resize(newfaces.size() + n); - - int count = 0; - - for (j = 0; j < n; j++) { - if (pat[j + 1] < 0) { - continue; - } - - BMVert *v1 = vs[j], *v2 = vs[pat[j + 1]]; - BMLoop *l1 = nullptr, *l2 = nullptr; - BMLoop *rl = nullptr; - - BMLoop *l3 = f2->l_first; - do { - if (l3->v == v1) { - l1 = l3; - } - else if (l3->v == v2) { - l2 = l3; - } - } while ((l3 = l3->next) != f2->l_first); - - if (l1 == l2 || !l1 || !l2) { - printf("%s: error 2!\n", __func__); - continue; - } - - validate_face(pbvh, f2, CHECK_FACE_MANIFOLD); - - bool log_edge = true; - BMFace *newf = nullptr; - BMEdge *exist_e = BM_edge_exists(v1, v2); - - if (exist_e) { - log_edge = false; - } - else { - newf = BM_face_split(bm, f2, l1, l2, &rl, nullptr, false); - exist_e = newf ? rl->e : nullptr; - } - - if (newf && exist_e && exist_e->l) { - exist_e->head.hflag &= ~BM_ELEM_TAG; - -#ifdef SUBD_ADD_TO_QUEUE - new_edges.append(exist_e); -#endif - - check_face_is_manifold(pbvh, newf); - check_face_is_manifold(pbvh, f2); - check_face_is_manifold(pbvh, f); - - validate_face(pbvh, f2, CHECK_FACE_MANIFOLD); - validate_face(pbvh, newf, CHECK_FACE_MANIFOLD); - - if (log_edge) { - BM_log_edge_added(bm, pbvh->bm_log, rl->e); - } - - bool ok = ni != DYNTOPO_NODE_NONE; - ok = ok && BM_ELEM_CD_GET_INT(v1, pbvh->cd_vert_node_offset) != DYNTOPO_NODE_NONE; - ok = ok && BM_ELEM_CD_GET_INT(v2, pbvh->cd_vert_node_offset) != DYNTOPO_NODE_NONE; - - if (ok) { - PBVHNode *node = pbvh->nodes + ni; - - node->flag |= (PBVHNodeFlags)node_updateflag; - - node->bm_faces->add(newf); - BM_ELEM_CD_SET_INT(newf, pbvh->cd_face_node_offset, ni); - } - else { - BM_ELEM_CD_SET_INT(newf, pbvh->cd_face_node_offset, DYNTOPO_NODE_NONE); - } - - if (count < n) { - newfaces[count++] = newf; - } - else { - printf("%s: error 4!\n", __func__); - } - f2 = newf; - } - else { - printf("%s: split error 2!\n", __func__); - continue; - } - } - - for (j = 0; j < count; j++) { - if (BM_ELEM_CD_GET_INT(newfaces[j], pbvh->cd_face_node_offset) == DYNTOPO_NODE_NONE) { - BKE_pbvh_bmesh_add_face(pbvh, newfaces[j], false, true); - } - - if (newfaces[j] != f) { - BM_log_face_added(bm, pbvh->bm_log, newfaces[j]); - } -#if 1 - if (newfaces[j]->len != 3) { - printf("%s: tesselation error!\n", __func__); - } -#endif - } - - if (f->len != 3) { - printf("%s: tesselation error!\n", __func__); - } - - if (BM_ELEM_CD_GET_INT(f, pbvh->cd_face_node_offset) == DYNTOPO_NODE_NONE) { - BKE_pbvh_bmesh_add_face(pbvh, f, false, true); - } - - BM_log_face_added(bm, pbvh->bm_log, f); - } - -#ifdef SUBD_ADD_TO_QUEUE - for (BMEdge *e : new_edges) { - float3 co = e->v1->co; - co = (co + e->v2->co) * 0.5f; - - float w = 0.0f; - w = calc_weighted_length(eq_ctx, e->v1, e->v2, SPLIT); - - if (test_near_brush(e, co)) { - if (w > eq_ctx->limit_len_max_sqr) { - add_split_edge_recursive(eq_ctx, e->l, w, eq_ctx->limit_len_max, 0); - } - else if (w < eq_ctx->limit_len_min_sqr && (eq_ctx->mode & PBVH_Collapse)) { - edge_queue_insert_unified(eq_ctx, e, w); - } - } - } -#endif - - bm_logstack_pop(); -} void detail_size_set(PBVH *pbvh, float detail_size, float detail_range) { detail_range = max_ff(detail_range, 0.1f); @@ -3119,6 +2460,146 @@ void BKE_pbvh_bmesh_add_face(PBVH *pbvh, struct BMFace *f, bool log_face, bool f bm_logstack_pop(); } +namespace blender::bke::dyntopo { +ATTR_NO_OPT static void pbvh_split_edge(EdgeQueueContext *eq_ctx, BMEdge *e) +{ + PBVH *pbvh = eq_ctx->pbvh; + BMesh *bm = pbvh->header.bm; + BMEdge *newe; + BMFace *newf = nullptr; + + if (!e->l) { + return; + } + + check_vert_fan_are_tris(pbvh, e->v1); + check_vert_fan_are_tris(pbvh, e->v2); + + Vector fs; + + BMLoop *l = e->l; + do { + if (BM_ELEM_CD_GET_INT(l->f, pbvh->cd_face_node_offset) != DYNTOPO_NODE_NONE) { + pbvh_bmesh_face_remove(pbvh, l->f, true, true, true); + BM_idmap_release(pbvh->bm_idmap, reinterpret_cast(l->f), true); + fs.append(l->f); + } + + BMLoop *l2 = l->f->l_first; + + do { + pbvh_boundary_update_bmesh(pbvh, l2->v); + dyntopo_add_flag(pbvh, l2->v, SCULPTFLAG_NEED_VALENCE | SCULPTFLAG_NEED_TRIANGULATE); + } while ((l2 = l2->next) != l->f->l_first); + } while ((l = l->radial_next) != e->l); + + BM_log_edge_removed(bm, pbvh->bm_log, e); + BM_idmap_release(pbvh->bm_idmap, reinterpret_cast(e), true); + + dyntopo_add_flag(pbvh, e->v1, SCULPTFLAG_NEED_VALENCE); + dyntopo_add_flag(pbvh, e->v2, SCULPTFLAG_NEED_VALENCE); + pbvh_boundary_update_bmesh(pbvh, e->v1); + pbvh_boundary_update_bmesh(pbvh, e->v2); + + BMVert *newv = BM_edge_split(bm, e, e->v1, &newe, 0.5f); + + /* Remove edge-in-minmax-heap tag. */ + e->head.hflag &= ~EDGE_QUEUE_FLAG; + newe->head.hflag &= ~EDGE_QUEUE_FLAG; + + BM_ELEM_CD_SET_INT(newv, pbvh->cd_vert_node_offset, DYNTOPO_NODE_NONE); + BM_idmap_check_assign(pbvh->bm_idmap, reinterpret_cast(newv)); + BM_log_vert_added(bm, pbvh->bm_log, newv); + + BM_idmap_check_assign(pbvh->bm_idmap, reinterpret_cast(e)); + BM_log_edge_added(bm, pbvh->bm_log, e); + BM_idmap_check_assign(pbvh->bm_idmap, reinterpret_cast(newe)); + BM_log_edge_added(bm, pbvh->bm_log, newe); + + dyntopo_add_flag(pbvh, newv, SCULPTFLAG_NEED_VALENCE | SCULPTFLAG_NEED_TRIANGULATE); + pbvh_boundary_update_bmesh(pbvh, newv); + + for (BMFace *f : fs) { + BMLoop *l = f->l_first; + do { + if (l->v == newv) { + break; + } + } while ((l = l->next) != f->l_first); + + BMEdge *exist_e = BM_edge_exists(l->v, l->next->next->v); + BMLoop *newl; + newf = BM_face_split(bm, f, l, l->next->next, &newl, nullptr, true); + + dyntopo_add_flag( + pbvh, l->next->next->v, SCULPTFLAG_NEED_VALENCE | SCULPTFLAG_NEED_TRIANGULATE); + pbvh_boundary_update_bmesh(pbvh, l->next->next->v); + + BM_ELEM_CD_SET_INT(f, pbvh->cd_face_node_offset, DYNTOPO_NODE_NONE); + BM_idmap_check_assign(pbvh->bm_idmap, reinterpret_cast(f)); + BKE_pbvh_bmesh_add_face(pbvh, f, true, false); + + if (!newf || newf == f) { + continue; + } + + BM_ELEM_CD_SET_INT(newf, pbvh->cd_face_node_offset, DYNTOPO_NODE_NONE); + + if (!exist_e) { + BM_log_edge_added(bm, pbvh->bm_log, newl->e); + BM_idmap_check_assign(pbvh->bm_idmap, reinterpret_cast(newl->e)); + BKE_pbvh_bmesh_add_face(pbvh, newf, true, false); + } + + newl->e->head.hflag &= ~EDGE_QUEUE_FLAG; + + copy_v3_v3(newf->no, f->no); +#if 1 + BMVert *vs[3] = {newl->v, newl->next->v, newl->next->next->v}; + if (eq_ctx->brush_tester->tri_in_range(vs, newl->f->no)) { + float w = 0.0f; + PBVHTopologyUpdateMode mode = edge_queue_test(eq_ctx, pbvh, newl->e, &w); + + if (mode == PBVH_Subdivide) { + add_split_edge_recursive(eq_ctx, newl, w, eq_ctx->limit_len_max, 0); + } + else if (mode == PBVH_Collapse) { + edge_queue_insert_unified(eq_ctx, newl->e, w); + } + } +#else + BMLoop *l2 = newl; + do { + BMLoop *l3 = l2; + do { + BMVert *vs[3] = {l3->v, l3->next->v, l3->next->next->v}; + + if (l3->e->head.hflag & EDGE_QUEUE_FLAG) { + continue; + } + + if (eq_ctx->brush_tester->tri_in_range(vs, l3->f->no)) { + float w = 0.0f; + PBVHTopologyUpdateMode mode = edge_queue_test(eq_ctx, pbvh, l3->e, &w); + + if (mode == PBVH_Subdivide) { + add_split_edge_recursive(eq_ctx, l3, w, eq_ctx->limit_len_max, 0); + } + else if (mode == PBVH_Collapse) { + edge_queue_insert_unified(eq_ctx, l3->e, w); + } + } + } while ((l3 = l3->next) != l2); + } while ((l2 = l2->radial_next) != newl); +#endif + } + + pbvh_bmesh_check_nodes(pbvh); + + check_for_fins(pbvh, newv); +} +} // namespace blender::bke::dyntopo + #include namespace myinterp { diff --git a/source/blender/blenkernel/intern/dyntopo_intern.hh b/source/blender/blenkernel/intern/dyntopo_intern.hh index 4dd5205702e..63033f9b846 100644 --- a/source/blender/blenkernel/intern/dyntopo_intern.hh +++ b/source/blender/blenkernel/intern/dyntopo_intern.hh @@ -104,8 +104,8 @@ inline bool bm_elem_is_free(BMElem *elem, int htype) SCULPT_CORNER_UV | SCULPT_CORNER_SHARP_ANGLE) #define DYNTOPO_MAX_ITER 512 -#define DYNTOPO_MAX_ITER_COLLAPSE 512 -#define DYNTOPO_MAX_ITER_SUBD 512 +#define DYNTOPO_MAX_ITER_COLLAPSE 16 +#define DYNTOPO_MAX_ITER_SUBD 16 #define DYNTOPO_USE_HEAP #define DYNTOPO_USE_MINMAX_HEAP @@ -136,6 +136,7 @@ inline bool bm_elem_is_free(BMElem *elem, int htype) */ #define DYNTOPO_SAFE_SMOOTH_FAC 0.05f + #ifdef USE_EDGEQUEUE_EVEN_SUBDIV # include "BKE_global.h" #endif @@ -266,16 +267,10 @@ struct EdgeQueueContext { int current_i = 0; int max_steps; PBVH *pbvh; - BMEdge **split_edges; - int split_edges_size; - int etot; - blender::Set subd_edges; bool modified = false; - int count_subd = 0; int count = 0; int curop = 0; - int max_subd; ~EdgeQueueContext(); EdgeQueueContext(BrushTester *brush_tester, @@ -289,7 +284,6 @@ struct EdgeQueueContext { void *mask_cb_data, int edge_limit_multiply); - void flush_subdivision(); void start(); bool done(); void step(); diff --git a/source/blender/blenkernel/intern/pbvh_bmesh.cc b/source/blender/blenkernel/intern/pbvh_bmesh.cc index 006d993e9a8..e54b3808c88 100644 --- a/source/blender/blenkernel/intern/pbvh_bmesh.cc +++ b/source/blender/blenkernel/intern/pbvh_bmesh.cc @@ -122,7 +122,7 @@ void pbvh_bmesh_check_nodes_simple(PBVH *pbvh) } } -void pbvh_bmesh_check_nodes(PBVH *pbvh) +ATTR_NO_OPT void pbvh_bmesh_check_nodes(PBVH *pbvh) { BMVert *v; BMIter iter; @@ -644,8 +644,10 @@ void pbvh_bmesh_vert_remove(PBVH *pbvh, BMVert *v) if (ni2 != DYNTOPO_NODE_NONE) { PBVHNode *node = pbvh->nodes + ni2; - node->flag |= PBVHNodeFlags(updateflag); - node->bm_other_verts->remove(v); + if (ni2 >= 0 && ni2 < pbvh->totnode && pbvh->nodes[ni2].flag & PBVH_Leaf) { + node->flag |= PBVHNodeFlags(updateflag); + node->bm_other_verts->remove(v); + } } } while ((l = l->radial_next) != e->l); } while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e); @@ -684,7 +686,12 @@ void pbvh_bmesh_face_remove( } if (ni2 != DYNTOPO_NODE_NONE && ni2 != ni) { - new_ni = ni2; + if (ni2 < 0 || ni2 >= pbvh->totnode || !(pbvh->nodes[ni2].bm_other_verts)) { + printf("error! invalid node index %d!\n", ni2); + } + else { + new_ni = ni2; + } } } @@ -1049,7 +1056,7 @@ static bool point_in_node(const PBVHNode *node, const float co[3]) co[1] <= node->vb.bmax[1] && co[2] >= node->vb.bmin[2] && co[2] <= node->vb.bmax[2]; } -void bke_pbvh_insert_face_finalize(PBVH *pbvh, BMFace *f, const int ni) +ATTR_NO_OPT void bke_pbvh_insert_face_finalize(PBVH *pbvh, BMFace *f, const int ni) { PBVHNode *node = pbvh->nodes + ni; BM_ELEM_CD_SET_INT(f, pbvh->cd_face_node_offset, ni); diff --git a/source/blender/bmesh/intern/bmesh_collapse.hh b/source/blender/bmesh/intern/bmesh_collapse.hh index e3171ccb0a8..8e54a6085ea 100644 --- a/source/blender/bmesh/intern/bmesh_collapse.hh +++ b/source/blender/bmesh/intern/bmesh_collapse.hh @@ -406,7 +406,7 @@ static bool cleanup_vert(BMesh *bm, BMVert *v, Callbacks &callbacks) f = BM_face_create_quad_tri(bm, v1, v2, v3, nullptr, f_example, BM_CREATE_NOP); BMLoop *l = f->l_first; - // ensure correct winding + /* Ensure correct winding. */ do { if (l->radial_next != l && l->radial_next->v == l->v) { BM_face_normal_flip(bm, f); @@ -607,18 +607,12 @@ BMVert *join_vert_kill_edge(BMesh *bm, } while ((e2 = BM_DISK_EDGE_NEXT(e2, v)) != v->e); } - // printf("fs len: %d\n", int(fs.size())); - for (BMFace *f : fs) { callbacks.on_face_kill(f); } /* Unlink loops. */ for (BMFace *f : fs) { - if (f->head.htype != BM_FACE) { - printf("f was freed!\n"); - continue; - } BMLoop *l = f->l_first; do { @@ -678,7 +672,6 @@ BMVert *join_vert_kill_edge(BMesh *bm, if (v_other == v_conn) { /* Flag for later deletion. */ if (!BM_ELEM_API_FLAG_TEST(l->e, tag)) { - // printf("%p l:%p 1\n", l->e->l, l); deles.append(l->e); } @@ -701,7 +694,6 @@ BMVert *join_vert_kill_edge(BMesh *bm, /* Flag for later deletion. */ if (!BM_ELEM_API_FLAG_TEST(l->e, tag)) { - // printf("%p l:%p 2\n", l->e->l, l); deles.append(l->e); } @@ -880,7 +872,7 @@ BMVert *join_vert_kill_edge(BMesh *bm, #endif /* Use euler criteria to check for duplicate faces. */ - if (0 && do_del && v_conn->e) { + if (do_del && v_conn->e) { #if 0 int tote = 0, totv = 0, totf = 0; @@ -977,9 +969,6 @@ if (do_del) { printf("%s: vert is not cleared\n", __func__); } - bool ok = !v_del->e; - ok = ok || (!v_del->e->l && v_del->e == BM_DISK_EDGE_NEXT(v_del->e, v_del)); - if (v_del->e) { BMIter iter; @@ -994,10 +983,8 @@ if (do_del) { } } - if (1 || ok) { - callbacks.on_vert_kill(v_del); - BM_vert_kill(bm, v_del); - } + callbacks.on_vert_kill(v_del); + BM_vert_kill(bm, v_del); } #ifdef JVKE_DEBUG -- 2.30.2 From 9044aefe8f130dbc0759c9e0d9b2ef254a85a91e Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Sun, 4 Jun 2023 20:59:52 -0700 Subject: [PATCH 160/279] temp-sculpt-dyntopo: Fix bug in cleanup mode --- source/blender/blenkernel/intern/dyntopo.cc | 23 +++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/source/blender/blenkernel/intern/dyntopo.cc b/source/blender/blenkernel/intern/dyntopo.cc index 914a0ecffca..0cae9483e5d 100644 --- a/source/blender/blenkernel/intern/dyntopo.cc +++ b/source/blender/blenkernel/intern/dyntopo.cc @@ -1649,8 +1649,7 @@ static bool cleanup_valence_3_4(EdgeQueueContext *ectx, PBVH *pbvh) } PBVHVertRef sv = {(intptr_t)v}; - if (!ectx->brush_tester->vert_in_range(v) || !v->e || - ectx->mask_cb(sv, ectx->mask_cb_data) < 0.5f) { + if (!v->e || ectx->mask_cb(sv, ectx->mask_cb_data) < 0.5f) { continue; } @@ -1963,8 +1962,24 @@ static bool do_cleanup_3_4(EdgeQueueContext *eq_ctx, PBVH *pbvh) BKE_pbvh_bmesh_update_valence(pbvh, {(intptr_t)v}); } - bool ok = BM_ELEM_CD_GET_INT(v, pbvh->cd_valence) < 5; - ok = ok && eq_ctx->brush_tester->vert_in_range(v); + if (BM_ELEM_CD_GET_INT(v, pbvh->cd_valence) > 4) { + continue; + } + + bool ok = eq_ctx->brush_tester->vert_in_range(v); + + if (!ok) { + /* Check if any surrounding vertex is in range. */ + BMEdge *e = v->e; + do { + BMVert *v2 = BM_edge_other_vert(e, v); + + if (eq_ctx->brush_tester->vert_in_range(v2)) { + ok = true; + break; + } + } while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e); + } if (ok) { edge_queue_insert_val34_vert(eq_ctx, v); -- 2.30.2 From 5ba920d371dc001c08c39db18d4e19f11f77781a Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Sun, 4 Jun 2023 22:13:08 -0700 Subject: [PATCH 161/279] temp-sculpt-dyntopo: Fix bug in new split function --- source/blender/blenkernel/intern/dyntopo.cc | 20 +++++++++++++------- source/blender/bmesh/intern/bmesh_core.c | 2 +- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/source/blender/blenkernel/intern/dyntopo.cc b/source/blender/blenkernel/intern/dyntopo.cc index 0cae9483e5d..5c11cddb0c6 100644 --- a/source/blender/blenkernel/intern/dyntopo.cc +++ b/source/blender/blenkernel/intern/dyntopo.cc @@ -2445,15 +2445,16 @@ void BKE_pbvh_bmesh_add_face(PBVH *pbvh, struct BMFace *f, bool log_face, bool f /* Look for node in srounding geometry. */ BMLoop *l = f->l_first; do { - ni = BM_ELEM_CD_GET_INT(l->radial_next->f, pbvh->cd_face_node_offset); + int ni2 = BM_ELEM_CD_GET_INT(l->radial_next->f, pbvh->cd_face_node_offset); - if (ni >= 0 && (!(pbvh->nodes[ni].flag & PBVH_Leaf) || ni >= pbvh->totnode)) { - printf("%s: error: ni: %d totnode: %d\n", __func__, ni, pbvh->totnode); + if (ni2 >= 0 && (ni2 >= pbvh->totnode || !(pbvh->nodes[ni2].flag & PBVH_Leaf))) { + printf("%s: error: ni: %d totnode: %d\n", __func__, ni2, pbvh->totnode); l = l->next; continue; } - if (ni >= 0 && (pbvh->nodes[ni].flag & PBVH_Leaf)) { + if (ni2 >= 0 && (pbvh->nodes[ni2].flag & PBVH_Leaf)) { + ni = ni2; break; } @@ -2504,7 +2505,7 @@ ATTR_NO_OPT static void pbvh_split_edge(EdgeQueueContext *eq_ctx, BMEdge *e) do { pbvh_boundary_update_bmesh(pbvh, l2->v); - dyntopo_add_flag(pbvh, l2->v, SCULPTFLAG_NEED_VALENCE | SCULPTFLAG_NEED_TRIANGULATE); + dyntopo_add_flag(pbvh, l2->v, SCULPTFLAG_NEED_VALENCE); } while ((l2 = l2->next) != l->f->l_first); } while ((l = l->radial_next) != e->l); @@ -2534,6 +2535,10 @@ ATTR_NO_OPT static void pbvh_split_edge(EdgeQueueContext *eq_ctx, BMEdge *e) dyntopo_add_flag(pbvh, newv, SCULPTFLAG_NEED_VALENCE | SCULPTFLAG_NEED_TRIANGULATE); pbvh_boundary_update_bmesh(pbvh, newv); + for (BMFace *f : fs) { + BM_ELEM_CD_SET_INT(f, pbvh->cd_face_node_offset, DYNTOPO_NODE_NONE); + } + for (BMFace *f : fs) { BMLoop *l = f->l_first; do { @@ -2559,11 +2564,12 @@ ATTR_NO_OPT static void pbvh_split_edge(EdgeQueueContext *eq_ctx, BMEdge *e) } BM_ELEM_CD_SET_INT(newf, pbvh->cd_face_node_offset, DYNTOPO_NODE_NONE); + BM_idmap_check_assign(pbvh->bm_idmap, reinterpret_cast(f)); + BKE_pbvh_bmesh_add_face(pbvh, newf, true, false); if (!exist_e) { - BM_log_edge_added(bm, pbvh->bm_log, newl->e); BM_idmap_check_assign(pbvh->bm_idmap, reinterpret_cast(newl->e)); - BKE_pbvh_bmesh_add_face(pbvh, newf, true, false); + BM_log_edge_added(bm, pbvh->bm_log, newl->e); } newl->e->head.hflag &= ~EDGE_QUEUE_FLAG; diff --git a/source/blender/bmesh/intern/bmesh_core.c b/source/blender/bmesh/intern/bmesh_core.c index cab19d164b3..95de7d4efc2 100644 --- a/source/blender/bmesh/intern/bmesh_core.c +++ b/source/blender/bmesh/intern/bmesh_core.c @@ -400,7 +400,7 @@ BMFace *BM_face_copy( * * \note Caller needs to handle customdata. */ -BLI_INLINE BMFace *bm_face_create__internal(BMesh *bm) +static ATTR_NO_OPT BMFace *bm_face_create__internal(BMesh *bm) { BMFace *f; -- 2.30.2 From a974c1ebf2034f72bad6e2c3089962a2a427697c Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Tue, 6 Jun 2023 02:42:02 -0700 Subject: [PATCH 162/279] temp-sculpt-dyntopo: UV and attribute reprojection changes * Attempted to code a better reproject function, kind of turned into a uv smooth funcion with boundary constraints. - The idea of the function is you feed it the same weights you're using to smooth the vertex coordinates and it derives reprojection weights. But UVs turned out to have so many edge cases and other types didn't that it sort of turned into a uv-only thing. * Rewrote edge collapse from scratch yet again, this time with almost no use of bitflag tags. Instead, `blender::Set` is used with static buffers. * Fixed paint tool with corner colors. * Fixed various UV boundary issues. --- source/blender/blenkernel/BKE_customdata.h | 1 + source/blender/blenkernel/BKE_sculpt.hh | 162 +++++ source/blender/blenkernel/intern/dyntopo.cc | 121 +++- .../blenkernel/intern/dyntopo_collapse.cc | 340 ++++++---- .../blenkernel/intern/dyntopo_intern.hh | 24 +- source/blender/blenkernel/intern/paint.cc | 6 + source/blender/blenkernel/intern/pbvh.cc | 17 +- .../blender/blenkernel/intern/pbvh_bmesh.cc | 299 ++++++++- .../blender/blenkernel/intern/pbvh_intern.hh | 4 +- source/blender/bmesh/intern/bmesh_collapse.cc | 119 ++++ source/blender/bmesh/intern/bmesh_collapse.hh | 580 +++++------------- source/blender/bmesh/intern/bmesh_core.c | 2 +- source/blender/bmesh/intern/bmesh_log.cc | 79 ++- .../blender/bmesh/intern/bmesh_log_intern.h | 9 +- .../bmesh/operators/bmo_removedoubles.c | 4 +- source/blender/editors/sculpt_paint/sculpt.cc | 12 +- .../editors/sculpt_paint/sculpt_api.cc | 5 +- .../editors/sculpt_paint/sculpt_cloth.cc | 4 + .../editors/sculpt_paint/sculpt_intern.hh | 9 +- .../editors/sculpt_paint/sculpt_smooth.cc | 111 +++- .../editors/sculpt_paint/sculpt_undo.cc | 50 +- 21 files changed, 1272 insertions(+), 686 deletions(-) diff --git a/source/blender/blenkernel/BKE_customdata.h b/source/blender/blenkernel/BKE_customdata.h index e9df4676b23..72aa4035258 100644 --- a/source/blender/blenkernel/BKE_customdata.h +++ b/source/blender/blenkernel/BKE_customdata.h @@ -364,6 +364,7 @@ void CustomData_copy_data_named(const struct CustomData *source, int dest_index, int count); +/* Swap attributes. Does not respect CD_FLAG_ELEM_NOCOPY. */ void CustomData_bmesh_swap_data(struct CustomData *source, struct CustomData *dest, void *src_block, diff --git a/source/blender/blenkernel/BKE_sculpt.hh b/source/blender/blenkernel/BKE_sculpt.hh index 6f36adc6f5e..133f8a96446 100644 --- a/source/blender/blenkernel/BKE_sculpt.hh +++ b/source/blender/blenkernel/BKE_sculpt.hh @@ -10,7 +10,11 @@ #include "BKE_attribute.h" #include "BKE_paint.h" #include "BKE_pbvh.h" + #include "BLI_compiler_compat.h" +#include "BLI_span.hh" +#include "BLI_vector.hh" +#include "BLI_math_vector_types.hh" /* * Stroke ID API. This API is used to detect if @@ -93,4 +97,162 @@ BLI_INLINE bool test_sculpt_flag(SculptSession *ss, PBVHVertRef vertex, uint8_t { return blender::bke::paint::vertex_attr_get(vertex, ss->attrs.flags) & flag; } +void interp_face_corners( + PBVH *pbvh, PBVHVertRef vertex, Span loops, Span ws, float factor); +bool loop_is_corner(BMLoop *l, int cd_offset); + + +static bool prop_eq(float a, float b, float limit) +{ + return std::fabs(a - b) < limit; +} +static bool prop_eq(float2 a, float2 b, float limit) +{ + return prop_eq(a[0], b[0], limit) && // + prop_eq(a[1], b[1], limit); +} +static bool prop_eq(float3 a, float3 b, float limit) +{ + return prop_eq(a[0], b[0], limit) && // + prop_eq(a[1], b[1], limit) && // + prop_eq(a[2], b[2], limit); +} +static bool prop_eq(float4 a, float4 b, float limit) +{ + return prop_eq(a[0], b[0], limit) && // + prop_eq(a[1], b[1], limit) && // + prop_eq(a[2], b[2], limit) && // + prop_eq(a[3], b[3], limit); +} + +static bool prop_eq_type(void *a, void *b, float limit, eCustomDataType type) +{ + switch (type) { + case CD_PROP_FLOAT: + return prop_eq(*static_cast(a), *static_cast(b), limit); + case CD_PROP_FLOAT2: + return prop_eq(*static_cast(a), *static_cast(b), limit); + case CD_PROP_FLOAT3: + return prop_eq(*static_cast(a), *static_cast(b), limit); + case CD_PROP_COLOR: + return prop_eq(*static_cast(a), *static_cast(b), limit); + } + return false; +} + +/* Finds sets of loops with the same vertex data + * prior to an operation, then re-snaps them afterwards. + */ +struct VertLoopSnapper { + Vector, 16> snap_sets; + Span layers; + Span &ls; + Vector max_indices; + float limit = 0.1; + + VertLoopSnapper(Span ls_, Span layers_) : ls(ls_) + { + snap_sets.resize(ls.size()); + for (auto &snap_set : snap_sets) { + for (int i = 0; i < layers.size(); i++) { + snap_set.append(0); + } + } + + for (int i : layers.index_range()) { + switch (layers[i]->type) { + case CD_PROP_FLOAT: + begin(i); + break; + case CD_PROP_FLOAT2: + begin(i); + break; + case CD_PROP_FLOAT3: + begin(i); + break; + case CD_PROP_COLOR: + begin(i); + break; + } + } + } + + void snap() + { + for (int i : layers.index_range()) { + switch (layers[i]->type) { + case CD_PROP_FLOAT: + do_snap(i); + break; + case CD_PROP_FLOAT2: + do_snap(i); + break; + case CD_PROP_FLOAT3: + do_snap(i); + break; + case CD_PROP_COLOR: + do_snap(i); + break; + } + } + } + + private: + template void begin(int layer_i) + { + CustomDataLayer *layer = layers[layer_i]; + int idx_base = 1; + + for (int i : ls.index_range()) { + if (snap_sets[i][layer_i] != 0) { + continue; + } + + T a = *BM_ELEM_CD_PTR(ls[i], layer->offset); + int set = snap_sets[i][layer_i] = idx_base++; + + for (int j : ls.index_range()) { + if (snap_sets[j][layer_i] != 0) { + continue; + } + + T b = *BM_ELEM_CD_PTR(ls[j], layer->offset); + if (prop_eq(a, b, limit)) { + snap_sets[j][layer_i] = set; + } + } + } + + max_indices.append(idx_base); + } + + template void do_snap(int layer_i) + { + const int cd_offset = layers[layer_i]->offset; + + for (int set_i : IndexRange(max_indices[layer_i])) { + T sum = {}; + float tot = 0.0f; + + for (int i : ls.index_range()) { + if (snap_sets[i][layer_i] == set_i) { + sum += *BM_ELEM_CD_PTR(ls[i], cd_offset); + tot += 1.0f; + } + } + + if (tot == 0.0f) { + continue; + } + + sum /= tot; + + for (int i : ls.index_range()) { + if (snap_sets[i][layer_i] == set_i) { + *BM_ELEM_CD_PTR(ls[i], cd_offset) = sum; + } + } + } + } +}; } // namespace blender::bke::sculpt diff --git a/source/blender/blenkernel/intern/dyntopo.cc b/source/blender/blenkernel/intern/dyntopo.cc index 5c11cddb0c6..c37c4af01d6 100644 --- a/source/blender/blenkernel/intern/dyntopo.cc +++ b/source/blender/blenkernel/intern/dyntopo.cc @@ -308,12 +308,32 @@ static void edge_queue_insert_unified(EdgeQueueContext *eq_ctx, BMEdge *e, float if (!(e->head.hflag & EDGE_QUEUE_FLAG)) { eq_ctx->edge_heap.insert(w, e); e->head.hflag |= EDGE_QUEUE_FLAG; + + if (e->l) { + BMLoop *l = e->l; + do { + BM_log_face_if_modified(eq_ctx->bm, eq_ctx->pbvh->bm_log, l->f); + } while ((l = l->radial_next) != e->l); + } } } static void edge_queue_insert_val34_vert(EdgeQueueContext *eq_ctx, BMVert *v) { eq_ctx->used_verts.append(v); + + BMEdge *e = v->e; + do { + BMLoop *l = e->l; + if (!l) { + continue; + } + + BMLoop *l2 = l; + do { + BM_log_face_if_modified(eq_ctx->bm, eq_ctx->pbvh->bm_log, l2->f); + } while ((l2 = l2->radial_next) != e->l); + } while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e); } /* @@ -1021,14 +1041,17 @@ bool destroy_nonmanifold_fins(PBVH *pbvh, BMEdge *e_root) } } - int nupdateflag = PBVH_UpdateDrawBuffers | PBVH_UpdateBB | PBVH_UpdateTriAreas; - nupdateflag = nupdateflag | PBVH_UpdateNormals | PBVH_UpdateTris | PBVH_RebuildDrawBuffers; + int node_updateflag = PBVH_UpdateDrawBuffers | PBVH_UpdateBB | PBVH_UpdateTriAreas; + node_updateflag = node_updateflag | PBVH_UpdateNormals | PBVH_UpdateTris | + PBVH_RebuildDrawBuffers; if (!minfs.size()) { bm_logstack_pop(); return false; } + const int updateflag = SCULPTFLAG_NEED_VALENCE; + // printf("manifold fin size: %d\n", (int)minfs.size()); const int tag = BM_ELEM_TAG_ALT; @@ -1061,6 +1084,8 @@ bool destroy_nonmanifold_fins(PBVH *pbvh, BMEdge *e_root) do { if (!(l->v->head.hflag & tag)) { l->v->head.hflag |= tag; + pbvh_boundary_update_bmesh(pbvh, l->v); + dyntopo_add_flag(pbvh, l->v, updateflag); vs.append(l->v); } @@ -1090,7 +1115,7 @@ bool destroy_nonmanifold_fins(PBVH *pbvh, BMEdge *e_root) int ni = BM_ELEM_CD_GET_INT(f, pbvh->cd_face_node_offset); if (ni >= 0 && ni < pbvh->totnode) { - pbvh->nodes[ni].flag |= (PBVHNodeFlags)nupdateflag; + pbvh->nodes[ni].flag |= (PBVHNodeFlags)node_updateflag; } pbvh_bmesh_face_remove(pbvh, f, true, true, false); @@ -1098,8 +1123,6 @@ bool destroy_nonmanifold_fins(PBVH *pbvh, BMEdge *e_root) BM_face_kill(pbvh->header.bm, f); } - const int mupdateflag = SCULPTFLAG_NEED_VALENCE; - for (int i = 0; i < es.size(); i++) { BMEdge *e = es[i]; @@ -1108,6 +1131,13 @@ bool destroy_nonmanifold_fins(PBVH *pbvh, BMEdge *e_root) BM_idmap_release(pbvh->bm_idmap, (BMElem *)e, true); BM_edge_kill(pbvh->header.bm, e); } + else { + pbvh_boundary_update_bmesh(pbvh, e->v1); + dyntopo_add_flag(pbvh, e->v1, updateflag); + + pbvh_boundary_update_bmesh(pbvh, e->v2); + dyntopo_add_flag(pbvh, e->v2, updateflag); + } } for (int i = 0; i < vs.size(); i++) { @@ -1122,7 +1152,7 @@ bool destroy_nonmanifold_fins(PBVH *pbvh, BMEdge *e_root) } else { pbvh_boundary_update_bmesh(pbvh, v); - dyntopo_add_flag(pbvh, v, mupdateflag); + dyntopo_add_flag(pbvh, v, updateflag); } } @@ -1296,6 +1326,13 @@ static void unified_edge_queue_create(EdgeQueueContext *eq_ctx, } for (BMFace *f : *node->bm_faces) { + if (BM_elem_is_free(reinterpret_cast(f), BM_FACE)) { + printf("%s: freed face in node!\n", __func__); + node->bm_faces->remove(f); + + continue; + } + BMLoop *l = f->l_first; do { l->e->head.hflag &= ~EDGE_QUEUE_FLAG; @@ -1370,9 +1407,30 @@ static void unified_edge_queue_create(EdgeQueueContext *eq_ctx, for (BMEdge *e : eq_ctx->edge_heap.values()) { e->head.hflag |= EDGE_QUEUE_FLAG; + + if (!e->l) { + continue; + } + + /* Log face/loop attributes. */ + for (int i = 0; i < 2; i++) { + BMVert *v = i ? e->v2 : e->v1; + BMEdge *e2 = e; + + do { + BMLoop *l = e2->l; + if (!l) { + continue; + } + + do { + BM_log_face_modified(eq_ctx->bm, eq_ctx->pbvh->bm_log, l->f); + } while ((l = l->radial_next) != e2->l); + } while ((e2 = BM_DISK_EDGE_NEXT(e2, v)) != v->e); + } } - if (push_subentry) { + if (1 || push_subentry) { BM_log_entry_add_ex(pbvh->header.bm, pbvh->bm_log, true); } } @@ -1660,8 +1718,7 @@ static bool cleanup_valence_3_4(EdgeQueueContext *ectx, PBVH *pbvh) validate_vert(pbvh, v, CHECK_VERT_ALL); - BKE_pbvh_bmesh_check_valence(pbvh, {(intptr_t)v}); - int val = BM_ELEM_CD_GET_INT(v, pbvh->cd_valence); + int val = BM_vert_edge_count(v); if (val != 4 && val != 3) { continue; @@ -2101,6 +2158,16 @@ EdgeQueueContext::~EdgeQueueContext() {} void EdgeQueueContext::start() { current_i = 0; + + for (int i : IndexRange(pbvh->totnode)) { + PBVHNode *node = &pbvh->nodes[i]; + + if ((node->flag & PBVH_Leaf) && (node->flag & PBVH_UpdateTopology)) { + for (BMFace *f : *node->bm_faces) { + BM_log_face_if_modified(bm, pbvh->bm_log, f); + } + } + } } bool EdgeQueueContext::done() @@ -3654,6 +3721,40 @@ void BKE_sculpt_reproject_cdata(SculptSession *ss, return; } + Vector loops; + Vector layers; + typemask = CD_MASK_PROP_FLOAT2; // | CD_MASK_PROP_FLOAT2 | CD_MASK_PROP_FLOAT3 | + //CD_MASK_PROP_COLOR; + for (int i = 0; i < ldata->totlayer; i++) { + CustomDataLayer *layer = ldata->layers + i; + + if (!(CD_TYPE_AS_MASK(layer->type) & typemask)) { + continue; + } + if (layer->flag & CD_FLAG_ELEM_NOINTERP) { + continue; + } + layers.append(layer); + } + + e = v->e; + do { + BMLoop *l = e->l; + if (!l) { + continue; + } + + BMLoop *l2 = l; + do { + BMLoop *l3 = l2->v == v ? l2 : l2->next; + if (!loops.contains(l3)) { + loops.append(l3); + } + } while ((l2 = l2->radial_next) != e->l); + } while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e); + + blender::bke::sculpt::VertLoopSnapper snapper = {loops, layers}; + int totstep = 2; for (int step = 0; step < totstep; step++) { float3 startco2; @@ -3776,6 +3877,8 @@ void BKE_sculpt_reproject_cdata(SculptSession *ss, tots[i] = 0; } + snapper.snap(); + return; // XXX /* Re-snap uvs. */ v = (BMVert *)vertex.i; diff --git a/source/blender/blenkernel/intern/dyntopo_collapse.cc b/source/blender/blenkernel/intern/dyntopo_collapse.cc index 3724d18701b..2ccc68bd04b 100644 --- a/source/blender/blenkernel/intern/dyntopo_collapse.cc +++ b/source/blender/blenkernel/intern/dyntopo_collapse.cc @@ -28,6 +28,7 @@ #include "BKE_dyntopo.hh" #include "BKE_paint.h" #include "BKE_pbvh.h" +#include "BKE_sculpt.h" #include "../../bmesh/intern/bmesh_collapse.hh" #include "bmesh.h" @@ -44,7 +45,9 @@ using blender::float3; using blender::float4; using blender::IndexRange; using blender::Map; +using blender::MutableSpan; using blender::Set; +using blender::Span; using blender::Vector; namespace blender::bke::dyntopo { @@ -453,7 +456,11 @@ static void vert_ring_do(BMesh *bm, bool pbvh_bmesh_collapse_edge_uvs( PBVH *pbvh, BMEdge *e, BMVert *v_conn, BMVert *v_del, EdgeQueueContext *eq_ctx) { + BMesh *bm = pbvh->header.bm; + bm_logstack_push(); + pbvh_check_vert_boundary(pbvh, v_conn); + pbvh_check_vert_boundary(pbvh, v_del); int boundflag1 = BM_ELEM_CD_GET_INT(v_conn, pbvh->cd_boundary_flag); int boundflag2 = BM_ELEM_CD_GET_INT(v_del, pbvh->cd_boundary_flag); @@ -485,7 +492,7 @@ bool pbvh_bmesh_collapse_edge_uvs( return false; } - bool snap = !(boundflag2 & SCULPTVERT_ALL_CORNER); + bool snap = !(boundflag1 & SCULPTVERT_ALL_CORNER); /* Snap non-UV attributes. */ if (snap) { @@ -499,9 +506,10 @@ bool pbvh_bmesh_collapse_edge_uvs( const float v_ws[2] = {0.5f, 0.5f}; const void *v_blocks[2] = {v_del->head.data, v_conn->head.data}; + eCustomDataMask typemask = CD_MASK_PROP_ALL; - CustomData_bmesh_interp( - &pbvh->header.bm->vdata, v_blocks, v_ws, nullptr, 2, v_conn->head.data); + CustomData_bmesh_interp_ex( + &pbvh->header.bm->vdata, v_blocks, v_ws, nullptr, 2, v_conn->head.data, typemask); /* Restore node index. */ BM_ELEM_CD_SET_INT(v_conn, pbvh->cd_vert_node_offset, ni_conn); @@ -515,18 +523,117 @@ bool pbvh_bmesh_collapse_edge_uvs( } } +#if 0 + if (!e->l) { + return snap; + } + /* Deal with UVs. */ - if (e->l) { - BMLoop *l = e->l; + BMLoop *l = e->l; + float ws[2]; + BMLoop *ls[2]; + const void *blocks[2]; - for (int step = 0; step < 2; step++) { - BMVert *v = step ? e->v2 : e->v1; - BMEdge *e2 = v->e; + do { + if (l->v == v_conn) { + ls[0] = l; + ls[1] = l->next; + } + else { + ls[0] = l->next; + ls[1] = l; + } - if (!e2) { + if (snap) { + ws[0] = ws[1] = 0.5f; + } + else { + ws[0] = l->v == v_conn ? 1.0f : 0.0f; + ws[1] = l->v == v_conn ? 0.0f : 0.0f; + } + + blocks[0] = ls[0]->head.data; + blocks[1] = ls[1]->head.data; + +# if 0 + CustomData_bmesh_interp(&bm->ldata, blocks, ws, nullptr, 2, l->head.data); + CustomData_bmesh_copy_data( + &bm->ldata, &pbvh->header.bm->ldata, l->head.data, &l->next->head.data); +# else + + PBVHVertRef vertex = {reinterpret_cast(v_conn)}; + blender::bke::sculpt::interp_face_corners( + pbvh, vertex, Span(ls, 2), Span(ws, 2), 1.0f); +# endif + } while ((l = l->radial_next) != e->l); + + return snap; +#endif + + const float limit = 0.005; + BMLoop *l = e->l; + + for (int step = 0; step < 2; step++) { + BMVert *v = step ? e->v2 : e->v1; + BMEdge *e2 = v->e; + + if (!e2) { + continue; + } + + do { + BMLoop *l2 = e2->l; + + if (!l2) { continue; } + do { + BMLoop *l3 = l2->v != v ? l2->next : l2; + + /* store visit bits for each uv layer in l3->head.index */ + l3->head.index = 0; + } while ((l2 = l2->radial_next) != e2->l); + } while ((e2 = BM_DISK_EDGE_NEXT(e2, v)) != v->e); + } + + float(*uv)[2] = static_cast(BLI_array_alloca(uv, 4 * totuv)); + + l = e->l; + if (!l) { + return snap; + } + + do { + const void *ls2[2] = {l->head.data, l->next->head.data}; + float ws2[2] = {0.5f, 0.5f}; + + if (!snap) { + const int axis = l->v == v_del ? 0 : 1; + + ws2[axis] = 0.0f; + ws2[axis ^ 1] = 1.0f; + } + + for (int step = 0; uv_layer && step < 2; step++) { + BMLoop *l1 = step ? l : l->next; + + for (int k = 0; k < totuv; k++) { + float *luv = (float *)BM_ELEM_CD_GET_VOID_P(l1, uv_layer[k].offset); + + copy_v2_v2(uv[k * 2 + step], luv); + } + } + + CustomData_bmesh_interp(&pbvh->header.bm->ldata, ls2, ws2, nullptr, 2, l->head.data); + CustomData_bmesh_copy_data( + &pbvh->header.bm->ldata, &pbvh->header.bm->ldata, l->head.data, &l->next->head.data); + + for (int step = 0; totuv >= 0 && step < 2; step++) { + BMVert *v = step ? l->next->v : l->v; + BMLoop *l1 = step ? l->next : l; + BMEdge *e2 = v->e; + do { BMLoop *l2 = e2->l; @@ -537,85 +644,36 @@ bool pbvh_bmesh_collapse_edge_uvs( do { BMLoop *l3 = l2->v != v ? l2->next : l2; - /* store visit bits for each uv layer in l3->head.index */ - l3->head.index = 0; - } while ((l2 = l2->radial_next) != e2->l); - } while ((e2 = BM_DISK_EDGE_NEXT(e2, v)) != v->e); - } - - float(*uv)[2] = static_cast(BLI_array_alloca(uv, 4 * totuv)); - - do { - const void *ls2[2] = {l->head.data, l->next->head.data}; - float ws2[2] = {0.5f, 0.5f}; - - if (!snap) { - const int axis = l->v == v_del ? 0 : 1; - - ws2[axis] = 0.0f; - ws2[axis ^ 1] = 1.0f; - } - - for (int step = 0; uv_layer && step < 2; step++) { - BMLoop *l1 = step ? l : l->next; - - for (int k = 0; k < totuv; k++) { - float *luv = (float *)BM_ELEM_CD_GET_VOID_P(l1, uv_layer[k].offset); - - copy_v2_v2(uv[k * 2 + step], luv); - } - } - - CustomData_bmesh_interp(&pbvh->header.bm->ldata, ls2, ws2, nullptr, 2, l->head.data); - CustomData_bmesh_copy_data( - &pbvh->header.bm->ldata, &pbvh->header.bm->ldata, l->head.data, &l->next->head.data); - - for (int step = 0; totuv >= 0 && step < 2; step++) { - BMVert *v = step ? l->next->v : l->v; - BMLoop *l1 = step ? l->next : l; - BMEdge *e2 = v->e; - - do { - BMLoop *l2 = e2->l; - - if (!l2) { + if (!l3 || l3 == l1 || l3 == l || l3 == l->next) { continue; } - do { - BMLoop *l3 = l2->v != v ? l2->next : l2; + for (int k = 0; k < totuv; k++) { + const int flag = 1 << k; - if (!l3 || l3 == l1 || l3 == l || l3 == l->next) { + if (l3->head.index & flag) { continue; } - for (int k = 0; k < totuv; k++) { - const int flag = 1 << k; + const int cd_uv = uv_layer[k].offset; - if (l3->head.index & flag) { - continue; - } + float *luv1 = (float *)BM_ELEM_CD_GET_VOID_P(l1, cd_uv); + float *luv2 = (float *)BM_ELEM_CD_GET_VOID_P(l3, cd_uv); - const int cd_uv = uv_layer[k].offset; + float dx = luv2[0] - uv[k * 2 + step][0]; + float dy = luv2[1] - uv[k * 2 + step][1]; - float *luv1 = (float *)BM_ELEM_CD_GET_VOID_P(l1, cd_uv); - float *luv2 = (float *)BM_ELEM_CD_GET_VOID_P(l3, cd_uv); + float delta = dx * dx + dy * dy; - float dx = luv2[0] - uv[k * 2 + step][0]; - float dy = luv2[1] - uv[k * 2 + step][1]; - - float delta = dx * dx + dy * dy; - - if (delta < 0.001) { - l3->head.index |= flag; - copy_v2_v2(luv2, luv1); - } + if (delta < limit) { + l3->head.index |= flag; + copy_v2_v2(luv2, luv1); } - } while ((l2 = l2->radial_next) != e2->l); - } while ((e2 = BM_DISK_EDGE_NEXT(e2, v)) != v->e); - } - } while ((l = l->radial_next) != e->l); - } + } + } while ((l2 = l2->radial_next) != e2->l); + } while ((e2 = BM_DISK_EDGE_NEXT(e2, v)) != v->e); + } + } while ((l = l->radial_next) != e->l); bm_logstack_pop(); return snap; @@ -651,7 +709,7 @@ class DyntopoCollapseCallbacks { inline void on_vert_create(BMVert *v) { dyntopo_add_flag(pbvh, v, SCULPTFLAG_NEED_VALENCE); - + pbvh_boundary_update_bmesh(pbvh, v); check_new_elem_id(v, pbvh); BM_log_vert_added(bm, pbvh->bm_log, v); } @@ -666,6 +724,11 @@ class DyntopoCollapseCallbacks { BM_log_face_added(bm, pbvh->bm_log, f); BM_ELEM_CD_SET_INT(f, pbvh->cd_face_node_offset, DYNTOPO_NODE_NONE); BKE_pbvh_bmesh_add_face(pbvh, f, false, false); + + BMLoop *l = f->l_first; + do { + pbvh_boundary_update_bmesh(pbvh, l->v); + } while ((l = l->next) != f->l_first); } }; @@ -713,22 +776,30 @@ BMVert *pbvh_bmesh_collapse_edge( return nullptr; } - /* one of the two vertices may be masked, select the correct one for deletion */ - if (!(boundflag1 & SCULPTVERT_ALL_CORNER) || DYNTOPO_MASK(eq_ctx->cd_vert_mask_offset, v1) < - DYNTOPO_MASK(eq_ctx->cd_vert_mask_offset, v2)) - { + float w1 = DYNTOPO_MASK(eq_ctx->cd_vert_mask_offset, v1); + float w2 = DYNTOPO_MASK(eq_ctx->cd_vert_mask_offset, v2); + + bool corner1 = (boundflag1 & SCULPTVERT_ALL_CORNER) || w1 >= 0.85; + bool corner2 = (boundflag2 & SCULPTVERT_ALL_CORNER) || w2 >= 0.85; + + if (corner1 && corner2) { + return nullptr; + } + + /* One of the two vertices may be masked or a corner, + * select the correct one for deletion. + */ + if (corner2 && !corner1) { v_del = v1; v_conn = v2; } else { v_del = v2; v_conn = v1; - - SWAP(int, boundflag1, boundflag2); } /* Don't collapse across boundaries. */ - if ((boundflag1 & SCULPTVERT_ALL_CORNER) || + if (boundflag2 != 0 && (boundflag1 & SCULPTVERT_ALL_BOUNDARY) != (boundflag2 & SCULPTVERT_ALL_BOUNDARY)) { bm_logstack_pop(); @@ -779,19 +850,8 @@ BMVert *pbvh_bmesh_collapse_edge( validate_vert(pbvh, v_conn, CHECK_VERT_FACES | CHECK_VERT_NODE_ASSIGNED); BMEdge *e2; - if (!uvs_snapped) { - float co[3]; - - copy_v3_v3(co, v_conn->co); - copy_v3_v3(v_conn->co, co); - } - else { - float co[3]; - - add_v3_v3v3(co, v_del->co, v_conn->co); - mul_v3_fl(co, 0.5f); - - copy_v3_v3(v_conn->co, co); + if (uvs_snapped) { + interp_v3_v3v3(v_conn->co, v_del->co, v_conn->co, 0.5f); } /* Full non-manifold collapse. */ @@ -812,6 +872,16 @@ BMVert *pbvh_bmesh_collapse_edge( vert_ring_maxdepth); #endif + if (BM_elem_is_free((BMElem *)v_conn, BM_VERT)) { + printf("v_conn was freed\n"); + return nullptr; + } + + validate_vert(pbvh, v_conn, CHECK_VERT_FACES | CHECK_VERT_NODE_ASSIGNED); + + pbvh_boundary_update_bmesh(pbvh, v_conn); + dyntopo_add_flag(pbvh, v_conn, mupdateflag); + if (!v_conn->e) { printf("%s: pbvh error, v_conn->e was null\n", __func__); return v_conn; @@ -822,43 +892,53 @@ BMVert *pbvh_bmesh_collapse_edge( do { validate_edge(pbvh, e2); } while ((e2 = BM_DISK_EDGE_NEXT(e2, v_conn)) != v_conn->e); - } - validate_vert(pbvh, v_conn, CHECK_VERT_FACES | CHECK_VERT_NODE_ASSIGNED); - - /* Flag boundaries for update. */ - e2 = v_conn->e; - do { - BMLoop *l = e2->l; - - if (!l) { - continue; - } + /* Flag boundaries for update. */ + e2 = v_conn->e; do { - BMLoop *l2 = l->f->l_first; + BMLoop *l = e2->l; + + if (!l) { + BMVert *v2 = BM_edge_other_vert(e2, v_conn); + pbvh_boundary_update_bmesh(pbvh, v2); + dyntopo_add_flag(pbvh, v2, mupdateflag); + continue; + } + do { - pbvh_boundary_update_bmesh(pbvh, l2->v); - dyntopo_add_flag(pbvh, l2->v, mupdateflag); - } while ((l2 = l2->next) != l->f->l_first); - } while ((l = l->radial_next) != e2->l); - } while ((e2 = BM_DISK_EDGE_NEXT(e2, v_conn)) != v_conn->e); + BMLoop *l2 = l->f->l_first; + do { + pbvh_boundary_update_bmesh(pbvh, l2->v); + dyntopo_add_flag(pbvh, l2->v, mupdateflag); + } while ((l2 = l2->next) != l->f->l_first); + } while ((l = l->radial_next) != e2->l); + } while ((e2 = BM_DISK_EDGE_NEXT(e2, v_conn)) != v_conn->e); + } pbvh_bmesh_check_nodes(pbvh); - if (!v_conn) { - bm_logstack_pop(); - return nullptr; - } - - if (0) { // XXX v_conn->e && !v_conn->e->l) { - BM_log_edge_removed(pbvh->header.bm, pbvh->bm_log, v_conn->e); - if (BM_idmap_get_id(pbvh->bm_idmap, reinterpret_cast(v_conn->e)) != BM_ID_NONE) { - BM_idmap_release(pbvh->bm_idmap, reinterpret_cast(v_conn->e), true); +#if 0 + /* Destroy wire edges */ + Vector es; + e2 = v_conn->e; + do { + if (!e2->l) { + es.append(e2); } - BM_edge_kill(pbvh->header.bm, v_conn->e); + } while ((e2 = BM_DISK_EDGE_NEXT(e2, v_conn)) != v_conn->e); + + for (BMEdge *e2 : es) { + BMVert *v2 = BM_edge_other_vert(e2, v_conn); + + BM_log_edge_removed(pbvh->header.bm, pbvh->bm_log, e2); + BM_idmap_release(pbvh->bm_idmap, reinterpret_cast(e2), true); + BM_edge_kill(pbvh->header.bm, e2); + + dyntopo_add_flag(pbvh, v2, SCULPTFLAG_NEED_VALENCE | SCULPTFLAG_NEED_TRIANGULATE); + BKE_sculpt_boundary_flag_update(eq_ctx->ss, {reinterpret_cast(v2)}); } - if (0) { // XXX !v_conn->e) { + if (!v_conn->e) { /* Delete isolated vertex. */ if (BM_ELEM_CD_GET_INT(v_conn, pbvh->cd_vert_node_offset) != DYNTOPO_NODE_NONE) { blender::bke::dyntopo::pbvh_bmesh_vert_remove(pbvh, v_conn); @@ -871,9 +951,7 @@ BMVert *pbvh_bmesh_collapse_edge( bm_logstack_pop(); return nullptr; } - - pbvh_boundary_update_bmesh(pbvh, v_conn); - dyntopo_add_flag(pbvh, v_conn, mupdateflag); +#endif if (BM_ELEM_CD_GET_INT(v_conn, pbvh->cd_vert_node_offset) == DYNTOPO_NODE_NONE) { printf("%s: error: failed to remove vert from pbvh? v_conn->e: %p v_conn->e->l\n", @@ -882,8 +960,14 @@ BMVert *pbvh_bmesh_collapse_edge( v_conn->e ? v_conn->e->l : nullptr); } + validate_vert(pbvh, v_conn, CHECK_VERT_FACES | CHECK_VERT_NODE_ASSIGNED); + if (v_conn) { check_for_fins(pbvh, v_conn); + + if (BM_elem_is_free((BMElem *)v_conn, BM_VERT)) { + v_conn = nullptr; + } } validate_vert(pbvh, v_conn, CHECK_VERT_FACES | CHECK_VERT_NODE_ASSIGNED); diff --git a/source/blender/blenkernel/intern/dyntopo_intern.hh b/source/blender/blenkernel/intern/dyntopo_intern.hh index 63033f9b846..4980f1383ca 100644 --- a/source/blender/blenkernel/intern/dyntopo_intern.hh +++ b/source/blender/blenkernel/intern/dyntopo_intern.hh @@ -136,7 +136,6 @@ inline bool bm_elem_is_free(BMElem *elem, int htype) */ #define DYNTOPO_SAFE_SMOOTH_FAC 0.05f - #ifdef USE_EDGEQUEUE_EVEN_SUBDIV # include "BKE_global.h" #endif @@ -383,6 +382,9 @@ BMFace *pbvh_bmesh_face_create(PBVH *pbvh, * to work in RelWithDebInfo builds. */ //#define CHECKMESH +namespace blender::bmesh { +int bmesh_elem_check_all(void *elem, char htype); +} enum eValidateVertFlags { CHECK_VERT_NONE = 0, @@ -428,7 +430,6 @@ inline bool check_face_is_manifold(PBVH *, BMFace *) return true; } #else -extern "C" int bmesh_elem_check_all(void *element, char htype); # include # include @@ -493,7 +494,7 @@ static const char *get_type_name(char htype) } } // namespace blender::bke::dyntopo::debug -extern "C" char *bm_get_err_str(int err); +extern "C" const char *bm_get_error_str(int err); template ATTR_NO_OPT static bool validate_elem(PBVH *pbvh, T *elem) { @@ -513,13 +514,13 @@ template ATTR_NO_OPT static bool validate_elem(PBVH *pbvh, return false; } - int ret = bmesh_elem_check_all(static_cast(elem), get_type_htype()); + int ret = blender::bmesh::bmesh_elem_check_all(static_cast(elem), get_type_htype()); if (ret) { blender::bke::dyntopo::debug::debug_printf("%s (%p) failed integrity checks with code %s\n", get_type_name(), elem, - bm_get_err_str(ret)); + bm_get_error_str(ret)); return false; } @@ -536,7 +537,8 @@ ATTR_NO_OPT static bool check_face_is_manifold(PBVH *pbvh, BMFace *f) BMLoop *l2 = l; do { if (count++ > 10) { - blender::bke::dyntopo::debug::debug_printf("Face %p has highly non-manifold edge %p\n", f, l->e); + blender::bke::dyntopo::debug::debug_printf( + "Face %p has highly non-manifold edge %p\n", f, l->e); return false; } } while ((l2 = l2->radial_next) != l); @@ -560,7 +562,8 @@ ATTR_NO_OPT static bool validate_face(PBVH *pbvh, BMFace *f, eValidateFaceFlags int ni = BM_ELEM_CD_GET_INT(f, pbvh->cd_face_node_offset); if (ni < 0 || ni >= pbvh->totnode) { if (ni != DYNTOPO_NODE_NONE || flags & CHECK_FACE_NODE_ASSIGNED) { - //blender::bke::dyntopo::debug::debug_printf("face %p has corrupted node index %d\n", f, ni); + // blender::bke::dyntopo::debug::debug_printf("face %p has corrupted node index %d\n", f, + // ni); return false; } else { @@ -570,7 +573,7 @@ ATTR_NO_OPT static bool validate_face(PBVH *pbvh, BMFace *f, eValidateFaceFlags PBVHNode *node = &pbvh->nodes[ni]; if (!(node->flag & PBVH_Leaf)) { - //printf("face %p has corrupted node index.", f); + // printf("face %p has corrupted node index.", f); return false; } @@ -619,7 +622,8 @@ ATTR_NO_OPT static bool validate_vert(PBVH *pbvh, BMVert *v, eValidateVertFlags if (ni < 0 || ni >= pbvh->totnode) { if (ni != DYNTOPO_NODE_NONE || flags & CHECK_VERT_NODE_ASSIGNED) { - //blender::bke::dyntopo::debug::debug_printf("vertex %p has corrupted node index %d\n", v, ni); + // blender::bke::dyntopo::debug::debug_printf("vertex %p has corrupted node index %d\n", v, + // ni); return false; } else { @@ -629,7 +633,7 @@ ATTR_NO_OPT static bool validate_vert(PBVH *pbvh, BMVert *v, eValidateVertFlags PBVHNode *node = &pbvh->nodes[ni]; if (!(node->flag & PBVH_Leaf)) { - //printf("Vertex %p has corrupted node index.", v); + // printf("Vertex %p has corrupted node index.", v); return false; } diff --git a/source/blender/blenkernel/intern/paint.cc b/source/blender/blenkernel/intern/paint.cc index b68c2a02a3a..c21251c8fab 100644 --- a/source/blender/blenkernel/intern/paint.cc +++ b/source/blender/blenkernel/intern/paint.cc @@ -1740,6 +1740,8 @@ static void sculpt_check_face_areas(Object *ob, PBVH *pbvh) if (!ss->attrs.face_areas) { SculptAttributeParams params = {0}; + + params.nointerp = true; ss->attrs.face_areas = sculpt_attribute_ensure_ex(ob, ATTR_DOMAIN_FACE, CD_PROP_FLOAT2, @@ -3580,6 +3582,9 @@ static SculptAttribute *sculpt_attribute_ensure_ex(Object *ob, SculptAttribute *attr = BKE_sculpt_attribute_get(ob, domain, proptype, name); if (attr) { + attr->params.nocopy = params->nocopy; + attr->params.nointerp = params->nointerp; + sculpt_attr_update(ob, attr); /* Since "stroke_only" is not a CustomData flag we have @@ -4021,6 +4026,7 @@ void BKE_sculpt_ensure_origfset(struct Object *ob) void BKE_sculpt_ensure_sculpt_layers(struct Object *ob) { SculptAttributeParams params = {}; + params.nointerp = params.nocopy = true; if (!ob->sculpt->attrs.flags) { ob->sculpt->attrs.flags = BKE_sculpt_attribute_ensure( diff --git a/source/blender/blenkernel/intern/pbvh.cc b/source/blender/blenkernel/intern/pbvh.cc index 2f3bd55a111..9532b90cff4 100644 --- a/source/blender/blenkernel/intern/pbvh.cc +++ b/source/blender/blenkernel/intern/pbvh.cc @@ -4074,6 +4074,11 @@ void BKE_pbvh_get_vert_face_areas(PBVH *pbvh, PBVHVertRef vertex, float *r_areas do { float w = 0.0f; + BMVert *v2 = BM_edge_other_vert(e, v); + + if (*BM_ELEM_CD_PTR(v2, pbvh->cd_flag) & SCULPTFLAG_VERT_FSET_HIDDEN) { + continue; + } if (!e->l) { w = 0.0f; @@ -4087,15 +4092,17 @@ void BKE_pbvh_get_vert_face_areas(PBVH *pbvh, PBVHVertRef vertex, float *r_areas } if (j >= valence) { - printf( - "%s: error, corrupt edge cycle, valence was %d expected %d\n", __func__, j, valence); + printf("%s: error, corrupt edge cycle, valence was %d expected %d\n", + __func__, + j + 1, + valence); + uint8_t *flags = BM_ELEM_CD_PTR(v, pbvh->cd_flag); + *flags |= SCULPTFLAG_NEED_VALENCE; break; } r_areas[j++] = w; - - e = v == e->v1 ? e->v1_disk_link.next : e->v2_disk_link.next; - } while (e != v->e); + } while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e); for (; j < valence; j++) { r_areas[j] = 1.0f; diff --git a/source/blender/blenkernel/intern/pbvh_bmesh.cc b/source/blender/blenkernel/intern/pbvh_bmesh.cc index e54b3808c88..98c95a5e30d 100644 --- a/source/blender/blenkernel/intern/pbvh_bmesh.cc +++ b/source/blender/blenkernel/intern/pbvh_bmesh.cc @@ -1056,7 +1056,7 @@ static bool point_in_node(const PBVHNode *node, const float co[3]) co[1] <= node->vb.bmax[1] && co[2] >= node->vb.bmin[2] && co[2] <= node->vb.bmax[2]; } -ATTR_NO_OPT void bke_pbvh_insert_face_finalize(PBVH *pbvh, BMFace *f, const int ni) +void bke_pbvh_insert_face_finalize(PBVH *pbvh, BMFace *f, const int ni) { PBVHNode *node = pbvh->nodes + ni; BM_ELEM_CD_SET_INT(f, pbvh->cd_face_node_offset, ni); @@ -2041,17 +2041,6 @@ void BKE_pbvh_update_vert_boundary(int cd_faceset_offset, int seamcount = 0; int quadcount = 0; -#ifdef MV_COLOR_BOUNDARY - int last_key = -1; -#endif - -#if 0 - struct FaceSetRef { - int fset; - BMVert *v2; - BMEdge *e; - } *fsets = nullptr; -#endif Vector fsets; float(*lastuv)[2] = do_uvs ? (float(*)[2])BLI_array_alloca(lastuv, totuv) : nullptr; @@ -2156,6 +2145,7 @@ void BKE_pbvh_update_vert_boundary(int cd_faceset_offset, bool corner = len_squared_v2v2(lastuv[i], lastuv2[i]) > uv_snap_limit && len_squared_v2v2(lastuv[i], luv) > uv_snap_limit && len_squared_v2v2(lastuv2[i], luv) > uv_snap_limit; + corner = blender::bke::sculpt::loop_is_corner(l, cd_uvs[i]); if (!same) { boundflag |= SCULPT_BOUNDARY_UV; @@ -2163,6 +2153,7 @@ void BKE_pbvh_update_vert_boundary(int cd_faceset_offset, if (corner) { boundflag |= SCULPT_CORNER_UV; + l->v->head.hflag |= BM_ELEM_SELECT; } if (!same) { @@ -2237,7 +2228,7 @@ void BKE_pbvh_update_vert_boundary(int cd_faceset_offset, } } - if (!e->l || e->l->radial_next == e->l) { + if (e->l && e->l->radial_next == e->l) { boundflag |= SCULPT_BOUNDARY_MESH; } @@ -2300,8 +2291,8 @@ void BKE_pbvh_recalc_bmesh_boundary(PBVH *pbvh) pbvh->cd_valence, v, &pbvh->header.bm->ldata, - pbvh->flags & PBVH_IGNORE_UVS ? 0 : pbvh->totuv, - pbvh->flags & PBVH_IGNORE_UVS, + pbvh->totuv, + true, pbvh->sharp_angle_limit); } } @@ -4027,3 +4018,281 @@ void update_sharp_boundary_bmesh(BMVert *v, int cd_boundary_flag, const float sh BM_ELEM_CD_SET_INT(v, cd_boundary_flag, flag); } } // namespace blender::bke::pbvh + +namespace blender::bke::sculpt { + +bool loop_is_corner(BMLoop *l, int cd_offset) +{ + BMVert *v = l->v; + + float2 value = *BM_ELEM_CD_PTR(l, cd_offset); + float limit = 0.1; + + Vector ls; + Vector keys; + + BMEdge *e = v->e; + do { + BMLoop *l2 = e->l; + + if (!l2) { + continue; + } + + do { + BMLoop *l3 = l2->v == v ? l2 : l2->next; + if (!ls.contains(l3)) { + ls.append(l3); + } + } while ((l2 = l2->radial_next) != e->l); + } while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e); + + int count = 0; + for (BMLoop *l2 : ls) { + float2 value2 = *BM_ELEM_CD_PTR(l2, cd_offset); + float2 dv = value2 - value; + + double f = dv[0] * dv[0] + dv[1] * dv[1]; + int key = int(f * 200.0); + if (!keys.contains(key)) { + keys.append(key); + } + } + + bool ret = keys.size() > 2; + if (ret) { + l->v->head.hflag |= BM_ELEM_SELECT; + } + + return ret; +} + +#if 0 +/* Angle test */ +ATTR_NO_OPT bool loop_is_corner(BMLoop *l, int cd_offset) +{ + BMVert *v = l->v; + BMEdge *e = v->e; + + float2 value = *BM_ELEM_CD_PTR(l, cd_offset); + float limit = 0.1; + + BMLoop *outer1 = nullptr, *outer2 = nullptr; + do { + BMLoop *l2 = e->l; + if (!l2) { + continue; + } + + do { + BMLoop *l3 = l2->v == v ? l2 : l2->next; + float2 value3 = *BM_ELEM_CD_PTR(l3, cd_offset); + + if (!prop_eq(value, value3, limit)) { + continue; + } + + bool outer = true; + + BMLoop *l4 = l2->radial_next; + while (l4 != l2) { + BMLoop *l5 = l4->v == v ? l4 : l4->next; + float2 value5 = *BM_ELEM_CD_PTR(l5, cd_offset); + + if (prop_eq(value, value5, limit)) { + outer = false; + break; + } + l4 = l4->radial_next; + } + + if (outer) { + BMLoop *l3 = l2->v == v ? l2->next : l2; + + if (!outer1) { + outer1 = l3; + } + else if (!outer2 && l3 != outer2) { + outer2 = l3; + } + + break; + } + } while ((l2 = l2->radial_next) != e->l); + } while ((e = BM_DISK_EDGE_NEXT(e, v)) != l->e); + + if (!outer1 || !outer2) { + return outer1 != nullptr; + } + + float2 t1 = *BM_ELEM_CD_PTR(outer1, cd_offset) - value; + float2 t2 = *BM_ELEM_CD_PTR(outer2, cd_offset) - value; + + normalize_v2(t1); + normalize_v2(t2); + float angle_limit = 110.0f / 180.0f * M_PI; + float angle = saacos(dot_v2v2(t1, t2)); + + if (angle < angle_limit) { + l->v->head.hflag |= BM_ELEM_SELECT; + } + + return angle < angle_limit; +} +#endif + +template +static void corner_interp(CustomDataLayer *layer, + BMLoop *l, + Span loops, + Span ws, + int cd_offset, + float factor) +{ + float *ws2 = (float *)BLI_array_alloca(ws2, loops.size() + 1); + T sum = {}; + float totsum = 0.0f; + + T value = *BM_ELEM_CD_PTR(l, cd_offset); + + float limit = 0.0001; + + if (layer->type == CD_PROP_FLOAT2) { + limit = 0.01; + } + else { + /* Do not restrict to islands for non-UVs */ + limit = FLT_MAX; + } + + for (int i : loops.index_range()) { + BMLoop *l2 = loops[i]; + BMLoop *l3 = l2->next->v == l->v ? l2->next : l2->prev; + T value3 = *BM_ELEM_CD_PTR(l3, cd_offset); + + if (prop_eq(value, value3, 0.01)) { + T value2 = *BM_ELEM_CD_PTR(l2, cd_offset); + sum += value2 * ws[i]; + totsum += ws[i]; + } + } + + if (totsum == 0.0f) { + return; + } + + sum /= totsum; + + *BM_ELEM_CD_PTR(l, cd_offset) = value + (sum - value) * factor; +} + +/* Interpolates loops surrounding a vertex, splitting any UV map by + * island as appropriate and enforcing proper boundary conditions. + */ +void interp_face_corners( + PBVH *pbvh, PBVHVertRef vertex, Span loops, Span ws, float factor) +{ + if (BKE_pbvh_type(pbvh) != PBVH_BMESH) { + return; /* Only for PBVH_BMESH. */ + } + + eCustomDataMask mask = CD_MASK_PROP_FLOAT | CD_MASK_PROP_FLOAT2 | CD_MASK_PROP_FLOAT3 | + CD_MASK_PROP_COLOR | CD_MASK_PROP_BYTE_COLOR; + + BMesh *bm = pbvh->header.bm; + BMVert *v = reinterpret_cast(vertex.i); + BMEdge *e = v->e; + BMLoop *l = e->l; + CustomData *cdata = &bm->ldata; + + Vector ls; + + /* Tag loops around vertex. */ + do { + l = e->l; + + if (!l) { + continue; + } + + do { + BMLoop *l2 = l->v == v ? l : l->next; + BM_elem_flag_enable(l2, BM_ELEM_TAG); + } while ((l = l->radial_next) != e->l); + } while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e); + + /* Build loop list. */ + do { + l = e->l; + + if (!l) { + continue; + } + + do { + BMLoop *l2 = l->v == v ? l : l->next; + if (BM_elem_flag_test(l2, BM_ELEM_TAG)) { + BM_elem_flag_disable(l2, BM_ELEM_TAG); + ls.append(l2); + } + } while ((l = l->radial_next) != e->l); + } while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e); + + Vector layers; + for (int layer_i : IndexRange(cdata->totlayer)) { + CustomDataLayer *layer = cdata->layers + layer_i; + eCustomDataType type = eCustomDataType(layer->type); + const int cd_offset = layer->offset; + + if ((layer->flag & CD_FLAG_ELEM_NOINTERP) || !(CD_TYPE_AS_MASK(layer->type) & mask)) { + continue; + } + if (layer->flag & CD_FLAG_TEMPORARY) { + continue; + } + + layers.append(layer); + } + + /* Interpolate. */ + if (loops.size() > 1) { + VertLoopSnapper corner_snap = {Span(ls), Span(layers)}; + + for (CustomDataLayer *layer : layers) { + Vector corners; + + if (layer->type == CD_PROP_FLOAT2) { + for (BMLoop *l : ls) { + corners.append(loop_is_corner(l, layer->offset)); + } + } + + for (int i : ls.index_range()) { + BMLoop *l = ls[i]; + + if (layer->type == CD_PROP_FLOAT2 && corners[i]) { + continue; + } + + switch (layer->type) { + case CD_PROP_FLOAT: + corner_interp(layer, l, loops, ws, layer->offset, factor); + break; + case CD_PROP_FLOAT2: + corner_interp(layer, l, loops, ws, layer->offset, factor); + break; + case CD_PROP_FLOAT3: + corner_interp(layer, l, loops, ws, layer->offset, factor); + break; + case CD_PROP_COLOR: + corner_interp(layer, l, loops, ws, layer->offset, factor); + break; + } + } + } + + /* Snap. */ + corner_snap.snap(); + } +} +} // namespace blender::bke::sculpt diff --git a/source/blender/blenkernel/intern/pbvh_intern.hh b/source/blender/blenkernel/intern/pbvh_intern.hh index 8ec049dbc64..cb5f2444cee 100644 --- a/source/blender/blenkernel/intern/pbvh_intern.hh +++ b/source/blender/blenkernel/intern/pbvh_intern.hh @@ -436,8 +436,8 @@ BLI_INLINE bool pbvh_check_vert_boundary(PBVH *pbvh, struct BMVert *v) pbvh->cd_valence, v, &pbvh->header.bm->ldata, - pbvh->flags & PBVH_IGNORE_UVS ? 0 : pbvh->totuv, - !(pbvh->flags & PBVH_IGNORE_UVS), + pbvh->totuv, + true, pbvh->sharp_angle_limit); return true; } diff --git a/source/blender/bmesh/intern/bmesh_collapse.cc b/source/blender/bmesh/intern/bmesh_collapse.cc index 98ff51ef01a..af649ca2e1f 100644 --- a/source/blender/bmesh/intern/bmesh_collapse.cc +++ b/source/blender/bmesh/intern/bmesh_collapse.cc @@ -1,11 +1,130 @@ #include "bmesh_collapse.hh" +#include "bmesh_private.h" using blender::bmesh::NullCollapseCallbacks; namespace blender::bmesh { + +#ifdef JVKE_DEBUG char *_last_local_obj = nullptr; +static ATTR_NO_OPT int bmesh_elem_check_all_intern(void *elem, char htype, int depth = 0) +{ + int ret = bmesh_elem_check(elem, htype); + + if (ret || depth > 2) { + return ret; + } + + switch (htype) { + case BM_VERT: { + BMVert *v = static_cast(elem); + + if (!v->e) { + return 0; + } + + int count1 = 0; + BMEdge *e = v->e; + + do { + BMVert *v2 = BM_edge_other_vert(e, v); + int ret2 = bmesh_elem_check_all_intern(static_cast(v2), BM_VERT, depth + 1); + if (ret2) { + return ret2; + } + + if (count1++ > 1000) { + return IS_EDGE_NULL_DISK_LINK; + } + + if (!e->l) { + continue; + } + + int count2 = 0; + BMLoop *l = e->l; + do { + int ret2 = bmesh_elem_check_all_intern(static_cast(l->f), BM_FACE, depth + 1); + if (ret2) { + return ret2; + } + + if (count2++ > 100) { + return IS_LOOP_WRONG_RADIAL_LENGTH; + } + } while ((l = l->radial_next) != e->l); + } while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e); + break; + } + case BM_EDGE: { + BMEdge *e = static_cast(elem); + BMLoop *l = e->l; + + if (!l) { + return 0; + } + + int count = 0; + + do { + if (count++ > 100) { + return IS_LOOP_WRONG_RADIAL_LENGTH; + } + } while ((l = l->radial_next) != e->l); + break; + } + case BM_LOOP: { + BMLoop *l = static_cast(elem); + BMLoop *l2 = l; + int count = 0; + + if (BM_elem_is_free((BMElem *)l->f, BM_FACE)) { + return IS_LOOP_WRONG_FACE_TYPE; + } + if (BM_elem_is_free((BMElem *)l->e, BM_EDGE)) { + return IS_LOOP_WRONG_EDGE_TYPE; + } + if (BM_elem_is_free((BMElem *)l->v, BM_VERT)) { + return IS_LOOP_WRONG_VERT_TYPE; + } + + do { + if (count++ > 100) { + return IS_LOOP_WRONG_RADIAL_LENGTH; + } + } while ((l2 = l2->radial_next) != l); + break; + } + case BM_FACE: { + BMFace *f = static_cast(elem); + BMLoop *l = f->l_first; + int count = 0; + + do { + if (count++ > 100000) { + return IS_FACE_WRONG_LENGTH; + } + + int ret2 = bmesh_elem_check_all_intern(static_cast(l), BM_LOOP, depth + 1); + if (ret2) { + return ret2; + } + } while ((l = l->next) != f->l_first); + + break; + } + } + + return 0; } +int bmesh_elem_check_all(void *elem, char htype) +{ + return bmesh_elem_check_all_intern(elem, htype); +} +#endif +} // namespace blender::bmesh + extern "C" BMVert *bmesh_kernel_join_vert_kill_edge( BMesh *bm, BMEdge *e, BMVert *v_kill, const bool do_del, const bool combine_flags) { diff --git a/source/blender/bmesh/intern/bmesh_collapse.hh b/source/blender/bmesh/intern/bmesh_collapse.hh index 8e54a6085ea..50b5957f1c1 100644 --- a/source/blender/bmesh/intern/bmesh_collapse.hh +++ b/source/blender/bmesh/intern/bmesh_collapse.hh @@ -356,12 +356,14 @@ static void trigger_jvke_error(int err, char *obj_text) printf("========= ERROR %s============\n\n%s\n\n", bm_get_error_str(err), obj_text); } +int bmesh_elem_check_all(void *elem, char htype); + extern char *_last_local_obj; # define JVKE_CHECK_ELEMENT(elem) \ { \ int err = 0; \ - if ((err = bmesh_elem_check(elem, (elem)->head.htype))) { \ + if ((err = bmesh_elem_check_all(elem, (elem)->head.htype))) { \ trigger_jvke_error(err, saved_obj); \ } \ } @@ -406,6 +408,9 @@ static bool cleanup_vert(BMesh *bm, BMVert *v, Callbacks &callbacks) f = BM_face_create_quad_tri(bm, v1, v2, v3, nullptr, f_example, BM_CREATE_NOP); BMLoop *l = f->l_first; + callbacks.on_vert_kill(v); + BM_vert_kill(bm, v); + /* Ensure correct winding. */ do { if (l->radial_next != l && l->radial_next->v == l->v) { @@ -414,10 +419,19 @@ static bool cleanup_vert(BMesh *bm, BMVert *v, Callbacks &callbacks) } } while ((l = l->next) != f->l_first); - callbacks.on_face_create(f); - callbacks.on_vert_kill(v); + l = f->l_first; + do { + if (l != l->radial_next) { + BMLoop *l2 = l->radial_next; + if (l2->v != l->v) { + l2 = l2->next; + } - BM_vert_kill(bm, v); + CustomData_bmesh_copy_data(&bm->ldata, &bm->ldata, l2->head.data, &l->head.data); + } + } while ((l = l->next) != f->l_first); + + callbacks.on_face_create(f); return true; } @@ -443,11 +457,27 @@ static void bmesh_kernel_check_val3_vert(BMesh *bm, BMEdge *e, Callbacks &callba do { BMLoop *l2 = l->prev; - if (l2 == l2->radial_next) { + if (l2 == l2->radial_next || !l2->v->e) { continue; } - if (BM_vert_edge_count(l2->v) == 3) { + bool bad = false; + int count = 0; + + BMEdge *e2 = l2->v->e; + do { + if (!e2->l || e2->l == e2->l->radial_next || e2->l->radial_next->radial_next != e2->l) { + bad = true; + break; + } + + bad = bad || e2->l->f->len != 3 || e2->l->radial_next->f->len != 3; + count++; + } while ((e2 = BM_DISK_EDGE_NEXT(e2, l2->v)) != l2->v->e); + + bad = bad || count != 3; + + if (!bad) { if (cleanup_vert(bm, l2->v, callbacks)) { stop = false; break; @@ -479,12 +509,12 @@ static void bmesh_kernel_check_val3_vert(BMesh *bm, BMEdge *e, Callbacks &callba template BMVert *join_vert_kill_edge(BMesh *bm, BMEdge *e, - BMVert *v_kill, + BMVert *v_del, const bool do_del, const bool combine_flags, Callbacks &callbacks) { - BMVert *v_conn = BM_edge_other_vert(e, v_kill); + BMVert *v_conn = BM_edge_other_vert(e, v_del); #ifdef JVKE_DEBUG char buf[LOCAL_OBJ_SIZE]; @@ -501,112 +531,41 @@ BMVert *join_vert_kill_edge(BMesh *bm, BLI_strncpy(_last_local_obj, saved_obj, strlen(saved_obj) + 1); #endif - /* Free any surrounding valence-3 rings disconnected from the edge. */ + /* Destroy any valence-3 verts that might turn into non-manifold "fins." */ bmesh_kernel_check_val3_vert(bm, e, callbacks); - Vector fs; - Vector deles; + Set es; + Set fs; - BMVert *v_del = BM_edge_other_vert(e, v_conn); - const int tag = _FLAG_WALK_ALT; /* Using bmhead.api_flag. */ const int dup_tag = _FLAG_OVERLAP; - const int final_tag = _FLAG_JF; - JVKE_CHECK_ELEMENT(v_conn); - JVKE_CHECK_ELEMENT(v_del); - - /* Clear tags. */ for (int i = 0; i < 2; i++) { BMVert *v = i ? v_del : v_conn; - if (!v->e) { - continue; - } - - BMEdge *e2 = v->e; + BMEdge *e = v->e; do { - if (!e2->l) { - continue; - } - - BMLoop *l = e2->l; - do { - BM_ELEM_API_FLAG_DISABLE(l->f, tag); - - BMLoop *l2 = l; - do { - BMLoop *l3 = l2; - do { - BM_ELEM_API_FLAG_DISABLE(l3->f, tag); - } while ((l3 = l3->radial_next) != l2); - } while ((l2 = l2->next) != l); - -#ifdef JVKE_DEBUG - if (l->radial_next == l) { - have_boundary = true; - } -#endif - - BM_ELEM_API_FLAG_DISABLE(l->f, dup_tag); - BM_ELEM_API_FLAG_DISABLE(l->e, dup_tag); - BM_ELEM_API_FLAG_DISABLE(l->v, dup_tag); - } while ((l = l->radial_next) != e2->l); - } while ((e2 = BM_DISK_EDGE_NEXT(e2, v)) != v->e); - } - - /* Build face list. */ - for (int i = 0; i < 2; i++) { - BMVert *v = i ? v_del : v_conn; - BMEdge *e2 = v->e; - - if (!e2 || !e2->l) { - continue; - } - - do { - BMLoop *l = e2->l; + es.add(e); + BMLoop *l = e->l; if (!l) { continue; } do { - if (!BM_ELEM_API_FLAG_TEST(l->f, tag)) { - BM_ELEM_API_FLAG_ENABLE(l->f, tag); - - BMLoop *l2 = l; - do { - BM_ELEM_API_FLAG_DISABLE(l2->e, tag); - } while ((l2 = l2->next) != l); - - if (fs.contains(l->f)) { - printf("%s: error!\n", __func__); - } - else { - fs.append(l->f); - } - } - + fs.add(l->f); BMLoop *l2 = l; do { + es.add(l2->e); BMLoop *l3 = l2; do { - if (l3->f != l->f && !BM_ELEM_API_FLAG_TEST(l3->f, tag)) { - BM_ELEM_API_FLAG_ENABLE(l3->f, tag); - if (fs.contains(l3->f)) { - printf("%s: error!\n", __func__); - } - else { - fs.append(l3->f); - } - } + fs.add(l3->f); } while ((l3 = l3->radial_next) != l2); } while ((l2 = l2->next) != l); - - } while ((l = l->radial_next) != e2->l); - } while ((e2 = BM_DISK_EDGE_NEXT(e2, v)) != v->e); + } while ((l = l->radial_next) != e->l); + } while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e); } + /* Inform callbacks we've "killed" all the faces. */ for (BMFace *f : fs) { callbacks.on_face_kill(f); } @@ -614,386 +573,151 @@ BMVert *join_vert_kill_edge(BMesh *bm, /* Unlink loops. */ for (BMFace *f : fs) { BMLoop *l = f->l_first; - do { - BMEdge *e2 = l->e; - - l->radial_next->radial_prev = l->radial_prev; - l->radial_prev->radial_next = l->radial_next; - - if (l == e2->l) { - e2->l = l->radial_next; - } - - if (l == e2->l) { - e2->l = nullptr; - } - - l->radial_next = l->radial_prev = l; - if (e2->l) { - BMLoop *l2 = e2->l; - int count = 0; - - do { - if (count++ > 10) { - printf("%s: Corrupted radial list\n", __func__); - break; - } - - if (l2 == l) { - printf("%s: Radial list still has deleted loop\n", __func__); - } - } while ((l2 = l2->next) != e2->l); - } + BMEdge *e = l->e; + bmesh_radial_loop_remove(l->e, l); + l->e = e; } while ((l = l->next) != f->l_first); } - /* Swap verts. */ - for (BMFace *f : fs) { - BMLoop *l = f->l_first, *lnext = nullptr; + /* Swap edges. */ + for (BMEdge *e : es) { + if (e->v1 != v_del && e->v2 != v_del) { + continue; + } + if (e->v1 == v_conn || e->v2 == v_conn) { + if (e->l) { + printf("%s: ErROR!\n", __func__); + } + + callbacks.on_edge_kill(e); + BM_edge_kill(bm, e); + continue; + } + + BMVert *otherv = e->v1 == v_del ? e->v2 : e->v1; + + BMEdge *exist = BM_edge_exists(otherv, v_conn); + + if (exist) { + if (e->l) { + printf("%s: ERROR!\n", __func__); + } + + if (combine_flags) { + exist->head.hflag |= e->head.hflag; + } + + callbacks.on_edge_kill(e); + BM_edge_kill(bm, e); + } + else { + callbacks.on_edge_kill(e); + bmesh_disk_vert_replace(e, v_conn, v_del); + callbacks.on_edge_create(e); + } + } + + auto remove_loop = [&bm](BMFace *f, BMLoop *l) { + l->next->prev = l->prev; + l->prev->next = l->next; + if (l == f->l_first) { + f->l_first = l->next; + } + + f->len--; + bm_kill_only_loop(bm, l); + }; + + /* Swap loops */ + for (BMFace *f : fs) { + BMLoop *l = f->l_first; + BMLoop *lnext; + bool found = false; + + /* Swap v_del and remove duplicate v_conn's. */ do { lnext = l->next; if (l->v == v_del) { l->v = v_conn; } - - BM_ELEM_API_FLAG_DISABLE(l->v, tag); - - for (int step = 0; step < 2; step++) { - BMVert *v_edge = step ? l->e->v2 : l->e->v1; - BMVert *v_other = BM_edge_other_vert(l->e, v_edge); - - if (v_edge != v_del) { - continue; - } - - if (v_other == v_conn) { - /* Flag for later deletion. */ - if (!BM_ELEM_API_FLAG_TEST(l->e, tag)) { - deles.append(l->e); - } - - BM_ELEM_API_FLAG_ENABLE(l->e, tag); + if (l->v == v_conn) { + if (found) { + remove_loop(f, l); } else { - BMEdge *e3; - - if ((e3 = BM_edge_exists(v_conn, v_other))) { - if (combine_flags) { - bool remove_smooth = !BM_elem_flag_test(l->e, BM_ELEM_SMOOTH); - remove_smooth = remove_smooth || !BM_elem_flag_test(e3, BM_ELEM_SMOOTH); - - e3->head.hflag |= l->e->head.hflag; - - if (remove_smooth) { - BM_elem_flag_disable(e3, BM_ELEM_SMOOTH); - } - } - - /* Flag for later deletion. */ - if (!BM_ELEM_API_FLAG_TEST(l->e, tag)) { - deles.append(l->e); - } - - BM_ELEM_API_FLAG_ENABLE(l->e, tag); - - l->e = e3; - } - else { - callbacks.on_edge_kill(l->e); - bmesh_disk_vert_replace(l->e, v_conn, v_del); - callbacks.on_edge_create(l->e); - } + found = true; } } } while ((l = lnext) != f->l_first); + + /* Remove any remaining duplicate verts. */ + do { + lnext = l->next; + if (l->v == l->next->v) { + remove_loop(f, l); + } + } while ((l = lnext) != f->l_first); } - for (BMEdge *e2 : deles) { - if (e2->l != nullptr) { - printf("%s: e2->l was not null!\n", __func__); - - while (e2->l) { - BMFace *f = e2->l->f; - - if (fs.contains(f)) { - printf("double entry for f! f->len: %d\n", f->len); - } - - if (!fs.contains(f)) { - fs.append(f); - callbacks.on_face_kill(f); - - /* Unlink face. */ - BMLoop *l = f->l_first; - do { - if (l->v == v_del) { - /* Duplicate vertex; it will be filtered out later. */ - l->v = l->next->v; - } - - if (l->e != e2) { - bmesh_radial_loop_remove(l->e, l); - } - } while ((l = l->next) != f->l_first); - } - - bmesh_radial_loop_remove(e2, e2->l); - } - } - - callbacks.on_edge_kill(e2); - BM_edge_kill(bm, e2); - } - - for (int i : fs.index_range()) { - BMFace *f = fs[i]; - BMLoop *l, *lnext; - - /* Validate. */ - l = f->l_first; - do { - lnext = l == l->next ? nullptr : l->next; - - if (l->v == l->next->v) { - l->prev->next = l->next; - l->next->prev = l->prev; - - if (l == l->f->l_first) { - l->f->l_first = l->next; - } - - l->f->len--; - - if (l == l->f->l_first) { - l->f->l_first = nullptr; - } + Vector finalfs; + /* Relink faces. */ + for (BMFace *f : fs) { + if (f->len < 3) { + BMLoop *l = f->l_first; + BMLoop *lnext; + do { + lnext = l->next; bm_kill_only_loop(bm, l); - } - } while (lnext && (l = lnext) != f->l_first); - - if (f->len <= 2) { - /* Kill face. */ - while (f->l_first) { - BMLoop *l2 = f->l_first; - - l2->prev->next = l2->next; - l2->next->prev = l2->prev; - f->l_first = l2->next; - - bm_kill_only_loop(bm, l2); - - if (f->l_first == l2) { - f->l_first = nullptr; - } - } + } while ((l = lnext) != f->l_first); bm_kill_only_face(bm, f); - fs[i] = nullptr; - } - } - - bm->elem_index_dirty |= BM_VERT | BM_EDGE | BM_FACE; - bm->elem_table_dirty |= BM_VERT | BM_EDGE | BM_FACE; - - /* Relink. */ - for (BMFace *f : fs) { - if (!f) { continue; } - BM_ELEM_API_FLAG_ENABLE(f, final_tag); - BMLoop *l = f->l_first; do { - l->e = BM_edge_exists(l->v, l->next->v); - - if (!l->e) { - printf("%s: missing edge! %p %p\n", __func__, l->v, l->next->v); - - l->e = BM_edge_create(bm, l->v, l->next->v, nullptr, BM_CREATE_NOP); - callbacks.on_edge_create(l->e); + BMEdge *exist_e = BM_edge_exists(l->v, l->next->v); + if (!exist_e) { + exist_e = BM_edge_create(bm, l->v, l->next->v, nullptr, BM_CREATE_NOP); + callbacks.on_edge_create(exist_e); } + l->e = exist_e; bmesh_radial_loop_append(l->e, l); - JVKE_CHECK_ELEMENT(l->e); - BM_ELEM_API_FLAG_DISABLE(l->e, dup_tag); BM_ELEM_API_FLAG_DISABLE(l->v, dup_tag); + BM_ELEM_API_FLAG_DISABLE(l->e, dup_tag); BM_ELEM_API_FLAG_DISABLE(l->f, dup_tag); } while ((l = l->next) != f->l_first); - if (f->head.htype != BM_FACE) { - printf("%s: error!! f was freed!\n", __func__); + callbacks.on_face_create(f); + finalfs.append(f); + } + + for (BMFace *f : finalfs) { + if (BM_elem_is_free((BMElem *)f, BM_FACE)) { continue; } - callbacks.on_face_create(f); + BMFace *f2; + while ((f2 = BM_face_find_double(f))) { + printf("%s: removing duplicate face.\n", __func__); + callbacks.on_face_kill(f2); + BM_face_kill(bm, f2); + } } JVKE_CHECK_ELEMENT(v_conn); -#ifdef JVKE_DEBUG - for (int step = 0; step < 2; step++) { - BMVert *v = step ? v_conn : v_del; - BMEdge *e1 = v->e; - - if (e1) { - do { - JVKE_CHECK_ELEMENT(e1); - - BMLoop *l = e1->l; - - if (!l) { - continue; - } - - /* boundary? */ - if (l == l->radial_next && !have_boundary) { - trigger_jvke_error(IS_LOOP_WRONG_RADIAL_LENGTH, saved_obj); - } - - if (!l) { - continue; - } - - do { - JVKE_CHECK_ELEMENT(l); - JVKE_CHECK_ELEMENT(l->v); - JVKE_CHECK_ELEMENT(l->e); - JVKE_CHECK_ELEMENT(l->f); - } while ((l = l->radial_next) != e1->l); - } while ((e1 = BM_DISK_EDGE_NEXT(e1, v)) != v->e); - } + if (do_del && !v_del->e) { + callbacks.on_vert_kill(v_del); + BM_vert_kill(bm, v_del); } -#endif - /* Use euler criteria to check for duplicate faces. */ - if (do_del && v_conn->e) { -#if 0 - int tote = 0, totv = 0, totf = 0; - - BMVert *v = v_conn; - BMEdge *e2 = v->e; - - if (!BM_ELEM_API_FLAG_TEST(v, dup_tag)) { - BM_ELEM_API_FLAG_ENABLE(v, dup_tag); - totv++; - } - - do { - BMVert *v2 = BM_edge_other_vert(e2, v); - - if (!BM_ELEM_API_FLAG_TEST(e2, dup_tag)) { - BM_ELEM_API_FLAG_ENABLE(e2, dup_tag); - tote++; - } - if (!BM_ELEM_API_FLAG_TEST(v2, dup_tag)) { - BM_ELEM_API_FLAG_ENABLE(v2, dup_tag); - totv++; - } - - if (e2->l) { - BMLoop *l_radial = e2->l; - do { - if (BM_ELEM_API_FLAG_TEST(l_radial->f, dup_tag)) { - continue; - } - - totf++; - - BM_ELEM_API_FLAG_ENABLE(l_radial->f, dup_tag); - BMLoop *l = l_radial; - - do { - if (!BM_ELEM_API_FLAG_TEST(l->v, dup_tag)) { - BM_ELEM_API_FLAG_ENABLE(l->v, dup_tag); - totv++; - } - - if (!BM_ELEM_API_FLAG_TEST(l->e, dup_tag)) { - BM_ELEM_API_FLAG_ENABLE(l->e, dup_tag); - tote++; - } - } while ((l = l->next) != l_radial); - } while ((l_radial = l_radial->radial_next) != e2->l); - } - } while ((e2 = BM_DISK_EDGE_NEXT(e2, v)) != v->e); - - int eul = totv - tote + totf; - if (eul != 1) { -#else - { - BMEdge *e2; - BMVert *v = v_conn; -#endif - e2 = v->e; - - do { - BMLoop *l = e2->l; - - if (!l) { - continue; - } - - BMLoop *l_next = l; - - do { - /* No guarantee each face has only one loop in radial - * list. - */ - l_next = l->radial_next; - - while (l_next != l && l_next->f == l->f) { - l_next = l->radial_next; - } - - BMFace *f; - - if ((f = BM_face_find_double(l->f))) { - callbacks.on_face_kill(l->f); - BM_face_kill(bm, l->f); - } - } while (e2->l && (l = l_next) != e2->l); - } while ((e2 = BM_DISK_EDGE_NEXT(e2, v)) != v->e); - } + return v_conn; } - -if (do_del) { - JVKE_CHECK_ELEMENT(v_del); - - if (v_del->e && v_del->e->l) { - printf("%s: vert is not cleared\n", __func__); - } - - if (v_del->e) { - BMIter iter; - - BMEdge *e2; - BM_ITER_ELEM (e2, &iter, v_del, BM_EDGES_OF_VERT) { - callbacks.on_edge_kill(e2); - } - - BMFace *f2; - BM_ITER_ELEM (f2, &iter, v_del, BM_FACES_OF_VERT) { - callbacks.on_face_kill(f2); - } - } - - callbacks.on_vert_kill(v_del); - BM_vert_kill(bm, v_del); -} - -#ifdef JVKE_DEBUG -bm_local_obj_free(saved_obj, buf); -#endif - -return v_conn; -} -#ifdef _OTHER_TRACES -# undef _OTHER_TRACES -#endif } // namespace blender::bmesh diff --git a/source/blender/bmesh/intern/bmesh_core.c b/source/blender/bmesh/intern/bmesh_core.c index 95de7d4efc2..3af3241b543 100644 --- a/source/blender/bmesh/intern/bmesh_core.c +++ b/source/blender/bmesh/intern/bmesh_core.c @@ -400,7 +400,7 @@ BMFace *BM_face_copy( * * \note Caller needs to handle customdata. */ -static ATTR_NO_OPT BMFace *bm_face_create__internal(BMesh *bm) +static BMFace *bm_face_create__internal(BMesh *bm) { BMFace *f; diff --git a/source/blender/bmesh/intern/bmesh_log.cc b/source/blender/bmesh/intern/bmesh_log.cc index 6a5efff2068..5582bf3f4a1 100644 --- a/source/blender/bmesh/intern/bmesh_log.cc +++ b/source/blender/bmesh/intern/bmesh_log.cc @@ -577,8 +577,8 @@ struct BMLogEntry { if (elem->head.htype != htype) { printf("%s: error: expected %s, got %s; id: %d\n", __func__, - get_elem_htype_str(elem->head.htype), get_elem_htype_str(htype), + get_elem_htype_str(elem->head.htype), id.id); return nullptr; } @@ -639,6 +639,17 @@ struct BMLogEntry { return static_cast(sets[sets.size() - 1]); } + BMLogSetDiff *first_diff_set(BMesh *bm) + { + for (BMLogSetBase *set : sets) { + if (set->type == LOG_SET_DIFF) { + return static_cast(set); + } + } + + return current_diff_set(bm); + } + void update_logvert(BMesh *bm, BMVert *v, BMLogVert *lv) { if (vdata.pool) { @@ -859,6 +870,29 @@ struct BMLogEntry { { current_diff_set(bm)->modify_face(bm, f); } + void modify_if_face(BMesh *bm, BMFace *f) + { + BMID id = get_elem_id(bm, f); + + bool exists = 0; + + for (int i = sets.size() - 1; i >= 0; i--) { + BMLogSetBase *set = sets[i]; + + if (set->type != LOG_SET_DIFF) { + continue; + } + // BMLogSetDiff *diff = current_diff_set(bm); + BMLogSetDiff *diff = static_cast(set); + if (diff->modified_faces.contains(id) || diff->removed_faces.contains(id) || + diff->added_faces.contains(id)) + { + return; + } + } + + first_diff_set(bm)->modify_face(bm, f); + } void undo(BMesh *bm, BMLogCallbacks *callbacks) { @@ -1054,6 +1088,12 @@ struct BMLog { current_entry->modify_face(bm, f); } + void modify_if_face(BMesh *bm, BMFace *f) + { + ensure_entry(bm); + current_entry->modify_if_face(bm, f); + } + void full_mesh(BMesh *bm) { ensure_entry(bm); @@ -1087,17 +1127,7 @@ void BMLogSetDiff::add_vert(BMesh *bm, BMVert *v) BMID id = entry->get_elem_id(bm, v); BMLogVert *lv = nullptr; - BMLogVert **modified_lv = modified_verts.lookup_ptr(id); - - if (modified_lv) { - modified_verts.remove(id); - lv = *modified_lv; - } - if (added_verts.contains(id)) { - if (lv) { - entry->free_logvert(lv); - } return; } @@ -1151,15 +1181,7 @@ void BMLogSetDiff::add_edge(BMesh *bm, BMEdge *e) BMID id = entry->get_elem_id(bm, e); BMLogEdge *le; - BMLogEdge **modified_le = modified_edges.lookup_ptr(id); - if (modified_le) { - le = *modified_le; - modified_edges.remove(id); - } - else { - le = entry->alloc_logedge(bm, e); - } - + le = entry->alloc_logedge(bm, e); added_edges.add_or_modify( id, [&](BMLogEdge **le_out) { *le_out = le; }, [&](BMLogEdge **le_out) { *le_out = le; }); } @@ -1206,18 +1228,7 @@ void BMLogSetDiff::add_face(BMesh *bm, BMFace *f) return; } - BMLogFace *lf; - - if (BMLogFace **ptr = modified_faces.lookup_ptr(id)) { - lf = *ptr; - modified_faces.remove(id); - entry->update_logface(bm, lf, f); - } - else { - lf = entry->alloc_logface(bm, f); - } - - added_faces.add(id, lf); + added_faces.add(id, entry->alloc_logface(bm, f)); } void BMLogSetDiff::remove_face(BMesh *bm, BMFace *f, bool no_check) @@ -1702,6 +1713,10 @@ void BM_log_face_modified(BMesh *bm, BMLog *log, BMFace *f) { log->modify_face(bm, f); } +void BM_log_face_if_modified(BMesh *bm, BMLog *log, BMFace *f) +{ + log->modify_if_face(bm, f); +} void BM_log_face_removed(BMesh *bm, BMLog *log, BMFace *f) { log->remove_face(bm, f); diff --git a/source/blender/bmesh/intern/bmesh_log_intern.h b/source/blender/bmesh/intern/bmesh_log_intern.h index f3d6635399e..04a291018c7 100644 --- a/source/blender/bmesh/intern/bmesh_log_intern.h +++ b/source/blender/bmesh/intern/bmesh_log_intern.h @@ -154,13 +154,12 @@ void BM_log_edge_added(BMesh *bm, BMLog *log, BMEdge *e); void BM_log_edge_modified(BMesh *bm, BMLog *log, BMEdge *e); -/* Log a face before it is modified - * - * This is intended to handle only header flags and we always - * assume face has been added before - */ +/* Log a face's flags and customdata. */ void BM_log_face_modified(BMesh *bm, BMLog *log, struct BMFace *f); +/* Log a face's flags and customdata if it doesn't exist in the log already. */ +void BM_log_face_if_modified(BMesh *bm, BMLog *log, struct BMFace *f); + /* Log a new face as added to the BMesh */ /* Log a new face as added to the BMesh * diff --git a/source/blender/bmesh/operators/bmo_removedoubles.c b/source/blender/bmesh/operators/bmo_removedoubles.c index 9a0257d28d7..42b43a61514 100644 --- a/source/blender/bmesh/operators/bmo_removedoubles.c +++ b/source/blender/bmesh/operators/bmo_removedoubles.c @@ -443,7 +443,7 @@ void bmo_pointmerge_exec(BMesh *bm, BMOperator *op) #define USE_BM_EDGE_COLLAPSE -ATTR_NO_OPT void bmo_collapse_exec(BMesh *bm, BMOperator *op) +void bmo_collapse_exec(BMesh *bm, BMOperator *op) { BMOperator weldop; BMWalker walker; @@ -510,7 +510,7 @@ ATTR_NO_OPT void bmo_collapse_exec(BMesh *bm, BMOperator *op) BLI_stack_pop(edge_stack, &e); #ifdef USE_BM_EDGE_COLLAPSE - if (BM_elem_is_free((BMElem*)e, BM_EDGE)) { + if (BM_elem_is_free((BMElem *)e, BM_EDGE)) { continue; } #endif diff --git a/source/blender/editors/sculpt_paint/sculpt.cc b/source/blender/editors/sculpt_paint/sculpt.cc index bc157cb4d86..b8459c54000 100644 --- a/source/blender/editors/sculpt_paint/sculpt.cc +++ b/source/blender/editors/sculpt_paint/sculpt.cc @@ -4120,11 +4120,12 @@ static void do_brush_action(Sculpt *sd, PBVHType type = BKE_pbvh_type(ss->pbvh); if (SCULPT_tool_is_paint(brush->sculpt_tool) && SCULPT_has_loop_colors(ob)) { - if (type != PBVH_FACES) { + if (type == PBVH_GRIDS) { return; } - - BKE_pbvh_ensure_node_loops(ss->pbvh); + else if (type == PBVH_FACES) { + BKE_pbvh_ensure_node_loops(ss->pbvh); + } } const bool use_original = sculpt_tool_needs_original(brush->sculpt_tool) || !ss->cache->accum; @@ -4263,6 +4264,11 @@ static void do_brush_action(Sculpt *sd, } for (int i : nodes.index_range()) { + /* Will save all face/loop customdata. */ + if (brush->autosmooth_factor > 0.0f || brush->topology_rake_factor > 0.0f) { + extra_type = SCULPT_UNDO_FACE_SETS; + } + SCULPT_ensure_dyntopo_node_undo(ob, nodes[i], undo_type, extra_type); switch (undo_type) { diff --git a/source/blender/editors/sculpt_paint/sculpt_api.cc b/source/blender/editors/sculpt_paint/sculpt_api.cc index 168bf89becd..dfdf22c2b21 100644 --- a/source/blender/editors/sculpt_paint/sculpt_api.cc +++ b/source/blender/editors/sculpt_paint/sculpt_api.cc @@ -245,7 +245,7 @@ eSculptBoundary SCULPT_edge_is_boundary(const SculptSession *ss, BMLoop *l = e->l; int cd_uv = layer.offset; - float limit = 0.0005; + float limit = 0.01; float2 a1 = BM_ELEM_CD_PTR((l->v == e->v1 ? l : l->next), cd_uv); float2 a2 = BM_ELEM_CD_PTR((l->v == e->v2 ? l : l->next), cd_uv); @@ -256,6 +256,7 @@ eSculptBoundary SCULPT_edge_is_boundary(const SculptSession *ss, if (len_v2v2(a1, b1) > limit || len_v2v2(a2, b2) > limit) { ret |= SCULPT_BOUNDARY_UV; + //e->head.hflag |= BM_ELEM_SELECT; goto uv_outer; } } while ((l = l->radial_next) != e->l); @@ -481,7 +482,7 @@ static bool sculpt_vertex_ensure_boundary(const SculptSession *ss, (BMVert *)vertex.i, &ss->bm->ldata, ss->totuv, - ss->reproject_smooth, + true, ss->sharp_angle_limit); } else if ((mask & (SCULPT_BOUNDARY_SHARP_ANGLE | SCULPT_CORNER_SHARP_ANGLE)) && diff --git a/source/blender/editors/sculpt_paint/sculpt_cloth.cc b/source/blender/editors/sculpt_paint/sculpt_cloth.cc index 18ca1e56829..ddbec68713c 100644 --- a/source/blender/editors/sculpt_paint/sculpt_cloth.cc +++ b/source/blender/editors/sculpt_paint/sculpt_cloth.cc @@ -1123,6 +1123,10 @@ void SCULPT_cloth_brush_simulation_init(SculptSession *ss, SculptClothSimulation const int totverts = SCULPT_vertex_count_get(ss); const bool has_deformation_pos = cloth_sim->deformation_pos != nullptr; const bool has_softbody_pos = cloth_sim->softbody_pos != nullptr; + + SCULPT_vertex_random_access_ensure(ss); + SCULPT_face_random_access_ensure(ss); + for (int i = 0; i < totverts; i++) { PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.hh b/source/blender/editors/sculpt_paint/sculpt_intern.hh index 8f9a41d4563..b4c3ebc4e94 100644 --- a/source/blender/editors/sculpt_paint/sculpt_intern.hh +++ b/source/blender/editors/sculpt_paint/sculpt_intern.hh @@ -1902,7 +1902,8 @@ void SCULPT_neighbor_coords_average(SculptSession *ss, PBVHVertRef index, float projection, float hard_corner_pin, - bool weighted); + bool weighted, + float factor = 1.0f); float SCULPT_neighbor_mask_average(SculptSession *ss, PBVHVertRef index); void SCULPT_neighbor_color_average(SculptSession *ss, float result[4], PBVHVertRef index); @@ -1914,11 +1915,13 @@ void SCULPT_neighbor_coords_average_interior(SculptSession *ss, float projection, float hard_corner_pin, bool use_area_weights, - bool smooth_origco = false); + bool smooth_origco = false, + float factor = 1.0f); BLI_INLINE bool SCULPT_need_reproject(const SculptSession *ss) { - return ss->reproject_smooth && ss->bm; // && CustomData_has_layer(&ss->bm->ldata, CD_PROP_FLOAT2); + return ss->reproject_smooth && + ss->bm; // && CustomData_has_layer(&ss->bm->ldata, CD_PROP_FLOAT2); } int SCULPT_vertex_island_get(SculptSession *ss, PBVHVertRef vertex); diff --git a/source/blender/editors/sculpt_paint/sculpt_smooth.cc b/source/blender/editors/sculpt_paint/sculpt_smooth.cc index 5fb79f40152..4d135eeb7f9 100644 --- a/source/blender/editors/sculpt_paint/sculpt_smooth.cc +++ b/source/blender/editors/sculpt_paint/sculpt_smooth.cc @@ -10,6 +10,7 @@ #include "BLI_alloca.h" #include "BLI_math.h" #include "BLI_math_vector_types.hh" +#include "BLI_set.hh" #include "BLI_task.h" #include "BLI_vector.hh" @@ -34,7 +35,15 @@ using blender::float2; using blender::float3; +using blender::float4; +using blender::IndexRange; +using blender::MutableSpan; +using blender::Set; +using blender::Span; using blender::Vector; + +//#define SMOOTH_FACE_CORNERS + float BKE_pbvh_bmesh_detail_size_avg_get(PBVH *pbvh); #if 0 @@ -90,7 +99,8 @@ static void SCULPT_neighbor_coords_average_interior_ex(SculptSession *ss, bool weighted, eSculptBoundary bound_type, eSculptCorner corner_type, - bool smooth_origco) + bool smooth_origco, + float factor) { float3 avg(0.0f, 0.0f, 0.0f); @@ -101,6 +111,9 @@ static void SCULPT_neighbor_coords_average_interior_ex(SculptSession *ss, } #endif + bound_type |= SCULPT_BOUNDARY_UV; + corner_type |= SCULPT_CORNER_UV; + const eSculptBoundary is_boundary = SCULPT_vertex_is_boundary(ss, vertex, bound_type); const eSculptCorner is_corner = SCULPT_vertex_is_corner(ss, vertex, corner_type); @@ -117,8 +130,9 @@ static void SCULPT_neighbor_coords_average_interior_ex(SculptSession *ss, vertex_no_get(ss, vertex, no); co = vertex_co_get(ss, vertex); + const int valence = SCULPT_vertex_valence_get(ss, vertex); + if (weighted) { - const int valence = SCULPT_vertex_valence_get(ss, vertex); areas = reinterpret_cast(BLI_array_alloca(areas, valence)); BKE_pbvh_get_vert_face_areas(ss->pbvh, vertex, areas, valence); } @@ -126,8 +140,35 @@ static void SCULPT_neighbor_coords_average_interior_ex(SculptSession *ss, float total = 0.0f; int totboundary = 0; +#ifdef SMOOTH_FACE_CORNERS + Vector ws; + Vector loops; + + bool is_bmesh = BKE_pbvh_type(ss->pbvh) == PBVH_BMESH; + auto addblock = [&](PBVHVertRef vertex2, PBVHEdgeRef edge2, float w) { + if (!is_bmesh) { + return; + } + BMVert *v = reinterpret_cast(vertex2.i); + BMEdge *e = reinterpret_cast(edge2.i); + + if (!e->l) { + return; + } + + BMLoop *l = e->l; + do { + BMLoop *l2 = l->v == v ? l : l->next; + + loops.append(l2); + ws.append(w); + } while ((l = l->radial_next) != e->l); + }; +#endif + SculptVertexNeighborIter ni; SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vertex, ni) { + bool project_ok; float w = weighted ? areas[ni.i] : 1.0f; eSculptBoundary is_boundary2; @@ -138,6 +179,7 @@ static void SCULPT_neighbor_coords_average_interior_ex(SculptSession *ss, else { is_boundary2 = SCULPT_vertex_is_boundary(ss, ni.vertex, bound_type); } + const eSculptBoundary smooth_types = !ss->hard_edge_mode ? SCULPT_BOUNDARY_FACE_SET | SCULPT_BOUNDARY_UV | SCULPT_BOUNDARY_SEAM : @@ -149,24 +191,40 @@ static void SCULPT_neighbor_coords_average_interior_ex(SculptSession *ss, /* Boundary vertices use only other boundary vertices. */ if (is_boundary) { + project_ok = false; + /* Handle smooth boundaries. */ if (bool(is_boundary2 & smooth_types) != bool(is_boundary & smooth_types)) { /* Project to plane. */ float3 t1 = float3(vertex_co_get(ss, ni.vertex)) - co; + float fac = dot_v3v3(t1, no); - avg += (co + no * dot_v3v3(t1, no)) * w; + float3 tco = (co + no * fac); + +#ifdef SMOOTH_FACE_CORNERS + addblock(vertex, ni.edge, w); +#endif + avg += tco * w; total += w; } else if (is_boundary & is_boundary2) { avg += float3(vertex_co_get(ss, ni.vertex)) * w; total += w; + project_ok = true; } } else { /* Interior vertices use all neighbors. */ avg += float3(vertex_co_get(ss, ni.vertex)) * w; total += w; + project_ok = true; } + +#ifdef SMOOTH_FACE_CORNERS + if (project_ok) { + addblock(ni.vertex, ni.edge, w); + } +#endif } SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); @@ -175,11 +233,19 @@ static void SCULPT_neighbor_coords_average_interior_ex(SculptSession *ss, total = 0.0; zero_v3(avg); +#ifdef SMOOTH_FACE_CORNERS + loops.clear(); + ws.clear(); +#endif + SculptVertexNeighborIter ni; SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vertex, ni) { float w = weighted ? areas[ni.i] : 1.0f; avg += float3(vertex_co_get(ss, ni.vertex)) * w; total += w; +#ifdef SMOOTH_FACE_CORNERS + addblock(ni.vertex, ni.edge, w); +#endif } SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); } @@ -190,6 +256,12 @@ static void SCULPT_neighbor_coords_average_interior_ex(SculptSession *ss, return; } +#ifdef SMOOTH_FACE_CORNERS + if (is_bmesh && !smooth_origco) { + blender::bke::sculpt::interp_face_corners(ss->pbvh, vertex, loops, ws, factor); + } +#endif + /* Project to plane if desired. */ avg = avg / (float)total - co; float t = dot_v3v3(avg, no); @@ -210,7 +282,8 @@ void SCULPT_neighbor_coords_average_interior(SculptSession *ss, float projection, float hard_corner_pin, bool use_area_weights, - bool smooth_origco) + bool smooth_origco, + float factor) { eSculptBoundary bound_type = ss->smooth_boundary_flag; eSculptCorner corner_type; @@ -231,7 +304,8 @@ void SCULPT_neighbor_coords_average_interior(SculptSession *ss, use_area_weights, bound_type, corner_type, - smooth_origco); + smooth_origco, + factor); } /* Compares four vectors seperated by 90 degrees around normal and picks the one closest @@ -604,14 +678,23 @@ void SCULPT_neighbor_coords_average(SculptSession *ss, PBVHVertRef vertex, float projection, float hard_corner_pin, - bool weighted) + bool weighted, + float factor) { eSculptCorner corner_type = SCULPT_CORNER_SHARP_MARK | SCULPT_CORNER_FACE_SET; eSculptBoundary bound_type = SCULPT_BOUNDARY_SHARP_MARK | SCULPT_BOUNDARY_SEAM | SCULPT_BOUNDARY_UV | SCULPT_BOUNDARY_FACE_SET; - SCULPT_neighbor_coords_average_interior_ex( - ss, result, vertex, projection, hard_corner_pin, weighted, bound_type, corner_type, false); + SCULPT_neighbor_coords_average_interior_ex(ss, + result, + vertex, + projection, + hard_corner_pin, + weighted, + bound_type, + corner_type, + false, + factor); } float SCULPT_neighbor_mask_average(SculptSession *ss, PBVHVertRef vertex) @@ -772,7 +855,11 @@ static void do_smooth_brush_task_cb_ex(void *__restrict userdata, float bstrength = data->strength; PBVHVertexIter vd; +#ifdef SMOOTH_FACE_CORNERS + const bool do_reproject = false; +#else const bool do_reproject = SCULPT_need_reproject(ss); +#endif CLAMP(bstrength, 0.0f, 1.0f); @@ -829,13 +916,14 @@ static void do_smooth_brush_task_cb_ex(void *__restrict userdata, float avg[3], val[3]; SCULPT_neighbor_coords_average_interior( - ss, avg, vd.vertex, projection, hard_corner_pin, weighted, false); + ss, avg, vd.vertex, projection, hard_corner_pin, weighted, false, fade); if (data->smooth_origco) { float origco_avg[3]; SCULPT_neighbor_coords_average_interior( - ss, origco_avg, vd.vertex, projection, hard_corner_pin, weighted, true); + ss, origco_avg, vd.vertex, projection, hard_corner_pin, weighted, true, fade); + float *origco = blender::bke::paint::vertex_attr_ptr(vd.vertex, ss->attrs.orig_co); interp_v3_v3v3(origco, origco, origco_avg, fade); } @@ -926,8 +1014,7 @@ void SCULPT_smooth( data.strength = strength; TaskParallelSettings settings; - //XXX - BKE_pbvh_parallel_range_settings(&settings, false, nodes.size()); + BKE_pbvh_parallel_range_settings(&settings, true, nodes.size()); BLI_task_parallel_range(0, nodes.size(), &data, do_smooth_brush_task_cb_ex, &settings); } } diff --git a/source/blender/editors/sculpt_paint/sculpt_undo.cc b/source/blender/editors/sculpt_paint/sculpt_undo.cc index 0d1565f9f9c..c1f43672181 100644 --- a/source/blender/editors/sculpt_paint/sculpt_undo.cc +++ b/source/blender/editors/sculpt_paint/sculpt_undo.cc @@ -700,6 +700,16 @@ typedef struct BmeshUndoData { bool is_redo; } BmeshUndoData; +static void bmesh_undo_vert_update(BmeshUndoData *data, BMVert *v, bool triangulate = false) +{ + *BM_ELEM_CD_PTR(v, data->cd_flags) |= SCULPTFLAG_NEED_VALENCE; + if (triangulate) { + *BM_ELEM_CD_PTR(v, data->cd_flags) |= SCULPTFLAG_NEED_TRIANGULATE; + } + + BKE_sculpt_boundary_flag_update(data->ob->sculpt, {reinterpret_cast(v)}); +} + static void bmesh_undo_on_vert_kill(BMVert *v, void *userdata) { BmeshUndoData *data = (BmeshUndoData *)userdata; @@ -730,14 +740,12 @@ static void bmesh_undo_on_vert_add(BMVert *v, void *userdata) data->balance_pbvh = true; + bmesh_undo_vert_update(data, v, true); + /* Flag vert as unassigned to a PBVH node; it'll be added to pbvh when * its owning faces are. */ BM_ELEM_CD_SET_INT(v, data->cd_vert_node_offset, -1); - - *(int *)BM_ELEM_CD_GET_VOID_P(v, data->cd_boundary_flag) |= SCULPT_BOUNDARY_NEEDS_UPDATE; - *BM_ELEM_CD_PTR(v, data->cd_flags) |= SCULPTFLAG_NEED_VALENCE | - SCULPTFLAG_NEED_TRIANGULATE; } static void bmesh_undo_on_face_kill(BMFace *f, void *userdata) @@ -754,8 +762,7 @@ static void bmesh_undo_on_face_kill(BMFace *f, void *userdata) BMLoop *l = f->l_first; do { - *BM_ELEM_CD_PTR(l->v, data->cd_flags) |= SCULPTFLAG_NEED_VALENCE; - *BM_ELEM_CD_PTR(l->v, data->cd_boundary_flag) |= SCULPT_BOUNDARY_NEEDS_UPDATE; + bmesh_undo_vert_update(data, l->v, true); } while ((l = l->next) != f->l_first); // data->do_full_recalc = true; @@ -775,16 +782,9 @@ static void bmesh_undo_on_face_add(BMFace *f, void *userdata) BMLoop *l = f->l_first; do { - *(int *)BM_ELEM_CD_GET_VOID_P(l->v, data->cd_boundary_flag) |= SCULPT_BOUNDARY_NEEDS_UPDATE; - - *BM_ELEM_CD_PTR(l->v, data->cd_flags) |= SCULPTFLAG_NEED_VALENCE; - - if (f->len > 3) { - *BM_ELEM_CD_PTR(l->v, data->cd_flags) |= SCULPTFLAG_NEED_TRIANGULATE; - } + bmesh_undo_vert_update(data, l->v, f->len > 3); int ni_l = BM_ELEM_CD_GET_INT(l->v, data->cd_vert_node_offset); - if (ni_l < 0 && ni >= 0) { BM_ELEM_CD_SET_INT(l->v, ni_l, ni); DyntopoSet *bm_unique_verts = BKE_pbvh_bmesh_node_unique_verts(node); @@ -807,7 +807,7 @@ static void bmesh_undo_full_mesh(void *userdata) BMVert *v; BM_ITER_MESH (v, &iter, data->bm, BM_VERTS_OF_MESH) { - BKE_pbvh_bmesh_update_valence(data->pbvh, BKE_pbvh_make_vref((intptr_t)v)); + bmesh_undo_vert_update(data, v, true); } data->pbvh = nullptr; @@ -826,32 +826,24 @@ static void bmesh_undo_on_edge_kill(BMEdge *e, void *userdata) { BmeshUndoData *data = (BmeshUndoData *)userdata; - *(int *)BM_ELEM_CD_GET_VOID_P(e->v1, data->cd_boundary_flag) |= SCULPT_BOUNDARY_NEEDS_UPDATE; - *(int *)BM_ELEM_CD_GET_VOID_P(e->v2, data->cd_boundary_flag) |= SCULPT_BOUNDARY_NEEDS_UPDATE; - - *BM_ELEM_CD_PTR(e->v1, data->cd_flags) |= SCULPTFLAG_NEED_VALENCE | - SCULPTFLAG_NEED_TRIANGULATE; - *BM_ELEM_CD_PTR(e->v2, data->cd_flags) |= SCULPTFLAG_NEED_VALENCE | - SCULPTFLAG_NEED_TRIANGULATE; + bmesh_undo_vert_update(data, e->v1, true); + bmesh_undo_vert_update(data, e->v2, true); }; ; static void bmesh_undo_on_edge_add(BMEdge *e, void *userdata) { BmeshUndoData *data = (BmeshUndoData *)userdata; - *(int *)BM_ELEM_CD_GET_VOID_P(e->v1, data->cd_boundary_flag) |= SCULPT_BOUNDARY_NEEDS_UPDATE; - *(int *)BM_ELEM_CD_GET_VOID_P(e->v2, data->cd_boundary_flag) |= SCULPT_BOUNDARY_NEEDS_UPDATE; - - *BM_ELEM_CD_PTR(e->v1, data->cd_flags) |= SCULPTFLAG_NEED_VALENCE | - SCULPTFLAG_NEED_TRIANGULATE; - *BM_ELEM_CD_PTR(e->v2, data->cd_flags) |= SCULPTFLAG_NEED_VALENCE | - SCULPTFLAG_NEED_TRIANGULATE; + bmesh_undo_vert_update(data, e->v1, true); + bmesh_undo_vert_update(data, e->v2, true); } static void bmesh_undo_on_vert_change(BMVert *v, void *userdata, void *old_customdata) { BmeshUndoData *data = (BmeshUndoData *)userdata; + bmesh_undo_vert_update(data, v, false); + if (!old_customdata) { BM_ELEM_CD_SET_INT(v, data->cd_vert_node_offset, -1); data->regen_all_unique_verts = true; -- 2.30.2 From 27bf295b3c8b2bbbaa36ed8f62e81dc9f5a8579d Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Tue, 6 Jun 2023 03:59:14 -0700 Subject: [PATCH 163/279] temp-sculpt-dyntopo: Use new uv smooth code for stability smooth Use the new UV smoothing code instead of reprojection for the small amount of surface relaxation dyntopo does to reduce numerical instability. --- source/blender/blenkernel/BKE_sculpt.hh | 35 ++++++++--- source/blender/blenkernel/intern/dyntopo.cc | 63 ++++++++++++++----- .../blender/blenkernel/intern/pbvh_bmesh.cc | 10 +-- source/blender/editors/sculpt_paint/sculpt.cc | 2 +- .../sculpt_paint/sculpt_paint_color.cc | 2 +- .../editors/sculpt_paint/sculpt_smooth.cc | 3 +- 6 files changed, 82 insertions(+), 33 deletions(-) diff --git a/source/blender/blenkernel/BKE_sculpt.hh b/source/blender/blenkernel/BKE_sculpt.hh index 133f8a96446..f5c098cc837 100644 --- a/source/blender/blenkernel/BKE_sculpt.hh +++ b/source/blender/blenkernel/BKE_sculpt.hh @@ -12,9 +12,11 @@ #include "BKE_pbvh.h" #include "BLI_compiler_compat.h" +#include "BLI_math_vector_types.hh" #include "BLI_span.hh" #include "BLI_vector.hh" -#include "BLI_math_vector_types.hh" + +#include /* * Stroke ID API. This API is used to detect if @@ -39,10 +41,8 @@ enum StrokeIDUser { }; ENUM_OPERATORS(StrokeIDUser, STROKEID_USER_LAYER_BRUSH); -void BKE_sculpt_reproject_cdata(SculptSession *ss, - PBVHVertRef vertex, - float startco[3], - float startno[3]); +void BKE_sculpt_reproject_cdata( + SculptSession *ss, PBVHVertRef vertex, float startco[3], float startno[3], bool do_uvs = true); namespace blender::bke::sculpt { BLI_INLINE bool stroke_id_clear(SculptSession *ss, PBVHVertRef vertex, StrokeIDUser user) @@ -101,7 +101,6 @@ void interp_face_corners( PBVH *pbvh, PBVHVertRef vertex, Span loops, Span ws, float factor); bool loop_is_corner(BMLoop *l, int cd_offset); - static bool prop_eq(float a, float b, float limit) { return std::fabs(a - b) < limit; @@ -148,9 +147,9 @@ struct VertLoopSnapper { Span layers; Span &ls; Vector max_indices; - float limit = 0.1; + float limit = 0.001; - VertLoopSnapper(Span ls_, Span layers_) : ls(ls_) + VertLoopSnapper(Span ls_, Span layers_) : ls(ls_), layers(layers_) { snap_sets.resize(ls.size()); for (auto &snap_set : snap_sets) { @@ -203,6 +202,26 @@ struct VertLoopSnapper { CustomDataLayer *layer = layers[layer_i]; int idx_base = 1; + float limit = 0.001; + + if constexpr (std::is_same_v) { + /* Set UV snap limit as 1/10th the average uv edge length. */ + limit = 0.1; + float len = 0.0; + + for (BMLoop *l : ls) { + T value1 = *BM_ELEM_CD_PTR(l, layer->offset); + T value2 = *BM_ELEM_CD_PTR(l->next, layer->offset); + + len += fabsf(value1[0] - value2[0]) * 0.5; + len += fabsf(value1[1] - value2[1]) * 0.5; + } + + len /= ls.size(); + limit *= len; + // printf("limit: %.5f len: %.5f\n", limit, len); + } + for (int i : ls.index_range()) { if (snap_sets[i][layer_i] != 0) { continue; diff --git a/source/blender/blenkernel/intern/dyntopo.cc b/source/blender/blenkernel/intern/dyntopo.cc index c37c4af01d6..d5a7a693e37 100644 --- a/source/blender/blenkernel/intern/dyntopo.cc +++ b/source/blender/blenkernel/intern/dyntopo.cc @@ -70,7 +70,7 @@ static void edge_queue_create_local(EdgeQueueContext *eq_ctx, PBVH *pbvh, PBVHTopologyUpdateMode local_mode); -static void surface_smooth_v_safe( +ATTR_NO_OPT static void surface_smooth_v_safe( SculptSession *ss, PBVH *pbvh, BMVert *v, float fac, bool reproject_cdata) { float co[3]; @@ -83,6 +83,23 @@ static void surface_smooth_v_safe( return; } + Vector loops; + Vector ws; + + auto addblock = [&](BMEdge *e, float w) { + if (!e->l) { + return; + } + + BMLoop *l = e->l; + do { + BMLoop *l2 = l->v != v ? l : l->next; + + loops.append(l2); + ws.append(w); + } while ((l = l->radial_next) != e->l); + }; + PBVHVertRef vertex = {reinterpret_cast(v)}; if (stroke_id_test(ss, vertex, STROKEID_USER_ORIGINAL)) { copy_v3_v3(origco1, v->co); @@ -136,11 +153,11 @@ static void surface_smooth_v_safe( float w2 = area_tri_v3(l->v->co, l->next->v->co, l->prev->v->co); w = (w1 + w2) * 0.5f; } - else { + else { /* Backup weight if bad areas */ w = len_squared_v3v3(e->v1->co, e->v2->co); } } - else { + else { /* Backup weight if bad areas */ w = len_squared_v3v3(e->v1->co, e->v2->co); } /* Note: we can't validate the boundary flags from with a thread @@ -154,6 +171,8 @@ static void surface_smooth_v_safe( continue; } + addblock(e, w); + sub_v3_v3v3(tan, v2->co, v->co); float d = dot_v3v3(tan, v->no); @@ -199,11 +218,18 @@ static void surface_smooth_v_safe( atomic_cas_float(&v->co[1], y, ny); atomic_cas_float(&v->co[2], z, nz); - /* Note: threading is disabled if reproject_cdata is on. */ + /* + * Use reprojection for non-UV attributes. UV attributes + * use blender::bke::sculpt::interp_face_corners using + * the weights we built earlier. + */ + /* Reproject attributes. */ if (reproject_cdata) { - BKE_sculpt_reproject_cdata(ss, vertex, startco, startno); + BKE_sculpt_reproject_cdata(ss, vertex, startco, startno, false); } + blender::bke::sculpt::interp_face_corners(pbvh, vertex, loops, ws, fac); + float *start_origco = blender::bke::paint::vertex_attr_ptr(vertex, ss->attrs.orig_co); /* Conflicts here should be pretty rare. */ @@ -3525,10 +3551,8 @@ inline void reproject_bm_data( } } -void BKE_sculpt_reproject_cdata(SculptSession *ss, - PBVHVertRef vertex, - float startco[3], - float startno[3]) +void BKE_sculpt_reproject_cdata( + SculptSession *ss, PBVHVertRef vertex, float startco[3], float startno[3], bool do_uvs) { int boundary_flag = blender::bke::paint::vertex_attr_get(vertex, ss->attrs.boundary_flags); if (boundary_flag & (SCULPT_BOUNDARY_UV)) { @@ -3610,7 +3634,7 @@ void BKE_sculpt_reproject_cdata(SculptSession *ss, l2->head.hflag |= tag; ls.append(l2); - for (int i = 0; i < totuv; i++) { + for (int i = 0; do_uvs && i < totuv; i++) { const int cd_uv = uvlayer[i].offset; float *luv = BM_ELEM_CD_PTR(l2, cd_uv); @@ -3677,6 +3701,10 @@ void BKE_sculpt_reproject_cdata(SculptSession *ss, eCustomDataMask typemask = CD_MASK_PROP_FLOAT | CD_MASK_PROP_FLOAT2 | CD_MASK_PROP_FLOAT3 | CD_MASK_PROP_BYTE_COLOR | CD_MASK_PROP_COLOR; + if (!do_uvs) { + typemask &= ~CD_MASK_PROP_FLOAT2; + } + CustomData *cdatas[2] = {&ss->bm->vdata, &ss->bm->ldata}; bool ok = false; @@ -3694,7 +3722,7 @@ void BKE_sculpt_reproject_cdata(SculptSession *ss, continue; } - /* We don't reproject origco/origno. */ + /* Don't reproject original data from start of stroke. */ if (i == 0) { bool bad = false; @@ -3723,12 +3751,12 @@ void BKE_sculpt_reproject_cdata(SculptSession *ss, Vector loops; Vector layers; - typemask = CD_MASK_PROP_FLOAT2; // | CD_MASK_PROP_FLOAT2 | CD_MASK_PROP_FLOAT3 | - //CD_MASK_PROP_COLOR; + eCustomDataMask snap_typemask = CD_MASK_PROP_FLOAT2; + for (int i = 0; i < ldata->totlayer; i++) { CustomDataLayer *layer = ldata->layers + i; - if (!(CD_TYPE_AS_MASK(layer->type) & typemask)) { + if (!(CD_TYPE_AS_MASK(layer->type) & snap_typemask)) { continue; } if (layer->flag & CD_FLAG_ELEM_NOINTERP) { @@ -3754,7 +3782,7 @@ void BKE_sculpt_reproject_cdata(SculptSession *ss, } while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e); blender::bke::sculpt::VertLoopSnapper snapper = {loops, layers}; - + int totstep = 2; for (int step = 0; step < totstep; step++) { float3 startco2; @@ -3878,7 +3906,10 @@ void BKE_sculpt_reproject_cdata(SculptSession *ss, } snapper.snap(); - return; // XXX + return; + + //XXX delete the below code after testing new snap code. + /* Re-snap uvs. */ v = (BMVert *)vertex.i; diff --git a/source/blender/blenkernel/intern/pbvh_bmesh.cc b/source/blender/blenkernel/intern/pbvh_bmesh.cc index 98c95a5e30d..2c6cb9ffc2a 100644 --- a/source/blender/blenkernel/intern/pbvh_bmesh.cc +++ b/source/blender/blenkernel/intern/pbvh_bmesh.cc @@ -4026,7 +4026,7 @@ bool loop_is_corner(BMLoop *l, int cd_offset) BMVert *v = l->v; float2 value = *BM_ELEM_CD_PTR(l, cd_offset); - float limit = 0.1; + float limit = 0.01; Vector ls; Vector keys; @@ -4075,7 +4075,7 @@ ATTR_NO_OPT bool loop_is_corner(BMLoop *l, int cd_offset) BMEdge *e = v->e; float2 value = *BM_ELEM_CD_PTR(l, cd_offset); - float limit = 0.1; + float limit = 0.01; BMLoop *outer1 = nullptr, *outer2 = nullptr; do { @@ -4187,9 +4187,9 @@ static void corner_interp(CustomDataLayer *layer, } /* Interpolates loops surrounding a vertex, splitting any UV map by - * island as appropriate and enforcing proper boundary conditions. + * island as appropriate and enforcing proper boundary conditions. */ -void interp_face_corners( +ATTR_NO_OPT void interp_face_corners( PBVH *pbvh, PBVHVertRef vertex, Span loops, Span ws, float factor) { if (BKE_pbvh_type(pbvh) != PBVH_BMESH) { @@ -4292,7 +4292,7 @@ void interp_face_corners( } /* Snap. */ - corner_snap.snap(); + //corner_snap.snap(); } } } // namespace blender::bke::sculpt diff --git a/source/blender/editors/sculpt_paint/sculpt.cc b/source/blender/editors/sculpt_paint/sculpt.cc index b8459c54000..f960ac84a01 100644 --- a/source/blender/editors/sculpt_paint/sculpt.cc +++ b/source/blender/editors/sculpt_paint/sculpt.cc @@ -7097,7 +7097,7 @@ void SCULPT_topology_islands_ensure(Object *ob) return; } - SculptAttributeParams params; + SculptAttributeParams params = {}; params.permanent = params.stroke_only = params.simple_array = false; ss->attrs.topology_island_key = BKE_sculpt_attribute_ensure( diff --git a/source/blender/editors/sculpt_paint/sculpt_paint_color.cc b/source/blender/editors/sculpt_paint/sculpt_paint_color.cc index a930b26c348..6fca70f2acc 100644 --- a/source/blender/editors/sculpt_paint/sculpt_paint_color.cc +++ b/source/blender/editors/sculpt_paint/sculpt_paint_color.cc @@ -398,7 +398,7 @@ void SCULPT_do_paint_brush(PaintModeSettings *paint_mode_settings, SculptAttribute *buffer_scl; - SculptAttributeParams params; + SculptAttributeParams params = {}; params.stroke_only = true; SculptAttributeParams params_id = {}; diff --git a/source/blender/editors/sculpt_paint/sculpt_smooth.cc b/source/blender/editors/sculpt_paint/sculpt_smooth.cc index 4d135eeb7f9..8d20a3fab6a 100644 --- a/source/blender/editors/sculpt_paint/sculpt_smooth.cc +++ b/source/blender/editors/sculpt_paint/sculpt_smooth.cc @@ -111,8 +111,7 @@ static void SCULPT_neighbor_coords_average_interior_ex(SculptSession *ss, } #endif - bound_type |= SCULPT_BOUNDARY_UV; - corner_type |= SCULPT_CORNER_UV; + //bound_type |= SCULPT_BOUNDARY_UV; const eSculptBoundary is_boundary = SCULPT_vertex_is_boundary(ss, vertex, bound_type); const eSculptCorner is_corner = SCULPT_vertex_is_corner(ss, vertex, corner_type); -- 2.30.2 From b49a18e497eb14458419fb0419f11954366d0aa5 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Tue, 6 Jun 2023 04:03:46 -0700 Subject: [PATCH 164/279] temp-sculpt-dyntopo: Move call to interp_face_corners into if block --- source/blender/blenkernel/intern/dyntopo.cc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/source/blender/blenkernel/intern/dyntopo.cc b/source/blender/blenkernel/intern/dyntopo.cc index d5a7a693e37..642410825a7 100644 --- a/source/blender/blenkernel/intern/dyntopo.cc +++ b/source/blender/blenkernel/intern/dyntopo.cc @@ -226,10 +226,9 @@ ATTR_NO_OPT static void surface_smooth_v_safe( /* Reproject attributes. */ if (reproject_cdata) { BKE_sculpt_reproject_cdata(ss, vertex, startco, startno, false); + blender::bke::sculpt::interp_face_corners(pbvh, vertex, loops, ws, fac); } - blender::bke::sculpt::interp_face_corners(pbvh, vertex, loops, ws, fac); - float *start_origco = blender::bke::paint::vertex_attr_ptr(vertex, ss->attrs.orig_co); /* Conflicts here should be pretty rare. */ -- 2.30.2 From c90f603a6d7bdd5940c931a32bb62d3bc198cbf2 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Tue, 6 Jun 2023 14:39:57 -0700 Subject: [PATCH 165/279] temp-sculpt-dyntopo: Cleanup gpu index buffer code Also fixed very nasty bug in customdata code. --- source/blender/blenkernel/BKE_pbvh.h | 14 +- .../blender/blenkernel/intern/customdata.cc | 86 ++--- source/blender/blenkernel/intern/dyntopo.cc | 12 +- source/blender/blenkernel/intern/paint.cc | 30 +- source/blender/blenkernel/intern/pbvh.cc | 18 +- .../blender/blenkernel/intern/pbvh_bmesh.cc | 306 ++++++------------ source/blender/bmesh/intern/bmesh_collapse.hh | 4 +- source/blender/draw/intern/draw_pbvh.cc | 171 +++++++--- source/blender/editors/sculpt_paint/sculpt.cc | 9 +- .../editors/sculpt_paint/sculpt_dyntopo.cc | 4 +- .../editors/sculpt_paint/sculpt_face_set.cc | 5 + 11 files changed, 320 insertions(+), 339 deletions(-) diff --git a/source/blender/blenkernel/BKE_pbvh.h b/source/blender/blenkernel/BKE_pbvh.h index 771cd5636fb..9ccdfd3bf67 100644 --- a/source/blender/blenkernel/BKE_pbvh.h +++ b/source/blender/blenkernel/BKE_pbvh.h @@ -139,11 +139,12 @@ typedef struct PBVHTri { } PBVHTri; typedef struct PBVHTriBuf { - PBVHTri *tris; - PBVHVertRef *verts; - int *edges; - int totvert, totedge, tottri; - int verts_size, edges_size, tris_size; + blender::Vector tris; + blender::Vector verts; + blender::Vector edges; + blender::Vector loops; + + int mat_nr = 0; # ifdef __cplusplus blender::Map vertmap; @@ -151,9 +152,6 @@ typedef struct PBVHTriBuf { void *vertmap; # endif - // private field - intptr_t *loops; - int totloop, mat_nr; float min[3], max[3]; } PBVHTriBuf; #else diff --git a/source/blender/blenkernel/intern/customdata.cc b/source/blender/blenkernel/intern/customdata.cc index aade96b82ea..6255574e63e 100644 --- a/source/blender/blenkernel/intern/customdata.cc +++ b/source/blender/blenkernel/intern/customdata.cc @@ -4336,67 +4336,45 @@ void CustomData_bmesh_copy_data_exclude_by_type(const CustomData *source, } } - /* copies a layer at a time */ - int dest_i = 0; - for (int src_i = 0; src_i < source->totlayer; src_i++) { - if (source->layers[src_i].flag & CD_FLAG_ELEM_NOCOPY) { - continue; - } + /* The old code broke if the ordering differed between two customdata sets. + * Led to disappearing face sets. + */ + blender::Set donelayers; - /* 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 < source->layers[src_i].type) { - if (was_new) { - CustomData_bmesh_set_default_n(dest, dest_block, dest_i); - } - dest_i++; - } - - while (dest_i < dest->totlayer && dest->layers[dest_i].type == source->layers[src_i].type) { - if (STREQ(dest->layers[dest_i].name, source->layers[src_i].name)) { - break; - } - else if (was_new) { - CustomData_bmesh_set_default_n(dest, dest_block, dest_i); - } - dest_i++; - } - - /* if there are no more dest layers, we're done */ - if (dest_i >= dest->totlayer) { - return; - } - - /* if we found a matching layer, copy the data */ - if (dest->layers[dest_i].type == source->layers[src_i].type && - STREQ(dest->layers[dest_i].name, source->layers[src_i].name)) + for (const CustomDataLayer &layer_src : + blender::Span(source->layers, source->totlayer)) + { + for (CustomDataLayer &layer_dst : + blender::MutableSpan(dest->layers, dest->totlayer)) { - if (no_mask || ((CD_TYPE_AS_MASK(dest->layers[dest_i].type) & mask_exclude) == 0)) { - const void *src_data = POINTER_OFFSET(src_block, source->layers[src_i].offset); - void *dest_data = POINTER_OFFSET(*dest_block, dest->layers[dest_i].offset); - const LayerTypeInfo *typeInfo = layerType_getInfo( - eCustomDataType(source->layers[src_i].type)); - if (typeInfo->copy) { - typeInfo->copy(src_data, dest_data, 1); - } - else { - memcpy(dest_data, src_data, typeInfo->size); - } + bool ok = !(layer_src.flag & CD_FLAG_ELEM_NOCOPY); + ok = ok && !(layer_dst.flag & mask_exclude); + ok = ok && layer_src.type == layer_dst.type; + ok = ok && STREQ(layer_src.name, layer_dst.name); + + if (!ok) { + continue; } - /* 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++; + donelayers.add(&layer_dst); + const void *src_data = POINTER_OFFSET(src_block, layer_src.offset); + void *dest_data = POINTER_OFFSET(*dest_block, layer_dst.offset); + const LayerTypeInfo *typeInfo = layerType_getInfo(eCustomDataType(layer_src.type)); + if (typeInfo->copy) { + typeInfo->copy(src_data, dest_data, 1); + } + else { + memcpy(dest_data, src_data, typeInfo->size); + } } } - /* Initialize the remaining layers if dest_block was newly allocated. */ - while (was_new && dest_i < dest->totlayer) { - CustomData_bmesh_set_default_n(dest, dest_block, dest_i); - dest_i++; + for (CustomDataLayer &layer_dst : + blender::MutableSpan(dest->layers, dest->totlayer)) + { + if (was_new && !donelayers.contains(&layer_dst)) { + CustomData_bmesh_set_default_n(dest, dest_block, int(&layer_dst - dest->layers)); + } } } diff --git a/source/blender/blenkernel/intern/dyntopo.cc b/source/blender/blenkernel/intern/dyntopo.cc index 642410825a7..20d974eebcf 100644 --- a/source/blender/blenkernel/intern/dyntopo.cc +++ b/source/blender/blenkernel/intern/dyntopo.cc @@ -70,7 +70,7 @@ static void edge_queue_create_local(EdgeQueueContext *eq_ctx, PBVH *pbvh, PBVHTopologyUpdateMode local_mode); -ATTR_NO_OPT static void surface_smooth_v_safe( +static void surface_smooth_v_safe( SculptSession *ss, PBVH *pbvh, BMVert *v, float fac, bool reproject_cdata) { float co[3]; @@ -778,8 +778,8 @@ static void unified_edge_queue_task_cb(void *__restrict userdata, #endif PBVHTriBuf *tribuf = node->tribuf; - for (int i = 0; i < node->tribuf->tottri; i++) { - PBVHTri *tri = node->tribuf->tris + i; + for (int i = 0; i < node->tribuf->tris.size(); i++) { + PBVHTri *tri = &node->tribuf->tris[i]; BMFace *f = (BMFace *)tri->f.i; if (f->head.hflag & facetag) { @@ -2200,7 +2200,7 @@ bool EdgeQueueContext::done() return totop == 0 || edge_heap.empty() || current_i >= max_steps; } -ATTR_NO_OPT void EdgeQueueContext::finish() +void EdgeQueueContext::finish() { while (!edge_heap.empty()) { BMEdge *e = edge_heap.pop_max(); @@ -2569,7 +2569,7 @@ void BKE_pbvh_bmesh_add_face(PBVH *pbvh, struct BMFace *f, bool log_face, bool f } namespace blender::bke::dyntopo { -ATTR_NO_OPT static void pbvh_split_edge(EdgeQueueContext *eq_ctx, BMEdge *e) +static void pbvh_split_edge(EdgeQueueContext *eq_ctx, BMEdge *e) { PBVH *pbvh = eq_ctx->pbvh; BMesh *bm = pbvh->header.bm; @@ -3907,7 +3907,7 @@ void BKE_sculpt_reproject_cdata( snapper.snap(); return; - //XXX delete the below code after testing new snap code. + // XXX delete the below code after testing new snap code. /* Re-snap uvs. */ v = (BMVert *)vertex.i; diff --git a/source/blender/blenkernel/intern/paint.cc b/source/blender/blenkernel/intern/paint.cc index c21251c8fab..a4ef2bd4558 100644 --- a/source/blender/blenkernel/intern/paint.cc +++ b/source/blender/blenkernel/intern/paint.cc @@ -3418,7 +3418,8 @@ static bool sculpt_attr_update(Object *ob, SculptAttribute *attr) attr->data = nullptr; } else { - attr->data = cdata->layers[layer_index].data; + attr->data = CustomData_get_layer_named_for_write( + cdata, attr->proptype, attr->name, elem_num); } } @@ -3426,9 +3427,23 @@ static bool sculpt_attr_update(Object *ob, SculptAttribute *attr) if (attr->params.nocopy) { cdata->layers[layer_index].flag |= CD_FLAG_ELEM_NOCOPY; } + else { + cdata->layers[layer_index].flag &= ~CD_FLAG_ELEM_NOCOPY; + } + if (attr->params.nointerp) { cdata->layers[layer_index].flag |= CD_FLAG_ELEM_NOINTERP; } + else { + cdata->layers[layer_index].flag &= ~CD_FLAG_ELEM_NOINTERP; + } + + if (attr->params.permanent) { + cdata->layers[layer_index].flag &= ~(CD_FLAG_TEMPORARY | CD_FLAG_NOCOPY); + } + else { + cdata->layers[layer_index].flag |= CD_FLAG_TEMPORARY | CD_FLAG_NOCOPY; + } } } @@ -3549,14 +3564,16 @@ SculptAttribute *BKE_sculpt_attribute_get(struct Object *ob, if (BKE_pbvh_type(ss->pbvh) == PBVH_FACES || (BKE_pbvh_type(ss->pbvh) == PBVH_GRIDS && domain == ATTR_DOMAIN_FACE)) { - attr->data = cdata->layers[index].data; + attr->data = CustomData_get_layer_named_for_write( + cdata, attr->proptype, attr->name, totelem); } + attr->params.nointerp = cdata->layers[index].flag & CD_FLAG_ELEM_NOINTERP; + attr->params.nocopy = cdata->layers[index].flag & CD_FLAG_ELEM_NOCOPY; attr->params.permanent = !(cdata->layers[index].flag & CD_FLAG_TEMPORARY); attr->used = true; attr->domain = domain; attr->proptype = proptype; - attr->data = cdata->layers[index].data; attr->bmesh_cd_offset = cdata->layers[index].offset; attr->elem_num = totelem; attr->layer = cdata->layers + index; @@ -3584,6 +3601,7 @@ static SculptAttribute *sculpt_attribute_ensure_ex(Object *ob, if (attr) { attr->params.nocopy = params->nocopy; attr->params.nointerp = params->nointerp; + attr->params.permanent = params->permanent; sculpt_attr_update(ob, attr); @@ -3903,7 +3921,7 @@ bool BKE_sculpt_attribute_destroy(Object *ob, SculptAttribute *attr) } Mesh *me = BKE_object_get_original_mesh(ob); - + ; if (attr->simple_array) { MEM_SAFE_FREE(attr->data); } @@ -3940,13 +3958,13 @@ bool BKE_sculpt_attribute_destroy(Object *ob, SculptAttribute *attr) if (layer_i != 0) { CustomData_free_layer(cdata, attr->proptype, totelem, layer_i); } - - sculpt_attribute_update_refs(ob); } attr->data = nullptr; attr->used = false; + sculpt_attribute_update_refs(ob); + return true; } diff --git a/source/blender/blenkernel/intern/pbvh.cc b/source/blender/blenkernel/intern/pbvh.cc index 9532b90cff4..3fbf37fda46 100644 --- a/source/blender/blenkernel/intern/pbvh.cc +++ b/source/blender/blenkernel/intern/pbvh.cc @@ -1820,6 +1820,10 @@ static void pbvh_update_draw_buffer_cb(void *__restrict userdata, BKE_pbvh_get_color_layer(pbvh, me, &vcol_layer, &vcol_domain); + if (BKE_pbvh_type(pbvh) == PBVH_BMESH) { + BKE_pbvh_bmesh_check_tris(pbvh, node); + } + if (node->flag & PBVH_RebuildDrawBuffers) { PBVH_GPU_Args args; pbvh_draw_args_init(pbvh, &args, node); @@ -3828,7 +3832,7 @@ void BKE_pbvh_check_tri_areas(PBVH *pbvh, PBVHNode *node) BKE_pbvh_bmesh_check_tris(pbvh, node); } - if (!node->tribuf || !node->tribuf->tottri) { + if (!node->tribuf || !node->tribuf->tris.size()) { return; } } @@ -3882,13 +3886,11 @@ void BKE_pbvh_check_tri_areas(PBVH *pbvh, PBVHNode *node) areabuf[cur_i] = 0.0f; } - for (int i = 0; i < node->tribuf->tottri; i++) { - PBVHTri *tri = node->tribuf->tris + i; - - BMVert *v1 = (BMVert *)(node->tribuf->verts[tri->v[0]].i); - BMVert *v2 = (BMVert *)(node->tribuf->verts[tri->v[1]].i); - BMVert *v3 = (BMVert *)(node->tribuf->verts[tri->v[2]].i); - BMFace *f = (BMFace *)tri->f.i; + for (PBVHTri &tri : node->tribuf->tris) { + BMVert *v1 = (BMVert *)(node->tribuf->verts[tri.v[0]].i); + BMVert *v2 = (BMVert *)(node->tribuf->verts[tri.v[1]].i); + BMVert *v3 = (BMVert *)(node->tribuf->verts[tri.v[2]].i); + BMFace *f = (BMFace *)tri.f.i; float *areabuf = (float *)BM_ELEM_CD_GET_VOID_P(f, cd_face_area); areabuf[cur_i] += area_tri_v3(v1->co, v2->co, v3->co); diff --git a/source/blender/blenkernel/intern/pbvh_bmesh.cc b/source/blender/blenkernel/intern/pbvh_bmesh.cc index 2c6cb9ffc2a..ad2705da71d 100644 --- a/source/blender/blenkernel/intern/pbvh_bmesh.cc +++ b/source/blender/blenkernel/intern/pbvh_bmesh.cc @@ -1396,12 +1396,11 @@ bool pbvh_bmesh_node_raycast(SculptSession *ss, BKE_pbvh_bmesh_check_tris(pbvh, node); - for (int i = 0; i < node->tribuf->tottri; i++) { - PBVHTri *tri = node->tribuf->tris + i; + for (PBVHTri &tri : node->tribuf->tris) { BMVert *verts[3] = { - (BMVert *)node->tribuf->verts[tri->v[0]].i, - (BMVert *)node->tribuf->verts[tri->v[1]].i, - (BMVert *)node->tribuf->verts[tri->v[2]].i, + (BMVert *)node->tribuf->verts[tri.v[0]].i, + (BMVert *)node->tribuf->verts[tri.v[1]].i, + (BMVert *)node->tribuf->verts[tri.v[2]].i, }; float *cos[3]; @@ -1449,7 +1448,7 @@ bool pbvh_bmesh_node_raycast(SculptSession *ss, } if (r_active_face) { - *r_active_face = tri->f; + *r_active_face = tri.f; } } } @@ -1469,12 +1468,11 @@ bool BKE_pbvh_bmesh_node_raycast_detail(PBVH *pbvh, } BKE_pbvh_bmesh_check_tris(pbvh, node); - for (int i = 0; i < node->tribuf->tottri; i++) { - PBVHTri *tri = node->tribuf->tris + i; - BMVert *v1 = (BMVert *)node->tribuf->verts[tri->v[0]].i; - BMVert *v2 = (BMVert *)node->tribuf->verts[tri->v[1]].i; - BMVert *v3 = (BMVert *)node->tribuf->verts[tri->v[2]].i; - BMFace *f = (BMFace *)tri->f.i; + for (PBVHTri &tri : node->tribuf->tris) { + BMVert *v1 = (BMVert *)node->tribuf->verts[tri.v[0]].i; + BMVert *v2 = (BMVert *)node->tribuf->verts[tri.v[1]].i; + BMVert *v3 = (BMVert *)node->tribuf->verts[tri.v[2]].i; + BMFace *f = (BMFace *)tri.f.i; if (pbvh_poly_hidden(pbvh, f)) { continue; @@ -1513,17 +1511,16 @@ bool pbvh_bmesh_node_nearest_to_ray(SculptSession *ss, BKE_pbvh_bmesh_check_tris(pbvh, node); PBVHTriBuf *tribuf = node->tribuf; - for (int i = 0; i < tribuf->tottri; i++) { - PBVHTri *tri = tribuf->tris + i; - BMFace *f = (BMFace *)tri->f.i; + for (PBVHTri &tri : tribuf->tris) { + BMFace *f = (BMFace *)tri.f.i; if (pbvh_poly_hidden(pbvh, f)) { continue; } - BMVert *v1 = (BMVert *)tribuf->verts[tri->v[0]].i; - BMVert *v2 = (BMVert *)tribuf->verts[tri->v[1]].i; - BMVert *v3 = (BMVert *)tribuf->verts[tri->v[2]].i; + BMVert *v1 = (BMVert *)tribuf->verts[tri.v[0]].i; + BMVert *v2 = (BMVert *)tribuf->verts[tri.v[1]].i; + BMVert *v3 = (BMVert *)tribuf->verts[tri.v[2]].i; float *co1, *co2, *co3; @@ -2402,7 +2399,7 @@ void BKE_pbvh_build_bmesh(PBVH *pbvh, BMLoop *l_first = BM_FACE_FIRST_LOOP(f); BMLoop *l_iter = l_first; - // check for currupted faceset + /* Check for currupted faceset. */ if (pbvh->cd_faceset_offset != -1 && BM_ELEM_CD_GET_INT(f, pbvh->cd_faceset_offset) == 0) { BM_ELEM_CD_SET_INT(f, pbvh->cd_faceset_offset, 1); } @@ -2578,25 +2575,6 @@ bool remesh_topology_nodes(blender::bke::dyntopo::BrushTester *brush_tester, } } // namespace blender::bke::dyntopo -static void pbvh_free_tribuf(PBVHTriBuf *tribuf) -{ - MEM_SAFE_FREE(tribuf->verts); - MEM_SAFE_FREE(tribuf->tris); - MEM_SAFE_FREE(tribuf->loops); - MEM_SAFE_FREE(tribuf->edges); - - tribuf->verts = nullptr; - tribuf->tris = nullptr; - tribuf->loops = nullptr; - tribuf->edges = nullptr; - - tribuf->totloop = tribuf->tottri = tribuf->totedge = tribuf->totvert = 0; - - tribuf->verts_size = 0; - tribuf->tris_size = 0; - tribuf->edges_size = 0; -} - PBVHTriBuf *BKE_pbvh_bmesh_get_tris(PBVH *pbvh, PBVHNode *node) { BKE_pbvh_bmesh_check_tris(pbvh, node); @@ -2607,90 +2585,26 @@ PBVHTriBuf *BKE_pbvh_bmesh_get_tris(PBVH *pbvh, PBVHNode *node) void BKE_pbvh_bmesh_free_tris(PBVH * /*pbvh*/, PBVHNode *node) { if (node->tribuf) { - pbvh_free_tribuf(node->tribuf); MEM_delete(node->tribuf); node->tribuf = nullptr; } if (node->tri_buffers) { - for (PBVHTriBuf &tri : *node->tri_buffers) { - pbvh_free_tribuf(&tri); - } - MEM_delete>(node->tri_buffers); node->tri_buffers = nullptr; } } -BLI_INLINE PBVHTri *pbvh_tribuf_add_tri(PBVHTriBuf *tribuf) -{ - tribuf->tottri++; - - if (tribuf->tottri >= tribuf->tris_size) { - size_t newsize = (size_t)32 + (size_t)tribuf->tris_size + (size_t)(tribuf->tris_size >> 1); - - if (!tribuf->tris) { - tribuf->tris = MEM_cnew_array(newsize, "tribuf tris"); - } - else { - tribuf->tris = (PBVHTri *)MEM_reallocN_id( - (void *)tribuf->tris, sizeof(*tribuf->tris) * newsize, "tribuf tris"); - } - - tribuf->tris_size = newsize; - } - - return tribuf->tris + tribuf->tottri - 1; -} - BLI_INLINE void pbvh_tribuf_add_vert(PBVHTriBuf *tribuf, PBVHVertRef vertex, BMLoop *l) { - tribuf->totvert++; - tribuf->totloop++; - - if (tribuf->totvert >= tribuf->verts_size) { - size_t newsize = (size_t)32 + (size_t)(tribuf->verts_size << 1); - - if (!tribuf->verts) { - tribuf->verts = MEM_cnew_array(newsize, "tribuf verts"); - tribuf->loops = MEM_cnew_array(newsize, "tribuf loops"); - } - else { - tribuf->verts = (PBVHVertRef *)MEM_reallocN_id( - (void *)tribuf->verts, sizeof(*tribuf->verts) * newsize, "tribuf verts"); - tribuf->loops = (intptr_t *)MEM_reallocN_id( - (void *)tribuf->loops, sizeof(*tribuf->loops) * newsize, "tribuf loops"); - } - - tribuf->verts_size = newsize; - } - - tribuf->verts[tribuf->totvert - 1] = vertex; - tribuf->loops[tribuf->totloop - 1] = (uintptr_t)l; + tribuf->verts.append(vertex); + tribuf->loops.append((uintptr_t)l); } BLI_INLINE void pbvh_tribuf_add_edge(PBVHTriBuf *tribuf, int v1, int v2) { - tribuf->totedge++; - - if (tribuf->totedge >= tribuf->edges_size) { - size_t newsize = (size_t)32 + (size_t)(tribuf->edges_size << 1); - - if (!tribuf->edges) { - tribuf->edges = MEM_cnew_array(2ULL * newsize, "tribuf edges"); - } - else { - tribuf->edges = (int *)MEM_reallocN_id( - (void *)tribuf->edges, sizeof(*tribuf->edges) * 2ULL * newsize, "tribuf edges"); - } - - tribuf->edges_size = newsize; - } - - int i = (tribuf->totedge - 1) * 2; - - tribuf->edges[i] = v1; - tribuf->edges[i + 1] = v2; + tribuf->edges.append(v1); + tribuf->edges.append(v2); } void pbvh_bmesh_check_other_verts(PBVHNode *node) @@ -2720,30 +2634,24 @@ void pbvh_bmesh_check_other_verts(PBVHNode *node) static void pbvh_init_tribuf(PBVHNode * /*node*/, PBVHTriBuf *tribuf) { - tribuf->tottri = 0; - tribuf->tris_size = 0; - tribuf->verts_size = 0; tribuf->mat_nr = 0; - tribuf->tottri = 0; - tribuf->totvert = 0; - tribuf->totloop = 0; - tribuf->totedge = 0; - tribuf->edges = nullptr; - tribuf->verts = nullptr; - tribuf->tris = nullptr; - tribuf->loops = nullptr; + tribuf->edges.clear(); + tribuf->verts.clear(); + tribuf->tris.clear(); + tribuf->loops.clear(); } static uintptr_t tri_loopkey(BMLoop *l, int mat_nr, int cd_fset, int cd_uvs[], int totuv) { - uintptr_t key = (uintptr_t)mat_nr; + uintptr_t key = ((uintptr_t)l->v) << 12ULL; + int i = 0; - key ^= (uintptr_t)l->v; + key ^= (uintptr_t)BLI_hash_int(mat_nr + i++); if (cd_fset >= 0) { // key ^= (uintptr_t)BLI_hash_int(BM_ELEM_CD_GET_INT(l->f, cd_fset)); - key ^= (uintptr_t)BM_ELEM_CD_GET_INT(l->f, cd_fset); + key ^= (uintptr_t)BLI_hash_int(BM_ELEM_CD_GET_INT(l->f, cd_fset) + i++); } for (int i = 0; i < totuv; i++) { @@ -2754,11 +2662,12 @@ static uintptr_t tri_loopkey(BMLoop *l, int mat_nr, int cd_fset, int cd_uvs[], i uintptr_t y = (uintptr_t)(luv[1] * snap); uintptr_t key2 = y * snap + x; - key ^= key2; + key ^= BLI_hash_int(key2 + i++); } return key; } + /* In order to perform operations on the original node coordinates * (currently just raycast), store the node's triangles and vertices. * @@ -2771,6 +2680,8 @@ bool BKE_pbvh_bmesh_check_tris(PBVH *pbvh, PBVHNode *node) return false; } + node->flag |= PBVH_RebuildDrawBuffers | PBVH_UpdateDrawBuffers; + int totuv = CustomData_number_of_layers(&bm->ldata, CD_PROP_FLOAT2); int *cd_uvs = (int *)BLI_array_alloca(cd_uvs, totuv); @@ -2809,16 +2720,16 @@ bool BKE_pbvh_bmesh_check_tris(PBVH *pbvh, PBVHNode *node) continue; } - /* Clear edgeflag for building edge indices later. */ + /* Set edgeflag for building edge indices later. */ BMLoop *l = f->l_first; do { - l->e->head.hflag &= ~edgeflag; + l->e->head.hflag |= edgeflag; } while ((l = l->next) != f->l_first); const int mat_nr = f->mat_nr; if (mat_map[mat_nr] == -1) { - PBVHTriBuf _tribuf = {0}; + PBVHTriBuf _tribuf = {}; mat_map[mat_nr] = tribufs->size(); @@ -2834,93 +2745,84 @@ bool BKE_pbvh_bmesh_check_tris(PBVH *pbvh, PBVHNode *node) BM_face_calc_tessellation(f, true, loops.data(), (uint(*)[3])loops_idx.data()); - for (int i = 0; i < tottri; i++) { - PBVHTri *tri = pbvh_tribuf_add_tri(node->tribuf); - PBVHTriBuf *mat_tribuf = &(*tribufs)[mat_map[mat_nr]]; - PBVHTri *mat_tri = pbvh_tribuf_add_tri(mat_tribuf); + auto add_tri_verts = + [cd_uvs, totuv, pbvh, &min, &max]( + PBVHTriBuf *tribuf, PBVHTri &tri, BMLoop *l, BMLoop *l2, int mat_nr, int j) { + int tri_v; - tri->eflag = mat_tri->eflag = 0; + if ((l->f->head.hflag & BM_ELEM_SMOOTH)) { + void *loopkey = reinterpret_cast( + tri_loopkey(l, mat_nr, pbvh->cd_faceset_offset, cd_uvs, totuv)); + + tri_v = tribuf->vertmap.lookup_or_add(loopkey, tribuf->verts.size()); + } + else { /* Flat shaded faces. */ + tri_v = tribuf->verts.size(); + } + + /* Newly added to the set? */ + if (tri_v == tribuf->verts.size()) { + PBVHVertRef sv = {(intptr_t)l->v}; + minmax_v3v3_v3(min, max, l->v->co); + pbvh_tribuf_add_vert(tribuf, sv, l); + } + + tri.v[j] = (intptr_t)tri_v; + tri.l[j] = (intptr_t)l; + }; + + /* Build index buffers. */ + for (int i = 0; i < tottri; i++) { + PBVHTriBuf *mat_tribuf = &(*tribufs)[mat_map[mat_nr]]; + + node->tribuf->tris.resize(node->tribuf->tris.size() + 1); + mat_tribuf->tris.resize(mat_tribuf->tris.size() + 1); + + PBVHTri &tri = node->tribuf->tris.last(); + PBVHTri &mat_tri = mat_tribuf->tris.last(); + + tri.eflag = mat_tri.eflag = 0; for (int j = 0; j < 3; j++) { - // BMLoop *l0 = loops[loops_idx[i][(j + 2) % 3]]; - BMLoop *l = loops[loops_idx[i][j]]; + BMLoop *l1 = loops[loops_idx[i][j]]; BMLoop *l2 = loops[loops_idx[i][(j + 1) % 3]]; - BMEdge *e = BM_edge_exists(l->v, l2->v); - - if (e) { - tri->eflag |= 1 << j; - mat_tri->eflag |= 1 << j; - } - - void *loopkey = reinterpret_cast( - tri_loopkey(l, mat_nr, pbvh->cd_faceset_offset, cd_uvs, totuv)); - - int &tri_v = node->tribuf->vertmap.lookup_or_add(loopkey, node->tribuf->totvert); - - /* Newly added to the set? */ - if (tri_v == node->tribuf->totvert) { - PBVHVertRef sv = {(intptr_t)l->v}; - minmax_v3v3_v3(min, max, l->v->co); - pbvh_tribuf_add_vert(node->tribuf, sv, l); - } - - tri->v[j] = (intptr_t)tri_v; - tri->l[j] = (intptr_t)l; - - int &mattri_v = mat_tribuf->vertmap.lookup_or_add(loopkey, mat_tribuf->totvert); - - /* Newly added to the set? */ - if (mattri_v == mat_tribuf->totvert) { - PBVHVertRef sv = {(intptr_t)l->v}; - minmax_v3v3_v3(min, max, l->v->co); - pbvh_tribuf_add_vert(mat_tribuf, sv, l); - } - - mat_tri->v[j] = (intptr_t)mattri_v; - mat_tri->l[j] = (intptr_t)l; + add_tri_verts(node->tribuf, tri, l1, l2, mat_nr, j); + add_tri_verts(mat_tribuf, mat_tri, l1, l2, mat_nr, j); } - copy_v3_v3(tri->no, f->no); - copy_v3_v3(mat_tri->no, f->no); - tri->f.i = (intptr_t)f; - mat_tri->f.i = (intptr_t)f; - } - } + for (int j = 0; j < 3; j++) { + BMLoop *l1 = loops[loops_idx[i][j]]; + BMLoop *l2 = loops[loops_idx[i][(j + 1) % 3]]; + BMEdge *e = nullptr; - for (BMFace *f : *node->bm_faces) { - if (pbvh_poly_hidden(pbvh, f)) { - continue; - } + if (e = BM_edge_exists(l1->v, l2->v)) { + tri.eflag |= 1 << j; - int mat_nr = f->mat_nr; - PBVHTriBuf *mat_tribuf = &(*tribufs)[mat_map[mat_nr]]; - - BMLoop *l = f->l_first; - do { - if (l->e->head.hflag & edgeflag) { - continue; + if (e->head.hflag & edgeflag) { + e->head.hflag &= ~edgeflag; + pbvh_tribuf_add_edge(node->tribuf, tri.v[j], tri.v[(j + 1) % 3]); + pbvh_tribuf_add_edge(mat_tribuf, tri.v[j], tri.v[(j + 1) % 3]); + } + } } - l->e->head.hflag |= edgeflag; - - int v1 = node->tribuf->vertmap.lookup_default((void *)l->e->v1, 0); - int v2 = node->tribuf->vertmap.lookup_default((void *)l->e->v2, 0); - - pbvh_tribuf_add_edge(node->tribuf, v1, v2); - - v1 = mat_tribuf->vertmap.lookup_default((void *)l->e->v1, 0); - v2 = mat_tribuf->vertmap.lookup_default((void *)l->e->v2, 0); - - pbvh_tribuf_add_edge(mat_tribuf, v1, v2); - } while ((l = l->next) != f->l_first); + copy_v3_v3(tri.no, f->no); + copy_v3_v3(mat_tri.no, f->no); + tri.f.i = (intptr_t)f; + mat_tri.f.i = (intptr_t)f; + } } + /* + void *loopkey = reinterpret_cast( + tri_loopkey(l, mat_nr, pbvh->cd_faceset_offset, cd_uvs, totuv)); + */ bm->elem_index_dirty |= BM_VERT; node->tri_buffers = tribufs; - if (node->tribuf->totvert) { + if (node->tribuf->verts.size()) { copy_v3_v3(node->tribuf->min, min); copy_v3_v3(node->tribuf->max, max); } @@ -3923,22 +3825,20 @@ void BKE_pbvh_bmesh_from_saved_indices(PBVH *pbvh) break; } - for (int k = 0; k < tribuf->totvert; k++) { + for (int k = 0; k < tribuf->verts.size(); k++) { tribuf->verts[k].i = (intptr_t)pbvh->header.bm->vtable[tribuf->verts[k].i]; } - for (int k = 0; k < tribuf->totloop; k++) { + for (int k = 0; k < tribuf->loops.size(); k++) { tribuf->loops[k] = (uintptr_t)ltable[tribuf->loops[k]]; } - for (int k = 0; k < tribuf->tottri; k++) { - PBVHTri *tri = tribuf->tris + k; - + for (PBVHTri &tri : tribuf->tris) { for (int l = 0; l < 3; l++) { - tri->l[l] = (uintptr_t)ltable[tri->l[l]]; + tri.l[l] = (uintptr_t)ltable[tri.l[l]]; } - tri->f.i = (intptr_t)pbvh->header.bm->ftable[tri->f.i]; + tri.f.i = (intptr_t)pbvh->header.bm->ftable[tri.f.i]; } } @@ -4189,7 +4089,7 @@ static void corner_interp(CustomDataLayer *layer, /* Interpolates loops surrounding a vertex, splitting any UV map by * island as appropriate and enforcing proper boundary conditions. */ -ATTR_NO_OPT void interp_face_corners( +void interp_face_corners( PBVH *pbvh, PBVHVertRef vertex, Span loops, Span ws, float factor) { if (BKE_pbvh_type(pbvh) != PBVH_BMESH) { @@ -4292,7 +4192,7 @@ ATTR_NO_OPT void interp_face_corners( } /* Snap. */ - //corner_snap.snap(); + // corner_snap.snap(); } } } // namespace blender::bke::sculpt diff --git a/source/blender/bmesh/intern/bmesh_collapse.hh b/source/blender/bmesh/intern/bmesh_collapse.hh index 50b5957f1c1..4063990df53 100644 --- a/source/blender/bmesh/intern/bmesh_collapse.hh +++ b/source/blender/bmesh/intern/bmesh_collapse.hh @@ -534,8 +534,8 @@ BMVert *join_vert_kill_edge(BMesh *bm, /* Destroy any valence-3 verts that might turn into non-manifold "fins." */ bmesh_kernel_check_val3_vert(bm, e, callbacks); - Set es; - Set fs; + Set es; + Set fs; const int dup_tag = _FLAG_OVERLAP; diff --git a/source/blender/draw/intern/draw_pbvh.cc b/source/blender/draw/intern/draw_pbvh.cc index 9de33eeaeb2..247b1f20a39 100644 --- a/source/blender/draw/intern/draw_pbvh.cc +++ b/source/blender/draw/intern/draw_pbvh.cc @@ -53,6 +53,8 @@ #define MAX_PBVH_BATCH_KEY 512 #define MAX_PBVH_VBOS 16 +#define USE_BMESH_INDEX_BUFFERS + using blender::char3; using blender::float2; using blender::float3; @@ -213,7 +215,7 @@ struct PBVHBatches { break; } case PBVH_BMESH: { - count = args->tribuf->tottri; + count = args->tribuf->tris.size(); } } @@ -223,10 +225,6 @@ struct PBVHBatches { PBVHBatches(PBVH_GPU_Args *args) { faces_count = count_faces(args); - - if (args->pbvh_type == PBVH_BMESH) { - tris_count = faces_count; - } } ~PBVHBatches() @@ -314,13 +312,37 @@ struct PBVHBatches { PBVHBatch &ensure_batch(PBVHAttrReq *attrs, int attrs_num, PBVH_GPU_Args *args, - bool do_coarse_grids) + bool do_coarse_grids, + bool need_lines = false) { if (!has_batch(attrs, attrs_num, do_coarse_grids)) { - create_batch(attrs, attrs_num, args, do_coarse_grids); + create_batch(attrs, attrs_num, args, do_coarse_grids, need_lines); } - return batches.lookup(build_key(attrs, attrs_num, do_coarse_grids)); + PBVHBatch &batch = batches.lookup(build_key(attrs, attrs_num, do_coarse_grids)); + + if (need_lines && !lines_count) { + if (lines_index) { + GPU_INDEXBUF_DISCARD_SAFE(lines_index); + lines_index = nullptr; + } + + check_index_buffers(args, true); + + for (PBVHBatch &batch : batches.values()) { + GPU_BATCH_DISCARD_SAFE(batch.lines); + + batch.lines = GPU_batch_create( + GPU_PRIM_LINES, nullptr, do_coarse_grids ? lines_index_coarse : lines_index); + batch.lines_count = do_coarse_grids ? lines_count_coarse : lines_count; + + for (PBVHVbo &vbo : vbos) { + GPU_batch_vertbuf_add(batch.lines, vbo.vert_buf, false); + } + } + } + + return batch; } void fill_vbo_normal_faces( @@ -749,7 +771,7 @@ struct PBVHBatches { void update(PBVH_GPU_Args *args) { - check_index_buffers(args); + check_index_buffers(args, false); for (PBVHVbo &vbo : vbos) { fill_vbo(vbo, args); @@ -758,8 +780,16 @@ struct PBVHBatches { void fill_vbo_bmesh(PBVHVbo &vbo, PBVH_GPU_Args *args) { +#ifdef USE_BMESH_INDEX_BUFFERS auto foreach_bmesh = [&](std::function callback) { - for (int i : IndexRange(args->tribuf->tottri)) { + for (int i : IndexRange(args->tribuf->loops.size())) { + BMLoop *l = reinterpret_cast(args->tribuf->loops[i]); + callback(l); + } + }; +#else + auto foreach_bmesh = [&](std::function callback) { + for (int i : IndexRange(args->tribuf->tris.size())) { PBVHTri *tri = args->tribuf->tris + i; BMFace *f = reinterpret_cast(tri->f.i); @@ -772,13 +802,23 @@ struct PBVHBatches { } } }; +#endif - faces_count = tris_count = args->tribuf->tottri; +#ifdef USE_BMESH_INDEX_BUFFERS + int existing_num = GPU_vertbuf_get_vertex_len(vbo.vert_buf); + void *existing_data = GPU_vertbuf_get_data(vbo.vert_buf); + + int vert_count = args->tribuf->loops.size(); + + // printf("%s: vert_count: %d of %d possible\n", __func__, vert_count, tris_count * 3); +#else + faces_count = tris_count = args->tribuf->tris.size(); int existing_num = GPU_vertbuf_get_vertex_len(vbo.vert_buf); void *existing_data = GPU_vertbuf_get_data(vbo.vert_buf); int vert_count = tris_count * 3; +#endif if (existing_data == nullptr || existing_num != vert_count) { /* Allocate buffer if not allocated yet or size changed. */ @@ -1124,17 +1164,26 @@ struct PBVHBatches { int count = count_faces(args); if (faces_count != count) { + for (PBVHBatch &batch : batches.values()) { + GPU_BATCH_DISCARD_SAFE(batch.tris); + GPU_BATCH_DISCARD_SAFE(batch.lines); + } + for (PBVHVbo &vbo : vbos) { vbo.clear_data(); } + vbos.clear(); + batches.clear(); + GPU_INDEXBUF_DISCARD_SAFE(tri_index); GPU_INDEXBUF_DISCARD_SAFE(lines_index); GPU_INDEXBUF_DISCARD_SAFE(tri_index_coarse); GPU_INDEXBUF_DISCARD_SAFE(lines_index_coarse); tri_index = lines_index = tri_index_coarse = lines_index_coarse = nullptr; - faces_count = tris_count = count; + faces_count = count; + tris_count = lines_count = 0; } } } @@ -1209,36 +1258,61 @@ struct PBVHBatches { lines_index = GPU_indexbuf_build(&elb_lines); } - void create_index_bmesh(PBVH_GPU_Args *args) + void create_index_bmesh_lines(PBVH_GPU_Args *args) { + printf("%s: Creating line index buffer\n", __func__); + GPUIndexBufBuilder elb_lines; - GPU_indexbuf_init(&elb_lines, GPU_PRIM_LINES, tris_count * 3 * 2, INT_MAX); + GPU_indexbuf_init(&elb_lines, GPU_PRIM_LINES, args->tribuf->edges.size(), INT_MAX); - int v_index = 0; - lines_count = 0; - - for (int i : IndexRange(args->tribuf->tottri)) { - PBVHTri *tri = args->tribuf->tris + i; - - if (tri->eflag & 1) { - GPU_indexbuf_add_line_verts(&elb_lines, v_index, v_index + 1); - lines_count++; - } - - if (tri->eflag & 2) { - GPU_indexbuf_add_line_verts(&elb_lines, v_index + 1, v_index + 2); - lines_count++; - } - - if (tri->eflag & 4) { - GPU_indexbuf_add_line_verts(&elb_lines, v_index + 2, v_index); - lines_count++; - } - - v_index += 3; + int edges_size = args->tribuf->edges.size(); + for (int i = 0; i < edges_size; i += 2) { + int v1 = args->tribuf->edges[i], v2 = args->tribuf->edges[i + 1]; + GPU_indexbuf_add_line_verts(&elb_lines, v1, v2); } lines_index = GPU_indexbuf_build(&elb_lines); + lines_count = args->tribuf->edges.size() * 2; + } + + void create_index_bmesh_faces(PBVH_GPU_Args *args) + { + GPUIndexBufBuilder elb_tris; + GPU_indexbuf_init(&elb_tris, GPU_PRIM_TRIS, args->tribuf->tris.size(), args->tribuf->verts.size()); + needs_tri_index = true; + + for (int i = 0; i < args->tribuf->tris.size(); i++) { + PBVHTri *tri = &args->tribuf->tris[i]; + GPU_indexbuf_add_tri_verts(&elb_tris, tri->v[0], tri->v[1], tri->v[2]); + } + + tris_count = args->tribuf->tris.size(); + tri_index = GPU_indexbuf_build(&elb_tris); + } + + void create_index_bmesh(PBVH_GPU_Args *args, bool need_lines) + { + if (!tri_index) { + create_index_bmesh_faces(args); + } + + if (need_lines && !lines_count && lines_index) { + GPU_INDEXBUF_DISCARD_SAFE(lines_index); + lines_index = nullptr; + } + + if (!lines_index) { + if (need_lines) { + create_index_bmesh_lines(args); + } + else { + GPUIndexBufBuilder elb_lines; + GPU_indexbuf_init(&elb_lines, GPU_PRIM_LINES, 0, INT_MAX); + + lines_index = GPU_indexbuf_build(&elb_lines); + lines_count = 0; + } + } } void create_index_grids(PBVH_GPU_Args *args, bool do_coarse) @@ -1410,14 +1484,14 @@ struct PBVHBatches { } } - void create_index(PBVH_GPU_Args *args) + void create_index(PBVH_GPU_Args *args, bool need_lines) { switch (args->pbvh_type) { case PBVH_FACES: create_index_faces(args); break; case PBVH_BMESH: - create_index_bmesh(args); + create_index_bmesh(args, need_lines); break; case PBVH_GRIDS: create_index_grids(args, false); @@ -1438,22 +1512,26 @@ struct PBVHBatches { batch.tris->flag |= GPU_BATCH_DIRTY; } - if (lines_index) { + if (batch.lines && lines_index) { GPU_batch_elembuf_set(batch.lines, lines_index, false); } } } - void check_index_buffers(PBVH_GPU_Args *args) + void check_index_buffers(PBVH_GPU_Args *args, bool need_lines) { - if (!lines_index) { - create_index(args); + if (!tri_index || (need_lines && !lines_index)) { + create_index(args, need_lines); } } - void create_batch(PBVHAttrReq *attrs, int attrs_num, PBVH_GPU_Args *args, bool do_coarse_grids) + void create_batch(PBVHAttrReq *attrs, + int attrs_num, + PBVH_GPU_Args *args, + bool do_coarse_grids, + bool need_lines) { - check_index_buffers(args); + check_index_buffers(args, need_lines); PBVHBatch batch; @@ -1469,6 +1547,9 @@ struct PBVHBatches { GPU_PRIM_LINES, nullptr, do_coarse_grids ? lines_index_coarse : lines_index); batch.lines_count = do_coarse_grids ? lines_count_coarse : lines_count; } + else { + batch.lines_count = 0; + } for (int i : IndexRange(attrs_num)) { PBVHAttrReq *attr = attrs + i; @@ -1543,7 +1624,7 @@ GPUBatch *DRW_pbvh_lines_get(PBVHBatches *batches, { do_coarse_grids &= args->pbvh_type == PBVH_GRIDS; - PBVHBatch &batch = batches->ensure_batch(attrs, attrs_num, args, do_coarse_grids); + PBVHBatch &batch = batches->ensure_batch(attrs, attrs_num, args, do_coarse_grids, true); *r_prim_count = batch.lines_count; diff --git a/source/blender/editors/sculpt_paint/sculpt.cc b/source/blender/editors/sculpt_paint/sculpt.cc index f960ac84a01..1978f362097 100644 --- a/source/blender/editors/sculpt_paint/sculpt.cc +++ b/source/blender/editors/sculpt_paint/sculpt.cc @@ -2456,11 +2456,10 @@ static void calc_area_normal_and_center_task_cb(void *__restrict userdata, if (use_original && data->has_bm_orco) { PBVHTriBuf *tribuf = BKE_pbvh_bmesh_get_tris(ss->pbvh, data->nodes[n]); - for (int i = 0; i < tribuf->tottri; i++) { - PBVHTri *tri = tribuf->tris + i; - PBVHVertRef v1 = tribuf->verts[tri->v[0]]; - PBVHVertRef v2 = tribuf->verts[tri->v[1]]; - PBVHVertRef v3 = tribuf->verts[tri->v[2]]; + for (PBVHTri &tri : tribuf->tris) { + PBVHVertRef v1 = tribuf->verts[tri.v[0]]; + PBVHVertRef v2 = tribuf->verts[tri.v[1]]; + PBVHVertRef v3 = tribuf->verts[tri.v[2]]; const float *co_tri[3] = { SCULPT_vertex_origco_get(ss, v1), diff --git a/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc b/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc index 042d30458bb..4c42e862a3b 100644 --- a/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc +++ b/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc @@ -290,10 +290,10 @@ static void SCULPT_dynamic_topology_disable_ex( Mesh *me = static_cast(ob->data); /* Destroy temporary layers. */ - BKE_sculpt_attribute_destroy_temporary_all(ob); + //BKE_sculpt_attribute_destroy_temporary_all(ob); if (ss->attrs.dyntopo_node_id_vertex) { - BKE_sculpt_attribute_destroy(ob, ss->attrs.dyntopo_node_id_vertex); + //BKE_sculpt_attribute_destroy(ob, ss->attrs.dyntopo_node_id_vertex); } if (ss->attrs.dyntopo_node_id_face) { diff --git a/source/blender/editors/sculpt_paint/sculpt_face_set.cc b/source/blender/editors/sculpt_paint/sculpt_face_set.cc index e6d5686541d..714d9d2f3f2 100644 --- a/source/blender/editors/sculpt_paint/sculpt_face_set.cc +++ b/source/blender/editors/sculpt_paint/sculpt_face_set.cc @@ -227,6 +227,11 @@ static void do_draw_face_sets_brush_task_cb_ex(void *__restrict userdata, } } BKE_pbvh_face_iter_end(fd); + + if (changed) { + BKE_pbvh_vert_tag_update_normal_triangulation(data->nodes[n]); + BKE_pbvh_node_mark_rebuild_draw(data->nodes[n]); + } } static void do_relax_face_sets_brush_task_cb_ex(void *__restrict userdata, -- 2.30.2 From ee4a37f38b67b5de3aca39354af8b27d98fb74ed Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Thu, 8 Jun 2023 14:49:06 -0700 Subject: [PATCH 166/279] temp-sculpt-dyntopo: Cleanup sculpt boundary code * Cleaned up inconsistent function naming. * Moved most pbvh api functions into the blender::bke::pbvh namespace. * Edge boundary flags are now cached in an attribute like vertex boundary flags are. * UV boundary flags are now only calculated when requested (and preserved during dyntopo remeshing). * UV snap limit is now calculated automatically, from 1/10th of the average uv edge length around a vertex. --- source/blender/blenkernel/BKE_paint.h | 106 ++- source/blender/blenkernel/BKE_pbvh.h | 93 ++- source/blender/blenkernel/BKE_sculpt.hh | 16 +- .../blender/blenkernel/intern/customdata.cc | 2 +- source/blender/blenkernel/intern/dyntopo.cc | 48 +- .../blenkernel/intern/dyntopo_collapse.cc | 57 +- source/blender/blenkernel/intern/paint.cc | 573 ++++++++++++--- source/blender/blenkernel/intern/pbvh.cc | 469 +++++++++++- .../blender/blenkernel/intern/pbvh_bmesh.cc | 676 +++++------------- .../blender/blenkernel/intern/pbvh_intern.hh | 63 +- source/blender/bmesh/intern/bmesh_collapse.hh | 8 + source/blender/draw/intern/draw_pbvh.cc | 2 - .../editors/sculpt_paint/paint_mask.cc | 1 + source/blender/editors/sculpt_paint/sculpt.cc | 33 +- .../editors/sculpt_paint/sculpt_api.cc | 311 ++------ .../editors/sculpt_paint/sculpt_dyntopo.cc | 11 +- .../editors/sculpt_paint/sculpt_smooth.cc | 2 +- .../editors/sculpt_paint/sculpt_undo.cc | 4 +- source/blender/makesdna/DNA_scene_enums.h | 3 +- 19 files changed, 1536 insertions(+), 942 deletions(-) diff --git a/source/blender/blenkernel/BKE_paint.h b/source/blender/blenkernel/BKE_paint.h index 631112f6dab..1da8e18bc4b 100644 --- a/source/blender/blenkernel/BKE_paint.h +++ b/source/blender/blenkernel/BKE_paint.h @@ -29,6 +29,8 @@ #include "bmesh_log.h" #ifdef __cplusplus +# include + extern "C" { #endif @@ -608,9 +610,10 @@ typedef struct SculptAttributePointers { /* Sculpt utility attributes. */ SculptAttribute *stroke_id; - SculptAttribute *boundary_flags; - SculptAttribute *valence; /* CD_PROP_INT32, vert */ - SculptAttribute *flags; /* CD_PROP_INT8, vert */ + SculptAttribute *boundary_flags; /* CD_PROP_INT32, vert */ + SculptAttribute *edge_boundary_flags; /* CD_PROP_INT32, vert */ + SculptAttribute *valence; /* CD_PROP_INT32, vert */ + SculptAttribute *flags; /* CD_PROP_INT8, vert */ SculptAttribute *orig_co, *orig_no; /* CD_PROP_FLOAT3, vert */ SculptAttribute *orig_fsets; /* CD_PROP_INT32, face */ @@ -881,6 +884,7 @@ struct SculptSession { float last_grab_delta[3]; const float (*vert_normals)[3]; + blender::Span poly_normals; int last_automasking_settings_hash; uchar last_automask_stroke_id; @@ -951,7 +955,7 @@ struct BMesh *BKE_sculptsession_empty_bmesh_create(void); void BKE_sculptsession_bmesh_attr_update_internal(struct Object *ob); /* Ensures non-temporary attributes in me exist in the sculpt mesh, or vice - * versa if load_to_mesh is true. + * versa if load_to_mesh is true. */ void BKE_sculptsession_sync_attributes(struct Object *ob, struct Mesh *me, bool load_to_mesh); @@ -1138,6 +1142,36 @@ static void vertex_attr_set(const PBVHVertRef vertex, const SculptAttribute *att *vertex_attr_ptr(vertex, attr) = data; } +/* + * Get a pointer to attribute data at vertex. + * + * Example: float *persistent_co = vertex_attr_ptr(vertex, ss->attrs.persistent_co); + */ +template static T *edge_attr_ptr(const PBVHEdgeRef edge, const SculptAttribute *attr) +{ + return elem_attr_ptr(edge, attr); +} +/* + * Get attribute data at vertex. + * + * Example: float weight = vertex_attr_get(vertex, ss->attrs.automasking_factor); + */ +template static T edge_attr_get(const PBVHEdgeRef edge, const SculptAttribute *attr) +{ + return *edge_attr_ptr(edge, attr); +} + +/* + * Set attribute data at vertex. + * + * vertex_attr_set(vertex, ss->attrs.automasking_factor, 1.0f); + */ +template +static void edge_attr_set(const PBVHEdgeRef edge, const SculptAttribute *attr, T data) +{ + *edge_attr_ptr(edge, attr) = data; +} + template static T *face_attr_ptr(const PBVHFaceRef face, const SculptAttribute *attr) { return elem_attr_ptr(face, attr); @@ -1163,14 +1197,68 @@ bool get_original_vertex(SculptSession *ss, void load_all_original(Object *ob); } // namespace blender::bke::paint -BLI_INLINE void BKE_sculpt_boundary_flag_update(SculptSession *ss, PBVHVertRef vertex) +template +inline void BKE_sculpt_boundary_flag_update(SculptSession *ss, PBVHElemRef elem) { - int *flags = blender::bke::paint::vertex_attr_ptr(vertex, ss->attrs.boundary_flags); + int *flags; + + if constexpr (std::is_same_v) { + flags = blender::bke::paint::vertex_attr_ptr(elem, ss->attrs.boundary_flags); + } + else { + flags = blender::bke::paint::edge_attr_ptr(elem, ss->attrs.edge_boundary_flags); + } + *flags |= SCULPT_BOUNDARY_NEEDS_UPDATE | SCULPT_BOUNDARY_UPDATE_SHARP_ANGLE; } -void BKE_sculpt_sharp_boundary_flag_update(SculptSession *ss, - PBVHVertRef vertex, - bool update_ring = false); +template +inline void BKE_sculpt_boundary_flag_uv_update(SculptSession *ss, PBVHElemRef elem) +{ + int *flags; + + if constexpr (std::is_same_v) { + flags = blender::bke::paint::vertex_attr_ptr(elem, ss->attrs.boundary_flags); + } + else { + flags = blender::bke::paint::edge_attr_ptr(elem, ss->attrs.edge_boundary_flags); + } + + *flags |= SCULPT_BOUNDARY_UPDATE_UV; +} + +template +inline void BKE_sculpt_sharp_boundary_flag_update(SculptSession *ss, + PBVHElemRef elem, + bool update_ring = false) +{ + int *flags; + + if constexpr (std::is_same_v) { + flags = blender::bke::paint::vertex_attr_ptr(elem, ss->attrs.boundary_flags); + } + else { + flags = blender::bke::paint::edge_attr_ptr(elem, ss->attrs.edge_boundary_flags); + } + + *flags |= SCULPT_BOUNDARY_UPDATE_SHARP_ANGLE; + + if constexpr (std::is_same_v) { + if (update_ring && ss->bm) { + BMVert *v = reinterpret_cast(elem.i); + if (!v->e) { + return; + } + + BMEdge *e = v->e; + do { + PBVHVertRef vertex2 = {reinterpret_cast(BM_edge_other_vert(e, v))}; + + int *flags2 = blender::bke::paint::vertex_attr_ptr(vertex2, ss->attrs.boundary_flags); + *flags2 |= SCULPT_BOUNDARY_UPDATE_SHARP_ANGLE; + } while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e); + } + } +} #endif diff --git a/source/blender/blenkernel/BKE_pbvh.h b/source/blender/blenkernel/BKE_pbvh.h index 9ccdfd3bf67..da5898a8081 100644 --- a/source/blender/blenkernel/BKE_pbvh.h +++ b/source/blender/blenkernel/BKE_pbvh.h @@ -372,6 +372,7 @@ void BKE_pbvh_build_bmesh(PBVH *pbvh, const int cd_face_node_offset, const int cd_face_areas, const int cd_boundary_flag, + const int cd_edge_boundary, const int cd_flag, const int cd_valence, const int cd_origco, @@ -384,6 +385,7 @@ void BKE_pbvh_update_offsets(PBVH *pbvh, const int cd_face_node_offset, const int cd_face_areas, const int cd_boudnary_flags, + const int cd_edge_boundary, const int cd_flag, const int cd_valence, const int cd_origco, @@ -547,8 +549,6 @@ int BKE_pbvh_get_grid_num_faces(const PBVH *pbvh); void BKE_pbvh_check_tri_areas(PBVH *pbvh, PBVHNode *node); void BKE_pbvh_face_areas_begin(PBVH *pbvh); -// updates boundaries and valences for whole mesh -void BKE_pbvh_bmesh_on_mesh_change(PBVH *pbvh); bool BKE_pbvh_bmesh_check_valence(PBVH *pbvh, PBVHVertRef vertex); void BKE_pbvh_bmesh_update_valence(PBVH *pbvh, PBVHVertRef vertex); void BKE_pbvh_bmesh_update_all_valence(PBVH *pbvh); @@ -975,30 +975,12 @@ struct BMVert *BKE_pbvh_vert_create_bmesh( PBVHNode *BKE_pbvh_node_from_face_bmesh(PBVH *pbvh, struct BMFace *f); PBVHNode *BKE_pbvh_node_from_index(PBVH *pbvh, int node_i); -void BKE_pbvh_sharp_limit_set(PBVH *pbvh, float limit); -float BKE_pbvh_test_sharp_faces_bmesh(BMFace *f1, BMFace *f2, float limit); -void BKE_pbvh_update_vert_boundary(int cd_faceset_offset, - int cd_vert_node_offset, - int cd_face_node_offset, - int cd_vcol, - int cd_boundary_flag, - const int cd_flag, - const int cd_valence, - struct BMVert *v, - const CustomData *ldata, - const int totuv, - const bool do_uvs, - float sharp_angle_limit); - PBVHNode *BKE_pbvh_get_node_leaf_safe(PBVH *pbvh, int i); void BKE_pbvh_get_vert_face_areas(PBVH *pbvh, PBVHVertRef vertex, float *r_areas, int valence); void BKE_pbvh_set_symmetry(PBVH *pbvh, int symmetry, int boundary_symmetry); int BKE_pbvh_do_fset_symmetry(int fset, const int symflag, const float *co); -bool BKE_pbvh_check_vert_boundary(PBVH *pbvh, struct BMVert *v); - -void BKE_pbvh_update_vert_boundary_grids(PBVH *pbvh, PBVHVertRef vertex); /* Uncomment to enable PBVH NaN debugging. */ //#define PBVH_CHECK_NANS @@ -1087,7 +1069,18 @@ void BKE_pbvh_pmap_set(PBVH *pbvh, blender::GroupedSpan pmap); namespace blender::bke::pbvh { void set_flags_valence(PBVH *pbvh, uint8_t *flags, int *valence); void set_original(PBVH *pbvh, Span origco, Span origno); -void update_sharp_boundary_bmesh(BMVert *v, int cd_boundary_flag, const float sharp_angle_limit); +void update_vert_boundary_bmesh(int cd_faceset_offset, + int cd_vert_node_offset, + int cd_face_node_offset, + int cd_vcol, + int cd_boundary_flag, + const int cd_flag, + const int cd_valence, + struct BMVert *v, + const CustomData *ldata, + float sharp_angle_limit); +void update_sharp_vertex_bmesh(BMVert *v, int cd_boundary_flag, const float sharp_angle_limit); + void update_vert_boundary_faces(int *boundary_flags, const int *face_sets, const bool *hide_poly, @@ -1096,13 +1089,56 @@ void update_vert_boundary_faces(int *boundary_flags, const int *corner_verts, const int *corner_edges, blender::OffsetIndices polys, - int totpoly, const blender::GroupedSpan &pmap, PBVHVertRef vertex, const bool *sharp_edges, const bool *seam_edges, uint8_t *flags, int *valence); +void update_edge_boundary_bmesh(BMEdge *e, + int cd_faceset_offset, + int cd_edge_boundary, + const int cd_flag, + const int cd_valence, + const CustomData *ldata, + float sharp_angle_limit); +void update_edge_boundary_faces(int edge, + Span vertex_positions, + Span vertex_normals, + Span edges, + OffsetIndices polys, + Span poly_normals, + int *edge_boundary_flags, + const int *vert_boundary_flags, + const int *face_sets, + const bool *sharp_edge, + const bool *seam_edge, + const GroupedSpan &pmap, + const GroupedSpan &epmap, + const CustomData *ldata, + float sharp_angle_limit, + blender::Span corner_verts, + blender::Span corner_edges); +void update_edge_boundary_grids(int edge, + Span edges, + OffsetIndices polys, + int *edge_boundary_flags, + const int *vert_boundary_flags, + const int *face_sets, + const bool *sharp_edge, + const bool *seam_edge, + const GroupedSpan &pmap, + const GroupedSpan &epmap, + const CustomData *ldata, + SubdivCCG *subdiv_ccg, + const CCGKey *key, + float sharp_angle_limit, + blender::Span corner_verts, + blender::Span corner_edges); +void update_vert_boundary_grids(PBVH *pbvh, int vertex); + +bool check_vert_boundary(PBVH *pbvh, PBVHVertRef vertex); +bool check_edge_boundary(PBVH *pbvh, PBVHEdgeRef edge); Vector search_gather(PBVH *pbvh, BKE_pbvh_SearchCallback scb, @@ -1113,5 +1149,18 @@ Vector get_flagged_nodes(PBVH *pbvh, int flag); void set_pmap(PBVH *pbvh, blender::GroupedSpan pmap); void set_vemap(PBVH *pbvh, blender::GroupedSpan vemap); struct GroupedSpan get_pmap(PBVH *pbvh); + +void sharp_limit_set(PBVH *pbvh, float limit); +float test_sharp_faces_bmesh(BMFace *f1, BMFace *f2, float limit); +float test_sharp_faces_mesh(int f1, + int f2, + float limit, + blender::Span positions, + blender::OffsetIndices &polys, + blender::Span poly_normals, + blender::Span corner_verts); + +blender::Span get_poly_normals(const PBVH *pbvh); +void set_vert_boundary_map(PBVH *pbvh, BLI_bitmap *vert_boundary_map); } // namespace blender::bke::pbvh #endif diff --git a/source/blender/blenkernel/BKE_sculpt.hh b/source/blender/blenkernel/BKE_sculpt.hh index f5c098cc837..50224592338 100644 --- a/source/blender/blenkernel/BKE_sculpt.hh +++ b/source/blender/blenkernel/BKE_sculpt.hh @@ -18,6 +18,8 @@ #include +struct Object; + /* * Stroke ID API. This API is used to detect if * an element has already been processed for some task @@ -45,6 +47,8 @@ void BKE_sculpt_reproject_cdata( SculptSession *ss, PBVHVertRef vertex, float startco[3], float startno[3], bool do_uvs = true); namespace blender::bke::sculpt { +void sculpt_vert_boundary_ensure(Object *ob); + BLI_INLINE bool stroke_id_clear(SculptSession *ss, PBVHVertRef vertex, StrokeIDUser user) { StrokeID *id = blender::bke::paint::vertex_attr_ptr(vertex, ss->attrs.stroke_id); @@ -97,24 +101,24 @@ BLI_INLINE bool test_sculpt_flag(SculptSession *ss, PBVHVertRef vertex, uint8_t { return blender::bke::paint::vertex_attr_get(vertex, ss->attrs.flags) & flag; } + void interp_face_corners( PBVH *pbvh, PBVHVertRef vertex, Span loops, Span ws, float factor); -bool loop_is_corner(BMLoop *l, int cd_offset); +float calc_uv_snap_limit(BMLoop *l, int cd_uv); +bool loop_is_corner(BMLoop *l, int cd_uv, float limit = 0.01); +/* NotForPR: TODO: find attribute API substitute for these prop_eq helper functions. */ static bool prop_eq(float a, float b, float limit) { return std::fabs(a - b) < limit; } static bool prop_eq(float2 a, float2 b, float limit) { - return prop_eq(a[0], b[0], limit) && // - prop_eq(a[1], b[1], limit); + return prop_eq(a[0], b[0], limit) && prop_eq(a[1], b[1], limit); } static bool prop_eq(float3 a, float3 b, float limit) { - return prop_eq(a[0], b[0], limit) && // - prop_eq(a[1], b[1], limit) && // - prop_eq(a[2], b[2], limit); + return prop_eq(a[0], b[0], limit) && prop_eq(a[1], b[1], limit) && prop_eq(a[2], b[2], limit); } static bool prop_eq(float4 a, float4 b, float limit) { diff --git a/source/blender/blenkernel/intern/customdata.cc b/source/blender/blenkernel/intern/customdata.cc index 6255574e63e..1bcd9a01103 100644 --- a/source/blender/blenkernel/intern/customdata.cc +++ b/source/blender/blenkernel/intern/customdata.cc @@ -4348,7 +4348,7 @@ void CustomData_bmesh_copy_data_exclude_by_type(const CustomData *source, blender::MutableSpan(dest->layers, dest->totlayer)) { bool ok = !(layer_src.flag & CD_FLAG_ELEM_NOCOPY); - ok = ok && !(layer_dst.flag & mask_exclude); + ok = ok && (no_mask || !(layer_dst.flag & mask_exclude)); ok = ok && layer_src.type == layer_dst.type; ok = ok && STREQ(layer_src.name, layer_dst.name); diff --git a/source/blender/blenkernel/intern/dyntopo.cc b/source/blender/blenkernel/intern/dyntopo.cc index 20d974eebcf..66ecd395fb0 100644 --- a/source/blender/blenkernel/intern/dyntopo.cc +++ b/source/blender/blenkernel/intern/dyntopo.cc @@ -121,7 +121,7 @@ static void surface_smooth_v_safe( } if (pbvh_boundary_needs_update_bmesh(pbvh, v)) { - pbvh_check_vert_boundary(pbvh, v); + pbvh_check_vert_boundary_bmesh(pbvh, v); } int boundmask = SCULPTVERT_SMOOTH_BOUNDARY; @@ -345,6 +345,10 @@ static void edge_queue_insert_unified(EdgeQueueContext *eq_ctx, BMEdge *e, float static void edge_queue_insert_val34_vert(EdgeQueueContext *eq_ctx, BMVert *v) { + if (!v->e) { + return; + } + eq_ctx->used_verts.append(v); BMEdge *e = v->e; @@ -923,6 +927,8 @@ bool check_face_is_tri(PBVH *pbvh, BMFace *f) BMLoop *l = f2->l_first; do { + pbvh_boundary_update_bmesh(pbvh, l->v); + pbvh_boundary_update_bmesh(pbvh, l->e); dyntopo_add_flag(pbvh, l->v, SCULPTFLAG_NEED_VALENCE); } while ((l = l->next) != f2->l_first); @@ -942,6 +948,8 @@ bool check_face_is_tri(PBVH *pbvh, BMFace *f) BMLoop *l = f2->l_first; do { + pbvh_boundary_update_bmesh(pbvh, l->v); + pbvh_boundary_update_bmesh(pbvh, l->e); dyntopo_add_flag(pbvh, l->v, SCULPTFLAG_NEED_VALENCE); validate_edge(pbvh, l->e); } while ((l = l->next) != f2->l_first); @@ -962,6 +970,9 @@ bool check_face_is_tri(PBVH *pbvh, BMFace *f) /* Detect new edges. */ BMLoop *l = f2->l_first; do { + pbvh_boundary_update_bmesh(pbvh, l->v); + pbvh_boundary_update_bmesh(pbvh, l->e); + if (l->e->head.index == -1) { BM_log_edge_added(pbvh->header.bm, pbvh->bm_log, l->e); dyntopo_add_flag(pbvh, l->v, SCULPTFLAG_NEED_VALENCE); @@ -1110,6 +1121,7 @@ bool destroy_nonmanifold_fins(PBVH *pbvh, BMEdge *e_root) if (!(l->v->head.hflag & tag)) { l->v->head.hflag |= tag; pbvh_boundary_update_bmesh(pbvh, l->v); + pbvh_boundary_update_bmesh(pbvh, l->e); dyntopo_add_flag(pbvh, l->v, updateflag); vs.append(l->v); } @@ -1157,6 +1169,8 @@ bool destroy_nonmanifold_fins(PBVH *pbvh, BMEdge *e_root) BM_edge_kill(pbvh->header.bm, e); } else { + pbvh_boundary_update_bmesh(pbvh, e); + pbvh_boundary_update_bmesh(pbvh, e->v1); dyntopo_add_flag(pbvh, e->v1, updateflag); @@ -1176,6 +1190,8 @@ bool destroy_nonmanifold_fins(PBVH *pbvh, BMEdge *e_root) BM_vert_kill(pbvh->header.bm, v); } else { + pbvh_boundary_update_bmesh(pbvh, v->e); + pbvh_boundary_update_bmesh(pbvh, v); dyntopo_add_flag(pbvh, v, updateflag); } @@ -1632,8 +1648,8 @@ static void edge_queue_create_local(EdgeQueueContext *eq_ctx, e->head.hflag &= ~EDGE_QUEUE_FLAG; - pbvh_check_vert_boundary(pbvh, e->v1); - pbvh_check_vert_boundary(pbvh, e->v2); + pbvh_check_vert_boundary_bmesh(pbvh, e->v1); + pbvh_check_vert_boundary_bmesh(pbvh, e->v2); int boundflag1 = BM_ELEM_CD_GET_INT(e->v1, pbvh->cd_boundary_flag); int boundflag2 = BM_ELEM_CD_GET_INT(e->v2, pbvh->cd_boundary_flag); @@ -1739,7 +1755,7 @@ static bool cleanup_valence_3_4(EdgeQueueContext *ectx, PBVH *pbvh) validate_vert(pbvh, v, CHECK_VERT_ALL); check_vert_fan_are_tris(pbvh, v); - pbvh_check_vert_boundary(pbvh, v); + pbvh_check_vert_boundary_bmesh(pbvh, v); validate_vert(pbvh, v, CHECK_VERT_ALL); @@ -1801,6 +1817,10 @@ static bool cleanup_valence_3_4(EdgeQueueContext *ectx, PBVH *pbvh) pbvh_boundary_update_bmesh(pbvh, l->v); } + pbvh_boundary_update_bmesh(pbvh, l->e); + pbvh_boundary_update_bmesh(pbvh, l->next->e); + pbvh_boundary_update_bmesh(pbvh, l->prev->e); + l = l->prev->radial_next; if (l->v != v) { @@ -2597,6 +2617,8 @@ static void pbvh_split_edge(EdgeQueueContext *eq_ctx, BMEdge *e) do { pbvh_boundary_update_bmesh(pbvh, l2->v); + pbvh_boundary_update_bmesh(pbvh, l2->e); + dyntopo_add_flag(pbvh, l2->v, SCULPTFLAG_NEED_VALENCE); } while ((l2 = l2->next) != l->f->l_first); } while ((l = l->radial_next) != e->l); @@ -2608,6 +2630,10 @@ static void pbvh_split_edge(EdgeQueueContext *eq_ctx, BMEdge *e) dyntopo_add_flag(pbvh, e->v2, SCULPTFLAG_NEED_VALENCE); pbvh_boundary_update_bmesh(pbvh, e->v1); pbvh_boundary_update_bmesh(pbvh, e->v2); + pbvh_boundary_update_bmesh(pbvh, e); + + int bf1 = BM_ELEM_CD_GET_INT(e->v1, pbvh->cd_boundary_flag); + int bf2 = BM_ELEM_CD_GET_INT(e->v2, pbvh->cd_boundary_flag); BMVert *newv = BM_edge_split(bm, e, e->v1, &newe, 0.5f); @@ -2615,6 +2641,15 @@ static void pbvh_split_edge(EdgeQueueContext *eq_ctx, BMEdge *e) e->head.hflag &= ~EDGE_QUEUE_FLAG; newe->head.hflag &= ~EDGE_QUEUE_FLAG; + /* Deal with UV boundary flags. */ + BM_ELEM_CD_SET_INT(newe, pbvh->cd_edge_boundary, BM_ELEM_CD_GET_INT(e, pbvh->cd_edge_boundary)); + if ((bf1 & SCULPT_BOUNDARY_UV) && (bf2 & SCULPT_BOUNDARY_UV)) { + *BM_ELEM_CD_PTR(newv, pbvh->cd_boundary_flag) |= SCULPT_BOUNDARY_UV; + } + else { + *BM_ELEM_CD_PTR(newv, pbvh->cd_boundary_flag) &= ~SCULPT_BOUNDARY_UV; + } + BM_ELEM_CD_SET_INT(newv, pbvh->cd_vert_node_offset, DYNTOPO_NODE_NONE); BM_idmap_check_assign(pbvh->bm_idmap, reinterpret_cast(newv)); BM_log_vert_added(bm, pbvh->bm_log, newv); @@ -2626,6 +2661,7 @@ static void pbvh_split_edge(EdgeQueueContext *eq_ctx, BMEdge *e) dyntopo_add_flag(pbvh, newv, SCULPTFLAG_NEED_VALENCE | SCULPTFLAG_NEED_TRIANGULATE); pbvh_boundary_update_bmesh(pbvh, newv); + pbvh_boundary_update_bmesh(pbvh, newe); for (BMFace *f : fs) { BM_ELEM_CD_SET_INT(f, pbvh->cd_face_node_offset, DYNTOPO_NODE_NONE); @@ -2647,6 +2683,10 @@ static void pbvh_split_edge(EdgeQueueContext *eq_ctx, BMEdge *e) pbvh, l->next->next->v, SCULPTFLAG_NEED_VALENCE | SCULPTFLAG_NEED_TRIANGULATE); pbvh_boundary_update_bmesh(pbvh, l->next->next->v); + pbvh_boundary_update_bmesh(pbvh, l->next->e); + pbvh_boundary_update_bmesh(pbvh, l->next->next->e); + pbvh_boundary_update_bmesh(pbvh, l->next->next->next->e); + BM_ELEM_CD_SET_INT(f, pbvh->cd_face_node_offset, DYNTOPO_NODE_NONE); BM_idmap_check_assign(pbvh->bm_idmap, reinterpret_cast(f)); BKE_pbvh_bmesh_add_face(pbvh, f, true, false); diff --git a/source/blender/blenkernel/intern/dyntopo_collapse.cc b/source/blender/blenkernel/intern/dyntopo_collapse.cc index 2ccc68bd04b..b6c967b2c69 100644 --- a/source/blender/blenkernel/intern/dyntopo_collapse.cc +++ b/source/blender/blenkernel/intern/dyntopo_collapse.cc @@ -459,8 +459,8 @@ bool pbvh_bmesh_collapse_edge_uvs( BMesh *bm = pbvh->header.bm; bm_logstack_push(); - pbvh_check_vert_boundary(pbvh, v_conn); - pbvh_check_vert_boundary(pbvh, v_del); + pbvh_check_vert_boundary_bmesh(pbvh, v_conn); + pbvh_check_vert_boundary_bmesh(pbvh, v_del); int boundflag1 = BM_ELEM_CD_GET_INT(v_conn, pbvh->cd_boundary_flag); int boundflag2 = BM_ELEM_CD_GET_INT(v_del, pbvh->cd_boundary_flag); @@ -708,14 +708,33 @@ class DyntopoCollapseCallbacks { inline void on_vert_create(BMVert *v) { - dyntopo_add_flag(pbvh, v, SCULPTFLAG_NEED_VALENCE); - pbvh_boundary_update_bmesh(pbvh, v); check_new_elem_id(v, pbvh); + pbvh_boundary_update_bmesh(pbvh, v); + dyntopo_add_flag(pbvh, v, SCULPTFLAG_NEED_VALENCE); BM_log_vert_added(bm, pbvh->bm_log, v); } + + inline void on_vert_combine(BMVert *dest, BMVert *source) + { + /* Combine boundary flags. */ + int boundflag = BM_ELEM_CD_GET_INT(source, pbvh->cd_boundary_flag); + BM_ELEM_CD_SET_INT(dest, pbvh->cd_boundary_flag, boundflag); + } + + inline void on_edge_combine(BMEdge *dest, BMEdge *source) + { + /* Combine boundary flags. */ + int boundflag = BM_ELEM_CD_GET_INT(source, pbvh->cd_edge_boundary); + BM_ELEM_CD_SET_INT(dest, pbvh->cd_edge_boundary, boundflag); + + pbvh_boundary_update_bmesh(pbvh, dest->v1); + pbvh_boundary_update_bmesh(pbvh, dest->v2); + } + inline void on_edge_create(BMEdge *e) { check_new_elem_id(e, pbvh); + pbvh_boundary_update_bmesh(pbvh, e); BM_log_edge_added(bm, pbvh->bm_log, e); } inline void on_face_create(BMFace *f) @@ -728,6 +747,7 @@ class DyntopoCollapseCallbacks { BMLoop *l = f->l_first; do { pbvh_boundary_update_bmesh(pbvh, l->v); + pbvh_boundary_update_bmesh(pbvh, l->e); } while ((l = l->next) != f->l_first); } }; @@ -756,8 +776,7 @@ BMVert *pbvh_bmesh_collapse_edge( pbvh_bmesh_check_nodes(pbvh); - const int mupdateflag = SCULPTFLAG_NEED_VALENCE; - // updateflag |= SCULPTFLAG_NEED_TRIANGULATE; // to check for non-manifold flaps + const int updateflag = SCULPTFLAG_NEED_VALENCE; validate_edge(pbvh, e); @@ -766,13 +785,15 @@ BMVert *pbvh_bmesh_collapse_edge( pbvh_bmesh_check_nodes(pbvh); - pbvh_check_vert_boundary(pbvh, v1); - pbvh_check_vert_boundary(pbvh, v2); + pbvh_check_vert_boundary_bmesh(pbvh, v1); + pbvh_check_vert_boundary_bmesh(pbvh, v2); int boundflag1 = BM_ELEM_CD_GET_INT(v1, pbvh->cd_boundary_flag); int boundflag2 = BM_ELEM_CD_GET_INT(v2, pbvh->cd_boundary_flag); - if ((boundflag1 & SCULPT_BOUNDARY_UV) != (boundflag2 & SCULPT_BOUNDARY_UV)) { + /* Don't collapse across boundaries. */ + if ((boundflag1 & SCULPTVERT_ALL_BOUNDARY) != (boundflag2 & SCULPTVERT_ALL_BOUNDARY)) { + bm_logstack_pop(); return nullptr; } @@ -783,6 +804,7 @@ BMVert *pbvh_bmesh_collapse_edge( bool corner2 = (boundflag2 & SCULPTVERT_ALL_CORNER) || w2 >= 0.85; if (corner1 && corner2) { + bm_logstack_pop(); return nullptr; } @@ -798,14 +820,6 @@ BMVert *pbvh_bmesh_collapse_edge( v_conn = v1; } - /* Don't collapse across boundaries. */ - if (boundflag2 != 0 && - (boundflag1 & SCULPTVERT_ALL_BOUNDARY) != (boundflag2 & SCULPTVERT_ALL_BOUNDARY)) - { - bm_logstack_pop(); - return nullptr; - } - /* Needed for vert_ring_do. */ const int tag = COLLAPSE_TAG; const int facetag = COLLAPSE_FACE_TAG; @@ -880,7 +894,7 @@ BMVert *pbvh_bmesh_collapse_edge( validate_vert(pbvh, v_conn, CHECK_VERT_FACES | CHECK_VERT_NODE_ASSIGNED); pbvh_boundary_update_bmesh(pbvh, v_conn); - dyntopo_add_flag(pbvh, v_conn, mupdateflag); + dyntopo_add_flag(pbvh, v_conn, updateflag); if (!v_conn->e) { printf("%s: pbvh error, v_conn->e was null\n", __func__); @@ -898,10 +912,12 @@ BMVert *pbvh_bmesh_collapse_edge( do { BMLoop *l = e2->l; + pbvh_boundary_update_bmesh(pbvh, e2); + if (!l) { BMVert *v2 = BM_edge_other_vert(e2, v_conn); pbvh_boundary_update_bmesh(pbvh, v2); - dyntopo_add_flag(pbvh, v2, mupdateflag); + dyntopo_add_flag(pbvh, v2, updateflag); continue; } @@ -909,7 +925,8 @@ BMVert *pbvh_bmesh_collapse_edge( BMLoop *l2 = l->f->l_first; do { pbvh_boundary_update_bmesh(pbvh, l2->v); - dyntopo_add_flag(pbvh, l2->v, mupdateflag); + pbvh_boundary_update_bmesh(pbvh, l2->e); + dyntopo_add_flag(pbvh, l2->v, updateflag); } while ((l2 = l2->next) != l->f->l_first); } while ((l = l->radial_next) != e2->l); } while ((e2 = BM_DISK_EDGE_NEXT(e2, v_conn)) != v_conn->e); diff --git a/source/blender/blenkernel/intern/paint.cc b/source/blender/blenkernel/intern/paint.cc index a4ef2bd4558..f4fe5cf39ab 100644 --- a/source/blender/blenkernel/intern/paint.cc +++ b/source/blender/blenkernel/intern/paint.cc @@ -23,6 +23,7 @@ #include "DNA_view3d_types.h" #include "DNA_workspace_types.h" +#include "BLI_alloca.h" #include "BLI_array.h" #include "BLI_bitmap.h" #include "BLI_hash.h" @@ -1384,19 +1385,79 @@ bool paint_calculate_rake_rotation(UnifiedPaintSettings *ups, return ok; } -static bool sculpt_boundary_flags_ensure(Object *ob, - PBVH *pbvh, - int totvert, - bool force_update = false) +/** + * Returns pointer to a CustomData associated with a given domain, if + * one exists. If not nullptr is returned (this may happen with e.g. + * multires and #ATTR_DOMAIN_POINT). + */ +static CustomData *sculpt_get_cdata(Object *ob, eAttrDomain domain) +{ + SculptSession *ss = ob->sculpt; + + if (ss->bm) { + switch (domain) { + case ATTR_DOMAIN_POINT: + return &ss->bm->vdata; + case ATTR_DOMAIN_EDGE: + return &ss->bm->edata; + case ATTR_DOMAIN_CORNER: + return &ss->bm->ldata; + case ATTR_DOMAIN_FACE: + return &ss->bm->pdata; + default: + BLI_assert_unreachable(); + return nullptr; + } + } + else { + Mesh *me = BKE_object_get_original_mesh(ob); + + switch (domain) { + case ATTR_DOMAIN_POINT: + /* Cannot get vertex domain for multires grids. */ + if (ss->pbvh && BKE_pbvh_type(ss->pbvh) == PBVH_GRIDS) { + return nullptr; + } + + return &me->vdata; + case ATTR_DOMAIN_CORNER: + return &me->ldata; + case ATTR_DOMAIN_EDGE: + return &me->edata; + case ATTR_DOMAIN_FACE: + return &me->pdata; + default: + BLI_assert_unreachable(); + return nullptr; + } + } +} + +static bool sculpt_boundary_flags_ensure( + Object *ob, PBVH *pbvh, int totvert, int totedge, bool force_update = false) { SculptSession *ss = ob->sculpt; bool ret = false; + if (!ss->attrs.edge_boundary_flags) { + SculptAttributeParams params = {0}; + params.nointerp = true; + + ss->attrs.edge_boundary_flags = sculpt_attribute_ensure_ex( + ob, + ATTR_DOMAIN_EDGE, + CD_PROP_INT32, + SCULPT_ATTRIBUTE_NAME(edge_boundary_flags), + ¶ms, + BKE_pbvh_type(pbvh)); + + force_update = true; + ret = true; + } + if (!ss->attrs.boundary_flags) { SculptAttributeParams params = {0}; - params.nointerp = true; - // params.nocopy = true; ss->attrs.boundary_flags = sculpt_attribute_ensure_ex(ob, ATTR_DOMAIN_POINT, @@ -1410,9 +1471,28 @@ static bool sculpt_boundary_flags_ensure(Object *ob, } if (force_update) { + if (ss->bm) { + BM_mesh_elem_table_ensure(ss->bm, BM_VERT | BM_EDGE); + } + for (int i = 0; i < totvert; i++) { PBVHVertRef vertex = BKE_pbvh_index_to_vertex(pbvh, i); BKE_sculpt_boundary_flag_update(ss, vertex); + BKE_sculpt_boundary_flag_uv_update(ss, vertex); + + if (ss->pbvh) { + blender::bke::pbvh::check_vert_boundary(ss->pbvh, vertex); + } + } + + for (int i = 0; i < totedge; i++) { + PBVHEdgeRef edge = BKE_pbvh_index_to_edge(pbvh, i); + BKE_sculpt_boundary_flag_update(ss, edge); + BKE_sculpt_boundary_flag_uv_update(ss, edge); + + if (ss->pbvh) { + blender::bke::pbvh::check_edge_boundary(ss->pbvh, edge); + } } } @@ -1424,7 +1504,7 @@ static bool sculpt_boundary_flags_ensure(Object *ob, bool BKE_sculptsession_boundary_flags_ensure(Object *ob) { return sculpt_boundary_flags_ensure( - ob, ob->sculpt->pbvh, BKE_sculptsession_vertex_count(ob->sculpt)); + ob, ob->sculpt->pbvh, BKE_sculptsession_vertex_count(ob->sculpt), ob->sculpt->totedges); } void BKE_sculptsession_free_deformMats(SculptSession *ss) @@ -1905,8 +1985,12 @@ static void sculpt_update_object( PBVH *pbvh = BKE_sculpt_object_pbvh_ensure(depsgraph, ob); sculpt_check_face_areas(ob, pbvh); + if (ss->bm) { + ss->totedges = ss->bm->totedge; + } + if (pbvh) { - BKE_pbvh_sharp_limit_set(pbvh, ss->sharp_angle_limit); + blender::bke::pbvh::sharp_limit_set(pbvh, ss->sharp_angle_limit); } /* Sculpt Face Sets. */ @@ -1932,15 +2016,17 @@ static void sculpt_update_object( } } - sculpt_boundary_flags_ensure(ob, pbvh, BKE_sculptsession_vertex_count(ss)); + sculpt_boundary_flags_ensure(ob, pbvh, BKE_sculptsession_vertex_count(ss), ss->totedges); BKE_pbvh_update_active_vcol(pbvh, me); if (BKE_pbvh_type(pbvh) == PBVH_FACES) { ss->vert_normals = BKE_pbvh_get_vert_normals(ss->pbvh); + ss->poly_normals = blender::bke::pbvh::get_poly_normals(ss->pbvh); } else { ss->vert_normals = nullptr; + ss->poly_normals = {}; } BLI_assert(pbvh == ss->pbvh); @@ -2323,7 +2409,7 @@ int *BKE_sculpt_face_sets_ensure(Object *ob) SculptAttributeParams params = {}; params.permanent = true; - CustomData *cdata = ss->bm ? &ss->bm->pdata : &mesh->pdata; + CustomData *cdata = sculpt_get_cdata(ob, ATTR_DOMAIN_FACE); bool clear = CustomData_get_named_layer_index( cdata, CD_PROP_INT32, SCULPT_ATTRIBUTE_NAME(face_set)) == -1; @@ -2573,7 +2659,7 @@ static PBVH *build_pbvh_for_dynamic_topology(Object *ob, bool /*update_sculptver BM_mesh_elem_table_ensure(ob->sculpt->bm, BM_VERT | BM_EDGE | BM_FACE); BKE_sculptsession_update_attr_refs(ob); - sculpt_boundary_flags_ensure(ob, pbvh, ob->sculpt->bm->totvert); + sculpt_boundary_flags_ensure(ob, pbvh, ob->sculpt->bm->totvert, ob->sculpt->bm->totedge); sculpt_check_face_areas(ob, pbvh); BKE_sculpt_ensure_idmap(ob); @@ -2587,6 +2673,7 @@ static PBVH *build_pbvh_for_dynamic_topology(Object *ob, bool /*update_sculptver ob->sculpt->attrs.dyntopo_node_id_face->bmesh_cd_offset, ob->sculpt->attrs.face_areas->bmesh_cd_offset, ob->sculpt->attrs.boundary_flags->bmesh_cd_offset, + ob->sculpt->attrs.edge_boundary_flags->bmesh_cd_offset, ob->sculpt->attrs.flags ? ob->sculpt->attrs.flags->bmesh_cd_offset : -1, ob->sculpt->attrs.valence ? ob->sculpt->attrs.valence->bmesh_cd_offset : -1, ob->sculpt->attrs.orig_co ? ob->sculpt->attrs.orig_co->bmesh_cd_offset : -1, @@ -2596,7 +2683,7 @@ static PBVH *build_pbvh_for_dynamic_topology(Object *ob, bool /*update_sculptver if (ob->sculpt->bm_log) { BKE_pbvh_set_bm_log(pbvh, ob->sculpt->bm_log); } - BKE_pbvh_sharp_limit_set(pbvh, ob->sculpt->sharp_angle_limit); + blender::bke::pbvh::sharp_limit_set(pbvh, ob->sculpt->sharp_angle_limit); return pbvh; } @@ -2619,10 +2706,13 @@ static PBVH *build_pbvh_from_regular_mesh(Object *ob, Mesh *me_eval_deform) BKE_sculpt_init_flags_valence(ob, pbvh, me->totvert, true); sculpt_check_face_areas(ob, pbvh); BKE_sculptsession_update_attr_refs(ob); + blender::bke::pbvh::set_pmap(ss->pbvh, ss->pmap); + blender::bke::sculpt::sculpt_vert_boundary_ensure(ob); BKE_pbvh_build_mesh(pbvh, me); - BKE_pbvh_sharp_limit_set(pbvh, ss->sharp_angle_limit); + + blender::bke::pbvh::sharp_limit_set(pbvh, ss->sharp_angle_limit); const bool is_deformed = check_sculpt_object_deformed(ob, true); if (is_deformed && me_eval_deform != nullptr) { @@ -2647,6 +2737,7 @@ static PBVH *build_pbvh_from_ccg(Object *ob, SubdivCCG *subdiv_ccg) BKE_sculptsession_update_attr_refs(ob); sculpt_check_face_areas(ob, pbvh); + blender::bke::sculpt::sculpt_vert_boundary_ensure(ob); BKE_pbvh_build_grids(pbvh, subdiv_ccg->grids, @@ -2659,7 +2750,7 @@ static PBVH *build_pbvh_from_ccg(Object *ob, SubdivCCG *subdiv_ccg) base_mesh, subdiv_ccg); - BKE_pbvh_sharp_limit_set(pbvh, ss->sharp_angle_limit); + blender::bke::pbvh::sharp_limit_set(pbvh, ss->sharp_angle_limit); CustomData_reset(&ob->sculpt->temp_vdata); CustomData_reset(&ob->sculpt->temp_pdata); @@ -2698,7 +2789,7 @@ extern "C" bool BKE_sculpt_init_flags_valence(Object *ob, } BKE_sculpt_ensure_origco(ob); - sculpt_boundary_flags_ensure(ob, pbvh, totvert); + sculpt_boundary_flags_ensure(ob, pbvh, totvert, ss->totedges); BKE_sculptsession_update_attr_refs(ob); if (reset_flags) { @@ -2739,7 +2830,7 @@ PBVH *BKE_sculpt_object_pbvh_ensure(Depsgraph *depsgraph, Object *ob) PBVH *pbvh = ss->pbvh; if (pbvh != nullptr) { - BKE_pbvh_sharp_limit_set(pbvh, ss->sharp_angle_limit); + blender::bke::pbvh::sharp_limit_set(pbvh, ss->sharp_angle_limit); /* NOTE: It is possible that pointers to grids or other geometry data changed. Need to update * those pointers. */ @@ -2773,9 +2864,7 @@ PBVH *BKE_sculpt_object_pbvh_ensure(Depsgraph *depsgraph, Object *ob) if (ss->bm != nullptr) { /* Sculpting on a BMesh (dynamic-topology) gets a special PBVH. */ - pbvh = build_pbvh_for_dynamic_topology(ob, false); - - ss->pbvh = pbvh; + pbvh = ss->pbvh = build_pbvh_for_dynamic_topology(ob, false); } else { /* Detect if we are loading from an undo memfile step. */ @@ -2814,8 +2903,6 @@ PBVH *BKE_sculpt_object_pbvh_ensure(Depsgraph *depsgraph, Object *ob) BKE_sculpt_init_flags_valence(ob, pbvh, bm->totvert, true); blender::bke::paint::load_all_original(ob); - - sculpt_boundary_flags_ensure(ob, pbvh, ss->bm->totvert); } else { Object *object_eval = DEG_get_evaluated_object(depsgraph, ob); @@ -2834,6 +2921,12 @@ PBVH *BKE_sculpt_object_pbvh_ensure(Depsgraph *depsgraph, Object *ob) sculpt_attribute_update_refs(ob); + /* Forcibly flag all boundaries for update. */ + sculpt_boundary_flags_ensure( + ob, pbvh, BKE_sculptsession_vertex_count(ss), ss->bm->totedge, true); + + sculpt_attribute_update_refs(ob); + return pbvh; } @@ -3184,46 +3277,6 @@ BMesh *BKE_sculptsession_empty_bmesh_create() return bm; } -/** - * Returns pointer to a CustomData associated with a given domain, if - * one exists. If not nullptr is returned (this may happen with e.g. - * multires and #ATTR_DOMAIN_POINT). - */ -static CustomData *sculpt_get_cdata(Object *ob, eAttrDomain domain) -{ - SculptSession *ss = ob->sculpt; - - if (ss->bm) { - switch (domain) { - case ATTR_DOMAIN_POINT: - return &ss->bm->vdata; - case ATTR_DOMAIN_FACE: - return &ss->bm->pdata; - default: - BLI_assert_unreachable(); - return nullptr; - } - } - else { - Mesh *me = BKE_object_get_original_mesh(ob); - - switch (domain) { - case ATTR_DOMAIN_POINT: - /* Cannot get vertex domain for multires grids. */ - if (ss->pbvh && BKE_pbvh_type(ss->pbvh) == PBVH_GRIDS) { - return nullptr; - } - - return &me->vdata; - case ATTR_DOMAIN_FACE: - return &me->pdata; - default: - BLI_assert_unreachable(); - return nullptr; - } - } -} - static int sculpt_attr_elem_count_get(Object *ob, eAttrDomain domain) { SculptSession *ss = ob->sculpt; @@ -3248,6 +3301,8 @@ static int sculpt_attr_elem_count_get(Object *ob, eAttrDomain domain) break; case ATTR_DOMAIN_FACE: return ss->totfaces; + case ATTR_DOMAIN_EDGE: + return ss->bm ? ss->bm->totedge : BKE_object_get_original_mesh(ob)->totedge; default: BLI_assert_unreachable(); return 0; @@ -3319,6 +3374,8 @@ static bool sculpt_attribute_create(SculptSession *ss, case ATTR_DOMAIN_POINT: cdata = &ss->bm->vdata; break; + case ATTR_DOMAIN_EDGE: + cdata = &ss->bm->edata; case ATTR_DOMAIN_FACE: cdata = &ss->bm->pdata; break; @@ -3357,6 +3414,8 @@ static bool sculpt_attribute_create(SculptSession *ss, case ATTR_DOMAIN_FACE: cdata = &me->pdata; break; + case ATTR_DOMAIN_EDGE: + cdata = &me->edata; default: out->used = false; return false; @@ -3562,7 +3621,8 @@ SculptAttribute *BKE_sculpt_attribute_get(struct Object *ob, attr = sculpt_alloc_attr(ss); if (BKE_pbvh_type(ss->pbvh) == PBVH_FACES || - (BKE_pbvh_type(ss->pbvh) == PBVH_GRIDS && domain == ATTR_DOMAIN_FACE)) + (BKE_pbvh_type(ss->pbvh) == PBVH_GRIDS && + ELEM(domain, ATTR_DOMAIN_FACE, ATTR_DOMAIN_EDGE))) { attr->data = CustomData_get_layer_named_for_write( cdata, attr->proptype, attr->name, totelem); @@ -3647,6 +3707,9 @@ static void sculptsession_bmesh_attr_update_internal(Object *ob) int cd_face_area = ss->attrs.face_areas ? ss->attrs.face_areas->bmesh_cd_offset : -1; int cd_boundary_flags = ss->attrs.boundary_flags ? ss->attrs.boundary_flags->bmesh_cd_offset : -1; + int cd_edge_boundary = ss->attrs.edge_boundary_flags ? + ss->attrs.edge_boundary_flags->bmesh_cd_offset : + -1; int cd_dyntopo_vert = ss->attrs.dyntopo_node_id_vertex ? ss->attrs.dyntopo_node_id_vertex->bmesh_cd_offset : -1; @@ -3662,6 +3725,7 @@ static void sculptsession_bmesh_attr_update_internal(Object *ob) cd_dyntopo_face, cd_face_area, cd_boundary_flags, + cd_edge_boundary, cd_flag, cd_valence, ss->attrs.orig_co ? ss->attrs.orig_co->bmesh_cd_offset : -1, @@ -3776,14 +3840,20 @@ void BKE_sculpt_attributes_destroy_temporary_stroke(Object *ob) } } -static void update_bmesh_offsets(Mesh *me, SculptSession *ss) +static void update_bmesh_offsets(Object *ob) { + Mesh *me = BKE_object_get_original_mesh(ob); + SculptSession *ss = ob->sculpt; + ss->cd_vert_node_offset = ss->attrs.dyntopo_node_id_vertex ? ss->attrs.dyntopo_node_id_vertex->bmesh_cd_offset : -1; ss->cd_face_node_offset = ss->attrs.dyntopo_node_id_face ? ss->attrs.dyntopo_node_id_face->bmesh_cd_offset : -1; + ss->cd_origco_offset = ss->attrs.orig_co ? ss->attrs.orig_co->bmesh_cd_offset : -1; + ss->cd_origno_offset = ss->attrs.orig_no ? ss->attrs.orig_no->bmesh_cd_offset : -1; + ss->cd_origvcol_offset = ss->attrs.orig_color ? ss->attrs.orig_color->bmesh_cd_offset : -1; CustomDataLayer *layer = BKE_id_attribute_search(&me->id, BKE_id_attributes_active_color_name(&me->id), @@ -3792,8 +3862,8 @@ static void update_bmesh_offsets(Mesh *me, SculptSession *ss) ATTR_DOMAIN_MASK_CORNER); if (layer) { eAttrDomain domain = BKE_id_attribute_domain(&me->id, layer); + CustomData *cdata = sculpt_get_cdata(ob, domain); - CustomData *cdata = domain == ATTR_DOMAIN_POINT ? &ss->bm->vdata : &ss->bm->ldata; int layer_i = CustomData_get_named_layer_index( cdata, eCustomDataType(layer->type), layer->name); @@ -3810,6 +3880,9 @@ static void update_bmesh_offsets(Mesh *me, SculptSession *ss) int cd_boundary_flags = ss->attrs.boundary_flags ? ss->attrs.boundary_flags->bmesh_cd_offset : -1; + int cd_edge_boundary = ss->attrs.edge_boundary_flags ? + ss->attrs.edge_boundary_flags->bmesh_cd_offset : + -1; if (ss->pbvh) { BKE_pbvh_update_offsets(ss->pbvh, @@ -3817,6 +3890,7 @@ static void update_bmesh_offsets(Mesh *me, SculptSession *ss) ss->cd_face_node_offset, ss->cd_face_areas, cd_boundary_flags, + cd_edge_boundary, ss->attrs.flags ? ss->attrs.flags->bmesh_cd_offset : -1, ss->attrs.valence ? ss->attrs.valence->bmesh_cd_offset : -1, ss->attrs.orig_co ? ss->attrs.orig_co->bmesh_cd_offset : -1, @@ -3856,7 +3930,7 @@ static void sculpt_attribute_update_refs(Object *ob) } if (ss->bm) { - update_bmesh_offsets(me, ss); + update_bmesh_offsets(ob); } else if (ss->pbvh) { if (ss->attrs.orig_co && ss->attrs.orig_no) { @@ -3927,9 +4001,7 @@ bool BKE_sculpt_attribute_destroy(Object *ob, SculptAttribute *attr) } else if (ss->bm) { if (attr->data_for_bmesh) { - CustomData *cdata = attr->domain == ATTR_DOMAIN_POINT ? &ss->bm->vdata : &ss->bm->pdata; - - BM_data_layer_free_named(ss->bm, cdata, attr->name); + BM_data_layer_free_named(ss->bm, sculpt_get_cdata(ob, attr->domain), attr->name); } } else { @@ -3941,6 +4013,10 @@ bool BKE_sculpt_attribute_destroy(Object *ob, SculptAttribute *attr) cdata = ss->bm ? &ss->bm->vdata : &me->vdata; totelem = ss->totvert; break; + case ATTR_DOMAIN_EDGE: + cdata = ss->bm ? &ss->bm->edata : &me->edata; + totelem = BKE_object_get_original_mesh(ob)->totedge; + break; case ATTR_DOMAIN_FACE: cdata = ss->bm ? &ss->bm->pdata : &me->pdata; totelem = ss->totfaces; @@ -4190,25 +4266,354 @@ void load_all_original(Object *ob) } // namespace blender::bke::paint -void BKE_sculpt_sharp_boundary_flag_update(SculptSession *ss, PBVHVertRef vertex, bool update_ring) +namespace blender::bke::sculpt { +void sculpt_vert_boundary_ensure(Object *object) { - int *flags = blender::bke::paint::vertex_attr_ptr(vertex, ss->attrs.boundary_flags); - *flags |= SCULPT_BOUNDARY_UPDATE_SHARP_ANGLE; + using namespace blender; + SculptSession *ss = object->sculpt; -#if 1 - if (update_ring && ss->bm) { - BMVert *v = reinterpret_cast(vertex.i); - if (!v->e) { - return; + /* PBVH_BMESH now handles boundaries itself. */ + if (ss->bm || ss->vertex_info.boundary) { + if (!ss->bm && ss->pbvh) { + blender::bke::pbvh::set_vert_boundary_map(ss->pbvh, ss->vertex_info.boundary); } - BMEdge *e = v->e; - do { - PBVHVertRef vertex2 = {reinterpret_cast(BM_edge_other_vert(e, v))}; - - int *flags2 = blender::bke::paint::vertex_attr_ptr(vertex2, ss->attrs.boundary_flags); - *flags2 |= SCULPT_BOUNDARY_UPDATE_SHARP_ANGLE; - } while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e); + return; } -#endif + + Mesh *base_mesh = BKE_mesh_from_object(object); + const blender::Span edges = base_mesh->edges(); + const OffsetIndices polys = base_mesh->polys(); + const Span corner_edges = base_mesh->corner_edges(); + + ss->vertex_info.boundary = BLI_BITMAP_NEW(base_mesh->totvert, "Boundary info"); + int *adjacent_faces_edge_count = static_cast( + MEM_calloc_arrayN(base_mesh->totedge, sizeof(int), "Adjacent face edge count")); + + for (const int i : polys.index_range()) { + for (const int edge : corner_edges.slice(polys[i])) { + adjacent_faces_edge_count[edge]++; + } + } + + for (const int e : edges.index_range()) { + if (adjacent_faces_edge_count[e] < 2) { + const int2 &edge = edges[e]; + BLI_BITMAP_SET(ss->vertex_info.boundary, edge[0], true); + BLI_BITMAP_SET(ss->vertex_info.boundary, edge[1], true); + } + } + + if (ss->pbvh) { + blender::bke::pbvh::set_vert_boundary_map(ss->pbvh, ss->vertex_info.boundary); + } + + MEM_freeN(adjacent_faces_edge_count); } + +float calc_uv_snap_limit(BMLoop *l, int cd_uv) +{ + BMVert *v = l->v; + + float avg_len = 0.0f; + int avg_tot = 0; + + BMEdge *e = v->e; + do { + BMLoop *l2 = e->l; + + if (!l2) { + continue; + } + + do { + float *uv1 = BM_ELEM_CD_PTR(l2, cd_uv); + float *uv2 = BM_ELEM_CD_PTR(l2->next, cd_uv); + + avg_len += len_v2v2(uv1, uv2); + avg_tot++; + } while ((l2 = l2->radial_next) != e->l); + } while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e); + + if (avg_tot > 0) { + /* 1/10th of average uv edge length. */ + return (avg_len / avg_tot) * 0.1f; + } + else { + return 0.005f; + } +} + +bool loop_is_corner(BMLoop *l, int cd_uv, float limit) +{ + BMVert *v = l->v; + + float2 value = *BM_ELEM_CD_PTR(l, cd_uv); + + Vector ls; + Vector keys; + + BMEdge *e = v->e; + do { + BMLoop *l2 = e->l; + + if (!l2) { + continue; + } + + do { + BMLoop *l3 = l2->v == v ? l2 : l2->next; + if (!ls.contains(l3)) { + ls.append(l3); + } + } while ((l2 = l2->radial_next) != e->l); + } while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e); + + const float scale = 1.0f / limit; + + for (BMLoop *l2 : ls) { + float2 value2 = *BM_ELEM_CD_PTR(l2, cd_uv); + float2 dv = value2 - value; + + double f = dv[0] * dv[0] + dv[1] * dv[1]; + int key = int(f * scale); + if (!keys.contains(key)) { + keys.append(key); + } + } + + bool ret = keys.size() > 2; + if (ret) { + l->v->head.hflag |= BM_ELEM_SELECT; + } + + return ret; +} + +#if 0 +/* Angle test */ +bool loop_is_corner(BMLoop *l, int cd_offset) +{ + BMVert *v = l->v; + BMEdge *e = v->e; + + float2 value = *BM_ELEM_CD_PTR(l, cd_offset); + float limit = 0.01; + + BMLoop *outer1 = nullptr, *outer2 = nullptr; + do { + BMLoop *l2 = e->l; + if (!l2) { + continue; + } + + do { + BMLoop *l3 = l2->v == v ? l2 : l2->next; + float2 value3 = *BM_ELEM_CD_PTR(l3, cd_offset); + + if (!prop_eq(value, value3, limit)) { + continue; + } + + bool outer = true; + + BMLoop *l4 = l2->radial_next; + while (l4 != l2) { + BMLoop *l5 = l4->v == v ? l4 : l4->next; + float2 value5 = *BM_ELEM_CD_PTR(l5, cd_offset); + + if (prop_eq(value, value5, limit)) { + outer = false; + break; + } + l4 = l4->radial_next; + } + + if (outer) { + BMLoop *l3 = l2->v == v ? l2->next : l2; + + if (!outer1) { + outer1 = l3; + } + else if (!outer2 && l3 != outer2) { + outer2 = l3; + } + + break; + } + } while ((l2 = l2->radial_next) != e->l); + } while ((e = BM_DISK_EDGE_NEXT(e, v)) != l->e); + + if (!outer1 || !outer2) { + return outer1 != nullptr; + } + + float2 t1 = *BM_ELEM_CD_PTR(outer1, cd_offset) - value; + float2 t2 = *BM_ELEM_CD_PTR(outer2, cd_offset) - value; + + normalize_v2(t1); + normalize_v2(t2); + float angle_limit = 110.0f / 180.0f * M_PI; + float angle = saacos(dot_v2v2(t1, t2)); + + if (angle < angle_limit) { + l->v->head.hflag |= BM_ELEM_SELECT; + } + + return angle < angle_limit; +} +#endif + +template +static void corner_interp(CustomDataLayer *layer, + BMLoop *l, + Span loops, + Span ws, + int cd_offset, + float factor) +{ + float *ws2 = (float *)BLI_array_alloca(ws2, loops.size() + 1); + T sum = {}; + float totsum = 0.0f; + + T value = *BM_ELEM_CD_PTR(l, cd_offset); + + float limit; + if (layer->type == CD_PROP_FLOAT2) { + // limit = 0.01; + limit = calc_uv_snap_limit(loops[0], cd_offset); + } + else { + /* Do not restrict to islands for non-UVs */ + limit = FLT_MAX; + } + + for (int i : loops.index_range()) { + BMLoop *l2 = loops[i]; + BMLoop *l3 = l2->next->v == l->v ? l2->next : l2->prev; + T value3 = *BM_ELEM_CD_PTR(l3, cd_offset); + + if (prop_eq(value, value3, 0.01)) { + T value2 = *BM_ELEM_CD_PTR(l2, cd_offset); + sum += value2 * ws[i]; + totsum += ws[i]; + } + } + + if (totsum == 0.0f) { + return; + } + + sum /= totsum; + + *BM_ELEM_CD_PTR(l, cd_offset) = value + (sum - value) * factor; +} + +/* Interpolates loops surrounding a vertex, splitting any UV map by + * island as appropriate and enforcing proper boundary conditions. + */ +void interp_face_corners( + PBVH *pbvh, PBVHVertRef vertex, Span loops, Span ws, float factor) +{ + if (BKE_pbvh_type(pbvh) != PBVH_BMESH) { + return; /* Only for PBVH_BMESH. */ + } + + eCustomDataMask mask = CD_MASK_PROP_FLOAT | CD_MASK_PROP_FLOAT2 | CD_MASK_PROP_FLOAT3 | + CD_MASK_PROP_COLOR | CD_MASK_PROP_BYTE_COLOR; + + BMesh *bm = BKE_pbvh_get_bmesh(pbvh); + BMVert *v = reinterpret_cast(vertex.i); + BMEdge *e = v->e; + BMLoop *l = e->l; + CustomData *cdata = &bm->ldata; + + Vector ls; + + /* Tag loops around vertex. */ + do { + l = e->l; + + if (!l) { + continue; + } + + do { + BMLoop *l2 = l->v == v ? l : l->next; + BM_elem_flag_enable(l2, BM_ELEM_TAG); + } while ((l = l->radial_next) != e->l); + } while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e); + + /* Build loop list. */ + do { + l = e->l; + + if (!l) { + continue; + } + + do { + BMLoop *l2 = l->v == v ? l : l->next; + if (BM_elem_flag_test(l2, BM_ELEM_TAG)) { + BM_elem_flag_disable(l2, BM_ELEM_TAG); + ls.append(l2); + } + } while ((l = l->radial_next) != e->l); + } while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e); + + Vector layers; + for (int layer_i : IndexRange(cdata->totlayer)) { + CustomDataLayer *layer = cdata->layers + layer_i; + + if ((layer->flag & CD_FLAG_ELEM_NOINTERP) || !(CD_TYPE_AS_MASK(layer->type) & mask)) { + continue; + } + if (layer->flag & CD_FLAG_TEMPORARY) { + continue; + } + + layers.append(layer); + } + + /* Interpolate. */ + if (loops.size() > 1) { + VertLoopSnapper corner_snap = {Span(ls), Span(layers)}; + + for (CustomDataLayer *layer : layers) { + Vector corners; + + if (layer->type == CD_PROP_FLOAT2) { + for (BMLoop *l : ls) { + corners.append(loop_is_corner(l, layer->offset)); + } + } + + for (int i : ls.index_range()) { + BMLoop *l = ls[i]; + + if (layer->type == CD_PROP_FLOAT2 && corners[i]) { + continue; + } + + switch (layer->type) { + case CD_PROP_FLOAT: + corner_interp(layer, l, loops, ws, layer->offset, factor); + break; + case CD_PROP_FLOAT2: + corner_interp(layer, l, loops, ws, layer->offset, factor); + break; + case CD_PROP_FLOAT3: + corner_interp(layer, l, loops, ws, layer->offset, factor); + break; + case CD_PROP_COLOR: + corner_interp(layer, l, loops, ws, layer->offset, factor); + break; + } + } + } + + /* Snap. */ + // corner_snap.snap(); + } +} +} // namespace blender::bke::sculpt diff --git a/source/blender/blenkernel/intern/pbvh.cc b/source/blender/blenkernel/intern/pbvh.cc index 3fbf37fda46..913d348ed67 100644 --- a/source/blender/blenkernel/intern/pbvh.cc +++ b/source/blender/blenkernel/intern/pbvh.cc @@ -1015,9 +1015,16 @@ void BKE_pbvh_update_mesh_pointers(PBVH *pbvh, Mesh *mesh) BLI_assert(pbvh->header.type == PBVH_FACES); pbvh->polys = mesh->polys(); + pbvh->edges = mesh->edges(); pbvh->corner_verts = mesh->corner_verts().data(); + pbvh->corner_edges = mesh->corner_edges().data(); pbvh->looptri_polys = mesh->looptri_polys().data(); + pbvh->seam_edges = static_cast( + CustomData_get_layer_named(&mesh->edata, CD_PROP_BOOL, ".uv_seam")); + pbvh->sharp_edges = static_cast( + CustomData_get_layer_named(&mesh->edata, CD_PROP_BOOL, "sharp_edge")); + if (!pbvh->deformed) { /* Deformed positions not matching the original mesh are owned directly by the PBVH, and are * set separately by #BKE_pbvh_vert_coords_apply. */ @@ -1054,8 +1061,15 @@ void BKE_pbvh_build_mesh(PBVH *pbvh, Mesh *mesh) const blender::OffsetIndices polys = mesh->polys(); const Span corner_verts = mesh->corner_verts(); + pbvh->polys = mesh->polys(); pbvh->corner_verts = mesh->corner_verts().data(); pbvh->corner_edges = mesh->corner_edges().data(); + pbvh->edges = mesh->edges(); + pbvh->seam_edges = static_cast( + CustomData_get_layer_named(&mesh->edata, CD_PROP_BOOL, ".uv_seam")); + pbvh->sharp_edges = static_cast( + CustomData_get_layer_named(&mesh->edata, CD_PROP_BOOL, "sharp_edge")); + pbvh->totloop = mesh->totloop; MLoopTri *looptri = static_cast( MEM_malloc_arrayN(looptri_num, sizeof(*looptri), __func__)); @@ -1151,6 +1165,7 @@ void BKE_pbvh_build_grids(PBVH *pbvh, pbvh->gridfaces = gridfaces; pbvh->grid_flag_mats = flagmats; pbvh->totgrid = totgrid; + pbvh->totloop = me->totloop; pbvh->gridkey = *key; pbvh->grid_hidden = grid_hidden; pbvh->subdiv_ccg = subdiv_ccg; @@ -1175,7 +1190,13 @@ void BKE_pbvh_build_grids(PBVH *pbvh, pbvh->pdata = &me->pdata; pbvh->polys = polys; + pbvh->edges = me->edges(); pbvh->corner_verts = me->corner_verts().data(); + pbvh->corner_edges = me->corner_edges().data(); + pbvh->seam_edges = static_cast( + CustomData_get_layer_named(&me->edata, CD_PROP_BOOL, ".uv_seam")); + pbvh->sharp_edges = static_cast( + CustomData_get_layer_named(&me->edata, CD_PROP_BOOL, "sharp_edge")); /* We also need the base mesh for PBVH draw. */ pbvh->mesh = me; @@ -3710,6 +3731,14 @@ const float (*BKE_pbvh_get_vert_normals(const PBVH *pbvh))[3] return pbvh->vert_normals; } +namespace blender::bke::pbvh { +Span get_poly_normals(const PBVH *pbvh) +{ + BLI_assert(pbvh->header.type == PBVH_FACES); + return pbvh->poly_normals; +} +} // namespace blender::bke::pbvh + const bool *BKE_pbvh_get_vert_hide(const PBVH *pbvh) { BLI_assert(pbvh->header.type == PBVH_FACES); @@ -4204,6 +4233,155 @@ void BKE_pbvh_set_symmetry(PBVH *pbvh, int symmetry) } namespace blender::bke::pbvh { +void set_vert_boundary_map(PBVH *pbvh, BLI_bitmap *vert_boundary_map) +{ + pbvh->vert_boundary_map = vert_boundary_map; +} + +void update_edge_boundary_grids(int edge, + Span edges, + OffsetIndices polys, + int *edge_boundary_flags, + const int *vert_boundary_flags, + const int *face_sets, + const bool *sharp_edge, + const bool *seam_edge, + const GroupedSpan &pmap, + const GroupedSpan &epmap, + const CustomData *ldata, + SubdivCCG *subdiv_ccg, + const CCGKey *key, + float sharp_angle_limit, + blender::Span corner_verts, + blender::Span corner_edges) +{ + // +} + +static void get_edge_polys(int edge, + const GroupedSpan &pmap, + const GroupedSpan &epmap, + Span edges, + OffsetIndices polys, + Span corner_edges, + int *r_poly1, + int *r_poly2) +{ + *r_poly1 = -1; + *r_poly2 = -1; + + if (!epmap.is_empty()) { + Span polys = epmap[edge]; + + if (polys.size() > 0) { + *r_poly1 = polys[0]; + } + if (polys.size() > 1) { + *r_poly1 = polys[1]; + } + } + else { + int v1 = edges[edge][0], v2 = edges[edge][1]; + + for (int poly : pmap[v1]) { + for (int loop : polys[poly]) { + if (corner_edges[loop] == edge) { + if (*r_poly1 == -1) { + *r_poly1 = poly; + } + else { + *r_poly2 = poly; + } + } + } + } + } +} + +void update_edge_boundary_faces(int edge, + Span vertex_positions, + Span vertex_normals, + Span edges, + OffsetIndices polys, + Span poly_normals, + int *edge_boundary_flags, + const int *vert_boundary_flags, + const int *face_sets, + const bool *sharp_edge, + const bool *seam_edge, + const GroupedSpan &pmap, + const GroupedSpan &epmap, + const CustomData * /*ldata*/, + float sharp_angle_limit, + blender::Span corner_verts, + blender::Span corner_edges) +{ + int oldflag = edge_boundary_flags[edge]; + bool update_uv = oldflag & SCULPT_BOUNDARY_UPDATE_UV; + bool update_sharp = oldflag & SCULPT_BOUNDARY_UPDATE_SHARP_ANGLE; + int newflag = 0; + + if (update_sharp) { + int poly1 = -1, poly2 = -1; + + edge_boundary_flags[edge] &= ~SCULPT_BOUNDARY_UPDATE_SHARP_ANGLE; + + get_edge_polys(edge, pmap, epmap, edges, polys, corner_edges, &poly1, &poly2); + if (poly1 != -1 && poly2 != -1 && + test_sharp_faces_mesh( + poly1, poly2, sharp_angle_limit, vertex_positions, polys, poly_normals, corner_verts)) + { + edge_boundary_flags[edge] |= SCULPT_BOUNDARY_SHARP_ANGLE; + } + else { + edge_boundary_flags[edge] &= ~SCULPT_BOUNDARY_SHARP_ANGLE; + } + } + + if (!update_uv) { + newflag |= oldflag & SCULPT_BOUNDARY_UV; + } + + if (!(oldflag & SCULPT_BOUNDARY_NEEDS_UPDATE)) { + return; + } + + /* Some boundary types require an edge->poly map to be fully accurate. */ + if (!epmap.is_empty()) { + if (face_sets) { + int fset = -1; + + for (int poly : epmap[edge]) { + if (fset == -1) { + fset = face_sets[poly]; + } + else if (face_sets[poly] != fset) { + newflag |= SCULPT_BOUNDARY_FACE_SET; + break; + } + } + } + newflag |= epmap[edge].size() == 1 ? SCULPT_BOUNDARY_MESH : 0; + } + else { /* No edge->poly map; approximate from vertices (will give artifacts on corners). */ + int v1 = edges[edge][0]; + int v2 = edges[edge][2]; + + int boundary_mask = ((1 << int(SCULPT_CORNER_BIT_SHIFT)) - 1); + int a = vert_boundary_flags[v1] & + ~(SCULPT_BOUNDARY_UPDATE_UV | SCULPT_BOUNDARY_UPDATE_SHARP_ANGLE); + int b = vert_boundary_flags[v2] & + ~(SCULPT_BOUNDARY_UPDATE_UV | SCULPT_BOUNDARY_UPDATE_SHARP_ANGLE); + + newflag |= a & b; + } + + newflag |= sharp_edge && sharp_edge[edge] ? SCULPT_BOUNDARY_SHARP_MARK : 0; + newflag |= (seam_edge && seam_edge[edge]) ? SCULPT_BOUNDARY_SEAM : 0; + + edge_boundary_flags[edge] = newflag; +} + void set_flags_valence(PBVH *pbvh, uint8_t *flags, int *valence) { pbvh->sculpt_flags = flags; @@ -4224,7 +4402,6 @@ void update_vert_boundary_faces(int *boundary_flags, const int *corner_verts, const int *corner_edges, OffsetIndices polys, - int /*totpoly*/, const blender::GroupedSpan &pmap, PBVHVertRef vertex, const bool *sharp_edges, @@ -4315,6 +4492,123 @@ void update_vert_boundary_faces(int *boundary_flags, *boundary_flag |= SCULPT_CORNER_SEAM; } } + +static bool check_unique_face_set_in_base_mesh(const PBVH *pbvh, int vertex, bool *r_corner) +{ + if (!pbvh->face_sets) { + return true; + } + int fset1 = -1, fset2 = -1, fset3 = -1; + + for (int poly : pbvh->pmap[vertex]) { + int fset = pbvh->face_sets[poly]; + + if (fset1 == -1) { + fset1 = fset; + } + else if (fset2 == -1 && fset != fset1) { + fset2 = fset; + } + else if (fset3 == -1 && fset != fset1 && fset != fset2) { + fset3 = fset; + } + } + + *r_corner = fset3 != -1; + return fset2 == -1; +} + +static bool check_boundary_vertex_in_base_mesh(const PBVH *pbvh, int vert) +{ + return pbvh->vert_boundary_map ? BLI_BITMAP_TEST(pbvh->vert_boundary_map, vert) : false; +} + +/** + * Checks if the face sets of the adjacent faces to the edge between \a v1 and \a v2 + * in the base mesh are equal. + */ +static bool check_unique_face_set_for_edge_in_base_mesh(const PBVH *pbvh, int v1, int v2) +{ + if (!pbvh->face_sets) { + return true; + } + + int p1 = -1, p2 = -1; + for (int poly : pbvh->pmap[v1]) { + const IndexRange p = pbvh->polys[poly]; + + for (int l = 0; l < p.size(); l++) { + const int *corner_verts = &pbvh->corner_verts[p.start() + l]; + if (*corner_verts == v2) { + if (p1 == -1) { + p1 = poly; + break; + } + + if (p2 == -1) { + p2 = poly; + break; + } + } + } + } + + if (p1 != -1 && p2 != -1) { + return abs(pbvh->face_sets[p1]) == (pbvh->face_sets[p2]); + } + return true; +} + +void update_vert_boundary_grids(PBVH *pbvh, int index) +{ + + int *flag = pbvh->boundary_flags + index; + + *flag = 0; + + const CCGKey *key = BKE_pbvh_get_grid_key(pbvh); + const int grid_index = index / key->grid_area; + const int vertex_index = index - grid_index * key->grid_area; + SubdivCCGCoord coord{}; + + coord.grid_index = grid_index; + coord.x = vertex_index % key->grid_size; + coord.y = vertex_index / key->grid_size; + + int v1, v2; + const SubdivCCGAdjacencyType adjacency = BKE_subdiv_ccg_coarse_mesh_adjacency_info_get( + pbvh->subdiv_ccg, &coord, {pbvh->corner_verts, pbvh->totloop}, pbvh->polys, &v1, &v2); + + bool fset_corner = false; + switch (adjacency) { + case SUBDIV_CCG_ADJACENT_VERTEX: + if (!check_unique_face_set_in_base_mesh(pbvh, v1, &fset_corner)) { + *flag |= SCULPT_BOUNDARY_FACE_SET; + } + if (check_boundary_vertex_in_base_mesh(pbvh, v1)) { + *flag |= SCULPT_BOUNDARY_MESH; + } + break; + case SUBDIV_CCG_ADJACENT_EDGE: { + if (!check_unique_face_set_for_edge_in_base_mesh(pbvh, v1, v2)) { + *flag |= SCULPT_BOUNDARY_FACE_SET; + } + + if (check_boundary_vertex_in_base_mesh(pbvh, v1) && + check_boundary_vertex_in_base_mesh(pbvh, v2)) { + *flag |= SCULPT_BOUNDARY_MESH; + } + break; + } + case SUBDIV_CCG_ADJACENT_NONE: + break; + } + + if (fset_corner) { + *flag |= SCULPT_CORNER_FACE_SET | SCULPT_BOUNDARY_FACE_SET; + } +} + } // namespace blender::bke::pbvh void BKE_pbvh_reproject_smooth_set(PBVH *pbvh, bool value) @@ -4802,4 +5096,177 @@ void set_vemap(PBVH *pbvh, GroupedSpan vemap) pbvh->vemap = vemap; } +static bool test_colinear_tri(int f, + Span positions, + blender::OffsetIndices polys, + Span corner_verts) +{ + Span verts = corner_verts.slice(polys[f]); + + float area_limit = 0.00001f; + area_limit = len_squared_v3v3(positions[verts[0]], positions[verts[1]]) * 0.001f; + + return area_tri_v3(positions[verts[0]], positions[verts[1]], positions[verts[2]]) <= area_limit; +} + +float test_sharp_faces_mesh(int f1, + int f2, + float limit, + Span positions, + blender::OffsetIndices &polys, + Span poly_normals, + Span corner_verts) +{ + float angle = saacos(dot_v3v3(poly_normals[f1], poly_normals[f2])); + + /* Detect coincident triangles. */ + if (polys[f1].size() == 3 && test_colinear_tri(f1, positions, polys, corner_verts)) { + return false; + } + if (polys[f2].size() == 3 && test_colinear_tri(f2, positions, polys, corner_verts)) { + return false; + } + + /* Try to ignore folded over edges. */ + if (angle > M_PI * 0.6) { + return false; + } + + return angle > limit; +} + +bool check_vert_boundary(PBVH *pbvh, PBVHVertRef vertex) +{ + switch (BKE_pbvh_type(pbvh)) { + case PBVH_BMESH: { + if (pbvh->cd_boundary_flag == -1) { + return false; + } + + return pbvh_check_vert_boundary_bmesh(pbvh, reinterpret_cast(vertex.i)); + } + case PBVH_FACES: { + if (!pbvh->boundary_flags) { + return false; + } + if (pbvh->boundary_flags[vertex.i] & + (SCULPT_BOUNDARY_NEEDS_UPDATE | SCULPT_BOUNDARY_UPDATE_UV)) { + update_vert_boundary_faces(pbvh->boundary_flags, + pbvh->face_sets, + pbvh->hide_poly, + pbvh->vert_positions, + nullptr, + pbvh->corner_verts, + pbvh->corner_edges, + pbvh->polys, + pbvh->pmap, + vertex, + pbvh->sharp_edges, + pbvh->seam_edges, + pbvh->sculpt_flags, + pbvh->valence); + return true; + } + } + case PBVH_GRIDS: { + if (!pbvh->boundary_flags) { + return false; + } + + if (pbvh->boundary_flags[vertex.i] & + (SCULPT_BOUNDARY_NEEDS_UPDATE | SCULPT_BOUNDARY_UPDATE_UV)) { + update_vert_boundary_grids(pbvh, vertex.i); + return true; + } + } + } + + return false; +} + +bool check_edge_boundary(PBVH *pbvh, PBVHEdgeRef edge) +{ + switch (BKE_pbvh_type(pbvh)) { + case PBVH_BMESH: { + BMEdge *e = reinterpret_cast(edge.i); + + if (pbvh->cd_edge_boundary == -1) { + return false; + } + + if (BM_ELEM_CD_GET_INT(e, pbvh->cd_edge_boundary) & + (SCULPT_BOUNDARY_NEEDS_UPDATE | SCULPT_BOUNDARY_UPDATE_UV)) + { + update_edge_boundary_bmesh(e, + pbvh->cd_faceset_offset, + pbvh->cd_edge_boundary, + pbvh->cd_flag, + pbvh->cd_valence, + &pbvh->header.bm->ldata, + pbvh->sharp_angle_limit); + } + } + case PBVH_FACES: { + if (!pbvh->edge_boundary_flags) { + return false; + } + if (pbvh->edge_boundary_flags[edge.i] & + (SCULPT_BOUNDARY_NEEDS_UPDATE | SCULPT_BOUNDARY_UPDATE_UV)) { + Span cos = {reinterpret_cast(pbvh->vert_positions), pbvh->totvert}; + Span nos = {reinterpret_cast(pbvh->vert_normals), pbvh->totvert}; + + update_edge_boundary_faces(edge.i, + cos, + nos, + pbvh->edges, + pbvh->polys, + pbvh->poly_normals, + pbvh->edge_boundary_flags, + pbvh->boundary_flags, + pbvh->face_sets, + pbvh->sharp_edges, + pbvh->seam_edges, + pbvh->pmap, + {}, + pbvh->ldata, + pbvh->sharp_angle_limit, + {pbvh->corner_verts, pbvh->totloop}, + {pbvh->corner_edges, pbvh->totloop}); + return true; + } + + break; + } + case PBVH_GRIDS: { + if (!pbvh->edge_boundary_flags) { + return false; + } + + if (pbvh->edge_boundary_flags[edge.i] & + (SCULPT_BOUNDARY_NEEDS_UPDATE | SCULPT_BOUNDARY_UPDATE_UV)) { + update_edge_boundary_grids(edge.i, + pbvh->edges, + pbvh->polys, + pbvh->edge_boundary_flags, + pbvh->boundary_flags, + pbvh->face_sets, + pbvh->sharp_edges, + pbvh->seam_edges, + pbvh->pmap, + {}, + pbvh->ldata, + pbvh->subdiv_ccg, + BKE_pbvh_get_grid_key(pbvh), + pbvh->sharp_angle_limit, + {pbvh->corner_verts, pbvh->totloop}, + {pbvh->corner_edges, pbvh->totloop}); + return true; + } + break; + } + } + + return false; +} + } // namespace blender::bke::pbvh diff --git a/source/blender/blenkernel/intern/pbvh_bmesh.cc b/source/blender/blenkernel/intern/pbvh_bmesh.cc index ad2705da71d..4655a8e9ccc 100644 --- a/source/blender/blenkernel/intern/pbvh_bmesh.cc +++ b/source/blender/blenkernel/intern/pbvh_bmesh.cc @@ -1943,24 +1943,6 @@ static void pbvh_bmesh_create_nodes_fast_recursive_final(PBVH *pbvh, /***************************** Public API *****************************/ -//#define MV_COLOR_BOUNDARY - -#ifdef MV_COLOR_BOUNDARY -static int color_boundary_key(float col[4]) -{ - const float steps = 2.0f; - float hsv[3]; - - rgb_to_hsv(col[0], col[1], col[2], hsv, hsv + 1, hsv + 2); - - int x = (int)((hsv[0] * 0.5f + 0.5f) * steps + 0.5f); - int y = (int)(hsv[1] * steps + 0.5f); - int z = (int)(hsv[2] * steps + 0.5f); - - return z * steps * steps + y * steps + x; -} -#endif - static bool test_colinear_tri(BMFace *f) { BMLoop *l = f->l_first; @@ -1971,18 +1953,12 @@ static bool test_colinear_tri(BMFace *f) return area_tri_v3(l->v->co, l->next->v->co, l->prev->v->co) <= area_limit; } -float BKE_pbvh_test_sharp_faces_bmesh(BMFace *f1, BMFace *f2, float limit) +namespace blender::bke::pbvh { + +float test_sharp_faces_bmesh(BMFace *f1, BMFace *f2, float limit) { float angle = saacos(dot_v3v3(f1->no, f2->no)); -#if 0 // XXX - float no1[3], no2[3]; - normal_tri_v3(no1, f1->l_first->v->co, f1->l_first->next->v->co, f1->l_first->next->next->v->co); - normal_tri_v3(no2, f2->l_first->v->co, f2->l_first->next->v->co, f2->l_first->next->next->v->co); - - angle = saacos(dot_v3v3(no1, no2)); -#endif - /* Detect coincident triangles. */ if (f1->len == 3 && test_colinear_tri(f1)) { return false; @@ -1999,32 +1975,178 @@ float BKE_pbvh_test_sharp_faces_bmesh(BMFace *f1, BMFace *f2, float limit) return angle > limit; } -void BKE_pbvh_update_vert_boundary(int cd_faceset_offset, - int cd_vert_node_offset, - int cd_face_node_offset, - int /*cd_vcol*/, - int cd_boundary_flag, - const int cd_flag, - const int cd_valence, - BMVert *v, - const CustomData *ldata, - const int totuv, - const bool /*do_uvs*/, - float sharp_angle_limit) +static void update_edge_boundary_bmesh_uv(BMEdge *e, int cd_edge_boundary, const CustomData *ldata) { - int newflag = *BM_ELEM_CD_PTR(v, cd_flag); - int boundflag = 0; + int base = ldata->typemap[CD_PROP_FLOAT2]; - const bool do_uvs = totuv > 0; + *BM_ELEM_CD_PTR(e, cd_edge_boundary) &= ~(SCULPT_BOUNDARY_UPDATE_UV | SCULPT_BOUNDARY_UV); + + for (int i = base; i < ldata->totlayer; i++) { + const CustomDataLayer &layer = ldata->layers[i]; + + if (layer.type != CD_PROP_FLOAT2) { + break; + } + if (layer.flag & (CD_FLAG_TEMPORARY | CD_FLAG_ELEM_NOINTERP)) { + continue; + } + + BMLoop *l = e->l; + + int cd_uv = layer.offset; + float limit = blender::bke::sculpt::calc_uv_snap_limit(l, cd_uv); + limit *= limit; + + float2 a1 = BM_ELEM_CD_PTR((l->v == e->v1 ? l : l->next), cd_uv); + float2 a2 = BM_ELEM_CD_PTR((l->v == e->v2 ? l : l->next), cd_uv); + + do { + float *b1 = BM_ELEM_CD_PTR((l->v == e->v1 ? l : l->next), cd_uv); + float *b2 = BM_ELEM_CD_PTR((l->v == e->v2 ? l : l->next), cd_uv); + + if (len_squared_v2v2(a1, b1) > limit || len_squared_v2v2(a2, b2) > limit) { + *BM_ELEM_CD_PTR(e, cd_edge_boundary) |= SCULPT_BOUNDARY_UV; + return; + } + } while ((l = l->radial_next) != e->l); + } +} + +void update_edge_boundary_bmesh(BMEdge *e, + int cd_faceset_offset, + int cd_edge_boundary, + const int cd_flag, + const int cd_valence, + const CustomData *ldata, + float sharp_angle_limit) +{ + int oldflag = BM_ELEM_CD_GET_INT(e, cd_edge_boundary); + + if (e->l && (oldflag & SCULPT_BOUNDARY_UPDATE_UV) && ldata->typemap[CD_PROP_FLOAT2] != -1) { + update_edge_boundary_bmesh_uv(e, cd_edge_boundary, ldata); + oldflag = BM_ELEM_CD_GET_INT(e, cd_edge_boundary); + } + + if (oldflag & SCULPT_BOUNDARY_UPDATE_SHARP_ANGLE) { + if (e->l && e->l != e->l->radial_next && + test_sharp_faces_bmesh(e->l->f, e->l->radial_next->f, sharp_angle_limit)) + { + *BM_ELEM_CD_PTR(e, cd_edge_boundary) |= SCULPT_BOUNDARY_SHARP_ANGLE; + } + else { + *BM_ELEM_CD_PTR(e, cd_edge_boundary) &= ~SCULPT_BOUNDARY_SHARP_ANGLE; + } + oldflag = BM_ELEM_CD_GET_INT(e, cd_edge_boundary); + } + + if (!(oldflag & SCULPT_BOUNDARY_NEEDS_UPDATE)) { + return; + } + + int newflag = oldflag & (SCULPT_BOUNDARY_UV | SCULPT_BOUNDARY_SHARP_ANGLE); + + if (!e->l || e->l == e->l->radial_next) { + newflag |= SCULPT_BOUNDARY_MESH; + } + + if (e->l && e->l != e->l->radial_next && cd_faceset_offset != -1) { + int fset1 = BM_ELEM_CD_GET_INT(e->l->f, cd_faceset_offset); + int fset2 = BM_ELEM_CD_GET_INT(e->l->radial_next->f, cd_faceset_offset); + + newflag |= fset1 != fset2 ? SCULPT_BOUNDARY_FACE_SET : 0; + } + + newflag |= !BM_elem_flag_test(e, BM_ELEM_SMOOTH) ? SCULPT_BOUNDARY_SHARP_MARK : 0; + newflag |= BM_elem_flag_test(e, BM_ELEM_SEAM) ? SCULPT_BOUNDARY_SEAM : 0; + + *BM_ELEM_CD_PTR(e, cd_edge_boundary) = newflag; +} +} // namespace blender::bke::pbvh + +static void pbvh_bmesh_update_uv_boundary_intern(BMVert *v, int cd_boundary, int cd_uv) +{ + int *boundflag = BM_ELEM_CD_PTR(v, cd_boundary); BMEdge *e = v->e; + do { + BMLoop *l = e->l; + if (!l) { + continue; + } + + float snap_limit = blender::bke::sculpt::calc_uv_snap_limit(l, cd_uv); + snap_limit *= snap_limit; + + float2 uv1a = *BM_ELEM_CD_PTR(l->v == v ? l : l->next, cd_uv); + float2 uv1b = *BM_ELEM_CD_PTR(l->v == v ? l->next : l, cd_uv); + + do { + float2 uv2a = *BM_ELEM_CD_PTR(l->v == v ? l : l->next, cd_uv); + float2 uv2b = *BM_ELEM_CD_PTR(l->v == v ? l->next : l, cd_uv); + + if (len_squared_v2v2(uv1a, uv2a) > snap_limit || len_squared_v2v2(uv1b, uv2b) > snap_limit) { + *boundflag |= SCULPT_BOUNDARY_UV; + } + + /* Corners are calculated from the number of distinct charts. */ + BMLoop *l2 = l->v == v ? l : l->next; + if (blender::bke::sculpt::loop_is_corner(l2, cd_uv)) { + *boundflag |= SCULPT_CORNER_UV; + *boundflag |= SCULPT_BOUNDARY_UV; + } + + } while ((l = l->radial_next) != e->l); + } while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e); +} + +static void pbvh_bmesh_update_uv_boundary(BMVert *v, int cd_boundary, const CustomData *ldata) +{ + int base_uv = ldata->typemap[CD_PROP_FLOAT2]; + int newflag = 0; + + *BM_ELEM_CD_PTR(v, cd_boundary) &= ~(SCULPT_BOUNDARY_UPDATE_UV | SCULPT_BOUNDARY_UV | + SCULPT_CORNER_UV); + + for (int i = base_uv; i < ldata->totlayer; i++) { + if (ldata->layers[i].type != CD_PROP_FLOAT2) { + break; + } + if (ldata->layers[i].flag & (CD_FLAG_TEMPORARY | CD_FLAG_ELEM_NOINTERP)) { + continue; + } + + pbvh_bmesh_update_uv_boundary_intern(v, cd_boundary, ldata->layers[i].offset); + } +} + +namespace blender::bke::pbvh { +void update_vert_boundary_bmesh(int cd_faceset_offset, + int cd_vert_node_offset, + int cd_face_node_offset, + int /*cd_vcol*/, + int cd_boundary_flag, + const int cd_flag, + const int cd_valence, + BMVert *v, + const CustomData *ldata, + float sharp_angle_limit) +{ + int newflag = *BM_ELEM_CD_PTR(v, cd_flag); newflag &= ~(SCULPTFLAG_VERT_FSET_HIDDEN | SCULPTFLAG_PBVH_BOUNDARY); + if (BM_ELEM_CD_GET_INT(v, cd_boundary_flag) & SCULPT_BOUNDARY_UPDATE_UV) { + pbvh_bmesh_update_uv_boundary(v, cd_boundary_flag, ldata); + } + + int boundflag = BM_ELEM_CD_GET_INT(v, cd_boundary_flag) & + (SCULPT_BOUNDARY_UV | SCULPT_CORNER_UV); + + BMEdge *e = v->e; if (!e) { boundflag |= SCULPT_BOUNDARY_MESH; BM_ELEM_CD_SET_INT(v, cd_valence, 0); - BM_ELEM_CD_SET_INT(v, cd_boundary_flag, 0); + BM_ELEM_CD_SET_INT(v, cd_boundary_flag, boundflag); *BM_ELEM_CD_PTR(v, cd_flag) = newflag; return; @@ -2040,32 +2162,6 @@ void BKE_pbvh_update_vert_boundary(int cd_faceset_offset, Vector fsets; - float(*lastuv)[2] = do_uvs ? (float(*)[2])BLI_array_alloca(lastuv, totuv) : nullptr; - float(*lastuv2)[2] = do_uvs ? (float(*)[2])BLI_array_alloca(lastuv2, totuv) : nullptr; - - int *disjount_uv_count = do_uvs ? (int *)BLI_array_alloca(disjount_uv_count, totuv) : nullptr; - int *cd_uvs = (int *)BLI_array_alloca(cd_uvs, totuv); - int base_uv_idx = ldata->typemap[CD_PROP_FLOAT2]; - bool uv_first = true; - - if (do_uvs) { - int base = ldata->typemap[CD_PROP_FLOAT2]; - int uv_i = 0; - - for (int i = base; i < ldata->totlayer; i++) { - CustomDataLayer *layer = ldata->layers + i; - if (layer->type != CD_PROP_FLOAT2) { - break; - } - - if (!(layer->flag & CD_FLAG_TEMPORARY)) { - cd_uvs[uv_i] = layer->offset; - disjount_uv_count[uv_i] = 0; - uv_i++; - } - } - } - int sharp_angle_num = 0; do { BMVert *v2 = v == e->v1 ? e->v2 : e->v1; @@ -2075,7 +2171,8 @@ void BKE_pbvh_update_vert_boundary(int cd_faceset_offset, } if (e->l && e->l != e->l->radial_next) { - if (BKE_pbvh_test_sharp_faces_bmesh(e->l->f, e->l->radial_next->f, sharp_angle_limit)) { + if (blender::bke::pbvh::test_sharp_faces_bmesh( + e->l->f, e->l->radial_next->f, sharp_angle_limit)) { boundflag |= SCULPT_BOUNDARY_SHARP_ANGLE; sharp_angle_num++; } @@ -2090,24 +2187,6 @@ void BKE_pbvh_update_vert_boundary(int cd_faceset_offset, } } -#ifdef MV_COLOR_BOUNDARY -# error "fix me! need to handle loops too" -#endif - -#ifdef MV_COLOR_BOUNDARY // CURRENTLY BROKEN - if (cd_vcol >= 0) { - float *color1 = BM_ELEM_CD_PTR(v, cd_vcol); - float *color2 = BM_ELEM_CD_PTR(v2, cd_vcol); - - int colorkey1 = color_boundary_key(color1); - int colorkey2 = color_boundary_key(color2); - - if (colorkey1 != colorkey2) { - boundflag |= SCUKPT_BOUNDARY_FACE_SETS; - } - } -#endif - if (!(e->head.hflag & BM_ELEM_SMOOTH)) { boundflag |= SCULPT_BOUNDARY_SHARP_MARK; sharpcount++; @@ -2118,51 +2197,6 @@ void BKE_pbvh_update_vert_boundary(int cd_faceset_offset, } if (e->l) { - /* detect uv island boundaries */ - if (do_uvs && totuv) { - BMLoop *l_iter = e->l; - do { - BMLoop *l = l_iter->v != v ? l_iter->next : l_iter; - - for (int i = 0; i < totuv; i++) { - float *luv = BM_ELEM_CD_PTR(l, cd_uvs[i]); - - if (uv_first) { - copy_v2_v2(lastuv[i], luv); - copy_v2_v2(lastuv2[i], luv); - - continue; - } - - const float uv_snap_limit = 0.01f * 0.01f; - - float dist = len_squared_v2v2(luv, lastuv[i]); - bool same = dist <= uv_snap_limit; - - bool corner = len_squared_v2v2(lastuv[i], lastuv2[i]) > uv_snap_limit && - len_squared_v2v2(lastuv[i], luv) > uv_snap_limit && - len_squared_v2v2(lastuv2[i], luv) > uv_snap_limit; - corner = blender::bke::sculpt::loop_is_corner(l, cd_uvs[i]); - - if (!same) { - boundflag |= SCULPT_BOUNDARY_UV; - } - - if (corner) { - boundflag |= SCULPT_CORNER_UV; - l->v->head.hflag |= BM_ELEM_SELECT; - } - - if (!same) { - copy_v2_v2(lastuv2[i], lastuv[i]); - copy_v2_v2(lastuv[i], luv); - } - } - - uv_first = false; - } while ((l_iter = l_iter->radial_next) != e->l); - } - if (BM_ELEM_CD_GET_INT(e->l->f, cd_face_node_offset) != ni) { newflag |= SCULPTFLAG_PBVH_BOUNDARY; } @@ -2187,35 +2221,20 @@ void BKE_pbvh_update_vert_boundary(int cd_faceset_offset, if (cd_faceset_offset != -1) { int fset = BM_ELEM_CD_GET_INT(e->l->f, cd_faceset_offset); - - bool ok = true; - for (int i = 0; i < fsets.size(); i++) { - if (fsets[i] == fset) { - ok = false; - } - } - - if (ok) { + if (!fsets.contains(fset)) { fsets.append(fset); } } - // also check e->l->radial_next, in case we are not manifold - // which can mess up the loop order + /* Also check e->l->radial_next, in case we are not manifold + * which can mess up the loop order + */ if (e->l->radial_next != e->l) { if (cd_faceset_offset != -1) { - // fset = abs(BM_ELEM_CD_GET_INT(e->l->radial_next->f, cd_faceset_offset)); - int fset2 = BM_ELEM_CD_GET_INT(e->l->radial_next->f, cd_faceset_offset); + int fset = BM_ELEM_CD_GET_INT(e->l->radial_next->f, cd_faceset_offset); - bool ok2 = true; - for (int i = 0; i < fsets.size(); i++) { - if (fsets[i] == fset2) { - ok2 = false; - } - } - - if (ok2) { - fsets.append(fset2); + if (!fsets.contains(fset)) { + fsets.append(fset); } } @@ -2256,22 +2275,18 @@ void BKE_pbvh_update_vert_boundary(int cd_faceset_offset, boundflag |= SCULPT_CORNER_MESH; } - /* XXX: does this need to be threadsafe? */ BM_ELEM_CD_SET_INT(v, cd_boundary_flag, boundflag); - *(BM_ELEM_CD_PTR(v, cd_flag)) = newflag; BM_ELEM_CD_SET_INT(v, cd_valence, val); + *(BM_ELEM_CD_PTR(v, cd_flag)) = newflag; } -bool BKE_pbvh_check_vert_boundary(PBVH *pbvh, BMVert *v) -{ - return pbvh_check_vert_boundary(pbvh, v); -} - -void BKE_pbvh_sharp_limit_set(PBVH *pbvh, float limit) +void sharp_limit_set(PBVH *pbvh, float limit) { pbvh->sharp_angle_limit = limit; } +} // namespace blender::bke::pbvh + /*Used by symmetrize to update boundary flags*/ void BKE_pbvh_recalc_bmesh_boundary(PBVH *pbvh) { @@ -2279,18 +2294,16 @@ void BKE_pbvh_recalc_bmesh_boundary(PBVH *pbvh) BMIter iter; BM_ITER_MESH (v, &iter, pbvh->header.bm, BM_VERTS_OF_MESH) { - BKE_pbvh_update_vert_boundary(pbvh->cd_faceset_offset, - pbvh->cd_vert_node_offset, - pbvh->cd_face_node_offset, - pbvh->cd_vcol_offset, - pbvh->cd_boundary_flag, - pbvh->cd_flag, - pbvh->cd_valence, - v, - &pbvh->header.bm->ldata, - pbvh->totuv, - true, - pbvh->sharp_angle_limit); + blender::bke::pbvh::update_vert_boundary_bmesh(pbvh->cd_faceset_offset, + pbvh->cd_vert_node_offset, + pbvh->cd_face_node_offset, + pbvh->cd_vcol_offset, + pbvh->cd_boundary_flag, + pbvh->cd_flag, + pbvh->cd_valence, + v, + &pbvh->header.bm->ldata, + pbvh->sharp_angle_limit); } } @@ -2341,6 +2354,7 @@ void BKE_pbvh_build_bmesh(PBVH *pbvh, const int cd_face_node_offset, const int cd_face_areas, const int cd_boundary_flag, + const int cd_edge_boundary, const int /*cd_flag_offset*/, const int /*cd_valence_offset*/, const int cd_origco, @@ -2353,6 +2367,7 @@ void BKE_pbvh_build_bmesh(PBVH *pbvh, pbvh->cd_face_node_offset = cd_face_node_offset; pbvh->cd_vert_mask_offset = CustomData_get_offset(&bm->vdata, CD_PAINT_MASK); pbvh->cd_boundary_flag = cd_boundary_flag; + pbvh->cd_edge_boundary = cd_edge_boundary; pbvh->cd_origco = cd_origco; pbvh->cd_origno = cd_origno; @@ -2360,8 +2375,6 @@ void BKE_pbvh_build_bmesh(PBVH *pbvh, &pbvh->header.bm->vdata, CD_PROP_INT8, ".sculpt_flags"); pbvh->cd_valence = CustomData_get_offset_named( &pbvh->header.bm->vdata, CD_PROP_INT8, ".sculpt_valence"); - pbvh->cd_boundary_flag = CustomData_get_offset_named( - &pbvh->header.bm->vdata, CD_PROP_INT8, ".sculpt_boundary_flags"); pbvh->mesh = me; @@ -2861,28 +2874,6 @@ void BKE_pbvh_bmesh_update_all_valence(PBVH *pbvh) } } -void BKE_pbvh_bmesh_on_mesh_change(PBVH *pbvh) -{ - BMIter iter; - BMVert *v; - - for (int i = 0; i < pbvh->totnode; i++) { - PBVHNode *node = pbvh->nodes + i; - - if (node->flag & PBVH_Leaf) { - node->flag |= PBVH_UpdateTriAreas | PBVH_UpdateCurvatureDir; - } - } - - BM_ITER_MESH (v, &iter, pbvh->header.bm, BM_VERTS_OF_MESH) { - int *flags = BM_ELEM_CD_PTR(v, pbvh->cd_boundary_flag); - *flags |= SCULPT_BOUNDARY_NEEDS_UPDATE; - - dyntopo_add_flag(pbvh, v, SCULPTFLAG_NEED_TRIANGULATE | SCULPTFLAG_NEED_VALENCE); - BKE_pbvh_bmesh_update_valence(pbvh, BKE_pbvh_make_vref((intptr_t)v)); - } -} - bool BKE_pbvh_bmesh_mark_update_valence(PBVH *pbvh, PBVHVertRef vertex) { BMVert *v = (BMVert *)vertex.i; @@ -3708,6 +3699,7 @@ void BKE_pbvh_update_offsets(PBVH *pbvh, const int cd_face_node_offset, const int cd_face_areas, const int cd_boundary_flag, + const int cd_edge_boundary, const int cd_flag, const int cd_valence, const int cd_origco, @@ -3731,6 +3723,8 @@ void BKE_pbvh_update_offsets(PBVH *pbvh, } pbvh->cd_boundary_flag = cd_boundary_flag; + pbvh->cd_edge_boundary = cd_edge_boundary; + pbvh->cd_curvature_dir = cd_curvature_dir; if (pbvh->bm_idmap) { @@ -3887,13 +3881,14 @@ float BKE_pbvh_bmesh_detail_size_avg_get(PBVH *pbvh) namespace blender::bke::pbvh { -void update_sharp_boundary_bmesh(BMVert *v, int cd_boundary_flag, const float sharp_angle_limit) +void update_sharp_vertex_bmesh(BMVert *v, int cd_boundary_flag, const float sharp_angle_limit) { int flag = BM_ELEM_CD_GET_INT(v, cd_boundary_flag); flag &= ~(SCULPT_BOUNDARY_UPDATE_SHARP_ANGLE | SCULPT_BOUNDARY_SHARP_ANGLE | SCULPT_CORNER_SHARP_ANGLE); if (!v->e) { + BM_ELEM_CD_SET_INT(v, cd_boundary_flag, flag); return; } @@ -3905,7 +3900,8 @@ void update_sharp_boundary_bmesh(BMVert *v, int cd_boundary_flag, const float sh continue; } - if (BKE_pbvh_test_sharp_faces_bmesh(e->l->f, e->l->radial_next->f, sharp_angle_limit)) { + if (blender::bke::pbvh::test_sharp_faces_bmesh( + e->l->f, e->l->radial_next->f, sharp_angle_limit)) { flag |= SCULPT_BOUNDARY_SHARP_ANGLE; sharp_num++; } @@ -3918,281 +3914,3 @@ void update_sharp_boundary_bmesh(BMVert *v, int cd_boundary_flag, const float sh BM_ELEM_CD_SET_INT(v, cd_boundary_flag, flag); } } // namespace blender::bke::pbvh - -namespace blender::bke::sculpt { - -bool loop_is_corner(BMLoop *l, int cd_offset) -{ - BMVert *v = l->v; - - float2 value = *BM_ELEM_CD_PTR(l, cd_offset); - float limit = 0.01; - - Vector ls; - Vector keys; - - BMEdge *e = v->e; - do { - BMLoop *l2 = e->l; - - if (!l2) { - continue; - } - - do { - BMLoop *l3 = l2->v == v ? l2 : l2->next; - if (!ls.contains(l3)) { - ls.append(l3); - } - } while ((l2 = l2->radial_next) != e->l); - } while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e); - - int count = 0; - for (BMLoop *l2 : ls) { - float2 value2 = *BM_ELEM_CD_PTR(l2, cd_offset); - float2 dv = value2 - value; - - double f = dv[0] * dv[0] + dv[1] * dv[1]; - int key = int(f * 200.0); - if (!keys.contains(key)) { - keys.append(key); - } - } - - bool ret = keys.size() > 2; - if (ret) { - l->v->head.hflag |= BM_ELEM_SELECT; - } - - return ret; -} - -#if 0 -/* Angle test */ -ATTR_NO_OPT bool loop_is_corner(BMLoop *l, int cd_offset) -{ - BMVert *v = l->v; - BMEdge *e = v->e; - - float2 value = *BM_ELEM_CD_PTR(l, cd_offset); - float limit = 0.01; - - BMLoop *outer1 = nullptr, *outer2 = nullptr; - do { - BMLoop *l2 = e->l; - if (!l2) { - continue; - } - - do { - BMLoop *l3 = l2->v == v ? l2 : l2->next; - float2 value3 = *BM_ELEM_CD_PTR(l3, cd_offset); - - if (!prop_eq(value, value3, limit)) { - continue; - } - - bool outer = true; - - BMLoop *l4 = l2->radial_next; - while (l4 != l2) { - BMLoop *l5 = l4->v == v ? l4 : l4->next; - float2 value5 = *BM_ELEM_CD_PTR(l5, cd_offset); - - if (prop_eq(value, value5, limit)) { - outer = false; - break; - } - l4 = l4->radial_next; - } - - if (outer) { - BMLoop *l3 = l2->v == v ? l2->next : l2; - - if (!outer1) { - outer1 = l3; - } - else if (!outer2 && l3 != outer2) { - outer2 = l3; - } - - break; - } - } while ((l2 = l2->radial_next) != e->l); - } while ((e = BM_DISK_EDGE_NEXT(e, v)) != l->e); - - if (!outer1 || !outer2) { - return outer1 != nullptr; - } - - float2 t1 = *BM_ELEM_CD_PTR(outer1, cd_offset) - value; - float2 t2 = *BM_ELEM_CD_PTR(outer2, cd_offset) - value; - - normalize_v2(t1); - normalize_v2(t2); - float angle_limit = 110.0f / 180.0f * M_PI; - float angle = saacos(dot_v2v2(t1, t2)); - - if (angle < angle_limit) { - l->v->head.hflag |= BM_ELEM_SELECT; - } - - return angle < angle_limit; -} -#endif - -template -static void corner_interp(CustomDataLayer *layer, - BMLoop *l, - Span loops, - Span ws, - int cd_offset, - float factor) -{ - float *ws2 = (float *)BLI_array_alloca(ws2, loops.size() + 1); - T sum = {}; - float totsum = 0.0f; - - T value = *BM_ELEM_CD_PTR(l, cd_offset); - - float limit = 0.0001; - - if (layer->type == CD_PROP_FLOAT2) { - limit = 0.01; - } - else { - /* Do not restrict to islands for non-UVs */ - limit = FLT_MAX; - } - - for (int i : loops.index_range()) { - BMLoop *l2 = loops[i]; - BMLoop *l3 = l2->next->v == l->v ? l2->next : l2->prev; - T value3 = *BM_ELEM_CD_PTR(l3, cd_offset); - - if (prop_eq(value, value3, 0.01)) { - T value2 = *BM_ELEM_CD_PTR(l2, cd_offset); - sum += value2 * ws[i]; - totsum += ws[i]; - } - } - - if (totsum == 0.0f) { - return; - } - - sum /= totsum; - - *BM_ELEM_CD_PTR(l, cd_offset) = value + (sum - value) * factor; -} - -/* Interpolates loops surrounding a vertex, splitting any UV map by - * island as appropriate and enforcing proper boundary conditions. - */ -void interp_face_corners( - PBVH *pbvh, PBVHVertRef vertex, Span loops, Span ws, float factor) -{ - if (BKE_pbvh_type(pbvh) != PBVH_BMESH) { - return; /* Only for PBVH_BMESH. */ - } - - eCustomDataMask mask = CD_MASK_PROP_FLOAT | CD_MASK_PROP_FLOAT2 | CD_MASK_PROP_FLOAT3 | - CD_MASK_PROP_COLOR | CD_MASK_PROP_BYTE_COLOR; - - BMesh *bm = pbvh->header.bm; - BMVert *v = reinterpret_cast(vertex.i); - BMEdge *e = v->e; - BMLoop *l = e->l; - CustomData *cdata = &bm->ldata; - - Vector ls; - - /* Tag loops around vertex. */ - do { - l = e->l; - - if (!l) { - continue; - } - - do { - BMLoop *l2 = l->v == v ? l : l->next; - BM_elem_flag_enable(l2, BM_ELEM_TAG); - } while ((l = l->radial_next) != e->l); - } while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e); - - /* Build loop list. */ - do { - l = e->l; - - if (!l) { - continue; - } - - do { - BMLoop *l2 = l->v == v ? l : l->next; - if (BM_elem_flag_test(l2, BM_ELEM_TAG)) { - BM_elem_flag_disable(l2, BM_ELEM_TAG); - ls.append(l2); - } - } while ((l = l->radial_next) != e->l); - } while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e); - - Vector layers; - for (int layer_i : IndexRange(cdata->totlayer)) { - CustomDataLayer *layer = cdata->layers + layer_i; - eCustomDataType type = eCustomDataType(layer->type); - const int cd_offset = layer->offset; - - if ((layer->flag & CD_FLAG_ELEM_NOINTERP) || !(CD_TYPE_AS_MASK(layer->type) & mask)) { - continue; - } - if (layer->flag & CD_FLAG_TEMPORARY) { - continue; - } - - layers.append(layer); - } - - /* Interpolate. */ - if (loops.size() > 1) { - VertLoopSnapper corner_snap = {Span(ls), Span(layers)}; - - for (CustomDataLayer *layer : layers) { - Vector corners; - - if (layer->type == CD_PROP_FLOAT2) { - for (BMLoop *l : ls) { - corners.append(loop_is_corner(l, layer->offset)); - } - } - - for (int i : ls.index_range()) { - BMLoop *l = ls[i]; - - if (layer->type == CD_PROP_FLOAT2 && corners[i]) { - continue; - } - - switch (layer->type) { - case CD_PROP_FLOAT: - corner_interp(layer, l, loops, ws, layer->offset, factor); - break; - case CD_PROP_FLOAT2: - corner_interp(layer, l, loops, ws, layer->offset, factor); - break; - case CD_PROP_FLOAT3: - corner_interp(layer, l, loops, ws, layer->offset, factor); - break; - case CD_PROP_COLOR: - corner_interp(layer, l, loops, ws, layer->offset, factor); - break; - } - } - } - - /* Snap. */ - // corner_snap.snap(); - } -} -} // namespace blender::bke::sculpt diff --git a/source/blender/blenkernel/intern/pbvh_intern.hh b/source/blender/blenkernel/intern/pbvh_intern.hh index cb5f2444cee..6df1cf181a6 100644 --- a/source/blender/blenkernel/intern/pbvh_intern.hh +++ b/source/blender/blenkernel/intern/pbvh_intern.hh @@ -3,6 +3,7 @@ #pragma once #include "BLI_array.hh" +#include "BLI_bitmap.h" #include "BLI_compiler_compat.h" #include "BLI_ghash.h" #include "BLI_math_vector_types.hh" @@ -169,6 +170,7 @@ struct PBVH { int *prim_indices; int totprim; int totvert; + int totloop; int faces_num; /* Do not use directly, use BKE_pbvh_num_faces. */ int leaf_limit; @@ -184,10 +186,14 @@ struct PBVH { bool *hide_vert; float (*vert_positions)[3]; blender::OffsetIndices polys; + blender::Span edges; bool *hide_poly; /** Only valid for polygon meshes. */ const int *corner_verts; const int *corner_edges; + const bool *sharp_edges; + const bool *seam_edges; + /* Owned by the #PBVH, because after deformations they have to be recomputed. */ const MLoopTri *looptri; blender::Span origco, origno; @@ -260,16 +266,6 @@ struct PBVH { int balance_counter; int stroke_id; - bool is_cached; - - /* This data is for validating cached PBVHs; - * it is not guaranteed to be valid in any way! */ - struct { - CustomData vdata, edata, ldata, pdata; - int totvert, totedge, totloop, totpoly; - struct BMesh *bm; - } cached_data; - bool invalid; blender::GroupedSpan pmap; blender::GroupedSpan vemap; @@ -282,8 +278,8 @@ struct PBVH { /* Used by DynTopo to invalidate the draw cache. */ bool draw_cache_invalid; - int *boundary_flags; - int cd_boundary_flag; + int *boundary_flags, *edge_boundary_flags; + int cd_boundary_flag, cd_edge_boundary; int cd_curvature_dir; PBVHGPUFormat *vbo_id; @@ -291,6 +287,8 @@ struct PBVH { PBVHPixels pixels; bool show_orig; float sharp_angle_limit; + + BLI_bitmap *vert_boundary_map; }; /* pbvh.cc */ @@ -422,27 +420,25 @@ void pbvh_bmesh_check_nodes_simple(PBVH *pbvh); void bke_pbvh_insert_face_finalize(PBVH *pbvh, BMFace *f, const int ni); void bke_pbvh_insert_face(PBVH *pbvh, struct BMFace *f); -BLI_INLINE bool pbvh_check_vert_boundary(PBVH *pbvh, struct BMVert *v) +BLI_INLINE bool pbvh_check_vert_boundary_bmesh(PBVH *pbvh, struct BMVert *v) { int flag = BM_ELEM_CD_GET_INT(v, pbvh->cd_boundary_flag); - if (flag & SCULPT_BOUNDARY_NEEDS_UPDATE) { - BKE_pbvh_update_vert_boundary(pbvh->cd_faceset_offset, - pbvh->cd_vert_node_offset, - pbvh->cd_face_node_offset, - pbvh->cd_vcol_offset, - pbvh->cd_boundary_flag, - pbvh->cd_flag, - pbvh->cd_valence, - v, - &pbvh->header.bm->ldata, - pbvh->totuv, - true, - pbvh->sharp_angle_limit); + if (flag & (SCULPT_BOUNDARY_NEEDS_UPDATE | SCULPT_BOUNDARY_UPDATE_UV)) { + blender::bke::pbvh::update_vert_boundary_bmesh(pbvh->cd_faceset_offset, + pbvh->cd_vert_node_offset, + pbvh->cd_face_node_offset, + pbvh->cd_vcol_offset, + pbvh->cd_boundary_flag, + pbvh->cd_flag, + pbvh->cd_valence, + v, + &pbvh->header.bm->ldata, + pbvh->sharp_angle_limit); return true; } else if (flag & SCULPT_BOUNDARY_UPDATE_SHARP_ANGLE) { - blender::bke::pbvh::update_sharp_boundary_bmesh( + blender::bke::pbvh::update_sharp_vertex_bmesh( v, pbvh->cd_boundary_flag, pbvh->sharp_angle_limit); } @@ -465,13 +461,16 @@ BLI_INLINE bool pbvh_boundary_needs_update_bmesh(PBVH *pbvh, BMVert *v) return *flags & SCULPT_BOUNDARY_NEEDS_UPDATE; } -BLI_INLINE void pbvh_boundary_update_bmesh(PBVH *pbvh, BMVert *v) +template inline void pbvh_boundary_update_bmesh(PBVH *pbvh, T *elem) { - if (pbvh->cd_boundary_flag == -1) { - printf("%s: error!\n", __func__); - return; + int *flags; + + if constexpr (std::is_same_v) { + flags = (int *)BM_ELEM_CD_GET_VOID_P(elem, pbvh->cd_boundary_flag); + } + else { + flags = (int *)BM_ELEM_CD_GET_VOID_P(elem, pbvh->cd_edge_boundary); } - int *flags = (int *)BM_ELEM_CD_GET_VOID_P(v, pbvh->cd_boundary_flag); *flags |= SCULPT_BOUNDARY_NEEDS_UPDATE; } diff --git a/source/blender/bmesh/intern/bmesh_collapse.hh b/source/blender/bmesh/intern/bmesh_collapse.hh index 4063990df53..4ac0501f768 100644 --- a/source/blender/bmesh/intern/bmesh_collapse.hh +++ b/source/blender/bmesh/intern/bmesh_collapse.hh @@ -45,6 +45,10 @@ namespace blender::bmesh { struct NullCollapseCallbacks { inline void on_vert_kill(BMVert *) {} inline void on_edge_kill(BMEdge *) {} + /* Called when verts are combined. */ + inline void on_vert_combine(BMVert * /*dest*/, BMVert * /*source*/) {} + /* Called when edges are combined. */ + inline void on_edge_combine(BMEdge * /*dest*/, BMEdge * /*source*/) {} inline void on_face_kill(BMFace *) {} inline void on_vert_create(BMVert *) {} inline void on_edge_create(BMEdge *) {} @@ -539,6 +543,8 @@ BMVert *join_vert_kill_edge(BMesh *bm, const int dup_tag = _FLAG_OVERLAP; + callbacks.on_vert_combine(v_conn, v_del); + for (int i = 0; i < 2; i++) { BMVert *v = i ? v_del : v_conn; @@ -605,6 +611,8 @@ BMVert *join_vert_kill_edge(BMesh *bm, printf("%s: ERROR!\n", __func__); } + callbacks.on_edge_combine(exist, e); + if (combine_flags) { exist->head.hflag |= e->head.hflag; } diff --git a/source/blender/draw/intern/draw_pbvh.cc b/source/blender/draw/intern/draw_pbvh.cc index 247b1f20a39..a18c14dfae9 100644 --- a/source/blender/draw/intern/draw_pbvh.cc +++ b/source/blender/draw/intern/draw_pbvh.cc @@ -1260,8 +1260,6 @@ struct PBVHBatches { void create_index_bmesh_lines(PBVH_GPU_Args *args) { - printf("%s: Creating line index buffer\n", __func__); - GPUIndexBufBuilder elb_lines; GPU_indexbuf_init(&elb_lines, GPU_PRIM_LINES, args->tribuf->edges.size(), INT_MAX); diff --git a/source/blender/editors/sculpt_paint/paint_mask.cc b/source/blender/editors/sculpt_paint/paint_mask.cc index b68550ee6fc..d1f0a194ce4 100644 --- a/source/blender/editors/sculpt_paint/paint_mask.cc +++ b/source/blender/editors/sculpt_paint/paint_mask.cc @@ -1539,6 +1539,7 @@ static void sculpt_gesture_apply_trim(SculptGestureContext *sgcontext) sgcontext->ss->cd_face_node_offset, sgcontext->ss->cd_face_areas, sgcontext->ss->attrs.boundary_flags->bmesh_cd_offset, + sgcontext->ss->attrs.edge_boundary_flags->bmesh_cd_offset, sgcontext->ss->attrs.flags->bmesh_cd_offset, sgcontext->ss->attrs.valence->bmesh_cd_offset, sgcontext->ss->attrs.orig_co->bmesh_cd_offset, diff --git a/source/blender/editors/sculpt_paint/sculpt.cc b/source/blender/editors/sculpt_paint/sculpt.cc index 1978f362097..c3f94f61731 100644 --- a/source/blender/editors/sculpt_paint/sculpt.cc +++ b/source/blender/editors/sculpt_paint/sculpt.cc @@ -6860,38 +6860,7 @@ struct SculptTopologyIDFloodFillData { void SCULPT_boundary_info_ensure(Object *object) { - using namespace blender; - SculptSession *ss = object->sculpt; - - /* PBVH_BMESH now handles boundaries itself. */ - if (ss->bm || ss->vertex_info.boundary) { - return; - } - - Mesh *base_mesh = BKE_mesh_from_object(object); - const blender::Span edges = base_mesh->edges(); - const OffsetIndices polys = base_mesh->polys(); - const Span corner_edges = base_mesh->corner_edges(); - - ss->vertex_info.boundary = BLI_BITMAP_NEW(base_mesh->totvert, "Boundary info"); - int *adjacent_faces_edge_count = static_cast( - MEM_calloc_arrayN(base_mesh->totedge, sizeof(int), "Adjacent face edge count")); - - for (const int i : polys.index_range()) { - for (const int edge : corner_edges.slice(polys[i])) { - adjacent_faces_edge_count[edge]++; - } - } - - for (const int e : edges.index_range()) { - if (adjacent_faces_edge_count[e] < 2) { - const int2 &edge = edges[e]; - BLI_BITMAP_SET(ss->vertex_info.boundary, edge[0], true); - BLI_BITMAP_SET(ss->vertex_info.boundary, edge[1], true); - } - } - - MEM_freeN(adjacent_faces_edge_count); + blender::bke::sculpt::sculpt_vert_boundary_ensure(object); } void SCULPT_ensure_vemap(SculptSession *ss) diff --git a/source/blender/editors/sculpt_paint/sculpt_api.cc b/source/blender/editors/sculpt_paint/sculpt_api.cc index dfdf22c2b21..ec6e9b20bde 100644 --- a/source/blender/editors/sculpt_paint/sculpt_api.cc +++ b/source/blender/editors/sculpt_paint/sculpt_api.cc @@ -125,44 +125,6 @@ using blender::OffsetIndices; using namespace blender::bke::paint; -/** - * Checks if the face sets of the adjacent faces to the edge between \a v1 and \a v2 - * in the base mesh are equal. - */ -static bool sculpt_check_unique_face_set_for_edge_in_base_mesh(const SculptSession *ss, - int v1, - int v2) -{ - if (!ss->face_sets) { - return true; - } - - int p1 = -1, p2 = -1; - for (int poly : ss->pmap[v1]) { - const IndexRange p = ss->polys[poly]; - - for (int l = 0; l < p.size(); l++) { - const int *corner_verts = &ss->corner_verts[p.start() + l]; - if (*corner_verts == v2) { - if (p1 == -1) { - p1 = poly; - break; - } - - if (p2 == -1) { - p2 = poly; - break; - } - } - } - } - - if (p1 != -1 && p2 != -1) { - return abs(ss->face_sets[p1]) == (ss->face_sets[p2]); - } - return true; -} - static bool sculpt_check_boundary_vertex_in_base_mesh(const SculptSession *ss, int index) { BLI_assert(ss->vertex_info.boundary); @@ -170,160 +132,78 @@ static bool sculpt_check_boundary_vertex_in_base_mesh(const SculptSession *ss, i return BLI_BITMAP_TEST(ss->vertex_info.boundary, index); } -static bool sculpt_check_unique_face_set_in_base_mesh(const SculptSession *ss, - int vertex, - bool *r_corner) -{ - if (!ss->attrs.face_set) { - return true; - } - int fset1 = -1, fset2 = -1, fset3 = -1; - - for (int poly : ss->pmap[vertex]) { - PBVHFaceRef face = {poly}; - int fset = face_attr_get(face, ss->attrs.face_set); - - if (fset1 == -1) { - fset1 = fset; - } - else if (fset2 == -1 && fset != fset1) { - fset2 = fset; - } - else if (fset3 == -1 && fset != fset1 && fset != fset2) { - fset3 = fset; - } - } - - *r_corner = fset3 != -1; - return fset2 == -1; -} - eSculptBoundary SCULPT_edge_is_boundary(const SculptSession *ss, const PBVHEdgeRef edge, eSculptBoundary typemask) { + int oldflag = blender::bke::paint::edge_attr_get(edge, ss->attrs.edge_boundary_flags); + bool update = oldflag & (SCULPT_BOUNDARY_NEEDS_UPDATE | SCULPT_BOUNDARY_UPDATE_UV | + SCULPT_BOUNDARY_UPDATE_SHARP_ANGLE); - int ret = 0; - - switch (BKE_pbvh_type(ss->pbvh)) { - case PBVH_BMESH: { - BMEdge *e = (BMEdge *)edge.i; - - if (e->l && e->l != e->l->radial_next && typemask & SCULPT_BOUNDARY_SHARP_ANGLE) { - if (BKE_pbvh_test_sharp_faces_bmesh(e->l->f, e->l->radial_next->f, ss->sharp_angle_limit)) - { - ret |= SCULPT_BOUNDARY_SHARP_ANGLE; - } + if (update) { + switch (BKE_pbvh_type(ss->pbvh)) { + case PBVH_BMESH: { + BMEdge *e = reinterpret_cast(edge.i); + blender::bke::pbvh::update_edge_boundary_bmesh( + e, + ss->attrs.face_set->bmesh_cd_offset, + ss->attrs.edge_boundary_flags->bmesh_cd_offset, + ss->attrs.flags->bmesh_cd_offset, + ss->attrs.valence->bmesh_cd_offset, + &ss->bm->ldata, + ss->sharp_angle_limit); + break; } + case PBVH_FACES: { + Span pos = {reinterpret_cast(ss->vert_positions), ss->totvert}; + Span nor = {reinterpret_cast(ss->vert_normals), ss->totvert}; - if (typemask & SCULPT_BOUNDARY_MESH) { - ret |= (!e->l || e->l == e->l->radial_next) ? SCULPT_BOUNDARY_MESH : 0; + blender::bke::pbvh::update_edge_boundary_faces(edge.i, + pos, + nor, + ss->edges, + ss->polys, + ss->poly_normals, + (int *)ss->attrs.edge_boundary_flags->data, + (int *)ss->attrs.boundary_flags->data, + (int *)ss->attrs.face_set->data, + ss->sharp_edge, + ss->seam_edge, + ss->pmap, + ss->epmap, + ss->ldata, + ss->sharp_angle_limit, + ss->corner_verts, + ss->corner_edges); + break; } + case PBVH_GRIDS: { + const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh); - if ((typemask & SCULPT_BOUNDARY_FACE_SET) && e->l && e->l != e->l->radial_next && - ss->cd_faceset_offset != -1) - { - int fset1 = BM_ELEM_CD_GET_INT(e->l->f, ss->cd_faceset_offset); - int fset2 = BM_ELEM_CD_GET_INT(e->l->radial_next->f, ss->cd_faceset_offset); + blender::bke::pbvh::update_edge_boundary_grids(edge.i, + ss->edges, + ss->polys, + (int *)ss->attrs.edge_boundary_flags->data, + (int *)ss->attrs.boundary_flags->data, + (int *)ss->attrs.face_set->data, + ss->sharp_edge, + ss->seam_edge, + ss->pmap, + ss->epmap, + ss->ldata, + ss->subdiv_ccg, + key, + ss->sharp_angle_limit, + ss->corner_verts, + ss->corner_edges); - ret |= fset1 != fset2 ? SCULPT_BOUNDARY_FACE_SET : 0; + break; } - - if (e->l && (typemask & SCULPT_BOUNDARY_UV) && ss->bm->ldata.typemap[CD_PROP_FLOAT2] != -1) { - CustomData *ldata = &ss->bm->ldata; - int base = ldata->typemap[CD_PROP_FLOAT2]; - - for (int i = base; i < ldata->totlayer; i++) { - CustomDataLayer &layer = ldata->layers[i]; - if (layer.type != CD_PROP_FLOAT2) { - break; - } - if (layer.flag & CD_FLAG_TEMPORARY) { - continue; - } - - BMLoop *l = e->l; - - int cd_uv = layer.offset; - float limit = 0.01; - - float2 a1 = BM_ELEM_CD_PTR((l->v == e->v1 ? l : l->next), cd_uv); - float2 a2 = BM_ELEM_CD_PTR((l->v == e->v2 ? l : l->next), cd_uv); - - do { - float *b1 = BM_ELEM_CD_PTR((l->v == e->v1 ? l : l->next), cd_uv); - float *b2 = BM_ELEM_CD_PTR((l->v == e->v2 ? l : l->next), cd_uv); - - if (len_v2v2(a1, b1) > limit || len_v2v2(a2, b2) > limit) { - ret |= SCULPT_BOUNDARY_UV; - //e->head.hflag |= BM_ELEM_SELECT; - goto uv_outer; - } - } while ((l = l->radial_next) != e->l); - } - } - uv_outer: - - if (typemask & SCULPT_BOUNDARY_SHARP_MARK) { - ret |= !BM_elem_flag_test(e, BM_ELEM_SMOOTH) ? SCULPT_BOUNDARY_SHARP_MARK : 0; - } - - if (typemask & SCULPT_BOUNDARY_SEAM) { - ret |= BM_elem_flag_test(e, BM_ELEM_SEAM) ? SCULPT_BOUNDARY_SEAM : 0; - } - - break; - } - case PBVH_FACES: { - eSculptBoundary epmap_mask = typemask & (SCULPT_BOUNDARY_MESH | SCULPT_BOUNDARY_FACE_SET); - - /* Some boundary types require an edge->poly map to be fully accurate. */ - if (epmap_mask && !ss->epmap.is_empty()) { - if (ss->face_sets && epmap_mask & SCULPT_BOUNDARY_FACE_SET) { - int fset = -1; - - for (int poly : ss->epmap[edge.i]) { - if (fset == -1) { - fset = ss->face_sets[poly]; - } - else if (ss->face_sets[poly] != fset) { - ret |= SCULPT_BOUNDARY_FACE_SET; - break; - } - } - } - if (epmap_mask & SCULPT_BOUNDARY_MESH) { - ret |= ss->epmap[edge.i].size() == 1 ? SCULPT_BOUNDARY_MESH : 0; - } - } - else if (epmap_mask) - { /* No edge->poly map; approximate from vertices (will give artifacts on corners). */ - PBVHVertRef v1, v2; - - SCULPT_edge_get_verts(ss, edge, &v1, &v2); - eSculptBoundary a = SCULPT_vertex_is_boundary(ss, v1, epmap_mask); - eSculptBoundary b = SCULPT_vertex_is_boundary(ss, v2, epmap_mask); - - ret |= a & b; - } - - if (typemask & SCULPT_BOUNDARY_SHARP_MARK) { - ret |= (ss->sharp_edge && ss->sharp_edge[edge.i]) ? SCULPT_BOUNDARY_SHARP_MARK : 0; - } - - if (typemask & SCULPT_BOUNDARY_SEAM) { - ret |= (ss->seam_edge && ss->seam_edge[edge.i]) ? SCULPT_BOUNDARY_SEAM : 0; - } - - break; - } - case PBVH_GRIDS: { - // not implemented - break; } } - return (eSculptBoundary)ret; + return eSculptBoundary( + blender::bke::paint::edge_attr_get(edge, ss->attrs.edge_boundary_flags)); } void SCULPT_edge_get_verts(const SculptSession *ss, @@ -364,53 +244,7 @@ PBVHVertRef SCULPT_edge_other_vertex(const SculptSession *ss, static void grids_update_boundary_flags(const SculptSession *ss, PBVHVertRef vertex) { - int *flag = vertex_attr_ptr(vertex, ss->attrs.boundary_flags); - - *flag = 0; - - int index = (int)vertex.i; - const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh); - const int grid_index = index / key->grid_area; - const int vertex_index = index - grid_index * key->grid_area; - SubdivCCGCoord coord{}; - - coord.grid_index = grid_index; - coord.x = vertex_index % key->grid_size; - coord.y = vertex_index / key->grid_size; - - int v1, v2; - const SubdivCCGAdjacencyType adjacency = BKE_subdiv_ccg_coarse_mesh_adjacency_info_get( - ss->subdiv_ccg, &coord, ss->corner_verts, ss->polys, &v1, &v2); - - bool fset_corner = false; - switch (adjacency) { - case SUBDIV_CCG_ADJACENT_VERTEX: - if (!sculpt_check_unique_face_set_in_base_mesh(ss, v1, &fset_corner)) { - *flag |= SCULPT_BOUNDARY_FACE_SET; - } - if (sculpt_check_boundary_vertex_in_base_mesh(ss, v1)) { - *flag |= SCULPT_BOUNDARY_MESH; - } - break; - case SUBDIV_CCG_ADJACENT_EDGE: { - if (!sculpt_check_unique_face_set_for_edge_in_base_mesh(ss, v1, v2)) { - *flag |= SCULPT_BOUNDARY_FACE_SET; - } - - if (sculpt_check_boundary_vertex_in_base_mesh(ss, v1) && - sculpt_check_boundary_vertex_in_base_mesh(ss, v2)) - { - *flag |= SCULPT_BOUNDARY_MESH; - } - break; - } - case SUBDIV_CCG_ADJACENT_NONE: - break; - } - - if (fset_corner) { - *flag |= SCULPT_CORNER_FACE_SET | SCULPT_BOUNDARY_FACE_SET; - } + blender::bke::pbvh::update_vert_boundary_grids(ss->pbvh, vertex.i); } static void faces_update_boundary_flags(const SculptSession *ss, const PBVHVertRef vertex) @@ -423,7 +257,6 @@ static void faces_update_boundary_flags(const SculptSession *ss, const PBVHVertR ss->corner_verts.data(), ss->corner_edges.data(), ss->polys, - ss->totfaces, ss->pmap, vertex, ss->sharp_edge, @@ -465,30 +298,28 @@ static bool sculpt_vertex_ensure_boundary(const SculptSession *ss, int mask) { eSculptBoundary flag = *vertex_attr_ptr(vertex, ss->attrs.boundary_flags); - bool needs_update = flag & SCULPT_BOUNDARY_NEEDS_UPDATE; + bool needs_update = flag & (SCULPT_BOUNDARY_NEEDS_UPDATE | SCULPT_BOUNDARY_UPDATE_UV); switch (BKE_pbvh_type(ss->pbvh)) { case PBVH_BMESH: { BMVert *v = reinterpret_cast(vertex.i); if (needs_update) { - BKE_pbvh_update_vert_boundary(ss->cd_faceset_offset, - ss->cd_vert_node_offset, - ss->cd_face_node_offset, - ss->cd_vcol_offset, - ss->attrs.boundary_flags->bmesh_cd_offset, - ss->attrs.flags->bmesh_cd_offset, - ss->attrs.valence->bmesh_cd_offset, - (BMVert *)vertex.i, - &ss->bm->ldata, - ss->totuv, - true, - ss->sharp_angle_limit); + blender::bke::pbvh::update_vert_boundary_bmesh(ss->cd_faceset_offset, + ss->cd_vert_node_offset, + ss->cd_face_node_offset, + ss->cd_vcol_offset, + ss->attrs.boundary_flags->bmesh_cd_offset, + ss->attrs.flags->bmesh_cd_offset, + ss->attrs.valence->bmesh_cd_offset, + (BMVert *)vertex.i, + &ss->bm->ldata, + ss->sharp_angle_limit); } else if ((mask & (SCULPT_BOUNDARY_SHARP_ANGLE | SCULPT_CORNER_SHARP_ANGLE)) && flag & SCULPT_BOUNDARY_UPDATE_SHARP_ANGLE) { - blender::bke::pbvh::update_sharp_boundary_bmesh( + blender::bke::pbvh::update_sharp_vertex_bmesh( v, ss->attrs.boundary_flags->bmesh_cd_offset, ss->sharp_angle_limit); } diff --git a/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc b/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc index 4c42e862a3b..6b5a0668246 100644 --- a/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc +++ b/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc @@ -250,7 +250,8 @@ void SCULPT_update_all_valence_boundary(Object *ob) BM_ITER_MESH (v, &iter, ss->bm, BM_VERTS_OF_MESH) { *BM_ELEM_CD_PTR(v, cd_flag) = SCULPTFLAG_NEED_TRIANGULATE; BM_ELEM_CD_SET_INT(v, cd_valence, BM_vert_edge_count(v)); - BM_ELEM_CD_SET_INT(v, cd_boundary, SCULPT_BOUNDARY_NEEDS_UPDATE); + *BM_ELEM_CD_PTR(v, cd_boundary) |= SCULPT_BOUNDARY_NEEDS_UPDATE | + SCULPT_BOUNDARY_UPDATE_SHARP_ANGLE; /* Update boundary if we have a pbvh. */ if (ss->pbvh) { @@ -270,9 +271,7 @@ void SCULPT_update_all_valence_boundary(Object *ob) PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); blender::bke::paint::vertex_attr_set( - vertex, - ss->attrs.flags, - SCULPTFLAG_NEED_VALENCE | SCULPTFLAG_NEED_TRIANGULATE ); + vertex, ss->attrs.flags, SCULPTFLAG_NEED_VALENCE | SCULPTFLAG_NEED_TRIANGULATE); BKE_sculpt_boundary_flag_update(ss, vertex); SCULPT_vertex_valence_get(ss, vertex); SCULPT_vertex_is_boundary(ss, vertex, SCULPT_BOUNDARY_ALL); @@ -290,10 +289,10 @@ static void SCULPT_dynamic_topology_disable_ex( Mesh *me = static_cast(ob->data); /* Destroy temporary layers. */ - //BKE_sculpt_attribute_destroy_temporary_all(ob); + BKE_sculpt_attribute_destroy_temporary_all(ob); if (ss->attrs.dyntopo_node_id_vertex) { - //BKE_sculpt_attribute_destroy(ob, ss->attrs.dyntopo_node_id_vertex); + BKE_sculpt_attribute_destroy(ob, ss->attrs.dyntopo_node_id_vertex); } if (ss->attrs.dyntopo_node_id_face) { diff --git a/source/blender/editors/sculpt_paint/sculpt_smooth.cc b/source/blender/editors/sculpt_paint/sculpt_smooth.cc index 8d20a3fab6a..d557ce52023 100644 --- a/source/blender/editors/sculpt_paint/sculpt_smooth.cc +++ b/source/blender/editors/sculpt_paint/sculpt_smooth.cc @@ -111,7 +111,7 @@ static void SCULPT_neighbor_coords_average_interior_ex(SculptSession *ss, } #endif - //bound_type |= SCULPT_BOUNDARY_UV; + bound_type |= SCULPT_BOUNDARY_UV; const eSculptBoundary is_boundary = SCULPT_vertex_is_boundary(ss, vertex, bound_type); const eSculptCorner is_corner = SCULPT_vertex_is_corner(ss, vertex, corner_type); diff --git a/source/blender/editors/sculpt_paint/sculpt_undo.cc b/source/blender/editors/sculpt_paint/sculpt_undo.cc index c1f43672181..8efc0d29a87 100644 --- a/source/blender/editors/sculpt_paint/sculpt_undo.cc +++ b/source/blender/editors/sculpt_paint/sculpt_undo.cc @@ -906,8 +906,8 @@ static void bmesh_undo_on_face_change(BMFace *f, BMLoop *l = f->l_first; do { - int flag = BM_ELEM_CD_GET_INT(l->v, data->cd_boundary_flag); - BM_ELEM_CD_SET_INT(l->v, data->cd_boundary_flag, flag | SCULPT_BOUNDARY_NEEDS_UPDATE); + *BM_ELEM_CD_PTR(l->v, data->cd_boundary_flag) |= SCULPT_BOUNDARY_NEEDS_UPDATE | + SCULPT_BOUNDARY_UPDATE_SHARP_ANGLE; } while ((l = l->next) != f->l_first); // attempt to find old node in old_customdata diff --git a/source/blender/makesdna/DNA_scene_enums.h b/source/blender/makesdna/DNA_scene_enums.h index 622444e5857..301ce49287f 100644 --- a/source/blender/makesdna/DNA_scene_enums.h +++ b/source/blender/makesdna/DNA_scene_enums.h @@ -28,8 +28,9 @@ typedef enum eSculptBoundary { SCULPT_BOUNDARY_UV = 1 << 5, SCULPT_BOUNDARY_NEEDS_UPDATE = 1 << 6, SCULPT_BOUNDARY_UPDATE_SHARP_ANGLE = 1 << 7, + SCULPT_BOUNDARY_UPDATE_UV = 1 << 8, - SCULPT_BOUNDARY_ALL = (1 << 0) | (1 << 1) | (1 << 2) | (1 << 3) | (1 << 4), + SCULPT_BOUNDARY_ALL = (1 << 0) | (1 << 1) | (1 << 2) | (1 << 3) | (1 << 4) | (1 << 5), SCULPT_BOUNDARY_DEFAULT = (1 << 0) | (1 << 3) | (1 << 4) // mesh and sharp } eSculptBoundary; ENUM_OPERATORS(eSculptBoundary, SCULPT_BOUNDARY_UPDATE_SHARP_ANGLE); -- 2.30.2 From 24f23f42b49868016ae2a8b405224c0750fc8601 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Fri, 9 Jun 2023 01:38:26 -0700 Subject: [PATCH 167/279] temp-sculpt-dyntopo: Add missing break statements --- source/blender/blenkernel/intern/paint.cc | 2 ++ source/blender/blenkernel/intern/pbvh_bmesh.cc | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/source/blender/blenkernel/intern/paint.cc b/source/blender/blenkernel/intern/paint.cc index 38d11a84b04..3b2a94a0e7d 100644 --- a/source/blender/blenkernel/intern/paint.cc +++ b/source/blender/blenkernel/intern/paint.cc @@ -3402,6 +3402,7 @@ static bool sculpt_attribute_create(SculptSession *ss, break; case ATTR_DOMAIN_EDGE: cdata = &ss->bm->edata; + break; case ATTR_DOMAIN_FACE: cdata = &ss->bm->pdata; break; @@ -3442,6 +3443,7 @@ static bool sculpt_attribute_create(SculptSession *ss, break; case ATTR_DOMAIN_EDGE: cdata = &me->edata; + break; default: out->used = false; return false; diff --git a/source/blender/blenkernel/intern/pbvh_bmesh.cc b/source/blender/blenkernel/intern/pbvh_bmesh.cc index 991356c99e9..603cfe38ad9 100644 --- a/source/blender/blenkernel/intern/pbvh_bmesh.cc +++ b/source/blender/blenkernel/intern/pbvh_bmesh.cc @@ -2109,6 +2109,10 @@ static void pbvh_bmesh_update_uv_boundary(BMVert *v, int cd_boundary, const Cust *BM_ELEM_CD_PTR(v, cd_boundary) &= ~(SCULPT_BOUNDARY_UPDATE_UV | SCULPT_BOUNDARY_UV | SCULPT_CORNER_UV); + if (base_uv == -1) { + return; + } + for (int i = base_uv; i < ldata->totlayer; i++) { if (ldata->layers[i].type != CD_PROP_FLOAT2) { break; -- 2.30.2 From 3a7d6d41d75b9f2f0e56265dc8b4ea108d210feb Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Fri, 9 Jun 2023 04:04:02 -0700 Subject: [PATCH 168/279] temp-sculpt-dyntopo: Fix boundery smoothing and undo bugs * Smoothing should prioritize hard edges over smooth ones. * Undo wasn't properly setting SCULPTFLAG_NEED_TRIANGULATE flags. --- source/blender/blenkernel/intern/dyntopo.cc | 4 ++ .../blenkernel/intern/dyntopo_collapse.cc | 10 ++++- source/blender/blenkernel/intern/paint.cc | 2 +- .../blender/blenkernel/intern/pbvh_bmesh.cc | 4 ++ .../editors/sculpt_paint/sculpt_geodesic.cc | 2 +- .../editors/sculpt_paint/sculpt_smooth.cc | 20 ++++++--- .../editors/sculpt_paint/sculpt_undo.cc | 43 +++++++++---------- 7 files changed, 52 insertions(+), 33 deletions(-) diff --git a/source/blender/blenkernel/intern/dyntopo.cc b/source/blender/blenkernel/intern/dyntopo.cc index 66ecd395fb0..7e4fb191d35 100644 --- a/source/blender/blenkernel/intern/dyntopo.cc +++ b/source/blender/blenkernel/intern/dyntopo.cc @@ -2309,6 +2309,10 @@ void EdgeQueueContext::step() RandomNumberGenerator srand(PIL_check_seconds_timer() * 10000); auto do_smooth = [&](BMVert *v) { + if (!surface_relax) { + return; + } + float prob = ops[curop] == PBVH_Subdivide ? 0.25 : 0.75; if (srand.get_float() > prob) { surface_smooth_v_safe(ss, pbvh, v, surface_smooth_fac, true); diff --git a/source/blender/blenkernel/intern/dyntopo_collapse.cc b/source/blender/blenkernel/intern/dyntopo_collapse.cc index b6c967b2c69..7ba81df3459 100644 --- a/source/blender/blenkernel/intern/dyntopo_collapse.cc +++ b/source/blender/blenkernel/intern/dyntopo_collapse.cc @@ -803,7 +803,15 @@ BMVert *pbvh_bmesh_collapse_edge( bool corner1 = (boundflag1 & SCULPTVERT_ALL_CORNER) || w1 >= 0.85; bool corner2 = (boundflag2 & SCULPTVERT_ALL_CORNER) || w2 >= 0.85; - if (corner1 && corner2) { + /* We allow two corners of the example sampe type[s] to collapse */ + if ((boundflag1 & SCULPTVERT_ALL_CORNER) && + (boundflag1 & SCULPTVERT_ALL_CORNER) != (boundflag2 & SCULPTVERT_ALL_CORNER)) + { + bm_logstack_pop(); + return nullptr; + } + + if (w1 >= 0.85 && w2 >= 0.85) { bm_logstack_pop(); return nullptr; } diff --git a/source/blender/blenkernel/intern/paint.cc b/source/blender/blenkernel/intern/paint.cc index 3b2a94a0e7d..d4653e762f8 100644 --- a/source/blender/blenkernel/intern/paint.cc +++ b/source/blender/blenkernel/intern/paint.cc @@ -2949,7 +2949,7 @@ PBVH *BKE_sculpt_object_pbvh_ensure(Depsgraph *depsgraph, Object *ob) /* Forcibly flag all boundaries for update. */ sculpt_boundary_flags_ensure( - ob, pbvh, BKE_sculptsession_vertex_count(ss), ss->bm->totedge, true); + ob, pbvh, BKE_sculptsession_vertex_count(ss), ss->bm ? ss->bm->totedge : ss->totedges, true); sculpt_attribute_update_refs(ob); diff --git a/source/blender/blenkernel/intern/pbvh_bmesh.cc b/source/blender/blenkernel/intern/pbvh_bmesh.cc index 603cfe38ad9..d896600bca9 100644 --- a/source/blender/blenkernel/intern/pbvh_bmesh.cc +++ b/source/blender/blenkernel/intern/pbvh_bmesh.cc @@ -2069,6 +2069,10 @@ static void pbvh_bmesh_update_uv_boundary_intern(BMVert *v, int cd_boundary, int { int *boundflag = BM_ELEM_CD_PTR(v, cd_boundary); + if (!v->e) { + return; + } + BMEdge *e = v->e; do { BMLoop *l = e->l; diff --git a/source/blender/editors/sculpt_paint/sculpt_geodesic.cc b/source/blender/editors/sculpt_paint/sculpt_geodesic.cc index 73ae70ff757..f70ed258293 100644 --- a/source/blender/editors/sculpt_paint/sculpt_geodesic.cc +++ b/source/blender/editors/sculpt_paint/sculpt_geodesic.cc @@ -547,7 +547,7 @@ static float *SCULPT_geodesic_bmesh_create(Object *ob, int v1_i = BM_elem_index_get(e->v1); int v2_i = BM_elem_index_get(e->v2); - if (dists[v1_i] == FLT_MAX || dists[v2_i] == FLT_MAX) { + if ((dists[v1_i] == FLT_MAX || dists[v2_i] == FLT_MAX) && v1 && v2) { if (dists[v1_i] > dists[v2_i]) { SWAP(BMVert *, v1, v2); SWAP(int, v1_i, v2_i); diff --git a/source/blender/editors/sculpt_paint/sculpt_smooth.cc b/source/blender/editors/sculpt_paint/sculpt_smooth.cc index 0222adacc4d..e0922ea076e 100644 --- a/source/blender/editors/sculpt_paint/sculpt_smooth.cc +++ b/source/blender/editors/sculpt_paint/sculpt_smooth.cc @@ -112,7 +112,9 @@ static void SCULPT_neighbor_coords_average_interior_ex(SculptSession *ss, } #endif - bound_type |= SCULPT_BOUNDARY_UV; + eSculptBoundary uvflag = ss->reproject_smooth ? SCULPT_BOUNDARY_UV : SCULPT_BOUNDARY_NONE; + + bound_type |= uvflag; const eSculptBoundary is_boundary = SCULPT_vertex_is_boundary(ss, vertex, bound_type); const eSculptCorner is_corner = SCULPT_vertex_is_corner(ss, vertex, corner_type); @@ -180,10 +182,9 @@ static void SCULPT_neighbor_coords_average_interior_ex(SculptSession *ss, is_boundary2 = SCULPT_vertex_is_boundary(ss, ni.vertex, bound_type); } - const eSculptBoundary smooth_types = !ss->hard_edge_mode ? - SCULPT_BOUNDARY_FACE_SET | SCULPT_BOUNDARY_UV | - SCULPT_BOUNDARY_SEAM : - SCULPT_BOUNDARY_UV | SCULPT_BOUNDARY_SEAM; + const eSculptBoundary smooth_types = !ss->hard_edge_mode ? SCULPT_BOUNDARY_FACE_SET | uvflag | + SCULPT_BOUNDARY_SEAM : + uvflag | SCULPT_BOUNDARY_SEAM; if (is_boundary2) { totboundary++; @@ -193,8 +194,13 @@ static void SCULPT_neighbor_coords_average_interior_ex(SculptSession *ss, if (is_boundary) { project_ok = false; + eSculptBoundary smooth_flag = is_boundary & smooth_types; + eSculptBoundary smooth_flag2 = is_boundary2 & smooth_types; + /* Handle smooth boundaries. */ - if (bool(is_boundary2 & smooth_types) != bool(is_boundary & smooth_types)) { + if (bool(smooth_flag) != bool(smooth_flag2) && + (smooth_flag == is_boundary && smooth_flag2 == is_boundary2)) + { /* Project to plane. */ float3 t1 = float3(vertex_co_get(ss, ni.vertex)) - co; float fac = dot_v3v3(t1, no); @@ -207,7 +213,7 @@ static void SCULPT_neighbor_coords_average_interior_ex(SculptSession *ss, avg += tco * w; total += w; } - else if (is_boundary & is_boundary2) { + else if ((is_boundary & ~smooth_flag) & (is_boundary2 & ~smooth_flag2)) { avg += float3(vertex_co_get(ss, ni.vertex)) * w; total += w; project_ok = true; diff --git a/source/blender/editors/sculpt_paint/sculpt_undo.cc b/source/blender/editors/sculpt_paint/sculpt_undo.cc index 978db14981e..9bd3cc18d94 100644 --- a/source/blender/editors/sculpt_paint/sculpt_undo.cc +++ b/source/blender/editors/sculpt_paint/sculpt_undo.cc @@ -696,7 +696,7 @@ typedef struct BmeshUndoData { bool balance_pbvh; int cd_face_node_offset, cd_vert_node_offset; int cd_face_node_offset_old, cd_vert_node_offset_old; - int cd_boundary_flag, cd_flags; + int cd_boundary_flag, cd_flags, cd_edge_boundary; bool regen_all_unique_verts; bool is_redo; } BmeshUndoData; @@ -773,7 +773,6 @@ static void bmesh_undo_on_face_kill(BMFace *f, void *userdata) static void bmesh_undo_on_face_add(BMFace *f, void *userdata) { BmeshUndoData *data = (BmeshUndoData *)userdata; - // data->do_full_recalc = true; BM_ELEM_CD_SET_INT(f, data->cd_face_node_offset, -1); BKE_pbvh_bmesh_add_face(data->pbvh, f, false, true); @@ -784,6 +783,8 @@ static void bmesh_undo_on_face_add(BMFace *f, void *userdata) BMLoop *l = f->l_first; do { bmesh_undo_vert_update(data, l->v, f->len > 3); + *BM_ELEM_CD_PTR(l->e, data->cd_edge_boundary) |= SCULPT_BOUNDARY_NEEDS_UPDATE | + SCULPT_BOUNDARY_UPDATE_SHARP_ANGLE; int ni_l = BM_ELEM_CD_GET_INT(l->v, data->cd_vert_node_offset); if (ni_l < 0 && ni >= 0) { @@ -835,6 +836,9 @@ static void bmesh_undo_on_edge_add(BMEdge *e, void *userdata) { BmeshUndoData *data = (BmeshUndoData *)userdata; + *BM_ELEM_CD_PTR(e, data->cd_edge_boundary) |= SCULPT_BOUNDARY_NEEDS_UPDATE | + SCULPT_BOUNDARY_UPDATE_SHARP_ANGLE; + bmesh_undo_vert_update(data, e->v1, true); bmesh_undo_vert_update(data, e->v2, true); } @@ -843,7 +847,7 @@ static void bmesh_undo_on_vert_change(BMVert *v, void *userdata, void *old_custo { BmeshUndoData *data = (BmeshUndoData *)userdata; - bmesh_undo_vert_update(data, v, false); + bmesh_undo_vert_update(data, v, true); if (!old_customdata) { BM_ELEM_CD_SET_INT(v, data->cd_vert_node_offset, -1); @@ -907,6 +911,8 @@ static void bmesh_undo_on_face_change(BMFace *f, BMLoop *l = f->l_first; do { + *BM_ELEM_CD_PTR(l->e, data->cd_edge_boundary) |= SCULPT_BOUNDARY_NEEDS_UPDATE | + SCULPT_BOUNDARY_UPDATE_SHARP_ANGLE; *BM_ELEM_CD_PTR(l->v, data->cd_boundary_flag) |= SCULPT_BOUNDARY_NEEDS_UPDATE | SCULPT_BOUNDARY_UPDATE_SHARP_ANGLE; } while ((l = l->next) != f->l_first); @@ -966,19 +972,17 @@ static void bmesh_undo_customdata_change(CustomData *domain, char htype, void *u static void sculpt_undo_bmesh_restore_generic(SculptUndoNode *unode, Object *ob, SculptSession *ss) { - BmeshUndoData data = {ob, - ss->pbvh, - ss->bm, - false, - false, - ss->cd_face_node_offset, - ss->cd_vert_node_offset, - -1, - -1, - ss->attrs.boundary_flags->bmesh_cd_offset, - ss->attrs.flags->bmesh_cd_offset, - false, - !unode->applied}; + BmeshUndoData data = {}; + data.ob = ob; + data.bm = ss->bm; + data.pbvh = ss->pbvh; + data.cd_face_node_offset = ss->cd_face_node_offset; + data.cd_vert_node_offset = ss->cd_vert_node_offset; + data.cd_face_node_offset_old = data.cd_vert_node_offset_old = -1; + data.cd_boundary_flag = ss->attrs.boundary_flags->bmesh_cd_offset; + data.cd_edge_boundary = ss->attrs.edge_boundary_flags->bmesh_cd_offset; + data.cd_flags = ss->attrs.flags->bmesh_cd_offset; + data.is_redo = !unode->applied; BMLogCallbacks callbacks = {bmesh_undo_on_vert_add, bmesh_undo_on_vert_kill, @@ -994,9 +998,7 @@ static void sculpt_undo_bmesh_restore_generic(SculptUndoNode *unode, Object *ob, (void *)&data}; BKE_sculptsession_update_attr_refs(ob); - BKE_sculpt_ensure_idmap(ob); - // pbvh_bmesh_check_nodes(ss->pbvh); if (unode->applied) { BM_log_undo(ss->bm, ss->bm_log, &callbacks); @@ -1020,17 +1022,12 @@ static void sculpt_undo_bmesh_restore_generic(SculptUndoNode *unode, Object *ob, } } - // pbvh_bmesh_check_nodes(ss->pbvh); BKE_pbvh_bmesh_regen_node_verts(ss->pbvh, false); - // pbvh_bmesh_check_nodes(ss->pbvh); - BKE_pbvh_update_bounds(ss->pbvh, PBVH_UpdateBB | PBVH_UpdateOriginalBB | PBVH_UpdateRedraw); if (data.balance_pbvh) { blender::bke::dyntopo::after_stroke(ss->pbvh, true); } - - // pbvh_bmesh_check_nodes(ss->pbvh); } else { printf("undo triggered pbvh rebuild"); -- 2.30.2 From ee1f44c804ea626d902e1f524f3b42026b1687ba Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Fri, 9 Jun 2023 14:58:06 -0700 Subject: [PATCH 169/279] temp-sculpt-dyntopo: Remove old pre-C++ debugging API --- source/blender/blenkernel/intern/dyntopo.cc | 23 --------------- .../blenkernel/intern/dyntopo_collapse.cc | 14 +-------- .../blender/blenkernel/intern/pbvh_bmesh.cc | 6 ---- source/blender/bmesh/intern/bmesh_log.cc | 29 ------------------- .../blender/bmesh/intern/bmesh_log_intern.h | 14 --------- .../editors/sculpt_paint/sculpt_undo.cc | 6 ---- 6 files changed, 1 insertion(+), 91 deletions(-) diff --git a/source/blender/blenkernel/intern/dyntopo.cc b/source/blender/blenkernel/intern/dyntopo.cc index 7e4fb191d35..3217725a220 100644 --- a/source/blender/blenkernel/intern/dyntopo.cc +++ b/source/blender/blenkernel/intern/dyntopo.cc @@ -861,8 +861,6 @@ bool check_face_is_tri(PBVH *pbvh, BMFace *f) return false; } - bm_logstack_push(); - LinkNode *dbl = nullptr; Vector fs; @@ -1004,7 +1002,6 @@ bool check_face_is_tri(PBVH *pbvh, BMFace *f) } pbvh_bmesh_check_nodes(pbvh); - bm_logstack_pop(); return false; } @@ -1012,8 +1009,6 @@ bool check_face_is_tri(PBVH *pbvh, BMFace *f) bool destroy_nonmanifold_fins(PBVH *pbvh, BMEdge *e_root) { #if !(DYNTOPO_DISABLE_FLAG & DYNTOPO_DISABLE_FIN_REMOVAL) - bm_logstack_push(); - static int max_faces = 64; Vector stack; @@ -1022,7 +1017,6 @@ bool destroy_nonmanifold_fins(PBVH *pbvh, BMEdge *e_root) Vector minfs; if (!l) { - bm_logstack_pop(); return false; } @@ -1082,7 +1076,6 @@ bool destroy_nonmanifold_fins(PBVH *pbvh, BMEdge *e_root) PBVH_RebuildDrawBuffers; if (!minfs.size()) { - bm_logstack_pop(); return false; } @@ -1199,7 +1192,6 @@ bool destroy_nonmanifold_fins(PBVH *pbvh, BMEdge *e_root) pbvh_bmesh_check_nodes(pbvh); - bm_logstack_pop(); return true; #else return false; @@ -1213,8 +1205,6 @@ bool check_for_fins(PBVH *pbvh, BMVert *v) return false; } - bm_logstack_push(); - do { if (!e) { printf("%s: e was nullptr\n", __func__); @@ -1226,7 +1216,6 @@ bool check_for_fins(PBVH *pbvh, BMVert *v) do { if (l != l->radial_next && l != l->radial_next->radial_next) { if (destroy_nonmanifold_fins(pbvh, e)) { - bm_logstack_pop(); return true; } } @@ -1234,7 +1223,6 @@ bool check_for_fins(PBVH *pbvh, BMVert *v) } } while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e); - bm_logstack_pop(); return false; } @@ -1729,8 +1717,6 @@ static bool cleanup_valence_3_4(EdgeQueueContext *ectx, PBVH *pbvh) { bool modified = false; - bm_logstack_push(); - bm_log_message(" == cleanup_valence_3_4 == "); const int cd_vert_node = pbvh->cd_vert_node_offset; @@ -2043,8 +2029,6 @@ static bool cleanup_valence_3_4(EdgeQueueContext *ectx, PBVH *pbvh) pbvh->header.bm->elem_table_dirty |= BM_VERT | BM_FACE | BM_EDGE; } - bm_logstack_pop(); - return modified; } @@ -2526,9 +2510,7 @@ void BKE_pbvh_bmesh_remove_face(PBVH *pbvh, BMFace *f, bool log_face) void BKE_pbvh_bmesh_remove_edge(PBVH *pbvh, BMEdge *e, bool log_edge) { if (log_edge) { - bm_logstack_push(); BM_log_edge_removed(pbvh->header.bm, pbvh->bm_log, e); - bm_logstack_pop(); } } @@ -2543,8 +2525,6 @@ void BKE_pbvh_bmesh_remove_vertex(PBVH *pbvh, BMVert *v, bool log_vert) void BKE_pbvh_bmesh_add_face(PBVH *pbvh, struct BMFace *f, bool log_face, bool force_tree_walk) { - bm_logstack_push(); - int ni = DYNTOPO_NODE_NONE; if (force_tree_walk) { @@ -2554,7 +2534,6 @@ void BKE_pbvh_bmesh_add_face(PBVH *pbvh, struct BMFace *f, bool log_face, bool f BM_log_face_added(pbvh->header.bm, pbvh->bm_log, f); } - bm_logstack_pop(); return; } @@ -2588,8 +2567,6 @@ void BKE_pbvh_bmesh_add_face(PBVH *pbvh, struct BMFace *f, bool log_face, bool f if (log_face) { BM_log_face_added(pbvh->header.bm, pbvh->bm_log, f); } - - bm_logstack_pop(); } namespace blender::bke::dyntopo { diff --git a/source/blender/blenkernel/intern/dyntopo_collapse.cc b/source/blender/blenkernel/intern/dyntopo_collapse.cc index 7ba81df3459..16b43bfdd4e 100644 --- a/source/blender/blenkernel/intern/dyntopo_collapse.cc +++ b/source/blender/blenkernel/intern/dyntopo_collapse.cc @@ -458,7 +458,6 @@ bool pbvh_bmesh_collapse_edge_uvs( { BMesh *bm = pbvh->header.bm; - bm_logstack_push(); pbvh_check_vert_boundary_bmesh(pbvh, v_conn); pbvh_check_vert_boundary_bmesh(pbvh, v_del); @@ -484,11 +483,9 @@ bool pbvh_bmesh_collapse_edge_uvs( /*have to check edge flags directly, vertex flag test above isn't specific enough and can sometimes let bad edges through*/ if ((boundflag1 & SCULPT_BOUNDARY_SHARP_MARK) && (e->head.hflag & BM_ELEM_SMOOTH)) { - bm_logstack_pop(); return false; } if ((boundflag1 & SCULPT_BOUNDARY_SEAM) && !(e->head.hflag & BM_ELEM_SEAM)) { - bm_logstack_pop(); return false; } @@ -675,7 +672,6 @@ bool pbvh_bmesh_collapse_edge_uvs( } } while ((l = l->radial_next) != e->l); - bm_logstack_pop(); return snap; } @@ -759,12 +755,9 @@ class DyntopoCollapseCallbacks { BMVert *pbvh_bmesh_collapse_edge( PBVH *pbvh, BMEdge *e, BMVert *v1, BMVert *v2, EdgeQueueContext *eq_ctx) { - bm_logstack_push(); - BMVert *v_del, *v_conn; if (pbvh->dyntopo_stop) { - bm_logstack_pop(); return nullptr; } @@ -793,7 +786,6 @@ BMVert *pbvh_bmesh_collapse_edge( /* Don't collapse across boundaries. */ if ((boundflag1 & SCULPTVERT_ALL_BOUNDARY) != (boundflag2 & SCULPTVERT_ALL_BOUNDARY)) { - bm_logstack_pop(); return nullptr; } @@ -803,16 +795,14 @@ BMVert *pbvh_bmesh_collapse_edge( bool corner1 = (boundflag1 & SCULPTVERT_ALL_CORNER) || w1 >= 0.85; bool corner2 = (boundflag2 & SCULPTVERT_ALL_CORNER) || w2 >= 0.85; - /* We allow two corners of the example sampe type[s] to collapse */ + /* We allow two corners of the example sampe type[s] to collapse */ if ((boundflag1 & SCULPTVERT_ALL_CORNER) && (boundflag1 & SCULPTVERT_ALL_CORNER) != (boundflag2 & SCULPTVERT_ALL_CORNER)) { - bm_logstack_pop(); return nullptr; } if (w1 >= 0.85 && w2 >= 0.85) { - bm_logstack_pop(); return nullptr; } @@ -973,7 +963,6 @@ BMVert *pbvh_bmesh_collapse_edge( BM_idmap_release(pbvh->bm_idmap, reinterpret_cast(v_conn), true); BM_vert_kill(pbvh->header.bm, v_conn); - bm_logstack_pop(); return nullptr; } #endif @@ -997,7 +986,6 @@ BMVert *pbvh_bmesh_collapse_edge( validate_vert(pbvh, v_conn, CHECK_VERT_FACES | CHECK_VERT_NODE_ASSIGNED); - bm_logstack_pop(); PBVH_CHECK_NAN(v_conn->co); return v_conn; diff --git a/source/blender/blenkernel/intern/pbvh_bmesh.cc b/source/blender/blenkernel/intern/pbvh_bmesh.cc index d896600bca9..dd77cd81436 100644 --- a/source/blender/blenkernel/intern/pbvh_bmesh.cc +++ b/source/blender/blenkernel/intern/pbvh_bmesh.cc @@ -287,7 +287,6 @@ namespace blender::bke::dyntopo { void pbvh_kill_vert(PBVH *pbvh, BMVert *v, bool log_vert, bool log_edges) { BMEdge *e = v->e; - bm_logstack_push(); if (e && log_edges) { do { @@ -318,7 +317,6 @@ void pbvh_kill_vert(PBVH *pbvh, BMVert *v, bool log_vert, bool log_edges) BM_idmap_release(pbvh->bm_idmap, (BMElem *)v, true); BM_vert_kill(pbvh->header.bm, v); - bm_logstack_pop(); } static BMVert *pbvh_bmesh_vert_create(PBVH *pbvh, @@ -666,8 +664,6 @@ void pbvh_bmesh_face_remove( return; } - bm_logstack_push(); - /* Check if any of this face's vertices need to be removed * from the node */ if (check_verts) { @@ -731,8 +727,6 @@ void pbvh_bmesh_face_remove( /* mark node for update */ f_node->flag |= PBVH_UpdateDrawBuffers | PBVH_UpdateNormals | PBVH_UpdateTris | PBVH_UpdateTriAreas | PBVH_UpdateCurvatureDir; - - bm_logstack_pop(); } } // namespace blender::bke::dyntopo diff --git a/source/blender/bmesh/intern/bmesh_log.cc b/source/blender/bmesh/intern/bmesh_log.cc index 5582bf3f4a1..1415666bce5 100644 --- a/source/blender/bmesh/intern/bmesh_log.cc +++ b/source/blender/bmesh/intern/bmesh_log.cc @@ -31,28 +31,6 @@ extern "C" { #include #include -#ifdef DO_LOG_PRINT -static int msg_idgen = 1; -static char msg_buffer[256] = {0}; - -# define SET_MSG(le) memcpy(le->msg, msg_buffer, sizeof(le->msg)) -# define GET_MSG(le) (le)->msg -# ifdef DEBUG_LOG_CALL_STACKS -# define LOGPRINT(entry, ...) \ - fprintf(DEBUG_FILE, "%d: %s: ", entry->id, bm_logstack_head()); \ - fprintf(DEBUG_FILE, __VA_ARGS__) -# else -# define LOGPRINT(entry, ...) \ - fprintf(DEBUG_FILE, "%s: ", __func__); \ - fprintf(DEBUG_FILE, __VA_ARGS__) -# endif -struct Mesh; -#else -# define GET_MSG(le) le ? "" : " " -# define SET_MSG(le) -# define LOGPRINT(...) -#endif - extern "C" void bm_log_message(const char *fmt, ...) { char msg[64]; @@ -61,13 +39,6 @@ extern "C" void bm_log_message(const char *fmt, ...) va_start(args, fmt); vsnprintf(msg, sizeof(msg), fmt, args); va_end(args); - -#ifdef DO_LOG_PRINT - BLI_snprintf(msg_buffer, 64, "%d %s", msg_idgen, msg); - msg_idgen++; - - fprintf(DEBUG_FILE, "%s\n", msg); -#endif } /* Avoid C++ runtime type ids. */ diff --git a/source/blender/bmesh/intern/bmesh_log_intern.h b/source/blender/bmesh/intern/bmesh_log_intern.h index 04a291018c7..18258b7486c 100644 --- a/source/blender/bmesh/intern/bmesh_log_intern.h +++ b/source/blender/bmesh/intern/bmesh_log_intern.h @@ -73,20 +73,6 @@ typedef struct BMLogCallbacks { void *userdata; } BMLogCallbacks; -//#define DEBUG_LOG_CALL_STACKS - -#ifdef DEBUG_LOG_CALL_STACKS -void _bm_logstack_pop(); -const char *_bm_logstack_head(); -void _bm_logstack_push(const char *name); -# define bm_logstack_push() _bm_logstack_push(__func__) -# define bm_logstack_pop() _bm_logstack_pop() -#else -# define bm_logstack_push() -# define bm_logstack_head "" -# define bm_logstack_pop() -#endif - struct BMIdMap; /* Allocate and initialize a new BMLog */ diff --git a/source/blender/editors/sculpt_paint/sculpt_undo.cc b/source/blender/editors/sculpt_paint/sculpt_undo.cc index 9bd3cc18d94..1481d7a1d1b 100644 --- a/source/blender/editors/sculpt_paint/sculpt_undo.cc +++ b/source/blender/editors/sculpt_paint/sculpt_undo.cc @@ -2220,9 +2220,7 @@ static SculptUndoNode *sculpt_undo_bmesh_push(Object *ob, PBVHNode *node, Sculpt case SCULPT_UNDO_COORDS: case SCULPT_UNDO_MASK: BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) { - bm_logstack_push(); BM_log_vert_before_modified(ss->bm, ss->bm_log, vd.bm_vert); - bm_logstack_pop(); } BKE_pbvh_vertex_iter_end; break; @@ -2231,9 +2229,7 @@ static SculptUndoNode *sculpt_undo_bmesh_push(Object *ob, PBVHNode *node, Sculpt DyntopoSet *faces = BKE_pbvh_bmesh_node_faces(node); BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) { - bm_logstack_push(); BM_log_vert_before_modified(ss->bm, ss->bm_log, vd.bm_vert); - bm_logstack_pop(); } BKE_pbvh_vertex_iter_end; @@ -2259,9 +2255,7 @@ static SculptUndoNode *sculpt_undo_bmesh_push(Object *ob, PBVHNode *node, Sculpt if (domain == ATTR_DOMAIN_POINT) { BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) { - bm_logstack_push(); BM_log_vert_before_modified(ss->bm, ss->bm_log, vd.bm_vert); - bm_logstack_pop(); } BKE_pbvh_vertex_iter_end; } -- 2.30.2 From b678c3b3f688ac2c258beeca8552fc0c455d92f5 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Fri, 9 Jun 2023 15:05:28 -0700 Subject: [PATCH 170/279] temp-sculpt-dyntopo: Revert some unrelated changes and cleanup collapse --- .../blenkernel/intern/dyntopo_collapse.cc | 407 +----------------- .../blender/editors/interface/CMakeLists.txt | 1 - .../editors/interface/interface_handlers.cc | 4 - .../editors/interface/interface_intern.hh | 4 - 4 files changed, 5 insertions(+), 411 deletions(-) diff --git a/source/blender/blenkernel/intern/dyntopo_collapse.cc b/source/blender/blenkernel/intern/dyntopo_collapse.cc index 16b43bfdd4e..83b67d259bd 100644 --- a/source/blender/blenkernel/intern/dyntopo_collapse.cc +++ b/source/blender/blenkernel/intern/dyntopo_collapse.cc @@ -38,7 +38,7 @@ #include "pbvh_intern.hh" #include -#include +#include using blender::float2; using blender::float3; @@ -52,18 +52,14 @@ using blender::Vector; namespace blender::bke::dyntopo { -typedef struct TraceData { +struct TraceData { PBVH *pbvh; BMEdge *e; SculptSession *ss; -} TraceData; - -const int COLLAPSE_TAG = 32; -const int COLLAPSE_FACE_TAG = 64; +}; template static void check_new_elem_id(T *elem, PBVH *pbvh) { -#if 1 int id = BM_ELEM_CD_GET_INT(elem, pbvh->bm_idmap->cd_id_off[int(elem->head.htype)]); if (id != BM_ID_NONE) { BMElem *existing = id < pbvh->bm_idmap->map_size ? BM_idmap_lookup(pbvh->bm_idmap, id) : @@ -83,287 +79,6 @@ template static void check_new_elem_id(T *elem, PBVH *pbvh) else { BM_idmap_check_assign(pbvh->bm_idmap, reinterpret_cast(elem)); } -#else - BM_idmap_check_assign(pbvh->bm_idmap, reinterpret_cast(elem)); -#endif -} - -static void collapse_ring_callback_pre(BMElem *elem, void *userdata) -{ - TraceData *data = static_cast(userdata); - - BM_idmap_check_assign(data->pbvh->bm_idmap, elem); - BMesh *bm = data->pbvh->header.bm; - - switch (elem->head.htype) { - case BM_VERT: { - BMVert *v = reinterpret_cast(elem); - - dyntopo_add_flag(data->pbvh, v, SCULPTFLAG_NEED_VALENCE); - - BM_log_vert_removed(bm, data->pbvh->bm_log, v); - pbvh_bmesh_vert_remove(data->pbvh, v); - BM_idmap_release(data->pbvh->bm_idmap, elem, false); - break; - } - case BM_EDGE: { - BMEdge *e = reinterpret_cast(elem); - BM_log_edge_removed(bm, data->pbvh->bm_log, e); - BM_idmap_release(data->pbvh->bm_idmap, elem, false); - break; - } - case BM_FACE: { - BMFace *f = reinterpret_cast(elem); - BM_log_face_removed(bm, data->pbvh->bm_log, f); - pbvh_bmesh_face_remove(data->pbvh, f, false, true, true); - BM_idmap_release(data->pbvh->bm_idmap, elem, false); - break; - } - } -} - -static void collapse_ring_callback_post(BMElem *elem, void *userdata) -{ - TraceData *data = static_cast(userdata); - BMesh *bm = data->pbvh->header.bm; - - switch (elem->head.htype) { - case BM_VERT: { - BMVert *v = reinterpret_cast(elem); - - dyntopo_add_flag(data->pbvh, v, SCULPTFLAG_NEED_VALENCE); - - check_new_elem_id(v, data->pbvh); - BM_log_vert_added(bm, data->pbvh->bm_log, v); - break; - } - case BM_EDGE: { - BMEdge *e = reinterpret_cast(elem); - check_new_elem_id(e, data->pbvh); - - BM_log_edge_added(bm, data->pbvh->bm_log, e); - break; - } - case BM_FACE: { - BMFace *f = reinterpret_cast(elem); - check_new_elem_id(f, data->pbvh); - - BM_log_face_added(bm, data->pbvh->bm_log, f); - BKE_pbvh_bmesh_add_face(data->pbvh, f, false, false); - break; - } - } -} - -static void tag_vert_ring(BMVert *v, const int tag, const int facetag) -{ - v->head.api_flag |= tag; - - BMEdge *e = v->e; - do { - BMVert *v_other = v == e->v1 ? e->v2 : e->v1; - - e->head.api_flag |= tag; - v_other->head.api_flag |= tag; - - BMLoop *l = e->l; - if (!l) { - continue; - } - do { - l->f->head.api_flag |= tag | facetag; - BMLoop *l2 = l; - do { - l2->v->head.api_flag |= tag; - l2->e->head.api_flag |= tag; - } while ((l2 = l2->next) != l); - } while ((l = l->radial_next) != e->l); - } while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e); -} - -template> -static void add_elem(T *elem, Vec &vec, int tag) -{ - if (elem->head.api_flag & tag) { - elem->head.api_flag &= ~tag; - vec.append(elem); - } -} - -template, - typename EV = Vector, - typename FV = Vector> - -static void add_vert_to_ring( - BMVert *v, const int tag, const int facetag, VV &verts, EV &edges, FV &faces) -{ - if (v->head.api_flag & tag) { - return; - } - - v->head.api_flag &= ~tag; - - if (!v->e) { - return; - } - - BMEdge *e = v->e; - do { - BMLoop *l = e->l; - if (!l) { - continue; - } - - do { - if (!(l->f->head.api_flag & facetag)) { - return; - } - } while ((l = l->radial_next) != e->l); - } while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e); - - verts.append(v); -} - -template, - typename EV = Vector, - typename FV = Vector> - -static void build_vert_ring( - BMVert *v, const int tag, const int facetag, VV &verts, EV &edges, FV &faces) -{ - add_elem(v, verts, tag); - - BMEdge *e = v->e; - do { - BMVert *v_other = v == e->v1 ? e->v2 : e->v1; - add_vert_to_ring(v_other, tag, facetag, verts, edges, faces); - - BMLoop *l = e->l; - bool bad = false; - - if (!l) { - continue; - } - do { - if (!(l->f->head.api_flag & facetag)) { - bad = true; - break; - } - - add_elem(l->f, faces, tag); - BMLoop *l2 = l; - do { - add_vert_to_ring(l2->v, tag, facetag, verts, edges, faces); - } while ((l2 = l2->next) != l); - } while ((l = l->radial_next) != e->l); - - if (!bad) { - add_elem(e, edges, tag); - } - else { - e->head.api_flag &= ~tag; - } - } while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e); - - e = v->e; - do { - BMLoop *l = e->l; - } while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e); -} - -static void vert_ring_do_(BMesh *bm, - BMVert *v, - BMVert *v_extra, - void (*callback)(BMElem *elem, void *userdata), - void *userdata, - const int tag, - const int facetag, - const int depth = 0) -{ - tag_vert_ring(v, tag, facetag); - if (v_extra) { - tag_vert_ring(v_extra, tag, facetag); - } - - Vector verts; - Vector edges; - Vector faces; - - build_vert_ring(v, tag, facetag, verts, edges, faces); - if (v_extra) { - build_vert_ring(v_extra, tag, facetag, verts, edges, faces); - } - - for (BMFace *f : faces) { - f->head.api_flag &= ~facetag; - } - - // printf("%d %d %d\n", verts.size(), edges.size(), faces.size()); - -#if 0 - BMIter iter; - BMVert *vi; - BMEdge *ei; - BMFace *fi; - BM_ITER_MESH (vi, &iter, bm, BM_VERTS_OF_MESH) { - vi->head.hflag &= ~BM_ELEM_SELECT; - } - BM_ITER_MESH (ei, &iter, bm, BM_EDGES_OF_MESH) { - ei->head.hflag &= ~BM_ELEM_SELECT; - } - BM_ITER_MESH (fi, &iter, bm, BM_FACES_OF_MESH) { - fi->head.hflag &= ~BM_ELEM_SELECT; - } -#endif - - for (BMFace *f : faces) { - f->head.hflag |= BM_ELEM_SELECT; - callback(reinterpret_cast(f), userdata); - } - - BMEdge *exist_e = v_extra ? BM_edge_exists(v, v_extra) : nullptr; - for (BMEdge *e : edges) { - if (exist_e == e) { - e->head.hflag &= ~BM_ELEM_SELECT; - } - else { - e->head.hflag |= BM_ELEM_SELECT; - } - callback(reinterpret_cast(e), userdata); - } - for (BMVert *v2 : verts) { - v2->head.hflag |= BM_ELEM_SELECT; - callback(reinterpret_cast(v2), userdata); - } -} - -template> -static void vert_ring_recurse(BMVert *v, FaceSet &faces, int depth) -{ - if (!v->e) { - return; - } - - BMEdge *e = v->e; - do { - BMVert *v2 = BM_edge_other_vert(e, v); - - if (!e->l) { - if (depth > 0) { - vert_ring_recurse(v2, faces, depth - 1); - } - continue; - } - - BMLoop *l = e->l; - do { - faces.add(l->f); - } while ((l = l->radial_next) != e->l); - - if (depth > 0) { - vert_ring_recurse(v2, faces, depth - 1); - } - } while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e); } static bool vert_is_nonmanifold(BMVert *v) @@ -382,82 +97,9 @@ static bool vert_is_nonmanifold(BMVert *v) return false; } -static void vert_ring_do(BMesh *bm, - BMVert *v, - BMVert *v_extra, - void (*callback)(BMElem *elem, void *userdata), - void *userdata, - int /*tag*/, - int /*facetag*/, - int max_depth = 0) -{ - blender::Set faces; - - vert_ring_recurse(v, faces, max_depth); - if (v_extra) { - vert_ring_recurse(v_extra, faces, max_depth); - } - - blender::Set verts; - blender::Set edges; - - for (BMFace *f : faces) { - BMLoop *l = f->l_first; - - do { - bool bad = false; - - BMLoop *l2 = l->radial_next; - do { - if (!faces.contains(l2->f)) { - bad = true; - break; - } - } while ((l2 = l2->radial_next) != l); - - if (!bad) { - edges.add(l->e); - } - } while ((l = l->next) != f->l_first); - } - - for (BMFace *f : faces) { - BMLoop *l = f->l_first; - do { - bool bad = false; - BMEdge *e = l->v->e; - - do { - if (!edges.contains(e)) { - bad = true; - break; - } - } while ((e = BM_DISK_EDGE_NEXT(e, l->v)) != l->v->e); - - if (!bad) { - verts.add(l->v); - } - } while ((l = l->next) != f->l_first); - } - - // printf("%d %d %d\n", verts.size(), edges.size(), faces.size()); - - for (BMFace *f : faces) { - callback(reinterpret_cast(f), userdata); - } - for (BMEdge *e : edges) { - callback(reinterpret_cast(e), userdata); - } - for (BMVert *v2 : verts) { - callback(reinterpret_cast(v2), userdata); - } -} - bool pbvh_bmesh_collapse_edge_uvs( PBVH *pbvh, BMEdge *e, BMVert *v_conn, BMVert *v_del, EdgeQueueContext *eq_ctx) { - BMesh *bm = pbvh->header.bm; - pbvh_check_vert_boundary_bmesh(pbvh, v_conn); pbvh_check_vert_boundary_bmesh(pbvh, v_del); @@ -818,71 +460,32 @@ BMVert *pbvh_bmesh_collapse_edge( v_conn = v1; } - /* Needed for vert_ring_do. */ - const int tag = COLLAPSE_TAG; - const int facetag = COLLAPSE_FACE_TAG; - int vert_ring_maxdepth = 0; - bool non_manifold_v1 = vert_is_nonmanifold(e->v1); bool non_manifold_v2 = vert_is_nonmanifold(e->v2); - if (non_manifold_v1 && non_manifold_v2) { - vert_ring_maxdepth++; - } /* Do not collapse non-manifold verts into manifold ones. */ - else if (non_manifold_v1 != non_manifold_v2) { + if (non_manifold_v1 != non_manifold_v2) { return nullptr; } -#define USE_COLLAPSE_CALLBACKS - -#ifdef USE_COLLAPSE_CALLBACKS DyntopoCollapseCallbacks callbacks(pbvh); -#endif /* Make sure original data is initialized before we snap it. */ BKE_pbvh_bmesh_check_origdata(eq_ctx->ss, v_conn, pbvh->stroke_id); BKE_pbvh_bmesh_check_origdata(eq_ctx->ss, v_del, pbvh->stroke_id); -#ifndef USE_COLLAPSE_CALLBACKS - /* Remove topology from PBVH and insert into bmlog. */ - vert_ring_do(pbvh->header.bm, - e->v1, - e->v2, - collapse_ring_callback_pre, - &tdata, - tag, - facetag, - vert_ring_maxdepth); -#endif pbvh_bmesh_check_nodes(pbvh); /* Snap UVS. */ bool uvs_snapped = pbvh_bmesh_collapse_edge_uvs(pbvh, e, v_conn, v_del, eq_ctx); validate_vert(pbvh, v_conn, CHECK_VERT_FACES | CHECK_VERT_NODE_ASSIGNED); - BMEdge *e2; if (uvs_snapped) { interp_v3_v3v3(v_conn->co, v_del->co, v_conn->co, 0.5f); } /* Full non-manifold collapse. */ -#ifdef USE_COLLAPSE_CALLBACKS blender::bmesh::join_vert_kill_edge(pbvh->header.bm, e, v_del, true, true, callbacks); -#else - BM_edge_collapse(pbvh->header.bm, e, v_del, true, true, true, true); -#endif - -#ifndef USE_COLLAPSE_CALLBACKS - vert_ring_do(pbvh->header.bm, - v_conn, - nullptr, - collapse_ring_callback_post, - static_cast(&tdata), - tag, - facetag, - vert_ring_maxdepth); -#endif if (BM_elem_is_free((BMElem *)v_conn, BM_VERT)) { printf("v_conn was freed\n"); @@ -968,7 +571,7 @@ BMVert *pbvh_bmesh_collapse_edge( #endif if (BM_ELEM_CD_GET_INT(v_conn, pbvh->cd_vert_node_offset) == DYNTOPO_NODE_NONE) { - printf("%s: error: failed to remove vert from pbvh? v_conn->e: %p v_conn->e->l\n", + printf("%s: error: failed to remove vert from pbvh? v_conn->e: %p v_conn->e->l: %p\n", __func__, v_conn->e, v_conn->e ? v_conn->e->l : nullptr); diff --git a/source/blender/editors/interface/CMakeLists.txt b/source/blender/editors/interface/CMakeLists.txt index 2b81b6f7f61..724a215fe05 100644 --- a/source/blender/editors/interface/CMakeLists.txt +++ b/source/blender/editors/interface/CMakeLists.txt @@ -24,7 +24,6 @@ set(INC ../../windowmanager ../../../../intern/ghost ../../../../intern/guardedalloc - ../../../../intern/atomic ../../bmesh # RNA_prototypes.h ${CMAKE_BINARY_DIR}/source/blender/makesrna diff --git a/source/blender/editors/interface/interface_handlers.cc b/source/blender/editors/interface/interface_handlers.cc index 8f67bbb64cb..51e16856b2c 100644 --- a/source/blender/editors/interface/interface_handlers.cc +++ b/source/blender/editors/interface/interface_handlers.cc @@ -6332,8 +6332,6 @@ static int ui_do_but_COLOR(bContext *C, uiBut *but, uiHandleButtonData *data, co } else { Scene *scene = CTX_data_scene(C); - ePaintMode mode = BKE_paintmode_get_active_from_context(C); - bool updated = false; if (but->rnaprop && RNA_property_subtype(but->rnaprop) == PROP_COLOR_GAMMA) { @@ -8671,9 +8669,7 @@ static void button_activate_exit( #ifdef USE_ALLSELECT { /* only RNA from this button is used */ - uiBut but_temp = *but; - uiSelectContextStore *selctx_data = &data->select_others; for (int i = 0; i < selctx_data->elems_len; i++) { uiSelectContextElem *other = &selctx_data->elems[i]; diff --git a/source/blender/editors/interface/interface_intern.hh b/source/blender/editors/interface/interface_intern.hh index 565ec3d7780..4087d83952b 100644 --- a/source/blender/editors/interface/interface_intern.hh +++ b/source/blender/editors/interface/interface_intern.hh @@ -229,7 +229,6 @@ struct uiBut { uchar unit_type = 0; short iconadd = 0; - char poison1[512]; /** #UI_BTYPE_BLOCK data */ uiBlockCreateFunc block_create_func = nullptr; @@ -244,7 +243,6 @@ struct uiBut { int rnaindex = 0; /* Operator data */ - char poison2[512]; wmOperatorType *optype = nullptr; PointerRNA *opptr = nullptr; wmOperatorCallContext opcontext = WM_OP_INVOKE_DEFAULT; @@ -260,8 +258,6 @@ struct uiBut { ImBuf *imb = nullptr; float imb_scale = 0; - char poison4[512]; - /** Active button data (set when the user is hovering or interacting with a button). */ uiHandleButtonData *active = nullptr; -- 2.30.2 From b88fc8d69d8d3fd9244670c6b6b4aa6bded0ea52 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Fri, 9 Jun 2023 15:53:41 -0700 Subject: [PATCH 171/279] temp-sculpt-dyntopo: Cleanup dyntopo code some more Move a few functions into methods of EdgeQueueContext --- source/blender/blenkernel/intern/dyntopo.cc | 202 ++++-------------- .../blenkernel/intern/dyntopo_collapse.cc | 19 +- .../blenkernel/intern/dyntopo_intern.hh | 7 +- .../blender/blenkernel/intern/pbvh_bmesh.cc | 52 +++-- 4 files changed, 77 insertions(+), 203 deletions(-) diff --git a/source/blender/blenkernel/intern/dyntopo.cc b/source/blender/blenkernel/intern/dyntopo.cc index 3217725a220..fcbbcb20ce2 100644 --- a/source/blender/blenkernel/intern/dyntopo.cc +++ b/source/blender/blenkernel/intern/dyntopo.cc @@ -46,6 +46,7 @@ #include "dyntopo_intern.hh" #include "pbvh_intern.hh" +#include #include //#define CLEAR_TAGS_IN_THREAD @@ -64,8 +65,6 @@ namespace blender::bke::dyntopo { using namespace blender::bke::sculpt; -static void pbvh_split_edge(struct EdgeQueueContext *eq_ctx, BMEdge *e); - static void edge_queue_create_local(EdgeQueueContext *eq_ctx, PBVH *pbvh, PBVHTopologyUpdateMode local_mode); @@ -328,28 +327,28 @@ static PBVHTopologyUpdateMode edge_queue_test(EdgeQueueContext *eq_ctx, return PBVH_None; } -static void edge_queue_insert_unified(EdgeQueueContext *eq_ctx, BMEdge *e, float w) +void EdgeQueueContext::insert_edge(BMEdge *e, float w) { if (!(e->head.hflag & EDGE_QUEUE_FLAG)) { - eq_ctx->edge_heap.insert(w, e); + edge_heap.insert(w, e); e->head.hflag |= EDGE_QUEUE_FLAG; if (e->l) { BMLoop *l = e->l; do { - BM_log_face_if_modified(eq_ctx->bm, eq_ctx->pbvh->bm_log, l->f); + BM_log_face_if_modified(bm, pbvh->bm_log, l->f); } while ((l = l->radial_next) != e->l); } } } -static void edge_queue_insert_val34_vert(EdgeQueueContext *eq_ctx, BMVert *v) +void EdgeQueueContext::insert_val34_vert(BMVert *v) { if (!v->e) { return; } - eq_ctx->used_verts.append(v); + used_verts.append(v); BMEdge *e = v->e; do { @@ -360,7 +359,7 @@ static void edge_queue_insert_val34_vert(EdgeQueueContext *eq_ctx, BMVert *v) BMLoop *l2 = l; do { - BM_log_face_if_modified(eq_ctx->bm, eq_ctx->pbvh->bm_log, l2->f); + BM_log_face_if_modified(bm, pbvh->bm_log, l2->f); } while ((l2 = l2->radial_next) != e->l); } while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e); } @@ -513,76 +512,6 @@ static bool skinny_bad_edge(BMEdge *e, const float limit = 4.0f) return false; } -#if 0 -static void add_split_edge_recursive( - EdgeQueueContext *eq_ctx, BMLoop *l_edge, const float len_sq, float limit_len, int depth) -{ - struct StackItem { - BMLoop *l_edge; - float len_sq; - float limit_len; - int depth; - - StackItem(BMLoop *l, float len, float limit, int d) - : l_edge(l), len_sq(len), limit_len(limit), depth(d) - { - } - }; - - Vector stack; - stack.append(StackItem(l_edge, len_sq, limit_len, depth)); - - while (stack.size() > 0) { -# if 0 // stack - StackItem item = stack.pop_last(); -# else // queue - StackItem item = stack[0]; - stack.remove_and_reorder(0); -# endif - - int depth = item.depth; - float len_sq = item.len_sq, limit_len = item.limit_len; - BMLoop *l_edge = item.l_edge; - - if (depth > DEPTH_START_LIMIT && eq_ctx->use_view_normal) { - if (dot_v3v3(l_edge->f->no, eq_ctx->view_normal) < 0.0f) { - continue; - } - } - - if (!skinny_bad_edge(l_edge->e)) { - edge_queue_insert_unified(eq_ctx, l_edge->e, len_sq); - } - - if ((l_edge->radial_next != l_edge)) { - const float len_sq_cmp = len_sq * EVEN_EDGELEN_THRESHOLD; - - limit_len *= EVEN_GENERATION_SCALE; - const float limit_len_sq = square_f(limit_len); - - BMLoop *l_iter = l_edge; - do { - BMLoop *l_adjacent[2] = {l_iter->next, l_iter->prev}; - for (int i = 0; i < (int)ARRAY_SIZE(l_adjacent); i++) { - if (l_adjacent[i]->e->head.hflag & EDGE_QUEUE_FLAG) { - continue; - } - - float len_sq_other = calc_weighted_length( - eq_ctx, l_adjacent[i]->e->v1, l_adjacent[i]->e->v2, SPLIT); - - bool insert_ok = len_sq_other > max_ff(len_sq_cmp, limit_len_sq); - if (!insert_ok) { - continue; - } - - stack.append(StackItem(l_adjacent[i]->radial_next, len_sq_other, limit_len, depth + 1)); - } - } while ((l_iter = l_iter->radial_next) != l_edge); - } - } -} -#else static void add_split_edge_recursive( EdgeQueueContext *eq_ctx, BMLoop *l_edge, const float len_sq, float limit_len, int depth) { @@ -593,7 +522,7 @@ static void add_split_edge_recursive( } // if (!skinny_bad_edge(l_edge->e)) { - edge_queue_insert_unified(eq_ctx, l_edge->e, len_sq); + eq_ctx->insert_edge(l_edge->e, len_sq); //} if ((l_edge->radial_next != l_edge)) { @@ -624,9 +553,8 @@ static void add_split_edge_recursive( } while ((l_iter = l_iter->radial_next) != l_edge); } } -#endif -typedef struct EdgeQueueThreadData { +struct EdgeQueueThreadData { PBVH *pbvh = nullptr; PBVHNode *node = nullptr; Vector edges; @@ -634,7 +562,7 @@ typedef struct EdgeQueueThreadData { int size = 0; bool is_collapse = false; int seed = 0; -} EdgeQueueThreadData; +}; static void edge_thread_data_insert(EdgeQueueThreadData *tdata, BMEdge *e) { @@ -1421,7 +1349,7 @@ static void unified_edge_queue_create(EdgeQueueContext *eq_ctx, float w; if (edge_queue_test(eq_ctx, pbvh, e, &w)) { - edge_queue_insert_unified(eq_ctx, e, w); + eq_ctx->insert_edge(e, w); } } } @@ -1430,7 +1358,7 @@ static void unified_edge_queue_create(EdgeQueueContext *eq_ctx, if (v->head.hflag & BM_ELEM_TAG) { v->head.hflag &= ~BM_ELEM_TAG; - edge_queue_insert_val34_vert(eq_ctx, v); + eq_ctx->insert_val34_vert(v); } } @@ -1459,9 +1387,8 @@ static void unified_edge_queue_create(EdgeQueueContext *eq_ctx, } } - if (1 || push_subentry) { - BM_log_entry_add_ex(pbvh->header.bm, pbvh->bm_log, true); - } + /* Push a subentry just to be on the safe side w.r.t. element IDs. */ + BM_log_entry_add_ex(pbvh->header.bm, pbvh->bm_log, true); } static void short_edge_queue_task_cb_local(void *__restrict userdata, @@ -1565,7 +1492,7 @@ static void edge_queue_create_local(EdgeQueueContext *eq_ctx, BKE_pbvh_bmesh_update_valence(pbvh, {(intptr_t)v}); } - edge_queue_insert_val34_vert(eq_ctx, v); + eq_ctx->insert_val34_vert(v); } } } @@ -1709,7 +1636,7 @@ static void edge_queue_create_local(EdgeQueueContext *eq_ctx, continue; } - edge_queue_insert_unified(eq_ctx, e, w); + eq_ctx->insert_edge(e, w); } } @@ -2068,7 +1995,7 @@ static bool do_cleanup_3_4(EdgeQueueContext *eq_ctx, PBVH *pbvh) } if (ok) { - edge_queue_insert_val34_vert(eq_ctx, v); + eq_ctx->insert_val34_vert(v); } } } @@ -2182,8 +2109,6 @@ EdgeQueueContext::EdgeQueueContext(BrushTester *brush_tester_, max_steps = (DYNTOPO_MAX_ITER * edge_limit_multiply) << (totop - 1); } -EdgeQueueContext::~EdgeQueueContext() {} - void EdgeQueueContext::start() { current_i = 0; @@ -2330,42 +2255,12 @@ void EdgeQueueContext::step() break; } -#if 0 - if (BM_vert_edge_count(e->v1) > 100 || BM_vert_edge_count(e->v2) > 100) { - printf("Pathological vertex for subdivide.\n"); - break; - } -#endif - do_smooth(e->v1); do_smooth(e->v2); e->head.hflag &= ~EDGE_QUEUE_FLAG; + split_edge(e); - pbvh_split_edge(this, e); -#if 0 - /* Add complete faces. */ - BMLoop *l = e->l; - if (l) { - do { - BMLoop *l2 = l; - do { - if (etot >= split_edges_size) { - break; - } - - if (calc_weighted_length(this, l->e->v1, l->e->v2, SPLIT) < limit_len_max_sqr) { - continue; - } - - if (subd_edges.add(l->e)) { - l->e->head.hflag &= ~EDGE_QUEUE_FLAG; - split_edges[etot++] = l->e; - } - } while ((l2 = l2->next) != l); - } while ((l = l->radial_next) != e->l); - } -#endif break; } case PBVH_Collapse: { @@ -2385,12 +2280,6 @@ void EdgeQueueContext::step() break; } - /*XXX*/ - if (BM_vert_edge_count(e->v1) > 100 || BM_vert_edge_count(e->v2) > 100) { - printf("Pathological vertex for collapse.\n"); - break; - } - if (bm_elem_is_free((BMElem *)e->v1, BM_VERT) || bm_elem_is_free((BMElem *)e->v2, BM_VERT)) { printf("%s: error! operated on freed bmesh elements! e: %p, e->v1: %p, e->v2: %p\n", __func__, @@ -2404,7 +2293,7 @@ void EdgeQueueContext::step() do_smooth(e->v2); modified = true; - pbvh_bmesh_collapse_edge(pbvh, e, e->v1, e->v2, this); + collapse_edge(pbvh, e, e->v1, e->v2); VALIDATE_LOG(pbvh->bm_log); break; } @@ -2474,17 +2363,26 @@ bool remesh_topology(BrushTester *brush_tester, edge_limit_multiply); eq_ctx.start(); - double time = PIL_check_seconds_timer(); + /* Apply a time limit to avoid excessive hangs on pathological topology. */ + + using Clock = std::chrono::high_resolution_clock; + using TimePoint = std::chrono::time_point; + + auto time = Clock::now(); + Clock::duration limit = std::chrono::duration_cast(std::chrono::milliseconds(350)); while (!eq_ctx.done()) { eq_ctx.step(); - if (PIL_check_seconds_timer() - time > 350.0 / 1000.0) { + if ((Clock::now() - time) > limit) { break; } } eq_ctx.finish(); + + printf("time: %dms\n", + int(std::chrono::duration_cast(Clock::now() - time).count())); return eq_ctx.modified; } @@ -2570,9 +2468,8 @@ void BKE_pbvh_bmesh_add_face(PBVH *pbvh, struct BMFace *f, bool log_face, bool f } namespace blender::bke::dyntopo { -static void pbvh_split_edge(EdgeQueueContext *eq_ctx, BMEdge *e) +void EdgeQueueContext::split_edge(BMEdge *e) { - PBVH *pbvh = eq_ctx->pbvh; BMesh *bm = pbvh->header.bm; BMEdge *newe; BMFace *newf = nullptr; @@ -2688,44 +2585,19 @@ static void pbvh_split_edge(EdgeQueueContext *eq_ctx, BMEdge *e) newl->e->head.hflag &= ~EDGE_QUEUE_FLAG; copy_v3_v3(newf->no, f->no); -#if 1 + BMVert *vs[3] = {newl->v, newl->next->v, newl->next->next->v}; - if (eq_ctx->brush_tester->tri_in_range(vs, newl->f->no)) { + if (brush_tester->tri_in_range(vs, newl->f->no)) { float w = 0.0f; - PBVHTopologyUpdateMode mode = edge_queue_test(eq_ctx, pbvh, newl->e, &w); + PBVHTopologyUpdateMode mode = edge_queue_test(this, pbvh, newl->e, &w); if (mode == PBVH_Subdivide) { - add_split_edge_recursive(eq_ctx, newl, w, eq_ctx->limit_len_max, 0); + add_split_edge_recursive(this, newl, w, limit_len_max, 0); } else if (mode == PBVH_Collapse) { - edge_queue_insert_unified(eq_ctx, newl->e, w); + insert_edge(newl->e, w); } } -#else - BMLoop *l2 = newl; - do { - BMLoop *l3 = l2; - do { - BMVert *vs[3] = {l3->v, l3->next->v, l3->next->next->v}; - - if (l3->e->head.hflag & EDGE_QUEUE_FLAG) { - continue; - } - - if (eq_ctx->brush_tester->tri_in_range(vs, l3->f->no)) { - float w = 0.0f; - PBVHTopologyUpdateMode mode = edge_queue_test(eq_ctx, pbvh, l3->e, &w); - - if (mode == PBVH_Subdivide) { - add_split_edge_recursive(eq_ctx, l3, w, eq_ctx->limit_len_max, 0); - } - else if (mode == PBVH_Collapse) { - edge_queue_insert_unified(eq_ctx, l3->e, w); - } - } - } while ((l3 = l3->next) != l2); - } while ((l2 = l2->radial_next) != newl); -#endif } pbvh_bmesh_check_nodes(pbvh); @@ -3589,7 +3461,7 @@ void BKE_sculpt_reproject_cdata( CustomData *ldata = &ss->bm->ldata; int totuv = 0; - CustomDataLayer *uvlayer = NULL; + CustomDataLayer *uvlayer = nullptr; /* Optimized substitute for CustomData_number_of_layers. */ if (ldata->typemap[CD_PROP_FLOAT2] != -1) { diff --git a/source/blender/blenkernel/intern/dyntopo_collapse.cc b/source/blender/blenkernel/intern/dyntopo_collapse.cc index 83b67d259bd..744c70531f4 100644 --- a/source/blender/blenkernel/intern/dyntopo_collapse.cc +++ b/source/blender/blenkernel/intern/dyntopo_collapse.cc @@ -37,8 +37,8 @@ #include "dyntopo_intern.hh" #include "pbvh_intern.hh" -#include #include +#include using blender::float2; using blender::float3; @@ -394,8 +394,7 @@ class DyntopoCollapseCallbacks { * This function is rather complicated. It has to * snap UVs, log geometry and free ids. */ -BMVert *pbvh_bmesh_collapse_edge( - PBVH *pbvh, BMEdge *e, BMVert *v1, BMVert *v2, EdgeQueueContext *eq_ctx) +BMVert *EdgeQueueContext::collapse_edge(PBVH *pbvh, BMEdge *e, BMVert *v1, BMVert *v2) { BMVert *v_del, *v_conn; @@ -405,7 +404,7 @@ BMVert *pbvh_bmesh_collapse_edge( TraceData tdata; - tdata.ss = eq_ctx->ss; + tdata.ss = ss; tdata.pbvh = pbvh; tdata.e = e; @@ -431,8 +430,8 @@ BMVert *pbvh_bmesh_collapse_edge( return nullptr; } - float w1 = DYNTOPO_MASK(eq_ctx->cd_vert_mask_offset, v1); - float w2 = DYNTOPO_MASK(eq_ctx->cd_vert_mask_offset, v2); + float w1 = DYNTOPO_MASK(cd_vert_mask_offset, v1); + float w2 = DYNTOPO_MASK(cd_vert_mask_offset, v2); bool corner1 = (boundflag1 & SCULPTVERT_ALL_CORNER) || w1 >= 0.85; bool corner2 = (boundflag2 & SCULPTVERT_ALL_CORNER) || w2 >= 0.85; @@ -471,13 +470,13 @@ BMVert *pbvh_bmesh_collapse_edge( DyntopoCollapseCallbacks callbacks(pbvh); /* Make sure original data is initialized before we snap it. */ - BKE_pbvh_bmesh_check_origdata(eq_ctx->ss, v_conn, pbvh->stroke_id); - BKE_pbvh_bmesh_check_origdata(eq_ctx->ss, v_del, pbvh->stroke_id); + BKE_pbvh_bmesh_check_origdata(ss, v_conn, pbvh->stroke_id); + BKE_pbvh_bmesh_check_origdata(ss, v_del, pbvh->stroke_id); pbvh_bmesh_check_nodes(pbvh); /* Snap UVS. */ - bool uvs_snapped = pbvh_bmesh_collapse_edge_uvs(pbvh, e, v_conn, v_del, eq_ctx); + bool uvs_snapped = pbvh_bmesh_collapse_edge_uvs(pbvh, e, v_conn, v_del, this); validate_vert(pbvh, v_conn, CHECK_VERT_FACES | CHECK_VERT_NODE_ASSIGNED); if (uvs_snapped) { @@ -553,7 +552,7 @@ BMVert *pbvh_bmesh_collapse_edge( BM_edge_kill(pbvh->header.bm, e2); dyntopo_add_flag(pbvh, v2, SCULPTFLAG_NEED_VALENCE | SCULPTFLAG_NEED_TRIANGULATE); - BKE_sculpt_boundary_flag_update(eq_ctx->ss, {reinterpret_cast(v2)}); + BKE_sculpt_boundary_flag_update(ss, {reinterpret_cast(v2)}); } if (!v_conn->e) { diff --git a/source/blender/blenkernel/intern/dyntopo_intern.hh b/source/blender/blenkernel/intern/dyntopo_intern.hh index 4980f1383ca..d3c19ab74dc 100644 --- a/source/blender/blenkernel/intern/dyntopo_intern.hh +++ b/source/blender/blenkernel/intern/dyntopo_intern.hh @@ -271,7 +271,6 @@ struct EdgeQueueContext { int count = 0; int curop = 0; - ~EdgeQueueContext(); EdgeQueueContext(BrushTester *brush_tester, Object *ob, PBVH *pbvh, @@ -283,6 +282,9 @@ struct EdgeQueueContext { void *mask_cb_data, int edge_limit_multiply); + void insert_val34_vert(BMVert *v); + void insert_edge(BMEdge *e, float w); + void start(); bool done(); void step(); @@ -300,6 +302,9 @@ struct EdgeQueueContext { return false; } + BMVert *collapse_edge(PBVH *pbvh, BMEdge *e, BMVert *v1, BMVert *v2); + void split_edge(BMEdge *e); + blender::RandomNumberGenerator rand; private: diff --git a/source/blender/blenkernel/intern/pbvh_bmesh.cc b/source/blender/blenkernel/intern/pbvh_bmesh.cc index dd77cd81436..10716acfedf 100644 --- a/source/blender/blenkernel/intern/pbvh_bmesh.cc +++ b/source/blender/blenkernel/intern/pbvh_bmesh.cc @@ -2011,8 +2011,8 @@ static void update_edge_boundary_bmesh_uv(BMEdge *e, int cd_edge_boundary, const void update_edge_boundary_bmesh(BMEdge *e, int cd_faceset_offset, int cd_edge_boundary, - const int cd_flag, - const int cd_valence, + const int /*cd_flag*/, + const int /*cd_valence*/, const CustomData *ldata, float sharp_angle_limit) { @@ -2102,7 +2102,6 @@ static void pbvh_bmesh_update_uv_boundary_intern(BMVert *v, int cd_boundary, int static void pbvh_bmesh_update_uv_boundary(BMVert *v, int cd_boundary, const CustomData *ldata) { int base_uv = ldata->typemap[CD_PROP_FLOAT2]; - int newflag = 0; *BM_ELEM_CD_PTR(v, cd_boundary) &= ~(SCULPT_BOUNDARY_UPDATE_UV | SCULPT_BOUNDARY_UV | SCULPT_CORNER_UV); @@ -2762,31 +2761,30 @@ bool BKE_pbvh_bmesh_check_tris(PBVH *pbvh, PBVHNode *node) BM_face_calc_tessellation(f, true, loops.data(), (uint(*)[3])loops_idx.data()); - auto add_tri_verts = - [cd_uvs, totuv, pbvh, &min, &max]( - PBVHTriBuf *tribuf, PBVHTri &tri, BMLoop *l, BMLoop *l2, int mat_nr, int j) { - int tri_v; + auto add_tri_verts = [cd_uvs, totuv, pbvh, &min, &max]( + PBVHTriBuf *tribuf, PBVHTri &tri, BMLoop *l, int mat_nr, int j) { + int tri_v; - if ((l->f->head.hflag & BM_ELEM_SMOOTH)) { - void *loopkey = reinterpret_cast( - tri_loopkey(l, mat_nr, pbvh->cd_faceset_offset, cd_uvs, totuv)); + if ((l->f->head.hflag & BM_ELEM_SMOOTH)) { + void *loopkey = reinterpret_cast( + tri_loopkey(l, mat_nr, pbvh->cd_faceset_offset, cd_uvs, totuv)); - tri_v = tribuf->vertmap.lookup_or_add(loopkey, tribuf->verts.size()); - } - else { /* Flat shaded faces. */ - tri_v = tribuf->verts.size(); - } + tri_v = tribuf->vertmap.lookup_or_add(loopkey, tribuf->verts.size()); + } + else { /* Flat shaded faces. */ + tri_v = tribuf->verts.size(); + } - /* Newly added to the set? */ - if (tri_v == tribuf->verts.size()) { - PBVHVertRef sv = {(intptr_t)l->v}; - minmax_v3v3_v3(min, max, l->v->co); - pbvh_tribuf_add_vert(tribuf, sv, l); - } + /* Newly added to the set? */ + if (tri_v == tribuf->verts.size()) { + PBVHVertRef sv = {(intptr_t)l->v}; + minmax_v3v3_v3(min, max, l->v->co); + pbvh_tribuf_add_vert(tribuf, sv, l); + } - tri.v[j] = (intptr_t)tri_v; - tri.l[j] = (intptr_t)l; - }; + tri.v[j] = (intptr_t)tri_v; + tri.l[j] = (intptr_t)l; + }; /* Build index buffers. */ for (int i = 0; i < tottri; i++) { @@ -2804,8 +2802,8 @@ bool BKE_pbvh_bmesh_check_tris(PBVH *pbvh, PBVHNode *node) BMLoop *l1 = loops[loops_idx[i][j]]; BMLoop *l2 = loops[loops_idx[i][(j + 1) % 3]]; - add_tri_verts(node->tribuf, tri, l1, l2, mat_nr, j); - add_tri_verts(mat_tribuf, mat_tri, l1, l2, mat_nr, j); + add_tri_verts(node->tribuf, tri, l1, mat_nr, j); + add_tri_verts(mat_tribuf, mat_tri, l1, mat_nr, j); } for (int j = 0; j < 3; j++) { @@ -2813,7 +2811,7 @@ bool BKE_pbvh_bmesh_check_tris(PBVH *pbvh, PBVHNode *node) BMLoop *l2 = loops[loops_idx[i][(j + 1) % 3]]; BMEdge *e = nullptr; - if (e = BM_edge_exists(l1->v, l2->v)) { + if ((e = BM_edge_exists(l1->v, l2->v))) { tri.eflag |= 1 << j; if (e->head.hflag & edgeflag) { -- 2.30.2 From 29f25abb66e6238baf253ffaef5e63c3d8fb70c1 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Fri, 9 Jun 2023 16:23:42 -0700 Subject: [PATCH 172/279] temp-sculpt-dyntopo: Performance tweaks --- source/blender/blenkernel/intern/dyntopo.cc | 4 +- .../blender/blenkernel/intern/pbvh_bmesh.cc | 63 +++++++++++-------- 2 files changed, 39 insertions(+), 28 deletions(-) diff --git a/source/blender/blenkernel/intern/dyntopo.cc b/source/blender/blenkernel/intern/dyntopo.cc index fcbbcb20ce2..0f79a3018a7 100644 --- a/source/blender/blenkernel/intern/dyntopo.cc +++ b/source/blender/blenkernel/intern/dyntopo.cc @@ -28,6 +28,7 @@ #include "BLI_set.hh" #include "BLI_task.h" #include "BLI_task.hh" +#include "BLI_timeit.hh" #include "BLI_utildefines.h" #include "BLI_vector.hh" @@ -2369,7 +2370,8 @@ bool remesh_topology(BrushTester *brush_tester, using TimePoint = std::chrono::time_point; auto time = Clock::now(); - Clock::duration limit = std::chrono::duration_cast(std::chrono::milliseconds(350)); + Clock::duration limit = std::chrono::duration_cast( + std::chrono::milliseconds(350)); while (!eq_ctx.done()) { eq_ctx.step(); diff --git a/source/blender/blenkernel/intern/pbvh_bmesh.cc b/source/blender/blenkernel/intern/pbvh_bmesh.cc index 10716acfedf..0398fc3828e 100644 --- a/source/blender/blenkernel/intern/pbvh_bmesh.cc +++ b/source/blender/blenkernel/intern/pbvh_bmesh.cc @@ -40,6 +40,7 @@ Topology rake: #include "BLI_sort_utils.h" #include "BLI_span.hh" #include "BLI_task.h" +#include "BLI_timeit.hh" #include "BLI_utildefines.h" #include "BLI_index_range.hh" @@ -2611,13 +2612,13 @@ void BKE_pbvh_bmesh_free_tris(PBVH * /*pbvh*/, PBVHNode *node) } } -BLI_INLINE void pbvh_tribuf_add_vert(PBVHTriBuf *tribuf, PBVHVertRef vertex, BMLoop *l) +static inline void pbvh_tribuf_add_vert(PBVHTriBuf *tribuf, PBVHVertRef vertex, BMLoop *l) { tribuf->verts.append(vertex); tribuf->loops.append((uintptr_t)l); } -BLI_INLINE void pbvh_tribuf_add_edge(PBVHTriBuf *tribuf, int v1, int v2) +static inline void pbvh_tribuf_add_edge(PBVHTriBuf *tribuf, int v1, int v2) { tribuf->edges.append(v1); tribuf->edges.append(v2); @@ -2656,6 +2657,11 @@ static void pbvh_init_tribuf(PBVHNode * /*node*/, PBVHTriBuf *tribuf) tribuf->verts.clear(); tribuf->tris.clear(); tribuf->loops.clear(); + + tribuf->edges.reserve(512); + tribuf->verts.reserve(512); + tribuf->tris.reserve(512); + tribuf->loops.reserve(512); } static uintptr_t tri_loopkey(BMLoop *l, int mat_nr, int cd_fset, int cd_uvs[], int totuv) @@ -2723,6 +2729,8 @@ bool BKE_pbvh_bmesh_check_tris(PBVH *pbvh, PBVHNode *node) Vector loops_idx; Vector *tribufs = MEM_new>("PBVHTriBuf tribufs"); + tribufs->reserve(MAXMAT); + node->flag &= ~PBVH_UpdateTris; const int edgeflag = BM_ELEM_TAG_ALT; @@ -2731,6 +2739,31 @@ bool BKE_pbvh_bmesh_check_tris(PBVH *pbvh, PBVHNode *node) INIT_MINMAX(min, max); + auto add_tri_verts = [cd_uvs, totuv, pbvh, &min, &max]( + PBVHTriBuf *tribuf, PBVHTri &tri, BMLoop *l, int mat_nr, int j) { + int tri_v; + + if ((l->f->head.hflag & BM_ELEM_SMOOTH)) { + void *loopkey = reinterpret_cast( + tri_loopkey(l, mat_nr, pbvh->cd_faceset_offset, cd_uvs, totuv)); + + tri_v = tribuf->vertmap.lookup_or_add(loopkey, tribuf->verts.size()); + } + else { /* Flat shaded faces. */ + tri_v = tribuf->verts.size(); + } + + /* Newly added to the set? */ + if (tri_v == tribuf->verts.size()) { + PBVHVertRef sv = {(intptr_t)l->v}; + minmax_v3v3_v3(min, max, l->v->co); + pbvh_tribuf_add_vert(tribuf, sv, l); + } + + tri.v[j] = (intptr_t)tri_v; + tri.l[j] = (intptr_t)l; + }; + for (BMFace *f : *node->bm_faces) { if (pbvh_poly_hidden(pbvh, f)) { continue; @@ -2751,6 +2784,7 @@ bool BKE_pbvh_bmesh_check_tris(PBVH *pbvh, PBVHNode *node) pbvh_init_tribuf(node, &_tribuf); _tribuf.mat_nr = mat_nr; + tribufs->append(_tribuf); } @@ -2761,31 +2795,6 @@ bool BKE_pbvh_bmesh_check_tris(PBVH *pbvh, PBVHNode *node) BM_face_calc_tessellation(f, true, loops.data(), (uint(*)[3])loops_idx.data()); - auto add_tri_verts = [cd_uvs, totuv, pbvh, &min, &max]( - PBVHTriBuf *tribuf, PBVHTri &tri, BMLoop *l, int mat_nr, int j) { - int tri_v; - - if ((l->f->head.hflag & BM_ELEM_SMOOTH)) { - void *loopkey = reinterpret_cast( - tri_loopkey(l, mat_nr, pbvh->cd_faceset_offset, cd_uvs, totuv)); - - tri_v = tribuf->vertmap.lookup_or_add(loopkey, tribuf->verts.size()); - } - else { /* Flat shaded faces. */ - tri_v = tribuf->verts.size(); - } - - /* Newly added to the set? */ - if (tri_v == tribuf->verts.size()) { - PBVHVertRef sv = {(intptr_t)l->v}; - minmax_v3v3_v3(min, max, l->v->co); - pbvh_tribuf_add_vert(tribuf, sv, l); - } - - tri.v[j] = (intptr_t)tri_v; - tri.l[j] = (intptr_t)l; - }; - /* Build index buffers. */ for (int i = 0; i < tottri; i++) { PBVHTriBuf *mat_tribuf = &(*tribufs)[mat_map[mat_nr]]; -- 2.30.2 From a5bf1a92b7fb6c0431b83b5e0cfd22f4bc11eec7 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Fri, 9 Jun 2023 17:44:06 -0700 Subject: [PATCH 173/279] temp-sculpt-dyntopo: Fix performance regressions * Quality now controls a time limit instead of an edge limit. * Only log loop customdata when necassary. * Removed the "Relax during remeshing" option, it was added to test the small amount of relaxation that's stochastically applied to reduce the numerical instability of collapse. Sadly it turned out to be necassary after all. --- scripts/startup/bl_ui/space_view3d_toolbar.py | 2 - source/blender/blenkernel/BKE_dyntopo.hh | 6 +- source/blender/blenkernel/intern/dyntopo.cc | 112 +++++++++--------- .../blenkernel/intern/dyntopo_intern.hh | 4 +- source/blender/blenkernel/intern/pbvh.cc | 1 + .../blender/blenkernel/intern/pbvh_bmesh.cc | 6 +- source/blender/editors/sculpt_paint/sculpt.cc | 6 +- source/blender/makesrna/intern/rna_mesh.c | 4 - 8 files changed, 70 insertions(+), 71 deletions(-) diff --git a/scripts/startup/bl_ui/space_view3d_toolbar.py b/scripts/startup/bl_ui/space_view3d_toolbar.py index dd954904ad3..8654e404d14 100644 --- a/scripts/startup/bl_ui/space_view3d_toolbar.py +++ b/scripts/startup/bl_ui/space_view3d_toolbar.py @@ -949,8 +949,6 @@ class VIEW3D_PT_sculpt_dyntopo(Panel, View3DPaintPanel): "detail_range" ) - sub.prop(context.object.data, "sculpt_dyntopo_relax") - #NotForPR if 0: scene = context.scene diff --git a/source/blender/blenkernel/BKE_dyntopo.hh b/source/blender/blenkernel/BKE_dyntopo.hh index 249ae9ab6fc..9e20e7fdeab 100644 --- a/source/blender/blenkernel/BKE_dyntopo.hh +++ b/source/blender/blenkernel/BKE_dyntopo.hh @@ -142,7 +142,8 @@ bool remesh_topology(blender::bke::dyntopo::BrushTester *brush_tester, bool updatePBVH, DyntopoMaskCB mask_cb, void *mask_cb_data, - int edge_limit_multiply); + int edge_limit_multiply, + float quality); bool remesh_topology_nodes(blender::bke::dyntopo::BrushTester *tester, struct Object *ob, @@ -156,7 +157,8 @@ bool remesh_topology_nodes(blender::bke::dyntopo::BrushTester *tester, bool updatePBVH, DyntopoMaskCB mask_cb, void *mask_cb_data, - int edge_limit_multiply); + int edge_limit_multiply, + float quality); void after_stroke(PBVH *pbvh, bool force_balance); } // namespace blender::bke::dyntopo diff --git a/source/blender/blenkernel/intern/dyntopo.cc b/source/blender/blenkernel/intern/dyntopo.cc index 0f79a3018a7..63a872a0d30 100644 --- a/source/blender/blenkernel/intern/dyntopo.cc +++ b/source/blender/blenkernel/intern/dyntopo.cc @@ -334,10 +334,23 @@ void EdgeQueueContext::insert_edge(BMEdge *e, float w) edge_heap.insert(w, e); e->head.hflag |= EDGE_QUEUE_FLAG; + if (ignore_loop_data) { + return; + } + + /* Log UVs. */ if (e->l) { BMLoop *l = e->l; do { - BM_log_face_if_modified(bm, pbvh->bm_log, l->f); + int ni = BM_ELEM_CD_GET_INT(l->f, cd_face_node_offset); + PBVHNode *node = BKE_pbvh_get_node_leaf_safe(pbvh, ni); + + /* Check if split_edge_add_recursive has wandered outside + * the set of PBVH_UpdateTopology flagged nodes. + */ + if (node && !(node->flag & PBVH_UpdateTopology)) { + BM_log_face_if_modified(bm, pbvh->bm_log, l->f); + } } while ((l = l->radial_next) != e->l); } } @@ -351,18 +364,20 @@ void EdgeQueueContext::insert_val34_vert(BMVert *v) used_verts.append(v); - BMEdge *e = v->e; - do { - BMLoop *l = e->l; - if (!l) { - continue; - } - - BMLoop *l2 = l; + if (!ignore_loop_data) { + BMEdge *e = v->e; do { - BM_log_face_if_modified(bm, pbvh->bm_log, l2->f); - } while ((l2 = l2->radial_next) != e->l); - } while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e); + BMLoop *l = e->l; + if (!l) { + continue; + } + + BMLoop *l2 = l; + do { + BM_log_face_if_modified(bm, pbvh->bm_log, l2->f); + } while ((l2 = l2->radial_next) != e->l); + } while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e); + } } /* @@ -448,6 +463,14 @@ static bool point_in_tri_v3(float p[3], float v1[3], float v2[3], float v3[3], f float dist_to_tri_sphere_simple(float p[3], float v1[3], float v2[3], float v3[3], float n[3]) { +#if 0 + float a = len_squared_v3v3(p, v1); + float b = len_squared_v3v3(p, v2); + float c = len_squared_v3v3(p, v3); + + float dis = min_ff(min_ff(a, b), c); + return dis; +#else float co[3]; float t1[3], t2[3], t3[3]; @@ -491,6 +514,7 @@ float dist_to_tri_sphere_simple(float p[3], float v1[3], float v2[3], float v3[3 dis = fmin(dis, len_squared_v3v3(p, co)); return dis; +#endif } static bool skinny_bad_edge(BMEdge *e, const float limit = 4.0f) @@ -1269,7 +1293,7 @@ static void unified_edge_queue_create(EdgeQueueContext *eq_ctx, TaskParallelSettings settings; BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = true; //! eq_ctx->reproject_cdata; + settings.use_threading = true; #ifdef DYNTOPO_NO_THREADING settings.use_threading = false; @@ -1355,39 +1379,6 @@ static void unified_edge_queue_create(EdgeQueueContext *eq_ctx, } } - for (BMVert *v : verts) { - if (v->head.hflag & BM_ELEM_TAG) { - v->head.hflag &= ~BM_ELEM_TAG; - - eq_ctx->insert_val34_vert(v); - } - } - - for (BMEdge *e : eq_ctx->edge_heap.values()) { - e->head.hflag |= EDGE_QUEUE_FLAG; - - if (!e->l) { - continue; - } - - /* Log face/loop attributes. */ - for (int i = 0; i < 2; i++) { - BMVert *v = i ? e->v2 : e->v1; - BMEdge *e2 = e; - - do { - BMLoop *l = e2->l; - if (!l) { - continue; - } - - do { - BM_log_face_modified(eq_ctx->bm, eq_ctx->pbvh->bm_log, l->f); - } while ((l = l->radial_next) != e2->l); - } while ((e2 = BM_DISK_EDGE_NEXT(e2, v)) != v->e); - } - } - /* Push a subentry just to be on the safe side w.r.t. element IDs. */ BM_log_entry_add_ex(pbvh->header.bm, pbvh->bm_log, true); } @@ -2028,6 +2019,7 @@ EdgeQueueContext::EdgeQueueContext(BrushTester *brush_tester_, int edge_limit_multiply) { ss = ob->sculpt; + pbvh = pbvh_; brush_tester = brush_tester_; use_view_normal = use_frontface_; @@ -2039,6 +2031,7 @@ EdgeQueueContext::EdgeQueueContext(BrushTester *brush_tester_, mask_cb_data = mask_cb_data_; view_normal = view_normal_; + ignore_loop_data = !bm->ldata.totlayer || !ss->reproject_smooth; updatePBVH = updatePBVH_; cd_vert_mask_offset = pbvh->cd_vert_mask_offset; cd_vert_node_offset = pbvh->cd_vert_node_offset; @@ -2046,11 +2039,7 @@ EdgeQueueContext::EdgeQueueContext(BrushTester *brush_tester_, local_mode = false; mode = mode_; - /* Need to do some final testing before deciding whether or not - * to remove surface relax. - */ - Mesh *me = static_cast(ob->data); - surface_relax = me->flag & ME_FLAG_UNUSED_5; + surface_relax = true; reproject_cdata = ss->reproject_smooth; max_heap_mm = (DYNTOPO_MAX_ITER * edge_limit_multiply) << 8; @@ -2114,12 +2103,15 @@ void EdgeQueueContext::start() { current_i = 0; - for (int i : IndexRange(pbvh->totnode)) { - PBVHNode *node = &pbvh->nodes[i]; + /* Preemptively log UVs. */ + if (!ignore_loop_data) { + for (int i : IndexRange(pbvh->totnode)) { + PBVHNode *node = &pbvh->nodes[i]; - if ((node->flag & PBVH_Leaf) && (node->flag & PBVH_UpdateTopology)) { - for (BMFace *f : *node->bm_faces) { - BM_log_face_if_modified(bm, pbvh->bm_log, f); + if ((node->flag & PBVH_Leaf) && (node->flag & PBVH_UpdateTopology)) { + for (BMFace *f : *node->bm_faces) { + BM_log_face_if_modified(bm, pbvh->bm_log, f); + } } } } @@ -2350,7 +2342,8 @@ bool remesh_topology(BrushTester *brush_tester, bool updatePBVH, DyntopoMaskCB mask_cb, void *mask_cb_data, - int edge_limit_multiply) + int edge_limit_multiply, + float quality) { EdgeQueueContext eq_ctx(brush_tester, ob, @@ -2369,9 +2362,12 @@ bool remesh_topology(BrushTester *brush_tester, using Clock = std::chrono::high_resolution_clock; using TimePoint = std::chrono::time_point; + quality *= quality; + int time_limit = 8 * (1.0 - quality) + 128 * quality; + auto time = Clock::now(); Clock::duration limit = std::chrono::duration_cast( - std::chrono::milliseconds(350)); + std::chrono::milliseconds(time_limit)); while (!eq_ctx.done()) { eq_ctx.step(); diff --git a/source/blender/blenkernel/intern/dyntopo_intern.hh b/source/blender/blenkernel/intern/dyntopo_intern.hh index d3c19ab74dc..2dd43b0b8f9 100644 --- a/source/blender/blenkernel/intern/dyntopo_intern.hh +++ b/source/blender/blenkernel/intern/dyntopo_intern.hh @@ -134,7 +134,7 @@ inline bool bm_elem_is_free(BMElem *elem, int htype) /* Slightly relax geometry by this factor along surface tangents * to improve convergence of dyntopo remesher. */ -#define DYNTOPO_SAFE_SMOOTH_FAC 0.05f +#define DYNTOPO_SAFE_SMOOTH_FAC 0.025f #ifdef USE_EDGEQUEUE_EVEN_SUBDIV # include "BKE_global.h" @@ -267,6 +267,8 @@ struct EdgeQueueContext { int max_steps; PBVH *pbvh; + bool ignore_loop_data = false; + bool modified = false; int count = 0; int curop = 0; diff --git a/source/blender/blenkernel/intern/pbvh.cc b/source/blender/blenkernel/intern/pbvh.cc index d0298ea1c29..ba375018697 100644 --- a/source/blender/blenkernel/intern/pbvh.cc +++ b/source/blender/blenkernel/intern/pbvh.cc @@ -20,6 +20,7 @@ #include "BLI_rand.h" #include "BLI_string.h" #include "BLI_task.h" +#include "BLI_timeit.hh" #include "BLI_index_range.hh" #include "BLI_map.hh" diff --git a/source/blender/blenkernel/intern/pbvh_bmesh.cc b/source/blender/blenkernel/intern/pbvh_bmesh.cc index 0398fc3828e..cbaf211e824 100644 --- a/source/blender/blenkernel/intern/pbvh_bmesh.cc +++ b/source/blender/blenkernel/intern/pbvh_bmesh.cc @@ -2553,7 +2553,8 @@ bool remesh_topology_nodes(blender::bke::dyntopo::BrushTester *brush_tester, bool updatePBVH, DyntopoMaskCB mask_cb, void *mask_cb_data, - int edge_limit_multiply) + int edge_limit_multiply, + float quality) { bool modified = false; Vector nodes; @@ -2586,7 +2587,8 @@ bool remesh_topology_nodes(blender::bke::dyntopo::BrushTester *brush_tester, updatePBVH, mask_cb, mask_cb_data, - edge_limit_multiply); + edge_limit_multiply, + quality); return modified; } diff --git a/source/blender/editors/sculpt_paint/sculpt.cc b/source/blender/editors/sculpt_paint/sculpt.cc index c146e1464c7..dba4def7518 100644 --- a/source/blender/editors/sculpt_paint/sculpt.cc +++ b/source/blender/editors/sculpt_paint/sculpt.cc @@ -3968,7 +3968,8 @@ static void sculpt_topology_update(Sculpt *sd, // mode |= PBVH_Collapse | PBVH_Subdivide; } - int edge_multiply = 1 + int(powf(ss->cached_dyntopo.quality, 3.0f) * 50.0f); + /* XXX we limit by time instead of edge queue steps now. */ + int edge_multiply = 50; //1 + int(powf(ss->cached_dyntopo.quality, 3.0f) * 50.0f); SculptSearchSphereData sdata{}; sdata.ss = ss, sdata.sd = sd, sdata.ob = ob; @@ -4015,7 +4016,8 @@ static void sculpt_topology_update(Sculpt *sd, true, mask_cb, mask_cb_data, - edge_multiply); + edge_multiply, + ss->cached_dyntopo.quality); SCULPT_dyntopo_automasking_end(mask_cb_data); diff --git a/source/blender/makesrna/intern/rna_mesh.c b/source/blender/makesrna/intern/rna_mesh.c index e83c60d4f65..e803ff734fa 100644 --- a/source/blender/makesrna/intern/rna_mesh.c +++ b/source/blender/makesrna/intern/rna_mesh.c @@ -3745,10 +3745,6 @@ static void rna_def_mesh(BlenderRNA *brna) prop = RNA_def_property(srna, "sculpt_ignore_uvs", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", ME_SCULPT_IGNORE_UVS); RNA_def_property_ui_text(prop, "Ignore UVs", ""); - - prop = RNA_def_property(srna, "sculpt_dyntopo_relax", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", ME_FLAG_UNUSED_5); - RNA_def_property_ui_text(prop, "Relax During Remeshing", "Relax geometry during remeshing"); /* End Symmetry */ -- 2.30.2 From b04276fc6076d43e8f3398ca125c2026ac2485e6 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Fri, 9 Jun 2023 17:54:43 -0700 Subject: [PATCH 174/279] temp-sculpt-dyntopo: Tweak maximum remesh time constant --- source/blender/blenkernel/intern/dyntopo.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/source/blender/blenkernel/intern/dyntopo.cc b/source/blender/blenkernel/intern/dyntopo.cc index 63a872a0d30..701bd41041f 100644 --- a/source/blender/blenkernel/intern/dyntopo.cc +++ b/source/blender/blenkernel/intern/dyntopo.cc @@ -2363,7 +2363,7 @@ bool remesh_topology(BrushTester *brush_tester, using TimePoint = std::chrono::time_point; quality *= quality; - int time_limit = 8 * (1.0 - quality) + 128 * quality; + int time_limit = 8 * (1.0 - quality) + 550 * quality; auto time = Clock::now(); Clock::duration limit = std::chrono::duration_cast( @@ -2379,8 +2379,8 @@ bool remesh_topology(BrushTester *brush_tester, eq_ctx.finish(); - printf("time: %dms\n", - int(std::chrono::duration_cast(Clock::now() - time).count())); + //printf("time: %dms\n", + // int(std::chrono::duration_cast(Clock::now() - time).count())); return eq_ctx.modified; } -- 2.30.2 From ef919c26ed87ebdfa6e0b54062940f59286e13d0 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Fri, 9 Jun 2023 17:55:35 -0700 Subject: [PATCH 175/279] temp-sculpt-dyntopo: fix windows compile error --- source/blender/bmesh/intern/bmesh_log.cc | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/source/blender/bmesh/intern/bmesh_log.cc b/source/blender/bmesh/intern/bmesh_log.cc index 1415666bce5..58c416fb3d0 100644 --- a/source/blender/bmesh/intern/bmesh_log.cc +++ b/source/blender/bmesh/intern/bmesh_log.cc @@ -845,15 +845,13 @@ struct BMLogEntry { { BMID id = get_elem_id(bm, f); - bool exists = 0; - for (int i = sets.size() - 1; i >= 0; i--) { BMLogSetBase *set = sets[i]; if (set->type != LOG_SET_DIFF) { continue; } - // BMLogSetDiff *diff = current_diff_set(bm); + BMLogSetDiff *diff = static_cast(set); if (diff->modified_faces.contains(id) || diff->removed_faces.contains(id) || diff->added_faces.contains(id)) -- 2.30.2 From a3264236124a2418c60ab2a53b895285878f896f Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Fri, 9 Jun 2023 19:05:27 -0700 Subject: [PATCH 176/279] temp-sculpt-dyntopo: Fix edge seam smoothing bug --- source/blender/blenkernel/intern/dyntopo.cc | 8 ++++++-- source/blender/editors/sculpt_paint/sculpt_smooth.cc | 11 +++++++---- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/source/blender/blenkernel/intern/dyntopo.cc b/source/blender/blenkernel/intern/dyntopo.cc index 701bd41041f..882c4c59657 100644 --- a/source/blender/blenkernel/intern/dyntopo.cc +++ b/source/blender/blenkernel/intern/dyntopo.cc @@ -2379,8 +2379,9 @@ bool remesh_topology(BrushTester *brush_tester, eq_ctx.finish(); - //printf("time: %dms\n", - // int(std::chrono::duration_cast(Clock::now() - time).count())); + // printf("time: %dms\n", + // int(std::chrono::duration_cast(Clock::now() - + // time).count())); return eq_ctx.modified; } @@ -2526,6 +2527,9 @@ void EdgeQueueContext::split_edge(BMEdge *e) *BM_ELEM_CD_PTR(newv, pbvh->cd_boundary_flag) &= ~SCULPT_BOUNDARY_UV; } + /* Flag newv to not update original coordinates. */ + stroke_id_test(ss, {reinterpret_cast(newv)}, STROKEID_USER_ORIGINAL); + BM_ELEM_CD_SET_INT(newv, pbvh->cd_vert_node_offset, DYNTOPO_NODE_NONE); BM_idmap_check_assign(pbvh->bm_idmap, reinterpret_cast(newv)); BM_log_vert_added(bm, pbvh->bm_log, newv); diff --git a/source/blender/editors/sculpt_paint/sculpt_smooth.cc b/source/blender/editors/sculpt_paint/sculpt_smooth.cc index e0922ea076e..f209befde7b 100644 --- a/source/blender/editors/sculpt_paint/sculpt_smooth.cc +++ b/source/blender/editors/sculpt_paint/sculpt_smooth.cc @@ -114,6 +114,11 @@ static void SCULPT_neighbor_coords_average_interior_ex(SculptSession *ss, eSculptBoundary uvflag = ss->reproject_smooth ? SCULPT_BOUNDARY_UV : SCULPT_BOUNDARY_NONE; + eSculptBoundary hard_flags = SCULPT_BOUNDARY_SHARP_MARK | SCULPT_BOUNDARY_SHARP_ANGLE; + if (ss->hard_edge_mode) { + hard_flags |= SCULPT_BOUNDARY_FACE_SET; + } + bound_type |= uvflag; const eSculptBoundary is_boundary = SCULPT_vertex_is_boundary(ss, vertex, bound_type); @@ -198,9 +203,7 @@ static void SCULPT_neighbor_coords_average_interior_ex(SculptSession *ss, eSculptBoundary smooth_flag2 = is_boundary2 & smooth_types; /* Handle smooth boundaries. */ - if (bool(smooth_flag) != bool(smooth_flag2) && - (smooth_flag == is_boundary && smooth_flag2 == is_boundary2)) - { + if (!(is_boundary & hard_flags) && bool(smooth_flag) != bool(smooth_flag2)) { /* Project to plane. */ float3 t1 = float3(vertex_co_get(ss, ni.vertex)) - co; float fac = dot_v3v3(t1, no); @@ -213,7 +216,7 @@ static void SCULPT_neighbor_coords_average_interior_ex(SculptSession *ss, avg += tco * w; total += w; } - else if ((is_boundary & ~smooth_flag) & (is_boundary2 & ~smooth_flag2)) { + else if ((is_boundary & hard_flags) & (is_boundary2 & hard_flags)) { avg += float3(vertex_co_get(ss, ni.vertex)) * w; total += w; project_ok = true; -- 2.30.2 From 284588fd4c1c07352b3b462978009f127493628e Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Fri, 9 Jun 2023 23:25:34 -0700 Subject: [PATCH 177/279] temp-sculpt-dyntopo: Bugfixes * Fixed exploding layer and draw sharp brushes * Fixed various crashes. --- build_files/cmake/macros.cmake | 2 +- source/blender/blenkernel/BKE_customdata.h | 3 + source/blender/blenkernel/BKE_pbvh.h | 53 +------ source/blender/blenkernel/BKE_sculpt.hh | 74 +++++++++- .../blender/blenkernel/intern/customdata.cc | 48 +++++++ source/blender/blenkernel/intern/dyntopo.cc | 62 +++++++- .../blenkernel/intern/dyntopo_collapse.cc | 25 +++- .../blenkernel/intern/dyntopo_intern.hh | 5 +- source/blender/blenkernel/intern/paint.cc | 7 - .../blender/blenkernel/intern/pbvh_bmesh.cc | 5 + source/blender/bmesh/intern/bmesh_log.cc | 19 +++ source/blender/draw/CMakeLists.txt | 3 - source/blender/editors/sculpt_paint/sculpt.cc | 2 +- .../editors/sculpt_paint/sculpt_api.cc | 134 ++++++++++++------ .../sculpt_paint/sculpt_brush_types.cc | 12 ++ .../sculpt_paint/sculpt_paint_color.cc | 9 +- .../editors/sculpt_paint/sculpt_smooth.cc | 4 +- 17 files changed, 341 insertions(+), 126 deletions(-) diff --git a/build_files/cmake/macros.cmake b/build_files/cmake/macros.cmake index 42a0dd58660..96b3712b5a2 100644 --- a/build_files/cmake/macros.cmake +++ b/build_files/cmake/macros.cmake @@ -1091,7 +1091,7 @@ function(msgfmt_simple add_custom_command( OUTPUT ${_file_to} COMMAND ${CMAKE_COMMAND} -E make_directory ${_file_to_path} - COMMAND ${CMAKE_COMMAND} -E env ${PLATFORM_ENV_BUILD} "$" ${_file_from} ${_file_to} + COMMAND ${CMAKE_COMMAND} -E env "$" ${_file_from} ${_file_to} DEPENDS msgfmt ${_file_from}) set_source_files_properties(${_file_to} PROPERTIES GENERATED TRUE) diff --git a/source/blender/blenkernel/BKE_customdata.h b/source/blender/blenkernel/BKE_customdata.h index b97e2651001..52c9f0a6e8e 100644 --- a/source/blender/blenkernel/BKE_customdata.h +++ b/source/blender/blenkernel/BKE_customdata.h @@ -365,6 +365,9 @@ void CustomData_copy_data_named(const struct CustomData *source, int dest_index, int count); +void CustomData_bmesh_poison(const struct CustomData *cdata, void *block); +void CustomData_bmesh_unpoison(const struct CustomData *cdata, void *block); + /* Swap attributes. Does not respect CD_FLAG_ELEM_NOCOPY. */ void CustomData_bmesh_swap_data(struct CustomData *source, struct CustomData *dest, diff --git a/source/blender/blenkernel/BKE_pbvh.h b/source/blender/blenkernel/BKE_pbvh.h index f442fcf2790..70178de55b5 100644 --- a/source/blender/blenkernel/BKE_pbvh.h +++ b/source/blender/blenkernel/BKE_pbvh.h @@ -794,7 +794,9 @@ void pbvh_vertex_iter_init(PBVH *pbvh, PBVHNode *node, struct PBVHVertexIter *vi } \ vi.co = vi.bm_vert->co; \ vi.fno = vi.bm_vert->no; \ - vi.mask = (float *)BM_ELEM_CD_GET_VOID_P(vi.bm_vert, vi.cd_vert_mask_offset); \ + vi.mask = vi.cd_vert_mask_offset != -1 ? \ + (float *)BM_ELEM_CD_GET_VOID_P(vi.bm_vert, vi.cd_vert_mask_offset) : \ + nullptr; \ } #define BKE_pbvh_vertex_iter_end \ @@ -984,55 +986,6 @@ void BKE_pbvh_set_symmetry(PBVH *pbvh, int symmetry, int boundary_symmetry); int BKE_pbvh_do_fset_symmetry(int fset, const int symflag, const float *co); -/* Uncomment to enable PBVH NaN debugging. */ -//#define PBVH_CHECK_NANS - -#ifdef PBVH_CHECK_NANS -# include "atomic_ops.h" -# include -# include - -/* Why is atomic_ops defining near & far macros? */ -# ifdef near -# undef near -# endif -# ifdef far -# undef far -# endif - -// static global to limit the number of reports per source file -static int _bke_pbvh_report_count = 0; - -# define PBVH_NAN_REPORT_LIMIT 16 - -// for debugging NaNs that don't appear on developer's machines -BLI_INLINE bool _pbvh_nan_check(const float *co, const char *func, const char *file, int line) -{ - bool bad = false; - - if (_bke_pbvh_report_count > PBVH_NAN_REPORT_LIMIT) { - return false; - } - - for (int i = 0; i < 3; i++) { - if (isnan(co[i]) || !isfinite(co[i])) { - const char *type = !isfinite(co[i]) ? "infinity" : "nan"; - printf("float corruption (vector[%d] was %s): %s:%d\n\t%s\n", i, type, func, line, file); - bad = true; - } - } - - if (bad) { - atomic_add_and_fetch_int32(&_bke_pbvh_report_count, 1); - } - - return bad; -} -# define PBVH_CHECK_NAN(co) _pbvh_nan_check(co, __func__, __FILE__, __LINE__) -#else -# define PBVH_CHECK_NAN(co) -#endif - /* Uses pmap to build an array of edge indices surrounding vertex r_edges, r_edges_size, heap_alloc define an existing array to put data in. diff --git a/source/blender/blenkernel/BKE_sculpt.hh b/source/blender/blenkernel/BKE_sculpt.hh index 50224592338..d8f0fa20c16 100644 --- a/source/blender/blenkernel/BKE_sculpt.hh +++ b/source/blender/blenkernel/BKE_sculpt.hh @@ -153,7 +153,7 @@ struct VertLoopSnapper { Vector max_indices; float limit = 0.001; - VertLoopSnapper(Span ls_, Span layers_) : ls(ls_), layers(layers_) + VertLoopSnapper(Span ls_, Span layers_) : layers(layers_), ls(ls_) { snap_sets.resize(ls.size()); for (auto &snap_set : snap_sets) { @@ -223,7 +223,6 @@ struct VertLoopSnapper { len /= ls.size(); limit *= len; - // printf("limit: %.5f len: %.5f\n", limit, len); } for (int i : ls.index_range()) { @@ -279,3 +278,74 @@ struct VertLoopSnapper { } }; } // namespace blender::bke::sculpt + +/* Uncomment to enable PBVH NaN debugging. */ +//#define PBVH_CHECK_NANS + +#ifdef PBVH_CHECK_NANS +# include "atomic_ops.h" +# include +# include + +/* Why is atomic_ops defining near & far macros? */ +# ifdef near +# undef near +# endif +# ifdef far +# undef far +# endif + +// static global to limit the number of reports per source file +static int _bke_pbvh_report_count = 0; + +# define PBVH_NAN_REPORT_LIMIT 16 + +// for debugging NaNs that don't appear on developer's machines +static ATTR_NO_OPT bool _pbvh_nan_check1(const float f, + const char *func, + const char *file, + int line) +{ + bool bad = false; + + if (_bke_pbvh_report_count > PBVH_NAN_REPORT_LIMIT) { + return false; + } + + if (isnan(f) || !isfinite(f)) { + const char *type = !isfinite(f) ? "infinity" : "nan"; + printf("float corruption (value was %s): %s:%d\n\t%s\n", type, func, line, file); + bad = true; + } + + if (bad) { + atomic_add_and_fetch_int32(&_bke_pbvh_report_count, 1); + } + + return bad; +} + +static ATTR_NO_OPT bool _pbvh_nan_check3(const float co[3], + const char *func, + const char *file, + int line) +{ + if (!co) { + return false; + } + + bool ret = false; + + for (int i = 0; i < 3; i++) { + ret |= _pbvh_nan_check1(co[i], func, file, line); + } + + return ret; +} + +# define PBVH_CHECK_NAN(co) _pbvh_nan_check3(co, __func__, __FILE__, __LINE__) +# define PBVH_CHECK_NAN1(f) _pbvh_nan_check1(f, __func__, __FILE__, __LINE__) +#else +# define PBVH_CHECK_NAN(co) +# define PBVH_CHECK_NAN1(f) +#endif diff --git a/source/blender/blenkernel/intern/customdata.cc b/source/blender/blenkernel/intern/customdata.cc index edd10d9ce5c..e8a6b4007bd 100644 --- a/source/blender/blenkernel/intern/customdata.cc +++ b/source/blender/blenkernel/intern/customdata.cc @@ -19,6 +19,7 @@ #include "DNA_meshdata_types.h" #include "BLI_array.hh" +#include "BLI_asan.h" #include "BLI_bitmap.h" #include "BLI_color.hh" #include "BLI_compiler_attrs.h" @@ -60,6 +61,10 @@ #include "CLG_log.h" +#ifdef WITH_ASAN +# define BM_ASAN_PAD 32 +#endif + /* only for customdata_data_transfer_interp_normal_normals */ #include "data_transfer_intern.h" @@ -2703,6 +2708,11 @@ static void customData_update_offsets(CustomData *data) const LayerTypeInfo *typeInfo; int offset = 0; + if (data->totlayer == 0) { + data->totsize = 0; + return; + } + /* Sort by alignment. */ int aligns[] = {8, 4, 2, 1}; Array donemap(data->totlayer, false); @@ -2757,6 +2767,10 @@ static void customData_update_offsets(CustomData *data) layer->offset = offset; offset += size; + +#ifdef WITH_ASAN + offset += BM_ASAN_PAD; +#endif } } @@ -2784,6 +2798,9 @@ static void customData_update_offsets(CustomData *data) layer->offset = offset; offset += size; +#ifdef WITH_ASAN + offset += BM_ASAN_PAD; +#endif } } @@ -2791,11 +2808,35 @@ static void customData_update_offsets(CustomData *data) offset += max_alignment - (offset % max_alignment); } +#ifdef WITH_ASAN + offset += BM_ASAN_PAD; +#endif + data->totsize = offset; CustomData_update_typemap(data); } +void CustomData_bmesh_poison(const CustomData *data, void *block) +{ +#ifdef WITH_ASAN + BLI_asan_poison(block, data->totsize); + for (int i = 0; i < data->totlayer; i++) { + CustomDataLayer *layer = data->layers + i; + size_t size = CustomData_sizeof(eCustomDataType(layer->type)); + + BLI_asan_unpoison(POINTER_OFFSET(block, layer->offset), size); + } +#endif +} + +void CustomData_bmesh_unpoison(const CustomData *data, void *block) +{ +#ifdef WITH_ASAN + BLI_asan_unpoison(block, data->totsize); +#endif +} + /* to use when we're in the middle of modifying layers */ static int CustomData_get_layer_index__notypemap(const CustomData *data, const eCustomDataType type) @@ -4107,6 +4148,7 @@ void CustomData_bmesh_free_block(CustomData *data, void **block) } if (data->totsize) { + CustomData_bmesh_unpoison(data, *block); BLI_mempool_free(data->pool, *block); } @@ -4143,6 +4185,8 @@ void CustomData_bmesh_alloc_block(CustomData *data, void **block) if (data->totsize > 0) { *block = BLI_mempool_alloc(data->pool); + CustomData_bmesh_poison(data, *block); + /* Clear toolflags pointer when created for the first time. */ int cd_tflags = data->typemap[CD_TOOLFLAGS]; if (cd_tflags != -1) { @@ -4247,7 +4291,9 @@ void CustomData_bmesh_swap_data(CustomData *source, CustomData_bmesh_alloc_block(dest, dest_block); if (*dest_block) { + CustomData_bmesh_unpoison(dest, *dest_block); memset(*dest_block, 0, dest->totsize); + CustomData_bmesh_poison(dest, *dest_block); CustomData_bmesh_set_default(dest, dest_block); } @@ -4331,7 +4377,9 @@ void CustomData_bmesh_copy_data_exclude_by_type(const CustomData *source, CustomData_bmesh_alloc_block(dest, dest_block); if (*dest_block) { + CustomData_bmesh_unpoison(dest, *dest_block); memset(*dest_block, 0, dest->totsize); + CustomData_bmesh_poison(dest, *dest_block); was_new = true; } diff --git a/source/blender/blenkernel/intern/dyntopo.cc b/source/blender/blenkernel/intern/dyntopo.cc index 882c4c59657..6249aacb771 100644 --- a/source/blender/blenkernel/intern/dyntopo.cc +++ b/source/blender/blenkernel/intern/dyntopo.cc @@ -83,6 +83,8 @@ static void surface_smooth_v_safe( return; } + PBVH_CHECK_NAN(v->co); + Vector loops; Vector ws; @@ -218,6 +220,8 @@ static void surface_smooth_v_safe( atomic_cas_float(&v->co[1], y, ny); atomic_cas_float(&v->co[2], z, nz); + PBVH_CHECK_NAN(v->co); + /* * Use reprojection for non-UV attributes. UV attributes * use blender::bke::sculpt::interp_face_corners using @@ -229,6 +233,8 @@ static void surface_smooth_v_safe( blender::bke::sculpt::interp_face_corners(pbvh, vertex, loops, ws, fac); } + PBVH_CHECK_NAN(v->co); + float *start_origco = blender::bke::paint::vertex_attr_ptr(vertex, ss->attrs.orig_co); /* Conflicts here should be pretty rare. */ @@ -2217,7 +2223,7 @@ void EdgeQueueContext::step() float prob = ops[curop] == PBVH_Subdivide ? 0.25 : 0.75; if (srand.get_float() > prob) { - surface_smooth_v_safe(ss, pbvh, v, surface_smooth_fac, true); + surface_smooth_v_safe(ss, pbvh, v, surface_smooth_fac, reproject_cdata); } }; @@ -2473,6 +2479,9 @@ void EdgeQueueContext::split_edge(BMEdge *e) BMEdge *newe; BMFace *newf = nullptr; + PBVH_CHECK_NAN(e->v1->co); + PBVH_CHECK_NAN(e->v2->co); + if (!e->l) { return; } @@ -2503,6 +2512,11 @@ void EdgeQueueContext::split_edge(BMEdge *e) BM_log_edge_removed(bm, pbvh->bm_log, e); BM_idmap_release(pbvh->bm_idmap, reinterpret_cast(e), true); + StrokeID stroke_id1 = blender::bke::paint::vertex_attr_get( + {reinterpret_cast(e->v1)}, ss->attrs.stroke_id); + StrokeID stroke_id2 = blender::bke::paint::vertex_attr_get( + {reinterpret_cast(e->v2)}, ss->attrs.stroke_id); + dyntopo_add_flag(pbvh, e->v1, SCULPTFLAG_NEED_VALENCE); dyntopo_add_flag(pbvh, e->v2, SCULPTFLAG_NEED_VALENCE); pbvh_boundary_update_bmesh(pbvh, e->v1); @@ -2514,6 +2528,8 @@ void EdgeQueueContext::split_edge(BMEdge *e) BMVert *newv = BM_edge_split(bm, e, e->v1, &newe, 0.5f); + PBVH_CHECK_NAN(newv->co); + /* Remove edge-in-minmax-heap tag. */ e->head.hflag &= ~EDGE_QUEUE_FLAG; newe->head.hflag &= ~EDGE_QUEUE_FLAG; @@ -2527,8 +2543,22 @@ void EdgeQueueContext::split_edge(BMEdge *e) *BM_ELEM_CD_PTR(newv, pbvh->cd_boundary_flag) &= ~SCULPT_BOUNDARY_UV; } - /* Flag newv to not update original coordinates. */ - stroke_id_test(ss, {reinterpret_cast(newv)}, STROKEID_USER_ORIGINAL); + /* Propagate current stroke id. */ + StrokeID stroke_id; + + if (stroke_id1.id < stroke_id2.id) { + std::swap(stroke_id1, stroke_id2); + } + + stroke_id.id = stroke_id1.id; + if (stroke_id2.id < stroke_id1.id) { + stroke_id.userflag = stroke_id1.userflag; + } + else { + stroke_id.userflag = stroke_id1.userflag & stroke_id2.userflag; + } + + *BM_ELEM_CD_PTR(newv, ss->attrs.stroke_id->bmesh_cd_offset) = stroke_id; BM_ELEM_CD_SET_INT(newv, pbvh->cd_vert_node_offset, DYNTOPO_NODE_NONE); BM_idmap_check_assign(pbvh->bm_idmap, reinterpret_cast(newv)); @@ -3413,6 +3443,13 @@ inline void reproject_bm_data( T totw = 0.0; for (int i = 0; i < f_src->len; i++) { + if (isnan(w[i])) { + printf("%s: NaN\n", __func__); + /* Use uniform weights. */ + totw = 0.0; + break; + } + totw += w[i]; } @@ -3571,7 +3608,7 @@ void BKE_sculpt_reproject_cdata( char *_blocks = static_cast(alloca(ldata->totsize * totloop)); void **blocks = static_cast(BLI_array_alloca(blocks, totloop)); - const int max_vblocks = valence * 2; + const int max_vblocks = valence * 3; char *_vblocks = static_cast(alloca(ss->bm->vdata.totsize * max_vblocks)); void **vblocks = static_cast(BLI_array_alloca(vblocks, max_vblocks)); @@ -3742,7 +3779,14 @@ void BKE_sculpt_reproject_cdata( BMLoop _interpl, *interpl = &_interpl; *interpl = *l; - memcpy(blocks[i], l->head.data, ldata->totsize); + +#ifdef WITH_ASAN + /* Can't unpoison memory in threaded code. */ + CustomData_bmesh_copy_data(&ss->bm->ldata, &ss->bm->ldata, l->head.data, &blocks[i]); +#else + memcpy(blocks[i], l->head.data, ss->bm->ldata.totsize); +#endif + interpl->head.data = blocks[i]; interp_v3_v3v3(l->v->co, startco, vco, t); @@ -3753,7 +3797,13 @@ void BKE_sculpt_reproject_cdata( void *vblock_old = interpl->v->head.data; void *vblock = vblocks[cur_vblock]; - memcpy((void *)vblock, v->head.data, ss->bm->vdata.totsize); +#ifdef WITH_ASAN + /* Can't unpoison memory in threaded code. */ + CustomData_bmesh_copy_data(&ss->bm->vdata, &ss->bm->vdata, v->head.data, &vblocks[i]); +#else + memcpy(vblocks[i], v->head.data, ss->bm->vdata.totsize); +#endif + interpl->v->head.data = (void *)vblock; reproject_bm_data(ss->bm, interpl, fakef, true, typemask); diff --git a/source/blender/blenkernel/intern/dyntopo_collapse.cc b/source/blender/blenkernel/intern/dyntopo_collapse.cc index 744c70531f4..cc34f3a57d1 100644 --- a/source/blender/blenkernel/intern/dyntopo_collapse.cc +++ b/source/blender/blenkernel/intern/dyntopo_collapse.cc @@ -326,14 +326,15 @@ class DyntopoCollapseCallbacks { inline void on_vert_kill(BMVert *v) { - dyntopo_add_flag(pbvh, v, SCULPTFLAG_NEED_VALENCE); - BM_log_vert_removed(bm, pbvh->bm_log, v); pbvh_bmesh_vert_remove(pbvh, v); BM_idmap_release(pbvh->bm_idmap, reinterpret_cast(v), false); } inline void on_edge_kill(BMEdge *e) { + dyntopo_add_flag(pbvh, e->v1, SCULPTFLAG_NEED_VALENCE); + dyntopo_add_flag(pbvh, e->v2, SCULPTFLAG_NEED_VALENCE); + BM_log_edge_removed(bm, pbvh->bm_log, e); BM_idmap_release(pbvh->bm_idmap, reinterpret_cast(e), false); } @@ -357,10 +358,15 @@ class DyntopoCollapseCallbacks { /* Combine boundary flags. */ int boundflag = BM_ELEM_CD_GET_INT(source, pbvh->cd_boundary_flag); BM_ELEM_CD_SET_INT(dest, pbvh->cd_boundary_flag, boundflag); + + dyntopo_add_flag(pbvh, dest, SCULPTFLAG_NEED_VALENCE); } inline void on_edge_combine(BMEdge *dest, BMEdge *source) { + dyntopo_add_flag(pbvh, dest->v1, SCULPTFLAG_NEED_VALENCE); + dyntopo_add_flag(pbvh, dest->v2, SCULPTFLAG_NEED_VALENCE); + /* Combine boundary flags. */ int boundflag = BM_ELEM_CD_GET_INT(source, pbvh->cd_edge_boundary); BM_ELEM_CD_SET_INT(dest, pbvh->cd_edge_boundary, boundflag); @@ -371,6 +377,9 @@ class DyntopoCollapseCallbacks { inline void on_edge_create(BMEdge *e) { + dyntopo_add_flag(pbvh, e->v1, SCULPTFLAG_NEED_VALENCE); + dyntopo_add_flag(pbvh, e->v2, SCULPTFLAG_NEED_VALENCE); + check_new_elem_id(e, pbvh); pbvh_boundary_update_bmesh(pbvh, e); BM_log_edge_added(bm, pbvh->bm_log, e); @@ -384,6 +393,7 @@ class DyntopoCollapseCallbacks { BMLoop *l = f->l_first; do { + dyntopo_add_flag(pbvh, l->v, SCULPTFLAG_NEED_VALENCE); pbvh_boundary_update_bmesh(pbvh, l->v); pbvh_boundary_update_bmesh(pbvh, l->e); } while ((l = l->next) != f->l_first); @@ -402,6 +412,9 @@ BMVert *EdgeQueueContext::collapse_edge(PBVH *pbvh, BMEdge *e, BMVert *v1, BMVer return nullptr; } + PBVH_CHECK_NAN(v1->co); + PBVH_CHECK_NAN(v2->co); + TraceData tdata; tdata.ss = ss; @@ -430,8 +443,8 @@ BMVert *EdgeQueueContext::collapse_edge(PBVH *pbvh, BMEdge *e, BMVert *v1, BMVer return nullptr; } - float w1 = DYNTOPO_MASK(cd_vert_mask_offset, v1); - float w2 = DYNTOPO_MASK(cd_vert_mask_offset, v2); + float w1 = mask_cb ? 1.0f - mask_cb({reinterpret_cast(v1)}, mask_cb_data) : 0.0f; + float w2 = mask_cb ? 1.0f - mask_cb({reinterpret_cast(v2)}, mask_cb_data) : 0.0f; bool corner1 = (boundflag1 & SCULPTVERT_ALL_CORNER) || w1 >= 0.85; bool corner2 = (boundflag2 & SCULPTVERT_ALL_CORNER) || w2 >= 0.85; @@ -588,7 +601,9 @@ BMVert *EdgeQueueContext::collapse_edge(PBVH *pbvh, BMEdge *e, BMVert *v1, BMVer validate_vert(pbvh, v_conn, CHECK_VERT_FACES | CHECK_VERT_NODE_ASSIGNED); - PBVH_CHECK_NAN(v_conn->co); + if (v_conn) { + PBVH_CHECK_NAN(v_conn->co); + } return v_conn; } diff --git a/source/blender/blenkernel/intern/dyntopo_intern.hh b/source/blender/blenkernel/intern/dyntopo_intern.hh index 2dd43b0b8f9..b41ccf10653 100644 --- a/source/blender/blenkernel/intern/dyntopo_intern.hh +++ b/source/blender/blenkernel/intern/dyntopo_intern.hh @@ -134,7 +134,7 @@ inline bool bm_elem_is_free(BMElem *elem, int htype) /* Slightly relax geometry by this factor along surface tangents * to improve convergence of dyntopo remesher. */ -#define DYNTOPO_SAFE_SMOOTH_FAC 0.025f +#define DYNTOPO_SAFE_SMOOTH_FAC 0.01f #ifdef USE_EDGEQUEUE_EVEN_SUBDIV # include "BKE_global.h" @@ -155,7 +155,8 @@ inline bool bm_elem_is_free(BMElem *elem, int htype) // #define USE_VERIFY -#define DYNTOPO_MASK(cd_mask_offset, v) BM_ELEM_CD_GET_FLOAT(v, cd_mask_offset) +#define DYNTOPO_MASK(cd_mask_offset, v) \ + (cd_mask_offset != -1 ? BM_ELEM_CD_GET_FLOAT(v, cd_mask_offset) : 0.0f) #ifdef USE_VERIFY static void pbvh_bmesh_verify(PBVH *pbvh); diff --git a/source/blender/blenkernel/intern/paint.cc b/source/blender/blenkernel/intern/paint.cc index d4653e762f8..bb10940c548 100644 --- a/source/blender/blenkernel/intern/paint.cc +++ b/source/blender/blenkernel/intern/paint.cc @@ -4411,9 +4411,6 @@ bool loop_is_corner(BMLoop *l, int cd_uv, float limit) } bool ret = keys.size() > 2; - if (ret) { - l->v->head.hflag |= BM_ELEM_SELECT; - } return ret; } @@ -4484,10 +4481,6 @@ bool loop_is_corner(BMLoop *l, int cd_offset) float angle_limit = 110.0f / 180.0f * M_PI; float angle = saacos(dot_v2v2(t1, t2)); - if (angle < angle_limit) { - l->v->head.hflag |= BM_ELEM_SELECT; - } - return angle < angle_limit; } #endif diff --git a/source/blender/blenkernel/intern/pbvh_bmesh.cc b/source/blender/blenkernel/intern/pbvh_bmesh.cc index cbaf211e824..35d15cca151 100644 --- a/source/blender/blenkernel/intern/pbvh_bmesh.cc +++ b/source/blender/blenkernel/intern/pbvh_bmesh.cc @@ -2201,6 +2201,11 @@ void update_vert_boundary_bmesh(int cd_faceset_offset, } if (e->l) { + if ((e->l->f->head.hflag & BM_ELEM_SMOOTH) || + (e->l->radial_next->f->head.hflag & BM_ELEM_SMOOTH)) { + newflag |= SCULPTFLAG_VERT_FSET_HIDDEN; + } + if (BM_ELEM_CD_GET_INT(e->l->f, cd_face_node_offset) != ni) { newflag |= SCULPTFLAG_PBVH_BOUNDARY; } diff --git a/source/blender/bmesh/intern/bmesh_log.cc b/source/blender/bmesh/intern/bmesh_log.cc index 58c416fb3d0..8603c45c7ab 100644 --- a/source/blender/bmesh/intern/bmesh_log.cc +++ b/source/blender/bmesh/intern/bmesh_log.cc @@ -693,6 +693,7 @@ struct BMLogEntry { void free_logvert(BMLogVert *lv) { if (lv->customdata) { + CustomData_bmesh_unpoison(&vdata, lv->customdata); BLI_mempool_free(vdata.pool, lv->customdata); } @@ -732,6 +733,7 @@ struct BMLogEntry { void free_logedge(BMesh * /*bm*/, BMLogEdge *le) { if (le->customdata) { + CustomData_bmesh_unpoison(&edata, le->customdata); BLI_mempool_free(edata.pool, le->customdata); } @@ -788,11 +790,13 @@ struct BMLogEntry { { if (lf->loop_customdata[0]) { for (int i = 0; i < lf->verts.size(); i++) { + CustomData_bmesh_unpoison(&ldata, lf->loop_customdata[i]); BLI_mempool_free(ldata.pool, lf->loop_customdata[i]); } } if (lf->customdata) { + CustomData_bmesh_unpoison(&pdata, lf->customdata); BLI_mempool_free(pdata.pool, lf->customdata); } @@ -1257,7 +1261,11 @@ void BMLogSetDiff::swap_verts(BMesh *bm, } if (old_customdata) { + CustomData_bmesh_unpoison(&bm->vdata, old_customdata); + CustomData_bmesh_unpoison(&bm->vdata, v->head.data); memcpy(old_customdata, v->head.data, bm->vdata.totsize); + CustomData_bmesh_poison(&bm->vdata, old_customdata); + CustomData_bmesh_poison(&bm->vdata, v->head.data); } entry->swap_logvert(bm, lv->id, v, lv); @@ -1271,6 +1279,7 @@ void BMLogSetDiff::swap_verts(BMesh *bm, } if (old_customdata) { + CustomData_bmesh_unpoison(&bm->vdata, old_customdata); BLI_mempool_free(bm->vdata.pool, old_customdata); } } @@ -1388,7 +1397,11 @@ void BMLogSetDiff::swap_edges(BMesh *bm, } if (old_customdata) { + CustomData_bmesh_unpoison(&bm->edata, old_customdata); + CustomData_bmesh_unpoison(&bm->edata, e->head.data); memcpy(old_customdata, e->head.data, bm->edata.totsize); + CustomData_bmesh_poison(&bm->edata, old_customdata); + CustomData_bmesh_poison(&bm->edata, e->head.data); } entry->swap_logedge(bm, le->id, e, le); @@ -1399,6 +1412,7 @@ void BMLogSetDiff::swap_edges(BMesh *bm, } if (old_customdata) { + CustomData_bmesh_unpoison(&bm->edata, old_customdata); BLI_mempool_free(bm->edata.pool, old_customdata); } } @@ -1496,7 +1510,11 @@ void BMLogSetDiff::swap_faces(BMesh *bm, } if (old_customdata) { + CustomData_bmesh_unpoison(&bm->pdata, old_customdata); + CustomData_bmesh_unpoison(&bm->pdata, f->head.data); memcpy(old_customdata, f->head.data, bm->pdata.totsize); + CustomData_bmesh_poison(&bm->pdata, old_customdata); + CustomData_bmesh_poison(&bm->pdata, f->head.data); } entry->swap_logface(bm, lf->id, f, lf); @@ -1510,6 +1528,7 @@ void BMLogSetDiff::swap_faces(BMesh *bm, } if (old_customdata) { + CustomData_bmesh_unpoison(&bm->pdata, old_customdata); BLI_mempool_free(bm->pdata.pool, old_customdata); } } diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt index 7a2a1c15535..720b9a59e95 100644 --- a/source/blender/draw/CMakeLists.txt +++ b/source/blender/draw/CMakeLists.txt @@ -816,9 +816,6 @@ if(WITH_GTESTS) endif() endif() -remove_cc_flag("-fsanitize=address") -remove_cc_flag("/fsanitize=address") - if(WITH_TBB) add_definitions(-DWITH_TBB) diff --git a/source/blender/editors/sculpt_paint/sculpt.cc b/source/blender/editors/sculpt_paint/sculpt.cc index dba4def7518..96bcaf5d588 100644 --- a/source/blender/editors/sculpt_paint/sculpt.cc +++ b/source/blender/editors/sculpt_paint/sculpt.cc @@ -3969,7 +3969,7 @@ static void sculpt_topology_update(Sculpt *sd, } /* XXX we limit by time instead of edge queue steps now. */ - int edge_multiply = 50; //1 + int(powf(ss->cached_dyntopo.quality, 3.0f) * 50.0f); + int edge_multiply = 50; // 1 + int(powf(ss->cached_dyntopo.quality, 3.0f) * 50.0f); SculptSearchSphereData sdata{}; sdata.ss = ss, sdata.sd = sd, sdata.ob = ob; diff --git a/source/blender/editors/sculpt_paint/sculpt_api.cc b/source/blender/editors/sculpt_paint/sculpt_api.cc index ec6e9b20bde..b56ec6f0989 100644 --- a/source/blender/editors/sculpt_paint/sculpt_api.cc +++ b/source/blender/editors/sculpt_paint/sculpt_api.cc @@ -146,7 +146,7 @@ eSculptBoundary SCULPT_edge_is_boundary(const SculptSession *ss, BMEdge *e = reinterpret_cast(edge.i); blender::bke::pbvh::update_edge_boundary_bmesh( e, - ss->attrs.face_set->bmesh_cd_offset, + ss->attrs.face_set ? ss->attrs.face_set->bmesh_cd_offset : -1, ss->attrs.edge_boundary_flags->bmesh_cd_offset, ss->attrs.flags->bmesh_cd_offset, ss->attrs.valence->bmesh_cd_offset, @@ -158,44 +158,46 @@ eSculptBoundary SCULPT_edge_is_boundary(const SculptSession *ss, Span pos = {reinterpret_cast(ss->vert_positions), ss->totvert}; Span nor = {reinterpret_cast(ss->vert_normals), ss->totvert}; - blender::bke::pbvh::update_edge_boundary_faces(edge.i, - pos, - nor, - ss->edges, - ss->polys, - ss->poly_normals, - (int *)ss->attrs.edge_boundary_flags->data, - (int *)ss->attrs.boundary_flags->data, - (int *)ss->attrs.face_set->data, - ss->sharp_edge, - ss->seam_edge, - ss->pmap, - ss->epmap, - ss->ldata, - ss->sharp_angle_limit, - ss->corner_verts, - ss->corner_edges); + blender::bke::pbvh::update_edge_boundary_faces( + edge.i, + pos, + nor, + ss->edges, + ss->polys, + ss->poly_normals, + (int *)ss->attrs.edge_boundary_flags->data, + (int *)ss->attrs.boundary_flags->data, + ss->attrs.face_set ? (int *)ss->attrs.face_set->data : nullptr, + ss->sharp_edge, + ss->seam_edge, + ss->pmap, + ss->epmap, + ss->ldata, + ss->sharp_angle_limit, + ss->corner_verts, + ss->corner_edges); break; } case PBVH_GRIDS: { const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh); - blender::bke::pbvh::update_edge_boundary_grids(edge.i, - ss->edges, - ss->polys, - (int *)ss->attrs.edge_boundary_flags->data, - (int *)ss->attrs.boundary_flags->data, - (int *)ss->attrs.face_set->data, - ss->sharp_edge, - ss->seam_edge, - ss->pmap, - ss->epmap, - ss->ldata, - ss->subdiv_ccg, - key, - ss->sharp_angle_limit, - ss->corner_verts, - ss->corner_edges); + blender::bke::pbvh::update_edge_boundary_grids( + edge.i, + ss->edges, + ss->polys, + (int *)ss->attrs.edge_boundary_flags->data, + (int *)ss->attrs.boundary_flags->data, + ss->attrs.face_set ? (int *)ss->attrs.face_set->data : nullptr, + ss->sharp_edge, + ss->seam_edge, + ss->pmap, + ss->epmap, + ss->ldata, + ss->subdiv_ccg, + key, + ss->sharp_angle_limit, + ss->corner_verts, + ss->corner_edges); break; } @@ -399,25 +401,71 @@ bool SCULPT_vertex_check_origdata(SculptSession *ss, PBVHVertRef vertex) return blender::bke::paint::get_original_vertex(ss, vertex, nullptr, nullptr, nullptr, nullptr); } +static int sculpt_calc_valence(const struct SculptSession *ss, PBVHVertRef vertex) +{ + + int tot = 0; + + switch (BKE_pbvh_type(ss->pbvh)) { + case PBVH_BMESH: { + BMVert *v = reinterpret_cast(vertex.i); + tot = BM_vert_edge_count(v); + break; + } + case PBVH_FACES: { + Vector edges; + for (int edge : ss->pmap[vertex.i]) { + if (!edges.contains(edge)) { + edges.append(edge); + } + } + + tot = edges.size(); + break; + } + case PBVH_GRIDS: { + const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh); + const int grid_index = vertex.i / key->grid_area; + const int vertex_index = vertex.i - grid_index * key->grid_area; + + SubdivCCGCoord coord{}; + coord.grid_index = grid_index; + coord.x = vertex_index % key->grid_size; + coord.y = vertex_index / key->grid_size; + + SubdivCCGNeighbors neighbors; + BKE_subdiv_ccg_neighbor_coords_get(ss->subdiv_ccg, &coord, true, &neighbors); + + tot = neighbors.size; + break; + } + } + + return tot; +} + int SCULPT_vertex_valence_get(const struct SculptSession *ss, PBVHVertRef vertex) { - SculptVertexNeighborIter ni; uint8_t flag = vertex_attr_get(vertex, ss->attrs.flags); if (flag & SCULPTFLAG_NEED_VALENCE) { - vertex_attr_set(vertex, ss->attrs.flags, flag & ~SCULPTFLAG_NEED_VALENCE); - - int tot = 0; - - SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vertex, ni) { - tot++; - } - SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); + int tot = sculpt_calc_valence(ss, vertex); vertex_attr_set(vertex, ss->attrs.valence, tot); + *vertex_attr_ptr(vertex, ss->attrs.flags) &= ~SCULPTFLAG_NEED_VALENCE; + return tot; } +#if 0 + int tot = vertex_attr_get(vertex, ss->attrs.valence); + int real_tot = sculpt_calc_valence(ss, vertex); + + if (tot != real_tot) { + printf("%s: invalid valence! got %d expected %d\n", tot, real_tot); + } +#endif + return vertex_attr_get(vertex, ss->attrs.valence); } diff --git a/source/blender/editors/sculpt_paint/sculpt_brush_types.cc b/source/blender/editors/sculpt_paint/sculpt_brush_types.cc index 2be68df072b..0c24ef6ade5 100644 --- a/source/blender/editors/sculpt_paint/sculpt_brush_types.cc +++ b/source/blender/editors/sculpt_paint/sculpt_brush_types.cc @@ -1561,6 +1561,13 @@ static void do_layer_brush_task_cb_ex(void *__restrict userdata, } } + PBVH_CHECK_NAN1(*disp_factor); + + if (isnan(*disp_factor)) { + vd.bm_vert->head.hflag |= BM_ELEM_SELECT; + continue; + } + /* When using persistent base, the layer brush (holding Control) invert mode resets the * height of the layer to 0. This makes possible to clean edges of previously added layers * on top of the base. */ @@ -1581,6 +1588,8 @@ static void do_layer_brush_task_cb_ex(void *__restrict userdata, *disp_factor = clamp_f(*disp_factor, -1.0f, 1.0f); } + PBVH_CHECK_NAN1(*disp_factor); + float final_co[3]; float normal[3]; @@ -1601,8 +1610,11 @@ static void do_layer_brush_task_cb_ex(void *__restrict userdata, mul_v3_fl(vdisp, fabsf(fade)); add_v3_v3v3(final_co, vd.co, vdisp); + PBVH_CHECK_NAN(final_co); SCULPT_clip(sd, ss, vd.co, final_co); + PBVH_CHECK_NAN1(*disp_factor); + if (vd.is_mesh) { BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); } diff --git a/source/blender/editors/sculpt_paint/sculpt_paint_color.cc b/source/blender/editors/sculpt_paint/sculpt_paint_color.cc index 1944835e582..bcdccde73a3 100644 --- a/source/blender/editors/sculpt_paint/sculpt_paint_color.cc +++ b/source/blender/editors/sculpt_paint/sculpt_paint_color.cc @@ -146,10 +146,11 @@ static void do_paint_brush_task_cb_ex(void *__restrict userdata, BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { SCULPT_vertex_check_origdata(ss, vd.vertex); - // check if we have a new stroke, in which we need to zero - // our temp layer. do this here before the brush check - // to ensure any geomtry dyntopo might subdivide has - // valid state. + /* Check if we have a new stroke, in which we need to zero + * our temp layer. Do this here before the brush check + * to ensure any geomtry dyntopo might subdivide has + * valid state. + */ float *color_buffer = vertex_attr_ptr(vd.vertex, buffer_scl); // mv->origcolor; if (blender::bke::sculpt::stroke_id_test(ss, vd.vertex, STROKEID_USER_PREV_COLOR)) { diff --git a/source/blender/editors/sculpt_paint/sculpt_smooth.cc b/source/blender/editors/sculpt_paint/sculpt_smooth.cc index f209befde7b..70971dbb27f 100644 --- a/source/blender/editors/sculpt_paint/sculpt_smooth.cc +++ b/source/blender/editors/sculpt_paint/sculpt_smooth.cc @@ -203,7 +203,7 @@ static void SCULPT_neighbor_coords_average_interior_ex(SculptSession *ss, eSculptBoundary smooth_flag2 = is_boundary2 & smooth_types; /* Handle smooth boundaries. */ - if (!(is_boundary & hard_flags) && bool(smooth_flag) != bool(smooth_flag2)) { + if (smooth_flag && !(is_boundary & hard_flags) && bool(smooth_flag) != bool(smooth_flag2)) { /* Project to plane. */ float3 t1 = float3(vertex_co_get(ss, ni.vertex)) - co; float fac = dot_v3v3(t1, no); @@ -216,7 +216,7 @@ static void SCULPT_neighbor_coords_average_interior_ex(SculptSession *ss, avg += tco * w; total += w; } - else if ((is_boundary & hard_flags) & (is_boundary2 & hard_flags)) { + else if ((is_boundary & hard_flags) == (is_boundary2 & hard_flags)) { avg += float3(vertex_co_get(ss, ni.vertex)) * w; total += w; project_ok = true; -- 2.30.2 From 734223b5f2151de2b5ec9a821635971c10494ee3 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Sat, 10 Jun 2023 02:54:57 -0700 Subject: [PATCH 178/279] temp-sculpt-dyntopo: Fix a large number of small bugs --- .../startup/bl_ui/properties_paint_common.py | 30 +-- source/blender/blenkernel/BKE_paint.h | 19 +- source/blender/blenkernel/BKE_sculpt.hh | 20 ++ source/blender/blenkernel/intern/dyntopo.cc | 98 +++++++-- .../blenkernel/intern/dyntopo_intern.hh | 6 +- source/blender/blenkernel/intern/paint.cc | 106 ++++++++- .../blender/blenkernel/intern/pbvh_bmesh.cc | 4 +- .../blenloader/intern/versioning_300.cc | 7 +- .../editors/sculpt_paint/sculpt_api.cc | 2 +- .../sculpt_paint/sculpt_brush_types.cc | 26 ++- .../editors/sculpt_paint/sculpt_intern.hh | 4 +- .../editors/sculpt_paint/sculpt_smooth.cc | 204 ++++++++++++------ source/blender/makesdna/DNA_scene_defaults.h | 2 +- source/blender/makesrna/intern/rna_brush.c | 2 +- 14 files changed, 417 insertions(+), 113 deletions(-) diff --git a/scripts/startup/bl_ui/properties_paint_common.py b/scripts/startup/bl_ui/properties_paint_common.py index d11f2dbb730..0810587cf15 100644 --- a/scripts/startup/bl_ui/properties_paint_common.py +++ b/scripts/startup/bl_ui/properties_paint_common.py @@ -652,6 +652,19 @@ def brush_settings(layout, context, brush, popover=False): layout.prop(brush.dyntopo, "disabled") + layout.separator() + layout.label(text="Smooth Settings") + + if capabilities.has_auto_smooth: + UnifiedPaintPanel.prop_unified( + layout, + context, + brush, + "auto_smooth_factor", + pressure_name="use_inverse_smooth_pressure", + slider=True, + ) + if capabilities.has_auto_smooth or brush.sculpt_tool == "SMOOTH": UnifiedPaintPanel.prop_unified( layout, @@ -659,6 +672,7 @@ def brush_settings(layout, context, brush, popover=False): brush, "auto_smooth_projection", slider=True, + text="Preserve Volume", ) is_smooth = capabilities.has_auto_smooth @@ -672,18 +686,8 @@ def brush_settings(layout, context, brush, popover=False): brush, "hard_corner_pin", slider=True, - unified_name = "use_unified_hard_corner_pin", - text="Corner Pin" - ) - - if capabilities.has_auto_smooth: - UnifiedPaintPanel.prop_unified( - layout, - context, - brush, - "auto_smooth_factor", - pressure_name="use_inverse_smooth_pressure", - slider=True, + unified_name="use_unified_hard_corner_pin", + text="Corner Pin", ) if capabilities.has_auto_smooth or brush.sculpt_tool == "SMOOTH": @@ -1110,7 +1114,7 @@ def brush_settings_advanced(layout, context, brush, popover=False): if is_cavity_active: props = row.operator("sculpt.mask_from_cavity", text="Create Mask") - props.settings_source = 'BRUSH' + props.settings_source = "BRUSH" col.prop(brush, "use_automasking_cavity_inverted", text="Cavity (inverted)") diff --git a/source/blender/blenkernel/BKE_paint.h b/source/blender/blenkernel/BKE_paint.h index b97d24ac7ff..d038d394202 100644 --- a/source/blender/blenkernel/BKE_paint.h +++ b/source/blender/blenkernel/BKE_paint.h @@ -564,6 +564,13 @@ typedef struct SculptAttribute { * inside of SculptSession.temp_attribute are used. */ bool used; + +#ifdef __cplusplus + bool is_empty() const + { + return !used; + } +#endif } SculptAttribute; #define SCULPT_MAX_ATTRIBUTES 64 @@ -948,6 +955,11 @@ void BKE_sculpt_attribute_destroy_temporary_all(struct Object *ob); /* Destroy attributes that were marked as stroke only in SculptAttributeParams. */ void BKE_sculpt_attributes_destroy_temporary_stroke(struct Object *ob); +/* Release a SculptAttribute ref without destroying the underlying attribute. */ +void BKE_sculpt_attribute_release_ref(struct Object *ob, SculptAttribute *attr); + +SculptAttribute BKE_sculpt_find_attribute(struct Object *ob, const char *name); + bool BKE_sculpt_init_flags_valence(struct Object *ob, struct PBVH *pbvh, int totvert, @@ -962,13 +974,6 @@ void BKE_sculptsession_bmesh_attr_update_internal(struct Object *ob); void BKE_sculptsession_sync_attributes(struct Object *ob, struct Mesh *me, bool load_to_mesh); void BKE_sculptsession_bmesh_add_layers(struct Object *ob); -SculptAttribute *BKE_sculptsession_attr_layer_get(struct Object *ob, - eAttrDomain domain, - int proptype, - const char *name, - SculptAttributeParams *params, - bool *r_is_new); -bool BKE_sculptsession_attr_release_layer(struct Object *ob, SculptAttribute *scl); void BKE_sculptsession_update_attr_refs(struct Object *ob); int BKE_sculptsession_get_totvert(const struct SculptSession *ss); diff --git a/source/blender/blenkernel/BKE_sculpt.hh b/source/blender/blenkernel/BKE_sculpt.hh index d8f0fa20c16..36a1a9efb52 100644 --- a/source/blender/blenkernel/BKE_sculpt.hh +++ b/source/blender/blenkernel/BKE_sculpt.hh @@ -343,9 +343,29 @@ static ATTR_NO_OPT bool _pbvh_nan_check3(const float co[3], return ret; } +static ATTR_NO_OPT bool _pbvh_nan_check4(const float co[4], + const char *func, + const char *file, + int line) +{ + if (!co) { + return false; + } + + bool ret = false; + + for (int i = 0; i < 4; i++) { + ret |= _pbvh_nan_check1(co[i], func, file, line); + } + + return ret; +} + # define PBVH_CHECK_NAN(co) _pbvh_nan_check3(co, __func__, __FILE__, __LINE__) # define PBVH_CHECK_NAN1(f) _pbvh_nan_check1(f, __func__, __FILE__, __LINE__) +# define PBVH_CHECK_NAN4(co) _pbvh_nan_check4(co, __func__, __FILE__, __LINE__) #else # define PBVH_CHECK_NAN(co) # define PBVH_CHECK_NAN1(f) +# define PBVH_CHECK_NAN4(co) #endif diff --git a/source/blender/blenkernel/intern/dyntopo.cc b/source/blender/blenkernel/intern/dyntopo.cc index 6249aacb771..7638a01a8a7 100644 --- a/source/blender/blenkernel/intern/dyntopo.cc +++ b/source/blender/blenkernel/intern/dyntopo.cc @@ -931,6 +931,9 @@ bool check_face_is_tri(PBVH *pbvh, BMFace *f) pbvh_boundary_update_bmesh(pbvh, l->e); if (l->e->head.index == -1) { + *BM_ELEM_CD_PTR(l->e, + pbvh->cd_edge_boundary) &= ~(SCULPT_BOUNDARY_UV | SCULPT_CORNER_UV); + BM_log_edge_added(pbvh->header.bm, pbvh->bm_log, l->e); dyntopo_add_flag(pbvh, l->v, SCULPTFLAG_NEED_VALENCE); dyntopo_add_flag(pbvh, l->next->v, SCULPTFLAG_NEED_VALENCE); @@ -2221,9 +2224,13 @@ void EdgeQueueContext::step() return; } - float prob = ops[curop] == PBVH_Subdivide ? 0.25 : 0.75; - if (srand.get_float() > prob) { - surface_smooth_v_safe(ss, pbvh, v, surface_smooth_fac, reproject_cdata); + if (srand.get_float() > 0.7) { + surface_smooth_v_safe(ss, + pbvh, + v, + surface_smooth_fac * + mask_cb({reinterpret_cast(v)}, mask_cb_data), + reproject_cdata); } }; @@ -2526,6 +2533,8 @@ void EdgeQueueContext::split_edge(BMEdge *e) int bf1 = BM_ELEM_CD_GET_INT(e->v1, pbvh->cd_boundary_flag); int bf2 = BM_ELEM_CD_GET_INT(e->v2, pbvh->cd_boundary_flag); + bool uv_boundary = BM_ELEM_CD_GET_INT(e, pbvh->cd_edge_boundary) & SCULPT_BOUNDARY_UV; + BMVert *newv = BM_edge_split(bm, e, e->v1, &newe, 0.5f); PBVH_CHECK_NAN(newv->co); @@ -2534,13 +2543,15 @@ void EdgeQueueContext::split_edge(BMEdge *e) e->head.hflag &= ~EDGE_QUEUE_FLAG; newe->head.hflag &= ~EDGE_QUEUE_FLAG; - /* Deal with UV boundary flags. */ BM_ELEM_CD_SET_INT(newe, pbvh->cd_edge_boundary, BM_ELEM_CD_GET_INT(e, pbvh->cd_edge_boundary)); - if ((bf1 & SCULPT_BOUNDARY_UV) && (bf2 & SCULPT_BOUNDARY_UV)) { - *BM_ELEM_CD_PTR(newv, pbvh->cd_boundary_flag) |= SCULPT_BOUNDARY_UV; + + /* Do not allow vertex uv boundary flags to propagate across non-boundary edges. */ + if (!uv_boundary) { + *BM_ELEM_CD_PTR(newv, + pbvh->cd_boundary_flag) &= ~(SCULPT_BOUNDARY_UV | SCULPT_CORNER_UV); } else { - *BM_ELEM_CD_PTR(newv, pbvh->cd_boundary_flag) &= ~SCULPT_BOUNDARY_UV; + *BM_ELEM_CD_PTR(newv, pbvh->cd_boundary_flag) |= SCULPT_BOUNDARY_UV; } /* Propagate current stroke id. */ @@ -3368,8 +3379,72 @@ template void tri_weights_v3(T *p, const T *a, const T *b, c } } // namespace myinterp +template +static void interp_prop_data( + const void **src_blocks, const float *weights, int count, void *dst_block, int cd_offset) +{ + SumT sum; + + if constexpr (std::is_same_v) { + sum = 0.0f; + } + else { + sum = {}; + } + + for (int i = 0; i < count; i++) { + const T *value = static_cast(POINTER_OFFSET(src_blocks[i], cd_offset)); + sum += (*value) * weights[i]; + } + + if (count > 0) { + T *dest = static_cast(POINTER_OFFSET(dst_block, cd_offset)); + *dest = sum; + } +} + +void reproject_interp_data(CustomData *data, + const void **src_blocks, + const float *weights, + const float *sub_weights, + int count, + void *dst_block, + eCustomDataMask typemask) +{ + using namespace blender; + + for (int i = 0; i < data->totlayer; i++) { + CustomDataLayer *layer = data->layers + i; + + if (!(CD_TYPE_AS_MASK(layer->type) & typemask)) { + continue; + } + if (layer->flag & (CD_FLAG_TEMPORARY | CD_FLAG_ELEM_NOINTERP)) { + continue; + } + + switch (layer->type) { + case CD_PROP_FLOAT: + interp_prop_data(src_blocks, weights, count, dst_block, layer->offset); + break; + case CD_PROP_FLOAT2: + interp_prop_data(src_blocks, weights, count, dst_block, layer->offset); + break; + case CD_PROP_FLOAT3: + interp_prop_data(src_blocks, weights, count, dst_block, layer->offset); + break; + case CD_PROP_COLOR: + interp_prop_data(src_blocks, weights, count, dst_block, layer->offset); + break; + // case CD_PROP_BYTE_COLOR: + // interp_prop_data(src_blocks, weights, count, dst_block, layer->offset); + // break; + } + } +} + template // myinterp::TestFloat> -inline void reproject_bm_data( +static void reproject_bm_data( BMesh *bm, BMLoop *l_dst, const BMFace *f_src, const bool do_vertex, eCustomDataMask typemask) { using namespace myinterp; @@ -3472,12 +3547,11 @@ inline void reproject_bm_data( fw = w; } - CustomData_bmesh_interp_ex( - &bm->ldata, blocks, fw, nullptr, f_src->len, l_dst->head.data, typemask); + reproject_interp_data(&bm->ldata, blocks, fw, nullptr, f_src->len, l_dst->head.data, typemask); if (do_vertex) { // bool inside = isect_point_poly_v2(co, cos_2d, l_dst->f->len, false); - CustomData_bmesh_interp_ex( + reproject_interp_data( &bm->vdata, vblocks, fw, nullptr, f_src->len, l_dst->v->head.data, typemask); } } @@ -3834,7 +3908,7 @@ void BKE_sculpt_reproject_cdata( float3 origco_saved = *origco; float3 origno_saved = *origno; - CustomData_bmesh_interp_ex( + reproject_interp_data( &ss->bm->vdata, (const void **)vblocks, ws, nullptr, cur_vblock, v->head.data, typemask); *origco = origco_saved; diff --git a/source/blender/blenkernel/intern/dyntopo_intern.hh b/source/blender/blenkernel/intern/dyntopo_intern.hh index b41ccf10653..967aa86a639 100644 --- a/source/blender/blenkernel/intern/dyntopo_intern.hh +++ b/source/blender/blenkernel/intern/dyntopo_intern.hh @@ -132,9 +132,11 @@ inline bool bm_elem_is_free(BMElem *elem, int htype) //#define SKINNY_EDGE_FIX /* Slightly relax geometry by this factor along surface tangents - * to improve convergence of dyntopo remesher. + * to improve convergence of dyntopo remesher. This relaxation is + * applied stochastically (by skipping verts randomly) to improve + * performance. */ -#define DYNTOPO_SAFE_SMOOTH_FAC 0.01f +#define DYNTOPO_SAFE_SMOOTH_FAC 0.025f #ifdef USE_EDGEQUEUE_EVEN_SUBDIV # include "BKE_global.h" diff --git a/source/blender/blenkernel/intern/paint.cc b/source/blender/blenkernel/intern/paint.cc index bb10940c548..9edc68d8d69 100644 --- a/source/blender/blenkernel/intern/paint.cc +++ b/source/blender/blenkernel/intern/paint.cc @@ -2392,7 +2392,10 @@ void BKE_sculpt_color_layer_create_if_needed(Object *object) using namespace blender::bke; Mesh *orig_me = BKE_object_get_original_mesh(object); - if (orig_me->attributes().contains(orig_me->active_color_attribute)) { + SculptAttribute attr = BKE_sculpt_find_attribute(object, orig_me->active_color_attribute); + if (!attr.is_empty() && (CD_TYPE_AS_MASK(attr.proptype) & CD_MASK_COLOR_ALL) && + ELEM(attr.domain, ATTR_DOMAIN_POINT, ATTR_DOMAIN_CORNER)) + { return; } @@ -2413,7 +2416,8 @@ void BKE_sculpt_color_layer_create_if_needed(Object *object) BKE_pbvh_update_active_vcol(object->sculpt->pbvh, orig_me); } - BKE_sculptsession_update_attr_refs(object); + /* Flush attribute into sculpt mesh. */ + BKE_sculptsession_sync_attributes(object, orig_me, false); } void BKE_sculpt_update_object_for_edit( @@ -3989,6 +3993,104 @@ void BKE_sculpt_attribute_destroy_temporary_all(Object *ob) } } +SculptAttribute BKE_sculpt_find_attribute(Object *ob, const char *name) +{ + SculptSession *ss = ob->sculpt; + + CustomData *cdatas[4]; + eAttrDomain domains[4] = { + ATTR_DOMAIN_POINT, ATTR_DOMAIN_EDGE, ATTR_DOMAIN_CORNER, ATTR_DOMAIN_FACE}; + + if (ss->bm) { + cdatas[0] = &ss->bm->vdata; + cdatas[1] = &ss->bm->edata; + cdatas[2] = &ss->bm->ldata; + cdatas[3] = &ss->bm->pdata; + } + else { + Mesh *me = static_cast(ob->data); + + cdatas[0] = &me->vdata; + cdatas[1] = &me->edata; + cdatas[2] = &me->ldata; + cdatas[3] = &me->pdata; + } + + bool is_grids = ss->pbvh && BKE_pbvh_type(ss->pbvh) == PBVH_GRIDS; + + for (int i = 0; i < 4; i++) { + eAttrDomain domain = domains[i]; + if (domain == ATTR_DOMAIN_POINT && is_grids) { + for (int i = 0; i < ARRAY_SIZE(ss->temp_attributes); i++) { + SculptAttribute *attr = &ss->temp_attributes[i]; + + if (attr->used && attr->domain == ATTR_DOMAIN_POINT && STREQ(attr->name, name)) { + return *attr; + } + } + + continue; + } + + CustomData *data = cdatas[i]; + for (int j = 0; j < data->totlayer; j++) { + CustomDataLayer &layer = data->layers[j]; + + if (STREQ(layer.name, name) && (CD_TYPE_AS_MASK(layer.type) & CD_MASK_PROP_ALL)) { + SculptAttribute *attr = BKE_sculpt_attribute_get( + ob, domain, eCustomDataType(layer.type), name); + SculptAttribute ret; + + ret = *attr; + BKE_sculpt_attribute_release_ref(ob, attr); + + return ret; + } + } + } + + SculptAttribute unused = {}; + return unused; +} + +void BKE_sculpt_attribute_release_ref(Object *ob, SculptAttribute *attr) +{ + SculptSession *ss = ob->sculpt; + eAttrDomain domain = attr->domain; + + BLI_assert(attr->used); + + /* Remove from convenience pointer struct. */ + SculptAttribute **ptrs = (SculptAttribute **)&ss->attrs; + int ptrs_num = sizeof(ss->attrs) / sizeof(void *); + + for (int i = 0; i < ptrs_num; i++) { + if (ptrs[i] == attr) { + ptrs[i] = nullptr; + } + } + + /* Remove from internal temp_attributes array. */ + for (int i = 0; i < SCULPT_MAX_ATTRIBUTES; i++) { + SculptAttribute *attr2 = ss->temp_attributes + i; + + if (STREQ(attr2->name, attr->name) && attr2->domain == attr->domain && + attr2->proptype == attr->proptype) + { + + attr2->used = false; + } + } + + /* Simple_array mode attributes are owned by SculptAttribute. */ + if (attr->simple_array) { + MEM_SAFE_FREE(attr->data); + } + + attr->data = nullptr; + attr->used = false; +} + bool BKE_sculpt_attribute_destroy(Object *ob, SculptAttribute *attr) { if (!attr || !attr->used) { diff --git a/source/blender/blenkernel/intern/pbvh_bmesh.cc b/source/blender/blenkernel/intern/pbvh_bmesh.cc index 35d15cca151..1bcf7ad97c2 100644 --- a/source/blender/blenkernel/intern/pbvh_bmesh.cc +++ b/source/blender/blenkernel/intern/pbvh_bmesh.cc @@ -2201,8 +2201,8 @@ void update_vert_boundary_bmesh(int cd_faceset_offset, } if (e->l) { - if ((e->l->f->head.hflag & BM_ELEM_SMOOTH) || - (e->l->radial_next->f->head.hflag & BM_ELEM_SMOOTH)) { + if ((e->l->f->head.hflag & BM_ELEM_HIDDEN) || + (e->l->radial_next->f->head.hflag & BM_ELEM_HIDDEN)) { newflag |= SCULPTFLAG_VERT_FSET_HIDDEN; } diff --git a/source/blender/blenloader/intern/versioning_300.cc b/source/blender/blenloader/intern/versioning_300.cc index 29dd4f89ba8..ac4e63a798b 100644 --- a/source/blender/blenloader/intern/versioning_300.cc +++ b/source/blender/blenloader/intern/versioning_300.cc @@ -4418,7 +4418,12 @@ void blo_do_versions_300(FileData *fd, Library * /*lib*/, Main *bmain) if (!DNA_struct_elem_find(fd->filesdna, "UnifiedPaintSettings", "int", "smooth_boundary_flag")) { LISTBASE_FOREACH (Brush *, brush, &bmain->brushes) { - if (ELEM(brush->sculpt_tool, SCULPT_TOOL_MASK, SCULPT_TOOL_DRAW_FACE_SETS)) { + if (ELEM(brush->sculpt_tool, + SCULPT_TOOL_MASK, + SCULPT_TOOL_DRAW_FACE_SETS, + SCULPT_TOOL_PAINT, + SCULPT_TOOL_SMEAR)) + { brush->dyntopo.flag |= DYNTOPO_DISABLED; } } diff --git a/source/blender/editors/sculpt_paint/sculpt_api.cc b/source/blender/editors/sculpt_paint/sculpt_api.cc index b56ec6f0989..0be059edc6f 100644 --- a/source/blender/editors/sculpt_paint/sculpt_api.cc +++ b/source/blender/editors/sculpt_paint/sculpt_api.cc @@ -457,7 +457,7 @@ int SCULPT_vertex_valence_get(const struct SculptSession *ss, PBVHVertRef vertex return tot; } -#if 0 +#if 1 int tot = vertex_attr_get(vertex, ss->attrs.valence); int real_tot = sculpt_calc_valence(ss, vertex); diff --git a/source/blender/editors/sculpt_paint/sculpt_brush_types.cc b/source/blender/editors/sculpt_paint/sculpt_brush_types.cc index 0c24ef6ade5..9465076e5cc 100644 --- a/source/blender/editors/sculpt_paint/sculpt_brush_types.cc +++ b/source/blender/editors/sculpt_paint/sculpt_brush_types.cc @@ -2920,7 +2920,7 @@ static void do_topology_rake_bmesh_task_cb_ex(void *__restrict userdata, sqrtf(test.dist), vd.no, vd.fno, - *vd.mask, + vd.mask ? *vd.mask : 0.0f, vd.vertex, thread_id, &automask_data); @@ -2938,8 +2938,17 @@ static void do_topology_rake_bmesh_task_cb_ex(void *__restrict userdata, int cd_temp = data->scl->bmesh_cd_offset; - SCULPT_bmesh_four_neighbor_average( - ss, avg, direction2, vd.bm_vert, projection, hard_corner_pin, cd_temp, true, false); + SCULPT_bmesh_four_neighbor_average(ss, + avg, + direction2, + vd.bm_vert, + projection, + hard_corner_pin, + cd_temp, + true, + false, + fade, + do_reproject); sub_v3_v3v3(val, avg, vd.co); madd_v3_v3v3fl(val, vd.co, val, fade); @@ -2957,13 +2966,16 @@ static void do_topology_rake_bmesh_task_cb_ex(void *__restrict userdata, hard_corner_pin, cd_temp, true, - true); + true, + fade, + false); float *origco = blender::bke::paint::vertex_attr_ptr(vd.vertex, ss->attrs.orig_co); interp_v3_v3v3(origco, origco, origco_avg, fade); } if (do_reproject) { - BKE_sculpt_reproject_cdata(ss, vd.vertex, oldco, oldno); + /* Use interp_face_corners instead. */ + //BKE_sculpt_reproject_cdata(ss, vd.vertex, oldco, oldno); } if (vd.is_mesh) { @@ -2986,11 +2998,11 @@ void SCULPT_bmesh_topology_rake(Sculpt *sd, Object *ob, Span nodes, SCULPT_smooth_undo_push(ob, nodes); /* Interactions increase both strength and quality. */ - const int iterations = 3; + const int iterations = 1; int iteration; const int count = iterations * strength + 1; - const float factor = iterations * strength / count; + const float factor = (iterations * strength / count) * 0.25f; if (!ss->attrs.rake_temp) { SculptAttributeParams params = {}; diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.hh b/source/blender/editors/sculpt_paint/sculpt_intern.hh index 158f9184368..5d4464bbd69 100644 --- a/source/blender/editors/sculpt_paint/sculpt_intern.hh +++ b/source/blender/editors/sculpt_paint/sculpt_intern.hh @@ -1896,7 +1896,9 @@ void SCULPT_bmesh_four_neighbor_average(SculptSession *ss, float hard_corner_pin, int cd_temp, bool weighted, - bool do_origco); + bool do_origco, + float factor, + bool reproject_uvs); void SCULPT_neighbor_coords_average(SculptSession *ss, float result[3], diff --git a/source/blender/editors/sculpt_paint/sculpt_smooth.cc b/source/blender/editors/sculpt_paint/sculpt_smooth.cc index 70971dbb27f..d548fb71121 100644 --- a/source/blender/editors/sculpt_paint/sculpt_smooth.cc +++ b/source/blender/editors/sculpt_paint/sculpt_smooth.cc @@ -43,7 +43,7 @@ using blender::Set; using blender::Span; using blender::Vector; -//#define SMOOTH_FACE_CORNERS +#define SMOOTH_FACE_CORNERS float BKE_pbvh_bmesh_detail_size_avg_get(PBVH *pbvh); @@ -92,6 +92,7 @@ static void SCULPT_neighbor_coors_average_for_detail(SculptSession *ss, } #endif +template static void SCULPT_neighbor_coords_average_interior_ex(SculptSession *ss, float result[3], PBVHVertRef vertex, @@ -145,9 +146,8 @@ static void SCULPT_neighbor_coords_average_interior_ex(SculptSession *ss, } float total = 0.0f; - int totboundary = 0; + int totboundary = 0, totsharp = 0; -#ifdef SMOOTH_FACE_CORNERS Vector ws; Vector loops; @@ -171,7 +171,6 @@ static void SCULPT_neighbor_coords_average_interior_ex(SculptSession *ss, ws.append(w); } while ((l = l->radial_next) != e->l); }; -#endif SculptVertexNeighborIter ni; SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vertex, ni) { @@ -195,6 +194,10 @@ static void SCULPT_neighbor_coords_average_interior_ex(SculptSession *ss, totboundary++; } + if (is_boundary2 & hard_flags) { + totsharp++; + } + /* Boundary vertices use only other boundary vertices. */ if (is_boundary) { project_ok = false; @@ -210,9 +213,10 @@ static void SCULPT_neighbor_coords_average_interior_ex(SculptSession *ss, float3 tco = (co + no * fac); -#ifdef SMOOTH_FACE_CORNERS - addblock(vertex, ni.edge, w); -#endif + if constexpr (smooth_face_corners) { + addblock(vertex, ni.edge, w); + } + avg += tco * w; total += w; } @@ -229,11 +233,11 @@ static void SCULPT_neighbor_coords_average_interior_ex(SculptSession *ss, project_ok = true; } -#ifdef SMOOTH_FACE_CORNERS - if (project_ok) { - addblock(ni.vertex, ni.edge, w); + if constexpr (smooth_face_corners) { + if (project_ok) { + addblock(ni.vertex, ni.edge, w); + } } -#endif } SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); @@ -242,19 +246,19 @@ static void SCULPT_neighbor_coords_average_interior_ex(SculptSession *ss, total = 0.0; zero_v3(avg); -#ifdef SMOOTH_FACE_CORNERS - loops.clear(); - ws.clear(); -#endif + if constexpr (smooth_face_corners) { + loops.clear(); + ws.clear(); + } SculptVertexNeighborIter ni; SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vertex, ni) { float w = weighted ? areas[ni.i] : 1.0f; avg += float3(vertex_co_get(ss, ni.vertex)) * w; total += w; -#ifdef SMOOTH_FACE_CORNERS - addblock(ni.vertex, ni.edge, w); -#endif + if constexpr (smooth_face_corners) { + addblock(ni.vertex, ni.edge, w); + } } SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); } @@ -265,11 +269,11 @@ static void SCULPT_neighbor_coords_average_interior_ex(SculptSession *ss, return; } -#ifdef SMOOTH_FACE_CORNERS - if (is_bmesh && !smooth_origco) { - blender::bke::sculpt::interp_face_corners(ss->pbvh, vertex, loops, ws, factor); + if constexpr (smooth_face_corners) { + if (is_bmesh && !smooth_origco) { + blender::bke::sculpt::interp_face_corners(ss->pbvh, vertex, loops, ws, factor); + } } -#endif /* Project to plane if desired. */ avg = avg / (float)total - co; @@ -305,16 +309,30 @@ void SCULPT_neighbor_coords_average_interior(SculptSession *ss, corner_type |= SCULPT_CORNER_FACE_SET; } - SCULPT_neighbor_coords_average_interior_ex(ss, - result, - vertex, - projection, - hard_corner_pin, - use_area_weights, - bound_type, - corner_type, - smooth_origco, - factor); + if (ss->reproject_smooth) { + SCULPT_neighbor_coords_average_interior_ex(ss, + result, + vertex, + projection, + hard_corner_pin, + use_area_weights, + bound_type, + corner_type, + smooth_origco, + factor); + } + else { + SCULPT_neighbor_coords_average_interior_ex(ss, + result, + vertex, + projection, + hard_corner_pin, + use_area_weights, + bound_type, + corner_type, + smooth_origco, + factor); + } } /* Compares four vectors seperated by 90 degrees around normal and picks the one closest @@ -426,7 +444,9 @@ void SCULPT_bmesh_four_neighbor_average(SculptSession *ss, float hard_corner_pin, int cd_temp, bool weighted, - bool do_origco) + bool do_origco, + float factor, + bool reproject_uvs) { float avg_co[3] = {0.0f, 0.0f, 0.0f}; float tot_co = 0.0f; @@ -438,6 +458,8 @@ void SCULPT_bmesh_four_neighbor_average(SculptSession *ss, float dir[3]; float dir3[3] = {0.0f, 0.0f, 0.0f}; + PBVH_CHECK_NAN4(field); + float *areas = nullptr; SCULPT_vertex_check_origdata(ss, BKE_pbvh_make_vref(intptr_t(v))); @@ -470,22 +492,6 @@ void SCULPT_bmesh_four_neighbor_average(SculptSession *ss, float *co1 = do_origco ? origco : v->co; float *no1 = do_origco ? origno : v->no; -#if 0 - float no1[3]; - - float avg_no1[3]; - copy_v3_v3(no1, do_origco ? origno : v->no); - - if (G.debug_value != 893) { - SCULPT_get_normal_average(ss, avg_no1, vertex, weighted, do_origco); - interp_v3_v3v3(no1, no1, avg_no1, 1.0f); - } - - /* Project direction into normal's plane. */ - madd_v3_v3fl(direction, no1, -dot_v3v3(no1, direction)); - normalize_v3(direction); -#endif - int valence = SCULPT_vertex_valence_get(ss, vertex); if (weighted) { @@ -526,6 +532,44 @@ void SCULPT_bmesh_four_neighbor_average(SculptSession *ss, BMEdge *e; bool had_bound = false; int area_i = 0; + int totboundary = 0; + + Vector loops; + Vector ws; + + auto addloop = [&](BMEdge *e, float w) { + if (!e->l) { + return; + } + +#if 0 + if (e->l->v == v) { + loops.append(e->l); + ws.append(w); + } + else { + loops.append(e->l->radial_next); + ws.append(w); + } + + return; +#endif + BMLoop *l = e->l; + l = l->v == v ? l->next : l; + + if (e->l->radial_next != e->l) { + w *= 0.5f; + + BMLoop *l2 = e->l->radial_next; + l2 = l2->v == v ? l2->next : l2; + + loops.append(l2); + ws.append(w); + } + + loops.append(l); + ws.append(w); + }; BM_ITER_ELEM_INDEX (e, &eiter, v, BM_EDGES_OF_VERT, area_i) { BMVert *v_other = (e->v1 == v) ? e->v2 : e->v1; @@ -551,10 +595,15 @@ void SCULPT_bmesh_four_neighbor_average(SculptSession *ss, ss, BKE_pbvh_make_eref(intptr_t(e)), boundary_mask); float dirw = 1.0f; + PBVH_CHECK_NAN(no1); + PBVH_CHECK_NAN(dir2); + /* Add to cross field. */ if (boundary2 != SCULPT_BOUNDARY_NONE) { had_bound = true; + totboundary++; + sub_v3_v3v3(dir2, co2, co1); madd_v3_v3fl(dir2, no1, -dot_v3v3(no1, dir2)); normalize_v3(dir2); @@ -579,6 +628,8 @@ void SCULPT_bmesh_four_neighbor_average(SculptSession *ss, madd_v3_v3fl(avg_co, co2, fac); tot_co += fac; + + addloop(e, fac); continue; } else if (boundary != SCULPT_BOUNDARY_NONE) { @@ -592,6 +643,8 @@ void SCULPT_bmesh_four_neighbor_average(SculptSession *ss, madd_v3_v3fl(avg_co, co3, fac); tot_co += fac; + + addloop(e, fac); } continue; } @@ -610,12 +663,28 @@ void SCULPT_bmesh_four_neighbor_average(SculptSession *ss, fac = fac * fac - 0.5f; fac *= fac; + PBVH_CHECK_NAN1(fac); + PBVH_CHECK_NAN(dir); + PBVH_CHECK_NAN(vec); + if (weighted) { fac *= areas[area_i]; } madd_v3_v3fl(avg_co, co2, fac); tot_co += fac; + addloop(e, fac); + } + + if (totboundary == 1) { + BM_ITER_ELEM_INDEX (e, &eiter, v, BM_EDGES_OF_VERT, area_i) { + BMVert *v_other = (e->v1 == v) ? e->v2 : e->v1; + float fac = weighted ? areas[area_i] : 1.0f; + + madd_v3_v3fl(avg_co, v_other->co, fac); + tot_co += fac; + addloop(e, fac); + } } if (tot_co > 0.0f) { @@ -632,6 +701,18 @@ void SCULPT_bmesh_four_neighbor_average(SculptSession *ss, copy_v3_v3(avg, co1); } + if (reproject_uvs && tot_co > 0.0f && !(boundary & SCULPT_BOUNDARY_UV)) { + float totw = 0.0f; + for (float w : ws) { + totw += w; + } + for (int i = 0; i < ws.size(); i++) { + ws[i] /= totw; + } + + blender::bke::sculpt::interp_face_corners(ss->pbvh, vertex, loops, ws, factor); + } + eSculptCorner corner_type = SCULPT_CORNER_MESH | SCULPT_CORNER_SHARP_MARK; if (ss->hard_edge_mode) { corner_type |= SCULPT_CORNER_FACE_SET; @@ -676,6 +757,7 @@ void SCULPT_bmesh_four_neighbor_average(SculptSession *ss, } vec_transform(field, no1, bi); + PBVH_CHECK_NAN4(field); } } @@ -694,16 +776,16 @@ void SCULPT_neighbor_coords_average(SculptSession *ss, eSculptBoundary bound_type = SCULPT_BOUNDARY_SHARP_MARK | SCULPT_BOUNDARY_SEAM | SCULPT_BOUNDARY_UV | SCULPT_BOUNDARY_FACE_SET; - SCULPT_neighbor_coords_average_interior_ex(ss, - result, - vertex, - projection, - hard_corner_pin, - weighted, - bound_type, - corner_type, - false, - factor); + SCULPT_neighbor_coords_average_interior_ex(ss, + result, + vertex, + projection, + hard_corner_pin, + weighted, + bound_type, + corner_type, + false, + factor); } float SCULPT_neighbor_mask_average(SculptSession *ss, PBVHVertRef vertex) @@ -864,11 +946,7 @@ static void do_smooth_brush_task_cb_ex(void *__restrict userdata, float bstrength = data->strength; PBVHVertexIter vd; -#ifdef SMOOTH_FACE_CORNERS - const bool do_reproject = false; -#else const bool do_reproject = SCULPT_need_reproject(ss); -#endif CLAMP(bstrength, 0.0f, 1.0f); diff --git a/source/blender/makesdna/DNA_scene_defaults.h b/source/blender/makesdna/DNA_scene_defaults.h index 63cde84ef2e..1efb2ecef88 100644 --- a/source/blender/makesdna/DNA_scene_defaults.h +++ b/source/blender/makesdna/DNA_scene_defaults.h @@ -282,7 +282,7 @@ .flag = UNIFIED_PAINT_SIZE | UNIFIED_PAINT_ALPHA | UNIFIED_PAINT_FLAG_HARD_EDGE_MODE | UNIFIED_PAINT_HARD_CORNER_PIN | UNIFIED_PAINT_FLAG_SHARP_ANGLE_LIMIT, \ .hard_corner_pin = 1.0f,\ .sharp_angle_limit = 0.38f,\ - .smooth_boundary_flag = SCULPT_BOUNDARY_MESH|SCULPT_BOUNDARY_FACE_SET|SCULPT_BOUNDARY_SEAM|SCULPT_BOUNDARY_SHARP_MARK,\ + .smooth_boundary_flag = SCULPT_BOUNDARY_MESH|SCULPT_BOUNDARY_FACE_SET|SCULPT_BOUNDARY_SEAM|SCULPT_BOUNDARY_SHARP_MARK|SCULPT_BOUNDARY_UV,\ } #define _DNA_DEFAULTS_ParticleEditSettings \ diff --git a/source/blender/makesrna/intern/rna_brush.c b/source/blender/makesrna/intern/rna_brush.c index ef578ddb52a..6828d30f4d7 100644 --- a/source/blender/makesrna/intern/rna_brush.c +++ b/source/blender/makesrna/intern/rna_brush.c @@ -3393,7 +3393,7 @@ static void rna_def_brush(BlenderRNA *brna) RNA_def_property_range(prop, 0.0f, 1.0f); RNA_def_property_ui_range(prop, 0.0f, 1.0f, 0.001, 3); RNA_def_property_ui_text( - prop, "Projection", "How much autosmooth should stick to surface\n(prevents shrinking)."); + prop, "Preserve Volume", "How much autosmooth should preserve volume."); RNA_def_property_update(prop, 0, "rna_Brush_update"); prop = RNA_def_property(srna, "sharp_angle_limit", PROP_FLOAT, PROP_ANGLE); -- 2.30.2 From aaa30bce5347192167a20c30adc80b0e467a3b0c Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Sat, 10 Jun 2023 02:59:35 -0700 Subject: [PATCH 179/279] temp-sculpt-dyntopo: Fix bug in last commit --- source/blender/editors/sculpt_paint/sculpt_smooth.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/editors/sculpt_paint/sculpt_smooth.cc b/source/blender/editors/sculpt_paint/sculpt_smooth.cc index d548fb71121..9dc57f9ee84 100644 --- a/source/blender/editors/sculpt_paint/sculpt_smooth.cc +++ b/source/blender/editors/sculpt_paint/sculpt_smooth.cc @@ -1020,7 +1020,7 @@ static void do_smooth_brush_task_cb_ex(void *__restrict userdata, SCULPT_clip(sd, ss, vd.co, val); if (do_reproject) { - BKE_sculpt_reproject_cdata(ss, vd.vertex, oldco, oldno); + BKE_sculpt_reproject_cdata(ss, vd.vertex, oldco, oldno, false); } if (vd.is_mesh) { -- 2.30.2 From 9b5a872b228112d82a450c4350fa4fe44fdd3757 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Sat, 10 Jun 2023 23:37:54 -0700 Subject: [PATCH 180/279] temp-sculpt-dyntopo: Fix thread conflict --- source/blender/blenkernel/intern/dyntopo.cc | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/source/blender/blenkernel/intern/dyntopo.cc b/source/blender/blenkernel/intern/dyntopo.cc index 7638a01a8a7..06879508b5d 100644 --- a/source/blender/blenkernel/intern/dyntopo.cc +++ b/source/blender/blenkernel/intern/dyntopo.cc @@ -3851,8 +3851,10 @@ void BKE_sculpt_reproject_cdata( /* Interpolate. */ BMLoop _interpl, *interpl = &_interpl; + BMVert _v = *l->v; *interpl = *l; + interpl->v = &_v; #ifdef WITH_ASAN /* Can't unpoison memory in threaded code. */ @@ -3868,7 +3870,6 @@ void BKE_sculpt_reproject_cdata( normalize_v3(l->v->no); if (l->v == v && cur_vblock < max_vblocks) { - void *vblock_old = interpl->v->head.data; void *vblock = vblocks[cur_vblock]; #ifdef WITH_ASAN @@ -3879,10 +3880,7 @@ void BKE_sculpt_reproject_cdata( #endif interpl->v->head.data = (void *)vblock; - reproject_bm_data(ss->bm, interpl, fakef, true, typemask); - - interpl->v->head.data = vblock_old; cur_vblock++; } else { -- 2.30.2 From 5d74725397e63aee92b0ba91945fe1a13ce6d74a Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Wed, 14 Jun 2023 22:42:00 -0700 Subject: [PATCH 181/279] temp-sculpt-dyntopo: Fix fairing and topological boundary smoothing Also removed a debug operator. --- source/blender/blenkernel/intern/mesh_fair.cc | 15 +- .../editors/sculpt_paint/sculpt_api.cc | 2 +- .../editors/sculpt_paint/sculpt_face_set.cc | 6 +- .../editors/sculpt_paint/sculpt_ops.cc | 291 ------------------ .../editors/sculpt_paint/sculpt_smooth.cc | 3 +- 5 files changed, 17 insertions(+), 300 deletions(-) diff --git a/source/blender/blenkernel/intern/mesh_fair.cc b/source/blender/blenkernel/intern/mesh_fair.cc index 65ca6bf7cbc..ed902cc6165 100644 --- a/source/blender/blenkernel/intern/mesh_fair.cc +++ b/source/blender/blenkernel/intern/mesh_fair.cc @@ -259,7 +259,7 @@ class BMeshFairingContext : public FairingContext { totvert_ = bm->totvert; totloop_ = bm->totloop; - BM_mesh_elem_table_ensure(bm, BM_VERT); + BM_mesh_elem_table_ensure(bm, BM_VERT | BM_FACE); BM_mesh_elem_index_ensure(bm, BM_LOOP); /* Deformation coords. */ @@ -270,7 +270,7 @@ class BMeshFairingContext : public FairingContext { } bmloop_.reinitialize(bm->totloop); - vert_to_loop_offsets_ = Array(bm->totvert, 0); + vert_to_loop_offsets_ = Array(bm->totvert + 1, 0); vert_to_loop_indices_.reinitialize(bm->totloop); BMVert *v; @@ -290,7 +290,12 @@ class BMeshFairingContext : public FairingContext { index_iter++; } } + + vert_to_loop_offsets_[bm->totvert] = bm->totloop; + vert_to_loop_offsets_.last() = index_iter; + vlmap_ = blender::GroupedSpan(blender::OffsetIndices(vert_to_loop_offsets_), + vert_to_loop_indices_); } void adjacents_coords_from_loop(const int loop, @@ -301,12 +306,12 @@ class BMeshFairingContext : public FairingContext { copy_v3_v3(r_adj_prev, bmloop_[loop]->prev->v->co); } - int other_vertex_index_from_loop(const int loop, const uint v) override + ATTR_NO_OPT int other_vertex_index_from_loop(const int loop, const uint v) override { BMLoop *l = bmloop_[loop]; BMVert *bmvert = BM_vert_at_index(bm, v); BMVert *bm_other_vert = BM_edge_other_vert(l->e, bmvert); - return BM_elem_index_get(bm_other_vert); + return bm_other_vert ? BM_elem_index_get(bm_other_vert) : v; } protected: @@ -345,7 +350,7 @@ class UniformVertexWeight : public VertexWeight { class VoronoiVertexWeight : public VertexWeight { public: - VoronoiVertexWeight(FairingContext *fairing_context) + ATTR_NO_OPT VoronoiVertexWeight(FairingContext *fairing_context) { const int totvert = fairing_context->vertex_count_get(); diff --git a/source/blender/editors/sculpt_paint/sculpt_api.cc b/source/blender/editors/sculpt_paint/sculpt_api.cc index 0be059edc6f..b56ec6f0989 100644 --- a/source/blender/editors/sculpt_paint/sculpt_api.cc +++ b/source/blender/editors/sculpt_paint/sculpt_api.cc @@ -457,7 +457,7 @@ int SCULPT_vertex_valence_get(const struct SculptSession *ss, PBVHVertRef vertex return tot; } -#if 1 +#if 0 int tot = vertex_attr_get(vertex, ss->attrs.valence); int real_tot = sculpt_calc_valence(ss, vertex); diff --git a/source/blender/editors/sculpt_paint/sculpt_face_set.cc b/source/blender/editors/sculpt_paint/sculpt_face_set.cc index 932400531cf..01c25b44236 100644 --- a/source/blender/editors/sculpt_paint/sculpt_face_set.cc +++ b/source/blender/editors/sculpt_paint/sculpt_face_set.cc @@ -683,7 +683,7 @@ static int sculpt_face_set_init_exec(bContext *C, wmOperator *op) const float threshold = RNA_float_get(op->ptr, "threshold"); Mesh *mesh = static_cast(ob->data); - ss->face_sets = BKE_sculpt_face_sets_ensure(ob); + BKE_sculpt_face_sets_ensure(ob); const bke::AttributeAccessor attributes = mesh->attributes(); switch (mode) { @@ -1395,7 +1395,6 @@ static void sculpt_face_set_delete_geometry(Object *ob, if (ss->bm) { BM_mesh_toolflags_set(bm, false); BM_idmap_check_attributes(ss->bm_idmap); - BKE_sculptsession_update_attr_refs(ob); } else { BMeshToMeshParams bmesh_to_mesh_params{}; @@ -1404,6 +1403,9 @@ static void sculpt_face_set_delete_geometry(Object *ob, BM_mesh_free(bm); } + + BKE_sculptsession_update_attr_refs(ob); + SCULPT_update_all_valence_boundary(ob); } static void sculpt_face_set_edit_fair_face_set(Object *ob, diff --git a/source/blender/editors/sculpt_paint/sculpt_ops.cc b/source/blender/editors/sculpt_paint/sculpt_ops.cc index 1ceba423ff3..36033d4f8f8 100644 --- a/source/blender/editors/sculpt_paint/sculpt_ops.cc +++ b/source/blender/editors/sculpt_paint/sculpt_ops.cc @@ -1299,284 +1299,6 @@ typedef struct BMLinkItem { int depth; } BMLinkItem; -static int sculpt_regularize_rake_exec(bContext *C, wmOperator *op) -{ - Object *ob = CTX_data_active_object(C); - SculptSession *ss = ob->sculpt; - - Object *sculpt_get_vis_object(bContext * C, SculptSession * ss, const char *name); - void sculpt_end_vis_object(bContext * C, SculptSession * ss, Object * ob, BMesh * bm); - - BMeshCreateParams params = {}; - params.use_toolflags = false; - - Object *visob = sculpt_get_vis_object(C, ss, "rakevis"); - BMesh *visbm = BM_mesh_create(&bm_mesh_allocsize_default, ¶ms); - - if (!ss) { - printf("mising sculpt session\n"); - return OPERATOR_CANCELLED; - } - if (!ss->bm) { - printf("bmesh only!\n"); - return OPERATOR_CANCELLED; - } - - BMesh *bm = ss->bm; - BMIter iter; - BMVert *v; - - int idx = CustomData_get_named_layer_index(&bm->vdata, CD_PROP_COLOR, "_rake_temp"); - if (idx < 0) { - printf("no rake temp\n"); - return OPERATOR_CANCELLED; - } - - int cd_vcol = bm->vdata.layers[idx].offset; - int cd_vcol_vis = -1; - - idx = CustomData_get_named_layer_index(&bm->vdata, CD_PROP_COLOR, "_rake_vis"); - if (idx >= 0) { - cd_vcol_vis = bm->vdata.layers[idx].offset; - } - - for (int step = 0; step < 33; step++) { - BLI_mempool *nodepool = BLI_mempool_create(sizeof(BMLinkItem), bm->totvert, 4196, 0); - - Vector nodes = blender::bke::pbvh::search_gather(ss->pbvh, nullptr, nullptr); - - SCULPT_undo_push_begin(ob, op); - for (int i = 0; i < nodes.size(); i++) { - SCULPT_ensure_dyntopo_node_undo(ob, nodes[i], SCULPT_UNDO_COLOR, -1); - BKE_pbvh_node_mark_update_color(nodes[i]); - } - SCULPT_undo_push_end(ob); - - bm->elem_index_dirty |= BM_VERT; - BM_mesh_elem_index_ensure(bm, BM_VERT); - - BLI_bitmap *visit = BLI_BITMAP_NEW(bm->totvert, "regularize rake visit bitmap"); - - BMVert **verts = MEM_cnew_array(bm->totvert, "verts"); - BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) { - verts[v->head.index] = v; - v->head.hflag &= ~BM_ELEM_SELECT; - } - - RNG *rng = BLI_rng_new((uint)BLI_thread_rand(0)); - BLI_rng_shuffle_array(rng, verts, sizeof(void *), bm->totvert); - - for (int i = 0; i < bm->totvert; i++) { - BMVert *v = verts[i]; - PBVHVertRef vertex = {(intptr_t)v}; - - int *boundflag = vertex_attr_ptr(vertex, ss->attrs.boundary_flags); - - if (*boundflag & (SCULPT_CORNER_MESH | SCULPT_CORNER_FACE_SET | SCULPT_CORNER_SHARP_MARK | - SCULPT_CORNER_SEAM)) - { - continue; - } - - if (BLI_BITMAP_TEST(visit, v->head.index)) { - continue; - } - - // v->head.hflag |= BM_ELEM_SELECT; - - float *dir = BM_ELEM_CD_PTR(v, cd_vcol); - normalize_v3(dir); - - BMLinkItem *node = static_cast(BLI_mempool_alloc(nodepool)); - node->next = node->prev = nullptr; - node->item = v; - node->depth = 0; - - BLI_BITMAP_SET(visit, v->head.index, true); - - ListBase queue = {node, node}; - const int boundtest = SCULPT_BOUNDARY_MESH | SCULPT_BOUNDARY_FACE_SET | - SCULPT_BOUNDARY_SEAM | SCULPT_BOUNDARY_SHARP_MARK; - while (queue.first) { - BMLinkItem *node2 = static_cast(BLI_poptail(&queue)); - BMVert *v2 = node2->item; - - float *dir2 = BM_ELEM_CD_PTR(v2, cd_vcol); - - if (cd_vcol_vis >= 0) { - float *color = BM_ELEM_CD_PTR(v2, cd_vcol_vis); - color[0] = color[1] = color[2] = (float)(node2->depth % 5) / 5.0f; - color[3] = 1.0f; - } - - if (step % 5 != 0 && node2->depth > 15) { - // break; - } - // dir2[0] = dir2[1] = dir2[2] = (float)(node2->depth % 5) / 5.0f; - // dir2[3] = 1.0f; - - BMIter viter; - BMEdge *e; - - int closest_vec_to_perp( - float dir[3], float r_dir2[3], float no[3], float *buckets, float w); - - // float buckets[8] = {0}; - float tmp[3]; - float dir32[3]; - float avg[3] = {0.0f}; - float tot = 0.0f; - - // angle_on_axis_v3v3v3_v3 - float tco[3]; - zero_v3(tco); - add_v3_fl(tco, 1000.0f); - // madd_v3_v3fl(tco, v2->no, -dot_v3v3(v2->no, tco)); - - float tanco[3]; - add_v3_v3v3(tanco, v2->co, dir2); - - float lastdir3[3]; - float firstdir3[3]; - bool first = true; - float thsum = 0.0f; - - // don't propegate across singularities - - BM_ITER_ELEM (e, &viter, v2, BM_EDGES_OF_VERT) { - // e = l->e; - BMVert *v3 = BM_edge_other_vert(e, v2); - float *dir3 = BM_ELEM_CD_PTR(v3, cd_vcol); - float dir32[3]; - - copy_v3_v3(dir32, dir3); - - if (first) { - first = false; - copy_v3_v3(firstdir3, dir32); - } - else { - float th = saacos(dot_v3v3(dir32, lastdir3)); - thsum += th; - } - - copy_v3_v3(lastdir3, dir32); - - add_v3_v3(avg, dir32); - tot += 1.0f; - } - - thsum += saacos(dot_v3v3(lastdir3, firstdir3)); - bool sing = thsum >= M_PI * 0.5f; - - // still apply smoothing even with singularity? - if (tot > 0.0f && !(*boundflag & boundtest)) { - mul_v3_fl(avg, 1.0 / tot); - interp_v3_v3v3(dir2, dir2, avg, sing ? 0.15 : 0.25); - normalize_v3(dir2); - } - - if (sing) { - v2->head.hflag |= BM_ELEM_SELECT; - - if (node2->depth == 0) { - continue; - } - } - - BM_ITER_ELEM (e, &viter, v2, BM_EDGES_OF_VERT) { - BMVert *v3 = BM_edge_other_vert(e, v2); - float *dir3 = BM_ELEM_CD_PTR(v3, cd_vcol); - - if (BLI_BITMAP_TEST(visit, v3->head.index)) { - continue; - } - - copy_v3_v3(dir32, dir3); - madd_v3_v3fl(dir32, v2->no, -dot_v3v3(dir3, v2->no)); - normalize_v3(dir32); - - if (dot_v3v3(dir32, dir2) < 0) { - negate_v3(dir32); - } - - cross_v3_v3v3(tmp, dir32, v2->no); - normalize_v3(tmp); - - if (dot_v3v3(tmp, dir2) < 0) { - negate_v3(tmp); - } - - float th1 = fabsf(saacos(dot_v3v3(dir2, dir32))); - float th2 = fabsf(saacos(dot_v3v3(dir2, tmp))); - - if (th2 < th1) { - copy_v3_v3(dir32, tmp); - } - - madd_v3_v3fl(dir32, v3->no, -dot_v3v3(dir32, v3->no)); - normalize_v3(dir32); - copy_v3_v3(dir3, dir32); - - // int bits = closest_vec_to_perp(dir2, dir32, v2->no, buckets, 1.0f); - -#if 0 - MSculptVert *mv3 = BKE_PBVH_SCULPTVERT(ss->cd_sculpt_vert, v3); - PBVHVertRef vertex3 = {(intptr_t)v3}; - - int *boundflag3 = vertex_attr_ptr(vertex3, ss->attrs.boundary_flags); - if (*boundflag3 & boundtest) { - continue; - } -#endif - - BLI_BITMAP_SET(visit, v3->head.index, true); - - BMLinkItem *node3 = (BMLinkItem *)BLI_mempool_alloc(nodepool); - node3->next = node3->prev = nullptr; - node3->item = v3; - node3->depth = node2->depth + 1; - - BLI_addhead(&queue, node3); - } - - BLI_mempool_free(nodepool, node2); - } - } - - MEM_SAFE_FREE(verts); - BLI_mempool_destroy(nodepool); - MEM_SAFE_FREE(visit); - } - - BMVert *v3; - BM_ITER_MESH (v3, &iter, bm, BM_VERTS_OF_MESH) { - float visco[3]; - float *dir3 = BM_ELEM_CD_PTR(v3, cd_vcol); - - madd_v3_v3v3fl(visco, v3->co, v3->no, 0.001); - BMVert *vis1 = BM_vert_create(visbm, visco, nullptr, BM_CREATE_NOP); - - madd_v3_v3v3fl(visco, visco, dir3, 0.003); - BMVert *vis2 = BM_vert_create(visbm, visco, nullptr, BM_CREATE_NOP); - BM_edge_create(visbm, vis1, vis2, nullptr, BM_CREATE_NOP); - - float tan[3]; - cross_v3_v3v3(tan, dir3, v3->no); - madd_v3_v3fl(visco, tan, 0.001); - - vis1 = BM_vert_create(visbm, visco, nullptr, BM_CREATE_NOP); - BM_edge_create(visbm, vis1, vis2, nullptr, BM_CREATE_NOP); - } - - DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); - WM_event_add_notifier(C, NC_GEOM | ND_DATA, ob->data); - - sculpt_end_vis_object(C, ss, visob, visbm); - - return OPERATOR_FINISHED; -} - enum CavityBakeMixMode { AUTOMASK_BAKE_MIX, AUTOMASK_BAKE_MULTIPLY, @@ -1757,18 +1479,6 @@ static int sculpt_bake_cavity_exec(bContext *C, wmOperator *op) return OPERATOR_FINISHED; } -void SCULPT_OT_regularize_rake_directions(wmOperatorType *ot) -{ - ot->name = "Regularize Rake Directions"; - ot->idname = "SCULPT_OT_regularize_rake_directions"; - ot->description = "Development operator"; - - /* API callbacks. */ - ot->poll = SCULPT_mode_poll; - ot->exec = sculpt_regularize_rake_exec; - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; -} - static void cavity_bake_ui(bContext *C, wmOperator *op) { uiLayout *layout = op->layout; @@ -2015,7 +1725,6 @@ void ED_operatortypes_sculpt(void) WM_operatortype_append(SCULPT_OT_expand); WM_operatortype_append(SCULPT_OT_mask_from_cavity); - WM_operatortype_append(SCULPT_OT_regularize_rake_directions); WM_operatortype_append(SCULPT_OT_reveal_all); } diff --git a/source/blender/editors/sculpt_paint/sculpt_smooth.cc b/source/blender/editors/sculpt_paint/sculpt_smooth.cc index 9dc57f9ee84..a99c8e233e6 100644 --- a/source/blender/editors/sculpt_paint/sculpt_smooth.cc +++ b/source/blender/editors/sculpt_paint/sculpt_smooth.cc @@ -115,7 +115,8 @@ static void SCULPT_neighbor_coords_average_interior_ex(SculptSession *ss, eSculptBoundary uvflag = ss->reproject_smooth ? SCULPT_BOUNDARY_UV : SCULPT_BOUNDARY_NONE; - eSculptBoundary hard_flags = SCULPT_BOUNDARY_SHARP_MARK | SCULPT_BOUNDARY_SHARP_ANGLE; + eSculptBoundary hard_flags = SCULPT_BOUNDARY_SHARP_MARK | SCULPT_BOUNDARY_SHARP_ANGLE | + SCULPT_BOUNDARY_MESH; if (ss->hard_edge_mode) { hard_flags |= SCULPT_BOUNDARY_FACE_SET; } -- 2.30.2 From a78e5e2d04102e117a0a6dc566af977bab2bdeb7 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Thu, 15 Jun 2023 01:07:03 -0700 Subject: [PATCH 182/279] temp-sculpt-dyntopo: Fix smoothing bugs --- source/blender/blenkernel/intern/dyntopo.cc | 127 +++--------------- source/blender/blenkernel/intern/mesh_fair.cc | 4 +- source/blender/blenkernel/intern/paint.cc | 5 + .../blender/blenkernel/intern/pbvh_bmesh.cc | 6 +- .../editors/sculpt_paint/sculpt_smooth.cc | 3 + 5 files changed, 30 insertions(+), 115 deletions(-) diff --git a/source/blender/blenkernel/intern/dyntopo.cc b/source/blender/blenkernel/intern/dyntopo.cc index 06879508b5d..3ced8214dd9 100644 --- a/source/blender/blenkernel/intern/dyntopo.cc +++ b/source/blender/blenkernel/intern/dyntopo.cc @@ -3444,7 +3444,7 @@ void reproject_interp_data(CustomData *data, } template // myinterp::TestFloat> -static void reproject_bm_data( +static bool reproject_bm_data( BMesh *bm, BMLoop *l_dst, const BMFace *f_src, const bool do_vertex, eCustomDataMask typemask) { using namespace myinterp; @@ -3487,7 +3487,7 @@ static void reproject_bm_data( float len_sq = len_squared_v3v3(l_iter->v->co, l_iter->next->v->co); if (len_sq < FLT_EPSILON * 100.0f) { - return; + return false; } if (do_vertex) { @@ -3497,9 +3497,22 @@ static void reproject_bm_data( mul_v2_m3v3(co, axis_mat, l_dst->v->co); + return false; T tco[2]; myinterp::copy_v2_v2(tco, co); + for (int i = 0; i < f_src->len; i++) { + float2 t1 = float2(cos_2d[(i + 1) % f_src->len]) - float2(cos_2d[i]); + float2 t2 = float2(cos_2d[(i + 2) % f_src->len]) - float2(cos_2d[(i + 1) % f_src->len]); + normalize_v2(t1); + normalize_v2(t2); + + float angle = saacos(dot_v2v2(t1, t2)); + if (angle > M_PI * 0.95) { + return false; /* Very acute face */ + } + } + /* interpolate */ if (f_src->len == 3) { myinterp::tri_weights_v3(tco, cos_2d[0], cos_2d[1], cos_2d[2], w); @@ -3554,6 +3567,8 @@ static void reproject_bm_data( reproject_interp_data( &bm->vdata, vblocks, fw, nullptr, f_src->len, l_dst->v->head.data, typemask); } + + return true; } void BKE_sculpt_reproject_cdata( @@ -3590,9 +3605,6 @@ void BKE_sculpt_reproject_cdata( int tag = BM_ELEM_TAG_ALT; - float *lastuvs = static_cast(BLI_array_alloca(lastuvs, totuv * 2)); - bool *snapuvs = static_cast(BLI_array_alloca(snapuvs, totuv)); - e = v->e; int valence = 0; @@ -3615,13 +3627,6 @@ void BKE_sculpt_reproject_cdata( Vector ls; - bool first = true; - bool bad = false; - - for (int i = 0; i < totuv; i++) { - snapuvs[i] = true; - } - do { BMLoop *l = e->l; @@ -3638,43 +3643,9 @@ void BKE_sculpt_reproject_cdata( l2->head.hflag |= tag; ls.append(l2); - - for (int i = 0; do_uvs && i < totuv; i++) { - const int cd_uv = uvlayer[i].offset; - float *luv = BM_ELEM_CD_PTR(l2, cd_uv); - - /* Check that we are not part of a uv seam. */ - if (!first) { - const float dx = lastuvs[i * 2] - luv[0]; - const float dy = lastuvs[i * 2 + 1] - luv[1]; - const float eps = 0.00001f; - - if (dx * dx + dy * dy > eps) { - bad = true; - snapuvs[i] = false; - } - } - - lastuvs[i * 2] = luv[0]; - lastuvs[i * 2 + 1] = luv[1]; - } - - first = false; - - if (bad) { - break; - } } while ((l = l->radial_next) != e->l); - - if (bad) { - break; - } } while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e); - if (bad || !ls.size()) { - return; - } - int totloop = ls.size(); /* Original (l->prev, l, l->next) projections for each loop ('l' remains unchanged). */ @@ -3874,9 +3845,9 @@ void BKE_sculpt_reproject_cdata( #ifdef WITH_ASAN /* Can't unpoison memory in threaded code. */ - CustomData_bmesh_copy_data(&ss->bm->vdata, &ss->bm->vdata, v->head.data, &vblocks[i]); + CustomData_bmesh_copy_data(&ss->bm->vdata, &ss->bm->vdata, v->head.data, &vblock); #else - memcpy(vblocks[i], v->head.data, ss->bm->vdata.totsize); + memcpy(vblock, v->head.data, ss->bm->vdata.totsize); #endif interpl->v->head.data = (void *)vblock; @@ -3914,65 +3885,5 @@ void BKE_sculpt_reproject_cdata( } } - int *tots = static_cast(BLI_array_alloca(tots, totuv)); - - for (int i = 0; i < totuv; i++) { - lastuvs[i * 2] = lastuvs[i * 2 + 1] = 0.0f; - tots[i] = 0; - } - snapper.snap(); - return; - - // XXX delete the below code after testing new snap code. - - /* Re-snap uvs. */ - v = (BMVert *)vertex.i; - - e = v->e; - do { - if (!e->l) { - continue; - } - - BMLoop *l_iter = e->l; - do { - BMLoop *l = l_iter->v != v ? l_iter->next : l_iter; - - for (int i = 0; i < totuv; i++) { - const int cd_uv = uvlayer[i].offset; - float *luv = BM_ELEM_CD_PTR(l, cd_uv); - - add_v2_v2(lastuvs + i * 2, luv); - tots[i]++; - } - } while ((l_iter = l_iter->radial_next) != e->l); - } while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e); - - for (int i = 0; i < totuv; i++) { - if (tots[i]) { - mul_v2_fl(lastuvs + i * 2, 1.0f / (float)tots[i]); - } - } - - e = v->e; - do { - if (!e->l) { - continue; - } - - BMLoop *l_iter = e->l; - do { - BMLoop *l = l_iter->v != v ? l_iter->next : l_iter; - - for (int i = 0; i < totuv; i++) { - const int cd_uv = uvlayer[i].offset; - float *luv = BM_ELEM_CD_PTR(l, cd_uv); - - if (snapuvs[i]) { - copy_v2_v2(luv, lastuvs + i * 2); - } - } - } while ((l_iter = l_iter->radial_next) != e->l); - } while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e); } diff --git a/source/blender/blenkernel/intern/mesh_fair.cc b/source/blender/blenkernel/intern/mesh_fair.cc index ed902cc6165..4f5f33b49ac 100644 --- a/source/blender/blenkernel/intern/mesh_fair.cc +++ b/source/blender/blenkernel/intern/mesh_fair.cc @@ -306,7 +306,7 @@ class BMeshFairingContext : public FairingContext { copy_v3_v3(r_adj_prev, bmloop_[loop]->prev->v->co); } - ATTR_NO_OPT int other_vertex_index_from_loop(const int loop, const uint v) override + int other_vertex_index_from_loop(const int loop, const uint v) override { BMLoop *l = bmloop_[loop]; BMVert *bmvert = BM_vert_at_index(bm, v); @@ -350,7 +350,7 @@ class UniformVertexWeight : public VertexWeight { class VoronoiVertexWeight : public VertexWeight { public: - ATTR_NO_OPT VoronoiVertexWeight(FairingContext *fairing_context) + VoronoiVertexWeight(FairingContext *fairing_context) { const int totvert = fairing_context->vertex_count_get(); diff --git a/source/blender/blenkernel/intern/paint.cc b/source/blender/blenkernel/intern/paint.cc index ce2a0f4cc01..48e58bd0ce5 100644 --- a/source/blender/blenkernel/intern/paint.cc +++ b/source/blender/blenkernel/intern/paint.cc @@ -3998,6 +3998,11 @@ void BKE_sculpt_attribute_destroy_temporary_all(Object *ob) SculptAttribute BKE_sculpt_find_attribute(Object *ob, const char *name) { + if (!name) { + SculptAttribute attr = {}; + return attr; + } + SculptSession *ss = ob->sculpt; CustomData *cdatas[4]; diff --git a/source/blender/blenkernel/intern/pbvh_bmesh.cc b/source/blender/blenkernel/intern/pbvh_bmesh.cc index 1bcf7ad97c2..cddec615acb 100644 --- a/source/blender/blenkernel/intern/pbvh_bmesh.cc +++ b/source/blender/blenkernel/intern/pbvh_bmesh.cc @@ -2194,10 +2194,6 @@ void update_vert_boundary_bmesh(int cd_faceset_offset, if (!(e->head.hflag & BM_ELEM_SMOOTH)) { boundflag |= SCULPT_BOUNDARY_SHARP_MARK; sharpcount++; - - if (sharpcount > 2) { - boundflag |= SCULPT_CORNER_SHARP_MARK; - } } if (e->l) { @@ -2273,7 +2269,7 @@ void update_vert_boundary_bmesh(int cd_faceset_offset, } if (!ELEM(sharpcount, 0, 2)) { - boundflag |= SCULPT_CORNER_SHARP_MARK; + //boundflag |= SCULPT_CORNER_SHARP_MARK; } if (seamcount == 1) { diff --git a/source/blender/editors/sculpt_paint/sculpt_smooth.cc b/source/blender/editors/sculpt_paint/sculpt_smooth.cc index a99c8e233e6..d5dd4bf6c4f 100644 --- a/source/blender/editors/sculpt_paint/sculpt_smooth.cc +++ b/source/blender/editors/sculpt_paint/sculpt_smooth.cc @@ -117,6 +117,9 @@ static void SCULPT_neighbor_coords_average_interior_ex(SculptSession *ss, eSculptBoundary hard_flags = SCULPT_BOUNDARY_SHARP_MARK | SCULPT_BOUNDARY_SHARP_ANGLE | SCULPT_BOUNDARY_MESH; + + hard_flags &= bound_type; + if (ss->hard_edge_mode) { hard_flags |= SCULPT_BOUNDARY_FACE_SET; } -- 2.30.2 From 97c181c2f825ddbafa6a44a9fa4a1dca5a0b9e41 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Thu, 15 Jun 2023 04:21:30 -0700 Subject: [PATCH 183/279] temp-sculpt-dyntopo: Prevent degenerate topology from very skinny faces --- source/blender/blenkernel/intern/dyntopo_intern.hh | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/source/blender/blenkernel/intern/dyntopo_intern.hh b/source/blender/blenkernel/intern/dyntopo_intern.hh index 967aa86a639..821b5f249b3 100644 --- a/source/blender/blenkernel/intern/dyntopo_intern.hh +++ b/source/blender/blenkernel/intern/dyntopo_intern.hh @@ -104,8 +104,15 @@ inline bool bm_elem_is_free(BMElem *elem, int htype) SCULPT_CORNER_UV | SCULPT_CORNER_SHARP_ANGLE) #define DYNTOPO_MAX_ITER 512 -#define DYNTOPO_MAX_ITER_COLLAPSE 16 -#define DYNTOPO_MAX_ITER_SUBD 16 + +/* Very long skinny edges are pathological for remeshing, + * they lead to degenerate geometry with 1/1 ratio between + * subdivison and collapse. Ideally we should detect this + * case and adjust the ratio dynamically, but for now just + * use a very high ratio all the time. + */ +#define DYNTOPO_MAX_ITER_COLLAPSE 64 +#define DYNTOPO_MAX_ITER_SUBD 1 #define DYNTOPO_USE_HEAP #define DYNTOPO_USE_MINMAX_HEAP -- 2.30.2 From 7ec336c4f4611ad70a02aa92b812036866b2f96d Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Thu, 15 Jun 2023 05:39:39 -0700 Subject: [PATCH 184/279] temp-sculpt-dyntopo: Fix memory corruption --- source/blender/blenkernel/intern/customdata.cc | 1 + source/blender/blenkernel/intern/mesh_legacy_convert.cc | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/source/blender/blenkernel/intern/customdata.cc b/source/blender/blenkernel/intern/customdata.cc index 2d1db541ec7..5ea608def77 100644 --- a/source/blender/blenkernel/intern/customdata.cc +++ b/source/blender/blenkernel/intern/customdata.cc @@ -2720,6 +2720,7 @@ static void customData_update_offsets(CustomData *data) if (data->totlayer == 0) { data->totsize = 0; + CustomData_update_typemap(data); return; } diff --git a/source/blender/blenkernel/intern/mesh_legacy_convert.cc b/source/blender/blenkernel/intern/mesh_legacy_convert.cc index 649269fe606..988eaff744c 100644 --- a/source/blender/blenkernel/intern/mesh_legacy_convert.cc +++ b/source/blender/blenkernel/intern/mesh_legacy_convert.cc @@ -2043,7 +2043,7 @@ void BKE_mesh_legacy_convert_polys_to_offsets(Mesh *mesh) } }); - CustomData_free(&old_poly_data, mesh->totloop); + CustomData_free(&old_poly_data, mesh->totpoly); } CustomData_free_layers(&mesh->pdata, CD_MPOLY, mesh->totpoly); -- 2.30.2 From f95e6d51e5e4eeacc98729970075b9fb662b94a8 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Thu, 15 Jun 2023 22:23:28 -0700 Subject: [PATCH 185/279] temp-sculpt-dyntopo: Use normal smooth code for relax brush --- .../sculpt_paint/sculpt_brush_types.cc | 52 ++++++++++++++----- 1 file changed, 40 insertions(+), 12 deletions(-) diff --git a/source/blender/editors/sculpt_paint/sculpt_brush_types.cc b/source/blender/editors/sculpt_paint/sculpt_brush_types.cc index 9465076e5cc..9aee8719e82 100644 --- a/source/blender/editors/sculpt_paint/sculpt_brush_types.cc +++ b/source/blender/editors/sculpt_paint/sculpt_brush_types.cc @@ -2548,6 +2548,13 @@ static void do_topology_relax_task_cb_ex(void *__restrict userdata, SCULPT_automasking_node_begin( data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]); + bool do_reproject = SCULPT_need_reproject(ss); + bool weighted = brush->flag2 & BRUSH_SMOOTH_USE_AREA_WEIGHT; + + if (weighted) { + BKE_pbvh_check_tri_areas(ss->pbvh, data->nodes[n]); + } + BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { SCULPT_orig_vert_data_update(ss, &orig_data, vd.vertex); if (!sculpt_brush_test_sq_fn(&test, orig_data.co)) { @@ -2555,18 +2562,35 @@ static void do_topology_relax_task_cb_ex(void *__restrict userdata, } SCULPT_automasking_node_update(ss, &automask_data, &vd); - const float fade = SCULPT_brush_strength_factor(ss, - brush, - orig_data.co, - sqrtf(test.dist), - orig_data.no, - nullptr, - vd.mask ? *vd.mask : 0.0f, - vd.vertex, - thread_id, - &automask_data); + const float fade = bstrength * SCULPT_brush_strength_factor(ss, + brush, + orig_data.co, + sqrtf(test.dist), + orig_data.no, + nullptr, + vd.mask ? *vd.mask : 0.0f, + vd.vertex, + thread_id, + &automask_data); + + float3 startco = vd.co; + float3 startno; + float3 avg; + + SCULPT_vertex_normal_get(ss, vd.vertex, startno); + + /* SCULPT_relax_vertex(ss, &vd, fade , SCULPT_BOUNDARY_MESH, vd.co); */ + + SCULPT_neighbor_coords_average_interior( + ss, avg, vd.vertex, 0.98f, 1.0f, weighted, false, fade); + + interp_v3_v3v3(vd.co, vd.co, avg, fade); + BKE_sculpt_sharp_boundary_flag_update(ss, vd.vertex); + + if (do_reproject) { + BKE_sculpt_reproject_cdata(ss, vd.vertex, startco, startno, false); + } - SCULPT_relax_vertex(ss, &vd, fade * bstrength, SCULPT_BOUNDARY_MESH, vd.co); if (vd.is_mesh) { BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); } @@ -2597,6 +2621,10 @@ void SCULPT_do_slide_relax_brush(Sculpt *sd, Object *ob, Span nodes) BKE_pbvh_parallel_range_settings(&settings, true, nodes.size()); if (ss->cache->alt_smooth) { SCULPT_boundary_info_ensure(ob); + if (brush->flag2 & BRUSH_SMOOTH_USE_AREA_WEIGHT) { + BKE_pbvh_face_areas_begin(ss->pbvh); + } + for (int i = 0; i < 4; i++) { BLI_task_parallel_range(0, nodes.size(), &data, do_topology_relax_task_cb_ex, &settings); } @@ -2975,7 +3003,7 @@ static void do_topology_rake_bmesh_task_cb_ex(void *__restrict userdata, if (do_reproject) { /* Use interp_face_corners instead. */ - //BKE_sculpt_reproject_cdata(ss, vd.vertex, oldco, oldno); + // BKE_sculpt_reproject_cdata(ss, vd.vertex, oldco, oldno); } if (vd.is_mesh) { -- 2.30.2 From 0fc08ada7a4d91c6c577f97502d512a567756a36 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Fri, 16 Jun 2023 01:58:15 -0700 Subject: [PATCH 186/279] temp-sculpt-dyntopo: Face set init operator now works with dyntopo --- .../editors/sculpt_paint/sculpt_face_set.cc | 47 +++++++++++++++---- 1 file changed, 37 insertions(+), 10 deletions(-) diff --git a/source/blender/editors/sculpt_paint/sculpt_face_set.cc b/source/blender/editors/sculpt_paint/sculpt_face_set.cc index 01c25b44236..5d317868491 100644 --- a/source/blender/editors/sculpt_paint/sculpt_face_set.cc +++ b/source/blender/editors/sculpt_paint/sculpt_face_set.cc @@ -586,7 +586,8 @@ static void sculpt_face_sets_init_flood_fill(Object *ob, const FaceSetsFloodFill BitVector<> visited_faces(mesh->totpoly, false); - int *face_sets = ss->face_sets; + int *face_sets = static_cast(CustomData_get_layer_named_for_write( + &mesh->pdata, CD_PROP_INT32, SCULPT_ATTRIBUTE_NAME(face_set), mesh->totpoly)); const Span edges = mesh->edges(); const OffsetIndices polys = mesh->polys(); @@ -663,11 +664,6 @@ static int sculpt_face_set_init_exec(bContext *C, wmOperator *op) BKE_sculpt_update_object_for_edit(depsgraph, ob, true, false, false); - /* Dyntopo not supported. */ - if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) { - return OPERATOR_CANCELLED; - } - PBVH *pbvh = ob->sculpt->pbvh; Vector nodes = blender::bke::pbvh::search_gather(pbvh, nullptr, nullptr); @@ -675,17 +671,34 @@ static int sculpt_face_set_init_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } + const float threshold = RNA_float_get(op->ptr, "threshold"); + + Mesh *mesh = static_cast(ob->data); + BKE_sculpt_face_sets_ensure(ob); + SCULPT_undo_push_begin(ob, op); for (PBVHNode *node : nodes) { SCULPT_undo_push_node(ob, node, SCULPT_UNDO_FACE_SETS); } - const float threshold = RNA_float_get(op->ptr, "threshold"); + /* Flush bmesh to base mesh. */ + if (ss->bm) { + BKE_sculptsession_bm_to_me_for_render(ob); + + if (!ss->epmap.is_empty()) { + ss->epmap = {}; + ss->edge_to_poly_indices = {}; + ss->edge_to_poly_offsets = {}; + } + + if (!ss->pmap.is_empty()) { + ss->pmap = {}; + ss->vert_to_poly_indices = {}; + ss->vert_to_poly_offsets = {}; + } + } - Mesh *mesh = static_cast(ob->data); - BKE_sculpt_face_sets_ensure(ob); const bke::AttributeAccessor attributes = mesh->attributes(); - switch (mode) { case SCULPT_FACE_SETS_FROM_LOOSE_PARTS: { const VArray hide_poly = *attributes.lookup_or_default( @@ -756,6 +769,20 @@ static int sculpt_face_set_init_exec(bContext *C, wmOperator *op) SCULPT_undo_push_end(ob); + if (ss->bm) { + SCULPT_face_random_access_ensure(ss); + BKE_sculpt_face_sets_ensure(ob); + + int cd_fset = ss->attrs.face_set->bmesh_cd_offset; + const int *face_sets = static_cast( + CustomData_get_layer_named(&mesh->pdata, CD_PROP_INT32, SCULPT_ATTRIBUTE_NAME(face_set))); + + for (int i = 0; i < mesh->totpoly; i++) { + BMFace *f = ss->bm->ftable[i]; + BM_ELEM_CD_SET_INT(f, cd_fset, face_sets[i]); + } + } + int verts_num = SCULPT_vertex_count_get(ob->sculpt); for (int i : IndexRange(verts_num)) { BKE_sculpt_boundary_flag_update(ob->sculpt, BKE_pbvh_index_to_vertex(ss->pbvh, i)); -- 2.30.2 From 909130c7e99a1f4b8128ec5194c6c1f77e563acc Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Fri, 16 Jun 2023 02:55:34 -0700 Subject: [PATCH 187/279] temp-sculpt-dyntopo: Cleanup dyntopo code a bit more * Fixed a few issues in the detail flood fill operator. * The entire notion of limiting "edge steps" has been removed from the API (since we limit by time now). --- source/blender/blenkernel/BKE_dyntopo.hh | 2 -- source/blender/blenkernel/intern/dyntopo.cc | 34 ++++++++----------- .../blenkernel/intern/dyntopo_intern.hh | 13 ++++--- .../blender/blenkernel/intern/pbvh_bmesh.cc | 4 +-- source/blender/editors/sculpt_paint/sculpt.cc | 4 --- .../editors/sculpt_paint/sculpt_detail.cc | 22 +++++++++--- 6 files changed, 38 insertions(+), 41 deletions(-) diff --git a/source/blender/blenkernel/BKE_dyntopo.hh b/source/blender/blenkernel/BKE_dyntopo.hh index 9e20e7fdeab..477e607d373 100644 --- a/source/blender/blenkernel/BKE_dyntopo.hh +++ b/source/blender/blenkernel/BKE_dyntopo.hh @@ -142,7 +142,6 @@ bool remesh_topology(blender::bke::dyntopo::BrushTester *brush_tester, bool updatePBVH, DyntopoMaskCB mask_cb, void *mask_cb_data, - int edge_limit_multiply, float quality); bool remesh_topology_nodes(blender::bke::dyntopo::BrushTester *tester, @@ -157,7 +156,6 @@ bool remesh_topology_nodes(blender::bke::dyntopo::BrushTester *tester, bool updatePBVH, DyntopoMaskCB mask_cb, void *mask_cb_data, - int edge_limit_multiply, float quality); void after_stroke(PBVH *pbvh, bool force_balance); diff --git a/source/blender/blenkernel/intern/dyntopo.cc b/source/blender/blenkernel/intern/dyntopo.cc index 3ced8214dd9..28a8ea9e0fc 100644 --- a/source/blender/blenkernel/intern/dyntopo.cc +++ b/source/blender/blenkernel/intern/dyntopo.cc @@ -334,6 +334,11 @@ static PBVHTopologyUpdateMode edge_queue_test(EdgeQueueContext *eq_ctx, return PBVH_None; } +void EdgeQueueContext::surface_smooth(BMVert *v, float fac) +{ + surface_smooth_v_safe(ss, pbvh, v, fac, reproject_cdata); +} + void EdgeQueueContext::insert_edge(BMEdge *e, float w) { if (!(e->head.hflag & EDGE_QUEUE_FLAG)) { @@ -2024,8 +2029,7 @@ EdgeQueueContext::EdgeQueueContext(BrushTester *brush_tester_, float3 view_normal_, bool updatePBVH_, DyntopoMaskCB mask_cb_, - void *mask_cb_data_, - int edge_limit_multiply) + void *mask_cb_data_) { ss = ob->sculpt; @@ -2051,7 +2055,6 @@ EdgeQueueContext::EdgeQueueContext(BrushTester *brush_tester_, surface_relax = true; reproject_cdata = ss->reproject_smooth; - max_heap_mm = (DYNTOPO_MAX_ITER * edge_limit_multiply) << 8; limit_len_min = pbvh->bm_min_edge_len; limit_len_max = pbvh->bm_max_edge_len; limit_len_min_sqr = limit_len_min * limit_len_min; @@ -2104,14 +2107,10 @@ EdgeQueueContext::EdgeQueueContext(BrushTester *brush_tester_, steps[0] = DYNTOPO_MAX_ITER_COLLAPSE; } - - max_steps = (DYNTOPO_MAX_ITER * edge_limit_multiply) << (totop - 1); } void EdgeQueueContext::start() { - current_i = 0; - /* Preemptively log UVs. */ if (!ignore_loop_data) { for (int i : IndexRange(pbvh->totnode)) { @@ -2128,7 +2127,12 @@ void EdgeQueueContext::start() bool EdgeQueueContext::done() { - return totop == 0 || edge_heap.empty() || current_i >= max_steps; + return totop == 0 || edge_heap.empty(); +} + +bool EdgeQueueContext::cleanup_valence_34() +{ + return do_cleanup_3_4(this, pbvh); } void EdgeQueueContext::finish() @@ -2311,7 +2315,6 @@ void EdgeQueueContext::step() } count++; - current_i++; } void EdgeQueueContext::report() @@ -2355,19 +2358,10 @@ bool remesh_topology(BrushTester *brush_tester, bool updatePBVH, DyntopoMaskCB mask_cb, void *mask_cb_data, - int edge_limit_multiply, float quality) { - EdgeQueueContext eq_ctx(brush_tester, - ob, - pbvh, - mode, - use_frontface, - view_normal, - updatePBVH, - mask_cb, - mask_cb_data, - edge_limit_multiply); + EdgeQueueContext eq_ctx( + brush_tester, ob, pbvh, mode, use_frontface, view_normal, updatePBVH, mask_cb, mask_cb_data); eq_ctx.start(); /* Apply a time limit to avoid excessive hangs on pathological topology. */ diff --git a/source/blender/blenkernel/intern/dyntopo_intern.hh b/source/blender/blenkernel/intern/dyntopo_intern.hh index 821b5f249b3..c31e933cc2f 100644 --- a/source/blender/blenkernel/intern/dyntopo_intern.hh +++ b/source/blender/blenkernel/intern/dyntopo_intern.hh @@ -143,7 +143,7 @@ inline bool bm_elem_is_free(BMElem *elem, int htype) * applied stochastically (by skipping verts randomly) to improve * performance. */ -#define DYNTOPO_SAFE_SMOOTH_FAC 0.025f +#define DYNTOPO_SAFE_SMOOTH_FAC 0.05f #ifdef USE_EDGEQUEUE_EVEN_SUBDIV # include "BKE_global.h" @@ -251,8 +251,6 @@ struct EdgeQueueContext { blender::MinMaxHeap edge_heap; - int max_heap_mm; - // TableGSet *used_verts; blender::Vector used_verts; float3 view_normal; @@ -273,8 +271,6 @@ struct EdgeQueueContext { int steps[2]; PBVHTopologyUpdateMode ops[2]; int totop = 0; - int current_i = 0; - int max_steps; PBVH *pbvh; bool ignore_loop_data = false; @@ -291,8 +287,7 @@ struct EdgeQueueContext { float3 view_normal, bool updatePBVH, DyntopoMaskCB mask_cb, - void *mask_cb_data, - int edge_limit_multiply); + void *mask_cb_data); void insert_val34_vert(BMVert *v); void insert_edge(BMEdge *e, float w); @@ -302,6 +297,10 @@ struct EdgeQueueContext { void step(); void finish(); + /* Remove 3 and 4 valence vertices surrounded only by triangles. */ + bool cleanup_valence_34(); + void surface_smooth(BMVert *v, float fac); + void report(); bool was_flushed() diff --git a/source/blender/blenkernel/intern/pbvh_bmesh.cc b/source/blender/blenkernel/intern/pbvh_bmesh.cc index cddec615acb..7f419becdaa 100644 --- a/source/blender/blenkernel/intern/pbvh_bmesh.cc +++ b/source/blender/blenkernel/intern/pbvh_bmesh.cc @@ -2269,7 +2269,7 @@ void update_vert_boundary_bmesh(int cd_faceset_offset, } if (!ELEM(sharpcount, 0, 2)) { - //boundflag |= SCULPT_CORNER_SHARP_MARK; + // boundflag |= SCULPT_CORNER_SHARP_MARK; } if (seamcount == 1) { @@ -2554,7 +2554,6 @@ bool remesh_topology_nodes(blender::bke::dyntopo::BrushTester *brush_tester, bool updatePBVH, DyntopoMaskCB mask_cb, void *mask_cb_data, - int edge_limit_multiply, float quality) { bool modified = false; @@ -2588,7 +2587,6 @@ bool remesh_topology_nodes(blender::bke::dyntopo::BrushTester *brush_tester, updatePBVH, mask_cb, mask_cb_data, - edge_limit_multiply, quality); return modified; diff --git a/source/blender/editors/sculpt_paint/sculpt.cc b/source/blender/editors/sculpt_paint/sculpt.cc index 96bcaf5d588..8effd53b60f 100644 --- a/source/blender/editors/sculpt_paint/sculpt.cc +++ b/source/blender/editors/sculpt_paint/sculpt.cc @@ -3968,9 +3968,6 @@ static void sculpt_topology_update(Sculpt *sd, // mode |= PBVH_Collapse | PBVH_Subdivide; } - /* XXX we limit by time instead of edge queue steps now. */ - int edge_multiply = 50; // 1 + int(powf(ss->cached_dyntopo.quality, 3.0f) * 50.0f); - SculptSearchSphereData sdata{}; sdata.ss = ss, sdata.sd = sd, sdata.ob = ob; sdata.radius_squared = square_f(ss->cache->radius * radius_scale * 1.25f); @@ -4016,7 +4013,6 @@ static void sculpt_topology_update(Sculpt *sd, true, mask_cb, mask_cb_data, - edge_multiply, ss->cached_dyntopo.quality); SCULPT_dyntopo_automasking_end(mask_cb_data); diff --git a/source/blender/editors/sculpt_paint/sculpt_detail.cc b/source/blender/editors/sculpt_paint/sculpt_detail.cc index 5c742195c67..6a9f2b7b1d6 100644 --- a/source/blender/editors/sculpt_paint/sculpt_detail.cc +++ b/source/blender/editors/sculpt_paint/sculpt_detail.cc @@ -141,7 +141,6 @@ static int sculpt_detail_flood_fill_run(Scene *scene, } double time = PIL_check_seconds_timer(); - int edge_step_mul = 1 + int(ss->cached_dyntopo.quality * 100.0f); unlock_main_thread(); @@ -164,6 +163,8 @@ static int sculpt_detail_flood_fill_run(Scene *scene, printf("radius: %.5f", radius); } + double last_flush = PIL_check_seconds_timer(); + int repeat = ss->cached_dyntopo.repeat; for (int i = 0; i < 1 + repeat; i++) { lock_main_thread(); @@ -203,20 +204,23 @@ static int sculpt_detail_flood_fill_run(Scene *scene, float3(0.0f, 0.0f, 1.0f), false, mask_cb, - mask_cb_data, - edge_step_mul); + mask_cb_data); + remesher.surface_smooth_fac = 0.25; remesher.start(); while (!remesher.done()) { remesher.step(); - if (developer_mode && remesher.was_flushed()) { + if (developer_mode && remesher.was_flushed() || + (PIL_check_seconds_timer() - last_flush > 0.5)) { DEG_id_tag_update(&ob->id, ID_RECALC_SHADING); unlock_main_thread(); update_main_thread(); PIL_sleep_ms(1); lock_main_thread(); + + last_flush = PIL_check_seconds_timer(); } if (should_stop()) { @@ -225,6 +229,11 @@ static int sculpt_detail_flood_fill_run(Scene *scene, } remesher.finish(); + /* Push a new subentry. */ + BM_log_entry_add_ex(ss->bm, ss->bm_log, true); + + blender::bke::dyntopo::after_stroke(ss->pbvh, true); + unlock_main_thread(); } @@ -254,6 +263,9 @@ static int sculpt_detail_flood_fill_exec(bContext *C, wmOperator *op) SCULPT_undo_push_begin(ob, op); SCULPT_undo_push_node(ob, nullptr, SCULPT_UNDO_COORDS); + double time = PIL_check_seconds_timer(); + auto should_stop = [&time]() { return PIL_check_seconds_timer() - time > 1.0f; }; + int ret = sculpt_detail_flood_fill_run( CTX_data_scene(C), CTX_data_active_object(C), @@ -264,7 +276,7 @@ static int sculpt_detail_flood_fill_exec(bContext *C, wmOperator *op) []() {}, []() {}, []() {}, - []() { return false; }); + should_stop); WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob); SCULPT_undo_push_end(ob); -- 2.30.2 From cd4aafc47103f41e87a517bbc3f49a09756b8879 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Fri, 16 Jun 2023 19:27:45 -0700 Subject: [PATCH 188/279] temp-sculpt-dyntopo: Fix box/lasso trim --- source/blender/blenkernel/intern/dyntopo.cc | 8 ++++++++ source/blender/blenkernel/intern/pbvh_bmesh.cc | 1 + source/blender/editors/sculpt_paint/paint_mask.cc | 2 +- 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/source/blender/blenkernel/intern/dyntopo.cc b/source/blender/blenkernel/intern/dyntopo.cc index 28a8ea9e0fc..d20a07354a7 100644 --- a/source/blender/blenkernel/intern/dyntopo.cc +++ b/source/blender/blenkernel/intern/dyntopo.cc @@ -2635,6 +2635,14 @@ void EdgeQueueContext::split_edge(BMEdge *e) insert_edge(newl->e, w); } } + else { + float w = 0.0f; + PBVHTopologyUpdateMode mode = edge_queue_test(this, pbvh, newl->e, &w); + + if (mode == PBVH_Collapse) { + insert_edge(newl->e, w); + } + } } pbvh_bmesh_check_nodes(pbvh); diff --git a/source/blender/blenkernel/intern/pbvh_bmesh.cc b/source/blender/blenkernel/intern/pbvh_bmesh.cc index 7f419becdaa..3334b100bc8 100644 --- a/source/blender/blenkernel/intern/pbvh_bmesh.cc +++ b/source/blender/blenkernel/intern/pbvh_bmesh.cc @@ -2366,6 +2366,7 @@ void BKE_pbvh_build_bmesh(PBVH *pbvh, const int cd_origno) { pbvh->bm_idmap = idmap; + pbvh->header.bm = bm; pbvh->cd_face_area = cd_face_areas; pbvh->cd_vert_node_offset = cd_vert_node_offset; diff --git a/source/blender/editors/sculpt_paint/paint_mask.cc b/source/blender/editors/sculpt_paint/paint_mask.cc index 13120eaa3e2..a0f64d7c19b 100644 --- a/source/blender/editors/sculpt_paint/paint_mask.cc +++ b/source/blender/editors/sculpt_paint/paint_mask.cc @@ -1530,7 +1530,7 @@ static void sculpt_gesture_apply_trim(SculptGestureContext *sgcontext) if (sgcontext->ss && sgcontext->ss->bm) { /* Rebuild pbvh. */ BKE_pbvh_free(sgcontext->ss->pbvh); sgcontext->ss->pbvh = BKE_pbvh_new(PBVH_BMESH); - + BKE_pbvh_build_bmesh(sgcontext->ss->pbvh, sculpt_mesh, sgcontext->ss->bm, -- 2.30.2 From 7986eaa20c84015f14d4665d1211fd3863429c1b Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Sun, 18 Jun 2023 04:09:06 -0700 Subject: [PATCH 189/279] temp-sculpt-dyntopo: Fix performance problem and a few minor bugs --- scripts/startup/bl_ui/space_view3d.py | 4 ++-- source/blender/blenkernel/intern/dyntopo.cc | 12 ++++++------ source/blender/blenkernel/intern/dyntopo_intern.hh | 2 +- source/blender/bmesh/intern/bmesh_operators.c | 5 ++++- .../blender/editors/sculpt_paint/sculpt_face_set.cc | 2 +- 5 files changed, 14 insertions(+), 11 deletions(-) diff --git a/scripts/startup/bl_ui/space_view3d.py b/scripts/startup/bl_ui/space_view3d.py index 1ad6bdc3128..2acf8e437ed 100644 --- a/scripts/startup/bl_ui/space_view3d.py +++ b/scripts/startup/bl_ui/space_view3d.py @@ -3592,8 +3592,8 @@ class VIEW3D_MT_face_sets_init(Menu): props = layout.operator("sculpt.face_sets_init", text="By Sharp Edges") props.mode = 'SHARP_EDGES' - props = layout.operator("sculpt.face_sets_init", text="By Face Maps") - props.mode = 'FACE_MAPS' + #props = layout.operator("sculpt.face_sets_init", text="By Face Maps") + #props.mode = 'FACE_MAPS' class VIEW3D_MT_random_mask(Menu): diff --git a/source/blender/blenkernel/intern/dyntopo.cc b/source/blender/blenkernel/intern/dyntopo.cc index d20a07354a7..f959d2a6f64 100644 --- a/source/blender/blenkernel/intern/dyntopo.cc +++ b/source/blender/blenkernel/intern/dyntopo.cc @@ -79,10 +79,6 @@ static void surface_smooth_v_safe( float tan[3]; float tot = 0.0; - if (BM_ELEM_CD_GET_INT(v, pbvh->cd_valence) > 12) { - return; - } - PBVH_CHECK_NAN(v->co); Vector loops; @@ -1987,7 +1983,7 @@ static bool do_cleanup_3_4(EdgeQueueContext *eq_ctx, PBVH *pbvh) bool ok = eq_ctx->brush_tester->vert_in_range(v); - if (!ok) { + if (!ok && v->e) { /* Check if any surrounding vertex is in range. */ BMEdge *e = v->e; do { @@ -2125,8 +2121,12 @@ void EdgeQueueContext::start() } } -bool EdgeQueueContext::done() +ATTR_NO_OPT bool EdgeQueueContext::done() { + if (edge_heap.min_weight() > limit_len_min_sqr && edge_heap.max_weight() < limit_len_max_sqr) { + return true; + } + return totop == 0 || edge_heap.empty(); } diff --git a/source/blender/blenkernel/intern/dyntopo_intern.hh b/source/blender/blenkernel/intern/dyntopo_intern.hh index c31e933cc2f..eaaca617495 100644 --- a/source/blender/blenkernel/intern/dyntopo_intern.hh +++ b/source/blender/blenkernel/intern/dyntopo_intern.hh @@ -143,7 +143,7 @@ inline bool bm_elem_is_free(BMElem *elem, int htype) * applied stochastically (by skipping verts randomly) to improve * performance. */ -#define DYNTOPO_SAFE_SMOOTH_FAC 0.05f +#define DYNTOPO_SAFE_SMOOTH_FAC 0.1f #ifdef USE_EDGEQUEUE_EVEN_SUBDIV # include "BKE_global.h" diff --git a/source/blender/bmesh/intern/bmesh_operators.c b/source/blender/bmesh/intern/bmesh_operators.c index dd50af6a557..b7c80f01624 100644 --- a/source/blender/bmesh/intern/bmesh_operators.c +++ b/source/blender/bmesh/intern/bmesh_operators.c @@ -1216,7 +1216,10 @@ static void bmo_flag_layer_alloc_do(BMesh *bm, short *oldflags = flags->flag; flags->flag = BLI_mempool_calloc(newpool); - memcpy(flags->flag, oldflags, old_totflags_size); + if (oldflags) { + memcpy(flags->flag, oldflags, old_totflags_size); + } + BM_elem_index_set(elem, i); /* set_inline */ BM_ELEM_API_FLAG_CLEAR((BMElemF *)elem); } diff --git a/source/blender/editors/sculpt_paint/sculpt_face_set.cc b/source/blender/editors/sculpt_paint/sculpt_face_set.cc index 5d317868491..9750ebef42c 100644 --- a/source/blender/editors/sculpt_paint/sculpt_face_set.cc +++ b/source/blender/editors/sculpt_paint/sculpt_face_set.cc @@ -1142,8 +1142,8 @@ void SCULPT_face_mark_boundary_update(SculptSession *ss, PBVHFaceRef face) for (int vert_i : ss->corner_verts.slice(ss->polys[face.i])) { PBVHVertRef vertex = {vert_i}; BKE_sculpt_boundary_flag_update(ss, vertex); - break; } + break; case PBVH_GRIDS: { const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh); int grid_index = BKE_subdiv_ccg_start_face_grid_index_get(ss->subdiv_ccg)[face.i]; -- 2.30.2 From 3a7b4cb0ffcfd6b91a58029a4b871cac0ef0c68d Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Mon, 19 Jun 2023 05:06:42 -0700 Subject: [PATCH 190/279] temp-sculpt-dyntopo: Split GPU normals of marked sharp edges TODO: use autosmooth settings to split by face angles too. --- source/blender/blenkernel/intern/dyntopo.cc | 2 +- source/blender/blenkernel/intern/paint.cc | 10 ++-- .../blender/blenkernel/intern/pbvh_bmesh.cc | 53 +++++++++++++---- source/blender/draw/intern/draw_pbvh.cc | 59 ++++++++++++++++++- .../editors/sculpt_paint/paint_mask.cc | 7 ++- 5 files changed, 112 insertions(+), 19 deletions(-) diff --git a/source/blender/blenkernel/intern/dyntopo.cc b/source/blender/blenkernel/intern/dyntopo.cc index f959d2a6f64..f041b084d0a 100644 --- a/source/blender/blenkernel/intern/dyntopo.cc +++ b/source/blender/blenkernel/intern/dyntopo.cc @@ -2121,7 +2121,7 @@ void EdgeQueueContext::start() } } -ATTR_NO_OPT bool EdgeQueueContext::done() +bool EdgeQueueContext::done() { if (edge_heap.min_weight() > limit_len_min_sqr && edge_heap.max_weight() < limit_len_max_sqr) { return true; diff --git a/source/blender/blenkernel/intern/paint.cc b/source/blender/blenkernel/intern/paint.cc index 48e58bd0ce5..dd47491d65e 100644 --- a/source/blender/blenkernel/intern/paint.cc +++ b/source/blender/blenkernel/intern/paint.cc @@ -2684,16 +2684,19 @@ void BKE_sculpt_sync_face_visibility_to_grids(Mesh *mesh, SubdivCCG *subdiv_ccg) static PBVH *build_pbvh_for_dynamic_topology(Object *ob, bool /*update_sculptverts*/) { PBVH *pbvh = ob->sculpt->pbvh = BKE_pbvh_new(PBVH_BMESH); - BKE_pbvh_set_bmesh(pbvh, ob->sculpt->bm); - sculptsession_bmesh_add_layers(ob); + BKE_pbvh_set_bmesh(pbvh, ob->sculpt->bm); BM_mesh_elem_table_ensure(ob->sculpt->bm, BM_VERT | BM_EDGE | BM_FACE); - BKE_sculptsession_update_attr_refs(ob); + sculptsession_bmesh_add_layers(ob); sculpt_boundary_flags_ensure(ob, pbvh, ob->sculpt->bm->totvert, ob->sculpt->bm->totedge); + BKE_sculpt_ensure_sculpt_layers(ob); + + BKE_sculptsession_update_attr_refs(ob); sculpt_check_face_areas(ob, pbvh); BKE_sculpt_ensure_idmap(ob); + blender::bke::pbvh::sharp_limit_set(pbvh, ob->sculpt->sharp_angle_limit); BKE_pbvh_build_bmesh(pbvh, BKE_object_get_original_mesh(ob), @@ -2714,7 +2717,6 @@ static PBVH *build_pbvh_for_dynamic_topology(Object *ob, bool /*update_sculptver if (ob->sculpt->bm_log) { BKE_pbvh_set_bm_log(pbvh, ob->sculpt->bm_log); } - blender::bke::pbvh::sharp_limit_set(pbvh, ob->sculpt->sharp_angle_limit); return pbvh; } diff --git a/source/blender/blenkernel/intern/pbvh_bmesh.cc b/source/blender/blenkernel/intern/pbvh_bmesh.cc index 3334b100bc8..4352cfe2563 100644 --- a/source/blender/blenkernel/intern/pbvh_bmesh.cc +++ b/source/blender/blenkernel/intern/pbvh_bmesh.cc @@ -30,6 +30,7 @@ Topology rake: #include "MEM_guardedalloc.h" #include "BLI_alloca.h" +#include "BLI_asan.h" #include "BLI_buffer.h" #include "BLI_ghash.h" #include "BLI_hash.h" @@ -2360,8 +2361,8 @@ void BKE_pbvh_build_bmesh(PBVH *pbvh, const int cd_face_areas, const int cd_boundary_flag, const int cd_edge_boundary, - const int /*cd_flag_offset*/, - const int /*cd_valence_offset*/, + const int cd_flag_offset, + const int cd_valence_offset, const int cd_origco, const int cd_origno) { @@ -2377,10 +2378,8 @@ void BKE_pbvh_build_bmesh(PBVH *pbvh, pbvh->cd_origco = cd_origco; pbvh->cd_origno = cd_origno; - pbvh->cd_flag = CustomData_get_offset_named( - &pbvh->header.bm->vdata, CD_PROP_INT8, ".sculpt_flags"); - pbvh->cd_valence = CustomData_get_offset_named( - &pbvh->header.bm->vdata, CD_PROP_INT8, ".sculpt_valence"); + pbvh->cd_flag = cd_flag_offset; + pbvh->cd_valence = cd_valence_offset; pbvh->mesh = me; @@ -2509,6 +2508,20 @@ void BKE_pbvh_build_bmesh(PBVH *pbvh, pbvh_print_mem_size(pbvh); + /* Update boundary flags. */ + BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) { + blender::bke::pbvh::update_vert_boundary_bmesh(pbvh->cd_faceset_offset, + pbvh->cd_vert_node_offset, + pbvh->cd_face_node_offset, + -1, + pbvh->cd_boundary_flag, + pbvh->cd_flag, + pbvh->cd_valence, + v, + &bm->ldata, + pbvh->sharp_angle_limit); + } + /* update face areas */ const int cd_face_area = pbvh->cd_face_area; for (int i = 0; i < pbvh->totnode; i++) { @@ -2666,7 +2679,8 @@ static void pbvh_init_tribuf(PBVHNode * /*node*/, PBVHTriBuf *tribuf) tribuf->loops.reserve(512); } -static uintptr_t tri_loopkey(BMLoop *l, int mat_nr, int cd_fset, int cd_uvs[], int totuv) +static uintptr_t tri_loopkey( + BMLoop *l, int mat_nr, int cd_fset, int cd_uvs[], int totuv, int cd_vert_boundary) { uintptr_t key = ((uintptr_t)l->v) << 12ULL; int i = 0; @@ -2678,6 +2692,14 @@ static uintptr_t tri_loopkey(BMLoop *l, int mat_nr, int cd_fset, int cd_uvs[], i key ^= (uintptr_t)BLI_hash_int(BM_ELEM_CD_GET_INT(l->f, cd_fset) + i++); } + /* Split by sharp flags.*/ + bool sharp = BM_ELEM_CD_GET_INT(l->v, cd_vert_boundary) & SCULPT_BOUNDARY_SHARP_MARK; + if (sharp) { + key ^= (uintptr_t)l; + + return key; + } + for (int i = 0; i < totuv; i++) { float *luv = BM_ELEM_CD_PTR(l, cd_uvs[i]); float snap = 4196.0f; @@ -2738,16 +2760,24 @@ bool BKE_pbvh_bmesh_check_tris(PBVH *pbvh, PBVHNode *node) const int edgeflag = BM_ELEM_TAG_ALT; float min[3], max[3]; - INIT_MINMAX(min, max); - auto add_tri_verts = [cd_uvs, totuv, pbvh, &min, &max]( + int ni = int(node - pbvh->nodes); + + auto add_tri_verts = [cd_uvs, totuv, pbvh, &min, &max, ni]( PBVHTriBuf *tribuf, PBVHTri &tri, BMLoop *l, int mat_nr, int j) { int tri_v; if ((l->f->head.hflag & BM_ELEM_SMOOTH)) { + /* This is called in threads, so updating boundary flags can be tricky. */ + if (BM_ELEM_CD_GET_INT(l->v, pbvh->cd_boundary_flag) & SCULPT_BOUNDARY_NEEDS_UPDATE) { + if (BM_ELEM_CD_GET_INT(l->v, pbvh->cd_vert_node_offset) == ni) { + pbvh_check_vert_boundary_bmesh(pbvh, l->v); + } + } + void *loopkey = reinterpret_cast( - tri_loopkey(l, mat_nr, pbvh->cd_faceset_offset, cd_uvs, totuv)); + tri_loopkey(l, mat_nr, pbvh->cd_faceset_offset, cd_uvs, totuv, pbvh->cd_boundary_flag)); tri_v = tribuf->vertmap.lookup_or_add(loopkey, tribuf->verts.size()); } @@ -2841,7 +2871,8 @@ bool BKE_pbvh_bmesh_check_tris(PBVH *pbvh, PBVHNode *node) } /* void *loopkey = reinterpret_cast( - tri_loopkey(l, mat_nr, pbvh->cd_faceset_offset, cd_uvs, totuv)); + tri_loopkey(l, mat_nr, pbvh->cd_faceset_offset, cd_uvs, totuv, + pbvh->cd_boundary_flag)); */ bm->elem_index_dirty |= BM_VERT; diff --git a/source/blender/draw/intern/draw_pbvh.cc b/source/blender/draw/intern/draw_pbvh.cc index f0a677660cf..8e14003e5cc 100644 --- a/source/blender/draw/intern/draw_pbvh.cc +++ b/source/blender/draw/intern/draw_pbvh.cc @@ -953,7 +953,61 @@ struct PBVHBatches { short no[3]; bool smooth = BM_elem_flag_test(l->f, BM_ELEM_SMOOTH); - normal_float_to_short_v3(no, smooth ? l->v->no : l->f->no); + if (!smooth) { + normal_float_to_short_v3(no, l->f->no); + *static_cast(GPU_vertbuf_raw_step(&access)) = no; + return; + } + + /* Deal with normal splitting. We do not (yet) implement + * the full autosmoothing functionality here, we just check + * for marked sharp edges. + */ + float3 fno = {}; + + /* Find start of normal span. */ + BMLoop *l2 = l; + do { + l2 = l2->prev; + + if (l2->radial_next == l2 || !(l2->e->head.hflag & BM_ELEM_SMOOTH)) { + break; + } + + l2 = l2->radial_next; + + if (l2->v != l->v) { + l2 = l2->next->next; + } + } while (l2 != l); + + /* No split edges. */ + if (l2 == l) { + normal_float_to_short_v3(no, l->v->no); + *static_cast(GPU_vertbuf_raw_step(&access)) = no; + return; + } + + /* Iterate over normal span */ + int i = 0; + while (1) { + fno += l2->f->no; + + if (i++ > 1000) { + printf("%s: infinite loop error.\n"); + break; + } + + l2 = l2->v == l->v ? l2->prev : l2->next; + l2 = l2->radial_next; + + if (l2 == l2->radial_next || !(l2->e->head.hflag & BM_ELEM_SMOOTH)) { + break; + } + } + normalize_v3(fno); + + normal_float_to_short_v3(no, fno); *static_cast(GPU_vertbuf_raw_step(&access)) = no; }); } @@ -1277,7 +1331,8 @@ struct PBVHBatches { void create_index_bmesh_faces(PBVH_GPU_Args *args) { GPUIndexBufBuilder elb_tris; - GPU_indexbuf_init(&elb_tris, GPU_PRIM_TRIS, args->tribuf->tris.size(), args->tribuf->verts.size()); + GPU_indexbuf_init( + &elb_tris, GPU_PRIM_TRIS, args->tribuf->tris.size(), args->tribuf->verts.size()); needs_tri_index = true; for (int i = 0; i < args->tribuf->tris.size(); i++) { diff --git a/source/blender/editors/sculpt_paint/paint_mask.cc b/source/blender/editors/sculpt_paint/paint_mask.cc index a0f64d7c19b..9053206e768 100644 --- a/source/blender/editors/sculpt_paint/paint_mask.cc +++ b/source/blender/editors/sculpt_paint/paint_mask.cc @@ -1529,8 +1529,13 @@ static void sculpt_gesture_apply_trim(SculptGestureContext *sgcontext) if (sgcontext->ss && sgcontext->ss->bm) { /* Rebuild pbvh. */ BKE_pbvh_free(sgcontext->ss->pbvh); + sgcontext->ss->pbvh = BKE_pbvh_new(PBVH_BMESH); - + BKE_pbvh_set_bmesh(sgcontext->ss->pbvh, sgcontext->ss->bm); + BKE_sculpt_ensure_sculpt_layers(sgcontext->vc.obact); + + blender::bke::pbvh::sharp_limit_set(sgcontext->ss->pbvh, sgcontext->ss->sharp_angle_limit); + BKE_pbvh_build_bmesh(sgcontext->ss->pbvh, sculpt_mesh, sgcontext->ss->bm, -- 2.30.2 From cfe14725961ce6e7c112efbdf9fe9f48a0b6fed9 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Mon, 19 Jun 2023 05:11:28 -0700 Subject: [PATCH 191/279] temp-sculpt-dyntopo: Fix bug with seam and marked sharp corner flags --- source/blender/blenkernel/intern/pbvh_bmesh.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/source/blender/blenkernel/intern/pbvh_bmesh.cc b/source/blender/blenkernel/intern/pbvh_bmesh.cc index 4352cfe2563..058252e601e 100644 --- a/source/blender/blenkernel/intern/pbvh_bmesh.cc +++ b/source/blender/blenkernel/intern/pbvh_bmesh.cc @@ -2269,11 +2269,11 @@ void update_vert_boundary_bmesh(int cd_faceset_offset, boundflag |= SCULPT_CORNER_SHARP_ANGLE; } - if (!ELEM(sharpcount, 0, 2)) { - // boundflag |= SCULPT_CORNER_SHARP_MARK; + if (sharpcount > 2) { + boundflag |= SCULPT_CORNER_SHARP_MARK; } - if (seamcount == 1) { + if (seamcount > 2) { boundflag |= SCULPT_CORNER_SEAM; } -- 2.30.2 From c29549339b0b4f571227ef880bb59e51f64b48cb Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Mon, 19 Jun 2023 05:45:24 -0700 Subject: [PATCH 192/279] temp-sculpt-dyntopo: Fix edge boundary flag update bug Fixes broken face set boundary smoothing. --- source/blender/blenkernel/BKE_paint.h | 52 ++++++++++++++++++- source/blender/blenkernel/intern/paint.cc | 2 - .../editors/sculpt_paint/paint_mask.cc | 3 ++ .../editors/sculpt_paint/sculpt_face_set.cc | 12 ++--- 4 files changed, 60 insertions(+), 9 deletions(-) diff --git a/source/blender/blenkernel/BKE_paint.h b/source/blender/blenkernel/BKE_paint.h index d038d394202..82d6bed1867 100644 --- a/source/blender/blenkernel/BKE_paint.h +++ b/source/blender/blenkernel/BKE_paint.h @@ -1205,12 +1205,62 @@ void load_all_original(Object *ob); } // namespace blender::bke::paint template -inline void BKE_sculpt_boundary_flag_update(SculptSession *ss, PBVHElemRef elem) +inline void BKE_sculpt_boundary_flag_update(SculptSession *ss, + PBVHElemRef elem, + bool flag_vert_edges = false) { int *flags; if constexpr (std::is_same_v) { + PBVHVertRef vertex = {elem.i}; flags = blender::bke::paint::vertex_attr_ptr(elem, ss->attrs.boundary_flags); + + if (flag_vert_edges) { + switch (BKE_pbvh_type(ss->pbvh)) { + case PBVH_BMESH: { + BMVert *v = reinterpret_cast(vertex.i); + if (!v->e) { + break; + } + + BMEdge *e = v->e; + do { + PBVHEdgeRef edge = {reinterpret_cast(e)}; + *blender::bke::paint::edge_attr_ptr( + edge, ss->attrs.edge_boundary_flags) |= SCULPT_BOUNDARY_NEEDS_UPDATE | + SCULPT_BOUNDARY_UPDATE_SHARP_ANGLE; + } while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e); + + break; + } + case PBVH_FACES: + /* If we have a vertex to edge map use it. */ + if (!ss->vemap.is_empty()) { + for (int edge_i : ss->vemap[vertex.i]) { + *blender::bke::paint::edge_attr_ptr( + {edge_i}, ss->attrs.edge_boundary_flags) |= SCULPT_BOUNDARY_NEEDS_UPDATE | + SCULPT_BOUNDARY_UPDATE_SHARP_ANGLE; + } + } + else { /* Otherwise use vertex to poly map. */ + for (int poly_i : ss->pmap[vertex.i]) { + for (int loop_i : ss->polys[poly_i]) { + if (ss->corner_verts[loop_i] == vertex.i) { + int edge_i = ss->corner_edges[loop_i]; + *blender::bke::paint::edge_attr_ptr( + {edge_i}, + ss->attrs.edge_boundary_flags) |= SCULPT_BOUNDARY_NEEDS_UPDATE | + SCULPT_BOUNDARY_UPDATE_SHARP_ANGLE; + } + } + } + } + break; + case PBVH_GRIDS: + /* Not supported. */ + break; + } + } } else { flags = blender::bke::paint::edge_attr_ptr(elem, ss->attrs.edge_boundary_flags); diff --git a/source/blender/blenkernel/intern/paint.cc b/source/blender/blenkernel/intern/paint.cc index dd47491d65e..b37b4dbf80a 100644 --- a/source/blender/blenkernel/intern/paint.cc +++ b/source/blender/blenkernel/intern/paint.cc @@ -2435,7 +2435,6 @@ int *BKE_sculpt_face_sets_ensure(Object *ob) SculptSession *ss = ob->sculpt; if (!ss->attrs.face_set) { - Mesh *mesh = static_cast(ob->data); SculptAttributeParams params = {}; params.permanent = true; @@ -2861,7 +2860,6 @@ PBVH *BKE_sculpt_object_pbvh_ensure(Depsgraph *depsgraph, Object *ob) } SculptSession *ss = ob->sculpt; - Scene *scene = DEG_get_input_scene(depsgraph); PBVH *pbvh = ss->pbvh; if (pbvh != nullptr) { diff --git a/source/blender/editors/sculpt_paint/paint_mask.cc b/source/blender/editors/sculpt_paint/paint_mask.cc index 9053206e768..e92b2e48532 100644 --- a/source/blender/editors/sculpt_paint/paint_mask.cc +++ b/source/blender/editors/sculpt_paint/paint_mask.cc @@ -760,6 +760,9 @@ static void face_set_gesture_apply_task_cb(void *__restrict userdata, continue; } SCULPT_face_set_set(sgcontext->ss, fd.face, face_set_operation->new_face_set_id); + for (int i : IndexRange(fd.verts_num)) { + BKE_sculpt_boundary_flag_update(sgcontext->ss, fd.verts[i], true); + } any_updated = true; } } diff --git a/source/blender/editors/sculpt_paint/sculpt_face_set.cc b/source/blender/editors/sculpt_paint/sculpt_face_set.cc index 9750ebef42c..2f8345d10cd 100644 --- a/source/blender/editors/sculpt_paint/sculpt_face_set.cc +++ b/source/blender/editors/sculpt_paint/sculpt_face_set.cc @@ -178,7 +178,7 @@ static void do_draw_face_sets_brush_task_cb_ex(void *__restrict userdata, continue; } - /* Face set automasking in inverted draw mode is tricky,, we have + /* Face set automasking in inverted draw mode is tricky, we have * to sample the automasking face set after the stroke has started. */ if (set_active_faceset && @@ -220,7 +220,7 @@ static void do_draw_face_sets_brush_task_cb_ex(void *__restrict userdata, if (fade > 0.05f) { for (int i = 0; i < fd.verts_num; i++) { - BKE_sculpt_boundary_flag_update(ss, fd.verts[i]); + BKE_sculpt_boundary_flag_update(ss, fd.verts[i], true); } *fd.face_set = ss->cache->paint_face_set; @@ -785,7 +785,7 @@ static int sculpt_face_set_init_exec(bContext *C, wmOperator *op) int verts_num = SCULPT_vertex_count_get(ob->sculpt); for (int i : IndexRange(verts_num)) { - BKE_sculpt_boundary_flag_update(ob->sculpt, BKE_pbvh_index_to_vertex(ss->pbvh, i)); + BKE_sculpt_boundary_flag_update(ob->sculpt, BKE_pbvh_index_to_vertex(ss->pbvh, i), true); } /* Sync face sets visibility and vertex visibility as now all Face Sets are visible. */ @@ -1133,7 +1133,7 @@ void SCULPT_face_mark_boundary_update(SculptSession *ss, PBVHFaceRef face) BMLoop *l = f->l_first; do { PBVHVertRef vertex = {reinterpret_cast(l->v)}; - BKE_sculpt_boundary_flag_update(ss, vertex); + BKE_sculpt_boundary_flag_update(ss, vertex, true); } while ((l = l->next) != f->l_first); break; @@ -1141,7 +1141,7 @@ void SCULPT_face_mark_boundary_update(SculptSession *ss, PBVHFaceRef face) case PBVH_FACES: { for (int vert_i : ss->corner_verts.slice(ss->polys[face.i])) { PBVHVertRef vertex = {vert_i}; - BKE_sculpt_boundary_flag_update(ss, vertex); + BKE_sculpt_boundary_flag_update(ss, vertex, true); } break; case PBVH_GRIDS: { @@ -1151,7 +1151,7 @@ void SCULPT_face_mark_boundary_update(SculptSession *ss, PBVHFaceRef face) int verts_num = ss->polys[face.i].size() * key->grid_area; for (int i = 0; i < verts_num; i++, vertex_i++) { - BKE_sculpt_boundary_flag_update(ss, {vertex_i}); + BKE_sculpt_boundary_flag_update(ss, {vertex_i}, true); } break; -- 2.30.2 From 1d8a19141d6164e9ad793348c98616c6795f577f Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Wed, 21 Jun 2023 05:24:21 -0700 Subject: [PATCH 193/279] temp-sculpt-dyntopo: Fix imporper handling of sharp flags in collapse --- source/blender/bmesh/intern/bmesh_collapse.hh | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/source/blender/bmesh/intern/bmesh_collapse.hh b/source/blender/bmesh/intern/bmesh_collapse.hh index 4ac0501f768..7418faf4704 100644 --- a/source/blender/bmesh/intern/bmesh_collapse.hh +++ b/source/blender/bmesh/intern/bmesh_collapse.hh @@ -614,7 +614,14 @@ BMVert *join_vert_kill_edge(BMesh *bm, callbacks.on_edge_combine(exist, e); if (combine_flags) { + /* The sharp flag is inverted so we can't just OR it. */ + bool sharp = !(exist->head.hflag & BM_ELEM_SMOOTH) || !(e->head.hflag & BM_ELEM_SMOOTH); + exist->head.hflag |= e->head.hflag; + + if (sharp) { + exist->head.hflag &= ~BM_ELEM_SMOOTH; + } } callbacks.on_edge_kill(e); -- 2.30.2 From e037eab4b9bd01bb778eea0f7100ae191890f8ba Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Thu, 22 Jun 2023 23:39:28 -0700 Subject: [PATCH 194/279] temp-sculpt-dyntopo: Fix a few minor bugs * Only run displacement erase/smear with PBVH_GRIDS * Fix mesh filter undo bug --- source/blender/blenkernel/BKE_pbvh.h | 1 + .../blender/blenkernel/intern/customdata.cc | 146 +++++++----------- source/blender/blenkernel/intern/pbvh.cc | 25 ++- .../blender/blenkernel/intern/pbvh_bmesh.cc | 1 + source/blender/editors/sculpt_paint/sculpt.cc | 4 + .../sculpt_paint/sculpt_brush_types.cc | 8 + .../sculpt_paint/sculpt_filter_mesh.cc | 2 +- 7 files changed, 90 insertions(+), 97 deletions(-) diff --git a/source/blender/blenkernel/BKE_pbvh.h b/source/blender/blenkernel/BKE_pbvh.h index 70178de55b5..fb4806ac6de 100644 --- a/source/blender/blenkernel/BKE_pbvh.h +++ b/source/blender/blenkernel/BKE_pbvh.h @@ -1117,5 +1117,6 @@ float test_sharp_faces_mesh(int f1, blender::Span get_poly_normals(const PBVH *pbvh); void set_vert_boundary_map(PBVH *pbvh, BLI_bitmap *vert_boundary_map); +void on_stroke_start(PBVH *pbvh); } // namespace blender::bke::pbvh #endif diff --git a/source/blender/blenkernel/intern/customdata.cc b/source/blender/blenkernel/intern/customdata.cc index 5ea608def77..80d0e6a41ba 100644 --- a/source/blender/blenkernel/intern/customdata.cc +++ b/source/blender/blenkernel/intern/customdata.cc @@ -69,6 +69,8 @@ /* only for customdata_data_transfer_interp_normal_normals */ #include "data_transfer_intern.h" +#include + using blender::Array; using blender::float2; using blender::ImplicitSharingInfo; @@ -2713,105 +2715,78 @@ void CustomData_free_typemask(CustomData *data, const int totelem, eCustomDataMa CustomData_reset(data); } +static int customData_get_alignment(eCustomDataType type) +{ + /* Handle array types. */ + if (ELEM(type, + CD_PROP_FLOAT2, + CD_PROP_FLOAT3, + CD_PROP_QUATERNION, + CD_PROP_COLOR, + CD_NORMAL, + CD_TANGENT, + CD_SHAPEKEY, + CD_ORIGSPACE_MLOOP, + CD_PROP_INT32_2D)) + { + return 4; + } + + if (ELEM(type, CD_TESSLOOPNORMAL)) { + return 2; + } + + if (type == CD_PROP_BYTE_COLOR) { + return 1; + } + + /* Derive the alignment from the element size. */ + int size = CustomData_sizeof(type); + + if (size >= 8) { + return 8; + } + if (size >= 4) { + return 4; + } + if (size >= 2) { + return 2; + } + + return 1; +} + +/* Update BMesh block offsets, respects alignment. */ static void customData_update_offsets(CustomData *data) { - const LayerTypeInfo *typeInfo; - int offset = 0; - if (data->totlayer == 0) { data->totsize = 0; CustomData_update_typemap(data); return; } - /* Sort by alignment. */ - int aligns[] = {8, 4, 2, 1}; - Array donemap(data->totlayer, false); - Array alignmap(data->totlayer); - + const std::array aligns = {8, 4, 2, 1}; int max_alignment = 1; - /* Do large structs first. */ - for (int j = 0; j < data->totlayer; j++) { - CustomDataLayer *layer = data->layers + j; + int offset = 0; + for (const int align : aligns) { + for (const int i : IndexRange(data->totlayer)) { + CustomDataLayer *layer = data->layers + i; - typeInfo = layerType_getInfo(eCustomDataType(layer->type)); - int size = (int)typeInfo->size; - - int alignment; - - /* Float vectors get 4-byte alignment. */ - if (ELEM(layer->type, - CD_PROP_COLOR, - CD_PROP_FLOAT2, - CD_PROP_FLOAT3, - CD_PROP_INT32_2D, - CD_CUSTOMLOOPNORMAL, - CD_NORMAL)) - { - alignment = 4; - } - else if (size > 4) { - alignment = 8; - } - else if (size > 2) { - alignment = 4; - } - else if (size > 1) { - alignment = 2; - } - else { - alignment = 1; - } - - max_alignment = max_ii(max_alignment, alignment); - alignmap[j] = alignment; - - /* Detect large structures */ - if (size > 8) { - donemap[j] = true; - - /* Align to 8-byte boundary. */ - if (size & 7) { - size += 8 - (size & 7); - } - - layer->offset = offset; - offset += size; - -#ifdef WITH_ASAN - offset += BM_ASAN_PAD; -#endif - } - } - - for (int i = 0; i < ARRAY_SIZE(aligns) + 1; i++) { - for (int j = 0; j < data->totlayer; j++) { - CustomDataLayer *layer = data->layers + j; - - if (donemap[j]) { + const int layer_align = customData_get_alignment(eCustomDataType(layer->type)); + if (layer_align != align) { continue; } - typeInfo = layerType_getInfo(eCustomDataType(layer->type)); - int size = (int)typeInfo->size; - - if (i < ARRAY_SIZE(aligns) && aligns[i] != alignmap[j]) { - continue; - } - - donemap[j] = true; - - int align2 = aligns[i] - (offset % aligns[i]); - if (align2 != aligns[i]) { - offset += align2; - } - layer->offset = offset; + + int size = CustomData_sizeof(eCustomDataType(layer->type)); + if (size % align != 0) { + size += align - (size % align); + } + offset += size; -#ifdef WITH_ASAN - offset += BM_ASAN_PAD; -#endif + max_alignment = max_ii(max_alignment, align); } } @@ -2819,12 +2794,7 @@ static void customData_update_offsets(CustomData *data) offset += max_alignment - (offset % max_alignment); } -#ifdef WITH_ASAN - offset += BM_ASAN_PAD; -#endif - data->totsize = offset; - CustomData_update_typemap(data); } diff --git a/source/blender/blenkernel/intern/pbvh.cc b/source/blender/blenkernel/intern/pbvh.cc index ba375018697..b2ec4b67d92 100644 --- a/source/blender/blenkernel/intern/pbvh.cc +++ b/source/blender/blenkernel/intern/pbvh.cc @@ -3734,14 +3734,6 @@ const float (*BKE_pbvh_get_vert_normals(const PBVH *pbvh))[3] return pbvh->vert_normals; } -namespace blender::bke::pbvh { -Span get_poly_normals(const PBVH *pbvh) -{ - BLI_assert(pbvh->header.type == PBVH_FACES); - return pbvh->poly_normals; -} -} // namespace blender::bke::pbvh - const bool *BKE_pbvh_get_vert_hide(const PBVH *pbvh) { BLI_assert(pbvh->header.type == PBVH_FACES); @@ -4236,6 +4228,23 @@ void BKE_pbvh_set_symmetry(PBVH *pbvh, int symmetry) } namespace blender::bke::pbvh { + +Span get_poly_normals(const PBVH *pbvh) +{ + BLI_assert(pbvh->header.type == PBVH_FACES); + return pbvh->poly_normals; +} + +void on_stroke_start(PBVH *pbvh) +{ + /* Load current node bounds into original bounds at stroke start.*/ + for (int i : IndexRange(pbvh->totnode)) { + PBVHNode *node = &pbvh->nodes[i]; + + node->orig_vb = node->vb; + } +} + void set_vert_boundary_map(PBVH *pbvh, BLI_bitmap *vert_boundary_map) { pbvh->vert_boundary_map = vert_boundary_map; diff --git a/source/blender/blenkernel/intern/pbvh_bmesh.cc b/source/blender/blenkernel/intern/pbvh_bmesh.cc index 058252e601e..c696d0581f0 100644 --- a/source/blender/blenkernel/intern/pbvh_bmesh.cc +++ b/source/blender/blenkernel/intern/pbvh_bmesh.cc @@ -778,6 +778,7 @@ static void pbvh_bmesh_node_finalize(PBVH *pbvh, BM_ELEM_CD_SET_INT(v, cd_vert_node_offset, node_index); } } + /* Update node bounding box */ BB_expand(&n->vb, v->co); BB_expand(&n->orig_vb, BM_ELEM_CD_PTR(v, pbvh->cd_origco)); diff --git a/source/blender/editors/sculpt_paint/sculpt.cc b/source/blender/editors/sculpt_paint/sculpt.cc index 8effd53b60f..ebd426dffc4 100644 --- a/source/blender/editors/sculpt_paint/sculpt.cc +++ b/source/blender/editors/sculpt_paint/sculpt.cc @@ -6359,6 +6359,10 @@ static bool sculpt_stroke_test_start(bContext *C, wmOperator *op, const float mv SCULPT_stroke_id_next(ob); ss->cache->stroke_id = ss->stroke_id; + if (ss->pbvh) { + blender::bke::pbvh::on_stroke_start(ss->pbvh); + } + SculptCursorGeometryInfo sgi; SCULPT_cursor_geometry_info_update(C, &sgi, mval, false, false); diff --git a/source/blender/editors/sculpt_paint/sculpt_brush_types.cc b/source/blender/editors/sculpt_paint/sculpt_brush_types.cc index 9aee8719e82..9fc6b05db31 100644 --- a/source/blender/editors/sculpt_paint/sculpt_brush_types.cc +++ b/source/blender/editors/sculpt_paint/sculpt_brush_types.cc @@ -2696,6 +2696,10 @@ void SCULPT_do_displacement_eraser_brush(Sculpt *sd, Object *ob, Spanpaint); BKE_curvemapping_init(brush->curve); + if (BKE_pbvh_type(ob->sculpt->pbvh) != PBVH_GRIDS) { + return; + } + /* Threaded loop over nodes. */ SculptThreadedTaskData data{}; data.sd = sd; @@ -2830,6 +2834,10 @@ void SCULPT_do_displacement_smear_brush(Sculpt *sd, Object *ob, Span Brush *brush = BKE_paint_brush(&sd->paint); SculptSession *ss = ob->sculpt; + if (BKE_pbvh_type(ss->pbvh) != PBVH_GRIDS) { + return; + } + BKE_curvemapping_init(brush->curve); const int totvert = SCULPT_vertex_count_get(ss); diff --git a/source/blender/editors/sculpt_paint/sculpt_filter_mesh.cc b/source/blender/editors/sculpt_paint/sculpt_filter_mesh.cc index f8d06d34520..2de00f78678 100644 --- a/source/blender/editors/sculpt_paint/sculpt_filter_mesh.cc +++ b/source/blender/editors/sculpt_paint/sculpt_filter_mesh.cc @@ -153,7 +153,7 @@ void SCULPT_filter_cache_init(bContext *C, data.filter_undo_type = undo_type; TaskParallelSettings settings; - BKE_pbvh_parallel_range_settings(&settings, true, ss->filter_cache->nodes.size()); + BKE_pbvh_parallel_range_settings(&settings, !ss->bm, ss->filter_cache->nodes.size()); BLI_task_parallel_range( 0, ss->filter_cache->nodes.size(), &data, filter_cache_init_task_cb, &settings); -- 2.30.2 From 6e3817ee23c5bed31e9d78bc152e38ae0114e0d3 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Fri, 23 Jun 2023 00:48:28 -0700 Subject: [PATCH 195/279] temp-sculpt-dyntopo: Fix broken vertex and weight paint modes --- source/blender/blenkernel/intern/paint.cc | 3 ++- source/blender/editors/sculpt_paint/paint_vertex.cc | 5 +++++ source/blender/editors/sculpt_paint/sculpt_undo.cc | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/source/blender/blenkernel/intern/paint.cc b/source/blender/blenkernel/intern/paint.cc index b37b4dbf80a..18d37a4aa97 100644 --- a/source/blender/blenkernel/intern/paint.cc +++ b/source/blender/blenkernel/intern/paint.cc @@ -2902,7 +2902,8 @@ PBVH *BKE_sculpt_object_pbvh_ensure(Depsgraph *depsgraph, Object *ob) else { /* Detect if we are loading from an undo memfile step. */ Mesh *mesh_orig = BKE_object_get_original_mesh(ob); - bool is_dyntopo = (mesh_orig->flag & ME_SCULPT_DYNAMIC_TOPOLOGY); + bool is_dyntopo = (mesh_orig->flag & ME_SCULPT_DYNAMIC_TOPOLOGY) && + ss->mode_type == OB_MODE_SCULPT; if (is_dyntopo) { BMesh *bm = BKE_sculptsession_empty_bmesh_create(); diff --git a/source/blender/editors/sculpt_paint/paint_vertex.cc b/source/blender/editors/sculpt_paint/paint_vertex.cc index b5afccbcd56..1c5818a0fbf 100644 --- a/source/blender/editors/sculpt_paint/paint_vertex.cc +++ b/source/blender/editors/sculpt_paint/paint_vertex.cc @@ -1660,6 +1660,11 @@ static void vwpaint_update_cache_invariants( cache->is_last_valid = false; cache->accum = true; + + SCULPT_stroke_id_next(ob); + if (ss->pbvh) { + blender::bke::pbvh::on_stroke_start(ss->pbvh); + } } /* Initialize the stroke cache variants from operator properties */ diff --git a/source/blender/editors/sculpt_paint/sculpt_undo.cc b/source/blender/editors/sculpt_paint/sculpt_undo.cc index 1481d7a1d1b..c2025a9d024 100644 --- a/source/blender/editors/sculpt_paint/sculpt_undo.cc +++ b/source/blender/editors/sculpt_paint/sculpt_undo.cc @@ -2133,7 +2133,7 @@ void SCULPT_undo_ensure_bmlog(Object *ob) UndoSculpt *usculpt = sculpt_undosys_step_get_nodes(us); - if (!ss->bm && !(me->flag & ME_SCULPT_DYNAMIC_TOPOLOGY)) { + if (!ss->bm && (!(me->flag & ME_SCULPT_DYNAMIC_TOPOLOGY) || ss->mode_type != OB_MODE_SCULPT)) { return; } -- 2.30.2 From 1e8603a8789060dbea54f0e23a48e7c944e23d25 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Fri, 23 Jun 2023 02:21:05 -0700 Subject: [PATCH 196/279] temp-sculpt-dyntopo: Remove detail_range parameter Detail_range controls how much edges have to deviate from the goal length before remeshing kicks in. It's a confusing paramter and 0.4f seems to work for most cases. --- scripts/startup/bl_ui/space_view3d_toolbar.py | 7 ------- source/blender/blenkernel/intern/brush.cc | 6 ++---- source/blender/blenkernel/intern/dyntopo.cc | 4 ++-- source/blender/blenkernel/intern/paint.cc | 9 --------- source/blender/blenloader/intern/versioning_300.cc | 4 ---- source/blender/editors/sculpt_paint/sculpt.cc | 6 +++--- source/blender/editors/sculpt_paint/sculpt_api.cc | 2 -- source/blender/editors/sculpt_paint/sculpt_detail.cc | 8 ++------ source/blender/makesdna/DNA_brush_defaults.h | 1 - source/blender/makesdna/DNA_brush_enums.h | 2 ++ source/blender/makesdna/DNA_brush_types.h | 2 +- source/blender/makesdna/DNA_scene_types.h | 3 +-- source/blender/makesrna/intern/rna_brush.c | 9 --------- 13 files changed, 13 insertions(+), 50 deletions(-) diff --git a/scripts/startup/bl_ui/space_view3d_toolbar.py b/scripts/startup/bl_ui/space_view3d_toolbar.py index 1109067a38e..770971f88c6 100644 --- a/scripts/startup/bl_ui/space_view3d_toolbar.py +++ b/scripts/startup/bl_ui/space_view3d_toolbar.py @@ -945,13 +945,6 @@ class VIEW3D_PT_sculpt_dyntopo(Panel, View3DPaintPanel): expand=True ) - UnifiedPaintPanel.prop_unified_dyntopo( - sub, - context, - brush, - "detail_range" - ) - #NotForPR if 0: scene = context.scene diff --git a/source/blender/blenkernel/intern/brush.cc b/source/blender/blenkernel/intern/brush.cc index 4435ee5dcef..28eb33535f7 100644 --- a/source/blender/blenkernel/intern/brush.cc +++ b/source/blender/blenkernel/intern/brush.cc @@ -1885,11 +1885,9 @@ void BKE_brush_sculpt_reset(Brush *br) br->alpha = 1.0f; br->rake_factor = 1.0f; br->dyntopo.inherit = ~(DYNTOPO_INHERIT_SPACING | DYNTOPO_INHERIT_SUBDIVIDE | - DYNTOPO_INHERIT_COLLAPSE | DYNTOPO_INHERIT_DETAIL_RANGE | - DYNTOPO_INHERIT_RADIUS_SCALE | DYNTOPO_INHERIT_REPEAT | - DYNTOPO_INHERIT_CLEANUP); + DYNTOPO_INHERIT_COLLAPSE | DYNTOPO_INHERIT_RADIUS_SCALE | + DYNTOPO_INHERIT_REPEAT | DYNTOPO_INHERIT_CLEANUP); br->dyntopo.flag |= DYNTOPO_SUBDIVIDE | DYNTOPO_COLLAPSE | DYNTOPO_CLEANUP; - br->dyntopo.detail_range = 0.4f; br->dyntopo.spacing = 0; br->dyntopo.radius_scale = 1.25; br->dyntopo.repeat = 1; diff --git a/source/blender/blenkernel/intern/dyntopo.cc b/source/blender/blenkernel/intern/dyntopo.cc index f041b084d0a..9bebe4587a3 100644 --- a/source/blender/blenkernel/intern/dyntopo.cc +++ b/source/blender/blenkernel/intern/dyntopo.cc @@ -1709,8 +1709,8 @@ static bool cleanup_valence_3_4(EdgeQueueContext *ectx, PBVH *pbvh) */ BMEdge *e = v->e; do { - float len = calc_weighted_length(ectx, e->v1, e->v2, COLLAPSE); - if (sqrtf(len) > ectx->limit_len_max * 1.2f) { + float len = calc_weighted_length(ectx, e->v1, e->v2, SPLIT); + if (sqrtf(len) > ectx->limit_len_max * 1.5f) { bad = true; break; } diff --git a/source/blender/blenkernel/intern/paint.cc b/source/blender/blenkernel/intern/paint.cc index 18d37a4aa97..9da0f6dbad6 100644 --- a/source/blender/blenkernel/intern/paint.cc +++ b/source/blender/blenkernel/intern/paint.cc @@ -2578,15 +2578,6 @@ void BKE_sculpt_toolsettings_data_ensure(Scene *scene) sd->detail_size = 8.0f; } - /* We check these flags here in case versioning code fails. */ - if (!sd->detail_range || !sd->dyntopo.spacing) { - sd->flags |= SCULPT_DYNTOPO_ENABLED; - } - - if (!sd->detail_range) { - sd->detail_range = 0.4f; - } - if (!sd->detail_percent) { sd->detail_percent = 25; } diff --git a/source/blender/blenloader/intern/versioning_300.cc b/source/blender/blenloader/intern/versioning_300.cc index 324e6f04de4..33938f31a6b 100644 --- a/source/blender/blenloader/intern/versioning_300.cc +++ b/source/blender/blenloader/intern/versioning_300.cc @@ -4398,10 +4398,6 @@ void blo_do_versions_300(FileData *fd, Library * /*lib*/, Main *bmain) ds->detail_size = sculpt->detail_size; ds->constant_detail = sculpt->constant_detail; - if (sculpt->detail_range != 0.0f) { - ds->detail_range = sculpt->detail_range; - } - sculpt->flags |= SCULPT_DYNTOPO_ENABLED; ds->flag = 0; diff --git a/source/blender/editors/sculpt_paint/sculpt.cc b/source/blender/editors/sculpt_paint/sculpt.cc index 9ccb50e1a02..1cba1ea2de2 100644 --- a/source/blender/editors/sculpt_paint/sculpt.cc +++ b/source/blender/editors/sculpt_paint/sculpt.cc @@ -6406,19 +6406,19 @@ static void sculpt_stroke_update_step(bContext *C, float object_space_constant_detail = 1.0f / (ss->cached_dyntopo.constant_detail * mat4_to_scale(ob->object_to_world)); blender::bke::dyntopo::detail_size_set( - ss->pbvh, object_space_constant_detail, ss->cached_dyntopo.detail_range); + ss->pbvh, object_space_constant_detail, DYNTOPO_DETAIL_RANGE); } else if (ss->cached_dyntopo.mode == DYNTOPO_DETAIL_BRUSH) { blender::bke::dyntopo::detail_size_set(ss->pbvh, ss->cache->radius * ss->cached_dyntopo.detail_percent / 100.0f, - ss->cached_dyntopo.detail_range); + DYNTOPO_DETAIL_RANGE); } else { /* Relative mode. */ blender::bke::dyntopo::detail_size_set(ss->pbvh, (ss->cache->radius / ss->cache->dyntopo_pixel_radius) * (ss->cached_dyntopo.detail_size * U.pixelsize), - ss->cached_dyntopo.detail_range); + DYNTOPO_DETAIL_RANGE); } float dyntopo_spacing = float(ss->cached_dyntopo.spacing) / 50.0f; diff --git a/source/blender/editors/sculpt_paint/sculpt_api.cc b/source/blender/editors/sculpt_paint/sculpt_api.cc index b56ec6f0989..f8560185c97 100644 --- a/source/blender/editors/sculpt_paint/sculpt_api.cc +++ b/source/blender/editors/sculpt_paint/sculpt_api.cc @@ -564,8 +564,6 @@ void SCULPT_apply_dyntopo_settings(Scene *scene, SculptSession *ss, Sculpt *scul ds_final->detail_percent = ds_final->inherit & DYNTOPO_INHERIT_DETAIL_PERCENT ? ds2->detail_percent : ds1->detail_percent; - ds_final->detail_range = ds_final->inherit & DYNTOPO_INHERIT_DETAIL_RANGE ? ds2->detail_range : - ds1->detail_range; ds_final->detail_size = ds_final->inherit & DYNTOPO_INHERIT_DETAIL_SIZE ? ds2->detail_size : ds1->detail_size; ds_final->mode = ds_final->inherit & DYNTOPO_INHERIT_MODE ? ds2->mode : ds1->mode; diff --git a/source/blender/editors/sculpt_paint/sculpt_detail.cc b/source/blender/editors/sculpt_paint/sculpt_detail.cc index 6a9f2b7b1d6..70229cae969 100644 --- a/source/blender/editors/sculpt_paint/sculpt_detail.cc +++ b/source/blender/editors/sculpt_paint/sculpt_detail.cc @@ -116,7 +116,7 @@ static int sculpt_detail_flood_fill_run(Scene *scene, } SCULPT_apply_dyntopo_settings(scene, ss, sd, brush); - float detail_range = ss->cached_dyntopo.detail_range; + float detail_range = DYNTOPO_DETAIL_RANGE; /* Update topology size. */ float object_space_constant_detail = 1.0f / (ss->cached_dyntopo.constant_detail * @@ -807,7 +807,7 @@ static void dyntopo_detail_size_parallel_lines_draw(uint pos3d, * where all edges have the exact maximum length, which never happens in practice. As the * minimum edge length for dyntopo is 0.4 * max_edge_length, this adjust the detail size to the * average between max and min edge length so the preview is more accurate. */ - object_space_constant_detail *= 1.0f - cd->detail_range * 0.5f; + object_space_constant_detail *= 1.0f - DYNTOPO_DETAIL_RANGE * 0.5f; const float total_len = len_v3v3(cd->preview_tri[0], cd->preview_tri[1]); const int tot_lines = int(total_len / object_space_constant_detail) + 1; @@ -1038,10 +1038,6 @@ static int dyntopo_detail_size_edit_invoke(bContext *C, wmOperator *op, const wm sd->dyntopo.constant_detail : brush->dyntopo.constant_detail; - /* Initial operator Custom Data setup. */ - cd->detail_range = brush->dyntopo.inherit & DYNTOPO_INHERIT_DETAIL_RANGE ? - sd->dyntopo.detail_range : - brush->dyntopo.detail_range; cd->draw_handle = ED_region_draw_cb_activate( region->type, dyntopo_detail_size_edit_draw, cd, REGION_DRAW_POST_VIEW); cd->active_object = active_object; diff --git a/source/blender/makesdna/DNA_brush_defaults.h b/source/blender/makesdna/DNA_brush_defaults.h index 939040fee12..1dba155e727 100644 --- a/source/blender/makesdna/DNA_brush_defaults.h +++ b/source/blender/makesdna/DNA_brush_defaults.h @@ -19,7 +19,6 @@ #define _DNA_DEFAULT_DynTopoSettings \ {\ - .detail_range = 0.4f,\ .detail_percent = 25.0f,\ .detail_size = 12.0f,\ .constant_detail = 3.0f,\ diff --git a/source/blender/makesdna/DNA_brush_enums.h b/source/blender/makesdna/DNA_brush_enums.h index 20d628edfa2..eb0e8625439 100644 --- a/source/blender/makesdna/DNA_brush_enums.h +++ b/source/blender/makesdna/DNA_brush_enums.h @@ -676,6 +676,8 @@ typedef enum eBrushCurvesSculptDensityMode { #define MAX_BRUSH_PIXEL_RADIUS 500 +#define DYNTOPO_DETAIL_RANGE 0.4f + #ifdef __cplusplus } #endif diff --git a/source/blender/makesdna/DNA_brush_types.h b/source/blender/makesdna/DNA_brush_types.h index ed5c33a2d60..ab3e9ea0508 100644 --- a/source/blender/makesdna/DNA_brush_types.h +++ b/source/blender/makesdna/DNA_brush_types.h @@ -147,7 +147,6 @@ typedef struct BrushGpencilSettings { } BrushGpencilSettings; typedef struct DynTopoSettings { - float detail_range; float detail_percent; float detail_size; float constant_detail; @@ -157,6 +156,7 @@ typedef struct DynTopoSettings { float radius_scale; int repeat; float quality; + int _pad[1]; } DynTopoSettings; typedef struct BrushCurvesSculptSettings { diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h index 07c169f23cf..3e8f13e24d5 100644 --- a/source/blender/makesdna/DNA_scene_types.h +++ b/source/blender/makesdna/DNA_scene_types.h @@ -1083,7 +1083,6 @@ typedef struct Sculpt { /** Maximum edge length for dynamic topology sculpting (in pixels). */ float detail_size DNA_DEPRECATED; - float detail_range DNA_DEPRECATED; /** Direction used for `SCULPT_OT_symmetrize` operator. */ int symmetrize_direction; @@ -1100,7 +1099,7 @@ typedef struct Sculpt { float dyntopo_radius_scale DNA_DEPRECATED; int automasking_cavity_blur_steps; float automasking_cavity_factor; - char _pad[4]; + char _pad[8]; float automasking_start_normal_limit, automasking_start_normal_falloff; float automasking_view_normal_limit, automasking_view_normal_falloff; diff --git a/source/blender/makesrna/intern/rna_brush.c b/source/blender/makesrna/intern/rna_brush.c index 3d6d5e2a836..8d292bb1ea5 100644 --- a/source/blender/makesrna/intern/rna_brush.c +++ b/source/blender/makesrna/intern/rna_brush.c @@ -474,7 +474,6 @@ static EnumPropertyItem rna_enum_brush_dyntopo_inherit[] = { {DYNTOPO_LOCAL_COLLAPSE, "LOCAL_COLLAPSE", ICON_NONE, "Local Collapse", ""}, {DYNTOPO_LOCAL_SUBDIVIDE, "LOCAL_SUBDIVIDE", ICON_NONE, "Local Subdivide", ""}, {DYNTOPO_DISABLED, "DISABLED", ICON_NONE, "Disable", ""}, - {DYNTOPO_INHERIT_DETAIL_RANGE, "DETAIL_RANGE", ICON_NONE, "All", ""}, {DYNTOPO_INHERIT_DETAIL_PERCENT, "DETAIL_PERCENT", ICON_NONE, "Percent", ""}, {DYNTOPO_INHERIT_MODE, "MODE", ICON_NONE, "Mode", ""}, {DYNTOPO_INHERIT_CONSTANT_DETAIL, "CONSTANT_DETAIL", ICON_NONE, "Constant Detail", ""}, @@ -1363,14 +1362,6 @@ static void rna_def_dyntopo_settings(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Detail Size", ""); RNA_def_property_update(prop, 0, "rna_Brush_dyntopo_update"); - prop = RNA_def_property(srna, "detail_range", PROP_FLOAT, PROP_NONE); - RNA_def_property_float_sdna(prop, NULL, "detail_range"); - RNA_def_property_range(prop, 0.0, 1.0); - RNA_def_property_ui_range(prop, 0.0, 1.0, 0.001, 4); - RNA_def_property_ui_text( - prop, "Detail Range", "Higher values are faster but produce lower quality topology"); - RNA_def_property_update(prop, 0, "rna_Brush_dyntopo_update"); - prop = RNA_def_property(srna, "constant_detail", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "constant_detail"); RNA_def_property_range(prop, 0.0001, FLT_MAX); -- 2.30.2 From 57391bd079228aeebddd77f3ff4f10132f83d34f Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Fri, 23 Jun 2023 02:50:06 -0700 Subject: [PATCH 197/279] temp-sculpt-dyntopo: Make dyntopo detail mode a menu --- scripts/startup/bl_ui/space_view3d_toolbar.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/startup/bl_ui/space_view3d_toolbar.py b/scripts/startup/bl_ui/space_view3d_toolbar.py index 770971f88c6..b9ca4d4a1b6 100644 --- a/scripts/startup/bl_ui/space_view3d_toolbar.py +++ b/scripts/startup/bl_ui/space_view3d_toolbar.py @@ -942,7 +942,7 @@ class VIEW3D_PT_sculpt_dyntopo(Panel, View3DPaintPanel): context, brush, "mode", - expand=True + expand=False ) #NotForPR -- 2.30.2 From adea20c375a3eb67b0b6031b2a3abf83748ac7d2 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Fri, 23 Jun 2023 03:25:37 -0700 Subject: [PATCH 198/279] temp-sculpt-dyntopo: Add Sculpt to DNA defaults. Also tweaked dyntopo defaults. --- release/datafiles/startup.blend | Bin 885428 -> 953096 bytes source/blender/blenkernel/intern/paint.cc | 20 ++++++------------ .../blenloader/intern/versioning_300.cc | 9 ++------ source/blender/makesdna/DNA_brush_defaults.h | 6 +++--- source/blender/makesdna/DNA_scene_defaults.h | 10 +++++++++ source/blender/makesdna/intern/dna_defaults.c | 4 ++++ 6 files changed, 26 insertions(+), 23 deletions(-) diff --git a/release/datafiles/startup.blend b/release/datafiles/startup.blend index 692a14f92b8dd3111819c7de21a48a79f856db2a..006ed6456ece0625d719ed5e1d00cad48cab78cd 100644 GIT binary patch literal 953096 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

hcKPp-d@V~u}) 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�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 diff --git a/source/blender/blenkernel/intern/paint.cc b/source/blender/blenkernel/intern/paint.cc index 9da0f6dbad6..7a862878dce 100644 --- a/source/blender/blenkernel/intern/paint.cc +++ b/source/blender/blenkernel/intern/paint.cc @@ -1142,12 +1142,12 @@ bool BKE_paint_ensure(ToolSettings *ts, Paint **r_paint) } else if ((Sculpt **)r_paint == &ts->sculpt) { Sculpt *data = MEM_cnew(__func__); + *data = *DNA_struct_default_get(Sculpt); + paint = &data->paint; /* Turn on X plane mirror symmetry by default */ paint->symmetry_flags |= PAINT_SYMM_X; - - data->dyntopo = *DNA_struct_default_get(DynTopoSettings); } else if ((GpPaint **)r_paint == &ts->gp_paint) { GpPaint *data = MEM_cnew(__func__); @@ -1181,6 +1181,8 @@ bool BKE_paint_ensure(ToolSettings *ts, Paint **r_paint) *r_paint = paint; + paint->tile_offset[0] = paint->tile_offset[1] = paint->tile_offset[2] = 1.0f; + BKE_paint_runtime_init(ts, paint); return false; @@ -2571,20 +2573,11 @@ int BKE_sculpt_mask_layers_ensure(Depsgraph *depsgraph, void BKE_sculpt_toolsettings_data_ensure(Scene *scene) { + bool exists = scene->toolsettings->sculpt; + BKE_paint_ensure(scene->toolsettings, (Paint **)&scene->toolsettings->sculpt); Sculpt *sd = scene->toolsettings->sculpt; - if (!sd->detail_size) { - sd->detail_size = 8.0f; - } - - if (!sd->detail_percent) { - sd->detail_percent = 25; - } - - if (!sd->dyntopo.constant_detail) { - sd->dyntopo = *DNA_struct_default_get(DynTopoSettings); - } if (!sd->automasking_start_normal_limit) { sd->automasking_start_normal_limit = 20.0f / 180.0f * M_PI; @@ -2604,6 +2597,7 @@ void BKE_sculpt_toolsettings_data_ensure(Scene *scene) if (!sd->paint.tile_offset[2]) { sd->paint.tile_offset[2] = 1.0f; } + if (!sd->automasking_cavity_curve || !sd->automasking_cavity_curve_op) { BKE_sculpt_check_cavity_curves(sd); } diff --git a/source/blender/blenloader/intern/versioning_300.cc b/source/blender/blenloader/intern/versioning_300.cc index 33938f31a6b..19c118e1ae9 100644 --- a/source/blender/blenloader/intern/versioning_300.cc +++ b/source/blender/blenloader/intern/versioning_300.cc @@ -4408,6 +4408,8 @@ void blo_do_versions_300(FileData *fd, Library * /*lib*/, Main *bmain) ds->flag |= DYNTOPO_COLLAPSE; } + ds->flag |= DYNTOPO_CLEANUP; + if (sculpt->flags & SCULPT_DYNTOPO_DETAIL_CONSTANT) { ds->mode = DYNTOPO_DETAIL_CONSTANT; } @@ -4420,13 +4422,6 @@ void blo_do_versions_300(FileData *fd, Library * /*lib*/, Main *bmain) else { ds->mode = DYNTOPO_DETAIL_RELATIVE; } - - if (sculpt->flags & SCULPT_DYNTOPO_SUBDIVIDE) { - ds->flag |= DYNTOPO_SUBDIVIDE; - } - if (sculpt->flags & SCULPT_DYNTOPO_COLLAPSE) { - ds->flag |= DYNTOPO_COLLAPSE; - } } } diff --git a/source/blender/makesdna/DNA_brush_defaults.h b/source/blender/makesdna/DNA_brush_defaults.h index 1dba155e727..768a89c17bb 100644 --- a/source/blender/makesdna/DNA_brush_defaults.h +++ b/source/blender/makesdna/DNA_brush_defaults.h @@ -20,9 +20,9 @@ #define _DNA_DEFAULT_DynTopoSettings \ {\ .detail_percent = 25.0f,\ - .detail_size = 12.0f,\ - .constant_detail = 3.0f,\ - .flag = DYNTOPO_COLLAPSE|DYNTOPO_SUBDIVIDE,\ + .detail_size = 8.0f,\ + .constant_detail = 16.0f,\ + .flag = DYNTOPO_COLLAPSE|DYNTOPO_SUBDIVIDE|DYNTOPO_CLEANUP,\ .mode = DYNTOPO_DETAIL_RELATIVE,\ .inherit = DYNTOPO_INHERIT_BITMASK,\ .spacing = 35,\ diff --git a/source/blender/makesdna/DNA_scene_defaults.h b/source/blender/makesdna/DNA_scene_defaults.h index 1efb2ecef88..96ebad5b486 100644 --- a/source/blender/makesdna/DNA_scene_defaults.h +++ b/source/blender/makesdna/DNA_scene_defaults.h @@ -8,6 +8,7 @@ #pragma once +#include "DNA_brush_defaults.h" #include "DNA_scene_enums.h" #include "DNA_view3d_defaults.h" @@ -378,6 +379,15 @@ .plane_axis = 2,\ } +#define _DNA_DEFAULT_Sculpt \ + { \ + .automasking_start_normal_limit = 0.34906585f, /* 20 / 180 * pi */ \ + .automasking_start_normal_falloff = 0.25f, \ + .automasking_view_normal_limit = 1.570796, /* 0.5 * pi */ \ + .automasking_view_normal_falloff = 0.25f, \ + .flags = SCULPT_DYNTOPO_ENABLED,\ + .dyntopo = _DNA_DEFAULT_DynTopoSettings,\ + }\ /* clang-format off */ /** \} */ diff --git a/source/blender/makesdna/intern/dna_defaults.c b/source/blender/makesdna/intern/dna_defaults.c index cdbca2e0f3a..5ec82087f5b 100644 --- a/source/blender/makesdna/intern/dna_defaults.c +++ b/source/blender/makesdna/intern/dna_defaults.c @@ -213,6 +213,7 @@ SDNA_DEFAULT_DECL_STRUCT(PointCloud); /* DNA_scene_defaults.h */ SDNA_DEFAULT_DECL_STRUCT(Scene); SDNA_DEFAULT_DECL_STRUCT(ToolSettings); +SDNA_DEFAULT_DECL_STRUCT(Sculpt); /* DNA_simulation_defaults.h */ SDNA_DEFAULT_DECL_STRUCT(Simulation); @@ -454,6 +455,9 @@ const void *DNA_default_table[SDNA_TYPE_MAX] = { SDNA_DEFAULT_DECL_EX(GP_Sculpt_Settings, ToolSettings.gp_sculpt), SDNA_DEFAULT_DECL_EX(GP_Sculpt_Guide, ToolSettings.gp_sculpt.guide), + SDNA_DEFAULT_DECL(Sculpt), + SDNA_DEFAULT_DECL_EX(DynTopoSettings, Sculpt.dyntopo), + /* DNA_simulation_defaults.h */ SDNA_DEFAULT_DECL(Simulation), -- 2.30.2 From 509f68d594a110707760180bb2317e0f63f76ee0 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Fri, 23 Jun 2023 03:36:40 -0700 Subject: [PATCH 199/279] temp-sculpt-dyntopo: Enable dyntopo for paint, but disable it for smear --- source/blender/blenkernel/intern/brush.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source/blender/blenkernel/intern/brush.cc b/source/blender/blenkernel/intern/brush.cc index 28eb33535f7..ab209dd6449 100644 --- a/source/blender/blenkernel/intern/brush.cc +++ b/source/blender/blenkernel/intern/brush.cc @@ -1958,7 +1958,7 @@ void BKE_brush_sculpt_reset(Brush *br) br->tip_roundness = 1.0f; br->density = 1.0f; br->flag &= ~BRUSH_SPACE_ATTEN; - disable_dyntopo = true; + disable_dyntopo = false; zero_v3(br->rgb); copy_v3_fl(br->secondary_rgb, 1.0f); break; @@ -1968,6 +1968,7 @@ void BKE_brush_sculpt_reset(Brush *br) br->flag &= ~BRUSH_ALPHA_PRESSURE; br->flag &= ~BRUSH_SPACE_ATTEN; br->curve_preset = BRUSH_CURVE_SPHERE; + disable_dyntopo = true; break; case SCULPT_TOOL_DISPLACEMENT_SMEAR: br->alpha = 1.0f; -- 2.30.2 From 944c12b947b45d46349f214e204729a1ba69def4 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Sat, 24 Jun 2023 01:05:59 -0700 Subject: [PATCH 200/279] temp-sculpt-dyntopo: Increase defautl sharp angle limit to 35 degrees --- source/blender/makesdna/DNA_scene_defaults.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/makesdna/DNA_scene_defaults.h b/source/blender/makesdna/DNA_scene_defaults.h index 96ebad5b486..b0dfa247952 100644 --- a/source/blender/makesdna/DNA_scene_defaults.h +++ b/source/blender/makesdna/DNA_scene_defaults.h @@ -282,7 +282,7 @@ .weight = 0.5f, \ .flag = UNIFIED_PAINT_SIZE | UNIFIED_PAINT_ALPHA | UNIFIED_PAINT_FLAG_HARD_EDGE_MODE | UNIFIED_PAINT_HARD_CORNER_PIN | UNIFIED_PAINT_FLAG_SHARP_ANGLE_LIMIT, \ .hard_corner_pin = 1.0f,\ - .sharp_angle_limit = 0.38f,\ + .sharp_angle_limit = 0.6108f,\ .smooth_boundary_flag = SCULPT_BOUNDARY_MESH|SCULPT_BOUNDARY_FACE_SET|SCULPT_BOUNDARY_SEAM|SCULPT_BOUNDARY_SHARP_MARK|SCULPT_BOUNDARY_UV,\ } -- 2.30.2 From b66ad0cc53999134c98ae4787316a5c2495276ef Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Sun, 25 Jun 2023 06:34:55 -0700 Subject: [PATCH 201/279] temp-sculpt-dyntopo: Fix broken foundary flags for PBVH_FACES And a few other minor bugs. --- source/blender/blenkernel/intern/pbvh.cc | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/source/blender/blenkernel/intern/pbvh.cc b/source/blender/blenkernel/intern/pbvh.cc index b2ec4b67d92..40325c1d4f3 100644 --- a/source/blender/blenkernel/intern/pbvh.cc +++ b/source/blender/blenkernel/intern/pbvh.cc @@ -721,7 +721,9 @@ static void build_sub(PBVH *pbvh, /* Decide whether this is a leaf or not */ const bool below_leaf_limit = count <= pbvh->leaf_limit || depth == PBVH_STACK_FIXED_DEPTH - 1; if (below_leaf_limit) { - if (!leaf_needs_material_split(pbvh, material_indices, sharp_faces, offset, count)) { + if (!leaf_needs_material_split(pbvh, material_indices, sharp_faces, offset, count) || + depth >= PBVH_STACK_FIXED_DEPTH - 1) + { build_leaf(pbvh, node_index, prim_bbc, offset, count); if (node_index == 0) { @@ -1073,6 +1075,8 @@ void BKE_pbvh_build_mesh(PBVH *pbvh, Mesh *mesh) pbvh->sharp_edges = static_cast( CustomData_get_layer_named(&mesh->edata, CD_PROP_BOOL, "sharp_edge")); pbvh->totloop = mesh->totloop; + pbvh->face_sets = static_cast(CustomData_get_layer_named_for_write( + &mesh->pdata, CD_PROP_INT32, SCULPT_ATTRIBUTE_NAME(face_set), mesh->totpoly)); MLoopTri *looptri = static_cast( MEM_malloc_arrayN(looptri_num, sizeof(*looptri), __func__)); @@ -4377,7 +4381,7 @@ void update_edge_boundary_faces(int edge, } else { /* No edge->poly map; approximate from vertices (will give artifacts on corners). */ int v1 = edges[edge][0]; - int v2 = edges[edge][2]; + int v2 = edges[edge][1]; int boundary_mask = ((1 << int(SCULPT_CORNER_BIT_SHIFT)) - 1); int a = vert_boundary_flags[v1] & -- 2.30.2 From 05a6480666cf1f05b377558860a90586e28bb729 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Tue, 27 Jun 2023 03:12:40 -0700 Subject: [PATCH 202/279] temp-sculpt-dyntopo: Re-enable wire cleanup code --- .../blenkernel/intern/dyntopo_collapse.cc | 41 ++++++++++--------- source/blender/blenkernel/intern/paint.cc | 2 +- source/blender/editors/interface/interface.cc | 2 +- 3 files changed, 23 insertions(+), 22 deletions(-) diff --git a/source/blender/blenkernel/intern/dyntopo_collapse.cc b/source/blender/blenkernel/intern/dyntopo_collapse.cc index cc34f3a57d1..f7d92134497 100644 --- a/source/blender/blenkernel/intern/dyntopo_collapse.cc +++ b/source/blender/blenkernel/intern/dyntopo_collapse.cc @@ -37,6 +37,7 @@ #include "dyntopo_intern.hh" #include "pbvh_intern.hh" +#include #include #include @@ -212,8 +213,7 @@ bool pbvh_bmesh_collapse_edge_uvs( const float limit = 0.005; BMLoop *l = e->l; - for (int step = 0; step < 2; step++) { - BMVert *v = step ? e->v2 : e->v1; + for (BMVert *v : std::array({e->v1, e->v2})) { BMEdge *e2 = v->e; if (!e2) { @@ -547,25 +547,27 @@ BMVert *EdgeQueueContext::collapse_edge(PBVH *pbvh, BMEdge *e, BMVert *v1, BMVer pbvh_bmesh_check_nodes(pbvh); -#if 0 /* Destroy wire edges */ - Vector es; - e2 = v_conn->e; - do { - if (!e2->l) { - es.append(e2); + if (v_conn->e) { + Vector es; + BMEdge *e2 = v_conn->e; + + do { + if (!e2->l) { + es.append(e2); + } + } while ((e2 = BM_DISK_EDGE_NEXT(e2, v_conn)) != v_conn->e); + + for (BMEdge *e2 : es) { + BMVert *v2 = BM_edge_other_vert(e2, v_conn); + + BM_log_edge_removed(pbvh->header.bm, pbvh->bm_log, e2); + BM_idmap_release(pbvh->bm_idmap, reinterpret_cast(e2), true); + BM_edge_kill(pbvh->header.bm, e2); + + dyntopo_add_flag(pbvh, v2, SCULPTFLAG_NEED_VALENCE | SCULPTFLAG_NEED_TRIANGULATE); + BKE_sculpt_boundary_flag_update(ss, {reinterpret_cast(v2)}); } - } while ((e2 = BM_DISK_EDGE_NEXT(e2, v_conn)) != v_conn->e); - - for (BMEdge *e2 : es) { - BMVert *v2 = BM_edge_other_vert(e2, v_conn); - - BM_log_edge_removed(pbvh->header.bm, pbvh->bm_log, e2); - BM_idmap_release(pbvh->bm_idmap, reinterpret_cast(e2), true); - BM_edge_kill(pbvh->header.bm, e2); - - dyntopo_add_flag(pbvh, v2, SCULPTFLAG_NEED_VALENCE | SCULPTFLAG_NEED_TRIANGULATE); - BKE_sculpt_boundary_flag_update(ss, {reinterpret_cast(v2)}); } if (!v_conn->e) { @@ -580,7 +582,6 @@ BMVert *EdgeQueueContext::collapse_edge(PBVH *pbvh, BMEdge *e, BMVert *v1, BMVer return nullptr; } -#endif if (BM_ELEM_CD_GET_INT(v_conn, pbvh->cd_vert_node_offset) == DYNTOPO_NODE_NONE) { printf("%s: error: failed to remove vert from pbvh? v_conn->e: %p v_conn->e->l: %p\n", diff --git a/source/blender/blenkernel/intern/paint.cc b/source/blender/blenkernel/intern/paint.cc index 70685306522..8d7d0e69829 100644 --- a/source/blender/blenkernel/intern/paint.cc +++ b/source/blender/blenkernel/intern/paint.cc @@ -662,7 +662,7 @@ ePaintMode BKE_paintmode_get_from_tool(const bToolRef *tref) Brush *BKE_paint_brush(Paint *p) { - return p->brush; + return p ? p->brush : nullptr; } const Brush *BKE_paint_brush_for_read(const Paint *p) diff --git a/source/blender/editors/interface/interface.cc b/source/blender/editors/interface/interface.cc index 3e3619a4908..7514d04ab68 100644 --- a/source/blender/editors/interface/interface.cc +++ b/source/blender/editors/interface/interface.cc @@ -755,7 +755,7 @@ static bool ui_but_equals_old(const uiBut *but, const uiBut *oldbut) /* Compares the contained function pointers. Buttons with different apply functions can be * considered to do different things, and as such do not equal each other. */ - /* XXX clang-cl bug? Have to de-const to access target methods.*/ + /* NotForPR: XXX clang-cl bug? Have to de-const to access target methods.*/ uiBut *nonconst_but = const_cast(but); uiBut *onconst_oldbut = const_cast(oldbut); -- 2.30.2 From 8a9ea83fa62adb21daf4a089d32f88e39146c394 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Tue, 27 Jun 2023 03:21:45 -0700 Subject: [PATCH 203/279] temp-sculpt-dyntopo: Fix texture draw in dynamic topology mode Also fixed a few crashes --- source/blender/blenkernel/intern/paint.cc | 8 ++++---- source/blender/draw/engines/workbench/workbench_engine.c | 2 +- source/blender/nodes/shader/node_shader_util.cc | 8 +++++--- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/source/blender/blenkernel/intern/paint.cc b/source/blender/blenkernel/intern/paint.cc index 8d7d0e69829..3d60cf941a8 100644 --- a/source/blender/blenkernel/intern/paint.cc +++ b/source/blender/blenkernel/intern/paint.cc @@ -3483,7 +3483,7 @@ static bool sculpt_attr_update(Object *ob, SculptAttribute *attr) /* Check if we are a coerced simple array and shouldn't be. */ bad |= (attr->simple_array && !attr->params.simple_array) && - !(BKE_pbvh_type(ss->pbvh) == PBVH_GRIDS && attr->domain == ATTR_DOMAIN_POINT); + !(ss->pbvh && BKE_pbvh_type(ss->pbvh) == PBVH_GRIDS && attr->domain == ATTR_DOMAIN_POINT); CustomData *cdata = sculpt_get_cdata(ob, attr->domain); if (cdata && !attr->simple_array) { @@ -3641,9 +3641,9 @@ SculptAttribute *BKE_sculpt_attribute_get(Object *ob, attr = sculpt_alloc_attr(ss); - if (BKE_pbvh_type(ss->pbvh) == PBVH_FACES || - (BKE_pbvh_type(ss->pbvh) == PBVH_GRIDS && - ELEM(domain, ATTR_DOMAIN_FACE, ATTR_DOMAIN_EDGE))) + if (ss->pbvh && (BKE_pbvh_type(ss->pbvh) == PBVH_FACES || + (BKE_pbvh_type(ss->pbvh) == PBVH_GRIDS && + ELEM(domain, ATTR_DOMAIN_FACE, ATTR_DOMAIN_EDGE)))) { attr->data = CustomData_get_layer_named_for_write( cdata, attr->proptype, attr->name, totelem); diff --git a/source/blender/draw/engines/workbench/workbench_engine.c b/source/blender/draw/engines/workbench/workbench_engine.c index 99808cc2a49..6ecba684a7a 100644 --- a/source/blender/draw/engines/workbench/workbench_engine.c +++ b/source/blender/draw/engines/workbench/workbench_engine.c @@ -343,7 +343,7 @@ static eV3DShadingColorType workbench_color_type_get(WORKBENCH_PrivateData *wpd, } if (is_sculpt_pbvh && color_type == V3D_SHADING_TEXTURE_COLOR && - BKE_pbvh_type(BKE_object_sculpt_pbvh_get(ob)) != PBVH_FACES) + !ELEM(BKE_pbvh_type(BKE_object_sculpt_pbvh_get(ob)), PBVH_FACES, PBVH_BMESH)) { /* Force use of material color for sculpt. */ color_type = V3D_SHADING_MATERIAL_COLOR; diff --git a/source/blender/nodes/shader/node_shader_util.cc b/source/blender/nodes/shader/node_shader_util.cc index caa13067928..9487dd98d13 100644 --- a/source/blender/nodes/shader/node_shader_util.cc +++ b/source/blender/nodes/shader/node_shader_util.cc @@ -187,9 +187,11 @@ void node_gpu_stack_from_data(GPUNodeStack *gs, int type, bNodeStack *ns) void node_data_from_gpu_stack(bNodeStack *ns, GPUNodeStack *gs) { - copy_v4_v4(ns->vec, gs->vec); - ns->data = gs->link; - ns->sockettype = gs->sockettype; + if (ns) { + copy_v4_v4(ns->vec, gs->vec); + ns->data = gs->link; + ns->sockettype = gs->sockettype; + } } static void gpu_stack_from_data_list(GPUNodeStack *gs, ListBase *sockets, bNodeStack **ns) -- 2.30.2 From 4a134abfa150e85875b8ae99590f00d9367a0ae1 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Tue, 27 Jun 2023 10:07:04 -0700 Subject: [PATCH 204/279] temp-sculpt-dyntopo: Fix UV reprojection and a few other fixes * Loop_is_corner now falls back to an angle test if the number of charts around a vertex is less than 2. * Dyntopo now starts at first dab. * Fixed crash. --- source/blender/blenkernel/BKE_sculpt.hh | 15 +- source/blender/blenkernel/intern/dyntopo.cc | 4 +- .../blenkernel/intern/dyntopo_collapse.cc | 7 +- source/blender/blenkernel/intern/paint.cc | 274 +++++++++++++----- .../blender/blenkernel/intern/pbvh_bmesh.cc | 32 +- .../editors/sculpt_paint/paint_mask.cc | 2 +- source/blender/editors/sculpt_paint/sculpt.cc | 1 + .../editors/sculpt_paint/sculpt_face_set.cc | 1 + .../editors/sculpt_paint/sculpt_smooth.cc | 15 +- 9 files changed, 253 insertions(+), 98 deletions(-) diff --git a/source/blender/blenkernel/BKE_sculpt.hh b/source/blender/blenkernel/BKE_sculpt.hh index 36a1a9efb52..f195e43272b 100644 --- a/source/blender/blenkernel/BKE_sculpt.hh +++ b/source/blender/blenkernel/BKE_sculpt.hh @@ -102,10 +102,17 @@ BLI_INLINE bool test_sculpt_flag(SculptSession *ss, PBVHVertRef vertex, uint8_t return blender::bke::paint::vertex_attr_get(vertex, ss->attrs.flags) & flag; } -void interp_face_corners( - PBVH *pbvh, PBVHVertRef vertex, Span loops, Span ws, float factor); +/* Interpolates loops surrounding a vertex, splitting any UV map by + * island as appropriate and enforcing proper boundary conditions. + */ +void interp_face_corners(PBVH *pbvh, + PBVHVertRef vertex, + Span loops, + Span ws, + float factor, + int cd_vert_boundary); float calc_uv_snap_limit(BMLoop *l, int cd_uv); -bool loop_is_corner(BMLoop *l, int cd_uv, float limit = 0.01); +bool loop_is_corner(BMLoop *l, int cd_uv, float limit = 0.01, const CustomData *ldata = nullptr); /* NotForPR: TODO: find attribute API substitute for these prop_eq helper functions. */ static bool prop_eq(float a, float b, float limit) @@ -114,7 +121,7 @@ static bool prop_eq(float a, float b, float limit) } static bool prop_eq(float2 a, float2 b, float limit) { - return prop_eq(a[0], b[0], limit) && prop_eq(a[1], b[1], limit); + return (a[0] - b[0]) * (a[0] - b[0]) + (a[1] - b[1]) * (a[1] - b[1]) <= limit * limit; } static bool prop_eq(float3 a, float3 b, float limit) { diff --git a/source/blender/blenkernel/intern/dyntopo.cc b/source/blender/blenkernel/intern/dyntopo.cc index 9bebe4587a3..757c4b97336 100644 --- a/source/blender/blenkernel/intern/dyntopo.cc +++ b/source/blender/blenkernel/intern/dyntopo.cc @@ -226,7 +226,7 @@ static void surface_smooth_v_safe( /* Reproject attributes. */ if (reproject_cdata) { BKE_sculpt_reproject_cdata(ss, vertex, startco, startno, false); - blender::bke::sculpt::interp_face_corners(pbvh, vertex, loops, ws, fac); + blender::bke::sculpt::interp_face_corners(pbvh, vertex, loops, ws, fac, pbvh->cd_boundary_flag); } PBVH_CHECK_NAN(v->co); @@ -2123,7 +2123,7 @@ void EdgeQueueContext::start() bool EdgeQueueContext::done() { - if (edge_heap.min_weight() > limit_len_min_sqr && edge_heap.max_weight() < limit_len_max_sqr) { + if (edge_heap.empty() || edge_heap.min_weight() > limit_len_min_sqr && edge_heap.max_weight() < limit_len_max_sqr) { return true; } diff --git a/source/blender/blenkernel/intern/dyntopo_collapse.cc b/source/blender/blenkernel/intern/dyntopo_collapse.cc index f7d92134497..f17bec428e8 100644 --- a/source/blender/blenkernel/intern/dyntopo_collapse.cc +++ b/source/blender/blenkernel/intern/dyntopo_collapse.cc @@ -203,7 +203,7 @@ bool pbvh_bmesh_collapse_edge_uvs( PBVHVertRef vertex = {reinterpret_cast(v_conn)}; blender::bke::sculpt::interp_face_corners( - pbvh, vertex, Span(ls, 2), Span(ws, 2), 1.0f); + pbvh, vertex, Span(ls, 2), Span(ws, 2), 1.0f, pbvh->cd_boundary_flag); # endif } while ((l = l->radial_next) != e->l); @@ -449,9 +449,8 @@ BMVert *EdgeQueueContext::collapse_edge(PBVH *pbvh, BMEdge *e, BMVert *v1, BMVer bool corner1 = (boundflag1 & SCULPTVERT_ALL_CORNER) || w1 >= 0.85; bool corner2 = (boundflag2 & SCULPTVERT_ALL_CORNER) || w2 >= 0.85; - /* We allow two corners of the example sampe type[s] to collapse */ - if ((boundflag1 & SCULPTVERT_ALL_CORNER) && - (boundflag1 & SCULPTVERT_ALL_CORNER) != (boundflag2 & SCULPTVERT_ALL_CORNER)) + /* We allow two corners of the same type[s] to collapse */ + if ((boundflag1 & SCULPTVERT_ALL_CORNER) != (boundflag2 & SCULPTVERT_ALL_CORNER)) { return nullptr; } diff --git a/source/blender/blenkernel/intern/paint.cc b/source/blender/blenkernel/intern/paint.cc index 3d60cf941a8..00ce7434947 100644 --- a/source/blender/blenkernel/intern/paint.cc +++ b/source/blender/blenkernel/intern/paint.cc @@ -4468,7 +4468,175 @@ float calc_uv_snap_limit(BMLoop *l, int cd_uv) } } -bool loop_is_corner(BMLoop *l, int cd_uv, float limit) +/* Angle test. loop_is_corner calls this if the chart count test fails. */ +static bool loop_is_corner_angle( + BMLoop *l, int cd_offset, const float limit, const float angle_limit, const CustomData *ldata) +{ + BMVert *v = l->v; + BMEdge *e = v->e; + + float2 uv_value = *BM_ELEM_CD_PTR(l, cd_offset); + + BMLoop *outer1 = nullptr, *outer2 = nullptr; + float2 outer1_value; + int cd_pin = -1, cd_sel = -1; + +#ifdef TEST_UV_CORNER_CALC + bool test_mode = false; + // test_mode = l->v->head.hflag & BM_ELEM_SELECT; + + if (ldata && test_mode) { + char name[512]; + for (int i = 0; i < ldata->totlayer; i++) { + CustomDataLayer *layer = ldata->layers + i; + if (layer->offset == cd_offset) { + sprintf(name, ".pn.%s", layer->name); + cd_pin = CustomData_get_offset_named(ldata, CD_PROP_BOOL, name); + + sprintf(name, ".vs.%s", layer->name); + cd_sel = CustomData_get_offset_named(ldata, CD_PROP_BOOL, name); + } + } + + if (cd_sel != -1) { + test_mode = BM_ELEM_CD_GET_BOOL(l, cd_sel); + } + } + + if (test_mode) { + printf("%s: start\n", __func__); + } +#endif + + do { + BMLoop *l2 = e->l; + if (!l2) { + continue; + } + + do { + BMLoop *uv_l2 = l2->v == v ? l2 : l2->next; + float2 uv_value2 = *BM_ELEM_CD_PTR(uv_l2, cd_offset); + + BMLoop *other_uv_l2 = l2->v == v ? l2->next : l2; + float2 other_uv_value2 = *BM_ELEM_CD_PTR(other_uv_l2, cd_offset); + + if (!prop_eq(uv_value, uv_value2, limit)) { + continue; + } + + bool outer = l2 == l2->radial_next; + + BMLoop *l3 = l2->radial_next; + while (l3 != l2) { + BMLoop *other_uv_l3 = l3->v == v ? l3->next : l3; + float2 other_uv_value3 = *BM_ELEM_CD_PTR(other_uv_l3, cd_offset); + + if (!prop_eq(other_uv_value2, other_uv_value3, limit)) { +#ifdef TEST_UV_CORNER_CALC + if (test_mode && cd_pin != -1) { + // BM_ELEM_CD_SET_BOOL(other_uv_l2, cd_pin, true); + // BM_ELEM_CD_SET_BOOL(other_uv_l3, cd_pin, true); + } +#endif + + BMLoop *uv_l3 = l3->v == v ? l3 : l3->next; + float2 uv_value3 = *BM_ELEM_CD_PTR(uv_l3, cd_offset); + + /* other_uv_value might be valid for one of the two arms, check. */ + if (prop_eq(uv_value, uv_value3, limit)) { +#ifdef TEST_UV_CORNER_CALC + if (test_mode) { + printf("%s: case 1\n", __func__); + } +#endif + + outer1 = other_uv_l2; + outer2 = other_uv_l3; + goto outer_break; + } + + outer = true; + break; + } + + l3 = l3->radial_next; + } + + if (outer) { +#ifdef TEST_UV_CORNER_CALC + if (test_mode && cd_pin != -1) { + // BM_ELEM_CD_SET_BOOL(other_uv_l2, cd_pin, true); + } +#endif + + if (!outer1) { + outer1 = other_uv_l2; + outer1_value = other_uv_value2; + } + else if (other_uv_l2 != outer2 && !prop_eq(outer1_value, other_uv_value2, limit)) { + outer2 = other_uv_l2; + +#ifdef TEST_UV_CORNER_CALC + if (test_mode) { + printf("%s: case 2\n", __func__); + } +#endif + goto outer_break; + } + } + } while ((l2 = l2->radial_next) != e->l); + } while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e); + +outer_break: + +#ifdef TEST_UV_CORNER_CALC + if (test_mode) { + printf("%s: l: %p, l->v: %p, outer1: %p, outer2: %p\n", __func__, l, l->v, outer1, outer2); + } +#endif + + if (!outer1 || !outer2) { + return false; + } + +#ifdef TEST_UV_CORNER_CALC + if (test_mode && cd_pin != -1) { + // BM_ELEM_CD_SET_BOOL(outer1, cd_pin, true); + // BM_ELEM_CD_SET_BOOL(outer2, cd_pin, true); + } +#endif + + float2 t1 = *BM_ELEM_CD_PTR(outer1, cd_offset) - uv_value; + float2 t2 = *BM_ELEM_CD_PTR(outer2, cd_offset) - uv_value; + + normalize_v2(t1); + normalize_v2(t2); + + if (dot_v2v2(t1, t2) < 0.0f) { + negate_v2(t2); + } + + float angle = saacos(dot_v2v2(t1, t2)); + +#ifdef TEST_UV_CORNER_CALC + if (test_mode) { + printf("%s: angle: %.5f\n", __func__, angle); + } +#endif + + bool ret = angle > angle_limit; + +#ifdef TEST_UV_CORNER_CALC + if (ret) { + // l->v->head.hflag |= BM_ELEM_SELECT; + } +#endif + + return ret; +} + +bool loop_is_corner(BMLoop *l, int cd_uv, float limit, const CustomData *ldata) { BMVert *v = l->v; @@ -4508,79 +4676,21 @@ bool loop_is_corner(BMLoop *l, int cd_uv, float limit) bool ret = keys.size() > 2; - return ret; -} + if (!ret) { + float angle_limit = 60.0f / 180.0f * M_PI; -#if 0 -/* Angle test */ -bool loop_is_corner(BMLoop *l, int cd_offset) -{ - BMVert *v = l->v; - BMEdge *e = v->e; - - float2 value = *BM_ELEM_CD_PTR(l, cd_offset); - float limit = 0.01; - - BMLoop *outer1 = nullptr, *outer2 = nullptr; - do { - BMLoop *l2 = e->l; - if (!l2) { - continue; - } - - do { - BMLoop *l3 = l2->v == v ? l2 : l2->next; - float2 value3 = *BM_ELEM_CD_PTR(l3, cd_offset); - - if (!prop_eq(value, value3, limit)) { - continue; - } - - bool outer = true; - - BMLoop *l4 = l2->radial_next; - while (l4 != l2) { - BMLoop *l5 = l4->v == v ? l4 : l4->next; - float2 value5 = *BM_ELEM_CD_PTR(l5, cd_offset); - - if (prop_eq(value, value5, limit)) { - outer = false; - break; - } - l4 = l4->radial_next; - } - - if (outer) { - BMLoop *l3 = l2->v == v ? l2->next : l2; - - if (!outer1) { - outer1 = l3; - } - else if (!outer2 && l3 != outer2) { - outer2 = l3; - } - - break; - } - } while ((l2 = l2->radial_next) != e->l); - } while ((e = BM_DISK_EDGE_NEXT(e, v)) != l->e); - - if (!outer1 || !outer2) { - return outer1 != nullptr; + return loop_is_corner_angle(l, cd_uv, limit, angle_limit, ldata); } - float2 t1 = *BM_ELEM_CD_PTR(outer1, cd_offset) - value; - float2 t2 = *BM_ELEM_CD_PTR(outer2, cd_offset) - value; - - normalize_v2(t1); - normalize_v2(t2); - float angle_limit = 110.0f / 180.0f * M_PI; - float angle = saacos(dot_v2v2(t1, t2)); - - return angle < angle_limit; -} +#ifdef TEST_UV_CORNER_CALC + if (ret) { + // l->v->head.hflag |= BM_ELEM_SELECT; + } #endif + return ret; +} + template static void corner_interp(CustomDataLayer *layer, BMLoop *l, @@ -4597,8 +4707,10 @@ static void corner_interp(CustomDataLayer *layer, float limit; if (layer->type == CD_PROP_FLOAT2) { - // limit = 0.01; - limit = calc_uv_snap_limit(loops[0], cd_offset); + /* Apparently 1/10th of average uv edge length is too small, + * muliply it to 1/2 + */ + limit = calc_uv_snap_limit(loops[0], cd_offset) * 4.0f; } else { /* Do not restrict to islands for non-UVs */ @@ -4610,7 +4722,7 @@ static void corner_interp(CustomDataLayer *layer, BMLoop *l3 = l2->next->v == l->v ? l2->next : l2->prev; T value3 = *BM_ELEM_CD_PTR(l3, cd_offset); - if (prop_eq(value, value3, 0.01)) { + if (prop_eq(value, value3, limit)) { T value2 = *BM_ELEM_CD_PTR(l2, cd_offset); sum += value2 * ws[i]; totsum += ws[i]; @@ -4629,8 +4741,12 @@ static void corner_interp(CustomDataLayer *layer, /* Interpolates loops surrounding a vertex, splitting any UV map by * island as appropriate and enforcing proper boundary conditions. */ -void interp_face_corners( - PBVH *pbvh, PBVHVertRef vertex, Span loops, Span ws, float factor) +void interp_face_corners(PBVH *pbvh, + PBVHVertRef vertex, + Span loops, + Span ws, + float factor, + int cd_vert_boundary) { if (BKE_pbvh_type(pbvh) != PBVH_BMESH) { return; /* Only for PBVH_BMESH. */ @@ -4694,14 +4810,20 @@ void interp_face_corners( /* Interpolate. */ if (loops.size() > 1) { + Vector corners; VertLoopSnapper corner_snap = {Span(ls), Span(layers)}; for (CustomDataLayer *layer : layers) { - Vector corners; + corners.clear(); if (layer->type == CD_PROP_FLOAT2) { for (BMLoop *l : ls) { - corners.append(loop_is_corner(l, layer->offset)); + /* Do not calculate corner here, use the stored corner flag. */ + bool corner = false; + // corner |= loop_is_corner(l, layer->offset); + corner |= BM_ELEM_CD_GET_INT(v, cd_vert_boundary) & SCULPT_CORNER_UV; + + corners.append(corner); } } diff --git a/source/blender/blenkernel/intern/pbvh_bmesh.cc b/source/blender/blenkernel/intern/pbvh_bmesh.cc index c696d0581f0..08602cbc173 100644 --- a/source/blender/blenkernel/intern/pbvh_bmesh.cc +++ b/source/blender/blenkernel/intern/pbvh_bmesh.cc @@ -2026,6 +2026,15 @@ void update_edge_boundary_bmesh(BMEdge *e, oldflag = BM_ELEM_CD_GET_INT(e, cd_edge_boundary); } +#if 0 + if (BM_ELEM_CD_GET_INT(e, cd_edge_boundary) & SCULPT_BOUNDARY_UV) { + e->head.hflag |= BM_ELEM_SELECT; + } + else { + e->head.hflag &= ~BM_ELEM_SELECT; + } +#endif + if (oldflag & SCULPT_BOUNDARY_UPDATE_SHARP_ANGLE) { if (e->l && e->l != e->l->radial_next && test_sharp_faces_bmesh(e->l->f, e->l->radial_next->f, sharp_angle_limit)) @@ -2062,7 +2071,10 @@ void update_edge_boundary_bmesh(BMEdge *e, } } // namespace blender::bke::pbvh -static void pbvh_bmesh_update_uv_boundary_intern(BMVert *v, int cd_boundary, int cd_uv) +static void pbvh_bmesh_update_uv_boundary_intern(BMVert *v, + int cd_boundary, + int cd_uv, + const CustomData *ldata) { int *boundflag = BM_ELEM_CD_PTR(v, cd_boundary); @@ -2078,7 +2090,7 @@ static void pbvh_bmesh_update_uv_boundary_intern(BMVert *v, int cd_boundary, int } float snap_limit = blender::bke::sculpt::calc_uv_snap_limit(l, cd_uv); - snap_limit *= snap_limit; + float snap_limit_sqr = snap_limit * snap_limit; float2 uv1a = *BM_ELEM_CD_PTR(l->v == v ? l : l->next, cd_uv); float2 uv1b = *BM_ELEM_CD_PTR(l->v == v ? l->next : l, cd_uv); @@ -2087,13 +2099,14 @@ static void pbvh_bmesh_update_uv_boundary_intern(BMVert *v, int cd_boundary, int float2 uv2a = *BM_ELEM_CD_PTR(l->v == v ? l : l->next, cd_uv); float2 uv2b = *BM_ELEM_CD_PTR(l->v == v ? l->next : l, cd_uv); - if (len_squared_v2v2(uv1a, uv2a) > snap_limit || len_squared_v2v2(uv1b, uv2b) > snap_limit) { + if (len_squared_v2v2(uv1a, uv2a) > snap_limit_sqr || + len_squared_v2v2(uv1b, uv2b) > snap_limit_sqr) { *boundflag |= SCULPT_BOUNDARY_UV; } /* Corners are calculated from the number of distinct charts. */ BMLoop *l2 = l->v == v ? l : l->next; - if (blender::bke::sculpt::loop_is_corner(l2, cd_uv)) { + if (blender::bke::sculpt::loop_is_corner(l2, cd_uv, snap_limit, ldata)) { *boundflag |= SCULPT_CORNER_UV; *boundflag |= SCULPT_BOUNDARY_UV; } @@ -2121,7 +2134,7 @@ static void pbvh_bmesh_update_uv_boundary(BMVert *v, int cd_boundary, const Cust continue; } - pbvh_bmesh_update_uv_boundary_intern(v, cd_boundary, ldata->layers[i].offset); + pbvh_bmesh_update_uv_boundary_intern(v, cd_boundary, ldata->layers[i].offset, ldata); } } @@ -2144,6 +2157,15 @@ void update_vert_boundary_bmesh(int cd_faceset_offset, pbvh_bmesh_update_uv_boundary(v, cd_boundary_flag, ldata); } +#if 0 + if (BM_ELEM_CD_GET_INT(v, cd_boundary_flag) & SCULPT_BOUNDARY_UV) { + v->head.hflag |= BM_ELEM_SELECT; + } + else { + v->head.hflag &= ~BM_ELEM_SELECT; + } +#endif + int boundflag = BM_ELEM_CD_GET_INT(v, cd_boundary_flag) & (SCULPT_BOUNDARY_UV | SCULPT_CORNER_UV); diff --git a/source/blender/editors/sculpt_paint/paint_mask.cc b/source/blender/editors/sculpt_paint/paint_mask.cc index e92b2e48532..a0aae6ba553 100644 --- a/source/blender/editors/sculpt_paint/paint_mask.cc +++ b/source/blender/editors/sculpt_paint/paint_mask.cc @@ -1217,7 +1217,7 @@ static void sculpt_gesture_trim_geometry_generate(SculptGestureContext *sgcontex const float(*ob_imat)[4] = vc->obact->world_to_object; - /* Write vertices coordinatesSCULPT_GESTURE_TRIM_DIFFERENCE for the front face. */ + /* Write vertices coordinates for the front face. */ float(*positions)[3] = BKE_mesh_vert_positions_for_write(trim_operation->mesh); float depth_point[3]; diff --git a/source/blender/editors/sculpt_paint/sculpt.cc b/source/blender/editors/sculpt_paint/sculpt.cc index 1cba1ea2de2..71753c341a1 100644 --- a/source/blender/editors/sculpt_paint/sculpt.cc +++ b/source/blender/editors/sculpt_paint/sculpt.cc @@ -6428,6 +6428,7 @@ static void sculpt_stroke_update_step(bContext *C, if (dyntopo_spacing > 0.0f) { do_dyntopo = do_dyntopo && (ss->cache->stroke_distance_t - ss->cache->last_dyntopo_t) > dyntopo_spacing; + do_dyntopo = do_dyntopo || ss->cache->stroke_distance_t == 0.0f; } if (do_dyntopo) { diff --git a/source/blender/editors/sculpt_paint/sculpt_face_set.cc b/source/blender/editors/sculpt_paint/sculpt_face_set.cc index 2f8345d10cd..0073982fa19 100644 --- a/source/blender/editors/sculpt_paint/sculpt_face_set.cc +++ b/source/blender/editors/sculpt_paint/sculpt_face_set.cc @@ -770,6 +770,7 @@ static int sculpt_face_set_init_exec(bContext *C, wmOperator *op) SCULPT_undo_push_end(ob); if (ss->bm) { + SCULPT_vertex_random_access_ensure(ss); SCULPT_face_random_access_ensure(ss); BKE_sculpt_face_sets_ensure(ob); diff --git a/source/blender/editors/sculpt_paint/sculpt_smooth.cc b/source/blender/editors/sculpt_paint/sculpt_smooth.cc index d5dd4bf6c4f..7a40262b3cb 100644 --- a/source/blender/editors/sculpt_paint/sculpt_smooth.cc +++ b/source/blender/editors/sculpt_paint/sculpt_smooth.cc @@ -190,9 +190,10 @@ static void SCULPT_neighbor_coords_average_interior_ex(SculptSession *ss, is_boundary2 = SCULPT_vertex_is_boundary(ss, ni.vertex, bound_type); } - const eSculptBoundary smooth_types = !ss->hard_edge_mode ? SCULPT_BOUNDARY_FACE_SET | uvflag | - SCULPT_BOUNDARY_SEAM : - uvflag | SCULPT_BOUNDARY_SEAM; + const eSculptBoundary smooth_types = (!ss->hard_edge_mode ? + SCULPT_BOUNDARY_FACE_SET | SCULPT_BOUNDARY_SEAM : + SCULPT_BOUNDARY_SEAM) | + uvflag; if (is_boundary2) { totboundary++; @@ -275,7 +276,8 @@ static void SCULPT_neighbor_coords_average_interior_ex(SculptSession *ss, if constexpr (smooth_face_corners) { if (is_bmesh && !smooth_origco) { - blender::bke::sculpt::interp_face_corners(ss->pbvh, vertex, loops, ws, factor); + blender::bke::sculpt::interp_face_corners( + ss->pbvh, vertex, loops, ws, factor, ss->attrs.boundary_flags->bmesh_cd_offset); } } @@ -714,7 +716,8 @@ void SCULPT_bmesh_four_neighbor_average(SculptSession *ss, ws[i] /= totw; } - blender::bke::sculpt::interp_face_corners(ss->pbvh, vertex, loops, ws, factor); + blender::bke::sculpt::interp_face_corners( + ss->pbvh, vertex, loops, ws, factor, ss->attrs.boundary_flags->bmesh_cd_offset); } eSculptCorner corner_type = SCULPT_CORNER_MESH | SCULPT_CORNER_SHARP_MARK; @@ -722,7 +725,7 @@ void SCULPT_bmesh_four_neighbor_average(SculptSession *ss, corner_type |= SCULPT_CORNER_FACE_SET; } - if (corner != SCULPT_CORNER_NONE) { + if (corner & corner_type) { interp_v3_v3v3(avg, avg, SCULPT_vertex_co_get(ss, vertex), hard_corner_pin); } -- 2.30.2 From a59c5c06834881c8d8e1e888916cd3cb5d244245 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Tue, 27 Jun 2023 10:57:38 -0700 Subject: [PATCH 205/279] temp-sculpt-dyntopo: Fix crashed in last commit --- source/blender/editors/sculpt_paint/sculpt.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/blender/editors/sculpt_paint/sculpt.cc b/source/blender/editors/sculpt_paint/sculpt.cc index 71753c341a1..616e740772a 100644 --- a/source/blender/editors/sculpt_paint/sculpt.cc +++ b/source/blender/editors/sculpt_paint/sculpt.cc @@ -6427,8 +6427,8 @@ static void sculpt_stroke_update_step(bContext *C, if (dyntopo_spacing > 0.0f) { do_dyntopo = do_dyntopo && - (ss->cache->stroke_distance_t - ss->cache->last_dyntopo_t) > dyntopo_spacing; - do_dyntopo = do_dyntopo || ss->cache->stroke_distance_t == 0.0f; + (ss->cache->stroke_distance_t == 0.0f || + ss->cache->stroke_distance_t - ss->cache->last_dyntopo_t) > dyntopo_spacing; } if (do_dyntopo) { -- 2.30.2 From 21f7238e76871d3b0bc083c1202524c96ffcc138 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Wed, 28 Jun 2023 00:22:09 -0700 Subject: [PATCH 206/279] temp-sculpt-dyntopo: Remove dyntopo inherit flags from UI * Inherit flags are now hardcoded in BKE_brush_dyntopo_inherit_flags. * DynTopoSettings.inherit has been removed from RNA. * There are now "overriden" properties in DynTopoSetting's RNA, e.g. is_subdivide_overriden. This is used to show a warning checkbox when a setting is local to a brush (as in the simplify or snake hook brushes). --- .../startup/bl_ui/properties_paint_common.py | 19 +++--- scripts/startup/bl_ui/space_view3d_toolbar.py | 8 ++- source/blender/blenkernel/BKE_brush.h | 6 ++ source/blender/blenkernel/intern/brush.cc | 27 ++++++-- source/blender/blenkernel/intern/dyntopo.cc | 2 - .../blenkernel/intern/dyntopo_intern.hh | 2 - source/blender/draw/intern/draw_pbvh.cc | 1 - .../editors/sculpt_paint/sculpt_api.cc | 35 +---------- source/blender/makesrna/intern/rna_brush.c | 62 ++++++++++++++++--- 9 files changed, 100 insertions(+), 62 deletions(-) diff --git a/scripts/startup/bl_ui/properties_paint_common.py b/scripts/startup/bl_ui/properties_paint_common.py index 69588664bfb..6b1d4d1d505 100644 --- a/scripts/startup/bl_ui/properties_paint_common.py +++ b/scripts/startup/bl_ui/properties_paint_common.py @@ -122,29 +122,32 @@ class UnifiedPaintPanel: def get_dyntopo_prop(context, brush, prop_name): sculpt = context.tool_settings.sculpt - if prop_name.upper() in brush.dyntopo.inherit: + if not getattr(brush.dyntopo, "is_" + prop_name + "_overridden"): return getattr(sculpt.dyntopo, prop_name) else: return getattr(brush.dyntopo, prop_name) @staticmethod def prop_unified_dyntopo( - layout, context, brush, prop_name, text=None, show_inherit=True, expand=False + layout, context, brush, prop_name, text=None, expand=False ): sculpt = context.tool_settings.sculpt - if prop_name.upper() in brush.dyntopo.inherit: + override_name = "is_" + prop_name + "_overridden" + inherit = not getattr(brush.dyntopo, override_name) + + if inherit: final_dyntopo = sculpt.dyntopo else: final_dyntopo = brush.dyntopo - if show_inherit: - layout = layout.row(align=True) - + layout = layout.row(align=True) layout.prop(final_dyntopo, prop_name, text=text, expand=expand) - if show_inherit: - layout.prop_enum(brush.dyntopo, "inherit", prop_name.upper(), text="") + if not inherit: + layout.prop( + brush.dyntopo, override_name, text="", icon="ERROR", emboss=False + ) @staticmethod def prop_unified_color(parent, context, brush, prop_name, *, text=None): diff --git a/scripts/startup/bl_ui/space_view3d_toolbar.py b/scripts/startup/bl_ui/space_view3d_toolbar.py index ab29e066404..e24b86fb3bd 100644 --- a/scripts/startup/bl_ui/space_view3d_toolbar.py +++ b/scripts/startup/bl_ui/space_view3d_toolbar.py @@ -1054,8 +1054,12 @@ class VIEW3D_PT_sculpt_options(Panel, View3DPaintPanel): col = layout.column(heading="Display", align=True) col.prop(sculpt, "show_low_resolution") col.prop(sculpt, "use_sculpt_delay_updates") - col.prop(sculpt, "use_deform_only") - col.prop(tool_settings, "show_origco") #NotForPR + col.prop(sculpt, "use_deform_only") + + import bpy + if bpy.app.debug_value == 889: + col.prop(tool_settings, "show_origco") + col.prop(context.object.data, "sculpt_ignore_uvs") col.label(text="Smooth Boundaries") diff --git a/source/blender/blenkernel/BKE_brush.h b/source/blender/blenkernel/BKE_brush.h index de97fcfd06a..956d4b6d6f3 100644 --- a/source/blender/blenkernel/BKE_brush.h +++ b/source/blender/blenkernel/BKE_brush.h @@ -67,6 +67,12 @@ struct Brush *BKE_brush_first_search(struct Main *bmain, eObjectMode ob_mode); void BKE_brush_sculpt_reset(struct Brush *brush); +/* Which dyntopo settings are inherited by this brush from scene + * defaults. In most cases this is everything except for the + * local dyntopo disable flag. + */ +int BKE_brush_dyntopo_inherit_flags(struct Brush *brush); + /** * Create a set of grease pencil Drawing presets. */ diff --git a/source/blender/blenkernel/intern/brush.cc b/source/blender/blenkernel/intern/brush.cc index ab209dd6449..7bc63c21349 100644 --- a/source/blender/blenkernel/intern/brush.cc +++ b/source/blender/blenkernel/intern/brush.cc @@ -1884,9 +1884,6 @@ void BKE_brush_sculpt_reset(Brush *br) case SCULPT_TOOL_SNAKE_HOOK: br->alpha = 1.0f; br->rake_factor = 1.0f; - br->dyntopo.inherit = ~(DYNTOPO_INHERIT_SPACING | DYNTOPO_INHERIT_SUBDIVIDE | - DYNTOPO_INHERIT_COLLAPSE | DYNTOPO_INHERIT_RADIUS_SCALE | - DYNTOPO_INHERIT_REPEAT | DYNTOPO_INHERIT_CLEANUP); br->dyntopo.flag |= DYNTOPO_SUBDIVIDE | DYNTOPO_COLLAPSE | DYNTOPO_CLEANUP; br->dyntopo.spacing = 0; br->dyntopo.radius_scale = 1.25; @@ -1979,8 +1976,6 @@ void BKE_brush_sculpt_reset(Brush *br) br->curve_preset = BRUSH_CURVE_SMOOTHER; break; case SCULPT_TOOL_SIMPLIFY: - br->dyntopo.inherit = ~(DYNTOPO_INHERIT_COLLAPSE | DYNTOPO_INHERIT_SUBDIVIDE | - DYNTOPO_INHERIT_CLEANUP); br->dyntopo.flag |= DYNTOPO_COLLAPSE | DYNTOPO_SUBDIVIDE | DYNTOPO_CLEANUP; break; case SCULPT_TOOL_MASK: @@ -1990,6 +1985,8 @@ void BKE_brush_sculpt_reset(Brush *br) break; } + br->dyntopo.inherit = BKE_brush_dyntopo_inherit_flags(br); + /* Cursor colors */ /* Default Alpha */ @@ -2739,3 +2736,23 @@ float BKE_brush_hard_corner_pin_get(const Scene *scene, const Brush *brush) return brush->hard_corner_pin; } + +int BKE_brush_dyntopo_inherit_flags(Brush *brush) +{ + if (!brush) { + return DYNTOPO_INHERIT_BITMASK; + } + + if (brush->sculpt_tool == SCULPT_TOOL_SIMPLIFY) { + return ~(DYNTOPO_INHERIT_COLLAPSE | DYNTOPO_INHERIT_SUBDIVIDE | DYNTOPO_INHERIT_CLEANUP | + DYNTOPO_INHERIT_DISABLED); + } + if (brush->sculpt_tool == SCULPT_TOOL_SNAKE_HOOK) { + return ~(DYNTOPO_INHERIT_SPACING | DYNTOPO_INHERIT_SUBDIVIDE | DYNTOPO_INHERIT_COLLAPSE | + DYNTOPO_INHERIT_RADIUS_SCALE | DYNTOPO_INHERIT_REPEAT | DYNTOPO_INHERIT_CLEANUP | + DYNTOPO_INHERIT_DISABLED | DYNTOPO_INHERIT_QUALITY); + } + + /* Inherit everything from scene defaults */ + return DYNTOPO_INHERIT_BITMASK & ~DYNTOPO_INHERIT_DISABLED; +} diff --git a/source/blender/blenkernel/intern/dyntopo.cc b/source/blender/blenkernel/intern/dyntopo.cc index 757c4b97336..ba27f196702 100644 --- a/source/blender/blenkernel/intern/dyntopo.cc +++ b/source/blender/blenkernel/intern/dyntopo.cc @@ -2392,8 +2392,6 @@ bool remesh_topology(BrushTester *brush_tester, return eq_ctx.modified; } -float dyntopo_params[5] = {5.0f, 1.0f, 4.0f}; - void detail_size_set(PBVH *pbvh, float detail_size, float detail_range) { detail_range = max_ff(detail_range, 0.1f); diff --git a/source/blender/blenkernel/intern/dyntopo_intern.hh b/source/blender/blenkernel/intern/dyntopo_intern.hh index eaaca617495..8c446c323de 100644 --- a/source/blender/blenkernel/intern/dyntopo_intern.hh +++ b/source/blender/blenkernel/intern/dyntopo_intern.hh @@ -230,8 +230,6 @@ static void pbvh_bmesh_verify(PBVH *pbvh); struct EdgeQueue; -extern float dyntopo_params[5]; - struct EdgeQueueContext { blender::bke::dyntopo::BrushTester *brush_tester; SculptSession *ss; diff --git a/source/blender/draw/intern/draw_pbvh.cc b/source/blender/draw/intern/draw_pbvh.cc index 8e14003e5cc..10aef1c2e61 100644 --- a/source/blender/draw/intern/draw_pbvh.cc +++ b/source/blender/draw/intern/draw_pbvh.cc @@ -922,7 +922,6 @@ struct PBVHBatches { if (args->show_orig) { int cd_origco = CustomData_get_offset_named( &args->bm->vdata, CD_PROP_FLOAT3, ".sculpt_orig_co"); - printf("cd_orig_co: %d\n", cd_origco); foreach_bmesh([&](BMLoop *l) { *static_cast(GPU_vertbuf_raw_step(&access)) = *BM_ELEM_CD_PTR( diff --git a/source/blender/editors/sculpt_paint/sculpt_api.cc b/source/blender/editors/sculpt_paint/sculpt_api.cc index f8560185c97..ba986195d04 100644 --- a/source/blender/editors/sculpt_paint/sculpt_api.cc +++ b/source/blender/editors/sculpt_paint/sculpt_api.cc @@ -499,42 +499,9 @@ void SCULPT_ensure_persistent_layers(SculptSession *ss, Object *ob) } } -namespace blender::bke::dyntopo { -extern float dyntopo_params[5]; -} - void SCULPT_apply_dyntopo_settings(Scene *scene, SculptSession *ss, Sculpt *sculpt, Brush *brush) { using namespace blender::bke::dyntopo; - if (scene->id.properties) { - auto load_prop = [&](const char *name, int index) { - IDProperty *prop = IDP_GetPropertyFromGroup(scene->id.properties, name); - if (prop) { - float val = 0.0f; - if (prop->type == IDP_FLOAT) { - val = IDP_Float(prop); - } - else if (prop->type == IDP_DOUBLE) { - val = float(IDP_Double(prop)); - } - else { - return prop; - } - - printf("%s: Found %s (%.5f)\n", __func__, name, val); - - dyntopo_params[index] = val; - } - - return prop; - }; - - IDProperty *prop1 = load_prop("dparam1", 0); - load_prop("dparam2", 1); - load_prop("dparam3", 2); - load_prop("dparam4", 3); - load_prop("dparam5", 4); - } if (!brush) { ss->cached_dyntopo = sculpt->dyntopo; @@ -546,7 +513,7 @@ void SCULPT_apply_dyntopo_settings(Scene *scene, SculptSession *ss, Sculpt *scul DynTopoSettings *ds_final = &ss->cached_dyntopo; - ds_final->inherit = ds1->inherit; + ds_final->inherit = BKE_brush_dyntopo_inherit_flags(brush); ds_final->flag = 0; for (int i = 0; i < DYNTOPO_MAX_FLAGS; i++) { diff --git a/source/blender/makesrna/intern/rna_brush.c b/source/blender/makesrna/intern/rna_brush.c index 60bfc01bca7..d372ff9ee5d 100644 --- a/source/blender/makesrna/intern/rna_brush.c +++ b/source/blender/makesrna/intern/rna_brush.c @@ -6,6 +6,8 @@ * \ingroup RNA */ +#include +#include #include #include "DNA_brush_types.h" @@ -1244,6 +1246,32 @@ static char *rna_BrushCurvesSculptSettings_path(const PointerRNA *UNUSED(ptr)) return BLI_strdup("curves_sculpt_settings"); } +static int rna_DynTopoSettings_inherit_get(PointerRNA *ptr) +{ + Brush *brush; + + if (GS(ptr->owner_id->name) == ID_BR) { + brush = (Brush *)ptr->owner_id; + } + else if (GS(ptr->owner_id->name) == ID_SCE) { + /* Try and fetch the active brush. */ + Scene *scene = (Scene *)ptr->owner_id; + if (scene->toolsettings && scene->toolsettings->sculpt) { + brush = scene->toolsettings->sculpt->paint.brush; + } + } + else { + BLI_assert_unreachable(); + return 0; + } + + if (!brush) { + /* No brush, scene defines all settings. */ + return DYNTOPO_INHERIT_BITMASK; + } + + return BKE_brush_dyntopo_inherit_flags(brush); +} #else static void rna_def_brush_texture_slot(BlenderRNA *brna) @@ -1412,12 +1440,31 @@ static void rna_def_dyntopo_settings(BlenderRNA *brna) RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); RNA_def_property_ui_text(prop, "Mode", "Detail Mode"); - prop = RNA_def_property(srna, "inherit", PROP_ENUM, 0); - RNA_def_property_enum_sdna(prop, NULL, "inherit"); - RNA_def_property_enum_items(prop, rna_enum_brush_dyntopo_inherit); - RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); - RNA_def_property_flag(prop, PROP_ENUM_FLAG); - RNA_def_property_ui_text(prop, "Inherit", "Which default dyntopo settings to use"); + /* Auto-generate is_XXX_overridden members. */ + EnumPropertyItem *item = rna_enum_brush_dyntopo_inherit; + while (item->identifier) { + char identifier[128]; + char name[128]; + + /* Makesrna does not compile with BLI_snprintf. */ + sprintf(identifier, "is_%s_overridden", item->identifier); + sprintf(name, "Overridden"); + + /* Makesrna does not compile with BLI_str_tolower_ascii. */ + char *c = identifier; + while (*c) { + *c = tolower(*c); + c++; + } + + /* Use strdup to avoid memory leak warnings; identifier and name are global constants. */ + prop = RNA_def_property(srna, strdup(identifier), PROP_BOOLEAN, 0); + RNA_def_property_boolean_negative_sdna(prop, NULL, "inherit", item->value); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE | PROP_EDITABLE); /* Read-only. */ + RNA_def_property_ui_text(prop, strdup(name), "Brush overrides this setting."); + + item++; + } prop = RNA_def_property(srna, "radius_scale", PROP_FLOAT, PROP_FACTOR); RNA_def_property_float_sdna(prop, NULL, "radius_scale"); @@ -3368,8 +3415,7 @@ static void rna_def_brush(BlenderRNA *brna) RNA_def_property_float_sdna(prop, NULL, "autosmooth_projection"); RNA_def_property_range(prop, 0.0f, 1.0f); RNA_def_property_ui_range(prop, 0.0f, 1.0f, 0.001, 3); - RNA_def_property_ui_text( - prop, "Preserve Volume", "How much autosmooth should preserve volume."); + RNA_def_property_ui_text(prop, "Preserve Volume", "How much autosmooth should preserve volume."); RNA_def_property_update(prop, 0, "rna_Brush_update"); prop = RNA_def_property(srna, "sharp_angle_limit", PROP_FLOAT, PROP_ANGLE); -- 2.30.2 From a0e1b02cc60f593494a50009e3e7c325b9f9dea3 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Wed, 28 Jun 2023 03:30:12 -0700 Subject: [PATCH 207/279] temp-sculpt-dyntopo: Rewrite uv collapse and other fixes * Rewrote edge collapse's UV collapse code. It now uses C++ and is hopefully clearer and less buggy. * Fixed recently introduced dyntopo spacing bug. * Fixed vertex paint not working. --- source/blender/blenkernel/intern/dyntopo.cc | 49 ++- .../blenkernel/intern/dyntopo_collapse.cc | 381 +++++++++++------- source/blender/blenkernel/intern/paint.cc | 15 +- .../blender/blenkernel/intern/pbvh_intern.hh | 22 +- .../editors/sculpt_paint/paint_vertex.cc | 11 +- source/blender/editors/sculpt_paint/sculpt.cc | 2 +- 6 files changed, 307 insertions(+), 173 deletions(-) diff --git a/source/blender/blenkernel/intern/dyntopo.cc b/source/blender/blenkernel/intern/dyntopo.cc index ba27f196702..67d7f3071c2 100644 --- a/source/blender/blenkernel/intern/dyntopo.cc +++ b/source/blender/blenkernel/intern/dyntopo.cc @@ -226,7 +226,8 @@ static void surface_smooth_v_safe( /* Reproject attributes. */ if (reproject_cdata) { BKE_sculpt_reproject_cdata(ss, vertex, startco, startno, false); - blender::bke::sculpt::interp_face_corners(pbvh, vertex, loops, ws, fac, pbvh->cd_boundary_flag); + blender::bke::sculpt::interp_face_corners( + pbvh, vertex, loops, ws, fac, pbvh->cd_boundary_flag); } PBVH_CHECK_NAN(v->co); @@ -1709,7 +1710,17 @@ static bool cleanup_valence_3_4(EdgeQueueContext *ectx, PBVH *pbvh) */ BMEdge *e = v->e; do { + BMLoop *l2 = e->l; + + /* Double check edge boundary flags, in addition to the vertex boundary flag test above. */ + pbvh_check_edge_boundary_bmesh(pbvh, e); + if (BM_ELEM_CD_GET_INT(e, ectx->pbvh->cd_edge_boundary) & SCULPTVERT_ALL_BOUNDARY) { + bad = true; + break; + } + float len = calc_weighted_length(ectx, e->v1, e->v2, SPLIT); + if (sqrtf(len) > ectx->limit_len_max * 1.5f) { bad = true; break; @@ -2123,7 +2134,9 @@ void EdgeQueueContext::start() bool EdgeQueueContext::done() { - if (edge_heap.empty() || edge_heap.min_weight() > limit_len_min_sqr && edge_heap.max_weight() < limit_len_max_sqr) { + if (edge_heap.empty() || + edge_heap.min_weight() > limit_len_min_sqr && edge_heap.max_weight() < limit_len_max_sqr) + { return true; } @@ -2518,6 +2531,7 @@ void EdgeQueueContext::split_edge(BMEdge *e) dyntopo_add_flag(pbvh, e->v1, SCULPTFLAG_NEED_VALENCE); dyntopo_add_flag(pbvh, e->v2, SCULPTFLAG_NEED_VALENCE); + pbvh_boundary_update_bmesh(pbvh, e->v1); pbvh_boundary_update_bmesh(pbvh, e->v2); pbvh_boundary_update_bmesh(pbvh, e); @@ -2544,6 +2558,7 @@ void EdgeQueueContext::split_edge(BMEdge *e) } else { *BM_ELEM_CD_PTR(newv, pbvh->cd_boundary_flag) |= SCULPT_BOUNDARY_UV; + *BM_ELEM_CD_PTR(newv, pbvh->cd_boundary_flag) &= ~SCULPT_CORNER_UV; } /* Propagate current stroke id. */ @@ -3383,6 +3398,7 @@ template static void interp_prop_data( const void **src_blocks, const float *weights, int count, void *dst_block, int cd_offset) { + using namespace blender; SumT sum; if constexpr (std::is_same_v) { @@ -3393,13 +3409,28 @@ static void interp_prop_data( } for (int i = 0; i < count; i++) { - const T *value = static_cast(POINTER_OFFSET(src_blocks[i], cd_offset)); - sum += (*value) * weights[i]; + const T value = *static_cast(POINTER_OFFSET(src_blocks[i], cd_offset)); + + if constexpr (std::is_same_v) { + sum[0] += float(value[0]) * weights[0]; + sum[1] += float(value[1]) * weights[1]; + sum[2] += float(value[2]) * weights[2]; + sum[3] += float(value[3]) * weights[3]; + } + else { + sum += value * weights[i]; + } } if (count > 0) { T *dest = static_cast(POINTER_OFFSET(dst_block, cd_offset)); - *dest = sum; + + if constexpr (std::is_same_v) { + *dest = {uchar(sum[0]), uchar(sum[1]), uchar(sum[2]), uchar(sum[3])}; + } + else { + *dest = sum; + } } } @@ -3436,9 +3467,9 @@ void reproject_interp_data(CustomData *data, case CD_PROP_COLOR: interp_prop_data(src_blocks, weights, count, dst_block, layer->offset); break; - // case CD_PROP_BYTE_COLOR: - // interp_prop_data(src_blocks, weights, count, dst_block, layer->offset); - // break; + case CD_PROP_BYTE_COLOR: + interp_prop_data(src_blocks, weights, count, dst_block, layer->offset); + break; } } } @@ -3675,7 +3706,7 @@ void BKE_sculpt_reproject_cdata( int cur_vblock = 0; eCustomDataMask typemask = CD_MASK_PROP_FLOAT | CD_MASK_PROP_FLOAT2 | CD_MASK_PROP_FLOAT3 | - CD_MASK_PROP_BYTE_COLOR | CD_MASK_PROP_COLOR; + CD_MASK_PROP_COLOR | CD_PROP_BYTE_COLOR; if (!do_uvs) { typemask &= ~CD_MASK_PROP_FLOAT2; diff --git a/source/blender/blenkernel/intern/dyntopo_collapse.cc b/source/blender/blenkernel/intern/dyntopo_collapse.cc index f17bec428e8..7d92b1c8f99 100644 --- a/source/blender/blenkernel/intern/dyntopo_collapse.cc +++ b/source/blender/blenkernel/intern/dyntopo_collapse.cc @@ -5,21 +5,21 @@ #include "DNA_meshdata_types.h" #include "DNA_modifier_types.h" +#include "BLI_alloca.h" #include "BLI_array.hh" #include "BLI_asan.h" #include "BLI_buffer.h" +#include "BLI_compiler_attrs.h" +#include "BLI_compiler_compat.h" #include "BLI_index_range.hh" #include "BLI_map.hh" +#include "BLI_math.h" +#include "BLI_math_vector.hh" #include "BLI_set.hh" #include "BLI_task.hh" #include "BLI_timeit.hh" -#include "BLI_vector.hh" - -#include "BLI_alloca.h" -#include "BLI_compiler_attrs.h" -#include "BLI_compiler_compat.h" -#include "BLI_math.h" #include "BLI_utildefines.h" +#include "BLI_vector.hh" #include "PIL_time.h" #include "atomic_ops.h" @@ -40,6 +40,7 @@ #include #include #include +#include using blender::float2; using blender::float3; @@ -98,31 +99,190 @@ static bool vert_is_nonmanifold(BMVert *v) return false; } +template +static void snap_corner_data( + BMesh *bm, BMEdge *e, BMVert *v_del, Span ls, int cd_uv, bool snap_midpoint) +{ + using namespace blender; + + Vector, 4> blocks; + Vector, 4> weights; + Vector, 4> loops; + Vector final_loops; + int cur_set = 0; + + for (int i : ls.index_range()) { + ls[i]->head.index = -1; + ls[i]->next->head.index = -1; /* So we can test l->next without checking if it's inside ls.*/ + } + + /* Build snapping sets (of UV vertices). */ + for (BMLoop *l1 : ls) { + if (!snap_midpoint && l1->v == v_del) { + continue; + } + + if (l1->head.index != -1) { + continue; + } + + l1->head.index = cur_set; + + blocks.resize(cur_set + 1); + weights.resize(cur_set + 1); + loops.resize(cur_set + 1); + + final_loops.append(l1); + + blocks[cur_set].append(l1->head.data); + weights[cur_set].append(1.0f); + loops[cur_set].append(l1); + + T uv1 = *BM_ELEM_CD_PTR(l1, cd_uv); + + T uvnext = *BM_ELEM_CD_PTR(l1->next, cd_uv); + float limit = max_ff(math::distance_squared(uv1, uvnext) * 0.1f, 0.000025f); + + for (BMLoop *l2 : ls) { + T uv2 = *BM_ELEM_CD_PTR(l2, cd_uv); + + if (l2 == l1 || l2->head.index != -1) { + continue; + } + + if (math::distance_squared(uv1, uv2) < limit) { + float w = 1.0; + if (!snap_midpoint && l2->v == v_del) { + w = 0.0f; + } + + l2->head.index = cur_set; + blocks[cur_set].append(l2->head.data); + weights[cur_set].append(w); + loops[cur_set].append(l2); + } + } + + cur_set++; + } + + /* Merge sets */ + for (int i : final_loops.index_range()) { + if (!final_loops[i]) { + continue; + } + + BMLoop *final_l = final_loops[i]; + BMLoop *next_l = final_l->next; + + if (final_l->e != e || next_l->head.index == -1 || next_l->head.index == i) { + continue; + } + + int set = next_l->head.index; + + blocks[i].append(next_l->head.data); + weights[i].append(1.0f); + + for (void *block : blocks[set]) { + blocks[i].append(block); + } + for (float w : weights[set]) { + weights[i].append(w); + } + + for (BMLoop *l : loops[set]) { + l->head.index = i; + } + + /* Flag set as deleted. */ + final_loops[set] = nullptr; + } + + /* Perform final UV snapping. */ + for (int i : final_loops.index_range()) { + BMLoop *final_l = final_loops[i]; + + if (!final_l) { + continue; + } + + float totw = 0.0f; + for (float w : weights[i]) { + totw += w; + } + + if (totw == 0.0f) { + BLI_assert_unreachable(); + + /* You never know with topology; this could happen. . .in that case + * just use uniform weights. + */ + + for (int j : weights[i].index_range()) { + weights[i][j] = 1.0f; + } + + totw = float(weights[i].size()); + } + + totw = 1.0f / totw; + for (int j : weights[i].index_range()) { + weights[i][j] *= totw; + } + + SumT sum = {}; + + for (int j : blocks[i].index_range()) { + void *ptr = POINTER_OFFSET(blocks[i][j], cd_uv); + T value = *static_cast(ptr); + + if (std::is_same_v) { + sum[0] += float(value[0]) * weights[i][j]; + sum[1] += float(value[1]) * weights[i][j]; + sum[2] += float(value[2]) * weights[i][j]; + sum[3] += float(value[3]) * weights[i][j]; + } + else { + sum += value * weights[i][j]; + } + } + + if constexpr (std::is_same_v) { + *BM_ELEM_CD_PTR(final_l, cd_uv) = sum; + } + else if constexpr (std::is_same_v) { + *BM_ELEM_CD_PTR(final_l, + cd_uv) = {uchar(sum[0]), uchar(sum[1]), uchar(sum[2]), uchar(sum[3])}; + } + } + + /* Copy snapped UVs to all loops. */ + for (BMLoop *l : ls) { + if (l->head.index == -1) { + printf("%s: Error: invalid uv set for loop\n", __func__); + continue; + } + + int set = l->head.index; + + if (final_loops[set] && l != final_loops[set]) { + *BM_ELEM_CD_PTR(l, cd_uv) = *BM_ELEM_CD_PTR(final_loops[set], cd_uv); + } + } +} + bool pbvh_bmesh_collapse_edge_uvs( PBVH *pbvh, BMEdge *e, BMVert *v_conn, BMVert *v_del, EdgeQueueContext *eq_ctx) { + BMesh *bm = pbvh->header.bm; + pbvh_check_vert_boundary_bmesh(pbvh, v_conn); pbvh_check_vert_boundary_bmesh(pbvh, v_del); int boundflag1 = BM_ELEM_CD_GET_INT(v_conn, pbvh->cd_boundary_flag); int boundflag2 = BM_ELEM_CD_GET_INT(v_del, pbvh->cd_boundary_flag); - int uvidx = pbvh->header.bm->ldata.typemap[CD_PROP_FLOAT2]; - CustomDataLayer *uv_layer = nullptr; - int totuv = 0; - - if (uvidx >= 0) { - uv_layer = pbvh->header.bm->ldata.layers + uvidx; - totuv = 0; - - while (uvidx < pbvh->header.bm->ldata.totlayer && - pbvh->header.bm->ldata.layers[uvidx].type == CD_PROP_FLOAT2) - { - uvidx++; - totuv++; - } - } - /*have to check edge flags directly, vertex flag test above isn't specific enough and can sometimes let bad edges through*/ if ((boundflag1 & SCULPT_BOUNDARY_SHARP_MARK) && (e->head.hflag & BM_ELEM_SMOOTH)) { @@ -149,7 +309,7 @@ bool pbvh_bmesh_collapse_edge_uvs( eCustomDataMask typemask = CD_MASK_PROP_ALL; CustomData_bmesh_interp_ex( - &pbvh->header.bm->vdata, v_blocks, v_ws, nullptr, 2, v_conn->head.data, typemask); + &bm->vdata, v_blocks, v_ws, nullptr, 2, v_conn->head.data, typemask); /* Restore node index. */ BM_ELEM_CD_SET_INT(v_conn, pbvh->cd_vert_node_offset, ni_conn); @@ -163,157 +323,64 @@ bool pbvh_bmesh_collapse_edge_uvs( } } -#if 0 if (!e->l) { return snap; } - /* Deal with UVs. */ + Vector ls; + + /* Append loops around edge first. */ BMLoop *l = e->l; - float ws[2]; - BMLoop *ls[2]; - const void *blocks[2]; - do { - if (l->v == v_conn) { - ls[0] = l; - ls[1] = l->next; - } - else { - ls[0] = l->next; - ls[1] = l; - } - - if (snap) { - ws[0] = ws[1] = 0.5f; - } - else { - ws[0] = l->v == v_conn ? 1.0f : 0.0f; - ws[1] = l->v == v_conn ? 0.0f : 0.0f; - } - - blocks[0] = ls[0]->head.data; - blocks[1] = ls[1]->head.data; - -# if 0 - CustomData_bmesh_interp(&bm->ldata, blocks, ws, nullptr, 2, l->head.data); - CustomData_bmesh_copy_data( - &bm->ldata, &pbvh->header.bm->ldata, l->head.data, &l->next->head.data); -# else - - PBVHVertRef vertex = {reinterpret_cast(v_conn)}; - blender::bke::sculpt::interp_face_corners( - pbvh, vertex, Span(ls, 2), Span(ws, 2), 1.0f, pbvh->cd_boundary_flag); -# endif + ls.append_non_duplicates(l); } while ((l = l->radial_next) != e->l); - return snap; -#endif - - const float limit = 0.005; - BMLoop *l = e->l; - + /* Now find loops around e->v1 and e->v2. */ for (BMVert *v : std::array({e->v1, e->v2})) { - BMEdge *e2 = v->e; - - if (!e2) { - continue; - } - + BMEdge *e = v->e; do { - BMLoop *l2 = e2->l; + BMLoop *l = e->l; - if (!l2) { + if (!l) { continue; } do { - BMLoop *l3 = l2->v != v ? l2->next : l2; + BMLoop *uv_l = l->v == v ? l : l->next; - /* store visit bits for each uv layer in l3->head.index */ - l3->head.index = 0; - } while ((l2 = l2->radial_next) != e2->l); - } while ((e2 = BM_DISK_EDGE_NEXT(e2, v)) != v->e); + ls.append_non_duplicates(uv_l); + } while ((l = l->radial_next) != e->l); + } while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e); } - float(*uv)[2] = static_cast(BLI_array_alloca(uv, 4 * totuv)); + for (int i : IndexRange(bm->ldata.totlayer)) { + CustomDataLayer *layer = &bm->ldata.layers[i]; - l = e->l; - if (!l) { - return snap; + if (layer->flag & CD_FLAG_ELEM_NOINTERP) { + continue; + } + + switch (layer->type) { + case CD_PROP_FLOAT: + snap_corner_data>(bm, e, v_del, ls, layer->offset, snap); + break; + case CD_PROP_FLOAT2: + snap_corner_data(bm, e, v_del, ls, layer->offset, snap); + break; + case CD_PROP_FLOAT3: + snap_corner_data(bm, e, v_del, ls, layer->offset, snap); + break; + case CD_PROP_COLOR: + snap_corner_data(bm, e, v_del, ls, layer->offset, snap); + break; + case CD_PROP_BYTE_COLOR: + snap_corner_data(bm, e, v_del, ls, layer->offset, snap); + break; + default: + break; + } } - do { - const void *ls2[2] = {l->head.data, l->next->head.data}; - float ws2[2] = {0.5f, 0.5f}; - - if (!snap) { - const int axis = l->v == v_del ? 0 : 1; - - ws2[axis] = 0.0f; - ws2[axis ^ 1] = 1.0f; - } - - for (int step = 0; uv_layer && step < 2; step++) { - BMLoop *l1 = step ? l : l->next; - - for (int k = 0; k < totuv; k++) { - float *luv = (float *)BM_ELEM_CD_GET_VOID_P(l1, uv_layer[k].offset); - - copy_v2_v2(uv[k * 2 + step], luv); - } - } - - CustomData_bmesh_interp(&pbvh->header.bm->ldata, ls2, ws2, nullptr, 2, l->head.data); - CustomData_bmesh_copy_data( - &pbvh->header.bm->ldata, &pbvh->header.bm->ldata, l->head.data, &l->next->head.data); - - for (int step = 0; totuv >= 0 && step < 2; step++) { - BMVert *v = step ? l->next->v : l->v; - BMLoop *l1 = step ? l->next : l; - BMEdge *e2 = v->e; - - do { - BMLoop *l2 = e2->l; - - if (!l2) { - continue; - } - - do { - BMLoop *l3 = l2->v != v ? l2->next : l2; - - if (!l3 || l3 == l1 || l3 == l || l3 == l->next) { - continue; - } - - for (int k = 0; k < totuv; k++) { - const int flag = 1 << k; - - if (l3->head.index & flag) { - continue; - } - - const int cd_uv = uv_layer[k].offset; - - float *luv1 = (float *)BM_ELEM_CD_GET_VOID_P(l1, cd_uv); - float *luv2 = (float *)BM_ELEM_CD_GET_VOID_P(l3, cd_uv); - - float dx = luv2[0] - uv[k * 2 + step][0]; - float dy = luv2[1] - uv[k * 2 + step][1]; - - float delta = dx * dx + dy * dy; - - if (delta < limit) { - l3->head.index |= flag; - copy_v2_v2(luv2, luv1); - } - } - } while ((l2 = l2->radial_next) != e2->l); - } while ((e2 = BM_DISK_EDGE_NEXT(e2, v)) != v->e); - } - } while ((l = l->radial_next) != e->l); - return snap; } @@ -437,12 +504,17 @@ BMVert *EdgeQueueContext::collapse_edge(PBVH *pbvh, BMEdge *e, BMVert *v1, BMVer int boundflag1 = BM_ELEM_CD_GET_INT(v1, pbvh->cd_boundary_flag); int boundflag2 = BM_ELEM_CD_GET_INT(v2, pbvh->cd_boundary_flag); + int e_boundflag = BM_ELEM_CD_GET_INT(e, pbvh->cd_edge_boundary); /* Don't collapse across boundaries. */ if ((boundflag1 & SCULPTVERT_ALL_BOUNDARY) != (boundflag2 & SCULPTVERT_ALL_BOUNDARY)) { return nullptr; } + if ((boundflag1 & SCULPTVERT_ALL_BOUNDARY) != (e_boundflag & SCULPTVERT_ALL_BOUNDARY)) { + return nullptr; + } + float w1 = mask_cb ? 1.0f - mask_cb({reinterpret_cast(v1)}, mask_cb_data) : 0.0f; float w2 = mask_cb ? 1.0f - mask_cb({reinterpret_cast(v2)}, mask_cb_data) : 0.0f; @@ -450,8 +522,7 @@ BMVert *EdgeQueueContext::collapse_edge(PBVH *pbvh, BMEdge *e, BMVert *v1, BMVer bool corner2 = (boundflag2 & SCULPTVERT_ALL_CORNER) || w2 >= 0.85; /* We allow two corners of the same type[s] to collapse */ - if ((boundflag1 & SCULPTVERT_ALL_CORNER) != (boundflag2 & SCULPTVERT_ALL_CORNER)) - { + if ((boundflag1 & SCULPTVERT_ALL_CORNER) != (boundflag2 & SCULPTVERT_ALL_CORNER)) { return nullptr; } diff --git a/source/blender/blenkernel/intern/paint.cc b/source/blender/blenkernel/intern/paint.cc index 00ce7434947..4ffc9d58341 100644 --- a/source/blender/blenkernel/intern/paint.cc +++ b/source/blender/blenkernel/intern/paint.cc @@ -2720,7 +2720,19 @@ static PBVH *build_pbvh_from_regular_mesh(Object *ob, Mesh *me_eval_deform) PBVH *pbvh = ob->sculpt->pbvh = BKE_pbvh_new(PBVH_FACES); + BKE_sculpt_ensure_sculpt_layers(ob); BKE_sculpt_init_flags_valence(ob, pbvh, me->totvert, true); + BKE_sculpt_ensure_origco(ob); + + Mesh *mesh = static_cast(ob->data); + Span positions = mesh->vert_positions(); + Span normals = mesh->vert_normals(); + + for (int i = 0; i < mesh->totvert; i++) { + blender::bke::paint::vertex_attr_set({i}, ss->attrs.orig_co, positions[i]); + blender::bke::paint::vertex_attr_set({i}, ss->attrs.orig_no, normals[i]); + } + sculpt_check_face_areas(ob, pbvh); BKE_sculptsession_update_attr_refs(ob); @@ -4752,8 +4764,7 @@ void interp_face_corners(PBVH *pbvh, return; /* Only for PBVH_BMESH. */ } - eCustomDataMask mask = CD_MASK_PROP_FLOAT | CD_MASK_PROP_FLOAT2 | CD_MASK_PROP_FLOAT3 | - CD_MASK_PROP_COLOR | CD_MASK_PROP_BYTE_COLOR; + eCustomDataMask mask = CD_MASK_PROP_FLOAT2; BMesh *bm = BKE_pbvh_get_bmesh(pbvh); BMVert *v = reinterpret_cast(vertex.i); diff --git a/source/blender/blenkernel/intern/pbvh_intern.hh b/source/blender/blenkernel/intern/pbvh_intern.hh index 9f666059024..9ddf33c5389 100644 --- a/source/blender/blenkernel/intern/pbvh_intern.hh +++ b/source/blender/blenkernel/intern/pbvh_intern.hh @@ -422,7 +422,7 @@ void pbvh_bmesh_check_nodes_simple(PBVH *pbvh); void bke_pbvh_insert_face_finalize(PBVH *pbvh, BMFace *f, const int ni); void bke_pbvh_insert_face(PBVH *pbvh, struct BMFace *f); -BLI_INLINE bool pbvh_check_vert_boundary_bmesh(PBVH *pbvh, struct BMVert *v) +inline bool pbvh_check_vert_boundary_bmesh(PBVH *pbvh, struct BMVert *v) { int flag = BM_ELEM_CD_GET_INT(v, pbvh->cd_boundary_flag); @@ -447,6 +447,26 @@ BLI_INLINE bool pbvh_check_vert_boundary_bmesh(PBVH *pbvh, struct BMVert *v) return false; } +inline bool pbvh_check_edge_boundary_bmesh(PBVH *pbvh, struct BMEdge *e) +{ + int flag = BM_ELEM_CD_GET_INT(e, pbvh->cd_edge_boundary); + + if (flag & (SCULPT_BOUNDARY_NEEDS_UPDATE | SCULPT_BOUNDARY_UPDATE_UV | + SCULPT_BOUNDARY_UPDATE_SHARP_ANGLE)) + { + blender::bke::pbvh::update_edge_boundary_bmesh(e, + pbvh->cd_faceset_offset, + pbvh->cd_edge_boundary, + pbvh->cd_flag, + pbvh->cd_valence, + &pbvh->header.bm->ldata, + pbvh->sharp_angle_limit); + return true; + } + + return false; +} + void pbvh_bmesh_check_other_verts(PBVHNode *node); void pbvh_bmesh_normals_update(PBVH *pbvh, blender::Span nodes); diff --git a/source/blender/editors/sculpt_paint/paint_vertex.cc b/source/blender/editors/sculpt_paint/paint_vertex.cc index 1c5818a0fbf..45da463aeff 100644 --- a/source/blender/editors/sculpt_paint/paint_vertex.cc +++ b/source/blender/editors/sculpt_paint/paint_vertex.cc @@ -1203,6 +1203,12 @@ static void vwpaint_init_stroke(Depsgraph *depsgraph, Object *ob) if (!ss->cache) { ss->cache = (StrokeCache *)MEM_callocN(sizeof(StrokeCache), "stroke cache"); } + + SCULPT_stroke_id_next(ob); + if (ss->pbvh) { + BKE_pbvh_update_bounds(ss->pbvh, PBVH_UpdateBB); + blender::bke::pbvh::on_stroke_start(ss->pbvh); + } } static void vertex_paint_init_stroke(Scene *scene, Depsgraph *depsgraph, Object *ob) @@ -1660,11 +1666,6 @@ static void vwpaint_update_cache_invariants( cache->is_last_valid = false; cache->accum = true; - - SCULPT_stroke_id_next(ob); - if (ss->pbvh) { - blender::bke::pbvh::on_stroke_start(ss->pbvh); - } } /* Initialize the stroke cache variants from operator properties */ diff --git a/source/blender/editors/sculpt_paint/sculpt.cc b/source/blender/editors/sculpt_paint/sculpt.cc index 616e740772a..ad751df46d3 100644 --- a/source/blender/editors/sculpt_paint/sculpt.cc +++ b/source/blender/editors/sculpt_paint/sculpt.cc @@ -6428,7 +6428,7 @@ static void sculpt_stroke_update_step(bContext *C, if (dyntopo_spacing > 0.0f) { do_dyntopo = do_dyntopo && (ss->cache->stroke_distance_t == 0.0f || - ss->cache->stroke_distance_t - ss->cache->last_dyntopo_t) > dyntopo_spacing; + ss->cache->stroke_distance_t - ss->cache->last_dyntopo_t > dyntopo_spacing); } if (do_dyntopo) { -- 2.30.2 From 32b40dba50f604a939705820daf5ad842669b85a Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Wed, 28 Jun 2023 05:02:05 -0700 Subject: [PATCH 208/279] temp-sculpt-dyntopo: Grey out overriden dyntopo settings --- .../startup/bl_ui/properties_paint_common.py | 1 + .../editors/sculpt_paint/sculpt_smooth.cc | 61 +++++++++++++++++-- 2 files changed, 57 insertions(+), 5 deletions(-) diff --git a/scripts/startup/bl_ui/properties_paint_common.py b/scripts/startup/bl_ui/properties_paint_common.py index 6b1d4d1d505..b8ea138d16b 100644 --- a/scripts/startup/bl_ui/properties_paint_common.py +++ b/scripts/startup/bl_ui/properties_paint_common.py @@ -142,6 +142,7 @@ class UnifiedPaintPanel: final_dyntopo = brush.dyntopo layout = layout.row(align=True) + layout.enabled = inherit layout.prop(final_dyntopo, prop_name, text=text, expand=expand) if not inherit: diff --git a/source/blender/editors/sculpt_paint/sculpt_smooth.cc b/source/blender/editors/sculpt_paint/sculpt_smooth.cc index 7a40262b3cb..37ac988e375 100644 --- a/source/blender/editors/sculpt_paint/sculpt_smooth.cc +++ b/source/blender/editors/sculpt_paint/sculpt_smooth.cc @@ -150,7 +150,7 @@ static void SCULPT_neighbor_coords_average_interior_ex(SculptSession *ss, } float total = 0.0f; - int totboundary = 0, totsharp = 0; + int totboundary = 0; Vector ws; Vector loops; @@ -176,6 +176,61 @@ static void SCULPT_neighbor_coords_average_interior_ex(SculptSession *ss, } while ((l = l->radial_next) != e->l); }; +#if 0 + if (weighted && ss->bm && ss->bm->ldata.typemap[CD_PROP_FLOAT2] != -1) { + SculptVertexNeighborIter ni; + BMVert *v = reinterpret_cast(vertex.i); + int cd_uv = ss->bm->ldata.layers[ss->bm->ldata.typemap[CD_PROP_FLOAT2]].offset; + float totarea = 0.0f; + int count = 0; + float(*points)[2] = (float(*)[2])BLI_array_alloca(points, valence * 4); + float *weights = (float *)BLI_array_alloca(weights, valence * 2); + float2 v_uv = {}; + + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vertex, ni) { + BMEdge *e = reinterpret_cast(ni.edge.i); + + BMLoop *l = e->l; + if (l->v == v) { + l = l->radial_next; + } + + BMLoop *l1 = l->v == v ? l : l->next; + BMLoop *other_l1 = l->v == v ? l->next : l; + + v_uv += *BM_ELEM_CD_PTR(l1, cd_uv); + copy_v2_v2(points[count], BM_ELEM_CD_PTR(other_l1, cd_uv)); + count++; + } + SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); + + if (count == 0) { + return; + } + + v_uv /= float(count); + interp_weights_poly_v2(weights, points, count, v_uv); + + totarea = 0.0f; + for (int i = 0; i < count; i++) { + float w = weights[i]; + w = min_ff(max_ff(w, 0.0f), 1.0f); + totarea += w; + areas[i] = w; + } + + for (int i = 0; i < count; i++) { + // break; + if (totarea != 0.0f) { + areas[i] /= totarea; + } + else { + areas[i] = 1.0f / float(count); + } + } + } +#endif + SculptVertexNeighborIter ni; SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vertex, ni) { bool project_ok; @@ -199,10 +254,6 @@ static void SCULPT_neighbor_coords_average_interior_ex(SculptSession *ss, totboundary++; } - if (is_boundary2 & hard_flags) { - totsharp++; - } - /* Boundary vertices use only other boundary vertices. */ if (is_boundary) { project_ok = false; -- 2.30.2 From 657103d0cc6326b7ca811d70e62d0f21e4fc0afc Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Wed, 28 Jun 2023 09:45:59 -0700 Subject: [PATCH 209/279] temp-sculpt-dyntopo: Fix anchored and drag dot modes for dyntopo * Anchored and drag dot now work with dyntopo * They also now only undo the parts of the mesh they changed. --- source/blender/editors/sculpt_paint/sculpt.cc | 216 +++++++++--------- .../editors/sculpt_paint/sculpt_intern.hh | 1 + 2 files changed, 108 insertions(+), 109 deletions(-) diff --git a/source/blender/editors/sculpt_paint/sculpt.cc b/source/blender/editors/sculpt_paint/sculpt.cc index ad751df46d3..c201148c6a7 100644 --- a/source/blender/editors/sculpt_paint/sculpt.cc +++ b/source/blender/editors/sculpt_paint/sculpt.cc @@ -1787,18 +1787,67 @@ static void sculpt_rake_data_update(SculptRakeData *srd, const float co[3]) bool SCULPT_stroke_is_dynamic_topology(const SculptSession *ss, const Brush *brush) { return ((BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) && - - (!ss->cache || (!ss->cache->alt_smooth)) && - - /* Requires mesh restore, which doesn't work with - * dynamic-topology. */ - !(brush->flag & BRUSH_ANCHORED) && !(brush->flag & BRUSH_DRAG_DOT) && - SCULPT_TOOL_HAS_DYNTOPO(brush->sculpt_tool)); } /** \} */ +static Vector sculpt_pbvh_gather_generic_intern(Object *ob, + Sculpt *sd, + const Brush *brush, + bool use_original, + float radius, + PBVHNodeFlags flag) +{ + SculptSession *ss = ob->sculpt; + Vector nodes; + PBVHNodeFlags leaf_flag = PBVH_Leaf; + + if (flag & PBVH_TexLeaf) { + leaf_flag = PBVH_TexLeaf; + } + + /* Build a list of all nodes that are potentially within the cursor or brush's area of influence. + */ + if (!ss->cache || brush->falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE) { + SculptSearchSphereData data{}; + data.ss = ss; + data.sd = sd; + data.radius_squared = square_f(radius); + data.original = use_original; + data.ignore_fully_ineffective = brush->sculpt_tool != SCULPT_TOOL_MASK; + data.center = nullptr; + nodes = blender::bke::pbvh::search_gather(ss->pbvh, SCULPT_search_sphere_cb, &data, leaf_flag); + } + else { + DistRayAABB_Precalc dist_ray_to_aabb_precalc; + dist_squared_ray_to_aabb_v3_precalc( + &dist_ray_to_aabb_precalc, ss->cache->location, ss->cache->view_normal); + SculptSearchCircleData data{}; + data.ss = ss; + data.sd = sd; + data.radius_squared = square_f(radius); + data.original = use_original; + data.dist_ray_to_aabb_precalc = &dist_ray_to_aabb_precalc; + data.ignore_fully_ineffective = brush->sculpt_tool != SCULPT_TOOL_MASK; + nodes = blender::bke::pbvh::search_gather(ss->pbvh, SCULPT_search_circle_cb, &data, leaf_flag); + } + + return nodes; +} + +static Vector sculpt_pbvh_gather_generic( + Object *ob, Sculpt *sd, const Brush *brush, bool use_original, float radius) +{ + return sculpt_pbvh_gather_generic_intern(ob, sd, brush, use_original, radius, PBVH_Leaf); +} + +static Vector sculpt_pbvh_gather_texpaint( + Object *ob, Sculpt *sd, const Brush *brush, bool use_original, float radius) +{ + return sculpt_pbvh_gather_generic_intern(ob, sd, brush, use_original, radius, PBVH_TexLeaf); +} + /* -------------------------------------------------------------------- */ /** \name Sculpt Paint Mesh * \{ */ @@ -1828,19 +1877,6 @@ static void paint_mesh_restore_co_task_cb(void *__restrict userdata, break; } - SculptUndoNode *unode; - - if (ss->bm) { - unode = SCULPT_undo_push_node(data->ob, data->nodes[n], type); - } - else { - unode = SCULPT_undo_get_node(data->nodes[n], type); - } - - if (!unode) { - return; - } - switch (type) { case SCULPT_UNDO_MASK: BKE_pbvh_node_mark_update_mask(data->nodes[n]); @@ -1862,7 +1898,9 @@ static void paint_mesh_restore_co_task_cb(void *__restrict userdata, bool modified = false; - if (unode->type == SCULPT_UNDO_FACE_SETS) { + if (!ss->bm && type & SCULPT_UNDO_FACE_SETS) { + SculptUndoNode *unode = SCULPT_undo_get_node(data->nodes[n], SCULPT_UNDO_FACE_SETS); + SculptOrigFaceData orig_face_data; SCULPT_orig_face_data_unode_init(&orig_face_data, data->ob, unode); @@ -1876,7 +1914,10 @@ static void paint_mesh_restore_co_task_cb(void *__restrict userdata, } BKE_pbvh_face_iter_end(fd); - return; + + if (!(type & ~SCULPT_UNDO_FACE_SETS)) { + return; + } } BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { @@ -1939,7 +1980,15 @@ static void paint_mesh_restore_co(Sculpt *sd, Object *ob) SculptSession *ss = ob->sculpt; Brush *brush = BKE_paint_brush(&sd->paint); - Vector nodes = blender::bke::pbvh::search_gather(ss->pbvh, nullptr, nullptr); + float radius = (brush->flag & BRUSH_ANCHORED) ? + 1.25 * max_ff(ss->cache->last_anchored_radius, ss->cache->radius) : + ss->cache->radius; + + if (brush->flag & BRUSH_DRAG_DOT) { + radius = max_ff(radius, ss->cache->initial_radius) * 1.25f; + } + + Vector nodes = sculpt_pbvh_gather_generic(ob, sd, brush, true, radius); SculptThreadedTaskData data{}; data.sd = sd; @@ -3205,64 +3254,6 @@ static Vector sculpt_pbvh_gather_cursor_update(Object *ob, return blender::bke::pbvh::search_gather(ss->pbvh, SCULPT_search_sphere_cb, &data); } -static Vector sculpt_pbvh_gather_generic_intern(Object *ob, - Sculpt *sd, - const Brush *brush, - bool use_original, - float radius_scale, - PBVHNodeFlags flag) -{ - SculptSession *ss = ob->sculpt; - Vector nodes; - PBVHNodeFlags leaf_flag = PBVH_Leaf; - - if (flag & PBVH_TexLeaf) { - leaf_flag = PBVH_TexLeaf; - } - - /* Build a list of all nodes that are potentially within the cursor or brush's area of influence. - */ - if (brush->falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE) { - SculptSearchSphereData data{}; - data.ss = ss; - data.sd = sd; - data.radius_squared = square_f(ss->cache->radius * radius_scale); - data.original = use_original; - data.ignore_fully_ineffective = brush->sculpt_tool != SCULPT_TOOL_MASK; - data.center = nullptr; - nodes = blender::bke::pbvh::search_gather(ss->pbvh, SCULPT_search_sphere_cb, &data, leaf_flag); - } - else { - DistRayAABB_Precalc dist_ray_to_aabb_precalc; - dist_squared_ray_to_aabb_v3_precalc( - &dist_ray_to_aabb_precalc, ss->cache->location, ss->cache->view_normal); - SculptSearchCircleData data{}; - data.ss = ss; - data.sd = sd; - data.radius_squared = ss->cache ? square_f(ss->cache->radius * radius_scale) : - ss->cursor_radius; - data.original = use_original; - data.dist_ray_to_aabb_precalc = &dist_ray_to_aabb_precalc; - data.ignore_fully_ineffective = brush->sculpt_tool != SCULPT_TOOL_MASK; - nodes = blender::bke::pbvh::search_gather(ss->pbvh, SCULPT_search_circle_cb, &data, leaf_flag); - } - - return nodes; -} - -static Vector sculpt_pbvh_gather_generic( - Object *ob, Sculpt *sd, const Brush *brush, bool use_original, float radius_scale) -{ - return sculpt_pbvh_gather_generic_intern(ob, sd, brush, use_original, radius_scale, PBVH_Leaf); -} - -static Vector sculpt_pbvh_gather_texpaint( - Object *ob, Sculpt *sd, const Brush *brush, bool use_original, float radius_scale) -{ - return sculpt_pbvh_gather_generic_intern( - ob, sd, brush, use_original, radius_scale, PBVH_TexLeaf); -} - /* Calculate primary direction of movement for many brushes. */ static void calc_sculpt_normal(Sculpt *sd, Object *ob, Span nodes, float r_area_no[3]) { @@ -3962,12 +3953,6 @@ static void sculpt_topology_update(Sculpt *sd, mode |= PBVH_Cleanup; } - /* Force both subdivide and collapse for simplify brush. */ - // XXX done with inherit flags now - if (brush->sculpt_tool == SCULPT_TOOL_SIMPLIFY) { - // mode |= PBVH_Collapse | PBVH_Subdivide; - } - SculptSearchSphereData sdata{}; sdata.ss = ss, sdata.sd = sd, sdata.ob = ob; sdata.radius_squared = square_f(ss->cache->radius * radius_scale * 1.25f); @@ -4129,11 +4114,12 @@ static void do_brush_action(Sculpt *sd, const bool use_original = sculpt_tool_needs_original(brush->sculpt_tool) || !ss->cache->accum; const bool use_pixels = sculpt_needs_pbvh_pixels(paint_mode_settings, brush, ob); bool needs_original = use_original || SCULPT_automasking_needs_original(sd, brush); + needs_original |= (brush->flag & (BRUSH_ANCHORED | BRUSH_DRAG_DOT)); if (sculpt_needs_pbvh_pixels(paint_mode_settings, brush, ob)) { sculpt_pbvh_update_pixels(paint_mode_settings, ss, ob); - texnodes = sculpt_pbvh_gather_texpaint(ob, sd, brush, use_original, 1.0f); + texnodes = sculpt_pbvh_gather_texpaint(ob, sd, brush, use_original, ss->cache->radius); if (texnodes.is_empty()) { return; @@ -4162,7 +4148,8 @@ static void do_brush_action(Sculpt *sd, if (brush->sculpt_tool == SCULPT_TOOL_DRAW && brush->flag & BRUSH_ORIGINAL_NORMAL) { radius_scale = 2.0f; } - nodes = sculpt_pbvh_gather_generic(ob, sd, brush, use_original, radius_scale); + nodes = sculpt_pbvh_gather_generic( + ob, sd, brush, use_original, ss->cache->radius * radius_scale); } /* Draw Face Sets in draw mode makes a single undo push, in alt-smooth mode deforms the @@ -5582,33 +5569,40 @@ static void sculpt_update_cache_variants(bContext *C, Sculpt *sd, Object *ob, Po } } - if (BKE_brush_use_size_pressure(brush) && paint_supports_dynamic_size(brush, PAINT_MODE_SCULPT)) - { - cache->radius = sculpt_brush_dynamic_size_get(brush, cache, cache->initial_radius); - cache->dyntopo_pixel_radius = sculpt_brush_dynamic_size_get( - brush, cache, ups->initial_pixel_radius); - } - else { - cache->radius = cache->initial_radius; - cache->dyntopo_pixel_radius = ups->initial_pixel_radius; - } - sculpt_update_cache_paint_variants(cache, brush); - cache->radius_squared = cache->radius * cache->radius; - if (brush->flag & BRUSH_ANCHORED) { /* True location has been calculated as part of the stroke system already here. */ if (brush->flag & BRUSH_EDGE_TO_EDGE) { RNA_float_get_array(ptr, "location", cache->true_location); } + cache->last_anchored_radius = cache->radius; + cache->radius = paint_calc_object_space_radius( cache->vc, cache->true_location, ups->pixel_radius); - cache->radius_squared = cache->radius * cache->radius; copy_v3_v3(cache->anchored_location, cache->true_location); } + else if (BKE_brush_use_size_pressure(brush) && + paint_supports_dynamic_size(brush, PAINT_MODE_SCULPT)) + { + cache->radius = sculpt_brush_dynamic_size_get(brush, cache, cache->initial_radius); + } + else { + cache->radius = cache->initial_radius; + } + + if (BKE_brush_use_size_pressure(brush) && paint_supports_dynamic_size(brush, PAINT_MODE_SCULPT)) + { + cache->dyntopo_pixel_radius = sculpt_brush_dynamic_size_get( + brush, cache, ups->initial_pixel_radius); + } + else { + cache->dyntopo_pixel_radius = ups->initial_pixel_radius; + } + + cache->radius_squared = cache->radius * cache->radius; sculpt_update_brush_delta(ups, ob, brush); @@ -6402,6 +6396,11 @@ static void sculpt_stroke_update_step(bContext *C, } cache->stroke_distance_t += stroke_delta; + float radius = ss->cache->radius; + if (brush->flag & BRUSH_ANCHORED) { + radius = SCULPT_calc_radius(cache->vc, brush, CTX_data_scene(C), cache->true_location); + } + if (ELEM(ss->cached_dyntopo.mode, DYNTOPO_DETAIL_CONSTANT, DYNTOPO_DETAIL_MANUAL)) { float object_space_constant_detail = 1.0f / (ss->cached_dyntopo.constant_detail * mat4_to_scale(ob->object_to_world)); @@ -6409,14 +6408,12 @@ static void sculpt_stroke_update_step(bContext *C, ss->pbvh, object_space_constant_detail, DYNTOPO_DETAIL_RANGE); } else if (ss->cached_dyntopo.mode == DYNTOPO_DETAIL_BRUSH) { - blender::bke::dyntopo::detail_size_set(ss->pbvh, - ss->cache->radius * ss->cached_dyntopo.detail_percent / - 100.0f, - DYNTOPO_DETAIL_RANGE); + blender::bke::dyntopo::detail_size_set( + ss->pbvh, radius * ss->cached_dyntopo.detail_percent / 100.0f, DYNTOPO_DETAIL_RANGE); } else { /* Relative mode. */ blender::bke::dyntopo::detail_size_set(ss->pbvh, - (ss->cache->radius / ss->cache->dyntopo_pixel_radius) * + (radius / ss->cache->dyntopo_pixel_radius) * (ss->cached_dyntopo.detail_size * U.pixelsize), DYNTOPO_DETAIL_RANGE); } @@ -6425,7 +6422,8 @@ static void sculpt_stroke_update_step(bContext *C, bool do_dyntopo = SCULPT_stroke_is_dynamic_topology(ss, brush); - if (dyntopo_spacing > 0.0f) { + bool has_spacing = !(brush->flag & BRUSH_ANCHORED); + if (has_spacing && dyntopo_spacing > 0.0f) { do_dyntopo = do_dyntopo && (ss->cache->stroke_distance_t == 0.0f || ss->cache->stroke_distance_t - ss->cache->last_dyntopo_t > dyntopo_spacing); diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.hh b/source/blender/editors/sculpt_paint/sculpt_intern.hh index 5d4464bbd69..351fba5715a 100644 --- a/source/blender/editors/sculpt_paint/sculpt_intern.hh +++ b/source/blender/editors/sculpt_paint/sculpt_intern.hh @@ -672,6 +672,7 @@ struct StrokeCache { float initial_mouse[2]; /* Variants */ + float last_anchored_radius; /* Used by paint_mesh_restore_co. */ float radius; float radius_squared; float true_location[3]; -- 2.30.2 From 336c5b82a3e6bc24d90aebde936caf50d17f9d37 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Thu, 29 Jun 2023 15:54:24 -0700 Subject: [PATCH 210/279] temp-sculpt-dyntopo: Fix performance regressions --- .../blender/blenkernel/intern/customdata.cc | 43 ++++++++++++++++++- source/blender/blenkernel/intern/dyntopo.cc | 4 +- source/blender/blenkernel/intern/paint.cc | 9 ++-- .../blender/blenkernel/intern/pbvh_bmesh.cc | 15 ++++--- source/blender/editors/sculpt_paint/sculpt.cc | 17 ++++---- .../editors/sculpt_paint/sculpt_intern.hh | 13 ++++-- .../editors/sculpt_paint/sculpt_undo.cc | 13 +++--- 7 files changed, 84 insertions(+), 30 deletions(-) diff --git a/source/blender/blenkernel/intern/customdata.cc b/source/blender/blenkernel/intern/customdata.cc index 80d0e6a41ba..ba5e6635aaf 100644 --- a/source/blender/blenkernel/intern/customdata.cc +++ b/source/blender/blenkernel/intern/customdata.cc @@ -4408,12 +4408,53 @@ void CustomData_bmesh_copy_data_exclude_by_type(const CustomData *source, } } +static void CustomData_bmesh_copy_data_simple(CustomData *data, void *src_block, void **dest_block) +{ + bool was_new = false; + + if (*dest_block == nullptr) { + CustomData_bmesh_alloc_block(data, dest_block, 0); + + if (*dest_block) { + CustomData_bmesh_unpoison(data, *dest_block); + memset(*dest_block, 0, data->totsize); + CustomData_bmesh_poison(data, *dest_block); + + was_new = true; + } + } + + for (int i = 0; i < data->totlayer; i++) { + CustomDataLayer *layer = data->layers + i; + + if (layer->flag & CD_FLAG_ELEM_NOCOPY) { + continue; + } + + const LayerTypeInfo *typeInfo = layerType_getInfo(eCustomDataType(data->layers[i].type)); + if (typeInfo->copy) { + typeInfo->copy( + POINTER_OFFSET(src_block, layer->offset), POINTER_OFFSET(*dest_block, layer->offset), 1); + } + else { + memcpy(POINTER_OFFSET(*dest_block, layer->offset), + POINTER_OFFSET(src_block, layer->offset), + typeInfo->size); + } + } +} + void CustomData_bmesh_copy_data(const CustomData *source, CustomData *dest, void *src_block, void **dest_block) { - CustomData_bmesh_copy_data_exclude_by_type(source, dest, src_block, dest_block, 0); + if (dest == source) { + CustomData_bmesh_copy_data_simple(dest, src_block, dest_block); + } + else { + CustomData_bmesh_copy_data_exclude_by_type(source, dest, src_block, dest_block, 0); + } } void *CustomData_bmesh_get(const CustomData *data, void *block, const eCustomDataType type) diff --git a/source/blender/blenkernel/intern/dyntopo.cc b/source/blender/blenkernel/intern/dyntopo.cc index 67d7f3071c2..a07f68cfd43 100644 --- a/source/blender/blenkernel/intern/dyntopo.cc +++ b/source/blender/blenkernel/intern/dyntopo.cc @@ -50,7 +50,7 @@ #include #include -//#define CLEAR_TAGS_IN_THREAD +// #define CLEAR_TAGS_IN_THREAD #define EDGE_QUEUE_FLAG BM_ELEM_TAG @@ -2383,7 +2383,7 @@ bool remesh_topology(BrushTester *brush_tester, using TimePoint = std::chrono::time_point; quality *= quality; - int time_limit = 8 * (1.0 - quality) + 550 * quality; + int time_limit = int(8.0f * (1.0f - quality) + 550.0f * quality); auto time = Clock::now(); Clock::duration limit = std::chrono::duration_cast( diff --git a/source/blender/blenkernel/intern/paint.cc b/source/blender/blenkernel/intern/paint.cc index 4ffc9d58341..e345de37dd5 100644 --- a/source/blender/blenkernel/intern/paint.cc +++ b/source/blender/blenkernel/intern/paint.cc @@ -2307,7 +2307,8 @@ static void sculpt_update_object( if (U.experimental.use_sculpt_texture_paint && ss->pbvh) { char *paint_canvas_key = BKE_paint_canvas_key_get(&scene->toolsettings->paint_mode, ob); if (ss->last_paint_canvas_key == nullptr || - !STREQ(paint_canvas_key, ss->last_paint_canvas_key)) { + !STREQ(paint_canvas_key, ss->last_paint_canvas_key)) + { MEM_SAFE_FREE(ss->last_paint_canvas_key); ss->last_paint_canvas_key = paint_canvas_key; BKE_pbvh_mark_rebuild_pixels(ss->pbvh); @@ -2322,7 +2323,8 @@ static void sculpt_update_object( if (U.experimental.use_sculpt_texture_paint && ss->pbvh) { char *paint_canvas_key = BKE_paint_canvas_key_get(&scene->toolsettings->paint_mode, ob); if (ss->last_paint_canvas_key == nullptr || - !STREQ(paint_canvas_key, ss->last_paint_canvas_key)) { + !STREQ(paint_canvas_key, ss->last_paint_canvas_key)) + { MEM_SAFE_FREE(ss->last_paint_canvas_key); ss->last_paint_canvas_key = paint_canvas_key; BKE_pbvh_mark_rebuild_pixels(ss->pbvh); @@ -3578,7 +3580,8 @@ static SculptAttribute *sculpt_get_cached_layer(SculptSession *ss, SculptAttribute *attr = ss->temp_attributes + i; if (attr->used && STREQ(attr->name, name) && attr->proptype == proptype && - attr->domain == domain) { + attr->domain == domain) + { return attr; } diff --git a/source/blender/blenkernel/intern/pbvh_bmesh.cc b/source/blender/blenkernel/intern/pbvh_bmesh.cc index 08602cbc173..55703281fbc 100644 --- a/source/blender/blenkernel/intern/pbvh_bmesh.cc +++ b/source/blender/blenkernel/intern/pbvh_bmesh.cc @@ -1439,7 +1439,8 @@ bool pbvh_bmesh_node_raycast(SculptSession *ss, madd_v3_v3v3fl(location, ray_start, ray_normal, *depth); for (int j = 0; j < 3; j++) { if (j == 0 || - len_squared_v3v3(location, cos[j]) < len_squared_v3v3(location, nearest_vertex_co)) { + len_squared_v3v3(location, cos[j]) < len_squared_v3v3(location, nearest_vertex_co)) + { copy_v3_v3(nearest_vertex_co, cos[j]); r_active_vertex->i = (intptr_t)verts[j]; } @@ -2100,7 +2101,8 @@ static void pbvh_bmesh_update_uv_boundary_intern(BMVert *v, float2 uv2b = *BM_ELEM_CD_PTR(l->v == v ? l->next : l, cd_uv); if (len_squared_v2v2(uv1a, uv2a) > snap_limit_sqr || - len_squared_v2v2(uv1b, uv2b) > snap_limit_sqr) { + len_squared_v2v2(uv1b, uv2b) > snap_limit_sqr) + { *boundflag |= SCULPT_BOUNDARY_UV; } @@ -2200,7 +2202,8 @@ void update_vert_boundary_bmesh(int cd_faceset_offset, if (e->l && e->l != e->l->radial_next) { if (blender::bke::pbvh::test_sharp_faces_bmesh( - e->l->f, e->l->radial_next->f, sharp_angle_limit)) { + e->l->f, e->l->radial_next->f, sharp_angle_limit)) + { boundflag |= SCULPT_BOUNDARY_SHARP_ANGLE; sharp_angle_num++; } @@ -2222,7 +2225,8 @@ void update_vert_boundary_bmesh(int cd_faceset_offset, if (e->l) { if ((e->l->f->head.hflag & BM_ELEM_HIDDEN) || - (e->l->radial_next->f->head.hflag & BM_ELEM_HIDDEN)) { + (e->l->radial_next->f->head.hflag & BM_ELEM_HIDDEN)) + { newflag |= SCULPTFLAG_VERT_FSET_HIDDEN; } @@ -3968,7 +3972,8 @@ void update_sharp_vertex_bmesh(BMVert *v, int cd_boundary_flag, const float shar } if (blender::bke::pbvh::test_sharp_faces_bmesh( - e->l->f, e->l->radial_next->f, sharp_angle_limit)) { + e->l->f, e->l->radial_next->f, sharp_angle_limit)) + { flag |= SCULPT_BOUNDARY_SHARP_ANGLE; sharp_num++; } diff --git a/source/blender/editors/sculpt_paint/sculpt.cc b/source/blender/editors/sculpt_paint/sculpt.cc index c201148c6a7..3c871068646 100644 --- a/source/blender/editors/sculpt_paint/sculpt.cc +++ b/source/blender/editors/sculpt_paint/sculpt.cc @@ -1786,8 +1786,7 @@ static void sculpt_rake_data_update(SculptRakeData *srd, const float co[3]) bool SCULPT_stroke_is_dynamic_topology(const SculptSession *ss, const Brush *brush) { - return ((BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) && - SCULPT_TOOL_HAS_DYNTOPO(brush->sculpt_tool)); + return ((BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) && SCULPT_TOOL_HAS_DYNTOPO(brush->sculpt_tool)); } /** \} */ @@ -3786,9 +3785,8 @@ static void topology_undopush_cb(PBVHNode *node, void *data) node, SCULPT_get_tool(sdata->ob->sculpt, sdata->brush) == SCULPT_TOOL_MASK ? SCULPT_UNDO_MASK : SCULPT_UNDO_COORDS, - 0); - - BKE_pbvh_node_mark_update(node); + 0, + SCULPT_UNDO_NO_TYPE); } int SCULPT_get_symmetry_pass(const SculptSession *ss) @@ -4244,7 +4242,8 @@ static void do_brush_action(Sculpt *sd, } if (ss->cache->supports_gravity && sd->gravity_factor > 0.0f && - undo_type != SCULPT_UNDO_COORDS) { + undo_type != SCULPT_UNDO_COORDS) + { extra_type = int(SCULPT_UNDO_COORDS); } @@ -5286,7 +5285,8 @@ static bool sculpt_needs_delta_from_anchored_origin(Brush *brush) return true; } if (brush->sculpt_tool == SCULPT_TOOL_CLOTH && - brush->cloth_deform_type == BRUSH_CLOTH_DEFORM_GRAB) { + brush->cloth_deform_type == BRUSH_CLOTH_DEFORM_GRAB) + { return true; } return false; @@ -6893,7 +6893,8 @@ void SCULPT_fake_neighbors_ensure(Sculpt *sd, Object *ob, const float max_dist) * recalculated. */ if (ss->fake_neighbors.fake_neighbor_index && - ss->fake_neighbors.current_max_distance == max_dist) { + ss->fake_neighbors.current_max_distance == max_dist) + { return; } diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.hh b/source/blender/editors/sculpt_paint/sculpt_intern.hh index 351fba5715a..0b5e8e088c1 100644 --- a/source/blender/editors/sculpt_paint/sculpt_intern.hh +++ b/source/blender/editors/sculpt_paint/sculpt_intern.hh @@ -1262,7 +1262,8 @@ void SCULPT_vertex_neighbors_get(const struct SculptSession *ss, #define SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN(ss, v_index, neighbor_iterator) \ SCULPT_vertex_neighbors_get(ss, v_index, false, &neighbor_iterator); \ for (neighbor_iterator.i = 0; neighbor_iterator.i < neighbor_iterator.size; \ - neighbor_iterator.i++) { \ + neighbor_iterator.i++) \ + { \ neighbor_iterator.has_edge = neighbor_iterator.neighbors[neighbor_iterator.i].edge.i != \ PBVH_REF_NONE; \ neighbor_iterator.vertex = neighbor_iterator.neighbors[neighbor_iterator.i].vertex; \ @@ -1287,7 +1288,8 @@ void SCULPT_vertex_neighbors_get(const struct SculptSession *ss, #define SCULPT_VERTEX_NEIGHBORS_ITER_END(neighbor_iterator) \ } \ if (!neighbor_iterator.no_free && \ - neighbor_iterator.neighbors != neighbor_iterator.neighbors_fixed) { \ + neighbor_iterator.neighbors != neighbor_iterator.neighbors_fixed) \ + { \ MEM_freeN(neighbor_iterator.neighbors); \ MEM_freeN(neighbor_iterator.neighbor_indices); \ } \ @@ -2266,7 +2268,7 @@ PBVHVertRef SCULPT_edge_other_vertex(const SculptSession *ss, const PBVHEdgeRef edge, const PBVHVertRef vertex); -//#define SCULPT_REPLAY +// #define SCULPT_REPLAY #ifdef SCULPT_REPLAY struct SculptReplayLog; struct SculptBrushSample; @@ -2291,7 +2293,10 @@ void SCULPT_undo_ensure_bmlog(struct Object *ob); bool SCULPT_ensure_dyntopo_node_undo(struct Object *ob, struct PBVHNode *node, SculptUndoType type, - int extraType); + int extraType, + SculptUndoType force_push_mask = SCULPT_UNDO_COORDS | + SCULPT_UNDO_COLOR | + SCULPT_UNDO_MASK); struct BMesh *SCULPT_dyntopo_empty_bmesh(); diff --git a/source/blender/editors/sculpt_paint/sculpt_undo.cc b/source/blender/editors/sculpt_paint/sculpt_undo.cc index c2025a9d024..4ad60eee1e5 100644 --- a/source/blender/editors/sculpt_paint/sculpt_undo.cc +++ b/source/blender/editors/sculpt_paint/sculpt_undo.cc @@ -89,7 +89,7 @@ using blender::bke::dyntopo::DyntopoSet; /* Uncomment to print the undo stack in the console on push/undo/redo. */ -//#define SCULPT_UNDO_DEBUG +// #define SCULPT_UNDO_DEBUG /* Implementation of undo system for objects in sculpt mode. * @@ -1433,7 +1433,8 @@ static void sculpt_undo_restore_list(bContext *C, Depsgraph *depsgraph, ListBase * ensure object is updated after the node is handled. */ const SculptUndoNode *first_unode = (const SculptUndoNode *)lb->first; if (first_unode->type != SCULPT_UNDO_GEOMETRY && - first_unode->type != SCULPT_UNDO_DYNTOPO_BEGIN) { + first_unode->type != SCULPT_UNDO_DYNTOPO_BEGIN) + { BKE_sculpt_update_object_for_edit(depsgraph, ob, false, need_mask, false); } @@ -2304,10 +2305,8 @@ static SculptUndoNode *sculpt_undo_bmesh_push(Object *ob, PBVHNode *node, Sculpt return unode; } -bool SCULPT_ensure_dyntopo_node_undo(Object *ob, - PBVHNode *node, - SculptUndoType type, - int extraType) +bool SCULPT_ensure_dyntopo_node_undo( + Object *ob, PBVHNode *node, SculptUndoType type, int extraType, SculptUndoType force_push_mask) { SculptSession *ss = ob->sculpt; @@ -2354,7 +2353,7 @@ bool SCULPT_ensure_dyntopo_node_undo(Object *ob, unode->nodemap_size = newsize; } - bool check = !((type | extraType) & (SCULPT_UNDO_COORDS | SCULPT_UNDO_COLOR | SCULPT_UNDO_MASK)); + bool check = !((type | extraType) & force_push_mask); if (check && unode->nodemap[n] & (1 << type)) { return false; } -- 2.30.2 From 8bbb475912c2b37f935346fe93ef2f66bb5216d3 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Thu, 29 Jun 2023 15:56:05 -0700 Subject: [PATCH 211/279] temp-sculpt-dyntopo: Run make format --- .../blender/blenkernel/intern/attribute_access.cc | 2 +- source/blender/blenkernel/intern/customdata.cc | 4 +--- source/blender/blenkernel/intern/paint.cc | 9 +++------ source/blender/blenkernel/intern/pbvh.c | 12 ++++++++---- source/blender/blenkernel/intern/pbvh_bmesh.cc | 15 +++++---------- source/blender/blenlib/BLI_compiler_attrs.h | 2 +- source/blender/bmesh/intern/bmesh_construct.c | 2 +- source/blender/bmesh/intern/bmesh_core.h | 7 ++----- source/blender/bmesh/intern/bmesh_interp.h | 2 +- source/blender/bmesh/intern/bmesh_mesh.h | 5 +---- source/blender/bmesh/operators/bmo_extrude.c | 3 ++- source/blender/editors/mesh/mesh_data.cc | 3 +-- source/blender/editors/sculpt_paint/sculpt.cc | 9 +++------ .../blender/editors/sculpt_paint/sculpt_detail.cc | 11 ++--------- .../blender/editors/sculpt_paint/sculpt_intern.hh | 6 ++---- .../blender/editors/sculpt_paint/sculpt_undo.cc | 3 +-- source/blender/makesrna/intern/rna_mesh.cc | 2 +- 17 files changed, 36 insertions(+), 61 deletions(-) diff --git a/source/blender/blenkernel/intern/attribute_access.cc b/source/blender/blenkernel/intern/attribute_access.cc index 9e25a12f6bd..a562f1d5701 100644 --- a/source/blender/blenkernel/intern/attribute_access.cc +++ b/source/blender/blenkernel/intern/attribute_access.cc @@ -8,10 +8,10 @@ #include "BKE_customdata.h" #include "BKE_deform.h" #include "BKE_geometry_set.hh" +#include "BKE_global.h" #include "BKE_mesh.hh" #include "BKE_pointcloud.h" #include "BKE_type_conversions.hh" -#include "BKE_global.h" #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" diff --git a/source/blender/blenkernel/intern/customdata.cc b/source/blender/blenkernel/intern/customdata.cc index ba5e6635aaf..0a73880acbb 100644 --- a/source/blender/blenkernel/intern/customdata.cc +++ b/source/blender/blenkernel/intern/customdata.cc @@ -4413,7 +4413,7 @@ static void CustomData_bmesh_copy_data_simple(CustomData *data, void *src_block, bool was_new = false; if (*dest_block == nullptr) { - CustomData_bmesh_alloc_block(data, dest_block, 0); + CustomData_bmesh_alloc_block(data, dest_block); if (*dest_block) { CustomData_bmesh_unpoison(data, *dest_block); @@ -5684,8 +5684,6 @@ void CustomData_blend_write(BlendWriter *writer, writer, CustomDataLayer, data->totlayer, data->layers, layers_to_write.data()); for (const CustomDataLayer &layer : layers_to_write) { - const LayerTypeInfo *typeInfo = layerType_getInfo(eCustomDataType(layer.type)); - switch (layer.type) { case CD_MDEFORMVERT: BKE_defvert_blend_write(writer, count, static_cast(layer.data)); diff --git a/source/blender/blenkernel/intern/paint.cc b/source/blender/blenkernel/intern/paint.cc index e345de37dd5..4ffc9d58341 100644 --- a/source/blender/blenkernel/intern/paint.cc +++ b/source/blender/blenkernel/intern/paint.cc @@ -2307,8 +2307,7 @@ static void sculpt_update_object( if (U.experimental.use_sculpt_texture_paint && ss->pbvh) { char *paint_canvas_key = BKE_paint_canvas_key_get(&scene->toolsettings->paint_mode, ob); if (ss->last_paint_canvas_key == nullptr || - !STREQ(paint_canvas_key, ss->last_paint_canvas_key)) - { + !STREQ(paint_canvas_key, ss->last_paint_canvas_key)) { MEM_SAFE_FREE(ss->last_paint_canvas_key); ss->last_paint_canvas_key = paint_canvas_key; BKE_pbvh_mark_rebuild_pixels(ss->pbvh); @@ -2323,8 +2322,7 @@ static void sculpt_update_object( if (U.experimental.use_sculpt_texture_paint && ss->pbvh) { char *paint_canvas_key = BKE_paint_canvas_key_get(&scene->toolsettings->paint_mode, ob); if (ss->last_paint_canvas_key == nullptr || - !STREQ(paint_canvas_key, ss->last_paint_canvas_key)) - { + !STREQ(paint_canvas_key, ss->last_paint_canvas_key)) { MEM_SAFE_FREE(ss->last_paint_canvas_key); ss->last_paint_canvas_key = paint_canvas_key; BKE_pbvh_mark_rebuild_pixels(ss->pbvh); @@ -3580,8 +3578,7 @@ static SculptAttribute *sculpt_get_cached_layer(SculptSession *ss, SculptAttribute *attr = ss->temp_attributes + i; if (attr->used && STREQ(attr->name, name) && attr->proptype == proptype && - attr->domain == domain) - { + attr->domain == domain) { return attr; } diff --git a/source/blender/blenkernel/intern/pbvh.c b/source/blender/blenkernel/intern/pbvh.c index 014a04290be..7e46b383e32 100644 --- a/source/blender/blenkernel/intern/pbvh.c +++ b/source/blender/blenkernel/intern/pbvh.c @@ -2067,7 +2067,8 @@ static void pbvh_bmesh_node_visibility_update(PBVHNode *node) BMVert *v; - TGSET_ITER (v, unique) { + TGSET_ITER(v, unique) + { if (!BM_elem_flag_test(v, BM_ELEM_HIDDEN)) { BKE_pbvh_node_fully_hidden_set(node, false); return; @@ -2075,7 +2076,8 @@ static void pbvh_bmesh_node_visibility_update(PBVHNode *node) } TGSET_ITER_END - TGSET_ITER (v, other) { + TGSET_ITER(v, other) + { if (!BM_elem_flag_test(v, BM_ELEM_HIDDEN)) { BKE_pbvh_node_fully_hidden_set(node, false); return; @@ -4415,7 +4417,8 @@ void BKE_pbvh_check_tri_areas(PBVH *pbvh, PBVHNode *node) BMFace *f; const int cd_face_area = pbvh->cd_face_area; - TGSET_ITER (f, node->bm_faces) { + TGSET_ITER(f, node->bm_faces) + { float *areabuf = BM_ELEM_CD_GET_VOID_P(f, cd_face_area); areabuf[cur_i] = 0.0f; } @@ -4433,7 +4436,8 @@ void BKE_pbvh_check_tri_areas(PBVH *pbvh, PBVHNode *node) areabuf[cur_i] += area_tri_v3(v1->co, v2->co, v3->co); } - TGSET_ITER (f, node->bm_faces) { + TGSET_ITER(f, node->bm_faces) + { float *areabuf = BM_ELEM_CD_GET_VOID_P(f, cd_face_area); /* sanity check on read side of read write buffer */ diff --git a/source/blender/blenkernel/intern/pbvh_bmesh.cc b/source/blender/blenkernel/intern/pbvh_bmesh.cc index 55703281fbc..08602cbc173 100644 --- a/source/blender/blenkernel/intern/pbvh_bmesh.cc +++ b/source/blender/blenkernel/intern/pbvh_bmesh.cc @@ -1439,8 +1439,7 @@ bool pbvh_bmesh_node_raycast(SculptSession *ss, madd_v3_v3v3fl(location, ray_start, ray_normal, *depth); for (int j = 0; j < 3; j++) { if (j == 0 || - len_squared_v3v3(location, cos[j]) < len_squared_v3v3(location, nearest_vertex_co)) - { + len_squared_v3v3(location, cos[j]) < len_squared_v3v3(location, nearest_vertex_co)) { copy_v3_v3(nearest_vertex_co, cos[j]); r_active_vertex->i = (intptr_t)verts[j]; } @@ -2101,8 +2100,7 @@ static void pbvh_bmesh_update_uv_boundary_intern(BMVert *v, float2 uv2b = *BM_ELEM_CD_PTR(l->v == v ? l->next : l, cd_uv); if (len_squared_v2v2(uv1a, uv2a) > snap_limit_sqr || - len_squared_v2v2(uv1b, uv2b) > snap_limit_sqr) - { + len_squared_v2v2(uv1b, uv2b) > snap_limit_sqr) { *boundflag |= SCULPT_BOUNDARY_UV; } @@ -2202,8 +2200,7 @@ void update_vert_boundary_bmesh(int cd_faceset_offset, if (e->l && e->l != e->l->radial_next) { if (blender::bke::pbvh::test_sharp_faces_bmesh( - e->l->f, e->l->radial_next->f, sharp_angle_limit)) - { + e->l->f, e->l->radial_next->f, sharp_angle_limit)) { boundflag |= SCULPT_BOUNDARY_SHARP_ANGLE; sharp_angle_num++; } @@ -2225,8 +2222,7 @@ void update_vert_boundary_bmesh(int cd_faceset_offset, if (e->l) { if ((e->l->f->head.hflag & BM_ELEM_HIDDEN) || - (e->l->radial_next->f->head.hflag & BM_ELEM_HIDDEN)) - { + (e->l->radial_next->f->head.hflag & BM_ELEM_HIDDEN)) { newflag |= SCULPTFLAG_VERT_FSET_HIDDEN; } @@ -3972,8 +3968,7 @@ void update_sharp_vertex_bmesh(BMVert *v, int cd_boundary_flag, const float shar } if (blender::bke::pbvh::test_sharp_faces_bmesh( - e->l->f, e->l->radial_next->f, sharp_angle_limit)) - { + e->l->f, e->l->radial_next->f, sharp_angle_limit)) { flag |= SCULPT_BOUNDARY_SHARP_ANGLE; sharp_num++; } diff --git a/source/blender/blenlib/BLI_compiler_attrs.h b/source/blender/blenlib/BLI_compiler_attrs.h index 554588b3d05..a2e934bcfae 100644 --- a/source/blender/blenlib/BLI_compiler_attrs.h +++ b/source/blender/blenlib/BLI_compiler_attrs.h @@ -92,7 +92,7 @@ /* NotForPR: Needed for debugging but already been rejected in * a standalone PR. - * Disable optimization for a function (for debugging use only!) + * Disable optimization for a function (for debugging use only!) */ #ifdef __clang__ # define ATTR_NO_OPT __attribute__((optnone)) diff --git a/source/blender/bmesh/intern/bmesh_construct.c b/source/blender/bmesh/intern/bmesh_construct.c index e5a862adbe1..b7df1ee22e0 100644 --- a/source/blender/bmesh/intern/bmesh_construct.c +++ b/source/blender/bmesh/intern/bmesh_construct.c @@ -659,7 +659,7 @@ BMesh *BM_mesh_copy(BMesh *bm_old) int i; const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_BM(bm_old); struct BMeshCreateParams params = {0}; - + params.use_toolflags = bm_old->use_toolflags; /* allocate a bmesh */ diff --git a/source/blender/bmesh/intern/bmesh_core.h b/source/blender/bmesh/intern/bmesh_core.h index 31f32341139..ce516889742 100644 --- a/source/blender/bmesh/intern/bmesh_core.h +++ b/source/blender/bmesh/intern/bmesh_core.h @@ -340,11 +340,8 @@ BMEdge *bmesh_kernel_join_edge_kill_vert(BMesh *bm, * +-+-+-+ +-+-+-+ * */ -BMVert *bmesh_kernel_join_vert_kill_edge(BMesh *bm, - BMEdge *e_kill, - BMVert *v_kill, - const bool do_del, - const bool combine_flags); +BMVert *bmesh_kernel_join_vert_kill_edge( + BMesh *bm, BMEdge *e_kill, BMVert *v_kill, const bool do_del, const bool combine_flags); BMVert *bmesh_kernel_join_vert_kill_edge_fast(BMesh *bm, BMEdge *e_kill, diff --git a/source/blender/bmesh/intern/bmesh_interp.h b/source/blender/bmesh/intern/bmesh_interp.h index 7b8fe7ef4dc..962ccb83139 100644 --- a/source/blender/bmesh/intern/bmesh_interp.h +++ b/source/blender/bmesh/intern/bmesh_interp.h @@ -65,7 +65,7 @@ void BM_data_interp_face_vert_edge( typedef struct BMCustomLayerReq { int type; - const char *name; /* Can be NULL. */ + const char *name; /* Can be NULL. */ int flag; } BMCustomLayerReq; void BM_data_layers_ensure(BMesh *bm, CustomData *data, BMCustomLayerReq *layers, int totlayer); diff --git a/source/blender/bmesh/intern/bmesh_mesh.h b/source/blender/bmesh/intern/bmesh_mesh.h index 50b8d42a633..bbffe7708ee 100644 --- a/source/blender/bmesh/intern/bmesh_mesh.h +++ b/source/blender/bmesh/intern/bmesh_mesh.h @@ -158,10 +158,7 @@ int BM_mesh_elem_count(BMesh *bm, char htype); * \warning Be careful if you keep pointers to affected BM elements, * or arrays, when using this func! */ -void BM_mesh_remap(BMesh *bm, - const uint *vert_idx, - const uint *edge_idx, - const uint *face_idx); +void BM_mesh_remap(BMesh *bm, const uint *vert_idx, const uint *edge_idx, const uint *face_idx); /** * Use new memory pools for this mesh. diff --git a/source/blender/bmesh/operators/bmo_extrude.c b/source/blender/bmesh/operators/bmo_extrude.c index 680d1f837f8..6e979f648b6 100644 --- a/source/blender/bmesh/operators/bmo_extrude.c +++ b/source/blender/bmesh/operators/bmo_extrude.c @@ -607,7 +607,8 @@ void bmo_extrude_face_region_exec(BMesh *bm, BMOperator *op) BMEdge *e_other = BM_DISK_EDGE_NEXT(e, v); if ((e_other == e) || (BM_DISK_EDGE_NEXT(e_other, v) == e)) { /* Loose edge or BMVert is edge pair. */ - BM_edge_collapse(bm, BMO_elem_flag_test(bm, e, EXT_TAG) ? e : e_other, v, true, true, true, true); + BM_edge_collapse( + bm, BMO_elem_flag_test(bm, e, EXT_TAG) ? e : e_other, v, true, true, true, true); } else { BLI_assert(!BM_vert_is_edge_pair(v)); diff --git a/source/blender/editors/mesh/mesh_data.cc b/source/blender/editors/mesh/mesh_data.cc index e7a301c4a57..f912a92b506 100644 --- a/source/blender/editors/mesh/mesh_data.cc +++ b/source/blender/editors/mesh/mesh_data.cc @@ -44,9 +44,9 @@ #include "ED_object.h" #include "ED_paint.h" #include "ED_screen.h" +#include "ED_sculpt.h" #include "ED_uvedit.h" #include "ED_view3d.h" -#include "ED_sculpt.h" #include "GEO_mesh_split_edges.hh" @@ -468,7 +468,6 @@ static bool layers_poll(bContext *C) !ID_IS_LINKED(data) && !ID_IS_OVERRIDE_LIBRARY(data)); } - /*********************** UV texture operators ************************/ static bool uv_texture_remove_poll(bContext *C) diff --git a/source/blender/editors/sculpt_paint/sculpt.cc b/source/blender/editors/sculpt_paint/sculpt.cc index 3c871068646..09d75ae46c7 100644 --- a/source/blender/editors/sculpt_paint/sculpt.cc +++ b/source/blender/editors/sculpt_paint/sculpt.cc @@ -4242,8 +4242,7 @@ static void do_brush_action(Sculpt *sd, } if (ss->cache->supports_gravity && sd->gravity_factor > 0.0f && - undo_type != SCULPT_UNDO_COORDS) - { + undo_type != SCULPT_UNDO_COORDS) { extra_type = int(SCULPT_UNDO_COORDS); } @@ -5285,8 +5284,7 @@ static bool sculpt_needs_delta_from_anchored_origin(Brush *brush) return true; } if (brush->sculpt_tool == SCULPT_TOOL_CLOTH && - brush->cloth_deform_type == BRUSH_CLOTH_DEFORM_GRAB) - { + brush->cloth_deform_type == BRUSH_CLOTH_DEFORM_GRAB) { return true; } return false; @@ -6893,8 +6891,7 @@ void SCULPT_fake_neighbors_ensure(Sculpt *sd, Object *ob, const float max_dist) * recalculated. */ if (ss->fake_neighbors.fake_neighbor_index && - ss->fake_neighbors.current_max_distance == max_dist) - { + ss->fake_neighbors.current_max_distance == max_dist) { return; } diff --git a/source/blender/editors/sculpt_paint/sculpt_detail.cc b/source/blender/editors/sculpt_paint/sculpt_detail.cc index 70229cae969..ba41fae3304 100644 --- a/source/blender/editors/sculpt_paint/sculpt_detail.cc +++ b/source/blender/editors/sculpt_paint/sculpt_detail.cc @@ -196,15 +196,8 @@ static int sculpt_detail_flood_fill_run(Scene *scene, BKE_pbvh_node_mark_topology_update(nodes[j]); } - EdgeQueueContext remesher(tester, - ob, - ss->pbvh, - mode, - false, - float3(0.0f, 0.0f, 1.0f), - false, - mask_cb, - mask_cb_data); + EdgeQueueContext remesher( + tester, ob, ss->pbvh, mode, false, float3(0.0f, 0.0f, 1.0f), false, mask_cb, mask_cb_data); remesher.surface_smooth_fac = 0.25; remesher.start(); diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.hh b/source/blender/editors/sculpt_paint/sculpt_intern.hh index 0b5e8e088c1..8ef77524845 100644 --- a/source/blender/editors/sculpt_paint/sculpt_intern.hh +++ b/source/blender/editors/sculpt_paint/sculpt_intern.hh @@ -1262,8 +1262,7 @@ void SCULPT_vertex_neighbors_get(const struct SculptSession *ss, #define SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN(ss, v_index, neighbor_iterator) \ SCULPT_vertex_neighbors_get(ss, v_index, false, &neighbor_iterator); \ for (neighbor_iterator.i = 0; neighbor_iterator.i < neighbor_iterator.size; \ - neighbor_iterator.i++) \ - { \ + neighbor_iterator.i++) { \ neighbor_iterator.has_edge = neighbor_iterator.neighbors[neighbor_iterator.i].edge.i != \ PBVH_REF_NONE; \ neighbor_iterator.vertex = neighbor_iterator.neighbors[neighbor_iterator.i].vertex; \ @@ -1288,8 +1287,7 @@ void SCULPT_vertex_neighbors_get(const struct SculptSession *ss, #define SCULPT_VERTEX_NEIGHBORS_ITER_END(neighbor_iterator) \ } \ if (!neighbor_iterator.no_free && \ - neighbor_iterator.neighbors != neighbor_iterator.neighbors_fixed) \ - { \ + neighbor_iterator.neighbors != neighbor_iterator.neighbors_fixed) { \ MEM_freeN(neighbor_iterator.neighbors); \ MEM_freeN(neighbor_iterator.neighbor_indices); \ } \ diff --git a/source/blender/editors/sculpt_paint/sculpt_undo.cc b/source/blender/editors/sculpt_paint/sculpt_undo.cc index 4ad60eee1e5..1cf8bb414d3 100644 --- a/source/blender/editors/sculpt_paint/sculpt_undo.cc +++ b/source/blender/editors/sculpt_paint/sculpt_undo.cc @@ -1433,8 +1433,7 @@ static void sculpt_undo_restore_list(bContext *C, Depsgraph *depsgraph, ListBase * ensure object is updated after the node is handled. */ const SculptUndoNode *first_unode = (const SculptUndoNode *)lb->first; if (first_unode->type != SCULPT_UNDO_GEOMETRY && - first_unode->type != SCULPT_UNDO_DYNTOPO_BEGIN) - { + first_unode->type != SCULPT_UNDO_DYNTOPO_BEGIN) { BKE_sculpt_update_object_for_edit(depsgraph, ob, false, need_mask, false); } diff --git a/source/blender/makesrna/intern/rna_mesh.cc b/source/blender/makesrna/intern/rna_mesh.cc index f2505d6701e..5ea40da0cc8 100644 --- a/source/blender/makesrna/intern/rna_mesh.cc +++ b/source/blender/makesrna/intern/rna_mesh.cc @@ -3335,7 +3335,7 @@ static void rna_def_mesh(BlenderRNA *brna) prop = RNA_def_property(srna, "sculpt_ignore_uvs", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", ME_SCULPT_IGNORE_UVS); RNA_def_property_ui_text(prop, "Ignore UVs", ""); - + /* End Symmetry */ prop = RNA_def_property(srna, "use_auto_smooth", PROP_BOOLEAN, PROP_NONE); -- 2.30.2 From 37fb533ce53d336d869c263ff4a43d0062353c26 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Thu, 29 Jun 2023 21:00:26 -0700 Subject: [PATCH 212/279] temp-sculpt-dyntopo: Fix more performance regressions * Use BM_log_face_if_modified instead of BM_log_face_modified. This ensures modified face data is only written once per stroke. * BM_log_vert_before_modified now uses same logic as BM_log_face_if_modified * Both functions now work by maintaining a Set inside of the owning BMLogEntry instead of checking each entry differential subset inside the entry manually. --- source/blender/blenkernel/BKE_pbvh_iter.hh | 272 ++++++++++++++++++ source/blender/blenkernel/CMakeLists.txt | 1 + source/blender/blenkernel/intern/dyntopo.cc | 2 + source/blender/blenkernel/intern/paint.cc | 9 +- source/blender/bmesh/intern/bmesh_log.cc | 96 +++++-- source/blender/editors/sculpt_paint/sculpt.cc | 14 +- .../editors/sculpt_paint/sculpt_smooth.cc | 155 +++++++++- .../editors/sculpt_paint/sculpt_undo.cc | 2 +- 8 files changed, 517 insertions(+), 34 deletions(-) create mode 100644 source/blender/blenkernel/BKE_pbvh_iter.hh diff --git a/source/blender/blenkernel/BKE_pbvh_iter.hh b/source/blender/blenkernel/BKE_pbvh_iter.hh new file mode 100644 index 00000000000..e3fc9aaeda7 --- /dev/null +++ b/source/blender/blenkernel/BKE_pbvh_iter.hh @@ -0,0 +1,272 @@ +/* NotForPR: This is a proposed new vertex iterator for brushes. + * It exists in this branch to debug threading problems + * (it builds a list of verts under the brush first and calls + * parallel_for on it, so it's much more efficient at allocating + * to threads--basically it lets us test if worker malallocation + * is the root cause of a performance problem). + * + * Delete this file before committing to master; a seperate PR + * will be created for this. + */ +#pragma once + +#include "BLI_array.hh" +#include "BLI_index_range.hh" +#include "BLI_math_vector_types.hh" +#include "BLI_span.hh" +#include "BLI_task.hh" +#include "BLI_threads.h" +#include "BLI_vector.hh" +#include "BLI_timeit.hh" + +#include "BKE_pbvh.h" +#include "intern/pbvh_intern.hh" + +#include +#include + +namespace blender::bke::pbvh { +template struct VertexRange { + struct iterator { + PBVHVertRef vertex; + float *co, *no, *mask, *color; + const float *origco, *origno; + const float *origcolor, *origmask; + PBVHNode *node; + NodeData *userdata; + bool is_mesh; + + iterator(IndexRange _range, VertexRange &_owner, int _i) : range(_range), owner(_owner), i(_i) + { + is_mesh = owner.pbvh->header.type == PBVH_FACES; + if (_i >= owner.range.start() && _i <= owner.range.last()) { + load_data(); + } + } + + iterator(const iterator &b) : range(b.range), owner(b.owner), i(b.i) {} + + iterator &operator*() + { + return *this; + } + + bool operator==(const iterator &b) + { + return b.i == i; + } + + bool operator!=(const iterator &b) + { + return b.i != i; + } + + iterator &operator++() + { + i++; + load_data(); + return *this; + } + + void load_data() + { + if (i == range.start() + range.size()) { + return; + } + + node = owner.nodes[owner.verts[i].second]; + vertex = owner.verts[i].first; + userdata = &owner.node_data[owner.verts[i].second]; + + switch (BKE_pbvh_type(owner.pbvh)) { + case PBVH_BMESH: { + BMVert *v = reinterpret_cast(vertex.i); + + co = v->co; + no = v->no; + + mask = owner.pbvh->cd_vert_mask_offset ? + BM_ELEM_CD_PTR(v, owner.pbvh->cd_vert_mask_offset) : + nullptr; + + break; + } + case PBVH_FACES: + mask = owner.vert_mask ? owner.vert_mask + vertex.i : nullptr; + co = owner.pbvh->vert_positions[vertex.i]; + no = owner.pbvh->vert_normals[vertex.i]; + + break; + case PBVH_GRIDS: + break; + } + } + + private: + int i; + VertexRange &owner; + IndexRange range; + }; + + PBVH *pbvh; + Span> verts; + IndexRange range; + float *vert_mask; + MutableSpan node_data; + Span nodes; + + VertexRange(PBVH *_pbvh, + Span> _verts, + Span _nodes, + MutableSpan _node_data, + IndexRange _range) + : pbvh(_pbvh), verts(_verts), range(_range), nodes(_nodes), node_data(_node_data) + { + if (BKE_pbvh_type(pbvh) == PBVH_FACES) { + vert_mask = static_cast( + CustomData_get_layer_for_write(pbvh->vdata, CD_PAINT_MASK, pbvh->totvert)); + } + } + + iterator begin() + { + return iterator(range, *this, range.start()); + } + + iterator end() + { + return iterator(range, *this, range.start() + range.size()); + } +}; + +struct ForEachThreadData { + Vector *ranges; + std::function visit; +}; + +static void *foreach_thread_task(void *userdata) +{ + ForEachThreadData *data = static_cast(userdata); + for (int i : data->ranges->index_range()) { + data->visit((*data->ranges)[i]); + } + + return nullptr; +} + +template +void brush_vertex_iter( + PBVH *pbvh, + Span nodes, + bool threaded, + std::function + filter_verts, + std::function node_visit_pre, + std::function range)> visit, + std::function node_visit_post) +{ + //SCOPED_TIMER(__func__); + + int vert_count = 0; + + Array node_data(nodes.size()); + Array used_nodes(nodes.size()); + + Vector> verts; + + int vertex_i = 0; + for (int i : nodes.index_range()) { + PBVHNode *node = nodes[i]; + + bool used = false; + + PBVHVertexIter vd; + BKE_pbvh_vertex_iter_begin (pbvh, node, vd, PBVH_ITER_UNIQUE) { + if (filter_verts(vd.vertex, vd.co, vd.fno, vd.mask ? *vd.mask : 0.0f)) { + verts.append(std::make_pair(vd.vertex, i)); + used = true; + } + } + BKE_pbvh_vertex_iter_end; + + used_nodes[i] = used; + } + + //printf("threaded: %s\n", threaded ? "true" : "false"); + + if (threaded) { + threading::parallel_for_each(nodes.index_range(), [&](int i) { + // + if (used_nodes[i]) { + node_data[i] = node_visit_pre(nodes[i]); + } + }); + } + else { + for (int i : nodes.index_range()) { + if (used_nodes[i]) { + node_data[i] = node_visit_pre(nodes[i]); + } + } + } + + int thread_count = BLI_system_thread_count(); + const int grain_size = 32; //max_ii(verts.size() / thread_count / 4, 4); + //printf("verts size:%d (%d)\n", verts.size(), verts.size() / grain_size); + + if (!threaded) { + visit(VertexRange(pbvh, verts, nodes, node_data, verts.index_range())); + } + else { +#if 1 + threading::parallel_for(verts.index_range(), grain_size, [&](IndexRange range) { + visit(VertexRange(pbvh, verts, nodes, node_data, range)); + }); +#else + int threads_count = BLI_system_thread_count(); + Array> ranges(threads_count); + + int i = 0; + int j = 0; + IndexRange vrange = verts.index_range(); + printf("\n"); + while (i < verts.size()) { + IndexRange range = vrange.slice(i, min_ii(grain_size, verts.size() - i)); + + // printf("%d %d: %d\n", range.start(), range.one_after_last(), range.size()); + + ranges[j].append(range); + + // printf("%d: %d\n", i, min_ii(i + grain_size, verts.size())); + j = (j + 1) % threads_count; + i += grain_size; + } + + ListBase threads; + BLI_threadpool_init(&threads, foreach_thread_task, threads_count); + Array tdata(threads_count); + + auto visit2 = [&](IndexRange range) { + visit(VertexRange(pbvh, verts, nodes, node_data, range)); + }; + + for (int i = 0; i < threads_count; i++) { + if (ranges[i].size() == 0) { + continue; + } + + tdata[i].ranges = &ranges[i]; + tdata[i].visit = visit2; + + BLI_threadpool_insert(&threads, &tdata[i]); + } + + BLI_threadpool_end(&threads); +#endif + } + + for (PBVHNode *node : nodes) { + node_visit_post(node); + } +} +} // namespace blender::bke::pbvh diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt index 36778b7c4b1..ea441879afc 100644 --- a/source/blender/blenkernel/CMakeLists.txt +++ b/source/blender/blenkernel/CMakeLists.txt @@ -471,6 +471,7 @@ set(SRC BKE_particle.h BKE_pbvh.h BKE_pbvh_pixels.hh + BKE_pbvh_iter.hh BKE_pointcache.h BKE_pointcloud.h BKE_pose_backup.h diff --git a/source/blender/blenkernel/intern/dyntopo.cc b/source/blender/blenkernel/intern/dyntopo.cc index a07f68cfd43..8bcefbd6935 100644 --- a/source/blender/blenkernel/intern/dyntopo.cc +++ b/source/blender/blenkernel/intern/dyntopo.cc @@ -2579,11 +2579,13 @@ void EdgeQueueContext::split_edge(BMEdge *e) *BM_ELEM_CD_PTR(newv, ss->attrs.stroke_id->bmesh_cd_offset) = stroke_id; BM_ELEM_CD_SET_INT(newv, pbvh->cd_vert_node_offset, DYNTOPO_NODE_NONE); + BM_idmap_check_assign(pbvh->bm_idmap, reinterpret_cast(newv)); BM_log_vert_added(bm, pbvh->bm_log, newv); BM_idmap_check_assign(pbvh->bm_idmap, reinterpret_cast(e)); BM_log_edge_added(bm, pbvh->bm_log, e); + BM_idmap_check_assign(pbvh->bm_idmap, reinterpret_cast(newe)); BM_log_edge_added(bm, pbvh->bm_log, newe); diff --git a/source/blender/blenkernel/intern/paint.cc b/source/blender/blenkernel/intern/paint.cc index 4ffc9d58341..e345de37dd5 100644 --- a/source/blender/blenkernel/intern/paint.cc +++ b/source/blender/blenkernel/intern/paint.cc @@ -2307,7 +2307,8 @@ static void sculpt_update_object( if (U.experimental.use_sculpt_texture_paint && ss->pbvh) { char *paint_canvas_key = BKE_paint_canvas_key_get(&scene->toolsettings->paint_mode, ob); if (ss->last_paint_canvas_key == nullptr || - !STREQ(paint_canvas_key, ss->last_paint_canvas_key)) { + !STREQ(paint_canvas_key, ss->last_paint_canvas_key)) + { MEM_SAFE_FREE(ss->last_paint_canvas_key); ss->last_paint_canvas_key = paint_canvas_key; BKE_pbvh_mark_rebuild_pixels(ss->pbvh); @@ -2322,7 +2323,8 @@ static void sculpt_update_object( if (U.experimental.use_sculpt_texture_paint && ss->pbvh) { char *paint_canvas_key = BKE_paint_canvas_key_get(&scene->toolsettings->paint_mode, ob); if (ss->last_paint_canvas_key == nullptr || - !STREQ(paint_canvas_key, ss->last_paint_canvas_key)) { + !STREQ(paint_canvas_key, ss->last_paint_canvas_key)) + { MEM_SAFE_FREE(ss->last_paint_canvas_key); ss->last_paint_canvas_key = paint_canvas_key; BKE_pbvh_mark_rebuild_pixels(ss->pbvh); @@ -3578,7 +3580,8 @@ static SculptAttribute *sculpt_get_cached_layer(SculptSession *ss, SculptAttribute *attr = ss->temp_attributes + i; if (attr->used && STREQ(attr->name, name) && attr->proptype == proptype && - attr->domain == domain) { + attr->domain == domain) + { return attr; } diff --git a/source/blender/bmesh/intern/bmesh_log.cc b/source/blender/bmesh/intern/bmesh_log.cc index 8603c45c7ab..802aee2969c 100644 --- a/source/blender/bmesh/intern/bmesh_log.cc +++ b/source/blender/bmesh/intern/bmesh_log.cc @@ -31,6 +31,8 @@ extern "C" { #include #include +#define USE_SIMPLE_CD_COPY + extern "C" void bm_log_message(const char *fmt, ...) { char msg[64]; @@ -458,6 +460,11 @@ struct BMLogEntry { LogElemAlloc epool; LogElemAlloc fpool; + /* Contains all faces from all differential subsets. */ + Set> verts; + Set> edges; + Set> faces; + CustomData vdata, edata, ldata, pdata; BMIdMap *idmap = nullptr; @@ -471,10 +478,28 @@ struct BMLogEntry { CustomData *src_pdata) : idmap(_idmap) { +#if 1 CustomData_copy_all_layout(src_vdata, &vdata); CustomData_copy_all_layout(src_edata, &edata); CustomData_copy_all_layout(src_ldata, &ldata); CustomData_copy_all_layout(src_pdata, &pdata); +#else + vdata = *src_vdata; + edata = *src_edata; + ldata = *src_ldata; + pdata = *src_pdata; + + vdata.layers = static_cast( + MEM_dupallocN(static_cast(vdata.layers))); + edata.layers = static_cast( + MEM_dupallocN(static_cast(edata.layers))); + ldata.layers = static_cast( + MEM_dupallocN(static_cast(ldata.layers))); + pdata.layers = static_cast( + MEM_dupallocN(static_cast(pdata.layers))); + + vdata.pool = edata.pool = ldata.pool = pdata.pool = nullptr; +#endif CustomData_bmesh_init_pool(&vdata, 0, BM_VERT); CustomData_bmesh_init_pool(&edata, 0, BM_EDGE); @@ -624,7 +649,12 @@ struct BMLogEntry { void update_logvert(BMesh *bm, BMVert *v, BMLogVert *lv) { if (vdata.pool) { +#ifdef USE_SIMPLE_CD_COPY + /* Signal simple copy by using bm->vdata for dest. */ + CustomData_bmesh_copy_data(&bm->vdata, &bm->vdata, v->head.data, &lv->customdata); +#else CustomData_bmesh_copy_data(&bm->vdata, &vdata, v->head.data, &lv->customdata); +#endif } lv->co = v->co; @@ -719,6 +749,8 @@ struct BMLogEntry { le->v1 = get_elem_id(bm, e->v1); le->v2 = get_elem_id(bm, e->v2); + edges.add(le->id); + update_logedge(bm, e, le); return le; @@ -727,7 +759,12 @@ struct BMLogEntry { void update_logedge(BMesh *bm, BMEdge *e, BMLogEdge *le) { le->flag = e->head.hflag; +#ifdef USE_SIMPLE_CD_COPY + /* Signal simple copy by using bm->edata for dest. */ + CustomData_bmesh_copy_data(&bm->edata, &bm->edata, e->head.data, &le->customdata); +#else CustomData_bmesh_copy_data(&bm->edata, &edata, e->head.data, &le->customdata); +#endif } void free_logedge(BMesh * /*bm*/, BMLogEdge *le) @@ -746,7 +783,13 @@ struct BMLogEntry { lf->id = get_elem_id(bm, f); lf->flag = f->head.hflag; + +#ifdef USE_SIMPLE_CD_COPY + /* Signal simple copy by using bm->pdata for dest. */ + CustomData_bmesh_copy_data(&bm->pdata, &bm->pdata, f->head.data, &lf->customdata); +#else CustomData_bmesh_copy_data(&bm->pdata, &pdata, f->head.data, &lf->customdata); +#endif BMLoop *l = f->l_first; do { @@ -754,7 +797,12 @@ struct BMLogEntry { void *loop_customdata = nullptr; if (l->head.data) { +#ifdef USE_SIMPLE_CD_COPY + /* Signal simple copy by using bm->ldata for dest. */ + CustomData_bmesh_copy_data(&bm->ldata, &bm->ldata, l->head.data, &loop_customdata); +#else CustomData_bmesh_copy_data(&bm->ldata, &ldata, l->head.data, &loop_customdata); +#endif } lf->loop_customdata.append(loop_customdata); @@ -766,7 +814,13 @@ struct BMLogEntry { void update_logface(BMesh *bm, BMLogFace *lf, BMFace *f) { lf->flag = f->head.hflag; + +#ifdef USE_SIMPLE_CD_COPY + /* Signal simple copy by using bm->ldata for dest. */ + CustomData_bmesh_copy_data(&bm->pdata, &bm->pdata, f->head.data, &lf->customdata); +#else CustomData_bmesh_copy_data(&bm->pdata, &pdata, f->head.data, &lf->customdata); +#endif if (f->len != lf->verts.size()) { printf("%s: error: face length changed.\n", __func__); @@ -779,7 +833,12 @@ struct BMLogEntry { void *loop_customdata = nullptr; if (l->head.data) { +#ifdef USE_SIMPLE_CD_COPY + /* Signal simple copy by using bm->ldata for dest. */ + CustomData_bmesh_copy_data(&bm->ldata, &bm->ldata, l->head.data, &loop_customdata); +#else CustomData_bmesh_copy_data(&bm->ldata, &ldata, l->head.data, &loop_customdata); +#endif } lf->loop_customdata[i++] = loop_customdata; @@ -818,6 +877,15 @@ struct BMLogEntry { current_diff_set(bm)->modify_vert(bm, v); } + void modify_if_vert(BMesh *bm, BMVert *v) + { + BMID id = get_elem_id(bm, v); + + if (!verts.contains(id)) { + current_diff_set(bm)->modify_vert(bm, v); + } + } + void add_edge(BMesh *bm, BMEdge *e) { current_diff_set(bm)->add_edge(bm, e); @@ -849,22 +917,9 @@ struct BMLogEntry { { BMID id = get_elem_id(bm, f); - for (int i = sets.size() - 1; i >= 0; i--) { - BMLogSetBase *set = sets[i]; - - if (set->type != LOG_SET_DIFF) { - continue; - } - - BMLogSetDiff *diff = static_cast(set); - if (diff->modified_faces.contains(id) || diff->removed_faces.contains(id) || - diff->added_faces.contains(id)) - { - return; - } + if (!faces.contains(id)) { + current_diff_set(bm)->modify_face(bm, f); } - - first_diff_set(bm)->modify_face(bm, f); } void undo(BMesh *bm, BMLogCallbacks *callbacks) @@ -1025,6 +1080,12 @@ struct BMLog { current_entry->modify_vert(bm, v); } + void modify_if_vert(BMesh *bm, BMVert *v) + { + ensure_entry(bm); + current_entry->modify_if_vert(bm, v); + } + void add_edge(BMesh *bm, BMEdge *e) { ensure_entry(bm); @@ -1146,6 +1207,7 @@ void BMLogSetDiff::modify_vert(BMesh *bm, BMVert *v) return; } + entry->verts.add(id); modified_verts.add(id, entry->alloc_logvert(bm, v)); } @@ -1240,7 +1302,9 @@ void BMLogSetDiff::modify_face(BMesh *bm, BMFace *f) } else { lf = entry->alloc_logface(bm, f); + modified_faces.add(id, lf); + entry->faces.add(lf->id); } } @@ -1653,7 +1717,7 @@ void BM_log_vert_removed(BMesh *bm, BMLog *log, BMVert *v) void BM_log_vert_before_modified(BMesh *bm, BMLog *log, BMVert *v) { - log->modify_vert(bm, v); + log->modify_if_vert(bm, v); } BMLogEntry *BM_log_entry_check_customdata(BMesh *bm, BMLog *log) diff --git a/source/blender/editors/sculpt_paint/sculpt.cc b/source/blender/editors/sculpt_paint/sculpt.cc index 09d75ae46c7..a2adc208e03 100644 --- a/source/blender/editors/sculpt_paint/sculpt.cc +++ b/source/blender/editors/sculpt_paint/sculpt.cc @@ -4242,16 +4242,12 @@ static void do_brush_action(Sculpt *sd, } if (ss->cache->supports_gravity && sd->gravity_factor > 0.0f && - undo_type != SCULPT_UNDO_COORDS) { + undo_type != SCULPT_UNDO_COORDS) + { extra_type = int(SCULPT_UNDO_COORDS); } for (int i : nodes.index_range()) { - /* Will save all face/loop customdata. */ - if (brush->autosmooth_factor > 0.0f || brush->topology_rake_factor > 0.0f) { - extra_type = SCULPT_UNDO_FACE_SETS; - } - SCULPT_ensure_dyntopo_node_undo(ob, nodes[i], undo_type, extra_type); switch (undo_type) { @@ -5284,7 +5280,8 @@ static bool sculpt_needs_delta_from_anchored_origin(Brush *brush) return true; } if (brush->sculpt_tool == SCULPT_TOOL_CLOTH && - brush->cloth_deform_type == BRUSH_CLOTH_DEFORM_GRAB) { + brush->cloth_deform_type == BRUSH_CLOTH_DEFORM_GRAB) + { return true; } return false; @@ -6891,7 +6888,8 @@ void SCULPT_fake_neighbors_ensure(Sculpt *sd, Object *ob, const float max_dist) * recalculated. */ if (ss->fake_neighbors.fake_neighbor_index && - ss->fake_neighbors.current_max_distance == max_dist) { + ss->fake_neighbors.current_max_distance == max_dist) + { return; } diff --git a/source/blender/editors/sculpt_paint/sculpt_smooth.cc b/source/blender/editors/sculpt_paint/sculpt_smooth.cc index 37ac988e375..03aa4756b8e 100644 --- a/source/blender/editors/sculpt_paint/sculpt_smooth.cc +++ b/source/blender/editors/sculpt_paint/sculpt_smooth.cc @@ -13,6 +13,7 @@ #include "BLI_math_vector_types.hh" #include "BLI_set.hh" #include "BLI_task.h" +#include "BLI_timeit.hh" #include "BLI_vector.hh" #include "DNA_brush_types.h" @@ -1098,24 +1099,165 @@ void SCULPT_smooth_undo_push(Object *ob, Span nodes) { SculptSession *ss = ob->sculpt; - if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH && SCULPT_need_reproject(ss) && - CustomData_has_layer(&ss->bm->ldata, CD_PROP_FLOAT2)) - { - BM_log_entry_add_ex(ss->bm, ss->bm_log, true); - + if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH && SCULPT_need_reproject(ss)) { for (PBVHNode *node : nodes) { PBVHFaceIter fd; BKE_pbvh_face_iter_begin (ss->pbvh, node, fd) { - BM_log_face_modified(ss->bm, ss->bm_log, reinterpret_cast(fd.face.i)); + BM_log_face_if_modified(ss->bm, ss->bm_log, reinterpret_cast(fd.face.i)); } BKE_pbvh_face_iter_end(fd); } } } +#if 0 //NotForPR, see comment in BKE_pbvh_iter.hh +# include "BKE_mesh_mapping.h" +# include "BKE_pbvh_iter.hh" + void SCULPT_smooth( Sculpt *sd, Object *ob, Span nodes, float bstrength, const bool smooth_mask) { + // SCOPED_TIMER(__func__); + + SculptSession *ss = ob->sculpt; + Brush *brush = BKE_paint_brush(&sd->paint); + + PBVHType type = BKE_pbvh_type(ss->pbvh); + int iteration, count; + float last; + + SCULPT_ensure_vemap(ss); + SCULPT_boundary_info_ensure(ob); + SCULPT_smooth_undo_push(ob, nodes); + + CLAMP(bstrength, 0.0f, 1.0f); + + const int max_iterations = 4; + const float fract = 1.0f / max_iterations; + + count = int(bstrength * max_iterations); + last = max_iterations * (bstrength - count * fract); + + if (type == PBVH_FACES && ss->pmap.is_empty()) { + BLI_assert_msg(0, "sculpt smooth: pmap missing"); + return; + } + + for (iteration = 0; iteration <= count; iteration++) { + const float strength = (iteration != count) ? 1.0f : last; + + if (brush->flag2 & BRUSH_SMOOTH_USE_AREA_WEIGHT) { + BKE_pbvh_face_areas_begin(ss->pbvh); + } + + SculptBrushTest test; + SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( + ss, &test, brush->falloff_shape); + float projection = brush->autosmooth_projection; + bool weighted = brush->flag2 & BRUSH_SMOOTH_USE_AREA_WEIGHT; + const bool do_reproject = SCULPT_need_reproject(ss); + float hard_corner_pin = BKE_brush_hard_corner_pin_get(ss->scene, brush); + bool smooth_origco = SCULPT_tool_needs_smooth_origco(brush->sculpt_tool); + + struct MyNodeData { + AutomaskingNodeData automask_data; + }; + + blender::bke::pbvh::brush_vertex_iter( + ss->pbvh, + nodes, + true, + /* Filter verts. */ + [&](PBVHVertRef vertex, const float *co, const float *no, float mask) { + return bool(sculpt_brush_test_sq_fn(&test, co)); + }, + /* Visit nodes and set up thread data. */ + [&](PBVHNode *node) { + MyNodeData data; + + if (brush->flag2 & BRUSH_SMOOTH_USE_AREA_WEIGHT) { + BKE_pbvh_check_tri_areas(ss->pbvh, node); + } + + SCULPT_automasking_node_begin(ob, ss, ss->cache->automasking, &data.automask_data, node); + return data; + }, + /* Main worker. */ + [&](blender::bke::pbvh::VertexRange range) { + float bstrength = strength; + CLAMP(bstrength, 0.0f, 1.0f); + + /* Needed for SCULPT_brush_strength_factor. */ + const int thread_id = BLI_task_parallel_thread_id(nullptr); + + for (auto &vd : range) { + PBVHVertexIter dummy; + dummy.vertex = vd.vertex; + SCULPT_automasking_node_update(ss, &vd.userdata->automask_data, &dummy); + + float fade = bstrength * SCULPT_brush_strength_factor( + ss, + brush, + vd.co, + sqrtf(test.dist), + vd.no, + vd.no, + smooth_mask ? 0.0f : (vd.mask ? *vd.mask : 0.0f), + vd.vertex, + thread_id, + &vd.userdata->automask_data); + if (smooth_mask) { + float val = SCULPT_neighbor_mask_average(ss, vd.vertex) - *vd.mask; + val *= fade * bstrength; + *vd.mask += val; + CLAMP(*vd.mask, 0.0f, 1.0f); + } + else { + float oldco[3]; + float oldno[3]; + copy_v3_v3(oldco, vd.co); + SCULPT_vertex_normal_get(ss, vd.vertex, oldno); + + float avg[3], val[3]; + SCULPT_neighbor_coords_average_interior( + ss, avg, vd.vertex, projection, hard_corner_pin, weighted, false, fade); + + if (smooth_origco) { + float origco_avg[3]; + + SCULPT_neighbor_coords_average_interior( + ss, origco_avg, vd.vertex, projection, hard_corner_pin, weighted, true, fade); + + float *origco = blender::bke::paint::vertex_attr_ptr(vd.vertex, + ss->attrs.orig_co); + interp_v3_v3v3(origco, origco, origco_avg, fade); + } + + sub_v3_v3v3(val, avg, vd.co); + madd_v3_v3v3fl(val, vd.co, val, fade); + SCULPT_clip(sd, ss, vd.co, val); + + if (do_reproject) { + BKE_sculpt_reproject_cdata(ss, vd.vertex, oldco, oldno, false); + } + + if (vd.is_mesh) { + BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); + } + BKE_sculpt_sharp_boundary_flag_update(ss, vd.vertex); + } + } + }, + /* Visit nodes again */ + [&](PBVHNode *node) { BKE_pbvh_node_mark_update(node); }); + } +} +#else +void SCULPT_smooth( + Sculpt *sd, Object *ob, Span nodes, float bstrength, const bool smooth_mask) +{ + SCOPED_TIMER(__func__); + SculptSession *ss = ob->sculpt; Brush *brush = BKE_paint_brush(&sd->paint); @@ -1163,6 +1305,7 @@ void SCULPT_smooth( BLI_task_parallel_range(0, nodes.size(), &data, do_smooth_brush_task_cb_ex, &settings); } } +#endif void SCULPT_do_smooth_brush(Sculpt *sd, Object *ob, Span nodes) { diff --git a/source/blender/editors/sculpt_paint/sculpt_undo.cc b/source/blender/editors/sculpt_paint/sculpt_undo.cc index 1cf8bb414d3..6abcec9a465 100644 --- a/source/blender/editors/sculpt_paint/sculpt_undo.cc +++ b/source/blender/editors/sculpt_paint/sculpt_undo.cc @@ -2273,7 +2273,7 @@ static SculptUndoNode *sculpt_undo_bmesh_push(Object *ob, PBVHNode *node, Sculpt DyntopoSet *faces = BKE_pbvh_bmesh_node_faces(node); for (BMFace *f : *faces) { - BM_log_face_modified(ss->bm, ss->bm_log, f); + BM_log_face_if_modified(ss->bm, ss->bm_log, f); } break; -- 2.30.2 From b135761e5cbf1ca9773028ccfc47635ebcd9061c Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Fri, 30 Jun 2023 01:35:15 -0700 Subject: [PATCH 213/279] temp-sculpt-dyntopo: Fix a few undo crashes --- release/datafiles/startup.blend | Bin 899888 -> 901352 bytes .../blender/blenkernel/intern/customdata.cc | 7 +- source/blender/blenkernel/intern/dyntopo.cc | 2 + source/blender/bmesh/intern/bmesh_log.cc | 74 +++++++----------- 4 files changed, 37 insertions(+), 46 deletions(-) diff --git a/release/datafiles/startup.blend b/release/datafiles/startup.blend index 361ec8c3e5bf399f6a903610bc166cb17ad4f9cd..4ac3913dfb12577d892272e6235b362955caf592 100644 GIT binary patch delta 103752 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 diff --git a/source/blender/blenkernel/intern/customdata.cc b/source/blender/blenkernel/intern/customdata.cc index 0a73880acbb..42a37810290 100644 --- a/source/blender/blenkernel/intern/customdata.cc +++ b/source/blender/blenkernel/intern/customdata.cc @@ -100,7 +100,8 @@ bool CustomData_layout_is_same(const CustomData *_a, const CustomData *_b) a.layers = b.layers = nullptr; a.pool = b.pool = nullptr; - + a.maxlayer = b.maxlayer; + if (memcmp((void *)&a, (void *)&b, sizeof(CustomData)) != 0) { return false; } @@ -110,6 +111,8 @@ bool CustomData_layout_is_same(const CustomData *_a, const CustomData *_b) CustomDataLayer clb = _b->layers[i]; cla.data = clb.data = nullptr; + cla.anonymous_id = clb.anonymous_id = nullptr; + cla.sharing_info = clb.sharing_info = nullptr; if (memcmp((void *)&cla, (void *)&clb, sizeof(CustomDataLayer)) != 0) { return false; @@ -4136,7 +4139,7 @@ void CustomData_bmesh_free_block(CustomData *data, void **block) *block = nullptr; } -void CustomData_bmesh_free_block_data(CustomData *data, void *block) +ATTR_NO_OPT void CustomData_bmesh_free_block_data(CustomData *data, void *block) { if (block == nullptr) { return; diff --git a/source/blender/blenkernel/intern/dyntopo.cc b/source/blender/blenkernel/intern/dyntopo.cc index 8bcefbd6935..6c61c1df015 100644 --- a/source/blender/blenkernel/intern/dyntopo.cc +++ b/source/blender/blenkernel/intern/dyntopo.cc @@ -2373,6 +2373,8 @@ bool remesh_topology(BrushTester *brush_tester, void *mask_cb_data, float quality) { + BM_log_entry_check_customdata(pbvh->header.bm, pbvh->bm_log); + EdgeQueueContext eq_ctx( brush_tester, ob, pbvh, mode, use_frontface, view_normal, updatePBVH, mask_cb, mask_cb_data); eq_ctx.start(); diff --git a/source/blender/bmesh/intern/bmesh_log.cc b/source/blender/bmesh/intern/bmesh_log.cc index 802aee2969c..395b8e0787d 100644 --- a/source/blender/bmesh/intern/bmesh_log.cc +++ b/source/blender/bmesh/intern/bmesh_log.cc @@ -221,7 +221,7 @@ struct BMLogFace : public BMLogElem { Vector loop_customdata; // int material_index; - void free(CustomData *domain, CustomData *loop_domain) + ATTR_NO_OPT void free(CustomData *domain, CustomData *loop_domain) { BMLogElem::free(domain); @@ -471,6 +471,8 @@ struct BMLogEntry { BMLog *log = nullptr; bool dead = false; + bool cd_layout_changed = false; + BMLogEntry(BMIdMap *_idmap, CustomData *src_vdata, CustomData *src_edata, @@ -507,6 +509,22 @@ struct BMLogEntry { CustomData_bmesh_init_pool(&pdata, 0, BM_FACE); } + void copy_custom_data(CustomData *source, CustomData *dest, void *src_block, void **dest_block) + { + if (!*dest_block) { + *dest_block = BLI_mempool_calloc(dest->pool); + } + +#ifdef USE_SIMPLE_CD_COPY + /* Signal simple copy by using bm->XXXdata for dest. */ + if (!cd_layout_changed) { + dest = source; + } +#endif + + CustomData_bmesh_copy_data(source, dest, src_block, dest_block); + } + ~BMLogEntry() { dead = true; @@ -648,14 +666,7 @@ struct BMLogEntry { void update_logvert(BMesh *bm, BMVert *v, BMLogVert *lv) { - if (vdata.pool) { -#ifdef USE_SIMPLE_CD_COPY - /* Signal simple copy by using bm->vdata for dest. */ - CustomData_bmesh_copy_data(&bm->vdata, &bm->vdata, v->head.data, &lv->customdata); -#else - CustomData_bmesh_copy_data(&bm->vdata, &vdata, v->head.data, &lv->customdata); -#endif - } + copy_custom_data(&bm->vdata, &vdata, v->head.data, &lv->customdata); lv->co = v->co; lv->no = v->no; @@ -759,12 +770,7 @@ struct BMLogEntry { void update_logedge(BMesh *bm, BMEdge *e, BMLogEdge *le) { le->flag = e->head.hflag; -#ifdef USE_SIMPLE_CD_COPY - /* Signal simple copy by using bm->edata for dest. */ - CustomData_bmesh_copy_data(&bm->edata, &bm->edata, e->head.data, &le->customdata); -#else - CustomData_bmesh_copy_data(&bm->edata, &edata, e->head.data, &le->customdata); -#endif + copy_custom_data(&bm->edata, &edata, e->head.data, &le->customdata); } void free_logedge(BMesh * /*bm*/, BMLogEdge *le) @@ -784,12 +790,7 @@ struct BMLogEntry { lf->id = get_elem_id(bm, f); lf->flag = f->head.hflag; -#ifdef USE_SIMPLE_CD_COPY - /* Signal simple copy by using bm->pdata for dest. */ - CustomData_bmesh_copy_data(&bm->pdata, &bm->pdata, f->head.data, &lf->customdata); -#else - CustomData_bmesh_copy_data(&bm->pdata, &pdata, f->head.data, &lf->customdata); -#endif + copy_custom_data(&bm->pdata, &pdata, f->head.data, &lf->customdata); BMLoop *l = f->l_first; do { @@ -797,12 +798,7 @@ struct BMLogEntry { void *loop_customdata = nullptr; if (l->head.data) { -#ifdef USE_SIMPLE_CD_COPY - /* Signal simple copy by using bm->ldata for dest. */ - CustomData_bmesh_copy_data(&bm->ldata, &bm->ldata, l->head.data, &loop_customdata); -#else - CustomData_bmesh_copy_data(&bm->ldata, &ldata, l->head.data, &loop_customdata); -#endif + copy_custom_data(&bm->ldata, &ldata, l->head.data, &loop_customdata); } lf->loop_customdata.append(loop_customdata); @@ -815,12 +811,7 @@ struct BMLogEntry { { lf->flag = f->head.hflag; -#ifdef USE_SIMPLE_CD_COPY - /* Signal simple copy by using bm->ldata for dest. */ - CustomData_bmesh_copy_data(&bm->pdata, &bm->pdata, f->head.data, &lf->customdata); -#else - CustomData_bmesh_copy_data(&bm->pdata, &pdata, f->head.data, &lf->customdata); -#endif + copy_custom_data(&bm->pdata, &pdata, f->head.data, &lf->customdata); if (f->len != lf->verts.size()) { printf("%s: error: face length changed.\n", __func__); @@ -830,18 +821,11 @@ struct BMLogEntry { BMLoop *l = f->l_first; int i = 0; do { - void *loop_customdata = nullptr; - if (l->head.data) { -#ifdef USE_SIMPLE_CD_COPY - /* Signal simple copy by using bm->ldata for dest. */ - CustomData_bmesh_copy_data(&bm->ldata, &bm->ldata, l->head.data, &loop_customdata); -#else - CustomData_bmesh_copy_data(&bm->ldata, &ldata, l->head.data, &loop_customdata); -#endif + copy_custom_data(&bm->ldata, &ldata, l->head.data, &lf->loop_customdata[i]); } - lf->loop_customdata[i++] = loop_customdata; + i++; } while ((l = l->next) != f->l_first); } @@ -1727,7 +1711,7 @@ BMLogEntry *BM_log_entry_check_customdata(BMesh *bm, BMLog *log) if (!entry) { fprintf(stdout, "no current entry; creating...\n"); fflush(stdout); - return BM_log_entry_add_ex(bm, log, false); + return BM_log_entry_add_ex(bm, log, true); } CustomData *cd1[4] = {&bm->vdata, &bm->edata, &bm->ldata, &bm->pdata}; @@ -1737,7 +1721,9 @@ BMLogEntry *BM_log_entry_check_customdata(BMesh *bm, BMLog *log) if (!CustomData_layout_is_same(cd1[i], cd2[i])) { fprintf(stdout, "Customdata changed for undo\n"); fflush(stdout); - return BM_log_entry_add_ex(bm, log, false); + + entry->cd_layout_changed = true; + return BM_log_entry_add_ex(bm, log, true); } } -- 2.30.2 From ae401b4a0d86dc3cb9708187fef6dac177be02d7 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Fri, 30 Jun 2023 04:27:35 -0700 Subject: [PATCH 214/279] temp-sculpt-dyntopo: Add back missing nullptr check --- source/blender/bmesh/intern/bmesh_log.cc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/source/blender/bmesh/intern/bmesh_log.cc b/source/blender/bmesh/intern/bmesh_log.cc index 395b8e0787d..a09e38f346b 100644 --- a/source/blender/bmesh/intern/bmesh_log.cc +++ b/source/blender/bmesh/intern/bmesh_log.cc @@ -511,6 +511,10 @@ struct BMLogEntry { void copy_custom_data(CustomData *source, CustomData *dest, void *src_block, void **dest_block) { + if (!dest->pool) { + return; + } + if (!*dest_block) { *dest_block = BLI_mempool_calloc(dest->pool); } -- 2.30.2 From 00d4fa3dec0c326d24b93aa82d5c756ace9c0832 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Fri, 30 Jun 2023 15:19:30 -0700 Subject: [PATCH 215/279] temp-sculpt-dyntopo: Fix uv collapse bug Apparently you have to use minmax instead of centroid when collapsing UV edges. I thought I had all the weights summing to produce the same result, but apparently not. This is also how editmode does it btw, that's where I finally found the solution. In other news, there is now a debug option to show UV edges in the uv editor in sculpt mode (in dynamic topology mode). It's purely for testing and is not meant for master. To use, simply uncomment the DEBUG_SHOW_SCULPT_BM_UV_EDGES definition in BKE_sculpt.h. --- source/blender/blenkernel/BKE_sculpt.h | 3 + .../blender/blenkernel/intern/customdata.cc | 13 +- source/blender/blenkernel/intern/dyntopo.cc | 2 +- .../blenkernel/intern/dyntopo_collapse.cc | 133 +++++++++++------- .../draw/engines/overlay/overlay_edit_uv.cc | 43 +++++- .../draw/intern/draw_cache_extract_mesh.cc | 37 +++++ .../draw_cache_extract_mesh_render_data.cc | 33 ++++- .../draw/intern/draw_cache_impl_mesh.cc | 4 +- source/blender/editors/include/ED_image.h | 7 +- source/blender/editors/screen/screen_ops.c | 4 +- source/blender/editors/sculpt_paint/sculpt.cc | 20 +++ .../editors/sculpt_paint/sculpt_intern.hh | 10 +- .../editors/sculpt_paint/sculpt_undo.cc | 6 +- .../blender/editors/space_image/image_edit.c | 27 +--- .../blender/editors/space_image/image_ops.c | 2 +- .../blender/editors/space_image/image_undo.cc | 44 ++++++ .../blender/editors/space_image/space_image.c | 2 +- .../transform/transform_convert_mesh_uv.c | 2 +- .../editors/transform/transform_generics.c | 6 +- .../editors/transform/transform_gizmo_2d.c | 2 +- source/blender/editors/util/ed_util_imbuf.c | 2 +- source/blender/makesrna/intern/rna_space.cc | 2 +- 22 files changed, 297 insertions(+), 107 deletions(-) diff --git a/source/blender/blenkernel/BKE_sculpt.h b/source/blender/blenkernel/BKE_sculpt.h index dd7c057109d..5d3370e41eb 100644 --- a/source/blender/blenkernel/BKE_sculpt.h +++ b/source/blender/blenkernel/BKE_sculpt.h @@ -1,5 +1,8 @@ #pragma once +/* NotForPR: Show uv mesh in uv editor in sculpt mode. */ +//#define DEBUG_SHOW_SCULPT_BM_UV_EDGES + /** When #BRUSH_ACCUMULATE is used */ #define SCULPT_TOOL_HAS_ACCUMULATE(t) \ ELEM(t, \ diff --git a/source/blender/blenkernel/intern/customdata.cc b/source/blender/blenkernel/intern/customdata.cc index 42a37810290..4ee2575c8c2 100644 --- a/source/blender/blenkernel/intern/customdata.cc +++ b/source/blender/blenkernel/intern/customdata.cc @@ -101,7 +101,7 @@ bool CustomData_layout_is_same(const CustomData *_a, const CustomData *_b) a.layers = b.layers = nullptr; a.pool = b.pool = nullptr; a.maxlayer = b.maxlayer; - + if (memcmp((void *)&a, (void *)&b, sizeof(CustomData)) != 0) { return false; } @@ -410,7 +410,8 @@ static void layerCopyValue_normal(const void *source, if (ELEM(mixmode, CDT_MIX_NOMIX, CDT_MIX_REPLACE_ABOVE_THRESHOLD, - CDT_MIX_REPLACE_BELOW_THRESHOLD)) { + CDT_MIX_REPLACE_BELOW_THRESHOLD)) + { /* Above/below threshold modes are not supported here, fallback to nomix (just in case). */ copy_v3_v3(no_dst, no_src); } @@ -887,7 +888,8 @@ static void layerCopyValue_mloopcol(const void *source, if (ELEM(mixmode, CDT_MIX_NOMIX, CDT_MIX_REPLACE_ABOVE_THRESHOLD, - CDT_MIX_REPLACE_BELOW_THRESHOLD)) { + CDT_MIX_REPLACE_BELOW_THRESHOLD)) + { /* Modes that do a full copy or nothing. */ if (ELEM(mixmode, CDT_MIX_REPLACE_ABOVE_THRESHOLD, CDT_MIX_REPLACE_BELOW_THRESHOLD)) { /* TODO: Check for a real valid way to get 'factor' value of our dest color? */ @@ -1318,7 +1320,8 @@ static void layerCopyValue_propcol(const void *source, if (ELEM(mixmode, CDT_MIX_NOMIX, CDT_MIX_REPLACE_ABOVE_THRESHOLD, - CDT_MIX_REPLACE_BELOW_THRESHOLD)) { + CDT_MIX_REPLACE_BELOW_THRESHOLD)) + { /* Modes that do a full copy or nothing. */ if (ELEM(mixmode, CDT_MIX_REPLACE_ABOVE_THRESHOLD, CDT_MIX_REPLACE_BELOW_THRESHOLD)) { /* TODO: Check for a real valid way to get 'factor' value of our dest color? */ @@ -4139,7 +4142,7 @@ void CustomData_bmesh_free_block(CustomData *data, void **block) *block = nullptr; } -ATTR_NO_OPT void CustomData_bmesh_free_block_data(CustomData *data, void *block) +void CustomData_bmesh_free_block_data(CustomData *data, void *block) { if (block == nullptr) { return; diff --git a/source/blender/blenkernel/intern/dyntopo.cc b/source/blender/blenkernel/intern/dyntopo.cc index 6c61c1df015..6683b90ae34 100644 --- a/source/blender/blenkernel/intern/dyntopo.cc +++ b/source/blender/blenkernel/intern/dyntopo.cc @@ -2051,7 +2051,7 @@ EdgeQueueContext::EdgeQueueContext(BrushTester *brush_tester_, mask_cb_data = mask_cb_data_; view_normal = view_normal_; - ignore_loop_data = !bm->ldata.totlayer || !ss->reproject_smooth; + ignore_loop_data = !bm->ldata.totlayer; updatePBVH = updatePBVH_; cd_vert_mask_offset = pbvh->cd_vert_mask_offset; cd_vert_node_offset = pbvh->cd_vert_node_offset; diff --git a/source/blender/blenkernel/intern/dyntopo_collapse.cc b/source/blender/blenkernel/intern/dyntopo_collapse.cc index 7d92b1c8f99..1c96e334d37 100644 --- a/source/blender/blenkernel/intern/dyntopo_collapse.cc +++ b/source/blender/blenkernel/intern/dyntopo_collapse.cc @@ -101,7 +101,7 @@ static bool vert_is_nonmanifold(BMVert *v) template static void snap_corner_data( - BMesh *bm, BMEdge *e, BMVert *v_del, Span ls, int cd_uv, bool snap_midpoint) + BMesh *bm, BMEdge *e, BMVert *v_del, Span ls, int cd_offset, bool snap_midpoint) { using namespace blender; @@ -116,12 +116,10 @@ static void snap_corner_data( ls[i]->next->head.index = -1; /* So we can test l->next without checking if it's inside ls.*/ } + // XXX todo: preserve UV pins + /* Build snapping sets (of UV vertices). */ for (BMLoop *l1 : ls) { - if (!snap_midpoint && l1->v == v_del) { - continue; - } - if (l1->head.index != -1) { continue; } @@ -135,30 +133,25 @@ static void snap_corner_data( final_loops.append(l1); blocks[cur_set].append(l1->head.data); - weights[cur_set].append(1.0f); + weights[cur_set].append(l1->v == v_del && !snap_midpoint ? 0.0f : 1.0f); loops[cur_set].append(l1); - T uv1 = *BM_ELEM_CD_PTR(l1, cd_uv); + T uv1 = *BM_ELEM_CD_PTR(l1, cd_offset); - T uvnext = *BM_ELEM_CD_PTR(l1->next, cd_uv); - float limit = max_ff(math::distance_squared(uv1, uvnext) * 0.1f, 0.000025f); + T uvnext = *BM_ELEM_CD_PTR(l1->next, cd_offset); + float limit = max_ff(math::distance_squared(uv1, uvnext) * 0.1f, 0.00001f); for (BMLoop *l2 : ls) { - T uv2 = *BM_ELEM_CD_PTR(l2, cd_uv); + T uv2 = *BM_ELEM_CD_PTR(l2, cd_offset); if (l2 == l1 || l2->head.index != -1) { continue; } if (math::distance_squared(uv1, uv2) < limit) { - float w = 1.0; - if (!snap_midpoint && l2->v == v_del) { - w = 0.0f; - } - l2->head.index = cur_set; blocks[cur_set].append(l2->head.data); - weights[cur_set].append(w); + weights[cur_set].append(l2->v == v_del && !snap_midpoint ? 0.0f : 1.0f); loops[cur_set].append(l2); } } @@ -167,48 +160,45 @@ static void snap_corner_data( } /* Merge sets */ - for (int i : final_loops.index_range()) { - if (!final_loops[i]) { + for (int set1 : final_loops.index_range()) { + if (!final_loops[set1]) { continue; } - BMLoop *final_l = final_loops[i]; + BMLoop *final_l = final_loops[set1]; BMLoop *next_l = final_l->next; - if (final_l->e != e || next_l->head.index == -1 || next_l->head.index == i) { + if (final_l->e != e || next_l->head.index == -1 || next_l->head.index == set1) { continue; } - int set = next_l->head.index; + int set2 = next_l->head.index; - blocks[i].append(next_l->head.data); - weights[i].append(1.0f); - - for (void *block : blocks[set]) { - blocks[i].append(block); + for (void *block : blocks[set2]) { + blocks[set1].append(block); } - for (float w : weights[set]) { - weights[i].append(w); + for (float w : weights[set2]) { + weights[set1].append(w); } - for (BMLoop *l : loops[set]) { - l->head.index = i; + for (BMLoop *l : loops[set2]) { + l->head.index = set1; } /* Flag set as deleted. */ - final_loops[set] = nullptr; + final_loops[set2] = nullptr; } /* Perform final UV snapping. */ - for (int i : final_loops.index_range()) { - BMLoop *final_l = final_loops[i]; + for (int set : final_loops.index_range()) { + BMLoop *final_l = final_loops[set]; if (!final_l) { continue; } float totw = 0.0f; - for (float w : weights[i]) { + for (float w : weights[set]) { totw += w; } @@ -219,41 +209,76 @@ static void snap_corner_data( * just use uniform weights. */ - for (int j : weights[i].index_range()) { - weights[i][j] = 1.0f; + for (int i : weights[set].index_range()) { + weights[set][i] = 1.0f; } - totw = float(weights[i].size()); + totw = float(weights[set].size()); } totw = 1.0f / totw; - for (int j : weights[i].index_range()) { - weights[i][j] *= totw; + for (int i : weights[set].index_range()) { + weights[set][i] *= totw; } SumT sum = {}; - for (int j : blocks[i].index_range()) { - void *ptr = POINTER_OFFSET(blocks[i][j], cd_uv); - T value = *static_cast(ptr); + /* TODO: port this code to a BMesh library function and use it in the collapse_uvs BMesh + * operator. */ - if (std::is_same_v) { - sum[0] += float(value[0]) * weights[i][j]; - sum[1] += float(value[1]) * weights[i][j]; - sum[2] += float(value[2]) * weights[i][j]; - sum[3] += float(value[3]) * weights[i][j]; + /* Use minmax centroid. Seems like the edge weights should have summed so their centroid + * is the same as a minmax centroid,but apparently not, and this is how editmode does it. + */ +#if 1 + SumT min = {}, max = {}; + + for (int i = 0; i < SumT::type_length; i++) { + min[i] = FLT_MAX; + max[i] = FLT_MIN; + } + + for (int i : blocks[set].index_range()) { + T value1 = *static_cast(POINTER_OFFSET(blocks[set][i], cd_offset)); + SumT value; + + if (weights[set][i] == 0.0f) { + continue; + } + + if constexpr (std::is_same_v) { + value = SumT(value1[0], value1[1], value1[2], value1[3]); } else { - sum += value * weights[i][j]; + value = value1; } + + min = math::min(value, min); + max = math::max(value, max); } + sum = (min + max) * 0.5f; +#else /* Original centroid version. */ + for (int i : blocks[set].index_range()) { + T value = *static_cast(POINTER_OFFSET(blocks[set][i], cd_offset)); + + if (std::is_same_v) { + sum[0] += float(value[0]) * weights[set][i]; + sum[1] += float(value[1]) * weights[set][i]; + sum[2] += float(value[2]) * weights[set][i]; + sum[3] += float(value[3]) * weights[set][i]; + } + else { + sum += value * weights[set][i]; + } + } +#endif + if constexpr (std::is_same_v) { - *BM_ELEM_CD_PTR(final_l, cd_uv) = sum; + *BM_ELEM_CD_PTR(final_l, cd_offset) = sum; } else if constexpr (std::is_same_v) { - *BM_ELEM_CD_PTR(final_l, - cd_uv) = {uchar(sum[0]), uchar(sum[1]), uchar(sum[2]), uchar(sum[3])}; + *BM_ELEM_CD_PTR(final_l, cd_offset) = { + uchar(sum[0]), uchar(sum[1]), uchar(sum[2]), uchar(sum[3])}; } } @@ -267,7 +292,7 @@ static void snap_corner_data( int set = l->head.index; if (final_loops[set] && l != final_loops[set]) { - *BM_ELEM_CD_PTR(l, cd_uv) = *BM_ELEM_CD_PTR(final_loops[set], cd_uv); + *BM_ELEM_CD_PTR(l, cd_offset) = *BM_ELEM_CD_PTR(final_loops[set], cd_offset); } } } @@ -281,7 +306,7 @@ bool pbvh_bmesh_collapse_edge_uvs( pbvh_check_vert_boundary_bmesh(pbvh, v_del); int boundflag1 = BM_ELEM_CD_GET_INT(v_conn, pbvh->cd_boundary_flag); - int boundflag2 = BM_ELEM_CD_GET_INT(v_del, pbvh->cd_boundary_flag); + // int boundflag2 = BM_ELEM_CD_GET_INT(v_del, pbvh->cd_boundary_flag); /*have to check edge flags directly, vertex flag test above isn't specific enough and can sometimes let bad edges through*/ @@ -332,7 +357,7 @@ bool pbvh_bmesh_collapse_edge_uvs( /* Append loops around edge first. */ BMLoop *l = e->l; do { - ls.append_non_duplicates(l); + ls.append(l); } while ((l = l->radial_next) != e->l); /* Now find loops around e->v1 and e->v2. */ diff --git a/source/blender/draw/engines/overlay/overlay_edit_uv.cc b/source/blender/draw/engines/overlay/overlay_edit_uv.cc index 10dac848033..7095e5a7a8e 100644 --- a/source/blender/draw/engines/overlay/overlay_edit_uv.cc +++ b/source/blender/draw/engines/overlay/overlay_edit_uv.cc @@ -17,6 +17,7 @@ #include "BKE_mask.h" #include "BKE_object.h" #include "BKE_paint.h" +#include "BKE_sculpt.h" #include "DNA_brush_types.h" #include "DNA_mesh_types.h" @@ -110,18 +111,28 @@ void OVERLAY_edit_uv_init(OVERLAY_Data *vedata) IMA_TYPE_UV_TEST); const bool is_uv_editor = sima->mode == SI_MODE_UV; const bool has_edit_object = (draw_ctx->object_edit) != nullptr; + +#ifdef DEBUG_SHOW_SCULPT_BM_UV_EDGES + const bool has_sculpt_object = draw_ctx->obact->mode == OB_MODE_SCULPT && + draw_ctx->obact->sculpt && draw_ctx->obact->sculpt->bm; +#else + const bool has_sculpt_object = false; +#endif + const bool is_paint_mode = sima->mode == SI_MODE_PAINT; const bool is_view_mode = sima->mode == SI_MODE_VIEW; const bool is_mask_mode = sima->mode == SI_MODE_MASK; - const bool is_edit_mode = draw_ctx->object_mode == OB_MODE_EDIT; - const bool do_uv_overlay = is_image_type && is_uv_editor && has_edit_object; + // NotForPR + const bool is_edit_mode = draw_ctx->object_mode == OB_MODE_EDIT || has_sculpt_object; + const bool do_uv_overlay = is_image_type && is_uv_editor && + (has_edit_object || has_sculpt_object); const bool show_modified_uvs = sima->flag & SI_DRAWSHADOW; const bool is_tiled_image = image && (image->source == IMA_SRC_TILED); const bool do_edges_only = (ts->uv_flag & UV_SYNC_SELECTION) ? - /* NOTE: Ignore #SCE_SELECT_EDGE because a single selected edge - * on the mesh may cause single UV vertices to be selected. */ - false : - (ts->uv_selectmode == UV_SELECT_EDGE); + /* NOTE: Ignore #SCE_SELECT_EDGE because a single selected edge + * on the mesh may cause single UV vertices to be selected. */ + false : + (ts->uv_selectmode == UV_SELECT_EDGE); const bool do_faces = ((sima->flag & SI_NO_DRAWFACES) == 0); const bool do_face_dots = (ts->uv_flag & UV_SYNC_SELECTION) ? (ts->selectmode & SCE_SELECT_FACE) != 0 : @@ -431,6 +442,18 @@ void OVERLAY_edit_uv_cache_init(OVERLAY_Data *vedata) } } +static CustomData *get_loop_customdata(Object *ob, Mesh *me) +{ + if (ob->mode == OB_MODE_EDIT) { + return &me->edit_mesh->bm->ldata; + } +#ifdef DEBUG_SHOW_SCULPT_BM_UV_EDGES + else if (ob->mode == OB_MODE_SCULPT && ob->sculpt && ob->sculpt->bm) { + return &ob->sculpt->bm->ldata; + } +#endif +} + static void overlay_edit_uv_cache_populate(OVERLAY_Data *vedata, Object *ob) { if (!(DRW_object_visibility_in_active_context(ob) & OB_VISIBLE_SELF)) { @@ -442,12 +465,18 @@ static void overlay_edit_uv_cache_populate(OVERLAY_Data *vedata, Object *ob) GPUBatch *geom; const DRWContextState *draw_ctx = DRW_context_state_get(); +#ifdef DEBUG_SHOW_SCULPT_BM_UV_EDGES + const bool is_edit_object = DRW_object_is_in_edit_mode(ob) || + (ob->mode == OB_MODE_SCULPT && ob->sculpt && ob->sculpt->bm); +#else const bool is_edit_object = DRW_object_is_in_edit_mode(ob); +#endif + Mesh *me = (Mesh *)ob->data; const bool has_active_object_uvmap = CustomData_get_active_layer(&me->ldata, CD_PROP_FLOAT2) != -1; const bool has_active_edit_uvmap = is_edit_object && - (CustomData_get_active_layer(&me->edit_mesh->bm->ldata, + (CustomData_get_active_layer(get_loop_customdata(ob, me), CD_PROP_FLOAT2) != -1); const bool draw_shadows = (draw_ctx->object_mode != OB_MODE_OBJECT) && (ob->mode == draw_ctx->object_mode); diff --git a/source/blender/draw/intern/draw_cache_extract_mesh.cc b/source/blender/draw/intern/draw_cache_extract_mesh.cc index 7757ba0570f..fcce3479b1d 100644 --- a/source/blender/draw/intern/draw_cache_extract_mesh.cc +++ b/source/blender/draw/intern/draw_cache_extract_mesh.cc @@ -22,6 +22,8 @@ #include "BLI_vector.hh" #include "BKE_editmesh.h" +#include "BKE_paint.h" //NotForPR, show uv edges in sculpt mode +#include "BKE_sculpt.h" //NotForPR show uv edges in sculpt mode #include "GPU_capabilities.h" @@ -381,11 +383,46 @@ BLI_INLINE void extract_task_range_run_iter(const MeshRenderData *mr, ExtractorIterData range_data; range_data.mr = mr; +#ifdef DEBUG_SHOW_SCULPT_BM_UV_EDGES + Vector looptris; +#endif + TaskParallelRangeFunc func; int stop; switch (iter_type) { case MR_ITER_LOOPTRI: +#ifdef DEBUG_SHOW_SCULPT_BM_UV_EDGES + if (is_mesh || mr->edit_bmesh) { + range_data.elems = is_mesh ? mr->looptris.data() : (void *)mr->edit_bmesh->looptris; + } + else { + /* Generate triangles ad-hoc. */ + BMIter iter; + BMFace *f; + BM_ITER_MESH (f, &iter, mr->bm, BM_FACES_OF_MESH) { + BMLoop *l = f->l_first; + + switch (f->len) { + case 4: + looptris.append(l); + looptris.append(l->next->next); + looptris.append(l->next->next->next); + case 3: + looptris.append(l); + looptris.append(l->next); + looptris.append(l->next->next); + break; + default: + /* Unsupported. */ + break; + } + } + + range_data.elems = static_cast(looptris.data()); + } +#else range_data.elems = is_mesh ? mr->looptris.data() : (void *)mr->edit_bmesh->looptris; +#endif func = is_mesh ? extract_range_iter_looptri_mesh : extract_range_iter_looptri_bm; stop = mr->tri_len; break; diff --git a/source/blender/draw/intern/draw_cache_extract_mesh_render_data.cc b/source/blender/draw/intern/draw_cache_extract_mesh_render_data.cc index a094c716965..5c8e32422af 100644 --- a/source/blender/draw/intern/draw_cache_extract_mesh_render_data.cc +++ b/source/blender/draw/intern/draw_cache_extract_mesh_render_data.cc @@ -20,6 +20,7 @@ #include "BKE_editmesh_cache.h" #include "BKE_mesh.hh" #include "BKE_mesh_runtime.h" +#include "BKE_paint.h" #include "GPU_batch.h" @@ -417,7 +418,37 @@ MeshRenderData *mesh_render_data_create(Object *object, copy_m4_m4(mr->obmat, obmat); - if (is_editmode) { + // NorForPR + if (!is_editmode && object->mode == OB_MODE_SCULPT && object->sculpt && object->sculpt->bm) { + mr->bm = object->sculpt->bm; + mr->edit_bmesh = nullptr; + mr->me = me; + mr->edit_data = is_mode_active ? mr->me->runtime->edit_data : nullptr; + mr->hide_unmapped_edges = false; + int bm_ensure_types = BM_VERT | BM_EDGE | BM_LOOP | BM_FACE; + + BM_mesh_elem_index_ensure(mr->bm, bm_ensure_types); + BM_mesh_elem_table_ensure(mr->bm, bm_ensure_types & ~BM_LOOP); + + mr->efa_act_uv = EDBM_uv_active_face_get(mr->edit_bmesh, false, false); + mr->efa_act = BM_mesh_active_face_get(mr->bm, false, true); + mr->eed_act = BM_mesh_active_edge_get(mr->bm); + mr->eve_act = BM_mesh_active_vert_get(mr->bm); + + mr->vert_crease_ofs = CustomData_get_offset_named( + &mr->bm->vdata, CD_PROP_FLOAT, "crease_vert"); + mr->edge_crease_ofs = CustomData_get_offset_named( + &mr->bm->edata, CD_PROP_FLOAT, "crease_edge"); + mr->bweight_ofs = CustomData_get_offset_named( + &mr->bm->edata, CD_PROP_FLOAT, "bevel_weight_edge"); +#ifdef WITH_FREESTYLE + mr->freestyle_edge_ofs = CustomData_get_offset(&mr->bm->edata, CD_FREESTYLE_EDGE); + mr->freestyle_face_ofs = CustomData_get_offset(&mr->bm->pdata, CD_FREESTYLE_FACE); +#endif + + mr->extract_type = MR_EXTRACT_BMESH; + } + else if (is_editmode) { Mesh *editmesh_eval_final = BKE_object_get_editmesh_eval_final(object); Mesh *editmesh_eval_cage = BKE_object_get_editmesh_eval_cage(object); diff --git a/source/blender/draw/intern/draw_cache_impl_mesh.cc b/source/blender/draw/intern/draw_cache_impl_mesh.cc index e466029f8b6..97438061320 100644 --- a/source/blender/draw/intern/draw_cache_impl_mesh.cc +++ b/source/blender/draw/intern/draw_cache_impl_mesh.cc @@ -45,6 +45,7 @@ #include "BKE_paint.h" #include "BKE_pbvh.h" #include "BKE_subdiv_modifier.h" +#include "BKE_sculpt.h" //NotForPR #include "atomic_ops.h" @@ -1385,7 +1386,8 @@ void DRW_mesh_batch_cache_create_requested(TaskGraph *task_graph, DRW_object_is_in_edit_mode(ob); /* This could be set for paint mode too, currently it's only used for edit-mode. */ - const bool is_mode_active = is_editmode && DRW_object_is_in_edit_mode(ob); + const bool is_mode_active = (is_editmode && DRW_object_is_in_edit_mode(ob)) || + ((ob->mode == OB_MODE_SCULPT) && ob->sculpt && ob->sculpt->bm); DRWBatchFlag batch_requested = cache->batch_requested; cache->batch_requested = (DRWBatchFlag)0; diff --git a/source/blender/editors/include/ED_image.h b/source/blender/editors/include/ED_image.h index 1781d145f92..9dbcba5f4ea 100644 --- a/source/blender/editors/include/ED_image.h +++ b/source/blender/editors/include/ED_image.h @@ -132,7 +132,12 @@ bool ED_image_slot_cycle(struct Image *image, int direction); bool ED_space_image_show_render(const struct SpaceImage *sima); bool ED_space_image_show_paint(const struct SpaceImage *sima); -bool ED_space_image_show_uvedit(const struct SpaceImage *sima, struct Object *obedit); + +/* NotForPR: show uv edges in sculpt dynamic topology mode. */ +bool ED_space_image_show_uvedit(const struct SpaceImage *sima, + struct Object *obedit, + struct Object *obact, + bool is_operator_poll); bool ED_space_image_paint_curve(const struct bContext *C); diff --git a/source/blender/editors/screen/screen_ops.c b/source/blender/editors/screen/screen_ops.c index 2bcf84ee384..dc20dd95bbe 100644 --- a/source/blender/editors/screen/screen_ops.c +++ b/source/blender/editors/screen/screen_ops.c @@ -584,14 +584,14 @@ bool ED_operator_uvedit(bContext *C) { SpaceImage *sima = CTX_wm_space_image(C); Object *obedit = CTX_data_edit_object(C); - return ED_space_image_show_uvedit(sima, obedit); + return ED_space_image_show_uvedit(sima, obedit, CTX_data_active_object(C), true); } bool ED_operator_uvedit_space_image(bContext *C) { SpaceImage *sima = CTX_wm_space_image(C); Object *obedit = CTX_data_edit_object(C); - return sima && ED_space_image_show_uvedit(sima, obedit); + return sima && ED_space_image_show_uvedit(sima, obedit, CTX_data_active_object(C), true); } bool ED_operator_uvmap(bContext *C) diff --git a/source/blender/editors/sculpt_paint/sculpt.cc b/source/blender/editors/sculpt_paint/sculpt.cc index a2adc208e03..8308733aa1c 100644 --- a/source/blender/editors/sculpt_paint/sculpt.cc +++ b/source/blender/editors/sculpt_paint/sculpt.cc @@ -57,12 +57,16 @@ #include "BKE_pbvh.h" #include "BKE_report.h" #include "BKE_scene.h" +#include "BKE_sculpt.h" #include "BKE_subdiv_ccg.h" #include "BKE_subsurf.h" #include "NOD_texture.h" #include "DEG_depsgraph.h" +#ifdef DEBUG_SHOW_SCULPT_BM_UV_EDGES +# include "DEG_depsgraph_query.h" +#endif #include "WM_api.h" #include "WM_types.h" @@ -6086,6 +6090,18 @@ void SCULPT_update_object_bounding_box(Object *ob) } } +void SCULPT_tag_uveditor_update(bContext *C, Depsgraph *depsgraph, Object *ob) +{ +#ifdef DEBUG_SHOW_SCULPT_BM_UV_EDGES + Object *obedit_eval = DEG_get_evaluated_object(depsgraph, ob); + BKE_mesh_batch_cache_dirty_tag(static_cast(obedit_eval->data), + BKE_MESH_BATCH_DIRTY_UVEDIT_ALL); + + DEG_id_tag_update(&ob->id, ID_RECALC_EDITORS | ID_RECALC_SELECT); + WM_event_add_notifier(C, NC_GEOM | ND_SELECT, ob->data); +#endif +} + void SCULPT_flush_update_step(bContext *C, SculptUpdateType update_flags) { using namespace blender; @@ -6115,6 +6131,10 @@ void SCULPT_flush_update_step(bContext *C, SculptUpdateType update_flags) } } +#ifdef DEBUG_SHOW_SCULPT_BM_UV_EDGES + SCULPT_tag_uveditor_update(C, depsgraph, ob); +#endif + DEG_id_tag_update(&ob->id, ID_RECALC_SHADING); /* Only current viewport matters, slower update for all viewports will diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.hh b/source/blender/editors/sculpt_paint/sculpt_intern.hh index 8ef77524845..25919648650 100644 --- a/source/blender/editors/sculpt_paint/sculpt_intern.hh +++ b/source/blender/editors/sculpt_paint/sculpt_intern.hh @@ -1262,7 +1262,8 @@ void SCULPT_vertex_neighbors_get(const struct SculptSession *ss, #define SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN(ss, v_index, neighbor_iterator) \ SCULPT_vertex_neighbors_get(ss, v_index, false, &neighbor_iterator); \ for (neighbor_iterator.i = 0; neighbor_iterator.i < neighbor_iterator.size; \ - neighbor_iterator.i++) { \ + neighbor_iterator.i++) \ + { \ neighbor_iterator.has_edge = neighbor_iterator.neighbors[neighbor_iterator.i].edge.i != \ PBVH_REF_NONE; \ neighbor_iterator.vertex = neighbor_iterator.neighbors[neighbor_iterator.i].vertex; \ @@ -1287,7 +1288,8 @@ void SCULPT_vertex_neighbors_get(const struct SculptSession *ss, #define SCULPT_VERTEX_NEIGHBORS_ITER_END(neighbor_iterator) \ } \ if (!neighbor_iterator.no_free && \ - neighbor_iterator.neighbors != neighbor_iterator.neighbors_fixed) { \ + neighbor_iterator.neighbors != neighbor_iterator.neighbors_fixed) \ + { \ MEM_freeN(neighbor_iterator.neighbors); \ MEM_freeN(neighbor_iterator.neighbor_indices); \ } \ @@ -2454,3 +2456,7 @@ int SCULPT_get_symmetry_pass(const struct SculptSession *ss); * autosmooth. */ #define SCULPT_tool_needs_smooth_origco(tool) ELEM(tool, SCULPT_TOOL_DRAW_SHARP) + +#ifdef DEBUG_SHOW_SCULPT_BM_UV_EDGES +void SCULPT_tag_uveditor_update(bContext *C, Depsgraph *depsgraph, Object *ob); +#endif diff --git a/source/blender/editors/sculpt_paint/sculpt_undo.cc b/source/blender/editors/sculpt_paint/sculpt_undo.cc index 6abcec9a465..9147c564150 100644 --- a/source/blender/editors/sculpt_paint/sculpt_undo.cc +++ b/source/blender/editors/sculpt_paint/sculpt_undo.cc @@ -1424,6 +1424,9 @@ static void sculpt_undo_restore_list(bContext *C, Depsgraph *depsgraph, ListBase } DEG_id_tag_update(&ob->id, ID_RECALC_SHADING); +#ifdef DEBUG_SHOW_SCULPT_BM_UV_EDGES + SCULPT_tag_uveditor_update(C, depsgraph, ob); +#endif sculpt_undo_print_nodes(ob, nullptr); @@ -1433,7 +1436,8 @@ static void sculpt_undo_restore_list(bContext *C, Depsgraph *depsgraph, ListBase * ensure object is updated after the node is handled. */ const SculptUndoNode *first_unode = (const SculptUndoNode *)lb->first; if (first_unode->type != SCULPT_UNDO_GEOMETRY && - first_unode->type != SCULPT_UNDO_DYNTOPO_BEGIN) { + first_unode->type != SCULPT_UNDO_DYNTOPO_BEGIN) + { BKE_sculpt_update_object_for_edit(depsgraph, ob, false, need_mask, false); } diff --git a/source/blender/editors/space_image/image_edit.c b/source/blender/editors/space_image/image_edit.c index 9fc19691de7..9e6ff56990e 100644 --- a/source/blender/editors/space_image/image_edit.c +++ b/source/blender/editors/space_image/image_edit.c @@ -101,7 +101,7 @@ void ED_space_image_auto_set(const bContext *C, SpaceImage *sima) /* Track image assigned to active face in edit mode. */ Object *ob = CTX_data_active_object(C); - if (!(ob && (ob->mode & OB_MODE_EDIT) && ED_space_image_show_uvedit(sima, ob))) { + if (!(ob && (ob->mode & OB_MODE_EDIT) && ED_space_image_show_uvedit(sima, ob, ob, false))) { return; } @@ -459,33 +459,10 @@ bool ED_space_image_show_paint(const SpaceImage *sima) return (sima->mode == SI_MODE_PAINT); } -bool ED_space_image_show_uvedit(const SpaceImage *sima, Object *obedit) -{ - if (sima) { - if (ED_space_image_show_render(sima)) { - return false; - } - if (sima->mode != SI_MODE_UV) { - return false; - } - } - - if (obedit && obedit->type == OB_MESH) { - BMEditMesh *em = BKE_editmesh_from_object(obedit); - bool ret; - - ret = EDBM_uv_check(em); - - return ret; - } - - return false; -} - bool ED_space_image_check_show_maskedit(SpaceImage *sima, Object *obedit) { /* check editmode - this is reserved for UV editing */ - if (obedit && ED_space_image_show_uvedit(sima, obedit)) { + if (obedit && ED_space_image_show_uvedit(sima, obedit, NULL, false)) { return false; } diff --git a/source/blender/editors/space_image/image_ops.c b/source/blender/editors/space_image/image_ops.c index 5fe20ab29df..4d3f19554e3 100644 --- a/source/blender/editors/space_image/image_ops.c +++ b/source/blender/editors/space_image/image_ops.c @@ -952,7 +952,7 @@ static int image_view_selected_exec(bContext *C, wmOperator *UNUSED(op)) /* get bounds */ float min[2], max[2]; - if (ED_space_image_show_uvedit(sima, obedit)) { + if (ED_space_image_show_uvedit(sima, obedit, NULL, false)) { uint objects_len = 0; Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs( scene, view_layer, ((View3D *)NULL), &objects_len); diff --git a/source/blender/editors/space_image/image_undo.cc b/source/blender/editors/space_image/image_undo.cc index 13b19f6a3b5..d5a3b574dae 100644 --- a/source/blender/editors/space_image/image_undo.cc +++ b/source/blender/editors/space_image/image_undo.cc @@ -41,6 +41,7 @@ #include "BKE_context.h" #include "BKE_image.h" #include "BKE_paint.h" +#include "BKE_sculpt.h" /* for DEBUG_SHOW_SCULPT_BM_UV_EDGES */ #include "BKE_undo_system.h" #include "DEG_depsgraph.h" @@ -1164,3 +1165,46 @@ void ED_image_undo_push_end(void) } /** \} */ + +/* NotForPR: shows uv edges in sculpt mode. Moved to image_undo.cc for easier reversion (it needs + * C++ to read .bm from ob->sculpt). + */ + +#include "BKE_editmesh.h" + +#include "ED_image.h" +#include "ED_mesh.h" + +extern "C" bool ED_space_image_show_uvedit(const SpaceImage *sima, + Object *obedit, + Object *obact, + bool is_operator_poll) +{ + if (sima) { + if (ED_space_image_show_render(sima)) { + return false; + } + if (sima->mode != SI_MODE_UV) { + return false; + } + } + + if (obedit && obedit->type == OB_MESH) { + BMEditMesh *em = BKE_editmesh_from_object(obedit); + bool ret; + + ret = EDBM_uv_check(em); + + return ret; + } + +#ifdef DEBUG_SHOW_SCULPT_BM_UV_EDGES + if (obact && obact->type == OB_MESH && obact->mode == OB_MODE_SCULPT && obact->sculpt && + obact->sculpt->bm) + { + return true; + } +#endif + + return false; +} diff --git a/source/blender/editors/space_image/space_image.c b/source/blender/editors/space_image/space_image.c index ba1c00048a3..a9aeabab301 100644 --- a/source/blender/editors/space_image/space_image.c +++ b/source/blender/editors/space_image/space_image.c @@ -649,7 +649,7 @@ static void image_main_region_draw(const bContext *C, ARegion *region) image_main_region_set_view2d(sima, region); /* check for mask (delay draw) */ - if (!ED_space_image_show_uvedit(sima, obedit) && sima->mode == SI_MODE_MASK) { + if (!ED_space_image_show_uvedit(sima, obedit, NULL, false) && sima->mode == SI_MODE_MASK) { mask = ED_space_image_get_mask(sima); } diff --git a/source/blender/editors/transform/transform_convert_mesh_uv.c b/source/blender/editors/transform/transform_convert_mesh_uv.c index 3ad2bc0623e..4af3feecbdf 100644 --- a/source/blender/editors/transform/transform_convert_mesh_uv.c +++ b/source/blender/editors/transform/transform_convert_mesh_uv.c @@ -265,7 +265,7 @@ static void createTransUVs(bContext *C, TransInfo *t) int count = 0, countsel = 0; const BMUVOffsets offsets = BM_uv_map_get_offsets(em->bm); - if (!ED_space_image_show_uvedit(sima, tc->obedit)) { + if (!ED_space_image_show_uvedit(sima, tc->obedit, CTX_data_active_object(C), false)) { continue; } diff --git a/source/blender/editors/transform/transform_generics.c b/source/blender/editors/transform/transform_generics.c index 1eec7b28805..dad9838499c 100644 --- a/source/blender/editors/transform/transform_generics.c +++ b/source/blender/editors/transform/transform_generics.c @@ -284,7 +284,11 @@ void initTransInfo(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve else if (t->spacetype == SPACE_IMAGE) { SpaceImage *sima = area->spacedata.first; BKE_view_layer_synced_ensure(t->scene, t->view_layer); - if (ED_space_image_show_uvedit(sima, BKE_view_layer_active_object_get(t->view_layer))) { + if (ED_space_image_show_uvedit(sima, + BKE_view_layer_active_object_get(t->view_layer), + CTX_data_active_object(C), + false)) + { /* UV transform */ } else if (sima->mode == SI_MODE_MASK) { diff --git a/source/blender/editors/transform/transform_gizmo_2d.c b/source/blender/editors/transform/transform_gizmo_2d.c index 75e31637fbc..eae746fb963 100644 --- a/source/blender/editors/transform/transform_gizmo_2d.c +++ b/source/blender/editors/transform/transform_gizmo_2d.c @@ -76,7 +76,7 @@ static bool gizmo2d_generic_poll(const bContext *C, wmGizmoGroupType *gzgt) case SPACE_IMAGE: { const SpaceImage *sima = area->spacedata.first; Object *obedit = CTX_data_edit_object(C); - if (!ED_space_image_show_uvedit(sima, obedit)) { + if (!ED_space_image_show_uvedit(sima, obedit, CTX_data_active_object(C), false)) { return false; } break; diff --git a/source/blender/editors/util/ed_util_imbuf.c b/source/blender/editors/util/ed_util_imbuf.c index acea65f03da..2e7d219021c 100644 --- a/source/blender/editors/util/ed_util_imbuf.c +++ b/source/blender/editors/util/ed_util_imbuf.c @@ -544,7 +544,7 @@ bool ED_imbuf_sample_poll(bContext *C) if (obedit) { /* Disable when UV editing so it doesn't swallow all click events * (use for setting cursor). */ - if (ED_space_image_show_uvedit(sima, obedit)) { + if (ED_space_image_show_uvedit(sima, obedit, CTX_data_active_object(C), false)) { return false; } } diff --git a/source/blender/makesrna/intern/rna_space.cc b/source/blender/makesrna/intern/rna_space.cc index 28cb580a9ae..2cd98ccfa48 100644 --- a/source/blender/makesrna/intern/rna_space.cc +++ b/source/blender/makesrna/intern/rna_space.cc @@ -1704,7 +1704,7 @@ static bool rna_SpaceImageEditor_show_uvedit_get(PointerRNA *ptr) BKE_view_layer_synced_ensure(scene, view_layer); obedit = BKE_view_layer_edit_object_get(view_layer); } - return ED_space_image_show_uvedit(sima, obedit); + return ED_space_image_show_uvedit(sima, obedit, NULL, false); } static bool rna_SpaceImageEditor_show_maskedit_get(PointerRNA *ptr) -- 2.30.2 From 4731773294004ea8f308b2380f012e85269b53b3 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Fri, 30 Jun 2023 15:26:09 -0700 Subject: [PATCH 216/279] temp-sculpt-dyntopo: Yet again tweak num. stability smoothing factor I may have to come up with some sort of hueristic for this. --- source/blender/blenkernel/intern/dyntopo_intern.hh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/blenkernel/intern/dyntopo_intern.hh b/source/blender/blenkernel/intern/dyntopo_intern.hh index 8c446c323de..e0761bee71b 100644 --- a/source/blender/blenkernel/intern/dyntopo_intern.hh +++ b/source/blender/blenkernel/intern/dyntopo_intern.hh @@ -143,7 +143,7 @@ inline bool bm_elem_is_free(BMElem *elem, int htype) * applied stochastically (by skipping verts randomly) to improve * performance. */ -#define DYNTOPO_SAFE_SMOOTH_FAC 0.1f +#define DYNTOPO_SAFE_SMOOTH_FAC 0.02f #ifdef USE_EDGEQUEUE_EVEN_SUBDIV # include "BKE_global.h" -- 2.30.2 From c26986136967af4d0a314aee084ba9e1a8718b0b Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Fri, 30 Jun 2023 15:31:22 -0700 Subject: [PATCH 217/279] temp-sculpt-dyntopo: Remove debug timer --- source/blender/editors/sculpt_paint/sculpt_smooth.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/editors/sculpt_paint/sculpt_smooth.cc b/source/blender/editors/sculpt_paint/sculpt_smooth.cc index 03aa4756b8e..8a5208697e2 100644 --- a/source/blender/editors/sculpt_paint/sculpt_smooth.cc +++ b/source/blender/editors/sculpt_paint/sculpt_smooth.cc @@ -1256,7 +1256,7 @@ void SCULPT_smooth( void SCULPT_smooth( Sculpt *sd, Object *ob, Span nodes, float bstrength, const bool smooth_mask) { - SCOPED_TIMER(__func__); + //SCOPED_TIMER(__func__); SculptSession *ss = ob->sculpt; Brush *brush = BKE_paint_brush(&sd->paint); -- 2.30.2 From 71148abcd16f00cdcb6328bc20a1fa8c3c142888 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Fri, 30 Jun 2023 15:48:18 -0700 Subject: [PATCH 218/279] temp-sculpt-dyntopo: Don't update sharp angle flags when not requested This was happening inside the dyntopo remesher loop, which led to mesh artifacts. --- source/blender/blenkernel/intern/dyntopo.cc | 13 +++++++++ .../blender/blenkernel/intern/pbvh_bmesh.cc | 28 +++++++++++++------ 2 files changed, 32 insertions(+), 9 deletions(-) diff --git a/source/blender/blenkernel/intern/dyntopo.cc b/source/blender/blenkernel/intern/dyntopo.cc index 6683b90ae34..50587c023d0 100644 --- a/source/blender/blenkernel/intern/dyntopo.cc +++ b/source/blender/blenkernel/intern/dyntopo.cc @@ -2542,6 +2542,8 @@ void EdgeQueueContext::split_edge(BMEdge *e) int bf2 = BM_ELEM_CD_GET_INT(e->v2, pbvh->cd_boundary_flag); bool uv_boundary = BM_ELEM_CD_GET_INT(e, pbvh->cd_edge_boundary) & SCULPT_BOUNDARY_UV; + bool sharp_angle_boundary = BM_ELEM_CD_GET_INT(e, pbvh->cd_edge_boundary) & + SCULPT_BOUNDARY_SHARP_ANGLE; BMVert *newv = BM_edge_split(bm, e, e->v1, &newe, 0.5f); @@ -2563,6 +2565,16 @@ void EdgeQueueContext::split_edge(BMEdge *e) *BM_ELEM_CD_PTR(newv, pbvh->cd_boundary_flag) &= ~SCULPT_CORNER_UV; } + /* Do not allow sharp angle boundary flags to propagate across non-boundary edges. */ + if (!sharp_angle_boundary) { + *BM_ELEM_CD_PTR(newv, pbvh->cd_boundary_flag) &= ~(SCULPT_BOUNDARY_SHARP_ANGLE | + SCULPT_CORNER_SHARP_ANGLE); + } + else { + *BM_ELEM_CD_PTR(newv, pbvh->cd_boundary_flag) |= SCULPT_BOUNDARY_SHARP_ANGLE; + *BM_ELEM_CD_PTR(newv, pbvh->cd_boundary_flag) &= ~SCULPT_CORNER_SHARP_ANGLE; + } + /* Propagate current stroke id. */ StrokeID stroke_id; @@ -2592,6 +2604,7 @@ void EdgeQueueContext::split_edge(BMEdge *e) BM_log_edge_added(bm, pbvh->bm_log, newe); dyntopo_add_flag(pbvh, newv, SCULPTFLAG_NEED_VALENCE | SCULPTFLAG_NEED_TRIANGULATE); + pbvh_boundary_update_bmesh(pbvh, newv); pbvh_boundary_update_bmesh(pbvh, newe); diff --git a/source/blender/blenkernel/intern/pbvh_bmesh.cc b/source/blender/blenkernel/intern/pbvh_bmesh.cc index 08602cbc173..8fed3eabd9d 100644 --- a/source/blender/blenkernel/intern/pbvh_bmesh.cc +++ b/source/blender/blenkernel/intern/pbvh_bmesh.cc @@ -1439,7 +1439,8 @@ bool pbvh_bmesh_node_raycast(SculptSession *ss, madd_v3_v3v3fl(location, ray_start, ray_normal, *depth); for (int j = 0; j < 3; j++) { if (j == 0 || - len_squared_v3v3(location, cos[j]) < len_squared_v3v3(location, nearest_vertex_co)) { + len_squared_v3v3(location, cos[j]) < len_squared_v3v3(location, nearest_vertex_co)) + { copy_v3_v3(nearest_vertex_co, cos[j]); r_active_vertex->i = (intptr_t)verts[j]; } @@ -2100,7 +2101,8 @@ static void pbvh_bmesh_update_uv_boundary_intern(BMVert *v, float2 uv2b = *BM_ELEM_CD_PTR(l->v == v ? l->next : l, cd_uv); if (len_squared_v2v2(uv1a, uv2a) > snap_limit_sqr || - len_squared_v2v2(uv1b, uv2b) > snap_limit_sqr) { + len_squared_v2v2(uv1b, uv2b) > snap_limit_sqr) + { *boundflag |= SCULPT_BOUNDARY_UV; } @@ -2153,6 +2155,9 @@ void update_vert_boundary_bmesh(int cd_faceset_offset, int newflag = *BM_ELEM_CD_PTR(v, cd_flag); newflag &= ~(SCULPTFLAG_VERT_FSET_HIDDEN | SCULPTFLAG_PBVH_BOUNDARY); + bool update_sharp_angle = BM_ELEM_CD_GET_INT(v, cd_boundary_flag) & + SCULPT_BOUNDARY_UPDATE_SHARP_ANGLE; + if (BM_ELEM_CD_GET_INT(v, cd_boundary_flag) & SCULPT_BOUNDARY_UPDATE_UV) { pbvh_bmesh_update_uv_boundary(v, cd_boundary_flag, ldata); } @@ -2198,11 +2203,14 @@ void update_vert_boundary_bmesh(int cd_faceset_offset, newflag |= SCULPTFLAG_PBVH_BOUNDARY; } - if (e->l && e->l != e->l->radial_next) { - if (blender::bke::pbvh::test_sharp_faces_bmesh( - e->l->f, e->l->radial_next->f, sharp_angle_limit)) { - boundflag |= SCULPT_BOUNDARY_SHARP_ANGLE; - sharp_angle_num++; + if (update_sharp_angle) { + if (e->l && e->l != e->l->radial_next) { + if (blender::bke::pbvh::test_sharp_faces_bmesh( + e->l->f, e->l->radial_next->f, sharp_angle_limit)) + { + boundflag |= SCULPT_BOUNDARY_SHARP_ANGLE; + sharp_angle_num++; + } } } @@ -2222,7 +2230,8 @@ void update_vert_boundary_bmesh(int cd_faceset_offset, if (e->l) { if ((e->l->f->head.hflag & BM_ELEM_HIDDEN) || - (e->l->radial_next->f->head.hflag & BM_ELEM_HIDDEN)) { + (e->l->radial_next->f->head.hflag & BM_ELEM_HIDDEN)) + { newflag |= SCULPTFLAG_VERT_FSET_HIDDEN; } @@ -3968,7 +3977,8 @@ void update_sharp_vertex_bmesh(BMVert *v, int cd_boundary_flag, const float shar } if (blender::bke::pbvh::test_sharp_faces_bmesh( - e->l->f, e->l->radial_next->f, sharp_angle_limit)) { + e->l->f, e->l->radial_next->f, sharp_angle_limit)) + { flag |= SCULPT_BOUNDARY_SHARP_ANGLE; sharp_num++; } -- 2.30.2 From 24c5536e46dccb99407d9d5aecd8052ee89a4da6 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Fri, 30 Jun 2023 15:49:19 -0700 Subject: [PATCH 219/279] temp-sculpt-dyntopo: Fix compile error --- source/blender/draw/engines/overlay/overlay_edit_uv.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/source/blender/draw/engines/overlay/overlay_edit_uv.cc b/source/blender/draw/engines/overlay/overlay_edit_uv.cc index 7095e5a7a8e..06b8143793b 100644 --- a/source/blender/draw/engines/overlay/overlay_edit_uv.cc +++ b/source/blender/draw/engines/overlay/overlay_edit_uv.cc @@ -452,6 +452,8 @@ static CustomData *get_loop_customdata(Object *ob, Mesh *me) return &ob->sculpt->bm->ldata; } #endif + + return nullptr; } static void overlay_edit_uv_cache_populate(OVERLAY_Data *vedata, Object *ob) -- 2.30.2 From 7e78f221ffab3f8f87757ca2c28162338558f847 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Fri, 30 Jun 2023 16:00:05 -0700 Subject: [PATCH 220/279] temp-sculpt-dyntopo: Add missing small buffer optimization size --- source/blender/blenkernel/intern/dyntopo_collapse.cc | 2 +- source/blender/draw/engines/overlay/overlay_edit_uv.cc | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/source/blender/blenkernel/intern/dyntopo_collapse.cc b/source/blender/blenkernel/intern/dyntopo_collapse.cc index 1c96e334d37..bbf066ae593 100644 --- a/source/blender/blenkernel/intern/dyntopo_collapse.cc +++ b/source/blender/blenkernel/intern/dyntopo_collapse.cc @@ -108,7 +108,7 @@ static void snap_corner_data( Vector, 4> blocks; Vector, 4> weights; Vector, 4> loops; - Vector final_loops; + Vector final_loops; int cur_set = 0; for (int i : ls.index_range()) { diff --git a/source/blender/draw/engines/overlay/overlay_edit_uv.cc b/source/blender/draw/engines/overlay/overlay_edit_uv.cc index 06b8143793b..6599424d7a3 100644 --- a/source/blender/draw/engines/overlay/overlay_edit_uv.cc +++ b/source/blender/draw/engines/overlay/overlay_edit_uv.cc @@ -452,8 +452,8 @@ static CustomData *get_loop_customdata(Object *ob, Mesh *me) return &ob->sculpt->bm->ldata; } #endif - - return nullptr; + ; + //return nullptr; } static void overlay_edit_uv_cache_populate(OVERLAY_Data *vedata, Object *ob) -- 2.30.2 From 6be5fac6026aedc9672cdd5f09ad5ab8c4fdb29e Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Fri, 30 Jun 2023 16:01:05 -0700 Subject: [PATCH 221/279] temp-sculpt-dyntopo: remove test code --- source/blender/draw/engines/overlay/overlay_edit_uv.cc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/source/blender/draw/engines/overlay/overlay_edit_uv.cc b/source/blender/draw/engines/overlay/overlay_edit_uv.cc index 6599424d7a3..696a6c5401f 100644 --- a/source/blender/draw/engines/overlay/overlay_edit_uv.cc +++ b/source/blender/draw/engines/overlay/overlay_edit_uv.cc @@ -452,8 +452,7 @@ static CustomData *get_loop_customdata(Object *ob, Mesh *me) return &ob->sculpt->bm->ldata; } #endif - ; - //return nullptr; + return nullptr; } static void overlay_edit_uv_cache_populate(OVERLAY_Data *vedata, Object *ob) -- 2.30.2 From df388b45464d2545263ee01bda2433bd5b4bd8e1 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Mon, 3 Jul 2023 20:03:41 -0700 Subject: [PATCH 222/279] temp-sculpt-dyntopo: Pre-emptively apply commit to avoid merge conflicts --- source/blender/blenkernel/BKE_dyntopo.hh | 2 +- source/blender/blenkernel/BKE_paint.h | 12 +- source/blender/blenkernel/BKE_pbvh.h | 1116 +---------------- source/blender/blenkernel/BKE_pbvh_api.hh | 965 ++++++++++++++ source/blender/blenkernel/BKE_pbvh_iter.hh | 2 +- source/blender/blenkernel/CMakeLists.txt | 1 + .../blenkernel/intern/cdderivedmesh.cc | 2 +- source/blender/blenkernel/intern/dyntopo.cc | 2 +- .../blenkernel/intern/dyntopo_collapse.cc | 2 +- .../blenkernel/intern/dyntopo_intern.hh | 2 +- source/blender/blenkernel/intern/multires.cc | 2 +- source/blender/blenkernel/intern/object.cc | 2 +- source/blender/blenkernel/intern/paint.cc | 2 +- source/blender/blenkernel/intern/pbvh.cc | 2 +- .../blender/blenkernel/intern/pbvh_bmesh.cc | 70 +- .../blender/blenkernel/intern/pbvh_colors.cc | 4 +- .../blender/blenkernel/intern/pbvh_intern.hh | 2 +- .../blender/blenkernel/intern/pbvh_pixels.cc | 2 +- .../blenkernel/intern/pbvh_pixels_copy.cc | 2 +- .../blenkernel/intern/pbvh_pixels_copy.hh | 2 +- .../blender/blenkernel/intern/subsurf_ccg.cc | 2 +- .../draw/engines/overlay/overlay_sculpt.cc | 2 +- .../draw/engines/workbench/workbench_engine.c | 1 - .../engines/workbench/workbench_engine.cc | 2 +- .../draw/engines/workbench/workbench_state.cc | 2 +- source/blender/draw/intern/DRW_render.h | 1 - .../draw/intern/draw_cache_impl_mesh.cc | 2 +- source/blender/draw/intern/draw_manager.c | 1 - .../blender/draw/intern/draw_manager_data.cc | 2 +- source/blender/draw/intern/draw_pbvh.cc | 2 +- source/blender/draw/intern/draw_sculpt.cc | 2 +- source/blender/editors/mesh/editmesh_utils.cc | 2 +- .../editors/sculpt_paint/paint_hide.cc | 2 +- .../editors/sculpt_paint/paint_mask.cc | 2 +- source/blender/editors/sculpt_paint/sculpt.cc | 2 +- .../editors/sculpt_paint/sculpt_api.cc | 2 +- .../sculpt_paint/sculpt_automasking.cc | 2 +- .../editors/sculpt_paint/sculpt_boundary.cc | 2 +- .../sculpt_paint/sculpt_brush_types.cc | 2 +- .../editors/sculpt_paint/sculpt_cloth.cc | 2 +- .../editors/sculpt_paint/sculpt_curvature.cc | 2 +- .../editors/sculpt_paint/sculpt_detail.cc | 2 +- .../editors/sculpt_paint/sculpt_dyntopo.cc | 2 +- .../editors/sculpt_paint/sculpt_expand.cc | 2 +- .../editors/sculpt_paint/sculpt_face_set.cc | 2 +- .../sculpt_paint/sculpt_face_set_topology.c | 2 +- .../sculpt_paint/sculpt_filter_color.cc | 2 +- .../sculpt_paint/sculpt_filter_mask.cc | 2 +- .../sculpt_paint/sculpt_filter_mesh.cc | 2 +- .../editors/sculpt_paint/sculpt_geodesic.cc | 2 +- .../editors/sculpt_paint/sculpt_gradient.c | 2 +- .../editors/sculpt_paint/sculpt_intern.hh | 2 +- .../sculpt_paint/sculpt_mask_expand.cc | 2 +- .../editors/sculpt_paint/sculpt_mask_init.cc | 2 +- .../sculpt_paint/sculpt_multiplane_scrape.cc | 2 +- .../editors/sculpt_paint/sculpt_ops.cc | 2 +- .../sculpt_paint/sculpt_paint_color.cc | 2 +- .../sculpt_paint/sculpt_paint_image.cc | 2 +- .../editors/sculpt_paint/sculpt_poly_loop.c | 2 +- .../editors/sculpt_paint/sculpt_pose.cc | 2 +- .../editors/sculpt_paint/sculpt_replay.c | 4 +- .../editors/sculpt_paint/sculpt_smooth.cc | 2 +- .../editors/sculpt_paint/sculpt_transform.cc | 2 +- .../blender/editors/space_info/info_stats.cc | 2 +- .../gpu/intern/gpu_shader_builder_stubs.cc | 2 +- .../makesrna/intern/rna_sculpt_paint.c | 1 - source/blender/makesrna/intern/rna_userdef.c | 4 +- source/blender/windowmanager/intern/wm_draw.c | 1 - 68 files changed, 1157 insertions(+), 1134 deletions(-) create mode 100644 source/blender/blenkernel/BKE_pbvh_api.hh diff --git a/source/blender/blenkernel/BKE_dyntopo.hh b/source/blender/blenkernel/BKE_dyntopo.hh index 477e607d373..06d2f6ae5db 100644 --- a/source/blender/blenkernel/BKE_dyntopo.hh +++ b/source/blender/blenkernel/BKE_dyntopo.hh @@ -9,7 +9,7 @@ */ #include "BKE_paint.h" -#include "BKE_pbvh.h" +#include "BKE_pbvh_api.hh" #include "BLI_math.h" #include "BLI_math_vector_types.hh" diff --git a/source/blender/blenkernel/BKE_paint.h b/source/blender/blenkernel/BKE_paint.h index 82d6bed1867..014f9bdeee7 100644 --- a/source/blender/blenkernel/BKE_paint.h +++ b/source/blender/blenkernel/BKE_paint.h @@ -8,16 +8,16 @@ * \ingroup bke */ -#include "BKE_attribute.h" -#include "BKE_pbvh.h" - #include "BLI_bitmap.h" #include "BLI_compiler_compat.h" +#include "BLI_utildefines.h" #ifdef __cplusplus # include "BLI_array.hh" +# include "BLI_math_vector_types.hh" # include "BLI_offset_indices.hh" + +# include "DNA_brush_types.h" #endif -#include "BLI_utildefines.h" #include "DNA_brush_enums.h" #include "DNA_customdata_types.h" @@ -27,6 +27,7 @@ #include "BKE_attribute.h" #include "BKE_pbvh.h" +#include "bmesh.h" #include "bmesh_log.h" #ifdef __cplusplus @@ -650,9 +651,6 @@ typedef struct SculptAttributePointers { } SculptAttributePointers; #ifdef __cplusplus -# include "BLI_math_vector_types.hh" -# include "DNA_brush_types.h" - struct SculptSession { /* Mesh data (not copied) can come either directly from a Mesh, or from a MultiresDM */ struct { /* Special handling for multires meshes */ diff --git a/source/blender/blenkernel/BKE_pbvh.h b/source/blender/blenkernel/BKE_pbvh.h index fb4806ac6de..e19263c02b9 100644 --- a/source/blender/blenkernel/BKE_pbvh.h +++ b/source/blender/blenkernel/BKE_pbvh.h @@ -6,64 +6,16 @@ /** \file * \ingroup bke - * \brief A BVH for high poly meshes. + * \brief External data structures for PBVH. Does not + * include data structures internal to the draw code. */ -#ifdef __cplusplus -# include "BKE_dyntopo_set.hh" -#endif - -#include "BLI_bitmap.h" #include "BLI_compiler_compat.h" -#include "BLI_ghash.h" -#ifdef __cplusplus -# include "BLI_offset_indices.hh" -# include "BLI_vector.hh" -#endif +#include "BLI_utildefines.h" -#include "bmesh.h" -#include "bmesh_log.h" - -/* For embedding CCGKey in iterator. */ -#include "BKE_attribute.h" -#include "BKE_ccg.h" - -#include "BLI_smallhash.h" - -#include - -#include "DNA_customdata_types.h" - -#ifdef __cplusplus -extern "C" { -#endif - -struct BMesh; -struct BMVert; -struct BMEdge; -struct BMFace; -struct BMIdMap; -struct Scene; -struct CCGElem; -struct CCGKey; -struct CustomData; -struct DMFlagMat; -struct IsectRayPrecalc; -struct MLoopTri; -struct Mesh; -struct PBVH; -struct MEdge; -struct PBVHBatches; struct PBVHNode; -struct PBVH_GPU_Args; -struct SculptSession; -struct SubdivCCG; -struct TaskParallelSettings; -struct Image; -struct ImageUser; - -typedef struct PBVH PBVH; -typedef struct PBVHNode PBVHNode; +struct PBVHBatches; +struct BMesh; typedef enum { PBVH_FACES, @@ -71,130 +23,8 @@ typedef enum { PBVH_BMESH, } PBVHType; -/* Public members of PBVH, used for inlined functions. */ -struct PBVHPublic { - PBVHType type; - BMesh *bm; -}; - -/* - * These structs represent logical verts/edges/faces. - * for PBVH_GRIDS and PBVH_FACES they store integer - * offsets, PBVH_BMESH stores pointers. - * - * The idea is to enforce stronger type checking by encapsulating - * intptr_t's in structs. - */ - -/* A generic PBVH vertex. - * - * NOTE: in PBVH_GRIDS we consider the final grid points - * to be vertices. This is not true of edges or faces which are pulled from - * the base mesh. - */ - -#ifdef __cplusplus -/* A few C++ methods to play nice with sets and maps. */ -# define PBVH_REF_CXX_METHODS(Class) \ - bool operator==(const Class b) const \ - { \ - return i == b.i; \ - } \ - uint64_t hash() const \ - { \ - return i; \ - } -#else -# define PBVH_REF_CXX_METHODS(cls) -#endif - -typedef struct PBVHVertRef { - intptr_t i; - - PBVH_REF_CXX_METHODS(PBVHVertRef) -} PBVHVertRef; - -/* NOTE: edges in PBVH_GRIDS are always pulled from the base mesh. */ -typedef struct PBVHEdgeRef { - intptr_t i; - - PBVH_REF_CXX_METHODS(PBVHVertRef) -} PBVHEdgeRef; - -/* NOTE: faces in PBVH_GRIDS are always puled from the base mesh. */ -typedef struct PBVHFaceRef { - intptr_t i; - - PBVH_REF_CXX_METHODS(PBVHVertRef) -} PBVHFaceRef; - -#define PBVH_REF_NONE -1LL - -#ifdef __cplusplus -typedef struct PBVHTri { - int v[3]; // references into PBVHTriBuf->verts - int eflag; // bitmask of which edges in the tri are real edges in the mesh - intptr_t l[3]; // loops - - float no[3]; - PBVHFaceRef f; -} PBVHTri; - -typedef struct PBVHTriBuf { - blender::Vector tris; - blender::Vector verts; - blender::Vector edges; - blender::Vector loops; - - int mat_nr = 0; - -# ifdef __cplusplus - blender::Map vertmap; -# else - void *vertmap; -# endif - - float min[3], max[3]; -} PBVHTriBuf; -#else -struct PBVHTri; -struct PBVHTriBuf; -#endif - -typedef struct { - float (*co)[3]; -} PBVHProxyNode; - -typedef struct { - float (*color)[4]; - int size; -} PBVHColorBufferNode; - -typedef struct PBVHPixels { - /** - * Storage for texture painting on PBVH level. - * - * Contains #blender::bke::pbvh::pixels::PBVHData - */ - void *data; -} PBVHPixels; - -typedef struct PBVHPixelsNode { - /** - * Contains triangle/pixel data used during texture painting. - * - * Contains #blender::bke::pbvh::pixels::NodeData. - */ - void *node_data; -} PBVHPixelsNode; - -typedef struct PBVHAttrReq { - char name[MAX_CUSTOMDATA_LAYER_NAME]; - eAttrDomain domain; - eCustomDataType type; -} PBVHAttrReq; - -typedef enum { +/* PBVHNodeFlags is needed by DRW_render.h and draw_cache.c. */ +typedef enum PBVHNodeFlags { PBVH_Leaf = 1 << 0, PBVH_UpdateNormals = 1 << 1, @@ -219,290 +49,75 @@ typedef enum { PBVH_UpdateTris = 1 << 18, PBVH_RebuildNodeVerts = 1 << 19, - /* tri areas are not guaranteed to be up to date, tools should - update all nodes on first step of brush*/ + /* Tri areas are not guaranteed to be up to date, tools should + * update all nodes on first step of brush. + */ PBVH_UpdateTriAreas = 1 << 20, PBVH_UpdateOtherVerts = 1 << 21, PBVH_TexLeaf = 1 << 22, - PBVH_TopologyUpdated = 1 << 23, /* Used internally by pbvh_bmesh.c */ + PBVH_TopologyUpdated = 1 << 23, /* Used internally by dyntopo.c. */ } PBVHNodeFlags; ENUM_OPERATORS(PBVHNodeFlags, PBVH_TopologyUpdated); -typedef struct PBVHFrustumPlanes { - float (*planes)[4]; - int num_planes; -} PBVHFrustumPlanes; +/* A few C++ methods for PBVHxxxRef structs to play nice with sets and maps. */ +#ifdef __cplusplus +# define PBVH_REF_CXX_METHODS(Class) \ + bool operator==(const Class b) const \ + { \ + return i == b.i; \ + } \ + uint64_t hash() const \ + { \ + return i; \ + } +#else +# define PBVH_REF_CXX_METHODS(Class) +#endif + +typedef struct PBVHVertRef { + intptr_t i; + + PBVH_REF_CXX_METHODS(PBVHVertRef) +} PBVHVertRef; + +/* NOTE: edges in PBVH_GRIDS are always pulled from the base mesh. */ +typedef struct PBVHEdgeRef { + intptr_t i; + + PBVH_REF_CXX_METHODS(PBVHVertRef) +} PBVHEdgeRef; + +/* NOTE: faces in PBVH_GRIDS are always puled from the base mesh. */ +typedef struct PBVHFaceRef { + intptr_t i; + + PBVH_REF_CXX_METHODS(PBVHVertRef) +} PBVHFaceRef; + +#define PBVH_REF_NONE -1LL + +/* Public members of PBVH, used for inlined functions. */ +struct PBVHPublic { + PBVHType type; + struct BMesh *bm; +}; + +typedef struct PBVH PBVH; +typedef struct PBVHNode PBVHNode; BLI_INLINE PBVHType BKE_pbvh_type(const PBVH *pbvh) { return ((const struct PBVHPublic *)pbvh)->type; } -BLI_INLINE BMesh *BKE_pbvh_get_bmesh(PBVH *pbvh) -{ - return ((struct PBVHPublic *)pbvh)->bm; -} +#ifdef __cplusplus +extern "C" { +#endif -void BKE_pbvh_set_frustum_planes(PBVH *pbvh, PBVHFrustumPlanes *planes); -void BKE_pbvh_get_frustum_planes(PBVH *pbvh, PBVHFrustumPlanes *planes); - -BLI_INLINE PBVHVertRef BKE_pbvh_make_vref(intptr_t i) -{ - PBVHVertRef ret = {i}; - return ret; -} - -BLI_INLINE PBVHEdgeRef BKE_pbvh_make_eref(intptr_t i) -{ - PBVHEdgeRef ret = {i}; - return ret; -} - -BLI_INLINE PBVHFaceRef BKE_pbvh_make_fref(intptr_t i) -{ - PBVHFaceRef ret = {i}; - return ret; -} - -BLI_INLINE int BKE_pbvh_vertex_to_index(PBVH *pbvh, PBVHVertRef v) -{ - return (BKE_pbvh_type(pbvh) == PBVH_BMESH && v.i != PBVH_REF_NONE ? - BM_elem_index_get((BMVert *)(v.i)) : - (v.i)); -} - -BLI_INLINE PBVHVertRef BKE_pbvh_index_to_vertex(PBVH *pbvh, int index) -{ - switch (BKE_pbvh_type(pbvh)) { - case PBVH_FACES: - case PBVH_GRIDS: - return BKE_pbvh_make_vref(index); - case PBVH_BMESH: - return BKE_pbvh_make_vref((intptr_t)BKE_pbvh_get_bmesh(pbvh)->vtable[index]); - } - - return BKE_pbvh_make_vref(PBVH_REF_NONE); -} - -BLI_INLINE int BKE_pbvh_edge_to_index(PBVH *pbvh, PBVHEdgeRef e) -{ - return (BKE_pbvh_type(pbvh) == PBVH_BMESH && e.i != PBVH_REF_NONE ? - BM_elem_index_get((BMEdge *)(e.i)) : - (e.i)); -} - -BLI_INLINE PBVHEdgeRef BKE_pbvh_index_to_edge(PBVH *pbvh, int index) -{ - switch (BKE_pbvh_type(pbvh)) { - case PBVH_FACES: - case PBVH_GRIDS: - return BKE_pbvh_make_eref(index); - case PBVH_BMESH: - return BKE_pbvh_make_eref((intptr_t)BKE_pbvh_get_bmesh(pbvh)->etable[index]); - } - - return BKE_pbvh_make_eref(PBVH_REF_NONE); -} - -BLI_INLINE int BKE_pbvh_face_to_index(PBVH *pbvh, PBVHFaceRef f) -{ - return (BKE_pbvh_type(pbvh) == PBVH_BMESH && f.i != PBVH_REF_NONE ? - BM_elem_index_get((BMFace *)(f.i)) : - (f.i)); -} - -BLI_INLINE PBVHFaceRef BKE_pbvh_index_to_face(PBVH *pbvh, int index) -{ - switch (BKE_pbvh_type(pbvh)) { - case PBVH_FACES: - case PBVH_GRIDS: - return BKE_pbvh_make_fref(index); - case PBVH_BMESH: - return BKE_pbvh_make_fref((intptr_t)BKE_pbvh_get_bmesh(pbvh)->ftable[index]); - } - - return BKE_pbvh_make_fref(PBVH_REF_NONE); -} - -/* Callbacks */ - -/** - * Returns true if the search should continue from this node, false otherwise. - */ -typedef bool (*BKE_pbvh_SearchCallback)(PBVHNode *node, void *data); - -typedef void (*BKE_pbvh_HitCallback)(PBVHNode *node, void *data); -typedef void (*BKE_pbvh_HitOccludedCallback)(PBVHNode *node, void *data, float *tmin); - -typedef void (*BKE_pbvh_SearchNearestCallback)(PBVHNode *node, void *data, float *tmin); - -PBVHNode *BKE_pbvh_get_node(PBVH *pbvh, int node); - -/* Building */ - -PBVH *BKE_pbvh_new(PBVHType type); - -/** - * Do a full rebuild with on Mesh data structure. - */ - -void BKE_pbvh_build_mesh(PBVH *pbvh, struct Mesh *mesh); -void BKE_pbvh_update_mesh_pointers(PBVH *pbvh, struct Mesh *mesh); - -/** - * Do a full rebuild with on Grids data structure. - */ -void BKE_pbvh_build_grids(PBVH *pbvh, - struct CCGElem **grids, - int totgrid, - struct CCGKey *key, - void **gridfaces, - struct DMFlagMat *flagmats, - unsigned int **grid_hidden, - float *face_areas, - struct Mesh *me, - struct SubdivCCG *subdiv_ccg); -/** - * Build a PBVH from a BMesh. - */ -void BKE_pbvh_build_bmesh(PBVH *pbvh, - struct Mesh *me, - struct BMesh *bm, - BMLog *log, - struct BMIdMap *idmap, - const int cd_vert_node_offset, - const int cd_face_node_offset, - const int cd_face_areas, - const int cd_boundary_flag, - const int cd_edge_boundary, - const int cd_flag, - const int cd_valence, - const int cd_origco, - const int cd_origno); - -void BKE_pbvh_set_idmap(PBVH *pbvh, struct BMIdMap *idmap); - -void BKE_pbvh_update_offsets(PBVH *pbvh, - const int cd_vert_node_offset, - const int cd_face_node_offset, - const int cd_face_areas, - const int cd_boudnary_flags, - const int cd_edge_boundary, - const int cd_flag, - const int cd_valence, - const int cd_origco, - const int cd_origno, - const int cd_curvature_dir); - -void BKE_pbvh_update_bmesh_offsets(PBVH *pbvh, int cd_vert_node_offset, int cd_face_node_offset); - -void BKE_pbvh_build_pixels(PBVH *pbvh, - struct Mesh *mesh, - struct Image *image, - struct ImageUser *image_user); -void BKE_pbvh_free(PBVH *pbvh); - -void BKE_pbvh_set_bm_log(PBVH *pbvh, BMLog *log); -BMLog *BKE_pbvh_get_bm_log(PBVH *pbvh); - -/** -checks if original data needs to be updated for v, and if so updates it. Stroke_id -is provided by the sculpt code and is used to detect updates. The reason we do it -inside the verts and not in the nodes is to allow splitting of the pbvh during the stroke. -*/ -bool BKE_pbvh_bmesh_check_origdata(struct SculptSession *ss, struct BMVert *v, int stroke_id); - -/** used so pbvh can differentiate between different strokes, - see BKE_pbvh_bmesh_check_origdata */ -void BKE_pbvh_set_stroke_id(PBVH *pbvh, int stroke_id); - -/* Hierarchical Search in the BVH, two methods: - * - For each hit calling a callback. - * - Gather nodes in an array (easy to multi-thread) see blender::bke::pbvh::search_gather. - */ - -void BKE_pbvh_search_callback(PBVH *pbvh, - BKE_pbvh_SearchCallback scb, - void *search_data, - BKE_pbvh_HitCallback hcb, - void *hit_data); - -/* Ray-cast - * the hit callback is called for all leaf nodes intersecting the ray; - * it's up to the callback to find the primitive within the leaves that is - * hit first */ - -void BKE_pbvh_raycast(PBVH *pbvh, - BKE_pbvh_HitOccludedCallback cb, - void *data, - const float ray_start[3], - const float ray_normal[3], - bool original, - int stroke_id); - -bool BKE_pbvh_node_raycast(struct SculptSession *ss, - PBVH *pbvh, - PBVHNode *node, - float (*origco)[3], - bool use_origco, - const float ray_start[3], - const float ray_normal[3], - struct IsectRayPrecalc *isect_precalc, - int *hit_count, - float *depth, - float *back_depth, - PBVHVertRef *active_vertex_index, - PBVHFaceRef *active_face_grid_index, - float *face_normal, - int stroke_id); - -bool BKE_pbvh_bmesh_node_raycast_detail(PBVH *pbvh, - PBVHNode *node, - const float ray_start[3], - struct IsectRayPrecalc *isect_precalc, - float *depth, - float *r_edge_length); - -/** - * For orthographic cameras, project the far away ray segment points to the root node so - * we can have better precision. - */ -void BKE_pbvh_raycast_project_ray_root( - PBVH *pbvh, bool original, float ray_start[3], float ray_end[3], float ray_normal[3]); - -void BKE_pbvh_find_nearest_to_ray(PBVH *pbvh, - BKE_pbvh_HitOccludedCallback cb, - void *data, - const float ray_start[3], - const float ray_normal[3], - bool original); - -bool BKE_pbvh_node_find_nearest_to_ray(struct SculptSession *ss, - PBVH *pbvh, - PBVHNode *node, - float (*origco)[3], - bool use_origco, - const float ray_start[3], - const float ray_normal[3], - float *depth, - float *dist_sq, - int stroke_id); - -/* Drawing */ - -void BKE_pbvh_draw_cb(PBVH *pbvh, - struct Mesh *me, - bool update_only_visible, - PBVHFrustumPlanes *update_frustum, - PBVHFrustumPlanes *draw_frustum, - void (*draw_fn)(void *user_data, - struct PBVHBatches *batches, - struct PBVH_GPU_Args *args), - void *user_data, - bool full_render, - PBVHAttrReq *attrs, - int attrs_num); +/* Needed by eevee_materias.c. */ +void BKE_pbvh_is_drawing_set(PBVH *pbvh, bool val); +/* Needed by basic_engine.c. */ void BKE_pbvh_draw_debug_cb(PBVH *pbvh, void (*draw_fn)(PBVHNode *node, void *user_data, @@ -511,612 +126,7 @@ void BKE_pbvh_draw_debug_cb(PBVH *pbvh, PBVHNodeFlags flag), void *user_data); -/* PBVH Access */ - -bool BKE_pbvh_has_faces(const PBVH *pbvh); - -/** - * Get the PBVH root's bounding box. - */ -void BKE_pbvh_bounding_box(const PBVH *pbvh, float min[3], float max[3]); - -/** - * Multi-res hidden data, only valid for type == PBVH_GRIDS. - */ -unsigned int **BKE_pbvh_grid_hidden(const PBVH *pbvh); - -void BKE_pbvh_sync_visibility_from_verts(PBVH *pbvh, struct Mesh *me); - -/** - * Returns the number of visible quads in the nodes' grids. - */ -int BKE_pbvh_count_grid_quads(BLI_bitmap **grid_hidden, - const int *grid_indices, - int totgrid, - int gridsize, - int display_gridsize); - -/** - * Multi-res level, only valid for type == #PBVH_GRIDS. - */ -const struct CCGKey *BKE_pbvh_get_grid_key(const PBVH *pbvh); - -struct CCGElem **BKE_pbvh_get_grids(const PBVH *pbvh); -BLI_bitmap **BKE_pbvh_get_grid_visibility(const PBVH *pbvh); -int BKE_pbvh_get_grid_num_verts(const PBVH *pbvh); -int BKE_pbvh_get_grid_num_faces(const PBVH *pbvh); - -/* Node Access */ - -void BKE_pbvh_check_tri_areas(PBVH *pbvh, PBVHNode *node); -void BKE_pbvh_face_areas_begin(PBVH *pbvh); - -bool BKE_pbvh_bmesh_check_valence(PBVH *pbvh, PBVHVertRef vertex); -void BKE_pbvh_bmesh_update_valence(PBVH *pbvh, PBVHVertRef vertex); -void BKE_pbvh_bmesh_update_all_valence(PBVH *pbvh); -bool BKE_pbvh_bmesh_mark_update_valence(PBVH *pbvh, PBVHVertRef vertex); - -/* if pbvh uses a split index buffer, will call BKE_pbvh_vert_tag_update_normal_triangulation; - otherwise does nothing. returns true if BKE_pbvh_vert_tag_update_normal_triangulation was - called.*/ -void BKE_pbvh_vert_tag_update_normal_triangulation(PBVHNode *node); -void BKE_pbvh_node_mark_original_update(PBVHNode *node); -void BKE_pbvh_vert_tag_update_normal_tri_area(PBVHNode *node); -void BKE_pbvh_update_all_tri_areas(PBVH *pbvh); -void BKE_pbvh_node_mark_update(PBVHNode *node); -void BKE_pbvh_node_mark_update_mask(PBVHNode *node); -void BKE_pbvh_node_mark_update_color(PBVHNode *node); -void BKE_pbvh_vert_tag_update_normal_visibility(PBVHNode *node); -void BKE_pbvh_node_mark_update_face_sets(PBVHNode *node); -void BKE_pbvh_node_mark_update_visibility(PBVHNode *node); -void BKE_pbvh_node_mark_rebuild_draw(PBVHNode *node); -void BKE_pbvh_node_mark_redraw(PBVHNode *node); -void BKE_pbvh_node_mark_normals_update(PBVHNode *node); -void BKE_pbvh_node_mark_topology_update(PBVHNode *node); -void BKE_pbvh_node_fully_hidden_set(PBVHNode *node, int fully_hidden); -bool BKE_pbvh_node_fully_hidden_get(PBVHNode *node); -void BKE_pbvh_node_fully_masked_set(PBVHNode *node, int fully_masked); -bool BKE_pbvh_node_fully_masked_get(PBVHNode *node); -void BKE_pbvh_node_fully_unmasked_set(PBVHNode *node, int fully_masked); -bool BKE_pbvh_node_fully_unmasked_get(PBVHNode *node); -void BKE_pbvh_node_mark_curvature_update(PBVHNode *node); - -void BKE_pbvh_mark_rebuild_pixels(PBVH *pbvh); -void BKE_pbvh_vert_tag_update_normal(PBVH *pbvh, PBVHVertRef vertex); - -void BKE_pbvh_node_get_grids(PBVH *pbvh, - PBVHNode *node, - int **grid_indices, - int *totgrid, - int *maxgrid, - int *gridsize, - struct CCGElem ***r_griddata); -void BKE_pbvh_node_num_verts(PBVH *pbvh, PBVHNode *node, int *r_uniquevert, int *r_totvert); -const int *BKE_pbvh_node_get_vert_indices(PBVHNode *node); -void BKE_pbvh_node_get_loops(PBVH *pbvh, - PBVHNode *node, - const int **r_loop_indices, - const int **r_corner_verts); - -/* Get number of faces in the mesh; for PBVH_GRIDS the - * number of base mesh faces is returned. - */ -int BKE_pbvh_num_faces(const PBVH *pbvh); - -void BKE_pbvh_node_get_BB(PBVHNode *node, float bb_min[3], float bb_max[3]); -void BKE_pbvh_node_get_original_BB(PBVHNode *node, float bb_min[3], float bb_max[3]); - -float BKE_pbvh_node_get_tmin(PBVHNode *node); - -/** - * Test if AABB is at least partially inside the #PBVHFrustumPlanes volume. - */ -bool BKE_pbvh_node_frustum_contain_AABB(PBVHNode *node, void *frustum); -/** - * Test if AABB is at least partially outside the #PBVHFrustumPlanes volume. - */ -bool BKE_pbvh_node_frustum_exclude_AABB(PBVHNode *node, void *frustum); - #ifdef __cplusplus -blender::bke::dyntopo::DyntopoSet *BKE_pbvh_bmesh_node_unique_verts(PBVHNode *node); -blender::bke::dyntopo::DyntopoSet *BKE_pbvh_bmesh_node_other_verts(PBVHNode *node); -blender::bke::dyntopo::DyntopoSet *BKE_pbvh_bmesh_node_faces(PBVHNode *node); -#endif - -void BKE_pbvh_bmesh_regen_node_verts(PBVH *pbvh, bool report); -void BKE_pbvh_bmesh_mark_node_regen(PBVH *pbvh, PBVHNode *node); - -/* Update Bounding Box/Redraw and clear flags. */ - -void BKE_pbvh_update_bounds(PBVH *pbvh, int flags); -void BKE_pbvh_update_vertex_data(PBVH *pbvh, int flags); -void BKE_pbvh_update_visibility(PBVH *pbvh); -void BKE_pbvh_update_normals(PBVH *pbvh, struct SubdivCCG *subdiv_ccg); -void BKE_pbvh_redraw_BB(PBVH *pbvh, float bb_min[3], float bb_max[3]); -void BKE_pbvh_get_grid_updates(PBVH *pbvh, bool clear, void ***r_gridfaces, int *r_totface); -void BKE_pbvh_grids_update(PBVH *pbvh, - struct CCGElem **grids, - void **gridfaces, - struct DMFlagMat *flagmats, - unsigned int **grid_hidden, - struct CCGKey *key); -void BKE_pbvh_subdiv_ccg_set(PBVH *pbvh, struct SubdivCCG *subdiv_ccg); -void BKE_pbvh_face_sets_set(PBVH *pbvh, int *face_sets); - -/** - * If an operation causes the hide status stored in the mesh to change, this must be called - * to update the references to those attributes, since they are only added when necessary. - */ -void BKE_pbvh_update_hide_attributes_from_mesh(PBVH *pbvh); - -void BKE_pbvh_face_sets_color_set(PBVH *pbvh, int seed, int color_default); - -/* Vertex Deformer. */ - -float (*BKE_pbvh_vert_coords_alloc(PBVH *pbvh))[3]; -void BKE_pbvh_vert_coords_apply(PBVH *pbvh, const float (*vertCos)[3], int totvert); -bool BKE_pbvh_is_deformed(PBVH *pbvh); - -/* Vertex Iterator. */ - -/* This iterator has quite a lot of code, but it's designed to: - * - allow the compiler to eliminate dead code and variables - * - spend most of the time in the relatively simple inner loop */ - -/* NOTE: PBVH_ITER_ALL does not skip hidden vertices, - * PBVH_ITER_UNIQUE does */ -#define PBVH_ITER_ALL 0 -#define PBVH_ITER_UNIQUE 1 - -#ifdef __cplusplus -typedef struct PBVHVertexIter { - /* iteration */ - int g; - int width; - int height; - int gx; - int gy; - int i; - int index; - PBVHVertRef vertex; - - /* grid */ - struct CCGKey key; - struct CCGElem **grids; - struct CCGElem *grid; - BLI_bitmap **grid_hidden, *gh; - int *grid_indices; - int totgrid; - int gridsize; - - /* mesh */ - float (*vert_positions)[3]; - float (*vert_normals)[3]; - const bool *hide_vert; - int totvert; - const int *vert_indices; - float *vmask; - bool is_mesh; - - /* bmesh */ - int bi; - int bm_cur_set; - blender::bke::dyntopo::DyntopoSet *bm_unique_verts, *bm_other_verts; - blender::bke::dyntopo::DyntopoSet::iterator bm_iter, bm_iter_end; - - struct CustomData *bm_vdata; - int cd_vert_mask_offset; - int cd_vcol_offset; - - /* result: these are all computed in the macro, but we assume - * that compiler optimization's will skip the ones we don't use */ - struct BMVert *bm_vert; - float *co; - float *no; - float *fno; - float *mask; - bool visible; -} PBVHVertexIter; -#else -struct PBVHVertexIter; -#endif - -void pbvh_vertex_iter_init(PBVH *pbvh, PBVHNode *node, struct PBVHVertexIter *vi, int mode); - -#define BKE_pbvh_vertex_iter_begin(pbvh, node, vi, mode) \ - pbvh_vertex_iter_init(pbvh, node, &vi, mode); \ -\ - for (vi.i = 0, vi.g = 0; vi.g < vi.totgrid; vi.g++) { \ - if (vi.grids) { \ - vi.width = vi.gridsize; \ - vi.height = vi.gridsize; \ - vi.index = vi.vertex.i = vi.grid_indices[vi.g] * vi.key.grid_area - 1; \ - vi.grid = vi.grids[vi.grid_indices[vi.g]]; \ - if (mode == PBVH_ITER_UNIQUE) { \ - vi.gh = vi.grid_hidden[vi.grid_indices[vi.g]]; \ - } \ - } \ - else { \ - vi.width = vi.totvert; \ - vi.height = 1; \ - } \ -\ - for (vi.gy = 0; vi.gy < vi.height; vi.gy++) { \ - for (vi.gx = 0; vi.gx < vi.width; vi.gx++, vi.i++) { \ - if (vi.grid) { \ - vi.co = CCG_elem_co(&vi.key, vi.grid); \ - vi.fno = CCG_elem_no(&vi.key, vi.grid); \ - vi.mask = vi.key.has_mask ? CCG_elem_mask(&vi.key, vi.grid) : NULL; \ - vi.grid = CCG_elem_next(&vi.key, vi.grid); \ - vi.index++; \ - vi.vertex.i++; \ - vi.visible = true; \ - if (vi.gh) { \ - if (BLI_BITMAP_TEST(vi.gh, vi.gy * vi.gridsize + vi.gx)) { \ - continue; \ - } \ - } \ - } \ - else if (vi.vert_positions) { \ - vi.visible = !(vi.hide_vert && vi.hide_vert[vi.vert_indices[vi.gx]]); \ - if (mode == PBVH_ITER_UNIQUE && !vi.visible) { \ - continue; \ - } \ - vi.co = vi.vert_positions[vi.vert_indices[vi.gx]]; \ - vi.no = vi.vert_normals[vi.vert_indices[vi.gx]]; \ - vi.index = vi.vertex.i = vi.vert_indices[vi.i]; \ - if (vi.vmask) { \ - vi.mask = &vi.vmask[vi.index]; \ - } \ - } \ - else { \ - if (vi.bm_iter == vi.bm_iter_end) { \ - if (vi.bm_cur_set == 0 && mode == PBVH_ITER_ALL) { \ - vi.bm_cur_set = 1; \ - vi.bm_iter = vi.bm_other_verts->begin(); \ - vi.bm_iter_end = vi.bm_other_verts->end(); \ - if (vi.bm_iter == vi.bm_iter_end) { \ - continue; \ - } \ - } \ - else { \ - continue; \ - } \ - } \ - BMVert *bv = *vi.bm_iter; \ - ++vi.bm_iter; \ - vi.bm_vert = bv; \ - vi.vertex.i = (intptr_t)bv; \ - vi.index = BM_elem_index_get(vi.bm_vert); \ - vi.visible = !BM_elem_flag_test_bool(vi.bm_vert, BM_ELEM_HIDDEN); \ - if (mode == PBVH_ITER_UNIQUE && !vi.visible) { \ - continue; \ - } \ - vi.co = vi.bm_vert->co; \ - vi.fno = vi.bm_vert->no; \ - vi.mask = vi.cd_vert_mask_offset != -1 ? \ - (float *)BM_ELEM_CD_GET_VOID_P(vi.bm_vert, vi.cd_vert_mask_offset) : \ - nullptr; \ - } - -#define BKE_pbvh_vertex_iter_end \ - } \ - } \ - } \ - ((void)0) - -#define PBVH_FACE_ITER_VERTS_RESERVED 8 - -#ifdef __cplusplus -struct PBVHFaceIter { - PBVHFaceRef face; - int index; - bool *hide; - int *face_set; - int i; - - PBVHVertRef *verts; - int verts_num; - - PBVHVertRef verts_reserved_[PBVH_FACE_ITER_VERTS_RESERVED]; - const PBVHNode *node_; - PBVHType pbvh_type_; - int verts_size_; - blender::bke::dyntopo::DyntopoSet::iterator bm_iter_, bm_iter_end_; - int cd_face_set_; - bool *hide_poly_; - int *face_sets_; - const int *poly_offsets_; - const int *looptri_polys_; - const int *corner_verts_; - int prim_index_; - const struct SubdivCCG *subdiv_ccg_; - const struct BMesh *bm; - CCGKey subdiv_key_; - - int last_poly_index_; -}; - -void BKE_pbvh_face_iter_init(PBVH *pbvh, PBVHNode *node, PBVHFaceIter *fd); -void BKE_pbvh_face_iter_step(PBVHFaceIter *fd); -bool BKE_pbvh_face_iter_done(PBVHFaceIter *fd); -void BKE_pbvh_face_iter_finish(PBVHFaceIter *fd); - -#endif - -/** - * Iterate over faces inside a #PBVHNode. These are either base mesh faces - * (for PBVH_FACES and PBVH_GRIDS) or BMesh faces (for PBVH_BMESH). - */ -#define BKE_pbvh_face_iter_begin(pbvh, node, fd) \ - BKE_pbvh_face_iter_init(pbvh, node, &fd); \ - for (; !BKE_pbvh_face_iter_done(&fd); BKE_pbvh_face_iter_step(&fd)) { - -#define BKE_pbvh_face_iter_end(fd) \ - } \ - BKE_pbvh_face_iter_finish(&fd) - -void BKE_pbvh_node_get_proxies(PBVHNode *node, PBVHProxyNode **proxies, int *proxy_count); -void BKE_pbvh_node_free_proxies(PBVHNode *node); -PBVHProxyNode *BKE_pbvh_node_add_proxy(PBVH *pbvh, PBVHNode *node); - -/** - * \note doing a full search on all vertices here seems expensive, - * however this is important to avoid having to recalculate bound-box & sync the buffers to the - * GPU (which is far more expensive!) See: #47232. - */ -bool BKE_pbvh_node_has_vert_with_normal_update_tag(PBVH *pbvh, PBVHNode *node); - -// void BKE_pbvh_node_BB_reset(PBVHNode *node); -// void BKE_pbvh_node_BB_expand(PBVHNode *node, float co[3]); - -bool pbvh_has_mask(const PBVH *pbvh); - -bool pbvh_has_face_sets(PBVH *pbvh); - -void pbvh_show_mask_set(PBVH *pbvh, bool show_mask); -void pbvh_show_face_sets_set(PBVH *pbvh, bool show_face_sets); - -/* Parallelization. */ - -void BKE_pbvh_parallel_range_settings(struct TaskParallelSettings *settings, - bool use_threading, - int totnode); - -float (*BKE_pbvh_get_vert_positions(const PBVH *pbvh))[3]; -const float (*BKE_pbvh_get_vert_normals(const PBVH *pbvh))[3]; -const bool *BKE_pbvh_get_vert_hide(const PBVH *pbvh); -bool *BKE_pbvh_get_vert_hide_for_write(PBVH *pbvh); - -const bool *BKE_pbvh_get_poly_hide(const PBVH *pbvh); - -/* Get active color attribute; if pbvh is non-null - * and is of type PBVH_BMESH the layer inside of - * pbvh->header.bm will be returned, otherwise the - * layer will be looked up inside of me. - */ -bool BKE_pbvh_get_color_layer(const PBVH *pbvh, - const struct Mesh *me, - CustomDataLayer **r_layer, - eAttrDomain *r_attr); - -/* Swaps colors at each element in indices (of domain pbvh->vcol_domain) - * with values in colors. PBVH_FACES only.*/ -void BKE_pbvh_swap_colors(PBVH *pbvh, - const int *indices, - const int indices_num, - float (*colors)[4]); - -/* Stores colors from the elements in indices (of domain pbvh->vcol_domain) - * into colors. PBVH_FACES only.*/ -void BKE_pbvh_store_colors(PBVH *pbvh, - const int *indices, - const int indices_num, - float (*colors)[4]); - -/* Like BKE_pbvh_store_colors but handles loop->vert conversion. PBVH_FACES only. */ -void BKE_pbvh_store_colors_vertex(PBVH *pbvh, - const int *indices, - const int indices_num, - float (*colors)[4]); - -bool BKE_pbvh_is_drawing(const PBVH *pbvh); -void BKE_pbvh_is_drawing_set(PBVH *pbvh, bool val); - -/* Do not call in PBVH_GRIDS mode */ -void BKE_pbvh_node_num_loops(PBVH *pbvh, PBVHNode *node, int *r_totloop); - -void BKE_pbvh_update_active_vcol(PBVH *pbvh, const struct Mesh *mesh); - -void BKE_pbvh_vertex_color_set(PBVH *pbvh, PBVHVertRef vertex, const float color[4]); -void BKE_pbvh_vertex_color_get(const PBVH *pbvh, PBVHVertRef vertex, float r_color[4]); - -void BKE_pbvh_ensure_node_loops(PBVH *pbvh); -bool BKE_pbvh_draw_cache_invalid(const PBVH *pbvh); -int BKE_pbvh_debug_draw_gen_get(PBVHNode *node); - -int BKE_pbvh_get_node_index(PBVH *pbvh, PBVHNode *node); -int BKE_pbvh_get_node_id(PBVH *pbvh, PBVHNode *node); - -void BKE_pbvh_curvature_update_set(PBVHNode *node, bool state); -bool BKE_pbvh_curvature_update_get(PBVHNode *node); - -int BKE_pbvh_get_totnodes(PBVH *pbvh); - -bool BKE_pbvh_bmesh_check_tris(PBVH *pbvh, PBVHNode *node); -struct PBVHTriBuf *BKE_pbvh_bmesh_get_tris(PBVH *pbvh, PBVHNode *node); -void BKE_pbvh_bmesh_free_tris(PBVH *pbvh, PBVHNode *node); - -/*recalculates boundary flags for *all* vertices. used by - symmetrize.*/ -void BKE_pbvh_recalc_bmesh_boundary(PBVH *pbvh); -void BKE_pbvh_set_boundary_flags(PBVH *pbvh, int *boundary_flags); - -/* saves all bmesh references to internal indices, to be restored later */ -void BKE_pbvh_bmesh_save_indices(PBVH *pbvh); - -/* restore bmesh references from previously indices saved by BKE_pbvh_bmesh_save_indices */ -void BKE_pbvh_bmesh_from_saved_indices(PBVH *pbvh); - -/* wraps calls to BM_mesh_toolflags_set in BKE_pbvh_bmesh_save_indices and - * BKE_pbvh_bmesh_from_saved_indices */ -void BKE_pbvh_bmesh_set_toolflags(PBVH *pbvh, bool use_toolflags); - -void BKE_pbvh_bmesh_remove_face(PBVH *pbvh, BMFace *f, bool log_face); -void BKE_pbvh_bmesh_remove_edge(PBVH *pbvh, BMEdge *e, bool log_edge); -void BKE_pbvh_bmesh_remove_vertex(PBVH *pbvh, BMVert *v, bool log_vert); -void BKE_pbvh_bmesh_add_face(PBVH *pbvh, struct BMFace *f, bool log_face, bool force_tree_walk); - -/* e_tri and f_example are allowed to be nullptr. */ -struct BMFace *BKE_pbvh_face_create_bmesh(PBVH *pbvh, - struct BMVert *v_tri[3], - struct BMEdge *e_tri[3], - const struct BMFace *f_example); - -/* If node is nullptr then one will be found in the pbvh. */ -struct BMVert *BKE_pbvh_vert_create_bmesh( - PBVH *pbvh, float co[3], float no[3], PBVHNode *node, struct BMVert *v_example); -PBVHNode *BKE_pbvh_node_from_face_bmesh(PBVH *pbvh, struct BMFace *f); -PBVHNode *BKE_pbvh_node_from_index(PBVH *pbvh, int node_i); - -PBVHNode *BKE_pbvh_get_node_leaf_safe(PBVH *pbvh, int i); - -void BKE_pbvh_get_vert_face_areas(PBVH *pbvh, PBVHVertRef vertex, float *r_areas, int valence); -void BKE_pbvh_set_symmetry(PBVH *pbvh, int symmetry, int boundary_symmetry); - -int BKE_pbvh_do_fset_symmetry(int fset, const int symflag, const float *co); - -/* -Uses pmap to build an array of edge indices surrounding vertex -r_edges, r_edges_size, heap_alloc define an existing array to put data in. - -final array is similarly put in these pointers. note that calling code -may pass a stack allocated array (*heap_alloc should be false), and must -check if heap_alloc is true afterwards and free *r_edges. - -r_polys is an array of integer pairs and must be same logical size as r_edges -*/ -void BKE_pbvh_pmap_to_edges(PBVH *pbvh, - PBVHVertRef vertex, - int **r_edges, - int *r_edges_size, - bool *heap_alloc, - int **r_polys); - -void BKE_pbvh_reproject_smooth_set(PBVH *pbvh, bool value); - -void BKE_pbvh_set_face_areas(PBVH *pbvh, float *face_areas); -void BKE_pbvh_set_bmesh(PBVH *pbvh, struct BMesh *bm); -void BKE_pbvh_free_bmesh(PBVH *pbvh, struct BMesh *bm); - -void BKE_pbvh_show_orig_set(PBVH *pbvh, bool show_orig); -bool BKE_pbvh_show_orig_get(PBVH *pbvh); - -void BKE_pbvh_flush_tri_areas(PBVH *pbvh); -void BKE_pbvh_bmesh_check_nodes(PBVH *pbvh); - -#ifdef __cplusplus -void BKE_pbvh_pmap_set(PBVH *pbvh, blender::GroupedSpan pmap); } +#endif /* extern "C" */ -# include "BLI_math_vector.hh" - -namespace blender::bke::pbvh { -void set_flags_valence(PBVH *pbvh, uint8_t *flags, int *valence); -void set_original(PBVH *pbvh, Span origco, Span origno); -void update_vert_boundary_bmesh(int cd_faceset_offset, - int cd_vert_node_offset, - int cd_face_node_offset, - int cd_vcol, - int cd_boundary_flag, - const int cd_flag, - const int cd_valence, - struct BMVert *v, - const CustomData *ldata, - float sharp_angle_limit); -void update_sharp_vertex_bmesh(BMVert *v, int cd_boundary_flag, const float sharp_angle_limit); - -void update_vert_boundary_faces(int *boundary_flags, - const int *face_sets, - const bool *hide_poly, - const float (*vert_positions)[3], - const blender::int2 *medge, - const int *corner_verts, - const int *corner_edges, - blender::OffsetIndices polys, - const blender::GroupedSpan &pmap, - PBVHVertRef vertex, - const bool *sharp_edges, - const bool *seam_edges, - uint8_t *flags, - int *valence); -void update_edge_boundary_bmesh(BMEdge *e, - int cd_faceset_offset, - int cd_edge_boundary, - const int cd_flag, - const int cd_valence, - const CustomData *ldata, - float sharp_angle_limit); -void update_edge_boundary_faces(int edge, - Span vertex_positions, - Span vertex_normals, - Span edges, - OffsetIndices polys, - Span poly_normals, - int *edge_boundary_flags, - const int *vert_boundary_flags, - const int *face_sets, - const bool *sharp_edge, - const bool *seam_edge, - const GroupedSpan &pmap, - const GroupedSpan &epmap, - const CustomData *ldata, - float sharp_angle_limit, - blender::Span corner_verts, - blender::Span corner_edges); -void update_edge_boundary_grids(int edge, - Span edges, - OffsetIndices polys, - int *edge_boundary_flags, - const int *vert_boundary_flags, - const int *face_sets, - const bool *sharp_edge, - const bool *seam_edge, - const GroupedSpan &pmap, - const GroupedSpan &epmap, - const CustomData *ldata, - SubdivCCG *subdiv_ccg, - const CCGKey *key, - float sharp_angle_limit, - blender::Span corner_verts, - blender::Span corner_edges); -void update_vert_boundary_grids(PBVH *pbvh, int vertex); - -bool check_vert_boundary(PBVH *pbvh, PBVHVertRef vertex); -bool check_edge_boundary(PBVH *pbvh, PBVHEdgeRef edge); - -Vector search_gather(PBVH *pbvh, - BKE_pbvh_SearchCallback scb, - void *search_data, - PBVHNodeFlags leaf_flag = PBVH_Leaf); -Vector gather_proxies(PBVH *pbvh); -Vector get_flagged_nodes(PBVH *pbvh, int flag); -void set_pmap(PBVH *pbvh, blender::GroupedSpan pmap); -void set_vemap(PBVH *pbvh, blender::GroupedSpan vemap); -struct GroupedSpan get_pmap(PBVH *pbvh); - -void sharp_limit_set(PBVH *pbvh, float limit); -float test_sharp_faces_bmesh(BMFace *f1, BMFace *f2, float limit); -float test_sharp_faces_mesh(int f1, - int f2, - float limit, - blender::Span positions, - blender::OffsetIndices &polys, - blender::Span poly_normals, - blender::Span corner_verts); - -blender::Span get_poly_normals(const PBVH *pbvh); -void set_vert_boundary_map(PBVH *pbvh, BLI_bitmap *vert_boundary_map); -void on_stroke_start(PBVH *pbvh); -} // namespace blender::bke::pbvh -#endif diff --git a/source/blender/blenkernel/BKE_pbvh_api.hh b/source/blender/blenkernel/BKE_pbvh_api.hh new file mode 100644 index 00000000000..4a0001040ac --- /dev/null +++ b/source/blender/blenkernel/BKE_pbvh_api.hh @@ -0,0 +1,965 @@ +/* SPDX-FileCopyrightText: 2023 Blender Foundation + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +/** \file + * \ingroup bke + * \brief A BVH for high poly meshes. + */ + +#include "BKE_dyntopo_set.hh" + +#include "BLI_bitmap.h" +#include "BLI_compiler_compat.h" +#include "BLI_ghash.h" +#include "BLI_offset_indices.hh" +#include "BLI_vector.hh" + +#include "DNA_customdata_types.h" + +/* For embedding CCGKey in iterator. */ +#include "BKE_attribute.h" +#include "BKE_ccg.h" +#include "BKE_pbvh.h" + +#include "bmesh.h" +#include "bmesh_log.h" + +#include + +struct BMesh; +struct BMVert; +struct BMEdge; +struct BMFace; +struct BMIdMap; +struct Scene; +struct CCGElem; +struct CCGKey; +struct CustomData; +struct DMFlagMat; +struct IsectRayPrecalc; +struct MLoopTri; +struct Mesh; +struct PBVH; +struct MEdge; +struct PBVHBatches; +struct PBVHNode; +struct PBVH_GPU_Args; +struct SculptSession; +struct SubdivCCG; +struct TaskParallelSettings; +struct Image; +struct ImageUser; + +struct PBVHTri { + int v[3]; // references into PBVHTriBuf->verts + int eflag; // bitmask of which edges in the tri are real edges in the mesh + intptr_t l[3]; // loops + PBVHFaceRef f; + float no[3]; +}; + +struct PBVHTriBuf { + blender::Vector tris; + blender::Vector verts; + blender::Vector edges; + blender::Vector loops; + + int mat_nr = 0; + + float min[3], max[3]; +}; + +struct PBVHProxyNode { + float (*co)[3]; +}; + +struct PBVHColorBufferNode { + float (*color)[4]; + int size; +}; + +struct PBVHPixels { + /** + * Storage for texture painting on PBVH level. + * + * Contains #blender::bke::pbvh::pixels::PBVHData + */ + void *data; +}; + +struct PBVHPixelsNode { + /** + * Contains triangle/pixel data used during texture painting. + * + * Contains #blender::bke::pbvh::pixels::NodeData. + */ + void *node_data; +}; + +struct PBVHAttrReq { + char name[MAX_CUSTOMDATA_LAYER_NAME]; + eAttrDomain domain; + eCustomDataType type; +}; + +struct PBVHFrustumPlanes { + float (*planes)[4]; + int num_planes; +}; + +BLI_INLINE BMesh *BKE_pbvh_get_bmesh(PBVH *pbvh) +{ + return ((struct PBVHPublic *)pbvh)->bm; +} + +BLI_INLINE PBVHVertRef BKE_pbvh_make_vref(intptr_t i) +{ + PBVHVertRef ret = {i}; + return ret; +} + +BLI_INLINE PBVHEdgeRef BKE_pbvh_make_eref(intptr_t i) +{ + PBVHEdgeRef ret = {i}; + return ret; +} + +BLI_INLINE PBVHFaceRef BKE_pbvh_make_fref(intptr_t i) +{ + PBVHFaceRef ret = {i}; + return ret; +} + +BLI_INLINE int BKE_pbvh_vertex_to_index(PBVH *pbvh, PBVHVertRef v) +{ + return (BKE_pbvh_type(pbvh) == PBVH_BMESH && v.i != PBVH_REF_NONE ? + BM_elem_index_get((BMVert *)(v.i)) : + (v.i)); +} + +BLI_INLINE PBVHVertRef BKE_pbvh_index_to_vertex(PBVH *pbvh, int index) +{ + switch (BKE_pbvh_type(pbvh)) { + case PBVH_FACES: + case PBVH_GRIDS: + return BKE_pbvh_make_vref(index); + case PBVH_BMESH: + return BKE_pbvh_make_vref((intptr_t)BKE_pbvh_get_bmesh(pbvh)->vtable[index]); + } + + return BKE_pbvh_make_vref(PBVH_REF_NONE); +} + +BLI_INLINE int BKE_pbvh_edge_to_index(PBVH *pbvh, PBVHEdgeRef e) +{ + return (BKE_pbvh_type(pbvh) == PBVH_BMESH && e.i != PBVH_REF_NONE ? + BM_elem_index_get((BMEdge *)(e.i)) : + (e.i)); +} + +BLI_INLINE PBVHEdgeRef BKE_pbvh_index_to_edge(PBVH *pbvh, int index) +{ + switch (BKE_pbvh_type(pbvh)) { + case PBVH_FACES: + case PBVH_GRIDS: + return BKE_pbvh_make_eref(index); + case PBVH_BMESH: + return BKE_pbvh_make_eref((intptr_t)BKE_pbvh_get_bmesh(pbvh)->etable[index]); + } + + return BKE_pbvh_make_eref(PBVH_REF_NONE); +} + +BLI_INLINE int BKE_pbvh_face_to_index(PBVH *pbvh, PBVHFaceRef f) +{ + return (BKE_pbvh_type(pbvh) == PBVH_BMESH && f.i != PBVH_REF_NONE ? + BM_elem_index_get((BMFace *)(f.i)) : + (f.i)); +} + +BLI_INLINE PBVHFaceRef BKE_pbvh_index_to_face(PBVH *pbvh, int index) +{ + switch (BKE_pbvh_type(pbvh)) { + case PBVH_FACES: + case PBVH_GRIDS: + return BKE_pbvh_make_fref(index); + case PBVH_BMESH: + return BKE_pbvh_make_fref((intptr_t)BKE_pbvh_get_bmesh(pbvh)->ftable[index]); + } + + return BKE_pbvh_make_fref(PBVH_REF_NONE); +} + +/* Callbacks */ + +/** + * Returns true if the search should continue from this node, false otherwise. + */ +typedef bool (*BKE_pbvh_SearchCallback)(PBVHNode *node, void *data); + +typedef void (*BKE_pbvh_HitCallback)(PBVHNode *node, void *data); +typedef void (*BKE_pbvh_HitOccludedCallback)(PBVHNode *node, void *data, float *tmin); + +typedef void (*BKE_pbvh_SearchNearestCallback)(PBVHNode *node, void *data, float *tmin); + +PBVHNode *BKE_pbvh_get_node(PBVH *pbvh, int node); + +/* Building */ + +PBVH *BKE_pbvh_new(PBVHType type); + +/** + * Do a full rebuild with on Mesh data structure. + */ + +void BKE_pbvh_build_mesh(PBVH *pbvh, Mesh *mesh); +void BKE_pbvh_update_mesh_pointers(PBVH *pbvh, Mesh *mesh); + +/** + * Do a full rebuild with on Grids data structure. + */ +void BKE_pbvh_build_grids(PBVH *pbvh, + CCGElem **grids, + int totgrid, + CCGKey *key, + void **gridfaces, + DMFlagMat *flagmats, + unsigned int **grid_hidden, + float *face_areas, + Mesh *me, + SubdivCCG *subdiv_ccg); +/** + * Build a PBVH from a BMesh. + */ +void BKE_pbvh_build_bmesh(PBVH *pbvh, + Mesh *me, + BMesh *bm, + BMLog *log, + BMIdMap *idmap, + const int cd_vert_node_offset, + const int cd_face_node_offset, + const int cd_face_areas, + const int cd_boundary_flag, + const int cd_edge_boundary, + const int cd_flag, + const int cd_valence, + const int cd_origco, + const int cd_origno); + +void BKE_pbvh_set_idmap(PBVH *pbvh, BMIdMap *idmap); + +void BKE_pbvh_update_offsets(PBVH *pbvh, + const int cd_vert_node_offset, + const int cd_face_node_offset, + const int cd_face_areas, + const int cd_boudnary_flags, + const int cd_edge_boundary, + const int cd_flag, + const int cd_valence, + const int cd_origco, + const int cd_origno, + const int cd_curvature_dir); + +void BKE_pbvh_update_bmesh_offsets(PBVH *pbvh, int cd_vert_node_offset, int cd_face_node_offset); + +void BKE_pbvh_build_pixels(PBVH *pbvh, Mesh *mesh, Image *image, ImageUser *image_user); +void BKE_pbvh_free(PBVH *pbvh); + +void BKE_pbvh_set_bm_log(PBVH *pbvh, BMLog *log); +BMLog *BKE_pbvh_get_bm_log(PBVH *pbvh); + +/** +checks if original data needs to be updated for v, and if so updates it. Stroke_id +is provided by the sculpt code and is used to detect updates. The reason we do it +inside the verts and not in the nodes is to allow splitting of the pbvh during the stroke. +*/ +bool BKE_pbvh_bmesh_check_origdata(SculptSession *ss, BMVert *v, int stroke_id); + +/** used so pbvh can differentiate between different strokes, + see BKE_pbvh_bmesh_check_origdata */ +void BKE_pbvh_set_stroke_id(PBVH *pbvh, int stroke_id); + +/* Hierarchical Search in the BVH, two methods: + * - For each hit calling a callback. + * - Gather nodes in an array (easy to multi-thread) see blender::bke::pbvh::search_gather. + */ + +void BKE_pbvh_search_callback(PBVH *pbvh, + BKE_pbvh_SearchCallback scb, + void *search_data, + BKE_pbvh_HitCallback hcb, + void *hit_data); + +/* Ray-cast + * the hit callback is called for all leaf nodes intersecting the ray; + * it's up to the callback to find the primitive within the leaves that is + * hit first */ + +void BKE_pbvh_raycast(PBVH *pbvh, + BKE_pbvh_HitOccludedCallback cb, + void *data, + const float ray_start[3], + const float ray_normal[3], + bool original, + int stroke_id); + +bool BKE_pbvh_node_raycast(SculptSession *ss, + PBVH *pbvh, + PBVHNode *node, + float (*origco)[3], + bool use_origco, + const float ray_start[3], + const float ray_normal[3], + IsectRayPrecalc *isect_precalc, + int *hit_count, + float *depth, + float *back_depth, + PBVHVertRef *active_vertex_index, + PBVHFaceRef *active_face_grid_index, + float *face_normal, + int stroke_id); + +bool BKE_pbvh_bmesh_node_raycast_detail(PBVH *pbvh, + PBVHNode *node, + const float ray_start[3], + IsectRayPrecalc *isect_precalc, + float *depth, + float *r_edge_length); + +/** + * For orthographic cameras, project the far away ray segment points to the root node so + * we can have better precision. + */ +void BKE_pbvh_raycast_project_ray_root( + PBVH *pbvh, bool original, float ray_start[3], float ray_end[3], float ray_normal[3]); + +void BKE_pbvh_find_nearest_to_ray(PBVH *pbvh, + BKE_pbvh_HitOccludedCallback cb, + void *data, + const float ray_start[3], + const float ray_normal[3], + bool original); + +bool BKE_pbvh_node_find_nearest_to_ray(SculptSession *ss, + PBVH *pbvh, + PBVHNode *node, + float (*origco)[3], + bool use_origco, + const float ray_start[3], + const float ray_normal[3], + float *depth, + float *dist_sq, + int stroke_id); + +/* Drawing */ +void BKE_pbvh_set_frustum_planes(PBVH *pbvh, PBVHFrustumPlanes *planes); +void BKE_pbvh_get_frustum_planes(PBVH *pbvh, PBVHFrustumPlanes *planes); + +void BKE_pbvh_draw_cb(PBVH *pbvh, + Mesh *me, + bool update_only_visible, + PBVHFrustumPlanes *update_frustum, + PBVHFrustumPlanes *draw_frustum, + void (*draw_fn)(void *user_data, PBVHBatches *batches, PBVH_GPU_Args *args), + void *user_data, + bool full_render, + PBVHAttrReq *attrs, + int attrs_num); + +/* PBVH Access */ + +bool BKE_pbvh_has_faces(const PBVH *pbvh); + +/** + * Get the PBVH root's bounding box. + */ +void BKE_pbvh_bounding_box(const PBVH *pbvh, float min[3], float max[3]); + +/** + * Multi-res hidden data, only valid for type == PBVH_GRIDS. + */ +unsigned int **BKE_pbvh_grid_hidden(const PBVH *pbvh); + +void BKE_pbvh_sync_visibility_from_verts(PBVH *pbvh, Mesh *me); + +/** + * Returns the number of visible quads in the nodes' grids. + */ +int BKE_pbvh_count_grid_quads(BLI_bitmap **grid_hidden, + const int *grid_indices, + int totgrid, + int gridsize, + int display_gridsize); + +/** + * Multi-res level, only valid for type == #PBVH_GRIDS. + */ +const CCGKey *BKE_pbvh_get_grid_key(const PBVH *pbvh); + +CCGElem **BKE_pbvh_get_grids(const PBVH *pbvh); +BLI_bitmap **BKE_pbvh_get_grid_visibility(const PBVH *pbvh); +int BKE_pbvh_get_grid_num_verts(const PBVH *pbvh); +int BKE_pbvh_get_grid_num_faces(const PBVH *pbvh); + +/* Node Access */ + +void BKE_pbvh_check_tri_areas(PBVH *pbvh, PBVHNode *node); +void BKE_pbvh_face_areas_begin(PBVH *pbvh); + +bool BKE_pbvh_bmesh_check_valence(PBVH *pbvh, PBVHVertRef vertex); +void BKE_pbvh_bmesh_update_valence(PBVH *pbvh, PBVHVertRef vertex); +void BKE_pbvh_bmesh_update_all_valence(PBVH *pbvh); +bool BKE_pbvh_bmesh_mark_update_valence(PBVH *pbvh, PBVHVertRef vertex); + +/* if pbvh uses a split index buffer, will call BKE_pbvh_vert_tag_update_normal_triangulation; + otherwise does nothing. returns true if BKE_pbvh_vert_tag_update_normal_triangulation was + called.*/ +void BKE_pbvh_vert_tag_update_normal_triangulation(PBVHNode *node); +void BKE_pbvh_node_mark_original_update(PBVHNode *node); +void BKE_pbvh_vert_tag_update_normal_tri_area(PBVHNode *node); +void BKE_pbvh_update_all_tri_areas(PBVH *pbvh); +void BKE_pbvh_node_mark_update(PBVHNode *node); +void BKE_pbvh_node_mark_update_mask(PBVHNode *node); +void BKE_pbvh_node_mark_update_color(PBVHNode *node); +void BKE_pbvh_vert_tag_update_normal_visibility(PBVHNode *node); +void BKE_pbvh_node_mark_update_face_sets(PBVHNode *node); +void BKE_pbvh_node_mark_update_visibility(PBVHNode *node); +void BKE_pbvh_node_mark_rebuild_draw(PBVHNode *node); +void BKE_pbvh_node_mark_redraw(PBVHNode *node); +void BKE_pbvh_node_mark_normals_update(PBVHNode *node); +void BKE_pbvh_node_mark_topology_update(PBVHNode *node); +void BKE_pbvh_node_fully_hidden_set(PBVHNode *node, int fully_hidden); +bool BKE_pbvh_node_fully_hidden_get(PBVHNode *node); +void BKE_pbvh_node_fully_masked_set(PBVHNode *node, int fully_masked); +bool BKE_pbvh_node_fully_masked_get(PBVHNode *node); +void BKE_pbvh_node_fully_unmasked_set(PBVHNode *node, int fully_masked); +bool BKE_pbvh_node_fully_unmasked_get(PBVHNode *node); +void BKE_pbvh_node_mark_curvature_update(PBVHNode *node); + +void BKE_pbvh_mark_rebuild_pixels(PBVH *pbvh); +void BKE_pbvh_vert_tag_update_normal(PBVH *pbvh, PBVHVertRef vertex); + +void BKE_pbvh_node_get_grids(PBVH *pbvh, + PBVHNode *node, + int **grid_indices, + int *totgrid, + int *maxgrid, + int *gridsize, + CCGElem ***r_griddata); +void BKE_pbvh_node_num_verts(PBVH *pbvh, PBVHNode *node, int *r_uniquevert, int *r_totvert); +const int *BKE_pbvh_node_get_vert_indices(PBVHNode *node); +void BKE_pbvh_node_get_loops(PBVH *pbvh, + PBVHNode *node, + const int **r_loop_indices, + const int **r_corner_verts); + +/* Get number of faces in the mesh; for PBVH_GRIDS the + * number of base mesh faces is returned. + */ +int BKE_pbvh_num_faces(const PBVH *pbvh); + +void BKE_pbvh_node_get_BB(PBVHNode *node, float bb_min[3], float bb_max[3]); +void BKE_pbvh_node_get_original_BB(PBVHNode *node, float bb_min[3], float bb_max[3]); + +float BKE_pbvh_node_get_tmin(PBVHNode *node); + +/** + * Test if AABB is at least partially inside the #PBVHFrustumPlanes volume. + */ +bool BKE_pbvh_node_frustum_contain_AABB(PBVHNode *node, void *frustum); +/** + * Test if AABB is at least partially outside the #PBVHFrustumPlanes volume. + */ +bool BKE_pbvh_node_frustum_exclude_AABB(PBVHNode *node, void *frustum); + +blender::bke::dyntopo::DyntopoSet *BKE_pbvh_bmesh_node_unique_verts(PBVHNode *node); +blender::bke::dyntopo::DyntopoSet *BKE_pbvh_bmesh_node_other_verts(PBVHNode *node); +blender::bke::dyntopo::DyntopoSet *BKE_pbvh_bmesh_node_faces(PBVHNode *node); + +void BKE_pbvh_bmesh_regen_node_verts(PBVH *pbvh, bool report); +void BKE_pbvh_bmesh_mark_node_regen(PBVH *pbvh, PBVHNode *node); + +/* Update Bounding Box/Redraw and clear flags. */ + +void BKE_pbvh_update_bounds(PBVH *pbvh, int flags); +void BKE_pbvh_update_vertex_data(PBVH *pbvh, int flags); +void BKE_pbvh_update_visibility(PBVH *pbvh); +void BKE_pbvh_update_normals(PBVH *pbvh, SubdivCCG *subdiv_ccg); +void BKE_pbvh_redraw_BB(PBVH *pbvh, float bb_min[3], float bb_max[3]); +void BKE_pbvh_get_grid_updates(PBVH *pbvh, bool clear, void ***r_gridfaces, int *r_totface); +void BKE_pbvh_grids_update(PBVH *pbvh, + CCGElem **grids, + void **gridfaces, + DMFlagMat *flagmats, + unsigned int **grid_hidden, + CCGKey *key); +void BKE_pbvh_subdiv_ccg_set(PBVH *pbvh, SubdivCCG *subdiv_ccg); +void BKE_pbvh_face_sets_set(PBVH *pbvh, int *face_sets); + +/** + * If an operation causes the hide status stored in the mesh to change, this must be called + * to update the references to those attributes, since they are only added when necessary. + */ +void BKE_pbvh_update_hide_attributes_from_mesh(PBVH *pbvh); + +void BKE_pbvh_face_sets_color_set(PBVH *pbvh, int seed, int color_default); + +/* Vertex Deformer. */ + +float (*BKE_pbvh_vert_coords_alloc(PBVH *pbvh))[3]; +void BKE_pbvh_vert_coords_apply(PBVH *pbvh, const float (*vertCos)[3], int totvert); +bool BKE_pbvh_is_deformed(PBVH *pbvh); + +/* Vertex Iterator. */ + +/* This iterator has quite a lot of code, but it's designed to: + * - allow the compiler to eliminate dead code and variables + * - spend most of the time in the relatively simple inner loop */ + +/* NOTE: PBVH_ITER_ALL does not skip hidden vertices, + * PBVH_ITER_UNIQUE does */ +#define PBVH_ITER_ALL 0 +#define PBVH_ITER_UNIQUE 1 + +struct PBVHVertexIter { + /* iteration */ + int g; + int width; + int height; + int gx; + int gy; + int i; + int index; + PBVHVertRef vertex; + + /* grid */ + CCGKey key; + CCGElem **grids; + CCGElem *grid; + BLI_bitmap **grid_hidden, *gh; + int *grid_indices; + int totgrid; + int gridsize; + + /* mesh */ + float (*vert_positions)[3]; + float (*vert_normals)[3]; + const bool *hide_vert; + int totvert; + const int *vert_indices; + float *vmask; + bool is_mesh; + + /* bmesh */ + int bi; + int bm_cur_set; + blender::bke::dyntopo::DyntopoSet *bm_unique_verts, *bm_other_verts; + blender::bke::dyntopo::DyntopoSet::iterator bm_iter, bm_iter_end; + + CustomData *bm_vdata; + int cd_vert_mask_offset; + int cd_vcol_offset; + + /* result: these are all computed in the macro, but we assume + * that compiler optimization's will skip the ones we don't use */ + BMVert *bm_vert; + float *co; + float *no; + float *fno; + float *mask; + bool visible; +}; + +void pbvh_vertex_iter_init(PBVH *pbvh, PBVHNode *node, PBVHVertexIter *vi, int mode); + +#define BKE_pbvh_vertex_iter_begin(pbvh, node, vi, mode) \ + pbvh_vertex_iter_init(pbvh, node, &vi, mode); \ +\ + for (vi.i = 0, vi.g = 0; vi.g < vi.totgrid; vi.g++) { \ + if (vi.grids) { \ + vi.width = vi.gridsize; \ + vi.height = vi.gridsize; \ + vi.index = vi.vertex.i = vi.grid_indices[vi.g] * vi.key.grid_area - 1; \ + vi.grid = vi.grids[vi.grid_indices[vi.g]]; \ + if (mode == PBVH_ITER_UNIQUE) { \ + vi.gh = vi.grid_hidden[vi.grid_indices[vi.g]]; \ + } \ + } \ + else { \ + vi.width = vi.totvert; \ + vi.height = 1; \ + } \ +\ + for (vi.gy = 0; vi.gy < vi.height; vi.gy++) { \ + for (vi.gx = 0; vi.gx < vi.width; vi.gx++, vi.i++) { \ + if (vi.grid) { \ + vi.co = CCG_elem_co(&vi.key, vi.grid); \ + vi.fno = CCG_elem_no(&vi.key, vi.grid); \ + vi.mask = vi.key.has_mask ? CCG_elem_mask(&vi.key, vi.grid) : NULL; \ + vi.grid = CCG_elem_next(&vi.key, vi.grid); \ + vi.index++; \ + vi.vertex.i++; \ + vi.visible = true; \ + if (vi.gh) { \ + if (BLI_BITMAP_TEST(vi.gh, vi.gy * vi.gridsize + vi.gx)) { \ + continue; \ + } \ + } \ + } \ + else if (vi.vert_positions) { \ + vi.visible = !(vi.hide_vert && vi.hide_vert[vi.vert_indices[vi.gx]]); \ + if (mode == PBVH_ITER_UNIQUE && !vi.visible) { \ + continue; \ + } \ + vi.co = vi.vert_positions[vi.vert_indices[vi.gx]]; \ + vi.no = vi.vert_normals[vi.vert_indices[vi.gx]]; \ + vi.index = vi.vertex.i = vi.vert_indices[vi.i]; \ + if (vi.vmask) { \ + vi.mask = &vi.vmask[vi.index]; \ + } \ + } \ + else { \ + if (vi.bm_iter == vi.bm_iter_end) { \ + if (vi.bm_cur_set == 0 && mode == PBVH_ITER_ALL) { \ + vi.bm_cur_set = 1; \ + vi.bm_iter = vi.bm_other_verts->begin(); \ + vi.bm_iter_end = vi.bm_other_verts->end(); \ + if (vi.bm_iter == vi.bm_iter_end) { \ + continue; \ + } \ + } \ + else { \ + continue; \ + } \ + } \ + BMVert *bv = *vi.bm_iter; \ + ++vi.bm_iter; \ + vi.bm_vert = bv; \ + vi.vertex.i = (intptr_t)bv; \ + vi.index = BM_elem_index_get(vi.bm_vert); \ + vi.visible = !BM_elem_flag_test_bool(vi.bm_vert, BM_ELEM_HIDDEN); \ + if (mode == PBVH_ITER_UNIQUE && !vi.visible) { \ + continue; \ + } \ + vi.co = vi.bm_vert->co; \ + vi.fno = vi.bm_vert->no; \ + vi.mask = vi.cd_vert_mask_offset != -1 ? \ + (float *)BM_ELEM_CD_GET_VOID_P(vi.bm_vert, vi.cd_vert_mask_offset) : \ + nullptr; \ + } + +#define BKE_pbvh_vertex_iter_end \ + } \ + } \ + } \ + ((void)0) + +#define PBVH_FACE_ITER_VERTS_RESERVED 8 + +struct PBVHFaceIter { + PBVHFaceRef face; + int index; + bool *hide; + int *face_set; + int i; + + PBVHVertRef *verts; + int verts_num; + + PBVHVertRef verts_reserved_[PBVH_FACE_ITER_VERTS_RESERVED]; + const PBVHNode *node_; + PBVHType pbvh_type_; + int verts_size_; + blender::bke::dyntopo::DyntopoSet::iterator bm_iter_, bm_iter_end_; + int cd_face_set_; + bool *hide_poly_; + int *face_sets_; + const int *poly_offsets_; + const int *looptri_polys_; + const int *corner_verts_; + int prim_index_; + const SubdivCCG *subdiv_ccg_; + const BMesh *bm; + CCGKey subdiv_key_; + + int last_poly_index_; +}; + +void BKE_pbvh_face_iter_init(PBVH *pbvh, PBVHNode *node, PBVHFaceIter *fd); +void BKE_pbvh_face_iter_step(PBVHFaceIter *fd); +bool BKE_pbvh_face_iter_done(PBVHFaceIter *fd); +void BKE_pbvh_face_iter_finish(PBVHFaceIter *fd); + +/** + * Iterate over faces inside a #PBVHNode. These are either base mesh faces + * (for PBVH_FACES and PBVH_GRIDS) or BMesh faces (for PBVH_BMESH). + */ +#define BKE_pbvh_face_iter_begin(pbvh, node, fd) \ + BKE_pbvh_face_iter_init(pbvh, node, &fd); \ + for (; !BKE_pbvh_face_iter_done(&fd); BKE_pbvh_face_iter_step(&fd)) { + +#define BKE_pbvh_face_iter_end(fd) \ + } \ + BKE_pbvh_face_iter_finish(&fd) + +void BKE_pbvh_node_get_proxies(PBVHNode *node, PBVHProxyNode **proxies, int *proxy_count); +void BKE_pbvh_node_free_proxies(PBVHNode *node); +PBVHProxyNode *BKE_pbvh_node_add_proxy(PBVH *pbvh, PBVHNode *node); + +/** + * \note doing a full search on all vertices here seems expensive, + * however this is important to avoid having to recalculate bound-box & sync the buffers to the + * GPU (which is far more expensive!) See: #47232. + */ +bool BKE_pbvh_node_has_vert_with_normal_update_tag(PBVH *pbvh, PBVHNode *node); + +// void BKE_pbvh_node_BB_reset(PBVHNode *node); +// void BKE_pbvh_node_BB_expand(PBVHNode *node, float co[3]); + +bool pbvh_has_mask(const PBVH *pbvh); + +bool pbvh_has_face_sets(PBVH *pbvh); + +void pbvh_show_mask_set(PBVH *pbvh, bool show_mask); +void pbvh_show_face_sets_set(PBVH *pbvh, bool show_face_sets); + +/* Parallelization. */ + +void BKE_pbvh_parallel_range_settings(TaskParallelSettings *settings, + bool use_threading, + int totnode); + +float (*BKE_pbvh_get_vert_positions(const PBVH *pbvh))[3]; +const float (*BKE_pbvh_get_vert_normals(const PBVH *pbvh))[3]; +const bool *BKE_pbvh_get_vert_hide(const PBVH *pbvh); +bool *BKE_pbvh_get_vert_hide_for_write(PBVH *pbvh); + +const bool *BKE_pbvh_get_poly_hide(const PBVH *pbvh); + +/* Get active color attribute; if pbvh is non-null + * and is of type PBVH_BMESH the layer inside of + * pbvh->header.bm will be returned, otherwise the + * layer will be looked up inside of me. + */ +bool BKE_pbvh_get_color_layer(const PBVH *pbvh, + const Mesh *me, + CustomDataLayer **r_layer, + eAttrDomain *r_attr); + +/* Swaps colors at each element in indices (of domain pbvh->vcol_domain) + * with values in colors. PBVH_FACES only.*/ +void BKE_pbvh_swap_colors(PBVH *pbvh, + const int *indices, + const int indices_num, + float (*colors)[4]); + +/* Stores colors from the elements in indices (of domain pbvh->vcol_domain) + * into colors. PBVH_FACES only.*/ +void BKE_pbvh_store_colors(PBVH *pbvh, + const int *indices, + const int indices_num, + float (*colors)[4]); + +/* Like BKE_pbvh_store_colors but handles loop->vert conversion. PBVH_FACES only. */ +void BKE_pbvh_store_colors_vertex(PBVH *pbvh, + const int *indices, + const int indices_num, + float (*colors)[4]); + +bool BKE_pbvh_is_drawing(const PBVH *pbvh); + +/* Do not call in PBVH_GRIDS mode */ +void BKE_pbvh_node_num_loops(PBVH *pbvh, PBVHNode *node, int *r_totloop); + +void BKE_pbvh_update_active_vcol(PBVH *pbvh, const Mesh *mesh); + +void BKE_pbvh_vertex_color_set(PBVH *pbvh, PBVHVertRef vertex, const float color[4]); +void BKE_pbvh_vertex_color_get(const PBVH *pbvh, PBVHVertRef vertex, float r_color[4]); + +void BKE_pbvh_ensure_node_loops(PBVH *pbvh); +bool BKE_pbvh_draw_cache_invalid(const PBVH *pbvh); +int BKE_pbvh_debug_draw_gen_get(PBVHNode *node); + +int BKE_pbvh_get_node_index(PBVH *pbvh, PBVHNode *node); +int BKE_pbvh_get_node_id(PBVH *pbvh, PBVHNode *node); + +void BKE_pbvh_curvature_update_set(PBVHNode *node, bool state); +bool BKE_pbvh_curvature_update_get(PBVHNode *node); + +int BKE_pbvh_get_totnodes(PBVH *pbvh); + +bool BKE_pbvh_bmesh_check_tris(PBVH *pbvh, PBVHNode *node); +PBVHTriBuf *BKE_pbvh_bmesh_get_tris(PBVH *pbvh, PBVHNode *node); +void BKE_pbvh_bmesh_free_tris(PBVH *pbvh, PBVHNode *node); + +/*recalculates boundary flags for *all* vertices. used by + symmetrize.*/ +void BKE_pbvh_recalc_bmesh_boundary(PBVH *pbvh); +void BKE_pbvh_set_boundary_flags(PBVH *pbvh, int *boundary_flags); + +/* saves all bmesh references to internal indices, to be restored later */ +void BKE_pbvh_bmesh_save_indices(PBVH *pbvh); + +/* restore bmesh references from previously indices saved by BKE_pbvh_bmesh_save_indices */ +void BKE_pbvh_bmesh_from_saved_indices(PBVH *pbvh); + +/* wraps calls to BM_mesh_toolflags_set in BKE_pbvh_bmesh_save_indices and + * BKE_pbvh_bmesh_from_saved_indices */ +void BKE_pbvh_bmesh_set_toolflags(PBVH *pbvh, bool use_toolflags); + +void BKE_pbvh_bmesh_remove_face(PBVH *pbvh, BMFace *f, bool log_face); +void BKE_pbvh_bmesh_remove_edge(PBVH *pbvh, BMEdge *e, bool log_edge); +void BKE_pbvh_bmesh_remove_vertex(PBVH *pbvh, BMVert *v, bool log_vert); +void BKE_pbvh_bmesh_add_face(PBVH *pbvh, BMFace *f, bool log_face, bool force_tree_walk); + +/* e_tri and f_example are allowed to be nullptr. */ +BMFace *BKE_pbvh_face_create_bmesh(PBVH *pbvh, + BMVert *v_tri[3], + BMEdge *e_tri[3], + const BMFace *f_example); + +/* If node is nullptr then one will be found in the pbvh. */ +BMVert *BKE_pbvh_vert_create_bmesh( + PBVH *pbvh, float co[3], float no[3], PBVHNode *node, BMVert *v_example); +PBVHNode *BKE_pbvh_node_from_face_bmesh(PBVH *pbvh, BMFace *f); +PBVHNode *BKE_pbvh_node_from_index(PBVH *pbvh, int node_i); + +PBVHNode *BKE_pbvh_get_node_leaf_safe(PBVH *pbvh, int i); + +void BKE_pbvh_get_vert_face_areas(PBVH *pbvh, PBVHVertRef vertex, float *r_areas, int valence); +void BKE_pbvh_set_symmetry(PBVH *pbvh, int symmetry, int boundary_symmetry); + +int BKE_pbvh_do_fset_symmetry(int fset, const int symflag, const float *co); + +/* +Uses pmap to build an array of edge indices surrounding vertex +r_edges, r_edges_size, heap_alloc define an existing array to put data in. + +final array is similarly put in these pointers. note that calling code +may pass a stack allocated array (*heap_alloc should be false), and must +check if heap_alloc is true afterwards and free *r_edges. + +r_polys is an array of integer pairs and must be same logical size as r_edges +*/ +void BKE_pbvh_pmap_to_edges(PBVH *pbvh, + PBVHVertRef vertex, + int **r_edges, + int *r_edges_size, + bool *heap_alloc, + int **r_polys); + +void BKE_pbvh_reproject_smooth_set(PBVH *pbvh, bool value); + +void BKE_pbvh_set_face_areas(PBVH *pbvh, float *face_areas); +void BKE_pbvh_set_bmesh(PBVH *pbvh, BMesh *bm); +void BKE_pbvh_free_bmesh(PBVH *pbvh, BMesh *bm); + +void BKE_pbvh_show_orig_set(PBVH *pbvh, bool show_orig); +bool BKE_pbvh_show_orig_get(PBVH *pbvh); + +void BKE_pbvh_flush_tri_areas(PBVH *pbvh); +void BKE_pbvh_bmesh_check_nodes(PBVH *pbvh); + +#include "BLI_math_vector.hh" + +namespace blender::bke::pbvh { +void set_flags_valence(PBVH *pbvh, uint8_t *flags, int *valence); +void set_original(PBVH *pbvh, Span origco, Span origno); +void update_vert_boundary_bmesh(int cd_faceset_offset, + int cd_vert_node_offset, + int cd_face_node_offset, + int cd_vcol, + int cd_boundary_flag, + const int cd_flag, + const int cd_valence, + BMVert *v, + const CustomData *ldata, + float sharp_angle_limit); +void update_sharp_vertex_bmesh(BMVert *v, int cd_boundary_flag, const float sharp_angle_limit); + +void update_vert_boundary_faces(int *boundary_flags, + const int *face_sets, + const bool *hide_poly, + const float (*vert_positions)[3], + const blender::int2 *medge, + const int *corner_verts, + const int *corner_edges, + blender::OffsetIndices polys, + const blender::GroupedSpan &pmap, + PBVHVertRef vertex, + const bool *sharp_edges, + const bool *seam_edges, + uint8_t *flags, + int *valence); +void update_edge_boundary_bmesh(BMEdge *e, + int cd_faceset_offset, + int cd_edge_boundary, + const int cd_flag, + const int cd_valence, + const CustomData *ldata, + float sharp_angle_limit); +void update_edge_boundary_faces(int edge, + Span vertex_positions, + Span vertex_normals, + Span edges, + OffsetIndices polys, + Span poly_normals, + int *edge_boundary_flags, + const int *vert_boundary_flags, + const int *face_sets, + const bool *sharp_edge, + const bool *seam_edge, + const GroupedSpan &pmap, + const GroupedSpan &epmap, + const CustomData *ldata, + float sharp_angle_limit, + blender::Span corner_verts, + blender::Span corner_edges); +void update_edge_boundary_grids(int edge, + Span edges, + OffsetIndices polys, + int *edge_boundary_flags, + const int *vert_boundary_flags, + const int *face_sets, + const bool *sharp_edge, + const bool *seam_edge, + const GroupedSpan &pmap, + const GroupedSpan &epmap, + const CustomData *ldata, + SubdivCCG *subdiv_ccg, + const CCGKey *key, + float sharp_angle_limit, + blender::Span corner_verts, + blender::Span corner_edges); +void update_vert_boundary_grids(PBVH *pbvh, int vertex); + +bool check_vert_boundary(PBVH *pbvh, PBVHVertRef vertex); +bool check_edge_boundary(PBVH *pbvh, PBVHEdgeRef edge); + +Vector search_gather(PBVH *pbvh, + BKE_pbvh_SearchCallback scb, + void *search_data, + PBVHNodeFlags leaf_flag = PBVH_Leaf); +Vector gather_proxies(PBVH *pbvh); +Vector get_flagged_nodes(PBVH *pbvh, int flag); +void set_pmap(PBVH *pbvh, GroupedSpan pmap); +void set_vemap(PBVH *pbvh, GroupedSpan vemap); +GroupedSpan get_pmap(PBVH *pbvh); + +void sharp_limit_set(PBVH *pbvh, float limit); +float test_sharp_faces_bmesh(BMFace *f1, BMFace *f2, float limit); +float test_sharp_faces_mesh(int f1, + int f2, + float limit, + blender::Span positions, + blender::OffsetIndices &polys, + blender::Span poly_normals, + blender::Span corner_verts); + +blender::Span get_poly_normals(const PBVH *pbvh); +void set_vert_boundary_map(PBVH *pbvh, BLI_bitmap *vert_boundary_map); +void on_stroke_start(PBVH *pbvh); +} // namespace blender::bke::pbvh diff --git a/source/blender/blenkernel/BKE_pbvh_iter.hh b/source/blender/blenkernel/BKE_pbvh_iter.hh index e3fc9aaeda7..b9de818f4d1 100644 --- a/source/blender/blenkernel/BKE_pbvh_iter.hh +++ b/source/blender/blenkernel/BKE_pbvh_iter.hh @@ -19,7 +19,7 @@ #include "BLI_vector.hh" #include "BLI_timeit.hh" -#include "BKE_pbvh.h" +#include "BKE_pbvh_api.hh" #include "intern/pbvh_intern.hh" #include diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt index ea441879afc..acd36616957 100644 --- a/source/blender/blenkernel/CMakeLists.txt +++ b/source/blender/blenkernel/CMakeLists.txt @@ -470,6 +470,7 @@ set(SRC BKE_paint.h BKE_particle.h BKE_pbvh.h + BKE_pbvh_api.hh BKE_pbvh_pixels.hh BKE_pbvh_iter.hh BKE_pointcache.h diff --git a/source/blender/blenkernel/intern/cdderivedmesh.cc b/source/blender/blenkernel/intern/cdderivedmesh.cc index 3762b833a30..c729b875759 100644 --- a/source/blender/blenkernel/intern/cdderivedmesh.cc +++ b/source/blender/blenkernel/intern/cdderivedmesh.cc @@ -25,7 +25,7 @@ #include "BKE_mesh_mapping.h" #include "BKE_object.h" #include "BKE_paint.h" -#include "BKE_pbvh.h" +#include "BKE_pbvh_api.hh" #include "DNA_curve_types.h" /* for Curve */ #include "DNA_mesh_types.h" diff --git a/source/blender/blenkernel/intern/dyntopo.cc b/source/blender/blenkernel/intern/dyntopo.cc index 50587c023d0..a15c0ca6dfa 100644 --- a/source/blender/blenkernel/intern/dyntopo.cc +++ b/source/blender/blenkernel/intern/dyntopo.cc @@ -38,7 +38,7 @@ #include "BKE_customdata.h" #include "BKE_dyntopo.hh" #include "BKE_paint.h" -#include "BKE_pbvh.h" +#include "BKE_pbvh_api.hh" #include "BKE_sculpt.hh" #include "bmesh.h" diff --git a/source/blender/blenkernel/intern/dyntopo_collapse.cc b/source/blender/blenkernel/intern/dyntopo_collapse.cc index bbf066ae593..ac6653afd68 100644 --- a/source/blender/blenkernel/intern/dyntopo_collapse.cc +++ b/source/blender/blenkernel/intern/dyntopo_collapse.cc @@ -27,7 +27,7 @@ #include "BKE_customdata.h" #include "BKE_dyntopo.hh" #include "BKE_paint.h" -#include "BKE_pbvh.h" +#include "BKE_pbvh_api.hh" #include "BKE_sculpt.h" #include "../../bmesh/intern/bmesh_collapse.hh" diff --git a/source/blender/blenkernel/intern/dyntopo_intern.hh b/source/blender/blenkernel/intern/dyntopo_intern.hh index e0761bee71b..90306d2db78 100644 --- a/source/blender/blenkernel/intern/dyntopo_intern.hh +++ b/source/blender/blenkernel/intern/dyntopo_intern.hh @@ -2,7 +2,7 @@ #include "BKE_dyntopo.hh" #include "BKE_paint.h" -#include "BKE_pbvh.h" +#include "BKE_pbvh_api.hh" #include "BKE_sculpt.hh" #include "BLI_asan.h" diff --git a/source/blender/blenkernel/intern/multires.cc b/source/blender/blenkernel/intern/multires.cc index dba13979b65..aa87189349d 100644 --- a/source/blender/blenkernel/intern/multires.cc +++ b/source/blender/blenkernel/intern/multires.cc @@ -31,7 +31,7 @@ #include "BKE_modifier.h" #include "BKE_multires.h" #include "BKE_paint.h" -#include "BKE_pbvh.h" +#include "BKE_pbvh_api.hh" #include "BKE_scene.h" #include "BKE_subdiv_ccg.h" #include "BKE_subsurf.h" diff --git a/source/blender/blenkernel/intern/object.cc b/source/blender/blenkernel/intern/object.cc index 6cc90516692..c84e9379bd6 100644 --- a/source/blender/blenkernel/intern/object.cc +++ b/source/blender/blenkernel/intern/object.cc @@ -117,7 +117,7 @@ #include "BKE_object.h" #include "BKE_paint.h" #include "BKE_particle.h" -#include "BKE_pbvh.h" +#include "BKE_pbvh_api.hh" #include "BKE_pointcache.h" #include "BKE_pointcloud.h" #include "BKE_pose_backup.h" diff --git a/source/blender/blenkernel/intern/paint.cc b/source/blender/blenkernel/intern/paint.cc index e345de37dd5..a01ff21d1cb 100644 --- a/source/blender/blenkernel/intern/paint.cc +++ b/source/blender/blenkernel/intern/paint.cc @@ -63,7 +63,7 @@ #include "BKE_multires.h" #include "BKE_object.h" #include "BKE_paint.h" -#include "BKE_pbvh.h" +#include "BKE_pbvh_api.hh" #include "BKE_scene.h" #include "BKE_sculpt.hh" #include "BKE_subdiv_ccg.h" diff --git a/source/blender/blenkernel/intern/pbvh.cc b/source/blender/blenkernel/intern/pbvh.cc index 40325c1d4f3..3b7a53839ec 100644 --- a/source/blender/blenkernel/intern/pbvh.cc +++ b/source/blender/blenkernel/intern/pbvh.cc @@ -45,7 +45,7 @@ #include "BKE_mesh_mapping.h" #include "BKE_object.h" #include "BKE_paint.h" -#include "BKE_pbvh.h" +#include "BKE_pbvh_api.hh" #include "BKE_sculpt.hh" #include "BKE_subdiv_ccg.h" diff --git a/source/blender/blenkernel/intern/pbvh_bmesh.cc b/source/blender/blenkernel/intern/pbvh_bmesh.cc index 8fed3eabd9d..e8e4dbac3be 100644 --- a/source/blender/blenkernel/intern/pbvh_bmesh.cc +++ b/source/blender/blenkernel/intern/pbvh_bmesh.cc @@ -61,7 +61,7 @@ Topology rake: #include "BKE_context.h" #include "BKE_global.h" #include "BKE_paint.h" -#include "BKE_pbvh.h" +#include "BKE_pbvh_api.hh" #include "DRW_pbvh.hh" @@ -273,12 +273,12 @@ ATTR_NO_OPT void pbvh_bmesh_check_nodes(PBVH *pbvh) } } -extern "C" void BKE_pbvh_bmesh_check_nodes(PBVH *pbvh) +void BKE_pbvh_bmesh_check_nodes(PBVH *pbvh) { pbvh_bmesh_check_nodes(pbvh); } #else -extern "C" void BKE_pbvh_bmesh_check_nodes(PBVH * /*pbvh*/) {} +void BKE_pbvh_bmesh_check_nodes(PBVH * /*pbvh*/) {} #endif /** \} */ @@ -2353,7 +2353,7 @@ void BKE_pbvh_set_idmap(PBVH *pbvh, BMIdMap *idmap) #if 0 PBVH *global_debug_pbvh = nullptr; -extern "C" void debug_pbvh_on_vert_kill(BMVert *v) +void debug_pbvh_on_vert_kill(BMVert *v) { PBVH *pbvh = global_debug_pbvh; @@ -2750,7 +2750,7 @@ static uintptr_t tri_loopkey( * (currently just raycast), store the node's triangles and vertices. * * Skips triangles that are hidden. */ -bool BKE_pbvh_bmesh_check_tris(PBVH *pbvh, PBVHNode *node) +ATTR_NO_OPT bool BKE_pbvh_bmesh_check_tris(PBVH *pbvh, PBVHNode *node) { BMesh *bm = pbvh->header.bm; @@ -2778,6 +2778,8 @@ bool BKE_pbvh_bmesh_check_tris(PBVH *pbvh, PBVHNode *node) BKE_pbvh_bmesh_free_tris(pbvh, node); } + blender::Map vertmap; + node->tribuf = MEM_new("node->tribuf"); pbvh_init_tribuf(node, node->tribuf); @@ -2796,7 +2798,7 @@ bool BKE_pbvh_bmesh_check_tris(PBVH *pbvh, PBVHNode *node) int ni = int(node - pbvh->nodes); - auto add_tri_verts = [cd_uvs, totuv, pbvh, &min, &max, ni]( + auto add_tri_verts = [cd_uvs, totuv, pbvh, &min, &max, ni, &vertmap]( PBVHTriBuf *tribuf, PBVHTri &tri, BMLoop *l, int mat_nr, int j) { int tri_v; @@ -2811,7 +2813,7 @@ bool BKE_pbvh_bmesh_check_tris(PBVH *pbvh, PBVHNode *node) void *loopkey = reinterpret_cast( tri_loopkey(l, mat_nr, pbvh->cd_faceset_offset, cd_uvs, totuv, pbvh->cd_boundary_flag)); - tri_v = tribuf->vertmap.lookup_or_add(loopkey, tribuf->verts.size()); + tri_v = vertmap.lookup_or_add(loopkey, tribuf->verts.size()); } else { /* Flat shaded faces. */ tri_v = tribuf->verts.size(); @@ -2920,6 +2922,60 @@ bool BKE_pbvh_bmesh_check_tris(PBVH *pbvh, PBVHNode *node) zero_v3(node->tribuf->max); } + /* Compact memory, does a simple copy. */ + auto compact_vector = [&](auto &vector) { + if (vector.capacity() < size_t(double(vector.size()) * 1.25)) { + return; + } + + if (vector.size() == 0) { + vector.clear_and_shrink(); + return; + } + + using Type = std::remove_reference_t; + +#if 1 + size_t count = vector.size(); + + printf("c1: %d\n", int(vector.capacity())); + + Vector cpy; + cpy.reserve(count); + cpy.extend_unchecked(vector.data(), count); + + vector = std::move(cpy); + printf("c2: %d\n", int(vector.capacity())); + +#else + size_t count = vector.size(); + size_t size = sizeof(*vector.data()) * count; + + Type *mem = static_cast(MEM_mallocN(size, "temp")); + memcpy(static_cast(mem), static_cast(vector.data()), size); + + vector.clear_and_shrink(); + vector.reserve(count); + vector.extend_unchecked(mem, count); + + MEM_freeN(static_cast(mem)); +#endif + // + }; + + auto compact_tribuf = [&compact_vector](PBVHTriBuf *tribuf) { + compact_vector(tribuf->verts); + compact_vector(tribuf->edges); + compact_vector(tribuf->loops); + compact_vector(tribuf->tris); + }; + + compact_tribuf(node->tribuf); + + for (PBVHTriBuf &tribuf : *tribufs) { + compact_tribuf(&tribuf); + } + return true; } diff --git a/source/blender/blenkernel/intern/pbvh_colors.cc b/source/blender/blenkernel/intern/pbvh_colors.cc index 714fd7c5856..8a7a787d128 100644 --- a/source/blender/blenkernel/intern/pbvh_colors.cc +++ b/source/blender/blenkernel/intern/pbvh_colors.cc @@ -26,7 +26,7 @@ #include "BKE_mesh.hh" #include "BKE_mesh_mapping.h" #include "BKE_paint.h" -#include "BKE_pbvh.h" +#include "BKE_pbvh_api.hh" #include "BKE_subdiv_ccg.h" #include "PIL_time.h" @@ -219,7 +219,6 @@ static void pbvh_vertex_color_set_bmesh(PBVH &pbvh, PBVHVertRef vertex, const fl } // namespace blender::bke -extern "C" { void BKE_pbvh_vertex_color_get(const PBVH *pbvh, PBVHVertRef vertex, float r_color[4]) { blender::bke::to_static_color_type(eCustomDataType(pbvh->color_layer->type), [&](auto dummy) { @@ -295,4 +294,3 @@ void BKE_pbvh_store_colors_vertex(PBVH *pbvh, }); } } -} diff --git a/source/blender/blenkernel/intern/pbvh_intern.hh b/source/blender/blenkernel/intern/pbvh_intern.hh index 9ddf33c5389..b39fb1bc453 100644 --- a/source/blender/blenkernel/intern/pbvh_intern.hh +++ b/source/blender/blenkernel/intern/pbvh_intern.hh @@ -18,7 +18,7 @@ #include "BKE_attribute.h" #include "BKE_paint.h" /* for SCULPT_BOUNDARY_NEEDS_UPDATE */ -#include "BKE_pbvh.h" +#include "BKE_pbvh_api.hh" #include "../../bmesh/intern/bmesh_idmap.h" #include "bmesh.h" diff --git a/source/blender/blenkernel/intern/pbvh_pixels.cc b/source/blender/blenkernel/intern/pbvh_pixels.cc index f2977a136d7..31d33c36e77 100644 --- a/source/blender/blenkernel/intern/pbvh_pixels.cc +++ b/source/blender/blenkernel/intern/pbvh_pixels.cc @@ -6,7 +6,7 @@ #include "BKE_customdata.h" #include "BKE_mesh.hh" #include "BKE_mesh_mapping.h" -#include "BKE_pbvh.h" +#include "BKE_pbvh_api.hh" #include "BKE_pbvh_pixels.hh" #include "DNA_image_types.h" diff --git a/source/blender/blenkernel/intern/pbvh_pixels_copy.cc b/source/blender/blenkernel/intern/pbvh_pixels_copy.cc index 523df3e1007..8e54816feaa 100644 --- a/source/blender/blenkernel/intern/pbvh_pixels_copy.cc +++ b/source/blender/blenkernel/intern/pbvh_pixels_copy.cc @@ -13,7 +13,7 @@ #include "IMB_imbuf_types.h" #include "BKE_image_wrappers.hh" -#include "BKE_pbvh.h" +#include "BKE_pbvh_api.hh" #include "BKE_pbvh_pixels.hh" #include "pbvh_intern.hh" diff --git a/source/blender/blenkernel/intern/pbvh_pixels_copy.hh b/source/blender/blenkernel/intern/pbvh_pixels_copy.hh index 18da2c171e0..d07bee4dee8 100644 --- a/source/blender/blenkernel/intern/pbvh_pixels_copy.hh +++ b/source/blender/blenkernel/intern/pbvh_pixels_copy.hh @@ -10,7 +10,7 @@ #include "IMB_imbuf_types.h" #include "BKE_image_wrappers.hh" -#include "BKE_pbvh.h" +#include "BKE_pbvh_api.hh" #include "BKE_pbvh_pixels.hh" #include "pbvh_uv_islands.hh" diff --git a/source/blender/blenkernel/intern/subsurf_ccg.cc b/source/blender/blenkernel/intern/subsurf_ccg.cc index 024a6432257..7992de4134b 100644 --- a/source/blender/blenkernel/intern/subsurf_ccg.cc +++ b/source/blender/blenkernel/intern/subsurf_ccg.cc @@ -40,7 +40,7 @@ #include "BKE_multires.h" #include "BKE_object.h" #include "BKE_paint.h" -#include "BKE_pbvh.h" +#include "BKE_pbvh_api.hh" #include "BKE_scene.h" #include "BKE_subsurf.h" diff --git a/source/blender/draw/engines/overlay/overlay_sculpt.cc b/source/blender/draw/engines/overlay/overlay_sculpt.cc index 0b718f77544..fed4888c3bd 100644 --- a/source/blender/draw/engines/overlay/overlay_sculpt.cc +++ b/source/blender/draw/engines/overlay/overlay_sculpt.cc @@ -12,7 +12,7 @@ #include "overlay_private.hh" #include "BKE_paint.h" -#include "BKE_pbvh.h" +#include "BKE_pbvh_api.hh" #include "BKE_subdiv_ccg.h" void OVERLAY_sculpt_cache_init(OVERLAY_Data *vedata) diff --git a/source/blender/draw/engines/workbench/workbench_engine.c b/source/blender/draw/engines/workbench/workbench_engine.c index 6ecba684a7a..8bd9b3a7019 100644 --- a/source/blender/draw/engines/workbench/workbench_engine.c +++ b/source/blender/draw/engines/workbench/workbench_engine.c @@ -20,7 +20,6 @@ #include "BKE_object.h" #include "BKE_paint.h" #include "BKE_particle.h" -#include "BKE_pbvh.h" #include "DNA_curves_types.h" #include "DNA_fluid_types.h" diff --git a/source/blender/draw/engines/workbench/workbench_engine.cc b/source/blender/draw/engines/workbench/workbench_engine.cc index 0f0ae56d1ba..aecc8d90662 100644 --- a/source/blender/draw/engines/workbench/workbench_engine.cc +++ b/source/blender/draw/engines/workbench/workbench_engine.cc @@ -7,7 +7,7 @@ #include "BKE_object.h" #include "BKE_paint.h" #include "BKE_particle.h" -#include "BKE_pbvh.h" +#include "BKE_pbvh_api.hh" #include "BKE_report.h" #include "DEG_depsgraph_query.h" #include "DNA_fluid_types.h" diff --git a/source/blender/draw/engines/workbench/workbench_state.cc b/source/blender/draw/engines/workbench/workbench_state.cc index a3b4cfc579c..963f80ca81f 100644 --- a/source/blender/draw/engines/workbench/workbench_state.cc +++ b/source/blender/draw/engines/workbench/workbench_state.cc @@ -10,7 +10,7 @@ #include "BKE_object.h" #include "BKE_paint.h" #include "BKE_particle.h" -#include "BKE_pbvh.h" +#include "BKE_pbvh_api.hh" #include "DEG_depsgraph_query.h" #include "DNA_fluid_types.h" #include "ED_paint.h" diff --git a/source/blender/draw/intern/DRW_render.h b/source/blender/draw/intern/DRW_render.h index f534d7505e0..04ae15e692b 100644 --- a/source/blender/draw/intern/DRW_render.h +++ b/source/blender/draw/intern/DRW_render.h @@ -1014,7 +1014,6 @@ void DRW_mesh_batch_cache_get_attributes(struct Object *object, struct Mesh *me, struct DRW_Attributes **r_attrs, struct DRW_MeshCDMask **r_cd_needed); - void DRW_sculpt_debug_cb( PBVHNode *node, void *user_data, const float bmin[3], const float bmax[3], PBVHNodeFlags flag); diff --git a/source/blender/draw/intern/draw_cache_impl_mesh.cc b/source/blender/draw/intern/draw_cache_impl_mesh.cc index 97438061320..1715a3dfbb8 100644 --- a/source/blender/draw/intern/draw_cache_impl_mesh.cc +++ b/source/blender/draw/intern/draw_cache_impl_mesh.cc @@ -43,7 +43,7 @@ #include "BKE_modifier.h" #include "BKE_object_deform.h" #include "BKE_paint.h" -#include "BKE_pbvh.h" +#include "BKE_pbvh_api.hh" #include "BKE_subdiv_modifier.h" #include "BKE_sculpt.h" //NotForPR diff --git a/source/blender/draw/intern/draw_manager.c b/source/blender/draw/intern/draw_manager.c index fd8dacbbc3d..c7cdb1176f3 100644 --- a/source/blender/draw/intern/draw_manager.c +++ b/source/blender/draw/intern/draw_manager.c @@ -35,7 +35,6 @@ #include "BKE_object.h" #include "BKE_paint.h" #include "BKE_particle.h" -#include "BKE_pbvh.h" #include "BKE_pointcache.h" #include "BKE_pointcloud.h" #include "BKE_screen.h" diff --git a/source/blender/draw/intern/draw_manager_data.cc b/source/blender/draw/intern/draw_manager_data.cc index a4a9e993e01..ed3c4a74ba2 100644 --- a/source/blender/draw/intern/draw_manager_data.cc +++ b/source/blender/draw/intern/draw_manager_data.cc @@ -19,7 +19,7 @@ #include "BKE_mesh.hh" #include "BKE_object.h" #include "BKE_paint.h" -#include "BKE_pbvh.h" +#include "BKE_pbvh_api.hh" #include "BKE_volume.h" /* For debug cursor position. */ diff --git a/source/blender/draw/intern/draw_pbvh.cc b/source/blender/draw/intern/draw_pbvh.cc index 10aef1c2e61..b716e8d7f39 100644 --- a/source/blender/draw/intern/draw_pbvh.cc +++ b/source/blender/draw/intern/draw_pbvh.cc @@ -39,7 +39,7 @@ #include "BKE_customdata.h" #include "BKE_mesh.hh" #include "BKE_paint.h" -#include "BKE_pbvh.h" +#include "BKE_pbvh_api.hh" #include "BKE_subdiv_ccg.h" #include "GPU_batch.h" diff --git a/source/blender/draw/intern/draw_sculpt.cc b/source/blender/draw/intern/draw_sculpt.cc index 9f216f14059..2975af1551b 100644 --- a/source/blender/draw/intern/draw_sculpt.cc +++ b/source/blender/draw/intern/draw_sculpt.cc @@ -12,7 +12,7 @@ #include "draw_pbvh.h" #include "BKE_paint.h" -#include "BKE_pbvh.h" +#include "BKE_pbvh_api.hh" #include "DRW_pbvh.hh" namespace blender::draw { diff --git a/source/blender/editors/mesh/editmesh_utils.cc b/source/blender/editors/mesh/editmesh_utils.cc index dce2c3eaeff..750f0cd3b17 100644 --- a/source/blender/editors/mesh/editmesh_utils.cc +++ b/source/blender/editors/mesh/editmesh_utils.cc @@ -29,7 +29,7 @@ #include "BKE_main.h" #include "BKE_mesh.h" #include "BKE_mesh_mapping.h" -#include "BKE_pbvh.h" +#include "BKE_pbvh_api.hh" #include "BKE_report.h" #include "DEG_depsgraph.h" diff --git a/source/blender/editors/sculpt_paint/paint_hide.cc b/source/blender/editors/sculpt_paint/paint_hide.cc index f79e5a25ed3..1d1651f430c 100644 --- a/source/blender/editors/sculpt_paint/paint_hide.cc +++ b/source/blender/editors/sculpt_paint/paint_hide.cc @@ -24,7 +24,7 @@ #include "BKE_mesh.hh" #include "BKE_multires.h" #include "BKE_paint.h" -#include "BKE_pbvh.h" +#include "BKE_pbvh_api.hh" #include "BKE_subsurf.h" #include "DEG_depsgraph.h" diff --git a/source/blender/editors/sculpt_paint/paint_mask.cc b/source/blender/editors/sculpt_paint/paint_mask.cc index a0aae6ba553..6c7ed98b560 100644 --- a/source/blender/editors/sculpt_paint/paint_mask.cc +++ b/source/blender/editors/sculpt_paint/paint_mask.cc @@ -37,7 +37,7 @@ #include "BKE_mesh_types.h" #include "BKE_multires.h" #include "BKE_paint.h" -#include "BKE_pbvh.h" +#include "BKE_pbvh_api.hh" #include "BKE_scene.h" #include "BKE_subsurf.h" diff --git a/source/blender/editors/sculpt_paint/sculpt.cc b/source/blender/editors/sculpt_paint/sculpt.cc index 8308733aa1c..a365f687875 100644 --- a/source/blender/editors/sculpt_paint/sculpt.cc +++ b/source/blender/editors/sculpt_paint/sculpt.cc @@ -54,7 +54,7 @@ #include "BKE_node_runtime.hh" #include "BKE_object.h" #include "BKE_paint.h" -#include "BKE_pbvh.h" +#include "BKE_pbvh_api.hh" #include "BKE_report.h" #include "BKE_scene.h" #include "BKE_sculpt.h" diff --git a/source/blender/editors/sculpt_paint/sculpt_api.cc b/source/blender/editors/sculpt_paint/sculpt_api.cc index ba986195d04..834dbb37d53 100644 --- a/source/blender/editors/sculpt_paint/sculpt_api.cc +++ b/source/blender/editors/sculpt_paint/sculpt_api.cc @@ -70,7 +70,7 @@ #include "BKE_object.h" #include "BKE_paint.h" #include "BKE_particle.h" -#include "BKE_pbvh.h" +#include "BKE_pbvh_api.hh" #include "BKE_pointcache.h" #include "BKE_report.h" #include "BKE_scene.h" diff --git a/source/blender/editors/sculpt_paint/sculpt_automasking.cc b/source/blender/editors/sculpt_paint/sculpt_automasking.cc index 24036f0c259..64794f54dfc 100644 --- a/source/blender/editors/sculpt_paint/sculpt_automasking.cc +++ b/source/blender/editors/sculpt_paint/sculpt_automasking.cc @@ -29,7 +29,7 @@ #include "BKE_mesh_mapping.h" #include "BKE_object.h" #include "BKE_paint.h" -#include "BKE_pbvh.h" +#include "BKE_pbvh_api.hh" #include "BKE_scene.h" #include "DEG_depsgraph.h" diff --git a/source/blender/editors/sculpt_paint/sculpt_boundary.cc b/source/blender/editors/sculpt_paint/sculpt_boundary.cc index 184cea5dc93..4ef1bc4d28e 100644 --- a/source/blender/editors/sculpt_paint/sculpt_boundary.cc +++ b/source/blender/editors/sculpt_paint/sculpt_boundary.cc @@ -32,7 +32,7 @@ #include "BKE_node.h" #include "BKE_object.h" #include "BKE_paint.h" -#include "BKE_pbvh.h" +#include "BKE_pbvh_api.hh" #include "paint_intern.hh" #include "sculpt_intern.hh" diff --git a/source/blender/editors/sculpt_paint/sculpt_brush_types.cc b/source/blender/editors/sculpt_paint/sculpt_brush_types.cc index 9fc6b05db31..40febeec3dd 100644 --- a/source/blender/editors/sculpt_paint/sculpt_brush_types.cc +++ b/source/blender/editors/sculpt_paint/sculpt_brush_types.cc @@ -28,7 +28,7 @@ #include "BKE_context.h" #include "BKE_kelvinlet.h" #include "BKE_paint.h" -#include "BKE_pbvh.h" +#include "BKE_pbvh_api.hh" #include "ED_view3d.h" diff --git a/source/blender/editors/sculpt_paint/sculpt_cloth.cc b/source/blender/editors/sculpt_paint/sculpt_cloth.cc index ef02ad7e93c..6017f949a68 100644 --- a/source/blender/editors/sculpt_paint/sculpt_cloth.cc +++ b/source/blender/editors/sculpt_paint/sculpt_cloth.cc @@ -29,7 +29,7 @@ #include "BKE_context.h" #include "BKE_modifier.h" #include "BKE_paint.h" -#include "BKE_pbvh.h" +#include "BKE_pbvh_api.hh" #include "DEG_depsgraph.h" #include "DEG_depsgraph_query.h" diff --git a/source/blender/editors/sculpt_paint/sculpt_curvature.cc b/source/blender/editors/sculpt_paint/sculpt_curvature.cc index ece3c9eb82d..422a1b2e07b 100644 --- a/source/blender/editors/sculpt_paint/sculpt_curvature.cc +++ b/source/blender/editors/sculpt_paint/sculpt_curvature.cc @@ -67,7 +67,7 @@ #include "BKE_object.h" #include "BKE_paint.h" #include "BKE_particle.h" -#include "BKE_pbvh.h" +#include "BKE_pbvh_api.hh" #include "BKE_pointcache.h" #include "BKE_report.h" #include "BKE_scene.h" diff --git a/source/blender/editors/sculpt_paint/sculpt_detail.cc b/source/blender/editors/sculpt_paint/sculpt_detail.cc index ba41fae3304..159e7ed7b45 100644 --- a/source/blender/editors/sculpt_paint/sculpt_detail.cc +++ b/source/blender/editors/sculpt_paint/sculpt_detail.cc @@ -18,7 +18,7 @@ #include "BKE_context.h" #include "BKE_dyntopo.hh" #include "BKE_paint.h" -#include "BKE_pbvh.h" +#include "BKE_pbvh_api.hh" #include "BKE_screen.h" #include "DEG_depsgraph.h" diff --git a/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc b/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc index b05eab402d0..1ea4cb92824 100644 --- a/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc +++ b/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc @@ -40,7 +40,7 @@ #include "BKE_object.h" #include "BKE_paint.h" #include "BKE_particle.h" -#include "BKE_pbvh.h" +#include "BKE_pbvh_api.hh" #include "BKE_pointcache.h" #include "BKE_scene.h" diff --git a/source/blender/editors/sculpt_paint/sculpt_expand.cc b/source/blender/editors/sculpt_paint/sculpt_expand.cc index 49a11712a9f..e64c98b5973 100644 --- a/source/blender/editors/sculpt_paint/sculpt_expand.cc +++ b/source/blender/editors/sculpt_paint/sculpt_expand.cc @@ -29,7 +29,7 @@ #include "BKE_mesh.hh" #include "BKE_mesh_mapping.h" #include "BKE_paint.h" -#include "BKE_pbvh.h" +#include "BKE_pbvh_api.hh" #include "BKE_report.h" #include "BKE_scene.h" #include "BKE_subdiv_ccg.h" diff --git a/source/blender/editors/sculpt_paint/sculpt_face_set.cc b/source/blender/editors/sculpt_paint/sculpt_face_set.cc index 0073982fa19..a75986af7ce 100644 --- a/source/blender/editors/sculpt_paint/sculpt_face_set.cc +++ b/source/blender/editors/sculpt_paint/sculpt_face_set.cc @@ -41,7 +41,7 @@ #include "BKE_mesh_mapping.h" #include "BKE_object.h" #include "BKE_paint.h" -#include "BKE_pbvh.h" +#include "BKE_pbvh_api.hh" #include "BKE_subdiv_ccg.h" #include "DEG_depsgraph.h" diff --git a/source/blender/editors/sculpt_paint/sculpt_face_set_topology.c b/source/blender/editors/sculpt_paint/sculpt_face_set_topology.c index 72d2bdb01eb..d7d2545ef92 100644 --- a/source/blender/editors/sculpt_paint/sculpt_face_set_topology.c +++ b/source/blender/editors/sculpt_paint/sculpt_face_set_topology.c @@ -47,7 +47,7 @@ #include "BKE_node.h" #include "BKE_object.h" #include "BKE_paint.h" -#include "BKE_pbvh.h" +#include "BKE_pbvh_api.hh" #include "BKE_scene.h" #include "DEG_depsgraph.h" diff --git a/source/blender/editors/sculpt_paint/sculpt_filter_color.cc b/source/blender/editors/sculpt_paint/sculpt_filter_color.cc index 76a08101b4f..681898f8434 100644 --- a/source/blender/editors/sculpt_paint/sculpt_filter_color.cc +++ b/source/blender/editors/sculpt_paint/sculpt_filter_color.cc @@ -19,7 +19,7 @@ #include "BKE_context.h" #include "BKE_paint.h" -#include "BKE_pbvh.h" +#include "BKE_pbvh_api.hh" #include "IMB_colormanagement.h" diff --git a/source/blender/editors/sculpt_paint/sculpt_filter_mask.cc b/source/blender/editors/sculpt_paint/sculpt_filter_mask.cc index 3c277f71202..d1fe1c7cacb 100644 --- a/source/blender/editors/sculpt_paint/sculpt_filter_mask.cc +++ b/source/blender/editors/sculpt_paint/sculpt_filter_mask.cc @@ -16,7 +16,7 @@ #include "BKE_context.h" #include "BKE_paint.h" -#include "BKE_pbvh.h" +#include "BKE_pbvh_api.hh" #include "BKE_scene.h" #include "DEG_depsgraph.h" diff --git a/source/blender/editors/sculpt_paint/sculpt_filter_mesh.cc b/source/blender/editors/sculpt_paint/sculpt_filter_mesh.cc index 2de00f78678..dde1c23e639 100644 --- a/source/blender/editors/sculpt_paint/sculpt_filter_mesh.cc +++ b/source/blender/editors/sculpt_paint/sculpt_filter_mesh.cc @@ -24,7 +24,7 @@ #include "BKE_context.h" #include "BKE_modifier.h" #include "BKE_paint.h" -#include "BKE_pbvh.h" +#include "BKE_pbvh_api.hh" #include "DEG_depsgraph.h" diff --git a/source/blender/editors/sculpt_paint/sculpt_geodesic.cc b/source/blender/editors/sculpt_paint/sculpt_geodesic.cc index f70ed258293..945e7223059 100644 --- a/source/blender/editors/sculpt_paint/sculpt_geodesic.cc +++ b/source/blender/editors/sculpt_paint/sculpt_geodesic.cc @@ -37,7 +37,7 @@ #include "BKE_mesh_mapping.h" #include "BKE_object.h" #include "BKE_paint.h" -#include "BKE_pbvh.h" +#include "BKE_pbvh_api.hh" #include "paint_intern.hh" #include "sculpt_intern.hh" diff --git a/source/blender/editors/sculpt_paint/sculpt_gradient.c b/source/blender/editors/sculpt_paint/sculpt_gradient.c index 3d6dfd45519..e8cd802f355 100644 --- a/source/blender/editors/sculpt_paint/sculpt_gradient.c +++ b/source/blender/editors/sculpt_paint/sculpt_gradient.c @@ -39,7 +39,7 @@ #include "BKE_mesh_mapping.h" #include "BKE_object.h" #include "BKE_paint.h" -#include "BKE_pbvh.h" +#include "BKE_pbvh_api.hh" #include "BKE_scene.h" #include "IMB_colormanagement.h" diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.hh b/source/blender/editors/sculpt_paint/sculpt_intern.hh index 25919648650..97bcc286924 100644 --- a/source/blender/editors/sculpt_paint/sculpt_intern.hh +++ b/source/blender/editors/sculpt_paint/sculpt_intern.hh @@ -18,7 +18,7 @@ #include "BKE_attribute.h" #include "BKE_dyntopo.hh" #include "BKE_paint.h" -#include "BKE_pbvh.h" +#include "BKE_pbvh_api.hh" #include "BKE_sculpt.h" #include "BKE_sculpt.hh" diff --git a/source/blender/editors/sculpt_paint/sculpt_mask_expand.cc b/source/blender/editors/sculpt_paint/sculpt_mask_expand.cc index 9db668c69b5..d846287946a 100644 --- a/source/blender/editors/sculpt_paint/sculpt_mask_expand.cc +++ b/source/blender/editors/sculpt_paint/sculpt_mask_expand.cc @@ -21,7 +21,7 @@ #include "BKE_ccg.h" #include "BKE_context.h" #include "BKE_paint.h" -#include "BKE_pbvh.h" +#include "BKE_pbvh_api.hh" #include "DEG_depsgraph.h" diff --git a/source/blender/editors/sculpt_paint/sculpt_mask_init.cc b/source/blender/editors/sculpt_paint/sculpt_mask_init.cc index 8083fde9c86..f3631cbf10a 100644 --- a/source/blender/editors/sculpt_paint/sculpt_mask_init.cc +++ b/source/blender/editors/sculpt_paint/sculpt_mask_init.cc @@ -23,7 +23,7 @@ #include "BKE_context.h" #include "BKE_multires.h" #include "BKE_paint.h" -#include "BKE_pbvh.h" +#include "BKE_pbvh_api.hh" #include "DEG_depsgraph.h" diff --git a/source/blender/editors/sculpt_paint/sculpt_multiplane_scrape.cc b/source/blender/editors/sculpt_paint/sculpt_multiplane_scrape.cc index b787ca9670d..4436765be01 100644 --- a/source/blender/editors/sculpt_paint/sculpt_multiplane_scrape.cc +++ b/source/blender/editors/sculpt_paint/sculpt_multiplane_scrape.cc @@ -16,7 +16,7 @@ #include "BKE_ccg.h" #include "BKE_context.h" #include "BKE_paint.h" -#include "BKE_pbvh.h" +#include "BKE_pbvh_api.hh" #include "sculpt_intern.hh" diff --git a/source/blender/editors/sculpt_paint/sculpt_ops.cc b/source/blender/editors/sculpt_paint/sculpt_ops.cc index 36033d4f8f8..3cdd805a650 100644 --- a/source/blender/editors/sculpt_paint/sculpt_ops.cc +++ b/source/blender/editors/sculpt_paint/sculpt_ops.cc @@ -44,7 +44,7 @@ #include "BKE_multires.h" #include "BKE_object.h" #include "BKE_paint.h" -#include "BKE_pbvh.h" +#include "BKE_pbvh_api.hh" #include "BKE_report.h" #include "BKE_scene.h" diff --git a/source/blender/editors/sculpt_paint/sculpt_paint_color.cc b/source/blender/editors/sculpt_paint/sculpt_paint_color.cc index bcdccde73a3..59a74c2df02 100644 --- a/source/blender/editors/sculpt_paint/sculpt_paint_color.cc +++ b/source/blender/editors/sculpt_paint/sculpt_paint_color.cc @@ -22,7 +22,7 @@ #include "BKE_colortools.h" #include "BKE_context.h" #include "BKE_paint.h" -#include "BKE_pbvh.h" +#include "BKE_pbvh_api.hh" #include "IMB_colormanagement.h" diff --git a/source/blender/editors/sculpt_paint/sculpt_paint_image.cc b/source/blender/editors/sculpt_paint/sculpt_paint_image.cc index 8f96005b0cd..081c9f3d870 100644 --- a/source/blender/editors/sculpt_paint/sculpt_paint_image.cc +++ b/source/blender/editors/sculpt_paint/sculpt_paint_image.cc @@ -22,7 +22,7 @@ #include "BKE_brush.h" #include "BKE_image_wrappers.hh" -#include "BKE_pbvh.h" +#include "BKE_pbvh_api.hh" #include "BKE_pbvh_pixels.hh" #include "bmesh.h" diff --git a/source/blender/editors/sculpt_paint/sculpt_poly_loop.c b/source/blender/editors/sculpt_paint/sculpt_poly_loop.c index b2f3613e160..f213b0bf22b 100644 --- a/source/blender/editors/sculpt_paint/sculpt_poly_loop.c +++ b/source/blender/editors/sculpt_paint/sculpt_poly_loop.c @@ -47,7 +47,7 @@ #include "BKE_node.h" #include "BKE_object.h" #include "BKE_paint.h" -#include "BKE_pbvh.h" +#include "BKE_pbvh_api.hh" #include "BKE_scene.h" #include "DEG_depsgraph.h" diff --git a/source/blender/editors/sculpt_paint/sculpt_pose.cc b/source/blender/editors/sculpt_paint/sculpt_pose.cc index 37e511214a8..733aac9efe6 100644 --- a/source/blender/editors/sculpt_paint/sculpt_pose.cc +++ b/source/blender/editors/sculpt_paint/sculpt_pose.cc @@ -20,7 +20,7 @@ #include "BKE_colortools.h" #include "BKE_context.h" #include "BKE_paint.h" -#include "BKE_pbvh.h" +#include "BKE_pbvh_api.hh" #include "paint_intern.hh" #include "sculpt_intern.hh" diff --git a/source/blender/editors/sculpt_paint/sculpt_replay.c b/source/blender/editors/sculpt_paint/sculpt_replay.c index e185359ddee..2ef7bda07c6 100644 --- a/source/blender/editors/sculpt_paint/sculpt_replay.c +++ b/source/blender/editors/sculpt_paint/sculpt_replay.c @@ -11,7 +11,7 @@ #include "BKE_main.h" #include "BKE_object.h" #include "BKE_paint.h" -#include "BKE_pbvh.h" +#include "BKE_pbvh_api.hh" #include "BKE_scene.h" #include "BKE_screen.h" @@ -37,7 +37,7 @@ #include "BKE_context.h" #include "BKE_global.h" #include "BKE_paint.h" -#include "BKE_pbvh.h" +#include "BKE_pbvh_api.hh" #include "paint_intern.h" #include "sculpt_intern.h" diff --git a/source/blender/editors/sculpt_paint/sculpt_smooth.cc b/source/blender/editors/sculpt_paint/sculpt_smooth.cc index 8a5208697e2..c653a722ac5 100644 --- a/source/blender/editors/sculpt_paint/sculpt_smooth.cc +++ b/source/blender/editors/sculpt_paint/sculpt_smooth.cc @@ -26,7 +26,7 @@ #include "BKE_mesh.hh" #include "BKE_mesh_mapping.h" #include "BKE_paint.h" -#include "BKE_pbvh.h" +#include "BKE_pbvh_api.hh" #include "sculpt_intern.hh" diff --git a/source/blender/editors/sculpt_paint/sculpt_transform.cc b/source/blender/editors/sculpt_paint/sculpt_transform.cc index 607f5a01e3f..3538d72e916 100644 --- a/source/blender/editors/sculpt_paint/sculpt_transform.cc +++ b/source/blender/editors/sculpt_paint/sculpt_transform.cc @@ -17,7 +17,7 @@ #include "BKE_context.h" #include "BKE_kelvinlet.h" #include "BKE_paint.h" -#include "BKE_pbvh.h" +#include "BKE_pbvh_api.hh" #include "DEG_depsgraph.h" diff --git a/source/blender/editors/space_info/info_stats.cc b/source/blender/editors/space_info/info_stats.cc index 91b03facbcb..2b82ea0345c 100644 --- a/source/blender/editors/space_info/info_stats.cc +++ b/source/blender/editors/space_info/info_stats.cc @@ -48,7 +48,7 @@ #include "BKE_object.h" #include "BKE_paint.h" #include "BKE_particle.h" -#include "BKE_pbvh.h" +#include "BKE_pbvh_api.hh" #include "BKE_scene.h" #include "BKE_subdiv_ccg.h" #include "BKE_subdiv_modifier.h" diff --git a/source/blender/gpu/intern/gpu_shader_builder_stubs.cc b/source/blender/gpu/intern/gpu_shader_builder_stubs.cc index caef85f17b3..711f204896c 100644 --- a/source/blender/gpu/intern/gpu_shader_builder_stubs.cc +++ b/source/blender/gpu/intern/gpu_shader_builder_stubs.cc @@ -20,7 +20,7 @@ #include "BKE_mesh.hh" #include "BKE_node.hh" #include "BKE_paint.h" -#include "BKE_pbvh.h" +#include "BKE_pbvh_api.hh" #include "BKE_subdiv_ccg.h" #include "DNA_userdef_types.h" diff --git a/source/blender/makesrna/intern/rna_sculpt_paint.c b/source/blender/makesrna/intern/rna_sculpt_paint.c index 18e5cfc344f..3323f51b57b 100644 --- a/source/blender/makesrna/intern/rna_sculpt_paint.c +++ b/source/blender/makesrna/intern/rna_sculpt_paint.c @@ -113,7 +113,6 @@ const EnumPropertyItem rna_enum_symmetrize_direction_items[] = { # include "BKE_gpencil_legacy.h" # include "BKE_object.h" # include "BKE_particle.h" -# include "BKE_pbvh.h" # include "BKE_pointcache.h" # include "DEG_depsgraph.h" diff --git a/source/blender/makesrna/intern/rna_userdef.c b/source/blender/makesrna/intern/rna_userdef.c index dae2d97a349..56805cf6c48 100644 --- a/source/blender/makesrna/intern/rna_userdef.c +++ b/source/blender/makesrna/intern/rna_userdef.c @@ -168,7 +168,6 @@ static const EnumPropertyItem rna_enum_preference_gpu_backend_items[] = { # include "BKE_mesh_runtime.h" # include "BKE_object.h" # include "BKE_paint.h" -# include "BKE_pbvh.h" # include "BKE_preferences.h" # include "BKE_screen.h" @@ -553,7 +552,8 @@ static const EnumPropertyItem *rna_UseDef_active_section_itemf(bContext *UNUSED( int totitem = 0; for (const EnumPropertyItem *it = rna_enum_preference_section_items; it->identifier != NULL; - it++) { + it++) + { if (it->value == USER_SECTION_EXPERIMENTAL) { continue; } diff --git a/source/blender/windowmanager/intern/wm_draw.c b/source/blender/windowmanager/intern/wm_draw.c index e26552fdbda..d835b9dfd59 100644 --- a/source/blender/windowmanager/intern/wm_draw.c +++ b/source/blender/windowmanager/intern/wm_draw.c @@ -29,7 +29,6 @@ #include "BKE_image.h" #include "BKE_main.h" #include "BKE_paint.h" -#include "BKE_pbvh.h" #include "BKE_scene.h" #include "BKE_screen.h" -- 2.30.2 From 99f79a6c5ec0f61d36b7d9dadb4f16a5e07ca82d Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Tue, 4 Jul 2023 01:22:28 -0700 Subject: [PATCH 223/279] temp-sculpt-dyntopo: Fix nasty memory leak --- build_files/cmake/macros.cmake | 2 +- source/blender/blenkernel/BKE_pbvh_api.hh | 7 +++---- source/blender/blenkernel/intern/pbvh_bmesh.cc | 6 ------ source/blender/editors/sculpt_paint/sculpt.cc | 15 --------------- .../blender/editors/sculpt_paint/sculpt_undo.cc | 2 +- source/blender/imbuf/intern/thumbs.cc | 1 + source/blender/makesrna/intern/rna_brush.cc | 2 +- source/blender/makesrna/intern/rna_wm_api.cc | 2 +- 8 files changed, 8 insertions(+), 29 deletions(-) diff --git a/build_files/cmake/macros.cmake b/build_files/cmake/macros.cmake index d717884d259..d24d9d0a17e 100644 --- a/build_files/cmake/macros.cmake +++ b/build_files/cmake/macros.cmake @@ -348,7 +348,7 @@ function(blender_link_libraries # NOT: "optimized libfoo libbar debug libfoo_d libbar_d" if(NOT "${library_deps}" STREQUAL "") set(next_library_mode "") - set(next_interface_mode "INTERFACE") + set(next_interface_mode "PRIVATE") foreach(library ${library_deps}) string(TOLOWER "${library}" library_lower) if(("${library_lower}" STREQUAL "optimized") OR diff --git a/source/blender/blenkernel/BKE_pbvh_api.hh b/source/blender/blenkernel/BKE_pbvh_api.hh index 513757858b6..058ca736151 100644 --- a/source/blender/blenkernel/BKE_pbvh_api.hh +++ b/source/blender/blenkernel/BKE_pbvh_api.hh @@ -30,7 +30,6 @@ #include struct BMesh; -struct BMLog; struct BMVert; struct BMEdge; struct BMFace; @@ -54,9 +53,9 @@ struct Image; struct ImageUser; struct PBVHTri { - int v[3]; // references into PBVHTriBuf->verts - int eflag; // bitmask of which edges in the tri are real edges in the mesh - intptr_t l[3]; // loops + int v[3]; /* References into PBVHTriBuf->verts. */ + int eflag; /* Bitmask of which edges in the tri are real edges in the mesh. */ + intptr_t l[3]; /* Loops, currently just BMLoop pointers for now. */ PBVHFaceRef f; float no[3]; }; diff --git a/source/blender/blenkernel/intern/pbvh_bmesh.cc b/source/blender/blenkernel/intern/pbvh_bmesh.cc index e8e4dbac3be..2daa8c65e19 100644 --- a/source/blender/blenkernel/intern/pbvh_bmesh.cc +++ b/source/blender/blenkernel/intern/pbvh_bmesh.cc @@ -2787,8 +2787,6 @@ ATTR_NO_OPT bool BKE_pbvh_bmesh_check_tris(PBVH *pbvh, PBVHNode *node) Vector loops_idx; Vector *tribufs = MEM_new>("PBVHTriBuf tribufs"); - tribufs->reserve(MAXMAT); - node->flag &= ~PBVH_UpdateTris; const int edgeflag = BM_ELEM_TAG_ALT; @@ -2934,18 +2932,14 @@ ATTR_NO_OPT bool BKE_pbvh_bmesh_check_tris(PBVH *pbvh, PBVHNode *node) } using Type = std::remove_reference_t; - #if 1 size_t count = vector.size(); - printf("c1: %d\n", int(vector.capacity())); - Vector cpy; cpy.reserve(count); cpy.extend_unchecked(vector.data(), count); vector = std::move(cpy); - printf("c2: %d\n", int(vector.capacity())); #else size_t count = vector.size(); diff --git a/source/blender/editors/sculpt_paint/sculpt.cc b/source/blender/editors/sculpt_paint/sculpt.cc index 45206761642..e3893fa2e49 100644 --- a/source/blender/editors/sculpt_paint/sculpt.cc +++ b/source/blender/editors/sculpt_paint/sculpt.cc @@ -6897,21 +6897,6 @@ void SCULPT_ensure_epmap(SculptSession *ss) ss->totedges, ss->edge_to_poly_offsets, ss->edge_to_poly_indices); -======= - Mesh *base_mesh = BKE_mesh_from_object(object); - - ss->vertex_info.boundary = BLI_BITMAP_NEW(base_mesh->totvert, "Boundary info"); - Array adjacent_faces_edge_count(base_mesh->totedge, 0); - array_utils::count_indices(base_mesh->corner_edges(), adjacent_faces_edge_count); - - const blender::Span edges = base_mesh->edges(); - for (const int e : edges.index_range()) { - if (adjacent_faces_edge_count[e] < 2) { - const int2 &edge = edges[e]; - BLI_BITMAP_SET(ss->vertex_info.boundary, edge[0], true); - BLI_BITMAP_SET(ss->vertex_info.boundary, edge[1], true); - } ->>>>>>> main } } diff --git a/source/blender/editors/sculpt_paint/sculpt_undo.cc b/source/blender/editors/sculpt_paint/sculpt_undo.cc index 6c36872aadf..0eb59b2dc7d 100644 --- a/source/blender/editors/sculpt_paint/sculpt_undo.cc +++ b/source/blender/editors/sculpt_paint/sculpt_undo.cc @@ -137,7 +137,7 @@ struct UndoSculpt { size_t undo_size; BMLog *bm_restore; -} UndoSculpt; +}; struct SculptAttrRef { eAttrDomain domain; diff --git a/source/blender/imbuf/intern/thumbs.cc b/source/blender/imbuf/intern/thumbs.cc index 5086b22ad11..d33e6ce22ca 100644 --- a/source/blender/imbuf/intern/thumbs.cc +++ b/source/blender/imbuf/intern/thumbs.cc @@ -20,6 +20,7 @@ #include "BLI_string.h" #include "BLI_system.h" #include "BLI_threads.h" +#include "BLI_string_utils.h" #include "BLI_utildefines.h" #include BLI_SYSTEM_PID_H diff --git a/source/blender/makesrna/intern/rna_brush.cc b/source/blender/makesrna/intern/rna_brush.cc index 955ba478ce0..ef5c1f0a5d6 100644 --- a/source/blender/makesrna/intern/rna_brush.cc +++ b/source/blender/makesrna/intern/rna_brush.cc @@ -807,7 +807,7 @@ static void rna_Brush_update(Main * /*bmain*/, Scene * /*scene*/, PointerRNA *pt // WM_main_add_notifier(NC_SPACE | ND_SPACE_VIEW3D, nullptr); } -static void rna_Brush_dyntopo_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr) {} +static void rna_Brush_dyntopo_update(Main * /*bmain*/, Scene * /*scene*/, PointerRNA *ptr) {} static void rna_Brush_material_update(bContext * /*C*/, PointerRNA * /*ptr*/) { diff --git a/source/blender/makesrna/intern/rna_wm_api.cc b/source/blender/makesrna/intern/rna_wm_api.cc index 2bfe6b52307..47e75da677b 100644 --- a/source/blender/makesrna/intern/rna_wm_api.cc +++ b/source/blender/makesrna/intern/rna_wm_api.cc @@ -62,7 +62,7 @@ const EnumPropertyItem rna_enum_window_cursor_items[] = { #ifdef RNA_RUNTIME -void wm_autosave_write(Main *bmain, wmWindowManager *wm); +extern "C" void wm_autosave_write(Main *bmain, wmWindowManager *wm); # include "BKE_context.h" # include "BKE_undo_system.h" -- 2.30.2 From 1378a2910358e875d93eeedc2d39809f27e37bd9 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Sat, 8 Jul 2023 22:04:21 -0700 Subject: [PATCH 224/279] temp-sculpt-dyntopo: Make attribute undistortion more user-configurable Also fixed a few bugs --- scripts/startup/bl_ui/space_view3d_toolbar.py | 2 +- source/blender/blenkernel/BKE_dyntopo_set.hh | 8 + source/blender/blenkernel/BKE_paint.h | 4 +- source/blender/blenkernel/BKE_pbvh_api.hh | 3 +- source/blender/blenkernel/BKE_sculpt.hh | 25 +- source/blender/blenkernel/intern/dyntopo.cc | 289 +++++++++++++----- .../blenkernel/intern/dyntopo_intern.hh | 2 +- source/blender/blenkernel/intern/paint.cc | 169 +++++----- source/blender/blenkernel/intern/pbvh.cc | 14 +- .../blender/blenkernel/intern/pbvh_bmesh.cc | 23 +- .../blender/blenkernel/intern/pbvh_intern.hh | 1 + .../blenloader/intern/versioning_400.cc | 13 + source/blender/bmesh/intern/bmesh_log.cc | 5 + .../blender/bmesh/intern/bmesh_log_intern.h | 2 + source/blender/editors/sculpt_paint/sculpt.cc | 16 +- .../sculpt_paint/sculpt_brush_types.cc | 15 +- .../editors/sculpt_paint/sculpt_detail.cc | 18 ++ .../editors/sculpt_paint/sculpt_intern.hh | 12 +- .../editors/sculpt_paint/sculpt_smooth.cc | 24 +- source/blender/makesdna/DNA_brush_enums.h | 9 + source/blender/makesdna/DNA_mesh_types.h | 4 +- source/blender/makesdna/DNA_scene_defaults.h | 2 + source/blender/makesdna/DNA_scene_types.h | 6 +- source/blender/makesrna/intern/rna_mesh.cc | 4 - source/blender/makesrna/intern/rna_scene.cc | 34 ++- 25 files changed, 470 insertions(+), 234 deletions(-) diff --git a/scripts/startup/bl_ui/space_view3d_toolbar.py b/scripts/startup/bl_ui/space_view3d_toolbar.py index e24b86fb3bd..be6f1b45af5 100644 --- a/scripts/startup/bl_ui/space_view3d_toolbar.py +++ b/scripts/startup/bl_ui/space_view3d_toolbar.py @@ -1060,7 +1060,7 @@ class VIEW3D_PT_sculpt_options(Panel, View3DPaintPanel): if bpy.app.debug_value == 889: col.prop(tool_settings, "show_origco") - col.prop(context.object.data, "sculpt_ignore_uvs") + col.prop(ups, "distort_correction_mode") col.label(text="Smooth Boundaries") col = layout.column(align=True) diff --git a/source/blender/blenkernel/BKE_dyntopo_set.hh b/source/blender/blenkernel/BKE_dyntopo_set.hh index d57430ea9ba..60d536e28fa 100644 --- a/source/blender/blenkernel/BKE_dyntopo_set.hh +++ b/source/blender/blenkernel/BKE_dyntopo_set.hh @@ -32,6 +32,14 @@ template class DyntopoSet { iterator(DyntopoSet *set, int i) : set_(set), i_(i) {} iterator(const iterator &b) : set_(b.set_), i_(b.i_) {} + iterator &operator=(const iterator &b) + { + set_ = b.set_; + i_ = b.i_; + + return *this; + } + inline T *operator*() { return set_->index_to_elem_[i_]; diff --git a/source/blender/blenkernel/BKE_paint.h b/source/blender/blenkernel/BKE_paint.h index 014f9bdeee7..9b8526752dc 100644 --- a/source/blender/blenkernel/BKE_paint.h +++ b/source/blender/blenkernel/BKE_paint.h @@ -739,7 +739,7 @@ struct SculptSession { int totuv; /* Reproject customdata during smooth. */ - bool reproject_smooth; + eAttrCorrectMode distort_correction_mode; /* Undo/redo log for dynamic topology sculpting */ BMLog *bm_log; @@ -976,7 +976,7 @@ void BKE_sculptsession_update_attr_refs(struct Object *ob); int BKE_sculptsession_get_totvert(const struct SculptSession *ss); -void BKE_sculptsession_reproject_smooth_set(struct Object *ob, bool value); +void BKE_sculpt_distort_correction_set(struct Object *ob, eAttrCorrectMode value); void BKE_sculptsession_free_attribute_refs(struct Object *ob); /** diff --git a/source/blender/blenkernel/BKE_pbvh_api.hh b/source/blender/blenkernel/BKE_pbvh_api.hh index 058ca736151..6fb7283e5d6 100644 --- a/source/blender/blenkernel/BKE_pbvh_api.hh +++ b/source/blender/blenkernel/BKE_pbvh_api.hh @@ -18,6 +18,7 @@ #include "BLI_vector.hh" #include "DNA_customdata_types.h" +#include "DNA_brush_enums.h" /* for eAttrCorrectMode */ /* For embedding CCGKey in iterator. */ #include "BKE_attribute.h" @@ -865,7 +866,7 @@ void BKE_pbvh_pmap_to_edges(PBVH *pbvh, bool *heap_alloc, int **r_polys); -void BKE_pbvh_reproject_smooth_set(PBVH *pbvh, bool value); +void BKE_pbvh_distort_correction_set(PBVH *pbvh, eAttrCorrectMode value); void BKE_pbvh_set_face_areas(PBVH *pbvh, float *face_areas); void BKE_pbvh_set_bmesh(PBVH *pbvh, BMesh *bm); diff --git a/source/blender/blenkernel/BKE_sculpt.hh b/source/blender/blenkernel/BKE_sculpt.hh index f195e43272b..76c0df2401a 100644 --- a/source/blender/blenkernel/BKE_sculpt.hh +++ b/source/blender/blenkernel/BKE_sculpt.hh @@ -12,6 +12,7 @@ #include "BKE_pbvh.h" #include "BLI_compiler_compat.h" +#include "BLI_math_vector.hh" #include "BLI_math_vector_types.hh" #include "BLI_span.hh" #include "BLI_vector.hh" @@ -43,8 +44,11 @@ enum StrokeIDUser { }; ENUM_OPERATORS(StrokeIDUser, STROKEID_USER_LAYER_BRUSH); -void BKE_sculpt_reproject_cdata( - SculptSession *ss, PBVHVertRef vertex, float startco[3], float startno[3], bool do_uvs = true); +void BKE_sculpt_reproject_cdata(SculptSession *ss, + PBVHVertRef vertex, + float startco[3], + float startno[3], + eAttrCorrectMode undistort_mode); namespace blender::bke::sculpt { void sculpt_vert_boundary_ensure(Object *ob); @@ -121,7 +125,8 @@ static bool prop_eq(float a, float b, float limit) } static bool prop_eq(float2 a, float2 b, float limit) { - return (a[0] - b[0]) * (a[0] - b[0]) + (a[1] - b[1]) * (a[1] - b[1]) <= limit * limit; + return blender::math::distance_squared(a, b) <= limit * limit; + // return (a[0] - b[0]) * (a[0] - b[0]) + (a[1] - b[1]) * (a[1] - b[1]) <= limit * limit; } static bool prop_eq(float3 a, float3 b, float limit) { @@ -162,6 +167,10 @@ struct VertLoopSnapper { VertLoopSnapper(Span ls_, Span layers_) : layers(layers_), ls(ls_) { + if (ls.size() == 0) { + return; + } + snap_sets.resize(ls.size()); for (auto &snap_set : snap_sets) { for (int i = 0; i < layers.size(); i++) { @@ -213,19 +222,19 @@ struct VertLoopSnapper { CustomDataLayer *layer = layers[layer_i]; int idx_base = 1; - float limit = 0.001; + limit = 0.001f; if constexpr (std::is_same_v) { /* Set UV snap limit as 1/10th the average uv edge length. */ - limit = 0.1; - float len = 0.0; + limit = 0.1f; + float len = 0.0f; for (BMLoop *l : ls) { T value1 = *BM_ELEM_CD_PTR(l, layer->offset); T value2 = *BM_ELEM_CD_PTR(l->next, layer->offset); - len += fabsf(value1[0] - value2[0]) * 0.5; - len += fabsf(value1[1] - value2[1]) * 0.5; + len += fabsf(value1[0] - value2[0]) * 0.5f; + len += fabsf(value1[1] - value2[1]) * 0.5f; } len /= ls.size(); diff --git a/source/blender/blenkernel/intern/dyntopo.cc b/source/blender/blenkernel/intern/dyntopo.cc index a15c0ca6dfa..90ac314b898 100644 --- a/source/blender/blenkernel/intern/dyntopo.cc +++ b/source/blender/blenkernel/intern/dyntopo.cc @@ -71,7 +71,7 @@ static void edge_queue_create_local(EdgeQueueContext *eq_ctx, PBVHTopologyUpdateMode local_mode); static void surface_smooth_v_safe( - SculptSession *ss, PBVH *pbvh, BMVert *v, float fac, bool reproject_cdata) + SculptSession *ss, PBVH *pbvh, BMVert *v, float fac, eAttrCorrectMode distort_correction_mode) { float co[3]; float origco[3], origco1[3]; @@ -200,7 +200,7 @@ static void surface_smooth_v_safe( float startco[3]; float startno[3]; - if (reproject_cdata) { + if (distort_correction_mode) { copy_v3_v3(startco, v->co); copy_v3_v3(startno, v->no); } @@ -224,10 +224,13 @@ static void surface_smooth_v_safe( * the weights we built earlier. */ /* Reproject attributes. */ - if (reproject_cdata) { - BKE_sculpt_reproject_cdata(ss, vertex, startco, startno, false); - blender::bke::sculpt::interp_face_corners( - pbvh, vertex, loops, ws, fac, pbvh->cd_boundary_flag); + if (distort_correction_mode) { + BKE_sculpt_reproject_cdata(ss, vertex, startco, startno, ss->distort_correction_mode); + + if (distort_correction_mode & UNDISTORT_RELAX_UVS) { + blender::bke::sculpt::interp_face_corners( + pbvh, vertex, loops, ws, fac, pbvh->cd_boundary_flag); + } } PBVH_CHECK_NAN(v->co); @@ -283,9 +286,10 @@ BLI_INLINE float calc_weighted_length(EdgeQueueContext *eq_ctx, float len = len_squared_v3v3(v1->co, v2->co); switch (mode) { - case SPLIT: + case SPLIT: { w = 1.0 + w * float(mode); return len > eq_ctx->limit_len_max_sqr ? len * w * w : len; + } case COLLAPSE: { #if 0 if (eq_ctx->brush_tester->is_sphere_or_tube) { @@ -333,7 +337,7 @@ static PBVHTopologyUpdateMode edge_queue_test(EdgeQueueContext *eq_ctx, void EdgeQueueContext::surface_smooth(BMVert *v, float fac) { - surface_smooth_v_safe(ss, pbvh, v, fac, reproject_cdata); + surface_smooth_v_safe(ss, pbvh, v, fac, distort_correction_mode); } void EdgeQueueContext::insert_edge(BMEdge *e, float w) @@ -703,7 +707,7 @@ static void unified_edge_queue_task_cb(void *__restrict userdata, const char facetag = BM_ELEM_TAG_ALT; /* Only do reprojection if UVs exist. */ - bool reproject_cdata = eq_ctx->reproject_cdata; + eAttrCorrectMode distort_correction_mode = eq_ctx->distort_correction_mode; /* * Clear edge flags. @@ -787,7 +791,7 @@ static void unified_edge_queue_task_cb(void *__restrict userdata, l_iter->v, eq_ctx->surface_smooth_fac * eq_ctx->mask_cb(sv, eq_ctx->mask_cb_data), - reproject_cdata); + distort_correction_mode); } float w = 0.0f; @@ -1276,8 +1280,6 @@ static void unified_edge_queue_create(EdgeQueueContext *eq_ctx, Vector tdata; - bool push_subentry = false; - for (int n = 0; n < pbvh->totnode; n++) { PBVHNode *node = &pbvh->nodes[n]; @@ -1344,6 +1346,8 @@ static void unified_edge_queue_create(EdgeQueueContext *eq_ctx, } } + // bool push_subentry = false; + Vector verts; for (int i = 0; i < count; i++) { for (BMEdge *e : tdata[i].edges) { @@ -1355,8 +1359,9 @@ static void unified_edge_queue_create(EdgeQueueContext *eq_ctx, if (e->l && e->l != e->l->radial_next->radial_next) { /* Fix non-manifold "fins". */ - destroy_nonmanifold_fins(pbvh, e); - push_subentry = true; + if (destroy_nonmanifold_fins(pbvh, e)) { + // push_subentry = true; + } if (bm_elem_is_free((BMElem *)e, BM_EDGE)) { continue; @@ -1710,8 +1715,6 @@ static bool cleanup_valence_3_4(EdgeQueueContext *ectx, PBVH *pbvh) */ BMEdge *e = v->e; do { - BMLoop *l2 = e->l; - /* Double check edge boundary flags, in addition to the vertex boundary flag test above. */ pbvh_check_edge_boundary_bmesh(pbvh, e); if (BM_ELEM_CD_GET_INT(e, ectx->pbvh->cd_edge_boundary) & SCULPTVERT_ALL_BOUNDARY) { @@ -2060,7 +2063,7 @@ EdgeQueueContext::EdgeQueueContext(BrushTester *brush_tester_, mode = mode_; surface_relax = true; - reproject_cdata = ss->reproject_smooth; + distort_correction_mode = ss->distort_correction_mode; limit_len_min = pbvh->bm_min_edge_len; limit_len_max = pbvh->bm_max_edge_len; @@ -2135,7 +2138,7 @@ void EdgeQueueContext::start() bool EdgeQueueContext::done() { if (edge_heap.empty() || - edge_heap.min_weight() > limit_len_min_sqr && edge_heap.max_weight() < limit_len_max_sqr) + (edge_heap.min_weight() > limit_len_min_sqr && edge_heap.max_weight() < limit_len_max_sqr)) { return true; } @@ -2247,7 +2250,7 @@ void EdgeQueueContext::step() v, surface_smooth_fac * mask_cb({reinterpret_cast(v)}, mask_cb_data), - reproject_cdata); + distort_correction_mode); } }; @@ -2538,9 +2541,6 @@ void EdgeQueueContext::split_edge(BMEdge *e) pbvh_boundary_update_bmesh(pbvh, e->v2); pbvh_boundary_update_bmesh(pbvh, e); - int bf1 = BM_ELEM_CD_GET_INT(e->v1, pbvh->cd_boundary_flag); - int bf2 = BM_ELEM_CD_GET_INT(e->v2, pbvh->cd_boundary_flag); - bool uv_boundary = BM_ELEM_CD_GET_INT(e, pbvh->cd_edge_boundary) & SCULPT_BOUNDARY_UV; bool sharp_angle_boundary = BM_ELEM_CD_GET_INT(e, pbvh->cd_edge_boundary) & SCULPT_BOUNDARY_SHARP_ANGLE; @@ -2708,6 +2708,9 @@ template constexpr T get_epsilon() else if constexpr (std::is_same_v) { return DBL_EPSILON; } + else if constexpr (std::is_same_v) { + return DBL_EPSILON; + } else { return T::EPSILON(); } @@ -2766,24 +2769,27 @@ template void cross_v3_v3v3(T r[3], const T a[3], const T b[ r[2] = a[0] * b[1] - a[1] * b[0]; } -template T len_squared_v2v2(const T *a, const T *b) +#define VEC_TEMPLATE(n) \ + template> + +VEC_TEMPLATE(2) Float len_squared_v2v2(const Vec a, const Vec b) { - T dx = a[0] - b[0]; - T dy = a[1] - b[1]; + Float dx = a[0] - b[0]; + Float dy = a[1] - b[1]; return dx * dx + dy * dy; } -template T dot_v2v2(const T *a, const T *b) +VEC_TEMPLATE(2) Float dot_v2v2(const Vec &a, const Vec &b) { return a[0] * b[0] + a[1] * b[1]; } -template T dot_v3v3(const T *a, const T *b) +VEC_TEMPLATE(3) Float dot_v3v3(const Vec &a, const Vec &b) { return a[0] * b[0] + a[1] * b[1] + a[2] * b[2]; } -template T len_squared_v2(const T *a) +VEC_TEMPLATE(2) Float len_squared_v2(const Vec &a) { return dot_v2v2(a, a); } @@ -2793,7 +2799,7 @@ template T len_v2(const T *a) return std::sqrt(len_squared_v2(a)); } -template T len_v2v2(const T *a, const T *b) +VEC_TEMPLATE(2) Float len_v2v2(const Vec a, const Vec b) { return std::sqrt(len_squared_v2v2(a, b)); } @@ -2804,6 +2810,42 @@ template void sub_v2_v2v2(T *r, const T *a, const T *b) r[1] = a[1] - b[1]; } +template void mul_v2_fl(T *r, T f) +{ + r[0] *= f; + r[1] *= f; +} + +template void negate_v2(T *r) +{ + r[0] = -r[0]; + r[1] = -r[1]; +} + +VEC_TEMPLATE(2) Float normalize_v2(Vec r) +{ + Float len = dot_v2v2(r, r); + if (len >= get_epsilon()) { + r[0] /= len; + r[1] /= len; + } + + return len; +} + +template T normalize_v3(T *r) +{ + T len = dot_v3v3(r, r); + + if (len >= get_epsilon()) { + r[0] /= len; + r[1] /= len; + r[2] /= len; + } + + return len; +} + /* Mean value weights - smooth interpolation weights for polygons with * more than 3 vertices */ template @@ -2878,7 +2920,7 @@ T dist_squared_to_line_segment_v2(const T *p, const T *v1, const T *v2) T dx2 = v2[0] - v1[0]; T dy2 = v2[1] - v1[1]; - T len_sqr = len_squared_v2v2(v1, v2); + T len_sqr = len_squared_v2v2(v1, v2); T len = std::sqrt(len_sqr); bool good; @@ -2892,7 +2934,7 @@ T dist_squared_to_line_segment_v2(const T *p, const T *v1, const T *v2) dy2 /= len; } else { - return len_squared_v2v2(p, v1); + return len_squared_v2v2(p, v1); } T fac = dx1 * dx2 + dy1 * dy2; @@ -2904,7 +2946,7 @@ T dist_squared_to_line_segment_v2(const T *p, const T *v1, const T *v2) } if (test) { - return len_squared_v2v2(p, v1); + return len_squared_v2v2(p, v1); } else { { @@ -2912,7 +2954,7 @@ T dist_squared_to_line_segment_v2(const T *p, const T *v1, const T *v2) test = fac >= len - get_epsilon() * 32.0; } if (test) { - return len_squared_v2v2(p, v2); + return len_squared_v2v2(p, v2); } } @@ -3344,7 +3386,7 @@ template bool isfinite(myinterp::TestFloat f) } } // namespace std -/* reduce symbolic algebra script +/* reduce algebra script on factor; off period; @@ -3362,9 +3404,107 @@ fw1 := part(ff, 1, 1, 2); fw2 := part(ff, 1, 2, 2); fw3 := part(ff, 1, 3, 2); off fort; + */ namespace myinterp { + +/* Distance is from the origin. */ +VEC_TEMPLATE(2) Float dist_to_line(Vec a, Vec b) +{ + Vec t = b - a; + a = -a; + + normalize_v2(t); + return t[0] * a[1] - t[1] * a[0]; +} + +VEC_TEMPLATE(2) Float line_t(Vec a, Vec b) +{ + Vec t = b - a; + a = -a; + + normalize_v2(t); + return dot_v2v2(a, t); +} + +template void tri_weights_v3_new(T *_p, T *_a, T *_b, T *_c, T *r_ws) +{ + using Vector = blender::VecBase; + + Vector p(_p); + Vector a(_a); + Vector b(_b); + Vector c(_c); + + a -= p; + b -= p; + c -= p; + + T ax = a[0], ay = a[1]; + T bx = b[0], by = b[1]; + T cx = c[0], cy = c[1]; + +#if 0 + // T cent[2] = {(a[0] + b[0] + c[0]) / 3.0f, (a[1] + b[1] + c[1]) / 3.0f}; + T div0 = len_v2v2(a, b); + + if (div0 > get_epsilon() * 1000) { + ax /= div0; + bx /= div0; + cx /= div0; + ay /= div0; + by /= div0; + cy /= div0; + } +#endif + + T div = (bx * cy - by * cx + (by - cy) * ax - (bx - cx) * ay); + float l = min_ff(min_ff(len_squared_v2v2(a, b), len_squared_v2v2(b, c)), len_squared_v2v2(c, a)); + + if (abs(div) < l * 0.1) { + /* Get distance to edges. */ + float l1 = dist_to_line(a, b); + float l2 = dist_to_line(b, c); + float l3 = dist_to_line(c, a); + float t; + + if (l1 > l2 && l1 > l3) { + t = line_t(a, b); + r_ws[0] = 1.0 - t; + r_ws[1] = t; + } + else if (l2 > l1 && l2 > l3) { + t = line_t(b, c); + r_ws[1] = 1.0 - t; + r_ws[2] = t; + } + else { + t = line_t(c, a); + r_ws[2] = 1.0 - t; + r_ws[0] = t; + } + + return; + } + + if (std::fabs(div) < get_epsilon() * 10000) { + r_ws[0] = r_ws[1] = r_ws[2] = 1.0 / 3.0; + return; + } + + r_ws[0] = (bx * cy - by * cx) / div; + r_ws[1] = (-ax * cy + ay * cx) / div; + r_ws[2] = (ax * by - ay * bx) / div; + +#if 0 + T px = a[0] * r_ws[0] + b[0] * r_ws[1] + c[0] * r_ws[2]; + T py = a[1] * r_ws[0] + b[1] * r_ws[1] + c[1] * r_ws[2]; + + printf("%.6f %.6f\n", float(px - p[0]), float(py - p[1])); +#endif +} + template void tri_weights_v3(T *p, const T *a, const T *b, const T *c, T *r_ws) { T ax = (a[0] - p[0]), ay = (a[1] - p[1]); @@ -3409,6 +3549,7 @@ template void tri_weights_v3(T *p, const T *a, const T *b, c printf("%.6f %.6f\n", float(px - p[0]), float(py - p[1])); #endif } + } // namespace myinterp template @@ -3492,22 +3633,31 @@ void reproject_interp_data(CustomData *data, } template // myinterp::TestFloat> -static bool reproject_bm_data( - BMesh *bm, BMLoop *l_dst, const BMFace *f_src, const bool do_vertex, eCustomDataMask typemask) +static bool reproject_bm_data(BMesh *bm, + BMLoop *l_dst, + const BMFace *f_src, + eAttrDomainMask domain_mask, + eCustomDataMask typemask) { using namespace myinterp; BMLoop *l_iter; BMLoop *l_first; - const void **vblocks = do_vertex ? + const void **vblocks = (domain_mask & ATTR_DOMAIN_MASK_POINT) ? static_cast(BLI_array_alloca(vblocks, f_src->len)) : nullptr; - const void **blocks = static_cast(BLI_array_alloca(blocks, f_src->len)); + const void **blocks = (domain_mask & ATTR_DOMAIN_MASK_CORNER) ? + static_cast(BLI_array_alloca(blocks, f_src->len)) : + nullptr; T(*cos_2d)[2] = static_cast(BLI_array_alloca(cos_2d, f_src->len)); T *w = static_cast(BLI_array_alloca(w, f_src->len)); float axis_mat[3][3]; /* use normal to transform into 2d xy coords */ float co[2]; + if (domain_mask == eAttrDomainMask(0)) { + return false; + } + /* Convert the 3d coords into 2d for projection. */ float axis_dominant[3]; if (!is_zero_v3(f_src->no)) { @@ -3531,21 +3681,22 @@ static bool reproject_bm_data( mul_v2_m3v3(co2d, axis_mat, l_iter->v->co); myinterp::copy_v2_v2(cos_2d[l_i], co2d); - blocks[l_i] = l_iter->head.data; + if (domain_mask & ATTR_DOMAIN_MASK_CORNER) { + blocks[l_i] = l_iter->head.data; + } float len_sq = len_squared_v3v3(l_iter->v->co, l_iter->next->v->co); if (len_sq < FLT_EPSILON * 100.0f) { return false; } - if (do_vertex) { + if (domain_mask & ATTR_DOMAIN_MASK_POINT) { vblocks[l_i] = l_iter->v->head.data; } } while ((void)l_i++, (l_iter = l_iter->next) != l_first); mul_v2_m3v3(co, axis_mat, l_dst->v->co); - return false; T tco[2]; myinterp::copy_v2_v2(tco, co); @@ -3563,7 +3714,7 @@ static bool reproject_bm_data( /* interpolate */ if (f_src->len == 3) { - myinterp::tri_weights_v3(tco, cos_2d[0], cos_2d[1], cos_2d[2], w); + myinterp::tri_weights_v3_new(tco, cos_2d[0], cos_2d[1], cos_2d[2], w); T sum = 0.0; for (int i = 0; i < 3; i++) { @@ -3608,9 +3759,11 @@ static bool reproject_bm_data( fw = w; } - reproject_interp_data(&bm->ldata, blocks, fw, nullptr, f_src->len, l_dst->head.data, typemask); + if (domain_mask & ATTR_DOMAIN_MASK_CORNER) { + reproject_interp_data(&bm->ldata, blocks, fw, nullptr, f_src->len, l_dst->head.data, typemask); + } - if (do_vertex) { + if (domain_mask & ATTR_DOMAIN_MASK_POINT) { // bool inside = isect_point_poly_v2(co, cos_2d, l_dst->f->len, false); reproject_interp_data( &bm->vdata, vblocks, fw, nullptr, f_src->len, l_dst->v->head.data, typemask); @@ -3619,8 +3772,11 @@ static bool reproject_bm_data( return true; } -void BKE_sculpt_reproject_cdata( - SculptSession *ss, PBVHVertRef vertex, float startco[3], float startno[3], bool do_uvs) +ATTR_NO_OPT void BKE_sculpt_reproject_cdata(SculptSession *ss, + PBVHVertRef vertex, + float startco[3], + float startno[3], + eAttrCorrectMode undistort_mode) { int boundary_flag = blender::bke::paint::vertex_attr_get(vertex, ss->attrs.boundary_flags); if (boundary_flag & (SCULPT_BOUNDARY_UV)) { @@ -3636,21 +3792,6 @@ void BKE_sculpt_reproject_cdata( CustomData *ldata = &ss->bm->ldata; - int totuv = 0; - CustomDataLayer *uvlayer = nullptr; - - /* Optimized substitute for CustomData_number_of_layers. */ - if (ldata->typemap[CD_PROP_FLOAT2] != -1) { - for (int i = ldata->typemap[CD_PROP_FLOAT2]; - i < ldata->totlayer && ldata->layers[i].type == CD_PROP_FLOAT2; - i++) - { - totuv++; - } - - uvlayer = ldata->layers + ldata->typemap[CD_PROP_FLOAT2]; - } - int tag = BM_ELEM_TAG_ALT; e = v->e; @@ -3725,7 +3866,7 @@ void BKE_sculpt_reproject_cdata( eCustomDataMask typemask = CD_MASK_PROP_FLOAT | CD_MASK_PROP_FLOAT2 | CD_MASK_PROP_FLOAT3 | CD_MASK_PROP_COLOR | CD_PROP_BYTE_COLOR; - if (!do_uvs) { + if (undistort_mode & UNDISTORT_RELAX_UVS) { typemask &= ~CD_MASK_PROP_FLOAT2; } @@ -3776,6 +3917,14 @@ void BKE_sculpt_reproject_cdata( Vector loops; Vector layers; eCustomDataMask snap_typemask = CD_MASK_PROP_FLOAT2; + eAttrDomainMask domain_mask = eAttrDomainMask(0); + if (undistort_mode & UNDISTORT_REPROJECT_VERTS) { + domain_mask |= ATTR_DOMAIN_MASK_POINT; + } + + if (undistort_mode & UNDISTORT_REPROJECT_CORNERS) { + domain_mask |= ATTR_DOMAIN_MASK_CORNER; + } for (int i = 0; i < ldata->totlayer; i++) { CustomDataLayer *layer = ldata->layers + i; @@ -3876,7 +4025,7 @@ void BKE_sculpt_reproject_cdata( interpl->v = &_v; #ifdef WITH_ASAN - /* Can't unpoison memory in threaded code. */ + /* Can't us memcpy due to poisoned memory pad bytes.*/ CustomData_bmesh_copy_data(&ss->bm->ldata, &ss->bm->ldata, l->head.data, &blocks[i]); #else memcpy(blocks[i], l->head.data, ss->bm->ldata.totsize); @@ -3892,28 +4041,30 @@ void BKE_sculpt_reproject_cdata( void *vblock = vblocks[cur_vblock]; #ifdef WITH_ASAN - /* Can't unpoison memory in threaded code. */ + /* Can't us memcpy due to poisoned memory pad bytes. */ CustomData_bmesh_copy_data(&ss->bm->vdata, &ss->bm->vdata, v->head.data, &vblock); #else memcpy(vblock, v->head.data, ss->bm->vdata.totsize); #endif interpl->v->head.data = (void *)vblock; - reproject_bm_data(ss->bm, interpl, fakef, true, typemask); + reproject_bm_data(ss->bm, interpl, fakef, domain_mask, typemask); cur_vblock++; } else { - reproject_bm_data(ss->bm, interpl, fakef, false, typemask); + reproject_bm_data(ss->bm, interpl, fakef, domain_mask, typemask); } copy_v3_v3(l->v->co, vco); copy_v3_v3(l->v->no, vno); - CustomData_bmesh_copy_data( - &ss->bm->ldata, &ss->bm->ldata, interpl->head.data, &l->head.data); + if (domain_mask & ATTR_DOMAIN_MASK_CORNER) { + CustomData_bmesh_copy_data( + &ss->bm->ldata, &ss->bm->ldata, interpl->head.data, &l->head.data); + } } - if (cur_vblock > 0) { + if (domain_mask & ATTR_DOMAIN_MASK_POINT && cur_vblock > 0) { float *ws = static_cast(BLI_array_alloca(ws, cur_vblock)); for (int i = 0; i < cur_vblock; i++) { ws[i] = 1.0f / float(cur_vblock); diff --git a/source/blender/blenkernel/intern/dyntopo_intern.hh b/source/blender/blenkernel/intern/dyntopo_intern.hh index 90306d2db78..239b74b5796 100644 --- a/source/blender/blenkernel/intern/dyntopo_intern.hh +++ b/source/blender/blenkernel/intern/dyntopo_intern.hh @@ -262,7 +262,7 @@ struct EdgeQueueContext { float limit_len_max_sqr; PBVHTopologyUpdateMode mode; - bool reproject_cdata; + eAttrCorrectMode distort_correction_mode; bool surface_relax; bool updatePBVH = false; diff --git a/source/blender/blenkernel/intern/paint.cc b/source/blender/blenkernel/intern/paint.cc index 10c7961c373..9204101ba5f 100644 --- a/source/blender/blenkernel/intern/paint.cc +++ b/source/blender/blenkernel/intern/paint.cc @@ -1840,12 +1840,12 @@ void BKE_sculpt_ensure_idmap(Object *ob) } } -void BKE_sculptsession_reproject_smooth_set(Object *ob, bool value) +void BKE_sculpt_distort_correction_set(Object *ob, eAttrCorrectMode value) { - ob->sculpt->reproject_smooth = value; + ob->sculpt->distort_correction_mode = value; if (ob->sculpt->pbvh) { - BKE_pbvh_reproject_smooth_set(ob->sculpt->pbvh, value); + BKE_pbvh_distort_correction_set(ob->sculpt->pbvh, value); } } @@ -1909,7 +1909,7 @@ static void sculpt_update_object( ss->depsgraph = depsgraph; - ss->reproject_smooth = !(me->flag & ME_SCULPT_IGNORE_UVS); + ss->distort_correction_mode = eAttrCorrectMode(ups.distort_correction_mode); ss->deform_modifiers_active = sculpt_modifiers_active(scene, sd, ob); @@ -2316,8 +2316,7 @@ static void sculpt_update_object( if (U.experimental.use_sculpt_texture_paint && ss->pbvh) { char *paint_canvas_key = BKE_paint_canvas_key_get(&scene->toolsettings->paint_mode, ob); if (ss->last_paint_canvas_key == nullptr || - !STREQ(paint_canvas_key, ss->last_paint_canvas_key)) - { + !STREQ(paint_canvas_key, ss->last_paint_canvas_key)) { MEM_SAFE_FREE(ss->last_paint_canvas_key); ss->last_paint_canvas_key = paint_canvas_key; BKE_pbvh_mark_rebuild_pixels(ss->pbvh); @@ -2332,8 +2331,7 @@ static void sculpt_update_object( if (U.experimental.use_sculpt_texture_paint && ss->pbvh) { char *paint_canvas_key = BKE_paint_canvas_key_get(&scene->toolsettings->paint_mode, ob); if (ss->last_paint_canvas_key == nullptr || - !STREQ(paint_canvas_key, ss->last_paint_canvas_key)) - { + !STREQ(paint_canvas_key, ss->last_paint_canvas_key)) { MEM_SAFE_FREE(ss->last_paint_canvas_key); ss->last_paint_canvas_key = paint_canvas_key; BKE_pbvh_mark_rebuild_pixels(ss->pbvh); @@ -3589,8 +3587,7 @@ static SculptAttribute *sculpt_get_cached_layer(SculptSession *ss, SculptAttribute *attr = ss->temp_attributes + i; if (attr->used && STREQ(attr->name, name) && attr->proptype == proptype && - attr->domain == domain) - { + attr->domain == domain) { return attr; } @@ -4074,7 +4071,6 @@ SculptAttribute BKE_sculpt_find_attribute(Object *ob, const char *name) void BKE_sculpt_attribute_release_ref(Object *ob, SculptAttribute *attr) { SculptSession *ss = ob->sculpt; - eAttrDomain domain = attr->domain; BLI_assert(attr->used); @@ -4493,8 +4489,11 @@ float calc_uv_snap_limit(BMLoop *l, int cd_uv) } /* Angle test. loop_is_corner calls this if the chart count test fails. */ -static bool loop_is_corner_angle( - BMLoop *l, int cd_offset, const float limit, const float angle_limit, const CustomData *ldata) +static bool loop_is_corner_angle(BMLoop *l, + int cd_offset, + const float limit, + const float angle_limit, + const CustomData * /*ldata*/) { BMVert *v = l->v; BMEdge *e = v->e; @@ -4503,9 +4502,10 @@ static bool loop_is_corner_angle( BMLoop *outer1 = nullptr, *outer2 = nullptr; float2 outer1_value; - int cd_pin = -1, cd_sel = -1; #ifdef TEST_UV_CORNER_CALC + int cd_pin = -1, cd_sel = -1; + bool test_mode = false; // test_mode = l->v->head.hflag & BM_ELEM_SELECT; @@ -4715,39 +4715,47 @@ bool loop_is_corner(BMLoop *l, int cd_uv, float limit, const CustomData *ldata) return ret; } -template +namespace detail { static void corner_interp(CustomDataLayer *layer, + BMVert *v, BMLoop *l, Span loops, Span ws, int cd_offset, - float factor) + float factor, + float2 &new_value) { float *ws2 = (float *)BLI_array_alloca(ws2, loops.size() + 1); - T sum = {}; + float2 sum = {}; float totsum = 0.0f; - T value = *BM_ELEM_CD_PTR(l, cd_offset); + float2 value = *BM_ELEM_CD_PTR(l, cd_offset); - float limit; - if (layer->type == CD_PROP_FLOAT2) { - /* Apparently 1/10th of average uv edge length is too small, - * muliply it to 1/2 - */ - limit = calc_uv_snap_limit(loops[0], cd_offset) * 4.0f; - } - else { - /* Do not restrict to islands for non-UVs */ - limit = FLT_MAX; - } + float limit, limit_sqr; + limit = calc_uv_snap_limit(l, cd_offset); + limit_sqr = limit * limit; + + /* Sum over uv verts surrounding l connected to the same chart. */ for (int i : loops.index_range()) { BMLoop *l2 = loops[i]; - BMLoop *l3 = l2->next->v == l->v ? l2->next : l2->prev; - T value3 = *BM_ELEM_CD_PTR(l3, cd_offset); + BMLoop *l3; - if (prop_eq(value, value3, limit)) { - T value2 = *BM_ELEM_CD_PTR(l2, cd_offset); + /* Find UV in l2's face that's owned by v. */ + if (l2->v == v) { + l3 = l2; + } + else if (l2->next->v == v) { + l3 = l2->next; + } + else { + l3 = l2->prev; + } + + float2 value3 = *BM_ELEM_CD_PTR(l3, cd_offset); + + if (math::distance_squared(value, value3) <= limit_sqr) { + float2 value2 = *BM_ELEM_CD_PTR(l2, cd_offset); sum += value2 * ws[i]; totsum += ws[i]; } @@ -4758,13 +4766,53 @@ static void corner_interp(CustomDataLayer *layer, } sum /= totsum; - - *BM_ELEM_CD_PTR(l, cd_offset) = value + (sum - value) * factor; + new_value = value + (sum - value) * factor; } /* Interpolates loops surrounding a vertex, splitting any UV map by * island as appropriate and enforcing proper boundary conditions. */ +static void interp_face_corners_intern(PBVH *pbvh, + BMVert *v, + Span loops, + Span ws, + float factor, + int cd_vert_boundary, + Span ls, + CustomDataLayer *layer) +{ + Vector corners; + Vector new_values; + + /* Build (semantic) corner tags. */ + for (BMLoop *l : ls) { + /* Do not calculate the corner state here, use stored corner flag. */ + bool corner = false; + + /* corner |= loop_is_corner(l, layer->offset); */ + corner |= BM_ELEM_CD_GET_INT(v, cd_vert_boundary) & SCULPT_CORNER_UV; + corners.append(corner); + } + + /* Interpolate loops. */ + for (int i : ls.index_range()) { + BMLoop *l = ls[i]; + + if (!corners[i]) { + corner_interp(layer, v, l, loops, ws, layer->offset, factor, new_values[i]); + } + } + + for (int i : ls.index_range()) { + BMLoop *l = ls[i]; + + if (!corners[i]) { + *BM_ELEM_CD_PTR(ls[i], layer->offset) = new_values[i]; + } + } +} +} // namespace detail + void interp_face_corners(PBVH *pbvh, PBVHVertRef vertex, Span loops, @@ -4776,15 +4824,13 @@ void interp_face_corners(PBVH *pbvh, return; /* Only for PBVH_BMESH. */ } - eCustomDataMask mask = CD_MASK_PROP_FLOAT2; - BMesh *bm = BKE_pbvh_get_bmesh(pbvh); BMVert *v = reinterpret_cast(vertex.i); BMEdge *e = v->e; BMLoop *l = e->l; CustomData *cdata = &bm->ldata; - Vector ls; + Vector ls; /* Tag loops around vertex. */ do { @@ -4821,10 +4867,8 @@ void interp_face_corners(PBVH *pbvh, for (int layer_i : IndexRange(cdata->totlayer)) { CustomDataLayer *layer = cdata->layers + layer_i; - if ((layer->flag & CD_FLAG_ELEM_NOINTERP) || !(CD_TYPE_AS_MASK(layer->type) & mask)) { - continue; - } - if (layer->flag & CD_FLAG_TEMPORARY) { + if (layer->type != CD_PROP_FLOAT2 || + (layer->flag & (CD_FLAG_ELEM_NOINTERP | CD_FLAG_TEMPORARY))) { continue; } @@ -4832,46 +4876,11 @@ void interp_face_corners(PBVH *pbvh, } /* Interpolate. */ - if (loops.size() > 1) { - Vector corners; - VertLoopSnapper corner_snap = {Span(ls), Span(layers)}; + if (layers.size() > 0 && loops.size() > 1) { + // VertLoopSnapper corner_snap = {Span(ls), Span(layers)}; for (CustomDataLayer *layer : layers) { - corners.clear(); - - if (layer->type == CD_PROP_FLOAT2) { - for (BMLoop *l : ls) { - /* Do not calculate corner here, use the stored corner flag. */ - bool corner = false; - // corner |= loop_is_corner(l, layer->offset); - corner |= BM_ELEM_CD_GET_INT(v, cd_vert_boundary) & SCULPT_CORNER_UV; - - corners.append(corner); - } - } - - for (int i : ls.index_range()) { - BMLoop *l = ls[i]; - - if (layer->type == CD_PROP_FLOAT2 && corners[i]) { - continue; - } - - switch (layer->type) { - case CD_PROP_FLOAT: - corner_interp(layer, l, loops, ws, layer->offset, factor); - break; - case CD_PROP_FLOAT2: - corner_interp(layer, l, loops, ws, layer->offset, factor); - break; - case CD_PROP_FLOAT3: - corner_interp(layer, l, loops, ws, layer->offset, factor); - break; - case CD_PROP_COLOR: - corner_interp(layer, l, loops, ws, layer->offset, factor); - break; - } - } + detail::interp_face_corners_intern(pbvh, v, loops, ws, factor, cd_vert_boundary, ls, layer); } /* Snap. */ diff --git a/source/blender/blenkernel/intern/pbvh.cc b/source/blender/blenkernel/intern/pbvh.cc index 3b7a53839ec..938f0dfe42a 100644 --- a/source/blender/blenkernel/intern/pbvh.cc +++ b/source/blender/blenkernel/intern/pbvh.cc @@ -4627,18 +4627,12 @@ void update_vert_boundary_grids(PBVH *pbvh, int index) } // namespace blender::bke::pbvh -void BKE_pbvh_reproject_smooth_set(PBVH *pbvh, bool value) +void BKE_pbvh_distort_correction_set(PBVH *pbvh, eAttrCorrectMode value) { - if (!!(pbvh->flags & PBVH_IGNORE_UVS) == !value) { - return; // no change - } + /* Condition to update UV boundaries.*/ + bool update = !pbvh->distort_correction_mode != !value; - if (!value) { - pbvh->flags |= PBVH_IGNORE_UVS; - } - else { - pbvh->flags &= ~PBVH_IGNORE_UVS; - } + pbvh->distort_correction_mode = value; pbvh_boundaries_flag_update(pbvh); } diff --git a/source/blender/blenkernel/intern/pbvh_bmesh.cc b/source/blender/blenkernel/intern/pbvh_bmesh.cc index 2daa8c65e19..1b22311f41c 100644 --- a/source/blender/blenkernel/intern/pbvh_bmesh.cc +++ b/source/blender/blenkernel/intern/pbvh_bmesh.cc @@ -817,11 +817,15 @@ static void pbvh_print_mem_size(PBVH *pbvh) float memsize2[4] = {0.0f, 0.0f, 0.0f, 0.0f}; float tot = 0.0f; + BLI_mempool *pools[4] = {bm->vpool, bm->epool, bm->lpool, bm->fpool}; + for (int i = 0; i < 4; i++) { CustomData *cdata = cdatas[i]; - memsize1[i] = (float)(sizes[i] * tots[i]) / 1024.0f / 1024.0f; - memsize2[i] = (float)(cdata->totsize * tots[i]) / 1024.0f / 1024.0f; + // memsize1[i] = (float)(sizes[i] * tots[i]) / 1024.0f / 1024.0f; + // memsize2[i] = (float)(cdata->totsize * tots[i]) / 1024.0f / 1024.0f; + memsize1[i] = float(BLI_mempool_get_size(pools[i])) / 1024.0f / 1024.0f; + memsize2[i] = cdata->pool ? float(BLI_mempool_get_size(cdata->pool)) / 1024.0 / 1024.0f : 0.0f; tot += memsize1[i] + memsize2[i]; } @@ -1439,8 +1443,7 @@ bool pbvh_bmesh_node_raycast(SculptSession *ss, madd_v3_v3v3fl(location, ray_start, ray_normal, *depth); for (int j = 0; j < 3; j++) { if (j == 0 || - len_squared_v3v3(location, cos[j]) < len_squared_v3v3(location, nearest_vertex_co)) - { + len_squared_v3v3(location, cos[j]) < len_squared_v3v3(location, nearest_vertex_co)) { copy_v3_v3(nearest_vertex_co, cos[j]); r_active_vertex->i = (intptr_t)verts[j]; } @@ -2101,8 +2104,7 @@ static void pbvh_bmesh_update_uv_boundary_intern(BMVert *v, float2 uv2b = *BM_ELEM_CD_PTR(l->v == v ? l->next : l, cd_uv); if (len_squared_v2v2(uv1a, uv2a) > snap_limit_sqr || - len_squared_v2v2(uv1b, uv2b) > snap_limit_sqr) - { + len_squared_v2v2(uv1b, uv2b) > snap_limit_sqr) { *boundflag |= SCULPT_BOUNDARY_UV; } @@ -2206,8 +2208,7 @@ void update_vert_boundary_bmesh(int cd_faceset_offset, if (update_sharp_angle) { if (e->l && e->l != e->l->radial_next) { if (blender::bke::pbvh::test_sharp_faces_bmesh( - e->l->f, e->l->radial_next->f, sharp_angle_limit)) - { + e->l->f, e->l->radial_next->f, sharp_angle_limit)) { boundflag |= SCULPT_BOUNDARY_SHARP_ANGLE; sharp_angle_num++; } @@ -2230,8 +2231,7 @@ void update_vert_boundary_bmesh(int cd_faceset_offset, if (e->l) { if ((e->l->f->head.hflag & BM_ELEM_HIDDEN) || - (e->l->radial_next->f->head.hflag & BM_ELEM_HIDDEN)) - { + (e->l->radial_next->f->head.hflag & BM_ELEM_HIDDEN)) { newflag |= SCULPTFLAG_VERT_FSET_HIDDEN; } @@ -4027,8 +4027,7 @@ void update_sharp_vertex_bmesh(BMVert *v, int cd_boundary_flag, const float shar } if (blender::bke::pbvh::test_sharp_faces_bmesh( - e->l->f, e->l->radial_next->f, sharp_angle_limit)) - { + e->l->f, e->l->radial_next->f, sharp_angle_limit)) { flag |= SCULPT_BOUNDARY_SHARP_ANGLE; sharp_num++; } diff --git a/source/blender/blenkernel/intern/pbvh_intern.hh b/source/blender/blenkernel/intern/pbvh_intern.hh index b39fb1bc453..d4cbce6a5a3 100644 --- a/source/blender/blenkernel/intern/pbvh_intern.hh +++ b/source/blender/blenkernel/intern/pbvh_intern.hh @@ -160,6 +160,7 @@ struct DMFlagMat; struct PBVH { PBVHPublic header; PBVHFlags flags; + eAttrCorrectMode distort_correction_mode; int idgen; diff --git a/source/blender/blenloader/intern/versioning_400.cc b/source/blender/blenloader/intern/versioning_400.cc index 3300d7ac558..231cabee537 100644 --- a/source/blender/blenloader/intern/versioning_400.cc +++ b/source/blender/blenloader/intern/versioning_400.cc @@ -14,6 +14,7 @@ #include "DNA_modifier_types.h" #include "DNA_movieclip_types.h" +#include "DNA_defaults.h" #include "DNA_genfile.h" #include "BLI_assert.h" @@ -257,6 +258,18 @@ void blo_do_versions_400(FileData *fd, Library * /*lib*/, Main *bmain) } } + if (!DNA_struct_elem_find( + fd->filesdna, "UnifiedPaintSettings", "char", "distort_correction_mode")) { + UnifiedPaintSettings defaults = *DNA_struct_default_get(UnifiedPaintSettings); + + LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) { + if (scene->toolsettings) { + scene->toolsettings->unified_paint_settings.distort_correction_mode = + defaults.distort_correction_mode; + } + } + } + /** * Versioning code until next subversion bump goes here. * diff --git a/source/blender/bmesh/intern/bmesh_log.cc b/source/blender/bmesh/intern/bmesh_log.cc index a09e38f346b..b64ed5957f2 100644 --- a/source/blender/bmesh/intern/bmesh_log.cc +++ b/source/blender/bmesh/intern/bmesh_log.cc @@ -1708,6 +1708,11 @@ void BM_log_vert_before_modified(BMesh *bm, BMLog *log, BMVert *v) log->modify_if_vert(bm, v); } +void BM_log_vert_modified(BMesh *bm, BMLog *log, BMVert *v) +{ + log->modify_vert(bm, v); +} + BMLogEntry *BM_log_entry_check_customdata(BMesh *bm, BMLog *log) { BMLogEntry *entry = log->current_entry; diff --git a/source/blender/bmesh/intern/bmesh_log_intern.h b/source/blender/bmesh/intern/bmesh_log_intern.h index 18258b7486c..888a0a6ab9e 100644 --- a/source/blender/bmesh/intern/bmesh_log_intern.h +++ b/source/blender/bmesh/intern/bmesh_log_intern.h @@ -127,6 +127,8 @@ void BM_log_redo(BMesh *bm, BMLog *log, BMLogCallbacks *callbacks); /* Log a vertex before it is modified */ void BM_log_vert_before_modified(BMesh *bm, BMLog *log, BMVert *v); +void BM_log_vert_modified(BMesh *bm, BMLog *log, BMVert *v); + /* Log a new vertex as added to the BMesh * * The new vertex gets a unique ID assigned. It is then added to a map diff --git a/source/blender/editors/sculpt_paint/sculpt.cc b/source/blender/editors/sculpt_paint/sculpt.cc index e3893fa2e49..1f72e5a01cb 100644 --- a/source/blender/editors/sculpt_paint/sculpt.cc +++ b/source/blender/editors/sculpt_paint/sculpt.cc @@ -4247,8 +4247,7 @@ static void do_brush_action(Sculpt *sd, } if (ss->cache->supports_gravity && sd->gravity_factor > 0.0f && - undo_type != SCULPT_UNDO_COORDS) - { + undo_type != SCULPT_UNDO_COORDS) { extra_type = int(SCULPT_UNDO_COORDS); } @@ -5090,8 +5089,7 @@ static void sculpt_update_cache_invariants( ss->hard_edge_mode = ups->hard_edge_mode; ss->smooth_boundary_flag = eSculptBoundary(ups->smooth_boundary_flag); - Mesh *me = BKE_object_get_original_mesh(ob); - BKE_sculptsession_reproject_smooth_set(ob, !(me->flag & ME_SCULPT_IGNORE_UVS)); + BKE_sculpt_distort_correction_set(ob, eAttrCorrectMode(ups->distort_correction_mode)); ss->cache = cache; @@ -5285,8 +5283,7 @@ static bool sculpt_needs_delta_from_anchored_origin(Brush *brush) return true; } if (brush->sculpt_tool == SCULPT_TOOL_CLOTH && - brush->cloth_deform_type == BRUSH_CLOTH_DEFORM_GRAB) - { + brush->cloth_deform_type == BRUSH_CLOTH_DEFORM_GRAB) { return true; } return false; @@ -6091,17 +6088,17 @@ void SCULPT_update_object_bounding_box(Object *ob) } } +#ifdef DEBUG_SHOW_SCULPT_BM_UV_EDGES void SCULPT_tag_uveditor_update(bContext *C, Depsgraph *depsgraph, Object *ob) { -#ifdef DEBUG_SHOW_SCULPT_BM_UV_EDGES Object *obedit_eval = DEG_get_evaluated_object(depsgraph, ob); BKE_mesh_batch_cache_dirty_tag(static_cast(obedit_eval->data), BKE_MESH_BATCH_DIRTY_UVEDIT_ALL); DEG_id_tag_update(&ob->id, ID_RECALC_EDITORS | ID_RECALC_SELECT); WM_event_add_notifier(C, NC_GEOM | ND_SELECT, ob->data); -#endif } +#endif void SCULPT_flush_update_step(bContext *C, SculptUpdateType update_flags) { @@ -6909,8 +6906,7 @@ void SCULPT_fake_neighbors_ensure(Sculpt *sd, Object *ob, const float max_dist) * recalculated. */ if (ss->fake_neighbors.fake_neighbor_index && - ss->fake_neighbors.current_max_distance == max_dist) - { + ss->fake_neighbors.current_max_distance == max_dist) { return; } diff --git a/source/blender/editors/sculpt_paint/sculpt_brush_types.cc b/source/blender/editors/sculpt_paint/sculpt_brush_types.cc index 40febeec3dd..0751180188f 100644 --- a/source/blender/editors/sculpt_paint/sculpt_brush_types.cc +++ b/source/blender/editors/sculpt_paint/sculpt_brush_types.cc @@ -2548,7 +2548,7 @@ static void do_topology_relax_task_cb_ex(void *__restrict userdata, SCULPT_automasking_node_begin( data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]); - bool do_reproject = SCULPT_need_reproject(ss); + eAttrCorrectMode distort_correction_mode = SCULPT_need_reproject(ss); bool weighted = brush->flag2 & BRUSH_SMOOTH_USE_AREA_WEIGHT; if (weighted) { @@ -2587,8 +2587,8 @@ static void do_topology_relax_task_cb_ex(void *__restrict userdata, interp_v3_v3v3(vd.co, vd.co, avg, fade); BKE_sculpt_sharp_boundary_flag_update(ss, vd.vertex); - if (do_reproject) { - BKE_sculpt_reproject_cdata(ss, vd.vertex, startco, startno, false); + if (distort_correction_mode & ~UNDISTORT_RELAX_UVS) { + BKE_sculpt_reproject_cdata(ss, vd.vertex, startco, startno, ss->distort_correction_mode); } if (vd.is_mesh) { @@ -2901,7 +2901,7 @@ static void do_topology_rake_bmesh_task_cb_ex(void *__restrict userdata, const Brush *brush = data->brush; const bool use_curvature = brush->flag2 & BRUSH_CURVATURE_RAKE; - const bool do_reproject = SCULPT_need_reproject(ss); + const eAttrCorrectMode distort_correction_mode = SCULPT_need_reproject(ss); float hard_corner_pin = BKE_brush_hard_corner_pin_get(ss->scene, brush); float direction[3]; @@ -2984,7 +2984,7 @@ static void do_topology_rake_bmesh_task_cb_ex(void *__restrict userdata, true, false, fade, - do_reproject); + distort_correction_mode & UNDISTORT_RELAX_UVS); sub_v3_v3v3(val, avg, vd.co); madd_v3_v3v3fl(val, vd.co, val, fade); @@ -3009,9 +3009,8 @@ static void do_topology_rake_bmesh_task_cb_ex(void *__restrict userdata, interp_v3_v3v3(origco, origco, origco_avg, fade); } - if (do_reproject) { - /* Use interp_face_corners instead. */ - // BKE_sculpt_reproject_cdata(ss, vd.vertex, oldco, oldno); + if (distort_correction_mode & ~UNDISTORT_RELAX_UVS) { + BKE_sculpt_reproject_cdata(ss, vd.vertex, oldco, oldno, ss->distort_correction_mode); } if (vd.is_mesh) { diff --git a/source/blender/editors/sculpt_paint/sculpt_detail.cc b/source/blender/editors/sculpt_paint/sculpt_detail.cc index 159e7ed7b45..56968afcf40 100644 --- a/source/blender/editors/sculpt_paint/sculpt_detail.cc +++ b/source/blender/editors/sculpt_paint/sculpt_detail.cc @@ -256,6 +256,15 @@ static int sculpt_detail_flood_fill_exec(bContext *C, wmOperator *op) SCULPT_undo_push_begin(ob, op); SCULPT_undo_push_node(ob, nullptr, SCULPT_UNDO_COORDS); + SculptSession *ss = ob->sculpt; + BMesh *bm = ss->bm; + BMIter iter; + BMVert *v; + + BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) { + BM_log_vert_before_modified(bm, ss->bm_log, v); + } + double time = PIL_check_seconds_timer(); auto should_stop = [&time]() { return PIL_check_seconds_timer() - time > 1.0f; }; @@ -354,6 +363,15 @@ int sculpt_detail_flood_fill_invoke(bContext *C, wmOperator *op, const wmEvent * SCULPT_undo_push_begin(ob, op); SCULPT_undo_push_node(ob, nullptr, SCULPT_UNDO_COORDS); + SculptSession *ss = ob->sculpt; + BMesh *bm = ss->bm; + BMIter iter; + BMVert *v; + + BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) { + BM_log_vert_before_modified(bm, ss->bm_log, v); + } + flood_fill_job.tool_settings = CTX_data_tool_settings(C); flood_fill_job.brush = BKE_paint_brush(&flood_fill_job.tool_settings->sculpt->paint); flood_fill_job.ob = CTX_data_active_object(C); diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.hh b/source/blender/editors/sculpt_paint/sculpt_intern.hh index 97bcc286924..01cf8f803eb 100644 --- a/source/blender/editors/sculpt_paint/sculpt_intern.hh +++ b/source/blender/editors/sculpt_paint/sculpt_intern.hh @@ -8,6 +8,7 @@ #pragma once +#include "DNA_brush_enums.h" /* For eAttrCorrectMode. */ #include "DNA_brush_types.h" #include "DNA_key_types.h" #include "DNA_listBase.h" @@ -1262,8 +1263,7 @@ void SCULPT_vertex_neighbors_get(const struct SculptSession *ss, #define SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN(ss, v_index, neighbor_iterator) \ SCULPT_vertex_neighbors_get(ss, v_index, false, &neighbor_iterator); \ for (neighbor_iterator.i = 0; neighbor_iterator.i < neighbor_iterator.size; \ - neighbor_iterator.i++) \ - { \ + neighbor_iterator.i++) { \ neighbor_iterator.has_edge = neighbor_iterator.neighbors[neighbor_iterator.i].edge.i != \ PBVH_REF_NONE; \ neighbor_iterator.vertex = neighbor_iterator.neighbors[neighbor_iterator.i].vertex; \ @@ -1288,8 +1288,7 @@ void SCULPT_vertex_neighbors_get(const struct SculptSession *ss, #define SCULPT_VERTEX_NEIGHBORS_ITER_END(neighbor_iterator) \ } \ if (!neighbor_iterator.no_free && \ - neighbor_iterator.neighbors != neighbor_iterator.neighbors_fixed) \ - { \ + neighbor_iterator.neighbors != neighbor_iterator.neighbors_fixed) { \ MEM_freeN(neighbor_iterator.neighbors); \ MEM_freeN(neighbor_iterator.neighbor_indices); \ } \ @@ -1924,10 +1923,9 @@ void SCULPT_neighbor_coords_average_interior(SculptSession *ss, bool smooth_origco = false, float factor = 1.0f); -BLI_INLINE bool SCULPT_need_reproject(const SculptSession *ss) +BLI_INLINE eAttrCorrectMode SCULPT_need_reproject(const SculptSession *ss) { - return ss->reproject_smooth && - ss->bm; // && CustomData_has_layer(&ss->bm->ldata, CD_PROP_FLOAT2); + return ss->bm ? ss->distort_correction_mode : UNDISTORT_NONE; } int SCULPT_vertex_island_get(SculptSession *ss, PBVHVertRef vertex); diff --git a/source/blender/editors/sculpt_paint/sculpt_smooth.cc b/source/blender/editors/sculpt_paint/sculpt_smooth.cc index c653a722ac5..fdfacff4200 100644 --- a/source/blender/editors/sculpt_paint/sculpt_smooth.cc +++ b/source/blender/editors/sculpt_paint/sculpt_smooth.cc @@ -114,8 +114,7 @@ static void SCULPT_neighbor_coords_average_interior_ex(SculptSession *ss, } #endif - eSculptBoundary uvflag = ss->reproject_smooth ? SCULPT_BOUNDARY_UV : SCULPT_BOUNDARY_NONE; - + eSculptBoundary uvflag = ss->distort_correction_mode ? SCULPT_BOUNDARY_UV : SCULPT_BOUNDARY_NONE; eSculptBoundary hard_flags = SCULPT_BOUNDARY_SHARP_MARK | SCULPT_BOUNDARY_SHARP_ANGLE | SCULPT_BOUNDARY_MESH; @@ -246,10 +245,10 @@ static void SCULPT_neighbor_coords_average_interior_ex(SculptSession *ss, is_boundary2 = SCULPT_vertex_is_boundary(ss, ni.vertex, bound_type); } - const eSculptBoundary smooth_types = (!ss->hard_edge_mode ? - SCULPT_BOUNDARY_FACE_SET | SCULPT_BOUNDARY_SEAM : - SCULPT_BOUNDARY_SEAM) | - uvflag; + eSculptBoundary smooth_types = (!ss->hard_edge_mode ? + SCULPT_BOUNDARY_FACE_SET | SCULPT_BOUNDARY_SEAM : + SCULPT_BOUNDARY_SEAM) | + uvflag; if (is_boundary2) { totboundary++; @@ -299,7 +298,7 @@ static void SCULPT_neighbor_coords_average_interior_ex(SculptSession *ss, SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); /* Ensure open strings of boundary edges don't shrink at the endpoints. */ - if (totboundary == 1) { + if (is_boundary && totboundary == 1) { total = 0.0; zero_v3(avg); @@ -313,6 +312,7 @@ static void SCULPT_neighbor_coords_average_interior_ex(SculptSession *ss, float w = weighted ? areas[ni.i] : 1.0f; avg += float3(vertex_co_get(ss, ni.vertex)) * w; total += w; + if constexpr (smooth_face_corners) { addblock(ni.vertex, ni.edge, w); } @@ -367,7 +367,7 @@ void SCULPT_neighbor_coords_average_interior(SculptSession *ss, corner_type |= SCULPT_CORNER_FACE_SET; } - if (ss->reproject_smooth) { + if (ss->distort_correction_mode & UNDISTORT_RELAX_UVS) { SCULPT_neighbor_coords_average_interior_ex(ss, result, vertex, @@ -1079,7 +1079,7 @@ static void do_smooth_brush_task_cb_ex(void *__restrict userdata, SCULPT_clip(sd, ss, vd.co, val); if (do_reproject) { - BKE_sculpt_reproject_cdata(ss, vd.vertex, oldco, oldno, false); + BKE_sculpt_reproject_cdata(ss, vd.vertex, oldco, oldno, ss->distort_correction_mode); } if (vd.is_mesh) { @@ -1110,7 +1110,7 @@ void SCULPT_smooth_undo_push(Object *ob, Span nodes) } } -#if 0 //NotForPR, see comment in BKE_pbvh_iter.hh +#if 0 // NotForPR, see comment in BKE_pbvh_iter.hh # include "BKE_mesh_mapping.h" # include "BKE_pbvh_iter.hh" @@ -1238,7 +1238,7 @@ void SCULPT_smooth( SCULPT_clip(sd, ss, vd.co, val); if (do_reproject) { - BKE_sculpt_reproject_cdata(ss, vd.vertex, oldco, oldno, false); + BKE_sculpt_reproject_cdata(ss, vd.vertex, oldco, oldno, ss->distort_correction_mode); } if (vd.is_mesh) { @@ -1256,7 +1256,7 @@ void SCULPT_smooth( void SCULPT_smooth( Sculpt *sd, Object *ob, Span nodes, float bstrength, const bool smooth_mask) { - //SCOPED_TIMER(__func__); + // SCOPED_TIMER(__func__); SculptSession *ss = ob->sculpt; Brush *brush = BKE_paint_brush(&sd->paint); diff --git a/source/blender/makesdna/DNA_brush_enums.h b/source/blender/makesdna/DNA_brush_enums.h index eb0e8625439..bc9bba20c8c 100644 --- a/source/blender/makesdna/DNA_brush_enums.h +++ b/source/blender/makesdna/DNA_brush_enums.h @@ -674,6 +674,15 @@ typedef enum eBrushCurvesSculptDensityMode { BRUSH_CURVES_SCULPT_DENSITY_MODE_REMOVE = 2, } eBrushCurvesSculptDensityMode; +/* How to correct distortion in smoothing tools.*/ +typedef enum eAttrCorrectMode { + UNDISTORT_NONE = 0, + UNDISTORT_REPROJECT_VERTS = (1 << 0), /* Reproject attributes. */ + UNDISTORT_REPROJECT_CORNERS = (1 << 1), + UNDISTORT_RELAX_UVS = (1 << 2), /* Relax UVs, overrides REPROJECT_CORNERS (for UVs). */ +} eAttrCorrectMode; +ENUM_OPERATORS(eAttrCorrectMode, UNDISTORT_RELAX_UVS); + #define MAX_BRUSH_PIXEL_RADIUS 500 #define DYNTOPO_DETAIL_RANGE 0.4f diff --git a/source/blender/makesdna/DNA_mesh_types.h b/source/blender/makesdna/DNA_mesh_types.h index 6dc6fa1b3ab..3bd819e1be9 100644 --- a/source/blender/makesdna/DNA_mesh_types.h +++ b/source/blender/makesdna/DNA_mesh_types.h @@ -407,12 +407,12 @@ enum { ME_FLAG_UNUSED_3 = 1 << 3, /* cleared */ ME_FLAG_UNUSED_4 = 1 << 4, /* cleared */ ME_AUTOSMOOTH = 1 << 5, - ME_SCULPT_IGNORE_UVS = 1 << 6, /* cleared */ + ME_FLAG_UNUSED_5 = 1 << 6, /* cleared */ ME_REMESH_REPROJECT_MATERIALS = 1 << 7, ME_REMESH_REPROJECT_VERTEX_COLORS = 1 << 8, ME_DS_EXPAND = 1 << 9, ME_SCULPT_DYNAMIC_TOPOLOGY = 1 << 10, - ME_FLAG_UNUSED_5 = 1 << 11, /* cleared */ + ME_FLAG_UNUSED_6 = 1 << 11, /* cleared */ ME_REMESH_REPROJECT_PAINT_MASK = 1 << 12, ME_REMESH_FIX_POLES = 1 << 13, ME_REMESH_REPROJECT_VOLUME = 1 << 14, diff --git a/source/blender/makesdna/DNA_scene_defaults.h b/source/blender/makesdna/DNA_scene_defaults.h index 93b31aa2f0f..805763a75cd 100644 --- a/source/blender/makesdna/DNA_scene_defaults.h +++ b/source/blender/makesdna/DNA_scene_defaults.h @@ -9,6 +9,7 @@ #pragma once #include "DNA_brush_defaults.h" +#include "DNA_brush_enums.h" #include "DNA_scene_enums.h" #include "DNA_view3d_defaults.h" @@ -284,6 +285,7 @@ .hard_corner_pin = 1.0f,\ .sharp_angle_limit = 0.6108f,\ .smooth_boundary_flag = SCULPT_BOUNDARY_MESH|SCULPT_BOUNDARY_FACE_SET|SCULPT_BOUNDARY_SEAM|SCULPT_BOUNDARY_SHARP_MARK|SCULPT_BOUNDARY_UV,\ + .distort_correction_mode = UNDISTORT_REPROJECT_VERTS|UNDISTORT_REPROJECT_CORNERS|UNDISTORT_RELAX_UVS,\ } #define _DNA_DEFAULTS_ParticleEditSettings \ diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h index 8b2e54c1c4a..359f485aa47 100644 --- a/source/blender/makesdna/DNA_scene_types.h +++ b/source/blender/makesdna/DNA_scene_types.h @@ -1399,8 +1399,10 @@ typedef struct UnifiedPaintSettings { float hard_corner_pin; float sharp_angle_limit; - char _pad[3]; - char hard_edge_mode; + char _pad[2]; + + char distort_correction_mode; /* eAttrCorrectMode bit mask. */ + char hard_edge_mode DNA_DEPRECATED; int smooth_boundary_flag; float start_pixel_radius; diff --git a/source/blender/makesrna/intern/rna_mesh.cc b/source/blender/makesrna/intern/rna_mesh.cc index 5ea40da0cc8..2f8922e888f 100644 --- a/source/blender/makesrna/intern/rna_mesh.cc +++ b/source/blender/makesrna/intern/rna_mesh.cc @@ -3332,10 +3332,6 @@ static void rna_def_mesh(BlenderRNA *brna) "is determined by the symmetry settings"); RNA_def_property_update(prop, 0, "rna_Mesh_update_draw"); - prop = RNA_def_property(srna, "sculpt_ignore_uvs", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", ME_SCULPT_IGNORE_UVS); - RNA_def_property_ui_text(prop, "Ignore UVs", ""); - /* End Symmetry */ prop = RNA_def_property(srna, "use_auto_smooth", PROP_BOOLEAN, PROP_NONE); diff --git a/source/blender/makesrna/intern/rna_scene.cc b/source/blender/makesrna/intern/rna_scene.cc index 9d16815bc95..d11364f5861 100644 --- a/source/blender/makesrna/intern/rna_scene.cc +++ b/source/blender/makesrna/intern/rna_scene.cc @@ -3963,6 +3963,24 @@ static void rna_def_unified_paint_settings(BlenderRNA *brna) StructRNA *srna; PropertyRNA *prop; + static EnumPropertyItem distort_correction_items[] = { + {UNDISTORT_NONE, + "DISABLED", + 0, + "Disabled", + "Disable attribute distortion correction\nto increase performance."}, + {UNDISTORT_REPROJECT_VERTS | UNDISTORT_REPROJECT_CORNERS, + "REPROJECT", + 0, + "Reproject", + "Reproject attributes."}, + {UNDISTORT_REPROJECT_VERTS | UNDISTORT_REPROJECT_CORNERS | UNDISTORT_RELAX_UVS, + "RELAX_UVS", + 0, + "Relax UVs", + "Relax UVs, reproject other attributes."}, + {0, nullptr, 0, nullptr, nullptr}}; + static const EnumPropertyItem brush_size_unit_items[] = { {0, "VIEW", 0, "View", "Measure brush size relative to the view"}, {UNIFIED_PAINT_BRUSH_LOCK_SIZE, @@ -3985,19 +4003,25 @@ static void rna_def_unified_paint_settings(BlenderRNA *brna) "Use Unified Radius", "Instead of per-brush radius, the radius is shared across brushes"); + prop = RNA_def_property(srna, "distort_correction_mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, nullptr, "distort_correction_mode"); + RNA_def_property_enum_items(prop, distort_correction_items); + RNA_def_property_ui_text( + prop, "Distortion Correction", "How smooth tools should correct attribute distortion."); + /* high-level flags to enable or disable unified paint settings */ prop = RNA_def_property(srna, "use_unified_hard_edge_mode", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", UNIFIED_PAINT_FLAG_HARD_EDGE_MODE); + RNA_def_property_boolean_sdna(prop, nullptr, "flag", UNIFIED_PAINT_FLAG_HARD_EDGE_MODE); RNA_def_property_ui_text( prop, "Use Unified Hard Edge Mode", "Use global setting for hard edge mode"); prop = RNA_def_property(srna, "use_unified_hard_corner_pin", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", UNIFIED_PAINT_HARD_CORNER_PIN); + RNA_def_property_boolean_sdna(prop, nullptr, "flag", UNIFIED_PAINT_HARD_CORNER_PIN); RNA_def_property_ui_text( prop, "Use Unified Hard Corner Pin", "Use global setting for hard corner pin"); prop = RNA_def_property(srna, "hard_corner_pin", PROP_FLOAT, PROP_NONE); - RNA_def_property_float_sdna(prop, NULL, "hard_corner_pin"); + RNA_def_property_float_sdna(prop, nullptr, "hard_corner_pin"); RNA_def_property_range(prop, 0.0f, 1.0f); RNA_def_property_ui_text( prop, "Use Unified Hard Corner Pin", "Use global setting for hard corner pin"); @@ -4005,12 +4029,12 @@ static void rna_def_unified_paint_settings(BlenderRNA *brna) RNA_def_property_update(prop, 0, "rna_UnifiedPaintSettings_update"); prop = RNA_def_property(srna, "use_unified_sharp_angle_limit", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", UNIFIED_PAINT_FLAG_SHARP_ANGLE_LIMIT); + RNA_def_property_boolean_sdna(prop, nullptr, "flag", UNIFIED_PAINT_FLAG_SHARP_ANGLE_LIMIT); RNA_def_property_ui_text( prop, "Use Unified Sharp Angle Limit", "Use global setting for sharp angle limit"); prop = RNA_def_property(srna, "sharp_angle_limit", PROP_FLOAT, PROP_ANGLE); - RNA_def_property_float_sdna(prop, NULL, "sharp_angle_limit"); + RNA_def_property_float_sdna(prop, nullptr, "sharp_angle_limit"); RNA_def_property_range(prop, 0.0f, M_PI); RNA_def_property_ui_text(prop, "Sharp Limit", ""); RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE); -- 2.30.2 From ae46f6ed025fac33dad909aeb28c98d803110b14 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Sun, 9 Jul 2023 01:06:45 -0700 Subject: [PATCH 225/279] temp-sculpt-dyntopo: View sculpt bmesh attributes in spreadsheet For debugging use only, uncomment `//#define DEBUG_SCULPT_BM_ATTRS` in spreadsheet_data_source_geometry.cc. Also wrote a basic BMesh attribute GVArray backend. To display hidden attributes, enter `bpy.app.debug_value = 892` into the python console. --- source/blender/blenkernel/BKE_sculpt.hh | 43 +--- source/blender/blenkernel/intern/dyntopo.cc | 18 +- source/blender/blenkernel/intern/paint.cc | 10 +- source/blender/blenlib/intern/cpp_types.cc | 4 + source/blender/bmesh/CMakeLists.txt | 1 + source/blender/bmesh/bmesh_varray.hh | 194 ++++++++++++++++++ source/blender/bmesh/intern/bmesh_log.cc | 2 +- .../editors/sculpt_paint/sculpt_api.cc | 6 +- .../spreadsheet_column_values.hh | 71 ++++++- .../spreadsheet_data_source_geometry.cc | 109 +++++++++- 10 files changed, 395 insertions(+), 63 deletions(-) create mode 100644 source/blender/bmesh/bmesh_varray.hh diff --git a/source/blender/blenkernel/BKE_sculpt.hh b/source/blender/blenkernel/BKE_sculpt.hh index 76c0df2401a..6c7fa14b16c 100644 --- a/source/blender/blenkernel/BKE_sculpt.hh +++ b/source/blender/blenkernel/BKE_sculpt.hh @@ -118,43 +118,6 @@ void interp_face_corners(PBVH *pbvh, float calc_uv_snap_limit(BMLoop *l, int cd_uv); bool loop_is_corner(BMLoop *l, int cd_uv, float limit = 0.01, const CustomData *ldata = nullptr); -/* NotForPR: TODO: find attribute API substitute for these prop_eq helper functions. */ -static bool prop_eq(float a, float b, float limit) -{ - return std::fabs(a - b) < limit; -} -static bool prop_eq(float2 a, float2 b, float limit) -{ - return blender::math::distance_squared(a, b) <= limit * limit; - // return (a[0] - b[0]) * (a[0] - b[0]) + (a[1] - b[1]) * (a[1] - b[1]) <= limit * limit; -} -static bool prop_eq(float3 a, float3 b, float limit) -{ - return prop_eq(a[0], b[0], limit) && prop_eq(a[1], b[1], limit) && prop_eq(a[2], b[2], limit); -} -static bool prop_eq(float4 a, float4 b, float limit) -{ - return prop_eq(a[0], b[0], limit) && // - prop_eq(a[1], b[1], limit) && // - prop_eq(a[2], b[2], limit) && // - prop_eq(a[3], b[3], limit); -} - -static bool prop_eq_type(void *a, void *b, float limit, eCustomDataType type) -{ - switch (type) { - case CD_PROP_FLOAT: - return prop_eq(*static_cast(a), *static_cast(b), limit); - case CD_PROP_FLOAT2: - return prop_eq(*static_cast(a), *static_cast(b), limit); - case CD_PROP_FLOAT3: - return prop_eq(*static_cast(a), *static_cast(b), limit); - case CD_PROP_COLOR: - return prop_eq(*static_cast(a), *static_cast(b), limit); - } - return false; -} - /* Finds sets of loops with the same vertex data * prior to an operation, then re-snaps them afterwards. */ @@ -181,7 +144,7 @@ struct VertLoopSnapper { for (int i : layers.index_range()) { switch (layers[i]->type) { case CD_PROP_FLOAT: - begin(i); + begin>(i); break; case CD_PROP_FLOAT2: begin(i); @@ -201,7 +164,7 @@ struct VertLoopSnapper { for (int i : layers.index_range()) { switch (layers[i]->type) { case CD_PROP_FLOAT: - do_snap(i); + do_snap>(i); break; case CD_PROP_FLOAT2: do_snap(i); @@ -255,7 +218,7 @@ struct VertLoopSnapper { } T b = *BM_ELEM_CD_PTR(ls[j], layer->offset); - if (prop_eq(a, b, limit)) { + if (math::distance_squared(a, b) <= limit * limit) { snap_sets[j][layer_i] = set; } } diff --git a/source/blender/blenkernel/intern/dyntopo.cc b/source/blender/blenkernel/intern/dyntopo.cc index 90ac314b898..ade6c12fa7e 100644 --- a/source/blender/blenkernel/intern/dyntopo.cc +++ b/source/blender/blenkernel/intern/dyntopo.cc @@ -1346,8 +1346,6 @@ static void unified_edge_queue_create(EdgeQueueContext *eq_ctx, } } - // bool push_subentry = false; - Vector verts; for (int i = 0; i < count; i++) { for (BMEdge *e : tdata[i].edges) { @@ -1358,10 +1356,8 @@ static void unified_edge_queue_create(EdgeQueueContext *eq_ctx, e->head.hflag &= ~EDGE_QUEUE_FLAG; if (e->l && e->l != e->l->radial_next->radial_next) { - /* Fix non-manifold "fins". */ - if (destroy_nonmanifold_fins(pbvh, e)) { - // push_subentry = true; - } + /* Delete non-manifold "fins". */ + destroy_nonmanifold_fins(pbvh, e); if (bm_elem_is_free((BMElem *)e, BM_EDGE)) { continue; @@ -3772,11 +3768,11 @@ static bool reproject_bm_data(BMesh *bm, return true; } -ATTR_NO_OPT void BKE_sculpt_reproject_cdata(SculptSession *ss, - PBVHVertRef vertex, - float startco[3], - float startno[3], - eAttrCorrectMode undistort_mode) +void BKE_sculpt_reproject_cdata(SculptSession *ss, + PBVHVertRef vertex, + float startco[3], + float startno[3], + eAttrCorrectMode undistort_mode) { int boundary_flag = blender::bke::paint::vertex_attr_get(vertex, ss->attrs.boundary_flags); if (boundary_flag & (SCULPT_BOUNDARY_UV)) { diff --git a/source/blender/blenkernel/intern/paint.cc b/source/blender/blenkernel/intern/paint.cc index 9204101ba5f..9284d666d0d 100644 --- a/source/blender/blenkernel/intern/paint.cc +++ b/source/blender/blenkernel/intern/paint.cc @@ -4545,7 +4545,7 @@ static bool loop_is_corner_angle(BMLoop *l, BMLoop *other_uv_l2 = l2->v == v ? l2->next : l2; float2 other_uv_value2 = *BM_ELEM_CD_PTR(other_uv_l2, cd_offset); - if (!prop_eq(uv_value, uv_value2, limit)) { + if (math::distance_squared(uv_value, uv_value2) > limit * limit) { continue; } @@ -4556,7 +4556,7 @@ static bool loop_is_corner_angle(BMLoop *l, BMLoop *other_uv_l3 = l3->v == v ? l3->next : l3; float2 other_uv_value3 = *BM_ELEM_CD_PTR(other_uv_l3, cd_offset); - if (!prop_eq(other_uv_value2, other_uv_value3, limit)) { + if (math::distance_squared(other_uv_value2, other_uv_value3) > limit * limit) { #ifdef TEST_UV_CORNER_CALC if (test_mode && cd_pin != -1) { // BM_ELEM_CD_SET_BOOL(other_uv_l2, cd_pin, true); @@ -4568,7 +4568,7 @@ static bool loop_is_corner_angle(BMLoop *l, float2 uv_value3 = *BM_ELEM_CD_PTR(uv_l3, cd_offset); /* other_uv_value might be valid for one of the two arms, check. */ - if (prop_eq(uv_value, uv_value3, limit)) { + if (math::distance_squared(uv_value, uv_value3) <= limit * limit) { #ifdef TEST_UV_CORNER_CALC if (test_mode) { printf("%s: case 1\n", __func__); @@ -4598,7 +4598,9 @@ static bool loop_is_corner_angle(BMLoop *l, outer1 = other_uv_l2; outer1_value = other_uv_value2; } - else if (other_uv_l2 != outer2 && !prop_eq(outer1_value, other_uv_value2, limit)) { + else if (other_uv_l2 != outer2 && + math::distance_squared(outer1_value, other_uv_value2) > limit * limit) + { outer2 = other_uv_l2; #ifdef TEST_UV_CORNER_CALC diff --git a/source/blender/blenlib/intern/cpp_types.cc b/source/blender/blenlib/intern/cpp_types.cc index 67e5ecc1402..22ef00360af 100644 --- a/source/blender/blenlib/intern/cpp_types.cc +++ b/source/blender/blenlib/intern/cpp_types.cc @@ -50,7 +50,9 @@ BLI_CPP_TYPE_MAKE(bool, CPPTypeFlags::BasicType) BLI_CPP_TYPE_MAKE(float, CPPTypeFlags::BasicType) BLI_CPP_TYPE_MAKE(blender::float2, CPPTypeFlags::BasicType) BLI_CPP_TYPE_MAKE(blender::float3, CPPTypeFlags::BasicType) +BLI_CPP_TYPE_MAKE(blender::float4, CPPTypeFlags::BasicType) BLI_CPP_TYPE_MAKE(blender::float4x4, CPPTypeFlags::BasicType) +BLI_CPP_TYPE_MAKE(blender::uchar4, CPPTypeFlags::BasicType) BLI_CPP_TYPE_MAKE(int8_t, CPPTypeFlags::BasicType) BLI_CPP_TYPE_MAKE(int16_t, CPPTypeFlags::BasicType) @@ -81,7 +83,9 @@ void register_cpp_types() BLI_CPP_TYPE_REGISTER(float); BLI_CPP_TYPE_REGISTER(blender::float2); BLI_CPP_TYPE_REGISTER(blender::float3); + BLI_CPP_TYPE_REGISTER(blender::float4); BLI_CPP_TYPE_REGISTER(blender::float4x4); + BLI_CPP_TYPE_REGISTER(blender::uchar4); BLI_CPP_TYPE_REGISTER(int8_t); BLI_CPP_TYPE_REGISTER(int16_t); diff --git a/source/blender/bmesh/CMakeLists.txt b/source/blender/bmesh/CMakeLists.txt index b397384887f..0b8cf87ce62 100644 --- a/source/blender/bmesh/CMakeLists.txt +++ b/source/blender/bmesh/CMakeLists.txt @@ -171,6 +171,7 @@ set(SRC # public includes bmesh.h bmesh_tools.h + bmesh_varray.hh ) set(LIB diff --git a/source/blender/bmesh/bmesh_varray.hh b/source/blender/bmesh/bmesh_varray.hh new file mode 100644 index 00000000000..d18828c7769 --- /dev/null +++ b/source/blender/bmesh/bmesh_varray.hh @@ -0,0 +1,194 @@ +#include "BLI_generic_virtual_array.hh" +#include "BLI_math_vector_types.hh" +#include "BLI_span.hh" +#include "BLI_utildefines.h" +#include "BLI_virtual_array.hh" + +#include "bmesh.h" +#include +#include + +namespace blender::bmesh { +template static constexpr int get_htype_from_type() +{ + if constexpr (std::is_same_v) { + return BM_VERT; + } + if constexpr (std::is_same_v) { + return BM_EDGE; + } + if constexpr (std::is_same_v) { + return BM_LOOP; + } + if constexpr (std::is_same_v) { + return BM_FACE; + } + else { + return -1; + // static_assert(false, "BMType is not one of (BMVert, BMEdge, BMLoop, BMFace)."); + } +} + +template()> +class BMeshAttrArray : public blender::VArrayImpl { + // static const int htype = get_htype_from_type(); + + private: + BMesh *bm_; + int cd_offset_; + + int64_t get_size(BMesh *bm) + { + if constexpr (htype == BM_VERT) { + return bm->totvert; + } + else if constexpr (htype == BM_EDGE) { + return bm->totedge; + } + else if constexpr (htype == BM_LOOP) { + return bm->totloop; + } + else if constexpr (htype == BM_FACE) { + return bm->totface; + } + else { + // static_assert(false, "Invalid element type."); + BLI_assert_unreachable(); + return 0; + } + } + + public: + using base_type = T; + + // BMeshAttrArray(const int64_t size) = 0; + BMeshAttrArray(BMesh *bm, int cd_offset) + : VArrayImpl(get_size(bm)), bm_(bm), cd_offset_(cd_offset) + { + // + } + + BMeshAttrArray(const BMeshAttrArray &b) + : VArrayImpl(get_size(b.bm_)), bm_(b.bm_), cd_offset_(b.cd_offset_) + { + } + + virtual CommonVArrayInfo common_info() const override + { + CommonVArrayInfo info = {}; + + info.type = CommonVArrayInfo::Type::Any; + info.may_have_ownership = false; + + return info; + } + + virtual T get(int64_t index) const override + { + BMType **table = nullptr; + + if constexpr (htype == BM_VERT) { + table = bm_->vtable; + } + else if constexpr (htype == BM_EDGE) { + table = bm_->etable; + } + else if constexpr (htype == BM_LOOP) { + // static_assert(false, "Not supported for loops"); + } + else if constexpr (htype == BM_FACE) { + table = bm_->ftable; + } + + return *static_cast(POINTER_OFFSET(table[index]->head.data, cd_offset_)); + } +}; + +namespace detail { +template +static GVArray bmesh_attr_gvarray_intern(BMesh *bm, CustomDataLayer *layer) +{ + auto make_array = [&](auto *impl) { + using T = typename std::remove_reference_t::base_type; + + return VArray(static_cast *>(impl)); + // + }; + + switch (layer->type) { + case CD_PROP_FLOAT: + return make_array(new BMeshAttrArray(bm, layer->offset)); + case CD_PROP_FLOAT2: + return make_array(new BMeshAttrArray(bm, layer->offset)); + case CD_PROP_FLOAT3: + return make_array(new BMeshAttrArray(bm, layer->offset)); + case CD_PROP_COLOR: + return make_array(new BMeshAttrArray(bm, layer->offset)); + case CD_PROP_BYTE_COLOR: + return make_array(new BMeshAttrArray(bm, layer->offset)); + case CD_PROP_BOOL: + return make_array(new BMeshAttrArray(bm, layer->offset)); + case CD_PROP_INT8: + return make_array(new BMeshAttrArray(bm, layer->offset)); + case CD_PROP_INT32: + return make_array(new BMeshAttrArray(bm, layer->offset)); + case CD_ORIGINDEX: + return make_array(new BMeshAttrArray(bm, layer->offset)); + } + + return GVArray(VArray::ForSpan({nullptr, 0})); +} +}; // namespace detail + +static GVArray bmesh_attr_gvarray(BMesh *bm, int htype, const char *name) +{ + CustomData *cdata; + + switch (htype) { + case BM_VERT: + cdata = &bm->vdata; + break; + case BM_EDGE: + cdata = &bm->edata; + break; + case BM_LOOP: + printf("%s: loops are not supported\n", __func__); + return GVArray(VArray::ForSpan({nullptr, 0})); + case BM_FACE: + cdata = &bm->pdata; + break; + default: + printf("Invalid element type %d\n", htype); + BLI_assert_unreachable(); + return GVArray(VArray::ForSpan({nullptr, 0})); + } + + CustomDataLayer *layer = nullptr; + + for (int i = 0; i < cdata->totlayer; i++) { + if (STREQ(cdata->layers[i].name, name)) { + layer = &cdata->layers[i]; + break; + } + } + + if (!layer) { + printf("Unknown attribute %s\n", name); + return GVArray(VArray::ForSpan({nullptr, 0})); + } + + switch (htype) { + case BM_VERT: + return detail::bmesh_attr_gvarray_intern(bm, layer); + case BM_EDGE: + return detail::bmesh_attr_gvarray_intern(bm, layer); + /*case BM_LOOP: + break;*/ + case BM_FACE: + return detail::bmesh_attr_gvarray_intern(bm, layer); + } + + return GVArray(VArray::ForSpan({nullptr, 0})); +} + +} // namespace blender::bmesh diff --git a/source/blender/bmesh/intern/bmesh_log.cc b/source/blender/bmesh/intern/bmesh_log.cc index b64ed5957f2..cce967c579a 100644 --- a/source/blender/bmesh/intern/bmesh_log.cc +++ b/source/blender/bmesh/intern/bmesh_log.cc @@ -221,7 +221,7 @@ struct BMLogFace : public BMLogElem { Vector loop_customdata; // int material_index; - ATTR_NO_OPT void free(CustomData *domain, CustomData *loop_domain) + void free(CustomData *domain, CustomData *loop_domain) { BMLogElem::free(domain); diff --git a/source/blender/editors/sculpt_paint/sculpt_api.cc b/source/blender/editors/sculpt_paint/sculpt_api.cc index 834dbb37d53..cd42714401f 100644 --- a/source/blender/editors/sculpt_paint/sculpt_api.cc +++ b/source/blender/editors/sculpt_paint/sculpt_api.cc @@ -134,7 +134,7 @@ static bool sculpt_check_boundary_vertex_in_base_mesh(const SculptSession *ss, i eSculptBoundary SCULPT_edge_is_boundary(const SculptSession *ss, const PBVHEdgeRef edge, - eSculptBoundary typemask) + eSculptBoundary boundary_types) { int oldflag = blender::bke::paint::edge_attr_get(edge, ss->attrs.edge_boundary_flags); bool update = oldflag & (SCULPT_BOUNDARY_NEEDS_UPDATE | SCULPT_BOUNDARY_UPDATE_UV | @@ -204,8 +204,8 @@ eSculptBoundary SCULPT_edge_is_boundary(const SculptSession *ss, } } - return eSculptBoundary( - blender::bke::paint::edge_attr_get(edge, ss->attrs.edge_boundary_flags)); + return boundary_types & eSculptBoundary(blender::bke::paint::edge_attr_get( + edge, ss->attrs.edge_boundary_flags)); } void SCULPT_edge_get_verts(const SculptSession *ss, diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_column_values.hh b/source/blender/editors/space_spreadsheet/spreadsheet_column_values.hh index e0b394888e3..c776087c63b 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_column_values.hh +++ b/source/blender/editors/space_spreadsheet/spreadsheet_column_values.hh @@ -4,11 +4,18 @@ #pragma once +#include "MEM_guardedalloc.h" + #include "DNA_space_types.h" #include "BLI_generic_virtual_array.hh" +#include "BLI_math_vector_types.hh" #include "BLI_string_ref.hh" +#include "BLI_mempool.h" + +#include "bmesh.h" + namespace blender::ed::spreadsheet { eSpreadsheetColumnValueType cpp_type_to_column_type(const CPPType &type); @@ -22,6 +29,7 @@ class ColumnValues final { std::string name_; GVArray data_; + void *bmesh_data_ = nullptr; public: ColumnValues(std::string name, GVArray data) : name_(std::move(name)), data_(std::move(data)) @@ -30,7 +38,68 @@ class ColumnValues final { BLI_assert(data_); } - virtual ~ColumnValues() = default; + ColumnValues(const ColumnValues &b) = delete; + + ColumnValues(std::string name, BLI_mempool *pool, int type, int offset, int htype) + : name_(std::move(name)) + { + using namespace blender; + + auto create_array = [&](auto typevar) { + using T = decltype(typevar); + + int count = BLI_mempool_len(pool); + + T *array = MEM_cnew_array(count, "ColumnValues::bmesh_data"); + bmesh_data_ = static_cast(array); + + BLI_mempool_iter iter; + BLI_mempool_iternew(pool, &iter); + void *velem; + int i = 0; + + for (velem = BLI_mempool_iterstep(&iter); velem; velem = BLI_mempool_iterstep(&iter), i++) { + BMElem *elem = static_cast(velem); + T *value = static_cast(POINTER_OFFSET(elem->head.data, offset)); + + array[i] = *value; + } + + data_ = VArray::ForSpan({array, count}); + }; + + switch (type) { + case CD_PROP_FLOAT: + create_array(float(0)); + break; + case CD_PROP_FLOAT2: + create_array(float2()); + break; + case CD_PROP_FLOAT3: + create_array(float3()); + break; + case CD_PROP_COLOR: + create_array(float4()); + break; + case CD_PROP_BYTE_COLOR: + create_array(uchar4()); + break; + case CD_PROP_INT32: + create_array(int(0)); + break; + case CD_PROP_INT8: + create_array(int8_t(0)); + case CD_PROP_BOOL: + create_array(bool(0)); + break; + } + } + + ~ColumnValues() { + if (bmesh_data_) { + MEM_freeN(bmesh_data_); + } + } eSpreadsheetColumnValueType type() const { diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc index 2b843d4dddf..ae4452366c7 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc +++ b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc @@ -17,6 +17,7 @@ #include "BKE_mesh.hh" #include "BKE_mesh_wrapper.h" #include "BKE_modifier.h" +#include "BKE_paint.h" #include "BKE_volume.h" #include "BKE_volume_openvdb.hh" @@ -44,6 +45,13 @@ #include "bmesh.h" +/* Uncomment to view sculpt attributes in the spreadsheet. */ +//#define DEBUG_SCULPT_BM_ATTRS + +#ifdef DEBUG_SCULPT_BM_ATTRS +# include "bmesh_varray.hh" +#endif + #include "spreadsheet_data_source_geometry.hh" #include "spreadsheet_intern.hh" @@ -51,6 +59,23 @@ using blender::nodes::geo_eval_log::ViewerNodeLog; namespace blender::ed::spreadsheet { +static BMesh *get_object_bmesh(Object *object_eval) +{ +#ifdef DEBUG_SCULPT_BM_ATTRS + if (object_eval->mode == OB_MODE_SCULPT) { + return object_eval->sculpt->bm; + } + else { +#endif + Object *object_orig = DEG_get_original_object(object_eval); + Mesh *mesh_orig = (Mesh *)object_orig->data; + + return mesh_orig->edit_mesh->bm; +#ifdef DEBUG_SCULPT_BM_ATTRS + } +#endif +} + void ExtraColumns::foreach_default_column_ids( FunctionRef fn) const { @@ -172,6 +197,45 @@ static std::unique_ptr build_mesh_debug_columns(const Mesh &mesh, void GeometryDataSource::foreach_default_column_ids( FunctionRef fn) const { +#ifdef DEBUG_SCULPT_BM_ATTRS + if (object_eval_->mode == OB_MODE_SCULPT && object_eval_->sculpt && object_eval_->sculpt->bm) { + CustomData *cdata = nullptr; + BMesh *bm = object_eval_->sculpt->bm; + + switch (domain_) { + case ATTR_DOMAIN_POINT: + cdata = &bm->vdata; + break; + case ATTR_DOMAIN_EDGE: + cdata = &bm->edata; + break; + case ATTR_DOMAIN_FACE: + cdata = &bm->pdata; + break; + } + + if (cdata) { + const eCustomDataMask typemask = CD_MASK_PROP_FLOAT | CD_MASK_PROP_FLOAT2 | + CD_MASK_PROP_FLOAT3 | CD_MASK_PROP_COLOR | + CD_MASK_PROP_BYTE_COLOR | CD_MASK_PROP_INT32 | + CD_MASK_PROP_INT8 | CD_MASK_ORIGINDEX; + + for (int i = 0; i < cdata->totlayer; i++) { + CustomDataLayer *layer = &cdata->layers[i]; + + if (!bke::allow_procedural_attribute_access(layer->name)) { + continue; + } + + if (CD_TYPE_AS_MASK(layer->type) & typemask) { + fn({layer->name}, false); + } + } + return; + } + } +#endif + if (!component_->attributes().has_value()) { return; } @@ -276,6 +340,41 @@ std::unique_ptr GeometryDataSource::get_column_values( } } +#ifdef DEBUG_SCULPT_BM_ATTRS + if (object_eval_->mode == OB_MODE_SCULPT && object_eval_->sculpt && object_eval_->sculpt->bm) { + char htype = 0; + BMesh *bm = object_eval_->sculpt->bm; + + CustomData *cdata = &bm->vdata; + for (int i = 0; !htype && i < 4; i++, cdata++) { + for (int j = 0; j < cdata->totlayer; j++) { + CustomDataLayer *layer = &cdata->layers[j]; + + if (STREQ(layer->name, column_id.name)) { + htype = 1 << i; + break; + } + } + } + + if (htype) { + BM_mesh_elem_table_ensure(object_eval_->sculpt->bm, htype); + + GVArray bmarray = blender::bmesh::bmesh_attr_gvarray( + object_eval_->sculpt->bm, htype, column_id.name); + + if (bmarray.size() > 0) { + printf("%s: bmesh adaptor: %s\n", __func__, column_id.name); + StringRefNull column_display_name = column_id.name; + return std::make_unique(column_display_name, std::move(bmarray)); + } + else { + printf("%s: unknown bmesh attribute %s\n", __func__, column_id.name); + } + } + } +#endif + bke::GAttributeReader attribute = attributes.lookup(column_id.name); if (!attribute) { return {}; @@ -310,7 +409,11 @@ bool GeometryDataSource::has_selection_filter() const if (object_orig->type != OB_MESH) { return false; } +#ifdef DEBUG_SCULPT_BM_ATTRS + if (!ELEM(object_orig->mode, OB_MODE_EDIT, OB_MODE_SCULPT)) { +#else if (object_orig->mode != OB_MODE_EDIT) { +#endif return false; } return true; @@ -349,13 +452,13 @@ IndexMask GeometryDataSource::apply_selection_filter(IndexMaskMemory &memory) co switch (component_->type()) { case bke::GeometryComponent::Type::Mesh: { BLI_assert(object_eval_->type == OB_MESH); - BLI_assert(object_eval_->mode == OB_MODE_EDIT); + // BLI_assert(object_eval_->mode == OB_MODE_EDIT); Object *object_orig = DEG_get_original_object(object_eval_); const Mesh *mesh_eval = geometry_set_.get_mesh_for_read(); const bke::AttributeAccessor attributes_eval = mesh_eval->attributes(); Mesh *mesh_orig = (Mesh *)object_orig->data; - BMesh *bm = mesh_orig->edit_mesh->bm; - BM_mesh_elem_table_ensure(bm, BM_VERT); + BMesh *bm = get_object_bmesh(object_eval_); + BM_mesh_elem_table_ensure(bm, BM_VERT | BM_EDGE | BM_FACE); const int *orig_indices = (const int *)CustomData_get_layer(&mesh_eval->vdata, CD_ORIGINDEX); if (orig_indices != nullptr) { -- 2.30.2 From eb47c52706e0b52cf47200ad3d3831da0377110d Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Sun, 9 Jul 2023 01:26:05 -0700 Subject: [PATCH 226/279] temp-sculpt-dyntopo: Cleanup some code --- source/blender/blenkernel/intern/dyntopo.cc | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/source/blender/blenkernel/intern/dyntopo.cc b/source/blender/blenkernel/intern/dyntopo.cc index ade6c12fa7e..811fc4446dd 100644 --- a/source/blender/blenkernel/intern/dyntopo.cc +++ b/source/blender/blenkernel/intern/dyntopo.cc @@ -3710,7 +3710,7 @@ static bool reproject_bm_data(BMesh *bm, /* interpolate */ if (f_src->len == 3) { - myinterp::tri_weights_v3_new(tco, cos_2d[0], cos_2d[1], cos_2d[2], w); + myinterp::tri_weights_v3(tco, cos_2d[0], cos_2d[1], cos_2d[2], w); T sum = 0.0; for (int i = 0; i < 3; i++) { @@ -3831,12 +3831,10 @@ void BKE_sculpt_reproject_cdata(SculptSession *ss, } while ((l = l->radial_next) != e->l); } while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e); - int totloop = ls.size(); - /* Original (l->prev, l, l->next) projections for each loop ('l' remains unchanged). */ - char *_blocks = static_cast(alloca(ldata->totsize * totloop)); - void **blocks = static_cast(BLI_array_alloca(blocks, totloop)); + char *_blocks = static_cast(alloca(ldata->totsize * ls.size())); + void **blocks = static_cast(BLI_array_alloca(blocks, ls.size())); const int max_vblocks = valence * 3; @@ -3847,7 +3845,7 @@ void BKE_sculpt_reproject_cdata(SculptSession *ss, vblocks[i] = static_cast(_vblocks); } - for (int i = 0; i < totloop; i++, _blocks += ldata->totsize) { + for (int i = 0; i < ls.size(); i++, _blocks += ldata->totsize) { blocks[i] = static_cast(_blocks); } @@ -3964,7 +3962,7 @@ void BKE_sculpt_reproject_cdata(SculptSession *ss, normalize_v3(startno2); /* Build fake face with starting coordinates. */ - for (int i = 0; i < totloop; i++) { + for (int i = 0; i < ls.size(); i++) { BMLoop *l = ls[i]; float no[3] = {0.0f, 0.0f, 0.0f}; @@ -4078,7 +4076,7 @@ void BKE_sculpt_reproject_cdata(SculptSession *ss, *origco = origco_saved; *origno = origno_saved; } - } - snapper.snap(); + snapper.snap(); + } } -- 2.30.2 From d9c88f32a50db909d3ae0935020c4858b5303638 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Sun, 9 Jul 2023 01:36:57 -0700 Subject: [PATCH 227/279] temp-sculpt-dyntopo: remove unused code --- .../spreadsheet_column_values.hh | 55 ------------------- 1 file changed, 55 deletions(-) diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_column_values.hh b/source/blender/editors/space_spreadsheet/spreadsheet_column_values.hh index c776087c63b..6d383209b3f 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_column_values.hh +++ b/source/blender/editors/space_spreadsheet/spreadsheet_column_values.hh @@ -40,61 +40,6 @@ class ColumnValues final { ColumnValues(const ColumnValues &b) = delete; - ColumnValues(std::string name, BLI_mempool *pool, int type, int offset, int htype) - : name_(std::move(name)) - { - using namespace blender; - - auto create_array = [&](auto typevar) { - using T = decltype(typevar); - - int count = BLI_mempool_len(pool); - - T *array = MEM_cnew_array(count, "ColumnValues::bmesh_data"); - bmesh_data_ = static_cast(array); - - BLI_mempool_iter iter; - BLI_mempool_iternew(pool, &iter); - void *velem; - int i = 0; - - for (velem = BLI_mempool_iterstep(&iter); velem; velem = BLI_mempool_iterstep(&iter), i++) { - BMElem *elem = static_cast(velem); - T *value = static_cast(POINTER_OFFSET(elem->head.data, offset)); - - array[i] = *value; - } - - data_ = VArray::ForSpan({array, count}); - }; - - switch (type) { - case CD_PROP_FLOAT: - create_array(float(0)); - break; - case CD_PROP_FLOAT2: - create_array(float2()); - break; - case CD_PROP_FLOAT3: - create_array(float3()); - break; - case CD_PROP_COLOR: - create_array(float4()); - break; - case CD_PROP_BYTE_COLOR: - create_array(uchar4()); - break; - case CD_PROP_INT32: - create_array(int(0)); - break; - case CD_PROP_INT8: - create_array(int8_t(0)); - case CD_PROP_BOOL: - create_array(bool(0)); - break; - } - } - ~ColumnValues() { if (bmesh_data_) { MEM_freeN(bmesh_data_); -- 2.30.2 From 5d7f8e84b76d57a7873fcbf584e7d60a05626a60 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Tue, 11 Jul 2023 11:37:31 -0700 Subject: [PATCH 228/279] temp-sculpt-dyntopo: Format fixes --- .../startup/bl_ui/properties_paint_common.py | 582 +++++++----------- .../blender/blenkernel/intern/customdata.cc | 17 +- 2 files changed, 239 insertions(+), 360 deletions(-) diff --git a/scripts/startup/bl_ui/properties_paint_common.py b/scripts/startup/bl_ui/properties_paint_common.py index b8ea138d16b..06a253cd944 100644 --- a/scripts/startup/bl_ui/properties_paint_common.py +++ b/scripts/startup/bl_ui/properties_paint_common.py @@ -12,16 +12,15 @@ class UnifiedPaintPanel: @staticmethod def get_brush_mode(context): - """Get the correct mode for this context. For any context where this returns None, - no brush options should be displayed.""" + """ Get the correct mode for this context. For any context where this returns None, + no brush options should be displayed.""" mode = context.mode - if mode == "PARTICLE": + if mode == 'PARTICLE': # Particle brush settings currently completely do their own thing. return None from bl_ui.space_toolsystem_common import ToolSelectPanelHelper - tool = ToolSelectPanelHelper.tool_active_from_context(context) if not tool: @@ -37,12 +36,12 @@ class UnifiedPaintPanel: if space_data: space_type = space_data.type - if space_type == "IMAGE_EDITOR": + if space_type == 'IMAGE_EDITOR': if space_data.show_uvedit: - return "UV_SCULPT" - return "PAINT_2D" - elif space_type in {"VIEW_3D", "PROPERTIES"}: - if mode == "PAINT_TEXTURE": + return 'UV_SCULPT' + return 'PAINT_2D' + elif space_type in {'VIEW_3D', 'PROPERTIES'}: + if mode == 'PAINT_TEXTURE': if tool_settings.image_paint: return mode else: @@ -57,50 +56,49 @@ class UnifiedPaintPanel: mode = UnifiedPaintPanel.get_brush_mode(context) # 3D paint settings - if mode == "SCULPT": + if mode == 'SCULPT': return tool_settings.sculpt - elif mode == "PAINT_VERTEX": + elif mode == 'PAINT_VERTEX': return tool_settings.vertex_paint - elif mode == "PAINT_WEIGHT": + elif mode == 'PAINT_WEIGHT': return tool_settings.weight_paint - elif mode == "PAINT_TEXTURE": + elif mode == 'PAINT_TEXTURE': return tool_settings.image_paint - elif mode == "PARTICLE": + elif mode == 'PARTICLE': return tool_settings.particle_edit # 2D paint settings - elif mode == "PAINT_2D": + elif mode == 'PAINT_2D': return tool_settings.image_paint - elif mode == "UV_SCULPT": + elif mode == 'UV_SCULPT': return tool_settings.uv_sculpt # Grease Pencil settings - elif mode == "PAINT_GPENCIL": + elif mode == 'PAINT_GPENCIL': return tool_settings.gpencil_paint - elif mode == "SCULPT_GPENCIL": + elif mode == 'SCULPT_GPENCIL': return tool_settings.gpencil_sculpt_paint - elif mode == "WEIGHT_GPENCIL": + elif mode == 'WEIGHT_GPENCIL': return tool_settings.gpencil_weight_paint - elif mode == "VERTEX_GPENCIL": + elif mode == 'VERTEX_GPENCIL': return tool_settings.gpencil_vertex_paint - elif mode == "SCULPT_CURVES": + elif mode == 'SCULPT_CURVES': return tool_settings.curves_sculpt return None @staticmethod def prop_unified( - layout, - context, - brush, - prop_name, - unified_name=None, - pressure_name=None, - icon="NONE", - text=None, - slider=False, - header=False, - dyntopo=False, + layout, + context, + brush, + prop_name, + unified_name=None, + pressure_name=None, + icon='NONE', + text=None, + slider=False, + header=False, ): - """Generalized way of adding brush options to the UI, - along with their pen pressure setting and global toggle, if they exist.""" + """ Generalized way of adding brush options to the UI, + along with their pen pressure setting and global toggle, if they exist. """ row = layout.row(align=True) ups = context.tool_settings.unified_paint_settings prop_owner = brush @@ -114,7 +112,7 @@ class UnifiedPaintPanel: if unified_name and not header: # NOTE: We don't draw UnifiedPaintSettings in the header to reduce clutter. D5928#136281 - row.prop(ups, unified_name, text="", icon="BRUSHES_ALL") + row.prop(ups, unified_name, text="", icon='BRUSHES_ALL') return row @@ -147,7 +145,7 @@ class UnifiedPaintPanel: if not inherit: layout.prop( - brush.dyntopo, override_name, text="", icon="ERROR", emboss=False + brush.dyntopo, override_name, text="", icon='ERROR', emboss=False ) @staticmethod @@ -181,16 +179,14 @@ class BrushSelectPanel(BrushPanel): row = layout.row() large_preview = True if large_preview: - row.column().template_ID_preview( - settings, "brush", new="brush.add", rows=3, cols=8, hide_buttons=False - ) + row.column().template_ID_preview(settings, "brush", new="brush.add", rows=3, cols=8, hide_buttons=False) else: row.column().template_ID(settings, "brush", new="brush.add") col = row.column() - col.menu("VIEW3D_MT_brush_context_menu", icon="DOWNARROW_HLT", text="") + col.menu("VIEW3D_MT_brush_context_menu", icon='DOWNARROW_HLT', text="") if brush is not None: - col.prop(brush, "use_custom_icon", toggle=True, icon="FILE_IMAGE", text="") + col.prop(brush, "use_custom_icon", toggle=True, icon='FILE_IMAGE', text="") if brush.use_custom_icon: layout.prop(brush, "icon_filepath", text="") @@ -198,7 +194,7 @@ class BrushSelectPanel(BrushPanel): class ColorPalettePanel(BrushPanel): bl_label = "Color Palette" - bl_options = {"DEFAULT_CLOSED"} + bl_options = {'DEFAULT_CLOSED'} @classmethod def poll(cls, context): @@ -208,7 +204,7 @@ class ColorPalettePanel(BrushPanel): settings = cls.paint_settings(context) brush = settings.brush - if context.space_data.type == "IMAGE_EDITOR" or context.image_paint_object: + if context.space_data.type == 'IMAGE_EDITOR' or context.image_paint_object: capabilities = brush.image_paint_capabilities return capabilities.has_color @@ -232,7 +228,7 @@ class ColorPalettePanel(BrushPanel): class ClonePanel(BrushPanel): bl_label = "Clone" - bl_options = {"DEFAULT_CLOSED"} + bl_options = {'DEFAULT_CLOSED'} @classmethod def poll(cls, context): @@ -242,9 +238,9 @@ class ClonePanel(BrushPanel): settings = cls.paint_settings(context) mode = cls.get_brush_mode(context) - if mode == "PAINT_TEXTURE": + if mode == 'PAINT_TEXTURE': brush = settings.brush - return brush.image_tool == "CLONE" + return brush.image_tool == 'CLONE' return False def draw_header(self, context): @@ -260,16 +256,13 @@ class ClonePanel(BrushPanel): ob = context.active_object col = layout.column() - if settings.mode == "MATERIAL": + if settings.mode == 'MATERIAL': if len(ob.material_slots) > 1: col.label(text="Materials") col.template_list( - "MATERIAL_UL_matslots", - "", - ob, - "material_slots", - ob, - "active_material_index", + "MATERIAL_UL_matslots", "", + ob, "material_slots", + ob, "active_material_index", rows=2, ) @@ -277,30 +270,25 @@ class ClonePanel(BrushPanel): if mat: col.label(text="Source Clone Slot") col.template_list( - "TEXTURE_UL_texpaintslots", - "", - mat, - "texture_paint_slots", - mat, - "paint_clone_slot", + "TEXTURE_UL_texpaintslots", "", + mat, "texture_paint_slots", + mat, "paint_clone_slot", rows=2, ) - elif settings.mode == "IMAGE": + elif settings.mode == 'IMAGE': mesh = ob.data clone_text = mesh.uv_layer_clone.name if mesh.uv_layer_clone else "" col.label(text="Source Clone Image") col.template_ID(settings, "clone_image") col.label(text="Source Clone UV Map") - col.menu( - "VIEW3D_MT_tools_projectpaint_clone", text=clone_text, translate=False - ) + col.menu("VIEW3D_MT_tools_projectpaint_clone", text=clone_text, translate=False) class TextureMaskPanel(BrushPanel): bl_label = "Texture Mask" - bl_options = {"DEFAULT_CLOSED"} + bl_options = {'DEFAULT_CLOSED'} def draw(self, context): layout = self.layout @@ -311,15 +299,13 @@ class TextureMaskPanel(BrushPanel): mask_tex_slot = brush.mask_texture_slot col = layout.column() - col.template_ID_preview( - mask_tex_slot, "texture", new="texture.new", rows=3, cols=8 - ) + col.template_ID_preview(mask_tex_slot, "texture", new="texture.new", rows=3, cols=8) # map_mode layout.row().prop(mask_tex_slot, "mask_map_mode", text="Mask Mapping") - if mask_tex_slot.map_mode == "STENCIL": - if brush.mask_texture and brush.mask_texture.type == "IMAGE": + if mask_tex_slot.map_mode == 'STENCIL': + if brush.mask_texture and brush.mask_texture.type == 'IMAGE': layout.operator("brush.stencil_fit_image_aspect").mask = True layout.operator("brush.stencil_reset_transform").mask = True @@ -332,10 +318,7 @@ class TextureMaskPanel(BrushPanel): if mask_tex_slot.has_texture_angle_source: col.prop(mask_tex_slot, "use_rake", text="Rake") - if ( - brush.brush_capabilities.has_random_texture_angle - and mask_tex_slot.has_random_texture_angle - ): + if brush.brush_capabilities.has_random_texture_angle and mask_tex_slot.has_random_texture_angle: col.prop(mask_tex_slot, "use_random", text="Random") if mask_tex_slot.use_random: col.prop(mask_tex_slot, "random_angle", text="Random Angle") @@ -347,7 +330,7 @@ class TextureMaskPanel(BrushPanel): class StrokePanel(BrushPanel): bl_label = "Stroke" - bl_options = {"DEFAULT_CLOSED"} + bl_options = {'DEFAULT_CLOSED'} bl_ui_units_x = 13 def draw(self, context): @@ -379,16 +362,11 @@ class StrokePanel(BrushPanel): row = col.row(align=True) row.prop(brush, "spacing", text="Spacing") - if mode == "SCULPT": - col.row().prop( - brush, "use_scene_spacing", text="Spacing Distance", expand=True - ) + if mode == 'SCULPT': + col.row().prop(brush, "use_scene_spacing", text="Spacing Distance", expand=True) - if mode in {"PAINT_TEXTURE", "PAINT_2D", "SCULPT"}: - if ( - brush.image_paint_capabilities.has_space_attenuation - or brush.sculpt_capabilities.has_space_attenuation - ): + if mode in {'PAINT_TEXTURE', 'PAINT_2D', 'SCULPT'}: + if brush.image_paint_capabilities.has_space_attenuation or brush.sculpt_capabilities.has_space_attenuation: col.prop(brush, "use_space_attenuation") if brush.use_curve: @@ -403,12 +381,10 @@ class StrokePanel(BrushPanel): col.prop(brush, "dash_ratio", text="Dash Ratio") col.prop(brush, "dash_samples", text="Dash Length") - if ( - mode == "SCULPT" and brush.sculpt_capabilities.has_jitter - ) or mode != "SCULPT": + if (mode == 'SCULPT' and brush.sculpt_capabilities.has_jitter) or mode != 'SCULPT': col.separator() row = col.row(align=True) - if brush.jitter_unit == "BRUSH": + if brush.jitter_unit == 'BRUSH': row.prop(brush, "jitter", slider=True) else: row.prop(brush, "jitter_absolute") @@ -421,7 +397,7 @@ class StrokePanel(BrushPanel): class SmoothStrokePanel(BrushPanel): bl_label = "Stabilize Stroke" - bl_options = {"DEFAULT_CLOSED"} + bl_options = {'DEFAULT_CLOSED'} @classmethod def poll(cls, context): @@ -438,9 +414,8 @@ class SmoothStrokePanel(BrushPanel): brush = settings.brush self.layout.use_property_split = False - self.layout.prop( - brush, "use_smooth_stroke", text=self.bl_label if self.is_popover else "" - ) + self.layout.prop(brush, "use_smooth_stroke", + text=self.bl_label if self.is_popover else "") def draw(self, context): layout = self.layout @@ -458,7 +433,7 @@ class SmoothStrokePanel(BrushPanel): class FalloffPanel(BrushPanel): bl_label = "Falloff" - bl_options = {"DEFAULT_CLOSED"} + bl_options = {'DEFAULT_CLOSED'} @classmethod def poll(cls, context): @@ -467,9 +442,9 @@ class FalloffPanel(BrushPanel): settings = cls.paint_settings(context) if not (settings and settings.brush and settings.brush.curve): return False - if cls.get_brush_mode(context) == "SCULPT_CURVES": + if cls.get_brush_mode(context) == 'SCULPT_CURVES': brush = settings.brush - if brush.curves_sculpt_tool in {"ADD", "DELETE"}: + if brush.curves_sculpt_tool in {'ADD', 'DELETE'}: return False return True @@ -486,35 +461,22 @@ class FalloffPanel(BrushPanel): row = col.row(align=True) row.prop(brush, "curve_preset", text="") - if brush.curve_preset == "CUSTOM": + if brush.curve_preset == 'CUSTOM': layout.template_curve_mapping(brush, "curve", brush=True) col = layout.column(align=True) row = col.row(align=True) - row.operator( - "brush.curve_preset", icon="SMOOTHCURVE", text="" - ).shape = "SMOOTH" - row.operator( - "brush.curve_preset", icon="SPHERECURVE", text="" - ).shape = "ROUND" - row.operator("brush.curve_preset", icon="ROOTCURVE", text="").shape = "ROOT" - row.operator( - "brush.curve_preset", icon="SHARPCURVE", text="" - ).shape = "SHARP" - row.operator("brush.curve_preset", icon="LINCURVE", text="").shape = "LINE" - row.operator("brush.curve_preset", icon="NOCURVE", text="").shape = "MAX" + row.operator("brush.curve_preset", icon='SMOOTHCURVE', text="").shape = 'SMOOTH' + row.operator("brush.curve_preset", icon='SPHERECURVE', text="").shape = 'ROUND' + row.operator("brush.curve_preset", icon='ROOTCURVE', text="").shape = 'ROOT' + row.operator("brush.curve_preset", icon='SHARPCURVE', text="").shape = 'SHARP' + row.operator("brush.curve_preset", icon='LINCURVE', text="").shape = 'LINE' + row.operator("brush.curve_preset", icon='NOCURVE', text="").shape = 'MAX' show_fallof_shape = False - if ( - mode in {"SCULPT", "PAINT_VERTEX", "PAINT_WEIGHT"} - and brush.sculpt_tool != "POSE" - ): + if mode in {'SCULPT', 'PAINT_VERTEX', 'PAINT_WEIGHT'} and brush.sculpt_tool != 'POSE': show_fallof_shape = True - if ( - not show_fallof_shape - and mode == "SCULPT_CURVES" - and context.space_data.type == "PROPERTIES" - ): + if not show_fallof_shape and mode == 'SCULPT_CURVES' and context.space_data.type == 'PROPERTIES': show_fallof_shape = True if show_fallof_shape: @@ -527,7 +489,7 @@ class FalloffPanel(BrushPanel): class DisplayPanel(BrushPanel): bl_label = "Brush Cursor" - bl_options = {"DEFAULT_CLOSED"} + bl_options = {'DEFAULT_CLOSED'} def draw_header(self, context): settings = self.paint_settings(context) @@ -554,64 +516,37 @@ class DisplayPanel(BrushPanel): col.active = brush.brush_capabilities.has_overlay and settings.show_brush col.prop(brush, "cursor_color_add", text="Cursor Color") - if mode == "SCULPT" and brush.sculpt_capabilities.has_secondary_color: + if mode == 'SCULPT' and brush.sculpt_capabilities.has_secondary_color: col.prop(brush, "cursor_color_subtract", text="Inverse Color") col.separator() row = col.row(align=True) row.prop(brush, "cursor_overlay_alpha", text="Falloff Opacity") + row.prop(brush, "use_cursor_overlay_override", toggle=True, text="", icon='BRUSH_DATA') row.prop( - brush, - "use_cursor_overlay_override", - toggle=True, - text="", - icon="BRUSH_DATA", - ) - row.prop( - brush, - "use_cursor_overlay", - text="", - toggle=True, - icon="HIDE_OFF" if brush.use_cursor_overlay else "HIDE_ON", + brush, "use_cursor_overlay", text="", toggle=True, + icon='HIDE_OFF' if brush.use_cursor_overlay else 'HIDE_ON', ) - if mode in {"PAINT_2D", "PAINT_TEXTURE", "PAINT_VERTEX", "SCULPT"}: + if mode in {'PAINT_2D', 'PAINT_TEXTURE', 'PAINT_VERTEX', 'SCULPT'}: row = col.row(align=True) row.prop(brush, "texture_overlay_alpha", text="Texture Opacity") - row.prop( - brush, - "use_primary_overlay_override", - toggle=True, - text="", - icon="BRUSH_DATA", - ) - if tex_slot.map_mode != "STENCIL": + row.prop(brush, "use_primary_overlay_override", toggle=True, text="", icon='BRUSH_DATA') + if tex_slot.map_mode != 'STENCIL': row.prop( - brush, - "use_primary_overlay", - text="", - toggle=True, - icon="HIDE_OFF" if brush.use_primary_overlay else "HIDE_ON", + brush, "use_primary_overlay", text="", toggle=True, + icon='HIDE_OFF' if brush.use_primary_overlay else 'HIDE_ON', ) - if mode in {"PAINT_TEXTURE", "PAINT_2D"}: + if mode in {'PAINT_TEXTURE', 'PAINT_2D'}: row = col.row(align=True) row.prop(brush, "mask_overlay_alpha", text="Mask Texture Opacity") - row.prop( - brush, - "use_secondary_overlay_override", - toggle=True, - text="", - icon="BRUSH_DATA", - ) - if tex_slot_mask.map_mode != "STENCIL": + row.prop(brush, "use_secondary_overlay_override", toggle=True, text="", icon='BRUSH_DATA') + if tex_slot_mask.map_mode != 'STENCIL': row.prop( - brush, - "use_secondary_overlay", - text="", - toggle=True, - icon="HIDE_OFF" if brush.use_secondary_overlay else "HIDE_ON", + brush, "use_secondary_overlay", text="", toggle=True, + icon='HIDE_OFF' if brush.use_secondary_overlay else 'HIDE_ON', ) @@ -622,16 +557,14 @@ class VIEW3D_MT_tools_projectpaint_clone(Menu): layout = self.layout for i, uv_layer in enumerate(context.active_object.data.uv_layers): - props = layout.operator( - "wm.context_set_int", text=uv_layer.name, translate=False - ) + props = layout.operator("wm.context_set_int", text=uv_layer.name, translate=False) props.data_path = "active_object.data.uv_layer_clone_index" props.value = i def brush_settings(layout, context, brush, popover=False): - """Draw simple brush settings for Sculpt, - Texture/Vertex/Weight Paint modes, or skip certain settings for the popover""" + """ Draw simple brush settings for Sculpt, + Texture/Vertex/Weight Paint modes, or skip certain settings for the popover """ mode = UnifiedPaintPanel.get_brush_mode(context) @@ -639,17 +572,14 @@ def brush_settings(layout, context, brush, popover=False): brush_shared_settings(layout, context, brush, popover) # Sculpt Mode # - if mode == "SCULPT": + if mode == 'SCULPT': capabilities = brush.sculpt_capabilities sculpt_tool = brush.sculpt_tool # normal_radius_factor layout.prop(brush, "normal_radius_factor", slider=True) - if ( - context.preferences.experimental.use_sculpt_tools_tilt - and capabilities.has_tilt - ): + if context.preferences.experimental.use_sculpt_tools_tilt and capabilities.has_tilt: layout.prop(brush, "tilt_strength_factor", slider=True) row = layout.row(align=True) @@ -672,7 +602,7 @@ def brush_settings(layout, context, brush, popover=False): slider=True, ) - if capabilities.has_auto_smooth or brush.sculpt_tool == "SMOOTH": + if capabilities.has_auto_smooth or brush.sculpt_tool == 'SMOOTH': UnifiedPaintPanel.prop_unified( layout, context, @@ -683,7 +613,7 @@ def brush_settings(layout, context, brush, popover=False): ) is_smooth = capabilities.has_auto_smooth - is_smooth = is_smooth or brush.sculpt_tool in {"SMOOTH", "SIMPLIFY"} + is_smooth = is_smooth or brush.sculpt_tool in {'SMOOTH', 'SIMPLIFY'} hard_edge = context.tool_settings.unified_paint_settings.hard_edge_mode if is_smooth: @@ -697,13 +627,13 @@ def brush_settings(layout, context, brush, popover=False): text="Corner Pin", ) - if capabilities.has_auto_smooth or brush.sculpt_tool == "SMOOTH": + if capabilities.has_auto_smooth or brush.sculpt_tool == 'SMOOTH': layout.prop(brush, "use_weighted_smooth") # topology_rake_factor if ( - capabilities.has_topology_rake - and context.sculpt_object.use_dynamic_topology_sculpting + capabilities.has_topology_rake and + context.sculpt_object.use_dynamic_topology_sculpting ): layout.prop(brush, "topology_rake_factor", slider=True) layout.prop(brush, "use_curvature_rake") @@ -715,7 +645,7 @@ def brush_settings(layout, context, brush, popover=False): # crease_pinch_factor if capabilities.has_pinch_factor: text = "Pinch" - if sculpt_tool in {"BLOB", "SNAKE_HOOK"}: + if sculpt_tool in {'BLOB', 'SNAKE_HOOK'}: text = "Magnify" layout.prop(brush, "crease_pinch_factor", slider=True, text=text) @@ -758,34 +688,30 @@ def brush_settings(layout, context, brush, popover=False): ups = context.scene.tool_settings.unified_paint_settings row = layout.row(align=True) UnifiedPaintPanel.prop_unified_color(row, context, brush, "color", text="") - UnifiedPaintPanel.prop_unified_color( - row, context, brush, "secondary_color", text="" - ) + UnifiedPaintPanel.prop_unified_color(row, context, brush, "secondary_color", text="") row.separator() - row.operator( - "paint.brush_colors_flip", icon="FILE_REFRESH", text="", emboss=False - ) - row.prop(ups, "use_unified_color", text="", icon="BRUSHES_ALL") + row.operator("paint.brush_colors_flip", icon='FILE_REFRESH', text="", emboss=False) + row.prop(ups, "use_unified_color", text="", icon='BRUSHES_ALL') layout.prop(brush, "blend", text="Blend Mode") # Per sculpt tool options. - if sculpt_tool == "CLAY_STRIPS": + if sculpt_tool == 'CLAY_STRIPS': row = layout.row() row.prop(brush, "tip_roundness") - elif sculpt_tool == "ELASTIC_DEFORM": + elif sculpt_tool == 'ELASTIC_DEFORM': layout.separator() layout.prop(brush, "elastic_deform_type") layout.prop(brush, "elastic_deform_volume_preservation", slider=True) layout.separator() - elif sculpt_tool == "SNAKE_HOOK": + elif sculpt_tool == 'SNAKE_HOOK': layout.separator() layout.prop(brush, "snake_hook_deform_type") layout.separator() - elif sculpt_tool == "POSE": + elif sculpt_tool == 'POSE': layout.separator() layout.prop(brush, "deform_target") layout.separator() @@ -793,12 +719,9 @@ def brush_settings(layout, context, brush, popover=False): layout.prop(brush, "pose_origin_type") layout.prop(brush, "pose_offset") layout.prop(brush, "pose_smooth_iterations") - if brush.pose_deform_type == "ROTATE_TWIST" and brush.pose_origin_type in { - "TOPOLOGY", - "FACE_SETS", - }: + if brush.pose_deform_type == 'ROTATE_TWIST' and brush.pose_origin_type in {'TOPOLOGY', 'FACE_SETS'}: layout.prop(brush, "pose_ik_segments") - if brush.pose_deform_type == "SCALE_TRANSLATE": + if brush.pose_deform_type == 'SCALE_TRANSLATE': layout.prop(brush, "use_pose_lock_rotation") layout.prop(brush, "use_pose_ik_anchored") layout.prop(brush, "use_connected_only") @@ -806,14 +729,14 @@ def brush_settings(layout, context, brush, popover=False): layout.separator() - elif sculpt_tool == "CLOTH": + elif sculpt_tool == 'CLOTH': layout.separator() layout.prop(brush, "cloth_simulation_area_type") - if brush.cloth_simulation_area_type != "GLOBAL": + if brush.cloth_simulation_area_type != 'GLOBAL': layout.prop(brush, "cloth_sim_limit") layout.prop(brush, "cloth_sim_falloff") - if brush.cloth_simulation_area_type == "LOCAL": + if brush.cloth_simulation_area_type == 'LOCAL': layout.prop(brush, "use_cloth_pin_simulation_boundary") layout.separator() @@ -827,25 +750,25 @@ def brush_settings(layout, context, brush, popover=False): layout.prop(brush, "use_cloth_collision") layout.separator() - elif sculpt_tool == "SCRAPE": + elif sculpt_tool == 'SCRAPE': row = layout.row(align=True) row.prop(brush, "area_radius_factor") row.prop(brush, "use_pressure_area_radius", text="") row = layout.row() row.prop(brush, "invert_to_scrape_fill", text="Invert to Fill") - elif sculpt_tool == "FILL": + elif sculpt_tool == 'FILL': row = layout.row(align=True) row.prop(brush, "area_radius_factor") row.prop(brush, "use_pressure_area_radius", text="") row = layout.row() row.prop(brush, "invert_to_scrape_fill", text="Invert to Scrape") - elif sculpt_tool == "GRAB": + elif sculpt_tool == 'GRAB': layout.prop(brush, "use_grab_active_vertex") layout.prop(brush, "use_grab_silhouette") - elif sculpt_tool == "PAINT": + elif sculpt_tool == 'PAINT': row = layout.row(align=True) row.prop(brush, "flow") row.prop(brush, "invert_flow_pressure", text="") @@ -875,11 +798,11 @@ def brush_settings(layout, context, brush, popover=False): row = layout.row() row.prop(brush, "tip_scale_x") - elif sculpt_tool == "SMEAR": + elif sculpt_tool == 'SMEAR': col = layout.column() col.prop(brush, "smear_deform_type") - elif sculpt_tool == "BOUNDARY": + elif sculpt_tool == 'BOUNDARY': layout.prop(brush, "deform_target") layout.separator() col = layout.column() @@ -887,58 +810,52 @@ def brush_settings(layout, context, brush, popover=False): col.prop(brush, "boundary_falloff_type") col.prop(brush, "boundary_offset") - elif sculpt_tool == "TOPOLOGY": + elif sculpt_tool == 'TOPOLOGY': col = layout.column() col.prop(brush, "slide_deform_type") - elif sculpt_tool == "MULTIPLANE_SCRAPE": + elif sculpt_tool == 'MULTIPLANE_SCRAPE': col = layout.column() col.prop(brush, "multiplane_scrape_angle") col.prop(brush, "use_multiplane_scrape_dynamic") col.prop(brush, "show_multiplane_scrape_planes_preview") - elif sculpt_tool == "SMOOTH": + elif sculpt_tool == 'SMOOTH': col = layout.column() col.prop(brush, "smooth_deform_type") - if brush.smooth_deform_type == "SURFACE": + if brush.smooth_deform_type == 'SURFACE': col.prop(brush, "surface_smooth_shape_preservation") col.prop(brush, "surface_smooth_current_vertex") col.prop(brush, "surface_smooth_iterations") - elif sculpt_tool == "DISPLACEMENT_SMEAR": + elif sculpt_tool == 'DISPLACEMENT_SMEAR': col = layout.column() col.prop(brush, "smear_deform_type") - elif sculpt_tool == "MASK": + elif sculpt_tool == 'MASK': layout.row().prop(brush, "mask_tool", expand=True) # End sculpt_tool interface. # 3D and 2D Texture Paint Mode. - elif mode in {"PAINT_TEXTURE", "PAINT_2D"}: + elif mode in {'PAINT_TEXTURE', 'PAINT_2D'}: capabilities = brush.image_paint_capabilities - if brush.image_tool == "FILL": + if brush.image_tool == 'FILL': # For some reason fill threshold only appears to be implemented in 2D paint. - if brush.color_type == "COLOR": - if mode == "PAINT_2D": - layout.prop( - brush, "fill_threshold", text="Fill Threshold", slider=True - ) - elif brush.color_type == "GRADIENT": + if brush.color_type == 'COLOR': + if mode == 'PAINT_2D': + layout.prop(brush, "fill_threshold", text="Fill Threshold", slider=True) + elif brush.color_type == 'GRADIENT': layout.row().prop(brush, "gradient_fill_mode", expand=True) - elif mode == "SCULPT_CURVES": - if brush.curves_sculpt_tool == "ADD": + elif mode == 'SCULPT_CURVES': + if brush.curves_sculpt_tool == 'ADD': layout.prop(brush.curves_sculpt_settings, "add_amount") col = layout.column(heading="Interpolate", align=True) col.prop(brush.curves_sculpt_settings, "interpolate_length", text="Length") col.prop(brush.curves_sculpt_settings, "interpolate_shape", text="Shape") - col.prop( - brush.curves_sculpt_settings, - "interpolate_point_count", - text="Point Count", - ) + col.prop(brush.curves_sculpt_settings, "interpolate_point_count", text="Point Count") col = layout.column() col.active = not brush.curves_sculpt_settings.interpolate_length @@ -947,13 +864,13 @@ def brush_settings(layout, context, brush, popover=False): col = layout.column() col.active = not brush.curves_sculpt_settings.interpolate_point_count col.prop(brush.curves_sculpt_settings, "points_per_curve") - elif brush.curves_sculpt_tool == "GROW_SHRINK": + elif brush.curves_sculpt_tool == 'GROW_SHRINK': layout.prop(brush.curves_sculpt_settings, "scale_uniform") layout.prop(brush.curves_sculpt_settings, "minimum_length") def brush_shared_settings(layout, context, brush, popover=False): - """Draw simple brush settings that are shared between different paint modes.""" + """ Draw simple brush settings that are shared between different paint modes. """ mode = UnifiedPaintPanel.get_brush_mode(context) @@ -967,14 +884,14 @@ def brush_shared_settings(layout, context, brush, popover=False): direction = False # 3D and 2D Texture Paint # - if mode in {"PAINT_TEXTURE", "PAINT_2D"}: + if mode in {'PAINT_TEXTURE', 'PAINT_2D'}: if not popover: blend_mode = brush.image_paint_capabilities.has_color size = brush.image_paint_capabilities.has_radius strength = strength_pressure = True # Sculpt # - if mode == "SCULPT": + if mode == 'SCULPT': size_mode = True if not popover: size = True @@ -983,7 +900,7 @@ def brush_shared_settings(layout, context, brush, popover=False): direction = not brush.sculpt_capabilities.has_direction # Vertex Paint # - if mode == "PAINT_VERTEX": + if mode == 'PAINT_VERTEX': if not popover: blend_mode = True size = True @@ -991,25 +908,25 @@ def brush_shared_settings(layout, context, brush, popover=False): strength_pressure = True # Weight Paint # - if mode == "PAINT_WEIGHT": + if mode == 'PAINT_WEIGHT': if not popover: size = True weight = brush.weight_paint_capabilities.has_weight strength = strength_pressure = True # Only draw blend mode for the Draw tool, because for other tools it is pointless. D5928#137944 - if brush.weight_tool == "DRAW": + if brush.weight_tool == 'DRAW': blend_mode = True # UV Sculpt # - if mode == "UV_SCULPT": + if mode == 'UV_SCULPT': size = True strength = True # Sculpt Curves # - if mode == "SCULPT_CURVES": + if mode == 'SCULPT_CURVES': size = True strength = True - direction = brush.curves_sculpt_tool in {"GROW_SHRINK", "SELECTION_PAINT"} + direction = brush.curves_sculpt_tool in {'GROW_SHRINK', 'SELECTION_PAINT'} ### Draw settings. ### ups = context.scene.tool_settings.unified_paint_settings @@ -1030,7 +947,7 @@ def brush_shared_settings(layout, context, brush, popover=False): size_owner = ups if ups.use_unified_size else brush size_prop = "size" - if size_mode and (size_owner.use_locked_size == "SCENE"): + if size_mode and (size_owner.use_locked_size == 'SCENE'): size_prop = "unprojected_radius" if size or size_mode: if size: @@ -1080,7 +997,7 @@ def brush_settings_advanced(layout, context, brush, popover=False): use_accumulate = False use_frontface = False - if mode == "SCULPT": + if mode == 'SCULPT': sculpt = context.tool_settings.sculpt capabilities = brush.sculpt_capabilities use_accumulate = capabilities.has_accumulate @@ -1099,10 +1016,7 @@ def brush_settings_advanced(layout, context, brush, popover=False): col.prop(brush, "use_automasking_boundary_edges", text="Mesh Boundary") col.prop(brush, "use_automasking_boundary_face_sets", text="Face Sets Boundary") - if ( - brush.use_automasking_boundary_edges - or brush.use_automasking_boundary_face_sets - ): + if brush.use_automasking_boundary_edges or brush.use_automasking_boundary_face_sets: col = layout.column() col.use_property_split = False split = col.split(factor=0.4) @@ -1115,13 +1029,11 @@ def brush_settings_advanced(layout, context, brush, popover=False): row = col.row() row.prop(brush, "use_automasking_cavity", text="Cavity") - is_cavity_active = ( - brush.use_automasking_cavity or brush.use_automasking_cavity_inverted - ) + is_cavity_active = brush.use_automasking_cavity or brush.use_automasking_cavity_inverted if is_cavity_active: props = row.operator("sculpt.mask_from_cavity", text="Create Mask") - props.settings_source = "BRUSH" + props.settings_source = 'BRUSH' col.prop(brush, "use_automasking_cavity_inverted", text="Cavity (inverted)") @@ -1167,42 +1079,42 @@ def brush_settings_advanced(layout, context, brush, popover=False): layout.separator() # 3D and 2D Texture Paint. - elif mode in {"PAINT_TEXTURE", "PAINT_2D"}: + elif mode in {'PAINT_TEXTURE', 'PAINT_2D'}: capabilities = brush.image_paint_capabilities use_accumulate = capabilities.has_accumulate - if mode == "PAINT_2D": + if mode == 'PAINT_2D': layout.prop(brush, "use_paint_antialiasing") else: layout.prop(brush, "use_alpha") # Tool specific settings - if brush.image_tool == "SOFTEN": + if brush.image_tool == 'SOFTEN': layout.separator() layout.row().prop(brush, "direction", expand=True) layout.prop(brush, "sharp_threshold") - if mode == "PAINT_2D": + if mode == 'PAINT_2D': layout.prop(brush, "blur_kernel_radius") layout.prop(brush, "blur_mode") - elif brush.image_tool == "MASK": + elif brush.image_tool == 'MASK': layout.prop(brush, "weight", text="Mask Value", slider=True) - elif brush.image_tool == "CLONE": - if mode == "PAINT_2D": + elif brush.image_tool == 'CLONE': + if mode == 'PAINT_2D': layout.prop(brush, "clone_image", text="Image") layout.prop(brush, "clone_alpha", text="Alpha") # Vertex Paint # - elif mode == "PAINT_VERTEX": + elif mode == 'PAINT_VERTEX': layout.prop(brush, "use_alpha") - if brush.vertex_tool != "SMEAR": + if brush.vertex_tool != 'SMEAR': use_accumulate = True use_frontface = True # Weight Paint - elif mode == "PAINT_WEIGHT": - if brush.weight_tool != "SMEAR": + elif mode == 'PAINT_WEIGHT': + if brush.weight_tool != 'SMEAR': use_accumulate = True use_frontface = True @@ -1224,30 +1136,24 @@ def draw_color_settings(context, layout, brush, color_type=False): row.prop(brush, "color_type", expand=True) # Color wheel - if brush.color_type == "COLOR": - UnifiedPaintPanel.prop_unified_color_picker( - layout, context, brush, "color", value_slider=True - ) + if brush.color_type == 'COLOR': + UnifiedPaintPanel.prop_unified_color_picker(layout, context, brush, "color", value_slider=True) row = layout.row(align=True) UnifiedPaintPanel.prop_unified_color(row, context, brush, "color", text="") - UnifiedPaintPanel.prop_unified_color( - row, context, brush, "secondary_color", text="" - ) + UnifiedPaintPanel.prop_unified_color(row, context, brush, "secondary_color", text="") row.separator() - row.operator( - "paint.brush_colors_flip", icon="FILE_REFRESH", text="", emboss=False - ) - row.prop(ups, "use_unified_color", text="", icon="BRUSHES_ALL") + row.operator("paint.brush_colors_flip", icon='FILE_REFRESH', text="", emboss=False) + row.prop(ups, "use_unified_color", text="", icon='BRUSHES_ALL') # Gradient - elif brush.color_type == "GRADIENT": + elif brush.color_type == 'GRADIENT': layout.template_color_ramp(brush, "gradient", expand=True) layout.use_property_split = True col = layout.column() - if brush.image_tool == "DRAW": + if brush.image_tool == 'DRAW': UnifiedPaintPanel.prop_unified( col, context, @@ -1259,7 +1165,7 @@ def draw_color_settings(context, layout, brush, color_type=False): ) col.prop(brush, "gradient_stroke_mode", text="Gradient Mapping") - if brush.gradient_stroke_mode in {"SPACING_REPEAT", "SPACING_CLAMP"}: + if brush.gradient_stroke_mode in {'SPACING_REPEAT', 'SPACING_CLAMP'}: col.prop(brush, "grad_spacing") @@ -1275,8 +1181,8 @@ def brush_texture_settings(layout, brush, sculpt): layout.separator() - if tex_slot.map_mode == "STENCIL": - if brush.texture and brush.texture.type == "IMAGE": + if tex_slot.map_mode == 'STENCIL': + if brush.texture and brush.texture.type == 'IMAGE': layout.operator("brush.stencil_fit_image_aspect") layout.operator("brush.stencil_reset_transform") @@ -1287,10 +1193,7 @@ def brush_texture_settings(layout, brush, sculpt): if tex_slot.has_texture_angle_source: col.prop(tex_slot, "use_rake", text="Rake") - if ( - brush.brush_capabilities.has_random_texture_angle - and tex_slot.has_random_texture_angle - ): + if brush.brush_capabilities.has_random_texture_angle and tex_slot.has_random_texture_angle: if sculpt: if brush.sculpt_capabilities.has_random_texture_angle: col.prop(tex_slot, "use_random", text="Random") @@ -1309,9 +1212,9 @@ def brush_texture_settings(layout, brush, sculpt): # texture_sample_bias layout.prop(brush, "texture_sample_bias", slider=True, text="Sample Bias") - if brush.sculpt_tool == "DRAW": + if brush.sculpt_tool == 'DRAW': col = layout.column() - col.active = tex_slot.map_mode == "AREA_PLANE" + col.active = tex_slot.map_mode == 'AREA_PLANE' col.prop(brush, "use_color_as_displacement", text="Vector Displacement") @@ -1324,8 +1227,8 @@ def brush_mask_texture_settings(layout, brush): # map_mode layout.row().prop(mask_tex_slot, "mask_map_mode", text="Mask Mapping") - if mask_tex_slot.map_mode == "STENCIL": - if brush.mask_texture and brush.mask_texture.type == "IMAGE": + if mask_tex_slot.map_mode == 'STENCIL': + if brush.mask_texture and brush.mask_texture.type == 'IMAGE': layout.operator("brush.stencil_fit_image_aspect").mask = True layout.operator("brush.stencil_reset_transform").mask = True @@ -1338,10 +1241,7 @@ def brush_mask_texture_settings(layout, brush): if mask_tex_slot.has_texture_angle_source: col.prop(mask_tex_slot, "use_rake", text="Rake") - if ( - brush.brush_capabilities.has_random_texture_angle - and mask_tex_slot.has_random_texture_angle - ): + if brush.brush_capabilities.has_random_texture_angle and mask_tex_slot.has_random_texture_angle: col.prop(mask_tex_slot, "use_random", text="Random") if mask_tex_slot.use_random: col.prop(mask_tex_slot, "random_angle", text="Random Angle") @@ -1359,9 +1259,7 @@ def brush_basic_texpaint_settings(layout, context, brush, *, compact=False): row = layout.row(align=True) row.ui_units_x = 4 UnifiedPaintPanel.prop_unified_color(row, context, brush, "color", text="") - UnifiedPaintPanel.prop_unified_color( - row, context, brush, "secondary_color", text="" - ) + UnifiedPaintPanel.prop_unified_color(row, context, brush, "secondary_color", text="") row.separator() layout.prop(brush, "blend", text="" if compact else "Blend") @@ -1404,7 +1302,7 @@ def brush_basic__draw_color_selector(context, layout, brush, gp_settings, props) txt_ma = ma.name maxw = 25 if len(txt_ma) > maxw: - txt_ma = txt_ma[: maxw - 5] + ".." + txt_ma[-3:] + txt_ma = txt_ma[:maxw - 5] + '..' + txt_ma[-3:] sub = row.row(align=True) sub.enabled = not gp_settings.use_material_pin @@ -1417,37 +1315,20 @@ def brush_basic__draw_color_selector(context, layout, brush, gp_settings, props) row.prop(gp_settings, "use_material_pin", text="") - if brush.gpencil_tool in {"DRAW", "FILL"}: + if brush.gpencil_tool in {'DRAW', 'FILL'}: row.separator(factor=1.0) sub_row = row.row(align=True) sub_row.enabled = not gp_settings.pin_draw_mode if gp_settings.pin_draw_mode: - sub_row.prop_enum( - gp_settings, "brush_draw_mode", "MATERIAL", text="", icon="MATERIAL" - ) - sub_row.prop_enum( - gp_settings, - "brush_draw_mode", - "VERTEXCOLOR", - text="", - icon="VPAINT_HLT", - ) + sub_row.prop_enum(gp_settings, "brush_draw_mode", 'MATERIAL', text="", icon='MATERIAL') + sub_row.prop_enum(gp_settings, "brush_draw_mode", 'VERTEXCOLOR', text="", icon='VPAINT_HLT') else: - sub_row.prop_enum( - settings, "color_mode", "MATERIAL", text="", icon="MATERIAL" - ) - sub_row.prop_enum( - settings, "color_mode", "VERTEXCOLOR", text="", icon="VPAINT_HLT" - ) + sub_row.prop_enum(settings, "color_mode", 'MATERIAL', text="", icon='MATERIAL') + sub_row.prop_enum(settings, "color_mode", 'VERTEXCOLOR', text="", icon='VPAINT_HLT') sub_row = row.row(align=True) - sub_row.enabled = ( - settings.color_mode == "VERTEXCOLOR" - or gp_settings.brush_draw_mode == "VERTEXCOLOR" - ) - sub_row.prop_with_popover( - brush, "color", text="", panel="TOPBAR_PT_gpencil_vertexcolor" - ) + sub_row.enabled = settings.color_mode == 'VERTEXCOLOR' or gp_settings.brush_draw_mode == 'VERTEXCOLOR' + sub_row.prop_with_popover(brush, "color", text="", panel="TOPBAR_PT_gpencil_vertexcolor") row.prop(gp_settings, "pin_draw_mode", text="") if props: @@ -1464,28 +1345,26 @@ def brush_basic_gpencil_paint_settings(layout, context, brush, *, compact=False) return # Brush details - if brush.gpencil_tool == "ERASE": + if brush.gpencil_tool == 'ERASE': row = layout.row(align=True) row.prop(brush, "size", text="Radius") - row.prop(gp_settings, "use_pressure", text="", icon="STYLUS_PRESSURE") - row.prop(gp_settings, "use_occlude_eraser", text="", icon="XRAY") + row.prop(gp_settings, "use_pressure", text="", icon='STYLUS_PRESSURE') + row.prop(gp_settings, "use_occlude_eraser", text="", icon='XRAY') row.prop(gp_settings, "use_default_eraser", text="") row = layout.row(align=True) row.prop(gp_settings, "eraser_mode", expand=True) - if gp_settings.eraser_mode == "SOFT": + if gp_settings.eraser_mode == 'SOFT': row = layout.row(align=True) row.prop(gp_settings, "pen_strength", slider=True) - row.prop( - gp_settings, "use_strength_pressure", text="", icon="STYLUS_PRESSURE" - ) + row.prop(gp_settings, "use_strength_pressure", text="", icon='STYLUS_PRESSURE') row = layout.row(align=True) row.prop(gp_settings, "eraser_strength_factor") row = layout.row(align=True) row.prop(gp_settings, "eraser_thickness_factor") # FIXME: tools must use their own UI drawing! - elif brush.gpencil_tool == "FILL": + elif brush.gpencil_tool == 'FILL': use_property_split_prev = layout.use_property_split if compact: row = layout.row(align=True) @@ -1506,47 +1385,45 @@ def brush_basic_gpencil_paint_settings(layout, context, brush, *, compact=False) else: # brush.gpencil_tool == 'DRAW/TINT': row = layout.row(align=True) row.prop(brush, "size", text="Radius") - row.prop(gp_settings, "use_pressure", text="", icon="STYLUS_PRESSURE") + row.prop(gp_settings, "use_pressure", text="", icon='STYLUS_PRESSURE') if gp_settings.use_pressure and not compact: col = layout.column() - col.template_curve_mapping( - gp_settings, "curve_sensitivity", brush=True, use_negative_slope=True - ) + col.template_curve_mapping(gp_settings, "curve_sensitivity", brush=True, + use_negative_slope=True) row = layout.row(align=True) row.prop(gp_settings, "pen_strength", slider=True) - row.prop(gp_settings, "use_strength_pressure", text="", icon="STYLUS_PRESSURE") + row.prop(gp_settings, "use_strength_pressure", text="", icon='STYLUS_PRESSURE') if gp_settings.use_strength_pressure and not compact: col = layout.column() - col.template_curve_mapping( - gp_settings, "curve_strength", brush=True, use_negative_slope=True - ) + col.template_curve_mapping(gp_settings, "curve_strength", brush=True, + use_negative_slope=True) - if brush.gpencil_tool == "TINT": + if brush.gpencil_tool == 'TINT': row = layout.row(align=True) row.prop(gp_settings, "vertex_mode", text="Mode") else: row = layout.row(align=True) - if context.region.type == "TOOL_HEADER": + if context.region.type == 'TOOL_HEADER': row.prop(gp_settings, "caps_type", text="", expand=True) else: row.prop(gp_settings, "caps_type", text="Caps Type") # FIXME: tools must use their own UI drawing! if tool.idname in { - "builtin.arc", - "builtin.curve", - "builtin.line", - "builtin.box", - "builtin.circle", - "builtin.polyline", + "builtin.arc", + "builtin.curve", + "builtin.line", + "builtin.box", + "builtin.circle", + "builtin.polyline", }: settings = context.tool_settings.gpencil_sculpt if compact: row = layout.row(align=True) - row.prop(settings, "use_thickness_curve", text="", icon="SPHERECURVE") + row.prop(settings, "use_thickness_curve", text="", icon='SPHERECURVE') sub = row.row(align=True) sub.active = settings.use_thickness_curve sub.popover( @@ -1559,9 +1436,7 @@ def brush_basic_gpencil_paint_settings(layout, context, brush, *, compact=False) sub = row.row(align=True) if settings.use_thickness_curve: # Curve - layout.template_curve_mapping( - settings, "thickness_primitive_curve", brush=True - ) + layout.template_curve_mapping(settings, "thickness_primitive_curve", brush=True) def brush_basic_gpencil_sculpt_settings(layout, _context, brush, *, compact=False): @@ -1575,7 +1450,7 @@ def brush_basic_gpencil_sculpt_settings(layout, _context, brush, *, compact=Fals row = layout.row(align=True) row.prop(brush, "size", slider=True) sub = row.row(align=True) - sub.enabled = tool not in {"GRAB", "CLONE"} + sub.enabled = tool not in {'GRAB', 'CLONE'} sub.prop(gp_settings, "use_pressure", text="") row = layout.row(align=True) @@ -1583,22 +1458,22 @@ def brush_basic_gpencil_sculpt_settings(layout, _context, brush, *, compact=Fals row.prop(brush, "use_pressure_strength", text="") if compact: - if tool in {"THICKNESS", "STRENGTH", "PINCH", "TWIST"}: + if tool in {'THICKNESS', 'STRENGTH', 'PINCH', 'TWIST'}: row.separator() row.prop(gp_settings, "direction", expand=True, text="") else: use_property_split_prev = layout.use_property_split layout.use_property_split = False - if tool in {"THICKNESS", "STRENGTH"}: + if tool in {'THICKNESS', 'STRENGTH'}: layout.row().prop(gp_settings, "direction", expand=True) - elif tool == "PINCH": + elif tool == 'PINCH': row = layout.row(align=True) - row.prop_enum(gp_settings, "direction", value="ADD", text="Pinch") - row.prop_enum(gp_settings, "direction", value="SUBTRACT", text="Inflate") - elif tool == "TWIST": + row.prop_enum(gp_settings, "direction", value='ADD', text="Pinch") + row.prop_enum(gp_settings, "direction", value='SUBTRACT', text="Inflate") + elif tool == 'TWIST': row = layout.row(align=True) - row.prop_enum(gp_settings, "direction", value="ADD", text="CCW") - row.prop_enum(gp_settings, "direction", value="SUBTRACT", text="CW") + row.prop_enum(gp_settings, "direction", value='ADD', text="CCW") + row.prop_enum(gp_settings, "direction", value='SUBTRACT', text="CW") layout.use_property_split = use_property_split_prev @@ -1609,13 +1484,11 @@ def brush_basic_gpencil_weight_settings(layout, _context, brush, *, compact=Fals row.prop(brush, "strength", slider=True) row.prop(brush, "use_pressure_strength", text="") - if brush.gpencil_weight_tool in {"WEIGHT"}: + if brush.gpencil_weight_tool in {'WEIGHT'}: layout.prop(brush, "weight", slider=True) gp_settings = brush.gpencil_settings - layout.prop( - gp_settings, "direction", expand=True, text="" if compact else "Direction" - ) + layout.prop(gp_settings, "direction", expand=True, text="" if compact else "Direction") def brush_basic_gpencil_vertex_settings(layout, _context, brush, *, compact=False): @@ -1624,22 +1497,23 @@ def brush_basic_gpencil_vertex_settings(layout, _context, brush, *, compact=Fals # Brush details row = layout.row(align=True) row.prop(brush, "size", text="Radius") - row.prop(gp_settings, "use_pressure", text="", icon="STYLUS_PRESSURE") + row.prop(gp_settings, "use_pressure", text="", icon='STYLUS_PRESSURE') - if brush.gpencil_vertex_tool in {"DRAW", "BLUR", "SMEAR"}: + if brush.gpencil_vertex_tool in {'DRAW', 'BLUR', 'SMEAR'}: row = layout.row(align=True) row.prop(gp_settings, "pen_strength", slider=True) - row.prop(gp_settings, "use_strength_pressure", text="", icon="STYLUS_PRESSURE") + row.prop(gp_settings, "use_strength_pressure", text="", icon='STYLUS_PRESSURE') - if brush.gpencil_vertex_tool in {"DRAW", "REPLACE"}: + if brush.gpencil_vertex_tool in {'DRAW', 'REPLACE'}: row = layout.row(align=True) row.prop(gp_settings, "vertex_mode", text="Mode") -classes = (VIEW3D_MT_tools_projectpaint_clone,) +classes = ( + VIEW3D_MT_tools_projectpaint_clone, +) if __name__ == "__main__": # only for live edit. from bpy.utils import register_class - for cls in classes: register_class(cls) diff --git a/source/blender/blenkernel/intern/customdata.cc b/source/blender/blenkernel/intern/customdata.cc index 3369d5e2464..5e649479da7 100644 --- a/source/blender/blenkernel/intern/customdata.cc +++ b/source/blender/blenkernel/intern/customdata.cc @@ -410,8 +410,7 @@ static void layerCopyValue_normal(const void *source, if (ELEM(mixmode, CDT_MIX_NOMIX, CDT_MIX_REPLACE_ABOVE_THRESHOLD, - CDT_MIX_REPLACE_BELOW_THRESHOLD)) - { + CDT_MIX_REPLACE_BELOW_THRESHOLD)) { /* Above/below threshold modes are not supported here, fallback to nomix (just in case). */ copy_v3_v3(no_dst, no_src); } @@ -888,8 +887,7 @@ static void layerCopyValue_mloopcol(const void *source, if (ELEM(mixmode, CDT_MIX_NOMIX, CDT_MIX_REPLACE_ABOVE_THRESHOLD, - CDT_MIX_REPLACE_BELOW_THRESHOLD)) - { + CDT_MIX_REPLACE_BELOW_THRESHOLD)) { /* Modes that do a full copy or nothing. */ if (ELEM(mixmode, CDT_MIX_REPLACE_ABOVE_THRESHOLD, CDT_MIX_REPLACE_BELOW_THRESHOLD)) { /* TODO: Check for a real valid way to get 'factor' value of our dest color? */ @@ -1320,8 +1318,7 @@ static void layerCopyValue_propcol(const void *source, if (ELEM(mixmode, CDT_MIX_NOMIX, CDT_MIX_REPLACE_ABOVE_THRESHOLD, - CDT_MIX_REPLACE_BELOW_THRESHOLD)) - { + CDT_MIX_REPLACE_BELOW_THRESHOLD)) { /* Modes that do a full copy or nothing. */ if (ELEM(mixmode, CDT_MIX_REPLACE_ABOVE_THRESHOLD, CDT_MIX_REPLACE_BELOW_THRESHOLD)) { /* TODO: Check for a real valid way to get 'factor' value of our dest color? */ @@ -2775,6 +2772,10 @@ static void customData_update_offsets(CustomData *data) int max_alignment = 1; int offset = 0; +#ifdef WITH_ASAN + offset += BM_ASAN_PAD; +#endif + for (const int align : aligns) { for (const int i : IndexRange(data->totlayer)) { CustomDataLayer *layer = data->layers + i; @@ -2792,6 +2793,10 @@ static void customData_update_offsets(CustomData *data) } offset += size; +#ifdef WITH_ASAN + offset += BM_ASAN_PAD; +#endif + max_alignment = max_ii(max_alignment, align); } } -- 2.30.2 From 0e9beeae970a1b68794f542be4982435a673af2c Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Tue, 11 Jul 2023 11:40:40 -0700 Subject: [PATCH 229/279] temp-sculpt-dyntopo: more format fixes --- .../startup/bl_ui/space_toolsystem_toolbar.py | 476 ++++++++---------- 1 file changed, 222 insertions(+), 254 deletions(-) diff --git a/scripts/startup/bl_ui/space_toolsystem_toolbar.py b/scripts/startup/bl_ui/space_toolsystem_toolbar.py index 5564f7b930f..f184945545d 100644 --- a/scripts/startup/bl_ui/space_toolsystem_toolbar.py +++ b/scripts/startup/bl_ui/space_toolsystem_toolbar.py @@ -27,16 +27,15 @@ def kmi_to_string_or_none(kmi): def generate_from_enum_ex( - _context, - *, - idname_prefix, - icon_prefix, - type, - attr, - cursor="DEFAULT", - tooldef_keywords=None, - icon_map=None, - use_separators=True, + _context, *, + idname_prefix, + icon_prefix, + type, + attr, + cursor='DEFAULT', + tooldef_keywords=None, + icon_map=None, + use_separators=True, ): if tooldef_keywords is None: tooldef_keywords = {} @@ -45,7 +44,8 @@ def generate_from_enum_ex( enum_items = getattr( type.bl_rna.properties[attr], - "enum_items_static_ui" if use_separators else "enum_items_static", + "enum_items_static_ui" if use_separators else + "enum_items_static", ) for enum in enum_items: @@ -104,11 +104,12 @@ class _defs_view3d_generic: props = tool.operator_properties("view3d.cursor3d") layout.prop(props, "use_depth") layout.prop(props, "orientation") - return dict( idname="builtin.cursor", label="Cursor", - description=("Set the cursor location, drag to transform"), + description=( + "Set the cursor location, drag to transform" + ), icon="ops.generic.cursor", keymap="3D View Tool: Cursor", draw_settings=draw_settings, @@ -143,7 +144,6 @@ class _defs_view3d_generic: kmi_to_string_or_none(kmi_add), kmi_to_string_or_none(kmi_remove), ) - return dict( idname="builtin.measure", label="Measure", @@ -155,6 +155,7 @@ class _defs_view3d_generic: class _defs_annotate: + def draw_settings_common(context, layout, tool): gpd = context.annotation_data region_type = context.region.type @@ -164,15 +165,15 @@ class _defs_annotate: text = gpd.layers.active_note maxw = 25 if len(text) > maxw: - text = text[: maxw - 5] + ".." + text[-3:] + text = text[:maxw - 5] + '..' + text[-3:] else: text = "" gpl = context.active_annotation_layer if gpl is not None: layout.label(text="Annotation:") - if context.space_data.type in {"VIEW_3D", "SEQUENCE_EDITOR"}: - if region_type == "TOOL_HEADER": + if context.space_data.type in {'VIEW_3D', 'SEQUENCE_EDITOR'}: + if region_type == 'TOOL_HEADER': sub = layout.split(align=True, factor=0.5) sub.ui_units_x = 6.5 sub.prop(gpl, "color", text="") @@ -189,30 +190,21 @@ class _defs_annotate: space_type = tool.space_type tool_settings = context.tool_settings - if space_type == "VIEW_3D": + if space_type == 'VIEW_3D': row = layout.row(align=True) - row.prop( - tool_settings, "annotation_stroke_placement_view3d", text="Placement" - ) - if tool_settings.gpencil_stroke_placement_view3d == "CURSOR": + row.prop(tool_settings, "annotation_stroke_placement_view3d", text="Placement") + if tool_settings.gpencil_stroke_placement_view3d == 'CURSOR': row.prop(tool_settings.gpencil_sculpt, "lockaxis") - elif tool_settings.gpencil_stroke_placement_view3d in {"SURFACE", "STROKE"}: + elif tool_settings.gpencil_stroke_placement_view3d in {'SURFACE', 'STROKE'}: row.prop(tool_settings, "use_gpencil_stroke_endpoints") - elif space_type in { - "IMAGE_EDITOR", - "NODE_EDITOR", - "SEQUENCE_EDITOR", - "CLIP_EDITOR", - }: + elif space_type in {'IMAGE_EDITOR', 'NODE_EDITOR', 'SEQUENCE_EDITOR', 'CLIP_EDITOR'}: row = layout.row(align=True) - row.prop( - tool_settings, "annotation_stroke_placement_view2d", text="Placement" - ) + row.prop(tool_settings, "annotation_stroke_placement_view2d", text="Placement") if tool.idname == "builtin.annotate_line": props = tool.operator_properties("gpencil.annotate") - if region_type == "TOOL_HEADER": + if region_type == 'TOOL_HEADER': row = layout.row() row.ui_units_x = 15 row.prop(props, "arrowstyle_start", text="Start") @@ -224,7 +216,7 @@ class _defs_annotate: col.prop(props, "arrowstyle_end", text="End") elif tool.idname == "builtin.annotate": props = tool.operator_properties("gpencil.annotate") - if region_type == "TOOL_HEADER": + if region_type == 'TOOL_HEADER': row = layout.row() row.prop(props, "use_stabilizer", text="Stabilize Stroke") subrow = layout.row(align=False) @@ -244,10 +236,10 @@ class _defs_annotate: idname="builtin.annotate", label="Annotate", icon="ops.gpencil.draw", - cursor="PAINT_BRUSH", + cursor='PAINT_BRUSH', keymap="Generic Tool: Annotate", draw_settings=draw_settings, - options={"KEYMAP_FALLBACK"}, + options={'KEYMAP_FALLBACK'}, ) @ToolDef.from_fn.with_args(draw_settings=draw_settings_common) @@ -256,10 +248,10 @@ class _defs_annotate: idname="builtin.annotate_line", label="Annotate Line", icon="ops.gpencil.draw.line", - cursor="PAINT_BRUSH", + cursor='PAINT_BRUSH', keymap="Generic Tool: Annotate Line", draw_settings=draw_settings, - options={"KEYMAP_FALLBACK"}, + options={'KEYMAP_FALLBACK'}, ) @ToolDef.from_fn.with_args(draw_settings=draw_settings_common) @@ -268,10 +260,10 @@ class _defs_annotate: idname="builtin.annotate_polygon", label="Annotate Polygon", icon="ops.gpencil.draw.poly", - cursor="PAINT_BRUSH", + cursor='PAINT_BRUSH', keymap="Generic Tool: Annotate Polygon", draw_settings=draw_settings, - options={"KEYMAP_FALLBACK"}, + options={'KEYMAP_FALLBACK'}, ) @ToolDef.from_fn @@ -280,21 +272,21 @@ class _defs_annotate: # TODO: Move this setting to tool_settings prefs = context.preferences layout.prop(prefs.edit, "grease_pencil_eraser_radius", text="Radius") - return dict( idname="builtin.annotate_eraser", label="Annotate Eraser", icon="ops.gpencil.draw.eraser", - cursor="ERASER", + cursor='ERASER', keymap="Generic Tool: Annotate Eraser", draw_settings=draw_settings, - options={"KEYMAP_FALLBACK"}, + options={'KEYMAP_FALLBACK'}, ) class _defs_transform: + def draw_transform_sculpt_tool_settings(context, layout): - if context.mode != "SCULPT": + if context.mode != 'SCULPT': return layout.prop(context.tool_settings.sculpt, "transform_mode") @@ -302,10 +294,7 @@ class _defs_transform: def translate(): def draw_settings(context, layout, _tool): _defs_transform.draw_transform_sculpt_tool_settings(context, layout) - _template_widget.VIEW3D_GGT_xform_gizmo.draw_settings_with_index( - context, layout, 1 - ) - + _template_widget.VIEW3D_GGT_xform_gizmo.draw_settings_with_index(context, layout, 1) return dict( idname="builtin.move", label="Move", @@ -321,10 +310,7 @@ class _defs_transform: def rotate(): def draw_settings(context, layout, _tool): _defs_transform.draw_transform_sculpt_tool_settings(context, layout) - _template_widget.VIEW3D_GGT_xform_gizmo.draw_settings_with_index( - context, layout, 2 - ) - + _template_widget.VIEW3D_GGT_xform_gizmo.draw_settings_with_index(context, layout, 2) return dict( idname="builtin.rotate", label="Rotate", @@ -340,10 +326,7 @@ class _defs_transform: def scale(): def draw_settings(context, layout, _tool): _defs_transform.draw_transform_sculpt_tool_settings(context, layout) - _template_widget.VIEW3D_GGT_xform_gizmo.draw_settings_with_index( - context, layout, 3 - ) - + _template_widget.VIEW3D_GGT_xform_gizmo.draw_settings_with_index(context, layout, 3) return dict( idname="builtin.scale", label="Scale", @@ -358,10 +341,7 @@ class _defs_transform: @ToolDef.from_fn def scale_cage(): def draw_settings(context, layout, _tool): - _template_widget.VIEW3D_GGT_xform_gizmo.draw_settings_with_index( - context, layout, 3 - ) - + _template_widget.VIEW3D_GGT_xform_gizmo.draw_settings_with_index(context, layout, 3) return dict( idname="builtin.scale_cage", label="Scale Cage", @@ -376,10 +356,7 @@ class _defs_transform: def shear(): def draw_settings(context, layout, _tool): # props = tool.operator_properties("transform.shear") - _template_widget.VIEW3D_GGT_xform_gizmo.draw_settings_with_index( - context, layout, 2 - ) - + _template_widget.VIEW3D_GGT_xform_gizmo.draw_settings_with_index(context, layout, 2) return dict( idname="builtin.shear", label="Shear", @@ -397,7 +374,7 @@ class _defs_transform: show_drag = True tool_settings = context.tool_settings - if tool_settings.workspace_tool_type == "FALLBACK": + if tool_settings.workspace_tool_type == 'FALLBACK': show_drag = False if show_drag: @@ -405,14 +382,14 @@ class _defs_transform: layout.prop(props, "drag_action") _defs_transform.draw_transform_sculpt_tool_settings(context, layout) - _template_widget.VIEW3D_GGT_xform_gizmo.draw_settings_with_index( - context, layout, 1 - ) + _template_widget.VIEW3D_GGT_xform_gizmo.draw_settings_with_index(context, layout, 1) return dict( idname="builtin.transform", label="Transform", - description=("Supports any combination of grab, rotate, and scale at once"), + description=( + "Supports any combination of grab, rotate, and scale at once" + ), icon="ops.transform.transform", widget="VIEW3D_GGT_xform_gizmo", keymap="3D View Tool: Transform", @@ -421,6 +398,7 @@ class _defs_transform: class _defs_view3d_select: + @ToolDef.from_fn def select(): return dict( @@ -438,7 +416,6 @@ class _defs_view3d_select: row = layout.row() row.use_property_split = False row.prop(props, "mode", text="", expand=True, icon_only=True) - return dict( idname="builtin.select_box", label="Select Box", @@ -455,7 +432,6 @@ class _defs_view3d_select: row = layout.row() row.use_property_split = False row.prop(props, "mode", text="", expand=True, icon_only=True) - return dict( idname="builtin.select_lasso", label="Select Lasso", @@ -476,7 +452,6 @@ class _defs_view3d_select: def draw_cursor(_context, tool, xy): from gpu_extras.presets import draw_circle_2d - props = tool.operator_properties("view3d.select_circle") radius = props.radius draw_circle_2d(xy, (1.0,) * 4, radius, segments=32) @@ -493,6 +468,7 @@ class _defs_view3d_select: class _defs_view3d_add: + @staticmethod def description_interactive_add(context, _item, _km, *, prefix): km = context.window_manager.keyconfigs.user.keymaps["View3D Placement Modal"] @@ -504,9 +480,9 @@ class _defs_view3d_add: return None if km is not None: - kmi_snap = keymap_item_from_propvalue("SNAP_ON") - kmi_center = keymap_item_from_propvalue("PIVOT_CENTER_ON") - kmi_fixed_aspect = keymap_item_from_propvalue("FIXED_ASPECT_ON") + kmi_snap = keymap_item_from_propvalue('SNAP_ON') + kmi_center = keymap_item_from_propvalue('PIVOT_CENTER_ON') + kmi_fixed_aspect = keymap_item_from_propvalue('FIXED_ASPECT_ON') else: kmi_snap = None kmi_center = None @@ -573,8 +549,7 @@ class _defs_view3d_add: label="Add Cube", icon="ops.mesh.primitive_cube_add_gizmo", description=lambda *args: _defs_view3d_add.description_interactive_add( - *args, - prefix=tip_("Add cube to mesh interactively"), + *args, prefix=tip_("Add cube to mesh interactively"), ), widget="VIEW3D_GGT_placement", keymap="3D View Tool: Object, Add Primitive", @@ -600,8 +575,7 @@ class _defs_view3d_add: label="Add Cone", icon="ops.mesh.primitive_cone_add_gizmo", description=lambda *args: _defs_view3d_add.description_interactive_add( - *args, - prefix=tip_("Add cone to mesh interactively"), + *args, prefix=tip_("Add cone to mesh interactively"), ), widget="VIEW3D_GGT_placement", keymap="3D View Tool: Object, Add Primitive", @@ -621,14 +595,12 @@ class _defs_view3d_add: if show_extra: layout.popover("TOPBAR_PT_tool_settings_extra", text="...") - return dict( idname="builtin.primitive_cylinder_add", label="Add Cylinder", icon="ops.mesh.primitive_cylinder_add_gizmo", description=lambda *args: _defs_view3d_add.description_interactive_add( - *args, - prefix=tip_("Add cylinder to mesh interactively"), + *args, prefix=tip_("Add cylinder to mesh interactively"), ), widget="VIEW3D_GGT_placement", keymap="3D View Tool: Object, Add Primitive", @@ -648,14 +620,12 @@ class _defs_view3d_add: if show_extra: layout.popover("TOPBAR_PT_tool_settings_extra", text="...") - return dict( idname="builtin.primitive_uv_sphere_add", label="Add UV Sphere", icon="ops.mesh.primitive_sphere_add_gizmo", description=lambda *args: _defs_view3d_add.description_interactive_add( - *args, - prefix=tip_("Add sphere to mesh interactively"), + *args, prefix=tip_("Add sphere to mesh interactively"), ), widget="VIEW3D_GGT_placement", keymap="3D View Tool: Object, Add Primitive", @@ -674,14 +644,12 @@ class _defs_view3d_add: if show_extra: layout.popover("TOPBAR_PT_tool_settings_extra", text="...") - return dict( idname="builtin.primitive_ico_sphere_add", label="Add Ico Sphere", icon="ops.mesh.primitive_sphere_add_gizmo", description=lambda *args: _defs_view3d_add.description_interactive_add( - *args, - prefix=tip_("Add sphere to mesh interactively"), + *args, prefix=tip_("Add sphere to mesh interactively"), ), widget="VIEW3D_GGT_placement", keymap="3D View Tool: Object, Add Primitive", @@ -692,8 +660,8 @@ class _defs_view3d_add: # ----------------------------------------------------------------------------- # Object Modes (named based on context.mode) - class _defs_edit_armature: + @ToolDef.from_fn def roll(): return dict( @@ -740,7 +708,7 @@ class _defs_edit_armature: return dict( idname="builtin.extrude_to_cursor", label="Extrude to Cursor", - cursor="CROSSHAIR", + cursor='CROSSHAIR', icon="ops.armature.extrude_cursor", widget=None, keymap=(), @@ -748,6 +716,7 @@ class _defs_edit_armature: class _defs_edit_mesh: + @ToolDef.from_fn def rip_region(): def draw_settings(_context, layout, tool): @@ -780,7 +749,6 @@ class _defs_edit_mesh: props = tool.operator_properties("mesh.polybuild_face_at_cursor_move") props_macro = props.MESH_OT_polybuild_face_at_cursor layout.prop(props_macro, "create_quads") - return dict( idname="builtin.poly_build", label="Poly Build", @@ -881,9 +849,9 @@ class _defs_edit_mesh: def draw_settings(context, layout, tool, *, extra=False): props = tool.operator_properties("mesh.bevel") - region_is_header = context.region.type == "TOOL_HEADER" + region_is_header = context.region.type == 'TOOL_HEADER' - edge_bevel = props.affect == "EDGES" + edge_bevel = props.affect == 'EDGES' if not extra: if region_is_header: @@ -925,7 +893,7 @@ class _defs_edit_mesh: col.active = edge_bevel col.prop(props, "miter_outer", text="Miter Outer") col.prop(props, "miter_inner", text="Inner") - if props.miter_inner == "ARC": + if props.miter_inner == 'ARC': col.prop(props, "spread") layout.separator() @@ -938,11 +906,9 @@ class _defs_edit_mesh: layout.prop(props, "profile_type") - if props.profile_type == "CUSTOM": + if props.profile_type == 'CUSTOM': tool_settings = context.tool_settings - layout.template_curveprofile( - tool_settings, "custom_bevel_profile_preset" - ) + layout.template_curveprofile(tool_settings, "custom_bevel_profile_preset") return dict( idname="builtin.bevel", @@ -959,7 +925,9 @@ class _defs_edit_mesh: idname="builtin.extrude_region", label="Extrude Region", # The operator description isn't useful in this case, give our own. - description=("Extrude freely or along an axis"), + description=( + "Extrude freely or along an axis" + ), icon="ops.mesh.extrude_region_move", widget="VIEW3D_GGT_xform_extrude", # Important to use same operator as 'E' key. @@ -987,7 +955,6 @@ class _defs_edit_mesh: props = tool.operator_properties("mesh.extrude_region_shrink_fatten") props_macro = props.TRANSFORM_OT_shrink_fatten layout.prop(props_macro, "use_even_offset") - return dict( idname="builtin.extrude_along_normals", label="Extrude Along Normals", @@ -1017,7 +984,7 @@ class _defs_edit_mesh: return dict( idname="builtin.extrude_to_cursor", label="Extrude to Cursor", - cursor="CROSSHAIR", + cursor='CROSSHAIR', icon="ops.mesh.dupli_extrude_cursor", widget=None, keymap=(), @@ -1026,6 +993,7 @@ class _defs_edit_mesh: @ToolDef.from_fn def loopcut_slide(): + def draw_settings(_context, layout, tool): props = tool.operator_properties("mesh.loopcut_slide") props_macro = props.MESH_OT_loopcut @@ -1057,7 +1025,6 @@ class _defs_edit_mesh: def draw_settings(_context, layout, tool): props = tool.operator_properties("mesh.vertices_smooth") layout.prop(props, "repeat") - return dict( idname="builtin.smooth", label="Smooth", @@ -1074,7 +1041,6 @@ class _defs_edit_mesh: layout.prop(props, "uniform") layout.prop(props, "normal") layout.prop(props, "seed") - return dict( idname="builtin.randomize", label="Randomize", @@ -1128,7 +1094,7 @@ class _defs_edit_mesh: layout.prop(props, "use_occlude_geometry") layout.prop(props, "only_selected") layout.prop(props, "xray") - region_is_header = bpy.context.region.type == "TOOL_HEADER" + region_is_header = bpy.context.region.type == 'TOOL_HEADER' if region_is_header: show_extra = True else: @@ -1143,16 +1109,15 @@ class _defs_edit_mesh: layout.prop(props, "angle_snapping_increment", text="") if show_extra: layout.popover("TOPBAR_PT_tool_settings_extra", text="...") - return dict( idname="builtin.knife", label="Knife", - cursor="KNIFE", + cursor='KNIFE', icon="ops.mesh.knife_tool", widget=None, keymap=(), draw_settings=draw_settings, - options={"KEYMAP_FALLBACK"}, + options={'KEYMAP_FALLBACK'}, ) @ToolDef.from_fn @@ -1163,7 +1128,6 @@ class _defs_edit_mesh: layout.prop(props, "clear_inner") layout.prop(props, "clear_outer") layout.prop(props, "threshold") - return dict( idname="builtin.bisect", label="Bisect", @@ -1175,6 +1139,7 @@ class _defs_edit_mesh: class _defs_edit_curve: + @ToolDef.from_fn def draw(): def draw_settings(context, layout, _tool, *, extra=False): @@ -1183,7 +1148,7 @@ class _defs_edit_curve: cps = tool_settings.curve_paint_settings region_type = context.region.type - if region_type == "TOOL_HEADER": + if region_type == 'TOOL_HEADER': if not extra: layout.prop(cps, "curve_type", text="") layout.prop(cps, "depth_mode", expand=True) @@ -1193,13 +1158,13 @@ class _defs_edit_curve: layout.use_property_split = True layout.use_property_decorate = False - if region_type != "TOOL_HEADER": + if region_type != 'TOOL_HEADER': layout.prop(cps, "curve_type") layout.separator() - if cps.curve_type == "BEZIER": + if cps.curve_type == 'BEZIER': layout.prop(cps, "fit_method") layout.prop(cps, "error_threshold") - if region_type != "TOOL_HEADER": + if region_type != 'TOOL_HEADER': row = layout.row(heading="Detect Corners", align=True) else: row = layout.row(heading="Corners", align=True) @@ -1217,13 +1182,13 @@ class _defs_edit_curve: col.prop(cps, "radius_max", text="Max") col.prop(cps, "use_pressure_radius") - if region_type != "TOOL_HEADER" or cps.depth_mode == "SURFACE": + if region_type != 'TOOL_HEADER' or cps.depth_mode == 'SURFACE': layout.separator() - if region_type != "TOOL_HEADER": + if region_type != 'TOOL_HEADER': row = layout.row() row.prop(cps, "depth_mode", expand=True) - if cps.depth_mode == "SURFACE": + if cps.depth_mode == 'SURFACE': col = layout.column() col.prop(cps, "surface_offset") col.prop(cps, "use_offset_absolute") @@ -1235,7 +1200,7 @@ class _defs_edit_curve: return dict( idname="builtin.draw", label="Draw", - cursor="PAINT_BRUSH", + cursor='PAINT_BRUSH', icon="ops.curve.draw", widget=None, keymap=(), @@ -1258,7 +1223,7 @@ class _defs_edit_curve: return dict( idname="builtin.extrude_cursor", label="Extrude to Cursor", - cursor="CROSSHAIR", + cursor='CROSSHAIR', icon="ops.curve.extrude_cursor", widget=None, keymap=(), @@ -1270,11 +1235,10 @@ class _defs_edit_curve: props = tool.operator_properties("curve.pen") layout.prop(props, "close_spline") layout.prop(props, "extrude_handle") - return dict( idname="builtin.pen", label="Curve Pen", - cursor="CROSSHAIR", + cursor='CROSSHAIR', icon="ops.curve.pen", widget=None, keymap=(), @@ -1296,7 +1260,9 @@ class _defs_edit_curve: return dict( idname="builtin.radius", label="Radius", - description=("Expand or contract the radius of the selected curve points"), + description=( + "Expand or contract the radius of the selected curve points" + ), icon="ops.curve.radius", widget="VIEW3D_GGT_tool_generic_handle_free", keymap=(), @@ -1309,7 +1275,6 @@ class _defs_edit_curve: layout.prop(props, "uniform") layout.prop(props, "normal") layout.prop(props, "seed") - return dict( idname="builtin.randomize", label="Randomize", @@ -1321,12 +1286,13 @@ class _defs_edit_curve: class _defs_edit_text: + @ToolDef.from_fn def select_text(): return dict( idname="builtin.select_text", label="Select Text", - cursor="TEXT", + cursor='TEXT', icon="ops.generic.select_box", widget=None, keymap=(), @@ -1334,6 +1300,7 @@ class _defs_edit_text: class _defs_pose: + @ToolDef.from_fn def breakdown(): return dict( @@ -1366,6 +1333,7 @@ class _defs_pose: class _defs_particle: + @staticmethod def generate_from_brushes(context): return generate_from_enum_ex( @@ -1378,6 +1346,7 @@ class _defs_particle: class _defs_sculpt: + @staticmethod def generate_from_brushes(context): return generate_from_enum_ex( @@ -1485,7 +1454,6 @@ class _defs_sculpt: layout.prop(props, "trim_orientation", expand=False) layout.prop(props, "trim_extrude_mode", expand=False) layout.prop(props, "use_cursor_depth", expand=False) - return dict( idname="builtin.box_trim", label="Box Trim", @@ -1503,7 +1471,6 @@ class _defs_sculpt: layout.prop(props, "trim_orientation", expand=False) layout.prop(props, "trim_extrude_mode", expand=False) layout.prop(props, "use_cursor_depth", expand=False) - return dict( idname="builtin.lasso_trim", label="Lasso Trim", @@ -1538,13 +1505,13 @@ class _defs_sculpt: row.prop(props, "deform_axis") layout.prop(props, "orientation", expand=False) - if props.type in {"SMOOTH", "SURFACE_SMOOTH", "ENHANCE_DETAILS", "SHARPEN"}: + if props.type in {'SMOOTH', 'SURFACE_SMOOTH', 'ENHANCE_DETAILS', 'SHARPEN'}: layout.prop(props, "hard_corner_pin", expand=False) - if props.type == "SURFACE_SMOOTH": + if props.type == 'SURFACE_SMOOTH': layout.prop(props, "surface_smooth_shape_preservation", expand=False) layout.prop(props, "surface_smooth_current_vertex", expand=False) - elif props.type == "SHARPEN": + elif props.type == 'SHARPEN': layout.prop(props, "sharpen_smooth_ratio", expand=False) layout.prop(props, "sharpen_intensify_detail_strength", expand=False) layout.prop(props, "sharpen_curvature_smooth_iterations", expand=False) @@ -1586,7 +1553,7 @@ class _defs_sculpt: def draw_settings(_context, layout, tool): props = tool.operator_properties("sculpt.color_filter") layout.prop(props, "type", expand=False) - if props.type == "FILL": + if props.type == 'FILL': layout.prop(props, "fill_color", expand=False) layout.prop(props, "strength") @@ -1635,16 +1602,15 @@ class _defs_sculpt: class _defs_vertex_paint: + @staticmethod def poll_select_mask(context): if context is None: return True ob = context.active_object - return ( - ob - and ob.type == "MESH" - and (ob.data.use_paint_mask or ob.data.use_paint_mask_vertex) - ) + return (ob and ob.type == 'MESH' and + (ob.data.use_paint_mask or + ob.data.use_paint_mask_vertex)) @staticmethod def generate_from_brushes(context): @@ -1658,12 +1624,14 @@ class _defs_vertex_paint: class _defs_texture_paint: + @staticmethod def poll_select_mask(context): if context is None: return True ob = context.active_object - return ob and ob.type == "MESH" and (ob.data.use_paint_mask) + return (ob and ob.type == 'MESH' and + (ob.data.use_paint_mask)) @staticmethod def generate_from_brushes(context): @@ -1673,21 +1641,20 @@ class _defs_texture_paint: icon_prefix="brush.paint_texture.", type=bpy.types.Brush, attr="image_tool", - cursor="PAINT_CROSS", + cursor='PAINT_CROSS', ) class _defs_weight_paint: + @staticmethod def poll_select_mask(context): if context is None: return True ob = context.active_object - return ( - ob - and ob.type == "MESH" - and (ob.data.use_paint_mask or ob.data.use_paint_mask_vertex) - ) + return (ob and ob.type == 'MESH' and + (ob.data.use_paint_mask or + ob.data.use_paint_mask_vertex)) @staticmethod def generate_from_brushes(context): @@ -1709,12 +1676,11 @@ class _defs_weight_paint: else: return layout.label(text="Weight: %.3f" % weight) - return dict( idname="builtin.sample_weight", label="Sample Weight", icon="ops.paint.weight_sample", - cursor="EYEDROPPER", + cursor='EYEDROPPER', widget=None, keymap=(), draw_settings=draw_settings, @@ -1726,7 +1692,7 @@ class _defs_weight_paint: idname="builtin.sample_vertex_group", label="Sample Vertex Group", icon="ops.paint.weight_sample_group", - cursor="EYEDROPPER", + cursor='EYEDROPPER', widget=None, keymap=(), ) @@ -1737,7 +1703,6 @@ class _defs_weight_paint: brush = context.tool_settings.weight_paint.brush if brush is not None: from bl_ui.properties_paint_common import UnifiedPaintPanel - UnifiedPaintPanel.prop_unified( layout, context, @@ -1783,6 +1748,7 @@ class _defs_paint_grease_pencil: class _defs_image_generic: + @staticmethod def poll_uvedit(context): if context is None: @@ -1799,7 +1765,9 @@ class _defs_image_generic: return dict( idname="builtin.cursor", label="Cursor", - description=("Set the cursor location, drag to transform"), + description=( + "Set the cursor location, drag to transform" + ), icon="ops.generic.cursor", keymap=(), ) @@ -1811,11 +1779,12 @@ class _defs_image_generic: def draw_settings(_context, layout, tool): props = tool.operator_properties("image.sample") layout.prop(props, "size") - return dict( idname="builtin.sample", label="Sample", - description=("Sample pixel values under the cursor"), + description=( + "Sample pixel values under the cursor" + ), icon="ops.paint.weight_sample", # XXX, needs own icon. keymap="Image Editor Tool: Sample", draw_settings=draw_settings, @@ -1823,6 +1792,7 @@ class _defs_image_generic: class _defs_image_uv_transform: + @ToolDef.from_fn def translate(): return dict( @@ -1861,7 +1831,9 @@ class _defs_image_uv_transform: return dict( idname="builtin.transform", label="Transform", - description=("Supports any combination of grab, rotate, and scale at once"), + description=( + "Supports any combination of grab, rotate, and scale at once" + ), icon="ops.transform.transform", widget="IMAGE_GGT_gizmo2d", # No keymap default action, only for gizmo! @@ -1869,6 +1841,7 @@ class _defs_image_uv_transform: class _defs_image_uv_select: + @ToolDef.from_fn def select(): return dict( @@ -1886,7 +1859,6 @@ class _defs_image_uv_select: row = layout.row() row.use_property_split = False row.prop(props, "mode", text="", expand=True, icon_only=True) - return dict( idname="builtin.select_box", label="Select Box", @@ -1903,7 +1875,6 @@ class _defs_image_uv_select: row = layout.row() row.use_property_split = False row.prop(props, "mode", text="", expand=True, icon_only=True) - return dict( idname="builtin.select_lasso", label="Select Lasso", @@ -1924,7 +1895,6 @@ class _defs_image_uv_select: def draw_cursor(_context, tool, xy): from gpu_extras.presets import draw_circle_2d - props = tool.operator_properties("uv.select_circle") radius = props.radius draw_circle_2d(xy, (1.0,) * 4, radius, segments=32) @@ -1941,6 +1911,7 @@ class _defs_image_uv_select: class _defs_image_uv_edit: + @ToolDef.from_fn def rip_region(): return dict( @@ -1950,16 +1921,16 @@ class _defs_image_uv_edit: # TODO: generic operator (UV version of `VIEW3D_GGT_tool_generic_handle_free`). widget=None, keymap=(), - options={"KEYMAP_FALLBACK"}, + options={'KEYMAP_FALLBACK'}, ) class _defs_image_uv_sculpt: + @staticmethod def generate_from_brushes(context): def draw_cursor(context, _tool, xy): from gpu_extras.presets import draw_circle_2d - tool_settings = context.tool_settings uv_sculpt = tool_settings.uv_sculpt if not uv_sculpt.show_brush: @@ -1984,12 +1955,13 @@ class _defs_image_uv_sculpt: operator="sculpt.uv_sculpt_stroke", keymap="Image Editor Tool: Uv, Sculpt Stroke", draw_cursor=draw_cursor, - options={"KEYMAP_FALLBACK"}, + options={'KEYMAP_FALLBACK'}, ), ) class _defs_gpencil_paint: + @staticmethod def gpencil_primitive_toolbar(context, layout, _tool, props): paint = context.tool_settings.gpencil_paint @@ -2022,7 +1994,7 @@ class _defs_gpencil_paint: icon_prefix="brush.gpencil_draw.", type=bpy.types.Brush, attr="gpencil_tool", - cursor="DOT", + cursor='DOT', tooldef_keywords=dict( operator="gpencil.draw", ), @@ -2035,12 +2007,11 @@ class _defs_gpencil_paint: row = layout.row() row.use_property_split = False row.prop(props, "flat_caps") - return dict( idname="builtin.cutter", label="Cutter", icon="ops.gpencil.stroke_cutter", - cursor="KNIFE", + cursor='KNIFE', widget=None, keymap=(), draw_settings=draw_settings, @@ -2056,7 +2027,7 @@ class _defs_gpencil_paint: idname="builtin.line", label="Line", icon="ops.gpencil.primitive_line", - cursor="CROSSHAIR", + cursor='CROSSHAIR', widget=None, keymap=(), draw_settings=draw_settings, @@ -2072,7 +2043,7 @@ class _defs_gpencil_paint: idname="builtin.polyline", label="Polyline", icon="ops.gpencil.primitive_polyline", - cursor="CROSSHAIR", + cursor='CROSSHAIR', widget=None, keymap=(), draw_settings=draw_settings, @@ -2088,7 +2059,7 @@ class _defs_gpencil_paint: idname="builtin.box", label="Box", icon="ops.gpencil.primitive_box", - cursor="CROSSHAIR", + cursor='CROSSHAIR', widget=None, keymap=(), draw_settings=draw_settings, @@ -2104,7 +2075,7 @@ class _defs_gpencil_paint: idname="builtin.circle", label="Circle", icon="ops.gpencil.primitive_circle", - cursor="CROSSHAIR", + cursor='CROSSHAIR', widget=None, keymap=(), draw_settings=draw_settings, @@ -2120,7 +2091,7 @@ class _defs_gpencil_paint: idname="builtin.arc", label="Arc", icon="ops.gpencil.primitive_arc", - cursor="CROSSHAIR", + cursor='CROSSHAIR', widget=None, keymap=(), draw_settings=draw_settings, @@ -2136,7 +2107,7 @@ class _defs_gpencil_paint: idname="builtin.curve", label="Curve", icon="ops.gpencil.primitive_curve", - cursor="CROSSHAIR", + cursor='CROSSHAIR', widget=None, keymap=(), draw_settings=draw_settings, @@ -2149,12 +2120,11 @@ class _defs_gpencil_paint: row = layout.row() row.use_property_split = False row.prop(props, "mode", expand=True) - return dict( idname="builtin.eyedropper", label="Eyedropper", icon="ops.paint.eyedropper_add", - cursor="EYEDROPPER", + cursor='EYEDROPPER', widget=None, keymap=(), draw_settings=draw_settings, @@ -2174,7 +2144,7 @@ class _defs_gpencil_paint: idname="builtin.interpolate", label="Interpolate", icon="ops.pose.breakdowner", - cursor="DEFAULT", + cursor='DEFAULT', widget=None, keymap=(), draw_settings=draw_settings, @@ -2184,11 +2154,11 @@ class _defs_gpencil_paint: class _defs_gpencil_edit: def is_segment(context): tool_settings = context.scene.tool_settings - if context.mode == "EDIT_GPENCIL": - return tool_settings.gpencil_selectmode_edit == "SEGMENT" - elif context.mode == "SCULPT_GPENCIL": + if context.mode == 'EDIT_GPENCIL': + return tool_settings.gpencil_selectmode_edit == 'SEGMENT' + elif context.mode == 'SCULPT_GPENCIL': return tool_settings.use_gpencil_select_mask_segment - elif context.mode == "VERTEX_GPENCIL": + elif context.mode == 'VERTEX_GPENCIL': return tool_settings.use_gpencil_vertex_select_mask_segment else: return False @@ -2207,10 +2177,7 @@ class _defs_gpencil_edit: def select(): def draw_settings(context, layout, _tool): if _defs_gpencil_edit.is_segment(context): - layout.prop( - context.tool_settings.gpencil_sculpt, "intersection_threshold" - ) - + layout.prop(context.tool_settings.gpencil_sculpt, "intersection_threshold") return dict( idname="builtin.select", label="Tweak", @@ -2228,10 +2195,7 @@ class _defs_gpencil_edit: row.use_property_split = False row.prop(props, "mode", text="", expand=True, icon_only=True) if _defs_gpencil_edit.is_segment(context): - layout.prop( - context.tool_settings.gpencil_sculpt, "intersection_threshold" - ) - + layout.prop(context.tool_settings.gpencil_sculpt, "intersection_threshold") return dict( idname="builtin.select_box", label="Select Box", @@ -2249,10 +2213,7 @@ class _defs_gpencil_edit: row.use_property_split = False row.prop(props, "mode", text="", expand=True, icon_only=True) if _defs_gpencil_edit.is_segment(context): - layout.prop( - context.tool_settings.gpencil_sculpt, "intersection_threshold" - ) - + layout.prop(context.tool_settings.gpencil_sculpt, "intersection_threshold") return dict( idname="builtin.select_lasso", label="Select Lasso", @@ -2271,13 +2232,10 @@ class _defs_gpencil_edit: row.prop(props, "mode", text="", expand=True, icon_only=True) layout.prop(props, "radius") if _defs_gpencil_edit.is_segment(context): - layout.prop( - context.tool_settings.gpencil_sculpt, "intersection_threshold" - ) + layout.prop(context.tool_settings.gpencil_sculpt, "intersection_threshold") def draw_cursor(_context, tool, xy): from gpu_extras.presets import draw_circle_2d - props = tool.operator_properties("gpencil.select_circle") radius = props.radius draw_circle_2d(xy, (1.0,) * 4, radius, segments=32) @@ -2297,8 +2255,11 @@ class _defs_gpencil_edit: return dict( idname="builtin.radius", label="Radius", - description=("Expand or contract the radius of the selected points"), + description=( + "Expand or contract the radius of the selected points" + ), icon="ops.gpencil.radius", + widget=None, keymap=(), ) @@ -2346,7 +2307,7 @@ class _defs_gpencil_edit: idname="builtin.transform_fill", label="Transform Fill", icon="ops.gpencil.transform_fill", - cursor="DEFAULT", + cursor='DEFAULT', widget=None, keymap=(), draw_settings=draw_settings, @@ -2367,7 +2328,7 @@ class _defs_gpencil_edit: idname="builtin.interpolate", label="Interpolate", icon="ops.pose.breakdowner", - cursor="DEFAULT", + cursor='DEFAULT', widget=None, keymap=(), draw_settings=draw_settings, @@ -2375,6 +2336,7 @@ class _defs_gpencil_edit: class _defs_gpencil_sculpt: + @staticmethod def poll_select_mask(context): if context is None: @@ -2382,12 +2344,11 @@ class _defs_gpencil_sculpt: ob = context.active_object tool_settings = context.scene.tool_settings return ( - ob is not None - and ob.type == "GPENCIL" - and ( - tool_settings.use_gpencil_select_mask_point - or tool_settings.use_gpencil_select_mask_stroke - or tool_settings.use_gpencil_select_mask_segment + ob is not None and + ob.type == 'GPENCIL' and ( + tool_settings.use_gpencil_select_mask_point or + tool_settings.use_gpencil_select_mask_stroke or + tool_settings.use_gpencil_select_mask_segment ) ) @@ -2406,6 +2367,7 @@ class _defs_gpencil_sculpt: class _defs_gpencil_weight: + @staticmethod def generate_from_brushes(context): return generate_from_enum_ex( @@ -2421,6 +2383,7 @@ class _defs_gpencil_weight: class _defs_curves_sculpt: + @staticmethod def generate_from_brushes(context): return generate_from_enum_ex( @@ -2437,6 +2400,7 @@ class _defs_curves_sculpt: class _defs_gpencil_vertex: + @staticmethod def poll_select_mask(context): if context is None: @@ -2444,12 +2408,11 @@ class _defs_gpencil_vertex: ob = context.active_object tool_settings = context.scene.tool_settings return ( - ob is not None - and ob.type == "GPENCIL" - and ( - tool_settings.use_gpencil_vertex_select_mask_point - or tool_settings.use_gpencil_vertex_select_mask_stroke - or tool_settings.use_gpencil_vertex_select_mask_segment + ob is not None and + ob.type == 'GPENCIL' and ( + tool_settings.use_gpencil_vertex_select_mask_point or + tool_settings.use_gpencil_vertex_select_mask_stroke or + tool_settings.use_gpencil_vertex_select_mask_segment ) ) @@ -2461,7 +2424,7 @@ class _defs_gpencil_vertex: icon_prefix="brush.paint_vertex.", type=bpy.types.Brush, attr="gpencil_vertex_tool", - cursor="DOT", + cursor='DOT', tooldef_keywords=dict( operator="gpencil.vertex_paint", ), @@ -2469,6 +2432,7 @@ class _defs_gpencil_vertex: class _defs_node_select: + @ToolDef.from_fn def select(): return dict( @@ -2486,7 +2450,6 @@ class _defs_node_select: row = layout.row() row.use_property_split = False row.prop(props, "mode", text="", expand=True, icon_only=True) - return dict( idname="builtin.select_box", label="Select Box", @@ -2503,7 +2466,6 @@ class _defs_node_select: row = layout.row() row.use_property_split = False row.prop(props, "mode", text="", expand=True, icon_only=True) - return dict( idname="builtin.select_lasso", label="Select Lasso", @@ -2524,7 +2486,6 @@ class _defs_node_select: def draw_cursor(_context, tool, xy): from gpu_extras.presets import draw_circle_2d - props = tool.operator_properties("node.select_circle") radius = props.radius draw_circle_2d(xy, (1.0,) * 4, radius, segments=32) @@ -2541,6 +2502,7 @@ class _defs_node_select: class _defs_node_edit: + @ToolDef.from_fn def links_cut(): return dict( @@ -2549,17 +2511,20 @@ class _defs_node_edit: icon="ops.node.links_cut", widget=None, keymap="Node Tool: Links Cut", - options={"KEYMAP_FALLBACK"}, + options={'KEYMAP_FALLBACK'}, ) class _defs_sequencer_generic: + @ToolDef.from_fn def cursor(): return dict( idname="builtin.cursor", label="Cursor", - description=("Set the cursor location, drag to transform"), + description=( + "Set the cursor location, drag to transform" + ), icon="ops.generic.cursor", keymap="Sequencer Tool: Cursor", ) @@ -2571,16 +2536,15 @@ class _defs_sequencer_generic: row = layout.row() row.use_property_split = False row.prop(props, "type", expand=True) - return dict( idname="builtin.blade", label="Blade", icon="ops.sequencer.blade", - cursor="CROSSHAIR", + cursor='CROSSHAIR', widget=None, keymap="Sequencer Tool: Blade", draw_settings=draw_settings, - options={"KEYMAP_FALLBACK"}, + options={'KEYMAP_FALLBACK'}, ) @ToolDef.from_fn @@ -2592,7 +2556,7 @@ class _defs_sequencer_generic: widget="SEQUENCER_GGT_gizmo_retime", operator=None, keymap=None, - options={"KEYMAP_FALLBACK"}, + options={'KEYMAP_FALLBACK'}, ) @ToolDef.from_fn @@ -2600,7 +2564,9 @@ class _defs_sequencer_generic: return dict( idname="builtin.sample", label="Sample", - description=("Sample pixel values under the cursor"), + description=( + "Sample pixel values under the cursor" + ), icon="ops.paint.weight_sample", # XXX, needs own icon. keymap="Sequencer Tool: Sample", ) @@ -2643,7 +2609,9 @@ class _defs_sequencer_generic: return dict( idname="builtin.transform", label="Transform", - description=("Supports any combination of grab, rotate, and scale at once"), + description=( + "Supports any combination of grab, rotate, and scale at once" + ), icon="ops.transform.transform", widget="SEQUENCER_GGT_gizmo2d", # No keymap default action, only for gizmo! @@ -2668,7 +2636,6 @@ class _defs_sequencer_select: row = layout.row() row.use_property_split = False row.prop(props, "mode", text="", expand=True, icon_only=True) - return dict( idname="builtin.select_box", label="Select Box", @@ -2680,10 +2647,10 @@ class _defs_sequencer_select: class IMAGE_PT_tools_active(ToolSelectPanelHelper, Panel): - bl_space_type = "IMAGE_EDITOR" - bl_region_type = "TOOLS" + bl_space_type = 'IMAGE_EDITOR' + bl_region_type = 'TOOLS' bl_label = "Tools" # not visible - bl_options = {"HIDE_HEADER"} + bl_options = {'HIDE_HEADER'} # Satisfy the `ToolSelectPanelHelper` API. keymap_prefix = "Image Editor Tool:" @@ -2695,7 +2662,7 @@ class IMAGE_PT_tools_active(ToolSelectPanelHelper, Panel): def tools_from_context(cls, context, mode=None): if mode is None: if context.space_data is None: - mode = "VIEW" + mode = 'VIEW' else: mode = context.space_data.mode for tools in (cls._tools[None], cls._tools.get(mode, ())): @@ -2743,11 +2710,11 @@ class IMAGE_PT_tools_active(ToolSelectPanelHelper, Panel): None: [ # for all modes ], - "VIEW": [ + 'VIEW': [ _defs_image_generic.sample, *_tools_annotate, ], - "UV": [ + 'UV': [ *_tools_select, _defs_image_generic.cursor, None, @@ -2763,10 +2730,10 @@ class IMAGE_PT_tools_active(ToolSelectPanelHelper, Panel): else () ), ], - "MASK": [ + 'MASK': [ None, ], - "PAINT": [ + 'PAINT': [ _defs_texture_paint.generate_from_brushes, None, *_tools_annotate, @@ -2775,10 +2742,10 @@ class IMAGE_PT_tools_active(ToolSelectPanelHelper, Panel): class NODE_PT_tools_active(ToolSelectPanelHelper, Panel): - bl_space_type = "NODE_EDITOR" - bl_region_type = "TOOLS" + bl_space_type = 'NODE_EDITOR' + bl_region_type = 'TOOLS' bl_label = "Tools" # not visible - bl_options = {"HIDE_HEADER"} + bl_options = {'HIDE_HEADER'} # Satisfy the `ToolSelectPanelHelper` API. keymap_prefix = "Node Editor Tool:" @@ -2839,10 +2806,10 @@ class NODE_PT_tools_active(ToolSelectPanelHelper, Panel): class VIEW3D_PT_tools_active(ToolSelectPanelHelper, Panel): - bl_space_type = "VIEW_3D" - bl_region_type = "TOOLS" + bl_space_type = 'VIEW_3D' + bl_region_type = 'TOOLS' bl_label = "Tools" # not visible - bl_options = {"HIDE_HEADER"} + bl_options = {'HIDE_HEADER'} # Satisfy the `ToolSelectPanelHelper` API. keymap_prefix = "3D View Tool:" @@ -2931,12 +2898,12 @@ class VIEW3D_PT_tools_active(ToolSelectPanelHelper, Panel): # _defs_view3d_generic.cursor, # End group. ], - "OBJECT": [ + 'OBJECT': [ *_tools_default, None, _tools_view3d_add, ], - "POSE": [ + 'POSE': [ *_tools_default, None, ( @@ -2945,7 +2912,7 @@ class VIEW3D_PT_tools_active(ToolSelectPanelHelper, Panel): _defs_pose.relax, ), ], - "EDIT_ARMATURE": [ + 'EDIT_ARMATURE': [ *_tools_default, None, _defs_edit_armature.roll, @@ -2960,8 +2927,9 @@ class VIEW3D_PT_tools_active(ToolSelectPanelHelper, Panel): ), _defs_transform.shear, ], - "EDIT_MESH": [ + 'EDIT_MESH': [ *_tools_default, + None, _tools_view3d_add, None, @@ -3008,7 +2976,7 @@ class VIEW3D_PT_tools_active(ToolSelectPanelHelper, Panel): _defs_edit_mesh.rip_edge, ), ], - "EDIT_CURVE": [ + 'EDIT_CURVE': [ *_tools_default, None, _defs_edit_curve.draw, @@ -3024,25 +2992,25 @@ class VIEW3D_PT_tools_active(ToolSelectPanelHelper, Panel): _defs_transform.shear, _defs_edit_curve.curve_vertex_randomize, ], - "EDIT_CURVES": [ + 'EDIT_CURVES': [ *_tools_default, ], - "EDIT_SURFACE": [ + 'EDIT_SURFACE': [ *_tools_default, None, _defs_transform.shear, ], - "EDIT_METABALL": [ + 'EDIT_METABALL': [ *_tools_default, None, _defs_transform.shear, ], - "EDIT_LATTICE": [ + 'EDIT_LATTICE': [ *_tools_default, None, _defs_transform.shear, ], - "EDIT_TEXT": [ + 'EDIT_TEXT': [ _defs_edit_text.select_text, _defs_view3d_generic.cursor, None, @@ -3058,7 +3026,7 @@ class VIEW3D_PT_tools_active(ToolSelectPanelHelper, Panel): None, _defs_particle.generate_from_brushes, ], - "SCULPT": [ + 'SCULPT': [ _defs_sculpt.generate_from_brushes, None, ( @@ -3091,7 +3059,7 @@ class VIEW3D_PT_tools_active(ToolSelectPanelHelper, Panel): None, *_tools_annotate, ], - "PAINT_TEXTURE": [ + 'PAINT_TEXTURE': [ _defs_texture_paint.generate_from_brushes, None, lambda context: ( @@ -3101,7 +3069,7 @@ class VIEW3D_PT_tools_active(ToolSelectPanelHelper, Panel): ), *_tools_annotate, ], - "PAINT_VERTEX": [ + 'PAINT_VERTEX': [ _defs_vertex_paint.generate_from_brushes, None, lambda context: ( @@ -3111,7 +3079,7 @@ class VIEW3D_PT_tools_active(ToolSelectPanelHelper, Panel): ), *_tools_annotate, ], - "PAINT_WEIGHT": [ + 'PAINT_WEIGHT': [ _defs_weight_paint.generate_from_brushes, _defs_weight_paint.gradient, None, @@ -3180,7 +3148,7 @@ class VIEW3D_PT_tools_active(ToolSelectPanelHelper, Panel): None, *_tools_annotate, ], - "SCULPT_GPENCIL": [ + 'SCULPT_GPENCIL': [ _defs_gpencil_sculpt.generate_from_brushes, None, *_tools_annotate, @@ -3190,12 +3158,12 @@ class VIEW3D_PT_tools_active(ToolSelectPanelHelper, Panel): else () ), ], - "WEIGHT_GPENCIL": [ + 'WEIGHT_GPENCIL': [ _defs_gpencil_weight.generate_from_brushes, None, *_tools_annotate, ], - "VERTEX_GPENCIL": [ + 'VERTEX_GPENCIL': [ _defs_gpencil_vertex.generate_from_brushes, None, *_tools_annotate, @@ -3206,7 +3174,7 @@ class VIEW3D_PT_tools_active(ToolSelectPanelHelper, Panel): else () ), ], - "SCULPT_CURVES": [ + 'SCULPT_CURVES': [ _defs_curves_sculpt.generate_from_brushes, None, *_tools_annotate, @@ -3215,10 +3183,10 @@ class VIEW3D_PT_tools_active(ToolSelectPanelHelper, Panel): class SEQUENCER_PT_tools_active(ToolSelectPanelHelper, Panel): - bl_space_type = "SEQUENCE_EDITOR" - bl_region_type = "TOOLS" + bl_space_type = 'SEQUENCE_EDITOR' + bl_region_type = 'TOOLS' bl_label = "Tools" # not visible - bl_options = {"HIDE_HEADER"} + bl_options = {'HIDE_HEADER'} # Satisfy the `ToolSelectPanelHelper` API. keymap_prefix = "Sequence Editor Tool:" @@ -3263,8 +3231,9 @@ class SEQUENCER_PT_tools_active(ToolSelectPanelHelper, Panel): # The keys match sequence editors view type: `context.space_data.view_type`. # The values represent the tools, see `ToolSelectPanelHelper` for details. _tools = { - None: [], - "PREVIEW": [ + None: [ + ], + 'PREVIEW': [ *_tools_select, _defs_sequencer_generic.cursor, None, @@ -3276,12 +3245,12 @@ class SEQUENCER_PT_tools_active(ToolSelectPanelHelper, Panel): _defs_sequencer_generic.sample, *_tools_annotate, ], - "SEQUENCER": [ + 'SEQUENCER': [ *_tools_select, _defs_sequencer_generic.blade, _defs_sequencer_generic.retime, ], - "SEQUENCER_PREVIEW": [ + 'SEQUENCER_PREVIEW': [ *_tools_select, None, *_tools_annotate, @@ -3300,6 +3269,5 @@ classes = ( if __name__ == "__main__": # only for live edit. from bpy.utils import register_class - for cls in classes: register_class(cls) -- 2.30.2 From 47bde28f3ce767ff307322f04740d8472091e3ca Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Thu, 13 Jul 2023 00:55:35 -0700 Subject: [PATCH 230/279] temp-sculpt-dyntopo: Remove various unused code --- source/blender/blenkernel/BKE_customdata.h | 19 +- source/blender/blenkernel/CMakeLists.txt | 71 - source/blender/blenkernel/intern/attribute.cc | 18 +- .../blender/blenkernel/intern/customdata.cc | 70 +- source/blender/blenkernel/intern/dyntopo.cc | 62 +- source/blender/blenkernel/intern/pbvh.c | 5780 ----------------- .../bmesh/intern/bmesh_mesh_convert.cc | 4 +- 7 files changed, 74 insertions(+), 5950 deletions(-) delete mode 100644 source/blender/blenkernel/intern/pbvh.c diff --git a/source/blender/blenkernel/BKE_customdata.h b/source/blender/blenkernel/BKE_customdata.h index 13c48dcdf8f..f486087db0f 100644 --- a/source/blender/blenkernel/BKE_customdata.h +++ b/source/blender/blenkernel/BKE_customdata.h @@ -135,7 +135,7 @@ bool CustomData_has_referenced(const struct CustomData *data); * implemented for `mloopuv/mloopcol`, for now. */ void CustomData_data_copy_value(eCustomDataType type, const void *source, void *dest); -void CustomData_data_set_default_value(const eCustomDataType type, void *elem); +void CustomData_data_set_default_value(eCustomDataType type, void *elem); /** * Mixes the "value" (e.g. `mloopuv` UV or `mloopcol` colors) from one block into @@ -332,9 +332,7 @@ bool CustomData_has_layer_named(const struct CustomData *data, */ int CustomData_number_of_layers(const struct CustomData *data, eCustomDataType type); int CustomData_number_of_anonymous_layers(const struct CustomData *data, eCustomDataType type); -int CustomData_number_of_layers_typemask(const struct CustomData *data, - eCustomDataMask mask, - bool skip_temporary); +int CustomData_number_of_layers_typemask(const struct CustomData *data, eCustomDataMask mask); /** * Set the #CD_FLAG_NOCOPY flag in custom data layers where the mask is @@ -373,21 +371,11 @@ void CustomData_bmesh_swap_data(struct CustomData *source, struct CustomData *dest, void *src_block, void **dest_block); -/* Executes a simple pointer swap; - * if an element ID attribute exists (cd_id_offset is not -1) - * it will unswap IDs. CD_TOOLFLAGS layer is also unswapped - * if it exists. - */ -void CustomData_bmesh_swap_data_simple(CustomData *data, - void **block1, - void **block2, - int cd_id_offset); void CustomData_copy_elements(eCustomDataType type, void *src_data_ofs, void *dst_data_ofs, int count); - void CustomData_bmesh_copy_data(const struct CustomData *source, struct CustomData *dest, void *src_block, @@ -839,9 +827,6 @@ void CustomData_blend_write(BlendWriter *writer, void CustomData_blend_read(struct BlendDataReader *reader, struct CustomData *data, int count); -void CustomData_unmark_temporary_nocopy(struct CustomData *data); -void CustomData_mark_temporary_nocopy(struct CustomData *data); - void CustomData_regen_active_refs(CustomData *data); #ifndef NDEBUG diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt index 8573b056a3a..0de5f1619fb 100644 --- a/source/blender/blenkernel/CMakeLists.txt +++ b/source/blender/blenkernel/CMakeLists.txt @@ -859,74 +859,3 @@ if(WITH_GTESTS) add_dependencies(bf_blenkernel_tests bf_rna) endif() -if(false) - set(PBVH_CACHE_TEST_INC - . - ../blenfont - ../blenlib - ../blenloader - ../blentranslation - ../bmesh - ../depsgraph - ../draw - ../functions - ../gpencil_modifiers - ../gpu - ../ikplugin - ../imbuf - ../makesdna - ../makesrna - ../modifiers - ../nodes - ../render - ../sequencer - ../shader_fx - ../simulation - ../../../intern/eigen - ../../../intern/ghost - ../../../intern/glew-mx - ../../../intern/guardedalloc - ../../../intern/iksolver/extern - ../../../intern/atomic - ../../../intern/clog - ../../../intern/libmv - ../../../intern/mantaflow/extern - ../../../intern/memutil - ../../../intern/mikktspace - ../../../intern/opensubdiv - ../../../extern/curve_fit_nd - ) - - set(PBVH_CACHE_TEST_SRC - intern/pbvh_cache_test_main.c - ) - - setup_libdirs() - - add_executable(pbvh_cache_test ${PBVH_CACHE_TEST_SRC} ${PBVH_CACHE_TEST_INC}) - setup_platform_linker_flags(pbvh_cache_test) - - target_link_libraries(pbvh_cache_test bf_blenkernel bf_bmesh bf_intern_ghost bf_blenlib bf_intern_guardedalloc) - - if(WIN32) - set_target_properties(pbvh_cache_test PROPERTIES VS_GLOBAL_VcpkgEnabled "false") - set_target_properties(pbvh_cache_test PROPERTIES - PDB_NAME "pbvh_cache_test_private" - PDB_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/$") - if(WITH_WINDOWS_PDB AND WITH_WINDOWS_STRIPPED_PDB) - # This is slightly messy, but single target generators like ninja will not have the - # CMAKE_CFG_INTDIR variable and multitarget generators like msbuild will not have - # CMAKE_BUILD_TYPE. This can be simplified by target_link_options and the $ - # generator expression in newer cmake (2.13+) but until that time this fill have suffice. - if(CMAKE_BUILD_TYPE) - set_property(TARGET pbvh_cache_test APPEND_STRING PROPERTY LINK_FLAGS " /PDBSTRIPPED:${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_BUILD_TYPE}/pbvh_cache_test_public.pdb") - else() - set_property(TARGET pbvh_cache_test APPEND_STRING PROPERTY LINK_FLAGS " /PDBSTRIPPED:${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/pbvh_cache_test_public.pdb") - endif() - endif() - endif() - - if (WIN32) - target_link_libraries(pbvh_cache_test Vfw32.lib Imm32.lib Version.lib Comctl32.lib Shcore.lib Pathcch.lib) - endif() -endif() diff --git a/source/blender/blenkernel/intern/attribute.cc b/source/blender/blenkernel/intern/attribute.cc index b780416da11..66ace574f7f 100644 --- a/source/blender/blenkernel/intern/attribute.cc +++ b/source/blender/blenkernel/intern/attribute.cc @@ -558,6 +558,22 @@ CustomDataLayer *BKE_id_attribute_search(ID *id, return nullptr; } +static int count_layers_typemask(const CustomData *data, eCustomDataMask mask, bool skip_temporary) +{ + int number = 0; + + for (int i = 0; i < data->totlayer; i++) { + bool ok = mask & CD_TYPE_AS_MASK(data->layers[i].type); + ok = ok && (!skip_temporary || !(data->layers[i].flag & (int)CD_FLAG_TEMPORARY)); + + if (ok) { + number++; + } + } + + return number; +} + int BKE_id_attributes_length(const ID *id, eAttrDomainMask domain_mask, eCustomDataMask mask, @@ -575,7 +591,7 @@ int BKE_id_attributes_length(const ID *id, } if ((1 << (int)domain) & domain_mask) { - length += CustomData_number_of_layers_typemask(customdata, mask, skip_temporary); + length += count_layers_typemask(customdata, mask, skip_temporary); } } diff --git a/source/blender/blenkernel/intern/customdata.cc b/source/blender/blenkernel/intern/customdata.cc index 53be2edd6c4..2d614041502 100644 --- a/source/blender/blenkernel/intern/customdata.cc +++ b/source/blender/blenkernel/intern/customdata.cc @@ -2289,13 +2289,7 @@ void CustomData_regen_active_refs(CustomData *data) int n = layer - base; if (n < 0) { - printf("error!\n"); - for (int j = 0; j < data->totlayer; j++) { - printf("%s", i == j ? "->" : " "); - printf("%d : \"%s\"\n", - data->layers[i].type, - data->layers[i].name ? data->layers[i].name : ""); - } + BLI_assert_unreachable(); } if (layer->active) { base->active = n; @@ -2401,10 +2395,6 @@ static bool customdata_merge_internal(const CustomData *source, const eCustomDataType type = eCustomDataType(src_layer.type); const int src_layer_flag = src_layer.flag; - if (src_layer.flag & CD_FLAG_NOCOPY) { - continue; - } - if (type != last_type) { current_type_layer_count = 0; max_current_type_layer_count = CustomData_layertype_layers_max(type); @@ -3479,17 +3469,12 @@ int CustomData_number_of_anonymous_layers(const CustomData *data, const eCustomD return number; } -int CustomData_number_of_layers_typemask(const CustomData *data, - eCustomDataMask mask, - bool skip_temporary) +int CustomData_number_of_layers_typemask(const CustomData *data, eCustomDataMask mask) { int number = 0; for (int i = 0; i < data->totlayer; i++) { - bool ok = mask & CD_TYPE_AS_MASK(data->layers[i].type); - ok = ok && (!skip_temporary || !(data->layers[i].flag & (int)CD_FLAG_TEMPORARY)); - - if (ok) { + if (mask & CD_TYPE_AS_MASK(data->layers[i].type)) { number++; } } @@ -3497,24 +3482,6 @@ int CustomData_number_of_layers_typemask(const CustomData *data, return number; } -void CustomData_unmark_temporary_nocopy(CustomData *data) -{ - for (int i = 0; i < data->totlayer; i++) { - if (data->layers[i].flag & CD_FLAG_TEMPORARY) { - data->layers[i].flag &= ~CD_FLAG_NOCOPY; - } - } -} - -void CustomData_mark_temporary_nocopy(CustomData *data) -{ - for (int i = 0; i < data->totlayer; i++) { - if (data->layers[i].flag & CD_FLAG_TEMPORARY) { - data->layers[i].flag |= CD_FLAG_NOCOPY; - } - } -} - void CustomData_free_temporary(CustomData *data, const int totelem) { int i, j; @@ -4044,7 +4011,7 @@ bool CustomData_bmesh_merge_layout(const CustomData *source, const char htype) { - if (CustomData_number_of_layers_typemask(source, mask, false) == 0) { + if (CustomData_number_of_layers_typemask(source, mask) == 0) { return false; } @@ -4220,7 +4187,7 @@ void CustomData_bmesh_free_block_data_exclude_by_type(CustomData *data, } } -void CustomData_data_set_default_value(const eCustomDataType type, void *elem) +void CustomData_data_set_default_value(eCustomDataType type, void *elem) { const LayerTypeInfo *typeInfo = layerType_getInfo(type); if (typeInfo->set_default_value) { @@ -4249,31 +4216,6 @@ void CustomData_bmesh_set_default(CustomData *data, void **block) } } -void CustomData_bmesh_swap_data_simple(CustomData *data, void **block1, void **block2, int cd_id) -{ - std::swap(*block1, *block2); - - int cd_toolflags = data->typemap[CD_TOOLFLAGS]; - cd_toolflags = cd_toolflags != -1 ? data->layers[cd_toolflags].offset : -1; - - /* Unswap toolflags and/or element IDs if they exist */ - if (*block1 && *block2) { - if (cd_toolflags != -1) { - MToolFlags *flags1 = static_cast(POINTER_OFFSET(*block1, cd_toolflags)); - MToolFlags *flags2 = static_cast(POINTER_OFFSET(*block2, cd_toolflags)); - - std::swap(*flags1, *flags2); - } - - if (cd_id != -1) { - int *id1 = static_cast(POINTER_OFFSET(*block1, cd_id)); - int *id2 = static_cast(POINTER_OFFSET(*block2, cd_id)); - - std::swap(*id1, *id2); - } - } -} - void CustomData_bmesh_swap_data(CustomData *source, CustomData *dest, void *src_block, @@ -4382,7 +4324,7 @@ void CustomData_bmesh_copy_data_exclude_by_type(const CustomData *source, } /* The old code broke if the ordering differed between two customdata sets. - * Led to disappearing face sets. + * Led to disappearing face sets. See PR #108683. */ blender::Set donelayers; diff --git a/source/blender/blenkernel/intern/dyntopo.cc b/source/blender/blenkernel/intern/dyntopo.cc index 811fc4446dd..e0a0565c189 100644 --- a/source/blender/blenkernel/intern/dyntopo.cc +++ b/source/blender/blenkernel/intern/dyntopo.cc @@ -66,6 +66,36 @@ namespace blender::bke::dyntopo { using namespace blender::bke::sculpt; +/* Executes a simple pointer swap; + * if an element ID attribute exists (cd_id_offset is not -1) + * it will unswap IDs. CD_TOOLFLAGS layer is also unswapped + * if it exists. + */ +static void bmesh_swap_data_simple(CustomData *data, void **block1, void **block2, int cd_id) +{ + std::swap(*block1, *block2); + + int cd_toolflags = data->typemap[CD_TOOLFLAGS]; + cd_toolflags = cd_toolflags != -1 ? data->layers[cd_toolflags].offset : -1; + + /* Unswap toolflags and/or element IDs if they exist */ + if (*block1 && *block2) { + if (cd_toolflags != -1) { + MToolFlags *flags1 = static_cast(POINTER_OFFSET(*block1, cd_toolflags)); + MToolFlags *flags2 = static_cast(POINTER_OFFSET(*block2, cd_toolflags)); + + std::swap(*flags1, *flags2); + } + + if (cd_id != -1) { + int *id1 = static_cast(POINTER_OFFSET(*block1, cd_id)); + int *id2 = static_cast(POINTER_OFFSET(*block2, cd_id)); + + std::swap(*id1, *id2); + } + } +} + static void edge_queue_create_local(EdgeQueueContext *eq_ctx, PBVH *pbvh, PBVHTopologyUpdateMode local_mode); @@ -1913,10 +1943,10 @@ static bool cleanup_valence_3_4(EdgeQueueContext *ectx, PBVH *pbvh) f2 = pbvh_bmesh_face_create(pbvh, n, vs, nullptr, example, true, false); BM_idmap_check_assign(pbvh->bm_idmap, reinterpret_cast(f2)); - CustomData_bmesh_swap_data_simple(&pbvh->header.bm->ldata, - &f2->l_first->prev->head.data, - &ls[3]->head.data, - pbvh->bm_idmap->cd_id_off[BM_LOOP]); + bmesh_swap_data_simple(&pbvh->header.bm->ldata, + &f2->l_first->prev->head.data, + &ls[3]->head.data, + pbvh->bm_idmap->cd_id_off[BM_LOOP]); CustomData_bmesh_copy_data(&pbvh->header.bm->ldata, &pbvh->header.bm->ldata, ls[0]->head.data, @@ -1934,18 +1964,18 @@ static bool cleanup_valence_3_4(EdgeQueueContext *ectx, PBVH *pbvh) } if (f1) { - CustomData_bmesh_swap_data_simple(&pbvh->header.bm->ldata, - &f1->l_first->head.data, - &ls[0]->head.data, - pbvh->bm_idmap->cd_id_off[BM_LOOP]); - CustomData_bmesh_swap_data_simple(&pbvh->header.bm->ldata, - &f1->l_first->next->head.data, - &ls[1]->head.data, - pbvh->bm_idmap->cd_id_off[BM_LOOP]); - CustomData_bmesh_swap_data_simple(&pbvh->header.bm->ldata, - &f1->l_first->prev->head.data, - &ls[2]->head.data, - pbvh->bm_idmap->cd_id_off[BM_LOOP]); + bmesh_swap_data_simple(&pbvh->header.bm->ldata, + &f1->l_first->head.data, + &ls[0]->head.data, + pbvh->bm_idmap->cd_id_off[BM_LOOP]); + bmesh_swap_data_simple(&pbvh->header.bm->ldata, + &f1->l_first->next->head.data, + &ls[1]->head.data, + pbvh->bm_idmap->cd_id_off[BM_LOOP]); + bmesh_swap_data_simple(&pbvh->header.bm->ldata, + &f1->l_first->prev->head.data, + &ls[2]->head.data, + pbvh->bm_idmap->cd_id_off[BM_LOOP]); BM_log_face_added(pbvh->header.bm, pbvh->bm_log, f1); validate_face(pbvh, f1, CHECK_FACE_MANIFOLD); diff --git a/source/blender/blenkernel/intern/pbvh.c b/source/blender/blenkernel/intern/pbvh.c deleted file mode 100644 index 7e46b383e32..00000000000 --- a/source/blender/blenkernel/intern/pbvh.c +++ /dev/null @@ -1,5780 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ - -/** \file - * \ingroup bke - */ - -#include "MEM_guardedalloc.h" - -#include "BLI_utildefines.h" - -#include "BLI_alloca.h" -#include "BLI_array.h" -#include "BLI_bitmap.h" -#include "BLI_ghash.h" -#include "BLI_listbase.h" -#include "BLI_math.h" -#include "BLI_rand.h" -#include "BLI_string.h" -#include "BLI_task.h" - -#include "DNA_mesh_types.h" -#include "DNA_meshdata_types.h" -#include "DNA_object_types.h" -#include "DNA_scene_types.h" - -#include "BKE_attribute.h" -#include "BKE_ccg.h" -#include "BKE_main.h" -#include "BKE_mesh.h" /* for BKE_mesh_calc_normals */ -#include "BKE_mesh_mapping.h" -#include "BKE_object.h" -#include "BKE_paint.h" -#include "BKE_pbvh.h" -#include "BKE_subdiv_ccg.h" - -#include "DEG_depsgraph_query.h" - -#include "DRW_pbvh.h" -#include "PIL_time.h" - -#include "PIL_time.h" - -#include "bmesh.h" - -#include "atomic_ops.h" - -#include "pbvh_intern.hh" - -#include - -#define LEAF_LIMIT 4000 - -/* Uncomment to test if triangles of the same face are - * properly clustered into single nodes. - */ -//#define TEST_PBVH_FACE_SPLIT - -/* Uncomment to test that faces are only assigned to one PBVHNode */ -//#define VALIDATE_UNIQUE_NODE_FACES - -//#define PERFCNTRS - -typedef struct PBVHStack { - PBVHNode *node; - bool revisiting; -} PBVHStack; - -typedef struct PBVHIter { - PBVH *pbvh; - BKE_pbvh_SearchCallback scb; - void *search_data; - - PBVHStack *stack; - int stacksize; - - PBVHStack stackfixed[PBVH_STACK_FIXED_DEPTH]; - int stackspace; -} PBVHIter; - -void BB_reset(BB *bb) -{ - bb->bmin[0] = bb->bmin[1] = bb->bmin[2] = FLT_MAX; - bb->bmax[0] = bb->bmax[1] = bb->bmax[2] = -FLT_MAX; -} - -void BB_intersect(BB *r_out, BB *a, BB *b) -{ - for (int i = 0; i < 3; i++) { - r_out->bmin[i] = max_ff(a->bmin[i], b->bmin[i]); - r_out->bmax[i] = min_ff(a->bmax[i], b->bmax[i]); - - if (r_out->bmax[i] < r_out->bmin[i]) { - r_out->bmax[i] = r_out->bmin[i] = 0.0f; - } - } -} - -float BB_volume(const BB *bb) -{ - float dx = bb->bmax[0] - bb->bmin[0]; - float dy = bb->bmax[1] - bb->bmin[1]; - float dz = bb->bmax[2] - bb->bmin[2]; - - return dx * dy * dz; -} - -/* Expand the bounding box to include a new coordinate */ -void BB_expand(BB *bb, const float co[3]) -{ - for (int i = 0; i < 3; i++) { - bb->bmin[i] = min_ff(bb->bmin[i], co[i]); - bb->bmax[i] = max_ff(bb->bmax[i], co[i]); - } -} - -void BB_expand_with_bb(BB *bb, BB *bb2) -{ - for (int i = 0; i < 3; i++) { - bb->bmin[i] = min_ff(bb->bmin[i], bb2->bmin[i]); - bb->bmax[i] = max_ff(bb->bmax[i], bb2->bmax[i]); - } -} - -int BB_widest_axis(const BB *bb) -{ - float dim[3]; - - for (int i = 0; i < 3; i++) { - dim[i] = bb->bmax[i] - bb->bmin[i]; - } - - if (dim[0] > dim[1]) { - if (dim[0] > dim[2]) { - return 0; - } - - return 2; - } - - if (dim[1] > dim[2]) { - return 1; - } - - return 2; -} - -void BBC_update_centroid(BBC *bbc) -{ - for (int i = 0; i < 3; i++) { - bbc->bcentroid[i] = (bbc->bmin[i] + bbc->bmax[i]) * 0.5f; - } -} - -/* Not recursive */ -static void update_node_vb(PBVH *pbvh, PBVHNode *node, int updateflag) -{ - if (!(updateflag & (PBVH_UpdateBB | PBVH_UpdateOriginalBB))) { - return; - } - - /* cannot clear flag here, causes leaky pbvh */ - // node->flag &= ~(updateflag & (PBVH_UpdateBB | PBVH_UpdateOriginalBB)); - - BB vb; - BB orig_vb; - - BB_reset(&vb); - BB_reset(&orig_vb); - - bool do_orig = true; // XXX updateflag & PBVH_UpdateOriginalBB; - bool do_normal = true; // XXX updateflag & PBVH_UpdateBB; - - if (node->flag & PBVH_Leaf) { - PBVHVertexIter vd; - - BKE_pbvh_vertex_iter_begin (pbvh, node, vd, PBVH_ITER_ALL) { - if (do_normal) { - BB_expand(&vb, vd.co); - } - - if (do_orig) { - float *origco = nullptr; - - if (pbvh->header.type == PBVH_BMESH) { - origco = pbvh->cd_origco != -1 ? BM_ELEM_CD_PTR(vd.bm_vert, pbvh->cd_origco) : - nullptr; - } - else { - origco = pbvh->origco - } - - if (origco) { - BB_expand(&orig_vb, vd.co); - } - else { - BB_expand(&orig_vb, mv->origco); - } - } - } - BKE_pbvh_vertex_iter_end; - } - else { - if (do_normal) { - BB_expand_with_bb(&vb, &pbvh->nodes[node->children_offset].vb); - BB_expand_with_bb(&vb, &pbvh->nodes[node->children_offset + 1].vb); - } - - if (do_orig) { - BB_expand_with_bb(&orig_vb, &pbvh->nodes[node->children_offset].orig_vb); - BB_expand_with_bb(&orig_vb, &pbvh->nodes[node->children_offset + 1].orig_vb); - } - } - - if (do_normal) { - node->vb = vb; - } - - if (do_orig) { -#if 0 - float size[3]; - - sub_v3_v3v3(size, orig_vb.bmax, orig_vb.bmin); - mul_v3_fl(size, 0.05); - - sub_v3_v3(orig_vb.bmin, size); - add_v3_v3(orig_vb.bmax, size); -#endif - node->orig_vb = orig_vb; - } -} - -// void BKE_pbvh_node_BB_reset(PBVHNode *node) -//{ -// BB_reset(&node->vb); -//} -// -// void BKE_pbvh_node_BB_expand(PBVHNode *node, float co[3]) -//{ -// BB_expand(&node->vb, co); -//} - -static bool face_materials_match(const PBVH *pbvh, const int a, const int b) -{ - if (pbvh->material_indices) { - if (pbvh->material_indices[a] != pbvh->material_indices[b]) { - return false; - } - } - return (pbvh->mpoly[a].flag & ME_SMOOTH) == (pbvh->mpoly[b].flag & ME_SMOOTH); -} - -static bool grid_materials_match(const DMFlagMat *f1, const DMFlagMat *f2) -{ - return ((f1->flag & ME_SMOOTH) == (f2->flag & ME_SMOOTH) && (f1->mat_nr == f2->mat_nr)); -} - -/* Adapted from BLI_kdopbvh.c */ -/* Returns the index of the first element on the right of the partition */ -static int partition_indices_faces(int *prim_indices, - int *prim_scratch, - int lo, - int hi, - int axis, - float mid, - BBC *prim_bbc, - const MLoopTri *looptri) -{ - for (int i = lo; i < hi; i++) { - prim_scratch[i - lo] = prim_indices[i]; - } - - int lo2 = lo, hi2 = hi - 1; - int i1 = lo, i2 = 0; - - while (i1 < hi) { - int poly = looptri[prim_scratch[i2]].poly; - bool side = prim_bbc[prim_scratch[i2]].bcentroid[axis] >= mid; - - while (i1 < hi && looptri[prim_scratch[i2]].poly == poly) { - prim_indices[side ? hi2-- : lo2++] = prim_scratch[i2]; - i1++; - i2++; - } - } - - return lo2; -} - -static int partition_indices_grids(int *prim_indices, - int *prim_scratch, - int lo, - int hi, - int axis, - float mid, - BBC *prim_bbc, - SubdivCCG *subdiv_ccg) -{ - for (int i = lo; i < hi; i++) { - prim_scratch[i - lo] = prim_indices[i]; - } - - int lo2 = lo, hi2 = hi - 1; - int i1 = lo, i2 = 0; - - while (i1 < hi) { - int poly = BKE_subdiv_ccg_grid_to_face_index(subdiv_ccg, prim_scratch[i2]); - bool side = prim_bbc[prim_scratch[i2]].bcentroid[axis] >= mid; - - while (i1 < hi && BKE_subdiv_ccg_grid_to_face_index(subdiv_ccg, prim_scratch[i2]) == poly) { - prim_indices[side ? hi2-- : lo2++] = prim_scratch[i2]; - i1++; - i2++; - } - } - - return lo2; -} - -/* Returns the index of the first element on the right of the partition */ -static int partition_indices_material(PBVH *pbvh, int lo, int hi) -{ - const MLoopTri *looptri = pbvh->looptri; - const DMFlagMat *flagmats = pbvh->grid_flag_mats; - const int *indices = pbvh->prim_indices; - int i = lo, j = hi; - - for (;;) { - if (pbvh->looptri) { - const int first = looptri[pbvh->prim_indices[lo]].poly; - for (; face_materials_match(pbvh, first, looptri[indices[i]].poly); i++) { - /* pass */ - } - for (; !face_materials_match(pbvh, first, looptri[indices[j]].poly); j--) { - /* pass */ - } - } - else { - const DMFlagMat *first = &flagmats[pbvh->prim_indices[lo]]; - for (; grid_materials_match(first, &flagmats[indices[i]]); i++) { - /* pass */ - } - for (; !grid_materials_match(first, &flagmats[indices[j]]); j--) { - /* pass */ - } - } - - if (!(i < j)) { - return i; - } - - SWAP(int, pbvh->prim_indices[i], pbvh->prim_indices[j]); - i++; - } -} - -void pbvh_grow_nodes(PBVH *pbvh, int totnode) -{ - if (UNLIKELY(totnode > pbvh->node_mem_count)) { - pbvh->node_mem_count = pbvh->node_mem_count + (pbvh->node_mem_count / 3); - if (pbvh->node_mem_count < totnode) { - pbvh->node_mem_count = totnode; - } - pbvh->nodes = MEM_recallocN(pbvh->nodes, sizeof(PBVHNode) * pbvh->node_mem_count); - } - - pbvh->totnode = totnode; - - for (int i = 0; i < pbvh->totnode; i++) { - PBVHNode *node = pbvh->nodes + i; - - if (!node->id) { - node->id = ++pbvh->idgen; - } - } -} - -/* Add a vertex to the map, with a positive value for unique vertices and - * a negative value for additional vertices */ -static int map_insert_vert(PBVH *pbvh, GHash *map, uint *face_verts, uint *uniq_verts, int vertex) -{ - void *key, **value_p; - - key = POINTER_FROM_INT(vertex); - if (!BLI_ghash_ensure_p(map, key, &value_p)) { - int value_i; - if (!pbvh->vert_bitmap[vertex]) { - pbvh->vert_bitmap[vertex] = true; - value_i = *uniq_verts; - (*uniq_verts)++; - } - else { - value_i = ~(*face_verts); - (*face_verts)++; - } - *value_p = POINTER_FROM_INT(value_i); - return value_i; - } - - return POINTER_AS_INT(*value_p); -} - -/* Find vertices used by the faces in this node and update the draw buffers */ -static void build_mesh_leaf_node(PBVH *pbvh, PBVHNode *node) -{ - bool has_visible = false; - - node->uniq_verts = node->face_verts = 0; - const int totface = node->totprim; - - /* reserve size is rough guess */ - GHash *map = BLI_ghash_int_new_ex("build_mesh_leaf_node gh", 2 * totface); - - int(*face_vert_indices)[3] = MEM_mallocN(sizeof(int[3]) * totface, "bvh node face vert indices"); - - node->face_vert_indices = (const int(*)[3])face_vert_indices; - - if (pbvh->respect_hide == false) { - has_visible = true; - } - - for (int i = 0; i < totface; i++) { - const MLoopTri *lt = &pbvh->looptri[node->prim_indices[i]]; - for (int j = 0; j < 3; j++) { - face_vert_indices[i][j] = map_insert_vert( - pbvh, map, &node->face_verts, &node->uniq_verts, pbvh->mloop[lt->tri[j]].v); - } - - if (has_visible == false) { - if (!paint_is_face_hidden(lt, pbvh->hide_poly)) { - has_visible = true; - } - } - } - - int *vert_indices = MEM_callocN(sizeof(int) * (node->uniq_verts + node->face_verts), - "bvh node vert indices"); - node->vert_indices = vert_indices; - - /* Build the vertex list, unique verts first */ - GHashIterator gh_iter; - GHASH_ITER (gh_iter, map) { - void *value = BLI_ghashIterator_getValue(&gh_iter); - int ndx = POINTER_AS_INT(value); - - if (ndx < 0) { - ndx = -ndx + node->uniq_verts - 1; - } - - vert_indices[ndx] = POINTER_AS_INT(BLI_ghashIterator_getKey(&gh_iter)); - } - - for (int i = 0; i < totface; i++) { - const int sides = 3; - - for (int j = 0; j < sides; j++) { - if (face_vert_indices[i][j] < 0) { - face_vert_indices[i][j] = -face_vert_indices[i][j] + node->uniq_verts - 1; - } - } - } - - BKE_pbvh_node_mark_rebuild_draw(node); - - BKE_pbvh_node_fully_hidden_set(node, !has_visible); - BKE_pbvh_vert_tag_update_normal_tri_area(node); - - BLI_ghash_free(map, NULL, NULL); -} - -static void update_vb(PBVH *pbvh, PBVHNode *node, BBC *prim_bbc, int offset, int count) -{ - BB_reset(&node->vb); - for (int i = offset + count - 1; i >= offset; i--) { - BB_expand_with_bb(&node->vb, (BB *)(&prim_bbc[pbvh->prim_indices[i]])); - } - node->orig_vb = node->vb; -} - -int BKE_pbvh_count_grid_quads(BLI_bitmap **grid_hidden, - const int *grid_indices, - int totgrid, - int gridsize, - int display_gridsize) -{ - const int gridarea = (gridsize - 1) * (gridsize - 1); - int totquad = 0; - - /* grid hidden layer is present, so have to check each grid for - * visibility */ - - int depth1 = (int)(log2((double)gridsize - 1.0) + DBL_EPSILON); - int depth2 = (int)(log2((double)display_gridsize - 1.0) + DBL_EPSILON); - - int skip = depth2 < depth1 ? 1 << (depth1 - depth2 - 1) : 1; - - for (int i = 0; i < totgrid; i++) { - const BLI_bitmap *gh = grid_hidden[grid_indices[i]]; - - if (gh) { - /* grid hidden are present, have to check each element */ - for (int y = 0; y < gridsize - skip; y += skip) { - for (int x = 0; x < gridsize - skip; x += skip) { - if (!paint_is_grid_face_hidden(gh, gridsize, x, y)) { - totquad++; - } - } - } - } - else { - totquad += gridarea; - } - } - - return totquad; -} - -static void build_grid_leaf_node(PBVH *pbvh, PBVHNode *node) -{ - int totquads = BKE_pbvh_count_grid_quads(pbvh->grid_hidden, - node->prim_indices, - node->totprim, - pbvh->gridkey.grid_size, - pbvh->gridkey.grid_size); - BKE_pbvh_node_fully_hidden_set(node, (totquads == 0)); - BKE_pbvh_node_mark_rebuild_draw(node); - BKE_pbvh_vert_tag_update_normal_tri_area(node); -} - -static void build_leaf(PBVH *pbvh, int node_index, BBC *prim_bbc, int offset, int count) -{ - pbvh->nodes[node_index].flag |= PBVH_Leaf; - - pbvh->nodes[node_index].prim_indices = pbvh->prim_indices + offset; - pbvh->nodes[node_index].totprim = count; - - /* Still need vb for searches */ - update_vb(pbvh, &pbvh->nodes[node_index], prim_bbc, offset, count); - - if (pbvh->looptri) { - build_mesh_leaf_node(pbvh, pbvh->nodes + node_index); - } - else { - build_grid_leaf_node(pbvh, pbvh->nodes + node_index); - } -} - -/* Return zero if all primitives in the node can be drawn with the - * same material (including flat/smooth shading), non-zero otherwise */ -static bool leaf_needs_material_split(PBVH *pbvh, int offset, int count) -{ - if (count <= 1) { - return false; - } - - if (pbvh->looptri) { - const MLoopTri *first = &pbvh->looptri[pbvh->prim_indices[offset]]; - for (int i = offset + count - 1; i > offset; i--) { - int prim = pbvh->prim_indices[i]; - if (!face_materials_match(pbvh, first->poly, pbvh->looptri[prim].poly)) { - return true; - } - } - } - else { - const DMFlagMat *first = &pbvh->grid_flag_mats[pbvh->prim_indices[offset]]; - - for (int i = offset + count - 1; i > offset; i--) { - int prim = pbvh->prim_indices[i]; - if (!grid_materials_match(first, &pbvh->grid_flag_mats[prim])) { - return true; - } - } - } - - return false; -} - -#ifdef TEST_PBVH_FACE_SPLIT -static void test_face_boundaries(PBVH *pbvh) -{ - int faces_num = BKE_pbvh_num_faces(pbvh); - int *node_map = MEM_calloc_arrayN(faces_num, sizeof(int), __func__); - for (int i = 0; i < faces_num; i++) { - node_map[i] = -1; - } - - for (int i = 0; i < pbvh->totnode; i++) { - PBVHNode *node = pbvh->nodes + i; - - if (!(node->flag & PBVH_Leaf)) { - continue; - } - - switch (BKE_pbvh_type(pbvh)) { - case PBVH_FACES: { - for (int j = 0; j < node->totprim; j++) { - int poly = pbvh->looptri[node->prim_indices[j]].poly; - - if (node_map[poly] >= 0 && node_map[poly] != i) { - int old_i = node_map[poly]; - int prim_i = node->prim_indices - pbvh->prim_indices + j; - - printf("PBVH split error; poly: %d, prim_i: %d, node1: %d, node2: %d, totprim: %d\n", - poly, - prim_i, - old_i, - i, - node->totprim); - } - - node_map[poly] = i; - } - break; - } - case PBVH_GRIDS: - break; - case PBVH_BMESH: - break; - } - } - - MEM_SAFE_FREE(node_map); -} -#endif - -/* Recursively build a node in the tree - * - * vb is the voxel box around all of the primitives contained in - * this node. - * - * cb is the bounding box around all the centroids of the primitives - * contained in this node - * - * offset and start indicate a range in the array of primitive indices - */ - -static void build_sub(PBVH *pbvh, - int node_index, - BB *cb, - BBC *prim_bbc, - int offset, - int count, - int *prim_scratch, - int depth) -{ - int end; - BB cb_backing; - - if (!prim_scratch) { - prim_scratch = MEM_malloc_arrayN(pbvh->totprim, sizeof(int), __func__); - } - - /* Decide whether this is a leaf or not */ - const bool below_leaf_limit = count <= pbvh->leaf_limit || depth == PBVH_STACK_FIXED_DEPTH - 1; - if (below_leaf_limit) { - if (!leaf_needs_material_split(pbvh, offset, count)) { - build_leaf(pbvh, node_index, prim_bbc, offset, count); - - if (node_index == 0) { - MEM_SAFE_FREE(prim_scratch); - } - - return; - } - } - - /* Add two child nodes */ - pbvh->nodes[node_index].children_offset = pbvh->totnode; - pbvh_grow_nodes(pbvh, pbvh->totnode + 2); - - /* Update parent node bounding box */ - update_vb(pbvh, &pbvh->nodes[node_index], prim_bbc, offset, count); - - if (!below_leaf_limit) { - /* Find axis with widest range of primitive centroids */ - if (!cb) { - cb = &cb_backing; - BB_reset(cb); - for (int i = offset + count - 1; i >= offset; i--) { - BB_expand(cb, prim_bbc[pbvh->prim_indices[i]].bcentroid); - } - } - const int axis = BB_widest_axis(cb); - - /* Partition primitives along that axis */ - if (pbvh->header.type == PBVH_FACES) { - end = partition_indices_faces(pbvh->prim_indices, - prim_scratch, - offset, - offset + count, - axis, - (cb->bmax[axis] + cb->bmin[axis]) * 0.5f, - prim_bbc, - pbvh->looptri); - } - else { - end = partition_indices_grids(pbvh->prim_indices, - prim_scratch, - offset, - offset + count, - axis, - (cb->bmax[axis] + cb->bmin[axis]) * 0.5f, - prim_bbc, - pbvh->subdiv_ccg); - } - } - else { - /* Partition primitives by material */ - end = partition_indices_material(pbvh, offset, offset + count - 1); - } - - /* Build children */ - build_sub(pbvh, - pbvh->nodes[node_index].children_offset, - NULL, - prim_bbc, - offset, - end - offset, - prim_scratch, - depth + 1); - build_sub(pbvh, - pbvh->nodes[node_index].children_offset + 1, - NULL, - prim_bbc, - end, - offset + count - end, - prim_scratch, - depth + 1); - - if (node_index == 0) { - MEM_SAFE_FREE(prim_scratch); - } -} - -static void pbvh_build(PBVH *pbvh, BB *cb, BBC *prim_bbc, int totprim) -{ - if (totprim != pbvh->totprim) { - pbvh->totprim = totprim; - if (pbvh->nodes) { - MEM_freeN(pbvh->nodes); - } - if (pbvh->prim_indices) { - MEM_freeN(pbvh->prim_indices); - } - pbvh->prim_indices = MEM_mallocN(sizeof(int) * totprim, "bvh prim indices"); - for (int i = 0; i < totprim; i++) { - pbvh->prim_indices[i] = i; - } - pbvh->totnode = 0; - if (pbvh->node_mem_count < 100) { - pbvh->node_mem_count = 100; - pbvh->nodes = MEM_callocN(sizeof(PBVHNode) * pbvh->node_mem_count, "bvh initial nodes"); - } - } - - pbvh->totnode = 1; - build_sub(pbvh, 0, cb, prim_bbc, 0, totprim, NULL, 0); -} - -void BKE_pbvh_set_face_areas(PBVH *pbvh, float *face_areas) -{ - pbvh->face_areas = face_areas; -} - -/* XXX investigate this global. */ -bool pbvh_show_orig_co; - -static void pbvh_draw_args_init(PBVH *pbvh, PBVH_GPU_Args *args, PBVHNode *node) -{ - memset((void *)args, 0, sizeof(*args)); - - args->pbvh_type = pbvh->header.type; - args->mesh_verts_num = pbvh->totvert; - args->mesh_grids_num = pbvh->totgrid; - args->node = node; - - BKE_pbvh_node_num_verts(pbvh, node, NULL, &args->node_verts_num); - - args->grid_hidden = pbvh->grid_hidden; - args->face_sets_color_default = pbvh->face_sets_color_default; - args->face_sets_color_seed = pbvh->face_sets_color_seed; - args->vert_positions = pbvh->vert_positions; - args->mloop = pbvh->mloop; - args->mpoly = pbvh->mpoly; - args->mlooptri = pbvh->looptri; - args->flat_vcol_shading = pbvh->flat_vcol_shading; - args->show_orig = pbvh_show_orig_co; - args->updategen = node->updategen; - args->msculptverts = pbvh->msculptverts; - - if (ELEM(pbvh->header.type, PBVH_FACES, PBVH_GRIDS)) { - args->hide_poly = pbvh->pdata ? - CustomData_get_layer_named(pbvh->pdata, CD_PROP_BOOL, ".hide_poly") : - NULL; - } - - switch (pbvh->header.type) { - case PBVH_FACES: - args->mesh_faces_num = pbvh->mesh->totpoly; - args->vdata = pbvh->vdata; - args->ldata = pbvh->ldata; - args->pdata = pbvh->pdata; - args->totprim = node->totprim; - args->me = pbvh->mesh; - args->mpoly = pbvh->mpoly; - args->vert_normals = pbvh->vert_normals; - - args->active_color = pbvh->mesh->active_color_attribute; - args->render_color = pbvh->mesh->default_color_attribute; - - args->prim_indices = node->prim_indices; - args->face_sets = pbvh->face_sets; - break; - case PBVH_GRIDS: - args->vdata = pbvh->vdata; - args->ldata = pbvh->ldata; - args->pdata = pbvh->pdata; - args->ccg_key = pbvh->gridkey; - args->me = pbvh->mesh; - args->totprim = node->totprim; - args->grid_indices = node->prim_indices; - args->subdiv_ccg = pbvh->subdiv_ccg; - args->face_sets = pbvh->face_sets; - args->mpoly = pbvh->mpoly; - - args->active_color = pbvh->mesh->active_color_attribute; - args->render_color = pbvh->mesh->default_color_attribute; - - args->mesh_grids_num = pbvh->totgrid; - args->grids = pbvh->grids; - args->gridfaces = pbvh->gridfaces; - args->grid_flag_mats = pbvh->grid_flag_mats; - args->vert_normals = pbvh->vert_normals; - - args->face_sets = pbvh->face_sets; - break; - case PBVH_BMESH: - args->bm = pbvh->header.bm; - - args->active_color = pbvh->mesh->active_color_attribute; - args->render_color = pbvh->mesh->default_color_attribute; - - args->me = pbvh->mesh; - args->vdata = &args->bm->vdata; - args->ldata = &args->bm->ldata; - args->pdata = &args->bm->pdata; - args->bm_faces = node->bm_faces; - args->bm_other_verts = node->bm_other_verts; - args->bm_unique_vert = node->bm_unique_verts; - args->totprim = BLI_table_gset_len(node->bm_faces); - args->cd_mask_layer = CustomData_get_offset(&pbvh->header.bm->vdata, CD_PAINT_MASK); - - args->tribuf = node->tribuf; - args->tri_buffers = node->tri_buffers; - args->tot_tri_buffers = node->tot_tri_buffers; - - break; - } -} - -#ifdef VALIDATE_UNIQUE_NODE_FACES -static void pbvh_validate_node_prims(PBVH *pbvh) -{ - int totface = 0; - - if (pbvh->header.type == PBVH_BMESH) { - return; - } - - for (int i = 0; i < pbvh->totnode; i++) { - PBVHNode *node = pbvh->nodes + i; - - if (!(node->flag & PBVH_Leaf)) { - continue; - } - - for (int j = 0; j < node->totprim; j++) { - int poly; - - if (pbvh->header.type == PBVH_FACES) { - poly = pbvh->looptri[node->prim_indices[j]].poly; - } - else { - poly = BKE_subdiv_ccg_grid_to_face_index(pbvh->subdiv_ccg, node->prim_indices[j]); - } - - totface = max_ii(totface, poly + 1); - } - } - - int *facemap = (int *)MEM_malloc_arrayN(totface, sizeof(*facemap), __func__); - - for (int i = 0; i < totface; i++) { - facemap[i] = -1; - } - - for (int i = 0; i < pbvh->totnode; i++) { - PBVHNode *node = pbvh->nodes + i; - - if (!(node->flag & PBVH_Leaf)) { - continue; - } - - for (int j = 0; j < node->totprim; j++) { - int poly; - - if (pbvh->header.type == PBVH_FACES) { - poly = pbvh->looptri[node->prim_indices[j]].poly; - } - else { - poly = BKE_subdiv_ccg_grid_to_face_index(pbvh->subdiv_ccg, node->prim_indices[j]); - } - - if (facemap[poly] != -1 && facemap[poly] != i) { - printf("%s: error: face spanned multiple nodes (old: %d new: %d)\n", - __func__, - facemap[poly], - i); - } - - facemap[poly] = i; - } - } - MEM_SAFE_FREE(facemap); -} -#endif - -void BKE_pbvh_build_mesh(PBVH *pbvh, - Mesh *mesh, - const MPoly *mpoly, - const MLoop *mloop, - float (*vert_positions)[3], - MSculptVert *msculptverts, - int totvert, - struct CustomData *vdata, - struct CustomData *ldata, - struct CustomData *pdata, - const MLoopTri *looptri, - int looptri_num, - bool fast_draw, - float *face_areas, - SculptPMap *pmap) -{ - BBC *prim_bbc = NULL; - BB cb; - - if (pbvh->pmap != pmap) { - BKE_pbvh_pmap_aquire(pmap); - } - - pbvh->pmap = pmap; - pbvh->face_areas = face_areas; - pbvh->mesh = mesh; - pbvh->header.type = PBVH_FACES; - pbvh->mpoly = mpoly; - pbvh->hide_poly = (bool *)CustomData_get_layer_named_for_write( - &mesh->pdata, CD_PROP_BOOL, ".hide_poly", mesh->totpoly); - pbvh->material_indices = (const int *)CustomData_get_layer_named( - &mesh->pdata, CD_PROP_INT32, "material_index"); - pbvh->mloop = mloop; - pbvh->looptri = looptri; - pbvh->msculptverts = msculptverts; - pbvh->vert_positions = vert_positions; - BKE_mesh_vertex_normals_ensure(mesh); - pbvh->vert_normals = BKE_mesh_vertex_normals_for_write(mesh); - pbvh->hide_vert = (bool *)CustomData_get_layer_named_for_write( - &mesh->vdata, CD_PROP_BOOL, ".hide_vert", mesh->totvert); - pbvh->vert_bitmap = MEM_calloc_arrayN(totvert, sizeof(bool), "bvh->vert_bitmap"); - pbvh->totvert = totvert; - -#ifdef TEST_PBVH_FACE_SPLIT - /* Use lower limit to increase probability of - * edge cases. - */ - pbvh->leaf_limit = 100; -#else - pbvh->leaf_limit = LEAF_LIMIT; -#endif - - pbvh->vdata = vdata; - pbvh->ldata = ldata; - pbvh->pdata = pdata; - pbvh->faces_num = mesh->totpoly; - - pbvh->face_sets_color_seed = mesh->face_sets_color_seed; - pbvh->face_sets_color_default = mesh->face_sets_color_default; - - BB_reset(&cb); - - /* For each face, store the AABB and the AABB centroid */ - prim_bbc = MEM_mallocN(sizeof(BBC) * looptri_num, "prim_bbc"); - - for (int i = 0; i < mesh->totvert; i++) { - msculptverts[i].flag &= ~SCULPTFLAG_NEED_VALENCE; - msculptverts[i].valence = pmap->pmap[i].count; - } - - for (int i = 0; i < looptri_num; i++) { - const MLoopTri *lt = &looptri[i]; - const int sides = 3; - BBC *bbc = prim_bbc + i; - - BB_reset((BB *)bbc); - - for (int j = 0; j < sides; j++) { - BB_expand((BB *)bbc, vert_positions[pbvh->mloop[lt->tri[j]].v]); - } - - BBC_update_centroid(bbc); - - BB_expand(&cb, bbc->bcentroid); - } - - if (looptri_num) { - pbvh_build(pbvh, &cb, prim_bbc, looptri_num); - -#ifdef TEST_PBVH_FACE_SPLIT - test_face_boundaries(pbvh); -#endif - } - - MEM_freeN(prim_bbc); - - /* Clear the bitmap so it can be used as an update tag later on. */ - memset(pbvh->vert_bitmap, 0, sizeof(bool) * totvert); - - BKE_pbvh_update_active_vcol(pbvh, mesh); - -#ifdef VALIDATE_UNIQUE_NODE_FACES - pbvh_validate_node_prims(pbvh); -#endif -} - -void BKE_pbvh_build_grids(PBVH *pbvh, - CCGElem **grids, - int totgrid, - CCGKey *key, - void **gridfaces, - DMFlagMat *flagmats, - BLI_bitmap **grid_hidden, - bool fast_draw, - float *face_areas, - Mesh *me, - SubdivCCG *subdiv_ccg) -{ - const int gridsize = key->grid_size; - - pbvh->header.type = PBVH_GRIDS; - pbvh->face_areas = face_areas; - pbvh->grids = grids; - pbvh->gridfaces = gridfaces; - pbvh->grid_flag_mats = flagmats; - pbvh->totgrid = totgrid; - pbvh->gridkey = *key; - pbvh->grid_hidden = grid_hidden; - pbvh->subdiv_ccg = subdiv_ccg; - pbvh->faces_num = me->totpoly; - - /* Find maximum number of grids per face. */ - int max_grids = 1; - const MPoly *mpoly = BKE_mesh_polys(me); - - for (int i = 0; i < me->totpoly; i++) { - max_grids = max_ii(max_grids, mpoly[i].totloop); - } - - /* Ensure leaf limit is at least 4 so there's room - * to split at original face boundaries. - * Fixes T102209. - */ - pbvh->leaf_limit = max_ii(LEAF_LIMIT / (gridsize * gridsize), max_grids); - - /* We need the base mesh attribute layout for PBVH draw. */ - pbvh->vdata = &me->vdata; - pbvh->ldata = &me->ldata; - pbvh->pdata = &me->pdata; - - pbvh->mpoly = BKE_mesh_polys(me); - pbvh->mloop = BKE_mesh_loops(me); - - /* We also need the base mesh for PBVH draw. */ - pbvh->mesh = me; - - BB cb; - BB_reset(&cb); - - /* For each grid, store the AABB and the AABB centroid */ - BBC *prim_bbc = MEM_mallocN(sizeof(BBC) * totgrid, "prim_bbc"); - - for (int i = 0; i < totgrid; i++) { - CCGElem *grid = grids[i]; - BBC *bbc = prim_bbc + i; - - BB_reset((BB *)bbc); - - for (int j = 0; j < gridsize * gridsize; j++) { - BB_expand((BB *)bbc, CCG_elem_offset_co(key, grid, j)); - } - - BBC_update_centroid(bbc); - - BB_expand(&cb, bbc->bcentroid); - } - - if (totgrid) { - pbvh_build(pbvh, &cb, prim_bbc, totgrid); - -#ifdef TEST_PBVH_FACE_SPLIT - test_face_boundaries(pbvh); -#endif - } - - if (fast_draw) { - pbvh->flags |= PBVH_FAST_DRAW; - } - - MEM_freeN(prim_bbc); -#ifdef VALIDATE_UNIQUE_NODE_FACES - pbvh_validate_node_prims(pbvh); -#endif -} - -PBVH *BKE_pbvh_new(PBVHType type) -{ - PBVH *pbvh = MEM_callocN(sizeof(PBVH), "pbvh"); - pbvh->respect_hide = true; - pbvh->draw_cache_invalid = true; - pbvh->header.type = type; - - /* Initialize this to true, instead of waiting for a draw engine - * to set it. Prevents a crash in draw manager instancing code. - */ - pbvh->is_drawing = true; - return pbvh; -} - -void BKE_pbvh_free(PBVH *pbvh) -{ - for (int i = 0; i < pbvh->totnode; i++) { - PBVHNode *node = &pbvh->nodes[i]; - - if (node->flag & PBVH_Leaf) { - if (node->draw_batches) { - DRW_pbvh_node_free(node->draw_batches); - } - if (node->vert_indices) { - MEM_freeN((void *)node->vert_indices); - } - if (node->loop_indices) { - MEM_freeN(node->loop_indices); - } - if (node->face_vert_indices) { - MEM_freeN((void *)node->face_vert_indices); - } - if (node->bm_faces) { - BLI_table_gset_free(node->bm_faces, NULL); - } - if (node->bm_unique_verts) { - BLI_table_gset_free(node->bm_unique_verts, NULL); - } - if (node->bm_other_verts) { - BLI_table_gset_free(node->bm_other_verts, NULL); - } - - if (node->tribuf || node->tri_buffers) { - BKE_pbvh_bmesh_free_tris(pbvh, node); - } - -#ifdef PROXY_ADVANCED - BKE_pbvh_free_proxyarray(pbvh, node); -#endif - pbvh_node_pixels_free(node); - } - } - - if (pbvh->deformed) { - if (pbvh->vert_positions) { - /* if pbvh was deformed, new memory was allocated for verts/faces -- free it */ - - MEM_freeN((void *)pbvh->vert_positions); - } - - pbvh->vert_positions = NULL; - } - - if (pbvh->looptri) { - MEM_freeN((void *)pbvh->looptri); - } - - if (pbvh->nodes) { - MEM_freeN(pbvh->nodes); - } - - if (pbvh->prim_indices) { - MEM_freeN(pbvh->prim_indices); - } - - MEM_SAFE_FREE(pbvh->vert_bitmap); - - BKE_pbvh_pmap_release(pbvh->pmap); - pbvh->pmap = NULL; - - pbvh->invalid = true; - pbvh_pixels_free(pbvh); - - MEM_freeN(pbvh); -} - -static void pbvh_iter_begin(PBVHIter *iter, - PBVH *pbvh, - BKE_pbvh_SearchCallback scb, - void *search_data) -{ - iter->pbvh = pbvh; - iter->scb = scb; - iter->search_data = search_data; - - iter->stack = iter->stackfixed; - iter->stackspace = PBVH_STACK_FIXED_DEPTH; - - iter->stack[0].node = pbvh->nodes; - iter->stack[0].revisiting = false; - iter->stacksize = 1; -} - -static void pbvh_iter_end(PBVHIter *iter) -{ - if (iter->stackspace > PBVH_STACK_FIXED_DEPTH) { - MEM_freeN(iter->stack); - } -} - -static void pbvh_stack_push(PBVHIter *iter, PBVHNode *node, bool revisiting) -{ - if (UNLIKELY(iter->stacksize == iter->stackspace)) { - iter->stackspace *= 2; - if (iter->stackspace != (PBVH_STACK_FIXED_DEPTH * 2)) { - iter->stack = MEM_reallocN(iter->stack, sizeof(PBVHStack) * iter->stackspace); - } - else { - iter->stack = MEM_mallocN(sizeof(PBVHStack) * iter->stackspace, "PBVHStack"); - memcpy(iter->stack, iter->stackfixed, sizeof(PBVHStack) * iter->stacksize); - } - } - - iter->stack[iter->stacksize].node = node; - iter->stack[iter->stacksize].revisiting = revisiting; - iter->stacksize++; -} - -static PBVHNode *pbvh_iter_next(PBVHIter *iter, PBVHNodeFlags leaf_flag = PBVH_Leaf) -{ - /* purpose here is to traverse tree, visiting child nodes beforse their - * parents, this order is necessary for e.g. computing bounding boxes */ - - while (iter->stacksize) { - /* pop node */ - iter->stacksize--; - PBVHNode *node = iter->stack[iter->stacksize].node; - - /* on a mesh with no faces this can happen - * can remove this check if we know meshes have at least 1 face */ - if (node == NULL) { - return NULL; - } - - bool revisiting = iter->stack[iter->stacksize].revisiting; - - /* revisiting node already checked */ - if (revisiting) { - return node; - } - - if (iter->scb && !iter->scb(node, iter->search_data)) { - continue; /* don't traverse, outside of search zone */ - } - - if (node->flag & leaf_flag) { - /* immediately hit leaf node */ - return node; - } - - /* come back later when children are done */ - pbvh_stack_push(iter, node, true); - - /* push two child nodes on the stack */ - pbvh_stack_push(iter, iter->pbvh->nodes + node->children_offset + 1, false); - pbvh_stack_push(iter, iter->pbvh->nodes + node->children_offset, false); - } - - return NULL; -} - -static PBVHNode *pbvh_iter_next_occluded(PBVHIter *iter) -{ - while (iter->stacksize) { - /* pop node */ - iter->stacksize--; - PBVHNode *node = iter->stack[iter->stacksize].node; - - /* on a mesh with no faces this can happen - * can remove this check if we know meshes have at least 1 face */ - if (node == NULL) { - return NULL; - } - - float ff = dot_v3v3(node->vb.bmin, node->vb.bmax); - if (isnan(ff) || !isfinite(ff)) { - printf("%s: nan!\n", __func__); - } - - if (iter->scb && !iter->scb(node, iter->search_data)) { - continue; /* don't traverse, outside of search zone */ - } - - if (node->flag & PBVH_Leaf) { - /* immediately hit leaf node */ - return node; - } - - pbvh_stack_push(iter, iter->pbvh->nodes + node->children_offset + 1, false); - pbvh_stack_push(iter, iter->pbvh->nodes + node->children_offset, false); - } - - return NULL; -} - -void BKE_pbvh_search_gather_ex(PBVH *pbvh, - BKE_pbvh_SearchCallback scb, - void *search_data, - PBVHNode ***r_array, - int *r_tot, - PBVHNodeFlags leaf_flag) -{ - PBVHIter iter; - PBVHNode **array = NULL, *node; - int tot = 0, space = 0; - - pbvh_iter_begin(&iter, pbvh, scb, search_data); - - while ((node = pbvh_iter_next(&iter, leaf_flag))) { - if (node->flag & leaf_flag) { - if (UNLIKELY(tot == space)) { - /* resize array if needed */ - space = (tot == 0) ? 32 : space * 2; - array = MEM_recallocN_id(array, sizeof(PBVHNode *) * space, __func__); - } - - array[tot] = node; - tot++; - } - } - - pbvh_iter_end(&iter); - - if (tot == 0 && array) { - MEM_freeN(array); - array = NULL; - } - - *r_array = array; - *r_tot = tot; -} - -void BKE_pbvh_search_gather( - PBVH *pbvh, BKE_pbvh_SearchCallback scb, void *search_data, PBVHNode ***r_array, int *r_tot) -{ - BKE_pbvh_search_gather_ex(pbvh, scb, search_data, r_array, r_tot, PBVH_Leaf); -} - -void BKE_pbvh_search_callback(PBVH *pbvh, - BKE_pbvh_SearchCallback scb, - void *search_data, - BKE_pbvh_HitCallback hcb, - void *hit_data) -{ - PBVHIter iter; - PBVHNode *node; - - pbvh_iter_begin(&iter, pbvh, scb, search_data); - - while ((node = pbvh_iter_next(&iter))) { - if (node->flag & PBVH_Leaf) { - hcb(node, hit_data); - } - } - - pbvh_iter_end(&iter); -} - -typedef struct node_tree { - PBVHNode *data; - - struct node_tree *left; - struct node_tree *right; -} node_tree; - -static void node_tree_insert(node_tree *tree, node_tree *new_node) -{ - if (new_node->data->tmin < tree->data->tmin) { - if (tree->left) { - node_tree_insert(tree->left, new_node); - } - else { - tree->left = new_node; - } - } - else { - if (tree->right) { - node_tree_insert(tree->right, new_node); - } - else { - tree->right = new_node; - } - } -} - -static void traverse_tree(node_tree *tree, - BKE_pbvh_HitOccludedCallback hcb, - void *hit_data, - float *tmin) -{ - if (tree->left) { - traverse_tree(tree->left, hcb, hit_data, tmin); - } - - hcb(tree->data, hit_data, tmin); - - if (tree->right) { - traverse_tree(tree->right, hcb, hit_data, tmin); - } -} - -static void free_tree(node_tree *tree) -{ - if (tree->left) { - free_tree(tree->left); - tree->left = NULL; - } - - if (tree->right) { - free_tree(tree->right); - tree->right = NULL; - } - - free(tree); -} - -float BKE_pbvh_node_get_tmin(PBVHNode *node) -{ - return node->tmin; -} - -static void BKE_pbvh_search_callback_occluded(PBVH *pbvh, - BKE_pbvh_SearchCallback scb, - void *search_data, - BKE_pbvh_HitOccludedCallback hcb, - void *hit_data) -{ - PBVHIter iter; - PBVHNode *node; - node_tree *tree = NULL; - - pbvh_iter_begin(&iter, pbvh, scb, search_data); - - while ((node = pbvh_iter_next_occluded(&iter))) { - if (node->flag & PBVH_Leaf) { - node_tree *new_node = malloc(sizeof(node_tree)); - - new_node->data = node; - - new_node->left = NULL; - new_node->right = NULL; - - if (tree) { - node_tree_insert(tree, new_node); - } - else { - tree = new_node; - } - } - } - - pbvh_iter_end(&iter); - - if (tree) { - float tmin = FLT_MAX; - traverse_tree(tree, hcb, hit_data, &tmin); - free_tree(tree); - } -} - -static bool update_search_cb(PBVHNode *node, void *data_v) -{ - int flag = POINTER_AS_INT(data_v); - - if (node->flag & PBVH_Leaf) { - return (node->flag & flag) != 0; - } - - return true; -} - -typedef struct PBVHUpdateData { - PBVH *pbvh; - PBVHNode **nodes; - int totnode; - - float (*vert_normals)[3]; - int flag; - bool show_sculpt_face_sets; - bool flat_vcol_shading; - Mesh *mesh; - PBVHAttrReq *attrs; - int attrs_num; -} PBVHUpdateData; - -static void pbvh_update_normals_clear_task_cb(void *__restrict userdata, - const int n, - const TaskParallelTLS *__restrict UNUSED(tls)) -{ - PBVHUpdateData *data = userdata; - PBVH *pbvh = data->pbvh; - PBVHNode *node = data->nodes[n]; - float(*vert_normals)[3] = data->vert_normals; - - if (node->flag & PBVH_UpdateNormals) { - const int *verts = node->vert_indices; - const int totvert = node->uniq_verts; - for (int i = 0; i < totvert; i++) { - const int v = verts[i]; - if (pbvh->vert_bitmap[v]) { - zero_v3(vert_normals[v]); - } - } - } -} - -static void pbvh_update_normals_accum_task_cb(void *__restrict userdata, - const int n, - const TaskParallelTLS *__restrict UNUSED(tls)) -{ - PBVHUpdateData *data = userdata; - - PBVH *pbvh = data->pbvh; - PBVHNode *node = data->nodes[n]; - float(*vert_normals)[3] = data->vert_normals; - - if (node->flag & PBVH_UpdateNormals) { - uint mpoly_prev = UINT_MAX; - float fn[3]; - - const int *faces = node->prim_indices; - const int totface = node->totprim; - - for (int i = 0; i < totface; i++) { - const MLoopTri *lt = &pbvh->looptri[faces[i]]; - const uint vtri[3] = { - pbvh->mloop[lt->tri[0]].v, - pbvh->mloop[lt->tri[1]].v, - pbvh->mloop[lt->tri[2]].v, - }; - const int sides = 3; - - /* Face normal and mask */ - if (lt->poly != mpoly_prev) { - const MPoly *mp = &pbvh->mpoly[lt->poly]; - BKE_mesh_calc_poly_normal(mp, &pbvh->mloop[mp->loopstart], pbvh->vert_positions, fn); - mpoly_prev = lt->poly; - } - - for (int j = sides; j--;) { - const int v = vtri[j]; - - if (pbvh->vert_bitmap[v]) { - /* NOTE: This avoids `lock, add_v3_v3, unlock` - * and is five to ten times quicker than a spin-lock. - * Not exact equivalent though, since atomicity is only ensured for one component - * of the vector at a time, but here it shall not make any sensible difference. */ - for (int k = 3; k--;) { - atomic_add_and_fetch_fl(&vert_normals[v][k], fn[k]); - } - } - } - } - } -} - -static void pbvh_update_normals_store_task_cb(void *__restrict userdata, - const int n, - const TaskParallelTLS *__restrict UNUSED(tls)) -{ - PBVHUpdateData *data = userdata; - PBVH *pbvh = data->pbvh; - PBVHNode *node = data->nodes[n]; - float(*vert_normals)[3] = data->vert_normals; - - if (node->flag & PBVH_UpdateNormals) { - const int *verts = node->vert_indices; - const int totvert = node->uniq_verts; - - for (int i = 0; i < totvert; i++) { - const int v = verts[i]; - - /* No atomics necessary because we are iterating over uniq_verts only, - * so we know only this thread will handle this vertex. */ - if (pbvh->vert_bitmap[v]) { - normalize_v3(vert_normals[v]); - pbvh->vert_bitmap[v] = false; - } - } - - node->flag &= ~PBVH_UpdateNormals; - } -} - -static void pbvh_faces_update_normals(PBVH *pbvh, PBVHNode **nodes, int totnode) -{ - /* subtle assumptions: - * - We know that for all edited vertices, the nodes with faces - * adjacent to these vertices have been marked with PBVH_UpdateNormals. - * This is true because if the vertex is inside the brush radius, the - * bounding box of its adjacent faces will be as well. - * - However this is only true for the vertices that have actually been - * edited, not for all vertices in the nodes marked for update, so we - * can only update vertices marked in the `vert_bitmap`. - */ - - PBVHUpdateData data = { - .pbvh = pbvh, - .nodes = nodes, - .vert_normals = pbvh->vert_normals, - }; - - TaskParallelSettings settings; - BKE_pbvh_parallel_range_settings(&settings, true, totnode); - - /* Zero normals before accumulation. */ - BLI_task_parallel_range(0, totnode, &data, pbvh_update_normals_clear_task_cb, &settings); - BLI_task_parallel_range(0, totnode, &data, pbvh_update_normals_accum_task_cb, &settings); - BLI_task_parallel_range(0, totnode, &data, pbvh_update_normals_store_task_cb, &settings); -} - -static void pbvh_update_mask_redraw_task_cb(void *__restrict userdata, - const int n, - const TaskParallelTLS *__restrict UNUSED(tls)) -{ - - PBVHUpdateData *data = userdata; - PBVH *pbvh = data->pbvh; - PBVHNode *node = data->nodes[n]; - if (node->flag & PBVH_UpdateMask) { - - bool has_unmasked = false; - bool has_masked = true; - if (node->flag & PBVH_Leaf) { - PBVHVertexIter vd; - - BKE_pbvh_vertex_iter_begin (pbvh, node, vd, PBVH_ITER_ALL) { - if (vd.mask && *vd.mask < 1.0f) { - has_unmasked = true; - } - if (vd.mask && *vd.mask > 0.0f) { - has_masked = false; - } - } - BKE_pbvh_vertex_iter_end; - } - else { - has_unmasked = true; - has_masked = true; - } - BKE_pbvh_node_fully_masked_set(node, !has_unmasked); - BKE_pbvh_node_fully_unmasked_set(node, has_masked); - - node->flag &= ~PBVH_UpdateMask; - } -} - -static void pbvh_update_mask_redraw(PBVH *pbvh, PBVHNode **nodes, int totnode, int flag) -{ - PBVHUpdateData data = { - .pbvh = pbvh, - .nodes = nodes, - .flag = flag, - }; - - TaskParallelSettings settings; - BKE_pbvh_parallel_range_settings(&settings, true, totnode); - BLI_task_parallel_range(0, totnode, &data, pbvh_update_mask_redraw_task_cb, &settings); -} - -static void pbvh_update_visibility_redraw_task_cb(void *__restrict userdata, - const int n, - const TaskParallelTLS *__restrict UNUSED(tls)) -{ - - PBVHUpdateData *data = userdata; - PBVH *pbvh = data->pbvh; - PBVHNode *node = data->nodes[n]; - if (node->flag & PBVH_UpdateVisibility) { - node->flag &= ~PBVH_UpdateVisibility; - BKE_pbvh_node_fully_hidden_set(node, true); - if (node->flag & PBVH_Leaf) { - PBVHVertexIter vd; - BKE_pbvh_vertex_iter_begin (pbvh, node, vd, PBVH_ITER_ALL) { - if (vd.visible) { - BKE_pbvh_node_fully_hidden_set(node, false); - return; - } - } - BKE_pbvh_vertex_iter_end; - } - } -} - -static void pbvh_update_visibility_redraw(PBVH *pbvh, PBVHNode **nodes, int totnode, int flag) -{ - PBVHUpdateData data = { - .pbvh = pbvh, - .nodes = nodes, - .flag = flag, - }; - - TaskParallelSettings settings; - BKE_pbvh_parallel_range_settings(&settings, true, totnode); - BLI_task_parallel_range(0, totnode, &data, pbvh_update_visibility_redraw_task_cb, &settings); -} - -static void pbvh_update_BB_redraw_task_cb(void *__restrict userdata, - const int n, - const TaskParallelTLS *__restrict UNUSED(tls)) -{ - PBVHUpdateData *data = userdata; - PBVH *pbvh = data->pbvh; - PBVHNode *node = data->nodes[n]; - const int flag = data->flag; - - update_node_vb(pbvh, node, flag); - - if ((flag & PBVH_UpdateRedraw) && (node->flag & PBVH_UpdateRedraw)) { - node->flag &= ~PBVH_UpdateRedraw; - } -} - -void pbvh_update_BB_redraw(PBVH *pbvh, PBVHNode **nodes, int totnode, int flag) -{ - /* update BB, redraw flag */ - PBVHUpdateData data = { - .pbvh = pbvh, - .nodes = nodes, - .flag = flag, - }; - - TaskParallelSettings settings; - BKE_pbvh_parallel_range_settings(&settings, true, totnode); - BLI_task_parallel_range(0, totnode, &data, pbvh_update_BB_redraw_task_cb, &settings); -} - -bool BKE_pbvh_get_color_layer(const Mesh *me, CustomDataLayer **r_layer, eAttrDomain *r_attr) -{ - CustomDataLayer *layer = BKE_id_attributes_color_find(&me->id, me->active_color_attribute); - - if (!layer || !ELEM(layer->type, CD_PROP_COLOR, CD_PROP_BYTE_COLOR)) { - *r_layer = NULL; - *r_attr = ATTR_DOMAIN_POINT; - return false; - } - - eAttrDomain domain = BKE_id_attribute_domain(&me->id, layer); - - if (!ELEM(domain, ATTR_DOMAIN_POINT, ATTR_DOMAIN_CORNER)) { - *r_layer = NULL; - *r_attr = ATTR_DOMAIN_POINT; - return false; - } - - *r_layer = layer; - *r_attr = domain; - - return true; -} - -static void pbvh_update_draw_buffer_cb(void *__restrict userdata, - const int n, - const TaskParallelTLS *__restrict UNUSED(tls)) -{ - /* Create and update draw buffers. The functions called here must not - * do any OpenGL calls. Flags are not cleared immediately, that happens - * after GPU_pbvh_buffer_flush() which does the final OpenGL calls. */ - PBVHUpdateData *data = userdata; - PBVH *pbvh = data->pbvh; - PBVHNode *node = data->nodes[n]; - Mesh *me = data->mesh; - - CustomDataLayer *vcol_layer = NULL; - eAttrDomain vcol_domain; - - BKE_pbvh_get_color_layer(pbvh, me, &vcol_layer, &vcol_domain); - - CustomData *vdata, *ldata; - - if (!pbvh->header.bm) { - vdata = pbvh->vdata ? pbvh->vdata : &me->vdata; - ldata = pbvh->ldata ? pbvh->ldata : &me->ldata; - } - else { - vdata = &pbvh->header.bm->vdata; - ldata = &pbvh->header.bm->ldata; - } - - Mesh me_query; - BKE_id_attribute_copy_domains_temp(ID_ME, vdata, NULL, ldata, NULL, NULL, &me_query.id); - me_query.active_color_attribute = me->active_color_attribute; - - if (!pbvh->header.bm) { - vdata = pbvh->vdata; - ldata = pbvh->ldata; - } - else { - vdata = &pbvh->header.bm->vdata; - ldata = &pbvh->header.bm->ldata; - } - - if (node->flag & PBVH_RebuildDrawBuffers) { - PBVH_GPU_Args args; - pbvh_draw_args_init(pbvh, &args, node); - - node->draw_batches = DRW_pbvh_node_create(&args); - } - - if (node->flag & PBVH_UpdateDrawBuffers) { - node->updategen++; - node->debug_draw_gen++; - - if (node->draw_batches) { - PBVH_GPU_Args args; - - pbvh_draw_args_init(pbvh, &args, node); - DRW_pbvh_node_update(node->draw_batches, &args); - } - } -} - -void BKE_pbvh_set_flat_vcol_shading(PBVH *pbvh, bool value) -{ - if (value != pbvh->flat_vcol_shading) { - for (int i = 0; i < pbvh->totnode; i++) { - PBVHNode *node = pbvh->nodes + i; - - if (!(node->flag & PBVH_Leaf)) { - continue; - } - - BKE_pbvh_node_mark_rebuild_draw(node); - } - } - - pbvh->flat_vcol_shading = value; -} - -void pbvh_free_draw_buffers(PBVH *UNUSED(pbvh), PBVHNode *node) -{ - if (node->draw_batches) { - DRW_pbvh_node_free(node->draw_batches); - node->draw_batches = NULL; - } -} - -static void pbvh_update_draw_buffers( - PBVH *pbvh, Mesh *me, PBVHNode **nodes, int totnode, int update_flag) -{ - const CustomData *vdata; - - switch (pbvh->header.type) { - case PBVH_BMESH: - if (!pbvh->header.bm) { - /* BMesh hasn't been created yet */ - return; - } - - vdata = &pbvh->header.bm->vdata; - break; - case PBVH_FACES: - vdata = pbvh->vdata; - break; - case PBVH_GRIDS: - vdata = NULL; - break; - } - UNUSED_VARS(vdata); - - if ((update_flag & PBVH_RebuildDrawBuffers) || ELEM(pbvh->header.type, PBVH_GRIDS, PBVH_BMESH)) { - /* Free buffers uses OpenGL, so not in parallel. */ - for (int n = 0; n < totnode; n++) { - PBVHNode *node = nodes[n]; - if (node->flag & PBVH_RebuildDrawBuffers) { - pbvh_free_draw_buffers(pbvh, node); - } - else if ((node->flag & PBVH_UpdateDrawBuffers) && node->draw_batches) { - PBVH_GPU_Args args; - - pbvh_draw_args_init(pbvh, &args, node); - DRW_pbvh_update_pre(node->draw_batches, &args); - } - } - } - - /* Parallel creation and update of draw buffers. */ - PBVHUpdateData data = { - .pbvh = pbvh, .nodes = nodes, .flat_vcol_shading = pbvh->flat_vcol_shading, .mesh = me}; - - TaskParallelSettings settings; - BKE_pbvh_parallel_range_settings(&settings, true, totnode); - BLI_task_parallel_range(0, totnode, &data, pbvh_update_draw_buffer_cb, &settings); - - for (int i = 0; i < totnode; i++) { - PBVHNode *node = nodes[i]; - - if (node->flag & PBVH_UpdateDrawBuffers) { - /* Flush buffers uses OpenGL, so not in parallel. */ - if (node->draw_batches) { - DRW_pbvh_node_gpu_flush(node->draw_batches); - } - } - - node->flag &= ~(PBVH_RebuildDrawBuffers | PBVH_UpdateDrawBuffers); - } -} - -static int pbvh_flush_bb(PBVH *pbvh, PBVHNode *node, int flag) -{ - int update = 0; - - /* Difficult to multi-thread well, we just do single threaded recursive. */ - if (node->flag & PBVH_Leaf) { - if (flag & PBVH_UpdateBB) { - update |= (node->flag & PBVH_UpdateBB); - node->flag &= ~PBVH_UpdateBB; - } - - if (flag & PBVH_UpdateOriginalBB) { - update |= (node->flag & PBVH_UpdateOriginalBB); - node->flag &= ~PBVH_UpdateOriginalBB; - } - - return update; - } - - update |= pbvh_flush_bb(pbvh, pbvh->nodes + node->children_offset, flag); - update |= pbvh_flush_bb(pbvh, pbvh->nodes + node->children_offset + 1, flag); - - update_node_vb(pbvh, node, update); - - return update; -} - -void BKE_pbvh_update_bounds(PBVH *pbvh, int flag) -{ - if (!pbvh->nodes) { - return; - } - - PBVHNode **nodes; - int totnode; - - BKE_pbvh_search_gather(pbvh, update_search_cb, POINTER_FROM_INT(flag), &nodes, &totnode); - - if (flag & (PBVH_UpdateBB | PBVH_UpdateOriginalBB | PBVH_UpdateRedraw)) { - pbvh_update_BB_redraw(pbvh, nodes, totnode, flag); - } - - if (flag & (PBVH_UpdateBB | PBVH_UpdateOriginalBB)) { - pbvh_flush_bb(pbvh, pbvh->nodes, flag); - } - - MEM_SAFE_FREE(nodes); -} - -void BKE_pbvh_update_vertex_data(PBVH *pbvh, int flag) -{ - if (!pbvh->nodes) { - return; - } - - PBVHNode **nodes; - int totnode; - - BKE_pbvh_search_gather(pbvh, update_search_cb, POINTER_FROM_INT(flag), &nodes, &totnode); - - if (flag & (PBVH_UpdateMask)) { - pbvh_update_mask_redraw(pbvh, nodes, totnode, flag); - } - - if (flag & (PBVH_UpdateColor)) { - for (int i = 0; i < totnode; i++) { - nodes[i]->flag |= PBVH_UpdateRedraw | PBVH_UpdateDrawBuffers | PBVH_UpdateColor; - } - } - - if (flag & (PBVH_UpdateVisibility)) { - pbvh_update_visibility_redraw(pbvh, nodes, totnode, flag); - } - - if (nodes) { - MEM_freeN(nodes); - } -} - -static void pbvh_faces_node_visibility_update(PBVH *pbvh, PBVHNode *node) -{ - int totvert, i; - BKE_pbvh_node_num_verts(pbvh, node, NULL, &totvert); - const int *vert_indices = BKE_pbvh_node_get_vert_indices(node); - - if (pbvh->hide_vert == NULL) { - BKE_pbvh_node_fully_hidden_set(node, false); - return; - } - for (i = 0; i < totvert; i++) { - if (!(pbvh->hide_vert[vert_indices[i]])) { - BKE_pbvh_node_fully_hidden_set(node, false); - return; - } - } - - BKE_pbvh_node_fully_hidden_set(node, true); -} - -static void pbvh_grids_node_visibility_update(PBVH *pbvh, PBVHNode *node) -{ - CCGElem **grids; - BLI_bitmap **grid_hidden; - int *grid_indices, totgrid, i; - - BKE_pbvh_node_get_grids(pbvh, node, &grid_indices, &totgrid, NULL, NULL, &grids); - grid_hidden = BKE_pbvh_grid_hidden(pbvh); - CCGKey key = *BKE_pbvh_get_grid_key(pbvh); - - for (i = 0; i < totgrid; i++) { - int g = grid_indices[i], x, y; - BLI_bitmap *gh = grid_hidden[g]; - - if (!gh) { - BKE_pbvh_node_fully_hidden_set(node, false); - return; - } - - for (y = 0; y < key.grid_size; y++) { - for (x = 0; x < key.grid_size; x++) { - if (!BLI_BITMAP_TEST(gh, y * key.grid_size + x)) { - BKE_pbvh_node_fully_hidden_set(node, false); - return; - } - } - } - } - BKE_pbvh_node_fully_hidden_set(node, true); -} - -static void pbvh_bmesh_node_visibility_update(PBVHNode *node) -{ - TableGSet *unique, *other; - - unique = BKE_pbvh_bmesh_node_unique_verts(node); - other = BKE_pbvh_bmesh_node_other_verts(node); - - BMVert *v; - - TGSET_ITER(v, unique) - { - if (!BM_elem_flag_test(v, BM_ELEM_HIDDEN)) { - BKE_pbvh_node_fully_hidden_set(node, false); - return; - } - } - TGSET_ITER_END - - TGSET_ITER(v, other) - { - if (!BM_elem_flag_test(v, BM_ELEM_HIDDEN)) { - BKE_pbvh_node_fully_hidden_set(node, false); - return; - } - } - TGSET_ITER_END - - BKE_pbvh_node_fully_hidden_set(node, true); -} - -static void pbvh_update_visibility_task_cb(void *__restrict userdata, - const int n, - const TaskParallelTLS *__restrict UNUSED(tls)) -{ - - PBVHUpdateData *data = userdata; - PBVH *pbvh = data->pbvh; - PBVHNode *node = data->nodes[n]; - if (node->flag & PBVH_UpdateVisibility) { - switch (BKE_pbvh_type(pbvh)) { - case PBVH_FACES: - pbvh_faces_node_visibility_update(pbvh, node); - break; - case PBVH_GRIDS: - pbvh_grids_node_visibility_update(pbvh, node); - break; - case PBVH_BMESH: - pbvh_bmesh_node_visibility_update(node); - break; - } - node->flag &= ~PBVH_UpdateVisibility; - } -} - -static void pbvh_update_visibility(PBVH *pbvh, PBVHNode **nodes, int totnode) -{ - PBVHUpdateData data = { - .pbvh = pbvh, - .nodes = nodes, - }; - - TaskParallelSettings settings; - BKE_pbvh_parallel_range_settings(&settings, true, totnode); - BLI_task_parallel_range(0, totnode, &data, pbvh_update_visibility_task_cb, &settings); -} - -void BKE_pbvh_update_visibility(PBVH *pbvh) -{ - if (!pbvh->nodes) { - return; - } - - PBVHNode **nodes; - int totnode; - - BKE_pbvh_search_gather( - pbvh, update_search_cb, POINTER_FROM_INT(PBVH_UpdateVisibility), &nodes, &totnode); - pbvh_update_visibility(pbvh, nodes, totnode); - - if (nodes) { - MEM_freeN(nodes); - } -} - -void BKE_pbvh_redraw_BB(PBVH *pbvh, float bb_min[3], float bb_max[3]) -{ - PBVHIter iter; - PBVHNode *node; - BB bb; - - BB_reset(&bb); - - pbvh_iter_begin(&iter, pbvh, NULL, NULL); - - while ((node = pbvh_iter_next(&iter))) { - if (node->flag & PBVH_UpdateRedraw) { - BB_expand_with_bb(&bb, &node->vb); - } - } - - pbvh_iter_end(&iter); - - copy_v3_v3(bb_min, bb.bmin); - copy_v3_v3(bb_max, bb.bmax); -} - -void BKE_pbvh_get_grid_updates(PBVH *pbvh, bool clear, void ***r_gridfaces, int *r_totface) -{ - GSet *face_set = BLI_gset_ptr_new(__func__); - PBVHNode *node; - PBVHIter iter; - - pbvh_iter_begin(&iter, pbvh, NULL, NULL); - - while ((node = pbvh_iter_next(&iter))) { - if (node->flag & PBVH_UpdateNormals) { - for (uint i = 0; i < node->totprim; i++) { - void *face = pbvh->gridfaces[node->prim_indices[i]]; - BLI_gset_add(face_set, face); - } - - if (clear) { - node->flag &= ~PBVH_UpdateNormals; - } - } - } - - pbvh_iter_end(&iter); - - const int tot = BLI_gset_len(face_set); - if (tot == 0) { - *r_totface = 0; - *r_gridfaces = NULL; - BLI_gset_free(face_set, NULL); - return; - } - - void **faces = MEM_mallocN(sizeof(*faces) * tot, "PBVH Grid Faces"); - - GSetIterator gs_iter; - int i; - GSET_ITER_INDEX (gs_iter, face_set, i) { - faces[i] = BLI_gsetIterator_getKey(&gs_iter); - } - - BLI_gset_free(face_set, NULL); - - *r_totface = tot; - *r_gridfaces = faces; -} - -/***************************** PBVH Access ***********************************/ - -bool BKE_pbvh_has_faces(const PBVH *pbvh) -{ - if (pbvh->header.type == PBVH_BMESH) { - return (pbvh->header.bm->totface != 0); - } - - return (pbvh->totprim != 0); -} - -void BKE_pbvh_bounding_box(const PBVH *pbvh, float min[3], float max[3]) -{ - if (pbvh->totnode) { - const BB *bb = &pbvh->nodes[0].vb; - copy_v3_v3(min, bb->bmin); - copy_v3_v3(max, bb->bmax); - } - else { - zero_v3(min); - zero_v3(max); - } -} - -BLI_bitmap **BKE_pbvh_grid_hidden(const PBVH *pbvh) -{ - BLI_assert(pbvh->header.type == PBVH_GRIDS); - return pbvh->grid_hidden; -} - -const CCGKey *BKE_pbvh_get_grid_key(const PBVH *pbvh) -{ - BLI_assert(pbvh->header.type == PBVH_GRIDS); - return &pbvh->gridkey; -} - -struct CCGElem **BKE_pbvh_get_grids(const PBVH *pbvh) -{ - BLI_assert(pbvh->header.type == PBVH_GRIDS); - return pbvh->grids; -} - -BLI_bitmap **BKE_pbvh_get_grid_visibility(const PBVH *pbvh) -{ - BLI_assert(pbvh->header.type == PBVH_GRIDS); - return pbvh->grid_hidden; -} - -int BKE_pbvh_get_grid_num_verts(const PBVH *pbvh) -{ - BLI_assert(pbvh->header.type == PBVH_GRIDS); - return pbvh->totgrid * pbvh->gridkey.grid_area; -} - -int BKE_pbvh_get_grid_num_faces(const PBVH *pbvh) -{ - BLI_assert(pbvh->header.type == PBVH_GRIDS); - return pbvh->totgrid * (pbvh->gridkey.grid_size - 1) * (pbvh->gridkey.grid_size - 1); -} - -/***************************** Node Access ***********************************/ - -void BKE_pbvh_node_mark_original_update(PBVHNode *node) -{ - node->flag |= PBVH_UpdateOriginalBB; -} - -void BKE_pbvh_node_mark_update(PBVHNode *node) -{ - node->flag |= PBVH_UpdateNormals | PBVH_UpdateBB | PBVH_UpdateOriginalBB | - PBVH_UpdateDrawBuffers | PBVH_UpdateRedraw | PBVH_UpdateCurvatureDir | - PBVH_RebuildPixels | PBVH_UpdateTriAreas; -} - -void BKE_pbvh_node_mark_update_mask(PBVHNode *node) -{ - node->flag |= PBVH_UpdateMask | PBVH_UpdateDrawBuffers | PBVH_UpdateRedraw; -} - -void BKE_pbvh_node_mark_update_color(PBVHNode *node) -{ - node->flag |= PBVH_UpdateColor | PBVH_UpdateDrawBuffers | PBVH_UpdateRedraw; -} - -void BKE_pbvh_node_mark_update_face_sets(PBVHNode *node) -{ - node->flag |= PBVH_UpdateDrawBuffers | PBVH_UpdateRedraw; -} - -void BKE_pbvh_mark_rebuild_pixels(PBVH *pbvh) -{ - for (int n = 0; n < pbvh->totnode; n++) { - PBVHNode *node = &pbvh->nodes[n]; - if (node->flag & PBVH_Leaf) { - node->flag |= PBVH_RebuildPixels; - } - } -} - -void BKE_pbvh_node_mark_update_visibility(PBVHNode *node) -{ - node->flag |= PBVH_UpdateVisibility | PBVH_RebuildDrawBuffers | PBVH_UpdateDrawBuffers | - PBVH_UpdateRedraw | PBVH_UpdateTris; -} - -void BKE_pbvh_vert_tag_update_normal_visibility(PBVHNode *node) -{ - node->flag |= PBVH_UpdateVisibility | PBVH_RebuildDrawBuffers | PBVH_UpdateDrawBuffers | - PBVH_UpdateRedraw | PBVH_UpdateCurvatureDir | PBVH_UpdateTris; -} - -void BKE_pbvh_node_mark_rebuild_draw(PBVHNode *node) -{ - node->flag |= PBVH_RebuildDrawBuffers | PBVH_UpdateDrawBuffers | PBVH_UpdateRedraw | - PBVH_UpdateCurvatureDir; -} - -void BKE_pbvh_node_mark_redraw(PBVHNode *node) -{ - node->flag |= PBVH_UpdateDrawBuffers | PBVH_UpdateRedraw; -} - -void BKE_pbvh_node_mark_normals_update(PBVHNode *node) -{ - node->flag |= PBVH_UpdateNormals | PBVH_UpdateCurvatureDir; -} - -void BKE_pbvh_node_mark_curvature_update(PBVHNode *node) -{ - node->flag |= PBVH_UpdateCurvatureDir; -} - -void BKE_pbvh_curvature_update_set(PBVHNode *node, bool state) -{ - if (state) { - node->flag |= PBVH_UpdateCurvatureDir; - } - else { - node->flag &= ~PBVH_UpdateCurvatureDir; - } -} - -bool BKE_pbvh_curvature_update_get(PBVHNode *node) -{ - return node->flag & PBVH_UpdateCurvatureDir; -} - -void BKE_pbvh_node_fully_hidden_set(PBVHNode *node, int fully_hidden) -{ - BLI_assert(node->flag & PBVH_Leaf); - - if (fully_hidden) { - node->flag |= PBVH_FullyHidden; - } - else { - node->flag &= ~PBVH_FullyHidden; - } -} - -bool BKE_pbvh_node_fully_hidden_get(PBVHNode *node) -{ - return (node->flag & PBVH_Leaf) && (node->flag & PBVH_FullyHidden); -} - -void BKE_pbvh_node_fully_masked_set(PBVHNode *node, int fully_masked) -{ - BLI_assert(node->flag & PBVH_Leaf); - - if (fully_masked) { - node->flag |= PBVH_FullyMasked; - } - else { - node->flag &= ~PBVH_FullyMasked; - } -} - -bool BKE_pbvh_node_fully_masked_get(PBVHNode *node) -{ - return (node->flag & PBVH_Leaf) && (node->flag & PBVH_FullyMasked); -} - -void BKE_pbvh_node_fully_unmasked_set(PBVHNode *node, int fully_masked) -{ - BLI_assert(node->flag & PBVH_Leaf); - - if (fully_masked) { - node->flag |= PBVH_FullyUnmasked; - } - else { - node->flag &= ~PBVH_FullyUnmasked; - } -} - -bool BKE_pbvh_node_fully_unmasked_get(PBVHNode *node) -{ - return (node->flag & PBVH_Leaf) && (node->flag & PBVH_FullyUnmasked); -} - -void BKE_pbvh_vert_tag_update_normal(PBVH *pbvh, PBVHVertRef vertex) -{ - BLI_assert(pbvh->header.type == PBVH_FACES); - pbvh->vert_bitmap[vertex.i] = true; -} - -void BKE_pbvh_node_get_loops(PBVH *pbvh, - PBVHNode *node, - const int **r_loop_indices, - const MLoop **r_loops) -{ - BLI_assert(BKE_pbvh_type(pbvh) == PBVH_FACES); - - if (r_loop_indices) { - *r_loop_indices = node->loop_indices; - } - - if (r_loops) { - *r_loops = pbvh->mloop; - } -} - -int BKE_pbvh_num_faces(const PBVH *pbvh) -{ - switch (pbvh->header.type) { - case PBVH_GRIDS: - case PBVH_FACES: - return pbvh->faces_num; - case PBVH_BMESH: - return pbvh->header.bm->totface; - } - - BLI_assert_unreachable(); - return 0; -} - -const int *BKE_pbvh_node_get_vert_indices(PBVHNode *node) - -{ - return node->vert_indices; -} - -void BKE_pbvh_node_num_verts(PBVH *pbvh, PBVHNode *node, int *r_uniquevert, int *r_totvert) -{ - int tot; - - switch (pbvh->header.type) { - case PBVH_GRIDS: - tot = node->totprim * pbvh->gridkey.grid_area; - if (r_totvert) { - *r_totvert = tot; - } - if (r_uniquevert) { - *r_uniquevert = tot; - } - break; - case PBVH_FACES: - if (r_totvert) { - *r_totvert = node->uniq_verts + node->face_verts; - } - if (r_uniquevert) { - *r_uniquevert = node->uniq_verts; - } - break; - case PBVH_BMESH: - // not a leaf? return zero - if (!(node->flag & PBVH_Leaf)) { - if (r_totvert) { - *r_totvert = 0; - } - - if (r_uniquevert) { - *r_uniquevert = 0; - } - - return; - } - - pbvh_bmesh_check_other_verts(node); - - tot = BLI_table_gset_len(node->bm_unique_verts); - if (r_totvert) { - *r_totvert = tot + BLI_table_gset_len(node->bm_other_verts); - } - if (r_uniquevert) { - *r_uniquevert = tot; - } - break; - } -} - -void BKE_pbvh_node_get_grids(PBVH *pbvh, - PBVHNode *node, - int **r_grid_indices, - int *r_totgrid, - int *r_maxgrid, - int *r_gridsize, - CCGElem ***r_griddata) -{ - switch (pbvh->header.type) { - case PBVH_GRIDS: - if (r_grid_indices) { - *r_grid_indices = node->prim_indices; - } - if (r_totgrid) { - *r_totgrid = node->totprim; - } - if (r_maxgrid) { - *r_maxgrid = pbvh->totgrid; - } - if (r_gridsize) { - *r_gridsize = pbvh->gridkey.grid_size; - } - if (r_griddata) { - *r_griddata = pbvh->grids; - } - break; - case PBVH_FACES: - case PBVH_BMESH: - if (r_grid_indices) { - *r_grid_indices = NULL; - } - if (r_totgrid) { - *r_totgrid = 0; - } - if (r_maxgrid) { - *r_maxgrid = 0; - } - if (r_gridsize) { - *r_gridsize = 0; - } - if (r_griddata) { - *r_griddata = NULL; - } - break; - } -} - -void BKE_pbvh_node_get_BB(PBVHNode *node, float bb_min[3], float bb_max[3]) -{ - copy_v3_v3(bb_min, node->vb.bmin); - copy_v3_v3(bb_max, node->vb.bmax); -} - -void BKE_pbvh_node_get_original_BB(PBVHNode *node, float bb_min[3], float bb_max[3]) -{ - copy_v3_v3(bb_min, node->orig_vb.bmin); - copy_v3_v3(bb_max, node->orig_vb.bmax); -} - -void BKE_pbvh_node_get_proxies(PBVHNode *node, PBVHProxyNode **proxies, int *proxy_count) -{ - if (node->proxy_count > 0) { - if (proxies) { - *proxies = node->proxies; - } - if (proxy_count) { - *proxy_count = node->proxy_count; - } - } - else { - if (proxies) { - *proxies = NULL; - } - if (proxy_count) { - *proxy_count = 0; - } - } -} - -bool BKE_pbvh_node_has_vert_with_normal_update_tag(PBVH *pbvh, PBVHNode *node) -{ - BLI_assert(pbvh->header.type == PBVH_FACES); - const int *verts = node->vert_indices; - const int totvert = node->uniq_verts + node->face_verts; - - for (int i = 0; i < totvert; i++) { - const int v = verts[i]; - - if (pbvh->vert_bitmap[v]) { - return true; - } - } - - return false; -} - -/********************************* Ray-cast ***********************************/ - -typedef struct { - struct IsectRayAABB_Precalc ray; - bool original; - int stroke_id; -} RaycastData; - -static bool ray_aabb_intersect(PBVHNode *node, void *data_v) -{ - RaycastData *rcd = data_v; - const float *bb_min, *bb_max; - - if (rcd->original) { - /* BKE_pbvh_node_get_original_BB */ - bb_min = node->orig_vb.bmin; - bb_max = node->orig_vb.bmax; - } - else { - /* BKE_pbvh_node_get_BB */ - bb_min = node->vb.bmin; - bb_max = node->vb.bmax; - } - - return isect_ray_aabb_v3(&rcd->ray, bb_min, bb_max, &node->tmin); -} - -void BKE_pbvh_raycast(PBVH *pbvh, - BKE_pbvh_HitOccludedCallback cb, - void *data, - const float ray_start[3], - const float ray_normal[3], - bool original, - int stroke_id) -{ - RaycastData rcd; - - isect_ray_aabb_v3_precalc(&rcd.ray, ray_start, ray_normal); - - rcd.original = original; - rcd.stroke_id = stroke_id; - pbvh->stroke_id = stroke_id; - - BKE_pbvh_search_callback_occluded(pbvh, ray_aabb_intersect, &rcd, cb, data); -} - -bool ray_face_intersection_quad(const float ray_start[3], - struct IsectRayPrecalc *isect_precalc, - const float t0[3], - const float t1[3], - const float t2[3], - const float t3[3], - float *depth) -{ - float depth_test; - - if ((isect_ray_tri_watertight_v3(ray_start, isect_precalc, t0, t1, t2, &depth_test, NULL) && - (depth_test < *depth)) || - (isect_ray_tri_watertight_v3(ray_start, isect_precalc, t0, t2, t3, &depth_test, NULL) && - (depth_test < *depth))) - { - *depth = depth_test; - return true; - } - - return false; -} - -bool ray_face_intersection_tri(const float ray_start[3], - struct IsectRayPrecalc *isect_precalc, - const float t0[3], - const float t1[3], - const float t2[3], - float *depth) -{ - float depth_test; - if (isect_ray_tri_watertight_v3(ray_start, isect_precalc, t0, t1, t2, &depth_test, NULL) && - (depth_test < *depth)) - { - *depth = depth_test; - return true; - } - - return false; -} - -bool ray_update_depth_and_hit_count(const float depth_test, - float *r_depth, - float *r_back_depth, - int *hit_count) -{ - (*hit_count)++; - if (depth_test < *r_depth) { - *r_back_depth = *r_depth; - *r_depth = depth_test; - return true; - } - else if (depth_test > *r_depth && depth_test <= *r_back_depth) { - *r_back_depth = depth_test; - return false; - } - - return false; -} - -bool ray_face_intersection_depth_quad(const float ray_start[3], - struct IsectRayPrecalc *isect_precalc, - const float t0[3], - const float t1[3], - const float t2[3], - const float t3[3], - float *r_depth, - float *r_back_depth, - int *hit_count) -{ - float depth_test; - if (!(isect_ray_tri_watertight_v3(ray_start, isect_precalc, t0, t1, t2, &depth_test, NULL) || - isect_ray_tri_watertight_v3(ray_start, isect_precalc, t0, t2, t3, &depth_test, NULL))) - { - return false; - } - return ray_update_depth_and_hit_count(depth_test, r_depth, r_back_depth, hit_count); -} - -bool ray_face_intersection_depth_tri(const float ray_start[3], - struct IsectRayPrecalc *isect_precalc, - const float t0[3], - const float t1[3], - const float t2[3], - float *r_depth, - float *r_back_depth, - int *hit_count) -{ - float depth_test; - - if (!isect_ray_tri_watertight_v3(ray_start, isect_precalc, t0, t1, t2, &depth_test, NULL)) { - return false; - } - return ray_update_depth_and_hit_count(depth_test, r_depth, r_back_depth, hit_count); -} - -/* Take advantage of the fact we know this won't be an intersection. - * Just handle ray-tri edges. */ -static float dist_squared_ray_to_tri_v3_fast(const float ray_origin[3], - const float ray_direction[3], - const float v0[3], - const float v1[3], - const float v2[3], - float r_point[3], - float *r_depth) -{ - const float *tri[3] = {v0, v1, v2}; - float dist_sq_best = FLT_MAX; - for (int i = 0, j = 2; i < 3; j = i++) { - float point_test[3], depth_test = FLT_MAX; - const float dist_sq_test = dist_squared_ray_to_seg_v3( - ray_origin, ray_direction, tri[i], tri[j], point_test, &depth_test); - if (dist_sq_test < dist_sq_best || i == 0) { - copy_v3_v3(r_point, point_test); - *r_depth = depth_test; - dist_sq_best = dist_sq_test; - } - } - return dist_sq_best; -} - -bool ray_face_nearest_quad(const float ray_start[3], - const float ray_normal[3], - const float t0[3], - const float t1[3], - const float t2[3], - const float t3[3], - float *depth, - float *dist_sq) -{ - float dist_sq_test; - float co[3], depth_test; - - if ((dist_sq_test = dist_squared_ray_to_tri_v3_fast( - ray_start, ray_normal, t0, t1, t2, co, &depth_test)) < *dist_sq) - { - *dist_sq = dist_sq_test; - *depth = depth_test; - if ((dist_sq_test = dist_squared_ray_to_tri_v3_fast( - ray_start, ray_normal, t0, t2, t3, co, &depth_test)) < *dist_sq) - { - *dist_sq = dist_sq_test; - *depth = depth_test; - } - return true; - } - - return false; -} - -bool ray_face_nearest_tri(const float ray_start[3], - const float ray_normal[3], - const float t0[3], - const float t1[3], - const float t2[3], - float *depth, - float *dist_sq) -{ - float dist_sq_test; - float co[3], depth_test; - - if ((dist_sq_test = dist_squared_ray_to_tri_v3_fast( - ray_start, ray_normal, t0, t1, t2, co, &depth_test)) < *dist_sq) - { - *dist_sq = dist_sq_test; - *depth = depth_test; - return true; - } - - return false; -} - -static bool pbvh_faces_node_raycast(PBVH *pbvh, - const PBVHNode *node, - float (*origco)[3], - const float ray_start[3], - const float ray_normal[3], - struct IsectRayPrecalc *isect_precalc, - int *hit_count, - float *depth, - float *depth_back, - PBVHVertRef *r_active_vertex_index, - PBVHFaceRef *r_active_face_index, - float *r_face_normal, - int stroke_id) -{ - const float(*positions)[3] = pbvh->vert_positions; - const MLoop *mloop = pbvh->mloop; - const int *faces = node->prim_indices; - int totface = node->totprim; - bool hit = false; - float nearest_vertex_co[3] = {0.0f}; - - for (int i = 0; i < totface; i++) { - const MLoopTri *lt = &pbvh->looptri[faces[i]]; - const int *face_verts = node->face_vert_indices[i]; - - if (pbvh->respect_hide && paint_is_face_hidden(lt, pbvh->hide_poly)) { - continue; - } - - const float *co[3]; - if (origco) { - /* Intersect with backed up original coordinates. */ - co[0] = origco[face_verts[0]]; - co[1] = origco[face_verts[1]]; - co[2] = origco[face_verts[2]]; - } - else { - /* intersect with current coordinates */ - co[0] = positions[mloop[lt->tri[0]].v]; - co[1] = positions[mloop[lt->tri[1]].v]; - co[2] = positions[mloop[lt->tri[2]].v]; - } - - if (!ray_face_intersection_depth_tri( - ray_start, isect_precalc, co[0], co[1], co[2], depth, depth_back, hit_count)) - { - continue; - } - - hit = true; - - if (r_face_normal) { - normal_tri_v3(r_face_normal, co[0], co[1], co[2]); - } - - if (r_active_vertex_index) { - float location[3] = {0.0f}; - madd_v3_v3v3fl(location, ray_start, ray_normal, *depth); - for (int j = 0; j < 3; j++) { - /* Always assign nearest_vertex_co in the first iteration to avoid comparison against - * uninitialized values. This stores the closest vertex in the current intersecting - * triangle. */ - if (j == 0 || - len_squared_v3v3(location, co[j]) < len_squared_v3v3(location, nearest_vertex_co)) { - copy_v3_v3(nearest_vertex_co, co[j]); - *r_active_vertex_index = (PBVHVertRef){.i = mloop[lt->tri[j]].v}; - *r_active_face_index = (PBVHFaceRef){.i = lt->poly}; - } - } - } - } - - return hit; -} - -static bool pbvh_grids_node_raycast(PBVH *pbvh, - PBVHNode *node, - float (*origco)[3], - const float ray_start[3], - const float ray_normal[3], - struct IsectRayPrecalc *isect_precalc, - int *hit_count, - float *depth, - float *back_depth, - - PBVHVertRef *r_active_vertex, - PBVHFaceRef *r_active_grid, - float *r_face_normal) -{ - const int totgrid = node->totprim; - const int gridsize = pbvh->gridkey.grid_size; - bool hit = false; - float nearest_vertex_co[3] = {0.0}; - const CCGKey *gridkey = &pbvh->gridkey; - - for (int i = 0; i < totgrid; i++) { - const int grid_index = node->prim_indices[i]; - CCGElem *grid = pbvh->grids[grid_index]; - BLI_bitmap *gh; - - if (!grid) { - continue; - } - - gh = pbvh->grid_hidden[grid_index]; - - for (int y = 0; y < gridsize - 1; y++) { - for (int x = 0; x < gridsize - 1; x++) { - /* check if grid face is hidden */ - if (gh) { - if (paint_is_grid_face_hidden(gh, gridsize, x, y)) { - continue; - } - } - - const float *co[4]; - if (origco) { - co[0] = origco[(y + 1) * gridsize + x]; - co[1] = origco[(y + 1) * gridsize + x + 1]; - co[2] = origco[y * gridsize + x + 1]; - co[3] = origco[y * gridsize + x]; - } - else { - co[0] = CCG_grid_elem_co(gridkey, grid, x, y + 1); - co[1] = CCG_grid_elem_co(gridkey, grid, x + 1, y + 1); - co[2] = CCG_grid_elem_co(gridkey, grid, x + 1, y); - co[3] = CCG_grid_elem_co(gridkey, grid, x, y); - } - - if (!ray_face_intersection_depth_quad(ray_start, - isect_precalc, - co[0], - co[1], - co[2], - co[3], - depth, - back_depth, - hit_count)) - { - continue; - } - hit = true; - - if (r_face_normal) { - normal_quad_v3(r_face_normal, co[0], co[1], co[2], co[3]); - } - - if (r_active_vertex) { - float location[3] = {0.0}; - madd_v3_v3v3fl(location, ray_start, ray_normal, *depth); - - const int x_it[4] = {0, 1, 1, 0}; - const int y_it[4] = {1, 1, 0, 0}; - - for (int j = 0; j < 4; j++) { - /* Always assign nearest_vertex_co in the first iteration to avoid comparison against - * uninitialized values. This stores the closest vertex in the current intersecting - * quad. */ - if (j == 0 || len_squared_v3v3(location, co[j]) < - len_squared_v3v3(location, nearest_vertex_co)) { - copy_v3_v3(nearest_vertex_co, co[j]); - - r_active_vertex->i = gridkey->grid_area * grid_index + - (y + y_it[j]) * gridkey->grid_size + (x + x_it[j]); - } - } - } - - if (r_active_grid) { - r_active_grid->i = grid_index; - } - } - } - - if (origco) { - origco += gridsize * gridsize; - } - } - - return hit; -} - -bool BKE_pbvh_node_raycast(PBVH *pbvh, - PBVHNode *node, - float (*origco)[3], - bool use_origco, - const float ray_start[3], - const float ray_normal[3], - struct IsectRayPrecalc *isect_precalc, - int *hit_count, - float *depth, - float *back_depth, - PBVHVertRef *active_vertex, - PBVHFaceRef *active_face_grid, - float *face_normal, - int stroke_id) -{ - bool hit = false; - - if (node->flag & PBVH_FullyHidden) { - return false; - } - - switch (pbvh->header.type) { - case PBVH_FACES: - hit |= pbvh_faces_node_raycast(pbvh, - node, - origco, - ray_start, - ray_normal, - isect_precalc, - hit_count, - depth, - back_depth, - active_vertex, - active_face_grid, - face_normal, - stroke_id); - - break; - case PBVH_GRIDS: - hit |= pbvh_grids_node_raycast(pbvh, - node, - origco, - ray_start, - ray_normal, - isect_precalc, - hit_count, - depth, - back_depth, - active_vertex, - active_face_grid, - face_normal); - break; - case PBVH_BMESH: - hit = pbvh_bmesh_node_raycast(pbvh, - node, - ray_start, - ray_normal, - isect_precalc, - hit_count, - depth, - back_depth, - use_origco, - active_vertex, - active_face_grid, - face_normal, - stroke_id); - break; - } - - return hit; -} - -void BKE_pbvh_raycast_project_ray_root( - PBVH *pbvh, bool original, float ray_start[3], float ray_end[3], float ray_normal[3]) -{ - if (pbvh->nodes) { - float rootmin_start, rootmin_end; - float bb_min_root[3], bb_max_root[3], bb_center[3], bb_diff[3]; - struct IsectRayAABB_Precalc ray; - float ray_normal_inv[3]; - float offset = 1.0f + 1e-3f; - const float offset_vec[3] = {1e-3f, 1e-3f, 1e-3f}; - - if (original) { - BKE_pbvh_node_get_original_BB(pbvh->nodes, bb_min_root, bb_max_root); - } - else { - BKE_pbvh_node_get_BB(pbvh->nodes, bb_min_root, bb_max_root); - } - - /* Slightly offset min and max in case we have a zero width node - * (due to a plane mesh for instance), or faces very close to the bounding box boundary. */ - mid_v3_v3v3(bb_center, bb_max_root, bb_min_root); - /* diff should be same for both min/max since it's calculated from center */ - sub_v3_v3v3(bb_diff, bb_max_root, bb_center); - /* handles case of zero width bb */ - add_v3_v3(bb_diff, offset_vec); - madd_v3_v3v3fl(bb_max_root, bb_center, bb_diff, offset); - madd_v3_v3v3fl(bb_min_root, bb_center, bb_diff, -offset); - - /* first project start ray */ - isect_ray_aabb_v3_precalc(&ray, ray_start, ray_normal); - if (!isect_ray_aabb_v3(&ray, bb_min_root, bb_max_root, &rootmin_start)) { - return; - } - - /* then the end ray */ - mul_v3_v3fl(ray_normal_inv, ray_normal, -1.0); - isect_ray_aabb_v3_precalc(&ray, ray_end, ray_normal_inv); - /* unlikely to fail exiting if entering succeeded, still keep this here */ - if (!isect_ray_aabb_v3(&ray, bb_min_root, bb_max_root, &rootmin_end)) { - return; - } - - madd_v3_v3v3fl(ray_start, ray_start, ray_normal, rootmin_start); - madd_v3_v3v3fl(ray_end, ray_end, ray_normal_inv, rootmin_end); - } -} - -/* -------------------------------------------------------------------- */ - -typedef struct { - struct DistRayAABB_Precalc dist_ray_to_aabb_precalc; - bool original; -} FindNearestRayData; - -static bool nearest_to_ray_aabb_dist_sq(PBVHNode *node, void *data_v) -{ - FindNearestRayData *rcd = data_v; - const float *bb_min, *bb_max; - - if (rcd->original) { - /* BKE_pbvh_node_get_original_BB */ - bb_min = node->orig_vb.bmin; - bb_max = node->orig_vb.bmax; - } - else { - /* BKE_pbvh_node_get_BB */ - bb_min = node->vb.bmin; - bb_max = node->vb.bmax; - } - - float co_dummy[3], depth; - node->tmin = dist_squared_ray_to_aabb_v3( - &rcd->dist_ray_to_aabb_precalc, bb_min, bb_max, co_dummy, &depth); - /* Ideally we would skip distances outside the range. */ - return depth > 0.0f; -} - -void BKE_pbvh_find_nearest_to_ray(PBVH *pbvh, - BKE_pbvh_SearchNearestCallback cb, - void *data, - const float ray_start[3], - const float ray_normal[3], - bool original) -{ - FindNearestRayData ncd; - - dist_squared_ray_to_aabb_v3_precalc(&ncd.dist_ray_to_aabb_precalc, ray_start, ray_normal); - ncd.original = original; - - BKE_pbvh_search_callback_occluded(pbvh, nearest_to_ray_aabb_dist_sq, &ncd, cb, data); -} - -static bool pbvh_faces_node_nearest_to_ray(PBVH *pbvh, - const PBVHNode *node, - float (*origco)[3], - const float ray_start[3], - const float ray_normal[3], - float *depth, - float *dist_sq) -{ - const float(*positions)[3] = pbvh->vert_positions; - const MLoop *mloop = pbvh->mloop; - const int *faces = node->prim_indices; - int i, totface = node->totprim; - bool hit = false; - - for (i = 0; i < totface; i++) { - const MLoopTri *lt = &pbvh->looptri[faces[i]]; - const int *face_verts = node->face_vert_indices[i]; - - if (pbvh->respect_hide && paint_is_face_hidden(lt, pbvh->hide_poly)) { - continue; - } - - if (origco) { - /* Intersect with backed-up original coordinates. */ - hit |= ray_face_nearest_tri(ray_start, - ray_normal, - origco[face_verts[0]], - origco[face_verts[1]], - origco[face_verts[2]], - depth, - dist_sq); - } - else { - /* intersect with current coordinates */ - hit |= ray_face_nearest_tri(ray_start, - ray_normal, - positions[mloop[lt->tri[0]].v], - positions[mloop[lt->tri[1]].v], - positions[mloop[lt->tri[2]].v], - depth, - dist_sq); - } - } - - return hit; -} - -static bool pbvh_grids_node_nearest_to_ray(PBVH *pbvh, - PBVHNode *node, - float (*origco)[3], - const float ray_start[3], - const float ray_normal[3], - float *depth, - float *dist_sq) -{ - const int totgrid = node->totprim; - const int gridsize = pbvh->gridkey.grid_size; - bool hit = false; - - for (int i = 0; i < totgrid; i++) { - CCGElem *grid = pbvh->grids[node->prim_indices[i]]; - BLI_bitmap *gh; - - if (!grid) { - continue; - } - - gh = pbvh->grid_hidden[node->prim_indices[i]]; - - for (int y = 0; y < gridsize - 1; y++) { - for (int x = 0; x < gridsize - 1; x++) { - /* check if grid face is hidden */ - if (gh) { - if (paint_is_grid_face_hidden(gh, gridsize, x, y)) { - continue; - } - } - - if (origco) { - hit |= ray_face_nearest_quad(ray_start, - ray_normal, - origco[y * gridsize + x], - origco[y * gridsize + x + 1], - origco[(y + 1) * gridsize + x + 1], - origco[(y + 1) * gridsize + x], - depth, - dist_sq); - } - else { - hit |= ray_face_nearest_quad(ray_start, - ray_normal, - CCG_grid_elem_co(&pbvh->gridkey, grid, x, y), - CCG_grid_elem_co(&pbvh->gridkey, grid, x + 1, y), - CCG_grid_elem_co(&pbvh->gridkey, grid, x + 1, y + 1), - CCG_grid_elem_co(&pbvh->gridkey, grid, x, y + 1), - depth, - dist_sq); - } - } - } - - if (origco) { - origco += gridsize * gridsize; - } - } - - return hit; -} - -bool BKE_pbvh_node_find_nearest_to_ray(PBVH *pbvh, - PBVHNode *node, - float (*origco)[3], - bool use_origco, - const float ray_start[3], - const float ray_normal[3], - float *depth, - float *dist_sq, - int stroke_id) -{ - bool hit = false; - - if (node->flag & PBVH_FullyHidden) { - return false; - } - - switch (pbvh->header.type) { - case PBVH_FACES: - hit |= pbvh_faces_node_nearest_to_ray( - pbvh, node, origco, ray_start, ray_normal, depth, dist_sq); - break; - case PBVH_GRIDS: - hit |= pbvh_grids_node_nearest_to_ray( - pbvh, node, origco, ray_start, ray_normal, depth, dist_sq); - break; - case PBVH_BMESH: - hit = pbvh_bmesh_node_nearest_to_ray( - pbvh, node, ray_start, ray_normal, depth, dist_sq, use_origco, stroke_id); - break; - } - - return hit; -} - -typedef enum { - ISECT_INSIDE, - ISECT_OUTSIDE, - ISECT_INTERSECT, -} PlaneAABBIsect; - -/* Adapted from: - * http://www.gamedev.net/community/forums/topic.asp?topic_id=512123 - * Returns true if the AABB is at least partially within the frustum - * (ok, not a real frustum), false otherwise. - */ -static PlaneAABBIsect test_frustum_aabb(const float bb_min[3], - const float bb_max[3], - PBVHFrustumPlanes *frustum) -{ - PlaneAABBIsect ret = ISECT_INSIDE; - float(*planes)[4] = frustum->planes; - - for (int i = 0; i < frustum->num_planes; i++) { - float vmin[3], vmax[3]; - - for (int axis = 0; axis < 3; axis++) { - if (planes[i][axis] < 0) { - vmin[axis] = bb_min[axis]; - vmax[axis] = bb_max[axis]; - } - else { - vmin[axis] = bb_max[axis]; - vmax[axis] = bb_min[axis]; - } - } - - if (dot_v3v3(planes[i], vmin) + planes[i][3] < 0) { - return ISECT_OUTSIDE; - } - if (dot_v3v3(planes[i], vmax) + planes[i][3] <= 0) { - ret = ISECT_INTERSECT; - } - } - - return ret; -} - -bool BKE_pbvh_node_frustum_contain_AABB(PBVHNode *node, void *data) -{ - const float *bb_min, *bb_max; - /* BKE_pbvh_node_get_BB */ - bb_min = node->vb.bmin; - bb_max = node->vb.bmax; - - return test_frustum_aabb(bb_min, bb_max, data) != ISECT_OUTSIDE; -} - -bool BKE_pbvh_node_frustum_exclude_AABB(PBVHNode *node, void *data) -{ - const float *bb_min, *bb_max; - /* BKE_pbvh_node_get_BB */ - bb_min = node->vb.bmin; - bb_max = node->vb.bmax; - - return test_frustum_aabb(bb_min, bb_max, data) != ISECT_INSIDE; -} - -void BKE_pbvh_update_normals(PBVH *pbvh, struct SubdivCCG *subdiv_ccg) -{ - /* Update normals */ - PBVHNode **nodes; - int totnode; - - if (pbvh->header.type == PBVH_BMESH) { - for (int i = 0; i < pbvh->totnode; i++) { - if (pbvh->nodes[i].flag & PBVH_Leaf) { - BKE_pbvh_bmesh_check_tris(pbvh, pbvh->nodes + i); - } - } - } - - BKE_pbvh_search_gather( - pbvh, update_search_cb, POINTER_FROM_INT(PBVH_UpdateNormals), &nodes, &totnode); - - if (totnode > 0) { - if (pbvh->header.type == PBVH_BMESH) { - pbvh_bmesh_normals_update(pbvh, nodes, totnode); - } - else if (pbvh->header.type == PBVH_FACES) { - pbvh_faces_update_normals(pbvh, nodes, totnode); - } - else if (pbvh->header.type == PBVH_GRIDS) { - struct CCGFace **faces; - int num_faces; - BKE_pbvh_get_grid_updates(pbvh, true, (void ***)&faces, &num_faces); - if (num_faces > 0) { - BKE_subdiv_ccg_update_normals(subdiv_ccg, faces, num_faces); - MEM_freeN(faces); - } - } - } - - MEM_SAFE_FREE(nodes); -} - -void BKE_pbvh_face_sets_color_set(PBVH *pbvh, int seed, int color_default) -{ - pbvh->face_sets_color_seed = seed; - pbvh->face_sets_color_default = color_default; -} - -/** - * PBVH drawing, updating draw buffers as needed and culling any nodes outside - * the specified frustum. - */ -typedef struct PBVHDrawSearchData { - PBVHFrustumPlanes *frustum; - int accum_update_flag; - PBVHAttrReq *attrs; - int attrs_num; -} PBVHDrawSearchData; - -static bool pbvh_draw_search_cb(PBVHNode *node, void *data_v) -{ - PBVHDrawSearchData *data = data_v; - if (data->frustum && !BKE_pbvh_node_frustum_contain_AABB(node, data->frustum)) { - return false; - } - - data->accum_update_flag |= node->flag; - return true; -} - -void BKE_pbvh_draw_cb(PBVH *pbvh, - Mesh *me, - bool update_only_visible, - PBVHFrustumPlanes *update_frustum, - PBVHFrustumPlanes *draw_frustum, - void (*draw_fn)(void *user_data, PBVHBatches *batches, PBVH_GPU_Args *args), - void *user_data, - bool UNUSED(full_render), - PBVHAttrReq *attrs, - int attrs_num) -{ - PBVHNode **nodes; - int totnode; - int update_flag = 0; - - pbvh->draw_cache_invalid = false; - - /* Search for nodes that need updates. */ - if (update_only_visible) { - /* Get visible nodes with draw updates. */ - PBVHDrawSearchData data = { - .frustum = update_frustum, .accum_update_flag = 0, attrs, attrs_num}; - BKE_pbvh_search_gather(pbvh, pbvh_draw_search_cb, &data, &nodes, &totnode); - update_flag = data.accum_update_flag; - } - else { - /* Get all nodes with draw updates, also those outside the view. */ - const int search_flag = PBVH_RebuildDrawBuffers | PBVH_UpdateDrawBuffers; - BKE_pbvh_search_gather( - pbvh, update_search_cb, POINTER_FROM_INT(search_flag), &nodes, &totnode); - update_flag = PBVH_RebuildDrawBuffers | PBVH_UpdateDrawBuffers; - } - - /* Update draw buffers. */ - if (totnode != 0 && (update_flag & (PBVH_RebuildDrawBuffers | PBVH_UpdateDrawBuffers))) { - // check that need_full_render is set to GPU_pbvh_need_full_render_get(), - // but only if nodes need updating} - pbvh_update_draw_buffers(pbvh, me, nodes, totnode, update_flag); - } - MEM_SAFE_FREE(nodes); - - /* Draw visible nodes. */ - PBVHDrawSearchData draw_data = {.frustum = draw_frustum, .accum_update_flag = 0}; - BKE_pbvh_search_gather(pbvh, pbvh_draw_search_cb, &draw_data, &nodes, &totnode); - - PBVH_GPU_Args args; - - for (int i = 0; i < totnode; i++) { - PBVHNode *node = nodes[i]; - if (!(node->flag & PBVH_FullyHidden)) { - pbvh_draw_args_init(pbvh, &args, node); - - draw_fn(user_data, node->draw_batches, &args); - } - } - - MEM_SAFE_FREE(nodes); -} - -void BKE_pbvh_draw_debug_cb(PBVH *pbvh, - void (*draw_fn)(PBVHNode *node, - void *user_data, - const float bmin[3], - const float bmax[3], - PBVHNodeFlags flag), - void *user_data) -{ - for (int a = 0; a < pbvh->totnode; a++) { - PBVHNode *node = &pbvh->nodes[a]; - - if (pbvh_show_orig_co) { - draw_fn(node, user_data, node->orig_vb.bmin, node->orig_vb.bmax, node->flag); - } - else { - draw_fn(node, user_data, node->vb.bmin, node->vb.bmax, node->flag); - } - } -} - -void BKE_pbvh_grids_update(PBVH *pbvh, - CCGElem **grids, - void **gridfaces, - DMFlagMat *flagmats, - BLI_bitmap **grid_hidden, - CCGKey *key) -{ - pbvh->gridkey = *key; - pbvh->grids = grids; - pbvh->gridfaces = gridfaces; - - if (flagmats != pbvh->grid_flag_mats || pbvh->grid_hidden != grid_hidden) { - pbvh->grid_flag_mats = flagmats; - pbvh->grid_hidden = grid_hidden; - - for (int a = 0; a < pbvh->totnode; a++) { - BKE_pbvh_node_mark_rebuild_draw(&pbvh->nodes[a]); - } - } -} - -float (*BKE_pbvh_vert_coords_alloc(PBVH *pbvh))[3] -{ - float(*vertCos)[3] = NULL; - - if (pbvh->vert_positions) { - vertCos = MEM_malloc_arrayN(pbvh->totvert, sizeof(float[3]), __func__); - memcpy(vertCos, pbvh->vert_positions, sizeof(float[3]) * pbvh->totvert); - } - - return vertCos; -} - -void BKE_pbvh_vert_coords_apply(PBVH *pbvh, const float (*vertCos)[3], const int totvert) -{ - if (totvert != pbvh->totvert) { - BLI_assert_msg(0, "PBVH: Given deforming vcos number does not match PBVH vertex number!"); - return; - } - - if (!pbvh->deformed) { - if (pbvh->vert_positions) { - /* if pbvh is not already deformed, verts/faces points to the */ - /* original data and applying new coords to this arrays would lead to */ - /* unneeded deformation -- duplicate verts/faces to avoid this */ - - pbvh->vert_positions = MEM_dupallocN(pbvh->vert_positions); - /* No need to dupalloc pbvh->looptri, this one is 'totally owned' by pbvh, - * it's never some mesh data. */ - - pbvh->deformed = true; - } - } - - if (pbvh->vert_positions) { - float(*positions)[3] = pbvh->vert_positions; - /* copy new verts coords */ - for (int a = 0; a < pbvh->totvert; a++) { - /* no need for float comparison here (memory is exactly equal or not) */ - if (memcmp(positions[a], vertCos[a], sizeof(float[3])) != 0) { - copy_v3_v3(positions[a], vertCos[a]); - BKE_pbvh_vert_tag_update_normal(pbvh, BKE_pbvh_make_vref(a)); - } - } - - for (int a = 0; a < pbvh->totnode; a++) { - BKE_pbvh_node_mark_update(&pbvh->nodes[a]); - } - - BKE_pbvh_update_bounds(pbvh, PBVH_UpdateBB | PBVH_UpdateOriginalBB); - } -} - -bool BKE_pbvh_is_deformed(PBVH *pbvh) -{ - return pbvh->deformed; -} -/* Proxies */ - -PBVHProxyNode *BKE_pbvh_node_add_proxy(PBVH *pbvh, PBVHNode *node) -{ - int index, totverts; - - index = node->proxy_count; - - node->proxy_count++; - - if (node->proxies) { - node->proxies = MEM_reallocN(node->proxies, node->proxy_count * sizeof(PBVHProxyNode)); - } - else { - node->proxies = MEM_mallocN(sizeof(PBVHProxyNode), "PBVHNodeProxy"); - } - - BKE_pbvh_node_num_verts(pbvh, node, &totverts, NULL); - node->proxies[index].co = MEM_callocN(sizeof(float[3]) * totverts, "PBVHNodeProxy.co"); - - return node->proxies + index; -} - -void BKE_pbvh_node_free_proxies(PBVHNode *node) -{ - for (int p = 0; p < node->proxy_count; p++) { - MEM_freeN(node->proxies[p].co); - node->proxies[p].co = NULL; - } - - MEM_SAFE_FREE(node->proxies); - node->proxies = NULL; - - node->proxy_count = 0; -} - -void BKE_pbvh_gather_proxies(PBVH *pbvh, PBVHNode ***r_array, int *r_tot) -{ - PBVHNode **array = NULL; - int tot = 0, space = 0; - - for (int n = 0; n < pbvh->totnode; n++) { - PBVHNode *node = pbvh->nodes + n; - - if (node->proxy_count > 0) { - if (tot == space) { - /* resize array if needed */ - space = (tot == 0) ? 32 : space * 2; - array = MEM_recallocN_id(array, sizeof(PBVHNode *) * space, __func__); - } - - array[tot] = node; - tot++; - } - } - - if (tot == 0 && array) { - MEM_freeN(array); - array = NULL; - } - - *r_array = array; - *r_tot = tot; -} - -void pbvh_vertex_iter_init(PBVH *pbvh, PBVHNode *node, PBVHVertexIter *vi, int mode) -{ - struct CCGElem **grids; - int *grid_indices; - int totgrid, gridsize, uniq_verts, totvert; - - vi->grid = NULL; - vi->no = NULL; - vi->fno = NULL; - vi->vert_positions = NULL; - vi->vertex.i = 0LL; - vi->index = 0; - - vi->respect_hide = pbvh->respect_hide; - if (pbvh->respect_hide == false) { - /* The same value for all vertices. */ - vi->visible = true; - } - - BKE_pbvh_node_get_grids(pbvh, node, &grid_indices, &totgrid, NULL, &gridsize, &grids); - BKE_pbvh_node_num_verts(pbvh, node, &uniq_verts, &totvert); - const int *vert_indices = BKE_pbvh_node_get_vert_indices(node); - vi->key = pbvh->gridkey; - - vi->grids = grids; - vi->grid_indices = grid_indices; - vi->totgrid = (grids) ? totgrid : 1; - vi->gridsize = gridsize; - - if (mode == PBVH_ITER_ALL) { - vi->totvert = totvert; - } - else { - vi->totvert = uniq_verts; - } - vi->vert_indices = vert_indices; - vi->vert_positions = pbvh->vert_positions; - vi->is_mesh = pbvh->vert_positions != NULL; - - if (pbvh->header.type == PBVH_BMESH) { - if (mode == PBVH_ITER_ALL) { - pbvh_bmesh_check_other_verts(node); - } - - vi->vert_positions = NULL; - - vi->bi = 0; - vi->bm_cur_set = node->bm_unique_verts; - vi->bm_unique_verts = node->bm_unique_verts; - vi->bm_other_verts = node->bm_other_verts; - vi->bm_vdata = &pbvh->header.bm->vdata; - vi->bm_vert = NULL; - - vi->cd_sculpt_vert = CustomData_get_offset(vi->bm_vdata, CD_DYNTOPO_VERT); - vi->cd_vert_mask_offset = CustomData_get_offset(vi->bm_vdata, CD_PAINT_MASK); - } - - vi->gh = NULL; - if (vi->grids && mode == PBVH_ITER_UNIQUE) { - vi->grid_hidden = pbvh->grid_hidden; - } - - vi->mask = NULL; - if (pbvh->header.type == PBVH_FACES) { - vi->vert_normals = pbvh->vert_normals; - vi->hide_vert = pbvh->hide_vert; - - vi->vmask = CustomData_get_layer_for_write(pbvh->vdata, CD_PAINT_MASK, pbvh->mesh->totvert); - } -} - -bool BKE_pbvh_draw_mask(const PBVH *pbvh) -{ - return BKE_pbvh_has_mask(pbvh); -} - -bool BKE_pbvh_has_mask(const PBVH *pbvh) -{ - switch (pbvh->header.type) { - case PBVH_GRIDS: - return (pbvh->gridkey.has_mask != 0); - case PBVH_FACES: - return (pbvh->vdata && CustomData_get_layer(pbvh->vdata, CD_PAINT_MASK)); - case PBVH_BMESH: - return (pbvh->header.bm && - (CustomData_get_offset(&pbvh->header.bm->vdata, CD_PAINT_MASK) != -1)); - } - - return false; -} - -bool BKE_pbvh_draw_face_sets(PBVH *pbvh) -{ - switch (pbvh->header.type) { - case PBVH_GRIDS: - case PBVH_FACES: - return pbvh->pdata && - CustomData_get_layer_named(pbvh->pdata, CD_PROP_INT32, ".sculpt_face_set") != NULL; - case PBVH_BMESH: - return (pbvh->header.bm && CustomData_get_named_layer_index(&pbvh->header.bm->pdata, - CD_PROP_INT32, - ".sculpt_face_set") != -1); - } - - return false; -} - -void BKE_pbvh_set_frustum_planes(PBVH *pbvh, PBVHFrustumPlanes *planes) -{ - pbvh->num_planes = planes->num_planes; - for (int i = 0; i < pbvh->num_planes; i++) { - copy_v4_v4(pbvh->planes[i], planes->planes[i]); - } -} - -void BKE_pbvh_get_frustum_planes(PBVH *pbvh, PBVHFrustumPlanes *planes) -{ - planes->num_planes = pbvh->num_planes; - for (int i = 0; i < planes->num_planes; i++) { - copy_v4_v4(planes->planes[i], pbvh->planes[i]); - } -} - -#include "BKE_global.h" -void BKE_pbvh_parallel_range_settings(TaskParallelSettings *settings, - bool use_threading, - int totnode) -{ - memset(settings, 0, sizeof(*settings)); - settings->use_threading = use_threading && totnode > 1 && G.debug_value != 890; -} - -float (*BKE_pbvh_get_vert_positions(const PBVH *pbvh))[3] -{ - BLI_assert(pbvh->header.type == PBVH_FACES); - return pbvh->vert_positions; -} - -const float (*BKE_pbvh_get_vert_normals(const PBVH *pbvh))[3] -{ - BLI_assert(pbvh->header.type == PBVH_FACES); - return pbvh->vert_normals; -} - -const bool *BKE_pbvh_get_vert_hide(const PBVH *pbvh) -{ - BLI_assert(pbvh->header.type == PBVH_FACES); - return pbvh->hide_vert; -} - -const bool *BKE_pbvh_get_poly_hide(const PBVH *pbvh) -{ - BLI_assert(ELEM(pbvh->header.type, PBVH_FACES, PBVH_GRIDS)); - return pbvh->hide_poly; -} - -bool *BKE_pbvh_get_vert_hide_for_write(PBVH *pbvh) -{ - BLI_assert(pbvh->header.type == PBVH_FACES); - if (pbvh->hide_vert) { - return pbvh->hide_vert; - } - pbvh->hide_vert = CustomData_get_layer_named_for_write( - &pbvh->mesh->vdata, CD_PROP_BOOL, ".hide_vert", pbvh->mesh->totvert); - if (pbvh->hide_vert) { - return pbvh->hide_vert; - } - pbvh->hide_vert = (bool *)CustomData_add_layer_named( - &pbvh->mesh->vdata, CD_PROP_BOOL, CD_SET_DEFAULT, NULL, pbvh->mesh->totvert, ".hide_vert"); - return pbvh->hide_vert; -} - -void BKE_pbvh_subdiv_ccg_set(PBVH *pbvh, SubdivCCG *subdiv_ccg) -{ - pbvh->subdiv_ccg = subdiv_ccg; - pbvh->gridfaces = (void **)subdiv_ccg->grid_faces; - pbvh->grid_hidden = subdiv_ccg->grid_hidden; - pbvh->grid_flag_mats = subdiv_ccg->grid_flag_mats; - pbvh->grids = subdiv_ccg->grids; -} - -void BKE_pbvh_face_sets_set(PBVH *pbvh, int *face_sets) -{ - pbvh->face_sets = face_sets; -} - -void BKE_pbvh_update_hide_attributes_from_mesh(PBVH *pbvh) -{ - if (pbvh->header.type == PBVH_FACES) { - pbvh->hide_vert = CustomData_get_layer_named_for_write( - &pbvh->mesh->vdata, CD_PROP_BOOL, ".hide_vert", pbvh->mesh->totvert); - pbvh->hide_poly = CustomData_get_layer_named_for_write( - &pbvh->mesh->pdata, CD_PROP_BOOL, ".hide_poly", pbvh->mesh->totpoly); - } -} - -void BKE_pbvh_respect_hide_set(PBVH *pbvh, bool respect_hide) -{ - pbvh->respect_hide = respect_hide; -} - -int BKE_pbvh_get_node_index(PBVH *pbvh, PBVHNode *node) -{ - return (int)(node - pbvh->nodes); -} - -int BKE_pbvh_get_totnodes(PBVH *pbvh) -{ - return pbvh->totnode; -} - -int BKE_pbvh_get_node_id(PBVH *pbvh, PBVHNode *node) -{ - return node->id; -} - -void BKE_pbvh_get_nodes(PBVH *pbvh, int flag, PBVHNode ***r_array, int *r_totnode) -{ - BKE_pbvh_search_gather(pbvh, update_search_cb, POINTER_FROM_INT(flag), r_array, r_totnode); -} - -PBVHNode *BKE_pbvh_node_from_index(PBVH *pbvh, int node_i) -{ - return pbvh->nodes + node_i; -} - -#ifdef PROXY_ADVANCED -// TODO: if this really works, make sure to pull the neighbor iterator out of sculpt.c and put it -// here -/* clang-format off */ -# include "BKE_context.h" -# include "DNA_object_types.h" -# include "DNA_scene_types.h" -# include "../../editors/sculpt_paint/sculpt_intern.h" -/* clang-format on */ - -int checkalloc(void **data, int esize, int oldsize, int newsize, int emask, int umask) -{ - // update channel if it already was allocated once, or is requested by umask - if (newsize != oldsize && (*data || (emask & umask))) { - if (*data) { - *data = MEM_reallocN(*data, newsize * esize); - } - else { - *data = MEM_mallocN(newsize * esize, "pbvh proxy vert arrays"); - } - return emask; - } - - return 0; -} - -void BKE_pbvh_ensure_proxyarray_indexmap(PBVH *pbvh, PBVHNode *node, GHash *vert_node_map) -{ - ProxyVertArray *p = &node->proxyverts; - - int totvert = 0; - BKE_pbvh_node_num_verts(pbvh, node, &totvert, NULL); - - bool update = !p->indexmap || p->size != totvert; - update = update || (p->indexmap && BLI_ghash_len(p->indexmap) != totvert); - - if (!update) { - return; - } - - if (p->indexmap) { - BLI_ghash_free(p->indexmap, NULL, NULL); - } - - GHash *gs = p->indexmap = BLI_ghash_ptr_new("BKE_pbvh_ensure_proxyarray_indexmap"); - - PBVHVertexIter vd; - - int i = 0; - BKE_pbvh_vertex_iter_begin (pbvh, node, vd, PBVH_ITER_UNIQUE) { - BLI_ghash_insert(gs, (void *)vd.vertex.i, (void *)i); - i++; - } - BKE_pbvh_vertex_iter_end; -} - -bool pbvh_proxyarray_needs_update(PBVH *pbvh, PBVHNode *node, int mask) -{ - ProxyVertArray *p = &node->proxyverts; - int totvert = 0; - - if (!(node->flag & PBVH_Leaf) || !node->bm_unique_verts) { - return false; - } - - BKE_pbvh_node_num_verts(pbvh, node, &totvert, NULL); - - bool bad = p->size != totvert; - bad = bad || ((mask & PV_NEIGHBORS) && p->neighbors_dirty); - bad = bad || (p->datamask & mask) != mask; - - bad = bad && totvert > 0; - - return bad; -} - -GHash *pbvh_build_vert_node_map(PBVH *pbvh, PBVHNode **nodes, int totnode, int mask) -{ - GHash *vert_node_map = BLI_ghash_ptr_new("BKE_pbvh_ensure_proxyarrays"); - - for (int i = 0; i < totnode; i++) { - PBVHVertexIter vd; - PBVHNode *node = nodes[i]; - - if (!(node->flag & PBVH_Leaf)) { - continue; - } - - BKE_pbvh_vertex_iter_begin (pbvh, node, vd, PBVH_ITER_UNIQUE) { - BLI_ghash_insert(vert_node_map, (void *)vd.vertex.i, (void *)(node - pbvh->nodes)); - } - BKE_pbvh_vertex_iter_end; - } - - return vert_node_map; -} - -void BKE_pbvh_ensure_proxyarrays( - SculptSession *ss, PBVH *pbvh, PBVHNode **nodes, int totnode, int mask) -{ - - bool update = false; - - for (int i = 0; i < totnode; i++) { - if (pbvh_proxyarray_needs_update(pbvh, nodes[i], mask)) { - update = true; - break; - } - } - - if (!update) { - return; - } - - GHash *vert_node_map = pbvh_build_vert_node_map(pbvh, nodes, totnode, mask); - - for (int i = 0; i < totnode; i++) { - if (nodes[i]->flag & PBVH_Leaf) { - BKE_pbvh_ensure_proxyarray_indexmap(pbvh, nodes[i], vert_node_map); - } - } - - for (int i = 0; i < totnode; i++) { - if (nodes[i]->flag & PBVH_Leaf) { - BKE_pbvh_ensure_proxyarray(ss, pbvh, nodes[i], mask, vert_node_map, false, false); - } - } - - if (vert_node_map) { - BLI_ghash_free(vert_node_map, NULL, NULL); - } -} - -void BKE_pbvh_ensure_proxyarray(SculptSession *ss, - PBVH *pbvh, - PBVHNode *node, - int mask, - GHash *vert_node_map, - bool check_indexmap, - bool force_update) -{ - ProxyVertArray *p = &node->proxyverts; - - if (check_indexmap) { - BKE_pbvh_ensure_proxyarray_indexmap(pbvh, node, vert_node_map); - } - - GHash *gs = p->indexmap; - - int totvert = 0; - BKE_pbvh_node_num_verts(pbvh, node, &totvert, NULL); - - if (!totvert) { - return; - } - - int updatemask = 0; - -# define UPDATETEST(name, emask, esize) \ - if (mask & emask) { \ - updatemask |= checkalloc((void **)&p->name, esize, p->size, totvert, emask, mask); \ - } - - UPDATETEST(ownerco, PV_OWNERCO, sizeof(void *)) - UPDATETEST(ownerno, PV_OWNERNO, sizeof(void *)) - UPDATETEST(ownermask, PV_OWNERMASK, sizeof(void *)) - UPDATETEST(ownercolor, PV_OWNERCOLOR, sizeof(void *)) - UPDATETEST(co, PV_CO, sizeof(float) * 3) - UPDATETEST(no, PV_NO, sizeof(short) * 3) - UPDATETEST(fno, PV_NO, sizeof(float) * 3) - UPDATETEST(mask, PV_MASK, sizeof(float)) - UPDATETEST(color, PV_COLOR, sizeof(float) * 4) - UPDATETEST(index, PV_INDEX, sizeof(PBVHVertRef)) - UPDATETEST(neighbors, PV_NEIGHBORS, sizeof(ProxyKey) * MAX_PROXY_NEIGHBORS) - - p->size = totvert; - - if (force_update) { - updatemask |= mask; - } - - if ((mask & PV_NEIGHBORS) && p->neighbors_dirty) { - updatemask |= PV_NEIGHBORS; - } - - if (!updatemask) { - return; - } - - p->datamask |= mask; - - PBVHVertexIter vd; - - int i = 0; - -# if 1 - BKE_pbvh_vertex_iter_begin (pbvh, node, vd, PBVH_ITER_UNIQUE) { - void **val; - - if (!BLI_ghash_ensure_p(gs, (void *)vd.vertex.i, &val)) { - *val = (void *)i; - }; - i++; - } - BKE_pbvh_vertex_iter_end; -# endif - - if (updatemask & PV_NEIGHBORS) { - p->neighbors_dirty = false; - } - - i = 0; - BKE_pbvh_vertex_iter_begin (pbvh, node, vd, PBVH_ITER_UNIQUE) { - if (i >= p->size) { - printf("error!! %s\n", __func__); - break; - } - - if (updatemask & PV_OWNERCO) { - p->ownerco[i] = vd.co; - } - if (updatemask & PV_INDEX) { - p->index[i] = vd.vertex; - } - if (updatemask & PV_OWNERNO) { - p->ownerno[i] = vd.no; - } - if (updatemask & PV_NO) { - if (vd.fno) { - if (p->fno) { - copy_v3_v3(p->fno[i], vd.fno); - } - normal_float_to_short_v3(p->no[i], vd.fno); - } - else if (vd.no) { - copy_v3_v3_short(p->no[i], vd.no); - if (p->fno) { - normal_short_to_float_v3(p->fno[i], vd.no); - } - } - else { - p->no[i][0] = p->no[i][1] = p->no[i][2] = 0; - if (p->fno) { - zero_v3(p->fno[i]); - } - } - } - if (updatemask & PV_CO) { - copy_v3_v3(p->co[i], vd.co); - } - if (updatemask & PV_OWNERMASK) { - p->ownermask[i] = vd.mask; - } - if (updatemask & PV_MASK) { - p->mask[i] = vd.mask ? *vd.mask : 0.0f; - } - if (updatemask & PV_COLOR) { - if (vd.vcol) { - copy_v4_v4(p->color[i], vd.vcol->color); - } - } - - if (updatemask & PV_NEIGHBORS) { - int j = 0; - SculptVertexNeighborIter ni; - - SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.vertex, ni) { - if (j >= MAX_PROXY_NEIGHBORS - 1) { - break; - } - - ProxyKey key; - - int *pindex = (int *)BLI_ghash_lookup_p(gs, (void *)ni.vertex.i); - - if (!pindex) { - if (vert_node_map) { - int *nindex = (int *)BLI_ghash_lookup_p(vert_node_map, (void *)ni.vertex.i); - - if (!nindex) { - p->neighbors_dirty = true; - continue; - } - - PBVHNode *node2 = pbvh->nodes + *nindex; - if (node2->proxyverts.indexmap) { - pindex = (int *)BLI_ghash_lookup_p(node2->proxyverts.indexmap, (void *)ni.vertex.i); - } - else { - pindex = NULL; - } - - if (!pindex) { - p->neighbors_dirty = true; - continue; - } - - key.node = (int)(node2 - pbvh->nodes); - key.pindex = *pindex; - //* - if (node2->proxyverts.size != 0 && - (key.pindex < 0 || key.pindex >= node2->proxyverts.size)) { - printf("error! %s\n", __func__); - fflush(stdout); - p->neighbors_dirty = true; - continue; - } - //*/ - } - else { - p->neighbors_dirty = true; - continue; - } - } - else { - key.node = (int)(node - pbvh->nodes); - key.pindex = *pindex; - } - - p->neighbors[i][j++] = key; - } - SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); - - p->neighbors[i][j].node = -1; - } - - i++; - } - BKE_pbvh_vertex_iter_end; -} - -typedef struct GatherProxyThread { - PBVHNode **nodes; - PBVH *pbvh; - int mask; -} GatherProxyThread; - -static void pbvh_load_proxyarray_exec(void *__restrict userdata, - const int n, - const TaskParallelTLS *__restrict tls) -{ - GatherProxyThread *data = (GatherProxyThread *)userdata; - PBVHNode *node = data->nodes[n]; - PBVHVertexIter vd; - ProxyVertArray *p = &node->proxyverts; - int i = 0; - - int mask = p->datamask; - - BKE_pbvh_ensure_proxyarray(NULL, data->pbvh, node, data->mask, NULL, false, true); -} - -void BKE_pbvh_load_proxyarrays(PBVH *pbvh, PBVHNode **nodes, int totnode, int mask) -{ - GatherProxyThread data = {.nodes = nodes, .pbvh = pbvh, .mask = mask}; - - mask = mask & ~PV_NEIGHBORS; // don't update neighbors in threaded code? - - TaskParallelSettings settings; - BKE_pbvh_parallel_range_settings(&settings, true, totnode); - BLI_task_parallel_range(0, totnode, &data, pbvh_load_proxyarray_exec, &settings); -} - -static void pbvh_gather_proxyarray_exec(void *__restrict userdata, - const int n, - const TaskParallelTLS *__restrict tls) -{ - GatherProxyThread *data = (GatherProxyThread *)userdata; - PBVHNode *node = data->nodes[n]; - PBVHVertexIter vd; - ProxyVertArray *p = &node->proxyverts; - int i = 0; - - int mask = p->datamask; - - BKE_pbvh_vertex_iter_begin (data->pbvh, node, vd, PBVH_ITER_UNIQUE) { - if (mask & PV_CO) { - copy_v3_v3(vd.co, p->co[i]); - } - - if (mask & PV_COLOR && vd.col) { - copy_v4_v4(vd.col, p->color[i]); - } - - if (vd.mask && (mask & PV_MASK)) { - *vd.mask = p->mask[i]; - } - - i++; - } - BKE_pbvh_vertex_iter_end; -} - -void BKE_pbvh_gather_proxyarray(PBVH *pbvh, PBVHNode **nodes, int totnode) -{ - GatherProxyThread data = {.nodes = nodes, .pbvh = pbvh}; - - TaskParallelSettings settings; - BKE_pbvh_parallel_range_settings(&settings, true, totnode); - BLI_task_parallel_range(0, totnode, &data, pbvh_gather_proxyarray_exec, &settings); -} - -void BKE_pbvh_free_proxyarray(PBVH *pbvh, PBVHNode *node) -{ - ProxyVertArray *p = &node->proxyverts; - - if (p->indexmap) { - BLI_ghash_free(p->indexmap, NULL, NULL); - } - if (p->co) - MEM_freeN(p->co); - if (p->no) - MEM_freeN(p->no); - if (p->index) - MEM_freeN(p->index); - if (p->mask) - MEM_freeN(p->mask); - if (p->ownerco) - MEM_freeN(p->ownerco); - if (p->ownerno) - MEM_freeN(p->ownerno); - if (p->ownermask) - MEM_freeN(p->ownermask); - if (p->ownercolor) - MEM_freeN(p->ownercolor); - if (p->color) - MEM_freeN(p->color); - if (p->neighbors) - MEM_freeN(p->neighbors); - - memset(p, 0, sizeof(*p)); -} - -void BKE_pbvh_update_proxyvert(PBVH *pbvh, PBVHNode *node, ProxyVertUpdateRec *rec) {} - -ProxyVertArray *BKE_pbvh_get_proxyarrays(PBVH *pbvh, PBVHNode *node) -{ - return &node->proxyverts; -} - -#endif - -PBVHNode *BKE_pbvh_get_node(PBVH *pbvh, int node) -{ - return pbvh->nodes + node; -} - -void BKE_pbvh_vert_tag_update_normal_triangulation(PBVHNode *node) -{ - node->flag |= PBVH_UpdateTris; -} - -void BKE_pbvh_vert_tag_update_normal_tri_area(PBVHNode *node) -{ - node->flag |= PBVH_UpdateTriAreas; -} - -/* must be called outside of threads */ -void BKE_pbvh_face_areas_begin(PBVH *pbvh) -{ - pbvh->face_area_i ^= 1; -} - -void BKE_pbvh_update_all_tri_areas(PBVH *pbvh) -{ - /* swap read/write face area buffers */ - pbvh->face_area_i ^= 1; - - for (int i = 0; i < pbvh->totnode; i++) { - PBVHNode *node = pbvh->nodes + i; - if (node->flag & PBVH_Leaf) { - node->flag |= PBVH_UpdateTriAreas; -#if 0 - // ensure node triangulations are valid - // so we don't end up doing it inside brush threads - BKE_pbvh_bmesh_check_tris(pbvh, node); -#endif - } - } -} - -void BKE_pbvh_check_tri_areas(PBVH *pbvh, PBVHNode *node) -{ - if (!(node->flag & PBVH_UpdateTriAreas)) { - return; - } - - if (pbvh->header.type == PBVH_BMESH) { - if (node->flag & PBVH_UpdateTris) { - BKE_pbvh_bmesh_check_tris(pbvh, node); - } - - if (!node->tribuf || !node->tribuf->tottri) { - return; - } - } - - node->flag &= ~PBVH_UpdateTriAreas; - - const int cur_i = pbvh->face_area_i ^ 1; - - switch (BKE_pbvh_type(pbvh)) { - case PBVH_FACES: { - for (int i = 0; i < (int)node->totprim; i++) { - const MLoopTri *lt = &pbvh->looptri[node->prim_indices[i]]; - - if (pbvh->hide_poly && pbvh->hide_poly[lt->poly]) { - /* Skip hidden faces. */ - continue; - } - - pbvh->face_areas[lt->poly * 2 + cur_i] = 0.0f; - } - - for (int i = 0; i < (int)node->totprim; i++) { - const MLoopTri *lt = &pbvh->looptri[node->prim_indices[i]]; - - if (pbvh->hide_poly && pbvh->hide_poly[lt->poly]) { - /* Skip hidden faces. */ - continue; - } - - float area = area_tri_v3(pbvh->vert_positions[pbvh->mloop[lt->tri[0]].v], - pbvh->vert_positions[pbvh->mloop[lt->tri[1]].v], - pbvh->vert_positions[pbvh->mloop[lt->tri[2]].v]); - - pbvh->face_areas[lt->poly * 2 + cur_i] += area; - - /* sanity check on read side of read write buffer */ - if (pbvh->face_areas[lt->poly * 2 + (cur_i ^ 1)] == 0.0f) { - pbvh->face_areas[lt->poly * 2 + (cur_i ^ 1)] = pbvh->face_areas[lt->poly * 2 + cur_i]; - } - } - break; - } - case PBVH_GRIDS: - break; - case PBVH_BMESH: { - BMFace *f; - const int cd_face_area = pbvh->cd_face_area; - - TGSET_ITER(f, node->bm_faces) - { - float *areabuf = BM_ELEM_CD_GET_VOID_P(f, cd_face_area); - areabuf[cur_i] = 0.0f; - } - TGSET_ITER_END; - - for (int i = 0; i < node->tribuf->tottri; i++) { - PBVHTri *tri = node->tribuf->tris + i; - - BMVert *v1 = (BMVert *)(node->tribuf->verts[tri->v[0]].i); - BMVert *v2 = (BMVert *)(node->tribuf->verts[tri->v[1]].i); - BMVert *v3 = (BMVert *)(node->tribuf->verts[tri->v[2]].i); - BMFace *f = (BMFace *)tri->f.i; - - float *areabuf = BM_ELEM_CD_GET_VOID_P(f, cd_face_area); - areabuf[cur_i] += area_tri_v3(v1->co, v2->co, v3->co); - } - - TGSET_ITER(f, node->bm_faces) - { - float *areabuf = BM_ELEM_CD_GET_VOID_P(f, cd_face_area); - - /* sanity check on read side of read write buffer */ - if (areabuf[cur_i ^ 1] == 0.0f) { - areabuf[cur_i ^ 1] = areabuf[cur_i]; - } - } - TGSET_ITER_END; - - break; - } - default: - break; - } -} - -static void pbvh_pmap_to_edges_add(PBVH *pbvh, - PBVHVertRef vertex, - int **r_edges, - int *r_edges_size, - bool *heap_alloc, - int e, - int p, - int *len, - int **r_polys) -{ - for (int i = 0; i < *len; i++) { - if ((*r_edges)[i] == e) { - if ((*r_polys)[i * 2 + 1] == -1) { - (*r_polys)[i * 2 + 1] = p; - } - return; - } - } - - if (*len >= *r_edges_size) { - int newsize = *len + ((*len) >> 1) + 1; - - int *r_edges_new = MEM_malloc_arrayN(newsize, sizeof(*r_edges_new), "r_edges_new"); - int *r_polys_new = MEM_malloc_arrayN(newsize * 2, sizeof(*r_polys_new), "r_polys_new"); - - memcpy((void *)r_edges_new, (void *)*r_edges, sizeof(int) * (*r_edges_size)); - memcpy((void *)r_polys_new, (void *)(*r_polys), sizeof(int) * 2 * (*r_edges_size)); - - *r_edges_size = newsize; - - if (*heap_alloc) { - MEM_freeN(*r_polys); - MEM_freeN(*r_edges); - } - - *r_edges = r_edges_new; - *r_polys = r_polys_new; - - *heap_alloc = true; - } - - (*r_polys)[*len * 2] = p; - (*r_polys)[*len * 2 + 1] = -1; - - (*r_edges)[*len] = e; - (*len)++; -} - -void BKE_pbvh_pmap_to_edges(PBVH *pbvh, - PBVHVertRef vertex, - int **r_edges, - int *r_edges_size, - bool *r_heap_alloc, - int **r_polys) -{ - MeshElemMap *map = pbvh->pmap + vertex.i; - int len = 0; - - for (int i = 0; i < map->count; i++) { - const MPoly *mp = pbvh->mpoly + map->indices[i]; - const MLoop *ml = pbvh->mloop + mp->loopstart; - - if (pbvh->hide_poly && pbvh->hide_poly[map->indices[i]]) { - /* Skip connectivity from hidden faces. */ - continue; - } - - for (int j = 0; j < mp->totloop; j++, ml++) { - if (ml->v == vertex.i) { - pbvh_pmap_to_edges_add(pbvh, - vertex, - r_edges, - r_edges_size, - r_heap_alloc, - ME_POLY_LOOP_PREV(pbvh->mloop, mp, j)->e, - map->indices[i], - &len, - r_polys); - pbvh_pmap_to_edges_add(pbvh, - vertex, - r_edges, - r_edges_size, - r_heap_alloc, - ml->e, - map->indices[i], - &len, - r_polys); - } - } - } - - *r_edges_size = len; -} - -void BKE_pbvh_set_vemap(PBVH *pbvh, MeshElemMap *vemap) -{ - pbvh->vemap = vemap; -} - -void BKE_pbvh_get_vert_face_areas(PBVH *pbvh, PBVHVertRef vertex, float *r_areas, int valence) -{ - const int cur_i = pbvh->face_area_i; - - switch (BKE_pbvh_type(pbvh)) { - case PBVH_FACES: { - int *edges = BLI_array_alloca(edges, 16); - int *polys = BLI_array_alloca(polys, 32); - bool heap_alloc = false; - int len = 16; - - BKE_pbvh_pmap_to_edges(pbvh, vertex, &edges, &len, &heap_alloc, &polys); - len = MIN2(len, valence); - - if (pbvh->vemap) { - /* sort poly references by vemap edge ordering */ - MeshElemMap *emap = pbvh->vemap + vertex.i; - - int *polys_old = BLI_array_alloca(polys, len * 2); - memcpy((void *)polys_old, (void *)polys, sizeof(int) * len * 2); - - /* note that wire edges will break this, but - should only result in incorrect weights - and isn't worth fixing */ - - for (int i = 0; i < len; i++) { - for (int j = 0; j < len; j++) { - if (emap->indices[i] == edges[j]) { - polys[i * 2] = polys_old[j * 2]; - polys[i * 2 + 1] = polys_old[j * 2 + 1]; - } - } - } - } - for (int i = 0; i < len; i++) { - r_areas[i] = pbvh->face_areas[polys[i * 2] * 2 + cur_i]; - - if (polys[i * 2 + 1] != -1) { - r_areas[i] += pbvh->face_areas[polys[i * 2 + 1] * 2 + cur_i]; - r_areas[i] *= 0.5f; - } - } - - if (heap_alloc) { - MEM_freeN(edges); - MEM_freeN(polys); - } - - break; - } - case PBVH_BMESH: { - BMVert *v = (BMVert *)vertex.i; - BMEdge *e = v->e; - - if (!e) { - for (int i = 0; i < valence; i++) { - r_areas[i] = 1.0f; - } - - return; - } - - const int cd_face_area = pbvh->cd_face_area; - int j = 0; - - do { - float w = 0.0f; - - if (!e->l) { - w = 0.0f; - } - else { - float *a1 = BM_ELEM_CD_GET_VOID_P(e->l->f, cd_face_area); - float *a2 = BM_ELEM_CD_GET_VOID_P(e->l->radial_next->f, cd_face_area); - - w += a1[cur_i] * 0.5f; - w += a2[cur_i] * 0.5f; - } - - if (j >= valence) { - printf("%s: error, corrupt edge cycle\n", __func__); - break; - } - - r_areas[j++] = w; - - e = v == e->v1 ? e->v1_disk_link.next : e->v2_disk_link.next; - } while (e != v->e); - - for (; j < valence; j++) { - r_areas[j] = 1.0f; - } - - break; - } - - case PBVH_GRIDS: { /* estimate from edge lengths */ - int index = (int)vertex.i; - - const CCGKey *key = BKE_pbvh_get_grid_key(pbvh); - const int grid_index = index / key->grid_area; - const int vertex_index = index - grid_index * key->grid_area; - - SubdivCCGCoord coord = {.grid_index = grid_index, - .x = vertex_index % key->grid_size, - .y = vertex_index / key->grid_size}; - - SubdivCCGNeighbors neighbors; - BKE_subdiv_ccg_neighbor_coords_get(pbvh->subdiv_ccg, &coord, false, &neighbors); - - float *co1 = CCG_elem_co(key, CCG_elem_offset(key, pbvh->grids[grid_index], vertex_index)); - float totw = 0.0f; - int i = 0; - - for (i = 0; i < neighbors.size; i++) { - SubdivCCGCoord *coord2 = neighbors.coords + i; - - int vertex_index2 = coord2->y * key->grid_size + coord2->x; - - float *co2 = CCG_elem_co( - key, CCG_elem_offset(key, pbvh->grids[coord2->grid_index], vertex_index2)); - float w = len_v3v3(co1, co2); - - r_areas[i] = w; - totw += w; - } - - if (neighbors.size != valence) { - printf("%s: error!\n", __func__); - } - if (totw < 0.000001f) { - for (int i = 0; i < neighbors.size; i++) { - r_areas[i] = 1.0f; - } - } - - for (; i < valence; i++) { - r_areas[i] = 1.0f; - } - - break; - } - } -} - -void BKE_pbvh_set_stroke_id(PBVH *pbvh, int stroke_id) -{ - pbvh->stroke_id = stroke_id; -} - -static void pbvh_boundaries_flag_update(PBVH *pbvh) -{ - - if (pbvh->header.bm) { - BMVert *v; - BMIter iter; - - BM_ITER_MESH (v, &iter, pbvh->header.bm, BM_VERTS_OF_MESH) { - pbvh_boundary_update_bmesh(pbvh, v); - } - } - else { - int totvert = pbvh->totvert; - - if (BKE_pbvh_type(pbvh) == PBVH_GRIDS) { - totvert = BKE_pbvh_get_grid_num_verts(pbvh); - } - - for (int i = 0; i < totvert; i++) { - pbvh->boundary_flags[i] |= SCULPT_BOUNDARY_NEEDS_UPDATE; - } - } -} - -void BKE_pbvh_set_symmetry(PBVH *pbvh, int symmetry) -{ - if (symmetry == pbvh->symmetry) { - return; - } - - pbvh->symmetry = symmetry; -} - -void BKE_pbvh_set_sculpt_verts(PBVH *pbvh, struct MSculptVert *msculptverts) -{ - pbvh->msculptverts = msculptverts; -} - -void BKE_pbvh_update_vert_boundary_grids(PBVH *pbvh, - struct SubdivCCG *subdiv_ccg, - PBVHVertRef vertex) -{ - MSculptVert *mv = pbvh->msculptverts + vertex.i; - - int *flags = pbvh->boundary_flags + vertex.i; - *flags = 0; - - /* TODO: finish this function. */ - - int index = (int)vertex.i; - - /* TODO: optimize this. We could fill #SculptVertexNeighborIter directly, - * maybe provide coordinate and mask pointers directly rather than converting - * back and forth between #CCGElem and global index. */ - const CCGKey *key = BKE_pbvh_get_grid_key(pbvh); - const int grid_index = index / key->grid_area; - const int vertex_index = index - grid_index * key->grid_area; - - SubdivCCGCoord coord = {.grid_index = grid_index, - .x = vertex_index % key->grid_size, - .y = vertex_index / key->grid_size}; - - SubdivCCGNeighbors neighbors; - BKE_subdiv_ccg_neighbor_coords_get(subdiv_ccg, &coord, false, &neighbors); - - mv->valence = neighbors.size; - mv->flag &= ~SCULPTFLAG_NEED_VALENCE; -} - -void BKE_pbvh_update_vert_boundary_faces(int *boundary_flags, - const int *face_sets, - const bool *hide_poly, - const float (*vert_positions)[3], - const MEdge *medge, - const MLoop *mloop, - const MPoly *mpoly, - MSculptVert *msculptverts, - const MeshElemMap *pmap, - PBVHVertRef vertex, - const bool *sharp_edges) -{ - MSculptVert *mv = msculptverts + vertex.i; - const MeshElemMap *vert_map = &pmap[vertex.i]; - - mv->flag &= ~SCULPTFLAG_VERT_FSET_HIDDEN; - - int last_fset = -1; - int last_fset2 = -1; - - int *flags = boundary_flags + vertex.i; - *flags = 0; - - int totsharp = 0, totseam = 0; - int visible = false; - - for (int i = 0; i < vert_map->count; i++) { - int f_i = vert_map->indices[i]; - - const MPoly *mp = mpoly + f_i; - const MLoop *ml = mloop + mp->loopstart; - int j = 0; - - for (j = 0; j < mp->totloop; j++, ml++) { - if (ml->v == (int)vertex.i) { - break; - } - } - - if (j < mp->totloop) { - const MEdge *me = medge + ml->e; - if (sharp_edges && sharp_edges[ml->e]) { - *flags |= SCULPT_BOUNDARY_SHARP_MARK; - totsharp++; - } - - if (me->flag & ME_SEAM) { - *flags |= SCULPT_BOUNDARY_SEAM; - totseam++; - } - } - - int fset = face_sets ? abs(face_sets[f_i]) : 1; - - if (!hide_poly || !hide_poly[f_i]) { - visible = true; - } - - if (i > 0 && fset != last_fset) { - *flags |= SCULPT_BOUNDARY_FACE_SET; - - if (i > 1 && last_fset2 != last_fset && last_fset != -1 && last_fset2 != -1 && fset != -1 && - last_fset2 != fset) - { - *flags |= SCULPT_CORNER_FACE_SET; - } - } - - if (i > 0 && last_fset != fset) { - last_fset2 = last_fset; - } - - last_fset = fset; - } - - if (!visible) { - mv->flag |= SCULPTFLAG_VERT_FSET_HIDDEN; - } - - if (totsharp > 2) { - *flags |= SCULPT_CORNER_SHARP_MARK; - } - - if (totseam > 2) { - *flags |= SCULPT_CORNER_SEAM; - } -} - -void BKE_pbvh_ignore_uvs_set(PBVH *pbvh, bool value) -{ - if (!!(pbvh->flags & PBVH_IGNORE_UVS) == value) { - return; // no change - } - - if (value) { - pbvh->flags |= PBVH_IGNORE_UVS; - } - else { - pbvh->flags &= ~PBVH_IGNORE_UVS; - } - - pbvh_boundaries_flag_update(pbvh); -} - -bool BKE_pbvh_cache(const struct Mesh *me, PBVH *pbvh) -{ - memset(&pbvh->cached_data, 0, sizeof(pbvh->cached_data)); - - if (pbvh->invalid) { - printf("invalid pbvh!\n"); - return false; - } - - switch (pbvh->header.type) { - case PBVH_BMESH: - if (!pbvh->header.bm) { - return false; - } - - pbvh->cached_data.bm = pbvh->header.bm; - - pbvh->cached_data.vdata = pbvh->header.bm->vdata; - pbvh->cached_data.edata = pbvh->header.bm->edata; - pbvh->cached_data.ldata = pbvh->header.bm->ldata; - pbvh->cached_data.pdata = pbvh->header.bm->pdata; - - pbvh->cached_data.totvert = pbvh->header.bm->totvert; - pbvh->cached_data.totedge = pbvh->header.bm->totedge; - pbvh->cached_data.totloop = pbvh->header.bm->totloop; - pbvh->cached_data.totpoly = pbvh->header.bm->totface; - break; - case PBVH_GRIDS: - pbvh->cached_data.vdata = me->vdata; - pbvh->cached_data.edata = me->edata; - pbvh->cached_data.ldata = me->ldata; - pbvh->cached_data.pdata = me->pdata; - - int grid_side = pbvh->gridkey.grid_size; - - pbvh->cached_data.totvert = pbvh->totgrid * grid_side * grid_side; - pbvh->cached_data.totedge = me->totedge; - pbvh->cached_data.totloop = me->totloop; - pbvh->cached_data.totpoly = pbvh->totgrid * (grid_side - 1) * (grid_side - 1); - break; - case PBVH_FACES: - pbvh->cached_data.vdata = me->vdata; - pbvh->cached_data.edata = me->edata; - pbvh->cached_data.ldata = me->ldata; - pbvh->cached_data.pdata = me->pdata; - - pbvh->cached_data.totvert = me->totvert; - pbvh->cached_data.totedge = me->totedge; - pbvh->cached_data.totloop = me->totloop; - pbvh->cached_data.totpoly = me->totpoly; - break; - } - - return true; -} - -static bool customdata_is_same(const CustomData *a, const CustomData *b) -{ - return memcmp(a, b, sizeof(CustomData)) == 0; -} - -bool BKE_pbvh_cache_is_valid(const struct Object *ob, - const struct Mesh *me, - const PBVH *pbvh, - int pbvh_type) -{ - if (pbvh->invalid) { - printf("pbvh invalid!\n"); - return false; - } - - if (pbvh->header.type != pbvh_type) { - return false; - } - - bool ok = true; - int totvert = 0, totedge = 0, totloop = 0, totpoly = 0; - const CustomData *vdata, *edata, *ldata, *pdata; - - MultiresModifierData *mmd = NULL; - - LISTBASE_FOREACH (ModifierData *, md, &ob->modifiers) { - if (md->type == eModifierType_Multires) { - mmd = (MultiresModifierData *)md; - break; - } - } - - if (mmd && (mmd->flags & eModifierMode_Realtime)) { - // return false; - } - - switch (pbvh_type) { - case PBVH_BMESH: - if (!pbvh->header.bm || pbvh->header.bm != pbvh->cached_data.bm) { - return false; - } - - totvert = pbvh->header.bm->totvert; - totedge = pbvh->header.bm->totedge; - totloop = pbvh->header.bm->totloop; - totpoly = pbvh->header.bm->totface; - - vdata = &pbvh->header.bm->vdata; - edata = &pbvh->header.bm->edata; - ldata = &pbvh->header.bm->ldata; - pdata = &pbvh->header.bm->pdata; - break; - case PBVH_FACES: - totvert = me->totvert; - totedge = me->totedge; - totloop = me->totloop; - totpoly = me->totpoly; - - vdata = &me->vdata; - edata = &me->edata; - ldata = &me->ldata; - pdata = &me->pdata; - break; - case PBVH_GRIDS: { - if (!mmd) { - return false; - } - - int grid_side = 1 + (1 << (mmd->sculptlvl - 1)); - - totvert = me->totloop * grid_side * grid_side; - totedge = me->totedge; - totloop = me->totloop; - totpoly = me->totloop * (grid_side - 1) * (grid_side - 1); - - vdata = &me->vdata; - edata = &me->edata; - ldata = &me->ldata; - pdata = &me->pdata; - break; - } - } - - ok = ok && totvert == pbvh->cached_data.totvert; - ok = ok && totedge == pbvh->cached_data.totedge; - ok = ok && totloop == pbvh->cached_data.totloop; - ok = ok && totpoly == pbvh->cached_data.totpoly; - - ok = ok && customdata_is_same(vdata, &pbvh->cached_data.vdata); - ok = ok && customdata_is_same(edata, &pbvh->cached_data.edata); - ok = ok && customdata_is_same(ldata, &pbvh->cached_data.ldata); - ok = ok && customdata_is_same(pdata, &pbvh->cached_data.pdata); - - return ok; -} - -GHash *cached_pbvhs = NULL; -static void pbvh_clear_cached_pbvhs(PBVH *exclude) -{ - PBVH **pbvhs = NULL; - BLI_array_staticdeclare(pbvhs, 8); - - GHashIterator iter; - GHASH_ITER (iter, cached_pbvhs) { - PBVH *pbvh = BLI_ghashIterator_getValue(&iter); - - if (pbvh != exclude) { - BLI_array_append(pbvhs, pbvh); - } - } - - for (int i = 0; i < BLI_array_len(pbvhs); i++) { - PBVH *pbvh = pbvhs[i]; - - if (pbvh->header.bm) { - BM_mesh_free(pbvh->header.bm); - } - - BKE_pbvh_free(pbvh); - } - - BLI_array_free(pbvhs); - BLI_ghash_clear(cached_pbvhs, MEM_freeN, NULL); -} - -void BKE_pbvh_clear_cache(PBVH *preserve) -{ - pbvh_clear_cached_pbvhs(NULL); -} - -#define PBVH_CACHE_KEY_SIZE 1024 - -static void pbvh_make_cached_key(Object *ob, char out[PBVH_CACHE_KEY_SIZE]) -{ - sprintf(out, "%s:%p", ob->id.name, G.main); -} - -void BKE_pbvh_invalidate_cache(Object *ob) -{ - Object *ob_orig = DEG_get_original_object(ob); - - char key[PBVH_CACHE_KEY_SIZE]; - pbvh_make_cached_key(ob_orig, key); -} - -PBVH *BKE_pbvh_get_or_free_cached(Object *ob, Mesh *me, PBVHType pbvh_type) -{ - Object *ob_orig = DEG_get_original_object(ob); - - char key[PBVH_CACHE_KEY_SIZE]; - pbvh_make_cached_key(ob_orig, key); - - PBVH *pbvh = BLI_ghash_lookup(cached_pbvhs, key); - - if (!pbvh) { - return NULL; - } - - if (BKE_pbvh_cache_is_valid(ob, me, pbvh, pbvh_type)) { - switch (pbvh_type) { - case PBVH_BMESH: - break; - case PBVH_FACES: - pbvh->vert_normals = BKE_mesh_vertex_normals_for_write(me); - case PBVH_GRIDS: - if (!pbvh->deformed) { - pbvh->vert_positions = BKE_mesh_vert_positions_for_write(me); - } - - pbvh->mloop = me->mloop; - pbvh->mpoly = me->mpoly; - pbvh->vdata = &me->vdata; - pbvh->ldata = &me->ldata; - pbvh->pdata = &me->pdata; - - pbvh->face_sets = (int *)CustomData_get_layer_named( - &me->pdata, CD_PROP_INT32, ".sculpt_face_set"); - - break; - } - - BKE_pbvh_update_active_vcol(pbvh, me); - - return pbvh; - } - - pbvh_clear_cached_pbvhs(NULL); - return NULL; -} - -void BKE_pbvh_set_cached(Object *ob, PBVH *pbvh) -{ - if (!pbvh) { - return; - } - - Object *ob_orig = DEG_get_original_object(ob); - - char key[PBVH_CACHE_KEY_SIZE]; - pbvh_make_cached_key(ob_orig, key); - - PBVH *exist = BLI_ghash_lookup(cached_pbvhs, key); - - if (pbvh->invalid) { - printf("pbvh invalid!"); - } - - if (exist && exist->invalid) { - printf("pbvh invalid!"); - } - - if (!exist || exist != pbvh) { - pbvh_clear_cached_pbvhs(pbvh); - - char key[PBVH_CACHE_KEY_SIZE]; - pbvh_make_cached_key(ob_orig, key); - - BLI_ghash_insert(cached_pbvhs, BLI_strdup(key), pbvh); - } - -#ifdef WITH_PBVH_CACHE - BKE_pbvh_cache(BKE_object_get_original_mesh(ob_orig), pbvh); -#endif -} - -struct SculptPMap *BKE_pbvh_get_pmap(PBVH *pbvh) -{ - return pbvh->pmap; -} - -void BKE_pbvh_set_pmap(PBVH *pbvh, SculptPMap *pmap) -{ - if (pbvh->pmap != pmap) { - BKE_pbvh_pmap_aquire(pmap); - } - - pbvh->pmap = pmap; -} - -/** Does not free pbvh itself. */ -void BKE_pbvh_cache_remove(PBVH *pbvh) -{ - char **keys = NULL; - BLI_array_staticdeclare(keys, 32); - - GHashIterator iter; - GHASH_ITER (iter, cached_pbvhs) { - PBVH *pbvh2 = BLI_ghashIterator_getValue(&iter); - - if (pbvh2 == pbvh) { - BLI_array_append(keys, (char *)BLI_ghashIterator_getKey(&iter)); - break; - } - } - - for (int i = 0; i < BLI_array_len(keys); i++) { - BLI_ghash_remove(cached_pbvhs, keys[i], MEM_freeN, NULL); - } - - BLI_array_free(keys); -} - -void BKE_pbvh_set_bmesh(PBVH *pbvh, BMesh *bm) -{ - pbvh->header.bm = bm; -} - -void BKE_pbvh_free_bmesh(PBVH *pbvh, BMesh *bm) -{ - if (pbvh) { - pbvh->header.bm = NULL; - } - - BM_mesh_free(bm); - - GHashIterator iter; - char **keys = NULL; - BLI_array_staticdeclare(keys, 32); - - PBVH **pbvhs = NULL; - BLI_array_staticdeclare(pbvhs, 8); - - GHASH_ITER (iter, cached_pbvhs) { - PBVH *pbvh2 = BLI_ghashIterator_getValue(&iter); - - if (pbvh2->header.bm == bm) { - pbvh2->header.bm = NULL; - - if (pbvh2 != pbvh) { - bool ok = true; - - for (int i = 0; i < BLI_array_len(pbvhs); i++) { - if (pbvhs[i] == pbvh2) { - ok = false; - } - } - - if (ok) { - BLI_array_append(pbvhs, pbvh2); - } - } - - BLI_array_append(keys, BLI_ghashIterator_getKey(&iter)); - } - } - - for (int i = 0; i < BLI_array_len(keys); i++) { - BLI_ghash_remove(cached_pbvhs, keys[i], MEM_freeN, NULL); - } - - for (int i = 0; i < BLI_array_len(pbvhs); i++) { - BKE_pbvh_free(pbvhs[i]); - } - - BLI_array_free(pbvhs); - BLI_array_free(keys); -} - -BMLog *BKE_pbvh_get_bm_log(PBVH *pbvh) -{ - return pbvh->bm_log; -} - -SculptPMap *BKE_pbvh_make_pmap(const struct Mesh *me) -{ - SculptPMap *pmap = MEM_callocN(sizeof(*pmap), "SculptPMap"); - - BKE_mesh_vert_poly_map_create(&pmap->pmap, - &pmap->pmap_mem, - BKE_mesh_polys(me), - BKE_mesh_loops(me), - me->totvert, - me->totpoly, - me->totloop); - - pmap->refcount = 1; - - return pmap; -} - -void BKE_pbvh_pmap_aquire(SculptPMap *pmap) -{ - pmap->refcount++; -} - -bool BKE_pbvh_pmap_release(SculptPMap *pmap) -{ - if (!pmap) { - return false; - } - - pmap->refcount--; - - // if (pmap->refcount < 0) { - // printf("%s: error!\n", __func__); - //} - - if (1 && pmap->refcount == 0) { - MEM_SAFE_FREE(pmap->pmap); - MEM_SAFE_FREE(pmap->pmap_mem); - MEM_SAFE_FREE(pmap); - - return true; - } - - return false; -} - -bool BKE_pbvh_is_drawing(const PBVH *pbvh) -{ - return pbvh->is_drawing; -} - -bool BKE_pbvh_draw_cache_invalid(const PBVH *pbvh) -{ - return pbvh->draw_cache_invalid; -} - -void BKE_pbvh_is_drawing_set(PBVH *pbvh, bool val) -{ - pbvh->is_drawing = val; -} - -void BKE_pbvh_node_num_loops(PBVH *pbvh, PBVHNode *node, int *r_totloop) -{ - UNUSED_VARS(pbvh); - BLI_assert(BKE_pbvh_type(pbvh) == PBVH_FACES); - - if (r_totloop) { - *r_totloop = node->loop_indices_num; - } -} - -void BKE_pbvh_update_active_vcol(PBVH *pbvh, const Mesh *mesh) -{ - CustomDataLayer *last_layer = pbvh->color_layer; - - Mesh me_query; - const CustomData *vdata, *ldata; - - if (pbvh->header.type == PBVH_BMESH && pbvh->header.bm) { - vdata = &pbvh->header.bm->vdata; - ldata = &pbvh->header.bm->ldata; - } - else { - vdata = &mesh->vdata; - ldata = &mesh->ldata; - } - - BKE_id_attribute_copy_domains_temp(ID_ME, vdata, NULL, ldata, NULL, NULL, &me_query.id); - me_query.active_color_attribute = mesh->active_color_attribute; - - BKE_pbvh_get_color_layer(&me_query, &pbvh->color_layer, &pbvh->color_domain); - - if (pbvh->color_layer && pbvh->header.bm) { - pbvh->cd_vcol_offset = pbvh->color_layer->offset; - } - else { - pbvh->cd_vcol_offset = -1; - } - - if (pbvh->color_layer != last_layer) { - for (int i = 0; i < pbvh->totnode; i++) { - PBVHNode *node = pbvh->nodes + i; - - if (node->flag & PBVH_Leaf) { - BKE_pbvh_node_mark_update_color(node); - } - } - } -} - -void BKE_pbvh_ensure_node_loops(PBVH *pbvh) -{ - BLI_assert(BKE_pbvh_type(pbvh) == PBVH_FACES); - - int totloop = 0; - - /* Check if nodes already have loop indices. */ - for (int i = 0; i < pbvh->totnode; i++) { - PBVHNode *node = pbvh->nodes + i; - - if (!(node->flag & PBVH_Leaf)) { - continue; - } - - if (node->loop_indices) { - return; - } - - totloop += node->totprim * 3; - } - - BLI_bitmap *visit = BLI_BITMAP_NEW(totloop, __func__); - - /* Create loop indices from node loop triangles. */ - for (int i = 0; i < pbvh->totnode; i++) { - PBVHNode *node = pbvh->nodes + i; - - if (!(node->flag & PBVH_Leaf)) { - continue; - } - - node->loop_indices = MEM_malloc_arrayN(node->totprim * 3, sizeof(int), __func__); - node->loop_indices_num = 0; - - for (int j = 0; j < (int)node->totprim; j++) { - const MLoopTri *mlt = pbvh->looptri + node->prim_indices[j]; - - for (int k = 0; k < 3; k++) { - if (!BLI_BITMAP_TEST(visit, mlt->tri[k])) { - node->loop_indices[node->loop_indices_num++] = mlt->tri[k]; - BLI_BITMAP_ENABLE(visit, mlt->tri[k]); - } - } - } - } - - MEM_SAFE_FREE(visit); -} - -bool BKE_pbvh_get_origvert( - PBVH *pbvh, PBVHVertRef vertex, const float **r_co, float **r_no, float **r_color) -{ - MSculptVert *mv; - - switch (pbvh->header.type) { - case PBVH_FACES: - case PBVH_GRIDS: - mv = pbvh->msculptverts + vertex.i; - - if (mv->stroke_id != pbvh->stroke_id) { - mv->stroke_id = pbvh->stroke_id; - float *mask = NULL; - - if (pbvh->header.type == PBVH_FACES) { - copy_v3_v3(mv->origco, pbvh->vert_positions[vertex.i]); - copy_v3_v3(mv->origno, pbvh->vert_normals[vertex.i]); - mask = (float *)CustomData_get_layer(pbvh->vdata, CD_PAINT_MASK); - - if (mask) { - mask += vertex.i; - } - } - else { - const CCGKey *key = BKE_pbvh_get_grid_key(pbvh); - const int grid_index = vertex.i / key->grid_area; - const int vertex_index = vertex.i - grid_index * key->grid_area; - CCGElem *elem = BKE_pbvh_get_grids(pbvh)[grid_index]; - - copy_v3_v3(mv->origco, CCG_elem_co(key, CCG_elem_offset(key, elem, vertex_index))); - copy_v3_v3(mv->origno, CCG_elem_no(key, CCG_elem_offset(key, elem, vertex_index))); - mask = key->has_mask ? CCG_elem_mask(key, CCG_elem_offset(key, elem, vertex_index)) : - NULL; - } - - if (mask) { - mv->origmask = (ushort)(*mask * 65535.0f); - } - - if (pbvh->color_layer) { - BKE_pbvh_vertex_color_get(pbvh, vertex, mv->origcolor); - } - } - break; - case PBVH_BMESH: { - BMVert *v = (BMVert *)vertex.i; - mv = BKE_PBVH_SCULPTVERT(pbvh->cd_sculpt_vert, v); - - if (mv->stroke_id != pbvh->stroke_id) { - mv->stroke_id = pbvh->stroke_id; - - copy_v3_v3(mv->origco, v->co); - copy_v3_v3(mv->origno, v->no); - - if (pbvh->cd_vert_mask_offset != -1) { - mv->origmask = (short)(BM_ELEM_CD_GET_FLOAT(v, pbvh->cd_vert_mask_offset) * 65535.0f); - } - - if (pbvh->cd_vcol_offset != -1) { - BKE_pbvh_vertex_color_get(pbvh, vertex, mv->origcolor); - } - } - break; - } - } - - if (r_co) { - *r_co = mv->origco; - } - - if (r_no) { - *r_no = mv->origno; - } - - if (r_color) { - *r_color = mv->origcolor; - } - - return true; -} - -int BKE_pbvh_debug_draw_gen_get(PBVHNode *node) -{ - return node->debug_draw_gen; -} - -void BKE_pbvh_set_boundary_flags(PBVH *pbvh, int *boundary_flags) -{ - pbvh->boundary_flags = boundary_flags; -} - -static void pbvh_face_iter_verts_reserve(PBVHFaceIter *fd, int verts_num) -{ - if (verts_num >= fd->verts_size_) { - fd->verts_size_ = (verts_num + 1) << 2; - - if (fd->verts != fd->verts_reserved_) { - MEM_SAFE_FREE(fd->verts); - } - - fd->verts = MEM_malloc_arrayN(fd->verts_size_, sizeof(void *), __func__); - } - - fd->verts_num = verts_num; -} - -BLI_INLINE int face_iter_prim_to_face(PBVHFaceIter *fd, int prim_index) -{ - if (fd->subdiv_ccg_) { - return BKE_subdiv_ccg_grid_to_face_index(fd->subdiv_ccg_, prim_index); - } - - return fd->looptri_[prim_index].poly; -} - -static void pbvh_face_iter_step(PBVHFaceIter *fd, bool do_step) -{ - if (do_step) { - fd->i++; - } - - switch (fd->pbvh_type_) { - case PBVH_BMESH: { - if (do_step) { - fd->bm_faces_iter_++; - - while (fd->bm_faces_iter_ < fd->bm_faces_->cur && - !fd->bm_faces_->elems[fd->bm_faces_iter_]) { - fd->bm_faces_iter_++; - } - - if (fd->bm_faces_iter_ >= fd->bm_faces_->cur) { - return; - } - } - - BMFace *f = (BMFace *)fd->bm_faces_->elems[fd->bm_faces_iter_]; - fd->face.i = (intptr_t)f; - fd->index = f->head.index; - - if (fd->cd_face_set_ != -1) { - fd->face_set = (int *)BM_ELEM_CD_GET_VOID_P(f, fd->cd_face_set_); - } - - /* TODO: BMesh doesn't yet use .hide_poly. */ - fd->hide = nullptr; - - pbvh_face_iter_verts_reserve(fd, f->len); - int vertex_i = 0; - - BMLoop *l = f->l_first; - do { - fd->verts[vertex_i++].i = (intptr_t)l->v; - } while ((l = l->next) != f->l_first); - - break; - } - case PBVH_GRIDS: - case PBVH_FACES: { - int face_index = 0; - - if (do_step) { - fd->prim_index_++; - - while (fd->prim_index_ < fd->node_->totprim) { - face_index = face_iter_prim_to_face(fd, fd->node_->prim_indices[fd->prim_index_]); - - if (face_index != fd->last_face_index_) { - break; - } - - fd->prim_index_++; - } - } - else if (fd->prim_index_ < fd->node_->totprim) { - face_index = face_iter_prim_to_face(fd, fd->node_->prim_indices[fd->prim_index_]); - } - - if (fd->prim_index_ >= fd->node_->totprim) { - return; - } - - fd->last_face_index_ = face_index; - const MPoly *mp = fd->mpoly_ + face_index; - - fd->face.i = fd->index = face_index; - - if (fd->face_sets_) { - fd->face_set = fd->face_sets_ + face_index; - } - if (fd->hide_poly_) { - fd->hide = fd->hide_poly_ + face_index; - } - - pbvh_face_iter_verts_reserve(fd, mp->totloop); - - const MLoop *ml = fd->mloop_ + mp->loopstart; - const int grid_area = fd->subdiv_key_.grid_area; - - for (int i = 0; i < mp->totloop; i++, ml++) { - if (fd->pbvh_type_ == PBVH_GRIDS) { - /* Grid corners. */ - fd->verts[i].i = (mp->loopstart + i) * grid_area + grid_area - 1; - } - else { - fd->verts[i].i = ml->v; - } - } - break; - } - } -} - -void BKE_pbvh_face_iter_step(PBVHFaceIter *fd) -{ - pbvh_face_iter_step(fd, true); -} - -void BKE_pbvh_face_iter_init(PBVH *pbvh, PBVHNode *node, PBVHFaceIter *fd) -{ - memset(fd, 0, sizeof(*fd)); - - fd->node_ = node; - fd->pbvh_type_ = BKE_pbvh_type(pbvh); - fd->verts = fd->verts_reserved_; - fd->verts_size_ = PBVH_FACE_ITER_VERTS_RESERVED; - - switch (BKE_pbvh_type(pbvh)) { - case PBVH_GRIDS: - fd->subdiv_ccg_ = pbvh->subdiv_ccg; - fd->subdiv_key_ = pbvh->gridkey; - ATTR_FALLTHROUGH; - case PBVH_FACES: - fd->mpoly_ = pbvh->mpoly; - fd->mloop_ = pbvh->mloop; - fd->looptri_ = pbvh->looptri; - fd->hide_poly_ = pbvh->hide_poly; - fd->face_sets_ = pbvh->face_sets; - fd->last_face_index_ = -1; - - break; - case PBVH_BMESH: - fd->bm = pbvh->header.bm; - fd->cd_face_set_ = CustomData_get_offset_named( - &pbvh->header.bm->pdata, CD_PROP_INT32, ".sculpt_face_set"); - - fd->bm_faces_iter_ = 0; - fd->bm_faces_ = node->bm_faces; - break; - } - - if (!BKE_pbvh_face_iter_done(fd)) { - pbvh_face_iter_step(fd, false); - } -} - -void BKE_pbvh_face_iter_finish(PBVHFaceIter *fd) -{ - if (fd->verts != fd->verts_reserved_) { - MEM_SAFE_FREE(fd->verts); - } -} - -bool BKE_pbvh_face_iter_done(PBVHFaceIter *fd) -{ - switch (fd->pbvh_type_) { - case PBVH_FACES: - case PBVH_GRIDS: - return fd->prim_index_ >= fd->node_->totprim; - case PBVH_BMESH: - return fd->bm_faces_iter_ >= fd->bm_faces_->cur; - default: - BLI_assert_unreachable(); - return true; - } -} - -void BKE_pbvh_sync_visibility_from_verts(PBVH *pbvh, Mesh *mesh) -{ - switch (pbvh->header.type) { - case PBVH_FACES: { - BKE_mesh_flush_hidden_from_verts(mesh); - BKE_pbvh_update_hide_attributes_from_mesh(pbvh); - break; - } - case PBVH_BMESH: { - BMIter iter; - BMVert *v; - BMEdge *e; - BMFace *f; - - BM_ITER_MESH (f, &iter, pbvh->header.bm, BM_FACES_OF_MESH) { - BM_elem_flag_disable(f, BM_ELEM_HIDDEN); - } - - BM_ITER_MESH (e, &iter, pbvh->header.bm, BM_EDGES_OF_MESH) { - BM_elem_flag_disable(e, BM_ELEM_HIDDEN); - } - - BM_ITER_MESH (v, &iter, pbvh->header.bm, BM_VERTS_OF_MESH) { - if (!BM_elem_flag_test(v, BM_ELEM_HIDDEN)) { - continue; - } - BMIter iter_l; - BMLoop *l; - - BM_ITER_ELEM (l, &iter_l, v, BM_LOOPS_OF_VERT) { - BM_elem_flag_enable(l->e, BM_ELEM_HIDDEN); - BM_elem_flag_enable(l->f, BM_ELEM_HIDDEN); - } - } - break; - } - case PBVH_GRIDS: { - const MPoly *mp = BKE_mesh_polys(mesh); - CCGKey key = pbvh->gridkey; - - bool *hide_poly = (bool *)CustomData_get_layer_named_for_write( - &mesh->pdata, CD_PROP_BOOL, ".hide_poly", mesh->totpoly); - - bool delete_hide_poly = true; - for (int face_index = 0; face_index < mesh->totpoly; face_index++, mp++) { - bool hidden = false; - - for (int loop_index = 0; !hidden && loop_index < mp->totloop; loop_index++) { - int grid_index = mp->loopstart + loop_index; - - if (pbvh->grid_hidden[grid_index] && - BLI_BITMAP_TEST(pbvh->grid_hidden[grid_index], key.grid_area - 1)) - { - hidden = true; - - break; - } - } - - if (hidden && !hide_poly) { - hide_poly = (bool *)CustomData_get_layer_named_for_write( - &mesh->pdata, CD_PROP_BOOL, ".hide_poly", mesh->totpoly); - - if (!hide_poly) { - CustomData_add_layer_named( - &mesh->pdata, CD_PROP_BOOL, CD_CONSTRUCT, NULL, mesh->totpoly, ".hide_poly"); - - hide_poly = (bool *)CustomData_get_layer_named_for_write( - &mesh->pdata, CD_PROP_BOOL, ".hide_poly", mesh->totpoly); - } - } - - if (hide_poly) { - delete_hide_poly = delete_hide_poly && !hidden; - hide_poly[face_index] = hidden; - } - } - - if (delete_hide_poly) { - CustomData_free_layer_named(&mesh->pdata, ".hide_poly", mesh->totpoly); - } - - BKE_mesh_flush_hidden_from_polys(mesh); - BKE_pbvh_update_hide_attributes_from_mesh(pbvh); - break; - } - } -} diff --git a/source/blender/bmesh/intern/bmesh_mesh_convert.cc b/source/blender/bmesh/intern/bmesh_mesh_convert.cc index 0e479c9e1a7..8389c8d11e1 100644 --- a/source/blender/bmesh/intern/bmesh_mesh_convert.cc +++ b/source/blender/bmesh/intern/bmesh_mesh_convert.cc @@ -170,7 +170,9 @@ static NoCopyLayerVector unmark_temp_cdlayers(CustomData *domains[4]) } } - CustomData_unmark_temporary_nocopy(data); + for (CustomDataLayer &layer : nocopy_list) { + layer.flag &= ~CD_FLAG_NOCOPY; + } } return nocopy_list; -- 2.30.2 From e02d61721220ca5f0615bedded71a394f4ddd450 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Thu, 13 Jul 2023 01:52:56 -0700 Subject: [PATCH 231/279] temp-sculpt-dyntopo: Code cleanup --- source/blender/blenkernel/BKE_pbvh_iter.hh | 272 ------------------ source/blender/blenkernel/CMakeLists.txt | 1 - source/blender/blenlib/BLI_heap_minmax.hh | 139 +-------- .../blenloader/intern/versioning_280.cc | 3 +- source/blender/bmesh/intern/bmesh_core.c | 2 +- source/blender/bmesh/intern/bmesh_marking.c | 6 +- .../bmesh/intern/bmesh_mesh_convert.cc | 32 +-- .../editors/sculpt_paint/sculpt_smooth.cc | 213 -------------- source/blender/makesdna/DNA_mesh_types.h | 2 +- source/blender/makesdna/DNA_scene_types.h | 4 +- source/blender/makesrna/intern/rna_mesh.cc | 6 - 11 files changed, 31 insertions(+), 649 deletions(-) delete mode 100644 source/blender/blenkernel/BKE_pbvh_iter.hh diff --git a/source/blender/blenkernel/BKE_pbvh_iter.hh b/source/blender/blenkernel/BKE_pbvh_iter.hh deleted file mode 100644 index b9de818f4d1..00000000000 --- a/source/blender/blenkernel/BKE_pbvh_iter.hh +++ /dev/null @@ -1,272 +0,0 @@ -/* NotForPR: This is a proposed new vertex iterator for brushes. - * It exists in this branch to debug threading problems - * (it builds a list of verts under the brush first and calls - * parallel_for on it, so it's much more efficient at allocating - * to threads--basically it lets us test if worker malallocation - * is the root cause of a performance problem). - * - * Delete this file before committing to master; a seperate PR - * will be created for this. - */ -#pragma once - -#include "BLI_array.hh" -#include "BLI_index_range.hh" -#include "BLI_math_vector_types.hh" -#include "BLI_span.hh" -#include "BLI_task.hh" -#include "BLI_threads.h" -#include "BLI_vector.hh" -#include "BLI_timeit.hh" - -#include "BKE_pbvh_api.hh" -#include "intern/pbvh_intern.hh" - -#include -#include - -namespace blender::bke::pbvh { -template struct VertexRange { - struct iterator { - PBVHVertRef vertex; - float *co, *no, *mask, *color; - const float *origco, *origno; - const float *origcolor, *origmask; - PBVHNode *node; - NodeData *userdata; - bool is_mesh; - - iterator(IndexRange _range, VertexRange &_owner, int _i) : range(_range), owner(_owner), i(_i) - { - is_mesh = owner.pbvh->header.type == PBVH_FACES; - if (_i >= owner.range.start() && _i <= owner.range.last()) { - load_data(); - } - } - - iterator(const iterator &b) : range(b.range), owner(b.owner), i(b.i) {} - - iterator &operator*() - { - return *this; - } - - bool operator==(const iterator &b) - { - return b.i == i; - } - - bool operator!=(const iterator &b) - { - return b.i != i; - } - - iterator &operator++() - { - i++; - load_data(); - return *this; - } - - void load_data() - { - if (i == range.start() + range.size()) { - return; - } - - node = owner.nodes[owner.verts[i].second]; - vertex = owner.verts[i].first; - userdata = &owner.node_data[owner.verts[i].second]; - - switch (BKE_pbvh_type(owner.pbvh)) { - case PBVH_BMESH: { - BMVert *v = reinterpret_cast(vertex.i); - - co = v->co; - no = v->no; - - mask = owner.pbvh->cd_vert_mask_offset ? - BM_ELEM_CD_PTR(v, owner.pbvh->cd_vert_mask_offset) : - nullptr; - - break; - } - case PBVH_FACES: - mask = owner.vert_mask ? owner.vert_mask + vertex.i : nullptr; - co = owner.pbvh->vert_positions[vertex.i]; - no = owner.pbvh->vert_normals[vertex.i]; - - break; - case PBVH_GRIDS: - break; - } - } - - private: - int i; - VertexRange &owner; - IndexRange range; - }; - - PBVH *pbvh; - Span> verts; - IndexRange range; - float *vert_mask; - MutableSpan node_data; - Span nodes; - - VertexRange(PBVH *_pbvh, - Span> _verts, - Span _nodes, - MutableSpan _node_data, - IndexRange _range) - : pbvh(_pbvh), verts(_verts), range(_range), nodes(_nodes), node_data(_node_data) - { - if (BKE_pbvh_type(pbvh) == PBVH_FACES) { - vert_mask = static_cast( - CustomData_get_layer_for_write(pbvh->vdata, CD_PAINT_MASK, pbvh->totvert)); - } - } - - iterator begin() - { - return iterator(range, *this, range.start()); - } - - iterator end() - { - return iterator(range, *this, range.start() + range.size()); - } -}; - -struct ForEachThreadData { - Vector *ranges; - std::function visit; -}; - -static void *foreach_thread_task(void *userdata) -{ - ForEachThreadData *data = static_cast(userdata); - for (int i : data->ranges->index_range()) { - data->visit((*data->ranges)[i]); - } - - return nullptr; -} - -template -void brush_vertex_iter( - PBVH *pbvh, - Span nodes, - bool threaded, - std::function - filter_verts, - std::function node_visit_pre, - std::function range)> visit, - std::function node_visit_post) -{ - //SCOPED_TIMER(__func__); - - int vert_count = 0; - - Array node_data(nodes.size()); - Array used_nodes(nodes.size()); - - Vector> verts; - - int vertex_i = 0; - for (int i : nodes.index_range()) { - PBVHNode *node = nodes[i]; - - bool used = false; - - PBVHVertexIter vd; - BKE_pbvh_vertex_iter_begin (pbvh, node, vd, PBVH_ITER_UNIQUE) { - if (filter_verts(vd.vertex, vd.co, vd.fno, vd.mask ? *vd.mask : 0.0f)) { - verts.append(std::make_pair(vd.vertex, i)); - used = true; - } - } - BKE_pbvh_vertex_iter_end; - - used_nodes[i] = used; - } - - //printf("threaded: %s\n", threaded ? "true" : "false"); - - if (threaded) { - threading::parallel_for_each(nodes.index_range(), [&](int i) { - // - if (used_nodes[i]) { - node_data[i] = node_visit_pre(nodes[i]); - } - }); - } - else { - for (int i : nodes.index_range()) { - if (used_nodes[i]) { - node_data[i] = node_visit_pre(nodes[i]); - } - } - } - - int thread_count = BLI_system_thread_count(); - const int grain_size = 32; //max_ii(verts.size() / thread_count / 4, 4); - //printf("verts size:%d (%d)\n", verts.size(), verts.size() / grain_size); - - if (!threaded) { - visit(VertexRange(pbvh, verts, nodes, node_data, verts.index_range())); - } - else { -#if 1 - threading::parallel_for(verts.index_range(), grain_size, [&](IndexRange range) { - visit(VertexRange(pbvh, verts, nodes, node_data, range)); - }); -#else - int threads_count = BLI_system_thread_count(); - Array> ranges(threads_count); - - int i = 0; - int j = 0; - IndexRange vrange = verts.index_range(); - printf("\n"); - while (i < verts.size()) { - IndexRange range = vrange.slice(i, min_ii(grain_size, verts.size() - i)); - - // printf("%d %d: %d\n", range.start(), range.one_after_last(), range.size()); - - ranges[j].append(range); - - // printf("%d: %d\n", i, min_ii(i + grain_size, verts.size())); - j = (j + 1) % threads_count; - i += grain_size; - } - - ListBase threads; - BLI_threadpool_init(&threads, foreach_thread_task, threads_count); - Array tdata(threads_count); - - auto visit2 = [&](IndexRange range) { - visit(VertexRange(pbvh, verts, nodes, node_data, range)); - }; - - for (int i = 0; i < threads_count; i++) { - if (ranges[i].size() == 0) { - continue; - } - - tdata[i].ranges = &ranges[i]; - tdata[i].visit = visit2; - - BLI_threadpool_insert(&threads, &tdata[i]); - } - - BLI_threadpool_end(&threads); -#endif - } - - for (PBVHNode *node : nodes) { - node_visit_post(node); - } -} -} // namespace blender::bke::pbvh diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt index 0de5f1619fb..458f6004518 100644 --- a/source/blender/blenkernel/CMakeLists.txt +++ b/source/blender/blenkernel/CMakeLists.txt @@ -465,7 +465,6 @@ set(SRC BKE_pbvh.h BKE_pbvh_api.hh BKE_pbvh_pixels.hh - BKE_pbvh_iter.hh BKE_pointcache.h BKE_pointcloud.h BKE_pose_backup.h diff --git a/source/blender/blenlib/BLI_heap_minmax.hh b/source/blender/blenlib/BLI_heap_minmax.hh index b8e96925d4a..0ebed9a221a 100644 --- a/source/blender/blenlib/BLI_heap_minmax.hh +++ b/source/blender/blenlib/BLI_heap_minmax.hh @@ -1,3 +1,6 @@ +/* SPDX-FileCopyrightText: 2023 Blender Foundation + * + * SPDX-License-Identifier: GPL-2.0-or-later */ #pragma once @@ -67,121 +70,6 @@ template class HeapValueIter { template -#if 0 -class MinMaxHeap { - struct MinMaxHeapNode { - Value value; - float weight; - - int child1 = -1, child2 = -1, parent = -1; - }; - - public: - MinMaxHeap(int reserved = 0) {} - MinMaxHeapNode *insert(float weight, Value value) - { - nodes.resize(nodes.size() + 1); - MinMaxHeapNode *node = &nodes.last(); - - node->weight = weight; - node->value = value; - - return node; - } - - MinMaxHeapNode *max() - { - MinMaxHeapNode *max_node = nullptr; - float max = FLT_MIN; - - for (MinMaxHeapNode &node : nodes) { - if (node.weight > max) { - max_node = &node; - max = node.weight; - } - } - - return max_node; - } - MinMaxHeapNode *min() - { - MinMaxHeapNode *min_node = nullptr; - float min = FLT_MAX; - - for (MinMaxHeapNode &node : nodes) { - if (node.weight < min) { - min_node = &node; - min = node.weight; - } - } - - return min_node; - } - - float min_weight() - { - return min()->weight; - } - - float max_weight() - { - return max()->weight; - } - - void pop_node(MinMaxHeapNode *node) - { - int i = node - nodes.data(); - - nodes[i] = nodes[nodes.size() - 1]; - nodes.pop_last(); - } - - Value pop_min(float *r_w = nullptr) - { - MinMaxHeapNode *node = min(); - if (r_w) { - *r_w = node->weight; - } - - Value ret = node->value; - pop_node(node); - - return ret; - } - - Value pop_max(float *r_w = nullptr) - { - MinMaxHeapNode *node = max(); - if (r_w) { - *r_w = node->weight; - } - - Value ret = node->value; - pop_node(node); - - return ret; - } - - int len() - { - return nodes.size(); - } - - bool empty() - { - return nodes.size() == 0; - } - - HeapValueIter values() - { - return HeapValueIter(nodes); - } - - private: - Vector nodes; -}; - -#else class MinMaxHeap { struct MinMaxHeapNode { Value value; @@ -277,11 +165,11 @@ class MinMaxHeap { MinMaxHeapNode *ret = heap_push_up(node); -# ifdef BLI_MINMAX_HEAP_VALIDATE +#ifdef BLI_MINMAX_HEAP_VALIDATE if (!is_valid()) { printf("invalid heap!\n"); } -# endif +#endif return ret; } @@ -327,11 +215,11 @@ class MinMaxHeap { return nodes.pop_last().value; } -# ifdef BLI_MINMAX_HEAP_VALIDATE +#ifdef BLI_MINMAX_HEAP_VALIDATE if (!is_valid()) { printf("invalid heap!\n"); } -# endif +#endif Value ret = nodes[0].value; MinMaxHeapNode last = heap_pop_last(); @@ -341,11 +229,11 @@ class MinMaxHeap { heap_push_down(&nodes[0]); -# ifdef BLI_MINMAX_HEAP_VALIDATE +#ifdef BLI_MINMAX_HEAP_VALIDATE if (!is_valid()) { printf("invalid heap!\n"); } -# endif +#endif return ret; } @@ -361,11 +249,11 @@ class MinMaxHeap { MinMaxHeapNode &node = max(); -# ifdef BLI_MINMAX_HEAP_VALIDATE +#ifdef BLI_MINMAX_HEAP_VALIDATE if (!is_valid()) { printf("invalid heap!\n"); } -# endif +#endif Value ret = node.value; if (r_w) { @@ -379,11 +267,11 @@ class MinMaxHeap { heap_push_down(&node); -# ifdef BLI_MINMAX_HEAP_VALIDATE +#ifdef BLI_MINMAX_HEAP_VALIDATE if (!is_valid()) { printf("invalid heap!\n"); } -# endif +#endif return ret; } @@ -715,5 +603,4 @@ class MinMaxHeap { Vector nodes; }; -#endif } // namespace blender diff --git a/source/blender/blenloader/intern/versioning_280.cc b/source/blender/blenloader/intern/versioning_280.cc index fda265ae416..e037bf538dc 100644 --- a/source/blender/blenloader/intern/versioning_280.cc +++ b/source/blender/blenloader/intern/versioning_280.cc @@ -4890,7 +4890,7 @@ void blo_do_versions_280(FileData *fd, Library * /*lib*/, Main *bmain) LISTBASE_FOREACH (Mesh *, me, &bmain->meshes) { me->flag &= ~(ME_FLAG_UNUSED_0 | ME_FLAG_UNUSED_1 | ME_FLAG_UNUSED_3 | ME_FLAG_UNUSED_4 | - ME_REMESH_REPROJECT_VERTEX_COLORS); + ME_FLAG_UNUSED_6 | ME_FLAG_UNUSED_7 | ME_REMESH_REPROJECT_VERTEX_COLORS); } LISTBASE_FOREACH (Material *, mat, &bmain->materials) { @@ -5877,6 +5877,7 @@ void blo_do_versions_280(FileData *fd, Library * /*lib*/, Main *bmain) LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) { ToolSettings *ts = scene->toolsettings; UnifiedPaintSettings *ups = &ts->unified_paint_settings; + ups->flag &= ~(UNIFIED_PAINT_FLAG_UNUSED_0 | UNIFIED_PAINT_FLAG_UNUSED_1); } /* Set the default render pass in the viewport to Combined. */ diff --git a/source/blender/bmesh/intern/bmesh_core.c b/source/blender/bmesh/intern/bmesh_core.c index 37a05dfdb0e..6bdf27151fe 100644 --- a/source/blender/bmesh/intern/bmesh_core.c +++ b/source/blender/bmesh/intern/bmesh_core.c @@ -402,7 +402,7 @@ BMFace *BM_face_copy( * * \note Caller needs to handle customdata. */ -static BMFace *bm_face_create__internal(BMesh *bm) +BLI_INLINE BMFace *bm_face_create__internal(BMesh *bm) { BMFace *f; diff --git a/source/blender/bmesh/intern/bmesh_marking.c b/source/blender/bmesh/intern/bmesh_marking.c index e668ace6e72..b33db7ed78d 100644 --- a/source/blender/bmesh/intern/bmesh_marking.c +++ b/source/blender/bmesh/intern/bmesh_marking.c @@ -157,16 +157,12 @@ static bool bm_vert_is_edge_select_any_other(const BMVert *v, const BMEdge *e_fi int i = 0; /* start by stepping over the current edge */ - while ((e_iter = bmesh_disk_edge_next(e_iter, v)) != e_first && i++ < 1000) { + while ((e_iter = bmesh_disk_edge_next(e_iter, v)) != e_first) { if (BM_elem_flag_test(e_iter, BM_ELEM_SELECT)) { return true; } } - if (i >= 1000) { - fprintf(stderr, "bmesh mesh error in %s\n", __func__); - } - return false; } diff --git a/source/blender/bmesh/intern/bmesh_mesh_convert.cc b/source/blender/bmesh/intern/bmesh_mesh_convert.cc index 8389c8d11e1..b47d6e22766 100644 --- a/source/blender/bmesh/intern/bmesh_mesh_convert.cc +++ b/source/blender/bmesh/intern/bmesh_mesh_convert.cc @@ -163,16 +163,13 @@ static NoCopyLayerVector unmark_temp_cdlayers(CustomData *domains[4]) for (int i = 0; i < 4; i++) { CustomData *data = domains[i]; - for (const CustomDataLayer &layer : - blender::Span(data->layers, data->totlayer)) { + for (CustomDataLayer &layer : + blender::MutableSpan(data->layers, data->totlayer)) { if ((layer.flag & CD_FLAG_TEMPORARY) && (layer.flag & CD_FLAG_NOCOPY)) { + layer.flag &= ~CD_FLAG_NOCOPY; nocopy_list.append(std::make_pair(layer, int(1 << i))); } } - - for (CustomDataLayer &layer : nocopy_list) { - layer.flag &= ~CD_FLAG_NOCOPY; - } } return nocopy_list; @@ -430,7 +427,7 @@ void BM_mesh_bm_from_me(BMesh *bm, const Mesh *me, const BMeshFromMeshParams *pa } if (tot_shape_keys) { - if (is_new || params->create_shapekey_layers) { + if (is_new) { /* Check if we need to generate unique ids for the shape-keys. * This also exists in the file reading code, but is here for a sanity check. */ if (!me->key->uidgen) { @@ -448,7 +445,7 @@ void BM_mesh_bm_from_me(BMesh *bm, const Mesh *me, const BMeshFromMeshParams *pa if (actkey && actkey->totelem == me->totvert) { keyco = params->use_shapekey ? static_cast(actkey->data) : nullptr; - if (is_new || params->create_shapekey_layers) { + if (is_new) { bm->shapenr = params->active_shapekey; } } @@ -501,21 +498,14 @@ void BM_mesh_bm_from_me(BMesh *bm, const Mesh *me, const BMeshFromMeshParams *pa CustomData_bmesh_init_pool(&bm->pdata, me->totpoly, BM_FACE); } - int *cd_shape_key_offset = static_cast( - tot_shape_keys ? MEM_mallocN(sizeof(int) * tot_shape_keys, "cd_shape_key_offset") : nullptr); - /* Only copy these values over if the source mesh is flagged to be using them. * Even if `bm` has these layers, they may have been added from another mesh, when `!is_new`. */ - -1; + const int cd_shape_key_offset = tot_shape_keys ? CustomData_get_offset(&bm->vdata, CD_SHAPEKEY) : + -1; const int cd_shape_keyindex_offset = is_new && (tot_shape_keys || params->add_key_index) ? CustomData_get_offset(&bm->vdata, CD_SHAPE_KEYINDEX) : -1; - for (int i = 0; i < tot_shape_keys; i++) { - int idx = CustomData_get_layer_index_n(&bm->vdata, CD_SHAPEKEY, i); - cd_shape_key_offset[i] = bm->vdata.layers[idx].offset; - } - const bool *select_vert = (const bool *)CustomData_get_layer_named( &me->vdata, CD_PROP_BOOL, ".select_vert"); const bool *select_edge = (const bool *)CustomData_get_layer_named( @@ -565,11 +555,9 @@ void BM_mesh_bm_from_me(BMesh *bm, const Mesh *me, const BMeshFromMeshParams *pa } /* Set shape-key data. */ - if (tot_shape_keys) { - for (int j = 0; j < tot_shape_keys; j++) { - float3 *co_dest = BM_ELEM_CD_PTR(v, cd_shape_key_offset[j]); - copy_v3_v3(*co_dest, shape_key_table[j][i]); - } + float(*co_dst)[3] = (float(*)[3])BM_ELEM_CD_GET_VOID_P(v, cd_shape_key_offset); + for (int j = 0; j < tot_shape_keys; j++, co_dst++) { + copy_v3_v3(*co_dst, shape_key_table[j][i]); } } if (is_new) { diff --git a/source/blender/editors/sculpt_paint/sculpt_smooth.cc b/source/blender/editors/sculpt_paint/sculpt_smooth.cc index fdfacff4200..82a1f7b529b 100644 --- a/source/blender/editors/sculpt_paint/sculpt_smooth.cc +++ b/source/blender/editors/sculpt_paint/sculpt_smooth.cc @@ -176,61 +176,6 @@ static void SCULPT_neighbor_coords_average_interior_ex(SculptSession *ss, } while ((l = l->radial_next) != e->l); }; -#if 0 - if (weighted && ss->bm && ss->bm->ldata.typemap[CD_PROP_FLOAT2] != -1) { - SculptVertexNeighborIter ni; - BMVert *v = reinterpret_cast(vertex.i); - int cd_uv = ss->bm->ldata.layers[ss->bm->ldata.typemap[CD_PROP_FLOAT2]].offset; - float totarea = 0.0f; - int count = 0; - float(*points)[2] = (float(*)[2])BLI_array_alloca(points, valence * 4); - float *weights = (float *)BLI_array_alloca(weights, valence * 2); - float2 v_uv = {}; - - SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vertex, ni) { - BMEdge *e = reinterpret_cast(ni.edge.i); - - BMLoop *l = e->l; - if (l->v == v) { - l = l->radial_next; - } - - BMLoop *l1 = l->v == v ? l : l->next; - BMLoop *other_l1 = l->v == v ? l->next : l; - - v_uv += *BM_ELEM_CD_PTR(l1, cd_uv); - copy_v2_v2(points[count], BM_ELEM_CD_PTR(other_l1, cd_uv)); - count++; - } - SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); - - if (count == 0) { - return; - } - - v_uv /= float(count); - interp_weights_poly_v2(weights, points, count, v_uv); - - totarea = 0.0f; - for (int i = 0; i < count; i++) { - float w = weights[i]; - w = min_ff(max_ff(w, 0.0f), 1.0f); - totarea += w; - areas[i] = w; - } - - for (int i = 0; i < count; i++) { - // break; - if (totarea != 0.0f) { - areas[i] /= totarea; - } - else { - areas[i] = 1.0f / float(count); - } - } - } -#endif - SculptVertexNeighborIter ni; SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vertex, ni) { bool project_ok; @@ -600,18 +545,6 @@ void SCULPT_bmesh_four_neighbor_average(SculptSession *ss, return; } -#if 0 - if (e->l->v == v) { - loops.append(e->l); - ws.append(w); - } - else { - loops.append(e->l->radial_next); - ws.append(w); - } - - return; -#endif BMLoop *l = e->l; l = l->v == v ? l->next : l; @@ -1110,154 +1043,9 @@ void SCULPT_smooth_undo_push(Object *ob, Span nodes) } } -#if 0 // NotForPR, see comment in BKE_pbvh_iter.hh -# include "BKE_mesh_mapping.h" -# include "BKE_pbvh_iter.hh" - void SCULPT_smooth( Sculpt *sd, Object *ob, Span nodes, float bstrength, const bool smooth_mask) { - // SCOPED_TIMER(__func__); - - SculptSession *ss = ob->sculpt; - Brush *brush = BKE_paint_brush(&sd->paint); - - PBVHType type = BKE_pbvh_type(ss->pbvh); - int iteration, count; - float last; - - SCULPT_ensure_vemap(ss); - SCULPT_boundary_info_ensure(ob); - SCULPT_smooth_undo_push(ob, nodes); - - CLAMP(bstrength, 0.0f, 1.0f); - - const int max_iterations = 4; - const float fract = 1.0f / max_iterations; - - count = int(bstrength * max_iterations); - last = max_iterations * (bstrength - count * fract); - - if (type == PBVH_FACES && ss->pmap.is_empty()) { - BLI_assert_msg(0, "sculpt smooth: pmap missing"); - return; - } - - for (iteration = 0; iteration <= count; iteration++) { - const float strength = (iteration != count) ? 1.0f : last; - - if (brush->flag2 & BRUSH_SMOOTH_USE_AREA_WEIGHT) { - BKE_pbvh_face_areas_begin(ss->pbvh); - } - - SculptBrushTest test; - SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( - ss, &test, brush->falloff_shape); - float projection = brush->autosmooth_projection; - bool weighted = brush->flag2 & BRUSH_SMOOTH_USE_AREA_WEIGHT; - const bool do_reproject = SCULPT_need_reproject(ss); - float hard_corner_pin = BKE_brush_hard_corner_pin_get(ss->scene, brush); - bool smooth_origco = SCULPT_tool_needs_smooth_origco(brush->sculpt_tool); - - struct MyNodeData { - AutomaskingNodeData automask_data; - }; - - blender::bke::pbvh::brush_vertex_iter( - ss->pbvh, - nodes, - true, - /* Filter verts. */ - [&](PBVHVertRef vertex, const float *co, const float *no, float mask) { - return bool(sculpt_brush_test_sq_fn(&test, co)); - }, - /* Visit nodes and set up thread data. */ - [&](PBVHNode *node) { - MyNodeData data; - - if (brush->flag2 & BRUSH_SMOOTH_USE_AREA_WEIGHT) { - BKE_pbvh_check_tri_areas(ss->pbvh, node); - } - - SCULPT_automasking_node_begin(ob, ss, ss->cache->automasking, &data.automask_data, node); - return data; - }, - /* Main worker. */ - [&](blender::bke::pbvh::VertexRange range) { - float bstrength = strength; - CLAMP(bstrength, 0.0f, 1.0f); - - /* Needed for SCULPT_brush_strength_factor. */ - const int thread_id = BLI_task_parallel_thread_id(nullptr); - - for (auto &vd : range) { - PBVHVertexIter dummy; - dummy.vertex = vd.vertex; - SCULPT_automasking_node_update(ss, &vd.userdata->automask_data, &dummy); - - float fade = bstrength * SCULPT_brush_strength_factor( - ss, - brush, - vd.co, - sqrtf(test.dist), - vd.no, - vd.no, - smooth_mask ? 0.0f : (vd.mask ? *vd.mask : 0.0f), - vd.vertex, - thread_id, - &vd.userdata->automask_data); - if (smooth_mask) { - float val = SCULPT_neighbor_mask_average(ss, vd.vertex) - *vd.mask; - val *= fade * bstrength; - *vd.mask += val; - CLAMP(*vd.mask, 0.0f, 1.0f); - } - else { - float oldco[3]; - float oldno[3]; - copy_v3_v3(oldco, vd.co); - SCULPT_vertex_normal_get(ss, vd.vertex, oldno); - - float avg[3], val[3]; - SCULPT_neighbor_coords_average_interior( - ss, avg, vd.vertex, projection, hard_corner_pin, weighted, false, fade); - - if (smooth_origco) { - float origco_avg[3]; - - SCULPT_neighbor_coords_average_interior( - ss, origco_avg, vd.vertex, projection, hard_corner_pin, weighted, true, fade); - - float *origco = blender::bke::paint::vertex_attr_ptr(vd.vertex, - ss->attrs.orig_co); - interp_v3_v3v3(origco, origco, origco_avg, fade); - } - - sub_v3_v3v3(val, avg, vd.co); - madd_v3_v3v3fl(val, vd.co, val, fade); - SCULPT_clip(sd, ss, vd.co, val); - - if (do_reproject) { - BKE_sculpt_reproject_cdata(ss, vd.vertex, oldco, oldno, ss->distort_correction_mode); - } - - if (vd.is_mesh) { - BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); - } - BKE_sculpt_sharp_boundary_flag_update(ss, vd.vertex); - } - } - }, - /* Visit nodes again */ - [&](PBVHNode *node) { BKE_pbvh_node_mark_update(node); }); - } -} -#else -void SCULPT_smooth( - Sculpt *sd, Object *ob, Span nodes, float bstrength, const bool smooth_mask) -{ - // SCOPED_TIMER(__func__); - SculptSession *ss = ob->sculpt; Brush *brush = BKE_paint_brush(&sd->paint); @@ -1305,7 +1093,6 @@ void SCULPT_smooth( BLI_task_parallel_range(0, nodes.size(), &data, do_smooth_brush_task_cb_ex, &settings); } } -#endif void SCULPT_do_smooth_brush(Sculpt *sd, Object *ob, Span nodes) { diff --git a/source/blender/makesdna/DNA_mesh_types.h b/source/blender/makesdna/DNA_mesh_types.h index 1f8df249844..ae647f397e9 100644 --- a/source/blender/makesdna/DNA_mesh_types.h +++ b/source/blender/makesdna/DNA_mesh_types.h @@ -408,7 +408,7 @@ enum { ME_FLAG_UNUSED_4 = 1 << 4, /* cleared */ ME_AUTOSMOOTH = 1 << 5, ME_FLAG_UNUSED_5 = 1 << 6, /* cleared */ - ME_REMESH_REPROJECT_MATERIALS = 1 << 7, + ME_FLAG_UNUSED_7 = 1 << 7, ME_REMESH_REPROJECT_VERTEX_COLORS = 1 << 8, ME_DS_EXPAND = 1 << 9, ME_SCULPT_DYNAMIC_TOPOLOGY = 1 << 10, diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h index a6f2cdb1e39..07ed95bb680 100644 --- a/source/blender/makesdna/DNA_scene_types.h +++ b/source/blender/makesdna/DNA_scene_types.h @@ -1400,7 +1400,7 @@ typedef struct UnifiedPaintSettings { float hard_corner_pin; float sharp_angle_limit; char _pad[2]; - + char distort_correction_mode; /* eAttrCorrectMode bit mask. */ char hard_edge_mode DNA_DEPRECATED; int smooth_boundary_flag; @@ -1426,6 +1426,8 @@ typedef enum { UNIFIED_PAINT_ALPHA = (1 << 1), /** Only used if unified size is enabled, mirrors the brush flag #BRUSH_LOCK_SIZE. */ UNIFIED_PAINT_BRUSH_LOCK_SIZE = (1 << 2), + UNIFIED_PAINT_FLAG_UNUSED_0 = (1 << 3), + UNIFIED_PAINT_FLAG_UNUSED_1 = (1 << 4), UNIFIED_PAINT_WEIGHT = (1 << 5), UNIFIED_PAINT_COLOR = (1 << 6), UNIFIED_PAINT_HARD_CORNER_PIN = (1 << 7), diff --git a/source/blender/makesrna/intern/rna_mesh.cc b/source/blender/makesrna/intern/rna_mesh.cc index 5632ba10cc1..30d8b1a7c24 100644 --- a/source/blender/makesrna/intern/rna_mesh.cc +++ b/source/blender/makesrna/intern/rna_mesh.cc @@ -3303,12 +3303,6 @@ static void rna_def_mesh(BlenderRNA *brna) RNA_def_property_update(prop, 0, "rna_Mesh_update_draw"); RNA_def_property_flag(prop, PROP_NO_DEG_UPDATE); - prop = RNA_def_property(srna, "use_remesh_preserve_materials", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", ME_REMESH_REPROJECT_MATERIALS); - RNA_def_property_boolean_default(prop, false); - RNA_def_property_ui_text(prop, "Preserve Materials", "Keep the current material slots"); - RNA_def_property_update(prop, 0, "rna_Mesh_update_draw"); - prop = RNA_def_property(srna, "remesh_mode", PROP_ENUM, PROP_NONE); RNA_def_property_enum_sdna(prop, nullptr, "remesh_mode"); RNA_def_property_enum_items(prop, rna_enum_mesh_remesh_mode_items); -- 2.30.2 From e53c1fb7da0b143a019ea0d474fbb2228a08d41c Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Thu, 13 Jul 2023 02:06:24 -0700 Subject: [PATCH 232/279] temp-sculpt-dyntopo: Remove extra shapekey code in bmesh conversion We probably do need it, but I'm not sure it survived all the repeated merging so I'm going to do it again. --- source/blender/blenkernel/intern/paint.cc | 2 -- .../bmesh/intern/bmesh_mesh_convert.cc | 21 +++++++------------ .../blender/bmesh/intern/bmesh_mesh_convert.h | 8 ++----- .../editors/sculpt_paint/sculpt_dyntopo.cc | 1 - .../editors/sculpt_paint/sculpt_undo.cc | 1 - 5 files changed, 10 insertions(+), 23 deletions(-) diff --git a/source/blender/blenkernel/intern/paint.cc b/source/blender/blenkernel/intern/paint.cc index 4e337e623b2..57f9a8b1517 100644 --- a/source/blender/blenkernel/intern/paint.cc +++ b/source/blender/blenkernel/intern/paint.cc @@ -2917,8 +2917,6 @@ PBVH *BKE_sculpt_object_pbvh_ensure(Depsgraph *depsgraph, Object *ob) params.calc_face_normal = true; params.use_shapekey = true; params.active_shapekey = ob->shapenr; - params.create_shapekey_layers = true; - params.ignore_id_layers = false; params.copy_temp_cdlayers = true; BM_mesh_bm_from_me(bm, mesh_orig, ¶ms); diff --git a/source/blender/bmesh/intern/bmesh_mesh_convert.cc b/source/blender/bmesh/intern/bmesh_mesh_convert.cc index b47d6e22766..f894a8bb0df 100644 --- a/source/blender/bmesh/intern/bmesh_mesh_convert.cc +++ b/source/blender/bmesh/intern/bmesh_mesh_convert.cc @@ -404,9 +404,9 @@ void BM_mesh_bm_from_me(BMesh *bm, const Mesh *me, const BMeshFromMeshParams *pa * At the moment it's simplest to assume all original meshes use the key-block and meshes * that are evaluated (through the modifier stack for example) use custom-data layers. */ - BLI_assert(!CustomData_has_layer(&mesh_vdata, CD_SHAPEKEY)); + BLI_assert(!CustomData_has_layer(&me->vdata, CD_SHAPEKEY)); } - if (is_new == false && CustomData_has_layer(&bm->vdata, CD_SHAPEKEY)) { + if (is_new == false) { tot_shape_keys = min_ii(tot_shape_keys, CustomData_number_of_layers(&bm->vdata, CD_SHAPEKEY)); } const float(**shape_key_table)[3] = tot_shape_keys ? (const float(**)[3])BLI_array_alloca( @@ -461,14 +461,7 @@ void BM_mesh_bm_from_me(BMesh *bm, const Mesh *me, const BMeshFromMeshParams *pa int j = CustomData_get_layer_index_n(&bm->vdata, CD_SHAPEKEY, i); bm->vdata.layers[j].uid = block->uid; } - else { - BM_data_layer_add_named(bm, &bm->vdata, CD_SHAPEKEY, block->name); - } - - int j = CustomData_get_layer_index_n(&bm->vdata, CD_SHAPEKEY, i); - - bm->vdata.layers[j].uid = block->uid; - shape_key_table[i] = (const float(*)[3])block->data; + shape_key_table[i] = static_cast(block->data); } } @@ -555,9 +548,11 @@ void BM_mesh_bm_from_me(BMesh *bm, const Mesh *me, const BMeshFromMeshParams *pa } /* Set shape-key data. */ - float(*co_dst)[3] = (float(*)[3])BM_ELEM_CD_GET_VOID_P(v, cd_shape_key_offset); - for (int j = 0; j < tot_shape_keys; j++, co_dst++) { - copy_v3_v3(*co_dst, shape_key_table[j][i]); + if (tot_shape_keys) { + float(*co_dst)[3] = (float(*)[3])BM_ELEM_CD_GET_VOID_P(v, cd_shape_key_offset); + for (int j = 0; j < tot_shape_keys; j++, co_dst++) { + copy_v3_v3(*co_dst, shape_key_table[j][i]); + } } } if (is_new) { diff --git a/source/blender/bmesh/intern/bmesh_mesh_convert.h b/source/blender/bmesh/intern/bmesh_mesh_convert.h index 53691ae0a96..7cb9d91cd4c 100644 --- a/source/blender/bmesh/intern/bmesh_mesh_convert.h +++ b/source/blender/bmesh/intern/bmesh_mesh_convert.h @@ -29,9 +29,6 @@ struct Main; struct Mesh; struct BMeshFromMeshParams { - /* automatically create shapekey layers */ - uint create_shapekey_layers; - bool calc_face_normal; bool calc_vert_normal; @@ -42,8 +39,7 @@ struct BMeshFromMeshParams { /* define the active shape key (index + 1) */ int active_shapekey; struct CustomData_MeshMasks cd_mask_extra; - uint copy_temp_cdlayers : 1; - uint ignore_id_layers : 1; + uint copy_temp_cdlayers; }; struct Object; @@ -79,7 +75,7 @@ struct BMeshToMeshParams { */ bool active_shapekey_to_mvert; struct CustomData_MeshMasks cd_mask_extra; - uint copy_temp_cdlayers : 1; + uint copy_temp_cdlayers; }; void BM_enter_multires_space(struct Object *ob, struct BMesh *bm, int space); diff --git a/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc b/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc index 1ea4cb92824..d92418ac7ab 100644 --- a/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc +++ b/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc @@ -162,7 +162,6 @@ void SCULPT_dynamic_topology_enable_ex(Main *bmain, BMeshFromMeshParams params = {}; params.use_shapekey = true; - params.create_shapekey_layers = true; params.active_shapekey = ob->shapenr; BM_mesh_bm_from_me(ss->bm, me, ¶ms); diff --git a/source/blender/editors/sculpt_paint/sculpt_undo.cc b/source/blender/editors/sculpt_paint/sculpt_undo.cc index ee9d3f508ea..03d3a8f8b88 100644 --- a/source/blender/editors/sculpt_paint/sculpt_undo.cc +++ b/source/blender/editors/sculpt_paint/sculpt_undo.cc @@ -1059,7 +1059,6 @@ static void sculpt_undo_bmesh_enable(Object *ob, SculptUndoNode *unode, bool is_ BMeshFromMeshParams params = {0}; params.use_shapekey = true; - params.create_shapekey_layers = true; params.active_shapekey = ob->shapenr; BM_mesh_bm_from_me(ss->bm, me, ¶ms); -- 2.30.2 From b6ba93f41d709eede8c836b5ae3c782cc873e134 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Thu, 13 Jul 2023 08:31:33 -0700 Subject: [PATCH 233/279] temp-sculpt-dyntopo: Add back function declaration deleted in merge --- source/blender/blenkernel/BKE_customdata.h | 1 + 1 file changed, 1 insertion(+) diff --git a/source/blender/blenkernel/BKE_customdata.h b/source/blender/blenkernel/BKE_customdata.h index f486087db0f..a3426e4fced 100644 --- a/source/blender/blenkernel/BKE_customdata.h +++ b/source/blender/blenkernel/BKE_customdata.h @@ -827,6 +827,7 @@ void CustomData_blend_write(BlendWriter *writer, void CustomData_blend_read(struct BlendDataReader *reader, struct CustomData *data, int count); +size_t CustomData_get_elem_size(const struct CustomDataLayer *layer); void CustomData_regen_active_refs(CustomData *data); #ifndef NDEBUG -- 2.30.2 From 6a6c8cd7b2ea02a949af86de6771aec27460102f Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Thu, 13 Jul 2023 09:12:39 -0700 Subject: [PATCH 234/279] temp-sculpt-dyntopo: Fix crash --- source/blender/bmesh/intern/bmesh_mesh_convert.cc | 2 -- 1 file changed, 2 deletions(-) diff --git a/source/blender/bmesh/intern/bmesh_mesh_convert.cc b/source/blender/bmesh/intern/bmesh_mesh_convert.cc index f894a8bb0df..87c55822098 100644 --- a/source/blender/bmesh/intern/bmesh_mesh_convert.cc +++ b/source/blender/bmesh/intern/bmesh_mesh_convert.cc @@ -698,8 +698,6 @@ void BM_mesh_bm_from_me(BMesh *bm, const Mesh *me, const BMeshFromMeshParams *pa restore_cd_copy_flags(mesh_domains, nocopy_layers); restore_cd_copy_flags(bmesh_domains, nocopy_layers); } - - MEM_SAFE_FREE(cd_shape_key_offset); } /** -- 2.30.2 From 25e796ec25f605550ca193b643956638fd530a13 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Thu, 13 Jul 2023 10:01:40 -0700 Subject: [PATCH 235/279] temp-sculpt-dyntopo: Fix crash --- source/blender/blenkernel/intern/paint.cc | 12 +++++++----- .../blender/blenloader/intern/versioning_defaults.cc | 2 +- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/source/blender/blenkernel/intern/paint.cc b/source/blender/blenkernel/intern/paint.cc index 57f9a8b1517..7a682c6e46c 100644 --- a/source/blender/blenkernel/intern/paint.cc +++ b/source/blender/blenkernel/intern/paint.cc @@ -4782,15 +4782,17 @@ static void interp_face_corners_intern(PBVH *pbvh, CustomDataLayer *layer) { Vector corners; - Vector new_values; + Array new_values(ls.size()); /* Build (semantic) corner tags. */ for (BMLoop *l : ls) { - /* Do not calculate the corner state here, use stored corner flag. */ - bool corner = false; + /* Do not calculate the corner state here, use stored corner flag. + * + * The corner would normally be calculated like so: + * corner = loop_is_corner(l, layer->offset); + */ + bool corner = BM_ELEM_CD_GET_INT(v, cd_vert_boundary) & SCULPT_CORNER_UV; - /* corner |= loop_is_corner(l, layer->offset); */ - corner |= BM_ELEM_CD_GET_INT(v, cd_vert_boundary) & SCULPT_CORNER_UV; corners.append(corner); } diff --git a/source/blender/blenloader/intern/versioning_defaults.cc b/source/blender/blenloader/intern/versioning_defaults.cc index 34fe4db71f4..2bb6cf7b473 100644 --- a/source/blender/blenloader/intern/versioning_defaults.cc +++ b/source/blender/blenloader/intern/versioning_defaults.cc @@ -24,6 +24,7 @@ #include "DNA_camera_types.h" #include "DNA_curveprofile_types.h" +#include "DNA_defaults.h" #include "DNA_gpencil_legacy_types.h" #include "DNA_light_types.h" #include "DNA_mask_types.h" @@ -781,7 +782,6 @@ void BLO_update_defaults_startup_blend(Main *bmain, const char *app_template) brush->sculpt_tool = SCULPT_TOOL_DISPLACEMENT_SMEAR; } - /* Use the same tool icon color in the brush cursor */ LISTBASE_FOREACH (Brush *, brush, &bmain->brushes) { if (brush->ob_mode & OB_MODE_SCULPT) { BLI_assert(brush->sculpt_tool != 0); -- 2.30.2 From 50e48248b598cb7bddec4c47d871d474af4c7979 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Thu, 13 Jul 2023 10:51:08 -0700 Subject: [PATCH 236/279] temp-sculpt-dyntopo: pre-merge commit --- source/blender/blenkernel/intern/paint.cc | 8 -------- .../blenloader/intern/versioning_400.cc | 18 ++++++++++++++++++ .../blenloader/intern/versioning_defaults.cc | 2 +- source/blender/makesdna/DNA_scene_defaults.h | 3 +++ 4 files changed, 22 insertions(+), 9 deletions(-) diff --git a/source/blender/blenkernel/intern/paint.cc b/source/blender/blenkernel/intern/paint.cc index 7a682c6e46c..039bb321fe3 100644 --- a/source/blender/blenkernel/intern/paint.cc +++ b/source/blender/blenkernel/intern/paint.cc @@ -2593,14 +2593,6 @@ void BKE_sculpt_toolsettings_data_ensure(Scene *scene) Sculpt *sd = scene->toolsettings->sculpt; - if (!sd->automasking_start_normal_limit) { - sd->automasking_start_normal_limit = 20.0f / 180.0f * M_PI; - sd->automasking_start_normal_falloff = 0.25f; - - sd->automasking_view_normal_limit = 90.0f / 180.0f * M_PI; - sd->automasking_view_normal_falloff = 0.25f; - } - /* Set sane default tiling offsets. */ if (!sd->paint.tile_offset[0]) { sd->paint.tile_offset[0] = 1.0f; diff --git a/source/blender/blenloader/intern/versioning_400.cc b/source/blender/blenloader/intern/versioning_400.cc index b3103d036ab..6901a7fa770 100644 --- a/source/blender/blenloader/intern/versioning_400.cc +++ b/source/blender/blenloader/intern/versioning_400.cc @@ -337,6 +337,24 @@ void blo_do_versions_400(FileData *fd, Library * /*lib*/, Main *bmain) } } + if (!DNA_struct_elem_find(fd->filesdna, "Sculpt", "float", "automasking_start_normal_limit")) { + const Sculpt *defaults = DNA_struct_default_get(Sculpt); + + LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) { + if (!scene->toolsettings || !scene->toolsettings->sculpt) { + continue; + } + + Sculpt *sd = scene->toolsettings->sculpt; + + sd->automasking_start_normal_limit = defaults->automasking_start_normal_limit; + sd->automasking_start_normal_falloff = defaults->automasking_start_normal_falloff; + + sd->automasking_view_normal_limit = defaults->automasking_view_normal_limit; + sd->automasking_view_normal_falloff = defaults->automasking_view_normal_limit; + } + } + /** * Versioning code until next subversion bump goes here. * diff --git a/source/blender/blenloader/intern/versioning_defaults.cc b/source/blender/blenloader/intern/versioning_defaults.cc index 2bb6cf7b473..d4bdcaaf748 100644 --- a/source/blender/blenloader/intern/versioning_defaults.cc +++ b/source/blender/blenloader/intern/versioning_defaults.cc @@ -349,7 +349,7 @@ static void blo_update_defaults_scene(Main *bmain, Scene *scene) } if (ts->sculpt) { - ts->sculpt->paint.symmetry_flags |= PAINT_SYMMETRY_FEATHER; + *ts->sculpt = *DNA_struct_default_get(Sculpt); } /* Correct default startup UVs. */ diff --git a/source/blender/makesdna/DNA_scene_defaults.h b/source/blender/makesdna/DNA_scene_defaults.h index 805763a75cd..18d4930d798 100644 --- a/source/blender/makesdna/DNA_scene_defaults.h +++ b/source/blender/makesdna/DNA_scene_defaults.h @@ -389,6 +389,9 @@ .automasking_view_normal_falloff = 0.25f, \ .flags = SCULPT_DYNTOPO_ENABLED,\ .dyntopo = _DNA_DEFAULT_DynTopoSettings,\ + .paint = {\ + .symmetry_flags = PAINT_SYMMETRY_FEATHER,\ + }\ }\ /* clang-format off */ -- 2.30.2 From 8d1701ac6c29ea118525f0fba3472f655a169f92 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Mon, 17 Jul 2023 09:52:28 -0700 Subject: [PATCH 237/279] temp-sculpt-dyntopo: revert build system change --- build_files/cmake/macros.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build_files/cmake/macros.cmake b/build_files/cmake/macros.cmake index d13eb2fa947..607456b3815 100644 --- a/build_files/cmake/macros.cmake +++ b/build_files/cmake/macros.cmake @@ -1178,7 +1178,7 @@ function(msgfmt_simple add_custom_command( OUTPUT ${_file_to} COMMAND ${CMAKE_COMMAND} -E make_directory ${_file_to_path} - COMMAND ${CMAKE_COMMAND} -E env "$" ${_file_from} ${_file_to} + COMMAND ${CMAKE_COMMAND} -E env ${PLATFORM_ENV_BUILD} "$" ${_file_from} ${_file_to} DEPENDS msgfmt ${_file_from}) set_source_files_properties(${_file_to} PROPERTIES GENERATED TRUE) -- 2.30.2 From dc0d3a2cba9740652882423383150a776293269d Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Mon, 17 Jul 2023 09:55:54 -0700 Subject: [PATCH 238/279] temp-sculpt-dyntopo: Revert more build system changes --- source/blender/blenlib/CMakeLists.txt | 1 - .../geometry/nodes/node_geo_mesh_primitive_ico_sphere.cc | 1 - source/blender/nodes/shader/node_shader_util.cc | 8 +++----- source/creator/CMakeLists.txt | 1 - source/creator/creator.c | 3 +-- 5 files changed, 4 insertions(+), 10 deletions(-) diff --git a/source/blender/blenlib/CMakeLists.txt b/source/blender/blenlib/CMakeLists.txt index 9cc8522916f..ba94d59e79d 100644 --- a/source/blender/blenlib/CMakeLists.txt +++ b/source/blender/blenlib/CMakeLists.txt @@ -139,7 +139,6 @@ set(SRC intern/string_search.cc intern/string_utf8.c intern/string_utils.c - #intern/string_regex.cc intern/system.c intern/task_graph.cc intern/task_iterator.c diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_ico_sphere.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_ico_sphere.cc index 4bc36d82be3..e068a66d014 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_ico_sphere.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_ico_sphere.cc @@ -89,7 +89,6 @@ static Mesh *create_ico_sphere_mesh(const int subdivisions, Mesh *mesh = reinterpret_cast(BKE_id_new_nomain(ID_ME, nullptr)); BKE_id_material_eval_ensure_default_slot(&mesh->id); BM_mesh_bm_to_me(nullptr, bm, mesh, ¶ms); - BM_mesh_free(bm); /* The code above generates a "UVMap" attribute. The code below renames that attribute, we don't diff --git a/source/blender/nodes/shader/node_shader_util.cc b/source/blender/nodes/shader/node_shader_util.cc index 76cd0c5f9dc..a0247d17ec8 100644 --- a/source/blender/nodes/shader/node_shader_util.cc +++ b/source/blender/nodes/shader/node_shader_util.cc @@ -187,11 +187,9 @@ void node_gpu_stack_from_data(GPUNodeStack *gs, int type, bNodeStack *ns) void node_data_from_gpu_stack(bNodeStack *ns, GPUNodeStack *gs) { - if (ns) { - copy_v4_v4(ns->vec, gs->vec); - ns->data = gs->link; - ns->sockettype = gs->sockettype; - } + copy_v4_v4(ns->vec, gs->vec); + ns->data = gs->link; + ns->sockettype = gs->sockettype; } static void gpu_stack_from_data_list(GPUNodeStack *gs, ListBase *sockets, bNodeStack **ns) diff --git a/source/creator/CMakeLists.txt b/source/creator/CMakeLists.txt index 01a1fee657c..065ca416f8d 100644 --- a/source/creator/CMakeLists.txt +++ b/source/creator/CMakeLists.txt @@ -3,7 +3,6 @@ # SPDX-License-Identifier: GPL-2.0-or-later set(INC - ../../intern/atomic ../../intern/clog ../blender/depsgraph ../blender/editors/include diff --git a/source/creator/creator.c b/source/creator/creator.c index 38ab9f6f164..58716971174 100644 --- a/source/creator/creator.c +++ b/source/creator/creator.c @@ -270,7 +270,6 @@ void gmp_blender_init_allocator() * - run #WM_main() event loop, * or exit immediately when running in background-mode. */ - int main(int argc, #ifdef USE_WIN32_UNICODE_ARGS const char **UNUSED(argv_c) @@ -324,7 +323,7 @@ int main(int argc, LocalFree(argv_16); /* free on early-exit */ - app_init_data.argv = (const char **)argv; + app_init_data.argv = argv; app_init_data.argv_num = argv_num; } # endif /* USE_WIN32_UNICODE_ARGS */ -- 2.30.2 From 5e9da50bc5d5cfb7660a8a2753b8fdaf6ee434ea Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Wed, 19 Jul 2023 13:19:37 -0700 Subject: [PATCH 239/279] temp-sculpt-dyntopo: Fix snake hook spacing bug Brush spacing didn't work for snake hook, which instead relied on INBETWEEN_MOUSEMOVE to throttle mousemove events. Since snake hook is an incremental tool this is not correct and led to frustratingly inconsistent behavior. Brush spacing now works with snake hook. --- source/blender/editors/sculpt_paint/paint_stroke.cc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/source/blender/editors/sculpt_paint/paint_stroke.cc b/source/blender/editors/sculpt_paint/paint_stroke.cc index 1ac1fe08e3e..e74f42806c8 100644 --- a/source/blender/editors/sculpt_paint/paint_stroke.cc +++ b/source/blender/editors/sculpt_paint/paint_stroke.cc @@ -266,7 +266,6 @@ static bool paint_tool_require_inbetween_mouse_events(Brush *brush, ePaintMode m SCULPT_TOOL_GRAB, SCULPT_TOOL_ROTATE, SCULPT_TOOL_THUMB, - SCULPT_TOOL_SNAKE_HOOK, SCULPT_TOOL_ELASTIC_DEFORM, SCULPT_TOOL_CLOTH, SCULPT_TOOL_BOUNDARY, @@ -1084,7 +1083,7 @@ bool paint_supports_dynamic_size(Brush *br, ePaintMode mode) switch (mode) { case PAINT_MODE_SCULPT: - if (sculpt_is_grab_tool(br)) { + if (sculpt_is_grab_tool(br) && br->sculpt_tool != SCULPT_TOOL_SNAKE_HOOK) { return false; } break; -- 2.30.2 From df7c991f10d0a48a65f0cdfebe4dd71c4f80dc77 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Fri, 21 Jul 2023 18:23:14 -0700 Subject: [PATCH 240/279] temp-sculpt-dyntopo: Fix undo bugs --- source/blender/blenkernel/intern/dyntopo.cc | 15 -------------- source/blender/editors/sculpt_paint/sculpt.cc | 16 ++++++++++----- .../sculpt_paint/sculpt_brush_types.cc | 5 +++-- .../editors/sculpt_paint/sculpt_intern.hh | 6 ++++-- .../editors/sculpt_paint/sculpt_smooth.cc | 20 ++++++++++++++----- .../editors/sculpt_paint/sculpt_undo.cc | 6 +++--- 6 files changed, 36 insertions(+), 32 deletions(-) diff --git a/source/blender/blenkernel/intern/dyntopo.cc b/source/blender/blenkernel/intern/dyntopo.cc index e0a0565c189..3e80c1d9225 100644 --- a/source/blender/blenkernel/intern/dyntopo.cc +++ b/source/blender/blenkernel/intern/dyntopo.cc @@ -405,21 +405,6 @@ void EdgeQueueContext::insert_val34_vert(BMVert *v) } used_verts.append(v); - - if (!ignore_loop_data) { - BMEdge *e = v->e; - do { - BMLoop *l = e->l; - if (!l) { - continue; - } - - BMLoop *l2 = l; - do { - BM_log_face_if_modified(bm, pbvh->bm_log, l2->f); - } while ((l2 = l2->radial_next) != e->l); - } while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e); - } } /* diff --git a/source/blender/editors/sculpt_paint/sculpt.cc b/source/blender/editors/sculpt_paint/sculpt.cc index c1253499e26..4aea7316915 100644 --- a/source/blender/editors/sculpt_paint/sculpt.cc +++ b/source/blender/editors/sculpt_paint/sculpt.cc @@ -1788,9 +1788,12 @@ static void sculpt_rake_data_update(SculptRakeData *srd, const float co[3]) /** \name Sculpt Dynamic Topology * \{ */ -bool SCULPT_stroke_is_dynamic_topology(const SculptSession *ss, const Brush *brush) +bool SCULPT_stroke_is_dynamic_topology(const SculptSession *ss, + const Sculpt *sd, + const Brush *brush) { - return ((BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) && SCULPT_TOOL_HAS_DYNTOPO(brush->sculpt_tool)); + return ((BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) && SCULPT_TOOL_HAS_DYNTOPO(brush->sculpt_tool) && + !(brush->dyntopo.flag & DYNTOPO_DISABLED) && (sd->flags & SCULPT_DYNTOPO_ENABLED)); } /** \} */ @@ -6446,7 +6449,7 @@ static void sculpt_stroke_update_step(bContext *C, float dyntopo_spacing = float(ss->cached_dyntopo.spacing) / 50.0f; - bool do_dyntopo = SCULPT_stroke_is_dynamic_topology(ss, brush); + bool do_dyntopo = SCULPT_stroke_is_dynamic_topology(ss, sd, brush); bool has_spacing = !(brush->flag & BRUSH_ANCHORED); if (has_spacing && dyntopo_spacing > 0.0f) { @@ -6682,8 +6685,11 @@ static void sculpt_brush_stroke_cancel(bContext *C, wmOperator *op) const Brush *brush = BKE_paint_brush(&sd->paint); /* XXX Canceling strokes that way does not work with dynamic topology, - * user will have to do real undo for now. See #46456. */ - if (ss->cache && !SCULPT_stroke_is_dynamic_topology(ss, brush)) { + * user will have to do real undo for now. See #46456. + * + * Update: this may actually work now. Test. + */ + if (ss->cache && !SCULPT_stroke_is_dynamic_topology(ss, sd, brush)) { paint_mesh_restore_co(sd, ob); } diff --git a/source/blender/editors/sculpt_paint/sculpt_brush_types.cc b/source/blender/editors/sculpt_paint/sculpt_brush_types.cc index 768e62b5b9f..355879ed813 100644 --- a/source/blender/editors/sculpt_paint/sculpt_brush_types.cc +++ b/source/blender/editors/sculpt_paint/sculpt_brush_types.cc @@ -1059,7 +1059,8 @@ static void do_clay_strips_brush_task_cb_ex(void *__restrict userdata, data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]); BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { - if (!SCULPT_brush_test_cube(&test, vd.co, mat, brush->tip_roundness, brush->tip_scale_x, true)) { + if (!SCULPT_brush_test_cube(&test, vd.co, mat, brush->tip_roundness, brush->tip_scale_x, true)) + { continue; } @@ -3029,7 +3030,7 @@ void SCULPT_bmesh_topology_rake(Sculpt *sd, Object *ob, Span nodes, SculptSession *ss = ob->sculpt; const float strength = clamp_f(bstrength, 0.0f, 1.0f); - SCULPT_smooth_undo_push(ob, nodes); + SCULPT_smooth_undo_push(sd, ob, nodes, brush); /* Interactions increase both strength and quality. */ const int iterations = 1; diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.hh b/source/blender/editors/sculpt_paint/sculpt_intern.hh index 1f872fbb836..ea40eb977ee 100644 --- a/source/blender/editors/sculpt_paint/sculpt_intern.hh +++ b/source/blender/editors/sculpt_paint/sculpt_intern.hh @@ -1704,7 +1704,9 @@ void sculpt_dynamic_topology_disable_with_undo(Main *bmain, * Others, like smooth, are better without. * Same goes for alt-key smoothing. */ -bool SCULPT_stroke_is_dynamic_topology(const SculptSession *ss, const Brush *brush); +bool SCULPT_stroke_is_dynamic_topology(const SculptSession *ss, + const Sculpt *sd, + const Brush *brush); void SCULPT_dynamic_topology_triangulate(struct SculptSession *ss, struct BMesh *bm); @@ -1939,7 +1941,7 @@ BLI_INLINE eAttrCorrectMode SCULPT_need_reproject(const SculptSession *ss) int SCULPT_vertex_island_get(SculptSession *ss, PBVHVertRef vertex); /** \} */ -void SCULPT_smooth_undo_push(Object *ob, Span nodes); +void SCULPT_smooth_undo_push(Sculpt *sd, Object *ob, Span nodes, Brush *brush); void SCULPT_smooth( Sculpt *sd, Object *ob, Span nodes, float bstrength, const bool smooth_mask); diff --git a/source/blender/editors/sculpt_paint/sculpt_smooth.cc b/source/blender/editors/sculpt_paint/sculpt_smooth.cc index 82a1f7b529b..0a909f92f96 100644 --- a/source/blender/editors/sculpt_paint/sculpt_smooth.cc +++ b/source/blender/editors/sculpt_paint/sculpt_smooth.cc @@ -882,7 +882,7 @@ static void SCULPT_enhance_details_brush(Sculpt *sd, Object *ob, Spansculpt; Brush *brush = BKE_paint_brush(&sd->paint); - SCULPT_smooth_undo_push(ob, nodes); + SCULPT_smooth_undo_push(sd, ob, nodes, brush); SCULPT_vertex_random_access_ensure(ss); SCULPT_boundary_info_ensure(ob); @@ -1028,15 +1028,25 @@ static void do_smooth_brush_task_cb_ex(void *__restrict userdata, } } -void SCULPT_smooth_undo_push(Object *ob, Span nodes) +void SCULPT_smooth_undo_push(Sculpt *sd, Object *ob, Span nodes, Brush *brush) { SculptSession *ss = ob->sculpt; if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH && SCULPT_need_reproject(ss)) { + bool have_dyntopo = SCULPT_stroke_is_dynamic_topology(ss, sd, brush); + for (PBVHNode *node : nodes) { PBVHFaceIter fd; + BKE_pbvh_face_iter_begin (ss->pbvh, node, fd) { - BM_log_face_if_modified(ss->bm, ss->bm_log, reinterpret_cast(fd.face.i)); + if (have_dyntopo) { + /* Always log face, uses more memory and is slower. */ + BM_log_face_modified(ss->bm, ss->bm_log, reinterpret_cast(fd.face.i)); + } + else { + /* Logs face once per stroke. */ + BM_log_face_if_modified(ss->bm, ss->bm_log, reinterpret_cast(fd.face.i)); + } } BKE_pbvh_face_iter_end(fd); } @@ -1055,7 +1065,7 @@ void SCULPT_smooth( float last; SCULPT_boundary_info_ensure(ob); - SCULPT_smooth_undo_push(ob, nodes); + SCULPT_smooth_undo_push(sd, ob, nodes, brush); /* PBVH_FACES needs ss->epmap. */ if (BKE_pbvh_type(ss->pbvh) == PBVH_FACES && ss->epmap.is_empty()) { @@ -1276,7 +1286,7 @@ void SCULPT_do_surface_smooth_brush(Sculpt *sd, Object *ob, Span nod Brush *brush = BKE_paint_brush(&sd->paint); SCULPT_boundary_info_ensure(ob); - SCULPT_smooth_undo_push(ob, nodes); + SCULPT_smooth_undo_push(sd, ob, nodes, brush); /* Threaded loop over nodes. */ SculptThreadedTaskData data{}; diff --git a/source/blender/editors/sculpt_paint/sculpt_undo.cc b/source/blender/editors/sculpt_paint/sculpt_undo.cc index 03d3a8f8b88..5d2badc6395 100644 --- a/source/blender/editors/sculpt_paint/sculpt_undo.cc +++ b/source/blender/editors/sculpt_paint/sculpt_undo.cc @@ -2223,7 +2223,7 @@ static SculptUndoNode *sculpt_undo_bmesh_push(Object *ob, PBVHNode *node, Sculpt case SCULPT_UNDO_COORDS: case SCULPT_UNDO_MASK: BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) { - BM_log_vert_before_modified(ss->bm, ss->bm_log, vd.bm_vert); + BM_log_vert_modified(ss->bm, ss->bm_log, vd.bm_vert); } BKE_pbvh_vertex_iter_end; break; @@ -2232,7 +2232,7 @@ static SculptUndoNode *sculpt_undo_bmesh_push(Object *ob, PBVHNode *node, Sculpt DyntopoSet *faces = BKE_pbvh_bmesh_node_faces(node); BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) { - BM_log_vert_before_modified(ss->bm, ss->bm_log, vd.bm_vert); + BM_log_vert_modified(ss->bm, ss->bm_log, vd.bm_vert); } BKE_pbvh_vertex_iter_end; @@ -2258,7 +2258,7 @@ static SculptUndoNode *sculpt_undo_bmesh_push(Object *ob, PBVHNode *node, Sculpt if (domain == ATTR_DOMAIN_POINT) { BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) { - BM_log_vert_before_modified(ss->bm, ss->bm_log, vd.bm_vert); + BM_log_vert_modified(ss->bm, ss->bm_log, vd.bm_vert); } BKE_pbvh_vertex_iter_end; } -- 2.30.2 From 9a0bdd8feaa7c958c18fef55f9ba5fc17bd60018 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Thu, 27 Jul 2023 09:57:35 -0700 Subject: [PATCH 241/279] temp-sculpt-dyntopo: Fix spiky artifacts in layer brush --- source/blender/editors/sculpt_paint/sculpt.cc | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/source/blender/editors/sculpt_paint/sculpt.cc b/source/blender/editors/sculpt_paint/sculpt.cc index 4aea7316915..8b336086889 100644 --- a/source/blender/editors/sculpt_paint/sculpt.cc +++ b/source/blender/editors/sculpt_paint/sculpt.cc @@ -6458,22 +6458,33 @@ static void sculpt_stroke_update_step(bContext *C, ss->cache->stroke_distance_t - ss->cache->last_dyntopo_t > dyntopo_spacing); } + /* Running dyntopo before layer brush causes artifacts. */ + bool run_dyntopo_after = brush->sculpt_tool == SCULPT_TOOL_LAYER; + if (do_dyntopo) { ss->cache->last_dyntopo_t = ss->cache->stroke_distance_t; - /* Note: dyntopo repeats happen after the dab. */ - do_symmetrical_brush_actions(sd, ob, sculpt_topology_update, ups, &tool_settings->paint_mode); + if (!run_dyntopo_after) { + /* Note: dyntopo repeats happen after the dab. */ + do_symmetrical_brush_actions( + sd, ob, sculpt_topology_update, ups, &tool_settings->paint_mode); + } } do_symmetrical_brush_actions(sd, ob, do_brush_action, ups, &tool_settings->paint_mode); sculpt_combine_proxies(sd, ob); - if (do_dyntopo && ss->cached_dyntopo.repeat) { + int dyntopo_repeat = ss->cached_dyntopo.repeat; + if (run_dyntopo_after) { + dyntopo_repeat++; + } + + if (do_dyntopo && dyntopo_repeat) { float3 location = ss->cache->true_location; add_v3_v3(cache->true_location, cache->grab_delta); - for (int i = 0; i < ss->cached_dyntopo.repeat; i++) { + for (int i = 0; i < dyntopo_repeat; i++) { do_symmetrical_brush_actions( sd, ob, sculpt_topology_update, ups, &tool_settings->paint_mode); } -- 2.30.2 From 86d4cb89d423cfc404354056ae40fce8582f482e Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Wed, 30 Aug 2023 13:07:26 -0700 Subject: [PATCH 242/279] temp-sculpt-dyntopo: Don't reuse bmesh in trim operators Preperation to revert changes to bmesh toolflags. * The trim operators no longer reuses ss->bm (and thus don't depend on toolflags being a customdata layer). * New method BKE_sculpt_set_bmesh. It sets a new bmesh, frees the old one and recalculates the PBVH (if needed). --- source/blender/blenkernel/BKE_paint.hh | 8 ++ source/blender/blenkernel/intern/dyntopo.cc | 4 +- source/blender/blenkernel/intern/paint.cc | 116 ++++++++++++++---- source/blender/bmesh/intern/bmesh_idmap.cc | 2 +- source/blender/bmesh/intern/bmesh_log.cc | 32 ++--- .../editors/sculpt_paint/paint_mask.cc | 71 ++++------- .../editors/sculpt_paint/sculpt_undo.cc | 4 +- 7 files changed, 147 insertions(+), 90 deletions(-) diff --git a/source/blender/blenkernel/BKE_paint.hh b/source/blender/blenkernel/BKE_paint.hh index cf3b5e2d277..881feeb8b59 100644 --- a/source/blender/blenkernel/BKE_paint.hh +++ b/source/blender/blenkernel/BKE_paint.hh @@ -1008,6 +1008,14 @@ char BKE_get_fset_boundary_symflag(Object *object); bool BKE_sculpt_has_persistent_base(SculptSession *ss); +/** + * Sets ob->sculpt->bm. The PBVH will be recreated if it exists + * (if it's of type PBVH_BMESH) as will ob->sculpt->bm_idmap. + * + * Note: BMLog (ob->sculpt->bm_log) doesn't need to be reallocated. + */ +void BKE_sculpt_set_bmesh(Object *ob, BMesh *bm, bool free_existing = true); + enum { SCULPT_MASK_LAYER_CALC_VERT = (1 << 0), SCULPT_MASK_LAYER_CALC_LOOP = (1 << 1), diff --git a/source/blender/blenkernel/intern/dyntopo.cc b/source/blender/blenkernel/intern/dyntopo.cc index f57f8f09dc4..28d4021493d 100644 --- a/source/blender/blenkernel/intern/dyntopo.cc +++ b/source/blender/blenkernel/intern/dyntopo.cc @@ -20,9 +20,9 @@ #include "BLI_index_range.hh" #include "BLI_linklist.h" #include "BLI_map.hh" +#include "BLI_math_geom.h" #include "BLI_math_matrix.h" #include "BLI_math_vector.h" -#include "BLI_math_geom.h" #include "BLI_math_vector_types.hh" #include "BLI_memarena.h" #include "BLI_rand.h" @@ -2435,7 +2435,7 @@ void detail_size_set(PBVH *pbvh, float detail_size, float detail_range) } } // namespace blender::bke::dyntopo -ATTR_NO_OPT void BKE_pbvh_bmesh_remove_face(PBVH *pbvh, BMFace *f, bool log_face) +void BKE_pbvh_bmesh_remove_face(PBVH *pbvh, BMFace *f, bool log_face) { blender::bke::dyntopo::pbvh_bmesh_face_remove(pbvh, f, log_face, true, true); } diff --git a/source/blender/blenkernel/intern/paint.cc b/source/blender/blenkernel/intern/paint.cc index 5d310f0b99b..89b3fae4b2a 100644 --- a/source/blender/blenkernel/intern/paint.cc +++ b/source/blender/blenkernel/intern/paint.cc @@ -2676,41 +2676,47 @@ void BKE_sculpt_sync_face_visibility_to_grids(Mesh *mesh, SubdivCCG *subdiv_ccg) } } -static PBVH *build_pbvh_for_dynamic_topology(Object *ob, bool /*update_sculptverts*/) +static PBVH *build_pbvh_for_dynamic_topology(Object *ob, bool update_flags_valence) { - PBVH *pbvh = ob->sculpt->pbvh = BKE_pbvh_new(PBVH_BMESH); + SculptSession *ss = ob->sculpt; + PBVH *pbvh = ss->pbvh = BKE_pbvh_new(PBVH_BMESH); - BKE_pbvh_set_bmesh(pbvh, ob->sculpt->bm); - BM_mesh_elem_table_ensure(ob->sculpt->bm, BM_VERT | BM_EDGE | BM_FACE); + BKE_pbvh_set_bmesh(pbvh, ss->bm); + BM_mesh_elem_table_ensure(ss->bm, BM_VERT | BM_EDGE | BM_FACE); sculptsession_bmesh_add_layers(ob); - sculpt_boundary_flags_ensure(ob, pbvh, ob->sculpt->bm->totvert, ob->sculpt->bm->totedge); + sculpt_boundary_flags_ensure(ob, pbvh, ss->bm->totvert, ss->bm->totedge); BKE_sculpt_ensure_sculpt_layers(ob); + if (update_flags_valence) { + BKE_sculpt_init_flags_valence(ob, ss->pbvh, ss->bm->totvert, true); + } + BKE_sculptsession_update_attr_refs(ob); + + BKE_sculpt_ensure_origco(ob); sculpt_check_face_areas(ob, pbvh); BKE_sculpt_ensure_idmap(ob); - blender::bke::pbvh::sharp_limit_set(pbvh, ob->sculpt->sharp_angle_limit); + blender::bke::pbvh::sharp_limit_set(pbvh, ss->sharp_angle_limit); BKE_pbvh_build_bmesh(pbvh, BKE_object_get_original_mesh(ob), - ob->sculpt->bm, - ob->sculpt->bm_log, - ob->sculpt->bm_idmap, - ob->sculpt->attrs.dyntopo_node_id_vertex->bmesh_cd_offset, - ob->sculpt->attrs.dyntopo_node_id_face->bmesh_cd_offset, - ob->sculpt->attrs.face_areas->bmesh_cd_offset, - ob->sculpt->attrs.boundary_flags->bmesh_cd_offset, - ob->sculpt->attrs.edge_boundary_flags->bmesh_cd_offset, - ob->sculpt->attrs.flags ? ob->sculpt->attrs.flags->bmesh_cd_offset : -1, - ob->sculpt->attrs.valence ? ob->sculpt->attrs.valence->bmesh_cd_offset : -1, - ob->sculpt->attrs.orig_co ? ob->sculpt->attrs.orig_co->bmesh_cd_offset : -1, - ob->sculpt->attrs.orig_no ? ob->sculpt->attrs.orig_no->bmesh_cd_offset : - -1); + ss->bm, + ss->bm_log, + ss->bm_idmap, + ss->attrs.dyntopo_node_id_vertex->bmesh_cd_offset, + ss->attrs.dyntopo_node_id_face->bmesh_cd_offset, + ss->attrs.face_areas->bmesh_cd_offset, + ss->attrs.boundary_flags->bmesh_cd_offset, + ss->attrs.edge_boundary_flags->bmesh_cd_offset, + ss->attrs.flags ? ss->attrs.flags->bmesh_cd_offset : -1, + ss->attrs.valence ? ss->attrs.valence->bmesh_cd_offset : -1, + ss->attrs.orig_co ? ss->attrs.orig_co->bmesh_cd_offset : -1, + ss->attrs.orig_no ? ss->attrs.orig_no->bmesh_cd_offset : -1); - if (ob->sculpt->bm_log) { - BKE_pbvh_set_bm_log(pbvh, ob->sculpt->bm_log); + if (ss->bm_log) { + BKE_pbvh_set_bm_log(pbvh, ss->bm_log); } return pbvh; @@ -2921,7 +2927,6 @@ PBVH *BKE_sculpt_object_pbvh_ensure(Depsgraph *depsgraph, Object *ob) BKE_sculpt_ensure_idmap(ob); SCULPT_undo_ensure_bmlog(ob); - /* Note build_pbvh_for_dynamic_topology respects the pbvh cache. */ pbvh = ss->pbvh = build_pbvh_for_dynamic_topology(ob, true); if (!CustomData_has_layer(&ss->bm->vdata, CD_PAINT_MASK)) { @@ -4276,6 +4281,73 @@ void BKE_sculpt_ensure_sculpt_layers(struct Object *ob) } } +void BKE_sculpt_set_bmesh(Object *ob, BMesh *bm, bool free_existing) +{ + SculptSession *ss = ob->sculpt; + + if (bm == ss->bm) { + return; + } + + /* Destroy existing idmap. */ + if (ss->bm_idmap) { + BM_idmap_destroy(ss->bm_idmap); + ss->bm_idmap = nullptr; + } + + if (!ss->bm) { + ss->bm = bm; + return; + } + + /* Free existing bmesh. */ + if (ss->bm && free_existing) { + BM_mesh_free(ss->bm); + ss->bm = nullptr; + } + + /* Destroy toolflags if they exist (will reallocate the bmesh). */ + BM_mesh_toolflags_set(bm, false); + + /* Ensure element indices & lookup tables are up to date. */ + bm->elem_index_dirty = BM_VERT | BM_EDGE | BM_FACE; + bm->elem_table_dirty = BM_VERT | BM_EDGE | BM_FACE; + BM_mesh_elem_table_ensure(bm, BM_VERT | BM_EDGE | BM_FACE); + BM_mesh_elem_index_ensure(bm, BM_VERT | BM_EDGE | BM_FACE); + + /* Set new bmesh. */ + ss->bm = bm; + + /* Invalidate any existing bmesh attributes. */ + SculptAttribute **attrs = reinterpret_cast(&ss->attrs); + int attrs_num = int(sizeof(ss->attrs) / sizeof(void *)); + for (int i = 0; i < attrs_num; i++) { + if (attrs[i] && attrs[i]->data_for_bmesh) { + attrs[i]->used = false; + attrs[i] = nullptr; + } + } + + /* Check for any stray attributes in the pool that weren't stored in ss->attrs. */ + for (int i = 0; i < SCULPT_MAX_ATTRIBUTES; i++) { + SculptAttribute *attr = ss->temp_attributes + i; + if (attr->used && attr->data_for_bmesh) { + attr->used = false; + } + } + + if (!ss->pbvh) { + /* Do nothing, no pbvh to rebuild. */ + return; + } + + /* Destroy and rebuild pbvh */ + BKE_pbvh_free(ss->pbvh); + ss->pbvh = nullptr; + + ss->pbvh = build_pbvh_for_dynamic_topology(ob, true); +} + namespace blender::bke::paint { bool get_original_vertex(SculptSession *ss, PBVHVertRef vertex, diff --git a/source/blender/bmesh/intern/bmesh_idmap.cc b/source/blender/bmesh/intern/bmesh_idmap.cc index 534f9ad2a61..cec89f073aa 100644 --- a/source/blender/bmesh/intern/bmesh_idmap.cc +++ b/source/blender/bmesh/intern/bmesh_idmap.cc @@ -498,7 +498,7 @@ void BM_idmap_assign(BMIdMap *idmap, BMElem *elem, int id) #endif } -ATTR_NO_OPT void BM_idmap_release(BMIdMap *idmap, BMElem *elem, bool clear_id) +void BM_idmap_release(BMIdMap *idmap, BMElem *elem, bool clear_id) { #ifdef DEBUG_BM_IDMAP idmap_check_elem(idmap, elem); diff --git a/source/blender/bmesh/intern/bmesh_log.cc b/source/blender/bmesh/intern/bmesh_log.cc index 7c64e6ccef8..5287b699190 100644 --- a/source/blender/bmesh/intern/bmesh_log.cc +++ b/source/blender/bmesh/intern/bmesh_log.cc @@ -593,7 +593,7 @@ struct BMLogEntry { CustomData_free(&pdata, 0); } - template ATTR_NO_OPT T *get_elem_from_id(BMesh * /*bm*/, BMID id) + template T *get_elem_from_id(BMesh * /*bm*/, BMID id) { T *elem = reinterpret_cast(BM_idmap_lookup(idmap, id.id)); char htype = 0; @@ -1378,9 +1378,9 @@ void BMLogSetDiff::restore_verts(BMesh *bm, bm->elem_table_dirty |= BM_VERT | BM_EDGE; } -ATTR_NO_OPT void BMLogSetDiff::remove_verts(BMesh *bm, - blender::Map, BMLogVert *> verts, - BMLogCallbacks *callbacks) +void BMLogSetDiff::remove_verts(BMesh *bm, + blender::Map, BMLogVert *> verts, + BMLogCallbacks *callbacks) { for (BMLogVert *lv : verts.values()) { BMVert *v = entry->get_elem_from_id(bm, lv->id); @@ -1402,9 +1402,9 @@ ATTR_NO_OPT void BMLogSetDiff::remove_verts(BMesh *bm, bm->elem_table_dirty |= BM_VERT | BM_EDGE; } -ATTR_NO_OPT void BMLogSetDiff::restore_edges(BMesh *bm, - blender::Map, BMLogEdge *> edges, - BMLogCallbacks *callbacks) +void BMLogSetDiff::restore_edges(BMesh *bm, + blender::Map, BMLogEdge *> edges, + BMLogCallbacks *callbacks) { for (BMLogEdge *le : edges.values()) { BMVert *v1 = entry->get_elem_from_id(bm, le->v1); @@ -1433,9 +1433,9 @@ ATTR_NO_OPT void BMLogSetDiff::restore_edges(BMesh *bm, } } -ATTR_NO_OPT void BMLogSetDiff::remove_edges(BMesh *bm, - blender::Map, BMLogEdge *> edges, - BMLogCallbacks *callbacks) +void BMLogSetDiff::remove_edges(BMesh *bm, + blender::Map, BMLogEdge *> edges, + BMLogCallbacks *callbacks) { for (BMLogEdge *le : edges.values()) { BMEdge *e = entry->get_elem_from_id(bm, le->id); @@ -1489,9 +1489,9 @@ void BMLogSetDiff::swap_edges(BMesh *bm, } } -ATTR_NO_OPT void BMLogSetDiff::restore_faces(BMesh *bm, - blender::Map, BMLogFace *> faces, - BMLogCallbacks *callbacks) +void BMLogSetDiff::restore_faces(BMesh *bm, + blender::Map, BMLogFace *> faces, + BMLogCallbacks *callbacks) { Vector verts; @@ -1541,9 +1541,9 @@ ATTR_NO_OPT void BMLogSetDiff::restore_faces(BMesh *bm, bm->elem_table_dirty |= BM_FACE; } -ATTR_NO_OPT void BMLogSetDiff::remove_faces(BMesh *bm, - blender::Map, BMLogFace *> faces, - BMLogCallbacks *callbacks) +void BMLogSetDiff::remove_faces(BMesh *bm, + blender::Map, BMLogFace *> faces, + BMLogCallbacks *callbacks) { for (BMLogFace *lf : faces.values()) { BMFace *f = entry->get_elem_from_id(bm, lf->id); diff --git a/source/blender/editors/sculpt_paint/paint_mask.cc b/source/blender/editors/sculpt_paint/paint_mask.cc index 5405df6610c..cbee7b71839 100644 --- a/source/blender/editors/sculpt_paint/paint_mask.cc +++ b/source/blender/editors/sculpt_paint/paint_mask.cc @@ -52,6 +52,7 @@ #include "ED_sculpt.hh" #include "ED_view3d.hh" +#include "../../bmesh/intern/bmesh_idmap.h" #include "bmesh.h" #include "tools/bmesh_boolean.h" @@ -1408,28 +1409,28 @@ static int bm_face_isect_pair(BMFace *f, void * /*user_data*/) static void sculpt_gesture_apply_trim(SculptGestureContext *sgcontext) { + SculptSession *ss = sgcontext->ss; + Object *ob = sgcontext->vc.obact; SculptGestureTrimOperation *trim_operation = (SculptGestureTrimOperation *)sgcontext->operation; - Mesh *sculpt_mesh = BKE_mesh_from_object(sgcontext->vc.obact); + Mesh *sculpt_mesh = BKE_mesh_from_object(ob); Mesh *trim_mesh = trim_operation->mesh; + const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(sculpt_mesh, trim_mesh); + BMeshCreateParams create_params = {0}; + create_params.use_toolflags = true; + + BMeshFromMeshParams convert_params = {0}; + convert_params.calc_face_normal = true; + BMesh *bm; - if (sgcontext->ss && sgcontext->ss->bm) { - bm = sgcontext->ss->bm; - BM_mesh_normals_update(bm); + if (ss->bm) { + bm = BM_mesh_copy(ss->bm); + BM_mesh_toolflags_set(bm, true); } - else { - const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(sculpt_mesh, trim_mesh); - BMeshCreateParams params = {0}; - params.use_toolflags = false; - - BMeshFromMeshParams params2 = {0}; - params2.calc_face_normal = true; - - bm = BM_mesh_create(&allocsize, ¶ms); - - BM_mesh_bm_from_me(bm, sculpt_mesh, ¶ms2); + bm = BM_mesh_create(&allocsize, &create_params); + BM_mesh_bm_from_me(bm, sculpt_mesh, &convert_params); } BMIter iter; @@ -1439,17 +1440,12 @@ static void sculpt_gesture_apply_trim(SculptGestureContext *sgcontext) } /* Create trim bmesh. */ - BMeshCreateParams params1 = {0}; + BMesh *trimbm = BM_mesh_create(&allocsize, &create_params); - const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(sculpt_mesh, trim_mesh); - BMesh *trimbm = BM_mesh_create(&allocsize, ¶ms1); - - BMeshFromMeshParams params3 = {0}; - params3.calc_face_normal = params3.calc_vert_normal = true; - BM_mesh_bm_from_me(trimbm, trim_mesh, ¶ms3); + convert_params.calc_face_normal = convert_params.calc_vert_normal = true; + BM_mesh_bm_from_me(trimbm, trim_mesh, &convert_params); BM_mesh_normals_update(bm); - BM_mesh_toolflags_set(bm, true); /* Add trim geometry to bm. */ BMO_op_callf(trimbm, BMO_FLAG_DEFAULTS, "duplicate geom=%avef dest=%p", bm, 3); @@ -1514,7 +1510,7 @@ static void sculpt_gesture_apply_trim(SculptGestureContext *sgcontext) } MEM_freeN(looptris); - BKE_sculptsession_update_attr_refs(sgcontext->vc.obact); + BKE_sculptsession_update_attr_refs(ob); /* Flag intersection edges as sharp.*/ BMEdge *e; @@ -1535,29 +1531,8 @@ static void sculpt_gesture_apply_trim(SculptGestureContext *sgcontext) BM_elem_flag_disable(e, BM_ELEM_SMOOTH); } - if (sgcontext->ss && sgcontext->ss->bm) { /* Rebuild pbvh. */ - BKE_pbvh_free(sgcontext->ss->pbvh); - - sgcontext->ss->pbvh = BKE_pbvh_new(PBVH_BMESH); - BKE_pbvh_set_bmesh(sgcontext->ss->pbvh, sgcontext->ss->bm); - BKE_sculpt_ensure_sculpt_layers(sgcontext->vc.obact); - - blender::bke::pbvh::sharp_limit_set(sgcontext->ss->pbvh, sgcontext->ss->sharp_angle_limit); - - BKE_pbvh_build_bmesh(sgcontext->ss->pbvh, - sculpt_mesh, - sgcontext->ss->bm, - sgcontext->ss->bm_log, - sgcontext->ss->bm_idmap, - sgcontext->ss->cd_vert_node_offset, - sgcontext->ss->cd_face_node_offset, - sgcontext->ss->cd_face_areas, - sgcontext->ss->attrs.boundary_flags->bmesh_cd_offset, - sgcontext->ss->attrs.edge_boundary_flags->bmesh_cd_offset, - sgcontext->ss->attrs.flags->bmesh_cd_offset, - sgcontext->ss->attrs.valence->bmesh_cd_offset, - sgcontext->ss->attrs.orig_co->bmesh_cd_offset, - sgcontext->ss->attrs.orig_no->bmesh_cd_offset); + if (ss && ss->bm) { /* Swap out sculpt bmesh. */ + BKE_sculpt_set_bmesh(ob, bm, true); } else { // save result to mesh BMeshToMeshParams params = {0}; @@ -1566,7 +1541,7 @@ static void sculpt_gesture_apply_trim(SculptGestureContext *sgcontext) Mesh *result = BKE_mesh_from_bmesh_nomain(bm, ¶ms, sculpt_mesh); BM_mesh_free(bm); - BKE_mesh_nomain_to_mesh(result, (Mesh *)sgcontext->vc.obact->data, sgcontext->vc.obact); + BKE_mesh_nomain_to_mesh(result, (Mesh *)ob->data, ob); } } diff --git a/source/blender/editors/sculpt_paint/sculpt_undo.cc b/source/blender/editors/sculpt_paint/sculpt_undo.cc index cab93e9dc72..0e7709e3b5f 100644 --- a/source/blender/editors/sculpt_paint/sculpt_undo.cc +++ b/source/blender/editors/sculpt_paint/sculpt_undo.cc @@ -748,7 +748,7 @@ static void bmesh_undo_on_vert_add(BMVert *v, void *userdata) BM_ELEM_CD_SET_INT(v, data->cd_vert_node_offset, -1); } -ATTR_NO_OPT static void bmesh_undo_on_face_kill(BMFace *f, void *userdata) +static void bmesh_undo_on_face_kill(BMFace *f, void *userdata) { BmeshUndoData *data = (BmeshUndoData *)userdata; int ni = BM_ELEM_CD_GET_INT(f, data->cd_face_node_offset); @@ -814,6 +814,8 @@ static void bmesh_undo_full_mesh(void *userdata) data->pbvh = nullptr; } + /* Recalculate face normals to prevent tessellation errors.*/ + BM_mesh_normals_update(data->bm); data->do_full_recalc = true; } -- 2.30.2 From 61d0426f46513431d48b1c12bd90966865f42492 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Wed, 30 Aug 2023 13:14:08 -0700 Subject: [PATCH 243/279] temp-sculpt-dyntopo: Add forwarding header for bmesh_idmap.h --- source/blender/blenkernel/intern/paint.cc | 2 +- source/blender/blenkernel/intern/pbvh_intern.hh | 2 +- source/blender/bmesh/CMakeLists.txt | 1 + source/blender/bmesh/bmesh_idmap.h | 2 ++ source/blender/editors/sculpt_paint/paint_mask.cc | 2 +- source/blender/editors/sculpt_paint/sculpt.cc | 2 +- source/blender/editors/sculpt_paint/sculpt_dyntopo.cc | 2 +- source/blender/editors/sculpt_paint/sculpt_face_set.cc | 2 +- source/blender/editors/sculpt_paint/sculpt_ops.cc | 2 +- source/blender/editors/sculpt_paint/sculpt_undo.cc | 2 +- 10 files changed, 11 insertions(+), 8 deletions(-) create mode 100644 source/blender/bmesh/bmesh_idmap.h diff --git a/source/blender/blenkernel/intern/paint.cc b/source/blender/blenkernel/intern/paint.cc index 89b3fae4b2a..411d0540929 100644 --- a/source/blender/blenkernel/intern/paint.cc +++ b/source/blender/blenkernel/intern/paint.cc @@ -79,7 +79,7 @@ #include "BLO_read_write.h" -#include "../../bmesh/intern/bmesh_idmap.h" +#include "bmesh_idmap.h" #include "bmesh.h" #include "bmesh_log.h" diff --git a/source/blender/blenkernel/intern/pbvh_intern.hh b/source/blender/blenkernel/intern/pbvh_intern.hh index 5afea5cba8e..68aa8b6bd47 100644 --- a/source/blender/blenkernel/intern/pbvh_intern.hh +++ b/source/blender/blenkernel/intern/pbvh_intern.hh @@ -13,7 +13,7 @@ #include "BKE_paint.hh" /* for SCULPT_BOUNDARY_NEEDS_UPDATE */ #include "BKE_pbvh_api.hh" -#include "../../bmesh/intern/bmesh_idmap.h" +#include "bmesh_idmap.h" #include "bmesh.h" #define PBVH_STACK_FIXED_DEPTH 100 diff --git a/source/blender/bmesh/CMakeLists.txt b/source/blender/bmesh/CMakeLists.txt index 5b6c1cdec44..e0b78b126da 100644 --- a/source/blender/bmesh/CMakeLists.txt +++ b/source/blender/bmesh/CMakeLists.txt @@ -164,6 +164,7 @@ set(SRC tools/bmesh_wireframe.h bmesh_class.h + bmesh_idmap.h bmesh_log.h # public includes diff --git a/source/blender/bmesh/bmesh_idmap.h b/source/blender/bmesh/bmesh_idmap.h new file mode 100644 index 00000000000..dfe47efa257 --- /dev/null +++ b/source/blender/bmesh/bmesh_idmap.h @@ -0,0 +1,2 @@ +#include "intern/bmesh_idmap.h" + diff --git a/source/blender/editors/sculpt_paint/paint_mask.cc b/source/blender/editors/sculpt_paint/paint_mask.cc index cbee7b71839..9f1a840a457 100644 --- a/source/blender/editors/sculpt_paint/paint_mask.cc +++ b/source/blender/editors/sculpt_paint/paint_mask.cc @@ -52,7 +52,7 @@ #include "ED_sculpt.hh" #include "ED_view3d.hh" -#include "../../bmesh/intern/bmesh_idmap.h" +#include "bmesh_idmap.h" #include "bmesh.h" #include "tools/bmesh_boolean.h" diff --git a/source/blender/editors/sculpt_paint/sculpt.cc b/source/blender/editors/sculpt_paint/sculpt.cc index 7fc48909f02..92d6a3bb691 100644 --- a/source/blender/editors/sculpt_paint/sculpt.cc +++ b/source/blender/editors/sculpt_paint/sculpt.cc @@ -83,7 +83,7 @@ #include "RNA_access.hh" #include "RNA_define.hh" -#include "../../bmesh/intern/bmesh_idmap.h" +#include "bmesh_idmap.h" #include "bmesh.h" using blender::float3; diff --git a/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc b/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc index bc87a73a1b1..5fc016bbbed 100644 --- a/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc +++ b/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc @@ -57,7 +57,7 @@ #include "UI_interface.hh" #include "UI_resources.hh" -#include "../../bmesh/intern/bmesh_idmap.h" +#include "bmesh_idmap.h" #include "bmesh.h" #include "bmesh_log.h" #include "bmesh_tools.h" diff --git a/source/blender/editors/sculpt_paint/sculpt_face_set.cc b/source/blender/editors/sculpt_paint/sculpt_face_set.cc index 506cd7bf7aa..c8f13baca18 100644 --- a/source/blender/editors/sculpt_paint/sculpt_face_set.cc +++ b/source/blender/editors/sculpt_paint/sculpt_face_set.cc @@ -57,7 +57,7 @@ #include "RNA_access.hh" #include "RNA_define.hh" -#include "../../bmesh/intern/bmesh_idmap.h" +#include "bmesh_idmap.h" #include "bmesh.h" using blender::Array; diff --git a/source/blender/editors/sculpt_paint/sculpt_ops.cc b/source/blender/editors/sculpt_paint/sculpt_ops.cc index f8f23905577..32a401c7eaa 100644 --- a/source/blender/editors/sculpt_paint/sculpt_ops.cc +++ b/source/blender/editors/sculpt_paint/sculpt_ops.cc @@ -85,7 +85,7 @@ #include "bmesh_log.h" #include "bmesh_tools.h" -#include "../../bmesh/intern/bmesh_idmap.h" +#include "bmesh_idmap.h" #include #include diff --git a/source/blender/editors/sculpt_paint/sculpt_undo.cc b/source/blender/editors/sculpt_paint/sculpt_undo.cc index 0e7709e3b5f..b311fd1cf47 100644 --- a/source/blender/editors/sculpt_paint/sculpt_undo.cc +++ b/source/blender/editors/sculpt_paint/sculpt_undo.cc @@ -80,7 +80,7 @@ #include "ED_sculpt.hh" #include "ED_undo.hh" -#include "../../bmesh/intern/bmesh_idmap.h" +#include "bmesh_idmap.h" #include "bmesh.h" #include "bmesh_log.h" #include "sculpt_intern.hh" -- 2.30.2 From 68f09fdd5075813d0cb758a3e00ebc34801469cb Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Wed, 30 Aug 2023 14:07:43 -0700 Subject: [PATCH 244/279] temp-sculpt-dyntopo: Remove CD_TOOLFLAGS Revert BMesh toolflags to behaviour in master. --- .../blender/blenkernel/intern/customdata.cc | 31 -- source/blender/blenkernel/intern/dyntopo.cc | 25 +- .../blender/blenkernel/intern/pbvh_bmesh.cc | 2 +- .../blenloader/intern/versioning_300.cc | 2 +- source/blender/bmesh/bmesh_class.h | 55 ++-- source/blender/bmesh/bmesh_idmap.h | 1 - source/blender/bmesh/bmesh_log.h | 4 +- .../blender/bmesh/intern/bmesh_construct.cc | 94 ++---- source/blender/bmesh/intern/bmesh_construct.h | 2 - source/blender/bmesh/intern/bmesh_core.cc | 283 ++++-------------- source/blender/bmesh/intern/bmesh_core.h | 6 +- source/blender/bmesh/intern/bmesh_interp.cc | 35 +-- source/blender/bmesh/intern/bmesh_marking.cc | 2 - source/blender/bmesh/intern/bmesh_mesh.cc | 220 ++++---------- source/blender/bmesh/intern/bmesh_mesh.h | 2 +- .../bmesh/intern/bmesh_mesh_convert.cc | 25 +- .../blender/bmesh/intern/bmesh_mesh_convert.h | 2 - .../bmesh/intern/bmesh_mesh_duplicate.cc | 6 - .../blender/bmesh/intern/bmesh_operator_api.h | 187 +++++------- .../blender/bmesh/intern/bmesh_operators.cc | 232 ++++++++------ source/blender/bmesh/intern/bmesh_private.h | 2 - source/blender/bmesh/operators/bmo_dupe.cc | 6 - .../bmesh/operators/bmo_removedoubles.cc | 7 +- .../editors/sculpt_paint/paint_mask.cc | 15 +- .../editors/sculpt_paint/sculpt_face_set.cc | 20 +- .../blender/makesdna/DNA_customdata_types.h | 7 +- source/blender/makesdna/DNA_meshdata_types.h | 5 - 27 files changed, 439 insertions(+), 839 deletions(-) diff --git a/source/blender/blenkernel/intern/customdata.cc b/source/blender/blenkernel/intern/customdata.cc index 8c58e50fa18..46eda7636e9 100644 --- a/source/blender/blenkernel/intern/customdata.cc +++ b/source/blender/blenkernel/intern/customdata.cc @@ -2009,14 +2009,6 @@ static const LayerTypeInfo LAYERTYPEINFO[CD_NUMTYPES] = { nullptr, nullptr, layerDefault_propquaternion}, - /*53: CD_BMESH_TOOLFLAGS */ - {sizeof(MToolFlags), - "MToolFlags", - 1, - nullptr, // flag singleton layer - nullptr, - nullptr, - layerInterp_noop}, }; static const char *LAYERTYPENAMES[CD_NUMTYPES] = { @@ -2075,7 +2067,6 @@ static const char *LAYERTYPENAMES[CD_NUMTYPES] = { "CDPropBoolean", "CDHairLength", "CDPropQuaternion", - "CDMToolFlags", }; const CustomData_MeshMasks CD_MASK_BAREMESH = { @@ -4123,18 +4114,6 @@ void CustomData_bmesh_alloc_block(CustomData *data, void **block) *block = BLI_mempool_alloc(data->pool); CustomData_bmesh_poison(data, *block); - - /* Clear toolflags pointer when created for the first time. */ - int cd_tflags = data->typemap[CD_TOOLFLAGS]; - if (cd_tflags != -1) { - cd_tflags = data->layers[cd_tflags].offset; - - char *ptr = (char *)*block; - ptr += cd_tflags; - - MToolFlags *flags = (MToolFlags *)ptr; - flags->flag = nullptr; - } } else { *block = nullptr; @@ -4221,11 +4200,6 @@ void CustomData_bmesh_swap_data(CustomData *source, dest_i_start++; } - if (source->layers[src_i].type == CD_TOOLFLAGS) { - /* Do not swap toolflags. */ - continue; - } - /* If there are no more dest layers, we're done. */ if (dest_i_start >= dest->totlayer) { return; @@ -4643,11 +4617,6 @@ void CustomData_bmesh_interp_ex(CustomData *data, for (int i = 0; i < data->totlayer; i++) { CustomDataLayer *layer = &data->layers[i]; - /* Ignore toolflag layers. */ - if (eCustomDataType(layer->type) == CD_TOOLFLAGS) { - continue; - } - const LayerTypeInfo *typeInfo = layerType_getInfo(eCustomDataType(layer->type)); if (!(CD_TYPE_AS_MASK(layer->type) & typemask)) { diff --git a/source/blender/blenkernel/intern/dyntopo.cc b/source/blender/blenkernel/intern/dyntopo.cc index 28d4021493d..b6eda0c7b13 100644 --- a/source/blender/blenkernel/intern/dyntopo.cc +++ b/source/blender/blenkernel/intern/dyntopo.cc @@ -70,31 +70,18 @@ using namespace blender::bke::sculpt; /* Executes a simple pointer swap; * if an element ID attribute exists (cd_id_offset is not -1) - * it will unswap IDs. CD_TOOLFLAGS layer is also unswapped - * if it exists. + * it will unswap IDs. */ static void bmesh_swap_data_simple(CustomData *data, void **block1, void **block2, int cd_id) { std::swap(*block1, *block2); - int cd_toolflags = data->typemap[CD_TOOLFLAGS]; - cd_toolflags = cd_toolflags != -1 ? data->layers[cd_toolflags].offset : -1; + /* Unswap element IDs if they exist. */ + if (cd_id != -1 && *block1 && *block2) { + int *id1 = static_cast(POINTER_OFFSET(*block1, cd_id)); + int *id2 = static_cast(POINTER_OFFSET(*block2, cd_id)); - /* Unswap toolflags and/or element IDs if they exist */ - if (*block1 && *block2) { - if (cd_toolflags != -1) { - MToolFlags *flags1 = static_cast(POINTER_OFFSET(*block1, cd_toolflags)); - MToolFlags *flags2 = static_cast(POINTER_OFFSET(*block2, cd_toolflags)); - - std::swap(*flags1, *flags2); - } - - if (cd_id != -1) { - int *id1 = static_cast(POINTER_OFFSET(*block1, cd_id)); - int *id2 = static_cast(POINTER_OFFSET(*block2, cd_id)); - - std::swap(*id1, *id2); - } + std::swap(*id1, *id2); } } diff --git a/source/blender/blenkernel/intern/pbvh_bmesh.cc b/source/blender/blenkernel/intern/pbvh_bmesh.cc index 2b0f9e4b601..a198ba293cb 100644 --- a/source/blender/blenkernel/intern/pbvh_bmesh.cc +++ b/source/blender/blenkernel/intern/pbvh_bmesh.cc @@ -656,7 +656,7 @@ void pbvh_bmesh_vert_remove(PBVH *pbvh, BMVert *v) } while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e); } -ATTR_NO_OPT void pbvh_bmesh_face_remove( +void pbvh_bmesh_face_remove( PBVH *pbvh, BMFace *f, bool log_face, bool check_verts, bool /*ensure_ownership_transfer*/) { PBVHNode *f_node = pbvh_bmesh_node_from_face(pbvh, f); diff --git a/source/blender/blenloader/intern/versioning_300.cc b/source/blender/blenloader/intern/versioning_300.cc index 6cc492f81e0..350e01577ea 100644 --- a/source/blender/blenloader/intern/versioning_300.cc +++ b/source/blender/blenloader/intern/versioning_300.cc @@ -4521,7 +4521,7 @@ void blo_do_versions_300(FileData *fd, Library * /*lib*/, Main *bmain) for (int i = 0; i < 4; i++, data++) { for (int j = 0; j < data->totlayer; j++) { - /* CD_TOOLFLAGS used to be CD_MESH_ID. */ + /* CD_PROP_QUATERNION used to be CD_MESH_ID. */ if (data->layers[j].type == 52) { CustomData_free_layer(data, eCustomDataType(52), 0, j); j--; diff --git a/source/blender/bmesh/bmesh_class.h b/source/blender/bmesh/bmesh_class.h index 79981cb69a6..3b9cbdb0be0 100644 --- a/source/blender/bmesh/bmesh_class.h +++ b/source/blender/bmesh/bmesh_class.h @@ -103,6 +103,11 @@ typedef struct BMVert { struct BMEdge *e; } BMVert; +typedef struct BMVert_OFlag { + BMVert base; + struct BMFlagLayer *oflags; +} BMVert_OFlag; + /* disk link structure, only used by edges */ typedef struct BMDiskLink { struct BMEdge *next, *prev; @@ -137,6 +142,11 @@ typedef struct BMEdge { BMDiskLink v1_disk_link, v2_disk_link; } BMEdge; +typedef struct BMEdge_OFlag { + BMEdge base; + struct BMFlagLayer *oflags; +} BMEdge_OFlag; + typedef struct BMLoop { BMHeader head; /* notice no flags layer */ @@ -278,6 +288,11 @@ typedef struct BMFace { // short _pad[3]; } BMFace; +typedef struct BMFace_OFlag { + BMFace base; + struct BMFlagLayer *oflags; +} BMFace_OFlag; + typedef struct BMFlagLayer { short f; /* flags */ } BMFlagLayer; @@ -412,10 +427,12 @@ enum { /* args for _Generic */ #define _BM_GENERIC_TYPE_ELEM_NONCONST \ - void *, BMVert *, BMEdge *, BMLoop *, BMFace *, BMElem *, BMElemF *, BMHeader * + void *, BMVert *, BMEdge *, BMLoop *, BMFace *, BMVert_OFlag *, BMEdge_OFlag *, BMFace_OFlag *, \ + BMElem *, BMElemF *, BMHeader * #define _BM_GENERIC_TYPE_ELEM_CONST \ - const void *, const BMVert *, const BMEdge *, const BMLoop *, const BMFace *, const BMElem *, \ + const void *, const BMVert *, const BMEdge *, const BMLoop *, const BMFace *, \ + const BMVert_OFlag *, const BMEdge_OFlag *, const BMFace_OFlag *, const BMElem *, \ const BMElemF *, const BMHeader * #define BM_CHECK_TYPE_ELEM_CONST(ele) CHECK_TYPE_ANY(ele, _BM_GENERIC_TYPES_CONST) @@ -426,22 +443,22 @@ enum { CHECK_TYPE_ANY(ele, _BM_GENERIC_TYPE_ELEM_NONCONST, _BM_GENERIC_TYPE_ELEM_CONST) /* vert */ -#define _BM_GENERIC_TYPE_VERT_NONCONST BMVert * -#define _BM_GENERIC_TYPE_VERT_CONST const BMVert * +#define _BM_GENERIC_TYPE_VERT_NONCONST BMVert *, BMVert_OFlag * +#define _BM_GENERIC_TYPE_VERT_CONST const BMVert *, const BMVert_OFlag * #define BM_CHECK_TYPE_VERT_CONST(ele) CHECK_TYPE_ANY(ele, _BM_GENERIC_TYPE_VERT_CONST) #define BM_CHECK_TYPE_VERT_NONCONST(ele) CHECK_TYPE_ANY(ele, _BM_GENERIC_TYPE_ELEM_NONCONST) #define BM_CHECK_TYPE_VERT(ele) \ CHECK_TYPE_ANY(ele, _BM_GENERIC_TYPE_VERT_NONCONST, _BM_GENERIC_TYPE_VERT_CONST) /* edge */ -#define _BM_GENERIC_TYPE_EDGE_NONCONST BMEdge * -#define _BM_GENERIC_TYPE_EDGE_CONST const BMEdge * +#define _BM_GENERIC_TYPE_EDGE_NONCONST BMEdge *, BMEdge_OFlag * +#define _BM_GENERIC_TYPE_EDGE_CONST const BMEdge *, const BMEdge_OFlag * #define BM_CHECK_TYPE_EDGE_CONST(ele) CHECK_TYPE_ANY(ele, _BM_GENERIC_TYPE_EDGE_CONST) #define BM_CHECK_TYPE_EDGE_NONCONST(ele) CHECK_TYPE_ANY(ele, _BM_GENERIC_TYPE_ELEM_NONCONST) #define BM_CHECK_TYPE_EDGE(ele) \ CHECK_TYPE_ANY(ele, _BM_GENERIC_TYPE_EDGE_NONCONST, _BM_GENERIC_TYPE_EDGE_CONST) /* face */ -#define _BM_GENERIC_TYPE_FACE_NONCONST BMFace * -#define _BM_GENERIC_TYPE_FACE_CONST const BMFace * +#define _BM_GENERIC_TYPE_FACE_NONCONST BMFace *, BMFace_OFlag * +#define _BM_GENERIC_TYPE_FACE_CONST const BMFace *, const BMFace_OFlag * #define BM_CHECK_TYPE_FACE_CONST(ele) CHECK_TYPE_ANY(ele, _BM_GENERIC_TYPE_FACE_CONST) #define BM_CHECK_TYPE_FACE_NONCONST(ele) CHECK_TYPE_ANY(ele, _BM_GENERIC_TYPE_ELEM_NONCONST) #define BM_CHECK_TYPE_FACE(ele) \ @@ -543,17 +560,6 @@ typedef bool (*BMLoopPairFilterFunc)(const BMLoop *, const BMLoop *, void *user_ (BLI_assert(offset != -1), (void *)((char *)(ele)->head.data + (offset))) #endif -#ifdef __cplusplus -} - -template inline Tptr BM_ELEM_CD_PTR(ElemType e, int offset) -{ - return reinterpret_cast(BM_ELEM_CD_GET_VOID_P(e, offset)); -} - -extern "C" { -#endif - #define BM_ELEM_CD_SET_FLOAT(ele, offset, f) \ { \ CHECK_TYPE_NONCONST(ele); \ @@ -626,6 +632,17 @@ extern "C" { #define BM_ELEM_CD_GET_FLOAT_AS_UCHAR(ele, offset) \ (BLI_assert(offset != -1), (uchar)(BM_ELEM_CD_GET_FLOAT(ele, offset) * 255.0f)) +#ifdef __cplusplus +} + +template inline Tptr BM_ELEM_CD_PTR(ElemType e, int offset) +{ + return reinterpret_cast(BM_ELEM_CD_GET_VOID_P(e, offset)); +} + +extern "C" { +#endif + /* Forward declarations. */ #ifdef USE_BMESH_HOLES diff --git a/source/blender/bmesh/bmesh_idmap.h b/source/blender/bmesh/bmesh_idmap.h index dfe47efa257..28106209b3a 100644 --- a/source/blender/bmesh/bmesh_idmap.h +++ b/source/blender/bmesh/bmesh_idmap.h @@ -1,2 +1 @@ #include "intern/bmesh_idmap.h" - diff --git a/source/blender/bmesh/bmesh_log.h b/source/blender/bmesh/bmesh_log.h index bdb9106837b..84c4df8d6cd 100644 --- a/source/blender/bmesh/bmesh_log.h +++ b/source/blender/bmesh/bmesh_log.h @@ -1,4 +1,2 @@ -#pragma once - -#include "bmesh.h" #include "intern/bmesh_log_intern.h" + diff --git a/source/blender/bmesh/intern/bmesh_construct.cc b/source/blender/bmesh/intern/bmesh_construct.cc index d2f06489712..fcf2060a6f8 100644 --- a/source/blender/bmesh/intern/bmesh_construct.cc +++ b/source/blender/bmesh/intern/bmesh_construct.cc @@ -11,9 +11,6 @@ #include "MEM_guardedalloc.h" #include "BLI_alloca.h" -#include "BLI_array.h" -#include "BLI_bitmap.h" -#include "BLI_ghash.h" #include "BLI_math_vector.h" #include "BLI_sort_utils.h" @@ -336,14 +333,9 @@ static void bm_vert_attrs_copy( if ((mask_exclude & CD_MASK_NORMAL) == 0) { copy_v3_v3(v_dst->no, v_src->no); } - - CustomData_bmesh_free_block_data_exclude_by_type( - &bm_dst->vdata, v_dst->head.data, mask_exclude | CD_TOOLFLAGS); - CustomData_bmesh_copy_data_exclude_by_type(&bm_src->vdata, - &bm_dst->vdata, - v_src->head.data, - &v_dst->head.data, - mask_exclude | CD_TOOLFLAGS); + CustomData_bmesh_free_block_data_exclude_by_type(&bm_dst->vdata, v_dst->head.data, mask_exclude); + CustomData_bmesh_copy_data_exclude_by_type( + &bm_src->vdata, &bm_dst->vdata, v_src->head.data, &v_dst->head.data, mask_exclude); } static void bm_edge_attrs_copy( @@ -353,14 +345,9 @@ static void bm_edge_attrs_copy( BLI_assert_msg(0, "BMEdge: source and target match"); return; } - - CustomData_bmesh_free_block_data_exclude_by_type( - &bm_dst->edata, e_dst->head.data, mask_exclude | CD_TOOLFLAGS); - CustomData_bmesh_copy_data_exclude_by_type(&bm_src->edata, - &bm_dst->edata, - e_src->head.data, - &e_dst->head.data, - mask_exclude | CD_TOOLFLAGS); + CustomData_bmesh_free_block_data_exclude_by_type(&bm_dst->edata, e_dst->head.data, mask_exclude); + CustomData_bmesh_copy_data_exclude_by_type( + &bm_src->edata, &bm_dst->edata, e_src->head.data, &e_dst->head.data, mask_exclude); } static void bm_loop_attrs_copy( @@ -370,14 +357,9 @@ static void bm_loop_attrs_copy( BLI_assert_msg(0, "BMLoop: source and target match"); return; } - - CustomData_bmesh_free_block_data_exclude_by_type( - &bm_dst->ldata, l_dst->head.data, mask_exclude | CD_TOOLFLAGS); - CustomData_bmesh_copy_data_exclude_by_type(&bm_src->ldata, - &bm_dst->ldata, - l_src->head.data, - &l_dst->head.data, - mask_exclude | CD_TOOLFLAGS); + CustomData_bmesh_free_block_data_exclude_by_type(&bm_dst->ldata, l_dst->head.data, mask_exclude); + CustomData_bmesh_copy_data_exclude_by_type( + &bm_src->ldata, &bm_dst->ldata, l_src->head.data, &l_dst->head.data, mask_exclude); } static void bm_face_attrs_copy( @@ -390,14 +372,9 @@ static void bm_face_attrs_copy( if ((mask_exclude & CD_MASK_NORMAL) == 0) { copy_v3_v3(f_dst->no, f_src->no); } - - CustomData_bmesh_free_block_data_exclude_by_type( - &bm_dst->pdata, f_dst->head.data, mask_exclude | CD_TOOLFLAGS); - CustomData_bmesh_copy_data_exclude_by_type(&bm_src->pdata, - &bm_dst->pdata, - f_src->head.data, - &f_dst->head.data, - mask_exclude | CD_TOOLFLAGS); + CustomData_bmesh_free_block_data_exclude_by_type(&bm_dst->pdata, f_dst->head.data, mask_exclude); + CustomData_bmesh_copy_data_exclude_by_type( + &bm_src->pdata, &bm_dst->pdata, f_src->head.data, &f_dst->head.data, mask_exclude); f_dst->mat_nr = f_src->mat_nr; } @@ -438,32 +415,20 @@ void BM_elem_attrs_copy_ex(BMesh *bm_src, /* Copy specific attributes */ switch (ele_dst->htype) { case BM_VERT: - bm_vert_attrs_copy(bm_src, - bm_dst, - (const BMVert *)ele_src, - (BMVert *)ele_dst, - cd_mask_exclude | CD_MASK_TOOLFLAGS); + bm_vert_attrs_copy( + bm_src, bm_dst, (const BMVert *)ele_src, (BMVert *)ele_dst, cd_mask_exclude); break; case BM_EDGE: - bm_edge_attrs_copy(bm_src, - bm_dst, - (const BMEdge *)ele_src, - (BMEdge *)ele_dst, - cd_mask_exclude | CD_MASK_TOOLFLAGS); + bm_edge_attrs_copy( + bm_src, bm_dst, (const BMEdge *)ele_src, (BMEdge *)ele_dst, cd_mask_exclude); break; case BM_LOOP: - bm_loop_attrs_copy(bm_src, - bm_dst, - (const BMLoop *)ele_src, - (BMLoop *)ele_dst, - cd_mask_exclude | CD_MASK_TOOLFLAGS); + bm_loop_attrs_copy( + bm_src, bm_dst, (const BMLoop *)ele_src, (BMLoop *)ele_dst, cd_mask_exclude); break; case BM_FACE: - bm_face_attrs_copy(bm_src, - bm_dst, - (const BMFace *)ele_src, - (BMFace *)ele_dst, - cd_mask_exclude | CD_MASK_TOOLFLAGS); + bm_face_attrs_copy( + bm_src, bm_dst, (const BMFace *)ele_src, (BMFace *)ele_dst, cd_mask_exclude); break; default: BLI_assert(0); @@ -522,8 +487,6 @@ static BMFace *bm_mesh_copy_new_face( BM_elem_attrs_copy_ex(bm_old, bm_new, f, f_new, 0xff, 0x0); f_new->head.hflag = f->head.hflag; /* low level! don't do this for normal api use */ - bm_elem_check_toolflags(bm_new, (BMElem *)f); - j = 0; l_iter = l_first = BM_FACE_FIRST_LOOP(f_new); do { @@ -598,18 +561,6 @@ void BM_mesh_copy_init_customdata(BMesh *bm_dst, BMesh *bm_src, const BMAllocTem CustomData_copy_layout(&bm_src->ldata, &bm_dst->ldata, CD_MASK_BMESH.lmask, CD_SET_DEFAULT, 0); CustomData_copy_layout(&bm_src->pdata, &bm_dst->pdata, CD_MASK_BMESH.pmask, CD_SET_DEFAULT, 0); - CustomData *srcdatas[4] = {&bm_src->vdata, &bm_src->edata, &bm_src->ldata, &bm_src->pdata}; - CustomData *dstdatas[4] = {&bm_dst->vdata, &bm_dst->edata, &bm_dst->ldata, &bm_dst->pdata}; - - for (int i = 0; i < 4; i++) { - CustomData *srcdata = srcdatas[i]; - CustomData *dstdata = dstdatas[i]; - - if (CustomData_has_layer(srcdata, CD_TOOLFLAGS)) { - CustomData_add_layer(dstdata, CD_TOOLFLAGS, CD_SET_DEFAULT, 0); - } - } - CustomData_bmesh_init_pool(&bm_dst->vdata, allocsize->totvert, BM_VERT); CustomData_bmesh_init_pool(&bm_dst->edata, allocsize->totedge, BM_EDGE); CustomData_bmesh_init_pool(&bm_dst->ldata, allocsize->totloop, BM_LOOP); @@ -679,9 +630,6 @@ BMesh *BM_mesh_copy(BMesh *bm_old) /* copy between meshes so can't use 'example' argument */ v_new = BM_vert_create(bm_new, v->co, nullptr, BM_CREATE_SKIP_CD); BM_elem_attrs_copy_ex(bm_old, bm_new, v, v_new, 0xff, 0x0); - - bm_elem_check_toolflags(bm_new, (BMElem *)v_new); - v_new->head.hflag = v->head.hflag; /* low level! don't do this for normal api use */ vtable[i] = v_new; BM_elem_index_set(v, i); /* set_inline */ @@ -701,8 +649,6 @@ BMesh *BM_mesh_copy(BMesh *bm_old) BM_CREATE_SKIP_CD); BM_elem_attrs_copy_ex(bm_old, bm_new, e, e_new, 0xff, 0x0); - bm_elem_check_toolflags(bm_new, (BMElem *)e_new); - e_new->head.hflag = e->head.hflag; /* low level! don't do this for normal api use */ etable[i] = e_new; BM_elem_index_set(e, i); /* set_inline */ diff --git a/source/blender/bmesh/intern/bmesh_construct.h b/source/blender/bmesh/intern/bmesh_construct.h index bb784e87ef4..76a30c4a03a 100644 --- a/source/blender/bmesh/intern/bmesh_construct.h +++ b/source/blender/bmesh/intern/bmesh_construct.h @@ -172,8 +172,6 @@ void BM_mesh_copy_init_customdata_all_layers(BMesh *bm_dst, const struct BMAllocTemplate *allocsize); BMesh *BM_mesh_copy(BMesh *bm_old); -/* BM -> ME */ - #ifdef __cplusplus } #endif diff --git a/source/blender/bmesh/intern/bmesh_core.cc b/source/blender/bmesh/intern/bmesh_core.cc index f9363b6c637..be2ff534adf 100644 --- a/source/blender/bmesh/intern/bmesh_core.cc +++ b/source/blender/bmesh/intern/bmesh_core.cc @@ -8,20 +8,12 @@ * Core BMesh functions for adding, removing BMesh elements. */ -//#define FORCE_BMESH_CHECK - -#if !defined(NDEBUG) || defined(FORCE_BMESH_CHECK) -# define BMESH_DEBUG -#endif - #include "MEM_guardedalloc.h" #include "BLI_alloca.h" #include "BLI_array.h" -#include "BLI_asan.h" #include "BLI_linklist_stack.h" #include "BLI_math_vector.h" -#include "BLI_smallhash.h" #include "BLI_utildefines_stack.h" #include "BLT_translation.h" @@ -32,11 +24,7 @@ #include "BKE_mesh.hh" #include "bmesh.h" -#include "bmesh_log_intern.h" #include "intern/bmesh_private.h" -#include "range_tree.h" - -#include /* use so valgrinds memcheck alerts us when undefined index is used. * TESTING ONLY! */ @@ -52,43 +40,6 @@ #endif -void bm_elem_check_toolflags(BMesh *bm, BMElem *elem) -{ - int cd_off = -1; - MToolFlags *flags; - BLI_mempool *flagpool = NULL; - - switch (elem->head.htype) { - case BM_VERT: - cd_off = bm->vdata.typemap[CD_TOOLFLAGS]; - cd_off = cd_off != -1 ? bm->vdata.layers[cd_off].offset : -1; - flagpool = bm->vtoolflagpool; - break; - case BM_EDGE: - cd_off = bm->edata.typemap[CD_TOOLFLAGS]; - cd_off = cd_off != -1 ? bm->edata.layers[cd_off].offset : -1; - flagpool = bm->etoolflagpool; - break; - case BM_FACE: - cd_off = bm->pdata.typemap[CD_TOOLFLAGS]; - cd_off = cd_off != -1 ? bm->pdata.layers[cd_off].offset : -1; - flagpool = bm->ftoolflagpool; - break; - } - - if (cd_off == -1 || !flagpool) { - return; - } - - flags = BM_ELEM_CD_PTR(elem, cd_off); - if (!flags->flag) { - flags->flag = static_cast(BLI_mempool_calloc(flagpool)); - } -} - -/** - * \brief Main function for creating a new vertex. - */ BMVert *BM_vert_create(BMesh *bm, const float co[3], const BMVert *v_example, @@ -112,6 +63,12 @@ BMVert *BM_vert_create(BMesh *bm, v->head.hflag = 0; v->head.api_flag = 0; + /* allocate flags */ + if (bm->use_toolflags) { + ((BMVert_OFlag *)v)->oflags = static_cast( + bm->vtoolflagpool ? BLI_mempool_calloc(bm->vtoolflagpool) : nullptr); + } + /* 'v->no' is handled by BM_elem_attrs_copy */ if (co) { copy_v3_v3(v->co, co); @@ -149,7 +106,6 @@ BMVert *BM_vert_create(BMesh *bm, } else { CustomData_bmesh_set_default(&bm->vdata, &v->head.data); - zero_v3(v->no); } } @@ -162,13 +118,6 @@ BMVert *BM_vert_create(BMesh *bm, } } - /* allocate flags */ - if (bm->use_toolflags && v->head.data) { - int cd_tflags = bm->vdata.layers[bm->vdata.typemap[CD_TOOLFLAGS]].offset; - MToolFlags *flags = BM_ELEM_CD_PTR(v, cd_tflags); - flags->flag = static_cast(BLI_mempool_calloc(bm->vtoolflagpool)); - } - BM_CHECK_ELEMENT(v); return v; @@ -203,6 +152,12 @@ BMEdge *BM_edge_create( e->head.hflag = BM_ELEM_SMOOTH | BM_ELEM_DRAW; e->head.api_flag = 0; + /* allocate flags */ + if (bm->use_toolflags) { + ((BMEdge_OFlag *)e)->oflags = static_cast( + bm->etoolflagpool ? BLI_mempool_calloc(bm->etoolflagpool) : nullptr); + } + e->v1 = v1; e->v2 = v2; e->l = nullptr; @@ -229,13 +184,6 @@ BMEdge *BM_edge_create( } } - /* allocate flags */ - if (bm->use_toolflags && e->head.data) { - int cd_tflags = bm->edata.layers[bm->edata.typemap[CD_TOOLFLAGS]].offset; - MToolFlags *flags = BM_ELEM_CD_PTR(e, cd_tflags); - flags->flag = static_cast(BLI_mempool_calloc(bm->etoolflagpool)); - } - BM_CHECK_ELEMENT(e); return e; @@ -260,7 +208,7 @@ static BMLoop *bm_loop_create(BMesh *bm, BLI_assert((l_example == nullptr) || (l_example->head.htype == BM_LOOP)); BLI_assert(!(create_flag & 1)); -#ifdef BMESH_DEBUG +#ifndef NDEBUG if (l_example) { /* ensure passing a loop is either sharing the same vertex, or entirely disconnected * use to catch mistake passing in loop offset-by-one. */ @@ -383,13 +331,11 @@ BMFace *BM_face_copy( f_copy = BM_face_create(bm_dst, verts, edges, f->len, nullptr, BM_CREATE_SKIP_CD); BM_elem_attrs_copy(bm_src, bm_dst, f, f_copy); - bm_elem_check_toolflags(bm_dst, (BMElem *)f_copy); l_iter = l_first = BM_FACE_FIRST_LOOP(f); l_copy = BM_FACE_FIRST_LOOP(f_copy); do { BM_elem_attrs_copy(bm_src, bm_dst, l_iter, l_copy); - l_copy = l_copy->next; } while ((l_iter = l_iter->next) != l_first); @@ -420,6 +366,12 @@ BLI_INLINE BMFace *bm_face_create__internal(BMesh *bm) f->head.hflag = 0; f->head.api_flag = 0; + /* allocate flags */ + if (bm->use_toolflags) { + ((BMFace_OFlag *)f)->oflags = static_cast( + bm->ftoolflagpool ? BLI_mempool_calloc(bm->ftoolflagpool) : nullptr); + } + #ifdef USE_BMESH_HOLES BLI_listbase_clear(&f->loops); #else @@ -509,13 +461,6 @@ BMFace *BM_face_create(BMesh *bm, } } - /* allocate flags */ - if (bm->use_toolflags && f->head.data) { - int cd_tflags = bm->pdata.layers[bm->pdata.typemap[CD_TOOLFLAGS]].offset; - MToolFlags *flags = BM_ELEM_CD_PTR(f, cd_tflags); - flags->flag = static_cast(BLI_mempool_calloc(bm->ftoolflagpool)); - } - BM_CHECK_ELEMENT(f); return f; @@ -542,7 +487,7 @@ BMFace *BM_face_create_verts(BMesh *bm, return BM_face_create(bm, vert_arr, edge_arr, len, f_example, create_flag); } -#ifdef BMESH_DEBUG +#ifndef NDEBUG enum BMeshElemErrorFlag { IS_NULL = (1 << 0), @@ -582,7 +527,7 @@ ENUM_OPERATORS(BMeshElemErrorFlag, IS_FACE_WRONG_LENGTH) int bmesh_elem_check(void *element, const char htype) { BMHeader *head = static_cast(element); - BMeshInternalError err = IS_OK; + BMeshElemErrorFlag err = BMeshElemErrorFlag(0); if (!element) { return IS_NULL; @@ -759,18 +704,18 @@ int bmesh_elem_check(void *element, const char htype) break; } - // BMESH_ASSERT(err == 0); + BMESH_ASSERT(err == 0); return err; } -#endif /* BMESH_DEBUG */ +#endif /* NDEBUG */ /** * low level function, only frees the vert, * doesn't change or adjust surrounding geometry */ -void bm_kill_only_vert(BMesh *bm, BMVert *v) +static void bm_kill_only_vert(BMesh *bm, BMVert *v) { bm->totvert--; bm->elem_index_dirty |= BM_VERT; @@ -779,18 +724,13 @@ void bm_kill_only_vert(BMesh *bm, BMVert *v) BM_select_history_remove(bm, v); - if (bm->vtoolflagpool) { - MToolFlags *flags = BM_ELEM_CD_PTR( - v, bm->vdata.layers[bm->vdata.typemap[CD_TOOLFLAGS]].offset); - - BLI_mempool_free(bm->vtoolflagpool, flags->flag); - flags->flag = NULL; - } - if (v->head.data) { CustomData_bmesh_free_block(&bm->vdata, &v->head.data); } + if (bm->vtoolflagpool) { + BLI_mempool_free(bm->vtoolflagpool, ((BMVert_OFlag *)v)->oflags); + } BLI_mempool_free(bm->vpool, v); } @@ -798,7 +738,7 @@ void bm_kill_only_vert(BMesh *bm, BMVert *v) * low level function, only frees the edge, * doesn't change or adjust surrounding geometry */ -void bm_kill_only_edge(BMesh *bm, BMEdge *e) +static void bm_kill_only_edge(BMesh *bm, BMEdge *e) { bm->totedge--; bm->elem_index_dirty |= BM_EDGE; @@ -807,19 +747,13 @@ void bm_kill_only_edge(BMesh *bm, BMEdge *e) BM_select_history_remove(bm, (BMElem *)e); - if (bm->etoolflagpool) { - MToolFlags *flags = BM_ELEM_CD_PTR( - e, bm->edata.layers[bm->edata.typemap[CD_TOOLFLAGS]].offset); - - BLI_mempool_free(bm->etoolflagpool, flags->flag); - } - if (e->head.data) { CustomData_bmesh_free_block(&bm->edata, &e->head.data); } - e->l = NULL; - + if (bm->etoolflagpool) { + BLI_mempool_free(bm->etoolflagpool, ((BMEdge_OFlag *)e)->oflags); + } BLI_mempool_free(bm->epool, e); } @@ -827,7 +761,7 @@ void bm_kill_only_edge(BMesh *bm, BMEdge *e) * low level function, only frees the face, * doesn't change or adjust surrounding geometry */ -void bm_kill_only_face(BMesh *bm, BMFace *f) +static void bm_kill_only_face(BMesh *bm, BMFace *f) { if (bm->act_face == f) { bm->act_face = nullptr; @@ -840,17 +774,13 @@ void bm_kill_only_face(BMesh *bm, BMFace *f) BM_select_history_remove(bm, (BMElem *)f); - if (bm->ftoolflagpool) { - MToolFlags *flags = BM_ELEM_CD_PTR( - f, bm->pdata.layers[bm->pdata.typemap[CD_TOOLFLAGS]].offset); - - BLI_mempool_free(bm->ftoolflagpool, flags->flag); - } - if (f->head.data) { CustomData_bmesh_free_block(&bm->pdata, &f->head.data); } + if (bm->ftoolflagpool) { + BLI_mempool_free(bm->ftoolflagpool, ((BMFace_OFlag *)f)->oflags); + } BLI_mempool_free(bm->fpool, f); } @@ -858,7 +788,7 @@ void bm_kill_only_face(BMesh *bm, BMFace *f) * low level function, only frees the loop, * doesn't change or adjust surrounding geometry */ -void bm_kill_only_loop(BMesh *bm, BMLoop *l) +static void bm_kill_only_loop(BMesh *bm, BMLoop *l) { bm->totloop--; bm->elem_index_dirty |= BM_LOOP; @@ -911,7 +841,7 @@ void BM_face_kill(BMesh *bm, BMFace *f) BMLoopList *ls, *ls_next; #endif -#ifndef BMESH_DEBUG +#ifdef NDEBUG /* check length since we may be removing degenerate faces */ if (f->len >= 3) { BM_CHECK_ELEMENT(f); @@ -1101,7 +1031,7 @@ void bmesh_kernel_loop_reverse(BMesh *bm, /* step to next (now swapped) */ } while ((l_iter = l_iter->prev) != l_first); -#ifdef BMESH_DEBUG +#ifndef NDEBUG /* validate radial */ int i; for (i = 0, l_iter = l_first; i < f->len; i++, l_iter = l_iter->next) { @@ -1413,12 +1343,6 @@ static BMFace *bm_face_create__sfme(BMesh *bm, BMFace *f_example) BM_elem_attrs_copy(bm, bm, f_example, f); - if (bm->use_toolflags && f->head.data) { - int cd_tflags = bm->pdata.layers[bm->pdata.typemap[CD_TOOLFLAGS]].offset; - MToolFlags *flags = BM_ELEM_CD_PTR(f, cd_tflags); - flags->flag = static_cast(BLI_mempool_calloc(bm->ftoolflagpool)); - } - return f; } @@ -1566,7 +1490,7 @@ BMVert *bmesh_kernel_split_edge_make_vert(BMesh *bm, BMVert *tv, BMEdge *e, BMEd BMLoop *l_next; BMEdge *e_new; BMVert *v_new, *v_old; -#ifdef BMESH_DEBUG +#ifndef NDEBUG int valence1, valence2; bool edok; int i; @@ -1576,7 +1500,7 @@ BMVert *bmesh_kernel_split_edge_make_vert(BMesh *bm, BMVert *tv, BMEdge *e, BMEd v_old = BM_edge_other_vert(e, tv); -#ifdef BMESH_DEBUG +#ifndef NDEBUG valence1 = bmesh_disk_count(v_old); valence2 = bmesh_disk_count(tv); #endif @@ -1597,7 +1521,7 @@ BMVert *bmesh_kernel_split_edge_make_vert(BMesh *bm, BMVert *tv, BMEdge *e, BMEd /* add e_new to tv's disk cycle */ bmesh_disk_edge_append(e_new, tv); -#ifdef BMESH_DEBUG +#ifndef NDEBUG /* verify disk cycles */ edok = bmesh_disk_validate(valence1, v_old->e, v_old); BMESH_ASSERT(edok != false); @@ -1612,7 +1536,7 @@ BMVert *bmesh_kernel_split_edge_make_vert(BMesh *bm, BMVert *tv, BMEdge *e, BMEd e->l = nullptr; if (l_next) { BMLoop *l_new, *l; -#ifdef BMESH_DEBUG +#ifndef NDEBUG int radlen = bmesh_radial_length(l_next); #endif bool is_first = true; @@ -1660,7 +1584,7 @@ BMVert *bmesh_kernel_split_edge_make_vert(BMesh *bm, BMVert *tv, BMEdge *e, BMEd } } -#ifdef BMESH_DEBUG +#ifndef NDEBUG /* verify length of radial cycle */ edok = bmesh_radial_validate(radlen, e->l); BMESH_ASSERT(edok != false); @@ -1725,7 +1649,7 @@ BMEdge *bmesh_kernel_join_edge_kill_vert(BMesh *bm, BMEdge *e_old; BMVert *v_old, *v_target; BMLoop *l_kill; -#ifdef BMESH_DEBUG +#ifndef NDEBUG int radlen, i; bool edok; #endif @@ -1737,7 +1661,7 @@ BMEdge *bmesh_kernel_join_edge_kill_vert(BMesh *bm, } if (bmesh_disk_count_at_most(v_kill, 3) == 2) { -#ifdef BMESH_DEBUG +#ifndef NDEBUG int valence1, valence2; BMLoop *l; #endif @@ -1751,14 +1675,14 @@ BMEdge *bmesh_kernel_join_edge_kill_vert(BMesh *bm, return nullptr; } - BMEdge *e_splice = NULL; + BMEdge *e_splice; BLI_SMALLSTACK_DECLARE(faces_degenerate, BMFace *); BMLoop *l_kill_next; /* Candidates for being duplicate. */ BLI_SMALLSTACK_DECLARE(faces_duplicate_candidate, BMFace *); -#ifdef BMESH_DEBUG +#ifndef NDEBUG /* For verification later, count valence of 'v_old' and 'v_target' */ valence1 = bmesh_disk_count(v_old); valence2 = bmesh_disk_count(v_target); @@ -1773,7 +1697,7 @@ BMEdge *bmesh_kernel_join_edge_kill_vert(BMesh *bm, /* remove e_kill from 'v_target's disk cycle */ bmesh_disk_edge_remove(e_kill, v_target); -#ifdef BMESH_DEBUG +#ifndef NDEBUG /* deal with radial cycle of e_kill */ radlen = bmesh_radial_length(e_kill->l); #endif @@ -1810,8 +1734,8 @@ BMEdge *bmesh_kernel_join_edge_kill_vert(BMesh *bm, bm_kill_only_loop(bm, l_kill); } while ((l_kill = l_kill_next) != e_kill->l); - /* `e_kill->l` is invalid but the edge is freed next. */ -#ifdef BMESH_DEBUG +/* `e_kill->l` is invalid but the edge is freed next. */ +#ifndef NDEBUG /* Validate radial cycle of e_old */ edok = bmesh_radial_validate(radlen, e_old->l); BMESH_ASSERT(edok != false); @@ -1828,7 +1752,7 @@ BMEdge *bmesh_kernel_join_edge_kill_vert(BMesh *bm, v_kill->e = nullptr; } -#ifdef BMESH_DEBUG +#ifndef NDEBUG /* Validate disk cycle lengths of 'v_old', 'v_target' are unchanged */ edok = bmesh_disk_validate(valence1, v_old->e, v_old); BMESH_ASSERT(edok != false); @@ -1881,71 +1805,6 @@ BMEdge *bmesh_kernel_join_edge_kill_vert(BMesh *bm, return nullptr; } -#ifdef _ -# undef _ -#endif - -#define _(s) \ - case s: \ - return #s; - -static const char *get_err_code_str(BMeshInternalError code) -{ - switch (code) { - _(IS_OK) - _(IS_NULL) - _(IS_WRONG_TYPE) - - _(IS_VERT_WRONG_EDGE_TYPE) - - _(IS_EDGE_NULL_DISK_LINK) - _(IS_EDGE_WRONG_LOOP_TYPE) - _(IS_EDGE_WRONG_FACE_TYPE) - _(IS_EDGE_NULL_RADIAL_LINK) - _(IS_EDGE_ZERO_FACE_LENGTH) - - _(IS_LOOP_WRONG_FACE_TYPE) - _(IS_LOOP_WRONG_EDGE_TYPE) - _(IS_LOOP_WRONG_VERT_TYPE) - _(IS_LOOP_VERT_NOT_IN_EDGE) - _(IS_LOOP_NULL_CYCLE_LINK) - _(IS_LOOP_ZERO_FACE_LENGTH) - _(IS_LOOP_WRONG_FACE_LENGTH) - _(IS_LOOP_WRONG_RADIAL_LENGTH) - - _(IS_FACE_NULL_LOOP) - _(IS_FACE_WRONG_LOOP_FACE) - _(IS_FACE_NULL_EDGE) - _(IS_FACE_NULL_VERT) - _(IS_FACE_LOOP_VERT_NOT_IN_EDGE) - _(IS_FACE_LOOP_WRONG_RADIAL_LENGTH) - _(IS_FACE_LOOP_WRONG_DISK_LENGTH) - _(IS_FACE_LOOP_DUPE_LOOP) - _(IS_FACE_LOOP_DUPE_VERT) - _(IS_FACE_LOOP_DUPE_EDGE) - _(IS_FACE_WRONG_LENGTH) - } - - return "(unknown-code)"; -} -#undef _ - -const char *bm_get_error_str(int err) -{ - static char buf[1024]; - buf[0] = 0; - - for (int i = 0; i < 27; i++) { - if (err & (1 << i)) { - strcat(buf, get_err_code_str(BMeshInternalError(1 << i))); - strcat(buf, "|"); - } - } - - return buf; -} - -/*original version of bmesh_kernel_join_vert_kill_edge*/ BMVert *bmesh_kernel_join_vert_kill_edge_fast(BMesh *bm, BMEdge *e_kill, BMVert *v_kill, @@ -1981,7 +1840,6 @@ BMVert *bmesh_kernel_join_vert_kill_edge_fast(BMesh *bm, BLI_SMALLSTACK_PUSH(faces_degenerate, l_kill->f); } } - l_kill_next = l_kill->radial_next; bm_kill_only_loop(bm, l_kill); @@ -1999,7 +1857,7 @@ BMVert *bmesh_kernel_join_vert_kill_edge_fast(BMesh *bm, /* inline BM_vert_splice(bm, v_target, v_kill); */ BMEdge *e; while ((e = v_kill->e)) { - BMEdge *e_target = NULL; + BMEdge *e_target; if (check_edge_exists) { e_target = BM_edge_exists(v_target, BM_edge_other_vert(e, v_kill)); @@ -2028,8 +1886,6 @@ BMVert *bmesh_kernel_join_vert_kill_edge_fast(BMesh *bm, bm_kill_only_vert(bm, v_kill); } - BM_CHECK_ELEMENT(v_target); - return v_target; } @@ -2131,10 +1987,7 @@ BMFace *bmesh_kernel_join_face_kill_edge(BMesh *bm, BMFace *f1, BMFace *f2, BMEd /* deallocate edge and its two loops as well as f2 */ if (bm->etoolflagpool) { - MToolFlags *flags = BM_ELEM_CD_PTR( - l_f1->e, bm->edata.layers[bm->edata.typemap[CD_TOOLFLAGS]].offset); - - BLI_mempool_free(bm->etoolflagpool, flags->flag); + BLI_mempool_free(bm->etoolflagpool, ((BMEdge_OFlag *)l_f1->e)->oflags); } BLI_mempool_free(bm->epool, l_f1->e); bm->totedge--; @@ -2142,14 +1995,9 @@ BMFace *bmesh_kernel_join_face_kill_edge(BMesh *bm, BMFace *f1, BMFace *f2, BMEd bm->totloop--; BLI_mempool_free(bm->lpool, l_f2); bm->totloop--; - if (bm->ftoolflagpool) { - MToolFlags *flags = BM_ELEM_CD_PTR( - f2, bm->pdata.layers[bm->pdata.typemap[CD_TOOLFLAGS]].offset); - - BLI_mempool_free(bm->ftoolflagpool, flags->flag); + BLI_mempool_free(bm->ftoolflagpool, ((BMFace_OFlag *)f2)->oflags); } - BLI_mempool_free(bm->fpool, f2); bm->totface--; /* account for both above */ @@ -2491,16 +2339,6 @@ void BM_vert_separate_tested_edges( /** \} */ -/** - * \brief Splice Edge - * - * Splice two unique edges which share the same two vertices into one edge. - * (\a e_src into \a e_dst, removing e_src). - * - * \return Success - * - * \note Edges must already have the same vertices. - */ bool BM_edge_splice(BMesh *bm, BMEdge *e_dst, BMEdge *e_src, bool combine_flags) { BMLoop *l; @@ -2529,8 +2367,9 @@ bool BM_edge_splice(BMesh *bm, BMEdge *e_dst, BMEdge *e_src, bool combine_flags) BM_CHECK_ELEMENT(e_dst); if (combine_flags) { - /* sharp flag is inverted to BM_ELEM_SMOOTH, which we - must take into account*/ + /* Sharp flag is inverted to BM_ELEM_SMOOTH which we + * must take into account. + */ if (!(e_dst->head.hflag & BM_ELEM_SMOOTH) || !(e_src->head.hflag & BM_ELEM_SMOOTH)) { e_dst->head.hflag = (e_dst->head.hflag | e_src->head.hflag) & ~BM_ELEM_SMOOTH; @@ -2549,7 +2388,7 @@ bool BM_edge_splice(BMesh *bm, BMEdge *e_dst, BMEdge *e_src, bool combine_flags) void bmesh_kernel_edge_separate(BMesh *bm, BMEdge *e, BMLoop *l_sep, const bool copy_select) { BMEdge *e_new; -#ifdef BMESH_DEBUG +#ifndef NDEBUG const int radlen = bmesh_radial_length(e->l); #endif @@ -2626,7 +2465,7 @@ BMVert *bmesh_kernel_unglue_region_make_vert(BMesh *bm, BMLoop *l_sep) edges[0] = l_sep->e; edges[1] = l_sep->prev->e; - for (i = 0; i < (int)ARRAY_SIZE(edges); i++) { + for (i = 0; i < ARRAY_SIZE(edges); i++) { BMEdge *e = edges[i]; bmesh_edge_vert_swap(e, v_new, v_sep); } @@ -2672,7 +2511,7 @@ BMVert *bmesh_kernel_unglue_region_make_vert_multi(BMesh *bm, BMLoop **larr, int BM_ELEM_API_FLAG_ENABLE(l_sep->prev, LOOP_VISIT); BMLoop *loop_pair[2] = {l_sep, l_sep->prev}; - for (int j = 0; j < (int)ARRAY_SIZE(loop_pair); j++) { + for (int j = 0; j < ARRAY_SIZE(loop_pair); j++) { BMEdge *e = loop_pair[j]->e; if (!BM_ELEM_API_FLAG_TEST(e, EDGE_VISIT)) { BM_ELEM_API_FLAG_ENABLE(e, EDGE_VISIT); @@ -2729,7 +2568,7 @@ BMVert *bmesh_kernel_unglue_region_make_vert_multi(BMesh *bm, BMLoop **larr, int else { v_new = BM_vert_create(bm, v_sep->co, v_sep, BM_CREATE_NOP); - for (i = 0; i < (int)STACK_SIZE(edges); i++) { + for (i = 0; i < STACK_SIZE(edges); i++) { BMEdge *e = edges[i]; BMLoop *l_iter, *l_first, *l_next; BMEdge *e_new; diff --git a/source/blender/bmesh/intern/bmesh_core.h b/source/blender/bmesh/intern/bmesh_core.h index 806f2eca8ca..1d87adfdea7 100644 --- a/source/blender/bmesh/intern/bmesh_core.h +++ b/source/blender/bmesh/intern/bmesh_core.h @@ -23,13 +23,9 @@ typedef enum eBMCreateFlag { * use if we immediately write custom-data into the element so this skips copying from 'example' * arguments or setting defaults, speeds up conversion when data is converted all at once. */ - BM_CREATE_SKIP_CD = (1 << 2), /* if true, you must call bm_elem_check_toolflags(bm, elem) later - if toolflags are on */ + BM_CREATE_SKIP_CD = (1 << 2), } eBMCreateFlag; -/* if toolflags are enabled, checks that internal pointer to toolflags it not null */ -void bm_elem_check_toolflags(BMesh *bm, BMElem *elem); - /** * \brief Main function for creating a new vertex. */ diff --git a/source/blender/bmesh/intern/bmesh_interp.cc b/source/blender/bmesh/intern/bmesh_interp.cc index f56d2a498ae..128f940e6ee 100644 --- a/source/blender/bmesh/intern/bmesh_interp.cc +++ b/source/blender/bmesh/intern/bmesh_interp.cc @@ -36,29 +36,8 @@ using blender::float4; using blender::IndexRange; using blender::Vector; -static void copy_cdata_simple(BMesh * /*bm*/, - CustomData *data_layer, - BMElem *ele_dst, - const BMElem *ele_src) -{ - int cd_tflags; - MToolFlags saved_tflags = {0}; - ; - if ((cd_tflags = CustomData_get_offset(data_layer, CD_TOOLFLAGS)) != -1) { - saved_tflags = *(MToolFlags *)BM_ELEM_CD_GET_VOID_P(ele_dst, cd_tflags); - } - - CustomData_bmesh_free_block_data(data_layer, ele_dst->head.data); - CustomData_bmesh_copy_data(data_layer, data_layer, ele_src->head.data, &ele_dst->head.data); - - if (cd_tflags != -1) { - *(MToolFlags *)BM_ELEM_CD_GET_VOID_P(ele_dst, cd_tflags) = saved_tflags; - } -} - /* edge and vertex share, currently there's no need to have different logic */ -static void bm_data_interp_from_elem(BMesh *bm, - CustomData *data_layer, +static void bm_data_interp_from_elem(CustomData *data_layer, const BMElem *ele_src_1, const BMElem *ele_src_2, BMElem *ele_dst, @@ -71,7 +50,9 @@ static void bm_data_interp_from_elem(BMesh *bm, /* do nothing */ } else { - copy_cdata_simple(bm, data_layer, ele_dst, ele_src_1); + CustomData_bmesh_free_block_data(data_layer, ele_dst->head.data); + CustomData_bmesh_copy_data( + data_layer, data_layer, ele_src_1->head.data, &ele_dst->head.data); } } else if (fac >= 1.0f) { @@ -79,7 +60,9 @@ static void bm_data_interp_from_elem(BMesh *bm, /* do nothing */ } else { - copy_cdata_simple(bm, data_layer, ele_dst, ele_src_2); + CustomData_bmesh_free_block_data(data_layer, ele_dst->head.data); + CustomData_bmesh_copy_data( + data_layer, data_layer, ele_src_2->head.data, &ele_dst->head.data); } } else { @@ -99,14 +82,14 @@ void BM_data_interp_from_verts( BMesh *bm, const BMVert *v_src_1, const BMVert *v_src_2, BMVert *v_dst, const float fac) { bm_data_interp_from_elem( - bm, &bm->vdata, (const BMElem *)v_src_1, (const BMElem *)v_src_2, (BMElem *)v_dst, fac); + &bm->vdata, (const BMElem *)v_src_1, (const BMElem *)v_src_2, (BMElem *)v_dst, fac); } void BM_data_interp_from_edges( BMesh *bm, const BMEdge *e_src_1, const BMEdge *e_src_2, BMEdge *e_dst, const float fac) { bm_data_interp_from_elem( - bm, &bm->edata, (const BMElem *)e_src_1, (const BMElem *)e_src_2, (BMElem *)e_dst, fac); + &bm->edata, (const BMElem *)e_src_1, (const BMElem *)e_src_2, (BMElem *)e_dst, fac); } /** diff --git a/source/blender/bmesh/intern/bmesh_marking.cc b/source/blender/bmesh/intern/bmesh_marking.cc index eced6eec881..26067140a95 100644 --- a/source/blender/bmesh/intern/bmesh_marking.cc +++ b/source/blender/bmesh/intern/bmesh_marking.cc @@ -154,7 +154,6 @@ static bool recount_totsels_are_ok(BMesh *bm) static bool bm_vert_is_edge_select_any_other(const BMVert *v, const BMEdge *e_first) { const BMEdge *e_iter = e_first; - int i = 0; /* start by stepping over the current edge */ while ((e_iter = bmesh_disk_edge_next(e_iter, v)) != e_first) { @@ -162,7 +161,6 @@ static bool bm_vert_is_edge_select_any_other(const BMVert *v, const BMEdge *e_fi return true; } } - return false; } diff --git a/source/blender/bmesh/intern/bmesh_mesh.cc b/source/blender/bmesh/intern/bmesh_mesh.cc index 3da628598e8..8074eb3faef 100644 --- a/source/blender/bmesh/intern/bmesh_mesh.cc +++ b/source/blender/bmesh/intern/bmesh_mesh.cc @@ -13,32 +13,21 @@ #include "DNA_listBase.h" #include "DNA_scene_types.h" -#include "BLI_alloca.h" #include "BLI_listbase.h" #include "BLI_math_matrix.h" #include "BLI_math_vector.h" -#include "BLI_rand.h" #include "BLI_utildefines.h" -#include "BLI_vector.hh" #include "BKE_customdata.h" #include "BKE_mesh.hh" -#include "DNA_meshdata_types.h" - #include "bmesh.h" -#include "bmesh_private.h" -#include "range_tree.h" - -using blender::Vector; const BMAllocTemplate bm_mesh_allocsize_default = {512, 1024, 2048, 512}; const BMAllocTemplate bm_mesh_chunksize_default = {512, 1024, 2048, 512}; -static void bm_alloc_toolflags(BMesh *bm); - static void bm_mempool_init_ex(const BMAllocTemplate *allocsize, - const bool /*use_toolflags*/, + const bool use_toolflags, BLI_mempool **r_vpool, BLI_mempool **r_epool, BLI_mempool **r_lpool, @@ -46,10 +35,18 @@ static void bm_mempool_init_ex(const BMAllocTemplate *allocsize, { size_t vert_size, edge_size, loop_size, face_size; - vert_size = sizeof(BMVert); - edge_size = sizeof(BMEdge); - loop_size = sizeof(BMLoop); - face_size = sizeof(BMFace); + if (use_toolflags == true) { + vert_size = sizeof(BMVert_OFlag); + edge_size = sizeof(BMEdge_OFlag); + loop_size = sizeof(BMLoop); + face_size = sizeof(BMFace_OFlag); + } + else { + vert_size = sizeof(BMVert); + edge_size = sizeof(BMEdge); + loop_size = sizeof(BMLoop); + face_size = sizeof(BMFace); + } if (r_vpool) { *r_vpool = BLI_mempool_create( @@ -61,7 +58,7 @@ static void bm_mempool_init_ex(const BMAllocTemplate *allocsize, } if (r_lpool) { *r_lpool = BLI_mempool_create( - loop_size, allocsize->totloop, bm_mesh_chunksize_default.totloop, BLI_MEMPOOL_ALLOW_ITER); + loop_size, allocsize->totloop, bm_mesh_chunksize_default.totloop, BLI_MEMPOOL_NOP); } if (r_fpool) { *r_fpool = BLI_mempool_create( @@ -82,16 +79,6 @@ void BM_mesh_elem_toolflags_ensure(BMesh *bm) { BLI_assert(bm->use_toolflags); - if (!CustomData_has_layer(&bm->vdata, CD_TOOLFLAGS)) { - if (bm->vtoolflagpool) { - printf("%s: Error: toolflags were deallocated improperly\n", __func__); - - BM_mesh_elem_toolflags_clear(bm); - - bm_alloc_toolflags_cdlayers(bm, true); - } - } - if (bm->vtoolflagpool && bm->etoolflagpool && bm->ftoolflagpool) { return; } @@ -100,15 +87,30 @@ void BM_mesh_elem_toolflags_ensure(BMesh *bm) bm->etoolflagpool = BLI_mempool_create(sizeof(BMFlagLayer), bm->totedge, 512, BLI_MEMPOOL_NOP); bm->ftoolflagpool = BLI_mempool_create(sizeof(BMFlagLayer), bm->totface, 512, BLI_MEMPOOL_NOP); - bm_alloc_toolflags(bm); + BMIter iter; + BMVert_OFlag *v_olfag; + BLI_mempool *toolflagpool = bm->vtoolflagpool; + BM_ITER_MESH (v_olfag, &iter, bm, BM_VERTS_OF_MESH) { + v_olfag->oflags = static_cast(BLI_mempool_calloc(toolflagpool)); + } + + BMEdge_OFlag *e_olfag; + toolflagpool = bm->etoolflagpool; + BM_ITER_MESH (e_olfag, &iter, bm, BM_EDGES_OF_MESH) { + e_olfag->oflags = static_cast(BLI_mempool_calloc(toolflagpool)); + } + + BMFace_OFlag *f_olfag; + toolflagpool = bm->ftoolflagpool; + BM_ITER_MESH (f_olfag, &iter, bm, BM_FACES_OF_MESH) { + f_olfag->oflags = static_cast(BLI_mempool_calloc(toolflagpool)); + } bm->totflags = 1; } void BM_mesh_elem_toolflags_clear(BMesh *bm) { - bool haveflags = bm->vtoolflagpool || bm->etoolflagpool || bm->ftoolflagpool; - if (bm->vtoolflagpool) { BLI_mempool_destroy(bm->vtoolflagpool); bm->vtoolflagpool = nullptr; @@ -121,12 +123,6 @@ void BM_mesh_elem_toolflags_clear(BMesh *bm) BLI_mempool_destroy(bm->ftoolflagpool); bm->ftoolflagpool = nullptr; } - - if (haveflags) { - BM_data_layer_free(bm, &bm->vdata, CD_TOOLFLAGS); - BM_data_layer_free(bm, &bm->edata, CD_TOOLFLAGS); - BM_data_layer_free(bm, &bm->pdata, CD_TOOLFLAGS); - } } BMesh *BM_mesh_create(const BMAllocTemplate *allocsize, const BMeshCreateParams *params) @@ -147,32 +143,6 @@ BMesh *BM_mesh_create(const BMAllocTemplate *allocsize, const BMeshCreateParams CustomData_reset(&bm->ldata); CustomData_reset(&bm->pdata); - bool init_cdata_pools = false; - - if (bm->use_toolflags) { - init_cdata_pools = true; - bm_alloc_toolflags_cdlayers(bm, false); - } - - if (init_cdata_pools) { - if (bm->vdata.totlayer) { - CustomData_bmesh_init_pool(&bm->vdata, 0, BM_VERT); - } - if (bm->edata.totlayer) { - CustomData_bmesh_init_pool(&bm->edata, 0, BM_EDGE); - } - if (bm->ldata.totlayer) { - CustomData_bmesh_init_pool(&bm->ldata, 0, BM_LOOP); - } - if (bm->pdata.totlayer) { - CustomData_bmesh_init_pool(&bm->pdata, 0, BM_FACE); - } - } - - if (bm->use_toolflags) { - BM_mesh_elem_toolflags_ensure(bm); - } - return bm; } @@ -250,20 +220,8 @@ void BM_mesh_data_free(BMesh *bm) MEM_freeN(bm->ftable); } - /* destroy flag pools */ - - if (bm->vtoolflagpool) { - BLI_mempool_destroy(bm->vtoolflagpool); - bm->vtoolflagpool = nullptr; - } - if (bm->etoolflagpool) { - BLI_mempool_destroy(bm->etoolflagpool); - bm->etoolflagpool = nullptr; - } - if (bm->ftoolflagpool) { - BLI_mempool_destroy(bm->ftoolflagpool); - bm->ftoolflagpool = nullptr; - } + /* destroy flag pool */ + BM_mesh_elem_toolflags_clear(bm); #ifdef USE_BMESH_HOLES BLI_mempool_destroy(bm->looplistpool); @@ -313,6 +271,7 @@ void BM_mesh_free(BMesh *bm) MEM_freeN(bm); } + void bmesh_edit_begin(BMesh * /*bm*/, BMOpTypeFlag /*type_flag*/) { /* Most operators seem to be using BMO_OPTYPE_FLAG_UNTAN_MULTIRES to change the MDisps to @@ -1107,10 +1066,10 @@ void BM_mesh_rebuild(BMesh *bm, BMVert *v_dst = static_cast(BLI_mempool_alloc(vpool_dst)); memcpy(v_dst, v_src, sizeof(BMVert)); if (use_toolflags) { - MToolFlags *flags = (MToolFlags *)BM_ELEM_CD_GET_VOID_P( - v_dst, bm->vdata.layers[bm->vdata.typemap[CD_TOOLFLAGS]].offset); - - flags->flag = bm->vtoolflagpool ? (short *)BLI_mempool_calloc(bm->vtoolflagpool) : nullptr; + ((BMVert_OFlag *)v_dst)->oflags = bm->vtoolflagpool ? + static_cast( + BLI_mempool_calloc(bm->vtoolflagpool)) : + nullptr; } vtable_dst[index] = v_dst; @@ -1126,10 +1085,10 @@ void BM_mesh_rebuild(BMesh *bm, BMEdge *e_dst = static_cast(BLI_mempool_alloc(epool_dst)); memcpy(e_dst, e_src, sizeof(BMEdge)); if (use_toolflags) { - MToolFlags *flags = (MToolFlags *)BM_ELEM_CD_GET_VOID_P( - e_dst, bm->edata.layers[bm->edata.typemap[CD_TOOLFLAGS]].offset); - - flags->flag = bm->etoolflagpool ? (short *)BLI_mempool_calloc(bm->etoolflagpool) : nullptr; + ((BMEdge_OFlag *)e_dst)->oflags = bm->etoolflagpool ? + static_cast( + BLI_mempool_calloc(bm->etoolflagpool)) : + nullptr; } etable_dst[index] = e_dst; @@ -1146,13 +1105,11 @@ void BM_mesh_rebuild(BMesh *bm, if (remap & BM_FACE) { BMFace *f_dst = static_cast(BLI_mempool_alloc(fpool_dst)); memcpy(f_dst, f_src, sizeof(BMFace)); - if (use_toolflags) { - MToolFlags *flags = (MToolFlags *)BM_ELEM_CD_GET_VOID_P( - f_dst, bm->pdata.layers[bm->pdata.typemap[CD_TOOLFLAGS]].offset); - - flags->flag = bm->ftoolflagpool ? (short *)BLI_mempool_calloc(bm->ftoolflagpool) : - nullptr; + ((BMFace_OFlag *)f_dst)->oflags = bm->ftoolflagpool ? + static_cast( + BLI_mempool_calloc(bm->ftoolflagpool)) : + nullptr; } ftable_dst[index] = f_dst; @@ -1334,72 +1291,20 @@ void BM_mesh_rebuild(BMesh *bm, } } -void bm_alloc_toolflags_cdlayers(BMesh *bm, bool set_elems) -{ - CustomData *cdatas[3] = {&bm->vdata, &bm->edata, &bm->pdata}; - int iters[3] = {BM_VERTS_OF_MESH, BM_EDGES_OF_MESH, BM_FACES_OF_MESH}; - - for (int i = 0; i < 3; i++) { - CustomData *cdata = cdatas[i]; - int cd_tflags = CustomData_get_offset(cdata, CD_TOOLFLAGS); - - if (cd_tflags == -1) { - if (set_elems) { - BM_data_layer_add(bm, cdata, CD_TOOLFLAGS); - } - else { - CustomData_add_layer(cdata, CD_TOOLFLAGS, CD_SET_DEFAULT, 0); - } - - int idx = CustomData_get_layer_index(cdata, CD_TOOLFLAGS); - - cdata->layers[idx].flag |= CD_FLAG_TEMPORARY | CD_FLAG_NOCOPY | CD_FLAG_ELEM_NOCOPY; - cd_tflags = cdata->layers[idx].offset; - - if (set_elems) { - BMIter iter; - BMElem *elem; - - BM_ITER_MESH (elem, &iter, bm, iters[i]) { - MToolFlags *flags = (MToolFlags *)BM_ELEM_CD_GET_VOID_P(elem, cd_tflags); - - flags->flag = nullptr; - } - } - } - } -} - -static void bm_alloc_toolflags(BMesh *bm) -{ - bm_alloc_toolflags_cdlayers(bm, true); - - CustomData *cdatas[3] = {&bm->vdata, &bm->edata, &bm->pdata}; - BLI_mempool *flagpools[3] = {bm->vtoolflagpool, bm->etoolflagpool, bm->ftoolflagpool}; - BLI_mempool *elempools[3] = {bm->vpool, bm->epool, bm->fpool}; - - for (int i = 0; i < 3; i++) { - CustomData *cdata = cdatas[i]; - int cd_tflags = CustomData_get_offset(cdata, CD_TOOLFLAGS); - - BLI_mempool_iter iter; - BLI_mempool_iternew(elempools[i], &iter); - BMElem *elem = (BMElem *)BLI_mempool_iterstep(&iter); - - for (; elem; elem = (BMElem *)BLI_mempool_iterstep(&iter)) { - MToolFlags *flags = (MToolFlags *)BM_ELEM_CD_GET_VOID_P(elem, cd_tflags); - - flags->flag = (short *)BLI_mempool_calloc(flagpools[i]); - } - } -} - void BM_mesh_toolflags_set(BMesh *bm, bool use_toolflags) { if (bm->use_toolflags == use_toolflags) { return; } + const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_BM(bm); + + BLI_mempool *vpool_dst = nullptr; + BLI_mempool *epool_dst = nullptr; + BLI_mempool *fpool_dst = nullptr; + + bm_mempool_init_ex(&allocsize, use_toolflags, &vpool_dst, &epool_dst, nullptr, &fpool_dst); + if (use_toolflags == false) { BLI_mempool_destroy(bm->vtoolflagpool); BLI_mempool_destroy(bm->etoolflagpool); @@ -1408,20 +1313,13 @@ void BM_mesh_toolflags_set(BMesh *bm, bool use_toolflags) bm->vtoolflagpool = nullptr; bm->etoolflagpool = nullptr; bm->ftoolflagpool = nullptr; + } + BMeshCreateParams params = {}; + params.use_toolflags = use_toolflags; - BM_data_layer_free(bm, &bm->vdata, CD_TOOLFLAGS); - BM_data_layer_free(bm, &bm->edata, CD_TOOLFLAGS); - BM_data_layer_free(bm, &bm->pdata, CD_TOOLFLAGS); - } - else { - bm_alloc_toolflags_cdlayers(bm, true); - } + BM_mesh_rebuild(bm, ¶ms, vpool_dst, epool_dst, nullptr, fpool_dst); bm->use_toolflags = use_toolflags; - - if (use_toolflags) { - BM_mesh_elem_toolflags_ensure(bm); - } } /* -------------------------------------------------------------------- */ diff --git a/source/blender/bmesh/intern/bmesh_mesh.h b/source/blender/bmesh/intern/bmesh_mesh.h index 31f50425560..56c4146241c 100644 --- a/source/blender/bmesh/intern/bmesh_mesh.h +++ b/source/blender/bmesh/intern/bmesh_mesh.h @@ -8,7 +8,6 @@ * \ingroup bmesh */ -#include "BLI_compiler_compat.h" #include "bmesh_class.h" #ifdef __cplusplus @@ -210,6 +209,7 @@ void BM_mesh_vert_coords_apply_with_mat4(BMesh *bm, const float (*vert_coords)[3], const float mat[4][4]); +/* Returns true if elem is freed. ASAN-safe. */ bool BM_elem_is_free(BMElem *elem, int htype); #ifdef __cplusplus diff --git a/source/blender/bmesh/intern/bmesh_mesh_convert.cc b/source/blender/bmesh/intern/bmesh_mesh_convert.cc index f922ca59ab0..ea2a7c8d2b5 100644 --- a/source/blender/bmesh/intern/bmesh_mesh_convert.cc +++ b/source/blender/bmesh/intern/bmesh_mesh_convert.cc @@ -350,10 +350,6 @@ void BM_mesh_bm_from_me(BMesh *bm, const Mesh *me, const BMeshFromMeshParams *pa restore_cd_copy_flags(bmesh_domains, nocopy_layers); } - if (bm->use_toolflags) { - bm_alloc_toolflags_cdlayers(bm, true); - } - return; } @@ -465,21 +461,6 @@ void BM_mesh_bm_from_me(BMesh *bm, const Mesh *me, const BMeshFromMeshParams *pa } } - if (bm->use_toolflags) { - bm_alloc_toolflags_cdlayers(bm, !is_new); - - if (!bm->vtoolflagpool) { - bm->vtoolflagpool = BLI_mempool_create( - sizeof(BMFlagLayer), bm->totvert, 512, BLI_MEMPOOL_NOP); - bm->etoolflagpool = BLI_mempool_create( - sizeof(BMFlagLayer), bm->totedge, 512, BLI_MEMPOOL_NOP); - bm->ftoolflagpool = BLI_mempool_create( - sizeof(BMFlagLayer), bm->totface, 512, BLI_MEMPOOL_NOP); - - bm->totflags = 1; - } - } - const Vector vert_info = mesh_to_bm_copy_info_calc(mesh_vdata, bm->vdata); const Vector edge_info = mesh_to_bm_copy_info_calc(mesh_edata, bm->edata); const Vector poly_info = mesh_to_bm_copy_info_calc(mesh_pdata, bm->pdata); @@ -540,8 +521,6 @@ void BM_mesh_bm_from_me(BMesh *bm, const Mesh *me, const BMeshFromMeshParams *pa mesh_attributes_copy_to_bmesh_block(bm->vdata, vert_info, i, v->head); - bm_elem_check_toolflags(bm, reinterpret_cast(v)); - /* Set shape key original index. */ if (cd_shape_keyindex_offset != -1) { BM_ELEM_CD_SET_INT(v, cd_shape_keyindex_offset, i); @@ -582,7 +561,6 @@ void BM_mesh_bm_from_me(BMesh *bm, const Mesh *me, const BMeshFromMeshParams *pa /* Copy Custom Data */ mesh_attributes_copy_to_bmesh_block(bm->edata, edge_info, i, e->head); - bm_elem_check_toolflags(bm, reinterpret_cast(e)); } if (is_new) { bm->elem_index_dirty &= ~BM_EDGE; /* Added in order, clear dirty flag. */ @@ -649,7 +627,6 @@ void BM_mesh_bm_from_me(BMesh *bm, const Mesh *me, const BMeshFromMeshParams *pa } while ((l_iter = l_iter->next) != l_first); mesh_attributes_copy_to_bmesh_block(bm->pdata, poly_info, i, f->head); - bm_elem_check_toolflags(bm, reinterpret_cast(f)); if (params->calc_face_normal) { BM_face_normal_update(f); @@ -1191,7 +1168,7 @@ static Vector bm_to_mesh_copy_info_calc(const CustomData & info.n = per_type_index[type]; info.bmesh_offset = bm_layer.offset; info.mesh_data = mesh_layer.data; - info.elem_size = CustomData_sizeof(type); + info.elem_size = CustomData_get_elem_size(&mesh_layer); infos.append(info); per_type_index[type]++; diff --git a/source/blender/bmesh/intern/bmesh_mesh_convert.h b/source/blender/bmesh/intern/bmesh_mesh_convert.h index cf8688fa07c..d3fead57854 100644 --- a/source/blender/bmesh/intern/bmesh_mesh_convert.h +++ b/source/blender/bmesh/intern/bmesh_mesh_convert.h @@ -78,8 +78,6 @@ struct BMeshToMeshParams { uint copy_temp_cdlayers; }; -void BM_enter_multires_space(struct Object *ob, struct BMesh *bm, int space); - /** * \param bmain: May be NULL in case \a calc_object_remap parameter option is not set. */ diff --git a/source/blender/bmesh/intern/bmesh_mesh_duplicate.cc b/source/blender/bmesh/intern/bmesh_mesh_duplicate.cc index df05ce3bf55..68dabb7ff0d 100644 --- a/source/blender/bmesh/intern/bmesh_mesh_duplicate.cc +++ b/source/blender/bmesh/intern/bmesh_mesh_duplicate.cc @@ -23,8 +23,6 @@ static BMVert *bm_vert_copy(BMesh *bm_src, BMesh *bm_dst, BMVert *v_src) BMVert *v_dst = BM_vert_create(bm_dst, v_src->co, nullptr, BM_CREATE_SKIP_CD); BM_elem_attrs_copy(bm_src, bm_dst, v_src, v_dst); - bm_elem_check_toolflags(bm_dst, (BMElem *)v_dst); - return v_dst; } @@ -39,8 +37,6 @@ static BMEdge *bm_edge_copy_with_arrays(BMesh *bm_src, BM_elem_attrs_copy(bm_src, bm_dst, e_src, e_dst); - bm_elem_check_toolflags(bm_dst, (BMElem *)e_dst); - return e_dst; } @@ -70,8 +66,6 @@ static BMFace *bm_face_copy_with_arrays( /* Copy attributes. */ BM_elem_attrs_copy(bm_src, bm_dst, f_src, f_dst); - bm_elem_check_toolflags(bm_dst, (BMElem *)f_dst); - /* Copy per-loop custom data. */ l_iter_src = l_first_src; l_iter_dst = BM_FACE_FIRST_LOOP(f_dst); diff --git a/source/blender/bmesh/intern/bmesh_operator_api.h b/source/blender/bmesh/intern/bmesh_operator_api.h index a013686f75f..9afec4ecbae 100644 --- a/source/blender/bmesh/intern/bmesh_operator_api.h +++ b/source/blender/bmesh/intern/bmesh_operator_api.h @@ -8,11 +8,8 @@ * \ingroup bmesh */ -#include "BLI_compiler_compat.h" #include "BLI_ghash.h" -#include "DNA_meshdata_types.h" - #include #ifdef __cplusplus @@ -59,142 +56,122 @@ extern "C" { struct GHashIterator; -BLI_INLINE BMFlagLayer *BMO_elem_flag_from_header(BMesh *bm, BMElem *ele) +BLI_INLINE BMFlagLayer *BMO_elem_flag_from_header(BMHeader *ele_head) { - int cd_tflags; - - switch (ele->head.htype) { + switch (ele_head->htype) { case BM_VERT: - cd_tflags = bm->vdata.layers[bm->vdata.typemap[CD_TOOLFLAGS]].offset; - break; + return ((BMVert_OFlag *)ele_head)->oflags; case BM_EDGE: - cd_tflags = bm->edata.layers[bm->edata.typemap[CD_TOOLFLAGS]].offset; - break; - case BM_FACE: - cd_tflags = bm->pdata.layers[bm->pdata.typemap[CD_TOOLFLAGS]].offset; - break; + return ((BMEdge_OFlag *)ele_head)->oflags; default: - return NULL; + return ((BMFace_OFlag *)ele_head)->oflags; } - - MToolFlags *flags = (MToolFlags *)BM_ELEM_CD_GET_VOID_P(ele, cd_tflags); - - return (BMFlagLayer *)flags->flag; } #define BMO_elem_flag_test(bm, ele, oflag) \ - _bmo_elem_flag_test(bm, BMO_elem_flag_from_header(bm, (BMElem *)(ele)), oflag) + _bmo_elem_flag_test(bm, BMO_elem_flag_from_header(&(ele)->head), oflag) #define BMO_elem_flag_test_bool(bm, ele, oflag) \ - _bmo_elem_flag_test_bool(bm, BMO_elem_flag_from_header(bm, (BMElem *)(ele)), oflag) + _bmo_elem_flag_test_bool(bm, BMO_elem_flag_from_header(&(ele)->head), oflag) #define BMO_elem_flag_enable(bm, ele, oflag) \ _bmo_elem_flag_enable( \ - bm, \ - (BM_CHECK_TYPE_ELEM_NONCONST(ele), BMO_elem_flag_from_header(bm, (BMElem *)(ele))), \ - oflag) + bm, (BM_CHECK_TYPE_ELEM_NONCONST(ele), BMO_elem_flag_from_header(&(ele)->head)), oflag) #define BMO_elem_flag_disable(bm, ele, oflag) \ _bmo_elem_flag_disable( \ - bm, \ - (BM_CHECK_TYPE_ELEM_NONCONST(ele), BMO_elem_flag_from_header(bm, (BMElem *)(ele))), \ - oflag) + bm, (BM_CHECK_TYPE_ELEM_NONCONST(ele), BMO_elem_flag_from_header(&(ele)->head)), oflag) #define BMO_elem_flag_set(bm, ele, oflag, val) \ - _bmo_elem_flag_set( \ - bm, \ - (BM_CHECK_TYPE_ELEM_NONCONST(ele), BMO_elem_flag_from_header(bm, (BMElem *)(ele))), \ - oflag, \ - val) + _bmo_elem_flag_set(bm, \ + (BM_CHECK_TYPE_ELEM_NONCONST(ele), BMO_elem_flag_from_header(&(ele)->head)), \ + oflag, \ + val) #define BMO_elem_flag_toggle(bm, ele, oflag) \ _bmo_elem_flag_toggle( \ - bm, \ - (BM_CHECK_TYPE_ELEM_NONCONST(ele), BMO_elem_flag_from_header(bm, (BMElem *)(ele))), \ - oflag) + bm, (BM_CHECK_TYPE_ELEM_NONCONST(ele), BMO_elem_flag_from_header(&(ele)->head)), oflag) /* take care not to instantiate args multiple times */ -#ifdef __GNUC__ -/* clang-format off */ -#define _BMO_CAST_V_CONST(bm, e) ({\ +#ifdef __GNUC___ +# define _BMO_CAST_V_CONST(e) \ + ({ \ typeof(e) _e = e; \ (BM_CHECK_TYPE_VERT(_e), \ - (const BMFlagLayer*) BMO_elem_flag_from_header(bm, \ - (BLI_assert(((const BMHeader *)_e)->htype == BM_VERT), \ - (BMElem*)_e))); \ -}) -#define _BMO_CAST_V(bm, e) ({\ - typeof(e) _e = e; \ - (BM_CHECK_TYPE_VERT_NONCONST(_e), \ - BMO_elem_flag_from_header(bm, \ - (BLI_assert(((const BMHeader *)_e)->htype == BM_VERT), \ - (BMElem*)_e))); \ -}) - -#define _BMO_CAST_E_CONST(bm, e) ({\ + BLI_assert(((const BMHeader *)_e)->htype == BM_VERT), \ + (const BMVert_OFlag *)_e); \ + }) +# define _BMO_CAST_E_CONST(e) \ + ({ \ typeof(e) _e = e; \ (BM_CHECK_TYPE_EDGE(_e), \ - (const BMFlagLayer*) BMO_elem_flag_from_header(bm, \ - (BLI_assert(((const BMHeader *)_e)->htype == BM_EDGE), \ - (BMElem*)_e))); \ -}) -#define _BMO_CAST_E(bm, e) ({\ - typeof(e) _e = e; \ - (BM_CHECK_TYPE_EDGE_NONCONST(_e), \ - BMO_elem_flag_from_header(bm, \ - (BLI_assert(((const BMHeader *)_e)->htype == BM_EDGE), \ - (BMElem*)_e))); \ -}) - -#define _BMO_CAST_F_CONST(bm, e) ({\ + BLI_assert(((const BMHeader *)_e)->htype == BM_EDGE), \ + (const BMEdge_OFlag *)_e); \ + }) +# define _BMO_CAST_F_CONST(e) \ + ({ \ typeof(e) _e = e; \ (BM_CHECK_TYPE_FACE(_e), \ - (const BMFlagLayer*) BMO_elem_flag_from_header(bm, \ - (BLI_assert(((const BMHeader *)_e)->htype == BM_FACE), \ - (BMElem*)_e))); \ -}) -#define _BMO_CAST_F(bm, e) ({\ + BLI_assert(((const BMHeader *)_e)->htype == BM_FACE), \ + (const BMFace_OFlag *)_e); \ + }) +# define _BMO_CAST_V(e) \ + ({ \ + typeof(e) _e = e; \ + (BM_CHECK_TYPE_VERT_NONCONST(_e), \ + BLI_assert(((BMHeader *)_e)->htype == BM_VERT), \ + (BMVert_OFlag *)_e); \ + }) +# define _BMO_CAST_E(e) \ + ({ \ + typeof(e) _e = e; \ + (BM_CHECK_TYPE_EDGE_NONCONST(_e), \ + BLI_assert(((BMHeader *)_e)->htype == BM_EDGE), \ + (BMEdge_OFlag *)_e); \ + }) +# define _BMO_CAST_F(e) \ + ({ \ typeof(e) _e = e; \ (BM_CHECK_TYPE_FACE_NONCONST(_e), \ - BMO_elem_flag_from_header(bm, \ - (BLI_assert(((const BMHeader *)_e)->htype == BM_FACE), \ - (BMElem*)_e))); \ -}) - -/* clang-format on */ + BLI_assert(((BMHeader *)_e)->htype == BM_FACE), \ + (BMFace_OFlag *)_e); \ + }) #else -# define _BMO_CAST_V_CONST(bm, e) \ - (BM_CHECK_TYPE_VERT(e), (const BMFlagLayer *)BMO_elem_flag_from_header(bm, (BMElem *)(e))) -# define _BMO_CAST_E_CONST(bm, e) \ - (BM_CHECK_TYPE_EDGE(e), (const BMFlagLayer *)BMO_elem_flag_from_header(bm, (BMElem *)(e))) -# define _BMO_CAST_F_CONST(bm, e) \ - (BM_CHECK_TYPE_FACE(e), (const BMFlagLayer *)BMO_elem_flag_from_header(bm, (BMElem *)(e))) -# define _BMO_CAST_V(bm, e) \ - (BM_CHECK_TYPE_VERT_NONCONST(e), BMO_elem_flag_from_header(bm, (BMElem *)(e))) -# define _BMO_CAST_E(bm, e) \ - (BM_CHECK_TYPE_EDGE_NONCONST(e), BMO_elem_flag_from_header(bm, (BMElem *)(e))) -# define _BMO_CAST_F(bm, e) \ - (BM_CHECK_TYPE_FACE_NONCONST(e), BMO_elem_flag_from_header(bm, (BMElem *)(e))) +# define _BMO_CAST_V_CONST(e) (BM_CHECK_TYPE_VERT(e), (const BMVert_OFlag *)e) +# define _BMO_CAST_E_CONST(e) (BM_CHECK_TYPE_EDGE(e), (const BMEdge_OFlag *)e) +# define _BMO_CAST_F_CONST(e) (BM_CHECK_TYPE_FACE(e), (const BMFace_OFlag *)e) +# define _BMO_CAST_V(e) (BM_CHECK_TYPE_VERT_NONCONST(e), (BMVert_OFlag *)e) +# define _BMO_CAST_E(e) (BM_CHECK_TYPE_EDGE_NONCONST(e), (BMEdge_OFlag *)e) +# define _BMO_CAST_F(e) (BM_CHECK_TYPE_FACE_NONCONST(e), (BMFace_OFlag *)e) #endif -#define BMO_vert_flag_test(bm, e, oflag) _bmo_elem_flag_test(bm, _BMO_CAST_V_CONST(bm, e), oflag) +#define BMO_vert_flag_test(bm, e, oflag) \ + _bmo_elem_flag_test(bm, _BMO_CAST_V_CONST(e)->oflags, oflag) #define BMO_vert_flag_test_bool(bm, e, oflag) \ - _bmo_elem_flag_test_bool(bm, _BMO_CAST_V_CONST(bm, e), oflag) -#define BMO_vert_flag_enable(bm, e, oflag) _bmo_elem_flag_enable(bm, _BMO_CAST_V(bm, e), oflag) -#define BMO_vert_flag_disable(bm, e, oflag) _bmo_elem_flag_disable(bm, _BMO_CAST_V(bm, e), oflag) -#define BMO_vert_flag_set(bm, e, oflag, val) _bmo_elem_flag_set(bm, _BMO_CAST_V(bm, e), oflag, val) -#define BMO_vert_flag_toggle(bm, e, oflag) _bmo_elem_flag_toggle(bm, _BMO_CAST_V(bm, e), oflag) + _bmo_elem_flag_test_bool(bm, _BMO_CAST_V_CONST(e)->oflags, oflag) +#define BMO_vert_flag_enable(bm, e, oflag) _bmo_elem_flag_enable(bm, _BMO_CAST_V(e)->oflags, oflag) +#define BMO_vert_flag_disable(bm, e, oflag) \ + _bmo_elem_flag_disable(bm, _BMO_CAST_V(e)->oflags, oflag) +#define BMO_vert_flag_set(bm, e, oflag, val) \ + _bmo_elem_flag_set(bm, _BMO_CAST_V(e)->oflags, oflag, val) +#define BMO_vert_flag_toggle(bm, e, oflag) _bmo_elem_flag_toggle(bm, _BMO_CAST_V(e)->oflags, oflag) -#define BMO_edge_flag_test(bm, e, oflag) _bmo_elem_flag_test(bm, _BMO_CAST_E_CONST(bm, e), oflag) +#define BMO_edge_flag_test(bm, e, oflag) \ + _bmo_elem_flag_test(bm, _BMO_CAST_E_CONST(e)->oflags, oflag) #define BMO_edge_flag_test_bool(bm, e, oflag) \ - _bmo_elem_flag_test_bool(bm, _BMO_CAST_E_CONST(bm, e), oflag) -#define BMO_edge_flag_enable(bm, e, oflag) _bmo_elem_flag_enable(bm, _BMO_CAST_E(bm, e), oflag) -#define BMO_edge_flag_disable(bm, e, oflag) _bmo_elem_flag_disable(bm, _BMO_CAST_E(bm, e), oflag) -#define BMO_edge_flag_set(bm, e, oflag, val) _bmo_elem_flag_set(bm, _BMO_CAST_E(bm, e), oflag, val) -#define BMO_edge_flag_toggle(bm, e, oflag) _bmo_elem_flag_toggle(bm, _BMO_CAST_E(bm, e), oflag) + _bmo_elem_flag_test_bool(bm, _BMO_CAST_E_CONST(e)->oflags, oflag) +#define BMO_edge_flag_enable(bm, e, oflag) _bmo_elem_flag_enable(bm, _BMO_CAST_E(e)->oflags, oflag) +#define BMO_edge_flag_disable(bm, e, oflag) \ + _bmo_elem_flag_disable(bm, _BMO_CAST_E(e)->oflags, oflag) +#define BMO_edge_flag_set(bm, e, oflag, val) \ + _bmo_elem_flag_set(bm, _BMO_CAST_E(e)->oflags, oflag, val) +#define BMO_edge_flag_toggle(bm, e, oflag) _bmo_elem_flag_toggle(bm, _BMO_CAST_E(e)->oflags, oflag) -#define BMO_face_flag_test(bm, e, oflag) _bmo_elem_flag_test(bm, _BMO_CAST_F_CONST(bm, e), oflag) +#define BMO_face_flag_test(bm, e, oflag) \ + _bmo_elem_flag_test(bm, _BMO_CAST_F_CONST(e)->oflags, oflag) #define BMO_face_flag_test_bool(bm, e, oflag) \ - _bmo_elem_flag_test_bool(bm, _BMO_CAST_F_CONST(bm, e), oflag) -#define BMO_face_flag_enable(bm, e, oflag) _bmo_elem_flag_enable(bm, _BMO_CAST_F(bm, e), oflag) -#define BMO_face_flag_disable(bm, e, oflag) _bmo_elem_flag_disable(bm, _BMO_CAST_F(bm, e), oflag) -#define BMO_face_flag_set(bm, e, oflag, val) _bmo_elem_flag_set(bm, _BMO_CAST_F(bm, e), oflag, val) -#define BMO_face_flag_toggle(bm, e, oflag) _bmo_elem_flag_toggle(bm, _BMO_CAST_F(bm, e), oflag) + _bmo_elem_flag_test_bool(bm, _BMO_CAST_F_CONST(e)->oflags, oflag) +#define BMO_face_flag_enable(bm, e, oflag) _bmo_elem_flag_enable(bm, _BMO_CAST_F(e)->oflags, oflag) +#define BMO_face_flag_disable(bm, e, oflag) \ + _bmo_elem_flag_disable(bm, _BMO_CAST_F(e)->oflags, oflag) +#define BMO_face_flag_set(bm, e, oflag, val) \ + _bmo_elem_flag_set(bm, _BMO_CAST_F(e)->oflags, oflag, val) +#define BMO_face_flag_toggle(bm, e, oflag) _bmo_elem_flag_toggle(bm, _BMO_CAST_F(e)->oflags, oflag) BLI_INLINE short _bmo_elem_flag_test(BMesh *bm, const BMFlagLayer *oflags, short oflag); BLI_INLINE bool _bmo_elem_flag_test_bool(BMesh *bm, const BMFlagLayer *oflags, short oflag); diff --git a/source/blender/bmesh/intern/bmesh_operators.cc b/source/blender/bmesh/intern/bmesh_operators.cc index b018b39270c..d7ed9a765ab 100644 --- a/source/blender/bmesh/intern/bmesh_operators.cc +++ b/source/blender/bmesh/intern/bmesh_operators.cc @@ -20,13 +20,9 @@ #include "BLT_translation.h" -#include "BKE_customdata.h" - #include "bmesh.h" #include "intern/bmesh_private.h" -#include "DNA_meshdata_types.h" - /* forward declarations */ static void bmo_flag_layer_alloc(BMesh *bm); static void bmo_flag_layer_free(BMesh *bm); @@ -1164,73 +1160,6 @@ void BMO_slot_buffer_flag_disable(BMesh *bm, } } -static void bmo_flag_layer_do(BMesh *bm, - int new_totflags, - void (*callback)(BMesh *bm, - int cd_tflags, - int itertype, - int htype, - int totelem, - int new_totflags, - BLI_mempool **pool_ptr)) -{ - int iters[3] = {BM_VERTS_OF_MESH, BM_EDGES_OF_MESH, BM_FACES_OF_MESH}; - int types[3] = {BM_VERT, BM_EDGE, BM_FACE}; - int tots[3] = {bm->totvert, bm->totedge, bm->totface}; - - BLI_mempool **pools[3] = {&bm->vtoolflagpool, &bm->etoolflagpool, &bm->ftoolflagpool}; - CustomData *cdatas[3] = {&bm->vdata, &bm->edata, &bm->pdata}; - - for (int i = 0; i < 3; i++) { - int cd_tflags = CustomData_get_offset(cdatas[i], CD_TOOLFLAGS); - - if (cd_tflags == -1) { - printf("%s: missing toolflags cd layer!\n", __func__); - } - - callback(bm, cd_tflags, iters[i], types[i], tots[i], new_totflags, pools[i]); - } - - bm->totflags = new_totflags; -} - -static void bmo_flag_layer_alloc_do(BMesh *bm, - int cd_tflags, - int itertype, - int /*htype*/, - int totelem, - int new_totflags, - BLI_mempool **pool_ptr) -{ - BMIter iter; - BMElem *elem; - int i; - - size_t old_totflags_size = (bm->totflags * sizeof(BMFlagLayer)); - old_totflags_size = MIN2(old_totflags_size, sizeof(BMFlagLayer) * new_totflags); - - BLI_mempool *oldpool = *pool_ptr; - BLI_mempool *newpool = BLI_mempool_create( - sizeof(BMFlagLayer) * new_totflags, totelem, 512, BLI_MEMPOOL_NOP); - - BM_ITER_MESH_INDEX (elem, &iter, bm, itertype, i) { - MToolFlags *flags = BM_ELEM_CD_PTR(elem, cd_tflags); - - short *oldflags = flags->flag; - flags->flag = static_cast(BLI_mempool_calloc(newpool)); - - if (oldflags) { - memcpy(flags->flag, oldflags, old_totflags_size); - } - - BM_elem_index_set(elem, i); /* set_inline */ - BM_ELEM_API_FLAG_CLEAR((BMElemF *)elem); - } - - *pool_ptr = newpool; - BLI_mempool_destroy(oldpool); -} - /** * \brief ALLOC/FREE FLAG LAYER * @@ -1246,40 +1175,163 @@ static void bmo_flag_layer_alloc_do(BMesh *bm, */ static void bmo_flag_layer_alloc(BMesh *bm) { - bmo_flag_layer_do(bm, bm->totflags + 1, bmo_flag_layer_alloc_do); + /* set the index values since we are looping over all data anyway, + * may save time later on */ + + BLI_mempool *voldpool = bm->vtoolflagpool; /* old flag pool */ + BLI_mempool *eoldpool = bm->etoolflagpool; /* old flag pool */ + BLI_mempool *foldpool = bm->ftoolflagpool; /* old flag pool */ + + /* store memcpy size for reuse */ + const size_t old_totflags_size = (bm->totflags * sizeof(BMFlagLayer)); + + bm->totflags++; + + bm->vtoolflagpool = BLI_mempool_create( + sizeof(BMFlagLayer) * bm->totflags, bm->totvert, 512, BLI_MEMPOOL_NOP); + bm->etoolflagpool = BLI_mempool_create( + sizeof(BMFlagLayer) * bm->totflags, bm->totedge, 512, BLI_MEMPOOL_NOP); + bm->ftoolflagpool = BLI_mempool_create( + sizeof(BMFlagLayer) * bm->totflags, bm->totface, 512, BLI_MEMPOOL_NOP); + + /* now go through and memcpy all the flags. Loops don't get a flag layer at this time. */ + BMIter iter; + int i; + + BMVert_OFlag *v_oflag; + BLI_mempool *newpool = bm->vtoolflagpool; + BM_ITER_MESH_INDEX (v_oflag, &iter, bm, BM_VERTS_OF_MESH, i) { + void *oldflags = v_oflag->oflags; + v_oflag->oflags = static_cast(BLI_mempool_calloc(newpool)); + memcpy(v_oflag->oflags, oldflags, old_totflags_size); + BM_elem_index_set(&v_oflag->base, i); /* set_inline */ + BM_ELEM_API_FLAG_CLEAR((BMElemF *)v_oflag); + } + + BMEdge_OFlag *e_oflag; + newpool = bm->etoolflagpool; + BM_ITER_MESH_INDEX (e_oflag, &iter, bm, BM_EDGES_OF_MESH, i) { + void *oldflags = e_oflag->oflags; + e_oflag->oflags = static_cast(BLI_mempool_calloc(newpool)); + memcpy(e_oflag->oflags, oldflags, old_totflags_size); + BM_elem_index_set(&e_oflag->base, i); /* set_inline */ + BM_ELEM_API_FLAG_CLEAR((BMElemF *)e_oflag); + } + + BMFace_OFlag *f_oflag; + newpool = bm->ftoolflagpool; + BM_ITER_MESH_INDEX (f_oflag, &iter, bm, BM_FACES_OF_MESH, i) { + void *oldflags = f_oflag->oflags; + f_oflag->oflags = static_cast(BLI_mempool_calloc(newpool)); + memcpy(f_oflag->oflags, oldflags, old_totflags_size); + BM_elem_index_set(&f_oflag->base, i); /* set_inline */ + BM_ELEM_API_FLAG_CLEAR((BMElemF *)f_oflag); + } + + BLI_mempool_destroy(voldpool); + BLI_mempool_destroy(eoldpool); + BLI_mempool_destroy(foldpool); + bm->elem_index_dirty &= ~(BM_VERT | BM_EDGE | BM_FACE); } static void bmo_flag_layer_free(BMesh *bm) { + /* set the index values since we are looping over all data anyway, + * may save time later on */ - bmo_flag_layer_do(bm, bm->totflags - 1, bmo_flag_layer_alloc_do); - bm->elem_index_dirty &= ~(BM_VERT | BM_EDGE | BM_FACE); -} + BLI_mempool *voldpool = bm->vtoolflagpool; + BLI_mempool *eoldpool = bm->etoolflagpool; + BLI_mempool *foldpool = bm->ftoolflagpool; -static void bmo_flag_layer_clear_do(BMesh *bm, - int cd_tflags, - int itertype, - int /*htype*/, - int /*totelem*/, - int totflag, - BLI_mempool ** /*pool_ptr*/) -{ + /* store memcpy size for reuse */ + const size_t new_totflags_size = ((bm->totflags - 1) * sizeof(BMFlagLayer)); + + /* de-increment the totflags first. */ + bm->totflags--; + + bm->vtoolflagpool = BLI_mempool_create(new_totflags_size, bm->totvert, 512, BLI_MEMPOOL_NOP); + bm->etoolflagpool = BLI_mempool_create(new_totflags_size, bm->totedge, 512, BLI_MEMPOOL_NOP); + bm->ftoolflagpool = BLI_mempool_create(new_totflags_size, bm->totface, 512, BLI_MEMPOOL_NOP); + + /* now go through and memcpy all the flag */ BMIter iter; - BMElem *elem; int i; - BM_ITER_MESH_INDEX (elem, &iter, bm, itertype, i) { - MToolFlags *flags = BM_ELEM_CD_PTR(elem, cd_tflags); - flags->flag[totflag - 1] = 0; - - BM_elem_index_set(elem, i); /* set_inline */ + BMVert_OFlag *v_oflag; + BLI_mempool *newpool = bm->vtoolflagpool; + BM_ITER_MESH_INDEX (v_oflag, &iter, bm, BM_VERTS_OF_MESH, i) { + void *oldflags = v_oflag->oflags; + v_oflag->oflags = static_cast(BLI_mempool_alloc(newpool)); + memcpy(v_oflag->oflags, oldflags, new_totflags_size); + BM_elem_index_set(&v_oflag->base, i); /* set_inline */ + BM_ELEM_API_FLAG_CLEAR((BMElemF *)v_oflag); } + + BMEdge_OFlag *e_oflag; + newpool = bm->etoolflagpool; + BM_ITER_MESH_INDEX (e_oflag, &iter, bm, BM_EDGES_OF_MESH, i) { + void *oldflags = e_oflag->oflags; + e_oflag->oflags = static_cast(BLI_mempool_alloc(newpool)); + memcpy(e_oflag->oflags, oldflags, new_totflags_size); + BM_elem_index_set(&e_oflag->base, i); /* set_inline */ + BM_ELEM_API_FLAG_CLEAR((BMElemF *)e_oflag); + } + + BMFace_OFlag *f_oflag; + newpool = bm->ftoolflagpool; + BM_ITER_MESH_INDEX (f_oflag, &iter, bm, BM_FACES_OF_MESH, i) { + void *oldflags = f_oflag->oflags; + f_oflag->oflags = static_cast(BLI_mempool_alloc(newpool)); + memcpy(f_oflag->oflags, oldflags, new_totflags_size); + BM_elem_index_set(&f_oflag->base, i); /* set_inline */ + BM_ELEM_API_FLAG_CLEAR((BMElemF *)f_oflag); + } + + BLI_mempool_destroy(voldpool); + BLI_mempool_destroy(eoldpool); + BLI_mempool_destroy(foldpool); + + bm->elem_index_dirty &= ~(BM_VERT | BM_EDGE | BM_FACE); } static void bmo_flag_layer_clear(BMesh *bm) { - bmo_flag_layer_do(bm, bm->totflags, bmo_flag_layer_clear_do); + /* set the index values since we are looping over all data anyway, + * may save time later on */ + const BMFlagLayer zero_flag = {0}; + + const int totflags_offset = bm->totflags - 1; + + /* now go through and memcpy all the flag */ + { + BMIter iter; + BMVert_OFlag *ele; + int i; + BM_ITER_MESH_INDEX (ele, &iter, bm, BM_VERTS_OF_MESH, i) { + ele->oflags[totflags_offset] = zero_flag; + BM_elem_index_set(&ele->base, i); /* set_inline */ + } + } + { + BMIter iter; + BMEdge_OFlag *ele; + int i; + BM_ITER_MESH_INDEX (ele, &iter, bm, BM_EDGES_OF_MESH, i) { + ele->oflags[totflags_offset] = zero_flag; + BM_elem_index_set(&ele->base, i); /* set_inline */ + } + } + { + BMIter iter; + BMFace_OFlag *ele; + int i; + BM_ITER_MESH_INDEX (ele, &iter, bm, BM_FACES_OF_MESH, i) { + ele->oflags[totflags_offset] = zero_flag; + BM_elem_index_set(&ele->base, i); /* set_inline */ + } + } + bm->elem_index_dirty &= ~(BM_VERT | BM_EDGE | BM_FACE); } diff --git a/source/blender/bmesh/intern/bmesh_private.h b/source/blender/bmesh/intern/bmesh_private.h index af4df2b9970..554973fe635 100644 --- a/source/blender/bmesh/intern/bmesh_private.h +++ b/source/blender/bmesh/intern/bmesh_private.h @@ -82,8 +82,6 @@ int bmesh_elem_check(void *element, char htype); int bmesh_radial_length(const BMLoop *l); int bmesh_disk_count_at_most(const BMVert *v, int count_max); int bmesh_disk_count(const BMVert *v); -void bm_rebuild_idmap(BMesh *bm); -void bm_alloc_toolflags_cdlayers(BMesh *bm, bool set_elems); /** * Internal BMHeader.api_flag diff --git a/source/blender/bmesh/operators/bmo_dupe.cc b/source/blender/bmesh/operators/bmo_dupe.cc index 3cb983e02c0..85820705d65 100644 --- a/source/blender/bmesh/operators/bmo_dupe.cc +++ b/source/blender/bmesh/operators/bmo_dupe.cc @@ -51,8 +51,6 @@ static BMVert *bmo_vert_copy(BMOperator *op, /* Copy attributes */ BM_elem_attrs_copy(bm_src, bm_dst, v_src, v_dst); - bm_elem_check_toolflags(bm_dst, (BMElem *)v_dst); - /* Mark the vert for output */ BMO_vert_flag_enable(bm_dst, v_dst, DUPE_NEW); @@ -115,8 +113,6 @@ static BMEdge *bmo_edge_copy(BMOperator *op, /* Copy attributes */ BM_elem_attrs_copy(bm_src, bm_dst, e_src, e_dst); - bm_elem_check_toolflags(bm_dst, (BMElem *)e_dst); - /* Mark the edge for output */ BMO_edge_flag_enable(bm_dst, e_dst, DUPE_NEW); @@ -169,8 +165,6 @@ static BMFace *bmo_face_copy(BMOperator *op, /* Copy attributes */ BM_elem_attrs_copy(bm_src, bm_dst, f_src, f_dst); - bm_elem_check_toolflags(bm_dst, (BMElem *)f_dst); - /* copy per-loop custom data */ l_iter_src = l_first_src; l_iter_dst = BM_FACE_FIRST_LOOP(f_dst); diff --git a/source/blender/bmesh/operators/bmo_removedoubles.cc b/source/blender/bmesh/operators/bmo_removedoubles.cc index efb86000ad1..49f26197941 100644 --- a/source/blender/bmesh/operators/bmo_removedoubles.cc +++ b/source/blender/bmesh/operators/bmo_removedoubles.cc @@ -279,12 +279,7 @@ void bmo_weld_verts_exec(BMesh *bm, BMOperator *op) bmesh_face_swap_data(f_new, f); if (bm->use_toolflags) { - MToolFlags *flags = (MToolFlags *)BM_ELEM_CD_GET_VOID_P( - f, bm->pdata.layers[bm->pdata.typemap[CD_TOOLFLAGS]].offset); - MToolFlags *flags_new = (MToolFlags *)BM_ELEM_CD_GET_VOID_P( - f_new, bm->pdata.layers[bm->pdata.typemap[CD_TOOLFLAGS]].offset); - - SWAP(short *, flags->flag, flags_new->flag); + SWAP(BMFlagLayer *, ((BMFace_OFlag *)f)->oflags, ((BMFace_OFlag *)f_new)->oflags); } BMO_face_flag_disable(bm, f, ELE_DEL); diff --git a/source/blender/editors/sculpt_paint/paint_mask.cc b/source/blender/editors/sculpt_paint/paint_mask.cc index 9f1a840a457..fe32075ea8a 100644 --- a/source/blender/editors/sculpt_paint/paint_mask.cc +++ b/source/blender/editors/sculpt_paint/paint_mask.cc @@ -52,8 +52,8 @@ #include "ED_sculpt.hh" #include "ED_view3d.hh" -#include "bmesh_idmap.h" #include "bmesh.h" +#include "bmesh_idmap.h" #include "tools/bmesh_boolean.h" #include "paint_intern.hh" @@ -1416,23 +1416,25 @@ static void sculpt_gesture_apply_trim(SculptGestureContext *sgcontext) Mesh *trim_mesh = trim_operation->mesh; const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(sculpt_mesh, trim_mesh); - BMeshCreateParams create_params = {0}; - create_params.use_toolflags = true; + BMeshCreateParams create_params = {}; + create_params.use_toolflags = false; - BMeshFromMeshParams convert_params = {0}; + BMeshFromMeshParams convert_params = {}; convert_params.calc_face_normal = true; BMesh *bm; if (ss->bm) { bm = BM_mesh_copy(ss->bm); - BM_mesh_toolflags_set(bm, true); } else { bm = BM_mesh_create(&allocsize, &create_params); BM_mesh_bm_from_me(bm, sculpt_mesh, &convert_params); } + BM_mesh_toolflags_set(bm, true); + BM_mesh_elem_toolflags_ensure(bm); + BMIter iter; BMFace *efa; BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) { @@ -1444,9 +1446,10 @@ static void sculpt_gesture_apply_trim(SculptGestureContext *sgcontext) convert_params.calc_face_normal = convert_params.calc_vert_normal = true; BM_mesh_bm_from_me(trimbm, trim_mesh, &convert_params); - BM_mesh_normals_update(bm); + BM_mesh_toolflags_set(trimbm, true); + /* Add trim geometry to bm. */ BMO_op_callf(trimbm, BMO_FLAG_DEFAULTS, "duplicate geom=%avef dest=%p", bm, 3); BM_mesh_free(trimbm); diff --git a/source/blender/editors/sculpt_paint/sculpt_face_set.cc b/source/blender/editors/sculpt_paint/sculpt_face_set.cc index c8f13baca18..a7d70bcdb78 100644 --- a/source/blender/editors/sculpt_paint/sculpt_face_set.cc +++ b/source/blender/editors/sculpt_paint/sculpt_face_set.cc @@ -57,8 +57,8 @@ #include "RNA_access.hh" #include "RNA_define.hh" -#include "bmesh_idmap.h" #include "bmesh.h" +#include "bmesh_idmap.h" using blender::Array; using blender::float3; @@ -1353,16 +1353,9 @@ static void sculpt_face_set_delete_geometry(Object *ob, Mesh *mesh = static_cast(ob->data); BMesh *bm; - if (ss->bm) { - bm = ss->bm; - BM_mesh_toolflags_set(bm, true); - BM_idmap_check_attributes(ss->bm_idmap); - BKE_sculptsession_update_attr_refs(ob); - } - else { + if (!ss->bm) { const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(mesh); BMeshCreateParams create_params{}; - create_params.use_toolflags = true; bm = BM_mesh_create(&allocsize, &create_params); @@ -1372,6 +1365,9 @@ static void sculpt_face_set_delete_geometry(Object *ob, BM_mesh_bm_from_me(bm, mesh, &convert_params); } + else { + bm = ss->bm; + } int cd_fset_offset = CustomData_get_offset_named( &bm->pdata, CD_PROP_INT32, SCULPT_ATTRIBUTE_NAME(face_set)); @@ -1403,11 +1399,7 @@ static void sculpt_face_set_delete_geometry(Object *ob, BM_mesh_elem_hflag_disable_all(bm, BM_VERT | BM_EDGE | BM_FACE, BM_ELEM_TAG, false); - if (ss->bm) { - BM_mesh_toolflags_set(bm, false); - BM_idmap_check_attributes(ss->bm_idmap); - } - else { + if (!ss->bm) { BMeshToMeshParams bmesh_to_mesh_params{}; bmesh_to_mesh_params.calc_object_remap = false; BM_mesh_bm_to_me(nullptr, bm, mesh, &bmesh_to_mesh_params); diff --git a/source/blender/makesdna/DNA_customdata_types.h b/source/blender/makesdna/DNA_customdata_types.h index 0b6575cf480..1581df22f21 100644 --- a/source/blender/makesdna/DNA_customdata_types.h +++ b/source/blender/makesdna/DNA_customdata_types.h @@ -85,8 +85,7 @@ typedef struct CustomData { * MUST be >= CD_NUMTYPES, but we can't use a define here. * Correct size is ensured in CustomData_update_typemap assert(). */ - int typemap[54]; - int _pad0[1]; + int typemap[53]; /** Number of layers, size of layers array. */ int totlayer, maxlayer; @@ -184,9 +183,8 @@ typedef enum eCustomDataType { /* CD_HAIRLENGTH = 51, */ /* UNUSED */ CD_PROP_QUATERNION = 52, - CD_TOOLFLAGS = 53, - CD_NUMTYPES = 54, + CD_NUMTYPES = 53, } eCustomDataType; /* Bits for eCustomDataMask */ @@ -228,7 +226,6 @@ typedef enum eCustomDataType { #define CD_MASK_PROP_INT8 (1ULL << CD_PROP_INT8) #define CD_MASK_PROP_INT32_2D (1ULL << CD_PROP_INT32_2D) #define CD_MASK_PROP_QUATERNION (1ULL << CD_PROP_QUATERNION) -#define CD_MASK_TOOLFLAGS (1ULL << CD_TOOLFLAGS) /** Multi-resolution loop data. */ #define CD_MASK_MULTIRES_GRIDS (CD_MASK_MDISPS | CD_GRID_PAINT_MASK) diff --git a/source/blender/makesdna/DNA_meshdata_types.h b/source/blender/makesdna/DNA_meshdata_types.h index b7a8c7adab6..847b2b530b1 100644 --- a/source/blender/makesdna/DNA_meshdata_types.h +++ b/source/blender/makesdna/DNA_meshdata_types.h @@ -506,11 +506,6 @@ typedef enum eSculptFlag { SCULPTFLAG_PBVH_BOUNDARY = (1 << 5), } eSculptFlag; -/* for internal bmesh toolflags api */ -typedef struct MToolFlags { - short *flag; -} MToolFlags; - #ifdef __cplusplus } #endif -- 2.30.2 From 360ae959819df6187d19b14cfff4a0db8e0afe1e Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Wed, 30 Aug 2023 15:21:02 -0700 Subject: [PATCH 245/279] temp-sculpt-dyntopo: Fix broken face set undo and automasking --- source/blender/blenkernel/intern/pbvh.cc | 1 + source/blender/editors/sculpt_paint/sculpt.cc | 32 +++++++++++-------- .../editors/sculpt_paint/sculpt_dyntopo.cc | 4 +-- .../editors/sculpt_paint/sculpt_undo.cc | 25 ++++++++------- 4 files changed, 35 insertions(+), 27 deletions(-) diff --git a/source/blender/blenkernel/intern/pbvh.cc b/source/blender/blenkernel/intern/pbvh.cc index cb16526fbd1..3529971a856 100644 --- a/source/blender/blenkernel/intern/pbvh.cc +++ b/source/blender/blenkernel/intern/pbvh.cc @@ -424,6 +424,7 @@ static int partition_indices_material( void pbvh_grow_nodes(PBVH *pbvh, int totnode) { pbvh->nodes.resize(totnode); + pbvh->nodes[pbvh->nodes.size() - 1].id = pbvh->nodes.size(); } /* Add a vertex to the map, with a positive value for unique vertices and diff --git a/source/blender/editors/sculpt_paint/sculpt.cc b/source/blender/editors/sculpt_paint/sculpt.cc index 92d6a3bb691..781a7188e60 100644 --- a/source/blender/editors/sculpt_paint/sculpt.cc +++ b/source/blender/editors/sculpt_paint/sculpt.cc @@ -83,8 +83,8 @@ #include "RNA_access.hh" #include "RNA_define.hh" -#include "bmesh_idmap.h" #include "bmesh.h" +#include "bmesh_idmap.h" using blender::float3; using blender::IndexRange; @@ -4113,12 +4113,6 @@ static void do_brush_action(Sculpt *sd, ((brush->falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE) && (brush->flag & BRUSH_ANCHORED))) { if (SCULPT_stroke_is_first_brush_step(ss->cache)) { - /* Initialize auto-masking cache. */ - if (SCULPT_is_automasking_enabled(sd, ss, brush)) { - ss->cache->automasking = SCULPT_automasking_cache_init(sd, brush, ob); - ss->last_automasking_settings_hash = SCULPT_automasking_settings_hash( - ob, ss->cache->automasking); - } /* Initialize surface smooth cache. */ if ((brush->sculpt_tool == SCULPT_TOOL_SMOOTH) && (brush->smooth_deform_type == BRUSH_SMOOTH_DEFORM_SURFACE)) @@ -4177,21 +4171,21 @@ static void do_brush_action(Sculpt *sd, extra_type = int(SCULPT_UNDO_COORDS); } - for (int i : nodes.index_range()) { - SCULPT_ensure_dyntopo_node_undo(ob, nodes[i], undo_type, extra_type); + for (PBVHNode *node : nodes) { + SCULPT_ensure_dyntopo_node_undo(ob, node, undo_type, extra_type); switch (undo_type) { case SCULPT_UNDO_FACE_SETS: - BKE_pbvh_node_mark_update_face_sets(nodes[i]); + BKE_pbvh_node_mark_update_face_sets(node); break; case SCULPT_UNDO_MASK: - BKE_pbvh_node_mark_update_mask(nodes[i]); + BKE_pbvh_node_mark_update_mask(node); break; case SCULPT_UNDO_COLOR: - BKE_pbvh_node_mark_update_color(nodes[i]); + BKE_pbvh_node_mark_update_color(node); break; case SCULPT_UNDO_COORDS: - BKE_pbvh_node_mark_update(nodes[i]); + BKE_pbvh_node_mark_update(node); break; case SCULPT_UNDO_HIDDEN: case SCULPT_UNDO_DYNTOPO_BEGIN: @@ -4203,7 +4197,7 @@ static void do_brush_action(Sculpt *sd, } if (extra_type == int(SCULPT_UNDO_COORDS)) { - BKE_pbvh_node_mark_update(nodes[i]); + BKE_pbvh_node_mark_update(node); } } } @@ -6353,6 +6347,16 @@ static void sculpt_stroke_update_step(bContext *C, ss->cache->stroke_distance_t - ss->cache->last_dyntopo_t > dyntopo_spacing); } + /* Have to initialize automasking here since dyntopo might invalidate the active face. */ + if (SCULPT_stroke_is_first_brush_step(ss->cache)) { + /* Initialize auto-masking cache. */ + if (SCULPT_is_automasking_enabled(sd, ss, brush)) { + ss->cache->automasking = SCULPT_automasking_cache_init(sd, brush, ob); + ss->last_automasking_settings_hash = SCULPT_automasking_settings_hash( + ob, ss->cache->automasking); + } + } + /* Running dyntopo before layer brush causes artifacts. */ bool run_dyntopo_after = brush->sculpt_tool == SCULPT_TOOL_LAYER; diff --git a/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc b/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc index 5fc016bbbed..f4fbc2ee6da 100644 --- a/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc +++ b/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc @@ -57,8 +57,8 @@ #include "UI_interface.hh" #include "UI_resources.hh" -#include "bmesh_idmap.h" #include "bmesh.h" +#include "bmesh_idmap.h" #include "bmesh_log.h" #include "bmesh_tools.h" @@ -364,7 +364,7 @@ void sculpt_dynamic_topology_disable_with_undo(Main *bmain, SCULPT_undo_push_end(ob); } - ss->active_vertex.i = ss->active_face.i = 0; + ss->active_vertex.i = ss->active_face.i = PBVH_REF_NONE; } } diff --git a/source/blender/editors/sculpt_paint/sculpt_undo.cc b/source/blender/editors/sculpt_paint/sculpt_undo.cc index b311fd1cf47..71818b005e4 100644 --- a/source/blender/editors/sculpt_paint/sculpt_undo.cc +++ b/source/blender/editors/sculpt_paint/sculpt_undo.cc @@ -80,8 +80,8 @@ #include "ED_sculpt.hh" #include "ED_undo.hh" -#include "bmesh_idmap.h" #include "bmesh.h" +#include "bmesh_idmap.h" #include "bmesh_log.h" #include "sculpt_intern.hh" @@ -1044,7 +1044,7 @@ static void sculpt_undo_bmesh_enable(Object *ob, SculptUndoNode *unode, bool is_ SCULPT_pbvh_clear(ob); - ss->active_face.i = ss->active_vertex.i = 0; + ss->active_face.i = ss->active_vertex.i = PBVH_REF_NONE; /* Create empty BMesh and enable logging. */ ss->bm = SCULPT_dyntopo_empty_bmesh(); @@ -1319,7 +1319,7 @@ static int sculpt_undo_bmesh_restore( sculpt_undo_bmesh_restore_begin(C, unode, ob, ss, dir); SCULPT_vertex_random_access_ensure(ss); - ss->active_face.i = ss->active_vertex.i = 0; + ss->active_face.i = ss->active_vertex.i = PBVH_REF_NONE; ret = true; break; @@ -2350,17 +2350,20 @@ bool SCULPT_ensure_dyntopo_node_undo( unode->nodemap_size = newsize; } - bool check = !((type | extraType) & force_push_mask); - if (check && unode->nodemap[n] & (1 << type)) { - return false; + auto check = [&unode, force_push_mask, n](int type) { + return (type & force_push_mask) || !(unode->nodemap[n] & (1 << type)); + }; + + if (check(type)) { + sculpt_undo_bmesh_push(ob, node, type); + } + + if (extraType >= 0 && check(extraType)) { + sculpt_undo_bmesh_push(ob, node, (SculptUndoType)extraType); + unode->nodemap[n] |= 1 << extraType; } unode->nodemap[n] |= 1 << type; - sculpt_undo_bmesh_push(ob, node, type); - - if (extraType >= 0) { - sculpt_undo_bmesh_push(ob, node, (SculptUndoType)extraType); - } return true; } -- 2.30.2 From b2e11a8bf2b62f8f600020b3d1c0dbaa9c894bfe Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Wed, 30 Aug 2023 15:25:21 -0700 Subject: [PATCH 246/279] Fix merge errors --- source/blender/bmesh/intern/bmesh_mesh_convert.h | 2 +- source/blender/bmesh/intern/bmesh_mesh_duplicate.cc | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/source/blender/bmesh/intern/bmesh_mesh_convert.h b/source/blender/bmesh/intern/bmesh_mesh_convert.h index d3fead57854..ab325e1692b 100644 --- a/source/blender/bmesh/intern/bmesh_mesh_convert.h +++ b/source/blender/bmesh/intern/bmesh_mesh_convert.h @@ -54,7 +54,7 @@ struct Object; * \warning This function doesn't calculate face normals. */ void BM_mesh_bm_from_me(BMesh *bm, const struct Mesh *me, const struct BMeshFromMeshParams *params) - ATTR_NONNULL(2, 3); + ATTR_NONNULL(1, 3); struct BMeshToMeshParams { /** Update object hook indices & vertex parents. */ diff --git a/source/blender/bmesh/intern/bmesh_mesh_duplicate.cc b/source/blender/bmesh/intern/bmesh_mesh_duplicate.cc index 68dabb7ff0d..71f18588deb 100644 --- a/source/blender/bmesh/intern/bmesh_mesh_duplicate.cc +++ b/source/blender/bmesh/intern/bmesh_mesh_duplicate.cc @@ -22,7 +22,6 @@ static BMVert *bm_vert_copy(BMesh *bm_src, BMesh *bm_dst, BMVert *v_src) { BMVert *v_dst = BM_vert_create(bm_dst, v_src->co, nullptr, BM_CREATE_SKIP_CD); BM_elem_attrs_copy(bm_src, bm_dst, v_src, v_dst); - return v_dst; } @@ -34,9 +33,7 @@ static BMEdge *bm_edge_copy_with_arrays(BMesh *bm_src, BMVert *e_dst_v1 = verts_dst[BM_elem_index_get(e_src->v1)]; BMVert *e_dst_v2 = verts_dst[BM_elem_index_get(e_src->v2)]; BMEdge *e_dst = BM_edge_create(bm_dst, e_dst_v1, e_dst_v2, nullptr, BM_CREATE_SKIP_CD); - BM_elem_attrs_copy(bm_src, bm_dst, e_src, e_dst); - return e_dst; } -- 2.30.2 From 2452c71d3ee5850e7a8238d60dd98f12cdf3cc2d Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Thu, 7 Sep 2023 14:39:31 -0700 Subject: [PATCH 247/279] Update Readme.MD --- README.md | 46 +++++++++++++++++++++++----------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index 76243cbeff3..6eeeb0c4c48 100644 --- a/README.md +++ b/README.md @@ -1,33 +1,33 @@ - +Sculpt-Blender +============== -Blender -======= +Sculpt-Blender is an independent branch of Blender focused on sculpting. +It's purpose is to develop and maintain key improvements needed to make +Blender usable for a wider variety of sculpt workflows. -Blender is the free and open source 3D creation suite. -It supports the entirety of the 3D pipeline-modeling, rigging, animation, simulation, rendering, compositing, -motion tracking and video editing. +It arose out of my personal artistic need for a better dynamic topology +system. -![Blender screenshot](https://code.blender.org/wp-content/uploads/2018/12/springrg.jpg "Blender screenshot") +This is not a hard fork. The branch will be regularly synced with official Blender. -Project Pages -------------- +Main supported features: +* Improved DynTopo that preserves attributes. +* Various edge boundaries (e.g. marked seams, face set boundaries, UV island boundaries, etc) + are preserved. +* Better hard surface modelling. -- [Main Website](http://www.blender.org) -- [Reference Manual](https://docs.blender.org/manual/en/latest/index.html) -- [User Community](https://www.blender.org/community/) +Changes will be limited to "needed" functional improvements. +Features like these are off the table for now: -Development ------------ - -- [Build Instructions](https://wiki.blender.org/wiki/Building_Blender) -- [Code Review & Bug Tracker](https://projects.blender.org) -- [Developer Forum](https://devtalk.blender.org) -- [Developer Documentation](https://wiki.blender.org) +* Sculpt layers. +* Node/stack based brush composer. +* Brush properties. +For developers: +* We'll try to avoid refactors. +* There is a copy of a new sculpt brush API in the branch, but only smooth brushes are allowed to use it + (it fixes a performance bug that particularly affects smooth brushes). + License ------- -- 2.30.2 From 4424fa22a3c6ef88023ecee60f69348cfb7a7663 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Tue, 12 Sep 2023 16:52:53 -0700 Subject: [PATCH 248/279] temp-sculpt-dyntopo: Add missing headers needed by linux Also fixed invalid static declaration. --- source/blender/bmesh/intern/bmesh_core.cc | 4 ++-- source/blender/makesrna/intern/rna_brush.cc | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/source/blender/bmesh/intern/bmesh_core.cc b/source/blender/bmesh/intern/bmesh_core.cc index be2ff534adf..6e80b5cc35d 100644 --- a/source/blender/bmesh/intern/bmesh_core.cc +++ b/source/blender/bmesh/intern/bmesh_core.cc @@ -761,7 +761,7 @@ static void bm_kill_only_edge(BMesh *bm, BMEdge *e) * low level function, only frees the face, * doesn't change or adjust surrounding geometry */ -static void bm_kill_only_face(BMesh *bm, BMFace *f) +void bm_kill_only_face(BMesh *bm, BMFace *f) { if (bm->act_face == f) { bm->act_face = nullptr; @@ -788,7 +788,7 @@ static void bm_kill_only_face(BMesh *bm, BMFace *f) * low level function, only frees the loop, * doesn't change or adjust surrounding geometry */ -static void bm_kill_only_loop(BMesh *bm, BMLoop *l) +void bm_kill_only_loop(BMesh *bm, BMLoop *l) { bm->totloop--; bm->elem_index_dirty |= BM_LOOP; diff --git a/source/blender/makesrna/intern/rna_brush.cc b/source/blender/makesrna/intern/rna_brush.cc index 3666d9cea50..9c6fa3c875e 100644 --- a/source/blender/makesrna/intern/rna_brush.cc +++ b/source/blender/makesrna/intern/rna_brush.cc @@ -9,6 +9,7 @@ #include #include #include +#include #include "DNA_brush_types.h" #include "DNA_gpencil_legacy_types.h" -- 2.30.2 From 534c53719250318b4e6535f00ecf05fcc7be0f99 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Fri, 22 Sep 2023 13:13:17 -0700 Subject: [PATCH 249/279] temp-sculpt-dyntopo: Fix crash in thumbnail generation code --- .../draw/intern/draw_cache_extract_mesh.cc | 2 +- .../draw_cache_extract_mesh_render_data.cc | 16 ++++++++++++++++ .../draw/intern/mesh_extractors/extract_mesh.hh | 1 + .../blender/windowmanager/intern/wm_operators.cc | 1 - 4 files changed, 18 insertions(+), 2 deletions(-) diff --git a/source/blender/draw/intern/draw_cache_extract_mesh.cc b/source/blender/draw/intern/draw_cache_extract_mesh.cc index 942d3fd20dd..a1b4c3474b3 100644 --- a/source/blender/draw/intern/draw_cache_extract_mesh.cc +++ b/source/blender/draw/intern/draw_cache_extract_mesh.cc @@ -385,7 +385,7 @@ BLI_INLINE void extract_task_range_run_iter(const MeshRenderData &mr, int stop; switch (iter_type) { case MR_ITER_LOOPTRI: - range_data.elems = is_mesh ? mr.looptris.data() : (void *)mr.edit_bmesh->looptris; + range_data.elems = is_mesh ? mr.looptris.data() : (void *)mr.bm_looptris.data(); func = is_mesh ? extract_range_iter_looptri_mesh : extract_range_iter_looptri_bm; stop = mr.tri_len; break; diff --git a/source/blender/draw/intern/draw_cache_extract_mesh_render_data.cc b/source/blender/draw/intern/draw_cache_extract_mesh_render_data.cc index 5cf3921a201..9766ad3d17a 100644 --- a/source/blender/draw/intern/draw_cache_extract_mesh_render_data.cc +++ b/source/blender/draw/intern/draw_cache_extract_mesh_render_data.cc @@ -446,6 +446,16 @@ MeshRenderData *mesh_render_data_create(Object *object, mr->eed_act = BM_mesh_active_edge_get(mr->bm); mr->eve_act = BM_mesh_active_vert_get(mr->bm); + BMeshCalcTessellation_Params bmpparams = {}; + bmpparams.face_normals = false; + + int tri_count = poly_to_tri_count(mr->bm->totface, mr->bm->totloop); + + BMLoop *(*looptri)[3] = static_cast( + MEM_mallocN(sizeof(void *) * 3 * tri_count, "sculpt draw extract loop tris")); + mr->bm_looptris = {looptri, tri_count}; + BM_mesh_calc_tessellation_ex(mr->bm, mr->bm_looptris.data(), &bmpparams); + mr->vert_crease_ofs = CustomData_get_offset_named( &mr->bm->vdata, CD_PROP_FLOAT, "crease_vert"); mr->edge_crease_ofs = CustomData_get_offset_named( @@ -468,6 +478,7 @@ MeshRenderData *mesh_render_data_create(Object *object, mr->edit_bmesh = me->edit_mesh; mr->me = (do_final) ? editmesh_eval_final : editmesh_eval_cage; mr->edit_data = is_mode_active ? mr->me->runtime->edit_data : nullptr; + mr->bm_looptris = {mr->edit_bmesh->looptris, mr->edit_bmesh->tottri}; /* If there is no distinct cage, hide unmapped edges that can't be selected. */ mr->hide_unmapped_edges = !do_final || editmesh_eval_final == editmesh_eval_cage; @@ -614,6 +625,11 @@ MeshRenderData *mesh_render_data_create(Object *object, void mesh_render_data_free(MeshRenderData *mr) { + /* Sculpt mode owns bm_looptris. */ + if (mr->extract_type == MR_EXTRACT_BMESH && mr->bm_looptris.data() && !mr->edit_bmesh) { + MEM_freeN(static_cast(mr->bm_looptris.data())); + } + MEM_delete(mr); } diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh.hh b/source/blender/draw/intern/mesh_extractors/extract_mesh.hh index 92adf795b86..794adaaeccf 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh.hh +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh.hh @@ -88,6 +88,7 @@ struct MeshRenderData { /* The triangulation of #Mesh faces, owned by the mesh. */ blender::Span looptris; blender::Span looptri_faces; + blender::MutableSpan bm_looptris; const int *material_indices; blender::Span vert_normals; blender::Span face_normals; diff --git a/source/blender/windowmanager/intern/wm_operators.cc b/source/blender/windowmanager/intern/wm_operators.cc index feaab219088..18b9f02e978 100644 --- a/source/blender/windowmanager/intern/wm_operators.cc +++ b/source/blender/windowmanager/intern/wm_operators.cc @@ -4029,7 +4029,6 @@ static void gesture_box_modal_keymap(wmKeyConfig *keyconf) WM_modalkeymap_assign(keymap, "MASK_OT_select_box"); WM_modalkeymap_assign(keymap, "PAINT_OT_mask_box_gesture"); WM_modalkeymap_assign(keymap, "SCULPT_OT_face_set_box_gesture"); - WM_modalkeymap_assign(keymap, "SCULPT_OT_project_box_gesture"); WM_modalkeymap_assign(keymap, "SCULPT_OT_trim_box_gesture"); WM_modalkeymap_assign(keymap, "VIEW2D_OT_zoom_border"); WM_modalkeymap_assign(keymap, "VIEW3D_OT_clip_border"); -- 2.30.2 From 895b201a8878a47c6ec32ce094dd090fdf31333f Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Tue, 26 Sep 2023 13:46:30 -0700 Subject: [PATCH 250/279] Revert Readme.MD --- README.md | 46 +++++++++++++++++++++++----------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index 6eeeb0c4c48..76243cbeff3 100644 --- a/README.md +++ b/README.md @@ -1,33 +1,33 @@ -Sculpt-Blender -============== + -Sculpt-Blender is an independent branch of Blender focused on sculpting. -It's purpose is to develop and maintain key improvements needed to make -Blender usable for a wider variety of sculpt workflows. +Blender +======= -It arose out of my personal artistic need for a better dynamic topology -system. +Blender is the free and open source 3D creation suite. +It supports the entirety of the 3D pipeline-modeling, rigging, animation, simulation, rendering, compositing, +motion tracking and video editing. -This is not a hard fork. The branch will be regularly synced with official Blender. +![Blender screenshot](https://code.blender.org/wp-content/uploads/2018/12/springrg.jpg "Blender screenshot") -Main supported features: -* Improved DynTopo that preserves attributes. -* Various edge boundaries (e.g. marked seams, face set boundaries, UV island boundaries, etc) - are preserved. -* Better hard surface modelling. +Project Pages +------------- -Changes will be limited to "needed" functional improvements. -Features like these are off the table for now: +- [Main Website](http://www.blender.org) +- [Reference Manual](https://docs.blender.org/manual/en/latest/index.html) +- [User Community](https://www.blender.org/community/) -* Sculpt layers. -* Node/stack based brush composer. -* Brush properties. +Development +----------- + +- [Build Instructions](https://wiki.blender.org/wiki/Building_Blender) +- [Code Review & Bug Tracker](https://projects.blender.org) +- [Developer Forum](https://devtalk.blender.org) +- [Developer Documentation](https://wiki.blender.org) -For developers: -* We'll try to avoid refactors. -* There is a copy of a new sculpt brush API in the branch, but only smooth brushes are allowed to use it - (it fixes a performance bug that particularly affects smooth brushes). - License ------- -- 2.30.2 From 88ab1fdae7719182d66220ea613257c6a8d72211 Mon Sep 17 00:00:00 2001 From: Joe Eagar Date: Tue, 26 Sep 2023 13:51:27 -0700 Subject: [PATCH 251/279] Update issue templates --- .github/ISSUE_TEMPLATE/bug_report.md | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 00000000000..44ced95acdb --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,24 @@ +--- +name: Bug report +about: Bug report +title: '' +labels: '' +assignees: '' + +--- + +**Describe the bug** +What is the bug doing. + +**Expected behavior** +What you expected to happen. + +**How to Reproduce** +How to reproduce the bug, including any screenshots. + +**.blend file** +A test .blend file if applicable + +**System Info** +Operating System: +Number of CPU cores (optional): -- 2.30.2 From 56c962aee5fdba808e7c93bf3dc15b194e668e2f Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Tue, 26 Sep 2023 13:55:42 -0700 Subject: [PATCH 252/279] Revert github stuff --- .github/ISSUE_TEMPLATE/bug_report.md | 24 ------------------------ .github/pull_request_template.md | 4 ---- .github/stale.yml | 21 --------------------- 3 files changed, 49 deletions(-) delete mode 100644 .github/ISSUE_TEMPLATE/bug_report.md delete mode 100644 .github/pull_request_template.md delete mode 100644 .github/stale.yml diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md deleted file mode 100644 index 44ced95acdb..00000000000 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ /dev/null @@ -1,24 +0,0 @@ ---- -name: Bug report -about: Bug report -title: '' -labels: '' -assignees: '' - ---- - -**Describe the bug** -What is the bug doing. - -**Expected behavior** -What you expected to happen. - -**How to Reproduce** -How to reproduce the bug, including any screenshots. - -**.blend file** -A test .blend file if applicable - -**System Info** -Operating System: -Number of CPU cores (optional): diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md deleted file mode 100644 index 4b8e99e669a..00000000000 --- a/.github/pull_request_template.md +++ /dev/null @@ -1,4 +0,0 @@ -This repository is only used as a mirror. Blender development happens on projects.blender.org. - -To get started with contributing code, please see: -https://wiki.blender.org/wiki/Process/Contributing_Code diff --git a/.github/stale.yml b/.github/stale.yml deleted file mode 100644 index db14bfd9d31..00000000000 --- a/.github/stale.yml +++ /dev/null @@ -1,21 +0,0 @@ -# Configuration for probot-stale - https://github.com/probot/stale -# This file is used on Blender's GitHub mirror to automatically close any pull request -# and invite contributors to join the official development platform on blender.org - -# Number of days of inactivity before an Issue or Pull Request becomes stale -daysUntilStale: 1 - -# Number of days of inactivity before an Issue or Pull Request with the stale label is closed. -# Set to false to disable. If disabled, issues still need to be closed manually, but will remain marked as stale. -daysUntilClose: 1 - -# Label to use when marking as stale -staleLabel: stale - -# Comment to post when closing a stale Issue or Pull Request. -closeComment: > - This issue has been automatically closed, because this repository is only - used as a mirror. Blender development happens on projects.blender.org. - - To get started contributing code, please read: - https://wiki.blender.org/wiki/Process/Contributing_Code -- 2.30.2 From 8ac2c44ef083d19c25d61337bda2c791b62b0ff4 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Tue, 26 Sep 2023 21:02:48 -0700 Subject: [PATCH 253/279] temp-sculpt-dyntopo: Fix face area calc Also enabled dyntopo settings for simplify brush for testing purposes. --- scripts/startup/bl_ui/properties_paint_common.py | 2 +- source/blender/blenkernel/intern/dyntopo.cc | 10 ---------- source/blender/editors/sculpt_paint/sculpt_smooth.cc | 10 ++++++---- 3 files changed, 7 insertions(+), 15 deletions(-) diff --git a/scripts/startup/bl_ui/properties_paint_common.py b/scripts/startup/bl_ui/properties_paint_common.py index 7b944b1a99e..f3748e22bda 100644 --- a/scripts/startup/bl_ui/properties_paint_common.py +++ b/scripts/startup/bl_ui/properties_paint_common.py @@ -140,7 +140,7 @@ class UnifiedPaintPanel: final_dyntopo = brush.dyntopo layout = layout.row(align=True) - layout.enabled = inherit + #layout.enabled = inherit layout.prop(final_dyntopo, prop_name, text=text, expand=expand) if not inherit: diff --git a/source/blender/blenkernel/intern/dyntopo.cc b/source/blender/blenkernel/intern/dyntopo.cc index b6eda0c7b13..c500b730227 100644 --- a/source/blender/blenkernel/intern/dyntopo.cc +++ b/source/blender/blenkernel/intern/dyntopo.cc @@ -310,16 +310,6 @@ BLI_INLINE float calc_weighted_length(EdgeQueueContext *eq_ctx, return len > eq_ctx->limit_len_max_sqr ? len * w * w : len; } case COLLAPSE: { -#if 0 - if (eq_ctx->brush_tester->is_sphere_or_tube) { - BrushSphere *sphere = static_cast(eq_ctx->brush_tester); - - float l1 = len_v3v3(v1->co, sphere->center()); - float l2 = len_v3v3(v2->co, sphere->center()); - float l = min_ff(min_ff(l1, l2) / sphere->radius(), 1.0f); - } -#endif - return len < eq_ctx->limit_len_min_sqr ? len + eq_ctx->limit_len_min_sqr * 1.0f * powf(w, 5.0) : len; diff --git a/source/blender/editors/sculpt_paint/sculpt_smooth.cc b/source/blender/editors/sculpt_paint/sculpt_smooth.cc index 0ce836cc387..208bbf4a807 100644 --- a/source/blender/editors/sculpt_paint/sculpt_smooth.cc +++ b/source/blender/editors/sculpt_paint/sculpt_smooth.cc @@ -939,10 +939,6 @@ static void do_smooth_brush_task(Object *ob, SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape( ss, &test, brush->falloff_shape); - if (brush->flag2 & BRUSH_SMOOTH_USE_AREA_WEIGHT) { - BKE_pbvh_check_tri_areas(ss->pbvh, node); - } - const int thread_id = BLI_task_parallel_thread_id(nullptr); AutomaskingNodeData automask_data; SCULPT_automasking_node_begin(ob, ss, ss->cache->automasking, &automask_data, node); @@ -1079,6 +1075,12 @@ void SCULPT_smooth( const float strength = (iteration != count) ? 1.0f : last; if (brush->flag2 & BRUSH_SMOOTH_USE_AREA_WEIGHT) { + threading::parallel_for(nodes.index_range(), 1, [&](const IndexRange range) { + for (const int i : range) { + BKE_pbvh_check_tri_areas(ss->pbvh, nodes[i]); + } + }); + BKE_pbvh_face_areas_begin(ss->pbvh); } -- 2.30.2 From 1e915c909ce89de7d3c58f4678394cdbf8f4733f Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Tue, 26 Sep 2023 23:58:33 -0700 Subject: [PATCH 254/279] temp-sculpt-dyntopo: Fix bug with dyntopo collapse ignoring marked seams --- source/blender/blenkernel/intern/dyntopo_collapse.cc | 8 +++++++- source/blender/blenkernel/intern/pbvh_bmesh.cc | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/source/blender/blenkernel/intern/dyntopo_collapse.cc b/source/blender/blenkernel/intern/dyntopo_collapse.cc index 7ac5389f355..079bbfd3bb8 100644 --- a/source/blender/blenkernel/intern/dyntopo_collapse.cc +++ b/source/blender/blenkernel/intern/dyntopo_collapse.cc @@ -528,6 +528,8 @@ BMVert *EdgeQueueContext::collapse_edge(PBVH *pbvh, BMEdge *e, BMVert *v1, BMVer pbvh_check_vert_boundary_bmesh(pbvh, v1); pbvh_check_vert_boundary_bmesh(pbvh, v2); + pbvh_check_edge_boundary_bmesh(pbvh, e); + int boundflag1 = BM_ELEM_CD_GET_INT(v1, pbvh->cd_boundary_flag); int boundflag2 = BM_ELEM_CD_GET_INT(v2, pbvh->cd_boundary_flag); int e_boundflag = BM_ELEM_CD_GET_INT(e, pbvh->cd_edge_boundary); @@ -537,7 +539,11 @@ BMVert *EdgeQueueContext::collapse_edge(PBVH *pbvh, BMEdge *e, BMVert *v1, BMVer return nullptr; } - if ((boundflag1 & SCULPTVERT_ALL_BOUNDARY) != (e_boundflag & SCULPTVERT_ALL_BOUNDARY)) { + /* TODO: Sharp angle flags aren't always matching between + * verts and edges, investigate. + */ + int edge_comp_flag = SCULPTVERT_ALL_BOUNDARY & ~SCULPT_BOUNDARY_SHARP_ANGLE; + if ((boundflag1 & edge_comp_flag) != (e_boundflag & edge_comp_flag)) { return nullptr; } diff --git a/source/blender/blenkernel/intern/pbvh_bmesh.cc b/source/blender/blenkernel/intern/pbvh_bmesh.cc index a198ba293cb..ee86b426d82 100644 --- a/source/blender/blenkernel/intern/pbvh_bmesh.cc +++ b/source/blender/blenkernel/intern/pbvh_bmesh.cc @@ -2746,7 +2746,7 @@ static uintptr_t tri_loopkey( * (currently just raycast), store the node's triangles and vertices. * * Skips triangles that are hidden. */ -ATTR_NO_OPT bool BKE_pbvh_bmesh_check_tris(PBVH *pbvh, PBVHNode *node) +bool BKE_pbvh_bmesh_check_tris(PBVH *pbvh, PBVHNode *node) { BMesh *bm = pbvh->header.bm; -- 2.30.2 From 43d8d2b8237840e43b783cebb9c3c8118d28811e Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Thu, 28 Sep 2023 14:02:23 -0700 Subject: [PATCH 255/279] temp-sculpt-dyntopo: Fix remesher thrashing on link skinny faces Also fixes bug where high-valence verts are generated during remeshing. --- source/blender/blenkernel/intern/dyntopo.cc | 34 ++++++++++++++++--- .../blenkernel/intern/dyntopo_intern.hh | 18 +++------- 2 files changed, 35 insertions(+), 17 deletions(-) diff --git a/source/blender/blenkernel/intern/dyntopo.cc b/source/blender/blenkernel/intern/dyntopo.cc index c500b730227..fefd099412d 100644 --- a/source/blender/blenkernel/intern/dyntopo.cc +++ b/source/blender/blenkernel/intern/dyntopo.cc @@ -352,6 +352,21 @@ void EdgeQueueContext::surface_smooth(BMVert *v, float fac) void EdgeQueueContext::insert_edge(BMEdge *e, float w) { if (!(e->head.hflag & EDGE_QUEUE_FLAG)) { + if (e->l) { + float len_sq = len_squared_v3v3(e->v1->co, e->v2->co); + float other_len = FLT_MAX; + + BMLoop *l = e->l; + do { + other_len = min_ff(other_len, len_squared_v3v3(l->next->e->v1->co, l->next->e->v2->co)); + other_len = min_ff(other_len, len_squared_v3v3(l->prev->e->v1->co, l->prev->e->v2->co)); + } while ((l = l->radial_next) != e->l); + + avg_elen += len_sq; + cross_elen += other_len; + totedge += 1.0f; + } + edge_heap.insert(w, e); e->head.hflag |= EDGE_QUEUE_FLAG; @@ -2092,20 +2107,31 @@ EdgeQueueContext::EdgeQueueContext(BrushTester *brush_tester_, ops[1] = PBVH_Collapse; totop = 2; - steps[1] = DYNTOPO_MAX_ITER_COLLAPSE; - steps[0] = DYNTOPO_MAX_ITER_SUBD; + float len1 = avg_elen / totedge; + float len2 = cross_elen / totedge; + + steps[0] = 1; + steps[1] = 1; + + /* Prevent thrashing on long skinny faces. */ + if (len2) { + float ratio = len1 / len2; + + /* Increase subdivision steps. */ + steps[0] = max_ii(min_ii(int(powf(ratio, 2.0f) * 10.0f), 500), 1); + } } else if (mode & PBVH_Subdivide) { ops[0] = PBVH_Subdivide; totop = 1; - steps[0] = DYNTOPO_MAX_ITER_SUBD; + steps[0] = 1; } else if (mode & PBVH_Collapse) { ops[0] = PBVH_Collapse; totop = 1; - steps[0] = DYNTOPO_MAX_ITER_COLLAPSE; + steps[0] = 1; } } diff --git a/source/blender/blenkernel/intern/dyntopo_intern.hh b/source/blender/blenkernel/intern/dyntopo_intern.hh index d610156f6f0..c12371b1d96 100644 --- a/source/blender/blenkernel/intern/dyntopo_intern.hh +++ b/source/blender/blenkernel/intern/dyntopo_intern.hh @@ -105,15 +105,6 @@ inline bool bm_elem_is_free(BMElem *elem, int htype) #define DYNTOPO_MAX_ITER 512 -/* Very long skinny edges are pathological for remeshing, - * they lead to degenerate geometry with 1/1 ratio between - * subdivison and collapse. Ideally we should detect this - * case and adjust the ratio dynamically, but for now just - * use a very high ratio all the time. - */ -#define DYNTOPO_MAX_ITER_COLLAPSE 64 -#define DYNTOPO_MAX_ITER_SUBD 1 - #define DYNTOPO_USE_HEAP #define DYNTOPO_USE_MINMAX_HEAP @@ -240,10 +231,11 @@ struct EdgeQueueContext { int cd_vert_mask_offset; int cd_vert_node_offset; int cd_face_node_offset; - float avg_elen; - float max_elen; - float min_elen; - float totedge; + float avg_elen = 0.0f; + float max_elen = 0.f; + float min_elen = 0.0f; + float cross_elen = 0.0f; /* used to detect skinny edges. */ + float totedge = 0.0f; bool local_mode; float surface_smooth_fac; -- 2.30.2 From 8d00a2897aa75df36e5033e14efe4e3f10acf77c Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Thu, 28 Sep 2023 14:12:37 -0700 Subject: [PATCH 256/279] temp-sculpt-dyntopo: Revert spreadsheet debug stuff --- .../spreadsheet_column_values.hh | 16 +-- .../spreadsheet_data_source_geometry.cc | 108 +----------------- 2 files changed, 4 insertions(+), 120 deletions(-) diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_column_values.hh b/source/blender/editors/space_spreadsheet/spreadsheet_column_values.hh index aef29c8719d..17b8215a9a9 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_column_values.hh +++ b/source/blender/editors/space_spreadsheet/spreadsheet_column_values.hh @@ -4,18 +4,11 @@ #pragma once -#include "MEM_guardedalloc.h" - #include "DNA_space_types.h" #include "BLI_generic_virtual_array.hh" -#include "BLI_math_vector_types.hh" #include "BLI_string_ref.hh" -#include "BLI_mempool.h" - -#include "bmesh.h" - namespace blender::ed::spreadsheet { eSpreadsheetColumnValueType cpp_type_to_column_type(const CPPType &type); @@ -29,7 +22,6 @@ class ColumnValues final { std::string name_; GVArray data_; - void *bmesh_data_ = nullptr; public: ColumnValues(std::string name, GVArray data) : name_(std::move(name)), data_(std::move(data)) @@ -38,13 +30,7 @@ class ColumnValues final { BLI_assert(data_); } - ColumnValues(const ColumnValues &b) = delete; - - ~ColumnValues() { - if (bmesh_data_) { - MEM_freeN(bmesh_data_); - } - } + virtual ~ColumnValues() = default; eSpreadsheetColumnValueType type() const { diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc index 62cd375cffe..406cb8f6841 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc +++ b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc @@ -44,13 +44,6 @@ #include "bmesh.h" -/* Uncomment to view sculpt attributes in the spreadsheet. */ -//#define DEBUG_SCULPT_BM_ATTRS - -#ifdef DEBUG_SCULPT_BM_ATTRS -# include "bmesh_varray.hh" -#endif - #include "spreadsheet_data_source_geometry.hh" #include "spreadsheet_intern.hh" @@ -58,23 +51,6 @@ using blender::nodes::geo_eval_log::ViewerNodeLog; namespace blender::ed::spreadsheet { -static BMesh *get_object_bmesh(Object *object_eval) -{ -#ifdef DEBUG_SCULPT_BM_ATTRS - if (object_eval->mode == OB_MODE_SCULPT) { - return object_eval->sculpt->bm; - } - else { -#endif - Object *object_orig = DEG_get_original_object(object_eval); - Mesh *mesh_orig = (Mesh *)object_orig->data; - - return mesh_orig->edit_mesh->bm; -#ifdef DEBUG_SCULPT_BM_ATTRS - } -#endif -} - void ExtraColumns::foreach_default_column_ids( FunctionRef fn) const { @@ -197,45 +173,6 @@ static std::unique_ptr build_mesh_debug_columns(const Mesh &mesh, void GeometryDataSource::foreach_default_column_ids( FunctionRef fn) const { -#ifdef DEBUG_SCULPT_BM_ATTRS - if (object_eval_->mode == OB_MODE_SCULPT && object_eval_->sculpt && object_eval_->sculpt->bm) { - CustomData *cdata = nullptr; - BMesh *bm = object_eval_->sculpt->bm; - - switch (domain_) { - case ATTR_DOMAIN_POINT: - cdata = &bm->vdata; - break; - case ATTR_DOMAIN_EDGE: - cdata = &bm->edata; - break; - case ATTR_DOMAIN_FACE: - cdata = &bm->pdata; - break; - } - - if (cdata) { - const eCustomDataMask typemask = CD_MASK_PROP_FLOAT | CD_MASK_PROP_FLOAT2 | - CD_MASK_PROP_FLOAT3 | CD_MASK_PROP_COLOR | - CD_MASK_PROP_BYTE_COLOR | CD_MASK_PROP_INT32 | - CD_MASK_PROP_INT8 | CD_MASK_ORIGINDEX; - - for (int i = 0; i < cdata->totlayer; i++) { - CustomDataLayer *layer = &cdata->layers[i]; - - if (!bke::allow_procedural_attribute_access(layer->name)) { - continue; - } - - if (CD_TYPE_AS_MASK(layer->type) & typemask) { - fn({layer->name}, false); - } - } - return; - } - } -#endif - if (!component_->attributes().has_value()) { return; } @@ -340,41 +277,6 @@ std::unique_ptr GeometryDataSource::get_column_values( } } -#ifdef DEBUG_SCULPT_BM_ATTRS - if (object_eval_->mode == OB_MODE_SCULPT && object_eval_->sculpt && object_eval_->sculpt->bm) { - char htype = 0; - BMesh *bm = object_eval_->sculpt->bm; - - CustomData *cdata = &bm->vdata; - for (int i = 0; !htype && i < 4; i++, cdata++) { - for (int j = 0; j < cdata->totlayer; j++) { - CustomDataLayer *layer = &cdata->layers[j]; - - if (STREQ(layer->name, column_id.name)) { - htype = 1 << i; - break; - } - } - } - - if (htype) { - BM_mesh_elem_table_ensure(object_eval_->sculpt->bm, htype); - - GVArray bmarray = blender::bmesh::bmesh_attr_gvarray( - object_eval_->sculpt->bm, htype, column_id.name); - - if (bmarray.size() > 0) { - printf("%s: bmesh adaptor: %s\n", __func__, column_id.name); - StringRefNull column_display_name = column_id.name; - return std::make_unique(column_display_name, std::move(bmarray)); - } - else { - printf("%s: unknown bmesh attribute %s\n", __func__, column_id.name); - } - } - } -#endif - bke::GAttributeReader attribute = attributes.lookup(column_id.name); if (!attribute) { return {}; @@ -409,11 +311,7 @@ bool GeometryDataSource::has_selection_filter() const if (object_orig->type != OB_MESH) { return false; } -#ifdef DEBUG_SCULPT_BM_ATTRS - if (!ELEM(object_orig->mode, OB_MODE_EDIT, OB_MODE_SCULPT)) { -#else if (object_orig->mode != OB_MODE_EDIT) { -#endif return false; } return true; @@ -452,13 +350,13 @@ IndexMask GeometryDataSource::apply_selection_filter(IndexMaskMemory &memory) co switch (component_->type()) { case bke::GeometryComponent::Type::Mesh: { BLI_assert(object_eval_->type == OB_MESH); - // BLI_assert(object_eval_->mode == OB_MODE_EDIT); + BLI_assert(object_eval_->mode == OB_MODE_EDIT); Object *object_orig = DEG_get_original_object(object_eval_); const Mesh *mesh_eval = geometry_set_.get_mesh(); const bke::AttributeAccessor attributes_eval = mesh_eval->attributes(); Mesh *mesh_orig = (Mesh *)object_orig->data; - BMesh *bm = get_object_bmesh(object_eval_); - BM_mesh_elem_table_ensure(bm, BM_VERT | BM_EDGE | BM_FACE); + BMesh *bm = mesh_orig->edit_mesh->bm; + BM_mesh_elem_table_ensure(bm, BM_VERT); const int *orig_indices = (const int *)CustomData_get_layer(&mesh_eval->vert_data, CD_ORIGINDEX); -- 2.30.2 From 2411cda1a763e2179a99c92df34dd3e01953a23d Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Thu, 28 Sep 2023 14:41:45 -0700 Subject: [PATCH 257/279] temp-sculpt-dyntopo: Cleanup warnings --- source/blender/blenkernel/BKE_sculpt.hh | 2 +- .../blender/blenkernel/intern/customdata.cc | 19 ++++++-------- source/blender/blenkernel/intern/dyntopo.cc | 25 ++----------------- .../blenkernel/intern/dyntopo_collapse.cc | 20 ++++++++++++--- source/blender/blenkernel/intern/paint.cc | 20 ++++++--------- .../sculpt_paint/sculpt_automasking.cc | 12 +++++---- .../makesrna/intern/rna_sculpt_paint.cc | 25 ------------------- 7 files changed, 43 insertions(+), 80 deletions(-) diff --git a/source/blender/blenkernel/BKE_sculpt.hh b/source/blender/blenkernel/BKE_sculpt.hh index 174fcf2f3bd..56cc8af817d 100644 --- a/source/blender/blenkernel/BKE_sculpt.hh +++ b/source/blender/blenkernel/BKE_sculpt.hh @@ -124,7 +124,7 @@ bool loop_is_corner(BMLoop *l, int cd_uv, float limit = 0.01, const CustomData * struct VertLoopSnapper { Vector, 16> snap_sets; Span layers; - Span &ls; + Span ls; Vector max_indices; float limit = 0.001; diff --git a/source/blender/blenkernel/intern/customdata.cc b/source/blender/blenkernel/intern/customdata.cc index 143087834d4..f7dc05ec6b0 100644 --- a/source/blender/blenkernel/intern/customdata.cc +++ b/source/blender/blenkernel/intern/customdata.cc @@ -1527,8 +1527,6 @@ static bool layerValidate_propfloat2(void *data, const uint totitems, const bool return has_errors; } -static void layerInterp_noop(const void **, const float *, const float *, int, void *) {} - static bool layerEqual_propfloat2(const void *data1, const void *data2) { const float2 &a = *static_cast(data1); @@ -2802,9 +2800,9 @@ static void customData_update_offsets(CustomData *data) CustomData_update_typemap(data); } +#ifdef WITH_ASAN void CustomData_bmesh_poison(const CustomData *data, void *block) { -#ifdef WITH_ASAN BLI_asan_poison(block, data->totsize); for (int i = 0; i < data->totlayer; i++) { CustomDataLayer *layer = data->layers + i; @@ -2812,15 +2810,19 @@ void CustomData_bmesh_poison(const CustomData *data, void *block) BLI_asan_unpoison(POINTER_OFFSET(block, layer->offset), size); } -#endif } +#else +void CustomData_bmesh_poison(const CustomData * /*data*/, void * /*block*/) {} +#endif +#ifdef WITH_ASAN void CustomData_bmesh_unpoison(const CustomData *data, void *block) { -#ifdef WITH_ASAN BLI_asan_unpoison(block, data->totsize); -#endif } +#else +void CustomData_bmesh_unpoison(const CustomData *, void *) {} +#endif /* to use when we're in the middle of modifying layers */ static int CustomData_get_layer_index__notypemap(const CustomData *data, @@ -4312,8 +4314,6 @@ void CustomData_bmesh_copy_data_exclude_by_type(const CustomData *source, static void CustomData_bmesh_copy_data_simple(CustomData *data, void *src_block, void **dest_block) { - bool was_new = false; - if (*dest_block == nullptr) { CustomData_bmesh_alloc_block(data, dest_block); @@ -4321,8 +4321,6 @@ static void CustomData_bmesh_copy_data_simple(CustomData *data, void *src_block, CustomData_bmesh_unpoison(data, *dest_block); memset(*dest_block, 0, data->totsize); CustomData_bmesh_poison(data, *dest_block); - - was_new = true; } } @@ -5681,7 +5679,6 @@ void CustomData_blend_read(BlendDataReader *reader, CustomData *data, const int int i = 0; while (i < data->totlayer) { CustomDataLayer *layer = &data->layers[i]; - const LayerTypeInfo *typeInfo = layerType_getInfo(eCustomDataType(layer->type)); if (layer->flag & CD_FLAG_EXTERNAL) { layer->flag &= ~CD_FLAG_IN_MEMORY; diff --git a/source/blender/blenkernel/intern/dyntopo.cc b/source/blender/blenkernel/intern/dyntopo.cc index fefd099412d..38b7caa5593 100644 --- a/source/blender/blenkernel/intern/dyntopo.cc +++ b/source/blender/blenkernel/intern/dyntopo.cc @@ -538,26 +538,6 @@ float dist_to_tri_sphere_simple(float p[3], float v1[3], float v2[3], float v3[3 #endif } -static bool skinny_bad_edge(BMEdge *e, const float limit = 4.0f) -{ - float len1 = len_v3v3(e->v1->co, e->v2->co); - - BMLoop *l = e->l; - do { - float len2 = len_v3v3(l->next->v->co, l->next->next->v->co); - if (len1 > 0.0f && len2 / len1 > limit) { - return true; - } - - len2 = len_v3v3(l->v->co, l->prev->v->co); - if (len1 > 0.0f && len2 / len1 > limit) { - return true; - } - } while ((l = l->radial_next) != e->l); - - return false; -} - static void add_split_edge_recursive( EdgeQueueContext *eq_ctx, BMLoop *l_edge, const float len_sq, float limit_len, int depth) { @@ -2401,7 +2381,6 @@ bool remesh_topology(BrushTester *brush_tester, /* Apply a time limit to avoid excessive hangs on pathological topology. */ using Clock = std::chrono::high_resolution_clock; - using TimePoint = std::chrono::time_point; quality *= quality; int time_limit = int(8.0f * (1.0f - quality) + 550.0f * quality); @@ -2479,7 +2458,7 @@ void BKE_pbvh_bmesh_add_face(PBVH *pbvh, struct BMFace *f, bool log_face, bool f int ni2 = BM_ELEM_CD_GET_INT(l->radial_next->f, pbvh->cd_face_node_offset); if (ni2 >= 0 && (ni2 >= pbvh->nodes.size() || !(pbvh->nodes[ni2].flag & PBVH_Leaf))) { - printf("%s: error: ni: %d totnode: %d\n", __func__, ni2, pbvh->nodes.size()); + printf("%s: error: ni: %d totnode: %d\n", __func__, ni2, int(pbvh->nodes.size())); l = l->next; continue; } @@ -3611,7 +3590,7 @@ static void interp_prop_data( void reproject_interp_data(CustomData *data, const void **src_blocks, const float *weights, - const float *sub_weights, + const float * /*sub_weights*/, int count, void *dst_block, eCustomDataMask typemask) diff --git a/source/blender/blenkernel/intern/dyntopo_collapse.cc b/source/blender/blenkernel/intern/dyntopo_collapse.cc index 079bbfd3bb8..f81419c2b05 100644 --- a/source/blender/blenkernel/intern/dyntopo_collapse.cc +++ b/source/blender/blenkernel/intern/dyntopo_collapse.cc @@ -102,7 +102,7 @@ static bool vert_is_nonmanifold(BMVert *v) template static void snap_corner_data( - BMesh *bm, BMEdge *e, BMVert *v_del, Span ls, int cd_offset, bool snap_midpoint) + BMesh * /*bm*/, BMEdge *e, BMVert *v_del, Span ls, int cd_offset, bool snap_midpoint) { using namespace blender; @@ -234,8 +234,22 @@ static void snap_corner_data( SumT min = {}, max = {}; for (int i = 0; i < SumT::type_length; i++) { - min[i] = FLT_MAX; - max[i] = FLT_MIN; + if constexpr (std::is_same_v) { + min[i] = 255; + max[i] = 0; + } + else if constexpr (std::is_same_v) { + min[i] = INT16_MAX; + max[i] = INT16_MIN; + } + else if constexpr (std::is_same_v) { + min[i] = INT32_MAX; + max[i] = INT32_MIN; + } + else if constexpr (std::is_same_v) { + min[i] = FLT_MAX; + max[i] = FLT_MIN; + } } for (int i : blocks[set].index_range()) { diff --git a/source/blender/blenkernel/intern/paint.cc b/source/blender/blenkernel/intern/paint.cc index 7cbf0954b3f..074d1a67242 100644 --- a/source/blender/blenkernel/intern/paint.cc +++ b/source/blender/blenkernel/intern/paint.cc @@ -2546,8 +2546,6 @@ int BKE_sculpt_mask_layers_ensure(Depsgraph *depsgraph, void BKE_sculpt_toolsettings_data_ensure(Scene *scene) { - bool exists = scene->toolsettings->sculpt; - BKE_paint_ensure(scene->toolsettings, (Paint **)&scene->toolsettings->sculpt); Sculpt *sd = scene->toolsettings->sculpt; @@ -2566,14 +2564,14 @@ void BKE_sculpt_toolsettings_data_ensure(Scene *scene) sd->automasking_view_normal_falloff = defaults->automasking_view_normal_limit; } - if (sd->detail_percent == 0.0f) { - sd->detail_percent = defaults->detail_percent; + if (sd->dyntopo.detail_percent == 0.0f) { + sd->dyntopo.detail_percent = defaults->detail_percent; } - if (sd->constant_detail == 0.0f) { - sd->constant_detail = defaults->constant_detail; + if (sd->dyntopo.constant_detail == 0.0f) { + sd->dyntopo.constant_detail = defaults->constant_detail; } - if (sd->detail_size == 0.0f) { - sd->detail_size = defaults->detail_size; + if (sd->dyntopo.detail_size == 0.0f) { + sd->dyntopo.detail_size = defaults->detail_size; } /* Set sane default tiling offsets. */ @@ -4751,7 +4749,7 @@ bool loop_is_corner(BMLoop *l, int cd_uv, float limit, const CustomData *ldata) } namespace detail { -static void corner_interp(CustomDataLayer *layer, +static void corner_interp(CustomDataLayer * /*layer*/, BMVert *v, BMLoop *l, Span loops, @@ -4807,7 +4805,7 @@ static void corner_interp(CustomDataLayer *layer, /* Interpolates loops surrounding a vertex, splitting any UV map by * island as appropriate and enforcing proper boundary conditions. */ -static void interp_face_corners_intern(PBVH *pbvh, +static void interp_face_corners_intern(PBVH * /*pbvh*/, BMVert *v, Span loops, Span ws, @@ -4841,8 +4839,6 @@ static void interp_face_corners_intern(PBVH *pbvh, } for (int i : ls.index_range()) { - BMLoop *l = ls[i]; - if (!corners[i]) { *BM_ELEM_CD_PTR(ls[i], layer->offset) = new_values[i]; } diff --git a/source/blender/editors/sculpt_paint/sculpt_automasking.cc b/source/blender/editors/sculpt_paint/sculpt_automasking.cc index c302f73cedf..c6123e675eb 100644 --- a/source/blender/editors/sculpt_paint/sculpt_automasking.cc +++ b/source/blender/editors/sculpt_paint/sculpt_automasking.cc @@ -82,7 +82,7 @@ bool SCULPT_is_automasking_mode_enabled(const Sculpt *sd, return (eAutomasking_flag)automasking & mode; } -bool SCULPT_is_automasking_enabled(const Sculpt *sd, const SculptSession *ss, const Brush *br) +bool SCULPT_is_automasking_enabled(const Sculpt *sd, const SculptSession * /*ss*/, const Brush *br) { if (SCULPT_is_automasking_mode_enabled(sd, br, BRUSH_AUTOMASKING_TOPOLOGY)) { return true; @@ -258,7 +258,7 @@ static float automasking_view_normal_factor(AutomaskingCache *automasking, automask_data); } -static float automasking_view_occlusion_factor(AutomaskingCache *automasking, +static float automasking_view_occlusion_factor(AutomaskingCache * /*automasking*/, SculptSession *ss, PBVHVertRef vertex, AutomaskingNodeData * /*automask_data*/, @@ -277,7 +277,7 @@ static float automasking_view_occlusion_factor(AutomaskingCache *automasking, /* Updates vertex stroke id. */ static float automasking_factor_end(SculptSession *ss, - AutomaskingCache *automasking, + AutomaskingCache * /*automasking*/, PBVHVertRef vertex, float value) { @@ -595,7 +595,9 @@ float SCULPT_automasking_factor_get(AutomaskingCache *automasking, return automasking_factor_end(ss, automasking, vert, mask); } -void SCULPT_automasking_cache_free(SculptSession *ss, Object *ob, AutomaskingCache *automasking) +void SCULPT_automasking_cache_free(SculptSession * /*ss*/, + Object * /*ob*/, + AutomaskingCache *automasking) { if (!automasking) { return; @@ -642,7 +644,7 @@ static void SCULPT_topology_automasking_init(Sculpt *sd, Object *ob) SCULPT_floodfill_init(ss, &flood); const float radius = ss->cache ? ss->cache->radius : FLT_MAX; SCULPT_floodfill_add_active(sd, ob, ss, &flood, radius); - + AutomaskFloodFillData fdata = {0}; fdata.radius = radius; diff --git a/source/blender/makesrna/intern/rna_sculpt_paint.cc b/source/blender/makesrna/intern/rna_sculpt_paint.cc index 5bb5b48e165..806bcfaf57b 100644 --- a/source/blender/makesrna/intern/rna_sculpt_paint.cc +++ b/source/blender/makesrna/intern/rna_sculpt_paint.cc @@ -863,31 +863,6 @@ static void rna_def_sculpt(BlenderRNA *brna) RNA_def_property_struct_type(prop, "DynTopoSettings"); RNA_def_property_pointer_sdna(prop, nullptr, "dyntopo"); - prop = RNA_def_property(srna, "detail_size", PROP_FLOAT, PROP_PIXEL); - RNA_def_property_ui_range(prop, 0.5, 40.0, 0.1, 2); - RNA_def_property_ui_scale_type(prop, PROP_SCALE_CUBIC); - RNA_def_property_ui_text( - prop, "Detail Size", "Maximum edge length for dynamic topology sculpting (in pixels)"); - RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, nullptr); - - prop = RNA_def_property(srna, "detail_percent", PROP_FLOAT, PROP_PERCENTAGE); - RNA_def_property_ui_range(prop, 0.5, 100.0, 10, 2); - RNA_def_property_ui_text( - prop, - "Detail Percentage", - "Maximum edge length for dynamic topology sculpting (in brush percenage)"); - RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, nullptr); - - prop = RNA_def_property(srna, "constant_detail_resolution", PROP_FLOAT, PROP_NONE); - RNA_def_property_float_sdna(prop, nullptr, "constant_detail"); - RNA_def_property_range(prop, 0.0001, FLT_MAX); - RNA_def_property_ui_range(prop, 0.001, 1000.0, 10, 2); - RNA_def_property_ui_text(prop, - "Resolution", - "Maximum edge length for dynamic topology sculpting (as divisor " - "of Blender unit - higher value means smaller edge length)"); - RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, nullptr); - const EnumPropertyItem *entry = rna_enum_brush_automasking_flag_items; do { prop = RNA_def_property(srna, entry->identifier, PROP_BOOLEAN, PROP_NONE); -- 2.30.2 From cbd822132ceb20fe1f1967b70af8c8f48d748748 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Thu, 28 Sep 2023 22:41:39 -0700 Subject: [PATCH 258/279] temp-sculpt-dyntopo: Fix crash in preview file saving --- .../draw/intern/mesh_extractors/extract_mesh_ibo_tris.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_tris.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_tris.cc index 9d4a5818442..8e36e112641 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_tris.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_tris.cc @@ -47,7 +47,8 @@ static void extract_tris_iter_face_bm(const MeshRenderData &mr, GPUIndexBufBuilder *elb = static_cast(_data); int tri_first_index_real = poly_to_tri_count(f_index, BM_elem_index_get(f->l_first)); - BMLoop *(*looptris)[3] = mr.edit_bmesh->looptris; + BMLoop *(*looptris)[3] = mr.bm_looptris.data(); + int tri_len = f->len - 2; for (int offs = 0; offs < tri_len; offs++) { BMLoop **elt = looptris[tri_first_index_real + offs]; -- 2.30.2 From 62cbd0673869f94eb881b924a3f070fed5be80f7 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Sun, 1 Oct 2023 16:02:16 -0700 Subject: [PATCH 259/279] temp-sculpt-dyntopo: Use faster dyntopo settings for snake hook brush --- source/blender/blenkernel/intern/brush.cc | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/source/blender/blenkernel/intern/brush.cc b/source/blender/blenkernel/intern/brush.cc index d3c45e27590..1e8aaf9c276 100644 --- a/source/blender/blenkernel/intern/brush.cc +++ b/source/blender/blenkernel/intern/brush.cc @@ -1865,10 +1865,12 @@ void BKE_brush_sculpt_reset(Brush *br) case SCULPT_TOOL_SNAKE_HOOK: br->alpha = 1.0f; br->rake_factor = 1.0f; - br->dyntopo.flag |= DYNTOPO_SUBDIVIDE | DYNTOPO_COLLAPSE | DYNTOPO_CLEANUP; - br->dyntopo.spacing = 0; - br->dyntopo.radius_scale = 1.25; - br->dyntopo.repeat = 1; + br->dyntopo.flag |= DYNTOPO_SUBDIVIDE | DYNTOPO_COLLAPSE; + br->dyntopo.flag &= ~DYNTOPO_CLEANUP; + br->dyntopo.spacing = 10; + br->dyntopo.quality = 0.3f; + br->dyntopo.radius_scale = 1.0; + br->dyntopo.repeat = 0; break; case SCULPT_TOOL_THUMB: br->size = 75; -- 2.30.2 From c138deefe2a0c2e59bce34bdbd0e3518ef913d8a Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Sat, 7 Oct 2023 02:10:42 -0700 Subject: [PATCH 260/279] temp-sculpt-dyntopo: Final fix long skinny edges Finally figured out the proper fix for long skinny edges, which is to fetch both the subdivide and collapse candidate edges from the minmax heap, then decide which leads to higher quality by comparing how far each is from the subdivide and collapse length limits. --- source/blender/blenkernel/intern/dyntopo.cc | 341 ++++++++++++------ .../blenkernel/intern/dyntopo_intern.hh | 15 +- source/blender/blenkernel/intern/pbvh.cc | 6 +- .../blender/blenkernel/intern/pbvh_bmesh.cc | 19 +- source/blender/blenlib/BLI_heap_minmax.hh | 35 +- source/blender/editors/sculpt_paint/sculpt.cc | 17 +- .../editors/sculpt_paint/sculpt_detail.cc | 11 +- .../editors/sculpt_paint/sculpt_intern.hh | 1 + 8 files changed, 325 insertions(+), 120 deletions(-) diff --git a/source/blender/blenkernel/intern/dyntopo.cc b/source/blender/blenkernel/intern/dyntopo.cc index 38b7caa5593..10c6aa960c0 100644 --- a/source/blender/blenkernel/intern/dyntopo.cc +++ b/source/blender/blenkernel/intern/dyntopo.cc @@ -52,7 +52,7 @@ #include #include -// #define CLEAR_TAGS_IN_THREAD +#define CLEAR_TAGS_IN_THREAD #define EDGE_QUEUE_FLAG BM_ELEM_TAG @@ -147,6 +147,9 @@ static void surface_smooth_v_safe( int boundflag = BM_ELEM_CD_GET_INT(v, pbvh->cd_boundary_flag); int bound1 = boundflag & boundmask; + if (boundflag & SCULPT_BOUNDARY_MESH) { + return; + } if (boundflag & (cornermask | SCULPT_BOUNDARY_SHARP_ANGLE)) { return; } @@ -296,6 +299,29 @@ enum WeightMode { COLLAPSE = 1, }; +#if 0 +static float get_cross_edge_len(BMEdge *e) +{ + BMLoop *l = e->l; + + float cross = 0.0f; + float tot = 0.0f; + do { + float l1 = len_squared_v3v3(l->next->e->v1->co, l->next->e->v2->co); + float l2 = len_squared_v3v3(l->prev->e->v1->co, l->prev->e->v2->co); + + cross += l1 + l2; + tot += 2.0f; + } while ((l = l->radial_next) != e->l); + + if (tot > 0.0f) { + cross /= tot; + } + + return cross; +} +#endif + BLI_INLINE float calc_weighted_length(EdgeQueueContext *eq_ctx, BMVert *v1, BMVert *v2, @@ -352,21 +378,6 @@ void EdgeQueueContext::surface_smooth(BMVert *v, float fac) void EdgeQueueContext::insert_edge(BMEdge *e, float w) { if (!(e->head.hflag & EDGE_QUEUE_FLAG)) { - if (e->l) { - float len_sq = len_squared_v3v3(e->v1->co, e->v2->co); - float other_len = FLT_MAX; - - BMLoop *l = e->l; - do { - other_len = min_ff(other_len, len_squared_v3v3(l->next->e->v1->co, l->next->e->v2->co)); - other_len = min_ff(other_len, len_squared_v3v3(l->prev->e->v1->co, l->prev->e->v2->co)); - } while ((l = l->radial_next) != e->l); - - avg_elen += len_sq; - cross_elen += other_len; - totedge += 1.0f; - } - edge_heap.insert(w, e); e->head.hflag |= EDGE_QUEUE_FLAG; @@ -705,6 +716,9 @@ static void unified_edge_queue_task_cb(void *__restrict userdata, * then accuracy in any individual runs. Profiling * has shown this loop overwhelms the L3 cache, * so randomly skip bits of it. + * + * Unfortunately profiling has shown it is necassary to clear + * the flags here and not in the main thread. */ #ifdef CLEAR_TAGS_IN_THREAD for (BMFace *f : *node->bm_faces) { @@ -714,7 +728,7 @@ static void unified_edge_queue_task_cb(void *__restrict userdata, f->head.hflag &= ~facetag; /* Stochastically skip faces. */ - if (rand.get_uint32() > (1 << 16)) { + if (rand.get_float() > 0.8f) { continue; } @@ -771,16 +785,8 @@ static void unified_edge_queue_task_cb(void *__restrict userdata, * but tangentially to surface. We can stochastically skip this and still get the * benefit to convergence. */ - if (do_smooth && rand.get_float() > 0.75f && - BM_ELEM_CD_GET_INT(l_iter->v, pbvh->cd_vert_node_offset) == ni) - { - PBVHVertRef sv = {(intptr_t)l_iter->v}; - surface_smooth_v_safe(eq_ctx->ss, - tdata->pbvh, - l_iter->v, - eq_ctx->surface_smooth_fac * - eq_ctx->mask_cb(sv, eq_ctx->mask_cb_data), - distort_correction_mode); + if (BM_ELEM_CD_GET_INT(l_iter->v, pbvh->cd_vert_node_offset) == ni) { + eq_ctx->stochastic_smooth(l_iter->v); } float w = 0.0f; @@ -2040,6 +2046,7 @@ EdgeQueueContext::EdgeQueueContext(BrushTester *brush_tester_, view_normal = view_normal_; ignore_loop_data = !bm->ldata.totlayer; + updatePBVH = updatePBVH_; cd_vert_mask_offset = pbvh->cd_vert_mask_offset; cd_vert_node_offset = pbvh->cd_vert_node_offset; @@ -2058,13 +2065,6 @@ EdgeQueueContext::EdgeQueueContext(BrushTester *brush_tester_, surface_smooth_fac = DYNTOPO_SAFE_SMOOTH_FAC; - if (mode & PBVH_LocalSubdivide) { - mode |= PBVH_Subdivide; - } - if (mode & PBVH_LocalSubdivide) { - mode |= PBVH_Collapse; - } - #ifdef DYNTOPO_REPORT report(); #endif @@ -2079,40 +2079,6 @@ EdgeQueueContext::EdgeQueueContext(BrushTester *brush_tester_, if (mode & (PBVH_Subdivide | PBVH_Collapse)) { unified_edge_queue_create(this, pbvh, mode & (PBVH_LocalSubdivide | PBVH_LocalCollapse)); } - - totop = 0; - - if ((mode & PBVH_Subdivide) && (mode & PBVH_Collapse)) { - ops[0] = PBVH_Subdivide; - ops[1] = PBVH_Collapse; - totop = 2; - - float len1 = avg_elen / totedge; - float len2 = cross_elen / totedge; - - steps[0] = 1; - steps[1] = 1; - - /* Prevent thrashing on long skinny faces. */ - if (len2) { - float ratio = len1 / len2; - - /* Increase subdivision steps. */ - steps[0] = max_ii(min_ii(int(powf(ratio, 2.0f) * 10.0f), 500), 1); - } - } - else if (mode & PBVH_Subdivide) { - ops[0] = PBVH_Subdivide; - totop = 1; - - steps[0] = 1; - } - else if (mode & PBVH_Collapse) { - ops[0] = PBVH_Collapse; - totop = 1; - - steps[0] = 1; - } } void EdgeQueueContext::start() @@ -2139,7 +2105,7 @@ bool EdgeQueueContext::done() return true; } - return totop == 0 || edge_heap.empty(); + return (mode & (PBVH_Collapse | PBVH_Subdivide | PBVH_LocalCollapse)) == 0; } bool EdgeQueueContext::cleanup_valence_34() @@ -2149,9 +2115,7 @@ bool EdgeQueueContext::cleanup_valence_34() void EdgeQueueContext::finish() { - while (!edge_heap.empty()) { - BMEdge *e = edge_heap.pop_max(); - + for (BMEdge *e : edge_heap.values()) { if (!BM_elem_is_free(reinterpret_cast(e), BM_EDGE)) { e->head.hflag &= ~EDGE_QUEUE_FLAG; } @@ -2178,8 +2142,8 @@ void EdgeQueueContext::finish() /* Recursively split nodes that have gotten too many * elements */ - if (updatePBVH) { - pbvh_bmesh_node_limit_ensure(pbvh, i); + if (updatePBVH) { // && !(G.debug_value & 1024)) { + // pbvh_bmesh_node_limit_ensure(pbvh, i); } } } @@ -2218,38 +2182,204 @@ void EdgeQueueContext::finish() BM_log_entry_add_ex(pbvh->header.bm, pbvh->bm_log, true); } +BMEdge *EdgeQueueContext::pop_invalid_edges(BMEdge *in_e, float &w, bool is_max) +{ + BMEdge *e = in_e; + WeightMode weightmode = is_max ? SPLIT : COLLAPSE; + + while (!edge_heap.empty() && e && + (bm_elem_is_free((BMElem *)e, BM_EDGE) || + fabs(calc_weighted_length(this, e->v1, e->v2, weightmode) - w) > w * 0.1)) + { + if (is_max) { + edge_heap.pop_max(); + } + else { + edge_heap.pop_min(); + } + + if (edge_heap.empty()) { + return static_cast(nullptr); + } + + /* The edge was freed. */ + if (bm_elem_is_free((BMElem *)e, BM_EDGE)) { + e = is_max ? edge_heap.pop_max(&w) : edge_heap.pop_min(&w); + continue; + } + + /* The weight was wrong. */ + e->head.hflag &= ~EDGE_QUEUE_FLAG; + edge_heap.insert(calc_weighted_length(this, e->v1, e->v2, weightmode), e); + + if (is_max) { + e = edge_heap.peek_max(&w); + } + else { + e = edge_heap.peek_min(&w); + } + } + + return BM_elem_is_free(reinterpret_cast(e), BM_EDGE) ? nullptr : e; +} + +void EdgeQueueContext::stochastic_smooth(BMVert *v) +{ + if (!surface_relax) { + return; + } + + if (rand.get_float() > 0.9) { + surface_smooth_v_safe(ss, + pbvh, + v, + surface_smooth_fac * + mask_cb({reinterpret_cast(v)}, mask_cb_data), + distort_correction_mode); + } +} + +/* Collapse and subdivide. */ +void EdgeQueueContext::step_multi() +{ + + float min_w, max_w; + BMEdge *min_e = edge_heap.peek_min(&min_w); + min_e = pop_invalid_edges(min_e, min_w, false); + if (edge_heap.empty()) { + return; + } + + BMEdge *max_e = edge_heap.peek_max(&max_w); + max_e = pop_invalid_edges(max_e, max_w, true); + if (edge_heap.empty()) { + return; + } + + if (min_w > limit_len_min_sqr) { + min_e = nullptr; + } + if (max_w < limit_len_max_sqr) { + max_e = nullptr; + } + + if (!min_e && !max_e) { + return; + } + + BMEdge *e = nullptr; + + PBVHTopologyUpdateMode op; + if (min_e && max_e && (min_e == max_e || fabs(min_w - max_w) < 0.0001f)) { + op = (count % 2) ? PBVH_Subdivide : PBVH_Collapse; + if (op == PBVH_Subdivide) { + e = max_e; + } + else { + e = min_e; + } + } + else if (!min_e) { + e = max_e; + op = PBVH_Subdivide; + } + else if (!max_e) { + e = min_e; + op = PBVH_Collapse; + } + else { + float l1 = sqrtf(calc_weighted_length(this, min_e->v1, min_e->v2, COLLAPSE)); + float l2 = sqrtf(calc_weighted_length(this, max_e->v1, max_e->v2, SPLIT)); + + if ((limit_len_min - l1) * 2.0 > (l2 - limit_len_max)) { + op = PBVH_Collapse; + e = min_e; + } + else { + op = PBVH_Subdivide; + e = max_e; + } + } + + if (!e) { + return; + } + + modified = true; + e->head.hflag &= ~EDGE_QUEUE_FLAG; + + stochastic_smooth(e->v1); + stochastic_smooth(e->v2); + + switch (op) { + case PBVH_Collapse: + edge_heap.pop_min(); + collapse_edge(pbvh, e, e->v1, e->v2); + break; + case PBVH_Subdivide: + edge_heap.pop_max(); + split_edge(e); + break; + } + + count++; +} + +/* Collapse or subdivide. */ +void EdgeQueueContext::step_single() +{ + BMEdge *e = nullptr; + float w; + + switch (mode & (PBVH_Subdivide | PBVH_Collapse)) { + case PBVH_Collapse: + e = edge_heap.pop_min(&w); + e = pop_invalid_edges(e, w, false); + + if (e) { + e->head.hflag &= ~EDGE_QUEUE_FLAG; + modified = true; + stochastic_smooth(e->v1); + stochastic_smooth(e->v2); + collapse_edge(pbvh, e, e->v1, e->v2); + } + break; + case PBVH_Subdivide: + e = edge_heap.pop_max(&w); + e = pop_invalid_edges(e, w, true); + + if (e) { + e->head.hflag &= ~EDGE_QUEUE_FLAG; + modified = true; + stochastic_smooth(e->v1); + stochastic_smooth(e->v2); + split_edge(e); + } + break; + } + + count++; +} + void EdgeQueueContext::step() { if (done()) { return; } - BMEdge *e = nullptr; - - if (count >= steps[curop]) { - curop = (curop + 1) % totop; - count = 0; - + /* TODO: this has something to do with detail floodfill op, review it. */ + if (count % 100 == 0) { flushed_ = true; } - RandomNumberGenerator srand(PIL_check_seconds_timer() * 10000); - - auto do_smooth = [&](BMVert *v) { - if (!surface_relax) { - return; - } - - if (srand.get_float() > 0.7) { - surface_smooth_v_safe(ss, - pbvh, - v, - surface_smooth_fac * - mask_cb({reinterpret_cast(v)}, mask_cb_data), - distort_correction_mode); - } - }; + if ((mode & (PBVH_Subdivide | PBVH_Collapse)) == (PBVH_Subdivide | PBVH_Collapse)) { + step_multi(); + } + else if (mode & (PBVH_Subdivide | PBVH_Collapse)) { + step_single(); + } +#if 0 switch (ops[curop]) { case PBVH_None: break; @@ -2277,8 +2407,8 @@ void EdgeQueueContext::step() break; } - do_smooth(e->v1); - do_smooth(e->v2); + stochastic_smooth(e->v1); + stochastic_smooth(e->v2); e->head.hflag &= ~EDGE_QUEUE_FLAG; split_edge(e); @@ -2311,8 +2441,8 @@ void EdgeQueueContext::step() break; } - do_smooth(e->v1); - do_smooth(e->v2); + stochastic_smooth(e->v1); + stochastic_smooth(e->v2); modified = true; collapse_edge(pbvh, e, e->v1, e->v2); @@ -2327,6 +2457,7 @@ void EdgeQueueContext::step() } count++; +#endif } void EdgeQueueContext::report() @@ -2383,7 +2514,7 @@ bool remesh_topology(BrushTester *brush_tester, using Clock = std::chrono::high_resolution_clock; quality *= quality; - int time_limit = int(8.0f * (1.0f - quality) + 550.0f * quality); + int time_limit = int(1.0f * (1.0f - quality) + 550.0f * quality); auto time = Clock::now(); Clock::duration limit = std::chrono::duration_cast( @@ -2399,9 +2530,11 @@ bool remesh_topology(BrushTester *brush_tester, eq_ctx.finish(); - // printf("time: %dms\n", - // int(std::chrono::duration_cast(Clock::now() - - // time).count())); +#if 0 + printf("time: %dms\n", + int(std::chrono::duration_cast(Clock::now() - + time).count())); +#endif return eq_ctx.modified; } diff --git a/source/blender/blenkernel/intern/dyntopo_intern.hh b/source/blender/blenkernel/intern/dyntopo_intern.hh index c12371b1d96..ed01134050d 100644 --- a/source/blender/blenkernel/intern/dyntopo_intern.hh +++ b/source/blender/blenkernel/intern/dyntopo_intern.hh @@ -231,11 +231,6 @@ struct EdgeQueueContext { int cd_vert_mask_offset; int cd_vert_node_offset; int cd_face_node_offset; - float avg_elen = 0.0f; - float max_elen = 0.f; - float min_elen = 0.0f; - float cross_elen = 0.0f; /* used to detect skinny edges. */ - float totedge = 0.0f; bool local_mode; float surface_smooth_fac; @@ -259,14 +254,13 @@ struct EdgeQueueContext { bool updatePBVH = false; int steps[2]; - PBVHTopologyUpdateMode ops[2]; - int totop = 0; PBVH *pbvh; bool ignore_loop_data = false; bool modified = false; int count = 0; + int _i = 0; int curop = 0; EdgeQueueContext(BrushTester *brush_tester, @@ -308,7 +302,14 @@ struct EdgeQueueContext { blender::RandomNumberGenerator rand; + /* Stochastically applies to vertices (uses a simple skip test). */ + void stochastic_smooth(BMVert *v); + private: + BMEdge *pop_invalid_edges(BMEdge *in_e, float &w, bool is_max); + void step_single(); + void step_multi(); + bool flushed_ = false; }; diff --git a/source/blender/blenkernel/intern/pbvh.cc b/source/blender/blenkernel/intern/pbvh.cc index 50402715cbe..117e5f0c897 100644 --- a/source/blender/blenkernel/intern/pbvh.cc +++ b/source/blender/blenkernel/intern/pbvh.cc @@ -423,8 +423,12 @@ static int partition_indices_material( void pbvh_grow_nodes(PBVH *pbvh, int totnode) { + int old = pbvh->nodes.size(); pbvh->nodes.resize(totnode); - pbvh->nodes[pbvh->nodes.size() - 1].id = pbvh->nodes.size(); + + for (int i = old; i < totnode; i++) { + pbvh->nodes[i].id = pbvh->idgen++; + } } /* Add a vertex to the map, with a positive value for unique vertices and diff --git a/source/blender/blenkernel/intern/pbvh_bmesh.cc b/source/blender/blenkernel/intern/pbvh_bmesh.cc index ee86b426d82..e66ed613308 100644 --- a/source/blender/blenkernel/intern/pbvh_bmesh.cc +++ b/source/blender/blenkernel/intern/pbvh_bmesh.cc @@ -3342,6 +3342,21 @@ static void pbvh_bmesh_compact_tree(PBVH *bvh) MEM_freeN(map); } +namespace blender::bke::pbvh { +void split_bmesh_nodes(PBVH *pbvh) +{ + for (int i : pbvh->nodes.index_range()) { + PBVHNode *n = &pbvh->nodes[i]; + + if (n->flag & PBVH_Leaf) { + /* Recursively split nodes that have gotten too many + * elements */ + pbvh_bmesh_node_limit_ensure(pbvh, i); + } + } +} +} // namespace blender::bke::pbvh + /* Prunes leaf nodes that are too small or degenerate. */ static void pbvh_bmesh_balance_tree(PBVH *pbvh) { @@ -3726,8 +3741,6 @@ static void pbvh_bmesh_join_nodes(PBVH *pbvh) namespace blender::bke::dyntopo { void after_stroke(PBVH *pbvh, bool force_balance) { - int totnode = pbvh->nodes.size(); - BKE_pbvh_update_bounds(pbvh, (PBVH_UpdateBB | PBVH_UpdateOriginalBB | PBVH_UpdateRedraw)); pbvh_bmesh_check_nodes(pbvh); @@ -3741,7 +3754,7 @@ void after_stroke(PBVH *pbvh, bool force_balance) pbvh_bmesh_check_nodes(pbvh); pbvh->balance_counter = 0; - totnode = pbvh->nodes.size(); + int totnode = pbvh->nodes.size(); for (int i = 0; i < totnode; i++) { PBVHNode *n = &pbvh->nodes[i]; diff --git a/source/blender/blenlib/BLI_heap_minmax.hh b/source/blender/blenlib/BLI_heap_minmax.hh index a28121042b0..22df09d75b8 100644 --- a/source/blender/blenlib/BLI_heap_minmax.hh +++ b/source/blender/blenlib/BLI_heap_minmax.hh @@ -208,9 +208,21 @@ class MinMaxHeap { return max().weight; } - Value pop_min() + Value peek_min(float *r_w = nullptr) + { + if (r_w) { + *r_w = nodes[0].weight; + } + + return nodes[0].value; + } + + Value pop_min(float *r_w = nullptr) { if (nodes.size() == 1) { + if (r_w) { + *r_w = nodes[0].weight; + } return nodes.pop_last().value; } @@ -221,6 +233,10 @@ class MinMaxHeap { #endif Value ret = nodes[0].value; + if (r_w) { + *r_w = nodes[0].weight; + } + MinMaxHeapNode last = heap_pop_last(); nodes[0].weight = last.weight; @@ -237,6 +253,23 @@ class MinMaxHeap { return ret; } + Value peek_max(float *r_w = nullptr) + { + if (nodes.size() == 1) { + if (r_w) { + *r_w = nodes[0].weight; + } + return nodes[0].value; + } + + MinMaxHeapNode &node = max(); + if (r_w) { + *r_w = node.weight; + } + + return node.value; + } + Value pop_max(float *r_w = nullptr) { if (nodes.size() == 1) { diff --git a/source/blender/editors/sculpt_paint/sculpt.cc b/source/blender/editors/sculpt_paint/sculpt.cc index 191b3b39ead..72f92603ad8 100644 --- a/source/blender/editors/sculpt_paint/sculpt.cc +++ b/source/blender/editors/sculpt_paint/sculpt.cc @@ -46,6 +46,7 @@ #include "BKE_colortools.h" #include "BKE_context.h" #include "BKE_dyntopo.hh" +#include "BKE_global.h" #include "BKE_image.h" #include "BKE_key.h" #include "BKE_lib_id.h" @@ -3939,9 +3940,10 @@ static void sculpt_topology_update( actf = BM_idmap_get_id(ss->bm_idmap, (BMElem *)ss->active_face.i); } - blender::bke::dyntopo::BrushSphere sphere_tester(ss->cache->location, ss->cache->radius); + blender::bke::dyntopo::BrushSphere sphere_tester(ss->cache->location, + ss->cache->radius * radius_scale); blender::bke::dyntopo::BrushTube tube_tester( - ss->cache->location, ss->cache->view_normal, ss->cache->radius); + ss->cache->location, ss->cache->view_normal, ss->cache->radius * radius_scale); /* do nodes under the brush cursor */ blender::bke::dyntopo::remesh_topology_nodes( @@ -6308,6 +6310,10 @@ static bool sculpt_stroke_test_start(bContext *C, wmOperator *op, const float mv return false; } +namespace blender::bke::pbvh { +void split_bmesh_nodes(PBVH *pbvh); +} + static void sculpt_stroke_update_step(bContext *C, wmOperator * /*op*/, PaintStroke *stroke, @@ -6414,6 +6420,13 @@ static void sculpt_stroke_update_step(bContext *C, copy_v3_v3(ss->cache->true_location, location); } + if (ss->bm && do_dyntopo) { + if (ss->cache->stroke_distance_t - ss->cache->last_dyntopo_nodesplit_t > 0.3f) { + ss->cache->last_dyntopo_nodesplit_t = ss->cache->stroke_distance_t; + blender::bke::pbvh::split_bmesh_nodes(ss->pbvh); + } + } + /* Hack to fix noise texture tearing mesh. */ sculpt_fix_noise_tear(sd, ob); diff --git a/source/blender/editors/sculpt_paint/sculpt_detail.cc b/source/blender/editors/sculpt_paint/sculpt_detail.cc index dfecc2f2be4..b4eb5b7f466 100644 --- a/source/blender/editors/sculpt_paint/sculpt_detail.cc +++ b/source/blender/editors/sculpt_paint/sculpt_detail.cc @@ -129,6 +129,7 @@ static int sculpt_detail_flood_fill_run(Scene *scene, void *mask_cb_data; SCULPT_dyntopo_automasking_init(ss, sd, nullptr, ob, &mask_cb, &mask_cb_data); + BM_log_entry_check_customdata(ss->bm, ss->bm_log); PBVHTopologyUpdateMode mode = PBVHTopologyUpdateMode(0); if (ss->cached_dyntopo.flag & DYNTOPO_SUBDIVIDE) { @@ -203,7 +204,13 @@ static int sculpt_detail_flood_fill_run(Scene *scene, remesher.surface_smooth_fac = 0.25; remesher.start(); - while (!remesher.done()) { + float quality = ss->cached_dyntopo.quality; + float time_limit = (min_ff(1.0f * (100.0f - quality + 2500.0f * quality), 2500.0f))*0.001f; + float quality_time = PIL_check_seconds_timer(); + + printf("Remesh\n"); + + while (!remesher.done() && PIL_check_seconds_timer() - quality_time < time_limit) { remesher.step(); if (developer_mode && @@ -223,9 +230,9 @@ static int sculpt_detail_flood_fill_run(Scene *scene, } remesher.finish(); + /* Push a new subentry. */ BM_log_entry_add_ex(ss->bm, ss->bm_log, true); - blender::bke::dyntopo::after_stroke(ss->pbvh, true); unlock_main_thread(); diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.hh b/source/blender/editors/sculpt_paint/sculpt_intern.hh index 2b94f9eeba1..4a5787f76de 100644 --- a/source/blender/editors/sculpt_paint/sculpt_intern.hh +++ b/source/blender/editors/sculpt_paint/sculpt_intern.hh @@ -714,6 +714,7 @@ struct StrokeCache { float last_stroke_distance_t; float last_dyntopo_t; + float last_dyntopo_nodesplit_t; float last_smooth_t[SCULPT_MAX_SYMMETRY_PASSES]; float last_rake_t[SCULPT_MAX_SYMMETRY_PASSES]; -- 2.30.2 From 1fc7b9986b97198db0c7f4347f4e548dc01887ee Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Sat, 7 Oct 2023 12:02:27 -0700 Subject: [PATCH 261/279] temp-sculpt-dyntopo: Fix remesher not working with dyntopo Also fixed a couple of truly nasty undo bugs. --- source/blender/blenkernel/intern/dyntopo.cc | 1 - source/blender/blenkernel/intern/paint.cc | 15 ++ source/blender/bmesh/intern/bmesh_idmap.cc | 140 ++++++++++-------- source/blender/bmesh/intern/bmesh_idmap.h | 5 +- source/blender/bmesh/intern/bmesh_log.cc | 20 ++- .../blender/editors/object/object_remesh.cc | 44 ++++-- .../editors/sculpt_paint/sculpt_dyntopo.cc | 2 +- .../editors/sculpt_paint/sculpt_undo.cc | 113 ++++++-------- 8 files changed, 193 insertions(+), 147 deletions(-) diff --git a/source/blender/blenkernel/intern/dyntopo.cc b/source/blender/blenkernel/intern/dyntopo.cc index 10c6aa960c0..0c74bd00bb8 100644 --- a/source/blender/blenkernel/intern/dyntopo.cc +++ b/source/blender/blenkernel/intern/dyntopo.cc @@ -2491,7 +2491,6 @@ void EdgeQueueContext::report() printf("v: %.2f e: %.2f l: %.2f f: %.2f\n", fvmem, femem, flmem, ffmem); } -// EdgeQueueContext bool remesh_topology(BrushTester *brush_tester, Object *ob, PBVH *pbvh, diff --git a/source/blender/blenkernel/intern/paint.cc b/source/blender/blenkernel/intern/paint.cc index 074d1a67242..ef4af05b880 100644 --- a/source/blender/blenkernel/intern/paint.cc +++ b/source/blender/blenkernel/intern/paint.cc @@ -1803,9 +1803,24 @@ void BKE_sculpt_ensure_idmap(Object *ob) ob->sculpt->bm_idmap = BM_idmap_new(ob->sculpt->bm, BM_VERT | BM_EDGE | BM_FACE); BM_idmap_check_ids(ob->sculpt->bm_idmap); + if (ob->sculpt->bm_log) { + BM_log_set_idmap(ob->sculpt->bm_log, ob->sculpt->bm_idmap); + } + + if (ob->sculpt->pbvh) { + BKE_pbvh_set_idmap(ob->sculpt->pbvh, ob->sculpt->bm_idmap); + } + /* Push id attributes into base mesh customdata layout. */ + BKE_sculptsession_update_attr_refs(ob); BKE_sculptsession_sync_attributes(ob, static_cast(ob->data), true); } + else { + if (BM_idmap_check_attributes(ob->sculpt->bm_idmap)) { + BKE_sculptsession_update_attr_refs(ob); + BKE_sculptsession_sync_attributes(ob, static_cast(ob->data), true); + } + } } void BKE_sculpt_distort_correction_set(Object *ob, eAttrCorrectMode value) diff --git a/source/blender/bmesh/intern/bmesh_idmap.cc b/source/blender/bmesh/intern/bmesh_idmap.cc index cec89f073aa..ef5cc4b9f66 100644 --- a/source/blender/bmesh/intern/bmesh_idmap.cc +++ b/source/blender/bmesh/intern/bmesh_idmap.cc @@ -11,6 +11,7 @@ #include "BKE_customdata.h" #include "DNA_customdata_types.h" +#include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" #include "bmesh_idmap.h" @@ -35,6 +36,23 @@ static void bm_idmap_debug_check_init(BMesh *bm) } #endif +const char *BM_idmap_attr_name_get(int htype) +{ + switch (htype) { + case BM_VERT: + return "vertex_id"; + case BM_EDGE: + return "edge_id"; + case BM_LOOP: + return "corner_id"; + case BM_FACE: + return "face_id"; + default: + BLI_assert_unreachable(); + return "error"; + } +} + static void idmap_log_message(const char *fmt, ...) { va_list args; @@ -80,29 +98,6 @@ BMIdMap *BM_idmap_new(BMesh *bm, int elem_mask) return idmap; } -static const char elem_names[9][16] = { - "corrupted", // 0 - "vertex", // 1 - "edge", // 2 - "corrupted", // 3 - "loop", // 4 - "corrupted", // 5 - "corrupted", // 6 - "corrupted", // 7 - "face", // 8 -}; - -#ifdef DEBUG_BM_IDMAP -static const char *get_type_name(char htype) -{ - if (htype <= 0 || htype >= 9) { - return "corrupted"; - } - - return elem_names[int(htype)]; -} -#endif - template static constexpr char get_elem_type() { if constexpr (std::is_same_v) { @@ -187,6 +182,22 @@ static void idmap_grow_map(BMIdMap *idmap, int newid) idmap->map_size = newsize; } +void BM_idmap_clear_attributes_mesh(Mesh *me) +{ + CustomData_free_layer_named(&me->vert_data, BM_idmap_attr_name_get(BM_VERT), me->totvert); + CustomData_free_layer_named(&me->edge_data, BM_idmap_attr_name_get(BM_EDGE), me->totedge); + CustomData_free_layer_named(&me->loop_data, BM_idmap_attr_name_get(BM_LOOP), me->totloop); + CustomData_free_layer_named(&me->face_data, BM_idmap_attr_name_get(BM_FACE), me->faces_num); +} + +void BM_idmap_clear_attributes(BMesh *bm) +{ + BM_data_layer_free_named(bm, &bm->vdata, BM_idmap_attr_name_get(BM_VERT)); + BM_data_layer_free_named(bm, &bm->edata, BM_idmap_attr_name_get(BM_EDGE)); + BM_data_layer_free_named(bm, &bm->ldata, BM_idmap_attr_name_get(BM_LOOP)); + BM_data_layer_free_named(bm, &bm->pdata, BM_idmap_attr_name_get(BM_FACE)); +} + void BM_idmap_check_ids(BMIdMap *idmap) { BMIter iter; @@ -298,54 +309,57 @@ void BM_idmap_check_ids(BMIdMap *idmap) idmap->maxid = max_id + 1; } -void BM_idmap_check_attributes(BMIdMap *idmap) +static bool bm_idmap_check_attr(BMIdMap *idmap, int type) { - auto check_attr = [&](int type) { - if (!(idmap->flag & type)) { - return; - } + if (!(idmap->flag & type)) { + return false; + } - CustomData *cdata; - const char *name; + CustomData *cdata; + const char *name = BM_idmap_attr_name_get(type); - switch (type) { - case BM_VERT: - name = "vertex_id"; - cdata = &idmap->bm->vdata; - break; - case BM_EDGE: - name = "edge_id"; - cdata = &idmap->bm->edata; - break; - case BM_LOOP: - name = "corner_id"; - cdata = &idmap->bm->ldata; - break; - case BM_FACE: - name = "face_id"; - cdata = &idmap->bm->pdata; - break; - default: - BLI_assert_unreachable(); - return; - } + switch (type) { + case BM_VERT: + cdata = &idmap->bm->vdata; + break; + case BM_EDGE: + cdata = &idmap->bm->edata; + break; + case BM_LOOP: + cdata = &idmap->bm->ldata; + break; + case BM_FACE: + cdata = &idmap->bm->pdata; + break; + default: + BLI_assert_unreachable(); + return false; + } - int idx = CustomData_get_named_layer_index(cdata, CD_PROP_INT32, name); + int idx = CustomData_get_named_layer_index(cdata, CD_PROP_INT32, name); + bool exists = idx != -1; - if (idx == -1) { - BM_data_layer_add_named(idmap->bm, cdata, CD_PROP_INT32, name); - idx = CustomData_get_named_layer_index(cdata, CD_PROP_INT32, name); - } + if (!exists) { + BM_data_layer_add_named(idmap->bm, cdata, CD_PROP_INT32, name); + idx = CustomData_get_named_layer_index(cdata, CD_PROP_INT32, name); + } - cdata->layers[idx].flag |= CD_FLAG_ELEM_NOINTERP | CD_FLAG_ELEM_NOCOPY; + cdata->layers[idx].flag |= CD_FLAG_ELEM_NOINTERP | CD_FLAG_ELEM_NOCOPY; + idmap->cd_id_off[type] = cdata->layers[idx].offset; - idmap->cd_id_off[type] = cdata->layers[idx].offset; - }; + return !exists; +} - check_attr(BM_VERT); - check_attr(BM_EDGE); - check_attr(BM_LOOP); - check_attr(BM_FACE); +bool BM_idmap_check_attributes(BMIdMap *idmap) +{ + bool ret = false; + + ret |= bm_idmap_check_attr(idmap, BM_VERT); + ret |= bm_idmap_check_attr(idmap, BM_EDGE); + ret |= bm_idmap_check_attr(idmap, BM_LOOP); + ret |= bm_idmap_check_attr(idmap, BM_FACE); + + return ret; } void BM_idmap_destroy(BMIdMap *idmap) diff --git a/source/blender/bmesh/intern/bmesh_idmap.h b/source/blender/bmesh/intern/bmesh_idmap.h index d3d90bf7887..ecdc083aec7 100644 --- a/source/blender/bmesh/intern/bmesh_idmap.h +++ b/source/blender/bmesh/intern/bmesh_idmap.h @@ -49,7 +49,7 @@ extern "C" { #endif BMIdMap *BM_idmap_new(BMesh *bm, int elem_mask); -void BM_idmap_check_attributes(BMIdMap *idmap); +bool BM_idmap_check_attributes(BMIdMap *idmap); void BM_idmap_check_ids(BMIdMap *idmap); void BM_idmap_destroy(BMIdMap *idmap); @@ -57,6 +57,9 @@ int BM_idmap_alloc(BMIdMap *idmap, BMElem *elem); void BM_idmap_assign(BMIdMap *idmap, BMElem *elem, int id); void BM_idmap_release(BMIdMap *idmap, BMElem *elem, bool clear_id); int BM_idmap_check_assign(BMIdMap *idmap, BMElem *elem); +void BM_idmap_clear_attributes(BMesh *bm); +const char *BM_idmap_attr_name_get(int htype); +void BM_idmap_clear_attributes_mesh(Mesh *me); BLI_INLINE int BM_idmap_get_id(BMIdMap *map, BMElem *elem) { diff --git a/source/blender/bmesh/intern/bmesh_log.cc b/source/blender/bmesh/intern/bmesh_log.cc index 5287b699190..922ac0f0d5d 100644 --- a/source/blender/bmesh/intern/bmesh_log.cc +++ b/source/blender/bmesh/intern/bmesh_log.cc @@ -595,6 +595,10 @@ struct BMLogEntry { template T *get_elem_from_id(BMesh * /*bm*/, BMID id) { + if (id.id < 0 || id.id >= idmap->map_size) { + return nullptr; + } + T *elem = reinterpret_cast(BM_idmap_lookup(idmap, id.id)); char htype = 0; @@ -628,9 +632,8 @@ struct BMLogEntry { { int id = _id.id; - if (check_unique) { - BMElem *old; - old = BM_idmap_lookup(idmap, id); + if (check_unique && id >= 0 && id < idmap->map_size) { + BMElem *old = BM_idmap_lookup(idmap, id); if (old && old != (BMElem *)elem) { printf( @@ -1151,12 +1154,23 @@ struct BMLog { void undo(BMesh *bm, BMLogCallbacks *callbacks) { + if (!current_entry) { + current_entry = first_entry; + while (current_entry->next) { + current_entry = current_entry->next; + } + } + current_entry->undo(bm, callbacks); current_entry = current_entry->prev; } void redo(BMesh *bm, BMLogCallbacks *callbacks) { + if (!current_entry) { + current_entry = first_entry; + } + current_entry = current_entry->next; if (current_entry) { current_entry->redo(bm, callbacks); diff --git a/source/blender/editors/object/object_remesh.cc b/source/blender/editors/object/object_remesh.cc index f56790b5af3..f6a6539a4a5 100644 --- a/source/blender/editors/object/object_remesh.cc +++ b/source/blender/editors/object/object_remesh.cc @@ -75,6 +75,8 @@ #include "object_intern.h" /* own include */ +#include "bmesh_idmap.h" + using blender::float3; using blender::IndexRange; using blender::Span; @@ -104,11 +106,6 @@ static bool object_remesh_poll(bContext *C) return false; } - if (ob->mode == OB_MODE_SCULPT && ob->sculpt->bm) { - CTX_wm_operator_poll_msg_set(C, "The remesher cannot run with dyntopo activated"); - return false; - } - if (BKE_modifiers_uses_multires(ob)) { CTX_wm_operator_poll_msg_set( C, "The remesher cannot run with a Multires modifier in the modifier stack"); @@ -145,6 +142,21 @@ static int voxel_remesh_exec(bContext *C, wmOperator *op) isovalue = mesh->remesh_voxel_size * 0.3f; } + bool is_dyntopo = false; + + if (ob->mode == OB_MODE_SCULPT) { + BKE_sculpt_update_object_for_edit(CTX_data_depsgraph_pointer(C), ob, false, false, false); + ED_sculpt_undo_geometry_begin(ob, op); + + if (ob->sculpt->bm) { + is_dyntopo = true; + BKE_sculptsession_bm_to_me(ob, false); + + /* Destroy dyntopo IDs so we don't waste time reprojecting them. */ + BM_idmap_clear_attributes_mesh(mesh); + } + } + Mesh *new_mesh = BKE_mesh_remesh_voxel( mesh, mesh->remesh_voxel_size, mesh->remesh_voxel_adaptivity, isovalue); @@ -153,10 +165,6 @@ static int voxel_remesh_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - if (ob->mode == OB_MODE_SCULPT) { - ED_sculpt_undo_geometry_begin(ob, op); - } - if (mesh->flag & ME_REMESH_FIX_POLES && mesh->remesh_voxel_adaptivity <= 0.0f) { Mesh *mesh_fixed_poles = BKE_mesh_remesh_voxel_fix_poles(new_mesh); BKE_id_free(nullptr, new_mesh); @@ -182,8 +190,24 @@ static int voxel_remesh_exec(bContext *C, wmOperator *op) BKE_mesh_nomain_to_mesh(new_mesh, mesh, ob); BKE_mesh_smooth_flag_set(static_cast(ob->data), smooth_normals); - + ; if (ob->mode == OB_MODE_SCULPT) { + /* Convert mesh back to bmesh. */ + if (is_dyntopo) { + BMesh *bm = BKE_sculptsession_empty_bmesh_create(); + BMeshFromMeshParams params = {0}; + params.calc_face_normal = true; + params.use_shapekey = true; + params.active_shapekey = ob->shapenr; + params.copy_temp_cdlayers = false; + + BM_mesh_bm_from_me(bm, mesh, ¶ms); + BM_mesh_elem_table_ensure(bm, BM_VERT | BM_EDGE | BM_FACE); + + BKE_sculpt_set_bmesh(ob, bm, true); + BKE_sculpt_ensure_idmap(ob); + } + ED_sculpt_undo_geometry_end(ob); } diff --git a/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc b/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc index aa57899bfe2..9a941efd017 100644 --- a/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc +++ b/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc @@ -275,7 +275,7 @@ static void SCULPT_dynamic_topology_disable_ex( Main *bmain, Depsgraph *depsgraph, Scene *scene, Object *ob, SculptUndoNode * /*unode*/) { SculptSession *ss = ob->sculpt; - Mesh *me = static_cast(ob->data); + Mesh *me = BKE_object_get_original_mesh(ob); /* Destroy temporary layers. */ BKE_sculpt_attribute_destroy_temporary_all(ob); diff --git a/source/blender/editors/sculpt_paint/sculpt_undo.cc b/source/blender/editors/sculpt_paint/sculpt_undo.cc index 536a3789399..6359cd0c652 100644 --- a/source/blender/editors/sculpt_paint/sculpt_undo.cc +++ b/source/blender/editors/sculpt_paint/sculpt_undo.cc @@ -1037,6 +1037,21 @@ static void sculpt_undo_bmesh_restore_generic(SculptUndoNode *unode, Object *ob, } } +static void sculpt_unode_bmlog_ensure(SculptSession *ss, SculptUndoNode *unode) +{ + if (!ss->bm_log && ss->bm && unode->bm_entry) { + ss->bm_log = BM_log_from_existing_entries_create(ss->bm, ss->bm_idmap, unode->bm_entry); + + if (!unode->applied) { + BM_log_undo_skip(ss->bm, ss->bm_log); + } + + if (ss->pbvh) { + BKE_pbvh_set_bm_log(ss->pbvh, ss->bm_log); + } + } +} + /* Create empty sculpt BMesh and enable logging. */ static void sculpt_undo_bmesh_enable(Object *ob, SculptUndoNode *unode, bool is_redo) { @@ -1045,6 +1060,11 @@ static void sculpt_undo_bmesh_enable(Object *ob, SculptUndoNode *unode, bool is_ SCULPT_pbvh_clear(ob); + if (ss->bm_idmap) { + BM_idmap_destroy(ss->bm_idmap); + ss->bm_idmap = nullptr; + } + ss->active_face.i = ss->active_vertex.i = PBVH_REF_NONE; /* Create empty BMesh and enable logging. */ @@ -1068,35 +1088,26 @@ static void sculpt_undo_bmesh_enable(Object *ob, SculptUndoNode *unode, bool is_ /* Calculate normals. */ BM_mesh_normals_update(ss->bm); + /* Ensure mask. */ + if (!CustomData_has_layer(&ss->bm->vdata, CD_PAINT_MASK)) { + BM_data_layer_add(ss->bm, &ss->bm->vdata, CD_PAINT_MASK); + } + BKE_sculptsession_update_attr_refs(ob); if (ss->pbvh) { blender::bke::paint::load_all_original(ob); } - if (ss->bm_idmap) { - BM_idmap_destroy(ss->bm_idmap); - ss->bm_idmap = nullptr; - } - BKE_sculpt_ensure_idmap(ob); if (!ss->bm_log) { - /* Restore the BMLog using saved entries. */ - ss->bm_log = BM_log_from_existing_entries_create(ss->bm, ss->bm_idmap, unode->bm_entry); - BMLogEntry *entry = is_redo ? BM_log_entry_prev(unode->bm_entry) : unode->bm_entry; - - BM_log_set_current_entry(ss->bm_log, entry); + sculpt_unode_bmlog_ensure(ss, unode); } else { BM_log_set_idmap(ss->bm_log, ss->bm_idmap); } - if (!CustomData_has_layer(&ss->bm->vdata, CD_PAINT_MASK)) { - BM_data_layer_add(ss->bm, &ss->bm->vdata, CD_PAINT_MASK); - BKE_sculptsession_update_attr_refs(ob); - } - SCULPT_update_all_valence_boundary(ob); me->flag |= ME_SCULPT_DYNAMIC_TOPOLOGY; @@ -1107,23 +1118,19 @@ static void sculpt_undo_bmesh_restore_begin( { if (unode->applied) { if (ss->bm && ss->bm_log) { - /*note that we can't log ids here. - not entirely sure why, and in thoery it shouldn't be necassary. - ids end up corrupted. - */ - -#if 1 - // BM_log_all_ids(ss->bm, ss->bm_log, unode->bm_entry); - - // need to run bmlog undo on empty log, - // getting a refcount error in the log - // ref counting system otherwise - - if (dir == -1) { - BM_log_undo_skip(ss->bm, ss->bm_log); +#if 0 + if (dir == 1) { + BM_log_redo_skip(ss->bm, ss->bm_log); + } + else { + BM_log_undo_skip(ss->bm, ss->bm_log); + } +#else + if (dir == 1) { + BM_log_redo(ss->bm, ss->bm_log, nullptr); } else { - BM_log_redo_skip(ss->bm, ss->bm_log); + BM_log_undo(ss->bm, ss->bm_log, nullptr); } #endif } @@ -1133,21 +1140,15 @@ static void sculpt_undo_bmesh_restore_begin( unode->applied = false; } else { - /*load bmesh from mesh data*/ + /* Load bmesh from mesh data. */ sculpt_undo_bmesh_enable(ob, unode, true); -#if 1 - // need to run bmlog undo on empty log, - // getting a refcount error in the log - // ref counting system otherwise - if (dir == 1) { BM_log_redo(ss->bm, ss->bm_log, nullptr); } else { BM_log_undo(ss->bm, ss->bm_log, nullptr); } -#endif unode->applied = true; } @@ -1165,11 +1166,6 @@ static void sculpt_undo_bmesh_restore_end( /*load bmesh from mesh data*/ sculpt_undo_bmesh_enable(ob, unode, false); -#if 1 - // need to run bmlog undo on empty log, - // getting a refcount error in the log - // ref counting system otherwise - if (dir == -1) { BM_log_undo(ss->bm, ss->bm_log, nullptr); } @@ -1190,16 +1186,11 @@ static void sculpt_undo_bmesh_restore_end( BM_ITER_MESH (f, &iter, ss->bm, BM_FACES_OF_MESH) { BM_ELEM_CD_SET_INT(f, ss->cd_face_node_offset, DYNTOPO_NODE_NONE); } -#endif unode->applied = false; } else { -#if 1 if (ss->bm && ss->bm_log) { - // need to run bmlog undo on empty log, - // getting a refcount error in the log - // ref counting system otherwise if (dir == -1) { BM_log_undo_skip(ss->bm, ss->bm_log); @@ -1208,7 +1199,6 @@ static void sculpt_undo_bmesh_restore_end( BM_log_redo_skip(ss->bm, ss->bm_log); } } -#endif /* Disable dynamic topology sculpting. */ SCULPT_dynamic_topology_disable(C, nullptr); @@ -1309,9 +1299,7 @@ static int sculpt_undo_bmesh_restore( { // handle transition from another undo type - if (!ss->bm_log && ss->bm && unode->bm_entry) { // && BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) { - ss->bm_log = BM_log_from_existing_entries_create(ss->bm, ss->bm_idmap, unode->bm_entry); - } + sculpt_unode_bmlog_ensure(ss, unode); bool ret = false; @@ -1432,8 +1420,11 @@ static void sculpt_undo_restore_list(bContext *C, Depsgraph *depsgraph, ListBase * Undo steps like geometry does not need object to be updated before they run and will * ensure object is updated after the node is handled. */ const SculptUndoNode *first_unode = (const SculptUndoNode *)lb->first; - if (first_unode->type != SCULPT_UNDO_GEOMETRY && - first_unode->type != SCULPT_UNDO_DYNTOPO_BEGIN) { + if (!ELEM(first_unode->type, + SCULPT_UNDO_GEOMETRY, + SCULPT_UNDO_DYNTOPO_BEGIN, + SCULPT_UNDO_DYNTOPO_SYMMETRIZE)) + { BKE_sculpt_update_object_for_edit(depsgraph, ob, false, need_mask, false); } @@ -1688,9 +1679,6 @@ static void sculpt_undo_free_list(ListBase *lb) unode->bm_log = nullptr; } - sculpt_undo_geometry_free_data(&unode->geometry_original); - sculpt_undo_geometry_free_data(&unode->geometry_modified); - if (unode->face_sets) { MEM_freeN(unode->face_sets); } @@ -2147,18 +2135,7 @@ void SCULPT_undo_ensure_bmlog(Object *ob) /*when transition between undo step types the log might simply have been freed, look for entries to rebuild it with*/ - if (!ss->bm_log) { - if (unode && unode->bm_entry) { - ss->bm_log = BM_log_from_existing_entries_create(ss->bm, ss->bm_idmap, unode->bm_entry); - } - else { - ss->bm_log = BM_log_create(ss->bm, ss->bm_idmap); - } - - if (ss->pbvh) { - BKE_pbvh_set_bm_log(ss->pbvh, ss->bm_log); - } - } + sculpt_unode_bmlog_ensure(ss, unode); } static SculptUndoNode *sculpt_undo_bmesh_push(Object *ob, PBVHNode *node, SculptUndoType type) -- 2.30.2 From 021f9cc17c264f389c787eb05fdd517a157d1e2a Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Sat, 7 Oct 2023 12:38:22 -0700 Subject: [PATCH 262/279] temp-sculpt-dyntopo: Tweak snake hook dyntopo defaults --- source/blender/blenkernel/intern/brush.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/blender/blenkernel/intern/brush.cc b/source/blender/blenkernel/intern/brush.cc index 1e8aaf9c276..067737a0188 100644 --- a/source/blender/blenkernel/intern/brush.cc +++ b/source/blender/blenkernel/intern/brush.cc @@ -1867,8 +1867,8 @@ void BKE_brush_sculpt_reset(Brush *br) br->rake_factor = 1.0f; br->dyntopo.flag |= DYNTOPO_SUBDIVIDE | DYNTOPO_COLLAPSE; br->dyntopo.flag &= ~DYNTOPO_CLEANUP; - br->dyntopo.spacing = 10; - br->dyntopo.quality = 0.3f; + br->dyntopo.spacing = 20; + br->dyntopo.quality = 0.35f; br->dyntopo.radius_scale = 1.0; br->dyntopo.repeat = 0; break; -- 2.30.2 From ebf283c601fcd73453a29b190b2afb9a19e13a19 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Sat, 7 Oct 2023 14:11:56 -0700 Subject: [PATCH 263/279] temp-sculpt-dyntopo: Fix broken subdivide-only dyntopo mode --- source/blender/blenkernel/intern/dyntopo.cc | 309 ++++++++---------- .../blenkernel/intern/dyntopo_intern.hh | 90 ++++- 2 files changed, 225 insertions(+), 174 deletions(-) diff --git a/source/blender/blenkernel/intern/dyntopo.cc b/source/blender/blenkernel/intern/dyntopo.cc index 0c74bd00bb8..b02eaf4aa46 100644 --- a/source/blender/blenkernel/intern/dyntopo.cc +++ b/source/blender/blenkernel/intern/dyntopo.cc @@ -72,7 +72,7 @@ using namespace blender::bke::sculpt; * if an element ID attribute exists (cd_id_offset is not -1) * it will unswap IDs. */ -static void bmesh_swap_data_simple(CustomData *data, void **block1, void **block2, int cd_id) +static void bmesh_swap_data_simple(CustomData * /*data*/, void **block1, void **block2, int cd_id) { std::swap(*block1, *block2); @@ -294,11 +294,6 @@ static float maskcb_get(EdgeQueueContext *eq_ctx, BMVert *v1, BMVert *v2) return 1.0f; } -enum WeightMode { - SPLIT = -1, - COLLAPSE = 1, -}; - #if 0 static float get_cross_edge_len(BMEdge *e) { @@ -340,23 +335,22 @@ BLI_INLINE float calc_weighted_length(EdgeQueueContext *eq_ctx, len + eq_ctx->limit_len_min_sqr * 1.0f * powf(w, 5.0) : len; } + case NONE: /* Avoid compiler warnings. */ + break; } BLI_assert_unreachable(); return 0.0f; } -static PBVHTopologyUpdateMode edge_queue_test(EdgeQueueContext *eq_ctx, - PBVH * /*pbvh*/, - BMEdge *e, - float *r_w) +static WeightMode edge_queue_test(EdgeQueueContext *eq_ctx, PBVH * /*pbvh*/, BMEdge *e, float *r_w) { float len1 = calc_weighted_length(eq_ctx, e->v1, e->v2, SPLIT); if ((eq_ctx->mode & PBVH_Subdivide) && len1 > eq_ctx->limit_len_max_sqr) { if (r_w) { *r_w = len1; } - return PBVH_Subdivide; + return SPLIT; } float len2 = calc_weighted_length(eq_ctx, e->v1, e->v2, COLLAPSE); @@ -364,10 +358,10 @@ static PBVHTopologyUpdateMode edge_queue_test(EdgeQueueContext *eq_ctx, if (r_w) { *r_w = len2; } - return PBVH_Collapse; + return COLLAPSE; } - return PBVH_None; + return NONE; } void EdgeQueueContext::surface_smooth(BMVert *v, float fac) @@ -375,10 +369,19 @@ void EdgeQueueContext::surface_smooth(BMVert *v, float fac) surface_smooth_v_safe(ss, pbvh, v, fac, distort_correction_mode); } -void EdgeQueueContext::insert_edge(BMEdge *e, float w) +void EdgeQueueContext::insert_edge(BMEdge *e, float w, WeightMode mode) { if (!(e->head.hflag & EDGE_QUEUE_FLAG)) { +#ifdef DYNTOPO_USE_SEP_HEAPS + if (mode == COLLAPSE) { + min_heap.insert(e, w); + } + else { + max_heap.insert(e, w); + } +#else edge_heap.insert(w, e); +#endif e->head.hflag |= EDGE_QUEUE_FLAG; if (ignore_loop_data) { @@ -559,7 +562,7 @@ static void add_split_edge_recursive( } // if (!skinny_bad_edge(l_edge->e)) { - eq_ctx->insert_edge(l_edge->e, len_sq); + eq_ctx->insert_edge(l_edge->e, len_sq, SPLIT); //} if ((l_edge->radial_next != l_edge)) { @@ -699,8 +702,6 @@ static void unified_edge_queue_task_cb(void *__restrict userdata, PBVHNode *node = tdata->node; EdgeQueueContext *eq_ctx = tdata->eq_ctx; - bool do_smooth = eq_ctx->surface_relax && eq_ctx->surface_smooth_fac > 0.0f; - BKE_pbvh_bmesh_check_tris(pbvh, node); int ni = int(node - &pbvh->nodes[0]); @@ -790,14 +791,14 @@ static void unified_edge_queue_task_cb(void *__restrict userdata, } float w = 0.0f; - PBVHTopologyUpdateMode mode = edge_queue_test(eq_ctx, pbvh, l_iter->e, &w); + WeightMode mode = edge_queue_test(eq_ctx, pbvh, l_iter->e, &w); /* Subdivide walks the mesh a bit for better transitions in the topology. */ - if (mode == PBVH_Subdivide) { + if (mode == SPLIT) { add_split_edge_recursive_threaded( tdata, l_iter->radial_next, l_iter, w, eq_ctx->limit_len_max, 0, true); } - else if (mode == PBVH_Collapse) { + else if (mode == COLLAPSE) { edge_thread_data_insert(tdata, l_iter->e); } } while ((l_iter = l_iter->next) != l_first); @@ -1380,8 +1381,13 @@ static void unified_edge_queue_create(EdgeQueueContext *eq_ctx, e->v2->head.hflag |= EDGE_QUEUE_FLAG; float w; - if (edge_queue_test(eq_ctx, pbvh, e, &w)) { - eq_ctx->insert_edge(e, w); + if (WeightMode wmode = edge_queue_test(eq_ctx, pbvh, e, &w)) { + bool insert = wmode == SPLIT && (eq_ctx->mode & PBVH_Subdivide); + insert |= wmode == COLLAPSE && (eq_ctx->mode & PBVH_Collapse); + + if (insert) { + eq_ctx->insert_edge(e, w, wmode); + } } } } @@ -1617,25 +1623,32 @@ static void edge_queue_create_local(EdgeQueueContext *eq_ctx, float len2 = calc_weighted_length(eq_ctx, e->v1, e->v2, SPLIT); float w = 0.0f; + WeightMode mode; + if (a && b) { + mode = edge_queue_test(eq_ctx, eq_ctx->pbvh, e, &w); +#if 0 ok = len1 < eq_ctx->limit_len_min_sqr || len1 > eq_ctx->limit_len_max_sqr; ok = ok || (len2 < pbvh->bm_min_edge_len || len2 > pbvh->bm_max_edge_len); w = (len1 + len2) * 0.5; +#endif } else if (a) { ok = len1 > eq_ctx->limit_len_max || len1 > pbvh->bm_max_edge_len; w = len1; + mode = SPLIT; } else if (b) { ok = len2 < eq_ctx->limit_len_min || len2 < pbvh->bm_min_edge_len; w = len2; + mode = COLLAPSE; } if (!ok) { continue; } - eq_ctx->insert_edge(e, w); + eq_ctx->insert_edge(e, w, mode); } } @@ -2097,13 +2110,25 @@ void EdgeQueueContext::start() } } -bool EdgeQueueContext::done() +ATTR_NO_OPT bool EdgeQueueContext::done() { +#ifndef DYNTOPO_USE_SEP_HEAPS if (edge_heap.empty() || (edge_heap.min_weight() > limit_len_min_sqr && edge_heap.max_weight() < limit_len_max_sqr)) { return true; } +#else + if (min_heap.empty() && max_heap.empty()) { + return true; + } + + if (!min_heap.empty() && !max_heap.empty() && min_heap.top_weight() > limit_len_min_sqr && + max_heap.top_weight() < limit_len_max_sqr) + { + return true; + } +#endif return (mode & (PBVH_Collapse | PBVH_Subdivide | PBVH_LocalCollapse)) == 0; } @@ -2115,11 +2140,20 @@ bool EdgeQueueContext::cleanup_valence_34() void EdgeQueueContext::finish() { +#ifdef DYNTOPO_USE_SEP_HEAPS + while (!min_heap.empty()) { + min_heap.pop()->head.hflag &= ~EDGE_QUEUE_FLAG; + } + while (!max_heap.empty()) { + max_heap.pop()->head.hflag &= ~EDGE_QUEUE_FLAG; + } +#else for (BMEdge *e : edge_heap.values()) { if (!BM_elem_is_free(reinterpret_cast(e), BM_EDGE)) { e->head.hflag &= ~EDGE_QUEUE_FLAG; } } +#endif if (mode & PBVH_Cleanup) { modified |= do_cleanup_3_4(this, pbvh); @@ -2182,14 +2216,47 @@ void EdgeQueueContext::finish() BM_log_entry_add_ex(pbvh->header.bm, pbvh->bm_log, true); } -BMEdge *EdgeQueueContext::pop_invalid_edges(BMEdge *in_e, float &w, bool is_max) +template +BMEdge *EdgeQueueContext::pop_invalid_edges(EdgeHeapT &heap, BMEdge *in_e, float &w, bool is_max) { + if (!in_e) { + return nullptr; + } + +#ifdef DYNTOPO_USE_SEP_HEAPS + BMEdge *e = in_e; + WeightMode weightmode = is_max ? SPLIT : COLLAPSE; + + while (!heap.empty() && e && + (bm_elem_is_free((BMElem *)e, BM_EDGE) || + fabs(calc_weighted_length(this, e->v1, e->v2, weightmode) - w) > w * 0.1f)) + { + heap.pop(); + + if (heap.empty()) { + return nullptr; + } + + /* The edge was freed. */ + if (bm_elem_is_free((BMElem *)e, BM_EDGE)) { + e = heap.pop(&w); + continue; + } + + /* The weight was wrong. */ + e->head.hflag |= EDGE_QUEUE_FLAG; + heap.insert(e, calc_weighted_length(this, e->v1, e->v2, weightmode)); + + e = heap.top(&w); + } + +#else BMEdge *e = in_e; WeightMode weightmode = is_max ? SPLIT : COLLAPSE; while (!edge_heap.empty() && e && (bm_elem_is_free((BMElem *)e, BM_EDGE) || - fabs(calc_weighted_length(this, e->v1, e->v2, weightmode) - w) > w * 0.1)) + fabs(calc_weighted_length(this, e->v1, e->v2, weightmode) - w) > w * 0.1f)) { if (is_max) { edge_heap.pop_max(); @@ -2199,7 +2266,7 @@ BMEdge *EdgeQueueContext::pop_invalid_edges(BMEdge *in_e, float &w, bool is_max) } if (edge_heap.empty()) { - return static_cast(nullptr); + return nullptr; } /* The edge was freed. */ @@ -2209,7 +2276,7 @@ BMEdge *EdgeQueueContext::pop_invalid_edges(BMEdge *in_e, float &w, bool is_max) } /* The weight was wrong. */ - e->head.hflag &= ~EDGE_QUEUE_FLAG; + e->head.hflag |= EDGE_QUEUE_FLAG; edge_heap.insert(calc_weighted_length(this, e->v1, e->v2, weightmode), e); if (is_max) { @@ -2220,6 +2287,8 @@ BMEdge *EdgeQueueContext::pop_invalid_edges(BMEdge *in_e, float &w, bool is_max) } } +#endif + return BM_elem_is_free(reinterpret_cast(e), BM_EDGE) ? nullptr : e; } @@ -2239,22 +2308,37 @@ void EdgeQueueContext::stochastic_smooth(BMVert *v) } } -/* Collapse and subdivide. */ -void EdgeQueueContext::step_multi() +/* Collapse and subdivide. + * + * Note: we enforce subdivide-only or collapse-only + * in unified_edge_queue_create, for the solver to + * converge properly edge splitting needs to be + * able to collapse degenerate edges. + */ +void EdgeQueueContext::step_intern() { float min_w, max_w; + +#ifdef DYNTOPO_USE_SEP_HEAPS + BMEdge *min_e = !min_heap.empty() ? min_heap.top(&min_w) : nullptr; + BMEdge *max_e = !max_heap.empty() ? max_heap.top(&max_w) : nullptr; + + min_e = pop_invalid_edges(min_heap, min_e, min_w, false); + max_e = pop_invalid_edges(max_heap, max_e, max_w, false); +#else BMEdge *min_e = edge_heap.peek_min(&min_w); - min_e = pop_invalid_edges(min_e, min_w, false); + min_e = pop_invalid_edges(edge_heap, min_e, min_w, false); if (edge_heap.empty()) { return; } BMEdge *max_e = edge_heap.peek_max(&max_w); - max_e = pop_invalid_edges(max_e, max_w, true); + max_e = pop_invalid_edges(edge_heap, max_e, max_w, true); if (edge_heap.empty()) { return; } +#endif if (min_w > limit_len_min_sqr) { min_e = nullptr; @@ -2311,51 +2395,21 @@ void EdgeQueueContext::step_multi() stochastic_smooth(e->v1); stochastic_smooth(e->v2); - switch (op) { - case PBVH_Collapse: - edge_heap.pop_min(); - collapse_edge(pbvh, e, e->v1, e->v2); - break; - case PBVH_Subdivide: - edge_heap.pop_max(); - split_edge(e); - break; + if (op == PBVH_Collapse) { +#ifdef DYNTOPO_USE_SEP_HEAPS + min_heap.pop(); +#else + edge_heap.pop_min(); +#endif + collapse_edge(pbvh, e, e->v1, e->v2); } - - count++; -} - -/* Collapse or subdivide. */ -void EdgeQueueContext::step_single() -{ - BMEdge *e = nullptr; - float w; - - switch (mode & (PBVH_Subdivide | PBVH_Collapse)) { - case PBVH_Collapse: - e = edge_heap.pop_min(&w); - e = pop_invalid_edges(e, w, false); - - if (e) { - e->head.hflag &= ~EDGE_QUEUE_FLAG; - modified = true; - stochastic_smooth(e->v1); - stochastic_smooth(e->v2); - collapse_edge(pbvh, e, e->v1, e->v2); - } - break; - case PBVH_Subdivide: - e = edge_heap.pop_max(&w); - e = pop_invalid_edges(e, w, true); - - if (e) { - e->head.hflag &= ~EDGE_QUEUE_FLAG; - modified = true; - stochastic_smooth(e->v1); - stochastic_smooth(e->v2); - split_edge(e); - } - break; + else if (op == PBVH_Subdivide) { +#ifdef DYNTOPO_USE_SEP_HEAPS + max_heap.pop(); +#else + edge_heap.pop_max(); +#endif + split_edge(e); } count++; @@ -2372,92 +2426,7 @@ void EdgeQueueContext::step() flushed_ = true; } - if ((mode & (PBVH_Subdivide | PBVH_Collapse)) == (PBVH_Subdivide | PBVH_Collapse)) { - step_multi(); - } - else if (mode & (PBVH_Subdivide | PBVH_Collapse)) { - step_single(); - } - -#if 0 - switch (ops[curop]) { - case PBVH_None: - break; - case PBVH_Subdivide: { - if (edge_heap.max_weight() < limit_len_max_sqr) { - break; - } - - float w = 0.0f; - e = edge_heap.pop_max(&w); - - while (!edge_heap.empty() && e && - (bm_elem_is_free((BMElem *)e, BM_EDGE) || - fabs(calc_weighted_length(this, e->v1, e->v2, SPLIT) - w) > w * 0.1)) - { - if (e && !bm_elem_is_free((BMElem *)e, BM_EDGE)) { - e->head.hflag &= ~EDGE_QUEUE_FLAG; - edge_heap.insert(calc_weighted_length(this, e->v1, e->v2, SPLIT), e); - } - - e = edge_heap.pop_max(&w); - } - - if (!e || bm_elem_is_free((BMElem *)e, BM_EDGE) || w < limit_len_max_sqr) { - break; - } - - stochastic_smooth(e->v1); - stochastic_smooth(e->v2); - - e->head.hflag &= ~EDGE_QUEUE_FLAG; - split_edge(e); - - break; - } - case PBVH_Collapse: { - if (edge_heap.min_weight() > limit_len_min_sqr) { - break; - } - - e = edge_heap.pop_min(); - while (!edge_heap.empty() && e && - (bm_elem_is_free((BMElem *)e, BM_EDGE) || - calc_weighted_length(this, e->v1, e->v2, COLLAPSE) > limit_len_min_sqr)) - { - e = edge_heap.pop_min(); - } - - if (!e || bm_elem_is_free((BMElem *)e, BM_EDGE)) { - break; - } - - if (bm_elem_is_free((BMElem *)e->v1, BM_VERT) || bm_elem_is_free((BMElem *)e->v2, BM_VERT)) { - printf("%s: error! operated on freed bmesh elements! e: %p, e->v1: %p, e->v2: %p\n", - __func__, - e, - e->v1, - e->v2); - break; - } - - stochastic_smooth(e->v1); - stochastic_smooth(e->v2); - - modified = true; - collapse_edge(pbvh, e, e->v1, e->v2); - VALIDATE_LOG(pbvh->bm_log); - break; - } - case PBVH_LocalSubdivide: - case PBVH_LocalCollapse: - case PBVH_Cleanup: - BLI_assert_unreachable(); - break; - } - - count++; -#endif + step_intern(); } void EdgeQueueContext::report() @@ -2513,7 +2482,7 @@ bool remesh_topology(BrushTester *brush_tester, using Clock = std::chrono::high_resolution_clock; quality *= quality; - int time_limit = int(1.0f * (1.0f - quality) + 550.0f * quality); + int time_limit = int(8.0f * (1.0f - quality) + 550.0f * quality); auto time = Clock::now(); Clock::duration limit = std::chrono::duration_cast( @@ -2783,21 +2752,21 @@ void EdgeQueueContext::split_edge(BMEdge *e) BMVert *vs[3] = {newl->v, newl->next->v, newl->next->next->v}; if (brush_tester->tri_in_range(vs, newl->f->no)) { float w = 0.0f; - PBVHTopologyUpdateMode mode = edge_queue_test(this, pbvh, newl->e, &w); + WeightMode mode = edge_queue_test(this, pbvh, newl->e, &w); - if (mode == PBVH_Subdivide) { + if (mode == SPLIT) { add_split_edge_recursive(this, newl, w, limit_len_max, 0); } - else if (mode == PBVH_Collapse) { - insert_edge(newl->e, w); + else if (mode == COLLAPSE) { + insert_edge(newl->e, w, mode); } } else { float w = 0.0f; - PBVHTopologyUpdateMode mode = edge_queue_test(this, pbvh, newl->e, &w); + WeightMode mode = edge_queue_test(this, pbvh, newl->e, &w); - if (mode == PBVH_Collapse) { - insert_edge(newl->e, w); + if (mode == COLLAPSE) { + insert_edge(newl->e, w, mode); } } } diff --git a/source/blender/blenkernel/intern/dyntopo_intern.hh b/source/blender/blenkernel/intern/dyntopo_intern.hh index ed01134050d..907b9eff258 100644 --- a/source/blender/blenkernel/intern/dyntopo_intern.hh +++ b/source/blender/blenkernel/intern/dyntopo_intern.hh @@ -15,6 +15,8 @@ #include "bmesh.h" #include "pbvh_intern.hh" +#include + using blender::float3; struct GHash; @@ -50,6 +52,81 @@ static inline void dyntopo_add_flag(PBVH *pbvh, BMVert *v, uint8_t flag) namespace blender::bke::dyntopo { +enum WeightMode { + SPLIT = -1, + NONE = 0, + COLLAPSE = 1, +}; + +/* Slow. */ +//#define DYNTOPO_USE_SEP_HEAPS + +#ifdef DYNTOPO_USE_SEP_HEAPS +namespace detail { +struct EdgeWeight { + BMEdge *e; + float w; +}; + +struct max_comparator { + bool operator()(const EdgeWeight &a, const EdgeWeight &b) + { + return a.w < b.w; + } +}; + +struct min_comparator { + bool operator()(const EdgeWeight &a, const EdgeWeight &b) + { + return a.w > b.w; + } +}; +template struct EdgeHeap { + + void insert(BMEdge *e, float w) + { + queue.push({e, w}); + } + + BMEdge *top(float *r_w = nullptr) + { + const EdgeWeight &top = queue.top(); + + if (r_w) { + *r_w = top.w; + } + + return top.e; + } + + float top_weight() + { + return queue.top().w; + } + + BMEdge *pop(float *r_w = nullptr) + { + EdgeWeight top = queue.top(); + queue.pop(); + + if (r_w) { + *r_w = top.w; + } + + return top.e; + } + + bool empty() + { + return queue.empty(); + } + + private: + std::priority_queue, comparator> queue; +}; +} // namespace detail +#endif + static int elem_sizes[] = {-1, (int)sizeof(BMVert), (int)sizeof(BMEdge), @@ -234,7 +311,12 @@ struct EdgeQueueContext { bool local_mode; float surface_smooth_fac; +#ifdef DYNTOPO_USE_SEP_HEAPS + detail::EdgeHeap min_heap; + detail::EdgeHeap max_heap; +#else blender::MinMaxHeap edge_heap; +#endif blender::Vector used_verts; @@ -274,7 +356,7 @@ struct EdgeQueueContext { void *mask_cb_data); void insert_val34_vert(BMVert *v); - void insert_edge(BMEdge *e, float w); + void insert_edge(BMEdge *e, float w, WeightMode mode); void start(); bool done(); @@ -306,9 +388,9 @@ struct EdgeQueueContext { void stochastic_smooth(BMVert *v); private: - BMEdge *pop_invalid_edges(BMEdge *in_e, float &w, bool is_max); - void step_single(); - void step_multi(); + template + BMEdge *pop_invalid_edges(EdgeHeapT &heap, BMEdge *in_e, float &w, bool is_max); + void step_intern(); bool flushed_ = false; }; -- 2.30.2 From ed58137cb55d0730dbd3596bf973e418552cc91a Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Sat, 7 Oct 2023 14:44:49 -0700 Subject: [PATCH 264/279] temp-sculpt-dyntopo: Fix warning --- source/blender/blenkernel/intern/dyntopo.cc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/source/blender/blenkernel/intern/dyntopo.cc b/source/blender/blenkernel/intern/dyntopo.cc index b02eaf4aa46..d7821e3c1ce 100644 --- a/source/blender/blenkernel/intern/dyntopo.cc +++ b/source/blender/blenkernel/intern/dyntopo.cc @@ -2720,6 +2720,10 @@ void EdgeQueueContext::split_edge(BMEdge *e) BMLoop *newl; newf = BM_face_split(bm, f, l, l->next->next, &newl, nullptr, true); + if (newf) { + BM_ELEM_CD_SET_INT(newf, pbvh->cd_face_node_offset, DYNTOPO_NODE_NONE); + } + dyntopo_add_flag( pbvh, l->next->next->v, SCULPTFLAG_NEED_VALENCE | SCULPTFLAG_NEED_TRIANGULATE); pbvh_boundary_update_bmesh(pbvh, l->next->next->v); -- 2.30.2 From 5d831c25befb25cc3d06ef185f18b25e8c59200a Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Sun, 8 Oct 2023 14:18:15 -0700 Subject: [PATCH 265/279] temp-sculpt-dyntopo: Revert a bunch of unrelated BMesh changes --- .gitignore | 3 + .../blenkernel/intern/dyntopo_collapse.cc | 2 +- .../blenkernel/intern/dyntopo_intern.hh | 8 - .../blenkernel/intern/mesh_remesh_voxel.cc | 2 +- source/blender/bmesh/CMakeLists.txt | 1 - source/blender/bmesh/bmesh_varray.hh | 194 ------------------ source/blender/bmesh/intern/bmesh_collapse.cc | 8 +- source/blender/bmesh/intern/bmesh_collapse.hh | 69 +++++-- .../blender/bmesh/intern/bmesh_construct.cc | 3 +- source/blender/bmesh/intern/bmesh_core.cc | 104 +--------- source/blender/bmesh/intern/bmesh_core.h | 16 +- source/blender/bmesh/intern/bmesh_inline.h | 4 +- source/blender/bmesh/intern/bmesh_interp.cc | 12 +- source/blender/bmesh/intern/bmesh_interp.h | 5 + source/blender/bmesh/intern/bmesh_log.cc | 2 +- .../blender/bmesh/intern/bmesh_log_intern.h | 31 +-- source/blender/bmesh/intern/bmesh_mesh.h | 3 +- .../bmesh/intern/bmesh_mesh_convert.cc | 2 - .../blender/bmesh/intern/bmesh_mesh_convert.h | 2 - source/blender/bmesh/intern/bmesh_mods.cc | 20 +- source/blender/bmesh/intern/bmesh_mods.h | 9 +- source/blender/bmesh/intern/bmesh_private.h | 5 - source/blender/bmesh/operators/bmo_dupe.cc | 3 +- source/blender/bmesh/operators/bmo_extrude.cc | 3 +- .../blender/bmesh/operators/bmo_fill_grid.cc | 2 +- .../blender/bmesh/operators/bmo_primitive.cc | 4 +- .../bmesh/operators/bmo_removedoubles.cc | 17 -- .../blender/bmesh/operators/bmo_symmetrize.cc | 4 - .../bmesh/tools/bmesh_decimate_collapse.cc | 6 +- .../editors/mesh/editmesh_polybuild.cc | 4 +- 30 files changed, 107 insertions(+), 441 deletions(-) delete mode 100644 source/blender/bmesh/bmesh_varray.hh diff --git a/.gitignore b/.gitignore index 19da8b9205c..8cd5a131bf6 100644 --- a/.gitignore +++ b/.gitignore @@ -72,3 +72,6 @@ waveletNoiseTile.bin CMakeSettings.json CMakePresets.json CMakeUserPresets.json +/myprofile/AppData +/myprofile_clean/AppData/Roaming/Blender Foundation/Blender/3.3/config +/Editing User_JoeEagar_Reports_2022 - Blender Developer Wiki_files diff --git a/source/blender/blenkernel/intern/dyntopo_collapse.cc b/source/blender/blenkernel/intern/dyntopo_collapse.cc index f81419c2b05..e8c671b8465 100644 --- a/source/blender/blenkernel/intern/dyntopo_collapse.cc +++ b/source/blender/blenkernel/intern/dyntopo_collapse.cc @@ -613,7 +613,7 @@ BMVert *EdgeQueueContext::collapse_edge(PBVH *pbvh, BMEdge *e, BMVert *v1, BMVer } /* Full non-manifold collapse. */ - blender::bmesh::join_vert_kill_edge(pbvh->header.bm, e, v_del, true, true, callbacks); + blender::bmesh::join_vert_kill_edge(pbvh->header.bm, e, v_del, true, callbacks); if (BM_elem_is_free((BMElem *)v_conn, BM_VERT)) { printf("v_conn was freed\n"); diff --git a/source/blender/blenkernel/intern/dyntopo_intern.hh b/source/blender/blenkernel/intern/dyntopo_intern.hh index 907b9eff258..0777d8cfa45 100644 --- a/source/blender/blenkernel/intern/dyntopo_intern.hh +++ b/source/blender/blenkernel/intern/dyntopo_intern.hh @@ -32,14 +32,6 @@ struct SculptSession; // (DYNTOPO_DISABLE_FIN_REMOVAL | DYNTOPO_DISABLE_COLLAPSE | DYNTOPO_DISABLE_TRIANGULATOR) #define DYNTOPO_DISABLE_FLAG 0 -extern "C" { -void bmesh_disk_edge_append(BMEdge *e, BMVert *v); -void bmesh_radial_loop_append(BMEdge *e, BMLoop *l); -void bm_kill_only_edge(BMesh *bm, BMEdge *e); -void bm_kill_only_loop(BMesh *bm, BMLoop *l); -void bm_kill_only_face(BMesh *bm, BMFace *f); -} - static inline bool dyntopo_test_flag(PBVH *pbvh, BMVert *v, uint8_t flag) { return *BM_ELEM_CD_PTR(v, pbvh->cd_flag) & flag; diff --git a/source/blender/blenkernel/intern/mesh_remesh_voxel.cc b/source/blender/blenkernel/intern/mesh_remesh_voxel.cc index 8f91504f2de..e5cd17468f4 100644 --- a/source/blender/blenkernel/intern/mesh_remesh_voxel.cc +++ b/source/blender/blenkernel/intern/mesh_remesh_voxel.cc @@ -506,7 +506,7 @@ Mesh *BKE_mesh_remesh_voxel_fix_poles(const Mesh *mesh) if (BM_elem_flag_test(ed, BM_ELEM_TAG)) { float co[3]; mid_v3_v3v3(co, ed->v1->co, ed->v2->co); - BMVert *vc = BM_edge_collapse(bm, ed, ed->v1, true, true, true, true); + BMVert *vc = BM_edge_collapse(bm, ed, ed->v1, true, true); copy_v3_v3(vc->co, co); } } diff --git a/source/blender/bmesh/CMakeLists.txt b/source/blender/bmesh/CMakeLists.txt index e0b78b126da..3ee2e41daff 100644 --- a/source/blender/bmesh/CMakeLists.txt +++ b/source/blender/bmesh/CMakeLists.txt @@ -170,7 +170,6 @@ set(SRC # public includes bmesh.h bmesh_tools.h - bmesh_varray.hh ) set(LIB diff --git a/source/blender/bmesh/bmesh_varray.hh b/source/blender/bmesh/bmesh_varray.hh deleted file mode 100644 index d18828c7769..00000000000 --- a/source/blender/bmesh/bmesh_varray.hh +++ /dev/null @@ -1,194 +0,0 @@ -#include "BLI_generic_virtual_array.hh" -#include "BLI_math_vector_types.hh" -#include "BLI_span.hh" -#include "BLI_utildefines.h" -#include "BLI_virtual_array.hh" - -#include "bmesh.h" -#include -#include - -namespace blender::bmesh { -template static constexpr int get_htype_from_type() -{ - if constexpr (std::is_same_v) { - return BM_VERT; - } - if constexpr (std::is_same_v) { - return BM_EDGE; - } - if constexpr (std::is_same_v) { - return BM_LOOP; - } - if constexpr (std::is_same_v) { - return BM_FACE; - } - else { - return -1; - // static_assert(false, "BMType is not one of (BMVert, BMEdge, BMLoop, BMFace)."); - } -} - -template()> -class BMeshAttrArray : public blender::VArrayImpl { - // static const int htype = get_htype_from_type(); - - private: - BMesh *bm_; - int cd_offset_; - - int64_t get_size(BMesh *bm) - { - if constexpr (htype == BM_VERT) { - return bm->totvert; - } - else if constexpr (htype == BM_EDGE) { - return bm->totedge; - } - else if constexpr (htype == BM_LOOP) { - return bm->totloop; - } - else if constexpr (htype == BM_FACE) { - return bm->totface; - } - else { - // static_assert(false, "Invalid element type."); - BLI_assert_unreachable(); - return 0; - } - } - - public: - using base_type = T; - - // BMeshAttrArray(const int64_t size) = 0; - BMeshAttrArray(BMesh *bm, int cd_offset) - : VArrayImpl(get_size(bm)), bm_(bm), cd_offset_(cd_offset) - { - // - } - - BMeshAttrArray(const BMeshAttrArray &b) - : VArrayImpl(get_size(b.bm_)), bm_(b.bm_), cd_offset_(b.cd_offset_) - { - } - - virtual CommonVArrayInfo common_info() const override - { - CommonVArrayInfo info = {}; - - info.type = CommonVArrayInfo::Type::Any; - info.may_have_ownership = false; - - return info; - } - - virtual T get(int64_t index) const override - { - BMType **table = nullptr; - - if constexpr (htype == BM_VERT) { - table = bm_->vtable; - } - else if constexpr (htype == BM_EDGE) { - table = bm_->etable; - } - else if constexpr (htype == BM_LOOP) { - // static_assert(false, "Not supported for loops"); - } - else if constexpr (htype == BM_FACE) { - table = bm_->ftable; - } - - return *static_cast(POINTER_OFFSET(table[index]->head.data, cd_offset_)); - } -}; - -namespace detail { -template -static GVArray bmesh_attr_gvarray_intern(BMesh *bm, CustomDataLayer *layer) -{ - auto make_array = [&](auto *impl) { - using T = typename std::remove_reference_t::base_type; - - return VArray(static_cast *>(impl)); - // - }; - - switch (layer->type) { - case CD_PROP_FLOAT: - return make_array(new BMeshAttrArray(bm, layer->offset)); - case CD_PROP_FLOAT2: - return make_array(new BMeshAttrArray(bm, layer->offset)); - case CD_PROP_FLOAT3: - return make_array(new BMeshAttrArray(bm, layer->offset)); - case CD_PROP_COLOR: - return make_array(new BMeshAttrArray(bm, layer->offset)); - case CD_PROP_BYTE_COLOR: - return make_array(new BMeshAttrArray(bm, layer->offset)); - case CD_PROP_BOOL: - return make_array(new BMeshAttrArray(bm, layer->offset)); - case CD_PROP_INT8: - return make_array(new BMeshAttrArray(bm, layer->offset)); - case CD_PROP_INT32: - return make_array(new BMeshAttrArray(bm, layer->offset)); - case CD_ORIGINDEX: - return make_array(new BMeshAttrArray(bm, layer->offset)); - } - - return GVArray(VArray::ForSpan({nullptr, 0})); -} -}; // namespace detail - -static GVArray bmesh_attr_gvarray(BMesh *bm, int htype, const char *name) -{ - CustomData *cdata; - - switch (htype) { - case BM_VERT: - cdata = &bm->vdata; - break; - case BM_EDGE: - cdata = &bm->edata; - break; - case BM_LOOP: - printf("%s: loops are not supported\n", __func__); - return GVArray(VArray::ForSpan({nullptr, 0})); - case BM_FACE: - cdata = &bm->pdata; - break; - default: - printf("Invalid element type %d\n", htype); - BLI_assert_unreachable(); - return GVArray(VArray::ForSpan({nullptr, 0})); - } - - CustomDataLayer *layer = nullptr; - - for (int i = 0; i < cdata->totlayer; i++) { - if (STREQ(cdata->layers[i].name, name)) { - layer = &cdata->layers[i]; - break; - } - } - - if (!layer) { - printf("Unknown attribute %s\n", name); - return GVArray(VArray::ForSpan({nullptr, 0})); - } - - switch (htype) { - case BM_VERT: - return detail::bmesh_attr_gvarray_intern(bm, layer); - case BM_EDGE: - return detail::bmesh_attr_gvarray_intern(bm, layer); - /*case BM_LOOP: - break;*/ - case BM_FACE: - return detail::bmesh_attr_gvarray_intern(bm, layer); - } - - return GVArray(VArray::ForSpan({nullptr, 0})); -} - -} // namespace blender::bmesh diff --git a/source/blender/bmesh/intern/bmesh_collapse.cc b/source/blender/bmesh/intern/bmesh_collapse.cc index aa7f80584ed..8404cc2ffef 100644 --- a/source/blender/bmesh/intern/bmesh_collapse.cc +++ b/source/blender/bmesh/intern/bmesh_collapse.cc @@ -25,10 +25,12 @@ int bmesh_elem_check_all(void *elem, char htype) #endif } // namespace blender::bmesh -extern "C" BMVert *bmesh_kernel_join_vert_kill_edge( - BMesh *bm, BMEdge *e, BMVert *v_kill, const bool do_del, const bool combine_flags) +extern "C" BMVert *bmesh_kernel_join_vert_kill_edge(BMesh *bm, + BMEdge *e, + BMVert *v_kill, + const bool do_del) { NullCollapseCallbacks callbacks; return blender::bmesh::join_vert_kill_edge( - bm, e, v_kill, do_del, combine_flags, callbacks); + bm, e, v_kill, do_del, callbacks); } diff --git a/source/blender/bmesh/intern/bmesh_collapse.hh b/source/blender/bmesh/intern/bmesh_collapse.hh index 7418faf4704..2a4816d0cb6 100644 --- a/source/blender/bmesh/intern/bmesh_collapse.hh +++ b/source/blender/bmesh/intern/bmesh_collapse.hh @@ -1,3 +1,44 @@ +/* SPDX-FileCopyrightText: 2023 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup bmesh + * + * Edge collapse. + * + * This is an implementation of non-manifold edge + * collapse. It exists because profiling consistently showed + * CPU cache misses were a major problem for DynTopo. + * DynTopo's edge split and collapse functions used to work + * by deleting and re-creating geometry which made things + * much worse. + * + * Things were made more complicated by DynTopo's undo (BMLog), + * which works by saving and restoring geometry that has to be logged + * prior to operations. There are two ways to do this for collapse: + * either log the faces, edges and verts surrounding an edge prior + * to collapse or insert callbacks into the collapse function to + * do it as needed. + * + * I went back and forth between the two approaches several times. + * The problem with simply logging the surrounding faces is that + * in practice it has to be two rings of adjacent faces around the + * edge to catch various non-manifold edge cases. This ended up + * taking far too much RAM for undo. + * + * The alternative is to add callbacks into the collapse function. + * This saves a lot of memory but makes collapse more complicated. + * + * The approach this file takes to use templates to avoid the + * overhead of checking for callback pointers in other code. + * + * In hindsight this is kind of a silly concern and will be + * rewritten to use runtime callbacks. + * + * + */ + #pragma once #include @@ -36,6 +77,12 @@ using blender::Vector; #include "bmesh_private.h" extern "C" { #include "bmesh_structure.h" + +void bmesh_disk_edge_append(BMEdge *e, BMVert *v); +void bmesh_radial_loop_append(BMEdge *e, BMLoop *l); +void bm_kill_only_edge(BMesh *bm, BMEdge *e); +void bm_kill_only_loop(BMesh *bm, BMLoop *l); +void bm_kill_only_face(BMesh *bm, BMFace *f); } //#define JVKE_DEBUG @@ -511,12 +558,8 @@ static void bmesh_kernel_check_val3_vert(BMesh *bm, BMEdge *e, Callbacks &callba */ template -BMVert *join_vert_kill_edge(BMesh *bm, - BMEdge *e, - BMVert *v_del, - const bool do_del, - const bool combine_flags, - Callbacks &callbacks) +BMVert *join_vert_kill_edge( + BMesh *bm, BMEdge *e, BMVert *v_del, const bool do_del, Callbacks &callbacks) { BMVert *v_conn = BM_edge_other_vert(e, v_del); @@ -613,15 +656,15 @@ BMVert *join_vert_kill_edge(BMesh *bm, callbacks.on_edge_combine(exist, e); - if (combine_flags) { - /* The sharp flag is inverted so we can't just OR it. */ - bool sharp = !(exist->head.hflag & BM_ELEM_SMOOTH) || !(e->head.hflag & BM_ELEM_SMOOTH); + /* Combine edge flags. The sharp flag is inverted + * so we can't just OR it. + */ + bool sharp = !(exist->head.hflag & BM_ELEM_SMOOTH) || !(e->head.hflag & BM_ELEM_SMOOTH); - exist->head.hflag |= e->head.hflag; + exist->head.hflag |= e->head.hflag; - if (sharp) { - exist->head.hflag &= ~BM_ELEM_SMOOTH; - } + if (sharp) { + exist->head.hflag &= ~BM_ELEM_SMOOTH; } callbacks.on_edge_kill(e); diff --git a/source/blender/bmesh/intern/bmesh_construct.cc b/source/blender/bmesh/intern/bmesh_construct.cc index af12a027f1d..683d690083c 100644 --- a/source/blender/bmesh/intern/bmesh_construct.cc +++ b/source/blender/bmesh/intern/bmesh_construct.cc @@ -617,8 +617,7 @@ BMesh *BM_mesh_copy(BMesh *bm_old) params.use_toolflags = bm_old->use_toolflags; bm_new = BM_mesh_create(&allocsize, ¶ms); - BM_mesh_copy_init_customdata_all_layers( - bm_new, bm_old, BM_VERT | BM_EDGE | BM_LOOP | BM_FACE, &allocsize); + BM_mesh_copy_init_customdata(bm_new, bm_old, &allocsize); vtable = static_cast( MEM_mallocN(sizeof(BMVert *) * bm_old->totvert, "BM_mesh_copy vtable")); diff --git a/source/blender/bmesh/intern/bmesh_core.cc b/source/blender/bmesh/intern/bmesh_core.cc index 3d35fb3be0e..4e5ccf3aff4 100644 --- a/source/blender/bmesh/intern/bmesh_core.cc +++ b/source/blender/bmesh/intern/bmesh_core.cc @@ -1776,7 +1776,7 @@ BMEdge *bmesh_kernel_join_edge_kill_vert(BMesh *bm, if (check_edge_exists) { if (e_splice) { /* removes e_splice */ - BM_edge_splice(bm, e_old, e_splice, false); + BM_edge_splice(bm, e_old, e_splice); } } @@ -1805,90 +1805,6 @@ BMEdge *bmesh_kernel_join_edge_kill_vert(BMesh *bm, return nullptr; } -BMVert *bmesh_kernel_join_vert_kill_edge_fast(BMesh *bm, - BMEdge *e_kill, - BMVert *v_kill, - const bool do_del, - const bool check_edge_exists, - const bool kill_degenerate_faces, - const bool combine_flags) -{ - BLI_SMALLSTACK_DECLARE(faces_degenerate, BMFace *); - BMVert *v_target = BM_edge_other_vert(e_kill, v_kill); - - BLI_assert(BM_vert_in_edge(e_kill, v_kill)); - - if (e_kill->l) { - BMLoop *l_kill, *l_first, *l_kill_next; - l_kill = l_first = e_kill->l; - do { - /* relink loops and fix vertex pointer */ - if (l_kill->next->v == v_kill) { - l_kill->next->v = v_target; - } - - l_kill->next->prev = l_kill->prev; - l_kill->prev->next = l_kill->next; - if (BM_FACE_FIRST_LOOP(l_kill->f) == l_kill) { - BM_FACE_FIRST_LOOP(l_kill->f) = l_kill->next; - } - - /* fix len attribute of face */ - l_kill->f->len--; - if (kill_degenerate_faces) { - if (l_kill->f->len < 3) { - BLI_SMALLSTACK_PUSH(faces_degenerate, l_kill->f); - } - } - l_kill_next = l_kill->radial_next; - - bm_kill_only_loop(bm, l_kill); - - } while ((l_kill = l_kill_next) != l_first); - - e_kill->l = nullptr; - } - - BM_edge_kill(bm, e_kill); - BM_CHECK_ELEMENT(v_kill); - BM_CHECK_ELEMENT(v_target); - - if (v_target->e && v_kill->e) { - /* Inline `BM_vert_splice(bm, v_target, v_kill)`. */ - BMEdge *e; - while ((e = v_kill->e)) { - BMEdge *e_target; - - if (check_edge_exists) { - e_target = BM_edge_exists(v_target, BM_edge_other_vert(e, v_kill)); - } - - bmesh_edge_vert_swap(e, v_target, v_kill); - BLI_assert(e->v1 != e->v2); - - if (check_edge_exists) { - if (e_target) { - BM_edge_splice(bm, e_target, e, combine_flags); - } - } - } - } - - if (kill_degenerate_faces) { - BMFace *f_kill; - while ((f_kill = static_cast(BLI_SMALLSTACK_POP(faces_degenerate)))) { - BM_face_kill(bm, f_kill); - } - } - - if (do_del) { - BLI_assert(v_kill->e == nullptr); - bm_kill_only_vert(bm, v_kill); - } - - return v_target; -} - BMFace *bmesh_kernel_join_face_kill_edge(BMesh *bm, BMFace *f1, BMFace *f2, BMEdge *e) { BMLoop *l_iter, *l_f1 = nullptr, *l_f2 = nullptr; @@ -2230,8 +2146,7 @@ static void bmesh_kernel_vert_separate__cleanup(BMesh *bm, LinkNode *edges_separ do { BMEdge *e = static_cast(n_step->link); BLI_assert(e != e_orig); - if ((e->v1 == e_orig->v1) && (e->v2 == e_orig->v2) && BM_edge_splice(bm, e_orig, e, false)) - { + if ((e->v1 == e_orig->v1) && (e->v2 == e_orig->v2) && BM_edge_splice(bm, e_orig, e)) { /* don't visit again */ n_prev->next = n_step->next; } @@ -2339,7 +2254,7 @@ void BM_vert_separate_tested_edges( /** \} */ -bool BM_edge_splice(BMesh *bm, BMEdge *e_dst, BMEdge *e_src, bool combine_flags) +bool BM_edge_splice(BMesh *bm, BMEdge *e_dst, BMEdge *e_src) { BMLoop *l; @@ -2366,19 +2281,6 @@ bool BM_edge_splice(BMesh *bm, BMEdge *e_dst, BMEdge *e_src, bool combine_flags) BM_CHECK_ELEMENT(e_src); BM_CHECK_ELEMENT(e_dst); - if (combine_flags) { - /* Sharp flag is inverted to BM_ELEM_SMOOTH which we - * must take into account. - */ - - if (!(e_dst->head.hflag & BM_ELEM_SMOOTH) || !(e_src->head.hflag & BM_ELEM_SMOOTH)) { - e_dst->head.hflag = (e_dst->head.hflag | e_src->head.hflag) & ~BM_ELEM_SMOOTH; - } - else { - e_dst->head.hflag |= e_src->head.hflag; - } - } - /* removes from disks too */ BM_edge_kill(bm, e_src); diff --git a/source/blender/bmesh/intern/bmesh_core.h b/source/blender/bmesh/intern/bmesh_core.h index 1d87adfdea7..00d8d6f2fff 100644 --- a/source/blender/bmesh/intern/bmesh_core.h +++ b/source/blender/bmesh/intern/bmesh_core.h @@ -106,7 +106,7 @@ void BM_vert_kill(BMesh *bm, BMVert *v); * * \note Edges must already have the same vertices. */ -bool BM_edge_splice(BMesh *bm, BMEdge *e_dst, BMEdge *e_src, bool combine_flags); +bool BM_edge_splice(BMesh *bm, BMEdge *e_dst, BMEdge *e_src); /** * \brief Splice Vert @@ -336,16 +336,10 @@ BMEdge *bmesh_kernel_join_edge_kill_vert(BMesh *bm, * +-+-+-+ +-+-+-+ * */ -BMVert *bmesh_kernel_join_vert_kill_edge( - BMesh *bm, BMEdge *e_kill, BMVert *v_kill, const bool do_del, const bool combine_flags); - -BMVert *bmesh_kernel_join_vert_kill_edge_fast(BMesh *bm, - BMEdge *e_kill, - BMVert *v_kill, - const bool do_del, - const bool check_edge_exists, - const bool kill_degenerate_faces, - const bool combine_flags); +BMVert *bmesh_kernel_join_vert_kill_edge(BMesh *bm, + BMEdge *e_kill, + BMVert *v_kill, + const bool do_del); /** * \brief Join Face Kill Edge (JFKE) diff --git a/source/blender/bmesh/intern/bmesh_inline.h b/source/blender/bmesh/intern/bmesh_inline.h index d329cc7a436..820142269ef 100644 --- a/source/blender/bmesh/intern/bmesh_inline.h +++ b/source/blender/bmesh/intern/bmesh_inline.h @@ -72,8 +72,8 @@ BLI_INLINE void _bm_elem_flag_merge(BMHeader *head_a, BMHeader *head_b) BLI_INLINE void _bm_elem_flag_merge_ex(BMHeader *head_a, BMHeader *head_b, const char hflag_and) { if (((head_a->hflag & head_b->hflag) & hflag_and) == 0) { - head_a->hflag &= (char)(~hflag_and); - head_b->hflag &= (char)(~hflag_and); + head_a->hflag &= ~hflag_and; + head_b->hflag &= ~hflag_and; } _bm_elem_flag_merge(head_a, head_b); } diff --git a/source/blender/bmesh/intern/bmesh_interp.cc b/source/blender/bmesh/intern/bmesh_interp.cc index 128f940e6ee..ff73f1bcecb 100644 --- a/source/blender/bmesh/intern/bmesh_interp.cc +++ b/source/blender/bmesh/intern/bmesh_interp.cc @@ -17,7 +17,6 @@ #include "BLI_math_geom.h" #include "BLI_math_matrix.h" #include "BLI_math_vector.h" -#include "BLI_math_vector_types.hh" #include "BLI_memarena.h" #include "BLI_string.h" #include "BLI_task.h" @@ -30,10 +29,6 @@ #include "bmesh.h" #include "intern/bmesh_private.h" -using blender::float2; -using blender::float3; -using blender::float4; -using blender::IndexRange; using blender::Vector; /* edge and vertex share, currently there's no need to have different logic */ @@ -784,9 +779,11 @@ static void update_data_blocks(BMesh *bm, CustomData *olddata, CustomData *data) BLI_mempool *oldpool = olddata->pool; void *block; - Vector nocopy_layers; + /* Temporarily clear CD_FLAG_ELEM_NOCOPY flags + * so we don't end up skipping them. + */ - /* Temporarily clear CD_FLAG_ELEM_NOCOPY flags. */ + Vector nocopy_layers; for (int i = 0; i < olddata->totlayer; i++) { if (olddata->layers[i].flag & CD_FLAG_ELEM_NOCOPY) { olddata->layers[i].flag &= ~CD_FLAG_ELEM_NOCOPY; @@ -826,7 +823,6 @@ static void update_data_blocks(BMesh *bm, CustomData *olddata, CustomData *data) BMLoop *l; CustomData_bmesh_init_pool(data, bm->totloop, BM_LOOP); - BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) { BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { block = nullptr; diff --git a/source/blender/bmesh/intern/bmesh_interp.h b/source/blender/bmesh/intern/bmesh_interp.h index ed51dd183ba..d7f6e6b4301 100644 --- a/source/blender/bmesh/intern/bmesh_interp.h +++ b/source/blender/bmesh/intern/bmesh_interp.h @@ -68,6 +68,11 @@ typedef struct BMCustomLayerReq { const char *name; /* Can be NULL. */ int flag; } BMCustomLayerReq; + +/* Add several layers at once to the bmesh. The advantage of + * this over BM_data_layer_add is that the customdata pools + * will only be reallocated once. + */ void BM_data_layers_ensure(BMesh *bm, CustomData *data, BMCustomLayerReq *layers, int totlayer); void BM_data_layer_add(BMesh *bm, CustomData *data, int type); diff --git a/source/blender/bmesh/intern/bmesh_log.cc b/source/blender/bmesh/intern/bmesh_log.cc index 922ac0f0d5d..625a545dd54 100644 --- a/source/blender/bmesh/intern/bmesh_log.cc +++ b/source/blender/bmesh/intern/bmesh_log.cc @@ -13,7 +13,7 @@ * - Adding and removing vertices * - Adding and removing edges * - Adding and removing faces - * - Attributes. + * - Custom Attributes. * - Moving vertices * - Element header flags. */ diff --git a/source/blender/bmesh/intern/bmesh_log_intern.h b/source/blender/bmesh/intern/bmesh_log_intern.h index 888a0a6ab9e..f2fa00718c2 100644 --- a/source/blender/bmesh/intern/bmesh_log_intern.h +++ b/source/blender/bmesh/intern/bmesh_log_intern.h @@ -196,36 +196,11 @@ void BM_log_edge_removed(BMesh *bm, BMLog *log, BMEdge *e); void BM_log_face_removed(BMesh *bm, BMLog *log, struct BMFace *f); void BM_log_face_removed_no_check(BMesh *bm, BMLog *log, struct BMFace *f); -/* Log all vertices/faces in the BMesh as added */ -/* Log all vertices/faces in the BMesh as added */ -void BM_log_all_added(BMesh *bm, BMLog *log); - +/* Log the complete mesh, will be stored as + * a Mesh copy. + */ void BM_log_full_mesh(BMesh *bm, BMLog *log); -/* Log all vertices/faces in the BMesh as removed */ -/* Log all vertices/faces in the BMesh as removed */ -void BM_log_before_all_removed(BMesh *bm, BMLog *log); - -/* Get the logged coordinates of a vertex */ -/* Get the logged coordinates of a vertex - * - * Does not modify the log or the vertex */ -const float *BM_log_original_vert_co(BMLog *log, BMVert *v); - -/* Get the logged normal of a vertex - * - * Does not modify the log or the vertex */ -const float *BM_log_original_vert_no(BMLog *log, BMVert *v); - -/* Get the logged mask of a vertex */ -/* Get the logged mask of a vertex - * - * Does not modify the log or the vertex */ -float BM_log_original_mask(BMLog *log, BMVert *v); - -/* Get the logged data of a vertex (avoid multiple lookups) */ -void BM_log_original_vert_data(BMLog *log, BMVert *v, const float **r_co, const float **r_no); - /* For internal use only (unit testing) */ /* For internal use only (unit testing) */ BMLogEntry *BM_log_current_entry(BMLog *log); diff --git a/source/blender/bmesh/intern/bmesh_mesh.h b/source/blender/bmesh/intern/bmesh_mesh.h index 56c4146241c..1837984ee1c 100644 --- a/source/blender/bmesh/intern/bmesh_mesh.h +++ b/source/blender/bmesh/intern/bmesh_mesh.h @@ -209,7 +209,8 @@ void BM_mesh_vert_coords_apply_with_mat4(BMesh *bm, const float (*vert_coords)[3], const float mat[4][4]); -/* Returns true if elem is freed. ASAN-safe. */ +/* Returns true if elem is freed. Used by DynTopo. + * Is ASAN-safe. */ bool BM_elem_is_free(BMElem *elem, int htype); #ifdef __cplusplus diff --git a/source/blender/bmesh/intern/bmesh_mesh_convert.cc b/source/blender/bmesh/intern/bmesh_mesh_convert.cc index 29a0e65557c..7dbfec21db6 100644 --- a/source/blender/bmesh/intern/bmesh_mesh_convert.cc +++ b/source/blender/bmesh/intern/bmesh_mesh_convert.cc @@ -271,7 +271,6 @@ void BM_mesh_bm_from_me(BMesh *bm, const Mesh *me, const BMeshFromMeshParams *pa /* Sanity check. */ return; } - const bool is_new = !(bm->totvert || (bm->vdata.totlayer || bm->edata.totlayer || bm->pdata.totlayer || bm->ldata.totlayer)); KeyBlock *actkey; @@ -447,7 +446,6 @@ void BM_mesh_bm_from_me(BMesh *bm, const Mesh *me, const BMeshFromMeshParams *pa } int i; - KeyBlock *block; for (i = 0, block = static_cast(me->key->block.first); i < tot_shape_keys; block = block->next, i++) diff --git a/source/blender/bmesh/intern/bmesh_mesh_convert.h b/source/blender/bmesh/intern/bmesh_mesh_convert.h index ab325e1692b..993b90da7f3 100644 --- a/source/blender/bmesh/intern/bmesh_mesh_convert.h +++ b/source/blender/bmesh/intern/bmesh_mesh_convert.h @@ -31,7 +31,6 @@ struct Mesh; struct BMeshFromMeshParams { bool calc_face_normal; bool calc_vert_normal; - /* add a vertex CD_SHAPE_KEYINDEX layer */ bool add_key_index; /* set vertex coordinates from the shapekey */ @@ -42,7 +41,6 @@ struct BMeshFromMeshParams { uint copy_temp_cdlayers; }; -struct Object; /** * \brief Mesh -> BMesh * \param bm: The mesh to write into, while this is typically a newly created BMesh, diff --git a/source/blender/bmesh/intern/bmesh_mods.cc b/source/blender/bmesh/intern/bmesh_mods.cc index 9acf6d946f8..99021d2385e 100644 --- a/source/blender/bmesh/intern/bmesh_mods.cc +++ b/source/blender/bmesh/intern/bmesh_mods.cc @@ -441,24 +441,10 @@ BMEdge *BM_vert_collapse_edge(BMesh *bm, #undef DO_V_INTERP -/** - * Collapse and edge into a single vertex. - */ -BMVert *BM_edge_collapse(BMesh *bm, - BMEdge *e_kill, - BMVert *v_kill, - const bool do_del, - const bool kill_degenerate_faces, - const bool combine_flags, - const bool full_non_manifold_collapse) +BMVert *BM_edge_collapse( + BMesh *bm, BMEdge *e_kill, BMVert *v_kill, bool do_del, bool /*kill_degenerate_faces*/) { - if (full_non_manifold_collapse || true) { - return bmesh_kernel_join_vert_kill_edge(bm, e_kill, v_kill, do_del, combine_flags); - } - else { - return bmesh_kernel_join_vert_kill_edge_fast( - bm, e_kill, v_kill, do_del, true, kill_degenerate_faces, combine_flags); - } + return bmesh_kernel_join_vert_kill_edge(bm, e_kill, v_kill, do_del); } BMVert *BM_edge_split(BMesh *bm, BMEdge *e, BMVert *v, BMEdge **r_e, float fac) diff --git a/source/blender/bmesh/intern/bmesh_mods.h b/source/blender/bmesh/intern/bmesh_mods.h index a6a13d5b260..3db4a174288 100644 --- a/source/blender/bmesh/intern/bmesh_mods.h +++ b/source/blender/bmesh/intern/bmesh_mods.h @@ -157,13 +157,8 @@ BMEdge *BM_vert_collapse_edge(BMesh *bm, /** * Collapse and edge into a single vertex. */ -BMVert *BM_edge_collapse(BMesh *bm, - BMEdge *e_kill, - BMVert *v_kill, - const bool do_del, - const bool kill_degenerate_faces, - const bool combine_flags, - const bool full_non_manifold_collapse); +BMVert *BM_edge_collapse( + BMesh *bm, BMEdge *e_kill, BMVert *v_kill, bool do_del, bool kill_degenerate_faces); /** * \brief Edge Split diff --git a/source/blender/bmesh/intern/bmesh_private.h b/source/blender/bmesh/intern/bmesh_private.h index 659d81e7f76..087f13fe7c9 100644 --- a/source/blender/bmesh/intern/bmesh_private.h +++ b/source/blender/bmesh/intern/bmesh_private.h @@ -89,11 +89,6 @@ enum { */ void poly_rotate_plane(const float normal[3], float (*verts)[3], uint nverts); -void bm_kill_only_face(BMesh *bm, BMFace *f); -void bm_kill_only_loop(BMesh *bm, BMLoop *l); -const char *bm_get_error_str(int err); -int bmesh_elem_check(void *element, const char htype); - /* include the rest of our private declarations */ #include "bmesh_structure.h" diff --git a/source/blender/bmesh/operators/bmo_dupe.cc b/source/blender/bmesh/operators/bmo_dupe.cc index 85820705d65..2b6ab768a2e 100644 --- a/source/blender/bmesh/operators/bmo_dupe.cc +++ b/source/blender/bmesh/operators/bmo_dupe.cc @@ -18,7 +18,6 @@ #include "bmesh.h" #include "intern/bmesh_operators_private.h" /* own include */ -#include "intern/bmesh_structure.h" /* local flag define */ #define DUPE_INPUT 1 /* input from operator */ @@ -581,7 +580,7 @@ void bmo_spin_exec(BMesh *bm, BMOperator *op) BMEdge *e_src = (BMEdge *)elem_array[i]; BMEdge *e_dst = BM_edge_find_double(e_src); if (e_dst != nullptr) { - BM_edge_splice(bm, e_dst, e_src, false); + BM_edge_splice(bm, e_dst, e_src); elem_array_len--; elem_array[i] = elem_array[elem_array_len]; continue; diff --git a/source/blender/bmesh/operators/bmo_extrude.cc b/source/blender/bmesh/operators/bmo_extrude.cc index 5e8e16dbf02..aea7a3a504b 100644 --- a/source/blender/bmesh/operators/bmo_extrude.cc +++ b/source/blender/bmesh/operators/bmo_extrude.cc @@ -611,8 +611,7 @@ void bmo_extrude_face_region_exec(BMesh *bm, BMOperator *op) BMEdge *e_other = BM_DISK_EDGE_NEXT(e, v); if ((e_other == e) || (BM_DISK_EDGE_NEXT(e_other, v) == e)) { /* Loose edge or BMVert is edge pair. */ - BM_edge_collapse( - bm, BMO_elem_flag_test(bm, e, EXT_TAG) ? e : e_other, v, true, true, true, true); + BM_edge_collapse(bm, BMO_elem_flag_test(bm, e, EXT_TAG) ? e : e_other, v, true, true); } else { BLI_assert(!BM_vert_is_edge_pair(v)); diff --git a/source/blender/bmesh/operators/bmo_fill_grid.cc b/source/blender/bmesh/operators/bmo_fill_grid.cc index a79daa32e48..3917dd54208 100644 --- a/source/blender/bmesh/operators/bmo_fill_grid.cc +++ b/source/blender/bmesh/operators/bmo_fill_grid.cc @@ -715,7 +715,7 @@ void bmo_grid_fill_exec(BMesh *bm, BMOperator *op) GSetIterator gs_iter; GSET_ITER (gs_iter, split_edges) { BMEdge *e = static_cast(BLI_gsetIterator_getKey(&gs_iter)); - BM_edge_collapse(bm, e, e->v2, true, true, false, false); + BM_edge_collapse(bm, e, e->v2, true, true); } BLI_gset_free(split_edges, nullptr); } diff --git a/source/blender/bmesh/operators/bmo_primitive.cc b/source/blender/bmesh/operators/bmo_primitive.cc index 7f84cb3fb6f..9ae81dfb739 100644 --- a/source/blender/bmesh/operators/bmo_primitive.cc +++ b/source/blender/bmesh/operators/bmo_primitive.cc @@ -1476,7 +1476,7 @@ void bmo_create_cone_exec(BMesh *bm, BMOperator *op) for (int i = 0; i < side_faces_len; i++) { f = side_faces[i]; BMLoop *l = BM_FACE_FIRST_LOOP(f); - BM_edge_collapse(bm, l->prev->e, l->prev->v, true, true, false, false); + BM_edge_collapse(bm, l->prev->e, l->prev->v, true, true); } } @@ -1488,7 +1488,7 @@ void bmo_create_cone_exec(BMesh *bm, BMOperator *op) for (int i = 0; i < side_faces_len; i++) { f = side_faces[i]; BMLoop *l = BM_FACE_FIRST_LOOP(f); - BM_edge_collapse(bm, l->next->e, l->next->v, true, true, false, false); + BM_edge_collapse(bm, l->next->e, l->next->v, true, true); } } diff --git a/source/blender/bmesh/operators/bmo_removedoubles.cc b/source/blender/bmesh/operators/bmo_removedoubles.cc index 49f26197941..42d7ed50071 100644 --- a/source/blender/bmesh/operators/bmo_removedoubles.cc +++ b/source/blender/bmesh/operators/bmo_removedoubles.cc @@ -16,7 +16,6 @@ #include "BLI_math_vector.h" #include "BLI_stack.h" #include "BLI_utildefines_stack.h" -#include "DNA_meshdata_types.h" #include "BKE_customdata.h" @@ -439,8 +438,6 @@ void bmo_pointmerge_exec(BMesh *bm, BMOperator *op) BMO_op_finish(bm, &weldop); } -#define USE_BM_EDGE_COLLAPSE - void bmo_collapse_exec(BMesh *bm, BMOperator *op) { BMOperator weldop; @@ -509,12 +506,6 @@ void bmo_collapse_exec(BMesh *bm, BMOperator *op) uint j; BLI_stack_pop(edge_stack, &e); -#ifdef USE_BM_EDGE_COLLAPSE - if (BM_elem_is_free((BMElem *)e, BM_EDGE)) { - continue; - } -#endif - for (j = 0; j < 2; j++) { BMVert *v_src = *((&e->v1) + j); @@ -522,11 +513,6 @@ void bmo_collapse_exec(BMesh *bm, BMOperator *op) if ((v_src != v_tar) && !BM_elem_flag_test(v_src, BM_ELEM_TAG)) { BM_elem_flag_enable(v_src, BM_ELEM_TAG); BMO_slot_map_elem_insert(&weldop, slot_targetmap, v_src, v_tar); - -#ifdef USE_BM_EDGE_COLLAPSE - BM_edge_collapse(bm, e, v_src, true, true, true, true); - break; -#endif } } } @@ -535,10 +521,7 @@ void bmo_collapse_exec(BMesh *bm, BMOperator *op) BLI_stack_free(edge_stack); -#ifndef USE_BM_EDGE_COLLAPSE BMO_op_exec(bm, &weldop); -#endif - BMO_op_finish(bm, &weldop); BMW_end(&walker); diff --git a/source/blender/bmesh/operators/bmo_symmetrize.cc b/source/blender/bmesh/operators/bmo_symmetrize.cc index e86245f6ffb..56f87048e0e 100644 --- a/source/blender/bmesh/operators/bmo_symmetrize.cc +++ b/source/blender/bmesh/operators/bmo_symmetrize.cc @@ -84,10 +84,6 @@ void bmo_symmetrize_exec(BMesh *bm, BMOperator *op) slot_targetmap = BMO_slot_get(op_weld.slots_in, "targetmap"); BMO_ITER (v, &siter, op_bisect.slots_out, "geom_cut.out", BM_VERT) { - if (!BM_vert_is_boundary(v)) { - continue; - } - BMVert *v_dupe = static_cast(BMO_slot_map_elem_get(slot_vertmap, v)); BMO_slot_map_elem_insert(&op_weld, slot_targetmap, v_dupe, v); } diff --git a/source/blender/bmesh/tools/bmesh_decimate_collapse.cc b/source/blender/bmesh/tools/bmesh_decimate_collapse.cc index dff3e5f5fb2..df77c08f260 100644 --- a/source/blender/bmesh/tools/bmesh_decimate_collapse.cc +++ b/source/blender/bmesh/tools/bmesh_decimate_collapse.cc @@ -1034,8 +1034,8 @@ static bool bm_edge_collapse(BMesh *bm, e_a_other[1]->head.hflag |= e_a_other[0]->head.hflag; e_b_other[1]->head.hflag |= e_b_other[0]->head.hflag; - BM_edge_splice(bm, e_a_other[1], e_a_other[0], false); - BM_edge_splice(bm, e_b_other[1], e_b_other[0], false); + BM_edge_splice(bm, e_a_other[1], e_a_other[0]); + BM_edge_splice(bm, e_b_other[1], e_b_other[0]); #ifdef USE_SYMMETRY /* update mirror map */ @@ -1094,7 +1094,7 @@ static bool bm_edge_collapse(BMesh *bm, BM_vert_splice(bm, v_other, v_clear); e_a_other[1]->head.hflag |= e_a_other[0]->head.hflag; - BM_edge_splice(bm, e_a_other[1], e_a_other[0], false); + BM_edge_splice(bm, e_a_other[1], e_a_other[0]); #ifdef USE_SYMMETRY /* update mirror map */ diff --git a/source/blender/editors/mesh/editmesh_polybuild.cc b/source/blender/editors/mesh/editmesh_polybuild.cc index a19f6bad6fd..4db31cc18e9 100644 --- a/source/blender/editors/mesh/editmesh_polybuild.cc +++ b/source/blender/editors/mesh/editmesh_polybuild.cc @@ -216,7 +216,7 @@ static int edbm_polybuild_delete_at_cursor_invoke(bContext *C, if (ele_act->head.htype == BM_VERT) { BMVert *v_act = (BMVert *)ele_act; if (BM_vert_is_edge_pair(v_act) && !BM_vert_is_wire(v_act)) { - BM_edge_collapse(bm, v_act->e, v_act, true, true, false, false); + BM_edge_collapse(bm, v_act->e, v_act, true, true); changed = true; } else { @@ -567,7 +567,7 @@ static int edbm_polybuild_dissolve_at_cursor_invoke(bContext *C, else if (ele_act->head.htype == BM_VERT) { BMVert *v_act = (BMVert *)ele_act; if (BM_vert_is_edge_pair(v_act)) { - BM_edge_collapse(bm, v_act->e, v_act, true, true, false, false); + BM_edge_collapse(bm, v_act->e, v_act, true, true); } else { /* too involved to do inline */ -- 2.30.2 From e8e6c40a51e6323f4e1334dd319b5c60f18946fe Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Sun, 8 Oct 2023 15:56:02 -0700 Subject: [PATCH 266/279] temp-sculpt-dyntopo Revert IDE changed file --- .gitignore | 3 --- 1 file changed, 3 deletions(-) diff --git a/.gitignore b/.gitignore index 8cd5a131bf6..19da8b9205c 100644 --- a/.gitignore +++ b/.gitignore @@ -72,6 +72,3 @@ waveletNoiseTile.bin CMakeSettings.json CMakePresets.json CMakeUserPresets.json -/myprofile/AppData -/myprofile_clean/AppData/Roaming/Blender Foundation/Blender/3.3/config -/Editing User_JoeEagar_Reports_2022 - Blender Developer Wiki_files -- 2.30.2 From b3a89ad0f4f72251edc69d1ba591f5bc8d703ec4 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Sun, 8 Oct 2023 16:19:27 -0700 Subject: [PATCH 267/279] temp-sculpt-dyntopo: Revert remnants of debug draw code --- source/blender/draw/DRW_engine.h | 3 -- source/blender/draw/intern/DRW_render.h | 1 + .../draw/intern/draw_cache_extract_mesh.cc | 2 +- .../draw_cache_extract_mesh_render_data.cc | 49 +------------------ .../draw/intern/draw_cache_impl_mesh.cc | 3 +- .../intern/mesh_extractors/extract_mesh.hh | 1 - .../mesh_extractors/extract_mesh_ibo_tris.cc | 3 +- 7 files changed, 5 insertions(+), 57 deletions(-) diff --git a/source/blender/draw/DRW_engine.h b/source/blender/draw/DRW_engine.h index 4f9305e05b8..bd890e8c562 100644 --- a/source/blender/draw/DRW_engine.h +++ b/source/blender/draw/DRW_engine.h @@ -28,9 +28,6 @@ struct GPUVertFormat; struct CustomDataLayer; struct CustomData; struct GPUViewport; -struct GPUVertFormat; -struct CustomData; -struct CustomDataLayer; struct ID; struct Main; struct Object; diff --git a/source/blender/draw/intern/DRW_render.h b/source/blender/draw/intern/DRW_render.h index 924e9f78f67..ced0dd08473 100644 --- a/source/blender/draw/intern/DRW_render.h +++ b/source/blender/draw/intern/DRW_render.h @@ -1014,6 +1014,7 @@ void DRW_mesh_batch_cache_get_attributes(struct Object *object, struct Mesh *me, struct DRW_Attributes **r_attrs, struct DRW_MeshCDMask **r_cd_needed); + void DRW_sculpt_debug_cb( PBVHNode *node, void *user_data, const float bmin[3], const float bmax[3], PBVHNodeFlags flag); diff --git a/source/blender/draw/intern/draw_cache_extract_mesh.cc b/source/blender/draw/intern/draw_cache_extract_mesh.cc index a1b4c3474b3..942d3fd20dd 100644 --- a/source/blender/draw/intern/draw_cache_extract_mesh.cc +++ b/source/blender/draw/intern/draw_cache_extract_mesh.cc @@ -385,7 +385,7 @@ BLI_INLINE void extract_task_range_run_iter(const MeshRenderData &mr, int stop; switch (iter_type) { case MR_ITER_LOOPTRI: - range_data.elems = is_mesh ? mr.looptris.data() : (void *)mr.bm_looptris.data(); + range_data.elems = is_mesh ? mr.looptris.data() : (void *)mr.edit_bmesh->looptris; func = is_mesh ? extract_range_iter_looptri_mesh : extract_range_iter_looptri_bm; stop = mr.tri_len; break; diff --git a/source/blender/draw/intern/draw_cache_extract_mesh_render_data.cc b/source/blender/draw/intern/draw_cache_extract_mesh_render_data.cc index 999c867f2b3..e5594396362 100644 --- a/source/blender/draw/intern/draw_cache_extract_mesh_render_data.cc +++ b/source/blender/draw/intern/draw_cache_extract_mesh_render_data.cc @@ -20,7 +20,6 @@ #include "BKE_editmesh_cache.hh" #include "BKE_mesh.hh" #include "BKE_mesh_runtime.hh" -#include "BKE_paint.hh" #include "GPU_batch.h" @@ -447,47 +446,7 @@ MeshRenderData *mesh_render_data_create(Object *object, copy_m4_m4(mr->obmat, obmat); - // NorForPR - if (!is_editmode && object->mode == OB_MODE_SCULPT && object->sculpt && object->sculpt->bm) { - mr->bm = object->sculpt->bm; - mr->edit_bmesh = nullptr; - mr->me = me; - mr->edit_data = is_mode_active ? mr->me->runtime->edit_data : nullptr; - mr->hide_unmapped_edges = false; - int bm_ensure_types = BM_VERT | BM_EDGE | BM_LOOP | BM_FACE; - - BM_mesh_elem_index_ensure(mr->bm, bm_ensure_types); - BM_mesh_elem_table_ensure(mr->bm, bm_ensure_types & ~BM_LOOP); - - mr->efa_act_uv = EDBM_uv_active_face_get(mr->edit_bmesh, false, false); - mr->efa_act = BM_mesh_active_face_get(mr->bm, false, true); - mr->eed_act = BM_mesh_active_edge_get(mr->bm); - mr->eve_act = BM_mesh_active_vert_get(mr->bm); - - BMeshCalcTessellation_Params bmpparams = {}; - bmpparams.face_normals = false; - - int tri_count = poly_to_tri_count(mr->bm->totface, mr->bm->totloop); - - BMLoop *(*looptri)[3] = static_cast( - MEM_mallocN(sizeof(void *) * 3 * tri_count, "sculpt draw extract loop tris")); - mr->bm_looptris = {looptri, tri_count}; - BM_mesh_calc_tessellation_ex(mr->bm, mr->bm_looptris.data(), &bmpparams); - - mr->vert_crease_ofs = CustomData_get_offset_named( - &mr->bm->vdata, CD_PROP_FLOAT, "crease_vert"); - mr->edge_crease_ofs = CustomData_get_offset_named( - &mr->bm->edata, CD_PROP_FLOAT, "crease_edge"); - mr->bweight_ofs = CustomData_get_offset_named( - &mr->bm->edata, CD_PROP_FLOAT, "bevel_weight_edge"); -#ifdef WITH_FREESTYLE - mr->freestyle_edge_ofs = CustomData_get_offset(&mr->bm->edata, CD_FREESTYLE_EDGE); - mr->freestyle_face_ofs = CustomData_get_offset(&mr->bm->pdata, CD_FREESTYLE_FACE); -#endif - - mr->extract_type = MR_EXTRACT_BMESH; - } - else if (is_editmode) { + if (is_editmode) { Mesh *editmesh_eval_final = BKE_object_get_editmesh_eval_final(object); Mesh *editmesh_eval_cage = BKE_object_get_editmesh_eval_cage(object); @@ -496,7 +455,6 @@ MeshRenderData *mesh_render_data_create(Object *object, mr->edit_bmesh = me->edit_mesh; mr->me = (do_final) ? editmesh_eval_final : editmesh_eval_cage; mr->edit_data = is_mode_active ? mr->me->runtime->edit_data : nullptr; - mr->bm_looptris = {mr->edit_bmesh->looptris, mr->edit_bmesh->tottri}; /* If there is no distinct cage, hide unmapped edges that can't be selected. */ mr->hide_unmapped_edges = !do_final || editmesh_eval_final == editmesh_eval_cage; @@ -643,11 +601,6 @@ MeshRenderData *mesh_render_data_create(Object *object, void mesh_render_data_free(MeshRenderData *mr) { - /* Sculpt mode owns bm_looptris. */ - if (mr->extract_type == MR_EXTRACT_BMESH && mr->bm_looptris.data() && !mr->edit_bmesh) { - MEM_freeN(static_cast(mr->bm_looptris.data())); - } - MEM_delete(mr); } diff --git a/source/blender/draw/intern/draw_cache_impl_mesh.cc b/source/blender/draw/intern/draw_cache_impl_mesh.cc index a6b87a338fc..214228d9015 100644 --- a/source/blender/draw/intern/draw_cache_impl_mesh.cc +++ b/source/blender/draw/intern/draw_cache_impl_mesh.cc @@ -1384,8 +1384,7 @@ void DRW_mesh_batch_cache_create_requested(TaskGraph *task_graph, DRW_object_is_in_edit_mode(ob); /* This could be set for paint mode too, currently it's only used for edit-mode. */ - const bool is_mode_active = (is_editmode && DRW_object_is_in_edit_mode(ob)) || - ((ob->mode == OB_MODE_SCULPT) && ob->sculpt && ob->sculpt->bm); + const bool is_mode_active = is_editmode && DRW_object_is_in_edit_mode(ob); DRWBatchFlag batch_requested = cache.batch_requested; cache.batch_requested = (DRWBatchFlag)0; diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh.hh b/source/blender/draw/intern/mesh_extractors/extract_mesh.hh index 794adaaeccf..92adf795b86 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh.hh +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh.hh @@ -88,7 +88,6 @@ struct MeshRenderData { /* The triangulation of #Mesh faces, owned by the mesh. */ blender::Span looptris; blender::Span looptri_faces; - blender::MutableSpan bm_looptris; const int *material_indices; blender::Span vert_normals; blender::Span face_normals; diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_tris.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_tris.cc index 8e36e112641..9d4a5818442 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_tris.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_tris.cc @@ -47,8 +47,7 @@ static void extract_tris_iter_face_bm(const MeshRenderData &mr, GPUIndexBufBuilder *elb = static_cast(_data); int tri_first_index_real = poly_to_tri_count(f_index, BM_elem_index_get(f->l_first)); - BMLoop *(*looptris)[3] = mr.bm_looptris.data(); - + BMLoop *(*looptris)[3] = mr.edit_bmesh->looptris; int tri_len = f->len - 2; for (int offs = 0; offs < tri_len; offs++) { BMLoop **elt = looptris[tri_first_index_real + offs]; -- 2.30.2 From 7f7d43b7c705b1980fd1cefcf62b2cd20e626a69 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Mon, 9 Oct 2023 16:38:37 -0700 Subject: [PATCH 268/279] temp-sculpt-dyntopo: Fix crash after voxel remesh --- source/blender/blenkernel/intern/paint.cc | 10 +++++++--- source/blender/editors/sculpt_paint/sculpt_undo.cc | 5 +++++ 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/source/blender/blenkernel/intern/paint.cc b/source/blender/blenkernel/intern/paint.cc index 00e5b9ebeb9..d90dde4dc1f 100644 --- a/source/blender/blenkernel/intern/paint.cc +++ b/source/blender/blenkernel/intern/paint.cc @@ -1493,6 +1493,8 @@ static bool sculpt_boundary_flags_ensure( force_update = true; ret = true; + + BKE_pbvh_set_boundary_flags(pbvh, static_cast(ss->attrs.boundary_flags->data)); } if (force_update) { @@ -2726,6 +2728,11 @@ static PBVH *build_pbvh_from_regular_mesh(Object *ob, Mesh *me_eval_deform) PBVH *pbvh = ob->sculpt->pbvh = BKE_pbvh_new(PBVH_FACES); + BKE_pbvh_build_mesh(pbvh, me); + + BKE_sculptsession_update_attr_refs(ob); + + blender::bke::pbvh::set_pmap(ss->pbvh, ss->pmap); BKE_sculpt_ensure_sculpt_layers(ob); BKE_sculpt_init_flags_valence(ob, pbvh, me->totvert, true); BKE_sculpt_ensure_origco(ob); @@ -2742,11 +2749,8 @@ static PBVH *build_pbvh_from_regular_mesh(Object *ob, Mesh *me_eval_deform) sculpt_check_face_areas(ob, pbvh); BKE_sculptsession_update_attr_refs(ob); - blender::bke::pbvh::set_pmap(ss->pbvh, ss->pmap); blender::bke::sculpt::sculpt_vert_boundary_ensure(ob); - BKE_pbvh_build_mesh(pbvh, me); - blender::bke::pbvh::sharp_limit_set(pbvh, ss->sharp_angle_limit); const bool is_deformed = check_sculpt_object_deformed(ob, true); diff --git a/source/blender/editors/sculpt_paint/sculpt_undo.cc b/source/blender/editors/sculpt_paint/sculpt_undo.cc index 6359cd0c652..4d82267db50 100644 --- a/source/blender/editors/sculpt_paint/sculpt_undo.cc +++ b/source/blender/editors/sculpt_paint/sculpt_undo.cc @@ -1254,6 +1254,8 @@ static void sculpt_undo_geometry_restore_data(SculptUndoNodeGeometry *geometry, geometry->face_offsets_sharing_info, &mesh->face_offset_indices, &mesh->runtime->face_offsets_sharing_info); + + BKE_sculptsession_update_attr_refs(object); } static void sculpt_undo_geometry_free_data(SculptUndoNodeGeometry *geometry) @@ -2909,6 +2911,9 @@ void ED_sculpt_undo_geometry_begin_ex(Object *ob, const char *name) void ED_sculpt_undo_geometry_end(Object *ob) { + /* Ensure sculpt attribute references are up to date. */ + BKE_sculptsession_update_attr_refs(ob); + SCULPT_undo_push_node(ob, nullptr, SCULPT_UNDO_GEOMETRY); SCULPT_undo_push_end(ob); } -- 2.30.2 From 0ee525c08a2d758124de0a4e6264781bb9e32897 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Tue, 10 Oct 2023 13:30:33 -0700 Subject: [PATCH 269/279] temp-sculpt-dyntopo: Code cleanup --- source/blender/blenkernel/BKE_paint.hh | 2 +- source/blender/blenkernel/BKE_pbvh_api.hh | 2 +- source/blender/blenkernel/intern/dyntopo.cc | 10 +- .../blenkernel/intern/dyntopo_collapse.cc | 2 +- source/blender/blenkernel/intern/paint.cc | 4 +- .../blender/blenkernel/intern/pbvh_bmesh.cc | 2 +- .../blender/blenkernel/intern/pbvh_intern.hh | 2 +- source/blender/bmesh/CMakeLists.txt | 9 +- source/blender/bmesh/bmesh_idmap.h | 1 - source/blender/bmesh/bmesh_idmap.hh | 1 + source/blender/bmesh/bmesh_log.h | 2 - source/blender/bmesh/bmesh_log.hh | 1 + source/blender/bmesh/intern/bmesh_idmap.cc | 181 +------------ .../intern/{bmesh_idmap.h => bmesh_idmap.hh} | 65 +++-- source/blender/bmesh/intern/bmesh_log.cc | 63 ++++- source/blender/bmesh/intern/bmesh_log.h | 0 source/blender/bmesh/intern/bmesh_log.hh | 119 +++++++++ .../blender/bmesh/intern/bmesh_log_intern.h | 241 ------------------ source/blender/draw/intern/draw_pbvh.cc | 2 +- .../blender/editors/object/object_remesh.cc | 2 +- .../editors/sculpt_paint/paint_mask.cc | 2 +- source/blender/editors/sculpt_paint/sculpt.cc | 2 +- .../editors/sculpt_paint/sculpt_api.cc | 2 +- .../editors/sculpt_paint/sculpt_detail.cc | 4 +- .../editors/sculpt_paint/sculpt_dyntopo.cc | 4 +- .../editors/sculpt_paint/sculpt_face_set.cc | 2 +- .../editors/sculpt_paint/sculpt_ops.cc | 6 +- .../editors/sculpt_paint/sculpt_undo.cc | 4 +- 28 files changed, 239 insertions(+), 498 deletions(-) delete mode 100644 source/blender/bmesh/bmesh_idmap.h create mode 100644 source/blender/bmesh/bmesh_idmap.hh delete mode 100644 source/blender/bmesh/bmesh_log.h create mode 100644 source/blender/bmesh/bmesh_log.hh rename source/blender/bmesh/intern/{bmesh_idmap.h => bmesh_idmap.hh} (68%) delete mode 100644 source/blender/bmesh/intern/bmesh_log.h create mode 100644 source/blender/bmesh/intern/bmesh_log.hh delete mode 100644 source/blender/bmesh/intern/bmesh_log_intern.h diff --git a/source/blender/blenkernel/BKE_paint.hh b/source/blender/blenkernel/BKE_paint.hh index 1dde2fee3c1..fbd669538ba 100644 --- a/source/blender/blenkernel/BKE_paint.hh +++ b/source/blender/blenkernel/BKE_paint.hh @@ -31,7 +31,6 @@ #include "BKE_pbvh.h" #include "bmesh.h" -#include "bmesh_log.h" #include @@ -39,6 +38,7 @@ struct SculptAttribute; struct BMFace; struct BMesh; struct BMIdMap; +struct BMLog; struct BlendDataReader; struct BlendLibReader; struct BlendWriter; diff --git a/source/blender/blenkernel/BKE_pbvh_api.hh b/source/blender/blenkernel/BKE_pbvh_api.hh index b36115ae48e..ea2271f9361 100644 --- a/source/blender/blenkernel/BKE_pbvh_api.hh +++ b/source/blender/blenkernel/BKE_pbvh_api.hh @@ -29,7 +29,7 @@ #include "BKE_pbvh.h" #include "bmesh.h" -#include "bmesh_log.h" +#include "bmesh_log.hh" #include diff --git a/source/blender/blenkernel/intern/dyntopo.cc b/source/blender/blenkernel/intern/dyntopo.cc index d7821e3c1ce..768476ca3fd 100644 --- a/source/blender/blenkernel/intern/dyntopo.cc +++ b/source/blender/blenkernel/intern/dyntopo.cc @@ -44,7 +44,7 @@ #include "BKE_sculpt.hh" #include "bmesh.h" -#include "bmesh_log.h" +#include "bmesh_log.hh" #include "dyntopo_intern.hh" #include "pbvh_intern.hh" @@ -707,9 +707,6 @@ static void unified_edge_queue_task_cb(void *__restrict userdata, const char facetag = BM_ELEM_TAG_ALT; - /* Only do reprojection if UVs exist. */ - eAttrCorrectMode distort_correction_mode = eq_ctx->distort_correction_mode; - /* * Clear edge flags. * @@ -2217,7 +2214,10 @@ void EdgeQueueContext::finish() } template -BMEdge *EdgeQueueContext::pop_invalid_edges(EdgeHeapT &heap, BMEdge *in_e, float &w, bool is_max) +BMEdge *EdgeQueueContext::pop_invalid_edges(EdgeHeapT & /*heap*/, + BMEdge *in_e, + float &w, + bool is_max) { if (!in_e) { return nullptr; diff --git a/source/blender/blenkernel/intern/dyntopo_collapse.cc b/source/blender/blenkernel/intern/dyntopo_collapse.cc index e8c671b8465..d681e1f5fbf 100644 --- a/source/blender/blenkernel/intern/dyntopo_collapse.cc +++ b/source/blender/blenkernel/intern/dyntopo_collapse.cc @@ -33,7 +33,7 @@ #include "../../bmesh/intern/bmesh_collapse.hh" #include "bmesh.h" -#include "bmesh_log.h" +#include "bmesh_log.hh" #include "dyntopo_intern.hh" #include "pbvh_intern.hh" diff --git a/source/blender/blenkernel/intern/paint.cc b/source/blender/blenkernel/intern/paint.cc index d90dde4dc1f..619bc6b48ed 100644 --- a/source/blender/blenkernel/intern/paint.cc +++ b/source/blender/blenkernel/intern/paint.cc @@ -80,8 +80,8 @@ #include "BLO_read_write.hh" #include "bmesh.h" -#include "bmesh_idmap.h" -#include "bmesh_log.h" +#include "bmesh_idmap.hh" +#include "bmesh_log.hh" // TODO: figure out bad cross module refs void SCULPT_undo_ensure_bmlog(Object *ob); diff --git a/source/blender/blenkernel/intern/pbvh_bmesh.cc b/source/blender/blenkernel/intern/pbvh_bmesh.cc index e66ed613308..1789c253814 100644 --- a/source/blender/blenkernel/intern/pbvh_bmesh.cc +++ b/source/blender/blenkernel/intern/pbvh_bmesh.cc @@ -68,7 +68,7 @@ Topology rake: #include "atomic_ops.h" #include "bmesh.h" -#include "bmesh_log.h" +#include "bmesh_log.hh" #include "dyntopo_intern.hh" #include "pbvh_intern.hh" diff --git a/source/blender/blenkernel/intern/pbvh_intern.hh b/source/blender/blenkernel/intern/pbvh_intern.hh index f463acc8fb5..4dc67117f21 100644 --- a/source/blender/blenkernel/intern/pbvh_intern.hh +++ b/source/blender/blenkernel/intern/pbvh_intern.hh @@ -14,7 +14,7 @@ #include "BKE_pbvh_api.hh" #include "bmesh.h" -#include "bmesh_idmap.h" +#include "bmesh_idmap.hh" #define PBVH_STACK_FIXED_DEPTH 100 diff --git a/source/blender/bmesh/CMakeLists.txt b/source/blender/bmesh/CMakeLists.txt index 3ee2e41daff..5d10ce17de5 100644 --- a/source/blender/bmesh/CMakeLists.txt +++ b/source/blender/bmesh/CMakeLists.txt @@ -70,7 +70,7 @@ set(SRC intern/bmesh_edgeloop.cc intern/bmesh_edgeloop.h intern/bmesh_idmap.cc - intern/bmesh_idmap.h + intern/bmesh_idmap.hh intern/bmesh_inline.h intern/bmesh_interp.cc intern/bmesh_interp.h @@ -78,8 +78,7 @@ set(SRC intern/bmesh_iterators.h intern/bmesh_iterators_inline.h intern/bmesh_log.cc - intern/bmesh_log_intern.h - intern/bmesh_log.h + intern/bmesh_log.hh intern/bmesh_marking.cc intern/bmesh_marking.h intern/bmesh_mesh.cc @@ -164,8 +163,8 @@ set(SRC tools/bmesh_wireframe.h bmesh_class.h - bmesh_idmap.h - bmesh_log.h + bmesh_idmap.hh + bmesh_log.hh # public includes bmesh.h diff --git a/source/blender/bmesh/bmesh_idmap.h b/source/blender/bmesh/bmesh_idmap.h deleted file mode 100644 index 28106209b3a..00000000000 --- a/source/blender/bmesh/bmesh_idmap.h +++ /dev/null @@ -1 +0,0 @@ -#include "intern/bmesh_idmap.h" diff --git a/source/blender/bmesh/bmesh_idmap.hh b/source/blender/bmesh/bmesh_idmap.hh new file mode 100644 index 00000000000..56435e8facc --- /dev/null +++ b/source/blender/bmesh/bmesh_idmap.hh @@ -0,0 +1 @@ +#include "intern/bmesh_idmap.hh" diff --git a/source/blender/bmesh/bmesh_log.h b/source/blender/bmesh/bmesh_log.h deleted file mode 100644 index 84c4df8d6cd..00000000000 --- a/source/blender/bmesh/bmesh_log.h +++ /dev/null @@ -1,2 +0,0 @@ -#include "intern/bmesh_log_intern.h" - diff --git a/source/blender/bmesh/bmesh_log.hh b/source/blender/bmesh/bmesh_log.hh new file mode 100644 index 00000000000..08493cbbed5 --- /dev/null +++ b/source/blender/bmesh/bmesh_log.hh @@ -0,0 +1 @@ +#include "intern/bmesh_log.hh" diff --git a/source/blender/bmesh/intern/bmesh_idmap.cc b/source/blender/bmesh/intern/bmesh_idmap.cc index ef5cc4b9f66..cd5a27fa567 100644 --- a/source/blender/bmesh/intern/bmesh_idmap.cc +++ b/source/blender/bmesh/intern/bmesh_idmap.cc @@ -14,28 +14,18 @@ #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" -#include "bmesh_idmap.h" +#include "bmesh_idmap.hh" #include #include using namespace blender; +/* Threshold of size of BMIDMap.freelist where .free_idx_map + * (a hash map) will be created to find IDs inside the freelist. + */ #define FREELIST_HASHMAP_THRESHOLD_HIGH 1024 #define FREELIST_HASHMAP_THRESHOLD_LOW 700 -#ifdef DEBUG_BM_IDMAP -static void bm_idmap_debug_check_init(BMesh *bm) -{ - /* Disable mempool allocation so we can use - * element pointers as backup IDs. - */ - BLI_mempool_ignore_free(bm->vpool); - BLI_mempool_ignore_free(bm->epool); - BLI_mempool_ignore_free(bm->lpool); - BLI_mempool_ignore_free(bm->fpool); -} -#endif - const char *BM_idmap_attr_name_get(int htype) { switch (htype) { @@ -61,30 +51,10 @@ static void idmap_log_message(const char *fmt, ...) va_end(args); } -#ifdef DEBUG_BM_IDMAP -static void _idmap_debug_insert(const char *func, BMIdMap *idmap, BMElem *elem, int id) -{ - if (id == BM_ID_NONE) { - idmap_log_message("%s: Tried to assign a null id\n", func); - } - - idmap->elem2id->add(elem, id); - idmap->id2elem->add(id, elem); -} - -# define idmap_debug_insert(idmap, elem, id) _idmap_debug_insert(__func__, idmap, elem, id) -#endif - BMIdMap *BM_idmap_new(BMesh *bm, int elem_mask) { BMIdMap *idmap = MEM_new("BMIdMap"); -#ifdef DEBUG_BM_IDMAP - bm_idmap_debug_check_init(bm); - idmap->elem2id = new blender::Map; - idmap->id2elem = new blender::Map; -#endif - for (int i = 0; i < ARRAY_SIZE(idmap->cd_id_off); i++) { idmap->cd_id_off[i] = -1; } @@ -114,55 +84,6 @@ template static constexpr char get_elem_type() } } -#ifdef DEBUG_BM_IDMAP -static bool _idmap_check_elem(const char *func, BMIdMap *idmap, BMElem *elem) -{ - int id = BM_idmap_get_id(idmap, elem); - bool exists = idmap->elem2id->contains(elem); - - if (!elem || !ELEM(elem->head.htype, BM_VERT, BM_EDGE, BM_LOOP, BM_FACE)) { - idmap_log_message("%s: bad call to idmap_check_elem; %p\n", func, elem); - return false; - } - - if (id == BM_ID_NONE && !exists) { - return true; - } - - if (id != BM_ID_NONE && !exists) { - idmap_log_message("%s: elem %p(%d, a %s) has an id but isn't in map\n", - func, - elem, - id, - get_type_name(elem->head.htype)); - if (idmap->id2elem->contains(id)) { - BMElem *elem2 = idmap->id2elem->lookup(id); - idmap_log_message( - " another elem %p (a %s) has the id\n", elem2, get_type_name(elem2->head.htype)); - } - return false; - } - - int id2 = idmap->elem2id->contains(elem) ? idmap->elem2id->lookup(elem) : -1; - if (id2 != id) { - idmap_log_message("%s: elem %p (a %s) has id %d; expected %d\n", - func, - elem, - get_type_name(elem->head.htype), - id, - id2); - } - - return true; -} - -# define idmap_check_elem(idmap, elem) _idmap_check_elem(__func__, idmap, elem) -#else -# define idmap_check_elem(idmap, elem) \ - do { \ - } while (0) -#endif - static void idmap_grow_map(BMIdMap *idmap, int newid) { if (idmap->map_size > newid) { @@ -205,15 +126,6 @@ void BM_idmap_check_ids(BMIdMap *idmap) BMEdge *e; BMFace *f; -#ifdef DEBUG_BM_IDMAP - bm_idmap_debug_check_init(idmap->bm); - delete idmap->id2elem; - delete idmap->elem2id; - - idmap->elem2id = new blender::Map; - idmap->id2elem = new blender::Map; -#endif - BM_idmap_check_attributes(idmap); idmap->freelist.clear(); @@ -277,10 +189,6 @@ void BM_idmap_check_ids(BMIdMap *idmap) idmap_grow_map(idmap, id); idmap->map[id] = reinterpret_cast(elem); - -#ifdef DEBUG_BM_IDMAP - idmap_debug_insert(idmap, reinterpret_cast(elem), id); -#endif }; if (idmap->flag & BM_VERT) { @@ -364,11 +272,6 @@ bool BM_idmap_check_attributes(BMIdMap *idmap) void BM_idmap_destroy(BMIdMap *idmap) { -#ifdef DEBUG_BM_IDMAP - delete idmap->elem2id; - delete idmap->id2elem; -#endif - if (idmap->free_idx_map) { MEM_delete(idmap->free_idx_map); } @@ -400,33 +303,6 @@ int BM_idmap_alloc(BMIdMap *idmap, BMElem *elem) { int id = BM_ID_NONE; -#ifdef DEBUG_BM_IDMAP - int id2 = BM_ELEM_CD_GET_INT(elem, idmap->cd_id_off[int(elem->head.htype)]); - - if (idmap->elem2id->contains(elem)) { - int id3 = idmap->elem2id->lookup(elem); - - if (id2 == id3) { - idmap_log_message("%s: elem %p already had id %d\n", __func__, elem, id3); - } - else { - idmap_log_message( - "%s: elem %p already has an id (%d), but its attribute has the wrong one (%d)\n", - __func__, - elem, - id3, - id2); - } - - idmap->elem2id->remove(elem); - } - - if (idmap->id2elem->contains(id)) { - idmap->id2elem->remove(id); - } - -#endif - while (idmap->freelist.size()) { id = idmap->freelist.pop_last(); @@ -450,10 +326,6 @@ int BM_idmap_alloc(BMIdMap *idmap, BMElem *elem) BM_ELEM_CD_SET_INT(elem, idmap->cd_id_off[int(elem->head.htype)], id); -#ifdef DEBUG_BM_IDMAP - idmap_debug_insert(idmap, elem, id); -#endif - return id; } @@ -482,51 +354,10 @@ void BM_idmap_assign(BMIdMap *idmap, BMElem *elem, int id) idmap->map[id] = elem; check_idx_map(idmap); - -#ifdef DEBUG_BM_IDMAP - if (idmap->elem2id->contains(elem) && idmap->elem2id->lookup(elem) == id) { - return; - } - - if (idmap->elem2id->contains(elem)) { - int id2 = idmap->elem2id->lookup(elem); - - idmap_log_message("%s: elem %p already had id %d, new id: %d\n", __func__, elem, id2, id); - idmap->elem2id->remove(elem); - } - - if (idmap->id2elem->contains(id)) { - BMElem *elem2 = idmap->id2elem->lookup(id); - if (elem2 != elem) { - idmap_log_message("%s: elem %p (a %s) took over id from elem %p (a %s)\n", - __func__, - elem, - get_type_name(elem->head.htype), - elem2, - get_type_name(elem2->head.htype)); - } - } - - idmap_debug_insert(idmap, elem, id); - idmap_check_elem(idmap, elem); -#endif } void BM_idmap_release(BMIdMap *idmap, BMElem *elem, bool clear_id) { -#ifdef DEBUG_BM_IDMAP - idmap_check_elem(idmap, elem); - - if (idmap->elem2id->contains(elem)) { - int id2 = idmap->elem2id->lookup(elem); - - if (idmap->id2elem->contains(id2)) { - idmap->id2elem->remove(id2); - } - idmap->elem2id->remove(elem); - } -#endif - int id = BM_ELEM_CD_GET_INT(elem, idmap->cd_id_off[int(elem->head.htype)]); if (id == BM_ID_NONE) { @@ -561,9 +392,5 @@ int BM_idmap_check_assign(BMIdMap *idmap, BMElem *elem) id = BM_idmap_alloc(idmap, elem); } -#ifdef DEBUG_BM_IDMAP - idmap_check_elem(idmap, elem); -#endif - return id; } diff --git a/source/blender/bmesh/intern/bmesh_idmap.h b/source/blender/bmesh/intern/bmesh_idmap.hh similarity index 68% rename from source/blender/bmesh/intern/bmesh_idmap.h rename to source/blender/bmesh/intern/bmesh_idmap.hh index ecdc083aec7..a92227be084 100644 --- a/source/blender/bmesh/intern/bmesh_idmap.h +++ b/source/blender/bmesh/intern/bmesh_idmap.hh @@ -1,19 +1,15 @@ -#include "BLI_compiler_attrs.h" #include "BLI_compiler_compat.h" +#include "BLI_map.hh" #include "BLI_sys_types.h" +#include "BLI_vector.hh" #include "bmesh.h" -//#define DEBUG_BM_IDMAP /* Debug idmap; note: disables mempool deallocation */ +//#define DEBUG_BM_IDMAP /* Debug ID map. */ -#define BM_ID_NONE 0 //-1 +#define BM_ID_NONE 0 -#ifdef __cplusplus -# include "BLI_map.hh" -# include "BLI_vector.hh" -#endif - -typedef struct BMIdMap { +struct BMIdMap { int flag; uint maxid; @@ -23,7 +19,6 @@ typedef struct BMIdMap { BMElem **map; int map_size; -#ifdef __cplusplus blender::Vector freelist; using FreeIdxMap = blender::Map; @@ -32,45 +27,47 @@ typedef struct BMIdMap { only used if freelist is bigger then a certain size, see FREELIST_HASHMAP_THRESHOLD_HIGH in bmesh_construct.c.*/ FreeIdxMap *free_idx_map; -#endif - -#ifdef DEBUG_BM_IDMAP -# ifdef __cplusplus - blender::Map *elem2id; - blender::Map *id2elem; -# else - void elem_idmap; -# endif -#endif -} BMIdMap; - -#ifdef __cplusplus -extern "C" { -#endif +}; BMIdMap *BM_idmap_new(BMesh *bm, int elem_mask); -bool BM_idmap_check_attributes(BMIdMap *idmap); -void BM_idmap_check_ids(BMIdMap *idmap); void BM_idmap_destroy(BMIdMap *idmap); -int BM_idmap_alloc(BMIdMap *idmap, BMElem *elem); +/* Ensures idmap attributes exist. */ +bool BM_idmap_check_attributes(BMIdMap *idmap); + +/* Ensures every element has a unique ID. */ +void BM_idmap_check_ids(BMIdMap *idmap); + +/* Explicitly assign an ID. id cannot be BM_ID_NONE (zero). */ void BM_idmap_assign(BMIdMap *idmap, BMElem *elem, int id); -void BM_idmap_release(BMIdMap *idmap, BMElem *elem, bool clear_id); + +/* Automatically allocate an ID. */ +int BM_idmap_alloc(BMIdMap *idmap, BMElem *elem); + +/* Checks if an element needs an ID (it's id is BM_ID_NONE), + * and if so allocates one. + */ int BM_idmap_check_assign(BMIdMap *idmap, BMElem *elem); -void BM_idmap_clear_attributes(BMesh *bm); + +/* Release an ID; if clear_id is true the id attribute for + * that element will be set to BM_ID_NONE. + */ +void BM_idmap_release(BMIdMap *idmap, BMElem *elem, bool clear_id); + const char *BM_idmap_attr_name_get(int htype); + +/* Deletes all id attributes. */ +void BM_idmap_clear_attributes(BMesh *bm); void BM_idmap_clear_attributes_mesh(Mesh *me); +/* Elem -> ID. */ BLI_INLINE int BM_idmap_get_id(BMIdMap *map, BMElem *elem) { return BM_ELEM_CD_GET_INT(elem, map->cd_id_off[(int)elem->head.htype]); } +/* ID -> elem. */ BLI_INLINE BMElem *BM_idmap_lookup(BMIdMap *map, int elem) { return elem >= 0 ? map->map[elem] : NULL; } - -#ifdef __cplusplus -} -#endif diff --git a/source/blender/bmesh/intern/bmesh_log.cc b/source/blender/bmesh/intern/bmesh_log.cc index 625a545dd54..221c8296728 100644 --- a/source/blender/bmesh/intern/bmesh_log.cc +++ b/source/blender/bmesh/intern/bmesh_log.cc @@ -38,8 +38,9 @@ #include "BLI_vector.hh" #include "bmesh.h" -#include "bmesh_idmap.h" -#include "bmesh_log_intern.h" +#include "bmesh_idmap.hh" +#include "bmesh_log.hh" + extern "C" { #include "bmesh_structure.h" } @@ -51,6 +52,11 @@ extern "C" { #include #include +using blender::float3; +using blender::Map; +using blender::Set; +using blender::Vector; + #define USE_SIMPLE_CD_COPY extern "C" void bm_log_message(const char *fmt, ...) @@ -66,8 +72,6 @@ extern "C" void bm_log_message(const char *fmt, ...) /* Avoid C++ runtime type ids. */ enum BMLogSetType { LOG_SET_DIFF, LOG_SET_FULL }; -namespace blender { - template struct BMID { int id = -1; @@ -593,6 +597,46 @@ struct BMLogEntry { CustomData_free(&pdata, 0); } + void print() + { + int av = 0, ae = 0, af = 0, mv = 0, me = 0, mf = 0, dv = 0, de = 0, df = 0; + int totmesh = 0; + + for (BMLogSetBase *set : sets) { + switch (set->type) { + case LOG_SET_DIFF: { + BMLogSetDiff *diff = static_cast(set); + + av += diff->added_verts.size(); + ae += diff->added_edges.size(); + af += diff->added_faces.size(); + + mv += diff->modified_verts.size(); + me += diff->modified_edges.size(); + mf += diff->modified_faces.size(); + + dv += diff->removed_verts.size(); + de += diff->removed_edges.size(); + df += diff->removed_faces.size(); + break; + } + case LOG_SET_FULL: + totmesh++; + break; + } + } + + if (av + ae + af + mv + me + mf + dv + de + df) { + printf(" addv: %d, adde: %d, addf: %d\n", av, ae, af); + printf(" modv: %d, mode: %d, modf: %d\n", mv, me, mf); + printf(" delv: %d, dele: %d, delf: %d\n", dv, de, df); + } + + if (totmesh > 0) { + printf(" totmesh: %d\n", totmesh); + } + } + template T *get_elem_from_id(BMesh * /*bm*/, BMID id) { if (id.id < 0 || id.id >= idmap->map_size) { @@ -1668,8 +1712,6 @@ static BMIdMap *entry_get_idmap(BMLogEntry *entry) return entry->idmap; } -} // namespace blender - BMLog *BM_log_from_existing_entries_create(BMesh *bm, BMIdMap *idmap, BMLogEntry *entry) { BMLog *log = BM_log_create(bm, idmap); @@ -1737,7 +1779,7 @@ void BM_log_vert_removed(BMesh *bm, BMLog *log, BMVert *v) log->remove_vert(bm, v); } -void BM_log_vert_before_modified(BMesh *bm, BMLog *log, BMVert *v) +void BM_log_vert_if_modified(BMesh *bm, BMLog *log, BMVert *v) { log->modify_if_vert(bm, v); } @@ -1874,9 +1916,6 @@ void BM_log_set_current_entry(BMLog *log, BMLogEntry *entry) bool BM_log_entry_drop(BMLogEntry *entry) { - float size = entry->calc_size() / 1024.0f / 1024.0f; - printf("%s: Freeing log entry %p, size: %.3fmb\n", __func__, entry, size); - if (entry->prev) { entry->prev->next = entry->next; } @@ -1900,5 +1939,7 @@ bool BM_log_entry_drop(BMLogEntry *entry) void BM_log_print_entry(BMLog * /*log*/, BMLogEntry *entry) { - printf("entry: %p", entry); + printf("BMLogEntry: %p", entry); + entry->print(); + printf("\n"); } diff --git a/source/blender/bmesh/intern/bmesh_log.h b/source/blender/bmesh/intern/bmesh_log.h deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/source/blender/bmesh/intern/bmesh_log.hh b/source/blender/bmesh/intern/bmesh_log.hh new file mode 100644 index 00000000000..05b707d2531 --- /dev/null +++ b/source/blender/bmesh/intern/bmesh_log.hh @@ -0,0 +1,119 @@ +/* SPDX-FileCopyrightText: 2023 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include "BLI_sys_types.h" + +/** \file + * \ingroup bmesh + * + * DynTopo undo backend. Stores removal, addition or customdata changes for verts, + * edges and faces. Elements are assigned unique IDs (which are stored in integer + * attributes) with BMIdMap (see bmesh_idmap.hh). + */ + +struct BMesh; +struct BMEdge; +struct BMElem; +struct BMFace; +struct BMIdMap; +struct BMLog; +struct BMLogEntry; +struct BMVert; +struct CustomData; + +struct BMLogCallbacks { + void (*on_vert_add)(BMVert *v, void *userdata); + void (*on_vert_kill)(BMVert *v, void *userdata); + void (*on_vert_change)(BMVert *v, void *userdata, void *old_customdata); + + void (*on_edge_add)(BMEdge *e, void *userdata); + void (*on_edge_kill)(BMEdge *e, void *userdata); + void (*on_edge_change)(BMEdge *e, void *userdata, void *old_customdata); + + void (*on_face_add)(BMFace *f, void *userdata); + void (*on_face_kill)(BMFace *f, void *userdata); + void (*on_face_change)(BMFace *f, void *userdata, void *old_customdata, char old_hflag); + + void (*on_full_mesh_load)(void *userdata); + void (*on_mesh_customdata_change)(CustomData *domain, char htype, void *userdata); + void *userdata; +}; + +/* Allocate and initialize a new BMLog */ +BMLog *BM_log_create(BMesh *bm, BMIdMap *idmap); + +/* Allocate and initialize a new BMLog using existing BMLogEntries + */ +BMLog *BM_log_from_existing_entries_create(BMesh *bm, BMIdMap *idmap, BMLogEntry *entry); + +/* Does not free the log's entries, just the BMLog itself. */ +bool BM_log_free(BMLog *log); + +/* Start a new log entry and update the log entry list */ +BMLogEntry *BM_log_entry_add(BMesh *bm, BMLog *log); + +/* Start a new log entry and update the log entry list. If combine_with_last + * is true entry will be added as a subentry of the last one. + */ +BMLogEntry *BM_log_entry_add_ex(BMesh *bm, BMLog *log, bool combine_with_last); + +/* Make sure customdata layout is valid. */ +BMLogEntry *BM_log_entry_check_customdata(BMesh *bm, BMLog *log); + +/* Undo one BMLogEntry. */ +void BM_log_undo(BMesh *bm, BMLog *log, BMLogCallbacks *callbacks); +/* Skip one BMLogEntry. */ +void BM_log_undo_skip(BMesh *bm, BMLog *log); + +/* Redo one BMLogEntry */ +void BM_log_redo(BMesh *bm, BMLog *log, BMLogCallbacks *callbacks); +/* Skip one BMLogEntry. */ +void BM_log_redo_skip(BMesh *bm, BMLog *log); + +/* Removes and deallocates a log entry. */ +bool BM_log_entry_drop(BMLogEntry *entry); +void BM_log_set_idmap(BMLog *log, BMIdMap *idmap); + +/* Log a vertex if it hasn't been logged in this undo step yet. */ +void BM_log_vert_if_modified(BMesh *bm, BMLog *log, BMVert *v); +void BM_log_vert_modified(BMesh *bm, BMLog *log, BMVert *v); + +/* Log a new vertex as added to the BMesh + * + * The new vertex gets a unique ID assigned. It is then added to a map + * of added vertices, with the key being its ID and the value + * containing everything needed to reconstruct that vertex. + */ +void BM_log_vert_added(BMesh *bm, BMLog *log, BMVert *v); +/* Log a vertex as removed from the BMesh */ +void BM_log_vert_removed(BMesh *bm, BMLog *log, BMVert *v); + +/* Log a new edge as added to the BMesh */ +void BM_log_edge_added(BMesh *bm, BMLog *log, BMEdge *e); +/* Log an edge's flags and customdata. */ +void BM_log_edge_modified(BMesh *bm, BMLog *log, BMEdge *e); +/* Log an edge as removed from the BMesh */ +void BM_log_edge_removed(BMesh *bm, BMLog *log, BMEdge *e); + +/* Log a face's flags and customdata. */ +void BM_log_face_modified(BMesh *bm, BMLog *log, BMFace *f); +/* Log a face's flags and customdata if it doesn't exist in the log already. */ +void BM_log_face_if_modified(BMesh *bm, BMLog *log, BMFace *f); +/* Log a new face as added to the BMesh. */ +void BM_log_face_added(BMesh *bm, BMLog *log, BMFace *f); +/* Log a face as removed from the BMesh */ +void BM_log_face_removed(BMesh *bm, BMLog *log, BMFace *f); +/* Logs a face as removed without checking if it's already been logged.*/ +void BM_log_face_removed_no_check(BMesh *bm, BMLog *log, BMFace *f); + +/* Log the complete mesh, will be stored as + * a Mesh copy. + */ +void BM_log_full_mesh(BMesh *bm, BMLog *log); + +/* Called from sculpt undo code. */ +void BM_log_print_entry(BMLog *log, BMLogEntry *entry); +int BM_log_entry_size(BMLogEntry *entry); diff --git a/source/blender/bmesh/intern/bmesh_log_intern.h b/source/blender/bmesh/intern/bmesh_log_intern.h deleted file mode 100644 index f2fa00718c2..00000000000 --- a/source/blender/bmesh/intern/bmesh_log_intern.h +++ /dev/null @@ -1,241 +0,0 @@ -/* - * 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 bmesh - */ - -//#define BM_LOG_TRACE -#ifdef BM_LOG_TRACE -# define BMLOG_DEBUG_ARGS , const char *func, int line -# define BMLOG_DEBUG_ARGS_VALUES , func, line -# define BMLOG_DEBUG_ARGS_INVOKE , __func__, __LINE__ -#else -# define BMLOG_DEBUG_ARGS -# define BMLOG_DEBUG_ARGS_VALUES -# define BMLOG_DEBUG_ARGS_INVOKE -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -struct BMFace; -struct BMVert; -struct BMesh; -struct RangeTreeUInt; -struct BMIdMap; -struct CustomData; - -#ifdef __cplusplus -namespace blender { -struct BMLog; -struct BMLogEntry; -} // namespace blender - -using BMLog = blender::BMLog; -using BMLogEntry = blender::BMLogEntry; -#else -typedef struct BMLog BMLog; -typedef struct BMLogEntry BMLogEntry; -#endif - -typedef struct BMLogCallbacks { - void (*on_vert_add)(struct BMVert *v, void *userdata); - void (*on_vert_kill)(struct BMVert *v, void *userdata); - void (*on_vert_change)(struct BMVert *v, void *userdata, void *old_customdata); - - void (*on_edge_add)(struct BMEdge *e, void *userdata); - void (*on_edge_kill)(struct BMEdge *e, void *userdata); - void (*on_edge_change)(struct BMEdge *e, void *userdata, void *old_customdata); - - void (*on_face_add)(struct BMFace *f, void *userdata); - void (*on_face_kill)(struct BMFace *f, void *userdata); - void (*on_face_change)(struct BMFace *f, void *userdata, void *old_customdata, char old_hflag); - - void (*on_full_mesh_load)(void *userdata); - void (*on_mesh_customdata_change)(struct CustomData *domain, char htype, void *userdata); - void *userdata; -} BMLogCallbacks; - -struct BMIdMap; - -/* Allocate and initialize a new BMLog */ -BMLog *BM_log_create(BMesh *bm, struct BMIdMap *idmap); - -/* Allocate and initialize a new BMLog using existing BMLogEntries */ -/* Allocate and initialize a new BMLog using existing BMLogEntries - * - * The 'entry' should be the last entry in the BMLog. Its prev pointer - * will be followed back to find the first entry. - * - * The unused IDs field of the log will be initialized by taking all - * keys from all GHashes in the log entry. - */ -BMLog *BM_log_from_existing_entries_create(BMesh *bm, struct BMIdMap *idmap, BMLogEntry *entry); - -/* Does not free the log's entries, just the BMLog itself. */ -bool BM_log_free(BMLog *log); - -BMLog *BM_log_unfreeze(BMesh *bm, BMLogEntry *entry); - -/* Get the number of log entries */ -/* Get the number of log entries */ -int BM_log_length(const BMLog *log); - -/* Apply a consistent ordering to BMesh vertices and faces */ -/* Apply a consistent ordering to BMesh vertices */ -void BM_log_mesh_elems_reorder(BMesh *bm, BMLog *log); - -/* Start a new log entry and update the log entry list */ -BMLogEntry *BM_log_entry_add(BMesh *bm, BMLog *log); -BMLogEntry *BM_log_entry_add_ex(BMesh *bm, BMLog *log, bool combine_with_last); -BMLogEntry *BM_log_all_ids(BMesh *bm, BMLog *log, BMLogEntry *entry); - -BMLogEntry *BM_log_entry_check_customdata(BMesh *bm, BMLog *log); - -/* Mark all used ids as unused for this node */ -void BM_log_cleanup_entry(BMLogEntry *entry); - -/* Remove an entry from the log. - returns true if the log's refcount - reached zero and was freed*/ -bool BM_log_entry_drop(BMLogEntry *entry); -bool BM_log_is_dead(BMLog *log); - -/* Undo one BMLogEntry. */ -void BM_log_undo(BMesh *bm, BMLog *log, BMLogCallbacks *callbacks); - -/* Redo one BMLogEntry */ -void BM_log_redo(BMesh *bm, BMLog *log, BMLogCallbacks *callbacks); - -/* Log a vertex before it is modified */ -void BM_log_vert_before_modified(BMesh *bm, BMLog *log, BMVert *v); - -void BM_log_vert_modified(BMesh *bm, BMLog *log, BMVert *v); - -/* Log a new vertex as added to the BMesh - * - * The new vertex gets a unique ID assigned. It is then added to a map - * of added vertices, with the key being its ID and the value - * containing everything needed to reconstruct that vertex. - */ -void BM_log_vert_added(BMesh *bm, BMLog *log, struct BMVert *v); - -/* Log a new edge as added to the BMesh */ -void BM_log_edge_added(BMesh *bm, BMLog *log, BMEdge *e); - -void BM_log_edge_modified(BMesh *bm, BMLog *log, BMEdge *e); - -/* Log a face's flags and customdata. */ -void BM_log_face_modified(BMesh *bm, BMLog *log, struct BMFace *f); - -/* Log a face's flags and customdata if it doesn't exist in the log already. */ -void BM_log_face_if_modified(BMesh *bm, BMLog *log, struct BMFace *f); - -/* Log a new face as added to the BMesh */ -/* Log a new face as added to the BMesh - * - * The new face gets a unique ID assigned. It is then added to a map - * of added faces, with the key being its ID and the value containing - * everything needed to reconstruct that face. - */ -void BM_log_face_added(BMesh *bm, BMLog *log, struct BMFace *f); - -/* Log a vertex as removed from the BMesh */ -/* Log a vertex as removed from the BMesh - * - * A couple things can happen here: - * - * If the vertex was added as part of the current log entry, then it's - * deleted and forgotten about entirely. Its unique ID is returned to - * the unused pool. - * - * If the vertex was already part of the BMesh before the current log - * entry, it is added to a map of deleted vertices, with the key being - * its ID and the value containing everything needed to reconstruct - * that vertex. - * - * If there's a move record for the vertex, that's used as the - * vertices original location, then the move record is deleted. - */ -void BM_log_vert_removed(BMesh *bm, BMLog *log, struct BMVert *v); - -/* Log an edge as removed from the BMesh */ -void BM_log_edge_removed(BMesh *bm, BMLog *log, BMEdge *e); - -/* Log a face as removed from the BMesh */ -/* Log a face as removed from the BMesh - * - * A couple things can happen here: - * - * If the face was added as part of the current log entry, then it's - * deleted and forgotten about entirely. Its unique ID is returned to - * the unused pool. - * - * If the face was already part of the BMesh before the current log - * entry, it is added to a map of deleted faces, with the key being - * its ID and the value containing everything needed to reconstruct - * that face. - */ -void BM_log_face_removed(BMesh *bm, BMLog *log, struct BMFace *f); -void BM_log_face_removed_no_check(BMesh *bm, BMLog *log, struct BMFace *f); - -/* Log the complete mesh, will be stored as - * a Mesh copy. - */ -void BM_log_full_mesh(BMesh *bm, BMLog *log); - -/* For internal use only (unit testing) */ -/* For internal use only (unit testing) */ -BMLogEntry *BM_log_current_entry(BMLog *log); -void BM_log_set_current_entry(BMLog *log, BMLogEntry *entry); -BMLogEntry *BM_log_entry_prev(BMLogEntry *entry); -BMLogEntry *BM_log_entry_next(BMLogEntry *entry); - -uint BM_log_vert_id_get(BMesh *bm, BMLog *log, BMVert *v); -BMVert *BM_log_id_vert_get(BMesh *bm, BMLog *log, uint id); -uint BM_log_face_id_get(BMesh *bm, BMLog *log, BMFace *f); -BMFace *BM_log_id_face_get(BMesh *bm, BMLog *log, uint id); - -void BM_log_print_entry(BMLog *log, BMLogEntry *entry); -void BM_log_redo_skip(BMesh *bm, BMLog *log); -void BM_log_undo_skip(BMesh *bm, BMLog *log); -BMVert *BM_log_edge_split_do(BMLog *log, BMEdge *e, BMVert *v, BMEdge **newe, float t); - -void BM_log_set_idmap(BMLog *log, struct BMIdMap *idmap); - -int BM_log_entry_size(BMLogEntry *entry); - -bool BM_log_has_vert(BMLog *log, BMVert *v); -bool BM_log_has_edge(BMLog *log, BMEdge *e); -bool BM_log_has_face(BMLog *log, BMFace *f); - -bool BM_log_has_vert_post(BMLog *log, BMVert *v); -bool BM_log_has_edge_post(BMLog *log, BMEdge *e); -bool BM_log_has_face_post(BMLog *log, BMFace *f); - -bool BM_log_has_vert_pre(BMLog *log, BMVert *v); -bool BM_log_has_edge_pre(BMLog *log, BMEdge *e); -bool BM_log_has_face_pre(BMLog *log, BMFace *f); - -bool BM_log_validate(BMesh *inbm, BMLogEntry *entry, bool is_applied); -bool BM_log_validate_cur(BMLog *log); -#ifdef __cplusplus -} -#endif diff --git a/source/blender/draw/intern/draw_pbvh.cc b/source/blender/draw/intern/draw_pbvh.cc index 91ba6a1a363..8c757eace66 100644 --- a/source/blender/draw/intern/draw_pbvh.cc +++ b/source/blender/draw/intern/draw_pbvh.cc @@ -974,7 +974,7 @@ struct PBVHBatches { fno += l2->f->no; if (i++ > 1000) { - printf("%s: infinite loop error.\n"); + printf("%s: infinite loop error.\n", __func__); break; } diff --git a/source/blender/editors/object/object_remesh.cc b/source/blender/editors/object/object_remesh.cc index f6a6539a4a5..e94ec860977 100644 --- a/source/blender/editors/object/object_remesh.cc +++ b/source/blender/editors/object/object_remesh.cc @@ -75,7 +75,7 @@ #include "object_intern.h" /* own include */ -#include "bmesh_idmap.h" +#include "bmesh_idmap.hh" using blender::float3; using blender::IndexRange; diff --git a/source/blender/editors/sculpt_paint/paint_mask.cc b/source/blender/editors/sculpt_paint/paint_mask.cc index 85ff4be84c1..d2625241c28 100644 --- a/source/blender/editors/sculpt_paint/paint_mask.cc +++ b/source/blender/editors/sculpt_paint/paint_mask.cc @@ -51,7 +51,7 @@ #include "ED_view3d.hh" #include "bmesh.h" -#include "bmesh_idmap.h" +#include "bmesh_idmap.hh" #include "tools/bmesh_boolean.h" #include "paint_intern.hh" diff --git a/source/blender/editors/sculpt_paint/sculpt.cc b/source/blender/editors/sculpt_paint/sculpt.cc index 72f92603ad8..dc31c7fe1fe 100644 --- a/source/blender/editors/sculpt_paint/sculpt.cc +++ b/source/blender/editors/sculpt_paint/sculpt.cc @@ -86,7 +86,7 @@ #include "RNA_define.hh" #include "bmesh.h" -#include "bmesh_idmap.h" +#include "bmesh_idmap.hh" using blender::float3; using blender::IndexRange; diff --git a/source/blender/editors/sculpt_paint/sculpt_api.cc b/source/blender/editors/sculpt_paint/sculpt_api.cc index f2714b13f1b..e70c3d7d837 100644 --- a/source/blender/editors/sculpt_paint/sculpt_api.cc +++ b/source/blender/editors/sculpt_paint/sculpt_api.cc @@ -109,7 +109,7 @@ #include "atomic_ops.h" #include "bmesh.h" -#include "bmesh_log.h" +#include "bmesh_log.hh" #include "bmesh_tools.h" #include "UI_resources.hh" diff --git a/source/blender/editors/sculpt_paint/sculpt_detail.cc b/source/blender/editors/sculpt_paint/sculpt_detail.cc index b4eb5b7f466..e38954f91ba 100644 --- a/source/blender/editors/sculpt_paint/sculpt_detail.cc +++ b/source/blender/editors/sculpt_paint/sculpt_detail.cc @@ -270,7 +270,7 @@ static int sculpt_detail_flood_fill_exec(bContext *C, wmOperator *op) BMVert *v; BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) { - BM_log_vert_before_modified(bm, ss->bm_log, v); + BM_log_vert_if_modified(bm, ss->bm_log, v); } double time = PIL_check_seconds_timer(); @@ -377,7 +377,7 @@ int sculpt_detail_flood_fill_invoke(bContext *C, wmOperator *op, const wmEvent * BMVert *v; BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) { - BM_log_vert_before_modified(bm, ss->bm_log, v); + BM_log_vert_if_modified(bm, ss->bm_log, v); } flood_fill_job.tool_settings = CTX_data_tool_settings(C); diff --git a/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc b/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc index 9a941efd017..52539f03ad6 100644 --- a/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc +++ b/source/blender/editors/sculpt_paint/sculpt_dyntopo.cc @@ -58,8 +58,8 @@ #include "UI_resources.hh" #include "bmesh.h" -#include "bmesh_idmap.h" -#include "bmesh_log.h" +#include "bmesh_idmap.hh" +#include "bmesh_log.hh" #include "bmesh_tools.h" #include diff --git a/source/blender/editors/sculpt_paint/sculpt_face_set.cc b/source/blender/editors/sculpt_paint/sculpt_face_set.cc index 897247faa59..37ecc2cfe71 100644 --- a/source/blender/editors/sculpt_paint/sculpt_face_set.cc +++ b/source/blender/editors/sculpt_paint/sculpt_face_set.cc @@ -58,7 +58,7 @@ #include "RNA_define.hh" #include "bmesh.h" -#include "bmesh_idmap.h" +#include "bmesh_idmap.hh" using blender::Array; using blender::float3; diff --git a/source/blender/editors/sculpt_paint/sculpt_ops.cc b/source/blender/editors/sculpt_paint/sculpt_ops.cc index a6d8fe881c8..2cbcfde5a06 100644 --- a/source/blender/editors/sculpt_paint/sculpt_ops.cc +++ b/source/blender/editors/sculpt_paint/sculpt_ops.cc @@ -82,10 +82,10 @@ #include "GPU_vertex_format.h" #include "bmesh.h" -#include "bmesh_log.h" +#include "bmesh_log.hh" #include "bmesh_tools.h" -#include "bmesh_idmap.h" +#include "bmesh_idmap.hh" #include #include @@ -1618,7 +1618,7 @@ static int sculpt_reveal_all_exec(bContext *C, wmOperator *op) BMVert *v; BM_ITER_MESH (v, &iter, ss->bm, BM_VERTS_OF_MESH) { - BM_log_vert_before_modified(ss->bm, ss->bm_log, v); + BM_log_vert_if_modified(ss->bm, ss->bm_log, v); } BM_ITER_MESH (f, &iter, ss->bm, BM_FACES_OF_MESH) { BM_log_face_modified(ss->bm, ss->bm_log, f); diff --git a/source/blender/editors/sculpt_paint/sculpt_undo.cc b/source/blender/editors/sculpt_paint/sculpt_undo.cc index 4d82267db50..d43e4e5c2c0 100644 --- a/source/blender/editors/sculpt_paint/sculpt_undo.cc +++ b/source/blender/editors/sculpt_paint/sculpt_undo.cc @@ -80,8 +80,8 @@ #include "ED_undo.hh" #include "bmesh.h" -#include "bmesh_idmap.h" -#include "bmesh_log.h" +#include "bmesh_idmap.hh" +#include "bmesh_log.hh" #include "sculpt_intern.hh" using blender::bke::dyntopo::DyntopoSet; -- 2.30.2 From 12edd719ce28e3f7531da830b34b120db60f6489 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Tue, 10 Oct 2023 13:39:52 -0700 Subject: [PATCH 270/279] temp-sculpt-dyntopo: Cleanup BMIdMap some more Use blender::Vector for BMIdMap.map --- .../blenkernel/intern/dyntopo_collapse.cc | 4 +-- .../blender/blenkernel/intern/pbvh_bmesh.cc | 2 +- source/blender/bmesh/intern/bmesh_idmap.cc | 33 +++++-------------- source/blender/bmesh/intern/bmesh_idmap.hh | 6 +--- source/blender/bmesh/intern/bmesh_log.cc | 4 +-- 5 files changed, 15 insertions(+), 34 deletions(-) diff --git a/source/blender/blenkernel/intern/dyntopo_collapse.cc b/source/blender/blenkernel/intern/dyntopo_collapse.cc index d681e1f5fbf..418b8c2685a 100644 --- a/source/blender/blenkernel/intern/dyntopo_collapse.cc +++ b/source/blender/blenkernel/intern/dyntopo_collapse.cc @@ -65,8 +65,8 @@ template static void check_new_elem_id(T *elem, PBVH *pbvh) { int id = BM_ELEM_CD_GET_INT(elem, pbvh->bm_idmap->cd_id_off[int(elem->head.htype)]); if (id != BM_ID_NONE) { - BMElem *existing = id < pbvh->bm_idmap->map_size ? BM_idmap_lookup(pbvh->bm_idmap, id) : - nullptr; + BMElem *existing = id < pbvh->bm_idmap->map.size() ? BM_idmap_lookup(pbvh->bm_idmap, id) : + nullptr; if (existing) { BM_idmap_check_assign(pbvh->bm_idmap, reinterpret_cast(elem)); diff --git a/source/blender/blenkernel/intern/pbvh_bmesh.cc b/source/blender/blenkernel/intern/pbvh_bmesh.cc index 1789c253814..ed8793fc90f 100644 --- a/source/blender/blenkernel/intern/pbvh_bmesh.cc +++ b/source/blender/blenkernel/intern/pbvh_bmesh.cc @@ -843,7 +843,7 @@ static void pbvh_print_mem_size(PBVH *pbvh) int ptrsize = (int)sizeof(void *); - float memsize3[3] = {(float)(ptrsize * pbvh->bm_idmap->map_size) / 1024.0f / 1024.0f, + float memsize3[3] = {(float)(ptrsize * pbvh->bm_idmap->map.size()) / 1024.0f / 1024.0f, (float)(ptrsize * pbvh->bm_idmap->freelist.capacity()) / 1024.0f / 1024.0f, pbvh->bm_idmap->free_idx_map ? (float)(4 * pbvh->bm_idmap->free_idx_map->capacity()) / 1024.0f / diff --git a/source/blender/bmesh/intern/bmesh_idmap.cc b/source/blender/bmesh/intern/bmesh_idmap.cc index cd5a27fa567..ee25799b681 100644 --- a/source/blender/bmesh/intern/bmesh_idmap.cc +++ b/source/blender/bmesh/intern/bmesh_idmap.cc @@ -86,21 +86,9 @@ template static constexpr char get_elem_type() static void idmap_grow_map(BMIdMap *idmap, int newid) { - if (idmap->map_size > newid) { - return; + if (idmap->map.size() <= newid) { + idmap->map.resize(newid + 1); } - - int newsize = (newid + 1); - newsize += newsize >> 1; - - if (idmap->map) { - idmap->map = (BMElem **)MEM_recallocN((void *)idmap->map, sizeof(void *) * newsize); - } - else { - idmap->map = (BMElem **)MEM_calloc_arrayN(newsize, sizeof(void *), "bm idmap"); - } - - idmap->map_size = newsize; } void BM_idmap_clear_attributes_mesh(Mesh *me) @@ -169,19 +157,17 @@ void BM_idmap_check_ids(BMIdMap *idmap) max_id++; - if (idmap->map_size >= max_id) { - memset((void *)idmap->map, 0, sizeof(void *) * idmap->map_size); - } - else { - MEM_SAFE_FREE(idmap->map); - idmap->map_size = max_id + 1; - idmap->map = (BMElem **)MEM_calloc_arrayN(max_id + 1, sizeof(BMElem *), "bm idmap->map"); + if (idmap->map.size() <= max_id) { + idmap->map.resize(max_id); } + /* Zero map. */ + memset(static_cast(idmap->map.data()), 0, sizeof(void *) * idmap->map.size()); + auto check_elem = [&](auto *elem) { int id = BM_ELEM_CD_GET_INT(elem, idmap->cd_id_off[int(elem->head.htype)]); - if (id == BM_ID_NONE || id < 0 || (id < idmap->map_size && idmap->map[id])) { + if (id == BM_ID_NONE || id < 0 || (id < idmap->map.size() && idmap->map[id])) { // printf("%s: Allocating new id for %p(%d): %d\n", __func__, elem, id, max_id); id = max_id++; BM_ELEM_CD_SET_INT(elem, idmap->cd_id_off[int(elem->head.htype)], id); @@ -276,7 +262,6 @@ void BM_idmap_destroy(BMIdMap *idmap) MEM_delete(idmap->free_idx_map); } - MEM_SAFE_FREE(idmap->map); MEM_delete(idmap); } @@ -364,7 +349,7 @@ void BM_idmap_release(BMIdMap *idmap, BMElem *elem, bool clear_id) idmap_log_message("%s: unassigned id!\n", __func__); return; }; - if (id < 0 || id >= idmap->map_size || (idmap->map[id] && idmap->map[id] != elem)) { + if (id < 0 || id >= idmap->map.size() || (idmap->map[id] && idmap->map[id] != elem)) { idmap_log_message("%s: id corruptions\n", __func__); } else { diff --git a/source/blender/bmesh/intern/bmesh_idmap.hh b/source/blender/bmesh/intern/bmesh_idmap.hh index a92227be084..cc724b5a1e0 100644 --- a/source/blender/bmesh/intern/bmesh_idmap.hh +++ b/source/blender/bmesh/intern/bmesh_idmap.hh @@ -5,8 +5,6 @@ #include "bmesh.h" -//#define DEBUG_BM_IDMAP /* Debug ID map. */ - #define BM_ID_NONE 0 struct BMIdMap { @@ -16,9 +14,7 @@ struct BMIdMap { int cd_id_off[15]; BMesh *bm; - BMElem **map; - int map_size; - + blender::Vector map; /* ID -> Element map. */ blender::Vector freelist; using FreeIdxMap = blender::Map; diff --git a/source/blender/bmesh/intern/bmesh_log.cc b/source/blender/bmesh/intern/bmesh_log.cc index 221c8296728..16de202d654 100644 --- a/source/blender/bmesh/intern/bmesh_log.cc +++ b/source/blender/bmesh/intern/bmesh_log.cc @@ -639,7 +639,7 @@ struct BMLogEntry { template T *get_elem_from_id(BMesh * /*bm*/, BMID id) { - if (id.id < 0 || id.id >= idmap->map_size) { + if (id.id < 0 || id.id >= idmap->map.size()) { return nullptr; } @@ -676,7 +676,7 @@ struct BMLogEntry { { int id = _id.id; - if (check_unique && id >= 0 && id < idmap->map_size) { + if (check_unique && id >= 0 && id < idmap->map.size()) { BMElem *old = BM_idmap_lookup(idmap, id); if (old && old != (BMElem *)elem) { -- 2.30.2 From 6626cacd4a8077fc918cf75db2a3de84e16f55d3 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Tue, 10 Oct 2023 13:40:58 -0700 Subject: [PATCH 271/279] temp-sculpt-dyntopo: Remove a few comments --- source/blender/bmesh/intern/bmesh_idmap.cc | 5 ----- source/blender/bmesh/intern/bmesh_log.cc | 3 +-- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/source/blender/bmesh/intern/bmesh_idmap.cc b/source/blender/bmesh/intern/bmesh_idmap.cc index ee25799b681..d61a662e902 100644 --- a/source/blender/bmesh/intern/bmesh_idmap.cc +++ b/source/blender/bmesh/intern/bmesh_idmap.cc @@ -168,7 +168,6 @@ void BM_idmap_check_ids(BMIdMap *idmap) int id = BM_ELEM_CD_GET_INT(elem, idmap->cd_id_off[int(elem->head.htype)]); if (id == BM_ID_NONE || id < 0 || (id < idmap->map.size() && idmap->map[id])) { - // printf("%s: Allocating new id for %p(%d): %d\n", __func__, elem, id, max_id); id = max_id++; BM_ELEM_CD_SET_INT(elem, idmap->cd_id_off[int(elem->head.htype)], id); } @@ -268,14 +267,10 @@ void BM_idmap_destroy(BMIdMap *idmap) static void check_idx_map(BMIdMap *idmap) { if (idmap->free_idx_map && idmap->freelist.size() < FREELIST_HASHMAP_THRESHOLD_LOW) { - // idmap_log_message("%s: Deleting free_idx_map\n", __func__); - MEM_delete(idmap->free_idx_map); idmap->free_idx_map = nullptr; } else if (!idmap->free_idx_map && idmap->freelist.size() < FREELIST_HASHMAP_THRESHOLD_HIGH) { - // idmap_log_message("%s: Adding free_idx_map\n", __func__); - idmap->free_idx_map = MEM_new("BMIdMap::FreeIdxMap"); for (int i : IndexRange(idmap->freelist.size())) { diff --git a/source/blender/bmesh/intern/bmesh_log.cc b/source/blender/bmesh/intern/bmesh_log.cc index 16de202d654..893e01fcd3f 100644 --- a/source/blender/bmesh/intern/bmesh_log.cc +++ b/source/blender/bmesh/intern/bmesh_log.cc @@ -243,8 +243,7 @@ struct BMLogEdge : public BMLogElem { struct BMLogFace : public BMLogElem { Vector, 5> verts; Vector loop_customdata; - // int material_index; - + void free(CustomData *domain, CustomData *loop_domain) { BMLogElem::free(domain); -- 2.30.2 From eaf57b35afbdbb1d2527dfd998dbc1720b5a1f39 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Tue, 10 Oct 2023 13:51:33 -0700 Subject: [PATCH 272/279] temp-sculpt-dyntopo: A bit more cleanup --- source/blender/blenkernel/intern/dyntopo.cc | 4 ---- .../blenkernel/intern/dyntopo_intern.hh | 1 - source/blender/bmesh/intern/bmesh_log.cc | 19 ------------------- 3 files changed, 24 deletions(-) diff --git a/source/blender/blenkernel/intern/dyntopo.cc b/source/blender/blenkernel/intern/dyntopo.cc index 768476ca3fd..74da9ccf806 100644 --- a/source/blender/blenkernel/intern/dyntopo.cc +++ b/source/blender/blenkernel/intern/dyntopo.cc @@ -1652,11 +1652,7 @@ static void edge_queue_create_local(EdgeQueueContext *eq_ctx, static bool cleanup_valence_3_4(EdgeQueueContext *ectx, PBVH *pbvh) { bool modified = false; - - bm_log_message(" == cleanup_valence_3_4 == "); - const int cd_vert_node = pbvh->cd_vert_node_offset; - int updateflag = SCULPTFLAG_NEED_VALENCE; for (BMVert *v : ectx->used_verts) { diff --git a/source/blender/blenkernel/intern/dyntopo_intern.hh b/source/blender/blenkernel/intern/dyntopo_intern.hh index 0777d8cfa45..bb21b779bd1 100644 --- a/source/blender/blenkernel/intern/dyntopo_intern.hh +++ b/source/blender/blenkernel/intern/dyntopo_intern.hh @@ -394,7 +394,6 @@ bool check_vert_fan_are_tris(PBVH *pbvh, BMVert *v); BMVert *pbvh_bmesh_collapse_edge( PBVH *pbvh, BMEdge *e, BMVert *v1, BMVert *v2, struct EdgeQueueContext *eq_ctx); -extern "C" void bm_log_message(const char *fmt, ...); void pbvh_bmesh_vert_remove(PBVH *pbvh, BMVert *v); inline bool bm_edge_tag_test(BMEdge *e) { diff --git a/source/blender/bmesh/intern/bmesh_log.cc b/source/blender/bmesh/intern/bmesh_log.cc index 893e01fcd3f..a662216c19e 100644 --- a/source/blender/bmesh/intern/bmesh_log.cc +++ b/source/blender/bmesh/intern/bmesh_log.cc @@ -57,18 +57,6 @@ using blender::Map; using blender::Set; using blender::Vector; -#define USE_SIMPLE_CD_COPY - -extern "C" void bm_log_message(const char *fmt, ...) -{ - char msg[64]; - - va_list args; - va_start(args, fmt); - vsnprintf(msg, sizeof(msg), fmt, args); - va_end(args); -} - /* Avoid C++ runtime type ids. */ enum BMLogSetType { LOG_SET_DIFF, LOG_SET_FULL }; @@ -542,13 +530,6 @@ struct BMLogEntry { *dest_block = BLI_mempool_calloc(dest->pool); } -#ifdef USE_SIMPLE_CD_COPY - /* Signal simple copy by using bm->XXXdata for dest. */ - if (!cd_layout_changed) { - dest = source; - } -#endif - CustomData_bmesh_copy_data(source, dest, src_block, dest_block); } -- 2.30.2 From 5401f064880b567d3fff0a266ff55c09170019ae Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Tue, 10 Oct 2023 13:54:17 -0700 Subject: [PATCH 273/279] temp-sculpt-dyntopo: Remove some ASAN stuff --- source/blender/bmesh/intern/bmesh_log.cc | 19 ------------------- source/blender/bmesh/intern/bmesh_log.hh | 5 ++++- 2 files changed, 4 insertions(+), 20 deletions(-) diff --git a/source/blender/bmesh/intern/bmesh_log.cc b/source/blender/bmesh/intern/bmesh_log.cc index a662216c19e..27bd3679c17 100644 --- a/source/blender/bmesh/intern/bmesh_log.cc +++ b/source/blender/bmesh/intern/bmesh_log.cc @@ -785,7 +785,6 @@ struct BMLogEntry { void free_logvert(BMLogVert *lv) { if (lv->customdata) { - CustomData_bmesh_unpoison(&vdata, lv->customdata); BLI_mempool_free(vdata.pool, lv->customdata); } @@ -827,7 +826,6 @@ struct BMLogEntry { void free_logedge(BMesh * /*bm*/, BMLogEdge *le) { if (le->customdata) { - CustomData_bmesh_unpoison(&edata, le->customdata); BLI_mempool_free(edata.pool, le->customdata); } @@ -884,13 +882,11 @@ struct BMLogEntry { { if (lf->loop_customdata[0]) { for (int i = 0; i < lf->verts.size(); i++) { - CustomData_bmesh_unpoison(&ldata, lf->loop_customdata[i]); BLI_mempool_free(ldata.pool, lf->loop_customdata[i]); } } if (lf->customdata) { - CustomData_bmesh_unpoison(&pdata, lf->customdata); BLI_mempool_free(pdata.pool, lf->customdata); } @@ -1371,11 +1367,7 @@ void BMLogSetDiff::swap_verts(BMesh *bm, } if (old_customdata) { - CustomData_bmesh_unpoison(&bm->vdata, old_customdata); - CustomData_bmesh_unpoison(&bm->vdata, v->head.data); memcpy(old_customdata, v->head.data, bm->vdata.totsize); - CustomData_bmesh_poison(&bm->vdata, old_customdata); - CustomData_bmesh_poison(&bm->vdata, v->head.data); } entry->swap_logvert(bm, lv->id, v, lv); @@ -1389,7 +1381,6 @@ void BMLogSetDiff::swap_verts(BMesh *bm, } if (old_customdata) { - CustomData_bmesh_unpoison(&bm->vdata, old_customdata); BLI_mempool_free(bm->vdata.pool, old_customdata); } } @@ -1507,11 +1498,7 @@ void BMLogSetDiff::swap_edges(BMesh *bm, } if (old_customdata) { - CustomData_bmesh_unpoison(&bm->edata, old_customdata); - CustomData_bmesh_unpoison(&bm->edata, e->head.data); memcpy(old_customdata, e->head.data, bm->edata.totsize); - CustomData_bmesh_poison(&bm->edata, old_customdata); - CustomData_bmesh_poison(&bm->edata, e->head.data); } entry->swap_logedge(bm, le->id, e, le); @@ -1522,7 +1509,6 @@ void BMLogSetDiff::swap_edges(BMesh *bm, } if (old_customdata) { - CustomData_bmesh_unpoison(&bm->edata, old_customdata); BLI_mempool_free(bm->edata.pool, old_customdata); } } @@ -1620,11 +1606,7 @@ void BMLogSetDiff::swap_faces(BMesh *bm, } if (old_customdata) { - CustomData_bmesh_unpoison(&bm->pdata, old_customdata); - CustomData_bmesh_unpoison(&bm->pdata, f->head.data); memcpy(old_customdata, f->head.data, bm->pdata.totsize); - CustomData_bmesh_poison(&bm->pdata, old_customdata); - CustomData_bmesh_poison(&bm->pdata, f->head.data); } entry->swap_logface(bm, lf->id, f, lf); @@ -1638,7 +1620,6 @@ void BMLogSetDiff::swap_faces(BMesh *bm, } if (old_customdata) { - CustomData_bmesh_unpoison(&bm->pdata, old_customdata); BLI_mempool_free(bm->pdata.pool, old_customdata); } } diff --git a/source/blender/bmesh/intern/bmesh_log.hh b/source/blender/bmesh/intern/bmesh_log.hh index 05b707d2531..8470692c83e 100644 --- a/source/blender/bmesh/intern/bmesh_log.hh +++ b/source/blender/bmesh/intern/bmesh_log.hh @@ -60,7 +60,10 @@ BMLogEntry *BM_log_entry_add(BMesh *bm, BMLog *log); */ BMLogEntry *BM_log_entry_add_ex(BMesh *bm, BMLog *log, bool combine_with_last); -/* Make sure customdata layout is valid. */ +/* Check if customdata layout has changed. If it has a new + * subentry will be pushed so any further logging will have + * the correct customdata. + */ BMLogEntry *BM_log_entry_check_customdata(BMesh *bm, BMLog *log); /* Undo one BMLogEntry. */ -- 2.30.2 From 12a37ed4ef12760ad12ba40ef054b50868c7f747 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Tue, 10 Oct 2023 14:16:33 -0700 Subject: [PATCH 274/279] temp-sculpt-dyntopo: Cleanup bmlog a bit more --- source/blender/blenkernel/intern/dyntopo.cc | 6 +- source/blender/bmesh/intern/bmesh_log.cc | 67 +++---------------- source/blender/bmesh/intern/bmesh_log.hh | 34 ++++++++-- .../editors/sculpt_paint/sculpt_detail.cc | 4 +- .../editors/sculpt_paint/sculpt_undo.cc | 2 +- 5 files changed, 42 insertions(+), 71 deletions(-) diff --git a/source/blender/blenkernel/intern/dyntopo.cc b/source/blender/blenkernel/intern/dyntopo.cc index 74da9ccf806..8f0e16439bc 100644 --- a/source/blender/blenkernel/intern/dyntopo.cc +++ b/source/blender/blenkernel/intern/dyntopo.cc @@ -1390,7 +1390,7 @@ static void unified_edge_queue_create(EdgeQueueContext *eq_ctx, } /* Push a subentry just to be on the safe side w.r.t. element IDs. */ - BM_log_entry_add_ex(pbvh->header.bm, pbvh->bm_log, true); + BM_log_entry_add_delta_set(pbvh->header.bm, pbvh->bm_log); } static void short_edge_queue_task_cb_local(void *__restrict userdata, @@ -2013,7 +2013,7 @@ static bool do_cleanup_3_4(EdgeQueueContext *eq_ctx, PBVH *pbvh) } } - BM_log_entry_add_ex(pbvh->header.bm, pbvh->bm_log, true); + BM_log_entry_add_delta_set(pbvh->header.bm, pbvh->bm_log); pbvh_bmesh_check_nodes(pbvh); @@ -2206,7 +2206,7 @@ void EdgeQueueContext::finish() } /* Push a subentry. */ - BM_log_entry_add_ex(pbvh->header.bm, pbvh->bm_log, true); + BM_log_entry_add_delta_set(pbvh->header.bm, pbvh->bm_log); } template diff --git a/source/blender/bmesh/intern/bmesh_log.cc b/source/blender/bmesh/intern/bmesh_log.cc index 27bd3679c17..cb8d0334014 100644 --- a/source/blender/bmesh/intern/bmesh_log.cc +++ b/source/blender/bmesh/intern/bmesh_log.cc @@ -5,17 +5,6 @@ /** \file * \ingroup bmesh * - * The BMLog is an interface for storing undo/redo steps as a BMesh is - * modified. It only stores changes to the BMesh, not full copies. - * - * Currently it supports the following types of changes: - * - * - Adding and removing vertices - * - Adding and removing edges - * - Adding and removing faces - * - Custom Attributes. - * - Moving vertices - * - Element header flags. */ #include "MEM_guardedalloc.h" @@ -41,10 +30,6 @@ #include "bmesh_idmap.hh" #include "bmesh_log.hh" -extern "C" { -#include "bmesh_structure.h" -} - #include #include #include @@ -231,7 +216,7 @@ struct BMLogEdge : public BMLogElem { struct BMLogFace : public BMLogElem { Vector, 5> verts; Vector loop_customdata; - + void free(CustomData *domain, CustomData *loop_domain) { BMLogElem::free(domain); @@ -260,7 +245,6 @@ struct BMLogSetBase { { return ""; } - virtual void print_info() {} virtual void undo(BMesh * /*bm*/, BMLogCallbacks * /*callbacks*/) {} virtual void redo(BMesh * /*bm*/, BMLogCallbacks * /*callbacks*/) {} }; @@ -285,22 +269,6 @@ struct BMLogSetDiff : public BMLogSetBase { return "Diff"; } - void print_info() override - { - printf(" modified: v: %d e: %d f: %d\n", - int(modified_verts.size()), - int(modified_edges.size()), - int(modified_faces.size())); - printf(" removed: v: %d e: %d f: %d\n", - int(removed_verts.size()), - int(removed_edges.size()), - int(removed_faces.size())); - printf(" added: v: %d e: %d f: %d\n", - int(added_verts.size()), - int(added_edges.size()), - int(added_faces.size())); - } - void add_vert(BMesh *bm, BMVert *v); void remove_vert(BMesh *bm, BMVert *v); void modify_vert(BMesh *bm, BMVert *v); @@ -368,8 +336,6 @@ struct BMLogSetFull : public BMLogSetBase { return "Full"; } - void print_info() {} - void swap(BMesh *bm) { CustomData_MeshMasks cd_mask_extra = {0, 0, 0, 0, 0}; @@ -955,30 +921,14 @@ struct BMLogEntry { void undo(BMesh *bm, BMLogCallbacks *callbacks) { -#ifdef BM_LOG_PRINT_DEBUG - printf("\n"); -#endif - for (int i = sets.size() - 1; i >= 0; i--) { -#ifdef BM_LOG_PRINT_DEBUG - printf("%s: - %d of %d\n", sets[i]->debug_name(), i, (int)(sets.size() - 1)); - sets[i]->print_info(); -#endif sets[i]->undo(bm, callbacks); } } void redo(BMesh *bm, BMLogCallbacks *callbacks) { -#ifdef BM_LOG_PRINT_DEBUG - printf("\n"); -#endif - for (int i = 0; i < sets.size(); i++) { -#ifdef BM_LOG_PRINT_DEBUG - printf("%s: - %d of %d\n", sets[i]->debug_name(), i, (int)(sets.size() - 1)); - sets[i]->print_info(); -#endif sets[i]->redo(bm, callbacks); } } @@ -1711,16 +1661,15 @@ bool BM_log_free(BMLog *log) return true; } -BMLogEntry *BM_log_entry_add_ex(BMesh *bm, BMLog *log, bool combine_with_last) +BMLogEntry *BM_log_entry_add_delta_set(BMesh *bm, BMLog *log) { - if (combine_with_last && log->current_entry) { - log->current_entry->push_set(bm, LOG_SET_DIFF); - } - else { + if (!log->current_entry) { log->push_entry(bm); } + else { + log->current_entry->push_set(bm, LOG_SET_DIFF); + } - log->current_entry->push_set(bm, LOG_SET_DIFF); return log->current_entry; } @@ -1757,7 +1706,7 @@ BMLogEntry *BM_log_entry_check_customdata(BMesh *bm, BMLog *log) if (!entry) { fprintf(stdout, "no current entry; creating...\n"); fflush(stdout); - return BM_log_entry_add_ex(bm, log, true); + return BM_log_entry_add_delta_set(bm, log); } CustomData *cd1[4] = {&bm->vdata, &bm->edata, &bm->ldata, &bm->pdata}; @@ -1769,7 +1718,7 @@ BMLogEntry *BM_log_entry_check_customdata(BMesh *bm, BMLog *log) fflush(stdout); entry->cd_layout_changed = true; - return BM_log_entry_add_ex(bm, log, true); + return BM_log_entry_add_delta_set(bm, log); } } diff --git a/source/blender/bmesh/intern/bmesh_log.hh b/source/blender/bmesh/intern/bmesh_log.hh index 8470692c83e..9a970701462 100644 --- a/source/blender/bmesh/intern/bmesh_log.hh +++ b/source/blender/bmesh/intern/bmesh_log.hh @@ -9,9 +9,30 @@ /** \file * \ingroup bmesh * - * DynTopo undo backend. Stores removal, addition or customdata changes for verts, - * edges and faces. Elements are assigned unique IDs (which are stored in integer - * attributes) with BMIdMap (see bmesh_idmap.hh). + * `BMLog` is the undo system used by DynTopo. Changes + * to a `BMesh` are stored incrementally. + * + * The following operations are supported for logging: + * - Adding and removing vertices + * - Adding and removing edges + * - Adding and removing faces + * - Attribute changes. + * - Element header flags. + * + * Internal details: + * + * Each sculpt undo step owns a pointer to a `BMLogEntry`. + * Every `BMLogEntry` in turn has a list of log sets. + * + * A log set is a subclass of `BMLogSetBase` and can be + * either a delta set (`BMLogSetDiff`) or a full mesh + * (BMLogSetFull). + * + * Particuarly complex mesh operations can sometimes benefit from + * having a clean `BMLogSetDiff` set, this helps avoid corrupting + * element IDs. This can be done with `BM_log_entry_add_delta_set`. + * + * To log a complete mesh, use `BM_log_full_mesh`. */ struct BMesh; @@ -55,10 +76,11 @@ bool BM_log_free(BMLog *log); /* Start a new log entry and update the log entry list */ BMLogEntry *BM_log_entry_add(BMesh *bm, BMLog *log); -/* Start a new log entry and update the log entry list. If combine_with_last - * is true entry will be added as a subentry of the last one. +/* + * Add a new delta set to the current log entry. If no entry + * exists one will be created. Returns current log entry. */ -BMLogEntry *BM_log_entry_add_ex(BMesh *bm, BMLog *log, bool combine_with_last); +BMLogEntry *BM_log_entry_add_delta_set(BMesh *bm, BMLog *log); /* Check if customdata layout has changed. If it has a new * subentry will be pushed so any further logging will have diff --git a/source/blender/editors/sculpt_paint/sculpt_detail.cc b/source/blender/editors/sculpt_paint/sculpt_detail.cc index e38954f91ba..b8965347099 100644 --- a/source/blender/editors/sculpt_paint/sculpt_detail.cc +++ b/source/blender/editors/sculpt_paint/sculpt_detail.cc @@ -205,7 +205,7 @@ static int sculpt_detail_flood_fill_run(Scene *scene, remesher.start(); float quality = ss->cached_dyntopo.quality; - float time_limit = (min_ff(1.0f * (100.0f - quality + 2500.0f * quality), 2500.0f))*0.001f; + float time_limit = (min_ff(1.0f * (100.0f - quality + 2500.0f * quality), 2500.0f)) * 0.001f; float quality_time = PIL_check_seconds_timer(); printf("Remesh\n"); @@ -232,7 +232,7 @@ static int sculpt_detail_flood_fill_run(Scene *scene, remesher.finish(); /* Push a new subentry. */ - BM_log_entry_add_ex(ss->bm, ss->bm_log, true); + BM_log_entry_add_delta_set(ss->bm, ss->bm_log); blender::bke::dyntopo::after_stroke(ss->pbvh, true); unlock_main_thread(); diff --git a/source/blender/editors/sculpt_paint/sculpt_undo.cc b/source/blender/editors/sculpt_paint/sculpt_undo.cc index d43e4e5c2c0..ea70f9bc7ea 100644 --- a/source/blender/editors/sculpt_paint/sculpt_undo.cc +++ b/source/blender/editors/sculpt_paint/sculpt_undo.cc @@ -2310,7 +2310,7 @@ bool SCULPT_ensure_dyntopo_node_undo(Object *ob, unode->typemask |= 1 << type; /* add a log sub-entry */ - BM_log_entry_add_ex(ss->bm, ss->bm_log, true); + BM_log_entry_add_delta_set(ss->bm, ss->bm_log); } if (!node) { -- 2.30.2 From a590a0bfd695556d9228548b892340bcdd307a44 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Tue, 10 Oct 2023 14:22:28 -0700 Subject: [PATCH 275/279] temp-sculpt-dyntopo: Add file comment to BM_idmap.hh --- source/blender/bmesh/intern/bmesh_idmap.hh | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/source/blender/bmesh/intern/bmesh_idmap.hh b/source/blender/bmesh/intern/bmesh_idmap.hh index cc724b5a1e0..cf83eb5ced8 100644 --- a/source/blender/bmesh/intern/bmesh_idmap.hh +++ b/source/blender/bmesh/intern/bmesh_idmap.hh @@ -1,4 +1,19 @@ -#include "BLI_compiler_compat.h" +/* SPDX-FileCopyrightText: 2023 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include "BLI_sys_types.h" + +/** \file + * \ingroup bmesh + * + * A simple self-contained element ID library. + * Stores IDs in integer attributes. + */ + + #include "BLI_compiler_compat.h" #include "BLI_map.hh" #include "BLI_sys_types.h" #include "BLI_vector.hh" -- 2.30.2 From dcac6b827ef5ee9b444a2a553d957b7864f0bd2a Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Tue, 10 Oct 2023 15:05:02 -0700 Subject: [PATCH 276/279] temp-sculpt-dyntopo: Use template deduction instead of BMElem for BMIDMap --- source/blender/blenkernel/intern/dyntopo.cc | 32 +++++++------- .../blenkernel/intern/dyntopo_collapse.cc | 20 ++++----- .../blender/blenkernel/intern/pbvh_bmesh.cc | 6 +-- source/blender/bmesh/intern/bmesh_idmap.cc | 43 +++++++++++++++---- source/blender/bmesh/intern/bmesh_idmap.hh | 16 +++---- source/blender/bmesh/intern/bmesh_log.cc | 32 +++++++------- source/blender/editors/sculpt_paint/sculpt.cc | 12 +++--- 7 files changed, 94 insertions(+), 67 deletions(-) diff --git a/source/blender/blenkernel/intern/dyntopo.cc b/source/blender/blenkernel/intern/dyntopo.cc index 8f0e16439bc..a33888e5c80 100644 --- a/source/blender/blenkernel/intern/dyntopo.cc +++ b/source/blender/blenkernel/intern/dyntopo.cc @@ -837,7 +837,7 @@ bool check_face_is_tri(PBVH *pbvh, BMFace *f) // BKE_pbvh_bmesh_remove_face(pbvh, f, true); pbvh_bmesh_face_remove(pbvh, f, false, true, true); BM_log_face_removed(pbvh->header.bm, pbvh->bm_log, f); - BM_idmap_release(pbvh->bm_idmap, (BMElem *)f, true); + BM_idmap_release(pbvh->bm_idmap, f, true); int len = (f->len - 2) * 3; @@ -888,7 +888,7 @@ bool check_face_is_tri(PBVH *pbvh, BMFace *f) dyntopo_add_flag(pbvh, l->v, SCULPTFLAG_NEED_VALENCE); } while ((l = l->next) != f2->l_first); - BM_idmap_release(pbvh->bm_idmap, (BMElem *)dbl->link, true); + BM_idmap_release(pbvh->bm_idmap, static_cast(dbl->link), true); BM_face_kill(pbvh->header.bm, f2); MEM_freeN(dbl); @@ -1110,7 +1110,7 @@ bool destroy_nonmanifold_fins(PBVH *pbvh, BMEdge *e_root) } pbvh_bmesh_face_remove(pbvh, f, true, true, false); - BM_idmap_release(pbvh->bm_idmap, (BMElem *)f, true); + BM_idmap_release(pbvh->bm_idmap, f, true); BM_face_kill(pbvh->header.bm, f); } @@ -1119,7 +1119,7 @@ bool destroy_nonmanifold_fins(PBVH *pbvh, BMEdge *e_root) if (!e->l) { BM_log_edge_removed(pbvh->header.bm, pbvh->bm_log, e); - BM_idmap_release(pbvh->bm_idmap, (BMElem *)e, true); + BM_idmap_release(pbvh->bm_idmap, e, true); BM_edge_kill(pbvh->header.bm, e); } else { @@ -1140,7 +1140,7 @@ bool destroy_nonmanifold_fins(PBVH *pbvh, BMEdge *e_root) pbvh_bmesh_vert_remove(pbvh, v); BM_log_vert_removed(pbvh->header.bm, pbvh->bm_log, v); - BM_idmap_release(pbvh->bm_idmap, (BMElem *)v, true); + BM_idmap_release(pbvh->bm_idmap, v, true); BM_vert_kill(pbvh->header.bm, v); } else { @@ -1852,7 +1852,7 @@ static bool cleanup_valence_3_4(EdgeQueueContext *ectx, PBVH *pbvh) if (!BM_edge_exists(ls[0]->v, ls[2]->v)) { BMEdge *e_diag = BM_edge_create( pbvh->header.bm, ls[0]->v, ls[2]->v, nullptr, BM_CREATE_NOP); - BM_idmap_check_assign(pbvh->bm_idmap, reinterpret_cast(e_diag)); + BM_idmap_check_assign(pbvh->bm_idmap, e_diag); BM_log_edge_added(pbvh->header.bm, pbvh->bm_log, e_diag); } } @@ -1877,7 +1877,7 @@ static bool cleanup_valence_3_4(EdgeQueueContext *ectx, PBVH *pbvh) if (ok1) { f1 = pbvh_bmesh_face_create(pbvh, n, vs, nullptr, l->f, true, false); - BM_idmap_check_assign(pbvh->bm_idmap, reinterpret_cast(f1)); + BM_idmap_check_assign(pbvh->bm_idmap, f1); normal_tri_v3( f1->no, f1->l_first->v->co, f1->l_first->next->v->co, f1->l_first->prev->v->co); @@ -1912,7 +1912,7 @@ static bool cleanup_valence_3_4(EdgeQueueContext *ectx, PBVH *pbvh) } f2 = pbvh_bmesh_face_create(pbvh, n, vs, nullptr, example, true, false); - BM_idmap_check_assign(pbvh->bm_idmap, reinterpret_cast(f2)); + BM_idmap_check_assign(pbvh->bm_idmap, f2); bmesh_swap_data_simple(&pbvh->header.bm->ldata, &f2->l_first->prev->head.data, @@ -2604,7 +2604,7 @@ void EdgeQueueContext::split_edge(BMEdge *e) do { if (BM_ELEM_CD_GET_INT(l->f, pbvh->cd_face_node_offset) != DYNTOPO_NODE_NONE) { pbvh_bmesh_face_remove(pbvh, l->f, true, true, true); - BM_idmap_release(pbvh->bm_idmap, reinterpret_cast(l->f), true); + BM_idmap_release(pbvh->bm_idmap, l->f, true); fs.append(l->f); } @@ -2619,7 +2619,7 @@ void EdgeQueueContext::split_edge(BMEdge *e) } while ((l = l->radial_next) != e->l); BM_log_edge_removed(bm, pbvh->bm_log, e); - BM_idmap_release(pbvh->bm_idmap, reinterpret_cast(e), true); + BM_idmap_release(pbvh->bm_idmap, e, true); StrokeID stroke_id1 = blender::bke::paint::vertex_attr_get( {reinterpret_cast(e->v1)}, ss->attrs.stroke_id); @@ -2686,13 +2686,13 @@ void EdgeQueueContext::split_edge(BMEdge *e) BM_ELEM_CD_SET_INT(newv, pbvh->cd_vert_node_offset, DYNTOPO_NODE_NONE); - BM_idmap_check_assign(pbvh->bm_idmap, reinterpret_cast(newv)); + BM_idmap_check_assign(pbvh->bm_idmap, newv); BM_log_vert_added(bm, pbvh->bm_log, newv); - BM_idmap_check_assign(pbvh->bm_idmap, reinterpret_cast(e)); + BM_idmap_check_assign(pbvh->bm_idmap, e); BM_log_edge_added(bm, pbvh->bm_log, e); - BM_idmap_check_assign(pbvh->bm_idmap, reinterpret_cast(newe)); + BM_idmap_check_assign(pbvh->bm_idmap, newe); BM_log_edge_added(bm, pbvh->bm_log, newe); dyntopo_add_flag(pbvh, newv, SCULPTFLAG_NEED_VALENCE | SCULPTFLAG_NEED_TRIANGULATE); @@ -2729,7 +2729,7 @@ void EdgeQueueContext::split_edge(BMEdge *e) pbvh_boundary_update_bmesh(pbvh, l->next->next->next->e); BM_ELEM_CD_SET_INT(f, pbvh->cd_face_node_offset, DYNTOPO_NODE_NONE); - BM_idmap_check_assign(pbvh->bm_idmap, reinterpret_cast(f)); + BM_idmap_check_assign(pbvh->bm_idmap, f); BKE_pbvh_bmesh_add_face(pbvh, f, true, false); if (!newf || newf == f) { @@ -2737,11 +2737,11 @@ void EdgeQueueContext::split_edge(BMEdge *e) } BM_ELEM_CD_SET_INT(newf, pbvh->cd_face_node_offset, DYNTOPO_NODE_NONE); - BM_idmap_check_assign(pbvh->bm_idmap, reinterpret_cast(f)); + BM_idmap_check_assign(pbvh->bm_idmap, f); BKE_pbvh_bmesh_add_face(pbvh, newf, true, false); if (!exist_e) { - BM_idmap_check_assign(pbvh->bm_idmap, reinterpret_cast(newl->e)); + BM_idmap_check_assign(pbvh->bm_idmap, newl->e); BM_log_edge_added(bm, pbvh->bm_log, newl->e); } diff --git a/source/blender/blenkernel/intern/dyntopo_collapse.cc b/source/blender/blenkernel/intern/dyntopo_collapse.cc index 418b8c2685a..bb0e844afc4 100644 --- a/source/blender/blenkernel/intern/dyntopo_collapse.cc +++ b/source/blender/blenkernel/intern/dyntopo_collapse.cc @@ -65,22 +65,22 @@ template static void check_new_elem_id(T *elem, PBVH *pbvh) { int id = BM_ELEM_CD_GET_INT(elem, pbvh->bm_idmap->cd_id_off[int(elem->head.htype)]); if (id != BM_ID_NONE) { - BMElem *existing = id < pbvh->bm_idmap->map.size() ? BM_idmap_lookup(pbvh->bm_idmap, id) : - nullptr; + T *existing = id < pbvh->bm_idmap->map.size() ? BM_idmap_lookup(pbvh->bm_idmap, id) : + nullptr; if (existing) { - BM_idmap_check_assign(pbvh->bm_idmap, reinterpret_cast(elem)); + BM_idmap_check_assign(pbvh->bm_idmap, elem); BM_idmap_release(pbvh->bm_idmap, existing, true); } - BM_idmap_assign(pbvh->bm_idmap, reinterpret_cast(elem), id); + BM_idmap_assign(pbvh->bm_idmap, elem, id); if (existing) { BM_idmap_check_assign(pbvh->bm_idmap, existing); } } else { - BM_idmap_check_assign(pbvh->bm_idmap, reinterpret_cast(elem)); + BM_idmap_check_assign(pbvh->bm_idmap, elem); } } @@ -435,7 +435,7 @@ class DyntopoCollapseCallbacks { { BM_log_vert_removed(bm, pbvh->bm_log, v); pbvh_bmesh_vert_remove(pbvh, v); - BM_idmap_release(pbvh->bm_idmap, reinterpret_cast(v), false); + BM_idmap_release(pbvh->bm_idmap, v, false); } inline void on_edge_kill(BMEdge *e) { @@ -443,13 +443,13 @@ class DyntopoCollapseCallbacks { dyntopo_add_flag(pbvh, e->v2, SCULPTFLAG_NEED_VALENCE); BM_log_edge_removed(bm, pbvh->bm_log, e); - BM_idmap_release(pbvh->bm_idmap, reinterpret_cast(e), false); + BM_idmap_release(pbvh->bm_idmap, e, false); } inline void on_face_kill(BMFace *f) { BM_log_face_removed(bm, pbvh->bm_log, f); pbvh_bmesh_face_remove(pbvh, f, false, true, true); - BM_idmap_release(pbvh->bm_idmap, reinterpret_cast(f), false); + BM_idmap_release(pbvh->bm_idmap, f, false); } inline void on_vert_create(BMVert *v) @@ -678,7 +678,7 @@ BMVert *EdgeQueueContext::collapse_edge(PBVH *pbvh, BMEdge *e, BMVert *v1, BMVer BMVert *v2 = BM_edge_other_vert(e2, v_conn); BM_log_edge_removed(pbvh->header.bm, pbvh->bm_log, e2); - BM_idmap_release(pbvh->bm_idmap, reinterpret_cast(e2), true); + BM_idmap_release(pbvh->bm_idmap, e2, true); BM_edge_kill(pbvh->header.bm, e2); dyntopo_add_flag(pbvh, v2, SCULPTFLAG_NEED_VALENCE | SCULPTFLAG_NEED_TRIANGULATE); @@ -693,7 +693,7 @@ BMVert *EdgeQueueContext::collapse_edge(PBVH *pbvh, BMEdge *e, BMVert *v1, BMVer } BM_log_vert_removed(pbvh->header.bm, pbvh->bm_log, v_conn); - BM_idmap_release(pbvh->bm_idmap, reinterpret_cast(v_conn), true); + BM_idmap_release(pbvh->bm_idmap, v_conn, true); BM_vert_kill(pbvh->header.bm, v_conn); return nullptr; diff --git a/source/blender/blenkernel/intern/pbvh_bmesh.cc b/source/blender/blenkernel/intern/pbvh_bmesh.cc index ed8793fc90f..be8b75932b7 100644 --- a/source/blender/blenkernel/intern/pbvh_bmesh.cc +++ b/source/blender/blenkernel/intern/pbvh_bmesh.cc @@ -305,12 +305,12 @@ void pbvh_kill_vert(PBVH *pbvh, BMVert *v, bool log_vert, bool log_edges) do { int id = BM_idmap_get_id(pbvh->bm_idmap, reinterpret_cast(l->f)); if (id != BM_ID_NONE) { - BM_idmap_release(pbvh->bm_idmap, (BMElem *)l->f, true); + BM_idmap_release(pbvh->bm_idmap, l->f, true); } } while ((l = l->radial_next) != e->l); } - BM_idmap_release(pbvh->bm_idmap, (BMElem *)e, true); + BM_idmap_release(pbvh->bm_idmap, e, true); } while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e); } @@ -318,7 +318,7 @@ void pbvh_kill_vert(PBVH *pbvh, BMVert *v, bool log_vert, bool log_edges) BM_log_vert_removed(pbvh->header.bm, pbvh->bm_log, v); } - BM_idmap_release(pbvh->bm_idmap, (BMElem *)v, true); + BM_idmap_release(pbvh->bm_idmap, v, true); BM_vert_kill(pbvh->header.bm, v); } diff --git a/source/blender/bmesh/intern/bmesh_idmap.cc b/source/blender/bmesh/intern/bmesh_idmap.cc index d61a662e902..6c98281579c 100644 --- a/source/blender/bmesh/intern/bmesh_idmap.cc +++ b/source/blender/bmesh/intern/bmesh_idmap.cc @@ -279,7 +279,7 @@ static void check_idx_map(BMIdMap *idmap) } } -int BM_idmap_alloc(BMIdMap *idmap, BMElem *elem) +template int BM_idmap_alloc(BMIdMap *idmap, T *elem) { int id = BM_ID_NONE; @@ -302,14 +302,14 @@ int BM_idmap_alloc(BMIdMap *idmap, BMElem *elem) } idmap_grow_map(idmap, id); - idmap->map[id] = elem; + idmap->map[id] = reinterpret_cast(elem); BM_ELEM_CD_SET_INT(elem, idmap->cd_id_off[int(elem->head.htype)], id); return id; } -void BM_idmap_assign(BMIdMap *idmap, BMElem *elem, int id) +template void BM_idmap_assign(BMIdMap *idmap, T *elem, int id) { /* Remove id from freelist. */ if (idmap->free_idx_map) { @@ -331,12 +331,12 @@ void BM_idmap_assign(BMIdMap *idmap, BMElem *elem, int id) BM_ELEM_CD_SET_INT(elem, idmap->cd_id_off[int(elem->head.htype)], id); idmap_grow_map(idmap, id); - idmap->map[id] = elem; + idmap->map[id] = reinterpret_cast(elem); check_idx_map(idmap); } -void BM_idmap_release(BMIdMap *idmap, BMElem *elem, bool clear_id) +template void BM_idmap_release(BMIdMap *idmap, T *elem, bool clear_id) { int id = BM_ELEM_CD_GET_INT(elem, idmap->cd_id_off[int(elem->head.htype)]); @@ -344,7 +344,9 @@ void BM_idmap_release(BMIdMap *idmap, BMElem *elem, bool clear_id) idmap_log_message("%s: unassigned id!\n", __func__); return; }; - if (id < 0 || id >= idmap->map.size() || (idmap->map[id] && idmap->map[id] != elem)) { + if (id < 0 || id >= idmap->map.size() || + (idmap->map[id] && idmap->map[id] != reinterpret_cast(elem))) + { idmap_log_message("%s: id corruptions\n", __func__); } else { @@ -364,13 +366,38 @@ void BM_idmap_release(BMIdMap *idmap, BMElem *elem, bool clear_id) } } -int BM_idmap_check_assign(BMIdMap *idmap, BMElem *elem) +template int BM_idmap_check_assign(BMIdMap *idmap, T *elem) { int id = BM_ELEM_CD_GET_INT(elem, idmap->cd_id_off[int(elem->head.htype)]); if (id == BM_ID_NONE) { - id = BM_idmap_alloc(idmap, elem); + id = BM_idmap_alloc(idmap, (BMElem *)elem); } return id; } + +/* Instantiate templates. */ +template void BM_idmap_assign(BMIdMap *idmap, BMElem *elem, int id); +template void BM_idmap_assign(BMIdMap *idmap, BMVert *elem, int id); +template void BM_idmap_assign(BMIdMap *idmap, BMEdge *elem, int id); +template void BM_idmap_assign(BMIdMap *idmap, BMLoop *elem, int id); +template void BM_idmap_assign(BMIdMap *idmap, BMFace *elem, int id); + +template int BM_idmap_check_assign(BMIdMap *idmap, BMElem *elem); +template int BM_idmap_check_assign(BMIdMap *idmap, BMVert *elem); +template int BM_idmap_check_assign(BMIdMap *idmap, BMEdge *elem); +template int BM_idmap_check_assign(BMIdMap *idmap, BMLoop *elem); +template int BM_idmap_check_assign(BMIdMap *idmap, BMFace *elem); + +template int BM_idmap_alloc(BMIdMap *idmap, BMElem *elem); +template int BM_idmap_alloc(BMIdMap *idmap, BMVert *elem); +template int BM_idmap_alloc(BMIdMap *idmap, BMEdge *elem); +template int BM_idmap_alloc(BMIdMap *idmap, BMLoop *elem); +template int BM_idmap_alloc(BMIdMap *idmap, BMFace *elem); + +template void BM_idmap_release(BMIdMap *idmap, BMElem *elem, bool clear_id); +template void BM_idmap_release(BMIdMap *idmap, BMVert *elem, bool clear_id); +template void BM_idmap_release(BMIdMap *idmap, BMEdge *elem, bool clear_id); +template void BM_idmap_release(BMIdMap *idmap, BMLoop *elem, bool clear_id); +template void BM_idmap_release(BMIdMap *idmap, BMFace *elem, bool clear_id); diff --git a/source/blender/bmesh/intern/bmesh_idmap.hh b/source/blender/bmesh/intern/bmesh_idmap.hh index cf83eb5ced8..b8a5e6e1373 100644 --- a/source/blender/bmesh/intern/bmesh_idmap.hh +++ b/source/blender/bmesh/intern/bmesh_idmap.hh @@ -13,7 +13,7 @@ * Stores IDs in integer attributes. */ - #include "BLI_compiler_compat.h" +#include "BLI_compiler_compat.h" #include "BLI_map.hh" #include "BLI_sys_types.h" #include "BLI_vector.hh" @@ -50,20 +50,20 @@ bool BM_idmap_check_attributes(BMIdMap *idmap); void BM_idmap_check_ids(BMIdMap *idmap); /* Explicitly assign an ID. id cannot be BM_ID_NONE (zero). */ -void BM_idmap_assign(BMIdMap *idmap, BMElem *elem, int id); +template void BM_idmap_assign(BMIdMap *idmap, T *elem, int id); /* Automatically allocate an ID. */ -int BM_idmap_alloc(BMIdMap *idmap, BMElem *elem); +template int BM_idmap_alloc(BMIdMap *idmap, T *elem); /* Checks if an element needs an ID (it's id is BM_ID_NONE), * and if so allocates one. */ -int BM_idmap_check_assign(BMIdMap *idmap, BMElem *elem); +template int BM_idmap_check_assign(BMIdMap *idmap, T *elem); /* Release an ID; if clear_id is true the id attribute for * that element will be set to BM_ID_NONE. */ -void BM_idmap_release(BMIdMap *idmap, BMElem *elem, bool clear_id); +template void BM_idmap_release(BMIdMap *idmap, T *elem, bool clear_id); const char *BM_idmap_attr_name_get(int htype); @@ -72,13 +72,13 @@ void BM_idmap_clear_attributes(BMesh *bm); void BM_idmap_clear_attributes_mesh(Mesh *me); /* Elem -> ID. */ -BLI_INLINE int BM_idmap_get_id(BMIdMap *map, BMElem *elem) +template BLI_INLINE int BM_idmap_get_id(BMIdMap *map, T *elem) { return BM_ELEM_CD_GET_INT(elem, map->cd_id_off[(int)elem->head.htype]); } /* ID -> elem. */ -BLI_INLINE BMElem *BM_idmap_lookup(BMIdMap *map, int elem) +template BLI_INLINE T *BM_idmap_lookup(BMIdMap *map, int elem) { - return elem >= 0 ? map->map[elem] : NULL; + return elem >= 0 ? reinterpret_cast(map->map[elem]) : NULL; } diff --git a/source/blender/bmesh/intern/bmesh_log.cc b/source/blender/bmesh/intern/bmesh_log.cc index cb8d0334014..2be3d4655a2 100644 --- a/source/blender/bmesh/intern/bmesh_log.cc +++ b/source/blender/bmesh/intern/bmesh_log.cc @@ -52,7 +52,7 @@ template struct BMID { T *lookup(BMIdMap *idmap) { - return reinterpret_cast(BM_idmap_lookup(idmap, id)); + return BM_idmap_lookup(idmap, id); } uint64_t hash() const @@ -589,7 +589,7 @@ struct BMLogEntry { return nullptr; } - T *elem = reinterpret_cast(BM_idmap_lookup(idmap, id.id)); + T *elem = BM_idmap_lookup(idmap, id.id); char htype = 0; if (!elem) { @@ -623,9 +623,9 @@ struct BMLogEntry { int id = _id.id; if (check_unique && id >= 0 && id < idmap->map.size()) { - BMElem *old = BM_idmap_lookup(idmap, id); + T *old = BM_idmap_lookup(idmap, id); - if (old && old != (BMElem *)elem) { + if (old && old != elem) { printf( "id conflict in BMLogEntry::assign_elem_id; elem %p (a %s) is being reassinged to id " "%d.\n", @@ -635,18 +635,18 @@ struct BMLogEntry { printf( " elem %p (a %s) will get a new id\n", old, get_elem_htype_str((int)old->head.htype)); - BM_idmap_assign(idmap, reinterpret_cast(elem), id); + BM_idmap_assign(idmap, elem, id); return; } } - BM_idmap_assign(idmap, reinterpret_cast(elem), id); + BM_idmap_assign(idmap, elem, id); } template BMID get_elem_id(BMesh * /*bm*/, T *elem) { - BM_idmap_check_assign(idmap, reinterpret_cast(elem)); - return BM_idmap_get_id(idmap, reinterpret_cast(elem)); + BM_idmap_check_assign(idmap, elem); + return BM_idmap_get_id(idmap, elem); } void push_set(BMesh *bm, BMLogSetType type) @@ -1247,7 +1247,7 @@ void BMLogSetDiff::modify_edge(BMesh *bm, BMEdge *e) void BMLogSetDiff::add_face(BMesh *bm, BMFace *f) { - BM_idmap_check_assign(entry->idmap, (BMElem *)f); + BM_idmap_check_assign(entry->idmap, f); BMID id = entry->get_elem_id(bm, f); @@ -1373,7 +1373,7 @@ void BMLogSetDiff::remove_verts(BMesh *bm, callbacks->on_vert_kill(v, callbacks->userdata); } - BM_idmap_release(entry->idmap, (BMElem *)v, false); + BM_idmap_release(entry->idmap, v, false); BM_vert_kill(bm, v); } @@ -1428,7 +1428,7 @@ void BMLogSetDiff::remove_edges(BMesh *bm, callbacks->on_edge_kill(e, callbacks->userdata); } - BM_idmap_release(entry->idmap, reinterpret_cast(e), true); + BM_idmap_release(entry->idmap, e, true); BM_edge_kill(bm, e); } } @@ -1531,7 +1531,7 @@ void BMLogSetDiff::remove_faces(BMesh *bm, callbacks->on_face_kill(f, callbacks->userdata); } - BM_idmap_release(entry->idmap, reinterpret_cast(f), true); + BM_idmap_release(entry->idmap, f, true); BM_face_kill(bm, f); } @@ -1766,22 +1766,22 @@ void BM_log_full_mesh(BMesh *bm, BMLog *log) BMVert *BM_log_id_vert_get(BMesh * /*bm*/, BMLog *log, uint id) { - return reinterpret_cast(BM_idmap_lookup(log->idmap, id)); + return BM_idmap_lookup(log->idmap, id); } uint BM_log_vert_id_get(BMesh * /*bm*/, BMLog *log, BMVert *v) { - return BM_idmap_get_id(log->idmap, reinterpret_cast(v)); + return BM_idmap_get_id(log->idmap, v); } BMFace *BM_log_id_face_get(BMesh * /*bm*/, BMLog *log, uint id) { - return reinterpret_cast(BM_idmap_lookup(log->idmap, id)); + return BM_idmap_lookup(log->idmap, id); } uint BM_log_face_id_get(BMesh * /*bm*/, BMLog *log, BMFace *f) { - return BM_idmap_get_id(log->idmap, reinterpret_cast(f)); + return BM_idmap_get_id(log->idmap, f); } int BM_log_entry_size(BMLogEntry *entry) diff --git a/source/blender/editors/sculpt_paint/sculpt.cc b/source/blender/editors/sculpt_paint/sculpt.cc index dc31c7fe1fe..3c0740ce8f2 100644 --- a/source/blender/editors/sculpt_paint/sculpt.cc +++ b/source/blender/editors/sculpt_paint/sculpt.cc @@ -3931,13 +3931,13 @@ static void sculpt_topology_update( int actv = BM_ID_NONE, actf = BM_ID_NONE; if (ss->active_vertex.i != PBVH_REF_NONE) { - BM_idmap_check_assign(ss->bm_idmap, (BMElem *)ss->active_vertex.i); - actv = BM_idmap_get_id(ss->bm_idmap, (BMElem *)ss->active_vertex.i); + BM_idmap_check_assign(ss->bm_idmap, (BMVert *)ss->active_vertex.i); + actv = BM_idmap_get_id(ss->bm_idmap, (BMVert *)ss->active_vertex.i); } if (ss->active_face.i != PBVH_REF_NONE) { - BM_idmap_check_assign(ss->bm_idmap, (BMElem *)ss->active_face.i); - actf = BM_idmap_get_id(ss->bm_idmap, (BMElem *)ss->active_face.i); + BM_idmap_check_assign(ss->bm_idmap, (BMFace *)ss->active_face.i); + actf = BM_idmap_get_id(ss->bm_idmap, (BMFace *)ss->active_face.i); } blender::bke::dyntopo::BrushSphere sphere_tester(ss->cache->location, @@ -3964,7 +3964,7 @@ static void sculpt_topology_update( SCULPT_dyntopo_automasking_end(mask_cb_data); if (actv != BM_ID_NONE) { - BMVert *v = (BMVert *)BM_idmap_lookup(ss->bm_idmap, actv); + BMVert *v = BM_idmap_lookup(ss->bm_idmap, actv); if (v && v->head.htype == BM_VERT) { ss->active_vertex.i = (intptr_t)v; @@ -3975,7 +3975,7 @@ static void sculpt_topology_update( } if (actf != BM_ID_NONE) { - BMFace *f = (BMFace *)BM_idmap_lookup(ss->bm_idmap, actf); + BMFace *f = BM_idmap_lookup(ss->bm_idmap, actf); if (f && f->head.htype == BM_FACE) { ss->active_face.i = (intptr_t)f; -- 2.30.2 From 7d7d5d3c9bdd4be41ef526325d89512c558400cc Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Fri, 13 Oct 2023 12:30:41 -0700 Subject: [PATCH 277/279] temp-sculpt-dyntopo: Collapse callbacks are function pointers again Decided against the template approach. --- .../blenkernel/intern/dyntopo_collapse.cc | 174 ++-- source/blender/bmesh/intern/bmesh_collapse.cc | 764 ++++++++++++++++- source/blender/bmesh/intern/bmesh_collapse.hh | 788 +----------------- 3 files changed, 877 insertions(+), 849 deletions(-) diff --git a/source/blender/blenkernel/intern/dyntopo_collapse.cc b/source/blender/blenkernel/intern/dyntopo_collapse.cc index bb0e844afc4..4c4cf29af24 100644 --- a/source/blender/blenkernel/intern/dyntopo_collapse.cc +++ b/source/blender/blenkernel/intern/dyntopo_collapse.cc @@ -424,88 +424,118 @@ bool pbvh_bmesh_collapse_edge_uvs( return snap; } -class DyntopoCollapseCallbacks { - PBVH *pbvh; - BMesh *bm; +static void on_vert_kill(void *customdata, BMVert *v) +{ + PBVH *pbvh = static_cast(customdata); + BMesh *bm = pbvh->header.bm; - public: - DyntopoCollapseCallbacks(PBVH *pbvh_) : pbvh(pbvh_), bm(pbvh_->header.bm) {} + BM_log_vert_removed(bm, pbvh->bm_log, v); + pbvh_bmesh_vert_remove(pbvh, v); + BM_idmap_release(pbvh->bm_idmap, v, false); +} - inline void on_vert_kill(BMVert *v) - { - BM_log_vert_removed(bm, pbvh->bm_log, v); - pbvh_bmesh_vert_remove(pbvh, v); - BM_idmap_release(pbvh->bm_idmap, v, false); - } - inline void on_edge_kill(BMEdge *e) - { - dyntopo_add_flag(pbvh, e->v1, SCULPTFLAG_NEED_VALENCE); - dyntopo_add_flag(pbvh, e->v2, SCULPTFLAG_NEED_VALENCE); +static void on_edge_kill(void *customdata, BMEdge *e) +{ + PBVH *pbvh = static_cast(customdata); + BMesh *bm = pbvh->header.bm; + dyntopo_add_flag(pbvh, e->v1, SCULPTFLAG_NEED_VALENCE); + dyntopo_add_flag(pbvh, e->v2, SCULPTFLAG_NEED_VALENCE); - BM_log_edge_removed(bm, pbvh->bm_log, e); - BM_idmap_release(pbvh->bm_idmap, e, false); - } - inline void on_face_kill(BMFace *f) - { - BM_log_face_removed(bm, pbvh->bm_log, f); - pbvh_bmesh_face_remove(pbvh, f, false, true, true); - BM_idmap_release(pbvh->bm_idmap, f, false); - } + BM_log_edge_removed(bm, pbvh->bm_log, e); + BM_idmap_release(pbvh->bm_idmap, e, false); +} - inline void on_vert_create(BMVert *v) - { - check_new_elem_id(v, pbvh); - pbvh_boundary_update_bmesh(pbvh, v); - dyntopo_add_flag(pbvh, v, SCULPTFLAG_NEED_VALENCE); - BM_log_vert_added(bm, pbvh->bm_log, v); - } +static void on_face_kill(void *customdata, BMFace *f) +{ + PBVH *pbvh = static_cast(customdata); + BMesh *bm = pbvh->header.bm; + BM_log_face_removed(bm, pbvh->bm_log, f); + pbvh_bmesh_face_remove(pbvh, f, false, true, true); + BM_idmap_release(pbvh->bm_idmap, f, false); +} - inline void on_vert_combine(BMVert *dest, BMVert *source) - { - /* Combine boundary flags. */ - int boundflag = BM_ELEM_CD_GET_INT(source, pbvh->cd_boundary_flag); - BM_ELEM_CD_SET_INT(dest, pbvh->cd_boundary_flag, boundflag); +static void on_vert_create(void *customdata, BMVert *v) +{ + PBVH *pbvh = static_cast(customdata); + BMesh *bm = pbvh->header.bm; + check_new_elem_id(v, pbvh); + pbvh_boundary_update_bmesh(pbvh, v); + dyntopo_add_flag(pbvh, v, SCULPTFLAG_NEED_VALENCE); + BM_log_vert_added(bm, pbvh->bm_log, v); +} - dyntopo_add_flag(pbvh, dest, SCULPTFLAG_NEED_VALENCE); - } +static void on_edge_create(void *customdata, BMEdge *e) +{ + PBVH *pbvh = static_cast(customdata); + BMesh *bm = pbvh->header.bm; + dyntopo_add_flag(pbvh, e->v1, SCULPTFLAG_NEED_VALENCE); + dyntopo_add_flag(pbvh, e->v2, SCULPTFLAG_NEED_VALENCE); - inline void on_edge_combine(BMEdge *dest, BMEdge *source) - { - dyntopo_add_flag(pbvh, dest->v1, SCULPTFLAG_NEED_VALENCE); - dyntopo_add_flag(pbvh, dest->v2, SCULPTFLAG_NEED_VALENCE); + check_new_elem_id(e, pbvh); + pbvh_boundary_update_bmesh(pbvh, e); + BM_log_edge_added(bm, pbvh->bm_log, e); +} - /* Combine boundary flags. */ - int boundflag = BM_ELEM_CD_GET_INT(source, pbvh->cd_edge_boundary); - BM_ELEM_CD_SET_INT(dest, pbvh->cd_edge_boundary, boundflag); +static void on_face_create(void *customdata, BMFace *f) +{ + PBVH *pbvh = static_cast(customdata); + BMesh *bm = pbvh->header.bm; + check_new_elem_id(f, pbvh); + BM_log_face_added(bm, pbvh->bm_log, f); + BM_ELEM_CD_SET_INT(f, pbvh->cd_face_node_offset, DYNTOPO_NODE_NONE); + BKE_pbvh_bmesh_add_face(pbvh, f, false, false); - pbvh_boundary_update_bmesh(pbvh, dest->v1); - pbvh_boundary_update_bmesh(pbvh, dest->v2); - } + BMLoop *l = f->l_first; + do { + dyntopo_add_flag(pbvh, l->v, SCULPTFLAG_NEED_VALENCE); + pbvh_boundary_update_bmesh(pbvh, l->v); + pbvh_boundary_update_bmesh(pbvh, l->e); + } while ((l = l->next) != f->l_first); +} - inline void on_edge_create(BMEdge *e) - { - dyntopo_add_flag(pbvh, e->v1, SCULPTFLAG_NEED_VALENCE); - dyntopo_add_flag(pbvh, e->v2, SCULPTFLAG_NEED_VALENCE); +static void on_vert_combine(void *customdata, BMVert *dest, BMVert *source) +{ + PBVH *pbvh = static_cast(customdata); + BMesh *bm = pbvh->header.bm; + /* Combine boundary flags. */ + int boundflag = BM_ELEM_CD_GET_INT(source, pbvh->cd_boundary_flag); + BM_ELEM_CD_SET_INT(dest, pbvh->cd_boundary_flag, boundflag); - check_new_elem_id(e, pbvh); - pbvh_boundary_update_bmesh(pbvh, e); - BM_log_edge_added(bm, pbvh->bm_log, e); - } - inline void on_face_create(BMFace *f) - { - check_new_elem_id(f, pbvh); - BM_log_face_added(bm, pbvh->bm_log, f); - BM_ELEM_CD_SET_INT(f, pbvh->cd_face_node_offset, DYNTOPO_NODE_NONE); - BKE_pbvh_bmesh_add_face(pbvh, f, false, false); + dyntopo_add_flag(pbvh, dest, SCULPTFLAG_NEED_VALENCE); +} - BMLoop *l = f->l_first; - do { - dyntopo_add_flag(pbvh, l->v, SCULPTFLAG_NEED_VALENCE); - pbvh_boundary_update_bmesh(pbvh, l->v); - pbvh_boundary_update_bmesh(pbvh, l->e); - } while ((l = l->next) != f->l_first); - } -}; +static void on_edge_combine(void *customdata, BMEdge *dest, BMEdge *source) +{ + PBVH *pbvh = static_cast(customdata); + BMesh *bm = pbvh->header.bm; + dyntopo_add_flag(pbvh, dest->v1, SCULPTFLAG_NEED_VALENCE); + dyntopo_add_flag(pbvh, dest->v2, SCULPTFLAG_NEED_VALENCE); + + /* Combine boundary flags. */ + int boundflag = BM_ELEM_CD_GET_INT(source, pbvh->cd_edge_boundary); + BM_ELEM_CD_SET_INT(dest, pbvh->cd_edge_boundary, boundflag); + + pbvh_boundary_update_bmesh(pbvh, dest->v1); + pbvh_boundary_update_bmesh(pbvh, dest->v2); +} + +void dyntopo_do_collapse(PBVH *pbvh, BMEdge *e, BMVert *v_del) +{ + + blender::bmesh::CollapseCallbacks cb; + + cb.customdata = static_cast(pbvh); + cb.on_vert_kill = on_vert_kill; + cb.on_edge_kill = on_edge_kill; + cb.on_face_kill = on_face_kill; + cb.on_vert_create = on_vert_create; + cb.on_edge_create = on_edge_create; + cb.on_face_create = on_face_create; + cb.on_vert_combine = on_vert_combine; + cb.on_edge_combine = on_edge_combine; + + blender::bmesh::join_vert_kill_edge(pbvh->header.bm, e, v_del, true, &cb); +} /* * This function is rather complicated. It has to @@ -596,8 +626,6 @@ BMVert *EdgeQueueContext::collapse_edge(PBVH *pbvh, BMEdge *e, BMVert *v1, BMVer return nullptr; } - DyntopoCollapseCallbacks callbacks(pbvh); - /* Make sure original data is initialized before we snap it. */ BKE_pbvh_bmesh_check_origdata(ss, v_conn, pbvh->stroke_id); BKE_pbvh_bmesh_check_origdata(ss, v_del, pbvh->stroke_id); @@ -613,7 +641,7 @@ BMVert *EdgeQueueContext::collapse_edge(PBVH *pbvh, BMEdge *e, BMVert *v1, BMVer } /* Full non-manifold collapse. */ - blender::bmesh::join_vert_kill_edge(pbvh->header.bm, e, v_del, true, callbacks); + dyntopo_do_collapse(pbvh, e, v_del); if (BM_elem_is_free((BMElem *)v_conn, BM_VERT)) { printf("v_conn was freed\n"); diff --git a/source/blender/bmesh/intern/bmesh_collapse.cc b/source/blender/bmesh/intern/bmesh_collapse.cc index 8404cc2ffef..1339a3ec9ee 100644 --- a/source/blender/bmesh/intern/bmesh_collapse.cc +++ b/source/blender/bmesh/intern/bmesh_collapse.cc @@ -1,7 +1,765 @@ +/* SPDX-FileCopyrightText: + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup bmesh + */ + +#pragma once + +#include +#include +#include +#include + +#include "MEM_guardedalloc.h" + +#include "DNA_meshdata_types.h" + +#include "BLI_compiler_attrs.h" +#include "BLI_compiler_compat.h" +#include "BLI_function_ref.hh" +#include "BLI_map.hh" +#include "BLI_math_vector_types.hh" +#include "BLI_set.hh" +#include "BLI_span.hh" +#include "BLI_utildefines.h" +#include "BLI_vector.hh" + +using blender::float2; +using blender::float3; +using blender::IndexRange; +using blender::Map; +using blender::MutableSpan; +using blender::Set; +using blender::Span; +using blender::Vector; + +#include +#include +#include +#include + +#include "bmesh.h" #include "bmesh_collapse.hh" #include "bmesh_private.h" +#include "bmesh_structure.h" -using blender::bmesh::NullCollapseCallbacks; +void bmesh_disk_edge_append(BMEdge *e, BMVert *v); +void bmesh_radial_loop_append(BMEdge *e, BMLoop *l); +void bm_kill_only_edge(BMesh *bm, BMEdge *e); +void bm_kill_only_loop(BMesh *bm, BMLoop *l); +void bm_kill_only_face(BMesh *bm, BMFace *f); + +//#define JVKE_DEBUG + +namespace blender::bmesh { +#ifdef JVKE_DEBUG + +static void bm_local_obj_free(char *str, char *fixed) +{ + if (str != fixed) { + MEM_freeN(str); + } +} + +# define LOCAL_OBJ_SIZE 512 + +static char *obj_append_line(const char *line, char *str, char *fixed, int *size, int *i) +{ + int len = (int)strlen(line); + + if (*i + len + 1 >= *size) { + *size += len + ((*size) >> 1); + + if (str == fixed) { + str = static_cast(MEM_mallocN(*size, "buf")); + memcpy(static_cast(str), fixed, LOCAL_OBJ_SIZE); + } + else { + str = static_cast(MEM_reallocN(str, *size)); + } + } + + memcpy(str + *i, line, len); + str[*i + len] = 0; + + *i += len; + + return str; +} + +/* NotForPr: saves an obj of the neighborhood around an edge prior to collapse + * into a buffer that can be read from a debugger. + */ +static char *bm_save_local_obj_text( + BMesh *, int depth, char buf[LOCAL_OBJ_SIZE], const char *fmt, ...) +{ + va_list vl; + va_start(vl, fmt); + + buf[0] = 0; + + Vector vs; + Vector initial_vs; + Vector es; + Vector initial_es; + Vector fs; + Vector initial_fs; + + Set visit; + + const char *c = fmt; + while (*c) { + if (*c == ' ' || *c == '\t') { + c++; + continue; + } + + void *ptr = va_arg(vl, void *); + + switch (*c) { + case 'v': + vs.append(static_cast(ptr)); + initial_vs.append(static_cast(ptr)); + break; + case 'e': + es.append(static_cast(ptr)); + initial_es.append(static_cast(ptr)); + break; + case 'f': + fs.append(static_cast(ptr)); + initial_fs.append(static_cast(ptr)); + break; + } + + c++; + } + + va_end(vl); + + int tag = 4; + for (BMFace *f : fs) { + BMLoop *l = f->l_first; + + do { + l->v->head.api_flag &= ~tag; + l->e->head.api_flag &= ~tag; + } while ((l = l->next) != f->l_first); + } + + for (BMEdge *e : es) { + e->v1->head.api_flag &= ~tag; + e->v2->head.api_flag &= ~tag; + } + + for (BMVert *v : vs) { + v->head.api_flag |= tag; + } + + for (BMEdge *e : es) { + if (!(e->v1->head.api_flag & tag)) { + vs.append(e->v1); + e->v1->head.api_flag |= tag; + } + + if (!(e->v2->head.api_flag & tag)) { + vs.append(e->v2); + e->v2->head.api_flag |= tag; + } + + e->head.api_flag |= tag; + } + + for (BMFace *f : fs) { + BMLoop *l = f->l_first; + + do { + if (!(l->v->head.api_flag & tag)) { + vs.append(l->v); + l->v->head.api_flag |= tag; + } + + if (!(l->e->head.api_flag & tag)) { + es.append(l->e); + l->e->head.api_flag |= tag; + } + } while ((l = l->next) != f->l_first); + } + + struct StackItem { + BMVert *v; + int depth; + }; + + Vector stack; + Set elemset; + + for (BMVert *v : vs) { + elemset.add(static_cast(v)); + } + for (BMEdge *e : es) { + elemset.add(static_cast(e)); + } + for (BMFace *f : fs) { + elemset.add(static_cast(f)); + } + + stack.clear(); + stack.append({vs[0], 0}); + while (stack.size() > 0) { + StackItem item = stack.pop_last(); + BMVert *v = item.v; + int startdepth = item.depth; + + if (elemset.add(static_cast(v))) { + vs.append(v); + } + + if (!v->e || item.depth > depth) { + continue; + } + + BMEdge *e = v->e; + do { + if (visit.add(static_cast(e))) { + stack.append({e->v1, startdepth + 1}); + stack.append({e->v2, startdepth + 1}); + } + + if (!e->l) { + continue; + } + + BMLoop *l = e->l; + do { + if (visit.add(static_cast(l->f))) { + if (elemset.add(static_cast(l->f))) { + fs.append(l->f); + } + + BMLoop *l2 = l; + do { + if (visit.add(static_cast(l->v))) { + stack.append({l->v, startdepth + 1}); + } + } while ((l2 = l2->next) != l); + } + } while ((l = l->radial_next) != e->l); + } while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e); + } + + char *str = buf; + int size = LOCAL_OBJ_SIZE - 1; + int stri = 0; + + char line[128]; + line[0] = 0; + + for (BMVert *v : vs) { + v->head.api_flag &= ~tag; + } + for (BMEdge *e : es) { + e->head.api_flag &= ~tag; + } + + for (BMFace *f : fs) { + f->head.api_flag &= ~tag; + } + for (BMVert *v : initial_vs) { + v->head.api_flag |= tag; + } + + for (BMEdge *e : initial_es) { + e->head.api_flag |= tag; + e->v1->head.api_flag |= tag; + e->v2->head.api_flag |= tag; + } + + for (BMFace *f : initial_fs) { + f->head.api_flag |= tag; + BMLoop *l = f->l_first; + + do { + l->v->head.api_flag |= tag; + } while ((l = l->next) != f->l_first); + } + + for (int i : vs.index_range()) { + BMVert *v = vs[i]; + + if (v->head.api_flag & tag) { + sprintf(line, "#select\n"); + str = obj_append_line(line, str, buf, &size, &stri); + } + + v->head.index = i + 1; + sprintf(line, "v %.4f %.4f %.4f\n", v->co[0], v->co[1], v->co[2]); + + str = obj_append_line(line, str, buf, &size, &stri); + } + + /* save wire edges */ + for (BMEdge *e : es) { + if (e->l) { + continue; + } + + sprintf(line, "l %d %d\n", e->v1->head.index, e->v2->head.index); + str = obj_append_line(line, str, buf, &size, &stri); + } + + for (BMFace *f : fs) { + BMLoop *l = f->l_first; + + sprintf(line, "f"); + str = obj_append_line(line, str, buf, &size, &stri); + + do { + sprintf(line, " %d", l->v->head.index); + + str = obj_append_line(line, str, buf, &size, &stri); + } while ((l = l->next) != f->l_first); + + str = obj_append_line("\n", str, buf, &size, &stri); + } + + return str; +} + +static void check_mesh_radial(BMesh *bm) +{ + return; + + BMIter iter; + BMEdge *e; + BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) { + int count = 0; + BMLoop *l = e->l; + + if (!l) { + continue; + } + + do { + if (BM_elem_is_free((BMElem *)l, BM_LOOP)) { + printf("Freed loop in edge %p radial cycle\n", e); + } + + if (count++ > 100) { + printf("Corrupted radial cycle for %p\n", e); + break; + } + } while ((l = l->radial_next) != e->l); + } +} + +static void trigger_jvke_error(int err, char *obj_text) +{ + printf("========= ERROR %s============\n\n%s\n\n", bm_get_error_str(err), obj_text); +} + +int bmesh_elem_check_all(void *elem, char htype); + +extern char *_last_local_obj; + +# define JVKE_CHECK_ELEMENT(elem) \ + { \ + int err = 0; \ + if ((err = bmesh_elem_check_all(elem, (elem)->head.htype))) { \ + trigger_jvke_error(err, saved_obj); \ + } \ + } +#else +# define JVKE_CHECK_ELEMENT(elem) +#endif + +static bool cleanup_vert(BMesh *bm, BMVert *v, CollapseCallbacks *callbacks) +{ + BMEdge *e = v->e; + + if (!e->l || e->l->f == e->l->radial_next->f) { + return false; + } + + BMFace *f_example = nullptr; + + if (callbacks && callbacks->on_face_kill) { + BMIter iter; + BMFace *f; + BM_ITER_ELEM (f, &iter, v, BM_FACES_OF_VERT) { + callbacks->on_face_kill(callbacks->customdata, f); + } + } + + do { + BMLoop *l = e->l; + if (!l) { + continue; + } + + if (callbacks && callbacks->on_edge_kill) { + callbacks->on_edge_kill(callbacks->customdata, e); + } + + if (!f_example) { + f_example = l->f; + } + } while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e); + + BMVert *v1 = BM_edge_other_vert(v->e, v); + BMVert *v2 = BM_edge_other_vert(BM_DISK_EDGE_NEXT(v->e, v), v); + BMVert *v3 = BM_edge_other_vert(BM_DISK_EDGE_NEXT(BM_DISK_EDGE_NEXT(v->e, v), v), v); + + BMFace *f = BM_face_create_quad_tri(bm, v1, v2, v3, nullptr, f_example, BM_CREATE_NOP); + BMLoop *l = f->l_first; + + if (callbacks && callbacks->on_vert_kill) { + callbacks->on_vert_kill(callbacks->customdata, v); + } + BM_vert_kill(bm, v); + + /* Ensure correct winding. */ + do { + if (l->radial_next != l && l->radial_next->v == l->v) { + BM_face_normal_flip(bm, f); + break; + } + } while ((l = l->next) != f->l_first); + + l = f->l_first; + do { + if (l != l->radial_next) { + BMLoop *l2 = l->radial_next; + if (l2->v != l->v) { + l2 = l2->next; + } + + CustomData_bmesh_copy_data(&bm->ldata, &bm->ldata, l2->head.data, &l->head.data); + } + } while ((l = l->next) != f->l_first); + + if (callbacks && callbacks->on_face_create) { + callbacks->on_face_create(callbacks->customdata, f); + } + + return true; +} + +static void bmesh_kernel_check_val3_vert(BMesh *bm, BMEdge *e, CollapseCallbacks *callbacks) +{ + if (!e->l) { + return; + } + + bool stop; + + do { + stop = true; + + BMLoop *l = e->l; + + if (!l) { + break; + } + + do { + BMLoop *l2 = l->prev; + + if (l2 == l2->radial_next || !l2->v->e) { + continue; + } + + bool bad = false; + int count = 0; + + BMEdge *e2 = l2->v->e; + do { + if (!e2->l || e2->l == e2->l->radial_next || e2->l->radial_next->radial_next != e2->l) { + bad = true; + break; + } + + bad = bad || e2->l->f->len != 3 || e2->l->radial_next->f->len != 3; + count++; + } while ((e2 = BM_DISK_EDGE_NEXT(e2, l2->v)) != l2->v->e); + + bad = bad || count != 3; + + if (!bad) { + if (cleanup_vert(bm, l2->v, callbacks)) { + stop = false; + break; + } + } + } while ((l = l->radial_next) != e->l); + } while (!stop); +} + +/** + * \brief Join Vert Kill Edge (JVKE) + * + * Collapse an edge, merging surrounding data. + * + * Unlike #BM_vert_collapse_edge & #bmesh_kernel_join_edge_kill_vert + * which only handle 2 valence verts, + * this can handle any number of connected edges/faces. + * + *

+ * Before: -> After:
+ * +-+-+-+    +-+-+-+
+ * | | | |    | \ / |
+ * +-+-+-+    +--+--+
+ * | | | |    | / \ |
+ * +-+-+-+    +-+-+-+
+ * 
+ */ + +BMVert *join_vert_kill_edge( + BMesh *bm, BMEdge *e, BMVert *v_del, const bool do_del, CollapseCallbacks *callbacks) +{ + BMVert *v_conn = BM_edge_other_vert(e, v_del); + +#ifdef JVKE_DEBUG + char buf[LOCAL_OBJ_SIZE]; + + bool have_boundary = false; + + if (_last_local_obj) { + MEM_freeN(static_cast(_last_local_obj)); + } + + char *saved_obj = bm_save_local_obj_text(bm, 2, buf, "e", e); + + _last_local_obj = static_cast(MEM_mallocN(strlen(saved_obj) + 1, "_last_local_obj")); + BLI_strncpy(_last_local_obj, saved_obj, strlen(saved_obj) + 1); +#endif + + /* Destroy any valence-3 verts that might turn into non-manifold "fins." */ + bmesh_kernel_check_val3_vert(bm, e, callbacks); + + Set es; + Set fs; + + const int dup_tag = _FLAG_OVERLAP; + + if (callbacks && callbacks->on_vert_combine) { + callbacks->on_vert_combine(callbacks->customdata, v_conn, v_del); + } + + for (int i = 0; i < 2; i++) { + BMVert *v = i ? v_del : v_conn; + + BMEdge *e = v->e; + do { + es.add(e); + + BMLoop *l = e->l; + if (!l) { + continue; + } + + do { + fs.add(l->f); + BMLoop *l2 = l; + do { + es.add(l2->e); + BMLoop *l3 = l2; + do { + fs.add(l3->f); + } while ((l3 = l3->radial_next) != l2); + } while ((l2 = l2->next) != l); + } while ((l = l->radial_next) != e->l); + } while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e); + } + + /* Inform callbacks we've "killed" all the faces. */ + for (BMFace *f : fs) { + if (callbacks && callbacks->on_face_kill) { + callbacks->on_face_kill(callbacks->customdata, f); + } + } + + /* Unlink loops. */ + for (BMFace *f : fs) { + BMLoop *l = f->l_first; + do { + BMEdge *e = l->e; + bmesh_radial_loop_remove(l->e, l); + l->e = e; + } while ((l = l->next) != f->l_first); + } + + /* Swap edges. */ + for (BMEdge *e : es) { + if (e->v1 != v_del && e->v2 != v_del) { + continue; + } + + if (e->v1 == v_conn || e->v2 == v_conn) { + if (e->l) { + printf("%s: ErROR!\n", __func__); + } + + if (callbacks && callbacks->on_edge_kill) { + callbacks->on_edge_kill(callbacks->customdata, e); + } + BM_edge_kill(bm, e); + continue; + } + + BMVert *otherv = e->v1 == v_del ? e->v2 : e->v1; + + BMEdge *exist = BM_edge_exists(otherv, v_conn); + + if (exist) { + if (e->l) { + printf("%s: ERROR!\n", __func__); + } + + if (callbacks && callbacks->on_edge_combine) { + callbacks->on_edge_combine(callbacks->customdata, exist, e); + } + + /* Combine edge flags. The sharp flag is inverted + * so we can't just OR it. + */ + bool sharp = !(exist->head.hflag & BM_ELEM_SMOOTH) || !(e->head.hflag & BM_ELEM_SMOOTH); + + exist->head.hflag |= e->head.hflag; + + if (sharp) { + exist->head.hflag &= ~BM_ELEM_SMOOTH; + } + + if (callbacks && callbacks->on_edge_kill) { + callbacks->on_edge_kill(callbacks->customdata, e); + } + + BM_edge_kill(bm, e); + } + else { + if (callbacks->on_edge_kill) { + callbacks->on_edge_kill(callbacks->customdata, e); + } + bmesh_disk_vert_replace(e, v_conn, v_del); + if (callbacks && callbacks->on_edge_create) { + callbacks->on_edge_create(callbacks->customdata, e); + } + } + } + + auto remove_loop = [&bm](BMFace *f, BMLoop *l) { + l->next->prev = l->prev; + l->prev->next = l->next; + if (l == f->l_first) { + f->l_first = l->next; + } + + f->len--; + bm_kill_only_loop(bm, l); + }; + + /* Swap loops */ + for (BMFace *f : fs) { + BMLoop *l = f->l_first; + BMLoop *lnext; + bool found = false; + + /* Swap v_del and remove duplicate v_conn's. */ + do { + lnext = l->next; + + if (l->v == v_del) { + l->v = v_conn; + } + if (l->v == v_conn) { + if (found) { + remove_loop(f, l); + } + else { + found = true; + } + } + } while ((l = lnext) != f->l_first); + + /* Remove any remaining duplicate verts. */ + do { + lnext = l->next; + if (l->v == l->next->v) { + remove_loop(f, l); + } + } while ((l = lnext) != f->l_first); + } + + Vector finalfs; + + /* Relink faces. */ + for (BMFace *f : fs) { + if (f->len < 3) { + BMLoop *l = f->l_first; + BMLoop *lnext; + do { + lnext = l->next; + bm_kill_only_loop(bm, l); + } while ((l = lnext) != f->l_first); + + bm_kill_only_face(bm, f); + continue; + } + + BMLoop *l = f->l_first; + do { + BMEdge *exist_e = BM_edge_exists(l->v, l->next->v); + if (!exist_e) { + exist_e = BM_edge_create(bm, l->v, l->next->v, nullptr, BM_CREATE_NOP); + if (callbacks && callbacks->on_edge_create) { + callbacks->on_edge_create(callbacks->customdata, exist_e); + } + } + + l->e = exist_e; + bmesh_radial_loop_append(l->e, l); + + BM_ELEM_API_FLAG_DISABLE(l->v, dup_tag); + BM_ELEM_API_FLAG_DISABLE(l->e, dup_tag); + BM_ELEM_API_FLAG_DISABLE(l->f, dup_tag); + } while ((l = l->next) != f->l_first); + + if (callbacks && callbacks->on_face_create) { + callbacks->on_face_create(callbacks->customdata, f); + } + finalfs.append(f); + } + + for (BMFace *f : finalfs) { + if (BM_elem_is_free((BMElem *)f, BM_FACE)) { + continue; + } + + BMFace *f2; + while ((f2 = BM_face_find_double(f))) { + printf("%s: removing duplicate face.\n", __func__); + if (callbacks && callbacks->on_face_kill) { + callbacks->on_face_kill(callbacks->customdata, f2); + } + BM_face_kill(bm, f2); + } + } + + JVKE_CHECK_ELEMENT(v_conn); + + if (do_del && !v_del->e) { + if (callbacks && callbacks->on_vert_kill) { + callbacks->on_vert_kill(callbacks->customdata, v_del); + } + BM_vert_kill(bm, v_del); + } + + return v_conn; +} +} // namespace blender::bmesh namespace blender::bmesh { @@ -30,7 +788,5 @@ extern "C" BMVert *bmesh_kernel_join_vert_kill_edge(BMesh *bm, BMVert *v_kill, const bool do_del) { - NullCollapseCallbacks callbacks; - return blender::bmesh::join_vert_kill_edge( - bm, e, v_kill, do_del, callbacks); + return blender::bmesh::join_vert_kill_edge(bm, e, v_kill, do_del, nullptr); } diff --git a/source/blender/bmesh/intern/bmesh_collapse.hh b/source/blender/bmesh/intern/bmesh_collapse.hh index 26f7ed92511..c6a4f903d2d 100644 --- a/source/blender/bmesh/intern/bmesh_collapse.hh +++ b/source/blender/bmesh/intern/bmesh_collapse.hh @@ -1,779 +1,23 @@ -/* SPDX-FileCopyrightText: 2023 Blender Authors - * - * SPDX-License-Identifier: GPL-2.0-or-later */ +#include "BLI_function_ref.hh" -/** \file - * \ingroup bmesh - * - * Edge collapse. - * - * This is an implementation of non-manifold edge - * collapse. It exists because profiling consistently showed - * CPU cache misses were a major problem for DynTopo. - * DynTopo's edge split and collapse functions used to work - * by deleting and re-creating geometry which made things - * much worse. - * - * Things were made more complicated by DynTopo's undo (BMLog), - * which works by saving and restoring geometry that has to be logged - * prior to operations. There are two ways to do this for collapse: - * either log the faces, edges and verts surrounding an edge prior - * to collapse or insert callbacks into the collapse function to - * do it as needed. - * - * I went back and forth between the two approaches several times. - * The problem with simply logging the surrounding faces is that - * in practice it has to be two rings of adjacent faces around the - * edge to catch various non-manifold edge cases. This ended up - * taking far too much RAM for undo. - * - * The alternative is to add callbacks into the collapse function. - * This saves a lot of memory but makes collapse more complicated. - * - * The approach this file takes to use templates to avoid the - * overhead of checking for callback pointers in other code. - * - * In hindsight this is kind of a silly concern and will be - * rewritten to use runtime callbacks. - * - * - */ - -#pragma once - -#include -#include -#include -#include - -#include "MEM_guardedalloc.h" - -#include "DNA_meshdata_types.h" - -#include "BLI_compiler_attrs.h" -#include "BLI_compiler_compat.h" -#include "BLI_map.hh" -#include "BLI_math_vector_types.hh" -#include "BLI_set.hh" -#include "BLI_span.hh" -#include "BLI_utildefines.h" -#include "BLI_vector.hh" - -using blender::float2; -using blender::float3; -using blender::IndexRange; -using blender::Map; -using blender::MutableSpan; -using blender::Set; -using blender::Span; -using blender::Vector; - -#include -#include -#include -#include - -#include "bmesh.h" -#include "bmesh_private.h" -#include "bmesh_structure.h" - -void bmesh_disk_edge_append(BMEdge *e, BMVert *v); -void bmesh_radial_loop_append(BMEdge *e, BMLoop *l); -void bm_kill_only_edge(BMesh *bm, BMEdge *e); -void bm_kill_only_loop(BMesh *bm, BMLoop *l); -void bm_kill_only_face(BMesh *bm, BMFace *f); - -//#define JVKE_DEBUG +struct BMVert; +struct BMEdge; +struct BMFace; +struct BMesh; namespace blender::bmesh { - -struct NullCollapseCallbacks { - inline void on_vert_kill(BMVert *) {} - inline void on_edge_kill(BMEdge *) {} - /* Called when verts are combined. */ - inline void on_vert_combine(BMVert * /*dest*/, BMVert * /*source*/) {} - /* Called when edges are combined. */ - inline void on_edge_combine(BMEdge * /*dest*/, BMEdge * /*source*/) {} - inline void on_face_kill(BMFace *) {} - inline void on_vert_create(BMVert *) {} - inline void on_edge_create(BMEdge *) {} - inline void on_face_create(BMFace *) {} +struct CollapseCallbacks { + void *customdata; + void (*on_vert_kill)(void *customdata, BMVert *) = nullptr; + void (*on_edge_kill)(void *customdata, BMEdge *) = nullptr; + void (*on_face_kill)(void *customdata, BMFace *) = nullptr; + void (*on_vert_combine)(void *customdata, BMVert * /*dest*/, BMVert * /*source*/) = nullptr; + void (*on_edge_combine)(void *customdata, BMEdge * /*dest*/, BMEdge * /*source*/) = nullptr; + void (*on_vert_create)(void *customdata, BMVert *) = nullptr; + void (*on_edge_create)(void *customdata, BMEdge *) = nullptr; + void (*on_face_create)(void *customdata, BMFace *) = nullptr; }; -#ifdef JVKE_DEBUG - -static void bm_local_obj_free(char *str, char *fixed) -{ - if (str != fixed) { - MEM_freeN(str); - } -} - -# define LOCAL_OBJ_SIZE 512 - -static char *obj_append_line(const char *line, char *str, char *fixed, int *size, int *i) -{ - int len = (int)strlen(line); - - if (*i + len + 1 >= *size) { - *size += len + ((*size) >> 1); - - if (str == fixed) { - str = static_cast(MEM_mallocN(*size, "buf")); - memcpy(static_cast(str), fixed, LOCAL_OBJ_SIZE); - } - else { - str = static_cast(MEM_reallocN(str, *size)); - } - } - - memcpy(str + *i, line, len); - str[*i + len] = 0; - - *i += len; - - return str; -} - -/* NotForPr: saves an obj of the neighborhood around an edge prior to collapse - * into a buffer that can be read from a debugger. - */ -static char *bm_save_local_obj_text( - BMesh *, int depth, char buf[LOCAL_OBJ_SIZE], const char *fmt, ...) -{ - va_list vl; - va_start(vl, fmt); - - buf[0] = 0; - - Vector vs; - Vector initial_vs; - Vector es; - Vector initial_es; - Vector fs; - Vector initial_fs; - - Set visit; - - const char *c = fmt; - while (*c) { - if (*c == ' ' || *c == '\t') { - c++; - continue; - } - - void *ptr = va_arg(vl, void *); - - switch (*c) { - case 'v': - vs.append(static_cast(ptr)); - initial_vs.append(static_cast(ptr)); - break; - case 'e': - es.append(static_cast(ptr)); - initial_es.append(static_cast(ptr)); - break; - case 'f': - fs.append(static_cast(ptr)); - initial_fs.append(static_cast(ptr)); - break; - } - - c++; - } - - va_end(vl); - - int tag = 4; - for (BMFace *f : fs) { - BMLoop *l = f->l_first; - - do { - l->v->head.api_flag &= ~tag; - l->e->head.api_flag &= ~tag; - } while ((l = l->next) != f->l_first); - } - - for (BMEdge *e : es) { - e->v1->head.api_flag &= ~tag; - e->v2->head.api_flag &= ~tag; - } - - for (BMVert *v : vs) { - v->head.api_flag |= tag; - } - - for (BMEdge *e : es) { - if (!(e->v1->head.api_flag & tag)) { - vs.append(e->v1); - e->v1->head.api_flag |= tag; - } - - if (!(e->v2->head.api_flag & tag)) { - vs.append(e->v2); - e->v2->head.api_flag |= tag; - } - - e->head.api_flag |= tag; - } - - for (BMFace *f : fs) { - BMLoop *l = f->l_first; - - do { - if (!(l->v->head.api_flag & tag)) { - vs.append(l->v); - l->v->head.api_flag |= tag; - } - - if (!(l->e->head.api_flag & tag)) { - es.append(l->e); - l->e->head.api_flag |= tag; - } - } while ((l = l->next) != f->l_first); - } - - struct StackItem { - BMVert *v; - int depth; - }; - - Vector stack; - Set elemset; - - for (BMVert *v : vs) { - elemset.add(static_cast(v)); - } - for (BMEdge *e : es) { - elemset.add(static_cast(e)); - } - for (BMFace *f : fs) { - elemset.add(static_cast(f)); - } - - stack.clear(); - stack.append({vs[0], 0}); - while (stack.size() > 0) { - StackItem item = stack.pop_last(); - BMVert *v = item.v; - int startdepth = item.depth; - - if (elemset.add(static_cast(v))) { - vs.append(v); - } - - if (!v->e || item.depth > depth) { - continue; - } - - BMEdge *e = v->e; - do { - if (visit.add(static_cast(e))) { - stack.append({e->v1, startdepth + 1}); - stack.append({e->v2, startdepth + 1}); - } - - if (!e->l) { - continue; - } - - BMLoop *l = e->l; - do { - if (visit.add(static_cast(l->f))) { - if (elemset.add(static_cast(l->f))) { - fs.append(l->f); - } - - BMLoop *l2 = l; - do { - if (visit.add(static_cast(l->v))) { - stack.append({l->v, startdepth + 1}); - } - } while ((l2 = l2->next) != l); - } - } while ((l = l->radial_next) != e->l); - } while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e); - } - - char *str = buf; - int size = LOCAL_OBJ_SIZE - 1; - int stri = 0; - - char line[128]; - line[0] = 0; - - for (BMVert *v : vs) { - v->head.api_flag &= ~tag; - } - for (BMEdge *e : es) { - e->head.api_flag &= ~tag; - } - - for (BMFace *f : fs) { - f->head.api_flag &= ~tag; - } - for (BMVert *v : initial_vs) { - v->head.api_flag |= tag; - } - - for (BMEdge *e : initial_es) { - e->head.api_flag |= tag; - e->v1->head.api_flag |= tag; - e->v2->head.api_flag |= tag; - } - - for (BMFace *f : initial_fs) { - f->head.api_flag |= tag; - BMLoop *l = f->l_first; - - do { - l->v->head.api_flag |= tag; - } while ((l = l->next) != f->l_first); - } - - for (int i : vs.index_range()) { - BMVert *v = vs[i]; - - if (v->head.api_flag & tag) { - sprintf(line, "#select\n"); - str = obj_append_line(line, str, buf, &size, &stri); - } - - v->head.index = i + 1; - sprintf(line, "v %.4f %.4f %.4f\n", v->co[0], v->co[1], v->co[2]); - - str = obj_append_line(line, str, buf, &size, &stri); - } - - /* save wire edges */ - for (BMEdge *e : es) { - if (e->l) { - continue; - } - - sprintf(line, "l %d %d\n", e->v1->head.index, e->v2->head.index); - str = obj_append_line(line, str, buf, &size, &stri); - } - - for (BMFace *f : fs) { - BMLoop *l = f->l_first; - - sprintf(line, "f"); - str = obj_append_line(line, str, buf, &size, &stri); - - do { - sprintf(line, " %d", l->v->head.index); - - str = obj_append_line(line, str, buf, &size, &stri); - } while ((l = l->next) != f->l_first); - - str = obj_append_line("\n", str, buf, &size, &stri); - } - - return str; -} - -static void check_mesh_radial(BMesh *bm) -{ - return; - - BMIter iter; - BMEdge *e; - BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) { - int count = 0; - BMLoop *l = e->l; - - if (!l) { - continue; - } - - do { - if (BM_elem_is_free((BMElem *)l, BM_LOOP)) { - printf("Freed loop in edge %p radial cycle\n", e); - } - - if (count++ > 100) { - printf("Corrupted radial cycle for %p\n", e); - break; - } - } while ((l = l->radial_next) != e->l); - } -} - -static void trigger_jvke_error(int err, char *obj_text) -{ - printf("========= ERROR %s============\n\n%s\n\n", bm_get_error_str(err), obj_text); -} - -int bmesh_elem_check_all(void *elem, char htype); - -extern char *_last_local_obj; - -# define JVKE_CHECK_ELEMENT(elem) \ - { \ - int err = 0; \ - if ((err = bmesh_elem_check_all(elem, (elem)->head.htype))) { \ - trigger_jvke_error(err, saved_obj); \ - } \ - } -#else -# define JVKE_CHECK_ELEMENT(elem) -#endif - -template -static bool cleanup_vert(BMesh *bm, BMVert *v, Callbacks &callbacks) -{ - BMEdge *e = v->e; - - if (!e->l || e->l->f == e->l->radial_next->f) { - return false; - } - - BMFace *f_example = nullptr; - - BMIter iter; - BMFace *f; - BM_ITER_ELEM (f, &iter, v, BM_FACES_OF_VERT) { - callbacks.on_face_kill(f); - } - - do { - BMLoop *l = e->l; - if (!l) { - continue; - } - - callbacks.on_edge_kill(e); - - if (!f_example) { - f_example = l->f; - } - } while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e); - - BMVert *v1 = BM_edge_other_vert(v->e, v); - BMVert *v2 = BM_edge_other_vert(BM_DISK_EDGE_NEXT(v->e, v), v); - BMVert *v3 = BM_edge_other_vert(BM_DISK_EDGE_NEXT(BM_DISK_EDGE_NEXT(v->e, v), v), v); - - f = BM_face_create_quad_tri(bm, v1, v2, v3, nullptr, f_example, BM_CREATE_NOP); - BMLoop *l = f->l_first; - - callbacks.on_vert_kill(v); - BM_vert_kill(bm, v); - - /* Ensure correct winding. */ - do { - if (l->radial_next != l && l->radial_next->v == l->v) { - BM_face_normal_flip(bm, f); - break; - } - } while ((l = l->next) != f->l_first); - - l = f->l_first; - do { - if (l != l->radial_next) { - BMLoop *l2 = l->radial_next; - if (l2->v != l->v) { - l2 = l2->next; - } - - CustomData_bmesh_copy_data(&bm->ldata, &bm->ldata, l2->head.data, &l->head.data); - } - } while ((l = l->next) != f->l_first); - - callbacks.on_face_create(f); - - return true; -} - -template -static void bmesh_kernel_check_val3_vert(BMesh *bm, BMEdge *e, Callbacks &callbacks) -{ - if (!e->l) { - return; - } - - bool stop; - - do { - stop = true; - - BMLoop *l = e->l; - - if (!l) { - break; - } - - do { - BMLoop *l2 = l->prev; - - if (l2 == l2->radial_next || !l2->v->e) { - continue; - } - - bool bad = false; - int count = 0; - - BMEdge *e2 = l2->v->e; - do { - if (!e2->l || e2->l == e2->l->radial_next || e2->l->radial_next->radial_next != e2->l) { - bad = true; - break; - } - - bad = bad || e2->l->f->len != 3 || e2->l->radial_next->f->len != 3; - count++; - } while ((e2 = BM_DISK_EDGE_NEXT(e2, l2->v)) != l2->v->e); - - bad = bad || count != 3; - - if (!bad) { - if (cleanup_vert(bm, l2->v, callbacks)) { - stop = false; - break; - } - } - } while ((l = l->radial_next) != e->l); - } while (!stop); -} - -/** - * \brief Join Vert Kill Edge (JVKE) - * - * Collapse an edge, merging surrounding data. - * - * Unlike #BM_vert_collapse_edge & #bmesh_kernel_join_edge_kill_vert - * which only handle 2 valence verts, - * this can handle any number of connected edges/faces. - * - *
- * Before: -> After:
- * +-+-+-+    +-+-+-+
- * | | | |    | \ / |
- * +-+-+-+    +--+--+
- * | | | |    | / \ |
- * +-+-+-+    +-+-+-+
- * 
- */ - -template BMVert *join_vert_kill_edge( - BMesh *bm, BMEdge *e, BMVert *v_del, const bool do_del, Callbacks &callbacks) -{ - BMVert *v_conn = BM_edge_other_vert(e, v_del); - -#ifdef JVKE_DEBUG - char buf[LOCAL_OBJ_SIZE]; - - bool have_boundary = false; - - if (_last_local_obj) { - MEM_freeN(static_cast(_last_local_obj)); - } - - char *saved_obj = bm_save_local_obj_text(bm, 2, buf, "e", e); - - _last_local_obj = static_cast(MEM_mallocN(strlen(saved_obj) + 1, "_last_local_obj")); - BLI_strncpy(_last_local_obj, saved_obj, strlen(saved_obj) + 1); -#endif - - /* Destroy any valence-3 verts that might turn into non-manifold "fins." */ - bmesh_kernel_check_val3_vert(bm, e, callbacks); - - Set es; - Set fs; - - const int dup_tag = _FLAG_OVERLAP; - - callbacks.on_vert_combine(v_conn, v_del); - - for (int i = 0; i < 2; i++) { - BMVert *v = i ? v_del : v_conn; - - BMEdge *e = v->e; - do { - es.add(e); - - BMLoop *l = e->l; - if (!l) { - continue; - } - - do { - fs.add(l->f); - BMLoop *l2 = l; - do { - es.add(l2->e); - BMLoop *l3 = l2; - do { - fs.add(l3->f); - } while ((l3 = l3->radial_next) != l2); - } while ((l2 = l2->next) != l); - } while ((l = l->radial_next) != e->l); - } while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e); - } - - /* Inform callbacks we've "killed" all the faces. */ - for (BMFace *f : fs) { - callbacks.on_face_kill(f); - } - - /* Unlink loops. */ - for (BMFace *f : fs) { - BMLoop *l = f->l_first; - do { - BMEdge *e = l->e; - bmesh_radial_loop_remove(l->e, l); - l->e = e; - } while ((l = l->next) != f->l_first); - } - - /* Swap edges. */ - for (BMEdge *e : es) { - if (e->v1 != v_del && e->v2 != v_del) { - continue; - } - - if (e->v1 == v_conn || e->v2 == v_conn) { - if (e->l) { - printf("%s: ErROR!\n", __func__); - } - - callbacks.on_edge_kill(e); - BM_edge_kill(bm, e); - continue; - } - - BMVert *otherv = e->v1 == v_del ? e->v2 : e->v1; - - BMEdge *exist = BM_edge_exists(otherv, v_conn); - - if (exist) { - if (e->l) { - printf("%s: ERROR!\n", __func__); - } - - callbacks.on_edge_combine(exist, e); - - /* Combine edge flags. The sharp flag is inverted - * so we can't just OR it. - */ - bool sharp = !(exist->head.hflag & BM_ELEM_SMOOTH) || !(e->head.hflag & BM_ELEM_SMOOTH); - - exist->head.hflag |= e->head.hflag; - - if (sharp) { - exist->head.hflag &= ~BM_ELEM_SMOOTH; - } - - callbacks.on_edge_kill(e); - BM_edge_kill(bm, e); - } - else { - callbacks.on_edge_kill(e); - bmesh_disk_vert_replace(e, v_conn, v_del); - callbacks.on_edge_create(e); - } - } - - auto remove_loop = [&bm](BMFace *f, BMLoop *l) { - l->next->prev = l->prev; - l->prev->next = l->next; - if (l == f->l_first) { - f->l_first = l->next; - } - - f->len--; - bm_kill_only_loop(bm, l); - }; - - /* Swap loops */ - for (BMFace *f : fs) { - BMLoop *l = f->l_first; - BMLoop *lnext; - bool found = false; - - /* Swap v_del and remove duplicate v_conn's. */ - do { - lnext = l->next; - - if (l->v == v_del) { - l->v = v_conn; - } - if (l->v == v_conn) { - if (found) { - remove_loop(f, l); - } - else { - found = true; - } - } - } while ((l = lnext) != f->l_first); - - /* Remove any remaining duplicate verts. */ - do { - lnext = l->next; - if (l->v == l->next->v) { - remove_loop(f, l); - } - } while ((l = lnext) != f->l_first); - } - - Vector finalfs; - - /* Relink faces. */ - for (BMFace *f : fs) { - if (f->len < 3) { - BMLoop *l = f->l_first; - BMLoop *lnext; - do { - lnext = l->next; - bm_kill_only_loop(bm, l); - } while ((l = lnext) != f->l_first); - - bm_kill_only_face(bm, f); - continue; - } - - BMLoop *l = f->l_first; - do { - BMEdge *exist_e = BM_edge_exists(l->v, l->next->v); - if (!exist_e) { - exist_e = BM_edge_create(bm, l->v, l->next->v, nullptr, BM_CREATE_NOP); - callbacks.on_edge_create(exist_e); - } - - l->e = exist_e; - bmesh_radial_loop_append(l->e, l); - - BM_ELEM_API_FLAG_DISABLE(l->v, dup_tag); - BM_ELEM_API_FLAG_DISABLE(l->e, dup_tag); - BM_ELEM_API_FLAG_DISABLE(l->f, dup_tag); - } while ((l = l->next) != f->l_first); - - callbacks.on_face_create(f); - finalfs.append(f); - } - - for (BMFace *f : finalfs) { - if (BM_elem_is_free((BMElem *)f, BM_FACE)) { - continue; - } - - BMFace *f2; - while ((f2 = BM_face_find_double(f))) { - printf("%s: removing duplicate face.\n", __func__); - callbacks.on_face_kill(f2); - BM_face_kill(bm, f2); - } - } - - JVKE_CHECK_ELEMENT(v_conn); - - if (do_del && !v_del->e) { - callbacks.on_vert_kill(v_del); - BM_vert_kill(bm, v_del); - } - - return v_conn; -} + BMesh *bm, BMEdge *e, BMVert *v_del, const bool do_del, CollapseCallbacks *callbacks); } // namespace blender::bmesh -- 2.30.2 From 8889a48166680633b662947c829c3fa36aa6d3e7 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Fri, 13 Oct 2023 13:44:13 -0700 Subject: [PATCH 278/279] temp-sculpt-dyntopo: Fix surface_smooth_v_safe not respecting sharpangle --- source/blender/blenkernel/intern/dyntopo.cc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/source/blender/blenkernel/intern/dyntopo.cc b/source/blender/blenkernel/intern/dyntopo.cc index a33888e5c80..1bc9c4626d4 100644 --- a/source/blender/blenkernel/intern/dyntopo.cc +++ b/source/blender/blenkernel/intern/dyntopo.cc @@ -162,6 +162,10 @@ static void surface_smooth_v_safe( BMVert *v2 = e->v1 == v ? e->v2 : e->v1; PBVHVertRef vertex2 = {reinterpret_cast(v2)}; + if (BM_ELEM_CD_GET_INT(e, pbvh->cd_edge_boundary) & SCULPT_BOUNDARY_SHARP_ANGLE) { + return; + } + float w; if (e->l && e->l->f->len == 3) { -- 2.30.2 From 368349753e697b4ae2eeb4c875c6be25055e7947 Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Tue, 9 Jan 2024 17:10:01 -0800 Subject: [PATCH 279/279] temp-sculpt-dyntopo: Fix merge bugs --- source/blender/blenkernel/BKE_dyntopo_set.hh | 4 - .../blender/blenkernel/intern/customdata.cc | 8 +- .../blender/blenkernel/intern/pbvh_bmesh.cc | 9 ++- .../blender/blenkernel/intern/pbvh_colors.cc | 80 +++++++++++++++++++ 4 files changed, 93 insertions(+), 8 deletions(-) diff --git a/source/blender/blenkernel/BKE_dyntopo_set.hh b/source/blender/blenkernel/BKE_dyntopo_set.hh index d27428c733a..248d26cff70 100644 --- a/source/blender/blenkernel/BKE_dyntopo_set.hh +++ b/source/blender/blenkernel/BKE_dyntopo_set.hh @@ -28,10 +28,6 @@ template class DyntopoSet { } DyntopoSet() {} DyntopoSet(const DyntopoSet &) = delete; - ATTR_NO_OPT ~DyntopoSet() - { - printf("~DyntopoSet()"); - } struct iterator { iterator() : set_(nullptr), i_(-1) {} diff --git a/source/blender/blenkernel/intern/customdata.cc b/source/blender/blenkernel/intern/customdata.cc index 8e0cfc08237..20f145a3f21 100644 --- a/source/blender/blenkernel/intern/customdata.cc +++ b/source/blender/blenkernel/intern/customdata.cc @@ -4267,7 +4267,7 @@ void CustomData_bmesh_copy_block(CustomData &data, void *src_block, void **dst_b if (*dst_block) { for (const CustomDataLayer &layer : Span(data.layers, data.totlayer)) { const LayerTypeInfo &info = *layerType_getInfo(eCustomDataType(layer.type)); - if (info.free) { + if (info.free && !(layer.flag & CD_FLAG_ELEM_NOCOPY)) { info.free(POINTER_OFFSET(*dst_block, layer.offset), 1); } } @@ -4276,10 +4276,14 @@ void CustomData_bmesh_copy_block(CustomData &data, void *src_block, void **dst_b if (data.totsize == 0) { return; } - *dst_block = BLI_mempool_alloc(data.pool); + *dst_block = BLI_mempool_calloc(data.pool); } for (const CustomDataLayer &layer : Span(data.layers, data.totlayer)) { + if (layer.flag & CD_FLAG_ELEM_NOCOPY) { + continue; + } + const int offset = layer.offset; const LayerTypeInfo &info = *layerType_getInfo(eCustomDataType(layer.type)); if (info.copy) { diff --git a/source/blender/blenkernel/intern/pbvh_bmesh.cc b/source/blender/blenkernel/intern/pbvh_bmesh.cc index b5459ecc13a..45ded37f693 100644 --- a/source/blender/blenkernel/intern/pbvh_bmesh.cc +++ b/source/blender/blenkernel/intern/pbvh_bmesh.cc @@ -888,6 +888,11 @@ static void pbvh_bmesh_node_split( BKE_pbvh_free_proxyarray(pbvh, n); #endif + if (!n->bm_faces) { + printf("%s: n->bm_faces was nullptr! depth: %d\n", __func__, depth); + return; + } + if (n->depth >= PBVH_STACK_FIXED_DEPTH || n->bm_faces->size() <= pbvh->leaf_limit) { /* Node limit not exceeded */ pbvh_bmesh_node_finalize(pbvh, node_index, cd_vert_node_offset, cd_face_node_offset, add_orco); @@ -915,7 +920,7 @@ static void pbvh_bmesh_node_split( /* Add two new child nodes */ const int children = pbvh->nodes.size(); n->children_offset = children; - pbvh->nodes.reserve(pbvh->nodes.size() + 2); + pbvh->nodes.resize(pbvh->nodes.size() + 2); /* Array reallocated, update current node pointer */ n = &pbvh->nodes[node_index]; @@ -2492,7 +2497,7 @@ ATTR_NO_OPT void build_bmesh(PBVH *pbvh, /* start recursion, assign faces to nodes accordingly */ pbvh_bmesh_node_limit_ensure_fast(pbvh, nodeinfo, bbc_array, &rootnode, leaves, arena); - pbvh->nodes.reserve(pbvh->nodes.size() + 1); + pbvh->nodes.resize(pbvh->nodes.size() + 1); rootnode.node_index = 0; pbvh_bmesh_create_nodes_fast_recursive_create(pbvh, nodeinfo, bbc_array, &rootnode); diff --git a/source/blender/blenkernel/intern/pbvh_colors.cc b/source/blender/blenkernel/intern/pbvh_colors.cc index cff85227c2a..fb4cbb5e08a 100644 --- a/source/blender/blenkernel/intern/pbvh_colors.cc +++ b/source/blender/blenkernel/intern/pbvh_colors.cc @@ -85,11 +85,58 @@ template<> void from_float(const float src[4], MPropCol &dst) copy_v4_v4(dst.color, src); } +template +static void pbvh_vertex_color_get_bmesh(const PBVH &pbvh, PBVHVertRef vertex, float r_color[4]) +{ + BMVert *v = reinterpret_cast(vertex.i); + int cd_color = pbvh.color_layer->offset; + + if (pbvh.color_domain == AttrDomain::Corner) { + zero_v4(r_color); + + if (!v->e) { + return; + } + + BMEdge *e = v->e; + int count = 0; + + do { + BMLoop *l = e->l; + if (l) { + do { + BMLoop *l_color = l->v != v ? l->next : l; + T *color = BM_ELEM_CD_PTR(l_color, cd_color); + + float temp[4]; + to_float(*color, temp); + add_v4_v4(r_color, temp); + count++; + } while ((l = l->radial_next) != e->l); + } + } while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e); + + if (count) { + mul_v4_fl(r_color, 1.0f / float(count)); + } + + return; + } + + T *color = BM_ELEM_CD_PTR(v, cd_color); + to_float(*color, r_color); +} + template static void pbvh_vertex_color_get(const PBVH &pbvh, PBVHVertRef vertex, float r_color[4]) { int index = vertex.i; + if (pbvh.header.bm) { + pbvh_vertex_color_get_bmesh(pbvh, vertex, r_color); + return; + } + if (pbvh.color_domain == AttrDomain::Corner) { int count = 0; zero_v4(r_color); @@ -118,11 +165,44 @@ static void pbvh_vertex_color_get(const PBVH &pbvh, PBVHVertRef vertex, float r_ } } +template +static void pbvh_vertex_color_set_bmesh(const PBVH &pbvh, PBVHVertRef vertex, const float color[4]) +{ + BMVert *v = reinterpret_cast(vertex.i); + int cd_color = pbvh.color_layer->offset; + + if (pbvh.color_domain == AttrDomain::Corner) { + if (!v->e) { + return; + } + + BMEdge *e = v->e; + do { + BMLoop *l = e->l; + if (l) { + do { + BMLoop *l_color = l->v != v ? l->next : l; + from_float(color, *BM_ELEM_CD_PTR(l_color, cd_color)); + } while ((l = l->radial_next) != e->l); + } + } while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e); + + return; + } + + from_float(color, *BM_ELEM_CD_PTR(v, cd_color)); +} + template static void pbvh_vertex_color_set(PBVH &pbvh, PBVHVertRef vertex, const float color[4]) { int index = vertex.i; + if (pbvh.header.bm) { + pbvh_vertex_color_set_bmesh(pbvh, vertex, color); + return; + } + if (pbvh.color_domain == AttrDomain::Corner) { for (const int i_face : pbvh.pmap[index]) { const IndexRange face = pbvh.faces[i_face]; -- 2.30.2