diff options
120 files changed, 10392 insertions, 1375 deletions
diff --git a/.clang-format b/.clang-format index 8a992fea3a9..780b718d3aa 100644 --- a/.clang-format +++ b/.clang-format @@ -262,6 +262,7 @@ ForEachMacros: - SET_SLOT_PROBING_BEGIN - MAP_SLOT_PROBING_BEGIN - VECTOR_SET_SLOT_PROBING_BEGIN + - TGSET_ITER # Use once we bump the minimum version to version 8. # # Without this string literals that in-line 'STRINGIFY' behave strangely (a bug?). diff --git a/intern/cycles/kernel/kernel_compat_cuda.h b/intern/cycles/kernel/kernel_compat_cuda.h index ea3b78b7cef..da74d6aafac 100644 --- a/intern/cycles/kernel/kernel_compat_cuda.h +++ b/intern/cycles/kernel/kernel_compat_cuda.h @@ -41,6 +41,18 @@ typedef unsigned long long uint64_t; typedef unsigned short half; typedef unsigned long long CUtexObject; +//XXX fix compiler error on windows +#ifdef __CUDA_ARCH__ +static __device__ float myfloor(float f) { + float f2 = (float)((int)f); + f2 += (float)(f < 0.0f); + + return f2; +} + +#define floor myfloor +#endif + #ifdef CYCLES_CUBIN_CC # define FLT_MIN 1.175494350822287507969e-38f # define FLT_MAX 340282346638528859811704183484516925440.0f diff --git a/intern/ghost/intern/GHOST_WindowWin32.cpp b/intern/ghost/intern/GHOST_WindowWin32.cpp index de4e77ffc24..eaa708f52b9 100644 --- a/intern/ghost/intern/GHOST_WindowWin32.cpp +++ b/intern/ghost/intern/GHOST_WindowWin32.cpp @@ -58,6 +58,38 @@ extern "C" { __declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001; } +#include <thread> +static void thread_cb( + GHOST_WIN32_WTInfo info, unsigned int a, unsigned int b, void *dest, int *status, int *ret) +{ + *ret = info(a, b, dest); + *status = 1; +} + +static unsigned int call_wt_info(GHOST_WIN32_WTInfo info, + unsigned int a, + unsigned int b, + void *dest) +{ + static int status = 0; + static int ret = 0; + + std::thread thread(thread_cb, info, a, b, dest, &status, &ret); + + std::this_thread::sleep_for(std::chrono::milliseconds(750)); + + if (!status) { + fprintf(stderr, "failed to initialize wintab properly\n"); + thread.detach(); + return 0; + } + else { + thread.join(); + } + + return ret; +} + GHOST_WindowWin32::GHOST_WindowWin32(GHOST_SystemWin32 *system, const char *title, GHOST_TInt32 left, 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.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/scripts/addons b/release/scripts/addons -Subproject 24e756c0da89bdbf88dc22163ae3b7ef4f1fbb7 +Subproject c8752443311b133f8783a6b9b2152eb7c92d06a diff --git a/release/scripts/startup/bl_ui/properties_paint_common.py b/release/scripts/startup/bl_ui/properties_paint_common.py index 9620666858f..b248a70208f 100644 --- a/release/scripts/startup/bl_ui/properties_paint_common.py +++ b/release/scripts/startup/bl_ui/properties_paint_common.py @@ -566,7 +566,10 @@ def brush_settings(layout, context, brush, popover=False): slider=True, ) - # topology_rake_factor + + 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 @@ -631,6 +634,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") @@ -917,6 +924,9 @@ def brush_settings_advanced(layout, context, brush, popover=False): # topology automasking col.prop(brush, "use_automasking_topology", text="Topology") + col.prop(brush, "concave_mask_factor", text="Cavity Factor"); + col.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_view3d_toolbar.py b/release/scripts/startup/bl_ui/space_view3d_toolbar.py index 83ab5bf3100..10010ab0b15 100644 --- a/release/scripts/startup/bl_ui/space_view3d_toolbar.py +++ b/release/scripts/startup/bl_ui/space_view3d_toolbar.py @@ -798,6 +798,7 @@ class VIEW3D_PT_sculpt_dyntopo(Panel, View3DPaintPanel): col.operator("sculpt.detail_flood_fill") col.prop(sculpt, "use_smooth_shading") + col.prop(sculpt, "use_flat_vcol_shading") class VIEW3D_PT_sculpt_voxel_remesh(Panel, View3DPaintPanel): diff --git a/source/blender/blenkernel/BKE_collection.h b/source/blender/blenkernel/BKE_collection.h index d15aebfe03d..6528c499146 100644 --- a/source/blender/blenkernel/BKE_collection.h +++ b/source/blender/blenkernel/BKE_collection.h @@ -26,6 +26,8 @@ #include "BLI_sys_types.h" #include "DNA_listBase.h" +#include "DNA_userdef_types.h" +#include "BKE_lib_id.h" #ifdef __cplusplus extern "C" { @@ -71,8 +73,8 @@ bool BKE_collection_delete(struct Main *bmain, struct Collection *collection, bo struct Collection *BKE_collection_duplicate(struct Main *bmain, struct Collection *parent, struct Collection *collection, - const uint duplicate_flags, - const uint duplicate_options); + const eDupli_ID_Flags duplicate_flags, + const eLibIDDuplicateFlags duplicate_options); /* Master Collection for Scene */ diff --git a/source/blender/blenkernel/BKE_customdata.h b/source/blender/blenkernel/BKE_customdata.h index ed319948160..b31f004628d 100644 --- a/source/blender/blenkernel/BKE_customdata.h +++ b/source/blender/blenkernel/BKE_customdata.h @@ -104,6 +104,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. */ @@ -140,6 +142,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, diff --git a/source/blender/blenkernel/BKE_lib_id.h b/source/blender/blenkernel/BKE_lib_id.h index 5a8d36b94ec..f6dd3e4fdfc 100644 --- a/source/blender/blenkernel/BKE_lib_id.h +++ b/source/blender/blenkernel/BKE_lib_id.h @@ -46,6 +46,7 @@ */ #include "BLI_compiler_attrs.h" +#include "DNA_userdef_types.h" #ifdef __cplusplus extern "C" { @@ -243,7 +244,7 @@ struct ID *BKE_id_copy_ex(struct Main *bmain, const int flag); struct ID *BKE_id_copy_for_duplicate(struct Main *bmain, struct ID *id, - const uint duplicate_flags); + const eDupli_ID_Flags duplicate_flags); void BKE_lib_id_swap(struct Main *bmain, struct ID *id_a, struct ID *id_b); void BKE_lib_id_swap_full(struct Main *bmain, struct ID *id_a, struct ID *id_b); diff --git a/source/blender/blenkernel/BKE_mesh.h b/source/blender/blenkernel/BKE_mesh.h index e39caac7c36..8bff9b6ba78 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, 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 fce25abba7f..391d0104be5 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 12c40e891c9..d57469e11c0 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" { @@ -153,8 +155,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 228b52123f3..3e5bf2570a6 100644 --- a/source/blender/blenkernel/BKE_paint.h +++ b/source/blender/blenkernel/BKE_paint.h @@ -27,6 +27,7 @@ #include "BLI_utildefines.h" #include "DNA_brush_enums.h" #include "DNA_object_enums.h" +#include "BKE_pbvh.h" #ifdef __cplusplus extern "C" { @@ -360,7 +361,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 +373,15 @@ typedef struct SculptBoundaryEditInfo { /* Edge for drawing the boundary preview in the cursor. */ typedef struct SculptBoundaryPreviewEdge { - int v1; - int v2; + SculptVertRef v1; + SculptVertRef v2; } SculptBoundaryPreviewEdge; typedef struct SculptBoundary { /* Vertex indices of the active boundary. */ - int *vertices; + SculptVertRef *vertices; + int *vertex_indices; + int vertices_capacity; int num_vertices; @@ -395,12 +399,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 @@ -440,7 +444,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; @@ -493,6 +497,9 @@ typedef struct SculptSession { struct BMesh *bm; int cd_vert_node_offset; int cd_face_node_offset; + int cd_vcol_offset; + int cd_faceset_offset; + bool bm_smooth_shading; /* Undo/redo log for dynamic topology sculpting */ struct BMLog *bm_log; @@ -520,9 +527,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. @@ -544,9 +551,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 */ diff --git a/source/blender/blenkernel/BKE_pbvh.h b/source/blender/blenkernel/BKE_pbvh.h index 0fa44067b16..b1d2156f1e4 100644 --- a/source/blender/blenkernel/BKE_pbvh.h +++ b/source/blender/blenkernel/BKE_pbvh.h @@ -22,20 +22,42 @@ */ #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> #ifdef __cplusplus extern "C" { #endif +typedef struct { + int64_t i; +} SculptVertRef; + +typedef SculptVertRef SculptFaceRef; + +BLI_INLINE SculptVertRef BKE_pbvh_make_vref(intptr_t i) +{ + SculptVertRef ret = {i}; + return ret; +} + +BLI_INLINE SculptFaceRef BKE_pbvh_make_fref(intptr_t i) +{ + SculptFaceRef ret = {i}; + return ret; +} + struct BMLog; struct BMesh; +struct BMVert; struct CCGElem; struct CCGKey; struct CustomData; +struct TableGSet; struct DMFlagMat; struct GPU_PBVH_Buffers; struct IsectRayPrecalc; @@ -52,12 +74,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 +174,7 @@ typedef enum { PBVH_UpdateTopology = 1 << 13, PBVH_UpdateColor = 1 << 14, + PBVH_Delete = 1 << 15 } PBVHNodeFlags; typedef struct PBVHFrustumPlanes { @@ -98,6 +195,8 @@ 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); + /* Building */ PBVH *BKE_pbvh_new(void); @@ -124,9 +223,24 @@ void BKE_pbvh_build_bmesh(PBVH *pbvh, 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_origco_offset, + const int cd_origno_offset, + const int cd_origvcol_offset); +void BKE_pbvh_update_offsets(PBVH *pbvh, + const int cd_vert_node_offset, + const int cd_face_node_offset, + const int cd_origco_offset, + const int cd_origno_offset, + const int cd_origvcol_offset); void BKE_pbvh_free(PBVH *pbvh); +/** 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); +void BKE_pbvh_update_origcolor_bmesh(PBVH *pbvh, PBVHNode *node); +void BKE_pbvh_update_origco_bmesh(PBVH *pbvh, PBVHNode *node); + /* Hierarchical Search in the BVH, two methods: * - for each hit calling a callback * - gather nodes in an array (easy to multithread) */ @@ -160,8 +274,8 @@ 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, + SculptVertRef *active_vertex_index, + SculptFaceRef *active_face_grid_index, float *face_normal); bool BKE_pbvh_bmesh_node_raycast_detail(PBVHNode *node, @@ -250,8 +364,22 @@ bool BKE_pbvh_bmesh_update_topology(PBVH *pbvh, const float view_normal[3], float radius, const bool use_frontface, - const bool use_projected); - + const bool use_projected, + int symaxis, + bool updatePBVH); + +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); /* Node Access */ void BKE_pbvh_node_mark_update(PBVHNode *node); @@ -292,10 +420,10 @@ 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_node_save_ortri(struct BMesh *bm, PBVHNode *node); void BKE_pbvh_bmesh_after_stroke(PBVH *pbvh); /* Update Bounding Box/Redraw and clear flags */ @@ -343,6 +471,7 @@ typedef struct PBVHVertexIter { int gy; int i; int index; + SculptVertRef vertex; bool respect_hide; /* grid */ @@ -362,10 +491,13 @@ 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_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 */ @@ -388,7 +520,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 +539,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 +560,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,21 +569,40 @@ 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); \ } @@ -460,6 +612,10 @@ 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); + 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); @@ -490,6 +646,11 @@ 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); +void BKE_pbvh_set_flat_vcol_shading(PBVH *pbvh, bool value); + +#define DYNTOPO_CD_INTERP + #ifdef __cplusplus } #endif diff --git a/source/blender/blenkernel/intern/anim_data.c b/source/blender/blenkernel/intern/anim_data.c index 633d6202222..521ebbfaf97 100644 --- a/source/blender/blenkernel/intern/anim_data.c +++ b/source/blender/blenkernel/intern/anim_data.c @@ -423,7 +423,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 13ba1957a32..3b5b16feea8 100644 --- a/source/blender/blenkernel/intern/brush.c +++ b/source/blender/blenkernel/intern/brush.c @@ -1839,6 +1839,13 @@ void BKE_brush_sculpt_reset(Brush *br) br->flag &= ~BRUSH_SPACE_ATTEN; br->curve_preset = BRUSH_CURVE_SPHERE; 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; case SCULPT_TOOL_DISPLACEMENT_SMEAR: br->alpha = 1.0f; br->spacing = 5; @@ -1906,6 +1913,7 @@ void BKE_brush_sculpt_reset(Brush *br) br->sub_col[2] = 0.005f; break; + case SCULPT_TOOL_VCOL_BOUNDARY: case SCULPT_TOOL_SIMPLIFY: case SCULPT_TOOL_PAINT: case SCULPT_TOOL_MASK: diff --git a/source/blender/blenkernel/intern/customdata.c b/source/blender/blenkernel/intern/customdata.c index d8d9675b42b..571010e2017 100644 --- a/source/blender/blenkernel/intern/customdata.c +++ b/source/blender/blenkernel/intern/customdata.c @@ -72,6 +72,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) @@ -2095,6 +2121,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, diff --git a/source/blender/blenkernel/intern/mesh.c b/source/blender/blenkernel/intern/mesh.c index e7a16463d87..5d5d2222f63 100644 --- a/source/blender/blenkernel/intern/mesh.c +++ b/source/blender/blenkernel/intern/mesh.c @@ -1004,14 +1004,15 @@ Mesh *BKE_mesh_copy_for_eval(struct 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(ob, bm, me, convert_params); return bm; } @@ -1021,7 +1022,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, @@ -1037,7 +1039,7 @@ 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); + BM_mesh_bm_to_me(NULL, NULL, bm, mesh, params); BKE_mesh_copy_settings(mesh, me_settings); return mesh; } diff --git a/source/blender/blenkernel/intern/mesh_mirror.c b/source/blender/blenkernel/intern/mesh_mirror.c index a22b52d68d5..d796254f306 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){ @@ -194,7 +198,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_remesh_voxel.c b/source/blender/blenkernel/intern/mesh_remesh_voxel.c index 0a5aa360553..3b3a6af9db9 100644 --- a/source/blender/blenkernel/intern/mesh_remesh_voxel.c +++ b/source/blender/blenkernel/intern/mesh_remesh_voxel.c @@ -443,7 +443,7 @@ struct Mesh *BKE_mesh_remesh_voxel_fix_poles(struct 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, diff --git a/source/blender/blenkernel/intern/multires.c b/source/blender/blenkernel/intern/multires.c index 45ac20ef154..30a80576921 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> @@ -852,17 +863,448 @@ 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; +} + +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; + // int cd_mask_off = tdata->cd_mask_off; + BMesh *bm = tdata->bm; + MultiResSpace op = tdata->bmop; + BMFace *f = bm->ftable[pidx]; + int gridSize = tdata->gridSize; + + int S, x, y; + + BMLoop *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); + + // bool has_grid_mask = tdata->has_grid_mask; + + l = f->l_first; + S = 0; + do { + // GridPaintMask *gpm = has_grid_mask ? BM_ELEM_CD_GET_VOID_P(l, cd_mask_off) : NULL; + MDisps *mdisp = BM_ELEM_CD_GET_VOID_P(l, cd_mdisps_off); + float(*dispgrid)[3] = NULL; + + dispgrid = mdisp->disps; + + float maxlen = len_v3v3(l->v->co, cent) * 15.0f; + maxlen = MAX2(maxlen, 0.00001f); + + 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); + if (len > maxlen) { + mul_v3_fl(data, maxlen/len); + } else if (isnan(len)) { + zero_v3(data); + } + break; + } + } + } + + S++; + l = l->next; + } while (l != f->l_first); +} + +/* XXX WARNING: subsurf elements from dm and oldGridData *must* be of the same format (size), + * because this code uses CCGKey's info from dm to access oldGridData's normals + * (through the call to grid_tangent_matrix())! */ +void BKE_multires_bmesh_space_set(Object *ob, BMesh *bm, int mode) +{ + if (!bm->totface || !CustomData_has_layer(&bm->ldata, CD_MDISPS)) { + return; + } + + 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; + } + + bm->multiresSpace = mode; + + 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; + + // CustomData_MeshMasks extra = {0}; + BM_mesh_bm_to_me_for_eval(bm, me, &extra); + SubdivSettings settings2; + + // ensure we control the level + MultiresModifierData mmdcpy = *mmd; + mmdcpy.lvl = mmdcpy.sculptlvl = mmdcpy.renderlvl = mmdcpy.totlvl; + + 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); + + 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, dGridSize, dSkip; + int totpoly = bm->totface; + + // paranoia recalc of indices/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]; + + /* when adding new faces in edit mode, need to allocate disps */ + int cd_disp_off = CustomData_get_offset(&bm->ldata, CD_MDISPS); + + BMFace *f; + BMIter iter; + i = 0; + 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); + if (!mdisp->disps) { + multires_reallocate_mdisps(1, mdisp, mmd->totlvl); + } + + l->head.index = i; + + if (f->len != 4) { + i++; + } + } + + if (f->len == 4) { + i++; + } + } + + TaskParallelSettings settings; + BLI_parallel_range_settings_defaults(&settings); + settings.min_iter_per_thread = CCG_TASK_LIMIT; + + bool has_grid_mask = CustomData_has_layer(&me->ldata, CD_GRID_PAINT_MASK); + + MultiresThreadedData data = { + .bmop = mode, + .sd = sd, + .lvl = mmd->totlvl, + .bm = bm, + .cd_mdisps_off = cd_disp_off, + .cd_mask_off = has_grid_mask ? CustomData_get_offset(&bm->ldata, CD_GRID_PAINT_MASK) : -1, + .has_grid_mask = has_grid_mask, + .gridSize = gridSize, + }; + + BLI_task_parallel_range(0, totpoly, &data, multires_bmesh_space_set_cb, &settings); + + // MDisps = CustomData_get + // if (mode == MULTIRES_SPACE_TANGENT) { + // ccgSubSurf_stitchFaces(ccgdm->ss, 0, NULL, 0); + // ccgSubSurf_updateNormals(ccgdm->ss, NULL, 0); + //} + + BKE_mesh_free(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)) @@ -1224,7 +1666,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); } } @@ -1340,7 +1782,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); @@ -1349,7 +1791,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_smooth.c b/source/blender/blenkernel/intern/multires_reshape_smooth.c index f10aae18142..d7ab6773991 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_unsubdivide.c b/source/blender/blenkernel/intern/multires_unsubdivide.c index 02b9bb852d6..7440700a3de 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 08c5beedbf3..a374c3252b9 100644 --- a/source/blender/blenkernel/intern/paint.c +++ b/source/blender/blenkernel/intern/paint.c @@ -1386,6 +1386,7 @@ static void sculptsession_bm_to_me_update_data_only(Object *ob, bool reorder) BM_log_mesh_elems_reorder(ss->bm, ss->bm_log); } BM_mesh_bm_to_me(NULL, + NULL, ss->bm, ob->data, (&(struct BMeshToMeshParams){ @@ -1795,6 +1796,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) @@ -1811,17 +1813,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); + 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); } @@ -2101,9 +2105,13 @@ static PBVH *build_pbvh_for_dynamic_topology(Object *ob) 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_origco_offset, + ob->sculpt->cd_origno_offset, + ob->sculpt->cd_origvcol_offset); pbvh_show_mask_set(pbvh, ob->sculpt->show_mask); pbvh_show_face_sets_set(pbvh, false); + return pbvh; } @@ -2168,12 +2176,17 @@ static PBVH *build_pbvh_from_ccg(Object *ob, SubdivCCG *subdiv_ccg, bool respect return pbvh; } +// XXX hack +extern SCULPT_dynamic_topology_sync_layers(Object *ob, Mesh *me); + 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))) { @@ -2183,6 +2196,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) { @@ -2193,6 +2208,11 @@ 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; } @@ -2213,6 +2233,11 @@ PBVH *BKE_sculpt_object_pbvh_ensure(Depsgraph *depsgraph, Object *ob) } ob->sculpt->pbvh = pbvh; + + if (pbvh) { + SCULPT_update_flat_vcol_shading(ob, scene); + } + return pbvh; } diff --git a/source/blender/blenkernel/intern/pbvh.c b/source/blender/blenkernel/intern/pbvh.c index 8a98780d918..9cd1c87a518 100644 --- a/source/blender/blenkernel/intern/pbvh.c +++ b/source/blender/blenkernel/intern/pbvh.c @@ -49,7 +49,7 @@ #include <limits.h> -#define LEAF_LIMIT 10000 +#define LEAF_LIMIT 4000 //#define PERFCNTRS @@ -693,14 +693,18 @@ 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); } + +#ifdef PROXY_ADVANCED + BKE_pbvh_free_proxyarray(pbvh, node); +#endif } } @@ -1015,6 +1019,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, @@ -1296,6 +1301,7 @@ static void pbvh_update_draw_buffer_cb(void *__restrict userdata, } if (node->flag & PBVH_UpdateDrawBuffers) { + const int update_flags = pbvh_get_buffers_update_flags(pbvh); switch (pbvh->type) { case PBVH_GRIDS: @@ -1328,12 +1334,33 @@ static void pbvh_update_draw_buffer_cb(void *__restrict userdata, node->bm_faces, node->bm_unique_verts, node->bm_other_verts, - update_flags); + update_flags, + pbvh->cd_vert_node_offset, + pbvh->face_sets_color_seed, + pbvh->face_sets_color_default, + data->flat_vcol_shading); 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; +} + static void pbvh_update_draw_buffers(PBVH *pbvh, PBVHNode **nodes, int totnode, int update_flag) { if ((update_flag & PBVH_RebuildDrawBuffers) || ELEM(pbvh->type, PBVH_GRIDS, PBVH_BMESH)) { @@ -1356,11 +1383,28 @@ static void pbvh_update_draw_buffers(PBVH *pbvh, PBVHNode **nodes, int totnode, } } + 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); + /* 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); @@ -1410,6 +1454,49 @@ 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_origvcol_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_ALL) + { + float *c1 = BM_ELEM_CD_GET_VOID_P(vd.bm_vert, pbvh->cd_origvcol_offset); + float *c2 = BM_ELEM_CD_GET_VOID_P(vd.bm_vert, cd_vcol_offset); + + copy_v4_v4(c1, c2); + } + BKE_pbvh_vertex_iter_end; +} + +void BKE_pbvh_update_origco_bmesh(PBVH *pbvh, PBVHNode *node) +{ + PBVHVertexIter vd; + + if (!pbvh->bm || pbvh->cd_origco_offset < 0 || pbvh->cd_origno_offset < 0) { + return; + } + + BKE_pbvh_vertex_iter_begin(pbvh, node, vd, PBVH_ITER_ALL) + { + float *no = BM_ELEM_CD_GET_VOID_P(vd.bm_vert, pbvh->cd_origno_offset); + float *co = BM_ELEM_CD_GET_VOID_P(vd.bm_vert, pbvh->cd_origco_offset); + + copy_v3_v3(co, vd.bm_vert->co); + copy_v3_v3(no, vd.bm_vert->no); + } + BKE_pbvh_vertex_iter_end; +} + void BKE_pbvh_update_bounds(PBVH *pbvh, int flag) { if (!pbvh->nodes) { @@ -1512,28 +1599,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); } @@ -1857,9 +1944,22 @@ 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; + } + + 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; @@ -2139,8 +2239,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; @@ -2189,8 +2289,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; } } } @@ -2207,8 +2307,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; @@ -2274,13 +2374,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; } } } @@ -2302,8 +2403,8 @@ 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, + SculptVertRef *active_vertex_index, + SculptFaceRef *active_face_grid_index, float *face_normal) { bool hit = false; @@ -2338,7 +2439,7 @@ bool BKE_pbvh_node_raycast(PBVH *pbvh, face_normal); break; case PBVH_BMESH: - BM_mesh_elem_index_ensure(pbvh->bm, BM_VERT); + // BM_mesh_elem_index_ensure(pbvh->bm, BM_VERT); hit = pbvh_bmesh_node_raycast(node, ray_start, ray_normal, @@ -2346,6 +2447,7 @@ bool BKE_pbvh_node_raycast(PBVH *pbvh, depth, use_origco, active_vertex_index, + active_face_grid_index, face_normal); break; } @@ -2913,9 +3015,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; } @@ -2941,8 +3057,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) { @@ -2970,9 +3089,15 @@ 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); + 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; + + // 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); } @@ -3002,6 +3127,15 @@ bool pbvh_has_mask(PBVH *pbvh) return false; } +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); +} bool pbvh_has_face_sets(PBVH *pbvh) { switch (pbvh->type) { @@ -3010,7 +3144,7 @@ bool pbvh_has_face_sets(PBVH *pbvh) 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; @@ -3070,3 +3204,462 @@ 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); +} + +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); +} + +#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 diff --git a/source/blender/blenkernel/intern/pbvh_bmesh.c b/source/blender/blenkernel/intern/pbvh_bmesh.c index 09e4ad93baa..27549dcb1ad 100644 --- a/source/blender/blenkernel/intern/pbvh_bmesh.c +++ b/source/blender/blenkernel/intern/pbvh_bmesh.c @@ -18,14 +18,38 @@ * \ingroup bli */ + /* + TODO: + + Convergence improvements: + 1. DONE: Limit number of edges processed per run. + 2. Scale split steps by ratio of long to short edges to + prevent runaway tesselation. + 3. Detect and dissolve three and four valence vertices that are surrounded by + all tris. + 4. Use different (coarser) brush spacing for applying dyntopo + + Drawing improvements: + 4. Build and cache vertex index buffers, to reduce GPU bandwidth + + Topology rake: + 5. Enable new curvature topology rake code and add to UI. + 6. 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_task.h" #include "BLI_utildefines.h" +#include "PIL_time.h" #include "BKE_DerivedMesh.h" #include "BKE_ccg.h" @@ -36,17 +60,49 @@ #include "bmesh.h" #include "pbvh_intern.h" +//#define DYNTOPO_TIME_LIMIT 0.015 +#define DYNTOPO_RUN_INTERVAL 0.02 +#define DYNTOPO_MAX_ITER 1024 + +#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 + #ifdef USE_EDGEQUEUE_EVEN_SUBDIV # include "BKE_global.h" #endif +#ifndef DEBUG +# define DEBUG_DEFINED +# define DEBUG +#endif + +#ifdef WIN32 +# include "crtdbg.h" +#endif + +static void check_heap() +{ +#ifdef WIN32 + if (!_CrtCheckMemory()) { + printf("Memory corruption!"); + _CrtDbgBreak(); + } +# ifdef DEBUG_DEFINED +# undef DEBUG_DEFINED +# undef DEBUG +# endif +#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). @@ -201,21 +257,20 @@ 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"); + 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 +280,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 +297,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 +308,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; + + if (add_orco) { + BKE_pbvh_bmesh_node_save_ortri(pbvh->bm, 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,63 +363,74 @@ 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 = c2->bm_unique_verts = NULL; + 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 */ + BMVert *v; + /* Mark this node's unique verts as unclaimed */ if (n->bm_unique_verts) { - GSET_ITER (gs_iter, n->bm_unique_verts) { - BMVert *v = BLI_gsetIterator_getKey(&gs_iter); + TGSET_ITER (v, n->bm_unique_verts) { BM_ELEM_CD_SET_INT(v, cd_vert_node_offset, DYNTOPO_NODE_NONE); } - 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) { @@ -368,8 +449,8 @@ static void pbvh_bmesh_node_split(PBVH *pbvh, const BBC *bbc_array, int node_ind 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]; @@ -381,11 +462,19 @@ static void pbvh_bmesh_node_split(PBVH *pbvh, const BBC *bbc_array, int node_ind n->orig_vb = n->vb; } +static void pbvh_bmesh_copy_facedata(BMesh *bm, BMFace *dest, BMFace *src) +{ + dest->head.hflag = src->head.hflag; + dest->mat_nr = src->mat_nr; + CustomData_bmesh_copy_data(&bm->pdata, &bm->pdata, src->head.data, &dest->head.data); +} + /* Recursively split the node if it exceeds the leaf_limit */ static 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); + if (bm_faces_size <= pbvh->leaf_limit) { /* Node limit not exceeded */ return false; @@ -394,10 +483,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,10 +510,12 @@ 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, pbvh->nodes[node_index].bm_ortri != NULL, 0); MEM_freeN(bbc_array); @@ -477,12 +578,18 @@ BLI_INLINE int pbvh_bmesh_node_index_from_face(PBVH *pbvh, const BMFace *key) BLI_INLINE PBVHNode *pbvh_bmesh_node_from_vert(PBVH *pbvh, const BMVert *key) { - return &pbvh->nodes[pbvh_bmesh_node_index_from_vert(pbvh, 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) { - return &pbvh->nodes[pbvh_bmesh_node_index_from_face(pbvh, 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)]; } static BMVert *pbvh_bmesh_vert_create(PBVH *pbvh, @@ -502,13 +609,14 @@ static BMVert *pbvh_bmesh_vert_create(PBVH *pbvh, /* This value is logged below */ copy_v3_v3(v->no, no); - BLI_gset_insert(node->bm_unique_verts, v); + 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; /* 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; } @@ -527,7 +635,7 @@ static BMFace *pbvh_bmesh_face_create( BMFace *f = BM_face_create(pbvh->bm, v_tri, e_tri, 3, f_example, BM_CREATE_NOP); f->head.hflag = f_example->head.hflag; - BLI_gset_insert(node->bm_faces, f); + 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 */ @@ -606,18 +714,21 @@ static void pbvh_bmesh_vert_ownership_transfer(PBVH *pbvh, PBVHNode *new_owner, { PBVHNode *current_owner = pbvh_bmesh_node_from_vert(pbvh, v); /* mark node for update */ - current_owner->flag |= PBVH_UpdateDrawBuffers | PBVH_UpdateBB; - BLI_assert(current_owner != new_owner); + if (current_owner) { + current_owner->flag |= PBVH_UpdateDrawBuffers | PBVH_UpdateBB; + + BLI_assert(current_owner != new_owner); - /* Remove current ownership */ - BLI_gset_remove(current_owner->bm_unique_verts, v, NULL); + /* 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_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)); + BLI_table_gset_insert(new_owner->bm_unique_verts, v); + BLI_table_gset_remove(new_owner->bm_other_verts, v, NULL); + BLI_assert(!BLI_table_gset_haskey(new_owner->bm_other_verts, v)); /* mark node for update */ new_owner->flag |= PBVH_UpdateDrawBuffers | PBVH_UpdateBB; @@ -629,7 +740,7 @@ static void pbvh_bmesh_vert_remove(PBVH *pbvh, BMVert *v) int f_node_index_prev = DYNTOPO_NODE_NONE; PBVHNode *v_node = pbvh_bmesh_node_from_vert(pbvh, v); - BLI_gset_remove(v_node->bm_unique_verts, v, NULL); + BLI_table_gset_remove(v_node->bm_unique_verts, v, NULL); BM_ELEM_CD_SET_INT(v, pbvh->cd_vert_node_offset, DYNTOPO_NODE_NONE); /* Have to check each neighboring face's node */ @@ -637,8 +748,12 @@ static void pbvh_bmesh_vert_remove(PBVH *pbvh, BMVert *v) 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_gset_remove calls */ + * 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; @@ -646,10 +761,10 @@ static void pbvh_bmesh_vert_remove(PBVH *pbvh, BMVert *v) f_node->flag |= PBVH_UpdateDrawBuffers | PBVH_UpdateBB; /* Remove current ownership */ - BLI_gset_remove(f_node->bm_other_verts, v, NULL); + BLI_table_gset_remove(f_node->bm_other_verts, v, NULL); - BLI_assert(!BLI_gset_haskey(f_node->bm_unique_verts, v)); - BLI_assert(!BLI_gset_haskey(f_node->bm_other_verts, v)); + BLI_assert(!BLI_table_gset_haskey(f_node->bm_unique_verts, v)); + BLI_assert(!BLI_table_gset_haskey(f_node->bm_other_verts, v)); } } BM_FACES_OF_VERT_ITER_END; @@ -659,6 +774,11 @@ static void pbvh_bmesh_face_remove(PBVH *pbvh, BMFace *f) { PBVHNode *f_node = pbvh_bmesh_node_from_face(pbvh, f); + if (!f_node) { + printf("pbvh corruption\n"); + fflush(stdout); + return; + } /* Check if any of this face's vertices need to be removed * from the node */ BMLoop *l_first = BM_FACE_FIRST_LOOP(f); @@ -666,7 +786,7 @@ static void pbvh_bmesh_face_remove(PBVH *pbvh, BMFace *f) 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)) { + if (BLI_table_gset_haskey(f_node->bm_unique_verts, v)) { /* Find a different node that uses 'v' */ PBVHNode *new_node; @@ -679,13 +799,13 @@ static void pbvh_bmesh_face_remove(PBVH *pbvh, BMFace *f) } else { /* Remove from other verts */ - BLI_gset_remove(f_node->bm_other_verts, v, NULL); + BLI_table_gset_remove(f_node->bm_other_verts, v, NULL); } } } while ((l_iter = l_iter->next) != l_first); /* Remove face from node and top level */ - BLI_gset_remove(f_node->bm_faces, f, NULL); + 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 */ @@ -730,6 +850,10 @@ struct EdgeQueue; 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; @@ -739,6 +863,7 @@ typedef struct EdgeQueue { #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 @@ -755,6 +880,50 @@ typedef struct { int cd_face_node_offset; } EdgeQueueContext; +BLI_INLINE float calc_weighted_edge_split(EdgeQueueContext *eq_ctx, BMVert *v1, BMVert *v2) +{ +#if 0 + float l = len_squared_v3v3(v1->co, v2->co); + float n; + + n = (float)(BM_vert_edge_count(v1) + BM_vert_edge_count(v2)) * 0.5f; + n = MAX2(n - 6.0f, 1.0f); + + return l * (n); +#elif 1 // 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) +{ +#if 0 + float l = len_squared_v3v3(v1->co, v2->co); + float n; + + n = (float)(BM_vert_edge_count(v1) + BM_vert_edge_count(v2)) * 0.5f; + n = MAX2(n - 5.0f, 1.0f); + + return l * (n*n*4.0f); +#elif 1 // 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 +} + /* 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)) @@ -794,18 +963,215 @@ static void pbvh_bmesh_edge_tag_verify(PBVH *pbvh) } #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; +} + +/* reduce script + +on factor; + +ax := 0; +ay := 0; + +e1x := bx - ax; +e1y := by - ay; +e2x := cx - bx; +e2y := cy - by; +e3x := ax - cx; +e3y := ay - cy; + +l1 := (e1x**2 + e1y**2)**0.5; +l2 := (e2x**2 + e2y**2)**0.5; +l3 := (e3x**2 + e3y**2)**0.5; + +load_package "avector"; + +e1 := avec(e1x / l1, e1y / l1, 0.0); +e2 := avec(e2x / l2, e2y / l2, 0.0); +e3 := avec(e3x / l3, e3y / l3, 0.0); + +ax := 0; +ay := 0; + +d1 := x1*e1[1] - y1*e1[0]; +d2 := x1*e2[1] - y1*e2[0]; +d3 := x1*e3[1] - y1*e3[0]; + +d1 := d1**2; +d2 := d2**2; +d3 := d3**2; + +on fort; +d1; +d2; +d3; +off fort; + +fdis := (sqrt(dis)/nz)**2 + planedis**2; +*/ + +static float dist_to_tri_sphere(float p[3], float v1[3], float v2[3], float v3[3], float n[3]) +{ + + // find projection axis; + int axis1, axis2, axis3; + double nx = n[0] < 0.0 ? -n[0] : n[0]; + double ny = n[1] < 0.0 ? -n[1] : n[1]; + double nz = n[2] < 0.0 ? -n[2] : n[2]; + + const double feps = 0.000001; + + if (nx > ny && nx > nz) { + axis1 = 1; + axis2 = 2; + axis3 = 0; + } + else if (ny > nx && ny > nz) { + axis1 = 0; + axis2 = 2; + axis3 = 1; + } + else { + axis1 = 0; + axis2 = 1; + axis3 = 2; + } + + double planedis = (p[0] - v1[0]) * n[0] + (p[1] - v1[1]) * n[1] + (p[2] - v1[2]) * n[2]; + planedis = planedis < 0.0 ? -planedis : planedis; + + double ax = v1[axis1], ay = v1[axis2]; + double bx = v2[axis1] - ax, by = v2[axis2] - ay; + double cx = v3[axis1] - ax, cy = v3[axis2] - ay; + double bx2 = bx * bx, by2 = by * by, cx2 = cx * cx, cy2 = cy * cy; + + double x1 = p[axis1] - ax; + double y1 = p[axis2] - ay; + + bool s1 = x1 * by - y1 * bx < 0.0; + bool s2 = x1 * (cy - by) - y1 * (cx - bx) < 0.0; + bool s3 = x1 * -cy - y1 * -cx < 0.0; + + int side = 0; + + int mask = s1 | (s2 << 1) | (s3 << 2); + if (mask == 0.0) { + return planedis * planedis; + } + + double d1, d2, d3, div; + + /* +//\ 3| +// \ | +// b +// | \ +// 1 | \ 2 +// | 0 \ +// ___a_______c___ +// 5 | 4 \ 6 +*/ + + double dis = 0.0; + switch (mask) { + case 1: + div = (bx2 + by2); + + if (div > feps) { + d1 = (bx * y1 - by * x1); + d1 = (d1 * d1) / div; + } + else { + d1 = x1 * x1 + y1 * y1; + } + + dis = d1; + break; + case 3: + dis = ((x1 - bx) * (x1 - bx) + (y1 - by) * (y1 - by)); + break; + case 2: + div = ((bx - cx) * (bx - cx) + (by - cy) * (by - cy)); + if (div > feps) { + d2 = ((bx - cx) * y1 - (by - cy) * x1); + d2 = (d2 * d2) / div; + } + else { + d2 = (x1 - bx) * (x1 - bx) + (y1 - by) * (y1 - by); + } + dis = d2; + break; + case 6: + dis = (x1 - cx) * (x1 - cx) + (y1 - cy) * (y1 - cy); + break; + case 4: + div = (cx2 + cy2); + + if (div > feps) { + d3 = (cx * y1 - cy * x1); + d3 = (d3 * d3) / div; + } + else { + d3 = (x1 - cx) * (x1 - cx) + (y1 - cy) * (y1 - cy); + } + + dis = d3; + break; + case 5: + dis = x1 * x1 + y1 * y1; + break; + } + + nz = n[axis3] < 0.0 ? -n[axis3] : n[axis3]; + + return (float)(dis + nz * nz * planedis * planedis) / (nz * nz); +} + static bool edge_queue_tri_in_sphere(const EdgeQueue *q, BMFace *f) { - BMVert *v_tri[3]; float c[3]; + float v1[3], v2[3], v3[3], co[3]; + const float mul = 1.0f; + + BMLoop *l = f->l_first; + + /* Check if triangle intersects the sphere */ + float dis = dist_to_tri_sphere(q->center, l->v->co, l->next->v->co, l->prev->v->co, f->no); + + // closest_on_tri_to_point_v3(c, co, v1, v2, v3); + + // float dis2 = len_squared_v3v3(q->center, c); + // float dis3 = sqrtf(dis2); + + return dis <= q->radius_squared; /* Get closest point in triangle to sphere center */ - BM_face_as_array_vert_tri(f, v_tri); +#if 0 + /* + closest_on_tri_to_point_v3 is being slow + */ + + float mindis = 1e17; + float dis; + copy_v3_v3(c, q->center); - closest_on_tri_to_point_v3(c, q->center, v_tri[0]->co, v_tri[1]->co, v_tri[2]->co); + for (int i=0; i<3; i++) { + dis = len_squared_v3v3(v_tri[i]->co, c); + mindis = MIN2(mindis, dis); + + dis = dist_squared_to_line_segment_v3(c, v_tri[i]->co, v_tri[(i+1)%3]->co); + mindis = MIN2(mindis, dis); + } + return mindis <= q->radius_squared; +#else + closest_on_tri_to_point_v3(c, q->center, l->v->co, l->next->v->co, l->prev->v->co); /* Check if triangle intersects the sphere */ return len_squared_v3v3(q->center, c) <= q->radius_squared; +#endif } static bool edge_queue_tri_in_circle(const EdgeQueue *q, BMFace *f) @@ -827,6 +1193,42 @@ static bool edge_queue_tri_in_circle(const EdgeQueue *q, BMFace *f) return len_squared_v3v3(q->center_proj, c) <= q->radius_squared; } +typedef struct EdgeQueueThreadData { + PBVH *pbvh; + PBVHNode *node; + BMEdge **edges; + EdgeQueueContext *eq_ctx; + int totedge; + int size; +} EdgeQueueThreadData; + +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); + } + } + + e->head.hflag |= BM_ELEM_TAG; + + 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; +} + /* Return true if the vertex mask is less than 1.0, false otherwise */ static bool check_mask(EdgeQueueContext *eq_ctx, BMVert *v) { @@ -835,6 +1237,10 @@ static bool check_mask(EdgeQueueContext *eq_ctx, BMVert *v) static void edge_queue_insert(EdgeQueueContext *eq_ctx, BMEdge *e, float priority) { + void **elems = eq_ctx->q->elems; + BLI_array_declare(elems); + BLI_array_len_set(elems, eq_ctx->q->totelems); + /* 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 @@ -848,7 +1254,14 @@ static void edge_queue_insert(EdgeQueueContext *eq_ctx, BMEdge *e, float priorit BMVert **pair = BLI_mempool_alloc(eq_ctx->pool); pair[0] = e->v1; pair[1] = e->v2; +#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); @@ -933,7 +1346,7 @@ static void short_edge_queue_edge_add(EdgeQueueContext *eq_ctx, BMEdge *e) if (EDGE_QUEUE_TEST(e) == false) #endif { - const float len_sq = BM_edge_calc_length_squared(e); + 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); } @@ -990,6 +1403,166 @@ static void short_edge_queue_face_add(EdgeQueueContext *eq_ctx, BMFace *f) } } +static void long_edge_queue_edge_add_recursive_2( + EdgeQueueThreadData *tdata, BMLoop *l_edge, BMLoop *l_end, const float len_sq, float limit_len) +{ + BLI_assert(len_sq > square_f(limit_len)); + + if (l_edge->e->head.hflag & BM_ELEM_TAG) { + return; + } + +#ifdef USE_EDGEQUEUE_FRONTFACE + if (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); + + /* temp support previous behavior! */ + if (UNLIKELY(G.debug_value == 1234)) { + return; + } + + if ((l_edge->radial_next != l_edge)) { + /* How much longer we need to be to consider for subdividing + * (avoids subdividing faces which are only *slightly* skinny) */ +#define EVEN_EDGELEN_THRESHOLD 1.2f + /* How much the limit increases per recursion + * (avoids performing subdivisions too far away). */ +#define EVEN_GENERATION_SCALE 1.6f + + const float len_sq_cmp = len_sq * EVEN_EDGELEN_THRESHOLD; + + limit_len *= EVEN_GENERATION_SCALE; + const float limit_len_sq = square_f(limit_len); + + BMLoop *l_iter = l_edge; + do { + BMLoop *l_adjacent[2] = {l_iter->next, l_iter->prev}; + for (int i = 0; i < ARRAY_SIZE(l_adjacent); i++) { + float len_sq_other = calc_weighted_edge_split( + 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); + long_edge_queue_edge_add_recursive_2( + tdata, 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 + } +} + +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; + + BMFace *f; + + 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 { +#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_2( + tdata, l_iter->radial_next, l_iter, len_sq, eq_ctx->q->limit_len); + } +#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 +} + +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 { + 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 { +#ifdef USE_EDGEQUEUE_EVEN_SUBDIV + const float len_sq = calc_weighted_edge_collapse(eq_ctx, l_iter->e->v1, l_iter->e->v2); + 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); + } +#else + const float len_sq = calc_weighted_edge_split(eq_ctx, l_iter->e->v1, l_iter->e->v2); + 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 +} + /* Create a priority queue containing vertex pairs connected by a long * edge as defined by PBVH.bm_max_edge_len. * @@ -1008,6 +1581,8 @@ static void long_edge_queue_create(EdgeQueueContext *eq_ctx, const bool use_projected) { 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_max_edge_len * pbvh->bm_max_edge_len; @@ -1025,32 +1600,69 @@ static void long_edge_queue_create(EdgeQueueContext *eq_ctx, 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; } #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)) { - GSetIterator gs_iter; + EdgeQueueThreadData td; - /* Check each face */ - GSET_ITER (gs_iter, node->bm_faces) { - BMFace *f = BLI_gsetIterator_getKey(&gs_iter); + 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); + + for (int i = 0; i < count; i++) { + EdgeQueueThreadData *td = tdata + i; + + BMEdge **edges = td->edges; + for (int j = 0; j < td->totedge; j++) { + BMEdge *e = edges[j]; + e->head.hflag &= ~BM_ELEM_TAG; + edge_queue_insert(eq_ctx, e, -calc_weighted_edge_split(eq_ctx, e->v1, e->v2)); + } + + if (td->edges) { + MEM_freeN(td->edges); } } + BLI_array_free(tdata); } /* Create a priority queue containing vertex pairs connected by a @@ -1071,6 +1683,8 @@ static void short_edge_queue_create(EdgeQueueContext *eq_ctx, const bool use_projected) { 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; @@ -1088,28 +1702,67 @@ static void short_edge_queue_create(EdgeQueueContext *eq_ctx, 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; } + EdgeQueueThreadData *tdata = NULL; + BLI_array_declare(tdata); + for (int n = 0; n < pbvh->totnode; n++) { PBVHNode *node = &pbvh->nodes[n]; + EdgeQueueThreadData td; - /* Check leaf nodes marked for topology update */ if ((node->flag & PBVH_Leaf) && (node->flag & PBVH_UpdateTopology) && !(node->flag & PBVH_FullyHidden)) { - GSetIterator gs_iter; + memset(&td, 0, sizeof(td)); + td.pbvh = pbvh; + td.node = node; + td.eq_ctx = eq_ctx; - /* Check each face */ - GSET_ITER (gs_iter, node->bm_faces) { - BMFace *f = BLI_gsetIterator_getKey(&gs_iter); + 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); + + for (int i = 0; i < count; i++) { + EdgeQueueThreadData *td = tdata + i; + + BMEdge **edges = td->edges; + for (int j = 0; j < td->totedge; j++) { + BMEdge *e = edges[j]; + e->head.hflag &= ~BM_ELEM_TAG; + edge_queue_insert(eq_ctx, e, calc_weighted_edge_collapse(eq_ctx, e->v1, e->v2)); + } + + if (td->edges) { + MEM_freeN(td->edges); + } + } + + BLI_array_free(tdata); } /*************************** Topology update **************************/ @@ -1119,6 +1772,8 @@ static void pbvh_bmesh_split_edge(EdgeQueueContext *eq_ctx, BMEdge *e, BLI_Buffer *edge_loops) { + BMesh *bm = pbvh->bm; + float co_mid[3], no_mid[3]; /* Get all faces adjacent to the edge */ @@ -1133,6 +1788,25 @@ static void pbvh_bmesh_split_edge(EdgeQueueContext *eq_ctx, BMVert *v_new = pbvh_bmesh_vert_create( pbvh, node_index, co_mid, no_mid, eq_ctx->cd_vert_mask_offset); +#ifdef DYNTOPO_CD_INTERP + // transfer edge flags + + BMEdge *e1 = BM_edge_create(pbvh->bm, e->v1, v_new, e, BM_CREATE_NOP); + BMEdge *e2 = BM_edge_create(pbvh->bm, v_new, e->v2, e, BM_CREATE_NOP); + + 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; + + /*TODO: is it worth interpolating edge customdata?*/ + + void *vsrcs[2] = {e->v1->head.data, e->v2->head.data}; + float vws[2] = {0.5f, 0.5f}; + CustomData_bmesh_interp(&pbvh->bm->vdata, vsrcs, vws, NULL, 2, v_new->head.data); +#endif + /* 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); @@ -1201,31 +1875,78 @@ static void pbvh_bmesh_split_edge(EdgeQueueContext *eq_ctx, f_new = pbvh_bmesh_face_create(pbvh, ni, v_tri, e_tri, f_adj); long_edge_queue_face_add(eq_ctx, f_new); + pbvh_bmesh_copy_facedata(bm, f_new, f_adj); + +#ifdef DYNTOPO_CD_INTERP + 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, 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, 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, lsrcs, lws, lws, 1, f_new->l_first->prev->head.data); + +#endif + 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); + pbvh_bmesh_copy_facedata(bm, f_new, f_adj); + +#ifdef DYNTOPO_CD_INTERP + lsrcs[0] = lfirst->head.data; + lsrcs[1] = lfirst->next->head.data; + lws[0] = lws[1] = 0.5f; + + CustomData_bmesh_interp(&pbvh->bm->ldata, 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, 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, lsrcs, lws, lws, 1, f_new->l_first->prev->head.data); +#endif + /* 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); - } + if (!BLI_table_gset_haskey(pbvh->nodes[ni].bm_unique_verts, v_new)) { + BLI_table_gset_add(pbvh->nodes[ni].bm_other_verts, v_new); } } @@ -1237,9 +1958,35 @@ static bool pbvh_bmesh_subdivide_long_edges(EdgeQueueContext *eq_ctx, BLI_Buffer *edge_loops) { bool any_subdivided = false; + double time = PIL_check_seconds_timer(); + + RNG *rng = BLI_rng_new((int)(time * 1000.0f)); + int step = 0; while (!BLI_heapsimple_is_empty(eq_ctx->q->heap)) { + if (step++ > DYNTOPO_MAX_ITER) { + 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 BMVert **pair = BLI_heapsimple_pop_min(eq_ctx->q->heap); +#endif BMVert *v1 = pair[0], *v2 = pair[1]; BMEdge *e; @@ -1261,7 +2008,7 @@ static bool pbvh_bmesh_subdivide_long_edges(EdgeQueueContext *eq_ctx, continue; } #else - BLI_assert(len_squared_v3v3(v1->co, v2->co) > eq_ctx->q->limit_len_squared); + 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 @@ -1278,10 +2025,25 @@ static bool pbvh_bmesh_subdivide_long_edges(EdgeQueueContext *eq_ctx, pbvh_bmesh_split_edge(eq_ctx, pbvh, e, edge_loops); } +#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 + BLI_rng_free(rng); + return any_subdivided; } @@ -1295,6 +2057,27 @@ static void pbvh_bmesh_collapse_edge(PBVH *pbvh, { BMVert *v_del, *v_conn; +#ifdef DYNTOPO_CD_INTERP + if (BM_elem_flag_test(e, BM_ELEM_SEAM)) { + for (int step = 0; step < 2; step++) { + int count = 0; + BMVert *v = step ? v2 : v1; + BMIter iter; + BMEdge *e2; + + BM_ITER_ELEM (e2, &iter, v, BM_EDGES_OF_VERT) { + if (BM_elem_flag_test(e2, BM_ELEM_SEAM)) { + count++; + } + } + + if (count < 2) { + return; + } + } + } +#endif + /* 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)) { @@ -1314,6 +2097,29 @@ static void pbvh_bmesh_collapse_edge(PBVH *pbvh, while ((l_adj = e->l)) { BMFace *f_adj = l_adj->f; +#ifdef DYNTOPO_CD_INTERP + int eflag = 0; + + // propegate flags to merged edges + BMLoop *l = f_adj->l_first; + do { + BMEdge *e2 = l->e; + + if (e2 != e) { + eflag |= e2->head.hflag & ~BM_ELEM_HIDDEN; + } + + l = l->next; + } while (l != f_adj->l_first); + + do { + BMEdge *e2 = l->e; + e2->head.hflag |= eflag; + + l = l->next; + } while (l != f_adj->l_first); +#endif + pbvh_bmesh_face_remove(pbvh, f_adj); BM_face_kill(pbvh->bm, f_adj); } @@ -1329,7 +2135,53 @@ static void pbvh_bmesh_collapse_edge(PBVH *pbvh, * really buy anything. */ BLI_buffer_clear(deleted_faces); +#define MAX_LS 24 + BMLoop *l; +#if 1 // def DYNTOPO_CD_INTERP + BMLoop *ls[MAX_LS]; + + int totl = 0; + + BM_LOOPS_OF_VERT_ITER_BEGIN (l, v_del) { + if (totl >= MAX_LS) { + break; + } + ls[totl++] = l; + } + BM_LOOPS_OF_VERT_ITER_END; + + BM_LOOPS_OF_VERT_ITER_BEGIN (l, v_conn) { + if (totl >= MAX_LS) { + break; + } + ls[totl++] = l; + } + BM_LOOPS_OF_VERT_ITER_END; + + void *blocks[MAX_LS]; + float ws[MAX_LS], w = totl > 1 ? 1.0f / (float)(totl - 1) : 1.0f; + + for (int i = 0; i < totl - 1; i++) { + blocks[i] = ls[i + 1]->head.data; + ws[i] = w; + } + + if (totl > 1) { + CustomData_bmesh_interp(&pbvh->bm->ldata, blocks, ws, NULL, totl - 1, ls[0]->head.data); + + BM_LOOPS_OF_VERT_ITER_BEGIN (l, v_del) { + CustomData_bmesh_copy_data( + &pbvh->bm->ldata, &pbvh->bm->ldata, ls[0]->head.data, &l->head.data); + } + BM_LOOPS_OF_VERT_ITER_END; + BM_LOOPS_OF_VERT_ITER_BEGIN (l, v_conn) { + CustomData_bmesh_copy_data( + &pbvh->bm->ldata, &pbvh->bm->ldata, ls[0]->head.data, &l->head.data); + } + BM_LOOPS_OF_VERT_ITER_END; + } +#endif BM_LOOPS_OF_VERT_ITER_BEGIN (l, v_del) { BMFace *existing_face; @@ -1368,11 +2220,26 @@ static void pbvh_bmesh_collapse_edge(PBVH *pbvh, 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); + BMFace *f2 = pbvh_bmesh_face_create(pbvh, ni, v_tri, e_tri, f); + +#ifdef DYNTOPO_CD_INTERP + BMLoop *l2 = f2->l_first; + // sync edge flags + l2->e->head.hflag |= (l->e->head.hflag & ~BM_ELEM_HIDDEN); + // l2->prev->e->head.hflag |= (l->prev->e->head.hflag & ~BM_ELEM_HIDDEN); + + pbvh_bmesh_copy_facedata(pbvh->bm, f2, f); + + 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); +#endif /* 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); + if (!BLI_table_gset_haskey(n->bm_unique_verts, v_conn)) { + BLI_table_gset_add(n->bm_other_verts, v_conn); } } @@ -1430,7 +2297,12 @@ static void pbvh_bmesh_collapse_edge(PBVH *pbvh, /* 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); + // 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, true); + // void *dummy; + // BKE_pbvh_bmesh_update_origvert(pbvh, v_conn, &dummy, &dummy, &dummy); + 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); @@ -1452,6 +2324,44 @@ static void pbvh_bmesh_collapse_edge(PBVH *pbvh, BM_vert_kill(pbvh->bm, v_del); } +void BKE_pbvh_bmesh_update_origvert( + PBVH *pbvh, BMVert *v, float **r_co, float **r_no, float **r_color) +{ + float *co = NULL, *no = NULL; + + BM_log_vert_before_modified(pbvh->bm_log, v, pbvh->cd_vert_mask_offset, r_color != NULL); + + if (r_co || r_no) { + co = BM_ELEM_CD_GET_VOID_P(v, pbvh->cd_origco_offset); + no = BM_ELEM_CD_GET_VOID_P(v, pbvh->cd_origno_offset); + + copy_v3_v3(co, v->co); + copy_v3_v3(no, v->no); + + if (r_co) { + *r_co = co; + } + + if (r_no) { + *r_no = no; + } + } + + if (r_color && pbvh->cd_vcol_offset >= 0 && pbvh->cd_origvcol_offset >= 0) { + MPropCol *ml1 = BM_ELEM_CD_GET_VOID_P(v, pbvh->cd_vcol_offset); + MPropCol *ml2 = BM_ELEM_CD_GET_VOID_P(v, pbvh->cd_origvcol_offset); + + copy_v4_v4(ml2->color, ml1->color); + + if (r_color) { + *r_color = ml2->color; + } + } + else if (r_color) { + *r_color = NULL; + } +} + static bool pbvh_bmesh_collapse_short_edges(EdgeQueueContext *eq_ctx, PBVH *pbvh, BLI_Buffer *deleted_faces) @@ -1461,8 +2371,39 @@ static bool pbvh_bmesh_collapse_short_edges(EdgeQueueContext *eq_ctx, /* 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(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++ > DYNTOPO_MAX_ITER) { + 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 BMVert **pair = BLI_heapsimple_pop_min(eq_ctx->q->heap); +#endif BMVert *v1 = pair[0], *v2 = pair[1]; BLI_mempool_free(eq_ctx->pool, pair); pair = NULL; @@ -1482,7 +2423,7 @@ static bool pbvh_bmesh_collapse_short_edges(EdgeQueueContext *eq_ctx, EDGE_QUEUE_DISABLE(e); #endif - if (len_squared_v3v3(v1->co, v2->co) >= min_len_squared) { + if (calc_weighted_edge_collapse(eq_ctx, v1, v2) >= min_len_squared) { continue; } @@ -1498,8 +2439,32 @@ static bool pbvh_bmesh_collapse_short_edges(EdgeQueueContext *eq_ctx, any_collapsed = true; 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; @@ -1513,7 +2478,8 @@ bool pbvh_bmesh_node_raycast(PBVHNode *node, struct IsectRayPrecalc *isect_precalc, float *depth, bool use_original, - int *r_active_vertex_index, + SculptVertRef *r_active_vertex_index, + SculptFaceRef *r_active_face_index, float *r_face_normal) { bool hit = false; @@ -1522,29 +2488,69 @@ bool pbvh_bmesh_node_raycast(PBVHNode *node, 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); + + bool hit2 = ray_face_intersection_tri(ray_start, + isect_precalc, + node->bm_orco[t[0]], + node->bm_orco[t[1]], + node->bm_orco[t[2]], + depth); + + // ensure sculpt active vertex is set r_active_vertex_index + if (hit2) { + int k = 0; + BMFace *f = NULL; + + TGSET_ITER_INDEX(f, node->bm_faces, k) + { + if (k == i) { + break; + } + } + TGSET_ITER_INDEX_END + + if (!f) { + continue; + } + + BMLoop *l = f->l_first; + + for (int j = 0; j < 3; j++, l = l->next) { + float dist = len_squared_v3v3(node->bm_orco[t[j]], ray_start); + + if (!hit || dist < len_squared_v3v3(ray_start, nearest_vertex_co)) { + int idx = t[j]; + + hit = true; + copy_v3_v3(nearest_vertex_co, node->bm_orco[t[j]]); + + if (r_active_vertex_index) { + *r_active_vertex_index = BKE_pbvh_make_vref((intptr_t)l->v); + } + + if (r_active_face_index) { + *r_active_face_index = BKE_pbvh_make_fref((intptr_t)l->f); + } + } + } + } + + hit = true; } } else { - GSetIterator gs_iter; - - GSET_ITER (gs_iter, node->bm_faces) { - BMFace *f = BLI_gsetIterator_getKey(&gs_iter); + BMFace *f; + TGSET_ITER (f, node->bm_faces) { 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); + bool hit2; - if (ray_face_intersection_tri( + if (hit2 = ray_face_intersection_tri( ray_start, isect_precalc, v_tri[0]->co, v_tri[1]->co, v_tri[2]->co, depth)) { - hit = true; if (r_face_normal) { normal_tri_v3(r_face_normal, v_tri[0]->co, v_tri[1]->co, v_tri[2]->co); @@ -1553,17 +2559,26 @@ bool pbvh_bmesh_node_raycast(PBVHNode *node, 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)) { + if (!hit || 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]); + SculptVertRef vref = {(intptr_t)v_tri[j]}; // BM_elem_index_get(v_tri[j]); + *r_active_vertex_index = vref; + + if (r_active_face_index) { + *r_active_face_index = BKE_pbvh_make_fref((intptr_t)f); + } } } } + + hit = true; } } } + TGSET_ITER_END } return hit; @@ -1579,14 +2594,13 @@ bool BKE_pbvh_bmesh_node_raycast_detail(PBVHNode *node, return 0; } - GSetIterator gs_iter; + BMFace *f; bool hit = false; BMFace *f_hit = NULL; - GSET_ITER (gs_iter, node->bm_faces) { - BMFace *f = BLI_gsetIterator_getKey(&gs_iter); - + TGSET_ITER (f, node->bm_faces) { BLI_assert(f->len == 3); + if (!BM_elem_flag_test(f, BM_ELEM_HIDDEN)) { BMVert *v_tri[3]; bool hit_local; @@ -1600,6 +2614,7 @@ bool BKE_pbvh_bmesh_node_raycast_detail(PBVHNode *node, } } } + TGSET_ITER_END if (hit) { BMVert *v_tri[3]; @@ -1637,11 +2652,9 @@ bool pbvh_bmesh_node_nearest_to_ray(PBVHNode *node, } } else { - GSetIterator gs_iter; - - GSET_ITER (gs_iter, node->bm_faces) { - BMFace *f = BLI_gsetIterator_getKey(&gs_iter); + BMFace *f; + TGSET_ITER (f, node->bm_faces) { BLI_assert(f->len == 3); if (!BM_elem_flag_test(f, BM_ELEM_HIDDEN)) { BMVert *v_tri[3]; @@ -1651,6 +2664,7 @@ bool pbvh_bmesh_node_nearest_to_ray(PBVHNode *node, ray_start, ray_normal, v_tri[0]->co, v_tri[1]->co, v_tri[2]->co, depth, dist_sq); } } + TGSET_ITER_END } return hit; @@ -1662,18 +2676,25 @@ void pbvh_bmesh_normals_update(PBVHNode **nodes, int totnode) 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; } } @@ -1818,11 +2839,11 @@ 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->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 +2854,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 +2862,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); } } @@ -1881,20 +2902,52 @@ void BKE_pbvh_build_bmesh(PBVH *pbvh, 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_origco_offset, + const int cd_origno_offset, + const int cd_origvcol_offset) { pbvh->cd_vert_node_offset = cd_vert_node_offset; pbvh->cd_face_node_offset = cd_face_node_offset; + pbvh->cd_origco_offset = cd_origco_offset; + pbvh->cd_origno_offset = cd_origno_offset; + pbvh->cd_vert_mask_offset = CustomData_get_offset(&bm->vdata, CD_PAINT_MASK); + pbvh->cd_origvcol_offset = cd_origvcol_offset; + pbvh->bm = bm; BKE_pbvh_bmesh_detail_size_set(pbvh, 0.75); 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); /* TODO: choose leaf limit better */ - pbvh->leaf_limit = 100; + pbvh->leaf_limit = 1000; + + BMIter iter; + BMVert *v; + + int cd_vcol_offset = -1; + if (cd_origvcol_offset >= 0) { + cd_vcol_offset = CustomData_get_offset(&bm->vdata, CD_PROP_COLOR); + } + + BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) { + float *co = BM_ELEM_CD_GET_VOID_P(v, cd_origco_offset); + float *no = BM_ELEM_CD_GET_VOID_P(v, cd_origno_offset); + + copy_v3_v3(co, v->co); + copy_v3_v3(no, v->no); + if (cd_origvcol_offset >= 0) { + MPropCol *c1 = BM_ELEM_CD_GET_VOID_P(v, cd_vcol_offset); + MPropCol *c2 = BM_ELEM_CD_GET_VOID_P(v, cd_origvcol_offset); + + copy_v4_v4(c2->color, c1->color); + } + } if (smooth_shading) { pbvh->flags |= PBVH_DYNTOPO_SMOOTH_SHADING; } @@ -1904,7 +2957,6 @@ void BKE_pbvh_build_bmesh(PBVH *pbvh, 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 +2978,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,6 +3004,52 @@ void BKE_pbvh_build_bmesh(PBVH *pbvh, MEM_freeN(nodeinfo); } +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) +{ + bool modified = false; + + for (int i = 0; i < pbvh->totnode; i++) { + PBVHNode *node = pbvh->nodes + i; + + if (!(node->flag & PBVH_Leaf) || !searchcb(node, searchdata)) { + continue; + } + + if (node->flag & PBVH_Leaf) { + undopush(node, searchdata); + + BKE_pbvh_node_mark_topology_update(pbvh->nodes + i); + BKE_pbvh_bmesh_node_save_ortri(pbvh->bm, pbvh->nodes + i); + } + } + + modified = modified || BKE_pbvh_bmesh_update_topology(pbvh, + mode, + center, + view_normal, + radius, + use_frontface, + use_projected, + sym_axis, + updatePBVH); + return modified; +} + /* Collapse short edges, subdivide long edges */ bool BKE_pbvh_bmesh_update_topology(PBVH *pbvh, PBVHTopologyUpdateMode mode, @@ -1960,8 +3057,20 @@ bool BKE_pbvh_bmesh_update_topology(PBVH *pbvh, const float view_normal[3], float radius, const bool use_frontface, - const bool use_projected) + const bool use_projected, + int sym_axis, + bool updatePBVH) { + /* + if (sym_axis >= 0 && + PIL_check_seconds_timer() - last_update_time[sym_axis] < DYNTOPO_RUN_INTERVAL) { + return false; + } + + if (sym_axis >= 0) { + last_update_time[sym_axis] = PIL_check_seconds_timer(); + }*/ + /* 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); @@ -1974,7 +3083,7 @@ bool BKE_pbvh_bmesh_update_topology(PBVH *pbvh, if (view_normal) { BLI_assert(len_squared_v3(view_normal) != 0.0f); } - +#if 1 if (mode & PBVH_Collapse) { EdgeQueue q; BLI_mempool *queue_pool = BLI_mempool_create(sizeof(BMVert *) * 2, 0, 128, BLI_MEMPOOL_NOP); @@ -1991,6 +3100,9 @@ bool BKE_pbvh_bmesh_update_topology(PBVH *pbvh, &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); + if (q.elems) { + MEM_freeN(q.elems); + } BLI_mempool_destroy(queue_pool); } @@ -2009,18 +3121,51 @@ bool BKE_pbvh_bmesh_update_topology(PBVH *pbvh, 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); + if (q.elems) { + MEM_freeN(q.elems); + } BLI_heapsimple_free(q.heap, NULL); BLI_mempool_destroy(queue_pool); } +#endif + if (modified) { - /* Unmark nodes */ - for (int n = 0; n < pbvh->totnode; n++) { - PBVHNode *node = &pbvh->nodes[n]; +#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 + + 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_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; + if ((node->flag & PBVH_Leaf) && (node->flag & PBVH_UpdateTopology)) { + node->flag &= ~PBVH_UpdateTopology; + } } } + BLI_buffer_free(&edge_loops); BLI_buffer_free(&deleted_faces); @@ -2035,43 +3180,45 @@ bool BKE_pbvh_bmesh_update_topology(PBVH *pbvh, * (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) +void BKE_pbvh_bmesh_node_save_ortri(BMesh *bm, PBVHNode *node) { /* Skip if original coords/triangles are already saved */ if (node->bm_orco) { return; } - const int totvert = BLI_gset_len(node->bm_unique_verts) + BLI_gset_len(node->bm_other_verts); + const int totvert = BLI_table_gset_len(node->bm_unique_verts) + + BLI_table_gset_len(node->bm_other_verts); - const int tottri = BLI_gset_len(node->bm_faces); + const int tottri = BLI_table_gset_len(node->bm_faces); node->bm_orco = MEM_mallocN(sizeof(*node->bm_orco) * totvert, __func__); node->bm_ortri = MEM_mallocN(sizeof(*node->bm_ortri) * tottri, __func__); /* Copy out the vertices and assign a temporary index */ int i = 0; - GSetIterator gs_iter; - GSET_ITER (gs_iter, node->bm_unique_verts) { - BMVert *v = BLI_gsetIterator_getKey(&gs_iter); + BMVert *v; + TGSET_ITER (v, node->bm_unique_verts) { 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); + TGSET_ITER_END + + TGSET_ITER (v, node->bm_other_verts) { copy_v3_v3(node->bm_orco[i], v->co); BM_elem_index_set(v, i); /* set_dirty! */ i++; } + TGSET_ITER_END + /* 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); - + BMFace *f; + TGSET_ITER (f, node->bm_faces) { if (BM_elem_flag_test(f, BM_ELEM_HIDDEN)) { continue; } @@ -2089,13 +3236,373 @@ void BKE_pbvh_bmesh_node_save_orig(BMesh *bm, PBVHNode *node) #endif i++; } + TGSET_ITER_END node->bm_tot_ortri = i; } +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; +} + +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_corect_tree(PBVH *pbvh, PBVHNode *node, PBVHNode *parent) +{ + const int size_lower = pbvh->leaf_limit - (pbvh->leaf_limit >> 1); + const int size_higher = pbvh->leaf_limit + (pbvh->leaf_limit >> 1); + + if (node->flag & PBVH_Leaf) { + // pbvh_trimesh_node_limit_ensure(pbvh, (int)(node - pbvh->nodes)); + return; + + // join nodes if subtree lacks verts, unless node is root + } + + 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; + + TableGSet *other = BLI_table_gset_new(__func__); + BMVert *v; + + node->children_offset = 0; + node->draw_buffers = NULL; + + // 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 + + // 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_corect_tree(pbvh, child, node); + } +} + +static void pbvh_bmesh_join_nodes(PBVH *bvh) +{ + pbvh_count_subtree_verts(bvh, bvh->nodes); + BKE_pbvh_bmesh_corect_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; + 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"); + } + else if ((n1->flag & PBVH_Delete) && (n2->flag & PBVH_Delete)) { + n->children_offset = 0; + n->flag |= PBVH_Leaf; + + 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"); + } + } + } + + 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; + } + if (n->draw_buffers) { + GPU_pbvh_buffers_free(n->draw_buffers); + n->draw_buffers = NULL; + } + 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->bm_orco) { + MEM_freeN(n->bm_orco); + n->bm_orco = NULL; + } + + if (n->bm_ortri) { + MEM_freeN(n->bm_ortri); + n->bm_ortri = NULL; + } + + 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)) { + 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; + + BMVert *v; + + // 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) { + check_heap(); + int totnode = pbvh->totnode; + + pbvh_bmesh_join_nodes(pbvh); + + check_heap(); + for (int i = 0; i < pbvh->totnode; i++) { - PBVHNode *n = &pbvh->nodes[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) { /* Free orco/ortri data */ pbvh_bmesh_node_drop_orig(n); @@ -2105,6 +3612,8 @@ void BKE_pbvh_bmesh_after_stroke(PBVH *pbvh) pbvh_bmesh_node_limit_ensure(pbvh, i); } } + + BKE_pbvh_update_bounds(pbvh, (PBVH_UpdateBB | PBVH_UpdateOriginalBB | PBVH_UpdateRedraw)); } void BKE_pbvh_bmesh_detail_size_set(PBVH *pbvh, float detail_size) @@ -2118,17 +3627,17 @@ 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) { 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; } @@ -2189,23 +3698,23 @@ static void print_flag_factors(int flag) static void pbvh_bmesh_verify(PBVH *pbvh) { /* build list of faces & verts to lookup */ - GSet *faces_all = BLI_gset_ptr_new_ex(__func__, pbvh->bm->totface); + GSet *faces_all = BLI_table_gset_new_ex(__func__, pbvh->bm->totface); BMIter iter; { 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); + BLI_table_gset_insert(faces_all, f); } } - GSet *verts_all = BLI_gset_ptr_new_ex(__func__, pbvh->bm->totvert); + GSet *verts_all = BLI_table_gset_new_ex(__func__, pbvh->bm->totvert); { 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); + BLI_table_gset_insert(verts_all, v); } } } @@ -2215,12 +3724,12 @@ static void pbvh_bmesh_verify(PBVH *pbvh) 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; + totface += n->bm_faces ? BLI_table_gset_len(n->bm_faces) : 0; + totvert += n->bm_unique_verts ? BLI_table_gset_len(n->bm_unique_verts) : 0; } - BLI_assert(totface == BLI_gset_len(faces_all)); - BLI_assert(totvert == BLI_gset_len(verts_all)); + BLI_assert(totface == BLI_table_gset_len(faces_all)); + BLI_assert(totvert == BLI_table_gset_len(verts_all)); } { @@ -2234,23 +3743,24 @@ static void pbvh_bmesh_verify(PBVH *pbvh) BLI_assert(n->flag & PBVH_Leaf); /* Check that the face's node knows it owns the face */ - BLI_assert(BLI_gset_haskey(n->bm_faces, f)); + BLI_assert(BLI_table_gset_haskey(n->bm_faces, f)); /* Check the face's vertices... */ BM_ITER_ELEM (v, &bm_iter, f, BM_VERTS_OF_FACE) { PBVHNode *nv; /* Check that the vertex is in the node */ - BLI_assert(BLI_gset_haskey(n->bm_unique_verts, v) ^ BLI_gset_haskey(n->bm_other_verts, v)); + BLI_assert(BLI_table_gset_haskey(n->bm_unique_verts, v) ^ + BLI_table_gset_haskey(n->bm_other_verts, v)); /* Check that the vertex has a node owner */ nv = pbvh_bmesh_node_lookup(pbvh, v); /* Check that the vertex's node knows it owns the vert */ - BLI_assert(BLI_gset_haskey(nv->bm_unique_verts, v)); + BLI_assert(BLI_table_gset_haskey(nv->bm_unique_verts, v)); /* Check that the vertex isn't duplicated as an 'other' vert */ - BLI_assert(!BLI_gset_haskey(nv->bm_other_verts, v)); + BLI_assert(!BLI_table_gset_haskey(nv->bm_other_verts, v)); } } } @@ -2270,10 +3780,10 @@ static void pbvh_bmesh_verify(PBVH *pbvh) BLI_assert(n->flag & PBVH_Leaf); /* Check that the vert's node knows it owns the vert */ - BLI_assert(BLI_gset_haskey(n->bm_unique_verts, v)); + BLI_assert(BLI_table_gset_haskey(n->bm_unique_verts, v)); /* Check that the vertex isn't duplicated as an 'other' vert */ - BLI_assert(!BLI_gset_haskey(n->bm_other_verts, v)); + BLI_assert(!BLI_table_gset_haskey(n->bm_other_verts, v)); /* Check that the vert's node also contains one of the vert's * adjacent faces */ @@ -2294,7 +3804,7 @@ static void pbvh_bmesh_verify(PBVH *pbvh) 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)); + BLI_assert(!BLI_table_gset_haskey(n_other->bm_unique_verts, v)); } } # endif @@ -2308,7 +3818,7 @@ static void pbvh_bmesh_verify(PBVH *pbvh) 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)) { + if ((n->bm_unique_verts != NULL) && BLI_table_gset_haskey(n->bm_unique_verts, vi)) { has_unique = true; } } @@ -2330,28 +3840,202 @@ static void pbvh_bmesh_verify(PBVH *pbvh) 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)); + BLI_assert(BLI_table_gset_haskey(faces_all, f)); } GSET_ITER (gs_iter, n->bm_unique_verts) { BMVert *v = BLI_gsetIterator_getKey(&gs_iter); PBVHNode *n_other = pbvh_bmesh_node_lookup(pbvh, v); - BLI_assert(!BLI_gset_haskey(n->bm_other_verts, v)); + BLI_assert(!BLI_table_gset_haskey(n->bm_other_verts, v)); BLI_assert(n == n_other); - BLI_assert(BLI_gset_haskey(verts_all, v)); + BLI_assert(BLI_table_gset_haskey(verts_all, v)); } GSET_ITER (gs_iter, n->bm_other_verts) { BMVert *v = BLI_gsetIterator_getKey(&gs_iter); /* this happens sometimes and seems harmless */ // BLI_assert(!BM_vert_face_check(v)); - BLI_assert(BLI_gset_haskey(verts_all, v)); + BLI_assert(BLI_table_gset_haskey(verts_all, v)); } } } - BLI_gset_free(faces_all, NULL); - BLI_gset_free(verts_all, NULL); + BLI_table_gset_free(faces_all, NULL); + BLI_table_gset_free(verts_all, NULL); } #endif + +void BKE_pbvh_update_offsets(PBVH *pbvh, + const int cd_vert_node_offset, + const int cd_face_node_offset, + const int cd_origco_offset, + const int cd_origno_offset, + const int cd_origvcol_offset) +{ + pbvh->cd_face_node_offset = cd_face_node_offset; + pbvh->cd_vert_node_offset = cd_vert_node_offset; + pbvh->cd_vert_mask_offset = CustomData_get_offset(&pbvh->bm->vdata, CD_PAINT_MASK); + pbvh->cd_origco_offset = cd_origco_offset; + pbvh->cd_origno_offset = cd_origno_offset; + pbvh->cd_origvcol_offset = cd_origvcol_offset; + pbvh->cd_faceset_offset = CustomData_get_offset(&pbvh->bm->pdata, CD_SCULPT_FACE_SETS); +} + +static void scan_edge_split(BMesh *bm, BMEdge **edges, int totedge) +{ + 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; + } + + if (e->v2_disk_link.next->v1 == e->v2) { + next = &e->v2_disk_link.next->v1_disk_link; + } + else { + next = &e->v2_disk_link.next->v2_disk_link; + } + + prev->next = e->v2_disk_link.next; + next->prev = e->v2_disk_link.prev; + } + + for (int i = 0; i < totedge; i++) { + BMEdge *e = edges[i]; + + BMVert *v2 = 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; + } + + 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; + + 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); +} diff --git a/source/blender/blenkernel/intern/pbvh_intern.h b/source/blender/blenkernel/intern/pbvh_intern.h index 63bc8753fc7..8a0253facc8 100644 --- a/source/blender/blenkernel/intern/pbvh_intern.h +++ b/source/blender/blenkernel/intern/pbvh_intern.h @@ -16,6 +16,8 @@ #pragma once +#include "BLI_ghash.h" + /** \file * \ingroup bli */ @@ -43,6 +45,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 +89,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,15 +101,18 @@ struct PBVHNode { PBVHProxyNode *proxies; /* Dyntopo */ - GSet *bm_faces; - GSet *bm_unique_verts; - GSet *bm_other_verts; + TableGSet *bm_faces; + TableGSet *bm_unique_verts; + TableGSet *bm_other_verts; float (*bm_orco)[3]; int (*bm_ortri)[3]; int bm_tot_ortri; /* 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 { @@ -170,12 +176,20 @@ struct PBVH { float bm_min_edge_len; int cd_vert_node_offset; int cd_face_node_offset; + int cd_vert_mask_offset; + int cd_vcol_offset; + int cd_origco_offset; + int cd_origno_offset; + int cd_origvcol_offset; + int cd_faceset_offset; float planes[6][4]; int num_planes; struct BMLog *bm_log; struct SubdivCCG *subdiv_ccg; + + bool flat_vcol_shading; }; /* pbvh.c */ @@ -224,7 +238,8 @@ bool pbvh_bmesh_node_raycast(PBVHNode *node, struct IsectRayPrecalc *isect_precalc, float *dist, bool use_original, - int *r_active_vertex_index, + SculptVertRef *r_active_vertex_index, + SculptFaceRef *r_active_face_index, float *r_face_normal); bool pbvh_bmesh_node_nearest_to_ray(PBVHNode *node, const float ray_start[3], diff --git a/source/blender/blenkernel/intern/scene.c b/source/blender/blenkernel/intern/scene.c index 3633dff8690..d77541924a7 100644 --- a/source/blender/blenkernel/intern/scene.c +++ b/source/blender/blenkernel/intern/scene.c @@ -2565,6 +2565,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..344162a6eb1 100644 --- a/source/blender/blenkernel/intern/subdiv_displacement_multires.c +++ b/source/blender/blenkernel/intern/subdiv_displacement_multires.c @@ -360,8 +360,9 @@ 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); + //XXX + //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); } static void free_displacement(SubdivDisplacement *displacement) diff --git a/source/blender/blenkernel/intern/subdiv_eval.c b/source/blender/blenkernel/intern/subdiv_eval.c index 201d308e096..b858c847a68 100644 --- a/source/blender/blenkernel/intern/subdiv_eval.c +++ b/source/blender/blenkernel/intern/subdiv_eval.c @@ -174,6 +174,13 @@ void BKE_subdiv_eval_limit_point( BKE_subdiv_eval_limit_point_and_derivatives(subdiv, ptex_face_index, u, v, r_P, NULL, NULL); } +#if 0 +static bool close_zero(float c[3]) { + const float eps = 0.00001; + return fabs(c[0]) < eps && fabs(c[1]) < eps && fabs(c[2]) < eps; +} +#endif + void BKE_subdiv_eval_limit_point_and_derivatives(Subdiv *subdiv, const int ptex_face_index, const float u, @@ -196,6 +203,7 @@ void BKE_subdiv_eval_limit_point_and_derivatives(Subdiv *subdiv, * that giving totally unusable derivatives. */ if (r_dPdu != NULL && r_dPdv != NULL) { + //if ((close_zero(r_dPdu) || close_zero(r_dPdv)) || equals_v3v3(r_dPdu, r_dPdv)) { if ((is_zero_v3(r_dPdu) || is_zero_v3(r_dPdv)) || equals_v3v3(r_dPdu, r_dPdv)) { subdiv->evaluator->evaluateLimit(subdiv->evaluator, ptex_face_index, diff --git a/source/blender/blenlib/BLI_ghash.h b/source/blender/blenlib/BLI_ghash.h index bc517f81955..fcf9ca61200 100644 --- a/source/blender/blenlib/BLI_ghash.h +++ b/source/blender/blenlib/BLI_ghash.h @@ -188,6 +188,49 @@ BLI_INLINE bool BLI_ghashIterator_done(GHashIterator *ghi) typedef struct GSet GSet; +typedef struct TableGSet { + struct GHash *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/intern/BLI_ghash_utils.c b/source/blender/blenlib/intern/BLI_ghash_utils.c index 83f64043cd0..b64659fca3f 100644 --- a/source/blender/blenlib/intern/BLI_ghash_utils.c +++ b/source/blender/blenlib/intern/BLI_ghash_utils.c @@ -288,4 +288,125 @@ GSet *BLI_gset_int_new(const char *info) return BLI_gset_int_new_ex(info, 0); } + +TableGSet *BLI_table_gset_new(const char *info) +{ + TableGSet *ts = MEM_callocN(sizeof(TableGSet), info); + + ts->ptr_to_idx = BLI_ghash_ptr_new(info); + + return ts; +} + +TableGSet *BLI_table_gset_new_ex(const char *info, int size) +{ + TableGSet *ts = MEM_callocN(sizeof(TableGSet), info); + + ts->ptr_to_idx = BLI_ghash_ptr_new_ex(info, size); + if (size) { + ts->elems = MEM_callocN(sizeof(void*)*size, info); + ts->size = size; + ts->length = 0; + ts->cur = 0; + } + + return ts; +} + +void BLI_table_gset_free(TableGSet *ts, GHashKeyFreeFP freefp) +{ + if (!ts->ptr_to_idx) { + printf("double call to BLI_table_gset_free!\n"); + return; + } + + if (ts->elems) { + MEM_freeN(ts->elems); + } + + BLI_ghash_free(ts->ptr_to_idx, freefp, NULL); + ts->ptr_to_idx = NULL; + + MEM_freeN(ts); +} + +bool BLI_table_gset_add(TableGSet *ts, void *elem) +{ + if (BLI_table_gset_haskey(ts, elem)) { + return true; + } + + BLI_table_gset_insert(ts, elem); + return false; +} + +void BLI_table_gset_insert(TableGSet *ts, void *elem) +{ + if (ts->cur >= ts->size) { + int newsize = (ts->cur + 1); + newsize = (newsize << 1) - (newsize >> 1); + newsize = MAX2(newsize, 8); + + 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_ghash_clear(ts->ptr_to_idx, NULL, NULL); + + // compact + int i = 0, j = 0; + for (i = 0; i < ts->cur; i++) { + void *elem2 = ts->elems[i]; + + if (elem2) { + BLI_ghash_insert(ts->ptr_to_idx, elem2, (void *)j); + ts->elems[j++] = elem2; + } + } + + ts->size = newsize; + ts->cur = j; + } + + BLI_ghash_insert(ts->ptr_to_idx, 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_ghash_lookup_p(ts->ptr_to_idx, elem); + if (!idx) { + return; + } + + int idx2 = *idx; + + BLI_ghash_remove(ts->ptr_to_idx, elem, freefp, NULL); + + 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_ghash_haskey(ts->ptr_to_idx, elem); +} + +int BLI_table_gset_len(TableGSet *ts) +{ + return ts->length; +} + /** \} */ diff --git a/source/blender/blenlib/intern/BLI_mempool.c b/source/blender/blenlib/intern/BLI_mempool.c index e5868abbe2c..461f867d861 100644 --- a/source/blender/blenlib/intern/BLI_mempool.c +++ b/source/blender/blenlib/intern/BLI_mempool.c @@ -37,6 +37,7 @@ #include "BLI_utildefines.h" #include "BLI_mempool.h" /* own include */ +#include "BLI_asan.h" #include "MEM_guardedalloc.h" @@ -223,13 +224,18 @@ static BLI_freenode *mempool_chunk_add(BLI_mempool *pool, while (j--) { curnode->next = NODE_STEP_NEXT(curnode); curnode->freeword = FREEWORD; - curnode = curnode->next; + + BLI_freenode *next = 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 = curnode->next; + ////BLI_asan_poison(curnode, pool->esize); + curnode = next; } } @@ -250,18 +256,20 @@ static BLI_freenode *mempool_chunk_add(BLI_mempool *pool, return curnode; } -static void mempool_chunk_free(BLI_mempool_chunk *mpchunk) +static void mempool_chunk_free(BLI_mempool *pool, BLI_mempool_chunk *mpchunk) { + //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 *pool, BLI_mempool_chunk *mpchunk) { BLI_mempool_chunk *mpchunk_next; for (; mpchunk; mpchunk = mpchunk_next) { mpchunk_next = mpchunk->next; - mempool_chunk_free(mpchunk); + mempool_chunk_free(pool, mpchunk); } } @@ -343,6 +351,8 @@ void *BLI_mempool_alloc(BLI_mempool *pool) free_pop = pool->free; + //BLI_asan_unpoison(free_pop, pool->esize); + BLI_assert(pool->chunk_tail->next == NULL); if (pool->flag & BLI_MEMPOOL_ALLOW_ITER) { @@ -404,7 +414,9 @@ void BLI_mempool_free(BLI_mempool *pool, void *addr) newhead->freeword = FREEWORD; } + newhead->next = pool->free; + pool->free = newhead; pool->totused--; @@ -421,7 +433,7 @@ 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(pool, first->next); first->next = NULL; pool->chunk_tail = first; @@ -439,15 +451,20 @@ void BLI_mempool_free(BLI_mempool *pool, void *addr) j = pool->pchunk; while (j--) { - curnode->next = NODE_STEP_NEXT(curnode); - curnode = curnode->next; + BLI_freenode *next = NODE_STEP_NEXT(curnode); + curnode->next = next; + //BLI_asan_poison(curnode, pool->esize); + curnode = next; } curnode = NODE_STEP_PREV(curnode); curnode->next = NULL; /* terminate the list */ + // BLI_asan_poison(newhead, pool->esize); #ifdef WITH_MEM_VALGRIND VALGRIND_MEMPOOL_FREE(pool, CHUNK_DATA(first)); #endif + } else { + //BLI_asan_poison(newhead, pool->esize); } } @@ -722,7 +739,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(pool, mpchunk); } while ((mpchunk = mpchunk_next)); } @@ -756,7 +773,7 @@ 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, pool->chunks); #ifdef WITH_MEM_VALGRIND VALGRIND_DESTROY_MEMPOOL(pool); diff --git a/source/blender/blenlib/intern/task_pool.cc b/source/blender/blenlib/intern/task_pool.cc index 00ba659a9c8..1c48bcf9af4 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_290.c b/source/blender/blenloader/intern/versioning_290.c index 5c8e9da41c4..f1245a31c70 100644 --- a/source/blender/blenloader/intern/versioning_290.c +++ b/source/blender/blenloader/intern/versioning_290.c @@ -1358,6 +1358,16 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain) } } + if (!MAIN_VERSION_ATLEAST(bmain, 293, 0)) { + for (Brush *br = bmain->brushes.first; br; br = br->id.next) { + if (br->sculpt_tool == SCULPT_TOOL_VCOL_BOUNDARY) { + if (br->vcol_boundary_exponent == 0.0f) { + br->vcol_boundary_exponent = 1.0f; + } + } + } + } + if (!MAIN_VERSION_ATLEAST(bmain, 292, 5)) { /* Initialize the opacity of the overlay wireframe */ if (!DNA_struct_elem_find(fd->filesdna, "View3DOverlay", "float", "wireframe_opacity")) { diff --git a/source/blender/blenloader/intern/versioning_defaults.c b/source/blender/blenloader/intern/versioning_defaults.c index 52efeb1e822..b2b93302512 100644 --- a/source/blender/blenloader/intern/versioning_defaults.c +++ b/source/blender/blenloader/intern/versioning_defaults.c @@ -699,6 +699,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/bmesh_class.h b/source/blender/bmesh/bmesh_class.h index c83fc0645c4..f61921cd61c 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 * @@ -376,6 +378,9 @@ typedef struct BMesh { * instead of crashing on invalid memory access. */ void *py_handle; + MultiresModifierData multires; //copy of multires settings + bool haveMultiResSettings; + int multiresSpace; } BMesh; /** #BMHeader.htype (char) */ diff --git a/source/blender/bmesh/intern/bmesh_interp.c b/source/blender/bmesh/intern/bmesh_interp.c index 6857a1f3929..48a19f6f01c 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" @@ -314,17 +316,52 @@ static bool quad_co(const float v1[3], /* rotate */ poly_rotate_plane(n, projverts, 5); + float projverts2[4][3]; + /* subtract origin */ for (i = 0; i < 4; i++) { sub_v2_v2(projverts[i], projverts[4]); + + copy_v3_v3(projverts2[i], projverts[i]); } - if (!isect_point_quad_v2(origin, projverts[0], projverts[1], projverts[2], projverts[3])) { + // expand quad a bit +#if 0 + float eps = FLT_EPSILON * 40000; + float c[3]; + + mid_v3_v3v3v3v3(c, projverts[0], projverts[1], projverts[2], projverts[3]); + + sub_v3_v3(projverts2[0], c); + sub_v3_v3(projverts2[1], c); + sub_v3_v3(projverts2[2], c); + sub_v3_v3(projverts2[3], c); + mul_v3_fl(projverts2[0], 1.0f + eps); + mul_v3_fl(projverts2[1], 1.0f + eps); + mul_v3_fl(projverts2[2], 1.0f + eps); + mul_v3_fl(projverts2[3], 1.0f + eps); + add_v3_v3(projverts2[0], c); + add_v3_v3(projverts2[1], c); + add_v3_v3(projverts2[2], c); + add_v3_v3(projverts2[3], c); +#endif + + if (!isect_point_quad_v2(origin, projverts2[0], projverts2[1], projverts2[2], projverts2[3])) { return false; } resolve_quad_uv_v2(r_uv, origin, projverts[0], projverts[3], projverts[2], projverts[1]); +#if 0 + float eps2 = FLT_EPSILON * 4000; + if (r_uv[0] < -eps2 || r_uv[1] < -eps2 || r_uv[0] > 1.0 + eps2 || r_uv[1] > 1.0 + eps2) { + return false; + } +#endif + + CLAMP(r_uv[0], 0.0f, 0.99999f); + CLAMP(r_uv[1], 0.0f, 0.99999f); + return true; } @@ -357,8 +394,7 @@ static bool mdisp_in_mdispquad(BMLoop *l_src, float r_axis_y[3], float r_uv[2]) { - float v1[3], v2[3], c[3], v3[3], v4[3], e1[3], e2[3]; - float eps = FLT_EPSILON * 4000; + float v1[3], v2[3], v3[3], v4[3], e1[3], e2[3]; if (is_zero_v3(l_src->v->no)) { BM_vert_normal_update_all(l_src->v); @@ -370,6 +406,8 @@ static bool mdisp_in_mdispquad(BMLoop *l_src, compute_mdisp_quad(l_dst, l_dst_f_center, v1, v2, v3, v4, e1, e2); /* expand quad a bit */ + float c[3]; + float eps = FLT_EPSILON * 400; mid_v3_v3v3v3v3(c, v1, v2, v3, v4); sub_v3_v3(v1, c); @@ -451,8 +489,10 @@ typedef struct BMLoopInterpMultiresData { BMLoop *l_src_first; int cd_loop_mdisp_offset; + int space; MDisps *md_dst; const float *f_src_center; + const float *f_dst_center; float *axis_x, *axis_y; float *v1, *v4; @@ -471,6 +511,7 @@ static void loop_interp_multires_cb(void *__restrict userdata, BMLoop *l_first = data->l_src_first; BMLoop *l_dst = data->l_dst; const int cd_loop_mdisp_offset = data->cd_loop_mdisp_offset; + int space = data->space; MDisps *md_dst = data->md_dst; const float *f_src_center = data->f_src_center; @@ -485,6 +526,19 @@ static void loop_interp_multires_cb(void *__restrict userdata, const int res = data->res; const float d = data->d; + float quad[4][3]; + + float n1[3], n2[3]; + normal_tri_v3(n1, l_dst->v->co, l_dst->next->v->co, data->f_dst_center); + + if (space == MULTIRES_SPACE_ABSOLUTE) { + BMLoop *l = l_dst; + + copy_v3_v3(quad[0], data->f_dst_center); + interp_v3_v3v3(quad[1], l->v->co, l->next->v->co, 0.5); + copy_v3_v3(quad[2], l->v->co); + interp_v3_v3v3(quad[3], l->v->co, l->prev->v->co, 0.5); + } float x = d * ix, y; int iy; @@ -496,24 +550,71 @@ static void loop_interp_multires_cb(void *__restrict userdata, madd_v3_v3v3fl(co2, v4, e2, y); interp_v3_v3v3(co, co1, co2, x); + float sum[3]; + int tot = 0; + zero_v3(sum); + float mindis = 1e17; + + float baseco[3]; + if (space == MULTIRES_SPACE_ABSOLUTE) { + interp_bilinear_quad_v3(quad, x, y, baseco); + } + do { MDisps *md_src; float src_axis_x[3], src_axis_y[3]; float uv[2]; + normal_tri_v3(n2, l_iter->v->co, l_iter->next->v->co, data->f_src_center); + float th = dot_v3v3(n1, n2); + if (th < 0.0f) { + negate_v3(n2); + } + + th = acos(dot_v3v3(n1, n2) * 0.999999f); + if (th > M_PI * 0.1) { + continue; + } + md_src = BM_ELEM_CD_GET_VOID_P(l_iter, cd_loop_mdisp_offset); if (mdisp_in_mdispquad(l_dst, l_iter, f_src_center, co, res, src_axis_x, src_axis_y, uv)) { - old_mdisps_bilinear(md_dst->disps[iy * res + ix], md_src->disps, res, uv[0], uv[1]); - bm_loop_flip_disp(src_axis_x, src_axis_y, axis_x, axis_y, md_dst->disps[iy * res + ix]); + float disp[3]; + copy_v3_v3(disp, md_dst->disps[iy * res + ix]); + + old_mdisps_bilinear(disp, md_src->disps, res, uv[0], uv[1]); - break; + if (space == MULTIRES_SPACE_TANGENT) { + bm_loop_flip_disp(src_axis_x, src_axis_y, axis_x, axis_y, disp); + } + + float l = len_v3v3(disp, baseco); + if (l < mindis) { + mindis = l; + // tot++; + // copy_v3_v3(sum, disp); + } + add_v3_v3(sum, disp); + tot++; + // break; } } while ((l_iter = l_iter->next) != l_first); + + if (tot) { + mul_v3_fl(sum, 1.0 / (float)tot); + copy_v3_v3(md_dst->disps[iy * res + ix], sum); + } + else { + // printf("failed to set disp: %f %f\n", x, y); + if (space == MULTIRES_SPACE_ABSOLUTE) { + // copy_v3_v3(md_dst->disps[iy * res + ix], baseco); + // copy_v3_v3(md_dst->disps[iy * res + ix], baseco); + } + } } } -void BM_loop_interp_multires_ex(BMesh *UNUSED(bm), +void BM_loop_interp_multires_ex(BMesh *bm, BMLoop *l_dst, const BMFace *f_src, const float f_dst_center[3], @@ -555,7 +656,9 @@ void BM_loop_interp_multires_ex(BMesh *UNUSED(bm), .cd_loop_mdisp_offset = cd_loop_mdisp_offset, .md_dst = md_dst, .f_src_center = f_src_center, + .f_dst_center = f_dst_center, .axis_x = axis_x, + .space = bm->multiresSpace, .axis_y = axis_y, .v1 = v1, .v4 = v4, @@ -601,6 +704,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 +723,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); + +#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; - copy_v3_v3(mdn->disps[y * sides], co1); - copy_v3_v3(mdl->disps[y], co1); + 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 +1021,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 +1033,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; - add_v3_v3v3(co2, mdl1->disps[o1], mdl2->disps[o2]); - mul_v3_fl(co2, 0.32); + if (l2->v != l->v) { + reverse = true; + l2 = l2->next; + } + + mdl2 = BM_ELEM_CD_GET_VOID_P(l2, cd_loop_mdisp_offset); + y = sides - 1; + + if (!mdl2->disps) { + continue; + } + + for (x = 0; x < sides; x++) { + int x2, y2, o1, o2; + + if (!reverse) { + x2 = sides - 1; + y2 = x; + } + else { + x2 = x; + y2 = y; + } - 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); + } } /** diff --git a/source/blender/bmesh/intern/bmesh_interp.h b/source/blender/bmesh/intern/bmesh_interp.h index c77281bd798..686053fa78b 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,7 @@ struct LinkNode; struct MemArena; +void BM_face_multires_stitch(BMesh *bm, BMFace *f); void BM_loop_interp_multires_ex(BMesh *bm, BMLoop *l_dst, const BMFace *f_src, diff --git a/source/blender/bmesh/intern/bmesh_log.c b/source/blender/bmesh/intern/bmesh_log.c index 40eed6238ca..b20a1407751 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 @@ -35,6 +36,7 @@ #include "BLI_listbase.h" #include "BLI_math.h" #include "BLI_mempool.h" +#include "BLI_threads.h" #include "BLI_utildefines.h" #include "BKE_customdata.h" @@ -45,6 +47,8 @@ #include "BLI_strict_flags.h" +#define CUSTOMDATA + struct BMLogEntry { struct BMLogEntry *next, *prev; @@ -75,6 +79,9 @@ 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; }; struct BMLog { @@ -93,6 +100,10 @@ struct BMLog { GHash *id_to_elem; GHash *elem_to_id; + ThreadRWMutex lock; + + BMesh *bm; + /* All BMLogEntrys, ordered from earliest to most recent */ ListBase entries; @@ -105,6 +116,10 @@ struct BMLog { * entries have been applied (i.e. there is nothing left to redo.) */ BMLogEntry *current_entry; + + int cd_origco_offset; + int cd_origno_offset; + int cd_origvcol_offset; }; typedef struct { @@ -112,10 +127,13 @@ typedef struct { short no[3]; char hflag; float mask; + void *customdata; } BMLogVert; typedef struct { uint v_ids[3]; + void *customdata[3]; + void *customdata_f; char hflag; } BMLogFace; @@ -125,11 +143,75 @@ typedef struct { #define logkey_hash BLI_ghashutil_inthash_p_simple #define logkey_cmp BLI_ghashutil_intcmp +static void *log_ghash_lookup(BMLog *entry, GHash *gh, const void *key) +{ + BLI_rw_mutex_lock(&entry->lock, THREAD_LOCK_READ); + void *ret = BLI_ghash_lookup(gh, key); + BLI_rw_mutex_unlock(&entry->lock); + + return ret; +} + +// this is not 100% threadsafe +static void **log_ghash_lookup_p(BMLog *entry, GHash *gh, const void *key) +{ + BLI_rw_mutex_lock(&entry->lock, THREAD_LOCK_READ); + void **ret = BLI_ghash_lookup_p(gh, key); + BLI_rw_mutex_unlock(&entry->lock); + + return ret; +} + +static void log_ghash_insert(BMLog *entry, GHash *gh, void *key, void *val) +{ + BLI_rw_mutex_lock(&entry->lock, THREAD_LOCK_WRITE); + BLI_ghash_insert(gh, key, val); + BLI_rw_mutex_unlock(&entry->lock); +} + +static bool log_ghash_remove( + BMLog *entry, GHash *gh, const void *key, GHashKeyFreeFP keyfree, GHashValFreeFP valfree) +{ + BLI_rw_mutex_lock(&entry->lock, THREAD_LOCK_WRITE); + bool ret = BLI_ghash_remove(gh, key, keyfree, valfree); + BLI_rw_mutex_unlock(&entry->lock); + + return ret; +} + +static bool log_ghash_reinsert( + BMLog *entry, GHash *gh, void *key, void *val, GHashKeyFreeFP keyfree, GHashValFreeFP valfree) +{ + BLI_rw_mutex_lock(&entry->lock, THREAD_LOCK_WRITE); + bool ret = BLI_ghash_reinsert(gh, key, val, keyfree, valfree); + BLI_rw_mutex_unlock(&entry->lock); + + return ret; +} + +static bool log_ghash_haskey(BMLog *entry, GHash *gh, const void *key) +{ + BLI_rw_mutex_lock(&entry->lock, THREAD_LOCK_READ); + bool ret = BLI_ghash_haskey(gh, key); + BLI_rw_mutex_unlock(&entry->lock); + + return ret; +} + +static bool log_ghash_ensure_p(BMLog *entry, GHash *gh, void *key, void ***val) +{ + BLI_rw_mutex_lock(&entry->lock, THREAD_LOCK_WRITE); + bool ret = BLI_ghash_ensure_p(gh, key, val); + BLI_rw_mutex_unlock(&entry->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)); + BLI_assert(log_ghash_haskey(log, log->elem_to_id, v)); + return POINTER_AS_UINT(log_ghash_lookup(log, log->elem_to_id, v)); } /* Set the vertex's unique ID in the log */ @@ -137,23 +219,23 @@ static void bm_log_vert_id_set(BMLog *log, BMVert *v, uint id) { void *vid = POINTER_FROM_UINT(id); - BLI_ghash_reinsert(log->id_to_elem, vid, v, NULL, NULL); - BLI_ghash_reinsert(log->elem_to_id, v, vid, NULL, NULL); + log_ghash_reinsert(log, log->id_to_elem, vid, v, NULL, NULL); + log_ghash_reinsert(log, log->elem_to_id, v, vid, NULL, NULL); } /* Get a vertex from its unique ID */ static BMVert *bm_log_vert_from_id(BMLog *log, uint id) { void *key = POINTER_FROM_UINT(id); - BLI_assert(BLI_ghash_haskey(log->id_to_elem, key)); - return BLI_ghash_lookup(log->id_to_elem, key); + BLI_assert(log_ghash_haskey(log, log->id_to_elem, key)); + return log_ghash_lookup(log, log->id_to_elem, key); } /* Get the face's unique ID from the log */ static uint bm_log_face_id_get(BMLog *log, BMFace *f) { - BLI_assert(BLI_ghash_haskey(log->elem_to_id, f)); - return POINTER_AS_UINT(BLI_ghash_lookup(log->elem_to_id, f)); + BLI_assert(log_ghash_haskey(log, log->elem_to_id, f)); + return POINTER_AS_UINT(log_ghash_lookup(log, log->elem_to_id, f)); } /* Set the face's unique ID in the log */ @@ -161,20 +243,74 @@ static void bm_log_face_id_set(BMLog *log, BMFace *f, uint id) { void *fid = POINTER_FROM_UINT(id); - BLI_ghash_reinsert(log->id_to_elem, fid, f, NULL, NULL); - BLI_ghash_reinsert(log->elem_to_id, f, fid, NULL, NULL); + log_ghash_reinsert(log, log->id_to_elem, fid, f, NULL, NULL); + log_ghash_reinsert(log, log->elem_to_id, f, fid, NULL, NULL); } /* Get a face from its unique ID */ static BMFace *bm_log_face_from_id(BMLog *log, uint id) { void *key = POINTER_FROM_UINT(id); - BLI_assert(BLI_ghash_haskey(log->id_to_elem, key)); - return BLI_ghash_lookup(log->id_to_elem, key); + BLI_assert(log_ghash_haskey(log, log->id_to_elem, key)); + return log_ghash_lookup(log, log->id_to_elem, key); } /************************ BMLogVert / BMLogFace ***********************/ +static void bm_log_vert_customdata(BMesh *bm, BMLog *log, BMVert *v, BMLogVert *lv) +{ +#ifdef CUSTOMDATA + // if (!lv) { + // return; + //} + BMLogEntry *entry = log->current_entry; + + if (!entry) { + return; + } + + if (lv->customdata) { + BLI_mempool_free(entry->vdata.pool, lv->customdata); + lv->customdata = NULL; + } + + CustomData_bmesh_copy_data(&bm->vdata, &entry->vdata, v->head.data, &lv->customdata); +#endif +} + +static void bm_log_face_customdata(BMesh *bm, BMLog *log, BMFace *f, BMLogFace *lf) +{ +#ifdef CUSTOMDATA + 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); + + BMLoop *l1 = f->l_first; + BMLoop *l2 = f->l_first->next; + BMLoop *l3 = f->l_first->prev; + BMLoop *ls[3] = {l1, l2, l3}; + + for (int i = 0; i < 3; i++) { + 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, ls[i]->head.data, &lf->customdata[i]); + } +#endif +} + /* Get a vertex's paint-mask value * * Returns zero if no paint-mask layer is present */ @@ -197,12 +333,17 @@ static void vert_mask_set(BMVert *v, const float new_mask, const int cd_vert_mas } /* 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, + BMLogVert *lv, + BMVert *v, + const int cd_vert_mask_offset) { 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); lv->hflag = v->head.hflag; + + bm_log_vert_customdata(log->bm, log, v, lv); } /* Allocate and initialize a BMLogVert */ @@ -210,8 +351,9 @@ static BMLogVert *bm_log_vert_alloc(BMLog *log, BMVert *v, const int cd_vert_mas { 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, lv, v, cd_vert_mask_offset); return lv; } @@ -223,6 +365,7 @@ static BMLogFace *bm_log_face_alloc(BMLog *log, BMFace *f) BMLogFace *lf = BLI_mempool_alloc(entry->pool_faces); BMVert *v[3]; + lf->customdata_f = lf->customdata[0] = lf->customdata[1] = lf->customdata[2] = NULL; BLI_assert(f->len == 3); // BM_iter_as_array(NULL, BM_VERTS_OF_FACE, f, (void **)v, 3); @@ -251,7 +394,7 @@ static void bm_log_verts_unmake(BMesh *bm, BMLog *log, GHash *verts) /* 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_vert_bmvert_copy(log, lv, v, cd_vert_mask_offset); BM_vert_kill(bm, v); } @@ -264,6 +407,12 @@ static void bm_log_faces_unmake(BMesh *bm, BMLog *log, GHash *faces) void *key = BLI_ghashIterator_getKey(&gh_iter); uint id = POINTER_AS_UINT(key); BMFace *f = bm_log_face_from_id(log, id); + + if (!f) { + printf("dyntopo error in %s\n", __func__); + continue; + } + BMEdge *e_tri[3]; BMLoop *l_iter; int i; @@ -283,9 +432,10 @@ static void bm_log_faces_unmake(BMesh *bm, BMLog *log, GHash *faces) } } -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) { const int cd_vert_mask_offset = CustomData_get_offset(&bm->vdata, CD_PAINT_MASK); + int cd_vcol_offset = CustomData_get_offset(&bm->vdata, CD_PROP_COLOR); GHashIterator gh_iter; GHASH_ITER (gh_iter, verts) { @@ -296,10 +446,32 @@ static void bm_log_verts_restore(BMesh *bm, BMLog *log, GHash *verts) 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)); + +#ifdef CUSTOMDATA + if (lv->customdata) { + CustomData_bmesh_copy_data(&entry->vdata, &bm->vdata, lv->customdata, &v->head.data); + } +#endif + + if (log->cd_origco_offset >= 0) { + float *oco = BM_ELEM_CD_GET_VOID_P(v, log->cd_origco_offset); + copy_v3_v3(oco, v->co); + } + + if (log->cd_origno_offset >= 0) { + float *ono = BM_ELEM_CD_GET_VOID_P(v, log->cd_origno_offset); + copy_v3_v3(ono, v->no); + } + + if (log->cd_origvcol_offset >= 0) { + float *ocolor = BM_ELEM_CD_GET_VOID_P(v, log->cd_origvcol_offset); + float *color = BM_ELEM_CD_GET_VOID_P(v, cd_vcol_offset); + copy_v3_v3(ocolor, color); + } } } -static void bm_log_faces_restore(BMesh *bm, BMLog *log, GHash *faces) +static void bm_log_faces_restore(BMesh *bm, BMLog *log, GHash *faces, BMLogEntry *entry) { GHashIterator gh_iter; GHASH_ITER (gh_iter, faces) { @@ -315,13 +487,30 @@ static void bm_log_faces_restore(BMesh *bm, BMLog *log, GHash *faces) f = BM_face_create_verts(bm, v, 3, NULL, BM_CREATE_NOP, true); f->head.hflag = lf->hflag; bm_log_face_id_set(log, f, POINTER_AS_UINT(key)); + +#ifdef CUSTOMDATA + if (lf->customdata_f) { + CustomData_bmesh_copy_data(&entry->pdata, &bm->pdata, lf->customdata_f, &f->head.data); + } + + BMLoop *ls[3] = {f->l_first, f->l_first->next, f->l_first->prev}; + + for (int i = 0; i < 3; i++) { + if (lf->customdata[i]) { + CustomData_bmesh_copy_data( + &entry->ldata, &bm->ldata, lf->customdata[i], &ls[i]->head.data); + } + } +#endif } } -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) { const int cd_vert_mask_offset = CustomData_get_offset(&bm->vdata, CD_PAINT_MASK); + int cd_vcol_offset = CustomData_get_offset(&bm->vdata, CD_PROP_COLOR); + GHashIterator gh_iter; GHASH_ITER (gh_iter, verts) { void *key = BLI_ghashIterator_getKey(&gh_iter); @@ -339,10 +528,32 @@ static void bm_log_vert_values_swap(BMesh *bm, BMLog *log, GHash *verts) mask = lv->mask; lv->mask = vert_mask_get(v, cd_vert_mask_offset); vert_mask_set(v, mask, cd_vert_mask_offset); + +#ifdef CUSTOMDATA + if (lv->customdata) { + CustomData_bmesh_copy_data(&entry->vdata, &bm->vdata, lv->customdata, &v->head.data); + } +#endif + + if (log->cd_origco_offset >= 0) { + float *oco = BM_ELEM_CD_GET_VOID_P(v, log->cd_origco_offset); + copy_v3_v3(oco, v->co); + } + + if (log->cd_origno_offset >= 0) { + float *ono = BM_ELEM_CD_GET_VOID_P(v, log->cd_origno_offset); + copy_v3_v3(ono, v->no); + } + + if (log->cd_origvcol_offset >= 0) { + float *ocolor = BM_ELEM_CD_GET_VOID_P(v, log->cd_origvcol_offset); + float *color = BM_ELEM_CD_GET_VOID_P(v, cd_vcol_offset); + copy_v3_v3(ocolor, color); + } } } -static void bm_log_face_values_swap(BMLog *log, GHash *faces) +static void bm_log_face_values_swap(BMLog *log, GHash *faces, BMLogEntry *entry) { GHashIterator gh_iter; GHASH_ITER (gh_iter, faces) { @@ -352,6 +563,22 @@ static void bm_log_face_values_swap(BMLog *log, GHash *faces) BMFace *f = bm_log_face_from_id(log, id); SWAP(char, f->head.hflag, lf->hflag); + +#ifdef CUSTOMDATA + if (lf->customdata_f) { + CustomData_bmesh_copy_data(&entry->pdata, &log->bm->pdata, lf->customdata_f, &f->head.data); + } + + BMLoop *ls[3] = {f->l_first, f->l_first->next, f->l_first->prev}; + + for (int i = 0; i < 3; i++) { + if (lf->customdata[i]) { + CustomData_bmesh_copy_data( + &entry->ldata, &log->bm->ldata, lf->customdata[i], &ls[i]->head.data); + } + } +#endif + } } @@ -409,6 +636,24 @@ static void bm_log_entry_free(BMLogEntry *entry) BLI_mempool_destroy(entry->pool_verts); BLI_mempool_destroy(entry->pool_faces); + + 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); + } + + CustomData_free(&entry->vdata, 0); + CustomData_free(&entry->edata, 0); + CustomData_free(&entry->ldata, 0); + CustomData_free(&entry->pdata, 0); } static void bm_log_id_ghash_retake(RangeTreeUInt *unused_ids, GHash *id_ghash) @@ -468,16 +713,30 @@ static void bm_log_id_ghash_release(BMLog *log, GHash *id_ghash) /***************************** Public API *****************************/ +void BM_log_set_cd_offsets(BMLog *log, + int cd_origco_offset, + int cd_origno_offset, + int cd_origvol_offset) +{ + log->cd_origco_offset = cd_origco_offset; + log->cd_origno_offset = cd_origno_offset; + log->cd_origvcol_offset = cd_origvol_offset; +} + /* Allocate, initialize, and assign a new BMLog */ -BMLog *BM_log_create(BMesh *bm) +BMLog *BM_log_create(BMesh *bm, int cd_origco_offset, int cd_origno_offset, int cd_origvcol_offset) { BMLog *log = MEM_callocN(sizeof(*log), __func__); const uint reserve_num = (uint)(bm->totvert + bm->totface); + BLI_rw_mutex_init(&log->lock); + 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); + BM_log_set_cd_offsets(log, cd_origco_offset, cd_origno_offset, cd_origvcol_offset); + /* Assign IDs to all existing vertices and faces */ bm_log_assign_ids(bm, log); @@ -516,7 +775,7 @@ 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, -1, -1); if (entry->prev) { log->current_entry = entry; @@ -560,6 +819,8 @@ void BM_log_free(BMLog *log) { BMLogEntry *entry; + BLI_rw_mutex_end(&log->lock); + if (log->unused_ids) { range_tree_uint_free(log->unused_ids); } @@ -618,7 +879,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,7 +889,7 @@ 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); @@ -639,6 +900,35 @@ void BM_log_mesh_elems_reorder(BMesh *bm, BMLog *log) MEM_freeN(farr); } +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); + } + +#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 * * If the log entry list is empty, or if the current log entry is the @@ -649,8 +939,15 @@ 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) { + log->bm = bm; + /* WARNING: this is now handled by the UndoSystem: BKE_UNDOSYS_TYPE_SCULPT * freeing here causes unnecessary complications. */ BMLogEntry *entry; @@ -673,6 +970,25 @@ BMLogEntry *BM_log_entry_add(BMLog *log) entry->log = log; log->current_entry = entry; +#ifdef CUSTOMDATA + if (combine_with_last) { + if (log->current_entry) { + log->current_entry->combined_next = entry; + } + entry->combined_prev = log->current_entry; + } + + 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); +#endif + return entry; } @@ -758,6 +1074,9 @@ void BM_log_undo(BMesh *bm, BMLog *log) { BMLogEntry *entry = log->current_entry; + bm->elem_index_dirty |= BM_VERT | BM_EDGE | BM_FACE; + bm->elem_table_dirty |= BM_VERT | BM_EDGE | BM_FACE; + if (entry) { log->current_entry = entry->prev; @@ -766,12 +1085,12 @@ void BM_log_undo(BMesh *bm, BMLog *log) bm_log_verts_unmake(bm, log, entry->added_verts); /* Restore deleted verts and faces */ - bm_log_verts_restore(bm, log, entry->deleted_verts); - bm_log_faces_restore(bm, log, entry->deleted_faces); + bm_log_verts_restore(bm, log, entry->deleted_verts, entry); + bm_log_faces_restore(bm, log, entry->deleted_faces, entry); /* 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); + bm_log_vert_values_swap(bm, log, entry->modified_verts, entry); + bm_log_face_values_swap(log, entry->modified_faces, entry); } } @@ -782,6 +1101,9 @@ void BM_log_redo(BMesh *bm, BMLog *log) { BMLogEntry *entry = log->current_entry; + bm->elem_index_dirty |= BM_VERT | BM_EDGE | BM_FACE; + bm->elem_table_dirty |= BM_VERT | BM_EDGE | BM_FACE; + if (!entry) { /* Currently at the beginning of the undo stack, move to first entry */ entry = log->entries.first; @@ -803,12 +1125,12 @@ void BM_log_redo(BMesh *bm, BMLog *log) bm_log_verts_unmake(bm, log, entry->deleted_verts); /* Restore previously added verts and faces */ - bm_log_verts_restore(bm, log, entry->added_verts); - bm_log_faces_restore(bm, log, entry->added_faces); + bm_log_verts_restore(bm, log, entry->added_verts, entry); + bm_log_faces_restore(bm, log, entry->added_faces, entry); /* 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); + bm_log_vert_values_swap(bm, log, entry->modified_verts, entry); + bm_log_face_values_swap(log, entry->modified_faces, entry); } } @@ -835,7 +1157,10 @@ 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; @@ -844,13 +1169,17 @@ void BM_log_vert_before_modified(BMLog *log, BMVert *v, const int cd_vert_mask_o void **val_p; /* Find or create the BMLogVert entry */ - if ((lv = BLI_ghash_lookup(entry->added_verts, key))) { - bm_log_vert_bmvert_copy(lv, v, cd_vert_mask_offset); + if ((lv = log_ghash_lookup(log, entry->added_verts, key))) { + bm_log_vert_bmvert_copy(log, lv, v, cd_vert_mask_offset); } - else if (!BLI_ghash_ensure_p(entry->modified_verts, key, &val_p)) { + else if (!log_ghash_ensure_p(log, entry->modified_verts, key, &val_p)) { lv = bm_log_vert_alloc(log, v, cd_vert_mask_offset); *val_p = lv; } + + if (lv && log_customdata) { + bm_log_vert_customdata(log->bm, log, v, lv); + } } /* Log a new vertex as added to the BMesh @@ -867,7 +1196,9 @@ void BM_log_vert_added(BMLog *log, BMVert *v, const int cd_vert_mask_offset) 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); + log_ghash_insert(log, log->current_entry->added_verts, key, lv); + + // bm_log_vert_customdata(log->bm, log, v, lv); } /* Log a face before it is modified @@ -882,7 +1213,9 @@ void BM_log_face_modified(BMLog *log, BMFace *f) void *key = POINTER_FROM_UINT(f_id); lf = bm_log_face_alloc(log, f); - BLI_ghash_insert(log->current_entry->modified_faces, key, lf); + 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 @@ -902,7 +1235,9 @@ void BM_log_face_added(BMLog *log, BMFace *f) 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 @@ -928,23 +1263,27 @@ void BM_log_vert_removed(BMLog *log, BMVert *v, const int cd_vert_mask_offset) 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)); + BLI_assert(!!log_ghash_lookup(log, entry->added_verts, key) == + !!log_ghash_haskey(log, entry->added_verts, key)); - if (BLI_ghash_remove(entry->added_verts, key, NULL, NULL)) { + if (log_ghash_remove(log, entry->added_verts, key, NULL, NULL)) { range_tree_uint_release(log->unused_ids, v_id); } else { BMLogVert *lv, *lv_mod; lv = bm_log_vert_alloc(log, v, cd_vert_mask_offset); - BLI_ghash_insert(entry->deleted_verts, key, lv); + 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))) { (*lv) = (*lv_mod); - BLI_ghash_remove(entry->modified_verts, key, NULL, NULL); + log_ghash_remove(log, entry->modified_verts, key, NULL, NULL); + } + + if (lv) { + bm_log_vert_customdata(log->bm, log, v, lv); } } } @@ -969,17 +1308,21 @@ void BM_log_face_removed(BMLog *log, BMFace *f) void *key = POINTER_FROM_UINT(f_id); /* if it has a key, it shouldn't be NULL */ - BLI_assert(!!BLI_ghash_lookup(entry->added_faces, key) == - !!BLI_ghash_haskey(entry->added_faces, key)); + 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)) { + if (log_ghash_remove(log, entry->added_faces, key, NULL, NULL)) { range_tree_uint_release(log->unused_ids, f_id); } else { BMLogFace *lf; lf = bm_log_face_alloc(log, f); - BLI_ghash_insert(entry->deleted_faces, key, lf); + log_ghash_insert(log, entry->deleted_faces, key, lf); + + if (lf) { + bm_log_face_customdata(log->bm, log, f, lf); + } } } @@ -1042,9 +1385,9 @@ const float *BM_log_original_vert_co(BMLog *log, BMVert *v) 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; } @@ -1060,9 +1403,9 @@ const short *BM_log_original_vert_no(BMLog *log, BMVert *v) 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; } @@ -1078,9 +1421,9 @@ float BM_log_original_mask(BMLog *log, BMVert *v) 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->mask; } @@ -1093,9 +1436,9 @@ void BM_log_original_vert_data(BMLog *log, BMVert *v, const float **r_co, const 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; } diff --git a/source/blender/bmesh/intern/bmesh_log.h b/source/blender/bmesh/intern/bmesh_log.h index 5c0ca78bddf..470515db157 100644 --- a/source/blender/bmesh/intern/bmesh_log.h +++ b/source/blender/bmesh/intern/bmesh_log.h @@ -29,7 +29,11 @@ typedef struct BMLog BMLog; typedef struct BMLogEntry BMLogEntry; /* Allocate and initialize a new BMLog */ -BMLog *BM_log_create(BMesh *bm); +BMLog *BM_log_create(BMesh *bm, int cd_origco_offset, int cd_origno_offset, int cd_origvol_offset); +void BM_log_set_cd_offsets(BMLog *log, + int cd_origco_offset, + int cd_origno_offset, + int cd_origvcol_offset); /* Allocate and initialize a new BMLog using existing BMLogEntries */ BMLog *BM_log_from_existing_entries_create(BMesh *bm, BMLogEntry *entry); @@ -44,7 +48,10 @@ 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_entry_check_customdata(BMesh *bm, BMLog *log); /* Mark all used ids as unused for this node */ void BM_log_cleanup_entry(BMLogEntry *entry); @@ -59,7 +66,10 @@ void BM_log_undo(BMesh *bm, BMLog *log); void BM_log_redo(BMesh *bm, BMLog *log); /* 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 a new vertex as added to the BMesh */ void BM_log_vert_added(BMLog *log, struct BMVert *v, const int cd_vert_mask_offset); diff --git a/source/blender/bmesh/intern/bmesh_marking.c b/source/blender/bmesh/intern/bmesh_marking.c index d479a555a58..5c43720f635 100644 --- a/source/blender/bmesh/intern/bmesh_marking.c +++ b/source/blender/bmesh/intern/bmesh_marking.c @@ -73,13 +73,19 @@ static void recount_totsels(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 5e879d41d43..2f17066da25 100644 --- a/source/blender/bmesh/intern/bmesh_mesh.c +++ b/source/blender/bmesh/intern/bmesh_mesh.c @@ -1965,6 +1965,11 @@ void BM_custom_loop_normals_from_vector_layer(BMesh *bm, bool add_sharp_edges) bm->spacearr_dirty &= ~(BM_SPACEARR_DIRTY | BM_SPACEARR_DIRTY_ALL); } +static void bmesh_convert_space(BMesh *bm, MultiResSpace from, MultiResSpace to) +{ + +} + /** * \brief BMesh Begin Edit * @@ -1972,24 +1977,22 @@ void BM_custom_loop_normals_from_vector_layer(BMesh *bm, bool add_sharp_edges) * 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); - + //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 } /** @@ -2000,17 +2003,10 @@ void bmesh_edit_end(BMesh *bm, BMOpTypeFlag type_flag) ListBase select_history; /* BMO_OPTYPE_FLAG_UNTAN_MULTIRES disabled for now, see comment above in bmesh_edit_begin. */ -#ifdef BMOP_UNTAN_MULTIRES_ENABLED /* switch multires data into tangent space */ - if ((flag & BMO_OPTYPE_FLAG_UNTAN_MULTIRES) && CustomData_has_layer(&bm->ldata, CD_MDISPS)) { - /* set normals to their previous winding */ - bmesh_rationalize_normals(bm, 1); - bmesh_mdisps_space_set(bm, MULTIRES_SPACE_ABSOLUTE, MULTIRES_SPACE_TANGENT); + if ((type_flag & BMO_OPTYPE_FLAG_UNTAN_MULTIRES) && CustomData_has_layer(&bm->ldata, CD_MDISPS)) { + BM_enter_multires_space(NULL, bm, MULTIRES_SPACE_TANGENT); } - else if (flag & BMO_OP_FLAG_RATIONALIZE_NORMALS) { - bmesh_rationalize_normals(bm, 1); - } -#endif /* compute normals, clear temp flags and flush selections */ if (type_flag & BMO_OPTYPE_FLAG_NORMALS_CALC) { diff --git a/source/blender/bmesh/intern/bmesh_mesh.h b/source/blender/bmesh/intern/bmesh_mesh.h index c1c2f17d7c1..313b2bca59d 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 MLoopNorSpaceArray; diff --git a/source/blender/bmesh/intern/bmesh_mesh_convert.c b/source/blender/bmesh/intern/bmesh_mesh_convert.c index ba413c3c547..7ab7e9aaa6d 100644 --- a/source/blender/bmesh/intern/bmesh_mesh_convert.c +++ b/source/blender/bmesh/intern/bmesh_mesh_convert.c @@ -176,8 +176,27 @@ static BMFace *bm_face_create_from_mpoly( return BM_face_create(bm, verts, edges, mp->totloop, NULL, BM_CREATE_SKIP_CD); } +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; +} + /** * \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. @@ -186,7 +205,7 @@ static BMFace *bm_face_create_from_mpoly( * * \warning This function doesn't calculate face normals. */ -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 +222,15 @@ 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; + 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); @@ -586,7 +614,7 @@ 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 +623,11 @@ void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *me, const struct BMeshToMesh BMIter iter; int i, j; + //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); diff --git a/source/blender/bmesh/intern/bmesh_mesh_convert.h b/source/blender/bmesh/intern/bmesh_mesh_convert.h index 1b5d001d35d..b2c9037318c 100644 --- a/source/blender/bmesh/intern/bmesh_mesh_convert.h +++ b/source/blender/bmesh/intern/bmesh_mesh_convert.h @@ -43,7 +43,9 @@ struct BMeshFromMeshParams { int active_shapekey; struct CustomData_MeshMasks cd_mask_extra; }; -void BM_mesh_bm_from_me(BMesh *bm, const struct Mesh *me, const struct BMeshFromMeshParams *params) + +struct Object; +void BM_mesh_bm_from_me(struct Object *ob, BMesh *bm, const struct Mesh *me, const struct BMeshFromMeshParams *params) ATTR_NONNULL(1, 3); struct BMeshToMeshParams { @@ -61,7 +63,11 @@ struct BMeshToMeshParams { uint update_shapekey_indices : 1; struct CustomData_MeshMasks cd_mask_extra; }; + +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); diff --git a/source/blender/bmesh/intern/bmesh_opdefines.c b/source/blender/bmesh/intern/bmesh_opdefines.c index bccac006e8e..1bef0ad2650 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 4ae2cc67140..9beeb5c900b 100644 --- a/source/blender/bmesh/intern/bmesh_polygon.c +++ b/source/blender/bmesh/intern/bmesh_polygon.c @@ -1449,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,24 +1467,6 @@ void BM_face_as_array_vert_quad(BMFace *f, BMVert *r_verts[4]) r_verts[3] = l->v; } -/** - * 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: diff --git a/source/blender/bmesh/intern/bmesh_polygon.h b/source/blender/bmesh/intern/bmesh_polygon.h index 8c2b9ee0bff..03cb5ae4bee 100644 --- a/source/blender/bmesh/intern/bmesh_polygon.h +++ b/source/blender/bmesh/intern/bmesh_polygon.h @@ -23,6 +23,7 @@ struct Heap; #include "BLI_compiler_attrs.h" +#include "BLI_compiler_compat.h" void BM_mesh_calc_tessellation(BMesh *bm, BMLoop *(*looptris)[3], int *r_looptris_tot); void BM_mesh_calc_tessellation_beauty(BMesh *bm, BMLoop *(*looptris)[3], int *r_looptris_tot); @@ -105,11 +106,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/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/draw/DRW_engine.h b/source/blender/draw/DRW_engine.h index 2d5b93f4272..f1171201a4e 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/intern/draw_cache.c b/source/blender/draw/intern/draw_cache.c index f902a95a19a..739723b3af0 100644 --- a/source/blender/draw/intern/draw_cache.c +++ b/source/blender/draw/intern/draw_cache.c @@ -37,6 +37,7 @@ #include "BKE_object.h" #include "BKE_paint.h" +#include "BKE_customdata.h" #include "GPU_batch.h" #include "GPU_batch_utils.h" @@ -484,6 +485,40 @@ 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); + + /* UV 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 uv 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/editors/datafiles/CMakeLists.txt b/source/blender/editors/datafiles/CMakeLists.txt index beb22d43930..8b4ded81b34 100644 --- a/source/blender/editors/datafiles/CMakeLists.txt +++ b/source/blender/editors/datafiles/CMakeLists.txt @@ -743,7 +743,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 diff --git a/source/blender/editors/include/UI_icons.h b/source/blender/editors/include/UI_icons.h index f0a4b3c462e..f5c501b8cff 100644 --- a/source/blender/editors/include/UI_icons.h +++ b/source/blender/editors/include/UI_icons.h @@ -919,6 +919,8 @@ 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) /* grease pencil sculpt */ DEF_ICON_COLOR(GPBRUSH_SMOOTH) diff --git a/source/blender/editors/mesh/editmesh_mask_extract.c b/source/blender/editors/mesh/editmesh_mask_extract.c index 7d849c096e7..6353bba1e97 100644 --- a/source/blender/editors/mesh/editmesh_mask_extract.c +++ b/source/blender/editors/mesh/editmesh_mask_extract.c @@ -120,7 +120,7 @@ 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, @@ -513,7 +513,7 @@ 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 +544,7 @@ 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, diff --git a/source/blender/editors/mesh/editmesh_tools.c b/source/blender/editors/mesh/editmesh_tools.c index 68ac2842bab..88151b07fb9 100644 --- a/source/blender/editors/mesh/editmesh_tools.c +++ b/source/blender/editors/mesh/editmesh_tools.c @@ -4208,7 +4208,7 @@ 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; @@ -4274,7 +4274,7 @@ 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; @@ -4456,7 +4456,7 @@ static bool mesh_separate_loose( BM_mesh_elem_hflag_disable_all(bm_old, BM_VERT | BM_EDGE | BM_FACE, BM_ELEM_SELECT, false); if (clear_object_data) { - BM_mesh_bm_to_me(NULL, + BM_mesh_bm_to_me(NULL, base_old->object, bm_old, me_old, (&(struct BMeshToMeshParams){ @@ -4543,7 +4543,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: @@ -4559,6 +4559,7 @@ static int edbm_separate_exec(bContext *C, wmOperator *op) if (retval_iter) { BM_mesh_bm_to_me(bmain, + ob, bm_old, me, (&(struct BMeshToMeshParams){ @@ -5710,7 +5711,7 @@ static void edbm_dissolve_prop__use_boundary_tear(wmOperatorType *ot) "Split off face corners instead of merging faces"); } -static int edbm_dissolve_verts_exec(bContext *C, wmOperator *op) +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"); @@ -5732,10 +5733,116 @@ static int edbm_dissolve_verts_exec(bContext *C, wmOperator *op) if (!EDBM_op_callf(em, op, - "dissolve_verts verts=%hv use_face_split=%b use_boundary_tear=%b", - BM_ELEM_SELECT, - use_face_split, - use_boundary_tear)) { + "test_mres_smooth")) { + continue; + } + + BM_custom_loop_normals_from_vector_layer(em->bm, false); + 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"); + 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, + "dissolve_verts verts=%hv use_face_split=%b use_boundary_tear=%b", + BM_ELEM_SELECT, + use_face_split, + use_boundary_tear)) { continue; } @@ -5748,6 +5855,7 @@ static int edbm_dissolve_verts_exec(bContext *C, wmOperator *op) return OPERATOR_FINISHED; } + void MESH_OT_dissolve_verts(wmOperatorType *ot) { /* identifiers */ diff --git a/source/blender/editors/mesh/editmesh_undo.c b/source/blender/editors/mesh/editmesh_undo.c index 79385e28aa9..d40bd790122 100644 --- a/source/blender/editors/mesh/editmesh_undo.c +++ b/source/blender/editors/mesh/editmesh_undo.c @@ -545,7 +545,7 @@ static void *undomesh_from_editmesh(UndoMesh *um, BMEditMesh *em, Key *key) /* BM_mesh_validate(em->bm); */ /* for troubleshooting */ BM_mesh_bm_to_me( - NULL, + NULL, NULL, em->bm, &um->me, (&(struct BMeshToMeshParams){ @@ -621,7 +621,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){ .calc_face_normal = true, diff --git a/source/blender/editors/mesh/editmesh_utils.c b/source/blender/editors/mesh/editmesh_utils.c index 94f386e08d5..ce77f90c5fe 100644 --- a/source/blender/editors/mesh/editmesh_utils.c +++ b/source/blender/editors/mesh/editmesh_utils.c @@ -352,6 +352,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 21feddfb886..fcef1866e3a 100644 --- a/source/blender/editors/mesh/mesh_intern.h +++ b/source/blender/editors/mesh/mesh_intern.h @@ -259,6 +259,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); @@ -285,3 +286,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 27d73497b49..46c744085cc 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_remesh.c b/source/blender/editors/object/object_remesh.c index 9ef2cce875f..2faa1a7d401 100644 --- a/source/blender/editors/object/object_remesh.c +++ b/source/blender/editors/object/object_remesh.c @@ -754,7 +754,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 = {{0}}; mmd.tolerance = QUADRIFLOW_MIRROR_BISECT_TOLERANCE; @@ -775,8 +775,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(NULL, mesh_bisect_temp); } @@ -844,7 +846,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_to_mesh_nomain( bisect_mesh, diff --git a/source/blender/editors/sculpt_paint/CMakeLists.txt b/source/blender/editors/sculpt_paint/CMakeLists.txt index fff172c0707..e4996e8196b 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 diff --git a/source/blender/editors/sculpt_paint/paint_cursor.c b/source/blender/editors/sculpt_paint/paint_cursor.c index b44f2f66d92..c76fa01816b 100644 --- a/source/blender/editors/sculpt_paint/paint_cursor.c +++ b/source/blender/editors/sculpt_paint/paint_cursor.c @@ -1214,7 +1214,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; @@ -1573,8 +1573,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_mask.c b/source/blender/editors/sculpt_paint/paint_mask.c index 26e2bcc42cf..91c8f9fb969 100644 --- a/source/blender/editors/sculpt_paint/paint_mask.c +++ b/source/blender/editors/sculpt_paint/paint_mask.c @@ -680,7 +680,7 @@ static bool sculpt_gesture_is_effected_lasso(SculptGestureContext *sgcontext, co static bool sculpt_gesture_is_vertex_effected(SculptGestureContext *sgcontext, PBVHVertexIter *vd) { float vertex_normal[3]; - SCULPT_vertex_normal_get(sgcontext->ss, vd->index, vertex_normal); + SCULPT_vertex_normal_get(sgcontext->ss, vd->vertex, vertex_normal); float dot = dot_v3v3(sgcontext->view_normal, vertex_normal); const bool is_effected_front_face = !(sgcontext->front_faces_only && dot < 0.0f); @@ -760,7 +760,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; } } @@ -977,7 +977,7 @@ 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, @@ -1043,7 +1043,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. */ @@ -1229,13 +1229,15 @@ static void sculpt_gesture_apply_trim(SculptGestureContext *sgcontext) .use_toolflags = false, })); - BM_mesh_bm_from_me(bm, + BM_mesh_bm_from_me(NULL, + bm, trim_mesh, &((struct BMeshFromMeshParams){ .calc_face_normal = true, })); - BM_mesh_bm_from_me(bm, + BM_mesh_bm_from_me(NULL, + bm, sculpt_mesh, &((struct BMeshFromMeshParams){ .calc_face_normal = true, diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c index 631327ddfe8..4bb9bcfdc0a 100644 --- a/source/blender/editors/sculpt_paint/sculpt.c +++ b/source/blender/editors/sculpt_paint/sculpt.c @@ -117,7 +117,7 @@ void SCULPT_vertex_random_access_ensure(SculptSession *ss) { - if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) { + if (ss->bm) { BM_mesh_elem_index_ensure(ss->bm, BM_VERT); BM_mesh_elem_table_ensure(ss->bm, BM_VERT); } @@ -137,22 +137,24 @@ int SCULPT_vertex_count_get(SculptSession *ss) return 0; } -const float *SCULPT_vertex_co_get(SculptSession *ss, int index) +const float *SCULPT_vertex_co_get(SculptSession *ss, SculptVertRef index) { 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)); } @@ -160,41 +162,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; @@ -202,32 +216,35 @@ 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) { if (ss->persistent_base) { - return ss->persistent_base[index].co; + if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) { + index.i = BM_elem_index_get((BMVert *)index.i); + } + return ss->persistent_base[index.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: @@ -236,8 +253,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, @@ -248,30 +265,30 @@ 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]) { 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) +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)); } @@ -280,12 +297,12 @@ float SCULPT_vertex_mask_get(SculptSession *ss, int index) return 0.0f; } -int SCULPT_active_vertex_get(SculptSession *ss) +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) @@ -338,44 +355,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); @@ -402,8 +424,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; + } } } @@ -416,8 +458,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; + } } } @@ -443,48 +496,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; } @@ -492,22 +605,34 @@ 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) { + 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); @@ -517,24 +642,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 < 0 ? 0 : 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]; } @@ -542,23 +682,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; } @@ -580,16 +737,54 @@ 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) { + 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) { + BM_elem_flag_enable(v, BM_ELEM_HIDDEN); + } + else { + 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]]); @@ -603,28 +798,68 @@ 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 bool sculpt_check_unique_face_set_in_base_mesh(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++) { @@ -672,18 +907,41 @@ 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(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: - return true; + case PBVH_BMESH: { + BMIter iter; + BMLoop *l; + BMVert *v = (BMVert *)index.i; + 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 !first; + } 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}; @@ -692,7 +950,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: @@ -717,8 +975,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; } @@ -727,10 +1001,12 @@ 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, + int neighbor_index) { for (int i = 0; i < iter->size; i++) { - if (iter->neighbors[i] == neighbor_index) { + if (iter->neighbors[i].i == neighbor.i) { return; } } @@ -739,51 +1015,61 @@ 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"); + iter->neighbors = MEM_mallocN(iter->capacity * sizeof(SculptVertRef), "neighbor array"); + iter->neighbor_indices = MEM_mallocN(iter->capacity * sizeof(int), "neighbor array"); memcpy(iter->neighbors, iter->neighbors_fixed, sizeof(int) * iter->size); } else { iter->neighbors = MEM_reallocN_id( - iter->neighbors, iter->capacity * sizeof(int), "neighbor array"); + iter->neighbors, iter->capacity * sizeof(SculptVertRef), "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] = neighbor; + iter->neighbor_indices[iter->size] = neighbor_index; iter->size++; } static void sculpt_vertex_neighbors_get_bmesh(SculptSession *ss, - int index, + SculptVertRef index, SculptVertexNeighborIter *iter) { - BMVert *v = BM_vert_at_index(ss->bm, index); + BMVert *v = (BMVert *)index.i; BMIter liter; BMLoop *l; 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; 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)); + + if (v_other != (BMVert *)index.i) { + sculpt_vertex_neighbor_add( + iter, BKE_pbvh_make_vref((intptr_t)v_other), BM_elem_index_get(v_other)); } } } } static void sculpt_vertex_neighbors_get_faces(SculptSession *ss, - int index, + 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; for (int i = 0; i < ss->pmap[index].count; i++) { const MPoly *p = &ss->mpoly[vert_map->indices[i]]; @@ -791,7 +1077,7 @@ static void sculpt_vertex_neighbors_get_faces(SculptSession *ss, 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) { if (f_adj_v[j] != index) { - sculpt_vertex_neighbor_add(iter, f_adj_v[j]); + sculpt_vertex_neighbor_add(iter, BKE_pbvh_make_vref(f_adj_v[j]), f_adj_v[j]); } } } @@ -799,17 +1085,21 @@ 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], + ss->fake_neighbors.fake_neighbor_index[index].i); } } } static void sculpt_vertex_neighbors_get_grids(SculptSession *ss, - const int index, + 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. */ @@ -828,17 +1118,21 @@ static void sculpt_vertex_neighbors_get_grids(SculptSession *ss, 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), 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], + ss->fake_neighbors.fake_neighbor_index[index].i); } } @@ -848,44 +1142,47 @@ static void sculpt_vertex_neighbors_get_grids(SculptSession *ss, } void SCULPT_vertex_neighbors_get(SculptSession *ss, - const int index, + 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); + 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) +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, index); + return BLI_BITMAP_TEST(ss->vertex_info.boundary, + BKE_pbvh_vertex_index_to_table(ss->pbvh, index)); } -bool SCULPT_vertex_is_boundary(const SculptSession *ss, const int index) +bool SCULPT_vertex_is_boundary(const SculptSession *ss, const SculptVertRef vertex) { switch (BKE_pbvh_type(ss->pbvh)) { case PBVH_FACES: { - if (!SCULPT_vertex_all_face_sets_visible_get(ss, index)) { + if (!SCULPT_vertex_all_face_sets_visible_get(ss, vertex)) { return true; } - return sculpt_check_boundary_vertex_in_base_mesh(ss, index); + return sculpt_check_boundary_vertex_in_base_mesh(ss, vertex); } case PBVH_BMESH: { - BMVert *v = BM_vert_at_index(ss->bm, index); + BMVert *v = (BMVert *)vertex.i; return BM_vert_is_boundary(v); } 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; @@ -897,10 +1194,10 @@ 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)); case SUBDIV_CCG_ADJACENT_EDGE: - return sculpt_check_boundary_vertex_in_base_mesh(ss, v1) && - sculpt_check_boundary_vertex_in_base_mesh(ss, v2); + return 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)); case SUBDIV_CCG_ADJACENT_NONE: return false; } @@ -964,6 +1261,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; @@ -982,6 +1280,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; } } @@ -996,15 +1295,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; @@ -1019,7 +1320,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 = { @@ -1032,6 +1333,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; @@ -1043,7 +1345,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) @@ -1094,11 +1396,11 @@ 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 index) { BLI_gsqueue_push(flood->queue, &index); } @@ -1110,7 +1412,7 @@ void SCULPT_floodfill_add_and_skip_initial(SculptFloodFill *flood, int index) } void SCULPT_floodfill_add_initial_with_symmetry( - Sculpt *sd, Object *ob, SculptSession *ss, SculptFloodFill *flood, int index, float radius) + Sculpt *sd, Object *ob, SculptSession *ss, SculptFloodFill *flood, SculptVertRef index, float radius) { /* Add active vertex and symmetric vertices to the queue. */ const char symm = SCULPT_mesh_symmetry_xyz_get(ob); @@ -1118,18 +1420,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); } } @@ -1144,7 +1446,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); } @@ -1155,26 +1459,30 @@ void SCULPT_floodfill_add_active( v = SCULPT_nearest_vertex_get(sd, ob, location, radius_squared, 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; } @@ -1182,7 +1490,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); @@ -1217,6 +1525,8 @@ 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); } @@ -1289,10 +1599,14 @@ void SCULPT_orig_vert_data_unode_init(SculptOrigVertData *data, Object *ob, Scul 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->datatype = data->unode->type; data->coords = data->unode->co; data->normals = data->unode->no; data->vmasks = data->unode->mask; @@ -1304,11 +1618,21 @@ 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; + + /*do not allocate an undo node for bmesh pbvh*/ + if (!ob->sculpt->bm) { + unode = SCULPT_undo_push_node(ob, node, type); + } + SCULPT_orig_vert_data_unode_init(data, ob, unode); + data->datatype = type; } /** @@ -1316,19 +1640,30 @@ void SCULPT_orig_vert_data_init(SculptOrigVertData *data, Object *ob, PBVHNode * */ void SCULPT_orig_vert_data_update(SculptOrigVertData *orig_data, PBVHVertexIter *iter) { - if (orig_data->unode->type == SCULPT_UNDO_COORDS) { + if (orig_data->datatype == 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); + orig_data->co = BM_ELEM_CD_GET_VOID_P(iter->bm_vert, orig_data->ss->cd_origco_offset); + + float *no = BM_ELEM_CD_GET_VOID_P(iter->bm_vert, orig_data->ss->cd_origno_offset); + normal_float_to_short_v3(orig_data->_no, no); + orig_data->no = orig_data->_no; + + orig_data->col = BM_ELEM_CD_GET_VOID_P(iter->bm_vert, orig_data->ss->cd_origvcol_offset); } else { orig_data->co = orig_data->coords[iter->i]; orig_data->no = orig_data->normals[iter->i]; } } - 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) { + if (orig_data->bm_log) { + orig_data->col = BM_ELEM_CD_GET_VOID_P(iter->bm_vert, orig_data->ss->cd_origvcol_offset); + } + else { + orig_data->col = orig_data->colors[iter->i]; + } } - else if (orig_data->unode->type == SCULPT_UNDO_MASK) { + else if (orig_data->datatype == SCULPT_UNDO_MASK) { if (orig_data->bm_log) { orig_data->mask = BM_log_original_mask(orig_data->bm_log, iter->bm_vert); } @@ -1482,7 +1817,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) { @@ -2065,11 +2400,16 @@ 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); + float *temp_co; + float *temp_no; + + BKE_pbvh_bmesh_update_origvert(ss->pbvh, vd.bm_vert, &temp_co, &temp_no, NULL); + if (temp_no) { + normal_float_to_short_v3(no_s, temp_no); + } + // BM_log_original_vert_data(ss->bm, ss->bm_log, vd.bm_vert, &temp_co, &temp_no_s, + // false); copy_v3_v3(co, temp_co); - copy_v3_v3_short(no_s, temp_no_s); } else { copy_v3_v3(co, unode->co[vd.i]); @@ -2433,6 +2773,8 @@ 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_PINCH: if (flip > 0.0f) { @@ -2476,7 +2818,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; @@ -2880,10 +3222,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; @@ -2897,7 +3239,8 @@ typedef struct { bool original; } SculptFindNearestToRayData; -static void do_topology_rake_bmesh_task_cb_ex(void *__restrict userdata, +__attribute__((optnone)) static void do_topology_rake_bmesh_task_cb_ex( + void *__restrict userdata, const int n, const TaskParallelTLS *__restrict tls) { @@ -2933,15 +3276,22 @@ static void do_topology_rake_bmesh_task_cb_ex(void *__restrict userdata, if (!sculpt_brush_test_sq_fn(&test, vd.co)) { 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); + //SculptCurvatureData cdata; + //SCULPT_calc_principle_curvatures(ss, vd.vertex, &cdata); + //copy_v3_v3(direction2, cdata.principle[0]); + copy_v3_v3(direction2, direction); + + SCULPT_bmesh_four_neighbor_average(avg, direction2, vd.bm_vert); sub_v3_v3v3(val, avg, vd.co); @@ -3008,7 +3358,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); @@ -3090,12 +3440,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); @@ -3156,7 +3506,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]; @@ -3183,11 +3533,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]); @@ -3228,7 +3578,7 @@ static void do_displacement_smear_store_prev_disp_task_cb_ex( 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; @@ -3240,16 +3590,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]); } } @@ -3302,7 +3656,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); @@ -3359,7 +3713,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; @@ -3371,6 +3725,7 @@ static void do_draw_sharp_brush_task_cb_ex(void *__restrict userdata, BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { SCULPT_orig_vert_data_update(&orig_data, &vd); + if (!sculpt_brush_test_sq_fn(&test, orig_data.co)) { continue; } @@ -3382,7 +3737,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); @@ -3442,7 +3797,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; @@ -3454,6 +3809,7 @@ static void do_topology_slide_task_cb_ex(void *__restrict userdata, BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { SCULPT_orig_vert_data_update(&orig_data, &vd); + if (!sculpt_brush_test_sq_fn(&test, orig_data.co)) { continue; } @@ -3464,7 +3820,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]; @@ -3486,10 +3842,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)); @@ -3519,31 +3875,31 @@ 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); + const bool is_boundary = SCULPT_vertex_is_boundary(ss, vd->vertex); 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. */ if (is_boundary) { - if (!SCULPT_vertex_is_boundary(ss, ni.index)) { + if (!SCULPT_vertex_is_boundary(ss, ni.vertex)) { 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++; } } @@ -3572,7 +3928,7 @@ void SCULPT_relax_vertex(SculptSession *ss, 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)) { @@ -3600,7 +3956,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]); @@ -3612,6 +3968,7 @@ static void do_topology_relax_task_cb_ex(void *__restrict userdata, BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { SCULPT_orig_vert_data_update(&orig_data, &vd); + if (!sculpt_brush_test_sq_fn(&test, orig_data.co)) { continue; } @@ -3622,7 +3979,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); @@ -3778,9 +4135,11 @@ static void do_crease_brush_task_cb_ex(void *__restrict userdata, BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { + if (!sculpt_brush_test_sq_fn(&test, vd.co)) { continue; } + /* Offset vertex. */ const float fade = SCULPT_brush_strength_factor(ss, brush, @@ -3789,7 +4148,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]; @@ -3835,7 +4194,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; @@ -3896,6 +4255,7 @@ static void do_pinch_brush_task_cb_ex(void *__restrict userdata, BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { + if (!sculpt_brush_test_sq_fn(&test, vd.co)) { continue; } @@ -3906,7 +4266,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]; @@ -3998,7 +4358,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; @@ -4023,7 +4383,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) { @@ -4087,7 +4447,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; @@ -4144,7 +4504,7 @@ static void do_elastic_deform_brush_task_cb_ex(void *__restrict userdata, mul_v3_fl(final_disp, 1.0f - *vd.mask); } - mul_v3_fl(final_disp, SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.index)); + mul_v3_fl(final_disp, SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.vertex)); copy_v3_v3(proxy[vd.i], final_disp); @@ -4176,6 +4536,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); } @@ -4354,7 +4717,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); @@ -4440,7 +4803,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); } @@ -4488,7 +4851,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); } @@ -4551,7 +4914,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; @@ -4574,7 +4937,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); @@ -4625,7 +4988,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; @@ -4649,7 +5012,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); @@ -4700,13 +5063,19 @@ static void do_layer_brush_task_cb_ex(void *__restrict userdata, 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( ss, &test, data->brush->falloff_shape); const int thread_id = BLI_task_parallel_thread_id(tls); + bool bmeshpbvh = BKE_pbvh_type(ss->pbvh) == PBVH_BMESH; + if (bmeshpbvh) { + BM_mesh_elem_index_ensure(ss->bm, BM_VERT); + // BM_mesh_elem_table_ensure(ss->bm, BM_VERT); + } + BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { SCULPT_orig_vert_data_update(&orig_data, &vd); @@ -4721,7 +5090,7 @@ 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; @@ -4757,9 +5126,10 @@ 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); 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), normal, *disp_factor); } else { normal_short_to_float_v3(normal, orig_data.no); @@ -4834,7 +5204,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]; @@ -4948,9 +5318,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) { @@ -5105,7 +5474,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); @@ -5232,9 +5601,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) { @@ -5386,7 +5754,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); @@ -5485,7 +5853,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); @@ -5602,7 +5970,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); @@ -5742,7 +6110,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); @@ -5824,6 +6192,17 @@ void SCULPT_vertcos_to_key(Object *ob, KeyBlock *kb, const float (*vertCos)[3]) BKE_keyblock_update_from_vertcos(ob, kb, vertCos); } +static void topology_undopush_cb(PBVHNode *node, void *data) +{ + SculptSearchSphereData *sdata = (SculptSearchSphereData *)data; + + SCULPT_undo_push_node(sdata->ob, + node, + sdata->brush->sculpt_tool == SCULPT_TOOL_MASK ? SCULPT_UNDO_MASK : + SCULPT_UNDO_COORDS); + BKE_pbvh_node_mark_update(node); +} + /* 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. */ @@ -5839,13 +6218,6 @@ static void sculpt_topology_update(Sculpt *sd, 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. */ @@ -5865,30 +6237,35 @@ static void sculpt_topology_update(Sculpt *sd, } } - 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]); - } - } - - 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)); - } - - MEM_SAFE_FREE(nodes); + PBVHNode **nodes = NULL; + SculptSearchSphereData sdata = {.ss = ss, + .sd = sd, + .ob = ob, + .radius_squared = square_f(ss->cache->radius * radius_scale), + .original = use_original, + .ignore_fully_ineffective = brush->sculpt_tool != + SCULPT_TOOL_MASK, + .center = NULL, + .brush = brush}; + + int symidx = ss->cache->mirror_symmetry_pass + (ss->cache->radial_symmetry_pass * 8); + if (symidx > 127) { + symidx = 127; + } + + /* do nodes under the brush cursor */ + BKE_pbvh_bmesh_update_topology_nodes(ss->pbvh, + SCULPT_search_sphere_cb, + topology_undopush_cb, + &sdata, + mode, + ss->cache->location, + ss->cache->view_normal, + ss->cache->radius, + (brush->flag & BRUSH_FRONTFACE) != 0, + (brush->falloff_shape != PAINT_FALLOFF_SHAPE_SPHERE), + symidx, + brush->sculpt_tool != SCULPT_TOOL_DRAW_SHARP); /* Update average stroke position. */ copy_v3_v3(location, ss->cache->true_location); @@ -5916,11 +6293,20 @@ 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]); } } @@ -5933,11 +6319,8 @@ static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSe /* Check for unsupported features. */ PBVHType type = BKE_pbvh_type(ss->pbvh); - if (brush->sculpt_tool == SCULPT_TOOL_PAINT && type != PBVH_FACES) { - return; - } - - if (brush->sculpt_tool == SCULPT_TOOL_SMEAR && type != PBVH_FACES) { + if (ELEM(brush->sculpt_tool, SCULPT_TOOL_PAINT, SCULPT_TOOL_SMEAR) && + !ELEM(type, PBVH_BMESH, PBVH_FACES)) { return; } @@ -5969,9 +6352,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); } @@ -6003,16 +6384,55 @@ 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); + if (SCULPT_ensure_dyntopo_node_undo(ob, nodes[i], SCULPT_UNDO_COLOR, other)) { + BKE_pbvh_update_origcolor_bmesh(ss->pbvh, nodes[i]); + + if (other != -1) { + BKE_pbvh_update_origco_bmesh(ss->pbvh, nodes[i]); + } + } + + BKE_pbvh_node_mark_update_color(nodes[i]); + // SCULPT_undo_push_node(ob, nodes[i], SCULPT_UNDO_COLOR); + } + } + 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); @@ -6149,6 +6569,9 @@ 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; } if (!ELEM(brush->sculpt_tool, SCULPT_TOOL_SMOOTH, SCULPT_TOOL_MASK) && @@ -6248,7 +6671,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 = BM_ELEM_CD_GET_VOID_P(vd.bm_vert, ss->cd_origco_offset); + 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]); @@ -6338,6 +6763,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); @@ -6625,6 +7054,24 @@ bool SCULPT_vertex_colors_poll(bContext *C) if (!U.experimental.use_sculpt_vertex_colors) { return false; } + + Object *ob = CTX_data_active_object(C); + + 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); } @@ -6712,6 +7159,8 @@ 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"; } return "Sculpting"; @@ -7324,6 +7773,8 @@ 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_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) || @@ -7360,7 +7811,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; } @@ -7397,7 +7848,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; } @@ -7512,7 +7963,10 @@ 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); + + if (BKE_pbvh_type(ss->pbvh) != PBVH_BMESH) { + SCULPT_vertex_random_access_ensure(ss); + } copy_v3_v3(out->active_vertex_co, SCULPT_active_vertex_co_get(ss)); switch (BKE_pbvh_type(ss->pbvh)) { @@ -7521,11 +7975,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; } @@ -7612,11 +8066,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; @@ -7823,6 +8272,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 @@ -7874,12 +8330,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 */ @@ -8201,8 +8676,10 @@ static int sculpt_set_persistent_base_exec(bContext *C, wmOperator *UNUSED(op)) "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; } @@ -8264,6 +8741,22 @@ static bool sculpt_no_multires_poll(bContext *C) return false; } +/* 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); + } +} static int sculpt_symmetrize_exec(bContext *C, wmOperator *op) { Main *bmain = CTX_data_main(C); @@ -8645,29 +9138,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); } @@ -8740,7 +9242,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; @@ -8803,7 +9305,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; @@ -8818,7 +9320,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; @@ -8875,10 +9377,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; } @@ -8887,19 +9393,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; } } @@ -8910,6 +9425,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; @@ -8923,15 +9439,20 @@ static void do_fake_neighbor_search_task_cb(void *__restrict userdata, NearestVertexFakeNeighborTLSData *nvtd = tls->userdata_chunk; PBVHVertexIter vd; + bool has_bmesh = false; + + 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; } } @@ -8945,17 +9466,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; @@ -8970,7 +9497,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 = { @@ -8984,6 +9511,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); @@ -8996,19 +9524,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; } @@ -9016,6 +9549,8 @@ void SCULPT_connected_components_ensure(Object *ob) { SculptSession *ss = ob->sculpt; + 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) { @@ -9031,10 +9566,12 @@ 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 (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); @@ -9091,12 +9628,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, 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); - /* 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) { + if (to_v.i != -1) { /* Add the fake neighbor if available. */ SCULPT_fake_neighbor_add(ss, from_v, to_v); } @@ -9214,16 +9752,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); @@ -9232,7 +9773,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) @@ -9322,7 +9863,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) @@ -9378,7 +9919,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"); @@ -9573,14 +10114,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_automasking.c b/source/blender/editors/sculpt_paint/sculpt_automasking.c index 5f5fb51d75f..6427d6eeb4f 100644 --- a/source/blender/editors/sculpt_paint/sculpt_automasking.c +++ b/source/blender/editors/sculpt_paint/sculpt_automasking.c @@ -100,6 +100,13 @@ 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)) { + return true; + } + if (br->concave_mask_factor > 0.0f) { + return true; + } + return false; } @@ -127,16 +134,42 @@ 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->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]; + mask = automasking->factor[BKE_pbvh_vertex_index_to_table(ss->pbvh, vert)]; + } + + if (do_concave) { + if (!automasking->factor) { + mask = SCULPT_calc_concavity(ss, vert); + } + + if (automasking->settings.flags & BRUSH_AUTOMASKING_INVERT_CONCAVITY) { + mask = 1.0 - mask; + } + + mask = pow(mask*1.5f, (0.5f + automasking->settings.concave_factor) * 8.0); + CLAMP(mask, 0.0f, 1.0f); + } + + if (automasking->factor) { + return mask; } if (automasking->settings.flags & BRUSH_AUTOMASKING_FACE_SETS) { @@ -157,7 +190,7 @@ float SCULPT_automasking_factor_get(AutomaskingCache *automasking, SculptSession } } - return 1.0f; + return mask; } void SCULPT_automasking_cache_free(AutomaskingCache *automasking) @@ -191,16 +224,19 @@ typedef struct AutomaskFloodFillData { 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; + data->automask_factor[BKE_pbvh_vertex_index_to_table(ss->pbvh, to_vref)] = 1.0f; + data->automask_factor[BKE_pbvh_vertex_index_to_table(ss->pbvh, from_vref)] = 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) @@ -255,7 +291,9 @@ static float *sculpt_face_sets_automasking_init(Sculpt *sd, Object *ob, float *a 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)) { + SculptVertRef vertex = BKE_pbvh_table_index_to_vertex(ss->pbvh, i); + + if (!SCULPT_vertex_has_face_set(ss, vertex, active_face_set)) { automask_factor[i] *= 0.0f; } } @@ -281,15 +319,17 @@ float *SCULPT_boundary_automasking_init(Object *ob, 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)) { 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 +338,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; } @@ -330,7 +372,65 @@ static void SCULPT_automasking_cache_settings_update(AutomaskingCache *automaski Brush *brush) { automasking->settings.flags = sculpt_automasking_mode_effective_bits(sd, brush); + + if (brush->concave_mask_factor != 0.0f) { + automasking->settings.flags |= BRUSH_AUTOMASKING_CONCAVITY; + } + automasking->settings.initial_face_set = SCULPT_active_face_set_get(ss); + automasking->settings.concave_factor = brush->concave_mask_factor; +} + +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, Brush *brush, float *factor) +{ + 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); + + factor[i] *= f; + } + // BKE_pbvh_vertex_iter_begin } AutomaskingCache *SCULPT_automasking_cache_init(Sculpt *sd, Brush *brush, Object *ob) @@ -378,6 +478,10 @@ AutomaskingCache *SCULPT_automasking_cache_init(Sculpt *sd, Brush *brush, Object SCULPT_boundary_automasking_init( ob, AUTOMASK_INIT_BOUNDARY_FACE_SETS, boundary_propagation_steps, automasking->factor); } + if (SCULPT_is_automasking_mode_enabled(sd, brush, BRUSH_AUTOMASKING_CONCAVITY)) { + SCULPT_vertex_random_access_ensure(ss); + SCULPT_concavity_automasking_init(ob, brush, automasking->factor); + } return automasking; } diff --git a/source/blender/editors/sculpt_paint/sculpt_boundary.c b/source/blender/editors/sculpt_paint/sculpt_boundary.c index f1fb402ae41..a92b14eb661 100644 --- a/source/blender/editors/sculpt_paint/sculpt_boundary.c +++ b/source/blender/editors/sculpt_paint/sculpt_boundary.c @@ -62,19 +62,28 @@ #define BOUNDARY_STEPS_NONE -1 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,23 +94,25 @@ 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)) { 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)) { @@ -114,6 +125,7 @@ static int sculpt_boundary_get_closest_boundary_vertex(SculptSession *ss, BoundaryInitialVertexFloodFillData fdata = { .initial_vertex = initial_vertex, + .initial_vertex_index = initial_vertex_index, .boundary_initial_vertex = BOUNDARY_VERTEX_NONE, .boundary_initial_vertex_steps = INT_MAX, .radius_sq = radius * radius, @@ -134,28 +146,37 @@ 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), + "boundary vertrefs"); + boundary->vertex_indices = MEM_reallocN_id( + boundary->vertex_indices, boundary->vertices_capacity * sizeof(int), "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; @@ -175,7 +196,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 +207,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)) { boundary_vertex_count++; } } @@ -218,24 +239,27 @@ 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; + 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)) { 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); + 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); } @@ -245,12 +269,15 @@ static bool boundary_floodfill_cb( static void sculpt_boundary_indices_init(SculptSession *ss, SculptBoundary *boundary, const bool init_boundary_distances, - const int initial_boundary_index) + const SculptVertRef initial_boundary_index) { const int totvert = SCULPT_vertex_count_get(ss); boundary->vertices = MEM_malloc_arrayN( + BOUNDARY_INDICES_BLOCK_SIZE, sizeof(SculptVertRef), "boundary vrefs"); + boundary->vertex_indices = MEM_malloc_arrayN( BOUNDARY_INDICES_BLOCK_SIZE, sizeof(int), "boundary indices"); + if (init_boundary_distances) { boundary->distance = MEM_calloc_arrayN(totvert, sizeof(float), "boundary distances"); } @@ -264,7 +291,7 @@ static void sculpt_boundary_indices_init(SculptSession *ss, 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 = { @@ -278,13 +305,13 @@ static void sculpt_boundary_indices_init(SculptSession *ss, SCULPT_floodfill_free(&flood); /* 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; } } @@ -302,7 +329,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); @@ -313,19 +340,22 @@ static void sculpt_boundary_edit_data_init(SculptSession *ss, totvert, sizeof(SculptBoundaryEditInfo), "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 +363,11 @@ 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 = BKE_pbvh_vertex_index_to_table( + ss->pbvh, boundary->vertices[i]); } } SCULPT_VERTEX_NEIGHBORS_ITER_END(ni_duplis); @@ -354,29 +388,31 @@ 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; 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); @@ -386,12 +422,12 @@ 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].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 +435,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 +448,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 +479,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 +491,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 +528,22 @@ 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; } 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; } @@ -555,30 +592,34 @@ static void sculpt_boundary_bend_data_init(SculptSession *ss, SculptBoundary *bo totvert, 3 * sizeof(float), "pivot positions"); 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; } + float dir[3]; float normal[3]; - SCULPT_vertex_normal_get(ss, i, normal); + SCULPT_vertex_normal_get(ss, vertex, normal); sub_v3_v3v3(dir, SCULPT_vertex_co_get(ss, boundary->edit_info[i].original_vertex), - SCULPT_vertex_co_get(ss, i)); + SCULPT_vertex_co_get(ss, vertex)); 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]); + copy_v3_v3(boundary->bend.pivot_positions[boundary->edit_info[i].original_vertex_i], + SCULPT_vertex_co_get(ss, vertex)); } for (int i = 0; i < totvert; i++) { 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]); + boundary->bend.pivot_positions[boundary->edit_info[i].original_vertex_i]); copy_v3_v3(boundary->bend.pivot_rotation_axis[i], - boundary->bend.pivot_rotation_axis[boundary->edit_info[i].original_vertex]); + boundary->bend.pivot_rotation_axis[boundary->edit_info[i].original_vertex_i]); } } @@ -588,12 +629,15 @@ static void sculpt_boundary_slide_data_init(SculptSession *ss, SculptBoundary *b boundary->slide.directions = MEM_calloc_arrayN(totvert, 3 * sizeof(float), "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) { } - 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++) { @@ -601,7 +645,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]); } } @@ -656,7 +700,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; @@ -679,7 +723,7 @@ static void do_boundary_brush_bend_task_cb_ex(void *__restrict userdata, } const float mask = vd.mask ? 1.0f - *vd.mask : 1.0f; - const float automask = SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.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]); @@ -711,7 +755,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); @@ -728,7 +772,7 @@ static void do_boundary_brush_slide_task_cb_ex(void *__restrict userdata, } const float mask = vd.mask ? 1.0f - *vd.mask : 1.0f; - const float automask = SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.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, @@ -758,7 +802,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); @@ -775,7 +819,7 @@ static void do_boundary_brush_inflate_task_cb_ex(void *__restrict userdata, } const float mask = vd.mask ? 1.0f - *vd.mask : 1.0f; - const float automask = SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.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); @@ -807,7 +851,7 @@ 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) { @@ -822,7 +866,7 @@ static void do_boundary_brush_grab_task_cb_ex(void *__restrict userdata, } const float mask = vd.mask ? 1.0f - *vd.mask : 1.0f; - const float automask = SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.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, @@ -851,7 +895,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; @@ -874,7 +918,7 @@ static void do_boundary_brush_twist_task_cb_ex(void *__restrict userdata, } const float mask = vd.mask ? 1.0f - *vd.mask : 1.0f; - const float automask = SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.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); @@ -906,7 +950,7 @@ 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) { @@ -924,9 +968,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++; } } @@ -960,7 +1004,8 @@ void SCULPT_do_boundary_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totn 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); } diff --git a/source/blender/editors/sculpt_paint/sculpt_cloth.c b/source/blender/editors/sculpt_paint/sculpt_cloth.c index 16d10f6d6bb..5760e4e9e10 100644 --- a/source/blender/editors/sculpt_paint/sculpt_cloth.c +++ b/source/blender/editors/sculpt_paint/sculpt_cloth.c @@ -219,20 +219,25 @@ 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; @@ -252,7 +257,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, @@ -387,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++; } @@ -558,7 +563,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]; @@ -806,7 +811,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); @@ -852,6 +857,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); @@ -874,10 +882,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); @@ -1150,16 +1158,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)); } } } @@ -1168,7 +1180,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)); } } @@ -1452,13 +1466,13 @@ static void cloth_filter_apply_forces_task_cb(void *__restrict userdata, 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; } } @@ -1477,7 +1491,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: @@ -1542,7 +1556,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_detail.c b/source/blender/editors/sculpt_paint/sculpt_detail.c index ddf7ba1e412..27414a92401 100644 --- a/source/blender/editors/sculpt_paint/sculpt_detail.c +++ b/source/blender/editors/sculpt_paint/sculpt_detail.c @@ -109,7 +109,7 @@ static int sculpt_detail_flood_fill_exec(bContext *C, wmOperator *UNUSED(op)) 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)) { + ss->pbvh, PBVH_Collapse | PBVH_Subdivide, center, NULL, size, false, false, -1, false)) { for (int i = 0; i < totnodes; i++) { BKE_pbvh_node_mark_topology_update(nodes[i]); } @@ -167,13 +167,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); diff --git a/source/blender/editors/sculpt_paint/sculpt_dyntopo.c b/source/blender/editors/sculpt_paint/sculpt_dyntopo.c index 87e0ea7f6a9..bfd7f843a7e 100644 --- a/source/blender/editors/sculpt_paint/sculpt_dyntopo.c +++ b/source/blender/editors/sculpt_paint/sculpt_dyntopo.c @@ -23,6 +23,7 @@ #include "MEM_guardedalloc.h" +#include "BLI_array.h" #include "BLI_blenlib.h" #include "BLI_hash.h" #include "BLI_math.h" @@ -110,19 +111,102 @@ void SCULPT_pbvh_clear(Object *ob) DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); } +void SCULPT_dyntopo_save_origverts(SculptSession *ss) +{ + BMIter iter; + BMVert *v; + + BM_ITER_MESH (v, &iter, ss->bm, BM_VERTS_OF_MESH) { + float *co = BM_ELEM_CD_GET_VOID_P(v, ss->cd_origco_offset); + float *no = BM_ELEM_CD_GET_VOID_P(v, ss->cd_origno_offset); + + copy_v3_v3(co, v->co); + copy_v3_v3(no, v->no); + } +} + +static char layer_id[] = "_dyntopo_node_id"; +static char origco_id[] = "_dyntopop_orig_co"; +static char origno_id[] = "_dyntopop_orig_no"; +static char origcolor_id[] = "_dyntopo_orig_vcol"; + +void SCULPT_dyntopo_node_layers_update_offsets(SculptSession *ss) +{ + SCULPT_dyntopo_node_layers_add(ss); +} + void SCULPT_dyntopo_node_layers_add(SculptSession *ss) { - int cd_node_layer_index; + 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); - char layer_id[] = "_dyntopo_node_id"; + if (have_vcol) { + cd_origvcol_index = CustomData_get_named_layer_index( + &ss->bm->vdata, CD_PROP_COLOR, origcolor_id); + + if (cd_origvcol_index == -1) { + BM_data_layer_add_named(ss->bm, &ss->bm->vdata, CD_PROP_COLOR, origcolor_id); + } + } + + cd_origco_index = CustomData_get_named_layer_index(&ss->bm->vdata, CD_PROP_FLOAT3, origco_id); + if (cd_origco_index == -1) { + BM_data_layer_add_named(ss->bm, &ss->bm->vdata, CD_PROP_FLOAT3, origco_id); + } + + cd_origno_index = CustomData_get_named_layer_index(&ss->bm->vdata, CD_PROP_FLOAT3, origno_id); + if (cd_origno_index == -1) { + BM_data_layer_add_named(ss->bm, &ss->bm->vdata, CD_PROP_FLOAT3, origno_id); + } 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); } + cd_face_node_layer_index = CustomData_get_named_layer_index( + &ss->bm->pdata, CD_PROP_INT32, layer_id); + if (cd_face_node_layer_index == -1) { + BM_data_layer_add_named(ss->bm, &ss->bm->pdata, CD_PROP_INT32, layer_id); + } + + // get indices again, as they might have changed after adding new layers + cd_origco_index = CustomData_get_named_layer_index(&ss->bm->vdata, CD_PROP_FLOAT3, origco_id); + cd_origno_index = CustomData_get_named_layer_index(&ss->bm->vdata, CD_PROP_FLOAT3, origno_id); + cd_node_layer_index = CustomData_get_named_layer_index(&ss->bm->vdata, CD_PROP_INT32, layer_id); + cd_face_node_layer_index = CustomData_get_named_layer_index( + &ss->bm->pdata, CD_PROP_INT32, layer_id); + + if (have_vcol) { + cd_origvcol_index = CustomData_get_named_layer_index( + &ss->bm->vdata, CD_PROP_COLOR, origcolor_id); + + ss->cd_origvcol_offset = CustomData_get_n_offset( + &ss->bm->vdata, + CD_PROP_COLOR, + cd_origvcol_index - CustomData_get_layer_index(&ss->bm->vdata, CD_PROP_COLOR)); + ss->bm->vdata.layers[cd_origvcol_index].flag |= CD_FLAG_TEMPORARY; + } + else { + ss->cd_origvcol_offset = -1; + } + + ss->cd_origco_offset = CustomData_get_n_offset( + &ss->bm->vdata, + CD_PROP_FLOAT3, + cd_origco_index - CustomData_get_layer_index(&ss->bm->vdata, CD_PROP_FLOAT3)); + ss->bm->vdata.layers[cd_origco_index].flag |= CD_FLAG_TEMPORARY; + + ss->cd_vcol_offset = CustomData_get_offset(&ss->bm->vdata, CD_PROP_COLOR); + + ss->cd_origno_offset = CustomData_get_n_offset( + &ss->bm->vdata, + CD_PROP_FLOAT3, + cd_origno_index - CustomData_get_layer_index(&ss->bm->vdata, CD_PROP_FLOAT3)); + ss->bm->vdata.layers[cd_origno_index].flag |= CD_FLAG_TEMPORARY; + ss->cd_vert_node_offset = CustomData_get_n_offset( &ss->bm->vdata, CD_PROP_INT32, @@ -130,19 +214,113 @@ 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_face_node_layer_index].flag |= CD_FLAG_TEMPORARY; + ss->cd_faceset_offset = CustomData_get_offset(&ss->bm->pdata, CD_SCULPT_FACE_SETS); - ss->bm->pdata.layers[cd_node_layer_index].flag |= CD_FLAG_TEMPORARY; + SCULPT_dyntopo_save_origverts(ss); +} + +/** + 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) { + 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; + } + + char typemap[CD_NUMTYPES] = { + 0, + }; + + for (int j = 0; j < data1->totlayer; j++) { + CustomDataLayer *cl = data1->layers + j; + CustomDataLayer *cl1 = cl; + + if ((1 << cl1->type) & badmask) { + continue; + } + + if (typemap[cl1->type]) { + continue; + } + + typemap[cl1->type] = 1; + cl1 = cl + CustomData_get_active_layer(data1, cl1->type); + + int idx = CustomData_get_named_layer_index(data2, cl1->type, cl1->name); + CustomData_set_layer_active_index(data2, cl1->type, idx); + + cl1 = cl + CustomData_get_render_layer(data1, cl1->type); + idx = CustomData_get_named_layer_index(data2, cl1->type, cl1->name); + CustomData_set_layer_render_index(data2, cl1->type, idx); + + cl1 = cl + CustomData_get_stencil_layer(data1, cl1->type); + idx = CustomData_get_named_layer_index(data2, cl1->type, cl1->name); + CustomData_set_layer_stencil_index(data2, cl1->type, idx); + + cl1 = cl + CustomData_get_clone_layer(data1, cl1->type); + idx = CustomData_get_named_layer_index(data2, cl1->type, cl1->name); + CustomData_set_layer_clone_index(data2, cl1->type, idx); + } + + BLI_array_free(newlayers); + } + + if (modified) { + SCULPT_dyntopo_node_layers_update_offsets(ss); + BKE_pbvh_update_offsets(ss->pbvh, + ss->cd_vert_node_offset, + ss->cd_face_node_offset, + ss->cd_origco_offset, + ss->cd_origno_offset, + ss->cd_origvcol_offset); + } } void SCULPT_dynamic_topology_enable_ex(Main *bmain, Depsgraph *depsgraph, Scene *scene, Object *ob) @@ -165,7 +343,8 @@ void SCULPT_dynamic_topology_enable_ex(Main *bmain, Depsgraph *depsgraph, Scene .use_toolflags = false, })); - BM_mesh_bm_from_me(ss->bm, + BM_mesh_bm_from_me(NULL, + ss->bm, me, (&(struct BMeshFromMeshParams){ .calc_face_normal = true, @@ -175,6 +354,30 @@ void SCULPT_dynamic_topology_enable_ex(Main *bmain, Depsgraph *depsgraph, Scene SCULPT_dynamic_topology_triangulate(ss->bm); BM_data_layer_add(ss->bm, &ss->bm->vdata, CD_PAINT_MASK); SCULPT_dyntopo_node_layers_add(ss); + + BMIter iter; + BMVert *v; + int cd_vcol_offset = CustomData_get_offset(&ss->bm->vdata, CD_PROP_COLOR); + + BM_ITER_MESH (v, &iter, ss->bm, BM_VERTS_OF_MESH) { + if (ss->cd_origco_offset >= 0) { + float *co = BM_ELEM_CD_GET_VOID_P(v, ss->cd_origco_offset); + copy_v3_v3(co, v->co); + } + if (ss->cd_origno_offset >= 0) { + float *no = BM_ELEM_CD_GET_VOID_P(v, ss->cd_origno_offset); + copy_v3_v3(no, v->no); + } + + if (ss->cd_origvcol_offset >= 0) { + + float *ocolor = BM_ELEM_CD_GET_VOID_P(v, ss->cd_origvcol_offset); + float *color = BM_ELEM_CD_GET_VOID_P(v, cd_vcol_offset); + + copy_v4_v4(ocolor, color); + } + } + /* Make sure the data for existing faces are initialized. */ if (me->totpoly != ss->bm->totface) { BM_mesh_normals_update(ss->bm); @@ -184,7 +387,8 @@ 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); + ss->bm_log = BM_log_create( + ss->bm, ss->cd_origco_offset, ss->cd_origno_offset, ss->cd_origvcol_offset); /* Update dependency graph, so modifiers that depend on dyntopo being enabled * are re-evaluated and the PBVH is re-created. */ @@ -233,16 +437,6 @@ static void SCULPT_dynamic_topology_disable_ex( 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; - /* 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; @@ -384,6 +578,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)) { @@ -397,6 +592,7 @@ enum eDynTopoWarnFlag SCULPT_dynamic_topology_check(Scene *scene, Object *ob) } } } +#endif { VirtualModifierData virtualModifierData; diff --git a/source/blender/editors/sculpt_paint/sculpt_expand.c b/source/blender/editors/sculpt_paint/sculpt_expand.c index db6d33c2700..892b30048f6 100644 --- a/source/blender/editors/sculpt_paint/sculpt_expand.c +++ b/source/blender/editors/sculpt_paint/sculpt_expand.c @@ -194,7 +194,7 @@ static float sculpt_expand_falloff_value_vertex_get(SculptSession *ss, } float rgba[4]; - const float *vertex_co = SCULPT_vertex_co_get(ss, v); + const float *vertex_co = SCULPT_vertex_co_get(ss, BKE_pbvh_table_index_to_vertex(ss->pbvh, v)); const float avg = BKE_brush_sample_tex_3d( expand_cache->scene, expand_cache->brush, vertex_co, rgba, 0, ss->tex_pool); @@ -227,7 +227,9 @@ static float sculpt_expand_max_vertex_falloff_get(ExpandCache *expand_cache) */ static bool sculpt_expand_state_get(SculptSession *ss, ExpandCache *expand_cache, const int v) { - if (!SCULPT_vertex_visible_get(ss, v)) { + SculptVertRef vref = BKE_pbvh_table_index_to_vertex(ss->pbvh, v); + + if (!SCULPT_vertex_visible_get(ss, vref)) { return false; } @@ -245,7 +247,7 @@ static bool sculpt_expand_state_get(SculptSession *ss, ExpandCache *expand_cache /* Face Sets are not being modified when using this function, so it is ok to get this directly * from the Sculpt API instead of implementing a custom function to get them from * expand_cache->original_face_sets. */ - const int face_set = SCULPT_vertex_face_set_get(ss, v); + const int face_set = SCULPT_vertex_face_set_get(ss, vref); enabled = BLI_gset_haskey(expand_cache->snap_enabled_face_sets, POINTER_FROM_INT(face_set)); } else { @@ -381,20 +383,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)) { is_expand_boundary = true; } @@ -410,12 +414,13 @@ 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, +static SculptVertRef sculpt_expand_get_vertex_index_for_symmetry_pass(Object *ob, const char symm_it, - const int original_vertex) + 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; } @@ -448,20 +453,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 +494,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) * + 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], 0.0f, 1.0f); + 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 +521,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 +548,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 +568,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); @@ -574,7 +587,7 @@ static float *sculpt_expand_spherical_falloff_create(Object *ob, const int v) if (symm_vertex != -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 +600,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 +615,7 @@ 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) { @@ -623,7 +636,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 +644,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,7 +664,7 @@ 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); @@ -663,14 +679,14 @@ static float *sculpt_expand_diagonals_falloff_create(Object *ob, const int v) /* 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); @@ -683,16 +699,21 @@ 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]]; + + int v_next_i = BKE_pbvh_vertex_index_to_table(ss->pbvh, v_next); + + 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] + 1.0f; + + dists[neighbor_v] = dists[v_next_i] + 1.0f; BLI_BITMAP_ENABLE(visited_vertices, neighbor_v); BLI_gsqueue_push(queue, &neighbor_v); } @@ -937,10 +958,12 @@ 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); @@ -957,8 +980,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 +1001,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; @@ -1406,14 +1433,16 @@ static void sculpt_expand_face_sets_restore(SculptSession *ss, ExpandCache *expa } } -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. */ @@ -1421,7 +1450,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; } @@ -1462,7 +1491,7 @@ 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, +static SculptVertRef sculpt_expand_target_vertex_update_and_get(bContext *C, Object *ob, const float mouse[2]) { @@ -1471,7 +1500,9 @@ static int sculpt_expand_target_vertex_update_and_get(bContext *C, 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; } /** @@ -1572,7 +1603,7 @@ static void sculpt_expand_finish(bContext *C) */ static void sculpt_expand_find_active_connected_components_from_vert(Object *ob, ExpandCache *expand_cache, - const int initial_vertex) + const SculptVertRef initial_vertex) { SculptSession *ss = ob->sculpt; for (int i = 0; i < EXPAND_SYMM_AREAS; i++) { @@ -1585,11 +1616,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]; } } @@ -1603,8 +1635,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); diff --git a/source/blender/editors/sculpt_paint/sculpt_face_set.c b/source/blender/editors/sculpt_paint/sculpt_face_set.c index df03d2adeaf..7356b0ba071 100644 --- a/source/blender/editors/sculpt_paint/sculpt_face_set.c +++ b/source/blender/editors/sculpt_paint/sculpt_face_set.c @@ -23,6 +23,7 @@ #include "MEM_guardedalloc.h" +#include "BLI_array.h" #include "BLI_blenlib.h" #include "BLI_hash.h" #include "BLI_math.h" @@ -135,6 +136,7 @@ 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); @@ -158,7 +160,7 @@ 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) { @@ -166,7 +168,34 @@ static void do_draw_face_sets_brush_task_cb_ex(void *__restrict userdata, } } } + 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 > 0.05f && fset > 0) { + 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)) { @@ -179,11 +208,11 @@ 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); } } } @@ -219,7 +248,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; } @@ -230,7 +259,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); @@ -344,8 +373,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); } } } @@ -357,7 +389,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; } @@ -371,15 +405,19 @@ 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); } } @@ -392,7 +430,8 @@ static int sculpt_face_set_create_exec(bContext *C, wmOperator *op) .use_toolflags = true, })); - BM_mesh_bm_from_me(bm, + BM_mesh_bm_from_me(NULL, + bm, mesh, (&(struct BMeshFromMeshParams){ .calc_face_normal = true, @@ -570,7 +609,8 @@ static void sculpt_face_sets_init_flood_fill(Object *ob, .use_toolflags = true, })); - BM_mesh_bm_from_me(bm, + BM_mesh_bm_from_me(NULL, + bm, mesh, (&(struct BMeshFromMeshParams){ .calc_face_normal = true, @@ -648,7 +688,8 @@ static void sculpt_face_sets_init_loop(Object *ob, const int mode) .use_toolflags = true, })); - BM_mesh_bm_from_me(bm, + BM_mesh_bm_from_me(NULL, + bm, mesh, (&(struct BMeshFromMeshParams){ .calc_face_normal = true, @@ -865,7 +906,9 @@ 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; } @@ -1080,12 +1123,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_GET_INT(l->radial_next->f, 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) { @@ -1114,6 +1205,11 @@ static void sculpt_face_set_shrink(Object *ob, const int active_face_set_id, const bool modify_hidden) { + if (ss && ss->bm) { + // XXX implement me + return; + } + Mesh *mesh = BKE_mesh_from_object(ob); for (int p = 0; p < mesh->totpoly; p++) { if (!modify_hidden && prev_face_sets[p] <= 0) { @@ -1180,7 +1276,8 @@ static void sculpt_face_set_delete_geometry(Object *ob, .use_toolflags = true, })); - BM_mesh_bm_from_me(bm, + BM_mesh_bm_from_me(ob, + bm, mesh, (&(struct BMeshFromMeshParams){ .calc_face_normal = true, @@ -1201,6 +1298,7 @@ static void sculpt_face_set_delete_geometry(Object *ob, BM_mesh_elem_hflag_disable_all(bm, BM_VERT | BM_EDGE | BM_FACE, BM_ELEM_TAG, false); BM_mesh_bm_to_me(NULL, + ob, bm, ob->data, (&(struct BMeshToMeshParams){ @@ -1215,17 +1313,21 @@ 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_vertex_has_face_set(ss, vref, active_face_set_id) && + SCULPT_vertex_has_unique_face_set(ss, vref); } MVert *mvert = SCULPT_mesh_deformed_mverts_get(ss); @@ -1242,13 +1344,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; diff --git a/source/blender/editors/sculpt_paint/sculpt_filter_color.c b/source/blender/editors/sculpt_paint/sculpt_filter_color.c index b5fade32a25..6f1fcfd8ea5 100644 --- a/source/blender/editors/sculpt_paint/sculpt_filter_color.c +++ b/source/blender/editors/sculpt_paint/sculpt_filter_color.c @@ -107,7 +107,7 @@ 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) @@ -119,7 +119,7 @@ static void color_filter_task_cb(void *__restrict userdata, 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; } @@ -197,7 +197,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; } diff --git a/source/blender/editors/sculpt_paint/sculpt_filter_mask.c b/source/blender/editors/sculpt_paint/sculpt_filter_mask.c index 0297ed73dd4..5ce5a862cbe 100644 --- a/source/blender/editors/sculpt_paint/sculpt_filter_mask.c +++ b/source/blender/editors/sculpt_paint/sculpt_filter_mask.c @@ -120,7 +120,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; @@ -140,7 +140,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; @@ -151,7 +151,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; @@ -233,7 +233,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); } } @@ -324,9 +326,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 3cf6a8cc561..d7c8a93f1ab 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. */ @@ -309,7 +309,7 @@ static void mesh_filter_task_cb(void *__restrict userdata, 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 @@ -327,7 +327,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; } } @@ -335,7 +335,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); sub_v3_v3v3(val, avg, orig_co); madd_v3_v3v3fl(val, orig_co, val, fade); sub_v3_v3v3(disp, val, orig_co); @@ -398,7 +398,7 @@ static void mesh_filter_task_cb(void *__restrict userdata, disp, vd.co, ss->filter_cache->surface_smooth_laplacian_disp, - vd.index, + vd.vertex, orig_data.co, ss->filter_cache->surface_smooth_shape_preservation); break; @@ -412,10 +412,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); } @@ -425,7 +425,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); 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])); @@ -487,9 +487,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); + sub_v3_v3v3(filter_cache->detail_directions[i], avg, SCULPT_vertex_co_get(ss, vertex)); } } @@ -513,8 +515,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]); } } @@ -535,8 +539,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); + 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]); } @@ -559,11 +565,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++; @@ -591,7 +598,7 @@ 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; } @@ -599,7 +606,7 @@ static void mesh_filter_surface_smooth_displace_task_cb( SCULPT_surface_smooth_displace_step(ss, vd.co, ss->filter_cache->surface_smooth_laplacian_disp, - vd.index, + 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_intern.h b/source/blender/editors/sculpt_paint/sculpt_intern.h index 19c4eda7593..6ec6f923a1d 100644 --- a/source/blender/editors/sculpt_paint/sculpt_intern.h +++ b/source/blender/editors/sculpt_paint/sculpt_intern.h @@ -51,6 +51,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 */ @@ -98,20 +99,20 @@ char SCULPT_mesh_symmetry_xyz_get(Object *object); void SCULPT_vertex_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); +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, int index); -void SCULPT_vertex_persistent_normal_get(SculptSession *ss, int index, float no[3]); +const float *SCULPT_vertex_persistent_co_get(SculptSession *ss, SculptVertRef index); +void SCULPT_vertex_persistent_normal_get(SculptSession *ss, SculptVertRef index, float no[3]); /* 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. */ @@ -122,22 +123,26 @@ float *SCULPT_brush_deform_target_vertex_co_get(SculptSession *ss, #define SCULPT_VERTEX_NEIGHBOR_FIXED_CAPACITY 256 typedef struct SculptVertexNeighborIter { /* Storage */ - int *neighbors; + SculptVertRef *neighbors; + int *neighbor_indices; + int size; int capacity; - int neighbors_fixed[SCULPT_VERTEX_NEIGHBOR_FIXED_CAPACITY]; + SculptVertRef 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; int index; bool is_duplicate; } SculptVertexNeighborIter; void SCULPT_vertex_neighbors_get(struct SculptSession *ss, - const int index, + const SculptVertRef vref, const bool include_duplicates, SculptVertexNeighborIter *iter); @@ -146,7 +151,8 @@ 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.vertex = neighbor_iterator.neighbors[neighbor_iterator.i]; \ + 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 +160,8 @@ 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.vertex = neighbor_iterator.neighbors[neighbor_iterator.i]; \ + 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,7 +172,7 @@ 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); void SCULPT_active_vertex_normal_get(SculptSession *ss, float normal[3]); @@ -185,14 +192,14 @@ void SCULPT_fake_neighbors_free(struct Object *ob); /* Vertex Info. */ void SCULPT_boundary_info_ensure(Object *object); /* Boundary Info needs to be initialized in order to use this function. */ -bool SCULPT_vertex_is_boundary(const SculptSession *ss, const int index); +bool SCULPT_vertex_is_boundary(const SculptSession *ss, const SculptVertRef index); 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,17 +207,17 @@ 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(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); @@ -224,24 +231,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 +258,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,14 +304,15 @@ 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_initial(SculptFloodFill *flood, SculptVertRef 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), + bool (*func)(SculptSession *ss, SculptVertRef from_v, SculptVertRef to_v, bool is_duplicate, void *userdata), void *userdata); void SCULPT_floodfill_free(SculptFloodFill *flood); @@ -319,6 +325,11 @@ enum eDynTopoWarnFlag { DYNTOPO_WARN_MODIFIER = (1 << 3), }; +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, @@ -341,7 +352,7 @@ 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. */ @@ -515,7 +526,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, @@ -548,12 +559,17 @@ 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); +void SCULPT_neighbor_coords_average(SculptSession *ss, float result[3], SculptVertRef index); +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); + +void SCULPT_smooth_vcol_boundary( + Sculpt *sd, Object *ob, PBVHNode **nodes, const int totnode, float bstrength); void SCULPT_smooth(Sculpt *sd, Object *ob, @@ -569,13 +585,13 @@ void SCULPT_surface_smooth_laplacian_step(SculptSession *ss, float *disp, const float co[3], float (*laplacian_disp)[3], - const int v_index, + const SculptVertRef v_index, const float origco[3], const float alpha); void SCULPT_surface_smooth_displace_step(SculptSession *ss, float *co, float (*laplacian_disp)[3], - const int v_index, + 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 +650,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 +688,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,7 +806,7 @@ 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; @@ -827,6 +847,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 { @@ -863,7 +885,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. */ @@ -894,6 +916,7 @@ typedef struct AutomaskingSettings { /* Flags from eAutomasking_flag. */ int flags; int initial_face_set; + float concave_factor; } AutomaskingSettings; typedef struct AutomaskingCache { @@ -1138,7 +1161,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 @@ -1299,8 +1322,17 @@ void SCULPT_cache_calc_brushdata_symm(StrokeCache *cache, const float angle); void SCULPT_cache_free(StrokeCache *cache); +void SCULPT_orig_vert_data_init(SculptOrigVertData *data, + Object *ob, + PBVHNode *node, + SculptUndoType type); +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); + 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); @@ -1363,3 +1395,21 @@ void SCULPT_OT_set_detail_size(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); + +void SCULPT_update_flat_vcol_shading(struct Object *ob, struct Scene *scene); +float SCULPT_calc_concavity(SculptSession *ss, SculptVertRef vref); + + +typedef struct SculptCurvatureData { + float ks[3]; + float principle[3][3]; // normalized +} SculptCurvatureData; + +bool SCULPT_calc_principle_curvatures(SculptSession *ss, + SculptVertRef vertex, + SculptCurvatureData *out); + diff --git a/source/blender/editors/sculpt_paint/sculpt_mask_expand.c b/source/blender/editors/sculpt_paint/sculpt_mask_expand.c index 5e229e020ad..e453ae49e65 100644 --- a/source/blender/editors/sculpt_paint/sculpt_mask_expand.c +++ b/source/blender/editors/sculpt_paint/sculpt_mask_expand.c @@ -119,7 +119,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; } @@ -139,7 +139,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); } @@ -190,7 +190,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. */ @@ -312,10 +313,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; @@ -325,8 +329,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; @@ -415,13 +419,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)); @@ -440,9 +446,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_multiplane_scrape.c b/source/blender/editors/sculpt_paint/sculpt_multiplane_scrape.c index cfc31e1dcdd..a3216c856bc 100644 --- a/source/blender/editors/sculpt_paint/sculpt_multiplane_scrape.c +++ b/source/blender/editors/sculpt_paint/sculpt_multiplane_scrape.c @@ -87,7 +87,6 @@ static void calc_multiplane_scrape_surface_task_cb(void *__restrict userdata, BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { - if (!sculpt_brush_test_sq_fn(&test, vd.co)) { continue; } @@ -108,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. */ @@ -168,7 +167,6 @@ static void do_multiplane_scrape_brush_task_cb_ex(void *__restrict userdata, BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { - if (!sculpt_brush_test_sq_fn(&test, vd.co)) { continue; } @@ -217,7 +215,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 5fdf8415f28..1780d5db8fc 100644 --- a/source/blender/editors/sculpt_paint/sculpt_paint_color.c +++ b/source/blender/editors/sculpt_paint/sculpt_paint_color.c @@ -97,11 +97,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) { @@ -124,7 +124,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]); @@ -165,7 +166,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. */ @@ -366,6 +367,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, @@ -396,7 +401,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]; @@ -419,10 +424,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) { @@ -456,7 +461,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; } @@ -470,13 +475,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 a85f805894b..923c838f895 100644 --- a/source/blender/editors/sculpt_paint/sculpt_pose.c +++ b/source/blender/editors/sculpt_paint/sculpt_pose.c @@ -172,7 +172,7 @@ 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) { @@ -238,7 +238,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); } @@ -384,7 +384,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; @@ -415,10 +416,12 @@ typedef struct PoseFloodFillData { } PoseFloodFillData; static bool pose_topology_floodfill_cb( - SculptSession *ss, int UNUSED(from_v), int to_v, bool is_duplicate, void *userdata) + 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; @@ -444,14 +447,14 @@ static bool pose_topology_floodfill_cb( } static bool pose_face_sets_floodfill_cb( - SculptSession *ss, int UNUSED(from_v), int to_v, bool is_duplicate, void *userdata) + 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; @@ -465,11 +468,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; @@ -483,11 +486,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) { @@ -502,11 +505,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; } @@ -515,15 +518,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; @@ -533,7 +536,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; @@ -557,8 +560,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); @@ -610,7 +611,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++; } @@ -680,12 +681,14 @@ 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. */ @@ -770,7 +773,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++) { @@ -826,9 +830,11 @@ static SculptPoseIKChain *pose_ik_chain_init_face_sets( } static bool pose_face_sets_fk_find_masked_floodfill_cb( - SculptSession *ss, int from_v, int to_v, bool is_duplicate, void *userdata) + 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; @@ -837,11 +843,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)); @@ -856,14 +862,14 @@ 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) + 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); } @@ -874,7 +880,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; @@ -882,7 +890,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; @@ -895,9 +903,11 @@ 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++; } } @@ -906,10 +916,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_smooth.c b/source/blender/editors/sculpt_paint/sculpt_smooth.c index 4c0795eb0f7..bd591ff9ace 100644 --- a/source/blender/editors/sculpt_paint/sculpt_smooth.c +++ b/source/blender/editors/sculpt_paint/sculpt_smooth.c @@ -58,11 +58,18 @@ #include "RNA_define.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 index) { float avg[3] = {0.0f, 0.0f, 0.0f}; int total = 0; @@ -74,14 +81,14 @@ void SCULPT_neighbor_coords_average_interior(SculptSession *ss, float result[3], neighbor_count++; 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)); + if (SCULPT_vertex_is_boundary(ss, ni.vertex)) { + add_v3_v3(avg, SCULPT_vertex_co_get(ss, ni.vertex)); total++; } } else { /* Interior vertices use all neighbors. */ - add_v3_v3(avg, SCULPT_vertex_co_get(ss, ni.index)); + add_v3_v3(avg, SCULPT_vertex_co_get(ss, ni.vertex)); total++; } } @@ -152,14 +159,14 @@ void SCULPT_bmesh_four_neighbor_average(float avg[3], float direction[3], BMVert /* Generic functions for laplacian smoothing. These functions do not take boundary vertices into * account. */ -void SCULPT_neighbor_coords_average(SculptSession *ss, float result[3], int index) +void SCULPT_neighbor_coords_average(SculptSession *ss, float result[3], SculptVertRef index) { float avg[3] = {0.0f, 0.0f, 0.0f}; int total = 0; SculptVertexNeighborIter ni; SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, index, ni) { - add_v3_v3(avg, SCULPT_vertex_co_get(ss, ni.index)); + add_v3_v3(avg, SCULPT_vertex_co_get(ss, ni.vertex)); total++; } SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); @@ -172,14 +179,14 @@ void SCULPT_neighbor_coords_average(SculptSession *ss, float result[3], int inde } } -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 +197,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); @@ -231,6 +238,7 @@ static void do_enhance_details_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; } @@ -242,7 +250,7 @@ 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]; @@ -274,8 +282,10 @@ static void SCULPT_enhance_details_brush(Sculpt *sd, 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); + + SCULPT_neighbor_coords_average(ss, avg, vertex); + sub_v3_v3v3(ss->cache->detail_directions[i], avg, SCULPT_vertex_co_get(ss, vertex)); } } @@ -291,6 +301,88 @@ static void SCULPT_enhance_details_brush(Sculpt *sd, 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) +{ + 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); + + 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) @@ -325,17 +417,17 @@ 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); + SCULPT_neighbor_coords_average_interior(ss, avg, vd.vertex); sub_v3_v3v3(val, avg, vd.co); madd_v3_v3v3fl(val, vd.co, val, fade); SCULPT_clip(sd, ss, vd.co, val); @@ -346,6 +438,7 @@ static void do_smooth_brush_task_cb_ex(void *__restrict userdata, } BKE_pbvh_vertex_iter_end; } +#endif void SCULPT_smooth(Sculpt *sd, Object *ob, @@ -373,9 +466,18 @@ void SCULPT_smooth(Sculpt *sd, return; } - SCULPT_vertex_random_access_ensure(ss); + if (type != PBVH_BMESH) { + 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; @@ -391,6 +493,10 @@ void SCULPT_smooth(Sculpt *sd, TaskParallelSettings settings; BKE_pbvh_parallel_range_settings(&settings, true, totnode); 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 } } @@ -414,7 +520,7 @@ void SCULPT_surface_smooth_laplacian_step(SculptSession *ss, float *disp, const float co[3], float (*laplacian_disp)[3], - const int v_index, + const SculptVertRef v_index, const float origco[3], const float alpha) { @@ -422,10 +528,12 @@ void SCULPT_surface_smooth_laplacian_step(SculptSession *ss, float weigthed_o[3], weigthed_q[3], d[3]; SCULPT_neighbor_coords_average(ss, laplacian_smooth_co, v_index); + 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(laplacian_disp[index], laplacian_smooth_co, d); sub_v3_v3v3(disp, laplacian_smooth_co, co); } @@ -433,22 +541,25 @@ void SCULPT_surface_smooth_laplacian_step(SculptSession *ss, void SCULPT_surface_smooth_displace_step(SculptSession *ss, float *co, float (*laplacian_disp)[3], - const int v_index, + 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]); 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, laplacian_disp[index], beta); mul_v3_fl(b_current_vertex, clamp_f(fade, 0.0f, 1.0f)); sub_v3_v3(co, b_current_vertex); } @@ -471,11 +582,12 @@ 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]); + 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); + if (!sculpt_brush_test_sq_fn(&test, vd.co)) { continue; } @@ -486,12 +598,12 @@ 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); + ss, disp, vd.co, ss->cache->surface_smooth_laplacian_disp, vd.vertex, orig_data.co, alpha); madd_v3_v3fl(vd.co, disp, clamp_f(fade, 0.0f, 1.0f)); if (vd.mvert) { vd.mvert->flag |= ME_VERT_PBVH_UPDATE; @@ -528,10 +640,10 @@ 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); + ss, vd.co, ss->cache->surface_smooth_laplacian_disp, vd.vertex, beta, fade); } BKE_pbvh_vertex_iter_end; } @@ -564,3 +676,232 @@ 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; + } + + //#define SHARPEN_VCOL_BOUNDARY + + 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; + +#ifdef SHARPEN_VCOL_BOUNDARY + float ntot = 0.0f; + float colavg[4], tot3 = 0.0f; + zero_v4(colavg); +#endif + + 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); + + //w *= w; + +#ifdef SHARPEN_VCOL_BOUNDARY + float w2 = -1.0f; + madd_v4_v4fl(colavg, col, w2); + tot3 += w2; + ntot += 1.0f; +#endif + + 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); + } + +#ifdef SHARPEN_VCOL_BOUNDARY + float w2 = ntot*40.0f; + madd_v4_v4fl(colavg, vd.col, w2); + tot3 += w2; + + if (tot3 > 0.0f) { + mul_v4_fl(colavg, 1.0f / tot3); + } + + //clamp_v4(colavg, 0.0f, 1.0f); + //negative numbers are undesirable, but dunno if I should clip above 1.0 or not except for alpha + for (int i=0; i<4; i++) { + colavg[i] = MAX2(colavg[i], 0.0f); + colavg[i] = MIN2(colavg[i], 1.0f); + } + //colavg[3] = MIN2(colavg[3], 1.0f); + + interp_v4_v4v4(vd.col, vd.col, colavg, fade*0.25); +#endif + + //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; + } + + if (type != PBVH_BMESH) { + 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 4554ea178ab..934c0432633 100644 --- a/source/blender/editors/sculpt_paint/sculpt_transform.c +++ b/source/blender/editors/sculpt_paint/sculpt_transform.c @@ -159,7 +159,7 @@ 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; diff --git a/source/blender/editors/sculpt_paint/sculpt_undo.c b/source/blender/editors/sculpt_paint/sculpt_undo.c index ec103bd2b98..6df6f8aa705 100644 --- a/source/blender/editors/sculpt_paint/sculpt_undo.c +++ b/source/blender/editors/sculpt_paint/sculpt_undo.c @@ -116,6 +116,7 @@ typedef struct UndoSculpt { } UndoSculpt; static UndoSculpt *sculpt_undo_get_nodes(void); +void sculpt_undo_print_nodes(void *active); static void update_cb(PBVHNode *node, void *rebuild) { @@ -133,6 +134,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' */ @@ -180,14 +183,16 @@ static bool sculpt_undo_restore_deformed( return false; } -static bool sculpt_undo_restore_coords(bContext *C, Depsgraph *depsgraph, SculptUndoNode *unode) +static bool sculpt_undo_restore_coords(bContext *C, + Depsgraph *depsgraph, + SculptUndoNode *unode) { ViewLayer *view_layer = CTX_data_view_layer(C); Object *ob = OBACT(view_layer); SculptSession *ss = ob->sculpt; SubdivCCG *subdiv_ccg = ss->subdiv_ccg; MVert *mvert; - int *index; + SculptVertRef *index; if (unode->maxvert) { /* Regular mesh restore. */ @@ -221,18 +226,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 +254,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 +308,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 +335,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 +355,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 +365,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; } } } @@ -421,7 +426,7 @@ static void sculpt_undo_bmesh_restore_generic(SculptUndoNode *unode, Object *ob, unode->applied = true; } - if (unode->type == SCULPT_UNDO_MASK) { + if (unode->type == SCULPT_UNDO_MASK || unode->type == SCULPT_UNDO_COLOR) { int totnode; PBVHNode **nodes; @@ -460,6 +465,8 @@ static void sculpt_undo_bmesh_enable(Object *ob, SculptUndoNode *unode) /* Restore the BMLog using saved entries. */ ss->bm_log = BM_log_from_existing_entries_create(ss->bm, unode->bm_entry); + BM_log_set_cd_offsets( + ss->bm_log, ss->cd_origco_offset, ss->cd_origno_offset, ss->cd_origvcol_offset); } static void sculpt_undo_bmesh_restore_begin(bContext *C, @@ -499,6 +506,8 @@ static void sculpt_undo_bmesh_restore_end(bContext *C, SCULPT_dynamic_topology_disable(C, NULL); unode->applied = true; } + + BM_mesh_elem_index_ensure(ss->bm, BM_VERT); } static void sculpt_undo_geometry_store_data(SculptUndoNodeGeometry *geometry, Object *object) @@ -590,17 +599,25 @@ static int sculpt_undo_bmesh_restore(bContext *C, Object *ob, SculptSession *ss) { + if (ss->bm_log) { + BM_log_set_cd_offsets( + ss->bm_log, ss->cd_origco_offset, ss->cd_origno_offset, ss->cd_origvcol_offset); + } + switch (unode->type) { case SCULPT_UNDO_DYNTOPO_BEGIN: sculpt_undo_bmesh_restore_begin(C, unode, ob, ss); + SCULPT_vertex_random_access_ensure(ss); return true; case SCULPT_UNDO_DYNTOPO_END: sculpt_undo_bmesh_restore_end(C, unode, ob, ss); + SCULPT_vertex_random_access_ensure(ss); return true; default: if (ss->bm_log) { sculpt_undo_bmesh_restore_generic(unode, ob, ss); + SCULPT_vertex_random_access_ensure(ss); return true; } break; @@ -664,7 +681,7 @@ static void sculpt_undo_restore_list(bContext *C, Depsgraph *depsgraph, ListBase DEG_id_tag_update(&ob->id, ID_RECALC_SHADING); - if (lb->first) { + if (!ss->bm && lb->first) { unode = lb->first; if (unode->type == SCULPT_UNDO_FACE_SETS) { sculpt_undo_restore_face_sets(C, unode); @@ -719,6 +736,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 +877,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 +909,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 +949,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 +978,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() @@ -1084,7 +1139,7 @@ static SculptUndoNode *sculpt_undo_alloc_node(Object *ob, PBVHNode *node, Sculpt else { /* Regular mesh. */ unode->maxvert = ss->totvert; - + const size_t alloc_size = sizeof(*unode->index) * (size_t)allvert; unode->index = MEM_callocN(alloc_size, "SculptUndoNode.index"); usculpt->undo_size += alloc_size; @@ -1104,6 +1159,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); @@ -1115,7 +1173,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); + + 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; @@ -1159,6 +1221,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); @@ -1222,7 +1286,10 @@ static SculptUndoNode *sculpt_undo_bmesh_push(Object *ob, PBVHNode *node, Sculpt SculptUndoNode *unode = usculpt->nodes.first; + 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)); @@ -1230,7 +1297,7 @@ static SculptUndoNode *sculpt_undo_bmesh_push(Object *ob, PBVHNode *node, Sculpt unode->applied = true; if (type == SCULPT_UNDO_DYNTOPO_END) { - unode->bm_entry = BM_log_entry_add(ss->bm_log); + unode->bm_entry = BM_log_entry_add(ss->bm, ss->bm_log); BM_log_before_all_removed(ss->bm, ss->bm_log); } else if (type == SCULPT_UNDO_DYNTOPO_BEGIN) { @@ -1242,17 +1309,21 @@ static SculptUndoNode *sculpt_undo_bmesh_push(Object *ob, PBVHNode *node, Sculpt SculptUndoNodeGeometry *geometry = &unode->geometry_bmesh_enter; sculpt_undo_geometry_store_data(geometry, ob); - unode->bm_entry = BM_log_entry_add(ss->bm_log); + unode->bm_entry = BM_log_entry_add(ss->bm, ss->bm_log); BM_log_all_added(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: @@ -1260,41 +1331,121 @@ static SculptUndoNode *sculpt_undo_bmesh_push(Object *ob, PBVHNode *node, Sculpt * 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); + void *dummy; + BKE_pbvh_bmesh_update_origvert(ss->pbvh, vd.bm_vert, &dummy, &dummy, &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); + TableGSet *faces = BKE_pbvh_bmesh_node_faces(node); + BMFace *f; + 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); + BM_log_vert_before_modified(ss->bm_log, vd.bm_vert, vd.cd_vert_mask_offset, true); + // BKE_pbvh_bmesh_update_origvert(ss->pbvh, vd.bm_vert, &dummy, &dummy, &dummy); } 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: { +#if 1 + BKE_pbvh_vertex_iter_begin(ss->pbvh, node, vd, PBVH_ITER_ALL) + { + void *dummy; + BKE_pbvh_bmesh_update_origvert(ss->pbvh, vd.bm_vert, NULL, NULL, &dummy); + } + BKE_pbvh_vertex_iter_end; +#endif + 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; } } + if (new_node) { + sculpt_undo_print_nodes(NULL); + } + return unode; } -SculptUndoNode *SCULPT_undo_push_node(Object *ob, PBVHNode *node, SculptUndoType type) +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_index(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; +} + +SculptUndoNode *SCULPT_undo_push_node(Object *ob, + PBVHNode *node, + SculptUndoType type) { SculptSession *ss = ob->sculpt; SculptUndoNode *unode; @@ -1308,20 +1459,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; } @@ -1343,7 +1498,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) { @@ -1380,6 +1539,8 @@ 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; @@ -1441,6 +1602,7 @@ typedef struct SculptUndoStep { UndoStep step; /* Note: will split out into list for multi-object-sculpt-mode. */ UndoSculpt data; + int id; } SculptUndoStep; static void sculpt_undosys_step_encode_init(struct bContext *UNUSED(C), UndoStep *us_p) @@ -1479,6 +1641,8 @@ static void sculpt_undosys_step_decode_undo_impl(struct bContext *C, BLI_assert(us->step.is_applied == true); sculpt_undo_restore_list(C, depsgraph, &us->data.nodes); us->step.is_applied = false; + + sculpt_undo_print_nodes(us); } static void sculpt_undosys_step_decode_redo_impl(struct bContext *C, @@ -1488,6 +1652,8 @@ static void sculpt_undosys_step_decode_redo_impl(struct bContext *C, BLI_assert(us->step.is_applied == false); sculpt_undo_restore_list(C, depsgraph, &us->data.nodes); us->step.is_applied = true; + + sculpt_undo_print_nodes(us); } static void sculpt_undosys_step_decode_undo(struct bContext *C, @@ -1727,3 +1893,106 @@ 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*/); +} + +static void print_sculpt_undo_step(UndoStep *us, UndoStep *active, int i) +{ + SculptUndoNode *node; + + if (us->type != BKE_UNDOSYS_TYPE_SCULPT) { + return; + } + + int id = -1; + + SculptUndoStep *su = (SculptUndoStep *)us; + if (!su->id) { + su->id = nodeidgen++; + } + + id = su->id; + + printf("id=%d %s %d %s\n", id, us == active ? "->" : " ", i, us->name); + + 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 + 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; + for (; us; us = us->next, i++) { + print_sculpt_undo_step(us, active, i); + } + + if (ustack->step_active) { + print_sculpt_undo_step(ustack->step_active, active, i); + } + +#endif +} diff --git a/source/blender/editors/uvedit/uvedit_unwrap_ops.c b/source/blender/editors/uvedit/uvedit_unwrap_ops.c index fc5f41e8ed5..42a54010ceb 100644 --- a/source/blender/editors/uvedit/uvedit_unwrap_ops.c +++ b/source/blender/editors/uvedit/uvedit_unwrap_ops.c @@ -2860,7 +2860,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, @@ -2871,7 +2872,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/gpu/GPU_buffers.h b/source/blender/gpu/GPU_buffers.h index b770bde65fc..50262148eb1 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,17 @@ 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); 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, + const int update_flags, + const int cd_vert_node_offset, + int face_sets_color_seed, + int face_sets_color_default, + bool flat_vcol); void GPU_pbvh_grid_buffers_update(GPU_PBVH_Buffers *buffers, struct SubdivCCG *subdiv_ccg, diff --git a/source/blender/gpu/GPU_vertex_format.h b/source/blender/gpu/GPU_vertex_format.h index e3a566e5f21..84b43d8060b 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 13d0139e406..3cf7079560d 100644 --- a/source/blender/gpu/intern/gpu_buffers.c +++ b/source/blender/gpu/intern/gpu_buffers.c @@ -42,6 +42,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,6 +51,8 @@ #include "GPU_batch.h" #include "GPU_buffers.h" +#include "DRW_engine.h" + #include "gpu_private.h" #include "bmesh.h" @@ -100,7 +103,7 @@ struct GPU_PBVH_Buffers { static struct { GPUVertFormat format; - uint pos, nor, msk, col, fset; + uint pos, nor, msk, col, fset, uv; } g_vbo_id = {{0}}; /** \} */ @@ -111,20 +114,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); } void gpu_pbvh_exit() @@ -787,15 +777,17 @@ GPU_PBVH_Buffers *GPU_pbvh_grid_buffers_build(int totgrid, BLI_bitmap **grid_hid * \{ */ /* Output a BMVert into a VertexBufferFormat array at v_index. */ -static void gpu_bmesh_vert_to_buffer_copy(BMVert *v, +__attribute__((optnone)) static void gpu_bmesh_vert_to_buffer_copy(BMVert *v, GPUVertBuf *vert_buf, int v_index, 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, + int cd_vcol_offset) { /* Vertex should always be visible if it's used by a visible face. */ BLI_assert(!BM_elem_flag_test(v, BM_ELEM_HIDDEN)); @@ -809,14 +801,54 @@ static void gpu_bmesh_vert_to_buffer_copy(BMVert *v, 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 * 50) % 32) / 32.0f; + } + 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) { + if (show_vcol && cd_vcol_offset >= 0) { + ushort vcol[4] = {USHRT_MAX, USHRT_MAX, USHRT_MAX, USHRT_MAX}; + int col[4] = {0, 0, 0, 0}; + int tot = 0; + + BMIter iter; + BMLoop *l; + + BM_ITER_ELEM (l, &iter, v, BM_LOOPS_OF_VERT) { + MLoopCol *ml = BM_ELEM_CD_GET_VOID_P(l, cd_vcol_offset); + + col[0] += ml->r; + col[1] += ml->g; + col[2] += ml->b; + col[3] += ml->a; + tot++; + } + + if (tot) { + col[0] /= tot; + col[1] /= tot; + col[2] /= tot; + col[3] /= tot; + + vcol[0] = (ushort)(col[0] * 257); + vcol[1] = (ushort)(col[1] * 257); + vcol[2] = (ushort)(col[2] * 257); + vcol[3] = (ushort)(col[3] * 257); + // printf("%d %d %d %d %d\n", vcol[0], vcol[1], vcol[2], vcol[3], tot); + } + // 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); + } + else if (show_vcol) { 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, v_index, vcol); } /* Add default face sets color to avoid artifacts. */ @@ -825,40 +857,40 @@ static void gpu_bmesh_vert_to_buffer_copy(BMVert *v, } /* 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) { - GSetIterator gh_iter; int totface = 0; + BMFace *f; - GSET_ITER (gh_iter, bm_faces) { - BMFace *f = BLI_gsetIterator_getKey(&gh_iter); - + TGSET_ITER (f, bm_faces) { if (!BM_elem_flag_test(f, BM_ELEM_HIDDEN)) { totface++; } } + TGSET_ITER_END return totface; } @@ -878,25 +910,290 @@ void GPU_pbvh_bmesh_buffers_update_free(GPU_PBVH_Buffers *buffers) } } +void GPU_pbvh_update_attribute_names(CustomData *vdata, CustomData *ldata) +{ + GPU_vertformat_clear(&g_vbo_id); + + /* 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, "c", GPU_COMP_U16, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); + + if (vdata && CustomData_has_layer(vdata, CD_PROP_COLOR)) { + const int cd_vcol_index = CustomData_get_layer_index(vdata, CD_PROP_COLOR); + CustomDataLayer *cl = vdata->layers + cd_vcol_index; + cl += cl->active; + + DRW_make_cdlayer_attr_aliases(&g_vbo_id.format, "c", vdata, cl); + GPU_vertformat_alias_add(&g_vbo_id.format, "ac"); + } + + g_vbo_id.fset = GPU_vertformat_attr_add( + &g_vbo_id.format, "fset", GPU_COMP_U8, 3, GPU_FETCH_INT_TO_FLOAT_UNIT); + + 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"); + + if (ldata && CustomData_has_layer(ldata, CD_MLOOPUV)) { + 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); + } + } +} + +static void gpu_flat_vcol_make_vert(float co[3], + BMVert *v, + GPUVertBuf *vert_buf, + int v_index, + int cd_vcol_offset, + const float fno[3]) +{ + MPropCol *mp = BM_ELEM_CD_GET_VOID_P(v, cd_vcol_offset); + 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, 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! */ +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, + const int update_flags, + const int cd_vert_node_offset, + int face_sets_color_seed, + int face_sets_color_default) +{ + const bool have_uv = CustomData_has_layer(&bm->ldata, CD_MLOOPUV); + const bool show_mask = (update_flags & GPU_PBVH_BUFFERS_SHOW_MASK) != 0; + const bool show_vcol = (update_flags & GPU_PBVH_BUFFERS_SHOW_VCOL) != 0; + 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_vcol_offset = CustomData_get_offset(&bm->vdata, CD_PROP_COLOR); + int cd_fset_offset = CustomData_get_offset(&bm->pdata, CD_SCULPT_FACE_SETS); + + /* Count visible triangles */ + tottri = gpu_bmesh_face_visible_count(bm_faces) * 6; + + // XXX disable indexed verts for now + totvert = tottri * 3; + + if (!tottri) { + if (BLI_table_gset_len(bm_faces) != 0) { + /* Node is just hidden. */ + } + else { + buffers->clear_bmesh_on_flush = true; + } + 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); + 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; + + /* Fill vertex buffer */ + if (!gpu_pbvh_vert_buf_data_set(buffers, totvert)) { + /* Memory map failed */ + return; + } + + int v_index = 0; + + // disable shared vertex mode for now + + 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 (!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; + + /* + + + v1 + |\ + | \ + v3 v4 + | v6 \ + | \ + v0---v5---v2 + */ + 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}; + + 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); + + float v3[3]; + float v4[3]; + float v5[3]; + float v6[3]; + + 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); + + 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_vcol_offset, f->no); + gpu_flat_vcol_make_vert( + cos[next], v[j], buffers->vert_buf, v_index + 1, cd_vcol_offset, f->no); + gpu_flat_vcol_make_vert( + cos[6], v[j], buffers->vert_buf, v_index + 2, cd_vcol_offset, f->no); + + gpu_flat_vcol_make_vert( + v[j]->co, v[j], buffers->vert_buf, v_index + 3, cd_vcol_offset, f->no); + gpu_flat_vcol_make_vert( + cos[6], v[j], buffers->vert_buf, v_index + 4, cd_vcol_offset, f->no); + gpu_flat_vcol_make_vert( + cos[prev], v[j], buffers->vert_buf, v_index + 5, cd_vcol_offset, f->no); + + v_index += 6; + } + /* + 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); + } + */ + } + } + TGSET_ITER_END + + 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 = (f) ? f->mat_nr : 0; + + buffers->show_overlay = !empty_mask || !default_face_set; + + gpu_pbvh_batch_init(buffers, GPU_PRIM_TRIS); +} + /* 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) + TableGSet *bm_faces, + TableGSet *bm_unique_verts, + TableGSet *bm_other_verts, + const int update_flags, + const int cd_vert_node_offset, + int face_sets_color_seed, + int face_sets_color_default, + bool flat_vcol) { + + 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, + update_flags, + cd_vert_node_offset, + face_sets_color_seed, + face_sets_color_default); + return; + } + + const bool have_uv = CustomData_has_layer(&bm->ldata, CD_MLOOPUV); const bool show_mask = (update_flags & GPU_PBVH_BUFFERS_SHOW_MASK) != 0; const bool show_vcol = (update_flags & GPU_PBVH_BUFFERS_SHOW_VCOL) != 0; + 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_vcol_offset = CustomData_get_offset(&bm->vdata, CD_PROP_COLOR); + int cd_fset_offset = CustomData_get_offset(&bm->pdata, CD_SCULPT_FACE_SETS); /* Count visible triangles */ tottri = gpu_bmesh_face_visible_count(bm_faces); + // XXX disable indexed verts for now if (buffers->smooth) { /* Count visible vertices */ totvert = gpu_bmesh_vert_visible_count(bm_unique_verts, bm_other_verts); @@ -906,7 +1203,7 @@ void GPU_pbvh_bmesh_buffers_update(GPU_PBVH_Buffers *buffers, } if (!tottri) { - if (BLI_gset_len(bm_faces) != 0) { + if (BLI_table_gset_len(bm_faces) != 0) { /* Node is just hidden. */ } else { @@ -918,6 +1215,10 @@ 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); + 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; /* Fill vertex buffer */ if (!gpu_pbvh_vert_buf_data_set(buffers, totvert)) { @@ -927,6 +1228,7 @@ void GPU_pbvh_bmesh_buffers_update(GPU_PBVH_Buffers *buffers, int v_index = 0; + // disable shared vertex mode for now if (buffers->smooth) { /* Fill the vertex and triangle buffer in one pass over faces. */ GPUIndexBufBuilder elb, elb_lines; @@ -934,11 +1236,9 @@ void GPU_pbvh_bmesh_buffers_update(GPU_PBVH_Buffers *buffers, GPU_indexbuf_init(&elb_lines, GPU_PRIM_LINES, tottri * 3, totvert); GHash *bm_vert_to_index = BLI_ghash_int_new_ex("bm_vert_to_index", totvert); + BMFace *f; - GSetIterator gs_iter; - GSET_ITER (gs_iter, bm_faces) { - f = BLI_gsetIterator_getKey(&gs_iter); - + TGSET_ITER (f, bm_faces) { if (!BM_elem_flag_test(f, BM_ELEM_HIDDEN)) { BMVert *v[3]; BM_face_as_array_vert_tri(f, v); @@ -956,9 +1256,11 @@ void GPU_pbvh_bmesh_buffers_update(GPU_PBVH_Buffers *buffers, NULL, NULL, cd_vert_mask_offset, + cd_vert_node_offset, show_mask, show_vcol, - &empty_mask); + &empty_mask, + cd_mcol_offset); idx[i] = v_index; v_index++; @@ -976,6 +1278,7 @@ void GPU_pbvh_bmesh_buffers_update(GPU_PBVH_Buffers *buffers, GPU_indexbuf_add_line_verts(&elb_lines, idx[2], idx[0]); } } + TGSET_ITER_END BLI_ghash_free(bm_vert_to_index, NULL, NULL); @@ -989,18 +1292,17 @@ void GPU_pbvh_bmesh_buffers_update(GPU_PBVH_Buffers *buffers, buffers->index_lines_buf = GPU_indexbuf_build(&elb_lines); } else { - GSetIterator gs_iter; - GPUIndexBufBuilder elb_lines; GPU_indexbuf_init(&elb_lines, GPU_PRIM_LINES, tottri * 3, tottri * 3); + BMFace *f; - GSET_ITER (gs_iter, bm_faces) { - f = BLI_gsetIterator_getKey(&gs_iter); - + TGSET_ITER (f, bm_faces) { BLI_assert(f->len == 3); 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; @@ -1016,19 +1318,71 @@ void GPU_pbvh_bmesh_buffers_update(GPU_PBVH_Buffers *buffers, 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}; + + 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++, - f->no, + v_index, + no, &fmask, cd_vert_mask_offset, + cd_vert_node_offset, show_mask, - show_vcol, - &empty_mask); + false, + &empty_mask, + -1); + + if (cd_vcol_offset >= 0) { + MPropCol *mp = BM_ELEM_CD_GET_VOID_P(l[i]->v, cd_vcol_offset); + 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, 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, 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 buffers->index_lines_buf = GPU_indexbuf_build(&elb_lines); buffers->tot_tri = tottri; @@ -1037,13 +1391,11 @@ void GPU_pbvh_bmesh_buffers_update(GPU_PBVH_Buffers *buffers, /* Get material index from the last face we iterated on. */ buffers->material_index = (f) ? f->mat_nr : 0; - buffers->show_overlay = !empty_mask; + buffers->show_overlay = !empty_mask || !default_face_set; gpu_pbvh_batch_init(buffers, GPU_PRIM_TRIS); } -/** \} */ - /* -------------------------------------------------------------------- */ /** \name Generic * \{ */ diff --git a/source/blender/io/alembic/exporter/abc_writer_mesh.cc b/source/blender/io/alembic/exporter/abc_writer_mesh.cc index fbc662113cc..151792f5ed6 100644 --- a/source/blender/io/alembic/exporter/abc_writer_mesh.cc +++ b/source/blender/io/alembic/exporter/abc_writer_mesh.cc @@ -189,7 +189,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 bba50064879..ceeffe070df 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_enums.h b/source/blender/makesdna/DNA_brush_enums.h index 093a1a00ece..396d248014d 100644 --- a/source/blender/makesdna/DNA_brush_enums.h +++ b/source/blender/makesdna/DNA_brush_enums.h @@ -328,6 +328,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 { @@ -462,6 +464,7 @@ typedef enum eBrushSculptTool { SCULPT_TOOL_BOUNDARY = 30, SCULPT_TOOL_DISPLACEMENT_ERASER = 31, SCULPT_TOOL_DISPLACEMENT_SMEAR = 32, + SCULPT_TOOL_VCOL_BOUNDARY = 33, } eBrushSculptTool; /* Brush.uv_sculpt_tool */ @@ -471,6 +474,11 @@ 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, \ @@ -500,7 +508,6 @@ typedef enum eBrushUVSculptTool { 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, \ @@ -512,6 +519,7 @@ 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_MASK) == 0) #define SCULPT_TOOL_HAS_TOPOLOGY_RAKE(t) \ diff --git a/source/blender/makesdna/DNA_brush_types.h b/source/blender/makesdna/DNA_brush_types.h index a11e7d77c67..ab636623390 100644 --- a/source/blender/makesdna/DNA_brush_types.h +++ b/source/blender/makesdna/DNA_brush_types.h @@ -243,7 +243,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; @@ -276,6 +276,8 @@ typedef struct Brush { float tilt_strength_factor; float topology_rake_factor; + float vcol_boundary_factor; + float vcol_boundary_exponent; float crease_pinch_factor; @@ -370,8 +372,8 @@ typedef struct Brush { float mask_stencil_pos[2]; float mask_stencil_dimension[2]; + float concave_mask_factor; struct BrushGpencilSettings *gpencil_settings; - } Brush; /* Struct to hold palette colors for sorting. */ diff --git a/source/blender/makesdna/DNA_modifier_types.h b/source/blender/makesdna/DNA_modifier_types.h index dbcb6ce45ea..c5e2397b995 100644 --- a/source/blender/makesdna/DNA_modifier_types.h +++ b/source/blender/makesdna/DNA_modifier_types.h @@ -1899,6 +1899,7 @@ typedef struct UVWarpModifierData { /* UVWarp modifier flags */ enum { MOD_UVWARP_INVERT_VGROUP = 1 << 0, + MOD_UVWARP_RESTRICT_ISLANDS = 1<<1 }; /* cache modifier */ diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h index c7f7e610a1a..97914e6cc06 100644 --- a/source/blender/makesdna/DNA_scene_types.h +++ b/source/blender/makesdna/DNA_scene_types.h @@ -2214,6 +2214,7 @@ typedef enum eSculptFlags { /* Don't display face sets in viewport. */ SCULPT_HIDE_FACE_SETS = (1 << 17), + SCULPT_DYNTOPO_FLAT_VCOL_SHADING = (1<<18) } eSculptFlags; /** #ImagePaintSettings.mode */ diff --git a/source/blender/makesrna/intern/rna_brush.c b/source/blender/makesrna/intern/rna_brush.c index e7daa55af6c..d603fea5a4a 100644 --- a/source/blender/makesrna/intern/rna_brush.c +++ b/source/blender/makesrna/intern/rna_brush.c @@ -135,10 +135,11 @@ const EnumPropertyItem rna_enum_brush_sculpt_tool_items[] = { {SCULPT_TOOL_MASK, "MASK", ICON_BRUSH_MASK, "Mask", ""}, {SCULPT_TOOL_DISPLACEMENT_ERASER, "DISPLACEMENT_ERASER", ICON_BRUSH_SCULPT_DRAW, "Multires Displacement Eraser", ""}, {SCULPT_TOOL_DISPLACEMENT_SMEAR, "DISPLACEMENT_SMEAR", ICON_BRUSH_SCULPT_DRAW, "Multires Displacement Smear", ""}, - {SCULPT_TOOL_PAINT, "PAINT", ICON_BRUSH_SCULPT_DRAW, "Paint", ""}, + {SCULPT_TOOL_PAINT, "PAINT", ICON_BRUSH_PAINT, "Paint", ""}, {SCULPT_TOOL_SMEAR, "SMEAR", ICON_BRUSH_SCULPT_DRAW, "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", ""}, + {0, NULL, 0, NULL, NULL}, }; /* clang-format on */ @@ -370,6 +371,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; @@ -1150,6 +1157,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"); @@ -2856,6 +2864,15 @@ static void rna_def_brush(BlenderRNA *brna) 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, "concave_mask_factor", PROP_FLOAT, PROP_FACTOR); + RNA_def_property_float_sdna(prop, NULL, "concave_mask_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, "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); @@ -2868,6 +2885,27 @@ 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, "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, "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); @@ -2992,6 +3030,16 @@ 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_topology", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "automasking_flags", BRUSH_AUTOMASKING_TOPOLOGY); RNA_def_property_ui_text(prop, diff --git a/source/blender/makesrna/intern/rna_modifier.c b/source/blender/makesrna/intern/rna_modifier.c index 5008240ea33..2635d8b9669 100644 --- a/source/blender/makesrna/intern/rna_modifier.c +++ b/source/blender/makesrna/intern/rna_modifier.c @@ -4975,6 +4975,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"); diff --git a/source/blender/makesrna/intern/rna_sculpt_paint.c b/source/blender/makesrna/intern/rna_sculpt_paint.c index 4481555b931..166d03367c2 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,15 @@ 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); } + + DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); + WM_main_add_notifier(NC_OBJECT | ND_MODIFIER | ND_DRAW, ob); } } @@ -818,6 +822,16 @@ 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_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, diff --git a/source/blender/modifiers/intern/MOD_bevel.c b/source/blender/modifiers/intern/MOD_bevel.c index a94411d897e..3a4287ce35a 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.c b/source/blender/modifiers/intern/MOD_boolean.c index 070ba3a1bcf..6242e26e251 100644 --- a/source/blender/modifiers/intern/MOD_boolean.c +++ b/source/blender/modifiers/intern/MOD_boolean.c @@ -1,21 +1,21 @@ /* - * 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. - */ + * 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 @@ -256,7 +256,8 @@ static BMesh *BMD_mesh_bm_create( .use_toolflags = false, })); - BM_mesh_bm_from_me(bm, + BM_mesh_bm_from_me(NULL, + bm, mesh_operand_ob, &((struct BMeshFromMeshParams){ .calc_face_normal = true, @@ -271,7 +272,8 @@ static BMesh *BMD_mesh_bm_create( } } - BM_mesh_bm_from_me(bm, + BM_mesh_bm_from_me(NULL, + bm, mesh, &((struct BMeshFromMeshParams){ .calc_face_normal = true, @@ -499,7 +501,8 @@ static Mesh *collection_boolean_exact(BooleanModifierData *bmd, Mesh *me = meshes[i]; Object *ob = objects[i]; /* Need normals for triangulation. */ - BM_mesh_bm_from_me(bm, + BM_mesh_bm_from_me(NULL, + bm, me, &((struct BMeshFromMeshParams){ .calc_face_normal = true, @@ -793,6 +796,7 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * /* Needed for multiple objects to work. */ BM_mesh_bm_to_me(NULL, + NULL, bm, mesh, (&(struct BMeshToMeshParams){ diff --git a/source/blender/modifiers/intern/MOD_decimate.c b/source/blender/modifiers/intern/MOD_decimate.c index b28b8fd39fc..8f9da001757 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 e02befd7efa..5e9a51adc36 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 4c1179af431..eca9e4811b1 100644 --- a/source/blender/modifiers/intern/MOD_particlesystem.c +++ b/source/blender/modifiers/intern/MOD_particlesystem.c @@ -256,7 +256,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_triangulate.c b/source/blender/modifiers/intern/MOD_triangulate.c index 04d24ac0883..6d5fe17104c 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 77b79167c2f..cc845854956 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 3d8e74d2cf5..d70a0c80c17 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_object_defgroup_name_index(ob, 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_boolean.cc b/source/blender/nodes/geometry/nodes/node_geo_boolean.cc index 38215b54640..c9e8e9a8dd0 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_boolean.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_boolean.cc @@ -69,8 +69,8 @@ static Mesh *mesh_boolean_calc(const Mesh *mesh_a, const Mesh *mesh_b, int boole { struct BMeshFromMeshParams bmesh_from_mesh_params = {0}; bmesh_from_mesh_params.calc_face_normal = true; - BM_mesh_bm_from_me(bm, mesh_b, &bmesh_from_mesh_params); - BM_mesh_bm_from_me(bm, mesh_a, &bmesh_from_mesh_params); + BM_mesh_bm_from_me(NULL, bm, mesh_b, &bmesh_from_mesh_params); + BM_mesh_bm_from_me(NULL, bm, mesh_a, &bmesh_from_mesh_params); } const int looptris_tot = poly_to_tri_count(bm->totface, bm->totloop); diff --git a/source/blender/python/bmesh/bmesh_py_types.c b/source/blender/python/bmesh/bmesh_py_types.c index 5fca6f4cec6..6375c1a1d37 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 */ @@ -1167,7 +1167,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, @@ -1228,7 +1229,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, diff --git a/source/blender/render/RE_render_ext.h b/source/blender/render/RE_render_ext.h new file mode 100644 index 00000000000..b394cfeee75 --- /dev/null +++ b/source/blender/render/RE_render_ext.h @@ -0,0 +1,121 @@ +/* + * 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) 2006 by Blender Foundation + * All rights reserved. + */ +/** \file + * \ingroup render + */ + +#pragma once + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ +/* this include is for non-render pipeline exports (still old cruft here) */ +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +/* called by meshtools */ +struct Depsgraph; +struct ImagePool; +struct MTex; +struct Tex; + +#ifdef __cplusplus +extern "C" { +#endif + +/* texture_procedural.c */ +bool RE_texture_evaluate(const struct MTex *mtex, + const float vec[3], + const int thread, + struct ImagePool *pool, + const bool skip_load_image, + const bool texnode_preview, + /* Return arguments. */ + float *r_intensity, + float r_rgba[4]) ATTR_NONNULL(1, 2, 7, 8); + +void texture_rgb_blend( + float in[3], const float tex[3], const float out[3], float fact, float facg, int blendtype); +float texture_value_blend(float tex, float out, float fact, float facg, int blendtype); + +void RE_texture_rng_init(void); +void RE_texture_rng_exit(void); + +/* texture_image.c */ +void ibuf_sample(struct ImBuf *ibuf, float fx, float fy, float dx, float dy, float result[4]); + +/* texture_pointdensity.c */ +struct PointDensity; + +void RE_point_density_cache(struct Depsgraph *depsgraph, struct PointDensity *pd); + +void RE_point_density_minmax(struct Depsgraph *depsgraph, + struct PointDensity *pd, + float r_min[3], + float r_max[3]); + +void RE_point_density_sample(struct Depsgraph *depsgraph, + struct PointDensity *pd, + const int resolution, + float *values); + +void RE_point_density_free(struct PointDensity *pd); + +void RE_point_density_fix_linking(void); + +/* texture_procedural.c */ + +/* Texture evaluation result. + * Note; tr tg tb ta has to remain in this order for array access. */ +typedef struct TexResult { + float tin, tr, tg, tb, ta; + int talpha; + float *nor; +} TexResult; + +/* This one uses nodes. */ +int multitex_ext(struct Tex *tex, + float texvec[3], + float dxt[3], + float dyt[3], + int osatex, + struct TexResult *texres, + const short thread, + struct ImagePool *pool, + bool scene_color_manage, + const bool skip_load_image); +/* Nodes disabled. */ +int multitex_ext_safe(struct Tex *tex, + const float texvec[3], + struct TexResult *texres, + struct ImagePool *pool, + bool scene_color_manage, + const bool skip_load_image); +/* Only for internal node usage. */ +int multitex_nodes(struct Tex *tex, + const float texvec[3], + float dxt[3], + float dyt[3], + int osatex, + struct TexResult *texres, + const short thread, + short which_output, + struct MTex *mtex, + struct ImagePool *pool); + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/render/RE_shader_ext.h b/source/blender/render/RE_shader_ext.h new file mode 100644 index 00000000000..d9d5a924949 --- /dev/null +++ b/source/blender/render/RE_shader_ext.h @@ -0,0 +1,83 @@ +/* + * 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) 2006 by Blender Foundation + * All rights reserved. + */ +/** \file + * \ingroup render + */ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ +/* this include is for texture exports */ +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +/* localized texture result data */ +/* note; tr tg tb ta has to remain in this order */ +typedef struct TexResult { + float tin, tr, tg, tb, ta; + int talpha; + float *nor; +} TexResult; + +typedef struct BakeImBufuserData { + float *displacement_buffer; + char *mask_buffer; +} BakeImBufuserData; + +/* node shaders... */ +struct ImagePool; +struct MTex; +struct Tex; + +/* this one uses nodes */ +int multitex_ext(struct Tex *tex, + float texvec[3], + float dxt[3], + float dyt[3], + int osatex, + struct TexResult *texres, + const short thread, + struct ImagePool *pool, + bool scene_color_manage, + const bool skip_load_image); +/* nodes disabled */ +int multitex_ext_safe(struct Tex *tex, + const float texvec[3], + struct TexResult *texres, + struct ImagePool *pool, + bool scene_color_manage, + const bool skip_load_image); +/* only for internal node usage */ +int multitex_nodes(struct Tex *tex, + const float texvec[3], + float dxt[3], + float dyt[3], + int osatex, + struct TexResult *texres, + const short thread, + short which_output, + struct MTex *mtex, + struct ImagePool *pool); + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/render/intern/bake_api.c b/source/blender/render/intern/bake_api.c new file mode 100644 index 00000000000..6f5db4986f2 --- /dev/null +++ b/source/blender/render/intern/bake_api.c @@ -0,0 +1,1070 @@ +/* + * 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 render + * + * \brief The API itself is simple. + * Blender sends a populated array of BakePixels to the renderer, + * and gets back an array of floats with the result. + * + * \section bake_api Development Notes for External Engines + * + * The Bake API is fully implemented with Python rna functions. + * The operator expects/call a function: + * + * ``def bake(scene, object, pass_type, object_id, pixel_array, num_pixels, depth, result)`` + * - scene: current scene (Python object) + * - object: object to render (Python object) + * - pass_type: pass to render (string, e.g., "COMBINED", "AO", "NORMAL", ...) + * - object_id: index of object to bake (to use with the pixel_array) + * - pixel_array: list of primitive ids and barycentric coordinates to + * `bake(Python object, see bake_pixel)`. + * - num_pixels: size of pixel_array, number of pixels to bake (int) + * - depth: depth of pixels to return (int, assuming always 4 now) + * - result: array to be populated by the engine (float array, PyLong_AsVoidPtr) + * + * \note Normals are expected to be in World Space and in the +X, +Y, +Z orientation. + * + * \subsection bake_pixel BakePixel data structure + * + * pixel_array is a Python object storing BakePixel elements: + * + * \code{.c} + * struct BakePixel { + * int primitive_id, object_id; + * float uv[2]; + * float du_dx, du_dy; + * float dv_dx, dv_dy; + * }; + * \endcode + * + * In python you have access to: + * - ``primitive_id``, ``object_id``, ``uv``, ``du_dx``, ``du_dy``, ``next`` + * - ``next()`` is a function that returns the next #BakePixel in the array. + * + * \note Pixels that should not be baked have ``primitive_id == -1`` + * + * For a complete implementation example look at the Cycles Bake commit. + */ + +#include <limits.h> + +#include "MEM_guardedalloc.h" + +#include "BLI_math.h" + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" + +#include "BKE_bvhutils.h" +#include "BKE_customdata.h" +#include "BKE_image.h" +#include "BKE_lib_id.h" +#include "BKE_mesh.h" +#include "BKE_mesh_runtime.h" +#include "BKE_mesh_tangent.h" +#include "BKE_node.h" + +#include "IMB_imbuf.h" +#include "IMB_imbuf_types.h" + +#include "RE_bake.h" + +/* local include */ +#include "render_types.h" +#include "zbuf.h" + +typedef struct BakeDataZSpan { + BakePixel *pixel_array; + int primitive_id; + BakeImage *bk_image; + ZSpan *zspan; + float du_dx, du_dy; + float dv_dx, dv_dy; +} BakeDataZSpan; + +/** + * struct wrapping up tangent space data + */ +typedef struct TSpace { + float tangent[3]; + float sign; +} TSpace; + +typedef struct TriTessFace { + const MVert *mverts[3]; + const TSpace *tspace[3]; + float *loop_normal[3]; + float normal[3]; /* for flat faces */ + bool is_smooth; +} TriTessFace; + +static void store_bake_pixel(void *handle, int x, int y, float u, float v) +{ + BakeDataZSpan *bd = (BakeDataZSpan *)handle; + BakePixel *pixel; + + const int width = bd->bk_image->width; + const size_t offset = bd->bk_image->offset; + const int i = offset + y * width + x; + + pixel = &bd->pixel_array[i]; + pixel->primitive_id = bd->primitive_id; + + /* At this point object_id is always 0, since this function runs for the + * low-poly mesh only. The object_id lookup indices are set afterwards. */ + + copy_v2_fl2(pixel->uv, u, v); + + pixel->du_dx = bd->du_dx; + pixel->du_dy = bd->du_dy; + pixel->dv_dx = bd->dv_dx; + pixel->dv_dy = bd->dv_dy; + pixel->object_id = 0; +} + +void RE_bake_mask_fill(const BakePixel pixel_array[], const size_t num_pixels, char *mask) +{ + size_t i; + if (!mask) { + return; + } + + /* only extend to pixels outside the mask area */ + for (i = 0; i < num_pixels; i++) { + if (pixel_array[i].primitive_id != -1) { + mask[i] = FILTER_MASK_USED; + } + } +} + +void RE_bake_margin(ImBuf *ibuf, char *mask, const int margin) +{ + /* margin */ + IMB_filter_extend(ibuf, mask, margin); + + if (ibuf->planes != R_IMF_PLANES_RGBA) { + /* clear alpha added by filtering */ + IMB_rectfill_alpha(ibuf, 1.0f); + } +} + +/** + * This function returns the coordinate and normal of a barycentric u,v + * for a face defined by the primitive_id index. + * The returned normal is actually the direction from the same barycentric coordinate + * in the cage to the base mesh + * The returned coordinate is the point in the cage mesh + */ +static void calc_point_from_barycentric_cage(TriTessFace *triangles_low, + TriTessFace *triangles_cage, + const float mat_low[4][4], + const float mat_cage[4][4], + int primitive_id, + float u, + float v, + float r_co[3], + float r_dir[3]) +{ + float data[2][3][3]; + float coord[2][3]; + float dir[3]; + int i; + + TriTessFace *triangle[2]; + + triangle[0] = &triangles_low[primitive_id]; + triangle[1] = &triangles_cage[primitive_id]; + + for (i = 0; i < 2; i++) { + copy_v3_v3(data[i][0], triangle[i]->mverts[0]->co); + copy_v3_v3(data[i][1], triangle[i]->mverts[1]->co); + copy_v3_v3(data[i][2], triangle[i]->mverts[2]->co); + interp_barycentric_tri_v3(data[i], u, v, coord[i]); + } + + /* convert from local to world space */ + mul_m4_v3(mat_low, coord[0]); + mul_m4_v3(mat_cage, coord[1]); + + sub_v3_v3v3(dir, coord[0], coord[1]); + normalize_v3(dir); + + copy_v3_v3(r_co, coord[1]); + copy_v3_v3(r_dir, dir); +} + +/** + * This function returns the coordinate and normal of a barycentric u,v + * for a face defined by the primitive_id index. + * The returned coordinate is extruded along the normal by cage_extrusion + */ +static void calc_point_from_barycentric_extrusion(TriTessFace *triangles, + const float mat[4][4], + const float imat[4][4], + int primitive_id, + float u, + float v, + float cage_extrusion, + float r_co[3], + float r_dir[3], + const bool is_cage) +{ + float data[3][3]; + float coord[3]; + float dir[3]; + float cage[3]; + bool is_smooth; + + TriTessFace *triangle = &triangles[primitive_id]; + is_smooth = triangle->is_smooth || is_cage; + + copy_v3_v3(data[0], triangle->mverts[0]->co); + copy_v3_v3(data[1], triangle->mverts[1]->co); + copy_v3_v3(data[2], triangle->mverts[2]->co); + + interp_barycentric_tri_v3(data, u, v, coord); + + if (is_smooth) { + normal_short_to_float_v3(data[0], triangle->mverts[0]->no); + normal_short_to_float_v3(data[1], triangle->mverts[1]->no); + normal_short_to_float_v3(data[2], triangle->mverts[2]->no); + + interp_barycentric_tri_v3(data, u, v, dir); + normalize_v3(dir); + } + else { + copy_v3_v3(dir, triangle->normal); + } + + mul_v3_v3fl(cage, dir, cage_extrusion); + add_v3_v3(coord, cage); + + normalize_v3(dir); + negate_v3(dir); + + /* convert from local to world space */ + mul_m4_v3(mat, coord); + mul_transposed_mat3_m4_v3(imat, dir); + normalize_v3(dir); + + copy_v3_v3(r_co, coord); + copy_v3_v3(r_dir, dir); +} + +static void barycentric_differentials_from_position(const float co[3], + const float v1[3], + const float v2[3], + const float v3[3], + const float dxco[3], + const float dyco[3], + const float facenor[3], + const bool differentials, + float *u, + float *v, + float *dx_u, + float *dx_v, + float *dy_u, + float *dy_v) +{ + /* find most stable axis to project */ + int axis1, axis2; + axis_dominant_v3(&axis1, &axis2, facenor); + + /* compute u,v and derivatives */ + float t00 = v3[axis1] - v1[axis1]; + float t01 = v3[axis2] - v1[axis2]; + float t10 = v3[axis1] - v2[axis1]; + float t11 = v3[axis2] - v2[axis2]; + + float detsh = (t00 * t11 - t10 * t01); + detsh = (detsh != 0.0f) ? 1.0f / detsh : 0.0f; + t00 *= detsh; + t01 *= detsh; + t10 *= detsh; + t11 *= detsh; + + *u = (v3[axis1] - co[axis1]) * t11 - (v3[axis2] - co[axis2]) * t10; + *v = (v3[axis2] - co[axis2]) * t00 - (v3[axis1] - co[axis1]) * t01; + if (differentials) { + *dx_u = dxco[axis1] * t11 - dxco[axis2] * t10; + *dx_v = dxco[axis2] * t00 - dxco[axis1] * t01; + *dy_u = dyco[axis1] * t11 - dyco[axis2] * t10; + *dy_v = dyco[axis2] * t00 - dyco[axis1] * t01; + } +} + +/** + * This function populates pixel_array and returns TRUE if things are correct + */ +static bool cast_ray_highpoly(BVHTreeFromMesh *treeData, + TriTessFace *triangle_low, + TriTessFace *triangles[], + BakePixel *pixel_array_low, + BakePixel *pixel_array, + const float mat_low[4][4], + BakeHighPolyData *highpoly, + const float co[3], + const float dir[3], + const int pixel_id, + const int tot_highpoly, + const float max_ray_distance) +{ + int i; + int hit_mesh = -1; + float hit_distance = max_ray_distance; + if (hit_distance == 0.0f) { + /* No ray distance set, use maximum. */ + hit_distance = FLT_MAX; + } + + BVHTreeRayHit *hits; + hits = MEM_mallocN(sizeof(BVHTreeRayHit) * tot_highpoly, "Bake Highpoly to Lowpoly: BVH Rays"); + + for (i = 0; i < tot_highpoly; i++) { + float co_high[3], dir_high[3]; + + hits[i].index = -1; + /* TODO: we should use FLT_MAX here, but sweepsphere code isn't prepared for that */ + hits[i].dist = BVH_RAYCAST_DIST_MAX; + + /* transform the ray from the world space to the highpoly space */ + mul_v3_m4v3(co_high, highpoly[i].imat, co); + + /* rotates */ + mul_v3_mat3_m4v3(dir_high, highpoly[i].imat, dir); + normalize_v3(dir_high); + + /* cast ray */ + if (treeData[i].tree) { + BLI_bvhtree_ray_cast(treeData[i].tree, + co_high, + dir_high, + 0.0f, + &hits[i], + treeData[i].raycast_callback, + &treeData[i]); + } + + if (hits[i].index != -1) { + float distance; + float hit_world[3]; + + /* distance comparison in world space */ + mul_v3_m4v3(hit_world, highpoly[i].obmat, hits[i].co); + distance = len_squared_v3v3(hit_world, co); + + if (distance < hit_distance) { + hit_mesh = i; + hit_distance = distance; + } + } + } + + if (hit_mesh != -1) { + int primitive_id_high = hits[hit_mesh].index; + TriTessFace *triangle_high = &triangles[hit_mesh][primitive_id_high]; + BakePixel *pixel_low = &pixel_array_low[pixel_id]; + BakePixel *pixel_high = &pixel_array[pixel_id]; + + pixel_high->primitive_id = primitive_id_high; + pixel_high->object_id = hit_mesh; + + /* ray direction in high poly object space */ + float dir_high[3]; + mul_v3_mat3_m4v3(dir_high, highpoly[hit_mesh].imat, dir); + normalize_v3(dir_high); + + /* compute position differentials on low poly object */ + float duco_low[3], dvco_low[3], dxco[3], dyco[3]; + sub_v3_v3v3(duco_low, triangle_low->mverts[0]->co, triangle_low->mverts[2]->co); + sub_v3_v3v3(dvco_low, triangle_low->mverts[1]->co, triangle_low->mverts[2]->co); + + mul_v3_v3fl(dxco, duco_low, pixel_low->du_dx); + madd_v3_v3fl(dxco, dvco_low, pixel_low->dv_dx); + mul_v3_v3fl(dyco, duco_low, pixel_low->du_dy); + madd_v3_v3fl(dyco, dvco_low, pixel_low->dv_dy); + + /* transform from low poly to high poly object space */ + mul_mat3_m4_v3(mat_low, dxco); + mul_mat3_m4_v3(mat_low, dyco); + mul_mat3_m4_v3(highpoly[hit_mesh].imat, dxco); + mul_mat3_m4_v3(highpoly[hit_mesh].imat, dyco); + + /* transfer position differentials */ + float tmp[3]; + mul_v3_v3fl(tmp, dir_high, 1.0f / dot_v3v3(dir_high, triangle_high->normal)); + madd_v3_v3fl(dxco, tmp, -dot_v3v3(dxco, triangle_high->normal)); + madd_v3_v3fl(dyco, tmp, -dot_v3v3(dyco, triangle_high->normal)); + + /* compute barycentric differentials from position differentials */ + barycentric_differentials_from_position(hits[hit_mesh].co, + triangle_high->mverts[0]->co, + triangle_high->mverts[1]->co, + triangle_high->mverts[2]->co, + dxco, + dyco, + triangle_high->normal, + true, + &pixel_high->uv[0], + &pixel_high->uv[1], + &pixel_high->du_dx, + &pixel_high->dv_dx, + &pixel_high->du_dy, + &pixel_high->dv_dy); + + /* verify we have valid uvs */ + BLI_assert(pixel_high->uv[0] >= -1e-3f && pixel_high->uv[1] >= -1e-3f && + pixel_high->uv[0] + pixel_high->uv[1] <= 1.0f + 1e-3f); + } + else { + pixel_array[pixel_id].primitive_id = -1; + pixel_array[pixel_id].object_id = -1; + } + + MEM_freeN(hits); + return hit_mesh != -1; +} + +/** + * This function populates an array of verts for the triangles of a mesh + * Tangent and Normals are also stored + */ +static TriTessFace *mesh_calc_tri_tessface(Mesh *me, bool tangent, Mesh *me_eval) +{ + int i; + MVert *mvert; + TSpace *tspace = NULL; + float(*loop_normals)[3] = NULL; + + const int tottri = poly_to_tri_count(me->totpoly, me->totloop); + MLoopTri *looptri; + TriTessFace *triangles; + + /* calculate normal for each polygon only once */ + unsigned int mpoly_prev = UINT_MAX; + float no[3]; + + mvert = CustomData_get_layer(&me->vdata, CD_MVERT); + looptri = MEM_mallocN(sizeof(*looptri) * tottri, __func__); + triangles = MEM_callocN(sizeof(TriTessFace) * tottri, __func__); + + BKE_mesh_recalc_looptri(me->mloop, me->mpoly, me->mvert, me->totloop, me->totpoly, looptri); + + if (tangent) { + BKE_mesh_ensure_normals_for_display(me_eval); + BKE_mesh_calc_normals_split(me_eval); + BKE_mesh_calc_loop_tangents(me_eval, true, NULL, 0); + + tspace = CustomData_get_layer(&me_eval->ldata, CD_TANGENT); + BLI_assert(tspace); + + loop_normals = CustomData_get_layer(&me_eval->ldata, CD_NORMAL); + } + + const float *precomputed_normals = CustomData_get_layer(&me->pdata, CD_NORMAL); + const bool calculate_normal = precomputed_normals ? false : true; + + for (i = 0; i < tottri; i++) { + const MLoopTri *lt = &looptri[i]; + const MPoly *mp = &me->mpoly[lt->poly]; + + triangles[i].mverts[0] = &mvert[me->mloop[lt->tri[0]].v]; + triangles[i].mverts[1] = &mvert[me->mloop[lt->tri[1]].v]; + triangles[i].mverts[2] = &mvert[me->mloop[lt->tri[2]].v]; + triangles[i].is_smooth = (mp->flag & ME_SMOOTH) != 0; + + if (tangent) { + triangles[i].tspace[0] = &tspace[lt->tri[0]]; + triangles[i].tspace[1] = &tspace[lt->tri[1]]; + triangles[i].tspace[2] = &tspace[lt->tri[2]]; + } + + if (loop_normals) { + triangles[i].loop_normal[0] = loop_normals[lt->tri[0]]; + triangles[i].loop_normal[1] = loop_normals[lt->tri[1]]; + triangles[i].loop_normal[2] = loop_normals[lt->tri[2]]; + } + + if (calculate_normal) { + if (lt->poly != mpoly_prev) { + BKE_mesh_calc_poly_normal(mp, &me->mloop[mp->loopstart], me->mvert, no); + mpoly_prev = lt->poly; + } + copy_v3_v3(triangles[i].normal, no); + } + else { + copy_v3_v3(triangles[i].normal, &precomputed_normals[lt->poly]); + } + } + + MEM_freeN(looptri); + + return triangles; +} + +bool RE_bake_pixels_populate_from_objects(struct Mesh *me_low, + BakePixel pixel_array_from[], + BakePixel pixel_array_to[], + BakeHighPolyData highpoly[], + const int tot_highpoly, + const size_t num_pixels, + const bool is_custom_cage, + const float cage_extrusion, + const float max_ray_distance, + float mat_low[4][4], + float mat_cage[4][4], + struct Mesh *me_cage) +{ + size_t i; + int primitive_id; + float u, v; + float imat_low[4][4]; + bool is_cage = me_cage != NULL; + bool result = true; + + Mesh *me_eval_low = NULL; + Mesh **me_highpoly; + BVHTreeFromMesh *treeData; + + /* Note: all coordinates are in local space */ + TriTessFace *tris_low = NULL; + TriTessFace *tris_cage = NULL; + TriTessFace **tris_high; + + /* assume all lowpoly tessfaces can be quads */ + tris_high = MEM_callocN(sizeof(TriTessFace *) * tot_highpoly, "MVerts Highpoly Mesh Array"); + + /* assume all highpoly tessfaces are triangles */ + me_highpoly = MEM_mallocN(sizeof(Mesh *) * tot_highpoly, "Highpoly Derived Meshes"); + treeData = MEM_callocN(sizeof(BVHTreeFromMesh) * tot_highpoly, "Highpoly BVH Trees"); + + if (!is_cage) { + me_eval_low = BKE_mesh_copy_for_eval(me_low, false); + tris_low = mesh_calc_tri_tessface(me_low, true, me_eval_low); + } + else if (is_custom_cage) { + tris_low = mesh_calc_tri_tessface(me_low, false, NULL); + tris_cage = mesh_calc_tri_tessface(me_cage, false, NULL); + } + else { + tris_cage = mesh_calc_tri_tessface(me_cage, false, NULL); + } + + invert_m4_m4(imat_low, mat_low); + + for (i = 0; i < tot_highpoly; i++) { + tris_high[i] = mesh_calc_tri_tessface(highpoly[i].me, false, NULL); + + me_highpoly[i] = highpoly[i].me; + BKE_mesh_runtime_looptri_ensure(me_highpoly[i]); + + if (me_highpoly[i]->runtime.looptris.len != 0) { + /* Create a bvh-tree for each highpoly object */ + BKE_bvhtree_from_mesh_get(&treeData[i], me_highpoly[i], BVHTREE_FROM_LOOPTRI, 2); + + if (treeData[i].tree == NULL) { + printf("Baking: out of memory while creating BHVTree for object \"%s\"\n", + highpoly[i].ob->id.name + 2); + result = false; + goto cleanup; + } + } + } + + for (i = 0; i < num_pixels; i++) { + float co[3]; + float dir[3]; + TriTessFace *tri_low; + + primitive_id = pixel_array_from[i].primitive_id; + + if (primitive_id == -1) { + pixel_array_to[i].primitive_id = -1; + continue; + } + + u = pixel_array_from[i].uv[0]; + v = pixel_array_from[i].uv[1]; + + /* calculate from low poly mesh cage */ + if (is_custom_cage) { + calc_point_from_barycentric_cage( + tris_low, tris_cage, mat_low, mat_cage, primitive_id, u, v, co, dir); + tri_low = &tris_cage[primitive_id]; + } + else if (is_cage) { + calc_point_from_barycentric_extrusion( + tris_cage, mat_low, imat_low, primitive_id, u, v, cage_extrusion, co, dir, true); + tri_low = &tris_cage[primitive_id]; + } + else { + calc_point_from_barycentric_extrusion( + tris_low, mat_low, imat_low, primitive_id, u, v, cage_extrusion, co, dir, false); + tri_low = &tris_low[primitive_id]; + } + + /* cast ray */ + if (!cast_ray_highpoly(treeData, + tri_low, + tris_high, + pixel_array_from, + pixel_array_to, + mat_low, + highpoly, + co, + dir, + i, + tot_highpoly, + max_ray_distance)) { + /* if it fails mask out the original pixel array */ + pixel_array_from[i].primitive_id = -1; + } + } + + /* garbage collection */ +cleanup: + for (i = 0; i < tot_highpoly; i++) { + free_bvhtree_from_mesh(&treeData[i]); + + if (tris_high[i]) { + MEM_freeN(tris_high[i]); + } + } + + MEM_freeN(tris_high); + MEM_freeN(treeData); + MEM_freeN(me_highpoly); + + if (me_eval_low) { + BKE_id_free(NULL, me_eval_low); + } + if (tris_low) { + MEM_freeN(tris_low); + } + if (tris_cage) { + MEM_freeN(tris_cage); + } + + return result; +} + +static void bake_differentials(BakeDataZSpan *bd, + const float *uv1, + const float *uv2, + const float *uv3) +{ + float A; + + /* assumes dPdu = P1 - P3 and dPdv = P2 - P3 */ + A = (uv2[0] - uv1[0]) * (uv3[1] - uv1[1]) - (uv3[0] - uv1[0]) * (uv2[1] - uv1[1]); + + if (fabsf(A) > FLT_EPSILON) { + A = 0.5f / A; + + bd->du_dx = (uv2[1] - uv3[1]) * A; + bd->dv_dx = (uv3[1] - uv1[1]) * A; + + bd->du_dy = (uv3[0] - uv2[0]) * A; + bd->dv_dy = (uv1[0] - uv3[0]) * A; + } + else { + bd->du_dx = bd->du_dy = 0.0f; + bd->dv_dx = bd->dv_dy = 0.0f; + } +} + +void RE_bake_pixels_populate(Mesh *me, + BakePixel pixel_array[], + const size_t num_pixels, + const BakeImages *bake_images, + const char *uv_layer) +{ + const MLoopUV *mloopuv; + if ((uv_layer == NULL) || (uv_layer[0] == '\0')) { + mloopuv = CustomData_get_layer(&me->ldata, CD_MLOOPUV); + } + else { + int uv_id = CustomData_get_named_layer(&me->ldata, CD_MLOOPUV, uv_layer); + mloopuv = CustomData_get_layer_n(&me->ldata, CD_MLOOPUV, uv_id); + } + + if (mloopuv == NULL) { + return; + } + + BakeDataZSpan bd; + bd.pixel_array = pixel_array; + bd.zspan = MEM_callocN(sizeof(ZSpan) * bake_images->size, "bake zspan"); + + /* initialize all pixel arrays so we know which ones are 'blank' */ + for (int i = 0; i < num_pixels; i++) { + pixel_array[i].primitive_id = -1; + pixel_array[i].object_id = 0; + } + + for (int i = 0; i < bake_images->size; i++) { + zbuf_alloc_span(&bd.zspan[i], bake_images->data[i].width, bake_images->data[i].height); + } + + const int tottri = poly_to_tri_count(me->totpoly, me->totloop); + MLoopTri *looptri = MEM_mallocN(sizeof(*looptri) * tottri, __func__); + + BKE_mesh_recalc_looptri(me->mloop, me->mpoly, me->mvert, me->totloop, me->totpoly, looptri); + + for (int i = 0; i < tottri; i++) { + const MLoopTri *lt = &looptri[i]; + const MPoly *mp = &me->mpoly[lt->poly]; + float vec[3][2]; + int mat_nr = mp->mat_nr; + int image_id = bake_images->lookup[mat_nr]; + + if (image_id < 0) { + continue; + } + + bd.bk_image = &bake_images->data[image_id]; + bd.primitive_id = i; + + for (int a = 0; a < 3; a++) { + const float *uv = mloopuv[lt->tri[a]].uv; + + /* Note, workaround for pixel aligned UVs which are common and can screw up our + * intersection tests where a pixel gets in between 2 faces or the middle of a quad, + * camera aligned quads also have this problem but they are less common. + * Add a small offset to the UVs, fixes bug T18685 - Campbell */ + vec[a][0] = uv[0] * (float)bd.bk_image->width - (0.5f + 0.001f); + vec[a][1] = uv[1] * (float)bd.bk_image->height - (0.5f + 0.002f); + } + + bake_differentials(&bd, vec[0], vec[1], vec[2]); + zspan_scanconvert(&bd.zspan[image_id], (void *)&bd, vec[0], vec[1], vec[2], store_bake_pixel); + } + + for (int i = 0; i < bake_images->size; i++) { + zbuf_free_span(&bd.zspan[i]); + } + + MEM_freeN(looptri); + MEM_freeN(bd.zspan); +} + +/* ******************** NORMALS ************************ */ + +/** + * convert a normalized normal to the -1.0 1.0 range + * the input is expected to be POS_X, POS_Y, POS_Z + */ +static void normal_uncompress(float out[3], const float in[3]) +{ + int i; + for (i = 0; i < 3; i++) { + out[i] = 2.0f * in[i] - 1.0f; + } +} + +static void normal_compress(float out[3], + const float in[3], + const eBakeNormalSwizzle normal_swizzle[3]) +{ + const int swizzle_index[6] = { + 0, /* R_BAKE_POSX */ + 1, /* R_BAKE_POSY */ + 2, /* R_BAKE_POSZ */ + 0, /* R_BAKE_NEGX */ + 1, /* R_BAKE_NEGY */ + 2, /* R_BAKE_NEGZ */ + }; + const float swizzle_sign[6] = { + +1.0f, /* R_BAKE_POSX */ + +1.0f, /* R_BAKE_POSY */ + +1.0f, /* R_BAKE_POSZ */ + -1.0f, /* R_BAKE_NEGX */ + -1.0f, /* R_BAKE_NEGY */ + -1.0f, /* R_BAKE_NEGZ */ + }; + + int i; + + for (i = 0; i < 3; i++) { + int index; + float sign; + + sign = swizzle_sign[normal_swizzle[i]]; + index = swizzle_index[normal_swizzle[i]]; + + /* + * There is a small 1e-5f bias for precision issues. otherwise + * we randomly get 127 or 128 for neutral colors in tangent maps. + * we choose 128 because it is the convention flat color. * + */ + + out[i] = sign * in[index] / 2.0f + 0.5f + 1e-5f; + } +} + +/** + * This function converts an object space normal map + * to a tangent space normal map for a given low poly mesh. + */ +void RE_bake_normal_world_to_tangent(const BakePixel pixel_array[], + const size_t num_pixels, + const int depth, + float result[], + Mesh *me, + const eBakeNormalSwizzle normal_swizzle[3], + float mat[4][4]) +{ + size_t i; + + TriTessFace *triangles; + + Mesh *me_eval = BKE_mesh_copy_for_eval(me, false); + + triangles = mesh_calc_tri_tessface(me, true, me_eval); + + BLI_assert(num_pixels >= 3); + + for (i = 0; i < num_pixels; i++) { + TriTessFace *triangle; + float tangents[3][3]; + float normals[3][3]; + float signs[3]; + int j; + + float tangent[3]; + float normal[3]; + float binormal[3]; + float sign; + float u, v, w; + + float tsm[3][3]; /* tangent space matrix */ + float itsm[3][3]; + + size_t offset; + float nor[3]; /* texture normal */ + + bool is_smooth; + + int primitive_id = pixel_array[i].primitive_id; + + offset = i * depth; + + if (primitive_id == -1) { + if (depth == 4) { + copy_v4_fl4(&result[offset], 0.5f, 0.5f, 1.0f, 1.0f); + } + else { + copy_v3_fl3(&result[offset], 0.5f, 0.5f, 1.0f); + } + continue; + } + + triangle = &triangles[primitive_id]; + is_smooth = triangle->is_smooth; + + for (j = 0; j < 3; j++) { + const TSpace *ts; + + if (is_smooth) { + if (triangle->loop_normal[j]) { + copy_v3_v3(normals[j], triangle->loop_normal[j]); + } + else { + normal_short_to_float_v3(normals[j], triangle->mverts[j]->no); + } + } + + ts = triangle->tspace[j]; + copy_v3_v3(tangents[j], ts->tangent); + signs[j] = ts->sign; + } + + u = pixel_array[i].uv[0]; + v = pixel_array[i].uv[1]; + w = 1.0f - u - v; + + /* normal */ + if (is_smooth) { + interp_barycentric_tri_v3(normals, u, v, normal); + } + else { + copy_v3_v3(normal, triangle->normal); + } + + /* tangent */ + interp_barycentric_tri_v3(tangents, u, v, tangent); + + /* sign */ + /* The sign is the same at all face vertices for any non degenerate face. + * Just in case we clamp the interpolated value though. */ + sign = (signs[0] * u + signs[1] * v + signs[2] * w) < 0 ? (-1.0f) : 1.0f; + + /* binormal */ + /* B = sign * cross(N, T) */ + cross_v3_v3v3(binormal, normal, tangent); + mul_v3_fl(binormal, sign); + + /* populate tangent space matrix */ + copy_v3_v3(tsm[0], tangent); + copy_v3_v3(tsm[1], binormal); + copy_v3_v3(tsm[2], normal); + + /* texture values */ + normal_uncompress(nor, &result[offset]); + + /* converts from world space to local space */ + mul_transposed_mat3_m4_v3(mat, nor); + + invert_m3_m3(itsm, tsm); + mul_m3_v3(itsm, nor); + normalize_v3(nor); + + /* save back the values */ + normal_compress(&result[offset], nor, normal_swizzle); + } + + /* garbage collection */ + MEM_freeN(triangles); + + if (me_eval) { + BKE_id_free(NULL, me_eval); + } +} + +void RE_bake_normal_world_to_object(const BakePixel pixel_array[], + const size_t num_pixels, + const int depth, + float result[], + struct Object *ob, + const eBakeNormalSwizzle normal_swizzle[3]) +{ + size_t i; + float iobmat[4][4]; + + invert_m4_m4(iobmat, ob->obmat); + + for (i = 0; i < num_pixels; i++) { + size_t offset; + float nor[3]; + + if (pixel_array[i].primitive_id == -1) { + continue; + } + + offset = i * depth; + normal_uncompress(nor, &result[offset]); + + /* rotates only without translation */ + mul_mat3_m4_v3(iobmat, nor); + normalize_v3(nor); + + /* save back the values */ + normal_compress(&result[offset], nor, normal_swizzle); + } +} + +void RE_bake_normal_world_to_world(const BakePixel pixel_array[], + const size_t num_pixels, + const int depth, + float result[], + const eBakeNormalSwizzle normal_swizzle[3]) +{ + size_t i; + + for (i = 0; i < num_pixels; i++) { + size_t offset; + float nor[3]; + + if (pixel_array[i].primitive_id == -1) { + continue; + } + + offset = i * depth; + normal_uncompress(nor, &result[offset]); + + /* save back the values */ + normal_compress(&result[offset], nor, normal_swizzle); + } +} + +void RE_bake_ibuf_clear(Image *image, const bool is_tangent) +{ + ImBuf *ibuf; + void *lock; + + const float vec_alpha[4] = {0.0f, 0.0f, 0.0f, 0.0f}; + const float vec_solid[4] = {0.0f, 0.0f, 0.0f, 1.0f}; + const float nor_alpha[4] = {0.5f, 0.5f, 1.0f, 0.0f}; + const float nor_solid[4] = {0.5f, 0.5f, 1.0f, 1.0f}; + + ibuf = BKE_image_acquire_ibuf(image, NULL, &lock); + BLI_assert(ibuf); + + if (is_tangent) { + IMB_rectfill(ibuf, (ibuf->planes == R_IMF_PLANES_RGBA) ? nor_alpha : nor_solid); + } + else { + IMB_rectfill(ibuf, (ibuf->planes == R_IMF_PLANES_RGBA) ? vec_alpha : vec_solid); + } + + BKE_image_release_ibuf(image, ibuf, lock); +} + +/* ************************************************************* */ + +int RE_pass_depth(const eScenePassType pass_type) +{ + /* IMB_buffer_byte_from_float assumes 4 channels + * making it work for now - XXX */ + return 4; + + switch (pass_type) { + case SCE_PASS_Z: + case SCE_PASS_AO: + case SCE_PASS_MIST: { + return 1; + } + case SCE_PASS_UV: { + return 2; + } + case SCE_PASS_COMBINED: + case SCE_PASS_SHADOW: + case SCE_PASS_NORMAL: + case SCE_PASS_VECTOR: + case SCE_PASS_INDEXOB: /* XXX double check */ + case SCE_PASS_RAYHITS: /* XXX double check */ + case SCE_PASS_EMIT: + case SCE_PASS_ENVIRONMENT: + case SCE_PASS_INDEXMA: + case SCE_PASS_DIFFUSE_DIRECT: + case SCE_PASS_DIFFUSE_INDIRECT: + case SCE_PASS_DIFFUSE_COLOR: + case SCE_PASS_GLOSSY_DIRECT: + case SCE_PASS_GLOSSY_INDIRECT: + case SCE_PASS_GLOSSY_COLOR: + case SCE_PASS_TRANSM_DIRECT: + case SCE_PASS_TRANSM_INDIRECT: + case SCE_PASS_TRANSM_COLOR: + case SCE_PASS_SUBSURFACE_DIRECT: + case SCE_PASS_SUBSURFACE_INDIRECT: + case SCE_PASS_SUBSURFACE_COLOR: + default: { + return 3; + } + } +} diff --git a/source/blender/render/intern/external_engine.c b/source/blender/render/intern/external_engine.c new file mode 100644 index 00000000000..769077c0e8c --- /dev/null +++ b/source/blender/render/intern/external_engine.c @@ -0,0 +1,1032 @@ +/* + * 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) 2006 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup render + */ + +#include <stddef.h> +#include <stdlib.h> +#include <string.h> + +#include "MEM_guardedalloc.h" + +#include "BLT_translation.h" + +#include "BLI_ghash.h" +#include "BLI_listbase.h" +#include "BLI_math_bits.h" +#include "BLI_rect.h" +#include "BLI_string.h" +#include "BLI_utildefines.h" + +#include "DNA_object_types.h" + +#include "BKE_camera.h" +#include "BKE_colortools.h" +#include "BKE_global.h" +#include "BKE_layer.h" +#include "BKE_node.h" +#include "BKE_report.h" +#include "BKE_scene.h" + +#include "DEG_depsgraph.h" +#include "DEG_depsgraph_debug.h" +#include "DEG_depsgraph_query.h" + +#include "RNA_access.h" + +#ifdef WITH_PYTHON +# include "BPY_extern.h" +#endif + +#include "RE_bake.h" +#include "RE_engine.h" +#include "RE_pipeline.h" + +#include "DRW_engine.h" + +#include "initrender.h" +#include "pipeline.h" +#include "render_result.h" +#include "render_types.h" + +/* Render Engine Types */ + +ListBase R_engines = {NULL, NULL}; + +void RE_engines_init(void) +{ + DRW_engines_register(); +} + +void RE_engines_exit(void) +{ + RenderEngineType *type, *next; + + DRW_engines_free(); + + for (type = R_engines.first; type; type = next) { + next = type->next; + + BLI_remlink(&R_engines, type); + + if (!(type->flag & RE_INTERNAL)) { + if (type->rna_ext.free) { + type->rna_ext.free(type->rna_ext.data); + } + + MEM_freeN(type); + } + } +} + +void RE_engines_register(RenderEngineType *render_type) +{ + if (render_type->draw_engine) { + DRW_engine_register(render_type->draw_engine); + } + BLI_addtail(&R_engines, render_type); +} + +RenderEngineType *RE_engines_find(const char *idname) +{ + RenderEngineType *type; + + type = BLI_findstring(&R_engines, idname, offsetof(RenderEngineType, idname)); + if (!type) { + type = BLI_findstring(&R_engines, "BLENDER_EEVEE", offsetof(RenderEngineType, idname)); + } + + return type; +} + +bool RE_engine_is_external(const Render *re) +{ + return (re->engine && re->engine->type && re->engine->type->render); +} + +bool RE_engine_is_opengl(RenderEngineType *render_type) +{ + /* TODO refine? Can we have ogl render engine without ogl render pipeline? */ + return (render_type->draw_engine != NULL) && DRW_engine_render_support(render_type->draw_engine); +} + +/* Create, Free */ + +RenderEngine *RE_engine_create(RenderEngineType *type) +{ + RenderEngine *engine = MEM_callocN(sizeof(RenderEngine), "RenderEngine"); + engine->type = type; + + BLI_mutex_init(&engine->update_render_passes_mutex); + + return engine; +} + +void RE_engine_free(RenderEngine *engine) +{ +#ifdef WITH_PYTHON + if (engine->py_instance) { + BPY_DECREF_RNA_INVALIDATE(engine->py_instance); + } +#endif + + BLI_mutex_end(&engine->update_render_passes_mutex); + + MEM_freeN(engine); +} + +/* Bake Render Results */ + +static RenderResult *render_result_from_bake(RenderEngine *engine, int x, int y, int w, int h) +{ + /* Create render result with specified size. */ + RenderResult *rr = MEM_callocN(sizeof(RenderResult), __func__); + + rr->rectx = w; + rr->recty = h; + rr->tilerect.xmin = x; + rr->tilerect.ymin = y; + rr->tilerect.xmax = x + w; + rr->tilerect.ymax = y + h; + + /* Add single baking render layer. */ + RenderLayer *rl = MEM_callocN(sizeof(RenderLayer), "bake render layer"); + rl->rectx = w; + rl->recty = h; + BLI_addtail(&rr->layers, rl); + + /* Add render passes. */ + RenderPass *result_pass = render_layer_add_pass( + rr, rl, engine->bake.depth, RE_PASSNAME_COMBINED, "", "RGBA"); + RenderPass *primitive_pass = render_layer_add_pass(rr, rl, 4, "BakePrimitive", "", "RGBA"); + RenderPass *differential_pass = render_layer_add_pass(rr, rl, 4, "BakeDifferential", "", "RGBA"); + + /* Fill render passes from bake pixel array, to be read by the render engine. */ + for (int ty = 0; ty < h; ty++) { + size_t offset = ty * w * 4; + float *primitive = primitive_pass->rect + offset; + float *differential = differential_pass->rect + offset; + + size_t bake_offset = (y + ty) * engine->bake.width + x; + const BakePixel *bake_pixel = engine->bake.pixels + bake_offset; + + for (int tx = 0; tx < w; tx++) { + if (bake_pixel->object_id != engine->bake.object_id) { + primitive[0] = int_as_float(-1); + primitive[1] = int_as_float(-1); + } + else { + primitive[0] = int_as_float(bake_pixel->object_id); + primitive[1] = int_as_float(bake_pixel->primitive_id); + primitive[2] = bake_pixel->uv[0]; + primitive[3] = bake_pixel->uv[1]; + + differential[0] = bake_pixel->du_dx; + differential[1] = bake_pixel->du_dy; + differential[2] = bake_pixel->dv_dx; + differential[3] = bake_pixel->dv_dy; + } + + primitive += 4; + differential += 4; + bake_pixel++; + } + } + + /* Initialize tile render result from full image bake result. */ + for (int ty = 0; ty < h; ty++) { + size_t offset = ty * w * engine->bake.depth; + size_t bake_offset = ((y + ty) * engine->bake.width + x) * engine->bake.depth; + size_t size = w * engine->bake.depth * sizeof(float); + + memcpy(result_pass->rect + offset, engine->bake.result + bake_offset, size); + } + + return rr; +} + +static void render_result_to_bake(RenderEngine *engine, RenderResult *rr) +{ + RenderPass *rpass = RE_pass_find_by_name(rr->layers.first, RE_PASSNAME_COMBINED, ""); + + if (!rpass) { + return; + } + + /* Copy from tile render result to full image bake result. */ + int x = rr->tilerect.xmin; + int y = rr->tilerect.ymin; + int w = rr->tilerect.xmax - rr->tilerect.xmin; + int h = rr->tilerect.ymax - rr->tilerect.ymin; + + for (int ty = 0; ty < h; ty++) { + size_t offset = ty * w * engine->bake.depth; + size_t bake_offset = ((y + ty) * engine->bake.width + x) * engine->bake.depth; + size_t size = w * engine->bake.depth * sizeof(float); + + memcpy(engine->bake.result + bake_offset, rpass->rect + offset, size); + } +} + +/* Render Results */ + +static RenderPart *get_part_from_result(Render *re, RenderResult *result) +{ + rcti key = result->tilerect; + BLI_rcti_translate(&key, re->disprect.xmin, re->disprect.ymin); + + return BLI_ghash_lookup(re->parts, &key); +} + +RenderResult *RE_engine_begin_result( + RenderEngine *engine, int x, int y, int w, int h, const char *layername, const char *viewname) +{ + if (engine->bake.pixels) { + RenderResult *result = render_result_from_bake(engine, x, y, w, h); + BLI_addtail(&engine->fullresult, result); + return result; + } + + Render *re = engine->re; + RenderResult *result; + rcti disprect; + + /* ensure the coordinates are within the right limits */ + CLAMP(x, 0, re->result->rectx); + CLAMP(y, 0, re->result->recty); + CLAMP(w, 0, re->result->rectx); + CLAMP(h, 0, re->result->recty); + + if (x + w > re->result->rectx) { + w = re->result->rectx - x; + } + if (y + h > re->result->recty) { + h = re->result->recty - y; + } + + /* allocate a render result */ + disprect.xmin = x; + disprect.xmax = x + w; + disprect.ymin = y; + disprect.ymax = y + h; + + result = render_result_new(re, &disprect, 0, RR_USE_MEM, layername, viewname); + + /* todo: make this thread safe */ + + /* can be NULL if we CLAMP the width or height to 0 */ + if (result) { + render_result_clone_passes(re, result, viewname); + + RenderPart *pa; + + /* Copy EXR tile settings, so pipeline knows whether this is a result + * for Save Buffers enabled rendering. + */ + result->do_exr_tile = re->result->do_exr_tile; + + BLI_addtail(&engine->fullresult, result); + + result->tilerect.xmin += re->disprect.xmin; + result->tilerect.xmax += re->disprect.xmin; + result->tilerect.ymin += re->disprect.ymin; + result->tilerect.ymax += re->disprect.ymin; + + pa = get_part_from_result(re, result); + + if (pa) { + pa->status = PART_STATUS_IN_PROGRESS; + } + } + + return result; +} + +void RE_engine_update_result(RenderEngine *engine, RenderResult *result) +{ + if (engine->bake.pixels) { + /* No interactive baking updates for now. */ + return; + } + + Render *re = engine->re; + + if (result) { + render_result_merge(re->result, result); + result->renlay = result->layers.first; /* weak, draws first layer always */ + re->display_update(re->duh, result, NULL); + } +} + +void RE_engine_add_pass(RenderEngine *engine, + const char *name, + int channels, + const char *chan_id, + const char *layername) +{ + Render *re = engine->re; + + if (!re || !re->result) { + return; + } + + RE_create_render_pass(re->result, name, channels, chan_id, layername, NULL); +} + +void RE_engine_end_result( + RenderEngine *engine, RenderResult *result, bool cancel, bool highlight, bool merge_results) +{ + Render *re = engine->re; + + if (!result) { + return; + } + + if (engine->bake.pixels) { + render_result_to_bake(engine, result); + BLI_remlink(&engine->fullresult, result); + render_result_free(result); + return; + } + + /* merge. on break, don't merge in result for preview renders, looks nicer */ + if (!highlight) { + /* for exr tile render, detect tiles that are done */ + RenderPart *pa = get_part_from_result(re, result); + + if (pa) { + pa->status = (!cancel && merge_results) ? PART_STATUS_MERGED : PART_STATUS_RENDERED; + } + else if (re->result->do_exr_tile) { + /* If written result does not match any tile and we are using save + * buffers, we are going to get OpenEXR save errors. */ + fprintf(stderr, "RenderEngine.end_result: dimensions do not match any OpenEXR tile.\n"); + } + } + + if (!cancel || merge_results) { + if (re->result->do_exr_tile) { + if (!cancel && merge_results) { + render_result_exr_file_merge(re->result, result, re->viewname); + render_result_merge(re->result, result); + } + } + else if (!(re->test_break(re->tbh) && (re->r.scemode & R_BUTS_PREVIEW))) { + render_result_merge(re->result, result); + } + + /* draw */ + if (!re->test_break(re->tbh)) { + result->renlay = result->layers.first; /* weak, draws first layer always */ + re->display_update(re->duh, result, NULL); + } + } + + /* free */ + BLI_remlink(&engine->fullresult, result); + render_result_free(result); +} + +RenderResult *RE_engine_get_result(RenderEngine *engine) +{ + return engine->re->result; +} + +/* Cancel */ + +bool RE_engine_test_break(RenderEngine *engine) +{ + Render *re = engine->re; + + if (re) { + return re->test_break(re->tbh); + } + + return 0; +} + +/* Statistics */ + +void RE_engine_update_stats(RenderEngine *engine, const char *stats, const char *info) +{ + Render *re = engine->re; + + /* stats draw callback */ + if (re) { + re->i.statstr = stats; + re->i.infostr = info; + re->stats_draw(re->sdh, &re->i); + re->i.infostr = NULL; + re->i.statstr = NULL; + } + + /* set engine text */ + engine->text[0] = '\0'; + + if (stats && stats[0] && info && info[0]) { + BLI_snprintf(engine->text, sizeof(engine->text), "%s | %s", stats, info); + } + else if (info && info[0]) { + BLI_strncpy(engine->text, info, sizeof(engine->text)); + } + else if (stats && stats[0]) { + BLI_strncpy(engine->text, stats, sizeof(engine->text)); + } +} + +void RE_engine_update_progress(RenderEngine *engine, float progress) +{ + Render *re = engine->re; + + if (re) { + CLAMP(progress, 0.0f, 1.0f); + re->progress(re->prh, progress); + } +} + +void RE_engine_update_memory_stats(RenderEngine *engine, float mem_used, float mem_peak) +{ + Render *re = engine->re; + + if (re) { + re->i.mem_used = mem_used; + re->i.mem_peak = mem_peak; + } +} + +void RE_engine_report(RenderEngine *engine, int type, const char *msg) +{ + Render *re = engine->re; + + if (re) { + BKE_report(engine->re->reports, type, msg); + } + else if (engine->reports) { + BKE_report(engine->reports, type, msg); + } +} + +void RE_engine_set_error_message(RenderEngine *engine, const char *msg) +{ + Render *re = engine->re; + if (re != NULL) { + RenderResult *rr = RE_AcquireResultRead(re); + if (rr) { + if (rr->error != NULL) { + MEM_freeN(rr->error); + } + rr->error = BLI_strdup(msg); + } + RE_ReleaseResult(re); + } +} + +const char *RE_engine_active_view_get(RenderEngine *engine) +{ + Render *re = engine->re; + return RE_GetActiveRenderView(re); +} + +void RE_engine_active_view_set(RenderEngine *engine, const char *viewname) +{ + Render *re = engine->re; + RE_SetActiveRenderView(re, viewname); +} + +float RE_engine_get_camera_shift_x(RenderEngine *engine, Object *camera, bool use_spherical_stereo) +{ + /* When using spherical stereo, get camera shift without multiview, + * leaving stereo to be handled by the engine. */ + Render *re = engine->re; + if (use_spherical_stereo || re == NULL) { + return BKE_camera_multiview_shift_x(NULL, camera, NULL); + } + + return BKE_camera_multiview_shift_x(&re->r, camera, re->viewname); +} + +void RE_engine_get_camera_model_matrix(RenderEngine *engine, + Object *camera, + bool use_spherical_stereo, + float *r_modelmat) +{ + /* When using spherical stereo, get model matrix without multiview, + * leaving stereo to be handled by the engine. */ + Render *re = engine->re; + if (use_spherical_stereo || re == NULL) { + BKE_camera_multiview_model_matrix(NULL, camera, NULL, (float(*)[4])r_modelmat); + } + else { + BKE_camera_multiview_model_matrix(&re->r, camera, re->viewname, (float(*)[4])r_modelmat); + } +} + +bool RE_engine_get_spherical_stereo(RenderEngine *engine, Object *camera) +{ + Render *re = engine->re; + return BKE_camera_multiview_spherical_stereo(re ? &re->r : NULL, camera) ? 1 : 0; +} + +rcti *RE_engine_get_current_tiles(Render *re, int *r_total_tiles, bool *r_needs_free) +{ + static rcti tiles_static[BLENDER_MAX_THREADS]; + const int allocation_step = BLENDER_MAX_THREADS; + int total_tiles = 0; + rcti *tiles = tiles_static; + int allocation_size = BLENDER_MAX_THREADS; + + BLI_rw_mutex_lock(&re->partsmutex, THREAD_LOCK_READ); + + *r_needs_free = false; + + if (!re->parts || (re->engine && (re->engine->flag & RE_ENGINE_HIGHLIGHT_TILES) == 0)) { + *r_total_tiles = 0; + BLI_rw_mutex_unlock(&re->partsmutex); + return NULL; + } + + GHashIterator pa_iter; + GHASH_ITER (pa_iter, re->parts) { + RenderPart *pa = BLI_ghashIterator_getValue(&pa_iter); + if (pa->status == PART_STATUS_IN_PROGRESS) { + if (total_tiles >= allocation_size) { + /* Just in case we're using crazy network rendering with more + * workers than BLENDER_MAX_THREADS. + */ + allocation_size += allocation_step; + if (tiles == tiles_static) { + /* Can not realloc yet, tiles are pointing to a + * stack memory. + */ + tiles = MEM_mallocN(allocation_size * sizeof(rcti), "current engine tiles"); + } + else { + tiles = MEM_reallocN(tiles, allocation_size * sizeof(rcti)); + } + *r_needs_free = true; + } + tiles[total_tiles] = pa->disprect; + + total_tiles++; + } + } + BLI_rw_mutex_unlock(&re->partsmutex); + *r_total_tiles = total_tiles; + return tiles; +} + +RenderData *RE_engine_get_render_data(Render *re) +{ + return &re->r; +} + +/* Depsgraph */ +static void engine_depsgraph_init(RenderEngine *engine, ViewLayer *view_layer) +{ + Main *bmain = engine->re->main; + Scene *scene = engine->re->scene; + + engine->depsgraph = DEG_graph_new(bmain, scene, view_layer, DAG_EVAL_RENDER); + DEG_debug_name_set(engine->depsgraph, "RENDER"); + + if (engine->re->r.scemode & R_BUTS_PREVIEW) { + Depsgraph *depsgraph = engine->depsgraph; + DEG_graph_relations_update(depsgraph); + DEG_evaluate_on_framechange(depsgraph, CFRA); + DEG_ids_check_recalc(bmain, depsgraph, scene, view_layer, true); + DEG_ids_clear_recalc(bmain, depsgraph); + } + else { + BKE_scene_graph_update_for_newframe(engine->depsgraph); + } + + engine->has_grease_pencil = DRW_render_check_grease_pencil(engine->depsgraph); +} + +static void engine_depsgraph_free(RenderEngine *engine) +{ + DEG_graph_free(engine->depsgraph); + + engine->depsgraph = NULL; +} + +void RE_engine_frame_set(RenderEngine *engine, int frame, float subframe) +{ + if (!engine->depsgraph) { + return; + } + + Render *re = engine->re; + double cfra = (double)frame + (double)subframe; + + CLAMP(cfra, MINAFRAME, MAXFRAME); + BKE_scene_frame_set(re->scene, cfra); + BKE_scene_graph_update_for_newframe(engine->depsgraph); + + BKE_scene_camera_switch_update(re->scene); +} + +/* Bake */ +void RE_bake_engine_set_engine_parameters(Render *re, Main *bmain, Scene *scene) +{ + re->scene = scene; + re->main = bmain; + render_copy_renderdata(&re->r, &scene->r); +} + +bool RE_bake_has_engine(Render *re) +{ + RenderEngineType *type = RE_engines_find(re->r.engine); + return (type->bake != NULL); +} + +bool RE_bake_engine(Render *re, + Depsgraph *depsgraph, + Object *object, + const int object_id, + const BakePixel pixel_array[], + const BakeImages *bake_images, + const int depth, + const eScenePassType pass_type, + const int pass_filter, + float result[]) +{ + RenderEngineType *type = RE_engines_find(re->r.engine); + RenderEngine *engine; + bool persistent_data = (re->r.mode & R_PERSISTENT_DATA) != 0; + + /* set render info */ + re->i.cfra = re->scene->r.cfra; + BLI_strncpy(re->i.scene_name, re->scene->id.name + 2, sizeof(re->i.scene_name) - 2); + + /* render */ + engine = re->engine; + + if (!engine) { + engine = RE_engine_create(type); + re->engine = engine; + } + + engine->flag |= RE_ENGINE_RENDERING; + + /* TODO: actually link to a parent which shouldn't happen */ + engine->re = re; + + engine->resolution_x = re->winx; + engine->resolution_y = re->winy; + + BLI_rw_mutex_lock(&re->partsmutex, THREAD_LOCK_WRITE); + RE_parts_init(re); + engine->tile_x = re->r.tilex; + engine->tile_y = re->r.tiley; + BLI_rw_mutex_unlock(&re->partsmutex); + + if (type->bake) { + engine->depsgraph = depsgraph; + + /* update is only called so we create the engine.session */ + if (type->update) { + type->update(engine, re->main, engine->depsgraph); + } + + for (int i = 0; i < bake_images->size; i++) { + const BakeImage *image = bake_images->data + i; + + engine->bake.pixels = pixel_array + image->offset; + engine->bake.result = result + image->offset * depth; + engine->bake.width = image->width; + engine->bake.height = image->height; + engine->bake.depth = depth; + engine->bake.object_id = object_id; + + type->bake( + engine, engine->depsgraph, object, pass_type, pass_filter, image->width, image->height); + + memset(&engine->bake, 0, sizeof(engine->bake)); + } + + engine->depsgraph = NULL; + } + + engine->tile_x = 0; + engine->tile_y = 0; + engine->flag &= ~RE_ENGINE_RENDERING; + + BLI_rw_mutex_lock(&re->partsmutex, THREAD_LOCK_WRITE); + + /* re->engine becomes zero if user changed active render engine during render */ + if (!persistent_data || !re->engine) { + RE_engine_free(engine); + re->engine = NULL; + } + + RE_parts_free(re); + BLI_rw_mutex_unlock(&re->partsmutex); + + if (BKE_reports_contain(re->reports, RPT_ERROR)) { + G.is_break = true; + } + + return true; +} + +/* Render */ + +static void engine_render_view_layer(Render *re, + RenderEngine *engine, + ViewLayer *view_layer_iter, + const bool use_engine, + const bool use_grease_pencil) +{ + /* Lock UI so scene can't be edited while we read from it in this render thread. */ + if (re->draw_lock) { + re->draw_lock(re->dlh, 1); + } + + /* Create depsgraph with scene evaluated at render resolution. */ + ViewLayer *view_layer = BLI_findstring( + &re->scene->view_layers, view_layer_iter->name, offsetof(ViewLayer, name)); + engine_depsgraph_init(engine, view_layer); + + /* Sync data to engine, within draw lock so scene data can be accessed safely. */ + if (use_engine) { + if (engine->type->update) { + engine->type->update(engine, re->main, engine->depsgraph); + } + } + + if (re->draw_lock) { + re->draw_lock(re->dlh, 0); + } + + /* Perform render with engine. */ + if (use_engine) { + if (engine->type->flag & RE_USE_GPU_CONTEXT) { + DRW_render_context_enable(engine->re); + } + + engine->type->render(engine, engine->depsgraph); + + if (engine->type->flag & RE_USE_GPU_CONTEXT) { + DRW_render_context_disable(engine->re); + } + } + + /* Optionally composite grease pencil over render result. */ + if (engine->has_grease_pencil && use_grease_pencil && !re->result->do_exr_tile) { + /* NOTE: External engine might have been requested to free its + * dependency graph, which is only allowed if there is no grease + * pencil (pipeline is taking care of that). */ + if (!RE_engine_test_break(engine) && engine->depsgraph != NULL) { + DRW_render_gpencil(engine, engine->depsgraph); + } + } + + /* Free dependency graph, if engine has not done it already. */ + engine_depsgraph_free(engine); +} + +int RE_engine_render(Render *re, int do_all) +{ + RenderEngineType *type = RE_engines_find(re->r.engine); + bool persistent_data = (re->r.mode & R_PERSISTENT_DATA) != 0; + + /* verify if we can render */ + if (!type->render) { + return 0; + } + if ((re->r.scemode & R_BUTS_PREVIEW) && !(type->flag & RE_USE_PREVIEW)) { + return 0; + } + if (do_all && !(type->flag & RE_USE_POSTPROCESS)) { + return 0; + } + if (!do_all && (type->flag & RE_USE_POSTPROCESS)) { + return 0; + } + + /* Lock drawing in UI during data phase. */ + if (re->draw_lock) { + re->draw_lock(re->dlh, 1); + } + + /* update animation here so any render layer animation is applied before + * creating the render result */ + if ((re->r.scemode & (R_NO_FRAME_UPDATE | R_BUTS_PREVIEW)) == 0) { + render_update_anim_renderdata(re, &re->scene->r, &re->scene->view_layers); + } + + /* create render result */ + BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE); + if (re->result == NULL || !(re->r.scemode & R_BUTS_PREVIEW)) { + int savebuffers = RR_USE_MEM; + + if (re->result) { + render_result_free(re->result); + } + + if ((type->flag & RE_USE_SAVE_BUFFERS) && (re->r.scemode & R_EXR_TILE_FILE)) { + savebuffers = RR_USE_EXR; + } + re->result = render_result_new(re, &re->disprect, 0, savebuffers, RR_ALL_LAYERS, RR_ALL_VIEWS); + } + BLI_rw_mutex_unlock(&re->resultmutex); + + if (re->result == NULL) { + /* Clear UI drawing locks. */ + if (re->draw_lock) { + re->draw_lock(re->dlh, 0); + } + /* Too small image is handled earlier, here it could only happen if + * there was no sufficient memory to allocate all passes. + */ + BKE_report(re->reports, RPT_ERROR, "Failed allocate render result, out of memory"); + G.is_break = true; + return 1; + } + + /* set render info */ + re->i.cfra = re->scene->r.cfra; + BLI_strncpy(re->i.scene_name, re->scene->id.name + 2, sizeof(re->i.scene_name)); + + /* render */ + RenderEngine *engine = re->engine; + + if (!engine) { + engine = RE_engine_create(type); + re->engine = engine; + } + + engine->flag |= RE_ENGINE_RENDERING; + + /* TODO: actually link to a parent which shouldn't happen */ + engine->re = re; + + if (re->flag & R_ANIMATION) { + engine->flag |= RE_ENGINE_ANIMATION; + } + if (re->r.scemode & R_BUTS_PREVIEW) { + engine->flag |= RE_ENGINE_PREVIEW; + } + engine->camera_override = re->camera_override; + + engine->resolution_x = re->winx; + engine->resolution_y = re->winy; + + BLI_rw_mutex_lock(&re->partsmutex, THREAD_LOCK_WRITE); + RE_parts_init(re); + engine->tile_x = re->partx; + engine->tile_y = re->party; + BLI_rw_mutex_unlock(&re->partsmutex); + + if (re->result->do_exr_tile) { + render_result_exr_file_begin(re, engine); + } + + /* Clear UI drawing locks. */ + if (re->draw_lock) { + re->draw_lock(re->dlh, 0); + } + + /* Render view layers. */ + bool delay_grease_pencil = false; + + if (type->render) { + FOREACH_VIEW_LAYER_TO_RENDER_BEGIN (re, view_layer_iter) { + engine_render_view_layer(re, engine, view_layer_iter, true, true); + + /* With save buffers there is no render buffer in memory for compositing, delay + * grease pencil in that case. */ + delay_grease_pencil = engine->has_grease_pencil && re->result->do_exr_tile; + + if (RE_engine_test_break(engine)) { + break; + } + } + FOREACH_VIEW_LAYER_TO_RENDER_END; + } + + /* Clear tile data */ + engine->tile_x = 0; + engine->tile_y = 0; + engine->flag &= ~RE_ENGINE_RENDERING; + + render_result_free_list(&engine->fullresult, engine->fullresult.first); + + BLI_rw_mutex_lock(&re->partsmutex, THREAD_LOCK_WRITE); + + /* For save buffers, read back from disk. */ + if (re->result->do_exr_tile) { + render_result_exr_file_end(re, engine); + } + + /* Perform delayed grease pencil rendering. */ + if (delay_grease_pencil) { + BLI_rw_mutex_unlock(&re->partsmutex); + + FOREACH_VIEW_LAYER_TO_RENDER_BEGIN (re, view_layer_iter) { + engine_render_view_layer(re, engine, view_layer_iter, false, true); + if (RE_engine_test_break(engine)) { + break; + } + } + FOREACH_VIEW_LAYER_TO_RENDER_END; + + BLI_rw_mutex_lock(&re->partsmutex, THREAD_LOCK_WRITE); + } + + /* re->engine becomes zero if user changed active render engine during render */ + if (!persistent_data || !re->engine) { + RE_engine_free(engine); + re->engine = NULL; + } + + if (re->r.scemode & R_EXR_CACHE_FILE) { + BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE); + render_result_exr_file_cache_write(re); + BLI_rw_mutex_unlock(&re->resultmutex); + } + + RE_parts_free(re); + BLI_rw_mutex_unlock(&re->partsmutex); + + if (BKE_reports_contain(re->reports, RPT_ERROR)) { + G.is_break = true; + } + +#ifdef WITH_FREESTYLE + if (re->r.mode & R_EDGE_FRS) { + RE_RenderFreestyleExternal(re); + } +#endif + + return 1; +} + +void RE_engine_update_render_passes(struct RenderEngine *engine, + struct Scene *scene, + struct ViewLayer *view_layer, + update_render_passes_cb_t callback, + void *callback_data) +{ + if (!(scene && view_layer && engine && callback && engine->type->update_render_passes)) { + return; + } + + BLI_mutex_lock(&engine->update_render_passes_mutex); + + engine->update_render_passes_cb = callback; + engine->update_render_passes_data = callback_data; + engine->type->update_render_passes(engine, scene, view_layer); + engine->update_render_passes_cb = NULL; + engine->update_render_passes_data = NULL; + + BLI_mutex_unlock(&engine->update_render_passes_mutex); +} + +void RE_engine_register_pass(struct RenderEngine *engine, + struct Scene *scene, + struct ViewLayer *view_layer, + const char *name, + int channels, + const char *chanid, + eNodeSocketDatatype type) +{ + if (!(scene && view_layer && engine && engine->update_render_passes_cb)) { + return; + } + + engine->update_render_passes_cb( + engine->update_render_passes_data, scene, view_layer, name, channels, chanid, type); +} + +void RE_engine_free_blender_memory(RenderEngine *engine) +{ + /* Weak way to save memory, but not crash grease pencil. + * + * TODO(sergey): Find better solution for this. + */ + if (engine->has_grease_pencil) { + return; + } + DEG_graph_free(engine->depsgraph); + engine->depsgraph = NULL; +} diff --git a/source/tools b/source/tools -Subproject dc4ccc3bfb646ef2a558bd3565f84080e99b0e8 +Subproject f067d32f4e8c830cfca758a74a49fa39d7f851e |