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

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoseph Eagar <joeedh@gmail.com>2021-09-18 09:32:08 +0300
committerJoseph Eagar <joeedh@gmail.com>2021-09-18 09:32:08 +0300
commit2d3d6eb7b2a9be40aa5f383a1ff57ef37debc197 (patch)
treed2d27ab69f8ffb0797b022288311821780e0e368
parent85d274a60c03b067efaddeda921f0edf1c0982b3 (diff)
parentf0c35d16f39d837c7bceb6740d3fe9c440676564 (diff)
Merge remote-tracking branch 'origin/temp_bmesh_multires' into sculpt-dev
-rw-r--r--.clang-format1
-rw-r--r--CMakeLists.txt13
-rw-r--r--extern/Eigen3/Eigen/src/Core/util/Macros.h2
-rw-r--r--extern/audaspace/bindings/C/AUD_Device.cpp2
-rw-r--r--extern/audaspace/include/util/ThreadPool.h8
-rw-r--r--extern/quadriflow/3rd/lemon-1.3.1/CMakeLists.txt2
-rw-r--r--extern/quadriflow/3rd/lemon-1.3.1/cmake/FindILOG.cmake2
-rw-r--r--extern/quadriflow/3rd/lemon-1.3.1/contrib/CMakeLists.txt1
-rw-r--r--extern/quadriflow/3rd/lemon-1.3.1/lemon/CMakeLists.txt1
-rw-r--r--extern/quadriflow/src/loader.cpp3
-rw-r--r--intern/atomic/intern/atomic_ops_msvc.h30
-rw-r--r--intern/guardedalloc/CMakeLists.txt1
-rw-r--r--intern/guardedalloc/intern/mallocn_guarded_impl.c2
-rw-r--r--intern/guardedalloc/intern/mallocn_lockfree_impl.c31
-rw-r--r--intern/quadriflow/quadriflow_capi.cpp52
-rw-r--r--intern/quadriflow/quadriflow_capi.hpp9
-rw-r--r--release/datafiles/icons/brush.sculpt.displacement_smear.datbin4436 -> 0 bytes
-rw-r--r--release/datafiles/icons/brush.sculpt.draw_sharp.datbin2492 -> 2492 bytes
-rw-r--r--release/datafiles/icons/brush.sculpt.paint.datbin0 -> 5606 bytes
-rw-r--r--release/datafiles/icons/brush.sculpt.smear.datbin0 -> 1700 bytes
-rw-r--r--release/datafiles/icons/brush.sculpt.vcol_boundary.datbin0 -> 7694 bytes
-rw-r--r--release/datafiles/icons/ops.armature.extrude.cursor.datbin1502 -> 0 bytes
-rw-r--r--release/datafiles/icons/ops.armature.extrude.datbin1250 -> 0 bytes
-rw-r--r--release/datafiles/icons/ops.curve.dupli_extrude_cursor.datbin4202 -> 0 bytes
m---------release/datafiles/locale0
m---------release/scripts/addons0
m---------release/scripts/addons_contrib0
-rw-r--r--release/scripts/startup/bl_ui/properties_paint_common.py97
-rw-r--r--release/scripts/startup/bl_ui/space_toolsystem_toolbar.py4
-rw-r--r--release/scripts/startup/bl_ui/space_userpref.py1
-rw-r--r--release/scripts/startup/bl_ui/space_view3d.py1
-rw-r--r--release/scripts/startup/bl_ui/space_view3d_toolbar.py83
-rw-r--r--source/blender/blenkernel/BKE_blender_version.h2
-rw-r--r--source/blender/blenkernel/BKE_brush.h9
-rw-r--r--source/blender/blenkernel/BKE_brush_engine.h129
-rw-r--r--source/blender/blenkernel/BKE_customdata.h21
-rw-r--r--source/blender/blenkernel/BKE_data_transfer.h11
-rw-r--r--source/blender/blenkernel/BKE_dyntopo.h24
-rw-r--r--source/blender/blenkernel/BKE_fcurve.h4
-rw-r--r--source/blender/blenkernel/BKE_mesh.h5
-rw-r--r--source/blender/blenkernel/BKE_mesh_mapping.h44
-rw-r--r--source/blender/blenkernel/BKE_mesh_mirror.h2
-rw-r--r--source/blender/blenkernel/BKE_multires.h2
-rw-r--r--source/blender/blenkernel/BKE_object.h6
-rw-r--r--source/blender/blenkernel/BKE_paint.h92
-rw-r--r--source/blender/blenkernel/BKE_pbvh.h501
-rw-r--r--source/blender/blenkernel/CMakeLists.txt76
-rw-r--r--source/blender/blenkernel/intern/DerivedMesh.cc2
-rw-r--r--source/blender/blenkernel/intern/anim_data.c2
-rw-r--r--source/blender/blenkernel/intern/brush.c270
-rw-r--r--source/blender/blenkernel/intern/brush_engine.c683
-rw-r--r--source/blender/blenkernel/intern/cdderivedmesh.c12
-rw-r--r--source/blender/blenkernel/intern/collection.c7
-rw-r--r--source/blender/blenkernel/intern/colortools.c3
-rw-r--r--source/blender/blenkernel/intern/curve_bevel.c2
-rw-r--r--source/blender/blenkernel/intern/customdata.c429
-rw-r--r--source/blender/blenkernel/intern/data_transfer.c7
-rw-r--r--source/blender/blenkernel/intern/dyntopo.c6175
-rw-r--r--source/blender/blenkernel/intern/idprop_utils.c2
-rw-r--r--source/blender/blenkernel/intern/lib_id.c2
-rw-r--r--source/blender/blenkernel/intern/mesh.c17
-rw-r--r--source/blender/blenkernel/intern/mesh_fair.cc5
-rw-r--r--source/blender/blenkernel/intern/mesh_mapping.c319
-rw-r--r--source/blender/blenkernel/intern/mesh_merge.c12
-rw-r--r--source/blender/blenkernel/intern/mesh_mirror.c12
-rw-r--r--source/blender/blenkernel/intern/mesh_remap.c14
-rw-r--r--source/blender/blenkernel/intern/mesh_remesh_voxel.cc94
-rw-r--r--source/blender/blenkernel/intern/multires.c461
-rw-r--r--source/blender/blenkernel/intern/multires_reshape.c6
-rw-r--r--source/blender/blenkernel/intern/multires_reshape_apply_base.c5
-rw-r--r--source/blender/blenkernel/intern/multires_reshape_smooth.c33
-rw-r--r--source/blender/blenkernel/intern/multires_reshape_util.c2
-rw-r--r--source/blender/blenkernel/intern/multires_unsubdivide.c4
-rw-r--r--source/blender/blenkernel/intern/paint.c238
-rw-r--r--source/blender/blenkernel/intern/pbvh.c1162
-rw-r--r--source/blender/blenkernel/intern/pbvh_bmesh.c5780
-rw-r--r--source/blender/blenkernel/intern/pbvh_cache_test_main.c31
-rw-r--r--source/blender/blenkernel/intern/pbvh_displacement.c266
-rw-r--r--source/blender/blenkernel/intern/pbvh_intern.h151
-rw-r--r--source/blender/blenkernel/intern/scene.c12
-rw-r--r--source/blender/blenkernel/intern/subdiv_displacement_multires.c1
-rw-r--r--source/blender/blenkernel/intern/subsurf_ccg.c5
-rw-r--r--source/blender/blenlib/BLI_asan.h58
-rw-r--r--source/blender/blenlib/BLI_compiler_attrs.h17
-rw-r--r--source/blender/blenlib/BLI_ghash.h47
-rw-r--r--source/blender/blenlib/BLI_linklist_stack.h2
-rw-r--r--source/blender/blenlib/BLI_mempool.h24
-rw-r--r--source/blender/blenlib/BLI_smallhash.h21
-rw-r--r--source/blender/blenlib/BLI_strict_flags.h24
-rw-r--r--source/blender/blenlib/CMakeLists.txt1
-rw-r--r--source/blender/blenlib/intern/BLI_mempool.c357
-rw-r--r--source/blender/blenlib/intern/BLI_table_gset.c153
-rw-r--r--source/blender/blenlib/intern/smallhash.c266
-rw-r--r--source/blender/blenlib/intern/task_pool.cc3
-rw-r--r--source/blender/blenloader/intern/versioning_260.c2
-rw-r--r--source/blender/blenloader/intern/versioning_280.c2
-rw-r--r--source/blender/blenloader/intern/versioning_290.c9
-rw-r--r--source/blender/blenloader/intern/versioning_300.c116
-rw-r--r--source/blender/blenloader/intern/versioning_defaults.c8
-rw-r--r--source/blender/bmesh/CMakeLists.txt1
-rw-r--r--source/blender/bmesh/bmesh_class.h38
-rw-r--r--source/blender/bmesh/intern/bmesh_construct.c439
-rw-r--r--source/blender/bmesh/intern/bmesh_construct.h1
-rw-r--r--source/blender/bmesh/intern/bmesh_core.c144
-rw-r--r--source/blender/bmesh/intern/bmesh_core.h9
-rw-r--r--source/blender/bmesh/intern/bmesh_inline.h6
-rw-r--r--source/blender/bmesh/intern/bmesh_interp.c692
-rw-r--r--source/blender/bmesh/intern/bmesh_interp.h11
-rw-r--r--source/blender/bmesh/intern/bmesh_iterators.c1
-rw-r--r--source/blender/bmesh/intern/bmesh_iterators_inline.h3
-rw-r--r--source/blender/bmesh/intern/bmesh_log.c2401
-rw-r--r--source/blender/bmesh/intern/bmesh_log.h76
-rw-r--r--source/blender/bmesh/intern/bmesh_marking.c8
-rw-r--r--source/blender/bmesh/intern/bmesh_mesh.c735
-rw-r--r--source/blender/bmesh/intern/bmesh_mesh.h27
-rw-r--r--source/blender/bmesh/intern/bmesh_mesh_convert.c342
-rw-r--r--source/blender/bmesh/intern/bmesh_mesh_convert.h18
-rw-r--r--source/blender/bmesh/intern/bmesh_mesh_convert_threaded.c1194
-rw-r--r--source/blender/bmesh/intern/bmesh_mesh_duplicate.c6
-rw-r--r--source/blender/bmesh/intern/bmesh_mods.c11
-rw-r--r--source/blender/bmesh/intern/bmesh_mods.h3
-rw-r--r--source/blender/bmesh/intern/bmesh_opdefines.c22
-rw-r--r--source/blender/bmesh/intern/bmesh_polygon.c39
-rw-r--r--source/blender/bmesh/intern/bmesh_polygon.h43
-rw-r--r--source/blender/bmesh/intern/bmesh_structure.h9
-rw-r--r--source/blender/bmesh/operators/bmo_dupe.c13
-rw-r--r--source/blender/bmesh/operators/bmo_extrude.c3
-rw-r--r--source/blender/bmesh/operators/bmo_fill_grid.c2
-rw-r--r--source/blender/bmesh/operators/bmo_inset.c7
-rw-r--r--source/blender/bmesh/operators/bmo_mesh_convert.c4
-rw-r--r--source/blender/bmesh/operators/bmo_primitive.c4
-rw-r--r--source/blender/bmesh/operators/bmo_symmetrize.c6
-rw-r--r--source/blender/bmesh/tests/bmesh_core_test.cc4
-rw-r--r--source/blender/bmesh/tools/bmesh_decimate_collapse.c6
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_cycle.cc2
-rw-r--r--source/blender/draw/DRW_engine.h7
-rw-r--r--source/blender/draw/engines/overlay/overlay_lattice.c1
-rw-r--r--source/blender/draw/engines/overlay/overlay_sculpt.c2
-rw-r--r--source/blender/draw/intern/draw_cache.c40
-rw-r--r--source/blender/draw/intern/draw_manager.c2
-rw-r--r--source/blender/draw/intern/draw_manager_data.c43
-rw-r--r--source/blender/editors/datafiles/CMakeLists.txt4
-rw-r--r--source/blender/editors/include/ED_object.h3
-rw-r--r--source/blender/editors/include/UI_icons.h3
-rw-r--r--source/blender/editors/interface/interface.c36
-rw-r--r--source/blender/editors/interface/interface_handlers.c4
-rw-r--r--source/blender/editors/interface/interface_intern.h10
-rw-r--r--source/blender/editors/mesh/editmesh_mask_extract.c59
-rw-r--r--source/blender/editors/mesh/editmesh_polybuild.c4
-rw-r--r--source/blender/editors/mesh/editmesh_tools.c122
-rw-r--r--source/blender/editors/mesh/editmesh_undo.c4
-rw-r--r--source/blender/editors/mesh/editmesh_utils.c1
-rw-r--r--source/blender/editors/mesh/mesh_intern.h2
-rw-r--r--source/blender/editors/mesh/mesh_ops.c3
-rw-r--r--source/blender/editors/object/object_modifier.c22
-rw-r--r--source/blender/editors/object/object_remesh.cc8
-rw-r--r--source/blender/editors/object/object_vgroup.c3
-rw-r--r--source/blender/editors/sculpt_paint/CMakeLists.txt10
-rw-r--r--source/blender/editors/sculpt_paint/paint_cursor.c23
-rw-r--r--source/blender/editors/sculpt_paint/paint_hide.c20
-rw-r--r--source/blender/editors/sculpt_paint/paint_image_proj.c15
-rw-r--r--source/blender/editors/sculpt_paint/paint_intern.h91
-rw-r--r--source/blender/editors/sculpt_paint/paint_mask.c90
-rw-r--r--source/blender/editors/sculpt_paint/paint_stroke.c165
-rw-r--r--source/blender/editors/sculpt_paint/paint_vertex.c22
-rw-r--r--source/blender/editors/sculpt_paint/sculpt.c4188
-rw-r--r--source/blender/editors/sculpt_paint/sculpt.cc20
-rw-r--r--source/blender/editors/sculpt_paint/sculpt.hh474
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_array.c636
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_automasking.c260
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_boundary.c1322
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_brush_machine.c0
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_cloth.c97
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_curvature.c261
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_detail.c104
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_displacement.c1
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_displacement.h1
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_dyntopo.c1632
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_expand.c535
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_face_set.c801
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_face_set_topology.c7
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_filter_color.c10
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_filter_mask.c103
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_filter_mesh.c71
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_geodesic.c781
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_gradient.c4
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_intern.h550
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_mask_expand.c26
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_mask_init.c2
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_multiplane_scrape.c5
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_paint_color.c29
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_poly_loop.c50
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_pose.c135
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_replay.c1228
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_smooth.c1369
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_symmetrize.c84
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_transform.c8
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_undo.c1148
-rw-r--r--source/blender/editors/space_info/info_stats.c16
-rw-r--r--source/blender/editors/util/ed_util.c2
-rw-r--r--source/blender/editors/uvedit/uvedit_unwrap_ops.c5
-rw-r--r--source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c3
-rw-r--r--source/blender/gpu/GPU_buffers.h30
-rw-r--r--source/blender/gpu/GPU_vertex_format.h2
-rw-r--r--source/blender/gpu/intern/gpu_buffers.c1449
-rw-r--r--source/blender/gpu/intern/gpu_node_graph.c2
-rw-r--r--source/blender/io/alembic/exporter/abc_writer_mesh.cc2
-rw-r--r--source/blender/io/collada/collada_utils.cpp4
-rw-r--r--source/blender/makesdna/DNA_brush_defaults.h18
-rw-r--r--source/blender/makesdna/DNA_brush_enums.h70
-rw-r--r--source/blender/makesdna/DNA_brush_types.h34
-rw-r--r--source/blender/makesdna/DNA_customdata_types.h13
-rw-r--r--source/blender/makesdna/DNA_mesh_types.h2
-rw-r--r--source/blender/makesdna/DNA_meshdata_types.h36
-rw-r--r--source/blender/makesdna/DNA_modifier_types.h12
-rw-r--r--source/blender/makesdna/DNA_node_types.h18
-rw-r--r--source/blender/makesdna/DNA_scene_defaults.h2
-rw-r--r--source/blender/makesdna/DNA_scene_types.h34
-rw-r--r--source/blender/makesdna/DNA_sculpt_brush_types.h84
-rw-r--r--source/blender/makesdna/DNA_userdef_types.h3
-rw-r--r--source/blender/makesdna/intern/CMakeLists.txt1
-rw-r--r--source/blender/makesdna/intern/makesdna.c2
-rw-r--r--source/blender/makesrna/RNA_access.h2
-rw-r--r--source/blender/makesrna/intern/CMakeLists.txt1
-rw-r--r--source/blender/makesrna/intern/makesrna.c1
-rw-r--r--source/blender/makesrna/intern/rna_brush.c357
-rw-r--r--source/blender/makesrna/intern/rna_brush_engine.c116
-rw-r--r--source/blender/makesrna/intern/rna_internal.h1
-rw-r--r--source/blender/makesrna/intern/rna_mesh.c6
-rw-r--r--source/blender/makesrna/intern/rna_modifier.c37
-rw-r--r--source/blender/makesrna/intern/rna_scene.c13
-rw-r--r--source/blender/makesrna/intern/rna_sculpt_paint.c144
-rw-r--r--source/blender/makesrna/intern/rna_userdef.c4
-rw-r--r--source/blender/modifiers/intern/MOD_bevel.c2
-rw-r--r--source/blender/modifiers/intern/MOD_boolean.cc6
-rw-r--r--source/blender/modifiers/intern/MOD_correctivesmooth.c55
-rw-r--r--source/blender/modifiers/intern/MOD_datatransfer.c17
-rw-r--r--source/blender/modifiers/intern/MOD_decimate.c3
-rw-r--r--source/blender/modifiers/intern/MOD_edgesplit.c5
-rw-r--r--source/blender/modifiers/intern/MOD_particlesystem.c2
-rw-r--r--source/blender/modifiers/intern/MOD_simulation.cc194
-rw-r--r--source/blender/modifiers/intern/MOD_skin.c2
-rw-r--r--source/blender/modifiers/intern/MOD_triangulate.c8
-rw-r--r--source/blender/modifiers/intern/MOD_uvwarp.c16
-rw-r--r--source/blender/modifiers/intern/MOD_wireframe.c2
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cube.cc1
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_ico_sphere.cc4
-rw-r--r--source/blender/python/bmesh/bmesh_py_types.c10
-rw-r--r--source/blender/python/intern/bpy_msgbus.c14
-rw-r--r--source/blender/render/intern/bake.c6
-rw-r--r--source/blender/windowmanager/intern/wm_draw.c38
-rw-r--r--source/blender/windowmanager/intern/wm_files.c6
-rw-r--r--source/creator/creator.c2
m---------source/tools0
254 files changed, 39427 insertions, 5193 deletions
diff --git a/.clang-format b/.clang-format
index bf20a4e4c4a..8e2bb41bb3a 100644
--- a/.clang-format
+++ b/.clang-format
@@ -263,6 +263,7 @@ ForEachMacros:
- SET_SLOT_PROBING_BEGIN
- MAP_SLOT_PROBING_BEGIN
- VECTOR_SET_SLOT_PROBING_BEGIN
+ - TGSET_ITER
StatementMacros:
- PyObject_HEAD
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 47712f0ac1e..83e86cae84a 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -578,6 +578,12 @@ if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_C_COMPILER_ID MATCHES "Clang")
[HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\LLVM\\LLVM;]/lib/clang/7.0.0/lib/windows
[HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\LLVM\\LLVM;]/lib/clang/6.0.0/lib/windows
)
+ find_library(
+ COMPILER_ASAN_LIBRARY_THUNK NAMES clang_rt.asan_dll_thunk-x86_64
+ PATHS
+ [HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\LLVM\\LLVM;]/lib/clang/7.0.0/lib/windows
+ [HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\LLVM\\LLVM;]/lib/clang/6.0.0/lib/windows
+ )
elseif(APPLE)
execute_process(COMMAND ${CMAKE_CXX_COMPILER}
-print-file-name=lib
@@ -598,6 +604,7 @@ if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_C_COMPILER_ID MATCHES "Clang")
)
endif()
+ mark_as_advanced(COMPILER_ASAN_LIBRARY_THUNK)
mark_as_advanced(COMPILER_ASAN_LIBRARY)
endif()
endif()
@@ -918,9 +925,9 @@ if(NOT CMAKE_BUILD_TYPE MATCHES "Release")
unset(_list_COMPILER_ASAN_CFLAGS)
unset(_is_CONFIG_DEBUG)
elseif(COMPILER_ASAN_LIBRARY)
- set(PLATFORM_LINKLIBS "${PLATFORM_LINKLIBS};${COMPILER_ASAN_LIBRARY}")
- set(PLATFORM_LINKFLAGS "${COMPILER_ASAN_LIBRARY} ${COMPILER_ASAN_LINKER_FLAGS}")
- set(PLATFORM_LINKFLAGS_DEBUG "${COMPILER_ASAN_LIBRARY} ${COMPILER_ASAN_LINKER_FLAGS}")
+ set(PLATFORM_LINKLIBS "${PLATFORM_LINKLIBS};\"${COMPILER_ASAN_LIBRARY}\" \"${COMPILER_ASAN_LIBRARY_THUNK}\"")
+ set(PLATFORM_LINKFLAGS "\"${COMPILER_ASAN_LIBRARY}\" \"${COMPILER_ASAN_LIBRARY_THUNK}\" ${COMPILER_ASAN_LINKER_FLAGS}")
+ set(PLATFORM_LINKFLAGS_DEBUG "\"${COMPILER_ASAN_LIBRARY}\" \"${COMPILER_ASAN_LIBRARY_THUNK}\" ${COMPILER_ASAN_LINKER_FLAGS}")
endif()
endif()
endif()
diff --git a/extern/Eigen3/Eigen/src/Core/util/Macros.h b/extern/Eigen3/Eigen/src/Core/util/Macros.h
index aa054a0b7ff..217051cc41f 100644
--- a/extern/Eigen3/Eigen/src/Core/util/Macros.h
+++ b/extern/Eigen3/Eigen/src/Core/util/Macros.h
@@ -389,7 +389,7 @@
// Does the compiler support result_of?
#ifndef EIGEN_HAS_STD_RESULT_OF
-#if EIGEN_MAX_CPP_VER>=11 && ((__has_feature(cxx_lambdas) || (defined(__cplusplus) && __cplusplus >= 201103L)))
+#if __cplusplus < 201703L && EIGEN_MAX_CPP_VER>=11 && ((__has_feature(cxx_lambdas) || (defined(__cplusplus) && __cplusplus >= 201103L && __cplusplus)))
#define EIGEN_HAS_STD_RESULT_OF 1
#else
#define EIGEN_HAS_STD_RESULT_OF 0
diff --git a/extern/audaspace/bindings/C/AUD_Device.cpp b/extern/audaspace/bindings/C/AUD_Device.cpp
index d4643094bc2..80a2f4c1fd8 100644
--- a/extern/audaspace/bindings/C/AUD_Device.cpp
+++ b/extern/audaspace/bindings/C/AUD_Device.cpp
@@ -221,7 +221,7 @@ AUD_API void AUD_Device_setListenerVelocity(AUD_Device* device, const float valu
AUD_API double AUD_Device_getRate(AUD_Device* device)
{
auto dev = device ? *device : DeviceManager::getDevice();
- return dev->getSpecs().rate;
+ return dev ? dev->getSpecs().rate : 0.0;
}
AUD_API float AUD_Device_getSpeedOfSound(AUD_Device* device)
diff --git a/extern/audaspace/include/util/ThreadPool.h b/extern/audaspace/include/util/ThreadPool.h
index 24ec089d52c..7b526f10775 100644
--- a/extern/audaspace/include/util/ThreadPool.h
+++ b/extern/audaspace/include/util/ThreadPool.h
@@ -87,11 +87,17 @@ public:
* \param args The arguments of the task.
* \return A future of the same type as the return type of the task.
*/
+#if __cplusplus > 201703L
+ template<class T, class... Args>
+ std::future<typename std::invoke_result<T, Args...>::type> enqueue(T&& t, Args&&... args)
+ {
+ using pkgdTask = std::packaged_task<typename std::invoke_result<T, Args...>::type()>;
+#else
template<class T, class... Args>
std::future<typename std::result_of<T(Args...)>::type> enqueue(T&& t, Args&&... args)
{
using pkgdTask = std::packaged_task<typename std::result_of<T(Args...)>::type()>;
-
+#endif
std::shared_ptr<pkgdTask> task = std::make_shared<pkgdTask>(std::bind(std::forward<T>(t), std::forward<Args>(args)...));
auto result = task->get_future();
diff --git a/extern/quadriflow/3rd/lemon-1.3.1/CMakeLists.txt b/extern/quadriflow/3rd/lemon-1.3.1/CMakeLists.txt
index b20b163a16a..7aa6d430906 100644
--- a/extern/quadriflow/3rd/lemon-1.3.1/CMakeLists.txt
+++ b/extern/quadriflow/3rd/lemon-1.3.1/CMakeLists.txt
@@ -67,7 +67,7 @@ SET(LEMON_ENABLE_ILOG YES CACHE STRING "Enable ILOG (CPLEX) solver backend.")
SET(LEMON_ENABLE_COIN YES CACHE STRING "Enable COIN solver backend.")
SET(LEMON_ENABLE_SOPLEX YES CACHE STRING "Enable SoPlex solver backend.")
-IF(LEMON_ENABLE_GLPK)
+IF(LEMON_ENABLE_GLPK)
FIND_PACKAGE(GLPK 4.33)
ENDIF(LEMON_ENABLE_GLPK)
IF(LEMON_ENABLE_ILOG)
diff --git a/extern/quadriflow/3rd/lemon-1.3.1/cmake/FindILOG.cmake b/extern/quadriflow/3rd/lemon-1.3.1/cmake/FindILOG.cmake
index 584df4f6994..a09fc9a2753 100644
--- a/extern/quadriflow/3rd/lemon-1.3.1/cmake/FindILOG.cmake
+++ b/extern/quadriflow/3rd/lemon-1.3.1/cmake/FindILOG.cmake
@@ -4,7 +4,7 @@ FIND_PATH(ILOG_ROOT_DIR
PATHS /opt/ibm/ILOG /usr/local/ibm/ILOG /usr/local/ILOG /usr/local/ilog
PATHS "$ENV{HOME}/ILOG" "$ENV{HOME}/.local/ILOG"
PATHS "$ENV{HOME}/ibm/ILOG" "$ENV{HOME}/.local/ibm/ILOG"
- PATHS "C:/Program Files/IBM/ILOG"
+ PATHS "C:/Program Files/IBM/ILOG"
PATH_SUFFIXES "CPLEX_Studio126" "CPLEX_Studio125"
"CPLEX_Studio124" "CPLEX_Studio123" "CPLEX_Studio122"
NO_DEFAULT_PATH
diff --git a/extern/quadriflow/3rd/lemon-1.3.1/contrib/CMakeLists.txt b/extern/quadriflow/3rd/lemon-1.3.1/contrib/CMakeLists.txt
index fd393bcc420..b6c11e2aad4 100644
--- a/extern/quadriflow/3rd/lemon-1.3.1/contrib/CMakeLists.txt
+++ b/extern/quadriflow/3rd/lemon-1.3.1/contrib/CMakeLists.txt
@@ -16,3 +16,4 @@ LINK_DIRECTORIES(
# ADD_EXECUTABLE(myprog myprog-main.cc)
# TARGET_LINK_LIBRARIES(myprog lemon)
+
diff --git a/extern/quadriflow/3rd/lemon-1.3.1/lemon/CMakeLists.txt b/extern/quadriflow/3rd/lemon-1.3.1/lemon/CMakeLists.txt
index f3501ca865b..4e6567e49c7 100644
--- a/extern/quadriflow/3rd/lemon-1.3.1/lemon/CMakeLists.txt
+++ b/extern/quadriflow/3rd/lemon-1.3.1/lemon/CMakeLists.txt
@@ -88,3 +88,4 @@ INSTALL(
FILES ${CMAKE_CURRENT_BINARY_DIR}/lemon.pc
DESTINATION lib/pkgconfig
)
+
diff --git a/extern/quadriflow/src/loader.cpp b/extern/quadriflow/src/loader.cpp
index a1596eeff9a..1aa50a40fc3 100644
--- a/extern/quadriflow/src/loader.cpp
+++ b/extern/quadriflow/src/loader.cpp
@@ -10,6 +10,7 @@
#include <fstream>
#include <unordered_map>
+#include <functional>
namespace qflow {
@@ -69,7 +70,7 @@ void load(const char* filename, MatrixXd& V, MatrixXi& F)
};
/// Hash function for obj_vertex
- struct obj_vertexHash {
+ struct obj_vertexHash : std::function<size_t(obj_vertex)> {
std::size_t operator()(const obj_vertex &v) const {
size_t hash = std::hash<uint32_t>()(v.p);
hash = hash * 37 + std::hash<uint32_t>()(v.uv);
diff --git a/intern/atomic/intern/atomic_ops_msvc.h b/intern/atomic/intern/atomic_ops_msvc.h
index c9ad1a46ab9..57589d9bcc3 100644
--- a/intern/atomic/intern/atomic_ops_msvc.h
+++ b/intern/atomic/intern/atomic_ops_msvc.h
@@ -49,27 +49,27 @@
/* 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) + (int64_t)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)) - (int64_t)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_fetch_and_add_uint64(uint64_t *p, uint64_t x)
{
- return 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 InterlockedExchangeAdd64((int64_t *)p, -((int64_t)x));
+ return (uint64_t)InterlockedExchangeAdd64((int64_t *)p, -((int64_t)x));
}
/* Signed */
@@ -103,32 +103,32 @@ 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_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 */
@@ -205,9 +205,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
}
@@ -215,9 +215,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/intern/guardedalloc/CMakeLists.txt b/intern/guardedalloc/CMakeLists.txt
index 88c6e7ca2c5..3e93b82945f 100644
--- a/intern/guardedalloc/CMakeLists.txt
+++ b/intern/guardedalloc/CMakeLists.txt
@@ -21,6 +21,7 @@
set(INC
.
../atomic
+ ../../source/blender/blenlib
)
set(INC_SYS
diff --git a/intern/guardedalloc/intern/mallocn_guarded_impl.c b/intern/guardedalloc/intern/mallocn_guarded_impl.c
index 98a8553a3eb..2034f8a3874 100644
--- a/intern/guardedalloc/intern/mallocn_guarded_impl.c
+++ b/intern/guardedalloc/intern/mallocn_guarded_impl.c
@@ -409,7 +409,7 @@ static void print_memhead_backtrace(MemHead *memh)
(void)memh; /* Ignored. */
}
# endif /* defined(__linux__) || defined(__APPLE__) */
-#endif /* DEBUG_BACKTRACE */
+#endif /* DEBUG_BACKTRACE */
static void make_memhead_header(MemHead *memh, size_t len, const char *str)
{
diff --git a/intern/guardedalloc/intern/mallocn_lockfree_impl.c b/intern/guardedalloc/intern/mallocn_lockfree_impl.c
index a843086a1f1..fbde663440a 100644
--- a/intern/guardedalloc/intern/mallocn_lockfree_impl.c
+++ b/intern/guardedalloc/intern/mallocn_lockfree_impl.c
@@ -30,6 +30,7 @@
/* to ensure strict conversions */
#include "../../source/blender/blenlib/BLI_strict_flags.h"
+#include "../../source/blender/blenlib/BLI_asan.h"
#include "atomic_ops.h"
#include "mallocn_intern.h"
@@ -59,6 +60,9 @@ enum {
#define MEMHEAD_ALIGNED_FROM_PTR(ptr) (((MemHeadAligned *)ptr) - 1)
#define MEMHEAD_IS_ALIGNED(memhead) ((memhead)->len & (size_t)MEMHEAD_ALIGN_FLAG)
+#define MEM_POISON_MEMHEAD(vmemh) BLI_asan_poison(MEMHEAD_FROM_PTR(vmemh), sizeof(MemHead))
+#define MEM_UNPOISON_MEMHEAD(vmemh) BLI_asan_unpoison(MEMHEAD_FROM_PTR(vmemh), sizeof(MemHead))
+
/* Uncomment this to have proper peak counter. */
#define USE_ATOMIC_MAX
@@ -93,7 +97,13 @@ print_error(const char *str, ...)
size_t MEM_lockfree_allocN_len(const void *vmemh)
{
if (vmemh) {
- return MEMHEAD_FROM_PTR(vmemh)->len & ~((size_t)(MEMHEAD_ALIGN_FLAG));
+ size_t ret;
+
+ MEM_UNPOISON_MEMHEAD(vmemh);
+ ret = MEMHEAD_FROM_PTR(vmemh)->len & ~((size_t)(MEMHEAD_ALIGN_FLAG));
+ MEM_POISON_MEMHEAD(vmemh);
+
+ return ret;
}
return 0;
@@ -119,6 +129,8 @@ void MEM_lockfree_freeN(void *vmemh)
atomic_sub_and_fetch_u(&totblock, 1);
atomic_sub_and_fetch_z(&mem_in_use, len);
+ MEM_UNPOISON_MEMHEAD(vmemh);
+
if (UNLIKELY(malloc_debug_memset && len)) {
memset(memh + 1, 255, len);
}
@@ -137,6 +149,9 @@ void *MEM_lockfree_dupallocN(const void *vmemh)
if (vmemh) {
MemHead *memh = MEMHEAD_FROM_PTR(vmemh);
const size_t prev_size = MEM_lockfree_allocN_len(vmemh);
+
+ MEM_UNPOISON_MEMHEAD(vmemh);
+
if (UNLIKELY(MEMHEAD_IS_ALIGNED(memh))) {
MemHeadAligned *memh_aligned = MEMHEAD_ALIGNED_FROM_PTR(vmemh);
newp = MEM_lockfree_mallocN_aligned(
@@ -145,6 +160,8 @@ void *MEM_lockfree_dupallocN(const void *vmemh)
else {
newp = MEM_lockfree_mallocN(prev_size, "dupli_malloc");
}
+
+ MEM_POISON_MEMHEAD(vmemh);
memcpy(newp, vmemh, prev_size);
}
return newp;
@@ -158,6 +175,8 @@ void *MEM_lockfree_reallocN_id(void *vmemh, size_t len, const char *str)
MemHead *memh = MEMHEAD_FROM_PTR(vmemh);
size_t old_len = MEM_lockfree_allocN_len(vmemh);
+ MEM_UNPOISON_MEMHEAD(vmemh);
+
if (LIKELY(!MEMHEAD_IS_ALIGNED(memh))) {
newp = MEM_lockfree_mallocN(len, "realloc");
}
@@ -166,6 +185,8 @@ void *MEM_lockfree_reallocN_id(void *vmemh, size_t len, const char *str)
newp = MEM_lockfree_mallocN_aligned(len, (size_t)memh_aligned->alignment, "realloc");
}
+ MEM_POISON_MEMHEAD(vmemh);
+
if (newp) {
if (len < old_len) {
/* shrink */
@@ -194,6 +215,8 @@ void *MEM_lockfree_recallocN_id(void *vmemh, size_t len, const char *str)
MemHead *memh = MEMHEAD_FROM_PTR(vmemh);
size_t old_len = MEM_lockfree_allocN_len(vmemh);
+ MEM_UNPOISON_MEMHEAD(vmemh);
+
if (LIKELY(!MEMHEAD_IS_ALIGNED(memh))) {
newp = MEM_lockfree_mallocN(len, "recalloc");
}
@@ -201,6 +224,7 @@ void *MEM_lockfree_recallocN_id(void *vmemh, size_t len, const char *str)
MemHeadAligned *memh_aligned = MEMHEAD_ALIGNED_FROM_PTR(vmemh);
newp = MEM_lockfree_mallocN_aligned(len, (size_t)memh_aligned->alignment, "recalloc");
}
+ MEM_POISON_MEMHEAD(vmemh);
if (newp) {
if (len < old_len) {
@@ -241,6 +265,7 @@ void *MEM_lockfree_callocN(size_t len, const char *str)
atomic_add_and_fetch_z(&mem_in_use, len);
update_maximum(&peak_mem, mem_in_use);
+ MEM_POISON_MEMHEAD(PTR_FROM_MEMHEAD(memh));
return PTR_FROM_MEMHEAD(memh);
}
print_error("Calloc returns null: len=" SIZET_FORMAT " in %s, total %u\n",
@@ -286,6 +311,8 @@ void *MEM_lockfree_mallocN(size_t len, const char *str)
atomic_add_and_fetch_z(&mem_in_use, len);
update_maximum(&peak_mem, mem_in_use);
+ MEM_POISON_MEMHEAD(PTR_FROM_MEMHEAD(memh));
+
return PTR_FROM_MEMHEAD(memh);
}
print_error("Malloc returns null: len=" SIZET_FORMAT " in %s, total %u\n",
@@ -357,6 +384,8 @@ void *MEM_lockfree_mallocN_aligned(size_t len, size_t alignment, const char *str
atomic_add_and_fetch_z(&mem_in_use, len);
update_maximum(&peak_mem, mem_in_use);
+ MEM_POISON_MEMHEAD(PTR_FROM_MEMHEAD(memh));
+
return PTR_FROM_MEMHEAD(memh);
}
print_error("Malloc returns null: len=" SIZET_FORMAT " in %s, total %u\n",
diff --git a/intern/quadriflow/quadriflow_capi.cpp b/intern/quadriflow/quadriflow_capi.cpp
index 086d5f7d296..d00f42ed218 100644
--- a/intern/quadriflow/quadriflow_capi.cpp
+++ b/intern/quadriflow/quadriflow_capi.cpp
@@ -70,9 +70,9 @@ static int check_if_canceled(float progress,
return cancel;
}
-void QFLOW_quadriflow_remesh(QuadriflowRemeshData *qrd,
- void (*update_cb)(void *, float progress, int *cancel),
- void *update_cb_data)
+ATTR_NO_OPT void QFLOW_quadriflow_remesh(QuadriflowRemeshData *qrd,
+ void (*update_cb)(void *, float progress, int *cancel),
+ void *update_cb_data)
{
Parametrizer field;
VertexMap vertexMap;
@@ -80,6 +80,12 @@ void QFLOW_quadriflow_remesh(QuadriflowRemeshData *qrd,
/* Get remeshing parameters. */
int faces = qrd->target_faces;
+ field.flag_adaptive_scale = 1;
+ field.flag_minimum_cost_flow = 1;
+ field.flag_preserve_boundary = 1;
+ field.flag_preserve_sharp = 1;
+ // field.flag_aggresive_sat = 1;
+
if (qrd->preserve_sharp) {
field.flag_preserve_sharp = 1;
}
@@ -106,6 +112,7 @@ void QFLOW_quadriflow_remesh(QuadriflowRemeshData *qrd,
/* Copy mesh to quadriflow data structures. */
std::vector<Vector3d> positions;
std::vector<uint32_t> indices;
+ std::vector<uint32_t> eflags;
std::vector<ObjVertex> vertices;
for (int i = 0; i < qrd->totverts; i++) {
@@ -114,16 +121,18 @@ void QFLOW_quadriflow_remesh(QuadriflowRemeshData *qrd,
}
for (int q = 0; q < qrd->totfaces; q++) {
- Vector3i f(qrd->faces[q * 3], qrd->faces[q * 3 + 1], qrd->faces[q * 3 + 2]);
+ Vector3i f(qrd->faces[q].v[0], qrd->faces[q].v[1], qrd->faces[q].v[2]);
ObjVertex tri[6];
- int nVertices = 3;
+ const int nVertices = 3;
tri[0] = ObjVertex(f[0]);
tri[1] = ObjVertex(f[1]);
tri[2] = ObjVertex(f[2]);
for (int i = 0; i < nVertices; ++i) {
+ eflags.push_back(qrd->faces[q].eflag[i]);
+
const ObjVertex &v = tri[i];
VertexMap::const_iterator it = vertexMap.find(v);
if (it == vertexMap.end()) {
@@ -138,7 +147,10 @@ void QFLOW_quadriflow_remesh(QuadriflowRemeshData *qrd,
}
field.F.resize(3, indices.size() / 3);
+ // field.FF.resize(3, indices.size() / 3);
+
memcpy(field.F.data(), indices.data(), sizeof(uint32_t) * indices.size());
+ // memcpy(field.FF.data(), eflags.data(), sizeof(uint32_t) * eflags.size());
field.V.resize(3, vertices.size());
for (uint32_t i = 0; i < vertices.size(); ++i) {
@@ -157,12 +169,17 @@ void QFLOW_quadriflow_remesh(QuadriflowRemeshData *qrd,
return;
}
+ const int steps = 2;
+
/* Setup mesh boundary constraints if needed */
- if (field.flag_preserve_boundary) {
+#if 0
+ if (true) { // field.flag_preserve_boundary) {
Hierarchy &mRes = field.hierarchy;
mRes.clearConstraints();
+
for (uint32_t i = 0; i < 3 * mRes.mF.cols(); ++i) {
- if (mRes.mE2E[i] == -1) {
+ if (mRes.mFF((i) % 3, i / 3) & QFLOW_CONSTRAINED) {
+ // if (mRes.mE2E[i] == -1) {
uint32_t i0 = mRes.mF(i % 3, i / 3);
uint32_t i1 = mRes.mF((i + 1) % 3, i / 3);
Vector3d p0 = mRes.mV[0].col(i0), p1 = mRes.mV[0].col(i1);
@@ -172,15 +189,20 @@ void QFLOW_quadriflow_remesh(QuadriflowRemeshData *qrd,
mRes.mCO[0].col(i0) = p0;
mRes.mCO[0].col(i1) = p1;
mRes.mCQ[0].col(i0) = mRes.mCQ[0].col(i1) = edge;
- mRes.mCQw[0][i0] = mRes.mCQw[0][i1] = mRes.mCOw[0][i0] = mRes.mCOw[0][i1] = 1.0;
+ mRes.mCQw[0][i0] = mRes.mCQw[0][i1] = mRes.mCOw[0][i0] = mRes.mCOw[0][i1] = 0.1;
}
}
}
- mRes.propagateConstraints();
+ for (int j = 0; j < 10; j++) {
+ mRes.propagateConstraints();
+ }
}
+#endif
/* Optimize the mesh field orientations (tangental field etc) */
- Optimizer::optimize_orientations(field.hierarchy);
+ for (int i = 0; i < steps; i++) {
+ Optimizer::optimize_orientations(field.hierarchy);
+ }
field.ComputeOrientationSingularities();
if (check_if_canceled(0.3f, update_cb, update_cb_data)) {
@@ -195,11 +217,13 @@ void QFLOW_quadriflow_remesh(QuadriflowRemeshData *qrd,
return;
}
- Optimizer::optimize_scale(field.hierarchy, field.rho, field.flag_adaptive_scale);
- field.flag_adaptive_scale = 1;
-
- Optimizer::optimize_positions(field.hierarchy, field.flag_adaptive_scale);
+ for (int i = 0; i < steps; i++) {
+ Optimizer::optimize_scale(field.hierarchy, field.rho, field.flag_adaptive_scale);
+ }
+ for (int i = 0; i < steps; i++) {
+ Optimizer::optimize_positions(field.hierarchy, field.flag_adaptive_scale);
+ }
field.ComputePositionSingularities();
if (check_if_canceled(0.5f, update_cb, update_cb_data)) {
diff --git a/intern/quadriflow/quadriflow_capi.hpp b/intern/quadriflow/quadriflow_capi.hpp
index 59af2826e15..563c25b4a84 100644
--- a/intern/quadriflow/quadriflow_capi.hpp
+++ b/intern/quadriflow/quadriflow_capi.hpp
@@ -23,9 +23,16 @@
extern "C" {
#endif
+enum { QFLOW_CONSTRAINED = 1 };
+
+typedef struct QuadriflowFace {
+ int v[3];
+ char eflag[3];
+} QuadriflowFace;
+
typedef struct QuadriflowRemeshData {
float *verts;
- int *faces;
+ QuadriflowFace *faces;
int totfaces;
int totverts;
diff --git a/release/datafiles/icons/brush.sculpt.displacement_smear.dat b/release/datafiles/icons/brush.sculpt.displacement_smear.dat
deleted file mode 100644
index 5d422130ea3..00000000000
--- a/release/datafiles/icons/brush.sculpt.displacement_smear.dat
+++ /dev/null
Binary files differ
diff --git a/release/datafiles/icons/brush.sculpt.draw_sharp.dat b/release/datafiles/icons/brush.sculpt.draw_sharp.dat
index 1877c0ae4d4..9bea1b02894 100644
--- a/release/datafiles/icons/brush.sculpt.draw_sharp.dat
+++ b/release/datafiles/icons/brush.sculpt.draw_sharp.dat
Binary files differ
diff --git a/release/datafiles/icons/brush.sculpt.paint.dat b/release/datafiles/icons/brush.sculpt.paint.dat
new file mode 100644
index 00000000000..ef5f5fe851a
--- /dev/null
+++ b/release/datafiles/icons/brush.sculpt.paint.dat
Binary files differ
diff --git a/release/datafiles/icons/brush.sculpt.smear.dat b/release/datafiles/icons/brush.sculpt.smear.dat
new file mode 100644
index 00000000000..c7214fb863b
--- /dev/null
+++ b/release/datafiles/icons/brush.sculpt.smear.dat
Binary files differ
diff --git a/release/datafiles/icons/brush.sculpt.vcol_boundary.dat b/release/datafiles/icons/brush.sculpt.vcol_boundary.dat
new file mode 100644
index 00000000000..41c69e4dda8
--- /dev/null
+++ b/release/datafiles/icons/brush.sculpt.vcol_boundary.dat
Binary files differ
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 ec8f2432052..00000000000
--- a/release/datafiles/icons/ops.armature.extrude.cursor.dat
+++ /dev/null
Binary files differ
diff --git a/release/datafiles/icons/ops.armature.extrude.dat b/release/datafiles/icons/ops.armature.extrude.dat
deleted file mode 100644
index 2194c5bf556..00000000000
--- a/release/datafiles/icons/ops.armature.extrude.dat
+++ /dev/null
Binary files differ
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 509ca7c9440..00000000000
--- a/release/datafiles/icons/ops.curve.dupli_extrude_cursor.dat
+++ /dev/null
Binary files differ
diff --git a/release/datafiles/locale b/release/datafiles/locale
-Subproject 62e82958a760dad775d9b3387d7fb535fd6de4c
+Subproject 326997b913d04bc5bc4656973d1e1a819f860dd
diff --git a/release/scripts/addons b/release/scripts/addons
-Subproject 4475cbd11a636382d57571e0f5dfeff1f90bd6b
+Subproject 59c8409947c4174983a36ec28dfeda2be9e254d
diff --git a/release/scripts/addons_contrib b/release/scripts/addons_contrib
-Subproject 788441f2930465bbfba8f0797b12dcef1d46694
+Subproject 98f6085e9d71ba35d41e5aafbcb7981bd7c4827
diff --git a/release/scripts/startup/bl_ui/properties_paint_common.py b/release/scripts/startup/bl_ui/properties_paint_common.py
index 63032b64c63..bf29c3f8159 100644
--- a/release/scripts/startup/bl_ui/properties_paint_common.py
+++ b/release/scripts/startup/bl_ui/properties_paint_common.py
@@ -549,15 +549,27 @@ def brush_settings(layout, context, brush, popover=False):
if context.preferences.experimental.use_sculpt_tools_tilt and capabilities.has_tilt:
layout.prop(brush, "tilt_strength_factor", slider=True)
+ UnifiedPaintPanel.prop_unified(
+ layout,
+ context,
+ brush,
+ "hard_edge_mode",
+ slider=True,
+ unified_name="use_unified_hard_edge_mode",
+ )
+
row = layout.row(align=True)
+
row.prop(brush, "hardness", slider=True)
row.prop(brush, "invert_hardness_pressure", text="")
row.prop(brush, "use_hardness_pressure", text="")
# auto_smooth_factor and use_inverse_smooth_pressure
if capabilities.has_auto_smooth:
+ box = layout.box().column() #.column() is a bit more compact
+
UnifiedPaintPanel.prop_unified(
- layout,
+ box,
context,
brush,
"auto_smooth_factor",
@@ -565,13 +577,70 @@ def brush_settings(layout, context, brush, popover=False):
slider=True,
)
- # topology_rake_factor
+ box.prop(brush, "boundary_smooth_factor")
+ box.prop(brush, "use_weighted_smooth")
+ box.prop(brush, "preserve_faceset_boundary")
+
+ if brush.preserve_faceset_boundary:
+ box.prop(brush, "autosmooth_fset_slide")
+
+ box.prop(brush, "use_custom_auto_smooth_spacing", text="Custom Spacing")
+ if brush.use_custom_auto_smooth_spacing:
+ UnifiedPaintPanel.prop_unified(
+ box,
+ context,
+ brush,
+ "auto_smooth_spacing",
+ slider=True,
+ text="Spacing"
+ )
+ UnifiedPaintPanel.prop_unified(
+ box,
+ context,
+ brush,
+ "auto_smooth_projection",
+ slider=True
+ )
+ UnifiedPaintPanel.prop_unified(
+ box,
+ context,
+ brush,
+ "auto_smooth_radius_factor",
+ slider=True
+ )
+ elif brush.sculpt_tool == "SMOOTH":
+ UnifiedPaintPanel.prop_unified(
+ layout,
+ context,
+ brush,
+ "auto_smooth_projection",
+ slider=True
+ )
+
+
+ if capabilities.has_vcol_boundary_smooth:
+ layout.prop(brush, "vcol_boundary_factor", slider=True)
+
if (
capabilities.has_topology_rake and
context.sculpt_object.use_dynamic_topology_sculpting
):
- layout.prop(brush, "topology_rake_factor", slider=True)
-
+ box = layout.box().column() #.column() is a bit more compact
+
+ box.prop(brush, "topology_rake_factor", slider=True)
+ box.prop(brush, "use_custom_topology_rake_spacing", text="Custom Spacing")
+
+ if brush.use_custom_topology_rake_spacing:
+ box.prop(brush, "topology_rake_spacing", text="Spacing")
+ box.prop(brush, "topology_rake_projection")
+
+ box.prop(brush, "topology_rake_radius_factor", slider=True)
+ box.prop(brush, "use_curvature_rake")
+ box.prop(brush, "ignore_falloff_for_topology_rake")
+
+ if context.sculpt_object.use_dynamic_topology_sculpting:
+ layout.prop(brush.dyntopo, "disabled", text="Disable Dyntopo")
+
# normal_weight
if capabilities.has_normal_weight:
layout.prop(brush, "normal_weight", slider=True)
@@ -630,6 +699,10 @@ def brush_settings(layout, context, brush, popover=False):
# Per sculpt tool options.
+ if sculpt_tool == "VCOL_BOUNDARY":
+ row = layout.row()
+ row.prop(brush, "vcol_boundary_exponent")
+
if sculpt_tool == 'CLAY_STRIPS':
row = layout.row()
row.prop(brush, "tip_roundness")
@@ -769,7 +842,15 @@ def brush_settings(layout, context, brush, popover=False):
elif sculpt_tool == 'SMOOTH':
col = layout.column()
+ col.prop(brush, "boundary_smooth_factor")
+
+ col.prop(brush, "use_weighted_smooth")
+ col.prop(brush, "preserve_faceset_boundary")
+ if brush.preserve_faceset_boundary:
+ col.prop(brush, "autosmooth_fset_slide")
+
col.prop(brush, "smooth_deform_type")
+
if brush.smooth_deform_type == 'SURFACE':
col.prop(brush, "surface_smooth_shape_preservation")
col.prop(brush, "surface_smooth_current_vertex")
@@ -933,6 +1014,14 @@ def brush_settings_advanced(layout, context, brush, popover=False):
# topology automasking
col.prop(brush, "use_automasking_topology", text="Topology")
+ col.prop(brush, "use_automasking_concave")
+
+ col2 = col.column()
+ col2.enabled = brush.use_automasking_concave
+
+ col2.prop(brush, "concave_mask_factor", text="Cavity Factor")
+ col2.prop(brush, "invert_automasking_concavity", text="Invert Cavity Mask")
+
# face masks automasking
col.prop(brush, "use_automasking_face_sets", text="Face Sets")
diff --git a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py
index 7546be30b48..a128bf8698d 100644
--- a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py
+++ b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py
@@ -1304,7 +1304,9 @@ class _defs_sculpt:
# Use 'bpy.context' instead of 'context' since it can be None.
prefs = bpy.context.preferences
if not prefs.experimental.use_sculpt_vertex_colors:
- exclude_filter = {'PAINT', 'SMEAR'}
+ exclude_filter = {'PAINT' : True, 'SMEAR' : True}
+ if not prefs.experimental.use_sculpt_uvsmooth:
+ exclude_filter['UV_SMOOTH'] = True
return generate_from_enum_ex(
context,
diff --git a/release/scripts/startup/bl_ui/space_userpref.py b/release/scripts/startup/bl_ui/space_userpref.py
index 0093110d326..be16179fdff 100644
--- a/release/scripts/startup/bl_ui/space_userpref.py
+++ b/release/scripts/startup/bl_ui/space_userpref.py
@@ -2248,6 +2248,7 @@ class USERPREF_PT_experimental_new_features(ExperimentalPanel, Panel):
self._draw_items(
context, (
({"property": "use_sculpt_vertex_colors"}, "T71947"),
+ ({"property": "use_sculpt_uvsmooth"}, ""),
({"property": "use_sculpt_tools_tilt"}, "T82877"),
({"property": "use_extended_asset_browser"}, ("project/view/130/", "Project Page")),
({"property": "use_override_templates"}, ("T73318", "Milestone 4")),
diff --git a/release/scripts/startup/bl_ui/space_view3d.py b/release/scripts/startup/bl_ui/space_view3d.py
index 39b780b36af..c13cd0d66f7 100644
--- a/release/scripts/startup/bl_ui/space_view3d.py
+++ b/release/scripts/startup/bl_ui/space_view3d.py
@@ -171,6 +171,7 @@ class VIEW3D_HT_tool_header(Header):
row.popover(panel="VIEW3D_PT_tools_weightpaint_symmetry_for_topbar", text="")
elif mode_string == 'SCULPT':
row.popover(panel="VIEW3D_PT_sculpt_symmetry_for_topbar", text="")
+ layout.prop(context.object.data, "use_fset_boundary_mirror");
elif mode_string == 'PAINT_VERTEX':
row.popover(panel="VIEW3D_PT_tools_vertexpaint_symmetry_for_topbar", text="")
diff --git a/release/scripts/startup/bl_ui/space_view3d_toolbar.py b/release/scripts/startup/bl_ui/space_view3d_toolbar.py
index f9f5aa00f29..92a0c69f00c 100644
--- a/release/scripts/startup/bl_ui/space_view3d_toolbar.py
+++ b/release/scripts/startup/bl_ui/space_view3d_toolbar.py
@@ -17,7 +17,7 @@
# ##### END GPL LICENSE BLOCK #####
# <pep8 compliant>
-from bpy.types import Menu, Panel, UIList
+from bpy.types import Menu, Panel, UIList, WindowManager
from bl_ui.properties_grease_pencil_common import (
GreasePencilSculptOptionsPanel,
GreasePencilDisplayPanel,
@@ -755,9 +755,77 @@ class VIEW3D_PT_tools_brush_falloff_normal(View3DPaintPanel, Panel):
# TODO, move to space_view3d.py
+class VIEW3D_PT_sculpt_dyntopo_advanced(Panel, View3DPaintPanel):
+ bl_context = ".sculpt_mode" # dot on purpose (access from topbar)
+ bl_label = "Dyntopo (Advanced)"
+ #bl_options = {'DEFAULT_CLOSED'}
+ bl_ui_units_x = 12
+
+ @classmethod
+ def poll(cls, context):
+ paint_settings = cls.paint_settings(context)
+ return (context.sculpt_object and context.tool_settings.sculpt and paint_settings)
+
+ def draw_header(self, context):
+ pass
+
+ def draw(self, context):
+ layout = self.layout
+ layout.use_property_split = True
+ layout.use_property_decorate = False
+
+ tool_settings = context.tool_settings
+ sculpt = tool_settings.sculpt
+ settings = self.paint_settings(context)
+ brush = settings.brush
+
+ col = layout.column()
+ col.label(text="Local Brush Settings")
+
+ row = col.row()
+ row.prop(brush.dyntopo, "disabled", text="Disable Dyntopo locally for this brush")
+
+ col.label(text="Overrides")
+ inherit_all = "ALL" in brush.dyntopo.inherit
+
+ col.prop_enum(brush.dyntopo, "inherit", value="ALL", text="Use All Defaults", icon="LOCKED" if inherit_all else "UNLOCKED")
+
+ def do_prop(key):
+ row = col.row()
+ if key.upper() in brush.dyntopo.inherit:
+ icon = "UNLOCKED"
+ else:
+ icon = "LOCKED"
+
+ row.prop_enum(brush.dyntopo, "inherit", value=key.upper(), icon=icon, text="")
+
+ row2 = row.row()
+ row2.prop(brush.dyntopo, key)
+
+ if icon == "UNLOCKED":
+ row2.enabled = False
+
+ if inherit_all:
+ row.enabled = False
+
+ col = layout.column()
+ do_prop("subdivide")
+ do_prop("collapse")
+ do_prop("cleanup")
+ do_prop("spacing")
+ do_prop("local_subdivide")
+ do_prop("local_collapse")
+ do_prop("detail_size")
+ do_prop("detail_range")
+ do_prop("detail_percent")
+ do_prop("constant_detail")
+ do_prop("mode")
+ do_prop("radius_scale")
+
+# 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 Mode"
bl_options = {'DEFAULT_CLOSED'}
bl_ui_units_x = 12
@@ -789,6 +857,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'}:
@@ -806,7 +876,12 @@ class VIEW3D_PT_sculpt_dyntopo(Panel, View3DPaintPanel):
if sculpt.detail_type_method in {'CONSTANT', 'MANUAL'}:
col.operator("sculpt.detail_flood_fill")
+ col.prop(sculpt, "use_dyntopo_cleanup")
col.prop(sculpt, "use_smooth_shading")
+ col.prop(sculpt, "use_flat_vcol_shading")
+
+ col.prop(sculpt, "dyntopo_spacing")
+ col.prop(sculpt, "dyntopo_radius_scale");
class VIEW3D_PT_sculpt_voxel_remesh(Panel, View3DPaintPanel):
@@ -866,6 +941,7 @@ 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_fast_draw")
col.prop(sculpt, "use_deform_only")
col.prop(sculpt, "show_sculpt_pivot")
col.prop(sculpt, "smooth_strength_factor")
@@ -944,6 +1020,7 @@ class VIEW3D_PT_sculpt_symmetry(Panel, View3DPaintPanel):
row.prop(sculpt, "tile_z", text="Z", toggle=True)
layout.prop(sculpt, "use_symmetry_feather", text="Feather")
+ layout.prop(mesh, "use_fset_boundary_mirror")
layout.prop(sculpt, "radial_symmetry", text="Radial")
layout.prop(sculpt, "tile_offset", text="Tile Offset")
@@ -951,6 +1028,7 @@ class VIEW3D_PT_sculpt_symmetry(Panel, View3DPaintPanel):
layout.prop(sculpt, "symmetrize_direction")
layout.operator("sculpt.symmetrize")
+ layout.prop(WindowManager.operator_properties_last("sculpt.symmetrize"), "merge_tolerance")
class VIEW3D_PT_sculpt_symmetry_for_topbar(Panel):
@@ -2284,6 +2362,7 @@ classes = (
VIEW3D_PT_tools_grease_pencil_brush_vertex_color,
VIEW3D_PT_tools_grease_pencil_brush_vertex_palette,
VIEW3D_PT_tools_grease_pencil_brush_vertex_falloff,
+ VIEW3D_PT_sculpt_dyntopo_advanced
)
if __name__ == "__main__": # only for live edit.
diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h
index d71cb559911..63d6b9121d2 100644
--- a/source/blender/blenkernel/BKE_blender_version.h
+++ b/source/blender/blenkernel/BKE_blender_version.h
@@ -39,7 +39,7 @@ extern "C" {
/* Blender file format version. */
#define BLENDER_FILE_VERSION BLENDER_VERSION
-#define BLENDER_FILE_SUBVERSION 22
+#define BLENDER_FILE_SUBVERSION 23
/* Minimum Blender version that supports reading file written with the current
* version. Older Blender versions will test this and show a warning if the file
diff --git a/source/blender/blenkernel/BKE_brush.h b/source/blender/blenkernel/BKE_brush.h
index 8ae036a5288..729956bdbc5 100644
--- a/source/blender/blenkernel/BKE_brush.h
+++ b/source/blender/blenkernel/BKE_brush.h
@@ -36,6 +36,8 @@ struct Main;
struct Scene;
struct ToolSettings;
struct UnifiedPaintSettings;
+struct DynTopoSettings;
+struct Sculpt;
// enum eCurveMappingPreset;
@@ -153,6 +155,13 @@ void BKE_brush_default_input_curves_set(struct Brush *brush);
/* 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);
+
#ifdef __cplusplus
}
#endif
diff --git a/source/blender/blenkernel/BKE_brush_engine.h b/source/blender/blenkernel/BKE_brush_engine.h
new file mode 100644
index 00000000000..5695cae3649
--- /dev/null
+++ b/source/blender/blenkernel/BKE_brush_engine.h
@@ -0,0 +1,129 @@
+#pragma once
+
+/*
+ * 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) 2001-2002 by NaN Holding BV.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup bke
+ * \brief New brush engine for sculpt
+ */
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "RNA_types.h"
+
+/*
+The new brush engine is based on command lists. These lists
+will eventually be created by a node editor.
+
+Key is the concept of BrushChannels. A brush channel is
+a logical parameter with a type, input settings (e.g. pen),
+a falloff curve, etc.
+
+Brush channels have a concept of inheritance. There is a
+BrushChannelSet (collection of channels) in Sculpt,
+in Brush, and in BrushCommand. Inheritence behavior
+is controller via BrushChannel->flag.
+
+This should completely replace UnifiedPaintSettings.
+*/
+struct BrushChannel;
+
+#include "BLO_read_write.h"
+#include "DNA_sculpt_brush_types.h"
+
+typedef struct BrushMappingDef {
+ int curve;
+ bool enabled;
+ bool inv;
+ float min, max;
+ int blendmode;
+} BrushMappingDef;
+
+typedef struct BrushMappingPreset {
+ // must match order of BRUSH_MAPPING_XXX enums
+ struct BrushMappingDef pressure, xtilt, ytilt, angle, speed;
+} BrushMappingPreset;
+
+#define MAX_BRUSH_ENUM_DEF 32
+
+typedef struct BrushEnumDef {
+ EnumPropertyItem items[MAX_BRUSH_ENUM_DEF];
+} BrushEnumDef;
+
+typedef struct BrushChannelType {
+ char name[32], idname[32];
+ float min, max, soft_min, soft_max;
+ BrushMappingPreset mappings;
+
+ int type, flag;
+ int ivalue;
+ float fvalue;
+ BrushEnumDef enumdef; // if an enum type
+} BrushChannelType;
+
+typedef struct BrushCommand {
+ int tool;
+ struct BrushChannelSet *params;
+ struct BrushChannelSet *params_final;
+ int totparam;
+} BrushCommand;
+
+typedef struct BrushCommandList {
+ BrushCommand *commands;
+ int totcommand;
+} BrushCommandList;
+
+void BKE_brush_channel_free(BrushChannel *ch);
+void BKE_brush_channel_copy(BrushChannel *dst, BrushChannel *src);
+void BKE_brush_channel_init(BrushChannel *ch, BrushChannelType *def);
+BrushChannelSet *BKE_brush_channelset_create();
+
+void BKE_brush_channelset_free(BrushChannelSet *chset);
+void BKE_brush_channelset_add(BrushChannelSet *chset, BrushChannel *ch);
+
+BrushChannel *BKE_brush_channelset_lookup(BrushChannelSet *chset, const char *idname);
+
+bool BKE_brush_channelset_has(BrushChannelSet *chset, const char *idname);
+
+void BKE_brush_channelset_add_builtin(BrushChannelSet *chset, const char *idname);
+bool BKE_brush_channelset_ensure_builtin(BrushChannelSet *chset, const char *idname);
+
+void BKE_brush_channelset_merge(BrushChannelSet *dst,
+ BrushChannelSet *child,
+ BrushChannelSet *parent);
+
+void BKE_brush_resolve_channels(struct Brush *brush, struct Sculpt *sd);
+int BKE_brush_channel_get_int(BrushChannelSet *chset, char *idname);
+float BKE_brush_channel_get_float(BrushChannelSet *chset, char *idname);
+float BKE_brush_channel_set_float(BrushChannelSet *chset, char *idname, float val);
+void BKE_brush_init_toolsettings(struct Sculpt *sd);
+void BKE_brush_builtin_create(struct Brush *brush, int tool);
+BrushCommandList *BKE_brush_commandlist_create();
+void BKE_brush_commandlist_free(BrushCommandList *cl);
+BrushCommand *BKE_brush_commandlist_add(BrushCommandList *cl);
+BrushCommand *BKE_brush_command_init(BrushCommand *command, int tool);
+void BKE_builtin_commandlist_create(BrushChannelSet *chset, BrushCommandList *cl, int tool);
+void BKE_brush_channelset_read(BlendDataReader *reader, BrushChannelSet *cset);
+void BKE_brush_channelset_write(BlendWriter *writer, BrushChannelSet *cset);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/source/blender/blenkernel/BKE_customdata.h b/source/blender/blenkernel/BKE_customdata.h
index 0732f1e5190..f7e1b7f0d81 100644
--- a/source/blender/blenkernel/BKE_customdata.h
+++ b/source/blender/blenkernel/BKE_customdata.h
@@ -105,6 +105,8 @@ bool CustomData_has_math(const struct CustomData *data);
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 customdata layers is referenced.
*/
@@ -141,6 +143,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 */
bool CustomData_merge(const struct CustomData *source,
@@ -277,6 +283,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,
@@ -605,6 +621,11 @@ void CustomData_blend_write(struct BlendWriter *writer,
struct ID *id);
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);
+
+int CustomData_get_elem_size(CustomDataLayer *layer);
+
#ifdef __cplusplus
}
#endif
diff --git a/source/blender/blenkernel/BKE_data_transfer.h b/source/blender/blenkernel/BKE_data_transfer.h
index a2544e43c3d..c45aa307e8f 100644
--- a/source/blender/blenkernel/BKE_data_transfer.h
+++ b/source/blender/blenkernel/BKE_data_transfer.h
@@ -54,9 +54,11 @@ enum {
DT_TYPE_UV = 1 << 24,
DT_TYPE_SHARP_FACE = 1 << 25,
DT_TYPE_FREESTYLE_FACE = 1 << 26,
-#define DT_TYPE_MAX 27
+ DT_TYPE_PROPCOL = 1 << 27,
+#define DT_TYPE_MAX 28
- DT_TYPE_VERT_ALL = DT_TYPE_MDEFORMVERT | DT_TYPE_SHAPEKEY | DT_TYPE_SKIN | DT_TYPE_BWEIGHT_VERT,
+ DT_TYPE_VERT_ALL = DT_TYPE_MDEFORMVERT | DT_TYPE_SHAPEKEY | DT_TYPE_SKIN | DT_TYPE_BWEIGHT_VERT |
+ DT_TYPE_PROPCOL,
DT_TYPE_EDGE_ALL = DT_TYPE_SHARP_EDGE | DT_TYPE_SEAM | DT_TYPE_CREASE | DT_TYPE_BWEIGHT_EDGE |
DT_TYPE_FREESTYLE_EDGE,
DT_TYPE_LOOP_ALL = DT_TYPE_VCOL | DT_TYPE_LNOR | DT_TYPE_UV,
@@ -74,7 +76,7 @@ int BKE_object_data_transfer_dttype_to_cdtype(const int dtdata_type);
int BKE_object_data_transfer_dttype_to_srcdst_index(const int dtdata_type);
#define DT_DATATYPE_IS_VERT(_dt) \
- ELEM(_dt, DT_TYPE_MDEFORMVERT, DT_TYPE_SHAPEKEY, DT_TYPE_SKIN, DT_TYPE_BWEIGHT_VERT)
+ ELEM(_dt, DT_TYPE_MDEFORMVERT, DT_TYPE_SHAPEKEY, DT_TYPE_SKIN, DT_TYPE_BWEIGHT_VERT, DT_TYPE_PROPCOL)
#define DT_DATATYPE_IS_EDGE(_dt) \
ELEM(_dt, \
DT_TYPE_CREASE, \
@@ -94,7 +96,8 @@ enum {
DT_MULTILAYER_INDEX_SHAPEKEY = 1,
DT_MULTILAYER_INDEX_VCOL = 2,
DT_MULTILAYER_INDEX_UV = 3,
- DT_MULTILAYER_INDEX_MAX = 4,
+ DT_MULTILAYER_INDEX_PROPCOL = 4,
+ DT_MULTILAYER_INDEX_MAX = 5,
};
/* Below we keep positive values for real layers idx (generated dynamically). */
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_fcurve.h b/source/blender/blenkernel/BKE_fcurve.h
index f494c2e30cc..ba0c9f53a69 100644
--- a/source/blender/blenkernel/BKE_fcurve.h
+++ b/source/blender/blenkernel/BKE_fcurve.h
@@ -78,8 +78,12 @@ typedef struct FModifierTypeInfo {
short size;
/** #eFMI_Action_Types. */
short acttype;
+#ifdef __cplusplus
+ short requires_;
+#else
/** #eFMI_Requirement_Flags. */
short requires;
+#endif
/** name of modifier in interface. */
char name[64];
/** name of struct for SDNA. */
diff --git a/source/blender/blenkernel/BKE_mesh.h b/source/blender/blenkernel/BKE_mesh.h
index b0a8fee1178..52d5ea135e0 100644
--- a/source/blender/blenkernel/BKE_mesh.h
+++ b/source/blender/blenkernel/BKE_mesh.h
@@ -69,7 +69,8 @@ extern "C" {
/* *** 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,
@@ -636,7 +637,7 @@ void BKE_mesh_calc_edges_tessface(struct Mesh *mesh);
/* In DerivedMesh.cc */
void BKE_mesh_wrapper_deferred_finalize(struct Mesh *me_eval,
- const CustomData_MeshMasks *cd_mask_finalize);
+ const struct CustomData_MeshMasks *cd_mask_finalize);
/* **** Depsgraph evaluation **** */
diff --git a/source/blender/blenkernel/BKE_mesh_mapping.h b/source/blender/blenkernel/BKE_mesh_mapping.h
index 0518f303744..c958d01a849 100644
--- a/source/blender/blenkernel/BKE_mesh_mapping.h
+++ b/source/blender/blenkernel/BKE_mesh_mapping.h
@@ -105,20 +105,28 @@ 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);
-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);
-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 struct MVert *mvert, // 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
+void BKE_mesh_vert_loop_map_create(
+ MeshElemMap **r_map,
+ int **r_mem,
+ const struct MVert *mvert, // 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,
const struct MVert *mvert,
@@ -128,7 +136,13 @@ void BKE_mesh_vert_looptri_map_create(MeshElemMap **r_map,
const struct MLoop *mloop,
const int totloop);
void BKE_mesh_vert_edge_map_create(
- MeshElemMap **r_map, int **r_mem, const struct MEdge *medge, int totvert, int totedge);
+ MeshElemMap **r_map,
+ int **r_mem,
+ const struct MVert *mvert, // 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);
void BKE_mesh_edge_loop_map_create(MeshElemMap **r_map,
diff --git a/source/blender/blenkernel/BKE_mesh_mirror.h b/source/blender/blenkernel/BKE_mesh_mirror.h
index 7b230b04410..b5d0fec6bc1 100644
--- a/source/blender/blenkernel/BKE_mesh_mirror.h
+++ b/source/blender/blenkernel/BKE_mesh_mirror.h
@@ -32,7 +32,7 @@ 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_multires.h b/source/blender/blenkernel/BKE_multires.h
index 11bfc4b2b3a..765f285c324 100644
--- a/source/blender/blenkernel/BKE_multires.h
+++ b/source/blender/blenkernel/BKE_multires.h
@@ -39,6 +39,7 @@ struct MultiresModifierData;
struct Object;
struct Scene;
struct SubdivCCG;
+struct BMesh;
struct MLoop;
struct MLoopTri;
@@ -217,6 +218,7 @@ BLI_INLINE void BKE_multires_construct_tangent_matrix(float tangent_matrix[3][3]
const float dPdv[3],
const int corner);
+void BKE_multires_bmesh_space_set(struct Object *ob, struct BMesh *bm, int mode);
/* Versioning. */
/* Convert displacement which is stored for simply-subdivided mesh to a Catmull-Clark
diff --git a/source/blender/blenkernel/BKE_object.h b/source/blender/blenkernel/BKE_object.h
index 0e153c5a82a..597096bdff9 100644
--- a/source/blender/blenkernel/BKE_object.h
+++ b/source/blender/blenkernel/BKE_object.h
@@ -25,6 +25,8 @@
#include "BLI_sys_types.h"
#include "DNA_object_enums.h"
+#include "DNA_userdef_types.h"
+#include "BKE_lib_id.h"
#ifdef __cplusplus
extern "C" {
@@ -154,8 +156,8 @@ bool BKE_object_obdata_is_libdata(const struct Object *ob);
struct Object *BKE_object_duplicate(struct Main *bmain,
struct Object *ob,
- uint dupflag,
- const uint duplicate_options);
+ eDupli_ID_Flags dupflag,
+ const eLibIDDuplicateFlags duplicate_options);
void BKE_object_obdata_size_init(struct Object *ob, const float size);
diff --git a/source/blender/blenkernel/BKE_paint.h b/source/blender/blenkernel/BKE_paint.h
index 11bf40dfa55..c207c816515 100644
--- a/source/blender/blenkernel/BKE_paint.h
+++ b/source/blender/blenkernel/BKE_paint.h
@@ -23,15 +23,19 @@
* \ingroup bke
*/
+#include "BKE_pbvh.h"
#include "BLI_bitmap.h"
#include "BLI_utildefines.h"
#include "DNA_brush_enums.h"
+#include "DNA_customdata_types.h"
#include "DNA_object_enums.h"
#ifdef __cplusplus
extern "C" {
#endif
+struct SculptCustomLayer;
+struct MDynTopoVert;
struct BMFace;
struct BMesh;
struct BlendDataReader;
@@ -348,6 +352,11 @@ typedef struct SculptClothSimulation {
/** #PBVHNode pointer as a key, index in #SculptClothSimulation.node_state as value. */
struct GHash *node_state_index;
eSculptClothNodeSimState *node_state;
+
+ // persistent base customdata layer offsets
+ int cd_pers_co;
+ int cd_pers_no;
+ int cd_pers_disp;
} SculptClothSimulation;
typedef struct SculptPersistentBase {
@@ -369,7 +378,8 @@ typedef struct SculptVertexInfo {
typedef struct SculptBoundaryEditInfo {
/* Vertex index from where the topology propagation reached this vertex. */
- int original_vertex;
+ SculptVertRef original_vertex;
+ int original_vertex_i;
/* How many steps were needed to reach this vertex from the boundary. */
int num_propagation_steps;
@@ -380,13 +390,23 @@ typedef struct SculptBoundaryEditInfo {
/* Edge for drawing the boundary preview in the cursor. */
typedef struct SculptBoundaryPreviewEdge {
- int v1;
- int v2;
+ SculptVertRef v1;
+ SculptVertRef 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. */
- int *vertices;
+ SculptVertRef *vertices;
+ int *vertex_indices;
+
int vertices_capacity;
int num_vertices;
@@ -395,6 +415,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;
+ SculptVertRef *boundary_closest;
+ int sculpt_totvert;
+
/* Data for drawing the preview. */
SculptBoundaryPreviewEdge *edges;
int edges_capacity;
@@ -404,12 +432,12 @@ typedef struct SculptBoundary {
bool forms_loop;
/* Initial vertex in the boundary which is closest to the current sculpt active vertex. */
- int initial_vertex;
+ SculptVertRef initial_vertex;
/* Vertex that at max_propagation_steps from the boundary and closest to the original active
* vertex that was used to initialize the boundary. This is used as a reference to check how much
* the deformation will go into the mesh and to calculate the strength of the brushes. */
- int pivot_vertex;
+ SculptVertRef pivot_vertex;
/* Stores the initial positions of the pivot and boundary initial vertex as they may be deformed
* during the brush action. This allows to use them as a reference positions and vectors for some
@@ -427,7 +455,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. */
@@ -469,14 +497,13 @@ typedef struct SculptArray {
SculptArrayCopy *copies[PAINT_SYMM_AREAS];
int num_copies;
-
struct {
- ScultpArrayPathPoint * points;
+ ScultpArrayPathPoint *points;
int tot_points;
int capacity;
float total_length;
} path;
-
+
int mode;
float normal[3];
float direction[3];
@@ -503,7 +530,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;
+ SculptVertRef *fake_neighbor_index;
} SculptFakeNeighbors;
@@ -522,8 +549,15 @@ typedef struct SculptSession {
/* These are always assigned to base mesh data when using PBVH_FACES and PBVH_GRIDS. */
struct MVert *mvert;
- struct MPoly *mpoly;
+ struct MEdge *medge;
struct MLoop *mloop;
+ struct MPoly *mpoly;
+
+ // only assigned in PBVH_FACES and PBVH_GRIDS
+ CustomData *vdata, *edata, *ldata, *pdata;
+
+ // for grids
+ CustomData temp_vdata, temp_pdata;
/* These contain the vertex and poly counts of the final mesh. */
int totvert, totpoly;
@@ -558,8 +592,13 @@ typedef struct SculptSession {
/* BMesh for dynamic topology sculpting */
struct BMesh *bm;
+ int cd_dyn_vert;
int cd_vert_node_offset;
int cd_face_node_offset;
+ int cd_vcol_offset;
+ int cd_faceset_offset;
+ int cd_face_areas;
+
bool bm_smooth_shading;
/* Undo/redo log for dynamic topology sculpting */
struct BMLog *bm_log;
@@ -573,7 +612,7 @@ typedef struct SculptSession {
bool show_face_sets;
/* Setting this to true allows a PBVH rebuild when evaluating the object even if the stroke or
- * filter caches are active. */
+ * filter caches are active. */
bool needs_pbvh_rebuild;
/* Painting on deformed mesh */
@@ -591,9 +630,9 @@ typedef struct SculptSession {
struct ExpandCache *expand_cache;
/* Cursor data and active vertex for tools */
- int active_vertex_index;
+ SculptVertRef active_vertex_index;
+ SculptFaceRef active_face_index;
- int active_face_index;
int active_grid_index;
/* When active, the cursor draws with faded colors, indicating that there is an action enabled.
@@ -616,14 +655,17 @@ 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;
- int face_set_last_poly;
- int face_set_last_edge;
+ SculptFaceRef face_set_last_poly;
+ SculptEdgeRef face_set_last_edge;
/* Dynamic mesh preview */
- int *preview_vert_index_list;
+ SculptVertRef *preview_vert_index_list;
int preview_vert_index_count;
/* Pose Brush Preview */
@@ -637,8 +679,8 @@ typedef struct SculptSession {
/* This is freed with the PBVH, so it is always in sync with the mesh. */
SculptPersistentBase *persistent_base;
-
- float (*limit_surface)[3];
+ // float (*limit_surface)[3];
+ struct SculptCustomLayer *limit_surface;
SculptVertexInfo vertex_info;
SculptFakeNeighbors fake_neighbors;
@@ -691,6 +733,13 @@ typedef struct SculptSession {
*/
char needs_flush_to_id;
+ // id of current stroke, used to detect
+ // if vertex original data needs to be updated
+ int stroke_id, boundary_symmetry;
+
+ bool fast_draw; // hides facesets/masks and forces smooth to save GPU bandwidth
+ struct MDynTopoVert *mdyntopo_verts; // for non-bmesh
+ int mdyntopo_verts_size;
} SculptSession;
void BKE_sculptsession_free(struct Object *ob);
@@ -698,6 +747,7 @@ 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);
+bool BKE_sculptsession_check_mdyntopo(SculptSession *ss, int totvert);
/* 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. */
@@ -734,6 +784,8 @@ void BKE_sculpt_ensure_orig_mesh_data(struct Scene *scene, struct Object *object
bool BKE_sculptsession_use_pbvh_draw(const struct Object *ob, const struct View3D *v3d);
+char BKE_get_fset_boundary_symflag(struct Object *object);
+
enum {
SCULPT_MASK_LAYER_CALC_VERT = (1 << 0),
SCULPT_MASK_LAYER_CALC_LOOP = (1 << 1),
diff --git a/source/blender/blenkernel/BKE_pbvh.h b/source/blender/blenkernel/BKE_pbvh.h
index 6e91b331ce4..0e1c440c5c7 100644
--- a/source/blender/blenkernel/BKE_pbvh.h
+++ b/source/blender/blenkernel/BKE_pbvh.h
@@ -22,25 +22,103 @@
*/
#include "BLI_bitmap.h"
+#include "BLI_compiler_compat.h"
#include "BLI_ghash.h"
/* For embedding CCGKey in iterator. */
#include "BKE_ccg.h"
+#include <stdint.h>
+
+//#define DEFRAGMENT_MEMORY
#ifdef __cplusplus
extern "C" {
#endif
+// experimental feature to detect quad diagonals and mark (but not dissolve) them
+//#define SCULPT_DIAGONAL_EDGE_MARKS
+
+typedef struct SculptVertRef {
+ intptr_t i;
+} SculptVertRef;
+
+typedef struct SculptEdgeRef {
+ intptr_t i;
+} SculptEdgeRef;
+
+typedef struct SculptFaceRef {
+ intptr_t i;
+} SculptFaceRef;
+
+#define SCULPT_REF_NONE ((intptr_t)-1)
+
+#if 0
+typedef struct SculptLoopRef {
+ intptr_t i;
+} SculptLoopRef;
+#endif
+
+BLI_INLINE SculptVertRef BKE_pbvh_make_vref(intptr_t i)
+{
+ SculptVertRef ret = {i};
+ return ret;
+}
+
+BLI_INLINE SculptEdgeRef BKE_pbvh_make_eref(intptr_t i)
+{
+ SculptEdgeRef ret = {i};
+ return ret;
+}
+
+BLI_INLINE SculptFaceRef BKE_pbvh_make_fref(intptr_t i)
+{
+ SculptFaceRef ret = {i};
+ return ret;
+}
+
+#ifdef DEFRAGMENT_MEMORY
+# include "BLI_smallhash.h"
+#endif
+
+typedef struct PBVHTri {
+ int v[3]; // references into PBVHTriBuf->verts
+ intptr_t l[3]; // loops
+ int eflag; // bitmask of which edges in the tri are real edges in the mesh
+
+ float no[3];
+ SculptFaceRef f;
+} PBVHTri;
+
+typedef struct PBVHTriBuf {
+ PBVHTri *tris;
+ SculptVertRef *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;
+
struct BMLog;
struct BMesh;
+struct BMVert;
+struct BMEdge;
+struct BMFace;
struct CCGElem;
struct CCGKey;
struct CustomData;
+struct TableGSet;
struct DMFlagMat;
struct GPU_PBVH_Buffers;
struct IsectRayPrecalc;
struct MLoop;
struct MLoopTri;
+struct MDynTopoVert;
struct MPoly;
struct MVert;
struct Mesh;
@@ -52,12 +130,86 @@ struct TaskParallelSettings;
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;
+ SculptVertRef *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;
+ SculptVertRef 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 SculptVertRefs 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 struct {
float (*co)[3];
} PBVHProxyNode;
typedef struct {
float (*color)[4];
+ int size;
} PBVHColorBufferNode;
typedef enum {
@@ -78,6 +230,15 @@ typedef enum {
PBVH_UpdateTopology = 1 << 13,
PBVH_UpdateColor = 1 << 14,
+ PBVH_Delete = 1 << 15,
+ PBVH_UpdateCurvatureDir = 1 << 16,
+ PBVH_UpdateTris = 1 << 17,
+ PBVH_RebuildNodeVerts = 1 << 18,
+
+ /* tri areas are not guaranteed to be up to date, tools should
+ update all nodes on first step of brush*/
+ PBVH_UpdateTriAreas = 1 << 19,
+ PBVH_UpdateOtherVerts = 1 << 20
} PBVHNodeFlags;
typedef struct PBVHFrustumPlanes {
@@ -98,6 +259,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(void);
@@ -106,27 +270,57 @@ void BKE_pbvh_build_mesh(PBVH *pbvh,
const struct MPoly *mpoly,
const struct MLoop *mloop,
struct MVert *verts,
+ struct MDynTopoVert *mdyntopo_verts,
int totvert,
struct CustomData *vdata,
struct CustomData *ldata,
struct CustomData *pdata,
const struct MLoopTri *looptri,
- int looptri_num);
+ int looptri_num,
+ bool fast_draw);
void BKE_pbvh_build_grids(PBVH *pbvh,
struct CCGElem **grids,
int totgrid,
struct CCGKey *key,
void **gridfaces,
struct DMFlagMat *flagmats,
- unsigned int **grid_hidden);
+ unsigned int **grid_hidden,
+ bool fast_draw);
void BKE_pbvh_build_bmesh(PBVH *pbvh,
struct BMesh *bm,
bool smooth_shading,
struct BMLog *log,
const int cd_vert_node_offset,
- const int cd_face_node_offset);
+ const int cd_face_node_offset,
+ const int cd_dyn_vert,
+ const int cd_face_areas,
+ bool fast_draw);
+void BKE_pbvh_update_offsets(PBVH *pbvh,
+ const int cd_vert_node_offset,
+ const int cd_face_node_offset,
+ const int cd_dyn_vert,
+ const int cd_face_areas);
void BKE_pbvh_free(PBVH *pbvh);
+void BKE_pbvh_set_bm_log(PBVH *pbvh, struct BMLog *log);
+
+/** update original data, only data whose r_** parameters are passed in will be updated*/
+void BKE_pbvh_bmesh_update_origvert(
+ PBVH *pbvh, struct BMVert *v, float **r_co, float **r_no, float **r_color, bool log_undo);
+void BKE_pbvh_update_origcolor_bmesh(PBVH *pbvh, PBVHNode *node);
+void BKE_pbvh_update_origco_bmesh(PBVH *pbvh, PBVHNode *node);
+
+/**
+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 multithread) */
@@ -150,7 +344,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,
@@ -162,11 +357,13 @@ bool BKE_pbvh_node_raycast(PBVH *pbvh,
int *hit_count,
float *depth,
float *back_depth,
- int *active_vertex_index,
- int *active_face_grid_index,
- float *face_normal);
+ SculptVertRef *active_vertex_index,
+ SculptFaceRef *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,
@@ -191,7 +388,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);
/* Drawing */
@@ -240,22 +438,60 @@ int BKE_pbvh_get_grid_num_faces(const PBVH *pbvh);
/* Only valid for type == PBVH_BMESH */
struct BMesh *BKE_pbvh_get_bmesh(PBVH *pbvh);
-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;
-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);
+typedef float (*DyntopoMaskCB)(SculptVertRef 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 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);
/* Node Access */
+void BKE_pbvh_check_tri_areas(PBVH *pbvh, PBVHNode *node);
+
+// updates boundaries and valences for whole mesh
+void BKE_pbvh_bmesh_on_mesh_change(PBVH *pbvh);
+bool BKE_pbvh_bmesh_check_valence(PBVH *pbvh, SculptVertRef vertex);
+void BKE_pbvh_bmesh_update_valence(int cd_dyn_vert, SculptVertRef 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, SculptVertRef vertex);
+
+void BKE_pbvh_node_mark_update_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);
@@ -294,10 +530,14 @@ 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);
-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);
-void BKE_pbvh_bmesh_node_save_orig(struct BMesh *bm, PBVHNode *node);
+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);
+
+// now generated PBVHTris
void BKE_pbvh_bmesh_after_stroke(PBVH *pbvh);
/* Update Bounding Box/Redraw and clear flags */
@@ -345,6 +585,7 @@ typedef struct PBVHVertexIter {
int gy;
int i;
int index;
+ SculptVertRef vertex;
bool respect_hide;
/* grid */
@@ -364,10 +605,14 @@ typedef struct PBVHVertexIter {
float *vmask;
/* 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_dyn_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 */
@@ -381,6 +626,8 @@ typedef struct PBVHVertexIter {
bool visible;
} PBVHVertexIter;
+#define BKE_PBVH_DYNVERT(cd_dyn_vert, v) ((MDynTopoVert *)BM_ELEM_CD_GET_VOID_P(v, cd_dyn_vert))
+
void pbvh_vertex_iter_init(PBVH *pbvh, PBVHNode *node, PBVHVertexIter *vi, int mode);
#define BKE_pbvh_vertex_iter_begin(pbvh, node, vi, mode) \
@@ -390,7 +637,7 @@ void pbvh_vertex_iter_init(PBVH *pbvh, PBVHNode *node, PBVHVertexIter *vi, int m
if (vi.grids) { \
vi.width = vi.gridsize; \
vi.height = vi.gridsize; \
- vi.index = vi.grid_indices[vi.g] * vi.key.grid_area - 1; \
+ vi.vertex.i = vi.index = 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]]; \
@@ -409,6 +656,7 @@ void pbvh_vertex_iter_init(PBVH *pbvh, PBVHNode *node, PBVHVertexIter *vi, int m
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)) { \
@@ -429,7 +677,7 @@ void pbvh_vertex_iter_init(PBVH *pbvh, PBVHNode *node, PBVHVertexIter *vi, int m
} \
vi.co = vi.mvert->co; \
vi.no = vi.mvert->no; \
- vi.index = vi.vert_indices[vi.i]; \
+ vi.index = vi.vertex.i = vi.vert_indices[vi.i]; \
if (vi.vmask) { \
vi.mask = &vi.vmask[vi.index]; \
} \
@@ -438,22 +686,41 @@ 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 = 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 = vi.bm_cur_set->elems[vi.bi++]; \
+ } \
} \
- else { \
- vi.bm_vert = BLI_gsetIterator_getKey(&vi.bm_other_verts); \
- BLI_gsetIterator_step(&vi.bm_other_verts); \
+ if (!bv) { \
+ continue; \
+ } \
+ vi.bm_vert = bv; \
+ if (vi.cd_vcol_offset >= 0) { \
+ MPropCol *vcol = BM_ELEM_CD_GET_VOID_P(bv, vi.cd_vcol_offset); \
+ vi.col = vcol->color; \
} \
+ 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.index = BM_elem_index_get(vi.bm_vert); \
- vi.mask = BM_ELEM_CD_GET_VOID_P(vi.bm_vert, vi.cd_vert_mask_offset); \
+ vi.mask = (float *)BM_ELEM_CD_GET_VOID_P(vi.bm_vert, vi.cd_vert_mask_offset); \
}
#define BKE_pbvh_vertex_iter_end \
@@ -462,24 +729,32 @@ void pbvh_vertex_iter_init(PBVH *pbvh, PBVHNode *node, PBVHVertexIter *vi, int m
} \
((void)0)
+#define BKE_pbvh_vertex_index_to_table(pbvh, v) \
+ (BKE_pbvh_type(pbvh) == PBVH_BMESH && v.i != -1 ? BM_elem_index_get((BMVert *)(v.i)) : (v.i))
+SculptVertRef BKE_pbvh_table_index_to_vertex(PBVH *pbvh, int idx);
+
+#define BKE_pbvh_edge_index_to_table(pbvh, v) \
+ (BKE_pbvh_type(pbvh) == PBVH_BMESH && v.i != -1 ? BM_elem_index_get((BMEdge *)(v.i)) : (v.i))
+SculptEdgeRef BKE_pbvh_table_index_to_edge(PBVH *pbvh, int idx);
+
+#define BKE_pbvh_face_index_to_table(pbvh, v) \
+ (BKE_pbvh_type(pbvh) == PBVH_BMESH && v.i != -1 ? BM_elem_index_get((BMFace *)(v.i)) : (v.i))
+SculptFaceRef BKE_pbvh_table_index_to_face(PBVH *pbvh, int idx);
+
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);
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]);
bool BKE_pbvh_node_vert_update_check_any(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);
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 */
@@ -492,6 +767,150 @@ struct MVert *BKE_pbvh_get_verts(const PBVH *pbvh);
PBVHColorBufferNode *BKE_pbvh_node_color_buffer_get(PBVHNode *node);
void BKE_pbvh_node_color_buffer_free(PBVH *pbvh);
+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_bmesh_remove_face(PBVH *pbvh, struct BMFace *f, bool log_face);
+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_dyn_vert,
+ int cd_faceset_offset,
+ struct BMVert *v,
+ int symmetry);
+
+#define DYNTOPO_DYNAMIC_TESS
+
+PBVHNode *BKE_pbvh_get_node_leaf_safe(PBVH *pbvh, int i);
+
+void BKE_pbvh_get_vert_face_areas(PBVH *pbvh, SculptVertRef 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_PTEX?
+} 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);
+
+ /*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 (*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, whatever
+ 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);
+
#ifdef __cplusplus
}
#endif
diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt
index 0b082bf1c5a..c16c8eee576 100644
--- a/source/blender/blenkernel/CMakeLists.txt
+++ b/source/blender/blenkernel/CMakeLists.txt
@@ -124,6 +124,7 @@ set(SRC
intern/displist.cc
intern/displist_tangent.c
intern/dynamicpaint.c
+ intern/dyntopo.c
intern/editlattice.c
intern/editmesh.c
intern/editmesh_bvh.c
@@ -235,6 +236,7 @@ set(SRC
intern/particle_system.c
intern/pbvh.c
intern/pbvh_bmesh.c
+ intern/pbvh_displacement.c
intern/pointcache.c
intern/pointcloud.cc
intern/preferences.c
@@ -287,6 +289,7 @@ set(SRC
intern/workspace.c
intern/world.c
intern/writeavi.c
+ intern/brush_engine.c
BKE_DerivedMesh.h
BKE_action.h
@@ -449,6 +452,7 @@ set(SRC
BKE_workspace.h
BKE_world.h
BKE_writeavi.h
+ BKE_brush_engine.h
nla_private.h
particle_private.h
@@ -794,3 +798,75 @@ if(WITH_GTESTS)
include(GTestTesting)
blender_add_test_lib(bf_blenkernel_tests "${TEST_SRC}" "${INC};${TEST_INC}" "${INC_SYS}" "${LIB}")
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}/$<CONFIG>")
+ 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 $<CONFIG>
+ # 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 59e81938e79..a98ca624885 100644
--- a/source/blender/blenkernel/intern/DerivedMesh.cc
+++ b/source/blender/blenkernel/intern/DerivedMesh.cc
@@ -1140,7 +1140,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 7e4ab754500..21bba9cdb08 100644
--- a/source/blender/blenkernel/intern/anim_data.c
+++ b/source/blender/blenkernel/intern/anim_data.c
@@ -425,7 +425,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/brush.c b/source/blender/blenkernel/intern/brush.c
index 0f3442ca0a9..72e1a1fcce3 100644
--- a/source/blender/blenkernel/intern/brush.c
+++ b/source/blender/blenkernel/intern/brush.c
@@ -34,6 +34,7 @@
#include "BLT_translation.h"
#include "BKE_brush.h"
+#include "BKE_brush_engine.h"
#include "BKE_colortools.h"
#include "BKE_context.h"
#include "BKE_gpencil.h"
@@ -221,7 +222,7 @@ static void brush_foreach_id(ID *id, LibraryForeachIDData *data)
BKE_texture_mtex_foreach_id(data, &brush->mask_mtex);
}
-static void brush_blend_write(BlendWriter *writer, ID *id, const void *id_address)
+ATTR_NO_OPT static void brush_blend_write(BlendWriter *writer, ID *id, const void *id_address)
{
Brush *brush = (Brush *)id;
@@ -272,6 +273,10 @@ static void brush_blend_write(BlendWriter *writer, ID *id, const void *id_addres
if (brush->gradient) {
BLO_write_struct(writer, ColorBand, brush->gradient);
}
+
+ if (brush->channels) {
+ BKE_brush_channelset_write(writer, brush->channels);
+ }
}
static void brush_reset_input_curve(CurveMapping *cumap)
@@ -304,6 +309,37 @@ static void brush_blend_read_data(BlendDataReader *reader, ID *id)
{
Brush *brush = (Brush *)id;
+ if (brush->channels) {
+ BLO_read_data_address(reader, &brush->channels);
+ BKE_brush_channelset_read(reader, brush->channels);
+ }
+ else {
+ BKE_brush_builtin_create(brush, brush->sculpt_tool);
+ }
+
+ if (brush->dyntopo.radius_scale == 0.0f) {
+ brush->dyntopo.radius_scale = 1.0f;
+ brush->dyntopo.inherit |= DYNTOPO_INHERIT_RADIUS_SCALE;
+ }
+
+ // detect old file data
+ if (brush->autosmooth_radius_factor == 0.0f) {
+ brush->autosmooth_radius_factor = 1.0f;
+ }
+
+ if (brush->topology_rake_radius_factor == 0.0f) {
+ brush->topology_rake_radius_factor = 1.0f;
+ }
+
+ if (brush->autosmooth_spacing == 0.0f) {
+ brush->autosmooth_spacing = 12;
+ }
+
+ if (brush->topology_rake_spacing == 0.0f) {
+ brush->topology_rake_spacing = 12;
+ brush->topology_rake_projection = 1.0f;
+ }
+
/* Falloff curve. */
BLO_read_data_address(reader, &brush->curve);
@@ -518,7 +554,13 @@ static void brush_defaults(Brush *brush)
FROM_DEFAULT(alpha);
FROM_DEFAULT(hardness);
FROM_DEFAULT(autosmooth_factor);
+ FROM_DEFAULT(autosmooth_projection);
+ FROM_DEFAULT(autosmooth_radius_factor);
+ FROM_DEFAULT(autosmooth_spacing);
FROM_DEFAULT(topology_rake_factor);
+ FROM_DEFAULT(topology_rake_radius_factor);
+ FROM_DEFAULT(topology_rake_projection);
+ FROM_DEFAULT(topology_rake_spacing);
FROM_DEFAULT(crease_pinch_factor);
FROM_DEFAULT(normal_radius_factor);
FROM_DEFAULT(wet_paint_radius_factor);
@@ -549,6 +591,7 @@ static void brush_defaults(Brush *brush)
FROM_DEFAULT(stencil_dimension);
FROM_DEFAULT(mtex);
FROM_DEFAULT(mask_mtex);
+ FROM_DEFAULT(dyntopo);
#undef FROM_DEFAULT
#undef FROM_DEFAULT_PTR
@@ -1716,6 +1759,8 @@ void BKE_brush_debug_print_state(Brush *br)
BR_TEST(plane_offset, f);
BR_TEST(autosmooth_factor, f);
+ BR_TEST(autosmooth_projection, f);
+ BR_TEST(autosmooth_radius_factor, f);
BR_TEST(topology_rake_factor, f);
@@ -1758,6 +1803,12 @@ void BKE_brush_sculpt_reset(Brush *br)
* assign this so logic below can remain the same. */
br->alpha = 0.5f;
+ bool disable_dyntopo = false;
+
+ // basic face set setup for all organic brushes
+ br->autosmooth_fset_slide = 1.0f;
+ br->flag2 |= BRUSH_SMOOTH_PRESERVE_FACE_SETS | BRUSH_SMOOTH_USE_AREA_WEIGHT;
+
/* Brush settings */
switch (br->sculpt_tool) {
case SCULPT_TOOL_DRAW_SHARP:
@@ -1770,6 +1821,9 @@ 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_FAIRING:
br->curve_preset = BRUSH_CURVE_SMOOTHER;
@@ -1787,6 +1841,8 @@ void BKE_brush_sculpt_reset(Brush *br)
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;
@@ -1855,18 +1911,29 @@ 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;
+ br->flag2 |= BRUSH_SMOOTH_PRESERVE_FACE_SETS | BRUSH_SMOOTH_USE_AREA_WEIGHT;
+
br->spacing = 5;
br->alpha = 0.7f;
br->surface_smooth_shape_preservation = 0.5f;
br->surface_smooth_current_vertex = 0.5f;
br->surface_smooth_iterations = 4;
+
+ disable_dyntopo = true;
break;
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;
@@ -1880,6 +1947,8 @@ 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_POSE:
br->pose_smooth_iterations = 4;
@@ -1888,18 +1957,24 @@ 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_BOUNDARY:
br->flag &= ~BRUSH_ALPHA_PRESSURE;
br->flag &= ~BRUSH_SPACE;
br->flag &= ~BRUSH_SPACE_ATTEN;
br->curve_preset = BRUSH_CURVE_CONSTANT;
+
+ disable_dyntopo = true;
break;
case SCULPT_TOOL_DRAW_FACE_SETS:
br->alpha = 0.5f;
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;
@@ -1907,6 +1982,8 @@ 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;
@@ -1915,6 +1992,8 @@ 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;
@@ -1932,6 +2011,8 @@ void BKE_brush_sculpt_reset(Brush *br)
br->density = 1.0f;
br->flag &= ~BRUSH_SPACE_ATTEN;
zero_v3(br->rgb);
+
+ disable_dyntopo = true;
break;
case SCULPT_TOOL_SMEAR:
br->alpha = 1.0f;
@@ -1939,6 +2020,18 @@ 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_VCOL_BOUNDARY:
+ br->flag &= ~BRUSH_SPACE_ATTEN;
+ br->spacing = 5;
+ br->alpha = 0.7f;
+ br->surface_smooth_shape_preservation = 0.5f;
+ br->surface_smooth_current_vertex = 0.5f;
+ br->surface_smooth_iterations = 4;
+
+ disable_dyntopo = true;
break;
case SCULPT_TOOL_DISPLACEMENT_SMEAR:
br->alpha = 1.0f;
@@ -1947,11 +2040,17 @@ void BKE_brush_sculpt_reset(Brush *br)
br->flag &= ~BRUSH_ALPHA_PRESSURE;
br->flag &= ~BRUSH_SPACE_ATTEN;
br->curve_preset = BRUSH_CURVE_SMOOTHER;
+ disable_dyntopo = true;
break;
default:
break;
}
+ if (disable_dyntopo) {
+ // disabled flag is never inherited
+ br->dyntopo.flag |= DYNTOPO_DISABLED;
+ }
+
/* Cursor colors */
/* Default Alpha */
@@ -2009,6 +2108,20 @@ void BKE_brush_sculpt_reset(Brush *br)
break;
case SCULPT_TOOL_SIMPLIFY:
+ // don't use DYNTOPO_INHERIT_BITMASK, we want to include
+ // future bits
+
+ br->flag2 |= BRUSH_SMOOTH_PRESERVE_FACE_SETS | BRUSH_SMOOTH_USE_AREA_WEIGHT |
+ BRUSH_CURVATURE_RAKE;
+ br->dyntopo.inherit = 0x7FFFFFFF &
+ ~(DYNTOPO_INHERIT_ALL | DYNTOPO_SUBDIVIDE | DYNTOPO_COLLAPSE);
+ br->dyntopo.flag |= DYNTOPO_COLLAPSE | DYNTOPO_SUBDIVIDE;
+ br->autosmooth_factor = 0.05;
+ br->topology_rake_factor = 0.35;
+ br->topology_rake_projection = 0.975;
+
+ break;
+ case SCULPT_TOOL_VCOL_BOUNDARY:
case SCULPT_TOOL_PAINT:
case SCULPT_TOOL_MASK:
case SCULPT_TOOL_DRAW_FACE_SETS:
@@ -2645,3 +2758,158 @@ struct ImBuf *BKE_brush_gen_radial_control_imbuf(Brush *br, bool secondary, bool
return im;
}
+
+void BKE_brush_get_dyntopo(Brush *brush, Sculpt *sd, DynTopoSettings *out)
+{
+ *out = brush->dyntopo;
+
+ // detect unconverted file data
+ if (!out->inherit && !out->detail_range) {
+ // reload default dyntopo settings
+ Brush brush2 = *brush;
+
+ // don't copy heap allocd data
+ brush2.curve = NULL;
+ brush2.icon_imbuf = NULL;
+ brush2.gpencil_settings = NULL;
+ brush2.gradient = NULL;
+ brush2.preview = NULL;
+
+ BKE_brush_sculpt_reset(&brush2);
+
+ brush->dyntopo = *out = brush2.dyntopo;
+
+ brush_free_data((ID *)&brush2);
+ }
+ else if (!out->detail_size) {
+ brush->dyntopo.inherit |= DYNTOPO_INHERIT_DETAIL_SIZE;
+ brush->dyntopo.detail_size = 8.0f;
+ }
+
+ int inherit = out->inherit;
+
+ if (inherit & DYNTOPO_INHERIT_ALL) {
+ inherit = 0x7FFFFFFF;
+ }
+
+ if (!(sd->flags & SCULPT_DYNTOPO_ENABLED)) {
+ out->flag |= DYNTOPO_DISABLED;
+ }
+
+ if (inherit & DYNTOPO_INHERIT_MODE) {
+ if (sd->flags & SCULPT_DYNTOPO_DETAIL_CONSTANT) {
+ out->mode = DYNTOPO_DETAIL_CONSTANT;
+ }
+ else if (sd->flags & SCULPT_DYNTOPO_DETAIL_BRUSH) {
+ out->mode = DYNTOPO_DETAIL_BRUSH;
+ }
+ else if (sd->flags & SCULPT_DYNTOPO_DETAIL_MANUAL) {
+ out->mode = DYNTOPO_DETAIL_MANUAL;
+ }
+ else {
+ out->mode = DYNTOPO_DETAIL_RELATIVE;
+ }
+ }
+
+ if (inherit & DYNTOPO_INHERIT_RADIUS_SCALE) {
+ out->radius_scale = sd->dyntopo_radius_scale;
+ }
+
+ if (inherit & DYNTOPO_INHERIT_DETAIL_SIZE) {
+ out->detail_size = sd->detail_size;
+ }
+
+ if (inherit & DYNTOPO_INHERIT_DETAIL_RANGE) {
+ out->detail_range = sd->detail_range;
+ }
+
+ if (inherit & DYNTOPO_INHERIT_DETAIL_PERCENT) {
+ out->detail_percent = sd->detail_percent;
+ }
+
+ if (inherit & DYNTOPO_INHERIT_SPACING) {
+ out->spacing = sd->dyntopo_spacing;
+ }
+
+ if (inherit & DYNTOPO_INHERIT_CONSTANT_DETAIL) {
+ out->constant_detail = sd->constant_detail;
+ }
+
+ if (inherit & DYNTOPO_SUBDIVIDE) {
+ if (sd->flags & SCULPT_DYNTOPO_SUBDIVIDE) {
+ out->flag |= DYNTOPO_SUBDIVIDE;
+ }
+ else {
+ out->flag &= ~DYNTOPO_SUBDIVIDE;
+ }
+ }
+
+ if (inherit & DYNTOPO_LOCAL_COLLAPSE) {
+ if (sd->flags & SCULPT_DYNTOPO_LOCAL_COLLAPSE) {
+ out->flag |= DYNTOPO_LOCAL_COLLAPSE;
+ }
+ else {
+ out->flag &= ~DYNTOPO_LOCAL_COLLAPSE;
+ }
+ }
+
+ if (inherit & DYNTOPO_LOCAL_SUBDIVIDE) {
+ if (sd->flags & SCULPT_DYNTOPO_LOCAL_SUBDIVIDE) {
+ out->flag |= DYNTOPO_LOCAL_SUBDIVIDE;
+ }
+ else {
+ out->flag &= ~DYNTOPO_LOCAL_SUBDIVIDE;
+ }
+ }
+
+ if (inherit & DYNTOPO_COLLAPSE) {
+ if (sd->flags & SCULPT_DYNTOPO_COLLAPSE) {
+ out->flag |= DYNTOPO_COLLAPSE;
+ }
+ else {
+ out->flag &= ~DYNTOPO_COLLAPSE;
+ }
+ }
+
+ if (inherit & DYNTOPO_CLEANUP) {
+ if (sd->flags & SCULPT_DYNTOPO_CLEANUP) {
+ out->flag |= DYNTOPO_CLEANUP;
+ }
+ else {
+ out->flag &= ~DYNTOPO_CLEANUP;
+ }
+ }
+};
+
+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)
+{
+ UnifiedPaintSettings *ups = &scene->toolsettings->unified_paint_settings;
+
+ return BKE_brush_hard_edge_mode_get(scene, brush) ? 0.0f : brush->autosmooth_fset_slide;
+}
diff --git a/source/blender/blenkernel/intern/brush_engine.c b/source/blender/blenkernel/intern/brush_engine.c
new file mode 100644
index 00000000000..d247a69e4b5
--- /dev/null
+++ b/source/blender/blenkernel/intern/brush_engine.c
@@ -0,0 +1,683 @@
+#include "MEM_guardedalloc.h"
+
+#include "BLI_alloca.h"
+#include "BLI_array.h"
+#include "BLI_bitmap.h"
+#include "BLI_compiler_attrs.h"
+#include "BLI_compiler_compat.h"
+#include "BLI_listbase.h"
+#include "BLI_math.h"
+#include "BLI_memarena.h"
+
+#include "DNA_brush_enums.h"
+#include "DNA_brush_types.h"
+#include "DNA_color_types.h"
+#include "DNA_curveprofile_types.h"
+#include "DNA_node_types.h"
+#include "DNA_sculpt_brush_types.h"
+
+#include "BKE_brush.h"
+#include "BKE_colorband.h"
+#include "BKE_colortools.h"
+#include "BKE_context.h"
+#include "BKE_node.h"
+#include "BKE_paint.h"
+
+#include "BKE_brush_engine.h"
+#include "BKE_curveprofile.h"
+
+#include "BLO_read_write.h"
+
+#define ICON_NONE -1
+
+/*
+Brush command lists.
+
+Command lists are built dynamically from
+brush flags, pen input settings, etc.
+
+Eventually they will be generated by node
+networks. BrushCommandPreset will be
+generated from the node group inputs.
+*/
+
+/* clang-format off */
+BrushChannelType brush_builtin_channels[] = {
+ {
+ .name = "Radius",
+ .idname = "RADIUS",
+ .min = 0.001f,
+ .type = BRUSH_CHANNEL_FLOAT,
+ .max = 2048.0f,
+ .soft_min = 0.1f,
+ .soft_max = 1024.0f,
+ .mappings = {
+ .pressure = {.curve = CURVE_PRESET_SMOOTH, .min = 0.0f, .max = 1.0f, .enabled = false},
+ }
+ },
+ {
+ .name = "Strength",
+ .idname = "STRENGTH",
+ .min = -1.0f,
+ .type = BRUSH_CHANNEL_FLOAT,
+ .max = 4.0f,
+ .soft_min = 0.0f,
+ .soft_max = 1.0f,
+ .mappings = {
+ .pressure = {.curve = CURVE_PRESET_SMOOTH, .min = 0.0f, .max = 1.0f, .enabled = true},
+ }
+ },
+ {
+ .name = "Spacing",
+ .idname = "SPACING",
+ .min = 0.001f,
+ .type = BRUSH_CHANNEL_FLOAT,
+ .max = 4.0f,
+ .soft_min = 0.005f,
+ .soft_max = 2.0f,
+ .mappings = {
+ .pressure = {.curve = CURVE_PRESET_SMOOTH, .min = 0.0f, .max = 1.0f, .enabled = true},
+ }
+ },
+ {
+ .name = "Autosmooth",
+ .idname = "AUTOSMOOTH",
+ .type = BRUSH_CHANNEL_FLOAT,
+ .min = -1.0f,
+ .max = 4.0f,
+ .soft_min = 0.0f,
+ .soft_max = 1.0f,
+ .mappings = {
+ .pressure = {.curve = CURVE_PRESET_SMOOTH, .min = 0.0f, .max = 1.0f, .enabled = false, .inv = true},
+ }
+ },
+ {
+ .name = "Topology Rake",
+ .idname = "TOPOLOGY_RAKE",
+ .type = BRUSH_CHANNEL_FLOAT,
+ .min = -1.0f,
+ .max = 4.0f,
+ .soft_min = 0.0f,
+ .soft_max = 1.0f,
+ .mappings = {
+ .pressure = {.curve = CURVE_PRESET_SMOOTH, .min = 0.0f, .max = 1.0f, .enabled = false},
+ }
+ },
+ {
+ .name = "Autosmooth Radius Scale",
+ .idname = "AUTOSMOOTH_RADIUS_SCALE",
+ .type = BRUSH_CHANNEL_FLOAT,
+ .min = 0.0001f,
+ .max = 25.0f,
+ .soft_min = 0.1f,
+ .soft_max = 4.0f,
+ .mappings = {
+ .pressure = {.curve = CURVE_PRESET_SMOOTH, .min = 0.0f, .max = 1.0f, .enabled = false},
+ }
+ },
+ {
+ .name = "Rake Radius Scale",
+ .idname = "TOPOLOGY_RAKE_RADIUS_SCALE",
+ .type = BRUSH_CHANNEL_FLOAT,
+ .min = 0.0001f,
+ .max = 25.0f,
+ .soft_min = 0.1f,
+ .soft_max = 4.0f,
+ .mappings = {
+ .pressure = {.curve = CURVE_PRESET_SMOOTH, .min = 0.0f, .max = 1.0f, .enabled = false},
+ }
+ },
+ {
+ .name = "Face Set Slide",
+ .idname = "FSET_SLIDE",
+ .type = BRUSH_CHANNEL_FLOAT,
+ .min = 0.0001f,
+ .max = 1.0f,
+ .soft_min = 0.1f,
+ .soft_max = 1.0f,
+ .mappings = {
+ .pressure = {.curve = CURVE_PRESET_SMOOTH, .min = 0.0f, .max = 1.0f, .enabled = false},
+ }
+ },
+ {
+ .name = "Boundary Smooth",
+ .idname = "BOUNDARY_SMOOTH",
+ .type = BRUSH_CHANNEL_FLOAT,
+ .min = 0.0001f,
+ .max = 1.0f,
+ .soft_min = 0.1f,
+ .soft_max = 1.0f,
+ .mappings = {
+ .pressure = {.curve = CURVE_PRESET_SMOOTH, .min = 0.0f, .max = 1.0f, .enabled = false},
+ }
+ },
+ {
+ .name = "Projection",
+ .idname = "PROJECTION",
+ .type = BRUSH_CHANNEL_FLOAT,
+ .min = 0.0001f,
+ .max = 1.0f,
+ .soft_min = 0.1f,
+ .soft_max = 1.0f,
+ .mappings = {
+ .pressure = {.curve = CURVE_PRESET_SMOOTH, .min = 0.0f, .max = 1.0f, .enabled = false},
+ }
+ },
+ {
+ .name = "Topology Rake Mode",
+ .idname = "TOPOLOGY_RAKE_MODE",
+ .type = BRUSH_CHANNEL_ENUM,
+ .enumdef = {.items = {
+ {0, "BRUSH_DIRECTION", ICON_NONE, "Stroke", "Stroke Direction"},
+ {1, "CURVATURE", ICON_NONE, "Curvature", "Follow mesh curvature"},
+ {-1, 0}
+ }}
+ },
+ {
+ .name = "Automasking",
+ .idname = "AUTOMASKING",
+ .flag = BRUSH_CHANNEL_INHERIT_IF_UNSET | BRUSH_CHANNEL_INHERIT,
+ .type = BRUSH_CHANNEL_BITMASK,
+ .enumdef = {.items = {
+ {BRUSH_AUTOMASKING_BOUNDARY_EDGES, "BOUNDARY_EDGE", ICON_NONE, "Boundary Edges", ""},
+ {BRUSH_AUTOMASKING_BOUNDARY_FACE_SETS, "BOUNDARY_FACE_SETS", ICON_NONE, "Boundary Face Sets", ""},
+ {BRUSH_AUTOMASKING_CONCAVITY, "CONCAVITY", ICON_NONE, "Concave", ""},
+ {BRUSH_AUTOMASKING_INVERT_CONCAVITY, "INVERT_CONCAVITY", ICON_NONE, "Invert Concave", "Invert Concave Map"},
+ {BRUSH_AUTOMASKING_FACE_SETS, "FACE_SETS", ICON_NONE, "Face Sets", ""},
+ {BRUSH_AUTOMASKING_TOPOLOGY, "TOPOLOGY", ICON_NONE, "Topology", ""}
+ }}
+ },
+ {
+ .name = "Disable Dyntopo",
+ .idname = "DYNTOPO_DISABLED",
+ .type = BRUSH_CHANNEL_INT,
+ .flag = BRUSH_CHANNEL_NO_MAPPINGS,
+ .ivalue = 0
+ },
+ {
+ .name = "Detail Range",
+ .idname = "DYNTOPO_DETAIL_RANGE",
+ .type = BRUSH_CHANNEL_FLOAT,
+ .min = 0.001,
+ .max = 0.99,
+ .ivalue = 0
+ },
+};
+
+/* clang-format on */
+const int builtin_channel_len = ARRAY_SIZE(brush_builtin_channels);
+
+void BKE_brush_channel_free(BrushChannel *ch)
+{
+ for (int i = 0; i < BRUSH_MAPPING_MAX; i++) {
+ BKE_curvemapping_free_data(&ch->mappings[i].curve);
+ }
+}
+
+ATTR_NO_OPT void BKE_brush_channel_copy(BrushChannel *dst, BrushChannel *src)
+{
+ *dst = *src;
+
+ for (int i = 0; i < BRUSH_MAPPING_MAX; i++) {
+ BKE_curvemapping_copy_data(&dst->mappings[i].curve, &src->mappings[i].curve);
+ }
+}
+
+ATTR_NO_OPT void BKE_brush_channel_init(BrushChannel *ch, BrushChannelType *def)
+{
+ memset(ch, 0, sizeof(*ch));
+
+ strcpy(ch->name, def->name);
+ strcpy(ch->idname, def->idname);
+
+ ch->flag = def->flag;
+ ch->fvalue = def->fvalue;
+ ch->ivalue = def->ivalue;
+
+ ch->def = def;
+
+ for (int i = 0; i < BRUSH_MAPPING_MAX; i++) {
+ BrushMapping *map = ch->mappings + i;
+ CurveMapping *curve = &map->curve;
+
+ memset(curve, 0, sizeof(*curve));
+
+ float min, max;
+
+ BrushMappingDef *mdef = (&def->mappings.pressure) + i;
+
+ if (mdef->min != mdef->max) {
+ min = mdef->min;
+ max = mdef->max;
+ }
+ else {
+ min = 0.0f;
+ max = 1.0f;
+ }
+
+ if (mdef->inv) {
+ ch->mappings[i].flag |= BRUSH_MAPPING_INVERT;
+ }
+
+ int slope = CURVEMAP_SLOPE_POSITIVE;
+
+ for (int i = 0; i < 4; i++) {
+ BKE_curvemap_reset(&curve->cm[i],
+ &(struct rctf){.xmax = 0, .ymax = min, .xmax = 1, .ymax = max},
+ mdef->curve,
+ slope);
+ }
+
+ BKE_curvemapping_init(curve);
+
+ map->blendmode = mdef->blendmode;
+ map->factor = 1.0f;
+
+ if (mdef->enabled) {
+ map->flag |= BRUSH_MAPPING_ENABLED;
+ }
+ }
+}
+
+BrushChannelSet *BKE_brush_channelset_create()
+{
+ return (BrushChannelSet *)MEM_callocN(sizeof(BrushChannelSet), "BrushChannelSet");
+}
+
+void BKE_brush_channelset_free(BrushChannelSet *chset)
+{
+ if (chset->channels) {
+ for (int i = 0; i < chset->totchannel; i++) {
+ BKE_brush_channel_free(chset->channels + i);
+ }
+
+ MEM_freeN(chset->channels);
+ }
+ MEM_freeN(chset);
+}
+
+void BKE_brush_channelset_add(BrushChannelSet *chset, BrushChannel *ch)
+{
+ chset->totchannel++;
+
+ if (!chset->channels) {
+ chset->channels = MEM_callocN(sizeof(BrushChannel) * chset->totchannel, "chset->channels");
+ }
+ else {
+ chset->channels = MEM_recallocN(chset->channels, sizeof(BrushChannel) * chset->totchannel);
+ }
+
+ memcpy(chset->channels + chset->totchannel - 1, ch, sizeof(BrushChannel));
+}
+
+ATTR_NO_OPT BrushChannel *BKE_brush_channelset_lookup(BrushChannelSet *chset, const char *idname)
+{
+ for (int i = 0; i < chset->totchannel; i++) {
+ if (STREQ(chset->channels[i].idname, idname)) {
+ return chset->channels + i;
+ }
+ }
+
+ return NULL;
+}
+
+bool BKE_brush_channelset_has(BrushChannelSet *chset, const char *idname)
+{
+ return BKE_brush_channelset_lookup(chset, idname) != NULL;
+}
+
+ATTR_NO_OPT void BKE_brush_channelset_add_builtin(BrushChannelSet *chset, const char *idname)
+{
+ BrushChannelType *def = NULL;
+
+ for (int i = 0; i < builtin_channel_len; i++) {
+ BrushChannelType *def2 = brush_builtin_channels + i;
+
+ if (STREQ(def2->idname, idname)) {
+ def = def2;
+ break;
+ }
+ }
+
+ if (!def) {
+ printf("%s: Could not find brush %s\n", __func__, idname);
+ return;
+ }
+
+ BrushChannel ch;
+
+ BKE_brush_channel_init(&ch, def);
+ BKE_brush_channelset_add(chset, &ch);
+}
+
+bool BKE_brush_channelset_ensure_builtin(BrushChannelSet *chset, const char *idname)
+{
+ if (!BKE_brush_channelset_has(chset, idname)) {
+ BKE_brush_channelset_add_builtin(chset, idname);
+ return true;
+ }
+
+ return false;
+}
+
+#define ADDCH(name) BKE_brush_channelset_ensure_builtin(chset, name)
+#define GETCH(name) BKE_brush_channelset_lookup(chset, name)
+
+void BKE_brush_channelset_merge(BrushChannelSet *dst,
+ BrushChannelSet *child,
+ BrushChannelSet *parent)
+{
+ // first add missing channels
+
+ for (int step = 0; step < 2; step++) {
+ BrushChannelSet *chset = step ? parent : child;
+
+ for (int i = 0; i < chset->totchannel; i++) {
+ BrushChannel *ch = chset->channels + i;
+
+ if (BKE_brush_channelset_has(dst, ch->idname)) {
+ continue;
+ }
+
+ BrushChannel ch2;
+ BKE_brush_channel_copy(&ch2, ch);
+ BKE_brush_channelset_add(chset, &ch2);
+ }
+ }
+
+ for (int i = 0; i < child->totchannel; i++) {
+ BrushChannel *ch = child->channels + i;
+ BrushChannel *mch = BKE_brush_channelset_lookup(dst, ch->idname);
+ BrushChannel *pch = BKE_brush_channelset_lookup(parent, ch->name);
+
+ bool ok = ch->flag & BRUSH_CHANNEL_INHERIT;
+
+ if (ch->flag & BRUSH_CHANNEL_INHERIT) {
+ BKE_brush_channel_free(mch);
+ BKE_brush_channel_copy(mch, pch);
+ continue;
+ }
+
+ if (ch->type == BRUSH_CHANNEL_BITMASK && (ch->flag & BRUSH_CHANNEL_INHERIT_IF_UNSET)) {
+ mch->ivalue = ch->ivalue | pch->ivalue;
+ }
+ }
+}
+
+void BKE_brush_resolve_channels(Brush *brush, Sculpt *sd)
+{
+ if (brush->channels_final) {
+ BKE_brush_channelset_free(brush->channels_final);
+ }
+
+ brush->channels_final = BKE_brush_channelset_create();
+
+ BKE_brush_channelset_merge(brush->channels_final, brush->channels, sd->channels);
+
+ if (!brush->commandlist) {
+ return;
+ }
+
+ BrushCommandList *cl = brush->commandlist;
+
+ for (int i = 0; i < cl->totcommand; i++) {
+ BrushCommand *command = cl->commands + i;
+
+ if (command->params_final) {
+ BKE_brush_channelset_free(command->params_final);
+ }
+
+ command->params_final = BKE_brush_channelset_create();
+ BKE_brush_channelset_merge(command->params_final, command->params, brush->channels_final);
+ }
+}
+
+int BKE_brush_channel_get_int(BrushChannelSet *chset, char *idname)
+{
+ BrushChannel *ch = BKE_brush_channelset_lookup(chset, idname);
+
+ if (!ch) {
+ printf("%s, unknown channel %s", __func__, idname);
+ return 0;
+ }
+
+ return ch->ivalue;
+}
+
+float BKE_brush_channel_get_float(BrushChannelSet *chset, char *idname)
+{
+ BrushChannel *ch = BKE_brush_channelset_lookup(chset, idname);
+
+ if (!ch) {
+ printf("%s, unknown channel %s", __func__, idname);
+ return 0;
+ }
+
+ return ch->fvalue;
+}
+
+float BKE_brush_channel_set_float(BrushChannelSet *chset, char *idname, float val)
+{
+ BrushChannel *ch = BKE_brush_channelset_lookup(chset, idname);
+
+ if (!ch) {
+ printf("%s, unknown channel %s", __func__, idname);
+ return 0;
+ }
+
+ float old = ch->fvalue;
+
+ ch->fvalue = val;
+
+ return old;
+}
+
+void BKE_brush_init_toolsettings(Sculpt *sd)
+{
+ BrushChannelSet *chset = sd->channels = BKE_brush_channelset_create();
+
+ ADDCH("RADIUS");
+ ADDCH("STRENGTH");
+ ADDCH("AUTOMASKING");
+ ADDCH("DYNTOPO_DISABLED");
+ ADDCH("DYNTOPO_DETAIL_RANGE");
+}
+
+void BKE_brush_builtin_create(Brush *brush, int tool)
+{
+ if (brush->channels) {
+ BKE_brush_channelset_free(brush->channels);
+ }
+
+ BrushChannelSet *chset = brush->channels = BKE_brush_channelset_create();
+
+ ADDCH("RADIUS");
+ ADDCH("STRENGTH");
+ ADDCH("AUTOSMOOTH");
+ ADDCH("TOPOLOGY_RAKE");
+ ADDCH("AUTOSMOOTH_RADIUS_SCALE");
+ ADDCH("TOPOLOGY_RAKE_RADIUS_SCALE");
+
+ ADDCH("AUTOMASKING");
+
+ switch (tool) {
+ case SCULPT_TOOL_DRAW: {
+ BrushChannel *ch = GETCH("STRENGTH");
+
+ ch->mappings[BRUSH_MAPPING_PRESSURE].flag &= ~BRUSH_MAPPING_ENABLED;
+ ch->flag = BRUSH_CHANNEL_INHERIT;
+ break;
+ }
+ default: {
+ // implement me!
+ BKE_brush_channelset_free(chset);
+ brush->channels = NULL;
+ break;
+ }
+ }
+}
+
+BrushCommandList *BKE_brush_commandlist_create()
+{
+ return MEM_callocN(sizeof(BrushCommandList), "BrushCommandList");
+}
+void BKE_brush_commandlist_free(BrushCommandList *cl)
+{
+ for (int i = 0; i < cl->totcommand; i++) {
+ BrushCommand *cmd = cl->commands + i;
+
+ if (cmd->params) {
+ BKE_brush_channelset_free(cmd->params);
+ }
+
+ if (cmd->params_final) {
+ BKE_brush_channelset_free(cmd->params_final);
+ }
+ }
+
+ MEM_SAFE_FREE(cl->commands);
+
+ MEM_freeN(cl);
+}
+BrushCommand *BKE_brush_commandlist_add(BrushCommandList *cl)
+{
+ cl->totcommand++;
+
+ if (!cl->commands) {
+ cl->commands = MEM_callocN(sizeof(BrushCommand) * cl->totcommand, "BrushCommand");
+ }
+ else {
+ cl->commands = MEM_recallocN(cl->commands, sizeof(BrushCommand) * cl->totcommand);
+ }
+
+ BrushCommand *cmd = cl->commands + cl->totcommand - 1;
+ cmd->params = BKE_brush_channelset_create();
+ cmd->params_final = NULL;
+
+ return cmd;
+}
+
+BrushCommand *BKE_brush_command_init(BrushCommand *command, int tool)
+{
+ BrushChannelSet *chset = command->params;
+
+ ADDCH("SPACING");
+
+ switch (tool) {
+ case SCULPT_TOOL_DRAW:
+ ADDCH("RADIUS");
+ ADDCH("STRENGTH");
+ break;
+ case SCULPT_TOOL_SMOOTH:
+ ADDCH("RADIUS");
+ ADDCH("STRENGTH");
+ ADDCH("FSET_SLIDE");
+ ADDCH("BOUNDARY_SMOOTH");
+ ADDCH("PROJECTION");
+ break;
+ case SCULPT_TOOL_TOPOLOGY_RAKE:
+ ADDCH("RADIUS");
+ ADDCH("STRENGTH");
+ // ADDCH("FSET_SLIDE");
+ // ADDCH("BOUNDARY_SMOOTH");
+ ADDCH("PROJECTION");
+ ADDCH("TOPOLOGY_RAKE_MODE");
+ break;
+ case SCULPT_TOOL_DYNTOPO:
+ break;
+ }
+
+ return command;
+}
+
+void BKE_builtin_commandlist_create(BrushChannelSet *chset, BrushCommandList *cl, int tool)
+{
+ BrushCommand *cmd;
+
+ cmd = BKE_brush_commandlist_add(cl);
+ BKE_brush_command_init(cmd, tool);
+
+ for (int i = 0; i < cmd->totparam; i++) {
+ // inherit from brush channels for main tool
+ cmd->params->channels[i].flag |= BRUSH_CHANNEL_INHERIT;
+ }
+
+ float radius = BKE_brush_channel_get_float(chset, "RADIUS");
+ float autosmooth_scale = BKE_brush_channel_get_float(chset, "AUTOSMOOTH_RADIUS_SCALE");
+
+ float autosmooth = BKE_brush_channel_get_float(chset, "AUTOSMOOTH");
+ if (autosmooth > 0.0f) {
+ cmd = BKE_brush_command_init(BKE_brush_commandlist_add(cl), SCULPT_TOOL_SMOOTH);
+ BKE_brush_channel_set_float(cmd->params, "STRENGTH", autosmooth);
+ BKE_brush_channel_set_float(cmd->params, "RADIUS", radius * autosmooth_scale);
+ BKE_brush_channel_set_float(
+ cmd->params, "PROJECTION", BKE_brush_channel_get_float(chset, "AUTOSMOOTH_PROJECTION"));
+ }
+}
+
+void BKE_brush_channelset_read(BlendDataReader *reader, BrushChannelSet *cset)
+{
+ BLO_read_data_address(reader, &cset->channels);
+
+ for (int i = 0; i < cset->totchannel; i++) {
+ BrushChannel *ch = cset->channels + i;
+
+ for (int j = 0; j < BRUSH_MAPPING_MAX; j++) {
+ BKE_curvemapping_blend_read(reader, &ch->mappings[j].curve);
+ }
+ }
+}
+
+void BKE_brush_channelset_write(BlendWriter *writer, BrushChannelSet *cset)
+{
+ BLO_write_struct(writer, BrushChannelSet, cset);
+ BLO_write_struct_array_by_name(writer, "BrushChannel", cset->totchannel, cset->channels);
+
+ for (int i = 0; i < cset->totchannel; i++) {
+ BrushChannel *ch = cset->channels + i;
+
+ for (int j = 0; j < BRUSH_MAPPING_MAX; j++) {
+ BKE_curvemapping_blend_write(writer, &ch->mappings[j].curve);
+ }
+ }
+}
+
+/* clang-format on */
+
+/* idea for building built-in preset node graphs:
+from brush_builder import Builder;
+
+def build(input, output):
+ input.add("Strength", "float", "strength").range(0.0, 3.0)
+ input.add("Radius", "float", "radius").range(0.01, 1024.0)
+ input.add("Autosmooth", "float", "autosmooth").range(0.0, 4.0)
+ input.add("Topology Rake", "float", "topology rake").range(0.0, 4.0)
+ input.add("Smooth Radius Scale", "float", "autosmooth_radius_scale").range(0.01, 5.0)
+ input.add("Rake Radius Scale", "float", "toporake_radius_scale").range(0.01, 5.0)
+
+ draw = input.make.tool("DRAW")
+ draw.radius = input.radius
+ draw.strength = input.strength
+
+ smooth = input.make.tool("SMOOTH")
+ smooth.radius = input.radius * input.autosmooth_radius_scale
+ smooth.strength = input.autosmooth;
+ smooth.flow = draw.outflow
+
+ rake = input.make.tool("TOPORAKE")
+ rake.radius = input.radius * input.toporake_radius_scale
+ rake.strength = input.topology;
+ rake.flow = smooth.outflow
+
+ output.out = rake.outflow
+
+preset = Builder(build)
+
+*/
+
+/*
+bNodeType sculpt_tool_node = {
+ .idname = "SculptTool",
+ .ui_name = "SculptTool",
+};*/
+/* cland-format on */
diff --git a/source/blender/blenkernel/intern/cdderivedmesh.c b/source/blender/blenkernel/intern/cdderivedmesh.c
index 039a971fe2c..8c3ec0b2ff8 100644
--- a/source/blender/blenkernel/intern/cdderivedmesh.c
+++ b/source/blender/blenkernel/intern/cdderivedmesh.c
@@ -171,8 +171,16 @@ static const MeshElemMap *cdDM_getPolyMap(Object *ob, DerivedMesh *dm)
if (!cddm->pmap && ob->type == OB_MESH) {
Mesh *me = ob->data;
- BKE_mesh_vert_poly_map_create(
- &cddm->pmap, &cddm->pmap_mem, me->mpoly, me->mloop, me->totvert, me->totpoly, me->totloop);
+ BKE_mesh_vert_poly_map_create(&cddm->pmap,
+ &cddm->pmap_mem,
+ me->mvert,
+ me->medge,
+ me->mpoly,
+ me->mloop,
+ me->totvert,
+ me->totpoly,
+ me->totloop,
+ false);
}
return cddm->pmap;
diff --git a/source/blender/blenkernel/intern/collection.c b/source/blender/blenkernel/intern/collection.c
index 2d172f23428..d088d4a4ed2 100644
--- a/source/blender/blenkernel/intern/collection.c
+++ b/source/blender/blenkernel/intern/collection.c
@@ -688,9 +688,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 f2c2e552a9f..c6d84965185 100644
--- a/source/blender/blenkernel/intern/colortools.c
+++ b/source/blender/blenkernel/intern/colortools.c
@@ -1241,7 +1241,8 @@ void BKE_curvemapping_blend_write(BlendWriter *writer, const CurveMapping *cumap
BKE_curvemapping_curves_blend_write(writer, cumap);
}
-void BKE_curvemapping_curves_blend_write(BlendWriter *writer, const CurveMapping *cumap)
+ATTR_NO_OPT void BKE_curvemapping_curves_blend_write(BlendWriter *writer,
+ const CurveMapping *cumap)
{
for (int a = 0; a < CM_TOT; a++) {
BLO_write_struct_array(writer, CurveMapPoint, cumap->cm[a].totpoint, cumap->cm[a].curve);
diff --git a/source/blender/blenkernel/intern/curve_bevel.c b/source/blender/blenkernel/intern/curve_bevel.c
index d205d8cca46..f853e584537 100644
--- a/source/blender/blenkernel/intern/curve_bevel.c
+++ b/source/blender/blenkernel/intern/curve_bevel.c
@@ -100,7 +100,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.c b/source/blender/blenkernel/intern/customdata.c
index ad2d5d267d5..4ed94d2f059 100644
--- a/source/blender/blenkernel/intern/customdata.c
+++ b/source/blender/blenkernel/intern/customdata.c
@@ -35,6 +35,7 @@
#include "DNA_meshdata_types.h"
#include "BLI_bitmap.h"
+#include "BLI_compiler_attrs.h"
#include "BLI_endian_switch.h"
#include "BLI_math.h"
#include "BLI_math_color_blend.h"
@@ -73,6 +74,32 @@ BLI_STATIC_ASSERT(ARRAY_SIZE(((CustomData *)NULL)->typemap) == CD_NUMTYPES, "siz
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;
+}
+
/** Update mask_dst with layers defined in mask_src (equivalent to a bitwise OR). */
void CustomData_MeshMasks_update(CustomData_MeshMasks *mask_dst,
const CustomData_MeshMasks *mask_src)
@@ -1475,6 +1502,93 @@ 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)
+{
+ const MDynTopoVert *mv = (MDynTopoVert *)dest;
+
+ memcpy(dest, source, count * sizeof(MDynTopoVert));
+}
+
+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];
+ MDynTopoVert *mv = (MDynTopoVert *)dest;
+ float totweight = 0.0f;
+
+ if (count == 0) {
+ memset(mv, 0, sizeof(*mv));
+ return;
+ }
+
+ zero_v3(co);
+ zero_v3(no);
+ origmask = 0.0f;
+ zero_v4(color);
+
+ for (int i = 0; i < count; i++) {
+ MDynTopoVert *mv2 = (MDynTopoVert *)sources[i];
+ float w;
+
+ if (i == 0) { // copy flag from first source
+ mv->flag = mv2->flag;
+ mv->stroke_id = mv2->stroke_id;
+ }
+
+ if (sub_weights) {
+ w = sub_weights[i];
+ }
+ else {
+ w = 1.0f;
+ }
+
+ madd_v3_v3fl(co, mv2->origco, w);
+ madd_v3_v3fl(no, mv2->origno, w);
+ madd_v4_v4fl(color, mv2->origcolor, w);
+ origmask += mv2->origmask * w;
+
+ totweight += w;
+ }
+
+ float mul = 1.0f / totweight;
+
+ mul_v3_fl(co, mul);
+ normalize_v3(no);
+
+ mul_v4_fl(color, mul);
+ origmask *= mul;
+
+ copy_v3_v3(mv->origco, co);
+ copy_v3_v3(mv->origno, no);
+ copy_v4_v4(mv->origcolor, color);
+
+ mv->origmask = origmask;
+}
+
+static void layerCopy_noop(const void *UNUSED(source), void *UNUSED(dest), int UNUSED(count))
+{
+ // do nothing
+}
+
+static void layerInterp_noop(const void **UNUSED(sources),
+ const float *UNUSED(weights),
+ const float *UNUSED(sub_weights),
+ int UNUSED(count),
+ void *UNUSED(dest))
+{
+ // 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 const LayerTypeInfo LAYERTYPEINFO[CD_NUMTYPES] = {
/* 0: CD_MVERT */
{sizeof(MVert), "MVert", 1, NULL, NULL, NULL, NULL, NULL, NULL},
@@ -1856,7 +1970,24 @@ static const LayerTypeInfo LAYERTYPEINFO[CD_NUMTYPES] = {
NULL,
NULL,
NULL},
-};
+ /* 51 CD_DYNTOPO_VERT */
+ {sizeof(MDynTopoVert),
+ "MDynTopoVert",
+ 1,
+ NULL, // flag singleton layer
+ layerDynTopoVert_copy,
+ NULL,
+ layerDynTopoVert_interp},
+ /*52 CD_MESH_ID */
+ {sizeof(unsigned int),
+ "MIntProperty",
+ 1,
+ NULL, // flag singleton layer
+ layerCopy_propInt,
+ NULL,
+ layerInterp_noop,
+ NULL,
+ layerDefault_mesh_id}};
static const char *LAYERTYPENAMES[CD_NUMTYPES] = {
/* 0-4 */ "CDMVert",
@@ -1912,62 +2043,65 @@ static const char *LAYERTYPENAMES[CD_NUMTYPES] = {
"CDPropFloat3",
"CDPropFloat2",
"CDPropBoolean",
-};
+ "CDDyntopoVert"};
const CustomData_MeshMasks CD_MASK_BAREMESH = {
- .vmask = CD_MASK_MVERT | CD_MASK_BWEIGHT,
- .emask = CD_MASK_MEDGE | CD_MASK_BWEIGHT,
+ .vmask = CD_MASK_MVERT | CD_MASK_BWEIGHT | CD_MASK_MESH_ID,
+ .emask = CD_MASK_MEDGE | CD_MASK_BWEIGHT | CD_MASK_MESH_ID,
.fmask = 0,
- .lmask = CD_MASK_MLOOP,
- .pmask = CD_MASK_MPOLY | CD_MASK_FACEMAP,
+ .lmask = CD_MASK_MLOOP | CD_MASK_MESH_ID,
+ .pmask = CD_MASK_MPOLY | CD_MASK_FACEMAP | CD_MASK_MESH_ID,
};
const CustomData_MeshMasks CD_MASK_BAREMESH_ORIGINDEX = {
- .vmask = CD_MASK_MVERT | CD_MASK_BWEIGHT | CD_MASK_ORIGINDEX,
- .emask = CD_MASK_MEDGE | CD_MASK_BWEIGHT | CD_MASK_ORIGINDEX,
+ .vmask = CD_MASK_MVERT | CD_MASK_BWEIGHT | CD_MASK_ORIGINDEX | CD_MASK_MESH_ID,
+ .emask = CD_MASK_MEDGE | CD_MASK_BWEIGHT | CD_MASK_ORIGINDEX | CD_MASK_MESH_ID,
.fmask = 0,
- .lmask = CD_MASK_MLOOP,
- .pmask = CD_MASK_MPOLY | CD_MASK_FACEMAP | CD_MASK_ORIGINDEX,
+ .lmask = CD_MASK_MLOOP | CD_MASK_MESH_ID,
+ .pmask = CD_MASK_MPOLY | CD_MASK_FACEMAP | CD_MASK_ORIGINDEX | CD_MASK_MESH_ID,
};
const CustomData_MeshMasks CD_MASK_MESH = {
.vmask = (CD_MASK_MVERT | CD_MASK_MDEFORMVERT | CD_MASK_MVERT_SKIN | CD_MASK_PAINT_MASK |
- CD_MASK_PROP_ALL | CD_MASK_PROP_COLOR),
- .emask = (CD_MASK_MEDGE | CD_MASK_FREESTYLE_EDGE | CD_MASK_PROP_ALL),
+ CD_MASK_PROP_ALL | CD_MASK_PROP_COLOR | CD_MASK_MESH_ID),
+ .emask = (CD_MASK_MEDGE | CD_MASK_FREESTYLE_EDGE | CD_MASK_PROP_ALL | CD_MASK_MESH_ID),
.fmask = 0,
.lmask = (CD_MASK_MLOOP | CD_MASK_MDISPS | CD_MASK_MLOOPUV | CD_MASK_MLOOPCOL |
- CD_MASK_CUSTOMLOOPNORMAL | CD_MASK_GRID_PAINT_MASK | CD_MASK_PROP_ALL),
+ CD_MASK_CUSTOMLOOPNORMAL | CD_MASK_GRID_PAINT_MASK | CD_MASK_PROP_ALL |
+ CD_MASK_MESH_ID),
.pmask = (CD_MASK_MPOLY | CD_MASK_FACEMAP | CD_MASK_FREESTYLE_FACE | CD_MASK_PROP_ALL |
- CD_MASK_SCULPT_FACE_SETS),
+ CD_MASK_SCULPT_FACE_SETS | CD_MASK_MESH_ID),
};
const CustomData_MeshMasks CD_MASK_EDITMESH = {
.vmask = (CD_MASK_MDEFORMVERT | CD_MASK_PAINT_MASK | CD_MASK_MVERT_SKIN | CD_MASK_SHAPEKEY |
- CD_MASK_SHAPE_KEYINDEX | CD_MASK_PROP_ALL | CD_MASK_PROP_COLOR),
- .emask = (CD_MASK_PROP_ALL),
+ CD_MASK_SHAPE_KEYINDEX | CD_MASK_PROP_ALL | CD_MASK_PROP_COLOR | CD_MASK_MESH_ID),
+ .emask = (CD_MASK_PROP_ALL | CD_MASK_MESH_ID),
.fmask = 0,
.lmask = (CD_MASK_MDISPS | CD_MASK_MLOOPUV | CD_MASK_MLOOPCOL | CD_MASK_CUSTOMLOOPNORMAL |
- CD_MASK_GRID_PAINT_MASK | CD_MASK_PROP_ALL),
- .pmask = (CD_MASK_FACEMAP | CD_MASK_PROP_ALL | CD_MASK_SCULPT_FACE_SETS),
+ CD_MASK_GRID_PAINT_MASK | CD_MASK_PROP_ALL | CD_MASK_MESH_ID),
+ .pmask = (CD_MASK_FACEMAP | CD_MASK_PROP_ALL | CD_MASK_SCULPT_FACE_SETS | 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_PROP_COLOR),
- .emask = (CD_MASK_ORIGINDEX | CD_MASK_FREESTYLE_EDGE | CD_MASK_PROP_ALL),
+ CD_MASK_PROP_COLOR | CD_MASK_MESH_ID),
+ .emask = (CD_MASK_ORIGINDEX | CD_MASK_FREESTYLE_EDGE | CD_MASK_PROP_ALL | CD_MASK_MESH_ID),
.fmask = (CD_MASK_ORIGINDEX | CD_MASK_ORIGSPACE | CD_MASK_PREVIEW_MCOL | CD_MASK_TANGENT),
.lmask = (CD_MASK_MLOOPUV | CD_MASK_MLOOPCOL | CD_MASK_CUSTOMLOOPNORMAL |
- CD_MASK_PREVIEW_MLOOPCOL | CD_MASK_ORIGSPACE_MLOOP |
- CD_MASK_PROP_ALL), /* XXX MISSING CD_MASK_MLOOPTANGENT ? */
+ CD_MASK_PREVIEW_MLOOPCOL | CD_MASK_ORIGSPACE_MLOOP | CD_MASK_PROP_ALL |
+ CD_MASK_MESH_ID), /* XXX MISSING CD_MASK_MLOOPTANGENT ? */
.pmask = (CD_MASK_ORIGINDEX | CD_MASK_FREESTYLE_FACE | CD_MASK_FACEMAP | CD_MASK_PROP_ALL |
- CD_MASK_SCULPT_FACE_SETS),
+ CD_MASK_SCULPT_FACE_SETS | CD_MASK_MESH_ID),
};
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_PROP_COLOR),
- .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_PROP_COLOR |
+ CD_MASK_DYNTOPO_VERT | 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,
.lmask = (CD_MASK_MDISPS | CD_MASK_MLOOPUV | CD_MASK_MLOOPCOL | CD_MASK_CUSTOMLOOPNORMAL |
- CD_MASK_GRID_PAINT_MASK | CD_MASK_PROP_ALL),
+ CD_MASK_GRID_PAINT_MASK | CD_MASK_PROP_ALL | CD_MASK_MESH_ID),
.pmask = (CD_MASK_FREESTYLE_FACE | CD_MASK_FACEMAP | CD_MASK_PROP_ALL |
- CD_MASK_SCULPT_FACE_SETS),
+ CD_MASK_SCULPT_FACE_SETS | CD_MASK_MESH_ID),
};
/**
* cover values copied by #mesh_loops_to_tessdata
@@ -2009,6 +2143,11 @@ static const LayerTypeInfo *layerType_getInfo(int type)
return &LAYERTYPEINFO[type];
}
+int CustomData_get_elem_size(CustomDataLayer *layer)
+{
+ return layerType_getInfo(layer->type)->size;
+}
+
static const char *layerType_getName(int type)
{
if (type < 0 || type >= CD_NUMTYPES) {
@@ -2093,6 +2232,26 @@ 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 = 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;
+ }
+ }
+}
+
bool CustomData_merge(const struct CustomData *source,
struct CustomData *dest,
CustomDataMask mask,
@@ -2285,11 +2444,45 @@ 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, 12, 6, 4, 2, 1};
+ BLI_bitmap *donemap = BLI_BITMAP_NEW_ALLOCA(data->totlayer);
+
+ // do large structs first
+ for (int j = 0; j < data->totlayer; j++) {
+ typeInfo = layerType_getInfo(data->layers[j].type);
+ if (typeInfo->size > 16 || typeInfo->size == 10) {
+ int size = (int)typeInfo->size;
+
+ BLI_BITMAP_SET(donemap, j, true);
+
+ // align to 8-byte boundary
+ if (size & 7) {
+ size += 8 - (size & 7);
+ }
- data->layers[i].offset = offset;
- offset += typeInfo->size;
+ data->layers[j].offset = offset;
+ offset += size;
+ }
+ }
+
+ for (int i = 0; i < ARRAY_SIZE(aligns) + 1; i++) {
+ for (int j = 0; j < data->totlayer; j++) {
+ if (BLI_BITMAP_TEST(donemap, j)) {
+ continue;
+ }
+
+ typeInfo = layerType_getInfo(data->layers[j].type);
+
+ if (i < ARRAY_SIZE(aligns) && typeInfo->size != aligns[i]) {
+ continue;
+ }
+
+ BLI_BITMAP_SET(donemap, j, true);
+
+ data->layers[j].offset = offset;
+ offset += typeInfo->size;
+ }
}
data->totsize = offset;
@@ -2883,6 +3076,24 @@ bool CustomData_is_referenced_layer(struct CustomData *data, int type)
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, int totelem)
{
int i, j;
@@ -3739,6 +3950,12 @@ static void CustomData_bmesh_set_default_n(CustomData *data, void **block, int n
int offset = data->layers[n].offset;
const LayerTypeInfo *typeInfo = layerType_getInfo(data->layers[n].type);
+ /* can't allow this to be called on CD_MESH_ID */
+
+ if (data->layers[n].type == CD_MESH_ID) {
+ return;
+ }
+
if (typeInfo->set_default) {
typeInfo->set_default(POINTER_OFFSET(*block, offset), 1);
}
@@ -3758,6 +3975,95 @@ void CustomData_bmesh_set_default(CustomData *data, void **block)
}
}
+void CustomData_bmesh_swap_data_simple(CustomData *data, void **block1, void **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;
+
+ // unswap ids if they exist
+ if (cd_id != -1 && *block1 && *block2) {
+ int *id1 = (int *)(((char *)*block1) + cd_id);
+ int *id2 = (int *)(((char *)*block2) + cd_id);
+
+ tmp = *id1;
+ *id1 = *id2;
+ *id2 = tmp;
+ }
+}
+
+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) {
+ memset(*dest_block, 0, dest->totsize);
+ 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 (source->layers[src_i].type == CD_MESH_ID) {
+ // do not swap ids
+ 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,
void *src_block,
@@ -3775,50 +4081,59 @@ void CustomData_bmesh_copy_data_exclude_by_type(const CustomData *source,
}
}
+ for (int dest_i = 0; dest_i < dest->totlayer; dest_i++) {
+ CustomData_bmesh_set_default_n(dest, dest_block, dest_i);
+ 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)) {
+ 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,
diff --git a/source/blender/blenkernel/intern/data_transfer.c b/source/blender/blenkernel/intern/data_transfer.c
index b83621e8b79..c4414f93a87 100644
--- a/source/blender/blenkernel/intern/data_transfer.c
+++ b/source/blender/blenkernel/intern/data_transfer.c
@@ -150,6 +150,7 @@ bool BKE_object_data_transfer_get_dttypes_capacity(const int dtdata_types,
case DT_TYPE_UV:
ret = true;
break;
+ case DT_TYPE_PROPCOL:
case DT_TYPE_VCOL:
*r_advanced_mixing = true;
*r_threshold = true;
@@ -230,12 +231,12 @@ int BKE_object_data_transfer_dttype_to_cdtype(const int dtdata_type)
return CD_FAKE_SHARP;
case DT_TYPE_FREESTYLE_FACE:
return CD_FREESTYLE_FACE;
-
case DT_TYPE_VCOL:
return CD_MLOOPCOL;
case DT_TYPE_LNOR:
return CD_FAKE_LNOR;
-
+ case DT_TYPE_PROPCOL:
+ return CD_PROP_COLOR;
default:
BLI_assert(0);
}
@@ -253,6 +254,8 @@ int BKE_object_data_transfer_dttype_to_srcdst_index(const int dtdata_type)
return DT_MULTILAYER_INDEX_UV;
case DT_TYPE_VCOL:
return DT_MULTILAYER_INDEX_VCOL;
+ case DT_TYPE_PROPCOL:
+ return DT_MULTILAYER_INDEX_PROPCOL;
default:
return DT_MULTILAYER_INDEX_INVALID;
}
diff --git a/source/blender/blenkernel/intern/dyntopo.c b/source/blender/blenkernel/intern/dyntopo.c
new file mode 100644
index 00000000000..51757540ba3
--- /dev/null
+++ b/source/blender/blenkernel/intern/dyntopo.c
@@ -0,0 +1,6175 @@
+#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_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_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_pbvh.h"
+
+#include "bmesh.h"
+#include "pbvh_intern.h"
+
+#include <stdio.h>
+
+//#define DYNTOPO_REPORT
+
+#define DYNVERT_VALENCE_TEMP (1 << 14)
+
+#define USE_NEW_SPLIT
+#define DYNVERT_SMOOTH_BOUNDARY (DYNVERT_BOUNDARY | DYNVERT_FSET_BOUNDARY | DYNVERT_SHARP_BOUNDARY)
+#define DYNVERT_ALL_BOUNDARY \
+ (DYNVERT_BOUNDARY | DYNVERT_FSET_BOUNDARY | DYNVERT_SHARP_BOUNDARY | DYNVERT_SEAM_BOUNDARY)
+#define DYNVERT_SMOOTH_CORNER (DYNVERT_CORNER | DYNVERT_FSET_CORNER | DYNVERT_SHARP_CORNER)
+#define DYNVERT_ALL_CORNER \
+ (DYNVERT_CORNER | DYNVERT_FSET_CORNER | DYNVERT_SHARP_CORNER | DYNVERT_SEAM_CORNER)
+
+#define DYNTOPO_MAX_ITER 4096
+
+#define DYNTOPO_USE_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 EdgeQueueContext;
+
+static bool destroy_nonmanifold_fins(PBVH *pbvh, BMEdge *e_root);
+static bool check_face_is_tri(PBVH *pbvh, BMFace *f);
+static bool check_vert_fan_are_tris(PBVH *pbvh, BMVert *v);
+static void pbvh_split_edges(struct EdgeQueueContext *eq_ctx,
+ PBVH *pbvh,
+ BMesh *bm,
+ BMEdge **edges,
+ int totedge,
+ bool ignore_isolated_edges);
+void bm_log_message(const char *fmt, ...);
+void pbvh_bmesh_check_nodes_simple(PBVH *pbvh);
+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,
+ bool is_collapse);
+
+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 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 = NULL;
+ MDynTopoVert *mv = BKE_PBVH_DYNVERT(pbvh->cd_dyn_vert, v);
+
+ mv->flag |= DYNVERT_NEED_VALENCE | DYNVERT_NEED_BOUNDARY | DYNVERT_NEED_DISK_SORT |
+ DYNVERT_NEED_TRIANGULATE;
+ }
+
+ BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
+ e->v1_disk_link.next = e->v1_disk_link.prev = NULL;
+ e->v2_disk_link.next = e->v2_disk_link.prev = NULL;
+ e->l = NULL;
+
+ 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!", 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, NULL);
+ }
+
+ 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, NULL, 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;
+ }
+
+ 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!");
+ }
+ } 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
+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 = NULL;
+ 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;
+ }
+
+ 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
+
+static BMEdge *bmesh_edge_create_log(PBVH *pbvh, BMVert *v1, BMVert *v2, BMEdge *e_example)
+{
+ BMEdge *e = BM_edge_exists(v1, v2);
+
+ if (e) {
+ return e;
+ }
+
+ e = BM_edge_create(pbvh->bm, v1, v2, e_example, BM_CREATE_NOP);
+
+ if (e_example) {
+ e->head.hflag |= e_example->head.hflag;
+ }
+
+ BM_log_edge_added(pbvh->bm_log, e);
+
+ return e;
+}
+
+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;
+
+ MDynTopoVert *mv1 = BKE_PBVH_DYNVERT(pbvh->cd_dyn_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);
+ }
+
+ // BKE_pbvh_bmesh_check_origdata(pbvh, v, pbvh->stroke_id);
+
+ zero_v3(co);
+ zero_v3(origco);
+
+ // this is a manual edge walk
+
+ BMEdge *e = v->e;
+ if (!e) {
+ return;
+ }
+
+ if (mv1->flag & DYNVERT_NEED_BOUNDARY) {
+ return; // can't update boundary in thread
+ }
+
+ // pbvh_check_vert_boundary(pbvh, v);
+
+ const int cd_dyn_vert = pbvh->cd_dyn_vert;
+
+ const bool bound1 = mv1->flag & DYNVERT_SMOOTH_BOUNDARY;
+
+ if (mv1->flag & DYNVERT_SMOOTH_CORNER) {
+ return;
+ }
+
+ do {
+ BMVert *v2 = e->v1 == v ? e->v2 : e->v1;
+
+ // can't check for boundary here, thread
+ pbvh_check_vert_boundary(pbvh, v2);
+
+ MDynTopoVert *mv2 = BKE_PBVH_DYNVERT(cd_dyn_vert, v2);
+ const bool bound2 = mv2->flag & DYNVERT_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);
+
+ volatile int stroke_id = mv1->stroke_id;
+
+ // atomic_cas_int32(&mv1->stroke_id, stroke_id, pbvh->stroke_id);
+}
+
+static void pbvh_kill_vert(PBVH *pbvh, BMVert *v)
+{
+ BMEdge *e = v->e;
+
+ if (e) {
+ do {
+ BM_log_edge_removed(pbvh->bm_log, e);
+ } while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e);
+ }
+
+ BM_vert_kill(pbvh->bm, v);
+}
+
+static void pbvh_log_vert_edges_kill(PBVH *pbvh, BMVert *v)
+{
+ BMEdge *e = v->e;
+
+ if (e) {
+ do {
+ BM_log_edge_removed(pbvh->bm_log, e);
+ e = BM_DISK_EDGE_NEXT(e, v);
+ } while (e != v->e);
+ }
+}
+
+static void bm_edges_from_tri(PBVH *pbvh, BMVert *v_tri[3], BMEdge *e_tri[3])
+{
+ e_tri[0] = bmesh_edge_create_log(pbvh, v_tri[0], v_tri[1], NULL);
+ e_tri[1] = bmesh_edge_create_log(pbvh, v_tri[1], v_tri[2], NULL);
+ e_tri[2] = bmesh_edge_create_log(pbvh, v_tri[2], v_tri[0], NULL);
+}
+
+static void bm_edges_from_tri_example(PBVH *pbvh, BMVert *v_tri[3], BMEdge *e_tri[3])
+{
+ e_tri[0] = bmesh_edge_create_log(pbvh, v_tri[0], v_tri[1], e_tri[0]);
+ e_tri[1] = bmesh_edge_create_log(pbvh, v_tri[1], v_tri[2], e_tri[1]);
+ e_tri[2] = bmesh_edge_create_log(pbvh, v_tri[2], v_tri[0], e_tri[2]);
+}
+
+BLI_INLINE void bm_face_as_array_index_tri(BMFace *f, int r_index[3])
+{
+ 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);
+}
+
+/**
+ * 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).
+ *
+ * <pre>
+ * l_radial_first->v & l_radial_first->next->v
+ * +---+
+ * | /
+ * | /
+ * + v_opposite
+ * </pre>
+ *
+ * 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 NULL;
+}
+
+/**
+ * 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)
+{
+ while (true) {
+ BMVert **v_next_p = (BMVert **)BLI_ghash_lookup_p(deleted_verts, v);
+ if (v_next_p == NULL) {
+ /* Not remapped. */
+ return v;
+ }
+ if (*v_next_p == NULL) {
+ /* removed and not remapped */
+ return NULL;
+ }
+
+ /* remapped */
+ v = *v_next_p;
+ }
+}
+
+static void pbvh_bmesh_copy_facedata(PBVH *pbvh, BMesh *bm, BMFace *dest, BMFace *src)
+{
+ dest->head.hflag = src->head.hflag;
+ dest->mat_nr = src->mat_nr;
+
+ int ni = BM_ELEM_CD_GET_INT(dest, pbvh->cd_face_node_offset);
+
+ CustomData_bmesh_copy_data(&bm->pdata, &bm->pdata, src->head.data, &dest->head.data);
+
+ BM_ELEM_CD_SET_INT(dest, pbvh->cd_face_node_offset, ni);
+}
+
+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->bm, co, NULL, BM_CREATE_NOP);
+ MDynTopoVert *mv = BKE_PBVH_DYNVERT(pbvh->cd_dyn_vert, v);
+
+ mv->flag |= DYNVERT_NEED_BOUNDARY | DYNVERT_NEED_DISK_SORT | DYNVERT_NEED_VALENCE;
+
+ if (v_example) {
+ v->head.hflag = v_example->head.hflag;
+
+ CustomData_bmesh_copy_data(
+ &pbvh->bm->vdata, &pbvh->bm->vdata, v_example->head.data, &v->head.data);
+
+ /* This value is logged below */
+ copy_v3_v3(v->no, no);
+
+ // keep MDynTopoVert copied from v_example as-is
+ }
+ else {
+ MDynTopoVert *mv = BKE_PBVH_DYNVERT(pbvh->cd_dyn_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->bm_log, v, cd_vert_mask_offset);
+ v->head.index = pbvh->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->bm, v1, v2, NULL, BM_CREATE_NOP);
+ BM_log_edge_added(pbvh->bm_log, e);
+ }
+
+ e_tri2[i] = e;
+ }
+
+ // f = BM_face_create_verts(pbvh->bm, v_tri, 3, f_example, BM_CREATE_NOP, true);
+ f = BM_face_create(pbvh->bm, v_tri, e_tri2, 3, f_example, BM_CREATE_NOP);
+ }
+ else {
+ f = BM_face_create(pbvh->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->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;
+ }
+
+ MDynTopoVert *mv = BKE_PBVH_DYNVERT(pbvh->cd_dyn_vert, l->v);
+ mv->flag |= DYNVERT_NEED_DISK_SORT | DYNVERT_NEED_BOUNDARY | DYNVERT_NEED_VALENCE;
+
+ l = l->next;
+ } while (l != f->l_first);
+ }
+ else {
+ BMLoop *l = f->l_first;
+ do {
+
+ MDynTopoVert *mv = BKE_PBVH_DYNVERT(pbvh->cd_dyn_vert, l->v);
+ mv->flag |= DYNVERT_NEED_DISK_SORT | DYNVERT_NEED_BOUNDARY | DYNVERT_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->bm, co, v_example, BM_CREATE_NOP);
+ BM_ELEM_CD_SET_INT(v, pbvh->cd_vert_node_offset, DYNTOPO_NODE_NONE);
+
+ MDynTopoVert *mv = BM_ELEM_CD_GET_VOID_P(v, pbvh->cd_dyn_vert);
+ mv->flag |= DYNVERT_NEED_VALENCE | DYNVERT_NEED_DISK_SORT | DYNVERT_NEED_BOUNDARY;
+
+ 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 NULL;
+}
+
+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, NULL);
+ }
+
+ /* 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;
+}
+
+static bool pbvh_bmesh_vert_relink(PBVH *pbvh, BMVert *v)
+{
+ const int cd_vert_node = pbvh->cd_vert_node_offset;
+ const int cd_face_node = pbvh->cd_face_node_offset;
+
+ BMFace *f;
+ BLI_assert(BM_ELEM_CD_GET_INT(v, cd_vert_node) == DYNTOPO_NODE_NONE);
+
+ bool added = false;
+
+ BM_FACES_OF_VERT_ITER_BEGIN (f, v) {
+ const int ni = BM_ELEM_CD_GET_INT(f, cd_face_node);
+
+ if (ni == DYNTOPO_NODE_NONE) {
+ continue;
+ }
+
+ PBVHNode *node = pbvh->nodes + ni;
+
+ if (BM_ELEM_CD_GET_INT(v, cd_vert_node) == DYNTOPO_NODE_NONE) {
+ BLI_table_gset_add(node->bm_unique_verts, v);
+ BM_ELEM_CD_SET_INT(v, cd_vert_node, ni);
+ }
+ }
+ BM_FACES_OF_VERT_ITER_END;
+
+ return added;
+}
+
+static 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, NULL);
+ v_node->flag |= 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 |= updateflag;
+
+ BLI_assert(!BLI_table_gset_haskey(f_node->bm_unique_verts, v));
+ }
+ }
+ BM_FACES_OF_VERT_ITER_END;
+}
+
+static 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;
+ }
+
+ /* 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, NULL);
+ 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->bm_log, f);
+ }
+
+ /* mark node for update */
+ f_node->flag |= PBVH_UpdateDrawBuffers | PBVH_UpdateNormals | PBVH_UpdateTris |
+ PBVH_UpdateOtherVerts;
+}
+
+void BKE_pbvh_bmesh_remove_face(PBVH *pbvh, BMFace *f, bool log_face)
+{
+ pbvh_bmesh_face_remove(pbvh, f, log_face, true, true);
+}
+
+void BKE_pbvh_bmesh_remove_vertex(PBVH *pbvh, BMVert *v, bool log_vert)
+{
+ pbvh_bmesh_vert_remove(pbvh, v);
+
+ if (log_vert) {
+ BM_log_vert_removed(pbvh->bm_log, v, pbvh->cd_vert_mask_offset);
+ }
+}
+
+void BKE_pbvh_bmesh_add_face(PBVH *pbvh, struct BMFace *f, bool log_face, bool force_tree_walk)
+{
+ int ni = -1;
+
+ if (force_tree_walk) {
+ bke_pbvh_insert_face(pbvh, f);
+
+ if (log_face) {
+ BM_log_face_added(pbvh->bm_log, f);
+ }
+ 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("EEK! ni: %d totnode: %d\n", 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->bm_log, f);
+ }
+}
+
+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 = 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(NULL, BM_LOOPS_OF_EDGE, e, buf->data, buf->count);
+ }
+}
+
+/****************************** EdgeQueue *****************************/
+
+struct EdgeQueue;
+
+typedef struct EdgePair {
+ BMVert *v1, *v2;
+ float limit_len_squared;
+} EdgePair;
+
+typedef struct EdgeQueue {
+ HeapSimple *heap;
+
+ void **elems;
+ int totelems;
+
+ 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 struct EdgeQueue *q, BMFace *f);
+ bool (*edge_queue_vert_in_range)(const struct EdgeQueue *q, BMVert *v);
+
+ const float *view_normal;
+#ifdef USE_EDGEQUEUE_FRONTFACE
+ unsigned int use_view_normal : 1;
+#endif
+} EdgeQueue;
+
+typedef struct EdgeQueueContext {
+ EdgeQueue *q;
+ BLI_mempool *pool;
+ BMesh *bm;
+ DyntopoMaskCB mask_cb;
+ void *mask_cb_data;
+ int cd_dyn_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;
+ BMVert **val34_verts;
+ int val34_verts_tot;
+ int val34_verts_size;
+ bool local_mode;
+ float surface_smooth_fac;
+} EdgeQueueContext;
+
+static void edge_queue_insert_val34_vert(EdgeQueueContext *eq_ctx, BMVert *v)
+{
+ MDynTopoVert *mv = BKE_PBVH_DYNVERT(eq_ctx->cd_dyn_vert, v);
+ // prevent double adding
+
+ if (mv->flag & DYNVERT_VALENCE_TEMP) {
+ return;
+ }
+
+ mv->flag |= DYNVERT_VALENCE_TEMP;
+
+ eq_ctx->val34_verts_tot++;
+
+ if (eq_ctx->val34_verts_tot > eq_ctx->val34_verts_size) {
+ int size2 = 4 + eq_ctx->val34_verts_tot + (eq_ctx->val34_verts_tot >> 1);
+
+ if (eq_ctx->val34_verts) {
+ eq_ctx->val34_verts = MEM_reallocN(eq_ctx->val34_verts, sizeof(void *) * size2);
+ }
+ else {
+ eq_ctx->val34_verts = MEM_mallocN(sizeof(void *) * size2, "val34_verts");
+ }
+
+ eq_ctx->val34_verts_size = size2;
+ }
+
+ eq_ctx->val34_verts[eq_ctx->val34_verts_tot - 1] = v;
+}
+
+BLI_INLINE float maskcb_get(EdgeQueueContext *eq_ctx, BMEdge *e)
+{
+ if (eq_ctx->mask_cb) {
+ SculptVertRef sv1 = {(intptr_t)e->v1};
+ SculptVertRef sv2 = {(intptr_t)e->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);
+
+ return (w1 + w2) * 0.5f;
+ }
+
+ return 1.0f;
+}
+
+BLI_INLINE float calc_weighted_edge_split(EdgeQueueContext *eq_ctx, BMVert *v1, BMVert *v2)
+{
+#ifdef FANCY_EDGE_WEIGHTS
+ float l = len_squared_v3v3(v1->co, v2->co);
+ // float val = (float)BM_vert_edge_count(v1) + (float)BM_vert_edge_count(v2);
+ MDynTopoVert *mv1 = BKE_PBVH_DYNVERT(eq_ctx->cd_dyn_vert, v1);
+ MDynTopoVert *mv2 = BKE_PBVH_DYNVERT(eq_ctx->cd_dyn_vert, v2);
+ float val = (float)(mv1->valence + mv2->valence) * 0.5f;
+
+ val -= 6.0f;
+ val = MAX2(val, 1.0f);
+
+ // val = powf(val, 0.5);
+ l *= val;
+
+ return l;
+#elif 0 // penalize 4-valence verts
+ float l = len_squared_v3v3(v1->co, v2->co);
+ if (BM_vert_edge_count(v1) == 4 || BM_vert_edge_count(v2) == 4) {
+ l *= 0.25f;
+ }
+
+ return l;
+#else
+ return len_squared_v3v3(v1->co, v2->co);
+#endif
+}
+
+BLI_INLINE float calc_weighted_edge_collapse(EdgeQueueContext *eq_ctx, BMVert *v1, BMVert *v2)
+{
+#ifdef FANCY_EDGE_WEIGHTS
+ float l = len_squared_v3v3(v1->co, v2->co);
+ // float val = (float)BM_vert_edge_count(v1) + (float)BM_vert_edge_count(v2);
+ MDynTopoVert *mv1 = BKE_PBVH_DYNVERT(eq_ctx->cd_dyn_vert, v1);
+ MDynTopoVert *mv2 = BKE_PBVH_DYNVERT(eq_ctx->cd_dyn_vert, v2);
+ float val = (float)(mv1->valence + mv2->valence) * 0.5f;
+
+ val -= 6.0f;
+ val = MAX2(val, 1.0f);
+
+ // val = powf(val, 0.5);
+ l *= val;
+
+ return l;
+#else
+ return len_squared_v3v3(v1->co, v2->co);
+#endif
+}
+
+/* only tag'd 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_vert_in_sphere(const EdgeQueue *q, BMVert *v)
+{
+ /* Check if triangle intersects the sphere */
+ return len_squared_v3v3(q->center, v->co) <= q->radius_squared;
+}
+
+/*
+ Profiling revealed the accurate distance to tri in blenlib was too slow,
+ so we use a simpler version here
+ */
+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 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 int sizes[] = {-1,
+ (int)sizeof(BMVert),
+ (int)sizeof(BMEdge),
+ 0,
+ (int)sizeof(BMLoop),
+ -1,
+ -1,
+ -1,
+ (int)sizeof(BMFace)};
+
+static 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;
+}
+
+static bool edge_queue_tri_in_sphere(const EdgeQueue *q, BMFace *f)
+{
+ BMLoop *l = f->l_first;
+
+#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 */
+ float dis = dist_to_tri_sphere_simple((float *)q->center,
+ (float *)l->v->co,
+ (float *)l->next->v->co,
+ (float *)l->prev->v->co,
+ (float *)f->no);
+
+ return dis <= 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;
+}
+
+typedef struct EdgeQueueThreadData {
+ PBVH *pbvh;
+ PBVHNode *node;
+ BMEdge **edges;
+ BMVert **val34_verts;
+ int val34_verts_tot;
+ EdgeQueueContext *eq_ctx;
+ int totedge;
+ int size;
+ bool is_collapse;
+} 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 = MEM_mallocN(sizeof(void *) * tdata->size, "edge_thread_data_insert");
+ }
+ else {
+ tdata->edges = 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 EdgeQueue *q, BMVert *v)
+{
+ float c[3];
+
+ project_plane_normalized_v3_v3v3(c, v->co, q->view_normal);
+
+ return len_squared_v3v3(q->center_proj, c) <= q->radius_squared;
+}
+
+static void edge_queue_insert(EdgeQueueContext *eq_ctx, BMEdge *e, float priority, float limit)
+{
+ void **elems = eq_ctx->q->elems;
+ BLI_array_declare(elems);
+ BLI_array_len_set(elems, eq_ctx->q->totelems);
+
+ if (eq_ctx->cd_vert_mask_offset == -1 ||
+ !((e->v1->head.hflag | e->v2->head.hflag) & BM_ELEM_HIDDEN)) {
+ float dis = len_v3v3(e->v1->co, e->v2->co);
+ eq_ctx->avg_elen += dis;
+ eq_ctx->max_elen = MAX2(eq_ctx->max_elen, dis);
+ eq_ctx->min_elen = MIN2(eq_ctx->min_elen, dis);
+ eq_ctx->totedge += 1.0f;
+
+ EdgePair *pair = BLI_mempool_alloc(eq_ctx->pool);
+
+ pair->v1 = e->v1;
+ pair->v2 = e->v2;
+ pair->limit_len_squared = limit * limit;
+
+#ifdef DYNTOPO_USE_HEAP
+ BLI_heapsimple_insert(eq_ctx->q->heap, priority, pair);
+#endif
+
+ BLI_array_append(elems, pair);
+ eq_ctx->q->elems = elems;
+ eq_ctx->q->totelems = BLI_array_len(elems);
+
+#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 w = maskcb_get(eq_ctx, e);
+ const float len_sq = BM_edge_calc_length_squared(e) * w * w;
+
+ if (len_sq > eq_ctx->q->limit_len_squared) {
+ edge_queue_insert(eq_ctx, e, -len_sq, eq_ctx->q->limit_len);
+ }
+ }
+}
+
+#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,
+ int depth)
+{
+ BLI_assert(len_sq > square_f(limit_len));
+
+# ifdef USE_EDGEQUEUE_FRONTFACE
+ if (depth > DEPTH_START_LIMIT && 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, eq_ctx->q->limit_len);
+ }
+
+ 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 = BM_edge_calc_length_squared(l_adjacent[i]->e);
+ float w = maskcb_get(eq_ctx, l_adjacent[i]->e);
+
+ len_sq_other *= w * w;
+
+ 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,
+ depth + 1);
+ }
+ }
+ } while ((l_iter = l_iter->radial_next) != l_end);
+ }
+}
+#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 = calc_weighted_edge_collapse(eq_ctx, e->v1, e->v2);
+ if (len_sq < eq_ctx->q->limit_len_squared) {
+ edge_queue_insert(eq_ctx, e, len_sq, eq_ctx->q->limit_len);
+ }
+ }
+}
+
+static void long_edge_queue_face_add(EdgeQueueContext *eq_ctx, BMFace *f, bool ignore_frontface)
+{
+#ifdef USE_EDGEQUEUE_FRONTFACE
+ if (!ignore_frontface && 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
+ float len_sq = BM_edge_calc_length_squared(l_iter->e);
+ float w = maskcb_get(eq_ctx, l_iter->e);
+
+ len_sq *= w * w;
+
+ 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,
+ DEPTH_START_LIMIT +
+ 1); // ignore_frontface ? 0 : DEPTH_START_LIMIT+1);
+ }
+#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);
+ }
+}
+
+static void short_edge_queue_edge_add_recursive_2(EdgeQueueThreadData *tdata,
+ BMLoop *l_edge,
+ BMLoop *l_end,
+ const float len_sq,
+ float limit_len,
+ int depth)
+{
+ 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->q->use_view_normal) {
+ if (dot_v3v3(l_edge->f->no, tdata->eq_ctx->q->view_normal) < 0.0f) {
+ return;
+ }
+ }
+#endif
+
+ 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_weighted_edge_collapse(
+ tdata->eq_ctx, l_adjacent[i]->e->v1, l_adjacent[i]->e->v2);
+
+ if (len_sq_other > max_ff(len_sq_cmp, limit_len_sq)) {
+ // edge_queue_insert(eq_ctx, l_adjacent[i]->e, -len_sq_other);
+ short_edge_queue_edge_add_recursive_2(tdata,
+ 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->q->use_view_normal) {
+ if (dot_v3v3(l_edge->f->no, tdata->eq_ctx->q->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++) {
+ BMEdge *e = l_adjacent[i]->e;
+
+ float len_sq_other = calc_weighted_edge_split(
+ tdata->eq_ctx, l_adjacent[i]->e->v1, l_adjacent[i]->e->v2);
+
+ float w = maskcb_get(tdata->eq_ctx, e);
+
+ len_sq_other *= w * w;
+
+ 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);
+ }
+}
+
+static int _long_edge_queue_task_cb_seed = 0;
+
+static void long_edge_queue_task_cb(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;
+ RNG *rng = BLI_rng_new(_long_edge_queue_task_cb_seed++); // I don't care if seed becomes mangled
+ BMVert **val34 = NULL;
+ BLI_array_declare(val34);
+
+ BMFace *f;
+ const int cd_dyn_vert = tdata->pbvh->cd_dyn_vert;
+
+ TGSET_ITER (f, node->bm_faces) {
+ BMLoop *l = f->l_first;
+
+ do {
+ l->e->head.hflag &= ~BM_ELEM_TAG;
+ l = l->next;
+ } while (l != f->l_first);
+ }
+ TGSET_ITER_END
+
+ TGSET_ITER (f, node->bm_faces) {
+#ifdef USE_EDGEQUEUE_FRONTFACE
+ if (eq_ctx->q->use_view_normal) {
+ if (dot_v3v3(f->no, eq_ctx->q->view_normal) < 0.0f) {
+ continue;
+ }
+ }
+#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 {
+ MDynTopoVert *mv = BKE_PBVH_DYNVERT(eq_ctx->cd_dyn_vert, l_iter->v);
+
+ /*
+ If valence is not up to date, just add it to the list;
+ long_edge_queue_create will check and de-duplicate this for us.
+
+ Can't update valence in a thread after all.
+ */
+ if (mv->valence < 5 || (mv->flag & DYNVERT_NEED_VALENCE)) {
+ BLI_array_append(val34, l_iter->v);
+ }
+
+ // try to improve convergence by applying a small amount of smoothing to topology,
+ // but tangentially to surface.
+ if (BLI_rng_get_float(rng) > 0.5) {
+ surface_smooth_v_safe(tdata->pbvh, l_iter->v, eq_ctx->surface_smooth_fac);
+ }
+
+#ifdef USE_EDGEQUEUE_EVEN_SUBDIV
+ float w = maskcb_get(eq_ctx, l_iter->e);
+ float len_sq = BM_edge_calc_length_squared(l_iter->e);
+
+ len_sq *= w * w;
+
+ if (len_sq > eq_ctx->q->limit_len_squared) {
+ long_edge_queue_edge_add_recursive_2(
+ tdata, l_iter->radial_next, l_iter, len_sq, eq_ctx->q->limit_len, 0, true);
+ }
+#else
+ const float len_sq = BM_edge_calc_length_squared(l_iter->e);
+ if (len_sq > eq_ctx->q->limit_len_squared) {
+ edge_thread_data_insert(tdata, l_iter->e);
+ }
+#endif
+ } while ((l_iter = l_iter->next) != l_first);
+ }
+ }
+ TGSET_ITER_END
+
+ BLI_rng_free(rng);
+
+ tdata->val34_verts = val34;
+ tdata->val34_verts_tot = BLI_array_len(val34);
+}
+
+static void short_edge_queue_task_cb(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) {
+ BMLoop *l = f->l_first;
+
+ do {
+ if (!l->e) {
+ printf("bmesh error! %s\n", __func__);
+ continue;
+ }
+
+ l->e->head.hflag &= ~BM_ELEM_TAG;
+ l = l->next;
+ } while (l != f->l_first);
+ }
+ TGSET_ITER_END
+
+ TGSET_ITER (f, node->bm_faces) {
+#ifdef USE_EDGEQUEUE_FRONTFACE
+ if (eq_ctx->q->use_view_normal) {
+ if (dot_v3v3(f->no, eq_ctx->q->view_normal) < 0.0f) {
+ continue;
+ }
+ }
+#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 {
+ float w = maskcb_get(eq_ctx, l_iter->e);
+
+ if (w == 0.0f) {
+ continue;
+ }
+
+ float len_sq = calc_weighted_edge_collapse(eq_ctx, l_iter->e->v1, l_iter->e->v2);
+ len_sq /= w * w;
+
+ if (len_sq < eq_ctx->q->limit_len_squared) {
+ short_edge_queue_edge_add_recursive_2(
+ tdata, l_iter->radial_next, l_iter, len_sq, eq_ctx->q->limit_len, 0);
+ }
+ } while ((l_iter = l_iter->next) != l_first);
+ }
+ }
+ TGSET_ITER_END
+}
+
+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->q->use_view_normal) {
+ if (dot_v3v3(f->no, eq_ctx->q->view_normal) < 0.0f) {
+ continue;
+ }
+ }
+#endif
+
+ if (eq_ctx->q->edge_queue_tri_in_range(eq_ctx->q, f)) {
+ BMLoop *l = f->l_first;
+
+ do {
+ edge_thread_data_insert(tdata, l->e);
+
+ } while ((l = l->next) != f->l_first);
+ }
+ }
+ TGSET_ITER_END
+}
+
+static bool check_face_is_tri(PBVH *pbvh, BMFace *f)
+{
+ bool origlen = f->len;
+
+ 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;
+ }
+
+ BMFace **fs = NULL;
+ BMEdge **es = NULL;
+ LinkNode *dbl = NULL;
+ BLI_array_staticdeclare(fs, 32);
+ BLI_array_staticdeclare(es, 32);
+
+ BMLoop *l = f->l_first;
+ do {
+ validate_vert(pbvh, pbvh->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, true, true, true);
+
+ int len = (f->len - 2) * 3;
+
+ BLI_array_grow_items(fs, len);
+ BLI_array_grow_items(es, len);
+
+ int totface = 0;
+ int totedge = 0;
+ MemArena *arena = NULL;
+ struct Heap *heap = NULL;
+
+ if (f->len > 4) {
+ arena = BLI_memarena_new(512, "ngon arena");
+ heap = BLI_heap_new();
+ }
+
+ BM_face_triangulate(pbvh->bm,
+ f,
+ fs,
+ &totface,
+ es,
+ &totedge,
+ &dbl,
+ MOD_TRIANGULATE_QUAD_FIXED,
+ MOD_TRIANGULATE_NGON_BEAUTY,
+ false,
+ arena,
+ heap);
+
+ while (totface && dbl) {
+ BMFace *f2 = dbl->link;
+ LinkNode *next = dbl->next;
+
+ for (int i = 0; i < totface; i++) {
+ if (fs[i] == f2) {
+ // fs[i] = NULL;
+ }
+ }
+
+ if (dbl->link != f) {
+ // f = NULL;
+ // BM_face_kill(pbvh->bm, 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("eek!\n");
+ continue;
+ }
+
+ // detect new edges
+ BMLoop *l = f2->l_first;
+ do {
+ if (l->e->head.index == -1) {
+ BM_log_edge_added(pbvh->bm_log, l->e);
+ l->e->head.index = 0;
+ }
+ } while ((l = l->next) != f2->l_first);
+
+ validate_face(pbvh, pbvh->bm, f2, false, true);
+ BKE_pbvh_bmesh_add_face(pbvh, f2, true, true);
+ }
+
+ if (f) {
+ BKE_pbvh_bmesh_add_face(pbvh, f, true, true);
+ }
+
+ BLI_array_free(fs);
+ BLI_array_free(es);
+
+ if (arena) {
+ BLI_memarena_free(arena);
+ }
+
+ if (heap) {
+ BLI_heap_free(heap, NULL);
+ }
+
+ return false;
+}
+
+static bool destroy_nonmanifold_fins(PBVH *pbvh, BMEdge *e_root)
+{
+ static int max_faces = 64;
+ BMFace **stack = NULL;
+ BLI_array_staticdeclare(stack, 32);
+
+ BMLoop *l = e_root->l;
+ BMLoop **ls = NULL;
+ BMFace **fs = NULL;
+ BLI_array_staticdeclare(ls, 5);
+ int minfs = INT_MAX;
+
+ if (!l) {
+ return false;
+ }
+
+ do {
+ BLI_array_append(ls, l);
+ } while ((l = l->radial_next) != e_root->l);
+
+ for (int i = 0; i < BLI_array_len(ls); i++) {
+ SmallHash visit;
+ BLI_smallhash_init(&visit);
+
+ BMLoop *l = ls[i];
+ BMFace *f = l->f;
+ BMFace **fs2 = NULL;
+ BLI_array_staticdeclare(fs2, 32);
+
+ BLI_array_clear(stack);
+ BLI_array_append(stack, f);
+ BLI_array_append(fs2, f);
+
+ BLI_smallhash_insert(&visit, (uintptr_t)f, NULL);
+
+ bool bad = false;
+
+ while (BLI_array_len(stack) > 0) {
+ f = BLI_array_pop(stack);
+ BMLoop *l = f->l_first;
+
+ do {
+ if (l->radial_next == l || l->radial_next->radial_next != l) {
+ continue;
+ }
+
+ void **val = NULL;
+ BMFace *f2 = l->radial_next->f;
+
+ if (!BLI_smallhash_ensure_p(&visit, (uintptr_t)f2, &val)) {
+ if (BLI_array_len(fs2) > max_faces) {
+ bad = true;
+ break;
+ }
+
+ *val = NULL;
+ BLI_array_append(stack, f2);
+ BLI_array_append(fs2, f2);
+ }
+ } while ((l = l->next) != f->l_first);
+
+ if (bad) {
+ break;
+ }
+ }
+
+ if (!bad && BLI_array_len(fs2) < minfs) {
+ minfs = BLI_array_len(fs2);
+ fs = BLI_array_alloca(fs, BLI_array_len(fs2));
+ memcpy(fs, fs2, sizeof(*fs) * BLI_array_len(fs2));
+ }
+
+ BLI_array_free(fs2);
+ BLI_smallhash_release(&visit);
+ }
+
+ int nupdateflag = PBVH_UpdateOtherVerts | PBVH_UpdateDrawBuffers | PBVH_UpdateBB |
+ PBVH_UpdateTriAreas;
+ nupdateflag = nupdateflag | PBVH_UpdateNormals | PBVH_UpdateTris | PBVH_RebuildDrawBuffers;
+
+ if (!fs) {
+ return false;
+ }
+
+ if (fs) {
+ printf("manifold fin size: %d\n", minfs);
+ const int tag = BM_ELEM_TAG_ALT;
+
+ for (int i = 0; i < minfs; i++) {
+ BMFace *f = fs[i];
+
+ BMLoop *l = f->l_first;
+ do {
+ l->v->head.hflag &= ~tag;
+ l->e->head.hflag &= ~tag;
+ } while ((l = l->next) != f->l_first);
+ }
+
+ BMVert **vs = NULL;
+ BLI_array_staticdeclare(vs, 32);
+
+ BMEdge **es = NULL;
+ BLI_array_staticdeclare(es, 32);
+
+ for (int i = 0; i < minfs; i++) {
+ BMFace *f = fs[i];
+
+ BMLoop *l = f->l_first;
+ do {
+ if (!(l->v->head.hflag & tag)) {
+ l->v->head.hflag |= tag;
+ BLI_array_append(vs, l->v);
+ }
+
+ if (!(l->e->head.hflag & tag)) {
+ l->e->head.hflag |= tag;
+ BLI_array_append(es, l->e);
+ }
+ } while ((l = l->next) != f->l_first);
+ }
+
+ for (int i = 0; i < minfs; i++) {
+ BMFace *f = fs[i];
+
+ int ni = BM_ELEM_CD_GET_INT(f, pbvh->cd_face_node_offset);
+ if (ni >= 0 && ni < pbvh->totnode) {
+ pbvh->nodes[ni].flag |= nupdateflag;
+ }
+
+ pbvh_bmesh_face_remove(pbvh, f, true, false, false);
+ BM_face_kill(pbvh->bm, f);
+ }
+
+ const int mupdateflag = DYNVERT_NEED_BOUNDARY | DYNVERT_NEED_DISK_SORT | DYNVERT_NEED_VALENCE;
+
+ for (int i = 0; i < BLI_array_len(es); i++) {
+ BMEdge *e = es[i];
+
+ if (!e->l) {
+ BM_log_edge_removed(pbvh->bm_log, e);
+ BM_edge_kill(pbvh->bm, e);
+ }
+ }
+
+ for (int i = 0; i < BLI_array_len(vs); i++) {
+ BMVert *v = vs[i];
+
+ if (!v->e) {
+ pbvh_bmesh_vert_remove(pbvh, v);
+
+ BM_log_vert_removed(pbvh->bm_log, v, pbvh->cd_vert_mask_offset);
+ BM_vert_kill(pbvh->bm, v);
+ }
+ else {
+ MDynTopoVert *mv = BKE_PBVH_DYNVERT(pbvh->cd_dyn_vert, v);
+ mv->flag |= mupdateflag;
+ }
+ }
+
+ BLI_array_free(vs);
+ BLI_array_free(es);
+ }
+
+ return true;
+}
+
+static bool check_for_fins(PBVH *pbvh, BMVert *v)
+{
+ BMEdge *e = v->e;
+ if (!e) {
+ return false;
+ }
+
+ do {
+ 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)) {
+ return true;
+ }
+ }
+ } while ((l = l->next) != e->l->f->l_first);
+ }
+ } while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e);
+
+ return false;
+}
+
+static bool check_vert_fan_are_tris(PBVH *pbvh, BMVert *v)
+{
+ MDynTopoVert *mv = BKE_PBVH_DYNVERT(pbvh->cd_dyn_vert, v);
+
+ if (!(mv->flag & DYNVERT_NEED_TRIANGULATE)) {
+ return true;
+ }
+
+ bm_log_message(" == triangulate == ");
+
+ BMFace **fs = NULL;
+ BLI_array_staticdeclare(fs, 32);
+
+ validate_vert(pbvh, pbvh->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 {
+ MDynTopoVert *mv_l = BKE_PBVH_DYNVERT(pbvh->cd_dyn_vert, l->v);
+
+ mv_l->flag |= DYNVERT_NEED_BOUNDARY | DYNVERT_NEED_VALENCE | DYNVERT_NEED_DISK_SORT;
+ } while ((l = l->next) != f->l_first);
+ BLI_array_append(fs, f);
+ }
+
+ mv->flag &= ~DYNVERT_NEED_TRIANGULATE;
+
+ for (int i = 0; i < BLI_array_len(fs); i++) {
+ check_face_is_tri(pbvh, fs[i]);
+ }
+
+ BLI_array_free(fs);
+
+ 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->q->edge_queue_tri_in_range = edge_queue_tri_in_circle;
+ eq_ctx->q->edge_queue_vert_in_range = edge_queue_vert_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;
+ eq_ctx->q->edge_queue_vert_in_range = edge_queue_vert_in_sphere;
+ }
+
+ eq_ctx->q->center = center;
+ eq_ctx->q->view_normal = view_normal;
+ eq_ctx->q->radius_squared = radius * radius;
+
+#ifdef USE_EDGEQUEUE_FRONTFACE
+ eq_ctx->q->use_view_normal = use_frontface;
+#else
+ UNUSED_VARS(use_frontface);
+#endif
+}
+
+/* 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,
+ const bool local_mode)
+{
+ if (local_mode) {
+ edge_queue_create_local(
+ eq_ctx, pbvh, center, view_normal, radius, use_frontface, use_projected, false);
+ return;
+ }
+
+ eq_ctx->q->heap = BLI_heapsimple_new();
+ eq_ctx->q->elems = NULL;
+ eq_ctx->q->totelems = 0;
+ eq_ctx->q->radius_squared = radius * radius;
+ eq_ctx->q->limit_len_squared = pbvh->bm_max_edge_len * pbvh->bm_max_edge_len;
+ eq_ctx->local_mode = local_mode;
+
+#ifdef USE_EDGEQUEUE_EVEN_SUBDIV
+ eq_ctx->q->limit_len = pbvh->bm_max_edge_len;
+#endif
+
+ edge_queue_init(eq_ctx, use_projected, use_frontface, center, view_normal, radius);
+
+#ifdef USE_EDGEQUEUE_TAG_VERIFY
+ pbvh_bmesh_edge_tag_verify(pbvh);
+#endif
+
+ EdgeQueueThreadData *tdata = NULL;
+ BLI_array_declare(tdata);
+
+ 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)) {
+ EdgeQueueThreadData td;
+
+ memset(&td, 0, sizeof(td));
+
+ td.pbvh = pbvh;
+ td.node = node;
+ td.eq_ctx = eq_ctx;
+
+ BLI_array_append(tdata, 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 = BLI_array_len(tdata);
+
+ TaskParallelSettings settings;
+
+ BLI_parallel_range_settings_defaults(&settings);
+ BLI_task_parallel_range(0, count, tdata, long_edge_queue_task_cb, &settings);
+ const int cd_dyn_vert = pbvh->cd_dyn_vert;
+
+ for (int i = 0; i < count; i++) {
+ EdgeQueueThreadData *td = tdata + i;
+
+ for (int j = 0; j < td->val34_verts_tot; j++) {
+ BMVert *v = td->val34_verts[j];
+ MDynTopoVert *mv = BKE_PBVH_DYNVERT(cd_dyn_vert, v);
+
+ if (mv->flag & DYNVERT_NEED_VALENCE) {
+ BKE_pbvh_bmesh_update_valence(pbvh->cd_dyn_vert, (SculptVertRef){.i = (intptr_t)v});
+ }
+ }
+
+ 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);
+ }
+
+ MDynTopoVert *mv1 = BKE_PBVH_DYNVERT(cd_dyn_vert, e->v1);
+ MDynTopoVert *mv2 = BKE_PBVH_DYNVERT(cd_dyn_vert, e->v2);
+
+ if (mv1->flag & DYNVERT_NEED_VALENCE) {
+ BKE_pbvh_bmesh_update_valence(pbvh->cd_dyn_vert, (SculptVertRef){.i = (intptr_t)e->v1});
+ }
+ if (mv2->flag & DYNVERT_NEED_VALENCE) {
+ BKE_pbvh_bmesh_update_valence(pbvh->cd_dyn_vert, (SculptVertRef){.i = (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;
+
+ edge_queue_insert(eq_ctx, e, w, eq_ctx->q->limit_len);
+ }
+
+ MEM_SAFE_FREE(td->edges);
+ MEM_SAFE_FREE(td->val34_verts);
+ }
+ BLI_array_free(tdata);
+}
+
+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,
+ bool is_collapse)
+{
+ eq_ctx->q->heap = BLI_heapsimple_new();
+ eq_ctx->q->elems = NULL;
+ eq_ctx->q->totelems = 0;
+ 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
+
+ edge_queue_init(eq_ctx, use_projected, use_frontface, center, view_normal, radius);
+
+ EdgeQueueThreadData *tdata = NULL;
+ BLI_array_declare(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 = is_collapse;
+ td.eq_ctx = eq_ctx;
+
+ BLI_array_append(tdata, td);
+ }
+ }
+
+ int count = BLI_array_len(tdata);
+
+ TaskParallelSettings settings;
+
+ BLI_parallel_range_settings_defaults(&settings);
+ BLI_task_parallel_range(0, count, tdata, short_edge_queue_task_cb_local, &settings);
+
+ const int cd_dyn_vert = pbvh->cd_dyn_vert;
+ BMEdge **edges = NULL;
+ float *lens = NULL;
+ BLI_array_declare(edges);
+ BLI_array_declare(lens);
+
+ 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)) {
+ BLI_array_append(edges, e);
+ e->head.hflag |= BM_ELEM_TAG;
+ }
+ }
+ }
+
+ for (int i = 0; i < count; i++) {
+ EdgeQueueThreadData *td = tdata + i;
+ MEM_SAFE_FREE(td->edges);
+ MEM_SAFE_FREE(td->val34_verts);
+ }
+
+ for (int i = 0; i < BLI_array_len(edges); 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 (!is_collapse) {
+ if (!(v->head.hflag & BM_ELEM_TAG)) {
+ v->head.hflag |= BM_ELEM_TAG;
+ MDynTopoVert *mv = BKE_PBVH_DYNVERT(pbvh->cd_dyn_vert, v);
+
+ if (mv->flag & DYNVERT_NEED_VALENCE) {
+ BKE_pbvh_bmesh_update_valence(pbvh->cd_dyn_vert, (SculptVertRef){.i = (intptr_t)v});
+ }
+ }
+ }
+ }
+ e->head.index = i;
+ BLI_array_append(lens, len);
+ }
+
+ // make sure tags around border edges are unmarked
+ for (int i = 0; i < BLI_array_len(edges); 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 < BLI_array_len(edges); i++) {
+ edges[i]->head.hflag |= BM_ELEM_TAG;
+ }
+
+ int totstep = is_collapse ? 3 : 3;
+
+ // blur lengths
+ for (int step = 0; step < totstep; step++) {
+ for (int i = 0; i < BLI_array_len(edges); 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->bm->elem_index_dirty |= BM_EDGE;
+ float sign = is_collapse ? 1.0f : -1.0f;
+
+ const float detail_range = pbvh->bm_min_edge_len == 0.0f ?
+ 0.0f :
+ pbvh->bm_max_edge_len / pbvh->bm_min_edge_len;
+
+ for (int i = 0; i < BLI_array_len(edges); i++) {
+ BMEdge *e = edges[i];
+ MDynTopoVert *mv1, *mv2;
+
+ e->head.hflag &= ~BM_ELEM_TAG;
+
+ mv1 = BKE_PBVH_DYNVERT(cd_dyn_vert, e->v1);
+ mv2 = BKE_PBVH_DYNVERT(cd_dyn_vert, e->v2);
+
+ pbvh_check_vert_boundary(pbvh, e->v1);
+ pbvh_check_vert_boundary(pbvh, e->v2);
+
+ if ((mv1->flag & DYNVERT_ALL_CORNER) || (mv2->flag & DYNVERT_ALL_CORNER)) {
+ continue;
+ }
+
+ if ((mv1->flag & DYNVERT_ALL_BOUNDARY) != (mv2->flag & DYNVERT_ALL_BOUNDARY)) {
+ continue;
+ }
+
+ // check seam/sharp flags here
+ // if (!(e->head.hflag & BM_ELEM_SMOOTH) || e->head.hflag & BM_ELEM_SEAM) {
+ // continue;
+ // }
+
+ float limit = lens[i];
+
+ // limit *= detail_range;
+ if (is_collapse) {
+ limit *= pbvh->bm_detail_range;
+ }
+ else {
+ limit *= 1.0 + pbvh->bm_detail_range;
+ }
+
+ eq_ctx->q->limit_len = limit;
+ eq_ctx->q->limit_len_squared = limit * limit;
+
+ if (sign * (len_v3v3(e->v2->co, e->v1->co) - limit) >= 0) {
+ continue;
+ }
+
+ float w;
+ if (is_collapse) {
+ w = calc_weighted_edge_collapse(eq_ctx, e->v1, e->v2);
+ }
+ else {
+ w = calc_weighted_edge_split(eq_ctx, e->v1, e->v2);
+ }
+
+ float w2 = maskcb_get(eq_ctx, e);
+
+ if (w2 > 0.0f) {
+ if (is_collapse) {
+ w /= w2 * w2;
+ }
+ else {
+ w *= w2 * w2;
+ }
+ }
+ else {
+ w = 100000.0f;
+ }
+
+ if (!is_collapse) {
+ w = -w;
+ }
+
+ edge_queue_insert(eq_ctx, e, w, limit);
+ }
+
+ BLI_array_free(edges);
+ BLI_array_free(lens);
+ BLI_array_free(tdata);
+}
+
+/* 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,
+ bool local_mode)
+{
+ if (local_mode) {
+ edge_queue_create_local(
+ eq_ctx, pbvh, center, view_normal, radius, use_frontface, use_projected, true);
+ return;
+ }
+
+ eq_ctx->local_mode = false;
+ eq_ctx->q->heap = BLI_heapsimple_new();
+ eq_ctx->q->elems = NULL;
+ eq_ctx->q->totelems = 0;
+ eq_ctx->q->center = center;
+ eq_ctx->q->radius_squared = radius * radius;
+ eq_ctx->q->limit_len = pbvh->bm_min_edge_len;
+ eq_ctx->q->limit_len_squared = pbvh->bm_min_edge_len * pbvh->bm_min_edge_len;
+
+ 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
+
+ edge_queue_init(eq_ctx, use_projected, use_frontface, center, view_normal, radius);
+
+ EdgeQueueThreadData *tdata = NULL;
+ BLI_array_declare(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.eq_ctx = eq_ctx;
+
+ BLI_array_append(tdata, td);
+ }
+
+#if 0
+ /* Check leaf nodes marked for topology update */
+ BMFace *f;
+
+ /* Check each face */
+ TGSET_ITER (f, node->bm_faces) {
+ short_edge_queue_face_add(eq_ctx, f);
+ }
+ TGSET_ITER_END
+ }
+#endif
+ }
+
+ int count = BLI_array_len(tdata);
+
+ TaskParallelSettings settings;
+
+ BLI_parallel_range_settings_defaults(&settings);
+ BLI_task_parallel_range(0, count, tdata, short_edge_queue_task_cb, &settings);
+
+ const int cd_dyn_vert = pbvh->cd_dyn_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];
+ MDynTopoVert *mv1, *mv2;
+
+ mv1 = BKE_PBVH_DYNVERT(cd_dyn_vert, e->v1);
+ mv2 = BKE_PBVH_DYNVERT(cd_dyn_vert, e->v2);
+
+ pbvh_check_vert_boundary(pbvh, e->v1);
+ pbvh_check_vert_boundary(pbvh, e->v2);
+
+ if ((mv1->flag & DYNVERT_ALL_CORNER) || (mv2->flag & DYNVERT_ALL_CORNER)) {
+ continue;
+ }
+
+ if ((mv1->flag & DYNVERT_ALL_BOUNDARY) != (mv2->flag & DYNVERT_ALL_BOUNDARY)) {
+ continue;
+ }
+
+ float w = calc_weighted_edge_collapse(eq_ctx, e->v1, e->v2);
+ float w2 = maskcb_get(eq_ctx, e);
+
+ if (w2 > 0.0f) {
+ w /= w2 * w2;
+ }
+ else {
+ w = 100000.0f;
+ }
+
+ e->head.hflag &= ~BM_ELEM_TAG;
+ edge_queue_insert(eq_ctx, e, w, eq_ctx->q->limit_len);
+ }
+
+ if (td->edges) {
+ MEM_freeN(td->edges);
+ }
+ }
+
+ BLI_array_free(tdata);
+}
+
+/*************************** Topology update **************************/
+
+static void pbvh_bmesh_split_edge(EdgeQueueContext *eq_ctx,
+ PBVH *pbvh,
+ BMEdge *e,
+ BLI_Buffer *edge_loops)
+{
+ BMesh *bm = pbvh->bm;
+
+ bm_log_message(" == split edge == ");
+
+ // pbvh_bmesh_check_nodes(pbvh);
+
+ // pbvh_bmesh_check_nodes(pbvh);
+
+ float co_mid[3], no_mid[3];
+ MDynTopoVert *mv1 = BKE_PBVH_DYNVERT(pbvh->cd_dyn_vert, e->v1);
+ MDynTopoVert *mv2 = BKE_PBVH_DYNVERT(pbvh->cd_dyn_vert, e->v2);
+
+ pbvh_check_vert_boundary(pbvh, e->v1);
+ pbvh_check_vert_boundary(pbvh, e->v2);
+
+ mv1->flag |= DYNVERT_NEED_VALENCE | DYNVERT_NEED_BOUNDARY | DYNVERT_NEED_DISK_SORT;
+ mv2->flag |= DYNVERT_NEED_VALENCE | DYNVERT_NEED_BOUNDARY | DYNVERT_NEED_DISK_SORT;
+
+ bool boundary = (mv1->flag & DYNVERT_ALL_BOUNDARY) && (mv2->flag & DYNVERT_ALL_BOUNDARY);
+
+ /* 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, NULL, eq_ctx->cd_vert_mask_offset);
+ // transfer edge flags
+
+ BMEdge *e1 = bmesh_edge_create_log(pbvh, e->v1, v_new, e);
+ BMEdge *e2 = bmesh_edge_create_log(pbvh, v_new, e->v2, e);
+
+ BM_log_edge_added(pbvh->bm_log, e1);
+ BM_log_edge_added(pbvh->bm_log, e2);
+
+ int eflag = e->head.hflag & ~BM_ELEM_HIDDEN;
+ int vflag = (e->v1->head.hflag | e->v2->head.hflag) & ~BM_ELEM_HIDDEN;
+
+ e1->head.hflag = e2->head.hflag = eflag;
+ v_new->head.hflag = vflag;
+
+ MDynTopoVert *mv_new = BKE_PBVH_DYNVERT(pbvh->cd_dyn_vert, v_new);
+
+ /*TODO: is it worth interpolating edge customdata?*/
+
+ int ni_new = BM_ELEM_CD_GET_INT(v_new, pbvh->cd_vert_node_offset);
+
+ void *vsrcs[2] = {e->v1->head.data, e->v2->head.data};
+ float vws[2] = {0.5f, 0.5f};
+ CustomData_bmesh_interp(
+ &pbvh->bm->vdata, (const void **)vsrcs, (float *)vws, NULL, 2, v_new->head.data);
+
+ // bke_pbvh_update_vert_boundary(pbvh->cd_dyn_vert, pbvh->cd_faceset_offset, v_new);
+ mv_new->flag |= DYNVERT_NEED_DISK_SORT | DYNVERT_NEED_VALENCE | DYNVERT_NEED_BOUNDARY;
+ mv_new->flag &= ~DYNVERT_VALENCE_TEMP;
+
+ int ni_new2 = BM_ELEM_CD_GET_INT(v_new, pbvh->cd_vert_node_offset);
+ if (ni_new2 != ni_new) {
+ // printf("error!\n");
+ BM_ELEM_CD_SET_INT(v_new, pbvh->cd_vert_node_offset, ni_new);
+ }
+
+ /* For each face, add two new triangles and delete the original */
+ for (int i = 0; i < (int)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;
+
+ MDynTopoVert *mv1b = BKE_PBVH_DYNVERT(pbvh->cd_dyn_vert, v1);
+ MDynTopoVert *mv2b = BKE_PBVH_DYNVERT(pbvh->cd_dyn_vert, v2);
+ MDynTopoVert *mv_opp = BKE_PBVH_DYNVERT(pbvh->cd_dyn_vert, v_opp);
+
+ mv1b->flag |= DYNVERT_NEED_VALENCE | DYNVERT_NEED_BOUNDARY | DYNVERT_NEED_DISK_SORT;
+ mv2b->flag |= DYNVERT_NEED_VALENCE | DYNVERT_NEED_BOUNDARY | DYNVERT_NEED_DISK_SORT;
+ mv_opp->flag |= DYNVERT_NEED_VALENCE | DYNVERT_NEED_BOUNDARY | DYNVERT_NEED_DISK_SORT;
+
+ 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)`
+ *
+ * <pre>
+ * + v3(v_opp)
+ * /|\
+ * / | \
+ * / | \
+ * e4/ | \ e3
+ * / |e5 \
+ * / | \
+ * / e1 | e2 \
+ * +-------+-------+
+ * v1 v4(v_new) v2
+ * (first) (second)
+ * </pre>
+ *
+ * - 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, v_tri, e_tri);
+ f_new = pbvh_bmesh_face_create(pbvh, ni, v_tri, e_tri, f_adj, false, true);
+ long_edge_queue_face_add(eq_ctx, f_new, true);
+
+ pbvh_bmesh_copy_facedata(pbvh, bm, f_new, f_adj);
+
+ // customdata interpolation
+ BMLoop *lfirst = f_adj->l_first;
+ while (lfirst->v != v1) {
+ lfirst = lfirst->next;
+
+ // paranoia check
+ if (lfirst == f_adj->l_first) {
+ break;
+ }
+ }
+
+ BMLoop *l1 = lfirst;
+ BMLoop *l2 = lfirst->next;
+ BMLoop *l3 = lfirst->next->next;
+
+ void *lsrcs[2] = {l1->head.data, l2->head.data};
+ float lws[2] = {0.5f, 0.5f};
+
+ CustomData_bmesh_interp(
+ &pbvh->bm->ldata, (const void **)lsrcs, lws, lws, 2, f_new->l_first->next->head.data);
+
+ lsrcs[0] = l1->head.data;
+ lws[0] = 1.0f;
+
+ CustomData_bmesh_interp(
+ &pbvh->bm->ldata, (const void **)lsrcs, lws, lws, 1, f_new->l_first->head.data);
+
+ lsrcs[0] = l3->head.data;
+ lws[0] = 1.0f;
+
+ CustomData_bmesh_interp(
+ &pbvh->bm->ldata, (const void **)lsrcs, lws, lws, 1, f_new->l_first->prev->head.data);
+
+ v_tri[0] = v_new;
+ v_tri[1] = v2;
+ /* v_tri[2] = v_opp; */ /* unchanged */
+ e_tri[0] = bmesh_edge_create_log(pbvh, v_tri[0], v_tri[1], NULL);
+ e_tri[2] = e_tri[1]; /* switched */
+ e_tri[1] = bmesh_edge_create_log(pbvh, v_tri[1], v_tri[2], NULL);
+
+ f_new = pbvh_bmesh_face_create(pbvh, ni, v_tri, e_tri, f_adj, false, true);
+
+ long_edge_queue_face_add(eq_ctx, f_new, true);
+
+ pbvh_bmesh_copy_facedata(pbvh, bm, f_new, f_adj);
+
+ // customdata interpolation
+ lsrcs[0] = lfirst->head.data;
+ lsrcs[1] = lfirst->next->head.data;
+ lws[0] = lws[1] = 0.5f;
+
+ CustomData_bmesh_interp(
+ &pbvh->bm->ldata, (const void **)lsrcs, lws, lws, 2, f_new->l_first->head.data);
+
+ lsrcs[0] = lfirst->next->head.data;
+ ;
+ lws[0] = 1.0f;
+
+ CustomData_bmesh_interp(
+ &pbvh->bm->ldata, (const void **)lsrcs, lws, lws, 1, f_new->l_first->next->head.data);
+
+ lsrcs[0] = lfirst->prev->head.data;
+ lws[0] = 1.0f;
+
+ CustomData_bmesh_interp(
+ &pbvh->bm->ldata, (const void **)lsrcs, lws, lws, 1, f_new->l_first->prev->head.data);
+
+ /* Delete original */
+ pbvh_bmesh_face_remove(pbvh, f_adj, true, true, true);
+ BM_face_kill(pbvh->bm, f_adj);
+ }
+
+ BM_log_edge_removed(pbvh->bm_log, e);
+ BM_edge_kill(pbvh->bm, e);
+
+ // pbvh_bmesh_check_nodes(pbvh);
+}
+
+static bool pbvh_bmesh_subdivide_long_edges(
+ EdgeQueueContext *eq_ctx, PBVH *pbvh, BLI_Buffer *edge_loops, int max_steps, bool has_cleanup)
+{
+ bool any_subdivided = false;
+ double time = PIL_check_seconds_timer();
+
+ if (pbvh->dyntopo_stop) {
+ return false;
+ }
+
+ RNG *rng = BLI_rng_new((int)(time * 1000.0f));
+ int step = 0;
+
+#ifdef USE_NEW_SPLIT
+ BMEdge **edges = NULL;
+ BLI_array_declare(edges);
+#endif
+
+ while (!BLI_heapsimple_is_empty(eq_ctx->q->heap)) {
+ if (step++ > max_steps) {
+ break;
+ }
+
+#ifdef DYNTOPO_TIME_LIMIT
+ if (PIL_check_seconds_timer() - time > DYNTOPO_TIME_LIMIT) {
+ break;
+ }
+#endif
+
+#ifndef DYNTOPO_USE_HEAP
+ if (eq_ctx->q->totelems == 0) {
+ break;
+ }
+
+ int ri = BLI_rng_get_int(rng) % eq_ctx->q->totelems;
+
+ BMVert **pair = eq_ctx->q->elems[ri];
+ eq_ctx->q->elems[ri] = eq_ctx->q->elems[eq_ctx->q->totelems - 1];
+ eq_ctx->q->totelems--;
+#else
+ EdgePair *pair = BLI_heapsimple_pop_min(eq_ctx->q->heap);
+#endif
+ BMVert *v1 = pair->v1, *v2 = pair->v2;
+ BMEdge *e;
+
+ BLI_mempool_free(eq_ctx->pool, pair);
+ pair = NULL;
+
+ if (bm_elem_is_free((BMElem *)v1, BM_VERT) || bm_elem_is_free((BMElem *)v2, BM_VERT)) {
+ continue;
+ }
+
+ /* 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 (subdiv 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(calc_weighted_edge_split(eq_ctx, 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)) {
+ continue;
+ }
+
+ any_subdivided = true;
+#ifdef USE_NEW_SPLIT
+ BLI_array_append(edges, e);
+#else
+
+ pbvh_bmesh_split_edge(eq_ctx, pbvh, e, edge_loops);
+#endif
+ }
+
+#if !defined(DYNTOPO_USE_HEAP) && defined(USE_EDGEQUEUE_TAG)
+ for (int i = 0; i < eq_ctx->q->totelems; i++) {
+ BMVert **pair = eq_ctx->q->elems[i];
+ BMVert *v1 = pair[0], *v2 = pair[1];
+
+ BMEdge *e = BM_edge_exists(v1, v2);
+
+ if (e) {
+ EDGE_QUEUE_DISABLE(e);
+ }
+ }
+#endif
+
+#ifdef USE_EDGEQUEUE_TAG_VERIFY
+ pbvh_bmesh_edge_tag_verify(pbvh);
+#endif
+
+#ifdef USE_NEW_SPLIT
+ pbvh_split_edges(eq_ctx, pbvh, pbvh->bm, edges, BLI_array_len(edges), has_cleanup);
+ BLI_array_free(edges);
+#endif
+
+ BLI_rng_free(rng);
+
+ return any_subdivided;
+}
+
+static 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)))));
+}
+
+static 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
+}
+
+static 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);
+ }
+ }
+}
+
+// copied from decimate modifier code
+static 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;
+}
+
+static void pbvh_bmesh_collapse_edge(PBVH *pbvh,
+ BMEdge *e,
+ BMVert *v1,
+ BMVert *v2,
+ GHash *deleted_verts,
+ BLI_Buffer *deleted_faces,
+ EdgeQueueContext *eq_ctx)
+{
+ BMVert *v_del, *v_conn;
+
+ if (pbvh->dyntopo_stop) {
+ return;
+ }
+
+ if (bm_edge_collapse_is_degenerate_topology(e)) {
+ return;
+ }
+
+ pbvh_check_vert_boundary(pbvh, v1);
+ pbvh_check_vert_boundary(pbvh, v2);
+
+ const int mupdateflag = DYNVERT_NEED_VALENCE | DYNVERT_NEED_BOUNDARY | DYNVERT_NEED_DISK_SORT;
+ // updateflag |= DYNVERT_NEED_TRIANGULATE; // to check for non-manifold flaps
+
+ validate_edge(pbvh, pbvh->bm, e, true, true);
+
+ check_vert_fan_are_tris(pbvh, e->v1);
+ check_vert_fan_are_tris(pbvh, e->v2);
+
+ MDynTopoVert *mv1 = BKE_PBVH_DYNVERT(pbvh->cd_dyn_vert, v1);
+ MDynTopoVert *mv2 = BKE_PBVH_DYNVERT(pbvh->cd_dyn_vert, v2);
+
+ /* one of the two vertices may be masked, select the correct one for deletion */
+ if (!(mv1->flag & DYNVERT_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(MDynTopoVert *, mv1, mv2);
+ }
+
+ if ((mv1->flag & DYNVERT_ALL_CORNER) ||
+ (mv1->flag & DYNVERT_ALL_BOUNDARY) != (mv2->flag & DYNVERT_ALL_BOUNDARY)) {
+ return;
+ }
+
+ /*have to check edge flags directly, vertex flag test above isn't specific enough and
+ can sometimes let bad edges through*/
+ if ((mv1->flag & DYNVERT_SHARP_BOUNDARY) && (e->head.hflag & BM_ELEM_SMOOTH)) {
+ return;
+ }
+ if ((mv1->flag & DYNVERT_SEAM_BOUNDARY) && !(e->head.hflag & BM_ELEM_SEAM)) {
+ return;
+ }
+
+ bool snap = !(mv2->flag & DYNVERT_ALL_CORNER);
+
+#if 0
+ // don't allow non-manifold case of
+ // there being 3-valence verts
+ // in neighborhood around edge
+ bool bad = false;
+ for (int i = 0; i < 2; i++) {
+ BMVert *v = i ? v_conn : v_del;
+
+ BMEdge *e2 = v->e;
+
+ do {
+ BMVert *v2 = v == e2->v1 ? e2->v2 : e2->v1;
+
+ int val = BM_vert_edge_count(v);
+ if (val < 4) {
+ bad = true;
+ break;
+ }
+ } while ((e2 = BM_DISK_EDGE_NEXT(e2, v)) != v->e);
+
+ if (bad) {
+ break;
+ }
+ }
+
+ if (bad) {
+ return; // bad edge
+ }
+#endif
+
+#if 0
+ // remove all faces from pbvh
+ for (int i = 0; i < 2; i++) {
+ BMVert *v = i ? v_conn : v_del;
+
+ BMEdge *e = v->e;
+ do {
+ BMLoop *l = e->l;
+
+ if (!l) {
+ continue;
+ }
+
+ 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, false, false);
+ }
+ } while ((l = l->radial_next) != e->l);
+ } while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e);
+ }
+
+ // repeatedly dissolve 3-valence verts
+ while (1) {
+ bool bad = false;
+
+ for (int i = 0; i < 2; i++) {
+ BMVert *v = i ? v_conn : v_del;
+
+ BMEdge *e2 = v->e;
+
+ if (!e2) {
+ continue;
+ }
+ BMEdge *enext;
+
+ do {
+ BMVert *v2 = BM_edge_other_vert(e2, v);
+ enext = BM_DISK_EDGE_NEXT(e2, v);
+
+ if (v2 == v_conn || v2 == v_del) {
+ continue;
+ }
+
+ if (BM_vert_edge_count(v2) < 4) {
+ BMEdge *e3 = v2->e;
+
+ do {
+ BMLoop *l = e3->l;
+ if (e3 == e) {
+ e = NULL;
+ }
+
+ if (!l) {
+ continue;
+ }
+
+ 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, false, false);
+ }
+ } while ((l = l->radial_next) != e3->l);
+
+ BM_log_edge_removed(pbvh->bm_log, e3);
+ } while ((e3 = BM_DISK_EDGE_NEXT(e3, v2)) != v2->e);
+
+ pbvh_bmesh_vert_remove(pbvh, v2);
+ BM_log_vert_removed(pbvh->bm_log, v2, pbvh->cd_vert_mask_offset);
+
+ BLI_ghash_insert(deleted_verts, v, NULL);
+
+ BM_vert_dissolve(pbvh->bm, v);
+ bad = true;
+ break;
+ }
+ } while ((e2 = enext) != v->e);
+ }
+
+ if (!bad) {
+ break;
+ }
+ }
+
+ if (!e || bm_elem_is_free((BMElem *)v_conn, BM_VERT) ||
+ bm_elem_is_free((BMElem *)v_del, BM_VERT)) {
+ return;
+ }
+#endif
+
+ BMLoop *l;
+
+ // 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->bm->vdata, v_blocks, v_ws, NULL, 2, v_conn->head.data);
+ BM_ELEM_CD_SET_INT(v_conn, pbvh->cd_vert_node_offset, ni_conn);
+
+ BMLoop **ls = NULL;
+ void **blocks = NULL;
+ float *ws = NULL;
+
+ BLI_array_staticdeclare(ls, 64);
+ BLI_array_staticdeclare(blocks, 64);
+ BLI_array_staticdeclare(ws, 64);
+
+ int totl = 0;
+
+ BM_LOOPS_OF_VERT_ITER_BEGIN (l, v_del) {
+ MDynTopoVert *mv_l = BKE_PBVH_DYNVERT(pbvh->cd_dyn_vert, l->next->v);
+ mv_l->flag |= mupdateflag;
+
+ BLI_array_append(ls, l);
+ totl++;
+ }
+ BM_LOOPS_OF_VERT_ITER_END;
+
+ BM_LOOPS_OF_VERT_ITER_BEGIN (l, v_conn) {
+ MDynTopoVert *mv_l = BKE_PBVH_DYNVERT(pbvh->cd_dyn_vert, l->next->v);
+ mv_l->flag |= mupdateflag;
+
+ BLI_array_append(ls, l);
+ totl++;
+ }
+ BM_LOOPS_OF_VERT_ITER_END;
+
+ float w = totl > 0 ? 1.0f / (float)(totl) : 1.0f;
+
+ for (int i = 0; i < totl; i++) {
+ BLI_array_append(blocks, ls[i]->head.data);
+ BLI_array_append(ws, w);
+ }
+
+ // snap customdata
+ if (totl > 0) {
+ CustomData_bmesh_interp(
+ &pbvh->bm->ldata, (const void **)blocks, ws, NULL, totl, ls[0]->head.data);
+ //*
+ BM_LOOPS_OF_VERT_ITER_BEGIN (l, v_del) {
+ BMLoop *l2 = l->v != v_del ? l->next : l;
+
+ if (l2 == ls[0]) {
+ continue;
+ }
+
+ CustomData_bmesh_copy_data(
+ &pbvh->bm->ldata, &pbvh->bm->ldata, ls[0]->head.data, &l2->head.data);
+ }
+ BM_LOOPS_OF_VERT_ITER_END;
+
+ BM_LOOPS_OF_VERT_ITER_BEGIN (l, v_conn) {
+ BMLoop *l2 = l->v != v_conn ? l->next : l;
+
+ if (l2 == ls[0]) {
+ continue;
+ }
+
+ CustomData_bmesh_copy_data(
+ &pbvh->bm->ldata, &pbvh->bm->ldata, ls[0]->head.data, &l2->head.data);
+ }
+ BM_LOOPS_OF_VERT_ITER_END;
+ //*/
+ }
+ }
+
+ validate_vert_faces(pbvh, pbvh->bm, v_conn, false, true);
+
+ BMVert **delvs = NULL;
+ BLI_array_staticdeclare(delvs, 8);
+
+ // detect non-manifold "pinch off"
+ l = e->l;
+ if (l) {
+ do {
+ BMLoop *l2 = l->f->l_first;
+ do {
+ MDynTopoVert *mv = BKE_PBVH_DYNVERT(pbvh->cd_dyn_vert, l2->v);
+
+ if (l2->v == v_conn || l2->v == v_del) {
+ continue;
+ }
+
+ // if (mv->flag & DYNVERT_NEED_VALENCE) {
+ BKE_pbvh_bmesh_update_valence(pbvh->cd_dyn_vert, (SculptVertRef){.i = (intptr_t)l2->v});
+ //}
+ // BKE_pbvh_bmesh_check_valence(pbvh, (SculptVertRef){.i = (intptr_t)l2->v});
+
+ if (mv->valence < 4) {
+ bool ok = true;
+
+ for (int i = 0; i < BLI_array_len(delvs); i++) {
+ if (delvs[i] == l2->v) {
+ ok = false;
+ break;
+ }
+ }
+
+ if (ok) {
+ BLI_array_append(delvs, l2->v);
+ }
+ }
+ } while ((l2 = l2->next) != l->f->l_first);
+ } while ((l = l->radial_next) != e->l);
+ }
+
+ const int tag = BM_ELEM_TAG_ALT;
+
+ // log edges around v_conn as removed
+#if 0
+ BMEdge *e2 = v_conn->e;
+ do {
+ e2->head.hflag &= ~tag;
+
+ if (e2 != e) {
+ // BM_log_edge_removed(pbvh->bm_log, e2);
+ }
+ } while ((e2 = BM_DISK_EDGE_NEXT(e2, v_conn)) != v_conn->e);
+#endif
+ BMEdge *e2;
+
+ for (int step = 0; step < 2; step++) {
+ BMVert *v_step = step ? v_del : v_conn;
+
+ e2 = v_step->e;
+
+ // remove faces and log edges around v_del from pbvh
+ do {
+ BMLoop *l = e2->l;
+
+ e2->head.hflag |= tag;
+
+ if (e2 != e) {
+ BM_log_edge_removed(pbvh->bm_log, e2);
+ }
+
+ if (!l) {
+ continue; // edge will be killed later
+ }
+
+ 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, false, false);
+ }
+ } while ((l = l->radial_next) != e2->l);
+ } while ((e2 = BM_DISK_EDGE_NEXT(e2, v_step)) != v_step->e);
+ }
+
+ pbvh_bmesh_vert_remove(pbvh, v_del);
+
+ BM_log_edge_removed(pbvh->bm_log, e);
+ BM_log_vert_removed(pbvh->bm_log, v_del, pbvh->cd_vert_mask_offset);
+
+ BLI_ghash_insert(deleted_verts, (void *)v_del, NULL);
+
+ if (!snap) {
+ float co[3];
+
+ copy_v3_v3(co, v_conn->co);
+ BM_edge_collapse(pbvh->bm, e, v_del, true, true, true);
+ copy_v3_v3(v_conn->co, co);
+ }
+ else {
+ BM_edge_collapse(pbvh->bm, e, v_del, true, true, true);
+ }
+
+ for (int i = 0; i < BLI_array_len(delvs); i++) {
+ BMVert *v = delvs[i];
+ BMEdge *e = v->e;
+
+ if (v == v_del) {
+ continue;
+ }
+
+ MDynTopoVert *mv1 = BKE_PBVH_DYNVERT(pbvh->cd_dyn_vert, v);
+ mv1->flag |= mupdateflag;
+
+ BKE_pbvh_bmesh_remove_vertex(pbvh, v, true);
+
+ if (v_conn == v) {
+ v_conn = NULL;
+ }
+
+ if (e) {
+ do {
+ BMLoop *l = e->l;
+ if (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);
+ }
+ } while ((l = l->radial_next) != e->l);
+ }
+ } while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e);
+ }
+
+ BLI_ghash_insert(deleted_verts, (void *)v, NULL);
+ pbvh_kill_vert(pbvh, v);
+ }
+
+ BLI_array_free(delvs);
+
+ if (!v_conn) {
+ return;
+ }
+
+ MDynTopoVert *mv_conn = BKE_PBVH_DYNVERT(pbvh->cd_dyn_vert, v_conn);
+ mv_conn->flag |= mupdateflag;
+
+ bool wasbad = false;
+
+ e2 = v_conn->e;
+ BMEdge *enext;
+ do {
+ if (!e2) {
+ break;
+ }
+
+ enext = BM_DISK_EDGE_NEXT(e2, v_conn);
+ BMLoop *l = e2->l;
+
+ BMVert *v2 = BM_edge_other_vert(e2, v_conn);
+ MDynTopoVert *mv2 = BKE_PBVH_DYNVERT(pbvh->cd_dyn_vert, v2);
+ mv2->flag |= mupdateflag;
+
+ // kill wire edge
+ if (!l) {
+ // edge should have been marked as removed earlier in the function
+ // BM_log_edge_removed(pbvh->bm_log, e2);
+ BM_edge_kill(pbvh->bm, e2);
+ continue;
+ }
+
+ if (e2->head.hflag & tag) {
+ e2->head.hflag &= ~tag;
+ BM_log_edge_added(pbvh->bm_log, e2);
+ }
+
+ BMLoop *lnext;
+
+ do {
+ BMLoop *l2 = l->f->l_first;
+ lnext = l->radial_next;
+
+ bool fbad = false;
+ do {
+
+ if (l2->v == l2->next->v) {
+ int ni = BM_ELEM_CD_GET_INT(l2->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, l2->f, NULL);
+ }
+
+ printf("duplicate verts in face!! %s\n", __func__);
+ wasbad = true;
+ BM_face_kill(pbvh->bm, l2->f);
+ fbad = true;
+
+ lnext = e2->l ? e2->l->radial_next : NULL;
+ break;
+ }
+
+ } while ((l2 = l2->next) != l->f->l_first);
+
+ if (!fbad && BM_ELEM_CD_GET_INT(l->f, pbvh->cd_face_node_offset) == DYNTOPO_NODE_NONE) {
+ BKE_pbvh_bmesh_add_face(pbvh, l->f, true, false);
+ }
+
+ if (!lnext) {
+ break;
+ }
+ } while ((l = lnext) != e2->l);
+ } while (v_conn->e && (e2 = enext) != v_conn->e);
+ // BM_vert_splice(pbvh->bm, v_del, v_conn);
+
+ MDynTopoVert *mv3 = BKE_PBVH_DYNVERT(pbvh->cd_dyn_vert, v_conn);
+ mv3->flag |= mupdateflag;
+
+ if (BM_ELEM_CD_GET_INT(v_conn, pbvh->cd_vert_node_offset) == DYNTOPO_NODE_NONE) {
+ printf("eek!\n");
+ }
+
+ for (int i = 0; i < 3; i++) {
+ if (!check_for_fins(pbvh, v_conn)) {
+ break;
+ }
+ }
+
+ if (wasbad) {
+ fix_mesh(pbvh, pbvh->bm);
+ return;
+ }
+
+#if 0
+
+ e = v_conn->e;
+ if (e) {
+ do {
+ enext = BM_DISK_EDGE_NEXT(e, v_conn);
+ BMVert *v2 = BM_edge_other_vert(e, v_conn);
+
+ MDynTopoVert *mv4 = BKE_PBVH_DYNVERT(pbvh->cd_dyn_vert, v2);
+
+ } while ((e = enext) != v_conn->e);
+ }
+#endif
+
+ validate_vert_faces(pbvh, pbvh->bm, v_conn, false, true);
+}
+
+static void pbvh_bmesh_collapse_edge1(PBVH *pbvh,
+ BMEdge *e,
+ BMVert *v1,
+ BMVert *v2,
+ GHash *deleted_verts,
+ BLI_Buffer *deleted_faces,
+ EdgeQueueContext *eq_ctx)
+{
+ BMVert *v_del, *v_conn;
+
+ validate_edge(pbvh, pbvh->bm, e, true, true);
+
+ check_vert_fan_are_tris(pbvh, e->v1);
+ check_vert_fan_are_tris(pbvh, e->v2);
+
+ validate_edge(pbvh, pbvh->bm, e, true, true);
+ // pbvh_bmesh_check_nodes_simple(pbvh);
+
+ bm_log_message(" == collapse == ");
+
+ // make sure origdata is up to date prior to interpolation
+ BKE_pbvh_bmesh_check_origdata(pbvh, e->v1, pbvh->stroke_id);
+ BKE_pbvh_bmesh_check_origdata(pbvh, e->v2, pbvh->stroke_id);
+
+ const int mupdateflag = DYNVERT_NEED_BOUNDARY | DYNVERT_NEED_DISK_SORT | DYNVERT_NEED_VALENCE;
+
+ // customdata interpolation
+
+ /* one of the two vertices may be masked, select the correct one for deletion */
+ if (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;
+ }
+
+ validate_vert_faces(pbvh, pbvh->bm, v_conn, false, true);
+
+ 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->bm->vdata, v_blocks, v_ws, NULL, 2, v_conn->head.data);
+ BM_ELEM_CD_SET_INT(v_conn, pbvh->cd_vert_node_offset, ni_conn);
+
+ /* Remove the merge vertex from the PBVH */
+ pbvh_bmesh_vert_remove(pbvh, v_del);
+
+ validate_vert_faces(pbvh, pbvh->bm, v_conn, false, true);
+ // pbvh_bmesh_check_nodes_simple(pbvh);
+
+ /* Remove all faces adjacent to the edge */
+ BMLoop *l_adj;
+ while ((l_adj = e->l)) {
+ BMFace *f_adj = l_adj->f;
+
+ int eflag = 0;
+
+ BMLoop *l = f_adj->l_first;
+ do {
+ BMEdge *e2 = l->e;
+
+ MDynTopoVert *mv_l = BKE_PBVH_DYNVERT(pbvh->cd_dyn_vert, l->v);
+ mv_l->flag |= mupdateflag;
+
+ l = l->next;
+ } while (l != f_adj->l_first);
+
+ pbvh_bmesh_face_remove(pbvh, f_adj, true, true, true);
+ BM_face_kill(pbvh->bm, f_adj);
+ }
+
+ validate_vert_faces(pbvh, pbvh->bm, v_conn, false, false);
+
+ // pbvh_bmesh_check_nodes_simple(pbvh);
+
+ /* Kill the edge */
+ BLI_assert(BM_edge_is_wire(e));
+
+ validate_edge(pbvh, pbvh->bm, e, true, true);
+
+ BM_log_edge_removed(pbvh->bm_log, e);
+ BM_edge_kill(pbvh->bm, e);
+
+ validate_vert_faces(pbvh, pbvh->bm, v_conn, false, false);
+
+ /* 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 = NULL;
+ BMLoop **ls = NULL;
+ void **blocks = NULL;
+ float *ws = NULL;
+
+ BLI_array_staticdeclare(ls, 64);
+ BLI_array_staticdeclare(blocks, 64);
+ BLI_array_staticdeclare(ws, 64);
+
+ int totl = 0;
+
+ BM_LOOPS_OF_VERT_ITER_BEGIN (l, v_del) {
+ MDynTopoVert *mv_l = BKE_PBVH_DYNVERT(pbvh->cd_dyn_vert, l->v);
+ mv_l->flag |= mupdateflag;
+
+ BLI_array_append(ls, l);
+ totl++;
+ }
+ BM_LOOPS_OF_VERT_ITER_END;
+
+ BM_LOOPS_OF_VERT_ITER_BEGIN (l, v_conn) {
+ MDynTopoVert *mv_l = BKE_PBVH_DYNVERT(pbvh->cd_dyn_vert, l->v);
+ mv_l->flag |= mupdateflag;
+
+ BLI_array_append(ls, l);
+ totl++;
+ }
+ BM_LOOPS_OF_VERT_ITER_END;
+
+ float w = totl > 0 ? 1.0f / (float)(totl) : 1.0f;
+
+ for (int i = 0; i < totl; i++) {
+ BLI_array_append(blocks, ls[i]->head.data);
+ BLI_array_append(ws, w);
+ }
+
+ // snap customdata
+ if (totl > 0) {
+ CustomData_bmesh_interp(
+ &pbvh->bm->ldata, (const void **)blocks, ws, NULL, totl, ls[0]->head.data);
+ //*
+ BM_LOOPS_OF_VERT_ITER_BEGIN (l, v_del) {
+ BMLoop *l2 = l->v != v_del ? l->next : l;
+
+ if (l2 == ls[0]) {
+ continue;
+ }
+
+ CustomData_bmesh_copy_data(
+ &pbvh->bm->ldata, &pbvh->bm->ldata, ls[0]->head.data, &l2->head.data);
+ }
+ BM_LOOPS_OF_VERT_ITER_END;
+
+ BM_LOOPS_OF_VERT_ITER_BEGIN (l, v_conn) {
+ BMLoop *l2 = l->v != v_conn ? l->next : l;
+
+ if (l2 == ls[0]) {
+ continue;
+ }
+
+ CustomData_bmesh_copy_data(
+ &pbvh->bm->ldata, &pbvh->bm->ldata, ls[0]->head.data, &l2->head.data);
+ }
+ BM_LOOPS_OF_VERT_ITER_END;
+ //*/
+ }
+
+ validate_vert_faces(pbvh, pbvh->bm, v_conn, false, true);
+
+ // pbvh_bmesh_check_nodes_simple(pbvh);
+
+ 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(NULL, BM_VERTS_OF_FACE, f, (void **)v_tri, 3);
+ BMFace *f = l->f;
+
+ bool ok = true;
+
+ for (int j = 0; j < (int)deleted_faces->count; j++) {
+ if (BLI_buffer_at(deleted_faces, BMFace *, j) == f) {
+ ok = false;
+ }
+ }
+
+ if (ok) {
+ BLI_buffer_append(deleted_faces, BMFace *, f);
+ }
+ else {
+ printf("tried to add same face to deleted list twice. %x %d\n", f, f->len);
+ continue;
+ }
+
+ /* 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
+ BMVert *v_tri[3] = {v_conn, l->next->v, l->prev->v};
+ 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
+ {
+ bool ok2 = true;
+
+ // check we're not already in deleted_faces
+ for (int i = 0; i < (int)deleted_faces->count; i++) {
+ if (BLI_buffer_at(deleted_faces, BMFace *, i) == existing_face) {
+ ok2 = false;
+ break;
+ }
+ }
+
+ if (ok2) {
+ BLI_buffer_append(deleted_faces, BMFace *, existing_face);
+ }
+ }
+ else
+ {
+ BMVert *old_tri[3] = {v_del, l->next->v, l->prev->v};
+ BMVert *v_tri[3] = {v_conn, l->next->v, l->prev->v};
+
+ if (v_conn == l->next->v || v_conn == l->prev->v) {
+ // this can happen on non-manifold meshes
+ continue;
+ }
+
+#ifdef CHECKMESH
+
+ int m = 0;
+# define LTEST1(l, bit) \
+ if ((l) != (l)->radial_next && (l) == (l)->radial_next->radial_next && \
+ (l)->v == (l)->radial_next->v) { \
+ m |= 1 << bit; \
+ }
+
+ if (l->f->len != 3) {
+ printf("error in %s!!\n", __func__);
+ }
+
+ if (BM_face_exists(v_tri, 3)) {
+ printf("face exists\n");
+ continue;
+ }
+
+ LTEST1(l, 0)
+ LTEST1(l->next, 0)
+ LTEST1(l->prev, 0)
+
+ if (m) {
+ printf("non manifold error! %d\n", m);
+ // SWAP(BMVert *, v_tri[0], v_tri[2]);
+ // SWAP(BMVert *, old_tri[0], old_tri[2]);
+ }
+#endif
+
+ MDynTopoVert *mv2 = BKE_PBVH_DYNVERT(pbvh->cd_dyn_vert, v_tri[1]);
+ MDynTopoVert *mv3 = BKE_PBVH_DYNVERT(pbvh->cd_dyn_vert, v_tri[2]);
+
+ mv2->flag |= mupdateflag;
+ mv3->flag |= mupdateflag;
+
+ 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;
+
+ n->flag |= PBVH_UpdateOtherVerts;
+
+ e_tri[0] = BM_edge_exists(old_tri[0], old_tri[1]);
+ e_tri[1] = BM_edge_exists(old_tri[1], old_tri[2]);
+ e_tri[2] = BM_edge_exists(old_tri[2], old_tri[0]);
+
+ validate_vert_faces(pbvh, pbvh->bm, v_conn, false, false);
+
+ if (!e_tri[0] || !e_tri[1] || !e_tri[2]) {
+ printf("%s: missing edges!\n", __func__);
+ bm_edges_from_tri(pbvh, old_tri, e_tri);
+ validate_vert_faces(pbvh, pbvh->bm, v_conn, false, false);
+ }
+
+ bm_edges_from_tri_example(pbvh, v_tri, e_tri);
+ validate_vert_faces(pbvh, pbvh->bm, v_conn, false, false);
+
+ BMFace *f2 = BM_face_exists(v_tri, 3);
+
+ if (!f2) {
+ f2 = pbvh_bmesh_face_create(pbvh, ni, v_tri, e_tri, f, false, true);
+ }
+
+ validate_vert_faces(pbvh, pbvh->bm, v_conn, false, false);
+
+ BMLoop *l2 = f2->l_first;
+
+ CustomData_bmesh_swap_data_simple(&pbvh->bm->edata, &l2->e->head.data, &l->e->head.data);
+ CustomData_bmesh_swap_data_simple(
+ &pbvh->bm->edata, &l2->next->e->head.data, &l->next->e->head.data);
+ CustomData_bmesh_swap_data_simple(
+ &pbvh->bm->edata, &l2->prev->e->head.data, &l->prev->e->head.data);
+
+ pbvh_bmesh_copy_facedata(pbvh, pbvh->bm, f2, f);
+ validate_vert_faces(pbvh, pbvh->bm, v_conn, false, false);
+
+ CustomData_bmesh_copy_data(&pbvh->bm->ldata, &pbvh->bm->ldata, l->head.data, &l2->head.data);
+ CustomData_bmesh_copy_data(
+ &pbvh->bm->ldata, &pbvh->bm->ldata, l->next->head.data, &l2->next->head.data);
+ CustomData_bmesh_copy_data(
+ &pbvh->bm->ldata, &pbvh->bm->ldata, l->prev->head.data, &l2->prev->head.data);
+
+#if 0
+ BMLoop *l3 = f2->l_first;
+ do {
+ if (l3->v == f2->l_first->v && l3->f != f2 && l3->f != l->f) {
+ printf("manifold error %s\n", __func__);
+ BM_face_normal_flip(pbvh->bm, f2);
+ break;
+ }
+ } while ((l3 = l3->radial_next) != f2->l_first);
+#endif
+ }
+ }
+ BM_LOOPS_OF_VERT_ITER_END;
+
+ // pbvh_bmesh_check_nodes_simple(pbvh);
+
+ /* Delete the tagged faces */
+ for (int i = 0; i < (int)deleted_faces->count; i++) {
+ v_conn ? validate_vert_faces(pbvh, pbvh->bm, v_conn, false, false) : NULL;
+
+ 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;
+
+ BMLoop *l1 = f_del->l_first;
+ do {
+ if (!l1->e) {
+ printf("bmesh error in %s!\n", __func__);
+ l1->e = bmesh_edge_create_log(pbvh, l1->v, l1->next->v, NULL);
+ }
+
+ MDynTopoVert *mv_l = BKE_PBVH_DYNVERT(pbvh->cd_dyn_vert, l->v);
+ mv_l->flag |= mupdateflag;
+
+ l1 = l1->next;
+ } while (l1 != f_del->l_first);
+
+ /* Remove the face */
+ pbvh_bmesh_face_remove(pbvh, f_del, true, true, true);
+ v_conn ? validate_vert_faces(pbvh, pbvh->bm, v_conn, false, false) : NULL;
+ BM_face_kill(pbvh->bm, f_del);
+ v_conn ? validate_vert_faces(pbvh, pbvh->bm, v_conn, false, false) : NULL;
+
+ /* Check if any of the face's edges are now unused by any
+ * face, if so delete them */
+ if (e_tri[0] == e_tri[1] || e_tri[1] == e_tri[2] || e_tri[0] == e_tri[2]) {
+ printf("%s: error: duplicate edges in e_tri %p %p %p\n",
+ __func__,
+ e_tri[0],
+ e_tri[1],
+ e_tri[2]);
+ }
+ else {
+ for (int j = 0; j < 3; j++) {
+ if (BM_edge_is_wire(e_tri[j])) {
+ v_conn ? validate_vert_faces(pbvh, pbvh->bm, v_conn, false, false) : NULL;
+ BM_log_edge_removed(pbvh->bm_log, e_tri[j]);
+ BM_edge_kill(pbvh->bm, e_tri[j]);
+ v_conn ? validate_vert_faces(pbvh, pbvh->bm, v_conn, false, false) : NULL;
+ }
+ }
+ }
+
+ /* 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 == NULL)) {
+ 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 = NULL;
+ }
+ BLI_ghash_insert(deleted_verts, v_tri[j], NULL);
+ pbvh_kill_vert(pbvh, 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 != NULL) {
+ // log vert in bmlog, but don't update original customata layers, we want them to be
+ // interpolated
+ BM_log_vert_before_modified(pbvh->bm_log, v_conn, eq_ctx->cd_vert_mask_offset, false);
+
+ 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);
+ }
+
+ BM_log_vert_removed(pbvh->bm_log, v_del, eq_ctx->cd_vert_mask_offset);
+ BLI_ghash_insert(deleted_verts, v_del, v_conn);
+
+ if (v_conn != NULL) {
+
+ /* update boundboxes 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) {
+ BMVert *v2 = BM_edge_other_vert(l->e, v_conn);
+ MDynTopoVert *mv2 = BKE_PBVH_DYNVERT(pbvh->cd_dyn_vert, v2);
+
+ mv2->flag |= mupdateflag;
+
+ PBVHNode *f_node = pbvh_bmesh_node_from_face(pbvh, l->f);
+ f_node->flag |= PBVH_UpdateDrawBuffers | PBVH_UpdateNormals | PBVH_UpdateBB |
+ PBVH_UpdateTris | PBVH_UpdateOtherVerts;
+ }
+ BM_LOOPS_OF_VERT_ITER_END;
+
+ MDynTopoVert *mv_conn = BKE_PBVH_DYNVERT(pbvh->cd_dyn_vert, v_conn);
+ mv_conn->flag |= mupdateflag;
+
+ MDynTopoVert *mv_del = BKE_PBVH_DYNVERT(pbvh->cd_dyn_vert, v_del);
+ mv_conn->flag |= mv_del->flag;
+
+ validate_vert(pbvh, pbvh->bm, v_conn, true, false);
+ }
+
+ validate_vert(pbvh, pbvh->bm, v_del, true, false);
+
+ if (v_del->e) {
+ BMEdge *e = v_del->e;
+
+ do {
+ if (e->l) {
+ BMLoop *l = e->l;
+ do {
+ printf("warning in collapse_edge\n");
+ int ni = BM_ELEM_CD_GET_INT(l->f, pbvh->cd_face_node_offset);
+
+ BM_log_face_removed(pbvh->bm_log, l->f);
+
+ // paranoia check, propegate update flags
+ MDynTopoVert *mv_l = BKE_PBVH_DYNVERT(pbvh->cd_dyn_vert, l->v);
+ mv_l->flag |= mupdateflag;
+ mv_l = BKE_PBVH_DYNVERT(pbvh->cd_dyn_vert, l->next->v);
+ mv_l->flag |= mupdateflag;
+
+ if (ni >= 0) {
+ PBVHNode *node = pbvh->nodes + ni;
+ BLI_table_gset_remove(node->bm_faces, l->f, NULL);
+ BM_ELEM_CD_SET_INT(l->f, pbvh->cd_face_node_offset, DYNTOPO_NODE_NONE);
+ }
+ else {
+ printf("face did not have a node ref\n");
+ }
+ } while ((l = l->radial_next) != e->l);
+ }
+
+ e = BM_DISK_EDGE_NEXT(e, v_del);
+ } while (e != v_del->e);
+
+ validate_vert(pbvh, pbvh->bm, v_del, true, false);
+ }
+
+ /* Delete v_del */
+ validate_vert(pbvh, pbvh->bm, v_del, true, false);
+ pbvh_kill_vert(pbvh, v_del);
+
+ BLI_array_free(ws);
+ BLI_array_free(blocks);
+ BLI_array_free(ls);
+
+ if (v_conn) {
+ validate_vert_faces(pbvh, pbvh->bm, v_conn, false, true);
+ }
+ // pbvh_bmesh_check_nodes_simple(pbvh);
+}
+
+static bool pbvh_bmesh_collapse_short_edges(EdgeQueueContext *eq_ctx,
+ PBVH *pbvh,
+ BLI_Buffer *deleted_faces,
+ int max_steps)
+{
+ if (pbvh->dyntopo_stop) {
+ return false;
+ }
+
+ 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 NULL when removed. */
+ GHash *deleted_verts = BLI_ghash_ptr_new("deleted_verts");
+
+ double time = PIL_check_seconds_timer();
+ RNG *rng = BLI_rng_new((unsigned int)(time * 1000.0f));
+
+//#define TEST_COLLAPSE
+#ifdef TEST_COLLAPSE
+ int _i = 0;
+#endif
+
+ int step = 0;
+
+ while (!BLI_heapsimple_is_empty(eq_ctx->q->heap)) {
+ if (step++ > max_steps) {
+ break;
+ }
+#ifdef DYNTOPO_TIME_LIMIT
+ if (PIL_check_seconds_timer() - time > DYNTOPO_TIME_LIMIT) {
+ break;
+ }
+#endif
+
+#ifndef DYNTOPO_USE_HEAP
+ if (eq_ctx->q->totelems == 0) {
+ break;
+ }
+
+ int ri = BLI_rng_get_int(rng) % eq_ctx->q->totelems;
+
+ BMVert **pair = eq_ctx->q->elems[ri];
+ eq_ctx->q->elems[ri] = eq_ctx->q->elems[eq_ctx->q->totelems - 1];
+ eq_ctx->q->totelems--;
+#else
+ EdgePair *pair = BLI_heapsimple_pop_min(eq_ctx->q->heap);
+#endif
+ BMVert *v1 = pair->v1, *v2 = pair->v2;
+ float limit_len_squared = pair->limit_len_squared;
+
+ BLI_mempool_free(eq_ctx->pool, pair);
+ pair = NULL;
+
+ /* 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;
+ }
+
+ if (bm_elem_is_free((BMElem *)v1, BM_VERT) || bm_elem_is_free((BMElem *)v2, BM_VERT)) {
+ continue;
+ }
+
+ /* Check that the edge still exists */
+ BMEdge *e;
+ if (!(e = BM_edge_exists(v1, v2))) {
+ continue;
+ }
+
+ /* Also ignore non-manifold edges */
+ if (e->l && e->l != e->l->radial_next->radial_next) {
+ continue;
+ }
+
+#ifdef USE_EDGEQUEUE_TAG
+ EDGE_QUEUE_DISABLE(e);
+#endif
+
+ if (calc_weighted_edge_collapse(eq_ctx, v1, v2) >= limit_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;
+
+ eq_ctx->q->limit_len_squared = limit_len_squared;
+ pbvh_bmesh_collapse_edge(pbvh, e, v1, v2, deleted_verts, deleted_faces, eq_ctx);
+
+#ifdef TEST_COLLAPSE
+ if (_i++ > 10) {
+ break;
+ }
+#endif
+ }
+
+#if !defined(DYNTOPO_USE_HEAP) && defined(USE_EDGEQUEUE_TAG)
+ for (int i = 0; i < eq_ctx->q->totelems; i++) {
+ BMVert **pair = eq_ctx->q->elems[i];
+ BMVert *v1 = pair[0], *v2 = pair[1];
+
+ /* 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;
+ }
+
+ BMEdge *e = BM_edge_exists(v1, v2);
+ if (e) {
+ EDGE_QUEUE_DISABLE(e);
+ }
+ }
+#endif
+ BLI_rng_free(rng);
+ BLI_ghash_free(deleted_verts, NULL, NULL);
+
+ return any_collapsed;
+}
+
+// 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)
+{
+ bool modified = false;
+
+ 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 = DYNVERT_NEED_BOUNDARY | DYNVERT_NEED_DISK_SORT | DYNVERT_NEED_VALENCE;
+
+ for (int vi = 0; vi < ectx->val34_verts_tot; vi++) {
+ if (pbvh->dyntopo_stop) {
+ break;
+ }
+
+ BMVert *v = ectx->val34_verts[vi];
+
+ 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;
+ }
+
+ if (len_squared_v3v3(v->co, center) >= rsqr || !v->e) {
+ continue;
+ }
+
+ if (check_for_fins(pbvh, v)) {
+ continue;
+ }
+
+ MDynTopoVert *mv = BKE_PBVH_DYNVERT(pbvh->cd_dyn_vert, v);
+
+ mv->flag &= ~DYNVERT_VALENCE_TEMP;
+
+ validate_vert(pbvh, pbvh->bm, v, false, true);
+ check_vert_fan_are_tris(pbvh, v);
+ validate_vert(pbvh, pbvh->bm, v, true, true);
+
+ BKE_pbvh_bmesh_check_valence(pbvh, (SculptVertRef){.i = (intptr_t)v});
+
+ int val = mv->valence;
+ if (val != 4 && val != 3) {
+ continue;
+ }
+
+ // check valence again
+ val = BM_vert_edge_count(v);
+ if (val != 4 && val != 3) {
+ printf("pbvh valence error\n");
+ continue;
+ }
+
+ pbvh_check_vert_boundary(pbvh, v);
+
+ if (mv->flag & DYNVERT_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 i = 0;
+
+ for (int j = 0; j < val; j++) {
+ ls[i++] = l->v == v ? l->next : l;
+
+ MDynTopoVert *mv_l;
+
+ if (l->v == v) {
+ mv_l = BKE_PBVH_DYNVERT(pbvh->cd_dyn_vert, l->next->v);
+ }
+ else {
+ mv_l = BKE_PBVH_DYNVERT(pbvh->cd_dyn_vert, l->v);
+ }
+
+ mv_l->flag |= 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, NULL);
+ }
+ }
+ }
+
+ BM_log_vert_removed(pbvh->bm_log, v, pbvh->cd_vert_mask_offset);
+ pbvh_bmesh_vert_remove(pbvh, v);
+
+#if 0
+
+ BMEdge *e2 = v->e;
+ if (e2) {
+ BMVert **verts = NULL;
+ BLI_array_staticdeclare(verts, 20);
+
+ do {
+ BMLoop *l = e2->l;
+ if (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, false, false);
+ }
+ } while ((l = l->radial_next) != e2->l);
+ }
+
+ BLI_array_append(verts, BM_edge_other_vert(e2, v));
+ BM_log_edge_removed(pbvh->bm_log, e2);
+ } while ((e2 = BM_DISK_EDGE_NEXT(e2, v)) != v->e);
+
+ // BM_disk_dissolve(pbvh->bm, v);
+ BM_vert_dissolve(pbvh->bm, v);
+
+ for (int j = 0; j < BLI_array_len(verts); j++) {
+ BMVert *v2 = verts[j];
+
+ MDynTopoVert *mv2 = BKE_PBVH_DYNVERT(pbvh->cd_dyn_vert, v2);
+ mv2->flag |= DYNVERT_NEED_BOUNDARY | DYNVERT_NEED_DISK_SORT | DYNVERT_NEED_VALENCE |
+ DYNVERT_NEED_TRIANGULATE;
+
+ BMEdge *e3 = v2->e;
+ do {
+ BMLoop *l2 = e3->l;
+ if (!l2) {
+ continue;
+ }
+
+ do {
+ if (BM_ELEM_CD_GET_INT(l2->f, pbvh->cd_face_node_offset) == DYNTOPO_NODE_NONE) {
+ BKE_pbvh_bmesh_add_face(pbvh, l2->f, true, false);
+
+ BMLoop *l3 = l2->f->l_first;
+ do {
+ MDynTopoVert *mv3 = BKE_PBVH_DYNVERT(pbvh->cd_dyn_vert, l3->v);
+ mv3->flag |= DYNVERT_NEED_BOUNDARY | DYNVERT_NEED_DISK_SORT |
+ DYNVERT_NEED_VALENCE | DYNVERT_NEED_TRIANGULATE;
+ } while ((l3 = l3->next) != l2->f->l_first);
+ }
+ } while ((l2 = l2->radial_next) != e3->l);
+ } while ((e3 = BM_DISK_EDGE_NEXT(e3, v2)) != v2->e);
+ }
+
+ for (int j = 0; j < BLI_array_len(verts); j++) {
+ BMVert *v2 = verts[j];
+
+ check_vert_fan_are_tris(pbvh, v2);
+ }
+
+ BLI_array_free(verts);
+ }
+ else {
+ BM_vert_kill(pbvh->bm, v);
+ }
+
+ continue;
+#endif
+
+ 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, NULL);
+
+ pbvh_bmesh_face_remove(pbvh, f, true, true, true);
+ }
+ }
+
+ modified = true;
+
+ if (!v->e) {
+ printf("mesh error!\n");
+ continue;
+ }
+
+ validate_vert(pbvh, pbvh->bm, v, false, true);
+
+ l = v->e->l;
+
+ bool flipped = false;
+
+ 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) {
+ flipped = true;
+ 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->bm, v, false, false);
+
+ MDynTopoVert *mv1 = BKE_PBVH_DYNVERT(pbvh->cd_dyn_vert, vs[0]);
+ MDynTopoVert *mv2 = BKE_PBVH_DYNVERT(pbvh->cd_dyn_vert, vs[1]);
+ MDynTopoVert *mv3 = BKE_PBVH_DYNVERT(pbvh->cd_dyn_vert, vs[2]);
+
+ mv1->flag |= updateflag;
+ mv2->flag |= updateflag;
+ mv3->flag |= updateflag;
+
+ BMFace *f1 = NULL;
+ 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, NULL, 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->bm, f1, false, false);
+ }
+ else {
+ // printf("eek1!\n");
+ }
+
+ BMFace *f2 = NULL;
+
+ 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) {
+ MDynTopoVert *mv1 = BKE_PBVH_DYNVERT(pbvh->cd_dyn_vert, vs[0]);
+ MDynTopoVert *mv2 = BKE_PBVH_DYNVERT(pbvh->cd_dyn_vert, vs[1]);
+ MDynTopoVert *mv3 = BKE_PBVH_DYNVERT(pbvh->cd_dyn_vert, vs[2]);
+
+ mv1->flag |= updateflag;
+ mv2->flag |= updateflag;
+ mv3->flag |= updateflag;
+
+ BMFace *example = NULL;
+ if (v->e && v->e->l) {
+ example = v->e->l->f;
+ }
+
+ f2 = pbvh_bmesh_face_create(pbvh, n, vs, NULL, example, true, false);
+
+ CustomData_bmesh_swap_data_simple(
+ &pbvh->bm->ldata, &f2->l_first->prev->head.data, &ls[3]->head.data);
+ CustomData_bmesh_copy_data(
+ &pbvh->bm->ldata, &pbvh->bm->ldata, ls[0]->head.data, &f2->l_first->head.data);
+ CustomData_bmesh_copy_data(
+ &pbvh->bm->ldata, &pbvh->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->bm_log, f2);
+
+ validate_face(pbvh, pbvh->bm, f2, false, false);
+ }
+
+ if (f1) {
+ CustomData_bmesh_swap_data_simple(
+ &pbvh->bm->ldata, &f1->l_first->head.data, &ls[0]->head.data);
+ CustomData_bmesh_swap_data_simple(
+ &pbvh->bm->ldata, &f1->l_first->next->head.data, &ls[1]->head.data);
+ CustomData_bmesh_swap_data_simple(
+ &pbvh->bm->ldata, &f1->l_first->prev->head.data, &ls[2]->head.data);
+
+ BM_log_face_added(pbvh->bm_log, f1);
+ }
+
+ validate_vert(pbvh, pbvh->bm, v, false, false);
+ pbvh_kill_vert(pbvh, v);
+
+ 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->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->bm, f2);
+ }
+ }
+ }
+
+ if (modified) {
+ pbvh->bm->elem_index_dirty |= BM_VERT | BM_FACE | BM_EDGE;
+ pbvh->bm->elem_table_dirty |= BM_VERT | BM_FACE | BM_EDGE;
+ }
+
+ 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, NULL);
+ 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 void on_vert_swap(BMVert *v1, BMVert *v2, void *userdata)
+{
+ SwapData *sdata = (SwapData *)userdata;
+ PBVH *pbvh = sdata->pbvh;
+ BMesh *bm = pbvh->bm;
+
+ MDynTopoVert *mv1 = BKE_PBVH_DYNVERT(pbvh->cd_dyn_vert, v1);
+ MDynTopoVert *mv2 = BKE_PBVH_DYNVERT(pbvh->cd_dyn_vert, v2);
+
+ mv1->flag |= DYNVERT_NEED_BOUNDARY | DYNVERT_NEED_VALENCE | DYNVERT_NEED_DISK_SORT;
+ mv2->flag |= DYNVERT_NEED_BOUNDARY | DYNVERT_NEED_VALENCE | DYNVERT_NEED_DISK_SORT;
+
+ int ni1 = BM_ELEM_CD_GET_INT(v1, pbvh->cd_vert_node_offset);
+ int ni2 = BM_ELEM_CD_GET_INT(v2, pbvh->cd_vert_node_offset);
+
+ // check we don't have an orphan vert
+ PBVHNode *node1 = v1->e && v1->e->l && ni1 >= 0 ? pbvh->nodes + ni1 : NULL;
+ PBVHNode *node2 = v2->e && v2->e->l && ni2 >= 0 ? pbvh->nodes + ni2 : NULL;
+
+ if ((node1 && !(node1->flag & PBVH_Leaf)) || (node2 && !(node2->flag & PBVH_Leaf))) {
+ printf("node error! %s\n", __func__);
+ }
+
+ int updateflag = PBVH_UpdateOtherVerts;
+
+ if (node1 && node1->bm_unique_verts) {
+ node1->flag |= PBVH_UpdateOtherVerts;
+ pbvh_node_tribuf_swap_verts(pbvh, node1, node2, v1, v2);
+ }
+
+ if (node2 && node2->bm_unique_verts && node2 != node1) {
+ node2->flag |= PBVH_UpdateOtherVerts;
+ pbvh_node_tribuf_swap_verts(pbvh, node2, node1, v2, v1);
+ }
+
+ if (!node1 || !node2) {
+ // eek!
+ printf("swap pbvh error! %s %d %d\n", __func__, ni1, ni2);
+ return;
+ }
+}
+
+static unsigned int rseed = 0;
+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)
+{
+ EdgeQueue q;
+
+ bool modified = false;
+
+ eq_ctx->q = &q;
+ edge_queue_init(eq_ctx, use_projected, use_frontface, center, view_normal, radius);
+
+ 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->q->edge_queue_vert_in_range(eq_ctx->q, v)) {
+ continue;
+ }
+
+ if (use_frontface && dot_v3v3(v->no, view_normal) < 0.0f) {
+ continue;
+ }
+
+ MDynTopoVert *mv = BKE_PBVH_DYNVERT(pbvh->cd_dyn_vert, v);
+
+ if (mv->flag & DYNVERT_NEED_VALENCE) {
+ BKE_pbvh_bmesh_update_valence(pbvh->cd_dyn_vert, (SculptVertRef){.i = (intptr_t)v});
+ }
+
+ if (mv->valence < 5) {
+ edge_queue_insert_val34_vert(eq_ctx, v);
+ }
+ }
+ TGSET_ITER_END;
+ }
+
+ BM_log_entry_add_ex(pbvh->bm, pbvh->bm_log, true);
+
+ pbvh_bmesh_check_nodes(pbvh);
+
+ // untag val34 verts
+ for (int i = 0; i < eq_ctx->val34_verts_tot; i++) {
+ BMVert *v = eq_ctx->val34_verts[i];
+
+ if (!v || v->head.htype != BM_VERT || !v->head.data) {
+ printf("%s error\n", __func__);
+ continue;
+ }
+ MDynTopoVert *mv = BKE_PBVH_DYNVERT(pbvh->cd_dyn_vert, v);
+
+ mv->flag &= ~DYNVERT_VALENCE_TEMP;
+ }
+
+ modified |= cleanup_valence_3_4(
+ eq_ctx, pbvh, center, view_normal, radius, use_frontface, use_projected);
+ pbvh_bmesh_check_nodes(pbvh);
+
+ return modified;
+}
+
+/* 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,
+ const bool use_frontface,
+ const bool use_projected,
+ int sym_axis,
+ bool updatePBVH,
+ DyntopoMaskCB mask_cb,
+ void *mask_cb_data,
+ int custom_max_steps)
+{
+
+ /* 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->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_dyn_vert = pbvh->cd_dyn_vert;
+ float ratio = 1.0f;
+
+ bool modified = false;
+
+ if (view_normal) {
+ BLI_assert(len_squared_v3(view_normal) != 0.0f);
+ }
+
+#ifdef DYNTOPO_REPORT
+ {
+ size_t totmem;
+ BMesh *bm = pbvh->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 ((mode & PBVH_Subdivide) && (!(mode & PBVH_Collapse) || (mode & PBVH_LocalCollapse))) {
+ safe_smooth = DYNTOPO_SAFE_SMOOTH_SUBD_ONLY_FAC;
+ }
+ else {
+ safe_smooth = DYNTOPO_SAFE_SMOOTH_FAC;
+ }
+
+ /*
+
+
+typedef struct EdgeQueueContext {
+ EdgeQueue *q;
+ BLI_mempool *pool;
+ BMesh *bm;
+ DyntopoMaskCB mask_cb;
+ void *mask_cb_data;
+ int cd_dyn_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;
+ BMVert **val34_verts;
+ int val34_verts_tot;
+ int val34_verts_size;
+ bool local_mode;
+ float surface_smooth_fac;
+} EdgeQueueContext;
+*/
+ EdgeQueueContext eq_ctx = {.q = NULL,
+ .pool = NULL,
+ .bm = pbvh->bm,
+ .mask_cb = mask_cb,
+ .mask_cb_data = mask_cb_data,
+
+ .cd_dyn_vert = cd_dyn_vert,
+ .cd_vert_mask_offset = cd_vert_mask_offset,
+ .cd_vert_node_offset = cd_vert_node_offset,
+ .cd_face_node_offset = cd_face_node_offset,
+ .avg_elen = 0.0f,
+ .max_elen = -1e17,
+ .min_elen = 1e17,
+ .totedge = 0.0f,
+ .val34_verts = NULL,
+ .val34_verts_tot = 0,
+ .val34_verts_size = 0,
+ .local_mode = false,
+ .surface_smooth_fac = safe_smooth};
+
+ int tempflag = 1 << 15;
+
+#if 1
+
+ // if no collapse, run cleanup here to avoid degenerate geometry
+ if ((mode & PBVH_Cleanup) && !(mode & PBVH_Collapse)) {
+ modified |= do_cleanup_3_4(
+ &eq_ctx, pbvh, center, view_normal, radius, use_frontface, use_projected);
+ }
+
+ if (mode & PBVH_Subdivide) {
+ BM_log_entry_add_ex(pbvh->bm, pbvh->bm_log, true);
+
+ EdgeQueue q;
+ BLI_mempool *queue_pool = BLI_mempool_create(sizeof(EdgePair), 0, 128, BLI_MEMPOOL_NOP);
+
+ eq_ctx.q = &q;
+ eq_ctx.pool = queue_pool;
+
+ long_edge_queue_create(&eq_ctx,
+ pbvh,
+ center,
+ view_normal,
+ radius,
+ use_frontface,
+ use_projected,
+ mode & PBVH_LocalSubdivide);
+
+# 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 emin = eq_ctx.min_elen;
+ if (emin == 0.0f) {
+ emin = 0.0001f;
+ }
+
+ if (avg_elen > 0.0f) {
+ ratio = (pbvh->bm_max_edge_len * 0.5 + emin * 0.5) / avg_elen;
+ ratio = MAX2(ratio, 0.75f);
+ ratio = MIN2(ratio, 1.0f);
+ }
+ }
+# else
+ ratio = 1.0f;
+# endif
+ float edgelen = eq_ctx.local_mode && eq_ctx.totedge ?
+ eq_ctx.avg_elen / eq_ctx.totedge :
+ (pbvh->bm_min_edge_len * 0.5f + pbvh->bm_max_edge_len * 0.5f);
+
+ float brusharea = radius / edgelen;
+ //(pbvh->bm_min_edge_len * 0.5f + pbvh->bm_max_edge_len * 0.5f);
+ brusharea = brusharea * brusharea * M_PI;
+
+ int max_steps;
+
+ if (custom_max_steps == 0) {
+ //(int)((float)DYNTOPO_MAX_ITER * ratio);
+ max_steps = (int)(brusharea * ratio * 2.0f);
+
+ max_steps = MIN2(max_steps, DYNTOPO_MAX_ITER);
+ }
+ else {
+ max_steps = custom_max_steps;
+ }
+
+# ifdef DYNTOPO_REPORT
+ printf("brusharea: %.2f, ratio: %.2f\n", brusharea, ratio);
+ printf("subdivide max_steps %d\n", max_steps);
+# endif
+
+ pbvh_bmesh_check_nodes(pbvh);
+ modified |= pbvh_bmesh_subdivide_long_edges(
+ &eq_ctx, pbvh, &edge_loops, max_steps, (mode & PBVH_Cleanup));
+ pbvh_bmesh_check_nodes(pbvh);
+
+ if (q.elems) {
+ MEM_freeN(q.elems);
+ }
+ BLI_heapsimple_free(q.heap, NULL);
+ BLI_mempool_destroy(queue_pool);
+ }
+
+#endif
+
+ // if have collapse, run cleanup for nicer geometry more compatible with topology rake
+ if ((mode & PBVH_Cleanup) && (mode & PBVH_Collapse)) {
+ modified |= do_cleanup_3_4(
+ &eq_ctx, pbvh, center, view_normal, radius, use_frontface, use_projected);
+ }
+
+ if (mode & PBVH_Collapse) {
+ BM_log_entry_add_ex(pbvh->bm, pbvh->bm_log, true);
+
+ EdgeQueue q;
+ BLI_mempool *queue_pool = BLI_mempool_create(sizeof(EdgePair), 0, 128, BLI_MEMPOOL_NOP);
+
+ eq_ctx.q = &q;
+ eq_ctx.pool = queue_pool;
+
+ short_edge_queue_create(&eq_ctx,
+ pbvh,
+ center,
+ view_normal,
+ radius,
+ use_frontface,
+ use_projected,
+ mode & PBVH_LocalCollapse);
+
+#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
+
+ float brusharea = radius / (pbvh->bm_min_edge_len * 0.5f + pbvh->bm_max_edge_len * 0.5f);
+ brusharea = brusharea * brusharea * M_PI;
+
+ int max_steps;
+
+ if (custom_max_steps == 0) {
+ max_steps = (int)((float)DYNTOPO_MAX_ITER * ratio);
+ max_steps = MIN2(max_steps, DYNTOPO_MAX_ITER);
+ }
+ else {
+ max_steps = custom_max_steps;
+ }
+
+#ifdef DYNTOPO_REPORT
+ printf("collapse max_steps %d\n", max_steps);
+#endif
+
+ // printf("max_steps %d\n", max_steps);
+ pbvh_bmesh_check_nodes(pbvh);
+ modified |= pbvh_bmesh_collapse_short_edges(&eq_ctx, pbvh, &deleted_faces, max_steps);
+ pbvh_bmesh_check_nodes(pbvh);
+
+ BLI_heapsimple_free(q.heap, NULL);
+ if (q.elems) {
+ MEM_freeN(q.elems);
+ }
+ BLI_mempool_destroy(queue_pool);
+ }
+
+#ifdef DEFRAGMENT_MEMORY
+ int dcount = 0;
+ const int dmax = 55;
+
+ RNG *rng = BLI_rng_new(rseed++);
+ SwapData swapdata = {.pbvh = pbvh};
+ for (int n = 0; n < pbvh->totnode; n++) {
+ PBVHNode *node = pbvh->nodes + n;
+
+ if (dcount > dmax) {
+ break;
+ }
+
+ if (!(node->flag & PBVH_Leaf) || !(node->flag & PBVH_UpdateTopology)) {
+ continue;
+ }
+
+ BMVert *v;
+
+ float radius_sqr = radius * radius;
+
+ TGSET_ITER (v, node->bm_unique_verts) {
+ if (len_squared_v3v3(v->co, center) > radius_sqr) {
+ continue;
+ }
+
+ MDynTopoVert *mv = BKE_PBVH_DYNVERT(pbvh->cd_dyn_vert, v);
+ mv->flag |= DYNVERT_NEED_BOUNDARY | DYNVERT_NEED_VALENCE | DYNVERT_NEED_DISK_SORT;
+
+ if (BLI_rng_get_double(rng) > 0.5f) {
+ continue;
+ }
+
+ if (BM_defragment_vertex(pbvh->bm, v, rng, on_vert_swap, &swapdata)) {
+ modified = true;
+ dcount++;
+ }
+
+ if (dcount > dmax) {
+ break;
+ }
+ }
+ TGSET_ITER_END;
+ }
+
+ BLI_rng_free(rng);
+#endif
+
+ if (modified) {
+
+#ifdef PROXY_ADVANCED
+ for (int i = 0; i < pbvh->totnode; i++) {
+ PBVHNode *node = pbvh->nodes + i;
+
+ // ensure proxyvert arrays are rebuilt
+ if (node->flag & PBVH_Leaf) {
+ BKE_pbvh_free_proxyarray(pbvh, node);
+ }
+ }
+#endif
+
+ // 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)) {
+
+ node->flag &= ~PBVH_UpdateTopology;
+
+ /* Recursively split nodes that have gotten too many
+ * elements */
+ if (updatePBVH) {
+ pbvh_bmesh_node_limit_ensure(pbvh, i);
+ }
+ }
+ }
+ }
+ else { // still unmark nodes
+ for (int i = 0; i < pbvh->totnode; i++) {
+ PBVHNode *node = pbvh->nodes + i;
+
+ if ((node->flag & PBVH_Leaf) && (node->flag & PBVH_UpdateTopology)) {
+ node->flag &= ~PBVH_UpdateTopology;
+ }
+ }
+ }
+
+ MEM_SAFE_FREE(eq_ctx.val34_verts);
+
+ 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);
+ }
+ }
+
+ return modified;
+}
+
+#ifdef USE_NEW_SPLIT
+# define SPLIT_TAG BM_ELEM_TAG_ALT
+
+/*
+#generate shifted and mirrored patterns
+
+table = [
+ [4, 3, -1, -1, -1],
+ [5, -1, 3, -1, 4, -1],
+ [6, -1, 3, -1, 5, -1, 1, -1]
+]
+
+table2 = {}
+
+def getmask(row):
+ mask = 0
+ for i in range(len(row)):
+ if row[i] >= 0:
+ mask |= 1 << i
+ return mask
+
+for row in table:
+ #table2.append(row)
+
+ 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 >= 0:
+ j2 = (j2 + i) % n
+ row2.append(j2)
+
+ mask = getmask(row2)
+ if mask not in table2:
+ table2[mask] = [n] + row2
+
+ 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):
+ if k not in table2:
+ buf += ' {-1},\n'
+ continue
+
+ buf += ' {'
+ row = table2[k]
+ for j in range(len(row)):
+ if j > 0:
+ buf += ", "
+ buf += str(row[j])
+ buf += '},\n'
+buf += '};\n'
+print(buf)
+
+*/
+
+static const int splitmap[43][16] = {
+ //{numverts, vert_connections...}
+ {-1}, // 0
+ {4, 2, -1, -1, -1}, // 1
+ {4, -1, 3, -1, -1}, // 2
+ {-1}, // 3
+ {4, -1, -1, 0, -1}, // 4
+ {5, 2, -1, 4, -1, -1}, // 5
+ {-1}, // 6
+ {-1}, // 7
+ {4, -1, -1, -1, 1}, // 8
+ {5, 2, -1, -1, 0, -1}, // 9
+ {5, -1, 3, -1, 0, -1}, // 10
+ {-1}, // 11
+ {-1}, // 12
+ {-1}, // 13
+ {-1}, // 14
+ {-1}, // 15
+ {-1}, // 16
+ {-1}, // 17
+ {5, -1, 3, -1, -1, 1}, // 18
+ {-1}, // 19
+ {5, -1, -1, 4, -1, 1}, // 20
+ {6, 2, -1, 4, -1, 0, -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, -1}, // 42
+};
+
+static void pbvh_split_edges(EdgeQueueContext *eq_ctx,
+ PBVH *pbvh,
+ BMesh *bm,
+ BMEdge **edges1,
+ int totedge,
+ bool ignore_isolated_edges)
+{
+ BMEdge **edges = edges1;
+ BMFace **faces = NULL;
+ BLI_array_declare(faces);
+
+ bm_log_message(" == split edges == ");
+
+//# define EXPAND_SPLIT_REGION
+# ifdef EXPAND_SPLIT_REGION
+ BLI_array_declare(edges);
+
+ edges = MEM_callocN(sizeof(void *) * totedge, "edges copy");
+ memcpy(edges, edges1, sizeof(void *) * totedge);
+
+ BLI_array_len_set(edges, totedge);
+
+ GSet *visit = BLI_gset_ptr_new("visit");
+ for (int i = 0; i < totedge; i++) {
+ BLI_gset_add(visit, edges[i]);
+ }
+
+ for (int i = 0; i < totedge; i++) {
+ BMEdge *e = edges[i];
+ BMLoop *l = e->l;
+
+ if (!l) {
+ continue;
+ }
+
+ do {
+ BMLoop *l2 = l->f->l_first;
+ do {
+ if (!BLI_gset_haskey(visit, l2->e)) {
+ // BLI_gset()
+ BLI_gset_add(visit, l2->e);
+ l2->e->head.hflag |= SPLIT_TAG;
+ BLI_array_append(edges, l2->e);
+ }
+ } while ((l2 = l2->next) != l->f->l_first);
+ } while ((l = l->radial_next) != e->l);
+ }
+
+ BLI_gset_free(visit, NULL);
+ totedge = BLI_array_len(edges);
+# endif
+
+ 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];
+ BMLoop *l = e->l;
+
+# if 0
+ int ni = BM_ELEM_CD_GET_INT(e->v1, pbvh->cd_vert_node_offset);
+ if (ni >= 0) {
+ PBVHNode *node = pbvh->nodes + ni;
+
+ BLI_table_gset_remove(node->bm_unique_verts, e->v1, NULL);
+ BM_ELEM_CD_SET_INT(e->v1, pbvh->cd_vert_node_offset, DYNTOPO_NODE_NONE);
+ }
+
+ ni = BM_ELEM_CD_GET_INT(e->v2, pbvh->cd_vert_node_offset);
+ if (ni >= 0) {
+ PBVHNode *node = pbvh->nodes + ni;
+
+ BLI_table_gset_remove(node->bm_unique_verts, e->v2, NULL);
+ BM_ELEM_CD_SET_INT(e->v2, pbvh->cd_vert_node_offset, DYNTOPO_NODE_NONE);
+ }
+# endif
+
+ check_vert_fan_are_tris(pbvh, e->v1);
+ check_vert_fan_are_tris(pbvh, e->v2);
+
+ if (!l) {
+ continue;
+ }
+
+ int _i = 0;
+
+ do {
+ if (!l) {
+ printf("error 1 %s\n", __func__);
+ break;
+ }
+
+ BMLoop *l2 = l->f->l_first;
+ int _j = 0;
+
+ do {
+ if (!l2->e) {
+ printf("error2 %s\n", __func__);
+ break;
+ }
+
+ l2->e->head.hflag &= ~SPLIT_TAG;
+ l2->v->head.hflag &= ~SPLIT_TAG;
+
+ MDynTopoVert *mv = BKE_PBVH_DYNVERT(pbvh->cd_dyn_vert, l2->v);
+ mv->flag |= DYNVERT_NEED_VALENCE | DYNVERT_NEED_BOUNDARY | DYNVERT_NEED_DISK_SORT;
+
+ if (_j > 10000) {
+ printf("infinite loop error 1\n");
+ fix_mesh(pbvh, pbvh->bm);
+ return;
+ }
+ } while ((l2 = l2->next) != l->f->l_first);
+
+ if (_i++ > 1000) {
+ printf("infinite loop error 2\n");
+ fix_mesh(pbvh, pbvh->bm);
+ return;
+ }
+
+ l->f->head.hflag &= ~SPLIT_TAG;
+ } while ((l = l->radial_next) != e->l);
+ }
+
+ 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)) {
+ l->f->head.hflag |= SPLIT_TAG;
+ BLI_array_append(faces, l->f);
+ }
+
+ } while ((l = l->radial_next) != e->l);
+ }
+
+ int totface = BLI_array_len(faces);
+ for (int i = 0; i < totface; i++) {
+ BMFace *f = faces[i];
+ BMLoop *l = f->l_first;
+
+ // pbvh_bmesh_face_remove(pbvh, f, true, false, false);
+ if (!ignore_isolated_edges) {
+ f->head.hflag |= SPLIT_TAG;
+ BM_log_face_removed(pbvh->bm_log, f);
+ }
+ 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];
+ BMVert *v1 = e->v1;
+ BMVert *v2 = e->v2;
+ BMEdge *newe = NULL;
+
+ if (!(e->head.hflag & SPLIT_TAG)) {
+ // printf("error split\n");
+ continue;
+ }
+
+ 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);
+
+ 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->bm_log, l->f);
+ }
+ } while ((l = l->radial_next) != e->l);
+ }
+
+ e->head.hflag &= ~SPLIT_TAG;
+
+ MDynTopoVert *mv1 = BKE_PBVH_DYNVERT(pbvh->cd_dyn_vert, e->v1);
+ MDynTopoVert *mv2 = BKE_PBVH_DYNVERT(pbvh->cd_dyn_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->bm, e, true, true);
+
+ BMVert *newv = BM_log_edge_split_do(pbvh->bm_log, e, e->v1, &newe, 0.5f);
+
+ validate_edge(pbvh, pbvh->bm, e, true, true);
+ validate_edge(pbvh, pbvh->bm, newe, true, true);
+ validate_vert(pbvh, pbvh->bm, newv, true, true);
+
+ MDynTopoVert *mv = BKE_PBVH_DYNVERT(pbvh->cd_dyn_vert, newv);
+
+ newv->head.hflag |= SPLIT_TAG;
+ mv->flag |= DYNVERT_NEED_VALENCE | DYNVERT_NEED_BOUNDARY | DYNVERT_NEED_DISK_SORT;
+ mv->stroke_id = pbvh->stroke_id;
+
+ mv->flag |= DYNVERT_NEED_DISK_SORT | DYNVERT_NEED_VALENCE | DYNVERT_NEED_BOUNDARY;
+ mv->flag &= ~DYNVERT_VALENCE_TEMP;
+
+ 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);
+ }
+
+ // this should never happen
+ if (true || ni == DYNTOPO_NODE_NONE) {
+ BMIter fiter;
+ BMFace *f;
+
+ for (int j = 0; j < 3; j++) {
+ BMVert *v = NULL;
+
+ switch (j) {
+ case 0:
+ v = newv;
+ break;
+ case 1:
+ v = v1;
+ break;
+ case 2:
+ v = v2;
+ break;
+ }
+
+ BM_ITER_ELEM (f, &fiter, v, BM_FACES_OF_VERT) {
+ int ni2 = BM_ELEM_CD_GET_INT(f, pbvh->cd_face_node_offset);
+
+ if (ni2 != DYNTOPO_NODE_NONE) {
+ ni = ni2;
+ break;
+ }
+ }
+
+ if (ni != DYNTOPO_NODE_NONE) {
+ break;
+ }
+ }
+ }
+
+ 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 |= node_updateflag;
+
+ BLI_table_gset_add(node->bm_unique_verts, newv);
+ BMIter iter;
+ BMFace *f;
+
+ 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("eek!");
+ }
+ }
+
+ bm_log_message(" == split edges (triangulate) == ");
+
+ BMVert **vs = NULL;
+ BLI_array_staticdeclare(vs, 32);
+
+ BMFace **newfaces = NULL;
+ BLI_array_declare(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);
+
+ BMLoop *l = f->l_first;
+ int j = 0;
+ do {
+ if (l->v->head.hflag & SPLIT_TAG) {
+ mask |= 1 << j;
+ }
+ j++;
+ } while ((l = l->next) != f->l_first);
+
+ int flen = j;
+
+ if (mask >= ARRAY_SIZE(splitmap)) {
+ printf("splitmap error!\n");
+ 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;
+
+ BLI_array_clear(vs);
+ BLI_array_reserve(vs, 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;
+ }
+
+ BLI_array_reserve(newfaces, 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 = NULL, *l2 = NULL;
+ BMLoop *rl = NULL;
+
+ 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;
+ }
+
+ bool log_edge = !BM_edge_exists(v1, v2);
+
+ BMFace *newf = BM_face_split(bm, f2, l1, l2, &rl, NULL, false);
+ if (newf) {
+ 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(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 |= 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("error!\n");
+ }
+ f2 = newf;
+ }
+ else {
+ printf("error!\n");
+ 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);
+ }
+ else {
+ BMFace *f = newfaces[j];
+
+ if (f->len != 3) {
+ printf("eek! f->len was not 3! len: %d\n", f->len);
+ }
+ }
+
+ BM_log_face_added(pbvh->bm_log, newfaces[j]);
+ }
+
+ 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(pbvh->bm_log, f);
+ }
+
+ BLI_array_free(vs);
+ BLI_array_free(newfaces);
+ BLI_array_free(faces);
+
+# ifdef EXPAND_SPLIT_REGION
+ if (edges != edges1) {
+ BLI_array_free(edges);
+ }
+# endif
+}
+#endif
diff --git a/source/blender/blenkernel/intern/idprop_utils.c b/source/blender/blenkernel/intern/idprop_utils.c
index 0cc212e1880..e427de44ce5 100644
--- a/source/blender/blenkernel/intern/idprop_utils.c
+++ b/source/blender/blenkernel/intern/idprop_utils.c
@@ -184,7 +184,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 60b6d7ad66d..3b4d7845ad7 100644
--- a/source/blender/blenkernel/intern/lib_id.c
+++ b/source/blender/blenkernel/intern/lib_id.c
@@ -674,7 +674,7 @@ ID *BKE_id_copy(Main *bmain, const ID *id)
* Invokes the appropriate copy method for the block and returns the result in
* newid, unless test. Returns true if the block can be copied.
*/
-ID *BKE_id_copy_for_duplicate(Main *bmain, ID *id, const eDupli_ID_Flags duplicate_flags)
+ID *BKE_id_copy_for_duplicate(Main *bmain, ID *id, const uint duplicate_flags)
{
if (id == NULL) {
return id;
diff --git a/source/blender/blenkernel/intern/mesh.c b/source/blender/blenkernel/intern/mesh.c
index d631993c4e8..01503708bf7 100644
--- a/source/blender/blenkernel/intern/mesh.c
+++ b/source/blender/blenkernel/intern/mesh.c
@@ -1123,14 +1123,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;
}
@@ -1140,7 +1141,8 @@ BMesh *BKE_mesh_to_bmesh(Mesh *me,
const bool add_key_index,
const struct BMeshCreateParams *params)
{
- return BKE_mesh_to_bmesh_ex(me,
+ return BKE_mesh_to_bmesh_ex(ob,
+ me,
params,
&(struct BMeshFromMeshParams){
.calc_face_normal = false,
@@ -1156,8 +1158,13 @@ Mesh *BKE_mesh_from_bmesh_nomain(BMesh *bm,
{
BLI_assert(params->calc_object_remap == false);
Mesh *mesh = BKE_id_new_nomain(ID_ME, NULL);
- BM_mesh_bm_to_me(NULL, bm, mesh, params);
- BKE_mesh_copy_parameters_for_eval(mesh, me_settings);
+
+ BM_mesh_bm_to_me(NULL, NULL, bm, mesh, params);
+
+ 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 e04f7e178ea..4d3562c22f1 100644
--- a/source/blender/blenkernel/intern/mesh_fair.cc
+++ b/source/blender/blenkernel/intern/mesh_fair.cc
@@ -216,11 +216,14 @@ class MeshFairingContext : public FairingContext {
mloop_ = mesh->mloop;
BKE_mesh_vert_loop_map_create(&vlmap_,
&vlmap_mem_,
+ mesh->mvert,
+ mesh->medge,
mesh->mpoly,
mesh->mloop,
mesh->totvert,
mesh->totpoly,
- mesh->totloop);
+ mesh->totloop,
+ false);
BKE_mesh_edge_loop_map_create(&elmap_,
&elmap_mem_,
diff --git a/source/blender/blenkernel/intern/mesh_mapping.c b/source/blender/blenkernel/intern/mesh_mapping.c
index d28bb9c0744..67bb14a6212 100644
--- a/source/blender/blenkernel/intern/mesh_mapping.c
+++ b/source/blender/blenkernel/intern/mesh_mapping.c
@@ -26,12 +26,16 @@
#include "DNA_meshdata_types.h"
#include "DNA_vec_types.h"
+#include "BLI_alloca.h"
#include "BLI_bitmap.h"
#include "BLI_buffer.h"
#include "BLI_math.h"
+#include "BLI_sort.h"
+#include "BLI_sort_utils.h"
#include "BLI_utildefines.h"
#include "BKE_customdata.h"
+#include "BKE_mesh.h"
#include "BKE_mesh_mapping.h"
#include "BLI_memarena.h"
@@ -194,6 +198,270 @@ 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 = NULL;
+ {
+ 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];
+ sub_v3_v3v3(dir_a, co_a, center);
+ normalize_v3(dir_a);
+
+ const float *co_b = NULL;
+ 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);
+
+ const float *co_a_opposite = NULL;
+ const float *co_b_opposite = NULL;
+
+ {
+ 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 != NULL) {
+ copy_v3_v3(r_center, center);
+ }
+ if (r_index_tangent != NULL) {
+ *r_index_tangent = co_a_index;
+ }
+}
+
+static bool build_disk_cycle_face(const MPoly *mpoly,
+ const MLoop *mloop,
+ const MEdge *medge,
+ const MVert *mvert,
+ 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];
+ unsigned int loops[2];
+
+ if (poly_get_adj_loops_from_vert(mp, mloop, (unsigned int)vertex_i, loops)) {
+ for (int j = 0; j < 2; j++) {
+ if (loops[j] != (unsigned int)vertex_i) {
+ bool ok = true;
+
+ for (int k = 0; k < *donelen; k++) {
+ if ((unsigned int)doneset[k] == loops[j]) {
+ ok = false;
+ }
+ }
+
+ if (ok) {
+ doneset[*donelen] = (int)loops[j];
+ sortdata[*donelen].elem = elem->indices[i];
+ sortdata[*donelen].co = mvert[loops[j]].co;
+ (*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 MVert *mvert,
+ 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 = mvert[v].co;
+ 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 MVert *mvert,
+ 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 = mvert[v].co;
+ 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 MVert *mvert,
+ int vertex_i,
+ MeshElemMap *elem,
+ bool is_loops,
+ bool is_edges)
+{
+ DiskCycleSortData *sortdata = BLI_array_alloca(sortdata, (unsigned int)elem->count);
+ int *doneset = BLI_array_alloca(doneset, (unsigned int)elem->count);
+ int donelen = 0;
+
+ if (is_loops) {
+ if (!build_disk_cycle_face(
+ mpoly, mloop, medge, mvert, vertex_i, elem, doneset, &donelen, sortdata)) {
+ return false;
+ }
+ }
+ else if (is_edges) {
+ if (!build_disk_cycle_edge(
+ mpoly, mloop, medge, mvert, vertex_i, elem, doneset, &donelen, sortdata)) {
+ return false;
+ }
+ }
+ else {
+ if (!build_disk_cycle_loop(
+ mpoly, mloop, medge, mvert, 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
@@ -203,12 +471,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 MVert *mvert,
+ 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_callocN(sizeof(MeshElemMap) * (size_t)totvert, __func__);
int *indices, *index_iter;
@@ -246,6 +517,12 @@ 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, mvert, i, map + i, do_loops, false);
+ }
+ }
+
*r_map = map;
*r_mem = indices;
}
@@ -257,13 +534,26 @@ static void mesh_vert_poly_or_loop_map_create(MeshElemMap **r_map,
*/
void BKE_mesh_vert_poly_map_create(MeshElemMap **r_map,
int **r_mem,
+ const MVert *mvert,
+ 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,
+ mvert,
+ medge,
+ mpoly,
+ mloop,
+ totvert,
+ totpoly,
+ totloop,
+ false,
+ sort_disk_cycles);
}
/**
@@ -273,13 +563,17 @@ void BKE_mesh_vert_poly_map_create(MeshElemMap **r_map,
*/
void BKE_mesh_vert_loop_map_create(MeshElemMap **r_map,
int **r_mem,
+ const MVert *mvert,
+ 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, mvert, medge, mpoly, mloop, totvert, totpoly, totloop, true, sort_disk_cycles);
}
/**
@@ -336,8 +630,13 @@ void BKE_mesh_vert_looptri_map_create(MeshElemMap **r_map,
* 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 MEdge *medge, int totvert, int totedge)
+void BKE_mesh_vert_edge_map_create(MeshElemMap **r_map,
+ int **r_mem,
+ const MVert *mvert,
+ const MEdge *medge,
+ int totvert,
+ int totedge,
+ bool sort_disk_cycles)
{
MeshElemMap *map = MEM_callocN(sizeof(MeshElemMap) * (size_t)totvert, "vert-edge map");
int *indices = MEM_mallocN(sizeof(int[2]) * (size_t)totedge, "vert-edge map mem");
@@ -371,6 +670,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(NULL, NULL, medge, mvert, i, map + i, false, true);
+ }
+ }
+
*r_map = map;
*r_mem = indices;
}
diff --git a/source/blender/blenkernel/intern/mesh_merge.c b/source/blender/blenkernel/intern/mesh_merge.c
index d3d835378ca..b4d49c7d73d 100644
--- a/source/blender/blenkernel/intern/mesh_merge.c
+++ b/source/blender/blenkernel/intern/mesh_merge.c
@@ -379,8 +379,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, mesh->mpoly, mesh->mloop, totvert, totpoly, totloop);
+ BKE_mesh_vert_poly_map_create(&poly_map,
+ &poly_map_mem,
+ mesh->mvert,
+ mesh->medge,
+ mesh->mpoly,
+ mesh->mloop,
+ totvert,
+ totpoly,
+ totloop,
+ false);
} /* done preparing for fast poly compare */
mp = mesh->mpoly;
diff --git a/source/blender/blenkernel/intern/mesh_mirror.c b/source/blender/blenkernel/intern/mesh_mirror.c
index b20d81e7b9c..6246fb27683 100644
--- a/source/blender/blenkernel/intern/mesh_mirror.c
+++ b/source/blender/blenkernel/intern/mesh_mirror.c
@@ -41,7 +41,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],
@@ -58,7 +59,8 @@ Mesh *BKE_mesh_mirror_bisect_on_mirror_plane_for_modifier(MirrorModifierData *mm
BMIter viter;
BMVert *v, *v_next;
- bm = BKE_mesh_to_bmesh_ex(mesh,
+ bm = BKE_mesh_to_bmesh_ex(ob,
+ mesh,
&(struct BMeshCreateParams){0},
&(struct BMeshFromMeshParams){
.calc_face_normal = true,
@@ -102,7 +104,8 @@ void BKE_mesh_mirror_apply_mirror_on_axis(struct Main *bmain,
const int axis,
const float dist)
{
- BMesh *bm = BKE_mesh_to_bmesh_ex(mesh,
+ BMesh *bm = BKE_mesh_to_bmesh_ex(NULL,
+ mesh,
&(struct BMeshCreateParams){
.use_toolflags = 1,
},
@@ -121,6 +124,7 @@ void BKE_mesh_mirror_apply_mirror_on_axis(struct Main *bmain,
true);
BM_mesh_bm_to_me(bmain,
+ NULL,
bm,
mesh,
(&(struct BMeshToMeshParams){
@@ -207,7 +211,7 @@ Mesh *BKE_mesh_mirror_apply_mirror_on_axis_for_modifier(MirrorModifierData *mmd,
Mesh *mesh_bisect = NULL;
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.c b/source/blender/blenkernel/intern/mesh_remap.c
index 53a31cbbc7a..b0d044043b6 100644
--- a/source/blender/blenkernel/intern/mesh_remap.c
+++ b/source/blender/blenkernel/intern/mesh_remap.c
@@ -773,9 +773,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;
@@ -1431,19 +1433,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,
+ verts_src,
+ 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,
+ verts_src,
+ 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 9651e2d2f01..233f54828ff 100644
--- a/source/blender/blenkernel/intern/mesh_remesh_voxel.cc
+++ b/source/blender/blenkernel/intern/mesh_remesh_voxel.cc
@@ -44,6 +44,7 @@
#include "BKE_editmesh.h"
#include "BKE_lib_id.h"
#include "BKE_mesh.h"
+#include "BKE_mesh_mapping.h"
#include "BKE_mesh_remesh_voxel.h" /* own include */
#include "BKE_mesh_runtime.h"
#include "BKE_paint.h"
@@ -67,17 +68,28 @@ using blender::MutableSpan;
using blender::Span;
#ifdef WITH_QUADRIFLOW
-static Mesh *remesh_quadriflow(const Mesh *input_mesh,
- int target_faces,
- int seed,
- bool preserve_sharp,
- bool preserve_boundary,
- bool adaptive_scale,
- void (*update_cb)(void *, float progress, int *cancel),
- void *update_cb_data)
+ATTR_NO_OPT static Mesh *remesh_quadriflow(const Mesh *input_mesh,
+ int target_faces,
+ int seed,
+ bool preserve_sharp,
+ bool preserve_boundary,
+ bool adaptive_scale,
+ void (*update_cb)(void *, float progress, int *cancel),
+ void *update_cb_data)
{
/* Ensure that the triangulated mesh data is up to data */
const MLoopTri *looptri = BKE_mesh_runtime_looptri_ensure(input_mesh);
+ MeshElemMap *epmap = nullptr;
+ int *epmem = nullptr;
+
+ BKE_mesh_edge_poly_map_create(&epmap,
+ &epmem,
+ input_mesh->medge,
+ input_mesh->totedge,
+ input_mesh->mpoly,
+ input_mesh->totpoly,
+ input_mesh->mloop,
+ input_mesh->totloop);
/* Gather the required data for export to the internal quadriflow mesh format. */
MVertTri *verttri = (MVertTri *)MEM_callocN(
@@ -88,17 +100,63 @@ static Mesh *remesh_quadriflow(const Mesh *input_mesh,
const int totfaces = BKE_mesh_runtime_looptri_len(input_mesh);
const int totverts = input_mesh->totvert;
Array<float3> verts(totverts);
- Array<int> faces(totfaces * 3);
+ Array<QuadriflowFace> faces(totfaces);
for (const int i : IndexRange(totverts)) {
verts[i] = input_mesh->mvert[i].co;
}
+ int *fsets = (int *)CustomData_get_layer(&input_mesh->pdata, CD_SCULPT_FACE_SETS);
+
for (const int i : IndexRange(totfaces)) {
MVertTri &vt = verttri[i];
- faces[i * 3] = vt.tri[0];
- faces[i * 3 + 1] = vt.tri[1];
- faces[i * 3 + 2] = vt.tri[2];
+ faces[i].eflag[0] = faces[i].eflag[1] = faces[i].eflag[2] = 0;
+
+ faces[i].v[0] = vt.tri[0];
+ faces[i].v[1] = vt.tri[1];
+ faces[i].v[2] = vt.tri[2];
+
+ for (const int j : IndexRange(3)) {
+ MLoop *l = input_mesh->mloop + looptri[i].tri[j];
+ MEdge *e = input_mesh->medge + l->e;
+
+ if (e->flag & ME_SHARP) {
+ faces[i].eflag[j] |= QFLOW_CONSTRAINED;
+ continue;
+ }
+
+ MeshElemMap *melem = epmap + looptri[i].poly;
+
+ if (melem->count == 1) {
+ faces[i].eflag[j] |= QFLOW_CONSTRAINED;
+ continue;
+ }
+
+ int fset = 0;
+ int mat_nr = 0;
+
+ for (int k : IndexRange(melem->count)) {
+ MPoly *p = input_mesh->mpoly + melem->indices[k];
+
+ if (k > 0 && p->mat_nr != mat_nr) {
+ faces[i].eflag[j] |= QFLOW_CONSTRAINED;
+ continue;
+ }
+
+ mat_nr = (int)p->mat_nr;
+
+ if (fsets) {
+ int fset2 = fsets[melem->indices[k]];
+
+ if (k > 0 && abs(fset) != abs(fset2)) {
+ faces[i].eflag[j] |= QFLOW_CONSTRAINED;
+ break;
+ }
+
+ fset = fset2;
+ }
+ }
+ }
}
/* Fill out the required input data */
@@ -160,6 +218,14 @@ static Mesh *remesh_quadriflow(const Mesh *input_mesh,
MEM_freeN(qrd.out_faces);
MEM_freeN(qrd.out_verts);
+ if (epmap) {
+ MEM_freeN((void *)epmap);
+ }
+
+ if (epmem) {
+ MEM_freeN((void *)epmem);
+ }
+
return mesh;
}
#endif
@@ -516,7 +582,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;
- BM_mesh_bm_from_me(bm, mesh, &bmesh_from_mesh_params);
+ BM_mesh_bm_from_me(NULL, bm, mesh, &bmesh_from_mesh_params);
BMVert *v;
BMEdge *ed, *ed_next;
@@ -550,7 +616,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, false);
copy_v3_v3(vc->co, co);
}
}
diff --git a/source/blender/blenkernel/intern/multires.c b/source/blender/blenkernel/intern/multires.c
index eaa11a6683a..034dde18a6d 100644
--- a/source/blender/blenkernel/intern/multires.c
+++ b/source/blender/blenkernel/intern/multires.c
@@ -33,22 +33,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"
@@ -57,6 +66,8 @@
#include "DEG_depsgraph_query.h"
+#include "bmesh.h"
+#include "multires_inline.h"
#include "multires_reshape.h"
#include <math.h>
@@ -846,17 +857,461 @@ static void grid_tangent_matrix(float mat[3][3], const CCGKey *key, int x, int y
typedef struct MultiresThreadedData {
DispOp op;
+ MultiResSpace bmop;
+ BMesh *bm;
+ int lvl;
CCGElem **gridData, **subGridData;
CCGKey *key;
CCGKey *sub_key;
+ Subdiv *sd;
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;
} MultiresThreadedData;
+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 NULL;
+ }
+
+ 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;
+ char *name = "multires_dump";
+
+ bContext *ctx = CTX_create();
+ CTX_data_main_set(ctx, bmain);
+ CTX_wm_manager_set(ctx, G.main->wm.first);
+ CTX_data_scene_set(ctx, 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 != NULL) {
+ DEG_id_tag_update_ex(bmain, (ID *)ob->data, ID_RECALC_EDITORS);
+ }
+
+ ob->modifiers.first = ob->modifiers.last = NULL;
+ zero_v3(ob->loc);
+
+ printf("users: %d\n", ob->id.us);
+
+ Mesh *me = 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 = 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 NULL;
+ }
+
+ 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 ?
+ MEM_callocN(sizeof(MVert) * me->totvert, "multires_dump_grids_bmesh.vert") :
+ NULL;
+ MEdge *medge = me->totedge ?
+ MEM_callocN(sizeof(MEdge) * me->totedge, "multires_dump_grids_bmesh.edge") :
+ NULL;
+ MLoop *mloop = me->totloop ?
+ MEM_callocN(sizeof(MLoop) * me->totloop, "multires_dump_grids_bmesh.loop") :
+ NULL;
+ MPoly *mpoly = me->totpoly ?
+ MEM_callocN(sizeof(MPoly) * me->totpoly, "multires_dump_grids_bmesh.poly") :
+ NULL;
+
+ me->cd_flag = 0;
+
+ CustomData_add_layer(&me->vdata, CD_MVERT, CD_ASSIGN, mvert, me->totvert);
+ CustomData_add_layer(&me->edata, CD_MEDGE, CD_ASSIGN, medge, me->totedge);
+ CustomData_add_layer(&me->ldata, CD_MLOOP, CD_ASSIGN, mloop, me->totloop);
+ CustomData_add_layer(&me->pdata, CD_MPOLY, CD_ASSIGN, mpoly, me->totpoly);
+
+ BKE_mesh_update_customdata_pointers(me, 0);
+
+ int loopi = 0;
+ int outli = 0;
+ int medi = 0;
+
+#define VINDEX(i, j) (loopi * dimen * dimen + ((j)*dimen + (i)))
+
+ // CustomData_daata_
+ BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
+ BMLoop *l = f->l_first;
+ do {
+ MDisps *md = 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];
+
+ MVert *mv = mvert + vidx;
+ copy_v3_v3(mv->co, 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, (void *)medi);
+ e = medi;
+
+ MEdge *med = medge + medi;
+
+ med->v1 = a;
+ med->v2 = b;
+
+ medi++;
+ }
+ else {
+ e = (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);
+ BKE_mesh_tessface_calc(me);
+
+ return ob;
+}
+
+//#define LIMIT_MAX_DISPLACEMENT
+
+static void multires_bmesh_space_set_cb(void *__restrict userdata,
+ const int pidx,
+ const TaskParallelTLS *__restrict UNUSED(tls))
+{
+ MultiresThreadedData *tdata = 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 = BM_ELEM_CD_GET_VOID_P(l, cd_mdisps_off);
+ float(*dispgrid)[3] = NULL;
+
+ 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 : NULL;
+
+ if (!mmd && ob) {
+ mmd = get_multires_modifier(NULL, 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 = *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, NULL);
+
+ // create a fake object with .sculpt set to NULL
+ Object fakeob;
+ if (ob) {
+ fakeob = *ob;
+ fakeob.sculpt = NULL;
+ }
+ 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 = 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 = {
+ .bmop = mode,
+ .sd = sd,
+ .lvl = mmd->totlvl,
+ .bm = bm,
+ .cd_mdisps_off = cd_disp_off,
+ .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 UNUSED(tls))
@@ -1218,7 +1673,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);
}
}
@@ -1334,7 +1789,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);
@@ -1343,7 +1798,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.c b/source/blender/blenkernel/intern/multires_reshape.c
index bd52d70b223..9dcc9544441 100644
--- a/source/blender/blenkernel/intern/multires_reshape.c
+++ b/source/blender/blenkernel/intern/multires_reshape.c
@@ -292,6 +292,12 @@ void multiresModifier_base_apply(struct Depsgraph *depsgraph,
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.c b/source/blender/blenkernel/intern/multires_reshape_apply_base.c
index b693b1114ba..ac163497b15 100644
--- a/source/blender/blenkernel/intern/multires_reshape_apply_base.c
+++ b/source/blender/blenkernel/intern/multires_reshape_apply_base.c
@@ -87,11 +87,14 @@ void multires_reshape_apply_base_refit_base_mesh(MultiresReshapeContext *reshape
int *pmap_mem;
BKE_mesh_vert_poly_map_create(&pmap,
&pmap_mem,
+ base_mesh->mvert,
+ base_mesh->medge,
base_mesh->mpoly,
base_mesh->mloop,
base_mesh->totvert,
base_mesh->totpoly,
- base_mesh->totloop);
+ base_mesh->totloop,
+ false);
float(*origco)[3] = MEM_calloc_arrayN(
base_mesh->totvert, sizeof(float[3]), "multires apply base origco");
diff --git a/source/blender/blenkernel/intern/multires_reshape_smooth.c b/source/blender/blenkernel/intern/multires_reshape_smooth.c
index 9fb158d2f84..e0557922068 100644
--- a/source/blender/blenkernel/intern/multires_reshape_smooth.c
+++ b/source/blender/blenkernel/intern/multires_reshape_smooth.c
@@ -48,6 +48,39 @@
#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.c b/source/blender/blenkernel/intern/multires_reshape_util.c
index 8fb406e54a5..ce5dc488e67 100644
--- a/source/blender/blenkernel/intern/multires_reshape_util.c
+++ b/source/blender/blenkernel/intern/multires_reshape_util.c
@@ -315,6 +315,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.c b/source/blender/blenkernel/intern/multires_unsubdivide.c
index 501e3f27389..2b73be61259 100644
--- a/source/blender/blenkernel/intern/multires_unsubdivide.c
+++ b/source/blender/blenkernel/intern/multires_unsubdivide.c
@@ -881,7 +881,8 @@ static BMesh *get_bmesh_from_mesh(Mesh *mesh)
.use_toolflags = true,
}));
- BM_mesh_bm_from_me(bm,
+ BM_mesh_bm_from_me(NULL,
+ bm,
mesh,
(&(struct BMeshFromMeshParams){
.calc_face_normal = true,
@@ -1151,6 +1152,7 @@ bool multires_unsubdivide_to_basemesh(MultiresUnsubdivideContext *context)
/* Store the new base-mesh as a mesh in context, free bmesh. */
context->base_mesh = BKE_mesh_new_nomain(0, 0, 0, 0, 0);
BM_mesh_bm_to_me(NULL,
+ NULL,
bm_base_mesh,
context->base_mesh,
(&(struct BMeshToMeshParams){
diff --git a/source/blender/blenkernel/intern/paint.c b/source/blender/blenkernel/intern/paint.c
index 6f4c5bf03a1..827314a583e 100644
--- a/source/blender/blenkernel/intern/paint.c
+++ b/source/blender/blenkernel/intern/paint.c
@@ -51,6 +51,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"
@@ -67,6 +68,7 @@
#include "BKE_pbvh.h"
#include "BKE_subdiv_ccg.h"
#include "BKE_subsurf.h"
+#include "BKE_undo_system.h"
#include "DEG_depsgraph.h"
#include "DEG_depsgraph_query.h"
@@ -77,6 +79,15 @@
#include "bmesh.h"
+// XXX todo: figure out bad cross module refs
+void SCULPT_dynamic_topology_sync_layers(Object *ob, Mesh *me);
+void SCULPT_on_sculptsession_bmesh_free(SculptSession *ss);
+void SCULPT_dyntopo_node_layers_add(SculptSession *ss);
+BMesh *SCULPT_dyntopo_empty_bmesh();
+void SCULPT_undo_ensure_bmlog(Object *ob);
+
+static void init_mdyntopo_layer(SculptSession *ss, int totvert);
+
static void palette_init_data(ID *id)
{
Palette *palette = (Palette *)id;
@@ -1075,7 +1086,8 @@ bool BKE_paint_ensure(ToolSettings *ts, struct 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;
+ 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_callocN(sizeof(*data), __func__);
@@ -1375,20 +1387,23 @@ 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);
- }
- BM_mesh_bm_to_me(NULL,
- ss->bm,
- ob->data,
- (&(struct BMeshToMeshParams){
- .calc_object_remap = false,
- }));
+ Mesh *me = BKE_object_get_original_mesh(ob);
+
+ BM_mesh_bm_to_me(
+ NULL,
+ NULL,
+ ss->bm,
+ ob->data,
+ (&(struct BMeshToMeshParams){.calc_object_remap = false,
+ /*
+ for memfile undo steps we need to
+ save id and temporary layers
+ */
+ .copy_temp_cdlayers = true,
+ .ignore_mesh_id_layers = false,
+ .cd_mask_extra = CD_MASK_MESH_ID | CD_MASK_DYNTOPO_VERT
+
+ }));
}
}
}
@@ -1468,7 +1483,19 @@ void BKE_sculptsession_free(Object *ob)
if (ob && ob->sculpt) {
SculptSession *ss = ob->sculpt;
+ if (ss->mdyntopo_verts) {
+ MEM_freeN(ss->mdyntopo_verts);
+ ss->mdyntopo_verts = NULL;
+ }
+
+ if (ss->bm_log && BM_log_free(ss->bm_log, true)) {
+ ss->bm_log = NULL;
+ }
+
+ /*try to save current mesh*/
if (ss->bm) {
+ SCULPT_on_sculptsession_bmesh_free(ss);
+
BKE_sculptsession_bm_to_me(ob, true);
BM_mesh_free(ss->bm);
}
@@ -1484,10 +1511,6 @@ void BKE_sculptsession_free(Object *ob)
MEM_SAFE_FREE(ss->vemap);
MEM_SAFE_FREE(ss->vemap_mem);
- if (ss->bm_log) {
- BM_log_free(ss->bm_log);
- }
-
MEM_SAFE_FREE(ss->texcache);
if (ss->tex_pool) {
@@ -1612,6 +1635,12 @@ static bool sculpt_modifiers_active(Scene *scene, Sculpt *sd, Object *ob)
return false;
}
+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;
+}
+
/**
* \param need_mask: So that the evaluated mesh that is returned has mask data.
*/
@@ -1625,7 +1654,7 @@ static void sculpt_update_object(Depsgraph *depsgraph,
Scene *scene = DEG_get_input_scene(depsgraph);
Sculpt *sd = scene->toolsettings->sculpt;
SculptSession *ss = ob->sculpt;
- const Mesh *me = BKE_object_get_original_mesh(ob);
+ Mesh *me = BKE_object_get_original_mesh(ob);
MultiresModifierData *mmd = BKE_sculpt_multires_active(scene, ob);
const bool use_face_sets = (ob->mode & OB_MODE_SCULPT) != 0;
@@ -1649,6 +1678,7 @@ static void sculpt_update_object(Depsgraph *depsgraph,
}
ss->shapekey_active = (mmd == NULL) ? BKE_keyblock_from_object(ob) : NULL;
+ ss->boundary_symmetry = (int)BKE_get_fset_boundary_symflag(ob);
/* NOTE: Weight pPaint require mesh info for loop lookup, but it never uses multires code path,
* so no extra checks is needed here. */
@@ -1663,14 +1693,16 @@ static void sculpt_update_object(Depsgraph *depsgraph,
/* 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->mvert = me->mvert;
- ss->mpoly = me->mpoly;
+ ss->medge = me->medge;
ss->mloop = me->mloop;
+ ss->mpoly = me->mpoly;
}
else {
ss->totvert = me->totvert;
ss->totpoly = me->totpoly;
ss->totfaces = me->totpoly;
ss->mvert = me->mvert;
+ ss->medge = me->medge;
ss->mpoly = me->mpoly;
ss->mloop = me->mloop;
ss->multires.active = false;
@@ -1678,6 +1710,11 @@ static void sculpt_update_object(Depsgraph *depsgraph,
ss->multires.level = 0;
ss->vmask = CustomData_get_layer(&me->vdata, CD_PAINT_MASK);
ss->vcol = CustomData_get_layer(&me->vdata, CD_PROP_COLOR);
+
+ ss->vdata = &me->vdata;
+ ss->edata = &me->edata;
+ ss->ldata = &me->ldata;
+ ss->pdata = &me->pdata;
}
/* Sculpt Face Sets. */
@@ -1690,8 +1727,10 @@ static void sculpt_update_object(Depsgraph *depsgraph,
}
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);
+
BLI_assert(pbvh == ss->pbvh);
UNUSED_VARS_NDEBUG(pbvh);
@@ -1701,8 +1740,16 @@ static void sculpt_update_object(Depsgraph *depsgraph,
BKE_pbvh_face_sets_color_set(ss->pbvh, me->face_sets_color_seed, me->face_sets_color_default);
if (need_pmap && ob->type == OB_MESH && !ss->pmap) {
- BKE_mesh_vert_poly_map_create(
- &ss->pmap, &ss->pmap_mem, me->mpoly, me->mloop, me->totvert, me->totpoly, me->totloop);
+ BKE_mesh_vert_poly_map_create(&ss->pmap,
+ &ss->pmap_mem,
+ me->mvert,
+ me->medge,
+ me->mpoly,
+ me->mloop,
+ me->totvert,
+ me->totpoly,
+ me->totloop,
+ false);
}
pbvh_show_mask_set(ss->pbvh, ss->show_mask);
@@ -1797,6 +1844,7 @@ void BKE_sculpt_update_object_after_eval(Depsgraph *depsgraph, Object *ob_eval)
BLI_assert(me_eval != NULL);
sculpt_update_object(depsgraph, ob_orig, me_eval, false, false, false);
+ SCULPT_dynamic_topology_sync_layers(ob_orig, me_eval);
}
void BKE_sculpt_color_layer_create_if_needed(struct Object *object)
@@ -1813,17 +1861,19 @@ void BKE_sculpt_color_layer_create_if_needed(struct Object *object)
CustomData_add_layer(&orig_me->vdata, CD_PROP_COLOR, CD_DEFAULT, NULL, orig_me->totvert);
BKE_mesh_update_customdata_pointers(orig_me, true);
DEG_id_tag_update(&orig_me->id, ID_RECALC_GEOMETRY_ALL_MODES);
+ SCULPT_dynamic_topology_sync_layers(object, orig_me);
}
-/** \warning Expects a fully evaluated depsgraph. */
void BKE_sculpt_update_object_for_edit(
Depsgraph *depsgraph, Object *ob_orig, bool need_pmap, bool need_mask, bool need_colors)
{
- 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);
- Mesh *me_eval = BKE_object_get_evaluated_mesh(ob_eval);
- BLI_assert(me_eval != NULL);
+ Mesh *me_eval = mesh_get_eval_final(depsgraph, scene_eval, ob_eval, &CD_MASK_BAREMESH);
+
+ BLI_assert(ob_orig == DEG_get_original_object(ob_orig));
sculpt_update_object(depsgraph, ob_orig, me_eval, true, need_mask, need_colors);
}
@@ -1900,11 +1950,30 @@ void BKE_sculpt_toolsettings_data_ensure(struct 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;
}
@@ -2098,14 +2167,21 @@ void BKE_sculpt_ensure_orig_mesh_data(Scene *scene, Object *object)
static PBVH *build_pbvh_for_dynamic_topology(Object *ob)
{
PBVH *pbvh = BKE_pbvh_new();
+
+ BKE_pbvh_set_symmetry(pbvh, 0, (int)BKE_get_fset_boundary_symflag(ob));
+
BKE_pbvh_build_bmesh(pbvh,
ob->sculpt->bm,
ob->sculpt->bm_smooth_shading,
ob->sculpt->bm_log,
ob->sculpt->cd_vert_node_offset,
- ob->sculpt->cd_face_node_offset);
+ ob->sculpt->cd_face_node_offset,
+ ob->sculpt->cd_dyn_vert,
+ ob->sculpt->cd_face_areas,
+ ob->sculpt->fast_draw);
pbvh_show_mask_set(pbvh, ob->sculpt->show_mask);
pbvh_show_face_sets_set(pbvh, false);
+
return pbvh;
}
@@ -2122,17 +2198,21 @@ static PBVH *build_pbvh_from_regular_mesh(Object *ob, Mesh *me_eval_deform, bool
BKE_sculpt_sync_face_set_visibility(me, NULL);
+ BKE_sculptsession_check_mdyntopo(ob->sculpt, me->totvert);
+
BKE_pbvh_build_mesh(pbvh,
me,
me->mpoly,
me->mloop,
me->mvert,
+ ob->sculpt->mdyntopo_verts,
me->totvert,
&me->vdata,
&me->ldata,
&me->pdata,
looptri,
- looptris_num);
+ looptris_num,
+ ob->sculpt->fast_draw);
pbvh_show_mask_set(pbvh, ob->sculpt->show_mask);
pbvh_show_face_sets_set(pbvh, ob->sculpt->show_face_sets);
@@ -2164,18 +2244,50 @@ static PBVH *build_pbvh_from_ccg(Object *ob, SubdivCCG *subdiv_ccg, bool respect
&key,
(void **)subdiv_ccg->grid_faces,
subdiv_ccg->grid_flag_mats,
- subdiv_ccg->grid_hidden);
+ subdiv_ccg->grid_hidden,
+ ob->sculpt->fast_draw);
+
+ BKE_sculptsession_check_mdyntopo(ob->sculpt, BKE_pbvh_get_grid_num_vertices(pbvh));
+
pbvh_show_mask_set(pbvh, ob->sculpt->show_mask);
pbvh_show_face_sets_set(pbvh, ob->sculpt->show_face_sets);
return pbvh;
}
+bool BKE_sculptsession_check_mdyntopo(SculptSession *ss, int totvert)
+{
+ if (!ss->bm && (!ss->mdyntopo_verts || totvert != ss->mdyntopo_verts_size)) {
+ init_mdyntopo_layer(ss, totvert);
+ return true;
+ }
+
+ return false;
+}
+
+static void init_mdyntopo_layer(SculptSession *ss, int totvert)
+{
+ if (ss->mdyntopo_verts) {
+ MEM_freeN(ss->mdyntopo_verts);
+ }
+
+ ss->mdyntopo_verts = MEM_calloc_arrayN(totvert, sizeof(*ss->mdyntopo_verts), "mdyntopo_verts");
+ ss->mdyntopo_verts_size = totvert;
+
+ MDynTopoVert *mv = ss->mdyntopo_verts;
+
+ for (int i = 0; i < totvert; i++, mv++) {
+ mv->flag = DYNVERT_NEED_BOUNDARY | DYNVERT_NEED_VALENCE | DYNVERT_NEED_DISK_SORT;
+ mv->stroke_id = -1;
+ }
+}
PBVH *BKE_sculpt_object_pbvh_ensure(Depsgraph *depsgraph, Object *ob)
{
if (ob == NULL || ob->sculpt == NULL) {
return NULL;
}
+ Scene *scene = DEG_get_input_scene(depsgraph);
+
bool respect_hide = true;
if (ob->mode & (OB_MODE_VERTEX_PAINT | OB_MODE_WEIGHT_PAINT)) {
if (!(BKE_paint_select_vert_test(ob) || BKE_paint_select_face_test(ob))) {
@@ -2185,6 +2297,8 @@ PBVH *BKE_sculpt_object_pbvh_ensure(Depsgraph *depsgraph, Object *ob)
PBVH *pbvh = ob->sculpt->pbvh;
if (pbvh != NULL) {
+ 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) {
@@ -2195,14 +2309,59 @@ PBVH *BKE_sculpt_object_pbvh_ensure(Depsgraph *depsgraph, Object *ob)
BKE_sculpt_bvh_update_from_ccg(pbvh, subdiv_ccg);
}
}
+ else if (BKE_pbvh_type(pbvh) == PBVH_BMESH) {
+ Object *object_eval = DEG_get_evaluated_object(depsgraph, ob);
+
+ SCULPT_dynamic_topology_sync_layers(ob, BKE_object_get_original_mesh(ob));
+ }
return pbvh;
}
if (ob->sculpt->bm != NULL) {
/* Sculpting on a BMesh (dynamic-topology) gets a special PBVH. */
pbvh = build_pbvh_for_dynamic_topology(ob);
+
+ 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) {
+ BMesh *bm = SCULPT_dyntopo_empty_bmesh();
+
+ ob->sculpt->bm = bm;
+
+ BM_mesh_bm_from_me(NULL,
+ bm,
+ mesh_orig,
+ (&(struct BMeshFromMeshParams){.calc_face_normal = true,
+ .use_shapekey = true,
+ .active_shapekey = ob->shapenr,
+ .ignore_id_layers = false,
+ .copy_temp_cdlayers = true,
+ .cd_mask_extra = CD_MASK_DYNTOPO_VERT}));
+
+ SCULPT_dyntopo_node_layers_add(ob->sculpt);
+
+ SCULPT_undo_ensure_bmlog(ob);
+
+ pbvh = build_pbvh_for_dynamic_topology(ob);
+ }
+ else {
+ Object *object_eval = DEG_get_evaluated_object(depsgraph, ob);
+ Mesh *mesh_eval = object_eval->data;
+ if (mesh_eval->runtime.subdiv_ccg != NULL) {
+ 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 = object_eval->data;
if (mesh_eval->runtime.subdiv_ccg != NULL) {
@@ -2210,11 +2369,20 @@ 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_mdyntopo(ob->sculpt, me_eval_deform->totvert);
+
pbvh = build_pbvh_from_regular_mesh(ob, me_eval_deform, respect_hide);
}
+#endif
}
ob->sculpt->pbvh = pbvh;
+
+ if (pbvh) {
+ SCULPT_update_flat_vcol_shading(ob, scene);
+ }
+
return pbvh;
}
@@ -2236,6 +2404,12 @@ bool BKE_sculptsession_use_pbvh_draw(const Object *ob, const View3D *v3d)
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. */
const bool full_shading = (v3d && (v3d->shading.type > OB_SOLID));
diff --git a/source/blender/blenkernel/intern/pbvh.c b/source/blender/blenkernel/intern/pbvh.c
index 22092dc4fc9..ccc217764db 100644
--- a/source/blender/blenkernel/intern/pbvh.c
+++ b/source/blender/blenkernel/intern/pbvh.c
@@ -30,6 +30,8 @@
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
+#include "DNA_object_types.h"
+#include "DNA_scene_types.h"
#include "BKE_ccg.h"
#include "BKE_mesh.h" /* for BKE_mesh_calc_normals */
@@ -49,7 +51,8 @@
#include <limits.h>
-#define LEAF_LIMIT 6000
+#define LEAF_LIMIT 4000
+#define LEAF_DEPTH_LIMIT 18
//#define PERFCNTRS
@@ -78,6 +81,27 @@ 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])
{
@@ -247,6 +271,14 @@ void pbvh_grow_nodes(PBVH *pbvh, int totnode)
}
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
@@ -475,13 +507,15 @@ static bool leaf_needs_material_split(PBVH *pbvh, int offset, int count)
* 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)
+static void build_sub(
+ PBVH *pbvh, int node_index, BB *cb, BBC *prim_bbc, int offset, int count, int depth)
{
int end;
BB cb_backing;
/* Decide whether this is a leaf or not */
- const bool below_leaf_limit = count <= pbvh->leaf_limit;
+ const bool below_leaf_limit = count <= pbvh->leaf_limit || depth >= pbvh->depth_limit;
+
if (below_leaf_limit) {
if (!leaf_needs_material_split(pbvh, offset, count)) {
build_leaf(pbvh, node_index, prim_bbc, offset, count);
@@ -521,13 +555,20 @@ static void build_sub(PBVH *pbvh, int node_index, BB *cb, BBC *prim_bbc, int off
}
/* Build children */
- build_sub(pbvh, pbvh->nodes[node_index].children_offset, NULL, prim_bbc, offset, end - offset);
+ build_sub(pbvh,
+ pbvh->nodes[node_index].children_offset,
+ NULL,
+ prim_bbc,
+ offset,
+ end - offset,
+ depth + 1);
build_sub(pbvh,
pbvh->nodes[node_index].children_offset + 1,
NULL,
prim_bbc,
end,
- offset + count - end);
+ offset + count - end,
+ depth + 1);
}
static void pbvh_build(PBVH *pbvh, BB *cb, BBC *prim_bbc, int totprim)
@@ -552,7 +593,7 @@ static void pbvh_build(PBVH *pbvh, BB *cb, BBC *prim_bbc, int totprim)
}
pbvh->totnode = 1;
- build_sub(pbvh, 0, cb, prim_bbc, 0, totprim);
+ build_sub(pbvh, 0, cb, prim_bbc, 0, totprim, 0);
}
/**
@@ -566,12 +607,15 @@ void BKE_pbvh_build_mesh(PBVH *pbvh,
const MPoly *mpoly,
const MLoop *mloop,
MVert *verts,
+ MDynTopoVert *mdyntopo_verts,
+
int totvert,
struct CustomData *vdata,
struct CustomData *ldata,
struct CustomData *pdata,
const MLoopTri *looptri,
- int looptri_num)
+ int looptri_num,
+ bool fast_draw)
{
BBC *prim_bbc = NULL;
BB cb;
@@ -582,9 +626,12 @@ void BKE_pbvh_build_mesh(PBVH *pbvh,
pbvh->mloop = mloop;
pbvh->looptri = looptri;
pbvh->verts = verts;
+ pbvh->mdyntopo_verts = mdyntopo_verts;
pbvh->vert_bitmap = BLI_BITMAP_NEW(totvert, "bvh->vert_bitmap");
pbvh->totvert = totvert;
pbvh->leaf_limit = LEAF_LIMIT;
+ pbvh->depth_limit = LEAF_DEPTH_LIMIT;
+
pbvh->vdata = vdata;
pbvh->ldata = ldata;
pbvh->pdata = pdata;
@@ -617,6 +664,10 @@ void BKE_pbvh_build_mesh(PBVH *pbvh,
pbvh_build(pbvh, &cb, prim_bbc, looptri_num);
}
+ if (fast_draw) {
+ pbvh->flags |= PBVH_FAST_DRAW;
+ }
+
MEM_freeN(prim_bbc);
MEM_freeN(pbvh->vert_bitmap);
}
@@ -628,7 +679,8 @@ void BKE_pbvh_build_grids(PBVH *pbvh,
CCGKey *key,
void **gridfaces,
DMFlagMat *flagmats,
- BLI_bitmap **grid_hidden)
+ BLI_bitmap **grid_hidden,
+ bool fast_draw)
{
const int gridsize = key->grid_size;
@@ -640,6 +692,7 @@ void BKE_pbvh_build_grids(PBVH *pbvh,
pbvh->gridkey = *key;
pbvh->grid_hidden = grid_hidden;
pbvh->leaf_limit = max_ii(LEAF_LIMIT / (gridsize * gridsize), 1);
+ pbvh->depth_limit = LEAF_DEPTH_LIMIT;
BB cb;
BB_reset(&cb);
@@ -666,6 +719,10 @@ void BKE_pbvh_build_grids(PBVH *pbvh,
pbvh_build(pbvh, &cb, prim_bbc, totgrid);
}
+ if (fast_draw) {
+ pbvh->flags |= PBVH_FAST_DRAW;
+ }
+
MEM_freeN(prim_bbc);
}
@@ -682,9 +739,8 @@ void BKE_pbvh_free(PBVH *pbvh)
PBVHNode *node = &pbvh->nodes[i];
if (node->flag & PBVH_Leaf) {
- if (node->draw_buffers) {
- GPU_pbvh_buffers_free(node->draw_buffers);
- }
+ pbvh_free_all_draw_buffers(node);
+
if (node->vert_indices) {
MEM_freeN((void *)node->vert_indices);
}
@@ -692,14 +748,22 @@ void BKE_pbvh_free(PBVH *pbvh)
MEM_freeN((void *)node->face_vert_indices);
}
if (node->bm_faces) {
- BLI_gset_free(node->bm_faces, NULL);
+ BLI_table_gset_free(node->bm_faces, NULL);
}
if (node->bm_unique_verts) {
- BLI_gset_free(node->bm_unique_verts, NULL);
+ BLI_table_gset_free(node->bm_unique_verts, NULL);
}
if (node->bm_other_verts) {
- BLI_gset_free(node->bm_other_verts, NULL);
+ 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
}
}
@@ -1014,6 +1078,7 @@ typedef struct PBVHUpdateData {
float (*vnors)[3];
int flag;
bool show_sculpt_face_sets;
+ bool flat_vcol_shading;
} PBVHUpdateData;
static void pbvh_update_normals_accum_task_cb(void *__restrict userdata,
@@ -1270,6 +1335,8 @@ static void pbvh_update_draw_buffer_cb(void *__restrict userdata,
PBVHNode *node = data->nodes[n];
if (node->flag & PBVH_RebuildDrawBuffers) {
+ node->updategen++;
+
switch (pbvh->type) {
case PBVH_GRIDS:
node->draw_buffers = GPU_pbvh_grid_buffers_build(node->totprim, pbvh->grid_hidden);
@@ -1285,14 +1352,29 @@ static void pbvh_update_draw_buffer_cb(void *__restrict userdata,
node->totprim,
pbvh->mesh);
break;
- case PBVH_BMESH:
- node->draw_buffers = GPU_pbvh_bmesh_buffers_build(pbvh->flags &
- PBVH_DYNTOPO_SMOOTH_SHADING);
+ case PBVH_BMESH: {
+ BKE_pbvh_bmesh_check_tris(pbvh, node);
+
+ node->tot_mat_draw_buffers = node->tot_tri_buffers;
+
+ if (node->tot_tri_buffers) {
+ node->mat_draw_buffers = MEM_malloc_arrayN(
+ node->tot_tri_buffers, sizeof(void *), "node->mat_draw_buffers");
+ }
+
+ for (int i = 0; i < node->tot_tri_buffers; i++) {
+ node->mat_draw_buffers[i] = GPU_pbvh_bmesh_buffers_build(pbvh->flags &
+ PBVH_DYNTOPO_SMOOTH_SHADING);
+ }
+
break;
+ }
}
}
if (node->flag & PBVH_UpdateDrawBuffers) {
+ node->updategen++;
+
const int update_flags = pbvh_get_buffers_update_flags(pbvh);
switch (pbvh->type) {
case PBVH_GRIDS:
@@ -1320,44 +1402,135 @@ static void pbvh_update_draw_buffer_cb(void *__restrict userdata,
update_flags);
break;
case PBVH_BMESH:
- GPU_pbvh_bmesh_buffers_update(node->draw_buffers,
- pbvh->bm,
- node->bm_faces,
- node->bm_unique_verts,
- node->bm_other_verts,
- update_flags);
+ if (BKE_pbvh_bmesh_check_tris(pbvh, node)) {
+ pbvh_free_all_draw_buffers(node);
+ node->tot_mat_draw_buffers = node->tot_tri_buffers;
+
+ pbvh_bmesh_check_other_verts(node);
+
+ if (node->tot_tri_buffers) {
+ node->mat_draw_buffers = MEM_malloc_arrayN(
+ node->tot_tri_buffers, sizeof(void *), "node->mat_draw_buffers");
+
+ for (int i = 0; i < node->tot_tri_buffers; i++) {
+ node->mat_draw_buffers[i] = GPU_pbvh_bmesh_buffers_build(
+ pbvh->flags & PBVH_DYNTOPO_SMOOTH_SHADING);
+ }
+ }
+ }
+
+ for (int i = 0; i < node->tot_mat_draw_buffers; i++) {
+ if (i >= node->tot_tri_buffers) {
+ printf("node->tot_mat_draw_buffers >= node->tot_tri_buffers! %d %d\n",
+ node->tot_mat_draw_buffers,
+ node->tot_tri_buffers);
+ continue;
+ }
+
+ GPU_pbvh_bmesh_buffers_update(node->mat_draw_buffers[i],
+ pbvh->bm,
+ node->bm_faces,
+ node->bm_unique_verts,
+ node->bm_other_verts,
+ node->tri_buffers + i,
+ update_flags,
+ pbvh->cd_vert_node_offset,
+ pbvh->face_sets_color_seed,
+ pbvh->face_sets_color_default,
+ data->flat_vcol_shading,
+ node->tri_buffers[i].mat_nr);
+ }
break;
}
}
}
+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_all_draw_buffers(PBVHNode *node)
+{
+ if (node->draw_buffers) {
+ GPU_pbvh_buffers_free(node->draw_buffers);
+ node->draw_buffers = NULL;
+ }
+
+ for (int i = 0; i < node->tot_mat_draw_buffers; i++) {
+ GPU_pbvh_buffers_free(node->mat_draw_buffers[i]);
+ }
+
+ MEM_SAFE_FREE(node->mat_draw_buffers);
+
+ node->mat_draw_buffers = NULL;
+ node->tot_mat_draw_buffers = 0;
+}
+
+void pbvh_update_free_all_draw_buffers(PBVH *pbvh, PBVHNode *node)
+{
+ if (pbvh->type == PBVH_GRIDS) {
+ GPU_pbvh_grid_buffers_update_free(
+ node->draw_buffers, pbvh->grid_flag_mats, node->prim_indices);
+ }
+ else if (pbvh->type == PBVH_BMESH) {
+ for (int i = 0; i < node->tot_mat_draw_buffers; i++) {
+ GPU_pbvh_bmesh_buffers_update_free(node->mat_draw_buffers[i]);
+ }
+ }
+}
+
static void pbvh_update_draw_buffers(PBVH *pbvh, PBVHNode **nodes, int totnode, int update_flag)
{
+
+ CustomData *vdata;
+ CustomData *ldata;
+
+ if (pbvh->type == PBVH_BMESH) {
+ if (pbvh->bm) {
+ vdata = &pbvh->bm->vdata;
+ ldata = &pbvh->bm->ldata;
+ }
+ else {
+ vdata = ldata = NULL;
+ }
+ }
+ else {
+ vdata = pbvh->vdata;
+ ldata = pbvh->ldata;
+ }
+
+ GPU_pbvh_update_attribute_names(
+ vdata, ldata, GPU_pbvh_need_full_render_get(), pbvh->flags & PBVH_FAST_DRAW);
+
if ((update_flag & PBVH_RebuildDrawBuffers) || ELEM(pbvh->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) {
- GPU_pbvh_buffers_free(node->draw_buffers);
- node->draw_buffers = NULL;
+ pbvh_free_all_draw_buffers(node);
}
- else if ((node->flag & PBVH_UpdateDrawBuffers) && node->draw_buffers) {
- if (pbvh->type == PBVH_GRIDS) {
- GPU_pbvh_grid_buffers_update_free(
- node->draw_buffers, pbvh->grid_flag_mats, node->prim_indices);
- }
- else if (pbvh->type == PBVH_BMESH) {
- GPU_pbvh_bmesh_buffers_update_free(node->draw_buffers);
- }
+ else if ((node->flag & PBVH_UpdateDrawBuffers)) {
+ pbvh_update_free_all_draw_buffers(pbvh, node);
}
}
}
/* Parallel creation and update of draw buffers. */
PBVHUpdateData data = {
- .pbvh = pbvh,
- .nodes = nodes,
- };
+ .pbvh = pbvh, .nodes = nodes, .flat_vcol_shading = pbvh->flat_vcol_shading};
TaskParallelSettings settings;
BKE_pbvh_parallel_range_settings(&settings, true, totnode);
@@ -1368,7 +1541,13 @@ 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. */
- GPU_pbvh_buffers_update_flush(node->draw_buffers);
+ if (node->draw_buffers) {
+ GPU_pbvh_buffers_update_flush(node->draw_buffers);
+ }
+
+ for (int i = 0; i < node->tot_mat_draw_buffers; i++) {
+ GPU_pbvh_buffers_update_flush(node->mat_draw_buffers[i]);
+ }
}
node->flag &= ~(PBVH_RebuildDrawBuffers | PBVH_UpdateDrawBuffers);
@@ -1407,6 +1586,46 @@ static int pbvh_flush_bb(PBVH *pbvh, PBVHNode *node, int flag)
return update;
}
+void BKE_pbvh_update_origcolor_bmesh(PBVH *pbvh, PBVHNode *node)
+{
+ PBVHVertexIter vd;
+
+ if (!pbvh->bm || pbvh->cd_vcol_offset < 0) {
+ return;
+ }
+
+ int cd_vcol_offset = CustomData_get_offset(&pbvh->bm->vdata, CD_PROP_COLOR);
+
+ if (cd_vcol_offset == -1) {
+ return;
+ }
+
+ BKE_pbvh_vertex_iter_begin (pbvh, node, vd, PBVH_ITER_UNIQUE) {
+ MDynTopoVert *mv = BKE_PBVH_DYNVERT(pbvh->cd_dyn_vert, vd.bm_vert);
+ float *c2 = BM_ELEM_CD_GET_VOID_P(vd.bm_vert, pbvh->cd_vcol_offset);
+
+ copy_v4_v4(mv->origcolor, c2);
+ }
+ BKE_pbvh_vertex_iter_end;
+}
+
+void BKE_pbvh_update_origco_bmesh(PBVH *pbvh, PBVHNode *node)
+{
+ PBVHVertexIter vd;
+
+ if (!pbvh->bm || pbvh->cd_dyn_vert < 0) {
+ return;
+ }
+
+ BKE_pbvh_vertex_iter_begin (pbvh, node, vd, PBVH_ITER_UNIQUE) {
+ MDynTopoVert *mv = BKE_PBVH_DYNVERT(pbvh->cd_dyn_vert, vd.bm_vert);
+
+ copy_v3_v3(mv->origco, vd.bm_vert->co);
+ copy_v3_v3(mv->origno, vd.bm_vert->no);
+ }
+ BKE_pbvh_vertex_iter_end;
+}
+
void BKE_pbvh_update_bounds(PBVH *pbvh, int flag)
{
if (!pbvh->nodes) {
@@ -1509,28 +1728,28 @@ 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 = 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 = 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);
}
@@ -1732,7 +1951,7 @@ BMesh *BKE_pbvh_get_bmesh(PBVH *pbvh)
void BKE_pbvh_node_mark_update(PBVHNode *node)
{
node->flag |= PBVH_UpdateNormals | PBVH_UpdateBB | PBVH_UpdateOriginalBB |
- PBVH_UpdateDrawBuffers | PBVH_UpdateRedraw;
+ PBVH_UpdateDrawBuffers | PBVH_UpdateRedraw | PBVH_UpdateCurvatureDir;
}
void BKE_pbvh_node_mark_update_mask(PBVHNode *node)
@@ -1748,12 +1967,13 @@ void BKE_pbvh_node_mark_update_color(PBVHNode *node)
void BKE_pbvh_node_mark_update_visibility(PBVHNode *node)
{
node->flag |= PBVH_UpdateVisibility | PBVH_RebuildDrawBuffers | PBVH_UpdateDrawBuffers |
- PBVH_UpdateRedraw;
+ PBVH_UpdateRedraw | PBVH_UpdateCurvatureDir;
}
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)
@@ -1763,7 +1983,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)
@@ -1854,9 +2094,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;
@@ -1944,16 +2199,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])
-{
- *r_orco_tris = node->bm_ortri;
- *r_orco_tris_num = node->bm_tot_ortri;
- *r_orco_coords = node->bm_orco;
-}
-
/**
* \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
@@ -1982,6 +2227,7 @@ bool BKE_pbvh_node_vert_update_check_any(PBVH *pbvh, PBVHNode *node)
typedef struct {
struct IsectRayAABB_Precalc ray;
bool original;
+ int stroke_id;
} RaycastData;
static bool ray_aabb_intersect(PBVHNode *node, void *data_v)
@@ -2008,12 +2254,14 @@ 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;
BKE_pbvh_search_callback_occluded(pbvh, ray_aabb_intersect, &rcd, cb, data);
}
@@ -2190,10 +2438,12 @@ static bool pbvh_faces_node_raycast(PBVH *pbvh,
struct IsectRayPrecalc *isect_precalc,
int *hit_count,
float *depth,
+
float *depth_back,
- int *r_active_vertex_index,
- int *r_active_face_index,
- float *r_face_normal)
+ SculptVertRef *r_active_vertex_index,
+ SculptFaceRef *r_active_face_index,
+ float *r_face_normal,
+ int stroke_id)
{
const MVert *vert = pbvh->verts;
const MLoop *mloop = pbvh->mloop;
@@ -2245,8 +2495,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 = mloop[lt->tri[j]].v;
- *r_active_face_index = lt->poly;
+ *r_active_vertex_index = (SculptVertRef){.i = mloop[lt->tri[j]].v};
+ *r_active_face_index = (SculptFaceRef){.i = lt->poly};
}
}
}
@@ -2264,8 +2514,9 @@ static bool pbvh_grids_node_raycast(PBVH *pbvh,
int *hit_count,
float *depth,
float *back_depth,
- int *r_active_vertex_index,
- int *r_active_grid_index,
+
+ SculptVertRef *r_active_vertex_index,
+ SculptFaceRef *r_active_grid_index,
float *r_face_normal)
{
const int totgrid = node->totprim;
@@ -2340,13 +2591,13 @@ static bool pbvh_grids_node_raycast(PBVH *pbvh,
len_squared_v3v3(location, nearest_vertex_co)) {
copy_v3_v3(nearest_vertex_co, co[j]);
- *r_active_vertex_index = gridkey->grid_area * grid_index +
- (y + y_it[j]) * gridkey->grid_size + (x + x_it[j]);
+ r_active_vertex_index->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;
+ r_active_grid_index->i = grid_index;
}
}
}
@@ -2369,9 +2620,10 @@ bool BKE_pbvh_node_raycast(PBVH *pbvh,
int *hit_count,
float *depth,
float *back_depth,
- int *active_vertex_index,
- int *active_face_grid_index,
- float *face_normal)
+ SculptVertRef *active_vertex_index,
+ SculptFaceRef *active_face_grid_index,
+ float *face_normal,
+ int stroke_id)
{
bool hit = false;
@@ -2392,7 +2644,9 @@ bool BKE_pbvh_node_raycast(PBVH *pbvh,
back_depth,
active_vertex_index,
active_face_grid_index,
- face_normal);
+ face_normal,
+ stroke_id);
+
break;
case PBVH_GRIDS:
hit |= pbvh_grids_node_raycast(pbvh,
@@ -2409,15 +2663,21 @@ bool BKE_pbvh_node_raycast(PBVH *pbvh,
face_normal);
break;
case PBVH_BMESH:
- BM_mesh_elem_index_ensure(pbvh->bm, BM_VERT);
- hit = pbvh_bmesh_node_raycast(node,
+ // BM_mesh_elem_index_ensure(pbvh->bm, BM_VERT);
+
+ hit = pbvh_bmesh_node_raycast(pbvh,
+ node,
ray_start,
ray_normal,
isect_precalc,
+ hit_count,
depth,
+ back_depth,
use_origco,
active_vertex_index,
- face_normal);
+ active_face_grid_index,
+ face_normal,
+ stroke_id);
break;
}
@@ -2632,7 +2892,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;
@@ -2651,7 +2912,7 @@ 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;
}
@@ -2805,6 +3066,27 @@ void BKE_pbvh_draw_cb(PBVH *pbvh,
/* 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
+
+ if (pbvh->type == PBVH_BMESH && pbvh->need_full_render != GPU_pbvh_need_full_render_get()) {
+ // update all nodes
+ MEM_SAFE_FREE(nodes);
+
+ printf("Rebuilding PBVH draw buffers...\n");
+
+ for (int i = 0; i < pbvh->totnode; i++) {
+ PBVHNode *node = pbvh->nodes + i;
+
+ node->flag |= PBVH_UpdateDrawBuffers | PBVH_RebuildDrawBuffers;
+ }
+
+ pbvh->need_full_render = GPU_pbvh_need_full_render_get();
+ BKE_pbvh_draw_cb(
+ pbvh, update_only_visible, update_frustum, draw_frustum, draw_fn, user_data);
+ return;
+ }
+
pbvh_update_draw_buffers(pbvh, nodes, totnode, update_flag);
}
MEM_SAFE_FREE(nodes);
@@ -2816,7 +3098,13 @@ void BKE_pbvh_draw_cb(PBVH *pbvh,
for (int i = 0; i < totnode; i++) {
PBVHNode *node = nodes[i];
if (!(node->flag & PBVH_FullyHidden)) {
- draw_fn(user_data, node->draw_buffers);
+ if (node->draw_buffers) {
+ draw_fn(user_data, node->draw_buffers);
+ }
+
+ for (int i = 0; i < node->tot_mat_draw_buffers; i++) {
+ draw_fn(user_data, node->mat_draw_buffers[i]);
+ }
}
}
@@ -2831,7 +3119,9 @@ void BKE_pbvh_draw_debug_cb(
for (int a = 0; a < pbvh->totnode; a++) {
PBVHNode *node = &pbvh->nodes[a];
- draw_fn(user_data, node->vb.bmin, node->vb.bmax, node->flag);
+ int num = a + node->updategen;
+
+ draw_fn(&num, node->vb.bmin, node->vb.bmax, node->flag);
}
}
@@ -2947,7 +3237,7 @@ void BKE_pbvh_node_free_proxies(PBVHNode *node)
node->proxies[p].co = NULL;
}
- MEM_freeN(node->proxies);
+ MEM_SAFE_FREE(node->proxies);
node->proxies = NULL;
node->proxy_count = 0;
@@ -2984,9 +3274,23 @@ void BKE_pbvh_gather_proxies(PBVH *pbvh, PBVHNode ***r_array, int *r_tot)
PBVHColorBufferNode *BKE_pbvh_node_color_buffer_get(PBVHNode *node)
{
+ unsigned int totvert;
+
+ if (node->bm_unique_verts) {
+ totvert = BLI_table_gset_len(node->bm_unique_verts);
+ }
+ else {
+ totvert = node->uniq_verts;
+ }
+
+ if (node->color_buffer.color && node->color_buffer.size != totvert) {
+ MEM_freeN(node->color_buffer.color);
+ node->color_buffer.color = NULL;
+ }
if (!node->color_buffer.color) {
- node->color_buffer.color = MEM_callocN(sizeof(float[4]) * node->uniq_verts, "Color buffer");
+ node->color_buffer.color = MEM_callocN(sizeof(float[4]) * totvert, "Color buffer");
+ node->color_buffer.size = totvert;
}
return &node->color_buffer;
}
@@ -3012,8 +3316,11 @@ void pbvh_vertex_iter_init(PBVH *pbvh, PBVHNode *node, PBVHVertexIter *vi, int m
vi->grid = NULL;
vi->no = NULL;
+ vi->col = NULL;
vi->fno = NULL;
vi->mvert = NULL;
+ vi->vertex.i = 0;
+ vi->index = 0;
vi->respect_hide = pbvh->respect_hide;
if (pbvh->respect_hide == false) {
@@ -3041,9 +3348,21 @@ void pbvh_vertex_iter_init(PBVH *pbvh, PBVHNode *node, PBVHVertexIter *vi, int m
vi->mverts = verts;
if (pbvh->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->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->bm->vdata;
+ vi->bm_vert = NULL;
+
+ vi->cd_dyn_vert = CustomData_get_offset(vi->bm_vdata, CD_DYNTOPO_VERT);
+
+ // we ensure pbvh->cd_vcol_offset is up to date here too
+ vi->cd_vcol_offset = pbvh->cd_vcol_offset = CustomData_get_offset(vi->bm_vdata, CD_PROP_COLOR);
vi->cd_vert_mask_offset = CustomData_get_offset(vi->bm_vdata, CD_PAINT_MASK);
}
@@ -3059,8 +3378,12 @@ void pbvh_vertex_iter_init(PBVH *pbvh, PBVHNode *node, PBVHVertexIter *vi, int m
}
}
-bool pbvh_has_mask(const PBVH *pbvh)
+bool BKE_pbvh_draw_mask(const PBVH *pbvh)
{
+ if (pbvh->flags & PBVH_FAST_DRAW) {
+ return false;
+ }
+
switch (pbvh->type) {
case PBVH_GRIDS:
return (pbvh->gridkey.has_mask != 0);
@@ -3073,15 +3396,49 @@ bool pbvh_has_mask(const PBVH *pbvh)
return false;
}
-bool pbvh_has_face_sets(PBVH *pbvh)
+SculptVertRef BKE_pbvh_table_index_to_vertex(PBVH *pbvh, int idx)
+{
+ if (pbvh->type == PBVH_BMESH) {
+ SculptVertRef ref = {(intptr_t)pbvh->bm->vtable[idx]};
+ return ref;
+ }
+
+ return BKE_pbvh_make_vref(idx);
+}
+
+SculptEdgeRef BKE_pbvh_table_index_to_edge(PBVH *pbvh, int idx)
+{
+ if (pbvh->type == PBVH_BMESH) {
+ SculptEdgeRef ref = {(intptr_t)pbvh->bm->etable[idx]};
+ return ref;
+ }
+
+ return BKE_pbvh_make_eref(idx);
+}
+
+SculptFaceRef BKE_pbvh_table_index_to_face(PBVH *pbvh, int idx)
+{
+ if (pbvh->type == PBVH_BMESH) {
+ SculptFaceRef ref = {(intptr_t)pbvh->bm->ftable[idx]};
+ return ref;
+ }
+
+ return BKE_pbvh_make_fref(idx);
+}
+
+bool BKE_pbvh_draw_face_sets(PBVH *pbvh)
{
+ if (pbvh->flags & PBVH_FAST_DRAW) {
+ return false;
+ }
+
switch (pbvh->type) {
case PBVH_GRIDS:
return (pbvh->pdata && CustomData_get_layer(pbvh->pdata, CD_SCULPT_FACE_SETS));
case PBVH_FACES:
return (pbvh->pdata && CustomData_get_layer(pbvh->pdata, CD_SCULPT_FACE_SETS));
case PBVH_BMESH:
- return false;
+ return (pbvh->bm && CustomData_get_layer(&pbvh->bm->pdata, CD_SCULPT_FACE_SETS));
}
return false;
@@ -3113,12 +3470,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;
}
MVert *BKE_pbvh_get_verts(const PBVH *pbvh)
@@ -3141,3 +3499,629 @@ 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(SculptVertRef))
+ 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;
+}
+
+void BKE_pbvh_node_mark_update_tri_area(PBVHNode *node)
+{
+ node->flag |= PBVH_UpdateTriAreas;
+}
+
+void BKE_pbvh_update_all_tri_areas(PBVH *pbvh)
+{
+ 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) || !node->tribuf || !node->tribuf->tottri) {
+ return;
+ }
+
+ node->flag &= ~PBVH_UpdateTriAreas;
+
+ if (node->flag & PBVH_UpdateTris) {
+ BKE_pbvh_bmesh_check_tris(pbvh, node);
+ }
+
+ switch (BKE_pbvh_type(pbvh)) {
+ case PBVH_BMESH: {
+ BMFace *f;
+ const int cd_face_area = pbvh->cd_face_area;
+
+ TGSET_ITER (f, node->bm_faces) {
+ BM_ELEM_CD_SET_FLOAT(f, cd_face_area, 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 area = area_tri_v3(v1->co, v2->co, v3->co);
+ float farea = BM_ELEM_CD_GET_FLOAT(f, cd_face_area);
+
+ BM_ELEM_CD_SET_FLOAT(f, cd_face_area, farea + area);
+ }
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+void BKE_pbvh_get_vert_face_areas(PBVH *pbvh, SculptVertRef vertex, float *r_areas, int valence)
+{
+ if (BKE_pbvh_type(pbvh) != PBVH_BMESH) {
+ // not supported
+ for (int i = 0; i < valence; i++) {
+ r_areas[i] = 1.0f;
+ }
+
+ return;
+ }
+
+ 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 {
+ w += BM_ELEM_CD_GET_FLOAT(e->l->f, cd_face_area) * 0.5f;
+ w += BM_ELEM_CD_GET_FLOAT(e->l->radial_next->f, cd_face_area) * 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);
+}
+
+void BKE_pbvh_set_stroke_id(PBVH *pbvh, int stroke_id)
+{
+ pbvh->stroke_id = stroke_id;
+}
+
+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;
+
+ if (pbvh->bm && BKE_pbvh_type(pbvh) == PBVH_BMESH) {
+ BMIter iter;
+ BMVert *v;
+
+ BM_ITER_MESH (v, &iter, pbvh->bm, BM_VERTS_OF_MESH) {
+ MDynTopoVert *mv = BKE_PBVH_DYNVERT(pbvh->cd_dyn_vert, v);
+
+ mv->flag |= DYNVERT_NEED_BOUNDARY;
+ }
+ }
+}
diff --git a/source/blender/blenkernel/intern/pbvh_bmesh.c b/source/blender/blenkernel/intern/pbvh_bmesh.c
index c30f94a4cf6..a17465275cf 100644
--- a/source/blender/blenkernel/intern/pbvh_bmesh.c
+++ b/source/blender/blenkernel/intern/pbvh_bmesh.c
@@ -18,17 +18,49 @@
* \ingroup bli
*/
+/*
+
+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_array.h"
#include "BLI_buffer.h"
#include "BLI_ghash.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 "PIL_time.h"
+#include "atomic_ops.h"
+
+#include "DNA_material_types.h"
+
#include "BKE_DerivedMesh.h"
#include "BKE_ccg.h"
+#include "BKE_context.h"
+#include "BKE_global.h"
#include "BKE_pbvh.h"
#include "GPU_buffers.h"
@@ -36,161 +68,172 @@
#include "bmesh.h"
#include "pbvh_intern.h"
-/* Avoid skinny faces */
-#define USE_EDGEQUEUE_EVEN_SUBDIV
-#ifdef USE_EDGEQUEUE_EVEN_SUBDIV
-# include "BKE_global.h"
-#endif
-
-/* Support for only operating on front-faces */
-#define USE_EDGEQUEUE_FRONTFACE
-
-/* 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
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
-// #define USE_VERIFY
+#include <stdarg.h>
-#ifdef USE_VERIFY
-static void pbvh_bmesh_verify(PBVH *pbvh);
-#endif
+static void _debugprint(const char *fmt, ...)
+{
+ va_list args;
+ va_start(args, fmt);
+ vprintf(fmt, args);
+ va_end(args);
+}
-/* -------------------------------------------------------------------- */
-/** \name BMesh Utility API
- *
- * Use some local functions which assume triangles.
- * \{ */
+void pbvh_bmesh_check_nodes_simple(PBVH *pbvh)
+{
+#if 0
+ for (int i = 0; i < pbvh->totnode; i++) {
+ PBVHNode *node = pbvh->nodes + i;
+ BMFace *f;
-/**
- * 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!
- */
+ if (!(node->flag & PBVH_Leaf)) {
+ continue;
+ }
-#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])
-{
- e_tri[0] = BM_edge_create(bm, v_tri[0], v_tri[1], NULL, BM_CREATE_NO_DOUBLE);
- e_tri[1] = BM_edge_create(bm, v_tri[1], v_tri[2], NULL, BM_CREATE_NO_DOUBLE);
- e_tri[2] = BM_edge_create(bm, v_tri[2], v_tri[0], NULL, BM_CREATE_NO_DOUBLE);
-}
-
-BLI_INLINE void bm_face_as_array_index_tri(BMFace *f, int r_index[3])
-{
- 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);
-}
+ TGSET_ITER (f, node->bm_faces) {
+ if (!f || f->head.htype != BM_FACE) {
+ _debugprint("Corrupted (freed?) face in node->bm_faces\n");
+ continue;
+ }
-/**
- * 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).
- *
- * <pre>
- * l_radial_first->v & l_radial_first->next->v
- * +---+
- * | /
- * | /
- * + v_opposite
- * </pre>
- *
- * 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;
+ if (BM_ELEM_CD_GET_INT(f, pbvh->cd_face_node_offset) != i) {
+ _debugprint("Face in more then one node\n");
}
- } while ((l_radial_iter = l_radial_iter->radial_next) != l_radial_first);
+ }
+ TGSET_ITER_END;
}
- return NULL;
+#endif
}
-/**
- * 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)
+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 == NULL) {
- /* Not remapped. */
- return v;
+#if 0
+ 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 == NULL) {
- /* removed and not remapped */
- return NULL;
+ }
+
+ BMVert *v;
+ BMIter iter;
+
+ BM_ITER_MESH (v, &iter, pbvh->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\n");
+ BM_ELEM_CD_SET_INT(v, pbvh->cd_vert_node_offset, DYNTOPO_NODE_NONE);
+ }
+
+ if (ni < -1 || ni >= pbvh->totnode) {
+ _debugprint("vert node ref was invalid");
+ continue;
+ }
+
+ if (ni == -1) {
+ continue;
}
- /* remapped */
- v = *v_next_p;
+ 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");
+ }
+
+ MDynTopoVert *mv = BKE_PBVH_DYNVERT(pbvh->cd_dyn_vert, v);
+ BKE_pbvh_bmesh_check_valence(pbvh, (SculptVertRef){.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;
+ }
+#endif
+}
+
+void pbvh_bmesh_pbvh_bmesh_check_nodes(PBVH *pbvh)
+{
+ pbvh_bmesh_check_nodes(pbvh);
}
/** \} */
@@ -201,21 +244,22 @@ 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;
/* 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);
+ BMFace *f;
- GSET_ITER (gs_iter, n->bm_faces) {
- BMFace *f = BLI_gsetIterator_getKey(&gs_iter);
-
+ TGSET_ITER (f, n->bm_faces) {
/* Update ownership of faces */
BM_ELEM_CD_SET_INT(f, cd_face_node_offset, node_index);
@@ -225,12 +269,12 @@ static void pbvh_bmesh_node_finalize(PBVH *pbvh,
do {
BMVert *v = l_iter->v;
- if (!BLI_gset_haskey(n->bm_unique_verts, v)) {
+ 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);
}
}
@@ -242,6 +286,7 @@ 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]);
@@ -252,37 +297,51 @@ static void pbvh_bmesh_node_finalize(PBVH *pbvh,
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_UpdateTopology | PBVH_UpdateCurvatureDir | PBVH_UpdateTris;
+
+ if (add_orco) {
+ BKE_pbvh_bmesh_check_tris(pbvh, n);
+ }
}
/* 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 (depth > 6 || 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 = 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;
@@ -293,83 +352,113 @@ 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->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 = NULL;
/* Partition the parent node's faces between the two children */
- GSET_ITER (gs_iter, n->bm_faces) {
- BMFace *f = 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);
}
}
-
+ TGSET_ITER_END
+#if 0
/* Enforce at least one primitive in each node */
- GSet *empty = NULL, *other;
- if (BLI_gset_len(c1->bm_faces) == 0) {
+ TableGSet *empty = NULL, *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, NULL);
+ void *key;
+ TGSET_ITER (key, other) {
+ BLI_table_gset_insert(empty, key);
+ BLI_table_gset_remove(other, key, NULL);
break;
}
+ TGSET_ITER_END
}
-
+#endif
/* Clear this node */
- /* Mark this node's unique verts as unclaimed */
+ BMVert *v;
+ TableGSet *bm_unique_verts = n->bm_unique_verts;
+
+ /* 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 = 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) {
+ int ni;
+
+ 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, NULL);
+ TGSET_ITER_END
+
+ BLI_table_gset_free(n->bm_unique_verts, NULL);
}
- /* Unclaim faces */
- GSET_ITER (gs_iter, n->bm_faces) {
- BMFace *f = 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, NULL);
}
- BLI_gset_free(n->bm_faces, NULL);
if (n->bm_other_verts) {
- BLI_gset_free(n->bm_other_verts, NULL);
+ BLI_table_gset_free(n->bm_other_verts, NULL);
}
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 = NULL;
n->bm_unique_verts = NULL;
n->bm_other_verts = NULL;
n->layer_disp = NULL;
- if (n->draw_buffers) {
- GPU_pbvh_buffers_free(n->draw_buffers);
- n->draw_buffers = NULL;
- }
+ pbvh_free_all_draw_buffers(n);
+
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];
@@ -382,10 +471,13 @@ 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);
+ 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) {
/* Node limit not exceeded */
return false;
@@ -394,10 +486,20 @@ static bool pbvh_bmesh_node_limit_ensure(PBVH *pbvh, int node_index)
/* For each BMFace, store the AABB and AABB centroid */
BBC *bbc_array = MEM_mallocN(sizeof(BBC) * bm_faces_size, "BBC");
- GSetIterator gs_iter;
+ BMFace *f;
+
int i;
- GSET_ITER_INDEX (gs_iter, bm_faces, i) {
- BMFace *f = 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);
@@ -411,1269 +513,657 @@ 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->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)
-{
- 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;
- }
-}
-
-static int pbvh_bmesh_node_index_from_elem(PBVH *pbvh, void *key)
-{
- 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);
-
- BLI_assert(node_index != DYNTOPO_NODE_NONE);
- BLI_assert(node_index < pbvh->totnode);
- (void)pbvh;
-
- return node_index;
-}
-
-static PBVHNode *pbvh_bmesh_node_from_elem(PBVH *pbvh, void *key)
-{
- return &pbvh->nodes[pbvh_bmesh_node_index_from_elem(pbvh, key)];
-}
-
-/* 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
-
-BLI_INLINE int pbvh_bmesh_node_index_from_vert(PBVH *pbvh, const BMVert *key)
+static bool point_in_node(const PBVHNode *node, const float co[3])
{
- 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;
+ 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];
}
-BLI_INLINE int pbvh_bmesh_node_index_from_face(PBVH *pbvh, const BMFace *key)
+void bke_pbvh_insert_face_finalize(PBVH *pbvh, BMFace *f, const int ni)
{
- 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;
-}
+ PBVHNode *node = pbvh->nodes + ni;
+ BM_ELEM_CD_SET_INT(f, pbvh->cd_face_node_offset, ni);
-BLI_INLINE PBVHNode *pbvh_bmesh_node_from_vert(PBVH *pbvh, const BMVert *key)
-{
- return &pbvh->nodes[pbvh_bmesh_node_index_from_vert(pbvh, key)];
-}
+ if (!(node->flag & PBVH_Leaf)) {
+ printf("major pbvh corruption error");
+ return;
+ }
-BLI_INLINE PBVHNode *pbvh_bmesh_node_from_face(PBVH *pbvh, const BMFace *key)
-{
- return &pbvh->nodes[pbvh_bmesh_node_index_from_face(pbvh, key)];
-}
+ BLI_table_gset_add(node->bm_faces, f);
-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];
+ int 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;
- BLI_assert((pbvh->totnode == 1 || node_index) && node_index <= pbvh->totnode);
+ node->flag |= updateflag;
- /* avoid initializing customdata because its quite involved */
- BMVert *v = BM_vert_create(pbvh->bm, co, NULL, BM_CREATE_SKIP_CD);
- CustomData_bmesh_set_default(&pbvh->bm->vdata, &v->head.data);
+ // 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);
+ MDynTopoVert *mv = BKE_PBVH_DYNVERT(pbvh->cd_dyn_vert, l->v);
- /* This value is logged below */
- copy_v3_v3(v->no, no);
+ BB_expand(&node->vb, l->v->co);
+ BB_expand(&node->orig_vb, mv->origco);
- BLI_gset_insert(node->bm_unique_verts, v);
- BM_ELEM_CD_SET_INT(v, pbvh->cd_vert_node_offset, node_index);
+ 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;
- node->flag |= PBVH_UpdateDrawBuffers | PBVH_UpdateBB;
+ if (ni != ni2) {
+ BLI_table_gset_add(node->bm_other_verts, l->v);
+ }
- /* Log the new vertex */
- BM_log_vert_added(pbvh->bm_log, v, cd_vert_mask_offset);
+ node2->flag |= updateflag;
- return v;
+ BB_expand(&node2->vb, l->v->co);
+ BB_expand(&node2->orig_vb, mv->origco);
+ }
+ l = l->next;
+ } while (l != f->l_first);
}
-/**
- * \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)
+void bke_pbvh_insert_face(PBVH *pbvh, struct BMFace *f)
{
- PBVHNode *node = &pbvh->nodes[node_index];
+ int i = 0;
+ bool ok = false;
+ int ni = -1;
- /* ensure we never add existing face */
- BLI_assert(!BM_face_exists(v_tri, 3));
+ while (i < pbvh->totnode) {
+ PBVHNode *node = pbvh->nodes + i;
+ bool ok2 = false;
- BMFace *f = BM_face_create(pbvh->bm, v_tri, e_tri, 3, f_example, BM_CREATE_NOP);
- f->head.hflag = f_example->head.hflag;
+ if (node->flag & PBVH_Leaf) {
+ ok = true;
+ ni = i;
+ break;
+ }
- BLI_gset_insert(node->bm_faces, f);
- BM_ELEM_CD_SET_INT(f, pbvh->cd_face_node_offset, node_index);
+ if (node->children_offset == 0) {
+ continue;
+ }
- /* mark node for update */
- node->flag |= PBVH_UpdateDrawBuffers | PBVH_UpdateNormals;
- node->flag &= ~PBVH_FullyHidden;
+ for (int j = 0; j < 2; j++) {
+ int ni2 = node->children_offset + j;
+ if (ni2 == 0) {
+ continue;
+ }
- /* Log the new face */
- BM_log_face_added(pbvh->bm_log, f);
+ PBVHNode *node2 = pbvh->nodes + ni2;
+ BMLoop *l = f->l_first;
- return f;
-}
+ do {
+ if (point_in_node(node2, l->v->co)) {
+ i = ni2;
+ ok2 = true;
+ break;
+ }
-/* 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;
+ l = l->next;
+ } 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 (ok2) {
+ break;
+ }
}
- }
- BM_FACES_OF_VERT_ITER_END;
-
- return count;
-}
-#endif
-
-#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;
- }
+ if (!ok2) {
+ break;
}
}
- BM_FACES_OF_VERT_ITER_END;
- return count;
-}
+ if (!ok) {
+ // find closest node
+ float co[3];
+ int tot = 0;
+ BMLoop *l = f->l_first;
-/* 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;
+ zero_v3(co);
- BM_FACES_OF_VERT_ITER_BEGIN (f, v) {
- PBVHNode *f_node = pbvh_bmesh_node_from_face(pbvh, f);
+ do {
+ add_v3_v3(co, l->v->co);
+ l = l->next;
+ tot++;
+ } while (l != f->l_first);
- if (f_node != current_node) {
- return f_node;
- }
- }
- BM_FACES_OF_VERT_ITER_END;
+ mul_v3_fl(co, 1.0f / (float)tot);
+ float mindis = 1e17;
- return NULL;
-}
+ for (int i = 0; i < pbvh->totnode; i++) {
+ PBVHNode *node = pbvh->nodes + i;
-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 */
- current_owner->flag |= PBVH_UpdateDrawBuffers | PBVH_UpdateBB;
+ if (!(node->flag & PBVH_Leaf)) {
+ continue;
+ }
- BLI_assert(current_owner != new_owner);
+ float cent[3];
+ add_v3_v3v3(cent, node->vb.bmin, node->vb.bmax);
+ mul_v3_fl(cent, 0.5f);
- /* Remove current ownership */
- BLI_gset_remove(current_owner->bm_unique_verts, v, NULL);
+ float dis = len_squared_v3v3(co, cent);
+ if (dis < mindis) {
+ mindis = dis;
+ ni = i;
+ }
+ }
+ }
- /* 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, NULL);
- BLI_assert(!BLI_gset_haskey(new_owner->bm_other_verts, v));
+ 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;
+ }
- /* mark node for update */
- new_owner->flag |= PBVH_UpdateDrawBuffers | PBVH_UpdateBB;
+ bke_pbvh_insert_face_finalize(pbvh, f, ni);
}
-static void pbvh_bmesh_vert_remove(PBVH *pbvh, BMVert *v)
+static void pbvh_bmesh_regen_node_verts(PBVH *pbvh, PBVHNode *node)
{
- /* never match for first time */
- int f_node_index_prev = DYNTOPO_NODE_NONE;
+ node->flag &= ~PBVH_RebuildNodeVerts;
- PBVHNode *v_node = pbvh_bmesh_node_from_vert(pbvh, v);
- BLI_gset_remove(v_node->bm_unique_verts, v, NULL);
- BM_ELEM_CD_SET_INT(v, pbvh->cd_vert_node_offset, DYNTOPO_NODE_NONE);
+ int usize = BLI_table_gset_len(node->bm_unique_verts);
+ int osize = BLI_table_gset_len(node->bm_other_verts);
- /* 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);
+ TableGSet *old_unique_verts = node->bm_unique_verts;
- /* 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;
+ BLI_table_gset_free(node->bm_other_verts, NULL);
- PBVHNode *f_node = &pbvh->nodes[f_node_index];
- f_node->flag |= PBVH_UpdateDrawBuffers | PBVH_UpdateBB;
+ BMVert *v;
+ TGSET_ITER (v, old_unique_verts) {
+ BM_ELEM_CD_SET_INT(v, pbvh->cd_vert_node_offset, -1);
+ }
+ TGSET_ITER_END;
- /* Remove current ownership */
- BLI_gset_remove(f_node->bm_other_verts, v, NULL);
+ node->bm_unique_verts = BLI_table_gset_new("bm_unique_verts");
+ node->bm_other_verts = BLI_table_gset_new("bm_other_verts");
- 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;
-}
+ const int cd_vert_node = pbvh->cd_vert_node_offset;
+ const int ni = (int)(node - pbvh->nodes);
-static void pbvh_bmesh_face_remove(PBVH *pbvh, BMFace *f)
-{
- PBVHNode *f_node = pbvh_bmesh_node_from_face(pbvh, f);
+ bool update = false;
- /* 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;
+ 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);
- new_node = pbvh_bmesh_vert_other_node_find(pbvh, v);
- BLI_assert(new_node || BM_vert_face_count_is_equal(v, 1));
+ if (ni2 == DYNTOPO_NODE_NONE) {
+ BM_ELEM_CD_SET_INT(l->v, cd_vert_node, ni);
+ ni2 = ni;
+ update = true;
+ }
- if (new_node) {
- pbvh_bmesh_vert_ownership_transfer(pbvh, new_node, v);
- }
+ if (ni2 == ni) {
+ BLI_table_gset_add(node->bm_unique_verts, l->v);
}
else {
- /* Remove from other verts */
- BLI_gset_remove(f_node->bm_other_verts, v, NULL);
+ BLI_table_gset_add(node->bm_other_verts, l->v);
}
- }
- } while ((l_iter = l_iter->next) != l_first);
-
- /* Remove face from node and top level */
- BLI_gset_remove(f_node->bm_faces, f, NULL);
- 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;
-}
-
-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 = 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(NULL, BM_LOOPS_OF_EDGE, e, buf->data, buf->count);
+ } while ((l = l->next) != f->l_first);
}
-}
-
-static void pbvh_bmesh_node_drop_orig(PBVHNode *node)
-{
- if (node->bm_orco) {
- MEM_freeN(node->bm_orco);
- }
- if (node->bm_ortri) {
- MEM_freeN(node->bm_ortri);
- }
- node->bm_orco = NULL;
- node->bm_ortri = NULL;
- node->bm_tot_ortri = 0;
-}
-
-/****************************** EdgeQueue *****************************/
-
-struct EdgeQueue;
-
-typedef 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
+ TGSET_ITER_END;
- bool (*edge_queue_tri_in_range)(const struct EdgeQueue *q, BMFace *f);
+ 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;
- const float *view_normal;
-#ifdef USE_EDGEQUEUE_FRONTFACE
- unsigned int use_view_normal : 1;
-#endif
-} EdgeQueue;
+ BM_ITER_ELEM (f2, &iter2, v, BM_FACES_OF_VERT) {
+ int ni2 = BM_ELEM_CD_GET_INT(f2, pbvh->cd_face_node_offset);
-typedef struct {
- EdgeQueue *q;
- BLI_mempool *pool;
- BMesh *bm;
- int cd_vert_mask_offset;
- int cd_vert_node_offset;
- int cd_face_node_offset;
-} EdgeQueueContext;
-
-/* only tag'd 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
+ if (ni2 >= 0) {
+ BM_ELEM_CD_SET_INT(v, pbvh->cd_vert_node_offset, ni2);
+ PBVHNode *node = pbvh->nodes + ni2;
-#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_table_gset_add(node->bm_unique_verts, v);
+ BLI_table_gset_remove(node->bm_other_verts, v, NULL);
- 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;
+ ok = true;
+ break;
+ }
+ }
- BLI_assert((EDGE_QUEUE_TEST(e_tri[0]) == false) && (EDGE_QUEUE_TEST(e_tri[1]) == false) &&
- (EDGE_QUEUE_TEST(e_tri[2]) == false));
+ if (!ok) {
+ printf("pbvh error: orphaned vert node reference\n");
}
}
}
-}
-#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;
-}
+ TGSET_ITER_END;
-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 = 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);
+ 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
}
-}
-static void long_edge_queue_edge_add(EdgeQueueContext *eq_ctx, BMEdge *e)
-{
-#ifdef USE_EDGEQUEUE_TAG
- if (EDGE_QUEUE_TEST(e) == false)
+ 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
- {
- 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 (update) {
+ node->flag |= PBVH_UpdateNormals | PBVH_UpdateDrawBuffers | PBVH_RebuildDrawBuffers |
+ PBVH_UpdateBB;
+ node->flag |= PBVH_UpdateOriginalBB | PBVH_UpdateRedraw | PBVH_UpdateColor | PBVH_UpdateTris |
+ PBVH_UpdateVisibility;
}
- 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
- }
+ BLI_table_gset_free(old_unique_verts, NULL);
}
-#endif /* USE_EDGEQUEUE_EVEN_SUBDIV */
-static void short_edge_queue_edge_add(EdgeQueueContext *eq_ctx, BMEdge *e)
+void BKE_pbvh_bmesh_mark_node_regen(PBVH *pbvh, PBVHNode *node)
{
-#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);
- }
- }
+ node->flag |= PBVH_RebuildNodeVerts;
}
-static void long_edge_queue_face_add(EdgeQueueContext *eq_ctx, BMFace *f)
+PBVHNode *BKE_pbvh_get_node_leaf_safe(PBVH *pbvh, int i)
{
-#ifdef USE_EDGEQUEUE_FRONTFACE
- if (eq_ctx->q->use_view_normal) {
- if (dot_v3v3(f->no, eq_ctx->q->view_normal) < 0.0f) {
- return;
+ if (i >= 0 && i < pbvh->totnode) {
+ PBVHNode *node = pbvh->nodes + i;
+ if ((node->flag & PBVH_Leaf) && !(node->flag & PBVH_Delete)) {
+ return node;
}
}
-#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);
- }
+ return NULL;
}
-static void short_edge_queue_face_add(EdgeQueueContext *eq_ctx, BMFace *f)
+void BKE_pbvh_bmesh_regen_node_verts(PBVH *pbvh)
{
-#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
+ for (int i = 0; i < pbvh->totnode; i++) {
+ PBVHNode *node = pbvh->nodes + i;
- if (eq_ctx->q->edge_queue_tri_in_range(eq_ctx->q, f)) {
- BMLoop *l_iter;
- BMLoop *l_first;
+ if (!(node->flag & PBVH_Leaf) || !(node->flag & PBVH_RebuildNodeVerts)) {
+ continue;
+ }
- /* 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);
+ pbvh_bmesh_regen_node_verts(pbvh, node);
}
}
-/* 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
+void BKE_pbvh_bmesh_update_origvert(
+ PBVH *pbvh, BMVert *v, float **r_co, float **r_no, float **r_color, bool log_undo)
+{
+ MDynTopoVert *mv = BKE_PBVH_DYNVERT(pbvh->cd_dyn_vert, v);
- 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);
+ if (log_undo) {
+ BM_log_vert_before_modified(pbvh->bm_log, v, pbvh->cd_vert_mask_offset, r_color != NULL);
}
- 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];
+ if (r_co || r_no) {
- /* Check leaf nodes marked for topology update */
- if ((node->flag & PBVH_Leaf) && (node->flag & PBVH_UpdateTopology) &&
- !(node->flag & PBVH_FullyHidden)) {
- GSetIterator gs_iter;
+ copy_v3_v3(mv->origco, v->co);
+ copy_v3_v3(mv->origno, v->no);
- /* Check each face */
- GSET_ITER (gs_iter, node->bm_faces) {
- BMFace *f = BLI_gsetIterator_getKey(&gs_iter);
+ if (r_co) {
+ *r_co = mv->origco;
+ }
- long_edge_queue_face_add(eq_ctx, f);
- }
+ if (r_no) {
+ *r_no = mv->origno;
}
}
-}
-
-/* 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;
+ if (r_color && pbvh->cd_vcol_offset >= 0) {
+ MPropCol *ml1 = BM_ELEM_CD_GET_VOID_P(v, pbvh->cd_vcol_offset);
-#ifdef USE_EDGEQUEUE_FRONTFACE
- eq_ctx->q->use_view_normal = use_frontface;
-#else
- UNUSED_VARS(use_frontface);
-#endif
+ copy_v4_v4(mv->origcolor, ml1->color);
- 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;
+ if (r_color) {
+ *r_color = mv->origcolor;
+ }
}
-
- 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 = 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);
+ else if (r_color) {
+ *r_color = NULL;
}
+}
- /* 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)`
- *
- * <pre>
- * + v3(v_opp)
- * /|\
- * / | \
- * / | \
- * e4/ | \ e3
- * / |e5 \
- * / | \
- * / e1 | e2 \
- * +-------+-------+
- * v1 v4(v_new) v2
- * (first) (second)
- * </pre>
- *
- * - 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->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->bm, v_tri[0], v_tri[1], NULL, BM_CREATE_NO_DOUBLE);
- e_tri[2] = e_tri[1]; /* switched */
- e_tri[1] = BM_edge_create(pbvh->bm, v_tri[1], v_tri[2], NULL, 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->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->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 = 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 = NULL;
-
- /* Check that the edge still exists */
- if (!(e = BM_edge_exists(v1, v2))) {
- continue;
- }
-#ifdef USE_EDGEQUEUE_TAG
- EDGE_QUEUE_DISABLE(e);
-#endif
+/************************* Called from pbvh.c *************************/
- /* At the moment edges never get shorter (subdiv 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
+bool BKE_pbvh_bmesh_check_origdata(PBVH *pbvh, BMVert *v, int stroke_id)
+{
+ // keep this up to date with surface_smooth_v_safe in dyntopo.c
- /* 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;
- }
+ MDynTopoVert *mv = BKE_PBVH_DYNVERT(pbvh->cd_dyn_vert, v);
- any_subdivided = true;
+ if (mv->stroke_id != stroke_id) {
+ float *dummy;
- pbvh_bmesh_split_edge(eq_ctx, pbvh, e, edge_loops);
+ BKE_pbvh_bmesh_update_origvert(pbvh, v, &dummy, &dummy, &dummy, false);
+ mv->stroke_id = stroke_id;
+ return true;
}
-#ifdef USE_EDGEQUEUE_TAG_VERIFY
- pbvh_bmesh_edge_tag_verify(pbvh);
-#endif
-
- return any_subdivided;
+ return false;
}
-static void pbvh_bmesh_collapse_edge(PBVH *pbvh,
- BMEdge *e,
- BMVert *v1,
- BMVert *v2,
- GHash *deleted_verts,
- BLI_Buffer *deleted_faces,
- EdgeQueueContext *eq_ctx)
+bool pbvh_bmesh_node_raycast(PBVH *pbvh,
+ PBVHNode *node,
+ const float ray_start[3],
+ const float ray_normal[3],
+ struct IsectRayPrecalc *isect_precalc,
+ int *hit_count,
+ float *depth,
+ float *back_depth,
+ bool use_original,
+ SculptVertRef *r_active_vertex_index,
+ SculptFaceRef *r_active_face_index,
+ float *r_face_normal,
+ int stroke_id)
{
- 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;
- }
+ bool hit = false;
+ float nearest_vertex_co[3] = {0.0f};
+ float nearest_vertex_dist = 1e17;
- /* Remove the merge vertex from the PBVH */
- pbvh_bmesh_vert_remove(pbvh, v_del);
+ BKE_pbvh_bmesh_check_tris(pbvh, node);
- /* Remove all faces adjacent to the edge */
- BMLoop *l_adj;
- while ((l_adj = e->l)) {
- BMFace *f_adj = l_adj->f;
+ PBVHTriBuf *tribuf = node->tribuf;
+ const int cd_dyn_vert = pbvh->cd_dyn_vert;
- pbvh_bmesh_face_remove(pbvh, f_adj);
- BM_face_kill(pbvh->bm, f_adj);
- }
+ for (int i = 0; i < node->tribuf->tottri; i++) {
+ PBVHTri *tri = tribuf->tris + 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;
- /* Kill the edge */
- BLI_assert(BM_edge_is_wire(e));
- BM_edge_kill(pbvh->bm, e);
+ BMFace *f = (BMFace *)tri->f.i;
- /* 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);
+ if (BM_elem_flag_test(f, BM_ELEM_HIDDEN)) {
+ continue;
+ }
- BMLoop *l;
+ float *co1, *co2, *co3;
- BM_LOOPS_OF_VERT_ITER_BEGIN (l, v_del) {
- BMFace *existing_face;
+ 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);
- /* Get vertices, replace use of v_del with v_conn */
- // BM_iter_as_array(NULL, 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;
- }
+ co1 = BKE_PBVH_DYNVERT(cd_dyn_vert, v1)->origco;
+ co2 = BKE_PBVH_DYNVERT(cd_dyn_vert, v2)->origco;
+ co3 = BKE_PBVH_DYNVERT(cd_dyn_vert, v3)->origco;
}
-#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 {
+ co1 = v1->co;
+ co2 = v2->co;
+ co3 = v3->co;
}
- 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->bm, v_tri, e_tri);
- pbvh_bmesh_face_create(pbvh, ni, v_tri, e_tri, f);
+ bool hit2 = ray_face_intersection_depth_tri(
+ ray_start, isect_precalc, co1, co2, co3, depth, back_depth, hit_count);
- /* 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);
- }
- }
+ // bool hit2 = ray_face_intersection_tri(ray_start, isect_precalc, co1, co2, co3, depth);
- BLI_buffer_append(deleted_faces, BMFace *, f);
- }
- BM_LOOPS_OF_VERT_ITER_END;
+ if (hit2) {
+ // ensure sculpt active vertex is set r_active_vertex_index
- /* Delete the tagged faces */
- for (int i = 0; i < deleted_faces->count; i++) {
- BMFace *f_del = BLI_buffer_at(deleted_faces, BMFace *, i);
+ for (int j = 0; j < 3; j++) {
+ BMVert *v = (BMVert *)tribuf->verts[tri->v[j]].i;
+ float *co = BKE_PBVH_DYNVERT(cd_dyn_vert, v)->origco;
- /* 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;
+ float dist = len_squared_v3v3(co, ray_start);
+ if (dist < nearest_vertex_dist) {
+ nearest_vertex_dist = dist;
+ copy_v3_v3(nearest_vertex_co, co);
- /* Remove the face */
- pbvh_bmesh_face_remove(pbvh, f_del);
- BM_face_kill(pbvh->bm, f_del);
+ hit = true;
+ if (r_active_vertex_index) {
+ *r_active_vertex_index = tribuf->verts[tri->v[j]];
+ }
- /* 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->bm, e_tri[j]);
- }
- }
+ if (r_active_face_index) {
+ *r_active_face_index = tri->f;
+ }
- /* 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 == NULL)) {
- pbvh_bmesh_vert_remove(pbvh, v_tri[j]);
+ if (r_face_normal) {
+ float no[3];
- BM_log_vert_removed(pbvh->bm_log, v_tri[j], eq_ctx->cd_vert_mask_offset);
+ if (use_original) {
+ copy_v3_v3(no, BKE_PBVH_DYNVERT(cd_dyn_vert, v1)->origno);
+ add_v3_v3(no, BKE_PBVH_DYNVERT(cd_dyn_vert, v2)->origno);
+ add_v3_v3(no, BKE_PBVH_DYNVERT(cd_dyn_vert, v3)->origno);
+ normalize_v3(no);
+ }
+ else {
+ copy_v3_v3(no, tri->no);
+ }
- if (v_tri[j] == v_conn) {
- v_conn = NULL;
+ copy_v3_v3(r_face_normal, no);
+ }
}
- BLI_ghash_insert(deleted_verts, v_tri[j], NULL);
- BM_vert_kill(pbvh->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 != NULL) {
- 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 boundboxes 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;
+ hit = true;
}
- 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 == NULL is OK */
- BLI_ghash_insert(deleted_verts, v_del, v_conn);
- BM_vert_kill(pbvh->bm, v_del);
+ return hit;
}
-static bool pbvh_bmesh_collapse_short_edges(EdgeQueueContext *eq_ctx,
- PBVH *pbvh,
- BLI_Buffer *deleted_faces)
+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)
{
- 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 NULL when removed. */
- GHash *deleted_verts = BLI_ghash_ptr_new("deleted_verts");
-
- while (!BLI_heapsimple_is_empty(eq_ctx->q->heap)) {
- BMVert **pair = BLI_heapsimple_pop_min(eq_ctx->q->heap);
- BMVert *v1 = pair[0], *v2 = pair[1];
- BLI_mempool_free(eq_ctx->pool, pair);
- pair = NULL;
+ if (node->flag & PBVH_FullyHidden) {
+ return false;
+ }
- /* 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;
- }
+ 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;
- /* Check that the edge still exists */
- BMEdge *e;
- if (!(e = BM_edge_exists(v1, v2))) {
+ if (BM_elem_flag_test(f, BM_ELEM_HIDDEN)) {
continue;
}
-#ifdef USE_EDGEQUEUE_TAG
- EDGE_QUEUE_DISABLE(e);
-#endif
- if (len_squared_v3v3(v1->co, v2->co) >= min_len_squared) {
- continue;
- }
+ bool hit_local = ray_face_intersection_tri(
+ ray_start, isect_precalc, v1->co, v2->co, v3->co, depth);
- /* 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;
- }
+ 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);
- any_collapsed = true;
+ /* detail returned will be set to the maximum allowed size, so take max here */
+ *r_edge_length = sqrtf(max_fff(len1, len2, len3));
- pbvh_bmesh_collapse_edge(pbvh, e, v1, v2, deleted_verts, deleted_faces, eq_ctx);
+ return true;
+ }
}
- BLI_ghash_free(deleted_verts, NULL, NULL);
-
- return any_collapsed;
+ return false;
}
-/************************* Called from pbvh.c *************************/
-
-bool pbvh_bmesh_node_raycast(PBVHNode *node,
- const float ray_start[3],
- const float ray_normal[3],
- struct IsectRayPrecalc *isect_precalc,
- float *depth,
- bool use_original,
- int *r_active_vertex_index,
- float *r_face_normal)
+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,
+ int stroke_id)
{
bool hit = false;
- float nearest_vertex_co[3] = {0.0f};
- 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_intersection_tri(ray_start,
- isect_precalc,
- node->bm_orco[t[0]],
- node->bm_orco[t[1]],
- node->bm_orco[t[2]],
- depth);
- }
- }
- else {
- GSetIterator gs_iter;
+ BKE_pbvh_bmesh_check_tris(pbvh, node);
+ PBVHTriBuf *tribuf = node->tribuf;
+ const int cd_dyn_vert = pbvh->cd_dyn_vert;
- GSET_ITER (gs_iter, node->bm_faces) {
- BMFace *f = BLI_gsetIterator_getKey(&gs_iter);
+ for (int i = 0; i < tribuf->tottri; i++) {
+ PBVHTri *tri = tribuf->tris + i;
+ BMFace *f = (BMFace *)tri->f.i;
- BLI_assert(f->len == 3);
- if (!BM_elem_flag_test(f, BM_ELEM_HIDDEN)) {
- BMVert *v_tri[3];
+ if (BM_elem_flag_test(f, BM_ELEM_HIDDEN)) {
+ continue;
+ }
- BM_face_as_array_vert_tri(f, v_tri);
+ 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;
- if (ray_face_intersection_tri(
- ray_start, isect_precalc, v_tri[0]->co, v_tri[1]->co, v_tri[2]->co, depth)) {
- hit = true;
+ float *co1, *co2, *co3;
- if (r_face_normal) {
- normal_tri_v3(r_face_normal, v_tri[0]->co, v_tri[1]->co, v_tri[2]->co);
- }
+ 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);
- 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++) {
- if (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_index = BM_elem_index_get(v_tri[j]);
- }
- }
- }
- }
- }
+ co1 = BKE_PBVH_DYNVERT(cd_dyn_vert, v1)->origco;
+ co2 = BKE_PBVH_DYNVERT(cd_dyn_vert, v2)->origco;
+ co3 = BKE_PBVH_DYNVERT(cd_dyn_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;
}
-bool BKE_pbvh_bmesh_node_raycast_detail(PBVHNode *node,
- const float ray_start[3],
- struct IsectRayPrecalc *isect_precalc,
- float *depth,
- float *r_edge_length)
+typedef struct UpdateNormalsTaskData {
+ PBVHNode **nodes;
+ int totnode;
+} UpdateNormalsTaskData;
+
+static void pbvh_update_normals_task_cb(void *__restrict userdata,
+ const int n,
+ const TaskParallelTLS *__restrict UNUSED(tls))
{
- if (node->flag & PBVH_FullyHidden) {
- return 0;
+ BMVert *v;
+ BMFace *f;
+ UpdateNormalsTaskData *data = (UpdateNormalsTaskData *)userdata;
+ PBVHNode *node = data->nodes[n];
+
+ node->flag |= PBVH_UpdateCurvatureDir;
+
+ TGSET_ITER (f, node->bm_faces) {
+ BM_face_normal_update(f);
}
+ TGSET_ITER_END
- GSetIterator gs_iter;
- bool hit = false;
- BMFace *f_hit = NULL;
+ TGSET_ITER (v, node->bm_unique_verts) {
+ // BM_vert_normal_update(v);
+ // optimized loop
+ BMEdge *e = v->e;
- GSET_ITER (gs_iter, node->bm_faces) {
- BMFace *f = BLI_gsetIterator_getKey(&gs_iter);
+ zero_v3(v->no);
- 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);
+ if (!e) {
+ continue;
+ }
+
+ do {
+ BMLoop *l = e->l;
- if (hit_local) {
- f_hit = f;
- hit = true;
+ if (!l) {
+ e = v == e->v1 ? e->v1_disk_link.next : e->v2_disk_link.next;
+ continue;
}
- }
- }
- 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);
+ do {
+ v->no[0] += l->f->no[0];
+ v->no[1] += l->f->no[1];
+ v->no[2] += l->f->no[2];
- /* detail returned will be set to the maximum allowed size, so take max here */
- *r_edge_length = sqrtf(max_fff(len1, len2, len3));
+ l = l->radial_next;
+ } while (l != e->l);
+
+ e = v == e->v1 ? e->v1_disk_link.next : e->v2_disk_link.next;
+ } while (e != v->e);
+
+ normalize_v3(v->no);
}
+ TGSET_ITER_END
- return hit;
+ node->flag &= ~PBVH_UpdateNormals;
}
-bool pbvh_bmesh_node_nearest_to_ray(PBVHNode *node,
- const float ray_start[3],
- const float ray_normal[3],
- float *depth,
- float *dist_sq,
- bool use_original)
+void pbvh_bmesh_normals_update(PBVHNode **nodes, int totnode)
{
- 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);
- }
- }
- else {
- GSetIterator gs_iter;
+ TaskParallelSettings settings;
+ UpdateNormalsTaskData data = {nodes, totnode};
- GSET_ITER (gs_iter, node->bm_faces) {
- BMFace *f = BLI_gsetIterator_getKey(&gs_iter);
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
+ BLI_task_parallel_range(0, totnode, &data, pbvh_update_normals_task_cb, &settings);
- BLI_assert(f->len == 3);
- if (!BM_elem_flag_test(f, BM_ELEM_HIDDEN)) {
- BMVert *v_tri[3];
+#if 0 // in theory we shouldn't need to update normals in bm_other_verts.
+ for (int i=0; i<totnode; i++) {
+ PBVHNode *node = nodes[i];
- 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);
- }
+ TGSET_ITER (v, node->bm_other_verts) {
+ BM_vert_normal_update(v);
}
+ TGSET_ITER_END
}
-
- return hit;
+#endif
}
-void pbvh_bmesh_normals_update(PBVHNode **nodes, int totnode)
+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(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(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(BLI_gsetIterator_getKey(&gs_iter));
+ TGSET_ITER (v, node->bm_other_verts) {
+ BM_vert_normal_update(v);
}
+ TGSET_ITER_END
+
node->flag &= ~PBVH_UpdateNormals;
}
}
@@ -1682,6 +1172,7 @@ void pbvh_bmesh_normals_update(PBVHNode **nodes, int totnode)
struct FastNodeBuildInfo {
int totface; /* number of faces */
int start; /* start of faces in array */
+ int depth;
struct FastNodeBuildInfo *child1;
struct FastNodeBuildInfo *child2;
};
@@ -1696,7 +1187,7 @@ static void pbvh_bmesh_node_limit_ensure_fast(
{
struct FastNodeBuildInfo *child1, *child2;
- if (node->totface <= pbvh->leaf_limit) {
+ if (node->totface <= pbvh->leaf_limit || node->depth >= pbvh->depth_limit) {
return;
}
@@ -1778,8 +1269,12 @@ static void pbvh_bmesh_node_limit_ensure_fast(
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 = NULL;
pbvh_bmesh_node_limit_ensure_fast(pbvh, nodeinfo, bbc_array, child1, arena);
@@ -1790,6 +1285,7 @@ static void pbvh_bmesh_create_nodes_fast_recursive(
PBVH *pbvh, BMFace **nodeinfo, BBC *bbc_array, struct FastNodeBuildInfo *node, int node_index)
{
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;
@@ -1817,12 +1313,12 @@ static void pbvh_bmesh_create_nodes_fast_recursive(
bool has_visible = false;
- n->flag = PBVH_Leaf;
- n->bm_faces = BLI_gset_ptr_new_ex("bm_faces", node->totface);
+ n->flag = PBVH_Leaf | PBVH_UpdateTris;
+ n->bm_faces = BLI_table_gset_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");
+ 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);
@@ -1833,7 +1329,7 @@ static void pbvh_bmesh_create_nodes_fast_recursive(
BBC *bbc = &bbc_array[BM_elem_index_get(f)];
/* Update ownership of faces */
- BLI_gset_insert(n->bm_faces, f);
+ BLI_table_gset_insert(n->bm_faces, f);
BM_ELEM_CD_SET_INT(f, cd_face_node_offset, node_index);
/* Update vertices */
@@ -1841,12 +1337,12 @@ static void pbvh_bmesh_create_nodes_fast_recursive(
BMLoop *l_iter = l_first;
do {
BMVert *v = l_iter->v;
- if (!BLI_gset_haskey(n->bm_unique_verts, v)) {
+ 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);
}
}
@@ -1869,42 +1365,378 @@ static void pbvh_bmesh_create_nodes_fast_recursive(
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;
}
}
/***************************** 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);
+}
+
+typedef struct FSetTemp {
+ BMVert *v;
+ int fset;
+ bool boundary;
+} FSetTemp;
+
+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;
+ }
+
+ // 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;
+}
+
+void bke_pbvh_update_vert_boundary(int cd_dyn_vert,
+ int cd_faceset_offset,
+ BMVert *v,
+ int bound_symmetry)
+{
+ MDynTopoVert *mv = BKE_PBVH_DYNVERT(cd_dyn_vert, v);
+
+ BMEdge *e = v->e;
+ mv->flag &= ~(DYNVERT_BOUNDARY | DYNVERT_FSET_BOUNDARY | DYNVERT_NEED_BOUNDARY |
+ DYNVERT_NEED_TRIANGULATE | DYNVERT_FSET_CORNER | DYNVERT_CORNER |
+ DYNVERT_NEED_VALENCE | DYNVERT_SEAM_BOUNDARY | DYNVERT_SHARP_BOUNDARY |
+ DYNVERT_SEAM_CORNER | DYNVERT_SHARP_CORNER);
+
+ if (!e) {
+ mv->flag |= DYNVERT_BOUNDARY;
+ mv->valence = 0;
+ return;
+ }
+
+ int val = 0;
+
+ int sharpcount = 0;
+ int seamcount = 0;
+ int fsetcount = 0;
+ int quadcount = 0;
+
+#if 0
+ struct FaceSetRef {
+ int fset;
+ BMVert *v2;
+ BMEdge *e;
+ } *fsets = NULL;
+#endif
+ int *fsets = NULL;
+ BLI_array_staticdeclare(fsets, 16);
+
+ do {
+ BMVert *v2 = v == e->v1 ? e->v2 : e->v1;
+
+ if (e->head.hflag & BM_ELEM_SEAM) {
+ mv->flag |= DYNVERT_SEAM_BOUNDARY;
+ seamcount++;
+
+ if (seamcount > 2) {
+ mv->flag |= DYNVERT_SEAM_CORNER;
+ }
+ }
+
+ if (!(e->head.hflag & BM_ELEM_SMOOTH)) {
+ mv->flag |= DYNVERT_SHARP_BOUNDARY;
+ sharpcount++;
+
+ if (sharpcount > 2) {
+ mv->flag |= DYNVERT_SHARP_CORNER;
+ }
+ }
+
+ if (e->l) {
+ if (e->l != e->l->radial_next) {
+ if (e->l->f->len > 3) {
+ quadcount++;
+ }
+
+ if (e->l->radial_next->f->len > 3) {
+ quadcount++;
+ }
+ }
+
+ int fset = BKE_pbvh_do_fset_symmetry(
+ BM_ELEM_CD_GET_INT(e->l->f, cd_faceset_offset), bound_symmetry, v2->co);
+
+ if (e->l->f->len > 3) {
+ mv->flag |= DYNVERT_NEED_TRIANGULATE;
+ }
+
+ bool ok = true;
+ for (int i = 0; i < BLI_array_len(fsets); i++) {
+ if (fsets[i] == fset) {
+ ok = false;
+ }
+ }
+
+ if (ok) {
+ BLI_array_append(fsets, 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) {
+ // 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 < BLI_array_len(fsets); i++) {
+ if (fsets[i] == fset2) {
+ ok2 = false;
+ }
+ }
+
+ if (ok2) {
+ BLI_array_append(fsets, fset2);
+ }
+
+ if (e->l->radial_next->f->len > 3) {
+ mv->flag |= DYNVERT_NEED_TRIANGULATE;
+ }
+ }
+ }
+
+ if (!e->l || e->l->radial_next == e->l) {
+ mv->flag |= DYNVERT_BOUNDARY;
+ }
+
+ val++;
+ } while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e);
+
+ if (BLI_array_len(fsets) > 1) {
+ mv->flag |= DYNVERT_FSET_BOUNDARY;
+ }
+
+ if (BLI_array_len(fsets) > 2) {
+ mv->flag |= DYNVERT_FSET_CORNER;
+ }
+
+ if (sharpcount == 1) {
+ mv->flag |= DYNVERT_SHARP_CORNER;
+ }
+
+ if (seamcount == 1) {
+ mv->flag |= DYNVERT_SEAM_CORNER;
+ }
+
+ mv->valence = val;
+ if ((mv->flag & DYNVERT_BOUNDARY) && quadcount >= 3) {
+ mv->flag |= DYNVERT_CORNER;
+ }
+
+ BLI_array_free(fsets);
+}
+
+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_dyn_vert,
+ int cd_faceset_offset,
+ BMVert *v,
+ int bound_symmetry)
+{
+ bke_pbvh_update_vert_boundary(cd_dyn_vert, cd_faceset_offset, v, bound_symmetry);
+}
+
+/*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->bm, BM_VERTS_OF_MESH) {
+ bke_pbvh_update_vert_boundary(
+ pbvh->cd_dyn_vert, pbvh->cd_faceset_offset, v, pbvh->boundary_symmetry);
+ }
+}
+
+void BKE_pbvh_update_all_boundary_bmesh(PBVH *pbvh)
+{
+ // update boundary flags in a hopefully faster manner then v->e iteration
+ // XXX or not, it's slower
+
+ BMIter iter;
+ BMEdge *e;
+ BMVert *v;
+
+ const int cd_fset = CustomData_get_offset(&pbvh->bm->pdata, CD_SCULPT_FACE_SETS);
+
+ BM_ITER_MESH (v, &iter, pbvh->bm, BM_VERTS_OF_MESH) {
+ MDynTopoVert *mv = BKE_PBVH_DYNVERT(pbvh->cd_dyn_vert, v);
+ mv->flag &= ~(DYNVERT_BOUNDARY | DYNVERT_FSET_BOUNDARY | DYNVERT_NEED_BOUNDARY |
+ DYNVERT_VERT_FSET_HIDDEN);
+
+ if (cd_fset >= 0 && v->e && v->e->l) {
+ mv->flag |= DYNVERT_VERT_FSET_HIDDEN;
+ }
+ }
+
+ BM_ITER_MESH (e, &iter, pbvh->bm, BM_EDGES_OF_MESH) {
+ if (!e->l || e->l == e->l->radial_next) {
+ MDynTopoVert *mv1 = BKE_PBVH_DYNVERT(pbvh->cd_dyn_vert, e->v1);
+ MDynTopoVert *mv2 = BKE_PBVH_DYNVERT(pbvh->cd_dyn_vert, e->v2);
+
+ mv1->flag |= DYNVERT_BOUNDARY;
+ mv2->flag |= DYNVERT_BOUNDARY;
+ }
+
+ if (!e->l) {
+ continue;
+ }
+
+ if (cd_fset < 0) {
+ MDynTopoVert *mv1 = BKE_PBVH_DYNVERT(pbvh->cd_dyn_vert, e->v1);
+ MDynTopoVert *mv2 = BKE_PBVH_DYNVERT(pbvh->cd_dyn_vert, e->v2);
+
+ BMLoop *l = e->l;
+
+ do {
+ if (l->f->len > 3) {
+ mv1->flag |= DYNVERT_NEED_TRIANGULATE;
+ mv2->flag |= DYNVERT_NEED_TRIANGULATE;
+ break;
+ }
+
+ l = l->radial_next;
+ } while (l != e->l);
+ continue;
+ }
+
+ MDynTopoVert *mv1 = BKE_PBVH_DYNVERT(pbvh->cd_dyn_vert, e->v1);
+ MDynTopoVert *mv2 = BKE_PBVH_DYNVERT(pbvh->cd_dyn_vert, e->v2);
+
+ BMLoop *l = e->l;
+ int lastfset = BM_ELEM_CD_GET_INT(l->f, cd_fset);
+ do {
+ int fset = BM_ELEM_CD_GET_INT(l->f, cd_fset);
+
+ if (l->f->len > 3) {
+ mv1->flag |= DYNVERT_NEED_TRIANGULATE;
+ mv2->flag |= DYNVERT_NEED_TRIANGULATE;
+ }
+
+ if (fset > 0) {
+ MDynTopoVert *mv = BKE_PBVH_DYNVERT(pbvh->cd_dyn_vert, l->v);
+ mv->flag &= ~DYNVERT_VERT_FSET_HIDDEN;
+ }
+
+ if (lastfset != fset) {
+ mv1->flag |= DYNVERT_FSET_BOUNDARY;
+ mv2->flag |= DYNVERT_FSET_BOUNDARY;
+ }
+
+ lastfset = fset;
+
+ l = l->radial_next;
+ } while (l != e->l);
+ }
+}
/* Build a PBVH from a BMesh */
void BKE_pbvh_build_bmesh(PBVH *pbvh,
BMesh *bm,
bool smooth_shading,
BMLog *log,
const int cd_vert_node_offset,
- const int cd_face_node_offset)
+ const int cd_face_node_offset,
+ const int cd_dyn_vert,
+ const int cd_face_areas,
+ bool fast_draw)
{
+ 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_dyn_vert = cd_dyn_vert;
+
+ smooth_shading |= fast_draw;
+
pbvh->bm = bm;
- BKE_pbvh_bmesh_detail_size_set(pbvh, 0.75);
+ BKE_pbvh_bmesh_detail_size_set(pbvh, 0.75f, 0.4f);
pbvh->type = PBVH_BMESH;
pbvh->bm_log = log;
+ pbvh->cd_vcol_offset = CustomData_get_offset(&bm->vdata, CD_PROP_COLOR);
+ pbvh->cd_faceset_offset = CustomData_get_offset(&bm->pdata, CD_SCULPT_FACE_SETS);
+
+ pbvh->depth_limit = 18;
/* TODO: choose leaf limit better */
- pbvh->leaf_limit = 100;
+ pbvh->leaf_limit = 1000;
+
+ BMIter iter;
+ BMVert *v;
+
+ // BKE_pbvh_update_all_boundary_bmesh(pbvh);
+
+ int cd_vcol_offset = CustomData_get_offset(&bm->vdata, CD_PROP_COLOR);
+
+ BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
+ MDynTopoVert *mv = BKE_PBVH_DYNVERT(cd_dyn_vert, v);
+
+ mv->flag = DYNVERT_NEED_DISK_SORT;
+
+ bke_pbvh_update_vert_boundary(
+ pbvh->cd_dyn_vert, pbvh->cd_faceset_offset, v, pbvh->boundary_symmetry);
+ BKE_pbvh_bmesh_update_valence(pbvh->cd_dyn_vert, (SculptVertRef){(intptr_t)v});
+
+ copy_v3_v3(mv->origco, v->co);
+ copy_v3_v3(mv->origno, v->no);
+
+ if (cd_vcol_offset >= 0) {
+ MPropCol *c1 = BM_ELEM_CD_GET_VOID_P(v, cd_vcol_offset);
+ copy_v4_v4(mv->origcolor, c1->color);
+ }
+ else {
+ zero_v4(mv->origcolor);
+ }
+ }
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_mallocN(sizeof(BBC) * bm->totface, "BBC");
BMFace **nodeinfo = MEM_mallocN(sizeof(*nodeinfo) * 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) {
@@ -1926,7 +1758,6 @@ 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) {
BM_ELEM_CD_SET_INT(v, cd_vert_node_offset, DYNTOPO_NODE_NONE);
}
@@ -1953,153 +1784,1438 @@ void BKE_pbvh_build_bmesh(PBVH *pbvh,
MEM_freeN(nodeinfo);
}
-/* 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,
- const bool use_frontface,
- const bool use_projected)
+void BKE_pbvh_set_bm_log(PBVH *pbvh, struct BMLog *log)
{
- /* 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->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;
+ pbvh->bm_log = log;
+}
+/*
+static double last_update_time[128] = {
+ 0,
+};
+*/
+
+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 modified = false;
- if (view_normal) {
- BLI_assert(len_squared_v3(view_normal) != 0.0f);
- }
-
- 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->bm,
- cd_vert_mask_offset,
- cd_vert_node_offset,
- cd_face_node_offset,
- };
-
- 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, NULL);
- BLI_mempool_destroy(queue_pool);
- }
-
- 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->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, NULL);
- 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);
-
-#ifdef USE_VERIFY
- pbvh_bmesh_verify(pbvh);
-#endif
+ 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) {
+ node->flag |= PBVH_UpdateCurvatureDir;
+ undopush(node, searchdata);
+
+ BKE_pbvh_node_mark_topology_update(pbvh->nodes + i);
+ }
+ }
+
+ // double start = PIL_check_seconds_timer();
+
+ modified = 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);
+
+ // double end = PIL_check_seconds_timer();
+
+ // printf("dyntopo time: %f\n", end - start);
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 = NULL;
+ tribuf->tris = NULL;
+ tribuf->loops = NULL;
+ tribuf->edges = NULL;
+
+ 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 = NULL;
+ }
+
+ 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 = NULL;
+ 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->bm, BM_VERT | BM_FACE);
+
+ // split by uvs
+ int layeri = CustomData_get_layer_index(&pbvh->bm->ldata, CD_MLOOPUV);
+ if (layeri < 0) {
+ return false;
+ }
+
+ int totlayer = 0;
+
+ while (layeri < pbvh->bm->ldata.totlayer && pbvh->bm->ldata.layers[layeri].type == CD_MLOOPUV) {
+ totlayer++;
+ layeri++;
+ }
+
+ const int cd_uv = pbvh->bm->ldata.layers[layeri].offset;
+ const int cd_size = CustomData_sizeof(CD_MLOOPUV);
+
+ SculptVertRef *verts = NULL;
+ PBVHTri *tris = NULL;
+ intptr_t *loops = NULL;
+
+ BLI_array_declare(verts);
+ BLI_array_declare(tris);
+ BLI_array_declare(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++;
+ BLI_array_append(loops, (intptr_t)l);
+
+ SculptVertRef sv = {(intptr_t)l->v};
+ BLI_array_append(verts, 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++) {
+ MLoopUV *uv1 = BM_ELEM_CD_GET_VOID_P(l, cd_uv + cd_size * i);
+ MLoopUV *uv2 = BM_ELEM_CD_GET_VOID_P(l2, cd_uv + cd_size * i);
+
+ if (len_v3v3(uv1->uv, uv2->uv) > 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);
+ BLI_array_append(tris, tri);
+ }
+ TGSET_ITER_END
+
+ if (node->tribuf) {
+ pbvh_free_tribuf(node->tribuf);
+ }
+ else {
+ node->tribuf = MEM_callocN(sizeof(*node->tribuf), "node->tribuf");
+ }
+
+ node->tribuf->verts = verts;
+ node->tribuf->loops = loops;
+ node->tribuf->tris = tris;
+
+ node->tribuf->tottri = BLI_array_len(tris);
+ node->tribuf->totvert = BLI_array_len(verts);
+ node->tribuf->totloop = BLI_array_len(loops);
+
+ 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_mallocN(sizeof(*tribuf->tris) * newsize, "tribuf tris");
+ }
+ else {
+ tribuf->tris = MEM_reallocN_id(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, SculptVertRef vertex)
+{
+ tribuf->totvert++;
+
+ if (tribuf->totvert >= tribuf->verts_size) {
+ size_t newsize = (size_t)32 + (size_t)(tribuf->verts_size << 1);
+
+ if (!tribuf->verts) {
+ tribuf->verts = MEM_mallocN(sizeof(*tribuf->verts) * newsize, "tribuf verts");
+ }
+ else {
+ tribuf->verts = MEM_reallocN_id(
+ tribuf->verts, sizeof(*tribuf->verts) * newsize, "tribuf verts");
+ }
+
+ tribuf->verts_size = newsize;
+ }
+
+ tribuf->verts[tribuf->totvert - 1] = vertex;
+}
+
+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_mallocN(sizeof(*tribuf->edges) * 2ULL * newsize, "tribuf edges");
+ }
+ else {
+ tribuf->edges = MEM_reallocN_id(
+ 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, NULL);
+ }
+
+ 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 = NULL;
+ tribuf->verts = NULL;
+ tribuf->tris = NULL;
+ tribuf->loops = NULL;
+
+ BLI_smallhash_init_ex(&tribuf->vertmap, node->bm_unique_verts->length);
+}
/* 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. */
-void BKE_pbvh_bmesh_node_save_orig(BMesh *bm, PBVHNode *node)
+bool BKE_pbvh_bmesh_check_tris(PBVH *pbvh, PBVHNode *node)
{
- /* Skip if original coords/triangles are already saved */
- if (node->bm_orco) {
- return;
+ BMesh *bm = pbvh->bm;
+
+ if (!(node->flag & PBVH_UpdateTris) && node->tribuf) {
+ return false;
}
- const int totvert = BLI_gset_len(node->bm_unique_verts) + BLI_gset_len(node->bm_other_verts);
+ node->flag |= PBVH_UpdateOtherVerts;
- const int tottri = BLI_gset_len(node->bm_faces);
+ int mat_map[MAXMAT];
- node->bm_orco = MEM_mallocN(sizeof(*node->bm_orco) * totvert, __func__);
- node->bm_ortri = MEM_mallocN(sizeof(*node->bm_ortri) * tottri, __func__);
+ for (int i = 0; i < MAXMAT; i++) {
+ mat_map[i] = -1;
+ }
- /* 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 = BLI_gsetIterator_getKey(&gs_iter);
- copy_v3_v3(node->bm_orco[i], v->co);
- BM_elem_index_set(v, i); /* set_dirty! */
- i++;
- }
- GSET_ITER (gs_iter, node->bm_other_verts) {
- BMVert *v = BLI_gsetIterator_getKey(&gs_iter);
- copy_v3_v3(node->bm_orco[i], v->co);
- BM_elem_index_set(v, i); /* set_dirty! */
- i++;
+ if (node->tribuf || node->tri_buffers) {
+ BKE_pbvh_bmesh_free_tris(pbvh, node);
}
- /* 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 = BLI_gsetIterator_getKey(&gs_iter);
+ node->tribuf = MEM_callocN(sizeof(*node->tribuf), "node->tribuf");
+ pbvh_init_tribuf(node, node->tribuf);
+
+ BMLoop **loops = NULL;
+ uint(*loops_idx)[3] = NULL;
+
+ BLI_array_staticdeclare(loops, 128);
+ BLI_array_staticdeclare(loops_idx, 128);
+
+ PBVHTriBuf *tribufs = NULL; // material-specific tribuffers
+ BLI_array_declare(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 (BM_elem_flag_test(f, BM_ELEM_HIDDEN)) {
continue;
}
-#if 0
- BMIter bm_iter;
- BMVert *v;
+#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] = BLI_array_len(tribufs);
+
+ pbvh_init_tribuf(node, &_tribuf);
+ _tribuf.mat_nr = mat_nr;
+ BLI_array_append(tribufs, _tribuf);
+ }
+
+#ifdef DYNTOPO_DYNAMIC_TESS
+ const int tottri = (f->len - 2);
+
+ BLI_array_clear(loops);
+ BLI_array_clear(loops_idx);
+ BLI_array_grow_items(loops, f->len);
+ BLI_array_grow_items(loops_idx, tottri);
+
+ BM_face_calc_tessellation(f, true, loops, loops_idx);
+
+ 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 = NULL;
+ 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;
+ }
+
+ if (!BLI_smallhash_ensure_p(&node->tribuf->vertmap, l->v, &val)) {
+ SculptVertRef 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 = NULL;
+ if (!BLI_smallhash_ensure_p(&mat_tribuf->vertmap, l->v, &val)) {
+ SculptVertRef 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;
+ }
+
+ 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;
- BM_ITER_ELEM (v, &bm_iter, f, BM_VERTS_OF_FACE) {
- node->bm_ortri[i][j] = BM_elem_index_get(v);
+
+ do {
+ void **val = NULL;
+
+ if (!BLI_ghash_ensure_p(vmap, l->v, &val)) {
+ SculptVertRef 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 = NULL;
+ if (!BLI_ghash_ensure_p(mat_vmaps[mat_nr], l->v, &val)) {
+ SculptVertRef 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 (BM_elem_flag_test(f, BM_ELEM_HIDDEN)) {
+ continue;
}
-#else
- bm_face_as_array_index_tri(f, node->bm_ortri[i]);
+
+ 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 = (int)BLI_smallhash_lookup(&node->tribuf->vertmap, (void *)l->e->v1);
+ int v2 = (int)BLI_smallhash_lookup(&node->tribuf->vertmap, (void *)l->e->v2);
+
+ pbvh_tribuf_add_edge(node->tribuf, v1, v2);
+
+ v1 = (int)BLI_smallhash_lookup(&mat_tribuf->vertmap, (void *)l->e->v1);
+ v2 = (int)BLI_smallhash_lookup(&mat_tribuf->vertmap, (void *)l->e->v2);
+
+ pbvh_tribuf_add_edge(mat_tribuf, v1, v2);
+ } while ((l = l->next) != f->l_first);
+ }
+ TGSET_ITER_END
+
+ BLI_array_free(loops);
+ BLI_array_free(loops_idx);
+
+ bm->elem_index_dirty |= BM_VERT;
+
+ node->tri_buffers = tribufs;
+ node->tot_tri_buffers = BLI_array_len(tribufs);
+
+ 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->bm, BM_VERTS_OF_MESH) {
+ MDynTopoVert *mv = BKE_PBVH_DYNVERT(pbvh->cd_dyn_vert, v);
+ mv->flag |= DYNVERT_NEED_DISK_SORT;
+ }
+}
+
+void BKE_pbvh_bmesh_update_all_valence(PBVH *pbvh)
+{
+ BMIter iter;
+ BMVert *v;
+
+ BM_ITER_MESH (v, &iter, pbvh->bm, BM_VERTS_OF_MESH) {
+ BKE_pbvh_bmesh_update_valence(pbvh->cd_dyn_vert, (SculptVertRef){(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_dyn_vert = pbvh->cd_dyn_vert;
+
+ BM_ITER_MESH (v, &iter, pbvh->bm, BM_VERTS_OF_MESH) {
+ MDynTopoVert *mv = BKE_PBVH_DYNVERT(cd_dyn_vert, v);
+
+ mv->flag |= DYNVERT_NEED_BOUNDARY | DYNVERT_NEED_DISK_SORT | DYNVERT_NEED_TRIANGULATE;
+ BKE_pbvh_bmesh_update_valence(pbvh->cd_dyn_vert, (SculptVertRef){.i = (intptr_t)v});
+ }
+}
+
+bool BKE_pbvh_bmesh_mark_update_valence(PBVH *pbvh, SculptVertRef vertex)
+{
+ BMVert *v = (BMVert *)vertex.i;
+ MDynTopoVert *mv = BM_ELEM_CD_GET_VOID_P(v, pbvh->cd_dyn_vert);
+
+ bool ret = mv->flag & DYNVERT_NEED_VALENCE;
+
+ mv->flag |= DYNVERT_NEED_VALENCE;
+
+ return ret;
+}
+
+bool BKE_pbvh_bmesh_check_valence(PBVH *pbvh, SculptVertRef vertex)
+{
+ BMVert *v = (BMVert *)vertex.i;
+ MDynTopoVert *mv = BM_ELEM_CD_GET_VOID_P(v, pbvh->cd_dyn_vert);
+
+ if (mv->flag & DYNVERT_NEED_VALENCE) {
+ BKE_pbvh_bmesh_update_valence(pbvh->cd_dyn_vert, vertex);
+ return true;
+ }
+
+ return false;
+}
+
+void BKE_pbvh_bmesh_update_valence(int cd_dyn_vert, SculptVertRef vertex)
+{
+ BMVert *v = (BMVert *)vertex.i;
+ BMEdge *e;
+
+ MDynTopoVert *mv = BM_ELEM_CD_GET_VOID_P(v, cd_dyn_vert);
+
+ mv->flag &= ~DYNVERT_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);
+
+ 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_UpdateTopology |
+ PBVH_UpdateNormals | PBVH_UpdateTris;
+
+ TableGSet *other = BLI_table_gset_new(__func__);
+ BMVert *v;
+
+ node->children_offset = 0;
+
+ pbvh_free_all_draw_buffers(node);
+
+ // 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, NULL);
+ 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
- i++;
+
+ // 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);
}
- node->bm_tot_ortri = i;
}
-void BKE_pbvh_bmesh_after_stroke(PBVH *pbvh)
+// 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 = NULL;
+ }
+ 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 = NULL;
+ }
+ }
+ }
+
+ totnode++;
+ }
+ }
+
+ int *map = MEM_callocN(sizeof(int) * 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 = NULL;
+ }
+
+ pbvh_free_all_draw_buffers(n);
+
+ if (n->vert_indices) {
+ MEM_freeN((void *)n->vert_indices);
+ n->vert_indices = NULL;
+ }
+ if (n->face_vert_indices) {
+ MEM_freeN((void *)n->face_vert_indices);
+ n->face_vert_indices = NULL;
+ }
+
+ 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, NULL);
+ n->bm_unique_verts = NULL;
+ }
+
+ if (n->bm_other_verts) {
+ BLI_table_gset_free(n->bm_other_verts, NULL);
+ n->bm_other_verts = NULL;
+ }
+
+ if (n->bm_faces) {
+ BLI_table_gset_free(n->bm_faces, NULL);
+ n->bm_faces = NULL;
+ }
+
+#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
+ }
+
+ BMVert **scratch = NULL;
+ BLI_array_declare(scratch);
+
+ for (int i = 0; i < bvh->totnode; i++) {
+ PBVHNode *n = bvh->nodes + i;
+
+ if (!(n->flag & PBVH_Leaf)) {
+ continue;
+ }
+
+ BLI_array_clear(scratch);
+ 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) {
+ BLI_array_append(scratch, v);
+ }
+ // BM_ELEM_CD_SET_INT(v, bvh->cd_vert_node_offset, i);
+ }
+ TGSET_ITER_END
+
+ int slen = BLI_array_len(scratch);
+ for (int j = 0; j < slen; j++) {
+ BMVert *v = scratch[j];
+
+ BLI_table_gset_remove(n->bm_other_verts, v, NULL);
+ BLI_table_gset_add(n->bm_unique_verts, v);
+ BM_ELEM_CD_SET_INT(v, bvh->cd_vert_node_offset, i);
+ }
+ }
+
+ BLI_array_free(scratch);
+ 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)
+{
+ PBVHNode **stack = NULL;
+ float *overlaps = MEM_calloc_arrayN(pbvh->totnode, sizeof(float), "overlaps");
+ PBVHNode **parentmap = MEM_calloc_arrayN(pbvh->totnode, sizeof(*parentmap), "parentmap");
+ int *depthmap = MEM_calloc_arrayN(pbvh->totnode, sizeof(*depthmap), "depthmap");
+ BLI_array_declare(stack);
+
+ BMFace **faces = NULL;
+ BLI_array_declare(faces);
+
+ PBVHNode **substack = NULL;
+ BLI_array_declare(substack);
+
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);
+ 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;
+
+ BLI_array_append(stack, pbvh->nodes);
+ while (BLI_array_len(stack) > 0) {
+ PBVHNode *node = BLI_array_pop(stack);
+ 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);
+
+ BB_intersect(&clip, &child1->vb, &child2->vb);
+ float overlap = BB_volume(&clip);
+
+ // for (int i = 0; i < depthmap[node - pbvh->nodes]; i++) {
+ // printf("-");
+ //}
+
+ // printf("volume: %.4f overlap: %.4f ratio: %.3f\n", volume, overlap, overlap / volume);
+
+ if (overlap > volume * 0.25) {
+ modified = true;
+ // printf(" DELETE!\n");
+
+ BLI_array_clear(substack);
+
+ BLI_array_append(substack, child1);
+ BLI_array_append(substack, child2);
+
+ while (BLI_array_len(substack) > 0) {
+ PBVHNode *node2 = BLI_array_pop(substack);
+
+ 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);
+ BLI_array_append(faces, f);
+ }
+ TGSET_ITER_END;
+
+ TGSET_ITER (v, node2->bm_unique_verts) {
+ 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) {
+ BLI_array_append(substack, pbvh->nodes + node2->children_offset);
+
+ if (node2->children_offset + 1 < pbvh->totnode) {
+ BLI_array_append(substack, pbvh->nodes + node2->children_offset + 1);
+ }
+ }
+ }
+ }
+
+ if (node->children_offset < pbvh->totnode) {
+ BLI_array_append(stack, child1);
+ }
+
+ if (node->children_offset + 1 < pbvh->totnode) {
+ BLI_array_append(stack, child2);
+ }
+ }
+ }
+
+ if (modified) {
+ pbvh_bmesh_compact_tree(pbvh);
+
+ printf("joined nodes; %d faces\n", BLI_array_len(faces));
+
+ for (int i = 0; i < BLI_array_len(faces); i++) {
+ 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]);
+ }
+ }
+
+ BLI_array_free(faces);
+ MEM_SAFE_FREE(parentmap);
+ MEM_SAFE_FREE(overlaps);
+ BLI_array_free(stack);
+ BLI_array_free(substack);
+}
+
+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, NULL);
+
+ // 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 = NULL;
+ }
+ 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 = NULL;
+ }
+ }
+ }
+
+ totnode++;
+ }
+ }
+
+ int *map = MEM_callocN(sizeof(int) * 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 = NULL;
+ }
+
+ pbvh_free_all_draw_buffers(n);
+
+ if (n->vert_indices) {
+ MEM_freeN((void *)n->vert_indices);
+ n->vert_indices = NULL;
+ }
+ if (n->face_vert_indices) {
+ MEM_freeN((void *)n->face_vert_indices);
+ n->face_vert_indices = NULL;
+ }
+
+ 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, NULL);
+ n->bm_unique_verts = NULL;
+ }
+
+ if (n->bm_other_verts) {
+ BLI_table_gset_free(n->bm_other_verts, NULL);
+ n->bm_other_verts = NULL;
+ }
+
+ if (n->bm_faces) {
+ BLI_table_gset_free(n->bm_faces, NULL);
+ n->bm_faces = NULL;
+ }
+
+#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
+ }
+
+ BMVert **scratch = NULL;
+ BLI_array_declare(scratch);
+
+ for (int i = 0; i < bvh->totnode; i++) {
+ PBVHNode *n = bvh->nodes + i;
+
+ if (!(n->flag & PBVH_Leaf)) {
+ continue;
+ }
+
+ BLI_array_clear(scratch);
+ 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) {
+ BLI_array_append(scratch, v);
+ }
+ // BM_ELEM_CD_SET_INT(v, bvh->cd_vert_node_offset, i);
+ }
+ TGSET_ITER_END
+
+ int slen = BLI_array_len(scratch);
+ for (int j = 0; j < slen; j++) {
+ BMVert *v = scratch[j];
+
+ BLI_table_gset_remove(n->bm_other_verts, v, NULL);
+ BLI_table_gset_add(n->bm_unique_verts, v);
+ BM_ELEM_CD_SET_INT(v, bvh->cd_vert_node_offset, i);
+ }
+ }
+
+ BLI_array_free(scratch);
+ MEM_freeN(map);
+}
+
+void BKE_pbvh_bmesh_after_stroke(PBVH *pbvh)
+{
+ 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 (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);
@@ -2107,10 +3223,11 @@ void BKE_pbvh_bmesh_after_stroke(PBVH *pbvh)
}
}
-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)
{
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)
@@ -2118,240 +3235,1851 @@ 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;
}
-struct GSet *BKE_pbvh_bmesh_node_faces(PBVHNode *node)
+struct TableGSet *BKE_pbvh_bmesh_node_faces(PBVHNode *node)
{
return node->bm_faces;
}
/****************************** Debugging *****************************/
-#if 0
+void BKE_pbvh_update_offsets(PBVH *pbvh,
+ const int cd_vert_node_offset,
+ const int cd_face_node_offset,
+ const int cd_dyn_vert,
+ const int cd_face_areas)
+{
+ 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->bm->vdata, CD_PAINT_MASK);
+ pbvh->cd_vcol_offset = CustomData_get_offset(&pbvh->bm->vdata, CD_PROP_COLOR);
+ pbvh->cd_dyn_vert = cd_dyn_vert;
+ pbvh->cd_faceset_offset = CustomData_get_offset(&pbvh->bm->pdata, CD_SCULPT_FACE_SETS);
+}
-static void pbvh_bmesh_print(PBVH *pbvh)
+static void scan_edge_split(BMesh *bm, BMEdge **edges, int totedge)
{
- fprintf(stderr, "\npbvh=%p\n", pbvh);
- fprintf(stderr, "bm_face_to_node:\n");
+ BMFace **faces = NULL;
+ BMEdge **newedges = NULL;
+ BMVert **newverts = NULL;
+ BMVert **fmap = NULL; // newverts that maps to faces
+ int *emap = NULL;
+
+ BLI_array_declare(faces);
+ BLI_array_declare(newedges);
+ BLI_array_declare(newverts);
+ BLI_array_declare(fmap);
+ BLI_array_declare(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;
+ }
- BMIter iter;
- BMFace *f;
- BM_ITER_MESH (f, &iter, pbvh->bm, BM_FACES_OF_MESH) {
- fprintf(stderr, " %d -> %d\n", BM_elem_index_get(f), pbvh_bmesh_node_index_from_face(pbvh, f));
- }
+ 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;
+ }
- fprintf(stderr, "bm_vert_to_node:\n");
- BMVert *v;
- BM_ITER_MESH (v, &iter, pbvh->bm, BM_FACES_OF_MESH) {
- fprintf(stderr, " %d -> %d\n", BM_elem_index_get(v), pbvh_bmesh_node_index_from_vert(pbvh, v));
+ prev->next = e->v2_disk_link.next;
+ next->prev = e->v2_disk_link.prev;
}
- for (int n = 0; n < pbvh->totnode; n++) {
- PBVHNode *node = &pbvh->nodes[n];
- if (!(node->flag & PBVH_Leaf)) {
+ for (int i = 0; i < totedge; i++) {
+ BMEdge *e = edges[i];
+
+ BMVert *v2 = BLI_mempool_alloc(bm->vpool);
+ memset(v2, 0, sizeof(*v2));
+ v2->head.data = BLI_mempool_alloc(bm->vdata.pool);
+
+ BLI_array_append(newverts, v2);
+
+ BMEdge *e2 = BLI_mempool_alloc(bm->epool);
+ BLI_array_append(newedges, 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;
}
- 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)));
+ do {
+ BLI_array_append(faces, l->f);
+ BMFace *f2 = BLI_mempool_alloc(bm->fpool);
+
+ BLI_array_append(faces, l->f);
+ BLI_array_append(fmap, v2);
+ BLI_array_append(emap, i);
+
+ BLI_array_append(faces, f2);
+ BLI_array_append(fmap, v2);
+ BLI_array_append(emap, i);
+
+ memset(f2, 0, sizeof(*f2));
+ f2->head.data = BLI_mempool_alloc(bm->ldata.pool);
+
+ BMLoop *prev = NULL;
+ BMLoop *l2 = NULL;
+
+ for (int j = 0; j < 3; j++) {
+ l2 = 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;
+
+ BLI_array_append(faces, f2);
+ l = l->radial_next;
+ } while (l != e->l);
+ }
+
+ for (int i = 0; i < BLI_array_len(newedges); i++) {
+ 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 = 0; i < BLI_array_len(faces); i += 2) {
+ 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;
}
+
+ BLI_array_free(newedges);
+ BLI_array_free(newverts);
+ BLI_array_free(faces);
+ BLI_array_free(fmap);
}
-static void print_flag_factors(int flag)
+#define MAX_RE_CHILD 3
+typedef struct ReVertNode {
+ int totvert, totchild;
+ struct ReVertNode *parent;
+ struct ReVertNode *children[MAX_RE_CHILD];
+ BMVert *verts[];
+} ReVertNode;
+
+BMesh *BKE_pbvh_reorder_bmesh(PBVH *pbvh)
{
- 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);
+ /*try to compute size of verts per node*/
+ int vsize = sizeof(BMVert);
+ vsize += pbvh->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 = MEM_calloc_arrayN(pbvh->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->bm, BM_VERTS_OF_MESH) {
+ v->head.hflag &= ~flag;
+ v->head.index = i++;
+ }
+
+ BMVert **stack = NULL;
+ BLI_array_declare(stack);
+
+ BM_ITER_MESH (v, &iter, pbvh->bm, BM_VERTS_OF_MESH) {
+ if (v->head.hflag & flag) {
+ continue;
+ }
+
+ ReVertNode *node = BLI_mempool_calloc(pool);
+
+ BLI_array_clear(stack);
+ BLI_array_append(stack, v);
+
+ v->head.hflag |= flag;
+
+ vnodemap[v->head.index] = node;
+ node->verts[node->totvert++] = v;
+
+ while (BLI_array_len(stack) > 0) {
+ BMVert *v2 = BLI_array_pop(stack);
+ 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++;
+
+ BLI_array_append(stack, v3);
+ }
+
+ e = e->v1 == v2 ? e->v1_disk_link.next : e->v2_disk_link.next;
+ } while (e != v2->e);
}
}
-}
+
+ const int steps = 4;
+ ReVertNode **roots = NULL;
+ BLI_array_declare(roots);
+
+ for (int step = 0; step < steps; step++) {
+ const bool last_step = step == steps - 1;
+
+ BM_ITER_MESH_INDEX (v, &iter, pbvh->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 = 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) {
+ BLI_array_append(roots, parent);
+ }
+
+ for (int j = 0; j < parent->totchild; j++) {
+ parent->children[j]->parent = parent;
+ }
+ }
+
+ BM_ITER_MESH_INDEX (v, &iter, pbvh->bm, BM_VERTS_OF_MESH, i) {
+ while (vnodemap[i]->parent) {
+ vnodemap[i] = vnodemap[i]->parent;
+ }
+ }
+ }
+
+ BLI_mempool_iter loopiter;
+ BLI_mempool_iternew(pbvh->bm->lpool, &loopiter);
+ BMLoop *l = BLI_mempool_iterstep(&loopiter);
+ BMEdge *e;
+ BMFace *f;
+
+ for (i = 0; l; l = BLI_mempool_iterstep(&loopiter), i++) {
+ l->head.hflag &= ~flag;
+ }
+ BM_ITER_MESH (e, &iter, pbvh->bm, BM_EDGES_OF_MESH) {
+ e->head.hflag &= ~flag;
+ }
+
+ BM_ITER_MESH (f, &iter, pbvh->bm, BM_FACES_OF_MESH) {
+ f->head.hflag &= ~flag;
+ }
+
+ int totroot = BLI_array_len(roots);
+ ReVertNode **nstack = NULL;
+ BLI_array_declare(nstack);
+ int vorder = 0, eorder = 0, lorder = 0, forder = 0;
+
+ for (i = 0; i < totroot; i++) {
+ BLI_array_clear(nstack);
+
+ ReVertNode *node = roots[i];
+ BLI_array_append(nstack, node);
+
+ while (BLI_array_len(nstack) > 0) {
+ ReVertNode *node2 = BLI_array_pop(nstack);
+
+ 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->bm->vdata, CD_PROP_COLOR);
+
+ if (cd_vcol >= 0) {
+ MPropCol *col = BM_ELEM_CD_GET_VOID_P(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++) {
+ BLI_array_append(nstack, node2->children[j]);
+ }
+ }
+ }
+ }
+
+ uint *vidx, *eidx, *lidx, *fidx;
+
+ vidx = MEM_malloc_arrayN(pbvh->bm->totvert, sizeof(*vidx), "vorder");
+ eidx = MEM_malloc_arrayN(pbvh->bm->totedge, sizeof(*eidx), "eorder");
+ lidx = MEM_malloc_arrayN(pbvh->bm->totloop, sizeof(*lidx), "lorder");
+ fidx = MEM_malloc_arrayN(pbvh->bm->totface, sizeof(*fidx), "forder");
+
+ printf("v %d %d\n", vorder, pbvh->bm->totvert);
+ printf("e %d %d\n", eorder, pbvh->bm->totedge);
+ printf("l %d %d\n", lorder, pbvh->bm->totloop);
+ printf("f %d %d\n", forder, pbvh->bm->totface);
+
+ BM_ITER_MESH_INDEX (v, &iter, pbvh->bm, BM_VERTS_OF_MESH, i) {
+ vidx[i] = (uint)v->head.index;
+ }
+
+ BM_ITER_MESH_INDEX (e, &iter, pbvh->bm, BM_EDGES_OF_MESH, i) {
+ eidx[i] = (uint)e->head.index;
+ }
+ BM_ITER_MESH_INDEX (f, &iter, pbvh->bm, BM_FACES_OF_MESH, i) {
+ fidx[i] = (uint)f->head.index;
+ }
+
+ BLI_mempool_iternew(pbvh->bm->lpool, &loopiter);
+ l = BLI_mempool_iterstep(&loopiter);
+
+ for (i = 0; l; l = 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", BLI_array_len(roots));
-#ifdef USE_VERIFY
+ BM_mesh_remap(pbvh->bm, vidx, eidx, fidx, lidx);
-static void pbvh_bmesh_verify(PBVH *pbvh)
+ 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->bm;
+}
+
+BMesh *BKE_pbvh_reorder_bmesh2(PBVH *pbvh)
{
- /* build list of faces & verts to lookup */
- GSet *faces_all = BLI_gset_ptr_new_ex(__func__, pbvh->bm->totface);
+ if (BKE_pbvh_type(pbvh) != PBVH_BMESH || pbvh->totnode == 0) {
+ return pbvh->bm;
+ }
+
+ // try to group memory allocations by node
+ struct {
+ BMEdge **edges;
+ int totedge;
+ BMVert **verts;
+ int totvert;
+ BMFace **faces;
+ int totface;
+ } *nodedata = MEM_callocN(sizeof(*nodedata) * pbvh->totnode, "nodedata");
+
BMIter iter;
+ int types[3] = {BM_VERTS_OF_MESH, BM_EDGES_OF_MESH, BM_FACES_OF_MESH};
- {
- BMFace *f;
- BM_ITER_MESH (f, &iter, pbvh->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);
+#define VISIT_TAG BM_ELEM_TAG
+
+ BM_mesh_elem_index_ensure(pbvh->bm, BM_VERT | BM_EDGE | BM_FACE);
+ BM_mesh_elem_table_ensure(pbvh->bm, BM_VERT | BM_EDGE | BM_FACE);
+
+ for (int i = 0; i < 3; i++) {
+ BMHeader *elem;
+
+ BM_ITER_MESH (elem, &iter, pbvh->bm, types[i]) {
+ elem->hflag &= ~VISIT_TAG;
}
}
- GSet *verts_all = BLI_gset_ptr_new_ex(__func__, pbvh->bm->totvert);
- {
+ for (int i = 0; i < pbvh->totnode; i++) {
+ PBVHNode *node = pbvh->nodes + i;
+
+ if (!(node->flag & PBVH_Leaf)) {
+ continue;
+ }
+
+ BMVert **verts = nodedata[i].verts;
+ BMEdge **edges = nodedata[i].edges;
+ BMFace **faces = nodedata[i].faces;
+
+ BLI_array_declare(verts);
+ BLI_array_declare(edges);
+ BLI_array_declare(faces);
+
BMVert *v;
- BM_ITER_MESH (v, &iter, pbvh->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);
+ BMFace *f;
+
+ TGSET_ITER (v, node->bm_unique_verts) {
+ if (v->head.hflag & VISIT_TAG) {
+ continue;
}
+
+ v->head.hflag |= VISIT_TAG;
+ BLI_array_append(verts, v);
+
+ BMEdge *e = v->e;
+ do {
+ if (!(e->head.hflag & VISIT_TAG)) {
+ e->head.hflag |= VISIT_TAG;
+ BLI_array_append(edges, 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;
+ }
+
+ BLI_array_append(faces, f);
+ f->head.hflag |= VISIT_TAG;
+ }
+ TGSET_ITER_END;
+
+ nodedata[i].verts = verts;
+ nodedata[i].edges = edges;
+ nodedata[i].faces = faces;
+
+ nodedata[i].totvert = BLI_array_len(verts);
+ nodedata[i].totedge = BLI_array_len(edges);
+ nodedata[i].totface = BLI_array_len(faces);
}
- /* 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;
+ BMAllocTemplate templ = {
+ pbvh->bm->totvert, pbvh->bm->totedge, pbvh->bm->totloop, pbvh->bm->totface};
+ struct BMeshCreateParams params = {0};
+
+ BMesh *bm2 = BM_mesh_create(&templ, &params);
+
+ CustomData_copy_all_layout(&pbvh->bm->vdata, &bm2->vdata);
+ CustomData_copy_all_layout(&pbvh->bm->edata, &bm2->edata);
+ CustomData_copy_all_layout(&pbvh->bm->ldata, &bm2->ldata);
+ CustomData_copy_all_layout(&pbvh->bm->pdata, &bm2->pdata);
+
+ CustomData_bmesh_init_pool(&bm2->vdata, pbvh->bm->totvert, BM_VERT);
+ CustomData_bmesh_init_pool(&bm2->edata, pbvh->bm->totedge, BM_EDGE);
+ CustomData_bmesh_init_pool(&bm2->ldata, pbvh->bm->totloop, BM_LOOP);
+ CustomData_bmesh_init_pool(&bm2->pdata, pbvh->bm->totface, BM_FACE);
+
+ BMVert **verts = NULL;
+ BMEdge **edges = NULL;
+ BMFace **faces = NULL;
+ BLI_array_declare(verts);
+ BLI_array_declare(edges);
+ BLI_array_declare(faces);
+
+ for (int i = 0; i < pbvh->totnode; i++) {
+ for (int j = 0; j < nodedata[i].totvert; j++) {
+ BMVert *v1 = nodedata[i].verts[j];
+ BMVert *v2 = BM_vert_create(bm2, v1->co, NULL, BM_CREATE_NOP);
+ BM_elem_attrs_copy_ex(pbvh->bm, bm2, v1, v2, 0, 0L);
+
+ v2->head.index = v1->head.index = BLI_array_len(verts);
+ BLI_array_append(verts, v2);
}
+ }
- BLI_assert(totface == BLI_gset_len(faces_all));
- BLI_assert(totvert == BLI_gset_len(verts_all));
+ for (int i = 0; i < pbvh->totnode; i++) {
+ for (int j = 0; j < nodedata[i].totedge; j++) {
+ BMEdge *e1 = nodedata[i].edges[j];
+ BMEdge *e2 = BM_edge_create(
+ bm2, verts[e1->v1->head.index], verts[e1->v2->head.index], NULL, BM_CREATE_NOP);
+ BM_elem_attrs_copy_ex(pbvh->bm, bm2, e1, e2, 0, 0L);
+
+ e2->head.index = e1->head.index = BLI_array_len(edges);
+ BLI_array_append(edges, e2);
+ }
}
- {
- BMFace *f;
- BM_ITER_MESH (f, &iter, pbvh->bm, BM_FACES_OF_MESH) {
- BMIter bm_iter;
- BMVert *v;
- PBVHNode *n = pbvh_bmesh_node_lookup(pbvh, f);
+ BMVert **fvs = NULL;
+ BMEdge **fes = NULL;
+ BLI_array_declare(fvs);
+ BLI_array_declare(fes);
- /* Check that the face's node is a leaf */
- BLI_assert(n->flag & PBVH_Leaf);
+ for (int i = 0; i < pbvh->totnode; i++) {
+ for (int j = 0; j < nodedata[i].totface; j++) {
+ BMFace *f1 = nodedata[i].faces[j];
- /* Check that the face's node knows it owns the face */
- BLI_assert(BLI_gset_haskey(n->bm_faces, f));
+ BLI_array_clear(fvs);
+ BLI_array_clear(fes);
- /* Check the face's vertices... */
- BM_ITER_ELEM (v, &bm_iter, f, BM_VERTS_OF_FACE) {
- PBVHNode *nv;
+ int totloop = 0;
+ BMLoop *l1 = f1->l_first;
+ do {
+ BLI_array_append(fvs, verts[l1->v->head.index]);
+ BLI_array_append(fes, edges[l1->e->head.index]);
+ l1 = l1->next;
+ totloop++;
+ } while (l1 != f1->l_first);
- /* 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));
+ BMFace *f2 = BM_face_create(bm2, fvs, fes, totloop, NULL, BM_CREATE_NOP);
+ f1->head.index = f2->head.index = BLI_array_len(faces);
+ BLI_array_append(faces, f2);
- /* Check that the vertex has a node owner */
- nv = pbvh_bmesh_node_lookup(pbvh, v);
+ // CustomData_bmesh_copy_data(&pbvh->bm->pdata, &bm2->pdata, f1->head.data,
+ // &f2->head.data);
+ BM_elem_attrs_copy_ex(pbvh->bm, bm2, f1, f2, 0, 0L);
- /* Check that the vertex's node knows it owns the vert */
- BLI_assert(BLI_gset_haskey(nv->bm_unique_verts, v));
+ BMLoop *l2 = f2->l_first;
+ do {
+ BM_elem_attrs_copy_ex(pbvh->bm, bm2, l1, l2, 0, 0L);
- /* Check that the vertex isn't duplicated as an 'other' vert */
- BLI_assert(!BLI_gset_haskey(nv->bm_other_verts, v));
- }
+ l1 = l1->next;
+ l2 = l2->next;
+ } while (l2 != f2->l_first);
}
}
- /* Check verts */
- {
+ 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;
- BM_ITER_MESH (v, &iter, pbvh->bm, BM_VERTS_OF_MESH) {
- /* vertex isn't tracked */
- if (BM_ELEM_CD_GET_INT(v, pbvh->cd_vert_node_offset) == DYNTOPO_NODE_NONE) {
- continue;
- }
+ BMFace *f;
- PBVHNode *n = pbvh_bmesh_node_lookup(pbvh, v);
+ 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;
- /* Check that the vert's node is a leaf */
- BLI_assert(n->flag & PBVH_Leaf);
+ BLI_table_gset_free(node->bm_faces, NULL);
+ BLI_table_gset_free(node->bm_other_verts, NULL);
+ BLI_table_gset_free(node->bm_unique_verts, NULL);
- /* Check that the vert's node knows it owns the vert */
- BLI_assert(BLI_gset_haskey(n->bm_unique_verts, v));
+ node->bm_faces = bm_faces;
+ node->bm_other_verts = bm_other_verts;
+ node->bm_unique_verts = bm_unique_verts;
- /* Check that the vertex isn't duplicated as an 'other' vert */
- BLI_assert(!BLI_gset_haskey(n->bm_other_verts, v));
+ node->flag |= PBVH_UpdateTris | PBVH_UpdateRedraw;
+ }
- /* Check that the vert's node also contains one of the vert's
- * adjacent faces */
- bool found = false;
- BMIter bm_iter;
- BMFace *f = NULL;
- BM_ITER_ELEM (f, &bm_iter, v, BM_FACES_OF_VERT) {
- if (pbvh_bmesh_node_lookup(pbvh, f) == n) {
- found = true;
- break;
+ 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);
+
+ MEM_freeN(nodedata);
+
+ BM_mesh_free(pbvh->bm);
+ pbvh->bm = bm2;
+
+ return bm2;
+}
+
+typedef struct SortElem {
+ BMElem *elem;
+ int index;
+ int cd_node_off;
+} SortElem;
+
+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->bm;
+
+ int **save_other_vs = MEM_calloc_arrayN(pbvh->totnode, sizeof(int *), __func__);
+ int **save_unique_vs = MEM_calloc_arrayN(pbvh->totnode, sizeof(int *), __func__);
+ int **save_fs = MEM_calloc_arrayN(pbvh->totnode, sizeof(int *), __func__);
+
+ SortElem *verts = MEM_malloc_arrayN(bm->totvert, sizeof(SortElem), __func__);
+ SortElem *edges = MEM_malloc_arrayN(bm->totedge, sizeof(SortElem), __func__);
+ SortElem *faces = MEM_malloc_arrayN(bm->totface, sizeof(SortElem), __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++) {
+ int *other_vs = NULL;
+ int *unique_vs = NULL;
+ int *fs = NULL;
+
+ BLI_array_declare(other_vs);
+ BLI_array_declare(unique_vs);
+ BLI_array_declare(fs);
+
+ PBVHNode *node = pbvh->nodes + i;
+ if (!(node->flag & PBVH_Leaf)) {
+ continue;
+ }
+
+ BMVert *v;
+ BMFace *f;
+
+ TGSET_ITER (v, node->bm_unique_verts) {
+ BLI_array_append(unique_vs, v->head.index);
+ }
+ TGSET_ITER_END;
+ TGSET_ITER (v, node->bm_other_verts) {
+ BLI_array_append(other_vs, v->head.index);
+ }
+ TGSET_ITER_END;
+ TGSET_ITER (f, node->bm_faces) {
+ BLI_array_append(fs, 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_malloc_arrayN(bm->totvert, sizeof(int), __func__);
+ uint *es = MEM_malloc_arrayN(bm->totedge, sizeof(int), __func__);
+ uint *fs = MEM_malloc_arrayN(bm->totface, sizeof(int), __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, NULL);
+
+ // create new mappings
+ BMVert **mapvs = MEM_malloc_arrayN(bm->totvert, sizeof(BMVert *), __func__);
+ BMEdge **mapes = MEM_malloc_arrayN(bm->totedge, sizeof(BMEdge *), __func__);
+ BMFace **mapfs = MEM_malloc_arrayN(bm->totface, sizeof(BMFace *), __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, NULL);
+ BLI_table_gset_free(node->bm_other_verts, NULL);
+ BLI_table_gset_free(node->bm_faces, NULL);
+
+ 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");
+
+ int *unique_vs = save_unique_vs[i];
+ int *other_vs = save_other_vs[i];
+ int *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]]);
+ }
+
+ MEM_SAFE_FREE(save_unique_vs[i]);
+ MEM_SAFE_FREE(save_other_vs[i]);
+ MEM_SAFE_FREE(save_fs[i]);
+
+ 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);
+
+ MEM_SAFE_FREE(save_other_vs);
+ MEM_SAFE_FREE(save_unique_vs);
+ MEM_SAFE_FREE(save_fs);
+
+ return pbvh->bm;
+}
+
+// only floats! and 8 byte aligned!
+typedef struct CacheParams {
+ float vchunk, echunk, lchunk, pchunk;
+ int cluster_steps, cluster_size;
+} CacheParams;
+
+typedef struct CacheParamDef {
+ char name[32];
+ float defvalue, min, max;
+} CacheParamDef;
+
+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));
+}
+
+typedef 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;
+} MeshTest;
+
+typedef struct ElemHeader {
+ short type, hflag;
+ int index;
+ void *data;
+} ElemHeader;
+
+typedef struct MeshVert2 {
+ ElemHeader head;
+ float co[3];
+ float no[3];
+ int e;
+} MeshVert2;
+
+typedef struct MeshEdge2 {
+ ElemHeader head;
+ int v1, v2;
+ int v1_next, v2_next;
+ int l;
+} MeshEdge2;
+
+typedef struct MeshLoop2 {
+ ElemHeader head;
+ int v, e, f, next, prev;
+ int radial_next, radial_prev;
+} MeshLoop2;
+
+typedef struct MeshFace2 {
+ ElemHeader head;
+ int l, len;
+ float no[3];
+} MeshFace2;
+
+typedef struct MeshTest2 {
+ MeshVert2 *verts;
+ MeshEdge2 *edges;
+ MeshLoop2 *loops;
+ MeshFace2 *faces;
+
+ int totvert, totedge, totloop, totface;
+ MemArena *arena;
+} MeshTest2;
+
+static MeshTest2 *meshtest2_from_bm(BMesh *bm)
+{
+ MeshTest2 *m2 = 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 = MEM_calloc_arrayN(bm->totvert, sizeof(MeshVert2), "MeshVert2s");
+ m2->edges = MEM_calloc_arrayN(bm->totedge, sizeof(MeshEdge2), "MeshEdge2s");
+ m2->loops = MEM_calloc_arrayN(m2->totloop, sizeof(MeshLoop2), "MeshLoop2s");
+ m2->faces = 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_callocN(sizeof(MeshTest), "MeshTest");
+ m->arena = BLI_memarena_new(1024 * 32, "m->arena");
+
+ m->v_co = BLI_memarena_alloc(m->arena, bm->totvert * sizeof(*m->v_co));
+ m->v_no = BLI_memarena_alloc(m->arena, bm->totvert * sizeof(*m->v_no));
+ m->v_e = BLI_memarena_alloc(m->arena, bm->totvert * sizeof(*m->v_e));
+ m->v_flag = BLI_memarena_alloc(m->arena, bm->totvert * sizeof(*m->v_flag));
+ m->v_index = BLI_memarena_alloc(m->arena, bm->totvert * sizeof(*m->v_index));
+
+ m->e_v1 = BLI_memarena_alloc(m->arena, bm->totedge * sizeof(*m->e_v1));
+ m->e_v1_next = BLI_memarena_alloc(m->arena, bm->totedge * sizeof(*m->e_v1));
+ m->e_v2 = BLI_memarena_alloc(m->arena, bm->totedge * sizeof(*m->e_v1));
+ m->e_v2_next = BLI_memarena_alloc(m->arena, bm->totedge * sizeof(*m->e_v1));
+ m->e_l = BLI_memarena_alloc(m->arena, bm->totedge * sizeof(*m->e_v1));
+ m->e_index = BLI_memarena_alloc(m->arena, bm->totedge * sizeof(*m->e_v1));
+ m->e_flag = BLI_memarena_alloc(m->arena, bm->totedge * sizeof(*m->e_v1));
+
+ m->l_v = BLI_memarena_alloc(m->arena, bm->totloop * sizeof(*m->l_e));
+ m->l_e = BLI_memarena_alloc(m->arena, bm->totloop * sizeof(*m->l_e));
+ m->l_f = BLI_memarena_alloc(m->arena, bm->totloop * sizeof(*m->l_e));
+ m->l_next = BLI_memarena_alloc(m->arena, bm->totloop * sizeof(*m->l_e));
+ m->l_prev = BLI_memarena_alloc(m->arena, bm->totloop * sizeof(*m->l_e));
+ m->l_radial_next = BLI_memarena_alloc(m->arena, bm->totloop * sizeof(*m->l_e));
+ m->l_radial_prev = BLI_memarena_alloc(m->arena, bm->totloop * sizeof(*m->l_e));
+
+ m->f_l = 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;
}
- }
- BLI_assert(found || f == NULL);
-# 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));
+ 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);
}
-# endif
}
+
+ 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);
}
-# if 0
- /* check that every vert belongs somewhere */
- /* Slow */
- BM_ITER_MESH (vi, &iter, pbvh->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 != NULL) && BLI_gset_haskey(n->bm_unique_verts, vi)) {
- has_unique = true;
+ 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 : NULL;
+ 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);
}
}
- BLI_assert(has_unique);
- vert_count++;
+
+ 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);
}
- /* If totvert differs from number of verts inside the hash. hash-totvert is checked above. */
- BLI_assert(vert_count == pbvh->bm->totvert);
-# endif
+ printf("time: %.5f\n", average / average_tot);
+ return average / average_tot;
+}
- /* 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;
+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;
+ }
- 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));
+ 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};
+
+ BMesh *bm = BM_mesh_create(
+ &templ,
+ &((struct BMeshCreateParams){.id_elem_mask = BM_VERT | BM_EDGE | BM_FACE,
+ .id_map = true,
+ .create_unique_ids = true,
+ .temporary_ids = false,
+ .no_reuse_ids = false}));
+
+ // 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");
- 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));
+ float df = 1.0f / (float)steps;
+
+ int hashdimen = steps * 8;
+
+ BMVert **grid = MEM_malloc_arrayN(steps * steps, sizeof(*grid), "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(bm, &bm->pdata, CD_SCULPT_FACE_SETS);
+
+ 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);
+
+ 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 = 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;
}
+ }
- 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));
+ 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);
+ }
}
}
}
- BLI_gset_free(faces_all, NULL);
- BLI_gset_free(verts_all, NULL);
+ // 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);
+ BLI_ghash_free(vhash, NULL, NULL);
+ 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_FLOAT, "__dyntopo_face_areas");
+
+ cd_vert_node = bm->vdata.layers[cd_vert_node].offset;
+ cd_face_node = bm->pdata.layers[cd_face_node].offset;
+ cd_face_area = bm->pdata.layers[cd_face_area].offset;
+
+ const int cd_dyn_vert = CustomData_get_offset(&bm->vdata, CD_DYNTOPO_VERT);
+ BMLog *bmlog = BM_log_create(bm, cd_dyn_vert);
+
+ PBVH *pbvh = BKE_pbvh_new();
+
+ 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, bm, false, bmlog, cd_vert_node, cd_face_node, cd_dyn_vert, cd_face_area, false);
+
+ 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];
+ 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");
}
-#endif
+#include "BLI_smallhash.h"
+
+static void hash_test()
+{
+ const int count = 1024 * 1024 * 4;
+
+ int *data = MEM_callocN(sizeof(*data) * count, "test data");
+
+ const int test_steps = 5;
+ 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 = 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 = 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 = data[ri];
+
+ BLI_smallhash_insert(&sh, 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 = data[ri];
+
+ void *val = BLI_ghash_lookup(gh, ptr);
+ int ri2 = POINTER_AS_INT(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 = data[ri];
+
+ void *val = BLI_smallhash_lookup(&sh, ptr);
+ int ri2 = POINTER_AS_INT(ptr);
+ }
+ printf(" %.3f\n", PIL_check_seconds_timer() - t);
+
+ BLI_rng_free(rng);
+ BLI_ghash_free(gh, NULL, NULL);
+ BLI_smallhash_release(&sh);
+ BLI_table_gset_free(gs, NULL);
+
+ MEM_freeN(data);
+}
+
+void pbvh_bmesh_do_cache_test()
+{
+ BMesh *bm;
+ PBVH *pbvh;
+
+ CacheParams params;
+
+ 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(&params);
+ // pbvh_bmesh_cache_test(&params, &bm, &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 <stdio.h>
+#include <stdlib.h>
+
+#include <math.h>
+
+// 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_displacement.c b/source/blender/blenkernel/intern/pbvh_displacement.c
new file mode 100644
index 00000000000..d42457f4100
--- /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.h"
+
+# 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.h b/source/blender/blenkernel/intern/pbvh_intern.h
index 84c4ae4dead..9d73c3cb2db 100644
--- a/source/blender/blenkernel/intern/pbvh_intern.h
+++ b/source/blender/blenkernel/intern/pbvh_intern.h
@@ -16,9 +16,17 @@
#pragma once
+#include "BLI_compiler_compat.h"
+#include "BLI_ghash.h"
+#include "DNA_customdata_types.h"
+#include "DNA_material_types.h"
+
+#include "bmesh.h"
+
/** \file
* \ingroup bli
*/
+struct MDynTopoVert;
/* Axis-aligned bounding box */
typedef struct {
@@ -35,6 +43,10 @@ typedef struct {
struct PBVHNode {
/* Opaque handle for drawing code */
struct GPU_PBVH_Buffers *draw_buffers;
+ struct GPU_PBVH_Buffers **mat_draw_buffers; // currently only used by pbvh_bmesh
+ int tot_mat_draw_buffers;
+
+ int id;
/* Voxel bounds */
BB vb;
@@ -43,6 +55,7 @@ struct PBVHNode {
/* For internal nodes, the offset of the children in the PBVH
* 'nodes' array. */
int children_offset;
+ int subtree_tottri;
/* Pointer into the PBVH prim_indices array and the number of
* primitives used by this leaf node.
@@ -86,7 +99,7 @@ struct PBVHNode {
/* Indicates whether this node is a leaf or not; also used for
* marking various updates that need to be applied. */
- PBVHNodeFlags flag : 16;
+ PBVHNodeFlags flag : 32;
/* Used for raycasting: how close bb is to the ray point. */
float tmin;
@@ -98,27 +111,39 @@ struct PBVHNode {
PBVHProxyNode *proxies;
/* Dyntopo */
- GSet *bm_faces;
- GSet *bm_unique_verts;
- GSet *bm_other_verts;
- float (*bm_orco)[3];
- int (*bm_ortri)[3];
- int bm_tot_ortri;
+ TableGSet *bm_faces;
+ TableGSet *bm_unique_verts;
+ TableGSet *bm_other_verts;
+
+ PBVHTriBuf *tribuf; // all triangles
+ PBVHTriBuf *tri_buffers; // tribuffers, one per material used
+ int tot_tri_buffers;
+
+ int updategen;
/* Used to store the brush color during a stroke and composite it over the original color */
PBVHColorBufferNode color_buffer;
+#ifdef PROXY_ADVANCED
+ ProxyVertArray proxyverts;
+#endif
};
typedef enum {
PBVH_DYNTOPO_SMOOTH_SHADING = 1,
+ PBVH_FAST_DRAW = 2 // hides facesets/masks and forces smooth to save GPU bandwidth
} PBVHFlags;
typedef struct PBVHBMeshLog PBVHBMeshLog;
+struct DMFlagMat;
struct PBVH {
PBVHType type;
PBVHFlags flags;
+ int idgen;
+
+ bool dyntopo_stop;
+
PBVHNode *nodes;
int node_mem_count, totnode;
@@ -127,6 +152,7 @@ struct PBVH {
int totvert;
int leaf_limit;
+ int depth_limit;
/* Mesh data */
const struct Mesh *mesh;
@@ -146,7 +172,7 @@ struct PBVH {
CCGKey gridkey;
CCGElem **grids;
void **gridfaces;
- const DMFlagMat *grid_flag_mats;
+ const struct DMFlagMat *grid_flag_mats;
int totgrid;
BLI_bitmap **grid_hidden;
@@ -168,14 +194,31 @@ struct PBVH {
BMesh *bm;
float bm_max_edge_len;
float bm_min_edge_len;
+ float bm_detail_range;
+
+ int cd_dyn_vert;
int cd_vert_node_offset;
int cd_face_node_offset;
+ int cd_vert_mask_offset;
+ int cd_vcol_offset;
+ int cd_faceset_offset;
+ int cd_face_area;
float planes[6][4];
int num_planes;
+ int symmetry;
+ int boundary_symmetry;
+
struct 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
+ struct MDynTopoVert *mdyntopo_verts;
};
/* pbvh.c */
@@ -184,6 +227,9 @@ void BB_expand(BB *bb, const float co[3]);
void BB_expand_with_bb(BB *bb, BB *bb2);
void BBC_update_centroid(BBC *bbc);
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],
struct IsectRayPrecalc *isect_precalc,
@@ -217,20 +263,97 @@ 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.c */
-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],
struct IsectRayPrecalc *isect_precalc,
- float *dist,
+ int *hit_count,
+ float *depth,
+ float *back_depth,
bool use_original,
- int *r_active_vertex_index,
- float *r_face_normal);
-bool pbvh_bmesh_node_nearest_to_ray(PBVHNode *node,
+ SculptVertRef *r_active_vertex_index,
+ SculptFaceRef *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_free_all_draw_buffers(PBVHNode *node);
+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);
+void pbvh_bmesh_check_nodes(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_dyn_vert,
+ int cd_faceset_offset,
+ BMVert *v,
+ int bound_symmetry);
+
+BLI_INLINE bool pbvh_check_vert_boundary(PBVH *pbvh, struct BMVert *v)
+{
+ MDynTopoVert *mv = (MDynTopoVert *)BM_ELEM_CD_GET_VOID_P(v, pbvh->cd_dyn_vert);
+
+ if (mv->flag & DYNVERT_NEED_BOUNDARY) {
+ bke_pbvh_update_vert_boundary(
+ pbvh->cd_dyn_vert, pbvh->cd_faceset_offset, v, pbvh->boundary_symmetry);
+ return true;
+ }
+
+ return false;
+}
+
+void pbvh_bmesh_check_other_verts(PBVHNode *node);
+
+//#define DEFRAGMENT_MEMORY
diff --git a/source/blender/blenkernel/intern/scene.c b/source/blender/blenkernel/intern/scene.c
index 6b5c94a2786..74fc5e6de7a 100644
--- a/source/blender/blenkernel/intern/scene.c
+++ b/source/blender/blenkernel/intern/scene.c
@@ -69,6 +69,7 @@
#include "BKE_anim_data.h"
#include "BKE_animsys.h"
#include "BKE_armature.h"
+#include "BKE_brush_engine.h"
#include "BKE_cachefile.h"
#include "BKE_collection.h"
#include "BKE_colortools.h"
@@ -849,6 +850,11 @@ 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->channels) {
+ BKE_brush_channelset_write(writer, tos->sculpt->channels);
+ }
+
BKE_paint_blend_write(writer, &tos->sculpt->paint);
}
if (tos->uvsculpt) {
@@ -1050,6 +1056,11 @@ static void scene_blend_read_data(BlendDataReader *reader, ID *id)
sce->toolsettings->particle.object = NULL;
sce->toolsettings->gp_sculpt.paintcursor = NULL;
+ if (sce->toolsettings->sculpt && sce->toolsettings->sculpt->channels) {
+ BLO_read_data_address(reader, &sce->toolsettings->sculpt->channels);
+ BKE_brush_channelset_read(reader, sce->toolsettings->sculpt->channels);
+ }
+
/* relink grease pencil interpolation curves */
BLO_read_data_address(reader, &sce->toolsettings->gp_interpolate.custom_ipo);
if (sce->toolsettings->gp_interpolate.custom_ipo) {
@@ -2393,6 +2404,7 @@ static void prepare_mesh_for_viewport_render(Main *bmain, const ViewLayer *view_
if (check_rendered_viewport_visible(bmain)) {
BMesh *bm = mesh->edit_mesh->bm;
BM_mesh_bm_to_me(bmain,
+ NULL,
bm,
mesh,
(&(struct BMeshToMeshParams){
diff --git a/source/blender/blenkernel/intern/subdiv_displacement_multires.c b/source/blender/blenkernel/intern/subdiv_displacement_multires.c
index 0fb08880dd5..65d93ff126e 100644
--- a/source/blender/blenkernel/intern/subdiv_displacement_multires.c
+++ b/source/blender/blenkernel/intern/subdiv_displacement_multires.c
@@ -360,6 +360,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/subsurf_ccg.c b/source/blender/blenkernel/intern/subsurf_ccg.c
index a1b45c2ac7d..5e7c882bbd3 100644
--- a/source/blender/blenkernel/intern/subsurf_ccg.c
+++ b/source/blender/blenkernel/intern/subsurf_ccg.c
@@ -1866,11 +1866,14 @@ static const MeshElemMap *ccgDM_getPolyMap(Object *ob, DerivedMesh *dm)
BKE_mesh_vert_poly_map_create(&ccgdm->pmap,
&ccgdm->pmap_mem,
+ me->mvert,
+ me->medge,
me->mpoly,
me->mloop,
me->totvert,
me->totpoly,
- me->totloop);
+ me->totloop,
+ false);
}
return ccgdm->pmap;
diff --git a/source/blender/blenlib/BLI_asan.h b/source/blender/blenlib/BLI_asan.h
index c38ad6b39d0..b26e3680e42 100644
--- a/source/blender/blenlib/BLI_asan.h
+++ b/source/blender/blenlib/BLI_asan.h
@@ -21,7 +21,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. */
@@ -40,3 +40,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_compiler_attrs.h b/source/blender/blenlib/BLI_compiler_attrs.h
index 4b5a7d671f2..00034b31e52 100644
--- a/source/blender/blenlib/BLI_compiler_attrs.h
+++ b/source/blender/blenlib/BLI_compiler_attrs.h
@@ -33,6 +33,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)))
@@ -99,6 +105,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_ghash.h b/source/blender/blenlib/BLI_ghash.h
index a2c5c6349a5..50592d75629 100644
--- a/source/blender/blenlib/BLI_ghash.h
+++ b/source/blender/blenlib/BLI_ghash.h
@@ -188,6 +188,53 @@ 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);
+
+#define TGSET_ITER(v, ts) \
+ { \
+ int _i1; \
+ for (_i1 = 0; _i1 < (ts)->cur; _i1++) { \
+ if (!(ts)->elems[_i1]) \
+ continue; \
+ v = (ts)->elems[_i1];
+
+#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 = (ts)->elems[_i1]; \
+ index++;
+
+#define TGSET_ITER_INDEX_END \
+ } \
+ }
+
typedef GHashHashFP GSetHashFP;
typedef GHashCmpFP GSetCmpFP;
typedef GHashKeyFreeFP GSetKeyFreeFP;
diff --git a/source/blender/blenlib/BLI_linklist_stack.h b/source/blender/blenlib/BLI_linklist_stack.h
index d07bc40c923..7f1f52fd691 100644
--- a/source/blender/blenlib/BLI_linklist_stack.h
+++ b/source/blender/blenlib/BLI_linklist_stack.h
@@ -52,7 +52,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_mempool.h b/source/blender/blenlib/BLI_mempool.h
index 61b572a4943..4093b9b5fe0 100644
--- a/source/blender/blenlib/BLI_mempool.h
+++ b/source/blender/blenlib/BLI_mempool.h
@@ -87,11 +87,35 @@ 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)
};
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_smallhash.h b/source/blender/blenlib/BLI_smallhash.h
index f7aec716e9e..4a1bf87cb53 100644
--- a/source/blender/blenlib/BLI_smallhash.h
+++ b/source/blender/blenlib/BLI_smallhash.h
@@ -39,11 +39,13 @@ 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 using_stack;
} SmallHash;
typedef struct {
@@ -57,22 +59,25 @@ void BLI_smallhash_release(SmallHash *sh) ATTR_NONNULL(1);
void BLI_smallhash_insert(SmallHash *sh, uintptr_t key, void *item) ATTR_NONNULL(1);
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)
- ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT;
-void **BLI_smallhash_lookup_p(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;
-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
double BLI_smallhash_calc_quality(SmallHash *sh);
#endif
diff --git a/source/blender/blenlib/BLI_strict_flags.h b/source/blender/blenlib/BLI_strict_flags.h
index b967d7e494d..ab7aacc4d2d 100644
--- a/source/blender/blenlib/BLI_strict_flags.h
+++ b/source/blender/blenlib/BLI_strict_flags.h
@@ -45,10 +45,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/CMakeLists.txt b/source/blender/blenlib/CMakeLists.txt
index 1eaf007e01b..46d0ee1fc03 100644
--- a/source/blender/blenlib/CMakeLists.txt
+++ b/source/blender/blenlib/CMakeLists.txt
@@ -153,6 +153,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
diff --git a/source/blender/blenlib/intern/BLI_mempool.c b/source/blender/blenlib/intern/BLI_mempool.c
index f968799326a..67a9327f343 100644
--- a/source/blender/blenlib/intern/BLI_mempool.c
+++ b/source/blender/blenlib/intern/BLI_mempool.c
@@ -36,6 +36,7 @@
#include "BLI_utildefines.h"
+#include "BLI_asan.h"
#include "BLI_mempool.h" /* own include */
#include "BLI_mempool_private.h" /* own include */
@@ -47,6 +48,12 @@
# include "valgrind/memcheck.h"
#endif
+#if (defined(__SANITIZE_ADDRESS__) || __has_feature(address_sanitizer))
+# define POISON_REDZONE_SIZE 32
+#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 */
@@ -117,6 +124,12 @@ struct BLI_mempool {
* 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. */
@@ -161,6 +174,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) {
@@ -202,6 +242,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;
@@ -222,22 +282,42 @@ 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);
+
+ 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);
+ 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);
+ BLI_freenode *prev = NODE_STEP_PREV(curnode);
+ BLI_asan_poison(curnode, pool->esize);
+
curnode = NODE_STEP_PREV(curnode);
+
+ BLI_asan_unpoison(curnode, pool->esize);
curnode->next = NULL;
+ BLI_asan_poison(curnode, pool->esize);
#ifdef USE_TOTALLOC
pool->totalloc += pool->pchunk;
@@ -245,24 +325,128 @@ 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);
last_tail->next = CHUNK_DATA(mpchunk);
+ BLI_asan_poison(last_tail, pool->esize);
}
return curnode;
}
-static void mempool_chunk_free(BLI_mempool_chunk *mpchunk)
+/*
+This preallocates a mempool suitable for threading. totelem 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 totelem,
+ const int pchunk,
+ void ***r_chunks,
+ int *r_totchunk,
+ int *r_esize,
+ int flag)
+{
+ BLI_mempool *pool = BLI_mempool_create(esize, 0, (uint)pchunk, (uint)flag);
+
+ // override pchunk, may not be a power of 2
+ pool->pchunk = (uint)pchunk;
+ pool->csize = (uint)pchunk * pool->esize;
+
+ if (totelem % pchunk == 0) {
+ pool->maxchunks = (uint)totelem / (uint)pchunk;
+ }
+ else {
+ pool->maxchunks = (uint)totelem / (uint)pchunk + 1;
+ }
+
+ if (totelem) {
+ 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 = totalloc;
+ pool->free = NULL;
+
+ int i = (int)pool->pchunk - 1;
+
+ while (lastchunk && totalloc > (uint)totelem) {
+ 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(BLI_mempool_chunk *mpchunk, BLI_mempool *pool)
{
+ BLI_asan_unpoison(mpchunk, sizeof(BLI_mempool_chunk) + pool->esize * pool->csize);
MEM_freeN(mpchunk);
}
-static void mempool_chunk_free_all(BLI_mempool_chunk *mpchunk)
+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);
}
}
@@ -275,6 +459,9 @@ BLI_mempool *BLI_mempool_create(uint esize, uint totelem, uint pchunk, uint flag
/* allocate the pool structure */
pool = MEM_mallocN(sizeof(BLI_mempool), "memory pool");
+ pool->totchunk = 0;
+ pool->chunktable = NULL;
+
/* set the elem size */
if (esize < (int)MEMPOOL_ELEM_SIZE_MIN) {
esize = (int)MEMPOOL_ELEM_SIZE_MIN;
@@ -284,6 +471,8 @@ BLI_mempool *BLI_mempool_create(uint esize, uint totelem, uint pchunk, uint flag
esize = MAX2(esize, (uint)sizeof(BLI_freenode));
}
+ esize += POISON_REDZONE_SIZE;
+
maxchunks = mempool_maxchunks(totelem, pchunk);
pool->chunks = NULL;
@@ -344,6 +533,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) {
@@ -363,10 +554,88 @@ void *BLI_mempool_alloc(BLI_mempool *pool)
void *BLI_mempool_calloc(BLI_mempool *pool)
{
void *retval = BLI_mempool_alloc(pool);
- memset(retval, 0, (size_t)pool->esize);
+ memset(retval, 0, (size_t)pool->esize - POISON_REDZONE_SIZE);
return retval;
}
+int BLI_mempool_find_real_index(BLI_mempool *pool, void *ptr)
+{
+ BLI_mempool_chunk *chunk = pool->chunks;
+ uintptr_t uptr = 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) {
+ return -1; // failed
+ }
+
+ return chunki * (int)pool->pchunk + ((int)(uptr - cptr)) / (int)pool->esize;
+}
+
+/*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)
+{
+ int istart = idx - range, iend = idx + range;
+ istart = MAX2(istart, 0);
+
+ int totelem = 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);
+
+ BLI_freenode *fnode = (BLI_freenode *)ptr;
+ if (fnode->freeword == FREEWORD) {
+ BLI_asan_poison(ptr, pool->esize);
+ continue;
+ }
+
+ r_elems[totelem++] = ptr;
+
+ if (totelem == r_elems_size) {
+ break;
+ }
+ }
+
+ return totelem;
+}
+
+int BLI_mempool_get_size(BLI_mempool *pool)
+{
+ BLI_mempool_chunk *chunk = pool->chunks;
+ int ret = 0;
+
+ while (chunk) {
+ chunk = chunk->next;
+
+ ret += (int)pool->pchunk;
+ }
+
+ return ret;
+}
+
/**
* Free an element from the mempool.
*
@@ -393,7 +662,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
@@ -408,6 +677,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--;
#ifdef WITH_MEM_VALGRIND
@@ -422,10 +693,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
@@ -440,11 +713,21 @@ 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);
+ 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);
+ BLI_freenode *prev = NODE_STEP_PREV(curnode);
+ BLI_asan_poison(curnode, pool->esize);
+
+ curnode = prev;
+
+ BLI_asan_unpoison(curnode, pool->esize);
curnode->next = NULL; /* terminate the list */
+ BLI_asan_poison(curnode, pool->esize);
#ifdef WITH_MEM_VALGRIND
VALGRIND_MEMPOOL_FREE(pool, CHUNK_DATA(first));
@@ -510,7 +793,7 @@ 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;
+ 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);
@@ -638,12 +921,16 @@ void *BLI_mempool_iterstep(BLI_mempool_iter *iter)
return NULL;
}
+ intptr_t freeword = 0;
+
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);
}
@@ -651,7 +938,13 @@ 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);
+ }
+ return ret2;
}
curnode = CHUNK_DATA(iter->curchunk);
}
@@ -678,6 +971,8 @@ void *mempool_iter_threadsafe_step(BLI_mempool_threadsafe_iter *ts_iter)
do {
ret = curnode;
+ BLI_asan_unpoison(ret, esize - POISON_REDZONE_SIZE);
+
if (++iter->curindex != iter->pool->pchunk) {
curnode = POINTER_OFFSET(curnode, esize);
}
@@ -693,17 +988,37 @@ 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);
+ return NULL;
+ }
+ else {
+ 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);
+ return NULL;
+ }
+ else {
+ 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);
return ret;
}
@@ -747,7 +1062,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));
}
@@ -781,7 +1096,9 @@ void BLI_mempool_clear(BLI_mempool *pool)
*/
void BLI_mempool_destroy(BLI_mempool *pool)
{
- mempool_chunk_free_all(pool->chunks);
+ mempool_chunk_free_all(pool->chunks, pool);
+
+ MEM_SAFE_FREE(pool->chunktable);
#ifdef WITH_MEM_VALGRIND
VALGRIND_DESTROY_MEMPOOL(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..2abeabd7fbf
--- /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(&ts->ptr_to_idx);
+
+ 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 = 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), 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), elem);
+ if (!idx) {
+ return;
+ }
+
+ int idx2 = *idx;
+
+ BLI_smallhash_remove(PTR_TO_IDX(ts), 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), elem);
+}
+
+int BLI_table_gset_len(TableGSet *ts)
+{
+ return ts->length;
+}
diff --git a/source/blender/blenlib/intern/smallhash.c b/source/blender/blenlib/intern/smallhash.c
index 006a3798dcd..ddeeb5f69dc 100644
--- a/source/blender/blenlib/intern/smallhash.c
+++ b/source/blender/blenlib/intern/smallhash.c
@@ -55,20 +55,56 @@
#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))
+#ifdef BLI_asan_poison
+# undef BLI_asan_poison
+#endif
+#ifdef BLI_asan_unpoison
+# undef BLI_asan_unpoison
+#endif
+
+#define BLI_asan_poison(a, b)
+#define BLI_asan_unpoison(a, b)
+
+/* 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 (uintptr_t)(MAKE_ID_8('s', 'm', 'h', 'c', 'f', 'r', 'e', 'e'))
+#define SMHASH_CELL_UNUSED (uintptr_t)(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 *), \
+ (CHECK_TYPE_INLINE(&(h), uintptr_t *), \
+ CHECK_TYPE_INLINE(&(hoff), uintptr_t *), \
((h) + (((hoff) = ((hoff)*2) + 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)
{
@@ -82,28 +118,46 @@ 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)
+BLI_INLINE uintptr_t smallhash_key(const uintptr_t key)
{
- return (uint)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
}
/**
* 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)
{
uint i;
+ BLI_asan_unpoison(&sh->buckets, sizeof(void *));
+
for (i = 0; i < sh->nbuckets; i++) {
sh->buckets[i].key = SMHASH_KEY_UNUSED;
sh->buckets[i].val = SMHASH_CELL_FREE;
}
+
+ BLI_asan_poison(&sh->buckets, sizeof(void *));
}
/**
@@ -111,19 +165,24 @@ 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);
+ BLI_asan_unpoison(&sh->buckets, sizeof(void *));
+
/* 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;
@@ -135,25 +194,37 @@ BLI_INLINE SmallHashEntry *smallhash_lookup(const SmallHash *sh, const uintptr_t
}
}
+ BLI_asan_poison(&sh->buckets, sizeof(void *));
+
return NULL;
}
BLI_INLINE SmallHashEntry *smallhash_lookup_first_free(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_asan_unpoison(&sh->buckets, sizeof(void *));
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 */
}
+ BLI_asan_poison(&sh->buckets, sizeof(void *));
+
return e;
}
BLI_INLINE void smallhash_resize_buckets(SmallHash *sh, const uint nbuckets)
{
+ check_stack_move(sh);
+
+ BLI_asan_unpoison(&sh->buckets, sizeof(void *));
+
SmallHashEntry *buckets_old = sh->buckets;
const uint nbuckets_old = sh->nbuckets;
const bool was_alloc = (buckets_old != sh->buckets_stack);
@@ -169,21 +240,29 @@ 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;
+ BLI_asan_poison(&sh->buckets, sizeof(void *));
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);
}
}
@@ -194,15 +273,23 @@ 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;
+ BLI_asan_poison(&sh->buckets, sizeof(void *));
+
if (nentries_reserve) {
smallhash_buckets_reserve(sh, nentries_reserve);
if (sh->nbuckets > SMSTACKSIZE) {
+ BLI_asan_unpoison(&sh->buckets, sizeof(void *));
sh->buckets = MEM_mallocN(sizeof(*sh->buckets) * sh->nbuckets, __func__);
+ BLI_asan_poison(&sh->buckets, sizeof(void *));
+
+ sh->using_stack = false;
}
}
@@ -217,24 +304,85 @@ void BLI_smallhash_init(SmallHash *sh)
/* NOTE: does *not* free *sh itself! only the direct data! */
void BLI_smallhash_release(SmallHash *sh)
{
- if (sh->buckets != sh->buckets_stack) {
+ check_stack_move(sh);
+
+ BLI_asan_unpoison(&sh->buckets, sizeof(void *));
+
+ 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);
+
+ BLI_asan_unpoison(&sh->buckets, sizeof(void *));
+
+ /* 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]) {
+ if (e->key == key) {
+ /* should never happen because unused keys are zero'd */
+ BLI_assert(e->val != SMHASH_CELL_UNUSED);
+ break;
+ }
+ }
+
+ BLI_asan_poison(&sh->buckets, sizeof(void *));
+
+ bool ret;
+
+ if (e->val == SMHASH_CELL_FREE || e->val == SMHASH_CELL_UNUSED) {
+ sh->nentries++;
+ sh->nfreecells--;
+ ret = false;
+ }
+ 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;
}
@@ -261,9 +409,46 @@ 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 % 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 */
+ 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--;
@@ -276,34 +461,54 @@ 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);
+
+ BLI_asan_unpoison(&sh->buckets, sizeof(void *));
+
+ 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;
+
+ BLI_asan_poison(&sh->buckets, sizeof(void *));
+}
+
+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;
}
BLI_INLINE SmallHashEntry *smallhash_iternext(SmallHashIter *iter, uintptr_t *key)
{
+ BLI_asan_unpoison(&iter->sh->buckets, sizeof(void *));
+
while (iter->i < iter->sh->nbuckets) {
if (smallhash_val_is_used(iter->sh->buckets[iter->i].val)) {
if (key) {
@@ -316,6 +521,7 @@ BLI_INLINE SmallHashEntry *smallhash_iternext(SmallHashIter *iter, uintptr_t *ke
iter->i++;
}
+ BLI_asan_poison(&iter->sh->buckets, sizeof(void *));
return NULL;
}
@@ -333,16 +539,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;
@@ -408,8 +618,8 @@ 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]) {
diff --git a/source/blender/blenlib/intern/task_pool.cc b/source/blender/blenlib/intern/task_pool.cc
index cbb5bf34477..b9e9db6e697 100644
--- a/source/blender/blenlib/intern/task_pool.cc
+++ b/source/blender/blenlib/intern/task_pool.cc
@@ -90,7 +90,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/intern/versioning_260.c b/source/blender/blenloader/intern/versioning_260.c
index b71dd5a27bb..6a41e0c7731 100644
--- a/source/blender/blenloader/intern/versioning_260.c
+++ b/source/blender/blenloader/intern/versioning_260.c
@@ -2420,7 +2420,7 @@ void blo_do_versions_260(FileData *fd, Library *UNUSED(lib), Main *bmain)
/* this can now be turned off */
ToolSettings *ts = scene->toolsettings;
if (ts->sculpt) {
- ts->sculpt->flags |= SCULPT_DYNTOPO_SUBDIVIDE;
+ ts->sculpt->flags |= SCULPT_DYNTOPO_SUBDIVIDE | SCULPT_DYNTOPO_CLEANUP;
}
/* 'Increment' mode disabled for nodes, use true grid snapping instead */
diff --git a/source/blender/blenloader/intern/versioning_280.c b/source/blender/blenloader/intern/versioning_280.c
index c0cc0819100..85e6dcbfc2d 100644
--- a/source/blender/blenloader/intern/versioning_280.c
+++ b/source/blender/blenloader/intern/versioning_280.c
@@ -4425,7 +4425,7 @@ 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);
+ ups->flag &= ~(UNIFIED_PAINT_FLAG_UNUSED_1);
}
/* Set the default render pass in the viewport to Combined. */
diff --git a/source/blender/blenloader/intern/versioning_290.c b/source/blender/blenloader/intern/versioning_290.c
index 046f3753812..c584e871fe8 100644
--- a/source/blender/blenloader/intern/versioning_290.c
+++ b/source/blender/blenloader/intern/versioning_290.c
@@ -32,6 +32,7 @@
#include "DNA_cachefile_types.h"
#include "DNA_collection_types.h"
#include "DNA_constraint_types.h"
+#include "DNA_defaults.h"
#include "DNA_fluid_types.h"
#include "DNA_genfile.h"
#include "DNA_gpencil_modifier_types.h"
@@ -56,6 +57,7 @@
#include "BKE_armature.h"
#include "BKE_brush.h"
#include "BKE_attribute.h"
+#include "BKE_brush.h"
#include "BKE_collection.h"
#include "BKE_colortools.h"
#include "BKE_cryptomatte.h"
@@ -1941,6 +1943,13 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain)
}
}
}
+
+ if (!DNA_struct_elem_find(fd->filesdna, "Light", "float", "diff_fac")) {
+ LISTBASE_FOREACH (Light *, light, &bmain->lights) {
+ light->diff_fac = 1.0f;
+ light->volume_fac = 1.0f;
+ }
+ }
}
if (!MAIN_VERSION_ATLEAST(bmain, 293, 15)) {
diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c
index 538634f4c9e..33927ad800f 100644
--- a/source/blender/blenloader/intern/versioning_300.c
+++ b/source/blender/blenloader/intern/versioning_300.c
@@ -48,6 +48,7 @@
#include "BKE_action.h"
#include "BKE_animsys.h"
#include "BKE_asset.h"
+#include "BKE_brush.h"
#include "BKE_collection.h"
#include "BKE_deform.h"
#include "BKE_fcurve.h"
@@ -1229,6 +1230,95 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain)
}
}
+ if (!MAIN_VERSION_ATLEAST(bmain, 300, 21)) {
+ LISTBASE_FOREACH (Brush *, br, &bmain->brushes) {
+ /* try to detect beta testers' files by seeing
+ if autosmooth_fset_slide is 0; this will
+ not work once it is added to DNA defaults
+ (right now it's being set in BKE_brush_sculpt_reset).*/
+ if (br->autosmooth_fset_slide == 0.0f) {
+ Brush defbrush = *br;
+
+ defbrush.pressure_size_curve = defbrush.pressure_strength_curve = NULL;
+
+ BKE_brush_sculpt_reset(&defbrush);
+ br->dyntopo = defbrush.dyntopo;
+
+ br->flag2 |= defbrush.flag2 & (BRUSH_SMOOTH_PRESERVE_FACE_SETS |
+ BRUSH_SMOOTH_USE_AREA_WEIGHT | BRUSH_CURVATURE_RAKE);
+
+ br->autosmooth_fset_slide = defbrush.autosmooth_fset_slide;
+ br->boundary_smooth_factor = defbrush.boundary_smooth_factor;
+ br->autosmooth_spacing = defbrush.autosmooth_spacing;
+ br->autosmooth_radius_factor = defbrush.autosmooth_radius_factor;
+ br->topology_rake_radius_factor = defbrush.topology_rake_radius_factor;
+ br->topology_rake_projection = defbrush.topology_rake_projection;
+ br->topology_rake_spacing = defbrush.topology_rake_spacing;
+
+ if (br->autosmooth_projection == 0.0f) {
+ br->autosmooth_projection = defbrush.autosmooth_projection;
+ }
+ }
+
+ if (br->sculpt_tool == SCULPT_TOOL_VCOL_BOUNDARY) {
+ if (br->vcol_boundary_exponent == 0.0f) {
+ br->vcol_boundary_exponent = 1.0f;
+ }
+ }
+ else if (br->sculpt_tool == SCULPT_TOOL_SIMPLIFY) {
+ br->dyntopo.inherit = DYNTOPO_INHERIT_BITMASK &
+ ~(DYNTOPO_INHERIT_ALL | DYNTOPO_SUBDIVIDE | DYNTOPO_COLLAPSE);
+ br->dyntopo.flag |= DYNTOPO_COLLAPSE | DYNTOPO_SUBDIVIDE | DYNTOPO_CLEANUP;
+ }
+ }
+
+ Scene *scene;
+ for (scene = bmain->scenes.first; scene; scene = scene->id.next) {
+ ToolSettings *ts = scene->toolsettings;
+
+ if (ts->sculpt) {
+ ts->sculpt->flags |= SCULPT_DYNTOPO_CLEANUP;
+ }
+ }
+
+ LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
+ ToolSettings *ts = scene->toolsettings;
+ if (ts && ts->sculpt) {
+ ts->sculpt->detail_range = 0.4f;
+ }
+ }
+ }
+
+ if (!MAIN_VERSION_ATLEAST(bmain, 300, 22)) {
+ LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
+ ToolSettings *ts = scene->toolsettings;
+
+ if (ts) {
+ ts->unified_paint_settings.flag |= UNIFIED_PAINT_FLAG_HARD_EDGE_MODE;
+ }
+ }
+ }
+
+ if (!MAIN_VERSION_ATLEAST(bmain, 300, 22)) {
+ LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
+ ToolSettings *ts = scene->toolsettings;
+
+ if (ts) {
+ ts->unified_paint_settings.flag |= UNIFIED_PAINT_FLAG_HARD_EDGE_MODE;
+ }
+ }
+ }
+
+ if (!MAIN_VERSION_ATLEAST(bmain, 300, 23)) {
+ LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
+ ToolSettings *ts = scene->toolsettings;
+
+ if (ts && ts->sculpt) {
+ ts->sculpt->flags |= SCULPT_DYNTOPO_ENABLED;
+ }
+ }
+ }
+
if (!MAIN_VERSION_ATLEAST(bmain, 300, 22)) {
LISTBASE_FOREACH (bNodeTree *, ntree, &bmain->nodetrees) {
if (ntree->type == NTREE_GEOMETRY) {
@@ -1248,19 +1338,6 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain)
}
}
}
- }
-
- /**
- * Versioning code until next subversion bump goes here.
- *
- * \note Be sure to check when bumping the version:
- * - "versioning_userdef.c", #blo_do_versions_userdef
- * - "versioning_userdef.c", #do_versions_theme
- *
- * \note Keep this message at the bottom of the function.
- */
- {
- /* Keep this block, even when empty. */
for (bScreen *screen = bmain->screens.first; screen; screen = screen->id.next) {
LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
@@ -1275,4 +1352,17 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain)
}
}
}
+
+ /**
+ * Versioning code until next subversion bump goes here.
+ *
+ * \note Be sure to check when bumping the version:
+ * - "versioning_userdef.c", #blo_do_versions_userdef
+ * - "versioning_userdef.c", #do_versions_theme
+ *
+ * \note Keep this message at the bottom of the function.
+ */
+ {
+ /* Keep this block, even when empty. */
+ }
}
diff --git a/source/blender/blenloader/intern/versioning_defaults.c b/source/blender/blenloader/intern/versioning_defaults.c
index ccccc6ffc1d..94a6c496120 100644
--- a/source/blender/blenloader/intern/versioning_defaults.c
+++ b/source/blender/blenloader/intern/versioning_defaults.c
@@ -688,6 +688,14 @@ void BLO_update_defaults_startup_blend(Main *bmain, const char *app_template)
brush->sculpt_tool = SCULPT_TOOL_PAINT;
}
+ brush_name = "Color Boundary";
+ brush = BLI_findstring(&bmain->brushes, brush_name, offsetof(ID, name) + 2);
+ if (!brush) {
+ brush = BKE_brush_add(bmain, brush_name, OB_MODE_SCULPT);
+ id_us_min(&brush->id);
+ brush->sculpt_tool = SCULPT_TOOL_VCOL_BOUNDARY;
+ }
+
brush_name = "Smear";
brush = BLI_findstring(&bmain->brushes, brush_name, offsetof(ID, name) + 2);
if (!brush) {
diff --git a/source/blender/bmesh/CMakeLists.txt b/source/blender/bmesh/CMakeLists.txt
index ec282888ffa..eeac69d997a 100644
--- a/source/blender/bmesh/CMakeLists.txt
+++ b/source/blender/bmesh/CMakeLists.txt
@@ -99,6 +99,7 @@ set(SRC
intern/bmesh_mesh.c
intern/bmesh_mesh.h
intern/bmesh_mesh_convert.c
+ intern/bmesh_mesh_convert_threaded.c
intern/bmesh_mesh_convert.h
intern/bmesh_mesh_duplicate.c
intern/bmesh_mesh_duplicate.h
diff --git a/source/blender/bmesh/bmesh_class.h b/source/blender/bmesh/bmesh_class.h
index 82c76c8ae4f..75997cc1af5 100644
--- a/source/blender/bmesh/bmesh_class.h
+++ b/source/blender/bmesh/bmesh_class.h
@@ -16,6 +16,8 @@
#pragma once
+#include "DNA_modifier_types.h"
+
/** \file
* \ingroup bmesh
*
@@ -34,6 +36,7 @@ struct BMFace;
struct BMLoop;
struct BMVert;
struct BMesh;
+struct GSet;
struct MLoopNorSpaceArray;
@@ -295,6 +298,10 @@ 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;
@@ -378,8 +385,35 @@ 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;
+ struct GSet *free_ids;
+#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;
} 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,
@@ -588,3 +622,7 @@ typedef bool (*BMLoopPairFilterFunc)(const BMLoop *, const BMLoop *, void *user_
#else
# 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])
diff --git a/source/blender/bmesh/intern/bmesh_construct.c b/source/blender/bmesh/intern/bmesh_construct.c
index 6f7b2cbc79f..f635c9f4384 100644
--- a/source/blender/bmesh/intern/bmesh_construct.c
+++ b/source/blender/bmesh/intern/bmesh_construct.c
@@ -26,6 +26,8 @@
#include "MEM_guardedalloc.h"
#include "BLI_alloca.h"
+#include "BLI_array.h"
+#include "BLI_ghash.h"
#include "BLI_math.h"
#include "BLI_sort_utils.h"
@@ -36,8 +38,177 @@
#include "bmesh.h"
#include "intern/bmesh_private.h"
+#include "range_tree.h"
+
#define SELECT 1
+#ifdef WITH_BM_ID_FREELIST
+static uint bm_id_freelist_pop(BMesh *bm)
+{
+ if (bm->idmap.freelist_len > 0) {
+ return bm->idmap.freelist[--bm->idmap.freelist_len];
+ }
+
+ return 0;
+}
+
+static void bm_id_freelist_take(BMesh *bm, uint id)
+{
+ if (!bm->idmap.free_ids || !BLI_gset_haskey(bm->idmap.free_ids, POINTER_FROM_UINT(id))) {
+ return;
+ }
+
+ 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 BLI_gset_haskey(bm->idmap.free_ids, POINTER_FROM_UINT(id));
+}
+
+void bm_id_freelist_push(BMesh *bm, uint id)
+{
+ bm->idmap.freelist_len++;
+
+ if (!bm->idmap.free_ids) {
+ bm->idmap.free_ids = BLI_gset_ptr_new("free_ids");
+ }
+
+ 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));
+ memcpy((void *)newlist, (void *)bm->idmap.freelist, bm->idmap.freelist_size);
+ }
+ else {
+ newlist = MEM_malloc_arrayN(size, sizeof(uint), "bm->idmap.freelist");
+ }
+
+ bm->idmap.freelist_size = size;
+ bm->idmap.freelist = newlist;
+ }
+
+ bm->idmap.freelist[bm->idmap.freelist_len - 1] = id;
+ BLI_gset_add(bm->idmap.free_ids, POINTER_FROM_UINT(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;
+ }
+ }
+}
+
+void bm_assign_id(BMesh *bm, BMElem *elem, uint id, bool check_unqiue)
+{
+ if (check_unqiue && (bm->idmap.flag & BM_HAS_ID_MAP)) {
+ if (BM_ELEM_FROM_ID(bm, id)) {
+
+ printf("had to alloc a new id in bm_assign_id for %p; old id: %d\n", elem, (int)id);
+ }
+ }
+
+#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;
+ }
+
+#ifdef WITH_BM_ID_FREELIST
+ uint id;
+
+ if (bm->idmap.freelist_len > 0) {
+ id = bm_id_freelist_pop(bm);
+ }
+ else {
+ id = bm->idmap.maxid + 1;
+ }
+#else
+ uint id = range_tree_uint_take_any(bm->idmap.idtree);
+#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
+
+#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.
*
@@ -409,6 +580,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(
@@ -421,9 +665,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(
@@ -433,9 +682,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(
@@ -445,9 +699,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(
@@ -460,10 +719,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);
}
/* BMESH_TODO: Special handling for hide flags? */
@@ -507,20 +771,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);
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);
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);
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);
break;
default:
BLI_assert(0);
@@ -567,7 +843,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;
@@ -595,15 +872,44 @@ 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_CALLOC, 0);
- CustomData_copy(&bm_src->edata, &bm_dst->edata, CD_MASK_BMESH.emask, CD_CALLOC, 0);
- CustomData_copy(&bm_src->ldata, &bm_dst->ldata, CD_MASK_BMESH.lmask, CD_CALLOC, 0);
- CustomData_copy(&bm_src->pdata, &bm_dst->pdata, CD_MASK_BMESH.pmask, CD_CALLOC, 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_CALLOC, 0);
+ CustomData_copy(
+ &bm_src->edata, &bm_dst->edata, CD_MASK_BMESH.emask | CD_MASK_MESH_ID, CD_CALLOC, 0);
+ CustomData_copy(
+ &bm_src->ldata, &bm_dst->ldata, CD_MASK_BMESH.lmask | CD_MASK_MESH_ID, CD_CALLOC, 0);
+ CustomData_copy(
+ &bm_src->pdata, &bm_dst->pdata, CD_MASK_BMESH.pmask | CD_MASK_MESH_ID, CD_CALLOC, 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
+ 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;
+ }
+ }
}
/**
@@ -646,6 +952,8 @@ 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)
@@ -661,21 +969,54 @@ BMesh *BM_mesh_copy(BMesh *bm_old)
const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_BM(bm_old);
/* allocate a bmesh */
- bm_new = BM_mesh_create(&allocsize,
- &((struct BMeshCreateParams){
- .use_toolflags = bm_old->use_toolflags,
- }));
+ bm_new = BM_mesh_create(
+ &allocsize,
+ &((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)}));
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");
ftable = MEM_mallocN(sizeof(BMFace *) * bm_old->totface, "BM_mesh_copy ftable");
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);
+
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 */
@@ -692,9 +1033,11 @@ 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);
+
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 */
@@ -710,6 +1053,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;
@@ -810,3 +1163,51 @@ char BM_face_flag_to_mflag(BMFace *f)
return (((hflag & BM_ELEM_SELECT) ? ME_FACE_SEL : 0) |
((hflag & BM_ELEM_SMOOTH) ? ME_SMOOTH : 0) | ((hflag & BM_ELEM_HIDDEN) ? ME_HIDE : 0));
}
+
+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);
+}
diff --git a/source/blender/bmesh/intern/bmesh_construct.h b/source/blender/bmesh/intern/bmesh_construct.h
index f5102283ede..30e2a0354f0 100644
--- a/source/blender/bmesh/intern/bmesh_construct.h
+++ b/source/blender/bmesh/intern/bmesh_construct.h
@@ -81,3 +81,4 @@ char BM_vert_flag_from_mflag(const char mflag);
char BM_face_flag_to_mflag(BMFace *f);
short BM_edge_flag_to_mflag(BMEdge *e);
char BM_vert_flag_to_mflag(BMVert *v);
+void BM_sort_disk_cycle(BMVert *v);
diff --git a/source/blender/bmesh/intern/bmesh_core.c b/source/blender/bmesh/intern/bmesh_core.c
index e72c689ddfb..5ca4f995369 100644
--- a/source/blender/bmesh/intern/bmesh_core.c
+++ b/source/blender/bmesh/intern/bmesh_core.c
@@ -37,6 +37,7 @@
#include "bmesh.h"
#include "intern/bmesh_private.h"
+#include "range_tree.h"
/* use so valgrinds memcheck alerts us when undefined index is used.
* TESTING ONLY! */
@@ -122,6 +123,10 @@ BMVert *BM_vert_create(BMesh *bm,
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) {
@@ -202,6 +207,10 @@ 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);
+ }
}
BM_CHECK_ELEMENT(e);
@@ -275,6 +284,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;
@@ -348,14 +361,18 @@ 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);
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);
@@ -479,6 +496,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) {
@@ -754,6 +775,8 @@ static 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 (v->head.data) {
@@ -766,17 +789,95 @@ static 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) {
+ BLI_gset_free(bm->idmap.free_ids, NULL);
+ 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 (e->head.data) {
@@ -793,7 +894,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;
@@ -804,6 +905,8 @@ 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 (f->head.data) {
@@ -820,12 +923,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);
}
@@ -1418,6 +1523,7 @@ 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);
return f;
}
@@ -1938,7 +2044,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);
}
}
@@ -1990,7 +2096,8 @@ BMVert *bmesh_kernel_join_vert_kill_edge(BMesh *bm,
BMVert *v_kill,
const bool do_del,
const bool check_edge_exists,
- const bool kill_degenerate_faces)
+ 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);
@@ -2047,7 +2154,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);
}
}
}
@@ -2467,7 +2574,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;
}
@@ -2594,7 +2702,7 @@ void BM_vert_separate_tested_edges(BMesh *UNUSED(bm),
*
* \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)
{
BMLoop *l;
@@ -2621,6 +2729,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);
@@ -2727,7 +2847,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);
}
@@ -2780,7 +2900,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);
@@ -2837,7 +2957,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 8f7580714ae..97cf2564268 100644
--- a/source/blender/bmesh/intern/bmesh_core.h
+++ b/source/blender/bmesh/intern/bmesh_core.h
@@ -31,6 +31,7 @@ typedef enum eBMCreateFlag {
* use if we immediately write customdata into the element so this skips copying from 'example'
* args or setting defaults, speeds up conversion when data is converted all at once. */
BM_CREATE_SKIP_CD = (1 << 2),
+ BM_CREATE_SKIP_ID = (1 << 3)
} eBMCreateFlag;
BMVert *BM_vert_create(BMesh *bm,
@@ -61,7 +62,7 @@ void BM_face_kill(BMesh *bm, BMFace *f);
void BM_edge_kill(BMesh *bm, BMEdge *e);
void BM_vert_kill(BMesh *bm, BMVert *v);
-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);
bool BM_vert_splice(BMesh *bm, BMVert *v_dst, BMVert *v_src);
bool BM_vert_splice_check_double(BMVert *v_a, BMVert *v_b);
@@ -122,9 +123,13 @@ BMVert *bmesh_kernel_join_vert_kill_edge(BMesh *bm,
BMVert *v_kill,
const bool do_del,
const bool check_edge_exists,
- const bool kill_degenerate_faces);
+ const bool kill_degenerate_faces,
+ const bool combine_flags);
BMFace *bmesh_kernel_join_face_kill_edge(BMesh *bm, BMFace *f1, BMFace *f2, BMEdge *e);
BMVert *bmesh_kernel_unglue_region_make_vert(BMesh *bm, BMLoop *l_sep);
BMVert *bmesh_kernel_unglue_region_make_vert_multi(BMesh *bm, BMLoop **larr, int larr_len);
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);
diff --git a/source/blender/bmesh/intern/bmesh_inline.h b/source/blender/bmesh/intern/bmesh_inline.h
index 4350b4d04ed..260ef98ddad 100644
--- a/source/blender/bmesh/intern/bmesh_inline.h
+++ b/source/blender/bmesh/intern/bmesh_inline.h
@@ -79,9 +79,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 288c5fa8158..21abb21275a 100644
--- a/source/blender/bmesh/intern/bmesh_interp.c
+++ b/source/blender/bmesh/intern/bmesh_interp.c
@@ -28,10 +28,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_task.h"
+#include "BLI_utildefines.h"
#include "BKE_customdata.h"
#include "BKE_multires.h"
@@ -39,8 +41,34 @@
#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);
+ }
+}
+
/* 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,
@@ -53,9 +81,13 @@ static void bm_data_interp_from_elem(CustomData *data_layer,
/* do nothing */
}
else {
+ int id = bm_save_id(bm, ele_dst);
+
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);
+
+ bm_restore_id(bm, ele_dst, id);
}
}
else if (fac >= 1.0f) {
@@ -63,9 +95,13 @@ static void bm_data_interp_from_elem(CustomData *data_layer,
/* do nothing */
}
else {
+ int id = bm_save_id(bm, ele_dst);
+
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);
+
+ bm_restore_id(bm, ele_dst, id);
}
}
else {
@@ -92,7 +128,7 @@ 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);
}
/**
@@ -106,7 +142,7 @@ 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);
}
/**
@@ -314,17 +350,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;
}
@@ -357,8 +428,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);
@@ -370,6 +440,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);
@@ -451,8 +523,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;
@@ -471,6 +545,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;
@@ -485,6 +560,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;
@@ -496,24 +584,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]);
+
+ 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);
+ }
- break;
+ 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],
@@ -555,7 +690,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,
@@ -601,6 +738,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)
@@ -618,58 +757,277 @@ void BM_face_interp_multires(BMesh *bm, BMFace *f_dst, const BMFace *f_src)
}
}
-/**
- * smooths boundaries between multires grids,
- * including some borders in adjacent faces
- */
-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);
- BMLoop *l;
- BMIter liter;
+ float(*orig)[3] = NULL;
+ BLI_array_staticdeclare(orig, 256 * 256);
- if (cd_loop_mdisp_offset == -1) {
+ if (cd_loop_mdisp_offset < 0) {
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;
+ float cent[3];
+ zero_v3(cent);
- /**
- * mdisps is a grid of displacements, ordered thus:
- * <pre>
- * v4/next
- * |
- * | v1/cent-----mid2 ---> x
- * | | |
- * | | |
- * v2/prev---mid1-----v3/cur
- * |
- * V
- * y
- * </pre>
- */
+ 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},
+ };
- sides = (int)sqrt(mdp->totdisp);
- for (y = 0; y < sides; y++) {
- mid_v3_v3v3(co1, mdn->disps[y * sides], mdl->disps[y]);
+ int totoff = sizeof(offs) / sizeof(*offs);
- copy_v3_v3(mdn->disps[y * sides], co1);
- copy_v3_v3(mdl->disps[y], co1);
+#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 *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:
@@ -697,7 +1055,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;
@@ -709,25 +1067,129 @@ 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);
+ 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;
+ }
- add_v3_v3v3(co2, mdl1->disps[o1], mdl2->disps[o2]);
- mul_v3_fl(co2, 0.32);
+ for (x = 0; x < sides; x++) {
+ int x2, y2, o1, o2;
+
+ if (!reverse) {
+ x2 = sides - 1;
+ y2 = x;
+ }
+ else {
+ x2 = x;
+ y2 = y;
+ }
- add_v3_v3v3(co, co1, co2);
+ 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);
+ }
}
/**
@@ -823,6 +1285,17 @@ static void update_data_blocks(BMesh *bm, CustomData *olddata, CustomData *data)
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;
@@ -883,6 +1356,12 @@ static void update_data_blocks(BMesh *bm, CustomData *olddata, CustomData *data)
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);
@@ -891,6 +1370,99 @@ static void update_data_blocks(BMesh *bm, CustomData *olddata, CustomData *data)
}
}
+void BM_data_layers_ensure(BMesh *bm, CustomData *data, BMCustomLayerReq *layers, int totlayer)
+{
+ bool modified = false;
+ CustomData old = *data;
+ CustomData temp;
+ CustomDataMask 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 << (CustomDataMask)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;
+ }
+
+ if (modified) {
+ CustomData_merge(&temp, data, mask, CD_ASSIGN, 0);
+ }
+
+ 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)
{
CustomData olddata;
@@ -907,6 +1479,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)
@@ -925,6 +1499,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_free(BMesh *bm, CustomData *data, int type)
@@ -947,6 +1523,8 @@ void BM_data_layer_free(BMesh *bm, CustomData *data, int type)
if (olddata.layers) {
MEM_freeN(olddata.layers);
}
+
+ bm_update_idmap_cdlayers(bm);
}
void BM_data_layer_free_n(BMesh *bm, CustomData *data, int type, int n)
@@ -969,6 +1547,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 c77281bd798..c5c5579df26 100644
--- a/source/blender/bmesh/intern/bmesh_interp.h
+++ b/source/blender/bmesh/intern/bmesh_interp.h
@@ -1,3 +1,4 @@
+
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -23,6 +24,13 @@
struct LinkNode;
struct MemArena;
+typedef struct BMCustomLayerReq {
+ int type;
+ char *name; // can be NULL
+ int flag;
+} BMCustomLayerReq;
+
+void BM_face_multires_stitch(BMesh *bm, BMFace *f);
void BM_loop_interp_multires_ex(BMesh *bm,
BMLoop *l_dst,
const BMFace *f_src,
@@ -51,6 +59,9 @@ void BM_data_interp_face_vert_edge(BMesh *bm,
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_free(BMesh *bm, CustomData *data, int type);
diff --git a/source/blender/bmesh/intern/bmesh_iterators.c b/source/blender/bmesh/intern/bmesh_iterators.c
index bd28022de4b..951c1234362 100644
--- a/source/blender/bmesh/intern/bmesh_iterators.c
+++ b/source/blender/bmesh/intern/bmesh_iterators.c
@@ -25,6 +25,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 81b6a58e58b..3589516bf6d 100644
--- a/source/blender/bmesh/intern/bmesh_iterators_inline.h
+++ b/source/blender/bmesh/intern/bmesh_iterators_inline.h
@@ -42,8 +42,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
index 9033e43374b..deb735c8cf3 100644
--- a/source/blender/bmesh/intern/bmesh_log.c
+++ b/source/blender/bmesh/intern/bmesh_log.c
@@ -1,3 +1,4 @@
+
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -31,19 +32,75 @@
#include "MEM_guardedalloc.h"
+#include "BLI_array.h"
+#include "BLI_compiler_attrs.h"
#include "BLI_ghash.h"
#include "BLI_listbase.h"
#include "BLI_math.h"
+#include "BLI_memarena.h"
#include "BLI_mempool.h"
+#include "BLI_string.h"
+#include "BLI_threads.h"
#include "BLI_utildefines.h"
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+
#include "BKE_customdata.h"
+#include "BKE_mesh.h"
+#include "BLI_strict_flags.h"
#include "bmesh.h"
#include "bmesh_log.h"
+#include "bmesh_private.h"
#include "range_tree.h"
-#include "BLI_strict_flags.h"
+#define CUSTOMDATA
+
+//#define DO_LOG_PRINT
+
+#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
+# define LOGPRINT(...) \
+ printf("%s: ", __func__); \
+ printf(__VA_ARGS__)
+struct Mesh;
+#else
+# define GET_MSG(le) le ? "" : " "
+# define SET_MSG(le)
+# define LOGPRINT(...)
+#endif
+
+#include <stdarg.h>
+
+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++;
+
+ printf("%s\n", msg);
+#endif
+}
+
+typedef enum { LOG_ENTRY_PARTIAL, LOG_ENTRY_FULL_MESH, LOG_ENTRY_MESH_IDS } BMLogEntryType;
+
+typedef struct BMLogIdMap {
+ int elemmask;
+ int elemtots[15];
+ int *maps[15];
+} BMLogIdMap;
struct BMLogEntry {
struct BMLogEntry *next, *prev;
@@ -54,18 +111,25 @@ struct BMLogEntry {
/* Elements that were in the previous entry, but have been
* deleted */
GHash *deleted_verts;
+ GHash *deleted_edges;
+ GHash *deleted_edges_post; // used for split edges
GHash *deleted_faces;
+
/* Elements that were not in the previous entry, but are in the
* result of this entry */
GHash *added_verts;
+ GHash *added_edges;
GHash *added_faces;
/* Vertices whose coordinates, mask value, or hflag have changed */
GHash *modified_verts;
+ GHash *modified_edges;
GHash *modified_faces;
BLI_mempool *pool_verts;
+ BLI_mempool *pool_edges;
BLI_mempool *pool_faces;
+ MemArena *arena;
/* This is only needed for dropping BMLogEntries while still in
* dynamic-topology mode, as that should release vert/face IDs
@@ -75,11 +139,21 @@ struct BMLogEntry {
* This field is not guaranteed to be valid, any use of it should
* check for NULL. */
BMLog *log;
+
+ CustomData vdata, edata, ldata, pdata;
+ struct BMLogEntry *combined_prev, *combined_next;
+
+ BMLogEntryType type;
+
+ struct Mesh
+ *full_copy_mesh; // avoid excessive memory use by saving a Mesh instead of copying the bmesh
+ BMLogIdMap idmap;
};
struct BMLog {
- /* Tree of free IDs */
- struct RangeTreeUInt *unused_ids;
+ // BMLogEntry *frozen_full_mesh;
+
+ int refcount;
/* Mapping from unique IDs to vertices and faces
*
@@ -90,8 +164,10 @@ struct BMLog {
* The ID is needed because element pointers will change as they
* are created and deleted.
*/
- GHash *id_to_elem;
- GHash *elem_to_id;
+
+ ThreadRWMutex lock;
+
+ BMesh *bm;
/* All BMLogEntrys, ordered from earliest to most recent */
ListBase entries;
@@ -105,18 +181,54 @@ struct BMLog {
* entries have been applied (i.e. there is nothing left to redo.)
*/
BMLogEntry *current_entry;
+
+ bool has_edges;
+ int cd_dyn_vert;
+ bool dead;
};
-typedef struct {
+typedef struct BMLogVert {
+#ifdef DO_LOG_PRINT
+ char msg[64];
+#endif
+
float co[3];
- short no[3];
+ float no[3];
char hflag;
- float mask;
+ void *customdata;
} BMLogVert;
+typedef struct BMLogEdge {
+#ifdef DO_LOG_PRINT
+ char msg[64];
+#endif
+
+ uint v1, v2;
+ char hflag;
+ void *customdata;
+ uint id;
+} BMLogEdge;
+
+#define MAX_FACE_RESERVED 8
+
typedef struct {
- uint v_ids[3];
+#ifdef DO_LOG_PRINT
+ char msg[64];
+#endif
+
+ uint *v_ids;
+ uint *l_ids;
+ void **customdata;
+
+ float no[3];
+ void *customdata_f;
char hflag;
+
+ size_t len;
+
+ void *customdata_res[MAX_FACE_RESERVED];
+ uint v_ids_res[MAX_FACE_RESERVED];
+ uint l_ids_res[MAX_FACE_RESERVED];
} BMLogFace;
/************************* Get/set element IDs ************************/
@@ -125,112 +237,330 @@ typedef struct {
#define logkey_hash BLI_ghashutil_inthash_p_simple
#define logkey_cmp BLI_ghashutil_intcmp
+static void log_idmap_load(BMesh *bm, BMLog *log, BMLogEntry *entry);
+static void log_idmap_swap(BMesh *bm, BMLog *log, BMLogEntry *entry);
+static void log_idmap_free(BMLogEntry *entry);
+
+static void full_copy_swap(BMesh *bm, BMLog *log, BMLogEntry *entry);
+static void full_copy_load(BMesh *bm, BMLog *log, BMLogEntry *entry);
+
+BMLogEntry *bm_log_entry_add_ex(
+ BMesh *bm, BMLog *log, bool combine_with_last, BMLogEntryType type, BMLogEntry *last_entry);
+static void bm_log_entry_free(BMLogEntry *entry);
+static bool bm_log_free_direct(BMLog *log, bool safe_mode);
+
+static void *log_ghash_lookup(BMLog *log, GHash *gh, const void *key)
+{
+ BLI_rw_mutex_lock(&log->lock, THREAD_LOCK_READ);
+ void *ret = BLI_ghash_lookup(gh, key);
+ BLI_rw_mutex_unlock(&log->lock);
+
+ return ret;
+}
+
+// this is not 100% threadsafe
+static void **log_ghash_lookup_p(BMLog *log, GHash *gh, const void *key)
+{
+ BLI_rw_mutex_lock(&log->lock, THREAD_LOCK_READ);
+ void **ret = BLI_ghash_lookup_p(gh, key);
+ BLI_rw_mutex_unlock(&log->lock);
+
+ return ret;
+}
+
+static void log_ghash_insert(BMLog *log, GHash *gh, void *key, void *val)
+{
+ BLI_rw_mutex_lock(&log->lock, THREAD_LOCK_WRITE);
+ BLI_ghash_insert(gh, key, val);
+ BLI_rw_mutex_unlock(&log->lock);
+}
+
+static bool log_ghash_remove(
+ BMLog *log, GHash *gh, const void *key, GHashKeyFreeFP keyfree, GHashValFreeFP valfree)
+{
+ BLI_rw_mutex_lock(&log->lock, THREAD_LOCK_WRITE);
+ bool ret = BLI_ghash_remove(gh, key, keyfree, valfree);
+ BLI_rw_mutex_unlock(&log->lock);
+
+ return ret;
+}
+
+static bool log_ghash_reinsert(
+ BMLog *log, GHash *gh, void *key, void *val, GHashKeyFreeFP keyfree, GHashValFreeFP valfree)
+{
+ BLI_rw_mutex_lock(&log->lock, THREAD_LOCK_WRITE);
+ bool ret = BLI_ghash_reinsert(gh, key, val, keyfree, valfree);
+ BLI_rw_mutex_unlock(&log->lock);
+
+ return ret;
+}
+
+static void bm_log_copy_id(CustomData *cdata, BMElem *elem, void *data)
+{
+ int cd_id = cdata->typemap[CD_MESH_ID];
+
+ if (cd_id >= 0) {
+ cd_id = cdata->layers[cd_id].offset;
+
+ int id = BM_ELEM_CD_GET_INT(elem, cd_id);
+
+ BMElem elem2;
+ elem2.head.data = data;
+
+ BM_ELEM_CD_SET_INT(&elem2, cd_id, id);
+ }
+}
+
+static bool log_ghash_haskey(BMLog *log, GHash *gh, const void *key)
+{
+ BLI_rw_mutex_lock(&log->lock, THREAD_LOCK_READ);
+ bool ret = BLI_ghash_haskey(gh, key);
+ BLI_rw_mutex_unlock(&log->lock);
+
+ return ret;
+}
+
+static bool log_ghash_ensure_p(BMLog *log, GHash *gh, void *key, void ***val)
+{
+ BLI_rw_mutex_lock(&log->lock, THREAD_LOCK_WRITE);
+ bool ret = BLI_ghash_ensure_p(gh, key, val);
+ BLI_rw_mutex_unlock(&log->lock);
+
+ return ret;
+}
+
/* 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));
+ return (uint)BM_ELEM_GET_ID(log->bm, v);
}
-/* Set the vertex's unique ID in the log */
-static void bm_log_vert_id_set(BMLog *log, BMVert *v, uint id)
+/* Get a vertex from its unique ID */
+static BMVert *bm_log_vert_from_id(BMLog *log, uint id)
{
- void *vid = POINTER_FROM_UINT(id);
+ return (BMVert *)BM_ELEM_FROM_ID(log->bm, id);
+}
- BLI_ghash_reinsert(log->id_to_elem, vid, v, NULL, NULL);
- BLI_ghash_reinsert(log->elem_to_id, v, vid, NULL, NULL);
+BMVert *BM_log_id_vert_get(BMLog *log, uint id)
+{
+ return bm_log_vert_from_id(log, id);
+}
+
+/* Get the vertex's unique ID from the log */
+static uint bm_log_edge_id_get(BMLog *log, BMEdge *e)
+{
+ return (uint)BM_ELEM_GET_ID(log->bm, e);
}
/* Get a vertex from its unique ID */
-static BMVert *bm_log_vert_from_id(BMLog *log, uint id)
+static BMEdge *bm_log_edge_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);
+ return (BMEdge *)BM_ELEM_FROM_ID(log->bm, id);
+}
+
+BMEdge *BM_log_id_edge_get(BMLog *log, uint id)
+{
+ return bm_log_edge_from_id(log, id);
}
/* 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));
+ return (uint)BM_ELEM_GET_ID(log->bm, f);
}
-/* Set the face's unique ID in the log */
-static void bm_log_face_id_set(BMLog *log, BMFace *f, uint id)
+uint BM_log_vert_id_get(BMLog *log, BMVert *v)
{
- void *fid = POINTER_FROM_UINT(id);
+ return bm_log_vert_id_get(log, v);
+}
- BLI_ghash_reinsert(log->id_to_elem, fid, f, NULL, NULL);
- BLI_ghash_reinsert(log->elem_to_id, f, fid, NULL, NULL);
+uint BM_log_face_id_get(BMLog *log, BMFace *f)
+{
+ return bm_log_face_id_get(log, f);
}
/* 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);
+ return (BMFace *)BM_ELEM_FROM_ID(log->bm, id);
+}
+
+BMFace *BM_log_id_face_get(BMLog *log, uint id)
+{
+ return bm_log_face_from_id(log, id);
}
/************************ 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)
+static void bm_log_vert_customdata(
+ BMesh *bm, BMLog *log, BMLogEntry *entry, BMVert *v, BMLogVert *lv)
{
- if (cd_vert_mask_offset != -1) {
- return BM_ELEM_CD_GET_FLOAT(v, cd_vert_mask_offset);
+#ifdef CUSTOMDATA
+ // if (!lv) {
+ // return;
+ //}
+
+ if (lv->customdata) {
+ BLI_mempool_free(entry->vdata.pool, lv->customdata);
+ lv->customdata = NULL;
}
- return 0.0f;
+
+ CustomData_bmesh_copy_data(&bm->vdata, &entry->vdata, v->head.data, &lv->customdata);
+
+ // forcibly copy id
+ // bm_log_copy_id(&bm->vdata, (BMElem *)v, lv->customdata);
+
+#endif
}
-/* 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)
+static void bm_log_edge_customdata(
+ BMesh *bm, BMLog *log, BMLogEntry *entry, BMEdge *e, BMLogEdge *le)
{
- if (cd_vert_mask_offset != -1) {
- BM_ELEM_CD_SET_FLOAT(v, cd_vert_mask_offset, new_mask);
+ if (le->customdata) {
+ BLI_mempool_free(entry->edata.pool, le->customdata);
+ le->customdata = NULL;
+ }
+
+ CustomData_bmesh_copy_data(&bm->edata, &entry->edata, e->head.data, &le->customdata);
+}
+
+static void bm_log_face_customdata(BMesh *bm, BMLog *log, BMFace *f, BMLogFace *lf)
+{
+ BMLogEntry *entry = log->current_entry;
+
+ if (!entry || !lf) {
+ printf("bmlog error\n");
+ return;
+ }
+
+ if (lf->customdata_f) {
+ BLI_mempool_free(entry->pdata.pool, lf->customdata_f);
+ lf->customdata_f = NULL;
}
+
+ CustomData_bmesh_copy_data(&bm->pdata, &entry->pdata, f->head.data, &lf->customdata_f);
+
+ // forcibly copy id
+ // bm_log_copy_id(&bm->pdata, (BMElem *)f, lf->customdata_f);
+
+ BMLoop *l = f->l_first;
+ int i = 0;
+ do {
+ if (lf->customdata[i]) {
+ BLI_mempool_free(entry->ldata.pool, lf->customdata[i]);
+ lf->customdata[i] = NULL;
+ }
+
+ CustomData_bmesh_copy_data(&bm->ldata, &entry->ldata, l->head.data, &lf->customdata[i]);
+ } while ((i++, l = l->next) != f->l_first);
}
/* 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)
+static void bm_log_vert_bmvert_copy(BMLog *log,
+ BMLogEntry *entry,
+ BMLogVert *lv,
+ BMVert *v,
+ const int cd_vert_mask_offset,
+ bool copy_customdata)
{
copy_v3_v3(lv->co, v->co);
- normal_float_to_short_v3(lv->no, v->no);
- lv->mask = vert_mask_get(v, cd_vert_mask_offset);
+ copy_v3_v3(lv->no, v->no);
+
lv->hflag = v->head.hflag;
+
+ if (copy_customdata) {
+ bm_log_vert_customdata(log->bm, log, entry, v, lv);
+ }
}
/* Allocate and initialize a BMLogVert */
-static BMLogVert *bm_log_vert_alloc(BMLog *log, BMVert *v, const int cd_vert_mask_offset)
+static BMLogVert *bm_log_vert_alloc(BMLog *log,
+ BMVert *v,
+ const int cd_vert_mask_offset,
+ bool log_customdata)
{
BMLogEntry *entry = log->current_entry;
BMLogVert *lv = BLI_mempool_alloc(entry->pool_verts);
+ lv->customdata = NULL;
- bm_log_vert_bmvert_copy(lv, v, cd_vert_mask_offset);
+ bm_log_vert_bmvert_copy(log, entry, lv, v, -1, log_customdata);
return lv;
}
+static void bm_log_edge_bmedge_copy(
+ BMLog *log, BMLogEntry *entry, BMLogEdge *le, BMEdge *e, bool copy_customdata)
+{
+ if (e->head.htype != BM_EDGE) {
+ printf("%s: e is not an edge; htype: %d\n", __func__, (int)e->head.htype);
+ }
+
+ le->v1 = (uint)BM_ELEM_GET_ID(log->bm, e->v1);
+ le->v2 = (uint)BM_ELEM_GET_ID(log->bm, e->v2);
+
+ le->id = (uint)BM_ELEM_GET_ID(log->bm, e);
+ le->hflag = e->head.hflag;
+
+ if (copy_customdata) {
+ bm_log_edge_customdata(log->bm, log, entry, e, le);
+ }
+}
+
+/* Allocate and initialize a BMLogVert */
+static BMLogEdge *bm_log_edge_alloc(BMLog *log, BMEdge *e, bool log_customdata)
+{
+ BMLogEntry *entry = log->current_entry;
+ BMLogEdge *le = BLI_mempool_alloc(entry->pool_edges);
+ le->customdata = NULL;
+
+#ifdef DO_LOG_PRINT
+ le->msg[0] = 0;
+#endif
+
+ bm_log_edge_bmedge_copy(log, entry, le, e, log_customdata);
+
+ return le;
+}
+
/* 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);
+ lf->len = (size_t)f->len;
+
+ bool have_loop_ids = (log->bm->idmap.flag & BM_LOOP);
+
+ if (f->len > MAX_FACE_RESERVED) {
+ lf->v_ids = (uint *)BLI_memarena_alloc(entry->arena, sizeof(*lf->v_ids) * lf->len);
+ lf->l_ids = (uint *)BLI_memarena_alloc(entry->arena, sizeof(*lf->l_ids) * lf->len);
+ lf->customdata = (void **)BLI_memarena_alloc(entry->arena, sizeof(void *) * lf->len);
+ }
+ else {
+ lf->v_ids = lf->v_ids_res;
+ lf->l_ids = lf->l_ids_res;
+ lf->customdata = lf->customdata_res;
+ }
- // BM_iter_as_array(NULL, BM_VERTS_OF_FACE, f, (void **)v, 3);
- BM_face_as_array_vert_tri(f, v);
+ lf->customdata_f = NULL;
- 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]);
+ copy_v3_v3(lf->no, f->no);
+
+ int i = 0;
+ BMLoop *l = f->l_first;
+ do {
+ if (have_loop_ids) {
+ lf->l_ids[i] = (uint)BM_ELEM_GET_ID(log->bm, l);
+ }
+ else {
+ lf->l_ids[i] = (uint)-1;
+ }
+
+ lf->v_ids[i] = bm_log_vert_id_get(log, l->v);
+
+ lf->customdata[i] = NULL;
+ } while ((i++, l = l->next) != f->l_first);
lf->hflag = f->head.hflag;
return lf;
@@ -238,10 +568,10 @@ static BMLogFace *bm_log_face_alloc(BMLog *log, BMFace *f)
/************************ Helpers for undo/redo ***********************/
-static void bm_log_verts_unmake(BMesh *bm, BMLog *log, GHash *verts)
+// exec vert kill callbacks before killing faces
+static void bm_log_verts_unmake_pre(
+ BMesh *bm, BMLog *log, GHash *verts, BMLogEntry *entry, BMLogCallbacks *callbacks)
{
- 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);
@@ -249,78 +579,367 @@ static void bm_log_verts_unmake(BMesh *bm, BMLog *log, GHash *verts)
uint id = POINTER_AS_UINT(key);
BMVert *v = bm_log_vert_from_id(log, id);
+ if (!v) {
+ printf("bm_log error; vertex id: %p\n", key);
+ continue;
+ }
+
+ if (v->head.htype != BM_VERT) {
+ printf("bm_log error; vertex id: %p, type was: %d\n", key, v->head.htype);
+ continue;
+ }
+
+ /* Ensure the log has the final values of the vertex before
+ * deleting it */
+ bm_log_vert_bmvert_copy(log, entry, lv, v, -1, true);
+
+ if (callbacks) {
+ callbacks->on_vert_kill(v, callbacks->userdata);
+ }
+ }
+}
+
+// exec vert kill callbacks before killing faces
+static void bm_log_edges_unmake_pre(
+ BMesh *bm, BMLog *log, GHash *edges, BMLogEntry *entry, BMLogCallbacks *callbacks)
+{
+ GHashIterator gh_iter;
+ GHASH_ITER (gh_iter, edges) {
+ void *key = BLI_ghashIterator_getKey(&gh_iter);
+ BMLogEdge *le = BLI_ghashIterator_getValue(&gh_iter);
+ uint id = POINTER_AS_UINT(key);
+ BMEdge *e = bm_log_edge_from_id(log, id);
+
+ if (!e) {
+ printf("%s: missing edge; id: %d [%s]\n", __func__, id, GET_MSG(le));
+ continue;
+ }
+
+ if (e->head.htype != BM_EDGE) {
+ printf("%s: not an edge; edge id: %d, type was: %d [%s]\n",
+ __func__,
+ id,
+ e->head.htype,
+ GET_MSG(le));
+ continue;
+ }
+
/* 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_log_edge_bmedge_copy(log, entry, le, e, true);
+
+ if (callbacks) {
+ callbacks->on_edge_kill(e, callbacks->userdata);
+ }
+ }
+}
+
+static void bm_log_edges_unmake(
+ BMesh *bm, BMLog *log, GHash *edges, BMLogEntry *entry, BMLogCallbacks *callbacks)
+{
+ GHashIterator gh_iter;
+ GHASH_ITER (gh_iter, edges) {
+ void *key = BLI_ghashIterator_getKey(&gh_iter);
+ uint id = POINTER_AS_UINT(key);
+ BMLogEdge *le = BLI_ghashIterator_getValue(&gh_iter);
+ BMEdge *e = bm_log_edge_from_id(log, id);
+
+ if (!e) {
+ printf("%s: missing edge; edge id: %d [%s]\n", __func__, id, GET_MSG(le));
+ continue;
+ }
+
+ if (e->head.htype != BM_EDGE) {
+ printf("%s: not an edge; edge id: %d, type: %d [%s]\n",
+ __func__,
+ id,
+ e->head.htype,
+ GET_MSG(le));
+ continue;
+ }
+
+ BM_edge_kill(bm, e);
+ }
+}
+
+static void bm_log_verts_unmake(
+ BMesh *bm, BMLog *log, GHash *verts, BMLogEntry *entry, BMLogCallbacks *callbacks)
+{
+ GHashIterator gh_iter;
+ GHASH_ITER (gh_iter, verts) {
+ void *key = BLI_ghashIterator_getKey(&gh_iter);
+ uint id = POINTER_AS_UINT(key);
+ BMVert *v = bm_log_vert_from_id(log, id);
+
+ if (!v) {
+ printf("bmlog error. vertex id: %p\n", key);
+ continue;
+ }
BM_vert_kill(bm, v);
}
}
-static void bm_log_faces_unmake(BMesh *bm, BMLog *log, GHash *faces)
+static void bm_log_faces_unmake(
+ BMesh *bm, BMLog *log, GHash *faces, BMLogEntry *entry, BMLogCallbacks *callbacks)
{
GHashIterator gh_iter;
+ BMEdge **e_tri = NULL;
+ BLI_array_staticdeclare(e_tri, 32);
+
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);
- BMEdge *e_tri[3];
- BMLoop *l_iter;
+
+ if (!f) {
+ printf("bmlog error in %s: missing face %d\n", __func__, id);
+ continue;
+ }
+
+ if (f->head.htype != BM_FACE) {
+ printf("bmlog error in %s: f was not a face, type was: %d\n", __func__, f->head.htype);
+ continue;
+ }
+
+ BLI_array_clear(e_tri);
+
+ BMLoop *l;
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;
+ // ensure we have final customdata for face in log
+
+ l = f->l_first;
+ i = 0;
+ do {
+ if (lf->customdata[i]) {
+ CustomData_bmesh_copy_data(&bm->ldata, &entry->ldata, l->head.data, &lf->customdata[i]);
+ }
+
+ BLI_array_append(e_tri, l->e);
+ } while ((i++, l = l->next) != f->l_first);
+
+ if (lf->customdata_f) {
+ CustomData_bmesh_copy_data(&bm->pdata, &entry->pdata, f->head.data, &lf->customdata_f);
+
+ // forcibly copy id
+ // bm_log_copy_id(&bm->pdata, (BMElem *)f, lf->customdata_f);
+ }
+
+ if (callbacks) {
+ callbacks->on_face_kill(f, callbacks->userdata);
}
- /* Remove any unused edges */
BM_face_kill(bm, f);
- for (i = 0; i < 3; i++) {
+
+#if 0
+ /* Remove any unused edges */
+ for (i = 0; i < (int)lf->len; i++) {
if (BM_edge_is_wire(e_tri[i])) {
BM_edge_kill(bm, e_tri[i]);
}
}
+#endif
}
+
+ BLI_array_free(e_tri);
}
-static void bm_log_verts_restore(BMesh *bm, BMLog *log, GHash *verts)
+static void bm_log_verts_restore(
+ BMesh *bm, BMLog *log, GHash *verts, BMLogEntry *entry, BMLogCallbacks *callbacks)
{
- 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);
+
+ BMVert *v = BM_vert_create(bm, lv->co, NULL, BM_CREATE_SKIP_ID);
+
v->head.hflag = lv->hflag;
- normal_short_to_float_v3(v->no, lv->no);
- bm_log_vert_id_set(log, v, POINTER_AS_UINT(key));
+ copy_v3_v3(v->no, lv->no);
+
+#ifdef CUSTOMDATA
+ if (lv->customdata) {
+ CustomData_bmesh_copy_data(&entry->vdata, &bm->vdata, lv->customdata, &v->head.data);
+ }
+#endif
+
+ bm_assign_id(bm, (BMElem *)v, POINTER_AS_UINT(key), false);
+
+ if (callbacks) {
+ callbacks->on_vert_add(v, callbacks->userdata);
+ }
}
}
-static void bm_log_faces_restore(BMesh *bm, BMLog *log, GHash *faces)
+static void bm_log_edges_restore(
+ BMesh *bm, BMLog *log, GHash *edges, BMLogEntry *entry, BMLogCallbacks *callbacks)
{
GHashIterator gh_iter;
+ GHASH_ITER (gh_iter, edges) {
+ void *key = BLI_ghashIterator_getKey(&gh_iter);
+ BMLogEdge *le = BLI_ghashIterator_getValue(&gh_iter);
+ uint id = POINTER_AS_UINT(key);
+
+ if (id != le->id) {
+ printf("%s: id differs from stored id in BMLogEdge!\n", __func__);
+ }
+
+ BMVert *v1 = bm_log_vert_from_id(log, le->v1);
+ BMVert *v2 = bm_log_vert_from_id(log, le->v2);
+
+ if (!v1 || !v2) {
+ printf("%s: missing edge verts: %p %p\n", __func__, v1, v2);
+ continue;
+ }
+
+ if (v1->head.htype != BM_VERT || v2->head.htype != BM_VERT) {
+ printf("%s: edge verts were not verts: %d %d\n", __func__, v1->head.htype, v2->head.htype);
+ continue;
+ }
+
+ BMEdge *e = BM_edge_exists(v1, v2);
+ if (e) {
+ printf("%s: edge already %d existed\n", __func__, (int)id);
+ bm_free_id(bm, (BMElem *)e);
+ }
+ else {
+ e = BM_edge_create(bm, v1, v2, NULL, BM_CREATE_SKIP_ID);
+ }
+
+ e->head.hflag = le->hflag;
+
+#ifdef CUSTOMDATA
+ if (le->customdata) {
+ CustomData_bmesh_copy_data(&entry->edata, &bm->edata, le->customdata, &e->head.data);
+ }
+#endif
+
+ bm_assign_id(bm, (BMElem *)e, POINTER_AS_UINT(key), false);
+
+ if ((uint)BM_ELEM_GET_ID(bm, e) != id) {
+ printf("%s: error assigning id\n", __func__);
+ }
+
+ if (callbacks) {
+ callbacks->on_edge_add(e, callbacks->userdata);
+ }
+ }
+}
+
+static void bm_log_faces_restore(
+ BMesh *bm, BMLog *log, GHash *faces, BMLogEntry *entry, BMLogCallbacks *callbacks)
+{
+ GHashIterator gh_iter;
+ BMVert **vs_tmp = NULL;
+ BLI_array_staticdeclare(vs_tmp, 32);
+
+ bool have_loop_ids = (log->bm->idmap.flag & BM_LOOP);
+
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);
+
+ BLI_array_clear(vs_tmp);
+ bool bad = false;
+
+ for (int i = 0; i < (int)lf->len; i++) {
+ BMVert *v = bm_log_vert_from_id(log, lf->v_ids[i]);
+
+ if (!v) {
+ BMIter iter;
+ BMVert *v2;
+ const int cd_id = bm->idmap.cd_id_off[BM_VERT];
+
+ bad = true;
+
+ BM_ITER_MESH (v2, &iter, bm, BM_VERTS_OF_MESH) {
+ int id = BM_ELEM_CD_GET_INT(v2, cd_id);
+
+ if (lf->v_ids[i] == (uint)id) {
+ printf("found vertex %d\n", id);
+ bad = false;
+ v = v2;
+ break;
+ }
+ }
+
+ if (bad) {
+ printf("Undo error! %p\n", v);
+ break;
+ }
+ }
+
+ if (bad) {
+ continue;
+ }
+
+ if (v->head.htype != BM_VERT) {
+ printf("vert %d in face %d was not a vertex\n", (int)lf->v_ids[i], POINTER_AS_INT(key));
+ continue;
+ }
+ BLI_array_append(vs_tmp, v);
+ }
+
+ if ((int)BLI_array_len(vs_tmp) < 2) {
+ printf("severely malformed face %d in %s\n", POINTER_AS_INT(key), __func__);
+ continue;
+ }
+
+#if 0
+ for (size_t j = 0; j < lf->len; j++) {
+ BMVert *v1 = bm_log_vert_from_id(log, lf->v_ids[j]);
+ BMVert *v2 = bm_log_vert_from_id(log, lf->v_ids[(j + 1) % lf->len]);
+
+ if (!v1 || !v2) {
+ continue;
+ }
+
+ if (!BM_edge_exists(v1, v2)) {
+ int id = POINTER_AS_INT(key);
+ printf("%s: missing edge, face %d had to create it\n", __func__, (int)id);
+ }
+ }
+#endif
+
+ BMFace *f = BM_face_create_verts(
+ bm, vs_tmp, (int)BLI_array_len(vs_tmp), NULL, BM_CREATE_SKIP_ID, true);
f->head.hflag = lf->hflag;
- bm_log_face_id_set(log, f, POINTER_AS_UINT(key));
+
+ copy_v3_v3(f->no, lf->no);
+
+ if (lf->customdata_f) {
+ CustomData_bmesh_copy_data(&entry->pdata, &bm->pdata, lf->customdata_f, &f->head.data);
+ }
+
+ bm_assign_id(bm, (BMElem *)f, POINTER_AS_UINT(key), false);
+
+ BMLoop *l = f->l_first;
+ int j = 0;
+
+ do {
+ if (have_loop_ids) {
+ bm_assign_id(bm, (BMElem *)l, lf->l_ids[j], false);
+ }
+
+ if (lf->customdata[j]) {
+ CustomData_bmesh_copy_data(&entry->ldata, &bm->ldata, lf->customdata[j], &l->head.data);
+ }
+ } while ((j++, l = l->next) != f->l_first);
+
+ if (callbacks) {
+ callbacks->on_face_add(f, callbacks->userdata);
+ }
}
+
+ BLI_array_free(vs_tmp);
}
-static void bm_log_vert_values_swap(BMesh *bm, BMLog *log, GHash *verts)
+static void bm_log_vert_values_swap(
+ BMesh *bm, BMLog *log, GHash *verts, BMLogEntry *entry, BMLogCallbacks *callbacks)
{
- const int cd_vert_mask_offset = CustomData_get_offset(&bm->vdata, CD_PAINT_MASK);
+ void *scratch = bm->vdata.pool ? BLI_mempool_alloc(bm->vdata.pool) : NULL;
GHashIterator gh_iter;
GHASH_ITER (gh_iter, verts) {
@@ -328,22 +947,85 @@ static void bm_log_vert_values_swap(BMesh *bm, BMLog *log, GHash *verts)
BMLogVert *lv = BLI_ghashIterator_getValue(&gh_iter);
uint id = POINTER_AS_UINT(key);
BMVert *v = bm_log_vert_from_id(log, id);
- float mask;
- short normal[3];
+
+ if (!v) {
+ printf("missing vert in bmlog! %d", id);
+ continue;
+ }
+
+ if (v->head.htype != BM_VERT) {
+ printf("not a vertex: %d\n", v->head.htype);
+ continue;
+ }
swap_v3_v3(v->co, lv->co);
- copy_v3_v3_short(normal, lv->no);
- normal_float_to_short_v3(lv->no, v->no);
- normal_short_to_float_v3(v->no, normal);
+ 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);
+
+ void *old_cdata = NULL;
+
+ if (lv->customdata) {
+ if (v->head.data) {
+ old_cdata = scratch;
+ memcpy(old_cdata, v->head.data, (size_t)bm->vdata.totsize);
+ }
+
+ CustomData_bmesh_swap_data(&entry->vdata, &bm->vdata, lv->customdata, &v->head.data);
+ }
+
+ if (callbacks) {
+ callbacks->on_vert_change(v, callbacks->userdata, old_cdata);
+ }
+ }
+
+ if (scratch) {
+ BLI_mempool_free(bm->vdata.pool, scratch);
+ }
+}
+
+static void bm_log_edge_values_swap(
+ BMesh *bm, BMLog *log, GHash *edges, BMLogEntry *entry, BMLogCallbacks *callbacks)
+{
+ void *scratch = bm->edata.pool ? BLI_mempool_alloc(bm->edata.pool) : NULL;
+
+ GHashIterator gh_iter;
+ GHASH_ITER (gh_iter, edges) {
+ void *key = BLI_ghashIterator_getKey(&gh_iter);
+ BMLogEdge *le = BLI_ghashIterator_getValue(&gh_iter);
+ uint id = POINTER_AS_UINT(key);
+ BMEdge *e = bm_log_edge_from_id(log, id);
+
+ SWAP(char, e->head.hflag, le->hflag);
+
+ void *old_cdata = NULL;
+
+ if (le->customdata) {
+ if (e->head.data) {
+ old_cdata = scratch;
+ memcpy(old_cdata, e->head.data, (size_t)bm->edata.totsize);
+ }
+
+ CustomData_bmesh_swap_data(&entry->edata, &bm->edata, le->customdata, &e->head.data);
+ }
+
+ if (callbacks) {
+ callbacks->on_edge_change(e, callbacks->userdata, old_cdata);
+ }
+ }
+
+ if (scratch) {
+ BLI_mempool_free(bm->edata.pool, scratch);
}
}
-static void bm_log_face_values_swap(BMLog *log, GHash *faces)
+static void bm_log_face_values_swap(BMLog *log,
+ GHash *faces,
+ BMLogEntry *entry,
+ BMLogCallbacks *callbacks)
{
+ void *scratch = log->bm->pdata.pool ? BLI_mempool_alloc(log->bm->pdata.pool) : NULL;
+
GHashIterator gh_iter;
GHASH_ITER (gh_iter, faces) {
void *key = BLI_ghashIterator_getKey(&gh_iter);
@@ -351,46 +1033,85 @@ static void bm_log_face_values_swap(BMLog *log, GHash *faces)
uint id = POINTER_AS_UINT(key);
BMFace *f = bm_log_face_from_id(log, id);
+ swap_v3_v3(f->no, lf->no);
SWAP(char, f->head.hflag, lf->hflag);
- }
-}
-/**********************************************************************/
+ void *old_cdata = NULL;
-/* 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;
+ if (f->head.data) {
+ old_cdata = scratch;
+ memcpy(old_cdata, f->head.data, (size_t)log->bm->pdata.totsize);
+ }
+
+ if (lf->customdata_f) {
+ CustomData_bmesh_swap_data(&entry->pdata, &log->bm->pdata, lf->customdata_f, &f->head.data);
+ }
+
+ int i = 0;
+ BMLoop *l = f->l_first;
- /* 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);
+ do {
+ if (lf->customdata[i]) {
+ CustomData_bmesh_swap_data(
+ &entry->ldata, &log->bm->ldata, lf->customdata[i], &l->head.data);
+ }
+ } while ((i++, l = l->next) != f->l_first);
+
+ if (callbacks) {
+ callbacks->on_face_change(f, callbacks->userdata, old_cdata);
+ }
}
- /* 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);
+ if (scratch) {
+ BLI_mempool_free(log->bm->pdata.pool, scratch);
}
}
+/**********************************************************************/
+
+static void bm_log_full_mesh_intern(BMesh *bm, BMLog *log, BMLogEntry *entry)
+{
+ CustomData_MeshMasks cd_mask_extra = {CD_MASK_DYNTOPO_VERT, 0, 0, 0, 0};
+
+ BM_mesh_elem_index_ensure(bm, BM_VERT | BM_EDGE | BM_FACE);
+
+ entry->full_copy_mesh = BKE_mesh_from_bmesh_nomain(
+ bm,
+ (&(struct BMeshToMeshParams){.update_shapekey_indices = false,
+ .calc_object_remap = false,
+ .cd_mask_extra = cd_mask_extra,
+ .copy_temp_cdlayers = true,
+ .ignore_mesh_id_layers = false}),
+ NULL);
+}
+
/* Allocate an empty log entry */
-static BMLogEntry *bm_log_entry_create(void)
+static BMLogEntry *bm_log_entry_create(BMLogEntryType type)
{
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->type = type;
+
+ if (type == LOG_ENTRY_PARTIAL) {
+ entry->deleted_verts = BLI_ghash_new(logkey_hash, logkey_cmp, __func__);
+ entry->deleted_edges = BLI_ghash_new(logkey_hash, logkey_cmp, __func__);
+ entry->deleted_edges_post = 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_edges = BLI_ghash_new(logkey_hash, logkey_cmp, __func__);
+ entry->added_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);
+ entry->modified_verts = BLI_ghash_new(logkey_hash, logkey_cmp, __func__);
+ entry->modified_edges = 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_edges = BLI_mempool_create(sizeof(BMLogEdge), 0, 64, BLI_MEMPOOL_NOP);
+ entry->pool_faces = BLI_mempool_create(sizeof(BMLogFace), 0, 64, BLI_MEMPOOL_NOP);
+
+ entry->arena = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, "bmlog arena");
+ }
return entry;
}
@@ -398,28 +1119,79 @@ static BMLogEntry *bm_log_entry_create(void)
/* Free the data in a log entry
*
* NOTE: does not free the log entry itself. */
-static void bm_log_entry_free(BMLogEntry *entry)
+static void bm_log_entry_free_direct(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);
+ switch (entry->type) {
+ case LOG_ENTRY_MESH_IDS:
+ log_idmap_free(entry);
+ break;
+ case LOG_ENTRY_FULL_MESH:
+
+ BKE_mesh_free_data_for_undo(entry->full_copy_mesh);
+ break;
+ case LOG_ENTRY_PARTIAL:
+ BLI_ghash_free(entry->deleted_verts, NULL, NULL);
+ BLI_ghash_free(entry->deleted_edges, NULL, NULL);
+ BLI_ghash_free(entry->deleted_edges_post, NULL, NULL);
+ BLI_ghash_free(entry->deleted_faces, NULL, NULL);
+
+ BLI_ghash_free(entry->added_verts, NULL, NULL);
+ BLI_ghash_free(entry->added_edges, NULL, NULL);
+ BLI_ghash_free(entry->added_faces, NULL, NULL);
+
+ BLI_ghash_free(entry->modified_verts, NULL, NULL);
+ BLI_ghash_free(entry->modified_edges, NULL, NULL);
+ BLI_ghash_free(entry->modified_faces, NULL, NULL);
+
+ BLI_mempool_destroy(entry->pool_verts);
+ BLI_mempool_destroy(entry->pool_edges);
+ BLI_mempool_destroy(entry->pool_faces);
+ BLI_memarena_free(entry->arena);
+
+ if (entry->vdata.pool) {
+ BLI_mempool_destroy(entry->vdata.pool);
+ }
+ if (entry->edata.pool) {
+ BLI_mempool_destroy(entry->edata.pool);
+ }
+ if (entry->ldata.pool) {
+ BLI_mempool_destroy(entry->ldata.pool);
+ }
+ if (entry->pdata.pool) {
+ BLI_mempool_destroy(entry->pdata.pool);
+ }
- BLI_mempool_destroy(entry->pool_verts);
- BLI_mempool_destroy(entry->pool_faces);
+ CustomData_free(&entry->vdata, 0);
+ CustomData_free(&entry->edata, 0);
+ CustomData_free(&entry->ldata, 0);
+ CustomData_free(&entry->pdata, 0);
+ break;
+ }
}
-static void bm_log_id_ghash_retake(RangeTreeUInt *unused_ids, GHash *id_ghash)
+/* Free the data in a log entry
+ * and handles bmlog ref counting
+ * NOTE: does not free the log entry itself. */
+static void bm_log_entry_free(BMLogEntry *entry)
{
- GHashIterator gh_iter;
+ BMLog *log = entry->log;
+ bool kill_log = false;
- GHASH_ITER (gh_iter, id_ghash) {
- void *key = BLI_ghashIterator_getKey(&gh_iter);
- uint id = POINTER_AS_UINT(key);
+ if (log) {
+ log->refcount--;
+
+ if (log->refcount < 0) {
+ fprintf(stderr, "BMLog refcount error\n");
+ log->refcount = 0;
+ }
- range_tree_uint_retake(unused_ids, id);
+ kill_log = !log->refcount;
+ }
+
+ bm_log_entry_free_direct(entry);
+
+ if (kill_log) {
+ bm_log_free_direct(log, true);
}
}
@@ -454,56 +1226,63 @@ static GHash *bm_log_compress_ids_to_indices(uint *ids, uint totid)
return map;
}
-/* Release all ID keys in id_ghash */
-static void bm_log_id_ghash_release(BMLog *log, GHash *id_ghash)
-{
- GHashIterator gh_iter;
+/***************************** Public API *****************************/
- 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);
- }
+void BM_log_set_cd_offsets(BMLog *log, int cd_dyn_vert)
+{
+ log->cd_dyn_vert = cd_dyn_vert;
}
-/***************************** Public API *****************************/
+void BM_log_set_bm(BMesh *bm, BMLog *log)
+{
+ log->bm = bm;
+}
/* Allocate, initialize, and assign a new BMLog */
-BMLog *BM_log_create(BMesh *bm)
+BMLog *BM_log_create(BMesh *bm, int cd_dyn_vert)
{
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);
+ BLI_rw_mutex_init(&log->lock);
- /* Assign IDs to all existing vertices and faces */
- bm_log_assign_ids(bm, log);
+ BM_log_set_cd_offsets(log, cd_dyn_vert);
return log;
}
-void BM_log_cleanup_entry(BMLogEntry *entry)
+BMLog *bm_log_from_existing_entries_create(BMesh *bm, BMLog *log, BMLogEntry *entry)
{
- BMLog *log = entry->log;
+ log->current_entry = entry;
- 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);
+ /* 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) {
+ BMLogEntry *entry2 = entry->combined_prev;
+
+ while (entry2) {
+ entry2->log = log;
+ entry2 = entry2->combined_prev;
- /* 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);
+ log->refcount++;
+ }
+
+ entry->log = log;
+ log->refcount++;
}
+
+ return log;
}
/* Allocate and initialize a new BMLog using existing BMLogEntries
@@ -516,69 +1295,91 @@ void BM_log_cleanup_entry(BMLogEntry *entry)
*/
BMLog *BM_log_from_existing_entries_create(BMesh *bm, BMLogEntry *entry)
{
- BMLog *log = BM_log_create(bm);
+ BMLog *log = BM_log_create(bm, -1);
- if (entry->prev) {
- log->current_entry = entry;
- }
- else {
- log->current_entry = NULL;
- }
+ bm_log_from_existing_entries_create(bm, log, entry);
- /* Let BMLog manage the entry list again */
- log->entries.first = log->entries.last = entry;
+ return log;
+}
- {
- while (entry->prev) {
- entry = entry->prev;
- log->entries.first = entry;
- }
- entry = log->entries.last;
- while (entry->next) {
- entry = entry->next;
- log->entries.last = entry;
- }
+BMLog *BM_log_unfreeze(BMesh *bm, BMLogEntry *entry)
+{
+ if (!entry || !entry->log) {
+ return NULL;
}
- for (entry = log->entries.first; entry; entry = entry->next) {
- entry->log = log;
+#if 0
+ BMLogEntry *frozen = entry->log->frozen_full_mesh;
+ if (!frozen && entry->type == LOG_ENTRY_FULL_MESH) {
+ frozen = entry;
+ }
- /* 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);
+ if (!frozen || frozen->type != LOG_ENTRY_FULL_MESH) {
+ return entry->log->bm == bm ? entry->log : NULL;
}
+#endif
- return log;
+ entry->log->bm = bm;
+
+#if 0
+ full_copy_load(bm, entry->log, frozen);
+
+ if (entry->log->frozen_full_mesh) {
+ entry->log->frozen_full_mesh->log = NULL;
+ bm_log_entry_free(entry->log->frozen_full_mesh);
+ entry->log->frozen_full_mesh = NULL;
+ }
+#endif
+ return entry->log;
}
-/* Free all the data in a BMLog including the log itself */
-void BM_log_free(BMLog *log)
+/* 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)
{
BMLogEntry *entry;
- if (log->unused_ids) {
- range_tree_uint_free(log->unused_ids);
- }
+ if (safe_mode && log->refcount) {
+#if 0
+ if (log->frozen_full_mesh) {
+ log->frozen_full_mesh->log = NULL;
+ bm_log_entry_free(log->frozen_full_mesh);
+ }
+#endif
- if (log->id_to_elem) {
- BLI_ghash_free(log->id_to_elem, NULL, NULL);
- }
+ // log->frozen_full_mesh = bm_log_entry_create(LOG_ENTRY_FULL_MESH);
+ // bm_log_full_mesh_intern(log->bm, log, log->frozen_full_mesh);
- if (log->elem_to_id) {
- BLI_ghash_free(log->elem_to_id, NULL, NULL);
+ return false;
}
+ log->dead = true;
+
+ BLI_rw_mutex_end(&log->lock);
+
/* 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);
+ return true;
+}
+
+bool BM_log_free(BMLog *log, bool safe_mode)
+{
+ if (log->dead) {
+ MEM_freeN(log);
+ return true;
+ }
+
+ if (bm_log_free_direct(log, safe_mode)) {
+ MEM_freeN(log);
+ return true;
+ }
+
+ return false;
}
/* Get the number of log entries */
@@ -587,9 +1388,54 @@ int BM_log_length(const BMLog *log)
return BLI_listbase_count(&log->entries);
}
+void BM_log_print_entry(BMLog *log, BMLogEntry *entry)
+{
+ BMLogEntry *first = entry;
+
+ if (!log) {
+ log = entry->log;
+ }
+
+ while (first->combined_prev) {
+ first = first->combined_prev;
+ }
+
+ printf("==bmlog step==\n");
+
+ while (first) {
+ switch (first->type) {
+ case LOG_ENTRY_FULL_MESH:
+ printf(" ==full mesh copy==\n");
+ break;
+ case LOG_ENTRY_MESH_IDS:
+ printf("==element IDs snapshot\n");
+ break;
+ case LOG_ENTRY_PARTIAL:
+ printf("==modified: ");
+ printf("v: %d ", BLI_ghash_len(first->modified_verts));
+ printf("e: %d ", BLI_ghash_len(first->modified_edges));
+ printf("f: %d ", BLI_ghash_len(first->modified_faces));
+ printf(" new: ");
+ printf("v: %d ", BLI_ghash_len(first->added_verts));
+ printf("e: %d ", BLI_ghash_len(first->added_edges));
+ printf("f: %d ", BLI_ghash_len(first->added_faces));
+ printf(" deleted: ");
+ printf("v: %d ", BLI_ghash_len(first->deleted_verts));
+ printf("e: %d ", BLI_ghash_len(first->deleted_edges));
+ printf("pe: %d ", BLI_ghash_len(first->deleted_edges_post));
+ printf("f: %d ", BLI_ghash_len(first->deleted_faces));
+ printf("\n");
+ break;
+ }
+
+ first = first->combined_next;
+ }
+}
+
/* Apply a consistent ordering to BMesh vertices */
void BM_log_mesh_elems_reorder(BMesh *bm, BMLog *log)
{
+#if 0 // TODO: make sure no edge cases relying on this function still exist
uint *varr;
uint *farr;
@@ -618,7 +1464,7 @@ void BM_log_mesh_elems_reorder(BMesh *bm, BMLog *log)
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);
+ const void *val = log_ghash_lookup(log, id_to_idx, key);
varr[i] = POINTER_AS_UINT(val);
}
BLI_ghash_free(id_to_idx, NULL, NULL);
@@ -628,15 +1474,49 @@ void BM_log_mesh_elems_reorder(BMesh *bm, BMLog *log)
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);
+ const void *val = log_ghash_lookup(log, 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);
+ BM_mesh_remap(bm, varr, NULL, farr, NULL);
MEM_freeN(varr);
MEM_freeN(farr);
+#endif
+}
+
+BMLogEntry *BM_log_entry_check_customdata(BMesh *bm, BMLog *log)
+{
+ BMLogEntry *entry = log->current_entry;
+
+ if (!entry) {
+ printf("no current entry; creating...\n");
+ fflush(stdout);
+ return BM_log_entry_add_ex(bm, log, false);
+ }
+
+ if (entry->type != LOG_ENTRY_PARTIAL) {
+ return BM_log_entry_add_ex(bm, log, true);
+ }
+
+#ifndef CUSTOMDATA
+ return entry;
+#else
+
+ 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])) {
+ printf("Customdata changed for undo\n");
+ fflush(stdout);
+ return BM_log_entry_add_ex(bm, log, true);
+ }
+ }
+
+ return entry;
+#endif
}
/* Start a new log entry and update the log entry list
@@ -649,8 +1529,22 @@ void BM_log_mesh_elems_reorder(BMesh *bm, BMLog *log)
*
* In either case, the new entry is set as the current log entry.
*/
-BMLogEntry *BM_log_entry_add(BMLog *log)
+BMLogEntry *BM_log_entry_add(BMesh *bm, BMLog *log)
{
+ return BM_log_entry_add_ex(bm, log, false);
+}
+
+BMLogEntry *bm_log_entry_add_ex(
+ BMesh *bm, BMLog *log, bool combine_with_last, BMLogEntryType type, BMLogEntry *last_entry)
+{
+ if (log->dead) {
+ fprintf(stderr, "BMLog Error: log is dead\n");
+ fflush(stderr);
+ return NULL;
+ }
+
+ log->bm = bm;
+
/* WARNING: this is now handled by the UndoSystem: BKE_UNDOSYS_TYPE_SCULPT
* freeing here causes unnecessary complications. */
BMLogEntry *entry;
@@ -668,14 +1562,53 @@ BMLogEntry *BM_log_entry_add(BMLog *log)
#endif
/* Create and append the new entry */
- entry = bm_log_entry_create();
- BLI_addtail(&log->entries, entry);
+ entry = bm_log_entry_create(type);
+
+ if (!last_entry || last_entry == log->current_entry) {
+ BLI_addtail(&log->entries, entry);
+ }
+
entry->log = log;
+
+ log->refcount++;
+
+ if (combine_with_last) {
+ if (!last_entry || last_entry == log->current_entry) {
+ if (log->current_entry) {
+ log->current_entry->combined_next = entry;
+ BLI_remlink(&log->entries, log->current_entry);
+ }
+
+ entry->combined_prev = log->current_entry;
+ }
+ else {
+ entry->combined_prev = last_entry;
+ last_entry->combined_next = entry;
+ }
+ }
+
+ if (type == LOG_ENTRY_PARTIAL) {
+ CustomData_copy_all_layout(&bm->vdata, &entry->vdata);
+ CustomData_copy_all_layout(&bm->edata, &entry->edata);
+ CustomData_copy_all_layout(&bm->ldata, &entry->ldata);
+ CustomData_copy_all_layout(&bm->pdata, &entry->pdata);
+
+ CustomData_bmesh_init_pool(&entry->vdata, 0, BM_VERT);
+ CustomData_bmesh_init_pool(&entry->edata, 0, BM_EDGE);
+ CustomData_bmesh_init_pool(&entry->ldata, 0, BM_LOOP);
+ CustomData_bmesh_init_pool(&entry->pdata, 0, BM_FACE);
+ }
+
log->current_entry = entry;
return entry;
}
+BMLogEntry *BM_log_entry_add_ex(BMesh *bm, BMLog *log, bool combine_with_last)
+{
+ return bm_log_entry_add_ex(bm, log, combine_with_last, LOG_ENTRY_PARTIAL, NULL);
+}
+
/* Remove an entry from the log
*
* Uses entry->log as the log. If the log is NULL, the entry will be
@@ -689,6 +1622,11 @@ void BM_log_entry_drop(BMLogEntry *entry)
{
BMLog *log = entry->log;
+ // go to head of entry subgroup
+ while (entry->combined_next) {
+ entry = entry->combined_next;
+ }
+
if (!log) {
/* Unlink */
BLI_assert(!(entry->prev && entry->next));
@@ -699,88 +1637,521 @@ void BM_log_entry_drop(BMLogEntry *entry)
entry->next->prev = NULL;
}
+ BMLogEntry *entry2 = entry->combined_prev;
+ while (entry2) {
+ BMLogEntry *prev = entry2->combined_prev;
+
+ bm_log_entry_free(entry2);
+ MEM_freeN(entry2);
+
+ entry2 = prev;
+ }
+
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);
+ if (log && log->current_entry == entry) {
+ log->current_entry = entry->prev;
}
- else {
- BLI_assert_msg(0, "Cannot drop BMLogEntry from middle");
+
+ if (log) {
+ BLI_remlink(&log->entries, entry);
}
- if (log->current_entry == entry) {
- log->current_entry = entry->prev;
+ // free subentries first
+ BMLogEntry *entry2 = entry->combined_prev;
+ while (entry2) {
+ BMLogEntry *prev = entry2->combined_prev;
+
+ bm_log_entry_free(entry2);
+ MEM_freeN(entry2);
+ entry2 = prev;
}
bm_log_entry_free(entry);
- BLI_freelinkN(&log->entries, entry);
+ MEM_freeN(entry);
+}
+
+static void full_copy_load(BMesh *bm, BMLog *log, BMLogEntry *entry)
+{
+ CustomData_MeshMasks cd_mask_extra = {CD_MASK_DYNTOPO_VERT, 0, 0, 0, 0};
+
+ BM_mesh_clear(bm);
+ BM_mesh_bm_from_me(NULL,
+ bm,
+ entry->full_copy_mesh,
+ (&(struct BMeshFromMeshParams){.calc_face_normal = false,
+ .add_key_index = false,
+ .use_shapekey = false,
+ .active_shapekey = -1,
+
+ .cd_mask_extra = cd_mask_extra,
+ .copy_temp_cdlayers = true,
+ .ignore_id_layers = false}));
+
+ bm->elem_index_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);
+}
+
+static void log_idmap_free(BMLogEntry *entry)
+{
+ for (int i = 0; i < 4; i++) {
+ int type = 1 << i;
+
+ MEM_SAFE_FREE(entry->idmap.maps[type]);
+ entry->idmap.maps[type] = NULL;
+ entry->idmap.elemtots[type] = 0;
+ }
+}
+
+static void log_idmap_save(BMesh *bm, BMLog *log, BMLogEntry *entry)
+{
+ log_idmap_free(entry);
+
+ entry->type = LOG_ENTRY_MESH_IDS;
+ memset((void *)&entry->idmap, 0, sizeof(entry->idmap));
+
+ entry->idmap.elemmask = BM_VERT | BM_EDGE | BM_FACE;
+ BMLogIdMap *idmap = &entry->idmap;
+
+ BMIter iter;
+
+ int cd_id_offs[4] = {CustomData_get_offset(&bm->vdata, CD_MESH_ID),
+ CustomData_get_offset(&bm->edata, CD_MESH_ID),
+ CustomData_get_offset(&bm->ldata, CD_MESH_ID),
+ CustomData_get_offset(&bm->pdata, CD_MESH_ID)};
+
+ const char iters[] = {BM_VERTS_OF_MESH, BM_EDGES_OF_MESH, 0, BM_FACES_OF_MESH};
+ int tots[] = {bm->totvert, bm->totedge, bm->totloop, bm->totface};
+
+ // enforce elemmask
+ for (int i = 0; i < 4; i++) {
+ int type = 1 << i;
+
+ if (!(idmap->elemmask & type) || !tots[i]) {
+ tots[i] = 0;
+ cd_id_offs[i] = -1;
+ }
+ }
+
+ // set up loop map which is handled specially
+ if (cd_id_offs[2] >= 0 && tots[2] > 0) {
+ idmap->maps[BM_LOOP] = MEM_malloc_arrayN((size_t)tots[2], sizeof(int), "idmap->maps[BM_LOOP]");
+ }
+
+ for (int i = 0; i < 4; i++) {
+ if (i == 2) { // loops are saved in face pass
+ continue;
+ }
+
+ int type = 1 << i;
+ const int cd_off = cd_id_offs[i];
+ const int tot = tots[i];
+
+ idmap->elemtots[type] = tot;
+
+ if (cd_off < 0 || tot == 0) {
+ continue;
+ }
+
+ int *map = idmap->maps[type] = MEM_malloc_arrayN(
+ (size_t)tot, sizeof(int), "idmap->maps entry");
+
+ BMElem *elem;
+ int j = 0;
+ int loopi = 0;
+ int cd_loop_off = cd_id_offs[2];
+ int *lmap = idmap->maps[2];
+
+ bool reported = false;
+
+ BM_ITER_MESH_INDEX (elem, &iter, bm, iters[i], j) {
+ int id = BM_ELEM_CD_GET_INT(elem, cd_off);
+
+ if (!reported && (BMElem *)BM_ELEM_FROM_ID(bm, id) != elem) {
+ printf("IDMap error for elem type %d\n", elem->head.htype);
+ printf(" further errors suppressed\n");
+ reported = true;
+ }
+
+ map[j] = id;
+
+ // deal with loops
+ if (type == BM_FACE && cd_loop_off >= 0 && lmap) {
+ BMFace *f = (BMFace *)elem;
+ BMLoop *l = f->l_first;
+
+ do {
+ lmap[loopi++] = BM_ELEM_CD_GET_INT(l, cd_loop_off);
+ } while ((l = l->next) != f->l_first);
+ }
+ }
+
+ if (type == BM_FACE) {
+ idmap->elemtots[BM_LOOP] = loopi;
+ }
+ }
+}
+
+static void log_idmap_load(BMesh *bm, BMLog *log, BMLogEntry *entry)
+{
+ const int cd_id_offs[4] = {CustomData_get_offset(&bm->vdata, CD_MESH_ID),
+ CustomData_get_offset(&bm->edata, CD_MESH_ID),
+ CustomData_get_offset(&bm->ldata, CD_MESH_ID),
+ CustomData_get_offset(&bm->pdata, CD_MESH_ID)};
+
+ const char iters[] = {BM_VERTS_OF_MESH, BM_EDGES_OF_MESH, 0, BM_FACES_OF_MESH};
+ const int tots[] = {bm->totvert, bm->totedge, bm->totloop, bm->totface};
+ BMLogIdMap *idmap = &entry->idmap;
+
+ BM_clear_ids(bm);
+
+ for (int i = 0; i < 4; i++) {
+ int type = 1 << i;
+
+ if (!(idmap->elemmask & type) || i == 2) {
+ continue;
+ }
+
+ if (cd_id_offs[i] < 0) {
+ printf("mesh doesn't have ids for elem type %d\n", type);
+ continue;
+ }
+
+ if (idmap->elemtots[type] != tots[i]) {
+ printf("idmap elem count mismatch error");
+ continue;
+ }
+
+ if (!idmap->elemtots[type]) {
+ continue;
+ }
+
+ const int cd_loop_id = (idmap->elemmask & type) ? cd_id_offs[2] : -1;
+
+ int j = 0;
+ BMElem *elem;
+ BMIter iter;
+ int *map = idmap->maps[type];
+ int loopi = 0;
+ int *lmap = idmap->maps[BM_LOOP];
+
+ BM_ITER_MESH_INDEX (elem, &iter, bm, iters[i], j) {
+ bm_assign_id(bm, elem, (uint)map[j], false);
+
+ // deal with loops
+ if (type == BM_FACE && cd_loop_id >= 0) {
+ BMFace *f = (BMFace *)elem;
+ BMLoop *l = f->l_first;
+
+ do {
+ bm_assign_id(bm, (BMElem *)l, (uint)lmap[loopi], false);
+
+ loopi++;
+ } while ((l = l->next) != f->l_first);
+ }
+ }
+ }
+}
+
+static void log_idmap_swap(BMesh *bm, BMLog *log, BMLogEntry *entry)
+{
+ const int cd_id_offs[4] = {CustomData_get_offset(&bm->vdata, CD_MESH_ID),
+ CustomData_get_offset(&bm->edata, CD_MESH_ID),
+ CustomData_get_offset(&bm->ldata, CD_MESH_ID),
+ CustomData_get_offset(&bm->pdata, CD_MESH_ID)};
+
+ const char iters[] = {BM_VERTS_OF_MESH, BM_EDGES_OF_MESH, 0, BM_FACES_OF_MESH};
+ const int tots[] = {bm->totvert, bm->totedge, bm->totloop, bm->totface};
+ BMLogIdMap *idmap = &entry->idmap;
+
+ BM_clear_ids(bm);
+
+ for (int i = 0; i < 4; i++) {
+ int type = 1 << i;
+
+ if (!(idmap->elemmask & type) || i == 2) {
+ continue;
+ }
+
+ if (cd_id_offs[i] < 0) {
+ printf("mesh doesn't have ids for elem type %d\n", type);
+ continue;
+ }
+
+ if (idmap->elemtots[type] != tots[i]) {
+ printf("idmap elem count mismatch error");
+ continue;
+ }
+
+ if (!idmap->elemtots[type]) {
+ continue;
+ }
+
+ const int cd_loop_id = (idmap->elemmask & type) ? cd_id_offs[2] : -1;
+
+ int cd_id = cd_id_offs[i];
+ int j = 0;
+ BMElem *elem;
+ BMIter iter;
+ int *map = idmap->maps[type];
+ int loopi = 0;
+ int *lmap = idmap->maps[BM_LOOP];
+
+ BM_ITER_MESH_INDEX (elem, &iter, bm, iters[i], j) {
+ int id = BM_ELEM_CD_GET_INT(elem, cd_id);
+
+ bm_assign_id(bm, elem, (uint)map[j], false);
+ map[j] = id;
+
+ // deal with loops
+ if (type == BM_FACE && cd_loop_id >= 0) {
+ BMFace *f = (BMFace *)elem;
+ BMLoop *l = f->l_first;
+
+ do {
+ int id2 = BM_ELEM_CD_GET_INT(l, cd_loop_id);
+
+ bm_assign_id(bm, (BMElem *)l, (uint)lmap[loopi], false);
+ lmap[loopi] = id2;
+
+ loopi++;
+ } while ((l = l->next) != f->l_first);
+ }
+ }
+ }
+}
+
+void BM_log_set_current_entry(BMLog *log, BMLogEntry *entry)
+{
+ // you cannot set the current entry to a sub-entry, so this should never happen.
+ while (entry && entry->combined_next) {
+ entry = entry->combined_next;
+ }
+
+ log->current_entry = entry;
+}
+
+BMLogEntry *BM_log_all_ids(BMesh *bm, BMLog *log, BMLogEntry *entry)
+{
+ if (!entry) {
+ entry = bm_log_entry_add_ex(bm, log, false, LOG_ENTRY_MESH_IDS, NULL);
+ }
+ else if (entry->type != LOG_ENTRY_MESH_IDS) {
+ entry = bm_log_entry_add_ex(bm, log, true, LOG_ENTRY_MESH_IDS, entry);
+ }
+
+ if (!entry) {
+ // log was dead
+ return NULL;
+ }
+
+ log_idmap_save(bm, log, entry);
+ return entry;
+}
+
+static void full_copy_swap(BMesh *bm, BMLog *log, BMLogEntry *entry)
+{
+ CustomData_MeshMasks cd_mask_extra = {CD_MASK_DYNTOPO_VERT, 0, 0, 0, 0};
+
+ BMLogEntry tmp = {0};
+
+ bm_log_full_mesh_intern(bm, log, &tmp);
+
+ BM_mesh_clear(bm);
+ BM_mesh_bm_from_me(NULL,
+ bm,
+ entry->full_copy_mesh,
+ (&(struct BMeshFromMeshParams){.calc_face_normal = false,
+ .add_key_index = false,
+ .use_shapekey = false,
+ .active_shapekey = -1,
+
+ .cd_mask_extra = cd_mask_extra,
+ .copy_temp_cdlayers = true,
+ .ignore_id_layers = false}));
+
+ 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(entry->full_copy_mesh);
+
+ entry->full_copy_mesh = tmp.full_copy_mesh;
}
/* Undo one BMLogEntry
*
* Has no effect if there's nothing left to undo */
-void BM_log_undo(BMesh *bm, BMLog *log)
+static void bm_log_undo_intern(
+ BMesh *bm, BMLog *log, BMLogEntry *entry, BMLogCallbacks *callbacks, const char *node_layer_id)
+{
+ bm->elem_index_dirty |= BM_VERT | BM_EDGE | BM_FACE;
+ bm->elem_table_dirty |= BM_VERT | BM_EDGE | BM_FACE;
+
+ if (entry->type == LOG_ENTRY_FULL_MESH) {
+ full_copy_swap(bm, log, entry);
+
+ if (callbacks) {
+ callbacks->on_full_mesh_load(callbacks->userdata);
+ }
+ return;
+ }
+ else if (entry->type == LOG_ENTRY_MESH_IDS) {
+ log_idmap_load(bm, log, entry);
+
+ if (callbacks && callbacks->on_mesh_id_restore) {
+ callbacks->on_mesh_id_restore(callbacks->userdata);
+ }
+ return;
+ }
+
+ bm_log_edges_restore(bm, log, entry->deleted_edges_post, entry, callbacks);
+
+ /* Delete added faces and verts */
+ bm_log_edges_unmake_pre(bm, log, entry->added_edges, entry, callbacks);
+ bm_log_verts_unmake_pre(bm, log, entry->added_verts, entry, callbacks);
+
+ bm_log_faces_unmake(bm, log, entry->added_faces, entry, callbacks);
+ bm_log_edges_unmake(bm, log, entry->added_edges, entry, callbacks);
+ bm_log_verts_unmake(bm, log, entry->added_verts, entry, callbacks);
+
+ /* Restore deleted verts and faces */
+ bm_log_verts_restore(bm, log, entry->deleted_verts, entry, callbacks);
+ bm_log_edges_restore(bm, log, entry->deleted_edges, entry, callbacks);
+ bm_log_faces_restore(bm, log, entry->deleted_faces, entry, callbacks);
+
+ /* Restore vertex coordinates, mask, and hflag */
+ bm_log_vert_values_swap(bm, log, entry->modified_verts, entry, callbacks);
+ bm_log_edge_values_swap(bm, log, entry->modified_edges, entry, callbacks);
+ bm_log_face_values_swap(log, entry->modified_faces, entry, callbacks);
+}
+
+void BM_log_undo_skip(BMesh *bm, BMLog *log)
+{
+ if (log->current_entry) {
+ log->current_entry = log->current_entry->prev;
+ }
+}
+
+void BM_log_redo_skip(BMesh *bm, BMLog *log)
+{
+ if (log->current_entry) {
+ log->current_entry = log->current_entry->next;
+ }
+ else {
+ log->current_entry = log->entries.first;
+ }
+}
+
+void BM_log_undo_single(BMesh *bm,
+ BMLog *log,
+ BMLogCallbacks *callbacks,
+ const char *node_layer_id)
{
BMLogEntry *entry = log->current_entry;
+ log->bm = bm;
- if (entry) {
- log->current_entry = entry->prev;
+ if (!entry) {
+ return;
+ }
- /* Delete added faces and verts */
- bm_log_faces_unmake(bm, log, entry->added_faces);
- bm_log_verts_unmake(bm, log, entry->added_verts);
+ BMLogEntry *preventry = entry->prev;
- /* Restore deleted verts and faces */
- bm_log_verts_restore(bm, log, entry->deleted_verts);
- bm_log_faces_restore(bm, log, entry->deleted_faces);
+ bm_log_undo_intern(bm, log, entry, callbacks, node_layer_id);
+ entry = entry->combined_prev;
- /* 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);
+ log->current_entry = entry ? entry : preventry;
+}
+
+void BM_log_undo(BMesh *bm, BMLog *log, BMLogCallbacks *callbacks, const char *node_layer_id)
+{
+ BMLogEntry *entry = log->current_entry;
+ log->bm = bm;
+
+ if (!entry) {
+ return;
+ }
+
+ BMLogEntry *preventry = entry->prev;
+
+ while (entry) {
+ bm_log_undo_intern(bm, log, entry, callbacks, node_layer_id);
+ entry = entry->combined_prev;
}
+
+ log->current_entry = preventry;
}
/* Redo one BMLogEntry
*
* Has no effect if there's nothing left to redo */
-void BM_log_redo(BMesh *bm, BMLog *log)
+static void bm_log_redo_intern(
+ BMesh *bm, BMLog *log, BMLogEntry *entry, BMLogCallbacks *callbacks, const char *node_layer_id)
+{
+ if (entry->type == LOG_ENTRY_FULL_MESH) {
+ // hrm, should we swap?
+ full_copy_swap(bm, log, entry);
+
+ if (callbacks) {
+ callbacks->on_full_mesh_load(callbacks->userdata);
+ }
+
+ return;
+ }
+ else if (entry->type == LOG_ENTRY_MESH_IDS) {
+ log_idmap_load(bm, log, entry);
+
+ if (callbacks && callbacks->on_mesh_id_restore) {
+ callbacks->on_mesh_id_restore(callbacks->userdata);
+ }
+ return;
+ }
+
+ bm->elem_index_dirty |= BM_VERT | BM_EDGE | BM_FACE;
+ bm->elem_table_dirty |= BM_VERT | BM_EDGE | BM_FACE;
+
+ /* Re-delete previously deleted faces and verts */
+ bm_log_edges_unmake_pre(bm, log, entry->deleted_edges, entry, callbacks);
+ bm_log_verts_unmake_pre(bm, log, entry->deleted_verts, entry, callbacks);
+
+ bm_log_faces_unmake(bm, log, entry->deleted_faces, entry, callbacks);
+ bm_log_edges_unmake(bm, log, entry->deleted_edges, entry, callbacks);
+ bm_log_verts_unmake(bm, log, entry->deleted_verts, entry, callbacks);
+
+ /* Restore previously added verts and faces */
+ bm_log_verts_restore(bm, log, entry->added_verts, entry, callbacks);
+ bm_log_edges_restore(bm, log, entry->added_edges, entry, callbacks);
+ bm_log_faces_restore(bm, log, entry->added_faces, entry, callbacks);
+
+ bm_log_edges_unmake(bm, log, entry->deleted_edges_post, entry, callbacks);
+
+ /* Restore vertex coordinates, mask, and hflag */
+ bm_log_vert_values_swap(bm, log, entry->modified_verts, entry, callbacks);
+ bm_log_edge_values_swap(bm, log, entry->modified_edges, entry, callbacks);
+ bm_log_face_values_swap(log, entry->modified_faces, entry, callbacks);
+}
+
+BMLogEntry *BM_log_entry_prev(BMLogEntry *entry)
+{
+ return entry->prev;
+}
+
+BMLogEntry *BM_log_entry_next(BMLogEntry *entry)
+{
+ return entry->next;
+}
+
+void BM_log_redo(BMesh *bm, BMLog *log, BMLogCallbacks *callbacks, const char *node_layer_id)
{
BMLogEntry *entry = log->current_entry;
+ log->bm = bm;
if (!entry) {
/* Currently at the beginning of the undo stack, move to first entry */
@@ -790,26 +2161,24 @@ void BM_log_redo(BMesh *bm, BMLog *log)
/* Move to next undo entry */
entry = entry->next;
}
- else {
+
+ if (!entry) {
/* 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);
+ BMLogEntry *nextentry = entry;
- /* Restore previously added verts and faces */
- bm_log_verts_restore(bm, log, entry->added_verts);
- bm_log_faces_restore(bm, log, entry->added_faces);
+ while (entry->combined_prev) {
+ entry = entry->combined_prev;
+ }
- /* 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);
+ while (entry) {
+ bm_log_redo_intern(bm, log, entry, callbacks, node_layer_id);
+ entry = entry->combined_next;
}
+
+ log->current_entry = nextentry;
}
/* Log a vertex before it is modified
@@ -835,54 +2204,99 @@ void BM_log_redo(BMesh *bm, BMLog *log)
* state so that a subsequent redo operation will restore the newer
* vertex state.
*/
-void BM_log_vert_before_modified(BMLog *log, BMVert *v, const int cd_vert_mask_offset)
+void BM_log_vert_before_modified(BMLog *log,
+ BMVert *v,
+ const int cd_vert_mask_offset,
+ bool log_customdata)
{
BMLogEntry *entry = log->current_entry;
BMLogVert *lv;
- uint v_id = bm_log_vert_id_get(log, v);
+ uint v_id = (uint)BM_ELEM_GET_ID(log->bm, v);
void *key = POINTER_FROM_UINT(v_id);
void **val_p;
+ // LOGPRINT("key %d\n", (int)key);
+
/* 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);
+ if ((lv = log_ghash_lookup(log, entry->added_verts, key))) {
+ bm_log_vert_bmvert_copy(log, entry, lv, v, -1, log_customdata);
}
- else if (!BLI_ghash_ensure_p(entry->modified_verts, key, &val_p)) {
- lv = bm_log_vert_alloc(log, v, cd_vert_mask_offset);
+ else if (!log_ghash_ensure_p(log, entry->modified_verts, key, &val_p)) {
+ lv = bm_log_vert_alloc(log, v, -1, true);
*val_p = lv;
}
}
+void BM_log_edge_before_modified(BMLog *log, BMEdge *e, bool log_customdata)
+{
+ BMLogEntry *entry = log->current_entry;
+ BMLogEdge *le;
+ uint e_id = (uint)BM_ELEM_GET_ID(log->bm, e);
+ void *key = POINTER_FROM_UINT(e_id);
+ void **val_p;
+
+ /* Find or create the BMLogVert entry */
+ if ((le = log_ghash_lookup(log, entry->added_edges, key))) {
+ bm_log_edge_bmedge_copy(log, entry, le, e, log_customdata);
+ }
+ else if (!log_ghash_ensure_p(log, entry->modified_edges, key, &val_p)) {
+ le = bm_log_edge_alloc(log, e, true);
+ *val_p = le;
+ }
+}
+
+/* Log a new edge as added to the BMesh
+ */
+void BM_log_edge_added(BMLog *log, BMEdge *e)
+{
+ // return; // XXX
+ BMLogEdge *le;
+ uint e_id = (uint)BM_ELEM_GET_ID(log->bm, e);
+ void *key = POINTER_FROM_UINT(e_id);
+ void **val = NULL;
+
+ LOGPRINT("key %d\n", (int)key);
+
+ le = bm_log_edge_alloc(log, e, true);
+ SET_MSG(le);
+
+ if (BLI_ghash_ensure_p(log->current_entry->added_edges, key, &val)) {
+ BLI_mempool_free(log->current_entry->pool_edges, *val);
+ }
+
+ *val = le;
+}
+
/* 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, BMVert *v, const int cd_vert_mask_offset)
{
BMLogVert *lv;
- uint v_id = range_tree_uint_take_any(log->unused_ids);
+ uint v_id = (uint)BM_ELEM_GET_ID(log->bm, v);
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);
+ LOGPRINT("key %d\n", (int)key);
+
+ lv = bm_log_vert_alloc(log, v, -1, true);
+ log_ghash_insert(log, log->current_entry->added_verts, key, lv);
}
/* Log a face before it is modified
*
- * This is intended to handle only header flags and we always
- * assume face has been added before
+ * We always assume face has been added before
*/
void BM_log_face_modified(BMLog *log, BMFace *f)
{
BMLogFace *lf;
- uint f_id = bm_log_face_id_get(log, f);
+ uint f_id = (uint)BM_ELEM_GET_ID(log->bm, f);
void *key = POINTER_FROM_UINT(f_id);
+ // LOGPRINT("key %d\n", (int)key);
+
lf = bm_log_face_alloc(log, f);
- BLI_ghash_insert(log->current_entry->modified_faces, key, lf);
+
+ log_ghash_insert(log, log->current_entry->modified_faces, key, lf);
+ bm_log_face_customdata(log->bm, log, f, lf);
}
/* Log a new face as added to the BMesh
@@ -894,15 +2308,15 @@ void BM_log_face_modified(BMLog *log, BMFace *f)
void BM_log_face_added(BMLog *log, BMFace *f)
{
BMLogFace *lf;
- uint f_id = range_tree_uint_take_any(log->unused_ids);
+ uint f_id = (uint)BM_ELEM_GET_ID(log->bm, f);
void *key = POINTER_FROM_UINT(f_id);
- /* Only triangles are supported for now */
- BLI_assert(f->len == 3);
+ LOGPRINT("key %d\n", (int)key);
- 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);
+ log_ghash_insert(log, log->current_entry->added_faces, key, lf);
+
+ bm_log_face_customdata(log->bm, log, f, lf);
}
/* Log a vertex as removed from the BMesh
@@ -924,28 +2338,185 @@ void BM_log_face_added(BMLog *log, BMFace *f)
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);
+ uint v_id = (uint)BM_ELEM_GET_ID(log->bm, 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));
+ LOGPRINT("key %d\n", (int)key);
- if (BLI_ghash_remove(entry->added_verts, key, NULL, NULL)) {
- range_tree_uint_release(log->unused_ids, v_id);
- }
- else {
+ if (!log_ghash_remove(log, entry->added_verts, key, NULL, NULL)) {
BMLogVert *lv, *lv_mod;
- lv = bm_log_vert_alloc(log, v, cd_vert_mask_offset);
- BLI_ghash_insert(entry->deleted_verts, key, lv);
+ lv = bm_log_vert_alloc(log, v, -1, false);
+ log_ghash_insert(log, 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))) {
+ if ((lv_mod = log_ghash_lookup(log, entry->modified_verts, key))) {
+ if (lv->customdata) {
+ BLI_mempool_free(entry->vdata.pool, lv->customdata);
+ }
+
(*lv) = (*lv_mod);
- BLI_ghash_remove(entry->modified_verts, key, NULL, NULL);
+ lv_mod->customdata = NULL;
+
+ log_ghash_remove(log, entry->modified_verts, key, NULL, NULL);
+ BLI_mempool_free(entry->pool_verts, lv_mod);
+ }
+ else {
+ bm_log_vert_customdata(log->bm, log, entry, v, lv);
+ }
+ }
+}
+
+void BM_log_edge_removed_post(BMLog *log, BMEdge *e)
+{
+ BMLogEntry *entry = log->current_entry;
+ uint e_id = (uint)BM_ELEM_GET_ID(log->bm, e);
+ void *key = POINTER_FROM_UINT(e_id);
+
+ LOGPRINT("key %d\n", (int)key);
+
+ if (1) { //! log_ghash_remove(log, entry->added_edges, key, NULL, NULL)) {
+ BMLogEdge *le, *le_mod;
+ void **val;
+
+ le = bm_log_edge_alloc(log, e, false);
+ SET_MSG(le);
+
+ if (BLI_ghash_ensure_p(entry->deleted_edges_post, key, &val)) {
+ BLI_mempool_free(entry->pool_edges, *val);
}
+
+ *val = (void *)le;
+
+#if 1
+ /* If the vertex was modified before deletion, ensure that the
+ * original edge values are stored */
+ if ((le_mod = log_ghash_lookup(log, entry->modified_edges, key))) {
+ if (le->customdata) {
+ BLI_mempool_free(entry->edata.pool, le->customdata);
+ }
+
+ (*le) = (*le_mod);
+ le_mod->customdata = NULL;
+
+ SET_MSG(le);
+
+ log_ghash_remove(log, entry->modified_edges, key, NULL, NULL);
+ BLI_mempool_free(entry->pool_edges, le_mod);
+ }
+ else {
+ bm_log_edge_customdata(log->bm, log, entry, e, le);
+ }
+#else
+ bm_log_edge_customdata(log->bm, log, entry, e, le);
+#endif
+ }
+}
+
+/**
+Splits e and logs the new edge and vertex.
+e is assigned a new ID.
+*/
+BMVert *BM_log_edge_split_do(BMLog *log, BMEdge *e, BMVert *v, BMEdge **newe, float t)
+{
+#if 0
+ BMVert *newv = BM_edge_split(log->bm, e, v, newe, t);
+ BM_log_vert_added(log, newv, -1);
+
+ return newv;
+
+#else
+ BMEdge *tmp = NULL;
+ if (!newe) {
+ newe = &tmp;
+ }
+
+ BMesh *bm = log->bm;
+
+ int eid0 = BM_ELEM_GET_ID(bm, e);
+
+ bm_log_message("edge split");
+ bm_log_message(" esplit: remove edge %d", eid0);
+ BM_log_edge_removed(log, e);
+
+ BMVert *v1 = e->v1, *v2 = e->v2;
+ uint id1 = (uint)BM_ELEM_GET_ID(bm, v1);
+ uint id2 = (uint)BM_ELEM_GET_ID(bm, v2);
+
+ bm_log_message(" esplit: split edge %d (v1=%d v2=%d)", eid0, id1, id2);
+ BMVert *newv = BM_edge_split(log->bm, e, v, newe, t);
+
+ uint id3 = (uint)BM_ELEM_GET_ID(bm, newv);
+ uint nid = (uint)BM_ELEM_GET_ID(bm, (*newe));
+
+ // get a new id
+ uint id = range_tree_uint_take_any(log->bm->idmap.idtree);
+
+ bm_free_id(log->bm, (BMElem *)e);
+ bm_assign_id(log->bm, (BMElem *)e, id, false);
+
+ bm_log_message(" esplit: add new vert %d", id3);
+ BM_log_vert_added(log, newv, -1);
+
+ bm_log_message(" esplit: add old edge (with new id %d)", id);
+ BM_log_edge_added(log, e);
+
+ bm_log_message(" esplit: add new edge %d", nid);
+ BM_log_edge_added(log, *newe);
+
+ return newv;
+#endif
+}
+
+void BM_log_edge_removed(BMLog *log, BMEdge *e)
+{
+ if (e->head.htype != BM_EDGE) {
+ printf("%s: e is not an edge; htype: %d\n", __func__, (int)e->head.htype);
+ return;
+ }
+
+ // return; // XXX
+ BMLogEntry *entry = log->current_entry;
+ uint e_id = (uint)BM_ELEM_GET_ID(log->bm, e);
+ void *key = POINTER_FROM_UINT(e_id);
+
+ LOGPRINT("key %d\n", (int)key);
+
+ if (!log_ghash_remove(log, entry->added_edges, key, NULL, NULL)) {
+ BMLogEdge *le, *le_mod;
+ void **val;
+
+ le = bm_log_edge_alloc(log, e, false);
+ SET_MSG(le);
+
+ if (BLI_ghash_ensure_p(entry->deleted_edges, key, &val)) {
+ BLI_mempool_free(entry->pool_edges, *val);
+ }
+
+ *val = (void *)le;
+
+#if 1
+ /* If the edge was modified before deletion, ensure that the
+ * original edge values are stored */
+ if ((le_mod = log_ghash_lookup(log, entry->modified_edges, key))) {
+ if (le->customdata) {
+ BLI_mempool_free(entry->edata.pool, le->customdata);
+ }
+
+ (*le) = (*le_mod);
+ le_mod->customdata = NULL;
+ SET_MSG(le);
+
+ log_ghash_remove(log, entry->modified_edges, key, NULL, NULL);
+ BLI_mempool_free(entry->pool_edges, le_mod);
+ }
+ else {
+ bm_log_edge_customdata(log->bm, log, entry, e, le);
+ }
+#else
+ bm_log_edge_customdata(log->bm, log, entry, e, le);
+#endif
}
}
@@ -965,30 +2536,54 @@ void BM_log_vert_removed(BMLog *log, BMVert *v, const int cd_vert_mask_offset)
void BM_log_face_removed(BMLog *log, BMFace *f)
{
BMLogEntry *entry = log->current_entry;
- uint f_id = bm_log_face_id_get(log, f);
+ uint f_id = (uint)BM_ELEM_GET_ID(log->bm, f);
void *key = POINTER_FROM_UINT(f_id);
+ LOGPRINT("key %d\n", (int)key);
+
/* 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));
+ BLI_assert(!!log_ghash_lookup(log, entry->added_faces, key) ==
+ !!log_ghash_haskey(log, 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;
+ if (!log_ghash_remove(log, entry->added_faces, key, NULL, NULL)) {
+ BMLogFace *lf = bm_log_face_alloc(log, f);
+
+ void **val;
+ if (BLI_ghash_ensure_p(entry->deleted_faces, key, &val)) {
+ BMLogFace *lf2 = (BMLogFace *)*val;
- lf = bm_log_face_alloc(log, f);
- BLI_ghash_insert(entry->deleted_faces, key, lf);
+ if (lf2->customdata_f) {
+ // BLI_mempool_free(entry->pdata.pool, lf2->customdata_f);
+ CustomData_bmesh_free_block(&entry->pdata, &lf2->customdata_f);
+ }
+
+ for (uint i = 0; i < lf2->len; i++) {
+ if (lf2->customdata[i]) {
+ CustomData_bmesh_free_block(&entry->ldata, &lf2->customdata[i]);
+ }
+ }
+
+ BLI_mempool_free(entry->pool_faces, (void *)lf2);
+ }
+
+ if (lf) {
+ bm_log_face_customdata(log->bm, log, f, lf);
+ }
+
+ *val = lf;
}
}
/* Log all vertices/faces in the BMesh as added */
void BM_log_all_added(BMesh *bm, BMLog *log)
{
- const int cd_vert_mask_offset = CustomData_get_offset(&bm->vdata, CD_PAINT_MASK);
+ if (!log->current_entry) {
+ BM_log_entry_add_ex(bm, log, false);
+ }
+
BMIter bm_iter;
BMVert *v;
+ BMEdge *e;
BMFace *f;
/* avoid unnecessary resizing on initialization */
@@ -1002,7 +2597,12 @@ void BM_log_all_added(BMesh *bm, BMLog *log)
/* 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);
+ BM_log_vert_added(log, v, -1);
+ }
+
+ /* Log all edges as newly created */
+ BM_ITER_MESH (e, &bm_iter, bm, BM_EDGES_OF_MESH) {
+ BM_log_edge_added(log, e);
}
/* Log all faces as newly created */
@@ -1011,12 +2611,52 @@ void BM_log_all_added(BMesh *bm, BMLog *log)
}
}
+void BM_log_full_mesh(BMesh *bm, BMLog *log)
+{
+ BMLogEntry *entry = log->current_entry;
+
+ if (!entry) {
+ entry = bm_log_entry_add_ex(bm, log, false, LOG_ENTRY_FULL_MESH, NULL);
+ }
+
+ // add an entry if current entry isn't empty or isn't LOG_ENTRY_PARTIAL
+ bool add = false;
+
+ if (entry->type == LOG_ENTRY_PARTIAL) {
+ add = BLI_ghash_len(entry->added_faces) > 0;
+ add |= BLI_ghash_len(entry->modified_verts) > 0;
+ add |= BLI_ghash_len(entry->modified_faces) > 0;
+ add |= BLI_ghash_len(entry->deleted_verts) > 0;
+ add |= BLI_ghash_len(entry->deleted_faces) > 0;
+ }
+ else {
+ add = true;
+ }
+
+ if (add) {
+ entry = bm_log_entry_add_ex(bm, log, true, LOG_ENTRY_FULL_MESH, NULL);
+ }
+ else {
+ bm_log_entry_free_direct(entry);
+ entry->type = LOG_ENTRY_FULL_MESH;
+ }
+
+ bm_log_full_mesh_intern(bm, log, entry);
+
+ // push a fresh entry
+ BM_log_entry_add_ex(bm, log, true);
+}
+
/* Log all vertices/faces in the BMesh as removed */
void BM_log_before_all_removed(BMesh *bm, BMLog *log)
{
- const int cd_vert_mask_offset = CustomData_get_offset(&bm->vdata, CD_PAINT_MASK);
+ if (!log->current_entry) {
+ BM_log_entry_add_ex(bm, log, false);
+ }
+
BMIter bm_iter;
BMVert *v;
+ BMEdge *e;
BMFace *f;
/* Log deletion of all faces */
@@ -1024,9 +2664,13 @@ void BM_log_before_all_removed(BMesh *bm, BMLog *log)
BM_log_face_removed(log, f);
}
+ BM_ITER_MESH (e, &bm_iter, bm, BM_EDGES_OF_MESH) {
+ BM_log_edge_removed(log, e);
+ }
+
/* 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);
+ BM_log_vert_removed(log, v, -1);
}
}
@@ -1037,65 +2681,57 @@ 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);
+ uint v_id = (uint)BM_ELEM_GET_ID(log->bm, v);
void *key = POINTER_FROM_UINT(v_id);
BLI_assert(entry);
- BLI_assert(BLI_ghash_haskey(entry->modified_verts, key));
+ BLI_assert(log_ghash_haskey(log, entry->modified_verts, key));
- lv = BLI_ghash_lookup(entry->modified_verts, key);
+ lv = log_ghash_lookup(log, entry->modified_verts, key);
return lv->co;
}
/* Get the logged normal of a vertex
*
* Does not modify the log or the vertex */
-const short *BM_log_original_vert_no(BMLog *log, BMVert *v)
+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);
+ uint v_id = (uint)BM_ELEM_GET_ID(log->bm, v);
void *key = POINTER_FROM_UINT(v_id);
BLI_assert(entry);
- BLI_assert(BLI_ghash_haskey(entry->modified_verts, key));
+ BLI_assert(log_ghash_haskey(log, entry->modified_verts, key));
- lv = BLI_ghash_lookup(entry->modified_verts, key);
+ lv = log_ghash_lookup(log, entry->modified_verts, key);
return lv->no;
}
-/* Get the logged mask of a vertex
+/* DEPRECATED
+ * Get the logged mask of a vertex
*
* Does not modify the log or the vertex */
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;
+ MDynTopoVert *mv = BM_ELEM_CD_GET_VOID_P(v, log->cd_dyn_vert);
+ return mv->origmask;
}
-void BM_log_original_vert_data(BMLog *log, BMVert *v, const float **r_co, const short **r_no)
+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);
+ uint v_id = (uint)BM_ELEM_GET_ID(log->bm, v);
void *key = POINTER_FROM_UINT(v_id);
BLI_assert(entry);
- BLI_assert(BLI_ghash_haskey(entry->modified_verts, key));
+ BLI_assert(log_ghash_haskey(log, entry->modified_verts, key));
- lv = BLI_ghash_lookup(entry->modified_verts, key);
+ lv = log_ghash_lookup(log, entry->modified_verts, key);
*r_co = lv->co;
*r_no = lv->no;
}
@@ -1108,12 +2744,6 @@ BMLogEntry *BM_log_current_entry(BMLog *log)
return log->current_entry;
}
-/* For internal use only (unit testing) */
-RangeTreeUInt *BM_log_unused_ids(BMLog *log)
-{
- return log->unused_ids;
-}
-
#if 0
/* Print the list of entries, marking the current one
*
@@ -1131,3 +2761,58 @@ void bm_log_print(const BMLog *log, const char *description)
}
}
#endif
+
+static int bmlog_entry_memsize(BMLogEntry *entry)
+{
+ int ret = 0;
+
+ if (entry->type == LOG_ENTRY_PARTIAL) {
+ ret += (int)BLI_mempool_get_size(entry->pool_verts);
+ ret += (int)BLI_mempool_get_size(entry->pool_edges);
+ ret += (int)BLI_mempool_get_size(entry->pool_faces);
+ ret += entry->vdata.pool ? (int)BLI_mempool_get_size(entry->vdata.pool) : 0;
+ ret += entry->edata.pool ? (int)BLI_mempool_get_size(entry->edata.pool) : 0;
+ ret += entry->ldata.pool ? (int)BLI_mempool_get_size(entry->ldata.pool) : 0;
+ ret += entry->pdata.pool ? (int)BLI_mempool_get_size(entry->pdata.pool) : 0;
+
+ // estimate ghash memory usage
+ ret += (int)BLI_ghash_len(entry->added_verts) * (int)sizeof(void *) * 4;
+ ret += (int)BLI_ghash_len(entry->added_edges) * (int)sizeof(void *) * 4;
+ ret += (int)BLI_ghash_len(entry->added_faces) * (int)sizeof(void *) * 4;
+
+ ret += (int)BLI_ghash_len(entry->modified_verts) * (int)sizeof(void *) * 4;
+ ret += (int)BLI_ghash_len(entry->modified_edges) * (int)sizeof(void *) * 4;
+ ret += (int)BLI_ghash_len(entry->modified_faces) * (int)sizeof(void *) * 4;
+
+ ret += (int)BLI_ghash_len(entry->deleted_verts) * (int)sizeof(void *) * 4;
+ ret += (int)BLI_ghash_len(entry->deleted_edges) * (int)sizeof(void *) * 4;
+ ret += (int)BLI_ghash_len(entry->deleted_faces) * (int)sizeof(void *) * 4;
+ }
+ else if (entry->type == LOG_ENTRY_FULL_MESH) {
+ Mesh *me = entry->full_copy_mesh;
+
+ ret += me->totvert * me->vdata.totsize;
+ ret += me->totedge * me->edata.totsize;
+ ret += me->totloop * me->ldata.totsize;
+ ret += me->totpoly * me->pdata.totsize;
+ }
+
+ return ret;
+}
+
+int BM_log_entry_size(BMLogEntry *entry)
+{
+ while (entry->combined_prev) {
+ entry = entry->combined_prev;
+ }
+
+ int ret = 0;
+
+ while (entry) {
+ ret += bmlog_entry_memsize(entry);
+
+ entry = entry->combined_next;
+ }
+
+ return ret;
+}
diff --git a/source/blender/bmesh/intern/bmesh_log.h b/source/blender/bmesh/intern/bmesh_log.h
index 5c0ca78bddf..d81aa1dc5ae 100644
--- a/source/blender/bmesh/intern/bmesh_log.h
+++ b/source/blender/bmesh/intern/bmesh_log.h
@@ -28,14 +28,37 @@ struct RangeTreeUInt;
typedef struct BMLog BMLog;
typedef struct BMLogEntry BMLogEntry;
+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;
+
/* Allocate and initialize a new BMLog */
-BMLog *BM_log_create(BMesh *bm);
+BMLog *BM_log_create(BMesh *bm, int cd_dyn_vert);
+void BM_log_set_cd_offsets(BMLog *log, int cd_dyn_vert);
/* Allocate and initialize a new BMLog using existing BMLogEntries */
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);
+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 */
int BM_log_length(const BMLog *log);
@@ -44,7 +67,11 @@ int BM_log_length(const BMLog *log);
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(BMLog *log);
+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);
@@ -52,18 +79,28 @@ void BM_log_cleanup_entry(BMLogEntry *entry);
/* Remove an entry from the log */
void BM_log_entry_drop(BMLogEntry *entry);
-/* Undo one BMLogEntry */
-void BM_log_undo(BMesh *bm, BMLog *log);
+/* Undo one BMLogEntry. node_layer_id is necassary to preserve node idxs with customdata, whose
+ * layout might have changed */
+void BM_log_undo(BMesh *bm, BMLog *log, BMLogCallbacks *callbacks, const char *node_layer_id);
/* Redo one BMLogEntry */
-void BM_log_redo(BMesh *bm, BMLog *log);
+void BM_log_redo(BMesh *bm, BMLog *log, BMLogCallbacks *callbacks, const char *node_layer_id);
/* Log a vertex before it is modified */
-void BM_log_vert_before_modified(BMLog *log, struct BMVert *v, const int cd_vert_mask_offset);
+void BM_log_vert_before_modified(BMLog *log,
+ struct BMVert *v,
+ const int cd_vert_mask_offset,
+ bool log_customdata);
+
+/* Log an edge before it is modified */
+void BM_log_edge_before_modified(BMLog *log, BMEdge *v, bool log_customdata);
/* Log a new vertex as added to the BMesh */
void BM_log_vert_added(BMLog *log, struct BMVert *v, const int cd_vert_mask_offset);
+/* Log a new edge as added to the BMesh */
+void BM_log_edge_added(BMLog *log, BMEdge *e);
+
/* Log a face before it is modified */
void BM_log_face_modified(BMLog *log, struct BMFace *f);
@@ -73,12 +110,17 @@ void BM_log_face_added(BMLog *log, struct BMFace *f);
/* Log a vertex as removed from the BMesh */
void BM_log_vert_removed(BMLog *log, struct BMVert *v, const int cd_vert_mask_offset);
+/* Log an edge as removed from the BMesh */
+void BM_log_edge_removed(BMLog *log, BMEdge *e);
+
/* Log a face as removed from the BMesh */
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);
+void BM_log_full_mesh(BMesh *bm, BMLog *log);
+
/* Log all vertices/faces in the BMesh as removed */
void BM_log_before_all_removed(BMesh *bm, BMLog *log);
@@ -86,14 +128,28 @@ void BM_log_before_all_removed(BMesh *bm, BMLog *log);
const float *BM_log_original_vert_co(BMLog *log, BMVert *v);
/* Get the logged normal of a vertex */
-const short *BM_log_original_vert_no(BMLog *log, BMVert *v);
+const float *BM_log_original_vert_no(BMLog *log, BMVert *v);
/* Get the logged mask of a 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 short **r_no);
+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);
-struct RangeTreeUInt *BM_log_unused_ids(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(BMLog *log, BMVert *v);
+BMVert *BM_log_id_vert_get(BMLog *log, uint id);
+uint BM_log_face_id_get(BMLog *log, BMFace *f);
+BMFace *BM_log_id_face_get(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);
+
+int BM_log_entry_size(BMLogEntry *entry);
diff --git a/source/blender/bmesh/intern/bmesh_marking.c b/source/blender/bmesh/intern/bmesh_marking.c
index b70e26f51ea..344d85a9ee0 100644
--- a/source/blender/bmesh/intern/bmesh_marking.c
+++ b/source/blender/bmesh/intern/bmesh_marking.c
@@ -166,13 +166,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.c b/source/blender/bmesh/intern/bmesh_mesh.c
index b2958a9e744..de85efc3868 100644
--- a/source/blender/bmesh/intern/bmesh_mesh.c
+++ b/source/blender/bmesh/intern/bmesh_mesh.c
@@ -25,14 +25,19 @@
#include "DNA_listBase.h"
#include "DNA_scene_types.h"
+#include "BLI_alloca.h"
+#include "BLI_array.h"
#include "BLI_listbase.h"
#include "BLI_math.h"
+#include "BLI_rand.h"
#include "BLI_utildefines.h"
#include "BKE_customdata.h"
#include "BKE_mesh.h"
#include "bmesh.h"
+#include "bmesh_private.h"
+#include "range_tree.h"
/* used as an extern, defined in bmesh.h */
const BMAllocTemplate bm_mesh_allocsize_default = {512, 1024, 2048, 512};
@@ -70,7 +75,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(
@@ -137,6 +142,23 @@ 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;
+ }
+}
+
/**
* \brief BMesh Make Mesh
*
@@ -154,6 +176,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 = MEM_callocN(sizeof(void *) * bm->idmap.map_size, "bmesh idmap");
+ bm->idmap.ghash = NULL;
+ }
+ }
+ else {
+ bm->idmap.map = NULL;
+ bm->idmap.ghash = NULL;
+ }
+
/* allocate one flag pool that we don't get rid of. */
bm->use_toolflags = params->use_toolflags;
bm->toolflag_index = 0;
@@ -164,6 +225,23 @@ BMesh *BM_mesh_create(const BMAllocTemplate *allocsize, const struct BMeshCreate
CustomData_reset(&bm->ldata);
CustomData_reset(&bm->pdata);
+ if (params->create_unique_ids) {
+ bm_init_idmap_cdlayers(bm);
+
+ 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);
+ }
+ }
+
return bm;
}
@@ -184,6 +262,24 @@ 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
+ if (bm->idmap.free_ids) {
+ BLI_gset_free(bm->idmap.free_ids, NULL);
+ }
+
+ MEM_SAFE_FREE(bm->idmap.free_ids);
+#endif
+
+ MEM_SAFE_FREE(bm->idmap.map);
+
+ if (bm->idmap.ghash) {
+ BLI_ghash_free(bm->idmap.ghash, NULL, NULL);
+ }
+
const bool is_ldata_free = CustomData_bmesh_has_free(&bm->ldata);
const bool is_pdata_free = CustomData_bmesh_has_free(&bm->pdata);
@@ -273,6 +369,7 @@ 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);
@@ -289,6 +386,28 @@ 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 = NULL;
+ bm->idmap.ghash = NULL;
+ bm->idmap.map_size = 0;
+
+#ifndef WITH_BM_ID_FREELIST
+ bm->idmap.idtree = range_tree_uint_alloc(0, (uint)-1);
+#else
+ if (bm->idmap.free_ids) {
+ BLI_gset_free(bm->idmap.free_ids, NULL);
+ }
+ MEM_SAFE_FREE(bm->idmap.freelist);
+
+ bm->idmap.freelist_len = bm->idmap.freelist_size = NULL;
+ bm->idmap.free_ids = NULL;
+ bm->idmap.freelist = NULL;
+#endif
+ bm_init_idmap_cdlayers(bm);
+ }
}
/**
@@ -317,24 +436,16 @@ void BM_mesh_free(BMesh *bm)
* 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 *UNUSED(bm), BMOpTypeFlag UNUSED(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)) {
- bmesh_mdisps_space_set(bm, MULTIRES_SPACE_TANGENT, MULTIRES_SPACE_ABSOLUTE);
-
+ BM_enter_multires_space(NULL, 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
}
/**
@@ -344,18 +455,11 @@ 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);
- }
- else if (flag & BMO_OP_FLAG_RATIONALIZE_NORMALS) {
- bmesh_rationalize_normals(bm, 1);
+ if ((type_flag & BMO_OPTYPE_FLAG_UNTAN_MULTIRES) &&
+ CustomData_has_layer(&bm->ldata, CD_MDISPS)) {
+ BM_enter_multires_space(NULL, bm, MULTIRES_SPACE_TANGENT);
}
-#endif
/* compute normals, clear temp flags and flush selections */
if (type_flag & BMO_OPTYPE_FLAG_NORMALS_CALC) {
@@ -818,7 +922,11 @@ int BM_mesh_elem_count(BMesh *bm, const 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)
{
/* Mapping old to new pointers. */
GHash *vptr_map = NULL, *eptr_map = NULL, *fptr_map = NULL;
@@ -835,6 +943,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;
@@ -853,7 +976,9 @@ void BM_mesh_remap(BMesh *bm, const uint *vert_idx, const uint *edge_idx, const
void **pyptrs = (cd_vert_pyptr != -1) ? MEM_mallocN(sizeof(void *) * totvert, __func__) : NULL;
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 = BM_ELEM_CD_GET_VOID_P(((BMElem *)ve), cd_vert_pyptr);
@@ -867,12 +992,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 = BM_ELEM_CD_GET_VOID_P(((BMElem *)new_vep), cd_vert_pyptr);
*pyptr = pyptrs[*new_idx];
@@ -887,6 +1015,75 @@ void BM_mesh_remap(BMesh *bm, const uint *vert_idx, const uint *edge_idx, const
}
}
+ GHash *lptr_map = NULL;
+
+ /* Remap Loops */
+ if (loop_idx) {
+ BMLoop **ltable = 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 = MEM_mallocN(sizeof(BMLoop) * totloop, "BM_mesh_remap loops copy");
+
+ void **pyptrs = (cd_loop_pyptr != -1) ? MEM_mallocN(sizeof(void *) * totloop, __func__) : NULL;
+ 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 = 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 = 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;
@@ -918,7 +1115,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 = BLI_ghash_lookup(lptr_map, (BMLoop *)new_edp->l);
+ }
+
BLI_ghash_insert(eptr_map, *edp, new_edp);
#if 0
printf(
@@ -971,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 = BLI_ghash_lookup(lptr_map, (void *)new_fap->l_first);
+
+ BMLoop *l = new_fap->l_first;
+
+ do {
+ l->next = BLI_ghash_lookup(lptr_map, (void *)l->next);
+ l->prev = BLI_ghash_lookup(lptr_map, (void *)l->prev);
+ l->radial_next = BLI_ghash_lookup(lptr_map, (void *)l->radial_next);
+ l->radial_prev = 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 = BM_ELEM_CD_GET_VOID_P(((BMElem *)new_fap), cd_poly_pyptr);
*pyptr = pyptrs[*new_idx];
@@ -1104,6 +1325,69 @@ void BM_mesh_remap(BMesh *bm, const uint *vert_idx, const uint *edge_idx, const
if (fptr_map) {
BLI_ghash_free(fptr_map, NULL, NULL);
}
+
+ // 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;
+ }
+ }
+ }
+ }
}
/**
@@ -1446,4 +1730,401 @@ 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;
+ }
+
+ BMLoop **ls1 = NULL;
+ BLI_array_staticdeclare(ls1, 64);
+ BMLoop **ls2 = NULL;
+ BLI_array_staticdeclare(ls2, 64);
+
+ BMEdge **es1 = NULL;
+ int *sides1 = NULL;
+
+ BLI_array_staticdeclare(es1, 32);
+ BLI_array_staticdeclare(sides1, 32);
+
+ BMEdge **es2 = NULL;
+ int *sides2 = NULL;
+
+ BLI_array_staticdeclare(es2, 32);
+ BLI_array_staticdeclare(sides2, 32);
+
+ 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) {
+ BLI_array_append(es2, e);
+ BLI_array_append(sides2, side);
+ }
+ else {
+ BLI_array_append(es1, e);
+ BLI_array_append(sides1, 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;
+
+ BMEdge **es = i ? es2 : es1;
+ int elen = i ? BLI_array_len(es2) : BLI_array_len(es1);
+ int *sides = i ? sides2 : sides1;
+
+ 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) {
+ BLI_array_append(ls2, l2);
+ }
+ else {
+ BLI_array_append(ls1, 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;
+
+ BMLoop **ls = i ? ls2 : ls1;
+
+ int llen = i ? BLI_array_len(ls2) : BLI_array_len(ls1);
+
+ for (int j = 0; j < llen; j++) {
+ ls[j]->v = v_2;
+ }
+ }
+
+ BLI_array_free(ls1);
+ BLI_array_free(ls2);
+ // BMVert tmp = *v1;
+ //*v1 = *v2;
+ //*v2 = tmp;
+
+ 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;
+
+ BLI_array_free(es1);
+ BLI_array_free(sides1);
+ BLI_array_free(es2);
+ BLI_array_free(sides2);
+}
+
+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 = 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 = NULL;
+ 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 = 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;
+}
/** \} */
diff --git a/source/blender/bmesh/intern/bmesh_mesh.h b/source/blender/bmesh/intern/bmesh_mesh.h
index bd0504b038a..e65ad38eada 100644
--- a/source/blender/bmesh/intern/bmesh_mesh.h
+++ b/source/blender/bmesh/intern/bmesh_mesh.h
@@ -22,6 +22,11 @@
#include "bmesh_class.h"
+typedef enum {
+ MULTIRES_SPACE_TANGENT, // convert absolute to tangent
+ MULTIRES_SPACE_ABSOLUTE // convert tangent to absolute
+} MultiResSpace;
+
struct BMAllocTemplate;
struct BMLoopNorEditDataArray;
struct BMPartialUpdate;
@@ -31,9 +36,20 @@ void BM_mesh_elem_toolflags_ensure(BMesh *bm);
void BM_mesh_elem_toolflags_clear(BMesh *bm);
struct BMeshCreateParams {
+ uint create_unique_ids : 1;
+ uint id_elem_mask : 4; // which element types to make unique ids for
+ uint id_map : 1; // maintain an id to element lookup table
uint use_toolflags : 1;
+ uint no_reuse_ids : 1; // do not reuse IDs; a GHash will be used internally instead of a lookup
+ // array
+ uint temporary_ids : 1;
};
+// 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);
+
BMesh *BM_mesh_create(const struct BMAllocTemplate *allocsize,
const struct BMeshCreateParams *params);
@@ -91,7 +107,11 @@ BMFace *BM_face_at_index_find_or_table(BMesh *bm, const int index);
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);
+void BM_mesh_remap(BMesh *bm,
+ const uint *vert_idx,
+ const uint *edge_idx,
+ const uint *face_idx,
+ const uint *loop_idx);
void BM_mesh_rebuild(BMesh *bm,
const struct BMeshCreateParams *params,
@@ -134,3 +154,8 @@ void BM_mesh_vert_coords_apply(BMesh *bm, const float (*vert_coords)[3]);
void BM_mesh_vert_coords_apply_with_mat4(BMesh *bm,
const float (*vert_coords)[3],
const float mat[4][4]);
+
+#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])
diff --git a/source/blender/bmesh/intern/bmesh_mesh_convert.c b/source/blender/bmesh/intern/bmesh_mesh_convert.c
index 9d29a90a7a4..23c8f642692 100644
--- a/source/blender/bmesh/intern/bmesh_mesh_convert.c
+++ b/source/blender/bmesh/intern/bmesh_mesh_convert.c
@@ -79,6 +79,7 @@
#include "MEM_guardedalloc.h"
#include "BLI_alloca.h"
+#include "BLI_array.h"
#include "BLI_listbase.h"
#include "BLI_math_vector.h"
@@ -94,6 +95,23 @@
#include "bmesh.h"
#include "intern/bmesh_private.h" /* For element checking. */
+#include "range_tree.h"
+
+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);
+}
void BM_mesh_cd_flag_ensure(BMesh *bm, Mesh *mesh, const char cd_flag)
{
@@ -157,6 +175,7 @@ char BM_mesh_cd_flag_from_bmesh(BMesh *bm)
if (CustomData_has_layer(&bm->edata, CD_CREASE)) {
cd_flag |= ME_CDFLAG_EDGE_CREASE;
}
+
return cd_flag;
}
@@ -173,11 +192,35 @@ static BMFace *bm_face_create_from_mpoly(
edges[j] = etable[ml->e];
}
- return BM_face_create(bm, verts, edges, mp->totloop, NULL, BM_CREATE_SKIP_CD);
+ return BM_face_create(
+ bm, verts, edges, mp->totloop, NULL, BM_CREATE_SKIP_CD | BM_CREATE_SKIP_ID);
+}
+
+void BM_enter_multires_space(Object *ob, BMesh *bm, int space)
+{
+ if (!bm->haveMultiResSettings && ob) {
+ MultiresModifierData *mmd = get_multires_modifier(NULL, ob, true);
+ if (mmd) {
+ bm->multires = *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 NULL (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.
@@ -185,8 +228,16 @@ static BMFace *bm_face_create_from_mpoly(
* 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(BMesh *bm, const Mesh *me, const struct BMeshFromMeshParams *params)
+
+void BM_mesh_bm_from_me(Object *ob,
+ BMesh *bm,
+ const Mesh *me,
+ const struct BMeshFromMeshParams *params)
{
const bool is_new = !(bm->totvert || (bm->vdata.totlayer || bm->edata.totlayer ||
bm->pdata.totlayer || bm->ldata.totlayer));
@@ -203,6 +254,57 @@ void BM_mesh_bm_from_me(BMesh *bm, const Mesh *me, const struct BMeshFromMeshPar
CustomData_MeshMasks mask = CD_MASK_BMESH;
CustomData_MeshMasks_update(&mask, &params->cd_mask_extra);
+ MultiresModifierData *mmd = ob ? get_multires_modifier(NULL, ob, true) : NULL;
+ 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) {
+ bm->multires = *mmd;
+ bm->haveMultiResSettings = true;
+ bm->multiresSpace = MULTIRES_SPACE_TANGENT;
+ }
+ else {
+ bm->haveMultiResSettings = false;
+ }
+
if (!me || !me->totvert) {
if (me && is_new) { /* No verts? still copy custom-data layout. */
CustomData_copy(&me->vdata, &bm->vdata, mask.vmask, CD_ASSIGN, 0);
@@ -215,6 +317,15 @@ void BM_mesh_bm_from_me(BMesh *bm, const Mesh *me, const struct BMeshFromMeshPar
CustomData_bmesh_init_pool(&bm->ldata, me->totloop, BM_LOOP);
CustomData_bmesh_init_pool(&bm->pdata, me->totpoly, BM_FACE);
}
+
+ if (params->copy_temp_cdlayers) {
+ bm_mark_temp_cdlayers(bm);
+ }
+
+ if (bm->idmap.flag & BM_HAS_IDS) {
+ bm_init_idmap_cdlayers(bm);
+ }
+
return; /* Sanity check. */
}
@@ -321,6 +432,28 @@ void BM_mesh_bm_from_me(BMesh *bm, const Mesh *me, const struct BMeshFromMeshPar
BM_mesh_cd_flag_apply(bm, me->cd_flag);
}
+ int *existing_id_layers[4] = {NULL, NULL, NULL, NULL};
+ 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) {
+ 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]) {
+ use_exist_ids |= 1 << i;
+ }
+ }
+ }
+
+ use_exist_ids &= bm->idmap.flag;
+
+ bm_init_idmap_cdlayers(bm);
+ }
+
const int cd_vert_bweight_offset = CustomData_get_offset(&bm->vdata, CD_BWEIGHT);
const int cd_edge_bweight_offset = CustomData_get_offset(&bm->edata, CD_BWEIGHT);
const int cd_edge_crease_offset = CustomData_get_offset(&bm->edata, CD_CREASE);
@@ -333,7 +466,8 @@ void BM_mesh_bm_from_me(BMesh *bm, const Mesh *me, const struct BMeshFromMeshPar
vtable = MEM_mallocN(sizeof(BMVert **) * me->totvert, __func__);
for (i = 0, mvert = me->mvert; i < me->totvert; i++, mvert++) {
- v = vtable[i] = BM_vert_create(bm, keyco ? keyco[i] : mvert->co, NULL, BM_CREATE_SKIP_CD);
+ v = vtable[i] = BM_vert_create(
+ bm, keyco ? keyco[i] : mvert->co, NULL, BM_CREATE_SKIP_CD | BM_CREATE_SKIP_ID);
BM_elem_index_set(v, i); /* set_ok */
/* Transfer flag. */
@@ -349,6 +483,15 @@ void BM_mesh_bm_from_me(BMesh *bm, const Mesh *me, const struct BMeshFromMeshPar
/* Copy Custom Data */
CustomData_to_bmesh_block(&me->vdata, &bm->vdata, i, &v->head.data, true);
+ if (has_ids & BM_VERT) {
+ if (use_exist_ids & BM_VERT) {
+ bm_assign_id(bm, (BMElem *)v, existing_id_layers[0][i], false);
+ }
+ else {
+ bm_alloc_id(bm, (BMElem *)v);
+ }
+ }
+
if (cd_vert_bweight_offset != -1) {
BM_ELEM_CD_SET_FLOAT(v, cd_vert_bweight_offset, (float)mvert->bweight / 255.0f);
}
@@ -389,6 +532,15 @@ void BM_mesh_bm_from_me(BMesh *bm, const Mesh *me, const struct BMeshFromMeshPar
/* Copy Custom Data */
CustomData_to_bmesh_block(&me->edata, &bm->edata, i, &e->head.data, true);
+ if (has_ids & BM_EDGE) {
+ if (use_exist_ids & BM_EDGE) {
+ bm_assign_id(bm, (BMElem *)e, existing_id_layers[1][i], false);
+ }
+ else {
+ bm_alloc_id(bm, (BMElem *)e);
+ }
+ }
+
if (cd_edge_bweight_offset != -1) {
BM_ELEM_CD_SET_FLOAT(e, cd_edge_bweight_offset, (float)medge->bweight / 255.0f);
}
@@ -450,11 +602,29 @@ void BM_mesh_bm_from_me(BMesh *bm, const Mesh *me, const struct BMeshFromMeshPar
/* Save index of corresponding #MLoop. */
CustomData_to_bmesh_block(&me->ldata, &bm->ldata, j++, &l_iter->head.data, true);
+
+ if (has_ids & BM_LOOP) {
+ if (use_exist_ids & BM_LOOP) {
+ 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(&me->pdata, &bm->pdata, i, &f->head.data, true);
+ if (has_ids & BM_FACE) {
+ if (use_exist_ids & BM_FACE) {
+ 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);
}
@@ -463,6 +633,99 @@ 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) {
+ // 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, NULL, NULL);
+ 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};
+
+ // 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);
+
+ 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);
+
+ 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);
+ }
+ }
+ }
+ }
+ }
/* -------------------------------------------------------------------- */
/* MSelect clears the array elements (avoid adding multiple times).
*
@@ -502,12 +765,29 @@ void BM_mesh_bm_from_me(BMesh *bm, const Mesh *me, const struct BMeshFromMeshPar
if (ftable) {
MEM_freeN(ftable);
}
+
+ 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;
+ }
+ }
+ }
}
/**
* \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 = NULL;
@@ -549,7 +829,7 @@ static BMVert **bm_to_mesh_vertex_map(BMesh *bm, int ototvert)
* Returns custom-data shapekey index from a keyblock 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;
@@ -586,7 +866,9 @@ BLI_INLINE void bmesh_quick_edgedraw_flag(MEdge *med, BMEdge *e)
*
* \param bmain: May be NULL in case \a calc_object_remap parameter option is not set.
*/
-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)
{
MEdge *med;
BMVert *v, *eve;
@@ -595,6 +877,15 @@ void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *me, const struct BMeshToMesh
BMIter iter;
int i, j;
+ 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_vert_bweight_offset = CustomData_get_offset(&bm->vdata, CD_BWEIGHT);
const int cd_edge_bweight_offset = CustomData_get_offset(&bm->edata, CD_BWEIGHT);
const int cd_edge_crease_offset = CustomData_get_offset(&bm->edata, CD_CREASE);
@@ -634,13 +925,30 @@ void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *me, const struct BMeshToMesh
me->totface = 0;
me->act_face = -1;
+ CustomData *srcdatas[] = {&bm->vdata, &bm->edata, &bm->ldata, &bm->pdata};
+ int id_flags[4] = {-1, -1, -1, -1};
+
{
CustomData_MeshMasks mask = CD_MASK_MESH;
CustomData_MeshMasks_update(&mask, &params->cd_mask_extra);
- CustomData_copy(&bm->vdata, &me->vdata, mask.vmask, CD_CALLOC, me->totvert);
- CustomData_copy(&bm->edata, &me->edata, mask.emask, CD_CALLOC, me->totedge);
- CustomData_copy(&bm->ldata, &me->ldata, mask.lmask, CD_CALLOC, me->totloop);
- CustomData_copy(&bm->pdata, &me->pdata, mask.pmask, CD_CALLOC, me->totpoly);
+ CustomDataMask extra2 = !params->ignore_mesh_id_layers ? CD_MASK_MESH_ID : 0;
+
+ // 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 | extra2, CD_CALLOC, me->totvert);
+ CustomData_copy(&bm->edata, &me->edata, mask.emask | extra2, CD_CALLOC, me->totedge);
+ CustomData_copy(&bm->ldata, &me->ldata, mask.lmask | extra2, CD_CALLOC, me->totloop);
+ CustomData_copy(&bm->pdata, &me->pdata, mask.pmask | extra2, CD_CALLOC, me->totpoly);
}
MVert *mvert = bm->totvert ? MEM_callocN(sizeof(MVert) * bm->totvert, "bm_to_me.vert") : NULL;
@@ -1003,6 +1311,20 @@ void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *me, const struct BMeshToMesh
/* To be removed as soon as COW is enabled by default. */
BKE_mesh_runtime_clear_geometry(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);
+ }
}
/**
diff --git a/source/blender/bmesh/intern/bmesh_mesh_convert.h b/source/blender/bmesh/intern/bmesh_mesh_convert.h
index 1b5d001d35d..4c46fda236a 100644
--- a/source/blender/bmesh/intern/bmesh_mesh_convert.h
+++ b/source/blender/bmesh/intern/bmesh_mesh_convert.h
@@ -42,9 +42,15 @@ 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;
};
-void BM_mesh_bm_from_me(BMesh *bm, const struct Mesh *me, const struct BMeshFromMeshParams *params)
- ATTR_NONNULL(1, 3);
+
+struct Object;
+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. */
@@ -60,11 +66,17 @@ struct BMeshToMeshParams {
*/
uint update_shapekey_indices : 1;
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);
+
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);
void BM_mesh_bm_to_me_for_eval(BMesh *bm,
struct Mesh *me,
diff --git a/source/blender/bmesh/intern/bmesh_mesh_convert_threaded.c b/source/blender/bmesh/intern/bmesh_mesh_convert_threaded.c
new file mode 100644
index 00000000000..1b5dc9643eb
--- /dev/null
+++ b/source/blender/bmesh/intern/bmesh_mesh_convert_threaded.c
@@ -0,0 +1,1194 @@
+#if 1
+# 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);
+ 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, &params->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_CALLOC, 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);
+ 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_CALLOC, 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_CALLOC, 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_CALLOC, 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, &params->cd_mask_extra);
+ CustomDataMask 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->totindex; i++) {
+ if (hmd->indexar[i] < ototvert) {
+ eve = vertMap[hmd->indexar[i]];
+
+ if (eve) {
+ hmd->indexar[j++] = BM_elem_index_get(eve);
+ }
+ }
+ else {
+ j++;
+ }
+ }
+
+ hmd->totindex = 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 1d393abcd56..6c67db7c7ce 100644
--- a/source/blender/bmesh/intern/bmesh_mesh_duplicate.c
+++ b/source/blender/bmesh/intern/bmesh_mesh_duplicate.c
@@ -34,6 +34,8 @@ 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);
+
return v_dst;
}
@@ -46,6 +48,8 @@ static BMEdge *bm_edge_copy_with_arrays(BMesh *bm_src,
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);
+
return e_dst;
}
@@ -74,12 +78,14 @@ 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);
/* 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 5fa12397a07..66dae665dff 100644
--- a/source/blender/bmesh/intern/bmesh_mods.c
+++ b/source/blender/bmesh/intern/bmesh_mods.c
@@ -563,10 +563,15 @@ 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)
+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)
{
- return bmesh_kernel_join_vert_kill_edge(bm, e_kill, v_kill, do_del, true, kill_degenerate_faces);
+ return bmesh_kernel_join_vert_kill_edge(
+ bm, e_kill, v_kill, do_del, true, kill_degenerate_faces, combine_flags);
}
/**
diff --git a/source/blender/bmesh/intern/bmesh_mods.h b/source/blender/bmesh/intern/bmesh_mods.h
index 4328187b95e..48260b503c5 100644
--- a/source/blender/bmesh/intern/bmesh_mods.h
+++ b/source/blender/bmesh/intern/bmesh_mods.h
@@ -64,7 +64,8 @@ BMVert *BM_edge_collapse(BMesh *bm,
BMEdge *e_kill,
BMVert *v_kill,
const bool do_del,
- const bool kill_degenerate_faces);
+ const bool kill_degenerate_faces,
+ const bool 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_opdefines.c b/source/blender/bmesh/intern/bmesh_opdefines.c
index 7865c79323d..bf53e84efa7 100644
--- a/source/blender/bmesh/intern/bmesh_opdefines.c
+++ b/source/blender/bmesh/intern/bmesh_opdefines.c
@@ -1064,6 +1064,21 @@ 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.
*/
@@ -1909,7 +1924,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),
};
/*
@@ -1938,7 +1953,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),
};
/*
@@ -1959,7 +1974,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),
};
/*
@@ -2178,6 +2193,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_polygon.c b/source/blender/bmesh/intern/bmesh_polygon.c
index 51ae47adacc..ecbdf316d90 100644
--- a/source/blender/bmesh/intern/bmesh_polygon.c
+++ b/source/blender/bmesh/intern/bmesh_polygon.c
@@ -28,6 +28,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"
@@ -1448,25 +1449,6 @@ void BM_face_splits_check_optimal(BMFace *f, BMLoop *(*loops)[2], int len)
}
/**
- * Small utility functions for fast access
- *
- * 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])
-{
- 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);
*/
@@ -1486,25 +1468,6 @@ void BM_face_as_array_vert_quad(BMFace *f, BMVert *r_verts[4])
}
/**
- * Small utility functions for fast access
- *
- * 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])
-{
- 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_LOOPS_OF_FACE, f, (void **)l, 4);
*/
diff --git a/source/blender/bmesh/intern/bmesh_polygon.h b/source/blender/bmesh/intern/bmesh_polygon.h
index 5be7f4a5f3b..543b2eb9c8c 100644
--- a/source/blender/bmesh/intern/bmesh_polygon.h
+++ b/source/blender/bmesh/intern/bmesh_polygon.h
@@ -24,6 +24,7 @@ struct BMPartialUpdate;
struct Heap;
#include "BLI_compiler_attrs.h"
+#include "BLI_compiler_compat.h"
void BM_face_calc_tessellation(const BMFace *f,
const bool use_fixed_quad,
@@ -103,11 +104,47 @@ void BM_face_triangulate(BMesh *bm,
void BM_face_splits_check_legal(BMesh *bm, BMFace *f, BMLoop *(*loops)[2], int len) ATTR_NONNULL();
void BM_face_splits_check_optimal(BMFace *f, BMLoop *(*loops)[2], int len) ATTR_NONNULL();
-void BM_face_as_array_vert_tri(BMFace *f, BMVert *r_verts[3]) ATTR_NONNULL();
-void BM_face_as_array_vert_quad(BMFace *f, BMVert *r_verts[4]) ATTR_NONNULL();
+/**
+ * Small utility functions for fast access
+ *
+ * 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;
+}
+
+/**
+ * Small utility functions for fast access
+ *
+ * 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;
+}
-void BM_face_as_array_loop_tri(BMFace *f, BMLoop *r_loops[3]) ATTR_NONNULL();
void BM_face_as_array_loop_quad(BMFace *f, BMLoop *r_loops[4]) ATTR_NONNULL();
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]);
diff --git a/source/blender/bmesh/intern/bmesh_structure.h b/source/blender/bmesh/intern/bmesh_structure.h
index ca51a9c39de..20016787965 100644
--- a/source/blender/bmesh/intern/bmesh_structure.h
+++ b/source/blender/bmesh/intern/bmesh_structure.h
@@ -91,4 +91,13 @@ 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);
diff --git a/source/blender/bmesh/operators/bmo_dupe.c b/source/blender/bmesh/operators/bmo_dupe.c
index d8b30fc1a45..cd70bf76ea9 100644
--- a/source/blender/bmesh/operators/bmo_dupe.c
+++ b/source/blender/bmesh/operators/bmo_dupe.c
@@ -28,6 +28,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 */
@@ -60,6 +61,9 @@ 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);
+
/* Mark the vert for output */
BMO_vert_flag_enable(bm_dst, v_dst, DUPE_NEW);
@@ -122,6 +126,9 @@ 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);
+
/* Mark the edge for output */
BMO_edge_flag_enable(bm_dst, e_dst, DUPE_NEW);
@@ -174,11 +181,15 @@ static BMFace *bmo_face_copy(BMOperator *op,
/* Copy attributes */
BM_elem_attrs_copy(bm_src, bm_dst, f_src, f_dst);
+ /* Handle Ids */
+ bm_alloc_id(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 */
@@ -590,7 +601,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 0cedc2324f2..d7e0a8d811f 100644
--- a/source/blender/bmesh/operators/bmo_extrude.c
+++ b/source/blender/bmesh/operators/bmo_extrude.c
@@ -616,7 +616,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);
+ BM_edge_collapse(
+ bm, BMO_elem_flag_test(bm, e, EXT_TAG) ? e : e_other, v, true, true, 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 6734cc60cad..36471072354 100644
--- a/source/blender/bmesh/operators/bmo_fill_grid.c
+++ b/source/blender/bmesh/operators/bmo_fill_grid.c
@@ -720,7 +720,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);
}
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 4833290aa0b..7abe0f4477a 100644
--- a/source/blender/bmesh/operators/bmo_inset.c
+++ b/source/blender/bmesh/operators/bmo_inset.c
@@ -1255,11 +1255,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 e480db64f9d..d4d8c4d0806 100644
--- a/source/blender/bmesh/operators/bmo_mesh_convert.c
+++ b/source/blender/bmesh/operators/bmo_mesh_convert.c
@@ -39,7 +39,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,
@@ -66,6 +67,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 d8047499780..7ff24c9f825 100644
--- a/source/blender/bmesh/operators/bmo_primitive.c
+++ b/source/blender/bmesh/operators/bmo_primitive.c
@@ -1507,7 +1507,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);
}
}
@@ -1519,7 +1519,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);
}
}
diff --git a/source/blender/bmesh/operators/bmo_symmetrize.c b/source/blender/bmesh/operators/bmo_symmetrize.c
index f5bfd22bd51..6565df13ea8 100644
--- a/source/blender/bmesh/operators/bmo_symmetrize.c
+++ b/source/blender/bmesh/operators/bmo_symmetrize.c
@@ -28,6 +28,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");
@@ -96,6 +98,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 202d16b09e3..8770e16ac2b 100644
--- a/source/blender/bmesh/tests/bmesh_core_test.cc
+++ b/source/blender/bmesh/tests/bmesh_core_test.cc
@@ -10,8 +10,10 @@ TEST(bmesh_core, BMVertCreate)
BMVert *bv1, *bv2, *bv3;
const float co1[3] = {1.0f, 2.0f, 0.0f};
- BMeshCreateParams bm_params;
+ BMeshCreateParams bm_params = {0};
+
bm_params.use_toolflags = true;
+
bm = BM_mesh_create(&bm_mesh_allocsize_default, &bm_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 97fccbe01fd..bbb60886895 100644
--- a/source/blender/bmesh/tools/bmesh_decimate_collapse.c
+++ b/source/blender/bmesh/tools/bmesh_decimate_collapse.c
@@ -1038,8 +1038,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 */
@@ -1098,7 +1098,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/depsgraph/intern/builder/deg_builder_cycle.cc b/source/blender/depsgraph/intern/builder/deg_builder_cycle.cc
index dbc8bbd31fa..68f571a50c0 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_cycle.cc
+++ b/source/blender/depsgraph/intern/builder/deg_builder_cycle.cc
@@ -140,7 +140,7 @@ bool schedule_non_checked_node(CyclesSolverState *state)
return false;
}
-bool check_relation_can_murder(Relation *relation)
+bool check_relation_can_murder(Relation *relation)
{
if (relation->flag & RELATION_FLAG_GODMODE) {
return false;
diff --git a/source/blender/draw/DRW_engine.h b/source/blender/draw/DRW_engine.h
index a125a13eaf9..d3638f6be2b 100644
--- a/source/blender/draw/DRW_engine.h
+++ b/source/blender/draw/DRW_engine.h
@@ -40,6 +40,9 @@ struct GHash;
struct GPUMaterial;
struct GPUOffScreen;
struct GPUViewport;
+struct GPUVertFormat;
+struct CustomData;
+struct CustomDataLayer;
struct ID;
struct Main;
struct Object;
@@ -80,6 +83,10 @@ typedef bool (*DRW_ObjectFilterFn)(struct Object *ob, void *user_data);
void DRW_draw_view(const struct bContext *C);
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,
+ struct CustomData *data,
+ struct CustomDataLayer *cl);
void DRW_draw_render_loop_ex(struct Depsgraph *depsgraph,
struct RenderEngineType *engine_type,
diff --git a/source/blender/draw/engines/overlay/overlay_lattice.c b/source/blender/draw/engines/overlay/overlay_lattice.c
index 84d9dcc3e94..c9fd45baaf0 100644
--- a/source/blender/draw/engines/overlay/overlay_lattice.c
+++ b/source/blender/draw/engines/overlay/overlay_lattice.c
@@ -68,6 +68,7 @@ void OVERLAY_lattice_cache_populate(OVERLAY_Data *vedata, Object *ob)
struct GPUBatch *geom = DRW_cache_lattice_wire_get(ob, false);
OVERLAY_extra_wire(cb, geom, ob->obmat, color);
+
}
void OVERLAY_edit_lattice_draw(OVERLAY_Data *vedata)
diff --git a/source/blender/draw/engines/overlay/overlay_sculpt.c b/source/blender/draw/engines/overlay/overlay_sculpt.c
index 24c52ec427d..24ff807bba8 100644
--- a/source/blender/draw/engines/overlay/overlay_sculpt.c
+++ b/source/blender/draw/engines/overlay/overlay_sculpt.c
@@ -60,7 +60,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/intern/draw_cache.c b/source/blender/draw/intern/draw_cache.c
index a3a5d6b065a..0009b24c0cb 100644
--- a/source/blender/draw/intern/draw_cache.c
+++ b/source/blender/draw/intern/draw_cache.c
@@ -35,6 +35,7 @@
#include "BLI_math.h"
#include "BLI_utildefines.h"
+#include "BKE_customdata.h"
#include "BKE_object.h"
#include "BKE_paint.h"
@@ -242,7 +243,7 @@ static void UNUSED_FUNCTION(add_fancy_edge)(GPUVertBuf *vbo,
GPU_vertbuf_attr_set(vbo, pos_id, (*v_idx)++, co2);
}
-#if 0 /* UNUSED */
+#if 0 /* UNUSED */
static void add_lat_lon_vert(GPUVertBuf *vbo,
uint pos_id,
uint nor_id,
@@ -484,6 +485,43 @@ 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,
+ CustomData *data,
+ CustomDataLayer *cl)
+{
+ 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 (i == CustomData_get_render_layer(data, cl->type)) {
+ GPU_vertformat_alias_add(format, base_name);
+ }
+ /* Active display layer name. */
+ if (i == CustomData_get_active_layer(data, cl->type)) {
+ 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_manager.c b/source/blender/draw/intern/draw_manager.c
index 47adc0acc60..e49f5235c15 100644
--- a/source/blender/draw/intern/draw_manager.c
+++ b/source/blender/draw/intern/draw_manager.c
@@ -29,6 +29,7 @@
#include "BLI_string.h"
#include "BLI_task.h"
#include "BLI_threads.h"
+#include "BLI_utildefines.h"
#include "BLF_api.h"
@@ -64,6 +65,7 @@
#include "ED_space_api.h"
#include "ED_view3d.h"
+#include "GPU_buffers.h"
#include "GPU_capabilities.h"
#include "GPU_framebuffer.h"
#include "GPU_immediate.h"
diff --git a/source/blender/draw/intern/draw_manager_data.c b/source/blender/draw/intern/draw_manager_data.c
index af331c86a8b..1baee2088e0 100644
--- a/source/blender/draw/intern/draw_manager_data.c
+++ b/source/blender/draw/intern/draw_manager_data.c
@@ -968,9 +968,23 @@ static void sculpt_draw_cb(DRWSculptCallbackData *scd, GPU_PBVH_Buffers *buffers
DRW_shgroup_uniform_vec3(
shgrp, "materialDiffuseColor", SCULPT_DEBUG_COLOR(scd->debug_node_nr++), 1);
}
+#if 0
+ float extramat[4][4], mat[4][4];
+ float *extra = GPU_pbvh_get_extra_matrix(buffers);
+
+ if (extra) {
+ memcpy(extramat, GPU_pbvh_get_extra_matrix(buffers), sizeof(float) * 16);
+ mul_m4_m4m4(mat, scd->ob->obmat, extramat);
+ }
+ else {
+ copy_m4_m4(mat, scd->ob->obmat);
+ }
+ DRW_shgroup_call_obmat(shgrp, geom, mat);
+#else
/* DRW_shgroup_call_no_cull reuses matrices calculations for all the drawcalls of this
* object. */
DRW_shgroup_call_no_cull(shgrp, geom, scd->ob);
+#endif
}
}
@@ -1092,14 +1106,12 @@ static void drw_sculpt_generate_calls(DRWSculptCallbackData *scd)
void DRW_shgroup_call_sculpt(DRWShadingGroup *shgroup, Object *ob, bool use_wire, bool use_mask)
{
- DRWSculptCallbackData scd = {
- .ob = ob,
- .shading_groups = &shgroup,
- .num_shading_groups = 1,
- .use_wire = use_wire,
- .use_mats = false,
- .use_mask = use_mask,
- };
+ DRWSculptCallbackData scd = {.ob = ob,
+ .shading_groups = &shgroup,
+ .num_shading_groups = 1,
+ .use_wire = use_wire,
+ .use_mats = false,
+ .use_mask = use_mask};
drw_sculpt_generate_calls(&scd);
}
@@ -1107,14 +1119,13 @@ void DRW_shgroup_call_sculpt_with_materials(DRWShadingGroup **shgroups,
int num_shgroups,
Object *ob)
{
- DRWSculptCallbackData scd = {
- .ob = ob,
- .shading_groups = shgroups,
- .num_shading_groups = num_shgroups,
- .use_wire = false,
- .use_mats = true,
- .use_mask = false,
- };
+ DRWSculptCallbackData scd = {.ob = ob,
+ .shading_groups = shgroups,
+ .num_shading_groups = num_shgroups,
+ .use_wire = false,
+ .use_mats = true,
+ .use_mask = false};
+
drw_sculpt_generate_calls(&scd);
}
diff --git a/source/blender/editors/datafiles/CMakeLists.txt b/source/blender/editors/datafiles/CMakeLists.txt
index 702fd2e375a..5f25114eaf3 100644
--- a/source/blender/editors/datafiles/CMakeLists.txt
+++ b/source/blender/editors/datafiles/CMakeLists.txt
@@ -750,7 +750,6 @@ set_property(GLOBAL PROPERTY ICON_GEOM_NAMES
brush.sculpt.cloth
brush.sculpt.crease
brush.sculpt.displacement_eraser
- brush.sculpt.displacement_smear
brush.sculpt.draw
brush.sculpt.draw_face_sets
brush.sculpt.draw_sharp
@@ -763,15 +762,18 @@ set_property(GLOBAL PROPERTY ICON_GEOM_NAMES
brush.sculpt.mask
brush.sculpt.multiplane_scrape
brush.sculpt.nudge
+ brush.sculpt.paint
brush.sculpt.pinch
brush.sculpt.pose
brush.sculpt.rotate
brush.sculpt.scrape
brush.sculpt.simplify
+ brush.sculpt.smear
brush.sculpt.smooth
brush.sculpt.snake_hook
brush.sculpt.thumb
brush.sculpt.topology
+ brush.sculpt.vcol_boundary
brush.uv_sculpt.grab
brush.uv_sculpt.pinch
brush.uv_sculpt.relax
diff --git a/source/blender/editors/include/ED_object.h b/source/blender/editors/include/ED_object.h
index 5397cd95ace..65e13b29015 100644
--- a/source/blender/editors/include/ED_object.h
+++ b/source/blender/editors/include/ED_object.h
@@ -266,7 +266,8 @@ void ED_object_sculptmode_enter_ex(struct Main *bmain,
struct Scene *scene,
struct Object *ob,
const bool force_dyntopo,
- struct ReportList *reports);
+ 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/include/UI_icons.h b/source/blender/editors/include/UI_icons.h
index ddd9ca4a98c..0ae3e61293c 100644
--- a/source/blender/editors/include/UI_icons.h
+++ b/source/blender/editors/include/UI_icons.h
@@ -919,6 +919,9 @@ DEF_ICON_COLOR(BRUSH_TEXFILL)
DEF_ICON_COLOR(BRUSH_TEXMASK)
DEF_ICON_COLOR(BRUSH_THUMB)
DEF_ICON_COLOR(BRUSH_ROTATE)
+DEF_ICON_COLOR(BRUSH_VCOL_BOUNDARY)
+DEF_ICON_COLOR(BRUSH_PAINT)
+DEF_ICON_COLOR(BRUSH_SCULPT_SMEAR)
/* grease pencil sculpt */
DEF_ICON_COLOR(GPBRUSH_SMOOTH)
diff --git a/source/blender/editors/interface/interface.c b/source/blender/editors/interface/interface.c
index fd75be5b847..ebfc3f307cb 100644
--- a/source/blender/editors/interface/interface.c
+++ b/source/blender/editors/interface/interface.c
@@ -88,8 +88,27 @@
#include "DEG_depsgraph_query.h"
+#include "BLI_asan.h"
#include "interface_intern.h"
+void poison_ui_but(struct uiBut *but)
+{
+ BLI_asan_poison(but->poison1, sizeof(but->poison1));
+ BLI_asan_poison(but->poison2, sizeof(but->poison2));
+ BLI_asan_poison(but->poison3, sizeof(but->poison3));
+ BLI_asan_poison(but->poison4, sizeof(but->poison4));
+ BLI_asan_poison(but->poison5, sizeof(but->poison5));
+}
+
+void unpoison_ui_but(struct uiBut *but)
+{
+ BLI_asan_unpoison(but->poison1, sizeof(but->poison1));
+ BLI_asan_unpoison(but->poison2, sizeof(but->poison2));
+ BLI_asan_unpoison(but->poison3, sizeof(but->poison3));
+ BLI_asan_unpoison(but->poison4, sizeof(but->poison4));
+ BLI_asan_unpoison(but->poison5, sizeof(but->poison5));
+}
+
/* prototypes. */
static void ui_but_to_pixelrect(struct rcti *rect,
const struct ARegion *region,
@@ -3370,9 +3389,16 @@ static void ui_but_free_type_specific(uiBut *but)
}
}
+#include "BLI_compiler_attrs.h"
+#include "BLI_threads.h"
+
/* can be called with C==NULL */
static void ui_but_free(const bContext *C, uiBut *but)
{
+ if (!BLI_thread_is_main()) {
+ printf("Evil!\n");
+ }
+
if (but->opptr) {
WM_operator_properties_free(but->opptr);
MEM_freeN(but->opptr);
@@ -3420,6 +3446,7 @@ static void ui_but_free(const bContext *C, uiBut *but)
BLI_assert(UI_butstore_is_registered(but->block, but) == false);
+ unpoison_ui_but(but);
MEM_freeN(but);
}
@@ -3966,7 +3993,11 @@ static uiBut *ui_but_alloc(const eButType type)
const char *alloc_str;
ui_but_alloc_info(type, &alloc_size, &alloc_str, NULL);
- return MEM_callocN(alloc_size, alloc_str);
+ uiBut *ret = MEM_callocN(alloc_size, alloc_str);
+
+ poison_ui_but(ret);
+
+ return ret;
}
/**
@@ -4000,7 +4031,10 @@ uiBut *ui_but_change_type(uiBut *but, eButType new_type)
const bool has_str_ptr_to_self = but->str == but->strdata;
const bool has_poin_ptr_to_self = but->poin == (char *)but;
+ unpoison_ui_but(but);
but = MEM_recallocN_id(but, alloc_size, alloc_str);
+ poison_ui_but(but);
+
but->type = new_type;
if (has_str_ptr_to_self) {
but->str = but->strdata;
diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c
index 77ae16d7cc7..d4eff9f1151 100644
--- a/source/blender/editors/interface/interface_handlers.c
+++ b/source/blender/editors/interface/interface_handlers.c
@@ -8566,7 +8566,11 @@ static void button_activate_exit(
#ifdef USE_ALLSELECT
{
/* only RNA from this button is used */
+
+ unpoison_ui_but(but);
uiBut but_temp = *but;
+ poison_ui_but(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.h b/source/blender/editors/interface/interface_intern.h
index d61104f094e..601227b75a3 100644
--- a/source/blender/editors/interface/interface_intern.h
+++ b/source/blender/editors/interface/interface_intern.h
@@ -239,6 +239,7 @@ struct uiBut {
short modifier_key;
short iconadd;
+ char poison1[512];
/** #UI_BTYPE_BLOCK data */
uiBlockCreateFunc block_create_func;
@@ -253,6 +254,7 @@ struct uiBut {
int rnaindex;
/* Operator data */
+ char poison2[512];
struct wmOperatorType *optype;
struct PointerRNA *opptr;
short opcontext;
@@ -262,6 +264,8 @@ struct uiBut {
ListBase extra_op_icons; /** #uiButExtraOpIcon */
+ char poison3[512];
+
/* Drag-able data, type is WM_DRAG_... */
char dragtype;
short dragflag;
@@ -269,6 +273,8 @@ struct uiBut {
struct ImBuf *imb;
float imb_scale;
+ char poison4[512];
+
/** Active button data (set when the user is hovering or interacting with a button). */
struct uiHandleButtonData *active;
@@ -279,6 +285,8 @@ struct uiBut {
double *editval;
float *editvec;
+ char poison5[512];
+
uiButPushedStateFunc pushed_state_func;
const void *pushed_state_arg;
@@ -1273,6 +1281,8 @@ bool ui_jump_to_target_button_poll(struct bContext *C);
/* interface_queries.c */
void ui_interface_tag_script_reload_queries(void);
+void poison_ui_but(struct uiBut *but);
+void unpoison_ui_but(struct uiBut *but);
#ifdef __cplusplus
}
diff --git a/source/blender/editors/mesh/editmesh_mask_extract.c b/source/blender/editors/mesh/editmesh_mask_extract.c
index cccfc7e934c..38e6bb80c0f 100644
--- a/source/blender/editors/mesh/editmesh_mask_extract.c
+++ b/source/blender/editors/mesh/editmesh_mask_extract.c
@@ -61,14 +61,16 @@
#include "mesh_intern.h" /* own include */
+#include "../sculpt_paint/sculpt_intern.h"
+
static bool geometry_extract_poll(bContext *C)
{
Object *ob = CTX_data_active_object(C);
if (ob != NULL && 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;
- }
+ // 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;
@@ -120,7 +122,8 @@ static int geometry_extract_apply(bContext *C,
.use_toolflags = true,
}));
- BM_mesh_bm_from_me(bm,
+ BM_mesh_bm_from_me(NULL,
+ bm,
new_mesh,
(&(struct BMeshFromMeshParams){
.calc_face_normal = true,
@@ -373,6 +376,7 @@ static int face_set_extract_invoke(bContext *C, wmOperator *op, const wmEvent *U
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;
}
@@ -513,7 +517,8 @@ static int paint_mask_slice_exec(bContext *C, wmOperator *op)
.use_toolflags = true,
}));
- BM_mesh_bm_from_me(bm,
+ BM_mesh_bm_from_me(NULL,
+ bm,
new_mesh,
(&(struct BMeshFromMeshParams){
.calc_face_normal = true,
@@ -544,7 +549,8 @@ static int paint_mask_slice_exec(bContext *C, wmOperator *op)
.use_toolflags = true,
}));
- BM_mesh_bm_from_me(bm,
+ BM_mesh_bm_from_me(NULL,
+ bm,
new_ob_mesh,
(&(struct BMeshFromMeshParams){
.calc_face_normal = true,
@@ -580,15 +586,42 @@ static int paint_mask_slice_exec(bContext *C, wmOperator *op)
if (ob->mode == OB_MODE_SCULPT) {
SculptSession *ss = ob->sculpt;
- ss->face_sets = CustomData_get_layer(&((Mesh *)ob->data)->pdata, CD_SCULPT_FACE_SETS);
- 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(ob->data);
- ED_sculpt_face_sets_initialize_none_to_id(ob->data, next_face_set_id);
+
+ /* 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 = CustomData_get_layer(&((Mesh *)ob->data)->pdata, CD_SCULPT_FACE_SETS);
+
+ if (ss->face_sets) {
+ 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);
+ }
+ break;
+ case PBVH_BMESH: {
+ if (ss->bm && CustomData_has_layer(&ss->bm->pdata, CD_SCULPT_FACE_SETS)) {
+ const int cd_fset = CustomData_get_offset(&ss->bm->pdata, CD_SCULPT_FACE_SETS);
+ BMFace *f;
+ BMIter iter;
+
+ const int next_face_set_id = SCULPT_face_set_next_available_get(ss);
+
+ 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);
}
+ ED_sculpt_undo_geometry_end(ob);
+
BKE_mesh_batch_cache_dirty_tag(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);
diff --git a/source/blender/editors/mesh/editmesh_polybuild.c b/source/blender/editors/mesh/editmesh_polybuild.c
index 303cf41df0d..e9594e62add 100644
--- a/source/blender/editors/mesh/editmesh_polybuild.c
+++ b/source/blender/editors/mesh/editmesh_polybuild.c
@@ -221,7 +221,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);
changed = true;
}
else {
@@ -570,7 +570,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);
}
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 122214b87d5..e90f38be727 100644
--- a/source/blender/editors/mesh/editmesh_tools.c
+++ b/source/blender/editors/mesh/editmesh_tools.c
@@ -4396,7 +4396,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;
@@ -4462,7 +4463,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;
@@ -4645,6 +4647,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){
@@ -4738,7 +4741,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:
@@ -4754,6 +4757,7 @@ static int edbm_separate_exec(bContext *C, wmOperator *op)
if (changed) {
BM_mesh_bm_to_me(bmain,
+ ob,
bm_old,
me,
(&(struct BMeshToMeshParams){
@@ -5943,6 +5947,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(
+ 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(
+ 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");
@@ -7019,7 +7133,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);
DEG_id_tag_update(ob->data, ID_RECALC_GEOMETRY);
WM_event_add_notifier(C, NC_GEOM | ND_DATA, ob->data);
diff --git a/source/blender/editors/mesh/editmesh_undo.c b/source/blender/editors/mesh/editmesh_undo.c
index f52cd94b8dc..171d8862861 100644
--- a/source/blender/editors/mesh/editmesh_undo.c
+++ b/source/blender/editors/mesh/editmesh_undo.c
@@ -607,7 +607,7 @@ static void *undomesh_from_editmesh(UndoMesh *um, BMEditMesh *em, Key *key, Undo
/* BM_mesh_validate(em->bm); */ /* for troubleshooting */
BM_mesh_bm_to_me(
- NULL,
+ NULL, NULL,
em->bm,
&um->me,
(&(struct BMeshToMeshParams){
@@ -679,7 +679,7 @@ static void undomesh_to_editmesh(UndoMesh *um, Object *ob, BMEditMesh *em, Key *
.use_toolflags = true,
}));
- BM_mesh_bm_from_me(bm,
+ BM_mesh_bm_from_me(NULL, bm,
&um->me,
(&(struct BMeshFromMeshParams){
/* Handled with tessellation. */
diff --git a/source/blender/editors/mesh/editmesh_utils.c b/source/blender/editors/mesh/editmesh_utils.c
index c6a8e771362..1bfe4ef254d 100644
--- a/source/blender/editors/mesh/editmesh_utils.c
+++ b/source/blender/editors/mesh/editmesh_utils.c
@@ -337,6 +337,7 @@ void EDBM_mesh_load_ex(Main *bmain, Object *ob, bool free_data)
}
BM_mesh_bm_to_me(bmain,
+ ob,
bm,
me,
(&(struct BMeshToMeshParams){
diff --git a/source/blender/editors/mesh/mesh_intern.h b/source/blender/editors/mesh/mesh_intern.h
index 8dbca8a4ea8..3c9d62c0dcd 100644
--- a/source/blender/editors/mesh/mesh_intern.h
+++ b/source/blender/editors/mesh/mesh_intern.h
@@ -262,6 +262,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);
/* *** editmesh_mask_extract.c *** */
void MESH_OT_paint_mask_extract(struct wmOperatorType *ot);
@@ -291,3 +292,4 @@ 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);
diff --git a/source/blender/editors/mesh/mesh_ops.c b/source/blender/editors/mesh/mesh_ops.c
index d98e03405a4..63d906208ff 100644
--- a/source/blender/editors/mesh/mesh_ops.c
+++ b/source/blender/editors/mesh/mesh_ops.c
@@ -207,6 +207,9 @@ 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);
+
}
#if 0 /* UNUSED, remove? */
diff --git a/source/blender/editors/object/object_modifier.c b/source/blender/editors/object/object_modifier.c
index b9942bc563a..26589019b0b 100644
--- a/source/blender/editors/object/object_modifier.c
+++ b/source/blender/editors/object/object_modifier.c
@@ -859,7 +859,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(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;
}
@@ -1396,6 +1406,9 @@ static bool modifier_apply_poll_ex(bContext *C, bool allow_shared)
Object *ob = (ptr.owner_id != NULL) ? (Object *)ptr.owner_id : ED_object_active_context(C);
ModifierData *md = ptr.data; /* May be NULL. */
+ allow_shared = true;
+ // allow_shared = allow_shared || (md && md->type == eModifierType_DataTransfer);
+
if (ID_IS_OVERRIDE_LIBRARY(ob) || ((ob->data != NULL) && ID_IS_OVERRIDE_LIBRARY(ob->data))) {
CTX_wm_operator_poll_msg_set(C, "Modifiers cannot be applied on override data");
return false;
@@ -1923,8 +1936,8 @@ static int multires_subdivide_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
- const eMultiresSubdivideModeType subdivide_mode = (eMultiresSubdivideModeType)(RNA_enum_get(
- op->ptr, "mode"));
+ const eMultiresSubdivideModeType subdivide_mode = (eMultiresSubdivideModeType)(
+ RNA_enum_get(op->ptr, "mode"));
multiresModifier_subdivide(object, mmd, subdivide_mode);
ED_object_iter_other(
@@ -2594,7 +2607,8 @@ static Object *modifier_skin_armature_create(Depsgraph *depsgraph, Main *bmain,
MVertSkin *mvert_skin = CustomData_get_layer(&me->vdata, CD_MVERT_SKIN);
int *emap_mem;
MeshElemMap *emap;
- BKE_mesh_vert_edge_map_create(&emap, &emap_mem, me->medge, me->totvert, me->totedge);
+ BKE_mesh_vert_edge_map_create(
+ &emap, &emap_mem, me->mvert, me->medge, me->totvert, me->totedge, false);
BLI_bitmap *edges_visited = BLI_BITMAP_NEW(me->totedge, "edge_visited");
diff --git a/source/blender/editors/object/object_remesh.cc b/source/blender/editors/object/object_remesh.cc
index e859b8fa85a..c7d0ea4cf8e 100644
--- a/source/blender/editors/object/object_remesh.cc
+++ b/source/blender/editors/object/object_remesh.cc
@@ -772,7 +772,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;
@@ -793,8 +793,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(
+
+ mesh_bisect = BKE_mesh_mirror_bisect_on_mirror_plane_for_modifier(ob,
&mmd, mesh_bisect, axis, plane_co, plane_no);
+
if (mesh_bisect_temp != mesh_bisect) {
BKE_id_free(nullptr, mesh_bisect_temp);
}
@@ -862,7 +864,7 @@ static void quadriflow_start_job(void *customdata, short *stop, short *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.c b/source/blender/editors/object/object_vgroup.c
index f0ab082cd9c..becb815f8b1 100644
--- a/source/blender/editors/object/object_vgroup.c
+++ b/source/blender/editors/object/object_vgroup.c
@@ -1963,7 +1963,8 @@ static void vgroup_smooth_subset(Object *ob,
emap_mem = NULL;
}
else {
- BKE_mesh_vert_edge_map_create(&emap, &emap_mem, me->medge, me->totvert, me->totedge);
+ BKE_mesh_vert_edge_map_create(
+ &emap, &emap_mem, me->mvert, me->medge, me->totvert, me->totedge, false);
}
weight_accum_prev = MEM_mallocN(sizeof(*weight_accum_prev) * dvert_tot, __func__);
diff --git a/source/blender/editors/sculpt_paint/CMakeLists.txt b/source/blender/editors/sculpt_paint/CMakeLists.txt
index c8c88e9d412..bb0d903317a 100644
--- a/source/blender/editors/sculpt_paint/CMakeLists.txt
+++ b/source/blender/editors/sculpt_paint/CMakeLists.txt
@@ -56,7 +56,9 @@ set(SRC
paint_vertex_weight_ops.c
paint_vertex_weight_utils.c
sculpt.c
+
sculpt_array.c
+ sculpt_curvature.c
sculpt_automasking.c
sculpt_boundary.c
sculpt_cloth.c
@@ -81,6 +83,14 @@ set(SRC
sculpt_transform.c
sculpt_undo.c
sculpt_uv.c
+ sculpt_displacement.c
+ sculpt_displacement.h
+ sculpt_replay.c
+
+ sculpt.cc
+ sculpt.hh
+
+ sculpt_brush_machine.c
paint_intern.h
sculpt_intern.h
diff --git a/source/blender/editors/sculpt_paint/paint_cursor.c b/source/blender/editors/sculpt_paint/paint_cursor.c
index 100ba5baf3b..e0296f7a6ac 100644
--- a/source/blender/editors/sculpt_paint/paint_cursor.c
+++ b/source/blender/editors/sculpt_paint/paint_cursor.c
@@ -31,13 +31,13 @@
#include "DNA_brush_types.h"
#include "DNA_color_types.h"
#include "DNA_customdata_types.h"
+#include "DNA_mesh_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
#include "DNA_screen_types.h"
#include "DNA_space_types.h"
#include "DNA_userdef_types.h"
#include "DNA_view3d_types.h"
-#include "DNA_mesh_types.h"
#include "BKE_brush.h"
#include "BKE_colortools.h"
@@ -1224,7 +1224,7 @@ typedef struct PaintCursorContext {
/* Sculpt related data. */
Sculpt *sd;
SculptSession *ss;
- int prev_active_vertex_index;
+ SculptVertRef prev_active_vertex_index;
bool is_stroke_active;
bool is_cursor_over_mesh;
bool is_multires;
@@ -1465,7 +1465,8 @@ static void paint_draw_3D_view_inactive_brush_cursor(PaintCursorContext *pcontex
80);
}
-static void sculpt_cursor_draw_active_face_set_color_set(PaintCursorContext *pcontext) {
+static void sculpt_cursor_draw_active_face_set_color_set(PaintCursorContext *pcontext)
+{
SculptSession *ss = pcontext->ss;
@@ -1485,7 +1486,6 @@ static void sculpt_cursor_draw_active_face_set_color_set(PaintCursorContext *pco
color[3] /= 2;
}
-
immUniformColor4ubv(color);
}
@@ -1501,7 +1501,9 @@ static void sculpt_cursor_draw_3D_face_set_preview(PaintCursorContext *pcontext)
GPU_line_width(1.0f);
sculpt_cursor_draw_active_face_set_color_set(pcontext);
- MPoly *poly = &ss->mpoly[ss->active_face_index];
+ int fi = BKE_pbvh_face_index_to_table(ss->pbvh, ss->active_face_index);
+
+ MPoly *poly = &ss->mpoly[fi];
MLoop *loops = ss->mloop;
const int totpoints = poly->totloop;
@@ -1516,7 +1518,6 @@ static void sculpt_cursor_draw_3D_face_set_preview(PaintCursorContext *pcontext)
immEnd();
*/
-
/*
int v_in_poly = 0;
for (int i = 0; i < totpoints; i++) {
@@ -1550,13 +1551,10 @@ static void sculpt_cursor_draw_3D_face_set_preview(PaintCursorContext *pcontext)
}
SCULPT_VERTEX_NEIGHBORS_ITER_END(ni);
-
-
-
immBegin(GPU_PRIM_LINES, total * 2);
SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, ss->active_vertex_index, ni) {
immVertex3fv(pcontext->pos, SCULPT_active_vertex_co_get(ss));
- immVertex3fv(pcontext->pos, SCULPT_vertex_co_get(ss, ni.index));
+ immVertex3fv(pcontext->pos, SCULPT_vertex_co_get(ss, ni.vertex));
}
SCULPT_VERTEX_NEIGHBORS_ITER_END(ni);
immEnd();
@@ -1687,8 +1685,8 @@ static void paint_cursor_draw_3d_view_brush_cursor_inactive(PaintCursorContext *
paint_cursor_update_object_space_radius(pcontext);
- const bool update_previews = pcontext->prev_active_vertex_index !=
- SCULPT_active_vertex_get(pcontext->ss);
+ const bool update_previews = pcontext->prev_active_vertex_index.i !=
+ SCULPT_active_vertex_get(pcontext->ss).i;
/* Setup drawing. */
wmViewport(&pcontext->region->winrct);
@@ -1753,7 +1751,6 @@ 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(
diff --git a/source/blender/editors/sculpt_paint/paint_hide.c b/source/blender/editors/sculpt_paint/paint_hide.c
index da627c6b7db..4e3e074c9bf 100644
--- a/source/blender/editors/sculpt_paint/paint_hide.c
+++ b/source/blender/editors/sculpt_paint/paint_hide.c
@@ -209,17 +209,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 = BLI_gsetIterator_getKey(&gs_iter);
+ BMVert *v;
+ TGSET_ITER (v, verts) {
float *vmask = CustomData_bmesh_get(&bm->vdata, v->head.data, CD_PAINT_MASK);
/* Hide vertex if in the hide volume. */
@@ -237,15 +235,14 @@ 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)
{
- GSetIterator gs_iter;
-
- GSET_ITER (gs_iter, faces) {
- BMFace *f = BLI_gsetIterator_getKey(&gs_iter);
+ BMFace *f;
+ TGSET_ITER (f, faces) {
if (paint_is_bmesh_face_hidden(f)) {
BM_elem_flag_enable(f, BM_ELEM_HIDDEN);
}
@@ -253,6 +250,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,
@@ -263,7 +261,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);
diff --git a/source/blender/editors/sculpt_paint/paint_image_proj.c b/source/blender/editors/sculpt_paint/paint_image_proj.c
index a58b1947b0c..75834e019f5 100644
--- a/source/blender/editors/sculpt_paint/paint_image_proj.c
+++ b/source/blender/editors/sculpt_paint/paint_image_proj.c
@@ -1081,10 +1081,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;
@@ -1092,9 +1093,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]);
@@ -1103,6 +1103,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? */
diff --git a/source/blender/editors/sculpt_paint/paint_intern.h b/source/blender/editors/sculpt_paint/paint_intern.h
index 7341d984c91..4f5187a71d0 100644
--- a/source/blender/editors/sculpt_paint/paint_intern.h
+++ b/source/blender/editors/sculpt_paint/paint_intern.h
@@ -51,6 +51,10 @@ typedef struct CoNo {
float no[3];
} CoNo;
+#include "DNA_listBase.h"
+#include "DNA_scene_types.h"
+#include "ED_view3d.h"
+
/* paint_stroke.c */
typedef bool (*StrokeGetLocation)(struct bContext *C, float location[3], const float mouse[2]);
typedef bool (*StrokeTestStart)(struct bContext *C, struct wmOperator *op, const float mouse[2]);
@@ -60,6 +64,80 @@ typedef void (*StrokeUpdateStep)(struct bContext *C,
typedef void (*StrokeRedraw)(const struct bContext *C, struct PaintStroke *stroke, bool final);
typedef void (*StrokeDone)(const struct bContext *C, struct PaintStroke *stroke);
+typedef struct PaintSample {
+ float mouse[2];
+ float pressure;
+} PaintSample;
+
+typedef struct PaintStroke {
+ void *mode_data;
+ void *stroke_cursor;
+ struct wmTimer *timer;
+ struct RNG *rng;
+
+ /* Cached values */
+ struct ViewContext vc;
+ struct Brush *brush;
+ struct UnifiedPaintSettings *ups;
+
+ /* used for lines and curves */
+ ListBase line;
+
+ /* Paint stroke can use up to PAINT_MAX_INPUT_SAMPLES prior inputs
+ * to smooth the stroke */
+ PaintSample samples[PAINT_MAX_INPUT_SAMPLES];
+ int num_samples;
+ int cur_sample;
+ int tot_samples;
+
+ float last_mouse_position[2];
+ float last_world_space_position[3];
+ bool stroke_over_mesh;
+ /* space distance covered so far */
+ float stroke_distance;
+ float stroke_distance_t; // divided by brush radius
+
+ /* Set whether any stroke step has yet occurred
+ * e.g. in sculpt mode, stroke doesn't start until cursor
+ * passes over the mesh */
+ bool stroke_started;
+ /* Set when enough motion was found for rake rotation */
+ bool rake_started;
+ /* event that started stroke, for modal() return */
+ int event_type;
+ /* check if stroke variables have been initialized */
+ bool stroke_init;
+ /* check if various brush mapping variables have been initialized */
+ bool brush_init;
+ float initial_mouse[2];
+ /* cached_pressure stores initial pressure for size pressure influence mainly */
+ float cached_size_pressure;
+ /* last pressure will store last pressure value for use in interpolation for space strokes */
+ float last_pressure;
+ int stroke_mode;
+
+ float last_tablet_event_pressure;
+
+ float zoom_2d;
+ int pen_flip;
+
+ /* Tilt, as read from the event. */
+ float x_tilt;
+ float y_tilt;
+
+ /* line constraint */
+ bool constrain_line;
+ float constrained_pos[2];
+
+ StrokeGetLocation get_location;
+ StrokeTestStart test_start;
+ StrokeUpdateStep update_step;
+ StrokeRedraw redraw;
+ StrokeDone done;
+
+ float spacing;
+} PaintStroke;
+
struct PaintStroke *paint_stroke_new(struct bContext *C,
struct wmOperator *op,
StrokeGetLocation get_location,
@@ -91,6 +169,19 @@ bool PAINT_brush_tool_poll(struct bContext *C);
void paint_cursor_start(struct Paint *p, bool (*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);
bool weight_paint_poll_ignore_tool(bContext *C);
diff --git a/source/blender/editors/sculpt_paint/paint_mask.c b/source/blender/editors/sculpt_paint/paint_mask.c
index a0f4bac2e7c..a78577a70f6 100644
--- a/source/blender/editors/sculpt_paint/paint_mask.c
+++ b/source/blender/editors/sculpt_paint/paint_mask.c
@@ -680,7 +680,7 @@ static bool sculpt_gesture_is_effected_lasso(SculptGestureContext *sgcontext, co
static bool sculpt_gesture_is_vertex_effected(SculptGestureContext *sgcontext, PBVHVertexIter *vd)
{
float vertex_normal[3];
- SCULPT_vertex_normal_get(sgcontext->ss, vd->index, vertex_normal);
+ SCULPT_vertex_normal_get(sgcontext->ss, vd->vertex, vertex_normal);
float dot = dot_v3v3(sgcontext->view_normal, vertex_normal);
const bool is_effected_front_face = !(sgcontext->front_faces_only && dot < 0.0f);
@@ -759,7 +759,7 @@ static void face_set_gesture_apply_task_cb(void *__restrict userdata,
BKE_pbvh_vertex_iter_begin (sgcontext->ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
if (sculpt_gesture_is_vertex_effected(sgcontext, &vd)) {
- SCULPT_vertex_face_set_set(sgcontext->ss, vd.index, face_set_operation->new_face_set_id);
+ SCULPT_vertex_face_set_set(sgcontext->ss, vd.vertex, face_set_operation->new_face_set_id);
any_updated = true;
}
}
@@ -994,7 +994,8 @@ static void sculpt_gesture_trim_normals_update(SculptGestureContext *sgcontext)
.use_toolflags = true,
}));
- BM_mesh_bm_from_me(bm,
+ BM_mesh_bm_from_me(NULL,
+ bm,
trim_mesh,
(&(struct BMeshFromMeshParams){
.calc_face_normal = true,
@@ -1060,7 +1061,7 @@ static void sculpt_gesture_trim_calculate_depth(SculptGestureContext *sgcontext)
trim_operation->depth_back = -FLT_MAX;
for (int i = 0; i < totvert; i++) {
- const float *vco = SCULPT_vertex_co_get(ss, i);
+ const float *vco = SCULPT_vertex_co_get(ss, BKE_pbvh_table_index_to_vertex(ss->pbvh, i));
/* 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. */
@@ -1252,7 +1253,7 @@ static void sculpt_gesture_trim_geometry_free(SculptGestureContext *sgcontext)
static int bm_face_isect_pair(BMFace *f, void *UNUSED(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)
@@ -1262,20 +1263,30 @@ static void sculpt_gesture_apply_trim(SculptGestureContext *sgcontext)
Mesh *trim_mesh = trim_operation->mesh;
BMesh *bm;
- const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(sculpt_mesh, trim_mesh);
- bm = BM_mesh_create(&allocsize,
- &((struct BMeshCreateParams){
- .use_toolflags = false,
- }));
- BM_mesh_bm_from_me(bm,
- trim_mesh,
- &((struct BMeshFromMeshParams){
- .calc_face_normal = true,
- }));
+ if (sgcontext->ss && sgcontext->ss->bm) {
+ bm = sgcontext->ss->bm;
+ BM_mesh_normals_update(bm);
+ }
- BM_mesh_bm_from_me(bm,
- sculpt_mesh,
+ else {
+ const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(sculpt_mesh, trim_mesh);
+ bm = BM_mesh_create(&allocsize,
+ &((struct BMeshCreateParams){
+ .use_toolflags = false,
+ }));
+
+ BM_mesh_bm_from_me(NULL,
+ bm,
+ sculpt_mesh,
+ &((struct BMeshFromMeshParams){
+ .calc_face_normal = true,
+ }));
+ }
+
+ BM_mesh_bm_from_me(NULL,
+ bm,
+ trim_mesh,
&((struct BMeshFromMeshParams){
.calc_face_normal = true,
}));
@@ -1336,15 +1347,32 @@ static void sculpt_gesture_apply_trim(SculptGestureContext *sgcontext)
MEM_freeN(looptris);
- Mesh *result = BKE_mesh_from_bmesh_nomain(bm,
- (&(struct BMeshToMeshParams){
- .calc_object_remap = false,
- }),
- sculpt_mesh);
- BM_mesh_free(bm);
- BKE_mesh_normals_tag_dirty(result);
- BKE_mesh_nomain_to_mesh(
- result, sgcontext->vc.obact->data, sgcontext->vc.obact, &CD_MASK_MESH, true);
+ if (sgcontext->ss && sgcontext->ss->bm) { // rebuild pbvh
+ BKE_pbvh_free(sgcontext->ss->pbvh);
+ sgcontext->ss->pbvh = BKE_pbvh_new();
+
+ BKE_pbvh_build_bmesh(sgcontext->ss->pbvh,
+ sgcontext->ss->bm,
+ sgcontext->ss->bm_smooth_shading,
+ sgcontext->ss->bm_log,
+ sgcontext->ss->cd_vert_node_offset,
+ sgcontext->ss->cd_face_node_offset,
+ sgcontext->ss->cd_dyn_vert,
+ sgcontext->ss->cd_face_areas,
+ sgcontext->ss->fast_draw);
+ }
+ else { // save result to mesh
+ Mesh *result = BKE_mesh_from_bmesh_nomain(bm,
+ (&(struct BMeshToMeshParams){
+ .calc_object_remap = false,
+ }),
+ sculpt_mesh);
+ BM_mesh_free(bm);
+ result->runtime.cd_dirty_vert |= CD_MASK_NORMAL;
+ BKE_mesh_normals_tag_dirty(result);
+ BKE_mesh_nomain_to_mesh(
+ result, sgcontext->vc.obact->data, sgcontext->vc.obact, &CD_MASK_MESH, true);
+ }
}
static void sculpt_gesture_trim_begin(bContext *C, SculptGestureContext *sgcontext)
@@ -1381,6 +1409,10 @@ static void sculpt_gesture_trim_end(bContext *UNUSED(C), SculptGestureContext *s
sculpt_gesture_trim_geometry_free(sgcontext);
+ if (sgcontext->ss && sgcontext->ss->bm) {
+ SCULPT_dynamic_topology_triangulate(sgcontext->ss, sgcontext->ss->bm);
+ }
+
SCULPT_undo_push_node(sgcontext->vc.obact, NULL, SCULPT_UNDO_GEOMETRY);
BKE_mesh_batch_cache_dirty_tag(sgcontext->vc.obact->data, BKE_MESH_BATCH_DIRTY_ALL);
DEG_id_tag_update(&sgcontext->vc.obact->id, ID_RECALC_GEOMETRY);
@@ -1566,7 +1598,7 @@ static void project_gesture_project_fairing_boundary_task_cb(
}
SculptVertexNeighborIter ni;
- SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.index, ni) {
+ SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.vertex, ni) {
if (project_operation->fairing_mask[ni.index] != vertex_fairing_mask) {
project_vertex = true;
break;
@@ -1754,8 +1786,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;
}
diff --git a/source/blender/editors/sculpt_paint/paint_stroke.c b/source/blender/editors/sculpt_paint/paint_stroke.c
index 938b7acd74b..ffa8714d162 100644
--- a/source/blender/editors/sculpt_paint/paint_stroke.c
+++ b/source/blender/editors/sculpt_paint/paint_stroke.c
@@ -67,77 +67,6 @@
# include "PIL_time_utildefines.h"
#endif
-typedef struct PaintSample {
- float mouse[2];
- float pressure;
-} PaintSample;
-
-typedef struct PaintStroke {
- void *mode_data;
- void *stroke_cursor;
- wmTimer *timer;
- struct RNG *rng;
-
- /* Cached values */
- ViewContext vc;
- Brush *brush;
- UnifiedPaintSettings *ups;
-
- /* used for lines and curves */
- ListBase line;
-
- /* Paint stroke can use up to PAINT_MAX_INPUT_SAMPLES prior inputs
- * to smooth the stroke */
- PaintSample samples[PAINT_MAX_INPUT_SAMPLES];
- int num_samples;
- int cur_sample;
- int tot_samples;
-
- float last_mouse_position[2];
- float last_world_space_position[3];
- bool stroke_over_mesh;
- /* space distance covered so far */
- float stroke_distance;
-
- /* Set whether any stroke step has yet occurred
- * e.g. in sculpt mode, stroke doesn't start until cursor
- * passes over the mesh */
- bool stroke_started;
- /* Set when enough motion was found for rake rotation */
- bool rake_started;
- /* event that started stroke, for modal() return */
- int event_type;
- /* check if stroke variables have been initialized */
- bool stroke_init;
- /* check if various brush mapping variables have been initialized */
- bool brush_init;
- float initial_mouse[2];
- /* cached_pressure stores initial pressure for size pressure influence mainly */
- float cached_size_pressure;
- /* last pressure will store last pressure value for use in interpolation for space strokes */
- float last_pressure;
- int stroke_mode;
-
- float last_tablet_event_pressure;
-
- float zoom_2d;
- int pen_flip;
-
- /* Tilt, as read from the event. */
- float x_tilt;
- float y_tilt;
-
- /* line constraint */
- bool constrain_line;
- float constrained_pos[2];
-
- StrokeGetLocation get_location;
- StrokeTestStart test_start;
- StrokeUpdateStep update_step;
- StrokeRedraw redraw;
- StrokeDone done;
-} PaintStroke;
-
/*** Cursors ***/
static void paint_draw_smooth_cursor(bContext *C, int x, int y, void *customdata)
{
@@ -290,6 +219,26 @@ static bool paint_tool_require_inbetween_mouse_events(Brush *brush, ePaintMode m
return true;
}
+bool paint_stroke_apply_subspacing(struct PaintStroke *stroke,
+ const float spacing,
+ const enum ePaintMode mode,
+ float *state)
+{
+ if (!paint_space_stroke_enabled(stroke->brush, mode)) {
+ return true;
+ }
+
+ const float t = stroke->stroke_distance_t;
+
+ if (t != 0.0f && t < *state + spacing) {
+ return false;
+ }
+ else {
+ *state = t;
+ return true;
+ }
+}
+
/* Initialize the stroke cache variants from operator properties */
static bool paint_brush_update(bContext *C,
Brush *brush,
@@ -432,11 +381,13 @@ static bool paint_brush_update(bContext *C,
ups->anchored_size /= 2.0f;
ups->pixel_radius /= 2.0f;
stroke->stroke_distance = ups->pixel_radius;
+ stroke->stroke_distance_t = 1.0f;
}
else {
copy_v2_v2(ups->anchored_initial_mouse, stroke->initial_mouse);
copy_v2_v2(mouse, stroke->initial_mouse);
stroke->stroke_distance = ups->pixel_radius;
+ stroke->stroke_distance_t = 1.0f;
}
ups->pixel_radius /= stroke->zoom_2d;
ups->draw_anchored = true;
@@ -774,6 +725,58 @@ static float paint_stroke_integrate_overlap(Brush *br, float factor)
return 1.0f / max;
}
+static float paint_space_get_final_size_intern(
+ bContext *C, const Scene *scene, PaintStroke *stroke, float pressure, float dpressure)
+{
+ ePaintMode mode = BKE_paintmode_get_active_from_context(C);
+ float size = BKE_brush_size_get(scene, stroke->brush) * pressure;
+
+ if (paint_stroke_use_scene_spacing(stroke->brush, mode)) {
+ if (!BKE_brush_use_locked_size(scene, stroke->brush)) {
+ float last_object_space_position[3];
+ mul_v3_m4v3(
+ last_object_space_position, stroke->vc.obact->imat, stroke->last_world_space_position);
+ size = paint_calc_object_space_radius(&stroke->vc, last_object_space_position, size);
+ }
+ else {
+ size = BKE_brush_unprojected_radius_get(scene, stroke->brush) * pressure;
+ }
+ }
+
+ return size;
+}
+
+static float paint_space_get_final_size(bContext *C,
+ const Scene *scene,
+ PaintStroke *stroke,
+ float pressure,
+ float dpressure,
+ float length)
+{
+ if (BKE_brush_use_size_pressure(stroke->brush)) {
+ /* use pressure to modify size. set spacing so that at 100%, the circles
+ * are aligned nicely with no overlap. for this the spacing needs to be
+ * the average of the previous and next size. */
+ float s = paint_space_stroke_spacing(C, scene, stroke, 1.0f, pressure);
+ float q = s * dpressure / (2.0f * length);
+ float pressure_fac = (1.0f + q) / (1.0f - q);
+
+ float last_size_pressure = stroke->last_pressure;
+ float new_size_pressure = stroke->last_pressure * pressure_fac;
+
+ /* average spacing */
+ float last_size = paint_space_get_final_size_intern(
+ C, scene, stroke, last_size_pressure, pressure);
+ float new_size = paint_space_get_final_size_intern(
+ C, scene, stroke, new_size_pressure, pressure);
+
+ return 0.5f * (last_size + new_size);
+ }
+ else {
+ return paint_space_get_final_size_intern(C, scene, stroke, 1.0, 0.0);
+ }
+}
+
static float paint_space_stroke_spacing_variable(bContext *C,
const Scene *scene,
PaintStroke *stroke,
@@ -805,6 +808,8 @@ static float paint_space_stroke_spacing_variable(bContext *C,
return paint_space_stroke_spacing(C, scene, stroke, 1.0f, pressure);
}
+#include "BLI_compiler_attrs.h"
+
/* For brushes with stroke spacing enabled, moves mouse in steps
* towards the final mouse location. */
static int paint_space_stroke(bContext *C,
@@ -874,8 +879,17 @@ static int paint_space_stroke(bContext *C,
ups->overlap_factor = paint_stroke_integrate_overlap(stroke->brush,
spacing / no_pressure_spacing);
+ if (use_scene_spacing) {
+ float size = paint_space_get_final_size(C, scene, stroke, pressure, dpressure, length);
+
+ stroke->stroke_distance += stroke->ups->pixel_radius * spacing / size;
+ stroke->stroke_distance_t += spacing / size;
+ }
+ else {
+ stroke->stroke_distance += spacing / stroke->zoom_2d;
+ stroke->stroke_distance_t += (spacing / stroke->zoom_2d) / stroke->ups->pixel_radius;
+ }
- stroke->stroke_distance += spacing / stroke->zoom_2d;
paint_brush_stroke_add_step(C, op, mouse, pressure);
length -= spacing;
@@ -1245,6 +1259,8 @@ static void paint_line_strokes_spacing(bContext *C,
length += *length_residue;
*length_residue = 0.0;
+ stroke->spacing = spacing;
+
if (length >= spacing) {
if (use_scene_spacing) {
float final_world_space_position[3];
@@ -1262,6 +1278,8 @@ static void paint_line_strokes_spacing(bContext *C,
ups->overlap_factor = paint_stroke_integrate_overlap(stroke->brush, 1.0);
stroke->stroke_distance += spacing / stroke->zoom_2d;
+ stroke->stroke_distance_t += (spacing / stroke->zoom_2d) / stroke->ups->pixel_radius;
+
paint_brush_stroke_add_step(C, op, mouse, 1.0);
length -= spacing;
@@ -1484,6 +1502,7 @@ int paint_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event)
/* one time stroke initialization */
if (!stroke->stroke_started) {
stroke->last_pressure = sample_average.pressure;
+
copy_v2_v2(stroke->last_mouse_position, sample_average.mouse);
if (paint_stroke_use_scene_spacing(br, mode)) {
stroke->stroke_over_mesh = SCULPT_stroke_get_location(
@@ -1570,6 +1589,8 @@ int paint_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event)
float dmouse[2];
sub_v2_v2v2(dmouse, mouse, stroke->last_mouse_position);
stroke->stroke_distance += len_v2(dmouse);
+ stroke->stroke_distance_t += len_v2(dmouse) / stroke->ups->pixel_radius;
+
paint_brush_stroke_add_step(C, op, mouse, pressure);
redraw = true;
}
diff --git a/source/blender/editors/sculpt_paint/paint_vertex.c b/source/blender/editors/sculpt_paint/paint_vertex.c
index 9387b84f437..51b96fb9c99 100644
--- a/source/blender/editors/sculpt_paint/paint_vertex.c
+++ b/source/blender/editors/sculpt_paint/paint_vertex.c
@@ -1151,18 +1151,24 @@ static void vertex_paint_init_session_data(const ToolSettings *ts, Object *ob)
gmap->vert_to_poly = NULL;
BKE_mesh_vert_loop_map_create(&gmap->vert_to_loop,
&gmap->vert_map_mem,
+ me->mvert,
+ 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,
+ me->mvert,
+ me->medge,
me->mpoly,
me->mloop,
me->totvert,
me->totpoly,
- me->totloop);
+ me->totloop,
+ false);
}
/* Create average brush arrays */
@@ -1247,7 +1253,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->sculpt->cache);
ob->sculpt->cache = NULL;
}
BKE_sculptsession_free(ob);
@@ -1316,7 +1322,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->sculpt->cache);
ob->sculpt->cache = NULL;
}
@@ -2510,7 +2516,7 @@ static void wpaint_stroke_done(const bContext *C, struct PaintStroke *stroke)
WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob);
- SCULPT_cache_free(ob->sculpt->cache);
+ SCULPT_cache_free(ob->sculpt, ob->sculpt->cache);
ob->sculpt->cache = NULL;
}
@@ -2561,7 +2567,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->sculpt->cache);
ob->sculpt->cache = NULL;
}
@@ -3450,7 +3456,7 @@ static void vpaint_stroke_done(const bContext *C, struct PaintStroke *stroke)
MEM_freeN(vpd);
- SCULPT_cache_free(ob->sculpt->cache);
+ SCULPT_cache_free(ob->sculpt, ob->sculpt->cache);
ob->sculpt->cache = NULL;
}
@@ -3502,7 +3508,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->sculpt->cache);
ob->sculpt->cache = NULL;
}
diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c
index d9afafd16d9..73b79e8a11e 100644
--- a/source/blender/editors/sculpt_paint/sculpt.c
+++ b/source/blender/editors/sculpt_paint/sculpt.c
@@ -24,16 +24,22 @@
#include "MEM_guardedalloc.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_link_utils.h"
+#include "BLI_linklist.h"
#include "BLI_linklist_stack.h"
+#include "BLI_listbase.h"
#include "BLI_math.h"
#include "BLI_math_color_blend.h"
+#include "BLI_rand.h"
#include "BLI_task.h"
#include "BLI_utildefines.h"
+#include "atomic_ops.h"
#include "BLT_translation.h"
@@ -41,12 +47,14 @@
#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 "BKE_attribute.h"
#include "BKE_brush.h"
#include "BKE_ccg.h"
#include "BKE_colortools.h"
@@ -113,6 +121,9 @@
#include <stdlib.h>
#include <string.h>
+static bool sculpt_check_boundary_vertex_in_base_mesh(const SculptSession *ss,
+ const SculptVertRef index);
+
/* Sculpt PBVH abstraction API
*
* This is read-only, for writing use PBVH vertex iterators. There vd.index matches
@@ -123,9 +134,31 @@
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);
+ }
+}
+
+/* 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);
}
}
@@ -143,22 +176,62 @@ int SCULPT_vertex_count_get(SculptSession *ss)
return 0;
}
-const float *SCULPT_vertex_co_get(SculptSession *ss, int index)
+MDynTopoVert *SCULPT_vertex_get_mdyntopo(SculptSession *ss, SculptVertRef vertex)
+{
+ switch (BKE_pbvh_type(ss->pbvh)) {
+ case PBVH_BMESH: {
+ BMVert *v = (BMVert *)vertex.i;
+ return BKE_PBVH_DYNVERT(ss->cd_dyn_vert, v);
+ }
+
+ case PBVH_GRIDS:
+ case PBVH_FACES: {
+ return ss->mdyntopo_verts + vertex.i;
+ }
+ }
+
+ return NULL;
+}
+
+float *SCULPT_vertex_origco_get(SculptSession *ss, SculptVertRef vertex)
+{
+ switch (BKE_pbvh_type(ss->pbvh)) {
+ case PBVH_BMESH: {
+ BMVert *v = (BMVert *)vertex.i;
+ return BKE_PBVH_DYNVERT(ss->cd_dyn_vert, v)->origco;
+ }
+
+ case PBVH_GRIDS:
+ case PBVH_FACES: {
+ return ss->mdyntopo_verts[vertex.i].origco;
+ }
+ }
+
+ return NULL;
+}
+
+const float *SCULPT_vertex_co_get(SculptSession *ss, SculptVertRef index)
{
+ if (ss->bm) {
+ return ((BMVert *)index.i)->co;
+ }
+
switch (BKE_pbvh_type(ss->pbvh)) {
case PBVH_FACES: {
if (ss->shapekey_active || ss->deform_modifiers_active) {
const MVert *mverts = BKE_pbvh_get_verts(ss->pbvh);
- return mverts[index].co;
+ return mverts[index.i].co;
}
- return ss->mvert[index].co;
+ return ss->mvert[index.i].co;
+ }
+ case PBVH_BMESH: {
+ BMVert *v = (BMVert *)index.i;
+ return v->co;
}
- case PBVH_BMESH:
- return BM_vert_at_index(BKE_pbvh_get_bmesh(ss->pbvh), index)->co;
case PBVH_GRIDS: {
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;
+ const int grid_index = index.i / key->grid_area;
+ const int vertex_index = index.i - grid_index * key->grid_area;
CCGElem *elem = BKE_pbvh_get_grids(ss->pbvh)[grid_index];
return CCG_elem_co(key, CCG_elem_offset(key, elem, vertex_index));
}
@@ -166,41 +239,53 @@ const float *SCULPT_vertex_co_get(SculptSession *ss, int index)
return NULL;
}
-const float *SCULPT_vertex_color_get(SculptSession *ss, int index)
+const float *SCULPT_vertex_color_get(SculptSession *ss, SculptVertRef index)
{
switch (BKE_pbvh_type(ss->pbvh)) {
case PBVH_FACES:
if (ss->vcol) {
- return ss->vcol[index].color;
+ return ss->vcol[index.i].color;
}
break;
- case PBVH_BMESH:
+ case PBVH_BMESH: {
+ BMVert *v = (BMVert *)index.i;
+
+ if (ss->cd_vcol_offset >= 0) {
+ MPropCol *col = BM_ELEM_CD_GET_VOID_P(v, ss->cd_vcol_offset);
+ return col->color;
+ }
+
+ break;
+ }
case PBVH_GRIDS:
break;
}
return NULL;
}
-void SCULPT_vertex_normal_get(SculptSession *ss, int index, float no[3])
+void SCULPT_vertex_normal_get(SculptSession *ss, SculptVertRef index, float no[3])
{
switch (BKE_pbvh_type(ss->pbvh)) {
case PBVH_FACES: {
if (ss->shapekey_active || ss->deform_modifiers_active) {
const MVert *mverts = BKE_pbvh_get_verts(ss->pbvh);
- normal_short_to_float_v3(no, mverts[index].no);
+ normal_short_to_float_v3(no, mverts[index.i].no);
}
else {
- normal_short_to_float_v3(no, ss->mvert[index].no);
+ normal_short_to_float_v3(no, ss->mvert[index.i].no);
}
break;
}
- case PBVH_BMESH:
- copy_v3_v3(no, BM_vert_at_index(BKE_pbvh_get_bmesh(ss->pbvh), index)->no);
+ case PBVH_BMESH: {
+ BMVert *v = (BMVert *)index.i;
+
+ copy_v3_v3(no, v->no);
break;
+ }
case PBVH_GRIDS: {
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;
+ const int grid_index = index.i / key->grid_area;
+ const int vertex_index = index.i - grid_index * key->grid_area;
CCGElem *elem = BKE_pbvh_get_grids(ss->pbvh)[grid_index];
copy_v3_v3(no, CCG_elem_no(key, CCG_elem_offset(key, elem, vertex_index)));
break;
@@ -208,80 +293,298 @@ void SCULPT_vertex_normal_get(SculptSession *ss, int index, float no[3])
}
}
-const float *SCULPT_vertex_persistent_co_get(SculptSession *ss, int index)
+const float *SCULPT_vertex_persistent_co_get(SculptSession *ss,
+ SculptVertRef index,
+ int cd_pers_co)
{
+ if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) {
+ if (cd_pers_co >= 0) {
+ BMVert *v = (BMVert *)index.i;
+ float *co = BM_ELEM_CD_GET_VOID_P(v, cd_pers_co);
+
+ return co;
+ }
+
+ return SCULPT_vertex_co_get(ss, index);
+ }
+
if (ss->persistent_base) {
- return ss->persistent_base[index].co;
+ int i = BKE_pbvh_vertex_index_to_table(ss->pbvh, index);
+
+ return ss->persistent_base[i].co;
}
+
return SCULPT_vertex_co_get(ss, index);
}
-const float *SCULPT_vertex_co_for_grab_active_get(SculptSession *ss, int index)
+const float *SCULPT_vertex_co_for_grab_active_get(SculptSession *ss, SculptVertRef vertex)
{
/* Always grab active shape key if the sculpt happens on shapekey. */
if (ss->shapekey_active) {
const MVert *mverts = BKE_pbvh_get_verts(ss->pbvh);
- return mverts[index].co;
+ return mverts[BKE_pbvh_vertex_index_to_table(ss->pbvh, vertex)].co;
}
/* Sculpting on the base mesh. */
if (ss->mvert) {
- return ss->mvert[index].co;
+ return ss->mvert[BKE_pbvh_vertex_index_to_table(ss->pbvh, vertex)].co;
}
/* Everything else, such as sculpting on multires. */
- return SCULPT_vertex_co_get(ss, index);
+ return SCULPT_vertex_co_get(ss, vertex);
}
-void SCULPT_vertex_limit_surface_get(SculptSession *ss, int index, float r_co[3])
+void SCULPT_vertex_limit_surface_get(SculptSession *ss, SculptVertRef vertex, float r_co[3])
{
- switch (BKE_pbvh_type(ss->pbvh)) {
- case PBVH_FACES:
- case PBVH_BMESH:
- if (ss->limit_surface) {
- copy_v3_v3(r_co, ss->limit_surface[index]);
- break;
- }
- copy_v3_v3(r_co, SCULPT_vertex_co_get(ss, index));
- break;
- case PBVH_GRIDS: {
- 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 = {.grid_index = grid_index,
- .x = vertex_index % key->grid_size,
- .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->limit_surface) {
+ float *f = SCULPT_temp_cdata_get(vertex, ss->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 = {.grid_index = grid_index,
+ .x = vertex_index % key->grid_size,
+ .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, int index, float no[3])
+void SCULPT_vertex_persistent_normal_get(SculptSession *ss,
+ SculptVertRef index,
+ float no[3],
+ int cd_pers_no)
{
+ if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) {
+ if (cd_pers_no >= 0) {
+ BMVert *v = (BMVert *)index.i;
+ float(*no2)[3] = BM_ELEM_CD_GET_VOID_P(v, cd_pers_no);
+
+ copy_v3_v3(no, (float *)no2);
+ return;
+ }
+
+ SCULPT_vertex_normal_get(ss, index, no);
+ return;
+ }
+
if (ss->persistent_base) {
- copy_v3_v3(no, ss->persistent_base[index].no);
+ copy_v3_v3(no, ss->persistent_base[BKE_pbvh_vertex_index_to_table(ss->pbvh, index)].no);
return;
}
SCULPT_vertex_normal_get(ss, index, no);
}
-float SCULPT_vertex_mask_get(SculptSession *ss, int index)
+static bool sculpt_temp_customlayer_get(SculptSession *ss,
+ AttributeDomain domain,
+ int proptype,
+ char *name,
+ SculptCustomLayer *out,
+ bool autocreate,
+ bool simple_array)
+{
+ if (simple_array) {
+ CustomData *cdata = NULL;
+ int totelem = 0;
+
+ if (ss->bm) {
+ switch (domain) {
+ case ATTR_DOMAIN_POINT:
+ cdata = &ss->bm->vdata;
+ totelem = ss->bm->totvert;
+ break;
+ case ATTR_DOMAIN_FACE:
+ cdata = &ss->bm->pdata;
+ totelem = ss->bm->totface;
+ break;
+ default:
+ return false;
+ }
+ }
+ else {
+ switch (domain) {
+ case ATTR_DOMAIN_POINT:
+ cdata = ss->vdata;
+ totelem = ss->bm->totvert;
+ break;
+ case ATTR_DOMAIN_FACE:
+ cdata = ss->pdata;
+ totelem = ss->bm->totface;
+ break;
+ default:
+ return false;
+ }
+ }
+
+ if (!cdata) {
+ printf("error in %s\n", __func__);
+ return false;
+ }
+
+ CustomData dummy = {0};
+ CustomData_reset(&dummy);
+ CustomData_add_layer(&dummy, proptype, CD_ASSIGN, NULL, 0);
+ int elemsize = (int)CustomData_get_elem_size(dummy.layers);
+
+ CustomData_free(&dummy, 0);
+
+ out->data = MEM_calloc_arrayN(totelem, elemsize, name);
+
+ out->is_cdlayer = false;
+ out->from_bmesh = ss->bm != NULL;
+ out->cd_offset = -1;
+ out->layer = NULL;
+
+ return true;
+ }
+
+ switch (BKE_pbvh_type(ss->pbvh)) {
+ case PBVH_BMESH: {
+ CustomData *cdata = NULL;
+ out->from_bmesh = true;
+
+ if (!ss->bm) {
+ return false;
+ }
+
+ switch (domain) {
+ case ATTR_DOMAIN_POINT:
+ cdata = &ss->bm->vdata;
+ break;
+ case ATTR_DOMAIN_FACE:
+ cdata = &ss->bm->pdata;
+ break;
+ default:
+ return false;
+ }
+
+ int idx = CustomData_get_named_layer_index(cdata, proptype, name);
+
+ if (idx < 0) {
+ if (!autocreate) {
+ return false;
+ }
+
+ BM_data_layer_add_named(ss->bm, cdata, proptype, name);
+ idx = CustomData_get_named_layer_index(cdata, proptype, name);
+ cdata->layers[idx].flag |= CD_FLAG_TEMPORARY;
+ SCULPT_dyntopo_node_layers_update_offsets(ss);
+ }
+
+ out->data = NULL;
+ out->is_cdlayer = true;
+ out->layer = cdata->layers + idx;
+ out->cd_offset = out->layer->offset;
+ out->elemsize = CustomData_get_elem_size(out->layer);
+
+ break;
+ }
+ case PBVH_FACES: {
+ CustomData *cdata = NULL;
+ int totelem = 0;
+
+ out->from_bmesh = false;
+
+ switch (domain) {
+ case ATTR_DOMAIN_POINT:
+ totelem = ss->totvert;
+ cdata = ss->vdata;
+ break;
+ case ATTR_DOMAIN_FACE:
+ totelem = ss->totfaces;
+ cdata = ss->pdata;
+ break;
+ default:
+ return false;
+ }
+
+ int idx = CustomData_get_named_layer_index(cdata, proptype, name);
+
+ if (idx < 0) {
+ if (!autocreate) {
+ return false;
+ }
+
+ CustomData_add_layer_named(cdata, proptype, CD_CALLOC, NULL, totelem, name);
+ idx = CustomData_get_named_layer_index(cdata, proptype, name);
+
+ cdata->layers[idx].flag |= CD_FLAG_TEMPORARY;
+ }
+
+ out->data = NULL;
+ out->is_cdlayer = true;
+ out->layer = cdata->layers + idx;
+ out->cd_offset = -1;
+ out->data = out->layer->data;
+ out->elemsize = CustomData_get_elem_size(out->layer);
+ break;
+ }
+ case PBVH_GRIDS: {
+ CustomData *cdata = NULL;
+ int totelem = 0;
+
+ out->from_bmesh = false;
+
+ switch (domain) {
+ case ATTR_DOMAIN_POINT:
+ totelem = BKE_pbvh_get_grid_num_vertices(ss->pbvh);
+ cdata = &ss->temp_vdata;
+ break;
+ case ATTR_DOMAIN_FACE:
+ // not supported
+ return false;
+ default:
+ return false;
+ }
+
+ int idx = CustomData_get_named_layer_index(cdata, proptype, name);
+
+ if (idx < 0) {
+ if (!autocreate) {
+ return false;
+ }
+
+ CustomData_add_layer_named(cdata, proptype, CD_CALLOC, NULL, totelem, name);
+ idx = CustomData_get_named_layer_index(cdata, proptype, name);
+ }
+
+ out->data = NULL;
+ out->is_cdlayer = true;
+ out->layer = cdata->layers + idx;
+ out->cd_offset = -1;
+ out->data = out->layer->data;
+ out->elemsize = CustomData_get_elem_size(out->layer);
+
+ break;
+ }
+ }
+
+ return true;
+}
+
+float SCULPT_vertex_mask_get(SculptSession *ss, SculptVertRef index)
{
BMVert *v;
float *mask;
switch (BKE_pbvh_type(ss->pbvh)) {
case PBVH_FACES:
- return ss->vmask[index];
+ return ss->vmask[index.i];
case PBVH_BMESH:
- v = BM_vert_at_index(BKE_pbvh_get_bmesh(ss->pbvh), index);
+ v = (BMVert *)index.i;
mask = BM_ELEM_CD_GET_VOID_P(v, CustomData_get_offset(&ss->bm->vdata, CD_PAINT_MASK));
return *mask;
case PBVH_GRIDS: {
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;
+ const int grid_index = index.i / key->grid_area;
+ const int vertex_index = index.i - grid_index * key->grid_area;
CCGElem *elem = BKE_pbvh_get_grids(ss->pbvh)[grid_index];
return *CCG_elem_mask(key, CCG_elem_offset(key, elem, vertex_index));
}
@@ -290,12 +593,67 @@ float SCULPT_vertex_mask_get(SculptSession *ss, int index)
return 0.0f;
}
-int SCULPT_active_vertex_get(SculptSession *ss)
+bool SCULPT_temp_customlayer_ensure(
+ SculptSession *ss, AttributeDomain domain, int proptype, char *name, bool simple_array)
+{
+ SculptCustomLayer scl;
+ return sculpt_temp_customlayer_get(ss, domain, proptype, name, &scl, true, simple_array);
+}
+
+bool SCULPT_temp_customlayer_release(SculptSession *ss,
+ AttributeDomain domain,
+ int proptype,
+ SculptCustomLayer *scl)
+{
+ if (!ss->bm) {
+ // for now, don't clean up bmesh temp layers
+ if (scl->is_cdlayer && BKE_pbvh_type(ss->pbvh) != PBVH_GRIDS) {
+ CustomData *cdata = NULL;
+ int totelem = 0;
+
+ switch (domain) {
+ case ATTR_DOMAIN_POINT:
+ cdata = ss->vdata;
+ totelem = ss->totvert;
+ break;
+ case ATTR_DOMAIN_FACE:
+ cdata = ss->pdata;
+ totelem = ss->totfaces;
+ break;
+ }
+
+ if (!cdata) {
+ printf("error, unknown domain in %s\n", __func__);
+ return false;
+ }
+
+ CustomData_free_layer(cdata, scl->layer->type, totelem, scl->layer - cdata->layers);
+ }
+ else {
+ MEM_SAFE_FREE(scl->data);
+ }
+
+ scl->data = NULL;
+ }
+ return true;
+}
+
+bool SCULPT_temp_customlayer_get(SculptSession *ss,
+ AttributeDomain domain,
+ int proptype,
+ char *name,
+ SculptCustomLayer *scl,
+ bool simple_array)
+{
+ return sculpt_temp_customlayer_get(ss, domain, proptype, name, scl, true, simple_array);
+}
+
+SculptVertRef SCULPT_active_vertex_get(SculptSession *ss)
{
if (ELEM(BKE_pbvh_type(ss->pbvh), PBVH_FACES, PBVH_BMESH, PBVH_GRIDS)) {
return ss->active_vertex_index;
}
- return 0;
+ return BKE_pbvh_make_vref(0);
}
const float *SCULPT_active_vertex_co_get(SculptSession *ss)
@@ -348,44 +706,49 @@ int SCULPT_active_face_set_get(SculptSession *ss)
{
switch (BKE_pbvh_type(ss->pbvh)) {
case PBVH_FACES:
- return ss->face_sets[ss->active_face_index];
+ return ss->face_sets[ss->active_face_index.i];
case PBVH_GRIDS: {
const int face_index = BKE_subdiv_ccg_grid_to_face_index(ss->subdiv_ccg,
ss->active_grid_index);
return ss->face_sets[face_index];
}
case PBVH_BMESH:
+ if (ss->cd_faceset_offset && ss->active_face_index.i) {
+ BMFace *f = (BMFace *)ss->active_face_index.i;
+ return BM_ELEM_CD_GET_INT(f, ss->cd_faceset_offset);
+ }
+
return SCULPT_FACE_SET_NONE;
}
return SCULPT_FACE_SET_NONE;
}
-void SCULPT_vertex_visible_set(SculptSession *ss, int index, bool visible)
+void SCULPT_vertex_visible_set(SculptSession *ss, SculptVertRef index, bool visible)
{
switch (BKE_pbvh_type(ss->pbvh)) {
case PBVH_FACES:
- SET_FLAG_FROM_TEST(ss->mvert[index].flag, !visible, ME_HIDE);
- ss->mvert[index].flag |= ME_VERT_PBVH_UPDATE;
+ SET_FLAG_FROM_TEST(ss->mvert[index.i].flag, !visible, ME_HIDE);
+ ss->mvert[index.i].flag |= ME_VERT_PBVH_UPDATE;
break;
case PBVH_BMESH:
- BM_elem_flag_set(BM_vert_at_index(ss->bm, index), BM_ELEM_HIDDEN, !visible);
+ BM_elem_flag_set((BMVert *)index.i, BM_ELEM_HIDDEN, !visible);
break;
case PBVH_GRIDS:
break;
}
}
-bool SCULPT_vertex_visible_get(SculptSession *ss, int index)
+bool SCULPT_vertex_visible_get(SculptSession *ss, SculptVertRef index)
{
switch (BKE_pbvh_type(ss->pbvh)) {
case PBVH_FACES:
- return !(ss->mvert[index].flag & ME_HIDE);
+ return !(ss->mvert[index.i].flag & ME_HIDE);
case PBVH_BMESH:
- return !BM_elem_flag_test(BM_vert_at_index(ss->bm, index), BM_ELEM_HIDDEN);
+ return !BM_elem_flag_test(((BMVert *)index.i), BM_ELEM_HIDDEN);
case PBVH_GRIDS: {
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;
+ const int grid_index = index.i / key->grid_area;
+ const int vertex_index = index.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);
@@ -412,8 +775,28 @@ void SCULPT_face_set_visibility_set(SculptSession *ss, int face_set, bool visibl
}
}
break;
- case PBVH_BMESH:
+ case PBVH_BMESH: {
+ BMIter iter;
+ BMFace *f;
+
+ BM_ITER_MESH (f, &iter, ss->bm, BM_FACES_OF_MESH) {
+ int fset = BM_ELEM_CD_GET_INT(f, ss->cd_faceset_offset);
+
+ if (abs(fset) != face_set) {
+ continue;
+ }
+
+ if (visible) {
+ fset = abs(fset);
+ }
+ else {
+ fset = -abs(fset);
+ }
+
+ BM_ELEM_CD_SET_INT(f, ss->cd_faceset_offset, fset);
+ }
break;
+ }
}
}
@@ -426,8 +809,19 @@ void SCULPT_face_sets_visibility_invert(SculptSession *ss)
ss->face_sets[i] *= -1;
}
break;
- case PBVH_BMESH:
+ case PBVH_BMESH: {
+ BMIter iter;
+ BMFace *f;
+
+ BM_ITER_MESH (f, &iter, ss->bm, BM_FACES_OF_MESH) {
+ int fset = BM_ELEM_CD_GET_INT(f, ss->cd_faceset_offset);
+
+ fset = -fset;
+
+ BM_ELEM_CD_SET_INT(f, ss->cd_faceset_offset, fset);
+ }
break;
+ }
}
}
@@ -453,48 +847,108 @@ void SCULPT_face_sets_visibility_all_set(SculptSession *ss, bool visible)
}
}
break;
- case PBVH_BMESH:
+ case PBVH_BMESH: {
+ BMIter iter;
+ BMFace *f;
+
+ if (!ss->bm) {
+ return;
+ }
+
+ // paranoia check of cd_faceset_offset
+ if (ss->cd_faceset_offset < 0) {
+ ss->cd_faceset_offset = CustomData_get_offset(&ss->bm->pdata, CD_SCULPT_FACE_SETS);
+ }
+ if (ss->cd_faceset_offset < 0) {
+ return;
+ }
+
+ BM_ITER_MESH (f, &iter, ss->bm, BM_FACES_OF_MESH) {
+ int fset = BM_ELEM_CD_GET_INT(f, ss->cd_faceset_offset);
+
+ /* This can run on geometry without a face set assigned, so its ID sign can't be changed to
+ * modify the visibility. Force that geometry to the ID 1 to enable changing the visibility
+ * here. */
+
+ if (fset == SCULPT_FACE_SET_NONE) {
+ fset = 1;
+ }
+
+ if (visible) {
+ fset = abs(fset);
+ }
+ else {
+ fset = -abs(fset);
+ }
+
+ BM_ELEM_CD_SET_INT(f, ss->cd_faceset_offset, fset);
+ }
break;
+ }
}
}
-bool SCULPT_vertex_any_face_set_visible_get(SculptSession *ss, int index)
+bool SCULPT_vertex_any_face_set_visible_get(SculptSession *ss, SculptVertRef index)
{
switch (BKE_pbvh_type(ss->pbvh)) {
case PBVH_FACES: {
- MeshElemMap *vert_map = &ss->pmap[index];
- for (int j = 0; j < ss->pmap[index].count; j++) {
+ MeshElemMap *vert_map = &ss->pmap[index.i];
+ for (int j = 0; j < ss->pmap[index.i].count; j++) {
if (ss->face_sets[vert_map->indices[j]] > 0) {
return true;
}
}
return false;
}
- case PBVH_BMESH:
- return true;
+ case PBVH_BMESH: {
+ BMIter iter;
+ BMLoop *l;
+ BMVert *v = (BMVert *)index.i;
+
+ BM_ITER_ELEM (l, &iter, v, BM_LOOPS_OF_VERT) {
+ int fset = BM_ELEM_CD_GET_INT(l->f, ss->cd_faceset_offset);
+ if (fset >= 0) {
+ return true;
+ }
+ }
+
+ return false;
+ }
case PBVH_GRIDS:
return true;
}
return true;
}
-bool SCULPT_vertex_all_face_sets_visible_get(const SculptSession *ss, int index)
+bool SCULPT_vertex_all_face_sets_visible_get(const SculptSession *ss, SculptVertRef index)
{
switch (BKE_pbvh_type(ss->pbvh)) {
case PBVH_FACES: {
- MeshElemMap *vert_map = &ss->pmap[index];
- for (int j = 0; j < ss->pmap[index].count; j++) {
+ MeshElemMap *vert_map = &ss->pmap[index.i];
+ for (int j = 0; j < ss->pmap[index.i].count; j++) {
if (ss->face_sets[vert_map->indices[j]] < 0) {
return false;
}
}
return true;
}
- case PBVH_BMESH:
+ case PBVH_BMESH: {
+ BMIter iter;
+ BMLoop *l;
+ BMVert *v = (BMVert *)index.i;
+
+ BM_ITER_ELEM (l, &iter, v, BM_LOOPS_OF_VERT) {
+ int fset = BM_ELEM_CD_GET_INT(l->f, ss->cd_faceset_offset);
+ if (fset < 0) {
+ return false;
+ }
+ }
+
return true;
+ }
case PBVH_GRIDS: {
const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh);
- const int grid_index = index / key->grid_area;
+ const int grid_index = index.i / key->grid_area;
const int face_index = BKE_subdiv_ccg_grid_to_face_index(ss->subdiv_ccg, grid_index);
return ss->face_sets[face_index] > 0;
}
@@ -502,22 +956,37 @@ bool SCULPT_vertex_all_face_sets_visible_get(const SculptSession *ss, int index)
return true;
}
-void SCULPT_vertex_face_set_set(SculptSession *ss, int index, int face_set)
+void SCULPT_vertex_face_set_set(SculptSession *ss, SculptVertRef index, int face_set)
{
switch (BKE_pbvh_type(ss->pbvh)) {
case PBVH_FACES: {
- MeshElemMap *vert_map = &ss->pmap[index];
- for (int j = 0; j < ss->pmap[index].count; j++) {
+ MeshElemMap *vert_map = &ss->pmap[index.i];
+ for (int j = 0; j < ss->pmap[index.i].count; j++) {
if (ss->face_sets[vert_map->indices[j]] > 0) {
ss->face_sets[vert_map->indices[j]] = abs(face_set);
}
}
} break;
- case PBVH_BMESH:
+ case PBVH_BMESH: {
+ BMIter iter;
+ BMLoop *l;
+ BMVert *v = (BMVert *)index.i;
+
+ BM_ITER_ELEM (l, &iter, v, BM_LOOPS_OF_VERT) {
+ int fset = BM_ELEM_CD_GET_INT(l->f, ss->cd_faceset_offset);
+ if (fset >= 0 && fset != abs(face_set)) {
+ MDynTopoVert *mv = BKE_PBVH_DYNVERT(ss->cd_dyn_vert, v);
+
+ mv->flag |= DYNVERT_NEED_BOUNDARY;
+ BM_ELEM_CD_SET_INT(l->f, ss->cd_faceset_offset, abs(face_set));
+ }
+ }
+
break;
+ }
case PBVH_GRIDS: {
const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh);
- const int grid_index = index / key->grid_area;
+ const int grid_index = index.i / 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] = abs(face_set);
@@ -527,10 +996,11 @@ void SCULPT_vertex_face_set_set(SculptSession *ss, int index, int face_set)
}
}
-void SCULPT_vertex_face_set_increase(SculptSession *ss, int index, const int increase)
+void SCULPT_vertex_face_set_increase(SculptSession *ss, SculptVertRef vertex, const int increase)
{
switch (BKE_pbvh_type(ss->pbvh)) {
case PBVH_FACES: {
+ int index = (int)vertex.i;
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) {
@@ -538,9 +1008,26 @@ void SCULPT_vertex_face_set_increase(SculptSession *ss, int index, const int inc
}
}
} break;
- case PBVH_BMESH:
+ 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);
@@ -552,24 +1039,39 @@ void SCULPT_vertex_face_set_increase(SculptSession *ss, int index, const int inc
}
}
-int SCULPT_vertex_face_set_get(SculptSession *ss, int index)
+int SCULPT_vertex_face_set_get(SculptSession *ss, SculptVertRef index)
{
switch (BKE_pbvh_type(ss->pbvh)) {
case PBVH_FACES: {
- MeshElemMap *vert_map = &ss->pmap[index];
+ MeshElemMap *vert_map = &ss->pmap[index.i];
int face_set = 0;
- for (int i = 0; i < ss->pmap[index].count; i++) {
+ for (int i = 0; i < ss->pmap[index.i].count; i++) {
if (ss->face_sets[vert_map->indices[i]] > face_set) {
face_set = abs(ss->face_sets[vert_map->indices[i]]);
}
}
return face_set;
}
- case PBVH_BMESH:
- return 0;
+ case PBVH_BMESH: {
+ BMIter iter;
+ BMLoop *l;
+ BMVert *v = (BMVert *)index.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: {
const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh);
- const int grid_index = index / key->grid_area;
+ const int grid_index = index.i / key->grid_area;
const int face_index = BKE_subdiv_ccg_grid_to_face_index(ss->subdiv_ccg, grid_index);
return ss->face_sets[face_index];
}
@@ -577,23 +1079,40 @@ int SCULPT_vertex_face_set_get(SculptSession *ss, int index)
return 0;
}
-bool SCULPT_vertex_has_face_set(SculptSession *ss, int index, int face_set)
+bool SCULPT_vertex_has_face_set(SculptSession *ss, SculptVertRef index, int face_set)
{
switch (BKE_pbvh_type(ss->pbvh)) {
case PBVH_FACES: {
- MeshElemMap *vert_map = &ss->pmap[index];
- for (int i = 0; i < ss->pmap[index].count; i++) {
+ MeshElemMap *vert_map = &ss->pmap[index.i];
+ for (int i = 0; i < ss->pmap[index.i].count; i++) {
if (ss->face_sets[vert_map->indices[i]] == face_set) {
return true;
}
}
return false;
}
- case PBVH_BMESH:
- return true;
+ case PBVH_BMESH: {
+ BMIter iter;
+ BMLoop *l;
+ BMVert *v = (BMVert *)index.i;
+
+ if (ss->cd_faceset_offset == -1) {
+ return false;
+ }
+
+ BM_ITER_ELEM (l, &iter, v, BM_LOOPS_OF_VERT) {
+ BMFace *f = l->f;
+
+ if (abs(BM_ELEM_CD_GET_INT(f, ss->cd_faceset_offset)) == abs(face_set)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
case PBVH_GRIDS: {
const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh);
- const int grid_index = index / key->grid_area;
+ const int grid_index = index.i / key->grid_area;
const int face_index = BKE_subdiv_ccg_grid_to_face_index(ss->subdiv_ccg, grid_index);
return ss->face_sets[face_index] == face_set;
}
@@ -601,6 +1120,54 @@ bool SCULPT_vertex_has_face_set(SculptSession *ss, int index, int face_set)
return true;
}
+/*
+calcs visibility state based on face sets.
+todo: also calc a face set boundary flag.
+*/
+void sculpt_vertex_faceset_update_bmesh(SculptSession *ss, SculptVertRef vert)
+{
+ if (!ss->bm) {
+ return;
+ }
+
+ BMVert *v = (BMVert *)vert.i;
+ BMEdge *e = v->e;
+ bool ok = false;
+ const int cd_faceset_offset = ss->cd_faceset_offset;
+
+ if (!e) {
+ return;
+ }
+
+ do {
+ BMLoop *l = e->l;
+ if (l) {
+ do {
+ if (BM_ELEM_CD_GET_INT(l->f, cd_faceset_offset) > 0) {
+ ok = true;
+ break;
+ }
+
+ l = l->radial_next;
+ } while (l != e->l);
+
+ if (ok) {
+ break;
+ }
+ }
+ e = v == e->v1 ? e->v1_disk_link.next : e->v2_disk_link.next;
+ } while (e != v->e);
+
+ MDynTopoVert *mv = BM_ELEM_CD_GET_VOID_P(v, ss->cd_dyn_vert);
+
+ if (ok) {
+ mv->flag &= ~DYNVERT_VERT_FSET_HIDDEN;
+ }
+ else {
+ mv->flag |= DYNVERT_VERT_FSET_HIDDEN;
+ }
+}
+
void SCULPT_visibility_sync_all_face_sets_to_vertices(Object *ob)
{
SculptSession *ss = ob->sculpt;
@@ -615,16 +1182,58 @@ void SCULPT_visibility_sync_all_face_sets_to_vertices(Object *ob)
BKE_sculpt_sync_face_sets_visibility_to_grids(mesh, ss->subdiv_ccg);
break;
}
- case PBVH_BMESH:
+ case PBVH_BMESH: {
+ BMIter iter;
+ BMFace *f;
+ BMVert *v;
+
+ 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 < 0) {
+ BM_elem_flag_enable(f, BM_ELEM_HIDDEN);
+ }
+ else {
+ BM_elem_flag_disable(f, BM_ELEM_HIDDEN);
+ }
+ }
+
+ BM_ITER_MESH (v, &iter, ss->bm, BM_VERTS_OF_MESH) {
+ MDynTopoVert *mv = BM_ELEM_CD_GET_VOID_P(v, ss->cd_dyn_vert);
+
+ BMIter iter2;
+ BMLoop *l;
+
+ int visible = false;
+
+ BM_ITER_ELEM (l, &iter2, v, BM_LOOPS_OF_VERT) {
+ if (!BM_elem_flag_test(l->f, BM_ELEM_HIDDEN)) {
+ visible = true;
+ break;
+ }
+ }
+
+ if (!visible) {
+ mv->flag |= DYNVERT_VERT_FSET_HIDDEN;
+ BM_elem_flag_enable(v, BM_ELEM_HIDDEN);
+ }
+ else {
+ mv->flag &= ~DYNVERT_VERT_FSET_HIDDEN;
+ BM_elem_flag_disable(v, BM_ELEM_HIDDEN);
+ }
+ }
break;
+ }
}
}
static void UNUSED_FUNCTION(sculpt_visibility_sync_vertex_to_face_sets)(SculptSession *ss,
- int index)
+ SculptVertRef vertex)
{
+ int index = (int)vertex.i;
MeshElemMap *vert_map = &ss->pmap[index];
- const bool visible = SCULPT_vertex_visible_get(ss, index);
+ const bool visible = SCULPT_vertex_visible_get(ss, vertex);
+
for (int i = 0; i < ss->pmap[index].count; i++) {
if (visible) {
ss->face_sets[vert_map->indices[i]] = abs(ss->face_sets[vert_map->indices[i]]);
@@ -638,28 +1247,110 @@ static void UNUSED_FUNCTION(sculpt_visibility_sync_vertex_to_face_sets)(SculptSe
void SCULPT_visibility_sync_all_vertex_to_face_sets(SculptSession *ss)
{
- if (BKE_pbvh_type(ss->pbvh) == PBVH_FACES) {
- for (int i = 0; i < ss->totfaces; i++) {
- MPoly *poly = &ss->mpoly[i];
- bool poly_visible = true;
- for (int l = 0; l < poly->totloop; l++) {
- MLoop *loop = &ss->mloop[poly->loopstart + l];
- if (!SCULPT_vertex_visible_get(ss, (int)loop->v)) {
- poly_visible = false;
+ switch (BKE_pbvh_type(ss->pbvh)) {
+ case PBVH_FACES: {
+ for (int i = 0; i < ss->totfaces; i++) {
+ MPoly *poly = &ss->mpoly[i];
+ bool poly_visible = true;
+ for (int l = 0; l < poly->totloop; l++) {
+ MLoop *loop = &ss->mloop[poly->loopstart + l];
+ if (!SCULPT_vertex_visible_get(ss, BKE_pbvh_make_vref(loop->v))) {
+ poly_visible = false;
+ }
+ }
+ if (poly_visible) {
+ ss->face_sets[i] = abs(ss->face_sets[i]);
+ }
+ else {
+ ss->face_sets[i] = -abs(ss->face_sets[i]);
}
}
- if (poly_visible) {
- ss->face_sets[i] = abs(ss->face_sets[i]);
+ break;
+ }
+ case PBVH_GRIDS:
+ break;
+ case PBVH_BMESH: {
+ BMIter iter;
+ BMFace *f;
+
+ if (!ss->bm) {
+ return;
}
- else {
- ss->face_sets[i] = -abs(ss->face_sets[i]);
+
+ BM_ITER_MESH (f, &iter, ss->bm, BM_FACES_OF_MESH) {
+ BMLoop *l = f->l_first;
+ bool visible = true;
+
+ do {
+ if (BM_elem_flag_test(l->v, BM_ELEM_HIDDEN)) {
+ visible = false;
+ break;
+ }
+ l = l->next;
+ } while (l != f->l_first);
+
+ int fset = BM_ELEM_CD_GET_INT(f, ss->cd_faceset_offset);
+ if (visible) {
+ fset = abs(fset);
+ }
+ else {
+ fset = -abs(fset);
+ }
+
+ BM_ELEM_CD_SET_INT(f, ss->cd_faceset_offset, fset);
+ }
+
+ break;
+ }
+ }
+}
+
+static SculptCornerType sculpt_check_corner_in_base_mesh(const SculptSession *ss,
+ SculptVertRef vertex,
+ bool check_facesets)
+{
+ int index = BKE_pbvh_vertex_index_to_table(ss->pbvh, vertex);
+
+ MeshElemMap *vert_map = &ss->pmap[index];
+ int face_set = -1;
+ int last1 = -1;
+ int last2 = -1;
+
+ int ret = 0;
+
+ if (sculpt_check_boundary_vertex_in_base_mesh(ss, vertex) && ss->pmap[index].count < 4) {
+ ret |= SCULPT_CORNER_MESH;
+ }
+
+ if (check_facesets) {
+ for (int i = 0; i < ss->pmap[index].count; i++) {
+ if (check_facesets) {
+ if (last2 != last1) {
+ last2 = last1;
+ }
+ if (last1 != face_set) {
+ last1 = face_set;
+ }
+ face_set = abs(ss->face_sets[vert_map->indices[i]]);
+
+ bool corner = last1 != -1 && last2 != -1 && face_set != -1;
+ corner = corner && last1 != last2 && last1 != face_set;
+
+ if (corner) {
+ ret |= SCULPT_CORNER_FACE_SET;
+ }
}
}
}
+
+ return ret;
}
-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,
+ SculptVertRef vertex)
{
+ int index = BKE_pbvh_vertex_index_to_table(ss->pbvh, vertex);
+
MeshElemMap *vert_map = &ss->pmap[index];
int face_set = -1;
for (int i = 0; i < ss->pmap[index].count; i++) {
@@ -679,7 +1370,9 @@ static bool sculpt_check_unique_face_set_in_base_mesh(SculptSession *ss, int ind
* 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)
+static bool sculpt_check_unique_face_set_for_edge_in_base_mesh(const SculptSession *ss,
+ int v1,
+ int v2)
{
MeshElemMap *vert_map = &ss->pmap[v1];
int p1 = -1, p2 = -1;
@@ -707,18 +1400,50 @@ 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, int index)
+bool SCULPT_vertex_has_unique_face_set(const SculptSession *ss, SculptVertRef index)
{
switch (BKE_pbvh_type(ss->pbvh)) {
case PBVH_FACES: {
return sculpt_check_unique_face_set_in_base_mesh(ss, index);
}
- case PBVH_BMESH:
+ case PBVH_BMESH: {
+ BMIter iter;
+ BMLoop *l;
+ BMVert *v = (BMVert *)index.i;
+ MDynTopoVert *mv = BKE_PBVH_DYNVERT(ss->cd_dyn_vert, v);
+
+ if (mv->flag & DYNVERT_NEED_BOUNDARY) {
+ BKE_pbvh_update_vert_boundary(
+ ss->cd_dyn_vert, ss->cd_faceset_offset, v, ss->boundary_symmetry);
+ }
+
+ return !(mv->flag & DYNVERT_FSET_BOUNDARY);
+
+#if 0
+ int face_set = 0;
+ bool first = true;
+ if (ss->cd_faceset_offset == -1) {
+ return false;
+ }
+
+ BM_ITER_ELEM (l, &iter, v, BM_LOOPS_OF_VERT) {
+ BMFace *f = l->f;
+ int face_set2 = BM_ELEM_CD_GET_INT(f, ss->cd_faceset_offset);
+
+ if (!first && abs(face_set2) != abs(face_set)) {
+ return false;
+ }
+
+ first = false;
+ face_set = face_set2;
+ }
return true;
+#endif
+ }
case PBVH_GRIDS: {
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;
+ const int grid_index = index.i / key->grid_area;
+ const int vertex_index = index.i - grid_index * key->grid_area;
const SubdivCCGCoord coord = {.grid_index = grid_index,
.x = vertex_index % key->grid_size,
.y = vertex_index / key->grid_size};
@@ -727,7 +1452,7 @@ bool SCULPT_vertex_has_unique_face_set(SculptSession *ss, int index)
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);
+ return sculpt_check_unique_face_set_in_base_mesh(ss, BKE_pbvh_make_vref(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:
@@ -752,8 +1477,24 @@ 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 = abs(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;
}
@@ -762,10 +1503,13 @@ int SCULPT_face_set_next_available_get(SculptSession *ss)
#define SCULPT_VERTEX_NEIGHBOR_FIXED_CAPACITY 256
-static void sculpt_vertex_neighbor_add(SculptVertexNeighborIter *iter, int neighbor_index)
+static void sculpt_vertex_neighbor_add(SculptVertexNeighborIter *iter,
+ SculptVertRef neighbor,
+ SculptEdgeRef edge,
+ int neighbor_index)
{
for (int i = 0; i < iter->size; i++) {
- if (iter->neighbors[i] == neighbor_index) {
+ if (iter->neighbors[i].vertex.i == neighbor.i) {
return;
}
}
@@ -774,51 +1518,123 @@ static void sculpt_vertex_neighbor_add(SculptVertexNeighborIter *iter, int neigh
iter->capacity += SCULPT_VERTEX_NEIGHBOR_FIXED_CAPACITY;
if (iter->neighbors == iter->neighbors_fixed) {
- iter->neighbors = MEM_mallocN(iter->capacity * sizeof(int), "neighbor array");
- memcpy(iter->neighbors, iter->neighbors_fixed, sizeof(int) * iter->size);
+ iter->neighbors = MEM_mallocN(iter->capacity * sizeof(struct _SculptNeighborRef),
+ "neighbor array");
+ iter->neighbor_indices = MEM_mallocN(iter->capacity * sizeof(int), "neighbor array");
+
+ memcpy(
+ iter->neighbors, iter->neighbors_fixed, sizeof(struct _SculptNeighborRef) * iter->size);
+ memcpy(iter->neighbor_indices, iter->neighbor_indices_fixed, sizeof(int) * iter->size);
+ }
+ else {
+ iter->neighbors = MEM_reallocN_id(
+ iter->neighbors, iter->capacity * sizeof(struct _SculptNeighborRef), "neighbor array");
+ iter->neighbor_indices = MEM_reallocN_id(
+ iter->neighbor_indices, iter->capacity * sizeof(int), "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_neighbor_add_nocheck(SculptVertexNeighborIter *iter,
+ SculptVertRef neighbor,
+ SculptEdgeRef edge,
+ int neighbor_index)
+{
+ if (iter->size >= iter->capacity) {
+ iter->capacity += SCULPT_VERTEX_NEIGHBOR_FIXED_CAPACITY;
+
+ if (iter->neighbors == iter->neighbors_fixed) {
+ iter->neighbors = MEM_mallocN(iter->capacity * sizeof(struct _SculptNeighborRef),
+ "neighbor array");
+ iter->neighbor_indices = MEM_mallocN(iter->capacity * sizeof(int), "neighbor array");
+
+ memcpy(
+ iter->neighbors, iter->neighbors_fixed, sizeof(struct _SculptNeighborRef) * iter->size);
+ memcpy(iter->neighbor_indices, iter->neighbor_indices_fixed, sizeof(int) * iter->size);
}
else {
iter->neighbors = MEM_reallocN_id(
- iter->neighbors, iter->capacity * sizeof(int), "neighbor array");
+ iter->neighbors, iter->capacity * sizeof(struct _SculptNeighborRef), "neighbor array");
+ iter->neighbor_indices = MEM_reallocN_id(
+ iter->neighbor_indices, iter->capacity * sizeof(int), "neighbor array");
}
}
- iter->neighbors[iter->size] = neighbor_index;
+ 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(SculptSession *ss,
- int index,
+static void sculpt_vertex_neighbors_get_bmesh(const SculptSession *ss,
+ SculptVertRef index,
SculptVertexNeighborIter *iter)
{
- BMVert *v = BM_vert_at_index(ss->bm, index);
- BMIter liter;
- BMLoop *l;
+ BMVert *v = (BMVert *)index.i;
+
+ iter->is_duplicate = false;
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->i = 0;
- 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 (BM_elem_index_get(v_other) != (int)index) {
- sculpt_vertex_neighbor_add(iter, 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 = NULL;
+
+ do {
+ BMVert *v2;
+ e2 = BM_DISK_EDGE_NEXT(e, v);
+ v2 = v == e->v1 ? e->v2 : e->v1;
+
+ MDynTopoVert *mv = BKE_PBVH_DYNVERT(ss->cd_dyn_vert, v2);
+
+ if (!(mv->flag & DYNVERT_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 != NULL);
+ 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(SCULPT_REF_NONE),
+ ss->fake_neighbors.fake_neighbor_index[index].i);
}
}
}
-static void sculpt_vertex_neighbors_get_faces(SculptSession *ss,
- int index,
+static void sculpt_vertex_neighbors_get_faces(const SculptSession *ss,
+ SculptVertRef vertex,
SculptVertexNeighborIter *iter)
{
+ int index = BKE_pbvh_vertex_index_to_table(ss->pbvh, vertex);
+
MeshElemMap *vert_map = &ss->pmap[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;
for (int i = 0; i < ss->pmap[index].count; i++) {
if (ss->face_sets[vert_map->indices[i]] < 0) {
@@ -829,8 +1645,21 @@ static void sculpt_vertex_neighbors_get_faces(SculptSession *ss,
uint f_adj_v[2];
if (poly_get_adj_loops_from_vert(p, ss->mloop, index, f_adj_v) != -1) {
for (int j = 0; j < ARRAY_SIZE(f_adj_v); j += 1) {
+ int e = 0;
+
if (f_adj_v[j] != index) {
- sculpt_vertex_neighbor_add(iter, f_adj_v[j]);
+ int loopidx = p->loopstart;
+
+ for (int k = 0; k < p->totloop; k++, loopidx++) {
+ const MEdge *e2 = &ss->medge[ss->mloop[loopidx].e];
+ if ((e2->v1 == index && e2->v2 == f_adj_v[j]) ||
+ (e2->v2 == index && e2->v1 == f_adj_v[j])) {
+ e = e2 - ss->medge;
+ }
+ }
+
+ sculpt_vertex_neighbor_add(
+ iter, BKE_pbvh_make_vref(f_adj_v[j]), BKE_pbvh_make_eref(e), f_adj_v[j]);
}
}
}
@@ -838,17 +1667,61 @@ static void sculpt_vertex_neighbors_get_faces(SculptSession *ss,
if (ss->fake_neighbors.use_fake_neighbors) {
BLI_assert(ss->fake_neighbors.fake_neighbor_index != NULL);
- if (ss->fake_neighbors.fake_neighbor_index[index] != FAKE_NEIGHBOR_NONE) {
- sculpt_vertex_neighbor_add(iter, ss->fake_neighbors.fake_neighbor_index[index]);
+ 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(SCULPT_REF_NONE),
+ ss->fake_neighbors.fake_neighbor_index[index].i);
+ }
+ }
+}
+
+static void sculpt_vertex_neighbors_get_faces_vemap(const SculptSession *ss,
+ SculptVertRef vertex,
+ SculptVertexNeighborIter *iter)
+{
+ int index = BKE_pbvh_vertex_index_to_table(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;
+
+ 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;
+
+ if (ss->face_sets[v] < 0) {
+ /* 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 != NULL);
+ 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(SCULPT_REF_NONE),
+ ss->fake_neighbors.fake_neighbor_index[index].i);
}
}
}
-static void sculpt_vertex_neighbors_get_grids(SculptSession *ss,
- const int index,
+static void sculpt_vertex_neighbors_get_grids(const SculptSession *ss,
+ const SculptVertRef 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. */
@@ -863,21 +1736,29 @@ 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;
for (int i = 0; i < neighbors.size; i++) {
- sculpt_vertex_neighbor_add(iter,
- 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(idx), BKE_pbvh_make_eref(SCULPT_REF_NONE), idx);
}
if (ss->fake_neighbors.use_fake_neighbors) {
BLI_assert(ss->fake_neighbors.fake_neighbor_index != NULL);
- if (ss->fake_neighbors.fake_neighbor_index[index] != FAKE_NEIGHBOR_NONE) {
- sculpt_vertex_neighbor_add(iter, ss->fake_neighbors.fake_neighbor_index[index]);
+ 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(SCULPT_REF_NONE),
+ ss->fake_neighbors.fake_neighbor_index[index].i);
}
}
@@ -886,45 +1767,286 @@ static void sculpt_vertex_neighbors_get_grids(SculptSession *ss,
}
}
-void SCULPT_vertex_neighbors_get(SculptSession *ss,
- const int index,
+void SCULPT_vertex_neighbors_get(const SculptSession *ss,
+ const SculptVertRef vertex,
const bool include_duplicates,
SculptVertexNeighborIter *iter)
{
switch (BKE_pbvh_type(ss->pbvh)) {
case PBVH_FACES:
- sculpt_vertex_neighbors_get_faces(ss, index, iter);
+ // use vemap if it exists, so result is in disk cycle order
+ if (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(ss, index, iter);
+ sculpt_vertex_neighbors_get_bmesh(ss, vertex, iter);
return;
case PBVH_GRIDS:
- sculpt_vertex_neighbors_get_grids(ss, index, include_duplicates, iter);
+ sculpt_vertex_neighbors_get_grids(ss, vertex, include_duplicates, iter);
return;
}
}
-static bool sculpt_check_boundary_vertex_in_base_mesh(const SculptSession *ss, const int index)
+SculptBoundaryType SCULPT_edge_is_boundary(const SculptSession *ss,
+ const SculptEdgeRef edge,
+ SculptBoundaryType typemask)
{
- BLI_assert(ss->vertex_info.boundary);
- return BLI_BITMAP_TEST(ss->vertex_info.boundary, index);
+
+ 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
+ MDynTopoVert *mv1 = BKE_PBVH_DYNVERT(ss->cd_dyn_vert, e->v1);
+ MDynTopoVert *mv2 = BKE_PBVH_DYNVERT(ss->cd_dyn_vert, e->v2);
+
+ return (mv1->flag & DYNVERT_FSET_BOUNDARY) && (mv2->flag & DYNVERT_FSET_BOUNDARY);
+ }
+ 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_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: {
+ int mask = typemask & (SCULPT_BOUNDARY_MESH | SCULPT_BOUNDARY_FACE_SET);
+ SculptVertRef v1, v2;
+
+ SCULPT_edge_get_verts(ss, edge, &v1, &v2);
+
+ if (mask) { // use less accurate approximation for now
+ SculptBoundaryType a = SCULPT_vertex_is_boundary(ss, v1, mask);
+ SculptBoundaryType b = SCULPT_vertex_is_boundary(ss, v2, mask);
+
+ ret |= a & b;
+ }
+
+ if (typemask & SCULPT_BOUNDARY_SHARP) {
+ ret |= ss->medge[edge.i].flag & ME_SHARP ? 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 (SculptBoundaryType)ret;
}
-bool SCULPT_vertex_is_boundary(const SculptSession *ss, const int index)
+void SCULPT_edge_get_verts(const SculptSession *ss,
+ const SculptEdgeRef edge,
+ SculptVertRef *r_v1,
+ SculptVertRef *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: {
- if (!SCULPT_vertex_all_face_sets_visible_get(ss, index)) {
- return true;
+ 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 = SCULPT_REF_NONE;
+ break;
+ }
+}
+
+SculptVertRef SCULPT_edge_other_vertex(const SculptSession *ss,
+ const SculptEdgeRef edge,
+ const SculptVertRef vertex)
+{
+ SculptVertRef v1, v2;
+
+ SCULPT_edge_get_verts(ss, edge, &v1, &v2);
+
+ return v1.i == vertex.i ? v2 : v1;
+}
+
+static bool sculpt_check_boundary_vertex_in_base_mesh(const SculptSession *ss,
+ const SculptVertRef index)
+{
+ BLI_assert(ss->vertex_info.boundary);
+ return BLI_BITMAP_TEST(ss->vertex_info.boundary,
+ BKE_pbvh_vertex_index_to_table(ss->pbvh, index));
+}
+
+SculptCornerType SCULPT_vertex_is_corner(const SculptSession *ss,
+ const SculptVertRef vertex,
+ SculptCornerType cornertype)
+{
+ bool check_facesets = cornertype & SCULPT_CORNER_FACE_SET;
+ SculptCornerType ret = 0;
+
+ switch (BKE_pbvh_type(ss->pbvh)) {
+ case PBVH_BMESH: {
+ BMVert *v = (BMVert *)vertex.i;
+ MDynTopoVert *mv = BKE_PBVH_DYNVERT(ss->cd_dyn_vert, v);
+
+ if (mv->flag & DYNVERT_NEED_BOUNDARY) {
+ BKE_pbvh_update_vert_boundary(
+ ss->cd_dyn_vert, ss->cd_faceset_offset, (BMVert *)vertex.i, ss->boundary_symmetry);
+ }
+
+ ret = 0;
+
+ if (cornertype & SCULPT_CORNER_MESH) {
+ ret |= (mv->flag & DYNVERT_CORNER) ? SCULPT_CORNER_MESH : 0;
+ }
+ if (cornertype & SCULPT_CORNER_FACE_SET) {
+ ret |= (mv->flag & DYNVERT_FSET_CORNER) ? SCULPT_CORNER_FACE_SET : 0;
+ }
+ if (cornertype & SCULPT_CORNER_SEAM) {
+ ret |= (mv->flag & DYNVERT_SEAM_CORNER) ? SCULPT_CORNER_SEAM : 0;
+ }
+ if (cornertype & SCULPT_CORNER_SHARP) {
+ ret |= (mv->flag & DYNVERT_SHARP_CORNER) ? SCULPT_CORNER_SHARP : 0;
+ }
+
+ break;
+ }
+ case PBVH_FACES:
+ if (ss->pmap) {
+ // sculpt_check_corner_face_set_in_base_mesh
+ ret = sculpt_check_corner_in_base_mesh(ss, vertex, check_facesets);
+ }
+ else {
+ // approximate
+ if (SCULPT_vertex_is_boundary(ss, vertex, SCULPT_BOUNDARY_MESH) &&
+ SCULPT_vertex_valence_get(ss, vertex) < 4) {
+ ret = SCULPT_CORNER_MESH;
+ }
+
+ // can't check face sets in this case
+ }
+ 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;
+ const SubdivCCGCoord coord = {.grid_index = grid_index,
+ .x = vertex_index % key->grid_size,
+ .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_corner_in_base_mesh(ss, BKE_pbvh_make_vref(v1), check_facesets);
+ case SUBDIV_CCG_ADJACENT_EDGE:
+ return false; // sculpt_check_unique_face_set_for_edge_in_base_mesh(ss, v1, v2);
+ case SUBDIV_CCG_ADJACENT_NONE:
+ return false;
+ break;
}
- return sculpt_check_boundary_vertex_in_base_mesh(ss, index);
}
+ }
+
+ return ret;
+}
+
+SculptBoundaryType SCULPT_vertex_is_boundary(const SculptSession *ss,
+ const SculptVertRef vertex,
+ SculptBoundaryType boundary_types)
+{
+ bool check_facesets = boundary_types & SCULPT_BOUNDARY_FACE_SET;
+
+ switch (BKE_pbvh_type(ss->pbvh)) {
case PBVH_BMESH: {
- BMVert *v = BM_vert_at_index(ss->bm, index);
- return BM_vert_is_boundary(v);
+ MDynTopoVert *mv = BKE_PBVH_DYNVERT(ss->cd_dyn_vert, ((BMVert *)(vertex.i)));
+
+ if (mv->flag & DYNVERT_NEED_BOUNDARY) {
+ BKE_pbvh_update_vert_boundary(
+ ss->cd_dyn_vert, ss->cd_faceset_offset, (BMVert *)vertex.i, ss->boundary_symmetry);
+ }
+
+ int flag = 0;
+ if (boundary_types & SCULPT_BOUNDARY_MESH) {
+ flag |= (mv->flag & DYNVERT_BOUNDARY) ? SCULPT_BOUNDARY_MESH : 0;
+ }
+ if (boundary_types & SCULPT_BOUNDARY_FACE_SET) {
+ flag |= (mv->flag & DYNVERT_FSET_BOUNDARY) ? SCULPT_BOUNDARY_FACE_SET : 0;
+ }
+ if (boundary_types & SCULPT_BOUNDARY_SHARP) {
+ flag |= (mv->flag & DYNVERT_SHARP_BOUNDARY) ? SCULPT_BOUNDARY_SHARP : 0;
+ }
+ if (boundary_types & SCULPT_BOUNDARY_SEAM) {
+ flag |= (mv->flag & DYNVERT_SEAM_BOUNDARY) ? SCULPT_BOUNDARY_SEAM : 0;
+ }
+
+ return flag;
+ }
+ case PBVH_FACES: {
+ int flag = 0;
+
+ if (!SCULPT_vertex_all_face_sets_visible_get(ss, vertex)) {
+ flag |= SCULPT_BOUNDARY_MESH;
+ }
+
+ if (check_facesets) {
+ bool ret = sculpt_check_boundary_vertex_in_base_mesh(ss, vertex);
+
+ if (ret) {
+ flag |= SCULPT_BOUNDARY_MESH;
+ }
+
+ if (!SCULPT_vertex_has_unique_face_set(ss, vertex)) {
+ flag |= SCULPT_BOUNDARY_FACE_SET;
+ }
+
+ return flag;
+ }
+ else if (sculpt_check_boundary_vertex_in_base_mesh(ss, vertex)) {
+ return SCULPT_BOUNDARY_MESH;
+ }
+
+ return 0;
}
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 vertex_index = index - grid_index * key->grid_area;
@@ -936,17 +2058,21 @@ bool SCULPT_vertex_is_boundary(const SculptSession *ss, const int index)
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);
+ return sculpt_check_boundary_vertex_in_base_mesh(ss, BKE_pbvh_make_vref(v1)) ?
+ SCULPT_BOUNDARY_MESH :
+ 0;
case SUBDIV_CCG_ADJACENT_EDGE:
- return sculpt_check_boundary_vertex_in_base_mesh(ss, v1) &&
- sculpt_check_boundary_vertex_in_base_mesh(ss, v2);
+ if (sculpt_check_boundary_vertex_in_base_mesh(ss, BKE_pbvh_make_vref(v1)) &&
+ sculpt_check_boundary_vertex_in_base_mesh(ss, BKE_pbvh_make_vref(v2))) {
+ return SCULPT_BOUNDARY_MESH;
+ }
case SUBDIV_CCG_ADJACENT_NONE:
- return false;
+ return 0;
}
}
}
- return false;
+ return 0;
}
/* Utilities */
@@ -965,8 +2091,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)
{
@@ -1003,6 +2129,7 @@ bool SCULPT_check_vertex_pivot_symmetry(const float vco[3], const float pco[3],
typedef struct NearestVertexTLSData {
int nearest_vertex_index;
+ SculptVertRef nearest_vertex;
float nearest_vertex_distance_squared;
} NearestVertexTLSData;
@@ -1020,6 +2147,7 @@ static void do_nearest_vertex_get_task_cb(void *__restrict userdata,
if (distance_squared < nvtd->nearest_vertex_distance_squared &&
distance_squared < data->max_distance_squared) {
nvtd->nearest_vertex_index = vd.index;
+ nvtd->nearest_vertex = vd.vertex;
nvtd->nearest_vertex_distance_squared = distance_squared;
}
}
@@ -1034,15 +2162,17 @@ static void nearest_vertex_get_reduce(const void *__restrict UNUSED(userdata),
NearestVertexTLSData *nvtd = chunk;
if (join->nearest_vertex_index == -1) {
join->nearest_vertex_index = nvtd->nearest_vertex_index;
+ join->nearest_vertex = nvtd->nearest_vertex;
join->nearest_vertex_distance_squared = nvtd->nearest_vertex_distance_squared;
}
else if (nvtd->nearest_vertex_distance_squared < join->nearest_vertex_distance_squared) {
join->nearest_vertex_index = nvtd->nearest_vertex_index;
+ join->nearest_vertex = nvtd->nearest_vertex;
join->nearest_vertex_distance_squared = nvtd->nearest_vertex_distance_squared;
}
}
-int SCULPT_nearest_vertex_get(
+SculptVertRef SCULPT_nearest_vertex_get(
Sculpt *sd, Object *ob, const float co[3], float max_distance, bool use_original)
{
SculptSession *ss = ob->sculpt;
@@ -1057,7 +2187,7 @@ int SCULPT_nearest_vertex_get(
};
BKE_pbvh_search_gather(ss->pbvh, SCULPT_search_sphere_cb, &data, &nodes, &totnode);
if (totnode == 0) {
- return -1;
+ return BKE_pbvh_make_vref(-1);
}
SculptThreadedTaskData task_data = {
@@ -1070,6 +2200,7 @@ int SCULPT_nearest_vertex_get(
copy_v3_v3(task_data.nearest_vertex_search_co, co);
NearestVertexTLSData nvtd;
nvtd.nearest_vertex_index = -1;
+ nvtd.nearest_vertex.i = -1;
nvtd.nearest_vertex_distance_squared = FLT_MAX;
TaskParallelSettings settings;
@@ -1081,7 +2212,7 @@ int SCULPT_nearest_vertex_get(
MEM_SAFE_FREE(nodes);
- return nvtd.nearest_vertex_index;
+ return nvtd.nearest_vertex;
}
bool SCULPT_is_symmetry_iteration_valid(char i, char symm)
@@ -1133,23 +2264,29 @@ void SCULPT_floodfill_init(SculptSession *ss, SculptFloodFill *flood)
int vertex_count = SCULPT_vertex_count_get(ss);
SCULPT_vertex_random_access_ensure(ss);
- flood->queue = BLI_gsqueue_new(sizeof(int));
+ flood->queue = BLI_gsqueue_new(sizeof(SculptVertRef));
flood->visited_vertices = BLI_BITMAP_NEW(vertex_count, "visited vertices");
}
-void SCULPT_floodfill_add_initial(SculptFloodFill *flood, int index)
+void SCULPT_floodfill_add_initial(SculptFloodFill *flood, SculptVertRef vertex)
{
- BLI_gsqueue_push(flood->queue, &index);
+ BLI_gsqueue_push(flood->queue, &vertex);
}
-void SCULPT_floodfill_add_and_skip_initial(SculptFloodFill *flood, int index)
+void SCULPT_floodfill_add_and_skip_initial(SculptSession *ss,
+ SculptFloodFill *flood,
+ SculptVertRef vertex)
{
- BLI_gsqueue_push(flood->queue, &index);
- BLI_BITMAP_ENABLE(flood->visited_vertices, index);
+ BLI_gsqueue_push(flood->queue, &vertex);
+ BLI_BITMAP_ENABLE(flood->visited_vertices, BKE_pbvh_vertex_index_to_table(ss->pbvh, vertex));
}
-void SCULPT_floodfill_add_initial_with_symmetry(
- Sculpt *sd, Object *ob, SculptSession *ss, SculptFloodFill *flood, int index, float radius)
+void SCULPT_floodfill_add_initial_with_symmetry(Sculpt *sd,
+ Object *ob,
+ SculptSession *ss,
+ SculptFloodFill *flood,
+ SculptVertRef vertex,
+ float radius)
{
/* Add active vertex and symmetric vertices to the queue. */
const char symm = SCULPT_mesh_symmetry_xyz_get(ob);
@@ -1157,18 +2294,18 @@ void SCULPT_floodfill_add_initial_with_symmetry(
if (!SCULPT_is_symmetry_iteration_valid(i, symm)) {
continue;
}
- int v = -1;
+ SculptVertRef v = {-1};
if (i == 0) {
- v = index;
+ v = vertex;
}
else if (radius > 0.0f) {
float radius_squared = (radius == FLT_MAX) ? FLT_MAX : radius * radius;
float location[3];
- flip_v3_v3(location, SCULPT_vertex_co_get(ss, index), i);
+ flip_v3_v3(location, SCULPT_vertex_co_get(ss, vertex), i);
v = SCULPT_nearest_vertex_get(sd, ob, location, radius_squared, false);
}
- if (v != -1) {
+ if (v.i != -1) {
SCULPT_floodfill_add_initial(flood, v);
}
}
@@ -1183,7 +2320,9 @@ void SCULPT_floodfill_add_active(
if (!SCULPT_is_symmetry_iteration_valid(i, symm)) {
continue;
}
- int v = -1;
+
+ SculptVertRef v = {-1};
+
if (i == 0) {
v = SCULPT_active_vertex_get(ss);
}
@@ -1193,26 +2332,31 @@ void SCULPT_floodfill_add_active(
v = SCULPT_nearest_vertex_get(sd, ob, location, radius, false);
}
- if (v != -1) {
+ if (v.i != -1) {
SCULPT_floodfill_add_initial(flood, v);
}
}
}
-void SCULPT_floodfill_execute(
- SculptSession *ss,
- SculptFloodFill *flood,
- bool (*func)(SculptSession *ss, int from_v, int to_v, bool is_duplicate, void *userdata),
- void *userdata)
+void SCULPT_floodfill_execute(SculptSession *ss,
+ SculptFloodFill *flood,
+ bool (*func)(SculptSession *ss,
+ SculptVertRef from_v,
+ SculptVertRef to_v,
+ bool is_duplicate,
+ void *userdata),
+ void *userdata)
{
while (!BLI_gsqueue_is_empty(flood->queue)) {
- int from_v;
+ SculptVertRef from_v;
BLI_gsqueue_pop(flood->queue, &from_v);
SculptVertexNeighborIter ni;
SCULPT_VERTEX_DUPLICATES_AND_NEIGHBORS_ITER_BEGIN (ss, from_v, ni) {
- const int to_v = ni.index;
+ const SculptVertRef to_v = ni.vertex;
- if (BLI_BITMAP_TEST(flood->visited_vertices, to_v)) {
+ const int to_index = BKE_pbvh_vertex_index_to_table(ss->pbvh, to_v);
+
+ if (BLI_BITMAP_TEST(flood->visited_vertices, to_index)) {
continue;
}
@@ -1220,7 +2364,7 @@ void SCULPT_floodfill_execute(
continue;
}
- BLI_BITMAP_ENABLE(flood->visited_vertices, to_v);
+ BLI_BITMAP_ENABLE(flood->visited_vertices, to_index);
if (func(ss, from_v, to_v, ni.is_duplicate, userdata)) {
BLI_gsqueue_push(flood->queue, &to_v);
@@ -1255,12 +2399,14 @@ static bool sculpt_tool_needs_original(const char sculpt_tool)
SCULPT_TOOL_DRAW_SHARP,
SCULPT_TOOL_ELASTIC_DEFORM,
SCULPT_TOOL_SMOOTH,
+ SCULPT_TOOL_PAINT,
+ SCULPT_TOOL_VCOL_BOUNDARY,
SCULPT_TOOL_BOUNDARY,
SCULPT_TOOL_FAIRING,
SCULPT_TOOL_POSE);
}
-static bool sculpt_tool_is_proxy_used(const char sculpt_tool)
+bool sculpt_tool_is_proxy_used(const char sculpt_tool)
{
return ELEM(sculpt_tool,
SCULPT_TOOL_SMOOTH,
@@ -1329,19 +2475,23 @@ typedef enum StrokeFlags {
void SCULPT_orig_vert_data_unode_init(SculptOrigVertData *data, Object *ob, SculptUndoNode *unode)
{
SculptSession *ss = ob->sculpt;
+
+ // do nothing
+
BMesh *bm = ss->bm;
memset(data, 0, sizeof(*data));
data->unode = unode;
+ data->pbvh = ss->pbvh;
+ data->ss = ss;
+
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;
+ // data->datatype = data->unode->type;
}
}
@@ -1349,37 +2499,66 @@ void SCULPT_orig_vert_data_unode_init(SculptOrigVertData *data, Object *ob, Scul
* Initialize a #SculptOrigVertData for accessing original vertex data;
* handles #BMesh, #Mesh, and multi-resolution.
*/
-void SCULPT_orig_vert_data_init(SculptOrigVertData *data, Object *ob, PBVHNode *node)
+void SCULPT_orig_vert_data_init(SculptOrigVertData *data,
+ Object *ob,
+ PBVHNode *node,
+ SculptUndoType type)
{
- SculptUndoNode *unode;
- unode = SCULPT_undo_push_node(ob, node, SCULPT_UNDO_COORDS);
+ SculptUndoNode *unode = NULL;
+ data->ss = ob->sculpt;
+
+ // don't need undo node here anymore
+ if (!ob->sculpt->bm) {
+ // unode = SCULPT_undo_push_node(ob, node, type);
+ }
+
SCULPT_orig_vert_data_unode_init(data, ob, unode);
+ data->datatype = type;
+}
+
+void SCULPT_vertex_check_origdata(SculptSession *ss, SculptVertRef vertex)
+{
+ // check if we need to update original data for current stroke
+ MDynTopoVert *mv = ss->bm ? BKE_PBVH_DYNVERT(ss->cd_dyn_vert, (BMVert *)vertex.i) :
+ ss->mdyntopo_verts + vertex.i;
+
+ if (mv->stroke_id != ss->stroke_id) {
+ mv->stroke_id = ss->stroke_id;
+
+ copy_v3_v3(mv->origco, SCULPT_vertex_co_get(ss, vertex));
+ SCULPT_vertex_normal_get(ss, vertex, mv->origno);
+
+ const float *color = SCULPT_vertex_color_get(ss, vertex);
+ if (color) {
+ copy_v4_v4(mv->origcolor, color);
+ }
+
+ mv->origmask = SCULPT_vertex_mask_get(ss, vertex);
+ }
}
/**
- * Update a #SculptOrigVertData for a particular vertex from the PBVH iterator.
+ * DEPRECATED use 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, SculptVertRef 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
+ MDynTopoVert *mv = SCULPT_vertex_get_mdyntopo(orig_data->ss, vertex);
+
+ SCULPT_vertex_check_origdata(orig_data->ss, vertex);
+
+ if (orig_data->datatype == SCULPT_UNDO_COORDS) {
+ float *no = mv->origno;
+ normal_float_to_short_v3(orig_data->_no, no);
+
+ orig_data->no = orig_data->_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 = mv->origmask;
}
}
@@ -1499,15 +2678,17 @@ static void sculpt_project_v3(const SculptProjectVector *spvc, const float vec[3
* Same goes for alt-key smoothing. */
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)) &&
+ return (
+ (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) &&
- /* Requires mesh restore, which doesn't work with
- * dynamic-topology. */
- !(brush->flag & BRUSH_ANCHORED) && !(brush->flag & BRUSH_DRAG_DOT) &&
+ (!ss->cache || (!ss->cache->alt_smooth)) &&
- SCULPT_TOOL_HAS_DYNTOPO(brush->sculpt_tool));
+ /* Requires mesh restore, which doesn't work with
+ * dynamic-topology. */
+ !(brush->flag & BRUSH_ANCHORED) && !(brush->flag & BRUSH_DRAG_DOT) &&
+ (brush->cached_dyntopo.flag & (DYNTOPO_SUBDIVIDE | DYNTOPO_COLLAPSE | DYNTOPO_CLEANUP)) &&
+ !(brush->cached_dyntopo.flag & DYNTOPO_DISABLED) &&
+ SCULPT_TOOL_HAS_DYNTOPO(brush->sculpt_tool));
}
/*** paint mesh ***/
@@ -1527,7 +2708,7 @@ static void paint_mesh_restore_co_task_cb(void *__restrict userdata,
unode = SCULPT_undo_push_node(data->ob, data->nodes[n], type);
}
else {
- unode = SCULPT_undo_get_node(data->nodes[n]);
+ unode = SCULPT_undo_get_node(data->nodes[n], type);
}
if (!unode) {
@@ -1540,7 +2721,7 @@ static void paint_mesh_restore_co_task_cb(void *__restrict userdata,
SCULPT_orig_vert_data_unode_init(&orig_data, data->ob, unode);
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 (orig_data.unode->type == SCULPT_UNDO_COORDS) {
copy_v3_v3(vd.co, orig_data.co);
@@ -2046,17 +3227,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;
-
- BKE_pbvh_node_get_bm_orco_data(data->nodes[n], &orco_tris, &orco_tris_num, &orco_coords);
-
- 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]],
+ 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;
+ SculptVertRef v1 = tribuf->verts[tri->v[0]];
+ SculptVertRef v2 = tribuf->verts[tri->v[1]];
+ SculptVertRef v3 = tribuf->verts[tri->v[2]];
+
+ const float(*co_tri[3]) = {
+ SCULPT_vertex_origco_get(ss, v1),
+ SCULPT_vertex_origco_get(ss, v2),
+ SCULPT_vertex_origco_get(ss, v3),
};
float co[3];
@@ -2108,11 +3290,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 short *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_short(no_s, temp_no_s);
+ BMVert *v = vd.bm_vert;
+ MDynTopoVert *mv = BKE_PBVH_DYNVERT(vd.cd_dyn_vert, v);
+
+ normal_float_to_short_v3(no_s, mv->origno);
+ copy_v3_v3(co, mv->origco);
}
else {
copy_v3_v3(co, unode->co[vd.i]);
@@ -2418,13 +3600,13 @@ static float brush_strength(const Sculpt *sd,
return root_alpha * feather * pressure * overlap;
}
else if (brush->cloth_deform_type == BRUSH_CLOTH_DEFORM_EXPAND) {
- /* Expand is more sensible to strength as it keeps expanding the cloth when sculpting over
- * the same vertices. */
+ /* Expand is more sensible to strength as it keeps expanding the cloth when sculpting
+ * over the same vertices. */
return 0.1f * alpha * flip * pressure * overlap * feather;
}
else {
- /* Multiply by 10 by default to get a larger range of strength depending on the size of the
- * brush and object. */
+ /* Multiply by 10 by default to get a larger range of strength depending on the size of
+ * the brush and object. */
return 10.0f * alpha * flip * pressure * overlap * feather;
}
case SCULPT_TOOL_DRAW_FACE_SETS:
@@ -2494,6 +3676,10 @@ static float brush_strength(const Sculpt *sd,
return smooth_strength_base * alpha;
}
+ case SCULPT_TOOL_VCOL_BOUNDARY:
+ return flip * alpha * pressure * feather;
+ case SCULPT_TOOL_UV_SMOOTH:
+ return flip * alpha * pressure * feather;
case SCULPT_TOOL_PINCH:
if (flip > 0.0f) {
return alpha * flip * pressure * overlap * feather;
@@ -2516,8 +3702,8 @@ static float brush_strength(const Sculpt *sd,
return root_alpha * feather;
case SCULPT_TOOL_ARRAY:
- //return root_alpha * feather;
- return alpha * pressure ;
+ // return root_alpha * feather;
+ return alpha * pressure;
case SCULPT_TOOL_ROTATE:
return alpha * pressure * feather;
@@ -2540,7 +3726,7 @@ float SCULPT_brush_strength_factor(SculptSession *ss,
const short vno[3],
const float fno[3],
const float mask,
- const int vertex_index,
+ const SculptVertRef vertex_index,
const int thread_id)
{
StrokeCache *cache = ss->cache;
@@ -2754,7 +3940,8 @@ static PBVHNode **sculpt_pbvh_gather_generic(Object *ob,
SculptSession *ss = ob->sculpt;
PBVHNode **nodes = NULL;
- /* Build a list of all nodes that are potentially within the cursor or brush's area of influence.
+ /* 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 = {
@@ -2953,10 +4140,10 @@ typedef struct {
* nodes. */
bool use_back_depth;
- int active_vertex_index;
+ SculptVertRef active_vertex_index;
float *face_normal;
- int active_face_grid_index;
+ SculptFaceRef active_face_grid_index;
struct IsectRayPrecalc isect_precalc;
} SculptRaycastData;
@@ -2978,6 +4165,7 @@ static void do_topology_rake_bmesh_task_cb_ex(void *__restrict userdata,
SculptSession *ss = data->ob->sculpt;
Sculpt *sd = data->sd;
const Brush *brush = data->brush;
+ PBVHNode *node = data->nodes[n];
float direction[3];
copy_v3_v3(direction, ss->cache->grab_delta_symmetry);
@@ -3000,26 +4188,92 @@ static void do_topology_rake_bmesh_task_cb_ex(void *__restrict userdata,
ss, &test, data->brush->falloff_shape);
const int thread_id = BLI_task_parallel_thread_id(tls);
+ const bool use_curvature = ss->cache->brush->flag2 & BRUSH_CURVATURE_RAKE;
+ int check_fsets = ss->cache->brush->flag2 & BRUSH_SMOOTH_PRESERVE_FACE_SETS;
+ check_fsets = check_fsets ? SCULPT_BOUNDARY_FACE_SET : 0;
+
+ if (use_curvature) {
+ SCULPT_curvature_begin(ss, node, false);
+ }
+
+ const bool have_bmesh = BKE_pbvh_type(ss->pbvh) == PBVH_BMESH;
+
+ const bool weighted = ss->cache->brush->flag2 & BRUSH_SMOOTH_USE_AREA_WEIGHT;
+ if (weighted || ss->cache->brush->boundary_smooth_factor > 0.0f) {
+ 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) {
+ BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
if (!sculpt_brush_test_sq_fn(&test, vd.co)) {
continue;
}
+
+ /* ignore boundary verts
+ might want to call normal smooth with
+ rake's projection in this case, I'm not entirely sure
+ - joeedh
+ */
+ if (have_bmesh) {
+ BMVert *v = (BMVert *)vd.vertex.i;
+ MDynTopoVert *mv = BKE_PBVH_DYNVERT(ss->cd_dyn_vert, v);
+
+ // if (mv->flag & (DYNVERT_BOUNDARY | DYNVERT_FSET_BOUNDARY)) {
+ // continue;
+ //}
+ }
+
+ float direction2[3];
const float fade =
bstrength *
SCULPT_brush_strength_factor(
- ss, brush, vd.co, sqrtf(test.dist), vd.no, vd.fno, *vd.mask, vd.index, thread_id) *
+ ss, brush, vd.co, sqrtf(test.dist), vd.no, vd.fno, *vd.mask, vd.vertex, thread_id) *
ss->cache->pressure;
float avg[3], val[3];
- SCULPT_bmesh_four_neighbor_average(avg, direction, vd.bm_vert);
+ if (use_curvature) {
+ SCULPT_curvature_dir_get(ss, vd.vertex, direction2, false);
+ }
+ else {
+ copy_v3_v3(direction2, direction);
+ }
+
+#if 0
+ if (SCULPT_vertex_is_boundary(
+ ss, vd.vertex, SCULPT_BOUNDARY_SHARP | SCULPT_BOUNDARY_MESH | check_fsets)) {
+ continue;
+ }
+
+ MDynTopoVert *mv = BKE_PBVH_DYNVERT(ss->cd_dyn_vert, vd.bm_vert);
+ if (check_fsets && (mv->flag & (DYNVERT_FSET_CORNER))) {
+ continue;
+ }
+
+ if (mv->flag & (DYNVERT_CORNER | DYNVERT_SHARP_CORNER)) {
+ continue;
+ }
+#endif
+
+ int steps = data->do_origco ? 2 : 1;
- sub_v3_v3v3(val, avg, vd.co);
+ for (int step = 0; step < steps; step++) {
+ float *co = step ? (float *)SCULPT_vertex_origco_get(ss, vd.vertex) : vd.co;
- madd_v3_v3v3fl(val, vd.co, val, fade);
+ SCULPT_bmesh_four_neighbor_average(ss,
+ avg,
+ direction2,
+ vd.bm_vert,
+ data->rake_projection,
+ check_fsets,
+ data->cd_temp,
+ data->cd_dyn_vert,
+ step);
- SCULPT_clip(sd, ss, vd.co, val);
+ sub_v3_v3v3(val, avg, co);
+ madd_v3_v3v3fl(val, co, val, fade);
+ SCULPT_clip(sd, ss, co, val);
+ }
if (vd.mvert) {
vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
@@ -3031,11 +4285,63 @@ static void do_topology_rake_bmesh_task_cb_ex(void *__restrict userdata,
static void bmesh_topology_rake(
Sculpt *sd, Object *ob, PBVHNode **nodes, const int totnode, float bstrength)
{
+ SculptSession *ss = ob->sculpt;
Brush *brush = BKE_paint_brush(&sd->paint);
const float strength = clamp_f(bstrength, 0.0f, 1.0f);
- /* Interactions increase both strength and quality. */
- const int iterations = 3;
+ Brush local_brush;
+
+ // vector4, nto color
+ SCULPT_dyntopo_ensure_templayer(ss, CD_PROP_COLOR, "_rake_temp");
+ int cd_temp = SCULPT_dyntopo_get_templayer(ss, CD_PROP_COLOR, "_rake_temp");
+
+#ifdef SCULPT_DIAGONAL_EDGE_MARKS
+ // reset edge flags, single threaded
+ for (int i = 0; i < totnode; i++) {
+ PBVHNode *node = nodes[i];
+ PBVHVertexIter vd;
+
+ SculptBrushTest test;
+ SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
+ ss, &test, brush->falloff_shape);
+
+ BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
+ if (!sculpt_brush_test_sq_fn(&test, vd.co)) {
+ continue;
+ }
+
+ BMVert *v = vd.bm_vert;
+ BMEdge *e = v->e;
+
+ if (!e) {
+ continue;
+ }
+
+ do {
+ e->head.hflag |= BM_ELEM_DRAW;
+ } while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e);
+ }
+ BKE_pbvh_vertex_iter_end;
+ }
+#endif
+
+ 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);
+ }
+
+ if (brush->flag2 & BRUSH_TOPOLOGY_RAKE_IGNORE_BRUSH_FALLOFF) {
+ local_brush = *brush;
+ brush = &local_brush;
+
+ brush->curve_preset = BRUSH_CURVE_SMOOTH;
+
+ /*note that brush hardness is calculated from ss->cache->paint_brush,
+ we can't override it by changing the brush here.
+ this seems desirably though?*/
+ }
+ /* Iterations increase both strength and quality. */
+ const int iterations = 3 + ((int)bstrength) * 2;
int iteration;
const int count = iterations * strength + 1;
@@ -3043,13 +4349,15 @@ static void bmesh_topology_rake(
for (iteration = 0; iteration <= count; iteration++) {
- SculptThreadedTaskData data = {
- .sd = sd,
- .ob = ob,
- .brush = brush,
- .nodes = nodes,
- .strength = factor,
- };
+ SculptThreadedTaskData data = {.sd = sd,
+ .ob = ob,
+ .brush = brush,
+ .nodes = nodes,
+ .strength = factor,
+ .cd_temp = cd_temp,
+ .cd_dyn_vert = ss->cd_dyn_vert,
+ .rake_projection = brush->topology_rake_projection,
+ .do_origco = SCULPT_stroke_needs_original(brush)};
TaskParallelSettings settings;
BKE_pbvh_parallel_range_settings(&settings, true, totnode);
@@ -3079,7 +4387,7 @@ static void do_mask_brush_draw_task_cb_ex(void *__restrict userdata,
}
const float fade = SCULPT_brush_strength_factor(
- ss, brush, vd.co, sqrtf(test.dist), vd.no, vd.fno, 0.0f, vd.index, thread_id);
+ ss, brush, vd.co, sqrtf(test.dist), vd.no, vd.fno, 0.0f, vd.vertex, thread_id);
if (bstrength > 0.0f) {
(*vd.mask) += fade * bstrength * (1.0f - *vd.mask);
@@ -3123,7 +4431,7 @@ static void do_mask_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
do_mask_brush_draw(sd, ob, nodes, totnode);
break;
case BRUSH_MASK_SMOOTH:
- SCULPT_smooth(sd, ob, nodes, totnode, ss->cache->bstrength, true);
+ SCULPT_smooth(sd, ob, nodes, totnode, ss->cache->bstrength, true, 0.0f, false);
break;
}
}
@@ -3160,12 +4468,12 @@ static void do_displacement_eraser_brush_task_cb_ex(void *__restrict userdata,
vd.no,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
- vd.index,
+ vd.vertex,
thread_id);
float limit_co[3];
float disp[3];
- SCULPT_vertex_limit_surface_get(ss, vd.index, limit_co);
+ SCULPT_vertex_limit_surface_get(ss, vd.vertex, limit_co);
sub_v3_v3v3(disp, limit_co, vd.co);
mul_v3_v3fl(proxy[vd.i], disp, fade);
@@ -3215,32 +4523,38 @@ static void do_fairing_brush_tag_store_task_cb_ex(void *__restrict userdata,
const Brush *brush = data->brush;
const int thread_id = BLI_task_parallel_thread_id(tls);
+ const int boundflag = SCULPT_BOUNDARY_MESH | SCULPT_BOUNDARY_SHARP;
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
if (!sculpt_brush_test_sq_fn(&test, vd.co)) {
continue;
}
- if (SCULPT_vertex_is_boundary(ss, vd.index)) {
+ if (SCULPT_vertex_is_boundary(ss, vd.vertex, boundflag)) {
continue;
}
+ float *prefair = SCULPT_temp_cdata_get(vd.vertex, ss->cache->prefairing_co);
+
const float fade = bstrength * SCULPT_brush_strength_factor(ss,
brush,
- ss->cache->prefairing_co[vd.index],
+ prefair,
sqrtf(test.dist),
vd.no,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
- vd.index,
+ vd.vertex,
thread_id);
if (fade == 0.0f) {
continue;
}
- ss->cache->fairing_fade[vd.index] = max_ff(fade, ss->cache->fairing_fade[vd.index]);
- ss->cache->fairing_mask[vd.index] = true;
+ float *fairing_fade = SCULPT_temp_cdata_get(vd.vertex, ss->cache->fairing_fade);
+ bool *fairing_mask = SCULPT_temp_cdata_get(vd.vertex, ss->cache->fairing_mask);
+
+ *fairing_fade = max_ff(fade, *fairing_fade);
+ *fairing_mask = true;
}
BKE_pbvh_vertex_iter_end;
}
@@ -3256,16 +4570,35 @@ static void do_fairing_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totno
}
if (!ss->cache->fairing_mask) {
- ss->cache->fairing_mask = MEM_malloc_arrayN(totvert, sizeof(bool), "fairing_mask");
- ss->cache->fairing_fade = MEM_malloc_arrayN(totvert, sizeof(float), "fairing_fade");
- ss->cache->prefairing_co = MEM_malloc_arrayN(totvert, sizeof(float) * 3, "prefairing_co");
+ // SCULPT_temp_customlayer_ensure(ss, ATTR_DOMAIN_POINT, CD_PROP_BOOL, "fairing_mask");
+ // SCULPT_temp_customlayer_ensure(ss, ATTR_DOMAIN_POINT, CD_PROP_FLOAT, "fairing_fade");
+ // SCULPT_temp_customlayer_ensure(ss, ATTR_DOMAIN_POINT, CD_PROP_FLOAT3, "prefairing_co");
+
+ // using simple array mode
+
+ ss->cache->fairing_mask = MEM_callocN(sizeof(*ss->cache->fairing_mask),
+ "ss->Cache->fairing_mask");
+ ss->cache->fairing_fade = MEM_callocN(sizeof(*ss->cache->fairing_mask),
+ "ss->Cache->fairing_fade");
+ ss->cache->prefairing_co = MEM_callocN(sizeof(*ss->cache->fairing_mask),
+ "ss->Cache->prefairing_co");
+
+ SCULPT_temp_customlayer_get(
+ ss, ATTR_DOMAIN_POINT, CD_PROP_BOOL, "fairing_mask", ss->cache->fairing_mask, true);
+ SCULPT_temp_customlayer_get(
+ ss, ATTR_DOMAIN_POINT, CD_PROP_FLOAT, "fairing_fade", ss->cache->fairing_fade, true);
+ SCULPT_temp_customlayer_get(
+ ss, ATTR_DOMAIN_POINT, CD_PROP_FLOAT3, "prefairing_co", ss->cache->prefairing_co, true);
}
if (SCULPT_stroke_is_main_symmetry_pass(ss->cache)) {
for (int i = 0; i < totvert; i++) {
- ss->cache->fairing_mask[i] = false;
- ss->cache->fairing_fade[i] = 0.0f;
- copy_v3_v3(ss->cache->prefairing_co[i], SCULPT_vertex_co_get(ss, i));
+ SculptVertRef vertex = BKE_pbvh_table_index_to_vertex(ss->pbvh, i);
+
+ *(bool *)SCULPT_temp_cdata_get(vertex, ss->cache->fairing_mask) = false;
+ *(float *)SCULPT_temp_cdata_get(vertex, ss->cache->fairing_fade) = 0.0f;
+ copy_v3_v3((float *)SCULPT_temp_cdata_get(vertex, ss->cache->prefairing_co),
+ SCULPT_vertex_co_get(ss, vertex));
}
}
@@ -3292,13 +4625,13 @@ static void do_fairing_brush_displace_task_cb_ex(void *__restrict userdata,
SculptSession *ss = data->ob->sculpt;
PBVHVertexIter vd;
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
- if (!ss->cache->fairing_mask[vd.index]) {
+ if (!*(bool *)SCULPT_temp_cdata_get(vd.vertex, ss->cache->fairing_mask)) {
continue;
}
float disp[3];
- sub_v3_v3v3(disp, vd.co, ss->cache->prefairing_co[vd.index]);
- mul_v3_fl(disp, ss->cache->fairing_fade[vd.index]);
- copy_v3_v3(vd.co, ss->cache->prefairing_co[vd.index]);
+ sub_v3_v3v3(disp, vd.co, SCULPT_temp_cdata_get(vd.vertex, ss->cache->prefairing_co));
+ mul_v3_fl(disp, *(float *)SCULPT_temp_cdata_get(vd.vertex, ss->cache->fairing_fade));
+ copy_v3_v3(vd.co, SCULPT_temp_cdata_get(vd.vertex, ss->cache->prefairing_co));
add_v3_v3(vd.co, disp);
if (vd.mvert) {
vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
@@ -3323,11 +4656,12 @@ static void sculpt_fairing_brush_exec_fairing_for_cache(Sculpt *sd, Object *ob)
case PBVH_FACES: {
MVert *mvert = SCULPT_mesh_deformed_mverts_get(ss);
BKE_mesh_prefair_and_fair_vertices(
- mesh, mvert, ss->cache->fairing_mask, MESH_FAIRING_DEPTH_TANGENCY);
+ mesh, mvert, ss->cache->fairing_mask->data, MESH_FAIRING_DEPTH_TANGENCY);
} break;
case PBVH_BMESH: {
+ // note that we allocated fairing_mask.data in simple array mode
BKE_bmesh_prefair_and_fair_vertices(
- ss->bm, ss->cache->fairing_mask, MESH_FAIRING_DEPTH_TANGENCY);
+ ss->bm, ss->cache->fairing_mask->data, MESH_FAIRING_DEPTH_TANGENCY);
} break;
case PBVH_GRIDS:
BLI_assert(false);
@@ -3381,7 +4715,7 @@ static void do_displacement_smear_brush_task_cb_ex(void *__restrict userdata,
vd.no,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
- vd.index,
+ vd.vertex,
thread_id);
float current_disp[3];
@@ -3408,11 +4742,11 @@ static void do_displacement_smear_brush_task_cb_ex(void *__restrict userdata,
float weights_accum = 1.0f;
SculptVertexNeighborIter ni;
- SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.index, ni) {
+ SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.vertex, ni) {
float vertex_disp[3];
float vertex_disp_norm[3];
float neighbor_limit_co[3];
- SCULPT_vertex_limit_surface_get(ss, ni.index, neighbor_limit_co);
+ SCULPT_vertex_limit_surface_get(ss, ni.vertex, neighbor_limit_co);
sub_v3_v3v3(vertex_disp,
ss->cache->limit_surface_co[ni.index],
ss->cache->limit_surface_co[vd.index]);
@@ -3452,7 +4786,7 @@ static void do_displacement_smear_store_prev_disp_task_cb_ex(
PBVHVertexIter vd;
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
sub_v3_v3v3(ss->cache->prev_displacement[vd.index],
- SCULPT_vertex_co_get(ss, vd.index),
+ SCULPT_vertex_co_get(ss, vd.vertex),
ss->cache->limit_surface_co[vd.index]);
}
BKE_pbvh_vertex_iter_end;
@@ -3464,16 +4798,20 @@ static void do_displacement_smear_brush(Sculpt *sd, Object *ob, PBVHNode **nodes
SculptSession *ss = ob->sculpt;
BKE_curvemapping_init(brush->curve);
+ SCULPT_vertex_random_access_ensure(ss);
const int totvert = SCULPT_vertex_count_get(ss);
if (!ss->cache->prev_displacement) {
ss->cache->prev_displacement = MEM_malloc_arrayN(
totvert, sizeof(float[3]), "prev displacement");
ss->cache->limit_surface_co = MEM_malloc_arrayN(totvert, sizeof(float[3]), "limit surface co");
+
for (int i = 0; i < totvert; i++) {
- SCULPT_vertex_limit_surface_get(ss, i, ss->cache->limit_surface_co[i]);
+ SculptVertRef vref = BKE_pbvh_table_index_to_vertex(ss->pbvh, i);
+
+ SCULPT_vertex_limit_surface_get(ss, vref, ss->cache->limit_surface_co[i]);
sub_v3_v3v3(ss->cache->prev_displacement[i],
- SCULPT_vertex_co_get(ss, i),
+ SCULPT_vertex_co_get(ss, vref),
ss->cache->limit_surface_co[i]);
}
}
@@ -3525,7 +4863,7 @@ static void do_draw_brush_task_cb_ex(void *__restrict userdata,
vd.no,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
- vd.index,
+ vd.vertex,
thread_id);
mul_v3_v3fl(proxy[vd.i], offset, fade);
@@ -3582,7 +4920,7 @@ static void do_draw_sharp_brush_task_cb_ex(void *__restrict userdata,
SculptOrigVertData orig_data;
float(*proxy)[3];
- SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]);
+ SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COORDS);
proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co;
@@ -3592,7 +4930,8 @@ static void do_draw_sharp_brush_task_cb_ex(void *__restrict userdata,
const int thread_id = BLI_task_parallel_thread_id(tls);
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;
}
@@ -3604,7 +4943,7 @@ static void do_draw_sharp_brush_task_cb_ex(void *__restrict userdata,
orig_data.no,
NULL,
vd.mask ? *vd.mask : 0.0f,
- vd.index,
+ vd.vertex,
thread_id);
mul_v3_v3fl(proxy[vd.i], offset, fade);
@@ -3671,13 +5010,15 @@ static void sculpt_stroke_cache_snap_context_init(bContext *C, Object *ob)
}
static void sculpt_scene_project_view_ray_init(Object *ob,
- const int vertex_index,
+ const SculptVertRef vertex,
float r_ray_normal[3],
float r_ray_origin[3])
{
SculptSession *ss = ob->sculpt;
+ int vertex_index = BKE_pbvh_vertex_index_to_table(ss->pbvh, vertex);
+
float world_space_vertex_co[3];
- mul_v3_m4v3(world_space_vertex_co, ob->obmat, SCULPT_vertex_co_get(ss, vertex_index));
+ mul_v3_m4v3(world_space_vertex_co, ob->obmat, SCULPT_vertex_co_get(ss, vertex));
if (ss->cache->vc->rv3d->is_persp) {
sub_v3_v3v3(r_ray_normal, world_space_vertex_co, ss->cache->view_origin);
normalize_v3(r_ray_normal);
@@ -3690,7 +5031,7 @@ static void sculpt_scene_project_view_ray_init(Object *ob,
}
static void sculpt_scene_project_vertex_normal_ray_init(Object *ob,
- const int vertex_index,
+ const SculptVertRef vertex,
const float original_normal[3],
float r_ray_normal[3],
float r_ray_origin[3])
@@ -3699,16 +5040,16 @@ static void sculpt_scene_project_vertex_normal_ray_init(Object *ob,
mul_v3_m4v3(r_ray_normal, ob->obmat, original_normal);
normalize_v3(r_ray_normal);
- mul_v3_m4v3(r_ray_origin, ob->obmat, SCULPT_vertex_co_get(ss, vertex_index));
+ mul_v3_m4v3(r_ray_origin, ob->obmat, SCULPT_vertex_co_get(ss, vertex));
}
static void sculpt_scene_project_brush_normal_ray_init(Object *ob,
- const int vertex_index,
+ const SculptVertRef vertex,
float r_ray_normal[3],
float r_ray_origin[3])
{
SculptSession *ss = ob->sculpt;
- mul_v3_m4v3(r_ray_origin, ob->obmat, SCULPT_vertex_co_get(ss, vertex_index));
+ mul_v3_m4v3(r_ray_origin, ob->obmat, SCULPT_vertex_co_get(ss, vertex));
mul_v3_m4v3(r_ray_normal, ob->obmat, ss->cache->sculpt_normal);
normalize_v3(r_ray_normal);
}
@@ -3792,7 +5133,7 @@ static void do_scene_project_brush_task_cb_ex(void *__restrict userdata,
const int thread_id = BLI_task_parallel_thread_id(tls);
SculptOrigVertData orig_data;
- SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]);
+ 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) {
@@ -3800,7 +5141,7 @@ static void do_scene_project_brush_task_cb_ex(void *__restrict userdata,
continue;
}
- SCULPT_orig_vert_data_update(&orig_data, &vd);
+ SCULPT_orig_vert_data_update(&orig_data, vd.vertex);
const float fade = bstrength * SCULPT_brush_strength_factor(ss,
brush,
@@ -3809,7 +5150,7 @@ static void do_scene_project_brush_task_cb_ex(void *__restrict userdata,
vd.no,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
- vd.index,
+ vd.vertex,
thread_id);
if (fade == 0.0f) {
@@ -3821,17 +5162,17 @@ static void do_scene_project_brush_task_cb_ex(void *__restrict userdata,
bool use_both_directions = false;
switch (brush->scene_project_direction_type) {
case BRUSH_SCENE_PROJECT_DIRECTION_VIEW:
- sculpt_scene_project_view_ray_init(data->ob, vd.index, ray_normal, ray_origin);
+ sculpt_scene_project_view_ray_init(data->ob, vd.vertex, ray_normal, ray_origin);
break;
case BRUSH_SCENE_PROJECT_DIRECTION_VERTEX_NORMAL: {
float normal[3];
normal_short_to_float_v3(normal, orig_data.no);
sculpt_scene_project_vertex_normal_ray_init(
- data->ob, vd.index, normal, ray_normal, ray_origin);
+ data->ob, vd.vertex, normal, ray_normal, ray_origin);
use_both_directions = true;
} break;
case BRUSH_SCENE_PROJECT_DIRECTION_BRUSH_NORMAL:
- sculpt_scene_project_brush_normal_ray_init(data->ob, vd.index, ray_normal, ray_origin);
+ sculpt_scene_project_brush_normal_ray_init(data->ob, vd.vertex, ray_normal, ray_origin);
use_both_directions = true;
break;
}
@@ -3895,7 +5236,7 @@ static void do_topology_slide_task_cb_ex(void *__restrict userdata,
SculptOrigVertData orig_data;
float(*proxy)[3];
- SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]);
+ SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COORDS);
proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co;
@@ -3905,7 +5246,7 @@ static void do_topology_slide_task_cb_ex(void *__restrict userdata,
const int thread_id = BLI_task_parallel_thread_id(tls);
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;
}
@@ -3916,7 +5257,7 @@ static void do_topology_slide_task_cb_ex(void *__restrict userdata,
orig_data.no,
NULL,
vd.mask ? *vd.mask : 0.0f,
- vd.index,
+ vd.vertex,
thread_id);
float current_disp[3];
float current_disp_norm[3];
@@ -3938,10 +5279,10 @@ static void do_topology_slide_task_cb_ex(void *__restrict userdata,
mul_v3_v3fl(current_disp, current_disp_norm, ss->cache->bstrength);
SculptVertexNeighborIter ni;
- SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.index, ni) {
+ SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.vertex, ni) {
float vertex_disp[3];
float vertex_disp_norm[3];
- sub_v3_v3v3(vertex_disp, SCULPT_vertex_co_get(ss, ni.index), vd.co);
+ sub_v3_v3v3(vertex_disp, SCULPT_vertex_co_get(ss, ni.vertex), vd.co);
normalize_v3_v3(vertex_disp_norm, vertex_disp);
if (dot_v3v3(current_disp_norm, vertex_disp_norm) > 0.0f) {
madd_v3_v3fl(final_disp, vertex_disp_norm, dot_v3v3(current_disp, vertex_disp));
@@ -3971,31 +5312,42 @@ void SCULPT_relax_vertex(SculptSession *ss,
int neighbor_count = 0;
zero_v3(smooth_pos);
zero_v3(boundary_normal);
- const bool is_boundary = SCULPT_vertex_is_boundary(ss, vd->index);
+
+ int bset = SCULPT_BOUNDARY_MESH | SCULPT_BOUNDARY_SHARP;
+ if (ss->cache->brush->flag2 & BRUSH_SMOOTH_PRESERVE_FACE_SETS) {
+ bset |= SCULPT_BOUNDARY_FACE_SET;
+ }
+
+ if (SCULPT_vertex_is_corner(ss, vd->vertex, (SculptCornerType)bset)) {
+ copy_v3_v3(r_final_pos, vd->co);
+ return;
+ }
+
+ const int is_boundary = SCULPT_vertex_is_boundary(ss, vd->vertex, bset);
SculptVertexNeighborIter ni;
- SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd->index, 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.index))) {
+ (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. */
+ /* 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.index)) {
+ if (!SCULPT_vertex_is_boundary(ss, ni.vertex, bset)) {
continue;
}
- add_v3_v3(smooth_pos, SCULPT_vertex_co_get(ss, ni.index));
+ 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.index), vd->co);
+ 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);
}
else {
- add_v3_v3(smooth_pos, SCULPT_vertex_co_get(ss, ni.index));
+ add_v3_v3(smooth_pos, SCULPT_vertex_co_get(ss, ni.vertex));
avg_count++;
}
}
@@ -4020,11 +5372,11 @@ void SCULPT_relax_vertex(SculptSession *ss,
float smooth_closest_plane[3];
float vno[3];
- if (is_boundary && avg_count == 2) {
+ if ((is_boundary & SCULPT_BOUNDARY_MESH) && avg_count == 2) {
normalize_v3_v3(vno, boundary_normal);
}
else {
- SCULPT_vertex_normal_get(ss, vd->index, vno);
+ SCULPT_vertex_normal_get(ss, vd->vertex, vno);
}
if (is_zero_v3(vno)) {
@@ -4052,7 +5404,7 @@ static void do_topology_relax_task_cb_ex(void *__restrict userdata,
PBVHVertexIter vd;
SculptOrigVertData orig_data;
- SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]);
+ SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COORDS);
BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n]);
@@ -4062,7 +5414,8 @@ static void do_topology_relax_task_cb_ex(void *__restrict userdata,
const int thread_id = BLI_task_parallel_thread_id(tls);
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;
}
@@ -4073,7 +5426,7 @@ static void do_topology_relax_task_cb_ex(void *__restrict userdata,
orig_data.no,
NULL,
vd.mask ? *vd.mask : 0.0f,
- vd.index,
+ vd.vertex,
thread_id);
SCULPT_relax_vertex(ss, &vd, fade * bstrength, false, vd.co);
@@ -4231,6 +5584,7 @@ static void do_crease_brush_task_cb_ex(void *__restrict userdata,
if (!sculpt_brush_test_sq_fn(&test, vd.co)) {
continue;
}
+
/* Offset vertex. */
const float fade = SCULPT_brush_strength_factor(ss,
brush,
@@ -4239,7 +5593,7 @@ static void do_crease_brush_task_cb_ex(void *__restrict userdata,
vd.no,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
- vd.index,
+ vd.vertex,
thread_id);
float val1[3];
float val2[3];
@@ -4285,7 +5639,7 @@ static void do_crease_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnod
/* We divide out the squared alpha and multiply by the squared crease
* to give us the pinch strength. */
- crease_correction = brush->crease_pinch_factor * brush->crease_pinch_factor;
+ crease_correction = brush->crease_pinch_factor * brush->crease_pinch_factor * 2.0;
brush_alpha = BKE_brush_alpha_get(scene, brush);
if (brush_alpha > 0.0f) {
crease_correction /= brush_alpha * brush_alpha;
@@ -4299,8 +5653,8 @@ static void do_crease_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnod
flippedbstrength *= -1.0f;
}
- /* Use surface normal for 'spvc', so the vertices are pinched towards a line instead of a single
- * point. Without this we get a 'flat' surface surrounding the pinch. */
+ /* Use surface normal for 'spvc', so the vertices are pinched towards a line instead of a
+ * single point. Without this we get a 'flat' surface surrounding the pinch. */
sculpt_project_v3_cache_init(&spvc, ss->cache->sculpt_normal_symm);
/* Threaded loop over nodes. */
@@ -4355,7 +5709,7 @@ static void do_pinch_brush_task_cb_ex(void *__restrict userdata,
vd.no,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
- vd.index,
+ vd.vertex,
thread_id);
float disp_center[3];
float x_disp[3];
@@ -4447,7 +5801,7 @@ static void do_grab_brush_task_cb_ex(void *__restrict userdata,
float(*proxy)[3];
const float bstrength = ss->cache->bstrength;
- SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]);
+ SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COORDS);
proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co;
@@ -4460,7 +5814,7 @@ static void do_grab_brush_task_cb_ex(void *__restrict userdata,
const bool use_geodesic_dists = brush->flag2 & BRUSH_USE_SURFACE_FALLOFF;
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;
@@ -4481,7 +5835,7 @@ static void do_grab_brush_task_cb_ex(void *__restrict userdata,
orig_data.no,
NULL,
vd.mask ? *vd.mask : 0.0f,
- vd.index,
+ vd.vertex,
thread_id);
if (grab_silhouette) {
@@ -4521,7 +5875,7 @@ static void do_grab_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
const int symm_pass = ss->cache->mirror_symmetry_pass;
float location[3];
flip_v3_v3(location, SCULPT_active_vertex_co_get(ss), symm_pass);
- int v = SCULPT_nearest_vertex_get(sd, ob, location, ss->cache->radius, false);
+ SculptVertRef v = SCULPT_nearest_vertex_get(sd, ob, location, ss->cache->radius, false);
ss->cache->geodesic_dists[symm_pass] = SCULPT_geodesic_from_vertex(
ob, v, ss->cache->initial_radius);
}
@@ -4556,7 +5910,7 @@ static void do_elastic_deform_brush_task_cb_ex(void *__restrict userdata,
const float bstrength = ss->cache->bstrength;
- SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]);
+ SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COORDS);
proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co;
@@ -4581,7 +5935,7 @@ static void do_elastic_deform_brush_task_cb_ex(void *__restrict userdata,
&params, 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);
float final_disp[3];
float orig_co[3];
@@ -4630,13 +5984,15 @@ static void do_elastic_deform_brush_task_cb_ex(void *__restrict userdata,
mul_v3_fl(final_disp, 1.0f - *vd.mask);
}
- mul_v3_fl(final_disp, SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.index));
+ mul_v3_fl(final_disp, SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.vertex));
- copy_v3_v3(proxy[vd.i], final_disp);
-
- if (vd.mvert) {
- vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
+ if (dot_v3v3(final_disp, final_disp) > 0.0000001) {
+ if (vd.mvert) {
+ vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
+ }
}
+
+ copy_v3_v3(proxy[vd.i], final_disp);
}
BKE_pbvh_vertex_iter_end;
}
@@ -4658,7 +6014,8 @@ static void do_elastic_deform_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, in
const int symm_pass = ss->cache->mirror_symmetry_pass;
float location[3];
flip_v3_v3(location, SCULPT_active_vertex_co_get(ss), symm_pass);
- int v = SCULPT_nearest_vertex_get(sd, ob, location, ss->cache->initial_radius, false);
+ SculptVertRef v = SCULPT_nearest_vertex_get(
+ sd, ob, location, ss->cache->initial_radius, false);
ss->cache->geodesic_dists[symm_pass] = SCULPT_geodesic_from_vertex(ob, v, FLT_MAX);
}
}
@@ -4672,6 +6029,9 @@ static void do_elastic_deform_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, in
};
TaskParallelSettings settings;
+
+ SCULPT_vertex_random_access_ensure(ss);
+
BKE_pbvh_parallel_range_settings(&settings, true, totnode);
BLI_task_parallel_range(0, totnode, &data, do_elastic_deform_brush_task_cb_ex, &settings);
}
@@ -4849,7 +6209,7 @@ static void do_nudge_brush_task_cb_ex(void *__restrict userdata,
vd.no,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
- vd.index,
+ vd.vertex,
thread_id);
mul_v3_v3fl(proxy[vd.i], cono, fade);
@@ -4934,7 +6294,7 @@ static void do_snake_hook_brush_task_cb_ex(void *__restrict userdata,
vd.no,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
- vd.index,
+ vd.vertex,
thread_id);
}
@@ -4982,7 +6342,7 @@ static void do_snake_hook_brush_task_cb_ex(void *__restrict userdata,
if (vd.mask) {
mul_v3_fl(disp, 1.0f - *vd.mask);
}
- mul_v3_fl(disp, SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.index));
+ mul_v3_fl(disp, SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.vertex));
copy_v3_v3(proxy[vd.i], disp);
}
@@ -5045,7 +6405,7 @@ static void do_thumb_brush_task_cb_ex(void *__restrict userdata,
float(*proxy)[3];
const float bstrength = ss->cache->bstrength;
- SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]);
+ SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COORDS);
proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co;
@@ -5055,7 +6415,7 @@ static void do_thumb_brush_task_cb_ex(void *__restrict userdata,
const int thread_id = BLI_task_parallel_thread_id(tls);
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;
@@ -5067,7 +6427,7 @@ static void do_thumb_brush_task_cb_ex(void *__restrict userdata,
orig_data.no,
NULL,
vd.mask ? *vd.mask : 0.0f,
- vd.index,
+ vd.vertex,
thread_id);
mul_v3_v3fl(proxy[vd.i], cono, fade);
@@ -5118,7 +6478,7 @@ static void do_rotate_brush_task_cb_ex(void *__restrict userdata,
float(*proxy)[3];
const float bstrength = ss->cache->bstrength;
- SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]);
+ SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COORDS);
proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co;
@@ -5128,7 +6488,7 @@ static void do_rotate_brush_task_cb_ex(void *__restrict userdata,
const int thread_id = BLI_task_parallel_thread_id(tls);
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;
@@ -5141,7 +6501,7 @@ static void do_rotate_brush_task_cb_ex(void *__restrict userdata,
orig_data.no,
NULL,
vd.mask ? *vd.mask : 0.0f,
- vd.index,
+ vd.vertex,
thread_id);
sub_v3_v3v3(vec, orig_data.co, ss->cache->location);
@@ -5178,6 +6538,8 @@ static void do_rotate_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnod
BLI_task_parallel_range(0, totnode, &data, do_rotate_brush_task_cb_ex, &settings);
}
+//#define LAYER_FACE_SET_MODE
+
static void do_layer_brush_task_cb_ex(void *__restrict userdata,
const int n,
const TaskParallelTLS *__restrict tls)
@@ -5187,12 +6549,41 @@ 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->persistent_base && brush->flag & BRUSH_PERSISTENT;
+ bool use_persistent_base = brush->flag & BRUSH_PERSISTENT;
+ const bool is_bmesh = BKE_pbvh_type(ss->pbvh) == PBVH_BMESH;
+
+ if (is_bmesh) {
+ use_persistent_base = use_persistent_base && data->cd_pers_co >= 0;
+
+ // check if we need to zero displacement factor
+ // in first run of brush stroke
+ if (!use_persistent_base) {
+ int nidx = BKE_pbvh_get_node_index(ss->pbvh, data->nodes[n]);
+
+ bool reset_disp = !BLI_BITMAP_TEST(ss->cache->layer_disp_map, nidx);
+ if (reset_disp) {
+ PBVHVertexIter vd;
+
+ BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
+ BMVert *v = (BMVert *)vd.vertex.i;
+ float *disp_factor = BM_ELEM_CD_GET_VOID_P(v, data->cd_layer_disp);
+
+ *disp_factor = 0.0f;
+
+ BLI_BITMAP_SET(ss->cache->layer_disp_map, nidx, true);
+ }
+ BKE_pbvh_vertex_iter_end;
+ }
+ }
+ }
+ else {
+ use_persistent_base = use_persistent_base && ss->persistent_base;
+ }
PBVHVertexIter vd;
SculptOrigVertData orig_data;
const float bstrength = ss->cache->bstrength;
- SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]);
+ SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COORDS);
SculptBrushTest test;
SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
@@ -5200,7 +6591,7 @@ static void do_layer_brush_task_cb_ex(void *__restrict userdata,
const int thread_id = BLI_task_parallel_thread_id(tls);
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;
@@ -5212,13 +6603,23 @@ static void do_layer_brush_task_cb_ex(void *__restrict userdata,
vd.no,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
- vd.index,
+ vd.vertex,
thread_id);
const int vi = vd.index;
float *disp_factor;
if (use_persistent_base) {
- disp_factor = &ss->persistent_base[vi].disp;
+ if (is_bmesh) {
+ BMVert *v = (BMVert *)vd.vertex.i;
+ disp_factor = BM_ELEM_CD_GET_VOID_P(v, data->cd_pers_disp);
+ }
+ else {
+ disp_factor = &ss->persistent_base[vi].disp;
+ }
+ }
+ else if (is_bmesh) {
+ BMVert *v = (BMVert *)vd.vertex.i;
+ disp_factor = BM_ELEM_CD_GET_VOID_P(v, data->cd_layer_disp);
}
else {
disp_factor = &ss->cache->layer_displacement_factor[vi];
@@ -5248,9 +6649,12 @@ static void do_layer_brush_task_cb_ex(void *__restrict userdata,
float normal[3];
if (use_persistent_base) {
- SCULPT_vertex_persistent_normal_get(ss, vi, normal);
+ SCULPT_vertex_persistent_normal_get(ss, vd.vertex, normal, data->cd_pers_no);
mul_v3_fl(normal, brush->height);
- madd_v3_v3v3fl(final_co, SCULPT_vertex_persistent_co_get(ss, vi), normal, *disp_factor);
+ madd_v3_v3v3fl(final_co,
+ SCULPT_vertex_persistent_co_get(ss, vd.vertex, data->cd_pers_co),
+ normal,
+ *disp_factor);
}
else {
normal_short_to_float_v3(normal, orig_data.no);
@@ -5270,27 +6674,157 @@ static void do_layer_brush_task_cb_ex(void *__restrict userdata,
}
}
BKE_pbvh_vertex_iter_end;
+
+#ifdef LAYER_FACE_SET_MODE
+ if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) {
+ TableGSet *bm_faces = BKE_pbvh_bmesh_node_faces(data->nodes[n]);
+ TableGSet *bm_unique_verts = BKE_pbvh_bmesh_node_unique_verts(data->nodes[n]);
+ BMFace *f;
+
+ const int cd_vcol = ss->cd_vcol_offset;
+
+ int fset2 = 1; // data->face_set;
+ const int cd_disp = use_persistent_base ? data->cd_pers_disp : data->cd_layer_disp;
+ int f_ni = BKE_pbvh_get_node_index(ss->pbvh, data->nodes[n]);
+ BMVert *v;
+
+ TGSET_ITER (v, bm_unique_verts) {
+ BMIter iter;
+ BM_ITER_ELEM (f, &iter, v, BM_FACES_OF_VERT) {
+ if (BM_ELEM_CD_GET_INT(f, ss->cd_face_node_offset) != f_ni) {
+ continue;
+ }
+ if (BM_ELEM_CD_GET_INT(f, ss->cd_faceset_offset) < 0) {
+ // continue;
+ }
+
+ fset2 = 1;
+ float height = 0.0;
+ int tot = 0;
+ BMLoop *l = f->l_first;
+
+ int inside = 0;
+
+ do {
+ int v_ni = BM_ELEM_CD_GET_INT(l->v, ss->cd_vert_node_offset);
+
+ float *disp_factor = BM_ELEM_CD_GET_VOID_P(l->v, cd_disp);
+ MDynTopoVert *mv = BKE_PBVH_DYNVERT(ss->cd_dyn_vert, l->v);
+ mv->flag |= DYNVERT_NEED_BOUNDARY;
+
+ if (cd_vcol >= 0) {
+ MPropCol *col = BM_ELEM_CD_GET_VOID_P(l->v, cd_vcol);
+ col->color[0] = MAX2(*disp_factor, 0.0f);
+ col->color[1] = MAX2(-(*disp_factor), 0.0f);
+ col->color[2] = 0.0f;
+ col->color[3] = 1.0f;
+ }
+
+ if (sculpt_brush_test_sq_fn(&test, l->v->co)) { // mv->origco)) {
+ inside++;
+ }
+
+ if (*disp_factor < 0.9) {
+ fset2 = data->face_set2;
+ }
+
+ height += *disp_factor;
+ tot++;
+ } while ((l = l->next) != f->l_first);
+
+ if (!inside) {
+ continue;
+ }
+
+ int *ptr = BM_ELEM_CD_GET_VOID_P(f, ss->cd_faceset_offset);
+ int old = *ptr;
+ atomic_cas_int32(ptr, old, fset2);
+
+ // BM_ELEM_CD_SET_INT(f, ss->cd_faceset_offset, fset2);
+ }
+ }
+ TGSET_ITER_END;
+ }
+
+#endif
}
static void do_layer_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
{
SculptSession *ss = ob->sculpt;
Brush *brush = BKE_paint_brush(&sd->paint);
+ int cd_pers_co = -1, cd_pers_no = -1, cd_pers_disp = -1, cd_layer_disp = -1;
+
+#ifdef LAYER_FACE_SET_MODE
+ if (SCULPT_stroke_is_first_brush_step(ss->cache)) {
+ ss->cache->paint_face_set = SCULPT_face_set_next_available_get(ss);
+ }
+
+ const int fset = ss->cache->paint_face_set;
+#endif
+
+ if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) {
+ if (ss->cache->layer_displacement_factor) {
+ MEM_SAFE_FREE(ss->cache->layer_displacement_factor);
+ ss->cache->layer_displacement_factor = NULL;
+ }
+
+ // note that we don't allow dyntopo to split the PBVH during
+ // the stroke (see DYNTOPO_HAS_DYNAMIC_SPLIT)
+ // so we don't have to worry about resizing ss->cache->layer_disp_map
+ if (!ss->cache->layer_disp_map) {
+ int totnode2 = BKE_pbvh_get_totnodes(ss->pbvh);
+
+ ss->cache->layer_disp_map = BLI_BITMAP_NEW(totnode2, "ss->cache->layer_disp_map");
+ ss->cache->layer_disp_map_size = totnode2;
+ }
+
+ SCULPT_dyntopo_ensure_templayer(ss, CD_PROP_FLOAT3, SCULPT_LAYER_PERS_CO);
+ SCULPT_dyntopo_ensure_templayer(ss, CD_PROP_FLOAT3, SCULPT_LAYER_PERS_NO);
+ SCULPT_dyntopo_ensure_templayer(ss, CD_PROP_FLOAT, SCULPT_LAYER_PERS_DISP);
+
+ SCULPT_dyntopo_ensure_templayer(ss, CD_PROP_FLOAT, SCULPT_LAYER_DISP);
- if (ss->cache->layer_displacement_factor == NULL) {
+ cd_pers_co = SCULPT_dyntopo_get_templayer(ss, CD_PROP_FLOAT3, SCULPT_LAYER_PERS_CO);
+ cd_pers_no = SCULPT_dyntopo_get_templayer(ss, CD_PROP_FLOAT3, SCULPT_LAYER_PERS_NO);
+ cd_pers_disp = SCULPT_dyntopo_get_templayer(ss, CD_PROP_FLOAT, SCULPT_LAYER_PERS_DISP);
+
+ cd_layer_disp = SCULPT_dyntopo_get_templayer(ss, CD_PROP_FLOAT, SCULPT_LAYER_DISP);
+
+ // should never happen
+ if (cd_pers_co < 0 || cd_pers_no < 0 || cd_pers_disp < 0 || cd_layer_disp < 0) {
+ printf("error!! %d %d %d %d\n", cd_pers_co, cd_pers_no, cd_pers_disp, cd_layer_disp);
+ return;
+ }
+ }
+ else if (ss->cache->layer_displacement_factor == NULL) {
ss->cache->layer_displacement_factor = MEM_callocN(sizeof(float) * SCULPT_vertex_count_get(ss),
"layer displacement factor");
}
- SculptThreadedTaskData data = {
- .sd = sd,
- .ob = ob,
- .brush = brush,
- .nodes = nodes,
+ SCULPT_vertex_random_access_ensure(ss);
+
+ SculptThreadedTaskData data = {.sd = sd,
+ .ob = ob,
+ .brush = brush,
+ .nodes = nodes,
+ .cd_pers_co = cd_pers_co,
+ .cd_pers_no = cd_pers_no,
+ .cd_pers_disp = cd_pers_disp,
+ .cd_layer_disp = cd_layer_disp,
+#ifdef LAYER_FACE_SET_MODE
+ .face_set = fset,
+ .face_set2 = fset + 1
+
+#endif
};
TaskParallelSettings settings;
+#ifdef LAYER_FACE_SET_MODE
+ BKE_pbvh_parallel_range_settings(&settings, false, totnode);
+#else
BKE_pbvh_parallel_range_settings(&settings, true, totnode);
+#endif
BLI_task_parallel_range(0, totnode, &data, do_layer_brush_task_cb_ex, &settings);
}
@@ -5324,7 +6858,7 @@ static void do_inflate_brush_task_cb_ex(void *__restrict userdata,
vd.no,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
- vd.index,
+ vd.vertex,
thread_id);
float val[3];
@@ -5437,9 +6971,8 @@ static void do_flatten_brush_task_cb_ex(void *__restrict userdata,
vd.no,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
- vd.index,
+ vd.vertex,
thread_id);
-
mul_v3_v3fl(proxy[vd.i], val, fade);
if (vd.mvert) {
@@ -5592,7 +7125,7 @@ static void do_clay_brush_task_cb_ex(void *__restrict userdata,
vd.no,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
- vd.index,
+ vd.vertex,
thread_id);
mul_v3_v3fl(proxy[vd.i], val, fade);
@@ -5703,7 +7236,7 @@ static void do_clay_strips_brush_task_cb_ex(void *__restrict userdata,
}
float vertex_no[3];
- SCULPT_vertex_normal_get(ss, vd.index, vertex_no);
+ SCULPT_vertex_normal_get(ss, vd.vertex, vertex_no);
if (dot_v3v3(area_no_sp, vertex_no) <= -0.1f) {
continue;
}
@@ -5716,6 +7249,7 @@ static void do_clay_strips_brush_task_cb_ex(void *__restrict userdata,
if (!SCULPT_plane_trim(ss->cache, brush, val)) {
continue;
}
+
/* The normal from the vertices is ignored, it causes glitch with planes, see: T44390. */
const float fade = bstrength * SCULPT_brush_strength_factor(ss,
brush,
@@ -5724,7 +7258,7 @@ static void do_clay_strips_brush_task_cb_ex(void *__restrict userdata,
vd.no,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
- vd.index,
+ vd.vertex,
thread_id);
mul_v3_v3fl(proxy[vd.i], val, fade);
@@ -5831,12 +7365,11 @@ static void do_clay_strips_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int t
BLI_task_parallel_range(0, totnode, &data, do_clay_strips_brush_task_cb_ex, &settings);
}
-
/****** Twist Brush **********/
static void do_twist_brush_task_cb_ex(void *__restrict userdata,
- const int n,
- const TaskParallelTLS *__restrict tls)
+ const int n,
+ const TaskParallelTLS *__restrict tls)
{
SculptThreadedTaskData *data = userdata;
SculptSession *ss = data->ob->sculpt;
@@ -5860,14 +7393,12 @@ static void do_twist_brush_task_cb_ex(void *__restrict userdata,
copy_v3_v3(stroke_line[0], ss->cache->location);
add_v3_v3v3(stroke_line[1], stroke_line[0], stroke_direction);
-
SculptOrigVertData orig_data;
- SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]);
-
+ SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COORDS);
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;
}
@@ -5878,7 +7409,7 @@ static void do_twist_brush_task_cb_ex(void *__restrict userdata,
vd.no,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
- vd.index,
+ vd.vertex,
thread_id);
if (fade == 0.0f) {
@@ -5892,15 +7423,12 @@ static void do_twist_brush_task_cb_ex(void *__restrict userdata,
float scaled_mat[4][4];
float scaled_mat_inv[4][4];
-
-
copy_m4_m4(scaled_mat, mat);
invert_m4(scaled_mat);
mul_v3_fl(scaled_mat[2], 0.7f * fade * (1.0f - bstrength));
invert_m4(scaled_mat);
-
- invert_m4_m4(scaled_mat_inv, scaled_mat);
+ invert_m4_m4(scaled_mat_inv, scaled_mat);
mul_v3_m4v3(local_vert_co, scaled_mat, vd.co);
closest_to_line_v3(vertex_in_line, local_vert_co, rotation_axis, origin);
@@ -5911,7 +7439,6 @@ static void do_twist_brush_task_cb_ex(void *__restrict userdata,
add_v3_v3(p_rotated, vertex_in_line);
mul_v3_m4v3(p_rotated, scaled_mat_inv, p_rotated);
-
float disp[3];
sub_v3_v3v3(disp, p_rotated, vd.co);
mul_v3_fl(disp, bstrength * fade);
@@ -5925,8 +7452,8 @@ static void do_twist_brush_task_cb_ex(void *__restrict userdata,
}
static void do_twist_brush_post_smooth_task_cb_ex(void *__restrict userdata,
- const int n,
- const TaskParallelTLS *__restrict tls)
+ const int n,
+ const TaskParallelTLS *__restrict tls)
{
SculptThreadedTaskData *data = userdata;
SculptSession *ss = data->ob->sculpt;
@@ -5944,8 +7471,6 @@ static void do_twist_brush_post_smooth_task_cb_ex(void *__restrict userdata,
ss, &test, data->brush->falloff_shape);
const int thread_id = BLI_task_parallel_thread_id(tls);
-
-
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
if (!sculpt_brush_test_sq_fn(&test, vd.co)) {
continue;
@@ -5959,34 +7484,33 @@ static void do_twist_brush_post_smooth_task_cb_ex(void *__restrict userdata,
mul_v3_m4v3(local_vert_co, scaled_mat, vd.co);
const float brush_fade = SCULPT_brush_strength_factor(ss,
- brush,
- vd.co,
- sqrtf(test.dist),
- vd.no,
- vd.fno,
- vd.mask ? *vd.mask : 0.0f,
- vd.index,
- thread_id);
-
+ brush,
+ vd.co,
+ sqrtf(test.dist),
+ vd.no,
+ vd.fno,
+ vd.mask ? *vd.mask : 0.0f,
+ vd.vertex,
+ thread_id);
+
float smooth_fade = SCULPT_brush_strength_factor(ss,
- brush,
- vd.co,
- local_vert_co[0],
- vd.no,
- vd.fno,
- vd.mask ? *vd.mask : 0.0f,
- vd.index,
- thread_id);
+ brush,
+ vd.co,
+ local_vert_co[0],
+ vd.no,
+ vd.fno,
+ vd.mask ? *vd.mask : 0.0f,
+ vd.vertex,
+ thread_id);
if (brush_fade == 0.0f) {
- //continue;
+ // continue;
}
if (smooth_fade == 0.0f) {
- //continue;
+ // continue;
}
-
smooth_fade = 1.0f - min_ff(fabsf(local_vert_co[0]), 1.0f);
smooth_fade = pow3f(smooth_fade);
@@ -5995,23 +7519,23 @@ static void do_twist_brush_post_smooth_task_cb_ex(void *__restrict userdata,
float vertex_in_line[3];
float scaled_mat_inv[4][4];
- float avg[3];
- float val[3];
- float disp[3];
+ float avg[3];
+ float val[3];
+ float disp[3];
-/*
- SCULPT_neighbor_coords_average(ss, avg, vd.index);
+ /*
+ SCULPT_neighbor_coords_average(ss, avg, vd.index);
- sub_v3_v3v3(disp, avg, vd.co);
- mul_v3_fl(disp, 1.0f - smooth_fade);
- add_v3_v3(vd.co, disp);
- */
+ sub_v3_v3v3(disp, avg, vd.co);
+ mul_v3_fl(disp, 1.0f - smooth_fade);
+ add_v3_v3(vd.co, disp);
+ */
- float final_co[3];
- SCULPT_relax_vertex(ss, &vd, clamp_f(smooth_fade, 0.0f, 1.0f), false, final_co);
+ float final_co[3];
+ SCULPT_relax_vertex(ss, &vd, clamp_f(smooth_fade, 0.0f, 1.0f), false, final_co);
- sub_v3_v3v3(disp, final_co, vd.co);
- add_v3_v3(vd.co, disp);
+ sub_v3_v3v3(disp, final_co, vd.co);
+ add_v3_v3(vd.co, disp);
if (vd.mvert) {
vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
@@ -6020,7 +7544,6 @@ static void do_twist_brush_post_smooth_task_cb_ex(void *__restrict userdata,
BKE_pbvh_vertex_iter_end;
}
-
static void do_twist_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
{
SculptSession *ss = ob->sculpt;
@@ -6031,7 +7554,6 @@ static void do_twist_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode
const float offset = SCULPT_brush_plane_offset_get(sd, ss);
const float displace = radius * (0.18f + offset);
-
SCULPT_vertex_random_access_ensure(ss);
SCULPT_boundary_info_ensure(ob);
@@ -6066,11 +7588,11 @@ static void do_twist_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode
return;
}
-/*
- mul_v3_v3v3(temp, area_no_sp, ss->cache->scale);
- mul_v3_fl(temp, displace);
- add_v3_v3(area_co, temp);
- */
+ /*
+ mul_v3_v3v3(temp, area_no_sp, ss->cache->scale);
+ mul_v3_fl(temp, displace);
+ add_v3_v3(area_co, temp);
+ */
/* Initialize brush local-space matrix. */
cross_v3_v3v3(mat[0], area_no, ss->cache->grab_delta_symmetry);
@@ -6088,7 +7610,7 @@ static void do_twist_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode
mul_m4_m4m4(tmat, mat, scale);
/* Scale rotation space. */
- //mul_v3_fl(tmat[2], 0.5f);
+ // mul_v3_fl(tmat[2], 0.5f);
float twist_mat[4][4];
invert_m4_m4(twist_mat, tmat);
@@ -6114,11 +7636,10 @@ static void do_twist_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode
data.mat = smooth_mat;
for (int i = 0; i < 2; i++) {
- BLI_task_parallel_range(0, totnode, &data, do_twist_brush_post_smooth_task_cb_ex, &settings);
+ BLI_task_parallel_range(0, totnode, &data, do_twist_brush_post_smooth_task_cb_ex, &settings);
}
}
-
static void do_fill_brush_task_cb_ex(void *__restrict userdata,
const int n,
const TaskParallelTLS *__restrict tls)
@@ -6167,7 +7688,7 @@ static void do_fill_brush_task_cb_ex(void *__restrict userdata,
vd.no,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
- vd.index,
+ vd.vertex,
thread_id);
mul_v3_v3fl(proxy[vd.i], val, fade);
@@ -6265,7 +7786,7 @@ static void do_scrape_brush_task_cb_ex(void *__restrict userdata,
vd.no,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
- vd.index,
+ vd.vertex,
thread_id);
mul_v3_v3fl(proxy[vd.i], val, fade);
@@ -6381,7 +7902,7 @@ static void do_clay_thumb_brush_task_cb_ex(void *__restrict userdata,
vd.no,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
- vd.index,
+ vd.vertex,
thread_id);
mul_v3_v3fl(proxy[vd.i], val, fade);
@@ -6436,8 +7957,8 @@ static void do_clay_thumb_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int to
return;
}
- /* Simulate the clay accumulation by increasing the plane angle as more samples are added to the
- * stroke. */
+ /* Simulate the clay accumulation by increasing the plane angle as more samples are added to
+ * the stroke. */
if (SCULPT_stroke_is_main_symmetry_pass(ss->cache)) {
ss->cache->clay_thumb_front_angle += 0.8f;
ss->cache->clay_thumb_front_angle = clamp_f(ss->cache->clay_thumb_front_angle, 0.0f, 60.0f);
@@ -6520,7 +8041,7 @@ static void do_gravity_task_cb_ex(void *__restrict userdata,
vd.no,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
- vd.index,
+ vd.vertex,
thread_id);
mul_v3_v3fl(proxy[vd.i], offset, fade);
@@ -6602,7 +8123,108 @@ 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,
+ sdata->brush->sculpt_tool == 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(SculptVertRef vertex, void *vdata)
+{
+ DynTopoAutomaskState *state = (DynTopoAutomaskState *)vdata;
+ float mask = SCULPT_automasking_factor_get(state->cache, state->ss, vertex);
+ float mask2 = 1.0f - SCULPT_vertex_mask_get(state->ss, vertex);
+
+ return mask * mask2;
+}
+
+static float sculpt_topology_automasking_mask_cb(SculptVertRef vertex, void *vdata)
+{
+ DynTopoAutomaskState *state = (DynTopoAutomaskState *)vdata;
+ return 1.0f - SCULPT_vertex_mask_get(state->ss, vertex);
+}
+
+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 = 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 = NULL;
+ *r_mask_cb_data = NULL;
+ return false;
+ }
+ }
+
+ DynTopoAutomaskState *state = 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);
+}
+
+/* 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,
@@ -6612,21 +8234,28 @@ static void sculpt_topology_update(Sculpt *sd,
{
SculptSession *ss = ob->sculpt;
- int n, totnode;
+ /* build brush radius scale */
+ float radius_scale = 1.0f;
+
+ if (brush->autosmooth_factor > 0.0f) {
+ radius_scale = MAX2(radius_scale, brush->autosmooth_radius_factor);
+ }
+
+ if (brush->topology_rake_factor > 0.0f) {
+ radius_scale = MAX2(radius_scale, brush->topology_rake_factor);
+ }
+
+ /* multiply with primary radius scale instead of max */
+ if (brush->cached_dyntopo.radius_scale > 0.0f) {
+ radius_scale *= brush->cached_dyntopo.radius_scale;
+ }
+
/* 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);
-
- /* 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);
MEM_SAFE_FREE(ss->vertex_info.connected_component);
@@ -6634,44 +8263,71 @@ static void sculpt_topology_update(Sculpt *sd,
PBVHTopologyUpdateMode mode = 0;
float location[3];
- if (!(sd->flags & SCULPT_DYNTOPO_DETAIL_MANUAL)) {
- if (sd->flags & SCULPT_DYNTOPO_SUBDIVIDE) {
+ if (brush->cached_dyntopo.mode != DYNTOPO_DETAIL_MANUAL) {
+ if (brush->cached_dyntopo.flag & DYNTOPO_SUBDIVIDE) {
mode |= PBVH_Subdivide;
}
+ else if (brush->cached_dyntopo.flag & DYNTOPO_LOCAL_SUBDIVIDE) {
+ mode |= PBVH_LocalSubdivide | PBVH_Subdivide;
+ }
- if ((sd->flags & SCULPT_DYNTOPO_COLLAPSE) || (brush->sculpt_tool == SCULPT_TOOL_SIMPLIFY)) {
+ if (brush->cached_dyntopo.flag & 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, nodes[n]);
+ else if (brush->cached_dyntopo.flag & 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 (brush->cached_dyntopo.flag & DYNTOPO_CLEANUP) {
+ mode |= PBVH_Cleanup;
}
- MEM_SAFE_FREE(nodes);
+ SculptSearchSphereData sdata = {
+ .ss = ss,
+ .sd = sd,
+ .ob = ob,
+ .radius_squared = square_f(ss->cache->radius * radius_scale * 1.25f),
+ .original = use_original,
+ .ignore_fully_ineffective = brush->sculpt_tool != SCULPT_TOOL_MASK,
+ .center = NULL,
+ .brush = brush};
+
+ int symidx = SCULPT_get_symmetry_pass(ss);
+
+ bool modified;
+ 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);
+
+ /* do nodes under the brush cursor */
+ modified = 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,
+ DYNTOPO_HAS_DYNAMIC_SPLIT(brush->sculpt_tool),
+ mask_cb,
+ mask_cb_data);
+
+ SCULPT_dyntopo_automasking_end(mask_cb_data);
/* Update average stroke position. */
copy_v3_v3(location, ss->cache->true_location);
mul_m4_v3(ob->obmat, location);
+
+ ss->totfaces = ss->totpoly = ss->bm->totface;
+ ss->totvert = ss->bm->totvert;
}
static void do_brush_action_task_cb(void *__restrict userdata,
@@ -6698,28 +8354,45 @@ static void do_brush_action_task_cb(void *__restrict userdata,
BKE_pbvh_node_mark_update_mask(data->nodes[n]);
}
else if (ELEM(data->brush->sculpt_tool, SCULPT_TOOL_PAINT, SCULPT_TOOL_SMEAR)) {
- SCULPT_undo_push_node(data->ob, data->nodes[n], SCULPT_UNDO_COLOR);
+ if (!ss->bm) {
+ if (data->brush->vcol_boundary_factor > 0.0f) {
+ SCULPT_undo_push_node(data->ob, data->nodes[n], SCULPT_UNDO_COORDS);
+ }
+
+ SCULPT_undo_push_node(data->ob, data->nodes[n], SCULPT_UNDO_COLOR);
+ }
+
BKE_pbvh_node_mark_update_color(data->nodes[n]);
}
else {
- SCULPT_undo_push_node(data->ob, data->nodes[n], SCULPT_UNDO_COORDS);
+ if (!ss->bm) {
+ SCULPT_undo_push_node(data->ob, data->nodes[n], SCULPT_UNDO_COORDS);
+ }
BKE_pbvh_node_mark_update(data->nodes[n]);
}
}
-static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSettings *ups)
+void do_brush_action(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSettings *ups)
{
SculptSession *ss = ob->sculpt;
int totnode;
PBVHNode **nodes;
- /* Check for unsupported features. */
- PBVHType type = BKE_pbvh_type(ss->pbvh);
- if (brush->sculpt_tool == SCULPT_TOOL_PAINT && type != PBVH_FACES) {
- return;
+ float radius_scale = 1.0f;
+
+ if (!ELEM(brush->sculpt_tool, SCULPT_TOOL_SMOOTH, SCULPT_TOOL_MASK) &&
+ brush->autosmooth_factor > 0 && brush->autosmooth_radius_factor != 1.0f) {
+ radius_scale = MAX2(radius_scale, brush->autosmooth_radius_factor);
+ }
+
+ if (sculpt_brush_use_topology_rake(ss, brush)) {
+ radius_scale = MAX2(radius_scale, brush->topology_rake_radius_factor);
}
- if (brush->sculpt_tool == SCULPT_TOOL_SMEAR && type != PBVH_FACES) {
+ /* Check for unsupported features. */
+ PBVHType type = BKE_pbvh_type(ss->pbvh);
+ if (ELEM(brush->sculpt_tool, SCULPT_TOOL_PAINT, SCULPT_TOOL_SMEAR) &&
+ !ELEM(type, PBVH_BMESH, PBVH_FACES)) {
return;
}
@@ -6728,6 +8401,8 @@ static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSe
}
/* 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;
if (SCULPT_tool_needs_all_pbvh_nodes(brush)) {
/* These brushes need to update all nodes as they are not constrained by the brush radius */
@@ -6737,13 +8412,10 @@ static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSe
nodes = SCULPT_cloth_brush_affected_nodes_gather(ss, brush, &totnode);
}
else {
- const bool use_original = sculpt_tool_needs_original(brush->sculpt_tool) ? true :
- ss->cache->original;
- float radius_scale = 1.0f;
/* With these options enabled not all required nodes are inside the original brush radius, so
* the brush can produce artifacts in some situations. */
if (brush->sculpt_tool == SCULPT_TOOL_DRAW && brush->flag & BRUSH_ORIGINAL_NORMAL) {
- radius_scale = 2.0f;
+ radius_scale = MAX2(radius_scale, 2.0f);
}
nodes = sculpt_pbvh_gather_generic(ob, sd, brush, use_original, radius_scale, &totnode);
}
@@ -6755,9 +8427,7 @@ static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSe
if (brush->sculpt_tool == SCULPT_TOOL_DRAW_FACE_SETS &&
SCULPT_stroke_is_first_brush_step(ss->cache) && !ss->cache->alt_smooth) {
- /* Dynamic-topology does not support Face Sets data, so it can't store/restore it from undo. */
- /* TODO(pablodp606): This check should be done in the undo code and not here, but the rest of
- * the sculpt code is not checking for unsupported undo types that may return a null node. */
+ // faceset undo node is created below for pbvh_bmesh
if (BKE_pbvh_type(ss->pbvh) != PBVH_BMESH) {
SCULPT_undo_push_node(ob, NULL, SCULPT_UNDO_FACE_SETS);
}
@@ -6789,16 +8459,48 @@ static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSe
}
float location[3];
- SculptThreadedTaskData task_data = {
- .sd = sd,
- .ob = ob,
- .brush = brush,
- .nodes = nodes,
- };
+ // dyntopo can't push undo nodes inside a thread
+ if (ss->bm) {
+ if (ELEM(brush->sculpt_tool, SCULPT_TOOL_PAINT, SCULPT_TOOL_SMEAR)) {
+ for (int i = 0; i < totnode; i++) {
+ int other = brush->vcol_boundary_factor > 0.0f ? SCULPT_UNDO_COORDS : -1;
- TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(&settings, true, totnode);
- BLI_task_parallel_range(0, totnode, &task_data, do_brush_action_task_cb, &settings);
+ SCULPT_ensure_dyntopo_node_undo(ob, nodes[i], SCULPT_UNDO_COLOR, other);
+ BKE_pbvh_node_mark_update_color(nodes[i]);
+ }
+ }
+ else if (brush->sculpt_tool == SCULPT_TOOL_DRAW_FACE_SETS) {
+ for (int i = 0; i < totnode; i++) {
+ if (ss->cache->alt_smooth) {
+ SCULPT_ensure_dyntopo_node_undo(ob, nodes[i], SCULPT_UNDO_FACE_SETS, SCULPT_UNDO_COORDS);
+ }
+ else {
+ SCULPT_ensure_dyntopo_node_undo(ob, nodes[i], SCULPT_UNDO_FACE_SETS, -1);
+ }
+
+ BKE_pbvh_node_mark_update(nodes[i]);
+ }
+ }
+ else {
+ for (int i = 0; i < totnode; i++) {
+ SCULPT_ensure_dyntopo_node_undo(ob, nodes[i], SCULPT_UNDO_COORDS, -1);
+
+ BKE_pbvh_node_mark_update(nodes[i]);
+ }
+ }
+ }
+ else {
+ SculptThreadedTaskData task_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, &task_data, do_brush_action_task_cb, &settings);
+ }
if (sculpt_brush_needs_normal(ss, brush)) {
update_sculpt_normal(sd, ob, nodes, totnode);
@@ -6825,6 +8527,8 @@ static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSe
bool invert = ss->cache->pen_flip || ss->cache->invert || brush->flag & BRUSH_DIR_IN;
+ SCULPT_replay_log_append(sd, ss, ob);
+
/* Apply one type of brush action. */
switch (brush->sculpt_tool) {
case SCULPT_TOOL_DRAW:
@@ -6832,7 +8536,7 @@ static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSe
break;
case SCULPT_TOOL_SMOOTH:
if (brush->smooth_deform_type == BRUSH_SMOOTH_DEFORM_LAPLACIAN) {
- SCULPT_do_smooth_brush(sd, ob, nodes, totnode);
+ SCULPT_do_smooth_brush(sd, ob, nodes, totnode, brush->autosmooth_projection);
}
else if (brush->smooth_deform_type == BRUSH_SMOOTH_DEFORM_SURFACE) {
SCULPT_do_surface_smooth_brush(sd, ob, nodes, totnode);
@@ -6955,22 +8659,97 @@ static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSe
break;
case SCULPT_TOOL_ARRAY:
SCULPT_do_array_brush(sd, ob, nodes, totnode);
+ case SCULPT_TOOL_VCOL_BOUNDARY:
+ SCULPT_smooth_vcol_boundary(sd, ob, nodes, totnode, ss->cache->bstrength);
+ break;
+ case SCULPT_TOOL_UV_SMOOTH:
+ SCULPT_uv_brush(sd, ob, nodes, totnode);
break;
}
- if (!ELEM(brush->sculpt_tool, SCULPT_TOOL_SMOOTH, SCULPT_TOOL_MASK) &&
- brush->autosmooth_factor > 0) {
+ bool apply_autosmooth =
+ !ELEM(brush->sculpt_tool, SCULPT_TOOL_BOUNDARY, SCULPT_TOOL_SMOOTH, SCULPT_TOOL_MASK) &&
+ brush->autosmooth_factor > 0;
+
+ if (brush->flag2 & BRUSH_CUSTOM_AUTOSMOOTH_SPACING) {
+ float spacing = (float)brush->autosmooth_spacing / 100.0f;
+
+ apply_autosmooth = apply_autosmooth &&
+ paint_stroke_apply_subspacing(
+ ss->cache->stroke,
+ spacing,
+ PAINT_MODE_SCULPT,
+ &ss->cache->last_smooth_t[SCULPT_get_symmetry_pass(ss)]);
+ }
+
+ if (apply_autosmooth) {
+ float start_radius = ss->cache->radius;
+
+ if (brush->autosmooth_radius_factor != 1.0f) {
+ // note that we expanded the pbvh node search radius earlier in this function
+ // so we just have to adjust the brush radius that's inside ss->cache
+
+ ss->cache->radius *= brush->autosmooth_radius_factor;
+ ss->cache->radius_squared = ss->cache->radius * ss->cache->radius;
+ }
+
if (brush->flag & BRUSH_INVERSE_SMOOTH_PRESSURE) {
- SCULPT_smooth(
- sd, ob, nodes, totnode, brush->autosmooth_factor * (1.0f - ss->cache->pressure), false);
+ SCULPT_smooth(sd,
+ ob,
+ nodes,
+ totnode,
+ brush->autosmooth_factor * (1.0f - ss->cache->pressure),
+ false,
+ brush->autosmooth_projection,
+ false);
}
else {
- SCULPT_smooth(sd, ob, nodes, totnode, brush->autosmooth_factor, false);
+ SCULPT_smooth(sd,
+ ob,
+ nodes,
+ totnode,
+ brush->autosmooth_factor,
+ false,
+ brush->autosmooth_projection,
+ false);
+ }
+
+ if (brush->autosmooth_radius_factor != 1.0f) {
+ ss->cache->radius = start_radius;
+ ss->cache->radius_squared = ss->cache->radius * ss->cache->radius;
}
}
- if (sculpt_brush_use_topology_rake(ss, brush)) {
+ bool use_topology_rake = sculpt_brush_use_topology_rake(ss, brush);
+
+ if (brush->flag2 & BRUSH_CUSTOM_TOPOLOGY_RAKE_SPACING) {
+ float spacing = (float)brush->topology_rake_spacing / 100.0f;
+
+ use_topology_rake = use_topology_rake &&
+ paint_stroke_apply_subspacing(
+ ss->cache->stroke,
+ spacing,
+ PAINT_MODE_SCULPT,
+ &ss->cache->last_rake_t[SCULPT_get_symmetry_pass(ss)]);
+ }
+
+ if (use_topology_rake) {
+ float start_radius = ss->cache->radius;
+
+ if (brush->topology_rake_radius_factor != 1.0f) {
+ // note that we expanded the pbvh node search radius earlier in this function
+ // so we just have to adjust the brush radius that's inside ss->cache
+
+ ss->cache->radius *= brush->topology_rake_radius_factor;
+ ss->cache->radius_squared = ss->cache->radius * ss->cache->radius;
+ }
+
bmesh_topology_rake(sd, ob, nodes, totnode, brush->topology_rake_factor);
+
+ if (brush->topology_rake_radius_factor != 1.0f) {
+ ss->cache->radius = start_radius;
+ ss->cache->radius_squared = ss->cache->radius * ss->cache->radius;
+ }
}
/* The cloth brush adds the gravity as a regular force and it is processed in the solver. */
@@ -7046,7 +8825,9 @@ static void sculpt_combine_proxies_task_cb(void *__restrict userdata,
if (use_orco) {
if (ss->bm) {
- copy_v3_v3(val, BM_log_original_vert_co(ss->bm_log, vd.bm_vert));
+ float *co = BKE_PBVH_DYNVERT(ss->cd_dyn_vert, vd.bm_vert)->origco;
+ copy_v3_v3(val, co);
+ // copy_v3_v3(val, BM_log_original_vert_co(ss->bm_log, vd.bm_vert));
}
else {
copy_v3_v3(val, orco[vd.i]);
@@ -7078,7 +8859,7 @@ static void sculpt_combine_proxies_task_cb(void *__restrict userdata,
BKE_pbvh_node_free_proxies(data->nodes[n]);
}
-static void sculpt_combine_proxies(Sculpt *sd, Object *ob)
+void sculpt_combine_proxies(Sculpt *sd, Object *ob)
{
SculptSession *ss = ob->sculpt;
Brush *brush = BKE_paint_brush(&sd->paint);
@@ -7174,6 +8955,10 @@ static void SCULPT_flush_stroke_deform_task_cb(void *__restrict userdata,
PBVHVertexIter vd;
+ if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) {
+ BM_mesh_elem_index_ensure(ss->bm, BM_VERT);
+ }
+
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
sculpt_flush_pbvhvert_deform(ob, &vd);
@@ -7232,8 +9017,8 @@ void SCULPT_flush_stroke_deform(Sculpt *sd, Object *ob, bool is_proxy_used)
MEM_SAFE_FREE(nodes);
/* Modifiers could depend on mesh normals, so we should update them.
- * NOTE: then if sculpting happens on locked key, normals should be re-calculate after applying
- * coords from key-block on base mesh. */
+ * NOTE: then if sculpting happens on locked key, normals should be re-calculate after
+ * applying coords from key-block on base mesh. */
BKE_mesh_calc_normals(me);
}
else if (ss->shapekey_active) {
@@ -7458,6 +9243,22 @@ bool SCULPT_vertex_colors_poll(bContext *C)
if (!U.experimental.use_sculpt_vertex_colors) {
return false;
}
+
+ return SCULPT_mode_poll(C);
+}
+
+bool SCULPT_vertex_colors_poll_no_bmesh(bContext *C)
+{
+ if (!U.experimental.use_sculpt_vertex_colors) {
+ return false;
+ }
+
+ Object *ob = CTX_data_active_object(C);
+
+ if (ob && ob->sculpt && ob->sculpt->bm) {
+ return false;
+ }
+
return SCULPT_mode_poll(C);
}
@@ -7555,6 +9356,10 @@ static const char *sculpt_tool_name(Sculpt *sd)
return "Clay Strips Brush";
case SCULPT_TOOL_ARRAY:
return "Array Brush";
+ case SCULPT_TOOL_VCOL_BOUNDARY:
+ return "Color Boundary";
+ case SCULPT_TOOL_UV_SMOOTH:
+ return "UV Smooth";
}
return "Sculpting";
@@ -7564,16 +9369,31 @@ 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, 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);
- MEM_SAFE_FREE(cache->prefairing_co);
- MEM_SAFE_FREE(cache->fairing_fade);
- MEM_SAFE_FREE(cache->fairing_mask);
+
+ if (ss->cache->fairing_mask) {
+ SCULPT_temp_customlayer_release(ss, ATTR_DOMAIN_POINT, CD_PROP_BOOL, ss->cache->fairing_mask);
+ }
+
+ if (ss->cache->fairing_fade) {
+ SCULPT_temp_customlayer_release(ss, ATTR_DOMAIN_POINT, CD_PROP_FLOAT, ss->cache->fairing_fade);
+ }
+
+ if (ss->cache->prefairing_co) {
+ SCULPT_temp_customlayer_release(
+ ss, ATTR_DOMAIN_POINT, CD_PROP_FLOAT3, ss->cache->prefairing_co);
+ }
+
+ MEM_SAFE_FREE(ss->cache->fairing_mask);
+ MEM_SAFE_FREE(ss->cache->fairing_fade);
+ MEM_SAFE_FREE(ss->cache->prefairing_co);
+
MEM_SAFE_FREE(cache->prev_displacement);
MEM_SAFE_FREE(cache->limit_surface_co);
@@ -7581,6 +9401,10 @@ void SCULPT_cache_free(StrokeCache *cache)
ED_transform_snap_object_context_destroy(cache->snap_context);
}
+ MEM_SAFE_FREE(cache->layer_disp_map);
+ cache->layer_disp_map = NULL;
+ cache->layer_disp_map_size = 0;
+
if (cache->pose_ik_chain) {
SCULPT_pose_ik_chain_free(cache->pose_ik_chain);
}
@@ -7588,6 +9412,7 @@ void SCULPT_cache_free(StrokeCache *cache)
for (int i = 0; i < PAINT_SYMM_AREAS; i++) {
if (cache->boundaries[i]) {
SCULPT_boundary_data_free(cache->boundaries[i]);
+ cache->boundaries[i] = NULL;
}
if (cache->geodesic_dists[i]) {
MEM_SAFE_FREE(cache->geodesic_dists[i]);
@@ -7647,6 +9472,7 @@ static void sculpt_update_cache_invariants(
float max_scale;
int mode;
+ cache->C = C;
ss->cache = cache;
/* Set scaling adjustment. */
@@ -7830,8 +9656,8 @@ static float sculpt_brush_dynamic_size_get(Brush *brush, StrokeCache *cache, flo
}
}
-/* In these brushes the grab delta is calculated always from the initial stroke location, which is
- * generally used to create grab deformations. */
+/* In these brushes the grab delta is calculated always from the initial stroke location, which
+ * is generally used to create grab deformations. */
static bool sculpt_needs_delta_from_anchored_origin(Brush *brush)
{
if (ELEM(brush->sculpt_tool,
@@ -8048,9 +9874,9 @@ static void sculpt_update_cache_paint_variants(StrokeCache *cache, const Brush *
1.0f - cache->pressure :
cache->pressure;
- /* This makes wet mix more sensible in higher values, which allows to create brushes that have
- * a wider pressure range were they only blend colors without applying too much of the brush
- * color. */
+ /* This makes wet mix more sensible in higher values, which allows to create brushes that
+ * have a wider pressure range were they only blend colors without applying too much of the
+ * brush color. */
cache->paint_brush.wet_mix = 1.0f - pow2f(1.0f - cache->paint_brush.wet_mix);
}
@@ -8176,7 +10002,7 @@ static void sculpt_update_cache_variants(bContext *C, Sculpt *sd, Object *ob, Po
/* Returns true if any of the smoothing modes are active (currently
* one of smooth brush, autosmooth, mask smooth, or shift-key
* smooth). */
-static bool sculpt_needs_connectivity_info(const Sculpt *sd,
+static bool sculpt_needs_connectivity_info(Sculpt *sd,
const Brush *brush,
SculptSession *ss,
int stroke_mode)
@@ -8188,6 +10014,9 @@ static bool sculpt_needs_connectivity_info(const Sculpt *sd,
(brush->sculpt_tool == SCULPT_TOOL_SMOOTH) || (brush->autosmooth_factor > 0) ||
((brush->sculpt_tool == SCULPT_TOOL_MASK) && (brush->mask_tool == BRUSH_MASK_SMOOTH)) ||
(brush->sculpt_tool == SCULPT_TOOL_POSE) ||
+ (brush->sculpt_tool == SCULPT_TOOL_VCOL_BOUNDARY) ||
+ (brush->sculpt_tool == SCULPT_TOOL_UV_SMOOTH) ||
+ (brush->sculpt_tool == SCULPT_TOOL_PAINT && brush->vcol_boundary_factor > 0.0f) ||
(brush->sculpt_tool == SCULPT_TOOL_BOUNDARY) ||
(brush->sculpt_tool == SCULPT_TOOL_FAIRING) ||
(brush->sculpt_tool == SCULPT_TOOL_TWIST) ||
@@ -8227,7 +10056,7 @@ static void sculpt_raycast_cb(PBVHNode *node, void *data_v, float *tmin)
}
else {
/* Intersect with coordinates from before we started stroke. */
- SculptUndoNode *unode = SCULPT_undo_get_node(node);
+ SculptUndoNode *unode = SCULPT_undo_get_node(node, SCULPT_UNDO_COORDS);
origco = (unode) ? unode->co : NULL;
use_origco = origco ? true : false;
}
@@ -8245,7 +10074,8 @@ static void sculpt_raycast_cb(PBVHNode *node, void *data_v, float *tmin)
&srd->back_depth,
&srd->active_vertex_index,
&srd->active_face_grid_index,
- srd->face_normal)) {
+ srd->face_normal,
+ srd->ss->stroke_id)) {
srd->hit = true;
*tmin = srd->depth;
}
@@ -8270,7 +10100,7 @@ static void sculpt_find_nearest_to_ray_cb(PBVHNode *node, void *data_v, float *t
}
else {
/* Intersect with coordinates from before we started stroke. */
- SculptUndoNode *unode = SCULPT_undo_get_node(node);
+ SculptUndoNode *unode = SCULPT_undo_get_node(node, SCULPT_UNDO_COORDS);
origco = (unode) ? unode->co : NULL;
use_origco = origco ? true : false;
}
@@ -8283,7 +10113,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;
}
@@ -8326,8 +10157,9 @@ float SCULPT_raycast_init(ViewContext *vc,
return dist;
}
-/* 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,
@@ -8379,7 +10211,8 @@ bool SCULPT_cursor_geometry_info_update(bContext *C,
.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, srd.ss->stroke_id);
/* Cursor is not over the mesh, return default values. */
if (!srd.hit) {
@@ -8391,6 +10224,7 @@ bool SCULPT_cursor_geometry_info_update(bContext *C,
/* Update the active vertex of the SculptSession. */
ss->active_vertex_index = srd.active_vertex_index;
+
SCULPT_vertex_random_access_ensure(ss);
copy_v3_v3(out->active_vertex_co, SCULPT_active_vertex_co_get(ss));
@@ -8400,11 +10234,11 @@ bool SCULPT_cursor_geometry_info_update(bContext *C,
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_index.i = 0;
+ ss->active_grid_index = srd.active_face_grid_index.i;
break;
case PBVH_BMESH:
- ss->active_face_index = 0;
+ ss->active_face_index = srd.active_face_grid_index;
ss->active_grid_index = 0;
break;
}
@@ -8502,11 +10336,6 @@ bool SCULPT_stroke_get_location(bContext *C, float out[3], const float mouse[2])
depth = SCULPT_raycast_init(&vc, mouse, 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;
@@ -8519,7 +10348,8 @@ bool SCULPT_stroke_get_location(bContext *C, float out[3], const float mouse[2])
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, srd.ss->stroke_id);
if (srd.hit) {
hit = true;
copy_v3_v3(out, ray_normal);
@@ -8701,7 +10531,8 @@ void SCULPT_flush_update_step(bContext *C, SculptUpdateType update_flags)
SCULPT_update_object_bounding_box(ob);
}
- if (SCULPT_get_redraw_rect(region, CTX_wm_region_view3d(C), ob, &r)) {
+ if (CTX_wm_region_view3d(C) &&
+ SCULPT_get_redraw_rect(region, CTX_wm_region_view3d(C), ob, &r)) {
if (ss->cache) {
ss->cache->current_r = r;
}
@@ -8719,6 +10550,13 @@ void SCULPT_flush_update_step(bContext *C, SculptUpdateType update_flags)
}
}
+bool all_nodes_callback(PBVHNode *node, void *data)
+{
+ return true;
+}
+
+void sculpt_undo_print_nodes(void *active);
+
void SCULPT_flush_update_done(const bContext *C, Object *ob, SculptUpdateType update_flags)
{
/* After we are done drawing the stroke, check if we need to do a more
@@ -8770,12 +10608,31 @@ void SCULPT_flush_update_done(const bContext *C, Object *ob, SculptUpdateType up
BKE_pbvh_update_vertex_data(ss->pbvh, PBVH_UpdateMask);
}
- if (update_flags & SCULPT_UPDATE_COLOR) {
- BKE_pbvh_update_vertex_data(ss->pbvh, PBVH_UpdateColor);
- }
-
if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) {
BKE_pbvh_bmesh_after_stroke(ss->pbvh);
+#if 0
+ if (update_flags & SCULPT_UPDATE_COLOR) {
+ PBVHNode **nodes;
+ int totnode = 0;
+
+ // BKE_pbvh_get_nodes(ss->pbvh, PBVH_UpdateColor, &nodes, &totnode);
+ BKE_pbvh_search_gather(ss->pbvh, all_nodes_callback, NULL, &nodes, &totnode);
+
+ for (int i = 0; i < totnode; i++) {
+ SCULPT_undo_push_node(ob, nodes[i], SCULPT_UNDO_COLOR);
+ }
+
+ if (nodes) {
+ MEM_freeN(nodes);
+ }
+ }
+#endif
+
+ sculpt_undo_print_nodes(NULL);
+ }
+
+ if (update_flags & SCULPT_UPDATE_COLOR) {
+ BKE_pbvh_update_vertex_data(ss->pbvh, PBVH_UpdateColor);
}
/* Optimization: if there is locked key and active modifiers present in */
@@ -8816,13 +10673,20 @@ static bool sculpt_stroke_test_start(bContext *C, struct wmOperator *op, const f
ED_view3d_init_mats_rv3d(ob, CTX_wm_region_view3d(C));
+ // increment stroke_id to flag origdata update
+ ss->stroke_id++;
+
+ if (ss->pbvh) {
+ BKE_pbvh_set_stroke_id(ss->pbvh, ss->stroke_id);
+ }
+
sculpt_update_cache_invariants(C, sd, ss, op, mouse);
SculptCursorGeometryInfo sgi;
SCULPT_cursor_geometry_info_update(C, &sgi, mouse, false, false);
SCULPT_undo_push_begin(ob, sculpt_tool_name(sd));
-
+
Brush *brush = BKE_paint_brush(&sd->paint);
if (brush->sculpt_tool == SCULPT_TOOL_ARRAY) {
SCULPT_undo_push_node(ob, NULL, SCULPT_UNDO_GEOMETRY);
@@ -8833,44 +10697,82 @@ static bool sculpt_stroke_test_start(bContext *C, struct wmOperator *op, const f
return false;
}
-static void sculpt_stroke_update_step(bContext *C,
- struct PaintStroke *UNUSED(stroke),
- PointerRNA *itemptr)
+void sculpt_stroke_update_step(bContext *C, struct PaintStroke *stroke, PointerRNA *itemptr)
{
UnifiedPaintSettings *ups = &CTX_data_tool_settings(C)->unified_paint_settings;
Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
Object *ob = CTX_data_active_object(C);
SculptSession *ss = ob->sculpt;
- const Brush *brush = BKE_paint_brush(&sd->paint);
+ Brush *brush = BKE_paint_brush(&sd->paint);
+
+ ss->cache->stroke_distance = stroke->stroke_distance;
+ ss->cache->stroke_distance_t = stroke->stroke_distance_t;
+ ss->cache->stroke = stroke;
+
+ if (SCULPT_stroke_is_first_brush_step(ss->cache)) {
+ ss->cache->last_dyntopo_t = 0.0f;
+
+ memset((void *)ss->cache->last_smooth_t, 0, sizeof(ss->cache->last_smooth_t));
+ memset((void *)ss->cache->last_rake_t, 0, sizeof(ss->cache->last_rake_t));
+ }
+
+ BKE_brush_get_dyntopo(brush, sd, &brush->cached_dyntopo);
if (brush->sculpt_tool == SCULPT_TOOL_SCENE_PROJECT) {
sculpt_stroke_cache_snap_context_init(C, ob);
}
SCULPT_stroke_modifiers_check(C, ob, brush);
- sculpt_update_cache_variants(C, sd, ob, itemptr);
+ if (itemptr) {
+ sculpt_update_cache_variants(C, sd, ob, itemptr);
+ }
sculpt_restore_mesh(sd, ob);
- 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->obmat));
- BKE_pbvh_bmesh_detail_size_set(ss->pbvh, object_space_constant_detail);
+ int boundsym = BKE_get_fset_boundary_symflag(ob);
+ ss->cache->boundary_symmetry = boundsym;
+ ss->boundary_symmetry = boundsym;
+
+ if (ss->pbvh) {
+ BKE_pbvh_set_symmetry(ss->pbvh, SCULPT_mesh_symmetry_xyz_get(ob), boundsym);
+ }
+
+ if (brush->cached_dyntopo.mode == DYNTOPO_DETAIL_CONSTANT ||
+ brush->cached_dyntopo.mode == DYNTOPO_DETAIL_MANUAL) {
+ float object_space_constant_detail = 1.0f / (brush->cached_dyntopo.constant_detail *
+ mat4_to_scale(ob->obmat));
+ BKE_pbvh_bmesh_detail_size_set(
+ ss->pbvh, object_space_constant_detail, brush->cached_dyntopo.detail_range);
}
- else if (sd->flags & SCULPT_DYNTOPO_DETAIL_BRUSH) {
- BKE_pbvh_bmesh_detail_size_set(ss->pbvh, ss->cache->radius * sd->detail_percent / 100.0f);
+ else if (brush->cached_dyntopo.mode == DYNTOPO_DETAIL_BRUSH) {
+ BKE_pbvh_bmesh_detail_size_set(ss->pbvh,
+ ss->cache->radius * brush->cached_dyntopo.detail_percent /
+ 100.0f,
+ brush->cached_dyntopo.detail_range);
}
else {
BKE_pbvh_bmesh_detail_size_set(ss->pbvh,
(ss->cache->radius / ss->cache->dyntopo_pixel_radius) *
- (sd->detail_size * U.pixelsize) / 0.4f);
+ (brush->cached_dyntopo.detail_size * U.pixelsize) / 0.4f,
+ brush->cached_dyntopo.detail_range);
}
if (SCULPT_stroke_is_dynamic_topology(ss, brush)) {
- do_symmetrical_brush_actions(sd, ob, sculpt_topology_update, ups);
+ float spacing = (float)brush->cached_dyntopo.spacing / 100.0f;
+
+ if (paint_stroke_apply_subspacing(
+ ss->cache->stroke, spacing, PAINT_MODE_SCULPT, &ss->cache->last_dyntopo_t)) {
+ do_symmetrical_brush_actions(sd, ob, sculpt_topology_update, ups);
+
+ if (brush->sculpt_tool == SCULPT_TOOL_SNAKE_HOOK) {
+ /* run dyntopo again for snake hook */
+ do_symmetrical_brush_actions(sd, ob, sculpt_topology_update, ups);
+ }
+ }
}
do_symmetrical_brush_actions(sd, ob, do_brush_action, ups);
- if (ss->needs_pbvh_rebuild) {
+ if (ss->needs_pbvh_rebuild) {
/* The mesh was modified, rebuild the PBVH. */
BKE_particlesystem_reset_all(ob);
BKE_ptcache_object_reset(CTX_data_scene(C), ob, PTCACHE_RESET_OUTDATED);
@@ -8984,14 +10886,14 @@ static void sculpt_stroke_done(const bContext *C, struct PaintStroke *UNUSED(str
}
BKE_pbvh_node_color_buffer_free(ss->pbvh);
- SCULPT_cache_free(ss->cache);
+ SCULPT_cache_free(ss, ss->cache);
ss->cache = NULL;
if (brush->sculpt_tool == SCULPT_TOOL_ARRAY) {
SCULPT_undo_push_node(ob, NULL, SCULPT_UNDO_GEOMETRY);
SCULPT_array_datalayers_free(ob);
}
-
+
SCULPT_undo_push_end();
if (brush->sculpt_tool == SCULPT_TOOL_MASK) {
@@ -9080,7 +10982,7 @@ static void sculpt_brush_stroke_cancel(bContext *C, wmOperator *op)
paint_stroke_cancel(C, op);
if (ss->cache) {
- SCULPT_cache_free(ss->cache);
+ SCULPT_cache_free(ss, ss->cache);
ss->cache = NULL;
}
@@ -9131,13 +11033,43 @@ static int sculpt_set_persistent_base_exec(bContext *C, wmOperator *UNUSED(op))
MEM_SAFE_FREE(ss->persistent_base);
+ if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) {
+ ss->persistent_base = NULL;
+
+ SCULPT_dyntopo_ensure_templayer(ss, CD_PROP_FLOAT3, SCULPT_LAYER_PERS_CO);
+ SCULPT_dyntopo_ensure_templayer(ss, CD_PROP_FLOAT3, SCULPT_LAYER_PERS_NO);
+ SCULPT_dyntopo_ensure_templayer(ss, CD_PROP_FLOAT, SCULPT_LAYER_PERS_DISP);
+
+ int cd_pers_co = SCULPT_dyntopo_get_templayer(ss, CD_PROP_FLOAT3, SCULPT_LAYER_PERS_CO);
+ int cd_pers_no = SCULPT_dyntopo_get_templayer(ss, CD_PROP_FLOAT3, SCULPT_LAYER_PERS_NO);
+ int cd_pers_disp = SCULPT_dyntopo_get_templayer(ss, CD_PROP_FLOAT, SCULPT_LAYER_PERS_DISP);
+
+ const int totvert = SCULPT_vertex_count_get(ss);
+ for (int i = 0; i < totvert; i++) {
+ SculptVertRef vertex = BKE_pbvh_table_index_to_vertex(ss->pbvh, i);
+ BMVert *v = (BMVert *)vertex.i;
+
+ float *co = BM_ELEM_CD_GET_VOID_P(v, cd_pers_co);
+ float *no = BM_ELEM_CD_GET_VOID_P(v, cd_pers_no);
+ float *disp = BM_ELEM_CD_GET_VOID_P(v, cd_pers_disp);
+
+ copy_v3_v3(co, SCULPT_vertex_co_get(ss, vertex));
+ SCULPT_vertex_normal_get(ss, vertex, no);
+ *disp = 0.0f;
+ }
+
+ return OPERATOR_FINISHED;
+ }
+
const int totvert = SCULPT_vertex_count_get(ss);
ss->persistent_base = MEM_mallocN(sizeof(SculptPersistentBase) * totvert,
"layer persistent base");
for (int i = 0; i < totvert; i++) {
- copy_v3_v3(ss->persistent_base[i].co, SCULPT_vertex_co_get(ss, i));
- SCULPT_vertex_normal_get(ss, i, ss->persistent_base[i].no);
+ SculptVertRef vertex = BKE_pbvh_table_index_to_vertex(ss->pbvh, i);
+
+ copy_v3_v3(ss->persistent_base[i].co, SCULPT_vertex_co_get(ss, vertex));
+ SCULPT_vertex_normal_get(ss, vertex, ss->persistent_base[i].no);
ss->persistent_base[i].disp = 0.0f;
}
@@ -9199,6 +11131,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, "Dynamic topology symmetrize");
+ SCULPT_undo_push_node(ob, NULL, 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_index.i = 0;
+ ss->active_face_index.i = 0;
+
+ BKE_pbvh_free(ss->pbvh);
+ ss->pbvh = NULL;
+
+ /* Finish undo. */
+ SCULPT_undo_push_end();
+
+ 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);
@@ -9222,7 +11217,6 @@ static int sculpt_symmetrize_exec(bContext *C, wmOperator *op)
* parts that symmetrize modifies). */
SCULPT_undo_push_begin(ob, "Dynamic topology symmetrize");
SCULPT_undo_push_node(ob, NULL, SCULPT_UNDO_DYNTOPO_SYMMETRIZE);
- BM_log_before_all_removed(ss->bm, ss->bm_log);
BM_mesh_toolflags_set(ss->bm, true);
@@ -9233,15 +11227,22 @@ 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_log_full_mesh(ss->bm, ss->bm_log);
+
/* Finish undo. */
- BM_log_all_added(ss->bm, ss->bm_log);
SCULPT_undo_push_end();
break;
@@ -9281,7 +11282,7 @@ static void SCULPT_OT_symmetrize(wmOperatorType *ot)
RNA_def_float(ot->srna,
"merge_tolerance",
- 0.001f,
+ 0.0002f,
0.0f,
FLT_MAX,
"Merge Distance",
@@ -9314,10 +11315,10 @@ static void sculpt_init_session(Main *bmain, Depsgraph *depsgraph, Scene *scene,
/* 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
- * initialized, which is used is some operators that modify the mesh topology to perform certain
- * actions in the new polys. After these operations are finished, all polys should have a valid
- * face set ID assigned (different from SCULPT_FACE_SET_NONE) to manage their visibility
- * correctly. */
+ * initialized, which is used is some operators that modify the mesh topology to perform
+ * certain actions in the new polys. After these operations are finished, all polys should have
+ * a valid face set ID assigned (different from SCULPT_FACE_SET_NONE) to manage their
+ * visibility correctly. */
/* 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. */
@@ -9335,7 +11336,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);
@@ -9359,32 +11361,26 @@ void ED_object_sculptmode_enter_ex(Main *bmain,
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 = NULL;
- if (me->totloop != me->totpoly * 3) {
- message_unsupported = TIP_("non-triangle face");
- }
- else if (mmd != NULL) {
+ if (mmd != NULL) {
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");
}
@@ -9393,19 +11389,37 @@ void ED_object_sculptmode_enter_ex(Main *bmain,
}
}
- if ((message_unsupported == NULL) || force_dyntopo) {
+ if (!has_multires && ((message_unsupported == NULL) || force_dyntopo)) {
/* Needed because we may be entering this mode before the undo system loads. */
wmWindowManager *wm = bmain->wm.first;
- bool has_undo = wm->undo_stack != NULL;
+ bool has_undo = do_undo && wm->undo_stack != NULL;
+
/* Undo push is needed to prevent memory leak. */
if (has_undo) {
SCULPT_undo_push_begin(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, NULL, SCULPT_UNDO_DYNTOPO_BEGIN);
SCULPT_undo_push_end();
}
+ else if (need_bmlog) {
+ if (ob->sculpt->bm_log) {
+ BM_log_free(ob->sculpt->bm_log, true);
+ ob->sculpt->bm_log = NULL;
+ }
+
+ SCULPT_undo_ensure_bmlog(ob);
+
+ // SCULPT_undo_ensure_bmlog failed to find a sculpt undo step
+ if (!ob->sculpt->bm_log) {
+ ob->sculpt->bm_log = BM_log_create(ob->sculpt->bm, ob->sculpt->cd_dyn_vert);
+ }
+ }
}
else {
BKE_reportf(
@@ -9424,7 +11438,7 @@ void ED_object_sculptmode_enter(struct bContext *C, Depsgraph *depsgraph, Report
Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
Object *ob = OBACT(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)
@@ -9504,7 +11518,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) {
@@ -9580,29 +11594,38 @@ void SCULPT_geometry_preview_lines_update(bContext *C, SculptSession *ss, float
const int max_preview_vertices = SCULPT_vertex_count_get(ss) * 3 * 2;
if (ss->preview_vert_index_list == NULL) {
- ss->preview_vert_index_list = MEM_callocN(max_preview_vertices * sizeof(int), "preview lines");
+ ss->preview_vert_index_list = MEM_callocN(max_preview_vertices * sizeof(SculptVertRef),
+ "preview lines");
}
- GSQueue *not_visited_vertices = BLI_gsqueue_new(sizeof(int));
- int active_v = SCULPT_active_vertex_get(ss);
+ GSQueue *not_visited_vertices = BLI_gsqueue_new(sizeof(SculptVertRef));
+ SculptVertRef active_v = SCULPT_active_vertex_get(ss);
BLI_gsqueue_push(not_visited_vertices, &active_v);
while (!BLI_gsqueue_is_empty(not_visited_vertices)) {
- int from_v;
+ SculptVertRef from_v;
+
BLI_gsqueue_pop(not_visited_vertices, &from_v);
+
SculptVertexNeighborIter ni;
SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, from_v, ni) {
if (totpoints + (ni.size * 2) < max_preview_vertices) {
- int to_v = ni.index;
+ SculptVertRef to_v = ni.vertex;
+ int to_v_i = BKE_pbvh_vertex_index_to_table(ss->pbvh, to_v);
+
ss->preview_vert_index_list[totpoints] = from_v;
totpoints++;
ss->preview_vert_index_list[totpoints] = to_v;
totpoints++;
- if (BLI_BITMAP_TEST(visited_vertices, to_v)) {
+
+ if (BLI_BITMAP_TEST(visited_vertices, to_v_i)) {
continue;
}
- BLI_BITMAP_ENABLE(visited_vertices, to_v);
+
+ BLI_BITMAP_ENABLE(visited_vertices, to_v_i);
+
const float *co = SCULPT_vertex_co_for_grab_active_get(ss, to_v);
+
if (len_squared_v3v3(brush_co, co) < radius * radius) {
BLI_gsqueue_push(not_visited_vertices, &to_v);
}
@@ -9675,7 +11698,7 @@ static void SCULPT_OT_vertex_to_loop_colors(wmOperatorType *ot)
ot->idname = "SCULPT_OT_vertex_to_loop_colors";
/* api callbacks */
- ot->poll = SCULPT_vertex_colors_poll;
+ ot->poll = SCULPT_vertex_colors_poll_no_bmesh;
ot->exec = vertex_to_loop_colors_exec;
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
@@ -9738,13 +11761,12 @@ static void SCULPT_OT_loop_to_vertex_colors(wmOperatorType *ot)
ot->idname = "SCULPT_OT_loop_to_vertex_colors";
/* api callbacks */
- ot->poll = SCULPT_vertex_colors_poll;
+ ot->poll = SCULPT_vertex_colors_poll_no_bmesh;
ot->exec = loop_to_vertex_colors_exec;
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
-
#define SAMPLE_COLOR_PREVIEW_SIZE 60
#define SAMPLE_COLOR_OFFSET_X -15
#define SAMPLE_COLOR_OFFSET_Y -15
@@ -9758,9 +11780,7 @@ typedef struct SampleColorCustomData {
float sampled_color[4];
} SampleColorCustomData;
-static void sculpt_sample_color_draw(const bContext *UNUSED(C),
- ARegion *UNUSED(ar),
- void *arg)
+static void sculpt_sample_color_draw(const bContext *UNUSED(C), ARegion *UNUSED(ar), void *arg)
{
SampleColorCustomData *sccd = (SampleColorCustomData *)arg;
GPU_line_width(2.0f);
@@ -9771,48 +11791,56 @@ static void sculpt_sample_color_draw(const bContext *UNUSED(C),
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);
+ 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);
-
+ 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 == NULL) {
- return false;
- }
-
- Object *object_sample = base_sample->object;
- if (object_sample->type != OB_MESH) {
- return 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 == NULL) {
+ return false;
+ }
- Object *ob_eval = DEG_get_evaluated_object(depsgraph, object_sample);
- Mesh *me_eval = BKE_object_get_evaluated_mesh(ob_eval);
- MPropCol *vcol = CustomData_get_layer(&me_eval->vdata, CD_PROP_COLOR);
+ Object *object_sample = base_sample->object;
+ if (object_sample->type != OB_MESH) {
+ return false;
+ }
- if (!vcol) {
- return false;
- }
+ Object *ob_eval = DEG_get_evaluated_object(depsgraph, object_sample);
+ Mesh *me_eval = BKE_object_get_evaluated_mesh(ob_eval);
+ MPropCol *vcol = CustomData_get_layer(&me_eval->vdata, CD_PROP_COLOR);
- ARegion *region = CTX_wm_region(C);
- Scene *scene = CTX_data_scene(C);
- float global_loc[3];
- if (!ED_view3d_autodist_simple(region, event->mval, global_loc, 0, NULL)) {
- return false;
- }
+ if (!vcol) {
+ return false;
+ }
- float object_loc[3];
- mul_v3_m4v3(object_loc, ob_eval->imat, global_loc);
+ ARegion *region = CTX_wm_region(C);
+ Scene *scene = CTX_data_scene(C);
+ float global_loc[3];
+ if (!ED_view3d_autodist_simple(region, event->mval, global_loc, 0, NULL)) {
+ return false;
+ }
+ float object_loc[3];
+ mul_v3_m4v3(object_loc, ob_eval->imat, global_loc);
BVHTreeFromMesh bvh;
BKE_bvhtree_from_mesh_get(&bvh, me_eval, BVHTREE_FROM_VERTS, 2);
@@ -9830,7 +11858,7 @@ static bool sculpt_sample_color_update_from_base(bContext *C, const wmEvent *eve
return true;
}
-static int sculpt_sample_color_modal(bContext *C, wmOperator *op, const wmEvent *event)
+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;
@@ -9858,10 +11886,9 @@ static int sculpt_sample_color_modal(bContext *C, wmOperator *op, const wmEvent
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) {
- const int active_vertex = SCULPT_active_vertex_get(ss);
+ SculptVertRef active_vertex = SCULPT_active_vertex_get(ss);
copy_v4_v4(sccd->sampled_color, SCULPT_vertex_color_get(ss, active_vertex));
IMB_colormanagement_scene_linear_to_srgb_v3(sccd->sampled_color);
}
@@ -9875,9 +11902,7 @@ static int sculpt_sample_color_modal(bContext *C, wmOperator *op, const wmEvent
return OPERATOR_RUNNING_MODAL;
}
-static int sculpt_sample_color_invoke(bContext *C,
- wmOperator *op,
- const wmEvent *UNUSED(e))
+static int sculpt_sample_color_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(e))
{
Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
Scene *scene = CTX_data_scene(C);
@@ -9886,28 +11911,33 @@ static int sculpt_sample_color_invoke(bContext *C,
ARegion *region = CTX_wm_region(C);
SculptSession *ss = ob->sculpt;
- if (!ss->vcol) {
+ if (!ss->bm && !ss->vcol) {
return OPERATOR_CANCELLED;
}
+ else {
+ const SculptVertRef active_vertex = SCULPT_active_vertex_get(ss);
+ const float *active_vertex_color = SCULPT_vertex_color_get(ss, active_vertex);
- SampleColorCustomData *sccd = MEM_callocN(sizeof(SampleColorCustomData), "Sample Color Custom Data");
- const int active_vertex = SCULPT_active_vertex_get(ss);
- copy_v4_v4(sccd->sampled_color, SCULPT_vertex_color_get(ss, active_vertex));
- copy_v4_v4(sccd->initial_color, BKE_brush_color_get(scene, brush));
-
- sccd->draw_handle = ED_region_draw_cb_activate(
- region->type, sculpt_sample_color_draw, sccd, REGION_DRAW_POST_PIXEL);
-
- op->customdata = sccd;
+ if (!active_vertex_color) {
+ return OPERATOR_CANCELLED;
+ }
- WM_event_add_modal_handler(C, op);
- ED_region_tag_redraw(region);
+ SampleColorCustomData *sccd = MEM_callocN(sizeof(SampleColorCustomData),
+ "Sample Color Custom Data");
+ copy_v4_v4(sccd->sampled_color, SCULPT_vertex_color_get(ss, active_vertex));
+ copy_v4_v4(sccd->initial_color, BKE_brush_color_get(scene, brush));
- return OPERATOR_RUNNING_MODAL;
-}
+ 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)
{
@@ -9951,10 +11981,14 @@ enum {
SCULPT_TOPOLOGY_ID_DEFAULT,
};
-static int SCULPT_vertex_get_connected_component(SculptSession *ss, int index)
+static int SCULPT_vertex_get_connected_component(SculptSession *ss, SculptVertRef vertex)
{
+ if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) {
+ vertex.i = BM_elem_index_get((BMVert *)vertex.i);
+ }
+
if (ss->vertex_info.connected_component) {
- return ss->vertex_info.connected_component[index];
+ return ss->vertex_info.connected_component[vertex.i];
}
return SCULPT_TOPOLOGY_ID_DEFAULT;
}
@@ -9963,19 +11997,28 @@ 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 = MEM_malloc_arrayN(
- totvert, sizeof(int), "fake neighbor");
+ totvert, sizeof(SculptVertRef), "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;
}
-static void SCULPT_fake_neighbor_add(SculptSession *ss, int v_index_a, int v_index_b)
+static void SCULPT_fake_neighbor_add(SculptSession *ss,
+ SculptVertRef v_index_a,
+ SculptVertRef v_index_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;
+ int tablea = (int)v_index_a.i, tableb = (int)v_index_b.i;
+
+ if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) {
+ tablea = BM_elem_index_get((BMVert *)v_index_a.i);
+ tableb = BM_elem_index_get((BMVert *)v_index_b.i);
+ }
+
+ if (ss->fake_neighbors.fake_neighbor_index[tablea].i == FAKE_NEIGHBOR_NONE) {
+ ss->fake_neighbors.fake_neighbor_index[tablea] = v_index_b;
+ ss->fake_neighbors.fake_neighbor_index[tableb] = v_index_a;
}
}
@@ -9986,6 +12029,7 @@ static void sculpt_pose_fake_neighbors_free(SculptSession *ss)
typedef struct NearestVertexFakeNeighborTLSData {
int nearest_vertex_index;
+ SculptVertRef nearest_vertex;
float nearest_vertex_distance_squared;
int current_topology_id;
} NearestVertexFakeNeighborTLSData;
@@ -9999,14 +12043,17 @@ static void do_fake_neighbor_search_task_cb(void *__restrict userdata,
NearestVertexFakeNeighborTLSData *nvtd = tls->userdata_chunk;
PBVHVertexIter vd;
+ SCULPT_vertex_random_access_ensure(ss);
+
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
- int vd_topology_id = SCULPT_vertex_get_connected_component(ss, vd.index);
+ int vd_topology_id = SCULPT_vertex_get_connected_component(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) {
nvtd->nearest_vertex_index = vd.index;
+ nvtd->nearest_vertex = vd.vertex;
nvtd->nearest_vertex_distance_squared = distance_squared;
}
}
@@ -10020,17 +12067,23 @@ static void fake_neighbor_search_reduce(const void *__restrict UNUSED(userdata),
{
NearestVertexFakeNeighborTLSData *join = chunk_join;
NearestVertexFakeNeighborTLSData *nvtd = chunk;
+
if (join->nearest_vertex_index == -1) {
join->nearest_vertex_index = nvtd->nearest_vertex_index;
+ join->nearest_vertex = nvtd->nearest_vertex;
join->nearest_vertex_distance_squared = nvtd->nearest_vertex_distance_squared;
}
else if (nvtd->nearest_vertex_distance_squared < join->nearest_vertex_distance_squared) {
join->nearest_vertex_index = nvtd->nearest_vertex_index;
+ join->nearest_vertex = nvtd->nearest_vertex;
join->nearest_vertex_distance_squared = nvtd->nearest_vertex_distance_squared;
}
}
-static int SCULPT_fake_neighbor_search(Sculpt *sd, Object *ob, const int index, float max_distance)
+static SculptVertRef SCULPT_fake_neighbor_search(Sculpt *sd,
+ Object *ob,
+ const SculptVertRef index,
+ float max_distance)
{
SculptSession *ss = ob->sculpt;
PBVHNode **nodes = NULL;
@@ -10045,7 +12098,7 @@ static int SCULPT_fake_neighbor_search(Sculpt *sd, Object *ob, const int index,
BKE_pbvh_search_gather(ss->pbvh, SCULPT_search_sphere_cb, &data, &nodes, &totnode);
if (totnode == 0) {
- return -1;
+ return BKE_pbvh_make_vref(-1);
}
SculptThreadedTaskData task_data = {
@@ -10059,6 +12112,7 @@ static int SCULPT_fake_neighbor_search(Sculpt *sd, Object *ob, const int index,
NearestVertexFakeNeighborTLSData nvtd;
nvtd.nearest_vertex_index = -1;
+ nvtd.nearest_vertex.i = -1;
nvtd.nearest_vertex_distance_squared = FLT_MAX;
nvtd.current_topology_id = SCULPT_vertex_get_connected_component(ss, index);
@@ -10071,19 +12125,24 @@ static int SCULPT_fake_neighbor_search(Sculpt *sd, Object *ob, const int index,
MEM_SAFE_FREE(nodes);
- return nvtd.nearest_vertex_index;
+ return nvtd.nearest_vertex;
}
typedef struct SculptTopologyIDFloodFillData {
int next_id;
} SculptTopologyIDFloodFillData;
-static bool SCULPT_connected_components_floodfill_cb(
- SculptSession *ss, int from_v, int to_v, bool UNUSED(is_duplicate), void *userdata)
+static bool SCULPT_connected_components_floodfill_cb(SculptSession *ss,
+ SculptVertRef from_v,
+ SculptVertRef to_v,
+ bool UNUSED(is_duplicate),
+ void *userdata)
{
SculptTopologyIDFloodFillData *data = userdata;
- ss->vertex_info.connected_component[from_v] = data->next_id;
- ss->vertex_info.connected_component[to_v] = data->next_id;
+ ss->vertex_info.connected_component[BKE_pbvh_vertex_index_to_table(ss->pbvh, from_v)] =
+ data->next_id;
+ ss->vertex_info.connected_component[BKE_pbvh_vertex_index_to_table(ss->pbvh, to_v)] =
+ data->next_id;
return true;
}
@@ -10091,7 +12150,10 @@ void SCULPT_connected_components_ensure(Object *ob)
{
SculptSession *ss = ob->sculpt;
- /* Topology IDs already initialized. They only need to be recalculated when the PBVH is rebuild.
+ SCULPT_vertex_random_access_ensure(ss);
+
+ /* Topology IDs already initialized. They only need to be recalculated when the PBVH is
+ * rebuild.
*/
if (ss->vertex_info.connected_component) {
return;
@@ -10106,10 +12168,16 @@ void SCULPT_connected_components_ensure(Object *ob)
int next_id = 0;
for (int i = 0; i < totvert; i++) {
+ SculptVertRef vertex = BKE_pbvh_table_index_to_vertex(ss->pbvh, i);
+
+ if (!SCULPT_vertex_visible_get(ss, vertex)) {
+ continue;
+ }
+
if (ss->vertex_info.connected_component[i] == SCULPT_TOPOLOGY_ID_NONE) {
SculptFloodFill flood;
SCULPT_floodfill_init(ss, &flood);
- SCULPT_floodfill_add_initial(&flood, i);
+ SCULPT_floodfill_add_initial(&flood, vertex);
SculptTopologyIDFloodFillData data;
data.next_id = next_id;
SCULPT_floodfill_execute(ss, &flood, SCULPT_connected_components_floodfill_cb, &data);
@@ -10122,32 +12190,39 @@ void SCULPT_connected_components_ensure(Object *ob)
void SCULPT_boundary_info_ensure(Object *object)
{
SculptSession *ss = object->sculpt;
- if (ss->vertex_info.boundary) {
+
+ // PBVH_BMESH now handles itself
+ if (ss->bm) {
return;
}
+ else {
+ if (ss->vertex_info.boundary) {
+ return;
+ }
- Mesh *base_mesh = BKE_mesh_from_object(object);
- ss->vertex_info.boundary = BLI_BITMAP_NEW(base_mesh->totvert, "Boundary info");
- int *adjacent_faces_edge_count = MEM_calloc_arrayN(
- base_mesh->totedge, sizeof(int), "Adjacent face edge count");
+ Mesh *base_mesh = BKE_mesh_from_object(object);
+ ss->vertex_info.boundary = BLI_BITMAP_NEW(base_mesh->totvert, "Boundary info");
+ int *adjacent_faces_edge_count = MEM_calloc_arrayN(
+ base_mesh->totedge, sizeof(int), "Adjacent face edge count");
- for (int p = 0; p < base_mesh->totpoly; p++) {
- MPoly *poly = &base_mesh->mpoly[p];
- for (int l = 0; l < poly->totloop; l++) {
- MLoop *loop = &base_mesh->mloop[l + poly->loopstart];
- adjacent_faces_edge_count[loop->e]++;
+ for (int p = 0; p < base_mesh->totpoly; p++) {
+ MPoly *poly = &base_mesh->mpoly[p];
+ for (int l = 0; l < poly->totloop; l++) {
+ MLoop *loop = &base_mesh->mloop[l + poly->loopstart];
+ adjacent_faces_edge_count[loop->e]++;
+ }
}
- }
- for (int e = 0; e < base_mesh->totedge; e++) {
- if (adjacent_faces_edge_count[e] < 2) {
- MEdge *edge = &base_mesh->medge[e];
- BLI_BITMAP_SET(ss->vertex_info.boundary, edge->v1, true);
- BLI_BITMAP_SET(ss->vertex_info.boundary, edge->v2, true);
+ for (int e = 0; e < base_mesh->totedge; e++) {
+ if (adjacent_faces_edge_count[e] < 2) {
+ MEdge *edge = &base_mesh->medge[e];
+ BLI_BITMAP_SET(ss->vertex_info.boundary, edge->v1, true);
+ BLI_BITMAP_SET(ss->vertex_info.boundary, edge->v2, true);
+ }
}
- }
- MEM_freeN(adjacent_faces_edge_count);
+ MEM_freeN(adjacent_faces_edge_count);
+ }
}
void SCULPT_fake_neighbors_ensure(Sculpt *sd, Object *ob, const float max_dist)
@@ -10155,7 +12230,8 @@ void SCULPT_fake_neighbors_ensure(Sculpt *sd, Object *ob, const float max_dist)
SculptSession *ss = ob->sculpt;
const int totvert = SCULPT_vertex_count_get(ss);
- /* Fake neighbors were already initialized with the same distance, so no need to be recalculated.
+ /* Fake neighbors were already initialized with the same distance, so no need to be
+ * recalculated.
*/
if (ss->fake_neighbors.fake_neighbor_index &&
ss->fake_neighbors.current_max_distance == max_dist) {
@@ -10166,12 +12242,13 @@ void SCULPT_fake_neighbors_ensure(Sculpt *sd, Object *ob, const float max_dist)
SCULPT_fake_neighbor_init(ss, max_dist);
for (int i = 0; i < totvert; i++) {
- const int from_v = i;
+ const SculptVertRef from_v = BKE_pbvh_table_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[from_v] == FAKE_NEIGHBOR_NONE) {
- const int to_v = SCULPT_fake_neighbor_search(sd, ob, from_v, max_dist);
- if (to_v != -1) {
+ /* This vertex does not have a fake neighbor yet, seach one for it. */
+ if (ss->fake_neighbors.fake_neighbor_index[i].i == FAKE_NEIGHBOR_NONE) {
+ const SculptVertRef to_v = SCULPT_fake_neighbor_search(sd, ob, from_v, max_dist);
+
+ if (to_v.i != -1) {
/* Add the fake neighbor if available. */
SCULPT_fake_neighbor_add(ss, from_v, to_v);
}
@@ -10288,16 +12365,19 @@ static void do_mask_by_color_contiguous_update_nodes_cb(
}
static bool sculpt_mask_by_color_contiguous_floodfill_cb(
- SculptSession *ss, int from_v, int to_v, bool is_duplicate, void *userdata)
+ SculptSession *ss, SculptVertRef from_v, SculptVertRef to_v, bool is_duplicate, void *userdata)
{
MaskByColorContiguousFloodFillData *data = userdata;
+ int to_v_i = BKE_pbvh_vertex_index_to_table(ss->pbvh, to_v);
+ int from_v_i = BKE_pbvh_vertex_index_to_table(ss->pbvh, from_v);
+
const float *current_color = SCULPT_vertex_color_get(ss, to_v);
float new_vertex_mask = sculpt_mask_by_color_delta_get(
current_color, data->initial_color, data->threshold, data->invert);
- data->new_mask[to_v] = new_vertex_mask;
+ data->new_mask[to_v_i] = new_vertex_mask;
if (is_duplicate) {
- data->new_mask[to_v] = data->new_mask[from_v];
+ data->new_mask[to_v_i] = data->new_mask[from_v_i];
}
float len = len_v3v3(current_color, data->initial_color);
@@ -10306,7 +12386,7 @@ static bool sculpt_mask_by_color_contiguous_floodfill_cb(
}
static void sculpt_mask_by_color_contiguous(Object *object,
- const int vertex,
+ const SculptVertRef vertex,
const float threshold,
const bool invert,
const bool preserve_mask)
@@ -10395,7 +12475,7 @@ static void do_mask_by_color_task_cb(void *__restrict userdata,
}
static void sculpt_mask_by_color_full_mesh(Object *object,
- const int vertex,
+ const SculptVertRef vertex,
const float threshold,
const bool invert,
const bool preserve_mask)
@@ -10441,8 +12521,8 @@ static int sculpt_mask_by_color_invoke(bContext *C, wmOperator *op, const wmEven
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;
float mouse[2];
mouse[0] = event->mval[0];
@@ -10451,7 +12531,7 @@ static int sculpt_mask_by_color_invoke(bContext *C, wmOperator *op, const wmEven
SCULPT_undo_push_begin(ob, "Mask by color");
- const int active_vertex = SCULPT_active_vertex_get(ss);
+ const SculptVertRef active_vertex = SCULPT_active_vertex_get(ss);
const float threshold = RNA_float_get(op->ptr, "threshold");
const bool invert = RNA_boolean_get(op->ptr, "invert");
const bool preserve_mask = RNA_boolean_get(op->ptr, "preserve_previous_mask");
@@ -10550,12 +12630,20 @@ static int sculpt_set_limit_surface_exec(bContext *C, wmOperator *UNUSED(op))
MEM_SAFE_FREE(ss->limit_surface);
+ ss->limit_surface = MEM_callocN(sizeof(*ss->limit_surface), "ss->limit_surface");
+
+ SCULPT_temp_customlayer_ensure(
+ ss, ATTR_DOMAIN_POINT, CD_PROP_FLOAT3, "_sculpt_limit_surface", false);
+ SCULPT_temp_customlayer_get(
+ ss, ATTR_DOMAIN_POINT, CD_PROP_FLOAT3, "_sculpt_limit_surface", ss->limit_surface, false);
+
const int totvert = SCULPT_vertex_count_get(ss);
- ss->limit_surface = MEM_mallocN(sizeof(float) * 3 * totvert,
- "limit surface");
for (int i = 0; i < totvert; i++) {
- SCULPT_neighbor_coords_average(ss, ss->limit_surface[i], i);
+ SculptVertRef vertex = BKE_pbvh_table_index_to_vertex(ss->pbvh, i);
+ float *f = SCULPT_temp_cdata_get(vertex, ss->limit_surface);
+
+ SCULPT_neighbor_coords_average(ss, f, vertex, 0.0, true);
}
return OPERATOR_FINISHED;
@@ -10575,6 +12663,684 @@ static void SCULPT_OT_set_limit_surface(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
+#if 0
+/* -------------------------------------------------------------------- */
+/** \name Dyntopo Detail Size Edit Operator
+ * \{ */
+
+/* Defines how much the mouse movement will modify the detail size value. */
+# define DETAIL_SIZE_DELTA_SPEED 0.08f
+# define DETAIL_SIZE_DELTA_ACCURATE_SPEED 0.004f
+
+typedef struct DyntopoDetailSizeEditCustomData {
+ void *draw_handle;
+ Object *active_object;
+
+ float init_mval[2];
+ float accurate_mval[2];
+
+ float outline_col[4];
+
+ bool accurate_mode;
+ bool sample_mode;
+
+ float init_detail_size;
+ float accurate_detail_size;
+ float detail_size;
+ float radius;
+
+ float preview_tri[3][3];
+ float gizmo_mat[4][4];
+} DyntopoDetailSizeEditCustomData;
+
+static void dyntopo_detail_size_parallel_lines_draw(uint pos3d,
+ DyntopoDetailSizeEditCustomData *cd,
+ const float start_co[3],
+ const float end_co[3],
+ bool flip,
+ const float angle)
+{
+ float object_space_constant_detail = 1.0f /
+ (cd->detail_size * mat4_to_scale(cd->active_object->obmat));
+
+ /* 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. */
+ object_space_constant_detail *= 0.7f;
+
+ 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;
+ const float tot_lines_fl = total_len / object_space_constant_detail;
+ float spacing_disp[3];
+ sub_v3_v3v3(spacing_disp, end_co, start_co);
+ normalize_v3(spacing_disp);
+
+ float line_disp[3];
+ rotate_v2_v2fl(line_disp, spacing_disp, DEG2RAD(angle));
+ mul_v3_fl(spacing_disp, total_len / tot_lines_fl);
+
+ immBegin(GPU_PRIM_LINES, (uint)tot_lines * 2);
+ for (int i = 0; i < tot_lines; i++) {
+ float line_length;
+ if (flip) {
+ line_length = total_len * ((float)i / (float)tot_lines_fl);
+ }
+ else {
+ line_length = total_len * (1.0f - ((float)i / (float)tot_lines_fl));
+ }
+ float line_start[3];
+ copy_v3_v3(line_start, start_co);
+ madd_v3_v3v3fl(line_start, line_start, spacing_disp, i);
+ float line_end[3];
+ madd_v3_v3v3fl(line_end, line_start, line_disp, line_length);
+ immVertex3fv(pos3d, line_start);
+ immVertex3fv(pos3d, line_end);
+ }
+ immEnd();
+}
+
+static void dyntopo_detail_size_edit_draw(const bContext *UNUSED(C),
+ ARegion *UNUSED(ar),
+ void *arg)
+{
+ DyntopoDetailSizeEditCustomData *cd = arg;
+ GPU_blend(GPU_BLEND_ALPHA);
+ GPU_line_smooth(true);
+
+ uint pos3d = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
+ GPU_matrix_push();
+ GPU_matrix_mul(cd->gizmo_mat);
+
+ /* Draw Cursor */
+ immUniformColor4fv(cd->outline_col);
+ GPU_line_width(3.0f);
+
+ imm_draw_circle_wire_3d(pos3d, 0, 0, cd->radius, 80);
+
+ /* Draw Triangle. */
+ immUniformColor4f(0.9f, 0.9f, 0.9f, 0.8f);
+ immBegin(GPU_PRIM_LINES, 6);
+ immVertex3fv(pos3d, cd->preview_tri[0]);
+ immVertex3fv(pos3d, cd->preview_tri[1]);
+
+ immVertex3fv(pos3d, cd->preview_tri[1]);
+ immVertex3fv(pos3d, cd->preview_tri[2]);
+
+ immVertex3fv(pos3d, cd->preview_tri[2]);
+ immVertex3fv(pos3d, cd->preview_tri[0]);
+ immEnd();
+
+ /* Draw Grid */
+ GPU_line_width(1.0f);
+ dyntopo_detail_size_parallel_lines_draw(
+ pos3d, cd, cd->preview_tri[0], cd->preview_tri[1], false, 60.0f);
+ dyntopo_detail_size_parallel_lines_draw(
+ pos3d, cd, cd->preview_tri[0], cd->preview_tri[1], true, 120.0f);
+ dyntopo_detail_size_parallel_lines_draw(
+ pos3d, cd, cd->preview_tri[0], cd->preview_tri[2], false, -60.0f);
+
+ immUnbindProgram();
+ GPU_matrix_pop();
+ GPU_blend(GPU_BLEND_NONE);
+ GPU_line_smooth(false);
+}
+
+static void dyntopo_detail_size_edit_cancel(bContext *C, wmOperator *op)
+{
+ Object *active_object = CTX_data_active_object(C);
+ SculptSession *ss = active_object->sculpt;
+ ARegion *region = CTX_wm_region(C);
+ DyntopoDetailSizeEditCustomData *cd = op->customdata;
+ ED_region_draw_cb_exit(region->type, cd->draw_handle);
+ ss->draw_faded_cursor = false;
+ MEM_freeN(op->customdata);
+ ED_workspace_status_text(C, NULL);
+}
+
+static void dyntopo_detail_size_sample_from_surface(Object *ob,
+ DyntopoDetailSizeEditCustomData *cd)
+{
+ SculptSession *ss = ob->sculpt;
+ const SculptVertRef active_vertex = SCULPT_active_vertex_get(ss);
+
+ float len_accum = 0;
+ int num_neighbors = 0;
+ SculptVertexNeighborIter ni;
+ SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, active_vertex, ni) {
+ len_accum += len_v3v3(SCULPT_vertex_co_get(ss, active_vertex),
+ SCULPT_vertex_co_get(ss, ni.vertex));
+ num_neighbors++;
+ }
+ SCULPT_VERTEX_NEIGHBORS_ITER_END(ni);
+
+ if (num_neighbors > 0) {
+ const float avg_edge_len = len_accum / num_neighbors;
+ /* Use 0.7 as the average of min and max dyntopo edge length. */
+ const float detail_size = 0.7f / (avg_edge_len * mat4_to_scale(cd->active_object->obmat));
+ cd->detail_size = clamp_f(detail_size, 1.0f, 500.0f);
+ }
+}
+
+static void dyntopo_detail_size_update_from_mouse_delta(DyntopoDetailSizeEditCustomData *cd,
+ const wmEvent *event)
+{
+ const float mval[2] = {event->mval[0], event->mval[1]};
+
+ float detail_size_delta;
+ if (cd->accurate_mode) {
+ detail_size_delta = mval[0] - cd->accurate_mval[0];
+ cd->detail_size = cd->accurate_detail_size +
+ detail_size_delta * DETAIL_SIZE_DELTA_ACCURATE_SPEED;
+ }
+ else {
+ detail_size_delta = mval[0] - cd->init_mval[0];
+ cd->detail_size = cd->init_detail_size + detail_size_delta * DETAIL_SIZE_DELTA_SPEED;
+ }
+
+ if (event->type == EVT_LEFTSHIFTKEY && event->val == KM_PRESS) {
+ cd->accurate_mode = true;
+ copy_v2_v2(cd->accurate_mval, mval);
+ cd->accurate_detail_size = cd->detail_size;
+ }
+ if (event->type == EVT_LEFTSHIFTKEY && event->val == KM_RELEASE) {
+ cd->accurate_mode = false;
+ cd->accurate_detail_size = 0.0f;
+ }
+
+ cd->detail_size = clamp_f(cd->detail_size, 1.0f, 500.0f);
+}
+
+static int dyntopo_detail_size_edit_modal(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ Object *active_object = CTX_data_active_object(C);
+ SculptSession *ss = active_object->sculpt;
+ ARegion *region = CTX_wm_region(C);
+ DyntopoDetailSizeEditCustomData *cd = op->customdata;
+ Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
+
+ /* Cancel modal operator */
+ if ((event->type == EVT_ESCKEY && event->val == KM_PRESS) ||
+ (event->type == RIGHTMOUSE && event->val == KM_PRESS)) {
+ dyntopo_detail_size_edit_cancel(C, op);
+ ED_region_tag_redraw(region);
+ return OPERATOR_FINISHED;
+ }
+
+ /* Finish modal operator */
+ if ((event->type == LEFTMOUSE && event->val == KM_RELEASE) ||
+ (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;
+ ss->draw_faded_cursor = false;
+ MEM_freeN(op->customdata);
+ ED_region_tag_redraw(region);
+ ED_workspace_status_text(C, NULL);
+ return OPERATOR_FINISHED;
+ }
+
+ ED_region_tag_redraw(region);
+
+ if (event->type == EVT_LEFTCTRLKEY && event->val == KM_PRESS) {
+ cd->sample_mode = true;
+ }
+ if (event->type == EVT_LEFTCTRLKEY && event->val == KM_RELEASE) {
+ cd->sample_mode = false;
+ }
+
+ /* Sample mode sets the detail size sampling the average edge length under the surface. */
+ if (cd->sample_mode) {
+ dyntopo_detail_size_sample_from_surface(active_object, cd);
+ return OPERATOR_RUNNING_MODAL;
+ }
+ /* Regular mode, changes the detail size by moving the cursor. */
+ dyntopo_detail_size_update_from_mouse_delta(cd, event);
+
+ return OPERATOR_RUNNING_MODAL;
+}
+
+static int dyntopo_detail_size_edit_invoke(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ ARegion *region = CTX_wm_region(C);
+ Object *active_object = CTX_data_active_object(C);
+ Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
+ Brush *brush = BKE_paint_brush(&sd->paint);
+
+ DyntopoDetailSizeEditCustomData *cd = MEM_callocN(sizeof(DyntopoDetailSizeEditCustomData),
+ "Dyntopo Detail Size Edit OP Custom Data");
+
+ /* Initial operator Custom Data setup. */
+ 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;
+ copy_v4_v4(cd->outline_col, brush->add_col);
+ op->customdata = cd;
+
+ 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. */
+ 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];
+ copy_m4_m4(cursor_trans, active_object->obmat);
+ translate_m4(
+ cursor_trans, ss->cursor_location[0], ss->cursor_location[1], ss->cursor_location[2]);
+
+ float cursor_normal[3];
+ if (!is_zero_v3(ss->cursor_sampled_normal)) {
+ copy_v3_v3(cursor_normal, ss->cursor_sampled_normal);
+ }
+ else {
+ copy_v3_v3(cursor_normal, ss->cursor_normal);
+ }
+
+ rotation_between_vecs_to_quat(quat, z_axis, cursor_normal);
+ quat_to_mat4(cursor_rot, quat);
+ copy_m4_m4(cd->gizmo_mat, cursor_trans);
+ mul_m4_m4_post(cd->gizmo_mat, cursor_rot);
+
+ /* Initialize the position of the triangle vertices. */
+ const float y_axis[3] = {0.0f, cd->radius, 0.0f};
+ for (int i = 0; i < 3; i++) {
+ zero_v3(cd->preview_tri[i]);
+ rotate_v2_v2fl(cd->preview_tri[i], y_axis, DEG2RAD(120.0f * i));
+ }
+
+ SCULPT_vertex_random_access_ensure(ss);
+
+ WM_event_add_modal_handler(C, op);
+ ED_region_tag_redraw(region);
+
+ ss->draw_faded_cursor = true;
+
+ const char *status_str = TIP_(
+ "Move the mouse to change the dyntopo detail size. LMB: confirm size, ESC/RMB: cancel");
+ ED_workspace_status_text(C, status_str);
+
+ return OPERATOR_RUNNING_MODAL;
+}
+
+static bool dyntopo_detail_size_edit_poll(bContext *C)
+{
+ Object *ob = CTX_data_active_object(C);
+ Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
+
+ return SCULPT_mode_poll(C) && ob->sculpt->bm && (sd->flags & SCULPT_DYNTOPO_DETAIL_CONSTANT);
+}
+
+static void SCULPT_OT_dyntopo_detail_size_edit(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Edit Dyntopo Detail Size";
+ ot->description = "Modify the constant detail size of dyntopo interactively";
+ ot->idname = "SCULPT_OT_dyntopo_detail_size_edit";
+
+ /* api callbacks */
+ ot->poll = dyntopo_detail_size_edit_poll;
+ ot->invoke = dyntopo_detail_size_edit_invoke;
+ ot->modal = dyntopo_detail_size_edit_modal;
+ ot->cancel = dyntopo_detail_size_edit_cancel;
+
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
+
+#endif
+
+int SCULPT_vertex_valence_get(const struct SculptSession *ss, SculptVertRef vertex)
+{
+ SculptVertexNeighborIter ni;
+ int tot = 0;
+ int mval = -1;
+
+#if 0
+ if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) {
+ BMVert *v = (BMVert *)vertex.i;
+ MDynTopoVert *mv = BKE_PBVH_DYNVERT(ss->cd_dyn_vert, v);
+
+ if (mv->flag & DYNVERT_NEED_VALENCE) {
+ BKE_pbvh_bmesh_update_valence(ss->cd_dyn_vert, vertex);
+ }
+
+ mval = mv->valence;
+
+# ifdef NDEBUG
+ return mval;
+# endif
+ }
+#else
+ if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) {
+ BMVert *v = (BMVert *)vertex.i;
+
+ return BM_vert_edge_count(v);
+ }
+#endif
+
+ SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vertex, ni) {
+ tot++;
+ }
+ SCULPT_VERTEX_NEIGHBORS_ITER_END(ni);
+
+#ifdef NDEBUG
+ if (mval >= 0 && mval != tot) {
+ printf("Out of date vertex valence detected! old: %d, should be: %d\n", mval, tot);
+ }
+#else
+ BLI_assert(mval < 0 || mval == tot);
+#endif
+
+ return tot;
+}
+
+typedef struct BMLinkItem {
+ struct BMLinkItem *next, *prev;
+ BMVert *item;
+ int depth;
+} BMLinkItem;
+
+ATTR_NO_OPT 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);
+
+ Object *visob = sculpt_get_vis_object(C, ss, "rakevis");
+ BMesh *visbm = BM_mesh_create(
+ &bm_mesh_allocsize_default,
+ &((struct BMeshCreateParams){.create_unique_ids = false, .use_toolflags = false}));
+
+ 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 = NULL;
+ 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, "Regularized Rake Directions");
+ 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();
+
+ MEM_SAFE_FREE(nodes);
+
+ BMVert **stack = NULL;
+ 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_malloc_arrayN(bm->totvert, sizeof(*verts), "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];
+
+ MDynTopoVert *mv = BKE_PBVH_DYNVERT(ss->cd_dyn_vert, v);
+ if (mv->flag &
+ (DYNVERT_CORNER | DYNVERT_FSET_CORNER | DYNVERT_SHARP_CORNER | DYNVERT_SEAM_CORNER)) {
+ continue;
+ }
+
+ if (BLI_BITMAP_TEST(visit, v->head.index)) {
+ continue;
+ }
+
+ // v->head.hflag |= BM_ELEM_SELECT;
+
+ float *dir = BM_ELEM_CD_GET_VOID_P(v, cd_vcol);
+ normalize_v3(dir);
+
+ BMLinkItem *node = BLI_mempool_alloc(nodepool);
+ node->next = node->prev = NULL;
+ node->item = v;
+ node->depth = 0;
+
+ BLI_BITMAP_SET(visit, v->head.index, true);
+
+ ListBase queue = {node, node};
+ const int boundflag = DYNVERT_BOUNDARY | DYNVERT_FSET_BOUNDARY | DYNVERT_SEAM_BOUNDARY |
+ DYNVERT_SHARP_BOUNDARY;
+ while (queue.first) {
+ BMLinkItem *node2 = BLI_poptail(&queue);
+ BMVert *v2 = node2->item;
+
+ float *dir2 = BM_ELEM_CD_GET_VOID_P(v2, cd_vcol);
+
+ if (cd_vcol_vis >= 0) {
+ float *color = BM_ELEM_CD_GET_VOID_P(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));
+
+ BMLoop *l;
+
+ float tanco[3];
+ add_v3_v3v3(tanco, v2->co, dir2);
+
+ SCULPT_dyntopo_check_disk_sort(ss, (SculptVertRef){.i = (intptr_t)v2});
+
+ float thlast, thfirst;
+ 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_GET_VOID_P(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 && !(mv->flag & boundflag)) {
+ 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_GET_VOID_P(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);
+
+ MDynTopoVert *mv3 = BKE_PBVH_DYNVERT(ss->cd_dyn_vert, v3);
+ if (mv3->flag & boundflag) {
+ // continue;
+ }
+
+ BLI_BITMAP_SET(visit, v3->head.index, true);
+
+ BMLinkItem *node3 = BLI_mempool_alloc(nodepool);
+ node3->next = node3->prev = NULL;
+ 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_GET_VOID_P(v3, cd_vcol);
+
+ madd_v3_v3v3fl(visco, v3->co, v3->no, 0.001);
+ BMVert *vis1 = BM_vert_create(visbm, visco, NULL, BM_CREATE_NOP);
+
+ madd_v3_v3v3fl(visco, visco, dir3, 0.003);
+ BMVert *vis2 = BM_vert_create(visbm, visco, NULL, BM_CREATE_NOP);
+ BM_edge_create(visbm, vis1, vis2, NULL, BM_CREATE_NOP);
+
+ float fisco2[3];
+ float tan[3];
+ cross_v3_v3v3(tan, dir3, v3->no);
+ madd_v3_v3fl(visco, tan, 0.001);
+
+ vis1 = BM_vert_create(visbm, visco, NULL, BM_CREATE_NOP);
+ BM_edge_create(visbm, vis1, vis2, NULL, 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;
+}
+
+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;
+}
void ED_operatortypes_sculpt(void)
{
@@ -10613,10 +13379,14 @@ void ED_operatortypes_sculpt(void)
WM_operatortype_append(SCULPT_OT_mask_by_color);
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_face_set_by_topology);
+ WM_operatortype_append(SCULPT_OT_spatial_sort_mesh);
+
WM_operatortype_append(SCULPT_OT_expand);
+ WM_operatortype_append(SCULPT_OT_regularize_rake_directions);
}
diff --git a/source/blender/editors/sculpt_paint/sculpt.cc b/source/blender/editors/sculpt_paint/sculpt.cc
new file mode 100644
index 00000000000..adfe2a1c8b1
--- /dev/null
+++ b/source/blender/editors/sculpt_paint/sculpt.cc
@@ -0,0 +1,20 @@
+#if 0
+# include "sculpt.hh"
+
+typedef blender::sculpt::SculptImpl<BMVert *,
+ BMEdge *,
+ BMFace *,
+ blender::sculpt::BMeshBackend,
+ blender::sculpt::BMeshPBVH>
+ BMeshSculpt;
+
+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
diff --git a/source/blender/editors/sculpt_paint/sculpt.hh b/source/blender/editors/sculpt_paint/sculpt.hh
new file mode 100644
index 00000000000..ca144ea6391
--- /dev/null
+++ b/source/blender/editors/sculpt_paint/sculpt.hh
@@ -0,0 +1,474 @@
+#if 0
+# pragma once
+
+/*
+
+This is a proof of concept of how a C++ sculpt system could work.
+
+We can't really use virtual-based polymorphism for performance reasons,
+so the idea is to use templates and C++20's concepts instead.
+
+*/
+
+# include "BKE_pbvh.h"
+# include "BLI_bitmap.h"
+# include "BLI_map.hh"
+# include "BLI_math.h"
+# include "BLI_mempool.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_mesh.h"
+# include "BKE_object.h"
+# include "BKE_paint.h"
+# include "BKE_pbvh.h"
+
+# include "sculpt_intern.h"
+
+# include "bmesh.h"
+# include <concepts>
+# include <functional>
+# include <iterator>
+
+/* clang-format off */
+#include "../../blenkernel/intern/pbvh_intern.h"
+/* clang-format on */
+
+extern PBVHNode *the_fake_node;
+
+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<std::forward_iterator_tag, BMVert> {
+ public:
+ ~unique_verts_iter(){};
+
+ unique_verts_iter(PBVHNode *node) : _node(node)
+ {
+ i = _i = 0;
+
+ while (_i < _node->bm_unique_verts->length && _node->bm_unique_verts->elems[_i] == nullptr) {
+ _i++;
+ }
+ }
+
+ 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;
+ color = a.color;
+ }
+
+ unique_verts_iter(bool is_end_iter)
+ {
+ i = _i = index = 0;
+ vertex = nullptr;
+ co = nullptr;
+ no = nullptr;
+ mask = nullptr;
+ color = nullptr;
+ }
+
+ unique_verts_iter()
+ {
+ i = _i = index = 0;
+ vertex = nullptr;
+ co = nullptr;
+ no = nullptr;
+ mask = nullptr;
+ color = nullptr;
+ }
+
+ inline unique_verts_iter &operator++()
+ {
+ i++;
+ _i++;
+
+ while (i < _node->bm_unique_verts->length && _node->bm_unique_verts->elems[_i] == nullptr) {
+ _i++;
+ }
+
+ if (_i >= _node->bm_unique_verts->length) {
+ vertex = NULL;
+ }
+ else {
+ vertex = reinterpret_cast<BMVert *>(_node->bm_unique_verts->elems + _i);
+ }
+
+ if (vertex) {
+ deprecated_vertref.i = reinterpret_cast<intptr_t>(vertex);
+ co = vertex->co;
+ no = vertex->no;
+ }
+
+ 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++;
+ }
+ }
+
+ inline unique_verts_iter begin()
+ {
+ unique_verts_iter ret = *this;
+
+ ret.reset();
+
+ return ret;
+ }
+
+ inline unique_verts_iter end()
+ {
+ return unique_verts_iter(true);
+ }
+
+ 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;
+ }
+
+ SculptVertRef deprecated_vertref;
+
+ int i;
+ int index;
+ BMVert *vertex;
+ float *co;
+ float *no;
+ float *mask;
+ float *color;
+
+ private:
+ PBVHNode *_node;
+ int _i;
+ };
+
+ 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(radius_scale),
+ test(*test),
+ test_func(test_func),
+ _ss(ss),
+ _thread_id(thread_id)
+ {
+ copy_v3_v3(this->center, center);
+ this->radius = radius;
+ this->radius_squared = radius * radius;
+ }
+
+ brush_verts_iter(const brush_verts_iter &a)
+ {
+ copy_v3_v3(center, a.center);
+ radius = a.radius;
+ radius_squared = a.radius_squared;
+ _node = a._node;
+ brush = a.brush;
+
+ _ss = a._ss;
+ }
+
+ brush_verts_iter(bool is_end)
+ {
+ brush = nullptr;
+ }
+
+ brush_verts_iter begin()
+ {
+ brush_verts_iter ret = *this;
+ unique_verts_iter &viter = static_cast<unique_verts_iter &>(ret);
+
+ viter.reset();
+
+ return ret;
+ }
+
+ brush_verts_iter end()
+ {
+ return brush_verts_iter(true);
+ }
+
+ inline brush_verts_iter &operator++()
+ {
+ unique_verts_iter::operator++();
+
+ skip_outside();
+
+ if (!vertex) {
+ brush = nullptr;
+ }
+
+ fade = SCULPT_brush_strength_factor(
+ _ss, brush, co, 3, NULL, no, mask ? *mask : 0.0f, deprecated_vertref, _thread_id);
+
+ return *this;
+ }
+
+ inline bool operator==(const brush_verts_iter &b)
+ {
+ // detect comparison with end iterator
+ if (!brush && !b.brush) {
+ return true;
+ }
+
+ return unique_verts_iter::operator==(static_cast<const unique_verts_iter &>(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;
+
+ unique_verts_iter viter;
+
+ private:
+ inline void skip_outside()
+ {
+ while (vertex && !test_func(&test, co)) {
+ unique_verts_iter::operator*();
+ }
+ }
+
+ SculptSession *_ss;
+ PBVHNode *_node;
+ int _thread_id;
+ };
+
+ 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)
+ */
+ void forVertsInRange(
+ BrushSearchArgs args,
+ std::function<void(brush_verts_iter biter, int node_i, void *userdata)> callback,
+ std::function<void(PBVHNode *node, int node_i, void *userdata)> 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_with_falloff_shape(
+ _ss, &default_test, args.brush->falloff_shape);
+ }
+
+ SculptSearchSphereData data = {.ss = _ss,
+ .sd = _sd,
+ .radius_squared = args.radius * args.radius,
+ .original = args.use_original,
+ .center = args.center};
+
+ 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);
+
+ for (auto viter : biter) {
+ callback(viter, (int)i, args.userdata);
+ }
+
+ if (node_callback) {
+ node_callback(nodes[i], (int)i, args.userdata);
+ }
+ }
+ });
+ }
+
+ private:
+ BMesh *_bm;
+ PBVH *_pbvh;
+ SculptSession *_ss;
+ Sculpt *_sd;
+};
+
+class BMeshBackend {
+ public:
+ BMeshBackend()
+ {
+ }
+
+ private:
+};
+
+/* clang-format off */
+
+template<class PBVHClass, class V, class E, class F>
+concept PBVHBackend = requires(PBVHClass b, V v){
+ {(*(b.forAllUniqueVerts(nullptr))).vertex} -> std::same_as<V>;
+ {b.getVertexCo(v)} -> std::same_as<float*>;
+ {b.getVertexNormal(v)} -> std::same_as<const float*>;
+};
+/* clang-format on */
+
+template<class V, class E, class F, class Backend, PBVHBackend<V, E, F> PBVHClass>
+class SculptImpl {
+ public:
+ PBVHClass *pbvh;
+ SculptSession *ss;
+
+ SculptImpl(SculptSession *ss, PBVHClass *pbvh) : pbvh(pbvh), ss(ss)
+ {
+ }
+
+ /*
+ &((BrushSearchArgs *){0})
+ */
+ inline void moveVerts(float cent[3], float radius, float offset[3])
+ {
+ /* clang-format off */
+ pbvh->forVertsInRange(
+ {
+ .brush = ss->cache->brush,
+ .radius = radius,
+ .use_threads = true,
+ .use_original = false
+ },
+
+ [&offset](auto viter, int node_i, void *userdata) {
+ //add offset to vertex coordinates
+
+ madd_v3_v3fl(viter.co, offset, viter.fade);
+ printf("yay");
+ },
+
+ [](PBVHNode *node, int node_i, void *userdata) {
+ BKE_pbvh_node_mark_update(node);
+ });
+
+ /* clang-format on */
+ }
+
+ private:
+};
+
+} // namespace sculpt
+} // namespace blender
+#endif
diff --git a/source/blender/editors/sculpt_paint/sculpt_array.c b/source/blender/editors/sculpt_paint/sculpt_array.c
index d93f61168ea..69f23c2db56 100644
--- a/source/blender/editors/sculpt_paint/sculpt_array.c
+++ b/source/blender/editors/sculpt_paint/sculpt_array.c
@@ -24,8 +24,8 @@
#include "MEM_guardedalloc.h"
#include "BLI_blenlib.h"
-#include "BLI_math.h"
#include "BLI_hash.h"
+#include "BLI_math.h"
#include "BLI_task.h"
#include "DNA_brush_types.h"
@@ -37,9 +37,9 @@
#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_lib_id.h"
#include "BKE_node.h"
#include "BKE_object.h"
#include "BKE_paint.h"
@@ -62,13 +62,16 @@
#include <math.h>
#include <stdlib.h>
-
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
+#define ARRAY_INSTANCE_ORIGINAL -1
-static void sculpt_vertex_array_data_get(SculptArray *array, const int vertex, int *r_copy, int *r_symmetry_pass) {
+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;
@@ -79,34 +82,29 @@ static void sculpt_vertex_array_data_get(SculptArray *array, const int vertex, i
*r_symmetry_pass = array->symmetry_pass[vertex];
}
-
-static void sculpt_array_datalayers_add(Mesh *mesh) {
- int *v_array_instance = CustomData_add_layer_named(&mesh->vdata,
- CD_PROP_INT32,
- CD_CALLOC,
- NULL,
- mesh->totvert,
- array_instance_cd_name);
+static void sculpt_array_datalayers_add(Mesh *mesh)
+{
+ int *v_array_instance = CustomData_add_layer_named(
+ &mesh->vdata, CD_PROP_INT32, CD_CALLOC, NULL, mesh->totvert, array_instance_cd_name);
for (int i = 0; i < mesh->totvert; i++) {
v_array_instance[i] = ARRAY_INSTANCE_ORIGINAL;
}
- CustomData_add_layer_named(&mesh->vdata,
- CD_PROP_INT32,
- CD_CALLOC,
- NULL,
- mesh->totvert,
- array_symmetry_pass_cd_name);
+ CustomData_add_layer_named(
+ &mesh->vdata, CD_PROP_INT32, CD_CALLOC, NULL, mesh->totvert, array_symmetry_pass_cd_name);
}
-void SCULPT_array_datalayers_free(Object *ob) {
+void SCULPT_array_datalayers_free(Object *ob)
+{
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);
+ 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);
+ 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);
}
@@ -114,43 +112,47 @@ void SCULPT_array_datalayers_free(Object *ob) {
const float source_geometry_threshold = 0.5f;
-static BMesh *sculpt_array_source_build(Object *ob, Brush *brush, SculptArray *array) {
+static BMesh *sculpt_array_source_build(Object *ob, Brush *brush, SculptArray *array)
+{
Mesh *sculpt_mesh = BKE_object_get_original_mesh(ob);
BMesh *srcbm;
const BMAllocTemplate allocsizea = BMALLOC_TEMPLATE_FROM_ME(sculpt_mesh);
srcbm = BM_mesh_create(&allocsizea,
- &((struct BMeshCreateParams){
- .use_toolflags = true,
- }));
+ &((struct BMeshCreateParams){
+ .use_toolflags = true,
+ }));
- BM_mesh_bm_from_me(srcbm,
+ BM_mesh_bm_from_me(NULL,
+ srcbm,
sculpt_mesh,
&((struct BMeshFromMeshParams){
.calc_face_normal = true,
}));
BM_mesh_elem_table_ensure(srcbm, BM_VERT);
- BM_mesh_elem_index_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++) {
- const float automask = SCULPT_automasking_factor_get(ss->cache->automasking, ss, i);
- const float mask = 1.0f - SCULPT_vertex_mask_get(ss, i);
- 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);
- }
-
-
+ SculptVertRef vertex = BKE_pbvh_table_index_to_vertex(ss->pbvh, i);
+
+ const float automask = SCULPT_automasking_factor_get(ss->cache->automasking, ss, vertex);
+ 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;
}
@@ -160,8 +162,8 @@ static BMesh *sculpt_array_source_build(Object *ob, Brush *brush, SculptArray *a
/* 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) {
+ 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);
@@ -179,12 +181,13 @@ static BMesh *sculpt_array_source_build(Object *ob, Brush *brush, SculptArray *a
"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) {
+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(
@@ -196,17 +199,18 @@ void sculpt_array_source_datalayer_update(BMesh *bm, const int symm_pass, const
&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);
+ 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);
+ 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) {
+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);
@@ -221,25 +225,26 @@ static void sculpt_array_final_mesh_write(Object *ob, BMesh *final_mesh) {
ss->needs_pbvh_rebuild = true;
}
-static void sculpt_array_ensure_geometry_indices(Object *ob, SculptArray *array) {
+static void sculpt_array_ensure_geometry_indices(Object *ob, SculptArray *array)
+{
Mesh *mesh = BKE_object_get_original_mesh(ob);
if (array->copy_index) {
-
+
return;
}
-
-printf("ALLOCATION COPY INDEX\n");
+ printf("ALLOCATION COPY INDEX\n");
array->copy_index = MEM_malloc_arrayN(mesh->totvert, sizeof(int), "array copy index");
- array->symmetry_pass = MEM_malloc_arrayN(mesh->totvert, sizeof(int), "array symmetry pass index");
+ array->symmetry_pass = MEM_malloc_arrayN(
+ mesh->totvert, sizeof(int), "array symmetry pass index");
int *cd_array_instance = CustomData_get_layer_named(
- &mesh->vdata, CD_PROP_INT32, array_instance_cd_name);
+ &mesh->vdata, CD_PROP_INT32, array_instance_cd_name);
int *cd_array_symm_pass = CustomData_get_layer_named(
- &mesh->vdata, CD_PROP_INT32, array_symmetry_pass_cd_name);
+ &mesh->vdata, CD_PROP_INT32, array_symmetry_pass_cd_name);
if (!cd_array_instance) {
printf("ERROR 1");
@@ -253,26 +258,27 @@ printf("ALLOCATION COPY INDEX\n");
SCULPT_array_datalayers_free(ob);
}
-static void sculpt_array_mesh_build(Sculpt *sd, Object *ob, SculptArray *array) {
+static void sculpt_array_mesh_build(Sculpt *sd, Object *ob, SculptArray *array)
+{
Mesh *sculpt_mesh = BKE_object_get_original_mesh(ob);
Brush *brush = BKE_paint_brush(&sd->paint);
sculpt_array_datalayers_add(sculpt_mesh);
BMesh *srcbm = sculpt_array_source_build(ob, brush, array);
-
+
BMesh *destbm;
const BMAllocTemplate allocsizeb = BMALLOC_TEMPLATE_FROM_ME(sculpt_mesh);
destbm = BM_mesh_create(&allocsizeb,
- &((struct BMeshCreateParams){
- .use_toolflags = true,
- }));
- BM_mesh_bm_from_me(destbm,
+ &((struct BMeshCreateParams){
+ .use_toolflags = true,
+ }));
+ BM_mesh_bm_from_me(NULL,
+ destbm,
sculpt_mesh,
&((struct BMeshFromMeshParams){
.calc_face_normal = true,
}));
-
BM_mesh_elem_toolflags_ensure(destbm);
const char symm = SCULPT_mesh_symmetry_xyz_get(ob);
for (char symm_it = 0; symm_it <= symm; symm_it++) {
@@ -280,24 +286,25 @@ static void sculpt_array_mesh_build(Sculpt *sd, Object *ob, SculptArray *array)
continue;
}
- for (int copy_index = 0; copy_index < array->num_copies; copy_index++) {
- sculpt_array_source_datalayer_update(srcbm, symm_it, copy_index);
+ for (int copy_index = 0; copy_index < array->num_copies; copy_index++) {
+ sculpt_array_source_datalayer_update(srcbm, symm_it, copy_index);
- 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);
- }
+ 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);
+ }
}
sculpt_array_final_mesh_write(ob, destbm);
BM_mesh_free(srcbm);
BM_mesh_free(destbm);
-
-
}
-static SculptArray *sculpt_array_cache_create(Object *ob, eBrushArrayDeformType deform_type, const int num_copies) {
+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;
@@ -309,22 +316,25 @@ static SculptArray *sculpt_array_cache_create(Object *ob, eBrushArrayDeformType
if (!SCULPT_is_symmetry_iteration_valid(symm_it, symm)) {
continue;
}
- array->copies[symm_it] = MEM_malloc_arrayN(num_copies, sizeof(SculptArrayCopy), "Sculpt array copies");
+ array->copies[symm_it] = MEM_malloc_arrayN(
+ num_copies, sizeof(SculptArrayCopy), "Sculpt array copies");
}
return array;
}
-static void sculpt_array_cache_free(SculptArray *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_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) {
+static void sculpt_array_init(Object *ob, Brush *brush, SculptArray *array)
+{
SculptSession *ss = ob->sculpt;
/* TODO: add options. */
@@ -332,28 +342,28 @@ static void sculpt_array_init(Object *ob, Brush *brush, SculptArray *array) {
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);
- }
+ 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;
+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) {
@@ -369,7 +379,7 @@ static void sculpt_array_position_in_path_search(float *r_position, float *r_dir
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;
@@ -403,7 +413,10 @@ static void sculpt_array_position_in_path_search(float *r_position, float *r_dir
}
}
-static void scultp_array_basis_from_direction(float r_mat[4][4], SculptArray *array, const float direction[3]) {
+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);
@@ -414,11 +427,16 @@ static void scultp_array_basis_from_direction(float r_mat[4][4], SculptArray *ar
normalize_v3(r_mat[2]);
}
-static float *sculpt_array_delta_from_path(SculptArray *array) {
+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) {
+static void sculpt_array_update_copy(StrokeCache *cache,
+ SculptArray *array,
+ SculptArrayCopy *copy,
+ Brush *brush)
+{
float copy_position[3];
unit_m4(copy->mat);
@@ -430,37 +448,32 @@ static void sculpt_array_update_copy(StrokeCache *cache, SculptArray *array, Scu
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;
+ 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);
+ 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;
@@ -476,10 +489,10 @@ static void sculpt_array_update_copy(StrokeCache *cache, SculptArray *array, Scu
copy->mat[1][1] = scale;
copy->mat[2][2] = scale;
*/
-
}
-static void sculpt_array_update(Object *ob, Brush *brush, SculptArray *array) {
+static void sculpt_array_update(Object *ob, Brush *brush, SculptArray *array)
+{
SculptSession *ss = ob->sculpt;
/* Main symmetry pass. */
@@ -490,47 +503,46 @@ static void sculpt_array_update(Object *ob, Brush *brush, SculptArray *array) {
}
for (int symm_pass = 1; symm_pass < PAINT_SYMM_AREAS; symm_pass++) {
- if (array->copies[symm_pass] == NULL) {
- continue;
- }
+ if (array->copies[symm_pass] == NULL) {
+ continue;
+ }
- float symm_orig[3];
- flip_v3_v3(symm_orig, array->source_origin, symm_pass);
+ 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 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);
- }
+ 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)
+ const int n,
+ const TaskParallelTLS *__restrict tls)
{
SculptThreadedTaskData *data = userdata;
SculptSession *ss = data->ob->sculpt;
SculptArray *array = ss->array;
- Mesh *mesh = BKE_object_get_original_mesh(data->ob);
+ Mesh *mesh = BKE_object_get_original_mesh(data->ob);
- bool any_modified = false;
+ bool any_modified = false;
PBVHVertexIter vd;
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
@@ -554,7 +566,6 @@ static void do_array_deform_task_cb_ex(void *__restrict userdata,
any_modified = true;
-
if (vd.mvert) {
vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
}
@@ -566,7 +577,8 @@ static void do_array_deform_task_cb_ex(void *__restrict userdata,
}
}
-static void sculpt_array_deform(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) {
+static void sculpt_array_deform(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
+{
/* Threaded loop over nodes. */
SculptSession *ss = ob->sculpt;
SculptThreadedTaskData data = {
@@ -577,22 +589,22 @@ static void sculpt_array_deform(Sculpt *sd, Object *ob, PBVHNode **nodes, int to
TaskParallelSettings settings;
BKE_pbvh_parallel_range_settings(&settings, true, totnode);
- BLI_task_parallel_range(
- 0, totnode, &data, do_array_deform_task_cb_ex, &settings);
+ 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)
+ const int n,
+ const TaskParallelTLS *__restrict tls)
{
SculptThreadedTaskData *data = userdata;
SculptSession *ss = data->ob->sculpt;
SculptArray *array = ss->array;
- Mesh *mesh = BKE_object_get_original_mesh(data->ob);
+ Mesh *mesh = BKE_object_get_original_mesh(data->ob);
- bool any_modified = false;
+ bool any_modified = false;
+
+ bool check_fsets = ss->cache->brush->flag2 & BRUSH_SMOOTH_PRESERVE_FACE_SETS;
PBVHVertexIter vd;
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
@@ -607,40 +619,37 @@ static void do_array_smooth_task_cb_ex(void *__restrict userdata,
}
float smooth_co[3];
- SCULPT_neighbor_coords_average(ss, smooth_co, vd.index);
+ SCULPT_neighbor_coords_average(
+ ss, smooth_co, vd.vertex, ss->cache->brush->autosmooth_projection, check_fsets);
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;
+ }
-/*
- 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;
- }
- */
-
+ 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.mvert) {
vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
}
@@ -652,9 +661,8 @@ static void do_array_smooth_task_cb_ex(void *__restrict userdata,
}
}
-static void sculpt_array_smooth(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) {
-
-
+static void sculpt_array_smooth(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
+{
/* Threaded loop over nodes. */
SculptSession *ss = ob->sculpt;
@@ -675,26 +683,29 @@ static void sculpt_array_smooth(Sculpt *sd, Object *ob, PBVHNode **nodes, int to
TaskParallelSettings settings;
BKE_pbvh_parallel_range_settings(&settings, true, totnode);
- BLI_task_parallel_range(
- 0, totnode, &data, do_array_smooth_task_cb_ex, &settings);
+ 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){
+static void sculpt_array_ensure_original_coordinates(Object *ob, SculptArray *array)
+{
SculptSession *ss = ob->sculpt;
Mesh *sculpt_mesh = BKE_object_get_original_mesh(ob);
const int totvert = SCULPT_vertex_count_get(ss);
- if (array->orco) {
- return;
- }
+ if (array->orco) {
+ return;
+ }
array->orco = MEM_malloc_arrayN(sculpt_mesh->totvert, sizeof(float) * 3, "array orco");
for (int i = 0; i < totvert; i++) {
- copy_v3_v3(array->orco[i], SCULPT_vertex_co_get(ss, i));
+ SculptVertRef vertex = BKE_pbvh_table_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){
+static void sculpt_array_ensure_base_transform(Sculpt *sd, Object *ob, SculptArray *array)
+{
SculptSession *ss = ob->sculpt;
Brush *brush = BKE_paint_brush(&sd->paint);
Mesh *sculpt_mesh = BKE_object_get_original_mesh(ob);
@@ -726,7 +737,8 @@ static void sculpt_array_ensure_base_transform(Sculpt *sd, Object *ob, SculptArr
return;
}
-static void sculpt_array_path_point_update(SculptArray *array, const int path_point_index) {
+static void sculpt_array_path_point_update(SculptArray *array, const int path_point_index)
+{
if (path_point_index == 0) {
return;
}
@@ -737,14 +749,14 @@ static void sculpt_array_path_point_update(SculptArray *array, const int path_po
ScultpArrayPathPoint *prev_path_point = &array->path.points[prev_path_point_index];
if (len_v3v3(prev_path_point->co, path_point->co) <= 0.0001f) {
- return;
+ 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) {
+static void sculpt_array_stroke_sample_add(Object *ob, SculptArray *array)
+{
SculptSession *ss = ob->sculpt;
if (!array->path.points) {
@@ -753,13 +765,13 @@ static void sculpt_array_stroke_sample_add(Object *ob, SculptArray *array) {
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);
+ // 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;
@@ -781,71 +793,72 @@ 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);
+ 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");
- /* 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++) {
+ SculptVertRef vertex = BKE_pbvh_table_index_to_vertex(ss->pbvh, i);
- for (int i = 0; i < totvert; i++) {
- int array_index = ARRAY_INSTANCE_ORIGINAL;
- int array_symm_pass = 0;
- sculpt_vertex_array_data_get(array, i, &array_index, &array_symm_pass);
+ 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;
- }
+ if (array_index == ARRAY_INSTANCE_ORIGINAL) {
+ continue;
+ }
- /* TODO: this can be cached. */
- SculptVertexNeighborIter ni;
- SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, i, 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;
+ /* 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);
}
- }
- SCULPT_VERTEX_NEIGHBORS_ITER_END(ni);
- }
- for(int smooth_iterations = 0; smooth_iterations < 4; smooth_iterations++) {
- for (int i = 0; i < totvert; i++) {
- float avg = array->smooth_strength[i];
- int count = 1;
- SculptVertexNeighborIter ni;
- SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, i, ni) {
- avg += array->smooth_strength[ni.index];
- count++;
- }
- SCULPT_VERTEX_NEIGHBORS_ITER_END(ni);
- array->smooth_strength[i] = avg / count;
+ for (int smooth_iterations = 0; smooth_iterations < 4; smooth_iterations++) {
+ for (int i = 0; i < totvert; i++) {
+ SculptVertRef vertex = BKE_pbvh_table_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 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++) {
+ SculptVertRef vertex = BKE_pbvh_table_index_to_vertex(ss->pbvh, i);
- /* Update Geometry Orco. */
- for (int i = 0; i < totvert; i++) {
int array_index = ARRAY_INSTANCE_ORIGINAL;
int array_symm_pass = 0;
sculpt_vertex_array_data_get(array, i, &array_index, &array_symm_pass);
@@ -853,39 +866,38 @@ void SCULPT_do_array_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode
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, i));
- 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);
+ 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;
+ /* 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);
}
- 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. */
@@ -903,24 +915,22 @@ void SCULPT_do_array_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode
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_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 (brush->array_count == 0) {
return;
}
if (!SCULPT_stroke_is_main_symmetry_pass(ss->cache)) {
- /* This brush manages its own symmetry. */
- return;
+ /* This brush manages its own symmetry. */
+ return;
}
if (SCULPT_stroke_is_first_brush_step(ss->cache)) {
@@ -932,51 +942,49 @@ void SCULPT_do_array_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode
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;
+ /* 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_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_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_stroke_sample_add(ob, ss->array);
- sculpt_array_update(ob, brush, ss->array);
+ sculpt_array_update(ob, brush, ss->array);
- sculpt_array_deform(sd, ob, nodes, totnode);
+ sculpt_array_deform(sd, ob, nodes, totnode);
}
-void SCULPT_array_path_draw(const uint gpuattr,
- Brush *brush,
- SculptSession *ss) {
-
- SculptArray *array = ss->array;
+void SCULPT_array_path_draw(const uint gpuattr, Brush *brush, SculptSession *ss)
+{
+ SculptArray *array = ss->array;
- /* Disable debug drawing. */
- return;
+ /* Disable debug drawing. */
+ return;
- if (!array) {
- return;
- }
+ if (!array) {
+ return;
+ }
- if (!array->path.points) {
- return;
- }
+ if (!array->path.points) {
+ return;
+ }
- if (array->path.tot_points < 2) {
- 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();
-} \ No newline at end of file
+ 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.c b/source/blender/editors/sculpt_paint/sculpt_automasking.c
index 1980aa615a7..69c6434ca9b 100644
--- a/source/blender/editors/sculpt_paint/sculpt_automasking.c
+++ b/source/blender/editors/sculpt_paint/sculpt_automasking.c
@@ -73,9 +73,7 @@ AutomaskingCache *SCULPT_automasking_active_cache_get(SculptSession *ss)
return NULL;
}
-bool SCULPT_is_automasking_mode_enabled(const Sculpt *sd,
- const Brush *br,
- const eAutomasking_flag mode)
+bool SCULPT_is_automasking_mode_enabled(Sculpt *sd, const Brush *br, const eAutomasking_flag mode)
{
if (br) {
return br->automasking_flags & mode || sd->automasking_flags & mode;
@@ -83,11 +81,13 @@ bool SCULPT_is_automasking_mode_enabled(const Sculpt *sd,
return sd->automasking_flags & mode;
}
-bool SCULPT_is_automasking_enabled(const Sculpt *sd, const SculptSession *ss, const Brush *br)
+bool SCULPT_is_automasking_enabled(Sculpt *sd, const SculptSession *ss, const Brush *br)
{
+ /*
if (br && SCULPT_stroke_is_dynamic_topology(ss, br)) {
return false;
- }
+ }*/
+
if (SCULPT_is_automasking_mode_enabled(sd, br, BRUSH_AUTOMASKING_TOPOLOGY)) {
return true;
}
@@ -100,10 +100,17 @@ bool SCULPT_is_automasking_enabled(const Sculpt *sd, const SculptSession *ss, co
if (SCULPT_is_automasking_mode_enabled(sd, br, BRUSH_AUTOMASKING_BOUNDARY_FACE_SETS)) {
return true;
}
+ if (SCULPT_is_automasking_mode_enabled(sd, br, BRUSH_AUTOMASKING_CONCAVITY)) {
+ if (br && br->concave_mask_factor == 0.0f) {
+ return false;
+ }
+ return true;
+ }
+
return false;
}
-static int sculpt_automasking_mode_effective_bits(const Sculpt *sculpt, const Brush *brush)
+static int sculpt_automasking_mode_effective_bits(Sculpt *sculpt, const Brush *brush)
{
if (brush) {
return sculpt->automasking_flags | brush->automasking_flags;
@@ -111,7 +118,19 @@ static int sculpt_automasking_mode_effective_bits(const Sculpt *sculpt, const Br
return sculpt->automasking_flags;
}
-static bool SCULPT_automasking_needs_factors_cache(const Sculpt *sd, const Brush *brush)
+static float sculpt_concavity_factor(AutomaskingCache *automasking, float fac)
+{
+ if (automasking->settings.flags & BRUSH_AUTOMASKING_INVERT_CONCAVITY) {
+ fac = 1.0 - fac;
+ }
+
+ fac = pow(fac * 1.5f, (0.5f + automasking->settings.concave_factor) * 8.0);
+ CLAMP(fac, 0.0f, 1.0f);
+
+ return fac;
+}
+
+static bool SCULPT_automasking_needs_factors_cache(Sculpt *sd, const Brush *brush)
{
const int automasking_flags = sculpt_automasking_mode_effective_bits(sd, brush);
@@ -127,16 +146,37 @@ static bool SCULPT_automasking_needs_factors_cache(const Sculpt *sd, const Brush
return false;
}
-float SCULPT_automasking_factor_get(AutomaskingCache *automasking, SculptSession *ss, int vert)
+float SCULPT_automasking_factor_get(AutomaskingCache *automasking,
+ SculptSession *ss,
+ SculptVertRef vert)
{
+ float mask = 1.0f;
+ bool do_concave;
+
if (!automasking) {
- return 1.0f;
+ return mask;
}
+
+ do_concave = ss->cache && ss->cache->brush && ss->cache->brush->concave_mask_factor > 0.0f &&
+ (automasking->settings.flags & BRUSH_AUTOMASKING_CONCAVITY);
+
/* If the cache is initialized with valid info, use the cache. This is used when the
* 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 (automasking->factor) {
- return automasking->factor[vert];
+ if (automasking->factorlayer) {
+ mask = *(float *)SCULPT_temp_cdata_get(vert, automasking->factorlayer);
+ }
+
+ // don't used cached automasking factors for facesets or concave in
+ // dyntopo
+ if (automasking->factorlayer && !ss->bm) {
+ return mask;
+ }
+
+ if (do_concave) {
+ float fac = SCULPT_calc_concavity(ss, vert);
+
+ mask *= sculpt_concavity_factor(automasking, fac);
}
if (automasking->settings.flags & BRUSH_AUTOMASKING_FACE_SETS) {
@@ -146,7 +186,7 @@ float SCULPT_automasking_factor_get(AutomaskingCache *automasking, SculptSession
}
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;
}
}
@@ -157,7 +197,7 @@ float SCULPT_automasking_factor_get(AutomaskingCache *automasking, SculptSession
}
}
- return 1.0f;
+ return mask;
}
void SCULPT_automasking_cache_free(AutomaskingCache *automasking)
@@ -166,11 +206,14 @@ void SCULPT_automasking_cache_free(AutomaskingCache *automasking)
return;
}
- MEM_SAFE_FREE(automasking->factor);
+ if (automasking->factorlayer) {
+ MEM_SAFE_FREE(automasking->factorlayer);
+ }
+
MEM_SAFE_FREE(automasking);
}
-static bool sculpt_automasking_is_constrained_by_radius(Brush *br)
+static bool sculpt_automasking_is_constrained_by_radius(const Brush *br)
{
/* 2D falloff is not constrained by radius. */
if (br->falloff_shape == PAINT_FALLOFF_SHAPE_TUBE) {
@@ -184,38 +227,47 @@ static bool sculpt_automasking_is_constrained_by_radius(Brush *br)
}
typedef struct AutomaskFloodFillData {
- float *automask_factor;
+ SculptCustomLayer *factorlayer;
float radius;
bool use_radius;
float location[3];
char symm;
} AutomaskFloodFillData;
-static bool automask_floodfill_cb(
- SculptSession *ss, int from_v, int to_v, bool UNUSED(is_duplicate), void *userdata)
+static bool automask_floodfill_cb(SculptSession *ss,
+ SculptVertRef from_vref,
+ SculptVertRef to_vref,
+ bool UNUSED(is_duplicate),
+ void *userdata)
{
AutomaskFloodFillData *data = userdata;
- data->automask_factor[to_v] = 1.0f;
- data->automask_factor[from_v] = 1.0f;
+ *(float *)SCULPT_temp_cdata_get(to_vref, data->factorlayer) = 1.0f;
+ *(float *)SCULPT_temp_cdata_get(from_vref, data->factorlayer) = 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));
+ SCULPT_vertex_co_get(ss, to_vref), data->location, data->radius, data->symm));
}
-static float *SCULPT_topology_automasking_init(Sculpt *sd, Object *ob, float *automask_factor)
+static void SCULPT_topology_automasking_init(Sculpt *sd,
+ Object *ob,
+ SculptCustomLayer *factorlayer)
{
SculptSession *ss = ob->sculpt;
- Brush *brush = BKE_paint_brush(&sd->paint);
+ const Brush *brush = BKE_paint_brush(&sd->paint);
if (BKE_pbvh_type(ss->pbvh) == PBVH_FACES && !ss->pmap) {
BLI_assert_msg(0, "Topology masking: pmap missing");
- return NULL;
+ return;
}
const int totvert = SCULPT_vertex_count_get(ss);
for (int i = 0; i < totvert; i++) {
- automask_factor[i] = 0.0f;
+ SculptVertRef vertex = BKE_pbvh_table_index_to_vertex(ss->pbvh, i);
+
+ float *fac = SCULPT_temp_cdata_get(vertex, factorlayer);
+ *fac = 0.0f;
}
/* Flood fill automask to connected vertices. Limited to vertices inside
@@ -234,7 +286,7 @@ static float *SCULPT_topology_automasking_init(Sculpt *sd, Object *ob, float *au
}
AutomaskFloodFillData fdata = {
- .automask_factor = automask_factor,
+ .factorlayer = factorlayer,
.radius = radius,
.use_radius = ss->cache && sculpt_automasking_is_constrained_by_radius(brush),
.symm = symm,
@@ -242,62 +294,66 @@ static float *SCULPT_topology_automasking_init(Sculpt *sd, Object *ob, float *au
copy_v3_v3(fdata.location, SCULPT_active_vertex_co_get(ss));
SCULPT_floodfill_execute(ss, &flood, automask_floodfill_cb, &fdata);
SCULPT_floodfill_free(&flood);
-
- return automask_factor;
}
-static float *sculpt_face_sets_automasking_init(Sculpt *sd, Object *ob, float *automask_factor)
+static void sculpt_face_sets_automasking_init(Sculpt *sd,
+ Object *ob,
+ SculptCustomLayer *factorlayer)
{
SculptSession *ss = ob->sculpt;
Brush *brush = BKE_paint_brush(&sd->paint);
if (!SCULPT_is_automasking_enabled(sd, ss, brush)) {
- return NULL;
+ return;
}
if (BKE_pbvh_type(ss->pbvh) == PBVH_FACES && !ss->pmap) {
BLI_assert_msg(0, "Face Sets automasking: pmap missing");
- return NULL;
+ return;
}
int tot_vert = SCULPT_vertex_count_get(ss);
int active_face_set = SCULPT_active_face_set_get(ss);
for (int i = 0; i < tot_vert; i++) {
- if (!SCULPT_vertex_has_face_set(ss, i, active_face_set)) {
- automask_factor[i] *= 0.0f;
+ SculptVertRef vertex = BKE_pbvh_table_index_to_vertex(ss->pbvh, i);
+
+ if (!SCULPT_vertex_has_face_set(ss, vertex, active_face_set)) {
+ *(float *)SCULPT_temp_cdata_get(vertex, factorlayer) = 0.0f;
}
}
- return automask_factor;
+ return;
}
#define EDGE_DISTANCE_INF -1
-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,
+ SculptCustomLayer *factorlayer)
{
SculptSession *ss = ob->sculpt;
if (!ss->pmap) {
BLI_assert_msg(0, "Boundary Edges masking: pmap missing");
- return NULL;
+ return;
}
const int totvert = SCULPT_vertex_count_get(ss);
int *edge_distance = MEM_callocN(sizeof(int) * totvert, "automask_factor");
for (int i = 0; i < totvert; i++) {
+ SculptVertRef vertex = BKE_pbvh_table_index_to_vertex(ss->pbvh, i);
+
edge_distance[i] = EDGE_DISTANCE_INF;
switch (mode) {
case AUTOMASK_INIT_BOUNDARY_EDGES:
- if (SCULPT_vertex_is_boundary(ss, i)) {
+ if (SCULPT_vertex_is_boundary(ss, vertex, SCULPT_BOUNDARY_MESH)) {
edge_distance[i] = 0;
}
break;
case AUTOMASK_INIT_BOUNDARY_FACE_SETS:
- if (!SCULPT_vertex_has_unique_face_set(ss, i)) {
+ if (!SCULPT_vertex_has_unique_face_set(ss, vertex)) {
edge_distance[i] = 0;
}
break;
@@ -306,11 +362,13 @@ float *SCULPT_boundary_automasking_init(Object *ob,
for (int propagation_it = 0; propagation_it < propagation_steps; propagation_it++) {
for (int i = 0; i < totvert; i++) {
+ SculptVertRef vref = BKE_pbvh_table_index_to_vertex(ss->pbvh, i);
+
if (edge_distance[i] != EDGE_DISTANCE_INF) {
continue;
}
SculptVertexNeighborIter ni;
- SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, i, ni) {
+ SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vref, ni) {
if (edge_distance[ni.index] == propagation_it) {
edge_distance[i] = propagation_it + 1;
}
@@ -320,28 +378,88 @@ float *SCULPT_boundary_automasking_init(Object *ob,
}
for (int i = 0; i < totvert; i++) {
+ SculptVertRef vertex = BKE_pbvh_table_index_to_vertex(ss->pbvh, i);
+
if (edge_distance[i] == EDGE_DISTANCE_INF) {
continue;
}
const float p = 1.0f - ((float)edge_distance[i] / (float)propagation_steps);
const float edge_boundary_automask = pow2f(p);
- automask_factor[i] *= (1.0f - edge_boundary_automask);
+
+ *(float *)SCULPT_temp_cdata_get(vertex, factorlayer) *= (1.0f - edge_boundary_automask);
}
MEM_SAFE_FREE(edge_distance);
- return automask_factor;
}
static void SCULPT_automasking_cache_settings_update(AutomaskingCache *automasking,
SculptSession *ss,
Sculpt *sd,
- Brush *brush)
+ const Brush *brush)
{
automasking->settings.flags = sculpt_automasking_mode_effective_bits(sd, brush);
+
automasking->settings.initial_face_set = SCULPT_active_face_set_get(ss);
+ automasking->settings.concave_factor = brush ? brush->concave_mask_factor : 0.0f;
}
-AutomaskingCache *SCULPT_automasking_cache_init(Sculpt *sd, Brush *brush, Object *ob)
+float SCULPT_calc_concavity(SculptSession *ss, SculptVertRef vref)
+{
+ SculptVertexNeighborIter ni;
+ float co[3], tot = 0.0, elen = 0.0;
+ const float *vco = SCULPT_vertex_co_get(ss, vref);
+
+ zero_v3(co);
+
+ SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vref, ni) {
+ const float *vco2 = SCULPT_vertex_co_get(ss, ni.vertex);
+
+ elen += len_v3v3(vco, vco2);
+ add_v3_v3(co, vco2);
+ tot += 1.0f;
+ }
+ SCULPT_VERTEX_NEIGHBORS_ITER_END(ni);
+
+ if (!tot) {
+ return 0.5f;
+ }
+
+ elen /= tot;
+ mul_v3_fl(co, 1.0 / tot);
+ sub_v3_v3(co, vco);
+ mul_v3_fl(co, -1.0 / elen);
+
+ float no[3];
+ SCULPT_vertex_normal_get(ss, vref, no);
+
+ float f = dot_v3v3(co, no) * 0.5 + 0.5;
+ return 1.0 - f;
+}
+
+static void SCULPT_concavity_automasking_init(Object *ob,
+ const Brush *brush,
+ AutomaskingCache *automasking,
+ SculptCustomLayer *factorlayer)
+{
+ SculptSession *ss = ob->sculpt;
+
+ if (!ss) {
+ return;
+ }
+
+ const int totvert = SCULPT_vertex_count_get(ss);
+
+ for (int i = 0; i < totvert; i++) {
+ SculptVertRef vref = BKE_pbvh_table_index_to_vertex(ss->pbvh, i);
+ float f = SCULPT_calc_concavity(ss, vref);
+ f = sculpt_concavity_factor(automasking, f);
+
+ *(float *)SCULPT_temp_cdata_get(vref, factorlayer) *= f;
+ }
+ // BKE_pbvh_vertex_iter_begin
+}
+
+AutomaskingCache *SCULPT_automasking_cache_init(Sculpt *sd, const Brush *brush, Object *ob)
{
SculptSession *ss = ob->sculpt;
const int totvert = SCULPT_vertex_count_get(ss);
@@ -358,9 +476,30 @@ AutomaskingCache *SCULPT_automasking_cache_init(Sculpt *sd, Brush *brush, Object
return automasking;
}
- automasking->factor = MEM_malloc_arrayN(totvert, sizeof(float), "automask_factor");
+ SCULPT_vertex_random_access_ensure(ss);
+ SCULPT_face_random_access_ensure(ss);
+
+ automasking->factorlayer = MEM_callocN(sizeof(*automasking->factorlayer),
+ "automasking->factorlayer");
+
+ if (!SCULPT_temp_customlayer_get(ss,
+ ATTR_DOMAIN_POINT,
+ CD_PROP_FLOAT,
+ "__sculpt_mask_factor",
+ automasking->factorlayer,
+ false)) {
+ // failed
+ MEM_freeN(automasking->factorlayer);
+ return automasking;
+ }
+
+ // automasking->factorlayer = SCULPT_temp_customlayer_ensure()
+ // automasking->factor = MEM_malloc_arrayN(totvert, sizeof(float), "automask_factor");
for (int i = 0; i < totvert; i++) {
- automasking->factor[i] = 1.0f;
+ SculptVertRef vertex = BKE_pbvh_table_index_to_vertex(ss->pbvh, i);
+ float *f = SCULPT_temp_cdata_get(vertex, automasking->factorlayer);
+
+ *f = 1.0f;
}
const int boundary_propagation_steps = brush ?
@@ -369,22 +508,35 @@ AutomaskingCache *SCULPT_automasking_cache_init(Sculpt *sd, Brush *brush, Object
if (SCULPT_is_automasking_mode_enabled(sd, brush, BRUSH_AUTOMASKING_TOPOLOGY)) {
SCULPT_vertex_random_access_ensure(ss);
- SCULPT_topology_automasking_init(sd, ob, automasking->factor);
+ SCULPT_topology_automasking_init(sd, ob, automasking->factorlayer);
}
+
+ if (SCULPT_is_automasking_mode_enabled(sd, brush, BRUSH_AUTOMASKING_BOUNDARY_FACE_SETS)) {
+ SCULPT_vertex_random_access_ensure(ss);
+ SCULPT_boundary_automasking_init(ob,
+ AUTOMASK_INIT_BOUNDARY_FACE_SETS,
+ boundary_propagation_steps,
+ automasking->factorlayer);
+ }
+
+ // for dyntopo, only topology and fset boundary area initialized here
+ if (ss->bm) {
+ return automasking;
+ }
+
if (SCULPT_is_automasking_mode_enabled(sd, brush, BRUSH_AUTOMASKING_FACE_SETS)) {
SCULPT_vertex_random_access_ensure(ss);
- sculpt_face_sets_automasking_init(sd, ob, automasking->factor);
+ sculpt_face_sets_automasking_init(sd, ob, automasking->factorlayer);
}
if (SCULPT_is_automasking_mode_enabled(sd, brush, BRUSH_AUTOMASKING_BOUNDARY_EDGES)) {
SCULPT_vertex_random_access_ensure(ss);
SCULPT_boundary_automasking_init(
- ob, AUTOMASK_INIT_BOUNDARY_EDGES, boundary_propagation_steps, automasking->factor);
+ ob, AUTOMASK_INIT_BOUNDARY_EDGES, boundary_propagation_steps, automasking->factorlayer);
}
- if (SCULPT_is_automasking_mode_enabled(sd, brush, BRUSH_AUTOMASKING_BOUNDARY_FACE_SETS)) {
+ if (SCULPT_is_automasking_mode_enabled(sd, brush, BRUSH_AUTOMASKING_CONCAVITY)) {
SCULPT_vertex_random_access_ensure(ss);
- SCULPT_boundary_automasking_init(
- ob, AUTOMASK_INIT_BOUNDARY_FACE_SETS, boundary_propagation_steps, automasking->factor);
+ SCULPT_concavity_automasking_init(ob, brush, automasking, automasking->factorlayer);
}
return automasking;
diff --git a/source/blender/editors/sculpt_paint/sculpt_boundary.c b/source/blender/editors/sculpt_paint/sculpt_boundary.c
index c2e8f2a8d90..08efea97c3b 100644
--- a/source/blender/editors/sculpt_paint/sculpt_boundary.c
+++ b/source/blender/editors/sculpt_paint/sculpt_boundary.c
@@ -23,6 +23,8 @@
#include "MEM_guardedalloc.h"
+#include "BLI_alloca.h"
+#include "BLI_array.h"
#include "BLI_blenlib.h"
#include "BLI_edgehash.h"
#include "BLI_math.h"
@@ -37,6 +39,10 @@
#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"
@@ -55,26 +61,61 @@
#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 <math.h>
#include <stdlib.h>
+#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);
+
typedef struct BoundaryInitialVertexFloodFillData {
- int initial_vertex;
+ SculptVertRef initial_vertex;
+ int initial_vertex_index;
int boundary_initial_vertex_steps;
- int boundary_initial_vertex;
+
+ SculptVertRef boundary_initial_vertex;
+
int *floodfill_steps;
float radius_sq;
} BoundaryInitialVertexFloodFillData;
-static bool boundary_initial_vertex_floodfill_cb(
- SculptSession *ss, int from_v, int to_v, bool is_duplicate, void *userdata)
+static bool boundary_initial_vertex_floodfill_cb(SculptSession *ss,
+ SculptVertRef from_vref,
+ SculptVertRef to_vref,
+ bool is_duplicate,
+ void *userdata)
{
BoundaryInitialVertexFloodFillData *data = userdata;
- if (!SCULPT_vertex_visible_get(ss, to_v)) {
+ int to_v = BKE_pbvh_vertex_index_to_table(ss->pbvh, to_vref);
+ int from_v = BKE_pbvh_vertex_index_to_table(ss->pbvh, from_vref);
+
+ if (!SCULPT_vertex_visible_get(ss, to_vref)) {
return false;
}
@@ -85,26 +126,27 @@ static bool boundary_initial_vertex_floodfill_cb(
data->floodfill_steps[to_v] = data->floodfill_steps[from_v];
}
- if (SCULPT_vertex_is_boundary(ss, to_v)) {
+ if (SCULPT_vertex_is_boundary(ss, to_vref, SCULPT_BOUNDARY_MESH)) {
if (data->floodfill_steps[to_v] < data->boundary_initial_vertex_steps) {
data->boundary_initial_vertex_steps = data->floodfill_steps[to_v];
- data->boundary_initial_vertex = to_v;
+ data->boundary_initial_vertex = to_vref;
}
}
const float len_sq = len_squared_v3v3(SCULPT_vertex_co_get(ss, data->initial_vertex),
- SCULPT_vertex_co_get(ss, to_v));
+ SCULPT_vertex_co_get(ss, to_vref));
return len_sq < data->radius_sq;
}
/* From a vertex index anywhere in the mesh, returns the closest vertex in a mesh boundary inside
* the given radius, if it exists. */
-static int sculpt_boundary_get_closest_boundary_vertex(SculptSession *ss,
- const int initial_vertex,
- const float radius)
+static SculptVertRef sculpt_boundary_get_closest_boundary_vertex(
+ SculptSession *ss,
+ const SculptVertRef initial_vertex,
+ const int initial_vertex_index,
+ 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;
}
@@ -114,13 +156,14 @@ static int sculpt_boundary_get_closest_boundary_vertex(SculptSession *ss,
BoundaryInitialVertexFloodFillData fdata = {
.initial_vertex = initial_vertex,
- .boundary_initial_vertex = BOUNDARY_VERTEX_NONE,
+ .initial_vertex_index = initial_vertex_index,
+ .boundary_initial_vertex = {BOUNDARY_VERTEX_NONE},
.boundary_initial_vertex_steps = INT_MAX,
.radius_sq = radius * radius,
};
fdata.floodfill_steps = MEM_calloc_arrayN(
- SCULPT_vertex_count_get(ss), sizeof(int), "floodfill steps");
+ SCULPT_vertex_count_get(ss), sizeof(int) * TSTN, "floodfill steps");
SCULPT_floodfill_execute(ss, &flood, boundary_initial_vertex_floodfill_cb, &fdata);
SCULPT_floodfill_free(&flood);
@@ -134,28 +177,39 @@ static int sculpt_boundary_get_closest_boundary_vertex(SculptSession *ss,
* deformations usually need in the boundary. */
static int BOUNDARY_INDICES_BLOCK_SIZE = 300;
-static void sculpt_boundary_index_add(SculptBoundary *boundary,
- const int new_index,
+static void sculpt_boundary_index_add(SculptSession *ss,
+ SculptBoundary *boundary,
+ const SculptVertRef new_index,
const float distance,
GSet *included_vertices)
{
boundary->vertices[boundary->num_vertices] = new_index;
+ boundary->vertex_indices[boundary->num_vertices] = BKE_pbvh_vertex_index_to_table(ss->pbvh,
+ new_index);
+
if (boundary->distance) {
- boundary->distance[new_index] = distance;
+ boundary->distance[BKE_pbvh_vertex_index_to_table(ss->pbvh, new_index)] = distance;
}
if (included_vertices) {
- BLI_gset_add(included_vertices, POINTER_FROM_INT(new_index));
+ BLI_gset_add(included_vertices, POINTER_FROM_INT(new_index.i));
}
boundary->num_vertices++;
if (boundary->num_vertices >= boundary->vertices_capacity) {
boundary->vertices_capacity += BOUNDARY_INDICES_BLOCK_SIZE;
- boundary->vertices = MEM_reallocN_id(
- boundary->vertices, boundary->vertices_capacity * sizeof(int), "boundary indices");
+ boundary->vertices = MEM_reallocN_id(boundary->vertices,
+ boundary->vertices_capacity * sizeof(SculptVertRef) *
+ TSTN,
+ "boundary vertrefs");
+ boundary->vertex_indices = MEM_reallocN_id(boundary->vertex_indices,
+ boundary->vertices_capacity * sizeof(int) * TSTN,
+ "boundary indices");
}
};
-static void sculpt_boundary_preview_edge_add(SculptBoundary *boundary, const int v1, const int v2)
+static void sculpt_boundary_preview_edge_add(SculptBoundary *boundary,
+ const SculptVertRef v1,
+ const SculptVertRef v2)
{
boundary->edges[boundary->num_edges].v1 = v1;
@@ -165,7 +219,8 @@ static void sculpt_boundary_preview_edge_add(SculptBoundary *boundary, const int
if (boundary->num_edges >= boundary->edges_capacity) {
boundary->edges_capacity += BOUNDARY_INDICES_BLOCK_SIZE;
boundary->edges = MEM_reallocN_id(boundary->edges,
- boundary->edges_capacity * sizeof(SculptBoundaryPreviewEdge),
+ boundary->edges_capacity *
+ sizeof(SculptBoundaryPreviewEdge) * TSTN,
"boundary edges");
}
};
@@ -175,7 +230,7 @@ static void sculpt_boundary_preview_edge_add(SculptBoundary *boundary, const int
* as well as to check if the initial vertex is valid.
*/
static bool sculpt_boundary_is_vertex_in_editable_boundary(SculptSession *ss,
- const int initial_vertex)
+ const SculptVertRef initial_vertex)
{
if (!SCULPT_vertex_visible_get(ss, initial_vertex)) {
@@ -186,9 +241,9 @@ static bool sculpt_boundary_is_vertex_in_editable_boundary(SculptSession *ss,
int boundary_vertex_count = 0;
SculptVertexNeighborIter ni;
SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, initial_vertex, ni) {
- if (SCULPT_vertex_visible_get(ss, ni.index)) {
+ if (SCULPT_vertex_visible_get(ss, ni.vertex)) {
neighbor_count++;
- if (SCULPT_vertex_is_boundary(ss, ni.index)) {
+ if (SCULPT_vertex_is_boundary(ss, ni.vertex, SCULPT_BOUNDARY_MESH)) {
boundary_vertex_count++;
}
}
@@ -218,73 +273,362 @@ typedef struct BoundaryFloodFillData {
GSet *included_vertices;
EdgeSet *preview_edges;
- int last_visited_vertex;
+ SculptVertRef last_visited_vertex;
} BoundaryFloodFillData;
static bool boundary_floodfill_cb(
- SculptSession *ss, int from_v, int to_v, bool is_duplicate, void *userdata)
+ SculptSession *ss, SculptVertRef from_v, SculptVertRef to_v, bool is_duplicate, void *userdata)
{
BoundaryFloodFillData *data = userdata;
SculptBoundary *boundary = data->boundary;
- if (!SCULPT_vertex_is_boundary(ss, to_v)) {
+ int from_v_i = BKE_pbvh_vertex_index_to_table(ss->pbvh, from_v);
+ int to_v_i = BKE_pbvh_vertex_index_to_table(ss->pbvh, 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),
SCULPT_vertex_co_get(ss, to_v));
const float distance_boundary_to_dst = boundary->distance ?
- boundary->distance[from_v] + edge_len :
+ boundary->distance[from_v_i] + edge_len :
0.0f;
- sculpt_boundary_index_add(boundary, to_v, distance_boundary_to_dst, data->included_vertices);
- if (!is_duplicate) {
- sculpt_boundary_preview_edge_add(boundary, from_v, to_v);
- }
+ sculpt_boundary_index_add(ss, boundary, to_v, distance_boundary_to_dst, data->included_vertices);
+ // if (!is_duplicate) {
+ sculpt_boundary_preview_edge_add(boundary, from_v, to_v);
+ //}
+
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_calloc_arrayN(
+ totvert, sizeof(float) * 3, "boundary->boundary_tangents");
+
+ for (int i = 0; i < totvert; i++) {
+ float f1 = boundary->boundary_dist[i];
+
+ if (f1 == FLT_MAX) {
+ continue;
+ }
+
+ SculptVertRef vertex = BKE_pbvh_table_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_index_to_table(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_calloc_arrayN(
+ totvert, sizeof(StoredCotangentW), "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 = NULL;
+ continue;
+ }
+
+ SculptVertRef vertex = BKE_pbvh_table_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, NULL, NULL, NULL, NULL);
+ }
+}
+
+static void sculpt_boundary_indices_init(Object *ob,
+ SculptSession *ss,
SculptBoundary *boundary,
const bool init_boundary_distances,
- const int initial_boundary_index)
+ const SculptVertRef initial_boundary_index,
+ const float radius)
{
const int totvert = SCULPT_vertex_count_get(ss);
boundary->vertices = MEM_malloc_arrayN(
- BOUNDARY_INDICES_BLOCK_SIZE, sizeof(int), "boundary indices");
+ BOUNDARY_INDICES_BLOCK_SIZE, sizeof(SculptVertRef) * TSTN, "boundary vrefs");
+ boundary->vertex_indices = MEM_malloc_arrayN(
+ BOUNDARY_INDICES_BLOCK_SIZE, sizeof(int) * TSTN, "boundary indices");
+
+ boundary->sculpt_totvert = totvert;
+
if (init_boundary_distances) {
- boundary->distance = MEM_calloc_arrayN(totvert, sizeof(float), "boundary distances");
+ boundary->distance = MEM_calloc_arrayN(totvert, sizeof(float) * TSTN, "boundary distances");
}
boundary->edges = MEM_malloc_arrayN(
- BOUNDARY_INDICES_BLOCK_SIZE, sizeof(SculptBoundaryPreviewEdge), "boundary edges");
+ BOUNDARY_INDICES_BLOCK_SIZE, sizeof(SculptBoundaryPreviewEdge) * TSTN, "boundary edges");
- GSet *included_vertices = BLI_gset_int_new_ex("included vertices", BOUNDARY_INDICES_BLOCK_SIZE);
+ GSet *included_vertices = BLI_gset_ptr_new_ex("included vertices", BOUNDARY_INDICES_BLOCK_SIZE);
SculptFloodFill flood;
SCULPT_floodfill_init(ss, &flood);
boundary->initial_vertex = initial_boundary_index;
copy_v3_v3(boundary->initial_vertex_position,
SCULPT_vertex_co_get(ss, boundary->initial_vertex));
- sculpt_boundary_index_add(boundary, initial_boundary_index, 0.0f, included_vertices);
+ sculpt_boundary_index_add(ss, boundary, initial_boundary_index, 0.0f, included_vertices);
SCULPT_floodfill_add_initial(&flood, initial_boundary_index);
BoundaryFloodFillData fdata = {
.boundary = boundary,
.included_vertices = included_vertices,
- .last_visited_vertex = BOUNDARY_VERTEX_NONE,
+ .last_visited_vertex = {BOUNDARY_VERTEX_NONE},
};
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_vertices) {
+ BMVert *v = (BMVert *)BLI_gsetIterator_getKey(&gi);
+ BLI_gset_add(boundary_verts, POINTER_FROM_INT(v->head.index));
+ }
+ }
+ else {
+ boundary_verts = included_vertices;
+ }
+
+ boundary->boundary_closest = MEM_calloc_arrayN(
+ totvert, sizeof(SculptVertRef), "boundary_closest");
+ boundary->boundary_dist = SCULPT_geodesic_distances_create(
+ ob, boundary_verts, radius, boundary->boundary_closest, NULL);
+
+ 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;
+ }
+
+ SculptVertRef vertex = BKE_pbvh_table_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_calloc_arrayN(
+ totvert, sizeof(float) * 3, "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;
+ }
+
+ SculptVertRef vertex = BKE_pbvh_table_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;
+ int j = 0;
+ SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vertex, ni) {
+ if (boundary->boundary_dist[ni.index] == FLT_MAX) {
+ j++;
+ continue;
+ }
+
+ add_v3_v3(tan, boundary->boundary_tangents[ni.index]);
+
+ tot += 1.0f;
+ j++;
+ }
+ 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_vertices) {
+ BLI_gset_free(boundary_verts, NULL);
+ }
+
/* Check if the boundary loops into itself and add the extra preview edge to close the loop. */
- if (fdata.last_visited_vertex != BOUNDARY_VERTEX_NONE &&
+ if (fdata.last_visited_vertex.i != BOUNDARY_VERTEX_NONE &&
sculpt_boundary_is_vertex_in_editable_boundary(ss, fdata.last_visited_vertex)) {
SculptVertexNeighborIter ni;
SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, fdata.last_visited_vertex, ni) {
- if (BLI_gset_haskey(included_vertices, POINTER_FROM_INT(ni.index)) &&
- sculpt_boundary_is_vertex_in_editable_boundary(ss, ni.index)) {
- sculpt_boundary_preview_edge_add(boundary, fdata.last_visited_vertex, ni.index);
+ if (BLI_gset_haskey(included_vertices, POINTER_FROM_INT(ni.vertex.i)) &&
+ sculpt_boundary_is_vertex_in_editable_boundary(ss, ni.vertex)) {
+ sculpt_boundary_preview_edge_add(boundary, fdata.last_visited_vertex, ni.vertex);
boundary->forms_loop = true;
}
}
@@ -294,6 +638,54 @@ static void sculpt_boundary_indices_init(SculptSession *ss,
BLI_gset_free(included_vertices, NULL);
}
+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_GET_VOID_P(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
@@ -302,7 +694,7 @@ static void sculpt_boundary_indices_init(SculptSession *ss,
*/
static void sculpt_boundary_edit_data_init(SculptSession *ss,
SculptBoundary *boundary,
- const int initial_vertex,
+ const SculptVertRef initial_vertex,
const float radius)
{
const int totvert = SCULPT_vertex_count_get(ss);
@@ -310,22 +702,25 @@ static void sculpt_boundary_edit_data_init(SculptSession *ss,
const bool has_duplicates = BKE_pbvh_type(ss->pbvh) == PBVH_GRIDS;
boundary->edit_info = MEM_malloc_arrayN(
- totvert, sizeof(SculptBoundaryEditInfo), "Boundary edit info");
+ totvert, sizeof(SculptBoundaryEditInfo) * TSTN, "Boundary edit info");
for (int i = 0; i < totvert; i++) {
- boundary->edit_info[i].original_vertex = BOUNDARY_VERTEX_NONE;
+ boundary->edit_info[i].original_vertex.i = BOUNDARY_VERTEX_NONE;
+ boundary->edit_info[i].original_vertex_i = BOUNDARY_VERTEX_NONE;
boundary->edit_info[i].num_propagation_steps = BOUNDARY_STEPS_NONE;
}
- GSQueue *current_iteration = BLI_gsqueue_new(sizeof(int));
- GSQueue *next_iteration = BLI_gsqueue_new(sizeof(int));
+ GSQueue *current_iteration = BLI_gsqueue_new(sizeof(SculptVertRef));
+ GSQueue *next_iteration = BLI_gsqueue_new(sizeof(SculptVertRef));
/* Initialized the first iteration with the vertices already in the boundary. This is propagation
* step 0. */
BLI_bitmap *visited_vertices = BLI_BITMAP_NEW(SCULPT_vertex_count_get(ss), "visited_vertices");
for (int i = 0; i < boundary->num_vertices; i++) {
- boundary->edit_info[boundary->vertices[i]].original_vertex = boundary->vertices[i];
- boundary->edit_info[boundary->vertices[i]].num_propagation_steps = 0;
+ boundary->edit_info[boundary->vertex_indices[i]].original_vertex = boundary->vertices[i];
+ boundary->edit_info[boundary->vertex_indices[i]].original_vertex_i =
+ boundary->vertex_indices[i];
+ boundary->edit_info[boundary->vertex_indices[i]].num_propagation_steps = 0;
/* This ensures that all duplicate vertices in the boundary have the same original_vertex
* index, so the deformation for them will be the same. */
@@ -333,7 +728,10 @@ static void sculpt_boundary_edit_data_init(SculptSession *ss,
SculptVertexNeighborIter ni_duplis;
SCULPT_VERTEX_DUPLICATES_AND_NEIGHBORS_ITER_BEGIN (ss, boundary->vertices[i], ni_duplis) {
if (ni_duplis.is_duplicate) {
- boundary->edit_info[ni_duplis.index].original_vertex = boundary->vertices[i];
+ int index = ni_duplis.index;
+
+ boundary->edit_info[index].original_vertex = boundary->vertices[i];
+ boundary->edit_info[index].original_vertex_i = boundary->vertex_indices[i];
}
}
SCULPT_VERTEX_NEIGHBORS_ITER_END(ni_duplis);
@@ -354,31 +752,36 @@ static void sculpt_boundary_edit_data_init(SculptSession *ss,
}
while (!BLI_gsqueue_is_empty(current_iteration)) {
- int from_v;
+ SculptVertRef from_v;
BLI_gsqueue_pop(current_iteration, &from_v);
+ const int from_v_i = BKE_pbvh_vertex_index_to_table(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.index);
+ const bool is_visible = SCULPT_vertex_visible_get(ss, ni.vertex);
+
if (!is_visible ||
boundary->edit_info[ni.index].num_propagation_steps != BOUNDARY_STEPS_NONE) {
continue;
}
boundary->edit_info[ni.index].original_vertex =
- boundary->edit_info[from_v].original_vertex;
+ boundary->edit_info[from_v_i].original_vertex;
+
+ boundary->edit_info[ni.index].original_vertex_i =
+ boundary->edit_info[from_v_i].original_vertex_i;
BLI_BITMAP_ENABLE(visited_vertices, ni.index);
if (ni.is_duplicate) {
/* Grids duplicates handling. */
boundary->edit_info[ni.index].num_propagation_steps =
- boundary->edit_info[from_v].num_propagation_steps;
+ boundary->edit_info[from_v_i].num_propagation_steps;
}
else {
boundary->edit_info[ni.index].num_propagation_steps =
- boundary->edit_info[from_v].num_propagation_steps + 1;
+ boundary->edit_info[from_v_i].num_propagation_steps + 1;
- BLI_gsqueue_push(next_iteration, &ni.index);
+ BLI_gsqueue_push(next_iteration, &ni.vertex);
/* When copying the data to the neighbor for the next iteration, it has to be copied to
* all its duplicates too. This is because it is not possible to know if the updated
@@ -386,12 +789,14 @@ static void sculpt_boundary_edit_data_init(SculptSession *ss,
* copy the data in the from_v neighbor iterator. */
if (has_duplicates) {
SculptVertexNeighborIter ni_duplis;
- SCULPT_VERTEX_DUPLICATES_AND_NEIGHBORS_ITER_BEGIN (ss, ni.index, ni_duplis) {
+ SCULPT_VERTEX_DUPLICATES_AND_NEIGHBORS_ITER_BEGIN (ss, ni.vertex, ni_duplis) {
if (ni_duplis.is_duplicate) {
boundary->edit_info[ni_duplis.index].original_vertex =
- boundary->edit_info[from_v].original_vertex;
+ boundary->edit_info[from_v_i].original_vertex;
+ boundary->edit_info[ni_duplis.index].original_vertex_i =
+ boundary->edit_info[from_v_i].original_vertex_i;
boundary->edit_info[ni_duplis.index].num_propagation_steps =
- boundary->edit_info[from_v].num_propagation_steps + 1;
+ boundary->edit_info[from_v_i].num_propagation_steps + 1;
}
}
SCULPT_VERTEX_NEIGHBORS_ITER_END(ni_duplis);
@@ -399,11 +804,11 @@ static void sculpt_boundary_edit_data_init(SculptSession *ss,
/* Check the distance using the vertex that was propagated from the initial vertex that
* was used to initialize the boundary. */
- if (boundary->edit_info[from_v].original_vertex == initial_vertex) {
- boundary->pivot_vertex = ni.index;
- copy_v3_v3(boundary->initial_pivot_position, SCULPT_vertex_co_get(ss, ni.index));
+ if (boundary->edit_info[from_v_i].original_vertex.i == initial_vertex.i) {
+ boundary->pivot_vertex = ni.vertex;
+ copy_v3_v3(boundary->initial_pivot_position, SCULPT_vertex_co_get(ss, ni.vertex));
accum_distance += len_v3v3(SCULPT_vertex_co_get(ss, from_v),
- SCULPT_vertex_co_get(ss, ni.index));
+ SCULPT_vertex_co_get(ss, ni.vertex));
}
}
}
@@ -412,7 +817,7 @@ static void sculpt_boundary_edit_data_init(SculptSession *ss,
/* Copy the new vertices to the queue to be processed in the next iteration. */
while (!BLI_gsqueue_is_empty(next_iteration)) {
- int next_v;
+ SculptVertRef next_v;
BLI_gsqueue_pop(next_iteration, &next_v);
BLI_gsqueue_push(current_iteration, &next_v);
}
@@ -443,7 +848,7 @@ static void sculpt_boundary_falloff_factor_init(SculptSession *ss,
brush, boundary->edit_info[i].num_propagation_steps, boundary->max_propagation_steps);
}
- if (boundary->edit_info[i].original_vertex == boundary->initial_vertex) {
+ if (boundary->edit_info[i].original_vertex.i == boundary->initial_vertex.i) {
/* All vertices that are propagated from the original vertex won't be affected by the
* boundary falloff, so there is no need to calculate anything else. */
continue;
@@ -455,7 +860,8 @@ static void sculpt_boundary_falloff_factor_init(SculptSession *ss,
continue;
}
- const float boundary_distance = boundary->distance[boundary->edit_info[i].original_vertex];
+ const float boundary_distance = boundary->distance[BKE_pbvh_vertex_index_to_table(
+ ss->pbvh, boundary->edit_info[i].original_vertex)];
float falloff_distance = 0.0f;
float direction = 1.0f;
@@ -491,22 +897,27 @@ static void sculpt_boundary_falloff_factor_init(SculptSession *ss,
* return NULL if there is no boundary from the given vertex using the given radius. */
SculptBoundary *SCULPT_boundary_data_init(Object *object,
Brush *brush,
- const int initial_vertex,
+ const SculptVertRef initial_vertex,
const float radius)
{
SculptSession *ss = object->sculpt;
- if (initial_vertex == BOUNDARY_VERTEX_NONE) {
+ if (initial_vertex.i == BOUNDARY_VERTEX_NONE) {
return NULL;
}
+ // 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);
- const int boundary_initial_vertex = sculpt_boundary_get_closest_boundary_vertex(
- ss, initial_vertex, radius);
+ const SculptVertRef boundary_initial_vertex = sculpt_boundary_get_closest_boundary_vertex(
+ ss, initial_vertex, BKE_pbvh_vertex_index_to_table(ss->pbvh, initial_vertex), radius);
- if (boundary_initial_vertex == BOUNDARY_VERTEX_NONE) {
+ if (boundary_initial_vertex.i == BOUNDARY_VERTEX_NONE) {
return NULL;
}
@@ -516,17 +927,23 @@ SculptBoundary *SCULPT_boundary_data_init(Object *object,
return NULL;
}
- SculptBoundary *boundary = MEM_callocN(sizeof(SculptBoundary), "Boundary edit data");
+ SculptBoundary *boundary = MEM_callocN(sizeof(SculptBoundary) * TSTN, "Boundary edit data");
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;
}
@@ -535,68 +952,573 @@ void SCULPT_boundary_data_free(SculptBoundary *boundary)
MEM_SAFE_FREE(boundary->vertices);
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 {
+ SculptVertRef v;
+ float co[3];
+
+ struct {
+ SculptVertRef v1, v2;
+ } edge;
+
+ float t, f;
+ bool has_edge;
+} ScalarFieldWalkData;
+
+static void sculpt_walk_scalar_field_init(SculptSession *ss,
+ SculptVertRef 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_index_to_table(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;
+ SculptVertRef v = wd->v, minv1 = {-1LL}, minv2 = {-1LL};
+ float mindis1 = FLT_MAX, mindis2 = FLT_MAX;
+ float minf1 = 0.0, minf2 = 0.0;
+ float minl1 = 0.0, minl2 = 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;
+ minl1 = len;
+ 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 len2 = len_v3v3(co3, wd->co);
+ float dist2 = f3 * len; // wd->f + (f2 - wd->f) * len;
+
+ if (dist2 < mindis2) {
+ mindis2 = dist2;
+ minf2 = f3;
+ minl2 = len2;
+ 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);
+
+ View3D *v3d = CTX_wm_view3d(C);
+ unsigned short local_view_bits = (v3d && v3d->localvd) ? v3d->local_view_uuid : 0;
+
+ 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->obmat);
+
+ 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;
+ }
+
+ 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);
+
+ Mesh *me = (Mesh *)ob->data;
+
+ BM_mesh_bm_to_me(bmain,
+ NULL,
+ bm,
+ me,
+ (&(struct BMeshToMeshParams){.calc_object_remap = false,
+ .update_shapekey_indices = false,
+ .copy_temp_cdlayers = false}));
+
+ 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 = MEM_calloc_arrayN(
totvert, 3 * sizeof(float), "pivot rotation axis");
boundary->bend.pivot_positions = MEM_calloc_arrayN(
- totvert, 3 * sizeof(float), "pivot positions");
+ totvert, 4 * sizeof(float), "pivot positions");
+
+ for (int i = 0; i < totvert; i++) {
+ boundary->bend.pivot_positions[i][3] = 0.0f;
+ }
for (int i = 0; i < totvert; i++) {
+ SculptVertRef vertex = BKE_pbvh_table_index_to_vertex(ss->pbvh, i);
+
+#ifdef VISBM
+ if (boundary->boundary_dist[i] != FLT_MAX) {
+ const float *co1 = SCULPT_vertex_co_get(ss, vertex);
+ float *dir = boundary->boundary_tangents[i];
+
+ BMVert *v1, *v2;
+
+ float tmp[3];
+ madd_v3_v3v3fl(tmp, co1, dir, 0.35);
+
+ v1 = BM_vert_create(visbm, co1, NULL, BM_CREATE_NOP);
+ v2 = BM_vert_create(visbm, tmp, NULL, BM_CREATE_NOP);
+ BM_edge_create(visbm, v1, v2, NULL, BM_CREATE_NOP);
+ }
+#endif
+
+ if (boundary->boundary_closest[i].i != -1LL) {
+ SculptVertRef v = boundary->boundary_closest[i];
+ boundary->edit_info[i].original_vertex = v;
+ boundary->edit_info[i].original_vertex_i = BKE_pbvh_vertex_index_to_table(ss->pbvh, v);
+ }
+
if (boundary->edit_info[i].num_propagation_steps != boundary->max_propagation_steps) {
continue;
}
+ }
+
+ for (int i = 0; i < totvert; i++) {
+ SculptVertRef vertex = BKE_pbvh_table_index_to_vertex(ss->pbvh, i);
+
+ if (boundary->edit_info[i].original_vertex_i == BOUNDARY_VERTEX_NONE) {
+ continue;
+ }
+
+ if (boundary->edit_info[i].num_propagation_steps != boundary->max_propagation_steps) {
+ continue;
+ }
+
+ const float *co1 = SCULPT_vertex_co_get(ss, vertex);
+
float dir[3];
float normal[3];
- SCULPT_vertex_normal_get(ss, i, normal);
- sub_v3_v3v3(dir,
- SCULPT_vertex_co_get(ss, boundary->edit_info[i].original_vertex),
- SCULPT_vertex_co_get(ss, i));
+ SCULPT_vertex_normal_get(ss, vertex, normal);
+ 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) {
+ float f1 = boundary->boundary_dist[i];
+
+ 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], dir, normal);
- normalize_v3(boundary->bend.pivot_rotation_axis[boundary->edit_info[i].original_vertex]);
- copy_v3_v3(boundary->bend.pivot_positions[boundary->edit_info[i].original_vertex],
- SCULPT_vertex_co_get(ss, i));
+ 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]);
+
+ const float *oco = SCULPT_vertex_co_get(ss, boundary->edit_info[i].original_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->num_vertices; vi++) {
+ SculptVertRef v = boundary->vertices[vi];
+ const float *co1 = SCULPT_vertex_co_get(ss, v);
+ int i = BKE_pbvh_vertex_index_to_table(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].num_propagation_steps != boundary->max_propagation_steps) {
+ continue;
+ }
+
+ SculptVertRef v2 = BKE_pbvh_table_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++) {
+ SculptVertRef vertex = BKE_pbvh_table_index_to_vertex(ss->pbvh, i);
+ const float *co1 = SCULPT_vertex_co_get(ss, vertex);
+ float dir[3];
+
if (boundary->edit_info[i].num_propagation_steps == BOUNDARY_STEPS_NONE) {
continue;
}
- copy_v3_v3(boundary->bend.pivot_positions[i],
- boundary->bend.pivot_positions[boundary->edit_info[i].original_vertex]);
- copy_v3_v3(boundary->bend.pivot_rotation_axis[i],
- boundary->bend.pivot_rotation_axis[boundary->edit_info[i].original_vertex]);
+
+ 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, NULL, BM_CREATE_NOP);
+
+ v2 = BM_vert_create(visbm, pos, NULL, BM_CREATE_NOP);
+ BM_edge_create(visbm, v1, v2, NULL, BM_CREATE_NOP);
+
+ float tmp[3];
+ madd_v3_v3v3fl(tmp, co1, dir, 0.35);
+
+ v2 = BM_vert_create(visbm, tmp, NULL, BM_CREATE_NOP);
+ BM_edge_create(visbm, v1, v2, NULL, BM_CREATE_NOP);
+ }
+#endif
}
+
+#ifdef VISBM
+ end_vis_object(ss, visob, visbm);
+#endif
}
static void sculpt_boundary_slide_data_init(SculptSession *ss, SculptBoundary *boundary)
{
const int totvert = SCULPT_vertex_count_get(ss);
- boundary->slide.directions = MEM_calloc_arrayN(totvert, 3 * sizeof(float), "slide directions");
+ boundary->slide.directions = MEM_calloc_arrayN(
+ totvert, 3 * sizeof(float) * TSTN, "slide directions");
for (int i = 0; i < totvert; i++) {
+ SculptVertRef vertex = BKE_pbvh_table_index_to_vertex(ss->pbvh, i);
+
if (boundary->edit_info[i].num_propagation_steps != boundary->max_propagation_steps) {
continue;
}
- sub_v3_v3v3(boundary->slide.directions[boundary->edit_info[i].original_vertex],
+
+ 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, i));
- normalize_v3(boundary->slide.directions[boundary->edit_info[i].original_vertex]);
+ SCULPT_vertex_co_get(ss, vertex));
+ normalize_v3(boundary->slide.directions[boundary->edit_info[i].original_vertex_i]);
}
for (int i = 0; i < totvert; i++) {
@@ -604,7 +1526,7 @@ static void sculpt_boundary_slide_data_init(SculptSession *ss, SculptBoundary *b
continue;
}
copy_v3_v3(boundary->slide.directions[i],
- boundary->slide.directions[boundary->edit_info[i].original_vertex]);
+ boundary->slide.directions[boundary->edit_info[i].original_vertex_i]);
}
}
@@ -612,7 +1534,7 @@ static void sculpt_boundary_twist_data_init(SculptSession *ss, SculptBoundary *b
{
zero_v3(boundary->twist.pivot_position);
float(*poly_verts)[3] = MEM_malloc_arrayN(
- boundary->num_vertices, sizeof(float) * 3, "poly verts");
+ boundary->num_vertices, sizeof(float) * 3 * TSTN, "poly verts");
for (int i = 0; i < boundary->num_vertices; i++) {
add_v3_v3(boundary->twist.pivot_position, SCULPT_vertex_co_get(ss, boundary->vertices[i]));
copy_v3_v3(poly_verts[i], SCULPT_vertex_co_get(ss, boundary->vertices[i]));
@@ -645,7 +1567,10 @@ static void sculpt_boundary_circle_data_init(SculptSession *ss, SculptBoundary *
if (propagation_step_index == -1) {
continue;
}
- add_v3_v3(boundary->circle.origin[propagation_step_index], SCULPT_vertex_co_get(ss, i));
+
+ SculptVertRef vertex = BKE_pbvh_table_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]++;
}
@@ -658,8 +1583,11 @@ static void sculpt_boundary_circle_data_init(SculptSession *ss, SculptBoundary *
if (propagation_step_index == -1) {
continue;
}
+
+ SculptVertRef vertex = BKE_pbvh_table_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, i));
+ boundary->circle.origin[propagation_step_index], SCULPT_vertex_co_get(ss, vertex));
}
for (int i = 0; i < totcircles; i++) {
@@ -698,7 +1626,7 @@ static void do_boundary_brush_bend_task_cb_ex(void *__restrict userdata,
PBVHVertexIter vd;
SculptOrigVertData orig_data;
- SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]);
+ SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COORDS);
const float disp = strength * sculpt_boundary_displacement_from_grab_delta_get(ss, boundary);
float angle_factor = disp / ss->cache->radius;
@@ -713,16 +1641,17 @@ static void do_boundary_brush_bend_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;
}
const float mask = vd.mask ? 1.0f - *vd.mask : 1.0f;
- const float automask = SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.index);
+ const float automask = SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.vertex);
float t_orig_co[3];
float *target_co = SCULPT_brush_deform_target_vertex_co_get(ss, brush->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,
@@ -752,7 +1681,7 @@ static void do_boundary_brush_slide_task_cb_ex(void *__restrict userdata,
PBVHVertexIter vd;
SculptOrigVertData orig_data;
- SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]);
+ SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COORDS);
const float disp = sculpt_boundary_displacement_from_grab_delta_get(ss, boundary);
@@ -761,14 +1690,14 @@ static void do_boundary_brush_slide_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;
}
const float mask = vd.mask ? 1.0f - *vd.mask : 1.0f;
- const float automask = SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.index);
+ const float automask = SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.vertex);
float *target_co = SCULPT_brush_deform_target_vertex_co_get(ss, brush->deform_target, &vd);
madd_v3_v3v3fl(target_co,
orig_data.co,
@@ -798,7 +1727,7 @@ static void do_boundary_brush_inflate_task_cb_ex(void *__restrict userdata,
PBVHVertexIter vd;
SculptOrigVertData orig_data;
- SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]);
+ SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COORDS);
const float disp = sculpt_boundary_displacement_from_grab_delta_get(ss, boundary);
@@ -807,14 +1736,14 @@ static void do_boundary_brush_inflate_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;
}
const float mask = vd.mask ? 1.0f - *vd.mask : 1.0f;
- const float automask = SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.index);
+ const float automask = SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.vertex);
float normal[3];
normal_short_to_float_v3(normal, orig_data.no);
float *target_co = SCULPT_brush_deform_target_vertex_co_get(ss, brush->deform_target, &vd);
@@ -846,21 +1775,21 @@ static void do_boundary_brush_grab_task_cb_ex(void *__restrict userdata,
PBVHVertexIter vd;
SculptOrigVertData orig_data;
- SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]);
+ SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COORDS);
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
if (boundary->edit_info[vd.index].num_propagation_steps == -1) {
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;
}
const float mask = vd.mask ? 1.0f - *vd.mask : 1.0f;
- const float automask = SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.index);
+ const float automask = SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.vertex);
float *target_co = SCULPT_brush_deform_target_vertex_co_get(ss, brush->deform_target, &vd);
madd_v3_v3v3fl(target_co,
orig_data.co,
@@ -889,7 +1818,7 @@ static void do_boundary_brush_twist_task_cb_ex(void *__restrict userdata,
PBVHVertexIter vd;
SculptOrigVertData orig_data;
- SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]);
+ SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COORDS);
const float disp = strength * sculpt_boundary_displacement_from_grab_delta_get(ss, boundary);
float angle_factor = disp / ss->cache->radius;
@@ -904,14 +1833,14 @@ static void do_boundary_brush_twist_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;
}
const float mask = vd.mask ? 1.0f - *vd.mask : 1.0f;
- const float automask = SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.index);
+ const float automask = SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.vertex);
float t_orig_co[3];
float *target_co = SCULPT_brush_deform_target_vertex_co_get(ss, brush->deform_target, &vd);
sub_v3_v3v3(t_orig_co, orig_data.co, boundary->twist.pivot_position);
@@ -943,14 +1872,14 @@ static void do_boundary_brush_smooth_task_cb_ex(void *__restrict userdata,
PBVHVertexIter vd;
SculptOrigVertData orig_data;
- SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]);
+ SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COORDS);
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
if (boundary->edit_info[vd.index].num_propagation_steps == -1) {
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;
@@ -960,9 +1889,9 @@ static void do_boundary_brush_smooth_task_cb_ex(void *__restrict userdata,
int total_neighbors = 0;
const int current_propagation_steps = boundary->edit_info[vd.index].num_propagation_steps;
SculptVertexNeighborIter ni;
- SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.index, ni) {
+ SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.vertex, ni) {
if (current_propagation_steps == boundary->edit_info[ni.index].num_propagation_steps) {
- add_v3_v3(coord_accum, SCULPT_vertex_co_get(ss, ni.index));
+ add_v3_v3(coord_accum, SCULPT_vertex_co_get(ss, ni.vertex));
total_neighbors++;
}
}
@@ -1002,14 +1931,14 @@ static void do_boundary_brush_circle_task_cb_ex(void *__restrict userdata,
PBVHVertexIter vd;
SculptOrigVertData orig_data;
- SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]);
+ SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COORDS);
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
if (boundary->edit_info[vd.index].num_propagation_steps == -1) {
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;
@@ -1028,7 +1957,7 @@ static void do_boundary_brush_circle_task_cb_ex(void *__restrict userdata,
sub_v3_v3v3(disp, target_circle_co, vd.co);
float *target_co = SCULPT_brush_deform_target_vertex_co_get(ss, brush->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.index);
+ const float automask = SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.vertex);
madd_v3_v3v3fl(target_co,
vd.co,
disp,
@@ -1041,16 +1970,127 @@ static void do_boundary_brush_circle_task_cb_ex(void *__restrict userdata,
BKE_pbvh_vertex_iter_end;
}
+static void SCULPT_boundary_autosmooth(SculptSession *ss, SculptBoundary *boundary)
+{
+ const int totvert = SCULPT_vertex_count_get(ss);
+ 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);
+
+ 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].num_propagation_steps == 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, ss->cache->brush->autosmooth_projection, NULL, false);
+
+ float *co = SCULPT_brush_deform_target_vertex_co_get(
+ ss, ss->cache->brush->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_calloc_arrayN(totvert, sizeof(float) * 3, "boundary->smoothco");
+
+ const float projection = 0.5f;
+
+ 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, NULL, false);
+
+ float *co = SCULPT_brush_deform_target_vertex_co_get(
+ ss, ss->cache->brush->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 * (1.0f + brush->boundary_offset) : radius;
+
const int symm_area = ss->cache->mirror_symmetry_pass;
if (SCULPT_stroke_is_first_brush_step_of_symmetry_pass(ss->cache)) {
- int initial_vertex;
+ SculptVertRef initial_vertex;
+
if (ss->cache->mirror_symmetry_pass == 0) {
initial_vertex = SCULPT_active_vertex_get(ss);
}
@@ -1065,10 +2105,9 @@ void SCULPT_do_boundary_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totn
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]);
@@ -1088,6 +2127,33 @@ 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);
}
+
+ 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);
+ }
+ }
+ }
}
/* No active boundary under the cursor. */
@@ -1128,6 +2194,12 @@ void SCULPT_do_boundary_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totn
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]);
+ }
}
void SCULPT_boundary_edges_preview_draw(const uint gpuattr,
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
--- /dev/null
+++ b/source/blender/editors/sculpt_paint/sculpt_brush_machine.c
diff --git a/source/blender/editors/sculpt_paint/sculpt_cloth.c b/source/blender/editors/sculpt_paint/sculpt_cloth.c
index 856a2652e21..d5ea08da5dd 100644
--- a/source/blender/editors/sculpt_paint/sculpt_cloth.c
+++ b/source/blender/editors/sculpt_paint/sculpt_cloth.c
@@ -223,26 +223,32 @@ static void cloth_brush_reallocate_constraints(SculptClothSimulation *cloth_sim)
static void cloth_brush_add_length_constraint(SculptSession *ss,
SculptClothSimulation *cloth_sim,
const int node_index,
- const int v1,
- const int v2,
+ const int v1i,
+ const int v2i,
const bool use_persistent)
{
SculptClothLengthConstraint *length_constraint =
&cloth_sim->length_constraints[cloth_sim->tot_length_constraints];
- length_constraint->elem_index_a = v1;
- length_constraint->elem_index_b = v2;
+ SculptVertRef v1, v2;
+
+ v1 = BKE_pbvh_table_index_to_vertex(ss->pbvh, v1i);
+ v2 = BKE_pbvh_table_index_to_vertex(ss->pbvh, v2i);
+
+ length_constraint->elem_index_a = v1i;
+ length_constraint->elem_index_b = v2i;
length_constraint->node = node_index;
- length_constraint->elem_position_a = cloth_sim->pos[v1];
- length_constraint->elem_position_b = cloth_sim->pos[v2];
+ length_constraint->elem_position_a = cloth_sim->pos[v1i];
+ length_constraint->elem_position_b = cloth_sim->pos[v2i];
length_constraint->type = SCULPT_CLOTH_CONSTRAINT_STRUCTURAL;
if (use_persistent) {
- length_constraint->length = len_v3v3(SCULPT_vertex_persistent_co_get(ss, v1),
- SCULPT_vertex_persistent_co_get(ss, v2));
+ length_constraint->length = len_v3v3(
+ SCULPT_vertex_persistent_co_get(ss, v1, cloth_sim->cd_pers_co),
+ SCULPT_vertex_persistent_co_get(ss, v2, cloth_sim->cd_pers_no));
}
else {
length_constraint->length = len_v3v3(SCULPT_vertex_co_get(ss, v1),
@@ -256,7 +262,7 @@ static void cloth_brush_add_length_constraint(SculptSession *ss,
cloth_brush_reallocate_constraints(cloth_sim);
/* Add the constraint to the #GSet to avoid creating it again. */
- BLI_edgeset_add(cloth_sim->created_length_constraints, v1, v2);
+ BLI_edgeset_add(cloth_sim->created_length_constraints, v1i, v2i);
}
static void cloth_brush_add_softbody_constraint(SculptClothSimulation *cloth_sim,
@@ -390,7 +396,7 @@ static void do_cloth_brush_build_constraints_task_cb_ex(
int tot_indices = 0;
build_indices[tot_indices] = vd.index;
tot_indices++;
- SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.index, ni) {
+ SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.vertex, ni) {
build_indices[tot_indices] = ni.index;
tot_indices++;
}
@@ -584,7 +590,7 @@ static void do_cloth_brush_apply_forces_task_cb_ex(void *__restrict userdata,
vd.no,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
- vd.index,
+ vd.vertex,
thread_id);
float brush_disp[3];
@@ -809,10 +815,10 @@ static void cloth_brush_solve_collision(Object *object,
}
static void cloth_simulation_noise_get(float *r_noise,
SculptSession *ss,
- const int index,
+ const SculptVertRef vertex,
const float strength)
{
- const uint *hash_co = (const uint *)SCULPT_vertex_co_get(ss, index);
+ const uint *hash_co = (const uint *)SCULPT_vertex_co_get(ss, vertex);
for (int i = 0; i < 3; i++) {
const uint hash = BLI_hash_int_2d(hash_co[0], hash_co[1]) ^ BLI_hash_int_2d(hash_co[2], i);
r_noise[i] = (hash * (1.0f / 0xFFFFFFFF) - 0.5f) * strength;
@@ -860,7 +866,7 @@ static void do_cloth_brush_solve_simulation_task_cb_ex(
mul_v3_fl(pos_diff, (1.0f - cloth_sim->damping) * sim_factor);
const float mask_v = (1.0f - (vd.mask ? *vd.mask : 0.0f)) *
- SCULPT_automasking_factor_get(automasking, ss, vd.index);
+ SCULPT_automasking_factor_get(automasking, ss, vd.vertex);
madd_v3_v3fl(cloth_sim->pos[i], pos_diff, mask_v);
madd_v3_v3fl(cloth_sim->pos[i], cloth_sim->acceleration[i], mask_v);
@@ -868,7 +874,7 @@ static void do_cloth_brush_solve_simulation_task_cb_ex(
/* Prevents the vertices from sliding without creating folds when all vertices and forces are
* in the same plane. */
float noise[3];
- cloth_simulation_noise_get(noise, ss, vd.index, 0.000001f);
+ cloth_simulation_noise_get(noise, ss, vd.vertex, 0.000001f);
add_v3_v3(cloth_sim->pos[i], noise);
if (USE_SOLVER_RIPPLE_CONSTRAINT) {
@@ -916,6 +922,9 @@ static void cloth_brush_satisfy_constraints(SculptSession *ss,
const int v1 = constraint->elem_index_a;
const int v2 = constraint->elem_index_b;
+ const SculptVertRef v1ref = BKE_pbvh_table_index_to_vertex(ss->pbvh, v1);
+ const SculptVertRef v2ref = BKE_pbvh_table_index_to_vertex(ss->pbvh, v2);
+
float v1_to_v2[3];
sub_v3_v3v3(v1_to_v2, constraint->elem_position_b, constraint->elem_position_a);
const float current_distance = len_v3(v1_to_v2);
@@ -938,10 +947,10 @@ static void cloth_brush_satisfy_constraints(SculptSession *ss,
mul_v3_v3fl(correction_vector_half, correction_vector, 0.5f);
- const float mask_v1 = (1.0f - SCULPT_vertex_mask_get(ss, v1)) *
- SCULPT_automasking_factor_get(automasking, ss, v1);
- const float mask_v2 = (1.0f - SCULPT_vertex_mask_get(ss, v2)) *
- SCULPT_automasking_factor_get(automasking, ss, v2);
+ const float mask_v1 = (1.0f - SCULPT_vertex_mask_get(ss, v1ref)) *
+ SCULPT_automasking_factor_get(automasking, ss, v1ref);
+ const float mask_v2 = (1.0f - SCULPT_vertex_mask_get(ss, v2ref)) *
+ SCULPT_automasking_factor_get(automasking, ss, v2ref);
float sim_location[3];
cloth_brush_simulation_location_get(ss, brush, sim_location);
@@ -1136,6 +1145,14 @@ SculptClothSimulation *SCULPT_cloth_brush_simulation_create(SculptSession *ss,
cloth_sim = MEM_callocN(sizeof(SculptClothSimulation), "cloth constraints");
+ if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) {
+ cloth_sim->cd_pers_co = SCULPT_dyntopo_get_templayer(ss, CD_PROP_FLOAT3, SCULPT_LAYER_PERS_CO);
+ cloth_sim->cd_pers_no = SCULPT_dyntopo_get_templayer(ss, CD_PROP_FLOAT3, SCULPT_LAYER_PERS_NO);
+ cloth_sim->cd_pers_disp = SCULPT_dyntopo_get_templayer(
+ ss, CD_PROP_FLOAT, SCULPT_LAYER_PERS_DISP);
+ }
+
+ // cloth_sim->cd_pers_co = SCULPT_dyntopo_get_templayer
cloth_sim->length_constraints = MEM_callocN(sizeof(SculptClothLengthConstraint) *
CLOTH_LENGTH_CONSTRAINTS_BLOCK,
"cloth length constraints");
@@ -1166,7 +1183,9 @@ SculptClothSimulation *SCULPT_cloth_brush_simulation_create(SculptSession *ss,
if (USE_SOLVER_RIPPLE_CONSTRAINT) {
cloth_sim->init_normal = MEM_calloc_arrayN(totverts, sizeof(float) * 3, "init noramls");
for (int i = 0; i < totverts; i++) {
- SCULPT_vertex_normal_get(ss, i, cloth_sim->init_normal[i]);
+ SculptVertRef vertex = BKE_pbvh_table_index_to_vertex(ss->pbvh, i);
+
+ SCULPT_vertex_normal_get(ss, vertex, cloth_sim->init_normal[i]);
}
}
@@ -1225,16 +1244,20 @@ 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 != NULL;
const bool has_softbody_pos = cloth_sim->softbody_pos != NULL;
+ SCULPT_vertex_random_access_ensure(ss);
+
for (int i = 0; i < totverts; i++) {
- copy_v3_v3(cloth_sim->last_iteration_pos[i], SCULPT_vertex_co_get(ss, i));
- copy_v3_v3(cloth_sim->init_pos[i], SCULPT_vertex_co_get(ss, i));
- copy_v3_v3(cloth_sim->prev_pos[i], SCULPT_vertex_co_get(ss, i));
+ SculptVertRef vertex = BKE_pbvh_table_index_to_vertex(ss->pbvh, i);
+
+ copy_v3_v3(cloth_sim->last_iteration_pos[i], SCULPT_vertex_co_get(ss, vertex));
+ copy_v3_v3(cloth_sim->init_pos[i], SCULPT_vertex_co_get(ss, vertex));
+ copy_v3_v3(cloth_sim->prev_pos[i], SCULPT_vertex_co_get(ss, vertex));
if (has_deformation_pos) {
- copy_v3_v3(cloth_sim->deformation_pos[i], SCULPT_vertex_co_get(ss, i));
+ copy_v3_v3(cloth_sim->deformation_pos[i], SCULPT_vertex_co_get(ss, vertex));
cloth_sim->deformation_strength[i] = 1.0f;
}
if (has_softbody_pos) {
- copy_v3_v3(cloth_sim->softbody_pos[i], SCULPT_vertex_co_get(ss, i));
+ copy_v3_v3(cloth_sim->softbody_pos[i], SCULPT_vertex_co_get(ss, vertex));
}
}
}
@@ -1243,7 +1266,9 @@ void SCULPT_cloth_brush_store_simulation_state(SculptSession *ss, SculptClothSim
{
const int totverts = SCULPT_vertex_count_get(ss);
for (int i = 0; i < totverts; i++) {
- copy_v3_v3(cloth_sim->pos[i], SCULPT_vertex_co_get(ss, i));
+ SculptVertRef vertex = BKE_pbvh_table_index_to_vertex(ss->pbvh, i);
+
+ copy_v3_v3(cloth_sim->pos[i], SCULPT_vertex_co_get(ss, vertex));
}
}
@@ -1545,19 +1570,19 @@ static void cloth_filter_apply_forces_task_cb(void *__restrict userdata,
mul_v3_fl(sculpt_gravity, sd->gravity_factor * data->filter_strength);
SculptOrigVertData orig_data;
- SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[i]);
+ SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[i], 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);
+ SCULPT_orig_vert_data_update(&orig_data, vd.vertex);
float fade = vd.mask ? *vd.mask : 0.0f;
- fade *= SCULPT_automasking_factor_get(ss->filter_cache->automasking, ss, vd.index);
+ fade *= SCULPT_automasking_factor_get(ss->filter_cache->automasking, ss, vd.vertex);
fade = 1.0f - fade;
float force[3] = {0.0f, 0.0f, 0.0f};
float disp[3], temp[3], transform[3][3];
if (ss->filter_cache->active_face_set != SCULPT_FACE_SET_NONE) {
- if (!SCULPT_vertex_has_face_set(ss, vd.index, ss->filter_cache->active_face_set)) {
+ if (!SCULPT_vertex_has_face_set(ss, vd.vertex, ss->filter_cache->active_face_set)) {
continue;
}
}
@@ -1576,7 +1601,7 @@ static void cloth_filter_apply_forces_task_cb(void *__restrict userdata,
break;
case CLOTH_FILTER_INFLATE: {
float normal[3];
- SCULPT_vertex_normal_get(ss, vd.index, normal);
+ SCULPT_vertex_normal_get(ss, vd.vertex, normal);
mul_v3_v3fl(force, normal, fade * data->filter_strength);
} break;
case CLOTH_FILTER_EXPAND:
@@ -1647,7 +1672,9 @@ static int sculpt_cloth_filter_modal(bContext *C, wmOperator *op, const wmEvent
const int totverts = SCULPT_vertex_count_get(ss);
for (int i = 0; i < totverts; i++) {
- copy_v3_v3(ss->filter_cache->cloth_sim->pos[i], SCULPT_vertex_co_get(ss, i));
+ SculptVertRef vertex = BKE_pbvh_table_index_to_vertex(ss->pbvh, i);
+
+ copy_v3_v3(ss->filter_cache->cloth_sim->pos[i], SCULPT_vertex_co_get(ss, vertex));
}
SculptThreadedTaskData data = {
@@ -1686,10 +1713,12 @@ static void sculpt_cloth_filter_face_set_pinch_origin_calculate(float r_pinch_or
float accum[3] = {0.0f};
int tot = 0;
for (int i = 0; i < totvert; i++) {
- if (!SCULPT_vertex_has_face_set(ss, i, active_face_set)) {
+ SculptVertRef vertex = BKE_pbvh_table_index_to_vertex(ss->pbvh, i);
+
+ if (!SCULPT_vertex_has_face_set(ss, vertex, active_face_set)) {
continue;
}
- add_v3_v3(accum, SCULPT_vertex_co_get(ss, i));
+ add_v3_v3(accum, SCULPT_vertex_co_get(ss, vertex));
tot++;
}
if (tot > 0) {
diff --git a/source/blender/editors/sculpt_paint/sculpt_curvature.c b/source/blender/editors/sculpt_paint/sculpt_curvature.c
new file mode 100644
index 00000000000..9bc2f840e3a
--- /dev/null
+++ b/source/blender/editors/sculpt_paint/sculpt_curvature.c
@@ -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.h"
+
+#include "RNA_access.h"
+#include "RNA_define.h"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "bmesh.h"
+#include "bmesh_tools.h"
+
+#include <math.h>
+#include <stdlib.h>
+#include <string.h>
+
+/*
+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,
+ SculptVertRef 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,
+ SculptVertRef 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;
+ MDynTopoVert *mv = BKE_PBVH_DYNVERT(ss->cd_dyn_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;
+ MDynTopoVert *mv = BKE_PBVH_DYNVERT(ss->cd_dyn_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.c b/source/blender/editors/sculpt_paint/sculpt_detail.c
index 12114806594..0e4dcf950ac 100644
--- a/source/blender/editors/sculpt_paint/sculpt_detail.c
+++ b/source/blender/editors/sculpt_paint/sculpt_detail.c
@@ -64,6 +64,7 @@ typedef struct {
float edge_length;
struct IsectRayPrecalc isect_precalc;
+ SculptSession *ss;
} SculptDetailRaycastData;
static bool sculpt_and_constant_or_manual_detail_poll(bContext *C)
@@ -110,18 +111,84 @@ static int sculpt_detail_flood_fill_exec(bContext *C, wmOperator *UNUSED(op))
/* Update topology size. */
float object_space_constant_detail = 1.0f / (sd->constant_detail * mat4_to_scale(ob->obmat));
- BKE_pbvh_bmesh_detail_size_set(ss->pbvh, object_space_constant_detail);
+ BKE_pbvh_bmesh_detail_size_set(ss->pbvh, object_space_constant_detail, sd->detail_range);
SCULPT_undo_push_begin(ob, "Dynamic topology flood fill");
SCULPT_undo_push_node(ob, NULL, SCULPT_UNDO_COORDS);
- while (BKE_pbvh_bmesh_update_topology(
- ss->pbvh, PBVH_Collapse | PBVH_Subdivide, center, NULL, 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, NULL, ob, &mask_cb, &mask_cb_data);
+
+ const int max_steps = 10;
+ 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,
+ NULL,
+ size,
+ false,
+ false,
+ -1,
+ false,
+ mask_cb,
+ mask_cb_data,
+ max_dyntopo_steps_coll);
+
+ 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,
+ NULL,
+ size,
+ false,
+ false,
+ -1,
+ false,
+ mask_cb,
+ mask_cb_data,
+ max_dyntopo_steps_subd);
+ 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,
+ NULL,
+ size,
+ false,
+ false,
+ -1,
+ false,
+ mask_cb,
+ mask_cb_data,
+ max_dyntopo_steps_coll);
}
+ SCULPT_dyntopo_automasking_end(mask_cb_data);
+
MEM_SAFE_FREE(nodes);
SCULPT_undo_push_end();
@@ -174,13 +241,13 @@ static void sample_detail_voxel(bContext *C, ViewContext *vc, int mx, int my)
BKE_sculpt_update_object_for_edit(depsgraph, ob, true, false, false);
/* Average the edge length of the connected edges to the active vertex. */
- int active_vertex = SCULPT_active_vertex_get(ss);
+ SculptVertRef active_vertex = SCULPT_active_vertex_get(ss);
const float *active_vertex_co = SCULPT_active_vertex_co_get(ss);
float edge_length = 0.0f;
int tot = 0;
SculptVertexNeighborIter ni;
SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, active_vertex, ni) {
- edge_length += len_v3v3(active_vertex_co, SCULPT_vertex_co_get(ss, ni.index));
+ edge_length += len_v3v3(active_vertex_co, SCULPT_vertex_co_get(ss, ni.vertex));
tot += 1;
}
SCULPT_VERTEX_NEIGHBORS_ITER_END(ni);
@@ -193,8 +260,13 @@ static void sculpt_raycast_detail_cb(PBVHNode *node, void *data_v, float *tmin)
{
if (BKE_pbvh_node_get_tmin(node) < *tmin) {
SculptDetailRaycastData *srd = 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,12 +287,20 @@ static void sample_detail_dyntopo(bContext *C, ViewContext *vc, ARegion *region,
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. */
@@ -569,14 +649,14 @@ static void dyntopo_detail_size_sample_from_surface(Object *ob,
DyntopoDetailSizeEditCustomData *cd)
{
SculptSession *ss = ob->sculpt;
- const int active_vertex = SCULPT_active_vertex_get(ss);
+ const SculptVertRef active_vertex = SCULPT_active_vertex_get(ss);
float len_accum = 0;
int num_neighbors = 0;
SculptVertexNeighborIter ni;
SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, active_vertex, ni) {
len_accum += len_v3v3(SCULPT_vertex_co_get(ss, active_vertex),
- SCULPT_vertex_co_get(ss, ni.index));
+ SCULPT_vertex_co_get(ss, ni.vertex));
num_neighbors++;
}
SCULPT_VERTEX_NEIGHBORS_ITER_END(ni);
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..6f70f09beec
--- /dev/null
+++ b/source/blender/editors/sculpt_paint/sculpt_displacement.h
@@ -0,0 +1 @@
+#pragma once
diff --git a/source/blender/editors/sculpt_paint/sculpt_dyntopo.c b/source/blender/editors/sculpt_paint/sculpt_dyntopo.c
index ae6dcbdbff4..e0118b58f7b 100644
--- a/source/blender/editors/sculpt_paint/sculpt_dyntopo.c
+++ b/source/blender/editors/sculpt_paint/sculpt_dyntopo.c
@@ -23,9 +23,15 @@
#include "MEM_guardedalloc.h"
+#include "BLI_alloca.h"
+#include "BLI_array.h"
#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"
@@ -35,6 +41,7 @@
#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"
@@ -76,14 +83,417 @@
#include <math.h>
#include <stdlib.h>
-void SCULPT_dynamic_topology_triangulate(BMesh *bm)
+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, NULL, NULL, NULL);
+ const BMAllocTemplate allocsize = {
+ .totvert = 2048 * 16, .totface = 2048 * 16, .totloop = 4196 * 16, .totedge = 2048 * 16};
+
+ BMesh *bm = BM_mesh_create(
+ &allocsize,
+ &((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}));
+
+ return bm;
+}
+// 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(float p[3], float q[3], 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;
+ }
+}
+
+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,
+ SculptVertRef 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.0;
+ float totw = 0.0;
+
+ 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,
+ SculptVertRef 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;
+
+ MVert *v = ss->mvert + vertex.i;
+ MEdge *e1 = ss->medge + elem->indices[i1];
+ MEdge *e2 = ss->medge + elem->indices[i2];
+ MEdge *e3 = ss->medge + elem->indices[i3];
+
+ MVert *v1 = (unsigned int)vertex.i == e1->v1 ? ss->mvert + e1->v2 : ss->mvert + e1->v1;
+ MVert *v2 = (unsigned int)vertex.i == e2->v1 ? ss->mvert + e2->v2 : ss->mvert + e2->v1;
+ MVert *v3 = (unsigned int)vertex.i == e3->v1 ? ss->mvert + e3->v2 : ss->mvert + e3->v1;
+
+ 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;
+ }
+ }
+
+ 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++) {
+ SculptVertRef vertex = BKE_pbvh_table_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,
+ mesh->mvert,
+ mesh->medge,
+ mesh->totvert,
+ mesh->totedge,
+ true);
+ }
+
+ break;
+ }
+ case PBVH_GRIDS: // not supported yet
+ break;
+ }
+}
+
+void SCULPT_get_cotangents(SculptSession *ss,
+ SculptVertRef 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, SculptVertRef vertex)
+{
+ BMVert *v = (BMVert *)vertex.i;
+ MDynTopoVert *mv = BKE_PBVH_DYNVERT(ss->cd_dyn_vert, v);
+
+ if (mv->flag & DYNVERT_NEED_DISK_SORT) {
+ mv->flag &= ~DYNVERT_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_index.i ?
+ BKE_pbvh_vertex_index_to_table(ss->pbvh, ss->active_vertex_index) :
+ -1;
+ int actf = ss->active_face_index.i ?
+ BKE_pbvh_face_index_to_table(ss->pbvh, ss->active_face_index) :
+ -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_index = BKE_pbvh_table_index_to_vertex(ss->pbvh, actv);
+ }
+ if (actf >= 0) {
+ ss->active_face_index = BKE_pbvh_table_index_to_face(ss->pbvh, actf);
+ }
+
+ SCULPT_dyntopo_node_layers_update_offsets(ss);
+
+ 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;
+
+ BMFace **faces_array = NULL;
+ BLI_array_declare(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;
+ BLI_array_clear(faces_array);
+ BLI_array_grow_items(faces_array, faces_array_tot);
+ // BMFace **faces_array = BLI_array_alloca(faces_array, faces_array_tot);
+
+ BM_face_triangulate(bm,
+ f,
+ faces_array,
+ &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, 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)
{
SculptSession *ss = ob->sculpt;
@@ -104,18 +514,103 @@ void SCULPT_pbvh_clear(Object *ob)
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
}
-void SCULPT_dyntopo_node_layers_add(SculptSession *ss)
+void SCULPT_dyntopo_save_origverts(SculptSession *ss)
+{
+ BMIter iter;
+ BMVert *v;
+
+ BM_ITER_MESH (v, &iter, ss->bm, BM_VERTS_OF_MESH) {
+ MDynTopoVert *mv = BKE_PBVH_DYNVERT(ss->cd_dyn_vert, v);
+
+ copy_v3_v3(mv->origco, v->co);
+ copy_v3_v3(mv->origno, v->no);
+
+ if (ss->cd_vcol_offset >= 0) {
+ MPropCol *mp = (MPropCol *)BM_ELEM_CD_GET_VOID_P(v, ss->cd_vcol_offset);
+ copy_v4_v4(mv->origcolor, mp->color);
+ }
+ }
+}
+
+char dyntopop_node_idx_layer_id[] = "_dyntopo_node_id";
+
+void SCULPT_dyntopo_node_layers_update_offsets(SculptSession *ss)
+{
+ SCULPT_dyntopo_node_layers_add(ss);
+ if (ss->pbvh) {
+ BKE_pbvh_update_offsets(ss->pbvh,
+ ss->cd_vert_node_offset,
+ ss->cd_face_node_offset,
+ ss->cd_dyn_vert,
+ ss->cd_face_areas);
+ }
+ if (ss->bm_log) {
+ BM_log_set_cd_offsets(ss->bm_log, ss->cd_dyn_vert);
+ }
+}
+
+bool SCULPT_dyntopo_has_templayer(SculptSession *ss, int type, const char *name)
{
- int cd_node_layer_index;
+ return CustomData_get_named_layer_index(&ss->bm->vdata, type, name) >= 0;
+}
- char layer_id[] = "_dyntopo_node_id";
+void SCULPT_dyntopo_ensure_templayer(SculptSession *ss, int type, const char *name)
+{
+ int li = CustomData_get_named_layer_index(&ss->bm->vdata, type, name);
- cd_node_layer_index = CustomData_get_named_layer_index(&ss->bm->vdata, CD_PROP_INT32, layer_id);
- if (cd_node_layer_index == -1) {
- BM_data_layer_add_named(ss->bm, &ss->bm->vdata, CD_PROP_INT32, layer_id);
- cd_node_layer_index = CustomData_get_named_layer_index(
- &ss->bm->vdata, CD_PROP_INT32, layer_id);
+ if (li < 0) {
+ BM_data_layer_add_named(ss->bm, &ss->bm->vdata, type, name);
+ SCULPT_dyntopo_node_layers_update_offsets(ss);
+
+ li = CustomData_get_named_layer_index(&ss->bm->vdata, type, name);
+ ss->bm->vdata.layers[li].flag |= CD_FLAG_TEMPORARY;
}
+}
+
+int SCULPT_dyntopo_get_templayer(SculptSession *ss, int type, const char *name)
+{
+ int li = CustomData_get_named_layer_index(&ss->bm->vdata, type, name);
+
+ if (li < 0) {
+ return -1;
+ }
+
+ return CustomData_get_n_offset(
+ &ss->bm->vdata, type, li - CustomData_get_layer_index(&ss->bm->vdata, type));
+}
+
+char dyntopop_faces_areas_layer_id[] = "__dyntopo_face_areas";
+
+void SCULPT_dyntopo_node_layers_add(SculptSession *ss)
+{
+ int cd_node_layer_index, cd_face_node_layer_index;
+
+ int cd_origco_index, cd_origno_index, cd_origvcol_index = -1;
+ bool have_vcol = CustomData_has_layer(&ss->bm->vdata, CD_PROP_COLOR);
+
+ BMCustomLayerReq vlayers[] = {{CD_PAINT_MASK, NULL, 0},
+ {CD_DYNTOPO_VERT, NULL, CD_FLAG_TEMPORARY},
+ {CD_PROP_INT32, dyntopop_node_idx_layer_id, CD_FLAG_TEMPORARY}};
+
+ BM_data_layers_ensure(ss->bm, &ss->bm->vdata, vlayers, 3);
+
+ BMCustomLayerReq flayers[] = {
+ {CD_PROP_INT32, dyntopop_node_idx_layer_id, CD_FLAG_TEMPORARY},
+ {CD_PROP_FLOAT, dyntopop_faces_areas_layer_id, CD_FLAG_TEMPORARY},
+ };
+ BM_data_layers_ensure(ss->bm, &ss->bm->pdata, flayers, 2);
+
+ // get indices again, as they might have changed after adding new layers
+ cd_node_layer_index = CustomData_get_named_layer_index(
+ &ss->bm->vdata, CD_PROP_INT32, dyntopop_node_idx_layer_id);
+ cd_face_node_layer_index = CustomData_get_named_layer_index(
+ &ss->bm->pdata, CD_PROP_INT32, dyntopop_node_idx_layer_id);
+
+ ss->cd_origvcol_offset = -1;
+
+ ss->cd_dyn_vert = CustomData_get_offset(&ss->bm->vdata, CD_DYNTOPO_VERT);
+
+ ss->cd_vcol_offset = CustomData_get_offset(&ss->bm->vdata, CD_PROP_COLOR);
ss->cd_vert_node_offset = CustomData_get_n_offset(
&ss->bm->vdata,
@@ -124,51 +619,272 @@ void SCULPT_dyntopo_node_layers_add(SculptSession *ss)
ss->bm->vdata.layers[cd_node_layer_index].flag |= CD_FLAG_TEMPORARY;
- cd_node_layer_index = CustomData_get_named_layer_index(&ss->bm->pdata, CD_PROP_INT32, layer_id);
- if (cd_node_layer_index == -1) {
- BM_data_layer_add_named(ss->bm, &ss->bm->pdata, CD_PROP_INT32, layer_id);
- cd_node_layer_index = CustomData_get_named_layer_index(
- &ss->bm->pdata, CD_PROP_INT32, layer_id);
- }
-
ss->cd_face_node_offset = CustomData_get_n_offset(
&ss->bm->pdata,
CD_PROP_INT32,
- cd_node_layer_index - CustomData_get_layer_index(&ss->bm->pdata, CD_PROP_INT32));
+ cd_face_node_layer_index - CustomData_get_layer_index(&ss->bm->pdata, CD_PROP_INT32));
- ss->bm->pdata.layers[cd_node_layer_index].flag |= CD_FLAG_TEMPORARY;
+ ss->bm->pdata.layers[cd_face_node_layer_index].flag |= CD_FLAG_TEMPORARY;
+ ss->cd_faceset_offset = CustomData_get_offset(&ss->bm->pdata, CD_SCULPT_FACE_SETS);
+
+ ss->cd_face_areas = CustomData_get_named_layer(
+ &ss->bm->pdata, CD_PROP_FLOAT, dyntopop_faces_areas_layer_id);
+ ss->cd_face_areas = ss->bm->pdata.layers[ss->cd_face_areas].offset;
}
+/**
+ Syncs customdata layers with internal bmesh, but ignores deleted layers.
+*/
+void SCULPT_dynamic_topology_sync_layers(Object *ob, Mesh *me)
+{
+ SculptSession *ss = ob->sculpt;
+
+ if (!ss || !ss->bm) {
+ 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 types[4] = {BM_VERT, BM_EDGE, BM_LOOP, BM_FACE};
+ int badmask = CD_MASK_MLOOP | CD_MASK_MVERT | CD_MASK_MEDGE | CD_MASK_MPOLY | CD_MASK_ORIGINDEX |
+ CD_MASK_ORIGSPACE | CD_MASK_MFACE;
+
+ for (int i = 0; i < 4; i++) {
+ CustomDataLayer **newlayers = NULL;
+ BLI_array_declare(newlayers);
+
+ CustomData *data1 = cd1[i];
+ CustomData *data2 = cd2[i];
+
+ if (!data1->layers) {
+ modified |= data2->layers != NULL;
+ 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) {
+ BLI_array_append(newlayers, cl1);
+ }
+ }
+
+ for (int j = 0; j < BLI_array_len(newlayers); j++) {
+ BM_data_layer_add_named(bm, data2, newlayers[j]->type, newlayers[j]->name);
+ modified = true;
+ }
+
+ 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;
+ }
+
+ for (int k = baseidx; k < data2->totlayer; k++) {
+ CustomDataLayer *cl3 = data2->layers + k;
+
+ if (cl3->type != cl2->type) {
+ break;
+ }
+
+ // based off of how CustomData_set_layer_XXXX_index works
+
+ cl3->active = (cl2->active + baseidx) - k;
+ cl3->active_rnd = (cl2->active_rnd + baseidx) - k;
+ cl3->active_mask = (cl2->active_mask + baseidx) - k;
+ cl3->active_clone = (cl2->active_clone + baseidx) - k;
+ }
+ }
+
+ BLI_array_free(newlayers);
+ }
+
+ if (modified) {
+ SCULPT_dyntopo_node_layers_update_offsets(ss);
+ }
+}
+
+BMesh *BM_mesh_bm_from_me_threaded(BMesh *bm,
+ Object *ob,
+ const Mesh *me,
+ const struct BMeshFromMeshParams *params);
+
void SCULPT_dynamic_topology_enable_ex(Main *bmain, Depsgraph *depsgraph, Scene *scene, Object *ob)
{
SculptSession *ss = ob->sculpt;
Mesh *me = ob->data;
- const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(me);
+ const BMAllocTemplate allocsize = {
+ .totvert = 2048 * 16, .totface = 2048 * 16, .totloop = 4196 * 16, .totedge = 2048 * 16};
SCULPT_pbvh_clear(ob);
+ if (ss->mdyntopo_verts) {
+ MEM_freeN(ss->mdyntopo_verts);
+ ss->mdyntopo_verts = NULL;
+ }
+
ss->bm_smooth_shading = (scene->toolsettings->sculpt->flags & SCULPT_DYNTOPO_SMOOTH_SHADING) !=
0;
/* Dynamic topology doesn't ensure selection state is valid, so remove T36280. */
BKE_mesh_mselect_clear(me);
- /* Create triangles-only BMesh. */
+#if 1
ss->bm = BM_mesh_create(&allocsize,
- &((struct BMeshCreateParams){
- .use_toolflags = false,
- }));
-
- BM_mesh_bm_from_me(ss->bm,
+ &((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}));
+
+ BM_mesh_bm_from_me(NULL,
+ ss->bm,
me,
(&(struct BMeshFromMeshParams){
.calc_face_normal = true,
.use_shapekey = true,
.active_shapekey = ob->shapenr,
}));
- SCULPT_dynamic_topology_triangulate(ss->bm);
- BM_data_layer_add(ss->bm, &ss->bm->vdata, CD_PAINT_MASK);
+#else
+ ss->bm = BM_mesh_bm_from_me_threaded(NULL,
+ NULL,
+ 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
+
SCULPT_dyntopo_node_layers_add(ss);
+ SCULPT_dyntopo_save_origverts(ss);
+
+ BMIter iter;
+ BMVert *v;
+
+ int cd_pers_co = -1, cd_pers_no = -1, cd_pers_disp = -1;
+ int cd_layer_disp = -1;
+
+ // convert layer brush data
+ if (ss->persistent_base) {
+ BMCustomLayerReq layers[] = {{CD_PROP_FLOAT3, SCULPT_LAYER_PERS_CO, CD_FLAG_TEMPORARY},
+ {CD_PROP_FLOAT3, SCULPT_LAYER_PERS_NO, CD_FLAG_TEMPORARY},
+ {CD_PROP_FLOAT, SCULPT_LAYER_PERS_DISP, CD_FLAG_TEMPORARY},
+ {CD_PROP_FLOAT, SCULPT_LAYER_DISP, CD_FLAG_TEMPORARY}};
+
+ BM_data_layers_ensure(ss->bm, &ss->bm->vdata, layers, 4);
+
+ cd_pers_co = SCULPT_dyntopo_get_templayer(ss, CD_PROP_FLOAT3, SCULPT_LAYER_PERS_CO);
+ cd_pers_no = SCULPT_dyntopo_get_templayer(ss, CD_PROP_FLOAT3, SCULPT_LAYER_PERS_NO);
+ cd_pers_disp = SCULPT_dyntopo_get_templayer(ss, CD_PROP_FLOAT, SCULPT_LAYER_PERS_DISP);
+ cd_layer_disp = SCULPT_dyntopo_get_templayer(ss, CD_PROP_FLOAT, SCULPT_LAYER_DISP);
+ }
+ else {
+ cd_layer_disp = SCULPT_dyntopo_get_templayer(ss, CD_PROP_FLOAT, SCULPT_LAYER_PERS_DISP);
+ }
+
+ int cd_vcol_offset = CustomData_get_offset(&ss->bm->vdata, CD_PROP_COLOR);
+ SCULPT_dyntopo_node_layers_update_offsets(ss);
+
+ int i = 0;
+ BMEdge *e;
+
+ BM_ITER_MESH (e, &iter, ss->bm, BM_EDGES_OF_MESH) {
+ e->head.hflag |= BM_ELEM_DRAW;
+ }
+
+ BM_ITER_MESH (v, &iter, ss->bm, BM_VERTS_OF_MESH) {
+ MDynTopoVert *mv = BKE_PBVH_DYNVERT(ss->cd_dyn_vert, v);
+
+ mv->flag |= DYNVERT_NEED_DISK_SORT | DYNVERT_NEED_VALENCE;
+
+ BKE_pbvh_update_vert_boundary(
+ ss->cd_dyn_vert, ss->cd_faceset_offset, v, ss->boundary_symmetry);
+ BKE_pbvh_bmesh_update_valence(ss->cd_dyn_vert, (SculptVertRef){.i = (intptr_t)v});
+
+ // persistent base
+ if (cd_pers_co >= 0) {
+ float *co = BM_ELEM_CD_GET_VOID_P(v, cd_pers_co);
+ float *no = BM_ELEM_CD_GET_VOID_P(v, cd_pers_no);
+ float *disp = BM_ELEM_CD_GET_VOID_P(v, cd_pers_disp);
+
+ copy_v3_v3(co, ss->persistent_base[i].co);
+ copy_v3_v3(no, ss->persistent_base[i].no);
+ *disp = ss->persistent_base[i].disp;
+ }
+
+ if (cd_layer_disp >= 0) {
+ float *disp = BM_ELEM_CD_GET_VOID_P(v, cd_layer_disp);
+ *disp = 0.0f;
+ }
+
+ copy_v3_v3(mv->origco, v->co);
+ copy_v3_v3(mv->origno, v->no);
+
+ if (ss->cd_vcol_offset >= 0) {
+ MPropCol *color = (MPropCol *)BM_ELEM_CD_GET_VOID_P(v, cd_vcol_offset);
+ copy_v4_v4(mv->origcolor, color->color);
+ }
+
+ i++;
+ }
+
/* Make sure the data for existing faces are initialized. */
if (me->totpoly != ss->bm->totface) {
BM_mesh_normals_update(ss->bm);
@@ -178,14 +894,46 @@ void SCULPT_dynamic_topology_enable_ex(Main *bmain, Depsgraph *depsgraph, Scene
me->flag |= ME_SCULPT_DYNAMIC_TOPOLOGY;
/* 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->cd_dyn_vert);
+ }
/* 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);
}
+void SCULPT_dyntopo_save_persistent_base(SculptSession *ss)
+{
+ int cd_pers_co = SCULPT_dyntopo_get_templayer(ss, CD_PROP_FLOAT3, SCULPT_LAYER_PERS_CO);
+ int cd_pers_no = SCULPT_dyntopo_get_templayer(ss, CD_PROP_FLOAT3, SCULPT_LAYER_PERS_NO);
+ int cd_pers_disp = SCULPT_dyntopo_get_templayer(ss, CD_PROP_FLOAT, SCULPT_LAYER_PERS_DISP);
+
+ if (cd_pers_co >= 0) {
+ BMIter iter;
+
+ MEM_SAFE_FREE(ss->persistent_base);
+ ss->persistent_base = MEM_callocN(sizeof(*ss->persistent_base) * ss->bm->totvert,
+ "ss->persistent_base");
+ BMVert *v;
+ int i = 0;
+
+ BM_ITER_MESH (v, &iter, ss->bm, BM_VERTS_OF_MESH) {
+ float *co = BM_ELEM_CD_GET_VOID_P(v, cd_pers_co);
+ float *no = BM_ELEM_CD_GET_VOID_P(v, cd_pers_no);
+ float *disp = BM_ELEM_CD_GET_VOID_P(v, cd_pers_disp);
+
+ copy_v3_v3(ss->persistent_base[i].co, co);
+ copy_v3_v3(ss->persistent_base[i].no, no);
+ ss->persistent_base[i].disp = *disp;
+
+ i++;
+ }
+ }
+}
/* Free the sculpt BMesh and BMLog
*
* If 'unode' is given, the BMesh's data is copied out to the unode
@@ -198,64 +946,41 @@ static void SCULPT_dynamic_topology_disable_ex(
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);
-
- BKE_mesh_update_customdata_pointers(me, false);
- }
- else {
- BKE_sculptsession_bm_to_me(ob, true);
-
- /* Reset Face Sets as they are no longer valid. */
- if (!CustomData_has_layer(&me->pdata, CD_SCULPT_FACE_SETS)) {
- CustomData_add_layer(&me->pdata, CD_SCULPT_FACE_SETS, CD_CALLOC, NULL, me->totpoly);
- }
- ss->face_sets = CustomData_get_layer(&me->pdata, CD_SCULPT_FACE_SETS);
- for (int i = 0; i < me->totpoly; i++) {
- ss->face_sets[i] = 1;
- }
- me->face_sets_color_default = 1;
+ BKE_sculptsession_bm_to_me(ob, true);
- /* Sync the visibility to vertices manually as the pmap is still not initialized. */
- for (int i = 0; i < me->totvert; i++) {
- me->mvert[i].flag &= ~ME_HIDE;
- me->mvert[i].flag |= ME_VERT_PBVH_UPDATE;
- }
+ /* Sync the visibility to vertices manually as the pmap is still not initialized. */
+ for (int i = 0; i < me->totvert; i++) {
+ me->mvert[i].flag &= ~ME_HIDE;
+ me->mvert[i].flag |= ME_VERT_PBVH_UPDATE;
}
/* Clear data. */
me->flag &= ~ME_SCULPT_DYNAMIC_TOPOLOGY;
+ bool disp_saved = false;
+
+ if (ss->bm_log) {
+ if (ss->bm) {
+ disp_saved = true;
+
+ // rebuild ss->persistent_base if necassary
+ SCULPT_dyntopo_save_persistent_base(ss);
+ }
+
+ BM_log_free(ss->bm_log, true);
+ ss->bm_log = NULL;
+ }
+
/* Typically valid but with global-undo they can be NULL, see: T36234. */
if (ss->bm) {
+ if (!disp_saved) {
+ // rebuild ss->persistent_base if necassary
+ SCULPT_dyntopo_save_persistent_base(ss);
+ }
+
BM_mesh_free(ss->bm);
ss->bm = NULL;
}
- if (ss->bm_log) {
- BM_log_free(ss->bm_log);
- ss->bm_log = NULL;
- }
BKE_particlesystem_reset_all(ob);
BKE_ptcache_object_reset(scene, ob, PTCACHE_RESET_OUTDATED);
@@ -292,6 +1017,8 @@ void sculpt_dynamic_topology_disable_with_undo(Main *bmain,
if (use_undo) {
SCULPT_undo_push_end();
}
+
+ ss->active_vertex_index.i = ss->active_face_index.i = 0;
}
}
@@ -301,6 +1028,7 @@ static void sculpt_dynamic_topology_enable_with_undo(Main *bmain,
Object *ob)
{
SculptSession *ss = ob->sculpt;
+
if (ss->bm == NULL) {
/* May be false in background mode. */
const bool use_undo = G.background ? (ED_undo_stack_get() != NULL) : true;
@@ -312,6 +1040,8 @@ static void sculpt_dynamic_topology_enable_with_undo(Main *bmain,
SCULPT_undo_push_node(ob, NULL, SCULPT_UNDO_DYNTOPO_BEGIN);
SCULPT_undo_push_end();
}
+
+ ss->active_vertex_index.i = ss->active_face_index.i = 0;
}
}
@@ -338,14 +1068,33 @@ static int sculpt_dynamic_topology_toggle_exec(bContext *C, wmOperator *UNUSED(o
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_("Vertex Data Detected!");
- const char *msg = TIP_("Dyntopo will not preserve vertex colors, UVs, or other customdata");
+ 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);
@@ -378,6 +1127,7 @@ enum eDynTopoWarnFlag SCULPT_dynamic_topology_check(Scene *scene, Object *ob)
BLI_assert(ss->bm == NULL);
UNUSED_VARS_NDEBUG(ss);
+#ifndef DYNTOPO_CD_INTERP
for (int i = 0; i < CD_NUMTYPES; i++) {
if (!ELEM(i, CD_MVERT, CD_MEDGE, CD_MFACE, CD_MLOOP, CD_MPOLY, CD_PAINT_MASK, CD_ORIGINDEX)) {
if (CustomData_has_layer(&me->vdata, i)) {
@@ -391,6 +1141,7 @@ enum eDynTopoWarnFlag SCULPT_dynamic_topology_check(Scene *scene, Object *ob)
}
}
}
+#endif
{
VirtualModifierData virtualModifierData;
@@ -403,6 +1154,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;
@@ -424,7 +1179,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);
}
@@ -438,7 +1196,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;
@@ -447,3 +1208,708 @@ void SCULPT_OT_dynamic_topology_toggle(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
+
+#define MAXUVLOOPS 32
+#define MAXUVNEIGHBORS 32
+
+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];
+ int totloop, totneighbor;
+} 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;
+} 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 = 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;
+ MLoopUV *uv = BM_ELEM_CD_GET_VOID_P(l, solver->cd_uv);
+
+ // float u = floorf(uv->uv[0] / solver->snap_limit) * solver->snap_limit;
+ // float v = floorf(uv->uv[1] / solver->snap_limit) * solver->snap_limit;
+
+ intptr_t x = (intptr_t)(uv->uv[0] * 16384.0);
+ intptr_t y = (intptr_t)(uv->uv[1] * 16384.0);
+ intptr_t key = y * 16384LL + x;
+
+ return POINTER_FROM_INT(key);
+}
+
+static UVSmoothVert *uvsolver_get_vert(UVSolver *solver, BMLoop *l)
+{
+ MLoopUV *uv = 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 = BLI_mempool_alloc(solver->verts);
+ memset(v, 0, sizeof(*v));
+
+ // copy_v2_v2(v->uv, uv->uv);
+ v->uv[0] = (double)uv->uv[0];
+ v->uv[1] = (double)uv->uv[1];
+
+ 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.5f * ((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 fabsf(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 = 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_v2_db(tri->vs[0]->uv, tri->vs[1]->uv, tri->vs[2]->uv);
+
+ if (area2d < 0.000001) {
+ tri->vs[0]->uv[0] -= 0.0001;
+ tri->vs[0]->uv[1] -= 0.0001;
+ tri->vs[1]->uv[0] += 0.0001;
+ tri->vs[2]->uv[1] += 0.0001;
+ }
+
+ solver->totarea2d += area2d;
+ solver->totarea3d += area3d;
+
+ tri->area2d = area2d;
+ tri->area3d = area3d;
+
+ for (int i = 0; !nocon && i < 3; i++) {
+
+ UVSmoothConstraint *con = BLI_mempool_alloc(solver->constraints);
+ memset((void *)con, 0, sizeof(*con));
+ con->type = CON_ANGLES;
+ con->k = 0.5;
+
+ 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 = BLI_mempool_alloc(solver->constraints);
+ memset((void *)con, 0, sizeof(*con));
+
+ con->vs[0] = v0;
+ con->vs[1] = v1;
+ con->vs[2] = v2;
+ con->totvert = 3;
+ con->tri = tri;
+ con->type = CON_AREA;
+ con->k = 1.0;
+ }
+
+#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->neighbors[v1->totneighbor++] = v2;
+ v2->neighbors[v2->totneighbor++] = v1;
+ }
+#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 (area2d - goal) * 1024.0;
+ }
+ default:
+ return 0.0f;
+ }
+}
+
+BLI_INLINE float uvsolver_vert_weight(UVSmoothVert *sv)
+{
+ double w = 1.0;
+
+ if (sv->pinned || sv->boundary) {
+ w = 100000.0;
+ }
+
+ return w;
+}
+
+static void uvsolver_solve_begin(UVSolver *solver)
+{
+ UVSmoothVert *sv;
+ BLI_mempool_iter iter;
+
+ BLI_mempool_iternew(solver->verts, &iter);
+ sv = BLI_mempool_iterstep(&iter);
+ BMIter liter;
+
+ for (; sv; sv = 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 = BLI_mempool_iterstep(&iter);
+ for (; sv1; sv1 = BLI_mempool_iterstep(&iter)) {
+ double uv[2] = {0.0, 0.0};
+ double tot = 0.0;
+
+ if (!sv1->totneighbor || sv1->pinned) {
+ continue;
+ }
+
+ for (int i = 0; i < sv1->totneighbor; i++) {
+ UVSmoothVert *sv2 = sv1->neighbors[i];
+
+ if (!sv2 || (sv1->boundary && !sv2->boundary)) {
+ continue;
+ }
+
+ uv[0] += sv2->uv[0];
+ uv[1] += sv2->uv[1];
+ tot += 1.0;
+ }
+
+ if (tot < 2.0) {
+ continue;
+ }
+
+ uv[0] /= tot;
+ uv[1] /= tot;
+
+ sv1->uv[0] += (uv[0] - sv1->uv[0]) * strength;
+ sv1->uv[1] += (uv[1] - sv1->uv[1]) * strength;
+ }
+
+ // update real uvs
+
+ const int cd_uv = solver->cd_uv;
+
+ BLI_mempool_iternew(solver->verts, &iter);
+ UVSmoothVert *sv = BLI_mempool_iterstep(&iter);
+ for (; sv; sv = BLI_mempool_iterstep(&iter)) {
+ for (int i = 0; i < sv->totloop; i++) {
+ BMLoop *l = sv->ls[i];
+ MLoopUV *uv = BM_ELEM_CD_GET_VOID_P(l, cd_uv);
+
+ uv->uv[0] = (float)sv->uv[0];
+ uv->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, fabs(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 = BLI_mempool_iterstep(&iter);
+ for (; con; con = 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 += 1.0 / uvsolver_vert_weight(sv);
+ }
+ }
+
+ if (totg < eval_limit) {
+ continue;
+ }
+
+ r1 *= -solver->strength * 0.75 * con->k / totg;
+ // totw = 1.0 / totw;
+
+ for (int i = 0; i < con->totvert; i++) {
+ UVSmoothVert *sv = con->vs[i];
+ double w = 1.0 / (uvsolver_vert_weight(sv) * totw);
+
+ for (int j = 0; j < 2; j++) {
+ sv->uv[j] += r1 * con->gs[i][j] * w;
+ }
+ }
+ }
+
+ // update real uvs
+
+ const int cd_uv = solver->cd_uv;
+
+ BLI_mempool_iternew(solver->verts, &iter);
+ UVSmoothVert *sv = BLI_mempool_iterstep(&iter);
+ for (; sv; sv = BLI_mempool_iterstep(&iter)) {
+ for (int i = 0; i < sv->totloop; i++) {
+ BMLoop *l = sv->ls[i];
+ MLoopUV *uv = BM_ELEM_CD_GET_VOID_P(l, cd_uv);
+
+ uv->uv[0] = (float)sv->uv[0];
+ uv->uv[1] = (float)sv->uv[1];
+ }
+ }
+
+ return (float)error / (float)totcon;
+}
+
+static void sculpt_uv_brush_cb(void *__restrict userdata,
+ const int n,
+ const TaskParallelTLS *__restrict tls)
+{
+ SculptUVThreadData *data1 = userdata;
+ SculptThreadedTaskData *data = &data1->data;
+ SculptSession *ss = data->ob->sculpt;
+ // const Brush *brush = data->brush;
+ // const float *offset = data->offset;
+
+ PBVHVertexIter vd;
+
+ SculptBrushTest test;
+ SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
+ ss, &test, data->brush->falloff_shape);
+ const int thread_id = BLI_task_parallel_thread_id(tls);
+
+ 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
+ }
+
+ float bstrength = ss->cache->bstrength;
+ const int cd_mask = CustomData_get_offset(&ss->bm->vdata, CD_PAINT_MASK);
+
+ 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_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->prev->v == l->v ? l2->prev : l2->next;
+ }
+
+ UVSmoothVert *sv = uvsolver_get_vert(data1->solver, l2);
+
+ if (lastv && lastv != sv) {
+ ok = false;
+ lastv->boundary = true;
+ sv->boundary = true;
+ }
+
+ lastv = sv;
+
+ MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l2, cd_uv);
+
+ add_v2_v2(uv, luv->uv);
+ 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;
+ }
+
+ MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l2, cd_uv);
+
+ if (len_v2v2(luv->uv, uv) < 0.02) {
+ copy_v2_v2(luv->uv, 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);
+ }
+
+ SculptVertRef 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];
+ const float bstrength = ss->cache->bstrength;
+
+ 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->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);
+
+ 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.c b/source/blender/editors/sculpt_paint/sculpt_expand.c
index 08716fabe59..91a1fd66508 100644
--- a/source/blender/editors/sculpt_paint/sculpt_expand.c
+++ b/source/blender/editors/sculpt_paint/sculpt_expand.c
@@ -157,10 +157,12 @@ enum {
*/
static bool sculpt_expand_is_vert_in_active_component(SculptSession *ss,
ExpandCache *expand_cache,
- const int v)
+ const SculptVertRef v)
{
+ const int v_i = BKE_pbvh_vertex_index_to_table(ss->pbvh, v);
+
for (int i = 0; i < EXPAND_SYMM_AREAS; i++) {
- if (ss->vertex_info.connected_component[v] == expand_cache->active_connected_components[i]) {
+ if (ss->vertex_info.connected_component[v_i] == expand_cache->active_connected_components[i]) {
return true;
}
}
@@ -172,10 +174,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 SculptFaceRef f)
{
- const MLoop *loop = &ss->mloop[ss->mpoly[f].loopstart];
- return sculpt_expand_is_vert_in_active_component(ss, expand_cache, loop->v);
+ if (ss->bm) {
+ BMFace *bf = (BMFace *)f.i;
+ BMLoop *l = bf->l_first;
+ SculptVertRef 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_table_index_to_vertex(ss->pbvh, loop->v));
+ }
}
/**
@@ -184,14 +196,16 @@ static bool sculpt_expand_is_face_in_active_component(SculptSession *ss,
*/
static float sculpt_expand_falloff_value_vertex_get(SculptSession *ss,
ExpandCache *expand_cache,
- const int v)
+ const SculptVertRef v)
{
+ const int v_i = BKE_pbvh_vertex_index_to_table(ss->pbvh, v);
+
if (expand_cache->texture_distortion_strength == 0.0f) {
- return expand_cache->vert_falloff[v];
+ return expand_cache->vert_falloff[v_i];
}
if (!expand_cache->brush->mtex.tex) {
- return expand_cache->vert_falloff[v];
+ return expand_cache->vert_falloff[v_i];
}
float rgba[4];
@@ -201,7 +215,7 @@ static float sculpt_expand_falloff_value_vertex_get(SculptSession *ss,
const float distortion = (avg - 0.5f) * expand_cache->texture_distortion_strength *
expand_cache->max_vert_falloff;
- return expand_cache->vert_falloff[v] + distortion;
+ return expand_cache->vert_falloff[v_i] + distortion;
}
/**
@@ -226,7 +240,9 @@ static float sculpt_expand_max_vertex_falloff_get(ExpandCache *expand_cache)
* Main function to get the state of a vertex for the current state and settings of a #ExpandCache.
* Returns true when the target data should be modified by expand.
*/
-static bool sculpt_expand_state_get(SculptSession *ss, ExpandCache *expand_cache, const int v)
+static bool sculpt_expand_state_get(SculptSession *ss,
+ ExpandCache *expand_cache,
+ const SculptVertRef v)
{
if (!SCULPT_vertex_visible_get(ss, v)) {
return false;
@@ -272,9 +288,13 @@ static bool sculpt_expand_state_get(SculptSession *ss, ExpandCache *expand_cache
* 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 SculptFaceRef f)
{
- if (expand_cache->original_face_sets[f] <= 0) {
+ const int f_i = BKE_pbvh_face_index_to_table(ss->pbvh, f);
+
+ if (expand_cache->original_face_sets[f_i] <= 0) {
return false;
}
@@ -289,7 +309,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 {
@@ -297,12 +317,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);
+ 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;
}
}
@@ -320,7 +340,7 @@ static bool sculpt_expand_face_state_get(SculptSession *ss, ExpandCache *expand_
*/
static float sculpt_expand_gradient_value_get(SculptSession *ss,
ExpandCache *expand_cache,
- const int v)
+ const SculptVertRef v)
{
if (!expand_cache->falloff_gradient) {
return 1.0f;
@@ -364,7 +384,8 @@ static BLI_bitmap *sculpt_expand_bitmap_from_enabled(SculptSession *ss, ExpandCa
const int totvert = SCULPT_vertex_count_get(ss);
BLI_bitmap *enabled_vertices = BLI_BITMAP_NEW(totvert, "enabled vertices");
for (int i = 0; i < totvert; i++) {
- const bool enabled = sculpt_expand_state_get(ss, expand_cache, i);
+ const bool enabled = sculpt_expand_state_get(
+ ss, expand_cache, BKE_pbvh_table_index_to_vertex(ss->pbvh, i));
BLI_BITMAP_SET(enabled_vertices, i, enabled);
}
return enabled_vertices;
@@ -382,20 +403,22 @@ static BLI_bitmap *sculpt_expand_boundary_from_enabled(SculptSession *ss,
const int totvert = SCULPT_vertex_count_get(ss);
BLI_bitmap *boundary_vertices = BLI_BITMAP_NEW(totvert, "boundary vertices");
for (int i = 0; i < totvert; i++) {
+ SculptVertRef vertex = BKE_pbvh_table_index_to_vertex(ss->pbvh, i);
+
if (!BLI_BITMAP_TEST(enabled_vertices, i)) {
continue;
}
bool is_expand_boundary = false;
SculptVertexNeighborIter ni;
- SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, i, ni) {
+ SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vertex, ni) {
if (!BLI_BITMAP_TEST(enabled_vertices, ni.index)) {
is_expand_boundary = true;
}
}
SCULPT_VERTEX_NEIGHBORS_ITER_END(ni);
- if (use_mesh_boundary && SCULPT_vertex_is_boundary(ss, i)) {
+ if (use_mesh_boundary && SCULPT_vertex_is_boundary(ss, vertex, SCULPT_BOUNDARY_MESH)) {
is_expand_boundary = true;
}
@@ -411,12 +434,12 @@ static BLI_bitmap *sculpt_expand_boundary_from_enabled(SculptSession *ss,
* Utility function to get the closet vertex after flipping an original vertex position based on
* an symmetry pass iteration index.
*/
-static int sculpt_expand_get_vertex_index_for_symmetry_pass(Object *ob,
- const char symm_it,
- const int original_vertex)
+static SculptVertRef sculpt_expand_get_vertex_index_for_symmetry_pass(
+ Object *ob, const char symm_it, const SculptVertRef original_vertex)
{
SculptSession *ss = ob->sculpt;
- int symm_vertex = SCULPT_EXPAND_VERTEX_NONE;
+ SculptVertRef symm_vertex = {SCULPT_EXPAND_VERTEX_NONE};
+
if (symm_it == 0) {
symm_vertex = original_vertex;
}
@@ -432,7 +455,7 @@ static int sculpt_expand_get_vertex_index_for_symmetry_pass(Object *ob,
* Geodesic: Initializes the falloff with geodesic distances from the given active vertex, taking
* symmetry into account.
*/
-static float *sculpt_expand_geodesic_falloff_create(Sculpt *sd, Object *ob, const int v)
+static float *sculpt_expand_geodesic_falloff_create(Sculpt *sd, Object *ob, const SculptVertRef v)
{
return SCULPT_geodesic_from_vertex_and_symm(sd, ob, v, FLT_MAX);
}
@@ -449,20 +472,23 @@ typedef struct ExpandFloodFillData {
} ExpandFloodFillData;
static bool expand_topology_floodfill_cb(
- SculptSession *UNUSED(ss), int from_v, int to_v, bool is_duplicate, void *userdata)
+ SculptSession *ss, SculptVertRef from_v, SculptVertRef to_v, bool is_duplicate, void *userdata)
{
ExpandFloodFillData *data = userdata;
+ int from_v_i = BKE_pbvh_vertex_index_to_table(ss->pbvh, from_v);
+ int to_v_i = BKE_pbvh_vertex_index_to_table(ss->pbvh, to_v);
+
if (!is_duplicate) {
- const float to_it = data->dists[from_v] + 1.0f;
- data->dists[to_v] = to_it;
+ const float to_it = data->dists[from_v_i] + 1.0f;
+ data->dists[to_v_i] = to_it;
}
else {
- data->dists[to_v] = data->dists[from_v];
+ data->dists[to_v_i] = data->dists[from_v_i];
}
return true;
}
-static float *sculpt_expand_topology_falloff_create(Sculpt *sd, Object *ob, const int v)
+static float *sculpt_expand_topology_falloff_create(Sculpt *sd, Object *ob, const SculptVertRef v)
{
SculptSession *ss = ob->sculpt;
const int totvert = SCULPT_vertex_count_get(ss);
@@ -487,23 +513,26 @@ static float *sculpt_expand_topology_falloff_create(Sculpt *sd, Object *ob, cons
* This creates falloff patterns that follow and snap to the hard edges of the object.
*/
static bool mask_expand_normal_floodfill_cb(
- SculptSession *ss, int from_v, int to_v, bool is_duplicate, void *userdata)
+ SculptSession *ss, SculptVertRef from_v, SculptVertRef to_v, bool is_duplicate, void *userdata)
{
ExpandFloodFillData *data = userdata;
+ int from_v_i = BKE_pbvh_vertex_index_to_table(ss->pbvh, from_v);
+ int to_v_i = BKE_pbvh_vertex_index_to_table(ss->pbvh, to_v);
+
if (!is_duplicate) {
float current_normal[3], prev_normal[3];
SCULPT_vertex_normal_get(ss, to_v, current_normal);
SCULPT_vertex_normal_get(ss, from_v, prev_normal);
- const float from_edge_factor = data->edge_factor[from_v];
- data->edge_factor[to_v] = dot_v3v3(current_normal, prev_normal) * from_edge_factor;
- data->dists[to_v] = dot_v3v3(data->original_normal, current_normal) *
- powf(from_edge_factor, data->edge_sensitivity);
- CLAMP(data->dists[to_v], 0.0f, 1.0f);
+ const float from_edge_factor = data->edge_factor[from_v_i];
+ data->edge_factor[to_v_i] = dot_v3v3(current_normal, prev_normal) * from_edge_factor;
+ data->dists[to_v_i] = dot_v3v3(data->original_normal, current_normal) *
+ powf(from_edge_factor, data->edge_sensitivity);
+ CLAMP(data->dists[to_v_i], 0.0f, 1.0f);
}
else {
/* PBVH_GRIDS duplicate handling. */
- data->edge_factor[to_v] = data->edge_factor[from_v];
- data->dists[to_v] = data->dists[from_v];
+ data->edge_factor[to_v_i] = data->edge_factor[from_v_i];
+ data->dists[to_v_i] = data->dists[from_v_i];
}
return true;
@@ -511,7 +540,7 @@ static bool mask_expand_normal_floodfill_cb(
static float *sculpt_expand_normal_falloff_create(Sculpt *sd,
Object *ob,
- const int v,
+ const SculptVertRef v,
const float edge_sensitivity)
{
SculptSession *ss = ob->sculpt;
@@ -538,8 +567,10 @@ static float *sculpt_expand_normal_falloff_create(Sculpt *sd,
for (int repeat = 0; repeat < 2; repeat++) {
for (int i = 0; i < totvert; i++) {
float avg = 0.0f;
+ SculptVertRef vref = BKE_pbvh_table_index_to_vertex(ss->pbvh, i);
+
SculptVertexNeighborIter ni;
- SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, i, ni) {
+ SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vref, ni) {
avg += dists[ni.index];
}
SCULPT_VERTEX_NEIGHBORS_ITER_END(ni);
@@ -556,7 +587,7 @@ static float *sculpt_expand_normal_falloff_create(Sculpt *sd,
* Spherical: Initializes the falloff based on the distance from a vertex, taking symmetry into
* account.
*/
-static float *sculpt_expand_spherical_falloff_create(Object *ob, const int v)
+static float *sculpt_expand_spherical_falloff_create(Object *ob, const SculptVertRef v)
{
SculptSession *ss = ob->sculpt;
const int totvert = SCULPT_vertex_count_get(ss);
@@ -571,11 +602,14 @@ static float *sculpt_expand_spherical_falloff_create(Object *ob, const int v)
if (!SCULPT_is_symmetry_iteration_valid(symm_it, symm)) {
continue;
}
- const int symm_vertex = sculpt_expand_get_vertex_index_for_symmetry_pass(ob, symm_it, v);
- if (symm_vertex != -1) {
+ const SculptVertRef symm_vertex = sculpt_expand_get_vertex_index_for_symmetry_pass(
+ ob, symm_it, v);
+ if (symm_vertex.i != -1) {
const float *co = SCULPT_vertex_co_get(ss, symm_vertex);
for (int i = 0; i < totvert; i++) {
- dists[i] = min_ff(dists[i], len_v3v3(co, SCULPT_vertex_co_get(ss, i)));
+ dists[i] = min_ff(
+ dists[i],
+ len_v3v3(co, SCULPT_vertex_co_get(ss, BKE_pbvh_table_index_to_vertex(ss->pbvh, i))));
}
}
}
@@ -588,13 +622,13 @@ static float *sculpt_expand_spherical_falloff_create(Object *ob, const int v)
* 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 int v)
+static float *sculpt_expand_boundary_topology_falloff_create(Object *ob, const SculptVertRef v)
{
SculptSession *ss = ob->sculpt;
const int totvert = SCULPT_vertex_count_get(ss);
float *dists = MEM_calloc_arrayN(sizeof(float), totvert, "spherical dist");
BLI_bitmap *visited_vertices = BLI_BITMAP_NEW(totvert, "visited vertices");
- GSQueue *queue = BLI_gsqueue_new(sizeof(int));
+ GSQueue *queue = BLI_gsqueue_new(sizeof(SculptVertRef));
/* Search and initialize a boundary per symmetry pass, then mark those vertices as visited. */
const char symm = SCULPT_mesh_symmetry_xyz_get(ob);
@@ -603,7 +637,8 @@ static float *sculpt_expand_boundary_topology_falloff_create(Object *ob, const i
continue;
}
- const int symm_vertex = sculpt_expand_get_vertex_index_for_symmetry_pass(ob, symm_it, v);
+ const SculptVertRef symm_vertex = sculpt_expand_get_vertex_index_for_symmetry_pass(
+ ob, symm_it, v);
SculptBoundary *boundary = SCULPT_boundary_data_init(ob, NULL, symm_vertex, FLT_MAX);
if (!boundary) {
@@ -612,7 +647,8 @@ static float *sculpt_expand_boundary_topology_falloff_create(Object *ob, const i
for (int i = 0; i < boundary->num_vertices; i++) {
BLI_gsqueue_push(queue, &boundary->vertices[i]);
- BLI_BITMAP_ENABLE(visited_vertices, boundary->vertices[i]);
+ BLI_BITMAP_ENABLE(visited_vertices,
+ BKE_pbvh_vertex_index_to_table(ss->pbvh, boundary->vertices[i]));
}
SCULPT_boundary_data_free(boundary);
}
@@ -624,7 +660,7 @@ static float *sculpt_expand_boundary_topology_falloff_create(Object *ob, const i
/* Propagate the values from the boundaries to the rest of the mesh. */
while (!BLI_gsqueue_is_empty(queue)) {
- int v_next;
+ SculptVertRef v_next;
BLI_gsqueue_pop(queue, &v_next);
SculptVertexNeighborIter ni;
@@ -632,7 +668,10 @@ static float *sculpt_expand_boundary_topology_falloff_create(Object *ob, const i
if (BLI_BITMAP_TEST(visited_vertices, ni.index)) {
continue;
}
- dists[ni.index] = dists[v_next] + 1.0f;
+
+ const int v_next_i = BKE_pbvh_vertex_index_to_table(ss->pbvh, v_next);
+
+ dists[ni.index] = dists[v_next_i] + 1.0f;
BLI_BITMAP_ENABLE(visited_vertices, ni.index);
BLI_gsqueue_push(queue, &ni.index);
}
@@ -649,32 +688,38 @@ static float *sculpt_expand_boundary_topology_falloff_create(Object *ob, const i
* the base mesh faces when checking a vertex neighbor. For this reason, this is not implement
* using the general flood-fill and sculpt neighbors accessors.
*/
-static float *sculpt_expand_diagonals_falloff_create(Object *ob, const int v)
+static float *sculpt_expand_diagonals_falloff_create(Object *ob, const SculptVertRef v)
{
SculptSession *ss = ob->sculpt;
const int totvert = SCULPT_vertex_count_get(ss);
float *dists = MEM_calloc_arrayN(sizeof(float), totvert, "spherical dist");
/* 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;
}
+ 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 vertices using the enabled symmetry passes. */
BLI_bitmap *visited_vertices = BLI_BITMAP_NEW(totvert, "visited vertices");
- GSQueue *queue = BLI_gsqueue_new(sizeof(int));
+ GSQueue *queue = BLI_gsqueue_new(sizeof(SculptVertRef));
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;
}
- const int symm_vertex = sculpt_expand_get_vertex_index_for_symmetry_pass(ob, symm_it, v);
+ const SculptVertRef symm_vertex = sculpt_expand_get_vertex_index_for_symmetry_pass(
+ ob, symm_it, v);
BLI_gsqueue_push(queue, &symm_vertex);
- BLI_BITMAP_ENABLE(visited_vertices, symm_vertex);
+ BLI_BITMAP_ENABLE(visited_vertices, BKE_pbvh_vertex_index_to_table(ss->pbvh, symm_vertex));
}
if (BLI_gsqueue_is_empty(queue)) {
@@ -684,18 +729,50 @@ static float *sculpt_expand_diagonals_falloff_create(Object *ob, const int v)
/* Propagate the falloff increasing the value by 1 each time a new vertex is visited. */
Mesh *mesh = ob->data;
while (!BLI_gsqueue_is_empty(queue)) {
- int v_next;
+ SculptVertRef v_next;
BLI_gsqueue_pop(queue, &v_next);
- for (int j = 0; j < ss->pmap[v_next].count; j++) {
- MPoly *p = &ss->mpoly[ss->pmap[v_next].indices[j]];
- for (int l = 0; l < p->totloop; l++) {
- const int neighbor_v = mesh->mloop[p->loopstart + l].v;
- if (BLI_BITMAP_TEST(visited_vertices, neighbor_v)) {
- continue;
+
+ int v_next_i = BKE_pbvh_vertex_index_to_table(ss->pbvh, v_next);
+
+ 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_vertices, neighbor_v_i)) {
+ l = l->next;
+ continue;
+ }
+
+ dists[neighbor_v_i] = dists[v_next_i] + 1.0f;
+ BLI_BITMAP_ENABLE(visited_vertices, 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[v_next_i].count; j++) {
+ MPoly *p = &ss->mpoly[ss->pmap[v_next_i].indices[j]];
+ for (int l = 0; l < p->totloop; l++) {
+ const int neighbor_v = mesh->mloop[p->loopstart + l].v;
+
+ if (BLI_BITMAP_TEST(visited_vertices, neighbor_v)) {
+ continue;
+ }
+
+ dists[neighbor_v] = dists[v_next_i] + 1.0f;
+ BLI_BITMAP_ENABLE(visited_vertices, neighbor_v);
+ BLI_gsqueue_push(queue, &neighbor_v);
}
- dists[neighbor_v] = dists[v_next] + 1.0f;
- BLI_BITMAP_ENABLE(visited_vertices, neighbor_v);
- BLI_gsqueue_push(queue, &neighbor_v);
}
}
}
@@ -708,7 +785,7 @@ static float *sculpt_expand_diagonals_falloff_create(Object *ob, const int v)
/**
* Poly Loop:
*/
-static float *sculpt_expand_poly_loop_falloff_create(Object *ob, const int v)
+static float *sculpt_expand_poly_loop_falloff_create(Object *ob, const SculptVertRef v)
{
SculptSession *ss = ob->sculpt;
const int totvert = SCULPT_vertex_count_get(ss);
@@ -725,7 +802,9 @@ static float *sculpt_expand_poly_loop_falloff_create(Object *ob, const int v)
continue;
}
- const int symm_vertex = sculpt_expand_get_vertex_index_for_symmetry_pass(ob, symm_it, v);
+ const SculptVertRef symm_vertex = sculpt_expand_get_vertex_index_for_symmetry_pass(
+ ob, symm_it, v);
+ // const int symm_vertex_i = BKE_pbvh_vertex_index_to_table(ss->pbvh, symm_vertex);
BLI_bitmap *poly_loop = sculpt_poly_loop_from_cursor(ob);
@@ -750,15 +829,17 @@ static float *sculpt_expand_poly_loop_falloff_create(Object *ob, const int v)
/* Propagate the values from the boundaries to the rest of the mesh. */
while (!BLI_gsqueue_is_empty(queue)) {
- int v_next;
- BLI_gsqueue_pop(queue, &v_next);
+ int v_next_i;
+ BLI_gsqueue_pop(queue, &v_next_i);
+
+ SculptVertRef v_next = BKE_pbvh_table_index_to_vertex(ss->pbvh, v_next_i);
SculptVertexNeighborIter ni;
SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, v_next, ni) {
if (BLI_BITMAP_TEST(visited_vertices, ni.index)) {
continue;
}
- dists[ni.index] = dists[v_next] + 1.0f;
+ dists[ni.index] = dists[v_next_i] + 1.0f;
BLI_BITMAP_ENABLE(visited_vertices, ni.index);
BLI_gsqueue_push(queue, &ni.index);
}
@@ -782,12 +863,15 @@ 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++) {
if (expand_cache->vert_falloff[i] == FLT_MAX) {
continue;
}
- if (!sculpt_expand_is_vert_in_active_component(ss, expand_cache, i)) {
+ SculptVertRef v = BKE_pbvh_table_index_to_vertex(ss->pbvh, i);
+
+ if (!sculpt_expand_is_vert_in_active_component(ss, expand_cache, v)) {
continue;
}
@@ -806,11 +890,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++) {
+ SculptFaceRef f = BKE_pbvh_table_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;
}
@@ -858,6 +944,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.
*/
@@ -872,14 +975,16 @@ static void sculpt_expand_mesh_face_falloff_from_vertex_falloff(SculptSession *s
mesh->totpoly, sizeof(float), "face falloff factors");
}
- 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;
}
}
@@ -895,7 +1000,7 @@ static void sculpt_expand_geodesics_from_state_boundary(Object *ob,
BLI_bitmap *enabled_vertices)
{
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_vertices = BLI_gset_int_new("initial_vertices");
BLI_bitmap *boundary_vertices = sculpt_expand_boundary_from_enabled(ss, enabled_vertices, false);
@@ -911,7 +1016,8 @@ 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_vertices, FLT_MAX);
+ expand_cache->vert_falloff = SCULPT_geodesic_distances_create(
+ ob, initial_vertices, FLT_MAX, NULL, NULL);
BLI_gset_free(initial_vertices, NULL);
}
@@ -938,7 +1044,8 @@ static void sculpt_expand_topology_from_state_boundary(Object *ob,
if (!BLI_BITMAP_TEST(boundary_vertices, i)) {
continue;
}
- SCULPT_floodfill_add_and_skip_initial(&flood, i);
+
+ SCULPT_floodfill_add_and_skip_initial(ss, &flood, BKE_pbvh_table_index_to_vertex(ss->pbvh, i));
}
MEM_freeN(boundary_vertices);
@@ -1003,17 +1110,19 @@ static void sculpt_expand_initialize_from_face_set_boundary(Object *ob,
BLI_bitmap *enabled_vertices = BLI_BITMAP_NEW(totvert, "enabled vertices");
for (int i = 0; i < totvert; i++) {
- if (!SCULPT_vertex_has_unique_face_set(ss, i)) {
+ SculptVertRef vref = BKE_pbvh_table_index_to_vertex(ss->pbvh, i);
+
+ if (!SCULPT_vertex_has_unique_face_set(ss, vref)) {
continue;
}
- if (!SCULPT_vertex_has_face_set(ss, i, active_face_set)) {
+ if (!SCULPT_vertex_has_face_set(ss, vref, active_face_set)) {
continue;
}
BLI_BITMAP_ENABLE(enabled_vertices, i);
}
/* TODO: Default to topology. */
- if (BKE_pbvh_type(ss->pbvh) == PBVH_FACES && false) {
+ if (BKE_pbvh_type(ss->pbvh) == PBVH_FACES) {
sculpt_expand_geodesics_from_state_boundary(ob, expand_cache, enabled_vertices);
}
else {
@@ -1024,8 +1133,10 @@ static void sculpt_expand_initialize_from_face_set_boundary(Object *ob,
if (internal_falloff) {
for (int i = 0; i < totvert; i++) {
- if (!(SCULPT_vertex_has_face_set(ss, i, active_face_set) &&
- SCULPT_vertex_has_unique_face_set(ss, i))) {
+ SculptVertRef vref = BKE_pbvh_table_index_to_vertex(ss->pbvh, i);
+
+ 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;
@@ -1043,7 +1154,9 @@ static void sculpt_expand_initialize_from_face_set_boundary(Object *ob,
}
else {
for (int i = 0; i < totvert; i++) {
- if (!SCULPT_vertex_has_face_set(ss, i, active_face_set)) {
+ SculptVertRef vref = BKE_pbvh_table_index_to_vertex(ss->pbvh, i);
+
+ if (!SCULPT_vertex_has_face_set(ss, vref, active_face_set)) {
continue;
}
expand_cache->vert_falloff[i] = 0.0f;
@@ -1059,20 +1172,23 @@ static void sculpt_expand_falloff_factors_from_vertex_and_symm_create(
ExpandCache *expand_cache,
Sculpt *sd,
Object *ob,
- const int v,
+ const SculptVertRef v,
eSculptExpandFalloffType falloff_type)
{
MEM_SAFE_FREE(expand_cache->vert_falloff);
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);
@@ -1203,7 +1319,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];
+ SculptFaceRef f = BKE_pbvh_table_index_to_face(ss->pbvh, i);
+
+ SCULPT_face_set_set(ss, f, expand_cache->original_face_sets[i]);
}
}
@@ -1303,12 +1421,12 @@ static void sculpt_expand_mask_update_task_cb(void *__restrict userdata,
PBVHVertexIter vd;
BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_ALL) {
const float initial_mask = *vd.mask;
- const bool enabled = sculpt_expand_state_get(ss, expand_cache, vd.index);
+ const bool enabled = sculpt_expand_state_get(ss, expand_cache, vd.vertex);
float new_mask;
if (enabled) {
- new_mask = sculpt_expand_gradient_value_get(ss, expand_cache, vd.index);
+ new_mask = sculpt_expand_gradient_value_get(ss, expand_cache, vd.vertex);
}
else {
new_mask = 0.0f;
@@ -1340,16 +1458,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++) {
+ SculptFaceRef f = BKE_pbvh_table_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);
}
}
@@ -1377,11 +1499,11 @@ static void sculpt_expand_colors_update_task_cb(void *__restrict userdata,
float initial_color[4];
copy_v4_v4(initial_color, vd.col);
- const bool enabled = sculpt_expand_state_get(ss, expand_cache, vd.index);
+ const bool enabled = sculpt_expand_state_get(ss, expand_cache, vd.vertex);
float fade;
if (enabled) {
- fade = sculpt_expand_gradient_value_get(ss, expand_cache, vd.index);
+ fade = sculpt_expand_gradient_value_get(ss, expand_cache, vd.vertex);
}
else {
fade = 0.0f;
@@ -1443,22 +1565,28 @@ static void sculpt_expand_original_state_store(Object *ob, ExpandCache *expand_c
/* Face Sets are always stored as they are needed for snapping. */
expand_cache->initial_face_sets = MEM_malloc_arrayN(totface, sizeof(int), "initial face set");
expand_cache->original_face_sets = MEM_malloc_arrayN(totface, sizeof(int), "original face set");
+
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 SculptFaceRef fref = BKE_pbvh_table_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;
}
if (expand_cache->target == SCULPT_EXPAND_TARGET_MASK) {
expand_cache->original_mask = MEM_malloc_arrayN(totvert, sizeof(float), "initial mask");
for (int i = 0; i < totvert; i++) {
- expand_cache->original_mask[i] = SCULPT_vertex_mask_get(ss, i);
+ expand_cache->original_mask[i] = SCULPT_vertex_mask_get(
+ ss, BKE_pbvh_table_index_to_vertex(ss->pbvh, i));
}
}
if (expand_cache->target == SCULPT_EXPAND_TARGET_COLORS) {
expand_cache->original_colors = MEM_malloc_arrayN(totvert, sizeof(float[4]), "initial colors");
for (int i = 0; i < totvert; i++) {
- copy_v4_v4(expand_cache->original_colors[i], SCULPT_vertex_color_get(ss, i));
+ copy_v4_v4(expand_cache->original_colors[i],
+ SCULPT_vertex_color_get(ss, BKE_pbvh_table_index_to_vertex(ss->pbvh, i)));
}
}
}
@@ -1469,26 +1597,32 @@ 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++) {
+ SculptFaceRef fref = BKE_pbvh_table_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 int vertex)
+static void sculpt_expand_update_for_vertex(bContext *C, Object *ob, const SculptVertRef vertex)
{
SculptSession *ss = ob->sculpt;
+ const int vertex_i = BKE_pbvh_vertex_index_to_table(ss->pbvh, vertex);
+
Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
ExpandCache *expand_cache = ss->expand_cache;
/* Update the active factor in the cache. */
- if (vertex == SCULPT_EXPAND_VERTEX_NONE) {
+ 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
* connected components to enabled. */
@@ -1496,7 +1630,7 @@ static void sculpt_expand_update_for_vertex(bContext *C, Object *ob, const int v
expand_cache->all_enabled = true;
}
else {
- expand_cache->active_falloff = expand_cache->vert_falloff[vertex];
+ expand_cache->active_falloff = expand_cache->vert_falloff[vertex_i];
expand_cache->all_enabled = false;
}
@@ -1537,16 +1671,18 @@ static void sculpt_expand_update_for_vertex(bContext *C, Object *ob, const int v
* Updates the #SculptSession cursor data and gets the active vertex
* if the cursor is over the mesh.
*/
-static int sculpt_expand_target_vertex_update_and_get(bContext *C,
- Object *ob,
- const float mouse[2])
+static SculptVertRef sculpt_expand_target_vertex_update_and_get(bContext *C,
+ Object *ob,
+ const float mouse[2])
{
SculptSession *ss = ob->sculpt;
SculptCursorGeometryInfo sgi;
if (SCULPT_cursor_geometry_info_update(C, &sgi, mouse, false, false)) {
return SCULPT_active_vertex_get(ss);
}
- return SCULPT_EXPAND_VERTEX_NONE;
+
+ SculptVertRef ret = {SCULPT_EXPAND_VERTEX_NONE};
+ return ret;
}
/**
@@ -1566,8 +1702,8 @@ static void sculpt_expand_reposition_pivot(bContext *C, Object *ob, ExpandCache
/* For boundary topology, position the pivot using only the boundary of the enabled vertices,
* 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_vertices = sculpt_expand_boundary_from_enabled(
ss, enabled_vertices, use_mesh_boundary);
@@ -1586,11 +1722,13 @@ static void sculpt_expand_reposition_pivot(bContext *C, Object *ob, ExpandCache
continue;
}
- if (!sculpt_expand_is_vert_in_active_component(ss, expand_cache, i)) {
+ SculptVertRef v = BKE_pbvh_table_index_to_vertex(ss->pbvh, i);
+
+ if (!sculpt_expand_is_vert_in_active_component(ss, expand_cache, v)) {
continue;
}
- const float *vertex_co = SCULPT_vertex_co_get(ss, i);
+ const float *vertex_co = SCULPT_vertex_co_get(ss, v);
if (!SCULPT_check_vertex_pivot_symmetry(vertex_co, expand_init_co, symm)) {
continue;
@@ -1645,9 +1783,8 @@ static void sculpt_expand_finish(bContext *C)
* Finds and stores in the #ExpandCache the sculpt connected component index for each symmetry pass
* needed for expand.
*/
-static void sculpt_expand_find_active_connected_components_from_vert(Object *ob,
- ExpandCache *expand_cache,
- const int initial_vertex)
+static void sculpt_expand_find_active_connected_components_from_vert(
+ Object *ob, ExpandCache *expand_cache, const SculptVertRef initial_vertex)
{
SculptSession *ss = ob->sculpt;
for (int i = 0; i < EXPAND_SYMM_AREAS; i++) {
@@ -1660,11 +1797,12 @@ static void sculpt_expand_find_active_connected_components_from_vert(Object *ob,
continue;
}
- const int symm_vertex = sculpt_expand_get_vertex_index_for_symmetry_pass(
+ const SculptVertRef symm_vertex = sculpt_expand_get_vertex_index_for_symmetry_pass(
ob, symm_it, initial_vertex);
+ const int symm_vertex_i = BKE_pbvh_vertex_index_to_table(ss->pbvh, symm_vertex);
expand_cache->active_connected_components[(int)symm_it] =
- ss->vertex_info.connected_component[symm_vertex];
+ ss->vertex_info.connected_component[symm_vertex_i];
}
}
@@ -1678,8 +1816,9 @@ static void sculpt_expand_set_initial_components_for_mouse(bContext *C,
const float mouse[2])
{
SculptSession *ss = ob->sculpt;
- int initial_vertex = sculpt_expand_target_vertex_update_and_get(C, ob, mouse);
- if (initial_vertex == SCULPT_EXPAND_VERTEX_NONE) {
+ SculptVertRef initial_vertex = sculpt_expand_target_vertex_update_and_get(C, ob, mouse);
+
+ if (initial_vertex.i == SCULPT_EXPAND_VERTEX_NONE) {
/* Cursor not over the mesh, for creating valid initial falloffs, fallback to the last active
* vertex in the sculpt session. */
initial_vertex = SCULPT_active_vertex_get(ss);
@@ -1752,17 +1891,15 @@ 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_index_to_table(ss->pbvh, ss->active_face_index)];
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;
}
@@ -1785,7 +1922,8 @@ static int sculpt_expand_modal(bContext *C, wmOperator *op, const wmEvent *event
/* Update and get the active vertex (and face) from the cursor. */
const float mouse[2] = {event->mval[0], event->mval[1]};
- const int target_expand_vertex = sculpt_expand_target_vertex_update_and_get(C, ob, mouse);
+ const SculptVertRef target_expand_vertex = sculpt_expand_target_vertex_update_and_get(
+ C, ob, mouse);
/* Handle the modal keymap state changes. */
ExpandCache *expand_cache = ss->expand_cache;
@@ -1969,12 +2107,133 @@ static int sculpt_expand_modal(bContext *C, wmOperator *op, const wmEvent *event
* 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) {
+ SculptFaceRef 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++) {
+ SculptFaceRef fref = BKE_pbvh_table_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 SculptFaceRef 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`.
+ * 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(int *r_face_sets,
SculptSession *ss,
ExpandCache *expand_cache,
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;
@@ -1982,7 +2241,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_table_index_to_face(ss->pbvh, i))) {
continue;
}
if (r_face_sets[i] != delete_id) {
@@ -2130,6 +2390,9 @@ 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);
+
/* Create and configure the Expand Cache. */
ss->expand_cache = MEM_callocN(sizeof(ExpandCache), "expand cache");
sculpt_expand_cache_initial_config_set(C, op, ss->expand_cache);
@@ -2153,13 +2416,6 @@ static int sculpt_expand_invoke(bContext *C, wmOperator *op, const wmEvent *even
return OPERATOR_CANCELLED;
}
- /* 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;
- }
-
sculpt_expand_ensure_sculptsession_data(ob);
/* Initialize undo. */
@@ -2189,7 +2445,8 @@ static int sculpt_expand_invoke(bContext *C, wmOperator *op, const wmEvent *even
eSculptExpandFalloffType falloff_type = 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;
}
diff --git a/source/blender/editors/sculpt_paint/sculpt_face_set.c b/source/blender/editors/sculpt_paint/sculpt_face_set.c
index 29c3c7f1184..53ac6793f3c 100644
--- a/source/blender/editors/sculpt_paint/sculpt_face_set.c
+++ b/source/blender/editors/sculpt_paint/sculpt_face_set.c
@@ -23,7 +23,9 @@
#include "MEM_guardedalloc.h"
+#include "BLI_array.h"
#include "BLI_blenlib.h"
+#include "BLI_compiler_attrs.h"
#include "BLI_hash.h"
#include "BLI_math.h"
#include "BLI_task.h"
@@ -49,6 +51,7 @@
#include "BKE_paint.h"
#include "BKE_pbvh.h"
#include "BKE_scene.h"
+#include "BKE_subdiv_ccg.h"
#include "DEG_depsgraph.h"
@@ -72,6 +75,101 @@
#include <math.h>
#include <stdlib.h>
+static int sculpt_face_material_get(SculptSession *ss, SculptFaceRef 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->mpoly[face.i].mat_nr;
+ }
+
+ return -1;
+}
+
+int SCULPT_face_set_get(SculptSession *ss, SculptFaceRef 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;
+}
+
+// returns previous face set
+int SCULPT_face_set_set(SculptSession *ss, SculptFaceRef face, int fset)
+{
+ int ret = 0;
+
+ switch (BKE_pbvh_type(ss->pbvh)) {
+ case PBVH_BMESH: {
+ BMFace *f = (BMFace *)face.i;
+ ret = BM_ELEM_CD_GET_INT(f, ss->cd_faceset_offset);
+
+ BM_ELEM_CD_SET_INT(f, ss->cd_faceset_offset, fset);
+ break;
+ }
+ case PBVH_FACES:
+ case PBVH_GRIDS:
+ ret = ss->face_sets[face.i];
+ ss->face_sets[face.i] = fset;
+ break;
+ }
+
+ return ret;
+}
+
+int SCULPT_face_set_flag_get(SculptSession *ss, SculptFaceRef face, char flag)
+{
+ if (ss->bm) {
+ BMFace *f = (BMFace *)face.i;
+
+ flag = BM_face_flag_from_mflag(flag);
+ return f->head.hflag & flag;
+ }
+ else {
+ return ss->mpoly[face.i].flag & flag;
+ }
+}
+
+int SCULPT_face_set_flag_set(SculptSession *ss, SculptFaceRef face, char flag, bool state)
+{
+ int ret;
+
+ if (ss->bm) {
+ BMFace *f = (BMFace *)face.i;
+
+ flag = BM_face_flag_from_mflag(flag);
+ ret = f->head.hflag & flag;
+
+ if (state) {
+ f->head.hflag |= flag;
+ }
+ else {
+ f->head.hflag &= ~flag;
+ }
+ }
+ else {
+ ret = ss->mpoly[face.i].flag & flag;
+
+ if (state) {
+ ss->mpoly[face.i].flag |= flag;
+ }
+ else {
+ ss->mpoly[face.i].flag &= ~flag;
+ }
+ }
+
+ return ret;
+}
/* Utils. */
int ED_sculpt_face_sets_find_next_available_id(struct Mesh *mesh)
{
@@ -118,6 +216,34 @@ int ED_sculpt_face_sets_active_update_and_get(bContext *C, Object *ob, const flo
return SCULPT_active_face_set_get(ss);
}
+static BMesh *sculpt_faceset_bm_begin(SculptSession *ss, Mesh *mesh)
+{
+ if (ss->bm) {
+ return ss->bm;
+ }
+
+ const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(mesh);
+ BMesh *bm = BM_mesh_create(&allocsize,
+ &((struct BMeshCreateParams){
+ .use_toolflags = true,
+ }));
+
+ BM_mesh_bm_from_me(NULL,
+ bm,
+ mesh,
+ (&(struct BMeshFromMeshParams){
+ .calc_face_normal = true,
+ }));
+ 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,
@@ -135,8 +261,33 @@ static void do_draw_face_sets_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);
const int thread_id = BLI_task_parallel_thread_id(tls);
+ const int active_fset = abs(ss->cache->paint_face_set);
MVert *mvert = SCULPT_mesh_deformed_mverts_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 &&
+ (brush->automasking_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;
+ }
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
if (BKE_pbvh_type(ss->pbvh) == PBVH_FACES) {
@@ -157,15 +308,146 @@ static void do_draw_face_sets_brush_task_cb_ex(void *__restrict userdata,
vd.no,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
- vd.index,
+ vd.vertex,
thread_id);
- if (fade > 0.05f && ss->face_sets[vert_map->indices[j]] > 0) {
- ss->face_sets[vert_map->indices[j]] = abs(ss->cache->paint_face_set);
+ 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;
+ }
+ }
+
+ MLoop *ml = &ss->mloop[p->loopstart];
+
+ for (int i = 0; i < p->totloop; i++, ml++) {
+ MVert *v = &ss->mvert[ml->v];
+ float fno[3];
+
+ normal_short_to_float_v3(fno, v->no);
+ float mask = ss->vmask ? ss->vmask[ml->v] : 0.0f;
+
+ const float fade2 = bstrength *
+ SCULPT_brush_strength_factor(ss,
+ brush,
+ v->co,
+ sqrtf(test.dist),
+ v->no,
+ fno,
+ mask,
+ (SculptVertRef){.i = ml->v},
+ thread_id);
+
+ if (fade2 < test_limit) {
+ ok = false;
+ break;
+ }
+ }
+
+ if (ok) {
+ ss->face_sets[vert_map->indices[j]] = abs(ss->cache->paint_face_set);
+ }
}
}
}
+ 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);
+
+ int fset = BM_ELEM_CD_GET_INT(f, ss->cd_faceset_offset);
+
+ if (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 {
+ short sno[3];
+ float mask = cd_mask >= 0 ? BM_ELEM_CD_GET_FLOAT(l->v, cd_mask) : 0.0f;
+
+ normal_float_to_short_v3(sno, l->v->no);
+
+ const float fade2 = bstrength * SCULPT_brush_strength_factor(
+ ss,
+ brush,
+ l->v->co,
+ sqrtf(test.dist),
+ sno,
+ l->v->no,
+ mask,
+ (SculptVertRef){.i = (intptr_t)l->v},
+ thread_id);
+
+ if (fade2 < test_limit) {
+ ok = false;
+ break;
+ }
+
+ MDynTopoVert *mv = BKE_PBVH_DYNVERT(ss->cd_dyn_vert, l->v);
+ mv->flag |= DYNVERT_NEED_BOUNDARY;
+ } while ((l = l->next) != f->l_first);
+
+ if (ok) {
+ BM_ELEM_CD_SET_INT(f, ss->cd_faceset_offset, active_fset);
+ }
+ }
+ }
+ }
+ }
else if (BKE_pbvh_type(ss->pbvh) == PBVH_GRIDS) {
{
if (!sculpt_brush_test_sq_fn(&test, vd.co)) {
@@ -178,16 +460,21 @@ static void do_draw_face_sets_brush_task_cb_ex(void *__restrict userdata,
vd.no,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
- vd.index,
+ vd.vertex,
thread_id);
if (fade > 0.05f) {
- SCULPT_vertex_face_set_set(ss, vd.index, ss->cache->paint_face_set);
+ SCULPT_vertex_face_set_set(ss, vd.vertex, ss->cache->paint_face_set);
}
}
}
}
BKE_pbvh_vertex_iter_end;
+
+ // restore automasking flag
+ if (set_active_faceset) {
+ ss->cache->automasking->settings.flags |= automasking_fset_flag;
+ }
}
static void do_relax_face_sets_brush_task_cb_ex(void *__restrict userdata,
@@ -217,7 +504,7 @@ static void do_relax_face_sets_brush_task_cb_ex(void *__restrict userdata,
if (!sculpt_brush_test_sq_fn(&test, vd.co)) {
continue;
}
- if (relax_face_sets == SCULPT_vertex_has_unique_face_set(ss, vd.index)) {
+ if (relax_face_sets == SCULPT_vertex_has_unique_face_set(ss, vd.vertex)) {
continue;
}
@@ -228,7 +515,7 @@ static void do_relax_face_sets_brush_task_cb_ex(void *__restrict userdata,
vd.no,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
- vd.index,
+ vd.vertex,
thread_id);
SCULPT_relax_vertex(ss, &vd, fade * bstrength, relax_face_sets, vd.co);
@@ -254,8 +541,27 @@ void SCULPT_do_draw_face_sets_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, in
.nodes = nodes,
};
+ 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*/
+ if (SCULPT_stroke_is_first_brush_step(ss->cache)) {
+ if (ss->cache->invert && ss->cache->automasking &&
+ (brush->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,13 +622,11 @@ 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;
- }
-
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;
@@ -342,8 +646,11 @@ static int sculpt_face_set_create_exec(bContext *C, wmOperator *op)
if (mode == SCULPT_FACE_SET_MASKED) {
for (int i = 0; i < tot_vert; i++) {
- if (SCULPT_vertex_mask_get(ss, i) >= threshold && SCULPT_vertex_visible_get(ss, i)) {
- SCULPT_vertex_face_set_set(ss, i, next_face_set);
+ SculptVertRef vertex = BKE_pbvh_table_index_to_vertex(ss->pbvh, i);
+
+ if (SCULPT_vertex_mask_get(ss, vertex) >= threshold &&
+ SCULPT_vertex_visible_get(ss, vertex)) {
+ SCULPT_vertex_face_set_set(ss, vertex, next_face_set);
}
}
}
@@ -355,7 +662,9 @@ static int sculpt_face_set_create_exec(bContext *C, wmOperator *op)
* sets and the performance hit of rendering the overlay. */
bool all_visible = true;
for (int i = 0; i < tot_vert; i++) {
- if (!SCULPT_vertex_visible_get(ss, i)) {
+ SculptVertRef vertex = BKE_pbvh_table_index_to_vertex(ss->pbvh, i);
+
+ if (!SCULPT_vertex_visible_get(ss, vertex)) {
all_visible = false;
break;
}
@@ -369,41 +678,36 @@ static int sculpt_face_set_create_exec(bContext *C, wmOperator *op)
}
for (int i = 0; i < tot_vert; i++) {
- if (SCULPT_vertex_visible_get(ss, i)) {
- SCULPT_vertex_face_set_set(ss, i, next_face_set);
+ SculptVertRef vertex = BKE_pbvh_table_index_to_vertex(ss->pbvh, i);
+
+ if (SCULPT_vertex_visible_get(ss, vertex)) {
+ SCULPT_vertex_face_set_set(ss, vertex, next_face_set);
}
}
}
if (mode == SCULPT_FACE_SET_ALL) {
for (int i = 0; i < tot_vert; i++) {
- SCULPT_vertex_face_set_set(ss, i, next_face_set);
+ SculptVertRef vertex = BKE_pbvh_table_index_to_vertex(ss->pbvh, i);
+
+ SCULPT_vertex_face_set_set(ss, vertex, next_face_set);
}
}
if (mode == SCULPT_FACE_SET_SELECTION) {
- Mesh *mesh = ob->data;
- BMesh *bm;
- const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(mesh);
- bm = BM_mesh_create(&allocsize,
- &((struct BMeshCreateParams){
- .use_toolflags = true,
- }));
-
- BM_mesh_bm_from_me(bm,
- mesh,
- (&(struct BMeshFromMeshParams){
- .calc_face_normal = true,
- }));
+ const int totface = ss->totfaces;
- BMIter iter;
- BMFace *f;
- BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
- if (BM_elem_flag_test(f, BM_ELEM_SELECT)) {
- ss->face_sets[BM_elem_index_get(f)] = next_face_set;
+ for (int i = 0; i < totface; i++) {
+ SculptFaceRef fref = BKE_pbvh_table_index_to_face(ss->pbvh, i);
+
+ // XXX check hidden?
+ int ok = !SCULPT_face_set_flag_get(ss, fref, ME_HIDE);
+ ok = ok && SCULPT_face_set_flag_get(ss, fref, ME_FACE_SEL);
+
+ if (ok) {
+ SCULPT_face_set_set(ss, fref, next_face_set);
}
}
- BM_mesh_free(bm);
}
for (int i = 0; i < totnode; i++) {
@@ -578,25 +882,21 @@ static void sculpt_face_sets_init_flood_fill(Object *ob,
SculptSession *ss = ob->sculpt;
Mesh *mesh = ob->data;
BMesh *bm;
- const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(mesh);
- bm = BM_mesh_create(&allocsize,
- &((struct BMeshCreateParams){
- .use_toolflags = true,
- }));
- BM_mesh_bm_from_me(bm,
- mesh,
- (&(struct BMeshFromMeshParams){
- .calc_face_normal = true,
- }));
+ SCULPT_vertex_random_access_ensure(ss);
+ SCULPT_face_random_access_ensure(ss);
- BLI_bitmap *visited_faces = BLI_BITMAP_NEW(mesh->totpoly, "visited faces");
- const int totfaces = mesh->totpoly;
+ bm = sculpt_faceset_bm_begin(ss, mesh);
- int *face_sets = ss->face_sets;
+ int totface = bm->totface;
- BM_mesh_elem_table_init(bm, BM_FACE);
- BM_mesh_elem_table_ensure(bm, BM_FACE);
+ BLI_bitmap *visited_faces = BLI_BITMAP_NEW(ss->totfaces, "visited faces");
+ const int totfaces = ss->totfaces; // mesh->totpoly;
+
+ if (!ss->bm) {
+ BM_mesh_elem_index_ensure(bm, BM_FACE);
+ BM_mesh_elem_table_ensure(bm, BM_FACE);
+ }
int next_face_set = 1;
@@ -607,7 +907,9 @@ static void sculpt_face_sets_init_flood_fill(Object *ob,
GSQueue *queue;
queue = BLI_gsqueue_new(sizeof(int));
- face_sets[i] = next_face_set;
+ SculptFaceRef fref = BKE_pbvh_table_index_to_face(ss->pbvh, i);
+ SCULPT_face_set_set(ss, fref, next_face_set);
+
BLI_BITMAP_ENABLE(visited_faces, i);
BLI_gsqueue_push(queue, &i);
@@ -634,7 +936,9 @@ static void sculpt_face_sets_init_flood_fill(Object *ob,
continue;
}
- face_sets[neighbor_face_index] = next_face_set;
+ SculptFaceRef fref2 = BKE_pbvh_table_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);
}
@@ -648,44 +952,63 @@ static void sculpt_face_sets_init_flood_fill(Object *ob,
MEM_SAFE_FREE(visited_faces);
- BM_mesh_free(bm);
+ sculpt_faceset_bm_end(ss, bm);
}
static void sculpt_face_sets_init_loop(Object *ob, const int mode)
{
Mesh *mesh = ob->data;
SculptSession *ss = ob->sculpt;
- BMesh *bm;
- const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(mesh);
- bm = BM_mesh_create(&allocsize,
- &((struct BMeshCreateParams){
- .use_toolflags = true,
- }));
- BM_mesh_bm_from_me(bm,
- mesh,
- (&(struct BMeshFromMeshParams){
- .calc_face_normal = true,
- }));
- BMIter iter;
- BMFace *f;
+ SCULPT_face_random_access_ensure(ss);
- const int cd_fmaps_offset = CustomData_get_offset(&bm->pdata, CD_FACEMAP);
+ int cd_fmaps_offset = -1;
+ if (ss->bm) {
+ cd_fmaps_offset = CustomData_get_offset(&ss->bm->pdata, CD_FACEMAP);
+ }
+
+ Mesh *me = NULL;
+ int *fmaps = NULL;
+
+ if (BKE_pbvh_type(ss->pbvh) == PBVH_GRIDS) {
+ me = ob->data;
+ fmaps = CustomData_get_layer(&me->pdata, CD_FACEMAP);
+ }
+ else if (BKE_pbvh_type(ss->pbvh) == PBVH_FACES) {
+ fmaps = CustomData_get_layer(ss->pdata, CD_FACEMAP);
+ }
+
+ for (int i = 0; i < ss->totfaces; i++) {
+ SculptFaceRef fref = BKE_pbvh_table_index_to_face(ss->pbvh, i);
- BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
if (mode == SCULPT_FACE_SETS_FROM_MATERIALS) {
- ss->face_sets[BM_elem_index_get(f)] = (int)(f->mat_nr + 1);
+ SCULPT_face_set_set(ss, fref, (int)(sculpt_face_material_get(ss, fref) + 1));
}
else if (mode == SCULPT_FACE_SETS_FROM_FACE_MAPS) {
- if (cd_fmaps_offset != -1) {
- ss->face_sets[BM_elem_index_get(f)] = BM_ELEM_CD_GET_INT(f, cd_fmaps_offset) + 2;
- }
- else {
- ss->face_sets[BM_elem_index_get(f)] = 1;
+ 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);
}
}
- BM_mesh_free(bm);
}
static int sculpt_face_set_init_exec(bContext *C, wmOperator *op)
@@ -696,11 +1019,6 @@ static int sculpt_face_set_init_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;
- }
-
BKE_sculpt_update_object_for_edit(depsgraph, ob, true, false, false);
PBVH *pbvh = ob->sculpt->pbvh;
@@ -850,11 +1168,6 @@ static int sculpt_face_sets_change_visibility_exec(bContext *C, wmOperator *op)
SculptSession *ss = ob->sculpt;
Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
- /* Dyntopo not supported. */
- if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) {
- return OPERATOR_CANCELLED;
- }
-
BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true, false);
const int tot_vert = SCULPT_vertex_count_get(ss);
@@ -883,17 +1196,31 @@ static int sculpt_face_sets_change_visibility_exec(bContext *C, wmOperator *op)
* be synced from face sets to non-manifold vertices. */
if (BKE_pbvh_type(ss->pbvh) == PBVH_GRIDS) {
for (int i = 0; i < tot_vert; i++) {
- if (!SCULPT_vertex_visible_get(ss, i)) {
+ SculptVertRef vertex = BKE_pbvh_table_index_to_vertex(ss->pbvh, i);
+
+ if (!SCULPT_vertex_visible_get(ss, vertex)) {
hidden_vertex = true;
break;
}
}
}
+ else if (ss->bm) {
+ BMIter iter;
+ BMFace *f;
- for (int i = 0; i < ss->totfaces; i++) {
- if (ss->face_sets[i] <= 0) {
- hidden_vertex = true;
- break;
+ BM_ITER_MESH (f, &iter, ss->bm, BM_FACES_OF_MESH) {
+ if (BM_ELEM_CD_GET_INT(f, ss->cd_faceset_offset) <= 0) {
+ hidden_vertex = true;
+ break;
+ }
+ }
+ }
+ else {
+ for (int i = 0; i < ss->totfaces; i++) {
+ if (ss->face_sets[i] <= 0) {
+ hidden_vertex = true;
+ break;
+ }
}
}
@@ -960,8 +1287,8 @@ 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;
float mouse[2];
mouse[0] = event->mval[0];
@@ -1000,22 +1327,21 @@ static int sculpt_face_sets_randomize_colors_exec(bContext *C, wmOperator *UNUSE
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;
- }
-
PBVH *pbvh = ob->sculpt->pbvh;
PBVHNode **nodes;
int totnode;
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];
+
+ SculptFaceRef fref = BKE_pbvh_table_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);
@@ -1129,12 +1455,60 @@ static EnumPropertyItem prop_sculpt_face_sets_edit_types[] = {
{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;
+ BMFace **faces = NULL;
+ BLI_array_declare(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) {
+ BLI_array_append(faces, f);
+ }
+ }
+
+ for (int i = 0; i < BLI_array_len(faces); i++) {
+ BMFace *f = faces[i];
+ 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);
+ }
+
+ BLI_array_free(faces);
+}
+
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)
{
+ 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) {
@@ -1167,7 +1541,9 @@ static void sculpt_face_set_fill_component(Object *ob,
const int totvert = SCULPT_vertex_count_get(ss);
for (int i = 0; i < totvert; i++) {
- if (!SCULPT_vertex_has_face_set(ss, i, active_face_set_id)) {
+ SculptVertRef vertex = BKE_pbvh_table_index_to_vertex(ss->pbvh, i);
+
+ if (!SCULPT_vertex_has_face_set(ss, vertex, active_face_set_id)) {
continue;
}
const int vertex_connected_component = ss->vertex_info.connected_component[i];
@@ -1178,23 +1554,83 @@ static void sculpt_face_set_fill_component(Object *ob,
}
for (int i = 0; i < totvert; i++) {
+ SculptVertRef vertex = BKE_pbvh_table_index_to_vertex(ss->pbvh, i);
+
const int vertex_connected_component = ss->vertex_info.connected_component[i];
if (!BLI_gset_haskey(connected_components, POINTER_FROM_INT(vertex_connected_component))) {
continue;
}
- SCULPT_vertex_face_set_set(ss, i, active_face_set_id);
+ 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;
+ BMFace **faces = NULL;
+ BLI_array_declare(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) {
+ BLI_array_append(faces, f);
+ }
+ }
+
+ for (int i = 0; i < BLI_array_len(faces); i++) {
+ BMFace *f = faces[i];
+ 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)) {
+ BM_ELEM_CD_SET_INT(f,
+ ss->cd_faceset_offset,
+ BM_ELEM_CD_GET_INT(l->radial_next->f, ss->cd_faceset_offset));
+ break;
+ }
+ l = l->next;
+ } while (l != f->l_first);
+ }
+
+ BLI_array_free(faces);
+}
+
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);
for (int p = 0; p < mesh->totpoly; p++) {
if (!modify_hidden && prev_face_sets[p] <= 0) {
@@ -1219,20 +1655,28 @@ static void sculpt_face_set_shrink(Object *ob,
}
}
-static bool check_single_face_set(SculptSession *ss, int *face_sets, const bool check_visible_only)
+static bool check_single_face_set(SculptSession *ss, const bool check_visible_only)
{
+ 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 (face_sets[f] > 0) {
- first_face_set = face_sets[f];
+ SculptFaceRef fref = BKE_pbvh_table_index_to_face(ss->pbvh, f);
+ int fset = SCULPT_face_set_get(ss, fref);
+
+ if (fset > 0) {
+ first_face_set = fset;
break;
}
}
}
else {
- first_face_set = abs(face_sets[0]);
+ SculptFaceRef fref = BKE_pbvh_table_index_to_face(ss->pbvh, 0);
+ first_face_set = abs(SCULPT_face_set_get(ss, fref));
}
if (first_face_set == SCULPT_FACE_SET_NONE) {
@@ -1240,8 +1684,12 @@ static bool check_single_face_set(SculptSession *ss, int *face_sets, const bool
}
for (int f = 0; f < ss->totfaces; f++) {
- const int face_set_id = check_visible_only ? face_sets[f] : abs(face_sets[f]);
- if (face_set_id != first_face_set) {
+ SculptFaceRef fref = BKE_pbvh_table_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;
}
}
@@ -1256,39 +1704,65 @@ static void sculpt_face_set_delete_geometry(Object *ob,
Mesh *mesh = ob->data;
const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(mesh);
- BMesh *bm = BM_mesh_create(&allocsize,
- &((struct BMeshCreateParams){
- .use_toolflags = true,
- }));
- BM_mesh_bm_from_me(bm,
- mesh,
- (&(struct BMeshFromMeshParams){
- .calc_face_normal = true,
- }));
+ if (ss->bm) {
+ BMFace **faces = NULL;
+ BLI_array_declare(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);
- 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);
+ 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) {
+ BLI_array_append(faces, f);
+ }
+ }
+
+ for (int i = 0; i < BLI_array_len(faces); i++) {
+ BKE_pbvh_bmesh_remove_face(ss->pbvh, faces[i], true);
+ }
+
+ BLI_array_free(faces);
}
- 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 {
+ BMesh *bm = BM_mesh_create(&allocsize,
+ &((struct BMeshCreateParams){
+ .use_toolflags = true,
+ }));
- BM_mesh_bm_to_me(NULL,
- bm,
- ob->data,
- (&(struct BMeshToMeshParams){
- .calc_object_remap = false,
- }));
+ BM_mesh_bm_from_me(ob,
+ bm,
+ mesh,
+ (&(struct BMeshFromMeshParams){
+ .calc_face_normal = true,
+ }));
- BM_mesh_free(bm);
+ 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);
+
+ BM_mesh_bm_to_me(NULL,
+ ob,
+ bm,
+ ob->data,
+ (&(struct BMeshToMeshParams){
+ .calc_object_remap = false,
+ }));
+
+ BM_mesh_free(bm);
+ }
}
static void sculpt_face_set_edit_fair_face_set(Object *ob,
@@ -1296,21 +1770,31 @@ static void sculpt_face_set_edit_fair_face_set(Object *ob,
const int fair_order)
{
SculptSession *ss = ob->sculpt;
+
const int totvert = SCULPT_vertex_count_get(ss);
Mesh *mesh = ob->data;
bool *fair_vertices = MEM_malloc_arrayN(sizeof(bool), totvert, "fair vertices");
+ SCULPT_vertex_random_access_ensure(ss);
SCULPT_boundary_info_ensure(ob);
for (int i = 0; i < totvert; i++) {
- fair_vertices[i] = !SCULPT_vertex_is_boundary(ss, i) &&
- SCULPT_vertex_has_face_set(ss, i, active_face_set_id) &&
- SCULPT_vertex_has_unique_face_set(ss, i);
+ SculptVertRef vref = BKE_pbvh_table_index_to_vertex(ss->pbvh, i);
+
+ 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);
+ }
+
+ if (ss->bm) {
+ BKE_bmesh_prefair_and_fair_vertices(ss->bm, fair_vertices, fair_order);
+ }
+ else {
+ MVert *mvert = SCULPT_mesh_deformed_mverts_get(ss);
+ BKE_mesh_prefair_and_fair_vertices(mesh, mvert, fair_vertices, fair_order);
}
- MVert *mvert = SCULPT_mesh_deformed_mverts_get(ss);
- BKE_mesh_prefair_and_fair_vertices(mesh, mvert, fair_vertices, fair_order);
MEM_freeN(fair_vertices);
}
@@ -1323,13 +1807,13 @@ static void sculpt_face_set_apply_edit(Object *ob,
switch (mode) {
case SCULPT_FACE_SET_EDIT_GROW: {
- int *prev_face_sets = MEM_dupallocN(ss->face_sets);
+ int *prev_face_sets = ss->face_sets ? 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 = MEM_dupallocN(ss->face_sets);
+ int *prev_face_sets = ss->face_sets ? 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;
@@ -1371,20 +1855,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 (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 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. */
+ * 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;
@@ -1503,7 +1985,8 @@ static void sculpt_face_set_extrude_id(Object *ob, SculptSession *ss, const int
.use_toolflags = true,
}));
- BM_mesh_bm_from_me(bm,
+ BM_mesh_bm_from_me(NULL,
+ bm,
mesh,
(&(struct BMeshFromMeshParams){
.calc_face_normal = true,
@@ -1575,6 +2058,7 @@ static void sculpt_face_set_extrude_id(Object *ob, SculptSession *ss, const int
BM_mesh_elem_hflag_disable_all(bm, BM_VERT | BM_EDGE | BM_FACE, BM_ELEM_SELECT, false);
BM_mesh_bm_to_me(NULL,
+ NULL,
bm,
ob->data,
(&(struct BMeshToMeshParams){
@@ -1626,16 +2110,21 @@ static int sculpt_face_set_edit_modal(bContext *C, wmOperator *op, const wmEvent
fsecd->orig_co = MEM_calloc_arrayN(totvert, sizeof(float) * 3, "origco");
fsecd->orig_no = MEM_calloc_arrayN(totvert, sizeof(float) * 3, "origno");
for (int i = 0; i < totvert; i++) {
- copy_v3_v3(fsecd->orig_co[i], SCULPT_vertex_co_get(ss, i));
- SCULPT_vertex_normal_get(ss, i, fsecd->orig_no[i]);
+ SculptVertRef vertex = BKE_pbvh_table_index_to_vertex(ss->pbvh, i);
+
+ copy_v3_v3(fsecd->orig_co[i], SCULPT_vertex_co_get(ss, vertex));
+ SCULPT_vertex_normal_get(ss, vertex, fsecd->orig_no[i]);
}
}
MVert *mvert = SCULPT_mesh_deformed_mverts_get(ss);
for (int i = 0; i < totvert; i++) {
- if (!SCULPT_vertex_has_face_set(ss, i, fsecd->active_face_set)) {
+ SculptVertRef vertex = BKE_pbvh_table_index_to_vertex(ss->pbvh, i);
+
+ if (!SCULPT_vertex_has_face_set(ss, vertex, fsecd->active_face_set)) {
continue;
}
+
madd_v3_v3v3fl(mvert[i].co, fsecd->orig_co[i], fsecd->orig_no[i], extrude_disp);
mvert[i].flag |= ME_VERT_PBVH_UPDATE;
}
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 808515ab723..a304738366a 100644
--- a/source/blender/editors/sculpt_paint/sculpt_face_set_topology.c
+++ b/source/blender/editors/sculpt_paint/sculpt_face_set_topology.c
@@ -133,14 +133,15 @@ static int sculpt_face_set_by_topology_invoke(bContext *C, wmOperator *op, const
SCULPT_undo_push_begin(ob, "face set edit");
SCULPT_undo_push_node(ob, nodes[0], SCULPT_UNDO_FACE_SETS);
- const int initial_poly = ss->active_face_index;
- const int initial_edge = sculpt_poly_loop_initial_edge_from_cursor(ob);
+ const int initial_poly_i = BKE_pbvh_face_index_to_table(ss->pbvh, ss->active_face_index);
+ const SculptFaceRef initial_poly = ss->active_face_index;
+ const SculptEdgeRef 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 != ss->face_set_last_poly && initial_edge != ss->face_set_last_edge) {
+ 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 {
diff --git a/source/blender/editors/sculpt_paint/sculpt_filter_color.c b/source/blender/editors/sculpt_paint/sculpt_filter_color.c
index 859ca9b2421..01217f1d7a8 100644
--- a/source/blender/editors/sculpt_paint/sculpt_filter_color.c
+++ b/source/blender/editors/sculpt_paint/sculpt_filter_color.c
@@ -107,18 +107,18 @@ static void color_filter_task_cb(void *__restrict userdata,
const int mode = data->filter_type;
SculptOrigVertData orig_data;
- SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]);
+ SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COLOR);
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);
float orig_color[3], final_color[4], hsv_color[3];
int hue;
float brightness, contrast, gain, delta, offset;
float fade = vd.mask ? *vd.mask : 0.0f;
fade = 1.0f - fade;
fade *= data->filter_strength;
- fade *= SCULPT_automasking_factor_get(ss->filter_cache->automasking, ss, vd.index);
+ fade *= SCULPT_automasking_factor_get(ss->filter_cache->automasking, ss, vd.vertex);
if (fade == 0.0f) {
continue;
}
@@ -196,7 +196,7 @@ static void color_filter_task_cb(void *__restrict userdata,
case COLOR_FILTER_SMOOTH: {
fade = clamp_f(fade, -1.0f, 1.0f);
float smooth_color[4];
- SCULPT_neighbor_color_average(ss, smooth_color, vd.index);
+ SCULPT_neighbor_color_average(ss, smooth_color, vd.vertex);
blend_color_interpolate_float(final_color, vd.col, smooth_color, fade);
break;
}
@@ -281,7 +281,7 @@ static int sculpt_color_filter_invoke(bContext *C, wmOperator *op, const wmEvent
if (!ss->pbvh) {
return OPERATOR_CANCELLED;
}
- if (BKE_pbvh_type(pbvh) != PBVH_FACES) {
+ if (BKE_pbvh_type(pbvh) == PBVH_GRIDS) {
return OPERATOR_CANCELLED;
}
diff --git a/source/blender/editors/sculpt_paint/sculpt_filter_mask.c b/source/blender/editors/sculpt_paint/sculpt_filter_mask.c
index 97b960ff9e4..ab862ad1508 100644
--- a/source/blender/editors/sculpt_paint/sculpt_filter_mask.c
+++ b/source/blender/editors/sculpt_paint/sculpt_filter_mask.c
@@ -119,7 +119,7 @@ static void mask_filter_task_cb(void *__restrict userdata,
switch (mode) {
case MASK_FILTER_SMOOTH:
case MASK_FILTER_SHARPEN: {
- float val = SCULPT_neighbor_mask_average(ss, vd.index);
+ float val = SCULPT_neighbor_mask_average(ss, vd.vertex);
val -= *vd.mask;
@@ -139,7 +139,7 @@ static void mask_filter_task_cb(void *__restrict userdata,
}
case MASK_FILTER_GROW:
max = 0.0f;
- SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.index, ni) {
+ SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.vertex, ni) {
float vmask_f = data->prev_mask[ni.index];
if (vmask_f > max) {
max = vmask_f;
@@ -150,7 +150,7 @@ static void mask_filter_task_cb(void *__restrict userdata,
break;
case MASK_FILTER_SHRINK:
min = 1.0f;
- SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.index, ni) {
+ SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.vertex, ni) {
float vmask_f = data->prev_mask[ni.index];
if (vmask_f < min) {
min = vmask_f;
@@ -232,7 +232,9 @@ static int sculpt_mask_filter_exec(bContext *C, wmOperator *op)
if (ELEM(filter_type, MASK_FILTER_GROW, MASK_FILTER_SHRINK)) {
prev_mask = MEM_mallocN(num_verts * sizeof(float), "prevmask");
for (int j = 0; j < num_verts; j++) {
- prev_mask[j] = SCULPT_vertex_mask_get(ss, j);
+ SculptVertRef vertex = BKE_pbvh_table_index_to_vertex(ss->pbvh, j);
+
+ prev_mask[j] = SCULPT_vertex_mask_get(ss, vertex);
}
}
@@ -340,8 +342,12 @@ typedef enum MaskFilterStepDirectionType {
} MaskFilterStepDirectionType;
/* Grown/Shrink vertex callbacks. */
-static float sculpt_ipmask_vertex_grow_cb(SculptSession *ss, const int vertex, float *current_mask)
+static float sculpt_ipmask_vertex_grow_cb(SculptSession *ss,
+ const SculptVertRef vertex,
+ float *current_mask)
{
+ int vertex_i = BKE_pbvh_vertex_index_to_table(ss->pbvh, vertex);
+
float max = 0.0f;
SculptVertexNeighborIter ni;
SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vertex, ni) {
@@ -355,9 +361,11 @@ static float sculpt_ipmask_vertex_grow_cb(SculptSession *ss, const int vertex, f
}
static float sculpt_ipmask_vertex_shrink_cb(SculptSession *ss,
- const int vertex,
+ const SculptVertRef vertex,
float *current_mask)
{
+ int vertex_i = BKE_pbvh_vertex_index_to_table(ss->pbvh, vertex);
+
float min = 1.0f;
SculptVertexNeighborIter ni;
SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vertex, ni) {
@@ -372,10 +380,12 @@ static float sculpt_ipmask_vertex_shrink_cb(SculptSession *ss,
/* Smooth/Sharpen vertex callbacks. */
static float sculpt_ipmask_vertex_smooth_cb(SculptSession *ss,
- const int vertex,
+ const SculptVertRef vertex,
float *current_mask)
{
- float accum = current_mask[vertex];
+ int vertex_i = BKE_pbvh_vertex_index_to_table(ss->pbvh, vertex);
+
+ float accum = current_mask[vertex_i];
int total = 1;
SculptVertexNeighborIter ni;
SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vertex, ni) {
@@ -383,23 +393,25 @@ static float sculpt_ipmask_vertex_smooth_cb(SculptSession *ss,
total++;
}
SCULPT_VERTEX_NEIGHBORS_ITER_END(ni);
- return total > 0 ? accum / total : current_mask[vertex];
+ return total > 0 ? accum / total : current_mask[vertex_i];
}
static float sculpt_ipmask_vertex_sharpen_cb(SculptSession *ss,
- const int vertex,
+ const SculptVertRef vertex,
float *current_mask)
{
+ int vertex_i = BKE_pbvh_vertex_index_to_table(ss->pbvh, vertex);
+
float accum = 0.0f;
int total = 0;
- float vmask = current_mask[vertex];
+ 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];
+ const float avg = total > 0 ? accum / total : current_mask[vertex_i];
const float val = avg - vmask;
float new_mask;
@@ -436,22 +448,26 @@ static float sculpt_ipmask_vertex_sharpen_cb(SculptSession *ss,
/* Harder/Softer callbacks. */
#define SCULPT_IPMASK_FILTER_HARDER_SOFTER_STEP 0.01f
-static float sculpt_ipmask_vertex_harder_cb(SculptSession *UNUSED(ss),
- const int vertex,
+static float sculpt_ipmask_vertex_harder_cb(SculptSession *ss,
+ const SculptVertRef vertex,
float *current_mask)
{
- return clamp_f(current_mask[vertex] += current_mask[vertex] *
- SCULPT_IPMASK_FILTER_HARDER_SOFTER_STEP,
+ int vertex_i = BKE_pbvh_vertex_index_to_table(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 *UNUSED(ss),
- const int vertex,
+static float sculpt_ipmask_vertex_softer_cb(SculptSession *ss,
+ const SculptVertRef vertex,
float *current_mask)
{
- return clamp_f(current_mask[vertex] -= current_mask[vertex] *
- SCULPT_IPMASK_FILTER_HARDER_SOFTER_STEP,
+ int vertex_i = BKE_pbvh_vertex_index_to_table(ss->pbvh, vertex);
+
+ return clamp_f(current_mask[vertex_i] -= current_mask[vertex_i] *
+ SCULPT_IPMASK_FILTER_HARDER_SOFTER_STEP,
0.0f,
1.0f);
}
@@ -475,18 +491,22 @@ static float sculpt_ipmask_filter_contrast(const float mask, const float contras
return clamp_f(gain * mask + offset, 0.0f, 1.0f);
}
-static float sculpt_ipmask_vertex_contrast_increase_cb(SculptSession *UNUSED(ss),
- const int vertex,
+static float sculpt_ipmask_vertex_contrast_increase_cb(SculptSession *ss,
+ const SculptVertRef vertex,
float *current_mask)
{
- return sculpt_ipmask_filter_contrast(current_mask[vertex], SCULPT_IPMASK_FILTER_CONTRAST_STEP);
+ int vertex_i = BKE_pbvh_vertex_index_to_table(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 *UNUSED(ss),
- const int vertex,
+static float sculpt_ipmask_vertex_contrast_decrease_cb(SculptSession *ss,
+ const SculptVertRef vertex,
float *current_mask)
{
- return sculpt_ipmask_filter_contrast(current_mask[vertex],
+ int vertex_i = BKE_pbvh_vertex_index_to_table(ss->pbvh, vertex);
+
+ return sculpt_ipmask_filter_contrast(current_mask[vertex_i],
-1.0f * SCULPT_IPMASK_FILTER_CONTRAST_STEP);
}
@@ -532,13 +552,15 @@ static void ipmask_filter_compute_step_task_cb(void *__restrict userdata,
const TaskParallelTLS *__restrict UNUSED(tls))
{
SculptIPMaskFilterTaskData *data = userdata;
+ SculptVertRef vertex = BKE_pbvh_table_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, i, data->current_mask);
+ data->ss, vertex, data->current_mask);
}
else {
data->next_mask[i] = data->ss->filter_cache->mask_filter_step_backward(
- data->ss, i, data->current_mask);
+ data->ss, vertex, data->current_mask);
}
}
@@ -585,7 +607,9 @@ static void sculpt_ipmask_store_reference_step(SculptSession *ss)
}
for (int i = 0; i < totvert; i++) {
- ss->filter_cache->mask_filter_ref[i] = SCULPT_vertex_mask_get(ss, i);
+ SculptVertRef vertex = BKE_pbvh_table_index_to_vertex(ss->pbvh, i);
+
+ ss->filter_cache->mask_filter_ref[i] = SCULPT_vertex_mask_get(ss, vertex);
}
}
@@ -600,7 +624,7 @@ static void ipmask_filter_apply_task_cb(void *__restrict userdata,
PBVHVertexIter vd;
bool update = false;
BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
- if (SCULPT_automasking_factor_get(filter_cache->automasking, ss, vd.index) < 0.5f) {
+ if (SCULPT_automasking_factor_get(filter_cache->automasking, ss, vd.vertex) < 0.5f) {
continue;
}
@@ -800,14 +824,14 @@ static void ipmask_filter_apply_from_original_task_cb(
if (steps == 0) {
return;
}
- const float step_size = 1.0f/steps;
+ const float step_size = 1.0f / steps;
- SCULPT_orig_vert_data_init(&orig_data, data->ob, 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) {
- if (SCULPT_automasking_factor_get(filter_cache->automasking, ss, vd.index) < 0.5f) {
+ if (SCULPT_automasking_factor_get(filter_cache->automasking, ss, vd.vertex) < 0.5f) {
continue;
}
- SCULPT_orig_vert_data_update(&orig_data, &vd);
+ SCULPT_orig_vert_data_update(&orig_data, vd.vertex);
float new_mask = orig_data.mask;
switch (filter_type) {
case IPMASK_FILTER_ADD_SUBSTRACT:
@@ -870,7 +894,8 @@ static void sculpt_ipmask_apply_from_original_mask_data(Object *ob,
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);
+ return ELEM(
+ filter_type, IPMASK_FILTER_INVERT, IPMASK_FILTER_ADD_SUBSTRACT, IPMASK_FILTER_QUANTIZE);
}
static void ipmask_filter_restore_original_mask_task_cb(
@@ -881,10 +906,10 @@ static void ipmask_filter_restore_original_mask_task_cb(
PBVHNode *node = data->nodes[i];
SculptOrigVertData orig_data;
bool update = false;
- SCULPT_orig_vert_data_init(&orig_data, data->ob, node);
+ 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);
+ SCULPT_orig_vert_data_update(&orig_data, vd.vertex);
*vd.mask = orig_data.mask;
update = true;
if (vd.mvert) {
@@ -1175,9 +1200,9 @@ static float neighbor_dirty_mask(SculptSession *ss, PBVHVertexIter *vd)
zero_v3(avg);
SculptVertexNeighborIter ni;
- SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd->index, ni) {
+ SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd->vertex, ni) {
float normalized[3];
- sub_v3_v3v3(normalized, SCULPT_vertex_co_get(ss, ni.index), vd->co);
+ sub_v3_v3v3(normalized, SCULPT_vertex_co_get(ss, ni.vertex), vd->co);
normalize_v3(normalized);
add_v3_v3(avg, normalized);
total++;
diff --git a/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c b/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c
index 455a1d3b033..3248da83f73 100644
--- a/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c
+++ b/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c
@@ -324,7 +324,7 @@ static void mesh_filter_task_cb(void *__restrict userdata,
const eSculptMeshFilterType filter_type = data->filter_type;
SculptOrigVertData orig_data;
- SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[i]);
+ SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[i], SCULPT_UNDO_COORDS);
/* When using the relax face sets meshes filter,
* each 3 iterations, do a whole mesh relax to smooth the contents of the Face Set. */
@@ -334,12 +334,12 @@ 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);
+ SCULPT_orig_vert_data_update(&orig_data, vd.vertex);
float orig_co[3], val[3], avg[3], normal[3], disp[3], disp2[3], transform[3][3], final_pos[3];
float fade = vd.mask ? *vd.mask : 0.0f;
fade = 1.0f - fade;
fade *= data->filter_strength;
- fade *= SCULPT_automasking_factor_get(ss->filter_cache->automasking, ss, vd.index);
+ fade *= SCULPT_automasking_factor_get(ss->filter_cache->automasking, ss, vd.vertex);
if (fade == 0.0f && filter_type != MESH_FILTER_SURFACE_SMOOTH) {
/* Surface Smooth can't skip the loop for this vertex as it needs to calculate its
@@ -357,7 +357,7 @@ static void mesh_filter_task_cb(void *__restrict userdata,
}
if (filter_type == MESH_FILTER_RELAX_FACE_SETS) {
- if (relax_face_sets == SCULPT_vertex_has_unique_face_set(ss, vd.index)) {
+ if (relax_face_sets == SCULPT_vertex_has_unique_face_set(ss, vd.vertex)) {
continue;
}
}
@@ -365,7 +365,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.index);
+ SCULPT_neighbor_coords_average_interior(ss, avg, vd.vertex, 0.0f, NULL, false);
sub_v3_v3v3(val, avg, orig_co);
madd_v3_v3v3fl(val, orig_co, val, fade);
sub_v3_v3v3(disp, val, orig_co);
@@ -425,13 +425,22 @@ static void mesh_filter_task_cb(void *__restrict userdata,
break;
}
case MESH_FILTER_SURFACE_SMOOTH: {
+ SculptCustomLayer scl = {.cd_offset = -1,
+ .from_bmesh = ss->bm != NULL,
+ .elemsize = sizeof(float) * 3,
+ .data = ss->filter_cache->surface_smooth_laplacian_disp,
+ .is_cdlayer = false,
+ .layer = NULL};
+
SCULPT_surface_smooth_laplacian_step(ss,
disp,
vd.co,
- ss->filter_cache->surface_smooth_laplacian_disp,
- vd.index,
+ &scl,
+ vd.vertex,
orig_data.co,
- ss->filter_cache->surface_smooth_shape_preservation);
+ ss->filter_cache->surface_smooth_shape_preservation,
+ 0.0f,
+ false);
break;
}
case MESH_FILTER_SHARPEN: {
@@ -443,10 +452,10 @@ static void mesh_filter_task_cb(void *__restrict userdata,
float disp_sharpen[3] = {0.0f, 0.0f, 0.0f};
SculptVertexNeighborIter ni;
- SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.index, ni) {
+ SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.vertex, ni) {
float disp_n[3];
sub_v3_v3v3(
- disp_n, SCULPT_vertex_co_get(ss, ni.index), SCULPT_vertex_co_get(ss, vd.index));
+ disp_n, SCULPT_vertex_co_get(ss, ni.vertex), SCULPT_vertex_co_get(ss, vd.vertex));
mul_v3_fl(disp_n, ss->filter_cache->sharpen_factor[ni.index]);
add_v3_v3(disp_sharpen, disp_n);
}
@@ -456,7 +465,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.index);
+ SCULPT_neighbor_coords_average(ss, avg_co, vd.vertex, 0.0f, false);
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]));
@@ -518,9 +527,11 @@ static void mesh_filter_enhance_details_init_directions(SculptSession *ss)
filter_cache->detail_directions = MEM_malloc_arrayN(
totvert, sizeof(float[3]), "detail directions");
for (int i = 0; i < totvert; i++) {
+ SculptVertRef vertex = BKE_pbvh_table_index_to_vertex(ss->pbvh, i);
+
float avg[3];
- SCULPT_neighbor_coords_average(ss, avg, i);
- sub_v3_v3v3(filter_cache->detail_directions[i], avg, SCULPT_vertex_co_get(ss, i));
+ SCULPT_neighbor_coords_average(ss, avg, vertex, 0.0f, false);
+ sub_v3_v3v3(filter_cache->detail_directions[i], avg, SCULPT_vertex_co_get(ss, vertex));
}
}
@@ -533,7 +544,9 @@ static void mesh_filter_sphere_center_calculate(
const int totvert = SCULPT_vertex_count_get(ss);
float center_accum[3] = {0.0f};
for (int i = 0; i < totvert; i++) {
- add_v3_v3(center_accum, SCULPT_vertex_co_get(ss, i));
+ SculptVertRef vertex = BKE_pbvh_table_index_to_vertex(ss->pbvh, i);
+
+ add_v3_v3(center_accum, SCULPT_vertex_co_get(ss, vertex));
}
mul_v3_v3fl(filter_cache->sphere_center, center_accum, 1.0f / totvert);
} break;
@@ -549,7 +562,9 @@ static void mesh_filter_sphere_radius_calculate(SculptSession *ss)
FilterCache *filter_cache = ss->filter_cache;
float accum = 0.0f;
for (int i = 0; i < totvert; i++) {
- accum += len_v3v3(filter_cache->sphere_center, SCULPT_vertex_co_get(ss, i));
+ SculptVertRef vertex = BKE_pbvh_table_index_to_vertex(ss->pbvh, i);
+
+ accum += len_v3v3(filter_cache->sphere_center, SCULPT_vertex_co_get(ss, vertex));
}
filter_cache->sphere_radius = accum / totvert;
}
@@ -574,8 +589,10 @@ static void mesh_filter_init_limit_surface_co(SculptSession *ss)
filter_cache->limit_surface_co = MEM_malloc_arrayN(
sizeof(float[3]), totvert, "limit surface co");
+
for (int i = 0; i < totvert; i++) {
- SCULPT_vertex_limit_surface_get(ss, i, filter_cache->limit_surface_co[i]);
+ SculptVertRef vertex = BKE_pbvh_table_index_to_vertex(ss->pbvh, i);
+ SCULPT_vertex_limit_surface_get(ss, vertex, filter_cache->limit_surface_co[i]);
}
}
@@ -596,8 +613,10 @@ static void mesh_filter_sharpen_init(SculptSession *ss,
for (int i = 0; i < totvert; i++) {
float avg[3];
- SCULPT_neighbor_coords_average(ss, avg, i);
- sub_v3_v3v3(filter_cache->detail_directions[i], avg, SCULPT_vertex_co_get(ss, i));
+ SculptVertRef vertex = BKE_pbvh_table_index_to_vertex(ss->pbvh, i);
+
+ SCULPT_neighbor_coords_average(ss, avg, vertex, 0.0f, false);
+ 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]);
}
@@ -620,11 +639,12 @@ static void mesh_filter_sharpen_init(SculptSession *ss,
smooth_iterations++) {
for (int i = 0; i < totvert; i++) {
float direction_avg[3] = {0.0f, 0.0f, 0.0f};
+ SculptVertRef vertex = BKE_pbvh_table_index_to_vertex(ss->pbvh, i);
float sharpen_avg = 0;
int total = 0;
SculptVertexNeighborIter ni;
- SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, i, ni) {
+ SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vertex, ni) {
add_v3_v3(direction_avg, filter_cache->detail_directions[ni.index]);
sharpen_avg += filter_cache->sharpen_factor[ni.index];
total++;
@@ -651,15 +671,22 @@ static void mesh_filter_surface_smooth_displace_task_cb(
float fade = vd.mask ? *vd.mask : 0.0f;
fade = 1.0f - fade;
fade *= data->filter_strength;
- fade *= SCULPT_automasking_factor_get(ss->filter_cache->automasking, ss, vd.index);
+ fade *= SCULPT_automasking_factor_get(ss->filter_cache->automasking, ss, vd.vertex);
if (fade == 0.0f) {
continue;
}
+ SculptCustomLayer scl = {.cd_offset = -1,
+ .from_bmesh = ss->bm != NULL,
+ .elemsize = sizeof(float) * 3,
+ .data = ss->filter_cache->surface_smooth_laplacian_disp,
+ .is_cdlayer = false,
+ .layer = NULL};
+
SCULPT_surface_smooth_displace_step(ss,
vd.co,
- ss->filter_cache->surface_smooth_laplacian_disp,
- vd.index,
+ &scl,
+ vd.vertex,
ss->filter_cache->surface_smooth_current_vertex,
clamp_f(fade, 0.0f, 1.0f));
}
diff --git a/source/blender/editors/sculpt_paint/sculpt_geodesic.c b/source/blender/editors/sculpt_paint/sculpt_geodesic.c
index d86d0938300..ecdbbe9280e 100644
--- a/source/blender/editors/sculpt_paint/sculpt_geodesic.c
+++ b/source/blender/editors/sculpt_paint/sculpt_geodesic.c
@@ -23,10 +23,16 @@
#include "MEM_guardedalloc.h"
+#include "BLI_alloca.h"
+#include "BLI_array.h"
#include "BLI_blenlib.h"
#include "BLI_linklist_stack.h"
#include "BLI_math.h"
+#include "BLI_memarena.h"
+#include "BLI_mempool.h"
+#include "BLI_sort_utils.h"
#include "BLI_task.h"
+#include "BLI_utildefines.h"
#include "BLT_translation.h"
@@ -77,8 +83,14 @@
#define SCULPT_GEODESIC_VERTEX_NONE -1
/* Propagate distance from v1 and v2 to v0. */
-static bool sculpt_geodesic_mesh_test_dist_add(
- MVert *mvert, const int v0, const int v1, const int v2, float *dists, GSet *initial_vertices)
+static bool sculpt_geodesic_mesh_test_dist_add(MVert *mvert,
+ const int v0,
+ const int v1,
+ const int v2,
+ float *dists,
+ GSet *initial_vertices,
+ SculptVertRef *r_closest_verts,
+ float (*cos)[3])
{
if (BLI_gset_haskey(initial_vertices, POINTER_FROM_INT(v0))) {
return false;
@@ -89,23 +101,202 @@ static bool sculpt_geodesic_mesh_test_dist_add(
return false;
}
+ float *co0 = cos ? cos[v0] : mvert[v0].co;
+ float *co1 = cos ? cos[v1] : mvert[v1].co;
+ float *co2 = v2 != SCULPT_GEODESIC_VERTEX_NONE ? (cos ? cos[v2] : mvert[v2].co) : NULL;
+
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(
- mvert[v0].co, mvert[v1].co, mvert[v2].co, 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, mvert[v1].co, mvert[v0].co);
+ 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_vertices,
+ SculptVertRef *r_closest_verts,
+ float (*cos)[3])
+{
+ if (BLI_gset_haskey(initial_vertices, 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_table_index_to_vertex(ss->pbvh, v0));
+ const float *co1 = cos ? cos[v1] :
+ SCULPT_vertex_co_get(ss, BKE_pbvh_table_index_to_vertex(ss->pbvh, v1));
+ const float *co2 = v2 != SCULPT_GEODESIC_VERTEX_NONE ?
+ (cos ? cos[v2] :
+ SCULPT_vertex_co_get(
+ ss, BKE_pbvh_table_index_to_vertex(ss->pbvh, v2))) :
+ NULL;
+
+ 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_vertices,
+ SculptVertRef *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) : NULL;
+
+ 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;
}
@@ -114,7 +305,9 @@ static bool sculpt_geodesic_mesh_test_dist_add(
static float *SCULPT_geodesic_mesh_create(Object *ob,
GSet *initial_vertices,
- const float limit_radius)
+ const float limit_radius,
+ SculptVertRef *r_closest_verts,
+ float (*cos)[3])
{
SculptSession *ss = ob->sculpt;
Mesh *mesh = BKE_object_get_original_mesh(ob);
@@ -142,7 +335,7 @@ static float *SCULPT_geodesic_mesh_create(Object *ob,
}
if (!ss->vemap) {
BKE_mesh_vert_edge_map_create(
- &ss->vemap, &ss->vemap_mem, mesh->medge, mesh->totvert, mesh->totedge);
+ &ss->vemap, &ss->vemap_mem, mesh->mvert, mesh->medge, mesh->totvert, mesh->totedge, true);
}
/* Both contain edge indices encoded as *void. */
@@ -154,9 +347,17 @@ static float *SCULPT_geodesic_mesh_create(Object *ob,
for (int i = 0; i < totvert; i++) {
if (BLI_gset_haskey(initial_vertices, POINTER_FROM_INT(i))) {
+ if (r_closest_verts) {
+ r_closest_verts[i] = BKE_pbvh_table_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;
}
}
@@ -177,9 +378,10 @@ static float *SCULPT_geodesic_mesh_create(Object *ob,
* number of vertices (usually just 1 or 2). */
GSET_ITER (gs_iter, initial_vertices) {
const int v = POINTER_AS_INT(BLI_gsetIterator_getKey(&gs_iter));
- float *v_co = verts[v].co;
+ float *v_co = cos ? cos[v] : verts[v].co;
+
for (int i = 0; i < totvert; i++) {
- if (len_squared_v3v3(v_co, verts[i].co) <= limit_radius_sq) {
+ if (len_squared_v3v3(v_co, cos ? cos[i] : verts[i].co) <= limit_radius_sq) {
BLI_BITMAP_ENABLE(affected_vertex, i);
}
}
@@ -208,8 +410,14 @@ static float *SCULPT_geodesic_mesh_create(Object *ob,
if (dists[v1] > dists[v2]) {
SWAP(int, v1, v2);
}
- sculpt_geodesic_mesh_test_dist_add(
- verts, v2, v1, SCULPT_GEODESIC_VERTEX_NONE, dists, initial_vertices);
+ sculpt_geodesic_mesh_test_dist_add(verts,
+ v2,
+ v1,
+ SCULPT_GEODESIC_VERTEX_NONE,
+ dists,
+ initial_vertices,
+ r_closest_verts,
+ cos);
}
if (ss->epmap[e].count != 0) {
@@ -227,7 +435,7 @@ static float *SCULPT_geodesic_mesh_create(Object *ob,
continue;
}
if (sculpt_geodesic_mesh_test_dist_add(
- verts, v_other, v1, v2, dists, initial_vertices)) {
+ verts, v_other, v1, v2, dists, initial_vertices, r_closest_verts, cos)) {
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];
@@ -271,6 +479,501 @@ static float *SCULPT_geodesic_mesh_create(Object *ob,
return dists;
}
+static float *SCULPT_geodesic_bmesh_create(Object *ob,
+ GSet *initial_vertices,
+ const float limit_radius,
+ SculptVertRef *r_closest_verts,
+ float (*cos)[3])
+{
+ SculptSession *ss = ob->sculpt;
+
+ if (!ss->bm) {
+ return NULL;
+ }
+
+ 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 = 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_vertices, POINTER_FROM_INT(i))) {
+ dists[i] = 0.0f;
+
+ if (r_closest_verts) {
+ r_closest_verts[i] = BKE_pbvh_table_index_to_vertex(ss->pbvh, i);
+ }
+ }
+ else {
+ dists[i] = FLT_MAX;
+ }
+ }
+
+ /* Masks vertices 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 vertices 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). */
+ GSET_ITER (gs_iter, initial_vertices) {
+ const int v_i = POINTER_AS_INT(BLI_gsetIterator_getKey(&gs_iter));
+ BMVert *v = (BMVert *)BKE_pbvh_table_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_table_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, NULL, dists, initial_vertices, 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_vertices, 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));
+
+ BLI_LINKSTACK_FREE(queue);
+ BLI_LINKSTACK_FREE(queue_next);
+ MEM_SAFE_FREE(edge_tag);
+ MEM_SAFE_FREE(affected_vertex);
+
+ 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_vertices,
+ const float limit_radius,
+ SculptVertRef *r_closest_verts,
+ float (*cos)[3])
+{
+ SculptSession *ss = ob->sculpt;
+ Mesh *mesh = BKE_object_get_original_mesh(ob);
+
+ const int totvert = SCULPT_vertex_count_get(ss);
+
+ const float limit_radius_sq = limit_radius * limit_radius;
+
+ float *dists = 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_vertices, POINTER_FROM_INT(i))) {
+ if (r_closest_verts) {
+ r_closest_verts[i] = BKE_pbvh_table_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
+ * 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
+ * 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). */
+ GSET_ITER (gs_iter, initial_vertices) {
+ const int v = POINTER_AS_INT(BLI_gsetIterator_getKey(&gs_iter));
+ SculptVertRef vertex = BKE_pbvh_table_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_table_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;
+
+ TempEdge *edges = NULL;
+ BLI_array_declare(edges);
+ GHash *ehash = BLI_ghash_ptr_new("geodesic multigrids ghash");
+
+ MeshElemMap *vmap = 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++) {
+ SculptVertRef vertex = BKE_pbvh_table_index_to_vertex(ss->pbvh, i);
+ MeshElemMap *map = vmap + i;
+
+ int val = SCULPT_vertex_valence_get(ss, vertex);
+ map->count = val;
+ map->indices = 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};
+ BLI_array_append(edges, te);
+ totedge++;
+ }
+
+ map->indices[j] = POINTER_AS_INT(*val);
+ j++;
+ }
+ SCULPT_VERTEX_NEIGHBORS_ITER_END(ni);
+ }
+
+ int(*e_otherv_map)[4] = 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;
+ SculptVertexNeighborIter ni2;
+
+ 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, 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, 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_vertices,
+ r_closest_verts,
+ cos);
+ }
+
+ TempEdge *te = edges + e;
+
+ 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_vertices, 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, NULL, NULL);
+ 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
* calculate the distance. */
@@ -279,16 +982,17 @@ static float *SCULPT_geodesic_fallback_create(Object *ob, GSet *initial_vertices
SculptSession *ss = ob->sculpt;
Mesh *mesh = BKE_object_get_original_mesh(ob);
- const int totvert = mesh->totvert;
+ const int totvert = SCULPT_vertex_count_get(ss);
float *dists = MEM_malloc_arrayN(totvert, sizeof(float), "distances");
- int first_affected = SCULPT_GEODESIC_VERTEX_NONE;
+ SculptVertRef first_affected = {SCULPT_GEODESIC_VERTEX_NONE};
+
GSetIterator gs_iter;
GSET_ITER (gs_iter, initial_vertices) {
- first_affected = POINTER_AS_INT(BLI_gsetIterator_getKey(&gs_iter));
+ first_affected.i = POINTER_AS_INT(BLI_gsetIterator_getKey(&gs_iter));
break;
}
- if (first_affected == SCULPT_GEODESIC_VERTEX_NONE) {
+ if (first_affected.i == SCULPT_GEODESIC_VERTEX_NONE) {
for (int i = 0; i < totvert; i++) {
dists[i] = FLT_MAX;
}
@@ -297,7 +1001,8 @@ static float *SCULPT_geodesic_fallback_create(Object *ob, GSet *initial_vertices
const float *first_affected_co = SCULPT_vertex_co_get(ss, first_affected);
for (int i = 0; i < totvert; i++) {
- dists[i] = len_v3v3(first_affected_co, SCULPT_vertex_co_get(ss, i));
+ dists[i] = len_v3v3(first_affected_co,
+ SCULPT_vertex_co_get(ss, BKE_pbvh_table_index_to_vertex(ss->pbvh, i)));
}
return dists;
@@ -305,15 +1010,22 @@ static float *SCULPT_geodesic_fallback_create(Object *ob, GSet *initial_vertices
float *SCULPT_geodesic_distances_create(Object *ob,
GSet *initial_vertices,
- const float limit_radius)
+ const float limit_radius,
+ SculptVertRef *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_vertices, limit_radius);
+ return SCULPT_geodesic_mesh_create(
+ ob, initial_vertices, limit_radius, r_closest_verts, vertco_override);
case PBVH_BMESH:
+ return SCULPT_geodesic_bmesh_create(
+ ob, initial_vertices, limit_radius, r_closest_verts, vertco_override);
case PBVH_GRIDS:
- return SCULPT_geodesic_fallback_create(ob, initial_vertices);
+ return SCULPT_geodesic_grids_create(
+ ob, initial_vertices, limit_radius, r_closest_verts, vertco_override);
+ // return SCULPT_geodesic_fallback_create(ob, initial_vertices);
}
BLI_assert(false);
return NULL;
@@ -321,7 +1033,7 @@ float *SCULPT_geodesic_distances_create(Object *ob,
float *SCULPT_geodesic_from_vertex_and_symm(Sculpt *sd,
Object *ob,
- const int vertex,
+ const SculptVertRef vertex,
const float limit_radius)
{
SculptSession *ss = ob->sculpt;
@@ -330,7 +1042,8 @@ float *SCULPT_geodesic_from_vertex_and_symm(Sculpt *sd,
const char symm = SCULPT_mesh_symmetry_xyz_get(ob);
for (char i = 0; i <= symm; ++i) {
if (SCULPT_is_symmetry_iteration_valid(i, symm)) {
- int v = -1;
+ SculptVertRef v = {-1};
+
if (i == 0) {
v = vertex;
}
@@ -339,22 +1052,34 @@ float *SCULPT_geodesic_from_vertex_and_symm(Sculpt *sd,
flip_v3_v3(location, SCULPT_vertex_co_get(ss, vertex), i);
v = SCULPT_nearest_vertex_get(sd, ob, location, FLT_MAX, false);
}
- if (v != -1) {
- BLI_gset_add(initial_vertices, POINTER_FROM_INT(v));
+
+ const int v_i = BKE_pbvh_vertex_index_to_table(ss->pbvh, v);
+
+ if (v_i != -1) {
+ BLI_gset_add(initial_vertices, POINTER_FROM_INT(v_i));
}
}
}
- float *dists = SCULPT_geodesic_distances_create(ob, initial_vertices, limit_radius);
+ float *dists = SCULPT_geodesic_distances_create(ob, initial_vertices, limit_radius, NULL, NULL);
BLI_gset_free(initial_vertices, NULL);
return dists;
}
-float *SCULPT_geodesic_from_vertex(Object *ob, const int vertex, const float limit_radius)
+float *SCULPT_geodesic_from_vertex(Object *ob,
+ const SculptVertRef vertex,
+ const float limit_radius)
{
+ SculptSession *ss = ob->sculpt;
+
+ SCULPT_vertex_random_access_ensure(ss);
+
GSet *initial_vertices = BLI_gset_int_new("initial_vertices");
- BLI_gset_add(initial_vertices, POINTER_FROM_INT(vertex));
- float *dists = SCULPT_geodesic_distances_create(ob, initial_vertices, limit_radius);
+
+ BLI_gset_add(initial_vertices,
+ POINTER_FROM_INT(BKE_pbvh_vertex_index_to_table(ss->pbvh, vertex)));
+
+ float *dists = SCULPT_geodesic_distances_create(ob, initial_vertices, limit_radius, NULL, NULL);
BLI_gset_free(initial_vertices, NULL);
return dists;
}
diff --git a/source/blender/editors/sculpt_paint/sculpt_gradient.c b/source/blender/editors/sculpt_paint/sculpt_gradient.c
index 3b8d839af35..c17eb1d4afb 100644
--- a/source/blender/editors/sculpt_paint/sculpt_gradient.c
+++ b/source/blender/editors/sculpt_paint/sculpt_gradient.c
@@ -86,12 +86,12 @@ static void sculpt_gradient_apply_task_cb(void *__restrict userdata,
SculptGradientContext *gcontext = ss->filter_cache->gradient_context;
SculptOrigVertData orig_data;
- SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]);
+ 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) {
float fade = vd.mask ? *vd.mask : 0.0f;
- fade *= SCULPT_automasking_factor_get(ss->filter_cache->automasking, ss, vd.index);
+ fade *= SCULPT_automasking_factor_get(ss->filter_cache->automasking, ss, vd.vertex);
if (fade == 0.0f) {
continue;
}
diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.h b/source/blender/editors/sculpt_paint/sculpt_intern.h
index e5aa3ffa7de..7eb1707c6d8 100644
--- a/source/blender/editors/sculpt_paint/sculpt_intern.h
+++ b/source/blender/editors/sculpt_paint/sculpt_intern.h
@@ -30,14 +30,18 @@
#include "DNA_vec_types.h"
#include "BLI_bitmap.h"
+#include "BLI_compiler_compat.h"
#include "BLI_gsqueue.h"
#include "BLI_threads.h"
#include "ED_view3d.h"
+#include "BKE_attribute.h"
#include "BKE_paint.h"
#include "BKE_pbvh.h"
+#include "bmesh.h"
+
struct AutomaskingCache;
struct KeyBlock;
struct Object;
@@ -46,6 +50,24 @@ struct bContext;
enum ePaintSymmetryFlags;
+typedef struct SculptCustomLayer {
+ bool is_cdlayer; // false for multires data
+ void *data; // only valid for multires and face
+ int elemsize;
+ int cd_offset; // for bmesh
+ CustomDataLayer *layer; // not for multires
+ bool from_bmesh; // note that layers can be fixed arrays but still from a bmesh, e.g. filter
+ // laplacian smooth
+} SculptCustomLayer;
+
+/*
+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
+
bool SCULPT_mode_poll(struct bContext *C);
bool SCULPT_mode_poll_view3d(struct bContext *C);
/* checks for a brush, not just sculpt mode */
@@ -53,6 +75,7 @@ bool SCULPT_poll(struct bContext *C);
bool SCULPT_poll_view3d(struct bContext *C);
bool SCULPT_vertex_colors_poll(struct bContext *C);
+bool SCULPT_vertex_colors_poll_no_bmesh(struct bContext *C);
/* Updates */
@@ -63,12 +86,12 @@ typedef enum SculptUpdateType {
SCULPT_UPDATE_COLOR = 1 << 3,
} SculptUpdateType;
-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_flush_update_step(struct bContext *C, SculptUpdateType update_flags);
+void SCULPT_flush_update_done(const struct bContext *C, Object *ob, SculptUpdateType update_flags);
void SCULPT_flush_stroke_deform(struct Sculpt *sd, Object *ob, bool is_proxy_used);
/* Should be used after modifying the mask or Face Sets IDs. */
-void SCULPT_tag_update_overlays(bContext *C);
+void SCULPT_tag_update_overlays(struct bContext *C);
/* Stroke */
@@ -100,22 +123,30 @@ char SCULPT_mesh_symmetry_xyz_get(Object *object);
/* Sculpt PBVH abstraction API */
void SCULPT_vertex_random_access_ensure(struct SculptSession *ss);
+void SCULPT_face_random_access_ensure(struct SculptSession *ss);
-int SCULPT_vertex_count_get(struct SculptSession *ss);
-const float *SCULPT_vertex_co_get(struct SculptSession *ss, int index);
-void SCULPT_vertex_normal_get(SculptSession *ss, int index, float no[3]);
-float SCULPT_vertex_mask_get(struct SculptSession *ss, int index);
-const float *SCULPT_vertex_color_get(SculptSession *ss, int index);
+int SCULPT_vertex_valence_get(const struct SculptSession *ss, SculptVertRef vertex);
-const float *SCULPT_vertex_persistent_co_get(SculptSession *ss, int index);
-void SCULPT_vertex_persistent_normal_get(SculptSession *ss, int index, float no[3]);
+int SCULPT_vertex_count_get(struct SculptSession *ss);
+const float *SCULPT_vertex_co_get(struct SculptSession *ss, SculptVertRef index);
+void SCULPT_vertex_normal_get(SculptSession *ss, SculptVertRef index, float no[3]);
+float SCULPT_vertex_mask_get(struct SculptSession *ss, SculptVertRef index);
+const float *SCULPT_vertex_color_get(SculptSession *ss, SculptVertRef index);
+
+const float *SCULPT_vertex_persistent_co_get(SculptSession *ss,
+ SculptVertRef index,
+ int cd_pers_co);
+void SCULPT_vertex_persistent_normal_get(SculptSession *ss,
+ SculptVertRef index,
+ float no[3],
+ int cd_pers_no);
/* Coordinates used for manipulating the base mesh when Grab Active Vertex is enabled. */
-const float *SCULPT_vertex_co_for_grab_active_get(SculptSession *ss, int index);
+const float *SCULPT_vertex_co_for_grab_active_get(SculptSession *ss, SculptVertRef index);
/* Returns the info of the limit surface when Multires is available, otherwise it returns the
* current coordinate of the vertex. */
-void SCULPT_vertex_limit_surface_get(SculptSession *ss, int index, float r_co[3]);
+void SCULPT_vertex_limit_surface_get(SculptSession *ss, SculptVertRef index, float r_co[3]);
/* Returns the pointer to the coordinates that should be edited from a brush tool iterator
* depending on the given deformation target. */
@@ -123,25 +154,36 @@ float *SCULPT_brush_deform_target_vertex_co_get(SculptSession *ss,
const int deform_target,
PBVHVertexIter *iter);
+struct _SculptNeighborRef {
+ SculptVertRef vertex;
+ SculptEdgeRef edge;
+};
+
#define SCULPT_VERTEX_NEIGHBOR_FIXED_CAPACITY 256
typedef struct SculptVertexNeighborIter {
/* Storage */
- int *neighbors;
+ struct _SculptNeighborRef *neighbors;
+ int *neighbor_indices;
+
int size;
int capacity;
- int 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. */
int num_duplicates;
int i;
/* Public */
+ SculptVertRef vertex;
+ SculptEdgeRef edge;
int index;
+ bool has_edge; // does this iteration step have an edge, fake neighbors do not
bool is_duplicate;
} SculptVertexNeighborIter;
-void SCULPT_vertex_neighbors_get(struct SculptSession *ss,
- const int index,
+void SCULPT_vertex_neighbors_get(const struct SculptSession *ss,
+ const SculptVertRef vref,
const bool include_duplicates,
SculptVertexNeighborIter *iter);
@@ -150,7 +192,11 @@ void SCULPT_vertex_neighbors_get(struct SculptSession *ss,
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.index = neighbor_iterator.neighbors[neighbor_iterator.i];
+ neighbor_iterator.has_edge = neighbor_iterator.neighbors[neighbor_iterator.i].edge.i != \
+ SCULPT_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
* first since they are nearest for floodfill. */
@@ -158,7 +204,11 @@ void SCULPT_vertex_neighbors_get(struct SculptSession *ss,
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.index = neighbor_iterator.neighbors[neighbor_iterator.i]; \
+ neighbor_iterator.has_edge = neighbor_iterator.neighbors[neighbor_iterator.i].edge.i != \
+ SCULPT_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);
@@ -169,9 +219,11 @@ void SCULPT_vertex_neighbors_get(struct SculptSession *ss,
} \
((void)0)
-int SCULPT_active_vertex_get(SculptSession *ss);
+SculptVertRef SCULPT_active_vertex_get(SculptSession *ss);
const float *SCULPT_active_vertex_co_get(SculptSession *ss);
+float *SCULPT_vertex_origco_get(SculptSession *ss, SculptVertRef vertex);
void SCULPT_active_vertex_normal_get(SculptSession *ss, float normal[3]);
+MDynTopoVert *SCULPT_vertex_get_mdyntopo(SculptSession *ss, SculptVertRef vertex);
/* Returns PBVH deformed vertices array if shape keys or deform modifiers are used, otherwise
* returns mesh original vertices array. */
@@ -189,15 +241,39 @@ void SCULPT_fake_neighbors_free(struct Object *ob);
/* Vertex Info. */
void SCULPT_boundary_info_ensure(Object *object);
void SCULPT_connected_components_ensure(Object *ob);
+
+/* this is a bitmask */
+typedef enum SculptCornerType {
+ SCULPT_CORNER_NONE = 0,
+ SCULPT_CORNER_MESH = 1 << 0,
+ SCULPT_CORNER_FACE_SET = 1 << 1,
+ SCULPT_CORNER_SEAM = 1 << 2,
+ SCULPT_CORNER_SHARP = 1 << 3
+} SculptCornerType;
+
+SculptCornerType SCULPT_vertex_is_corner(const SculptSession *ss,
+ const SculptVertRef index,
+ SculptCornerType cornertype);
+
+typedef enum SculptBoundaryType {
+ SCULPT_BOUNDARY_MESH = 1 << 0,
+ SCULPT_BOUNDARY_FACE_SET = 1 << 1,
+ SCULPT_BOUNDARY_SEAM = 1 << 2,
+ SCULPT_BOUNDARY_SHARP = 1 << 3,
+ SCULPT_BOUNDARY_ALL = (1 << 0) | (1 << 1) | (1 << 2) | (1 << 3)
+} SculptBoundaryType;
+
/* Boundary Info needs to be initialized in order to use this function. */
-bool SCULPT_vertex_is_boundary(const SculptSession *ss, const int index);
+SculptBoundaryType SCULPT_vertex_is_boundary(const SculptSession *ss,
+ const SculptVertRef index,
+ SculptBoundaryType boundary_types);
void SCULPT_connected_components_ensure(Object *ob);
/* Sculpt Visibility API */
-void SCULPT_vertex_visible_set(SculptSession *ss, int index, bool visible);
-bool SCULPT_vertex_visible_get(SculptSession *ss, int index);
+void SCULPT_vertex_visible_set(SculptSession *ss, SculptVertRef index, bool visible);
+bool SCULPT_vertex_visible_get(SculptSession *ss, SculptVertRef index);
void SCULPT_visibility_sync_all_face_sets_to_vertices(struct Object *ob);
void SCULPT_visibility_sync_all_vertex_to_face_sets(struct SculptSession *ss);
@@ -205,22 +281,29 @@ void SCULPT_visibility_sync_all_vertex_to_face_sets(struct SculptSession *ss);
/* Face Sets API */
int SCULPT_active_face_set_get(SculptSession *ss);
-int SCULPT_vertex_face_set_get(SculptSession *ss, int index);
-void SCULPT_vertex_face_set_set(SculptSession *ss, int index, int face_set);
-void SCULPT_vertex_face_set_increase(SculptSession *ss, int index, const int increase);
+int SCULPT_vertex_face_set_get(SculptSession *ss, SculptVertRef vertex);
+void SCULPT_vertex_face_set_set(SculptSession *ss, SculptVertRef vertex, int face_set);
+void SCULPT_vertex_face_set_increase(SculptSession *ss, SculptVertRef vertex, const int increase);
-bool SCULPT_vertex_has_face_set(SculptSession *ss, int index, int face_set);
-bool SCULPT_vertex_has_unique_face_set(SculptSession *ss, int index);
+bool SCULPT_vertex_has_face_set(SculptSession *ss, SculptVertRef index, int face_set);
+bool SCULPT_vertex_has_unique_face_set(const SculptSession *ss, SculptVertRef index);
int SCULPT_face_set_next_available_get(SculptSession *ss);
void SCULPT_face_set_visibility_set(SculptSession *ss, int face_set, bool visible);
-bool SCULPT_vertex_all_face_sets_visible_get(const SculptSession *ss, int index);
-bool SCULPT_vertex_any_face_set_visible_get(SculptSession *ss, int index);
+bool SCULPT_vertex_all_face_sets_visible_get(const SculptSession *ss, SculptVertRef index);
+bool SCULPT_vertex_any_face_set_visible_get(SculptSession *ss, SculptVertRef index);
void SCULPT_face_sets_visibility_invert(SculptSession *ss);
void SCULPT_face_sets_visibility_all_set(SculptSession *ss, bool visible);
+int SCULPT_face_set_get(SculptSession *ss, SculptFaceRef face);
+
+// returns previous face set
+int SCULPT_face_set_set(SculptSession *ss, SculptFaceRef face, int fset);
+int SCULPT_face_set_flag_get(SculptSession *ss, SculptFaceRef face, char flag);
+int SCULPT_face_set_flag_set(SculptSession *ss, SculptFaceRef face, char flag, bool state);
+
bool SCULPT_stroke_is_main_symmetry_pass(struct StrokeCache *cache);
bool SCULPT_stroke_is_first_brush_step(struct StrokeCache *cache);
bool SCULPT_stroke_is_first_brush_step_of_symmetry_pass(struct StrokeCache *cache);
@@ -230,24 +313,22 @@ typedef struct {
struct BMLog *bm_log;
struct SculptUndoNode *unode;
+ int datatype;
float (*coords)[3];
short (*normals)[3];
const float *vmasks;
float (*colors)[4];
+ short _no[3];
/* Original coordinate, normal, and mask. */
const float *co;
const short *no;
float mask;
const float *col;
+ struct PBVH *pbvh;
+ struct SculptSession *ss;
} SculptOrigVertData;
-void SCULPT_orig_vert_data_init(SculptOrigVertData *data, Object *ob, PBVHNode *node);
-void SCULPT_orig_vert_data_update(SculptOrigVertData *orig_data, PBVHVertexIter *iter);
-void SCULPT_orig_vert_data_unode_init(SculptOrigVertData *data,
- Object *ob,
- struct SculptUndoNode *unode);
-
/* Utils. */
void SCULPT_calc_brush_plane(struct Sculpt *sd,
struct Object *ob,
@@ -259,11 +340,11 @@ void SCULPT_calc_brush_plane(struct Sculpt *sd,
void SCULPT_calc_area_normal(
Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode, float r_area_no[3]);
-int SCULPT_nearest_vertex_get(struct Sculpt *sd,
- struct Object *ob,
- const float co[3],
- float max_distance,
- bool use_original);
+SculptVertRef SCULPT_nearest_vertex_get(struct Sculpt *sd,
+ struct Object *ob,
+ const float co[3],
+ float max_distance,
+ bool use_original);
int SCULPT_plane_point_side(const float co[3], const float plane[4]);
int SCULPT_plane_trim(const struct StrokeCache *cache,
@@ -305,26 +386,36 @@ void SCULPT_floodfill_add_initial_with_symmetry(struct Sculpt *sd,
struct Object *ob,
struct SculptSession *ss,
SculptFloodFill *flood,
- int index,
+ SculptVertRef index,
float radius);
-void SCULPT_floodfill_add_initial(SculptFloodFill *flood, int index);
-void SCULPT_floodfill_add_and_skip_initial(SculptFloodFill *flood, int index);
-void SCULPT_floodfill_execute(
- struct SculptSession *ss,
- SculptFloodFill *flood,
- bool (*func)(SculptSession *ss, int from_v, int to_v, bool is_duplicate, void *userdata),
- void *userdata);
+
+void SCULPT_floodfill_add_initial(SculptFloodFill *flood, SculptVertRef index);
+void SCULPT_floodfill_add_and_skip_initial(struct SculptSession *ss,
+ SculptFloodFill *flood,
+ SculptVertRef vertex);
+void SCULPT_floodfill_execute(struct SculptSession *ss,
+ SculptFloodFill *flood,
+ bool (*func)(SculptSession *ss,
+ SculptVertRef from_v,
+ SculptVertRef to_v,
+ bool is_duplicate,
+ void *userdata),
+ void *userdata);
void SCULPT_floodfill_free(SculptFloodFill *flood);
/* Dynamic topology */
enum eDynTopoWarnFlag {
- DYNTOPO_WARN_VDATA = (1 << 0),
DYNTOPO_WARN_EDATA = (1 << 1),
- DYNTOPO_WARN_LDATA = (1 << 2),
DYNTOPO_WARN_MODIFIER = (1 << 3),
+ DYNTOPO_ERROR_MULTIRES = (1 << 4)
};
+struct Mesh;
+
+void SCULPT_dyntopo_node_layers_update_offsets(SculptSession *ss);
+void SCULPT_dynamic_topology_sync_layers(Object *ob, struct Mesh *me);
+
void SCULPT_dynamic_topology_enable_ex(struct Main *bmain,
struct Depsgraph *depsgraph,
Scene *scene,
@@ -337,8 +428,9 @@ void sculpt_dynamic_topology_disable_with_undo(struct Main *bmain,
bool SCULPT_stroke_is_dynamic_topology(const SculptSession *ss, const Brush *brush);
-void SCULPT_dynamic_topology_triangulate(struct BMesh *bm);
+void SCULPT_dynamic_topology_triangulate(struct SculptSession *ss, struct BMesh *bm);
void SCULPT_dyntopo_node_layers_add(struct SculptSession *ss);
+void SCULPT_dyntopo_save_origverts(struct SculptSession *ss);
enum eDynTopoWarnFlag SCULPT_dynamic_topology_check(Scene *scene, Object *ob);
@@ -347,43 +439,46 @@ void SCULPT_pbvh_clear(Object *ob);
/* Auto-masking. */
float SCULPT_automasking_factor_get(struct AutomaskingCache *automasking,
SculptSession *ss,
- int vert);
+ SculptVertRef vert);
/* 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);
-struct AutomaskingCache *SCULPT_automasking_cache_init(Sculpt *sd, Brush *brush, Object *ob);
+struct AutomaskingCache *SCULPT_automasking_cache_init(Sculpt *sd, const Brush *brush, Object *ob);
void SCULPT_automasking_cache_free(struct AutomaskingCache *automasking);
-bool SCULPT_is_automasking_mode_enabled(const Sculpt *sd,
- const Brush *br,
- const eAutomasking_flag mode);
-bool SCULPT_is_automasking_enabled(const Sculpt *sd, const SculptSession *ss, const Brush *br);
+bool SCULPT_is_automasking_mode_enabled(Sculpt *sd, const Brush *br, const eAutomasking_flag mode);
+bool SCULPT_is_automasking_enabled(Sculpt *sd, const SculptSession *ss, const Brush *br);
typedef enum eBoundaryAutomaskMode {
AUTOMASK_INIT_BOUNDARY_EDGES = 1,
AUTOMASK_INIT_BOUNDARY_FACE_SETS = 2,
} eBoundaryAutomaskMode;
-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,
+ SculptCustomLayer *factorlayer);
/* Geodesic distances. */
/* 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. */
+Geodesic distances will only work when used with PBVH_FACES or PBVH_BMESH, 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(struct Object *ob,
struct GSet *initial_vertices,
- const float limit_radius);
+ const float limit_radius,
+ SculptVertRef *r_closest_verts,
+ float (*vertco_override)[3]);
float *SCULPT_geodesic_from_vertex_and_symm(struct Sculpt *sd,
struct Object *ob,
- const int vertex,
+ const SculptVertRef vertex,
const float limit_radius);
-float *SCULPT_geodesic_from_vertex(Object *ob, const int vertex, const float limit_radius);
+float *SCULPT_geodesic_from_vertex(Object *ob,
+ const SculptVertRef vertex,
+ const float limit_radius);
/* Filters. */
void SCULPT_filter_cache_init(struct bContext *C, Object *ob, Sculpt *sd, const int undo_type);
@@ -526,7 +621,7 @@ void SCULPT_pose_ik_chain_free(struct SculptPoseIKChain *ik_chain);
/* Boundary Brush. */
struct SculptBoundary *SCULPT_boundary_data_init(Object *object,
Brush *brush,
- const int initial_vertex,
+ const SculptVertRef initial_vertex,
const float radius);
void SCULPT_boundary_data_free(struct SculptBoundary *boundary);
void SCULPT_do_boundary_brush(struct Sculpt *sd,
@@ -543,9 +638,7 @@ void SCULPT_boundary_pivot_line_preview_draw(const uint gpuattr, struct SculptSe
/* Array Brush. */
void SCULPT_do_array_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode);
void SCULPT_array_datalayers_free(Object *ob);
-void SCULPT_array_path_draw(const uint gpuattr,
- Brush *brush,
- SculptSession *ss);
+void SCULPT_array_path_draw(const uint gpuattr, Brush *brush, SculptSession *ss);
/* Multi-plane Scrape Brush. */
void SCULPT_do_multiplane_scrape_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode);
@@ -563,37 +656,62 @@ void SCULPT_do_paint_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode
/* Smear Brush. */
void SCULPT_do_smear_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode);
-/* Smooth Brush. */
-void SCULPT_bmesh_four_neighbor_average(float avg[3], float direction[3], struct BMVert *v);
-
-void SCULPT_neighbor_coords_average(SculptSession *ss, float result[3], int index);
-float SCULPT_neighbor_mask_average(SculptSession *ss, int index);
-void SCULPT_neighbor_color_average(SculptSession *ss, float result[4], int index);
+/* Topology rake */
+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_dyn_vert,
+ bool do_origco);
+
+/* Smoothing api */
+void SCULPT_neighbor_coords_average(
+ SculptSession *ss, float result[3], SculptVertRef index, float projection, bool check_fsets);
+float SCULPT_neighbor_mask_average(SculptSession *ss, SculptVertRef index);
+void SCULPT_neighbor_color_average(SculptSession *ss, float result[4], SculptVertRef index);
/* Mask the mesh boundaries smoothing only the mesh surface without using automasking. */
-void SCULPT_neighbor_coords_average_interior(SculptSession *ss, float result[3], int index);
+void SCULPT_neighbor_coords_average_interior(SculptSession *ss,
+ float result[3],
+ SculptVertRef index,
+ float projection,
+ SculptCustomLayer *bound_scl,
+ bool do_origco);
+
+void SCULPT_smooth_vcol_boundary(
+ Sculpt *sd, Object *ob, PBVHNode **nodes, const int totnode, float bstrength);
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);
+ const bool smooth_mask,
+ float projection,
+ bool do_origco);
+
+void SCULPT_do_smooth_brush(
+ Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode, float projection);
/* Surface Smooth Brush. */
void SCULPT_surface_smooth_laplacian_step(SculptSession *ss,
float *disp,
const float co[3],
- float (*laplacian_disp)[3],
- const int v_index,
+ struct SculptCustomLayer *scl,
+ const SculptVertRef v_index,
const float origco[3],
- const float alpha);
+ const float alpha,
+ const float projection,
+ bool check_fsets);
+
void SCULPT_surface_smooth_displace_step(SculptSession *ss,
float *co,
- float (*laplacian_disp)[3],
- const int v_index,
+ struct SculptCustomLayer *scl,
+ const SculptVertRef v_index,
const float beta,
const float fade);
void SCULPT_do_surface_smooth_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode);
@@ -661,8 +779,8 @@ typedef struct SculptUndoNode {
int totvert;
/* non-multires */
- int maxvert; /* to verify if totvert it still the same */
- int *index; /* to restore into right location */
+ int maxvert; /* to verify if totvert it still the same */
+ SculptVertRef *index; /* to restore into right location */
BLI_bitmap *vert_hidden;
/* multires */
@@ -699,7 +817,11 @@ typedef struct SculptUndoNode {
/* Sculpt Face Sets */
int *face_sets;
+ bool *nodemap;
+ int nodemap_size;
+
size_t undo_size;
+ // int gen, lasthash;
} SculptUndoNode;
/* Factor of brush to have rake point following behind
@@ -824,10 +946,10 @@ typedef struct SculptThreadedTaskData {
bool mask_by_color_preserve_mask;
/* Index of the vertex that is going to be used as a reference for the colors. */
- int mask_by_color_vertex;
+ SculptVertRef 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;
@@ -835,6 +957,14 @@ typedef struct SculptThreadedTaskData {
ThreadMutex mutex;
+ // Layer brush
+ int cd_pers_co, cd_pers_no, cd_pers_disp;
+ int cd_layer_disp, cd_temp, cd_dyn_vert;
+
+ float smooth_projection;
+ float rake_projection;
+ SculptCustomLayer *scl, *scl2;
+ bool do_origco;
} SculptThreadedTaskData;
/*************** Brush testing declarations ****************/
@@ -868,6 +998,8 @@ typedef struct {
bool original;
/* This ignores fully masked and fully hidden nodes. */
bool ignore_fully_ineffective;
+ struct Object *ob;
+ struct Brush *brush;
} SculptSearchSphereData;
typedef struct {
@@ -906,7 +1038,7 @@ float SCULPT_brush_strength_factor(struct SculptSession *ss,
const short vno[3],
const float fno[3],
const float mask,
- const int vertex_index,
+ const SculptVertRef vertex_index,
const int thread_id);
/* Tilts a normal by the x and y tilt values using the view axis. */
@@ -937,13 +1069,16 @@ typedef struct AutomaskingSettings {
/* Flags from eAutomasking_flag. */
int flags;
int initial_face_set;
+ int current_face_set; // used by faceset draw tool
+ float concave_factor;
} AutomaskingSettings;
typedef struct AutomaskingCache {
AutomaskingSettings settings;
/* Precomputed auto-mask factor indexed by vertex, owned by the auto-masking system and
* initialized in #SCULPT_automasking_cache_init when needed. */
- float *factor;
+ // float *factor;
+ SculptCustomLayer *factorlayer;
} AutomaskingCache;
typedef struct StrokeCache {
@@ -990,7 +1125,7 @@ typedef struct StrokeCache {
/* Multires Displacement Smear. */
float (*prev_displacement)[3];
float (*limit_surface_co)[3];
-
+
/* The rest is temporary storage that isn't saved as a property */
bool first_time; /* Beginning of stroke may do some things special */
@@ -1020,6 +1155,7 @@ typedef struct StrokeCache {
/* 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;
+ int boundary_symmetry; // controls splitting face sets by mirror axis
int mirror_symmetry_pass; /* The symmetry pass we are currently on between 0 and 7. */
float true_view_normal[3];
float view_normal[3];
@@ -1048,9 +1184,9 @@ typedef struct StrokeCache {
float anchored_location[3];
/* Fairing. */
- bool *fairing_mask;
- float *fairing_fade;
- float (*prefairing_co)[3];
+ SculptCustomLayer *fairing_mask;
+ SculptCustomLayer *fairing_fade;
+ SculptCustomLayer *prefairing_co;
/* Paint Brush. */
struct {
@@ -1121,6 +1257,18 @@ typedef 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 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;
} StrokeCache;
/* Sculpt Filters */
@@ -1197,7 +1345,7 @@ typedef struct ExpandCache {
* during the execution of Expand by moving the origin. */
float initial_mouse_move[2];
float initial_mouse[2];
- int initial_active_vertex;
+ SculptVertRef initial_active_vertex;
int initial_active_face_set;
/* Maximum number of vertices allowed in the SculptSession for previewing the falloff using
@@ -1333,7 +1481,7 @@ typedef struct SculptGradientContext {
} SculptGradientContext;
/* IPMask filter vertex callback function. */
-typedef float(SculptIPMaskFilterStepVertexCB)(struct SculptSession *, int, float *);
+typedef float(SculptIPMaskFilterStepVertexCB)(struct SculptSession *, SculptVertRef, float *);
typedef struct MaskFilterDeltaStep {
int totelem;
@@ -1421,10 +1569,21 @@ void SCULPT_cache_calc_brushdata_symm(StrokeCache *cache,
const char symm,
const char axis,
const float angle);
-void SCULPT_cache_free(StrokeCache *cache);
+void SCULPT_cache_free(SculptSession *ss, StrokeCache *cache);
+
+void SCULPT_vertex_check_origdata(SculptSession *ss, SculptVertRef vertex);
+
+void SCULPT_orig_vert_data_init(SculptOrigVertData *data,
+ Object *ob,
+ PBVHNode *node,
+ SculptUndoType type);
+void SCULPT_orig_vert_data_update(SculptOrigVertData *orig_data, SculptVertRef vertex);
+void SCULPT_orig_vert_data_unode_init(SculptOrigVertData *data,
+ Object *ob,
+ struct SculptUndoNode *unode);
SculptUndoNode *SCULPT_undo_push_node(Object *ob, PBVHNode *node, SculptUndoType type);
-SculptUndoNode *SCULPT_undo_get_node(PBVHNode *node);
+SculptUndoNode *SCULPT_undo_get_node(PBVHNode *node, SculptUndoType type);
SculptUndoNode *SCULPT_undo_get_first_node(void);
void SCULPT_undo_push_begin(struct Object *ob, const char *name);
void SCULPT_undo_push_end(void);
@@ -1440,7 +1599,7 @@ bool SCULPT_get_redraw_rect(struct ARegion *region,
rcti *rect);
/* Poly Loops. */
-int sculpt_poly_loop_initial_edge_from_cursor(Object *ob);
+SculptEdgeRef sculpt_poly_loop_initial_edge_from_cursor(Object *ob);
BLI_bitmap *sculpt_poly_loop_from_cursor(struct Object *ob);
/* Operators. */
@@ -1502,3 +1661,196 @@ void SCULPT_OT_dyntopo_detail_size_edit(struct wmOperatorType *ot);
/* Dyntopo. */
void SCULPT_OT_dynamic_topology_toggle(struct wmOperatorType *ot);
+bool SCULPT_ensure_dyntopo_node_undo(struct Object *ob,
+ struct PBVHNode *node,
+ SculptUndoType type,
+ int extraType);
+
+float SCULPT_calc_concavity(SculptSession *ss, SculptVertRef vref);
+
+typedef struct SculptCurvatureData {
+ float ks[3];
+ float principle[3][3]; // normalized
+} SculptCurvatureData;
+
+/*
+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,
+ SculptVertRef vertex,
+ SculptCurvatureData *out,
+ bool useAccurateSolver);
+
+void SCULPT_curvature_begin(SculptSession *ss, struct PBVHNode *node, bool useAccurateSolver);
+void SCULPT_curvature_dir_get(SculptSession *ss,
+ SculptVertRef v,
+ float dir[3],
+ bool useAccurateSolver);
+
+/*
+
+DEPRECATED in favor of SCULPT_temp_customlayer_ensure
+which works with all three PBVH types
+
+Ensure a named temporary layer exists, creating it if necassary.
+The layer will be marked with CD_FLAG_TEMPORARY.
+*/
+void SCULPT_dyntopo_ensure_templayer(SculptSession *ss, int type, const char *name);
+
+bool SCULPT_dyntopo_has_templayer(SculptSession *ss, int type, const char *name);
+
+/* Get a named temporary vertex customdata layer offset, if it exists. If not
+ -1 is returned.*/
+int SCULPT_dyntopo_get_templayer(SculptSession *ss, int type, const char *name);
+
+void SCULPT_dyntopo_save_persistent_base(SculptSession *ss);
+
+#define SCULPT_LAYER_PERS_CO "__dyntopo_layer_pers_co"
+#define SCULPT_LAYER_PERS_NO "__dyntopo_layer_pers_no"
+#define SCULPT_LAYER_PERS_DISP "__dyntopo_layer_pers_disp"
+#define SCULPT_LAYER_DISP "__dyntopo_layer_disp"
+
+// these tools don't support dynamic pbvh splitting during the stroke
+#define DYNTOPO_HAS_DYNAMIC_SPLIT(tool) (ELEM(tool, SCULPT_TOOL_LAYER) == 0)
+
+/*get current symmetry pass index inclusive of both
+ mirror and radial symmetry*/
+int SCULPT_get_symmetry_pass(const SculptSession *ss);
+
+void SCULPT_on_sculptsession_bmesh_free(SculptSession *ss);
+void SCULPT_reorder_bmesh(SculptSession *ss);
+
+// TODO: support faces
+static inline void *SCULPT_temp_cdata_get(SculptVertRef vertex, SculptCustomLayer *scl)
+{
+ if (scl->data) {
+ char *p = (char *)scl->data;
+ int idx = (int)vertex.i;
+
+ if (scl->from_bmesh) {
+ BMVert *v = (BMVert *)vertex.i;
+ idx = v->head.index;
+ }
+
+ return p + scl->elemsize * (int)vertex.i;
+ }
+ else {
+ BMVert *v = (BMVert *)vertex.i;
+ return BM_ELEM_CD_GET_VOID_P(v, scl->cd_offset);
+ }
+
+ return NULL;
+}
+
+/*
+create a custom vertex or face attribute.
+always create all of your attributes together with SCULPT_temp_customlayer_ensure,
+
+then initialize their SculptCustomLayer's with SCULPT_temp_customlayer_get
+afterwards. Otherwise customdata offsets will be wrong (for PBVH_BMESH).
+
+return true on success. if false, layer was not created.
+
+Access per element data with SCULPT_temp_cdata_get.
+*/
+bool SCULPT_temp_customlayer_ensure(
+ SculptSession *ss, AttributeDomain domain, int proptype, char *name, bool simple_array);
+bool SCULPT_temp_customlayer_get(SculptSession *ss,
+ AttributeDomain domain,
+ int proptype,
+ char *name,
+ SculptCustomLayer *scl,
+ bool use_simple_array);
+bool SCULPT_temp_customlayer_release(SculptSession *ss,
+ AttributeDomain domain,
+ int proptype,
+ SculptCustomLayer *scl);
+
+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);
+void SCULPT_uv_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode);
+
+// returns true if edge disk list around vertex was sorted
+// be careful of this function.
+bool SCULPT_dyntopo_check_disk_sort(SculptSession *ss, SculptVertRef 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,
+ SculptVertRef 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,
+ SculptVertRef 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);
+char SCULPT_mesh_fset_boundary_symmetry_get(struct Object *object);
+
+// exponent to make boundary_smooth_factor more user-friendly
+#define BOUNDARY_SMOOTH_EXP 2.0
+
+// edges
+
+SculptBoundaryType SCULPT_edge_is_boundary(const SculptSession *ss,
+ const SculptEdgeRef edge,
+ SculptBoundaryType typemask);
+void SCULPT_edge_get_verts(const SculptSession *ss,
+ const SculptEdgeRef edge,
+ SculptVertRef *r_v1,
+ SculptVertRef *r_v2);
+SculptVertRef SCULPT_edge_other_vertex(const SculptSession *ss,
+ const SculptEdgeRef edge,
+ const SculptVertRef 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
+
+struct BMesh *SCULPT_dyntopo_empty_bmesh();
+
+#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)
+
+void SCULPT_undo_ensure_bmlog(struct Object *ob);
diff --git a/source/blender/editors/sculpt_paint/sculpt_mask_expand.c b/source/blender/editors/sculpt_paint/sculpt_mask_expand.c
index 7f91c5dbc6e..a890a488701 100644
--- a/source/blender/editors/sculpt_paint/sculpt_mask_expand.c
+++ b/source/blender/editors/sculpt_paint/sculpt_mask_expand.c
@@ -117,7 +117,7 @@ static void sculpt_expand_task_cb(void *__restrict userdata,
int vi = vd.index;
float final_mask = *vd.mask;
if (data->mask_expand_use_normals) {
- if (ss->filter_cache->normal_factor[SCULPT_active_vertex_get(ss)] <
+ if (ss->filter_cache->normal_factor[BKE_pbvh_vertex_index_to_table(ss->pbvh, SCULPT_active_vertex_get(ss))] <
ss->filter_cache->normal_factor[vd.index]) {
final_mask = 1.0f;
}
@@ -137,7 +137,7 @@ static void sculpt_expand_task_cb(void *__restrict userdata,
if (data->mask_expand_create_face_set) {
if (final_mask == 1.0f) {
- SCULPT_vertex_face_set_set(ss, vd.index, ss->filter_cache->new_face_set);
+ SCULPT_vertex_face_set_set(ss, vd.vertex, ss->filter_cache->new_face_set);
}
BKE_pbvh_node_mark_redraw(node);
}
@@ -188,7 +188,8 @@ static int sculpt_mask_expand_modal(bContext *C, wmOperator *op, const wmEvent *
mouse[1] = event->mval[1];
if (SCULPT_cursor_geometry_info_update(C, &sgi, mouse, false, false)) {
/* The cursor is over the mesh, get the update iteration from the updated active vertex. */
- mask_expand_update_it = ss->filter_cache->mask_update_it[(int)SCULPT_active_vertex_get(ss)];
+ int vi = BKE_pbvh_vertex_index_to_table(ss->pbvh, SCULPT_active_vertex_get(ss));
+ mask_expand_update_it = ss->filter_cache->mask_update_it[vi];
}
else {
/* When the cursor is outside the mesh, affect the entire connected component. */
@@ -309,10 +310,13 @@ typedef struct MaskExpandFloodFillData {
} MaskExpandFloodFillData;
static bool mask_expand_floodfill_cb(
- SculptSession *ss, int from_v, int to_v, bool is_duplicate, void *userdata)
+ SculptSession *ss, SculptVertRef from_vref, SculptVertRef to_vref, bool is_duplicate, void *userdata)
{
MaskExpandFloodFillData *data = userdata;
+ int to_v = BKE_pbvh_vertex_index_to_table(ss->pbvh, to_vref);
+ int from_v = BKE_pbvh_vertex_index_to_table(ss->pbvh, from_vref);
+
if (!is_duplicate) {
int to_it = ss->filter_cache->mask_update_it[from_v] + 1;
ss->filter_cache->mask_update_it[to_v] = to_it;
@@ -322,8 +326,8 @@ static bool mask_expand_floodfill_cb(
if (data->use_normals) {
float current_normal[3], prev_normal[3];
- SCULPT_vertex_normal_get(ss, to_v, current_normal);
- SCULPT_vertex_normal_get(ss, from_v, prev_normal);
+ SCULPT_vertex_normal_get(ss, to_vref, current_normal);
+ SCULPT_vertex_normal_get(ss, from_vref, prev_normal);
const float from_edge_factor = ss->filter_cache->edge_factor[from_v];
ss->filter_cache->edge_factor[to_v] = dot_v3v3(current_normal, prev_normal) *
from_edge_factor;
@@ -412,13 +416,15 @@ static int sculpt_mask_expand_invoke(bContext *C, wmOperator *op, const wmEvent
else {
ss->filter_cache->prev_mask = MEM_callocN(sizeof(float) * vertex_count, "prev mask");
for (int i = 0; i < vertex_count; i++) {
- ss->filter_cache->prev_mask[i] = SCULPT_vertex_mask_get(ss, i);
+ SculptVertRef vertex = BKE_pbvh_table_index_to_vertex(ss->pbvh, i);
+
+ ss->filter_cache->prev_mask[i] = SCULPT_vertex_mask_get(ss, vertex);
}
}
ss->filter_cache->mask_update_last_it = 1;
ss->filter_cache->mask_update_current_it = 1;
- ss->filter_cache->mask_update_it[SCULPT_active_vertex_get(ss)] = 0;
+ ss->filter_cache->mask_update_it[BKE_pbvh_vertex_index_to_table(ss->pbvh, SCULPT_active_vertex_get(ss))] = 0;
copy_v3_v3(ss->filter_cache->mask_expand_initial_co, SCULPT_active_vertex_co_get(ss));
@@ -437,9 +443,11 @@ static int sculpt_mask_expand_invoke(bContext *C, wmOperator *op, const wmEvent
if (use_normals) {
for (int repeat = 0; repeat < 2; repeat++) {
for (int i = 0; i < vertex_count; i++) {
+ SculptVertRef vertex = BKE_pbvh_table_index_to_vertex(ss->pbvh, i);
+
float avg = 0.0f;
SculptVertexNeighborIter ni;
- SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, i, ni) {
+ SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vertex, ni) {
avg += ss->filter_cache->normal_factor[ni.index];
}
SCULPT_VERTEX_NEIGHBORS_ITER_END(ni);
diff --git a/source/blender/editors/sculpt_paint/sculpt_mask_init.c b/source/blender/editors/sculpt_paint/sculpt_mask_init.c
index 0c383cdf035..349f781c7a1 100644
--- a/source/blender/editors/sculpt_paint/sculpt_mask_init.c
+++ b/source/blender/editors/sculpt_paint/sculpt_mask_init.c
@@ -115,7 +115,7 @@ static void mask_init_task_cb(void *__restrict userdata,
*vd.mask = BLI_hash_int_01(vd.index + seed);
break;
case SCULPT_MASK_INIT_RANDOM_PER_FACE_SET: {
- const int face_set = SCULPT_vertex_face_set_get(ss, vd.index);
+ const int face_set = SCULPT_vertex_face_set_get(ss, vd.vertex);
*vd.mask = BLI_hash_int_01(face_set + seed);
break;
}
diff --git a/source/blender/editors/sculpt_paint/sculpt_multiplane_scrape.c b/source/blender/editors/sculpt_paint/sculpt_multiplane_scrape.c
index f78f30a2cfd..a7f42349777 100644
--- a/source/blender/editors/sculpt_paint/sculpt_multiplane_scrape.c
+++ b/source/blender/editors/sculpt_paint/sculpt_multiplane_scrape.c
@@ -107,7 +107,7 @@ static void calc_multiplane_scrape_surface_task_cb(void *__restrict userdata,
vd.no,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
- vd.index,
+ vd.vertex,
thread_id);
/* Sample the normal and area of the +X and -X axis individually. */
@@ -166,7 +166,6 @@ static void do_multiplane_scrape_brush_task_cb_ex(void *__restrict userdata,
const int thread_id = BLI_task_parallel_thread_id(tls);
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
-
if (!sculpt_brush_test_sq_fn(&test, vd.co)) {
continue;
}
@@ -215,7 +214,7 @@ static void do_multiplane_scrape_brush_task_cb_ex(void *__restrict userdata,
vd.no,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
- vd.index,
+ vd.vertex,
thread_id);
mul_v3_v3fl(proxy[vd.i], val, fade);
diff --git a/source/blender/editors/sculpt_paint/sculpt_paint_color.c b/source/blender/editors/sculpt_paint/sculpt_paint_color.c
index 936ebb7e8f7..e86c5b9ecb9 100644
--- a/source/blender/editors/sculpt_paint/sculpt_paint_color.c
+++ b/source/blender/editors/sculpt_paint/sculpt_paint_color.c
@@ -96,11 +96,11 @@ static void do_color_smooth_task_cb_exec(void *__restrict userdata,
vd.no,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
- vd.index,
+ vd.vertex,
thread_id);
float smooth_color[4];
- SCULPT_neighbor_color_average(ss, smooth_color, vd.index);
+ SCULPT_neighbor_color_average(ss, smooth_color, vd.vertex);
blend_color_interpolate_float(vd.col, vd.col, smooth_color, fade);
if (vd.mvert) {
@@ -123,7 +123,8 @@ static void do_paint_brush_task_cb_ex(void *__restrict userdata,
PBVHColorBufferNode *color_buffer;
SculptOrigVertData orig_data;
- SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]);
+ SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COLOR);
+ orig_data.datatype = SCULPT_UNDO_COLOR;
color_buffer = BKE_pbvh_node_color_buffer_get(data->nodes[n]);
@@ -139,7 +140,7 @@ static void do_paint_brush_task_cb_ex(void *__restrict userdata,
IMB_colormanagement_srgb_to_scene_linear_v3(brush_color);
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);
bool affect_vertex = false;
float distance_to_stroke_location = 0.0f;
@@ -163,7 +164,7 @@ static void do_paint_brush_task_cb_ex(void *__restrict userdata,
vd.no,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
- vd.index,
+ vd.vertex,
thread_id);
/* Density. */
@@ -363,6 +364,10 @@ void SCULPT_do_paint_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_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,
@@ -392,7 +397,7 @@ static void do_smear_brush_task_cb_exec(void *__restrict userdata,
vd.no,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
- vd.index,
+ vd.vertex,
thread_id);
float current_disp[3];
@@ -415,10 +420,10 @@ static void do_smear_brush_task_cb_exec(void *__restrict userdata,
mul_v3_v3fl(current_disp, current_disp_norm, ss->cache->bstrength);
SculptVertexNeighborIter ni;
- SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.index, ni) {
+ SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.vertex, ni) {
float vertex_disp[3];
float vertex_disp_norm[3];
- sub_v3_v3v3(vertex_disp, SCULPT_vertex_co_get(ss, ni.index), vd.co);
+ sub_v3_v3v3(vertex_disp, SCULPT_vertex_co_get(ss, ni.vertex), vd.co);
const float *neighbor_color = ss->cache->prev_colors[ni.index];
normalize_v3_v3(vertex_disp_norm, vertex_disp);
if (dot_v3v3(current_disp_norm, vertex_disp_norm) >= 0.0f) {
@@ -451,7 +456,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) {
- copy_v4_v4(ss->cache->prev_colors[vd.index], SCULPT_vertex_color_get(ss, vd.index));
+ copy_v4_v4(ss->cache->prev_colors[vd.index], SCULPT_vertex_color_get(ss, vd.vertex));
}
BKE_pbvh_vertex_iter_end;
}
@@ -465,13 +470,17 @@ void SCULPT_do_smear_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode
return;
}
+ SCULPT_vertex_random_access_ensure(ss);
+
const int totvert = SCULPT_vertex_count_get(ss);
if (SCULPT_stroke_is_first_brush_step(ss->cache)) {
if (!ss->cache->prev_colors) {
ss->cache->prev_colors = MEM_callocN(sizeof(float[4]) * totvert, "prev colors");
for (int i = 0; i < totvert; i++) {
- copy_v4_v4(ss->cache->prev_colors[i], SCULPT_vertex_color_get(ss, i));
+ SculptVertRef vertex = BKE_pbvh_table_index_to_vertex(ss->pbvh, i);
+
+ copy_v4_v4(ss->cache->prev_colors[i], SCULPT_vertex_color_get(ss, vertex));
}
}
}
diff --git a/source/blender/editors/sculpt_paint/sculpt_poly_loop.c b/source/blender/editors/sculpt_paint/sculpt_poly_loop.c
index 461834eff0e..8628a1facdc 100644
--- a/source/blender/editors/sculpt_paint/sculpt_poly_loop.c
+++ b/source/blender/editors/sculpt_paint/sculpt_poly_loop.c
@@ -89,7 +89,7 @@ static void sculpt_poly_loop_topology_data_ensure(Object *ob)
}
if (!ss->vemap) {
BKE_mesh_vert_edge_map_create(
- &ss->vemap, &ss->vemap_mem, mesh->medge, mesh->totvert, mesh->totedge);
+ &ss->vemap, &ss->vemap_mem, mesh->mvert, mesh->medge, mesh->totvert, mesh->totedge, false);
}
}
@@ -138,7 +138,7 @@ static int sculpt_poly_loop_opposite_edge_in_quad(SculptSession *ss,
return ss->mloop[ss->mpoly[poly].loopstart + next_edge_index_in_poly].e;
}
-int sculpt_poly_loop_initial_edge_from_cursor(Object *ob)
+SculptEdgeRef sculpt_poly_loop_initial_edge_from_cursor(Object *ob)
{
SculptSession *ss = ob->sculpt;
Mesh *mesh = BKE_object_get_original_mesh(ob);
@@ -148,10 +148,10 @@ int sculpt_poly_loop_initial_edge_from_cursor(Object *ob)
float *location = ss->cursor_location;
MVert *mvert = SCULPT_mesh_deformed_mverts_get(ss);
- MPoly *initial_poly = &mesh->mpoly[ss->active_face_index];
+ MPoly *initial_poly = &mesh->mpoly[ss->active_face_index.i];
if (initial_poly->totloop != 4) {
- return 0;
+ return (SculptEdgeRef){.i = SCULPT_REF_NONE};
}
int closest_vert = mesh->mloop[initial_poly->loopstart].v;
@@ -178,7 +178,7 @@ int sculpt_poly_loop_initial_edge_from_cursor(Object *ob)
closest_vert_on_initial_edge = other_vert;
}
}
- return initial_edge;
+ return (SculptEdgeRef){.i = initial_edge};
}
static void sculpt_poly_loop_iterate_and_fill(SculptSession *ss,
@@ -211,8 +211,13 @@ static void sculpt_poly_loop_iterate_and_fill(SculptSession *ss,
}
}
-
-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) {
+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;
@@ -225,15 +230,15 @@ static void sculpt_poly_loop_symm_poly_find(Object *ob, const int poly_index, co
MPoly *original_poly = &mesh->mpoly[poly_index];
float original_poly_center[3];
- BKE_mesh_calc_poly_center(original_poly, &mesh->mloop[original_poly->loopstart], mvert, original_poly_center);
+ BKE_mesh_calc_poly_center(
+ original_poly, &mesh->mloop[original_poly->loopstart], mvert, 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];
@@ -244,11 +249,10 @@ static void sculpt_poly_loop_symm_poly_find(Object *ob, const int poly_index, co
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, mvert[original_edge->v1].co, mvert[original_edge->v2].co);
@@ -272,9 +276,6 @@ static void sculpt_poly_loop_symm_poly_find(Object *ob, const int poly_index, co
*r_edge_index = search_edge_index;
}
-
-
-
}
BLI_bitmap *sculpt_poly_loop_from_cursor(Object *ob)
@@ -284,25 +285,28 @@ BLI_bitmap *sculpt_poly_loop_from_cursor(Object *ob)
BLI_bitmap *poly_loop = BLI_BITMAP_NEW(mesh->totpoly, "poly loop");
sculpt_poly_loop_topology_data_ensure(ob);
- const int initial_edge = sculpt_poly_loop_initial_edge_from_cursor(ob);
- const int initial_poly = ss->active_face_index;
+ const SculptEdgeRef initial_edge = sculpt_poly_loop_initial_edge_from_cursor(ob);
+ const SculptFaceRef initial_poly = ss->active_face_index;
+
+ const int initial_edge_i = BKE_pbvh_edge_index_to_table(ss->pbvh, initial_edge);
+ const int initial_poly_i = BKE_pbvh_face_index_to_table(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, initial_edge, symm_it, &initial_poly_symm, &initial_edge_symm);
+
+ 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);
+ 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.c b/source/blender/editors/sculpt_paint/sculpt_pose.c
index 3b2193c5beb..7a7dc7067bf 100644
--- a/source/blender/editors/sculpt_paint/sculpt_pose.c
+++ b/source/blender/editors/sculpt_paint/sculpt_pose.c
@@ -174,14 +174,14 @@ static void do_pose_brush_bend_task_cb_ex(void *__restrict userdata,
float final_pos[3];
SculptOrigVertData orig_data;
- SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]);
+ 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);
+ 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.index));
+ (1.0f - SCULPT_vertex_mask_get(ss, vd.vertex));
if (ik_chain_weight == 0.0f) {
continue;
}
@@ -241,10 +241,10 @@ static void do_pose_brush_task_cb_ex(void *__restrict userdata,
float final_pos[3];
SculptOrigVertData orig_data;
- SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]);
+ SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COORDS);
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);
float total_disp[3];
zero_v3(total_disp);
@@ -267,7 +267,7 @@ static void do_pose_brush_task_cb_ex(void *__restrict userdata,
/* Apply the vertex mask to the displacement. */
const float mask = vd.mask ? 1.0f - *vd.mask : 1.0f;
- const float automask = SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.index);
+ const float automask = SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.vertex);
mul_v3_fl(disp, mask * automask);
/* Accumulate the displacement. */
@@ -306,7 +306,7 @@ static void pose_brush_grow_factor_task_cb_ex(void *__restrict userdata,
float max = 0.0f;
/* Grow the factor. */
- SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.index, ni) {
+ SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.vertex, ni) {
float vmask_f = data->prev_mask[ni.index];
max = MAX2(vmask_f, max);
}
@@ -452,7 +452,8 @@ typedef struct PoseFloodFillData {
int current_face_set;
int next_face_set;
int prev_face_set;
- int next_vertex;
+ SculptVertRef next_vertex;
+ int next_vertex_index;
bool next_face_set_found;
@@ -482,11 +483,16 @@ typedef struct PoseFloodFillData {
int target_face_set;
} PoseFloodFillData;
-static bool pose_topology_floodfill_cb(
- SculptSession *ss, int UNUSED(from_v), int to_v, bool is_duplicate, void *userdata)
+static bool pose_topology_floodfill_cb(SculptSession *ss,
+ SculptVertRef UNUSED(from_v),
+ SculptVertRef to_vref,
+ bool is_duplicate,
+ void *userdata)
{
PoseFloodFillData *data = userdata;
- const float *co = SCULPT_vertex_co_get(ss, to_v);
+ int to_v = BKE_pbvh_vertex_index_to_table(ss->pbvh, to_vref);
+
+ const float *co = SCULPT_vertex_co_get(ss, to_vref);
if (data->pose_factor) {
data->pose_factor[to_v] = 1.0f;
@@ -511,15 +517,18 @@ static bool pose_topology_floodfill_cb(
return false;
}
-static bool pose_face_sets_floodfill_cb(
- SculptSession *ss, int UNUSED(from_v), int to_v, bool is_duplicate, void *userdata)
+static bool pose_face_sets_floodfill_cb(SculptSession *ss,
+ SculptVertRef UNUSED(from_v),
+ SculptVertRef to_v,
+ bool is_duplicate,
+ void *userdata)
{
PoseFloodFillData *data = userdata;
- const int index = to_v;
+ const int index = BKE_pbvh_vertex_index_to_table(ss->pbvh, to_v);
bool visit_next = false;
- const float *co = SCULPT_vertex_co_get(ss, index);
+ const float *co = SCULPT_vertex_co_get(ss, to_v);
const bool symmetry_check = SCULPT_check_vertex_pivot_symmetry(
co, data->pose_initial_co, data->symm) &&
!is_duplicate;
@@ -533,11 +542,11 @@ static bool pose_face_sets_floodfill_cb(
if (sculpt_pose_brush_is_vertex_inside_brush_radius(
co, data->pose_initial_co, data->radius, data->symm)) {
- const int visited_face_set = SCULPT_vertex_face_set_get(ss, index);
+ const int visited_face_set = SCULPT_vertex_face_set_get(ss, to_v);
BLI_gset_add(data->visited_face_sets, POINTER_FROM_INT(visited_face_set));
}
else if (symmetry_check) {
- data->current_face_set = SCULPT_vertex_face_set_get(ss, index);
+ data->current_face_set = SCULPT_vertex_face_set_get(ss, to_v);
BLI_gset_add(data->visited_face_sets, POINTER_FROM_INT(data->current_face_set));
}
return true;
@@ -551,11 +560,11 @@ static bool pose_face_sets_floodfill_cb(
GSetIterator gs_iter;
GSET_ITER (gs_iter, data->visited_face_sets) {
const int visited_face_set = POINTER_AS_INT(BLI_gsetIterator_getKey(&gs_iter));
- is_vertex_valid |= SCULPT_vertex_has_face_set(ss, index, visited_face_set);
+ is_vertex_valid |= SCULPT_vertex_has_face_set(ss, to_v, visited_face_set);
}
}
else {
- is_vertex_valid = SCULPT_vertex_has_face_set(ss, index, data->current_face_set);
+ is_vertex_valid = SCULPT_vertex_has_face_set(ss, to_v, data->current_face_set);
}
if (!is_vertex_valid) {
@@ -570,11 +579,11 @@ static bool pose_face_sets_floodfill_cb(
/* Fallback origin accumulation. */
if (symmetry_check) {
- add_v3_v3(data->fallback_origin, SCULPT_vertex_co_get(ss, index));
+ add_v3_v3(data->fallback_origin, SCULPT_vertex_co_get(ss, to_v));
data->fallback_count++;
}
- if (!symmetry_check || SCULPT_vertex_has_unique_face_set(ss, index)) {
+ if (!symmetry_check || SCULPT_vertex_has_unique_face_set(ss, to_v)) {
return visit_next;
}
@@ -583,15 +592,15 @@ static bool pose_face_sets_floodfill_cb(
bool count_as_boundary = false;
SculptVertexNeighborIter ni;
- SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, index, ni) {
- int next_face_set_candidate = SCULPT_vertex_face_set_get(ss, ni.index);
+ SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, to_v, ni) {
+ int next_face_set_candidate = SCULPT_vertex_face_set_get(ss, ni.vertex);
/* Check if we can get a valid face set for the next iteration from this neighbor. */
- if (SCULPT_vertex_has_unique_face_set(ss, ni.index) &&
+ if (SCULPT_vertex_has_unique_face_set(ss, ni.vertex) &&
!BLI_gset_haskey(data->visited_face_sets, POINTER_FROM_INT(next_face_set_candidate))) {
if (!data->next_face_set_found) {
data->next_face_set = next_face_set_candidate;
- data->next_vertex = ni.index;
+ data->next_vertex = ni.vertex;
data->next_face_set_found = true;
}
count_as_boundary = true;
@@ -601,7 +610,7 @@ static bool pose_face_sets_floodfill_cb(
/* Origin accumulation. */
if (count_as_boundary) {
- add_v3_v3(data->pose_origin, SCULPT_vertex_co_get(ss, index));
+ add_v3_v3(data->pose_origin, SCULPT_vertex_co_get(ss, to_v));
data->tot_co++;
}
return visit_next;
@@ -625,8 +634,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);
@@ -677,7 +684,7 @@ static void pose_brush_init_task_cb_ex(void *__restrict userdata,
SculptVertexNeighborIter ni;
float avg = 0.0f;
int total = 0;
- SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.index, ni) {
+ SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.vertex, ni) {
avg += data->pose_factor[ni.index];
total++;
}
@@ -748,12 +755,15 @@ 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];
int totvert = SCULPT_vertex_count_get(ss);
- int nearest_vertex_index = SCULPT_nearest_vertex_get(sd, ob, initial_location, FLT_MAX, true);
+ SculptVertRef nearest_vertex = SCULPT_nearest_vertex_get(
+ sd, ob, initial_location, FLT_MAX, true);
+ int nearest_vertex_index = BKE_pbvh_vertex_index_to_table(ss->pbvh, nearest_vertex);
/* Init the buffers used to keep track of the changes in the pose factors as more segments are
* added to the IK chain. */
@@ -838,7 +848,8 @@ static SculptPoseIKChain *pose_ik_chain_init_face_sets(
int current_face_set = SCULPT_FACE_SET_NONE;
int prev_face_set = SCULPT_FACE_SET_NONE;
- int current_vertex = SCULPT_active_vertex_get(ss);
+ SculptVertRef current_vertex = SCULPT_active_vertex_get(ss);
+ int current_vertex_i = BKE_pbvh_vertex_index_to_table(ss->pbvh, current_vertex);
for (int s = 0; s < ik_chain->tot_segments; s++) {
@@ -867,6 +878,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);
@@ -893,10 +911,15 @@ static SculptPoseIKChain *pose_ik_chain_init_face_sets(
return ik_chain;
}
-static bool pose_face_sets_fk_find_masked_floodfill_cb(
- SculptSession *ss, int from_v, int to_v, bool is_duplicate, void *userdata)
+static bool pose_face_sets_fk_find_masked_floodfill_cb(SculptSession *ss,
+ SculptVertRef from_vr,
+ SculptVertRef to_vr,
+ bool is_duplicate,
+ void *userdata)
{
PoseFloodFillData *data = userdata;
+ int from_v = BKE_pbvh_vertex_index_to_table(ss->pbvh, from_vr);
+ int to_v = BKE_pbvh_vertex_index_to_table(ss->pbvh, to_vr);
if (!is_duplicate) {
data->floodfill_it[to_v] = data->floodfill_it[from_v] + 1;
@@ -905,11 +928,11 @@ static bool pose_face_sets_fk_find_masked_floodfill_cb(
data->floodfill_it[to_v] = data->floodfill_it[from_v];
}
- const int to_face_set = SCULPT_vertex_face_set_get(ss, to_v);
+ const int to_face_set = SCULPT_vertex_face_set_get(ss, to_vr);
if (!BLI_gset_haskey(data->visited_face_sets, POINTER_FROM_INT(to_face_set))) {
- if (SCULPT_vertex_has_unique_face_set(ss, to_v) &&
- !SCULPT_vertex_has_unique_face_set(ss, from_v) &&
- SCULPT_vertex_has_face_set(ss, from_v, to_face_set)) {
+ if (SCULPT_vertex_has_unique_face_set(ss, to_vr) &&
+ !SCULPT_vertex_has_unique_face_set(ss, from_vr) &&
+ SCULPT_vertex_has_face_set(ss, from_vr, to_face_set)) {
BLI_gset_add(data->visited_face_sets, POINTER_FROM_INT(to_face_set));
@@ -924,14 +947,17 @@ static bool pose_face_sets_fk_find_masked_floodfill_cb(
}
}
- return SCULPT_vertex_has_face_set(ss, to_v, data->initial_face_set);
+ return SCULPT_vertex_has_face_set(ss, to_vr, data->initial_face_set);
}
-static bool pose_face_sets_fk_set_weights_floodfill_cb(
- SculptSession *ss, int UNUSED(from_v), int to_v, bool UNUSED(is_duplicate), void *userdata)
+static bool pose_face_sets_fk_set_weights_floodfill_cb(SculptSession *ss,
+ SculptVertRef UNUSED(from_v),
+ SculptVertRef to_v,
+ bool UNUSED(is_duplicate),
+ void *userdata)
{
PoseFloodFillData *data = userdata;
- data->fk_weights[to_v] = 1.0f;
+ data->fk_weights[BKE_pbvh_vertex_index_to_table(ss->pbvh, to_v)] = 1.0f;
return !SCULPT_vertex_has_face_set(ss, to_v, data->masked_face_set);
}
@@ -942,7 +968,9 @@ static SculptPoseIKChain *pose_ik_chain_init_face_sets_fk(
SculptPoseIKChain *ik_chain = pose_ik_chain_new(1, totvert);
- const int active_vertex = SCULPT_active_vertex_get(ss);
+ const SculptVertRef active_vertex = SCULPT_active_vertex_get(ss);
+ const int active_vertex_i = BKE_pbvh_vertex_index_to_table(ss->pbvh, active_vertex);
+
const int active_face_set = SCULPT_active_face_set_get(ss);
SculptFloodFill flood;
@@ -950,7 +978,7 @@ static SculptPoseIKChain *pose_ik_chain_init_face_sets_fk(
SCULPT_floodfill_add_initial(&flood, active_vertex);
PoseFloodFillData fdata;
fdata.floodfill_it = MEM_calloc_arrayN(totvert, sizeof(int), "floodfill iteration");
- fdata.floodfill_it[active_vertex] = 1;
+ fdata.floodfill_it[active_vertex_i] = 1;
fdata.initial_face_set = active_face_set;
fdata.masked_face_set = SCULPT_FACE_SET_NONE;
fdata.target_face_set = SCULPT_FACE_SET_NONE;
@@ -963,9 +991,12 @@ static SculptPoseIKChain *pose_ik_chain_init_face_sets_fk(
int origin_count = 0;
float origin_acc[3] = {0.0f};
for (int i = 0; i < totvert; i++) {
- if (fdata.floodfill_it[i] != 0 && SCULPT_vertex_has_face_set(ss, i, fdata.initial_face_set) &&
- SCULPT_vertex_has_face_set(ss, i, fdata.masked_face_set)) {
- add_v3_v3(origin_acc, SCULPT_vertex_co_get(ss, i));
+ SculptVertRef vref = BKE_pbvh_table_index_to_vertex(ss->pbvh, i);
+
+ if (fdata.floodfill_it[i] != 0 &&
+ SCULPT_vertex_has_face_set(ss, vref, fdata.initial_face_set) &&
+ SCULPT_vertex_has_face_set(ss, vref, fdata.masked_face_set)) {
+ add_v3_v3(origin_acc, SCULPT_vertex_co_get(ss, vref));
origin_count++;
}
}
@@ -974,10 +1005,12 @@ static SculptPoseIKChain *pose_ik_chain_init_face_sets_fk(
float target_acc[3] = {0.0f};
if (fdata.target_face_set != fdata.masked_face_set) {
for (int i = 0; i < totvert; i++) {
+ SculptVertRef vref = BKE_pbvh_table_index_to_vertex(ss->pbvh, i);
+
if (fdata.floodfill_it[i] != 0 &&
- SCULPT_vertex_has_face_set(ss, i, fdata.initial_face_set) &&
- SCULPT_vertex_has_face_set(ss, i, fdata.target_face_set)) {
- add_v3_v3(target_acc, SCULPT_vertex_co_get(ss, i));
+ SCULPT_vertex_has_face_set(ss, vref, fdata.initial_face_set) &&
+ SCULPT_vertex_has_face_set(ss, vref, fdata.target_face_set)) {
+ add_v3_v3(target_acc, SCULPT_vertex_co_get(ss, vref));
target_count++;
}
}
@@ -1203,11 +1236,13 @@ static void sculpt_pose_do_bend_deform(SculptSession *ss, Brush *UNUSED(brush))
float smd_limit[2];
for (int i = 0; i < totvert; i++) {
+ SculptVertRef vertex = BKE_pbvh_table_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, i));
+ 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]);
}
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..b129c049381
--- /dev/null
+++ b/source/blender/editors/sculpt_paint/sculpt_replay.c
@@ -0,0 +1,1228 @@
+#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 <string.h>
+
+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 <stdarg.h>
+
+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);
+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;
+
+ SCULPT_dyntopo_node_layers_update_offsets(ss);
+
+ /* 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(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;
+ 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);
+ 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();
+ 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_index.i = -1LL;
+ ss.active_face_index.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_index.i != -1LL) {
+ samp->have_active_vertex = true;
+ // copy_v3_v3(samp->active_vertex_co, SCULPT_vertex_co_get(ss, ss->active_vertex_index));
+ }
+ 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.c b/source/blender/editors/sculpt_paint/sculpt_smooth.c
index 4006359e0e4..c4aec5f9dd0 100644
--- a/source/blender/editors/sculpt_paint/sculpt_smooth.c
+++ b/source/blender/editors/sculpt_paint/sculpt_smooth.c
@@ -1,3 +1,4 @@
+
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -23,15 +24,20 @@
#include "MEM_guardedalloc.h"
+#include "BLI_alloca.h"
#include "BLI_blenlib.h"
+#include "BLI_compiler_attrs.h"
#include "BLI_hash.h"
#include "BLI_math.h"
+#include "BLI_rand.h"
#include "BLI_task.h"
+#include "BLI_threads.h"
#include "DNA_brush_types.h"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
+#include "BKE_attribute.h"
#include "BKE_brush.h"
#include "BKE_context.h"
#include "BKE_mesh.h"
@@ -57,79 +63,607 @@
#include "RNA_access.h"
#include "RNA_define.h"
+#include "atomic_ops.h"
#include "bmesh.h"
-
+#ifdef PROXY_ADVANCED
+/* clang-format off */
+#include "BKE_DerivedMesh.h"
+#include "../../blenkernel/intern/pbvh_intern.h"
+/* clang-format on */
+#endif
#include <math.h>
#include <stdlib.h>
-void SCULPT_neighbor_coords_average_interior(SculptSession *ss, float result[3], int index)
+void SCULPT_neighbor_coords_average_interior(SculptSession *ss,
+ float result[3],
+ SculptVertRef vertex,
+ float projection,
+ SculptCustomLayer *bound_scl,
+ bool do_origco)
+{
+ float avg[3] = {0.0f, 0.0f, 0.0f};
+
+ MDynTopoVert *mv = SCULPT_vertex_get_mdyntopo(ss, vertex);
+
+ if (do_origco) {
+ SCULPT_vertex_check_origdata(ss, vertex);
+ }
+
+ float total = 0.0f;
+ int neighbor_count = 0;
+ bool check_fsets = ss->cache->brush->flag2 & BRUSH_SMOOTH_PRESERVE_FACE_SETS;
+
+ int bflag = SCULPT_BOUNDARY_MESH | SCULPT_BOUNDARY_SHARP;
+ float bound_smooth = powf(ss->cache->brush->boundary_smooth_factor, BOUNDARY_SMOOTH_EXP);
+ float slide_fset = BKE_brush_fset_slide_get(ss->scene, ss->cache->brush);
+
+ slide_fset = MAX2(slide_fset, bound_smooth);
+
+ if (check_fsets) {
+ bflag |= SCULPT_BOUNDARY_FACE_SET;
+ }
+
+ const SculptBoundaryType is_boundary = SCULPT_vertex_is_boundary(ss, vertex, bflag);
+
+ const float *co = do_origco ? mv->origco : SCULPT_vertex_co_get(ss, vertex);
+ float no[3];
+
+ if (true || projection > 0.0f) {
+ if (do_origco) {
+ copy_v3_v3(no, mv->origno);
+ }
+ else {
+ SCULPT_vertex_normal_get(ss, vertex, no);
+ }
+ }
+
+ const bool weighted = (ss->cache->brush->flag2 & BRUSH_SMOOTH_USE_AREA_WEIGHT) && !is_boundary;
+ float *areas = NULL;
+
+ SculptCornerType ctype = SCULPT_CORNER_MESH | SCULPT_CORNER_SHARP;
+ if (check_fsets) {
+ ctype |= SCULPT_CORNER_FACE_SET;
+ }
+
+ // bool have_bmesh = ss->bm;
+
+ if (weighted || bound_scl) {
+ int val = SCULPT_vertex_valence_get(ss, vertex);
+ areas = BLI_array_alloca(areas, val);
+
+ BKE_pbvh_get_vert_face_areas(ss->pbvh, vertex, areas, val);
+ }
+
+ float *b1 = NULL, btot = 0.0f, b1_orig;
+
+ if (bound_scl) {
+ b1 = SCULPT_temp_cdata_get(vertex, bound_scl);
+ b1_orig = *b1;
+ *b1 = 0.0f;
+ }
+
+ SculptVertexNeighborIter ni;
+ SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vertex, ni) {
+ MDynTopoVert *mv2 = SCULPT_vertex_get_mdyntopo(ss, ni.vertex);
+ const float *co2;
+
+ if (!do_origco || mv2->stroke_id != ss->stroke_id) {
+ co2 = SCULPT_vertex_co_get(ss, ni.vertex);
+ }
+ else {
+ co2 = mv2->origco;
+ }
+
+ neighbor_count++;
+
+ float tmp[3], w;
+ bool ok = false;
+
+ if (weighted) {
+ w = areas[ni.i];
+ }
+ else {
+ w = 1.0f;
+ }
+
+ bool do_diffuse = false;
+
+ /*use the new edge api if edges are available, if not estimate boundary
+ from verts*/
+
+ SculptBoundaryType final_boundary = 0;
+
+ if (ni.has_edge) {
+ final_boundary = SCULPT_edge_is_boundary(ss, ni.edge, bflag);
+
+#ifdef SCULPT_DIAGONAL_EDGE_MARKS
+ if (ss->bm) {
+ BMEdge *e = (BMEdge *)ni.edge.i;
+ if (!(e->head.hflag & BM_ELEM_DRAW)) {
+ neighbor_count--;
+ continue;
+ }
+ }
+#endif
+ }
+ else {
+ final_boundary = is_boundary & SCULPT_vertex_is_boundary(ss, ni.vertex, bflag);
+ }
+
+ do_diffuse = bound_scl != NULL;
+
+ if (is_boundary) {
+ /* Boundary vertices use only other boundary vertices.
+
+ This if statement needs to be refactored a bit, it's confusing.
+
+ */
+
+ bool slide = (slide_fset > 0.0f && is_boundary == SCULPT_BOUNDARY_FACE_SET) ||
+ bound_smooth > 0.0f;
+ slide = slide && !final_boundary;
+
+ if (slide) {
+ // project non-boundary offset onto boundary normal
+ float t[3];
+
+ w *= slide_fset;
+
+ sub_v3_v3v3(t, co2, co);
+ madd_v3_v3v3fl(tmp, co, no, dot_v3v3(t, no));
+ ok = true;
+ }
+ else if (final_boundary) {
+ copy_v3_v3(tmp, co2);
+ ok = true;
+ do_diffuse = false;
+ }
+ else {
+ ok = false;
+ }
+ }
+ else {
+ copy_v3_v3(tmp, co2);
+ ok = true;
+ }
+
+ if (do_diffuse && bound_scl && !is_boundary) {
+ /*
+ simple boundary inflator using an ad-hoc diffusion-based pseudo-geodesic field
+
+ makes more rounded edges.
+ */
+ copy_v3_v3(tmp, co2);
+ ok = true;
+
+ float len = len_v3v3(co, tmp);
+ float w2 = 1.0f;
+
+ float *b2 = SCULPT_temp_cdata_get(ni.vertex, bound_scl);
+ float b2_val = *b2 + len;
+
+ if (SCULPT_vertex_is_boundary(ss, ni.vertex, bflag)) {
+ w2 = 1000.0f;
+ b2_val = len;
+ }
+
+ *b1 += b2_val * w2;
+ btot += w2;
+
+ float no2[3];
+
+ if (!do_origco || mv2->stroke_id != ss->stroke_id) {
+ SCULPT_vertex_normal_get(ss, ni.vertex, no2);
+ }
+ else {
+ copy_v3_v3(no2, mv2->origno);
+ }
+
+ float radius = ss->cache->radius * 10.0f;
+
+ float th = radius - b1_orig;
+ th = MAX2(th, 0.0f);
+ th /= radius;
+
+#if 0
+ float *color = (float *)SCULPT_vertex_color_get(ss, ni.vertex);
+ color[0] = color[1] = color[2] = th;
+ color[3] = 1.0f;
+#endif
+
+ float fac = ss->cache->brush->boundary_smooth_factor;
+ fac = MIN2(fac * 4.0f, 1.0f);
+ fac = powf(fac, 0.2);
+ th *= fac;
+
+ sub_v3_v3(tmp, co);
+ madd_v3_v3fl(tmp, no2, th * dot_v3v3(no2, tmp));
+ add_v3_v3(tmp, co);
+ }
+
+ if (!ok) {
+ continue;
+ }
+
+ if (projection > 0.0f) {
+ sub_v3_v3(tmp, co);
+ float fac = dot_v3v3(tmp, no);
+ madd_v3_v3fl(tmp, no, -fac * projection);
+ madd_v3_v3fl(avg, tmp, w);
+ }
+ else {
+ madd_v3_v3fl(avg, tmp, w);
+ }
+
+ total += w;
+ }
+ SCULPT_VERTEX_NEIGHBORS_ITER_END(ni);
+
+ if (btot != 0.0f) {
+ *b1 /= btot;
+ //*b1 += (b1_orig - *b1) * 0.95f;
+ }
+ else if (b1) {
+ *b1 = b1_orig;
+ }
+
+ /* Do not modify corner vertices. */
+ if (neighbor_count <= 2 && is_boundary) {
+ copy_v3_v3(result, co);
+ return;
+ }
+
+ /* Avoid division by 0 when there are no neighbors. */
+ if (total == 0.0f) {
+ copy_v3_v3(result, co);
+ return;
+ }
+
+ mul_v3_v3fl(result, avg, 1.0f / total);
+
+ if (projection > 0.0f) {
+ add_v3_v3(result, co);
+ }
+
+ SculptCornerType c = SCULPT_vertex_is_corner(ss, vertex, ctype);
+ float corner_smooth;
+
+ if (c == 0) {
+ return;
+ }
+
+ if (c & SCULPT_CORNER_FACE_SET) {
+ corner_smooth = MAX2(slide_fset, bound_smooth);
+ }
+ else {
+ corner_smooth = bound_smooth;
+ }
+
+ interp_v3_v3v3(result, result, co, 1.0f - corner_smooth);
+}
+
+void SCULPT_neighbor_coords_average_interior_velocity(SculptSession *ss,
+ float result[3],
+ SculptVertRef vertex,
+ float projection,
+ SculptCustomLayer *scl)
{
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, index);
+ bool check_fsets = ss->cache->brush->flag2 & BRUSH_SMOOTH_PRESERVE_FACE_SETS;
+ int bflag = SCULPT_BOUNDARY_MESH | SCULPT_BOUNDARY_SHARP;
+
+ if (check_fsets) {
+ bflag |= SCULPT_BOUNDARY_FACE_SET;
+ }
+
+ const bool is_boundary = SCULPT_vertex_is_boundary(ss, vertex, bflag);
+ const float *co = SCULPT_vertex_co_get(ss, vertex);
+ float no[3];
+
+ if (projection > 0.0f) {
+ SCULPT_vertex_normal_get(ss, vertex, no);
+ }
+
+ float vel[3];
+
+ copy_v3_v3(vel, (float *)SCULPT_temp_cdata_get(vertex, scl));
+ mul_v3_fl(vel, 0.4f);
SculptVertexNeighborIter ni;
- SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, index, ni) {
+ SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vertex, ni) {
neighbor_count++;
+
+ float tmp[3];
+ bool ok = false;
+
+ float *vel2 = SCULPT_temp_cdata_get(ni.vertex, scl);
+
+ // propegate smooth velocities a bit
+ madd_v3_v3fl(vel2, vel, 1.0f / (float)ni.size);
+
if (is_boundary) {
/* Boundary vertices use only other boundary vertices. */
- if (SCULPT_vertex_is_boundary(ss, ni.index)) {
- add_v3_v3(avg, SCULPT_vertex_co_get(ss, ni.index));
- total++;
+ if (SCULPT_vertex_is_boundary(ss, ni.vertex, bflag)) {
+ copy_v3_v3(tmp, SCULPT_vertex_co_get(ss, ni.vertex));
+ ok = true;
}
}
else {
/* Interior vertices use all neighbors. */
- add_v3_v3(avg, SCULPT_vertex_co_get(ss, ni.index));
- total++;
+ copy_v3_v3(tmp, SCULPT_vertex_co_get(ss, ni.vertex));
+ ok = true;
+ }
+
+ if (!ok) {
+ continue;
+ }
+
+ if (projection > 0.0f) {
+ sub_v3_v3(tmp, co);
+ float fac = dot_v3v3(tmp, no);
+ madd_v3_v3fl(tmp, no, -fac * projection);
+ add_v3_v3(avg, tmp);
}
+ else {
+ add_v3_v3(avg, tmp);
+ }
+
+ total++;
}
SCULPT_VERTEX_NEIGHBORS_ITER_END(ni);
/* Do not modify corner vertices. */
- if (neighbor_count <= 2 && is_boundary) {
- copy_v3_v3(result, SCULPT_vertex_co_get(ss, index));
+ if (neighbor_count <= 2) {
+ 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, index));
+ copy_v3_v3(result, SCULPT_vertex_co_get(ss, vertex));
return;
}
mul_v3_v3fl(result, avg, 1.0f / total);
+
+ if (projection > 0.0f) {
+ add_v3_v3(result, co);
+ }
+}
+
+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);
+ }
+}
+
+volatile int blehrand = 0;
+static int blehrand_get()
+{
+ int i = blehrand;
+ i = (i * 124325 + 231423322) & 524287;
+
+ blehrand = i;
+ return i;
}
/* 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],
+ BMVert *v,
+ float projection,
+ bool check_fsets,
+ int cd_temp,
+ int cd_dyn_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);
+
+ MDynTopoVert *mv = BKE_PBVH_DYNVERT(cd_dyn_vert, v);
+
+ float *col = BM_ELEM_CD_GET_VOID_P(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, (SculptVertRef){.i = (intptr_t)v});
+
+ if (do_origco) {
+ // SCULPT_vertex_check_origdata(ss, (SculptVertRef){.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) {
+ SculptVertRef vertex = {.i = (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);
+ }
+
+ 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_GET_VOID_P(v_other, cd_temp);
+
+ float bucketw = 1.0f; // col2[3] < col[3] ? 2.0f : 1.0f;
+ // bucketw /= 0.00001f + len_v3v3(e->v1->co, e->v2->co);
+ // if (weighted) {
+ // bucketw = 1.0 / (0.000001 + areas[area_i]);
+ //}
+ // if (e == v->e) {
+ // bucketw *= 2.0;
+ //}
+
+ MDynTopoVert *mv2 = BKE_PBVH_DYNVERT(cd_dyn_vert, v_other);
+ float *co2;
+ float *no2;
+
+ if (!do_origco || mv2->stroke_id != ss->stroke_id) {
+ co2 = v_other->co;
+ no2 = v_other->no;
+ }
+ else {
+ co2 = mv2->origco;
+ no2 = mv2->origno;
+ }
+
+ // bool bound = (mv2->flag &
+ // (DYNVERT_BOUNDARY)); // | DYNVERT_FSET_BOUNDARY | DYNVERT_SHARP_BOUNDARY));
+ // bool bound2 = (mv2->flag &
+ // (DYNVERT_BOUNDARY | DYNVERT_FSET_BOUNDARY | DYNVERT_SHARP_BOUNDARY));
+
+ SculptBoundaryType bflag = SCULPT_BOUNDARY_FACE_SET | SCULPT_BOUNDARY_MESH |
+ SCULPT_BOUNDARY_SHARP | SCULPT_BOUNDARY_SEAM;
+
+ int bound = SCULPT_edge_is_boundary(ss, (SculptEdgeRef){.i = (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;
}
@@ -139,47 +673,201 @@ void SCULPT_bmesh_four_neighbor_average(float avg[3], float direction[3], BMVert
/* 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);
+ }
+
+ // 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;
+ }
+ }
+
+ // negate_v3(col);
+ vec_transform(col, no1, bi);
+ // negate_v3(col);
}
}
+static void sculpt_neighbor_coords_average_fset(SculptSession *ss,
+ float result[3],
+ SculptVertRef vertex,
+ float projection)
+{
+ float avg[3] = {0.0f, 0.0f, 0.0f};
+ float *co, no[3];
+ float total = 0.0f;
+
+ bool boundary = !SCULPT_vertex_has_unique_face_set(ss, vertex);
+
+ if (projection > 0.0f) {
+ co = (float *)SCULPT_vertex_co_get(ss, vertex);
+ SCULPT_vertex_normal_get(ss, vertex, no);
+ }
+
+ const bool weighted = (ss->cache->brush->flag2 & BRUSH_SMOOTH_USE_AREA_WEIGHT) && !boundary;
+ float *areas;
+
+ if (weighted) {
+ int val = SCULPT_vertex_valence_get(ss, vertex);
+ areas = BLI_array_alloca(areas, val);
+
+ BKE_pbvh_get_vert_face_areas(ss->pbvh, vertex, areas, val);
+ }
+
+ SculptVertexNeighborIter ni;
+ SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vertex, ni) {
+ const float *co2 = SCULPT_vertex_co_get(ss, ni.vertex);
+ float w;
+
+ if (weighted) {
+ w = areas[ni.i];
+ }
+ else {
+ w = 1.0f;
+ }
+
+ if (boundary && SCULPT_vertex_has_unique_face_set(ss, ni.vertex)) {
+ continue;
+ }
+
+ if (projection > 0.0f) {
+ float tmp[3];
+
+ sub_v3_v3v3(tmp, co2, co);
+ float fac = dot_v3v3(tmp, no);
+ madd_v3_v3fl(tmp, no, -fac * projection);
+
+ madd_v3_v3fl(avg, tmp, w);
+ }
+ else {
+ madd_v3_v3fl(avg, co2, w);
+ }
+ total += w;
+ }
+ SCULPT_VERTEX_NEIGHBORS_ITER_END(ni);
+
+ if (total > (boundary ? 1.0f : 0.0f)) {
+ mul_v3_v3fl(result, avg, 1.0f / total);
+
+ if (projection > 0.0) {
+ add_v3_v3(result, co);
+ }
+ }
+ else {
+ copy_v3_v3(result, SCULPT_vertex_co_get(ss, vertex));
+ }
+}
/* Generic functions for laplacian smoothing. These functions do not take boundary vertices into
* account. */
-void SCULPT_neighbor_coords_average(SculptSession *ss, float result[3], int index)
+void SCULPT_neighbor_coords_average(
+ SculptSession *ss, float result[3], SculptVertRef vertex, float projection, bool check_fsets)
{
+ if (check_fsets) {
+ sculpt_neighbor_coords_average_fset(ss, result, vertex, projection);
+ return;
+ }
+
float avg[3] = {0.0f, 0.0f, 0.0f};
- int total = 0;
+ float *co, no[3];
+ float total = 0.0f;
+
+ if (projection > 0.0f) {
+ co = (float *)SCULPT_vertex_co_get(ss, vertex);
+ SCULPT_vertex_normal_get(ss, vertex, no);
+ }
+
+ const bool weighted = ss->cache->brush->flag2 & BRUSH_SMOOTH_USE_AREA_WEIGHT;
+ float *areas;
+
+ if (weighted) {
+ int val = SCULPT_vertex_valence_get(ss, vertex);
+ areas = BLI_array_alloca(areas, val);
+
+ BKE_pbvh_get_vert_face_areas(ss->pbvh, vertex, areas, val);
+ }
SculptVertexNeighborIter ni;
- SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, index, ni) {
- add_v3_v3(avg, SCULPT_vertex_co_get(ss, ni.index));
- total++;
+ SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vertex, ni) {
+ const float *co2 = SCULPT_vertex_co_get(ss, ni.vertex);
+ float w;
+
+ if (weighted) {
+ w = areas[ni.i];
+ }
+ else {
+ w = 1.0f;
+ }
+
+ if (projection > 0.0f) {
+ float tmp[3];
+
+ sub_v3_v3v3(tmp, co2, co);
+ float fac = dot_v3v3(tmp, no);
+ madd_v3_v3fl(tmp, no, -fac * projection);
+
+ madd_v3_v3fl(avg, tmp, w);
+ }
+ else {
+ madd_v3_v3fl(avg, co2, w);
+ }
+ total += w;
}
SCULPT_VERTEX_NEIGHBORS_ITER_END(ni);
- if (total > 0) {
+ if (total > 0.0f) {
mul_v3_v3fl(result, avg, 1.0f / total);
+
+ if (projection > 0.0) {
+ add_v3_v3(result, co);
+ }
}
else {
- copy_v3_v3(result, SCULPT_vertex_co_get(ss, index));
+ copy_v3_v3(result, SCULPT_vertex_co_get(ss, vertex));
}
}
-float SCULPT_neighbor_mask_average(SculptSession *ss, int index)
+float SCULPT_neighbor_mask_average(SculptSession *ss, SculptVertRef index)
{
float avg = 0.0f;
int total = 0;
SculptVertexNeighborIter ni;
SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, index, ni) {
- avg += SCULPT_vertex_mask_get(ss, ni.index);
+ avg += SCULPT_vertex_mask_get(ss, ni.vertex);
total++;
}
SCULPT_VERTEX_NEIGHBORS_ITER_END(ni);
@@ -190,14 +878,14 @@ float SCULPT_neighbor_mask_average(SculptSession *ss, int index)
return SCULPT_vertex_mask_get(ss, index);
}
-void SCULPT_neighbor_color_average(SculptSession *ss, float result[4], int index)
+void SCULPT_neighbor_color_average(SculptSession *ss, float result[4], SculptVertRef index)
{
float avg[4] = {0.0f, 0.0f, 0.0f, 0.0f};
int total = 0;
SculptVertexNeighborIter ni;
SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, index, ni) {
- add_v4_v4(avg, SCULPT_vertex_color_get(ss, ni.index));
+ add_v4_v4(avg, SCULPT_vertex_color_get(ss, ni.vertex));
total++;
}
SCULPT_VERTEX_NEIGHBORS_ITER_END(ni);
@@ -241,11 +929,13 @@ static void do_enhance_details_brush_task_cb_ex(void *__restrict userdata,
vd.no,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
- vd.index,
+ vd.vertex,
thread_id);
float disp[3];
- madd_v3_v3v3fl(disp, vd.co, ss->cache->detail_directions[vd.index], fade);
+ float *dir = SCULPT_temp_cdata_get(vd.vertex, data->scl);
+
+ madd_v3_v3v3fl(disp, vd.co, dir, fade);
SCULPT_clip(sd, ss, vd.co, disp);
if (vd.mvert) {
@@ -263,33 +953,42 @@ static void SCULPT_enhance_details_brush(Sculpt *sd,
SculptSession *ss = ob->sculpt;
Brush *brush = BKE_paint_brush(&sd->paint);
+ SculptCustomLayer scl;
+
+ SCULPT_temp_customlayer_ensure(
+ ss, ATTR_DOMAIN_POINT, CD_PROP_FLOAT3, "__dyntopo_detail_dir", false);
+ SCULPT_temp_customlayer_get(
+ ss, ATTR_DOMAIN_POINT, CD_PROP_FLOAT3, "__dyntopo_detail_dir", &scl, false);
+
+ 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);
+ }
+
SCULPT_vertex_random_access_ensure(ss);
SCULPT_boundary_info_ensure(ob);
if (SCULPT_stroke_is_first_brush_step(ss->cache)) {
const int totvert = SCULPT_vertex_count_get(ss);
- ss->cache->detail_directions = MEM_malloc_arrayN(
- totvert, 3 * sizeof(float), "details directions");
for (int i = 0; i < totvert; i++) {
float avg[3];
- SCULPT_neighbor_coords_average(ss, avg, i);
- sub_v3_v3v3(ss->cache->detail_directions[i], avg, SCULPT_vertex_co_get(ss, i));
+ SculptVertRef vertex = BKE_pbvh_table_index_to_vertex(ss->pbvh, i);
+ float *dir = SCULPT_temp_cdata_get(vertex, &scl);
+
+ SCULPT_neighbor_coords_average(ss, avg, vertex, 0.0f, false);
+ sub_v3_v3v3(dir, avg, SCULPT_vertex_co_get(ss, vertex));
}
}
- SculptThreadedTaskData data = {
- .sd = sd,
- .ob = ob,
- .brush = brush,
- .nodes = nodes,
- };
+ SculptThreadedTaskData data = {.sd = sd, .ob = ob, .brush = brush, .nodes = nodes, .scl = &scl};
TaskParallelSettings settings;
BKE_pbvh_parallel_range_settings(&settings, true, totnode);
BLI_task_parallel_range(0, totnode, &data, do_enhance_details_brush_task_cb_ex, &settings);
}
+#ifdef PROXY_ADVANCED
static void do_smooth_brush_task_cb_ex(void *__restrict userdata,
const int n,
const TaskParallelTLS *__restrict tls)
@@ -311,6 +1010,107 @@ static void do_smooth_brush_task_cb_ex(void *__restrict userdata,
const int thread_id = BLI_task_parallel_thread_id(tls);
+ PBVHNode **nodes = data->nodes;
+ ProxyVertArray *p = &nodes[n]->proxyverts;
+
+ for (int i = 0; i < p->size; i++) {
+ float co[3] = {0.0f, 0.0f, 0.0f};
+ int ni = 0;
+
+# if 1
+ if (sculpt_brush_test_sq_fn(&test, p->co[i])) {
+ const float fade = bstrength * SCULPT_brush_strength_factor(
+ ss,
+ brush,
+ p->co[i],
+ sqrtf(test.dist),
+ p->no[i],
+ p->fno[i],
+ smooth_mask ? 0.0f : (p->mask ? p->mask[i] : 0.0f),
+ p->index[i],
+ thread_id);
+# else
+ if (1) {
+ const float fade = 1.0;
+# endif
+
+ while (ni < MAX_PROXY_NEIGHBORS && p->neighbors[i][ni].node >= 0) {
+ ProxyKey *key = p->neighbors[i] + ni;
+ PBVHNode *n2 = ss->pbvh->nodes + key->node;
+
+ // printf("%d %d %d %p\n", key->node, key->pindex, ss->pbvh->totnode, n2);
+
+ if (key->pindex < 0 || key->pindex >= n2->proxyverts.size) {
+ printf("corruption!\n");
+ fflush(stdout);
+ ni++;
+ continue;
+ }
+
+ if (n2->proxyverts.co) {
+ add_v3_v3(co, n2->proxyverts.co[key->pindex]);
+ ni++;
+ }
+ }
+
+ // printf("ni %d\n", ni);
+
+ if (ni > 2) {
+ mul_v3_fl(co, 1.0f / (float)ni);
+ }
+ else {
+ copy_v3_v3(co, p->co[i]);
+ }
+
+ // printf("%f %f %f ", co[0], co[1], co[2]);
+
+ interp_v3_v3v3(p->co[i], p->co[i], co, fade);
+ }
+ }
+}
+
+#else
+
+static void do_smooth_brush_task_cb_ex(void *__restrict userdata,
+ const int n,
+ const TaskParallelTLS *__restrict tls)
+{
+ SculptThreadedTaskData *data = userdata;
+ SculptSession *ss = data->ob->sculpt;
+ Sculpt *sd = data->sd;
+ const Brush *brush = data->brush;
+ const bool smooth_mask = data->smooth_mask;
+ float bstrength = data->strength;
+ float projection = data->smooth_projection;
+
+ PBVHVertexIter vd;
+
+ CLAMP(bstrength, 0.0f, 1.0f);
+
+ SculptBrushTest test;
+ SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
+ ss, &test, data->brush->falloff_shape);
+
+ const int thread_id = BLI_task_parallel_thread_id(tls);
+ const bool weighted = ss->cache->brush->flag2 & BRUSH_SMOOTH_USE_AREA_WEIGHT;
+ const bool check_fsets = ss->cache->brush->flag2 & BRUSH_SMOOTH_PRESERVE_FACE_SETS;
+
+ SculptCornerType ctype = SCULPT_CORNER_MESH | SCULPT_CORNER_SHARP;
+ if (check_fsets) {
+ ctype |= SCULPT_CORNER_FACE_SET;
+ }
+
+ if (weighted || ss->cache->brush->boundary_smooth_factor > 0.0f) {
+ BKE_pbvh_check_tri_areas(ss->pbvh, data->nodes[n]);
+ }
+
+ bool modified = false;
+ // const float bound_smooth = powf(ss->cache->brush->boundary_smooth_factor,
+ // BOUNDARY_SMOOTH_EXP);
+ // const float slide_fset = BKE_brush_fset_slide_get(ss->scene, ss->cache->brush);
+
+ SculptCustomLayer *bound_scl = data->scl2;
+
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
if (!sculpt_brush_test_sq_fn(&test, vd.co)) {
continue;
@@ -323,24 +1123,99 @@ static void do_smooth_brush_task_cb_ex(void *__restrict userdata,
vd.no,
vd.fno,
smooth_mask ? 0.0f : (vd.mask ? *vd.mask : 0.0f),
- vd.index,
+ vd.vertex,
thread_id);
if (smooth_mask) {
- float val = SCULPT_neighbor_mask_average(ss, vd.index) - *vd.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 avg[3], val[3];
- SCULPT_neighbor_coords_average_interior(ss, avg, vd.index);
- sub_v3_v3v3(val, avg, vd.co);
- madd_v3_v3v3fl(val, vd.co, val, fade);
- SCULPT_clip(sd, ss, vd.co, val);
+
+ // if (SCULPT_vertex_is_corner(ss, vd.vertex, ctype) & ~SCULPT_CORNER_FACE_SET) {
+ // continue;
+ //}
+
+ int steps = data->do_origco ? 2 : 1;
+ for (int step = 0; step < steps; step++) {
+ float *co = step ? (float *)SCULPT_vertex_origco_get(ss, vd.vertex) : vd.co;
+
+ SCULPT_neighbor_coords_average_interior(ss, avg, vd.vertex, projection, bound_scl, step);
+
+ sub_v3_v3v3(val, avg, co);
+ madd_v3_v3v3fl(val, co, val, fade);
+ SCULPT_clip(sd, ss, co, val);
+ }
}
if (vd.mvert) {
vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
}
+
+ modified = true;
+ }
+ BKE_pbvh_vertex_iter_end;
+
+ if (modified && weighted) {
+ BKE_pbvh_node_mark_update_tri_area(data->nodes[n]);
+ }
+}
+#endif
+
+static void do_smooth_brush_task_cb_ex_scl(void *__restrict userdata,
+ const int n,
+ const TaskParallelTLS *__restrict tls)
+{
+ SculptThreadedTaskData *data = userdata;
+ SculptSession *ss = data->ob->sculpt;
+ Sculpt *sd = data->sd;
+ const Brush *brush = data->brush;
+ float bstrength = data->strength;
+ float projection = data->smooth_projection;
+
+ SculptCustomLayer *scl = data->scl;
+
+ PBVHVertexIter vd;
+
+ CLAMP(bstrength, 0.0f, 1.0f);
+
+ SculptBrushTest test;
+ SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
+ ss, &test, data->brush->falloff_shape);
+
+ const int thread_id = BLI_task_parallel_thread_id(tls);
+
+ BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
+ if (!sculpt_brush_test_sq_fn(&test, vd.co)) {
+ 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);
+
+ float avg[3], val[3];
+
+ SCULPT_neighbor_coords_average_interior_velocity(ss, avg, vd.vertex, projection, scl);
+
+ sub_v3_v3v3(val, avg, vd.co);
+
+ float *vel = (float *)SCULPT_temp_cdata_get(vd.vertex, scl);
+ interp_v3_v3v3(vel, vel, val, 0.5);
+
+ madd_v3_v3v3fl(val, vd.co, vel, fade);
+
+ SCULPT_clip(sd, ss, vd.co, val);
+
+ if (vd.mvert) {
+ vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
+ }
}
BKE_pbvh_vertex_iter_end;
}
@@ -350,7 +1225,9 @@ void SCULPT_smooth(Sculpt *sd,
PBVHNode **nodes,
const int totnode,
float bstrength,
- const bool smooth_mask)
+ const bool smooth_mask,
+ float projection,
+ bool do_origco)
{
SculptSession *ss = ob->sculpt;
Brush *brush = BKE_paint_brush(&sd->paint);
@@ -361,11 +1238,29 @@ void SCULPT_smooth(Sculpt *sd,
int iteration, count;
float last;
+ if (SCULPT_stroke_is_first_brush_step(ss->cache) &&
+ ((ss->cache->brush->flag2 & BRUSH_SMOOTH_USE_AREA_WEIGHT) ||
+ ss->cache->brush->boundary_smooth_factor > 0.0f)) {
+ BKE_pbvh_update_all_tri_areas(ss->pbvh);
+ }
+
CLAMP(bstrength, 0.0f, 1.0f);
count = (int)(bstrength * max_iterations);
last = max_iterations * (bstrength - count * fract);
+ SculptCustomLayer scl;
+#if 0
+ bool have_scl = smooth_mask ? false :
+ SCULPT_temp_customlayer_ensure(
+ ss, ATTR_DOMAIN_POINT, CD_PROP_FLOAT3, "__scl_smooth_vel");
+ if (have_scl) {
+ SCULPT_temp_customlayer_get(ss, ATTR_DOMAIN_POINT, CD_PROP_FLOAT3, "__scl_smooth_vel", &scl, false);
+ }
+#else
+ bool have_scl = false;
+#endif
+
if (type == PBVH_FACES && !ss->pmap) {
BLI_assert_msg(0, "sculpt smooth: pmap missing");
return;
@@ -374,6 +1269,24 @@ void SCULPT_smooth(Sculpt *sd,
SCULPT_vertex_random_access_ensure(ss);
SCULPT_boundary_info_ensure(ob);
+ SculptCustomLayer _scl, *bound_scl = NULL;
+
+ /* create temp layer for psuedo-geodesic field */
+ if (ss->cache->brush->boundary_smooth_factor > 0.0f) {
+ float bound_smooth = powf(ss->cache->brush->boundary_smooth_factor, BOUNDARY_SMOOTH_EXP);
+
+ bound_scl = &_scl;
+ SCULPT_temp_customlayer_ensure(ss, ATTR_DOMAIN_POINT, CD_PROP_FLOAT, "__smooth_bdist", false);
+ SCULPT_temp_customlayer_get(
+ ss, ATTR_DOMAIN_POINT, CD_PROP_FLOAT, "__smooth_bdist", bound_scl, false);
+ }
+
+#ifdef PROXY_ADVANCED
+ int datamask = PV_CO | PV_NEIGHBORS | PV_NO | PV_INDEX | PV_MASK;
+ BKE_pbvh_ensure_proxyarrays(ss, ss->pbvh, nodes, totnode, datamask);
+
+ BKE_pbvh_load_proxyarrays(ss->pbvh, nodes, totnode, PV_CO | PV_NO | PV_MASK);
+#endif
for (iteration = 0; iteration <= count; iteration++) {
const float strength = (iteration != count) ? 1.0f : last;
@@ -384,24 +1297,45 @@ void SCULPT_smooth(Sculpt *sd,
.nodes = nodes,
.smooth_mask = smooth_mask,
.strength = strength,
+ .smooth_projection = projection,
+ .scl = have_scl ? &scl : NULL,
+ .scl2 = bound_scl,
+ .do_origco = SCULPT_stroke_needs_original(ss->cache->brush),
};
TaskParallelSettings settings;
BKE_pbvh_parallel_range_settings(&settings, true, totnode);
- BLI_task_parallel_range(0, totnode, &data, do_smooth_brush_task_cb_ex, &settings);
+ if (0) { // have_scl) {
+ BLI_task_parallel_range(0, totnode, &data, do_smooth_brush_task_cb_ex_scl, &settings);
+ }
+ else {
+ BLI_task_parallel_range(0, totnode, &data, do_smooth_brush_task_cb_ex, &settings);
+ }
+
+#ifdef PROXY_ADVANCED
+ BKE_pbvh_gather_proxyarray(ss->pbvh, nodes, totnode);
+#endif
}
}
-void SCULPT_do_smooth_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
+void SCULPT_do_smooth_brush(
+ Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode, float projection)
{
SculptSession *ss = ob->sculpt;
+
+ if (SCULPT_stroke_is_first_brush_step(ss->cache) &&
+ ((ss->cache->brush->flag2 & BRUSH_SMOOTH_USE_AREA_WEIGHT) ||
+ ss->cache->brush->boundary_smooth_factor > 0.0f)) {
+ BKE_pbvh_update_all_tri_areas(ss->pbvh);
+ }
+
if (ss->cache->bstrength <= 0.0f) {
/* Invert mode, intensify details. */
SCULPT_enhance_details_brush(sd, ob, nodes, totnode);
}
else {
/* Regular mode, smooth. */
- SCULPT_smooth(sd, ob, nodes, totnode, ss->cache->bstrength, false);
+ SCULPT_smooth(sd, ob, nodes, totnode, ss->cache->bstrength, false, projection, false);
}
}
@@ -411,42 +1345,49 @@ void SCULPT_do_smooth_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnod
void SCULPT_surface_smooth_laplacian_step(SculptSession *ss,
float *disp,
const float co[3],
- float (*laplacian_disp)[3],
- const int v_index,
+ SculptCustomLayer *scl,
+ const SculptVertRef v_index,
const float origco[3],
- const float alpha)
+ const float alpha,
+ const float projection,
+ bool check_fsets)
{
float laplacian_smooth_co[3];
float weigthed_o[3], weigthed_q[3], d[3];
- SCULPT_neighbor_coords_average(ss, laplacian_smooth_co, v_index);
+ SCULPT_neighbor_coords_average(ss, laplacian_smooth_co, v_index, projection, check_fsets);
+
+ int index = BKE_pbvh_vertex_index_to_table(ss->pbvh, v_index);
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);
+ sub_v3_v3v3((float *)SCULPT_temp_cdata_get(v_index, scl), 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 int v_index,
+ SculptCustomLayer *scl,
+ const SculptVertRef v_index,
const float beta,
const float fade)
{
float b_avg[3] = {0.0f, 0.0f, 0.0f};
float b_current_vertex[3];
int total = 0;
+ int index = BKE_pbvh_vertex_index_to_table(ss->pbvh, v_index);
+
SculptVertexNeighborIter ni;
SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, v_index, ni) {
- add_v3_v3(b_avg, laplacian_disp[ni.index]);
+ add_v3_v3(b_avg, (float *)SCULPT_temp_cdata_get(ni.vertex, scl));
total++;
}
SCULPT_VERTEX_NEIGHBORS_ITER_END(ni);
+
if (total > 0) {
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, (float *)SCULPT_temp_cdata_get(v_index, scl), beta);
mul_v3_fl(b_current_vertex, clamp_f(fade, 0.0f, 1.0f));
sub_v3_v3(co, b_current_vertex);
}
@@ -469,10 +1410,20 @@ static void SCULPT_do_surface_smooth_brush_laplacian_task_cb_ex(
ss, &test, data->brush->falloff_shape);
const int thread_id = BLI_task_parallel_thread_id(tls);
- SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]);
+ const bool weighted = ss->cache->brush->flag2 & BRUSH_SMOOTH_USE_AREA_WEIGHT;
+
+ if (weighted) {
+ BKE_pbvh_check_tri_areas(ss->pbvh, data->nodes[n]);
+ }
+
+ bool modified = false;
+
+ bool check_fsets = ss->cache->brush->flag2 & BRUSH_SMOOTH_PRESERVE_FACE_SETS;
+ SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COORDS);
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;
}
@@ -483,18 +1434,31 @@ static void SCULPT_do_surface_smooth_brush_laplacian_task_cb_ex(
vd.no,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
- vd.index,
+ vd.vertex,
thread_id);
float disp[3];
- SCULPT_surface_smooth_laplacian_step(
- ss, disp, vd.co, ss->cache->surface_smooth_laplacian_disp, vd.index, orig_data.co, alpha);
+ SCULPT_surface_smooth_laplacian_step(ss,
+ disp,
+ vd.co,
+ data->scl,
+ vd.vertex,
+ orig_data.co,
+ alpha,
+ data->smooth_projection,
+ check_fsets);
madd_v3_v3fl(vd.co, disp, clamp_f(fade, 0.0f, 1.0f));
if (vd.mvert) {
vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
}
+
+ modified = true;
}
BKE_pbvh_vertex_iter_end;
+
+ if (modified && weighted) {
+ BKE_pbvh_node_mark_update_tri_area(data->nodes[n]);
+ }
}
static void SCULPT_do_surface_smooth_brush_displace_task_cb_ex(
@@ -524,10 +1488,9 @@ static void SCULPT_do_surface_smooth_brush_displace_task_cb_ex(
vd.no,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
- vd.index,
+ vd.vertex,
thread_id);
- SCULPT_surface_smooth_displace_step(
- ss, vd.co, ss->cache->surface_smooth_laplacian_disp, vd.index, beta, fade);
+ SCULPT_surface_smooth_displace_step(ss, vd.co, data->scl, vd.vertex, beta, fade);
}
BKE_pbvh_vertex_iter_end;
}
@@ -537,19 +1500,31 @@ void SCULPT_do_surface_smooth_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, in
Brush *brush = BKE_paint_brush(&sd->paint);
SculptSession *ss = ob->sculpt;
+ SculptCustomLayer scl;
+
+ SCULPT_temp_customlayer_ensure(
+ ss, ATTR_DOMAIN_POINT, CD_PROP_FLOAT3, "__dyntopo_lapsmooth", false);
+ SCULPT_temp_customlayer_get(
+ ss, ATTR_DOMAIN_POINT, CD_PROP_FLOAT3, "__dyntopo_lapsmooth", &scl, false);
+
+ 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);
+ }
+
if (SCULPT_stroke_is_first_brush_step(ss->cache)) {
- BLI_assert(ss->cache->surface_smooth_laplacian_disp == NULL);
- ss->cache->surface_smooth_laplacian_disp = MEM_callocN(
- sizeof(float[3]) * SCULPT_vertex_count_get(ss), "HC smooth laplacian b");
+ // BLI_assert(ss->cache->surface_smooth_laplacian_disp == NULL);
+ // ss->cache->surface_smooth_laplacian_disp = MEM_callocN(
+ // sizeof(float[3]) * SCULPT_vertex_count_get(ss), "HC smooth laplacian b");
}
/* Threaded loop over nodes. */
- SculptThreadedTaskData data = {
- .sd = sd,
- .ob = ob,
- .brush = brush,
- .nodes = nodes,
- };
+ SculptThreadedTaskData data = {.sd = sd,
+ .ob = ob,
+ .brush = brush,
+ .nodes = nodes,
+ .smooth_projection = brush->autosmooth_projection,
+ .scl = &scl};
TaskParallelSettings settings;
BKE_pbvh_parallel_range_settings(&settings, true, totnode);
@@ -588,7 +1563,7 @@ static void SCULPT_do_directional_smooth_task_cb_ex(void *__restrict userdata,
vd.no,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
- vd.index,
+ vd.vertex,
thread_id);
float stroke_disp[3];
@@ -599,9 +1574,9 @@ static void SCULPT_do_directional_smooth_task_cb_ex(void *__restrict userdata,
int neighbor_count = 0;
SculptVertexNeighborIter ni;
- SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.index, ni) {
+ SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.vertex, ni) {
float vertex_neighbor_disp[3];
- const float *neighbor_co = SCULPT_vertex_co_get(ss, ni.index);
+ const float *neighbor_co = SCULPT_vertex_co_get(ss, ni.vertex);
sub_v3_v3v3(vertex_neighbor_disp, neighbor_co, vd.co);
normalize_v3(vertex_neighbor_disp);
if (fabsf(dot_v3v3(stroke_disp, vertex_neighbor_disp)) > 0.6f) {
@@ -651,8 +1626,8 @@ void SCULPT_do_directional_smooth_brush(Sculpt *sd, Object *ob, PBVHNode **nodes
}
static void SCULPT_do_uniform_weigths_smooth_task_cb_ex(void *__restrict userdata,
- const int n,
- const TaskParallelTLS *__restrict tls)
+ const int n,
+ const TaskParallelTLS *__restrict tls)
{
SculptThreadedTaskData *data = userdata;
SculptSession *ss = data->ob->sculpt;
@@ -677,18 +1652,16 @@ static void SCULPT_do_uniform_weigths_smooth_task_cb_ex(void *__restrict userdat
vd.no,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
- vd.index,
+ vd.vertex,
thread_id);
-
-
-
float len_accum = 0;
int tot_neighbors = 0;
SculptVertexNeighborIter ni;
- SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.index, ni) {
- len_accum += len_v3v3(SCULPT_vertex_co_get(ss, vd.index), SCULPT_vertex_co_get(ss, ni.index));
+ SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.vertex, ni) {
+ len_accum += len_v3v3(SCULPT_vertex_co_get(ss, vd.vertex),
+ SCULPT_vertex_co_get(ss, ni.vertex));
tot_neighbors++;
}
SCULPT_VERTEX_NEIGHBORS_ITER_END(ni);
@@ -700,16 +1673,16 @@ static void SCULPT_do_uniform_weigths_smooth_task_cb_ex(void *__restrict userdat
const float len_avg = bstrength * len_accum / tot_neighbors;
-
float co_accum[3] = {0.0f};
- SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.index, ni) {
+ SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.vertex, ni) {
const float neighbor_co[3];
const float neighbor_disp[3];
- sub_v3_v3v3(neighbor_disp, SCULPT_vertex_co_get(ss, ni.index), SCULPT_vertex_co_get(ss, vd.index));
+ sub_v3_v3v3(
+ neighbor_disp, SCULPT_vertex_co_get(ss, ni.vertex), SCULPT_vertex_co_get(ss, vd.vertex));
normalize_v3(neighbor_disp);
mul_v3_fl(neighbor_disp, len_avg);
- add_v3_v3v3(neighbor_co, SCULPT_vertex_co_get(ss, vd.index), neighbor_disp);
+ add_v3_v3v3(neighbor_co, SCULPT_vertex_co_get(ss, vd.vertex), neighbor_disp);
add_v3_v3(co_accum, neighbor_co);
}
SCULPT_VERTEX_NEIGHBORS_ITER_END(ni);
@@ -729,8 +1702,6 @@ static void SCULPT_do_uniform_weigths_smooth_task_cb_ex(void *__restrict userdat
}
}
-
-
void SCULPT_do_uniform_weights_smooth_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
{
Brush *brush = BKE_paint_brush(&sd->paint);
@@ -746,6 +1717,194 @@ void SCULPT_do_uniform_weights_smooth_brush(Sculpt *sd, Object *ob, PBVHNode **n
TaskParallelSettings settings;
BKE_pbvh_parallel_range_settings(&settings, true, totnode);
for (int i = 0; i < brush->surface_smooth_iterations; i++) {
- BLI_task_parallel_range(0, totnode, &data, SCULPT_do_uniform_weigths_smooth_task_cb_ex, &settings);
+ BLI_task_parallel_range(
+ 0, totnode, &data, SCULPT_do_uniform_weigths_smooth_task_cb_ex, &settings);
+ }
+}
+
+static void do_smooth_vcol_boundary_brush_task_cb_ex(void *__restrict userdata,
+ const int n,
+ const TaskParallelTLS *__restrict tls)
+{
+ SculptThreadedTaskData *data = userdata;
+ SculptSession *ss = data->ob->sculpt;
+ Sculpt *sd = data->sd;
+ const Brush *brush = data->brush;
+ const bool smooth_mask = data->smooth_mask;
+ float bstrength = data->strength;
+
+ PBVHVertexIter vd;
+
+ CLAMP(bstrength, 0.0f, 1.0f);
+
+ SculptBrushTest test;
+ SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
+ ss, &test, data->brush->falloff_shape);
+
+ const int thread_id = BLI_task_parallel_thread_id(tls);
+
+ float avg[4] = {0.0f, 0.0f, 0.0f, 0.0f};
+ float tot = 0.0f;
+ BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
+ if (!vd.col) {
+ continue;
+ }
+
+ if (sculpt_brush_test_sq_fn(&test, vd.co)) {
+ 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);
+
+ madd_v3_v3fl(avg, vd.col, fade);
+ tot += fade;
+ }
+ }
+ BKE_pbvh_vertex_iter_end;
+
+ if (tot == 0.0f) {
+ return;
+ }
+ tot = 1.0f / tot;
+
+ mul_v3_fl(avg, tot);
+
+ float exp = brush->vcol_boundary_exponent;
+ // detect bad value
+
+ if (exp == 0.0f) {
+ exp = 1.0f;
}
-} \ No newline at end of file
+
+ BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
+ if (sculpt_brush_test_sq_fn(&test, vd.co)) {
+ 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);
+ if (!vd.col) {
+ continue;
+ }
+
+ float avg2[3], avg3[3], val[3];
+ float tot2 = 0.0f, tot4 = 0.0f;
+
+ copy_v4_v4(avg, vd.col);
+
+ zero_v3(avg2);
+ zero_v3(avg3);
+
+ madd_v3_v3fl(avg2, vd.co, 0.5f);
+ tot2 += 0.5f;
+
+ SculptVertexNeighborIter ni;
+ SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.vertex, ni) {
+ const float *col = SCULPT_vertex_color_get(ss, ni.vertex);
+ const float *co = SCULPT_vertex_co_get(ss, ni.vertex);
+
+ // simple color metric. TODO: plug in appropriate color space code?
+ float dv[4];
+ sub_v4_v4v4(dv, col, avg);
+ float w = (fabs(dv[0]) + fabs(dv[1]) + fabs(dv[2]) + fabs(dv[3])) / 4.0;
+
+ w = powf(w, exp);
+
+ madd_v3_v3fl(avg3, co, 1.0f);
+ tot4 += 1.0f;
+
+ madd_v3_v3fl(avg2, co, w);
+ tot2 += w;
+ }
+ SCULPT_VERTEX_NEIGHBORS_ITER_END(ni);
+
+ if (tot2 == 0.0f) {
+ continue;
+ }
+
+ if (tot4 > 0.0f) {
+ mul_v3_fl(avg3, 1.0f / tot4);
+ }
+
+ /* try to avoid perfectly colinear triangles, and the normal discontinuities they create,
+ by blending slightly with unweighted smoothed position */
+ mul_v3_fl(avg2, 1.0f / tot2);
+ interp_v3_v3v3(avg2, avg2, avg3, 0.025);
+
+ sub_v3_v3v3(val, avg2, vd.co);
+ madd_v3_v3v3fl(val, vd.co, val, fade);
+ SCULPT_clip(sd, ss, vd.co, val);
+
+ if (vd.mvert) {
+ vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
+ }
+ }
+ }
+ BKE_pbvh_vertex_iter_end;
+}
+
+void SCULPT_smooth_vcol_boundary(
+ Sculpt *sd, Object *ob, PBVHNode **nodes, const int totnode, float bstrength)
+{
+ SculptSession *ss = ob->sculpt;
+
+ Brush *brush = BKE_paint_brush(&sd->paint);
+
+ const int max_iterations = 4;
+ const float fract = 1.0f / max_iterations;
+ PBVHType type = BKE_pbvh_type(ss->pbvh);
+ int iteration, count;
+ float last;
+
+ CLAMP(bstrength, 0.0f, 1.0f);
+
+ count = (int)(bstrength * max_iterations);
+ last = max_iterations * (bstrength - count * fract);
+
+ if (type == PBVH_FACES && !ss->pmap) {
+ BLI_assert(!"sculpt smooth: pmap missing");
+ return;
+ }
+
+ SCULPT_vertex_random_access_ensure(ss);
+ SCULPT_boundary_info_ensure(ob);
+
+#ifdef PROXY_ADVANCED
+ int datamask = PV_CO | PV_NEIGHBORS | PV_NO | PV_INDEX | PV_MASK;
+ BKE_pbvh_ensure_proxyarrays(ss, ss->pbvh, nodes, totnode, datamask);
+
+ BKE_pbvh_load_proxyarrays(ss->pbvh, nodes, totnode, PV_CO | PV_NO | PV_MASK);
+#endif
+ for (iteration = 0; iteration <= count; iteration++) {
+ const float strength = (iteration != count) ? 1.0f : last;
+
+ SculptThreadedTaskData data = {
+ .sd = sd,
+ .ob = ob,
+ .brush = brush,
+ .nodes = nodes,
+ .smooth_mask = false,
+ .strength = strength,
+ };
+
+ TaskParallelSettings settings;
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
+ BLI_task_parallel_range(
+ 0, totnode, &data, do_smooth_vcol_boundary_brush_task_cb_ex, &settings);
+
+#ifdef PROXY_ADVANCED
+ BKE_pbvh_gather_proxyarray(ss->pbvh, nodes, totnode);
+#endif
+ }
+}
diff --git a/source/blender/editors/sculpt_paint/sculpt_symmetrize.c b/source/blender/editors/sculpt_paint/sculpt_symmetrize.c
index ea0178e0edb..a4581c76872 100644
--- a/source/blender/editors/sculpt_paint/sculpt_symmetrize.c
+++ b/source/blender/editors/sculpt_paint/sculpt_symmetrize.c
@@ -87,14 +87,13 @@ static int mirrtopo_vert_sort(const void *v1, const void *v2)
}
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;
+ /* Nothing to do. */
+ return;
}
MEdge *medge = NULL, *med;
@@ -115,14 +114,14 @@ void SCULPT_symmetrize_map_ensure(Object *ob)
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;
+ 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]++;
- }
+ 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);
@@ -134,12 +133,12 @@ void SCULPT_symmetrize_map_ensure(Object *ob)
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]);
- }
+ 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 */
@@ -185,22 +184,22 @@ void SCULPT_symmetrize_map_ensure(Object *ob)
/* 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;
+ 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;
@@ -211,11 +210,9 @@ void SCULPT_symmetrize_map_ensure(Object *ob)
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)
+ const int n,
+ const TaskParallelTLS *__restrict tls)
{
SculptThreadedTaskData *data = userdata;
SculptSession *ss = data->ob->sculpt;
@@ -234,13 +231,15 @@ static void do_shape_symmetrize_brush_task_cb(void *__restrict userdata,
}
const int symmetrical_index = ss->vertex_info.symmetrize_map[vd.index];
+ const SculptVertRef symmetrical_vertex = BKE_pbvh_table_index_to_vertex(ss->pbvh,
+ symmetrical_index);
if (symmetrical_index == -1) {
- continue;
+ continue;
}
float symm_co[3];
- copy_v3_v3(symm_co, SCULPT_vertex_co_get(ss, symmetrical_index));
+ copy_v3_v3(symm_co, SCULPT_vertex_co_get(ss, symmetrical_vertex));
symm_co[0] *= -1;
float new_co[3];
@@ -253,11 +252,9 @@ static void do_shape_symmetrize_brush_task_cb(void *__restrict userdata,
vd.no,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
- vd.index,
+ vd.vertex,
thread_id);
-
-
float disp[3];
sub_v3_v3v3(disp, new_co, vd.co);
madd_v3_v3v3fl(vd.co, vd.co, disp, fade);
@@ -278,13 +275,12 @@ void SCULPT_do_symmetrize_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int to
SculptSession *ss = ob->sculpt;
Brush *brush = BKE_paint_brush(&sd->paint);
-
if (BKE_pbvh_type(ss->pbvh) != PBVH_FACES) {
- return;
+ return;
}
if (!SCULPT_stroke_is_main_symmetry_pass(ss->cache)) {
- return;
+ return;
}
SCULPT_symmetrize_map_ensure(ob);
@@ -299,4 +295,4 @@ void SCULPT_do_symmetrize_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int to
TaskParallelSettings settings;
BKE_pbvh_parallel_range_settings(&settings, true, totnode);
BLI_task_parallel_range(0, totnode, &data, do_shape_symmetrize_brush_task_cb, &settings);
-} \ No newline at end of file
+}
diff --git a/source/blender/editors/sculpt_paint/sculpt_transform.c b/source/blender/editors/sculpt_paint/sculpt_transform.c
index efe3b8acafe..74d4bd14528 100644
--- a/source/blender/editors/sculpt_paint/sculpt_transform.c
+++ b/source/blender/editors/sculpt_paint/sculpt_transform.c
@@ -184,13 +184,13 @@ static void sculpt_transform_task_cb(void *__restrict userdata,
PBVHNode *node = data->nodes[i];
SculptOrigVertData orig_data;
- SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[i]);
+ SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[i], SCULPT_UNDO_COORDS);
PBVHVertexIter vd;
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);
+ 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;
@@ -263,7 +263,7 @@ static void sculpt_elastic_transform_task_cb(void *__restrict userdata,
float(*proxy)[3] = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[i])->co;
SculptOrigVertData orig_data;
- SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[i]);
+ SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[i], SCULPT_UNDO_COORDS);
KelvinletParams params;
/* TODO(pablodp606): These parameters can be exposed if needed as transform strength and volume
@@ -279,7 +279,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);
diff --git a/source/blender/editors/sculpt_paint/sculpt_undo.c b/source/blender/editors/sculpt_paint/sculpt_undo.c
index 501a1e53276..a5fa698a128 100644
--- a/source/blender/editors/sculpt_paint/sculpt_undo.c
+++ b/source/blender/editors/sculpt_paint/sculpt_undo.c
@@ -23,6 +23,7 @@
*/
#include <stddef.h>
+#include <string.h>
#include "MEM_guardedalloc.h"
@@ -70,6 +71,8 @@
#include "bmesh.h"
#include "sculpt_intern.h"
+#define WHEN_GLOBAL_UNDO_WORKS
+
/* Implementation of undo system for objects in sculpt mode.
*
* Each undo step in sculpt mode consists of list of nodes, each node contains:
@@ -113,10 +116,21 @@ typedef struct UndoSculpt {
ListBase nodes;
size_t undo_size;
+ BMLog *bm_restore;
} UndoSculpt;
-static UndoSculpt *sculpt_undo_get_nodes(void);
+typedef struct SculptUndoStep {
+ UndoStep step;
+ /* NOTE: will split out into list for multi-object-sculpt-mode. */
+ UndoSculpt data;
+ int id;
+} SculptUndoStep;
+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);
+void sculpt_undo_push_begin_ex(Object *ob, const char *name, bool no_first_entry_check);
static void update_cb(PBVHNode *node, void *rebuild)
{
BKE_pbvh_node_mark_update(node);
@@ -133,6 +147,8 @@ struct PartialUpdateData {
char *modified_grids;
};
+static UndoSculpt *sculpt_undosys_step_get_nodes(UndoStep *us_p);
+
/**
* A version of #update_cb that tests for 'ME_VERT_PBVH_UPDATE'
*/
@@ -170,6 +186,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])
{
@@ -187,7 +205,7 @@ static bool sculpt_undo_restore_coords(bContext *C, Depsgraph *depsgraph, Sculpt
SculptSession *ss = ob->sculpt;
SubdivCCG *subdiv_ccg = ss->subdiv_ccg;
MVert *mvert;
- int *index;
+ SculptVertRef *index;
if (unode->maxvert) {
/* Regular mesh restore. */
@@ -221,18 +239,18 @@ static bool sculpt_undo_restore_coords(bContext *C, Depsgraph *depsgraph, Sculpt
if (unode->orig_co) {
if (ss->deform_modifiers_active) {
for (int i = 0; i < unode->totvert; i++) {
- sculpt_undo_restore_deformed(ss, unode, i, index[i], vertCos[index[i]]);
+ sculpt_undo_restore_deformed(ss, unode, i, index[i].i, vertCos[index[i].i]);
}
}
else {
for (int i = 0; i < unode->totvert; i++) {
- swap_v3_v3(vertCos[index[i]], unode->orig_co[i]);
+ swap_v3_v3(vertCos[index[i].i], unode->orig_co[i]);
}
}
}
else {
for (int i = 0; i < unode->totvert; i++) {
- swap_v3_v3(vertCos[index[i]], unode->co[i]);
+ swap_v3_v3(vertCos[index[i].i], unode->co[i]);
}
}
@@ -249,21 +267,21 @@ static bool sculpt_undo_restore_coords(bContext *C, Depsgraph *depsgraph, Sculpt
if (unode->orig_co) {
if (ss->deform_modifiers_active) {
for (int i = 0; i < unode->totvert; i++) {
- sculpt_undo_restore_deformed(ss, unode, i, index[i], mvert[index[i]].co);
- mvert[index[i]].flag |= ME_VERT_PBVH_UPDATE;
+ sculpt_undo_restore_deformed(ss, unode, i, index[i].i, mvert[index[i].i].co);
+ mvert[index[i].i].flag |= ME_VERT_PBVH_UPDATE;
}
}
else {
for (int i = 0; i < unode->totvert; i++) {
- swap_v3_v3(mvert[index[i]].co, unode->orig_co[i]);
- mvert[index[i]].flag |= ME_VERT_PBVH_UPDATE;
+ swap_v3_v3(mvert[index[i].i].co, unode->orig_co[i]);
+ mvert[index[i].i].flag |= ME_VERT_PBVH_UPDATE;
}
}
}
else {
for (int i = 0; i < unode->totvert; i++) {
- swap_v3_v3(mvert[index[i]].co, unode->co[i]);
- mvert[index[i]].flag |= ME_VERT_PBVH_UPDATE;
+ swap_v3_v3(mvert[index[i].i].co, unode->co[i]);
+ mvert[index[i].i].flag |= ME_VERT_PBVH_UPDATE;
}
}
}
@@ -303,7 +321,7 @@ static bool sculpt_undo_restore_hidden(bContext *C, SculptUndoNode *unode)
MVert *mvert = ss->mvert;
for (int i = 0; i < unode->totvert; i++) {
- MVert *v = &mvert[unode->index[i]];
+ MVert *v = &mvert[unode->index[i].i];
if ((BLI_BITMAP_TEST(unode->vert_hidden, i) != 0) != ((v->flag & ME_HIDE) != 0)) {
BLI_BITMAP_FLIP(unode->vert_hidden, i);
v->flag ^= ME_HIDE;
@@ -330,13 +348,13 @@ static bool sculpt_undo_restore_color(bContext *C, SculptUndoNode *unode)
if (unode->maxvert) {
/* regular mesh restore */
- int *index = unode->index;
+ SculptVertRef *index = unode->index;
MVert *mvert = ss->mvert;
MPropCol *vcol = ss->vcol;
for (int i = 0; i < unode->totvert; i++) {
- copy_v4_v4(vcol[index[i]].color, unode->col[i]);
- mvert[index[i]].flag |= ME_VERT_PBVH_UPDATE;
+ swap_v4_v4(vcol[index[i].i].color, unode->col[i]);
+ mvert[index[i].i].flag |= ME_VERT_PBVH_UPDATE;
}
}
return true;
@@ -350,7 +368,7 @@ static bool sculpt_undo_restore_mask(bContext *C, SculptUndoNode *unode)
SubdivCCG *subdiv_ccg = ss->subdiv_ccg;
MVert *mvert;
float *vmask;
- int *index;
+ SculptVertRef *index;
if (unode->maxvert) {
/* Regular mesh restore. */
@@ -360,9 +378,9 @@ static bool sculpt_undo_restore_mask(bContext *C, SculptUndoNode *unode)
vmask = ss->vmask;
for (int i = 0; i < unode->totvert; i++) {
- if (vmask[index[i]] != unode->mask[i]) {
- SWAP(float, vmask[index[i]], unode->mask[i]);
- mvert[index[i]].flag |= ME_VERT_PBVH_UPDATE;
+ if (vmask[index[i].i] != unode->mask[i]) {
+ SWAP(float, vmask[index[i].i], unode->mask[i]);
+ mvert[index[i].i].flag |= ME_VERT_PBVH_UPDATE;
}
}
}
@@ -397,44 +415,291 @@ static bool sculpt_undo_restore_face_sets(bContext *C, SculptUndoNode *unode)
Mesh *me = BKE_object_get_original_mesh(ob);
int *face_sets = CustomData_get_layer(&me->pdata, CD_SCULPT_FACE_SETS);
for (int i = 0; i < me->totpoly; i++) {
- face_sets[i] = unode->face_sets[i];
+ SWAP(int, face_sets[i], unode->face_sets[i]);
}
return false;
}
-static void sculpt_undo_bmesh_restore_generic_task_cb(
- void *__restrict userdata, const int n, const TaskParallelTLS *__restrict UNUSED(tls))
+extern const char dyntopop_node_idx_layer_id[];
+
+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_dyn_vert;
+ bool regen_all_unique_verts;
+ bool is_redo;
+} BmeshUndoData;
+
+static void bmesh_undo_on_vert_kill(BMVert *v, void *userdata)
{
- PBVHNode **nodes = userdata;
+ BmeshUndoData *data = (BmeshUndoData *)userdata;
+ // data->do_full_recalc = true;
+
+ if (BM_ELEM_CD_GET_INT(v, data->cd_vert_node_offset) < 0) {
+ // something went wrong
+ printf("pbvh bmesh undo error\n");
+ data->do_full_recalc = true;
+ 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);
+
+ MDynTopoVert *mv = BKE_PBVH_DYNVERT(data->cd_dyn_vert, v);
+ mv->flag |= DYNVERT_NEED_DISK_SORT | DYNVERT_NEED_VALENCE | DYNVERT_NEED_TRIANGULATE |
+ DYNVERT_NEED_BOUNDARY;
+}
+
+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 {
+ MDynTopoVert *mv = BKE_PBVH_DYNVERT(data->cd_dyn_vert, l->v);
+ mv->flag |= DYNVERT_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;
+
+ MDynTopoVert *mv1 = BKE_PBVH_DYNVERT(data->cd_dyn_vert, e->v1);
+ MDynTopoVert *mv2 = BKE_PBVH_DYNVERT(data->cd_dyn_vert, e->v2);
+
+ mv1->flag |= DYNVERT_NEED_BOUNDARY | DYNVERT_NEED_TRIANGULATE | DYNVERT_NEED_DISK_SORT |
+ DYNVERT_NEED_VALENCE;
+ mv2->flag |= DYNVERT_NEED_BOUNDARY | DYNVERT_NEED_TRIANGULATE | DYNVERT_NEED_DISK_SORT |
+ DYNVERT_NEED_VALENCE;
+}
+
+static void bmesh_undo_on_edge_add(BMEdge *e, void *userdata)
+{
+ BmeshUndoData *data = (BmeshUndoData *)userdata;
+
+ MDynTopoVert *mv1 = BKE_PBVH_DYNVERT(data->cd_dyn_vert, e->v1);
+ MDynTopoVert *mv2 = BKE_PBVH_DYNVERT(data->cd_dyn_vert, e->v2);
- BKE_pbvh_node_mark_redraw(nodes[n]);
+ mv1->flag |= DYNVERT_NEED_BOUNDARY | DYNVERT_NEED_TRIANGULATE | DYNVERT_NEED_DISK_SORT |
+ DYNVERT_NEED_VALENCE;
+ mv2->flag |= DYNVERT_NEED_BOUNDARY | DYNVERT_NEED_TRIANGULATE | DYNVERT_NEED_DISK_SORT |
+ DYNVERT_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 {
+ BM_ELEM_CD_SET_INT(v, data->cd_vert_node_offset, -1);
+ 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();
+
+ // 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_dyn_vert,
+ 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,
+ NULL,
+ (void *)&data};
+
+ SCULPT_dyntopo_node_layers_update_offsets(ss);
+
+ 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, dyntopop_node_idx_layer_id);
unode->applied = false;
}
else {
- BM_log_redo(ss->bm, ss->bm_log);
+ BM_log_redo(ss->bm, ss->bm_log, &callbacks, dyntopop_node_idx_layer_id);
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, NULL, NULL, &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);
+ }
+
+ pbvh_bmesh_check_nodes(ss->pbvh);
}
else {
SCULPT_pbvh_clear(ob);
@@ -442,63 +707,154 @@ static void sculpt_undo_bmesh_restore_generic(SculptUndoNode *unode, Object *ob,
}
/* 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 = ob->data;
SCULPT_pbvh_clear(ob);
+ ss->active_face_index.i = ss->active_vertex_index.i = 0;
/* Create empty BMesh and enable logging. */
+ ss->bm = SCULPT_dyntopo_empty_bmesh();
+#if 0
ss->bm = BM_mesh_create(&bm_mesh_allocsize_default,
- &((struct BMeshCreateParams){
- .use_toolflags = false,
- }));
- BM_data_layer_add(ss->bm, &ss->bm->vdata, CD_PAINT_MASK);
+ &((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
+
+ BM_mesh_bm_from_me(NULL,
+ ss->bm,
+ me,
+ (&(struct BMeshFromMeshParams){
+ .calc_face_normal = true,
+ .use_shapekey = true,
+ .active_shapekey = ob->shapenr,
+ }));
+
SCULPT_dyntopo_node_layers_add(ss);
- me->flag |= ME_SCULPT_DYNAMIC_TOPOLOGY;
+ SCULPT_dyntopo_node_layers_update_offsets(ss);
+
+ if (ss->pbvh && ss->bm) {
+ SCULT_dyntopo_flag_all_disk_sort(ss);
+ }
- /* Restore the BMLog using saved entries. */
- ss->bm_log = BM_log_from_existing_entries_create(ss->bm, unode->bm_entry);
+ if (!ss->bm_log) {
+ /* Restore the BMLog using saved entries. */
+ ss->bm_log = BM_log_from_existing_entries_create(ss->bm, 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);
+ }
+
+ BM_log_set_cd_offsets(ss->bm_log, ss->cd_dyn_vert);
}
-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, NULL, dyntopop_node_idx_layer_id);
+ }
+ else {
+ BM_log_undo(ss->bm, ss->bm_log, NULL, dyntopop_node_idx_layer_id);
+ }
+#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);
+ /*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
- /* Restore the mesh from the last log entry. */
- BM_log_undo(ss->bm, ss->bm_log);
+ if (dir == -1) {
+ BM_log_undo(ss->bm, ss->bm_log, NULL, dyntopop_node_idx_layer_id);
+ }
+ else {
+ BM_log_redo(ss->bm, ss->bm_log, NULL, dyntopop_node_idx_layer_id);
+ }
+#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, NULL);
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)
@@ -585,28 +941,109 @@ 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, unode->bm_entry);
+ }
+#endif
+
+ if (ss->bm_log && ss->bm &&
+ !ELEM(unode->type, SCULPT_UNDO_DYNTOPO_BEGIN, SCULPT_UNDO_DYNTOPO_END)) {
+ SCULPT_dyntopo_node_layers_update_offsets(ss);
+ BM_log_set_cd_offsets(ss->bm_log, ss->cd_dyn_vert);
+
+#if 0
+ if (ss->active_face_index.i && ss->active_face_index.i != -1LL) {
+ ss->active_face_index.i = (intptr_t)BM_log_face_id_get(ss->bm_log,
+ (BMFace *)ss->active_face_index.i);
+ }
+ else {
+ ss->active_face_index.i = -1;
+ }
+
+ if (ss->active_vertex_index.i && ss->active_vertex_index.i != -1LL) {
+ ss->active_vertex_index.i = (intptr_t)BM_log_vert_id_get(
+ ss->bm_log, (BMVert *)ss->active_vertex_index.i);
+ }
+ else {
+ ss->active_vertex_index.i = -1;
+ }
+#endif
+ ss->active_face_index.i = ss->active_vertex_index.i = 0;
+ }
+ else {
+ ss->active_face_index.i = ss->active_vertex_index.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_index.i = ss->active_vertex_index.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_index.i = ss->active_vertex_index.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_index.i != -1) {
+ BMFace *f = BM_log_id_face_get(ss->bm_log, (uint)ss->active_face_index.i);
+ if (f && f->head.htype == BM_FACE) {
+ ss->active_face_index.i = (intptr_t)f;
+ }
+ else {
+ ss->active_face_index.i = 0LL;
+ }
+ }
+ else {
+ ss->active_face_index.i = 0LL;
+ }
+
+ if (ss->active_vertex_index.i != -1) {
+ BMVert *v = BM_log_id_vert_get(ss->bm_log, (uint)ss->active_vertex_index.i);
+
+ if (v && v->head.htype == BM_VERT) {
+ ss->active_vertex_index.i = (intptr_t)v;
+ }
+ else {
+ ss->active_vertex_index.i = 0LL;
+ }
+ }
+ else {
+ ss->active_vertex_index.i = 0LL;
+ }
+ }
+ else {
+ ss->active_face_index.i = ss->active_vertex_index.i = 0;
+ }
+
+ return ret;
}
/* Geometry updates (such as Apply Base, for example) will re-evaluate the object and refine its
@@ -635,7 +1072,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);
@@ -647,8 +1084,38 @@ static void sculpt_undo_restore_list(bContext *C, Depsgraph *depsgraph, ListBase
bool update = false, rebuild = false, update_mask = false, update_visibility = false;
bool need_mask = false;
bool need_refine_subdiv = false;
+ bool did_first_hack = false;
for (unode = lb->first; unode; unode = unode->next) {
+#if 0
+ if (unode->bm_entry && !ss->bm) {
+ // file loading breaks undo because the stack isn't initialized
+ // detect that case and try to fix it
+
+ did_first_hack = true;
+
+ ss->active_face_index.i = ss->active_vertex_index.i = 0;
+ SCULPT_dynamic_topology_enable_ex(CTX_data_main(C), depsgraph, scene, ob);
+
+ // see if we have a saved log in the entry
+ BMLog *log = BM_log_unfreeze(ss->bm, unode->bm_entry);
+
+ if (log) {
+ if (ss->bm_log) {
+ BM_log_free(ss->bm_log, false);
+ }
+
+ ss->bm_log = log;
+
+ SCULPT_dyntopo_node_layers_update_offsets(ss);
+ BM_log_set_cd_offsets(ss->bm_log, ss->cd_dyn_vert);
+ }
+
+ // PBVH is corrupted at this point, destroy it
+ SCULPT_pbvh_clear(ob);
+ }
+#endif
+
/* Restore pivot. */
copy_v3_v3(ss->pivot_pos, unode->pivot_pos);
copy_v3_v3(ss->pivot_rot, unode->pivot_rot);
@@ -664,7 +1131,9 @@ static void sculpt_undo_restore_list(bContext *C, Depsgraph *depsgraph, ListBase
DEG_id_tag_update(&ob->id, ID_RECALC_SHADING);
- if (lb->first) {
+ sculpt_undo_print_nodes(NULL);
+
+ if (!ss->bm && lb->first) {
unode = lb->first;
if (unode->type == SCULPT_UNDO_FACE_SETS) {
sculpt_undo_restore_face_sets(C, unode);
@@ -697,11 +1166,12 @@ 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) {
+ 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, lb->first, ob, ss)) {
+ if (sculpt_undo_bmesh_restore(C, lb->first, ob, ss, dir)) {
return;
}
}
@@ -719,6 +1189,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;
}
}
@@ -859,6 +1330,9 @@ static void sculpt_undo_free_list(ListBase *lb)
if (unode->co) {
MEM_freeN(unode->co);
}
+ if (unode->nodemap) {
+ MEM_freeN(unode->nodemap);
+ }
if (unode->no) {
MEM_freeN(unode->no);
}
@@ -888,6 +1362,7 @@ static void sculpt_undo_free_list(ListBase *lb)
if (unode->bm_entry) {
BM_log_entry_drop(unode->bm_entry);
+ unode->bm_entry = NULL;
}
sculpt_undo_geometry_free_data(&unode->geometry_original);
@@ -927,7 +1402,28 @@ static bool sculpt_undo_cleanup(bContext *C, ListBase *lb)
}
#endif
-SculptUndoNode *SCULPT_undo_get_node(PBVHNode *node)
+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();
@@ -935,7 +1431,19 @@ SculptUndoNode *SCULPT_undo_get_node(PBVHNode *node)
return NULL;
}
- return BLI_findptr(&usculpt->nodes, node, offsetof(SculptUndoNode, node));
+ if (type < 0) {
+ return BLI_findptr(&usculpt->nodes, node, offsetof(SculptUndoNode, node));
+ }
+
+ SculptUndoNode *unode;
+
+ for (unode = usculpt->nodes.first; unode; unode = unode->next) {
+ if (unode->node == node && type == unode->type) {
+ return unode;
+ }
+ }
+
+ return NULL;
}
SculptUndoNode *SCULPT_undo_get_first_node()
@@ -1104,6 +1612,9 @@ 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);
+
BKE_pbvh_vertex_iter_begin (ss->pbvh, unode->node, vd, PBVH_ITER_ALL) {
copy_v3_v3(unode->co[vd.i], vd.co);
if (vd.no) {
@@ -1114,7 +1625,11 @@ 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]]);
+ SCULPT_orig_vert_data_update(&orig_data, vd.vertex);
+
+ int index = BKE_pbvh_vertex_index_to_table(ss->pbvh, unode->index[vd.i]);
+
+ copy_v3_v3(unode->orig_co[vd.i], orig_data.co);
}
}
BKE_pbvh_vertex_iter_end;
@@ -1157,6 +1672,8 @@ static void sculpt_undo_store_color(Object *ob, SculptUndoNode *unode)
SculptSession *ss = ob->sculpt;
PBVHVertexIter vd;
+ // unode->gen++;
+
BKE_pbvh_vertex_iter_begin (ss->pbvh, unode->node, vd, PBVH_ITER_ALL) {
copy_v4_v4(unode->col[vd.i], vd.col);
}
@@ -1209,6 +1726,64 @@ static SculptUndoNode *sculpt_undo_face_sets_push(Object *ob, SculptUndoType typ
return unode;
}
+void SCULPT_undo_ensure_bmlog(Object *ob)
+{
+ if (!ob->sculpt) {
+ return;
+ }
+
+ 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);
+
+ SculptSession *ss = ob->sculpt;
+ Mesh *me = BKE_object_get_original_mesh(ob);
+
+ if (!ss->bm && !(me->flag & ME_SCULPT_DYNAMIC_TOPOLOGY)) {
+ return;
+ }
+
+ if (!usculpt) {
+ // happens during file load
+ return;
+ }
+
+ SculptUndoNode *unode = usculpt->nodes.first;
+
+ // this can happen in certain cases when going to/from other undo types
+ // I think.
+ if (!ss->bm_log) {
+ if (unode && unode->bm_entry) {
+ ss->bm_log = BM_log_from_existing_entries_create(ss->bm, unode->bm_entry);
+ }
+ else {
+ ss->bm_log = BM_log_create(ss->bm, ss->cd_dyn_vert);
+ }
+
+ if (ss->pbvh) {
+ BKE_pbvh_set_bm_log(ss->pbvh, ss->bm_log);
+ }
+ }
+}
+
static SculptUndoNode *sculpt_undo_bmesh_push(Object *ob, PBVHNode *node, SculptUndoType type)
{
UndoSculpt *usculpt = sculpt_undo_get_nodes();
@@ -1217,76 +1792,216 @@ static SculptUndoNode *sculpt_undo_bmesh_push(Object *ob, PBVHNode *node, Sculpt
SculptUndoNode *unode = usculpt->nodes.first;
+ SCULPT_undo_ensure_bmlog(ob);
+
+ if (!ss->bm_log) {
+ ss->bm_log = BM_log_create(ss->bm, ss->cd_dyn_vert);
+ }
+
+ bool new_node = false;
+
if (unode == NULL) {
+ new_node = true;
unode = 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_entry = BM_log_all_ids(ss->bm, ss->bm_log, NULL);
+ 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_entry = BM_log_all_ids(ss->bm, ss->bm_log, NULL);
+ 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_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_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_log_vert_before_modified(ss->bm_log, vd.bm_vert, vd.cd_vert_mask_offset, false);
}
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_log_vert_before_modified(ss->bm_log, vd.bm_vert, vd.cd_vert_mask_offset, true);
}
BKE_pbvh_vertex_iter_end;
- GSET_ITER (gs_iter, faces) {
- BMFace *f = BLI_gsetIterator_getKey(&gs_iter);
+ TGSET_ITER (f, faces) {
BM_log_face_modified(ss->bm_log, f);
}
+ TGSET_ITER_END
+ break;
+ }
+
+ case SCULPT_UNDO_COLOR: {
+ BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
+ float *dummy;
+ BM_log_vert_before_modified(ss->bm_log, vd.bm_vert, vd.cd_vert_mask_offset, true);
+ }
+ BKE_pbvh_vertex_iter_end;
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_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;
+ }
+ }
+
+ if (new_node) {
+ sculpt_undo_print_nodes(NULL);
+ }
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 = usculpt->nodes.first;
+
+ if (!unode || unode->type != type) {
+ unode = sculpt_undo_alloc_node_type(ob, type);
+
+ BLI_strncpy(unode->idname, ob->id.name, sizeof(unode->idname));
+
+ unode->type = type;
+ unode->applied = true;
+ unode->bm_entry = BM_log_entry_add(ss->bm, ss->bm_log);
+
+ return SCULPT_ensure_dyntopo_node_undo(ob, node, type, extraType);
+ }
+
+ int n = BKE_pbvh_get_node_id(ss->pbvh, node);
+
+ if (unode->nodemap_size <= n) {
+ int newsize = (n + 1) * 2;
+
+ if (!unode->nodemap) {
+ unode->nodemap = MEM_callocN(sizeof(*unode->nodemap) * newsize, "unode->nodemap");
+ }
+ else {
+ unode->nodemap = MEM_recallocN(unode->nodemap, sizeof(*unode->nodemap) * newsize);
+ }
+
+ unode->nodemap_size = newsize;
+ }
+
+ if (unode->nodemap[n]) {
+ return false;
+ }
+
+ unode->nodemap[n] = 1;
+ sculpt_undo_bmesh_push(ob, node, type);
+
+ if (extraType >= 0) {
+ sculpt_undo_bmesh_push(ob, node, 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 = 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, NULL, SCULPT_UNDO_DYNTOPO_BEGIN);
+ SCULPT_undo_push_end();
+ }
+
+ return bad;
+}
+
SculptUndoNode *SCULPT_undo_push_node(Object *ob, PBVHNode *node, SculptUndoType type)
{
SculptSession *ss = ob->sculpt;
@@ -1301,20 +2016,24 @@ 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(NULL);
BLI_thread_unlock(LOCK_CUSTOM1);
return unode;
}
if (type == SCULPT_UNDO_GEOMETRY) {
unode = sculpt_undo_geometry_push(ob, type);
+ sculpt_undo_print_nodes(NULL);
BLI_thread_unlock(LOCK_CUSTOM1);
return unode;
}
if (type == SCULPT_UNDO_FACE_SETS) {
unode = sculpt_undo_face_sets_push(ob, type);
+ sculpt_undo_print_nodes(NULL);
BLI_thread_unlock(LOCK_CUSTOM1);
return unode;
}
- if ((unode = SCULPT_undo_get_node(node))) {
+ if ((unode = SCULPT_undo_get_node(node, type))) {
+ sculpt_undo_print_nodes(NULL);
BLI_thread_unlock(LOCK_CUSTOM1);
return unode;
}
@@ -1336,7 +2055,11 @@ SculptUndoNode *SCULPT_undo_push_node(Object *ob, PBVHNode *node, SculptUndoType
int allvert;
BKE_pbvh_node_num_verts(ss->pbvh, node, NULL, &allvert);
BKE_pbvh_node_get_verts(ss->pbvh, node, &vert_indices, NULL);
- memcpy(unode->index, vert_indices, sizeof(int) * unode->totvert);
+
+ for (int i = 0; i < unode->totvert; i++) {
+ unode->index[i].i = vert_indices[i];
+ }
+ // memcpy(unode->index, vert_indices, sizeof(int) * unode->totvert);
}
switch (type) {
@@ -1373,16 +2096,24 @@ SculptUndoNode *SCULPT_undo_push_node(Object *ob, PBVHNode *node, SculptUndoType
unode->shapeName[0] = '\0';
}
+ sculpt_undo_print_nodes(NULL);
+
BLI_thread_unlock(LOCK_CUSTOM1);
return unode;
}
-void SCULPT_undo_push_begin(Object *ob, const char *name)
+void sculpt_undo_push_begin_ex(Object *ob, const char *name, bool no_first_entry_check)
{
+ SCULPT_undo_ensure_bmlog(ob);
+
UndoStack *ustack = ED_undo_stack_get();
if (ob != NULL) {
+ 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);
@@ -1395,6 +2126,11 @@ void SCULPT_undo_push_begin(Object *ob, const char *name)
BKE_undosys_step_push_init_with_type(ustack, C, name, BKE_UNDOSYS_TYPE_SCULPT);
}
+void SCULPT_undo_push_begin(Object *ob, const char *name)
+{
+ sculpt_undo_push_begin_ex(ob, name, false);
+}
+
void SCULPT_undo_push_end(void)
{
SCULPT_undo_push_end_ex(false);
@@ -1407,6 +2143,10 @@ void SCULPT_undo_push_end_ex(const bool use_nested_undo)
/* We don't need normals in the undo stack. */
for (unode = 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);
@@ -1430,12 +2170,6 @@ void SCULPT_undo_push_end_ex(const bool use_nested_undo)
/** \name Implements ED Undo System
* \{ */
-typedef struct SculptUndoStep {
- UndoStep step;
- /* NOTE: will split out into list for multi-object-sculpt-mode. */
- UndoSculpt data;
-} SculptUndoStep;
-
static void sculpt_undosys_step_encode_init(struct bContext *UNUSED(C), UndoStep *us_p)
{
SculptUndoStep *us = (SculptUndoStep *)us_p;
@@ -1470,8 +2204,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(us);
}
static void sculpt_undosys_step_decode_redo_impl(struct bContext *C,
@@ -1479,8 +2215,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(us);
}
static void sculpt_undosys_step_decode_undo(struct bContext *C,
@@ -1554,10 +2292,13 @@ static void sculpt_undosys_step_decode(
BKE_scene_graph_evaluated_ensure(depsgraph, bmain);
Mesh *me = 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, NULL);
+#endif
+ ED_object_sculptmode_enter_ex(bmain, depsgraph, scene, ob, true, NULL, false);
}
if (ob->sculpt) {
@@ -1628,10 +2369,19 @@ 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 NULL;
+ }
+
UndoStep *us = BKE_undosys_stack_init_or_active_with_type(ustack, BKE_UNDOSYS_TYPE_SCULPT);
return sculpt_undosys_step_get_nodes(us);
}
+void SCULPT_on_sculptsession_bmesh_free(SculptSession *ss)
+{
+}
+
/** \} */
/* -------------------------------------------------------------------- */
@@ -1677,7 +2427,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 == NULL) {
return;
}
@@ -1726,3 +2477,186 @@ void ED_sculpt_undo_push_multires_mesh_end(bContext *C, const char *str)
}
/** \} */
+
+#ifdef _
+# undef _
+#endif
+#define _(type) \
+ case type: \
+ return #type;
+static 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(NULL, 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 = usculpt->nodes.first; node; node = node->next) {
+ print_sculpt_node(node);
+ }
+ }
+}
+void sculpt_undo_print_nodes(void *active)
+{
+#if 0
+
+ printf("=================== sculpt undo steps ==============\n");
+
+ UndoStack *ustack = ED_undo_stack_get();
+ UndoStep *us = ustack->steps.first;
+ if (active == NULL) {
+ active = ustack->step_active;
+ }
+
+ SculptUndoNode *node;
+
+ if (!us) {
+ return;
+ }
+
+ printf("\n");
+ if (ustack->step_init) {
+ printf("===undo init===\n");
+ print_sculpt_undo_step(ustack->step_init, active, -1);
+ printf("===============\n");
+ }
+
+ int i = 0, act_i = -1;
+ for (; us; us = us->next, i++) {
+ if (active == us) {
+ act_i = i;
+ }
+
+ print_sculpt_undo_step(us, active, i);
+ }
+
+ if (ustack->step_active) {
+ printf("\n\n==active step:==\n");
+ print_sculpt_undo_step(ustack->step_active, active, act_i);
+ }
+
+#endif
+}
+
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+#include "DNA_object_types.h"
+#include "DNA_scene_types.h"
+
+void BM_log_undo_single(BMesh *bm,
+ BMLog *log,
+ BMLogCallbacks *callbacks,
+ const char *node_layer_id);
+
+void SCULPT_substep_undo(bContext *C, int dir)
+{
+ 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_dyn_vert,
+ 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,
+ NULL,
+ (void *)&data};
+
+ BM_log_undo_single(ss->bm, ss->bm_log, &callbacks, dyntopop_node_idx_layer_id);
+
+ BKE_sculpt_update_object_for_edit(depsgraph, ob, true, false, false);
+ DEG_id_tag_update(&ob->id, ID_RECALC_SHADING);
+}
diff --git a/source/blender/editors/space_info/info_stats.c b/source/blender/editors/space_info/info_stats.c
index e749e1a7947..ebc192c56b8 100644
--- a/source/blender/editors/space_info/info_stats.c
+++ b/source/blender/editors/space_info/info_stats.c
@@ -382,8 +382,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_vertices(ss->pbvh);
@@ -443,15 +445,7 @@ static void stats_update(Depsgraph *depsgraph,
FOREACH_OBJECT_END;
}
else if (ob && (ob->mode & OB_MODE_SCULPT)) {
- /* Sculpt Mode. */
- if (stats_is_object_dynamic_topology_sculpt(ob)) {
- /* Dynamic topology. Do not count all vertices,
- * dynamic topology stats are initialized later as part of sculpt stats. */
- }
- else {
- /* When dynamic topology is not enabled both sculpt stats and scene stats are collected. */
- stats_object_sculpt(ob, stats);
- }
+ stats_object_sculpt(ob, stats);
}
else {
/* Objects. */
diff --git a/source/blender/editors/util/ed_util.c b/source/blender/editors/util/ed_util.c
index 73f328f85d7..92d56ccf521 100644
--- a/source/blender/editors/util/ed_util.c
+++ b/source/blender/editors/util/ed_util.c
@@ -135,7 +135,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, true, 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.c b/source/blender/editors/uvedit/uvedit_unwrap_ops.c
index 3d5dabda23d..84be69b7c4f 100644
--- a/source/blender/editors/uvedit/uvedit_unwrap_ops.c
+++ b/source/blender/editors/uvedit/uvedit_unwrap_ops.c
@@ -2897,7 +2897,8 @@ void ED_uvedit_add_simple_uvs(Main *bmain, const Scene *scene, Object *ob)
ED_mesh_uv_texture_ensure(me, NULL);
- BM_mesh_bm_from_me(bm,
+ BM_mesh_bm_from_me(NULL,
+ bm,
me,
(&(struct BMeshFromMeshParams){
.calc_face_normal = true,
@@ -2908,7 +2909,7 @@ void ED_uvedit_add_simple_uvs(Main *bmain, const Scene *scene, Object *ob)
/* Set the margin really quickly before the packing operation. */
scene->toolsettings->uvcalc_margin = 0.001f;
uvedit_pack_islands(scene, ob, bm);
- BM_mesh_bm_to_me(bmain, bm, me, (&(struct BMeshToMeshParams){0}));
+ BM_mesh_bm_to_me(bmain, NULL, bm, me, (&(struct BMeshToMeshParams){0}));
BM_mesh_free(bm);
if (sync_selection) {
diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c
index 725cc0741f0..7176501f86d 100644
--- a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c
+++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c
@@ -1694,7 +1694,8 @@ static void lineart_geometry_object_load(LineartObjectInfo *obi, LineartRenderBu
&((struct BMeshCreateParams){
.use_toolflags = true,
}));
- BM_mesh_bm_from_me(bm,
+ BM_mesh_bm_from_me(orig_ob,
+ bm,
obi->original_me,
&((struct BMeshFromMeshParams){
.calc_face_normal = true,
diff --git a/source/blender/gpu/GPU_buffers.h b/source/blender/gpu/GPU_buffers.h
index b770bde65fc..e2ccf8fe941 100644
--- a/source/blender/gpu/GPU_buffers.h
+++ b/source/blender/gpu/GPU_buffers.h
@@ -34,6 +34,7 @@ struct CCGElem;
struct CCGKey;
struct DMFlagMat;
struct GSet;
+struct TableGSet;
struct MLoop;
struct MLoopCol;
struct MLoopTri;
@@ -43,6 +44,7 @@ struct MVert;
struct Mesh;
struct PBVH;
struct SubdivCCG;
+struct CustomData;
/* Buffers for drawing from PBVH grids. */
typedef struct GPU_PBVH_Buffers GPU_PBVH_Buffers;
@@ -85,12 +87,24 @@ void GPU_pbvh_mesh_buffers_update(GPU_PBVH_Buffers *buffers,
const struct MPropCol *vtcol,
const int update_flags);
+void GPU_pbvh_update_attribute_names(
+ struct CustomData *vdata,
+ struct CustomData *ldata,
+ bool need_full_render,
+ bool fast_mode); // fast mode renders without vcol, uv, facesets, even mask, etc
+
void GPU_pbvh_bmesh_buffers_update(GPU_PBVH_Buffers *buffers,
struct BMesh *bm,
- struct GSet *bm_faces,
- struct GSet *bm_unique_verts,
- struct GSet *bm_other_verts,
- const int update_flags);
+ struct TableGSet *bm_faces,
+ struct TableGSet *bm_unique_verts,
+ struct TableGSet *bm_other_verts,
+ struct PBVHTriBuf *tribuf,
+ const int update_flags,
+ const int cd_vert_node_offset,
+ int face_sets_color_seed,
+ int face_sets_color_default,
+ bool flat_vcol,
+ short mat_nr);
void GPU_pbvh_grid_buffers_update(GPU_PBVH_Buffers *buffers,
struct SubdivCCG *subdiv_ccg,
@@ -114,8 +128,14 @@ void GPU_pbvh_buffers_free(GPU_PBVH_Buffers *buffers);
struct GPUBatch *GPU_pbvh_buffers_batch_get(GPU_PBVH_Buffers *buffers, bool fast, bool wires);
short GPU_pbvh_buffers_material_index_get(GPU_PBVH_Buffers *buffers);
-
bool GPU_pbvh_buffers_has_overlays(GPU_PBVH_Buffers *buffers);
+float *GPU_pbvh_get_extra_matrix(GPU_PBVH_Buffers *buffers);
+
+/** if need_full_render is false, only the active (not render!) vcol layer will
+ be uploaded to GPU*/
+
+void GPU_pbvh_need_full_render_set(bool state);
+bool GPU_pbvh_need_full_render_get(void);
#ifdef __cplusplus
}
diff --git a/source/blender/gpu/GPU_vertex_format.h b/source/blender/gpu/GPU_vertex_format.h
index 0d5388c6b82..58ad02fc566 100644
--- a/source/blender/gpu/GPU_vertex_format.h
+++ b/source/blender/gpu/GPU_vertex_format.h
@@ -112,6 +112,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);
void GPU_vertformat_multiload_enable(GPUVertFormat *format, int load_count);
diff --git a/source/blender/gpu/intern/gpu_buffers.c b/source/blender/gpu/intern/gpu_buffers.c
index 43483916236..026fa6f717a 100644
--- a/source/blender/gpu/intern/gpu_buffers.c
+++ b/source/blender/gpu/intern/gpu_buffers.c
@@ -25,16 +25,20 @@
#include <limits.h>
#include <stddef.h>
+#include <stdlib.h>
#include <string.h>
#include "MEM_guardedalloc.h"
+#include "BLI_alloca.h"
+#include "BLI_array.h"
#include "BLI_bitmap.h"
#include "BLI_ghash.h"
#include "BLI_hash.h"
#include "BLI_math.h"
#include "BLI_math_color.h"
#include "BLI_math_color_blend.h"
+#include "BLI_string.h"
#include "BLI_utildefines.h"
#include "DNA_meshdata_types.h"
@@ -42,6 +46,7 @@
#include "BKE_DerivedMesh.h"
#include "BKE_ccg.h"
+#include "BKE_global.h"
#include "BKE_mesh.h"
#include "BKE_paint.h"
#include "BKE_pbvh.h"
@@ -50,13 +55,31 @@
#include "GPU_batch.h"
#include "GPU_buffers.h"
+#include "DRW_engine.h"
+
#include "gpu_private.h"
#include "bmesh.h"
+//#define GPU_PERF_TEST // will disable vcol, uvs, mask, fset colors, etc
+
/* XXX: the rest of the code in this file is used for optimized PBVH
* drawing and doesn't interact at all with the buffer code above */
+/*
+this tests a low-data draw mode. faceset and mask overlays must be disabled,
+meshes cannot have uv layers, and DynTopo must be on, along with "draw smooth."
+
+Normalizes coordinates to 16 bit integers, normals to 8-bit bytes, and skips
+all other attributes.
+
+To test, enable #if 0 branch in sculpt_draw_cb in draw_manager_data.c
+*/
+
+//#define QUANTIZED_PERF_TEST
+
+//#define NEW_ATTR_SYSTEM
+
struct GPU_PBVH_Buffers {
GPUIndexBuf *index_buf, *index_buf_fast;
GPUIndexBuf *index_lines_buf, *index_lines_buf_fast;
@@ -95,14 +118,240 @@ struct GPU_PBVH_Buffers {
* smooth-shaded or all faces are flat-shaded */
bool smooth;
+ void *last_tribuf_tris; // used to detect if we can reuse index buffers
+
bool show_overlay;
+#ifdef QUANTIZED_PERF_TEST
+ float matrix[4][4];
+#endif
};
+#ifdef NEW_ATTR_SYSTEM
+typedef struct CDLayerType {
+ int type;
+ char gpu_attr_name[32];
+ GPUVertCompType source_type;
+ GPUVertCompType comp_type;
+ uint comp_len;
+ GPUVertFetchMode fetch_mode;
+ char gpu_attr_code[8];
+} CDLayerType;
+
+typedef struct CDAttrLayers {
+ CDLayerType type;
+ uint totlayer;
+ uint *layers;
+ int *offsets;
+ uint *attrs;
+} CDAttrLayers;
+#endif
+
static struct {
GPUVertFormat format;
- uint pos, nor, msk, col, fset;
+ uint pos, nor, msk, fset, uv;
+ uint col[MAX_MCOL];
+ int totcol;
+
+#ifdef NEW_ATTR_SYSTEM
+ CDAttrLayers *vertex_attrs;
+ CDAttrLayers *loop_attrs;
+ int vertex_attrs_len;
+ int loop_attrs_len;
+#endif
+
+ bool active_vcol_only;
+ bool need_full_render;
+ bool fast_mode;
} g_vbo_id = {{0}};
+#ifdef NEW_ATTR_SYSTEM
+static CDLayerType cd_vert_layers[] = {
+ {CD_PROP_COLOR, "c", GPU_COMP_F32, GPU_COMP_U16, 4, GPU_FETCH_INT_TO_FLOAT_UNIT, "c"}};
+static CDLayerType cd_loop_layers[] = {
+ {CD_MLOOPUV, "uvs", GPU_COMP_F32, GPU_COMP_F32, 2, GPU_FETCH_FLOAT, "u"}};
+
+static void build_cd_layers(GPUVertFormat *format,
+ CDAttrLayers *cdattr,
+ CustomData *cd,
+ CDLayerType *type)
+{
+ uint *layers = NULL;
+ int *offsets = NULL;
+ uint *attrs = NULL;
+
+ BLI_array_declare(layers);
+ BLI_array_declare(offsets);
+ BLI_array_declare(attrs);
+
+ cdattr->type = *type;
+ cdattr->totlayer = 0;
+
+ int act = 0;
+ int actidx = CustomData_get_active_layer_index(cd, type->type);
+
+ for (int i = 0; i < cd->totlayer; i++) {
+ CustomDataLayer *cl = cd->layers + i;
+
+ if (cl->type != type->type || (cl->flag & CD_FLAG_TEMPORARY)) {
+ continue;
+ }
+
+ cdattr->totlayer++;
+
+ /*
+ g_vbo_id.col[ci++] = GPU_vertformat_attr_add(
+ &g_vbo_id.format, "c", GPU_COMP_U16, 4, GPU_FETCH_INT_TO_FLOAT_UNIT);
+ g_vbo_id.totcol++;
+
+ DRW_make_cdlayer_attr_aliases(&g_vbo_id.format, "c", vdata, cl);
+
+ if (idx == act) {
+ GPU_vertformat_alias_add(&g_vbo_id.format, "ac");
+ }
+
+ */
+
+ uint attr = GPU_vertformat_attr_add(
+ format, type->gpu_attr_name, type->comp_type, type->comp_len, type->fetch_mode);
+
+ BLI_array_append(layers, i);
+ BLI_array_append(offsets, cl->offset);
+ BLI_array_append(attrs, attr);
+
+ DRW_make_cdlayer_attr_aliases(format, type->gpu_attr_code, cd, cl);
+
+ if (i == actidx) {
+ char buf[128];
+
+ BLI_snprintf(buf, sizeof(buf), "a%s", type->gpu_attr_code);
+ GPU_vertformat_alias_add(&g_vbo_id.format, buf);
+ }
+ }
+
+ cdattr->offsets = offsets;
+ cdattr->layers = layers;
+ cdattr->attrs = attrs;
+}
+
+/*must match GPUVertCompType*/
+static int gpu_comp_map[] = {
+ 1, // GPU_COMP_I8 = 0,
+ 1, // GPU_COMP_U8,
+ 2, // GPU_COMP_I16,
+ 2, // GPU_COMP_U16,
+ 4, // GPU_COMP_I32,
+ 4, // GPU_COMP_U32,
+
+ 4, // GPU_COMP_F32,
+
+ 4 // GPU_COMP_I10,
+};
+
+static void convert_gpu_data(void *src,
+ void *dst,
+ GPUVertCompType srcType,
+ GPUVertCompType dstType)
+{
+ if (srcType == dstType) {
+ memcpy(dst, src, gpu_comp_map[(int)srcType]);
+ return;
+ }
+
+ double val = 0;
+
+ switch (srcType) {
+ case GPU_COMP_I8:
+ val = ((float)*((signed char *)(src))) / 127.0;
+ break;
+ case GPU_COMP_U8:
+ val = ((float)*((unsigned char *)(src))) / 255.0;
+ break;
+ case GPU_COMP_I16:
+ val = ((float)*((unsigned short *)(src))) / 32767.0;
+ break;
+ case GPU_COMP_U16:
+ val = ((float)*((signed short *)(src))) / 65535.0;
+ break;
+ case GPU_COMP_I32:
+ val = ((float)*((signed int *)(src))) / 2147483647.0;
+ break;
+ case GPU_COMP_U32:
+ val = ((float)*((unsigned int *)(src))) / 4294967295.0;
+ break;
+ case GPU_COMP_F32:
+ val = *(float *)src;
+ break;
+ case GPU_COMP_I10: // handle elsewhere
+ break;
+ }
+
+ switch (dstType) {
+ case GPU_COMP_I8:
+ *((signed char *)dst) = (signed char)(val * 127.0);
+ break;
+ case GPU_COMP_U8:
+ *((unsigned char *)dst) = (unsigned char)(val * 255.0);
+ break;
+ case GPU_COMP_I16:
+ *((signed short *)dst) = (signed short)(val * 32767.0);
+ break;
+ case GPU_COMP_U16:
+ *((unsigned short *)dst) = (unsigned short)(val * 65535.0);
+ break;
+ case GPU_COMP_I32:
+ *((signed int *)dst) = (signed int)(val * 2147483647.0);
+ break;
+ case GPU_COMP_U32:
+ *((unsigned int *)dst) = (unsigned int)(val * 4294967295.0);
+ break;
+ case GPU_COMP_F32:
+ *((float *)dst) = (float)val;
+ break;
+ case GPU_COMP_I10: // handle elsewhere
+ break;
+ }
+}
+
+/*
+ GPUVertBuf *vert_buf
+ GPU_vertbuf_attr_set(vert_buf, g_vbo_id.pos, v_index, v->co);
+*/
+
+static void set_cd_data_bmesh(
+ GPUVertBuf *vert_buf, CDAttrLayers *attr_array, int attr_array_len, BMElem *elem, int vertex)
+{
+ for (int i = 0; i < attr_array_len; i++) {
+ CDAttrLayers *attr = attr_array + i;
+
+ int dst_size = gpu_comp_map[(int)attr->type.comp_type];
+ int src_size = gpu_comp_map[(int)attr->type.source_type];
+ void *dest = alloca(dst_size *
+ attr->type.comp_len); // ensure proper alignment by making this a
+ void *dest2 = dest;
+
+ for (int j = 0; j < attr->totlayer; j++) {
+ void *data = BM_ELEM_CD_GET_VOID_P(elem, attr->offsets[j]);
+
+ for (int k = 0; k < attr->type.comp_len; k++) {
+ convert_gpu_data(data, dest2, attr->type.source_type, attr->type.comp_type);
+
+ data = (void *)(((char *)data) + src_size);
+ dest2 = (void *)(((char *)dest2) + dst_size);
+ }
+
+ GPU_vertbuf_attr_set(vert_buf, attr->attrs[j], vertex, dest);
+ }
+ }
+}
+
+static void free_cd_layers(CDAttrLayers *cdattr)
+{
+ MEM_SAFE_FREE(cdattr->layers);
+ MEM_SAFE_FREE(cdattr->offsets);
+ MEM_SAFE_FREE(cdattr->attrs);
+}
+#endif
+
/** \} */
/* -------------------------------------------------------------------- */
@@ -111,20 +360,7 @@ static struct {
void gpu_pbvh_init()
{
- /* Initialize vertex buffer (match 'VertexBufferFormat'). */
- if (g_vbo_id.format.attr_len == 0) {
- g_vbo_id.pos = GPU_vertformat_attr_add(
- &g_vbo_id.format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
- g_vbo_id.nor = GPU_vertformat_attr_add(
- &g_vbo_id.format, "nor", GPU_COMP_I16, 3, GPU_FETCH_INT_TO_FLOAT_UNIT);
- /* TODO: Do not allocate these `.msk` and `.col` when they are not used. */
- g_vbo_id.msk = GPU_vertformat_attr_add(
- &g_vbo_id.format, "msk", GPU_COMP_U8, 1, GPU_FETCH_INT_TO_FLOAT_UNIT);
- g_vbo_id.col = GPU_vertformat_attr_add(
- &g_vbo_id.format, "ac", GPU_COMP_U16, 4, GPU_FETCH_INT_TO_FLOAT_UNIT);
- g_vbo_id.fset = GPU_vertformat_attr_add(
- &g_vbo_id.format, "fset", GPU_COMP_U8, 3, GPU_FETCH_INT_TO_FLOAT_UNIT);
- }
+ GPU_pbvh_update_attribute_names(NULL, NULL, false, false);
}
void gpu_pbvh_exit()
@@ -143,10 +379,11 @@ static bool gpu_pbvh_vert_buf_data_set(GPU_PBVH_Buffers *buffers, uint vert_len)
if (buffers->vert_buf == NULL) {
/* Initialize vertex buffer (match 'VertexBufferFormat'). */
buffers->vert_buf = GPU_vertbuf_create_with_format_ex(&g_vbo_id.format, GPU_USAGE_DYNAMIC);
- GPU_vertbuf_data_alloc(buffers->vert_buf, vert_len);
}
- else if (vert_len != buffers->vert_buf->vertex_len) {
- GPU_vertbuf_data_resize(buffers->vert_buf, vert_len);
+ if (GPU_vertbuf_get_data(buffers->vert_buf) == NULL ||
+ GPU_vertbuf_get_vertex_len(buffers->vert_buf) != vert_len) {
+ /* Allocate buffer if not allocated yet or size changed. */
+ GPU_vertbuf_data_alloc(buffers->vert_buf, vert_len);
}
#else
if (buffers->vert_buf == NULL) {
@@ -163,8 +400,23 @@ static bool gpu_pbvh_vert_buf_data_set(GPU_PBVH_Buffers *buffers, uint vert_len)
return GPU_vertbuf_get_data(buffers->vert_buf) != NULL;
}
+float *GPU_pbvh_get_extra_matrix(GPU_PBVH_Buffers *buffers)
+{
+#ifdef QUANTIZED_PERF_TEST
+ return (float *)buffers->matrix;
+#else
+ return NULL;
+#endif
+}
+
static void gpu_pbvh_batch_init(GPU_PBVH_Buffers *buffers, GPUPrimType prim)
{
+#ifdef QUANTIZED_PERF_TEST
+ memset(buffers->matrix, 0, sizeof(buffers->matrix));
+ buffers->matrix[0][0] = buffers->matrix[1][1] = buffers->matrix[2][2] = buffers->matrix[3][3] =
+ 1.0f;
+#endif
+
if (buffers->triangles == NULL) {
buffers->triangles = GPU_batch_create(prim,
buffers->vert_buf,
@@ -215,9 +467,11 @@ void GPU_pbvh_mesh_buffers_update(GPU_PBVH_Buffers *buffers,
const MPropCol *vtcol,
const int update_flags)
{
- const bool show_mask = vmask && (update_flags & GPU_PBVH_BUFFERS_SHOW_MASK) != 0;
+ const bool show_mask = vmask && (update_flags & GPU_PBVH_BUFFERS_SHOW_MASK) != 0 &&
+ !g_vbo_id.fast_mode;
const bool show_face_sets = sculpt_face_sets &&
- (update_flags & GPU_PBVH_BUFFERS_SHOW_SCULPT_FACE_SETS) != 0;
+ (update_flags & GPU_PBVH_BUFFERS_SHOW_SCULPT_FACE_SETS) != 0 &&
+ !g_vbo_id.fast_mode;
const bool show_vcol = (vcol || (vtcol && U.experimental.use_sculpt_vertex_colors)) &&
(update_flags & GPU_PBVH_BUFFERS_SHOW_VCOL) != 0;
bool empty_mask = true;
@@ -239,7 +493,7 @@ void GPU_pbvh_mesh_buffers_update(GPU_PBVH_Buffers *buffers,
GPU_vertbuf_attr_get_raw_data(buffers->vert_buf, g_vbo_id.msk, &msk_step);
GPU_vertbuf_attr_get_raw_data(buffers->vert_buf, g_vbo_id.fset, &fset_step);
if (show_vcol) {
- GPU_vertbuf_attr_get_raw_data(buffers->vert_buf, g_vbo_id.col, &col_step);
+ GPU_vertbuf_attr_get_raw_data(buffers->vert_buf, g_vbo_id.col[0], &col_step);
}
/* calculate normal for each polygon only once */
@@ -319,8 +573,11 @@ void GPU_pbvh_mesh_buffers_update(GPU_PBVH_Buffers *buffers,
memcpy(GPU_vertbuf_raw_step(&col_step), scol, sizeof(scol));
}
}
- /* Face Sets. */
- memcpy(GPU_vertbuf_raw_step(&fset_step), face_set_color, sizeof(uchar[3]));
+
+ if (!g_vbo_id.fast_mode) {
+ /* Face Sets. */
+ memcpy(GPU_vertbuf_raw_step(&fset_step), face_set_color, sizeof(uchar[3]));
+ }
}
}
}
@@ -333,7 +590,7 @@ void GPU_pbvh_mesh_buffers_update(GPU_PBVH_Buffers *buffers,
const MPoly *mp = &buffers->mpoly[lt->poly];
buffers->material_index = mp->mat_nr;
- buffers->show_overlay = !empty_mask || !default_face_set;
+ buffers->show_overlay = (!empty_mask || !default_face_set) && !g_vbo_id.fast_mode;
buffers->mvert = mvert;
}
@@ -354,7 +611,7 @@ GPU_PBVH_Buffers *GPU_pbvh_mesh_buffers_build(const MPoly *mpoly,
buffers = MEM_callocN(sizeof(GPU_PBVH_Buffers), "GPU_Buffers");
/* smooth or flat for all */
- buffers->smooth = mpoly[looptri[face_indices[0]].poly].flag & ME_SMOOTH;
+ buffers->smooth = (mpoly[looptri[face_indices[0]].poly].flag & ME_SMOOTH) || g_vbo_id.fast_mode;
buffers->show_overlay = false;
@@ -448,7 +705,7 @@ static void gpu_pbvh_grid_fill_index_buffers(GPU_PBVH_Buffers *buffers,
GPU_indexbuf_init(&elb_lines, GPU_PRIM_LINES, 2 * totgrid * gridsize * (gridsize - 1), INT_MAX);
GPU_indexbuf_init(&elb_lines_fast, GPU_PRIM_LINES, 4 * totgrid, INT_MAX);
- if (buffers->smooth) {
+ if (buffers->smooth || g_vbo_id.fast_mode) {
uint offset = 0;
const uint grid_vert_len = gridsize * gridsize;
for (int i = 0; i < totgrid; i++, offset += grid_vert_len) {
@@ -568,7 +825,7 @@ void GPU_pbvh_grid_buffers_update_free(GPU_PBVH_Buffers *buffers,
const struct DMFlagMat *grid_flag_mats,
const int *grid_indices)
{
- const bool smooth = grid_flag_mats[grid_indices[0]].flag & ME_SMOOTH;
+ const bool smooth = (grid_flag_mats[grid_indices[0]].flag & ME_SMOOTH) || g_vbo_id.fast_mode;
if (buffers->smooth != smooth) {
buffers->smooth = smooth;
@@ -597,10 +854,11 @@ void GPU_pbvh_grid_buffers_update(GPU_PBVH_Buffers *buffers,
const struct CCGKey *key,
const int update_flags)
{
- const bool show_mask = (update_flags & GPU_PBVH_BUFFERS_SHOW_MASK) != 0;
+ const bool show_mask = (update_flags & GPU_PBVH_BUFFERS_SHOW_MASK) != 0 && !g_vbo_id.fast_mode;
const bool show_vcol = (update_flags & GPU_PBVH_BUFFERS_SHOW_VCOL) != 0;
const bool show_face_sets = sculpt_face_sets &&
- (update_flags & GPU_PBVH_BUFFERS_SHOW_SCULPT_FACE_SETS) != 0;
+ (update_flags & GPU_PBVH_BUFFERS_SHOW_SCULPT_FACE_SETS) != 0 &&
+ !g_vbo_id.fast_mode;
bool empty_mask = true;
bool default_face_set = true;
@@ -609,7 +867,13 @@ void GPU_pbvh_grid_buffers_update(GPU_PBVH_Buffers *buffers,
/* Build VBO */
const int has_mask = key->has_mask;
- buffers->smooth = grid_flag_mats[grid_indices[0]].flag & ME_SMOOTH;
+ bool smooth = (grid_flag_mats[grid_indices[0]].flag & ME_SMOOTH) || g_vbo_id.fast_mode;
+ ;
+ if (smooth != buffers->smooth) {
+ GPU_pbvh_grid_buffers_update_free(buffers, grid_flag_mats, grid_indices);
+ }
+
+ buffers->smooth = smooth;
uint vert_per_grid = (buffers->smooth) ? key->grid_area : (square_i(key->grid_size - 1) * 4);
uint vert_count = totgrid * vert_per_grid;
@@ -659,7 +923,7 @@ void GPU_pbvh_grid_buffers_update(GPU_PBVH_Buffers *buffers,
}
}
- if (buffers->smooth) {
+ if (buffers->smooth || g_vbo_id.fast_mode) {
for (y = 0; y < key->grid_size; y++) {
for (x = 0; x < key->grid_size; x++) {
CCGElem *elem = CCG_grid_elem(key, grid, x, y);
@@ -679,10 +943,12 @@ void GPU_pbvh_grid_buffers_update(GPU_PBVH_Buffers *buffers,
if (show_vcol) {
const ushort vcol[4] = {USHRT_MAX, USHRT_MAX, USHRT_MAX, USHRT_MAX};
- GPU_vertbuf_attr_set(buffers->vert_buf, g_vbo_id.col, vbo_index, &vcol);
+ GPU_vertbuf_attr_set(buffers->vert_buf, g_vbo_id.col[0], vbo_index, &vcol);
}
- GPU_vertbuf_attr_set(buffers->vert_buf, g_vbo_id.fset, vbo_index, &face_set_color);
+ if (!g_vbo_id.fast_mode) {
+ GPU_vertbuf_attr_set(buffers->vert_buf, g_vbo_id.fset, vbo_index, &face_set_color);
+ }
vbo_index += 1;
}
@@ -733,15 +999,23 @@ void GPU_pbvh_grid_buffers_update(GPU_PBVH_Buffers *buffers,
}
const ushort vcol[4] = {USHRT_MAX, USHRT_MAX, USHRT_MAX, USHRT_MAX};
- GPU_vertbuf_attr_set(buffers->vert_buf, g_vbo_id.col, vbo_index + 0, &vcol);
- GPU_vertbuf_attr_set(buffers->vert_buf, g_vbo_id.col, vbo_index + 1, &vcol);
- GPU_vertbuf_attr_set(buffers->vert_buf, g_vbo_id.col, vbo_index + 2, &vcol);
- GPU_vertbuf_attr_set(buffers->vert_buf, g_vbo_id.col, vbo_index + 3, &vcol);
+ if (1) { // g_vbo_id.totcol > 0 || !g_vbo_id.fast_mode) {
+ GPU_vertbuf_attr_set(buffers->vert_buf, g_vbo_id.col[0], vbo_index + 0, &vcol);
+ GPU_vertbuf_attr_set(buffers->vert_buf, g_vbo_id.col[0], vbo_index + 1, &vcol);
+ GPU_vertbuf_attr_set(buffers->vert_buf, g_vbo_id.col[0], vbo_index + 2, &vcol);
+ GPU_vertbuf_attr_set(buffers->vert_buf, g_vbo_id.col[0], vbo_index + 3, &vcol);
+ }
- GPU_vertbuf_attr_set(buffers->vert_buf, g_vbo_id.fset, vbo_index + 0, &face_set_color);
- GPU_vertbuf_attr_set(buffers->vert_buf, g_vbo_id.fset, vbo_index + 1, &face_set_color);
- GPU_vertbuf_attr_set(buffers->vert_buf, g_vbo_id.fset, vbo_index + 2, &face_set_color);
- GPU_vertbuf_attr_set(buffers->vert_buf, g_vbo_id.fset, vbo_index + 3, &face_set_color);
+ if (!g_vbo_id.fast_mode) {
+ GPU_vertbuf_attr_set(
+ buffers->vert_buf, g_vbo_id.fset, vbo_index + 0, &face_set_color);
+ GPU_vertbuf_attr_set(
+ buffers->vert_buf, g_vbo_id.fset, vbo_index + 1, &face_set_color);
+ GPU_vertbuf_attr_set(
+ buffers->vert_buf, g_vbo_id.fset, vbo_index + 2, &face_set_color);
+ GPU_vertbuf_attr_set(
+ buffers->vert_buf, g_vbo_id.fset, vbo_index + 3, &face_set_color);
+ }
vbo_index += 4;
}
@@ -761,7 +1035,7 @@ void GPU_pbvh_grid_buffers_update(GPU_PBVH_Buffers *buffers,
buffers->totgrid = totgrid;
buffers->grid_flag_mats = grid_flag_mats;
buffers->gridkey = *key;
- buffers->show_overlay = !empty_mask || !default_face_set;
+ buffers->show_overlay = (!empty_mask || !default_face_set) && !g_vbo_id.fast_mode;
}
/* Threaded - do not call any functions that use OpenGL calls! */
@@ -786,6 +1060,8 @@ GPU_PBVH_Buffers *GPU_pbvh_grid_buffers_build(int totgrid, BLI_bitmap **grid_hid
/** \name BMesh PBVH
* \{ */
+static int debug_pass = 0;
+
/* Output a BMVert into a VertexBufferFormat array at v_index. */
static void gpu_bmesh_vert_to_buffer_copy(BMVert *v,
GPUVertBuf *vert_buf,
@@ -793,120 +1069,416 @@ static void gpu_bmesh_vert_to_buffer_copy(BMVert *v,
const float fno[3],
const float *fmask,
const int cd_vert_mask_offset,
+ const int cd_vert_node_offset,
const bool show_mask,
const bool show_vcol,
- bool *empty_mask)
+ bool *empty_mask,
+ const int cd_vcol_offsets[MAX_MCOL],
+ int totvcol)
{
/* Vertex should always be visible if it's used by a visible face. */
BLI_assert(!BM_elem_flag_test(v, BM_ELEM_HIDDEN));
+#ifdef NEW_ATTR_SYSTEM
+ // static void set_cd_data_bmesh(GPUVertBuf *vert_buf, CDAttrLayers *attr, BMElem *elem, int
+ // vertex)
+ set_cd_data_bmesh(
+ vert_buf, g_vbo_id.vertex_attrs, g_vbo_id.vertex_attrs_len, (BMElem *)v, v_index);
+#endif
+
/* Set coord, normal, and mask */
GPU_vertbuf_attr_set(vert_buf, g_vbo_id.pos, v_index, v->co);
short no_short[3];
+
normal_float_to_short_v3(no_short, fno ? fno : v->no);
GPU_vertbuf_attr_set(vert_buf, g_vbo_id.nor, v_index, no_short);
+#ifndef GPU_PERF_TEST
if (show_mask) {
float effective_mask = fmask ? *fmask : BM_ELEM_CD_GET_FLOAT(v, cd_vert_mask_offset);
+
+ if (G.debug_value == 889) {
+ int ni = BM_ELEM_CD_GET_INT(v, cd_vert_node_offset);
+
+ effective_mask = ni == -1 ? 0.0f : (float)(((ni + debug_pass) * 511) % 64) / 64;
+ }
+
uchar cmask = (uchar)(effective_mask * 255);
GPU_vertbuf_attr_set(vert_buf, g_vbo_id.msk, v_index, &cmask);
*empty_mask = *empty_mask && (cmask == 0);
}
- if (show_vcol) {
+# ifndef NEW_ATTR_SYSTEM
+ if (show_vcol && totvcol > 0) {
+ for (int i = 0; i < totvcol; i++) {
+ ushort vcol[4] = {USHRT_MAX, USHRT_MAX, USHRT_MAX, USHRT_MAX};
+
+ MPropCol *col = BM_ELEM_CD_GET_VOID_P(v, cd_vcol_offsets[i]);
+
+ vcol[0] = unit_float_to_ushort_clamp(col->color[0]);
+ vcol[1] = unit_float_to_ushort_clamp(col->color[1]);
+ vcol[2] = unit_float_to_ushort_clamp(col->color[2]);
+ vcol[3] = unit_float_to_ushort_clamp(col->color[3]);
+
+ // const ushort vcol[4] = {USHRT_MAX, USHRT_MAX, USHRT_MAX, USHRT_MAX};
+ GPU_vertbuf_attr_set(vert_buf, g_vbo_id.col[i], v_index, vcol);
+ }
+ }
+ else if (show_vcol && !g_vbo_id.fast_mode) { // ensure first vcol attribute is not zero
+ const ushort vcol[4] = {USHRT_MAX, USHRT_MAX, USHRT_MAX, USHRT_MAX};
+ GPU_vertbuf_attr_set(vert_buf, g_vbo_id.col[0], v_index, vcol);
+ }
+# else
+ if (show_vcol && totvcol == 0) { // ensure first vcol attribute is not zero
const ushort vcol[4] = {USHRT_MAX, USHRT_MAX, USHRT_MAX, USHRT_MAX};
- GPU_vertbuf_attr_set(vert_buf, g_vbo_id.col, v_index, &vcol);
+ GPU_vertbuf_attr_set(vert_buf, g_vbo_id.col[0], v_index, vcol);
}
+# endif
- /* Add default face sets color to avoid artifacts. */
- const uchar face_set[3] = {UCHAR_MAX, UCHAR_MAX, UCHAR_MAX};
- GPU_vertbuf_attr_set(vert_buf, g_vbo_id.fset, v_index, &face_set);
+ if (!g_vbo_id.fast_mode) {
+ /* Add default face sets color to avoid artifacts. */
+ const uchar face_set[3] = {UCHAR_MAX, UCHAR_MAX, UCHAR_MAX};
+ GPU_vertbuf_attr_set(vert_buf, g_vbo_id.fset, v_index, &face_set);
+ }
+#endif
}
/* Return the total number of vertices that don't have BM_ELEM_HIDDEN set */
-static int gpu_bmesh_vert_visible_count(GSet *bm_unique_verts, GSet *bm_other_verts)
+static int gpu_bmesh_vert_visible_count(TableGSet *bm_unique_verts, TableGSet *bm_other_verts)
{
- GSetIterator gs_iter;
int totvert = 0;
+ BMVert *v;
- GSET_ITER (gs_iter, bm_unique_verts) {
- BMVert *v = BLI_gsetIterator_getKey(&gs_iter);
+ TGSET_ITER (v, bm_unique_verts) {
if (!BM_elem_flag_test(v, BM_ELEM_HIDDEN)) {
totvert++;
}
}
- GSET_ITER (gs_iter, bm_other_verts) {
- BMVert *v = BLI_gsetIterator_getKey(&gs_iter);
+ TGSET_ITER_END
+
+ TGSET_ITER (v, bm_other_verts) {
if (!BM_elem_flag_test(v, BM_ELEM_HIDDEN)) {
totvert++;
}
}
+ TGSET_ITER_END
return totvert;
}
/* Return the total number of visible faces */
-static int gpu_bmesh_face_visible_count(GSet *bm_faces)
+static int gpu_bmesh_face_visible_count(TableGSet *bm_faces, int mat_nr)
{
- GSetIterator gh_iter;
int totface = 0;
+ BMFace *f;
- GSET_ITER (gh_iter, bm_faces) {
- BMFace *f = BLI_gsetIterator_getKey(&gh_iter);
-
- if (!BM_elem_flag_test(f, BM_ELEM_HIDDEN)) {
+ TGSET_ITER (f, bm_faces) {
+ if (!BM_elem_flag_test(f, BM_ELEM_HIDDEN) && f->mat_nr == mat_nr) {
totface++;
}
}
+ TGSET_ITER_END
return totface;
}
void GPU_pbvh_bmesh_buffers_update_free(GPU_PBVH_Buffers *buffers)
{
- if (buffers->smooth) {
- /* Smooth needs to recreate index buffer, so we have to invalidate the batch. */
- GPU_BATCH_DISCARD_SAFE(buffers->triangles);
- GPU_BATCH_DISCARD_SAFE(buffers->lines);
- GPU_INDEXBUF_DISCARD_SAFE(buffers->index_lines_buf);
- GPU_INDEXBUF_DISCARD_SAFE(buffers->index_buf);
+ if (buffers->last_tribuf_tris) {
+ // bmesh indexed drawing frees buffers by itself
+ return;
}
- else {
- GPU_BATCH_DISCARD_SAFE(buffers->lines);
- GPU_INDEXBUF_DISCARD_SAFE(buffers->index_lines_buf);
+
+ GPU_BATCH_DISCARD_SAFE(buffers->lines);
+ GPU_INDEXBUF_DISCARD_SAFE(buffers->index_lines_buf);
+}
+
+static int gpu_pbvh_bmesh_make_vcol_offs(CustomData *vdata,
+ int r_cd_vcols[MAX_MCOL],
+ int r_cd_layers[MAX_MCOL],
+ bool active_only)
+{
+ if (active_only) {
+ int idx = CustomData_get_offset(vdata, CD_PROP_COLOR);
+
+ if (idx >= 0) {
+ r_cd_vcols[0] = idx;
+ r_cd_layers[0] = CustomData_get_active_layer_index(vdata, CD_PROP_COLOR);
+
+ return 1;
+ }
+
+ return 0;
+ }
+
+ int count = 0;
+ int tot = CustomData_number_of_layers(vdata, CD_PROP_COLOR);
+
+ for (int i = 0; i < tot; i++) {
+ int idx = CustomData_get_layer_index_n(vdata, CD_PROP_COLOR, i);
+ // idx = CustomData_get_active_layer_index(vdata, CD_PROP_COLOR);
+
+ if (idx < 0) {
+ printf("eek, corruption in customdata!\n");
+ break;
+ }
+
+ CustomDataLayer *cl = vdata->layers + idx;
+
+ if (cl->flag & CD_FLAG_TEMPORARY) {
+ continue; // ignore original color layer
+ }
+
+ r_cd_layers[count] = idx;
+ r_cd_vcols[count] = CustomData_get_n_offset(vdata, CD_PROP_COLOR, i);
+
+ count++;
+ }
+
+ // ensure render layer is last
+ // draw cache code seems to need this
+ int render = CustomData_get_render_layer_index(vdata, CD_PROP_COLOR);
+
+ for (int i = 0; i < count; i++) {
+ if (r_cd_layers[i] == render) {
+ SWAP(int, r_cd_layers[i], r_cd_layers[count - 1]);
+ SWAP(int, r_cd_vcols[i], r_cd_vcols[count - 1]);
+ break;
+ }
+ }
+
+ return count;
+}
+
+void GPU_pbvh_need_full_render_set(bool state)
+{
+ g_vbo_id.need_full_render = state;
+ g_vbo_id.active_vcol_only = !state;
+}
+
+bool GPU_pbvh_need_full_render_get()
+{
+ return g_vbo_id.need_full_render;
+}
+
+void GPU_pbvh_update_attribute_names(CustomData *vdata,
+ CustomData *ldata,
+ bool need_full_render,
+ bool fast_mode)
+{
+ const bool active_only = !need_full_render;
+
+ debug_pass++;
+
+ GPU_vertformat_clear(&g_vbo_id.format);
+
+ g_vbo_id.fast_mode = fast_mode;
+
+ // g_vbo_id.loop_attrs = build_cd_layers(vdata, )
+ /* Initialize vertex buffer (match 'VertexBufferFormat'). */
+ if (g_vbo_id.format.attr_len == 0) {
+#ifdef QUANTIZED_PERF_TEST
+ g_vbo_id.pos = GPU_vertformat_attr_add(
+ &g_vbo_id.format, "pos", GPU_COMP_U16, 3, GPU_FETCH_INT_TO_FLOAT_UNIT);
+ g_vbo_id.nor = GPU_vertformat_attr_add(
+ &g_vbo_id.format, "nor", GPU_COMP_I8, 3, GPU_FETCH_INT_TO_FLOAT_UNIT);
+#else
+ g_vbo_id.pos = GPU_vertformat_attr_add(
+ &g_vbo_id.format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
+ g_vbo_id.nor = GPU_vertformat_attr_add(
+ &g_vbo_id.format, "nor", GPU_COMP_I16, 3, GPU_FETCH_INT_TO_FLOAT_UNIT);
+#endif
+
+ /* TODO: Do not allocate these `.msk` and `.col` when they are not used. */
+
+#ifdef QUANTIZED_PERF_TEST
+ return;
+#endif
+
+#ifndef GPU_PERF_TEST
+ if (!fast_mode) {
+ g_vbo_id.msk = GPU_vertformat_attr_add(
+ &g_vbo_id.format, "msk", GPU_COMP_U8, 1, GPU_FETCH_INT_TO_FLOAT_UNIT);
+ }
+#endif
+
+ g_vbo_id.totcol = 0;
+
+#ifdef NEW_ATTR_SYSTEM
+ if (g_vbo_id.loop_attrs) {
+ free_cd_layers(g_vbo_id.loop_attrs);
+ }
+ if (g_vbo_id.vertex_attrs) {
+ free_cd_layers(g_vbo_id.vertex_attrs);
+ }
+
+ CDAttrLayers *vattr = NULL, *lattr = NULL;
+ BLI_array_declare(vattr);
+ BLI_array_declare(lattr);
+
+ for (int i = 0; vdata && i < ARRAY_SIZE(cd_vert_layers); i++) {
+ if (!CustomData_has_layer(vdata, cd_vert_layers[i].type)) {
+ continue;
+ }
+
+ CDAttrLayers attr;
+ build_cd_layers(&g_vbo_id.format, &attr, vdata, cd_vert_layers + i);
+ BLI_array_append(vattr, attr);
+ }
+
+ for (int i = 0; ldata && i < ARRAY_SIZE(cd_loop_layers); i++) {
+ if (!CustomData_has_layer(ldata, cd_loop_layers[i].type)) {
+ continue;
+ }
+
+ CDAttrLayers attr;
+ build_cd_layers(&g_vbo_id.format, &attr, ldata, cd_loop_layers + i);
+ BLI_array_append(lattr, attr);
+ }
+
+ g_vbo_id.vertex_attrs = vattr;
+ g_vbo_id.loop_attrs = lattr;
+ g_vbo_id.vertex_attrs_len = BLI_array_len(vattr);
+ g_vbo_id.loop_attrs_len = BLI_array_len(lattr);
+#endif
+
+#if !defined(NEW_ATTR_SYSTEM) && !defined(GPU_PERF_TEST)
+ if (vdata && CustomData_has_layer(vdata, CD_PROP_COLOR)) {
+ const int act = CustomData_get_active_layer_index(vdata, CD_PROP_COLOR);
+ int ci = 0;
+
+ int cd_vcol_offs[MAX_MCOL];
+ int cd_vcol_layers[MAX_MCOL];
+ int totlayer = gpu_pbvh_bmesh_make_vcol_offs(
+ vdata, cd_vcol_offs, cd_vcol_layers, active_only);
+
+ for (int i = 0; i < totlayer; i++) {
+ int idx = cd_vcol_layers[i];
+ CustomDataLayer *cl = vdata->layers + idx;
+
+ if (g_vbo_id.totcol < MAX_MCOL) {
+ g_vbo_id.col[ci++] = GPU_vertformat_attr_add(
+ &g_vbo_id.format, "c", GPU_COMP_U16, 4, GPU_FETCH_INT_TO_FLOAT_UNIT);
+ g_vbo_id.totcol++;
+
+ DRW_make_cdlayer_attr_aliases(&g_vbo_id.format, "c", vdata, cl);
+
+ if (idx == act) {
+ GPU_vertformat_alias_add(&g_vbo_id.format, "ac");
+ }
+ }
+ }
+ }
+
+ // ensure at least one vertex color layer
+ if (!fast_mode && g_vbo_id.totcol == 0) {
+ g_vbo_id.col[0] = GPU_vertformat_attr_add(
+ &g_vbo_id.format, "c", GPU_COMP_U16, 4, GPU_FETCH_INT_TO_FLOAT_UNIT);
+ g_vbo_id.totcol = 1;
+
+ GPU_vertformat_alias_add(&g_vbo_id.format, "ac");
+ }
+#elif !defined(GPU_PERF_TEST)
+ // ensure at least one vertex color layer
+ if (!vdata || !CustomData_has_layer(vdata, CD_PROP_COLOR)) {
+ g_vbo_id.col[0] = GPU_vertformat_attr_add(
+ &g_vbo_id.format, "c", GPU_COMP_U16, 4, GPU_FETCH_INT_TO_FLOAT_UNIT);
+ g_vbo_id.totcol = 1;
+
+ GPU_vertformat_alias_add(&g_vbo_id.format, "ac");
+ }
+
+#endif
+
+#ifndef GPU_PERF_TEST
+ if (!fast_mode) {
+ g_vbo_id.fset = GPU_vertformat_attr_add(
+ &g_vbo_id.format, "fset", GPU_COMP_U8, 3, GPU_FETCH_INT_TO_FLOAT_UNIT);
+ }
+
+ if (ldata && CustomData_has_layer(ldata, CD_MLOOPUV)) {
+ g_vbo_id.uv = GPU_vertformat_attr_add(
+ &g_vbo_id.format, "uvs", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
+ GPU_vertformat_alias_add(&g_vbo_id.format, "u");
+
+ const int cd_uv_index = CustomData_get_layer_index(ldata, CD_MLOOPUV);
+ CustomDataLayer *cl = ldata->layers + cd_uv_index;
+ cl += cl->active;
+
+ DRW_make_cdlayer_attr_aliases(&g_vbo_id.format, "u", ldata, cl);
+ }
+#endif
+ }
+}
+
+static void gpu_flat_vcol_make_vert(float co[3],
+ BMVert *v,
+ GPUVertBuf *vert_buf,
+ int v_index,
+ const int cd_vcol_offsets[MAX_MCOL],
+ int totoffsets,
+ const float fno[3])
+{
+ for (int i = 0; i < totoffsets; i++) {
+ MPropCol *mp = BM_ELEM_CD_GET_VOID_P(v, cd_vcol_offsets[i]);
+ ushort vcol[4];
+
+ // printf(
+ // "%.2f %.2f %.2f %.2f\n", mp->color[0], mp->color[1], mp->color[2], mp->color[3]);
+ vcol[0] = unit_float_to_ushort_clamp(mp->color[0]);
+ vcol[1] = unit_float_to_ushort_clamp(mp->color[1]);
+ vcol[2] = unit_float_to_ushort_clamp(mp->color[2]);
+ vcol[3] = unit_float_to_ushort_clamp(mp->color[3]);
+
+ GPU_vertbuf_attr_set(vert_buf, g_vbo_id.col[i], v_index, vcol);
}
+
+ /* Set coord, normal, and mask */
+ GPU_vertbuf_attr_set(vert_buf, g_vbo_id.pos, v_index, co);
+
+ short no_short[3];
+
+ normal_float_to_short_v3(no_short, fno ? fno : v->no);
+ GPU_vertbuf_attr_set(vert_buf, g_vbo_id.nor, v_index, no_short);
}
/* Creates a vertex buffer (coordinate, normal, color) and, if smooth
* shading, an element index buffer.
* Threaded - do not call any functions that use OpenGL calls! */
-void GPU_pbvh_bmesh_buffers_update(GPU_PBVH_Buffers *buffers,
- BMesh *bm,
- GSet *bm_faces,
- GSet *bm_unique_verts,
- GSet *bm_other_verts,
- const int update_flags)
+static void GPU_pbvh_bmesh_buffers_update_flat_vcol(GPU_PBVH_Buffers *buffers,
+ BMesh *bm,
+ TableGSet *bm_faces,
+ TableGSet *bm_unique_verts,
+ TableGSet *bm_other_verts,
+ PBVHTriBuf *tribuf,
+ const int update_flags,
+ const int cd_vert_node_offset,
+ int face_sets_color_seed,
+ int face_sets_color_default,
+ short mat_nr)
{
- const bool show_mask = (update_flags & GPU_PBVH_BUFFERS_SHOW_MASK) != 0;
- const bool show_vcol = (update_flags & GPU_PBVH_BUFFERS_SHOW_VCOL) != 0;
+ bool active_vcol_only = g_vbo_id.active_vcol_only;
+
+ const bool show_face_sets = CustomData_has_layer(&bm->pdata, CD_SCULPT_FACE_SETS) &&
+ (update_flags & GPU_PBVH_BUFFERS_SHOW_SCULPT_FACE_SETS) != 0;
+
int tottri, totvert;
bool empty_mask = true;
- BMFace *f = NULL;
+ int cd_fset_offset = CustomData_get_offset(&bm->pdata, CD_SCULPT_FACE_SETS);
- /* Count visible triangles */
- tottri = gpu_bmesh_face_visible_count(bm_faces);
+ int cd_vcols[MAX_MCOL];
+ int cd_vcol_layers[MAX_MCOL];
- if (buffers->smooth) {
- /* Count visible vertices */
- totvert = gpu_bmesh_vert_visible_count(bm_unique_verts, bm_other_verts);
- }
- else {
- totvert = tottri * 3;
- }
+ const int cd_vcol_count = gpu_pbvh_bmesh_make_vcol_offs(
+ &bm->vdata, cd_vcols, cd_vcol_layers, active_vcol_only);
+
+ /* Count visible triangles */
+ tottri = tribuf->tottri * 6;
+ totvert = tottri * 3;
if (!tottri) {
- if (BLI_gset_len(bm_faces) != 0) {
+ if (BLI_table_gset_len(bm_faces) != 0) {
/* Node is just hidden. */
}
else {
@@ -919,6 +1491,8 @@ void GPU_pbvh_bmesh_buffers_update(GPU_PBVH_Buffers *buffers,
/* TODO: make mask layer optional for bmesh buffer. */
const int cd_vert_mask_offset = CustomData_get_offset(&bm->vdata, CD_PAINT_MASK);
+ bool default_face_set = true;
+
/* Fill vertex buffer */
if (!gpu_pbvh_vert_buf_data_set(buffers, totvert)) {
/* Memory map failed */
@@ -927,123 +1501,650 @@ void GPU_pbvh_bmesh_buffers_update(GPU_PBVH_Buffers *buffers,
int v_index = 0;
- if (buffers->smooth) {
- /* Fill the vertex and triangle buffer in one pass over faces. */
- GPUIndexBufBuilder elb, elb_lines;
- GPU_indexbuf_init(&elb, GPU_PRIM_TRIS, tottri, totvert);
- GPU_indexbuf_init(&elb_lines, GPU_PRIM_LINES, tottri * 3, totvert);
+ GPUIndexBufBuilder elb_lines;
+ GPU_indexbuf_init(&elb_lines, GPU_PRIM_LINES, tottri * 3, tottri * 3);
+
+ for (int i = 0; i < tribuf->tottri; i++) {
+ PBVHTri *tri = tribuf->tris + i;
+ BMFace *f = (BMFace *)tri->f.i;
- GHash *bm_vert_to_index = BLI_ghash_int_new_ex("bm_vert_to_index", totvert);
+ if (f->mat_nr != mat_nr) {
+ continue;
+ }
- GSetIterator gs_iter;
- GSET_ITER (gs_iter, bm_faces) {
- f = BLI_gsetIterator_getKey(&gs_iter);
+ if (!BM_elem_flag_test(f, BM_ELEM_HIDDEN)) {
+ BMVert *v[3];
- if (!BM_elem_flag_test(f, BM_ELEM_HIDDEN)) {
- BMVert *v[3];
- BM_face_as_array_vert_tri(f, v);
+ float fmask = 0.0f;
- uint idx[3];
- for (int i = 0; i < 3; i++) {
- void **idx_p;
- if (!BLI_ghash_ensure_p(bm_vert_to_index, v[i], &idx_p)) {
- /* Add vertex to the vertex buffer each time a new one is encountered */
- *idx_p = POINTER_FROM_UINT(v_index);
+ v[0] = (BMVert *)tribuf->verts[tri->v[0]].i;
+ v[1] = (BMVert *)tribuf->verts[tri->v[1]].i;
+ v[2] = (BMVert *)tribuf->verts[tri->v[2]].i;
- gpu_bmesh_vert_to_buffer_copy(v[i],
- buffers->vert_buf,
- v_index,
- NULL,
- NULL,
- cd_vert_mask_offset,
- show_mask,
- show_vcol,
- &empty_mask);
-
- idx[i] = v_index;
- v_index++;
- }
- else {
- /* Vertex already in the vertex buffer, just get the index. */
- idx[i] = POINTER_AS_UINT(*idx_p);
- }
+ /* Average mask value */
+ for (int j = 0; j < 3; j++) {
+ fmask += BM_ELEM_CD_GET_FLOAT(v[j], cd_vert_mask_offset);
+ }
+ fmask /= 3.0f;
+
+ uchar face_set_color[4] = {UCHAR_MAX, UCHAR_MAX, UCHAR_MAX, UCHAR_MAX};
+
+ if (show_face_sets && cd_fset_offset >= 0) {
+ const int fset = BM_ELEM_CD_GET_INT(f, cd_fset_offset);
+
+ /* Skip for the default color Face Set to render it white. */
+ if (fset != face_sets_color_default) {
+ BKE_paint_face_set_overlay_color_get(fset, face_sets_color_seed, face_set_color);
+ default_face_set = false;
+ }
+ }
+
+ float cent[3] = {0.0f, 0.0f, 0.0f};
+ add_v3_v3(cent, v[0]->co);
+ add_v3_v3(cent, v[1]->co);
+ add_v3_v3(cent, v[2]->co);
+ mul_v3_fl(cent, 1.0 / 3.0);
+
+ float cos[7][3];
+
+ copy_v3_v3(cos[0], v[0]->co);
+ copy_v3_v3(cos[1], v[1]->co);
+ copy_v3_v3(cos[2], v[2]->co);
+
+ copy_v3_v3(cos[6], cent);
+
+ interp_v3_v3v3(cos[3], v[0]->co, v[1]->co, 0.5f);
+ interp_v3_v3v3(cos[4], v[1]->co, v[2]->co, 0.5f);
+ interp_v3_v3v3(cos[5], v[2]->co, v[0]->co, 0.5f);
+
+ const int v_start = v_index;
+
+ for (int j = 0; j < 3; j++) {
+ int next = 3 + ((j) % 3);
+ int prev = 3 + ((j + 3 - 1) % 3);
+
+ gpu_flat_vcol_make_vert(
+ v[j]->co, v[j], buffers->vert_buf, v_index, cd_vcols, cd_vcol_count, f->no);
+ gpu_flat_vcol_make_vert(
+ cos[next], v[j], buffers->vert_buf, v_index + 1, cd_vcols, cd_vcol_count, f->no);
+ gpu_flat_vcol_make_vert(
+ cos[6], v[j], buffers->vert_buf, v_index + 2, cd_vcols, cd_vcol_count, f->no);
+
+ gpu_flat_vcol_make_vert(
+ v[j]->co, v[j], buffers->vert_buf, v_index + 3, cd_vcols, cd_vcol_count, f->no);
+ gpu_flat_vcol_make_vert(
+ cos[6], v[j], buffers->vert_buf, v_index + 4, cd_vcols, cd_vcol_count, f->no);
+ gpu_flat_vcol_make_vert(
+ cos[prev], v[j], buffers->vert_buf, v_index + 5, cd_vcols, cd_vcol_count, f->no);
+
+ /*
+ v1
+ |\
+ | \
+ v3 v4
+ | v6 \
+ | \
+ v0---v5---v2
+ */
+
+ next = j == 2 ? v_start : v_index + 6;
+
+ if (tri->eflag & 1) {
+ GPU_indexbuf_add_line_verts(&elb_lines, v_index, next);
+ // 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 + 0);
+ }
+
+ if (tri->eflag & 2) {
+ // GPU_indexbuf_add_line_verts(&elb_lines, v_index + 1, v_index + 2);
}
- GPU_indexbuf_add_tri_verts(&elb, idx[0], idx[1], idx[2]);
+ if (tri->eflag & 4) {
+ // GPU_indexbuf_add_line_verts(&elb_lines, v_index + 2, v_index + 0);
+ }
- GPU_indexbuf_add_line_verts(&elb_lines, idx[0], idx[1]);
- GPU_indexbuf_add_line_verts(&elb_lines, idx[1], idx[2]);
- GPU_indexbuf_add_line_verts(&elb_lines, idx[2], idx[0]);
+ v_index += 6;
}
}
+ }
- BLI_ghash_free(bm_vert_to_index, NULL, NULL);
+ buffers->index_lines_buf = GPU_indexbuf_build(&elb_lines);
+ buffers->tot_tri = tottri;
- buffers->tot_tri = tottri;
- if (buffers->index_buf == NULL) {
- buffers->index_buf = GPU_indexbuf_build(&elb);
+ /* Get material index from the last face we iterated on. */
+ buffers->material_index = mat_nr;
+
+ buffers->show_overlay = (!empty_mask || !default_face_set) && !g_vbo_id.fast_mode;
+
+ gpu_pbvh_batch_init(buffers, GPU_PRIM_TRIS);
+}
+
+static void GPU_pbvh_bmesh_buffers_update_indexed(GPU_PBVH_Buffers *buffers,
+ BMesh *bm,
+ TableGSet *bm_faces,
+ TableGSet *bm_unique_verts,
+ TableGSet *bm_other_verts,
+ PBVHTriBuf *tribuf,
+ const int update_flags,
+ const int cd_vert_node_offset,
+ int face_sets_color_seed,
+ int face_sets_color_default,
+ bool flat_vcol,
+ short mat_nr)
+{
+
+ bool active_vcol_only = g_vbo_id.active_vcol_only;
+
+ const bool show_mask = (update_flags & GPU_PBVH_BUFFERS_SHOW_MASK) != 0 && !g_vbo_id.fast_mode;
+ bool show_vcol = (update_flags & GPU_PBVH_BUFFERS_SHOW_VCOL) != 0;
+
+ if (g_vbo_id.totcol == 0 && g_vbo_id.fast_mode) {
+ show_vcol = false;
+ }
+
+ bool need_indexed = buffers->last_tribuf_tris != tribuf->tris;
+
+ buffers->last_tribuf_tris = tribuf->tris;
+
+ int tottri, totvert;
+ bool empty_mask = true;
+
+ int cd_vcols[MAX_MCOL];
+ int cd_vcol_layers[MAX_MCOL];
+
+ int cd_vcol_count = gpu_pbvh_bmesh_make_vcol_offs(
+ &bm->vdata, cd_vcols, cd_vcol_layers, active_vcol_only);
+
+ /* Count visible triangles */
+ tottri = tribuf->tottri;
+
+ /* Count visible vertices */
+ totvert = tribuf->totvert;
+
+ if (!tottri) {
+ if (BLI_table_gset_len(bm_faces) != 0) {
+ /* Node is just hidden. */
}
else {
- GPU_indexbuf_build_in_place(&elb, buffers->index_buf);
+ buffers->clear_bmesh_on_flush = true;
}
- buffers->index_lines_buf = GPU_indexbuf_build(&elb_lines);
+ buffers->tot_tri = 0;
+ return;
+ }
+
+ /* TODO, make mask layer optional for bmesh buffer */
+ const int cd_vert_mask_offset = CustomData_get_offset(&bm->vdata, CD_PAINT_MASK);
+
+ bool default_face_set = true;
+
+ /* Fill vertex buffer */
+ if (!gpu_pbvh_vert_buf_data_set(buffers, totvert)) {
+ /* Memory map failed */
+ return;
+ }
+
+#ifdef QUANTIZED_PERF_TEST
+ GPUVertBuf *vert_buf = buffers->vert_buf;
+
+ float min[3];
+ float max[3];
+ float mat[4][4];
+ float imat[4][4];
+ float scale[3];
+
+ INIT_MINMAX(min, max);
+ for (int i = 0; i < tribuf->totvert; i++) {
+ BMVert *v = (BMVert *)tribuf->verts[i].i;
+
+ minmax_v3v3_v3(min, max, v->co);
+ }
+
+ sub_v3_v3v3(scale, max, min);
+
+ scale[0] = scale[0] != 0.0f ? 1.0f / scale[0] : 0.0f;
+ scale[1] = scale[1] != 0.0f ? 1.0f / scale[1] : 0.0f;
+ scale[2] = scale[2] != 0.0f ? 1.0f / scale[2] : 0.0f;
+
+ memset((float *)mat, 0, sizeof(float) * 16);
+
+ mat[0][0] = scale[0];
+ mat[1][1] = scale[1];
+ mat[2][2] = scale[2];
+
+ mat[3][0] = -min[0] * scale[0];
+ mat[3][1] = -min[1] * scale[1];
+ mat[3][2] = -min[2] * scale[2];
+
+ mat[3][3] = 1.0f;
+
+ invert_m4_m4(imat, mat);
+#endif
+
+ for (int i = 0; i < tribuf->totvert; i++) {
+ BMVert *v = (BMVert *)tribuf->verts[i].i;
+
+#ifdef QUANTIZED_PERF_TEST
+ float co[3];
+ copy_v3_v3(co, v->co);
+ mul_v3_m4v3(co, mat, co);
+ // sub_v3_v3(co, tribuf->min);
+ // mul_v3_v3(co, scale);
+
+ // normal_float_to_short_
+ unsigned short co_short[3];
+ co_short[0] = (unsigned short)(co[0] * 65535.0f);
+ co_short[1] = (unsigned short)(co[1] * 65535.0f);
+ co_short[2] = (unsigned short)(co[2] * 65535.0f);
+
+ GPU_vertbuf_attr_set(vert_buf, g_vbo_id.pos, i, co_short);
+
+ signed char no_short[3];
+ // normal_float_to_short_v3(no_short, v->no);
+ no_short[0] = (signed char)(v->no[0] * 127.0f);
+ no_short[1] = (signed char)(v->no[1] * 127.0f);
+ no_short[2] = (signed char)(v->no[2] * 127.0f);
+
+ GPU_vertbuf_attr_set(vert_buf, g_vbo_id.nor, i, no_short);
+#else
+ gpu_bmesh_vert_to_buffer_copy(v,
+ buffers->vert_buf,
+ i,
+ NULL,
+ NULL,
+ cd_vert_mask_offset,
+ cd_vert_node_offset,
+ show_mask,
+ show_vcol,
+ &empty_mask,
+ cd_vcols,
+ cd_vcol_count);
+#endif
+ }
+
+ if (!need_indexed) {
+ buffers->material_index = mat_nr;
+ buffers->show_overlay = (!empty_mask || !default_face_set) && !g_vbo_id.fast_mode;
+
+ gpu_pbvh_batch_init(buffers, GPU_PRIM_TRIS);
+ return;
+ }
+
+ GPU_BATCH_DISCARD_SAFE(buffers->triangles);
+ GPU_BATCH_DISCARD_SAFE(buffers->lines);
+ GPU_INDEXBUF_DISCARD_SAFE(buffers->index_lines_buf);
+ GPU_INDEXBUF_DISCARD_SAFE(buffers->index_buf);
+
+ /* Fill the vertex and triangle buffer in one pass over faces. */
+ GPUIndexBufBuilder elb, elb_lines;
+ GPU_indexbuf_init(&elb, GPU_PRIM_TRIS, tottri, totvert);
+ GPU_indexbuf_init(&elb_lines, GPU_PRIM_LINES, tottri * 3, totvert);
+
+ for (int i = 0; i < tribuf->tottri; i++) {
+ PBVHTri *tri = tribuf->tris + i;
+
+ GPU_indexbuf_add_tri_verts(&elb, tri->v[0], tri->v[1], tri->v[2]);
+
+#ifndef QUANTIZED_PERF_TEST
+ GPU_indexbuf_add_line_verts(&elb_lines, tri->v[0], tri->v[1]);
+ GPU_indexbuf_add_line_verts(&elb_lines, tri->v[1], tri->v[2]);
+ GPU_indexbuf_add_line_verts(&elb_lines, tri->v[2], tri->v[0]);
+#endif
+ }
+
+ buffers->tot_tri = tottri;
+
+ if (buffers->index_buf == NULL) {
+ buffers->index_buf = GPU_indexbuf_build(&elb);
}
else {
- GSetIterator gs_iter;
+ GPU_indexbuf_build_in_place(&elb, buffers->index_buf);
+ }
+ buffers->index_lines_buf = GPU_indexbuf_build(&elb_lines);
- GPUIndexBufBuilder elb_lines;
- GPU_indexbuf_init(&elb_lines, GPU_PRIM_LINES, tottri * 3, tottri * 3);
+ buffers->material_index = mat_nr;
+ buffers->show_overlay = (!empty_mask || !default_face_set) && !g_vbo_id.fast_mode;
- GSET_ITER (gs_iter, bm_faces) {
- f = BLI_gsetIterator_getKey(&gs_iter);
+ gpu_pbvh_batch_init(buffers, GPU_PRIM_TRIS);
- BLI_assert(f->len == 3);
+#ifdef QUANTIZED_PERF_TEST
+ copy_m4_m4(buffers->matrix, imat);
+#endif
+}
- if (!BM_elem_flag_test(f, BM_ELEM_HIDDEN)) {
- BMVert *v[3];
- float fmask = 0.0f;
- int i;
+/* Creates a vertex buffer (coordinate, normal, color) and, if smooth
+ * shading, an element index buffer.
+ * Threaded - do not call any functions that use OpenGL calls! */
+void GPU_pbvh_bmesh_buffers_update(GPU_PBVH_Buffers *buffers,
+ BMesh *bm,
+ TableGSet *bm_faces,
+ TableGSet *bm_unique_verts,
+ TableGSet *bm_other_verts,
+ PBVHTriBuf *tribuf,
+ const int update_flags,
+ const int cd_vert_node_offset,
+ int face_sets_color_seed,
+ int face_sets_color_default,
+ bool flat_vcol,
+ short mat_nr)
+{
- BM_face_as_array_vert_tri(f, v);
+ bool active_vcol_only = g_vbo_id.active_vcol_only;
+
+ if (flat_vcol && CustomData_has_layer(&bm->vdata, CD_PROP_COLOR)) {
+ GPU_pbvh_bmesh_buffers_update_flat_vcol(buffers,
+ bm,
+ bm_faces,
+ bm_unique_verts,
+ bm_other_verts,
+ tribuf,
+ update_flags,
+ cd_vert_node_offset,
+ face_sets_color_seed,
+ face_sets_color_default,
+ mat_nr);
+ return;
+ }
- /* Average mask value */
- for (i = 0; i < 3; i++) {
- fmask += BM_ELEM_CD_GET_FLOAT(v[i], cd_vert_mask_offset);
- }
- fmask /= 3.0f;
-
- GPU_indexbuf_add_line_verts(&elb_lines, v_index + 0, 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 + 0);
-
- for (i = 0; i < 3; i++) {
- gpu_bmesh_vert_to_buffer_copy(v[i],
- buffers->vert_buf,
- v_index++,
- f->no,
- &fmask,
- cd_vert_mask_offset,
- show_mask,
- show_vcol,
- &empty_mask);
+ const bool have_uv = CustomData_has_layer(&bm->ldata, CD_MLOOPUV);
+ const bool show_mask = (update_flags & GPU_PBVH_BUFFERS_SHOW_MASK) != 0 && !g_vbo_id.fast_mode;
+ const bool show_face_sets = CustomData_has_layer(&bm->pdata, CD_SCULPT_FACE_SETS) &&
+ (update_flags & GPU_PBVH_BUFFERS_SHOW_SCULPT_FACE_SETS) != 0 &&
+ !g_vbo_id.fast_mode;
+
+ int tottri, totvert;
+ bool empty_mask = true;
+ int cd_fset_offset = CustomData_get_offset(&bm->pdata, CD_SCULPT_FACE_SETS);
+
+ int cd_vcols[MAX_MCOL];
+ int cd_vcol_layers[MAX_MCOL];
+
+ int cd_vcol_count = gpu_pbvh_bmesh_make_vcol_offs(
+ &bm->vdata, cd_vcols, cd_vcol_layers, active_vcol_only);
+
+ /* Count visible triangles */
+ if (buffers->smooth && !have_uv) {
+ GPU_pbvh_bmesh_buffers_update_indexed(buffers,
+ bm,
+ bm_faces,
+ bm_unique_verts,
+ bm_other_verts,
+ tribuf,
+ update_flags,
+ cd_vert_node_offset,
+ face_sets_color_seed,
+ face_sets_color_default,
+ flat_vcol,
+ mat_nr);
+ return;
+ }
+
+ buffers->last_tribuf_tris = NULL;
+
+ /* TODO, make mask layer optional for bmesh buffer */
+ const int cd_vert_mask_offset = CustomData_get_offset(&bm->vdata, CD_PAINT_MASK);
+ const int cd_mcol_offset = CustomData_get_offset(&bm->ldata, CD_MLOOPCOL);
+ const int cd_uv_offset = CustomData_get_offset(&bm->ldata, CD_MLOOPUV);
+
+ bool default_face_set = true;
+
+#ifdef DYNTOPO_DYNAMIC_TESS
+ tottri = tribuf->tottri;
+ totvert = tottri * 3;
+
+ if (!tottri) {
+ /* empty node (i.e. not just hidden)? */
+ if (BLI_table_gset_len(bm_faces) == 0) {
+ buffers->clear_bmesh_on_flush = true;
+ }
+
+ buffers->tot_tri = 0;
+ return;
+ }
+ /* Fill vertex buffer */
+ if (!gpu_pbvh_vert_buf_data_set(buffers, totvert)) {
+ /* Memory map failed */
+ return;
+ }
+
+ int v_index = 0;
+
+ GPUIndexBufBuilder elb_lines;
+ GPU_indexbuf_init(&elb_lines, GPU_PRIM_LINES, tottri * 3, tottri * 3);
+
+ for (int i = 0; i < tribuf->tottri; i++) {
+ PBVHTri *tri = tribuf->tris + i;
+ BMFace *f = (BMFace *)tri->f.i;
+ BMLoop **l = (BMLoop **)tri->l;
+ BMVert *v[3];
+
+ if (BM_elem_flag_test(f, BM_ELEM_HIDDEN)) {
+ continue;
+ }
+
+ v[0] = l[0]->v;
+ v[1] = l[1]->v;
+ v[2] = l[2]->v;
+
+ float fmask = 0.0f;
+ int i;
+
+ /* Average mask value */
+ for (i = 0; i < 3; i++) {
+ fmask += BM_ELEM_CD_GET_FLOAT(v[i], cd_vert_mask_offset);
+ }
+ fmask /= 3.0f;
+
+ if (tri->eflag & 1) {
+ GPU_indexbuf_add_line_verts(&elb_lines, v_index + 0, v_index + 1);
+ }
+
+ if (tri->eflag & 2) {
+ GPU_indexbuf_add_line_verts(&elb_lines, v_index + 1, v_index + 2);
+ }
+
+ if (tri->eflag & 4) {
+ GPU_indexbuf_add_line_verts(&elb_lines, v_index + 2, v_index + 0);
+ }
+
+ uchar face_set_color[4] = {UCHAR_MAX, UCHAR_MAX, UCHAR_MAX, UCHAR_MAX};
+
+ if (show_face_sets && cd_fset_offset >= 0) {
+ const int fset = BM_ELEM_CD_GET_INT(f, cd_fset_offset);
+
+ /* Skip for the default color Face Set to render it white. */
+ if (fset != face_sets_color_default) {
+ BKE_paint_face_set_overlay_color_get(fset, face_sets_color_seed, face_set_color);
+ default_face_set = false;
+ }
+ }
+
+ for (int j = 0; j < 3; j++) {
+ float *no = buffers->smooth ? v[j]->no : f->no;
+
+ gpu_bmesh_vert_to_buffer_copy(v[j],
+ buffers->vert_buf,
+ v_index,
+ no,
+ &fmask,
+ cd_vert_mask_offset,
+ cd_vert_node_offset,
+ show_mask,
+ false,
+ &empty_mask,
+ NULL,
+ 0);
+# ifndef GPU_PERF_TEST
+ if (cd_vcol_count >= 0) {
+ for (int k = 0; k < cd_vcol_count; k++) {
+ MPropCol *mp = BM_ELEM_CD_GET_VOID_P(l[j]->v, cd_vcols[k]);
+ ushort vcol[4];
+
+ // printf(
+ // "%.2f %.2f %.2f %.2f\n", mp->color[0], mp->color[1], mp->color[2],
+ // mp->color[3]);
+ vcol[0] = unit_float_to_ushort_clamp(mp->color[0]);
+ vcol[1] = unit_float_to_ushort_clamp(mp->color[1]);
+ vcol[2] = unit_float_to_ushort_clamp(mp->color[2]);
+ vcol[3] = unit_float_to_ushort_clamp(mp->color[3]);
+
+ GPU_vertbuf_attr_set(buffers->vert_buf, g_vbo_id.col[k], v_index, vcol);
}
}
+ else if (cd_mcol_offset >= 0) {
+ ushort vcol[4];
+
+ MLoopCol *ml = BM_ELEM_CD_GET_VOID_P(l[j], cd_mcol_offset);
+
+ vcol[0] = ml->r * 257;
+ vcol[1] = ml->g * 257;
+ vcol[2] = ml->b * 257;
+ vcol[3] = ml->a * 257;
+
+ GPU_vertbuf_attr_set(buffers->vert_buf, g_vbo_id.col[0], v_index, vcol);
+ }
+
+ if (have_uv) {
+ MLoopUV *mu = BM_ELEM_CD_GET_VOID_P(l[j], cd_uv_offset);
+ GPU_vertbuf_attr_set(buffers->vert_buf, g_vbo_id.uv, v_index, mu->uv);
+ }
+
+ if (show_face_sets) {
+ GPU_vertbuf_attr_set(buffers->vert_buf, g_vbo_id.fset, v_index, face_set_color);
+ }
+# endif
+ v_index++;
+ }
+ }
+#else
+ tottri = tribuf->tottri;
+ totvert = tottri * 3;
+
+ if (!tottri) {
+ /* empty node (i.e. not just hidden)? */
+ if (!BLI_table_gset_len(bm_faces) != 0) {
+ buffers->clear_bmesh_on_flush = true;
}
- buffers->index_lines_buf = GPU_indexbuf_build(&elb_lines);
- buffers->tot_tri = tottri;
+ buffers->tot_tri = 0;
+ return;
+ }
+ /* Fill vertex buffer */
+ if (!gpu_pbvh_vert_buf_data_set(buffers, totvert)) {
+ /* Memory map failed */
+ return;
}
- /* Get material index from the last face we iterated on. */
- buffers->material_index = (f) ? f->mat_nr : 0;
+ int v_index = 0;
+
+ GPUIndexBufBuilder elb_lines;
+ GPU_indexbuf_init(&elb_lines, GPU_PRIM_LINES, tottri * 3, tottri * 3);
+
+ TGSET_ITER (f, bm_faces) {
+ BLI_assert(f->len == 3);
+ if (f->mat_nr != mat_nr) {
+ continue;
+ }
+
+ if (!BM_elem_flag_test(f, BM_ELEM_HIDDEN)) {
+ BMVert *v[3];
+ BMLoop *l[3] = {f->l_first, f->l_first->next, f->l_first->prev};
+
+ float fmask = 0.0f;
+ int i;
+
+ BM_face_as_array_vert_tri(f, v);
+
+ /* Average mask value */
+ for (i = 0; i < 3; i++) {
+ fmask += BM_ELEM_CD_GET_FLOAT(v[i], cd_vert_mask_offset);
+ }
+ fmask /= 3.0f;
+
+ GPU_indexbuf_add_line_verts(&elb_lines, v_index + 0, 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 + 0);
+
+ uchar face_set_color[4] = {UCHAR_MAX, UCHAR_MAX, UCHAR_MAX, UCHAR_MAX};
- buffers->show_overlay = !empty_mask;
+ if (show_face_sets && cd_fset_offset >= 0) {
+ const int fset = BM_ELEM_CD_GET_INT(f, cd_fset_offset);
+
+ /* Skip for the default color Face Set to render it white. */
+ if (fset != face_sets_color_default) {
+ BKE_paint_face_set_overlay_color_get(fset, face_sets_color_seed, face_set_color);
+ default_face_set = false;
+ }
+ }
+
+ for (i = 0; i < 3; i++) {
+ float *no = buffers->smooth ? v[i]->no : f->no;
+
+ gpu_bmesh_vert_to_buffer_copy(v[i],
+ buffers->vert_buf,
+ v_index,
+ no,
+ &fmask,
+ cd_vert_mask_offset,
+ cd_vert_node_offset,
+ show_mask,
+ false,
+ &empty_mask,
+ NULL,
+ 0);
+
+ if (cd_vcol_count >= 0) {
+ for (int j = 0; j < cd_vcol_count; j++) {
+ MPropCol *mp = BM_ELEM_CD_GET_VOID_P(l[i]->v, cd_vcols[j]);
+ ushort vcol[4];
+
+ // printf(
+ // "%.2f %.2f %.2f %.2f\n", mp->color[0], mp->color[1], mp->color[2],
+ // mp->color[3]);
+ vcol[0] = unit_float_to_ushort_clamp(mp->color[0]);
+ vcol[1] = unit_float_to_ushort_clamp(mp->color[1]);
+ vcol[2] = unit_float_to_ushort_clamp(mp->color[2]);
+ vcol[3] = unit_float_to_ushort_clamp(mp->color[3]);
+
+ GPU_vertbuf_attr_set(buffers->vert_buf, g_vbo_id.col[j], v_index, vcol);
+ }
+ }
+ else if (cd_mcol_offset >= 0) {
+ ushort vcol[4];
+
+ MLoopCol *ml = BM_ELEM_CD_GET_VOID_P(l[i], cd_mcol_offset);
+
+ vcol[0] = ml->r * 257;
+ vcol[1] = ml->g * 257;
+ vcol[2] = ml->b * 257;
+ vcol[3] = ml->a * 257;
+
+ GPU_vertbuf_attr_set(buffers->vert_buf, g_vbo_id.col[0], v_index, vcol);
+ }
+
+ if (have_uv) {
+ MLoopUV *mu = BM_ELEM_CD_GET_VOID_P(l[i], cd_uv_offset);
+ GPU_vertbuf_attr_set(buffers->vert_buf, g_vbo_id.uv, v_index, mu->uv);
+ }
+
+ GPU_vertbuf_attr_set(buffers->vert_buf, g_vbo_id.fset, v_index, face_set_color);
+
+ v_index++;
+ }
+ }
+ }
+ TGSET_ITER_END
+#endif
+
+ buffers->index_lines_buf = GPU_indexbuf_build(&elb_lines);
+ buffers->tot_tri = tottri;
+
+ /* Get material index from the last face we iterated on. */
+ buffers->material_index = mat_nr;
+ buffers->show_overlay = (!empty_mask || !default_face_set) && !g_vbo_id.fast_mode;
gpu_pbvh_batch_init(buffers, GPU_PRIM_TRIS);
}
-/** \} */
-
/* -------------------------------------------------------------------- */
/** \name Generic
* \{ */
@@ -1055,8 +2156,8 @@ GPU_PBVH_Buffers *GPU_pbvh_bmesh_buffers_build(bool smooth_shading)
buffers = MEM_callocN(sizeof(GPU_PBVH_Buffers), "GPU_Buffers");
buffers->use_bmesh = true;
- buffers->smooth = smooth_shading;
- buffers->show_overlay = true;
+ buffers->smooth = smooth_shading || g_vbo_id.fast_mode;
+ buffers->show_overlay = (!g_vbo_id.fast_mode) && !g_vbo_id.fast_mode;
return buffers;
}
@@ -1072,7 +2173,7 @@ GPUBatch *GPU_pbvh_buffers_batch_get(GPU_PBVH_Buffers *buffers, bool fast, bool
bool GPU_pbvh_buffers_has_overlays(GPU_PBVH_Buffers *buffers)
{
- return buffers->show_overlay;
+ return buffers->show_overlay && !g_vbo_id.fast_mode;
}
short GPU_pbvh_buffers_material_index_get(GPU_PBVH_Buffers *buffers)
diff --git a/source/blender/gpu/intern/gpu_node_graph.c b/source/blender/gpu/intern/gpu_node_graph.c
index 585cb296bac..3169e419f19 100644
--- a/source/blender/gpu/intern/gpu_node_graph.c
+++ b/source/blender/gpu/intern/gpu_node_graph.c
@@ -662,6 +662,8 @@ bool GPU_link(GPUMaterial *mat, const char *name, ...)
return true;
}
+#include "BLI_compiler_attrs.h"
+
bool GPU_stack_link(GPUMaterial *material,
bNode *bnode,
const char *name,
diff --git a/source/blender/io/alembic/exporter/abc_writer_mesh.cc b/source/blender/io/alembic/exporter/abc_writer_mesh.cc
index 8f410978211..781bba363c4 100644
--- a/source/blender/io/alembic/exporter/abc_writer_mesh.cc
+++ b/source/blender/io/alembic/exporter/abc_writer_mesh.cc
@@ -168,7 +168,7 @@ void ABCGenericMeshWriter::do_write(HierarchyContext &context)
struct BMeshCreateParams bmcp = {false};
struct BMeshFromMeshParams bmfmp = {true, false, false, 0};
- BMesh *bm = BKE_mesh_to_bmesh_ex(mesh, &bmcp, &bmfmp);
+ BMesh *bm = BKE_mesh_to_bmesh_ex(object, mesh, &bmcp, &bmfmp);
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 60c4a9bad13..f250fc8557e 100644
--- a/source/blender/io/collada/collada_utils.cpp
+++ b/source/blender/io/collada/collada_utils.cpp
@@ -448,12 +448,12 @@ void bc_triangulate_mesh(Mesh *me)
BMesh *bm = BM_mesh_create(&bm_mesh_allocsize_default, &bm_create_params);
BMeshFromMeshParams bm_from_me_params = {0};
bm_from_me_params.calc_face_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 = {0};
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/makesdna/DNA_brush_defaults.h b/source/blender/makesdna/DNA_brush_defaults.h
index c3f171977a9..a73fcc7c1d3 100644
--- a/source/blender/makesdna/DNA_brush_defaults.h
+++ b/source/blender/makesdna/DNA_brush_defaults.h
@@ -42,7 +42,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, \
@@ -111,6 +117,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 398534ba8f0..e65e488affe 100644
--- a/source/blender/makesdna/DNA_brush_enums.h
+++ b/source/blender/makesdna/DNA_brush_enums.h
@@ -343,6 +343,8 @@ typedef enum eAutomasking_flag {
BRUSH_AUTOMASKING_FACE_SETS = (1 << 1),
BRUSH_AUTOMASKING_BOUNDARY_EDGES = (1 << 2),
BRUSH_AUTOMASKING_BOUNDARY_FACE_SETS = (1 << 3),
+ BRUSH_AUTOMASKING_CONCAVITY = (1 << 4),
+ BRUSH_AUTOMASKING_INVERT_CONCAVITY = (1 << 5)
} eAutomasking_flag;
typedef enum ePaintBrush_flag {
@@ -422,9 +424,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 {
@@ -485,6 +503,11 @@ typedef enum eBrushSculptTool {
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
} eBrushSculptTool;
/* Brush.uv_sculpt_tool */
@@ -494,6 +517,8 @@ typedef enum eBrushUVSculptTool {
UV_SCULPT_TOOL_PINCH = 2,
} eBrushUVSculptTool;
+#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, \
@@ -518,13 +543,9 @@ typedef enum eBrushUVSculptTool {
#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_FAIRING, \
- SCULPT_TOOL_DRAW_SHARP, \
SCULPT_TOOL_SLIDE_RELAX, \
SCULPT_TOOL_ELASTIC_DEFORM, \
SCULPT_TOOL_BOUNDARY, \
@@ -535,15 +556,15 @@ typedef enum eBrushUVSculptTool {
\
/* These brushes could handle dynamic topology, \ \
* but user feedback indicates it's better not to */ \
- SCULPT_TOOL_SMOOTH, \
+ SCULPT_TOOL_VCOL_BOUNDARY, \
+ SCULPT_TOOL_UV_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)
@@ -634,6 +655,41 @@ enum {
PAINT_FALLOFF_SHAPE_TUBE = 1,
};
+// 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
+};
+
#define MAX_BRUSH_PIXEL_RADIUS 500
#define GP_MAX_BRUSH_PIXEL_RADIUS 1000
diff --git a/source/blender/makesdna/DNA_brush_types.h b/source/blender/makesdna/DNA_brush_types.h
index 28f6d6c9ecc..f49a08a761f 100644
--- a/source/blender/makesdna/DNA_brush_types.h
+++ b/source/blender/makesdna/DNA_brush_types.h
@@ -36,6 +36,8 @@ struct CurveMapping;
struct Image;
struct MTex;
struct Material;
+struct BrushChannelSet;
+struct BrushCommandList;
typedef struct BrushClone {
/** Image for clone tool. */
@@ -153,6 +155,17 @@ typedef struct BrushGpencilSettings {
struct Material *material;
} 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 Brush {
ID id;
@@ -250,7 +263,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;
@@ -279,10 +292,21 @@ typedef struct Brush {
char _pad1[2];
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;
@@ -384,8 +408,16 @@ typedef struct Brush {
float mask_stencil_pos[2];
float mask_stencil_dimension[2];
+ float concave_mask_factor;
struct BrushGpencilSettings *gpencil_settings;
+ DynTopoSettings dyntopo, cached_dyntopo;
+
+ /* new brush engine stuff */
+
+ struct BrushChannelSet *channels;
+ struct BrushChannelSet *channels_final;
+ struct BrushCommandList *commandlist;
} Brush;
/* Struct to hold palette colors for sorting. */
diff --git a/source/blender/makesdna/DNA_customdata_types.h b/source/blender/makesdna/DNA_customdata_types.h
index 6acea8da15f..9c57f74a332 100644
--- a/source/blender/makesdna/DNA_customdata_types.h
+++ b/source/blender/makesdna/DNA_customdata_types.h
@@ -84,7 +84,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[51];
+ int typemap[53];
/** Number of layers, size of layers array. */
int totlayer, maxlayer;
/** In editmode, total size of all data layers. */
@@ -166,7 +166,9 @@ typedef enum CustomDataType {
CD_PROP_BOOL = 50,
- CD_NUMTYPES = 51,
+ CD_DYNTOPO_VERT = 51,
+ CD_MESH_ID = 52,
+ CD_NUMTYPES = 53,
} CustomDataType;
/* Bits for CustomDataMask */
@@ -220,6 +222,9 @@ typedef enum CustomDataType {
#define CD_MASK_PROP_FLOAT2 (1ULL << CD_PROP_FLOAT2)
#define CD_MASK_PROP_BOOL (1ULL << CD_PROP_BOOL)
+#define CD_MASK_DYNTOPO_VERT (1ULL << CD_DYNTOPO_VERT)
+#define CD_MASK_MESH_ID (1ULL << CD_MESH_ID)
+
/** Multires loop data. */
#define CD_MASK_MULTIRES_GRIDS (CD_MASK_MDISPS | CD_GRID_PAINT_MASK)
@@ -246,11 +251,13 @@ 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),
+ CD_FLAG_ELEM_NOCOPY = (1 << 5), // disables CustomData_bmesh_copy_data.
};
/* Limits */
diff --git a/source/blender/makesdna/DNA_mesh_types.h b/source/blender/makesdna/DNA_mesh_types.h
index f4f73bdc78d..50717f442e3 100644
--- a/source/blender/makesdna/DNA_mesh_types.h
+++ b/source/blender/makesdna/DNA_mesh_types.h
@@ -299,7 +299,7 @@ enum {
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 bc6b35c8e43..bbf573af03f 100644
--- a/source/blender/makesdna/DNA_meshdata_types.h
+++ b/source/blender/makesdna/DNA_meshdata_types.h
@@ -525,6 +525,42 @@ typedef struct MRecast {
/** \} */
+typedef struct MDynTopoVert {
+ short flag, valence;
+
+ /**original coordinates*/
+ float origco[3], origno[3];
+
+ /**original color*/
+ float origcolor[4];
+
+ float origmask;
+ float curvature_dir[3];
+
+ /* id of current stroke, used to detect
+ if vertex original data needs to be updated*/
+ int stroke_id;
+} MDynTopoVert;
+
+/*MDynTopoVert->flag*/
+enum {
+ DYNVERT_BOUNDARY = (1 << 0),
+ DYNVERT_VERT_FSET_HIDDEN = (1 << 1),
+ DYNVERT_FSET_BOUNDARY = (1 << 2),
+ DYNVERT_NEED_BOUNDARY = (1 << 3),
+ DYNVERT_NEED_TRIANGULATE = (1 << 4),
+ DYNVERT_NEED_DISK_SORT = (1 << 5),
+ DYNVERT_NEED_VALENCE = (1 << 6),
+ DYNVERT_FSET_CORNER = (1 << 7),
+ DYNVERT_CORNER = (1 << 8),
+ DYNVERT_API_TEMP1 = (1 << 9),
+ DYNVERT_SEAM_BOUNDARY = (1 << 10),
+ DYNVERT_SHARP_BOUNDARY = (1 << 11),
+ DYNVERT_SEAM_CORNER = (1 << 12),
+ DYNVERT_SHARP_CORNER = (1 << 13),
+ DYNVERT_SPLIT_TEMP = (1 << 15),
+};
+
#ifdef __cplusplus
}
#endif
diff --git a/source/blender/makesdna/DNA_modifier_types.h b/source/blender/makesdna/DNA_modifier_types.h
index 31daa778b03..17794295eb9 100644
--- a/source/blender/makesdna/DNA_modifier_types.h
+++ b/source/blender/makesdna/DNA_modifier_types.h
@@ -1862,10 +1862,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];
@@ -1919,9 +1919,7 @@ typedef struct UVWarpModifierData {
} UVWarpModifierData;
/* UVWarp modifier flags */
-enum {
- MOD_UVWARP_INVERT_VGROUP = 1 << 0,
-};
+enum { MOD_UVWARP_INVERT_VGROUP = 1 << 0, MOD_UVWARP_RESTRICT_ISLANDS = 1 << 1 };
/* cache modifier */
typedef struct MeshCacheModifierData {
@@ -2078,9 +2076,9 @@ typedef struct DataTransferModifierData {
char _pad1[4];
/** DT_MULTILAYER_INDEX_MAX; See DT_FROMLAYERS_ enum in ED_object.h. */
- int layers_select_src[4];
+ int layers_select_src[5];
/** DT_MULTILAYER_INDEX_MAX; See DT_TOLAYERS_ enum in ED_object.h. */
- int layers_select_dst[4];
+ int layers_select_dst[5];
/** See CDT_MIX_ enum in BKE_customdata.h. */
int mix_mode;
diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h
index c4cbc71762c..94520d59eea 100644
--- a/source/blender/makesdna/DNA_node_types.h
+++ b/source/blender/makesdna/DNA_node_types.h
@@ -395,8 +395,8 @@ typedef struct bNode {
/* XXX NODE_UPDATE is a generic update flag. More fine-grained updates
* might be used in the future, but currently all work the same way.
*/
-#define NODE_UPDATE 0xFFFF /* generic update flag (includes all others) */
-#define NODE_UPDATE_ID 1 /* associated id data block has changed */
+#define NODE_UPDATE 0xFFFF /* generic update flag (includes all others) */
+#define NODE_UPDATE_ID 1 /* associated id data block has changed */
#define NODE_UPDATE_OPERATOR 2 /* node update triggered from update operator */
/* Unique hash key for identifying node instances
@@ -442,9 +442,9 @@ typedef struct bNodeLink {
/* link->flag */
#define NODE_LINKFLAG_HILITE (1 << 0) /* link has been successfully validated */
#define NODE_LINK_VALID (1 << 1)
-#define NODE_LINK_TEST (1 << 2) /* free test flag, undefined */
+#define NODE_LINK_TEST (1 << 2) /* free test flag, undefined */
#define NODE_LINK_TEMP_HIGHLIGHT (1 << 3) /* Link is highlighted for picking. */
-#define NODE_LINK_MUTED (1 << 4) /* Link is muted. */
+#define NODE_LINK_MUTED (1 << 4) /* Link is muted. */
/* tree->edit_quality/tree->render_quality */
#define NTREE_QUALITY_HIGH 0
@@ -558,11 +558,11 @@ typedef struct bNodeTree {
#define NTREE_TYPE_INIT 1
/* ntree->flag */
-#define NTREE_DS_EXPAND (1 << 0) /* for animation editors */
-#define NTREE_COM_OPENCL (1 << 1) /* use opencl */
-#define NTREE_TWO_PASS (1 << 2) /* two pass */
+#define NTREE_DS_EXPAND (1 << 0) /* for animation editors */
+#define NTREE_COM_OPENCL (1 << 1) /* use opencl */
+#define NTREE_TWO_PASS (1 << 2) /* two pass */
#define NTREE_COM_GROUPNODE_BUFFER (1 << 3) /* use groupnode buffers */
-#define NTREE_VIEWER_BORDER (1 << 4) /* use a border for viewer nodes */
+#define NTREE_VIEWER_BORDER (1 << 4) /* use a border for viewer nodes */
/* NOTE: DEPRECATED, use (id->tag & LIB_TAG_LOCALIZED) instead. */
/* tree is localized copy, free when deleting node groups */
@@ -1490,7 +1490,7 @@ typedef struct NodeGeometryAttributeCapture {
#define NODE_IES_EXTERNAL 1
/* frame node flags */
-#define NODE_FRAME_SHRINK 1 /* keep the bounding box minimal */
+#define NODE_FRAME_SHRINK 1 /* keep the bounding box minimal */
#define NODE_FRAME_RESIZEABLE 2 /* test flag, if frame can be resized by user */
/* proxy node flags */
diff --git a/source/blender/makesdna/DNA_scene_defaults.h b/source/blender/makesdna/DNA_scene_defaults.h
index 61707964191..10c8e0140f9 100644
--- a/source/blender/makesdna/DNA_scene_defaults.h
+++ b/source/blender/makesdna/DNA_scene_defaults.h
@@ -290,7 +290,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 2840c795884..c8c5a0de034 100644
--- a/source/blender/makesdna/DNA_scene_types.h
+++ b/source/blender/makesdna/DNA_scene_types.h
@@ -484,7 +484,7 @@ typedef struct ImageFormatData {
#define R_IMF_IMTYPE_INVALID 255
/** #ImageFormatData.flag */
-#define R_IMF_FLAG_ZBUF (1 << 0) /* was R_OPENEXR_ZBUF */
+#define R_IMF_FLAG_ZBUF (1 << 0) /* was R_OPENEXR_ZBUF */
#define R_IMF_FLAG_PREVIEW_JPG (1 << 1) /* was R_PREVIEW_JPG */
/* Return values from #BKE_imtype_valid_depths, note this is depths per channel. */
@@ -526,8 +526,8 @@ typedef enum eImageFormatDepth {
/** #ImageFormatData.jp2_flag */
#define R_IMF_JP2_FLAG_YCC (1 << 0) /* when disabled use RGB */ /* was R_JPEG2K_YCC */
-#define R_IMF_JP2_FLAG_CINE_PRESET (1 << 1) /* was R_JPEG2K_CINE_PRESET */
-#define R_IMF_JP2_FLAG_CINE_48 (1 << 2) /* was R_JPEG2K_CINE_48FPS */
+#define R_IMF_JP2_FLAG_CINE_PRESET (1 << 1) /* was R_JPEG2K_CINE_PRESET */
+#define R_IMF_JP2_FLAG_CINE_48 (1 << 2) /* was R_JPEG2K_CINE_48FPS */
/** #ImageFormatData.jp2_codec */
#define R_IMF_JP2_CODEC_JP2 0
@@ -971,6 +971,8 @@ typedef struct ParticleEditSettings {
/* ------------------------------------------- */
/* Sculpt */
+struct BrushChannelSet;
+
/* Sculpt */
typedef struct Sculpt {
Paint paint;
@@ -995,6 +997,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;
@@ -1006,8 +1009,12 @@ typedef struct Sculpt {
/** Constant detail resolution (Blender unit / constant_detail). */
float constant_detail;
float detail_percent;
+ int dyntopo_spacing;
struct Object *gravity_object;
+ float dyntopo_radius_scale;
+ int _pad[1];
+ struct BrushChannelSet *channels;
} Sculpt;
typedef struct UvSculpt {
@@ -1248,7 +1255,8 @@ typedef struct UnifiedPaintSettings {
float pixel_radius;
float initial_pixel_radius;
- char _pad[4];
+ char _pad[3];
+ char hard_edge_mode;
/* drawing pressure */
float size_pressure_value;
@@ -1272,7 +1280,7 @@ 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),
} eUnifiedPaintSettingsFlags;
@@ -1855,12 +1863,12 @@ typedef struct Scene {
#define R_MODE_UNUSED_20 (1 << 20) /* cleared */
#define R_MODE_UNUSED_21 (1 << 21) /* cleared */
-#define R_NO_OVERWRITE (1 << 22) /* skip existing files */
-#define R_TOUCH (1 << 23) /* touch files before rendering */
+#define R_NO_OVERWRITE (1 << 22) /* skip existing files */
+#define R_TOUCH (1 << 23) /* touch files before rendering */
#define R_SIMPLIFY (1 << 24)
-#define R_EDGE_FRS (1 << 25) /* R_EDGE reserved for Freestyle */
+#define R_EDGE_FRS (1 << 25) /* R_EDGE reserved for Freestyle */
#define R_PERSISTENT_DATA (1 << 26) /* keep data around for re-render */
-#define R_MODE_UNUSED_27 (1 << 27) /* cleared */
+#define R_MODE_UNUSED_27 (1 << 27) /* cleared */
/** #RenderData.seq_flag */
enum {
@@ -2251,6 +2259,14 @@ typedef enum eSculptFlags {
/* 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 */
diff --git a/source/blender/makesdna/DNA_sculpt_brush_types.h b/source/blender/makesdna/DNA_sculpt_brush_types.h
new file mode 100644
index 00000000000..eba4c89c5b5
--- /dev/null
+++ b/source/blender/makesdna/DNA_sculpt_brush_types.h
@@ -0,0 +1,84 @@
+/*
+ * 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) 2011 Blender Foundation.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup DNA
+ *
+ * Structs used for the sculpt brush system
+ */
+#pragma once
+
+typedef struct BrushMapping {
+ char name[64];
+ CurveMapping curve;
+ float factor;
+ short blendmode;
+ short input_channel;
+ int flag, _pad[1];
+} BrushMapping;
+
+typedef struct BrushChannel {
+ char idname[64];
+ char name[64];
+
+ struct BrushChannelType *def;
+
+ float fvalue;
+ int ivalue;
+ BrushMapping mappings[5]; // should always be BRUSH_MAPPING_MAX
+
+ int type, flag;
+} BrushChannel;
+
+typedef struct BrushChannelSet {
+ BrushChannel *channels;
+ int totchannel, _pad[1];
+} BrushChannelSet;
+
+// mapping flags
+enum { BRUSH_MAPPING_ENABLED = 1 << 0, BRUSH_MAPPING_INVERT = 1 << 1 };
+
+// mapping types
+enum {
+ BRUSH_MAPPING_PRESSURE = 0,
+ BRUSH_MAPPING_XTILT = 1,
+ BRUSH_MAPPING_YTILT = 2,
+ BRUSH_MAPPING_ANGLE = 3,
+ BRUSH_MAPPING_SPEED = 4,
+ BRUSH_MAPPING_MAX = 5 // see BrushChannel.mappings
+};
+
+static_assert(offsetof(BrushChannel, type) - offsetof(BrushChannel, mappings) ==
+ sizeof(BrushMapping) * BRUSH_MAPPING_MAX,
+ "BrushChannel.mappings must == BRUSH_MAPPING_MAX");
+
+// BrushChannel->flag
+enum {
+ BRUSH_CHANNEL_INHERIT = 1 << 0,
+ BRUSH_CHANNEL_INHERIT_IF_UNSET = 1 << 1,
+ BRUSH_CHANNEL_NO_MAPPINGS = 1 << 2
+};
+
+// BrushChannelType->type
+enum {
+ BRUSH_CHANNEL_FLOAT = 1 << 0,
+ BRUSH_CHANNEL_INT = 1 << 1,
+ BRUSH_CHANNEL_ENUM = 1 << 2,
+ BRUSH_CHANNEL_BITMASK = 1 << 3,
+};
diff --git a/source/blender/makesdna/DNA_userdef_types.h b/source/blender/makesdna/DNA_userdef_types.h
index 4f86201ced2..1bdbc82cd50 100644
--- a/source/blender/makesdna/DNA_userdef_types.h
+++ b/source/blender/makesdna/DNA_userdef_types.h
@@ -646,8 +646,9 @@ typedef struct UserDef_Experimental {
char use_sculpt_tools_tilt;
char use_extended_asset_browser;
char use_override_templates;
+ char use_sculpt_uvsmooth;
char use_geometry_nodes_fields;
- char _pad[4];
+ char _pad[3];
/** `makesdna` does not allow empty structs. */
} UserDef_Experimental;
diff --git a/source/blender/makesdna/intern/CMakeLists.txt b/source/blender/makesdna/intern/CMakeLists.txt
index db34cf83fa9..41e95205ccc 100644
--- a/source/blender/makesdna/intern/CMakeLists.txt
+++ b/source/blender/makesdna/intern/CMakeLists.txt
@@ -165,6 +165,7 @@ set(SRC
../DNA_view3d_defaults.h
../DNA_volume_defaults.h
../DNA_world_defaults.h
+ ../DNA_sculpt_brush_types.h
)
set(LIB
diff --git a/source/blender/makesdna/intern/makesdna.c b/source/blender/makesdna/intern/makesdna.c
index 0b7848b6662..ecf8bfd0ab9 100644
--- a/source/blender/makesdna/intern/makesdna.c
+++ b/source/blender/makesdna/intern/makesdna.c
@@ -143,6 +143,7 @@ static const char *includefiles[] = {
"DNA_pointcache_types.h",
"DNA_uuid_types.h",
"DNA_asset_types.h",
+ "DNA_sculpt_brush_types.h",
/* see comment above before editing! */
@@ -1667,6 +1668,7 @@ int main(int argc, char **argv)
#include "DNA_rigidbody_types.h"
#include "DNA_scene_types.h"
#include "DNA_screen_types.h"
+#include "DNA_sculpt_brush_types.h"
#include "DNA_sdna_types.h"
#include "DNA_sequence_types.h"
#include "DNA_session_uuid_types.h"
diff --git a/source/blender/makesrna/RNA_access.h b/source/blender/makesrna/RNA_access.h
index ce53e3390e1..aaf37b10db3 100644
--- a/source/blender/makesrna/RNA_access.h
+++ b/source/blender/makesrna/RNA_access.h
@@ -99,6 +99,8 @@ extern StructRNA RNA_BooleanModifier;
extern StructRNA RNA_Brush;
extern StructRNA RNA_BrushCapabilitiesImagePaint;
extern StructRNA RNA_BrushCapabilitiesVertexPaint;
+extern StructRNA RNA_BrushChannel;
+extern StructRNA RNA_BrushChannelSet;
extern StructRNA RNA_BrushTextureSlot;
extern StructRNA RNA_BuildGpencilModifier;
extern StructRNA RNA_BuildModifier;
diff --git a/source/blender/makesrna/intern/CMakeLists.txt b/source/blender/makesrna/intern/CMakeLists.txt
index 7e6d0aea2ee..cdbd7fe023e 100644
--- a/source/blender/makesrna/intern/CMakeLists.txt
+++ b/source/blender/makesrna/intern/CMakeLists.txt
@@ -34,6 +34,7 @@ set(DEFSRC
rna_attribute.c
rna_boid.c
rna_brush.c
+ rna_brush_engine.c
rna_cachefile.c
rna_camera.c
rna_cloth.c
diff --git a/source/blender/makesrna/intern/makesrna.c b/source/blender/makesrna/intern/makesrna.c
index 36f19907080..eda878f378d 100644
--- a/source/blender/makesrna/intern/makesrna.c
+++ b/source/blender/makesrna/intern/makesrna.c
@@ -4365,6 +4365,7 @@ static RNAProcessItem PROCESS_ITEMS[] = {
{"rna_asset.c", NULL, RNA_def_asset},
{"rna_boid.c", NULL, RNA_def_boid},
{"rna_brush.c", NULL, RNA_def_brush},
+ {"rna_brush_engine.c", NULL, RNA_def_brush_engine},
{"rna_cachefile.c", NULL, RNA_def_cachefile},
{"rna_camera.c", "rna_camera_api.c", RNA_def_camera},
{"rna_cloth.c", NULL, RNA_def_cloth},
diff --git a/source/blender/makesrna/intern/rna_brush.c b/source/blender/makesrna/intern/rna_brush.c
index d32de7394cc..abcbb3e5737 100644
--- a/source/blender/makesrna/intern/rna_brush.c
+++ b/source/blender/makesrna/intern/rna_brush.c
@@ -136,7 +136,8 @@ const EnumPropertyItem rna_enum_brush_sculpt_tool_items[] = {
{SCULPT_TOOL_MASK, "MASK", ICON_BRUSH_MASK, "Mask", ""},
{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_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", ""},
@@ -144,6 +145,9 @@ const EnumPropertyItem rna_enum_brush_sculpt_tool_items[] = {
{SCULPT_TOOL_SYMMETRIZE, "SYMMETRIZE", ICON_BRUSH_SCULPT_DRAW, "Symmetrize", ""},
{SCULPT_TOOL_ARRAY, "ARRAY", ICON_BRUSH_SCULPT_DRAW, "Array", ""},
{0, NULL, 0, NULL, NULL},
+ {SCULPT_TOOL_VCOL_BOUNDARY, "VCOL_BOUNDARY", ICON_BRUSH_VCOL_BOUNDARY, "Sharpen Color Boundary", ""},
+ {SCULPT_TOOL_UV_SMOOTH, "UV_SMOOTH", ICON_BRUSH_GRAB, "UV Smooth", ""},
+ {0, NULL, 0, NULL, NULL},
};
/* clang-format on */
@@ -346,6 +350,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
@@ -375,6 +415,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;
@@ -677,6 +723,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 */
@@ -1134,6 +1185,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;
@@ -1157,6 +1326,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");
@@ -2465,7 +2635,6 @@ static void rna_def_brush(BlenderRNA *brna)
"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");
@@ -2475,11 +2644,9 @@ static void rna_def_brush(BlenderRNA *brna)
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_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);
@@ -2530,6 +2697,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);
@@ -2925,13 +3108,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 "
@@ -2939,6 +3148,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);
@@ -3063,6 +3329,21 @@ static void rna_def_brush(BlenderRNA *brna)
"When locked keep using the plane origin of surface where stroke was initiated");
RNA_def_property_update(prop, 0, "rna_Brush_update");
+ // note that concavity flag is derived from brush->concave_mask_factor being nonzero,
+ // so we just expose the invert concave flag here
+ prop = RNA_def_property(srna, "invert_automasking_concavity", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(
+ prop, NULL, "automasking_flags", BRUSH_AUTOMASKING_INVERT_CONCAVITY);
+ RNA_def_property_ui_text(
+ prop, "Invert Cavity Mask", "Invert mask to expose convex instead of concave areas");
+ RNA_def_property_update(prop, 0, "rna_Brush_update");
+
+ prop = RNA_def_property(srna, "use_automasking_concave", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "automasking_flags", BRUSH_AUTOMASKING_CONCAVITY);
+ RNA_def_property_ui_text(
+ prop, "Cavity Auto-Masking", "Filter verts by concavity; use with paint brushes");
+ RNA_def_property_update(prop, 0, "rna_Brush_update");
+
prop = RNA_def_property(srna, "use_automasking_topology", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "automasking_flags", BRUSH_AUTOMASKING_TOPOLOGY);
RNA_def_property_ui_text(prop,
@@ -3106,6 +3387,23 @@ 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(
@@ -3121,14 +3419,40 @@ static void rna_def_brush(BlenderRNA *brna)
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_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, "Fill Holes", "");
+ 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);
@@ -3374,6 +3698,12 @@ static void rna_def_brush(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Gradient", "");
RNA_def_property_update(prop, 0, "rna_Brush_update");
+ prop = RNA_def_property(srna, "channels", PROP_POINTER, 0);
+ RNA_def_property_pointer_sdna(prop, NULL, "channels");
+ RNA_def_property_struct_type(prop, "BrushChannelSet");
+ RNA_def_property_ui_text(prop, "Channels", "");
+ RNA_def_property_update(prop, 0, "rna_Brush_update");
+
/* gradient source */
prop = RNA_def_property(srna, "gradient_stroke_mode", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, brush_gradient_items);
@@ -3569,6 +3899,12 @@ static void rna_def_brush(BlenderRNA *brna)
RNA_def_property_pointer_sdna(prop, NULL, "gpencil_settings");
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", "");
}
/**
@@ -3648,6 +3984,7 @@ static void rna_def_operator_stroke_element(BlenderRNA *brna)
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_brush_engine.c b/source/blender/makesrna/intern/rna_brush_engine.c
new file mode 100644
index 00000000000..a16aec474d5
--- /dev/null
+++ b/source/blender/makesrna/intern/rna_brush_engine.c
@@ -0,0 +1,116 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/** \file
+ * \ingroup RNA
+ */
+
+#include <stdlib.h>
+
+#include "DNA_brush_types.h"
+#include "DNA_gpencil_types.h"
+#include "DNA_material_types.h"
+#include "DNA_object_types.h"
+#include "DNA_scene_types.h"
+#include "DNA_texture_types.h"
+#include "DNA_workspace_types.h"
+
+#include "BLI_math.h"
+
+#include "RNA_define.h"
+#include "RNA_enum_types.h"
+
+#include "rna_internal.h"
+
+#include "IMB_imbuf.h"
+
+#include "BKE_brush_engine.h"
+#include "DNA_sculpt_brush_types.h"
+#include "WM_types.h"
+
+#ifdef RNA_RUNTIME
+
+int rna_BrushChannelSet_channels_begin(CollectionPropertyIterator *iter, struct PointerRNA *ptr)
+{
+ BrushChannelSet *chset = ptr->data;
+
+ rna_iterator_array_begin(
+ iter, chset->channels, sizeof(BrushChannel), chset->totchannel, false, NULL);
+
+ return 1;
+}
+
+int rna_BrushChannelSet_channels_assignint(struct PointerRNA *ptr,
+ int key,
+ const struct PointerRNA *assign_ptr)
+{
+ BrushChannelSet *chset = ptr->data;
+ BrushChannel *ch = chset->channels + key;
+ BrushChannel *src = assign_ptr->data;
+
+ BKE_brush_channel_copy(ch, src);
+
+ return 1;
+}
+
+#endif
+
+void RNA_def_brush_channel(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ srna = RNA_def_struct(brna, "BrushChannel", NULL);
+ RNA_def_struct_sdna(srna, "BrushChannel");
+ RNA_def_struct_ui_text(srna, "Brush Channel", "Brush Channel");
+
+ prop = RNA_def_property(srna, "idname", PROP_STRING, PROP_NONE);
+ RNA_def_property_string_sdna(prop, "BrushChannel", "idname");
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE | PROP_ANIMATABLE);
+
+ RNA_def_struct_name_property(srna, prop);
+}
+
+void RNA_def_brush_channelset(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ srna = RNA_def_struct(brna, "BrushChannelSet", NULL);
+ RNA_def_struct_sdna(srna, "BrushChannelSet");
+ RNA_def_struct_ui_text(srna, "Channel Set", "Brush Channel Collection");
+
+ prop = RNA_def_property(srna, "channels", PROP_COLLECTION, PROP_NONE);
+ RNA_def_property_collection_sdna(prop, NULL, "channels", "totchannel");
+ RNA_def_property_collection_funcs(prop,
+ "rna_BrushChannelSet_channels_begin",
+ "rna_iterator_array_next",
+ "rna_iterator_array_end",
+ "rna_iterator_array_get",
+ NULL,
+ NULL,
+ NULL,
+ "rna_BrushChannelSet_channels_assignint");
+ RNA_def_property_struct_type(prop, "BrushChannel");
+ RNA_def_property_override_flag(
+ prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY | PROPOVERRIDE_LIBRARY_INSERTION);
+}
+
+void RNA_def_brush_engine(BlenderRNA *brna)
+{
+ RNA_def_brush_channel(brna);
+ RNA_def_brush_channelset(brna);
+}
diff --git a/source/blender/makesrna/intern/rna_internal.h b/source/blender/makesrna/intern/rna_internal.h
index 0bb76fd933a..157ec9fbd90 100644
--- a/source/blender/makesrna/intern/rna_internal.h
+++ b/source/blender/makesrna/intern/rna_internal.h
@@ -156,6 +156,7 @@ 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_cachefile(struct BlenderRNA *brna);
void RNA_def_camera(struct BlenderRNA *brna);
void RNA_def_cloth(struct BlenderRNA *brna);
diff --git a/source/blender/makesrna/intern/rna_mesh.c b/source/blender/makesrna/intern/rna_mesh.c
index ea814f2d2da..0064ff36cda 100644
--- a/source/blender/makesrna/intern/rna_mesh.c
+++ b/source/blender/makesrna/intern/rna_mesh.c
@@ -3343,6 +3343,12 @@ 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");
+
/* 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 c99dfea524f..3708a7dc637 100644
--- a/source/blender/makesrna/intern/rna_modifier.c
+++ b/source/blender/makesrna/intern/rna_modifier.c
@@ -3247,6 +3247,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);
@@ -4948,6 +4955,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");
@@ -6330,6 +6344,7 @@ static void rna_def_modifier_datatransfer(BlenderRNA *brna)
# if 0
{DT_TYPE_SKIN, "SKIN", 0, "Skin Weight", "Transfer skin weights"},
# endif
+ {DT_TYPE_PROPCOL, "PROPCOL", 0, "Sculpt Colors", "Transfer sculpt colors"},
{DT_TYPE_BWEIGHT_VERT, "BEVEL_WEIGHT_VERT", 0, "Bevel Weight", "Transfer bevel weights"},
{0, NULL, 0, NULL, NULL},
};
@@ -6574,6 +6589,17 @@ static void rna_def_modifier_datatransfer(BlenderRNA *brna)
RNA_def_property_update(prop, 0, "rna_Modifier_update");
prop = RNA_def_enum(srna,
+ "layers_propcol_select_src",
+ rna_enum_dt_layers_select_src_items,
+ DT_LAYERS_ALL_SRC,
+ "Source Layers Selection",
+ "Which layers to transfer, in case of multi-layers types");
+ RNA_def_property_enum_sdna(prop, NULL, "layers_select_src[DT_MULTILAYER_INDEX_PROPCOL]");
+ RNA_def_property_enum_funcs(
+ prop, NULL, NULL, "rna_DataTransferModifier_layers_select_src_itemf");
+ RNA_def_property_update(prop, 0, "rna_Modifier_update");
+
+ prop = RNA_def_enum(srna,
"layers_uv_select_src",
rna_enum_dt_layers_select_src_items,
DT_LAYERS_ALL_SRC,
@@ -6620,6 +6646,17 @@ static void rna_def_modifier_datatransfer(BlenderRNA *brna)
RNA_def_property_update(prop, 0, "rna_Modifier_update");
prop = RNA_def_enum(srna,
+ "layers_propcol_select_dst",
+ rna_enum_dt_layers_select_dst_items,
+ DT_LAYERS_NAME_DST,
+ "Destination Layers Matching",
+ "How to match source and destination layers");
+ RNA_def_property_enum_sdna(prop, NULL, "layers_select_dst[DT_MULTILAYER_INDEX_PROPCOL]");
+ RNA_def_property_enum_funcs(
+ prop, NULL, NULL, "rna_DataTransferModifier_layers_select_dst_itemf");
+ RNA_def_property_update(prop, 0, "rna_Modifier_update");
+
+ prop = RNA_def_enum(srna,
"layers_uv_select_dst",
rna_enum_dt_layers_select_dst_items,
DT_LAYERS_NAME_DST,
diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c
index badaaa14aa4..6c5ae1f3300 100644
--- a/source/blender/makesrna/intern/rna_scene.c
+++ b/source/blender/makesrna/intern/rna_scene.c
@@ -3597,6 +3597,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,
@@ -3669,6 +3675,13 @@ 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");
}
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 6bf9058a40d..3ad578b0f22 100644
--- a/source/blender/makesrna/intern/rna_sculpt_paint.c
+++ b/source/blender/makesrna/intern/rna_sculpt_paint.c
@@ -368,6 +368,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);
@@ -375,13 +377,16 @@ static void rna_Sculpt_update(bContext *C, PointerRNA *UNUSED(ptr))
Object *ob = OBACT(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);
}
}
@@ -537,6 +542,58 @@ static bool rna_ImaPaint_detect_data(ImagePaintSettings *imapaint)
return imapaint->missing_data == 0;
}
+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);
+void SCULPT_replay_parse(const char *buf);
+void SCULPT_replay(bContext *ctx);
+
+static void rna_SCULPT_replay_test(Sculpt *sculpt)
+{
+ SCULPT_replay_test();
+}
+
+static void rna_SCULPT_replay_start(Sculpt *sculpt)
+{
+ SCULPT_replay_log_start();
+}
+
+static const char *rna_SCULPT_replay_serialize(Sculpt *sculpt)
+{
+ return SCULPT_replay_serialize();
+}
+
+static void rna_SCULPT_replay_parse(Sculpt *sculpt, const char *buf)
+{
+ SCULPT_replay_parse(buf);
+}
+
+static void rna_SCULPT_replay_free(Sculpt *sculpt)
+{
+ SCULPT_replay_log_end();
+}
+
+static void rna_SCULPT_replay_replay(bContext *ctx)
+{
+ SCULPT_replay(ctx);
+}
+
+void SCULPT_replay_make_cube(struct bContext *C, int steps);
+static void rna_SCULPT_replay_make_cube(bContext *ctx, int steps)
+{
+ SCULPT_replay_make_cube(ctx, steps);
+}
+
+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(PointerRNA *UNUSED(ptr))
{
return BLI_strdup("tool_settings.gpencil_sculpt");
@@ -817,6 +874,12 @@ 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_ShowMask_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);
+ RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_Sculpt_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);
@@ -832,6 +895,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);
@@ -851,6 +929,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");
+
prop = RNA_def_property(srna, "use_automasking_topology", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "automasking_flags", BRUSH_AUTOMASKING_TOPOLOGY);
RNA_def_property_ui_text(prop,
@@ -929,6 +1032,41 @@ static void rna_def_sculpt(BlenderRNA *brna)
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, "test_replay", "rna_SCULPT_replay_test");
+ RNA_def_function_ui_description(func, "Test sculpt replay serialization");
+
+ func = RNA_def_function(srna, "replay_start", "rna_SCULPT_replay_start");
+ RNA_def_function_ui_description(func, "Test sculpt replay serialization");
+
+ func = RNA_def_function(srna, "replay_free", "rna_SCULPT_replay_free");
+ RNA_def_function_ui_description(func, "Test sculpt replay serialization");
+
+ func = RNA_def_function(srna, "replay_replay", "rna_SCULPT_replay_replay");
+ RNA_def_function_ui_description(func, "Test sculpt replay serialization");
+ RNA_def_function_flag(func, FUNC_NO_SELF | FUNC_USE_CONTEXT);
+
+ func = RNA_def_function(srna, "replay_make_cube", "rna_SCULPT_replay_make_cube");
+ RNA_def_function_ui_description(func, "Test sculpt replay serialization");
+ RNA_def_function_flag(func, FUNC_NO_SELF | FUNC_USE_CONTEXT);
+ RNA_def_int(func, "steps", 15, 1, 500, "steps", "steps", 1, 250);
+
+ 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);
+
+ func = RNA_def_function(srna, "replay_serialize", "rna_SCULPT_replay_serialize");
+ RNA_def_function_ui_description(func, "Test sculpt replay serialization");
+ RNA_def_function_return(func, RNA_def_string(func, "ret", NULL, 1024 * 32, "return", "return"));
+
+ func = RNA_def_function(srna, "replay_parse", "rna_SCULPT_replay_parse");
+ RNA_def_string(func, "buf", NULL, 1024 * 32, "buf", "buf");
+
+ RNA_def_function_ui_description(func, "Test sculpt replay serialization");
}
static void rna_def_uv_sculpt(BlenderRNA *brna)
diff --git a/source/blender/makesrna/intern/rna_userdef.c b/source/blender/makesrna/intern/rna_userdef.c
index 563c6ea35e0..12af21e681d 100644
--- a/source/blender/makesrna/intern/rna_userdef.c
+++ b/source/blender/makesrna/intern/rna_userdef.c
@@ -6328,6 +6328,10 @@ 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_geometry_nodes_fields", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "use_geometry_nodes_fields", 1);
RNA_def_property_ui_text(prop, "Geometry Nodes Fields", "Enable field nodes in geometry nodes");
diff --git a/source/blender/modifiers/intern/MOD_bevel.c b/source/blender/modifiers/intern/MOD_bevel.c
index add95a0d248..9a22b221852 100644
--- a/source/blender/modifiers/intern/MOD_bevel.c
+++ b/source/blender/modifiers/intern/MOD_bevel.c
@@ -122,7 +122,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(mesh,
+ bm = BKE_mesh_to_bmesh_ex(ctx->object, 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 c5d6902e1bc..fa4fbe48f57 100644
--- a/source/blender/modifiers/intern/MOD_boolean.cc
+++ b/source/blender/modifiers/intern/MOD_boolean.cc
@@ -250,7 +250,7 @@ static BMesh *BMD_mesh_bm_create(
BMeshFromMeshParams params{};
params.calc_face_normal = true;
- BM_mesh_bm_from_me(bm, mesh_operand_ob, &params);
+ BM_mesh_bm_from_me(object, bm, mesh_operand_ob, &params);
if (UNLIKELY(*r_is_flip)) {
const int cd_loop_mdisp_offset = CustomData_get_offset(&bm->ldata, CD_MDISPS);
@@ -261,7 +261,7 @@ static BMesh *BMD_mesh_bm_create(
}
}
- BM_mesh_bm_from_me(bm, mesh, &params);
+ BM_mesh_bm_from_me(object, bm, mesh, &params);
return bm;
}
@@ -537,7 +537,7 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
/* Needed for multiple objects to work. */
BMeshToMeshParams params{};
params.calc_object_remap = false;
- BM_mesh_bm_to_me(nullptr, bm, mesh, &params);
+ BM_mesh_bm_to_me(nullptr, nullptr, bm, mesh, &params);
result = BKE_mesh_from_bmesh_for_eval_nomain(bm, nullptr, mesh);
BM_mesh_free(bm);
diff --git a/source/blender/modifiers/intern/MOD_correctivesmooth.c b/source/blender/modifiers/intern/MOD_correctivesmooth.c
index 9e8f5bee396..ede301889a0 100644
--- a/source/blender/modifiers/intern/MOD_correctivesmooth.c
+++ b/source/blender/modifiers/intern/MOD_correctivesmooth.c
@@ -193,7 +193,10 @@ static void smooth_iter__simple(CorrectiveSmoothModifierData *csmd,
uint i;
const uint numEdges = (uint)mesh->totedge;
+ const float projection = csmd->projection;
+
const MEdge *edges = mesh->medge;
+ const MVert *verts = mesh->mvert;
float *vertex_edge_count_div;
struct SmoothingData_Simple {
@@ -239,8 +242,24 @@ static void smooth_iter__simple(CorrectiveSmoothModifierData *csmd,
sd_v1 = &smooth_data[edges[i].v1];
sd_v2 = &smooth_data[edges[i].v2];
- add_v3_v3(sd_v1->delta, edge_dir);
- sub_v3_v3(sd_v2->delta, edge_dir);
+ if (projection > 0.0f) {
+ float edge_dir2[3];
+ float no[3];
+
+ normal_short_to_float_v3(no, verts[edges[i].v1].no);
+ madd_v3_v3v3fl(edge_dir2, edge_dir, no, -dot_v3v3(edge_dir, no) * projection);
+ add_v3_v3(sd_v1->delta, edge_dir2);
+
+ negate_v3(edge_dir);
+
+ normal_short_to_float_v3(no, verts[edges[i].v2].no);
+ madd_v3_v3v3fl(edge_dir2, edge_dir, no, -dot_v3v3(edge_dir, no) * projection);
+ add_v3_v3(sd_v2->delta, edge_dir2);
+ }
+ else {
+ add_v3_v3(sd_v1->delta, edge_dir);
+ sub_v3_v3(sd_v2->delta, edge_dir);
+ }
}
for (i = 0; i < numVerts; i++) {
@@ -270,7 +289,9 @@ static void smooth_iter__length_weight(CorrectiveSmoothModifierData *csmd,
/* NOTE: the way this smoothing method works, its approx half as strong as the simple-smooth,
* and 2.0 rarely spikes, double the value for consistent behavior. */
const float lambda = csmd->lambda * 2.0f;
+ const float projection = csmd->projection;
const MEdge *edges = mesh->medge;
+ const MVert *verts = mesh->mvert;
float *vertex_edge_count;
uint i;
@@ -305,8 +326,24 @@ static void smooth_iter__length_weight(CorrectiveSmoothModifierData *csmd,
sd_v1 = &smooth_data[edges[i].v1];
sd_v2 = &smooth_data[edges[i].v2];
- add_v3_v3(sd_v1->delta, edge_dir);
- sub_v3_v3(sd_v2->delta, edge_dir);
+ if (projection > 0.0f) {
+ float edge_dir2[3];
+ float no[3];
+
+ normal_short_to_float_v3(no, verts[edges[i].v1].no);
+ madd_v3_v3v3fl(edge_dir2, edge_dir, no, -dot_v3v3(edge_dir, no) * projection);
+ add_v3_v3(sd_v1->delta, edge_dir2);
+
+ negate_v3(edge_dir);
+
+ normal_short_to_float_v3(no, verts[edges[i].v2].no);
+ madd_v3_v3v3fl(edge_dir2, edge_dir, no, -dot_v3v3(edge_dir, no) * projection);
+ add_v3_v3(sd_v2->delta, edge_dir2);
+ }
+ else {
+ add_v3_v3(sd_v1->delta, edge_dir);
+ sub_v3_v3(sd_v2->delta, edge_dir);
+ }
sd_v1->edge_length_sum += edge_dist;
sd_v2->edge_length_sum += edge_dist;
@@ -787,6 +824,7 @@ static void panel_draw(const bContext *UNUSED(C), Panel *panel)
uiLayoutSetPropSep(layout, true);
uiItemR(layout, ptr, "factor", 0, IFACE_("Factor"), ICON_NONE);
+ uiItemR(layout, ptr, "projection", 0, IFACE_("Projection"), ICON_NONE);
uiItemR(layout, ptr, "iterations", 0, NULL, ICON_NONE);
uiItemR(layout, ptr, "scale", 0, NULL, ICON_NONE);
uiItemR(layout, ptr, "smooth_type", 0, NULL, ICON_NONE);
@@ -821,6 +859,13 @@ static void blendWrite(BlendWriter *writer, const ModifierData *md)
}
}
+bool dependsOnNormals(ModifierData *md)
+{
+ CorrectiveSmoothModifierData *csmd = (CorrectiveSmoothModifierData *)md;
+
+ return csmd->projection > 0.0f;
+}
+
static void blendRead(BlendDataReader *reader, ModifierData *md)
{
CorrectiveSmoothModifierData *csmd = (CorrectiveSmoothModifierData *)md;
@@ -859,7 +904,7 @@ ModifierTypeInfo modifierType_CorrectiveSmooth = {
/* isDisabled */ NULL,
/* updateDepsgraph */ NULL,
/* dependsOnTime */ NULL,
- /* dependsOnNormals */ NULL,
+ /* dependsOnNormals */ dependsOnNormals,
/* foreachIDLink */ NULL,
/* foreachTexLink */ NULL,
/* freeRuntimeData */ NULL,
diff --git a/source/blender/modifiers/intern/MOD_datatransfer.c b/source/blender/modifiers/intern/MOD_datatransfer.c
index e2b2cc58d48..4d670ba8afd 100644
--- a/source/blender/modifiers/intern/MOD_datatransfer.c
+++ b/source/blender/modifiers/intern/MOD_datatransfer.c
@@ -369,6 +369,20 @@ static void face_corner_panel_draw(const bContext *UNUSED(C), Panel *panel)
uiItemR(layout, ptr, "loop_mapping", 0, IFACE_("Mapping"), ICON_NONE);
}
+static void vert_propcol_panel_draw(const bContext *UNUSED(C), Panel *panel)
+{
+ uiLayout *layout = panel->layout;
+
+ PointerRNA *ptr = modifier_panel_get_property_pointers(panel, NULL);
+
+ uiLayoutSetPropSep(layout, true);
+
+ uiLayoutSetActive(layout, RNA_enum_get(ptr, "data_types_verts") & DT_TYPE_PROPCOL);
+
+ uiItemR(layout, ptr, "layers_propcol_select_src", 0, IFACE_("Layer Selection"), ICON_NONE);
+ uiItemR(layout, ptr, "layers_propcol_select_dst", 0, IFACE_("Layer Mapping"), ICON_NONE);
+}
+
static void face_corner_vcol_panel_draw(const bContext *UNUSED(C), Panel *panel)
{
uiLayout *layout = panel->layout;
@@ -450,6 +464,9 @@ static void panelRegister(ARegionType *region_type)
region_type, "vertex_vgroup", "Vertex Groups", NULL, vertex_vgroup_panel_draw, vertex_panel);
modifier_subpanel_register(
+ region_type, "vert_propcol", "Sculpt Colors", NULL, vert_propcol_panel_draw, vertex_panel);
+
+ modifier_subpanel_register(
region_type, "edge", "", edge_panel_draw_header, edge_panel_draw, panel_type);
PanelType *face_corner_panel = modifier_subpanel_register(region_type,
diff --git a/source/blender/modifiers/intern/MOD_decimate.c b/source/blender/modifiers/intern/MOD_decimate.c
index 56fcbbd8b7c..3b71106a4ca 100644
--- a/source/blender/modifiers/intern/MOD_decimate.c
+++ b/source/blender/modifiers/intern/MOD_decimate.c
@@ -171,7 +171,8 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
}
}
- bm = BKE_mesh_to_bmesh_ex(mesh,
+ bm = BKE_mesh_to_bmesh_ex(ctx->object,
+ 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 b21a536ad8a..7e4befe3b2a 100644
--- a/source/blender/modifiers/intern/MOD_edgesplit.c
+++ b/source/blender/modifiers/intern/MOD_edgesplit.c
@@ -68,7 +68,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,
@@ -128,7 +129,7 @@ static void initData(ModifierData *md)
MEMCPY_STRUCT_AFTER(emd, DNA_struct_default_get(EdgeSplitModifierData), modifier);
}
-static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *UNUSED(ctx), Mesh *mesh)
+static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *mesh)
{
Mesh *result;
EdgeSplitModifierData *emd = (EdgeSplitModifierData *)md;
diff --git a/source/blender/modifiers/intern/MOD_particlesystem.c b/source/blender/modifiers/intern/MOD_particlesystem.c
index 71fc7f3e424..33385e91637 100644
--- a/source/blender/modifiers/intern/MOD_particlesystem.c
+++ b/source/blender/modifiers/intern/MOD_particlesystem.c
@@ -255,7 +255,7 @@ static void deformVertsEM(ModifierData *md,
const bool do_temp_mesh = (mesh == NULL);
if (do_temp_mesh) {
mesh = BKE_id_new_nomain(ID_ME, ((ID *)ob->data)->name);
- BM_mesh_bm_to_me(NULL, editData->bm, mesh, &((BMeshToMeshParams){0}));
+ BM_mesh_bm_to_me(NULL, NULL, editData->bm, mesh, &((BMeshToMeshParams){0}));
}
deformVerts(md, ob, mesh, vertexCos, numVerts);
diff --git a/source/blender/modifiers/intern/MOD_simulation.cc b/source/blender/modifiers/intern/MOD_simulation.cc
new file mode 100644
index 00000000000..0766c59cda6
--- /dev/null
+++ b/source/blender/modifiers/intern/MOD_simulation.cc
@@ -0,0 +1,194 @@
+/*
+ * 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) 2005 by the Blender Foundation.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup modifiers
+ */
+
+#include <cstring>
+#include <iostream>
+#include <string>
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_float3.hh"
+#include "BLI_listbase.h"
+#include "BLI_string.h"
+#include "BLI_utildefines.h"
+
+#include "DNA_defaults.h"
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+#include "DNA_modifier_types.h"
+#include "DNA_object_types.h"
+#include "DNA_pointcloud_types.h"
+#include "DNA_scene_types.h"
+#include "DNA_screen_types.h"
+#include "DNA_simulation_types.h"
+
+#include "BKE_customdata.h"
+#include "BKE_lib_query.h"
+#include "BKE_mesh.h"
+#include "BKE_modifier.h"
+#include "BKE_pointcloud.h"
+#include "BKE_screen.h"
+#include "BKE_simulation.h"
+
+#include "BLO_read_write.h"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "RNA_access.h"
+
+#include "DEG_depsgraph_build.h"
+#include "DEG_depsgraph_query.h"
+
+#include "MOD_modifiertypes.h"
+#include "MOD_ui_common.h"
+
+using blender::float3;
+
+static void initData(ModifierData *md)
+{
+ SimulationModifierData *smd = (SimulationModifierData *)md;
+
+ BLI_assert(MEMCMP_STRUCT_AFTER_IS_ZERO(smd, modifier));
+
+ MEMCPY_STRUCT_AFTER(smd, DNA_struct_default_get(SimulationModifierData), modifier);
+}
+
+static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphContext *UNUSED(ctx))
+{
+ SimulationModifierData *smd = reinterpret_cast<SimulationModifierData *>(md);
+ UNUSED_VARS(smd);
+}
+
+static void foreachIDLink(ModifierData *md,
+ Object *UNUSED(ob),
+ IDWalkFunc UNUSED(walk),
+ void *UNUSED(userData))
+{
+ SimulationModifierData *smd = reinterpret_cast<SimulationModifierData *>(md);
+ UNUSED_VARS(smd);
+}
+
+static bool isDisabled(const struct Scene *UNUSED(scene),
+ ModifierData *md,
+ bool UNUSED(useRenderParams))
+{
+ SimulationModifierData *smd = reinterpret_cast<SimulationModifierData *>(md);
+ UNUSED_VARS(smd);
+ return false;
+}
+
+static PointCloud *modifyPointCloud(ModifierData *md,
+ const ModifierEvalContext *UNUSED(ctx),
+ PointCloud *pointcloud)
+{
+ SimulationModifierData *smd = reinterpret_cast<SimulationModifierData *>(md);
+ UNUSED_VARS(smd);
+ return pointcloud;
+}
+
+static void panel_draw(const bContext *UNUSED(C), Panel *panel)
+{
+ uiLayout *layout = panel->layout;
+
+ PointerRNA ob_ptr;
+ PointerRNA *ptr = modifier_panel_get_property_pointers(panel, &ob_ptr);
+
+ uiLayoutSetPropSep(layout, true);
+ uiLayoutSetPropDecorate(layout, false);
+
+ uiItemL(layout, "This modifier does nothing currently", ICON_INFO);
+
+ modifier_panel_end(layout, ptr);
+}
+
+static void panelRegister(ARegionType *region_type)
+{
+ modifier_panel_register(region_type, eModifierType_Simulation, panel_draw);
+}
+
+static void blendWrite(BlendWriter *writer, const ModifierData *md)
+{
+ const SimulationModifierData *smd = reinterpret_cast<const SimulationModifierData *>(md);
+ UNUSED_VARS(smd, writer);
+}
+
+static void blendRead(BlendDataReader *reader, ModifierData *md)
+{
+ SimulationModifierData *smd = reinterpret_cast<SimulationModifierData *>(md);
+ UNUSED_VARS(smd, reader);
+}
+
+static void copyData(const ModifierData *md, ModifierData *target, const int flag)
+{
+ const SimulationModifierData *smd = reinterpret_cast<const SimulationModifierData *>(md);
+ SimulationModifierData *tsmd = reinterpret_cast<SimulationModifierData *>(target);
+ UNUSED_VARS(smd, tsmd);
+
+ BKE_modifier_copydata_generic(md, target, flag);
+}
+
+static void freeData(ModifierData *md)
+{
+ SimulationModifierData *smd = reinterpret_cast<SimulationModifierData *>(md);
+ UNUSED_VARS(smd);
+}
+
+ModifierTypeInfo modifierType_Simulation = {
+ /* name */ "Simulation",
+ /* structName */ "SimulationModifierData",
+ /* structSize */ sizeof(SimulationModifierData),
+#ifdef WITH_GEOMETRY_NODES
+ /* srna */ &RNA_SimulationModifier,
+#else
+ /* srna */ &RNA_Modifier,
+#endif
+ /* type */ eModifierTypeType_None,
+ /* flags */ (ModifierTypeFlag)0,
+ /* icon */ ICON_PHYSICS, /* TODO: Use correct icon. */
+
+ /* copyData */ copyData,
+
+ /* deformVerts */ nullptr,
+ /* deformMatrices */ nullptr,
+ /* deformVertsEM */ nullptr,
+ /* deformMatricesEM */ nullptr,
+ /* modifyMesh */ nullptr,
+ /* modifyHair */ nullptr,
+ /* modifyPointCloud */ modifyPointCloud,
+ /* modifyVolume */ nullptr,
+
+ /* initData */ initData,
+ /* requiredDataMask */ nullptr,
+ /* freeData */ freeData,
+ /* isDisabled */ isDisabled,
+ /* updateDepsgraph */ updateDepsgraph,
+ /* dependsOnTime */ nullptr,
+ /* dependsOnNormals */ nullptr,
+ /* foreachIDLink */ foreachIDLink,
+ /* foreachTexLink */ nullptr,
+ /* freeRuntimeData */ nullptr,
+ /* panelRegister */ panelRegister,
+ /* blendWrite */ blendWrite,
+ /* blendRead */ blendRead,
+};
diff --git a/source/blender/modifiers/intern/MOD_skin.c b/source/blender/modifiers/intern/MOD_skin.c
index 7d90935f678..40094a7e4e7 100644
--- a/source/blender/modifiers/intern/MOD_skin.c
+++ b/source/blender/modifiers/intern/MOD_skin.c
@@ -1936,7 +1936,7 @@ static Mesh *base_skin(Mesh *origmesh, SkinModifierData *smd, eSkinErrorFlag *r_
totvert = origmesh->totvert;
totedge = origmesh->totedge;
- BKE_mesh_vert_edge_map_create(&emap, &emapmem, medge, totvert, totedge);
+ BKE_mesh_vert_edge_map_create(&emap, &emapmem, mvert, medge, totvert, totedge, false);
emat = build_edge_mats(nodes, mvert, totvert, medge, emap, totedge, &has_valid_root);
skin_nodes = build_frames(mvert, totvert, nodes, emap, emat);
diff --git a/source/blender/modifiers/intern/MOD_triangulate.c b/source/blender/modifiers/intern/MOD_triangulate.c
index 52d5f3e97ef..ecd2a0b3365 100644
--- a/source/blender/modifiers/intern/MOD_triangulate.c
+++ b/source/blender/modifiers/intern/MOD_triangulate.c
@@ -76,7 +76,8 @@ Mesh *triangulate_mesh(Mesh *mesh,
cd_mask_extra.lmask |= CD_MASK_NORMAL;
}
- bm = BKE_mesh_to_bmesh_ex(mesh,
+ bm = BKE_mesh_to_bmesh_ex(NULL,
+ mesh,
&((struct BMeshCreateParams){0}),
&((struct BMeshFromMeshParams){
.calc_face_normal = true,
@@ -124,12 +125,11 @@ static void initData(ModifierData *md)
md->mode |= eModifierMode_Editmode;
}
-static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *UNUSED(ctx), Mesh *mesh)
+static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *mesh)
{
TriangulateModifierData *tmd = (TriangulateModifierData *)md;
Mesh *result;
- if (!(result = triangulate_mesh(
- mesh, tmd->quad_method, tmd->ngon_method, tmd->min_vertices, tmd->flag))) {
+ if (!(result = triangulate_mesh(mesh, tmd->quad_method, tmd->ngon_method, tmd->min_vertices, tmd->flag))) {
return mesh;
}
diff --git a/source/blender/modifiers/intern/MOD_uvwarp.c b/source/blender/modifiers/intern/MOD_uvwarp.c
index 3f161d339c2..9650df193bd 100644
--- a/source/blender/modifiers/intern/MOD_uvwarp.c
+++ b/source/blender/modifiers/intern/MOD_uvwarp.c
@@ -100,6 +100,7 @@ typedef struct UVWarpData {
MDeformVert *dvert;
int defgrp_index;
+ bool restrict_island;
float (*warp_mat)[4];
bool invert_vgroup;
@@ -123,6 +124,19 @@ static void uv_warp_compute(void *__restrict userdata,
int l;
if (dvert) {
+ if (data->restrict_island) {
+ for (l = 0; l < mp->totloop; l++, ml++) {
+ const float weight = data->invert_vgroup ?
+ 1.0f - BKE_defvert_find_weight(&dvert[ml->v], defgrp_index) :
+ BKE_defvert_find_weight(&dvert[ml->v], defgrp_index);
+ if (weight == 0.0f) {
+ return;
+ }
+ }
+
+ ml = &data->mloop[mp->loopstart];
+ }
+
for (l = 0; l < mp->totloop; l++, ml++, mluv++) {
float uv[2];
const float weight = data->invert_vgroup ?
@@ -221,6 +235,7 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
UVWarpData data = {
.mpoly = mpoly,
+ .restrict_island = umd->flag & MOD_UVWARP_RESTRICT_ISLANDS,
.mloop = mloop,
.mloopuv = mloopuv,
.dvert = dvert,
@@ -299,6 +314,7 @@ static void panel_draw(const bContext *UNUSED(C), Panel *panel)
}
modifier_vgroup_ui(layout, ptr, &ob_ptr, "vertex_group", "invert_vertex_group", NULL);
+ uiItemR(layout, ptr, "restrict_to_islands", 0, NULL, ICON_NONE);
modifier_panel_end(layout, ptr);
}
diff --git a/source/blender/modifiers/intern/MOD_wireframe.c b/source/blender/modifiers/intern/MOD_wireframe.c
index 706960182cf..296eb78a5ea 100644
--- a/source/blender/modifiers/intern/MOD_wireframe.c
+++ b/source/blender/modifiers/intern/MOD_wireframe.c
@@ -78,7 +78,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(mesh,
+ bm = BKE_mesh_to_bmesh_ex(ob, mesh,
&(struct BMeshCreateParams){0},
&(struct BMeshFromMeshParams){
.calc_face_normal = true,
diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cube.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cube.cc
index af8ce02b3c1..23a411e36dc 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cube.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cube.cc
@@ -140,6 +140,7 @@ static void calculate_polys(const CuboidConfig &config,
MutableSpan<MPoly> polys,
MutableSpan<MLoop> loops)
{
+
int loop_index = 0;
int poly_index = 0;
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 5ea7165ac31..bf26c03272e 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
@@ -53,8 +53,10 @@ static Mesh *create_ico_sphere_mesh(const int subdivisions, const float radius)
BMeshToMeshParams params{};
params.calc_object_remap = false;
Mesh *mesh = (Mesh *)BKE_id_new_nomain(ID_ME, nullptr);
+
BKE_id_material_eval_ensure_default_slot(&mesh->id);
- BM_mesh_bm_to_me(nullptr, bm, mesh, &params);
+ BM_mesh_bm_to_me(nullptr, nullptr, bm, mesh, &params);
+
BM_mesh_free(bm);
return mesh;
diff --git a/source/blender/python/bmesh/bmesh_py_types.c b/source/blender/python/bmesh/bmesh_py_types.c
index e5e601e0eb6..3e94f184781 100644
--- a/source/blender/python/bmesh/bmesh_py_types.c
+++ b/source/blender/python/bmesh/bmesh_py_types.c
@@ -1056,7 +1056,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, &params);
+ BM_mesh_bm_to_me(bmain, NULL, bm, me, &params);
/* 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 */
@@ -1149,7 +1149,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,
@@ -1210,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,
@@ -2762,7 +2764,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);
diff --git a/source/blender/python/intern/bpy_msgbus.c b/source/blender/python/intern/bpy_msgbus.c
index 75a5f6f72ae..e96d312cdc2 100644
--- a/source/blender/python/intern/bpy_msgbus.c
+++ b/source/blender/python/intern/bpy_msgbus.c
@@ -201,6 +201,16 @@ static void bpy_msgbus_subscribe_value_free_data(struct wmMsgSubscribeKey *UNUSE
/** \} */
+extern void pbvh_bmesh_do_cache_test();
+
+PyDoc_STRVAR(exec_bmesh_cache_test_doc, "internal development function\n");
+static PyObject *exec_bmesh_cache_test(PyObject *self)
+{
+ pbvh_bmesh_do_cache_test();
+
+ Py_RETURN_NONE;
+}
+
/* -------------------------------------------------------------------- */
/** \name Public Message Bus API
* \{ */
@@ -379,6 +389,10 @@ static struct PyMethodDef BPy_msgbus_methods[] = {
(PyCFunction)bpy_msgbus_clear_by_owner,
METH_O,
bpy_msgbus_clear_by_owner_doc},
+ {"pbvh_bmesh_do_cache_test",
+ (PyCFunction)exec_bmesh_cache_test,
+ METH_NOARGS,
+ exec_bmesh_cache_test_doc},
{NULL, NULL, 0, NULL},
};
diff --git a/source/blender/render/intern/bake.c b/source/blender/render/intern/bake.c
index 76839651b5d..c2c0fd1efbc 100644
--- a/source/blender/render/intern/bake.c
+++ b/source/blender/render/intern/bake.c
@@ -939,6 +939,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/intern/wm_draw.c b/source/blender/windowmanager/intern/wm_draw.c
index 328950cf8f9..2d7e41f5f30 100644
--- a/source/blender/windowmanager/intern/wm_draw.c
+++ b/source/blender/windowmanager/intern/wm_draw.c
@@ -52,6 +52,7 @@
#include "ED_view3d.h"
#include "GPU_batch_presets.h"
+#include "GPU_buffers.h"
#include "GPU_context.h"
#include "GPU_debug.h"
#include "GPU_framebuffer.h"
@@ -1051,6 +1052,43 @@ void wm_draw_update(bContext *C)
GPU_context_main_lock();
BKE_image_free_unused_gpu_textures();
+ /*We can save GPU bandwidth for PBVH drawing if we know for sure that no
+ viewport has EEVEE running in it. As in no viewport in any windows.
+
+ This is because PBVH only supplies one set of drawing buffers
+ to the draw manager. Creating more buffers for specific drawengines
+ is simply not feasible for performance reasons.
+ */
+
+ LISTBASE_FOREACH (wmWindow *, win, &wm->windows) {
+ GHOST_TWindowState state = GHOST_GetWindowState(win->ghostwin);
+
+ if (state == GHOST_kWindowStateMinimized) {
+ continue;
+ }
+
+ CTX_wm_window_set(C, win);
+
+ GPU_pbvh_need_full_render_set(false);
+
+ if (wm_draw_update_test_window(bmain, C, win)) {
+ bScreen *screen = WM_window_get_active_screen(win);
+
+ /* Draw screen areas into own frame buffer. */
+ ED_screen_areas_iter (win, screen, area) {
+ if (area->spacetype != SPACE_VIEW3D) {
+ continue;
+ }
+
+ CTX_wm_area_set(C, area);
+ View3D *v3d = CTX_wm_view3d(C);
+ if (v3d->shading.type >= OB_MATERIAL) {
+ GPU_pbvh_need_full_render_set(true);
+ }
+ }
+ }
+ }
+
LISTBASE_FOREACH (wmWindow *, win, &wm->windows) {
#ifdef WIN32
GHOST_TWindowState state = GHOST_GetWindowState(win->ghostwin);
diff --git a/source/blender/windowmanager/intern/wm_files.c b/source/blender/windowmanager/intern/wm_files.c
index 2ce2bcc2f3c..41c1982a0e5 100644
--- a/source/blender/windowmanager/intern/wm_files.c
+++ b/source/blender/windowmanager/intern/wm_files.c
@@ -461,10 +461,10 @@ static void wm_init_userdef(Main *bmain)
}
/* return codes */
-#define BKE_READ_EXOTIC_FAIL_PATH -3 /* file format is not supported */
+#define BKE_READ_EXOTIC_FAIL_PATH -3 /* file format is not supported */
#define BKE_READ_EXOTIC_FAIL_FORMAT -2 /* file format is not supported */
-#define BKE_READ_EXOTIC_FAIL_OPEN -1 /* Can't open the file */
-#define BKE_READ_EXOTIC_OK_BLEND 0 /* .blend file */
+#define BKE_READ_EXOTIC_FAIL_OPEN -1 /* Can't open the file */
+#define BKE_READ_EXOTIC_OK_BLEND 0 /* .blend file */
#if 0
# define BKE_READ_EXOTIC_OK_OTHER 1 /* other supported formats */
#endif
diff --git a/source/creator/creator.c b/source/creator/creator.c
index 2ec4a2aa616..53cb1c1b454 100644
--- a/source/creator/creator.c
+++ b/source/creator/creator.c
@@ -308,7 +308,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 /* WIN32 */
diff --git a/source/tools b/source/tools
-Subproject c8579c5cf43229843df505da9644b5b0b720197
+Subproject 548055f40213c775a6b77025525c91e8466e70d