diff options
248 files changed, 38433 insertions, 4510 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 Binary files differdeleted file mode 100644 index 5d422130ea3..00000000000 --- a/release/datafiles/icons/brush.sculpt.displacement_smear.dat +++ /dev/null diff --git a/release/datafiles/icons/brush.sculpt.draw_sharp.dat b/release/datafiles/icons/brush.sculpt.draw_sharp.dat Binary files differindex 1877c0ae4d4..9bea1b02894 100644 --- a/release/datafiles/icons/brush.sculpt.draw_sharp.dat +++ b/release/datafiles/icons/brush.sculpt.draw_sharp.dat diff --git a/release/datafiles/icons/brush.sculpt.paint.dat b/release/datafiles/icons/brush.sculpt.paint.dat Binary files differnew file mode 100644 index 00000000000..ef5f5fe851a --- /dev/null +++ b/release/datafiles/icons/brush.sculpt.paint.dat diff --git a/release/datafiles/icons/brush.sculpt.smear.dat b/release/datafiles/icons/brush.sculpt.smear.dat Binary files differnew file mode 100644 index 00000000000..c7214fb863b --- /dev/null +++ b/release/datafiles/icons/brush.sculpt.smear.dat diff --git a/release/datafiles/icons/brush.sculpt.vcol_boundary.dat b/release/datafiles/icons/brush.sculpt.vcol_boundary.dat Binary files differnew file mode 100644 index 00000000000..41c69e4dda8 --- /dev/null +++ b/release/datafiles/icons/brush.sculpt.vcol_boundary.dat diff --git a/release/datafiles/icons/ops.armature.extrude.cursor.dat b/release/datafiles/icons/ops.armature.extrude.cursor.dat Binary files differdeleted file mode 100644 index ec8f2432052..00000000000 --- a/release/datafiles/icons/ops.armature.extrude.cursor.dat +++ /dev/null diff --git a/release/datafiles/icons/ops.armature.extrude.dat b/release/datafiles/icons/ops.armature.extrude.dat Binary files differdeleted file mode 100644 index 2194c5bf556..00000000000 --- a/release/datafiles/icons/ops.armature.extrude.dat +++ /dev/null diff --git a/release/datafiles/icons/ops.curve.dupli_extrude_cursor.dat b/release/datafiles/icons/ops.curve.dupli_extrude_cursor.dat Binary files differdeleted file mode 100644 index 509ca7c9440..00000000000 --- a/release/datafiles/icons/ops.curve.dupli_extrude_cursor.dat +++ /dev/null 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 c038f5f906a..bc76f338c38 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") @@ -756,7 +829,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") @@ -916,6 +997,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 c5191e80aef..ddf469a122b 100644 --- a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py +++ b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py @@ -1294,7 +1294,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 a332295715c..1c2c73a2600 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 16b5ed33f3f..7f9918fd9b9 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): @@ -865,6 +940,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.separator() @@ -938,6 +1014,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") @@ -945,6 +1022,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): @@ -2278,6 +2356,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 452a08bc9c8..4e8873f2cac 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; @@ -151,6 +153,13 @@ void BKE_brush_scale_size(int *r_brush_size, /* 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 73413b61456..22362b34548 100644 --- a/source/blender/blenkernel/BKE_paint.h +++ b/source/blender/blenkernel/BKE_paint.h @@ -23,15 +23,18 @@ * \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 MDynTopoVert; struct BMFace; struct BMesh; struct BlendDataReader; @@ -342,6 +345,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 { @@ -360,7 +368,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; @@ -371,13 +380,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; @@ -386,6 +405,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; @@ -395,12 +422,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 @@ -418,7 +445,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. */ @@ -440,7 +467,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; @@ -459,8 +486,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; @@ -495,8 +529,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; @@ -524,9 +563,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. @@ -548,9 +587,12 @@ typedef struct SculptSession { struct RegionView3D *rv3d; struct View3D *v3d; struct Scene *scene; + int cd_origvcol_offset; + int cd_origco_offset; + int cd_origno_offset; /* Dynamic mesh preview */ - int *preview_vert_index_list; + SculptVertRef *preview_vert_index_list; int preview_vert_index_count; /* Pose Brush Preview */ @@ -612,6 +654,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); @@ -619,6 +668,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. */ @@ -655,6 +705,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 056a7e2d897..663e7f8d7f7 100644 --- a/source/blender/blenkernel/BKE_pbvh.h +++ b/source/blender/blenkernel/BKE_pbvh.h @@ -22,20 +22,97 @@ */ #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; + +#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; +} + +#define SCULPT_REF_NONE ((intptr_t)-1) + +#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; @@ -52,12 +129,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 +229,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 +258,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 +269,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 +343,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, @@ -160,11 +354,13 @@ bool BKE_pbvh_node_raycast(PBVH *pbvh, const float ray_normal[3], struct IsectRayPrecalc *isect_precalc, float *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, @@ -189,7 +385,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 */ @@ -238,22 +435,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); @@ -292,10 +527,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 */ @@ -343,6 +582,7 @@ typedef struct PBVHVertexIter { int gy; int i; int index; + SculptVertRef vertex; bool respect_hide; /* grid */ @@ -362,10 +602,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 */ @@ -379,6 +623,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) \ @@ -388,7 +634,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]]; \ @@ -407,6 +653,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)) { \ @@ -427,7 +674,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]; \ } \ @@ -436,22 +683,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 \ @@ -460,24 +726,28 @@ 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_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 */ @@ -490,6 +760,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 d70b941695e..78fc0b54f5f 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" @@ -262,12 +263,47 @@ 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_blend_read_data(BlendDataReader *reader, ID *id) +ATTR_NO_OPT 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); @@ -464,7 +500,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); @@ -495,6 +537,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 @@ -1662,6 +1705,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); @@ -1703,6 +1748,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: @@ -1714,11 +1765,16 @@ void BKE_brush_sculpt_reset(Brush *br) br->curve_preset = BRUSH_CURVE_SMOOTHER; br->spacing = 10; br->alpha = 1.0f; + + disable_dyntopo = true; + break; case SCULPT_TOOL_SLIDE_RELAX: br->spacing = 10; br->alpha = 1.0f; br->slide_deform_type = BRUSH_SLIDE_DEFORM_DRAG; + + disable_dyntopo = true; break; case SCULPT_TOOL_CLAY: br->flag |= BRUSH_SIZE_PRESSURE; @@ -1766,18 +1822,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; @@ -1791,6 +1858,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; @@ -1799,18 +1868,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; @@ -1818,6 +1893,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; @@ -1826,6 +1903,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; @@ -1843,6 +1922,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; @@ -1850,6 +1931,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; @@ -1858,11 +1951,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 */ @@ -1919,6 +2018,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: @@ -2554,3 +2667,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/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..f78a322dd24 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, @@ -2883,6 +3042,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 +3916,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 +3941,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 +4047,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 6ac1aa9b2b9..89edda6e306 100644 --- a/source/blender/blenkernel/intern/mesh_fair.cc +++ b/source/blender/blenkernel/intern/mesh_fair.cc @@ -213,11 +213,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); /* Deformation coords. */ co_.reserve(mesh->totvert); 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 9f5703a015d..8ac18889f15 100644 --- a/source/blender/blenkernel/intern/mesh_remesh_voxel.cc +++ b/source/blender/blenkernel/intern/mesh_remesh_voxel.cc @@ -43,6 +43,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" @@ -65,17 +66,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( @@ -86,17 +98,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 */ @@ -158,6 +216,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 @@ -415,7 +481,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; @@ -449,7 +515,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 d6030941c6d..ab7d32160e5 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 + + })); } } } @@ -1466,7 +1481,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); } @@ -1482,10 +1509,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) { @@ -1608,6 +1631,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. */ @@ -1621,7 +1650,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; @@ -1645,6 +1674,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. */ @@ -1659,14 +1689,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; @@ -1674,6 +1706,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. */ @@ -1686,8 +1723,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); @@ -1697,8 +1736,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); @@ -1793,6 +1840,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) @@ -1809,17 +1857,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, need_pmap, need_mask, need_colors); } @@ -1896,11 +1946,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; } @@ -2094,14 +2163,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; } @@ -2118,17 +2194,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); @@ -2160,18 +2240,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))) { @@ -2181,6 +2293,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) { @@ -2191,14 +2305,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) { @@ -2206,11 +2365,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; } @@ -2232,6 +2400,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 ca1fada8c76..fc25826e731 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 10000 +#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); } @@ -2136,8 +2384,8 @@ static bool pbvh_faces_node_raycast(PBVH *pbvh, const float ray_normal[3], struct IsectRayPrecalc *isect_precalc, float *depth, - int *r_active_vertex_index, - int *r_active_face_index, + SculptVertRef *r_active_vertex_index, + SculptFaceRef *r_active_face_index, float *r_face_normal) { const MVert *vert = pbvh->verts; @@ -2186,8 +2434,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 = BKE_pbvh_make_vref(mloop[lt->tri[j]].v); + r_active_face_index->i = lt->poly; } } } @@ -2204,8 +2452,8 @@ static bool pbvh_grids_node_raycast(PBVH *pbvh, const float ray_normal[3], struct IsectRayPrecalc *isect_precalc, float *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; @@ -2271,13 +2519,14 @@ 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 = BKE_pbvh_make_vref(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; } } } @@ -2299,9 +2548,10 @@ bool BKE_pbvh_node_raycast(PBVH *pbvh, const float ray_normal[3], struct IsectRayPrecalc *isect_precalc, float *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; @@ -2335,15 +2585,19 @@ 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, depth, use_origco, active_vertex_index, - face_normal); + active_face_grid_index, + face_normal, + stroke_id); break; } @@ -2558,7 +2812,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; @@ -2577,7 +2832,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; } @@ -2731,6 +2986,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); @@ -2742,7 +3018,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]); + } } } @@ -2757,7 +3039,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); } } @@ -2873,7 +3157,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; @@ -2910,9 +3194,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; } @@ -2938,8 +3236,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) { @@ -2967,9 +3268,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); } @@ -2985,8 +3298,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); @@ -2999,15 +3316,39 @@ 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); +} + +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; @@ -3039,12 +3380,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) @@ -3067,3 +3409,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..23e887f6eca 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,651 @@ 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, + float *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; - } - - /* Remove the merge vertex from the PBVH */ - pbvh_bmesh_vert_remove(pbvh, v_del); - - /* Remove all faces adjacent to the edge */ - BMLoop *l_adj; - while ((l_adj = e->l)) { - BMFace *f_adj = l_adj->f; - - pbvh_bmesh_face_remove(pbvh, f_adj); - BM_face_kill(pbvh->bm, f_adj); - } + bool hit = false; + float nearest_vertex_co[3] = {0.0f}; + float nearest_vertex_dist = 1e17; - /* Kill the edge */ - BLI_assert(BM_edge_is_wire(e)); - BM_edge_kill(pbvh->bm, e); + BKE_pbvh_bmesh_check_tris(pbvh, node); - /* 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); + PBVHTriBuf *tribuf = node->tribuf; + const int cd_dyn_vert = pbvh->cd_dyn_vert; - BMLoop *l; + 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; - BM_LOOPS_OF_VERT_ITER_BEGIN (l, v_del) { - BMFace *existing_face; + BMFace *f = (BMFace *)tri->f.i; - /* 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; - } + if (BM_elem_flag_test(f, BM_ELEM_HIDDEN)) { + continue; } -#endif - /* Check if a face using these vertices already exists. If so, - * skip adding this face and mark the existing one for - * deletion as well. Prevents extraneous "flaps" from being - * created. */ -#if 0 - if (UNLIKELY(existing_face = BM_face_exists(v_tri, 3))) -#else - if (UNLIKELY(existing_face = bm_face_exists_tri_from_loop_vert(l->next, v_conn))) -#endif - { - BLI_buffer_append(deleted_faces, BMFace *, existing_face); - } - else - { - BMVert *v_tri[3] = {v_conn, l->next->v, l->prev->v}; + float *co1, *co2, *co3; - 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); + 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); - /* 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); - } + 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; + } + 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"); + if (node->flag & PBVH_FullyHidden) { + return 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]; - BLI_mempool_free(eq_ctx->pool, pair); - pair = NULL; + 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 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)) { + if (BM_elem_flag_test(f, BM_ELEM_HIDDEN)) { continue; } - /* Check that the edge still exists */ - BMEdge *e; - if (!(e = BM_edge_exists(v1, v2))) { - continue; - } -#ifdef USE_EDGEQUEUE_TAG - EDGE_QUEUE_DISABLE(e); -#endif + bool hit_local = ray_face_intersection_tri( + ray_start, isect_precalc, v1->co, v2->co, v3->co, depth); - if (len_squared_v3v3(v1->co, v2->co) >= min_len_squared) { - 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); - /* 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; - } + /* detail returned will be set to the maximum allowed size, so take max here */ + *r_edge_length = sqrtf(max_fff(len1, len2, len3)); - any_collapsed = true; - - 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; + } - if (hit_local) { - f_hit = f; - hit = true; + do { + BMLoop *l = e->l; + + 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]; + + 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); - /* detail returned will be set to the maximum allowed size, so take max here */ - *r_edge_length = sqrtf(max_fff(len1, len2, len3)); + 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; + TaskParallelSettings settings; + UpdateNormalsTaskData data = {nodes, totnode}; - 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; + BKE_pbvh_parallel_range_settings(&settings, true, totnode); + BLI_task_parallel_range(0, totnode, &data, pbvh_update_normals_task_cb, &settings); - GSET_ITER (gs_iter, node->bm_faces) { - BMFace *f = BLI_gsetIterator_getKey(&gs_iter); +#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]; - BLI_assert(f->len == 3); - if (!BM_elem_flag_test(f, BM_ELEM_HIDDEN)) { - BMVert *v_tri[3]; - - BM_face_as_array_vert_tri(f, v_tri); - 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 +1166,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 +1181,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 +1263,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 +1279,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 +1307,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 +1323,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 +1331,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 +1359,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 +1752,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 +1778,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 +3217,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 +3229,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; + } -#ifdef USE_VERIFY + 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)); -static void pbvh_bmesh_verify(PBVH *pbvh) + BM_mesh_remap(pbvh->bm, vidx, eidx, fidx, lidx); + + MEM_SAFE_FREE(vidx); + MEM_SAFE_FREE(eidx); + MEM_SAFE_FREE(lidx); + MEM_SAFE_FREE(fidx); + + MEM_SAFE_FREE(nstack); + MEM_SAFE_FREE(roots); + BLI_mempool_destroy(pool); + MEM_SAFE_FREE(stack); + MEM_SAFE_FREE(vnodemap); + + return pbvh->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, ¶ms); + + 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; - 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)); + float co[3]; + + zero_v3(co); + int tot = 0.0; + + if (ei == -1) { + continue; + } + + const float *no = m->v_no[vi]; + const float *vco = m->v_co[vi]; + + do { + int ev1 = m->e_v1[ei]; + int ev2 = m->e_v2[ei]; + + int v2i = ev1 == vi ? ev2 : ev1; + + float co2[3]; + + sub_v3_v3v3(co2, m->v_co[v2i], vco); + madd_v3_v3fl(co2, no, -dot_v3v3(no, co2) * 0.9f); + add_v3_v3(co, co2); + + tot++; + + ei = ev1 == vi ? m->e_v1_next[ei] : m->e_v2_next[ei]; + } while (ei != startei); + + if (tot == 0.0) { + continue; + } + + mul_v3_fl(co, 1.0f / (float)tot); + madd_v3_v3fl(m->v_co[vi], co, 0.5f); } + } + + double time2 = PIL_check_seconds_timer(); + + double time = time2 - time1; + + printf(" time: %.5f, %d of %d\n", time, iter + 1, SMOOTH_TEST_STEPS); + + // skip first five + if (iter >= 5) { + average += time; + average_tot += 1.0f; + } + + BLI_rng_free(rng); + } + + printf("time: %.5f\n", average / average_tot); + return average / average_tot; +} + +/* +test results from blenderartists thread: + +random, cluster, percent, data, data_perc, indices, ind_perc, mem (gb) + [1.22, 1.04, 14.42, 0.73, 67, 0.94, 29, 0], + [1.49, 1.46, 2.35, 1.10, 36, 1.17, 27, 0], + [1.29, 1.13, 14, 0.75, 71.54, 0.89, 45.08 , 0], + [1.58, 1.40, 12.3, 1.09, 44.7, 1.11, 42.42, 16], + [1.53, 1.36, 12.77, 1.08, 41.6, 1.07, 42.91, 0], + [1.56, 1.39, 12.47, 1.09, 42.65, 1.10, 42.15, 16], + [1.22, 1.06, 15.05, 0.75, 63.85, 0.82, 49.67, 32] + +[random] average: 1.41 variange: 0.15 median: 1.49 +[cluster] average: 1.26 variange: 0.17 median: 1.36 +[cluster-percent] average: 11.91 variange: 4.02 median: 12.77 +[data] average: 0.94 variange: 0.17 median: 1.08 +[data-percent] average: 52.48 variange: 13.37 median: 44.70 +[indices] average: 1.01 variange: 0.12 median: 1.07 +[indices-percent] average: 39.75 variange: 7.82 median: 42.42 + +So looks like the biggest gain is from replacing pointers with indices +(which lessens total memory bandwidth). The pure data-oriented version +is a tad bit faster then the index-replacement one, but not by that much. +*/ + +void pbvh_bmesh_cache_test(CacheParams *params, BMesh **r_bm, PBVH **r_pbvh_out) +{ + // build mesh + const int steps = 325; + + printf("== Starting Test ==\n"); + + printf("building test mesh. . .\n"); + + BMAllocTemplate templ = {0, 0, 0, 0}; + + 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); - 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)); + GHash *vhash = BLI_ghash_ptr_new("vhash"); + + 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(¶ms); + // pbvh_bmesh_cache_test(¶ms, &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..b04aa5f2f11 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, @@ -218,19 +264,85 @@ bool ray_face_nearest_tri(const float ray_start[3], void pbvh_update_BB_redraw(PBVH *bvh, PBVHNode **nodes, int totnode, int flag); /* 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, bool use_original, - int *r_active_vertex_index, - float *r_face_normal); -bool pbvh_bmesh_node_nearest_to_ray(PBVHNode *node, + struct SculptVertRef *r_active_vertex_index, + struct 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..65f4873f0fd 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 bf0463432db..7ad21b9f1ed 100644 --- a/source/blender/blenloader/intern/versioning_280.c +++ b/source/blender/blenloader/intern/versioning_280.c @@ -4424,7 +4424,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 f023813555f..7693dc5c8fb 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" @@ -55,6 +56,7 @@ #include "BKE_animsys.h" #include "BKE_armature.h" #include "BKE_attribute.h" +#include "BKE_brush.h" #include "BKE_collection.h" #include "BKE_colortools.h" #include "BKE_cryptomatte.h" @@ -1940,6 +1942,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..2ac98a11e18 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,93 @@ 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; + + 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 +1336,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 +1350,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 ae0dbb7f808..f94b0020fe8 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..380596492aa 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 %x; 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..e5ea4461119 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, ¶ms->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, ¶ms->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, ¶ms->cd_mask_extra); + + MultiresModifierData *mmd = ob ? get_multires_modifier(NULL, ob, true) : NULL; + + if (mmd) { + bm->multires = *mmd; + bm->haveMultiResSettings = true; + bm->multiresSpace = MULTIRES_SPACE_TANGENT; + } + else { + bm->haveMultiResSettings = false; + } + + CustomData_copy(&me->vdata, &bm->vdata, mask.vmask, CD_ASSIGN, CD_FLAG_NOCOPY); + CustomData_copy(&me->edata, &bm->edata, mask.emask, CD_ASSIGN, CD_FLAG_NOCOPY); + CustomData_copy(&me->ldata, &bm->ldata, mask.lmask, CD_ASSIGN, CD_FLAG_NOCOPY); + CustomData_copy(&me->pdata, &bm->pdata, mask.pmask, CD_ASSIGN, CD_FLAG_NOCOPY); + + CustomData *cds[] = {&bm->vdata, &bm->edata, &bm->ldata, &bm->pdata}; + + // clear customdata->layers[X].data pointers + for (int i = 0; i < 4; i++) { + CustomData *cd = cds[i]; + for (int j = 0; j < cd->totlayer; j++) { + cd->layers[j].data = NULL; + } + } + bm_mesh_cd_flag_apply(bm, me->cd_flag); + + data.cd_vert_bweight = CustomData_get_offset(&bm->vdata, CD_BWEIGHT); + data.cd_edge_bweight = CustomData_get_offset(&bm->edata, CD_BWEIGHT); + data.cd_crease = CustomData_get_offset(&bm->edata, CD_CREASE); + + if (bm->vdata.totlayer) { + bm->vdata.pool = BLI_mempool_create_for_tasks( + bm->vdata.totsize, bm->totvert, VCHUNK, &vdata, &totdv, &data.cdvsize, BLI_MEMPOOL_NOP); + } + if (bm->edata.totlayer) { + bm->edata.pool = BLI_mempool_create_for_tasks( + bm->edata.totsize, bm->totedge, ECHUNK, &edata, &totde, &data.cdesize, BLI_MEMPOOL_NOP); + } + if (bm->ldata.totlayer) { + bm->ldata.pool = BLI_mempool_create_for_tasks( + bm->ldata.totsize, bm->totloop, LCHUNK, &ldata, &totdl, &data.cdlsize, BLI_MEMPOOL_NOP); + } + if (bm->pdata.totlayer) { + bm->pdata.pool = BLI_mempool_create_for_tasks( + bm->pdata.totsize, bm->totface, FCHUNK, &fdata, &totdf, &data.cdfsize, BLI_MEMPOOL_NOP); + } + + data.vdata = vdata; + data.edata = edata; + data.ldata = ldata; + data.fdata = fdata; + + data.totdv = totdv; + data.totde = totde; + data.totdl = totdl; + data.totdf = totdf; + + /* -------------------------------------------------------------------- */ + /* Shape Key */ + int tot_shape_keys = 0; + if (me->key != NULL && DEG_is_original_id(&me->id)) { + /* Evaluated meshes can be topologically inconsistent with their shape keys. + * Shape keys are also already integrated into the state of the evaluated + * mesh, so considering them here would kind of apply them twice. */ + tot_shape_keys = BLI_listbase_count(&me->key->block); + + /* Original meshes must never contain a shape-key custom-data layers. + * + * This may happen if and object's mesh data is accidentally + * set to the output from the modifier stack, causing it to be an "original" ID, + * even though the data isn't fully compatible (hence this assert). + * + * This results in: + * - The newly created #BMesh having twice the number of custom-data layers. + * - When converting the #BMesh back to a regular mesh, + * At least one of the extra shape-key blocks will be created in #Mesh.key + * depending on the value of #CustomDataLayer.uid. + * + * We could support mixing both kinds of data if there is a compelling use-case for it. + * At the moment it's simplest to assume all original meshes use the key-block and meshes + * that are evaluated (through the modifier stack for example) use custom-data layers. + */ + BLI_assert(!CustomData_has_layer(&me->vdata, CD_SHAPEKEY)); + } + if (is_new == false) { + tot_shape_keys = min_ii(tot_shape_keys, CustomData_number_of_layers(&bm->vdata, CD_SHAPEKEY)); + } + + float(**shape_key_table)[3] = tot_shape_keys ? + BLI_array_alloca(shape_key_table, tot_shape_keys) : + NULL; + + if ((params->active_shapekey != 0) && tot_shape_keys > 0) { + actkey = BLI_findlink(&me->key->block, params->active_shapekey - 1); + } + else { + actkey = NULL; + } + + if (is_new) { + if (tot_shape_keys || params->add_key_index) { + CustomData_add_layer(&bm->vdata, CD_SHAPE_KEYINDEX, CD_ASSIGN, NULL, 0); + } + } + + if (tot_shape_keys) { + if (is_new) { + /* Check if we need to generate unique ids for the shape-keys. + * This also exists in the file reading code, but is here for a sanity check. */ + if (!me->key->uidgen) { + fprintf(stderr, + "%s had to generate shape key uid's in a situation we shouldn't need to! " + "(bmesh internal error)\n", + __func__); + + me->key->uidgen = 1; + for (block = me->key->block.first; block; block = block->next) { + block->uid = me->key->uidgen++; + } + } + } + + if (actkey && actkey->totelem == me->totvert) { + keyco = params->use_shapekey ? actkey->data : NULL; + if (is_new) { + bm->shapenr = params->active_shapekey; + } + } + + for (i = 0, block = me->key->block.first; i < tot_shape_keys; block = block->next, i++) { + if (is_new) { + CustomData_add_layer_named(&bm->vdata, CD_SHAPEKEY, CD_ASSIGN, NULL, 0, block->name); + int j = CustomData_get_layer_index_n(&bm->vdata, CD_SHAPEKEY, i); + bm->vdata.layers[j].uid = block->uid; + } + shape_key_table[i] = (float(*)[3])block->data; + } + } + + data.tot_shape_keys = tot_shape_keys; + data.shape_key_table = shape_key_table; + + TaskParallelSettings settings; + + BLI_parallel_range_settings_defaults(&settings); + BLI_task_parallel_range(0, data.totcv, &data, bm_vert_task, &settings); + BLI_task_parallel_range(0, data.totce, &data, bm_edge_task, &settings); + BLI_task_parallel_range(0, data.totcl, &data, bm_loop_task, &settings); + BLI_task_parallel_range(0, data.totcf, &data, bm_face_task, &settings); + + BMIter iter; + + // link edges + BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) { + bmesh_disk_edge_append(e, e->v1); + bmesh_disk_edge_append(e, e->v2); + } + + // link radial lists + i = 0; + BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { + BMLoop *l = f->l_first; + + do { + bmesh_radial_loop_append(l->e, l); + + l = l->next; + } while (l != f->l_first); + + i++; + } + + printf("totface: %d\n", i); + + bm->elem_index_dirty = BM_VERT | BM_EDGE | BM_FACE; + bm->elem_table_dirty = BM_VERT | BM_EDGE | BM_FACE; + + return bm; +} + +static void bm_unmark_temp_cdlayers(BMesh *bm) +{ + CustomData_unmark_temporary_nocopy(&bm->vdata); + CustomData_unmark_temporary_nocopy(&bm->edata); + CustomData_unmark_temporary_nocopy(&bm->ldata); + CustomData_unmark_temporary_nocopy(&bm->pdata); +} + +static void bm_mark_temp_cdlayers(BMesh *bm) +{ + CustomData_mark_temporary_nocopy(&bm->vdata); + CustomData_mark_temporary_nocopy(&bm->edata); + CustomData_mark_temporary_nocopy(&bm->ldata); + CustomData_mark_temporary_nocopy(&bm->pdata); +} + +typedef struct BMToMeTask { + Mesh *me; + BMesh *bm; + Object *ob; + Main *bmain; + const struct BMeshToMeshParams *params; + struct CustomData_MeshMasks mask; + uint64_t extra2; +} BMToMeTask; + +static void me_vert_task(void *__restrict userdata) +{ + BMToMeTask *data = (BMToMeTask *)userdata; + Mesh *me = data->me; + BMesh *bm = data->bm; + + CustomData_free(&me->vdata, me->totvert); + me->totvert = bm->totvert; + + CustomData_copy(&bm->vdata, &me->vdata, data->mask.vmask | data->extra2, CD_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, ¶ms->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 03c99e40d1e..cd166a96e00 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); @@ -288,3 +289,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 94823b92c44..6df29316c07 100644 --- a/source/blender/editors/mesh/mesh_ops.c +++ b/source/blender/editors/mesh/mesh_ops.c @@ -205,6 +205,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 d56cb3c7548..577cb63587d 100644 --- a/source/blender/editors/object/object_remesh.cc +++ b/source/blender/editors/object/object_remesh.cc @@ -763,7 +763,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; @@ -784,8 +784,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); } @@ -853,7 +855,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 3b668a1bd4c..77a633f0932 100644 --- a/source/blender/editors/sculpt_paint/CMakeLists.txt +++ b/source/blender/editors/sculpt_paint/CMakeLists.txt @@ -56,6 +56,7 @@ set(SRC paint_vertex_weight_ops.c paint_vertex_weight_utils.c sculpt.c + sculpt_curvature.c sculpt_automasking.c sculpt_boundary.c sculpt_cloth.c @@ -76,6 +77,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 ab2b2f4b16b..2c918033ad5 100644 --- a/source/blender/editors/sculpt_paint/paint_cursor.c +++ b/source/blender/editors/sculpt_paint/paint_cursor.c @@ -1223,7 +1223,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; @@ -1589,8 +1589,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); 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 e65d6ce2d48..09f4d7a5ffb 100644 --- a/source/blender/editors/sculpt_paint/paint_mask.c +++ b/source/blender/editors/sculpt_paint/paint_mask.c @@ -679,7 +679,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); @@ -758,7 +758,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; } } @@ -974,7 +974,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, @@ -1040,7 +1041,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. */ @@ -1210,7 +1211,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) @@ -1220,20 +1221,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); + } + + 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(bm, - sculpt_mesh, + BM_mesh_bm_from_me(NULL, + bm, + trim_mesh, &((struct BMeshFromMeshParams){ .calc_face_normal = true, })); @@ -1294,15 +1305,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) @@ -1339,6 +1367,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); @@ -1548,8 +1580,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 de01bc3a474..a8cb05c6d2c 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) { @@ -288,6 +217,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, @@ -430,11 +379,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; @@ -767,6 +718,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, @@ -797,6 +800,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, @@ -866,8 +871,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; @@ -1233,6 +1247,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]; @@ -1250,6 +1266,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; @@ -1472,6 +1490,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( @@ -1558,6 +1577,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..33d8be08c0c 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 */ diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c index 7bde864e73f..fcece692172 100644 --- a/source/blender/editors/sculpt_paint/sculpt.c +++ b/source/blender/editors/sculpt_paint/sculpt.c @@ -24,15 +24,21 @@ #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_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" @@ -40,12 +46,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" @@ -101,6 +109,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 @@ -111,12 +122,34 @@ void SCULPT_vertex_random_access_ensure(SculptSession *ss) { - if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) { + 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_mesh_elem_table_ensure(ss->bm, BM_VERT); } } +/* 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); + } +} + int SCULPT_vertex_count_get(SculptSession *ss) { switch (BKE_pbvh_type(ss->pbvh)) { @@ -131,22 +164,64 @@ 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: { + MDynTopoVert *mv = ss->mdyntopo_verts + vertex.i; + + 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)); } @@ -154,41 +229,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; @@ -196,32 +283,48 @@ 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 index, float r_co[3]) { switch (BKE_pbvh_type(ss->pbvh)) { case PBVH_FACES: @@ -230,8 +333,8 @@ void SCULPT_vertex_limit_surface_get(SculptSession *ss, int index, float r_co[3] 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; SubdivCCGCoord coord = {.grid_index = grid_index, .x = vertex_index % key->grid_size, @@ -242,30 +345,177 @@ void SCULPT_vertex_limit_surface_get(SculptSession *ss, int index, float r_co[3] } } -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) +{ + 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)); } @@ -274,12 +524,27 @@ 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) +{ + SculptCustomLayer scl; + return sculpt_temp_customlayer_get(ss, domain, proptype, name, &scl, true); +} + +bool SCULPT_temp_customlayer_get( + SculptSession *ss, AttributeDomain domain, int proptype, char *name, SculptCustomLayer *scl) +{ + return sculpt_temp_customlayer_get(ss, domain, proptype, name, scl, true); +} + +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) @@ -332,44 +597,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); @@ -396,8 +666,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; + } } } @@ -410,8 +700,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; + } } } @@ -437,48 +738,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; } @@ -486,22 +847,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); @@ -511,24 +887,39 @@ void SCULPT_vertex_face_set_set(SculptSession *ss, int index, int face_set) } } -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]; } @@ -536,23 +927,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; } @@ -560,6 +968,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; @@ -574,16 +1030,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]]); @@ -597,28 +1095,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 bool sculpt_check_unique_face_set_in_base_mesh(SculptSession *ss, int index) +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(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++) { @@ -638,7 +1218,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; @@ -666,18 +1248,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}; @@ -686,7 +1300,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: @@ -711,8 +1325,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; } @@ -721,10 +1351,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; } } @@ -733,51 +1366,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) { @@ -788,8 +1493,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]); } } } @@ -797,17 +1515,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_grids(SculptSession *ss, - const int index, +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(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. */ @@ -822,21 +1584,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); } } @@ -845,45 +1615,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; @@ -895,17 +1906,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 */ @@ -924,8 +1939,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) { @@ -962,6 +1977,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; @@ -979,6 +1995,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; } } @@ -993,15 +2010,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; @@ -1016,7 +2035,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 = { @@ -1029,6 +2048,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; @@ -1040,7 +2060,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) @@ -1091,23 +2111,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); @@ -1115,18 +2141,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); } } @@ -1141,7 +2167,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); } @@ -1151,26 +2179,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; + + const int to_index = BKE_pbvh_vertex_index_to_table(ss->pbvh, to_v); - if (BLI_BITMAP_TEST(flood->visited_vertices, to_v)) { + if (BLI_BITMAP_TEST(flood->visited_vertices, to_index)) { continue; } @@ -1178,7 +2211,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); @@ -1213,11 +2246,13 @@ 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_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, @@ -1280,19 +2315,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; } } @@ -1300,37 +2339,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; } } @@ -1450,15 +2518,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 ***/ @@ -1478,7 +2548,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) { @@ -1491,7 +2561,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); @@ -1997,17 +3067,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]; @@ -2059,11 +3130,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]); @@ -2359,13 +3430,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: @@ -2427,7 +3498,10 @@ static float brush_strength(const Sculpt *sd, case SCULPT_TOOL_SMOOTH: return flip * alpha * pressure * feather; - + 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; @@ -2470,7 +3544,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; @@ -2684,7 +3758,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 = { @@ -2874,10 +3949,10 @@ typedef struct { float depth; bool original; - 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; @@ -2899,6 +3974,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); @@ -2921,26 +3997,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); + } - sub_v3_v3v3(val, avg, vd.co); +#if 0 + if (SCULPT_vertex_is_boundary( + ss, vd.vertex, SCULPT_BOUNDARY_SHARP | SCULPT_BOUNDARY_MESH | check_fsets)) { + continue; + } - madd_v3_v3v3fl(val, vd.co, val, fade); + MDynTopoVert *mv = BKE_PBVH_DYNVERT(ss->cd_dyn_vert, vd.bm_vert); + if (check_fsets && (mv->flag & (DYNVERT_FSET_CORNER))) { + continue; + } - SCULPT_clip(sd, ss, vd.co, val); + if (mv->flag & (DYNVERT_CORNER | DYNVERT_SHARP_CORNER)) { + continue; + } +#endif + + 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_bmesh_four_neighbor_average(ss, + avg, + direction2, + vd.bm_vert, + data->rake_projection, + check_fsets, + data->cd_temp, + data->cd_dyn_vert, + 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; @@ -2952,11 +4094,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; @@ -2964,13 +4158,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); @@ -3000,7 +4196,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); @@ -3044,7 +4240,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; } } @@ -3081,12 +4277,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); @@ -3146,7 +4342,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]; @@ -3173,11 +4369,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]); @@ -3217,7 +4413,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; @@ -3229,16 +4425,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]); } } @@ -3290,7 +4490,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); @@ -3347,7 +4547,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; @@ -3357,7 +4557,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; } @@ -3369,7 +4570,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); @@ -3429,7 +4630,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; @@ -3439,7 +4640,8 @@ 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; } @@ -3450,7 +4652,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]; @@ -3472,10 +4674,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)); @@ -3505,31 +4707,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++; } } @@ -3554,11 +4767,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)) { @@ -3586,7 +4799,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]); @@ -3596,7 +4809,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; } @@ -3607,7 +4821,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); @@ -3765,6 +4979,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, @@ -3773,7 +4988,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]; @@ -3819,7 +5034,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; @@ -3833,8 +5048,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. */ @@ -3889,7 +5104,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]; @@ -3981,7 +5196,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; @@ -3993,7 +5208,7 @@ static void do_grab_brush_task_cb_ex(void *__restrict userdata, const bool grab_silhouette = brush->flag2 & BRUSH_GRAB_SILHOUETTE; 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; @@ -4005,7 +5220,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) { @@ -4069,7 +5284,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; @@ -4094,7 +5309,7 @@ static void do_elastic_deform_brush_task_cb_ex(void *__restrict userdata, ¶ms, ss->cache->radius, force, 1.0f, brush->elastic_deform_volume_preservation); BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { - SCULPT_orig_vert_data_update(&orig_data, &vd); + SCULPT_orig_vert_data_update(&orig_data, vd.vertex); float final_disp[3]; switch (brush->elastic_deform_type) { case BRUSH_ELASTIC_DEFORM_GRAB: @@ -4125,13 +5340,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)); - - copy_v3_v3(proxy[vd.i], final_disp); + mul_v3_fl(final_disp, SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.vertex)); - 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; } @@ -4157,6 +5374,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); } @@ -4334,7 +5554,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); @@ -4419,7 +5639,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); } @@ -4467,7 +5687,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); } @@ -4530,7 +5750,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; @@ -4540,7 +5760,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; @@ -4552,7 +5772,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); @@ -4603,7 +5823,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; @@ -4613,7 +5833,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; @@ -4626,7 +5846,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); @@ -4663,6 +5883,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) @@ -4672,12 +5894,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( @@ -4685,7 +5936,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; @@ -4697,13 +5948,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]; @@ -4733,9 +5994,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); @@ -4755,27 +6019,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); } @@ -4809,7 +6203,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]; @@ -4922,9 +6316,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) { @@ -5077,7 +6470,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); @@ -5203,9 +6596,8 @@ 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); if (vd.mvert) { @@ -5260,14 +6652,14 @@ static void do_clay_strips_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int t mul_v3_fl(temp, displace); add_v3_v3(area_co, temp); - /* Clay Strips uses a cube test with falloff in the XY axis (not in Z) and a plane to deform the - * vertices. When in Add mode, vertices that are below the plane and inside the cube are move - * towards the plane. In this situation, there may be cases where a vertex is outside the cube - * but below the plane, so won't be deformed, causing artifacts. In order to prevent these + /* Clay Strips uses a cube test with falloff in the XY axis (not in Z) and a plane to deform + * the vertices. When in Add mode, vertices that are below the plane and inside the cube are + * move towards the plane. In this situation, there may be cases where a vertex is outside the + * cube but below the plane, so won't be deformed, causing artifacts. In order to prevent these * artifacts, this displaces the test cube space in relation to the plane in order to * deform more vertices that may be below it. */ - /* The 0.7 and 1.25 factors are arbitrary and don't have any relation between them, they were set - * by doing multiple tests using the default "Clay Strips" brush preset. */ + /* The 0.7 and 1.25 factors are arbitrary and don't have any relation between them, they were + * set by doing multiple tests using the default "Clay Strips" brush preset. */ float area_co_displaced[3]; madd_v3_v3v3fl(area_co_displaced, area_co, area_no, -radius * 0.7f); @@ -5286,8 +6678,8 @@ static void do_clay_strips_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int t scale_m4_fl(scale, ss->cache->radius); mul_m4_m4m4(tmat, mat, scale); - /* Deform the local space in Z to scale the test cube. As the test cube does not have falloff in - * Z this does not produce artifacts in the falloff cube and allows to deform extra vertices + /* Deform the local space in Z to scale the test cube. As the test cube does not have falloff + * in Z this does not produce artifacts in the falloff cube and allows to deform extra vertices * during big deformation while keeping the surface as uniform as possible. */ mul_v3_fl(tmat[2], 1.25f); @@ -5356,7 +6748,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); @@ -5454,7 +6846,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); @@ -5570,7 +6962,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); @@ -5625,8 +7017,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); @@ -5709,7 +7101,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); @@ -5791,7 +7183,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, @@ -5801,65 +7294,97 @@ 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.connected_component); 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; + + 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, @@ -5883,32 +7408,51 @@ 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; } /* 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 */ @@ -5918,13 +7462,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); } @@ -5936,9 +7477,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); } @@ -5970,16 +7509,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); @@ -6006,6 +7577,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: @@ -6013,7 +7586,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); @@ -6116,21 +7689,97 @@ static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSe case SCULPT_TOOL_SMEAR: SCULPT_do_smear_brush(sd, ob, nodes, totnode); break; + 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. */ @@ -6214,7 +7863,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]); @@ -6239,7 +7890,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); @@ -6304,6 +7955,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); @@ -6362,8 +8017,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) { @@ -6587,6 +8242,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); } @@ -6674,6 +8345,10 @@ static const char *sculpt_tool_name(Sculpt *sd) return "Paint Brush"; case SCULPT_TOOL_SMEAR: return "Smear Brush"; + case SCULPT_TOOL_VCOL_BOUNDARY: + return "Color Boundary"; + case SCULPT_TOOL_UV_SMOOTH: + return "UV Smooth"; } return "Sculpting"; @@ -6693,6 +8368,10 @@ void SCULPT_cache_free(StrokeCache *cache) MEM_SAFE_FREE(cache->prev_displacement); MEM_SAFE_FREE(cache->limit_surface_co); + 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); } @@ -6700,6 +8379,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; } } @@ -6756,6 +8436,7 @@ static void sculpt_update_cache_invariants( float max_scale; int mode; + cache->C = C; ss->cache = cache; /* Set scaling adjustment. */ @@ -6932,8 +8613,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, @@ -7146,9 +8827,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); } @@ -7274,7 +8955,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) @@ -7286,6 +8967,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_SLIDE_RELAX) || (brush->sculpt_tool == SCULPT_TOOL_CLOTH) || (brush->sculpt_tool == SCULPT_TOOL_SMEAR) || @@ -7322,7 +9006,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; } @@ -7338,7 +9022,8 @@ static void sculpt_raycast_cb(PBVHNode *node, void *data_v, float *tmin) &srd->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; } @@ -7359,7 +9044,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; } @@ -7372,7 +9057,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; } @@ -7415,8 +9101,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, @@ -7462,7 +9149,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) { @@ -7474,6 +9162,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)); @@ -7483,11 +9172,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; } @@ -7574,11 +9263,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; @@ -7591,7 +9275,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); @@ -7767,7 +9452,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; } @@ -7785,6 +9471,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 @@ -7836,12 +9529,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 */ @@ -7882,6 +9594,13 @@ 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; @@ -7894,35 +9613,73 @@ 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); 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); } - else if (sd->flags & SCULPT_DYNTOPO_DETAIL_BRUSH) { - BKE_pbvh_bmesh_detail_size_set(ss->pbvh, ss->cache->radius * sd->detail_percent / 100.0f); + + 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 (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); @@ -8161,13 +9918,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; } @@ -8229,6 +10016,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); @@ -8252,7 +10102,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); @@ -8263,15 +10112,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; @@ -8311,7 +10167,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", @@ -8344,10 +10200,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. */ @@ -8365,7 +10221,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); @@ -8389,32 +10246,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"); } @@ -8423,19 +10274,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( @@ -8454,7 +10323,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) @@ -8534,7 +10403,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) { @@ -8610,29 +10479,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); } @@ -8705,7 +10583,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; @@ -8768,7 +10646,7 @@ 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; @@ -8783,7 +10661,7 @@ static int sculpt_sample_color_invoke(bContext *C, Object *ob = CTX_data_active_object(C); Brush *brush = BKE_paint_brush(&sd->paint); SculptSession *ss = ob->sculpt; - int active_vertex = SCULPT_active_vertex_get(ss); + SculptVertRef active_vertex = SCULPT_active_vertex_get(ss); const float *active_vertex_color = SCULPT_vertex_color_get(ss, active_vertex); if (!active_vertex_color) { return OPERATOR_CANCELLED; @@ -8840,10 +10718,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; } @@ -8852,19 +10734,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; } } @@ -8875,6 +10766,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; @@ -8888,14 +10780,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; } } @@ -8909,17 +10804,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; @@ -8934,7 +10835,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 = { @@ -8948,6 +10849,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); @@ -8960,19 +10862,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; } @@ -8980,7 +10887,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; @@ -8995,10 +10905,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); @@ -9011,32 +10927,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) @@ -9044,7 +10967,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) { @@ -9055,12 +10979,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); } @@ -9177,16 +11102,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); @@ -9195,7 +11123,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) @@ -9284,7 +11212,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) @@ -9330,8 +11258,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]; @@ -9340,7 +11268,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"); @@ -9395,6 +11323,685 @@ static void SCULPT_OT_mask_by_color(wmOperatorType *ot) 1.0f); } +#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) { WM_operatortype_append(SCULPT_OT_brush_stroke); @@ -9430,6 +12037,7 @@ 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_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_automasking.c b/source/blender/editors/sculpt_paint/sculpt_automasking.c index 35f48400fe2..09d5298f7a5 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 @@ -226,7 +278,7 @@ static float *SCULPT_topology_automasking_init(Sculpt *sd, Object *ob, float *au SCULPT_floodfill_add_active(sd, ob, ss, &flood, radius); AutomaskFloodFillData fdata = { - .automask_factor = automask_factor, + .factorlayer = factorlayer, .radius = radius, .use_radius = ss->cache && sculpt_automasking_is_constrained_by_radius(brush), .symm = SCULPT_mesh_symmetry_xyz_get(ob), @@ -234,62 +286,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; @@ -298,11 +354,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; } @@ -312,28 +370,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); @@ -350,9 +468,29 @@ 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)) { + // 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 ? @@ -361,22 +499,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 37678ec276a..b55e362c142 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,66 +952,571 @@ 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); + + 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++) { @@ -602,7 +1524,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]); } } @@ -610,7 +1532,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])); @@ -657,7 +1579,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; @@ -672,16 +1594,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, @@ -711,7 +1634,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); @@ -720,14 +1643,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, @@ -757,7 +1680,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); @@ -766,14 +1689,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); @@ -805,21 +1728,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, @@ -848,7 +1771,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; @@ -863,14 +1786,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); @@ -902,14 +1825,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; @@ -919,9 +1842,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++; } } @@ -946,16 +1869,126 @@ static void do_boundary_brush_smooth_task_cb_ex(void *__restrict userdata, BKE_pbvh_vertex_iter_end; } +static void SCULPT_boundary_autosmooth(SculptSession *ss, SculptBoundary *boundary) +{ + 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); } @@ -970,10 +2003,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]); @@ -990,6 +2022,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. */ @@ -1027,6 +2086,12 @@ void SCULPT_do_boundary_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totn BLI_task_parallel_range(0, totnode, &data, do_boundary_brush_smooth_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 a53a2126af4..a6d23131fdb 100644 --- a/source/blender/editors/sculpt_paint/sculpt_cloth.c +++ b/source/blender/editors/sculpt_paint/sculpt_cloth.c @@ -219,26 +219,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), @@ -252,7 +258,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, @@ -386,7 +392,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++; } @@ -556,7 +562,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]; @@ -803,7 +809,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); @@ -849,6 +855,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); @@ -871,10 +880,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); @@ -1065,6 +1074,13 @@ 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"); @@ -1147,16 +1163,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)); } } } @@ -1165,7 +1185,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)); } } @@ -1448,13 +1470,13 @@ static void cloth_filter_apply_forces_task_cb(void *__restrict userdata, PBVHVertexIter vd; BKE_pbvh_vertex_iter_begin (ss->pbvh, node, 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); 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; } } @@ -1473,7 +1495,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: @@ -1538,7 +1560,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 = { 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 188bb0a88eb..4676ff50079 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..2d5d428e676 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 40874375772..3257b1a9c5e 100644 --- a/source/blender/editors/sculpt_paint/sculpt_expand.c +++ b/source/blender/editors/sculpt_paint/sculpt_expand.c @@ -156,10 +156,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; } } @@ -171,10 +173,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)); + } } /** @@ -183,14 +195,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]; @@ -200,7 +214,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; } /** @@ -225,7 +239,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; @@ -271,9 +287,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; } @@ -288,7 +308,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 { @@ -296,12 +316,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; } } @@ -319,7 +339,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; @@ -363,7 +383,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; @@ -381,20 +402,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; } @@ -410,12 +433,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; } @@ -431,7 +454,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); } @@ -448,20 +471,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); @@ -486,23 +512,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; @@ -510,7 +539,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; @@ -537,8 +566,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); @@ -555,7 +586,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); @@ -570,11 +601,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)))); } } } @@ -587,13 +621,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); @@ -602,7 +636,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) { @@ -611,7 +646,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); } @@ -623,7 +659,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; @@ -631,7 +667,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); } @@ -648,32 +687,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)) { @@ -683,18 +728,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); } } } @@ -716,12 +793,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; } @@ -740,11 +820,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; } @@ -792,6 +874,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. */ @@ -806,14 +905,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; } } @@ -829,7 +930,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); @@ -845,7 +946,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); } @@ -872,7 +974,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); @@ -937,16 +1040,18 @@ 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); } - if (BKE_pbvh_type(ss->pbvh) == PBVH_FACES) { + if (ELEM(BKE_pbvh_type(ss->pbvh), PBVH_GRIDS, PBVH_FACES, PBVH_BMESH)) { sculpt_expand_geodesics_from_state_boundary(ob, expand_cache, enabled_vertices); } else { @@ -957,8 +1062,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; @@ -976,7 +1083,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; @@ -992,20 +1101,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); @@ -1131,7 +1243,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]); } } @@ -1231,12 +1345,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; @@ -1268,16 +1382,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); } } @@ -1305,11 +1423,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; @@ -1371,22 +1489,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))); } } } @@ -1397,26 +1521,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. */ @@ -1424,7 +1554,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; } @@ -1465,16 +1595,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)) { return SCULPT_active_vertex_get(ss); } - return SCULPT_EXPAND_VERTEX_NONE; + + SculptVertRef ret = {SCULPT_EXPAND_VERTEX_NONE}; + return ret; } /** @@ -1494,8 +1626,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); @@ -1514,11 +1646,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; @@ -1573,9 +1707,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++) { @@ -1588,11 +1721,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]; } } @@ -1606,8 +1740,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); @@ -1680,17 +1815,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; } @@ -1713,7 +1846,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; @@ -1899,12 +2033,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; @@ -1912,7 +2167,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) { @@ -2060,6 +2316,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); @@ -2083,13 +2342,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. */ @@ -2119,7 +2371,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 bdbdb75732a..1504da20caf 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++) { @@ -579,25 +883,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; @@ -608,7 +908,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); @@ -635,7 +937,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); } @@ -649,44 +953,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) @@ -697,11 +1020,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; @@ -851,11 +1169,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); @@ -884,17 +1197,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; + } } } @@ -961,8 +1288,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]; @@ -1001,22 +1328,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); @@ -1095,12 +1421,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) { @@ -1123,12 +1497,70 @@ static void sculpt_face_set_grow(Object *ob, } } +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) { @@ -1153,20 +1585,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) { @@ -1174,8 +1614,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; } } @@ -1190,39 +1634,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); + + 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); + } + else { + BMesh *bm = BM_mesh_create(&allocsize, + &((struct BMeshCreateParams){ + .use_toolflags = true, + })); + + BM_mesh_bm_from_me(ob, + bm, + mesh, + (&(struct BMeshFromMeshParams){ + .calc_face_normal = true, + })); + + 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_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, - bm, - ob->data, - (&(struct BMeshToMeshParams){ - .calc_object_remap = false, - })); - - BM_mesh_free(bm); + BM_mesh_free(bm); + } } static void sculpt_face_set_edit_fair_face_set(Object *ob, @@ -1230,21 +1700,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); } @@ -1257,13 +1737,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; @@ -1284,20 +1764,18 @@ static bool sculpt_face_set_edit_is_operation_valid(SculptSession *ss, const eSculptFaceSetEditMode mode, const bool modify_hidden) { - if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) { - /* Dyntopo is not supported. */ - return false; - } + SCULPT_vertex_random_access_ensure(ss); + SCULPT_face_random_access_ensure(ss); if (mode == SCULPT_FACE_SET_EDIT_DELETE_GEOMETRY) { if (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; diff --git a/source/blender/editors/sculpt_paint/sculpt_filter_color.c b/source/blender/editors/sculpt_paint/sculpt_filter_color.c index 4b49bf2cefb..dfb1f2e74dc 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 10f141e2311..4c6c156e361 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); } } @@ -323,9 +325,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 3fc1a7674f7..4e50b9fbd9b 100644 --- a/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c +++ b/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c @@ -293,7 +293,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. */ @@ -303,12 +303,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 @@ -326,7 +326,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; } } @@ -334,7 +334,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); @@ -393,13 +393,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: { @@ -411,10 +420,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); } @@ -424,7 +433,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])); @@ -486,9 +495,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)); } } @@ -512,8 +523,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]); } } @@ -534,8 +547,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]); } @@ -558,11 +573,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++; @@ -589,15 +605,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_intern.h b/source/blender/editors/sculpt_paint/sculpt_intern.h index 696c3332a2b..d1f90750445 100644 --- a/source/blender/editors/sculpt_paint/sculpt_intern.h +++ b/source/blender/editors/sculpt_paint/sculpt_intern.h @@ -30,12 +30,16 @@ #include "DNA_vec_types.h" #include "BLI_bitmap.h" +#include "BLI_compiler_compat.h" #include "BLI_gsqueue.h" #include "BLI_threads.h" +#include "BKE_attribute.h" #include "BKE_paint.h" #include "BKE_pbvh.h" +#include "bmesh.h" + struct AutomaskingCache; struct KeyBlock; struct Object; @@ -44,6 +48,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 */ @@ -51,6 +73,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 */ @@ -61,12 +84,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 */ @@ -96,22 +119,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. */ @@ -119,25 +150,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); @@ -146,7 +188,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. */ @@ -154,7 +200,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); @@ -165,9 +215,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. */ @@ -184,15 +236,38 @@ void SCULPT_fake_neighbors_free(struct Object *ob); /* Vertex Info. */ void SCULPT_boundary_info_ensure(Object *object); + +/* 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 +} 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); @@ -200,21 +275,28 @@ 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); +int SCULPT_vertex_face_set_get(SculptSession *ss, SculptVertRef index); +void SCULPT_vertex_face_set_set(SculptSession *ss, SculptVertRef index, int face_set); -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); @@ -224,24 +306,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, @@ -253,11 +333,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, @@ -299,26 +379,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, @@ -331,8 +421,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); @@ -341,43 +432,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); @@ -515,7 +609,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, @@ -545,37 +639,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); @@ -634,8 +753,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 */ @@ -672,7 +791,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 @@ -786,10 +909,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; @@ -797,6 +920,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 ****************/ @@ -830,6 +961,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 { @@ -866,7 +999,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. */ @@ -897,13 +1030,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 { @@ -977,6 +1113,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]; @@ -1066,6 +1203,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 */ @@ -1141,7 +1290,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 @@ -1302,8 +1451,19 @@ void SCULPT_cache_calc_brushdata_symm(StrokeCache *cache, const float angle); void SCULPT_cache_free(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); @@ -1370,3 +1530,186 @@ 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); + +/* +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 SCULPT_temp_customlayer_get( + SculptSession *ss, AttributeDomain domain, int proptype, char *name, 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 9b06b2ee5d5..cf34fdc1506 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)) { /* 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_pose.c b/source/blender/editors/sculpt_paint/sculpt_pose.c index 587ce346428..09517820485 100644 --- a/source/blender/editors/sculpt_paint/sculpt_pose.c +++ b/source/blender/editors/sculpt_paint/sculpt_pose.c @@ -172,10 +172,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); @@ -198,7 +198,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. */ @@ -237,7 +237,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); } @@ -383,7 +383,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; @@ -413,11 +414,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; @@ -442,15 +448,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; @@ -464,11 +473,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; @@ -482,11 +491,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) { @@ -501,11 +510,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; } @@ -514,15 +523,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; @@ -532,7 +541,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; @@ -556,8 +565,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); @@ -608,7 +615,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++; } @@ -678,12 +685,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. */ @@ -768,7 +778,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++) { @@ -797,6 +808,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); @@ -823,10 +841,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; @@ -835,11 +858,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)); @@ -854,14 +877,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); } @@ -872,7 +898,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; @@ -880,7 +908,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; @@ -893,9 +921,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++; } } @@ -904,10 +935,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++; } } 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..5ec37901369 --- /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 38165b7622f..870c45339aa 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,40 @@ 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"); + SCULPT_temp_customlayer_get(ss, ATTR_DOMAIN_POINT, CD_PROP_FLOAT3, "__dyntopo_detail_dir", &scl); + + 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 +1008,106 @@ 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,21 +1120,96 @@ 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; } @@ -350,7 +1222,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 +1235,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); + } +#else + bool have_scl = false; +#endif + if (type == PBVH_FACES && !ss->pmap) { BLI_assert_msg(0, "sculpt smooth: pmap missing"); return; @@ -374,6 +1266,23 @@ 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"); + SCULPT_temp_customlayer_get(ss, ATTR_DOMAIN_POINT, CD_PROP_FLOAT, "__smooth_bdist", bound_scl); + } + +#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 +1293,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 +1341,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 +1406,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 +1430,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 +1484,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 +1496,29 @@ 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"); + SCULPT_temp_customlayer_get(ss, ATTR_DOMAIN_POINT, CD_PROP_FLOAT3, "__dyntopo_lapsmooth", &scl); + + 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); @@ -560,3 +1529,190 @@ void SCULPT_do_surface_smooth_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, in 0, totnode, &data, SCULPT_do_surface_smooth_brush_displace_task_cb_ex, &settings); } } + +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; + } + + 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_transform.c b/source/blender/editors/sculpt_paint/sculpt_transform.c index 3c0a591e8a7..61ea43efd66 100644 --- a/source/blender/editors/sculpt_paint/sculpt_transform.c +++ b/source/blender/editors/sculpt_paint/sculpt_transform.c @@ -159,13 +159,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; diff --git a/source/blender/editors/sculpt_paint/sculpt_undo.c b/source/blender/editors/sculpt_paint/sculpt_undo.c index 501a1e53276..12f4f90a6e4 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,217 @@ 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) { + float *dummy; + 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 +2017,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 +2056,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 +2097,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 +2127,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 +2144,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 +2171,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 +2205,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 +2216,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 +2293,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 +2370,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 +2428,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 +2478,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 72b2db89cef..6f0b09614c7 100644 --- a/source/blender/makesdna/DNA_brush_enums.h +++ b/source/blender/makesdna/DNA_brush_enums.h @@ -326,6 +326,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 { @@ -405,6 +407,20 @@ typedef enum eBrushFlags2 { BRUSH_CLOTH_USE_COLLISION = (1 << 6), BRUSH_AREA_RADIUS_PRESSURE = (1 << 7), BRUSH_GRAB_SILHOUETTE = (1 << 8), + BRUSH_CURVATURE_RAKE = (1 << 9), + BRUSH_CUSTOM_AUTOSMOOTH_SPACING = (1 << 10), + BRUSH_CUSTOM_TOPOLOGY_RAKE_SPACING = (1 << 11), + BRUSH_TOPOLOGY_RAKE_IGNORE_BRUSH_FALLOFF = (1 << 12), + BRUSH_SMOOTH_USE_AREA_WEIGHT = (1 << 13), + + /*preserve face set boundaries*/ + BRUSH_SMOOTH_PRESERVE_FACE_SETS = (1 << 14), + + /*topology rake in dynamic mode*/ + BRUSH_DYNAMIC_RAKE = (1 << 15), + + /* sets face set slide to 0.0 */ + BRUSH_HARD_EDGE_MODE = (1 << 16), } eBrushFlags2; typedef enum { @@ -460,6 +476,11 @@ typedef enum eBrushSculptTool { SCULPT_TOOL_BOUNDARY = 30, SCULPT_TOOL_DISPLACEMENT_ERASER = 31, SCULPT_TOOL_DISPLACEMENT_SMEAR = 32, + SCULPT_TOOL_VCOL_BOUNDARY = 33, + SCULPT_TOOL_UV_SMOOTH = 34, + + SCULPT_TOOL_TOPOLOGY_RAKE = 35, + SCULPT_TOOL_DYNTOPO = 36 } eBrushSculptTool; /* Brush.uv_sculpt_tool */ @@ -469,6 +490,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, \ @@ -493,12 +516,8 @@ 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_DRAW_SHARP, \ SCULPT_TOOL_SLIDE_RELAX, \ SCULPT_TOOL_ELASTIC_DEFORM, \ SCULPT_TOOL_BOUNDARY, \ @@ -509,15 +528,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) @@ -608,6 +627,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 634ebdff253..42135992477 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; @@ -246,7 +259,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; @@ -275,10 +288,21 @@ typedef struct Brush { char _pad1[6]; 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; @@ -373,8 +397,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 97f14b2195d..f3177e96bb0 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 7800e7f9efe..70146d02108 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; @@ -987,6 +989,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; @@ -998,8 +1001,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 { @@ -1240,7 +1247,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; @@ -1264,7 +1272,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; @@ -1847,12 +1855,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 { @@ -2242,6 +2250,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; /** #ImagePaintSettings.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 25caa411979..e3454af8f2e 100644 --- a/source/blender/makesrna/intern/rna_brush.c +++ b/source/blender/makesrna/intern/rna_brush.c @@ -135,10 +135,12 @@ 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_SMEAR, "SMEAR", ICON_BRUSH_SCULPT_DRAW, "Smear", ""}, + {SCULPT_TOOL_PAINT, "PAINT", ICON_BRUSH_PAINT, "Paint", ""}, + {SCULPT_TOOL_SMEAR, "SMEAR", ICON_BRUSH_SCULPT_SMEAR, "Smear", ""}, {SCULPT_TOOL_DRAW_FACE_SETS, "DRAW_FACE_SETS", ICON_BRUSH_MASK, "Draw Face Sets", ""}, - {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 */ @@ -341,6 +343,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 @@ -370,6 +408,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; @@ -671,6 +715,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 */ @@ -1127,6 +1176,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; @@ -1150,6 +1317,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"); @@ -2473,6 +2641,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); @@ -2868,13 +3052,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 " @@ -2882,6 +3092,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); @@ -3006,6 +3273,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, @@ -3049,12 +3331,57 @@ static void rna_def_brush(BlenderRNA *brna) "Apply the maximum grab strength to the active vertex instead of the cursor location"); RNA_def_property_update(prop, 0, "rna_Brush_update"); + prop = RNA_def_property(srna, "use_weighted_smooth", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag2", BRUSH_SMOOTH_USE_AREA_WEIGHT); + RNA_def_property_ui_text(prop, "Weight By Area", "Weight by face area to get a smoother result"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); + + prop = RNA_def_property(srna, "preserve_faceset_boundary", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag2", BRUSH_SMOOTH_PRESERVE_FACE_SETS); + RNA_def_property_ui_text( + prop, "Preserve Face Sets", "Preserve face set boundaries when smoothing"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); + + prop = RNA_def_property(srna, "hard_edge_mode", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag2", BRUSH_HARD_EDGE_MODE); + RNA_def_property_ui_text( + prop, "Hard Edge Mode", "Hard edge mode; treat all face set boundaries as hard edges"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); + prop = RNA_def_property(srna, "use_grab_silhouette", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag2", BRUSH_GRAB_SILHOUETTE); RNA_def_property_ui_text( prop, "Grab Silhouette", "Grabs trying to automask the silhouette of the object"); RNA_def_property_update(prop, 0, "rna_Brush_update"); + prop = RNA_def_property(srna, "use_curvature_rake", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag2", BRUSH_CURVATURE_RAKE); + RNA_def_property_ui_text( + prop, "Curvature Rake", "Topology rake follows curvature instead of brush direction"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); + + prop = RNA_def_property(srna, "ignore_falloff_for_topology_rake", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag2", BRUSH_TOPOLOGY_RAKE_IGNORE_BRUSH_FALLOFF); + RNA_def_property_ui_text( + prop, "Ignore Brush Falloff", "Ignore brush falloff settings for topology rake"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); + + prop = RNA_def_property(srna, "use_custom_auto_smooth_spacing", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag2", BRUSH_CUSTOM_AUTOSMOOTH_SPACING); + RNA_def_property_ui_text( + prop, + "Use Custom Autosmooth Spacing", + "Use custom spacing for autosmooth (must be larger then brush spacing)"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); + + prop = RNA_def_property(srna, "use_custom_topology_rake_spacing", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag2", BRUSH_CUSTOM_TOPOLOGY_RAKE_SPACING); + RNA_def_property_ui_text( + prop, + "Use Custom Rake Spacing", + "Use custom spacing for topology rake (must be larger then brush spacing)"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); + prop = RNA_def_property(srna, "use_paint_antialiasing", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "sampling_flag", BRUSH_PAINT_ANTIALIASING); RNA_def_property_ui_text(prop, "Anti-Aliasing", "Smooths the edges of the strokes"); @@ -3288,6 +3615,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); @@ -3483,6 +3816,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", ""); } /** @@ -3562,6 +3901,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 fbc578acb8e..944efbc40e8 100644 --- a/source/blender/makesrna/intern/rna_mesh.c +++ b/source/blender/makesrna/intern/rna_mesh.c @@ -3337,6 +3337,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 1da08448c5b..2c1f509d9aa 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"); @@ -784,6 +841,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); @@ -799,6 +862,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); @@ -818,6 +896,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, @@ -876,6 +979,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, ¶ms); + BM_mesh_bm_from_me(object, bm, mesh_operand_ob, ¶ms); 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, ¶ms); + BM_mesh_bm_from_me(object, bm, mesh, ¶ms); 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, ¶ms); + BM_mesh_bm_to_me(nullptr, nullptr, bm, mesh, ¶ms); 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, ¶ms); + BM_mesh_bm_to_me(nullptr, nullptr, bm, mesh, ¶ms); + 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, ¶ms); + BM_mesh_bm_to_me(bmain, NULL, bm, me, ¶ms); /* we could have the user do this but if they forget blender can easy crash * since the references arrays for the objects derived meshes are now invalid */ @@ -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 |